1 year ago

#76480

test-img

adurdin

How to disable static tls with mingw32 gcc?

I am building a win32 dll using mingw32 and gcc (tdmgcc32 9.2.0), that is a plugin to an existing, win32 exe. But when the exe tries to load it, an access violation occurs within the LoadLibraryA() call. The stack trace shows:

    ntdll.dll!LdrpAllocateTlsEntry()
    ntdll.dll!LdrpHandleTlsData()
    ntdll.dll!LdrpDoPostSnapWork()
    ntdll.dll!_LdrpSnapModule@4()
    ntdll.dll!LdrpProcessWork()
    ntdll!LdrpDrawinWorkQueue()
    ntdll!LdrpLoadDllInternal()
    ntdll!LdrLoadDll()
    KernelBase.dll!LoadLibraryExW()
    KernelBase.dll!LoadLibraryA()

In trying to understand this, I came across this note:

Statically declared TLS data objects can be used only in statically loaded image files. This fact makes it unreliable to use static TLS data in a DLL unless you know that the DLL, or anything statically linked with it, will never be loaded dynamically with the LoadLibrary API function.

And additionally:

On Windows operating systems before Windows Vista, __declspec( thread ) has some limitations. If a DLL declares any data or object as __declspec( thread ), it can cause a protection fault if dynamically loaded. ...

(Now, I am using Windows 10, but the exe that is loading the dll was built for Windows 2000)

Continuing to investigate, I found KB118816 that restates the issue, but adds:

To determine whether a DLL uses static TLS, the Dumpbin.exe tool can be used to dump the header information. A DLL has static TLS if the OPTIONAL HEADER VALUES contains a size that is greater than 0 (zero) in the Thread Storage Directory

And running dumpbin /headers on my dll confirmed it had static TLS data:

30004 [      18] RVA [size] of Thread Storage Directory

Now, none of my code in the dll uses __declspec(thread), and in fact the TLS data is the same size when I build an empty dll; so I presume this is being used for std initialization or something.

So my question is: how can I prevent gcc generating static TLS data? is there a compile option to make it generate code that uses TlsAlloc() or something instead?

The compile flags I am using right now are:

-W -Wall -Wno-unused-parameter -std=c++11 -masm=att -fno-pcc-struct-return -mms-bitfields

And the flags to dllwrap/ld:

--target i386-mingw32 -mwindows -mdll -Wl,--enable-auto-image-base

Update:

I found that this exact situation I describe is detailed in MinGW bug 1557 from 2011, which was closed without being addressed as "out of date". In the comments, Cesar Strauss identifies libmingw32.a as the source of the static tls data, and I have confirmed that is still the case in my situation.

This is exactly what happened to my DLLs. My hook DLL is being loaded at the start time of any executable (via LoadPerProcess registry entry), and in some cases loading my DLL will trigger a segmentation fault (Windows will hide this fault, thus user won't observe it). However, the functionality of my DLL will be, obviously, missing.

...

Unfortunately, some runtime (I think libgcc.a, have to do more research here) already contains several (four?) TLS variables, thus any DLL built by mingw will contain a .tls section.

...

I suspect the mingw runtime is the culprit, instead:

tlsmcrt.o: file format pe-i386
tlsmthread.o: file format pe-i386
tlssup.o: file format pe-i386
6 .tls$AAA 00000004 00000000 00000000 00000fa6 2**2
7 .tls$ZZZ 00000004 00000000 00000000 00000faa 2**2
10 .tls 00000018 00000000 00000000 00000fb6 2**2
tlsthrd.o: file format pe-i386````

I also discovered through more debugging and experimentation why I was only experiencing the problem sporadically: the LoadLibrary() failure only occurs when the dll cannot be loaded at its default base address and is relocated.

Conclusion:

I can see three possible solutions to this:

  1. build my own libmingw32.a that does not use __declspec(thread) somehow.
  2. move the entire project over to visual c++ where (whether by design or by luck) this situation does not arise, because static TLS is not used.
  3. pick a base address for the dll that is less likely to clash, and keep my fingers crossed.

In the context where this project will be deployed, (1) is too unmanageable to be viable, (2) is quite painful, and (3) has a high chance of success. Sucks to be relying on a dice roll when deploying software, but it is the option that is the best fit at the moment.

c++

windows

dll

mingw

0 Answers

Your Answer

Accepted video resources