From ac941749537fc60c6ceb193462dfe145111a1ef7 Mon Sep 17 00:00:00 2001 From: Mike McLaughlin Date: Thu, 20 Feb 2020 16:28:14 -0800 Subject: [PATCH] Fix createdump failures on alpine in diagnostics repo tests (#32579) Add "loadbias" to dynamic entries RVAs on Alpine Linux. Move the Windows includes files and other defines from elfreader.h to elfreader.cpp. Make the two ways the ElfReader is used more clear by renaming and cleanup some function names: 1) PopulateForSymbolLookup (was PopulateElfInfo) for looking up symbols and caches symbol/string table state in the ElfReader class. 2) EnumerateElfInfo (was PopulateElfInfo also) which is used by createdump to enumerate all the native modules and their program headers and doesn't depend on or caches any state. --- src/coreclr/src/debug/createdump/crashinfo.cpp | 13 +-- src/coreclr/src/debug/dbgutil/CMakeLists.txt | 4 + src/coreclr/src/debug/dbgutil/elfreader.cpp | 155 +++++++++++++++++-------- src/coreclr/src/debug/dbgutil/elfreader.h | 38 ++---- 4 files changed, 123 insertions(+), 87 deletions(-) diff --git a/src/coreclr/src/debug/createdump/crashinfo.cpp b/src/coreclr/src/debug/createdump/crashinfo.cpp index cfc6b55..a04a6e8 100644 --- a/src/coreclr/src/debug/createdump/crashinfo.cpp +++ b/src/coreclr/src/debug/createdump/crashinfo.cpp @@ -415,14 +415,7 @@ CrashInfo::GetDSOInfo() int phnum = m_auxvValues[AT_PHNUM]; assert(m_auxvValues[AT_PHENT] == sizeof(Phdr)); assert(phnum != PN_XNUM); - - if (!PopulateELFInfo(phdrAddr, phnum)) { - return false; - } - if (!EnumerateLinkMapEntries()) { - return false; - } - return true; + return EnumerateElfInfo(phdrAddr, phnum); } // @@ -443,13 +436,13 @@ CrashInfo::VisitModule(uint64_t baseAddress, std::string& moduleName) // Now populate the elfreader with the runtime module info and // lookup the DAC table symbol to ensure that all the memory // necessary is in the core dump. - if (PopulateELFInfo(baseAddress)) { + if (PopulateForSymbolLookup(baseAddress)) { uint64_t symbolOffset; TryLookupSymbol("g_dacTable", &symbolOffset); } } } - EnumerateProgramHeaders(baseAddress, nullptr); + EnumerateProgramHeaders(baseAddress); } // diff --git a/src/coreclr/src/debug/dbgutil/CMakeLists.txt b/src/coreclr/src/debug/dbgutil/CMakeLists.txt index 2276831..0ff9188 100644 --- a/src/coreclr/src/debug/dbgutil/CMakeLists.txt +++ b/src/coreclr/src/debug/dbgutil/CMakeLists.txt @@ -9,6 +9,10 @@ include_directories(${CLR_DIR}/src/inc/llvm) add_definitions(-DPAL_STDCPP_COMPAT) +if(CLR_CMAKE_TARGET_ALPINE_LINUX) + add_definitions(-DTARGET_ALPINE_LINUX) +endif(CLR_CMAKE_TARGET_ALPINE_LINUX) + set(DBGUTIL_SOURCES dbgutil.cpp ) diff --git a/src/coreclr/src/debug/dbgutil/elfreader.cpp b/src/coreclr/src/debug/dbgutil/elfreader.cpp index d345495..86ac8e1 100644 --- a/src/coreclr/src/debug/dbgutil/elfreader.cpp +++ b/src/coreclr/src/debug/dbgutil/elfreader.cpp @@ -2,8 +2,13 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +#include +#include +#include +#include +#define __STDC_FORMAT_MACROS +#include #include "elfreader.h" -#include #define Elf_Ehdr ElfW(Ehdr) #define Elf_Phdr ElfW(Phdr) @@ -12,6 +17,22 @@ #define Elf_Dyn ElfW(Dyn) #define Elf_Sym ElfW(Sym) +#if TARGET_64BIT +#define PRIx PRIx64 +#define PRIu PRIu64 +#define PRId PRId64 +#define PRIA "016" +#define PRIxA PRIA PRIx +#define TARGET_WORDSIZE 64 +#else +#define PRIx PRIx32 +#define PRIu PRIu32 +#define PRId PRId32 +#define PRIA "08" +#define PRIxA PRIA PRIx +#define TARGET_WORDSIZE 32 +#endif + #ifdef HOST_UNIX #define TRACE(args...) Trace(args) #else @@ -54,7 +75,7 @@ bool TryGetSymbol(ICorDebugDataTarget* dataTarget, uint64_t baseAddress, const char* symbolName, uint64_t* symbolAddress) { ElfReaderExport elfreader(dataTarget); - if (elfreader.PopulateELFInfo(baseAddress)) + if (elfreader.PopulateForSymbolLookup(baseAddress)) { uint64_t symbolOffset; if (elfreader.TryLookupSymbol(symbolName, &symbolOffset)) @@ -72,7 +93,6 @@ TryGetSymbol(ICorDebugDataTarget* dataTarget, uint64_t baseAddress, const char* // ElfReader::ElfReader() : - m_rdebugAddr(nullptr), m_gnuHashTableAddr(nullptr), m_stringTableAddr(nullptr), m_stringTableSize(0), @@ -91,51 +111,40 @@ ElfReader::~ElfReader() } // -// Initialize the ELF reader from a module the base address +// Initialize the ELF reader from a module the base address. This function +// caches the info neccessary in the ElfReader class look up symbols. // bool -ElfReader::PopulateELFInfo(uint64_t baseAddress) +ElfReader::PopulateForSymbolLookup(uint64_t baseAddress) { - TRACE("PopulateELFInfo: base %" PRIA PRIx64 "\n", baseAddress); + TRACE("PopulateForSymbolLookup: base %" PRIA PRIx64 "\n", baseAddress); Elf_Dyn* dynamicAddr = nullptr; + uint64_t loadbias = 0; // Enumerate program headers searching for the PT_DYNAMIC header, etc. - if (!EnumerateProgramHeaders(baseAddress, &dynamicAddr)) { - return false; - } - return EnumerateDynamicEntries(dynamicAddr); -} - -// -// Initialize the ELF reader from the root program header -// -bool -ElfReader::PopulateELFInfo(Elf_Phdr* phdrAddr, int phnum) -{ - TRACE("PopulateELFInfo: phdr %p phnum %d\n", phdrAddr, phnum); - - if (phdrAddr == nullptr || phnum <= 0) { - return false; - } - uint64_t baseAddress = (uint64_t)phdrAddr - sizeof(Elf_Ehdr); - Elf_Dyn* dynamicAddr = nullptr; - - // Enumerate program headers searching for the PT_DYNAMIC header, etc. - if (!EnumerateProgramHeaders(phdrAddr, phnum, baseAddress, &dynamicAddr)) { + if (!EnumerateProgramHeaders( + baseAddress, +#ifdef TARGET_ALPINE_LINUX + // On Alpine, the below dynamic entries for hash, string table, etc. are + // RVAs instead of absolute address like on all other Linux distros. Get + // the "loadbias" (basically the base address of the module) and add to + // these RVAs. + &loadbias, +#else + nullptr, +#endif + &dynamicAddr)) + { return false; } - return EnumerateDynamicEntries(dynamicAddr); -} -bool -ElfReader::EnumerateDynamicEntries(Elf_Dyn* dynamicAddr) -{ if (dynamicAddr == nullptr) { return false; } // Search for dynamic entries - for (;;) { + for (;;) + { Elf_Dyn dyn; if (!ReadMemory(dynamicAddr, &dyn, sizeof(dyn))) { TRACE("ERROR: ReadMemory(%p, %" PRIx ") dyn FAILED\n", dynamicAddr, sizeof(dyn)); @@ -145,20 +154,17 @@ ElfReader::EnumerateDynamicEntries(Elf_Dyn* dynamicAddr) if (dyn.d_tag == DT_NULL) { break; } - else if (dyn.d_tag == DT_DEBUG) { - m_rdebugAddr = (void*)dyn.d_un.d_ptr; - } else if (dyn.d_tag == DT_GNU_HASH) { - m_gnuHashTableAddr = (void*)dyn.d_un.d_ptr; + m_gnuHashTableAddr = (void*)(dyn.d_un.d_ptr + loadbias); } else if (dyn.d_tag == DT_STRTAB) { - m_stringTableAddr = (void*)dyn.d_un.d_ptr; + m_stringTableAddr = (void*)(dyn.d_un.d_ptr + loadbias); } else if (dyn.d_tag == DT_STRSZ) { m_stringTableSize = (int)dyn.d_un.d_ptr; } else if (dyn.d_tag == DT_SYMTAB) { - m_symbolTableAddr = (void*)dyn.d_un.d_ptr; + m_symbolTableAddr = (void*)(dyn.d_un.d_ptr + loadbias); } dynamicAddr++; } @@ -319,13 +325,60 @@ ElfReader::GetStringAtIndex(int index, std::string& result) #ifdef HOST_UNIX // +// Enumerate all the ELF info starting from the root program header. This +// function doesn't cache any state in the ElfReader class. +// +bool +ElfReader::EnumerateElfInfo(Elf_Phdr* phdrAddr, int phnum) +{ + TRACE("EnumerateElfInfo: phdr %p phnum %d\n", phdrAddr, phnum); + + if (phdrAddr == nullptr || phnum <= 0) { + return false; + } + uint64_t baseAddress = (uint64_t)phdrAddr - sizeof(Elf_Ehdr); + Elf_Dyn* dynamicAddr = nullptr; + + // Enumerate program headers searching for the PT_DYNAMIC header, etc. + if (!EnumerateProgramHeaders(phdrAddr, phnum, baseAddress, nullptr, &dynamicAddr)) { + return false; + } + return EnumerateLinkMapEntries(dynamicAddr); +} + +// // Enumerate through the dynamic debug link map entries // bool -ElfReader::EnumerateLinkMapEntries() +ElfReader::EnumerateLinkMapEntries(Elf_Dyn* dynamicAddr) { - struct r_debug* rdebugAddr = reinterpret_cast(m_rdebugAddr); + if (dynamicAddr == nullptr) { + return false; + } + + // Search dynamic entries for DT_DEBUG (r_debug entry) + struct r_debug* rdebugAddr = nullptr; + for (;;) + { + Elf_Dyn dyn; + if (!ReadMemory(dynamicAddr, &dyn, sizeof(dyn))) { + TRACE("ERROR: ReadMemory(%p, %" PRIx ") dyn FAILED\n", dynamicAddr, sizeof(dyn)); + return false; + } + TRACE("DSO: dyn %p tag %" PRId " (%" PRIx ") d_ptr %" PRIxA "\n", dynamicAddr, dyn.d_tag, dyn.d_tag, dyn.d_un.d_ptr); + if (dyn.d_tag == DT_NULL) { + break; + } + else if (dyn.d_tag == DT_DEBUG) { + rdebugAddr = reinterpret_cast(dyn.d_un.d_ptr); + } + dynamicAddr++; + } + TRACE("DSO: rdebugAddr %p\n", rdebugAddr); + if (rdebugAddr == nullptr) { + return false; + } struct r_debug debugEntry; if (!ReadMemory(rdebugAddr, &debugEntry, sizeof(debugEntry))) { @@ -334,7 +387,8 @@ ElfReader::EnumerateLinkMapEntries() } // Add the DSO link_map entries - for (struct link_map* linkMapAddr = debugEntry.r_map; linkMapAddr != nullptr;) { + for (struct link_map* linkMapAddr = debugEntry.r_map; linkMapAddr != nullptr;) + { struct link_map map; if (!ReadMemory(linkMapAddr, &map, sizeof(map))) { TRACE("ERROR: ReadMemory(%p, %" PRIx ") link_map FAILED\n", linkMapAddr, sizeof(map)); @@ -343,7 +397,8 @@ ElfReader::EnumerateLinkMapEntries() // Read the module's name and make sure the memory is added to the core dump std::string moduleName; int i = 0; - if (map.l_name != nullptr) { + if (map.l_name != nullptr) + { for (; i < PATH_MAX; i++) { char ch; @@ -371,7 +426,7 @@ ElfReader::EnumerateLinkMapEntries() #endif // HOST_UNIX bool -ElfReader::EnumerateProgramHeaders(uint64_t baseAddress, Elf_Dyn** pdynamicAddr) +ElfReader::EnumerateProgramHeaders(uint64_t baseAddress, uint64_t* ploadbias, Elf_Dyn** pdynamicAddr) { Elf_Ehdr ehdr; if (!ReadMemory((void*)baseAddress, &ehdr, sizeof(ehdr))) { @@ -400,17 +455,18 @@ ElfReader::EnumerateProgramHeaders(uint64_t baseAddress, Elf_Dyn** pdynamicAddr) _ASSERTE(ehdr.e_ident[EI_DATA] == ELFDATA2LSB); Elf_Phdr* phdrAddr = reinterpret_cast(baseAddress + ehdr.e_phoff); - return EnumerateProgramHeaders(phdrAddr, phnum, baseAddress, pdynamicAddr); + return EnumerateProgramHeaders(phdrAddr, phnum, baseAddress, ploadbias, pdynamicAddr); } // // Enumerate and find the dynamic program header entry // bool -ElfReader::EnumerateProgramHeaders(Elf_Phdr* phdrAddr, int phnum, uint64_t baseAddress, Elf_Dyn** pdynamicAddr) +ElfReader::EnumerateProgramHeaders(Elf_Phdr* phdrAddr, int phnum, uint64_t baseAddress, uint64_t* ploadbias, Elf_Dyn** pdynamicAddr) { uint64_t loadbias = baseAddress; + // Calculate the load bias from the PT_LOAD program headers for (int i = 0; i < phnum; i++) { Elf_Phdr ph; @@ -425,6 +481,11 @@ ElfReader::EnumerateProgramHeaders(Elf_Phdr* phdrAddr, int phnum, uint64_t baseA } } + if (ploadbias != nullptr) { + *ploadbias = loadbias; + } + + // Enumerate all the program headers for (int i = 0; i < phnum; i++) { Elf_Phdr ph; diff --git a/src/coreclr/src/debug/dbgutil/elfreader.h b/src/coreclr/src/debug/dbgutil/elfreader.h index 77129bb..642ae2b 100644 --- a/src/coreclr/src/debug/dbgutil/elfreader.h +++ b/src/coreclr/src/debug/dbgutil/elfreader.h @@ -2,12 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -#include -#include -#include -#include -#define __STDC_FORMAT_MACROS -#include #include #ifdef HOST_UNIX #include @@ -15,22 +9,6 @@ #include #include -#if TARGET_64BIT -#define PRIx PRIx64 -#define PRIu PRIu64 -#define PRId PRId64 -#define PRIA "016" -#define PRIxA PRIA PRIx -#define TARGET_WORDSIZE 64 -#else -#define PRIx PRIx32 -#define PRIu PRIu32 -#define PRId PRId32 -#define PRIA "08" -#define PRIxA PRIA PRIx -#define TARGET_WORDSIZE 32 -#endif - #ifndef ElfW /* We use this macro to refer to ELF types independent of the native wordsize. `ElfW(TYPE)' is used in place of `Elf32_TYPE' or `Elf64_TYPE'. */ @@ -49,7 +27,6 @@ typedef struct { class ElfReader { private: - void* m_rdebugAddr; // DT_DEBUG void* m_gnuHashTableAddr; // DT_GNU_HASH void* m_stringTableAddr; // DT_STRTAB int m_stringTableSize; // DT_STRSIZ @@ -62,14 +39,12 @@ private: public: ElfReader(); virtual ~ElfReader(); - bool PopulateELFInfo(uint64_t baseAddress); - bool PopulateELFInfo(ElfW(Phdr)* phdrAddr, int phnum); - bool TryLookupSymbol(std::string symbolName, uint64_t* symbolOffset); #ifdef HOST_UNIX - bool EnumerateLinkMapEntries(); + bool EnumerateElfInfo(ElfW(Phdr)* phdrAddr, int phnum); #endif - bool EnumerateProgramHeaders(uint64_t baseAddress, ElfW(Dyn)** pdynamicAddr); - bool EnumerateProgramHeaders(ElfW(Phdr)* phdrAddr, int phnum, uint64_t baseAddress, ElfW(Dyn)** pdynamicAddr); + bool PopulateForSymbolLookup(uint64_t baseAddress); + bool TryLookupSymbol(std::string symbolName, uint64_t* symbolOffset); + bool EnumerateProgramHeaders(uint64_t baseAddress, uint64_t* ploadbias = nullptr, ElfW(Dyn)** pdynamicAddr = nullptr); private: bool GetSymbol(int32_t index, ElfW(Sym)* symbol); @@ -78,7 +53,10 @@ private: uint32_t Hash(const std::string& symbolName); bool GetChain(int index, int32_t* chain); bool GetStringAtIndex(int index, std::string& result); - bool EnumerateDynamicEntries(ElfW(Dyn)* dynamicAddr); +#ifdef HOST_UNIX + bool EnumerateLinkMapEntries(ElfW(Dyn)* dynamicAddr); +#endif + bool EnumerateProgramHeaders(ElfW(Phdr)* phdrAddr, int phnum, uint64_t baseAddress, uint64_t* ploadbias, ElfW(Dyn)** pdynamicAddr); virtual void VisitModule(uint64_t baseAddress, std::string& moduleName) { }; virtual void VisitProgramHeader(uint64_t loadbias, uint64_t baseAddress, ElfW(Phdr)* phdr) { }; virtual bool ReadMemory(void* address, void* buffer, size_t size) = 0; -- 2.7.4