// 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
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))
//
ElfReader::ElfReader() :
- m_rdebugAddr(nullptr),
m_gnuHashTableAddr(nullptr),
m_stringTableAddr(nullptr),
m_stringTableSize(0),
}
//
-// 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));
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++;
}
#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))) {
}
// 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));
// 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;
#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))) {
_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;
}
}
+ if (ploadbias != nullptr) {
+ *ploadbias = loadbias;
+ }
+
+ // Enumerate all the program headers
for (int i = 0; i < phnum; i++)
{
Elf_Phdr ph;
// 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>
#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'. */
class ElfReader
{
private:
- void* m_rdebugAddr; // DT_DEBUG
void* m_gnuHashTableAddr; // DT_GNU_HASH
void* m_stringTableAddr; // DT_STRTAB
int m_stringTableSize; // DT_STRSIZ
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);
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;