This is the mail archive of the libc-alpha@sourceware.org mailing list for the glibc project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

Re: [RFC] stdlib: Make atexit to not act as __cxa_atexit



On 03/07/2019 07:05, Florian Weimer wrote:
> * Adhemerval Zanella:
> 
>>>> We need __cxa_finalize because it is called by __do_global_dtors_aux
>>>> from initfini callbacks.
>>>
>>> (Note: The comment is about crtbeginS.o built from libgcc sources.)
>>>
>>> If all versions of the ELF constructor in crtbeginS.o call
>>> __cxa_finalize as their first action, then we can turn __cxa_finalize
>>> into a NOP and have the dynamic linker perform the original actions of
>>> __cxa_finalize directly, before running the ELF destructors for this
>>> object.
>>
>> In fact I though about it and one issue I found is __cxa_finalize uses
>> __dso_handle and it is a hidden symbol for each DSO.  So, at _dl_fini
>> we will need to resolve __cxa_finalize from libc.so (or potentially
>> other function that handles the exit handlers), then resolve the
>> __dso_handle from the referenced object (with potentially a hack to
>> lookup for local objects), and finally call __cxa_finalize with
>> __dso_handler as argument. Not sure it is really a better design.
> 
> So … what I was implying but didn't say explicitly is that at one point,
> we will have to map (the address of) __dso_handle to a link map using
> _dl_find_dso_for_object.
> 
> I need to submit my patch to speed up _dl_find_dso_for_object
> substantially.  Then it shouldn't be a problem at all to call it in
> response to each call to atexit, and put new entries on both the global
> linked list and the per-DSO list.
> 
> Not sure to what extend we'd need to avoid quadratic-time algorithms
> here.

For _dl_fini, we already have the link_map for object that calls 
__cxa_finalize. What we need is to filter out the exit handlers registered
by the object itself, so its calls only the functions registered by the 
shared library referenced by the link_map.

Not sure how easily we can accomplish it on exit handlers registration 
functions (the __dso_handler trick is to make this easier). 

> 
>>>> Also making the loader itself handler atexit
>>>> is not the best option IMHO: we will either to call a private symbol on 
>>>> libc (which might require an additional lookup) or export the internal 
>>>> data so loader can implement the callback issuing itself.
>>>
>>> Anything would be better than this pointless indirection through libgcc.
>>> We have accumlated lots of cruft in the crt* objects which serve no
>>> purpose at all for current binaries, and eventually, we will have to
>>> clean this up.
>>>
>>> I think crtbegin*.o could completely go away if we turn __cxa_finalize
>>> into a NOP on the glibc side.
>>
>> I think if __dso_handle was exported as a global symbol it could
>> simplify the indirection by a bit.
> 
> I don't see how this is possible.  It would trigger interposition, and
> we need a unique address for each DSO, to identify it.

My idea would be to resolve the symbol without the interposition and make
the loader itself setup the value.  But I did not think this through, most
likely this does not really help.

> 
> Now the __dso_handle interface is fairly broken and probably predates
> the tight integration of libc and the dynamic linker.  We could easily
> cache something that allows to quickly locate the actual linkmap in a
> variable link __dso_handle, but we do not.
> 
>> One idea is to change the order atexit handlers are handled slightly:
>>
>>   - atexit handlers are not registered through __cxa_atexit, a new
>>     symbol __atexit adds a new entry as ef_at (similar to my proposal).
>>
>>   - __cxa_finalize is changed to handle ef_at similar to ef_cxa by
>>     calling the function pointer and setting it as ef_free. dlclose
>>     will issue atexit as expected.
>>
>>   - __run_exit_handlers, used on both exit and quick_exit, will issue
>>     atexit handler (ef_at) first and then handle on_exit (ef_on), and
>>     __cxa_atexit (ef_cxa).  This keeps the expected order or atexit
>>     handlers, however the order regarding ef_cxa will change.
>>
>>   - It will required some changes internally to handle atexit and
>>     __cxa_atexit differently regarding lock acquisition since before
>>     ef_cxa handler we can't get newly added ef_at ones.
> 
> I think you also need to do something on dlclose to deallocate atexit
> handlers that have run, to avoid a memory leak.  Setting them to ef_free
> is not sufficient.

The stdlib/exit.c already handles that on the main loop. The main issue 
is we explicit support that all atexit handler (on_exit, __cxa_atexit, 
__cxa_at_quick_exit) can call any interface to add new handlers. 

The idea I am exploring is to change on how new handlers are included in
the exit handler lists, so on a default execution (without handler adding
new entries) atexit/on_exit are executed before __cxa_atexit. 

The change is to make exit_function_list have an associated type which
hold an only a specific set of handlers.  Currently we required just
two types, one for ef_at/ef_on handlers and one for ef_cxa. New inclusion 
are added in a ordered manner, where ef_at/ef_on are added on 
exit_function_list always before ef_cxa.  

Although on __cxa_finalize we will also need to explicit handle ef_at as
well (for dlclose), the idea is also to mimic the ef_cxa behaviour and set
the ef_at/ef_on free after its execution to avoid multiple executions 
(as a side note I think by not setting ef_on currently we might run on_exit 
handlers twice in some scenarios).

The main issue, which imho there is no solution but it also expected,
is if the any of exit handlers add new handlers itself.  The exit/
__cxa_finalize will just rewind itself.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]