[misc] attempt to fix DLL address resolver for ARM64

* SymLoadModuleEx() on the DLL fails (with ERROR_SUCCESS, sic) on ARM64, so we have to resort
  to loading the DLL in memory and look for the "RSDS" section to access the GUID and Age.
* Except that, once you sort that, you end up with SymEnumSymbols() producing two separate
  addresses for each symbol, on account that the Windows 11 ARM64 DLLs are actually an
  unholy union of X64 and ARM64 *in the same binary*, under an abomination that Microsoft
  calls ARM64X (See https://learn.microsoft.com/en-us/windows/arm/arm64x-pe for details).
* And, of course, Microsoft did not provide any means during PDB enumeration to tell which
  address is for which arch. Especially, the bloody pSymInfo->TypeIndex, that they could
  easily have used for this, is utterly pointless as it remains set to 0...
* This means that, even after all this effort, we're still nowhere close to have a solution
  that can make DLL address resolution work on Windows 11 ARM64.
This commit is contained in:
Pete Batard 2023-07-05 22:25:25 +01:00
parent 0363bfe503
commit f411d526d6
No known key found for this signature in database
GPG Key ID: 38E0CF5E69EDD671
2 changed files with 61 additions and 27 deletions

View File

@ -33,7 +33,7 @@ LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL
IDD_DIALOG DIALOGEX 12, 12, 232, 326 IDD_DIALOG DIALOGEX 12, 12, 232, 326
STYLE DS_SETFONT | DS_MODALFRAME | DS_CENTER | WS_MINIMIZEBOX | WS_POPUP | WS_CAPTION | WS_SYSMENU STYLE DS_SETFONT | DS_MODALFRAME | DS_CENTER | WS_MINIMIZEBOX | WS_POPUP | WS_CAPTION | WS_SYSMENU
EXSTYLE WS_EX_ACCEPTFILES EXSTYLE WS_EX_ACCEPTFILES
CAPTION "Rufus 4.2.2066" CAPTION "Rufus 4.2.2067"
FONT 9, "Segoe UI Symbol", 400, 0, 0x0 FONT 9, "Segoe UI Symbol", 400, 0, 0x0
BEGIN BEGIN
LTEXT "Drive Properties",IDS_DRIVE_PROPERTIES_TXT,8,6,53,12,NOT WS_GROUP LTEXT "Drive Properties",IDS_DRIVE_PROPERTIES_TXT,8,6,53,12,NOT WS_GROUP
@ -392,8 +392,8 @@ END
// //
VS_VERSION_INFO VERSIONINFO VS_VERSION_INFO VERSIONINFO
FILEVERSION 4,2,2066,0 FILEVERSION 4,2,2067,0
PRODUCTVERSION 4,2,2066,0 PRODUCTVERSION 4,2,2067,0
FILEFLAGSMASK 0x3fL FILEFLAGSMASK 0x3fL
#ifdef _DEBUG #ifdef _DEBUG
FILEFLAGS 0x1L FILEFLAGS 0x1L
@ -411,13 +411,13 @@ BEGIN
VALUE "Comments", "https://rufus.ie" VALUE "Comments", "https://rufus.ie"
VALUE "CompanyName", "Akeo Consulting" VALUE "CompanyName", "Akeo Consulting"
VALUE "FileDescription", "Rufus" VALUE "FileDescription", "Rufus"
VALUE "FileVersion", "4.2.2066" VALUE "FileVersion", "4.2.2067"
VALUE "InternalName", "Rufus" VALUE "InternalName", "Rufus"
VALUE "LegalCopyright", "© 2011-2023 Pete Batard (GPL v3)" VALUE "LegalCopyright", "© 2011-2023 Pete Batard (GPL v3)"
VALUE "LegalTrademarks", "https://www.gnu.org/licenses/gpl-3.0.html" VALUE "LegalTrademarks", "https://www.gnu.org/licenses/gpl-3.0.html"
VALUE "OriginalFilename", "rufus-4.2.exe" VALUE "OriginalFilename", "rufus-4.2.exe"
VALUE "ProductName", "Rufus" VALUE "ProductName", "Rufus"
VALUE "ProductVersion", "4.2.2066" VALUE "ProductVersion", "4.2.2067"
END END
END END
BLOCK "VarFileInfo" BLOCK "VarFileInfo"

View File

@ -42,6 +42,7 @@
#define FACILITY_WIM 322 #define FACILITY_WIM 322
#define DEFAULT_BASE_ADDRESS 0x100000000ULL #define DEFAULT_BASE_ADDRESS 0x100000000ULL
#define RSDS_SIG 0x53445352
/* /*
* Globals * Globals
@ -51,6 +52,15 @@ HWND hStatus;
size_t ubuffer_pos = 0; size_t ubuffer_pos = 0;
char ubuffer[UBUFFER_SIZE]; // Buffer for ubpushf() messages we don't log right away char ubuffer[UBUFFER_SIZE]; // Buffer for ubpushf() messages we don't log right away
#pragma pack(push, 1)
typedef struct {
DWORD Signature; // "RSDS"
GUID Guid;
DWORD Age;
CHAR PdbName[1];
} debug_info_t;
#pragma pack(pop)
void uprintf(const char *format, ...) void uprintf(const char *format, ...)
{ {
static char buf[4096]; static char buf[4096];
@ -1157,7 +1167,6 @@ HANDLE CreatePreallocatedFile(const char* lpFileName, DWORD dwDesiredAccess,
PF_TYPE_DECL(WINAPI, BOOL, SymInitialize, (HANDLE, PCSTR, BOOL)); PF_TYPE_DECL(WINAPI, BOOL, SymInitialize, (HANDLE, PCSTR, BOOL));
PF_TYPE_DECL(WINAPI, DWORD64, SymLoadModuleEx, (HANDLE, HANDLE, PCSTR, PCSTR, DWORD64, DWORD, PMODLOAD_DATA, DWORD)); PF_TYPE_DECL(WINAPI, DWORD64, SymLoadModuleEx, (HANDLE, HANDLE, PCSTR, PCSTR, DWORD64, DWORD, PMODLOAD_DATA, DWORD));
PF_TYPE_DECL(WINAPI, BOOL, SymGetModuleInfo64, (HANDLE, DWORD64, PIMAGEHLP_MODULE64));
PF_TYPE_DECL(WINAPI, BOOL, SymUnloadModule64, (HANDLE, DWORD64)); PF_TYPE_DECL(WINAPI, BOOL, SymUnloadModule64, (HANDLE, DWORD64));
PF_TYPE_DECL(WINAPI, BOOL, SymEnumSymbols, (HANDLE, ULONG64, PCSTR, PSYM_ENUMERATESYMBOLS_CALLBACK, PVOID)); PF_TYPE_DECL(WINAPI, BOOL, SymEnumSymbols, (HANDLE, ULONG64, PCSTR, PSYM_ENUMERATESYMBOLS_CALLBACK, PVOID));
PF_TYPE_DECL(WINAPI, BOOL, SymCleanup, (HANDLE)); PF_TYPE_DECL(WINAPI, BOOL, SymCleanup, (HANDLE));
@ -1183,40 +1192,50 @@ uint32_t ResolveDllAddress(dll_resolver_t* resolver)
{ {
uint32_t r = 0; uint32_t r = 0;
uint32_t i; uint32_t i;
debug_info_t* info = NULL;
char url[MAX_PATH], saved_id[MAX_PATH], path[MAX_PATH]; char url[MAX_PATH], saved_id[MAX_PATH], path[MAX_PATH];
DWORD64 base_address; uint8_t* buf = NULL;
IMAGEHLP_MODULE64 info = { sizeof(IMAGEHLP_MODULE64) }; DWORD* dbuf;
DWORD64 base_address = 0ULL;
PF_INIT(SymInitialize, DbgHelp); PF_INIT(SymInitialize, DbgHelp);
PF_INIT(SymLoadModuleEx, DbgHelp); PF_INIT(SymLoadModuleEx, DbgHelp);
PF_INIT(SymGetModuleInfo64, DbgHelp);
PF_INIT(SymUnloadModule64, DbgHelp); PF_INIT(SymUnloadModule64, DbgHelp);
PF_INIT(SymEnumSymbols, DbgHelp); PF_INIT(SymEnumSymbols, DbgHelp);
PF_INIT(SymCleanup, DbgHelp); PF_INIT(SymCleanup, DbgHelp);
if (pfSymInitialize == NULL || pfSymLoadModuleEx == NULL || pfSymGetModuleInfo64 == NULL || if (pfSymInitialize == NULL || pfSymLoadModuleEx == NULL || pfSymUnloadModule64 == NULL ||
pfSymUnloadModule64 == NULL || pfSymEnumSymbols == NULL || pfSymCleanup == NULL || pfSymEnumSymbols == NULL || pfSymCleanup == NULL || resolver->count == 0 ||
resolver->count == 0 || resolver->path == NULL || resolver->name == NULL || resolver->address == NULL) resolver->path == NULL || resolver->name == NULL || resolver->address == NULL)
return 0; return 0;
if (!pfSymInitialize(hRufus, NULL, FALSE)) { // Get the PDB unique address from the DLL. Note that we can *NOT* use SymGetModuleInfo64() to
uprintf("Could not initialize DLL symbol handler"); // obtain the data we need because Microsoft either *BOTCHED* or *DELIBERATELY CRIPPLED* their
// SymLoadModuleEx()/SymLoadModule64() implementation on ARM64, so that the return value is always
// 0 with GetLastError() set to ERROR_SUCCESS, thereby *FALSELY* indicating that the module is
// already loaded... So we just load the whole DLL into a buffer and look for an "RSDS" section
// per https://www.godevtool.com/Other/pdb.htm
r = read_file(resolver->path, &buf);
if (r == 0)
return 0; return 0;
dbuf = (DWORD*)buf;
for (i = 0; i < (r - sizeof(debug_info_t)) / sizeof(DWORD); i++) {
if (dbuf[i] == RSDS_SIG) {
info = (debug_info_t*)&dbuf[i];
if (safe_strstr(info->PdbName, ".pdb") != NULL)
break;
}
} }
if (info == NULL) {
// Get the PDB unique address from the DLL uprintf("Could not find debug info in '%s'", resolver->path);
base_address = pfSymLoadModuleEx(hRufus, NULL, resolver->path, NULL, DEFAULT_BASE_ADDRESS, 0, NULL, 0);
if (base_address == 0ULL || !pfSymGetModuleInfo64(hRufus, base_address, &info)) {
uprintf("Could not obtain DLL symbol info");
goto out; goto out;
} }
assert(base_address == DEFAULT_BASE_ADDRESS);
pfSymUnloadModule64(hRufus, base_address);
// Check settings to see if we have existing data for these DLL calls. // Check settings to see if we have existing data for these DLL calls.
for (i = 0; i < resolver->count; i++) { for (i = 0; i < resolver->count; i++) {
static_sprintf(saved_id, "%s@%s%x:%s", _filenameU(resolver->path), static_sprintf(saved_id, "%s@%s%x:%s", _filenameU(resolver->path),
GuidToString(&info.PdbSig70, FALSE), (int)info.PdbAge, resolver->name[i]); GuidToString(&info->Guid, FALSE), info->Age, resolver->name[i]);
resolver->address[i] = ReadSetting32(saved_id); resolver->address[i] = ReadSetting32(saved_id);
if (resolver->address[i] == 0) if (resolver->address[i] == 0)
break; break;
@ -1232,15 +1251,28 @@ uint32_t ResolveDllAddress(dll_resolver_t* resolver)
if (MessageBoxExU(hMainDialog, lmprintf(MSG_345), lmprintf(MSG_115), if (MessageBoxExU(hMainDialog, lmprintf(MSG_345), lmprintf(MSG_115),
MB_YESNO | MB_ICONWARNING | MB_IS_RTL, selected_langid) != IDYES) MB_YESNO | MB_ICONWARNING | MB_IS_RTL, selected_langid) != IDYES)
goto out; goto out;
static_sprintf(path, "%s\\%s.pdb", temp_dir, info.ModuleName); static_sprintf(path, "%s\\%s", temp_dir, info->PdbName);
static_sprintf(url, "http://msdl.microsoft.com/download/symbols/%s.pdb/%s%x/%s.pdb", static_sprintf(url, "http://msdl.microsoft.com/download/symbols/%s/%s%x/%s",
info.ModuleName, GuidToString(&info.PdbSig70, FALSE), (int)info.PdbAge, info.ModuleName); info->PdbName, GuidToString(&info->Guid, FALSE), info->Age, info->PdbName);
if (DownloadToFileOrBufferEx(url, path, SYMBOL_SERVER_USER_AGENT, NULL, hMainDialog, FALSE) < 200 * KB) if (DownloadToFileOrBufferEx(url, path, SYMBOL_SERVER_USER_AGENT, NULL, hMainDialog, FALSE) < 200 * KB)
goto out; goto out;
if (!pfSymInitialize(hRufus, NULL, FALSE)) {
uprintf("Could not initialize DLL symbol handler");
goto out;
}
// NB: SymLoadModuleEx() does not load a PDB unless the file has an explicit '.pdb' extension // NB: SymLoadModuleEx() does not load a PDB unless the file has an explicit '.pdb' extension
base_address = pfSymLoadModuleEx(hRufus, NULL, path, NULL, DEFAULT_BASE_ADDRESS, 0, NULL, 0); base_address = pfSymLoadModuleEx(hRufus, NULL, path, NULL, DEFAULT_BASE_ADDRESS, 0, NULL, 0);
assert(base_address == DEFAULT_BASE_ADDRESS); assert(base_address == DEFAULT_BASE_ADDRESS);
// On Windows 11 ARM64 the following call will return *TWO* different addresses for the same
// call, because most Windows DLL's are ARM64X, which means that they are an unholy union of
// both X64 and ARM64 code in the same binary...
// See https://learn.microsoft.com/en-us/windows/arm/arm64x-pe
// Now this would be all swell and dandy if Microsoft's debugging/symbol APIs had followed
// and would give us a hint of the architecture behind each duplicate address, but of course,
// the SYMBOL_INFO passed to EnumSymProc contains no such data. So we currently don't have a
// way to tell which of the two addresses we get on ARM64 is for which architecture... :(
pfSymEnumSymbols(hRufus, base_address, "*!*", EnumSymProc, resolver); pfSymEnumSymbols(hRufus, base_address, "*!*", EnumSymProc, resolver);
DeleteFileU(path); DeleteFileU(path);
@ -1248,7 +1280,7 @@ uint32_t ResolveDllAddress(dll_resolver_t* resolver)
r = 0; r = 0;
for (i = 0; i < resolver->count; i++) { for (i = 0; i < resolver->count; i++) {
static_sprintf(saved_id, "%s@%s%x:%s", _filenameU(resolver->path), static_sprintf(saved_id, "%s@%s%x:%s", _filenameU(resolver->path),
GuidToString(&info.PdbSig70, FALSE), (int)info.PdbAge, resolver->name[i]); GuidToString(&info->Guid, FALSE), info->Age, resolver->name[i]);
if (resolver->address[i] != 0) { if (resolver->address[i] != 0) {
WriteSetting32(saved_id, resolver->address[i]); WriteSetting32(saved_id, resolver->address[i]);
r++; r++;
@ -1256,7 +1288,9 @@ uint32_t ResolveDllAddress(dll_resolver_t* resolver)
} }
out: out:
pfSymUnloadModule64(hRufus, base_address); free(buf);
if (base_address != 0)
pfSymUnloadModule64(hRufus, base_address);
pfSymCleanup(hRufus); pfSymCleanup(hRufus);
return r; return r;
} }