crt: Improve mingw-w64 _register_thread_local_exe_atexit_callback implementation which is used for msvcrt builds

Check that the _register_thread_local_exe_atexit_callback function is not
called multiple times. UCRT also disallow to call it multiple times.

Improve guard in run_callback to ensure that the callback is not executed
more times even if the run_callback is called more times.

Add mingw-w64 wrapper around the CRT exit() function which will register
the callback via atexit before calling the CRT exit function. This ensures
that the CRT exit function would call the callback as the first atexit
callback.

Signed-off-by: LIU Hao <lh_mouse@126.com>
diff --git a/mingw-w64-crt/Makefile.am b/mingw-w64-crt/Makefile.am
index 61c2b3d..07c050d 100644
--- a/mingw-w64-crt/Makefile.am
+++ b/mingw-w64-crt/Makefile.am
@@ -182,6 +182,7 @@
   misc/__mingw_mbrtowc_cp.c \
   misc/__mingw_wcrtomb_cp.c \
   misc/_onexit.c \
+  misc/exit.c \
   misc/mbrlen.c \
   misc/mbrtowc.c \
   misc/mbsinit.c \
diff --git a/mingw-w64-crt/lib-common/msvcr120_app.def.in b/mingw-w64-crt/lib-common/msvcr120_app.def.in
index 06260ee..6bbbf54 100644
--- a/mingw-w64-crt/lib-common/msvcr120_app.def.in
+++ b/mingw-w64-crt/lib-common/msvcr120_app.def.in
@@ -1970,7 +1970,7 @@
 F_ARM32(erfcl) ; Can't use long double functions from the CRT on x86
 erff
 F_ARM32(erfl) ; Can't use long double functions from the CRT on x86
-exit
+__msvcrt_exit DATA == exit ; mingw-w64 provides exit() function as wrapper around renamed __msvcrt_exit symbol
 exp
 exp2
 exp2f
diff --git a/mingw-w64-crt/lib-common/msvcrt.def.in b/mingw-w64-crt/lib-common/msvcrt.def.in
index 39ece04..fdf8eb8 100644
--- a/mingw-w64-crt/lib-common/msvcrt.def.in
+++ b/mingw-w64-crt/lib-common/msvcrt.def.in
@@ -956,7 +956,7 @@
 ctime
 difftime
 div
-exit
+__msvcrt_exit DATA == exit ; mingw-w64 provides exit() function as wrapper around renamed __msvcrt_exit symbol
 exp F_X86_NATIVE(DATA)
 fabs DATA
 fclose
diff --git a/mingw-w64-crt/lib32/crtdll.def.in b/mingw-w64-crt/lib32/crtdll.def.in
index 154d7ad..5dd3c99 100644
--- a/mingw-w64-crt/lib32/crtdll.def.in
+++ b/mingw-w64-crt/lib32/crtdll.def.in
@@ -350,7 +350,7 @@
 ctime
 difftime
 div
-exit
+__msvcrt_exit DATA == exit ; mingw-w64 provides exit() function as wrapper around renamed __msvcrt_exit symbol
 exp DATA
 fabs DATA
 fclose
diff --git a/mingw-w64-crt/lib32/msvcr100.def.in b/mingw-w64-crt/lib32/msvcr100.def.in
index 1db3e07..a08fb20 100644
--- a/mingw-w64-crt/lib32/msvcr100.def.in
+++ b/mingw-w64-crt/lib32/msvcr100.def.in
@@ -1672,7 +1672,7 @@
 ; If we implement cosh too, we can set it to DATA only.
 cosh
 div
-exit
+__msvcrt_exit DATA == exit ; mingw-w64 provides exit() function as wrapper around renamed __msvcrt_exit symbol
 exp DATA
 fabs DATA
 fclose
diff --git a/mingw-w64-crt/lib32/msvcr100d.def.in b/mingw-w64-crt/lib32/msvcr100d.def.in
index 9899fe0..6939f0d 100644
--- a/mingw-w64-crt/lib32/msvcr100d.def.in
+++ b/mingw-w64-crt/lib32/msvcr100d.def.in
@@ -1738,7 +1738,7 @@
 cos DATA ; overwritten
 cosh
 div
-exit
+__msvcrt_exit DATA == exit ; mingw-w64 provides exit() function as wrapper around renamed __msvcrt_exit symbol
 exp DATA ; overwritten
 fabs DATA ; overwritten
 fclose
diff --git a/mingw-w64-crt/lib32/msvcr110.def.in b/mingw-w64-crt/lib32/msvcr110.def.in
index 1a4db81..9a7f16f 100644
--- a/mingw-w64-crt/lib32/msvcr110.def.in
+++ b/mingw-w64-crt/lib32/msvcr110.def.in
@@ -1804,7 +1804,7 @@
 cos DATA
 cosh ; If we implement cosh, we can set it to DATA only.
 div
-exit
+__msvcrt_exit DATA == exit ; mingw-w64 provides exit() function as wrapper around renamed __msvcrt_exit symbol
 exp DATA
 fabs DATA
 fclose
diff --git a/mingw-w64-crt/lib32/msvcr110d.def.in b/mingw-w64-crt/lib32/msvcr110d.def.in
index 0b52690..bf4ba2d 100644
--- a/mingw-w64-crt/lib32/msvcr110d.def.in
+++ b/mingw-w64-crt/lib32/msvcr110d.def.in
@@ -1871,7 +1871,7 @@
 cos DATA ; overwritten
 cosh
 div
-exit
+__msvcrt_exit DATA == exit ; mingw-w64 provides exit() function as wrapper around renamed __msvcrt_exit symbol
 exp DATA ; overwritten
 fabs DATA ; overwritten
 fclose
diff --git a/mingw-w64-crt/lib32/msvcr120.def.in b/mingw-w64-crt/lib32/msvcr120.def.in
index af88bbb..43ae5cb 100644
--- a/mingw-w64-crt/lib32/msvcr120.def.in
+++ b/mingw-w64-crt/lib32/msvcr120.def.in
@@ -1943,7 +1943,7 @@
 ; erfcl ; Can't use long double functions from the CRT on x86
 erff
 ; erfl ; Can't use long double functions from the CRT on x86
-exit
+__msvcrt_exit DATA == exit ; mingw-w64 provides exit() function as wrapper around renamed __msvcrt_exit symbol
 exp
 exp2
 exp2f
diff --git a/mingw-w64-crt/lib32/msvcr120d.def.in b/mingw-w64-crt/lib32/msvcr120d.def.in
index b8d4c9b..df3d1a0 100644
--- a/mingw-w64-crt/lib32/msvcr120d.def.in
+++ b/mingw-w64-crt/lib32/msvcr120d.def.in
@@ -2010,7 +2010,7 @@
 ; erfcl ; Can't use long double functions from the CRT on x86
 erff
 ; erfl ; Can't use long double functions from the CRT on x86
-exit
+__msvcrt_exit DATA == exit ; mingw-w64 provides exit() function as wrapper around renamed __msvcrt_exit symbol
 exp
 exp2
 exp2f
diff --git a/mingw-w64-crt/lib32/msvcr40d.def.in b/mingw-w64-crt/lib32/msvcr40d.def.in
index aced5b3..bcf0e6a 100644
--- a/mingw-w64-crt/lib32/msvcr40d.def.in
+++ b/mingw-w64-crt/lib32/msvcr40d.def.in
@@ -1475,7 +1475,7 @@
 ctime
 difftime
 div
-exit
+__msvcrt_exit DATA == exit ; mingw-w64 provides exit() function as wrapper around renamed __msvcrt_exit symbol
 exp
 fabs
 fclose
diff --git a/mingw-w64-crt/lib32/msvcr70.def.in b/mingw-w64-crt/lib32/msvcr70.def.in
index 53e2bfe..1c44a28 100644
--- a/mingw-w64-crt/lib32/msvcr70.def.in
+++ b/mingw-w64-crt/lib32/msvcr70.def.in
@@ -725,7 +725,7 @@
 ctime
 difftime
 div
-exit
+__msvcrt_exit DATA == exit ; mingw-w64 provides exit() function as wrapper around renamed __msvcrt_exit symbol
 exp
 fabs
 fclose
diff --git a/mingw-w64-crt/lib32/msvcr70d.def.in b/mingw-w64-crt/lib32/msvcr70d.def.in
index 7784d76..1eb2a6c 100644
--- a/mingw-w64-crt/lib32/msvcr70d.def.in
+++ b/mingw-w64-crt/lib32/msvcr70d.def.in
@@ -769,7 +769,7 @@
 ctime
 difftime
 div
-exit
+__msvcrt_exit DATA == exit ; mingw-w64 provides exit() function as wrapper around renamed __msvcrt_exit symbol
 exp
 fabs
 fclose
diff --git a/mingw-w64-crt/lib32/msvcr71.def.in b/mingw-w64-crt/lib32/msvcr71.def.in
index aa33cd3..576109f 100644
--- a/mingw-w64-crt/lib32/msvcr71.def.in
+++ b/mingw-w64-crt/lib32/msvcr71.def.in
@@ -719,7 +719,7 @@
 ctime
 difftime
 div
-exit
+__msvcrt_exit DATA == exit ; mingw-w64 provides exit() function as wrapper around renamed __msvcrt_exit symbol
 exp
 fabs
 fclose
diff --git a/mingw-w64-crt/lib32/msvcr71d.def.in b/mingw-w64-crt/lib32/msvcr71d.def.in
index a317d1e..f64aaa0 100644
--- a/mingw-w64-crt/lib32/msvcr71d.def.in
+++ b/mingw-w64-crt/lib32/msvcr71d.def.in
@@ -763,7 +763,7 @@
 ctime
 difftime
 div
-exit
+__msvcrt_exit DATA == exit ; mingw-w64 provides exit() function as wrapper around renamed __msvcrt_exit symbol
 exp
 fabs
 fclose
diff --git a/mingw-w64-crt/lib32/msvcr80.def.in b/mingw-w64-crt/lib32/msvcr80.def.in
index 7d57998..2153e69 100644
--- a/mingw-w64-crt/lib32/msvcr80.def.in
+++ b/mingw-w64-crt/lib32/msvcr80.def.in
@@ -1312,7 +1312,7 @@
 cos DATA
 cosh ; If we implement cosh, we can set it to DATA only.
 div
-exit
+__msvcrt_exit DATA == exit ; mingw-w64 provides exit() function as wrapper around renamed __msvcrt_exit symbol
 exp DATA
 fabs DATA
 fclose
diff --git a/mingw-w64-crt/lib32/msvcr80d.def.in b/mingw-w64-crt/lib32/msvcr80d.def.in
index d4deb17..3597f3a 100644
--- a/mingw-w64-crt/lib32/msvcr80d.def.in
+++ b/mingw-w64-crt/lib32/msvcr80d.def.in
@@ -1395,7 +1395,7 @@
 cos DATA ; overwritten
 cosh
 div
-exit
+__msvcrt_exit DATA == exit ; mingw-w64 provides exit() function as wrapper around renamed __msvcrt_exit symbol
 exp DATA ; overwritten
 fabs DATA ; overwritten
 fclose
diff --git a/mingw-w64-crt/lib32/msvcr90.def.in b/mingw-w64-crt/lib32/msvcr90.def.in
index a6f5752..5c4c9c2 100644
--- a/mingw-w64-crt/lib32/msvcr90.def.in
+++ b/mingw-w64-crt/lib32/msvcr90.def.in
@@ -1306,7 +1306,7 @@
 ; If we have cosh implementation, we can set it to DATA only.
 cosh
 div
-exit
+__msvcrt_exit DATA == exit ; mingw-w64 provides exit() function as wrapper around renamed __msvcrt_exit symbol
 exp DATA
 fabs DATA
 fclose
diff --git a/mingw-w64-crt/lib32/msvcr90d.def.in b/mingw-w64-crt/lib32/msvcr90d.def.in
index eb4f68e..f636d34 100644
--- a/mingw-w64-crt/lib32/msvcr90d.def.in
+++ b/mingw-w64-crt/lib32/msvcr90d.def.in
@@ -1378,7 +1378,7 @@
 ; If we implement cosh too, we can set it to DATA only.
 cosh
 div
-exit
+__msvcrt_exit DATA == exit ; mingw-w64 provides exit() function as wrapper around renamed __msvcrt_exit symbol
 exp DATA
 fabs DATA
 fclose
diff --git a/mingw-w64-crt/lib32/msvcrt10.def.in b/mingw-w64-crt/lib32/msvcrt10.def.in
index 8a7d408..4ba98bf 100644
--- a/mingw-w64-crt/lib32/msvcrt10.def.in
+++ b/mingw-w64-crt/lib32/msvcrt10.def.in
@@ -1126,7 +1126,7 @@
 ctime
 difftime
 div
-exit
+__msvcrt_exit DATA == exit ; mingw-w64 provides exit() function as wrapper around renamed __msvcrt_exit symbol
 exp
 fabs
 fclose
diff --git a/mingw-w64-crt/lib32/msvcrt20.def.in b/mingw-w64-crt/lib32/msvcrt20.def.in
index 8a87561..ee35087 100644
--- a/mingw-w64-crt/lib32/msvcrt20.def.in
+++ b/mingw-w64-crt/lib32/msvcrt20.def.in
@@ -1343,7 +1343,7 @@
 ctime
 difftime
 div
-exit
+__msvcrt_exit DATA == exit ; mingw-w64 provides exit() function as wrapper around renamed __msvcrt_exit symbol
 exp
 fabs
 fclose
diff --git a/mingw-w64-crt/lib32/msvcrt40.def.in b/mingw-w64-crt/lib32/msvcrt40.def.in
index 4c698c1..6347304 100644
--- a/mingw-w64-crt/lib32/msvcrt40.def.in
+++ b/mingw-w64-crt/lib32/msvcrt40.def.in
@@ -1443,7 +1443,7 @@
 ctime
 difftime
 div
-exit
+__msvcrt_exit DATA == exit ; mingw-w64 provides exit() function as wrapper around renamed __msvcrt_exit symbol
 exp
 fabs
 fclose
diff --git a/mingw-w64-crt/lib32/msvcrtd.def.in b/mingw-w64-crt/lib32/msvcrtd.def.in
index ea0c17c..130113d 100644
--- a/mingw-w64-crt/lib32/msvcrtd.def.in
+++ b/mingw-w64-crt/lib32/msvcrtd.def.in
@@ -669,7 +669,7 @@
 ctime
 difftime
 div
-exit
+__msvcrt_exit DATA == exit ; mingw-w64 provides exit() function as wrapper around renamed __msvcrt_exit symbol
 exp
 fabs
 fclose
diff --git a/mingw-w64-crt/lib64/msvcr100.def.in b/mingw-w64-crt/lib64/msvcr100.def.in
index 2e7991c..b082f74 100644
--- a/mingw-w64-crt/lib64/msvcr100.def.in
+++ b/mingw-w64-crt/lib64/msvcr100.def.in
@@ -1624,7 +1624,7 @@
 cosh
 coshf DATA
 div
-exit
+__msvcrt_exit DATA == exit ; mingw-w64 provides exit() function as wrapper around renamed __msvcrt_exit symbol
 exp DATA
 expf DATA
 fabs DATA
diff --git a/mingw-w64-crt/lib64/msvcr100d.def.in b/mingw-w64-crt/lib64/msvcr100d.def.in
index 26c568d..b737e02 100644
--- a/mingw-w64-crt/lib64/msvcr100d.def.in
+++ b/mingw-w64-crt/lib64/msvcr100d.def.in
@@ -1689,7 +1689,7 @@
 cosh
 coshf DATA ; overwritten
 div
-exit
+__msvcrt_exit DATA == exit ; mingw-w64 provides exit() function as wrapper around renamed __msvcrt_exit symbol
 exp DATA ; overwritten
 expf DATA ; overwritten
 fabs DATA ; overwritten
diff --git a/mingw-w64-crt/lib64/msvcr110.def.in b/mingw-w64-crt/lib64/msvcr110.def.in
index 83362bc..9df2ee9 100644
--- a/mingw-w64-crt/lib64/msvcr110.def.in
+++ b/mingw-w64-crt/lib64/msvcr110.def.in
@@ -1748,7 +1748,7 @@
 cosh
 coshf
 div
-exit
+__msvcrt_exit DATA == exit ; mingw-w64 provides exit() function as wrapper around renamed __msvcrt_exit symbol
 exp
 expf
 fabs
diff --git a/mingw-w64-crt/lib64/msvcr110d.def.in b/mingw-w64-crt/lib64/msvcr110d.def.in
index 6217687..c657ad6 100644
--- a/mingw-w64-crt/lib64/msvcr110d.def.in
+++ b/mingw-w64-crt/lib64/msvcr110d.def.in
@@ -1813,7 +1813,7 @@
 cosh
 coshf
 div
-exit
+__msvcrt_exit DATA == exit ; mingw-w64 provides exit() function as wrapper around renamed __msvcrt_exit symbol
 exp
 expf
 fabs
diff --git a/mingw-w64-crt/lib64/msvcr120.def.in b/mingw-w64-crt/lib64/msvcr120.def.in
index 9095011..130f10f 100644
--- a/mingw-w64-crt/lib64/msvcr120.def.in
+++ b/mingw-w64-crt/lib64/msvcr120.def.in
@@ -1886,7 +1886,7 @@
 ; erfcl ; Can't use long double functions from the CRT on x86
 erff
 ; erfl ; Can't use long double functions from the CRT on x86
-exit
+__msvcrt_exit DATA == exit ; mingw-w64 provides exit() function as wrapper around renamed __msvcrt_exit symbol
 exp
 exp2
 exp2f
diff --git a/mingw-w64-crt/lib64/msvcr120d.def.in b/mingw-w64-crt/lib64/msvcr120d.def.in
index 31c3258..278ec14 100644
--- a/mingw-w64-crt/lib64/msvcr120d.def.in
+++ b/mingw-w64-crt/lib64/msvcr120d.def.in
@@ -1951,7 +1951,7 @@
 ; erfcl ; Can't use long double functions from the CRT on x86
 erff
 ; erfl ; Can't use long double functions from the CRT on x86
-exit
+__msvcrt_exit DATA == exit ; mingw-w64 provides exit() function as wrapper around renamed __msvcrt_exit symbol
 exp
 exp2
 exp2f
diff --git a/mingw-w64-crt/lib64/msvcr80.def.in b/mingw-w64-crt/lib64/msvcr80.def.in
index f81f10e..a46fbae 100644
--- a/mingw-w64-crt/lib64/msvcr80.def.in
+++ b/mingw-w64-crt/lib64/msvcr80.def.in
@@ -1247,7 +1247,7 @@
 cosh
 coshf
 div
-exit
+__msvcrt_exit DATA == exit ; mingw-w64 provides exit() function as wrapper around renamed __msvcrt_exit symbol
 exp DATA
 expf DATA
 fabs
diff --git a/mingw-w64-crt/lib64/msvcr80d.def.in b/mingw-w64-crt/lib64/msvcr80d.def.in
index 3fbac94..dc2ece7 100644
--- a/mingw-w64-crt/lib64/msvcr80d.def.in
+++ b/mingw-w64-crt/lib64/msvcr80d.def.in
@@ -1324,7 +1324,7 @@
 cosh
 coshf
 div
-exit
+__msvcrt_exit DATA == exit ; mingw-w64 provides exit() function as wrapper around renamed __msvcrt_exit symbol
 exp DATA ; overwritten
 expf DATA ; overwritten
 fabs
diff --git a/mingw-w64-crt/lib64/msvcr90.def.in b/mingw-w64-crt/lib64/msvcr90.def.in
index 3efea26..2282832 100644
--- a/mingw-w64-crt/lib64/msvcr90.def.in
+++ b/mingw-w64-crt/lib64/msvcr90.def.in
@@ -1244,7 +1244,7 @@
 cosh
 coshf DATA
 div
-exit
+__msvcrt_exit DATA == exit ; mingw-w64 provides exit() function as wrapper around renamed __msvcrt_exit symbol
 exp DATA
 expf DATA
 fabs DATA
diff --git a/mingw-w64-crt/lib64/msvcr90d.def.in b/mingw-w64-crt/lib64/msvcr90d.def.in
index c95a741..5bdfa14 100644
--- a/mingw-w64-crt/lib64/msvcr90d.def.in
+++ b/mingw-w64-crt/lib64/msvcr90d.def.in
@@ -1310,7 +1310,7 @@
 cosh
 coshf
 div
-exit
+__msvcrt_exit DATA == exit ; mingw-w64 provides exit() function as wrapper around renamed __msvcrt_exit symbol
 exp DATA
 expf DATA
 fabs
diff --git a/mingw-w64-crt/libarm32/msvcr110.def.in b/mingw-w64-crt/libarm32/msvcr110.def.in
index 3c067e0..4decd8f 100644
--- a/mingw-w64-crt/libarm32/msvcr110.def.in
+++ b/mingw-w64-crt/libarm32/msvcr110.def.in
@@ -1734,7 +1734,7 @@
 cosh
 coshf
 div
-exit
+__msvcrt_exit DATA == exit ; mingw-w64 provides exit() function as wrapper around renamed __msvcrt_exit symbol
 exp
 expf
 fabs
diff --git a/mingw-w64-crt/libarm32/msvcr110d.def.in b/mingw-w64-crt/libarm32/msvcr110d.def.in
index 7164dbb..81fc746 100644
--- a/mingw-w64-crt/libarm32/msvcr110d.def.in
+++ b/mingw-w64-crt/libarm32/msvcr110d.def.in
@@ -1799,7 +1799,7 @@
 cosh
 coshf
 div
-exit
+__msvcrt_exit DATA == exit ; mingw-w64 provides exit() function as wrapper around renamed __msvcrt_exit symbol
 exp
 expf
 fabs
diff --git a/mingw-w64-crt/libarm32/msvcr120.def.in b/mingw-w64-crt/libarm32/msvcr120.def.in
index 8e3b65f..2baa561 100644
--- a/mingw-w64-crt/libarm32/msvcr120.def.in
+++ b/mingw-w64-crt/libarm32/msvcr120.def.in
@@ -1853,7 +1853,7 @@
 erfcl
 erff
 erfl
-exit
+__msvcrt_exit DATA == exit ; mingw-w64 provides exit() function as wrapper around renamed __msvcrt_exit symbol
 exp
 exp2
 exp2f
diff --git a/mingw-w64-crt/libarm32/msvcr120d.def.in b/mingw-w64-crt/libarm32/msvcr120d.def.in
index ef27891..afd9d45 100644
--- a/mingw-w64-crt/libarm32/msvcr120d.def.in
+++ b/mingw-w64-crt/libarm32/msvcr120d.def.in
@@ -1918,7 +1918,7 @@
 erfcl
 erff
 erfl
-exit
+__msvcrt_exit DATA == exit ; mingw-w64 provides exit() function as wrapper around renamed __msvcrt_exit symbol
 exp
 exp2
 exp2f
diff --git a/mingw-w64-crt/misc/exit.c b/mingw-w64-crt/misc/exit.c
new file mode 100644
index 0000000..88e1b3a
--- /dev/null
+++ b/mingw-w64-crt/misc/exit.c
@@ -0,0 +1,21 @@
+/**
+ * This file has no copyright assigned and is placed in the Public Domain.
+ * This file is part of the mingw-w64 runtime package.
+ * No warranty is given; refer to the file DISCLAIMER.PD within this package.
+ */
+
+#include <stdlib.h>
+
+void (__cdecl *__mingw_prepare_thread_local_exe_atexit_callback_ptr)(void) __attribute__((common)); /* tentative */
+extern void (__cdecl *__MINGW_IMP_SYMBOL(__msvcrt_exit))(int code) __MINGW_ATTRIB_NORETURN;
+
+__MINGW_ATTRIB_NORETURN void __cdecl exit(int code)
+{
+  /* __mingw_prepare_thread_local_exe_atexit_callback_ptr is thread safe and
+   * also can be called multiple times. Registration is done under CRT lock
+   * and execution of the registered callback is done exactly once. */
+  if (__mingw_prepare_thread_local_exe_atexit_callback_ptr)
+    __mingw_prepare_thread_local_exe_atexit_callback_ptr();
+  __MINGW_IMP_SYMBOL(__msvcrt_exit)(code);
+}
+typeof(exit) *__MINGW_IMP_SYMBOL(exit) = exit;
diff --git a/mingw-w64-crt/misc/register_tls_atexit.c b/mingw-w64-crt/misc/register_tls_atexit.c
index 09d9270..1875a67 100644
--- a/mingw-w64-crt/misc/register_tls_atexit.c
+++ b/mingw-w64-crt/misc/register_tls_atexit.c
@@ -5,25 +5,42 @@
  */
 
 #include <process.h>
-#include <windows.h>
-#include <winnt.h>
 #include <stdlib.h>
+#include <internal.h>
 
 static _tls_callback_type callback;
 
-static void run_callback(void)
+static void __cdecl run_callback(void)
 {
+  /* Ensure that callback is called only once. */
+  static int called = 0;
+  if (called)
+    return;
+  called = 1;
   if (callback)
     callback(NULL, DLL_PROCESS_DETACH, 0);
-  callback = NULL;
 }
 
+/* mingw-w64 exit() wrapper calls this function pointer prior CRT atexit() call
+ * to ensure that the registered callback is called before any other callback.
+ * If some other code calls the exported exit DLL function directly then the
+ * atexit() call inside _register_thread_local_exe_atexit_callback guarantees
+ * at least that the callback is called (but does not have to as a first one). */
+static void __cdecl set_atexit_callback(void)
+{
+  atexit(run_callback);
+}
+void (__cdecl *__mingw_prepare_thread_local_exe_atexit_callback_ptr)(void) = set_atexit_callback;
+
 void __cdecl _register_thread_local_exe_atexit_callback(_tls_callback_type cb)
 {
+  /* _register_thread_local_exe_atexit_callback cannot be called multiple times. */
+  if (callback)
+    _amsg_exit(10); /* _RT_ABORT */
   callback = cb;
   /* This should guarantee that the callback is called. It won't be run in the
    * exact right spot as intended to, but it will be run. */
-  atexit(run_callback);
+  set_atexit_callback();
 }
 
 typeof(_register_thread_local_exe_atexit_callback) *__MINGW_IMP_SYMBOL(_register_thread_local_exe_atexit_callback) = _register_thread_local_exe_atexit_callback;