Segfault when adding a static destructor in druntime/src/rt/sections_elf_shared.d

Johan Engelen j at j.nl
Tue Jan 8 14:30:24 UTC 2019


On Tuesday, 8 January 2019 at 12:54:11 UTC, RazvanN wrote:
> Hi all,
>
> I am working on issue 14650 [1]

Great!
(I am _extremely_ surprised that dtors are not called for 
globals.)

and I would like to implement a
> solution where static destructors are destroying global 
> variables. However, I have the following problem in 
> druntime/src/rt/sections_elf_shared:
>
> struct ThreadDSO
> {
>     DSO* _pdso;
>     static if (_pdso.sizeof == 8) uint _refCnt, _addCnt;
>     else static if (_pdso.sizeof == 4) ushort _refCnt, _addCnt;
>     else static assert(0, "unimplemented");
>     void[] _tlsRange;
>     alias _pdso this;
>     // update the _tlsRange for the executing thread
>     void updateTLSRange() nothrow @nogc
>     {
>         _tlsRange = _pdso.tlsRange();
>     }
> }
> Array!(ThreadDSO) _loadedDSOs;
>
>
> For this code, I would have to create the following static 
> destructor:
>
> static ~this() { _loadedDSOs.__dtor(); }
>
> Because Array defines a destructor which sets its length to 0.
>
> However this code leads to segfault when compiling any program 
> with the runtime (betterC code compiles successfully). In my 
> attempt to debug it, I dropped my patch and added the above 
> mentioned static destructor manually in druntime which lead to 
> the same effect. Interestingly, _loadedDSOs.__dtor runs 
> successfully, the segmentation fault comes from somewhere 
> higher in the call path (outside of the _d_run_main function 
> (in rt/dmain2.d)). I'm thinking that the static destructor 
> somehow screws up the object which is later referenced after 
> the main program finished executing. Does someone well versed 
> in druntime has any ideas what's happening?

This is my guess:

Have a look at `_d_dso_registry` and it's description. The 
function is also called upon shutdown and it accesses 
`_loadedDSOs`.
As part of shutdown, `_d_dso_registry` calls 
`runModuleDestructors` (which will run your compiler-inserted 
static dtor), but _after_ that `_d_dso_registry` accesses 
`_loadedDSOs`. I don't know exactly why the segfault happens, but 
the code assumes in several places that `_loadedDSOs` is 
non-empty. For example, `popBack` is called and `popBack` is 
invalid for length=0 (it will set the length to `size_t.max` !).

I think the solution is to not have `_loadedDSOs` be of type 
`Array!T` but of a special type that explicitly has no dtor (i.e. 
the "dtor" should only be called explicitly such that the data 
needed for shutdown survives `runModuleDestructors`). This 
probably applies to more of these druntime low-level arrays and 
other data structures.

-Johan

[1] The dtor of Array calls reset, and reset has a bug in 
rt.util.container.Array. Note the invariant: `assert(!_ptr == 
!_length);`, which triggers when `_length` is set to 0, but 
`_ptr` is not set to `null`. !!!


More information about the Digitalmars-d-learn mailing list