Re: -z preinitarray
From: Jonathan Adams (jwadams_at_gmail.com)
Date: 04/30/05
- Previous message: Casper H.S. ***: "Re: Why cannot truss peek inside sendmsg()"
- In reply to: Dan Koren: "Re: -z preinitarray"
- Next in thread: Casper H.S. ***: "Re: -z preinitarray"
- Messages sorted by: [ date ] [ thread ] [ subject ] [ author ]
Date: Sat, 30 Apr 2005 02:34:38 -0700
In article <427338f6@news.meer.net>, "Dan Koren" <dankoren@yahoo.com>
wrote:
>
> "Jonathan Adams" <jwadams@gmail.com> wrote in message
> news:jwadams-0BD266.15453329042005@news1nwk.sfbay.sun.com...
> >
> > It generally can't, anyway; relying on init sections to
> > set everything up is a bad idea. It's much better to set
> > everything up in static data,
>
>
> Which may or may not be possible depending on what the code
> is intended/designed to do.
It's usually possible; you set up the global data so that your fast
paths will fail, then put the check for initialization in the slow
path.
> > and arrange for any additional initialization to happen
> > at the first call to malloc/calloc/realloc/etc.
>
> While this would certainly work, it would also require each
> and every routine in the library to check if the library has
> already been initialized on every call. If I decide to follow
> such an approach, I would rather start with a statically
> initialized call table in which every entry points to the
> initialization section. Maybe I should simply patch the GOT! ;-)
No, it doesn't. It does require every initial path will be able to
reach the initialization code, however. Patching the GOT would be
probably be a bad idea. =]
Better designed interfaces tend to make this a lot easier; if you have to
get a handle to do anything, the initialization can happen when the first
handle is created.
> > The problem is that init section ordering is not
> > guaranteed, especially when cyclic dependencies
> > happen. The classic problem is LD_BIND_NOW=1
> > plus C++; libCrun.so.1, libc.so.1, and libmymalloc.so
> > form a cyclic dependency group, the linker calls
> > libCrun's initializer first,
>
>
> Even if I set -z initfirst when building the library?
What if a library that uses malloc() also specifies '-z initfirst'?
(For example, the "old" libthread on Solaris 8 and prior does exactly
that; it even calls malloc() from its init section.)
> While quite plausible, this example is also contrived.
It actually happened; I had to fix this precise problem for libumem in
Solaris:
4924624 running C++ executables with libumem and LD_BIND_NOW causes
SIGSEGV
Previously, I did most of my initialization in the init section; now,
nothing done in the init section is required for proper initialization.
Ironically, it looks like the underlying problem no longer exists.[1]
The point remains that init sections can bite you when you least expect
it. If you have to have one, it should do as little as possible. You
are probably right that it is safe to use one to initialize a memory
allocator; I'll never do it again, having been bitten once. =]
> A correctly designed memory allocator does not (should
> not) depend on any other library. All it needs to do is
> make one system call to allocate a slab to get rolling.
> Unless of course it happens to have been written in C++,
> in which case its author should be defrocked and sent
> to work in support for the rest of his/her life ;-)
It has to depend on libc, to make that system call. But libc shouldn't
directly depend on anything (besides possibly the dynamic linker),
either. (I agree that C++ is entirely inappropriate for an allocator)
> > Summary: init sections are evil and unreliable; avoid
> > them if you possibly can.
>
> Init sections do not have to be evil and unreliable, if
> correctly designed and implemented. Unfortunately, most
> systems currently available in the marketplace seem to
> have resulted from quiltwork.
I think the real problem is that dynamic linking has both a very high
level of granularity, and huge amounts of legacy behavior. Small
problems can magnify into large problems unexpectedly, and different
features can interact destructively. On top of that, you have little
control over how people use your library.
> Also worth mentioning is the fact that most (if not all)
> of this trouble could be avoided if one links the library
> with the executable and assign its initializer to the
> .preinit_array. Unfortunately, that would render the
> allocator useless for existing applications.
You could make it optional; make the init function safe to call multiple
times. Then, any fool calling malloc() in his init sections can add it
to preinit_array.
I'd also note that it's typically not a problem, anyway; most dynamic
linkers will make sure a library is initialized before going through a
PLT to it, so as long as your init section doesn't call out to someone
who will call back into you, you'll be fine. LD_BIND_NOW breaks that,
since all the PLTs are resolved.
Cheers,
- jonathan
[1] Earlier in Solaris 10's development, libc referenced (using a weak
symbol) some stack unwinding goop in libC (the C++ runtime). That
caused the dependency loop; libC depends on libumem depends on libc
depends on libC. In Solaris 10 FCS, libc uses dlsym(3dl) to look up the
symbol, so the dependency (and the problem) are gone.
- Previous message: Casper H.S. ***: "Re: Why cannot truss peek inside sendmsg()"
- In reply to: Dan Koren: "Re: -z preinitarray"
- Next in thread: Casper H.S. ***: "Re: -z preinitarray"
- Messages sorted by: [ date ] [ thread ] [ subject ] [ author ]