Support exception handling on statically-linked binaries
authorJohn Marino <draco@marino.st>
Sun, 5 Feb 2012 16:21:27 +0000 (17:21 +0100)
committerJohn Marino <draco@marino.st>
Sun, 5 Feb 2012 16:49:40 +0000 (17:49 +0100)
The real-time dynamic linker handles exceptions on dynamically-linked
binaries through the dl_iterate_phdr function.  The RTLD isn't invoked
on statically-built executables so thrown exceptions weren't getting
handled.  There was a dummy dl_iterate_phdr function in libc with a
weak symbol that gets looked at when the rtld version isn't present.
This function was populated and gets called when the statically-linked
executable throws an exception.

It requires the GNU_EH_FRAME program header to be present.  The base
gcc 4.4 spec file was modified to emit this header for statically-built
binaries in addition to the dynamically lined ones.

contrib/gcc-4.4/gcc/config/dragonfly.h
lib/libc/gen/dlfcn.c

index 891acd0..d169bb8 100644 (file)
@@ -125,8 +125,7 @@ along with GCC; see the file COPYING3.  If not see
   "/usr/libexec/ld-elf.so.2"
 
 #if defined(HAVE_LD_EH_FRAME_HDR)
-#define LINK_EH_SPEC \
-  "%{!static:--eh-frame-hdr}"
+#define LINK_EH_SPEC "--eh-frame-hdr"
 #endif
 
 /* Use --as-needed -lgcc_s for eh support.  */
index 29f98d7..07237fd 100644 (file)
@@ -30,6 +30,7 @@
 #include <link.h>
 #include <stddef.h>
 
+extern char **environ;
 void   _rtld_error(const char *, ...);
 
 static char sorry[] = "Service unavailable";
@@ -114,13 +115,68 @@ dlinfo(void *handle __unused, int request __unused, void *p __unused)
        return 0;
 }
 
+__dso_hidden struct dl_phdr_info
+build_phdr_info(void)
+{
+       struct dl_phdr_info phdr_info;
+       Elf_Addr *sp;
+       Elf_Auxinfo *aux, *auxp;
+       size_t phent;
+       unsigned int i;
+
+       sp = (Elf_Addr *) environ;
+       while (*sp++ != 0)
+               ;
+       aux = (Elf_Auxinfo *) sp;
+       phent = 0;
+       memset (&phdr_info, 0, sizeof(phdr_info));
+       for (auxp = aux; auxp->a_type != AT_NULL; auxp++) {
+               switch (auxp->a_type) {
+               case AT_BASE:
+                       phdr_info.dlpi_addr = (Elf_Addr) auxp->a_un.a_ptr;
+                       break;
+
+               case AT_EXECPATH:
+                       phdr_info.dlpi_name = (const char *) auxp->a_un.a_ptr;
+                       break;
+
+               case AT_PHDR:
+                       phdr_info.dlpi_phdr = (const Elf_Phdr *) auxp->a_un.a_ptr;
+                       break;
+
+               case AT_PHENT:
+                       phent = auxp->a_un.a_val;
+                       break;
+
+               case AT_PHNUM:
+                       phdr_info.dlpi_phnum = (Elf_Half) auxp->a_un.a_val;
+                       break;
+               }
+       }
+
+       for (i = 0; i < phdr_info.dlpi_phnum; i++)
+           if (phdr_info.dlpi_phdr[i].p_type == PT_TLS) {
+               phdr_info.dlpi_tls_modid = 1;
+               phdr_info.dlpi_tls_data =
+                 (void*) phdr_info.dlpi_phdr[i].p_vaddr;
+           }
+
+       return (phdr_info);
+}
+
 #pragma weak dl_iterate_phdr
 int
 dl_iterate_phdr(int (*callback)(struct dl_phdr_info *, size_t, void *),
     void *data)
 {
-       _rtld_error(sorry);
-       return 0;
+       static seen = 0;
+       static struct dl_phdr_info phdr_info;
+       if (!seen) {
+               seen = 1;
+               phdr_info = build_phdr_info();
+       }
+
+       return callback(&phdr_info, sizeof(phdr_info), data);
 }
 
 #pragma weak _rtld_addr_phdr