Fix createdump failures on alpine in diagnostics repo tests (#32579)
authorMike McLaughlin <mikem@microsoft.com>
Fri, 21 Feb 2020 00:28:14 +0000 (16:28 -0800)
committerGitHub <noreply@github.com>
Fri, 21 Feb 2020 00:28:14 +0000 (16:28 -0800)
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
src/coreclr/src/debug/dbgutil/CMakeLists.txt
src/coreclr/src/debug/dbgutil/elfreader.cpp
src/coreclr/src/debug/dbgutil/elfreader.h

index cfc6b5503a7e9dc5e19cf8b7226443b8b6fa7fb7..a04a6e824586596f6d316e6e05680e1c228f35b9 100644 (file)
@@ -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);
 }
 
 //
index 2276831cbcbd519cb3decc2b9e9f103986578115..0ff9188e0c3511f7a763bb8f10b314a4e406bd89 100644 (file)
@@ -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
 )
index d345495f2e3572b1aa2a3be8f29acb00d7924e7e..86ac8e1355b3f752de9912bd9a34f833d3476450 100644 (file)
@@ -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 <windows.h>
+#include <clrdata.h>
+#include <cor.h>
+#include <cordebug.h>
+#define __STDC_FORMAT_MACROS
+#include <inttypes.h>
 #include "elfreader.h"
-#include <arrayholder.h>
 
 #define Elf_Ehdr   ElfW(Ehdr)
 #define Elf_Phdr   ElfW(Phdr)
 #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++;
     }
@@ -318,14 +324,61 @@ 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<struct r_debug*>(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<struct r_debug*>(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<Elf_Phdr*>(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;
index 77129bbcf1a38daad6ad6ae3cf22568723a7fcbc..642ae2bea8712b0debed42989b8c7a8b33fbe4f3 100644 (file)
@@ -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 <windows.h>
-#include <clrdata.h>
-#include <cor.h>
-#include <cordebug.h>
-#define __STDC_FORMAT_MACROS
-#include <inttypes.h>
 #include <elf.h>
 #ifdef HOST_UNIX
 #include <link.h>
@@ -15,22 +9,6 @@
 #include <string>
 #include <vector>
 
-#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;