Remove DAC dependence on compile time DAC_TABLE_RVA definition on Linux (#32331)
authorMike McLaughlin <mikem@microsoft.com>
Sun, 16 Feb 2020 01:20:07 +0000 (17:20 -0800)
committerGitHub <noreply@github.com>
Sun, 16 Feb 2020 01:20:07 +0000 (17:20 -0800)
Remove DAC dependence on compile time DAC_TABLE_RVA definition on Linux

Issue #https://github.com/dotnet/runtime/issues/1976

Added a fairly generic elf reader class.

Change createdump to use the elfreader for all the ELF enumeration
and find the g_dacTable to ensure all the symbol and string tables
are in the coredump.

15 files changed:
src/coreclr/src/CMakeLists.txt
src/coreclr/src/debug/CMakeLists.txt
src/coreclr/src/debug/createdump/CMakeLists.txt
src/coreclr/src/debug/createdump/crashinfo.cpp
src/coreclr/src/debug/createdump/crashinfo.h
src/coreclr/src/debug/daccess/CMakeLists.txt
src/coreclr/src/debug/daccess/daccess.cpp
src/coreclr/src/debug/daccess/enummem.cpp
src/coreclr/src/debug/dbgutil/CMakeLists.txt
src/coreclr/src/debug/dbgutil/dbgutil.cpp
src/coreclr/src/debug/dbgutil/elfreader.cpp [new file with mode: 0644]
src/coreclr/src/debug/dbgutil/elfreader.h [new file with mode: 0644]
src/coreclr/src/debug/ee/dactable.cpp
src/coreclr/src/dlls/mscoree/mscorwks_unixexports.src
src/coreclr/src/inc/crosscomp.h

index d08608b..6a75325 100644 (file)
@@ -13,6 +13,7 @@ if(CLR_CMAKE_TARGET_WIN32 AND FEATURE_EVENT_TRACE)
     include_directories("${GENERATED_INCLUDE_DIR}/etw")
 endif(CLR_CMAKE_TARGET_WIN32 AND FEATURE_EVENT_TRACE)
 
+add_subdirectory(debug/dbgutil)
 
 if(CLR_CMAKE_HOST_UNIX)
   if(CLR_CMAKE_HOST_LINUX AND NOT CLR_CMAKE_HOST_UNIX_X86)
index bcfc257..fcba648 100644 (file)
@@ -1,6 +1,5 @@
 add_subdirectory(daccess)
-add_subdirectory(dbgutil)
 add_subdirectory(ildbsymlib)
 add_subdirectory(ee)
 add_subdirectory(di)
-add_subdirectory(shim)
\ No newline at end of file
+add_subdirectory(shim)
index 0bdcbac..e110566 100644 (file)
@@ -44,6 +44,7 @@ add_dependencies(createdump pal_redefines_file)
 target_link_libraries(createdump
     createdump_lib
     corguids
+    dbgutil
     # share the PAL in the dac module
     mscordaccore
 )
index ec2ad71..cfc6b55 100644 (file)
@@ -371,15 +371,6 @@ CrashInfo::EnumerateModuleMappings()
 
             if (moduleName != nullptr && *moduleName == '/')
             {
-                if (m_coreclrPath.empty())
-                {
-                    std::string coreclrPath;
-                    coreclrPath.append(moduleName);
-                    size_t last = coreclrPath.rfind(MAKEDLLNAME_A("coreclr"));
-                    if (last != std::string::npos) {
-                        m_coreclrPath = coreclrPath.substr(0, last);
-                    }
-                }
                 m_moduleMappings.insert(memoryRegion);
             }
             else
@@ -425,183 +416,64 @@ CrashInfo::GetDSOInfo()
     assert(m_auxvValues[AT_PHENT] == sizeof(Phdr));
     assert(phnum != PN_XNUM);
 
-    if (phnum <= 0 || phdrAddr == nullptr) {
+    if (!PopulateELFInfo(phdrAddr, phnum)) {
         return false;
     }
-    uint64_t baseAddress = (uint64_t)phdrAddr - sizeof(Ehdr);
-    ElfW(Dyn)* dynamicAddr = nullptr;
-
-    TRACE("DSO: base %" PRIA PRIx64 " phdr %p phnum %d\n", baseAddress, phdrAddr, phnum);
-
-    // Enumerate program headers searching for the PT_DYNAMIC header, etc.
-    if (!EnumerateProgramHeaders(phdrAddr, phnum, baseAddress, &dynamicAddr))
-    {
-        return false;
-    }
-    if (dynamicAddr == nullptr) {
-        return false;
-    }
-
-    // Search for dynamic debug (DT_DEBUG) entry
-    struct r_debug* rdebugAddr = nullptr;
-    for (;;) {
-        ElfW(Dyn) dyn;
-        if (!ReadMemory(dynamicAddr, &dyn, sizeof(dyn))) {
-            fprintf(stderr, "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_DEBUG) {
-            rdebugAddr = reinterpret_cast<struct r_debug*>(dyn.d_un.d_ptr);
-        }
-        else if (dyn.d_tag == DT_NULL) {
-            break;
-        }
-        dynamicAddr++;
-    }
-
-    // Add the DSO r_debug entry
-    TRACE("DSO: rdebugAddr %p\n", rdebugAddr);
-    struct r_debug debugEntry;
-    if (!ReadMemory(rdebugAddr, &debugEntry, sizeof(debugEntry))) {
-        fprintf(stderr, "ReadMemory(%p, %" PRIx ") r_debug FAILED\n", rdebugAddr, sizeof(debugEntry));
+    if (!EnumerateLinkMapEntries()) {
         return false;
     }
-
-    // Add the DSO link_map entries
-    ArrayHolder<char> moduleName = new char[PATH_MAX];
-    for (struct link_map* linkMapAddr = debugEntry.r_map; linkMapAddr != nullptr;) {
-        struct link_map map;
-        if (!ReadMemory(linkMapAddr, &map, sizeof(map))) {
-            fprintf(stderr, "ReadMemory(%p, %" PRIx ") link_map FAILED\n", linkMapAddr, sizeof(map));
-            return false;
-        }
-        // Read the module's name and make sure the memory is added to the core dump
-        int i = 0;
-        if (map.l_name != nullptr) {
-            for (; i < PATH_MAX; i++)
-            {
-                if (!ReadMemory(map.l_name + i, &moduleName[i], 1)) {
-                    TRACE("DSO: ReadMemory link_map name %p + %d FAILED\n", map.l_name, i);
-                    break;
-                }
-                if (moduleName[i] == '\0') {
-                    break;
-                }
-            }
-        }
-        moduleName[i] = '\0';
-        TRACE("\nDSO: link_map entry %p l_ld %p l_addr (Ehdr) %" PRIx " %s\n", linkMapAddr, map.l_ld, map.l_addr, (char*)moduleName);
-
-        // Read the ELF header and info adding it to the core dump
-        if (!GetELFInfo(map.l_addr)) {
-            return false;
-        }
-        linkMapAddr = map.l_next;
-    }
-
     return true;
 }
 
 //
 // Add all the necessary ELF headers to the core dump
 //
-bool
-CrashInfo::GetELFInfo(uint64_t baseAddress)
+void
+CrashInfo::VisitModule(uint64_t baseAddress, std::string& moduleName)
 {
     if (baseAddress == 0 || baseAddress == m_auxvValues[AT_SYSINFO_EHDR] || baseAddress == m_auxvValues[AT_BASE]) {
-        return true;
-    }
-    Ehdr ehdr;
-    if (!ReadMemory((void*)baseAddress, &ehdr, sizeof(ehdr))) {
-        TRACE("ReadMemory(%p, %" PRIx ") ehdr FAILED\n", (void*)baseAddress, sizeof(ehdr));
-        return true;
+        return;
     }
-    int phnum = ehdr.e_phnum;
-    assert(phnum != PN_XNUM);
-    assert(ehdr.e_phentsize == sizeof(Phdr));
-#ifdef HOST_64BIT
-    assert(ehdr.e_ident[EI_CLASS] == ELFCLASS64);
-#else
-    assert(ehdr.e_ident[EI_CLASS] == ELFCLASS32);
-#endif
-    assert(ehdr.e_ident[EI_DATA] == ELFDATA2LSB);
-
-    TRACE("ELF: type %d mach 0x%x ver %d flags 0x%x phnum %d phoff %" PRIxA " phentsize 0x%02x shnum %d shoff %" PRIxA " shentsize 0x%02x shstrndx %d\n",
-        ehdr.e_type, ehdr.e_machine, ehdr.e_version, ehdr.e_flags, phnum, ehdr.e_phoff, ehdr.e_phentsize, ehdr.e_shnum, ehdr.e_shoff, ehdr.e_shentsize, ehdr.e_shstrndx);
-
-    if (ehdr.e_phoff != 0 && phnum > 0)
+    if (m_coreclrPath.empty())
     {
-        Phdr* phdrAddr = reinterpret_cast<Phdr*>(baseAddress + ehdr.e_phoff);
+        size_t last = moduleName.rfind(MAKEDLLNAME_A("coreclr"));
+        if (last != std::string::npos) {
+            m_coreclrPath = moduleName.substr(0, last);
 
-        if (!EnumerateProgramHeaders(phdrAddr, phnum, baseAddress, nullptr))
-        {
-            return false;
+            // 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)) {
+                uint64_t symbolOffset;
+                TryLookupSymbol("g_dacTable", &symbolOffset);
+            }
         }
     }
-
-    return true;
+    EnumerateProgramHeaders(baseAddress, nullptr);
 }
 
 //
-// Enumerate the program headers adding the build id note, unwind frame
+// Called for each program header adding the build id note, unwind frame
 // region and module addresses to the crash info.
 //
-bool
-CrashInfo::EnumerateProgramHeaders(Phdr* phdrAddr, int phnum, uint64_t baseAddress, ElfW(Dyn)** pdynamicAddr)
+void
+CrashInfo::VisitProgramHeader(uint64_t loadbias, uint64_t baseAddress, Phdr* phdr)
 {
-    uint64_t loadbias = baseAddress;
-
-    for (int i = 0; i < phnum; i++)
-    {
-        Phdr ph;
-        if (!ReadMemory(phdrAddr + i, &ph, sizeof(ph))) {
-            fprintf(stderr, "ReadMemory(%p, %" PRIx ") phdr FAILED\n", phdrAddr + i, sizeof(ph));
-            return false;
-        }
-        if (ph.p_type == PT_LOAD && ph.p_offset == 0)
-        {
-            loadbias -= ph.p_vaddr;
-            TRACE("PHDR: loadbias %" PRIA PRIx64 "\n", loadbias);
-            break;
-        }
-    }
-
-    for (int i = 0; i < phnum; i++)
+    switch (phdr->p_type)
     {
-        Phdr ph;
-        if (!ReadMemory(phdrAddr + i, &ph, sizeof(ph))) {
-            fprintf(stderr, "ReadMemory(%p, %" PRIx ") phdr FAILED\n", phdrAddr + i, sizeof(ph));
-            return false;
+    case PT_DYNAMIC:
+    case PT_NOTE:
+    case PT_GNU_EH_FRAME:
+        if (phdr->p_vaddr != 0 && phdr->p_memsz != 0) {
+            InsertMemoryRegion(loadbias + phdr->p_vaddr, phdr->p_memsz);
         }
-        TRACE("PHDR: %p type %d (%x) vaddr %" PRIxA " memsz %" PRIxA " paddr %" PRIxA " filesz %" PRIxA " offset %" PRIxA " align %" PRIxA "\n",
-            phdrAddr + i, ph.p_type, ph.p_type, ph.p_vaddr, ph.p_memsz, ph.p_paddr, ph.p_filesz, ph.p_offset, ph.p_align);
-
-        switch (ph.p_type)
-        {
-        case PT_DYNAMIC:
-            if (pdynamicAddr != nullptr)
-            {
-                *pdynamicAddr = reinterpret_cast<ElfW(Dyn)*>(loadbias + ph.p_vaddr);
-                break;
-            }
-            // fall into InsertMemoryRegion
+        break;
 
-        case PT_NOTE:
-        case PT_GNU_EH_FRAME:
-            if (ph.p_vaddr != 0 && ph.p_memsz != 0) {
-                InsertMemoryRegion(loadbias + ph.p_vaddr, ph.p_memsz);
-            }
-            break;
-
-        case PT_LOAD:
-            MemoryRegion region(0, loadbias + ph.p_vaddr, loadbias + ph.p_vaddr + ph.p_memsz, baseAddress);
-            m_moduleAddresses.insert(region);
-            break;
-        }
+    case PT_LOAD:
+        MemoryRegion region(0, loadbias + phdr->p_vaddr, loadbias + phdr->p_vaddr + phdr->p_memsz, baseAddress);
+        m_moduleAddresses.insert(region);
+        break;
     }
-
-    return true;
 }
 
 //
@@ -1092,3 +964,14 @@ CrashInfo::GetStatus(pid_t pid, pid_t* ppid, pid_t* tgid, char** name)
     fclose(statusFile);
     return true;
 }
+
+void
+CrashInfo::Trace(const char* format, ...)
+{
+    if (g_diagnostics) {
+        va_list ap;
+        va_start(ap, format);
+        vprintf(format, ap);
+        va_end(ap);
+    }
+}
index 9fbc211..341e3af 100644 (file)
@@ -2,6 +2,8 @@
 // The .NET Foundation licenses this file to you under the MIT license.
 // See the LICENSE file in the project root for more information.
 
+#include "../dbgutil/elfreader.h"
+
 // typedef for our parsing of the auxv variables in /proc/pid/auxv.
 #if defined(__i386) || defined(__ARM_EABI__)
 typedef Elf32_auxv_t elf_aux_entry;
@@ -24,7 +26,7 @@ typedef __typeof__(((elf_aux_entry*) 0)->a_un.a_val) elf_aux_val_t;
 // All interesting auvx entry types are AT_SYSINFO_EHDR and below
 #define AT_MAX (AT_SYSINFO_EHDR + 1)
 
-class CrashInfo : public ICLRDataEnumMemoryRegionsCallback
+class CrashInfo : public ElfReader, public ICLRDataEnumMemoryRegionsCallback
 {
 private:
     LONG m_ref;                                     // reference count
@@ -78,10 +80,10 @@ public:
 
 private:
     bool GetAuxvEntries();
-    bool EnumerateModuleMappings();
     bool GetDSOInfo();
-    bool GetELFInfo(uint64_t baseAddress);
-    bool EnumerateProgramHeaders(ElfW(Phdr)* phdrAddr, int phnum, uint64_t baseAddress, ElfW(Dyn)** pdynamicAddr);
+    void VisitModule(uint64_t baseAddress, std::string& moduleName);
+    void VisitProgramHeader(uint64_t loadbias, uint64_t baseAddress, ElfW(Phdr)* phdr);
+    bool EnumerateModuleMappings();
     bool EnumerateMemoryRegionsWithDAC(MINIDUMP_TYPE minidumpType);
     bool EnumerateManagedModules(IXCLRDataProcess* pClrDataProcess);
     bool UnwindAllThreads(IXCLRDataProcess* pClrDataProcess);
@@ -91,4 +93,5 @@ private:
     uint32_t GetMemoryRegionFlags(uint64_t start);
     bool ValidRegion(const MemoryRegion& region);
     void CombineMemoryRegions();
+    void Trace(const char* format, ...);
 };
index dcd6fa1..bbee976 100644 (file)
@@ -41,7 +41,9 @@ target_precompile_header(TARGET daccess HEADER stdafx.h)
 
 add_dependencies(daccess eventing_headers)
 
-if(CLR_CMAKE_HOST_UNIX)
+if(CLR_CMAKE_HOST_DARWIN OR CLR_CMAKE_HOST_FREEBSD OR CLR_CMAKE_HOST_NETBSD)
+  add_definitions(-DUSE_DAC_TABLE_RVA)
+
   add_custom_command(
     OUTPUT  ${GENERATED_INCLUDE_DIR}/dactablerva.h
     DEPENDS coreclr
@@ -61,4 +63,4 @@ if(CLR_CMAKE_HOST_UNIX)
   )
 
   add_dependencies(daccess dactablerva_header)
-endif(CLR_CMAKE_HOST_UNIX)
+endif(CLR_CMAKE_HOST_DARWIN OR CLR_CMAKE_HOST_FREEBSD OR CLR_CMAKE_HOST_NETBSD)
index 72a9c51..fea12eb 100644 (file)
 #include "dwreport.h"
 #include "primitives.h"
 #include "dbgutil.h"
+
 #ifdef TARGET_UNIX
+#ifdef USE_DAC_TABLE_RVA
 #include <dactablerva.h>
+#else
+extern bool TryGetSymbol(ICorDebugDataTarget* dataTarget, uint64_t baseAddress, const char* symbolName, uint64_t* symbolAddress);
+#endif
 #endif
 
 #include "dwbucketmanager.hpp"
@@ -7245,16 +7250,39 @@ bool ClrDataAccess::MdCacheGetEEName(TADDR taEEStruct, SString & eeName)
 #define _WIDE2(x) W(x)
 
 HRESULT
-ClrDataAccess::GetDacGlobals()
+GetDacTableAddress(ICorDebugDataTarget* dataTarget, ULONG64 baseAddress, PULONG64 dacTableAddress)
 {
 #ifdef TARGET_UNIX
+#ifdef USE_DAC_TABLE_RVA
 #ifdef DAC_TABLE_SIZE
     if (DAC_TABLE_SIZE != sizeof(g_dacGlobals))
     {
         return E_INVALIDARG;
     }
 #endif
-    ULONG64 dacTableAddress = m_globalBase + DAC_TABLE_RVA;
+    // On MacOS, FreeBSD or NetBSD use the RVA include file
+    *dacTableAddress = baseAddress + DAC_TABLE_RVA;
+#else
+    // On Linux try to get the dac table address via the export symbol
+    if (!TryGetSymbol(dataTarget, baseAddress, "g_dacTable", dacTableAddress))
+    {
+        return CORDBG_E_MISSING_DEBUGGER_EXPORTS;
+    }
+#endif
+#endif
+    return S_OK;
+}
+
+HRESULT
+ClrDataAccess::GetDacGlobals()
+{
+#ifdef TARGET_UNIX
+    ULONG64 dacTableAddress;
+    HRESULT hr = GetDacTableAddress(m_pTarget, m_globalBase, &dacTableAddress);
+    if (FAILED(hr))
+    {
+        return hr;
+    }
     if (FAILED(ReadFromDataTarget(m_pTarget, dacTableAddress, (BYTE*)&g_dacGlobals, sizeof(g_dacGlobals))))
     {
         return CORDBG_E_MISSING_DEBUGGER_EXPORTS;
index 1a5cff8..ec7fd1b 100644 (file)
 #include "binder.h"
 #include "win32threadpool.h"
 
-#ifdef TARGET_UNIX
-#include <dactablerva.h>
-#endif
-
 #ifdef FEATURE_APPX
 #include "appxutil.h"
 #endif // FEATURE_APPX
 
+extern HRESULT GetDacTableAddress(ICorDebugDataTarget* dataTarget, ULONG64 baseAddress, PULONG64 dacTableAddress);
+
 #if defined(DAC_MEASURE_PERF)
 
 unsigned __int64 g_nTotalTime;
@@ -196,8 +194,13 @@ HRESULT ClrDataAccess::EnumMemCLRStatic(IN CLRDataEnumMemoryFlags flags)
     ReportMem(m_globalBase + g_dacGlobals.id, sizeof(size_type));
 
 #ifdef TARGET_UNIX
+    ULONG64 dacTableAddress;
+    HRESULT hr = GetDacTableAddress(m_pTarget, m_globalBase, &dacTableAddress);
+    if (FAILED(hr)) {
+        return hr;
+    }
     // Add the dac table memory in coreclr
-    CATCH_ALL_EXCEPT_RETHROW_COR_E_OPERATIONCANCELLED ( ReportMem(m_globalBase + DAC_TABLE_RVA, sizeof(g_dacGlobals)); )
+    CATCH_ALL_EXCEPT_RETHROW_COR_E_OPERATIONCANCELLED ( ReportMem(dacTableAddress, sizeof(g_dacGlobals)); )
 #endif
 
     // Cannot use CATCH_ALL_EXCEPT_RETHROW_COR_E_OPERATIONCANCELLED
index c432a32..2276831 100644 (file)
@@ -5,8 +5,18 @@ endif(CLR_CMAKE_HOST_WIN32)
 
 set(CMAKE_INCLUDE_CURRENT_DIR ON)
 
+include_directories(${CLR_DIR}/src/inc/llvm)
+
+add_definitions(-DPAL_STDCPP_COMPAT)
+
 set(DBGUTIL_SOURCES
     dbgutil.cpp
 )
 
+if(CLR_CMAKE_TARGET_LINUX)
+    list(APPEND DBGUTIL_SOURCES
+        elfreader.cpp
+    )
+endif(CLR_CMAKE_TARGET_LINUX)
+
 add_library_clr(dbgutil STATIC ${DBGUTIL_SOURCES})
index 62135af..0f30fdf 100644 (file)
@@ -17,6 +17,8 @@
 #include <assert.h>
 #include <stdio.h>
 
+#ifdef HOST_WINDOWS
+
 // Returns the RVA of the resource section for the module specified by the given data target and module base.
 // Returns failure if the module doesn't have a resource section.
 //
@@ -391,6 +393,8 @@ HRESULT GetNextLevelResourceEntryRVAByName(ICorDebugDataTarget* pDataTarget,
     return hr;
 }
 
+#endif // HOST_WINDOWS
+
 // A small wrapper that reads from the data target and throws on error
 HRESULT ReadFromDataTarget(ICorDebugDataTarget* pDataTarget,
     ULONG64 addr,
diff --git a/src/coreclr/src/debug/dbgutil/elfreader.cpp b/src/coreclr/src/debug/dbgutil/elfreader.cpp
new file mode 100644 (file)
index 0000000..d345495
--- /dev/null
@@ -0,0 +1,516 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+#include "elfreader.h"
+#include <arrayholder.h>
+
+#define Elf_Ehdr   ElfW(Ehdr)
+#define Elf_Phdr   ElfW(Phdr)
+#define Elf_Shdr   ElfW(Shdr)
+#define Elf_Nhdr   ElfW(Nhdr)
+#define Elf_Dyn    ElfW(Dyn)
+#define Elf_Sym    ElfW(Sym)
+
+#ifdef HOST_UNIX
+#define TRACE(args...) Trace(args)
+#else
+#define TRACE(args, ...)
+#endif
+
+#ifndef HOST_WINDOWS
+static const char ElfMagic[] = { 0x7f, 'E', 'L', 'F', '\0' };
+#endif
+
+class ElfReaderExport: public ElfReader
+{
+private:
+    ICorDebugDataTarget* m_dataTarget;
+
+public:
+    ElfReaderExport(ICorDebugDataTarget* dataTarget) :
+        m_dataTarget(dataTarget)
+    {
+        dataTarget->AddRef();
+    }
+
+    virtual ~ElfReaderExport()
+    {
+        m_dataTarget->Release();
+    }
+
+private:
+    virtual bool ReadMemory(void* address, void* buffer, size_t size)
+    {
+        uint32_t read = 0;
+        return SUCCEEDED(m_dataTarget->ReadVirtual(reinterpret_cast<CLRDATA_ADDRESS>(address), reinterpret_cast<PBYTE>(buffer), (uint32_t)size, &read));
+    }
+};
+
+//
+// Main entry point to get an export symbol
+//
+bool
+TryGetSymbol(ICorDebugDataTarget* dataTarget, uint64_t baseAddress, const char* symbolName, uint64_t* symbolAddress)
+{
+    ElfReaderExport elfreader(dataTarget);
+    if (elfreader.PopulateELFInfo(baseAddress))
+    {
+        uint64_t symbolOffset;
+        if (elfreader.TryLookupSymbol(symbolName, &symbolOffset))
+        {
+            *symbolAddress = baseAddress + symbolOffset;
+            return true;
+        }
+    }
+    *symbolAddress = 0;
+    return false;
+}
+
+//
+// ELF reader constructor/destructor
+//
+
+ElfReader::ElfReader() :
+    m_rdebugAddr(nullptr),
+    m_gnuHashTableAddr(nullptr),
+    m_stringTableAddr(nullptr),
+    m_stringTableSize(0),
+    m_symbolTableAddr(nullptr),
+    m_buckets(nullptr),
+    m_chainsAddress(nullptr)
+{
+    memset(&m_hashTable, 0, sizeof(m_hashTable));
+}
+
+ElfReader::~ElfReader()
+{
+    if (m_buckets != nullptr) {
+        delete m_buckets;
+    }
+}
+
+//
+// Initialize the ELF reader from a module the base address
+//
+bool
+ElfReader::PopulateELFInfo(uint64_t baseAddress)
+{
+    TRACE("PopulateELFInfo: base %" PRIA PRIx64 "\n", baseAddress);
+    Elf_Dyn* dynamicAddr = nullptr;
+
+    // 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)) {
+        return false;
+    }
+    return EnumerateDynamicEntries(dynamicAddr);
+}
+
+bool
+ElfReader::EnumerateDynamicEntries(Elf_Dyn* dynamicAddr)
+{
+    if (dynamicAddr == nullptr) {
+        return false;
+    }
+
+    // Search for dynamic entries
+    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) {
+            m_rdebugAddr = (void*)dyn.d_un.d_ptr;
+        }
+        else if (dyn.d_tag == DT_GNU_HASH) {
+            m_gnuHashTableAddr = (void*)dyn.d_un.d_ptr;
+        }
+        else if (dyn.d_tag == DT_STRTAB) {
+            m_stringTableAddr = (void*)dyn.d_un.d_ptr;
+        }
+        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;
+        }
+        dynamicAddr++;
+    }
+
+    if (m_gnuHashTableAddr == nullptr || m_stringTableAddr == nullptr || m_symbolTableAddr == nullptr) {
+        TRACE("ERROR: hash, string or symbol table address not found\n");
+        return false;
+    }
+
+    // Initialize the hash table
+    if (!InitializeGnuHashTable()) {
+        return false;
+    }
+
+    return true;
+}
+
+//
+// Symbol table support
+//
+
+bool
+ElfReader::TryLookupSymbol(std::string symbolName, uint64_t* symbolOffset)
+{
+    std::vector<int32_t> symbolIndexes;
+    if (GetPossibleSymbolIndex(symbolName, symbolIndexes)) {
+        Elf_Sym symbol;
+        for (int32_t possibleLocation : symbolIndexes)
+        {
+            if (GetSymbol(possibleLocation, &symbol))
+            {
+                std::string possibleName;
+                if (GetStringAtIndex(symbol.st_name, possibleName))
+                {
+                    if (symbolName.compare(possibleName) == 0)
+                    {
+                        *symbolOffset = symbol.st_value;
+                        TRACE("TryLookupSymbol found '%s' at offset %" PRIxA "\n", symbolName.c_str(), *symbolOffset);
+                        return true;
+                    }
+                }
+            }
+        }
+    }
+    TRACE("TryLookupSymbol '%s' not found\n", symbolName.c_str());
+    *symbolOffset = 0;
+    return false;
+}
+
+bool
+ElfReader::GetSymbol(int32_t index, Elf_Sym* symbol)
+{
+    int symSize = sizeof(Elf_Sym);
+    if (!ReadMemory((char*)m_symbolTableAddr + (index * symSize), symbol, symSize)) {
+        return false;
+    }
+    return true;
+}
+
+//
+// Hash (GNU) hash table support
+//
+
+bool
+ElfReader::InitializeGnuHashTable()
+{
+    if (!ReadMemory(m_gnuHashTableAddr, &m_hashTable, sizeof(m_hashTable))) {
+        TRACE("ERROR: InitializeGnuHashTable hashtable ReadMemory(%p) FAILED\n", m_gnuHashTableAddr);
+        return false;
+    }
+    if (m_hashTable.BucketCount <= 0 || m_hashTable.SymbolOffset == 0) {
+        TRACE("ERROR: InitializeGnuHashTable invalid BucketCount or SymbolOffset\n");
+        return false;
+    }
+    m_buckets = new (std::nothrow) int32_t[m_hashTable.BucketCount];
+    if (m_buckets == nullptr) {
+        return false;
+    }
+    void* bucketsAddress = (char*)m_gnuHashTableAddr + sizeof(GnuHashTable) + (m_hashTable.BloomSize * sizeof(size_t));
+    if (!ReadMemory(bucketsAddress, m_buckets, m_hashTable.BucketCount * sizeof(int32_t))) {
+        TRACE("ERROR: InitializeGnuHashTable buckets ReadMemory(%p) FAILED\n", bucketsAddress);
+        return false;
+    }
+    m_chainsAddress = (char*)bucketsAddress + (m_hashTable.BucketCount * sizeof(int32_t));
+    return true;
+}
+
+bool
+ElfReader::GetPossibleSymbolIndex(const std::string& symbolName, std::vector<int32_t>& symbolIndexes)
+{
+    uint32_t hash = Hash(symbolName);
+    int i = m_buckets[hash % m_hashTable.BucketCount] - m_hashTable.SymbolOffset;
+    TRACE("GetPossibleSymbolIndex hash %08x index: %d BucketCount %d SymbolOffset %08x\n", hash, i, m_hashTable.BucketCount, m_hashTable.SymbolOffset);
+    for (;; i++)
+    {
+        int32_t chainVal;
+        if (!GetChain(i, &chainVal)) {
+            TRACE("ERROR: GetPossibleSymbolIndex GetChain FAILED\n");
+            return false;
+        }
+        if ((chainVal & 0xfffffffe) == (hash & 0xfffffffe))
+        {
+            symbolIndexes.push_back(i + m_hashTable.SymbolOffset);
+        }
+        if ((chainVal & 0x1) == 0x1)
+        {
+            break;
+        }
+    }
+    return true;
+}
+
+uint32_t
+ElfReader::Hash(const std::string& symbolName)
+{
+    uint32_t h = 5381;
+    for (int i = 0; i < symbolName.length(); i++)
+    {
+        h = (h << 5) + h + symbolName[i];
+    }
+    return h;
+}
+
+bool
+ElfReader::GetChain(int index, int32_t* chain)
+{
+    return ReadMemory((char*)m_chainsAddress + (index * sizeof(int32_t)), chain, sizeof(int32_t));
+}
+
+//
+// String table support
+//
+
+bool
+ElfReader::GetStringAtIndex(int index, std::string& result)
+{
+    while(true)
+    {
+        if (index > m_stringTableSize) {
+            TRACE("ERROR: GetStringAtIndex index %d > string table size\n", index);
+            return false;
+        }
+        char ch;
+        void* address = (char*)m_stringTableAddr + index;
+        if (!ReadMemory(address, &ch, sizeof(ch))) {
+            TRACE("ERROR: GetStringAtIndex ReadMemory(%p) FAILED\n", address);
+            return false;
+        }
+        if (ch == '\0') {
+            break;
+        }
+        result.append(1, ch);
+        index++;
+    }
+    return true;
+}
+
+#ifdef HOST_UNIX
+
+//
+// Enumerate through the dynamic debug link map entries
+//
+bool
+ElfReader::EnumerateLinkMapEntries()
+{
+    struct r_debug* rdebugAddr = reinterpret_cast<struct r_debug*>(m_rdebugAddr);
+    TRACE("DSO: rdebugAddr %p\n", rdebugAddr);
+
+    struct r_debug debugEntry;
+    if (!ReadMemory(rdebugAddr, &debugEntry, sizeof(debugEntry))) {
+        TRACE("ERROR: ReadMemory(%p, %" PRIx ") r_debug FAILED\n", rdebugAddr, sizeof(debugEntry));
+        return false;
+    }
+
+    // Add the DSO link_map entries
+    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));
+            return false;
+        }
+        // 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) {
+            for (; i < PATH_MAX; i++)
+            {
+                char ch;
+                if (!ReadMemory(map.l_name + i, &ch, sizeof(ch))) {
+                    TRACE("DSO: ReadMemory link_map name %p + %d FAILED\n", map.l_name, i);
+                    break;
+                }
+                if (ch == '\0') {
+                    break;
+                }
+                moduleName.append(1, ch);
+            }
+        }
+        TRACE("\nDSO: link_map entry %p l_ld %p l_addr (Ehdr) %" PRIx " %s\n", linkMapAddr, map.l_ld, map.l_addr, moduleName.c_str());
+
+        // Call the derived class for each module
+        VisitModule(map.l_addr, moduleName);
+
+        linkMapAddr = map.l_next;
+    }
+
+    return true;
+}
+
+#endif // HOST_UNIX
+
+bool
+ElfReader::EnumerateProgramHeaders(uint64_t baseAddress, Elf_Dyn** pdynamicAddr)
+{
+    Elf_Ehdr ehdr;
+    if (!ReadMemory((void*)baseAddress, &ehdr, sizeof(ehdr))) {
+        TRACE("ERROR: EnumerateProgramHeaders ReadMemory(%p, %" PRIx ") ehdr FAILED\n", (void*)baseAddress, sizeof(ehdr));
+        return false;
+    }
+    if (memcmp(ehdr.e_ident, ElfMagic, strlen(ElfMagic)) != 0) {
+        TRACE("ERROR: EnumerateProgramHeaders Invalid elf header signature\n");
+        return false;
+    }
+    int phnum = ehdr.e_phnum;
+    if (ehdr.e_phoff == 0 || phnum <= 0) {
+        return false;
+    }
+    TRACE("ELF: type %d mach 0x%x ver %d flags 0x%x phnum %d phoff %" PRIxA " phentsize 0x%02x shnum %d shoff %" PRIxA " shentsize 0x%02x shstrndx %d\n",
+        ehdr.e_type, ehdr.e_machine, ehdr.e_version, ehdr.e_flags, phnum, ehdr.e_phoff, ehdr.e_phentsize, ehdr.e_shnum, ehdr.e_shoff, ehdr.e_shentsize, ehdr.e_shstrndx);
+#ifdef PN_XNUM
+     _ASSERTE(phnum != PN_XNUM);
+#endif
+    _ASSERTE(ehdr.e_phentsize == sizeof(Elf_Phdr));
+#ifdef TARGET_64BIT
+    _ASSERTE(ehdr.e_ident[EI_CLASS] == ELFCLASS64);
+#else
+    _ASSERTE(ehdr.e_ident[EI_CLASS] == ELFCLASS32);
+#endif
+    _ASSERTE(ehdr.e_ident[EI_DATA] == ELFDATA2LSB);
+
+    Elf_Phdr* phdrAddr = reinterpret_cast<Elf_Phdr*>(baseAddress + ehdr.e_phoff);
+    return EnumerateProgramHeaders(phdrAddr, phnum, baseAddress, pdynamicAddr);
+}
+
+//
+// Enumerate and find the dynamic program header entry
+//
+bool
+ElfReader::EnumerateProgramHeaders(Elf_Phdr* phdrAddr, int phnum, uint64_t baseAddress, Elf_Dyn** pdynamicAddr)
+{
+    uint64_t loadbias = baseAddress;
+
+    for (int i = 0; i < phnum; i++)
+    {
+        Elf_Phdr ph;
+        if (!ReadMemory(phdrAddr + i, &ph, sizeof(ph))) {
+            TRACE("ERROR: ReadMemory(%p, %" PRIx ") phdr FAILED\n", phdrAddr + i, sizeof(ph));
+            return false;
+        }
+        if (ph.p_type == PT_LOAD && ph.p_offset == 0) {
+            loadbias -= ph.p_vaddr;
+            TRACE("PHDR: loadbias %" PRIA PRIx64 "\n", loadbias);
+            break;
+        }
+    }
+
+    for (int i = 0; i < phnum; i++)
+    {
+        Elf_Phdr ph;
+        if (!ReadMemory(phdrAddr + i, &ph, sizeof(ph))) {
+            TRACE("ERROR: ReadMemory(%p, %" PRIx ") phdr FAILED\n", phdrAddr + i, sizeof(ph));
+            return false;
+        }
+        TRACE("PHDR: %p type %d (%x) vaddr %" PRIxA " memsz %" PRIxA " paddr %" PRIxA " filesz %" PRIxA " offset %" PRIxA " align %" PRIxA "\n",
+            phdrAddr + i, ph.p_type, ph.p_type, ph.p_vaddr, ph.p_memsz, ph.p_paddr, ph.p_filesz, ph.p_offset, ph.p_align);
+
+        switch (ph.p_type)
+        {
+        case PT_DYNAMIC:
+            if (pdynamicAddr != nullptr) {
+                *pdynamicAddr = reinterpret_cast<Elf_Dyn*>(loadbias + ph.p_vaddr);
+            }
+            break;
+        }
+
+        // Give any derived classes a chance at the program header
+        VisitProgramHeader(loadbias, baseAddress, &ph);
+    }
+
+    return true;
+}
+
+#ifdef HOST_WINDOWS
+
+/* ELF 32bit header */
+Elf32_Ehdr::Elf32_Ehdr()
+{
+    e_ident[EI_MAG0] = ElfMagic[0];
+    e_ident[EI_MAG1] = ElfMagic[1];
+    e_ident[EI_MAG2] = ElfMagic[2];
+    e_ident[EI_MAG3] = ElfMagic[3];
+    e_ident[EI_CLASS] = ELFCLASS32;
+    e_ident[EI_DATA] = ELFDATA2LSB;
+    e_ident[EI_VERSION] = EV_CURRENT;
+    e_ident[EI_OSABI] = ELFOSABI_NONE;
+    e_ident[EI_ABIVERSION] = 0;
+    for (int i = EI_PAD; i < EI_NIDENT; ++i) {
+        e_ident[i] = 0;
+    }
+    e_type = ET_REL;
+#if defined(TARGET_X86)
+    e_machine = EM_386;
+#elif defined(TARGET_ARM)
+    e_machine = EM_ARM;
+#endif
+    e_flags = 0;
+    e_version = 1;
+    e_entry = 0;
+    e_phoff = 0;
+    e_ehsize = sizeof(Elf32_Ehdr);
+    e_phentsize = 0;
+    e_phnum = 0;
+}
+
+/* ELF 64bit header */
+Elf64_Ehdr::Elf64_Ehdr()
+{
+    e_ident[EI_MAG0] = ElfMagic[0];
+    e_ident[EI_MAG1] = ElfMagic[1];
+    e_ident[EI_MAG2] = ElfMagic[2];
+    e_ident[EI_MAG3] = ElfMagic[3];
+    e_ident[EI_CLASS] = ELFCLASS64;
+    e_ident[EI_DATA] = ELFDATA2LSB;
+    e_ident[EI_VERSION] = EV_CURRENT;
+    e_ident[EI_OSABI] = ELFOSABI_NONE;
+    e_ident[EI_ABIVERSION] = 0;
+    for (int i = EI_PAD; i < EI_NIDENT; ++i) {
+        e_ident[i] = 0;
+    }
+    e_type = ET_REL;
+#if defined(TARGET_AMD64)
+    e_machine = EM_X86_64;
+#elif defined(TARGET_ARM64)
+    e_machine = EM_AARCH64;
+#endif
+    e_flags = 0;
+    e_version = 1;
+    e_entry = 0;
+    e_phoff = 0;
+    e_ehsize = sizeof(Elf64_Ehdr);
+    e_phentsize = 0;
+    e_phnum = 0;
+}
+
+#endif // HOST_WINDOWS
diff --git a/src/coreclr/src/debug/dbgutil/elfreader.h b/src/coreclr/src/debug/dbgutil/elfreader.h
new file mode 100644 (file)
index 0000000..77129bb
--- /dev/null
@@ -0,0 +1,86 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// 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>
+#endif // HOST_UNIX
+#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'.  */
+#define ElfW(type)      _ElfW (Elf, TARGET_WORDSIZE, type)
+#define _ElfW(e,w,t)    _ElfW_1 (e, w, _##t)
+#define _ElfW_1(e,w,t)  e##w##t
+#endif
+
+typedef struct {
+    int32_t BucketCount;
+    int32_t SymbolOffset;
+    int32_t BloomSize;
+    int32_t BloomShift;
+} GnuHashTable;
+
+class ElfReader
+{
+private:
+    void* m_rdebugAddr;                     // DT_DEBUG
+    void* m_gnuHashTableAddr;               // DT_GNU_HASH
+    void* m_stringTableAddr;                // DT_STRTAB
+    int m_stringTableSize;                  // DT_STRSIZ
+    void* m_symbolTableAddr;                // DT_SYMTAB
+
+    GnuHashTable m_hashTable;               // gnu hash table info
+    int32_t* m_buckets;                     // gnu hash table buckets    
+    void* m_chainsAddress;
+
+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();
+#endif
+    bool EnumerateProgramHeaders(uint64_t baseAddress, ElfW(Dyn)** pdynamicAddr);
+    bool EnumerateProgramHeaders(ElfW(Phdr)* phdrAddr, int phnum, uint64_t baseAddress, ElfW(Dyn)** pdynamicAddr);
+
+private:
+    bool GetSymbol(int32_t index, ElfW(Sym)* symbol);
+    bool InitializeGnuHashTable();
+    bool GetPossibleSymbolIndex(const std::string& symbolName, std::vector<int32_t>& symbolIndexes);
+    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);
+    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;
+    virtual void Trace(const char* format, ...) { };
+};
index 2f60124..967ac69 100644 (file)
@@ -40,6 +40,7 @@ extern DWORD gAppDomainTLSIndex;
 extern BOOL g_fAppX;
 #endif // FEATURE_APPX
 
+DLLEXPORT 
 DacGlobals g_dacTable;
 
 // DAC global pointer table initialization
index 1085876..2f742f3 100644 (file)
@@ -8,3 +8,6 @@ coreclr_shutdown_2
 ; Functions exported by the coreclr
 DllMain
 GetCLRRuntimeHost
+; DAC table export
+g_dacTable
+
index 2fc8858..f05c3d3 100644 (file)
@@ -18,7 +18,7 @@
 #define MAKE_TARGET_DLLNAME_W(name) name W(".dll")
 #define MAKE_TARGET_DLLNAME_A(name) name ".dll"
 #else // TARGET_WINDOWS
-#ifdef TARGET_OSX
+#ifdef TARGET_DARWIN
 #define MAKE_TARGET_DLLNAME_W(name) W("lib") name W(".dylib")
 #define MAKE_TARGET_DLLNAME_A(name)  "lib" name  ".dylib"
 #else