Fixed the backtrace wrapper (uses eh_frame information only)

This commit is contained in:
rajdakin 2022-02-14 13:13:12 +01:00
parent 97857d55a0
commit 77925f264f
12 changed files with 917 additions and 72 deletions

View File

@ -21,12 +21,12 @@ option(NOGIT "Set to ON if not building from a git clone repo (like when buildin
if(LARCH64) if(LARCH64)
set(LD80BITS OFF CACHE BOOL "") set(LD80BITS OFF CACHE BOOL "")
set(NOALIGN OFF CACHE BOOL "") set(NOALIGN OFF CACHE BOOL "")
set(ARM_DYNAREC OFF CACHE BOOL "") set(ARM_DYNAREC OFF CACHE BOOL "")
endif() endif()
if(PPC64LE) if(PPC64LE)
set(LD80BITS OFF CACHE BOOL "") set(LD80BITS OFF CACHE BOOL "")
set(NOALIGN OFF CACHE BOOL "") set(NOALIGN OFF CACHE BOOL "")
set(ARM_DYNAREC OFF CACHE BOOL "") set(ARM_DYNAREC OFF CACHE BOOL "")
endif() endif()
if(RK3399 OR RPI4ARM64 OR RK3326 OR TEGRAX1 OR PHYTIUM OR SD845 OR LX2160A) if(RK3399 OR RPI4ARM64 OR RK3326 OR TEGRAX1 OR PHYTIUM OR SD845 OR LX2160A)
set(LD80BITS OFF CACHE BOOL "") set(LD80BITS OFF CACHE BOOL "")
@ -166,6 +166,7 @@ set(ELFLOADER_SRC
"${BOX64_ROOT}/src/build_info.c" "${BOX64_ROOT}/src/build_info.c"
"${BOX64_ROOT}/src/custommem.c" "${BOX64_ROOT}/src/custommem.c"
"${BOX64_ROOT}/src/dynarec/dynarec.c" "${BOX64_ROOT}/src/dynarec/dynarec.c"
"${BOX64_ROOT}/src/elfs/elfdwarf_private.c"
"${BOX64_ROOT}/src/elfs/elfloader.c" "${BOX64_ROOT}/src/elfs/elfloader.c"
"${BOX64_ROOT}/src/elfs/elfparser.c" "${BOX64_ROOT}/src/elfs/elfparser.c"
"${BOX64_ROOT}/src/elfs/elfload_dump.c" "${BOX64_ROOT}/src/elfs/elfload_dump.c"
@ -572,96 +573,101 @@ endif()
set(CPACK_DEBIAN_FILE_NAME "${BOX64}-${BOX64_MAJOR}.${BOX64_MINOR}.${BOX64_REVISION}_Linux-${CPACK_DEBIAN_PACKAGE_ARCHITECTURE}.deb") set(CPACK_DEBIAN_FILE_NAME "${BOX64}-${BOX64_MAJOR}.${BOX64_MINOR}.${BOX64_REVISION}_Linux-${CPACK_DEBIAN_PACKAGE_ARCHITECTURE}.deb")
INCLUDE(CPack) INCLUDE(CPack)
add_test(test01 ${CMAKE_COMMAND} -D TEST_PROGRAM=${CMAKE_BINARY_DIR}/${BOX64} add_test(test01 ${CMAKE_COMMAND} -D TEST_PROGRAM=${CMAKE_BINARY_DIR}/${BOX64}
-D TEST_ARGS=${CMAKE_SOURCE_DIR}/tests/test01 -D TEST_OUTPUT=tmpfile01.txt -D TEST_ARGS=${CMAKE_SOURCE_DIR}/tests/test01 -D TEST_OUTPUT=tmpfile01.txt
-D TEST_REFERENCE=${CMAKE_SOURCE_DIR}/tests/ref01.txt -D TEST_REFERENCE=${CMAKE_SOURCE_DIR}/tests/ref01.txt
-P ${CMAKE_SOURCE_DIR}/runTest.cmake ) -P ${CMAKE_SOURCE_DIR}/runTest.cmake )
add_test(test02 ${CMAKE_COMMAND} -D TEST_PROGRAM=${CMAKE_BINARY_DIR}/${BOX64} add_test(test02 ${CMAKE_COMMAND} -D TEST_PROGRAM=${CMAKE_BINARY_DIR}/${BOX64}
-D TEST_ARGS=${CMAKE_SOURCE_DIR}/tests/test02 -D TEST_OUTPUT=tmpfile02.txt -D TEST_ARGS=${CMAKE_SOURCE_DIR}/tests/test02 -D TEST_OUTPUT=tmpfile02.txt
-D TEST_REFERENCE=${CMAKE_SOURCE_DIR}/tests/ref02.txt -D TEST_REFERENCE=${CMAKE_SOURCE_DIR}/tests/ref02.txt
-P ${CMAKE_SOURCE_DIR}/runTest.cmake ) -P ${CMAKE_SOURCE_DIR}/runTest.cmake )
add_test(test03 ${CMAKE_COMMAND} -D TEST_PROGRAM=${CMAKE_BINARY_DIR}/${BOX64} add_test(test03 ${CMAKE_COMMAND} -D TEST_PROGRAM=${CMAKE_BINARY_DIR}/${BOX64}
-D TEST_ARGS=${CMAKE_SOURCE_DIR}/tests/test03 -D TEST_OUTPUT=tmpfile03.txt -D TEST_ARGS=${CMAKE_SOURCE_DIR}/tests/test03 -D TEST_OUTPUT=tmpfile03.txt
-D TEST_REFERENCE=${CMAKE_SOURCE_DIR}/tests/ref03.txt -D TEST_REFERENCE=${CMAKE_SOURCE_DIR}/tests/ref03.txt
-P ${CMAKE_SOURCE_DIR}/runTest.cmake ) -P ${CMAKE_SOURCE_DIR}/runTest.cmake )
add_test(test04 ${CMAKE_COMMAND} -D TEST_PROGRAM=${CMAKE_BINARY_DIR}/${BOX64} add_test(test04 ${CMAKE_COMMAND} -D TEST_PROGRAM=${CMAKE_BINARY_DIR}/${BOX64}
-D TEST_ARGS=${CMAKE_SOURCE_DIR}/tests/test04 -D TEST_ARGS2=yeah -D TEST_OUTPUT=tmpfile04.txt -D TEST_ARGS=${CMAKE_SOURCE_DIR}/tests/test04 -D TEST_ARGS2=yeah -D TEST_OUTPUT=tmpfile04.txt
-D TEST_REFERENCE=${CMAKE_SOURCE_DIR}/tests/ref04.txt -D TEST_REFERENCE=${CMAKE_SOURCE_DIR}/tests/ref04.txt
-P ${CMAKE_SOURCE_DIR}/runTest.cmake ) -P ${CMAKE_SOURCE_DIR}/runTest.cmake )
add_test(test05 ${CMAKE_COMMAND} -D TEST_PROGRAM=${CMAKE_BINARY_DIR}/${BOX64} add_test(test05 ${CMAKE_COMMAND} -D TEST_PROGRAM=${CMAKE_BINARY_DIR}/${BOX64}
-D TEST_ARGS=${CMAKE_SOURCE_DIR}/tests/test05 -D TEST_ARGS2=7 -D TEST_OUTPUT=tmpfile05.txt -D TEST_ARGS=${CMAKE_SOURCE_DIR}/tests/test05 -D TEST_ARGS2=7 -D TEST_OUTPUT=tmpfile05.txt
-D TEST_REFERENCE=${CMAKE_SOURCE_DIR}/tests/ref05.txt -D TEST_REFERENCE=${CMAKE_SOURCE_DIR}/tests/ref05.txt
-P ${CMAKE_SOURCE_DIR}/runTest.cmake ) -P ${CMAKE_SOURCE_DIR}/runTest.cmake )
add_test(test06 ${CMAKE_COMMAND} -D TEST_PROGRAM=${CMAKE_BINARY_DIR}/${BOX64} add_test(test06 ${CMAKE_COMMAND} -D TEST_PROGRAM=${CMAKE_BINARY_DIR}/${BOX64}
-D TEST_ARGS=${CMAKE_SOURCE_DIR}/tests/test06 -D TEST_OUTPUT=tmpfile06.txt -D TEST_ARGS=${CMAKE_SOURCE_DIR}/tests/test06 -D TEST_OUTPUT=tmpfile06.txt
-D TEST_REFERENCE=${CMAKE_SOURCE_DIR}/tests/ref06.txt -D TEST_REFERENCE=${CMAKE_SOURCE_DIR}/tests/ref06.txt
-P ${CMAKE_SOURCE_DIR}/runTest.cmake ) -P ${CMAKE_SOURCE_DIR}/runTest.cmake )
add_test(test07 ${CMAKE_COMMAND} -D TEST_PROGRAM=${CMAKE_BINARY_DIR}/${BOX64} add_test(test07 ${CMAKE_COMMAND} -D TEST_PROGRAM=${CMAKE_BINARY_DIR}/${BOX64}
-D TEST_ARGS=${CMAKE_SOURCE_DIR}/tests/test07 -D TEST_OUTPUT=tmpfile07.txt -D TEST_ARGS=${CMAKE_SOURCE_DIR}/tests/test07 -D TEST_OUTPUT=tmpfile07.txt
-D TEST_REFERENCE=${CMAKE_SOURCE_DIR}/tests/ref07.txt -D TEST_REFERENCE=${CMAKE_SOURCE_DIR}/tests/ref07.txt
-P ${CMAKE_SOURCE_DIR}/runTest.cmake ) -P ${CMAKE_SOURCE_DIR}/runTest.cmake )
add_test(test08 ${CMAKE_COMMAND} -D TEST_PROGRAM=${CMAKE_BINARY_DIR}/${BOX64} add_test(test08 ${CMAKE_COMMAND} -D TEST_PROGRAM=${CMAKE_BINARY_DIR}/${BOX64}
-D TEST_ARGS=${CMAKE_SOURCE_DIR}/tests/test08 -D TEST_OUTPUT=tmpfile08.txt -D TEST_ARGS=${CMAKE_SOURCE_DIR}/tests/test08 -D TEST_OUTPUT=tmpfile08.txt
-D TEST_REFERENCE=${CMAKE_SOURCE_DIR}/tests/ref08.txt -D TEST_REFERENCE=${CMAKE_SOURCE_DIR}/tests/ref08.txt
-P ${CMAKE_SOURCE_DIR}/runTest.cmake ) -P ${CMAKE_SOURCE_DIR}/runTest.cmake )
add_test(test09 ${CMAKE_COMMAND} -D TEST_PROGRAM=${CMAKE_BINARY_DIR}/${BOX64} add_test(test09 ${CMAKE_COMMAND} -D TEST_PROGRAM=${CMAKE_BINARY_DIR}/${BOX64}
-D TEST_ARGS=${CMAKE_SOURCE_DIR}/tests/test09 -D TEST_OUTPUT=tmpfile09.txt -D TEST_ARGS=${CMAKE_SOURCE_DIR}/tests/test09 -D TEST_OUTPUT=tmpfile09.txt
-D TEST_REFERENCE=${CMAKE_SOURCE_DIR}/tests/ref09.txt -D TEST_REFERENCE=${CMAKE_SOURCE_DIR}/tests/ref09.txt
-P ${CMAKE_SOURCE_DIR}/runTest.cmake ) -P ${CMAKE_SOURCE_DIR}/runTest.cmake )
add_test(test10 ${CMAKE_COMMAND} -D TEST_PROGRAM=${CMAKE_BINARY_DIR}/${BOX64} add_test(test10 ${CMAKE_COMMAND} -D TEST_PROGRAM=${CMAKE_BINARY_DIR}/${BOX64}
-D TEST_ARGS=${CMAKE_SOURCE_DIR}/tests/test10 -D TEST_OUTPUT=tmpfile10.txt -D TEST_ARGS=${CMAKE_SOURCE_DIR}/tests/test10 -D TEST_OUTPUT=tmpfile10.txt
-D TEST_REFERENCE=${CMAKE_SOURCE_DIR}/tests/ref10.txt -D TEST_REFERENCE=${CMAKE_SOURCE_DIR}/tests/ref10.txt
-P ${CMAKE_SOURCE_DIR}/runTest.cmake ) -P ${CMAKE_SOURCE_DIR}/runTest.cmake )
add_test(test11 ${CMAKE_COMMAND} -D TEST_PROGRAM=${CMAKE_BINARY_DIR}/${BOX64} add_test(test11 ${CMAKE_COMMAND} -D TEST_PROGRAM=${CMAKE_BINARY_DIR}/${BOX64}
-D TEST_ARGS=${CMAKE_SOURCE_DIR}/tests/test11 -D TEST_OUTPUT=tmpfile11.txt -D TEST_ARGS=${CMAKE_SOURCE_DIR}/tests/test11 -D TEST_OUTPUT=tmpfile11.txt
-D TEST_REFERENCE=${CMAKE_SOURCE_DIR}/tests/ref11.txt -D TEST_REFERENCE=${CMAKE_SOURCE_DIR}/tests/ref11.txt
-P ${CMAKE_SOURCE_DIR}/runTest.cmake ) -P ${CMAKE_SOURCE_DIR}/runTest.cmake )
add_test(test12 ${CMAKE_COMMAND} -D TEST_PROGRAM=${CMAKE_BINARY_DIR}/${BOX64} add_test(test12 ${CMAKE_COMMAND} -D TEST_PROGRAM=${CMAKE_BINARY_DIR}/${BOX64}
-D TEST_ARGS=${CMAKE_SOURCE_DIR}/tests/test12 -D TEST_OUTPUT=tmpfile12.txt -D TEST_ARGS=${CMAKE_SOURCE_DIR}/tests/test12 -D TEST_OUTPUT=tmpfile12.txt
-D TEST_REFERENCE=${CMAKE_SOURCE_DIR}/tests/ref12.txt -D TEST_REFERENCE=${CMAKE_SOURCE_DIR}/tests/ref12.txt
-P ${CMAKE_SOURCE_DIR}/runTest.cmake ) -P ${CMAKE_SOURCE_DIR}/runTest.cmake )
add_test(test13 ${CMAKE_COMMAND} -D TEST_PROGRAM=${CMAKE_BINARY_DIR}/${BOX64} add_test(test13 ${CMAKE_COMMAND} -D TEST_PROGRAM=${CMAKE_BINARY_DIR}/${BOX64}
-D TEST_ARGS=${CMAKE_SOURCE_DIR}/tests/test13 -D TEST_OUTPUT=tmpfile13.txt -D TEST_ARGS=${CMAKE_SOURCE_DIR}/tests/test13 -D TEST_OUTPUT=tmpfile13.txt
-D TEST_REFERENCE=${CMAKE_SOURCE_DIR}/tests/ref13.txt -D TEST_REFERENCE=${CMAKE_SOURCE_DIR}/tests/ref13.txt
-P ${CMAKE_SOURCE_DIR}/runTest.cmake ) -P ${CMAKE_SOURCE_DIR}/runTest.cmake )
#add_test(test14 ${CMAKE_COMMAND} -D TEST_PROGRAM=${CMAKE_BINARY_DIR}/${BOX64} #add_test(test14 ${CMAKE_COMMAND} -D TEST_PROGRAM=${CMAKE_BINARY_DIR}/${BOX64}
# -D TEST_ARGS=${CMAKE_SOURCE_DIR}/tests/test14 -D TEST_OUTPUT=tmpfile14.txt # -D TEST_ARGS=${CMAKE_SOURCE_DIR}/tests/test14 -D TEST_OUTPUT=tmpfile14.txt
# -D TEST_REFERENCE=${CMAKE_SOURCE_DIR}/tests/ref14.txt # -D TEST_REFERENCE=${CMAKE_SOURCE_DIR}/tests/ref14.txt
# -P ${CMAKE_SOURCE_DIR}/runTest.cmake ) # -P ${CMAKE_SOURCE_DIR}/runTest.cmake )
add_test(test15 ${CMAKE_COMMAND} -D TEST_PROGRAM=${CMAKE_BINARY_DIR}/${BOX64} add_test(test15 ${CMAKE_COMMAND} -D TEST_PROGRAM=${CMAKE_BINARY_DIR}/${BOX64}
-D TEST_ARGS=${CMAKE_SOURCE_DIR}/tests/test15 -D TEST_OUTPUT=tmpfile15.txt -D TEST_ARGS=${CMAKE_SOURCE_DIR}/tests/test15 -D TEST_OUTPUT=tmpfile15.txt
-D TEST_REFERENCE=${CMAKE_SOURCE_DIR}/tests/ref15.txt -D TEST_REFERENCE=${CMAKE_SOURCE_DIR}/tests/ref15.txt
-P ${CMAKE_SOURCE_DIR}/runTest.cmake ) -P ${CMAKE_SOURCE_DIR}/runTest.cmake )
add_test(test16 ${CMAKE_COMMAND} -D TEST_PROGRAM=${CMAKE_BINARY_DIR}/${BOX64} add_test(test16 ${CMAKE_COMMAND} -D TEST_PROGRAM=${CMAKE_BINARY_DIR}/${BOX64}
-D TEST_ARGS=${CMAKE_SOURCE_DIR}/tests/test16 -D TEST_OUTPUT=tmpfile16.txt -D TEST_ARGS=${CMAKE_SOURCE_DIR}/tests/test16 -D TEST_OUTPUT=tmpfile16.txt
-D TEST_REFERENCE=${CMAKE_SOURCE_DIR}/tests/ref16.txt -D TEST_REFERENCE=${CMAKE_SOURCE_DIR}/tests/ref16.txt
-P ${CMAKE_SOURCE_DIR}/runTest.cmake ) -P ${CMAKE_SOURCE_DIR}/runTest.cmake )
add_test(test17 ${CMAKE_COMMAND} -D TEST_PROGRAM=${CMAKE_BINARY_DIR}/${BOX64} add_test(test17 ${CMAKE_COMMAND} -D TEST_PROGRAM=${CMAKE_BINARY_DIR}/${BOX64}
-D TEST_ARGS=${CMAKE_SOURCE_DIR}/tests/test17 -D TEST_OUTPUT=tmpfile17.txt -D TEST_ARGS=${CMAKE_SOURCE_DIR}/tests/test17 -D TEST_OUTPUT=tmpfile17.txt
-D TEST_REFERENCE=${CMAKE_SOURCE_DIR}/tests/ref17.txt -D TEST_REFERENCE=${CMAKE_SOURCE_DIR}/tests/ref17.txt
-P ${CMAKE_SOURCE_DIR}/runTest.cmake ) -P ${CMAKE_SOURCE_DIR}/runTest.cmake )
add_test(aes ${CMAKE_COMMAND} -D TEST_PROGRAM=${CMAKE_BINARY_DIR}/${BOX64} add_test(aes ${CMAKE_COMMAND} -D TEST_PROGRAM=${CMAKE_BINARY_DIR}/${BOX64}
-D TEST_ARGS=${CMAKE_SOURCE_DIR}/tests/test18 -D TEST_OUTPUT=tmpfile18.txt -D TEST_ARGS=${CMAKE_SOURCE_DIR}/tests/test18 -D TEST_OUTPUT=tmpfile18.txt
-D TEST_REFERENCE=${CMAKE_SOURCE_DIR}/tests/ref18.txt -D TEST_REFERENCE=${CMAKE_SOURCE_DIR}/tests/ref18.txt
-P ${CMAKE_SOURCE_DIR}/runTest.cmake ) -P ${CMAKE_SOURCE_DIR}/runTest.cmake )
add_test(backtrace ${CMAKE_COMMAND} -D TEST_PROGRAM=${CMAKE_BINARY_DIR}/${BOX64}
-D TEST_ARGS=${CMAKE_SOURCE_DIR}/tests/test19 -D TEST_OUTPUT=tmpfile19.txt
-D TEST_REFERENCE=${CMAKE_SOURCE_DIR}/tests/ref19.txt
-P ${CMAKE_SOURCE_DIR}/runTest.cmake )
file(GLOB extension_tests "${CMAKE_SOURCE_DIR}/tests/extensions/*.c") file(GLOB extension_tests "${CMAKE_SOURCE_DIR}/tests/extensions/*.c")
foreach(file ${extension_tests}) foreach(file ${extension_tests})
get_filename_component(testname "${file}" NAME_WE) get_filename_component(testname "${file}" NAME_WE)

View File

@ -111,6 +111,8 @@ void DynaCall(x64emu_t* emu, uintptr_t addr)
uint64_t old_rsi = R_RSI; uint64_t old_rsi = R_RSI;
uint64_t old_rbp = R_RBP; uint64_t old_rbp = R_RBP;
uint64_t old_rip = R_RIP; uint64_t old_rip = R_RIP;
Push64(emu, GetRBP(emu)); // set frame pointer
SetRBP(emu, GetRSP(emu)); // save RSP
PushExit(emu); PushExit(emu);
R_RIP = addr; R_RIP = addr;
emu->df = d_none; emu->df = d_none;

717
src/elfs/elfdwarf_private.c Normal file
View File

@ -0,0 +1,717 @@
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include "debug.h"
#include "elfloader_private.h"
#include "elfdwarf_private.h"
typedef struct dwarf_unwind_constr_s {
uint8_t reg_count;
uint64_t *table;
uint8_t *statuses;
#define GET_STATUS(n,i) (((n).statuses[(i) >> 1] >> (((i) & 1) << 2)) & 0xF)
#define SET_STATUS(n,i,v) (n).statuses[(i) >> 1] = ((n).statuses[(i) >> 1] & (0xF0 >> (((i) & 1) << 2))) | (v << (((i) & 1) << 2))
#define REGSTATUS_undefined 0b0000
#define REGSTATUS_same_val 0b0101
#define REGSTATUS_offset 0b0010
#define REGSTATUS_val_offset 0b0011
#define REGSTATUS_register 0b0100
} dwarf_unwind_constr_t;
#define DW_EH_PE_uptr 0x00
#define DW_EH_PE_uleb128 0x01
#define DW_EH_PE_udata2 0x02
#define DW_EH_PE_udata4 0x03
#define DW_EH_PE_udata8 0x04
#define DW_EH_PE_SIZE_MASK 0x07
#define DW_EH_PE_signed 0x08
#define DW_EH_PE_absptr 0x00
#define DW_EH_PE_pcrel 0x10
#define DW_EH_PE_textrel 0x20 // Unsupported
#define DW_EH_PE_datarel 0x30 // Unsupported
#define DW_EH_PE_indirect 0x80 // May crash
#define DW_EH_PE_omit 0xff
#define READ_U1(var, ptr) do { (var) = *( uint8_t*)(ptr); (ptr) += 1; } while (0)
#define READ_S1(var, ptr) do { (var) = *( int8_t*)(ptr); (ptr) += 1; } while (0)
#define READ_U2(var, ptr) do { (var) = *(uint16_t*)(ptr); (ptr) += 2; } while (0)
#define READ_S2(var, ptr) do { (var) = *( int16_t*)(ptr); (ptr) += 2; } while (0)
#define READ_U4(var, ptr) do { (var) = *(uint32_t*)(ptr); (ptr) += 4; } while (0)
#define READ_S4(var, ptr) do { (var) = *( int32_t*)(ptr); (ptr) += 4; } while (0)
#define READ_U8(var, ptr) do { (var) = *(uint64_t*)(ptr); (ptr) += 8; } while (0)
#define READ_S8(var, ptr) do { (var) = *( int64_t*)(ptr); (ptr) += 8; } while (0)
#define READ_ULEB128(var, ptr) readULEB(&(var), &(ptr))
#define READ_SLEB128(var, ptr) readSLEB((int64_t*)&(var), &(ptr))
// Seems like DW_EH_PE_signed is actually an invalid encoding
#define READ_ENCODED(var, ptr, enc, isptr) do { uintptr_t optr = (uintptr_t)(ptr); \
if ((((enc) & DW_EH_PE_SIZE_MASK) == DW_EH_PE_uleb128) && ((enc) & DW_EH_PE_signed)) READ_SLEB128(var, ptr); \
else if ((((enc) & DW_EH_PE_SIZE_MASK) == DW_EH_PE_uleb128) && !((enc) & DW_EH_PE_signed)) READ_ULEB128(var, ptr); \
else if ((((enc) & DW_EH_PE_SIZE_MASK) == DW_EH_PE_udata2 ) && ((enc) & DW_EH_PE_signed)) READ_S2 (var, ptr); \
else if ((((enc) & DW_EH_PE_SIZE_MASK) == DW_EH_PE_udata2 ) && !((enc) & DW_EH_PE_signed)) READ_U2 (var, ptr); \
else if ((((enc) & DW_EH_PE_SIZE_MASK) == DW_EH_PE_udata4 ) && ((enc) & DW_EH_PE_signed)) READ_S4 (var, ptr); \
else if ((((enc) & DW_EH_PE_SIZE_MASK) == DW_EH_PE_udata4 ) && !((enc) & DW_EH_PE_signed)) READ_U4 (var, ptr); \
else if ((((enc) & DW_EH_PE_SIZE_MASK) == DW_EH_PE_udata8 ) && ((enc) & DW_EH_PE_signed)) READ_S8 (var, ptr); \
else if ((((enc) & DW_EH_PE_SIZE_MASK) == DW_EH_PE_udata8 ) && !((enc) & DW_EH_PE_signed)) READ_U8 (var, ptr); \
else if ((((enc) & DW_EH_PE_SIZE_MASK) == DW_EH_PE_uptr ) && ((enc) & DW_EH_PE_signed)) READ_S8 (var, ptr); \
else if ((((enc) & DW_EH_PE_SIZE_MASK) == DW_EH_PE_uptr ) && !((enc) & DW_EH_PE_signed)) READ_U8 (var, ptr); \
else { printf_log(LOG_DEBUG, "Invalid encoding 0x%02X\n", (enc)); (var) = 0; } \
if (((enc) & DW_EH_PE_pcrel) && isptr) (var) += optr; \
if ((enc) & DW_EH_PE_indirect) (var) = *(uint64_t*)(var); \
} while (0)
#define SKIP_1(ptr) (ptr) += 1
#define SKIP_2(ptr) (ptr) += 2
#define SKIP_4(ptr) (ptr) += 4
#define SKIP_8(ptr) (ptr) += 8
#define SKIP_LEB128(ptr) do { ++(ptr); } while (((*(((unsigned char*)(ptr))-1))) & (1 << 7))
#define SKIP_ENCODED(ptr, enc) \
do { if (((enc) & DW_EH_PE_SIZE_MASK) == DW_EH_PE_uleb128) SKIP_LEB128(ptr); \
else if (((enc) & DW_EH_PE_SIZE_MASK) == DW_EH_PE_udata2 ) SKIP_2 (ptr); \
else if (((enc) & DW_EH_PE_SIZE_MASK) == DW_EH_PE_udata4 ) SKIP_4 (ptr); \
else if (((enc) & DW_EH_PE_SIZE_MASK) == DW_EH_PE_udata8 ) SKIP_8 (ptr); \
else if (((enc) & DW_EH_PE_SIZE_MASK) == DW_EH_PE_uptr ) SKIP_8 (ptr); \
else printf_log(LOG_DEBUG, "Invalid encoding 0x%02X\n", (enc)); \
} while (0)
// LEB128 wil only work on 64 bits numbers (unsigned long, like the Linux kernel)
void readULEB(uint64_t *var, unsigned char **ptr) {
*var = 0;
int shift = 0;
while (1) {
unsigned char byte = **ptr; ++*ptr;
*var |= ((byte & 0x7F) << shift);
if (!(byte & (1 << 7))) break;
shift += 7;
}
}
void readSLEB(int64_t *var, unsigned char **ptr) {
uint64_t tmp = 0;
int shift = 0;
unsigned char byte;
while (1) {
byte = **ptr; ++*ptr;
tmp |= ((byte & 0x7F) << shift);
if (!(byte & (1 << 7))) break;
shift += 7;
}
// Sign extend
if (byte & (1 << 6)) {
*(uint64_t*)var = tmp | (0xFFFFFFFFFFFFFFFFull << (shift + 7));
} else {
*var = tmp;
}
}
uintptr_t get_parent_registers(dwarf_unwind_t *unwind, const elfheader_t *ehdr, uintptr_t addr, char *success) {
if (!ehdr) {
*success = 0;
return 0;
}
unsigned char ehfh_version = *(unsigned char*)ehdr->ehframehdr;
if (ehfh_version != 1) {
*success = 0;
return 0;
}
// printf_log(LOG_NONE, "Address: 0x%016lX\nEH frame address: 0x%016lX\n", addr, ehdr->ehframe);
// Not using the binary search table (for now)
unsigned char *cur_addr = (unsigned char*)ehdr->ehframe;
unsigned char *end_addr = (unsigned char*)ehdr->ehframe_end;
#define AUG_EHDATA (1 << 0)
#define AUG_AUGDATA (1 << 1)
#define AUG_LSDA (1 << 2)
#define AUG_PARG (1 << 3)
#define AUG_FDEENC (1 << 4)
#define AUG_SIGHDLER (1 << 5)
unsigned short aug_fields = 0;
unsigned char lsda_encoding = DW_EH_PE_omit;
unsigned char fde_encoding = DW_EH_PE_omit;
uint64_t eh_data = 0; // Note: these are never reset, so use aug_fields & AUG_EHDATA
uint64_t code_alignment_factor = 0;
int64_t data_alignment_factor = 0;
uint64_t cie_aug_data_len = 0; // Note: these are never reset, so use aug_fields & AUG_AUGDATA
unsigned char *cie_aug_data = NULL; // Note: these are never reset, so use aug_fields & AUG_AUGDATA
unsigned char *initCIEinstr = NULL; // Never NULL if initialized
unsigned char *endCIEinstr = NULL; // Never NULL if initialized
uint64_t return_addr_reg = 0; // Description absent in the LSBCS 5.0, but still present
while (cur_addr < end_addr) {
// Length
uint64_t len; int extended = 0;
READ_U4(len, cur_addr);
if (!len) {
// Terminator, end parsing
*success = 0;
return 0;
} else if (len == 0xFFFFFFFF) {
// Extended Length (optional)
extended = 1;
READ_U8(len, cur_addr);
}
(void)extended;
// printf_log(LOG_NONE, "[???] Length %02X\n", len);
// printf_log(LOG_NONE, "[???] Extended %c\n", extended ? 'Y' : 'N');
unsigned char *next_addr = cur_addr + len;
// CIE ID (always 0) or CIE Pointer (never 0)
uint32_t cie_ptr;
READ_U4(cie_ptr, cur_addr);
if (cie_ptr == 0) {
// Current block is a CIE
// printf_log(LOG_NONE, "[CIE] ID %02X\n", cie_ptr);
// Version (always 1 or 3)
uint8_t cie_version;
READ_U1(cie_version, cur_addr);
// printf_log(LOG_NONE, "[CIE] Version %01X\n", cie_version);
if ((cie_version != 1) && (cie_version != 3)) { // Only 1 is in the LSBCS 5.0 but I found 3 exists as well
// Invalid CIE version
printf_log(LOG_DEBUG, "Invalid CIE version %d (should be 1 or 3), stopping parsing\n", cie_version);
// Failed to process CIE
*success = 0;
return 0;
}
// Augmentation String
char *aug_str = (char*)cur_addr; cur_addr += strlen(aug_str) + 1;
// printf_log(LOG_NONE, "[CIE] Augmentation st %s\n", aug_str);
aug_fields = (aug_str[0] == 'z') ? AUG_AUGDATA : 0;
for (char *aug_str2 = aug_str + ((aug_fields & AUG_AUGDATA) ? 1 : 0); *aug_str2; ++aug_str2) {
if ((aug_str2[0] == 'e') && (aug_str2[1] == 'h')) {
// Use has also been left untold in the Linux Standard Base Core Specification 3.0RC1
printf_log(LOG_DEBUG, "Warning: EH data detected but this was removed from the standard\n");
aug_fields |= AUG_EHDATA; ++aug_str2;
} else if (aug_fields & AUG_AUGDATA) {
if (aug_str2[0] == 'L') {
aug_fields |= AUG_LSDA;
} else if (aug_str2[0] == 'P') {
aug_fields |= AUG_PARG;
printf_log(LOG_DEBUG, "Warning: augmentation string attribute 'P' unsupported\n");
} else if (aug_str2[0] == 'S') {
aug_fields |= AUG_SIGHDLER;
printf_log(LOG_DEBUG, "Warning: augmentation string attribute 'S' ignored\n");
} else if (aug_str2[0] == 'R') {
aug_fields |= AUG_FDEENC;
} else {
printf_log(LOG_DEBUG, "Error: invalid augmentation string %s\n", aug_str);
// Failed to process augmentation string
break;
}
} else {
printf_log(LOG_DEBUG, "Error: invalid augmentation string %s\n", aug_str);
// Failed to process augmentation string
break;
}
}
// printf_log(LOG_NONE, "[CIE] Augmentation fs %02x\n", aug_fields);
// Code Alignment Factor
READ_ULEB128(code_alignment_factor, cur_addr);
// printf_log(LOG_NONE, "[CIE] Code AF %d\n", code_alignment_factor);
// Data Alignment Factor
READ_SLEB128(data_alignment_factor, cur_addr);
// printf_log(LOG_NONE, "[CIE] Data AF %d\n", data_alignment_factor);
if (cie_version == 1) {
READ_U1(return_addr_reg, cur_addr);
} else {
READ_ULEB128(return_addr_reg, cur_addr);
}
// printf_log(LOG_NONE, "[CIE] Return addr reg %01X\n", return_addr_reg);
lsda_encoding = DW_EH_PE_omit;
fde_encoding = DW_EH_PE_absptr;
if (aug_fields & AUG_AUGDATA) {
// Augmentation Data Length (optional)
READ_ULEB128(cie_aug_data_len, cur_addr);
// printf_log(LOG_NONE, "[CIE] Aug data len %01X\n", cie_aug_data_len);
// Augmentation Data (optional)
cie_aug_data = cur_addr; cur_addr += cie_aug_data_len;
char *cie_aug_data2 = (char*)cie_aug_data;
for (char *aug_str2 = aug_str + ((aug_fields & AUG_AUGDATA) ? 1 : 0); *aug_str2; ++aug_str2) {
if ((aug_str2[0] == 'e') && (aug_str2[1] == 'h')) {
break; // Unknown usage
} else if (aug_fields & AUG_AUGDATA) {
if (aug_str2[0] == 'L') {
READ_U1(lsda_encoding, cie_aug_data2);
if (lsda_encoding != DW_EH_PE_omit) printf_log(LOG_DEBUG, "Warning: LSDA unsupported\n");
} else if (aug_str2[0] == 'P') {
unsigned char pencoding;
READ_U1(pencoding, cie_aug_data2);
SKIP_ENCODED(cie_aug_data2, pencoding);
} else if (aug_str2[0] == 'S') {
} else if (aug_str2[0] == 'R') {
READ_U1(fde_encoding, cie_aug_data2);
// printf_log(LOG_NONE, "[CIE] FDE encoding %02X\n", fde_encoding);
if (fde_encoding == DW_EH_PE_omit) printf_log(LOG_DEBUG, "Error: FDE encoding set to 'omit'\n");
} else {
break;
}
} else {
break;
}
}
}
// Initial Instructions
initCIEinstr = cur_addr;
endCIEinstr = next_addr;
} else if (initCIEinstr) {
// Current block is a FDE
// printf_log(LOG_NONE, "[FDE] Ptr %02X\n", cie_ptr);
// PC Begin
uintptr_t pc_begin;
READ_ENCODED(pc_begin, cur_addr, fde_encoding, 1);
// printf_log(LOG_NONE, "[FDE] PC begin %016lX\n", pc_begin);
// PC Range
uint64_t pc_range;
READ_ENCODED(pc_range, cur_addr, fde_encoding, 0);
// printf_log(LOG_NONE, "[FDE] PC end %016lX\n", pc_begin + pc_range);
uint64_t aug_data_len;
unsigned char *aug_data;
if (aug_fields & AUG_AUGDATA) {
// Augmentation Data Length (optional)
READ_ULEB128(aug_data_len, cur_addr);
// printf_log(LOG_NONE, "[FDE] Aug data len %01X\n", aug_data_len);
// Augmentation Data (optional)
aug_data = cur_addr; cur_addr += aug_data_len;
(void)aug_data;
}
if (lsda_encoding != DW_EH_PE_omit) {
SKIP_ENCODED(cur_addr, lsda_encoding);
}
// Call Frame Instructions
if ((pc_begin <= addr) && (addr < pc_begin + pc_range)) {
// Corresponding Frame Descriptor Entry found!
#define setmax(a, b) a = (a < (b) ? (b) : a)
dwarf_unwind_constr_t unwind_constr;
unwind_constr.reg_count = return_addr_reg;
uint64_t maxstacksize = 0;
uint64_t curstacksize = 0;
unsigned char *cur_inst = initCIEinstr;
uint64_t nreg;
#define PARSE_INST \
switch (inst >> 6) { \
case 0b01: \
break; \
case 0b10: \
setmax(unwind_constr.reg_count, inst & 0x3F); \
SKIP_LEB128(cur_inst); \
break; \
case 0b11: \
setmax(unwind_constr.reg_count, inst & 0x3F); \
break; \
case 0b00: \
switch (inst & 0x3F) { \
case 0b000000: \
break; \
case 0b001010: \
++curstacksize; \
setmax(maxstacksize, curstacksize); \
break; \
case 0b001011: \
if (!curstacksize) { \
printf_log(LOG_DEBUG, "Negative stack size during CFI execution\n"); \
*success = 0; \
return 0; \
} \
--curstacksize; \
break; \
case 0b000010: \
SKIP_1(cur_inst); \
break; \
case 0b000011: \
SKIP_2(cur_inst); \
break; \
case 0b000100: \
SKIP_4(cur_inst); \
break; \
case 0b000001: \
SKIP_8(cur_inst); \
break; \
case 0b001110: \
case 0b010011: \
SKIP_LEB128(cur_inst); \
break; \
case 0b000110: \
case 0b000111: \
case 0b001000: \
case 0b001101: \
READ_ULEB128(nreg, cur_inst); \
setmax(unwind_constr.reg_count, nreg); \
break; \
case 0b000101: \
case 0b001100: \
case 0b010001: \
case 0b010010: \
case 0b010100: \
case 0b010101: \
READ_ULEB128(nreg, cur_inst); \
setmax(unwind_constr.reg_count, nreg); \
SKIP_LEB128(cur_inst); \
break; \
case 0b001001: \
READ_ULEB128(nreg, cur_inst); \
setmax(unwind_constr.reg_count, nreg); \
READ_ULEB128(nreg, cur_inst); \
setmax(unwind_constr.reg_count, nreg); \
break; \
default: \
/* Contains undefined, user and expression CFIs */ \
printf_log(LOG_DEBUG, "Unknown CFI 0x%02X\n", inst); \
*success = 0; \
return 0; \
} \
break; \
}
while (cur_inst < endCIEinstr) {
unsigned char inst; READ_U1(inst, cur_inst);
PARSE_INST
}
cur_inst = cur_addr;
while (cur_inst < next_addr) {
unsigned char inst; READ_U1(inst, cur_inst);
PARSE_INST
}
#undef PARSE_INST
uint64_t cfa_reg = -1;
unsigned char cfa_signed;
union { uint64_t uoff; int64_t soff; } cfa_offset = { .uoff = 0 };
++unwind_constr.reg_count;
size_t tablelen = unwind_constr.reg_count * sizeof(uint64_t);
size_t statuseslen = ((unwind_constr.reg_count+1) >> 1) * sizeof(uint8_t);
unwind_constr.table = (uint64_t*)malloc(tablelen);
unwind_constr.statuses = (uint8_t*)calloc((unwind_constr.reg_count+1) >> 1, sizeof(uint8_t));
// ~~undefined is 0: no initialization needed~~ still initialize the first 17 to same_val
for (int i = 0; (i < 17) && (i <= unwind_constr.reg_count); ++i) {
SET_STATUS(unwind_constr, i, REGSTATUS_same_val);
}
curstacksize = 0;
uint64_t **table_stack = (uint64_t**)malloc(maxstacksize * sizeof(uint64_t*));
for (uint64_t i = 0; i < maxstacksize; ++i) {
table_stack[i] = (uint64_t*)malloc(tablelen);
}
cur_inst = initCIEinstr;
uintptr_t cur_pointed_addr = pc_begin;
// Missing:
/* DW_CFA_def_cfa_expression 0 0x0f BLOCK */
/* DW_CFA_expression 0 0x10 ULEB128 register BLOCK */
/* DW_CFA_val_expression 0 0x16 ULEB128 BLOCK */
/* DW_CFA_lo_user 0 0x1c */
/* DW_CFA_GNU_args_size 0 0x2e ULEB128 argsize */
/* DW_CFA_GNU_negative_offset_extended 0 0x2f ULEB128 register ULEB128 offset (obsoleted by DW_CFA_offset_extended_sf) */
/* DW_CFA_hi_user 0 0x3f */
// Known "bug": DW_CFA_set_loc can go backwards, this is ignored
uint64_t tmpreg;
uint64_t tmpuval;
int64_t tmpsval;
#define PARSE_INST \
switch (inst >> 6) { \
case 0b01: /* DW_CFA_advance_loc 0x1 delta */ \
cur_pointed_addr += (inst & 0x3F) * code_alignment_factor; \
break; \
case 0b10: /* DW_CFA_offset 0x2 register ULEB128 offset */ \
READ_ULEB128(tmpuval, cur_inst); \
SET_STATUS(unwind_constr, inst & 0x3F, REGSTATUS_offset); \
unwind_constr.table[inst & 0x3F] = (int64_t)tmpuval * data_alignment_factor; \
break; \
case 0b11: /* DW_CFA_restore 0x3 register */ \
RESTORE_REG(inst & 0x3F) \
break; \
case 0b00: \
switch (inst & 0x3F) { \
case 0b000001: /* DW_CFA_set_loc 0 0x01 address */ \
READ_U8(cur_pointed_addr, cur_inst); \
break; \
case 0b000010: /* DW_CFA_advance_loc1 0 0x02 1-byte delta */ \
READ_U1(tmpuval, cur_inst); \
cur_pointed_addr += tmpuval * code_alignment_factor; \
break; \
case 0b000011: /* DW_CFA_advance_loc2 0 0x03 2-byte delta */ \
READ_U2(tmpuval, cur_inst); \
cur_pointed_addr += tmpuval * code_alignment_factor; \
break; \
case 0b000100: /* DW_CFA_advance_loc4 0 0x04 4-byte delta */ \
READ_U4(tmpuval, cur_inst); \
cur_pointed_addr += tmpuval * code_alignment_factor; \
break; \
\
case 0b001100: /* DW_CFA_def_cfa 0 0x0c ULEB128 register ULEB128 offset */ \
READ_ULEB128(cfa_reg, cur_inst); \
cfa_signed = 0; \
READ_ULEB128(cfa_offset.uoff, cur_inst); \
break; \
case 0b010010: /* DW_CFA_def_cfa_sf 0 0x12 ULEB128 register SLEB128 offset */ \
READ_ULEB128(cfa_reg, cur_inst); \
cfa_signed = 1; \
READ_SLEB128(cfa_offset.soff, cur_inst); \
cfa_offset.soff *= data_alignment_factor; \
break; \
case 0b001101: /* DW_CFA_def_cfa_register 0 0x0d ULEB128 register */ \
READ_ULEB128(cfa_reg, cur_inst); \
break; \
case 0b001110: /* DW_CFA_def_cfa_offset 0 0x0e ULEB128 offset */ \
cfa_signed = 0; \
READ_ULEB128(cfa_offset.uoff, cur_inst); \
break; \
case 0b010011: /* DW_CFA_def_cfa_offset_sf 0 0x13 SLEB128 offset */ \
READ_SLEB128(cfa_offset.soff, cur_inst); \
cfa_offset.soff *= data_alignment_factor; \
break; \
/* DW_CFA_def_cfa_expression */ \
\
case 0b000111: /* DW_CFA_undefined 0 0x07 ULEB128 register */ \
READ_ULEB128(tmpreg, cur_inst); \
SET_STATUS(unwind_constr, tmpreg, REGSTATUS_undefined); \
break; \
case 0b001000: /* DW_CFA_same_value 0 0x08 ULEB128 register */ \
READ_ULEB128(tmpreg, cur_inst); \
SET_STATUS(unwind_constr, tmpreg, REGSTATUS_same_val); \
break; \
case 0b000101: /* DW_CFA_offset_extended 0 0x05 ULEB128 register ULEB128 offset */ \
READ_ULEB128(tmpreg, cur_inst); \
READ_ULEB128(tmpuval, cur_inst); \
SET_STATUS(unwind_constr, tmpreg, REGSTATUS_offset); \
unwind_constr.table[tmpreg] = (int64_t)tmpuval * data_alignment_factor; \
break; \
case 0b010001: /* DW_CFA_offset_extended_sf 0 0x11 ULEB128 register SLEB128 offset */ \
READ_ULEB128(tmpreg, cur_inst); \
READ_SLEB128(tmpsval, cur_inst); \
SET_STATUS(unwind_constr, tmpreg, REGSTATUS_offset); \
unwind_constr.table[tmpreg] = tmpsval * data_alignment_factor; \
break; \
case 0b010100: /* DW_CFA_val_offset 0 0x14 ULEB128 register ULEB128 offset */ \
READ_ULEB128(tmpreg, cur_inst); \
READ_ULEB128(tmpuval, cur_inst); \
SET_STATUS(unwind_constr, tmpreg, REGSTATUS_val_offset); \
unwind_constr.table[tmpreg] = (int64_t)tmpuval * data_alignment_factor; \
break; \
case 0b010101: /* DW_CFA_val_offset_sf 0 0x15 ULEB128 register SLEB128 offset */ \
READ_ULEB128(tmpreg, cur_inst); \
READ_SLEB128(tmpsval, cur_inst); \
SET_STATUS(unwind_constr, tmpreg, REGSTATUS_val_offset); \
unwind_constr.table[tmpreg] = tmpsval * data_alignment_factor; \
break; \
case 0b001001: /* DW_CFA_register 0 0x09 ULEB128 register ULEB128 register */ \
READ_ULEB128(tmpreg, cur_inst); \
READ_ULEB128(tmpuval, cur_inst); \
SET_STATUS(unwind_constr, tmpreg, REGSTATUS_register); \
unwind_constr.table[tmpreg] = tmpuval; \
break; \
/* DW_CFA_expression */ \
/* DW_CFA_val_expression */ \
case 0b000110: /* DW_CFA_restore_extended 0 0x06 ULEB128 register */ \
READ_ULEB128(tmpreg, cur_inst); \
RESTORE_REG(tmpreg) \
break; \
\
case 0b001010: /* DW_CFA_remember_state 0 0x0a */ \
memcpy(table_stack[curstacksize], unwind_constr.table, tablelen); \
++curstacksize; \
break; \
case 0b001011: /* DW_CFA_restore_state 0 0x0b */ \
--curstacksize; \
memcpy(unwind_constr.table, table_stack[curstacksize], tablelen); \
break; \
\
case 0b000000: /* DW_CFA_nop 0 0 */ \
break; \
default: \
/* Contains undefined, user and expression CFIs */ \
printf_log(LOG_DEBUG, "Unknown CFI 0x%02X\n", inst); \
FAILED \
} \
break; \
}
#define RESTORE_REG(reg) \
printf_log(LOG_DEBUG, "Trying to restore register 0x%02lX while in the intial CFIs\n", (uint64_t)reg); \
FAILED
#define FAILED \
free(unwind_constr.statuses); \
free(unwind_constr.table); \
for (uint64_t i = 0; i < maxstacksize; ++i) free(table_stack[i]); \
free(table_stack); \
*success = 0; return 0;
while (cur_inst < endCIEinstr) {
unsigned char inst; READ_U1(inst, cur_inst);
// printf_log(LOG_NONE, "Executing pre 0x%02X\n", inst);
PARSE_INST
}
#undef FAILED
#undef RESTORE_REG
uint64_t *init_table = (uint64_t*)malloc(tablelen);
memcpy(init_table, unwind_constr.table, tablelen);
uint8_t *init_statuses = (uint8_t*)malloc(statuseslen);
memcpy(init_statuses, unwind_constr.statuses, statuseslen);
cur_inst = cur_addr;
#define RESTORE_REG(reg) \
unwind_constr.table[reg] = init_table[reg]; \
SET_STATUS(unwind_constr, (reg), ((init_statuses[(reg) >> 1] >> (((reg) & 1) << 2)) & 0xF));
#define FAILED \
free(init_statuses); \
free(init_table); \
free(unwind_constr.statuses); \
free(unwind_constr.table); \
for (uint64_t i = 0; i < maxstacksize; ++i) free(table_stack[i]); \
free(table_stack); \
*success = 0; return 0;
while ((cur_inst < next_addr) && (cur_pointed_addr <= addr)) {
unsigned char inst; READ_U1(inst, cur_inst);
// printf_log(LOG_NONE, "Executing post 0x%02X\n", inst);
PARSE_INST
}
#undef FAILED
#undef RESTORE_REG
#undef PARSE_INST
free(init_statuses);
free(init_table);
for (int i = 0; i < maxstacksize; ++i) {
free(table_stack[i]);
}
free(table_stack);
dwarf_unwind_t new_unwind;
new_unwind.reg_count = unwind_constr.reg_count;
new_unwind.regs = calloc(unwind_constr.reg_count, sizeof(uint64_t));
uintptr_t cfa = unwind->regs[cfa_reg];
if (cfa_signed) cfa += cfa_offset.soff;
else cfa += cfa_offset.uoff;
// printf_log(LOG_NONE, "Done, rewriting registers (CFA at r%d(0x%016lX) + %lld (%c) -> 0x%016lX\n", cfa_reg, unwind->regs[cfa_reg], cfa_offset.soff, cfa_signed ? 's' : 'u', cfa);
for (uint64_t i = 0; i < unwind_constr.reg_count; ++i) {
switch (GET_STATUS(unwind_constr, i)) {
case REGSTATUS_undefined:
// printf_log(LOG_NONE, "Register %02lX: (undefined)\n", i);
break;
case REGSTATUS_same_val:
if (i >= unwind->reg_count) {
printf_log(LOG_DEBUG, "Invalid register status (value copied from register 0x%02lX)\n", i);
free(unwind_constr.statuses);
free(unwind_constr.table);
free(new_unwind.regs);
*success = 0;
return 0;
}
new_unwind.regs[i] = unwind->regs[i];
// printf_log(LOG_NONE, "Register %02lX: copy %016lX\n", i, new_unwind.regs[i]);
break;
case REGSTATUS_offset:
new_unwind.regs[i] = *(uint64_t*)(cfa + (int64_t)unwind_constr.table[i]);
// printf_log(LOG_NONE, "Register %02lX: offset %016lX [%016lX + %lld]\n", i, new_unwind.regs[i], cfa, (int64_t)unwind_constr.table[i]);
break;
case REGSTATUS_val_offset:
new_unwind.regs[i] = (uint64_t)(cfa + (int64_t)unwind_constr.table[i]);
// printf_log(LOG_NONE, "Register %02lX: voff %016lX\n", i, new_unwind.regs[i]);
break;
case REGSTATUS_register:
if (unwind_constr.table[i] >= unwind->reg_count) {
printf_log(LOG_DEBUG, "Invalid register status (value copied from register 0x%02lX)\n", unwind_constr.table[i]);
free(unwind_constr.statuses);
free(unwind_constr.table);
free(new_unwind.regs);
*success = 0;
return 0;
}
new_unwind.regs[i] = unwind->regs[unwind_constr.table[i]];
// printf_log(LOG_NONE, "Register %02lX: reg %016lX\n", i, new_unwind.regs[i]);
break;
}
}
*success = (GET_STATUS(unwind_constr, return_addr_reg) == REGSTATUS_undefined) ? 0 : ((aug_fields & AUG_SIGHDLER) ? 2 : 1);
free(unwind_constr.statuses);
free(unwind_constr.table);
free(unwind->regs);
unwind->reg_count = new_unwind.reg_count;
unwind->regs = new_unwind.regs;
// Maybe?
unwind->regs[cfa_reg] = cfa;
// printf_log(LOG_NONE, "Returning %016lX\n", unwind->regs[return_addr_reg]);
return unwind->regs[return_addr_reg];
}
} else {
// printf_log(LOG_NONE, "[FDE] Ptr %02X\n", cie_ptr);
printf_log(LOG_DEBUG, "Unexpected FDE, corresponding CIE missing\n");
return 0;
}
cur_addr = next_addr;
}
*success = 0;
return 0;
}
dwarf_unwind_t *init_dwarf_unwind_registers(x64emu_t *emu) {
dwarf_unwind_t *unwind_struct = (dwarf_unwind_t*)malloc(sizeof(dwarf_unwind_t));
unwind_struct->reg_count = 17;
unwind_struct->regs = (uint64_t*)malloc(17*sizeof(uint64_t));
/* x86_64-abi-0.99.pdf
* Register Name | Number | Abbreviation
* General Purpose Register RAX | 0 | %rax
* General Purpose Register RDX | 1 | %rdx
* General Purpose Register RCX | 2 | %rcx
* General Purpose Register RBX | 3 | %rbx
* General Purpose Register RSI | 4 | %rsi
* General Purpose Register RDI | 5 | %rdi
* Frame Pointer Register RBP | 6 | %rbp
* Stack Pointer Register RSP | 7 | %rsp
* Extended Integer Registers 8-15 | 8-15 | %r8-%r15
* Return Address RA | 16 |
* Vector Registers 0-7 | 17-24 | %xmm0-%xmm7
* Extended Vector Registers 8-15 | 25-32 | %xmm8-%xmm15
* Floating Point Registers 0-7 | 33-40 | %st0-%st7
* MMX Registers 0-7 | 41-48 | %mm0-%mm7
* Flag Register | 49 | %rFLAGS
* Segment Register ES | 50 | %es
* Segment Register CS | 51 | %cs
* Segment Register SS | 52 | %ss
* Segment Register DS | 53 | %ds
* Segment Register FS | 54 | %fs
* Segment Register GS | 55 | %gs
* Reserved | 56-57 |
* FS Base address | 58 | %fs.base
* GS Base address | 59 | %gs.base
* Reserved | 60-61 |
* Task Register | 62 | %tr
* LDT Register | 63 | %ldtr
* 128-bit Media Control and Status | 64 | %mxcsr
* x87 Control Word | 65 | %fcw
* x87 Status Word | 66 | %fsw
*/
unwind_struct->regs[ 0] = emu->regs[_RAX].q[0];
unwind_struct->regs[ 1] = emu->regs[_RDX].q[0];
unwind_struct->regs[ 2] = emu->regs[_RCX].q[0];
unwind_struct->regs[ 3] = emu->regs[_RBX].q[0];
unwind_struct->regs[ 4] = emu->regs[_RSI].q[0];
unwind_struct->regs[ 5] = emu->regs[_RDI].q[0];
unwind_struct->regs[ 6] = emu->regs[_RBP].q[0];
unwind_struct->regs[ 7] = emu->regs[_RSP].q[0] + 8;
unwind_struct->regs[ 8] = emu->regs[_R8 ].q[0];
unwind_struct->regs[ 9] = emu->regs[_R9 ].q[0];
unwind_struct->regs[10] = emu->regs[_R10].q[0];
unwind_struct->regs[11] = emu->regs[_R11].q[0];
unwind_struct->regs[12] = emu->regs[_R12].q[0];
unwind_struct->regs[13] = emu->regs[_R13].q[0];
unwind_struct->regs[14] = emu->regs[_R14].q[0];
unwind_struct->regs[15] = emu->regs[_R15].q[0];
unwind_struct->regs[16] = emu->ip.q[0];
return unwind_struct;
}
void free_dwarf_unwind_registers(dwarf_unwind_t **unwind_struct) {
free((*unwind_struct)->regs);
free(*unwind_struct);
*unwind_struct = NULL;
}

View File

@ -0,0 +1,19 @@
#ifndef __ELFDWARF_PRIVATE_H_
#define __ELFDWARF_PRIVATE_H_
#include "emu/x64emu_private.h"
typedef struct dwarf_unwind_s {
uint8_t reg_count;
uint64_t *regs;
} dwarf_unwind_t;
typedef struct elfheader_s elfheader_t;
dwarf_unwind_t *init_dwarf_unwind_registers(x64emu_t *emu);
void free_dwarf_unwind_registers(dwarf_unwind_t **unwind_struct);
// Returns the callee's address on success or NULL on failure (may be NULL regardless).
// If success equals 2, the frame is a signal frame.
uintptr_t get_parent_registers(dwarf_unwind_t *emu, const elfheader_t *ehdr, uintptr_t addr, char *success);
#endif // __ELFDWARF_PRIVATE_H_

View File

@ -9,6 +9,8 @@ typedef struct library_s library_t;
typedef struct needed_libs_s needed_libs_t; typedef struct needed_libs_s needed_libs_t;
#include <pthread.h> #include <pthread.h>
#include <elf.h>
#include "elfloader.h"
struct elfheader_s { struct elfheader_s {
char* name; char* name;
@ -66,6 +68,9 @@ struct elfheader_s {
uintptr_t plt_end; uintptr_t plt_end;
uintptr_t text; uintptr_t text;
size_t textsz; size_t textsz;
uintptr_t ehframe;
uintptr_t ehframe_end;
uintptr_t ehframehdr;
uintptr_t paddr; uintptr_t paddr;
uintptr_t vaddr; uintptr_t vaddr;

View File

@ -349,6 +349,17 @@ elfheader_t* ParseElfHeader(FILE* f, const char* name, int exec)
h->textsz = h->SHEntries[ii].sh_size; h->textsz = h->SHEntries[ii].sh_size;
printf_log(LOG_DEBUG, "The .text is at address %p, and is %zu big\n", (void*)h->text, h->textsz); printf_log(LOG_DEBUG, "The .text is at address %p, and is %zu big\n", (void*)h->text, h->textsz);
} }
ii = FindSection(h->SHEntries, h->numSHEntries, h->SHStrTab, ".eh_frame");
if(ii) {
h->ehframe = (uintptr_t)(h->SHEntries[ii].sh_addr);
h->ehframe_end = h->ehframe + h->SHEntries[ii].sh_size;
printf_log(LOG_DEBUG, "The .eh_frame section is at address %p..%p\n", (void*)h->ehframe, (void*)h->ehframe_end);
}
ii = FindSection(h->SHEntries, h->numSHEntries, h->SHStrTab, ".eh_frame_hdr");
if(ii) {
h->ehframehdr = (uintptr_t)(h->SHEntries[ii].sh_addr);
printf_log(LOG_DEBUG, "The .eh_frame_hdr section is at address %p\n", (void*)h->ehframehdr);
}
LoadNamedSection(f, h->SHEntries, h->numSHEntries, h->SHStrTab, ".dynstr", "DynSym Strings", SHT_STRTAB, (void**)&h->DynStr, NULL); LoadNamedSection(f, h->SHEntries, h->numSHEntries, h->SHStrTab, ".dynstr", "DynSym Strings", SHT_STRTAB, (void**)&h->DynStr, NULL);
LoadNamedSection(f, h->SHEntries, h->numSHEntries, h->SHStrTab, ".dynsym", "DynSym", SHT_DYNSYM, (void**)&h->DynSym, &h->numDynSym); LoadNamedSection(f, h->SHEntries, h->numSHEntries, h->SHStrTab, ".dynsym", "DynSym", SHT_DYNSYM, (void**)&h->DynSym, &h->numDynSym);

View File

@ -459,6 +459,8 @@ void EmuCall(x64emu_t* emu, uintptr_t addr)
uint64_t old_rsi = R_RSI; uint64_t old_rsi = R_RSI;
uint64_t old_rbp = R_RBP; uint64_t old_rbp = R_RBP;
uint64_t old_rip = R_RIP; uint64_t old_rip = R_RIP;
Push64(emu, GetRBP(emu)); // set frame pointer
SetRBP(emu, GetRSP(emu)); // save RSP
PushExit(emu); PushExit(emu);
R_RIP = addr; R_RIP = addr;
emu->df = d_none; emu->df = d_none;

View File

@ -234,9 +234,10 @@ static void* pthread_routine(void* p)
et->emu->type = EMUTYPE_MAIN; et->emu->type = EMUTYPE_MAIN;
// setup callstack and run... // setup callstack and run...
x64emu_t* emu = et->emu; x64emu_t* emu = et->emu;
Push64(emu, 0); // PUSH BP Push64(emu, 0); // PUSH 0 (backtrace marker: return address is 0)
Push64(emu, 0); // PUSH BP
R_RBP = R_RSP; // MOV BP, SP R_RBP = R_RSP; // MOV BP, SP
R_RSP -= 56; // Gard zone R_RSP -= 56; // Guard zone
PushExit(emu); PushExit(emu);
R_RIP = et->fnc; R_RIP = et->fnc;
R_RDI = (uintptr_t)et->arg; R_RDI = (uintptr_t)et->arg;

View File

@ -2502,35 +2502,34 @@ EXPORT int my_semctl(int semid, int semnum, int cmd, union semun b)
} }
// Backtrace stuff // Backtrace stuff
typedef struct i386_layout_s
{
struct i386_layout_s *next;
void *return_address;
} i386_layout_t;
#include "elfs/elfdwarf_private.h"
EXPORT int my_backtrace(x64emu_t* emu, void** buffer, int size) EXPORT int my_backtrace(x64emu_t* emu, void** buffer, int size)
{ {
// Get current Framepointer if (!size) return 0;
i386_layout_t *fp = (i386_layout_t*)R_RBP; dwarf_unwind_t *unwind = init_dwarf_unwind_registers(emu);
if((uintptr_t)fp == (uintptr_t)buffer) // cannot find the FramePointer if it's not set in BP properly int idx = 0;
return 0; char success = 0;
uintptr_t stack_end = (uintptr_t)(emu->init_stack) + emu->size_stack; uintptr_t addr = *(uintptr_t*)R_RSP;
uintptr_t stack_start = (uintptr_t)(emu->init_stack); buffer[0] = (void*)addr;
// check if fp is on another stack (in case of beeing call from a signal with altstack) while (++idx < size) {
x64emu_t *thread_emu = thread_get_emu(); uintptr_t ret_addr = get_parent_registers(unwind, FindElfAddress(my_context, addr), addr, &success);
if(emu!=thread_emu && (((uintptr_t)fp>(uintptr_t)(thread_emu->init_stack)) && ((uintptr_t)fp<((uintptr_t)(thread_emu->init_stack) + thread_emu->size_stack)))) { if (ret_addr == (uintptr_t)GetExit()) {
stack_end = (uintptr_t)(thread_emu->init_stack) + thread_emu->size_stack; // TODO: do something to be able to get the function name
stack_start = (uintptr_t)(thread_emu->init_stack); buffer[idx] = (void*)ret_addr;
} success = 2;
int idx=0; // See elfdwarf_private.c for the register mapping
while(idx<size) { unwind->regs[6] = unwind->regs[7]; // mov rsp, rbp
if(!(uintptr_t)fp || ((uintptr_t)fp+sizeof(void*)>=stack_end) || ((uintptr_t)fp<=stack_start)) { unwind->regs[7] = *(uint64_t*)unwind->regs[6]; // pop rbp
return idx; unwind->regs[6] += 8;
} ret_addr = *(uint64_t*)unwind->regs[6]; // ret
buffer[idx] = fp->return_address; unwind->regs[6] += 8;
fp = fp->next; if (++idx < size) buffer[idx] = (void*)ret_addr;
++idx; } else if (!success) break;
else buffer[idx] = (void*)ret_addr;
addr = ret_addr;
} }
free_dwarf_unwind_registers(&unwind);
return idx; return idx;
} }
@ -2538,16 +2537,21 @@ EXPORT char** my_backtrace_symbols(x64emu_t* emu, uintptr_t* buffer, int size)
{ {
(void)emu; (void)emu;
char** ret = (char**)calloc(1, size*sizeof(char*) + size*100); // capping each strings to 100 chars char** ret = (char**)calloc(1, size*sizeof(char*) + size*100); // capping each strings to 100 chars
char* s = (char*)(ret+size*sizeof(char*)); char* s = (char*)(ret+size);
for (int i=0; i<size; ++i) { for (int i=0; i<size; ++i) {
uintptr_t start = 0; uintptr_t start = 0;
uint64_t sz = 0; uint64_t sz = 0;
const char* symbname = FindNearestSymbolName(FindElfAddress(my_context, buffer[i]), (void*)buffer[i], &start, &sz); elfheader_t *hdr = FindElfAddress(my_context, buffer[i]);
if(symbname && buffer[i]>=start && (buffer[i]<(start+sz) || !sz)) const char* symbname = FindNearestSymbolName(hdr, (void*)buffer[i], &start, &sz);
snprintf(s, 100, "%s+%ld [%p]\n", symbname, buffer[i] - start, (void*)buffer[i]); if (symbname && buffer[i]>=start && (buffer[i]<(start+sz) || !sz)) {
else snprintf(s, 100, "%s(%s+%lx) [%p]", ElfName(hdr), symbname, buffer[i] - start, (void*)buffer[i]);
snprintf(s, 100, "??? [%p]\n", (void*)buffer[i]); } else if (hdr) {
s+=100; snprintf(s, 100, "%s+%lx [%p]", ElfName(hdr), buffer[i] - (uintptr_t)GetBaseAddress(hdr), (void*)buffer[i]);
} else {
snprintf(s, 100, "??? [%p]", (void*)buffer[i]);
}
ret[i] = s;
s += 100;
} }
return ret; return ret;
} }

5
tests/ref19.txt Normal file
View File

@ -0,0 +1,5 @@
backtrace() returned 4 addresses
:myfunc3
:main
???
:_start

BIN
tests/test19 Executable file

Binary file not shown.

73
tests/test19.c Normal file
View File

@ -0,0 +1,73 @@
#include <execinfo.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
/// build with `gcc -march=core2 -g -O2 test19.c -o test19 -no-pie -rdynamic`
#define BT_BUF_SIZE 100
void myfunc3()
{
int nptrs;
void *buffer[BT_BUF_SIZE];
char **strings;
nptrs = backtrace(buffer, BT_BUF_SIZE);
printf("backtrace() returned %d addresses\n", nptrs);
/* The call backtrace_symbols_fd(buffer, nptrs, STDOUT_FILENO)
would produce similar output to the following: */
strings = backtrace_symbols(buffer, nptrs);
if (strings == NULL) {
perror("backtrace_symbols");
exit(EXIT_FAILURE);
}
for (int j = 0; j < nptrs; j++) {
// clean-up output so it can be compared
char* p = strchr(strings[j], '[');
if(p)
p[-1] = '\0';
p = strchr(strings[j], '(');
if(p)
*p = ':';
p = strchr(p?p:strings[j], '+');
if(p)
*p = '\0';
p = strchr(p?p:strings[j], ')');
if(p)
*p = '\0';
p = strchr(strings[j], ':');
if(!p)
p = strings[j];
printf("%s\n", p);
}
free(strings);
}
static void /* "static" means don't export the symbol... */
myfunc2(void)
{
myfunc3();
}
void myfunc(int ncalls)
{
if (ncalls > 1)
myfunc(ncalls - 1);
else
myfunc2();
}
int main(int argc, char *argv[])
{
int ncall = 4;
if (argc == 2) {
ncall = atoi(argv[1]);
}
myfunc(ncall);
exit(EXIT_SUCCESS);
}