Skip to content

Commit

Permalink
Properly calculate the ELF unwind info section size (#72146)
Browse files Browse the repository at this point in the history
Properly calculate the ELF unwind info section size

Uses the same method that windbg does to calculate the eh_frame section size by partial decoding the FDE/CIE's enough to figure out the total size. We can't use the .eh_frame section because ELF module sections are not in-memory.

Change the ReadMemoryAdapter passed to the new PAL_GetUnwindInfoSize function to read memory directly and not add it to the memory region list
  • Loading branch information
mikem8361 authored Jul 17, 2022
1 parent 072eda8 commit cd5e461
Show file tree
Hide file tree
Showing 4 changed files with 202 additions and 5 deletions.
46 changes: 44 additions & 2 deletions src/coreclr/debug/createdump/crashinfounix.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,12 @@

#include "createdump.h"

#ifndef PT_ARM_EXIDX
#define PT_ARM_EXIDX 0x70000001 /* See llvm ELF.h */
#endif

extern CrashInfo* g_crashInfo;

int g_readProcessMemoryErrno = 0;

bool GetStatus(pid_t pid, pid_t* ppid, pid_t* tgid, std::string* name);
Expand Down Expand Up @@ -334,6 +340,14 @@ CrashInfo::VisitModule(uint64_t baseAddress, std::string& moduleName)
EnumerateProgramHeaders(baseAddress);
}

// Helper for PAL_GetUnwindInfoSize. Reads memory directly without adding it to the memory region list.
BOOL
ReadMemoryAdapter(PVOID address, PVOID buffer, SIZE_T size)
{
size_t read = 0;
return g_crashInfo->ReadProcessMemory(address, buffer, size, &read);
}

//
// Called for each program header adding the build id note, unwind frame
// region and module addresses to the crash info.
Expand All @@ -345,12 +359,40 @@ CrashInfo::VisitProgramHeader(uint64_t loadbias, uint64_t baseAddress, Phdr* phd
{
case PT_DYNAMIC:
case PT_NOTE:
case PT_GNU_EH_FRAME:
if (phdr->p_vaddr != 0 && phdr->p_memsz != 0) {
#if defined(TARGET_ARM)
case PT_ARM_EXIDX:
#endif
if (phdr->p_vaddr != 0 && phdr->p_memsz != 0)
{
InsertMemoryRegion(loadbias + phdr->p_vaddr, phdr->p_memsz);
}
break;

case PT_GNU_EH_FRAME:
if (phdr->p_vaddr != 0 && phdr->p_memsz != 0)
{
uint64_t ehFrameHdrStart = loadbias + phdr->p_vaddr;
uint64_t ehFrameHdrSize = phdr->p_memsz;
TRACE("VisitProgramHeader: ehFrameHdrStart %016llx ehFrameHdrSize %08llx\n", ehFrameHdrStart, ehFrameHdrSize);
InsertMemoryRegion(ehFrameHdrStart, ehFrameHdrSize);

uint64_t ehFrameStart;
uint64_t ehFrameSize;
if (PAL_GetUnwindInfoSize(baseAddress, ehFrameHdrStart, ReadMemoryAdapter, &ehFrameStart, &ehFrameSize))
{
TRACE("VisitProgramHeader: ehFrameStart %016llx ehFrameSize %08llx\n", ehFrameStart, ehFrameSize);
if (ehFrameStart != 0 && ehFrameSize != 0)
{
InsertMemoryRegion(ehFrameStart, ehFrameSize);
}
}
else
{
TRACE("VisitProgramHeader: PAL_GetUnwindInfoSize FAILED\n");
}
}
break;

case PT_LOAD:
AddModuleAddressRange(loadbias + phdr->p_vaddr, loadbias + phdr->p_vaddr + phdr->p_memsz, baseAddress);
break;
Expand Down
1 change: 1 addition & 0 deletions src/coreclr/dlls/mscordac/mscordac_unixexports.src
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ nativeStringResourceTable_mscorrc
#PAL_GetLogicalCpuCountFromOS
#PAL_GetTotalCpuCount
#PAL_GetNumaProcessorNode
#PAL_GetUnwindInfoSize
#PAL_get_stdout
#PAL_get_stderr
#PAL_GetApplicationGroupId
Expand Down
3 changes: 2 additions & 1 deletion src/coreclr/pal/inc/pal.h
Original file line number Diff line number Diff line change
Expand Up @@ -2662,14 +2662,15 @@ PALIMPORT
size_t
PALAPI
PAL_GetLogicalProcessorCacheSizeFromOS();
#define GetLogicalProcessorCacheSizeFromOS PAL_GetLogicalProcessorCacheSizeFromOS

typedef BOOL(*UnwindReadMemoryCallback)(PVOID address, PVOID buffer, SIZE_T size);

PALIMPORT BOOL PALAPI PAL_VirtualUnwind(CONTEXT *context, KNONVOLATILE_CONTEXT_POINTERS *contextPointers);

PALIMPORT BOOL PALAPI PAL_VirtualUnwindOutOfProc(CONTEXT *context, KNONVOLATILE_CONTEXT_POINTERS *contextPointers, PULONG64 functionStart, SIZE_T baseAddress, UnwindReadMemoryCallback readMemoryCallback);

#define GetLogicalProcessorCacheSizeFromOS PAL_GetLogicalProcessorCacheSizeFromOS
PALIMPORT BOOL PALAPI PAL_GetUnwindInfoSize(SIZE_T baseAddress, ULONG64 ehFrameHdrAddr, UnwindReadMemoryCallback readMemoryCallback, PULONG64 ehFrameStart, PULONG64 ehFrameSize);

/* PAL_CS_NATIVE_DATA_SIZE is defined as sizeof(PAL_CRITICAL_SECTION_NATIVE_DATA) */

Expand Down
157 changes: 155 additions & 2 deletions src/coreclr/pal/src/exception/remote-unwind.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,7 @@ typedef struct _libunwindInfo
UnwindReadMemoryCallback ReadMemory;
} libunwindInfo;

#if defined(__APPLE__) || defined(FEATURE_USE_SYSTEM_LIBUNWIND)
#ifdef HOST_UNIX

#define EXTRACT_BITS(value, mask) ((value >> __builtin_ctz(mask)) & (((1 << __builtin_popcount(mask))) - 1))

Expand Down Expand Up @@ -527,6 +527,10 @@ ReadEncodedPointer(
return true;
}

#endif // HOST_UNIX

#if defined(__APPLE__) || defined(FEATURE_USE_SYSTEM_LIBUNWIND)

template<class T>
static bool
BinarySearchEntries(
Expand Down Expand Up @@ -2282,7 +2286,7 @@ find_proc_info(unw_addr_space_t as, unw_word_t ip, unw_proc_info_t *pip, int nee
}

#ifdef FEATURE_USE_SYSTEM_LIBUNWIND
if (ehFrameHdrAddr == 0) {
if (ehFrameHdrAddr == 0) {
ASSERT("ELF: No PT_GNU_EH_FRAME program header\n");
return -UNW_EINVAL;
}
Expand Down Expand Up @@ -2506,6 +2510,148 @@ PAL_VirtualUnwindOutOfProc(CONTEXT *context, KNONVOLATILE_CONTEXT_POINTERS *cont
return result;
}

BOOL
PALAPI
PAL_GetUnwindInfoSize(SIZE_T baseAddress, ULONG64 ehFrameHdrAddr, UnwindReadMemoryCallback readMemoryCallback, PULONG64 ehFrameStart, PULONG64 ehFrameSize)
{
_ASSERTE(ehFrameStart != nullptr);
_ASSERTE(ehFrameSize != nullptr);
_ASSERTE(ehFrameHdrAddr != 0);
*ehFrameStart = 0;
*ehFrameSize = 0;

#ifdef HOST_UNIX
libunwindInfo info;
info.BaseAddress = baseAddress;
info.Context = nullptr;
info.FunctionStart = 0;
info.ReadMemory = readMemoryCallback;

eh_frame_hdr ehFrameHdr;
if (!info.ReadMemory((PVOID)ehFrameHdrAddr, &ehFrameHdr, sizeof(eh_frame_hdr))) {
ERROR("ELF: reading ehFrameHdrAddr %p\n", ehFrameHdrAddr);
return FALSE;
}
TRACE("ehFrameHdrAddr %p version %d eh_frame_ptr_enc %d fde_count_enc %d table_enc %d\n",
ehFrameHdrAddr, ehFrameHdr.version, ehFrameHdr.eh_frame_ptr_enc, ehFrameHdr.fde_count_enc, ehFrameHdr.table_enc);

if (ehFrameHdr.version != DW_EH_VERSION) {
ASSERT("ehFrameHdr version %x not supported\n", ehFrameHdr.version);
return FALSE;
}
unw_word_t addr = ehFrameHdrAddr + sizeof(eh_frame_hdr);
unw_word_t ehFramePtr;
unw_word_t fdeCount;

// Decode the eh_frame_hdr info
if (!ReadEncodedPointer(&info, &addr, ehFrameHdr.eh_frame_ptr_enc, UINTPTR_MAX, &ehFramePtr)) {
ERROR("decoding eh_frame_ptr\n");
return FALSE;
}
if (!ReadEncodedPointer(&info, &addr, ehFrameHdr.fde_count_enc, UINTPTR_MAX, &fdeCount)) {
ERROR("decoding fde_count_enc\n");
return FALSE;
}
TRACE("ehFrameStart %p fdeCount %p\n", ehFrameStart, fdeCount);

// If there are no frame table entries
if (fdeCount == 0) {
TRACE("No frame table entries\n");
return FALSE;
}

uint64_t totalSize = 0;
uint64_t encounteredCieCount = 0;
uint64_t encounteredFdeCount = 0;
addr = ehFramePtr;

while (true)
{
bool is64BitEncoding = false;
uint64_t initialLength = 0;
uint32_t initialLength32;

if (!ReadValue32(&info, &addr, &initialLength32)) {
return FALSE;
}
totalSize += sizeof(initialLength32);

if (initialLength32 >= 0xfffffff0)
{
if (initialLength32 == 0xffffffff)
{
// 64 bit encoding
is64BitEncoding = true;
if (!ReadValue64(&info, &addr, &initialLength)) {
return FALSE;
}
totalSize += sizeof(initialLength);
}
else
{
ASSERT("Length encoding not supported: %08x\n", initialLength32);
return FALSE;
}
}
else
{
// 32 bit encoding
initialLength = static_cast<uint64_t>(initialLength32);
}

if (initialLength == 0)
{
break;
}

// "addr" either points to a CIE_id in a CIE or CIE_ptr in a FDE. A value of zero indicates a CIE.
uint64_t ciePtr;
if (is64BitEncoding)
{
if (!ReadValue64(&info, &addr, &ciePtr)) {
return FALSE;
}
addr -= sizeof(ciePtr);
}
else
{
uint32_t ciePtr32;
if (!ReadValue32(&info, &addr, &ciePtr32)) {
return FALSE;
}
addr -= sizeof(ciePtr32);
ciePtr = static_cast<uint64_t>(ciePtr32);
}

if (ciePtr == 0)
{
encounteredCieCount++;
}
else
{
encounteredFdeCount++;
}

totalSize += initialLength;
addr += initialLength;

// If we've seen more FDEs than expected, somehow either the header is inconsistent or we overread the end of the table.
if (encounteredFdeCount >= fdeCount)
{
break;
}
}

_ASSERTE(encounteredFdeCount == fdeCount);

*ehFrameStart = ehFramePtr;
*ehFrameSize = totalSize;
return TRUE;
#else
return FALSE;
#endif // HOST_UNIX
}

#else

BOOL
Expand All @@ -2515,4 +2661,11 @@ PAL_VirtualUnwindOutOfProc(CONTEXT *context, KNONVOLATILE_CONTEXT_POINTERS *cont
return FALSE;
}

BOOL
PALAPI
PAL_GetUnwindInfoSize(SIZE_T baseAddress, ULONG64 ehFrameHdrAddr, UnwindReadMemoryCallback readMemoryCallback, PULONG64 ehFrameStart, PULONG64 ehFrameSize)
{
return FALSE;
}

#endif // defined(__APPLE__) || defined(HAVE_UNW_GET_ACCESSORS)

0 comments on commit cd5e461

Please sign in to comment.