A.Olokhtonov
3 years ago
4 changed files with 375 additions and 43 deletions
@ -0,0 +1,372 @@ |
|||||||
|
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; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
Loading…
Reference in new issue