diff --git a/.editorconfig b/.editorconfig index 96e3729..eb21893 100644 --- a/.editorconfig +++ b/.editorconfig @@ -14,3 +14,6 @@ indent_size = 4 [*.{vcxproj,xml,nuspec,targets}] indent_style = space indent_size = 2 + +[*.cmd] +charset = unset diff --git a/.github/workflows/Build&Test.yml b/.github/workflows/Build&Test.yml index f6fe6c5..a746482 100644 --- a/.github/workflows/Build&Test.yml +++ b/.github/workflows/Build&Test.yml @@ -132,13 +132,13 @@ jobs: $RunFaild = 0 - &vstest.console src\Release\YY-Thunks.UnitTest.dll "/logger:trx;LogFileName=UnitTestWin32.trx" "/Blame:CollectDump;CollectAlways=false;DumpType=full /Diag:TestResults\Win32.log" + &vstest.console src\Release\YY-Thunks.UnitTest.dll "/logger:trx;LogFileName=UnitTestWin32.trx" "/Blame:CollectDump;CollectAlways=false;DumpType=full" "/Diag:TestResults\Win32.log" if($lastexitcode -ne 0) { $RunFaild = 1 } - &vstest.console src\x64\Release\YY-Thunks.UnitTest.dll "/logger:trx;LogFileName=UnitTestWin64.trx" "/Blame:CollectDump;CollectAlways=false;DumpType=full /Diag:TestResults\Win64.log" + &vstest.console src\x64\Release\YY-Thunks.UnitTest.dll "/logger:trx;LogFileName=UnitTestWin64.trx" "/Blame:CollectDump;CollectAlways=false;DumpType=full" "/Diag:TestResults\Win64.log" if($lastexitcode -ne 0) { $RunFaild = 1 diff --git a/NuGet/build/native/YY-Thunks.targets b/NuGet/build/native/YY-Thunks.targets index 81e2868..592bdba 100644 --- a/NuGet/build/native/YY-Thunks.targets +++ b/NuGet/build/native/YY-Thunks.targets @@ -10,6 +10,7 @@ 5.01 5.02 $(MSBuildThisFileDirectory)objs\$(PlatformShortName)\$(YY_Thunks_File);%(AdditionalDependencies) + DllMainCRTStartupForYY_Thunks @@ -21,4 +22,4 @@ - \ No newline at end of file + diff --git a/ReadMe.md b/Readme.ocs.md similarity index 89% rename from ReadMe.md rename to Readme.ocs.md index a7b0d2f..6c67c15 100644 --- a/ReadMe.md +++ b/Readme.ocs.md @@ -6,6 +6,8 @@ [![nuget](https://img.shields.io/nuget/vpre/YY-Thunks)](https://www.nuget.org/packages/YY-Thunks) [![Build&Test](https://github.com/Chuyu-Team/YY-Thunks/actions/workflows/Build&Test.yml/badge.svg)](https://github.com/Chuyu-Team/YY-Thunks/actions/workflows/Build&Test.yml) +- [英语](Readme.md) + ## 1. 关于 YY-Thunks 众所周知,从 Windows 的每次更新又会新增大量 API,这使得兼容不同版本的 Windows @@ -22,8 +24,20 @@ YY-Thunks(鸭船),存在的目的就是抹平不同系统的差异,编 ### 1.1. 原理 使用 `LoadLibrary` 以及 `GetProcAddress` 动态加载 API,不存在时做出补偿措施, -最大限度模拟原始 API 行为,让你的程序正常运行。 +最大限度模拟原始 API 行为,让你的程序正常运行。大概就像下面这个`GetTickCount64`函数: +```cpp +// 注意:Windows XP 不支持 GetTickCount64 +ULONGLONG WINAPI GetTickCount64(VOID) +{ + if (auto const _pfnGetTickCount64 = try_get_GetTickCount64()) + { + return _pfnGetTickCount64(); + } + // Fallback + return GetTickCount(); +} +``` ### 1.2. 亮点 * 更快!更安全!`鸭船`内建2级缓存以及按需加载机制,同时自动加密所有函数指针, @@ -57,7 +71,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下使用thread_local可能崩溃!) +5. 重新编译代码。 > 温馨提示:如果需要兼容 Vista,【所需的最低版本】无需修改,但是【附加依赖项】请选择 `objs\$(PlatformShortName)\YY_Thunks_for_Vista.obj`。 diff --git a/src/Build.cmd b/src/Build.cmd index cb536be..724119f 100644 --- a/src/Build.cmd +++ b/src/Build.cmd @@ -1,8 +1,8 @@ -@echo off +@echo off setlocal -::需要设置LibMaker.exe路径到环境变量Path。 -::如果你需要LibMaker.exe,请自行到 初雨开源项目交流QQ群(633710173)群文件中下载。 +::ÐèҪÉèÖÃLibMaker.exe·¾¶µ½»·¾³±äÁ¿Path¡£ +::Èç¹ûÄãÐèҪLibMaker.exe£¬Çë×ÔÐе½ ³õÓ꿪ԴÏîĿ½»Á÷QQȺ£¨633710173£©ȺÎļþÖÐÏÂÔء£ pushd "%~dp0.." @@ -12,7 +12,7 @@ if "%Platform%"=="" set Platform=x86 md "objs\\%Platform%" -::先生成 YY_Thunks_List.hpp +::ÏÈÉú³É YY_Thunks_List.hpp msbuild "%~dp0YY-Thunks.UnitTest\YY-Thunks.UnitTest.vcxproj" -t:Build_YY_Thunks_List_hpp call:Build%Platform% @@ -26,8 +26,8 @@ goto:eof :BuildObj cl /O1 /Os /Oi /GS- /std:c++17 /arch:IA32 /Z7 /MT /Fo"objs\\%Platform%\\%1" /Zl /c /D "NDEBUG" /D "YY_Thunks_Support_Version=%2" "%~dp0Thunks\YY_Thunks.cpp" -::进行函数名称进行修正 __imp__%s_%u -> __imp__%s@%u -LibMaker.exe FixObj "%~dp0..\\objs\\%Platform%\\%1" /WeakExternFix:__security_cookie=%PointType% /WeakExternFix:__YY_Thunks_Process_Terminating=4 /WeakExternFix:__acrt_atexit_table=%PointType% +::½øÐк¯ÊýÃû³ƽøÐÐÐÞÕý __imp__%s_%u -> __imp__%s@%u +LibMaker.exe FixObj "%~dp0..\\objs\\%Platform%\\%1" /WeakExternFix:__security_cookie=%PointType% /WeakExternFix:__YY_Thunks_Process_Terminating=4 /WeakExternFix:__acrt_atexit_table=%PointType% /WeakExternFix:__pfnDllMainCRTStartupForYY_Thunks=%PointType% LibMaker.exe AppendWeak /MACHINE:%Platform% /DEF:"%~dp0def\\%Platform%\\PSAPI2Kernel32.def" /OUT:"%~dp0..\\objs\\%Platform%\\%1" diff --git a/src/Thunks/DllMainCRTStartup.hpp b/src/Thunks/DllMainCRTStartup.hpp new file mode 100644 index 0000000..5cd2d57 --- /dev/null +++ b/src/Thunks/DllMainCRTStartup.hpp @@ -0,0 +1,463 @@ +#if defined(YY_Thunks_Implemented) + +namespace YY::Thunks::internal +{ + extern "C" BOOL WINAPI _DllMainCRTStartup( + HINSTANCE const instance, + DWORD const reason, + LPVOID const reserved + ); + + // 如果希望自定义函数入口点,可以设置此函数 + extern "C" extern decltype(_DllMainCRTStartup)* const __pfnDllMainCRTStartupForYY_Thunks; + +#if YY_Thunks_Support_Version < NTDDI_WIN6 + 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 + +#pragma section(".CRT$XLB", long, read) // First MS CRT Loader TLS Callback + + enum class TlsMode + { + None, + ByFirstCallback, + ByDllMainCRTStartupForYY_Thunks, + }; + + // 用来记录第一次Tls回调的时机 + // 如果来自ByFirstCallback,那么说明当前模块的TLS功能完全正常(模块是EXE或者来自于隐式依赖的DLL),YY-Thunks无需额外修复 + // 如果来自ByDllMainCRTStartupForYY_Thunks,那么说明它是动态加载的DLL,TLS没有初始化 + static TlsMode g_TlsMode; + + static + VOID + NTAPI + FirstCallback( + PVOID DllHandle, + DWORD Reason, + PVOID Reserved + ) + { + if (DLL_PROCESS_ATTACH == Reason) + { + if (g_TlsMode == TlsMode::None) + { + g_TlsMode = TlsMode::ByFirstCallback; + } + } + } + + struct TlsRawItem + { + TlsRawItem* pNext; + TlsRawItem* pPrev; + BYTE* pBase; + void** pOldTlsIndex; + }; + 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(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; + + __try + { + // XP不会为动态加载的DLL设置这个字段(符合预期,毕竟没有真的初始化) + /*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; + } + + const auto _TlsIndex = *(ULONG*)pTlsImage->AddressOfIndex; + if (uMaxTlsIndex < _TlsIndex) + uMaxTlsIndex = _TlsIndex; + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + } + } + + return uMaxTlsIndex; + + } + + static SYSTEM_PROCESS_INFORMATION* __fastcall GetCurrentProcessInfo(StringBuffer& _szBuffer) + { + const auto _pfnNtQuerySystemInformation = try_get_NtQuerySystemInformation(); + if (!_pfnNtQuerySystemInformation) + return nullptr; + + ULONG _cbBuffer = max(4096, _szBuffer.uBufferLength); + ULONG _cbRet = 0; + for (;;) + { + auto _pBuffer = _szBuffer.GetBuffer(_cbBuffer); + if (!_pBuffer) + return nullptr; + + LONG _Status = _pfnNtQuerySystemInformation(SystemProcessInformation, _pBuffer, _cbBuffer, &_cbRet); + if (_Status >= 0) + break; + + if (STATUS_INFO_LENGTH_MISMATCH != _Status) + return nullptr; + + _cbBuffer = _cbRet; + } + + const auto _uCurrentProcessId = GetCurrentProcessId(); + auto _pInfo = (SYSTEM_PROCESS_INFORMATION*)_szBuffer.GetBuffer(0); + for (;;) + { + if (_pInfo->ProcessId == (HANDLE)_uCurrentProcessId) + return _pInfo; + + if (_pInfo->NextEntryDelta == 0) + break; + + _pInfo = reinterpret_cast(reinterpret_cast(_pInfo) + _pInfo->NextEntryDelta); + } + + return nullptr; + } + + static bool __fastcall AllocTlsData(TEB* _pTeb = nullptr) noexcept + { + if (_tls_index == 0) + return false; + if (!_pTeb) + _pTeb = (TEB*)NtCurrentTeb(); + + auto _pTlsIndex = (void**)_pTeb->ThreadLocalStoragePointer; + void** _pOldTlsIndex = nullptr; + 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; + } + + if (_pTeb == (TEB*)NtCurrentTeb()) + { + Free(_pTlsIndex); + } + else + { + // 其他线程的无法直接释放,玩意缓存里恰好正在使用这块那么会崩溃的 + _pOldTlsIndex = _pTlsIndex; + } + _pTlsIndex = _pNewTlsIndex; + } + const size_t _cbTlsRaw = _tls_used.EndAddressOfRawData - _tls_used.StartAddressOfRawData; + auto _pRawTlsData = (BYTE*)Alloc(_cbTlsRaw, HEAP_ZERO_MEMORY); + if (!_pRawTlsData) + { + // 释放不是安全的,极小的概率可能野,但是现在现在就这样吧。 + Free(_pOldTlsIndex); + return false; + } + + memcpy(_pRawTlsData, (void*)_tls_used.StartAddressOfRawData, _cbTlsRaw); + InterlockedExchange((uintptr_t*)&_pTlsIndex[_tls_index], (uintptr_t)_pRawTlsData); + + s_CurrentNode.pBase = _pRawTlsData; + s_CurrentNode.pOldTlsIndex = _pOldTlsIndex; + + 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; + + if (s_CurrentNode.pOldTlsIndex) + { + Free(s_CurrentNode.pOldTlsIndex); + s_CurrentNode.pOldTlsIndex = nullptr; + } + + 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 bool __fastcall CreateTlsIndex() noexcept + { + if (_tls_index != 0) + return false; + + _tls_index = GetMaxTlsIndex() + 1; + AllocTlsData((TEB*)NtCurrentTeb()); + + // 同时给所有历史的线程追加新DLL产生的Tls内存 + do + { + const auto _pfnNtQueryInformationThread = try_get_NtQueryInformationThread(); + if (!_pfnNtQueryInformationThread) + break; + + StringBuffer _Buffer; + auto _pProcessInfo = GetCurrentProcessInfo(_Buffer); + if (!_pProcessInfo) + { + break; + } + + const auto _uCurrentThreadId = GetCurrentThreadId(); + + for (ULONG i = 0; i != _pProcessInfo->ThreadCount; ++i) + { + auto& _Thread = _pProcessInfo->Threads[i]; + + if (_uCurrentThreadId == (DWORD)_Thread.ClientId.UniqueThread) + continue; + + auto _hThread = OpenThread(THREAD_QUERY_INFORMATION, FALSE, (DWORD)_Thread.ClientId.UniqueThread); + if (!_hThread) + { + continue; + } + + THREAD_BASIC_INFORMATION _ThreadBasicInfo = {}; + LONG _Status = _pfnNtQueryInformationThread(_hThread, ThreadBasicInformation, &_ThreadBasicInfo, sizeof(_ThreadBasicInfo), nullptr); + if (_Status >= 0 && _ThreadBasicInfo.TebBaseAddress) + { + AllocTlsData((TEB*)_ThreadBasicInfo.TebBaseAddress); + } + + CloseHandle(_hThread); + } + } while (false); + + return true; + } + + static void __fastcall FreeTlsIndex() noexcept + { + if (_tls_index == 0) + return; + + // 故意不加锁…… + for (auto _pItem = pRoot; _pItem;) + { + auto _pNext = _pItem->pNext; + if (_pItem->pOldTlsIndex) + { + Free(_pItem->pOldTlsIndex); + _pItem->pOldTlsIndex = nullptr; + } + 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(_tls_used.AddressOfCallBacks); + if (_pfnTlsCallbacks) + { + for (; *_pfnTlsCallbacks; ++_pfnTlsCallbacks) + { + (*_pfnTlsCallbacks)(_hInstance, _uReason, nullptr); + } + } + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + } + } +#endif // YY_Thunks_Support_Version < NTDDI_WIN6 + + // 我们始终提供DllMainCRTStartupForYY_Thunks函数,虽然理论上Vista(包含)以上就不用修正了,但是为了做到体验统一 + // 避免Vista模式设置 DllMainCRTStartupForYY_Thunks 就发生链接失败问题。 + // 这个函数的存在意义是修复Windows XP系统显式加载的DLL里无法使用TLS问题。 + extern "C" BOOL WINAPI DllMainCRTStartupForYY_Thunks( + HINSTANCE const _hInstance, + DWORD const _uReason, + LPVOID const _pReserved + ) + { + const auto _pfnDllMainCRTStartup = __pfnDllMainCRTStartupForYY_Thunks ? __pfnDllMainCRTStartupForYY_Thunks : &_DllMainCRTStartup; + +#if YY_Thunks_Support_Version < NTDDI_WIN6 + __declspec(allocate(".CRT$XLB")) static const PIMAGE_TLS_CALLBACK s_FirstCallback = FirstCallback; + __foreinclude(s_FirstCallback); + __foreinclude(_tls_used); + if (internal::GetSystemVersion() < internal::MakeVersion(6, 0)) + { + // Vista开始已经可以动态的处理TLS问题了,所以这里只针对老系统处理。 + switch (_uReason) + { + case DLL_PROCESS_ATTACH: + if (g_TlsMode == TlsMode::None) + { + g_TlsMode = TlsMode::ByDllMainCRTStartupForYY_Thunks; + } + _tls_index_old = _tls_index; + if (_tls_index == 0 && g_TlsMode == TlsMode::ByDllMainCRTStartupForYY_Thunks) + { + CreateTlsIndex(); + return _pfnDllMainCRTStartup(_hInstance, _uReason, _pReserved); + } + break; + case DLL_THREAD_ATTACH: + if (_tls_index_old == 0 && g_TlsMode == TlsMode::ByDllMainCRTStartupForYY_Thunks) + { + AllocTlsData(); + CallTlsCallback(_hInstance, _uReason); + return _pfnDllMainCRTStartup(_hInstance, _uReason, _pReserved); + } + break; + case DLL_THREAD_DETACH: + if (_tls_index_old == 0 && g_TlsMode == TlsMode::ByDllMainCRTStartupForYY_Thunks) + { + CallTlsCallback(_hInstance, _uReason); + auto _bRet = _pfnDllMainCRTStartup(_hInstance, _uReason, _pReserved); + FreeTlsData(); + return _bRet; + } + break; + case DLL_PROCESS_DETACH: +#if (YY_Thunks_Support_Version < NTDDI_WINXP) + __YY_Thunks_Process_Terminating = _pReserved != nullptr; +#endif + if (_tls_index_old == 0 && g_TlsMode == TlsMode::ByDllMainCRTStartupForYY_Thunks) + { + CallTlsCallback(_hInstance, _uReason); + auto _bRet = _pfnDllMainCRTStartup(_hInstance, _uReason, _pReserved); + FreeTlsIndex(); + return _bRet; + } + break; + } + } +#endif + return _pfnDllMainCRTStartup(_hInstance, _uReason, _pReserved); + } +} +#endif diff --git a/src/Thunks/YY_Thunks.h b/src/Thunks/YY_Thunks.h index a6b6df5..2f209bd 100644 --- a/src/Thunks/YY_Thunks.h +++ b/src/Thunks/YY_Thunks.h @@ -174,11 +174,11 @@ static __forceinline unsigned __int64 __fastcall __crt_rotate_pointer_value(unsi #ifdef _WIN64 #define __foreinclude(p) \ __pragma(warning(suppress:6031)) \ - _bittest64(reinterpret_cast(&p), 0) + _bittest64((LONG_PTR*)(&p), 0) #else #define __foreinclude(p) \ __pragma(warning(suppress:6031)) \ - _bittest(reinterpret_cast(&p), 0) + _bittest((LONG_PTR*)(&p), 0) #endif diff --git a/src/YY-Thunks.UnitTest/YY-Thunks.UnitTest.vcxproj b/src/YY-Thunks.UnitTest/YY-Thunks.UnitTest.vcxproj index 5f2fd8f..b7ce630 100644 --- a/src/YY-Thunks.UnitTest/YY-Thunks.UnitTest.vcxproj +++ b/src/YY-Thunks.UnitTest/YY-Thunks.UnitTest.vcxproj @@ -110,6 +110,7 @@ true true $(VCInstallDir)UnitTest\lib;%(AdditionalLibraryDirectories) + DllMainCRTStartupForYY_Thunks @@ -128,6 +129,7 @@ Windows $(VCInstallDir)UnitTest\lib;%(AdditionalLibraryDirectories) + DllMainCRTStartupForYY_Thunks @@ -146,6 +148,7 @@ Windows $(VCInstallDir)UnitTest\lib;%(AdditionalLibraryDirectories) + DllMainCRTStartupForYY_Thunks @@ -168,6 +171,7 @@ true true $(VCInstallDir)UnitTest\lib;%(AdditionalLibraryDirectories) + DllMainCRTStartupForYY_Thunks @@ -222,6 +226,7 @@ + diff --git a/src/YY-Thunks.UnitTest/YY-Thunks.UnitTest.vcxproj.filters b/src/YY-Thunks.UnitTest/YY-Thunks.UnitTest.vcxproj.filters index b34511e..3cbda50 100644 --- a/src/YY-Thunks.UnitTest/YY-Thunks.UnitTest.vcxproj.filters +++ b/src/YY-Thunks.UnitTest/YY-Thunks.UnitTest.vcxproj.filters @@ -308,6 +308,9 @@ Thunks + + Thunks + diff --git a/src/YY-Thunks.UnitTest/weak.c b/src/YY-Thunks.UnitTest/weak.c index d5e8855..6cd4b85 100644 --- a/src/YY-Thunks.UnitTest/weak.c +++ b/src/YY-Thunks.UnitTest/weak.c @@ -2,3 +2,5 @@ const int __YY_Thunks_Process_Terminating; const void* __acrt_atexit_table; + +const void* __pfnDllMainCRTStartupForYY_Thunks;