Index Home About Blog
Date:   Sun, 9 Jan 2000 22:54:01 -0800
From: Zack Weinberg <zack@wolery.cumb.org>
Subject: Stuff dragged in by crt1 (was Re: Announce: initrd-tftp 0.1)
Newsgroups: fa.linux.kernel

>>Even if you don't want them, you end up linking in malloc, stdio, and other
>>large chunks of useless stuff because the init/shutdown code (crt*.o) needs
>>to initialize these subsystems.  Some clever changes to libc would
>>dramatically improve this situation, but it would take some work.  Someday,
>>perhaps I'll have a chance...
>
>You can make things quite a lot better by including these lines in your 
>program:
>
>exit() { }
>abort() { }
>atexit() { }
>void *__libc_stack_end;
>
>The latter is really a granularity problem with libc but the first three are 
>probably to be expected.  There's no easy way to avoid that excess code being 
>linked in without imposing additional runtime overhead.

Actually, it is not hard at all - I just hacked up devo libc to produce a 4k
stripped static executable from

int main(void)
{
   write(1, "hello world\n", 12);
   return 0;
}

exit() and atexit() are quite small in themselves, but they drag in malloc,
and malloc drags in stdio, and stdio drags in gconv and the dynamic loader.
I created an internal entry point that doesn't use malloc, which is safe
because we're executing before any user code does so we know we have room
in the static atexit table.

abort() goes to great lengths to ensure that all buffered stdio output is
flushed, and therefore drags in stdio, etc.  In the place where crt1 calls
abort, there can't possibly be any buffered stdio output, and neither can
there be a handler for SIGABRT, so we can just call kill.

__libc_stack_end was being defined in the wrong place, and would drag
in the dynamic loader, which uses malloc and stdio.


A remaining annoyance is that if you add a call to malloc() to the above,
it bloats back up to 237k.  This is because the MALLOC_CHECK stuff refers
to stdio.  I would personally like to see all that ripped out to a preload
library, or even scrapped, but I doubt I'll get my wish.  It would also
be nice if wide streams and multibyte support didn't get pulled in unless
you actually used them.  That would be much harder, but libio without
wide streams and all the junk it refers to is only about 100k (I think).

zw


Subject: Re: Stuff dragged in by crt1 (was Re: Announce: initrd-tftp 0.1)
From: Ulrich Drepper <drepper@cygnus.com>
Date:   10 Jan 2000 15:41:31 -0800
Newsgroups: fa.linux.kernel

Zack Weinberg <zack@wolery.cumb.org> writes:

> I created an internal entry point that doesn't use malloc, which is safe
> because we're executing before any user code does so we know we have room
> in the static atexit table.

That's not true.  Global constructors unfortunately exist and it is
allowed (and through C++ code will happen very frequently) to call
atexit etc.

There is a problem with all these constructors and until we have
priorities in constructors (or at least the preinit support) there
will always be problems popping up.

-- 
---------------.      drepper at gnu.org  ,-.   1325 Chesapeake Terrace
Ulrich Drepper  \    ,-------------------'   \  Sunnyvale, CA 94089 USA
Cygnus Solutions `--' drepper at cygnus.com   `------------------------


Date:   Mon, 10 Jan 2000 16:00:28 -0800
From: Zack Weinberg <zack@wolery.cumb.org>
Subject: Re: Stuff dragged in by crt1 (was Re: Announce: initrd-tftp 0.1)
Newsgroups: fa.linux.kernel

On Mon, Jan 10, 2000 at 03:41:31PM -0800, Ulrich Drepper wrote:
> Zack Weinberg <zack@wolery.cumb.org> writes:
> 
> > I created an internal entry point that doesn't use malloc, which is safe
> > because we're executing before any user code does so we know we have room
> > in the static atexit table.
> 
> That's not true.  Global constructors unfortunately exist and it is
> allowed (and through C++ code will happen very frequently) to call
> atexit etc.
> 
> There is a problem with all these constructors and until we have
> priorities in constructors (or at least the preinit support) there
> will always be problems popping up.

I know about global constructors, but if you look closely at libc-start.c
you will see that what I said is still true.

  /* Register the destructor of the dynamic linker if there is any.  */
  if (rtld_fini != NULL)
    __internal_atexit (rtld_fini);

  /* Call the initializer of the libc.  */
  __libc_init_first (argc, argv, __environ);

  /* Register the destructor of the program, if any.  */
  if (fini)
    __internal_atexit (fini);

  /* Call the initializer of the program, if any.  */
  if (init)
    (*init) ();

The constructors you are worried about are all executed by (*init)().
We control the code executed by __libc_init_first, and in the current source
base none of it calls atexit.  Therefore, we can be absolutely certain that
the above __internal_atexit calls are safe.

zw


Subject: Re: Stuff dragged in by crt1 (was Re: Announce: initrd-tftp 0.1)
From: Ulrich Drepper <drepper@cygnus.com>
Date:   10 Jan 2000 20:00:11 -0800
Newsgroups: fa.linux.kernel

Zack Weinberg <zack@wolery.cumb.org> writes:

> The constructors you are worried about are all executed by (*init)().
> We control the code executed by __libc_init_first, and in the current source
> base none of it calls atexit.  Therefore, we can be absolutely certain that
> the above __internal_atexit calls are safe.

That's not true for shared objects.  The constructors of the shared
objects involved are called before __libc_start_main is reached.

-- 
---------------.      drepper at gnu.org  ,-.   1325 Chesapeake Terrace
Ulrich Drepper  \    ,-------------------'   \  Sunnyvale, CA 94089 USA
Cygnus Solutions `--' drepper at cygnus.com   `------------------------


Date:   Mon, 10 Jan 2000 21:10:32 -0800
From: Zack Weinberg <zack@wolery.cumb.org>
Subject: Re: Stuff dragged in by crt1 (was Re: Announce: initrd-tftp 0.1)
Newsgroups: fa.linux.kernel

On Mon, Jan 10, 2000 at 08:00:11PM -0800, Ulrich Drepper wrote:
> Zack Weinberg <zack@wolery.cumb.org> writes:
> 
> > The constructors you are worried about are all executed by (*init)().
> > We control the code executed by __libc_init_first, and in the current source
> > base none of it calls atexit.  Therefore, we can be absolutely certain that
> > the above __internal_atexit calls are safe.
> 
> That's not true for shared objects.  The constructors of the shared
> objects involved are called before __libc_start_main is reached.

From _dl_open_worker?  Is there a reason why we couldn't postpone all of that
until we hit (*init)()?

In any case, this is not a problem for statically linked binaries which is
the main place that part of the patch is interesting.  You could make it be
#ifdef PIC ? atexit : __internal_atexit.

zw


Index Home About Blog