C++ Logo

std-proposals

Advanced search

Re: [std-proposals] "shared libraries are outside the scope of the C++ standard"

From: Thiago Macieira <thiago_at_[hidden]>
Date: Thu, 13 Jul 2023 09:35:23 -0700
On Thursday, 13 July 2023 04:01:16 PDT Ofek Shilon via Std-Proposals wrote:
> > BTW, I'll add a 2(c): _exit() runs global destructors in DLLs. This is not
> > an
> > OS ABI limitation, but caused by how _exit() is implemented (calls
> > TerminateProcess()) and how the runtimes implement global destruction.
> > That
> > means it can be fixed without OS modifications, but still has a high cost.
>
> Can you please elaborate on which clause this doesn't conform to? Perhaps
> you're referring to runtime loading/unloading (LoadLibrary/UnloadLibrary on
> windows) or Lazy-Binding? ("Delayed-Load" on windows)

See [support.start.term]:

> [[noreturn]] void _Exit(int status) noexcept;
>
> Effects: This function has the semantics specified in the C standard library.
>
> Remarks: The program is terminated without executing destructors for objects
> of automatic, thread, or static storage duration and without calling
> functions passed to atexit() ([basic.start.term]).

I am not referring to *explicit* runtime loading and unloading actions by the
user. I am referring to runtime loading and unloading performed by the runtime
itself because you've linked to DLLs.

_exit() and _Exit() don't run any functions registered with atexit(), so
they're basically a call to TerminateProcess(). At that point, all threads
except the current one disappear (problem 2(d)), and the Win32 system starts
unloading the DLLs. As it does that, it'll call DllMain() on each DLL and this
causes the C++ runtime built into each of those DLLs to run their static
destructors.

Therefore, this is a violation of the standard as worded. It says "without
executing destructors for objects of [...] static storage" without
qualifications, so I don't think you can justify the cop-out of having stepped
outside the standard.

$ head -n50 liba.cpp test.cpp
==> liba.cpp <==
#include <stdio.h>
__declspec(dllexport) void ImplementedInLibA() {}

struct S
{
  S() { puts("S created"); }
  ~S() { puts("S destroyed"); }
};
static S s;

==> test.cpp <==
#include <stdlib.h>
__declspec(dllimport) void ImplementedInLibA();
int main()
{
  ImplementedInLibA();
  _exit(0);
}
$ g++ -o liba.dll -mdll liba.cpp
$ gcc -o test.exe test.cpp ./liba.dll
$ ./test.exe
S created
S destroyed

$ cl -nologo -MD -LD -Feliba.dll liba.cpp
liba.cpp
   Creating library liba.lib and object liba.exp
$ cl -nologo -MD -Z7 test.cpp liba.lib
test.cpp
$ ./test.exe
S created
S destroyed

Backtrace with GCC:
#0 S::~S (this=0x7ff8dadc7020 <s>, __in_chrg=<optimized out>) at liba.cpp:7
#1 0x00007ff8dadc138e in __tcf_0 () at liba.cpp:9
#2 0x00007ff8dadc230c in _execute_onexit_table
(table=table_at_entry=0x7ff8dadc7000 <atexit_table>) at C:/M/B/src/mingw-w64/
mingw-w64-crt/misc/onexit_table.c:69
#3 0x00007ff8dadc114c in _CRT_INIT (hDllHandle=hDllHandle_at_entry=0x7ff8dadc0000,
dwReason=dwReason_at_entry=0, lpreserved=lpreserved_at_entry=0x1)
    at C:/M/B/src/mingw-w64/mingw-w64-crt/crt/crtdll.c:130
#4 0x00007ff8dadc12e4 in __DllMainCRTStartup (hDllHandle=0x7ff8dadc0000,
dwReason=0, lpreserved=0x1) at C:/M/B/src/mingw-w64/mingw-w64-crt/crt/
crtdll.c:196
#5 0x00007ff8f577868f in ntdll!RtlActivateActivationContextUnsafeFast () from
C:\WINDOWS\SYSTEM32\ntdll.dll
#6 0x00007ff8f57a1096 in ntdll!LdrShutdownProcess () from C:
\WINDOWS\SYSTEM32\ntdll.dll
#7 0x00007ff8f57a0c8d in ntdll!RtlExitUserProcess () from C:
\WINDOWS\SYSTEM32\ntdll.dll
#8 0x00007ff8f49182ab in KERNEL32!ExitProcess () from C:
\WINDOWS\System32\kernel32.dll
#9 0x00007ff8f44ad7bd in msvcrt!_exit () from C:\WINDOWS\System32\msvcrt.dll
#10 0x00007ff8f44ade3b in msvcrt!_initterm_e () from C:
\WINDOWS\System32\msvcrt.dll
#11 0x00007ff71f751470 in main () at test.cpp:6

Backtrace with VS:
  * frame #0: 0x00007ff8ebd82480 liba.dll`S::~S at liba.cpp:7
    frame #1: 0x00007ff8ebd86b90 liba.dll``dynamic atexit destructor for 's'' +
16
    frame #2: 0x00007ff8f2b73abb KernelBase.dll`FlsGetValue + 27
    frame #3: 0x00007ff8f31ce1c9 ucrtbase.dll`_o_wcstok_s + 217
    frame #4: 0x00007ff8f31d006e ucrtbase.dll`_o_free + 222
    frame #5: 0x00007ff8f31cda1d ucrtbase.dll`_execute_onexit_table + 61
    frame #6: 0x00007ff8ebd82bf1
liba.dll`dllmain_crt_process_detach(is_terminating=<unavailable>) at
dll_dllmain.cpp:180

-- 
Thiago Macieira - thiago (AT) macieira.info - thiago (AT) kde.org
   Software Architect - Intel DCAI Cloud Engineering

Received on 2023-07-13 16:35:25