rtld: Add main object initialization and finalization
authorJohn Marino <draco@marino.st>
Sat, 17 Mar 2012 18:52:17 +0000 (19:52 +0100)
committerJohn Marino <draco@marino.st>
Sun, 18 Mar 2012 02:11:56 +0000 (03:11 +0100)
Since DragonFly's inception, crt1 has called _init and _fini functions of
the binary rather than leaving this task to the runtime linker.  Likely
this was done in order to use the same crt code for both statically and
dynamically linked binaries.

When FreeBSD imported DragonFly's preinit, init, and fini array handling
code, they moved the _init and _fini calls to rtld.  In order for rtld to
maintain compatiblity with binaries created with crt code that call these
functions, rtld looks for an ELF note that indicates the binary was built
with a crt that does not call _init and _fini.

This commit imports this capability as well as synchronizes some of the
changes FreeBSD made to the DragonFly code.  Many of the differences
weren't necessary, but some represented improvements.  In any case, it
benefits both operating systems to minimize the differences between the
runtime linkers in order to continue to collaborate and share new
features.

Taken from: FreeBSD SVN 232831 (2012-03-11)

One key difference between the DragonFly and FreeBSD implementations is
that FreeBSD will not properly execute a binary that requires
initialization and/or finalization if the PT_NOTE program header is
intentionally omitted using a custom linker script and the PHDRS
directive.  In the same case, DragonFly will look to see if the main
binary contains a gnu hash dynamic tag.  If it does, it assumes the binary
was built with the new crt, which is a very good assumption.  Since gnu
hash was inserted by default into binaries only a week ago, there's only
a window of few days where this isn't true, and PT_NOTE-free binaries are
very rare.

libexec/rtld-elf/Makefile
libexec/rtld-elf/i386/rtld_machdep.h
libexec/rtld-elf/map_object.c
libexec/rtld-elf/rtld.c
libexec/rtld-elf/rtld.h
libexec/rtld-elf/x86_64/rtld_machdep.h

index 3527ad1..2017433 100644 (file)
@@ -13,6 +13,7 @@ WARNS?=               2
 
 CFLAGS+=       -DIN_RTLD
 CFLAGS+=       -I${.CURDIR}/${MACHINE_ARCH} -I${.CURDIR} -D__thread=
+CFLAGS+=       -I${.CURDIR}/../../lib/csu/common
 LDFLAGS+=      -nostdlib -e .rtld_start -Wl,--no-undefined
 INSTALLFLAGS=  -C -b
 .ifndef NOFSCHG
index 987b110..d842519 100644 (file)
@@ -60,8 +60,8 @@ reloc_jmpslot(Elf_Addr *where, Elf_Addr target,
 #define call_initfini_pointer(obj, target) \
        (((InitFunc)(target))())
 
-#define call_array_pointer(target, argc, argv, env) \
-       (((InitArrayFunc)(target))(argc, argv, env))
+#define call_init_pointer(obj, target) \
+       (((InitArrFunc)(target))(main_argc, main_argv, environ))
 
 #define round(size, align) \
        (((size) + (align) - 1) & ~((align) - 1))
index ffb816a..215ae0c 100644 (file)
@@ -85,6 +85,8 @@ map_object(int fd, const char *path, const struct stat *sb)
     Elf_Word stack_flags;
     Elf_Addr relro_page;
     size_t relro_size;
+    Elf_Addr note_start;
+    Elf_Addr note_end;
 
     hdr = get_elf_header(fd, path);
     if (hdr == NULL)
@@ -103,6 +105,8 @@ map_object(int fd, const char *path, const struct stat *sb)
     phdr_vaddr = 0;
     relro_page = 0;
     relro_size = 0;
+    note_start = 0;
+    note_end = 0;
     segs = alloca(sizeof(segs[0]) * hdr->e_phnum);
     stack_flags = RTLD_DEFAULT_STACK_PF_EXEC | PF_R | PF_W;
     while (phdr < phlimit) {
@@ -142,6 +146,15 @@ map_object(int fd, const char *path, const struct stat *sb)
            relro_page = phdr->p_vaddr;
            relro_size = phdr->p_memsz;
            break;
+
+       case PT_NOTE:
+           if (phdr->p_offset > PAGE_SIZE ||
+             phdr->p_offset + phdr->p_filesz > PAGE_SIZE)
+               break;
+           note_start = (Elf_Addr)(char *)hdr + phdr->p_offset;
+           note_end = note_start + phdr->p_filesz;
+           digest_notes(obj, note_start, note_end);
+           break;
        }
 
        ++phdr;
index 9a7723c..fa45cae 100644 (file)
@@ -65,6 +65,7 @@
 #include "rtld.h"
 #include "libmap.h"
 #include "rtld_printf.h"
+#include "notes.h"
 
 #define PATH_RTLD      "/usr/libexec/ld-elf.so.2"
 #define LD_ARY_CACHE   16
@@ -255,11 +256,10 @@ char *__progname;
 char **environ;
 
 /*
- * Globals passed as arguments to .init_array and .preinit_array functions
+ * Used to pass argc, argv to init functions.
  */
-
-int glac;
-char **glav;
+int main_argc;
+char **main_argv;
 
 /*
  * Globals to control TLS allocation.
@@ -409,8 +409,8 @@ _rtld(Elf_Addr *sp, func_ptr_type *exit_proc, Obj_Entry **objp)
     __progname = obj_rtld.path;
     argv0 = argv[0] != NULL ? argv[0] : "(null)";
     environ = env;
-    glac = argc;
-    glav = argv;
+    main_argc = argc;
+    main_argv = argv;
 
     trust = !issetugid();
 
@@ -566,9 +566,6 @@ _rtld(Elf_Addr *sp, func_ptr_type *exit_proc, Obj_Entry **objp)
     obj_tail = &obj_main->next;
     obj_count++;
     obj_loads++;
-    /* Make sure we don't call the main program's init and fini functions. */
-    obj_main->init = obj_main->fini = (Elf_Addr)NULL;
-    obj_main->init_array = obj_main->fini_array = (Elf_Addr)NULL;
 
     /* Initialize a fake symbol for resolving undefined weak references. */
     sym_zero.st_info = ELF_ST_INFO(STB_GLOBAL, STT_NOTYPE);
@@ -705,8 +702,18 @@ _rtld_call_init(void)
     RtldLockState lockstate;
     Obj_Entry *obj;
 
-    preinitialize_main_object();
     wlock_acquire(rtld_bind_lock, &lockstate);
+    if (obj_main->crt_no_init) {
+       preinitialize_main_object();
+    }
+    else {
+       /*
+        * Make sure we don't call the main program's init and fini functions
+        * for binaries linked with old crt1 which calls _init itself.
+        */
+       obj_main->init = obj_main->fini = (Elf_Addr)NULL;
+       obj_main->init_array = obj_main->fini_array = (Elf_Addr)NULL;
+    }
     objlist_call_init(&initlist, &lockstate);
     objlist_clear(&initlist);
     dbg("loading filtees");
@@ -1266,6 +1273,7 @@ digest_phdr(const Elf_Phdr *phdr, int phnum, caddr_t entry, const char *path)
     Obj_Entry *obj;
     const Elf_Phdr *phlimit = phdr + phnum;
     const Elf_Phdr *ph;
+    Elf_Addr note_start, note_end;
     int nsegs = 0;
 
     obj = obj_new();
@@ -1321,6 +1329,12 @@ digest_phdr(const Elf_Phdr *phdr, int phnum, caddr_t entry, const char *path)
            obj->relro_page = obj->relocbase + trunc_page(ph->p_vaddr);
            obj->relro_size = round_page(ph->p_memsz);
            break;
+
+       case PT_NOTE:
+           note_start = (Elf_Addr)obj->relocbase + ph->p_vaddr;
+           note_end = note_start + ph->p_filesz;
+           digest_notes(obj, note_start, note_end);
+           break;
        }
     }
     if (nsegs < 1) {
@@ -1332,6 +1346,42 @@ digest_phdr(const Elf_Phdr *phdr, int phnum, caddr_t entry, const char *path)
     return obj;
 }
 
+void
+digest_notes(Obj_Entry *obj, Elf_Addr note_start, Elf_Addr note_end)
+{
+       const Elf_Note *note;
+       const char *note_name;
+       uintptr_t p;
+
+       for (note = (const Elf_Note *)note_start; (Elf_Addr)note < note_end;
+           note = (const Elf_Note *)((const char *)(note + 1) +
+             roundup2(note->n_namesz, sizeof(Elf32_Addr)) +
+             roundup2(note->n_descsz, sizeof(Elf32_Addr)))) {
+               if (note->n_namesz != sizeof(NOTE_VENDOR) ||
+                   note->n_descsz != sizeof(int32_t))
+                       continue;
+               if (note->n_type != ABI_NOTETYPE && note->n_type != CRT_NOINIT_NOTETYPE)
+                       continue;
+               note_name = (const char *)(note + 1);
+               if (strncmp(NOTE_VENDOR, note_name, sizeof(NOTE_VENDOR)) != 0)
+                       continue;
+               switch (note->n_type) {
+               case ABI_NOTETYPE:
+                       /* FreeBSD osrel note */
+                       p = (uintptr_t)(note + 1);
+                       p += roundup2(note->n_namesz, sizeof(Elf32_Addr));
+                       obj->osrel = *(const int32_t *)(p);
+                       dbg("note osrel %d", obj->osrel);
+                       break;
+               case CRT_NOINIT_NOTETYPE:
+                       /* FreeBSD 'crt does not call init' note */
+                       obj->crt_no_init = true;
+                       dbg("note crt_no_init");
+                       break;
+               }
+       }
+}
+
 static Obj_Entry *
 dlcheck(void *handle)
 {
@@ -1717,7 +1767,8 @@ initlist_add_objects(Obj_Entry *obj, Obj_Entry **tail, Objlist *list)
        initlist_add_neededs(obj->needed, list);
 
     /* Add the object to the init list. */
-    if (obj->init != (Elf_Addr)NULL || obj->init_array != (Elf_Addr)NULL)
+    if (obj->preinit_array != (Elf_Addr)NULL || obj->init != (Elf_Addr)NULL ||
+      obj->init_array != (Elf_Addr)NULL)
        objlist_push_tail(list, obj);
 
     /* Add the object to the global fini list in the reverse order. */
@@ -2052,10 +2103,10 @@ objlist_call_fini(Objlist *list, Obj_Entry *root, RtldLockState *lockstate)
             * values of 0 or 1, but they need to be ignored.
             */
            fini_addr = (Elf_Addr *)elm->obj->fini_array;
-           if (fini_addr != (Elf_Addr)NULL) {
+           if (fini_addr != NULL && elm->obj->fini_array_num > 0) {
                for (index = elm->obj->fini_array_num - 1; index >= 0; index--) {
                    if (fini_addr[index] != 0 && fini_addr[index] != 1) {
-                       dbg("DSO Array: calling fini function for %s at %p",
+                       dbg("calling fini array function for %s at %p",
                            elm->obj->path, (void *)fini_addr[index]);
                        LD_UTRACE(UTRACE_FINI_CALL, elm->obj,
                            (void *)fini_addr[index], 0, 0, elm->obj->path);
@@ -2093,21 +2144,22 @@ objlist_call_fini(Objlist *list, Obj_Entry *root, RtldLockState *lockstate)
 static void
 preinitialize_main_object (void)
 {
-    Elf_Addr *init_addr;
+    Elf_Addr *preinit_addr;
     int index;
 
-    init_addr = (Elf_Addr *)obj_main->preinit_array;
-    if (init_addr == (Elf_Addr)NULL)
+    preinit_addr = (Elf_Addr *)obj_main->preinit_array;
+    if (preinit_addr == NULL)
        return;
 
-    for (index = 0; index < obj_main->preinit_array_num; index++)
-       if (init_addr[index] != 0 && init_addr[index] != 1) {
-           dbg("Calling preinit array function for %s at %p",
-               obj_main->path, (void *)init_addr[index]);
-           LD_UTRACE(UTRACE_INIT_CALL, obj_main, (void *)init_addr[index],
+    for (index = 0; index < obj_main->preinit_array_num; index++) {
+       if (preinit_addr[index] != 0 && preinit_addr[index] != 1) {
+           dbg("calling preinit function for %s at %p", obj_main->path,
+               (void *)preinit_addr[index]);
+           LD_UTRACE(UTRACE_INIT_CALL, obj_main, (void *)preinit_addr[index],
                0, 0, obj_main->path);
-           call_array_pointer(init_addr[index], glac, glav, environ);
+           call_init_pointer(obj_main, preinit_addr[index]);
        }
+    }
 }
 
 /*
@@ -2156,22 +2208,23 @@ objlist_call_init(Objlist *list, RtldLockState *lockstate)
          * need to be ignored.
          */
          if (elm->obj->init != (Elf_Addr)NULL) {
-           dbg("DSO: calling init function for %s at %p", elm->obj->path,
+           dbg("calling init function for %s at %p", elm->obj->path,
                (void *)elm->obj->init);
            LD_UTRACE(UTRACE_INIT_CALL, elm->obj, (void *)elm->obj->init,
                0, 0, elm->obj->path);
            call_initfini_pointer(elm->obj, elm->obj->init);
        }
        init_addr = (Elf_Addr *)elm->obj->init_array;
-       if (init_addr != (Elf_Addr)NULL) {
-           for (index = 0; index < elm->obj->init_array_num; index++)
+       if (init_addr != NULL) {
+           for (index = 0; index < elm->obj->init_array_num; index++) {
                if (init_addr[index] != 0 && init_addr[index] != 1) {
-                   dbg("DSO Array: calling init function for %s at %p",
-                       elm->obj->path, (void *)init_addr[index]);
+                   dbg("calling init array function for %s at %p", elm->obj->path,
+                       (void *)init_addr[index]);
                    LD_UTRACE(UTRACE_INIT_CALL, elm->obj,
                        (void *)init_addr[index], 0, 0, elm->obj->path);
-                   call_array_pointer(init_addr[index], glac, glav, environ);
+                   call_init_pointer(elm->obj, init_addr[index]);
                }
+           }
        }
        wlock_acquire(rtld_bind_lock, lockstate);
     }
index b58cea7..680c748 100644 (file)
@@ -59,6 +59,10 @@ extern size_t tls_static_space;
 extern int tls_dtv_generation;
 extern int tls_max_index;
 
+extern int main_argc;
+extern char **main_argv;
+extern char **environ;
+
 struct stat;
 struct Struct_Obj_Entry;
 
@@ -72,7 +76,7 @@ typedef STAILQ_HEAD(Struct_Objlist, Struct_Objlist_Entry) Objlist;
 
 /* Types of init and fini functions */
 typedef void (*InitFunc)(void);
-typedef void (*InitArrayFunc)(int, char **, char **);
+typedef void (*InitArrFunc)(int, char **, char **);
 
 /* Lists of shared object dependencies */
 typedef struct Struct_Needed_Entry {
@@ -218,6 +222,8 @@ typedef struct Struct_Obj_Entry {
     int init_array_num;        /* Number of entries in init_array */
     int fini_array_num;        /* Number of entries in fini_array */
 
+    int32_t osrel;             /* OSREL note value */
+
     bool mainprog : 1;         /* True if this is the main program */
     bool rtld : 1;             /* True if this is the dynamic linker */
     bool textrel : 1;          /* True if there are relocations to text seg */
@@ -239,6 +245,7 @@ typedef struct Struct_Obj_Entry {
     bool filtees_loaded : 1;   /* Filtees loaded */
     bool irelative : 1;                /* Object has R_MACHDEP_IRELATIVE relocs */
     bool gnu_ifunc : 1;                /* Object has references to STT_GNU_IFUNC */
+    bool crt_no_init : 1;      /* Object's crt does not call _init/_fini */
     bool valid_hash_sysv : 1;  /* A valid System V hash hash tag is available */
     bool valid_hash_gnu : 1;   /* A valid GNU hash tag is available */
 
@@ -327,6 +334,7 @@ const Elf_Sym *find_symdef(unsigned long, const Obj_Entry *,
   const Obj_Entry **, int, SymCache *, struct Struct_RtldLockState *);
 void init_pltgot(Obj_Entry *);
 void lockdflt_init(void);
+void digest_notes(Obj_Entry *, Elf_Addr, Elf_Addr);
 void obj_free(Obj_Entry *);
 Obj_Entry *obj_new(void);
 void _rtld_bind_start(void);
index d159884..05c0e7a 100644 (file)
@@ -60,8 +60,8 @@ reloc_jmpslot(Elf_Addr *where, Elf_Addr target,
 #define call_initfini_pointer(obj, target) \
        (((InitFunc)(target))())
 
-#define call_array_pointer(target, argc, argv, env) \
-       (((InitArrayFunc)(target))(argc, argv, env))
+#define call_init_pointer(obj, target) \
+       (((InitArrFunc)(target))(main_argc, main_argv, environ))
 
 #define round(size, align) \
        (((size) + (align) - 1) & ~((align) - 1))