Skip to content

Instantly share code, notes, and snippets.

@jay
Last active March 12, 2024 13:23
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jay/423e12ea7f5b86c72f59719c8f5a006c to your computer and use it in GitHub Desktop.
Save jay/423e12ea7f5b86c72f59719c8f5a006c to your computer and use it in GitHub Desktop.
Test whether or not a DLL's atexit handler is called on FreeLibrary

This is a demonstration of how a DLL's atexit handler is called on FreeLibrary if Microsoft's CRT is used.


Mixing Cygwin and Microsoft's CRT does not seem to work well. However according to Cygwin it is possible:

https://cygwin.com/faq/faq.html#faq.programming.msvs-mingw

https://github.com/openunix/cygwin/blob/master/winsup/testsuite/winsup.api/cygload.cc

I tried a program linked to Microsoft's CRT and a DLL linked to cygwin's CRT and the atexit handlers weren't called.


If you use a program and DLL that both use Microsoft's CRT (doesn't have to be the same version) or both use Cygwin's CRT then the test should PASS:

>>> main
libfoo was loaded.
>>> libfoo_init
<<< libfoo_init
libfoo was initialized.
>>> libfoo atexit handler
<<< libfoo atexit handler
libfoo was unloaded.
PASS: libfoo's atexit handler ran before main end.
<<< main
>>> main atexit handler
<<< main atexit handler

Other references:

curl/curl#1055

openssl/openssl#1693

/* libfoo
Test that a DLL atexit handler is called during FreeLibrary.
gcc -Wall -Wextra -o main main.c
gcc -Wall -Wextra -shared -o libfoo.dll libfoo.c
cl.exe /W4 /MD main.c
cl.exe /W4 /LD /MD libfoo.c
If you withhold MD from either the results should be the same.
Ref https://github.com/openssl/openssl/pull/1693
https://gist.github.com/jay/423e12ea7f5b86c72f59719c8f5a006c
*/
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
int *atexit_handler_was_called = NULL;
void goodbye(void) {
fprintf(stderr, ">>> libfoo atexit handler\n");
fprintf(stderr, "<<< libfoo atexit handler\n");
*atexit_handler_was_called = TRUE;
}
__declspec(dllexport)
int libfoo_init(int *notify) {
fprintf(stderr, ">>> libfoo_init\n");
if(!notify)
return FALSE;
atexit_handler_was_called = notify;
*atexit_handler_was_called = FALSE;
if(atexit(goodbye))
return FALSE;
fprintf(stderr, "<<< libfoo_init\n");
return TRUE;
}
/* main
Test that a DLL atexit handler is called during FreeLibrary.
gcc -Wall -Wextra -o main main.c
gcc -Wall -Wextra -shared -o libfoo.dll libfoo.c
cl.exe /W4 /MD main.c
cl.exe /W4 /LD /MD libfoo.c
If you withhold MD from either the results should be the same.
Ref https://github.com/openssl/openssl/pull/1693
https://gist.github.com/jay/423e12ea7f5b86c72f59719c8f5a006c
*/
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
void goodbye(void) {
fprintf(stderr, ">>> main atexit handler\n");
fprintf(stderr, "<<< main atexit handler\n");
}
int main() {
HMODULE dll;
int (*libfoo_init)(int *);
int libfoo_atexit_handler_was_called = FALSE;
fprintf(stderr, ">>> main\n");
if(atexit(goodbye)) {
fprintf(stderr, "main atexit handler failed to register\n");
goto cleanup;
}
dll = LoadLibraryA("libfoo");
if(!dll) {
fprintf(stderr, "libfoo not found\n");
goto cleanup;
}
fprintf(stderr, "libfoo was loaded.\n");
libfoo_init = (int (*)(int *))GetProcAddress(dll, "libfoo_init");
if(!libfoo_init) {
fprintf(stderr, "libfoo does not have an libfoo_init function\n");
goto cleanup;
}
if(!libfoo_init(&libfoo_atexit_handler_was_called)) {
fprintf(stderr, "libfoo_init() failed.\n");
goto cleanup;
}
fprintf(stderr, "libfoo was initialized.\n");
FreeLibrary(dll);
fprintf(stderr, "libfoo was unloaded.\n");
cleanup:
if(libfoo_atexit_handler_was_called)
fprintf(stderr, "PASS: libfoo's atexit handler ran before main end.\n");
else
fprintf(stderr, "\a\aFAIL: libfoo's atexit handler hasn't run yet.\n");
fprintf(stderr, "<<< main\n");
return !libfoo_atexit_handler_was_called;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment