> Date: Sun, 21 Aug 2016 16:35:21 -0700 > From: Philip Guenther <guent...@gmail.com> > > On Fri, 19 Aug 2016, Mark Kettenis wrote: > > > From: Philip Guenther <guent...@gmail.com> > > > Date: Thu, 18 Aug 2016 21:09:10 -0700 > > > > > > On Thursday, August 18, 2016, Mark Kettenis <mark.kette...@xs4all.nl> > > > wrote: > > > ... > > > > > > > > > > There is a functional change here. Our current ld.so doesn't run > > > > > > DT_INIT and DT_FINI for the main executable. The ELF standard is a > > > > > > bit > > > > > > ambiguous about this, but Linux does run tose for the main > > > > > > executable. > > > > > > And Solaris allegedly does as well. So my diff changes that. > > > > > > > > > > ld.so doesn't run them because __start() in csu does! Note that > > > > > csu needs to run them for static executables, and we use the > > > > > same crt0.o for both static and dynamic executables. I think > > > > > you're double executing them with this. > > > > > > > > We're not double executing because we don't create a DT_INIT entry for > > > > them. > > > > > > Hmm, is that a bug? Static and dynamic should ideally behave the same for > > > all these, no? > > > > Ah, perhaps I wasn't clear. We don't create DT_INIT for both static > > and dynamic executables. > > Hmm, I'm trying to decide if that's a bug or not.
Probably depends on your point of view I guess. Looking at /usr/bin/groff (a C++ binary) on a somewhat recent Linux system, I see DT_INIT, DT_INITARRAY, DT_FINI and DT_FINIARRAY entries. Madmess! Not sure what Solaris does here. > > You raise an interesting question though. Traditional static > > executables cannot have DT_INIT since they don't have a .dynamic > > section. But static PIE executables can have DT_INIT. So should our > > self-relocation code attempt to exeute it? > > To talk mostly at myself... > > It's an underdocumented part of the ELF standard how code in .init > sections gets executed, and how that interacts with setting DT_INIT. > > The Solaris 11 linker guide says: > The sections .init and .fini provide a runtime initialization > and termination code block, respectively. The compiler drivers > typically supply .init and .fini sections with files they add > to the beginning and end of your input file list. These compiler > provided files have the effect of encapsulating the .init and > .fini code from your relocatable objects into individual > functions. These functions are identified by the reserved symbol > names _init and _fini respectively. When creating a dynamic > object, the link-editor identifies these symbols with the > .dynamic tags DT_INIT and DT_FINI accordingly. These tags > identify the associated sections so they can be called by the > runtime linker. > > We agree with that for shared-libraries, but for executables we don't, > presumably because we can't depend on the viability of the DT_INIT hook > because non-PIE, static executables don't have an dynamic section at all, > so instead the .init section code ends up in a function __init(): note > the extra underbar. That function is then called from __start(). > > > So I think it's fine for you to change ld.so to execute the DT_INIT > function for executables: it's won't normally be set but if code > explicitly sets it then we'll be fine...as long as they aren't *depending* > on doing that to disable execution of .init section code...but if someone > did that they deserve to lose: if you don't want .init code, then DON'T > INCLUDE IT. The same may apply to executing DT_INIT functions for static, > PIE executables. That was my reasoning. And I was guessing that for the ARM C++ ABI we'd need DT_INITARRAY and DT_FINIARRAY support in the main executable. > ...but in the end we still need to be able to support static, non-PIE, > which means that at least in some cases _start() has to execute .init code > and we don't have a great way to handle that case differently in _start(). > So for now we need to not handle .init section code in executables via > DT_INIT, which makes calling DT_INIT of executables, whether dynamic or > static PIE, mostly a moot point and subject to whatever we want to do. We do have protection for running stuff twice in both __do_init() and __do_fini(), so there are options. Anyway, here is a new diff, with the DT_PREINIT_ARRAY issue fixed. ok? Index: libexec/ld.so/loader.c =================================================================== RCS file: /cvs/src/libexec/ld.so/loader.c,v retrieving revision 1.165 diff -u -p -r1.165 loader.c --- libexec/ld.so/loader.c 14 Aug 2016 04:30:39 -0000 1.165 +++ libexec/ld.so/loader.c 22 Aug 2016 21:32:31 -0000 @@ -53,6 +53,7 @@ void _dl_debug_state(void); void _dl_setup_env(const char *_argv0, char **_envp); void _dl_dtors(void); void _dl_fixup_user_env(void); +void _dl_call_preinit(elf_object_t *); void _dl_call_init_recurse(elf_object_t *object, int initfirst); int _dl_pagesz; @@ -73,9 +74,31 @@ struct r_debug *_dl_debug_map; void _dl_dopreload(char *paths); /* - * Run dtors for all objects that are eligible. + * Run dtors for a single object. */ +void +_dl_run_dtors(elf_object_t *obj) +{ + if (obj->dyn.fini_array) { + int num = obj->dyn.fini_arraysz / sizeof(Elf_Addr); + int i; + + DL_DEB(("doing finiarray obj %p @%p: [%s]\n", + obj, obj->dyn.fini, obj->load_name)); + for (i = num; i > 0; i--) + (*obj->dyn.fini_array[i-1])(); + } + + if (obj->dyn.fini) { + DL_DEB(("doing dtors obj %p @%p: [%s]\n", + obj, obj->dyn.fini, obj->load_name)); + (*obj->dyn.fini)(); + } +} +/* + * Run dtors for all objects that are eligible. + */ void _dl_run_all_dtors(void) { @@ -91,10 +114,10 @@ _dl_run_all_dtors(void) while (fini_complete == 0) { fini_complete = 1; - for (node = _dl_objects->next; + for (node = _dl_objects; node != NULL; node = node->next) { - if ((node->dyn.fini) && + if ((node->dyn.fini || node->dyn.fini_array) && (OBJECT_REF_CNT(node) == 0) && (node->status & STAT_INIT_DONE) && ((node->status & STAT_FINI_DONE) == 0)) { @@ -105,10 +128,10 @@ _dl_run_all_dtors(void) node->status |= STAT_FINI_READY; } } - for (node = _dl_objects->next; + for (node = _dl_objects; node != NULL; node = node->next ) { - if ((node->dyn.fini) && + if ((node->dyn.fini || node->dyn.fini_array) && (OBJECT_REF_CNT(node) == 0) && (node->status & STAT_INIT_DONE) && ((node->status & STAT_FINI_DONE) == 0) && @@ -120,18 +143,14 @@ _dl_run_all_dtors(void) } - for (node = _dl_objects->next; + for (node = _dl_objects; node != NULL; node = node->next ) { if (node->status & STAT_FINI_READY) { - DL_DEB(("doing dtors obj %p @%p: [%s]\n", - node, node->dyn.fini, - node->load_name)); - fini_complete = 0; node->status |= STAT_FINI_DONE; node->status &= ~STAT_FINI_READY; - (*node->dyn.fini)(); + _dl_run_dtors(node); } } @@ -157,11 +176,6 @@ _dl_dtors(void) DL_DEB(("doing dtors\n")); - /* main program runs its dtors itself - * but we want to run dtors on all it's children); - */ - _dl_objects->status |= STAT_FINI_DONE; - _dl_objects->opencount--; _dl_notify_unload_shlib(_dl_objects); @@ -605,14 +619,10 @@ _dl_boot(const char **argv, char **envp, _dl_debug_state(); /* - * The first object is the executable itself, - * it is responsible for running it's own ctors/dtors - * thus do NOT run the ctors for the executable, all of - * the shared libraries which follow. * Do not run init code if run from ldd. */ if (_dl_objects->next != NULL) { - _dl_objects->status |= STAT_INIT_DONE; + _dl_call_preinit(_dl_objects); _dl_call_init(_dl_objects); } @@ -690,6 +700,20 @@ _dl_rtld(elf_object_t *object) } void +_dl_call_preinit(elf_object_t *object) +{ + if (object->dyn.preinit_array) { + int num = object->dyn.preinit_arraysz / sizeof(Elf_Addr); + int i; + + DL_DEB(("doing preinitarray obj %p @%p: [%s]\n", + object, object->dyn.preinit_array, object->load_name)); + for (i = 0; i < num; i++) + (*object->dyn.preinit_array[i])(); + } +} + +void _dl_call_init(elf_object_t *object) { _dl_call_init_recurse(object, 1); @@ -721,6 +745,16 @@ _dl_call_init_recurse(elf_object_t *obje DL_DEB(("doing ctors obj %p @%p: [%s]\n", object, object->dyn.init, object->load_name)); (*object->dyn.init)(); + } + + if (object->dyn.init_array) { + int num = object->dyn.init_arraysz / sizeof(Elf_Addr); + int i; + + DL_DEB(("doing initarray obj %p @%p: [%s]\n", + object, object->dyn.init_array, object->load_name)); + for (i = 0; i < num; i++) + (*object->dyn.init_array[i])(); } object->status |= STAT_INIT_DONE; Index: libexec/ld.so/resolve.c =================================================================== RCS file: /cvs/src/libexec/ld.so/resolve.c,v retrieving revision 1.74 diff -u -p -r1.74 resolve.c --- libexec/ld.so/resolve.c 8 Aug 2016 21:59:20 -0000 1.74 +++ libexec/ld.so/resolve.c 22 Aug 2016 21:32:31 -0000 @@ -246,6 +246,7 @@ _dl_finalize_object(const char *objname, int phdrc, const int objtype, const long lbase, const long obase) { elf_object_t *object; + #if 0 _dl_printf("objname [%s], dynp %p, objtype %x lbase %lx, obase %lx\n", objname, dynp, objtype, lbase, obase); @@ -322,6 +323,12 @@ _dl_finalize_object(const char *objname, object->Dyn.info[DT_FINI] += obase; if (object->Dyn.info[DT_JMPREL]) object->Dyn.info[DT_JMPREL] += obase; + if (object->Dyn.info[DT_INIT_ARRAY]) + object->Dyn.info[DT_INIT_ARRAY] += obase; + if (object->Dyn.info[DT_FINI_ARRAY]) + object->Dyn.info[DT_FINI_ARRAY] += obase; + if (object->Dyn.info[DT_PREINIT_ARRAY]) + object->Dyn.info[DT_PREINIT_ARRAY] += obase; if (object->Dyn.info[DT_HASH] != 0) { Elf_Word *hashtab = (Elf_Word *)object->Dyn.info[DT_HASH]; Index: libexec/ld.so/resolve.h =================================================================== RCS file: /cvs/src/libexec/ld.so/resolve.h,v retrieving revision 1.79 diff -u -p -r1.79 resolve.h --- libexec/ld.so/resolve.h 8 Aug 2016 21:59:20 -0000 1.79 +++ libexec/ld.so/resolve.h 22 Aug 2016 21:32:31 -0000 @@ -81,13 +81,23 @@ struct elf_object { const char *soname; const char *rpath; Elf_Addr symbolic; - Elf_Rel *rel; + Elf_Rel *rel; Elf_Addr relsz; Elf_Addr relent; Elf_Addr pltrel; Elf_Addr debug; Elf_Addr textrel; Elf_Addr jmprel; + Elf_Addr bind_now; + void (**init_array)(void); + void (**fini_array)(void); + Elf_Addr init_arraysz; + Elf_Addr fini_arraysz; + const char *runpath; + Elf_Addr flags; + Elf_Addr encoding; + void (**preinit_array)(void); + Elf_Addr preinit_arraysz; } u; } Dyn; #define dyn Dyn.u