> Date: Sun, 21 Aug 2016 16:35:21 -0700
> From: Philip Guenther <[email protected]>
>
> On Fri, 19 Aug 2016, Mark Kettenis wrote:
> > > From: Philip Guenther <[email protected]>
> > > Date: Thu, 18 Aug 2016 21:09:10 -0700
> > >
> > > On Thursday, August 18, 2016, Mark Kettenis <[email protected]>
> > > 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