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
3 years ago
|
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;
|
||
|
}
|
||
|
}
|
||
|
}
|