From 65a461ff7745f42fb007d75fc5558b250c96cd39 Mon Sep 17 00:00:00 2001 From: "A.Olokhtonov" Date: Sat, 24 Jul 2021 14:47:25 +0300 Subject: [PATCH] Correctly iterate eh_frame, getting all FDE ranges --- command.c | 6 ++ eh_frame.c | 254 +++++++++++++++++++++++++++++++++++++++++++++-------- main.c | 6 +- 3 files changed, 228 insertions(+), 38 deletions(-) diff --git a/command.c b/command.c index 8984e9b..600021e 100644 --- a/command.c +++ b/command.c @@ -140,4 +140,10 @@ command_backtrace(struct mi_process proc) fb = ptrace(PTRACE_PEEKDATA, proc.pid, fb, NULL); return_address = ptrace(PTRACE_PEEKDATA, proc.pid, fb + 8, NULL); } +} + +static void +command_print(struct mi_process proc, char *command, int command_length) +{ + } \ No newline at end of file diff --git a/eh_frame.c b/eh_frame.c index fd2d797..4532f64 100644 --- a/eh_frame.c +++ b/eh_frame.c @@ -32,6 +32,28 @@ enum dwarf_cfa_op { DW_CFA_hi_user = 0x3f, }; +enum dwarf_cie_pointer_format { + DW_EH_PE_absptr = 0x00, + DW_EH_PE_uleb128 = 0x01, + DW_EH_PE_udata2 = 0x02, + DW_EH_PE_udata4 = 0x03, + DW_EH_PE_udata8 = 0x04, + DW_EH_PE_sleb128 = 0x09, + DW_EH_PE_sdata2 = 0x0A, + DW_EH_PE_sdata4 = 0x0B, + DW_EH_PE_sdata8 = 0x0C, +}; + +enum dwarf_cie_pointer_application { + DW_EH_PE_pcrel = 0x10, + DW_EH_PE_textrel = 0x20, + DW_EH_PE_datarel = 0x30, + DW_EH_PE_funcrel = 0x40, + DW_EH_PE_aligned = 0x50, + DW_EH_PE_indirect = 0x80, + DW_EH_PE_omit = 0xFF, +}; + struct dwarf_cie_header { u32 length; u8 version; @@ -40,6 +62,12 @@ struct dwarf_cie_header { u32 return_address_register; u32 augmentation_data_length; u8 *augmentation_data; + + int has_z; + + enum dwarf_cie_pointer_format pointer_format; + enum dwarf_cie_pointer_application pointer_application; + u8 pointer_indirect; }; struct dwarf_fde_header { @@ -53,10 +81,8 @@ struct dwarf_fde_header { }; static u64 -iterate_call_frame_instructions(struct dwarf_cie_header header, u8 *data, u8 *original_data) +iterate_call_frame_instructions(u8 *data, u64 to_read) { - u64 already_read = data - original_data - 4; - u64 leftover = header.length - already_read; u64 read = 0; for (;;) { @@ -247,7 +273,7 @@ iterate_call_frame_instructions(struct dwarf_cie_header header, u8 *data, u8 *or data += increment; read += increment + 1; - if (read >= leftover) { + if (read >= to_read) { break; } } @@ -256,12 +282,10 @@ iterate_call_frame_instructions(struct dwarf_cie_header header, u8 *data, u8 *or } static u64 -read_one_cie(u64 length, u8 *data, u8 *original_data) +read_one_cie(struct dwarf_cie_header *header, u64 length, u8 *data, u8 *original_data) { - struct dwarf_cie_header header = { 0 }; - - header.length = length; - header.version = *data++; + header->length = length; + header->version = *data++; char *augmenation_string = (char *) data; @@ -282,50 +306,204 @@ read_one_cie(u64 length, u8 *data, u8 *original_data) (void) has_L; (void) has_P; - (void) has_R; - data += decode_leb128(data, &header.code_alignment); - data += decode_leb128s(data, &header.data_alignment); - data += decode_leb128(data, &header.return_address_register); + data += decode_leb128(data, &header->code_alignment); + data += decode_leb128s(data, &header->data_alignment); + data += decode_leb128(data, &header->return_address_register); if (has_z) { - data += decode_leb128(data, &header.augmentation_data_length); - header.augmentation_data = data; - data += header.augmentation_data_length; + data += decode_leb128(data, &header->augmentation_data_length); + header->augmentation_data = data; + data += header->augmentation_data_length; } - data += iterate_call_frame_instructions(header, data, original_data); + header->has_z = has_z; + + data += iterate_call_frame_instructions(data, header->length - (data - original_data - 4)); + + if (has_R) { + // NOTE(aolo2): this shit is undocumented. Best sources I could find: + // - gdb source code (dwarf2cfi.c) + // - this random blog post: https://dandylife.net/blog/archives/686 + // - this pdf by Igor Skochinsky: + // https://www.hexblog.com/wp-content/uploads/2012/06/Recon-2012-Skochinsky-Compiler-Internals.pdf + // - perf source code: http://ansymbol.com/linux/v3.13/source/tools/perf/util/unwind.c + + + // NOTE(aolo2): WAIT! There ARE docs! There are just no links to them from the .eh_frame page! + // https://refspecs.linuxfoundation.org/LSB_3.0.0/LSB-PDA/LSB-PDA.junk/dwarfext.html + + u8 byte = *header->augmentation_data; + header->pointer_format = byte & 0x0f; + header->pointer_application = byte & 0x70; + header->pointer_indirect = byte & 0x80; + } return(data - original_data); } static u64 -read_one_fde(u64 length, u32 cie_offset, u8 *data, u8 *original_data) +read_encoded_pointer(struct mi_process proc, struct dwarf_cie_header *cie, u8 *data, u64 *dest, u8 *section_base) +{ + u64 offset = 0; + s64 final_value; + + switch (cie->pointer_format) { + case DW_EH_PE_absptr: { + u64 value; + memcpy(&value, data, 8); + offset = 8; + final_value = value; + break; + } + + case DW_EH_PE_uleb128: { + u32 value; + offset = decode_leb128(data, &value); + final_value= value; + break; + } + + case DW_EH_PE_sleb128: { + s32 value; + offset = decode_leb128s(data, &value); + final_value = value; + break; + } + + case DW_EH_PE_udata2: { + u16 value; + memcpy(&value, data, 2); + offset = 2; + final_value = value; + break; + } + + case DW_EH_PE_udata4: { + u32 value; + memcpy(&value, data, 4); + offset = 4; + final_value = value; + break; + } + + case DW_EH_PE_udata8: { + u64 value; + memcpy(&value, data, 8); + offset = 8; + final_value = value; + break; + } + + case DW_EH_PE_sdata2: { + s16 value; + memcpy(&value, data, 2); + offset = 2; + final_value = value; + break; + } + + case DW_EH_PE_sdata4: { + s32 value; + memcpy(&value, data, 4); + offset = 4; + final_value = value; + break; + } + + case DW_EH_PE_sdata8: { + s64 value; + memcpy(&value, data, 8); + offset = 8; + final_value = value; + break; + } + } + + struct mi_registers regs = get_process_registers(proc); + struct mi_function *func = get_function_around_pc(proc, regs.rip - proc.base_address); + + switch (cie->pointer_application) { + case DW_EH_PE_pcrel: { + *dest = data - proc.elf + final_value; + break; + } + + case DW_EH_PE_textrel: { + struct elf_section_table_entry_x64 text = get_section_entry(proc.elf, ".text"); + *dest = text.virtual_address + final_value - proc.base_address; + break; + } + + case DW_EH_PE_datarel: { + struct elf_section_table_entry_x64 got = get_section_entry(proc.elf, ".got"); + struct elf_section_table_entry_x64 eh_frame_hdr = get_section_entry(proc.elf, ".eh_frame_hdr"); + + if (got.virtual_address) { + *dest = got.virtual_address + final_value - proc.base_address; + } else { + *dest = eh_frame_hdr.virtual_address + final_value - proc.base_address; + } + + break; + } + + case DW_EH_PE_funcrel: { + if (func) { + *dest = func->low_pc + final_value; + } else { + DIE("could not find function around pc while decoding address!\n"); + } + break; + } + + default: { + DIE("unsupported pointer application: DW_EH_PE_aligned, DW_EH_PE_indirect, or DW_EH_PE_omit\n"); + } + } + + return(offset); +} + +static u64 +read_one_fde(struct mi_process proc, struct dwarf_cie_header *cie, u64 length, u32 cie_offset, u8 *data, u8 *original_data, u8 *section_base) { struct dwarf_fde_header header = { 0 }; header.length = length; header.cie = (struct dwarf_cie_header *) (data - 4 - cie_offset); - memcpy(&header.low_pc, data, 8); - data += 8; - memcpy(&header.high_pc, data, 8); - data += 8; - header.high_pc = header.low_pc + header.high_pc; - // if (cie has 'z') - data += decode_leb128(data, &header.augmentation_data_length); - header.augmentation_data = data; - data += header.augmentation_data_length; + u32 pointer_size = read_encoded_pointer(proc, cie, data, &header.low_pc, section_base); + data += pointer_size; + + u64 fde_length = 0; + if (pointer_size == 2) { + memcpy(&fde_length, data, 2); + data += 2; + } else if (pointer_size == 4) { + memcpy(&fde_length, data, 4); + data += 4; + } else if (pointer_size == 8) { + memcpy(&fde_length, data, 8); + data += 8; + } + + header.high_pc = header.low_pc + fde_length; + if (cie->has_z) { + data += decode_leb128(data, &header.augmentation_data_length); + header.augmentation_data = data; + data += header.augmentation_data_length; + } struct dwarf_cie_header hh = { 0 }; - data += iterate_call_frame_instructions(hh, data, original_data); + data += iterate_call_frame_instructions(data, header.length - (data - original_data - 4)); - return(0); + return(data - original_data); } static u64 -read_one_call_frame_record(u8 *data) +read_one_call_frame_record(struct mi_process proc, struct dwarf_cie_header *last_cie, u8 *section_base, u8 *data) { u8 *original_data = data; u64 length; @@ -336,6 +514,11 @@ read_one_call_frame_record(u8 *data) length = length32; data += 4; + if (length == 0) { + // NOTE(aolo2): terminator + return(4); + } + if (length == 0) { memcpy(&length, data, 8); data += 8; @@ -347,23 +530,24 @@ read_one_call_frame_record(u8 *data) u64 result; if (cie_id == 0) { - result = read_one_cie(length, data, original_data); + result = read_one_cie(last_cie, length, data, original_data); } else { - result = read_one_fde(length, cie_id, data, original_data); + result = read_one_fde(proc, last_cie, length, cie_id, data, original_data, section_base); } return(result); } static void -parse_eh_frame(u8 *file) +parse_eh_frame(struct mi_process proc) { - struct elf_section_table_entry_x64 eh_frame = get_section_entry(file, ".eh_frame"); + struct elf_section_table_entry_x64 eh_frame = get_section_entry(proc.elf, ".eh_frame"); + struct dwarf_cie_header last_cie = { 0 }; u64 read = 0; for (;;) { - u64 size = read_one_call_frame_record(file + eh_frame.offset_in_file + read); + u64 size = read_one_call_frame_record(proc, &last_cie, proc.elf + eh_frame.offset_in_file, proc.elf + eh_frame.offset_in_file + read); read += size; if (read >= eh_frame.size) { break; diff --git a/main.c b/main.c index 90f5436..3dd40c0 100644 --- a/main.c +++ b/main.c @@ -1,8 +1,8 @@ #include "common.h" +#include "util.c" #include "dwarf.c" #include "eh_frame.c" -#include "util.c" #include "command.c" int @@ -22,7 +22,7 @@ main(int argc, char *argv[]) char *last_command = malloc(max_command_length + 1); parse_debug_info(process.elf, &process.debug); - parse_eh_frame(process.elf); + parse_eh_frame(process); process.base_address = 0x555555554000UL; // get_executable_base_address(file, proc.pid); process.main_address = get_address_of_subroutine(process, "main"); @@ -52,7 +52,7 @@ main(int argc, char *argv[]) } else if (0 == strncmp(command, "start\n", command_length)) { command_start(process); } else if (0 == strncmp(command, "print ", 6)) { - //command_print(process, command + 6, command_length - 7); + command_print(process, command + 6, command_length - 7); } else if (0 == strncmp(command, "next\n", command_length)) { command_next(process); } else if (0 == strncmp(command, "line\n", command_length)) {