-
Notifications
You must be signed in to change notification settings - Fork 1.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
winapisupp.cpp
: Dramatically shrink the function pointer table
#2841
Changes from all commits
805c1bc
c3a7fee
5765cd9
9165510
407a0e7
975c4cf
2e13066
da8d4d9
037445b
86eb557
c80d039
1472f80
2f2877b
75e974d
68e342e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -14,11 +14,36 @@ | |
#pragma warning(disable : 4265) // non-virtual destructor in base class | ||
#include <wrl/wrappers/corewrappers.h> | ||
#pragma warning(pop) | ||
#include <cstdint> | ||
|
||
#if !defined(_ONECORE) | ||
namespace { | ||
|
||
enum wrapKERNEL32Functions { | ||
#if !defined(_CRT_WINDOWS) && !defined(UNDOCKED_WINDOWS_UCRT) | ||
eGetCurrentPackageId, | ||
#endif // !defined(_CRT_WINDOWS) && !defined(UNDOCKED_WINDOWS_UCRT) | ||
|
||
#if _STL_WIN32_WINNT < _WIN32_WINNT_WIN8 | ||
eGetSystemTimePreciseAsFileTime, | ||
#endif // _STL_WIN32_WINNT < _WIN32_WINNT_WIN8 | ||
|
||
eMaxKernel32Function | ||
}; | ||
|
||
PVOID __KERNEL32Functions[eMaxKernel32Function > 0 ? eMaxKernel32Function : 1]{}; | ||
|
||
// Use this macro for caching a function pointer from a DLL | ||
#define STOREFUNCTIONPOINTER(instance, function_name) \ | ||
__KERNEL32Functions[e##function_name] = reinterpret_cast<PVOID>(GetProcAddress(instance, #function_name)) | ||
|
||
// Use this macro for retrieving a cached function pointer from a DLL | ||
#define IFDYNAMICGETCACHEDFUNCTION(name) \ | ||
if (const auto pf##name = reinterpret_cast<decltype(&name)>(__KERNEL32Functions[e##name]); pf##name) | ||
|
||
Comment on lines
+36
to
+42
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. now that we are only doing this a few times it might be easier to just expand these macros. If we're going to use macros for this we could also use xmacros to define the above enumeration and the below initialization function at the same time. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hmm - the primary value of the macros is now enforcing the consistent use of X-macros are definitely a potential technique, although they are quite powerful/opaque. As there are a small number of locations (definition, initialization, usage) and they're slowly-changing, I tend to think that we don't need such machinery at this time - especially now that the number of enumerators is so much smaller. I'll think about refactoring this in a followup PR. I think it might be feasible to have one macro for definition (using Thanks for bringing this up! |
||
} // unnamed namespace | ||
#endif // ^^^ !defined(_ONECORE) ^^^ | ||
|
||
#if !defined(_CRT_WINDOWS) && !defined(UNDOCKED_WINDOWS_UCRT) | ||
// GetCurrentPackageId retrieves the current package id, if the app is deployed via a package. | ||
using PFNGETCURRENTPACKAGEID = BOOL(WINAPI*)(UINT32*, BYTE*); | ||
|
||
#if !defined _CRT_APP | ||
#if defined _ONECORE | ||
|
@@ -46,16 +71,14 @@ extern "C" int __crt_IsPackagedAppHelper() { | |
L"appmodel.dll" // LNM implementation DLL | ||
}; | ||
|
||
wchar_t const* const* const first_possible_apiset = possible_apisets; | ||
wchar_t const* const* const last_possible_apiset = possible_apisets + _countof(possible_apisets); | ||
for (wchar_t const* const* it = first_possible_apiset; it != last_possible_apiset; ++it) { | ||
HMODULEHandle const apiset(LoadLibraryExW(*it, nullptr, LOAD_LIBRARY_SEARCH_SYSTEM32)); | ||
for (auto& dll : possible_apisets) { | ||
HMODULEHandle const apiset(LoadLibraryExW(dll, nullptr, LOAD_LIBRARY_SEARCH_SYSTEM32)); | ||
if (!apiset.IsValid()) { | ||
continue; | ||
} | ||
|
||
PFNGETCURRENTPACKAGEID const get_current_package_id = | ||
reinterpret_cast<PFNGETCURRENTPACKAGEID>(GetProcAddress(apiset.Get(), "GetCurrentPackageId")); | ||
auto const get_current_package_id = | ||
reinterpret_cast<decltype(&GetCurrentPackageId)>(GetProcAddress(apiset.Get(), "GetCurrentPackageId")); | ||
|
||
if (!get_current_package_id) { | ||
continue; | ||
|
@@ -79,8 +102,8 @@ extern "C" int __crt_IsPackagedAppHelper() { | |
LONG retValue = APPMODEL_ERROR_NO_PACKAGE; | ||
UINT32 bufferLength = 0; | ||
|
||
IFDYNAMICGETCACHEDFUNCTION(PFNGETCURRENTPACKAGEID, GetCurrentPackageId, pfn) { | ||
retValue = pfn(&bufferLength, nullptr); | ||
IFDYNAMICGETCACHEDFUNCTION(GetCurrentPackageId) { | ||
retValue = pfGetCurrentPackageId(&bufferLength, nullptr); | ||
} | ||
|
||
if (retValue == ERROR_INSUFFICIENT_BUFFER) { | ||
|
@@ -110,7 +133,8 @@ extern "C" int __crt_IsPackagedAppHelper() { | |
// | ||
// Exit: | ||
// TRUE if Packaged app, FALSE if not. | ||
extern "C" BOOL __cdecl __crtIsPackagedApp() { | ||
// TRANSITION, ABI: preserved for binary compatibility | ||
extern "C" _CRTIMP2 BOOL __cdecl __crtIsPackagedApp() { | ||
#ifdef _CRT_APP | ||
return TRUE; | ||
#else | ||
|
@@ -349,8 +373,7 @@ extern "C" BOOLEAN __cdecl __crtTryAcquireSRWLockExclusive(_Inout_ PSRWLOCK cons | |
|
||
extern "C" void __cdecl __crtGetSystemTimePreciseAsFileTime(_Out_ LPFILETIME lpSystemTimeAsFileTime) { | ||
// use GetSystemTimePreciseAsFileTime if it is available (only on Windows 8+)... | ||
IFDYNAMICGETCACHEDFUNCTION( | ||
PFNGETSYSTEMTIMEPRECISEASFILETIME, GetSystemTimePreciseAsFileTime, pfGetSystemTimePreciseAsFileTime) { | ||
IFDYNAMICGETCACHEDFUNCTION(GetSystemTimePreciseAsFileTime) { | ||
pfGetSystemTimePreciseAsFileTime(lpSystemTimeAsFileTime); | ||
return; | ||
} | ||
|
@@ -370,64 +393,17 @@ extern "C" void __cdecl __crtGetSystemTimePreciseAsFileTime(_Out_ LPFILETIME lpS | |
|
||
#else // defined _ONECORE | ||
|
||
extern "C" PVOID __KERNEL32Functions[eMaxKernel32Function] = {nullptr}; | ||
|
||
static int __cdecl initialize_pointers() { | ||
HINSTANCE hKernel32 = GetModuleHandleW(L"kernel32.dll"); | ||
_Analysis_assume_(hKernel32); | ||
|
||
STOREFUNCTIONPOINTER(hKernel32, FlsAlloc); | ||
STOREFUNCTIONPOINTER(hKernel32, FlsFree); | ||
STOREFUNCTIONPOINTER(hKernel32, FlsGetValue); | ||
STOREFUNCTIONPOINTER(hKernel32, FlsSetValue); | ||
STOREFUNCTIONPOINTER(hKernel32, InitializeCriticalSectionEx); | ||
STOREFUNCTIONPOINTER(hKernel32, InitOnceExecuteOnce); | ||
STOREFUNCTIONPOINTER(hKernel32, CreateEventExW); | ||
STOREFUNCTIONPOINTER(hKernel32, CreateSemaphoreW); | ||
STOREFUNCTIONPOINTER(hKernel32, CreateSemaphoreExW); | ||
STOREFUNCTIONPOINTER(hKernel32, CreateThreadpoolTimer); | ||
STOREFUNCTIONPOINTER(hKernel32, SetThreadpoolTimer); | ||
STOREFUNCTIONPOINTER(hKernel32, WaitForThreadpoolTimerCallbacks); | ||
STOREFUNCTIONPOINTER(hKernel32, CloseThreadpoolTimer); | ||
STOREFUNCTIONPOINTER(hKernel32, CreateThreadpoolWait); | ||
STOREFUNCTIONPOINTER(hKernel32, SetThreadpoolWait); | ||
STOREFUNCTIONPOINTER(hKernel32, CloseThreadpoolWait); | ||
STOREFUNCTIONPOINTER(hKernel32, FlushProcessWriteBuffers); | ||
STOREFUNCTIONPOINTER(hKernel32, FreeLibraryWhenCallbackReturns); | ||
STOREFUNCTIONPOINTER(hKernel32, GetCurrentProcessorNumber); | ||
STOREFUNCTIONPOINTER(hKernel32, CreateSymbolicLinkW); | ||
#if defined(_CRT_WINDOWS) || defined(UNDOCKED_WINDOWS_UCRT) | ||
STOREFUNCTIONPOINTER(hKernel32, SetDefaultDllDirectories); | ||
STOREFUNCTIONPOINTER(hKernel32, EnumSystemLocalesEx); | ||
STOREFUNCTIONPOINTER(hKernel32, CompareStringEx); | ||
STOREFUNCTIONPOINTER(hKernel32, GetLocaleInfoEx); | ||
STOREFUNCTIONPOINTER(hKernel32, GetUserDefaultLocaleName); | ||
STOREFUNCTIONPOINTER(hKernel32, IsValidLocaleName); | ||
STOREFUNCTIONPOINTER(hKernel32, LCMapStringEx); | ||
#else | ||
#if !defined(_CRT_WINDOWS) && !defined(UNDOCKED_WINDOWS_UCRT) | ||
STOREFUNCTIONPOINTER(hKernel32, GetCurrentPackageId); | ||
#endif | ||
STOREFUNCTIONPOINTER(hKernel32, GetTickCount64); | ||
STOREFUNCTIONPOINTER(hKernel32, GetFileInformationByHandleEx); | ||
STOREFUNCTIONPOINTER(hKernel32, SetFileInformationByHandle); | ||
#endif // !defined(_CRT_WINDOWS) && !defined(UNDOCKED_WINDOWS_UCRT) | ||
|
||
#if _STL_WIN32_WINNT < _WIN32_WINNT_WIN8 | ||
STOREFUNCTIONPOINTER(hKernel32, GetSystemTimePreciseAsFileTime); | ||
STOREFUNCTIONPOINTER(hKernel32, InitializeConditionVariable); | ||
STOREFUNCTIONPOINTER(hKernel32, WakeConditionVariable); | ||
STOREFUNCTIONPOINTER(hKernel32, WakeAllConditionVariable); | ||
STOREFUNCTIONPOINTER(hKernel32, SleepConditionVariableCS); | ||
STOREFUNCTIONPOINTER(hKernel32, InitializeSRWLock); | ||
STOREFUNCTIONPOINTER(hKernel32, AcquireSRWLockExclusive); | ||
STOREFUNCTIONPOINTER(hKernel32, TryAcquireSRWLockExclusive); | ||
STOREFUNCTIONPOINTER(hKernel32, ReleaseSRWLockExclusive); | ||
STOREFUNCTIONPOINTER(hKernel32, SleepConditionVariableSRW); | ||
STOREFUNCTIONPOINTER(hKernel32, CreateThreadpoolWork); | ||
STOREFUNCTIONPOINTER(hKernel32, SubmitThreadpoolWork); | ||
STOREFUNCTIONPOINTER(hKernel32, CloseThreadpoolWork); | ||
#if !defined(_CRT_WINDOWS) && !defined(UNDOCKED_WINDOWS_UCRT) | ||
STOREFUNCTIONPOINTER(hKernel32, CompareStringEx); | ||
STOREFUNCTIONPOINTER(hKernel32, GetLocaleInfoEx); | ||
STOREFUNCTIONPOINTER(hKernel32, LCMapStringEx); | ||
#endif | ||
#endif // _STL_WIN32_WINNT < _WIN32_WINNT_WIN8 | ||
|
||
return 0; | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it strikes me as not the best idea to preprocess out enumerators like this without explicitly assigning values. If this is ever passed across ABI boundaries (it does not appear to be now) it might blow up pretty immediately
Pre-existing though
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agreed, pre-existing. I believe that the risk is low (as you mentioned, this table and its enumerators aren't part of the ABI surface area - also, the conditions are inherently of the form "how is the STL being built"), and it's further mitigated by this PR, as the table and its enumerators are now confined to an unnamed namespace in a single TU.
That said, it now seems to me that the case for having a table is weaker than ever. We never iterate over the table, so we could simply use separate function pointers (which the compiler could still store densely). That would avoid any concerns about varying enumerator values, and actually make debugging potentially easier. I may explore this in a followup PR.