Skip to content

Commit

Permalink
Fea #83, Windows XP添加动态DLL TLS兼容
Browse files Browse the repository at this point in the history
  • Loading branch information
mingkuang-Chuyu committed May 22, 2024
1 parent 1f01599 commit 0efdd31
Show file tree
Hide file tree
Showing 4 changed files with 302 additions and 1 deletion.
3 changes: 2 additions & 1 deletion ReadMe.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,8 @@ YY-Thunks(鸭船),存在的目的就是抹平不同系统的差异,编
2. 【链接器】-【输入】-【附加依赖项】,添加
`objs\$(PlatformShortName)\YY_Thunks_for_WinXP.obj`
3. 【链接器】-【系统】-【所需的最低版本】,根据实际情况填写 `5.01`(WinXP 32位) 或者 `5.02`(WinXP x64、2003)。
4. 重新编译代码。
4. 如果是编译DLL,那么【链接器】-【高级】-【自定义入口点】更改为`_DllMainCRTStartupForYY_Thunks`(如果不更改那么XP下使用TLS时可能崩溃!)
5. 重新编译代码。

> 温馨提示:如果需要兼容 Vista,【所需的最低版本】无需修改,但是【附加依赖项】请选择
`objs\$(PlatformShortName)\YY_Thunks_for_Vista.obj`
Expand Down
296 changes: 296 additions & 0 deletions src/Thunks/DllMainCRTStartup.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,296 @@
#if defined(YY_Thunks_Implemented) && YY_Thunks_Support_Version < NTDDI_WIN6

namespace YY::Thunks::internal
{
extern "C" BOOL WINAPI _DllMainCRTStartup(
HINSTANCE const instance,
DWORD const reason,
LPVOID const reserved
);

extern "C" ULONG _tls_index;
static ULONG _tls_index_old;
#ifdef _WIN64
extern "C" const IMAGE_TLS_DIRECTORY64 _tls_used;
#else
extern "C" const IMAGE_TLS_DIRECTORY _tls_used;
#endif

struct TlsRawItem
{
TlsRawItem* pNext;
TlsRawItem* pPrev;
BYTE* pBase;
};
static thread_local TlsRawItem s_CurrentNode;

static volatile LONG uStatus = 0;
static TlsRawItem* volatile pRoot = nullptr;

static ULONG __fastcall GetTlsIndexBufferCount(TEB* _pTeb)
{
auto _ppThreadLocalStoragePointer = (void**)_pTeb->ThreadLocalStoragePointer;
if (!_ppThreadLocalStoragePointer)
return 0;

return HeapSize(_pTeb->ProcessEnvironmentBlock->ProcessHeap, 0, _ppThreadLocalStoragePointer) / sizeof(void*);
}
static PVOID NTAPI RtlImageDirectoryEntryToData(
__in PVOID pBaseAddress,
__in ULONG dwDirectory,
__out PULONG pSize
)
{
auto _pDosHeader = (PIMAGE_DOS_HEADER)pBaseAddress;
auto _pNtHerder = reinterpret_cast<PIMAGE_NT_HEADERS>(PBYTE(pBaseAddress) + _pDosHeader->e_lfanew);
auto& _DataDirectory = _pNtHerder->OptionalHeader.DataDirectory[dwDirectory];

*pSize = _DataDirectory.Size;
if (_DataDirectory.Size == 0 || _DataDirectory.VirtualAddress == 0)
return nullptr;

return PBYTE(pBaseAddress) + _DataDirectory.VirtualAddress;
}

static ULONG __fastcall GetMaxTlsIndex() noexcept
{
ULONG uMaxTlsIndex = 0;

auto _pLdr = (_PEB_LDR_DATA_XP*)(TEB*)NtCurrentTeb()->ProcessEnvironmentBlock->Ldr;
auto _pHerder = &_pLdr->InLoadOrderModuleList;
for (auto _pItem = _pHerder->Flink; _pItem != _pHerder; )
{
auto _pEntry = CONTAINING_RECORD(_pItem, _LDR_DATA_TABLE_ENTRY_XP, InLoadOrderModuleList);
_pItem = _pItem->Flink;

/*if (!_pEntry->TlsIndex)
continue;*/

ULONG TlsSize;
auto pTlsImage = (PIMAGE_TLS_DIRECTORY)RtlImageDirectoryEntryToData(
_pEntry->BaseAddress,
IMAGE_DIRECTORY_ENTRY_TLS,
&TlsSize);

if (pTlsImage == nullptr || pTlsImage->AddressOfIndex == 0)
{
continue;
}


__try
{

const auto _TlsIndex = *(ULONG*)pTlsImage->AddressOfIndex;
if (uMaxTlsIndex < _TlsIndex)
uMaxTlsIndex = _TlsIndex;
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
}
}

return uMaxTlsIndex;

}

static bool __fastcall AllocTlsData() noexcept
{
if (_tls_index == 0)
return false;

auto _pTeb= (TEB*)NtCurrentTeb();
auto _pTlsIndex = (void**)_pTeb->ThreadLocalStoragePointer;
auto _cTlsIndexLength = GetTlsIndexBufferCount(_pTeb);
if (_cTlsIndexLength <= _tls_index)
{
// Index不足,扩充……
auto _cNewTlsIndexLength = _tls_index + 128;
auto _pNewTlsIndex = (void**)Alloc(_cNewTlsIndexLength * sizeof(void*), HEAP_ZERO_MEMORY);
if (!_pNewTlsIndex)
return false;

memcpy(_pNewTlsIndex, _pTlsIndex, _cTlsIndexLength * sizeof(void*));
if ((void*)InterlockedCompareExchange((uintptr_t*)&_pTeb->ThreadLocalStoragePointer, (uintptr_t)_pNewTlsIndex, (uintptr_t)_pTlsIndex) != _pTlsIndex)
{
Free(_pNewTlsIndex);
return false;
}
Free(_pTlsIndex);
_pTlsIndex = _pNewTlsIndex;
}
const size_t _cbTlsRaw = _tls_used.EndAddressOfRawData - _tls_used.StartAddressOfRawData;
auto _pRawTlsData = (BYTE*)Alloc(_cbTlsRaw, HEAP_ZERO_MEMORY);
if (!_pRawTlsData)
return false;

memcpy(_pRawTlsData, (void*)_tls_used.StartAddressOfRawData, _cbTlsRaw);
InterlockedExchange((uintptr_t*)&_pTlsIndex[_tls_index], (uintptr_t)_pRawTlsData);

s_CurrentNode.pBase = _pRawTlsData;
for (;;)
{
if (!_interlockedbittestandset(&uStatus, 0))
{
s_CurrentNode.pNext = pRoot;
if (pRoot)
{
pRoot->pPrev = &s_CurrentNode;
}
pRoot = &s_CurrentNode;
_interlockedbittestandreset(&uStatus, 0);
break;
}
}
return true;
}

static void __fastcall FreeTlsData()
{
if (_tls_index == 0)
return;
auto _pTeb = (TEB*)NtCurrentTeb();
if (_tls_index >= GetTlsIndexBufferCount(_pTeb))
return;

auto _ppTlsIndex = (void**)_pTeb->ThreadLocalStoragePointer;
if (_ppTlsIndex[_tls_index] == nullptr)
return;

if (s_CurrentNode.pBase != _ppTlsIndex[_tls_index])
return;

s_CurrentNode.pBase = nullptr;

for (;;)
{
if (!_interlockedbittestandset(&uStatus, 0))
{
auto pPrev = s_CurrentNode.pPrev;
auto pNext = s_CurrentNode.pNext;
if (pPrev)
{
pPrev->pNext = pNext;
}
else
{
pRoot = pNext;
}

if (pNext)
{
pNext->pPrev = pPrev;
}
_interlockedbittestandreset(&uStatus, 0);

auto _pTlsRawData = (void*)InterlockedExchange((uintptr_t*)&_ppTlsIndex[_tls_index], 0);
Free(_pTlsRawData);
break;
}
}
}

static ULONG __fastcall CreateTlsIndex() noexcept
{
return GetMaxTlsIndex() + 1;
}

static void __fastcall FreeTlsIndex() noexcept
{
if (_tls_index == 0)
return;

// 故意不加锁……
for (auto _pItem = pRoot; _pItem;)
{
auto _pNext = _pItem->pNext;
Free(_pItem->pBase);
_pItem = _pNext;
}
}

static void __fastcall CallTlsCallback(
HINSTANCE const _hInstance,
DWORD const _uReason)
{
if (_tls_index == 0)
return;

auto _pTeb = (TEB*)NtCurrentTeb();
auto _ppThreadLocalStoragePointer = (void**)_pTeb->ThreadLocalStoragePointer;
if (!_ppThreadLocalStoragePointer)
return;

if (_tls_index >= GetTlsIndexBufferCount(_pTeb))
return;

if (!_ppThreadLocalStoragePointer[_tls_index])
return;

__try
{
auto _pfnTlsCallbacks = reinterpret_cast<PIMAGE_TLS_CALLBACK*>(_tls_used.AddressOfCallBacks);
if (_pfnTlsCallbacks)
{
for (; *_pfnTlsCallbacks; ++_pfnTlsCallbacks)
{
(*_pfnTlsCallbacks)(_hInstance, _uReason, nullptr);
}
}
}
__except (EXCEPTION_EXECUTE_HANDLER)
{

}
}

extern "C" BOOL WINAPI DllMainCRTStartupForYY_Thunks(
HINSTANCE const _hInstance,
DWORD const _uReason,
LPVOID const _pReserved
)
{
if (internal::GetSystemVersion() < internal::MakeVersion(6, 0))
{
// Vista开始已经可以动态的处理TLS问题了,所以这里只针对老系统处理。
switch (_uReason)
{
case DLL_PROCESS_ATTACH:
_tls_index_old = _tls_index;
if (_tls_index == 0)
{
_tls_index = CreateTlsIndex();
AllocTlsData();
}
break;
case DLL_THREAD_ATTACH:
if (_tls_index_old == 0)
{
AllocTlsData();
CallTlsCallback(_hInstance, _uReason);
}
break;
case DLL_THREAD_DETACH:
if (_tls_index_old == 0)
{
CallTlsCallback(_hInstance, _uReason);
FreeTlsData();
}
break;
case DLL_PROCESS_DETACH:
#if (YY_Thunks_Support_Version < NTDDI_WINXP)
__YY_Thunks_Process_Terminating = _pReserved != nullptr;
#endif
if (_tls_index_old == 0)
{
CallTlsCallback(_hInstance, _uReason);
FreeTlsIndex();
}
break;
}
}

return _DllMainCRTStartup(_hInstance, _uReason, _pReserved);
}
}
#endif
1 change: 1 addition & 0 deletions src/YY-Thunks.UnitTest/YY-Thunks.UnitTest.vcxproj
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,7 @@
<ClInclude Include="..\Thunks\d3d11.hpp" />
<ClInclude Include="..\Thunks\d3d9.hpp" />
<ClInclude Include="..\Thunks\dbghelp.hpp" />
<ClInclude Include="..\Thunks\DllMainCRTStartup.hpp" />
<ClInclude Include="..\Thunks\dwmapi.hpp" />
<ClInclude Include="..\Thunks\dwrite.hpp" />
<ClInclude Include="..\Thunks\dxgi.hpp" />
Expand Down
3 changes: 3 additions & 0 deletions src/YY-Thunks.UnitTest/YY-Thunks.UnitTest.vcxproj.filters
Original file line number Diff line number Diff line change
Expand Up @@ -308,6 +308,9 @@
<ClInclude Include="..\Thunks\dbghelp.hpp">
<Filter>Thunks</Filter>
</ClInclude>
<ClInclude Include="..\Thunks\DllMainCRTStartup.hpp">
<Filter>Thunks</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<None Include="..\def\x86\PSAPI2Kernel32.def">
Expand Down

0 comments on commit 0efdd31

Please sign in to comment.