Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Static dtors are not run on DSO unload #159

Open
raziel- opened this issue Mar 23, 2024 · 16 comments
Open

Static dtors are not run on DSO unload #159

raziel- opened this issue Mar 23, 2024 · 16 comments

Comments

@raziel-
Copy link

raziel- commented Mar 23, 2024

GCC11.3.0

Unloading a DSO that registers destructors/functions to be run on exit, will always crash when the main program exits

static dtors are not run on DSO unload, but at program exit. Since the SO is already unloaded, this causes a crash.

A simplified version of the repro code is at
https://github.com/PushmePullyu/sotest/tree/repro-dlclose-dtors
which removes the SDL dependency

See here for the full discussion:
https://bugs.scummvm.org/ticket/15015

@salass00
Copy link
Contributor

Constructor/destructor functions that are added in the .ctors/.dtors section should already be called at dlclose() for DSOs loaded by dlopen() as this is handled by libdl/elf.library.

From what I understand however gcc does not use that method, but instead __cxa_atexit() and if this is not available (as in our case) atexit(). As atexit() is used explicitly to add callbacks that are to be called on program exit (it has no knowledge of DSOs) this cannot work when called from a DSO loaded by dlopen().

Adding __cxa_atexit() support to newlib should be possible (in fact the framework for this already exists there), but in addition to this newlib specific support will need to be added to libdl or libdl functions will need to be integrated into newlib (basically dlclose() will need to call __call_exitprocs() with __dso_handle as the 2nd parameter before unloading the DSO). An alternative solution to modifying libdl would be to call __call_exitprocs() from __shlib_call_destructors() but since IIRC newer elf.library versions bypass the __shlib_call_constructors()/__shlib_call_destructors() functions entirely (not sure why this change was made) this might not be possible any more.

I'm still not quite sure where the __dso_handle comes from and how the compiler gets access to it but I assume it's a symbol that's supposed to be in the DSO somewhere.

@raziel-
Copy link
Author

raziel- commented Apr 17, 2024

@salass00

anything i can do (with my little to non existing knowledge) to help?

btw, the original sotest branch might vanish, so i forked it here:
https://github.com/raziel-/dlclose-dtors-sotest

@salass00
Copy link
Contributor

After some more googling I have found that there is a function __cxa_finalize() for calling atexit functions registered by a specific DSO (using this is preferable to using the newlib private __call_exitprocs() function). Both __cxa_atexit() and __cxa_finalize() need to be added to the INewlib interface and to libc.

@raziel-
Copy link
Author

raziel- commented Apr 19, 2024

so, the ball is back in the field of the newlib devs?

@afxgroup
Copy link

I know one of them!! :D

@raziel-
Copy link
Author

raziel- commented Apr 19, 2024

@afxgroup

would you alert him about this thread?
or are you referring to yourself? 😀

@afxgroup
Copy link

He is @salass00 :)

@raziel-
Copy link
Author

raziel- commented Apr 19, 2024

oh 😀

@salass00
Copy link
Contributor

The __cxa_atexit() and __cxa_finalize() functions are now exported from the newlib library and from the libc. I also added the __dso_handle hidden symbol to shcrtbegin.o and __shlib_call_destructors() calls __cxa_finalize(&__dso_handle) before iterating over the .dtors array as usual.

Compiling the libfoo.so with -fuse-cxa-atexit a call to __cxa_atexit() is generated by gcc instead of atexit() however sotest still crashes because (as I suspected) elf.library 53.37+ is bypassing the __shlib_call_destructors() mechanism.

@raziel-
Copy link
Author

raziel- commented Apr 21, 2024

wow, thanks a lot :-)

is a new newlib needed, or is a recompiled gcc enough?

@salass00
Copy link
Contributor

It will require an updated newlib.library and associated developer files. The current gcc should be fine though as long as you use -fuse-cxa-atexit when compiling DSOs.

An updated elf.library will likely be needed as well because of the problem I described earlier. Adding the __cxa_finalize() call directly to libdl.so is more complicated and does nothing for programs that do not use libdl.so but instead use the elf.library directly to open and close DSOs (they would need similar fixes as well).

@raziel-
Copy link
Author

raziel- commented Apr 21, 2024

may i be so bold to ask you to relay those informations to the people in charge of newlib, elf.library and the other parts that needs updating?

im afraid im just a small unimportant user

@salass00
Copy link
Contributor

I already wrote a mail to the os4-developer mailing list. A good suggestion that I got was to use a .dtors destructor function to call __cxa_finalize() however implementing it is turning out to be problematic still as I only have the shcrtbegin.o and shcrtend.o files to work with.

@raziel-
Copy link
Author

raziel- commented Apr 22, 2024

thank you very much

@salass00
Copy link
Contributor

My initial thought was that the .dtors section was being badly generated because when I tried to dump the contents of it with objdump it outputted the first sentinel entry (0xffffffff) and after that just "...", however this turned out to be a red herring. The real reason that it wasn't working was that IElf->DLClose() didn't have any code to call destructors when a DSO is unloaded (open count goes to zero).

After adding the missing functionality to elf.library the sotest program works as it's supposed to.

@raziel-
Copy link
Author

raziel- commented Apr 27, 2024

😮
wow...thank you, i never thought it would be possible to fix that so fast...kudos

elf.library is part of...Hyperion, amikit, aeon?
i lost track, sorry

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants