* 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.
This commit is contained in:
parent
15c04fd16e
commit
6e780c8bf5
|
@ -1,3 +1,9 @@
|
|||
2005-06-03 Max Kaehn <slothman@electric-cloud.com>
|
||||
|
||||
* 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.
|
||||
|
||||
2005-06-02 Christopher Faylor <cgf@timesys.com>
|
||||
|
||||
* dlfcn.cc (get_full_path_of_dll): Use a relative path when converting
|
||||
|
|
|
@ -955,7 +955,15 @@ dll_crt0 (per_process *uptr)
|
|||
_dll_crt0 ();
|
||||
}
|
||||
|
||||
/* This must be called by anyone who uses LoadLibrary to load cygwin1.dll */
|
||||
/* This must be called by anyone who uses LoadLibrary to load cygwin1.dll.
|
||||
* You must have CYGTLS_PADSIZE bytes reserved at the bottom of the stack
|
||||
* calling this function, and that storage must not be overwritten until you
|
||||
* unload cygwin1.dll, as it is used for _my_tls. It is best to load
|
||||
* cygwin1.dll before spawning any additional threads in your process.
|
||||
*
|
||||
* See winsup/testsuite/cygload for an example of how to use cygwin1.dll
|
||||
* from MSVC and non-cygwin MinGW applications.
|
||||
*/
|
||||
extern "C" void
|
||||
cygwin_dll_init ()
|
||||
{
|
||||
|
@ -974,6 +982,9 @@ cygwin_dll_init ()
|
|||
user_data->envptr = &envp;
|
||||
user_data->fmode_ptr = &_fmode;
|
||||
|
||||
main_environ = user_data->envptr;
|
||||
*main_environ = NULL;
|
||||
initialize_main_tls((char *)&_my_tls);
|
||||
dll_crt0_1 (NULL);
|
||||
}
|
||||
|
||||
|
|
|
@ -68,8 +68,8 @@ get_full_path_of_dll (const char* str, char *name)
|
|||
|
||||
if (isabspath (name) ||
|
||||
(ret = check_path_access ("LD_LIBRARY_PATH=", name, real_filename)
|
||||
?: check_path_access ("/usr/bin:/usr/lib", name, real_filename)) == NULL)
|
||||
real_filename.check (name); /* Convert */
|
||||
?: check_path_access ("/usr/lib", name, real_filename)) == NULL)
|
||||
real_filename.check (name, PC_SYM_FOLLOW | PC_NOFULL | PC_NULLEMPTY); /* Convert */
|
||||
|
||||
if (!real_filename.error)
|
||||
ret = strcpy (name, real_filename);
|
||||
|
|
|
@ -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 <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.
|
Loading…
Reference in New Issue