> 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

Reply via email to