You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
372 lines
10 KiB
372 lines
10 KiB
enum dwarf_cfa_op_base { |
|
DW_CFA_advance_loc = 0x1, |
|
DW_CFA_offset = 0x2, |
|
DW_CFA_restore = 0x3 |
|
}; |
|
|
|
enum dwarf_cfa_op { |
|
DW_CFA_nop = 0x00, |
|
DW_CFA_set_loc = 0x01, |
|
DW_CFA_advance_loc1 = 0x02, |
|
DW_CFA_advance_loc2 = 0x03, |
|
DW_CFA_advance_loc4 = 0x04, |
|
DW_CFA_offset_extended = 0x05, |
|
DW_CFA_restore_extended = 0x06, |
|
DW_CFA_undefined = 0x07, |
|
DW_CFA_same_value = 0x08, |
|
DW_CFA_register = 0x09, |
|
DW_CFA_remember_state = 0x0a, |
|
DW_CFA_restore_state = 0x0b, |
|
DW_CFA_def_cfa = 0x0c, |
|
DW_CFA_def_cfa_register = 0x0d, |
|
DW_CFA_def_cfa_offset = 0x0e, |
|
DW_CFA_def_cfa_expression = 0x0f, |
|
DW_CFA_expression = 0x10, |
|
DW_CFA_offset_extended_sf = 0x11, |
|
DW_CFA_def_cfa_sf = 0x12, |
|
DW_CFA_def_cfa_offset_sf = 0x13, |
|
DW_CFA_val_offset = 0x14, |
|
DW_CFA_val_offset_sf = 0x15, |
|
DW_CFA_val_expression = 0x16, |
|
DW_CFA_lo_user = 0x1c, |
|
DW_CFA_hi_user = 0x3f, |
|
}; |
|
|
|
struct dwarf_cie_header { |
|
u32 length; |
|
u8 version; |
|
u32 code_alignment; |
|
s32 data_alignment; |
|
u32 return_address_register; |
|
u32 augmentation_data_length; |
|
u8 *augmentation_data; |
|
}; |
|
|
|
struct dwarf_fde_header { |
|
u32 length; |
|
u64 optional_length; |
|
struct dwarf_cie_header *cie; |
|
u64 low_pc; |
|
u64 high_pc; |
|
u32 augmentation_data_length; |
|
u8 *augmentation_data; |
|
}; |
|
|
|
static u64 |
|
iterate_call_frame_instructions(struct dwarf_cie_header header, u8 *data, u8 *original_data) |
|
{ |
|
u64 already_read = data - original_data - 4; |
|
u64 leftover = header.length - already_read; |
|
u64 read = 0; |
|
|
|
for (;;) { |
|
u8 op_byte = *data++; |
|
enum dwarf_cfa_op_base high_two = op_byte >> 6; |
|
enum dwarf_cfa_op low_six = op_byte & 0x3f; |
|
|
|
u32 increment = 0; |
|
|
|
if (high_two == DW_CFA_advance_loc) { |
|
u8 delta = low_six; |
|
} else if (high_two == DW_CFA_offset) { |
|
u8 reg = low_six; |
|
u32 factored_offset; |
|
increment += decode_leb128(data, &factored_offset); |
|
} else if (high_two == DW_CFA_restore) { |
|
u8 reg = low_six; |
|
|
|
} else if (high_two == 0) { |
|
switch (low_six) { |
|
case DW_CFA_nop: { |
|
break; |
|
} |
|
|
|
case DW_CFA_set_loc: { |
|
u64 address; |
|
memcpy(&address, data, 8); |
|
increment = 8; |
|
break; |
|
} |
|
|
|
case DW_CFA_advance_loc1: { |
|
u8 advance; |
|
memcpy(&advance, data, 1); |
|
increment = 1; |
|
break; |
|
} |
|
|
|
case DW_CFA_advance_loc2: { |
|
u16 advance; |
|
memcpy(&advance, data, 2); |
|
increment = 2; |
|
break; |
|
} |
|
|
|
case DW_CFA_advance_loc4: { |
|
u32 advance; |
|
memcpy(&advance, data, 4); |
|
increment = 4; |
|
break; |
|
} |
|
|
|
case DW_CFA_offset_extended: { |
|
u32 reg; |
|
u32 offset; |
|
increment += decode_leb128(data, ®); |
|
increment += decode_leb128(data, &offset); |
|
break; |
|
} |
|
|
|
case DW_CFA_restore_extended: { |
|
u32 reg; |
|
increment += decode_leb128(data, ®); |
|
break; |
|
} |
|
|
|
case DW_CFA_undefined: { |
|
u32 reg; |
|
increment += decode_leb128(data, ®); |
|
break; |
|
} |
|
|
|
case DW_CFA_same_value: { |
|
u32 reg; |
|
increment += decode_leb128(data, ®); |
|
break; |
|
} |
|
|
|
case DW_CFA_register: { |
|
u32 reg; |
|
s32 factored_offset; |
|
increment += decode_leb128(data, ®); |
|
increment += decode_leb128s(data, &factored_offset); |
|
break; |
|
} |
|
|
|
case DW_CFA_remember_state: { |
|
break; |
|
} |
|
|
|
case DW_CFA_restore_state: { |
|
break; |
|
} |
|
|
|
case DW_CFA_def_cfa: { |
|
u32 reg; |
|
u32 nonfactored_offset; |
|
increment += decode_leb128(data, ®); |
|
increment += decode_leb128(data, &nonfactored_offset); |
|
break; |
|
} |
|
|
|
case DW_CFA_def_cfa_register: { |
|
u32 reg; |
|
increment += decode_leb128(data, ®); |
|
break; |
|
} |
|
|
|
case DW_CFA_def_cfa_offset: { |
|
u32 offset; |
|
increment += decode_leb128(data, &offset); |
|
break; |
|
} |
|
|
|
case DW_CFA_def_cfa_expression: { |
|
u32 length; |
|
increment += decode_leb128(data, &length); |
|
increment += length; |
|
break; |
|
} |
|
|
|
case DW_CFA_expression: { |
|
u32 reg; |
|
u32 length; |
|
increment += decode_leb128(data, ®); |
|
increment += decode_leb128(data, &length); |
|
increment += length; |
|
break; |
|
} |
|
|
|
case DW_CFA_offset_extended_sf: { |
|
u32 reg; |
|
s32 factored_offset; |
|
increment += decode_leb128(data, ®); |
|
increment += decode_leb128s(data, &factored_offset); |
|
break; |
|
} |
|
|
|
case DW_CFA_def_cfa_sf: { |
|
u32 reg; |
|
s32 factored_offset; |
|
increment += decode_leb128(data, ®); |
|
increment += decode_leb128s(data, &factored_offset); |
|
break; |
|
} |
|
|
|
case DW_CFA_def_cfa_offset_sf: { |
|
s32 factored_offset; |
|
increment += decode_leb128s(data, &factored_offset); |
|
break; |
|
} |
|
|
|
case DW_CFA_val_offset: { |
|
u32 reg; |
|
u32 factored_offset; |
|
increment += decode_leb128(data, ®); |
|
increment += decode_leb128(data, &factored_offset); |
|
break; |
|
} |
|
|
|
case DW_CFA_val_offset_sf: { |
|
u32 reg; |
|
s32 factored_offset; |
|
increment += decode_leb128(data, ®); |
|
increment += decode_leb128s(data, &factored_offset); |
|
break; |
|
} |
|
|
|
case DW_CFA_val_expression: { |
|
u32 reg; |
|
u32 length; |
|
increment += decode_leb128(data, ®); |
|
increment += decode_leb128(data, &length); |
|
increment += length; |
|
break; |
|
} |
|
|
|
case DW_CFA_lo_user: { |
|
break; |
|
} |
|
|
|
case DW_CFA_hi_user: { |
|
break; |
|
} |
|
} |
|
} |
|
|
|
data += increment; |
|
read += increment + 1; |
|
|
|
if (read >= leftover) { |
|
break; |
|
} |
|
} |
|
|
|
return(read); |
|
} |
|
|
|
static u64 |
|
read_one_cie(u64 length, u8 *data, u8 *original_data) |
|
{ |
|
struct dwarf_cie_header header = { 0 }; |
|
|
|
header.length = length; |
|
header.version = *data++; |
|
|
|
char *augmenation_string = (char *) data; |
|
|
|
// NOTE: null-terminated string |
|
int has_z = 0; |
|
int has_L = 0; |
|
int has_P = 0; |
|
int has_R = 0; |
|
|
|
while (*data) { |
|
if (*data == 'z') has_z = 1; |
|
if (*data == 'L') has_L = 1; |
|
if (*data == 'P') has_P = 1; |
|
if (*data == 'R') has_R = 1; |
|
++data; |
|
} |
|
++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); |
|
|
|
if (has_z) { |
|
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); |
|
|
|
return(data - original_data); |
|
} |
|
|
|
static u64 |
|
read_one_fde(u64 length, u32 cie_offset, u8 *data, u8 *original_data) |
|
{ |
|
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; |
|
|
|
|
|
struct dwarf_cie_header hh = { 0 }; |
|
data += iterate_call_frame_instructions(hh, data, original_data); |
|
|
|
return(0); |
|
} |
|
|
|
static u64 |
|
read_one_call_frame_record(u8 *data) |
|
{ |
|
u8 *original_data = data; |
|
u64 length; |
|
u32 length32; |
|
u32 cie_id; |
|
|
|
memcpy(&length32, data, 4); |
|
length = length32; |
|
data += 4; |
|
|
|
if (length == 0) { |
|
memcpy(&length, data, 8); |
|
data += 8; |
|
} |
|
|
|
memcpy(&cie_id, data, 4); |
|
data += 4; |
|
|
|
u64 result; |
|
|
|
if (cie_id == 0) { |
|
result = read_one_cie(length, data, original_data); |
|
} else { |
|
result = read_one_fde(length, cie_id, data, original_data); |
|
} |
|
|
|
return(result); |
|
} |
|
|
|
static void |
|
parse_eh_frame(u8 *file) |
|
{ |
|
struct elf_section_table_entry_x64 eh_frame = get_section_entry(file, ".eh_frame"); |
|
|
|
u64 read = 0; |
|
|
|
for (;;) { |
|
u64 size = read_one_call_frame_record(file + eh_frame.offset_in_file + read); |
|
read += size; |
|
if (read >= eh_frame.size) { |
|
break; |
|
} |
|
} |
|
} |