Browse Source

Correctly iterate eh_frame, getting all FDE ranges

master
A.Olokhtonov 3 years ago
parent
commit
65a461ff77
  1. 6
      command.c
  2. 254
      eh_frame.c
  3. 6
      main.c

6
command.c

@ -141,3 +141,9 @@ command_backtrace(struct mi_process proc)
return_address = ptrace(PTRACE_PEEKDATA, proc.pid, fb + 8, NULL); return_address = ptrace(PTRACE_PEEKDATA, proc.pid, fb + 8, NULL);
} }
} }
static void
command_print(struct mi_process proc, char *command, int command_length)
{
}

254
eh_frame.c

@ -32,6 +32,28 @@ enum dwarf_cfa_op {
DW_CFA_hi_user = 0x3f, 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 { struct dwarf_cie_header {
u32 length; u32 length;
u8 version; u8 version;
@ -40,6 +62,12 @@ struct dwarf_cie_header {
u32 return_address_register; u32 return_address_register;
u32 augmentation_data_length; u32 augmentation_data_length;
u8 *augmentation_data; 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 { struct dwarf_fde_header {
@ -53,10 +81,8 @@ struct dwarf_fde_header {
}; };
static u64 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; u64 read = 0;
for (;;) { for (;;) {
@ -247,7 +273,7 @@ iterate_call_frame_instructions(struct dwarf_cie_header header, u8 *data, u8 *or
data += increment; data += increment;
read += increment + 1; read += increment + 1;
if (read >= leftover) { if (read >= to_read) {
break; break;
} }
} }
@ -256,12 +282,10 @@ iterate_call_frame_instructions(struct dwarf_cie_header header, u8 *data, u8 *or
} }
static u64 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; char *augmenation_string = (char *) data;
@ -282,50 +306,204 @@ read_one_cie(u64 length, u8 *data, u8 *original_data)
(void) has_L; (void) has_L;
(void) has_P; (void) has_P;
(void) has_R;
data += decode_leb128(data, &header.code_alignment); data += decode_leb128(data, &header->code_alignment);
data += decode_leb128s(data, &header.data_alignment); data += decode_leb128s(data, &header->data_alignment);
data += decode_leb128(data, &header.return_address_register); data += decode_leb128(data, &header->return_address_register);
if (has_z) { if (has_z) {
data += decode_leb128(data, &header.augmentation_data_length); data += decode_leb128(data, &header->augmentation_data_length);
header.augmentation_data = data; header->augmentation_data = data;
data += header.augmentation_data_length; 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); return(data - original_data);
} }
static u64 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 }; struct dwarf_fde_header header = { 0 };
header.length = length; header.length = length;
header.cie = (struct dwarf_cie_header *) (data - 4 - cie_offset); 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') u32 pointer_size = read_encoded_pointer(proc, cie, data, &header.low_pc, section_base);
data += decode_leb128(data, &header.augmentation_data_length); data += pointer_size;
header.augmentation_data = data;
data += header.augmentation_data_length; 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 }; 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 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; u8 *original_data = data;
u64 length; u64 length;
@ -336,6 +514,11 @@ read_one_call_frame_record(u8 *data)
length = length32; length = length32;
data += 4; data += 4;
if (length == 0) {
// NOTE(aolo2): terminator
return(4);
}
if (length == 0) { if (length == 0) {
memcpy(&length, data, 8); memcpy(&length, data, 8);
data += 8; data += 8;
@ -347,23 +530,24 @@ read_one_call_frame_record(u8 *data)
u64 result; u64 result;
if (cie_id == 0) { if (cie_id == 0) {
result = read_one_cie(length, data, original_data); result = read_one_cie(last_cie, length, data, original_data);
} else { } 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); return(result);
} }
static void 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; u64 read = 0;
for (;;) { 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; read += size;
if (read >= eh_frame.size) { if (read >= eh_frame.size) {
break; break;

6
main.c

@ -1,8 +1,8 @@
#include "common.h" #include "common.h"
#include "util.c"
#include "dwarf.c" #include "dwarf.c"
#include "eh_frame.c" #include "eh_frame.c"
#include "util.c"
#include "command.c" #include "command.c"
int int
@ -22,7 +22,7 @@ main(int argc, char *argv[])
char *last_command = malloc(max_command_length + 1); char *last_command = malloc(max_command_length + 1);
parse_debug_info(process.elf, &process.debug); 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.base_address = 0x555555554000UL; // get_executable_base_address(file, proc.pid);
process.main_address = get_address_of_subroutine(process, "main"); 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)) { } else if (0 == strncmp(command, "start\n", command_length)) {
command_start(process); command_start(process);
} else if (0 == strncmp(command, "print ", 6)) { } 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)) { } else if (0 == strncmp(command, "next\n", command_length)) {
command_next(process); command_next(process);
} else if (0 == strncmp(command, "line\n", command_length)) { } else if (0 == strncmp(command, "line\n", command_length)) {

Loading…
Cancel
Save