[ARM/FDPIC v6 06/24] [ARM] FDPIC: Add support for c++ exceptions
authorChristophe Lyon <christophe.lyon@st.com>
Tue, 10 Sep 2019 07:47:49 +0000 (09:47 +0200)
committerChristophe Lyon <clyon@gcc.gnu.org>
Tue, 10 Sep 2019 07:47:49 +0000 (09:47 +0200)
The main difference with existing support is that function addresses
are function descriptor addresses instead. This means that all code
dealing with function pointers now has to cope with function
descriptors instead.

For the same reason, Linux kernel helpers can no longer be called by
dereferencing their address, so we implement wrappers that directly
call the kernel helpers.

When restoring a function address, we also have to restore the FDPIC
register value (r9).

2019-09-10  Christophe Lyon  <christophe.lyon@st.com>
Mickaël Guêné <mickael.guene@st.com>

gcc/
* ginclude/unwind-arm-common.h (unwinder_cache): Add reserved5
field.

libgcc/
* config/arm/linux-atomic.c (__kernel_cmpxchg): Add FDPIC support.
(__kernel_dmb): Likewise.
(__fdpic_cmpxchg): New function.
(__fdpic_dmb): New function.
* config/arm/unwind-arm.h (FDPIC_REGNUM): New define.
(gnu_Unwind_Find_got): New function.
(_Unwind_decode_typeinfo_ptr): Add FDPIC support.
* unwind-arm-common.inc (UCB_PR_GOT): New.
(funcdesc_t): New struct.
(get_eit_entry): Add FDPIC support.
(unwind_phase2): Likewise.
(unwind_phase2_forced): Likewise.
(__gnu_Unwind_RaiseException): Likewise.
(__gnu_Unwind_Resume): Likewise.
(__gnu_Unwind_Backtrace): Likewise.
* unwind-pe.h (read_encoded_value_with_base): Likewise.

libstdc++/
* libsupc++/eh_personality.cc (get_ttype_entry): Add FDPIC
support.

Co-Authored-By: Mickaël Guêné <mickael.guene@st.com>
From-SVN: r275568

gcc/ChangeLog
gcc/ginclude/unwind-arm-common.h
libgcc/ChangeLog
libgcc/config/arm/linux-atomic.c
libgcc/config/arm/unwind-arm.h
libgcc/unwind-arm-common.inc
libgcc/unwind-pe.h
libstdc++-v3/ChangeLog
libstdc++-v3/libsupc++/eh_personality.cc

index 62fb38ab6dfc1a0283edab1a120c2b053d51141f..a212eb3ce52bf1a3d4328890f1d7d06841138c27 100644 (file)
@@ -1,3 +1,9 @@
+2019-09-10  Christophe Lyon  <christophe.lyon@st.com>
+       Mickaël Guêné <mickael.guene@st.com>
+
+       * ginclude/unwind-arm-common.h (unwinder_cache): Add reserved5
+       field.
+
 2019-09-10  Christophe Lyon  <christophe.lyon@st.com>
        Mickaël Guêné <mickael.guene@st.com>
 
index 6df783e0346925aa5e5435af327c082101ebdbe4..d4eb03e7a98acff79ef902ec7c5b0138a093c711 100644 (file)
@@ -91,7 +91,7 @@ extern "C" {
          _uw reserved2;  /* Personality routine address */
          _uw reserved3;  /* Saved callsite address */
          _uw reserved4;  /* Forced unwind stop arg */
-         _uw reserved5;
+         _uw reserved5;  /* Personality routine GOT value in FDPIC mode.  */
        }
       unwinder_cache;
       /* Propagation barrier cache (valid after phase 1): */
index bb411298aa317f193e151458c79fbf3995fcb80d..8f29b5ed6080041269b3ae32f059f1b23720c723 100644 (file)
@@ -1,3 +1,23 @@
+2019-09-10  Christophe Lyon  <christophe.lyon@st.com>
+       Mickaël Guêné <mickael.guene@st.com>
+
+       * config/arm/linux-atomic.c (__kernel_cmpxchg): Add FDPIC support.
+       (__kernel_dmb): Likewise.
+       (__fdpic_cmpxchg): New function.
+       (__fdpic_dmb): New function.
+       * config/arm/unwind-arm.h (FDPIC_REGNUM): New define.
+       (gnu_Unwind_Find_got): New function.
+       (_Unwind_decode_typeinfo_ptr): Add FDPIC support.
+       * unwind-arm-common.inc (UCB_PR_GOT): New.
+       (funcdesc_t): New struct.
+       (get_eit_entry): Add FDPIC support.
+       (unwind_phase2): Likewise.
+       (unwind_phase2_forced): Likewise.
+       (__gnu_Unwind_RaiseException): Likewise.
+       (__gnu_Unwind_Resume): Likewise.
+       (__gnu_Unwind_Backtrace): Likewise.
+       * unwind-pe.h (read_encoded_value_with_base): Likewise.
+
 2019-09-10  Christophe Lyon  <christophe.lyon@st.com>
        Mickaël Guêné <mickael.guene@st.com>
 
index 06a6d4685bcfc067d2fbe6b791d7182dd7904a58..565f829c5c0373c240ce1752d4db4ab2fe008599 100644 (file)
@@ -25,11 +25,62 @@ see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
 
 /* Kernel helper for compare-and-exchange.  */
 typedef int (__kernel_cmpxchg_t) (int oldval, int newval, int *ptr);
-#define __kernel_cmpxchg (*(__kernel_cmpxchg_t *) 0xffff0fc0)
+
+#define STR(X) #X
+#define XSTR(X) STR(X)
+
+#define KERNEL_CMPXCHG 0xffff0fc0
+
+#if __FDPIC__
+/* Non-FDPIC ABIs call __kernel_cmpxchg directly by dereferencing its
+   address, but under FDPIC we would generate a broken call
+   sequence. That's why we have to implement __kernel_cmpxchg and
+   __kernel_dmb here: this way, the FDPIC call sequence works.  */
+#define __kernel_cmpxchg __fdpic_cmpxchg
+#else
+#define __kernel_cmpxchg (*(__kernel_cmpxchg_t *) KERNEL_CMPXCHG)
+#endif
 
 /* Kernel helper for memory barrier.  */
 typedef void (__kernel_dmb_t) (void);
-#define __kernel_dmb (*(__kernel_dmb_t *) 0xffff0fa0)
+
+#define KERNEL_DMB 0xffff0fa0
+
+#if __FDPIC__
+#define __kernel_dmb __fdpic_dmb
+#else
+#define __kernel_dmb (*(__kernel_dmb_t *) KERNEL_DMB)
+#endif
+
+#if __FDPIC__
+static int __fdpic_cmpxchg (int oldval, int newval, int *ptr)
+{
+  int result;
+
+  asm volatile (
+               "ldr    ip, 1f\n\t"
+               "bx     ip\n\t"
+               "1:\n\t"
+               ".word " XSTR(KERNEL_CMPXCHG) "\n\t"
+               : "=r" (result)
+               : "r" (oldval) , "r" (newval), "r" (ptr)
+               : "r3", "memory");
+  /* The result is actually returned by the kernel helper, we need
+     this to avoid a warning.  */
+  return result;
+}
+
+static void __fdpic_dmb (void)
+{
+  asm volatile (
+               "ldr    ip, 1f\n\t"
+               "bx     ip\n\t"
+               "1:\n\t"
+               ".word " XSTR(KERNEL_DMB) "\n\t"
+               );
+}
+
+#endif
 
 /* Note: we implement byte, short and int versions of atomic operations using
    the above kernel helpers; see linux-atomic-64bit.c for "long long" (64-bit)
index 43c5379fe00553551a98e79cedeb92f927c42864..2bf320ab85830fe9f74214e89bf1f7331f4697b0 100644 (file)
 /* Use IP as a scratch register within the personality routine.  */
 #define UNWIND_POINTER_REG 12
 
+#define FDPIC_REGNUM 9
+
+#define STR(x) #x
+#define XSTR(x) STR(x)
+
 #ifdef __cplusplus
 extern "C" {
 #endif
+_Unwind_Ptr __attribute__((weak)) __gnu_Unwind_Find_got (_Unwind_Ptr);
+
+static inline _Unwind_Ptr gnu_Unwind_Find_got (_Unwind_Ptr ptr)
+{
+    _Unwind_Ptr res;
+
+    if (__gnu_Unwind_Find_got)
+       res =  __gnu_Unwind_Find_got (ptr);
+    else
+      {
+       asm volatile ("mov %[result], r" XSTR(FDPIC_REGNUM)
+                     : [result]"=r" (res)
+                     :
+                     :);
+      }
+
+    return res;
+}
+
   /* Decode an R_ARM_TARGET2 relocation.  */
   static inline _Unwind_Word
   _Unwind_decode_typeinfo_ptr (_Unwind_Word base __attribute__ ((unused)),
@@ -48,7 +72,12 @@ extern "C" {
       if (!tmp)
        return 0;
 
-#if (defined(linux) && !defined(__uClinux__)) || defined(__NetBSD__) \
+#if __FDPIC__
+      /* For FDPIC, we store the offset of the GOT entry.  */
+      /* So, first get GOT from dynamic linker and then use indirect access.  */
+      tmp += gnu_Unwind_Find_got (ptr);
+      tmp = *(_Unwind_Word *) tmp;
+#elif (defined(linux) && !defined(__uClinux__)) || defined(__NetBSD__) \
     || defined(__FreeBSD__) || defined(__fuchsia__)
       /* Pc-relative indirect.  */
 #define _GLIBCXX_OVERRIDE_TTYPE_ENCODING (DW_EH_PE_pcrel | DW_EH_PE_indirect)
index fd572fe26c1267daccf4f516ca89d351e661aae1..0bacc11ef1a75ad6d0ba35cb296527c648c38b17 100644 (file)
@@ -62,6 +62,7 @@ __gnu_Unwind_Find_exidx (_Unwind_Ptr, int *);
 #define UCB_PR_ADDR(ucbp) ((ucbp)->unwinder_cache.reserved2)
 #define UCB_SAVED_CALLSITE_ADDR(ucbp) ((ucbp)->unwinder_cache.reserved3)
 #define UCB_FORCED_STOP_ARG(ucbp) ((ucbp)->unwinder_cache.reserved4)
+#define UCB_PR_GOT(ucbp) ((ucbp)->unwinder_cache.reserved5)
 
 /* Unwind descriptors.  */
 
@@ -85,6 +86,16 @@ typedef struct __EIT_entry
   _uw content;
 } __EIT_entry;
 
+#ifdef __FDPIC__
+
+/* Only used in FDPIC case.  */
+struct funcdesc_t
+{
+  unsigned int ptr;
+  unsigned int got;
+};
+#endif
+
 /* Assembly helper functions.  */
 
 /* Restore core register state.  Never returns.  */
@@ -259,7 +270,21 @@ get_eit_entry (_Unwind_Control_Block *ucbp, _uw return_address)
     {
       /* One of the predefined standard routines.  */
       _uw idx = (*(_uw *) ucbp->pr_cache.ehtp >> 24) & 0xf;
+#if __FDPIC__
+      {
+       struct funcdesc_t *funcdesc
+         = (struct funcdesc_t *) __gnu_unwind_get_pr_addr (idx);
+       if (funcdesc)
+         {
+           UCB_PR_ADDR (ucbp) = funcdesc->ptr;
+           UCB_PR_GOT (ucbp) = funcdesc->got;
+         }
+       else
+         UCB_PR_ADDR (ucbp) = 0;
+      }
+#else
       UCB_PR_ADDR (ucbp) = __gnu_unwind_get_pr_addr (idx);
+#endif
       if (UCB_PR_ADDR (ucbp) == 0)
        {
          /* Failed */
@@ -270,6 +295,10 @@ get_eit_entry (_Unwind_Control_Block *ucbp, _uw return_address)
     {
       /* Execute region offset to PR */
       UCB_PR_ADDR (ucbp) = selfrel_offset31 (ucbp->pr_cache.ehtp);
+#if __FDPIC__
+      UCB_PR_GOT (ucbp)
+       = (unsigned int) gnu_Unwind_Find_got ((_Unwind_Ptr) UCB_PR_ADDR (ucbp));
+#endif
     }
   return _URC_OK;
 }
@@ -291,14 +320,29 @@ unwind_phase2 (_Unwind_Control_Block * ucbp, phase2_vrs * vrs)
       UCB_SAVED_CALLSITE_ADDR (ucbp) = VRS_PC(vrs);
 
       /* Call the pr to decide what to do.  */
+#if __FDPIC__
+      {
+       volatile struct funcdesc_t funcdesc;
+       funcdesc.ptr = UCB_PR_ADDR (ucbp);
+       funcdesc.got = UCB_PR_GOT (ucbp);
+       pr_result = ((personality_routine) &funcdesc)
+         (_US_UNWIND_FRAME_STARTING, ucbp, (_Unwind_Context *) vrs);
+      }
+#else
       pr_result = ((personality_routine) UCB_PR_ADDR (ucbp))
        (_US_UNWIND_FRAME_STARTING, ucbp, (_Unwind_Context *) vrs);
+#endif
     }
   while (pr_result == _URC_CONTINUE_UNWIND);
   
   if (pr_result != _URC_INSTALL_CONTEXT)
     abort();
 
+#if __FDPIC__
+  /* r9 could have been lost due to PLT jump.  Restore correct value.  */
+  vrs->core.r[FDPIC_REGNUM] = gnu_Unwind_Find_got (VRS_PC (vrs));
+#endif
+
   uw_restore_core_regs (vrs, &vrs->core);
 }
 
@@ -346,8 +390,18 @@ unwind_phase2_forced (_Unwind_Control_Block *ucbp, phase2_vrs *entry_vrs,
          next_vrs = saved_vrs;
 
          /* Call the pr to decide what to do.  */
+#if __FDPIC__
+         {
+           volatile struct funcdesc_t funcdesc;
+           funcdesc.ptr = UCB_PR_ADDR (ucbp);
+           funcdesc.got = UCB_PR_GOT (ucbp);
+           pr_result = ((personality_routine) &funcdesc)
+             (action, ucbp, (void *) &next_vrs);
+         }
+#else
          pr_result = ((personality_routine) UCB_PR_ADDR (ucbp))
            (action, ucbp, (void *) &next_vrs);
+#endif
 
          saved_vrs.prev_sp = VRS_SP (&next_vrs);
        }
@@ -384,6 +438,11 @@ unwind_phase2_forced (_Unwind_Control_Block *ucbp, phase2_vrs *entry_vrs,
       return _URC_FAILURE;
     }
 
+#if __FDPIC__
+  /* r9 could have been lost due to PLT jump.  Restore correct value.  */
+  saved_vrs.core.r[FDPIC_REGNUM] = gnu_Unwind_Find_got (VRS_PC (&saved_vrs));
+#endif
+
   uw_restore_core_regs (&saved_vrs, &saved_vrs.core);
 }
 
@@ -429,8 +488,18 @@ __gnu_Unwind_RaiseException (_Unwind_Control_Block * ucbp,
        return _URC_FAILURE;
 
       /* Call the pr to decide what to do.  */
+#if __FDPIC__
+      {
+       volatile struct funcdesc_t funcdesc;
+       funcdesc.ptr = UCB_PR_ADDR (ucbp);
+       funcdesc.got = UCB_PR_GOT (ucbp);
+       pr_result = ((personality_routine) &funcdesc)
+         (_US_VIRTUAL_UNWIND_FRAME, ucbp, (void *) &saved_vrs);
+      }
+#else
       pr_result = ((personality_routine) UCB_PR_ADDR (ucbp))
        (_US_VIRTUAL_UNWIND_FRAME, ucbp, (void *) &saved_vrs);
+#endif
     }
   while (pr_result == _URC_CONTINUE_UNWIND);
 
@@ -488,13 +557,27 @@ __gnu_Unwind_Resume (_Unwind_Control_Block * ucbp, phase2_vrs * entry_vrs)
     }
 
   /* Call the cached PR.  */
+#if __FDPIC__
+  {
+    volatile struct funcdesc_t funcdesc;
+    funcdesc.ptr = UCB_PR_ADDR (ucbp);
+    funcdesc.got = UCB_PR_GOT (ucbp);
+    pr_result = ((personality_routine) &funcdesc)
+      (_US_UNWIND_FRAME_RESUME, ucbp, (_Unwind_Context *) entry_vrs);
+  }
+#else
   pr_result = ((personality_routine) UCB_PR_ADDR (ucbp))
        (_US_UNWIND_FRAME_RESUME, ucbp, (_Unwind_Context *) entry_vrs);
+#endif
 
   switch (pr_result)
     {
     case _URC_INSTALL_CONTEXT:
       /* Upload the registers to enter the landing pad.  */
+#if __FDPIC__
+      /* r9 could have been lost due to PLT jump.  Restore correct value.  */
+      entry_vrs->core.r[FDPIC_REGNUM] = gnu_Unwind_Find_got (VRS_PC (entry_vrs));
+#endif
       uw_restore_core_regs (entry_vrs, &entry_vrs->core);
 
     case _URC_CONTINUE_UNWIND:
@@ -586,9 +669,20 @@ __gnu_Unwind_Backtrace(_Unwind_Trace_Fn trace, void * trace_argument,
        }
 
       /* Call the pr to decide what to do.  */
+#if __FDPIC__
+      {
+       volatile struct funcdesc_t funcdesc;
+       funcdesc.ptr = UCB_PR_ADDR (ucbp);
+       funcdesc.got = UCB_PR_GOT (ucbp);
+       code = ((personality_routine) &funcdesc)
+         (_US_VIRTUAL_UNWIND_FRAME | _US_FORCE_UNWIND,
+          ucbp, (void *) &saved_vrs);
+      }
+#else
       code = ((personality_routine) UCB_PR_ADDR (ucbp))
        (_US_VIRTUAL_UNWIND_FRAME | _US_FORCE_UNWIND, 
         ucbp, (void *) &saved_vrs);
+#endif
     }
   while (code != _URC_END_OF_STACK
         && code != _URC_FAILURE);
index 4ed1c6699d6c8275359b43cc03ef0317833d56bb..1c9dae54de6a243b7790b667a6cdda5bc78de183 100644 (file)
@@ -262,10 +262,27 @@ read_encoded_value_with_base (unsigned char encoding, _Unwind_Ptr base,
 
       if (result != 0)
        {
+#if __FDPIC__
+         /* FDPIC relative addresses imply taking the GOT address
+            into account.  */
+         if ((encoding & DW_EH_PE_pcrel) && (encoding & DW_EH_PE_indirect))
+           {
+             result += gnu_Unwind_Find_got ((_Unwind_Ptr) u);
+             result = *(_Unwind_Internal_Ptr *) result;
+           }
+         else
+           {
+             result += ((encoding & 0x70) == DW_EH_PE_pcrel
+                        ? (_Unwind_Internal_Ptr) u : base);
+             if (encoding & DW_EH_PE_indirect)
+               result = *(_Unwind_Internal_Ptr *) result;
+           }
+#else
          result += ((encoding & 0x70) == DW_EH_PE_pcrel
                     ? (_Unwind_Internal_Ptr) u : base);
          if (encoding & DW_EH_PE_indirect)
            result = *(_Unwind_Internal_Ptr *) result;
+#endif
        }
     }
 
index 7bb093de36639489f7768ad213f4fc6628385372..1c115a74cb5df531b6baa8fce6886f1bf2418969 100644 (file)
@@ -1,3 +1,9 @@
+2019-09-10  Christophe Lyon  <christophe.lyon@st.com>
+       Mickaël Guêné <mickael.guene@st.com>
+
+       * libsupc++/eh_personality.cc (get_ttype_entry): Add FDPIC
+       support.
+
 2019-09-10  Jonathan Wakely  <jwakely@redhat.com>
 
        PR libstdc++/91711
index 35e4e461d69d35093647e1afe828d0359976badb..1528ab9b5debba8ade79be10b508b942f4e17e0d 100644 (file)
@@ -93,7 +93,15 @@ get_ttype_entry (lsda_header_info *info, _uleb128_t i)
   _Unwind_Ptr ptr;
 
   i *= size_of_encoded_value (info->ttype_encoding);
-  read_encoded_value_with_base (info->ttype_encoding, info->ttype_base,
+  read_encoded_value_with_base (
+#if __FDPIC__
+                               /* Force these flags to nake sure to
+                                  take the GOT into account.  */
+                               (DW_EH_PE_pcrel | DW_EH_PE_indirect),
+#else
+                               info->ttype_encoding,
+#endif
+                               info->ttype_base,
                                info->TType - i, &ptr);
 
   return reinterpret_cast<const std::type_info *>(ptr);