From f411d526d699a2c5e2334646b0641b888d1fc546 Mon Sep 17 00:00:00 2001 From: Pete Batard Date: Wed, 5 Jul 2023 22:25:25 +0100 Subject: [PATCH] [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. --- src/rufus.rc | 10 +++---- src/stdio.c | 78 +++++++++++++++++++++++++++++++++++++--------------- 2 files changed, 61 insertions(+), 27 deletions(-) diff --git a/src/rufus.rc b/src/rufus.rc index 0f3d4b52..c468a3d4 100644 --- a/src/rufus.rc +++ b/src/rufus.rc @@ -33,7 +33,7 @@ LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL IDD_DIALOG DIALOGEX 12, 12, 232, 326 STYLE DS_SETFONT | DS_MODALFRAME | DS_CENTER | WS_MINIMIZEBOX | WS_POPUP | WS_CAPTION | WS_SYSMENU EXSTYLE WS_EX_ACCEPTFILES -CAPTION "Rufus 4.2.2066" +CAPTION "Rufus 4.2.2067" FONT 9, "Segoe UI Symbol", 400, 0, 0x0 BEGIN LTEXT "Drive Properties",IDS_DRIVE_PROPERTIES_TXT,8,6,53,12,NOT WS_GROUP @@ -392,8 +392,8 @@ END // VS_VERSION_INFO VERSIONINFO - FILEVERSION 4,2,2066,0 - PRODUCTVERSION 4,2,2066,0 + FILEVERSION 4,2,2067,0 + PRODUCTVERSION 4,2,2067,0 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L @@ -411,13 +411,13 @@ BEGIN VALUE "Comments", "https://rufus.ie" VALUE "CompanyName", "Akeo Consulting" VALUE "FileDescription", "Rufus" - VALUE "FileVersion", "4.2.2066" + VALUE "FileVersion", "4.2.2067" VALUE "InternalName", "Rufus" VALUE "LegalCopyright", "© 2011-2023 Pete Batard (GPL v3)" VALUE "LegalTrademarks", "https://www.gnu.org/licenses/gpl-3.0.html" VALUE "OriginalFilename", "rufus-4.2.exe" VALUE "ProductName", "Rufus" - VALUE "ProductVersion", "4.2.2066" + VALUE "ProductVersion", "4.2.2067" END END BLOCK "VarFileInfo" diff --git a/src/stdio.c b/src/stdio.c index f784683b..073636ce 100644 --- a/src/stdio.c +++ b/src/stdio.c @@ -42,6 +42,7 @@ #define FACILITY_WIM 322 #define DEFAULT_BASE_ADDRESS 0x100000000ULL +#define RSDS_SIG 0x53445352 /* * Globals @@ -51,6 +52,15 @@ HWND hStatus; size_t ubuffer_pos = 0; 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, ...) { 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, 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, SymEnumSymbols, (HANDLE, ULONG64, PCSTR, PSYM_ENUMERATESYMBOLS_CALLBACK, PVOID)); PF_TYPE_DECL(WINAPI, BOOL, SymCleanup, (HANDLE)); @@ -1183,40 +1192,50 @@ uint32_t ResolveDllAddress(dll_resolver_t* resolver) { uint32_t r = 0; uint32_t i; + debug_info_t* info = NULL; char url[MAX_PATH], saved_id[MAX_PATH], path[MAX_PATH]; - DWORD64 base_address; - IMAGEHLP_MODULE64 info = { sizeof(IMAGEHLP_MODULE64) }; + uint8_t* buf = NULL; + DWORD* dbuf; + DWORD64 base_address = 0ULL; PF_INIT(SymInitialize, DbgHelp); PF_INIT(SymLoadModuleEx, DbgHelp); - PF_INIT(SymGetModuleInfo64, DbgHelp); PF_INIT(SymUnloadModule64, DbgHelp); PF_INIT(SymEnumSymbols, DbgHelp); PF_INIT(SymCleanup, DbgHelp); - if (pfSymInitialize == NULL || pfSymLoadModuleEx == NULL || pfSymGetModuleInfo64 == NULL || - pfSymUnloadModule64 == NULL || pfSymEnumSymbols == NULL || pfSymCleanup == NULL || - resolver->count == 0 || resolver->path == NULL || resolver->name == NULL || resolver->address == NULL) + if (pfSymInitialize == NULL || pfSymLoadModuleEx == NULL || pfSymUnloadModule64 == NULL || + pfSymEnumSymbols == NULL || pfSymCleanup == NULL || resolver->count == 0 || + resolver->path == NULL || resolver->name == NULL || resolver->address == NULL) return 0; - if (!pfSymInitialize(hRufus, NULL, FALSE)) { - uprintf("Could not initialize DLL symbol handler"); + // Get the PDB unique address from the DLL. Note that we can *NOT* use SymGetModuleInfo64() to + // 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; + + 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; + } } - - // Get the PDB unique address from the DLL - 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"); + if (info == NULL) { + uprintf("Could not find debug info in '%s'", resolver->path); 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. for (i = 0; i < resolver->count; i++) { 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); if (resolver->address[i] == 0) break; @@ -1232,15 +1251,28 @@ uint32_t ResolveDllAddress(dll_resolver_t* resolver) if (MessageBoxExU(hMainDialog, lmprintf(MSG_345), lmprintf(MSG_115), MB_YESNO | MB_ICONWARNING | MB_IS_RTL, selected_langid) != IDYES) goto out; - static_sprintf(path, "%s\\%s.pdb", temp_dir, info.ModuleName); - static_sprintf(url, "http://msdl.microsoft.com/download/symbols/%s.pdb/%s%x/%s.pdb", - info.ModuleName, GuidToString(&info.PdbSig70, FALSE), (int)info.PdbAge, info.ModuleName); + static_sprintf(path, "%s\\%s", temp_dir, info->PdbName); + static_sprintf(url, "http://msdl.microsoft.com/download/symbols/%s/%s%x/%s", + info->PdbName, GuidToString(&info->Guid, FALSE), info->Age, info->PdbName); if (DownloadToFileOrBufferEx(url, path, SYMBOL_SERVER_USER_AGENT, NULL, hMainDialog, FALSE) < 200 * KB) 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 base_address = pfSymLoadModuleEx(hRufus, NULL, path, NULL, DEFAULT_BASE_ADDRESS, 0, NULL, 0); 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); DeleteFileU(path); @@ -1248,7 +1280,7 @@ uint32_t ResolveDllAddress(dll_resolver_t* resolver) r = 0; for (i = 0; i < resolver->count; i++) { 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) { WriteSetting32(saved_id, resolver->address[i]); r++; @@ -1256,7 +1288,9 @@ uint32_t ResolveDllAddress(dll_resolver_t* resolver) } out: - pfSymUnloadModule64(hRufus, base_address); + free(buf); + if (base_address != 0) + pfSymUnloadModule64(hRufus, base_address); pfSymCleanup(hRufus); return r; }