From 6e780c8bf5805e72414adad805f40ef4df1ebf57 Mon Sep 17 00:00:00 2001 From: Christopher Faylor Date: Sat, 4 Jun 2005 02:11:50 +0000 Subject: * dcrt0.cc (cygwin_dll_init): Now initializes main_environ and cygtls. Comment to explain the caveats of this method. * how-cygtls-works.txt: New file. --- winsup/cygwin/how-cygtls-works.txt | 75 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 75 insertions(+) create mode 100644 winsup/cygwin/how-cygtls-works.txt (limited to 'winsup/cygwin/how-cygtls-works.txt') diff --git a/winsup/cygwin/how-cygtls-works.txt b/winsup/cygwin/how-cygtls-works.txt new file mode 100644 index 000000000..69363f1fb --- /dev/null +++ b/winsup/cygwin/how-cygtls-works.txt @@ -0,0 +1,75 @@ +Copyright 2005 Red Hat Inc., 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 : + +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. -- cgit v1.2.3