Skip to content

linker: Fix atexit handlers loaded by linker on PE

Tamar Christina requested to merge Phyx/ghc:gh-19297-fix-atexit-handlers into master

Hi all,

This fixes a couple of issues, primarily the segfault reported in #19297 (closed). When network runs, it registers an atexit handler in order to shut down sockets when it's done.

On windows on-demand unloading is disabled, but not final unloading. i.e. the linker cleans up after itself during shutdown.

This is where the problem is...

during rts shutdown in hs_exit_ we do

    /* free linker data */
    exitLinker();

which has the intention of freeing the resources the linker held (we also should run destructors but we don't at the moment.. but that's missing functionality) . Now this function looks like

void
exitLinker( void ) {
#if defined(OBJFORMAT_PEi386)
   exitLinker_PEi386();
#endif
#if defined(OBJFORMAT_ELF) || defined(OBJFORMAT_MACHO)
   if (linker_init_done == 1) {
      regfree(&re_invalid);
      regfree(&re_realso);
#if defined(THREADED_RTS)
      closeMutex(&dl_mutex);
#endif
   }
#endif
   if (linker_init_done == 1) {
       freeHashTable(symhash, free);
       exitUnloadCheck();
   }
#if defined(THREADED_RTS)
   closeMutex(&linker_mutex);
#endif
}

Now first problem here is that the OC unload stuff exitUnloadCheck for the on-demand unloading was added after exitLinker_PEi386. This... can't ever work as it that whole unload logic contains references to ObjectCode created by the linker. So if you call it after exit, that code is gone.

I guess we get away with it because the code unloading is disabled on Windows, were it not, this would segfault.

Anyway back to the main story, so we called exitLinker_PEi386 which unloaded the objects and... wipes the private heap the linker created to free all the memory the linker used in one go.

There in lies the problem... the heap is gone, so the atexit handler now points to unmapped memory...

Indeed commenting out exitLinker() in rts shutdown fixes the problem. Why does it work in ghci? I have no idea.. I think we get lucky. Ghci has a lot of work to do to shut down, so I'm guessing the exit handlers manage to run before the OS clears the pages.

So the issue is, how do we free memory so we don't leak like a siv.. but still work with exit handlers. and I think the answer to that is... register the heap cleanup code in an exit handler itself.

This fixes both the issues above.

Fixes #19297 (closed)

I can't think of any way to write a sensible test for this as it's essentially a race condition..

Edited by Tamar Christina

Merge request reports