libc/winsup/cygwin/how-cygtls-works.txt

76 lines
3.0 KiB
Plaintext

Contributed by Max Kaehn
All cygwin threads have separate context in an object of class _cygtls. The
storage for this object is kept on the stack in the bottom CYGTLS_PADSIZE
bytes. Each thread references the storage via the Thread Environment Block
(aka Thread Information Block), which Windows maintains for each user thread
in the system, with the address in the FS segment register. The memory
is laid out as in the NT_TIB structure from <w32api/winnt.h>:
typedef struct _NT_TIB {
struct _EXCEPTION_REGISTRATION_RECORD *ExceptionList;
PVOID StackBase;
PVOID StackLimit;
PVOID SubSystemTib;
_ANONYMOUS_UNION union {
PVOID FiberData;
DWORD Version;
} DUMMYUNIONNAME;
PVOID ArbitraryUserPointer;
struct _NT_TIB *Self;
} NT_TIB,*PNT_TIB;
Cygwin sees it like this:
extern exception_list *_except_list asm ("%fs:0"); // exceptions.cc
extern char *_tlsbase __asm__ ("%fs:4"); // cygtls.h
extern char *_tlstop __asm__ ("%fs:8"); // cygtls.h
And accesses cygtls like this:
#define _my_tls (((_cygtls *) _tlsbase)[-1]) // cygtls.h
Initialization always goes through _cygtls::init_thread(). It works
in the following ways:
* In the main thread, _dll_crt0() provides CYGTLS_PADSIZE bytes on the stack
and passes them to initialize_main_tls(), which calls _cygtls::init_thread().
It then calls dll_crt0_1(), which terminates with cygwin_exit() rather than
by returning, so the storage never goes out of scope.
If you load cygwin1.dll dynamically from a non-cygwin application, it is
vital that the bottom CYGTLS_PADSIZE bytes of the stack are not in use
before you call cygwin_dll_init(). See winsup/testsuite/cygload for
more information.
* Threads other than the main thread receive DLL_THREAD_ATTACH messages
to dll_entry() (in init.cc).
- dll_entry() calls munge_threadfunc(), which grabs the function pointer
for the thread from the stack frame and substitutes threadfunc_fe(),
- which then passes the original function pointer to _cygtls::call(),
- which then allocates CYGTLS_PADSIZE bytes on the stack and hands them
to call2(),
- which allocates an exception_list object on the stack and hands it to
init_exceptions() (in exceptions.cc), which attaches it to the end of
the list of exception handlers, changing _except_list (aka
tib->ExceptionList), then passes the cygtls storage to init_thread().
call2() calls ExitThread() instead of returning, so the storage never
goes out of scope.
Note that the padding isn't necessarily going to be just where the _cygtls
structure lives; it just makes sure there's enough room on the stack when the
CYGTLS_PADSIZE bytes down from there are overwritten.
Debugging
You can examine the segment registers in gdb via "info w32 selector $fs"
(which is using GetThreadSelectorEntry()) to get results like this:
Selector $fs
0x03b: base=0x7ffdd000 limit=0x00000fff 32-bit Data (Read/Write, Exp-up)
Priviledge level = 3. Byte granular.
"x/3x 0x7ffdd000" will give you _except_list, _tlsbase, and _tlstop.