DWARF stuff...
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

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, &reg);
increment += decode_leb128(data, &offset);
break;
}
case DW_CFA_restore_extended: {
u32 reg;
increment += decode_leb128(data, &reg);
break;
}
case DW_CFA_undefined: {
u32 reg;
increment += decode_leb128(data, &reg);
break;
}
case DW_CFA_same_value: {
u32 reg;
increment += decode_leb128(data, &reg);
break;
}
case DW_CFA_register: {
u32 reg;
s32 factored_offset;
increment += decode_leb128(data, &reg);
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, &reg);
increment += decode_leb128(data, &nonfactored_offset);
break;
}
case DW_CFA_def_cfa_register: {
u32 reg;
increment += decode_leb128(data, &reg);
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, &reg);
increment += decode_leb128(data, &length);
increment += length;
break;
}
case DW_CFA_offset_extended_sf: {
u32 reg;
s32 factored_offset;
increment += decode_leb128(data, &reg);
increment += decode_leb128s(data, &factored_offset);
break;
}
case DW_CFA_def_cfa_sf: {
u32 reg;
s32 factored_offset;
increment += decode_leb128(data, &reg);
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, &reg);
increment += decode_leb128(data, &factored_offset);
break;
}
case DW_CFA_val_offset_sf: {
u32 reg;
s32 factored_offset;
increment += decode_leb128(data, &reg);
increment += decode_leb128s(data, &factored_offset);
break;
}
case DW_CFA_val_expression: {
u32 reg;
u32 length;
increment += decode_leb128(data, &reg);
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;
}
}
}