|
|
@ -1,5 +1,6 @@ |
|
|
|
static u64 |
|
|
|
static u64 |
|
|
|
iterate_call_frame_instructions(u8 *data, u64 to_read) |
|
|
|
iterate_call_frame_instructions(struct dwarf_cie *cie, u8 *data, u64 to_read, |
|
|
|
|
|
|
|
struct mi_registers *regs, struct dwarf_regset *regset, u64 location) |
|
|
|
{ |
|
|
|
{ |
|
|
|
u64 read = 0; |
|
|
|
u64 read = 0; |
|
|
|
|
|
|
|
|
|
|
@ -12,13 +13,15 @@ iterate_call_frame_instructions(u8 *data, u64 to_read) |
|
|
|
|
|
|
|
|
|
|
|
if (high_two == DW_CFA_advance_loc) { |
|
|
|
if (high_two == DW_CFA_advance_loc) { |
|
|
|
u8 delta = low_six; |
|
|
|
u8 delta = low_six; |
|
|
|
|
|
|
|
if (regset) { |
|
|
|
|
|
|
|
regset->loc += delta * cie->code_alignment; |
|
|
|
|
|
|
|
} |
|
|
|
} else if (high_two == DW_CFA_offset) { |
|
|
|
} else if (high_two == DW_CFA_offset) { |
|
|
|
u8 reg = low_six; |
|
|
|
u8 reg = low_six; |
|
|
|
u32 factored_offset; |
|
|
|
u32 factored_offset; |
|
|
|
increment += decode_leb128(data, &factored_offset); |
|
|
|
increment += decode_leb128(data, &factored_offset); |
|
|
|
} else if (high_two == DW_CFA_restore) { |
|
|
|
} else if (high_two == DW_CFA_restore) { |
|
|
|
u8 reg = low_six; |
|
|
|
u8 reg = low_six; |
|
|
|
|
|
|
|
|
|
|
|
} else if (high_two == 0) { |
|
|
|
} else if (high_two == 0) { |
|
|
|
switch (low_six) { |
|
|
|
switch (low_six) { |
|
|
|
case DW_CFA_nop: { |
|
|
|
case DW_CFA_nop: { |
|
|
@ -29,6 +32,9 @@ iterate_call_frame_instructions(u8 *data, u64 to_read) |
|
|
|
u64 address; |
|
|
|
u64 address; |
|
|
|
memcpy(&address, data, 8); |
|
|
|
memcpy(&address, data, 8); |
|
|
|
increment = 8; |
|
|
|
increment = 8; |
|
|
|
|
|
|
|
if (regset) { |
|
|
|
|
|
|
|
regset->loc = address; // TODO: encoding??
|
|
|
|
|
|
|
|
} |
|
|
|
break; |
|
|
|
break; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
@ -36,6 +42,9 @@ iterate_call_frame_instructions(u8 *data, u64 to_read) |
|
|
|
u8 advance; |
|
|
|
u8 advance; |
|
|
|
memcpy(&advance, data, 1); |
|
|
|
memcpy(&advance, data, 1); |
|
|
|
increment = 1; |
|
|
|
increment = 1; |
|
|
|
|
|
|
|
if (regset) { |
|
|
|
|
|
|
|
regset->loc += advance * cie->code_alignment; |
|
|
|
|
|
|
|
} |
|
|
|
break; |
|
|
|
break; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
@ -43,6 +52,9 @@ iterate_call_frame_instructions(u8 *data, u64 to_read) |
|
|
|
u16 advance; |
|
|
|
u16 advance; |
|
|
|
memcpy(&advance, data, 2); |
|
|
|
memcpy(&advance, data, 2); |
|
|
|
increment = 2; |
|
|
|
increment = 2; |
|
|
|
|
|
|
|
if (regset) { |
|
|
|
|
|
|
|
regset->loc += advance * cie->code_alignment; |
|
|
|
|
|
|
|
} |
|
|
|
break; |
|
|
|
break; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
@ -50,6 +62,9 @@ iterate_call_frame_instructions(u8 *data, u64 to_read) |
|
|
|
u32 advance; |
|
|
|
u32 advance; |
|
|
|
memcpy(&advance, data, 4); |
|
|
|
memcpy(&advance, data, 4); |
|
|
|
increment = 4; |
|
|
|
increment = 4; |
|
|
|
|
|
|
|
if (regset) { |
|
|
|
|
|
|
|
regset->loc += advance * cie->code_alignment; |
|
|
|
|
|
|
|
} |
|
|
|
break; |
|
|
|
break; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
@ -98,20 +113,35 @@ iterate_call_frame_instructions(u8 *data, u64 to_read) |
|
|
|
case DW_CFA_def_cfa: { |
|
|
|
case DW_CFA_def_cfa: { |
|
|
|
u32 reg; |
|
|
|
u32 reg; |
|
|
|
u32 nonfactored_offset; |
|
|
|
u32 nonfactored_offset; |
|
|
|
|
|
|
|
|
|
|
|
increment += decode_leb128(data, ®); |
|
|
|
increment += decode_leb128(data, ®); |
|
|
|
increment += decode_leb128(data, &nonfactored_offset); |
|
|
|
increment += decode_leb128(data + increment, &nonfactored_offset); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (regset) { |
|
|
|
|
|
|
|
regset->cfa_offset = nonfactored_offset; |
|
|
|
|
|
|
|
regset->cfa_register = reg; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
break; |
|
|
|
break; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
case DW_CFA_def_cfa_register: { |
|
|
|
case DW_CFA_def_cfa_register: { |
|
|
|
u32 reg; |
|
|
|
u32 reg; |
|
|
|
increment += decode_leb128(data, ®); |
|
|
|
increment += decode_leb128(data, ®); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (regset) { |
|
|
|
|
|
|
|
regset->cfa_register = reg; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
break; |
|
|
|
break; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
case DW_CFA_def_cfa_offset: { |
|
|
|
case DW_CFA_def_cfa_offset: { |
|
|
|
u32 offset; |
|
|
|
u32 offset; |
|
|
|
increment += decode_leb128(data, &offset); |
|
|
|
increment += decode_leb128(data, &offset); |
|
|
|
|
|
|
|
if (regset) { |
|
|
|
|
|
|
|
regset->cfa_offset = offset; |
|
|
|
|
|
|
|
} |
|
|
|
break; |
|
|
|
break; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
@ -188,6 +218,11 @@ iterate_call_frame_instructions(u8 *data, u64 to_read) |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (location && regset->loc > location) { |
|
|
|
|
|
|
|
regset->cfa = regset->system[regset->cfa_register] + regset->cfa_offset; |
|
|
|
|
|
|
|
break; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
data += increment; |
|
|
|
data += increment; |
|
|
|
read += increment + 1; |
|
|
|
read += increment + 1; |
|
|
|
|
|
|
|
|
|
|
@ -236,8 +271,10 @@ read_one_cie(struct dwarf_cie *header, u64 length, u8 *data, u8 *original_data) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
header->has_z = has_z; |
|
|
|
header->has_z = has_z; |
|
|
|
|
|
|
|
header->instructions = data; |
|
|
|
|
|
|
|
header->instructions_length = header->length - (data - original_data - 4); |
|
|
|
|
|
|
|
|
|
|
|
data += iterate_call_frame_instructions(data, header->length - (data - original_data - 4)); |
|
|
|
data += iterate_call_frame_instructions(header, data, header->instructions_length, 0, 0, 0); |
|
|
|
|
|
|
|
|
|
|
|
if (has_R) { |
|
|
|
if (has_R) { |
|
|
|
// NOTE(aolo2): this shit is undocumented. Best sources I could find:
|
|
|
|
// NOTE(aolo2): this shit is undocumented. Best sources I could find:
|
|
|
@ -372,14 +409,13 @@ read_encoded_pointer(struct mi_process proc, struct dwarf_cie *cie, u8 *data, u6 |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
static u64 |
|
|
|
static u64 |
|
|
|
read_one_fde(struct mi_process proc, struct dwarf_cie *cie, u64 length, u32 cie_offset, u8 *data, u8 *original_data) |
|
|
|
read_one_fde(struct mi_process proc, struct dwarf_cie *cie, u64 length, u8 *data, u8 *original_data, |
|
|
|
|
|
|
|
struct dwarf_fde *header) |
|
|
|
{ |
|
|
|
{ |
|
|
|
struct dwarf_fde header = { 0 }; |
|
|
|
header->length = length; |
|
|
|
|
|
|
|
header->cie = *cie; |
|
|
|
header.length = length; |
|
|
|
|
|
|
|
header.cie = (struct dwarf_cie *) (data - 4 - cie_offset); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
u32 pointer_size = read_encoded_pointer(proc, cie, data, &header.low_pc); |
|
|
|
u32 pointer_size = read_encoded_pointer(proc, cie, data, &header->low_pc); |
|
|
|
data += pointer_size; |
|
|
|
data += pointer_size; |
|
|
|
|
|
|
|
|
|
|
|
u64 fde_length = 0; |
|
|
|
u64 fde_length = 0; |
|
|
@ -394,21 +430,24 @@ read_one_fde(struct mi_process proc, struct dwarf_cie *cie, u64 length, u32 cie_ |
|
|
|
data += 8; |
|
|
|
data += 8; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
header.high_pc = header.low_pc + fde_length; |
|
|
|
header->high_pc = header->low_pc + fde_length; |
|
|
|
|
|
|
|
|
|
|
|
if (cie->has_z) { |
|
|
|
if (cie->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(data, header.length - (data - original_data - 4)); |
|
|
|
header->instructions = data; |
|
|
|
|
|
|
|
header->instructions_length = header->length - (data - original_data - 4); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
data += iterate_call_frame_instructions(cie, data, header->instructions_length, 0, 0, 0); |
|
|
|
|
|
|
|
|
|
|
|
return(data - original_data); |
|
|
|
return(data - original_data); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
static u64 |
|
|
|
static u64 |
|
|
|
read_one_call_frame_record(struct mi_process proc, struct dwarf_cie *last_cie, u8 *data) |
|
|
|
read_one_call_frame_record(struct mi_process proc, struct dwarf_cie *last_cie, struct dwarf_fde *last_fde, int *is_cie, u8 *data) |
|
|
|
{ |
|
|
|
{ |
|
|
|
u8 *original_data = data; |
|
|
|
u8 *original_data = data; |
|
|
|
u64 length; |
|
|
|
u64 length; |
|
|
@ -436,49 +475,76 @@ read_one_call_frame_record(struct mi_process proc, struct dwarf_cie *last_cie, u |
|
|
|
|
|
|
|
|
|
|
|
if (cie_id == 0) { |
|
|
|
if (cie_id == 0) { |
|
|
|
result = read_one_cie(last_cie, length, data, original_data); |
|
|
|
result = read_one_cie(last_cie, length, data, original_data); |
|
|
|
|
|
|
|
*is_cie = 1; |
|
|
|
} else { |
|
|
|
} else { |
|
|
|
result = read_one_fde(proc, last_cie, length, cie_id, data, original_data); |
|
|
|
result = read_one_fde(proc, last_cie, length, data, original_data, last_fde); |
|
|
|
|
|
|
|
*is_cie = 0; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
return(result); |
|
|
|
return(result); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
static void |
|
|
|
static struct dwarf_fde |
|
|
|
parse_eh_frame(struct mi_process proc) |
|
|
|
eh_frame_find_fde(struct mi_process proc, u64 pc) |
|
|
|
{ |
|
|
|
{ |
|
|
|
struct elf_section_table_entry_x64 eh_frame = get_section_entry(proc.elf, ".eh_frame"); |
|
|
|
struct elf_section_table_entry_x64 eh_frame = get_section_entry(proc.elf, ".eh_frame"); |
|
|
|
struct dwarf_cie last_cie = { 0 }; |
|
|
|
struct dwarf_cie last_cie = { 0 }; |
|
|
|
|
|
|
|
struct dwarf_fde last_fde = { 0 }; |
|
|
|
|
|
|
|
int is_cie = 0; |
|
|
|
|
|
|
|
|
|
|
|
u64 read = 0; |
|
|
|
u64 read = 0; |
|
|
|
|
|
|
|
|
|
|
|
for (;;) { |
|
|
|
for (;;) { |
|
|
|
u64 size = read_one_call_frame_record(proc, &last_cie, proc.elf + eh_frame.offset_in_file + read); |
|
|
|
u64 size = read_one_call_frame_record(proc, &last_cie, &last_fde, &is_cie, proc.elf + eh_frame.offset_in_file + read); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (!is_cie) { |
|
|
|
|
|
|
|
if (last_fde.low_pc <= pc && pc < last_fde.high_pc) { |
|
|
|
|
|
|
|
return(last_fde); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
read += size; |
|
|
|
read += size; |
|
|
|
if (read >= eh_frame.size) { |
|
|
|
if (read >= eh_frame.size) { |
|
|
|
break; |
|
|
|
break; |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static struct dwarf_fde * |
|
|
|
last_fde.length = 0; |
|
|
|
eh_frame_find_fde(struct mi_process proc, u64 pc) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return(last_fde); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
static struct dwarf_regset |
|
|
|
static struct dwarf_regset |
|
|
|
eh_frame_init_registers(struct mi_process proc, struct dwarf_cie *cie) |
|
|
|
eh_frame_init_registers(struct mi_process proc, struct mi_registers regs, struct dwarf_cie cie) |
|
|
|
{ |
|
|
|
{ |
|
|
|
|
|
|
|
struct dwarf_regset regset = { 0 }; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
regset.system[0] = regs._sys.rax; |
|
|
|
|
|
|
|
regset.system[1] = regs._sys.rdx; |
|
|
|
|
|
|
|
regset.system[2] = regs._sys.rcx; |
|
|
|
|
|
|
|
regset.system[3] = regs._sys.rbx; |
|
|
|
|
|
|
|
regset.system[4] = regs._sys.rsi; |
|
|
|
|
|
|
|
regset.system[5] = regs._sys.rdi; |
|
|
|
|
|
|
|
regset.system[6] = regs._sys.rbp; |
|
|
|
|
|
|
|
regset.system[7] = regs._sys.rsp; |
|
|
|
|
|
|
|
regset.system[8] = regs._sys.r8; |
|
|
|
|
|
|
|
regset.system[9] = regs._sys.r9; |
|
|
|
|
|
|
|
regset.system[10] = regs._sys.r10; |
|
|
|
|
|
|
|
regset.system[11] = regs._sys.r11; |
|
|
|
|
|
|
|
regset.system[12] = regs._sys.r12; |
|
|
|
|
|
|
|
regset.system[13] = regs._sys.r13; |
|
|
|
|
|
|
|
regset.system[14] = regs._sys.r14; |
|
|
|
|
|
|
|
regset.system[15] = regs._sys.r15; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
iterate_call_frame_instructions(&cie, cie.instructions, cie.instructions_length, ®s, ®set, 0); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return(regset); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
static struct dwarf_regset |
|
|
|
static struct dwarf_regset |
|
|
|
eh_frame_find_pc(struct mi_process proc, struct dwarf_fde *fde, struct dwarf_regset regs, u64 pc) |
|
|
|
eh_frame_find_pc(struct mi_process proc, struct dwarf_fde fde, struct mi_registers regs, struct dwarf_regset regset, u64 pc) |
|
|
|
{ |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static u64 |
|
|
|
|
|
|
|
eh_frame_compute_cfa(struct mi_process proc, struct dwarf_regset regs) |
|
|
|
|
|
|
|
{ |
|
|
|
{ |
|
|
|
|
|
|
|
regset.loc = fde.low_pc; |
|
|
|
|
|
|
|
iterate_call_frame_instructions(&fde.cie, fde.instructions, fde.instructions_length, ®s, ®set, pc); |
|
|
|
|
|
|
|
return(regset); |
|
|
|
} |
|
|
|
} |