diff --git a/src/coreclr/nativeaot/Runtime/Pal.h b/src/coreclr/nativeaot/Runtime/Pal.h index dc80f8d32ab61e..6a4c57ce7307a1 100644 --- a/src/coreclr/nativeaot/Runtime/Pal.h +++ b/src/coreclr/nativeaot/Runtime/Pal.h @@ -161,7 +161,7 @@ bool PalInit(); // Given the OS handle of a loaded module, compute the upper and lower virtual address bounds (inclusive). void PalGetModuleBounds(HANDLE hOsHandle, _Out_ uint8_t ** ppLowerBound, _Out_ uint8_t ** ppUpperBound); -void PalGetPDBInfo(HANDLE hOsHandle, GUID * pGuidSignature, _Out_ uint32_t * pdwAge, _Out_writes_z_(cchPath) WCHAR * wszPath, int32_t cchPath); +void PalGetPDBInfo(HANDLE hOsHandle, GUID * pGuidSignature, _Out_ uint32_t * pdwAge, _Out_writes_z_(cchPath) WCHAR * wszPath, int32_t cchPath, _Out_ uint32_t * pcbBuildId, _Out_ void ** ppBuildId); struct NATIVE_CONTEXT; diff --git a/src/coreclr/nativeaot/Runtime/eventpipe/gen-eventing-event-inc.lst b/src/coreclr/nativeaot/Runtime/eventpipe/gen-eventing-event-inc.lst index 40ba078c965e49..6047675921ff13 100644 --- a/src/coreclr/nativeaot/Runtime/eventpipe/gen-eventing-event-inc.lst +++ b/src/coreclr/nativeaot/Runtime/eventpipe/gen-eventing-event-inc.lst @@ -83,8 +83,8 @@ GenAwareBegin GenAwareEnd IncreaseMemoryPressure LockCreated -ModuleDCEnd_V2 -ModuleLoad_V2 +ModuleDCEnd_V3 +ModuleLoad_V3 PinObjectAtGCTime PinPlugAtGCTime PrvDestroyGCHandle diff --git a/src/coreclr/nativeaot/Runtime/eventtrace.cpp b/src/coreclr/nativeaot/Runtime/eventtrace.cpp index 83bc0ed476eba0..9463e980adad01 100644 --- a/src/coreclr/nativeaot/Runtime/eventtrace.cpp +++ b/src/coreclr/nativeaot/Runtime/eventtrace.cpp @@ -379,13 +379,32 @@ void ETW::LoaderLog::SendModuleEvent(HANDLE pModule, uint32_t dwEventOptions) GUID nativeGuid; uint32_t dwAge; WCHAR wszPath[1024]; - PalGetPDBInfo(pModule, &nativeGuid, &dwAge, wszPath, ARRAY_SIZE(wszPath)); + uint32_t cbBuildId; + void* pBuildId; + PalGetPDBInfo(pModule, &nativeGuid, &dwAge, wszPath, ARRAY_SIZE(wszPath), &cbBuildId, &pBuildId); + + WCHAR wszBuildId[65]; + size_t written = 0; + for (size_t i = 0; i < cbBuildId; i++) + { + if (written + 3 <= ARRAY_SIZE(wszBuildId)) { // 2 hex digits + 1 null terminator + const WCHAR* hexDigits = W("0123456789ABCDEF"); + // Convert each byte to hex and append to the output string + uint8_t c = ((uint8_t*)pBuildId)[i]; + wszBuildId[written++] = hexDigits[c >> 4]; + wszBuildId[written++] = hexDigits[c & 0xF]; + } else { + // If buffer not enough to fit, truncate + break; + } + } + wszBuildId[written] = 0; GUID zeroGuid = { 0 }; if (dwEventOptions & ETW::EnumerationLog::EnumerationStructs::DomainAssemblyModuleLoad) { - FireEtwModuleLoad_V2( + FireEtwModuleLoad_V3( ULONGLONG(pModule), 0, // AssemblyID ETW::LoaderLog::LoaderStructs::NativeModule, // Module Flags @@ -398,12 +417,13 @@ void ETW::LoaderLog::SendModuleEvent(HANDLE pModule, uint32_t dwEventOptions) NULL, // ManagedPdbBuildPath, &nativeGuid, // NativePdbSignature, dwAge, // NativePdbAge, - wszPath // NativePdbBuildPath, + wszPath, // NativePdbBuildPath, + wszBuildId // NativeBuildId, ); } else if (dwEventOptions & ETW::EnumerationLog::EnumerationStructs::DomainAssemblyModuleDCEnd) { - FireEtwModuleDCEnd_V2( + FireEtwModuleDCEnd_V3( ULONGLONG(pModule), 0, // AssemblyID ETW::LoaderLog::LoaderStructs::NativeModule, // Module Flags @@ -416,7 +436,8 @@ void ETW::LoaderLog::SendModuleEvent(HANDLE pModule, uint32_t dwEventOptions) NULL, // ManagedPdbBuildPath, &nativeGuid, // NativePdbSignature, dwAge, // NativePdbAge, - wszPath // NativePdbBuildPath, + wszPath, // NativePdbBuildPath, + wszBuildId // NativeBuildId, ); } else diff --git a/src/coreclr/nativeaot/Runtime/unix/PalUnix.cpp b/src/coreclr/nativeaot/Runtime/unix/PalUnix.cpp index 00334ae945be0f..b6045f6f74d5ac 100644 --- a/src/coreclr/nativeaot/Runtime/unix/PalUnix.cpp +++ b/src/coreclr/nativeaot/Runtime/unix/PalUnix.cpp @@ -44,6 +44,8 @@ #ifdef TARGET_LINUX #include +#include +#include #endif #if HAVE_PTHREAD_GETTHREADID_NP @@ -95,13 +97,96 @@ void RhFailFast() abort(); } -void PalGetPDBInfo(HANDLE hOsHandle, GUID * pGuidSignature, _Out_ uint32_t * pdwAge, _Out_writes_z_(cchPath) WCHAR * wszPath, int32_t cchPath) +#if TARGET_LINUX + +struct PalGetPDBInfoPhdrCallbackData +{ + void* Base; + void* BuildID; + uint32_t BuildIDLength; +}; + +static int PalGetPDBInfoPhdrCallback(struct dl_phdr_info *info, size_t size, void* pData) +{ + struct PalGetPDBInfoPhdrCallbackData* pCallbackData = (struct PalGetPDBInfoPhdrCallbackData*)pData; + + // Find the module of interest + void* loadAddress = NULL; + for (ElfW(Half) i = 0; i < info->dlpi_phnum; i++) + { + if (info->dlpi_phdr[i].p_type == PT_LOAD) + { + loadAddress = (void*)(info->dlpi_addr + info->dlpi_phdr[i].p_vaddr); + if (loadAddress == pCallbackData->Base) + break; + } + } + + if (loadAddress != pCallbackData->Base) + { + return 0; + } + + // Got the module of interest. Now iterate program headers and try to find the GNU build ID note + for (ElfW(Half) i = 0; i < info->dlpi_phnum; i++) + { + // Must be a note section. We don't check the name because while there's a convention for the name, + // the convention is not mandatory. + if (info->dlpi_phdr[i].p_type != PT_NOTE) + continue; + + // Got a note section, iterate over the contents and find the GNU build id one + ElfW(Nhdr) *note = (ElfW(Nhdr)*)(info->dlpi_addr + info->dlpi_phdr[i].p_vaddr); + ElfW(Addr) align = info->dlpi_phdr[i].p_align; + ElfW(Addr) size = info->dlpi_phdr[i].p_memsz; + ElfW(Addr) start = (ElfW(Addr))note; + + while ((ElfW(Addr)) (note + 1) - start < size) + { + if (note->n_namesz == 4 + && note->n_type == NT_GNU_BUILD_ID + && memcmp(note + 1, "GNU", 4) == 0) + { + // Got the note, fill out the callback data and return. + pCallbackData->BuildID = (uint8_t*)note + sizeof(ElfW(Nhdr)) + ALIGN_UP(note->n_namesz, align); + pCallbackData->BuildIDLength = note->n_descsz; + return 1; + } + + // Skip over the note. Size of the note is determined by the header and payload (aligned) + size_t offset = sizeof(ElfW(Nhdr)) + + ALIGN_UP(note->n_namesz, align) + + ALIGN_UP(note->n_descsz, align); + note = (ElfW(Nhdr)*)((uint8_t*)note + offset); + } + } + + return 0; +} +#endif + +void PalGetPDBInfo(HANDLE hOsHandle, GUID * pGuidSignature, _Out_ uint32_t * pdwAge, _Out_writes_z_(cchPath) WCHAR * wszPath, int32_t cchPath, _Out_ uint32_t * pcbBuildId, _Out_ void ** ppBuildId) { memset(pGuidSignature, 0, sizeof(*pGuidSignature)); *pdwAge = 0; + *ppBuildId = NULL; + *pcbBuildId = 0; if (cchPath <= 0) return; wszPath[0] = L'\0'; + +#if TARGET_LINUX + struct PalGetPDBInfoPhdrCallbackData data; + data.Base = hOsHandle; + + if (!dl_iterate_phdr(&PalGetPDBInfoPhdrCallback, &data)) + { + return; + } + + *pcbBuildId = data.BuildIDLength; + *ppBuildId = data.BuildID; +#endif } static void UnmaskActivationSignal() diff --git a/src/coreclr/nativeaot/Runtime/windows/PalCommon.cpp b/src/coreclr/nativeaot/Runtime/windows/PalCommon.cpp index 5983da31147ee7..8e4e582c5cec00 100644 --- a/src/coreclr/nativeaot/Runtime/windows/PalCommon.cpp +++ b/src/coreclr/nativeaot/Runtime/windows/PalCommon.cpp @@ -50,10 +50,12 @@ void PalGetModuleBounds(HANDLE hOsHandle, _Out_ uint8_t ** ppLowerBound, _Out_ u // // This is a simplification of similar code in CLR's GetCodeViewInfo // in eventtrace.cpp. -void PalGetPDBInfo(HANDLE hOsHandle, GUID * pGuidSignature, _Out_ uint32_t * pdwAge, _Out_writes_z_(cchPath) WCHAR * wszPath, int32_t cchPath) +void PalGetPDBInfo(HANDLE hOsHandle, GUID * pGuidSignature, _Out_ uint32_t * pdwAge, _Out_writes_z_(cchPath) WCHAR * wszPath, int32_t cchPath, _Out_ uint32_t * pcbBuildId, _Out_ void ** ppBuildId) { // Zero-init [out]-params ZeroMemory(pGuidSignature, sizeof(*pGuidSignature)); + *ppBuildId = NULL; + *pcbBuildId = 0; *pdwAge = 0; if (cchPath <= 0) return; diff --git a/src/coreclr/vm/ClrEtwAll.man b/src/coreclr/vm/ClrEtwAll.man index b2010b111e1c78..c04c58cfdac656 100644 --- a/src/coreclr/vm/ClrEtwAll.man +++ b/src/coreclr/vm/ClrEtwAll.man @@ -1932,6 +1932,40 @@ + + + +