shadPS4/src/core/loader/dwarf.cpp
raphaelthegreat 24446bcc32 Cuphead work
2024-06-12 13:00:15 +03:00

140 lines
4.1 KiB
C++

// SPDX-FileCopyrightText: Copyright (C) 2001-2024 Free Software Foundation, Inc.
// SPDX-License-Identifier: GPL-2.0-or-later
#include "common/assert.h"
#include "common/logging/log.h"
#include "core/loader/dwarf.h"
namespace Dwarf {
template <typename T>
T get(uintptr_t addr) {
T val;
memcpy(&val, reinterpret_cast<void*>(addr), sizeof(T));
return val;
}
static uintptr_t getEncodedP(uintptr_t& addr, uintptr_t end, u8 encoding,
uintptr_t datarelBase) {
const uintptr_t startAddr = addr;
const u8 *p = (u8*)addr;
uintptr_t result;
// First get value
switch (encoding & 0x0F) {
case DW_EH_PE_ptr:
result = get<uintptr_t>(addr);
p += sizeof(uintptr_t);
addr = (uintptr_t)p;
break;
case DW_EH_PE_udata2:
result = get<u16>(addr);
p += sizeof(u16);
addr = (uintptr_t)p;
break;
case DW_EH_PE_udata4:
result = get<u32>(addr);
p += sizeof(u32);
addr = (uintptr_t)p;
break;
case DW_EH_PE_udata8:
result = get<u64>(addr);
p += sizeof(u64);
addr = (uintptr_t)p;
break;
case DW_EH_PE_sdata2:
// Sign extend from signed 16-bit value.
result = get<s16>(addr);
p += sizeof(s16);
addr = (uintptr_t)p;
break;
case DW_EH_PE_sdata4:
// Sign extend from signed 32-bit value.
result = get<s32>(addr);
p += sizeof(s32);
addr = (uintptr_t)p;
break;
case DW_EH_PE_sdata8:
result = get<s64>(addr);
p += sizeof(s64);
addr = (uintptr_t)p;
break;
default:
UNREACHABLE_MSG("unknown pointer encoding");
}
// Then add relative offset
switch (encoding & 0x70) {
case DW_EH_PE_absptr:
// do nothing
break;
case DW_EH_PE_pcrel:
result += startAddr;
break;
case DW_EH_PE_textrel:
UNREACHABLE_MSG("DW_EH_PE_textrel pointer encoding not supported");
break;
case DW_EH_PE_datarel:
// DW_EH_PE_datarel is only valid in a few places, so the parameter has a
// default value of 0, and we abort in the event that someone calls this
// function with a datarelBase of 0 and DW_EH_PE_datarel encoding.
if (datarelBase == 0)
UNREACHABLE_MSG("DW_EH_PE_datarel is invalid with a datarelBase of 0");
result += datarelBase;
break;
case DW_EH_PE_funcrel:
UNREACHABLE_MSG("DW_EH_PE_funcrel pointer encoding not supported");
break;
case DW_EH_PE_aligned:
UNREACHABLE_MSG("DW_EH_PE_aligned pointer encoding not supported");
break;
default:
UNREACHABLE_MSG("unknown pointer encoding");
break;
}
if (encoding & DW_EH_PE_indirect) {
result = get<uintptr_t>(result);
}
return result;
}
bool DecodeEHHdr(uintptr_t ehHdrStart, uintptr_t ehHdrEnd, EHHeaderInfo& ehHdrInfo) {
auto p = ehHdrStart;
// Ensure that we don't read data beyond the end of .eh_frame_hdr
if (ehHdrEnd - ehHdrStart < 4) {
// Don't print a message for an empty .eh_frame_hdr (this can happen if
// the linker script defines symbols for it even in the empty case).
if (ehHdrEnd == ehHdrStart) {
return false;
}
LOG_ERROR(Core_Linker, "Unsupported .eh_frame_hdr at {:#x} "
"need at least 4 bytes of data but only got {:#x}",
ehHdrStart, ehHdrEnd - ehHdrStart);
return false;
}
const u8 version = get<u8>(p++);
if (version != 1) {
LOG_CRITICAL(Core_Linker, "Unsupported .eh_frame_hdr version: {:#x} at {:#x}",
version, ehHdrStart);
return false;
}
const u8 eh_frame_ptr_enc = get<u8>(p++);
const u8 fde_count_enc = get<u8>(p++);
ehHdrInfo.table_enc = get<u8>(p++);
ehHdrInfo.eh_frame_ptr = getEncodedP(p, ehHdrEnd, eh_frame_ptr_enc, ehHdrStart);
ehHdrInfo.fde_count =
fde_count_enc == DW_EH_PE_omit
? 0
: getEncodedP(p, ehHdrEnd, fde_count_enc, ehHdrStart);
ehHdrInfo.table = p;
return true;
}
} // namespace Dwarf