Ping! malloc bug, and PIC and flat shared library support for m68k

Richard Sandiford richard at codesourcery.com
Sat Nov 18 10:31:34 UTC 2006


Mike Frysinger <vapier at gentoo.org> writes:
> On Friday 17 November 2006 13:27, Richard Sandiford wrote:
>> Are you asking instead whether normal .init_array and .fini_array
>> sections are handled by the patch as-is?  If so, the answer's yes.
>> It handles both .init/.fini sections and .init_array/.fini_array
>> sections, much as __uClibc_main.c already does for "static"
>> uClinux executables.
>
> i meant, we have all this code in place already to handle
> __preinit_array_{end,start} in __uClibc_main.c for static binaries and the
> pointers to these things are provided implicitly by the linker

Oh, sorry, I hadn't realised it was specifically __preinit_array_*
you're asking about.  The reason for that is:

        /* .preinit_array is usually only supported for executables.
         * However, the distinction between the executable and its
         * shared libraries isn't as pronounced for flat files; a shared
         * library is really just a part of an executable that can be
         * shared with other executables.  We therefore allow
         * .preinit_array to be used in libraries too.  */

In other words, I wanted to handle an executable with shared flat libraries
in the same way as a stand-alone executable would be handled.

> in my mind, there should be no sep code to handle init/fini code with
> shared flat ... so what am i missing ?  is it that since handling of
> shared flat code is such a bastardize project (needed in order to stay
> so damn small/lean compared to ELF), the init/fini arrays cannot be
> provided by the linker and must be set up by the kernel and passed
> down into userspace ?

If you're asking about having no separate init/fini code at all,
the reason is this:

If you have a flat executable that uses shared libraries 0..n-1,
the kernel's set-up procedure is as follows:

  - populate the stack with argc, argc, envp etc.
  - push the executable's entry point on the stack
  - push shared library n-1's entry point on the stack
  - ...
  - push shared library 1's entry point on the stack
  - transfer control to shared library 0's entry point

Thus each shared library gets a chance to do some initialisation,
then "returns" to the next startup routine.  This can't really
be changed without changes to the file format.

It's therefore not possible for each shared library's entry point to
call a local __uClibc_main (which would have linker-resolved references
to the library's initialisation and finalisation data).  If we did, one
library would be finalised before the next is initialised.

I think the only way to get the existing __uClibc_main to do all
initialisation and finalisation is to call it once in the executable
and have it somehow do the library's initialisation and finalisation
too.  (This is not what would happen for ELF shared libraries,
of course.)  And the only ways I can see of doing that are:

  (1) For each shared library, insert functions into the executable's
      .init and .fini that call the library's .init and .fini functions.
      Create functions that iterate over the library's .preinit_array,
      .init_array and .fini_array arrays, and insert pointers to these
      functions in the executable's arrays.

  (2) Link the contents of each library's initialisation and finalisation
      sections into the executable rather than the library itself.

(1) doesn't really achieve the goal of keeping everything in __uClibc_main
because you still have separate array walking routines.  (2) doesn't really
achieve the goal of shared libraries because you're duplicating information
in each executable.

Also, I think the only practical way of implementing (1) and (2) is to
have two separate ELF objects for each shared library: the library itself
(which is linked into the executable with -R), and a file containing the
init/fini data that should be linked normally into the executable.

Richard



More information about the uClibc mailing list