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)
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)
target_link_libraries(createdump
createdump_lib
corguids
+ dbgutil
# share the PAL in the dac module
mscordaccore
)
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
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;
}
//
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);
+ }
+}
// 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;
// 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
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);
uint32_t GetMemoryRegionFlags(uint64_t start);
bool ValidRegion(const MemoryRegion& region);
void CombineMemoryRegions();
+ void Trace(const char* format, ...);
};
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
)
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)
#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"
#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;
#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;
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
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})
#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.
//
return hr;
}
+#endif // HOST_WINDOWS
+
// A small wrapper that reads from the data target and throws on error
HRESULT ReadFromDataTarget(ICorDebugDataTarget* pDataTarget,
ULONG64 addr,
--- /dev/null
+// 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
--- /dev/null
+// 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, ...) { };
+};
extern BOOL g_fAppX;
#endif // FEATURE_APPX
+DLLEXPORT
DacGlobals g_dacTable;
// DAC global pointer table initialization
; Functions exported by the coreclr
DllMain
GetCLRRuntimeHost
+; DAC table export
+g_dacTable
+
#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