blob: 21a174061a4eae871af0233d26ed139d48337ecc [file]
#include <assert.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#include <windows.h>
/* mingw-w64 headers */
#include "libtest.h"
static int check_tls_section(HMODULE module)
{
IMAGE_DOS_HEADER *dos_header = (IMAGE_DOS_HEADER *)module; /* This cast does not work on Win32s, HMODULE is not IMAGE_DOS_HEADER* */
assert(dos_header->e_magic == IMAGE_DOS_SIGNATURE);
IMAGE_NT_HEADERS *nt_headers = (IMAGE_NT_HEADERS *)((BYTE *)dos_header + dos_header->e_lfanew);
assert(nt_headers->Signature == IMAGE_NT_SIGNATURE);
assert(nt_headers->FileHeader.SizeOfOptionalHeader >= offsetof(IMAGE_OPTIONAL_HEADER, DataDirectory));
assert(nt_headers->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR_MAGIC);
assert(nt_headers->FileHeader.SizeOfOptionalHeader >= offsetof(IMAGE_OPTIONAL_HEADER, DataDirectory[nt_headers->OptionalHeader.NumberOfRvaAndSizes]));
return
nt_headers->OptionalHeader.NumberOfRvaAndSizes > IMAGE_DIRECTORY_ENTRY_TLS &&
nt_headers->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_TLS].Size != 0 &&
nt_headers->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_TLS].VirtualAddress != 0;
}
#if defined(__i386__)
/* We need to make sure that we align the stack to 16 bytes for the sake of SSE */
__attribute__((force_align_arg_pointer))
#endif
static DWORD WINAPI thread_main(LPVOID user_data)
{
int (*register_func)(void);
int ret = 0;
register_func = (typeof(register_func))user_data;
if (register_func) {
printf("Registering for DLL_THREAD_DETACH\n");
if (register_func() != 0) {
printf("Error: Registration function for DLL_THREAD_DETACH failed\n");
ret = 1;
}
}
return ret;
}
int main(void)
{
mingw_test_init();
int ret = 0;
HMODULE lib;
int (*get_func)(void);
void (*set_var_func)(int *var);
int (*register_func)(void);
int var;
HANDLE thread;
int skip_thread;
/* Test for native PE TLS callback */
printf("libtls01.dll - BEGIN - Native PE TLS callback\n");
if (_osplatform != VER_PLATFORM_WIN32s && _winver < _WIN32_WINNT_VISTA) {
printf("libtls01.dll - Skipped because Win9x and WinNT pre-Vista systems do not call native PE TLS callbacks on LoadLibrary()\n");
} else {
lib = LoadLibraryA("libtls01.dll");
if (!lib) {
printf("Error: Cannot load library libtls01.dll: %lu\n", GetLastError());
ret = 1;
} else {
if (_osplatform != VER_PLATFORM_WIN32s) {
if (!check_tls_section(lib)) {
printf("Error: Library libtls01.dll does not have TLS section\n");
ret = 1;
} else {
printf("libtls01.dll - TLS section is present\n");
}
}
get_func = (typeof(get_func))(void(*)(void))GetProcAddress(lib, "get_process_attach");
if (!get_func) {
printf("Error: Cannot resolve get_process_attach symbol from library libtls01.dll: %lu\n", GetLastError());
ret = 1;
} else {
if (get_func() != 1) {
printf("Error: TLS callback in libtls01.dll was called %d times for DLL_PROCESS_ATTACH\n", get_func());
ret = 1;
} else {
printf("libtls01.dll - TLS callback for DLL_PROCESS_ATTACH was called\n");
}
}
get_func = (typeof(get_func))(void(*)(void))GetProcAddress(lib, "get_thread_attach");
if (!get_func) {
printf("Error: Cannot resolve get_thread_attach symbol from library libtls01.dll: %lu\n", GetLastError());
ret = 1;
}
set_var_func = (typeof(set_var_func))(void(*)(void))GetProcAddress(lib, "set_thread_detach_var");
if (!set_var_func) {
printf("Error: Cannot resolve set_thread_detach_var symbol from library libtls01.dll: %lu\n", GetLastError());
ret = 1;
} else {
set_var_func(&var);
}
var = 0;
skip_thread = 0;
thread = CreateThread(NULL, 0, thread_main, NULL, 0, &(DWORD){0} /*out: ThreadId*/);
if (!thread) {
if (GetLastError() != ERROR_CALL_NOT_IMPLEMENTED) {
printf("Error: Cannot spawn new thread: %lu\n", GetLastError());
ret = 1;
} else {
printf("Skipped thread subcase\n");
skip_thread = 1;
}
} else {
DWORD tret;
WaitForSingleObject(thread, INFINITE);
GetExitCodeThread(thread, &tret);
CloseHandle(thread);
if (tret) {
printf("Error: Thread failed\n");
ret = 1;
}
}
if (!skip_thread) {
if (get_func) {
if (get_func() != 1) {
printf("Error: TLS callback in libtls01.dll was called %d times for DLL_THREAD_ATTACH\n", get_func());
ret = 1;
} else {
printf("libtls01.dll - TLS callback for DLL_THREAD_ATTACH was called\n");
}
}
if (set_var_func) {
if (var != 1) {
printf("Error: TLS callback in libtls01.dll was called %d times for DLL_THREAD_DETACH\n", var);
ret = 1;
} else {
printf("libtls01.dll - TLS callback for DLL_THREAD_DETACH was called\n");
}
}
}
set_var_func = (typeof(set_var_func))(void(*)(void))GetProcAddress(lib, "set_process_detach_var");
if (!set_var_func) {
printf("Error: Cannot resolve set_process_detach_var symbol from library libtls01.dll: %lu\n", GetLastError());
ret = 1;
} else {
set_var_func(&var);
}
var = 0;
FreeLibrary(lib);
if (set_var_func) {
if (var != 1) {
printf("Error: TLS callback in libtls01.dll was called %d times for DLL_PROCESS_DETACH\n", var);
ret = 1;
} else {
printf("libtls01.dll - TLS callback for DLL_PROCESS_DETACH was called\n");
}
}
}
}
printf("libtls01.dll - END\n\n");
/* Test for CRT dyn TLS callback which uses PE TLS callback __dyn_tls_init */
printf("libtls02.dll - BEGIN - __dyn_tls_init\n");
if (_osplatform != VER_PLATFORM_WIN32s && _winver < _WIN32_WINNT_VISTA) {
printf("libtls02.dll - Skipped because Win9x and WinNT pre-Vista systems do not call native PE TLS callbacks on LoadLibrary()\n");
} else {
lib = LoadLibraryA("libtls02.dll");
if (!lib) {
printf("Error: Cannot load library libtls02.dll: %lu\n", GetLastError());
ret = 1;
} else {
if (_osplatform != VER_PLATFORM_WIN32s) {
if (!check_tls_section(lib)) {
printf("Error: Library libtls02.dll does not have TLS section\n");
ret = 1;
} else {
printf("libtls02.dll - TLS section is present\n");
}
}
get_func = (typeof(get_func))(void(*)(void))GetProcAddress(lib, "get_process_or_thread_attach");
if (!get_func) {
printf("Error: Cannot resolve get_process_attach symbol from library libtls02.dll: %lu\n", GetLastError());
ret = 1;
} else {
if (get_func() != 1) {
printf("Error: TLS callback in libtls02.dll was called %d times for DLL_PROCESS_ATTACH\n", get_func());
ret = 1;
} else {
printf("libtls02.dll - TLS callback for DLL_PROCESS_ATTACH was called\n");
}
skip_thread = 0;
thread = CreateThread(NULL, 0, thread_main, NULL, 0, &(DWORD){0} /*out: ThreadId*/);
if (!thread) {
if (GetLastError() != ERROR_CALL_NOT_IMPLEMENTED) {
printf("Error: Cannot spawn new thread: %lu\n", GetLastError());
ret = 1;
} else {
printf("Skipped thread subcase\n");
skip_thread = 1;
}
} else {
DWORD tret;
WaitForSingleObject(thread, INFINITE);
GetExitCodeThread(thread, &tret);
CloseHandle(thread);
if (tret) {
printf("Error: Thread failed\n");
ret = 1;
}
}
if (!skip_thread) {
if (get_func() != 2) {
printf("Error: TLS callback in libtls02.dll was called %d times for DLL_THREAD_ATTACH\n", get_func()-1);
ret = 1;
} else {
printf("libtls02.dll - TLS callback for DLL_THREAD_ATTACH was called\n");
}
}
}
FreeLibrary(lib);
}
}
printf("libtls02.dll - END\n\n");
/* Test for CRT dtor TLS callback via __tlregdtor which uses PE TLS callback __dyn_tls_dtor and PE TLS variables */
printf("libtls03.dll - BEGIN - __tlregdtor\n");
if (_winver < _WIN32_WINNT_VISTA) {
printf("libtls03.dll - Skipped because pre-Vista systems do not support native PE TLS variables for LoadLibrary()\n");
} else {
lib = LoadLibraryA("libtls03.dll");
if (!lib) {
printf("Error: Cannot load library libtls03.dll: %lu\n", GetLastError());
ret = 1;
} else {
if (_osplatform != VER_PLATFORM_WIN32s) {
if (!check_tls_section(lib)) {
printf("Error: Library libtls03.dll does not have TLS section\n");
ret = 1;
} else {
printf("libtls03.dll - TLS section is present\n");
}
}
set_var_func = (typeof(set_var_func))(void(*)(void))GetProcAddress(lib, "set_process_or_thread_detach_var");
if (!set_var_func) {
printf("Error: Cannot resolve set_process_or_thread_detach_var symbol from library libtls03.dll: %lu\n", GetLastError());
ret = 1;
} else {
set_var_func(&var);
}
register_func = (typeof(register_func))(void(*)(void))GetProcAddress(lib, "register_thread_detach_for_current_thread");
if (!register_func) {
printf("Error: Cannot resolve register_thread_detach_for_current_thread symbol from library libtls03.dll: %lu\n", GetLastError());
ret = 1;
} else {
printf("Registering for DLL_PROCESS_DETACH\n");
if (register_func() != 0) {
printf("Error: Registration function for DLL_PROCESS_DETACH failed\n");
ret = 1;
}
}
var = 0;
skip_thread = 0;
thread = CreateThread(NULL, 0, thread_main, (void *)register_func, 0, &(DWORD){0} /*out: ThreadId*/);
if (!thread) {
if (GetLastError() != ERROR_CALL_NOT_IMPLEMENTED) {
printf("Error: Cannot spawn new thread: %lu\n", GetLastError());
ret = 1;
} else {
printf("Skipped thread subcase\n");
skip_thread = 1;
}
} else {
DWORD tret;
WaitForSingleObject(thread, INFINITE);
GetExitCodeThread(thread, &tret);
CloseHandle(thread);
if (tret) {
printf("Error: Thread failed\n");
ret = 1;
}
}
if (!skip_thread) {
if (set_var_func) {
if (var != 1) {
printf("Error: TLS callback in libtls03.dll was called %d times for DLL_THREAD_DETACH\n", var);
ret = 1;
} else {
printf("libtls03.dll - TLS callback for DLL_THREAD_DETACH was called\n");
}
}
}
var = 0;
FreeLibrary(lib);
if (set_var_func) {
if (var != 1) {
printf("Error: TLS callback in libtls03.dll was called %d times for DLL_PROCESS_DETACH\n", var);
ret = 1;
} else {
printf("libtls03.dll - TLS callback for DLL_PROCESS_DETACH was called\n");
}
}
}
}
printf("libtls03.dll - END\n\n");
/* Tests for _onexit, atexit, __cxa_atexit, __mingw_cxa_atexit (they do not have to use native PE TLS callbacks) */
static const char *const libnames1[] = { "libtls04.dll", "libtls05.dll", "libtls06.dll", "libtls07.dll" };
static const char *const descrs1[] = { "_onexit", "atexit", "__cxa_atexit", "__mingw_cxa_atexit" };
for (size_t i = 0; i < sizeof(libnames1)/sizeof(libnames1[0]); i++) {
const char *const libname = libnames1[i];
const char *const descr = descrs1[i];
printf("%s - BEGIN - %s\n", libname, descr);
lib = LoadLibraryA(libname);
if (!lib) {
printf("Error: Cannot load library %s: %lu\n", libname, GetLastError());
ret = 1;
} else {
set_var_func = (typeof(set_var_func))(void(*)(void))GetProcAddress(lib, "set_process_detach_var");
if (!set_var_func) {
printf("Error: Cannot resolve set_process_detach_var symbol from library %s: %lu\n", libname, GetLastError());
ret = 1;
} else {
set_var_func(&var);
}
/* Run new thread just to verify that it will not call our process detach callback */
var = 0;
skip_thread = 0;
thread = CreateThread(NULL, 0, thread_main, NULL, 0, &(DWORD){0} /*out: ThreadId*/);
if (!thread) {
if (GetLastError() != ERROR_CALL_NOT_IMPLEMENTED) {
printf("Error: Cannot spawn new thread: %lu\n", GetLastError());
ret = 1;
} else {
printf("Skipped thread subcase\n");
skip_thread = 1;
}
} else {
DWORD tret;
WaitForSingleObject(thread, INFINITE);
GetExitCodeThread(thread, &tret);
CloseHandle(thread);
if (tret) {
printf("Error: Thread failed\n");
ret = 1;
}
}
if (!skip_thread) {
if (set_var_func) {
if (var != 0) {
printf("Error: TLS callback in %s was called %d times for DLL_THREAD_DETACH\n", libname, var);
ret = 1;
} else {
printf("%s - TLS callback for DLL_THREAD_DETACH was NOT called\n", libname);
}
}
}
var = 0;
FreeLibrary(lib);
if (set_var_func) {
if (var != 1) {
printf("Error: TLS callback in %s was called %d times for DLL_PROCESS_DETACH\n", libname, var);
ret = 1;
} else {
printf("%s - TLS callback for DLL_PROCESS_DETACH was called\n", libname);
}
}
}
printf("%s - END\n\n", libname);
}
/* Tests for __cxa_thread_atexit, __mingw_cxa_thread_atexit (they do not have to use native PE TLS callbacks) */
static const char *const libnames2[] = { "libtls08.dll", "libtls09.dll" };
static const char *const descrs2[] = { "__cxa_thread_atexit", "__mingw_cxa_thread_atexit" };
for (size_t i = 0; i < sizeof(libnames2)/sizeof(libnames2[0]); i++) {
const char *const libname = libnames2[i];
const char *const descr = descrs2[i];
printf("%s - BEGIN - %s\n", libname, descr);
lib = LoadLibraryA(libname);
if (!lib) {
printf("Error: Cannot load library %s: %lu\n", libname, GetLastError());
ret = 1;
} else {
set_var_func = (typeof(set_var_func))(void(*)(void))GetProcAddress(lib, "set_process_or_thread_detach_var");
if (!set_var_func) {
printf("Error: Cannot resolve set_process_detach_var symbol from library %s: %lu\n", libname, GetLastError());
ret = 1;
} else {
set_var_func(&var);
}
register_func = (typeof(register_func))(void(*)(void))GetProcAddress(lib, "register_thread_detach_for_current_thread");
if (!register_func) {
printf("Error: Cannot resolve register_thread_detach_for_current_thread symbol from library %s: %lu\n", libname, GetLastError());
ret = 1;
} else {
printf("Registering for DLL_PROCESS_DETACH\n");
if (register_func() != 0) {
printf("Error: Registration function for DLL_PROCESS_DETACH failed\n");
ret = 1;
}
}
var = 0;
skip_thread = 0;
thread = CreateThread(NULL, 0, thread_main, (void *)register_func, 0, &(DWORD){0} /*out: ThreadId*/);
if (!thread) {
if (GetLastError() != ERROR_CALL_NOT_IMPLEMENTED) {
printf("Error: Cannot spawn new thread: %lu\n", GetLastError());
ret = 1;
} else {
printf("Skipped thread subcase\n");
skip_thread = 1;
}
} else {
DWORD tret;
WaitForSingleObject(thread, INFINITE);
GetExitCodeThread(thread, &tret);
CloseHandle(thread);
if (tret) {
printf("Error: Thread failed\n");
ret = 1;
}
}
if (!skip_thread) {
if (set_var_func) {
if (var != 1) {
printf("Error: TLS callback in %s was called %d times for DLL_THREAD_DETACH\n", libname, var);
ret = 1;
} else {
printf("%s - TLS callback for DLL_THREAD_DETACH was called\n", libname);
}
}
}
var = 0;
FreeLibrary(lib);
if (set_var_func) {
if (var != 1) {
printf("Error: TLS callback in %s was called %d times for DLL_PROCESS_DETACH\n", libname, var);
ret = 1;
} else {
printf("%s - TLS callback for DLL_PROCESS_DETACH was called\n", libname);
}
}
}
printf("%s - END\n\n", libname);
}
/* Tests for __mingwthr_key_dtor, ___w64_mingwthr_add_key_dtor (they do not have to use native PE TLS callbacks) */
static const char *const libnames3[] = { "libtls10.dll", "libtls11.dll" };
static const char *const descrs3[] = { "__mingwthr_key_dtor", "___w64_mingwthr_add_key_dtor" };
for (size_t i = 0; i < sizeof(libnames3)/sizeof(libnames3[0]); i++) {
const char *const libname = libnames3[i];
const char *const descr = descrs3[i];
printf("%s - BEGIN - %s\n", libname, descr);
lib = LoadLibraryA(libname);
if (!lib) {
printf("Error: Cannot load library %s: %lu\n", libname, GetLastError());
ret = 1;
} else {
set_var_func = (typeof(set_var_func))(void(*)(void))GetProcAddress(lib, "set_thread_detach_var");
if (!set_var_func) {
printf("Error: Cannot resolve set_process_detach_var symbol from library %s: %lu\n", libname, GetLastError());
ret = 1;
} else {
set_var_func(&var);
}
var = 0;
skip_thread = 0;
thread = CreateThread(NULL, 0, thread_main, NULL, 0, &(DWORD){0} /*out: ThreadId*/);
if (!thread) {
if (GetLastError() != ERROR_CALL_NOT_IMPLEMENTED) {
printf("Error: Cannot spawn new thread: %lu\n", GetLastError());
ret = 1;
} else {
printf("Skipped thread subcase\n");
skip_thread = 1;
}
} else {
DWORD tret;
WaitForSingleObject(thread, INFINITE);
GetExitCodeThread(thread, &tret);
CloseHandle(thread);
if (tret) {
printf("Error: Thread failed\n");
ret = 1;
}
}
if (!skip_thread) {
if (set_var_func) {
if (var != 1) {
printf("Error: TLS callback in %s was called %d times for DLL_THREAD_DETACH\n", libname, var);
ret = 1;
} else {
printf("%s - TLS callback for DLL_THREAD_DETACH was called\n", libname);
}
}
}
var = 0;
FreeLibrary(lib);
if (set_var_func) {
if (var != 0) {
printf("Error: TLS callback in %s was called %d times for DLL_PROCESS_DETACH\n", libname, var);
ret = 1;
} else {
printf("%s - TLS callback for DLL_PROCESS_DETACH was NOT called\n", libname);
}
}
}
printf("%s - END\n\n", libname);
}
if (ret == 0) printf("ALL OK\n"); else printf("FAILED\n");
return ret;
}