Skip to content
This repository has been archived by the owner on Oct 12, 2022. It is now read-only.

Commit

Permalink
add DLL support for Windows Server 2003 and XP/64
Browse files Browse the repository at this point in the history
fix crash in DLL on Windows/64
support creating thread inside DLL
  • Loading branch information
rainers committed Apr 25, 2011
1 parent 30c44e9 commit f0e4cff
Show file tree
Hide file tree
Showing 3 changed files with 92 additions and 29 deletions.
82 changes: 63 additions & 19 deletions src/core/sys/windows/_dll.d
Expand Up @@ -52,8 +52,8 @@ private:
alias extern(Windows)
void* fnRtlAllocateHeap(void* HeapHandle, uint Flags, uint Size);

// find a code sequence and return the (relative) address that follows
static void* findCodeReference( void* adr, int len, ref ubyte[] pattern, bool relative )
// find a code sequence and return the address after the sequence
static void* findCodeSequence( void* adr, int len, ref ubyte[] pattern )
{
if( !adr )
return null;
Expand All @@ -64,14 +64,28 @@ private:
if( code[ p .. p + pattern.length ] == pattern[ 0 .. $ ] )
{
ubyte* padr = code + p + pattern.length;
if( relative )
return padr + 4 + *cast(int*) padr;
return *cast(void**) padr;
return padr;
}
}
return null;
}

// find a code sequence and return the (relative) address that follows
static void* findCodeReference( void* adr, int len, ref ubyte[] pattern, bool relative )
{
if( !adr )
return null;

ubyte* padr = cast(ubyte*) findCodeSequence( adr, len, pattern );
if( padr )
{
if( relative )
return padr + 4 + *cast(int*) padr;
return *cast(void**) padr;
}
return null;
}

// crawl through ntdll to find function _LdrpAllocateTls@0 and references
// to _LdrpNumberOfTlsEntries, _NtdllBaseTag and _LdrpTlsList
// LdrInitializeThunk
Expand All @@ -87,11 +101,14 @@ private:

static __gshared ubyte[] jmp_LdrpInitialize = [ 0x33, 0xED, 0xE9 ]; // xor ebp,ebp; jmp _LdrpInitialize
static __gshared ubyte[] jmp__LdrpInitialize = [ 0x5D, 0xE9 ]; // pop ebp; jmp __LdrpInitialize
static __gshared ubyte[] jmp__LdrpInitialize_xp64 = [ 0x5D, 0x90, 0x90, 0x90, 0x90, 0x90 ]; // pop ebp; nop; nop; nop; nop; nop;
static __gshared ubyte[] call_LdrpInitializeThread = [ 0xFF, 0x75, 0x08, 0xE8 ]; // push [ebp+8]; call _LdrpInitializeThread
static __gshared ubyte[] call_LdrpAllocateTls = [ 0x00, 0x00, 0xE8 ]; // jne 0xc3; call _LdrpAllocateTls
static __gshared ubyte[] call_LdrpAllocateTls_svr03 = [ 0x65, 0xfc, 0x00, 0xE8 ]; // and [ebp+fc], 0; call _LdrpAllocateTls
static __gshared ubyte[] jne_LdrpAllocateTls = [ 0x0f, 0x85 ]; // jne body_LdrpAllocateTls
static __gshared ubyte[] mov_LdrpNumberOfTlsEntries = [ 0x8B, 0x0D ]; // mov ecx, _LdrpNumberOfTlsEntries
static __gshared ubyte[] mov_NtdllBaseTag = [ 0x51, 0x8B, 0x0D ]; // push ecx; mov ecx, _NtdllBaseTag
static __gshared ubyte[] mov_NtdllBaseTag_srv03 = [ 0x50, 0xA1 ]; // push eax; mov eax, _NtdllBaseTag
static __gshared ubyte[] mov_LdrpTlsList = [ 0x8B, 0x3D ]; // mov edi, _LdrpTlsList

static LdrpTlsListEntry* addTlsListEntry( void** peb, void* tlsstart, void* tlsend, void* tls_callbacks_a, int* tlsindex )
Expand All @@ -105,13 +122,19 @@ private:
{
void* pLdrpInitialize = findCodeReference( fn, 20, jmp_LdrpInitialize, true );
void* p_LdrpInitialize = findCodeReference( pLdrpInitialize, 40, jmp__LdrpInitialize, true );
if( !p_LdrpInitialize )
p_LdrpInitialize = findCodeSequence( pLdrpInitialize, 40, jmp__LdrpInitialize_xp64 );
void* pLdrpInitializeThread = findCodeReference( p_LdrpInitialize, 200, call_LdrpInitializeThread, true );
void* pLdrpAllocateTls = findCodeReference( pLdrpInitializeThread, 40, call_LdrpAllocateTls, true );
if(!pLdrpAllocateTls)
pLdrpAllocateTls = findCodeReference( pLdrpInitializeThread, 100, call_LdrpAllocateTls_svr03, true );
void* pBodyAllocateTls = findCodeReference( pLdrpAllocateTls, 40, jne_LdrpAllocateTls, true );

int* pLdrpNumberOfTlsEntries = cast(int*) findCodeReference( pBodyAllocateTls, 20, mov_LdrpNumberOfTlsEntries, false );
int* pLdrpNumberOfTlsEntries = cast(int*) findCodeReference( pBodyAllocateTls, 60, mov_LdrpNumberOfTlsEntries, false );
pNtdllBaseTag = cast(int*) findCodeReference( pBodyAllocateTls, 30, mov_NtdllBaseTag, false );
LdrpTlsListEntry* pLdrpTlsList = cast(LdrpTlsListEntry*)findCodeReference( pBodyAllocateTls, 60, mov_LdrpTlsList, false );
if(!pNtdllBaseTag)
pNtdllBaseTag = cast(int*) findCodeReference( pBodyAllocateTls, 30, mov_NtdllBaseTag_srv03, false );
LdrpTlsListEntry* pLdrpTlsList = cast(LdrpTlsListEntry*)findCodeReference( pBodyAllocateTls, 80, mov_LdrpTlsList, false );

if( !pLdrpNumberOfTlsEntries || !pNtdllBaseTag || !pLdrpTlsList )
return null;
Expand Down Expand Up @@ -346,8 +369,12 @@ public:
function (uint id, void* context) {
if( !thread_findByAddr( id ) )
{
thread_attachByAddr( id );
thread_moduleTlsCtor( id );
// if the OS has not prepared TLS for us, don't attach to the thread
if( GetTlsDataAddress( id ) )
{
thread_attachByAddr( id );
thread_moduleTlsCtor( id );
}
}
return true;
}, null );
Expand Down Expand Up @@ -382,22 +409,39 @@ public:
*/
static bool tlsCtorRun;
static this() { tlsCtorRun = true; }
static ~this() { tlsCtorRun = false; }

// to be called from DllMain with reason DLL_THREAD_ATTACH
void dll_thread_attach( bool attach_thread = true, bool initTls = true )
bool dll_thread_attach( bool attach_thread = true, bool initTls = true )
{
if( attach_thread && !thread_findByAddr( GetCurrentThreadId() ) )
thread_attachThis();
if( initTls && !tlsCtorRun ) // avoid duplicate calls
_moduleTlsCtor();
// if the OS has not prepared TLS for us, don't attach to the thread
// (happened when running under x64 OS)
if( !GetTlsDataAddress( GetCurrentThreadId() ) )
return false;
if( !thread_findByAddr( GetCurrentThreadId() ) )
{
// only attach to thread and initalize it if it is not in the thread list (so it's not created by "new Thread")
if( attach_thread )
thread_attachThis();
if( initTls && !tlsCtorRun ) // avoid duplicate calls
_moduleTlsCtor();
}
return true;
}

// to be called from DllMain with reason DLL_THREAD_DETACH
void dll_thread_detach( bool detach_thread = true, bool exitTls = true )
bool dll_thread_detach( bool detach_thread = true, bool exitTls = true )
{
if( exitTls )
_moduleTlsDtor();
if( detach_thread )
thread_detachThis();
// if the OS has not prepared TLS for us, we did not attach to the thread
if( !GetTlsDataAddress( GetCurrentThreadId() ) )
return false;
if( thread_findByAddr( GetCurrentThreadId() ) )
{
if( exitTls && tlsCtorRun ) // avoid dtors to be run twice
_moduleTlsDtor();
if( detach_thread )
thread_detachThis();
}
return true;
}
}
23 changes: 15 additions & 8 deletions src/core/sys/windows/_thread.d
Expand Up @@ -201,7 +201,7 @@ private:
return;
}

// temporarily set current TLS data pointer to the data pointer of the referenced thread
// temporarily set current TLS array pointer to the array pointer of the referenced thread
void** curteb = getTEB();
void** teb = getTEB( id );
assert( teb && curteb );
Expand All @@ -211,14 +211,9 @@ private:
if( !curtlsarray || !tlsarray )
return;

void** curtlsdata = cast(void**) curtlsarray[_tls_index];
void** tlsdata = cast(void**) tlsarray[_tls_index];
if( !curtlsdata || !tlsdata )
return;

curtlsarray[_tls_index] = tlsdata;
curteb[11] = tlsarray;
fn();
curtlsarray[_tls_index] = curtlsdata;
curteb[11] = curtlsarray;
}
}

Expand All @@ -229,6 +224,7 @@ public:
alias thread_aux.OpenThreadHandle OpenThreadHandle;
alias thread_aux.enumProcessThreads enumProcessThreads;

// get the start of the TLS memory of the thread with the given handle
void* GetTlsDataAddress( HANDLE hnd )
{
if( void** teb = getTEB( hnd ) )
Expand All @@ -237,6 +233,17 @@ public:
return null;
}

// get the start of the TLS memory of the thread with the given identifier
void* GetTlsDataAddress( uint id )
{
HANDLE hnd = OpenThread( thread_aux.THREAD_QUERY_INFORMATION, FALSE, id );
assert( hnd, "OpenThread failed" );

void* tls = GetTlsDataAddress( hnd );
CloseHandle( hnd );
return tls;
}

///////////////////////////////////////////////////////////////////
// run _moduleTlsCtor in the context of the given thread
void thread_moduleTlsCtor( uint id )
Expand Down
16 changes: 14 additions & 2 deletions src/core/thread.d
Expand Up @@ -794,6 +794,16 @@ class Thread
// having thread being treated like a daemon thread.
synchronized( slock )
{
// when creating threads from inside a DLL, DllMain(THREAD_ATTACH)
// might be called before _beginthreadex returns, but the dll
// helper functions need to know whether the thread is created
// from the runtime itself or from another DLL or the application
// to just attach to it
// as the consequence, the new Thread object is added before actual
// creation of the thread. There should be no problem with the GC
// calling thread_suspendAll, because of the slock synchronization
add( this );

version( Windows )
{
m_hndl = cast(HANDLE) _beginthreadex( null, m_sz, &thread_entryPoint, cast(void*) this, 0, &m_addr );
Expand All @@ -817,7 +827,6 @@ class Thread
if( m_tmach == m_tmach.init )
throw new ThreadException( "Error creating thread" );
}
add( this );
}
}

Expand Down Expand Up @@ -2039,7 +2048,10 @@ version( Windows )
auto pstart = cast(void*) &_tlsstart;
auto pend = cast(void*) &_tlsend;
auto pos = GetTlsDataAddress( thisThread.m_hndl );
thisThread.m_tls = pos[0 .. pend - pstart];
if( pos ) // on x64, threads without TLS happen to exist
thisThread.m_tls = pos[0 .. pend - pstart];
else
thisThread.m_tls = [];
}
}
else
Expand Down

0 comments on commit f0e4cff

Please sign in to comment.