thread_local переменные могут конструироваться до обычных глобальных переменных.
Это может происходить, если кто-то создаёт thread до того, как все глобальные объекты проинициализированы,
для этой нити и будет создан инстанс thread_local переменной.
Казалось бы, в здравом уме никто сам это делать не будет.
Но сторонние приложения тоже могут инджектить свои DLLки.
И это не обязательно malware, могут быть, например, и anti-malware.
Собственно, судя по дампу, там anti-malware.
Ещё одна деталь: для сторонних нитей конструктор thread_local вызывается из TLS-callback, а внутри PE Loader в ntdll все ислючения из TLS-коллбэков по-тихому ловятся.
(ловятся как SEH, т.е. если бросить С++ исключение, то С++ объект от него утечёт)
Так, легко не знать, что ситуация имеет место, пока не подебажишься.
Вывод: не пытайтесь ничего сложного делать в конструкторе thread_local переменной, если хотите работать в Windows.
Что же касается деструкторов thread_local, то с ними, думаю, всё в порядке.
Для нити main (или нити, вызвавшей exit) в силе комментарий из exit.cpp (200), снабженный даже ссылкой на стандарт:
// If this module has any dynamically initialized
// __declspec(thread) variables, then we invoke their
// destruction for the primary thread. All thread_local
// destructors are sequenced before any atexit calls or static
// object destructors (3.6.3/1)
Других нитей как бы не должно быть, и если они есть, то для них просто не будет вызван TLS-callback с DLL_THREAD_DETACH
(объект как бы утечёт вместе со всем остальным, что утекло от работающей нити).