|
|
|
static struct elf_section_table_entry_x64
|
|
|
|
get_section_entry(u8 *file, char *name)
|
|
|
|
{
|
|
|
|
struct elf_header_x64 header = { 0 };
|
|
|
|
memcpy(&header, file, sizeof(header));
|
|
|
|
|
|
|
|
for (int i = 0; i < header.header_table_entry_count; ++i) {
|
|
|
|
struct elf_header_table_entry_x64 header_entry = { 0 };
|
|
|
|
u64 offset = header.header_table_offset + header.header_table_entry_size * i;
|
|
|
|
memcpy(&header_entry, file + offset, sizeof(header_entry));
|
|
|
|
}
|
|
|
|
|
|
|
|
struct elf_section_table_entry_x64 shstrtab_header = { 0 };
|
|
|
|
u64 shstrtab_header_offset = header.section_table_offset + header.section_table_entry_size * header.section_names_table_index;
|
|
|
|
memcpy(&shstrtab_header, file + shstrtab_header_offset, sizeof(shstrtab_header));
|
|
|
|
|
|
|
|
u64 shstrtab_offset = shstrtab_header.offset_in_file;
|
|
|
|
|
|
|
|
struct elf_section_table_entry_x64 result = { 0 };
|
|
|
|
|
|
|
|
for (int i = 0; i < header.section_table_entry_count; ++i) {
|
|
|
|
struct elf_section_table_entry_x64 section_entry = { 0 };
|
|
|
|
u64 offset = header.section_table_offset + header.section_table_entry_size * i;
|
|
|
|
memcpy(§ion_entry, file + offset, sizeof(section_entry));
|
|
|
|
u64 section_name_offset = shstrtab_offset + section_entry.name_offset;
|
|
|
|
|
|
|
|
if (strncmp((char *) file + section_name_offset, name, strlen(name) + 1) == 0) {
|
|
|
|
result = section_entry;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
return(result);
|
|
|
|
}
|
|
|
|
|
|
|
|
static u64
|
|
|
|
get_section_offset(u8 *file, char *name)
|
|
|
|
{
|
|
|
|
struct elf_section_table_entry_x64 entry = get_section_entry(file, name);
|
|
|
|
u64 result = entry.offset_in_file;
|
|
|
|
return(result);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
dwarf_section_contribution_is_64(u8 *file, u64 offset)
|
|
|
|
{
|
|
|
|
u32 length;
|
|
|
|
memcpy(&length, file + offset, 4);
|
|
|
|
return(length >= 0xFFFFFFF0);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
decode_leb128(u8 *at, u32 *dest)
|
|
|
|
{
|
|
|
|
int offset = 0;
|
|
|
|
|
|
|
|
u64 result = 0;
|
|
|
|
u64 shift = 0;
|
|
|
|
|
|
|
|
for (;;) {
|
|
|
|
u8 byte = at[offset++];
|
|
|
|
|
|
|
|
result |= ((byte & 127) << shift);
|
|
|
|
|
|
|
|
if ((byte & 128) == 0) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
shift += 7;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (dest) {
|
|
|
|
*dest = result;
|
|
|
|
}
|
|
|
|
|
|
|
|
return(offset);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
decode_leb128s(u8 *at, s32 *dest)
|
|
|
|
{
|
|
|
|
int offset = 0;
|
|
|
|
|
|
|
|
s64 result = 0;
|
|
|
|
u64 shift = 0;
|
|
|
|
u32 size = 32;
|
|
|
|
u8 byte;
|
|
|
|
|
|
|
|
for (;;) {
|
|
|
|
byte = at[offset++];
|
|
|
|
result |= ((byte & 127) << shift);
|
|
|
|
shift += 7;
|
|
|
|
if ((byte & 128) == 0) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((shift < size) && (byte & 64)) {
|
|
|
|
result |= -(1 << shift);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (dest) {
|
|
|
|
*dest = result;
|
|
|
|
}
|
|
|
|
|
|
|
|
return(offset);
|
|
|
|
}
|
|
|
|
|
|
|
|
static u64
|
|
|
|
abbrev_entry_offset(u8 *file, u64 abbrev_offset, u32 requested_code)
|
|
|
|
{
|
|
|
|
u32 code, tag;
|
|
|
|
u32 offset = 0;
|
|
|
|
|
|
|
|
do {
|
|
|
|
offset += decode_leb128(file + abbrev_offset + offset, &code);
|
|
|
|
offset += decode_leb128(file + abbrev_offset + offset, &tag);
|
|
|
|
|
|
|
|
u32 has_children = file[abbrev_offset + offset++];
|
|
|
|
(void) has_children;
|
|
|
|
|
|
|
|
if (code == requested_code) {
|
|
|
|
return(abbrev_offset);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (code == 0) {
|
|
|
|
/* Abbreviation code not found, this should not happen */
|
|
|
|
assert(0);
|
|
|
|
return(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
u32 attribute, form;
|
|
|
|
|
|
|
|
do {
|
|
|
|
offset += decode_leb128(file + abbrev_offset + offset, &form);
|
|
|
|
offset += decode_leb128(file + abbrev_offset + offset, &attribute);
|
|
|
|
} while (attribute != 0 || form != 0);
|
|
|
|
|
|
|
|
abbrev_offset += offset;
|
|
|
|
offset = 0;
|
|
|
|
} while (code != 0);
|
|
|
|
|
|
|
|
assert(0);
|
|
|
|
|
|
|
|
return(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
static s64
|
|
|
|
compute_dwarf_expression_value(u8 *at, u32 length)
|
|
|
|
{
|
|
|
|
s64 result = 0;
|
|
|
|
s64 stack[128] = { 0 };
|
|
|
|
u32 stack_head = 0;
|
|
|
|
u8 *original_at = at;
|
|
|
|
|
|
|
|
enum dwarf_expression_op op;
|
|
|
|
u32 increment;
|
|
|
|
|
|
|
|
while (at - original_at < length) {
|
|
|
|
op = *at++;
|
|
|
|
|
|
|
|
increment = 0;
|
|
|
|
|
|
|
|
switch (op) {
|
|
|
|
case DW_OP_addr: {
|
|
|
|
u64 address;
|
|
|
|
memcpy(&address, at, 8);
|
|
|
|
stack[stack_head++] = address;
|
|
|
|
increment = 8;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case DW_OP_deref: {
|
|
|
|
u64 address = stack[--stack_head];
|
|
|
|
s64 value;
|
|
|
|
memcpy(&value, (void *) address, 8); // TODO: surely not this...
|
|
|
|
stack[stack_head++] = value;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case DW_OP_const1u: {
|
|
|
|
u8 const_value = *at;
|
|
|
|
stack[stack_head++] = const_value;
|
|
|
|
increment = 1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case DW_OP_const1s: {
|
|
|
|
s8 const_value = *((s8 *) at);
|
|
|
|
stack[stack_head++] = const_value;
|
|
|
|
increment = 1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case DW_OP_const2u: {
|
|
|
|
u16 const_value;
|
|
|
|
memcpy(&const_value, at, 2);
|
|
|
|
stack[stack_head++] = const_value;
|
|
|
|
increment = 2;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case DW_OP_const2s: {
|
|
|
|
s16 const_value;
|
|
|
|
memcpy(&const_value, at, 2);
|
|
|
|
stack[stack_head++] = const_value;
|
|
|
|
increment = 2;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case DW_OP_const4u: {
|
|
|
|
u32 const_value;
|
|
|
|
memcpy(&const_value, at, 4);
|
|
|
|
stack[stack_head++] = const_value;
|
|
|
|
increment = 4;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case DW_OP_const4s: {
|
|
|
|
s32 const_value;
|
|
|
|
memcpy(&const_value, at, 4);
|
|
|
|
stack[stack_head++] = const_value;
|
|
|
|
increment = 4;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case DW_OP_const8u: {
|
|
|
|
u64 const_value;
|
|
|
|
memcpy(&const_value, at, 8);
|
|
|
|
stack[stack_head++] = const_value;
|
|
|
|
increment = 8;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case DW_OP_const8s: {
|
|
|
|
s64 const_value;
|
|
|
|
memcpy(&const_value, at, 8);
|
|
|
|
stack[stack_head++] = const_value;
|
|
|
|
increment = 8;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case DW_OP_constu: {
|
|
|
|
u32 const_value;
|
|
|
|
increment = decode_leb128(at, &const_value);
|
|
|
|
stack[stack_head++] = const_value;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case DW_OP_consts: {
|
|
|
|
s32 const_value;
|
|
|
|
increment = decode_leb128s(at, &const_value);
|
|
|
|
stack[stack_head++] = const_value;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case DW_OP_dup: {
|
|
|
|
s64 top = stack[stack_head - 1];
|
|
|
|
stack[stack_head++] = top;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case DW_OP_drop: {
|
|
|
|
--stack_head;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case DW_OP_over: {
|
|
|
|
s64 value = stack[stack_head - 2];
|
|
|
|
stack[stack_head++] = value;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case DW_OP_pick: {
|
|
|
|
u8 stack_index = *at;
|
|
|
|
increment = 1;
|
|
|
|
// NOTE: 0 means top of stack
|
|
|
|
s64 value = stack[stack_head - 1 - stack_index];
|
|
|
|
stack[stack_head++] = value;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case DW_OP_swap: {
|
|
|
|
s64 top = stack[stack_head - 1];
|
|
|
|
s64 second_from_top = stack[stack_head - 2];
|
|
|
|
stack[stack_head - 2] = top;
|
|
|
|
stack[stack_head - 1] = second_from_top;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case DW_OP_rot: {
|
|
|
|
s64 top = stack[stack_head - 1];
|
|
|
|
s64 second = stack[stack_head - 2];
|
|
|
|
s64 third = stack[stack_head - 3];
|
|
|
|
stack[stack_head - 1] = second;
|
|
|
|
stack[stack_head - 2] = third;
|
|
|
|
stack[stack_head - 3] = top;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case DW_OP_xderef: {
|
|
|
|
/*
|
|
|
|
TODO: The top two stack elements are popped, and a data item is retrieved through an implementation-defined address calculation and pushed as the new stack top. The size of the data retrieved from the dereferenced address is the size of an address on the target machine.
|
|
|
|
*/
|
|
|
|
stack_head -= 2;
|
|
|
|
s64 value = 0xdeadbeef;
|
|
|
|
stack[stack_head++] = value;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case DW_OP_abs: {
|
|
|
|
stack[stack_head - 1] = ABS(stack[stack_head - 1]);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case DW_OP_and: {
|
|
|
|
s64 a = stack[--stack_head];
|
|
|
|
s64 b = stack[--stack_head];
|
|
|
|
s64 result = b & a;
|
|
|
|
stack[stack_head++] = result;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case DW_OP_div: {
|
|
|
|
s64 a = stack[--stack_head];
|
|
|
|
s64 b = stack[--stack_head];
|
|
|
|
s64 result = b / a;
|
|
|
|
stack[stack_head++] = result;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case DW_OP_minus: {
|
|
|
|
s64 a = stack[--stack_head];
|
|
|
|
s64 b = stack[--stack_head];
|
|
|
|
s64 result = b - a;
|
|
|
|
stack[stack_head++] = result;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case DW_OP_mod: {
|
|
|
|
s64 a = stack[--stack_head];
|
|
|
|
s64 b = stack[--stack_head];
|
|
|
|
s64 result = b % a;
|
|
|
|
stack[stack_head++] = result;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case DW_OP_mul: {
|
|
|
|
s64 a = stack[--stack_head];
|
|
|
|
s64 b = stack[--stack_head];
|
|
|
|
s64 result = b * a;
|
|
|
|
stack[stack_head++] = result;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case DW_OP_neg: {
|
|
|
|
stack[stack_head - 1] = -stack[stack_head - 1];
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case DW_OP_not: {
|
|
|
|
stack[stack_head - 1] = ~stack[stack_head - 1];
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case DW_OP_or: {
|
|
|
|
s64 a = stack[--stack_head];
|
|
|
|
s64 b = stack[--stack_head];
|
|
|
|
s64 result = b | a;
|
|
|
|
stack[stack_head++] = result;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case DW_OP_plus: {
|
|
|
|
s64 a = stack[--stack_head];
|
|
|
|
s64 b = stack[--stack_head];
|
|
|
|
s64 result = b + a;
|
|
|
|
stack[stack_head++] = result;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case DW_OP_plus_uconst: {
|
|
|
|
u32 const_value;
|
|
|
|
increment = decode_leb128(at, &const_value);
|
|
|
|
s64 a = stack[--stack_head];
|
|
|
|
s64 result = a + const_value;
|
|
|
|
stack[stack_head++] = result;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case DW_OP_shl: {
|
|
|
|
s64 a = stack[--stack_head];
|
|
|
|
s64 b = stack[--stack_head];
|
|
|
|
s64 result = b << a;
|
|
|
|
stack[stack_head++] = result;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case DW_OP_shr: {
|
|
|
|
s64 a = stack[--stack_head];
|
|
|
|
s64 b = stack[--stack_head];
|
|
|
|
s64 result = b >> a;
|
|
|
|
stack[stack_head++] = result;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case DW_OP_shra: {
|
|
|
|
s64 a = stack[--stack_head];
|
|
|
|
s64 b = stack[--stack_head];
|
|
|
|
s64 result;
|
|
|
|
|
|
|
|
if (b > 0) {
|
|
|
|
result = b >> a;
|
|
|
|
} else {
|
|
|
|
result = ~(~b >> a);
|
|
|
|
}
|
|
|
|
|
|
|
|
stack[stack_head++] = result;
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case DW_OP_xor: {
|
|
|
|
s64 a = stack[--stack_head];
|
|
|
|
s64 b = stack[--stack_head];
|
|
|
|
s64 result = b ^ a;
|
|
|
|
stack[stack_head++] = result;
|
|
|
|
break;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case DW_OP_skip: {
|
|
|
|
s16 const_value;
|
|
|
|
memcpy(&const_value, at, 2);
|
|
|
|
increment = 2;
|
|
|
|
increment += const_value;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case DW_OP_bra: {
|
|
|
|
s16 const_value;
|
|
|
|
memcpy(&const_value, at, 2);
|
|
|
|
increment = 2;
|
|
|
|
s64 top = stack[--stack_head];
|
|
|
|
if (top) {
|
|
|
|
increment += const_value;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case DW_OP_eq: {
|
|
|
|
s64 a = stack[--stack_head];
|
|
|
|
s64 b = stack[--stack_head];
|
|
|
|
stack[stack_head++] = (b == a ? 1 : 0);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case DW_OP_ge: {
|
|
|
|
s64 a = stack[--stack_head];
|
|
|
|
s64 b = stack[--stack_head];
|
|
|
|
stack[stack_head++] = (b >= a ? 1 : 0);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case DW_OP_gt: {
|
|
|
|
s64 a = stack[--stack_head];
|
|
|
|
s64 b = stack[--stack_head];
|
|
|
|
stack[stack_head++] = (b > a ? 1 : 0);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case DW_OP_le: {
|
|
|
|
s64 a = stack[--stack_head];
|
|
|
|
s64 b = stack[--stack_head];
|
|
|
|
stack[stack_head++] = (b <= a ? 1 : 0);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case DW_OP_lt: {
|
|
|
|
s64 a = stack[--stack_head];
|
|
|
|
s64 b = stack[--stack_head];
|
|
|
|
stack[stack_head++] = (b < a ? 1 : 0);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case DW_OP_ne: {
|
|
|
|
s64 a = stack[--stack_head];
|
|
|
|
s64 b = stack[--stack_head];
|
|
|
|
stack[stack_head++] = (b != a ? 1 : 0);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case DW_OP_lit0 ... DW_OP_lit31: {
|
|
|
|
u32 value = op - DW_OP_lit0;
|
|
|
|
stack[stack_head++] = value;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case DW_OP_reg0 ... DW_OP_reg31: {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case DW_OP_breg0 ... DW_OP_breg31: {
|
|
|
|
s32 offset;
|
|
|
|
increment = decode_leb128s(at, &offset);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case DW_OP_regx: {
|
|
|
|
u32 reg;
|
|
|
|
increment = decode_leb128(at, ®);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case DW_OP_fbreg: {
|
|
|
|
s32 offset;
|
|
|
|
increment = decode_leb128s(at, &offset);
|
|
|
|
stack[stack_head++] = offset;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case DW_OP_bregx: {
|
|
|
|
u32 reg;
|
|
|
|
s32 offset;
|
|
|
|
|
|
|
|
increment = decode_leb128(at, ®);
|
|
|
|
increment += decode_leb128s(at, &offset);
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case DW_OP_piece: {
|
|
|
|
u32 size;
|
|
|
|
increment = decode_leb128(at, &size);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case DW_OP_deref_size: {
|
|
|
|
u8 size = *at;
|
|
|
|
increment = 1;
|
|
|
|
u64 address = stack[--stack_head];
|
|
|
|
s64 value;
|
|
|
|
memcpy(&value, (void *) address, size); // TODO: surely not this..
|
|
|
|
stack[stack_head++] = value;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case DW_OP_xderef_size: {
|
|
|
|
u8 size = *at;
|
|
|
|
increment = 1;
|
|
|
|
/*
|
|
|
|
TODO: The top two stack elements are popped, and a data item is retrieved through an implementation-defined address calculation and pushed as the new stack top. The size of the data retrieved from the dereferenced address is the size of an address on the target machine.
|
|
|
|
*/
|
|
|
|
stack_head -= 2;
|
|
|
|
s64 value = 0xdeadbeef;
|
|
|
|
stack[stack_head++] = value;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case DW_OP_nop: {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case DW_OP_push_object_address: {
|
|
|
|
/*
|
|
|
|
TODO: The DW_OP_push_object_address operation pushes the address of the object currently being evaluated as part of evaluation of a user presented expression. This object may correspond to an independent variable described by its own debugging information entry or it may be a component of an array, structure, or class whose address has been dynamically determined by an earlier step during user expression evaluation.
|
|
|
|
*/
|
|
|
|
s64 value = 0xdeadbeef;
|
|
|
|
stack[stack_head++] = value;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case DW_OP_call2: {
|
|
|
|
u16 offset;
|
|
|
|
memcpy(&offset, at, 2);
|
|
|
|
increment = 2;
|
|
|
|
DIE("i can't call2\n");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case DW_OP_call4: {
|
|
|
|
u32 offset;
|
|
|
|
memcpy(&offset, at, 4);
|
|
|
|
increment = 4;
|
|
|
|
DIE("i can't call4\n");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case DW_OP_call_ref: {
|
|
|
|
u32 offset; // u64 on 64-bit dwarf !
|
|
|
|
memcpy(&offset, at, 4);
|
|
|
|
increment = 4;
|
|
|
|
DIE("i can't call_ref\n");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case DW_OP_form_tls_address: {
|
|
|
|
/*
|
|
|
|
TODO: The DW_OP_form_tls_address operation pops a value from the stack, translates it into an address in the current thread's thread-local storage block, and pushes the address. If the DWARF expression containing the DW_OP_form_tls_address operation belongs to the main executable's DWARF info, the operation uses the main executable's thread-local storage block; if the expression belongs to a shared library's DWARF info, then it uses that shared library's thread-local storage block.
|
|
|
|
*/
|
|
|
|
s64 top = stack[--stack_head];
|
|
|
|
s64 value = 0xdeadbeef;
|
|
|
|
stack[stack_head++] = value;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case DW_OP_call_frame_cfa: {
|
|
|
|
s64 address = 0xdeadbeef; // TODO: get CFA address
|
|
|
|
stack[stack_head++] = address;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case DW_OP_bit_piece: {
|
|
|
|
u32 size;
|
|
|
|
u32 offset;
|
|
|
|
|
|
|
|
increment = decode_leb128(at, &size);
|
|
|
|
increment += decode_leb128(at, &offset);
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case DW_OP_implicit_value: {
|
|
|
|
u32 size;
|
|
|
|
increment = decode_leb128(at, &size);
|
|
|
|
increment += size;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case DW_OP_stack_value: {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case DW_OP_lo_user: {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case DW_OP_hi_user: {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
default: {
|
|
|
|
printf("unknown dwarf op %d\n", op);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
at += increment;
|
|
|
|
}
|
|
|
|
|
|
|
|
result = stack[stack_head - 1];
|
|
|
|
|
|
|
|
return(result);
|
|
|
|
}
|
|
|
|
|
|
|
|
static u32
|
|
|
|
read_actual_debug_data(u8 *file, u64 string_offset, u32 address_size, u32 form, u64 data_offset, u64 *value)
|
|
|
|
{
|
|
|
|
u32 increment = 0;
|
|
|
|
|
|
|
|
switch (form) {
|
|
|
|
case DW_FORM_sec_offset:
|
|
|
|
case DW_FORM_strp: {
|
|
|
|
u32 offset;
|
|
|
|
memcpy(&offset, file + data_offset, 4);
|
|
|
|
char *str = (char *) (file + string_offset + offset);
|
|
|
|
*value = (u64) str;
|
|
|
|
increment = 4; // 8 bytes for x64 DWARF!
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case DW_FORM_addr: {
|
|
|
|
memcpy(value, file + data_offset, address_size);
|
|
|
|
increment = address_size;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case DW_FORM_string: {
|
|
|
|
char *ptr = (char *) (file + data_offset);
|
|
|
|
*value = (u64) ptr;
|
|
|
|
increment = strlen(ptr) + 1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case DW_FORM_flag_present: {
|
|
|
|
*value = 1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case DW_FORM_ref4: {
|
|
|
|
u32 offset;
|
|
|
|
memcpy(&offset, file + data_offset, 4);
|
|
|
|
increment = 4;
|
|
|
|
//*value = file[base_data_offset + offset];
|
|
|
|
*value = offset;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case DW_FORM_exprloc: {
|
|
|
|
u32 length;
|
|
|
|
increment = decode_leb128(file + data_offset, &length);
|
|
|
|
|
|
|
|
s64 expression_value = compute_dwarf_expression_value(file + data_offset + increment, length);
|
|
|
|
*value = expression_value;
|
|
|
|
|
|
|
|
increment += length;
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case DW_FORM_data1: {
|
|
|
|
*value = file[data_offset];
|
|
|
|
increment = 1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case DW_FORM_data2: {
|
|
|
|
memcpy(value, file + data_offset, 2);
|
|
|
|
increment = 2;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case DW_FORM_data4: {
|
|
|
|
memcpy(value, file + data_offset, 4);
|
|
|
|
increment = 4;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case DW_FORM_data8: {
|
|
|
|
memcpy(value, file + data_offset, 8);
|
|
|
|
increment = 8;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case DW_FORM_sdata: {
|
|
|
|
increment = decode_leb128s(file + data_offset, (s32 *) value);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case DW_FORM_udata: {
|
|
|
|
increment = decode_leb128(file + data_offset, (u32 *) value);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
default: {
|
|
|
|
if (form) {
|
|
|
|
printf("unknown attribute form %d\n", form);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return(increment);
|
|
|
|
}
|
|
|
|
|
|
|
|
static u64
|
|
|
|
read_debug_info_for_compilation_unit(u8 *file, struct mi_debuginfo *dest,
|
|
|
|
u64 debug_info_offset, u64 debug_abbrev_offset, u64 debug_str_offset)
|
|
|
|
{
|
|
|
|
struct dwarf_debug_info_header_x32 di_header = { 0 };
|
|
|
|
u32 header_size = sizeof(di_header);
|
|
|
|
memcpy(&di_header, file + debug_info_offset, header_size);
|
|
|
|
|
|
|
|
u64 abbrev_offset = debug_abbrev_offset + di_header.debug_abbrev_offset;
|
|
|
|
u64 data_offset = debug_info_offset + header_size;
|
|
|
|
|
|
|
|
u32 code, tag;
|
|
|
|
u64 schema_offset;
|
|
|
|
u32 depth = 0;
|
|
|
|
//u64 base_data_offset = data_offset - header_size;
|
|
|
|
|
|
|
|
int vars_from = dest->var_count;
|
|
|
|
int types_from = dest->type_count;
|
|
|
|
|
|
|
|
struct mi_compunit *comp_unit = dest->compilation_units + dest->cu_count;
|
|
|
|
struct mi_function *func = dest->functions + dest->func_count;
|
|
|
|
struct mi_variable *variable = dest->variables + dest->var_count;
|
|
|
|
struct mi_type *type = dest->types + dest->type_count;
|
|
|
|
|
|
|
|
comp_unit->functions_from = dest->func_count;
|
|
|
|
|
|
|
|
for (;;) {
|
|
|
|
s64 record_offset = data_offset - debug_info_offset;
|
|
|
|
|
|
|
|
data_offset += decode_leb128(file + data_offset, &code);
|
|
|
|
|
|
|
|
if (code == 0) {
|
|
|
|
if (depth > 1) {
|
|
|
|
--depth;
|
|
|
|
continue;
|
|
|
|
} else {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
schema_offset = abbrev_entry_offset(file, abbrev_offset, code);
|
|
|
|
schema_offset += decode_leb128(file + schema_offset, NULL);
|
|
|
|
schema_offset += decode_leb128(file + schema_offset, &tag);
|
|
|
|
|
|
|
|
//printf("%d %s\n", code, tag_to_str(tag));
|
|
|
|
|
|
|
|
u32 has_children = file[schema_offset++];
|
|
|
|
if (has_children) {
|
|
|
|
++depth;
|
|
|
|
}
|
|
|
|
|
|
|
|
u32 attribute, form;
|
|
|
|
|
|
|
|
do {
|
|
|
|
schema_offset += decode_leb128(file + schema_offset, &attribute);
|
|
|
|
schema_offset += decode_leb128(file + schema_offset, &form);
|
|
|
|
|
|
|
|
u64 value;
|
|
|
|
|
|
|
|
data_offset += read_actual_debug_data(file, debug_str_offset, di_header.address_size, form, data_offset, &value);
|
|
|
|
|
|
|
|
if (tag == DW_TAG_compile_unit) {
|
|
|
|
if (attribute == DW_AT_low_pc) {
|
|
|
|
comp_unit->low_pc = value;
|
|
|
|
} else if (attribute == DW_AT_high_pc) {
|
|
|
|
comp_unit->high_pc = value;
|
|
|
|
} else if (attribute == DW_AT_comp_dir) {
|
|
|
|
comp_unit->source.comp_dir = (char *) value;
|
|
|
|
}
|
|
|
|
} else if (tag == DW_TAG_subprogram) {
|
|
|
|
if (attribute == DW_AT_name) {
|
|
|
|
func->name = (char *) value;
|
|
|
|
} else if (attribute == DW_AT_low_pc) {
|
|
|
|
func->low_pc = value;
|
|
|
|
} else if (attribute == DW_AT_high_pc) {
|
|
|
|
func->high_pc = value;
|
|
|
|
}
|
|
|
|
} else if (tag == DW_TAG_variable) {
|
|
|
|
if (attribute == DW_AT_name) {
|
|
|
|
variable->name = (char *) value;
|
|
|
|
} else if (attribute == DW_AT_location) {
|
|
|
|
variable->location = value;
|
|
|
|
} else if (attribute == DW_AT_type) {
|
|
|
|
variable->type = value;
|
|
|
|
}
|
|
|
|
} else if (tag == DW_TAG_base_type) {
|
|
|
|
type->_offset = record_offset;
|
|
|
|
if (attribute == DW_AT_name) {
|
|
|
|
type->name = (char *) value;
|
|
|
|
} else if (attribute == DW_AT_byte_size) {
|
|
|
|
type->size = value;
|
|
|
|
} else if (attribute == DW_AT_encoding) {
|
|
|
|
enum dwarf_type_encoding encoding = value;
|
|
|
|
switch (encoding) {
|
|
|
|
case DW_ATE_address: {
|
|
|
|
type->encoding = MI_ADDRESS;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case DW_ATE_boolean: {
|
|
|
|
type->encoding = MI_BOOLEAN;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case DW_ATE_float: {
|
|
|
|
type->encoding = MI_FLOAT;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case DW_ATE_signed_char:
|
|
|
|
case DW_ATE_signed: {
|
|
|
|
type->encoding = MI_SIGNED;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case DW_ATE_unsigned_char:
|
|
|
|
case DW_ATE_unsigned: {
|
|
|
|
type->encoding = MI_UNSIGNED;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
default: {
|
|
|
|
DIE("unexpected type encoding!\n");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} while (attribute != 0 || form != 0);
|
|
|
|
|
|
|
|
// NOTE(aolo2): DIE completely processed, finish it up
|
|
|
|
if (tag == DW_TAG_subprogram) {
|
|
|
|
func->high_pc = func->low_pc + func->high_pc;
|
|
|
|
func->variables_from = dest->var_count;
|
|
|
|
++comp_unit->functions_count;
|
|
|
|
++dest->func_count;
|
|
|
|
++func;
|
|
|
|
} else if (tag == DW_TAG_variable) {
|
|
|
|
struct mi_function *parent = func - 1;
|
|
|
|
++parent->variables_count;
|
|
|
|
++dest->var_count;
|
|
|
|
++variable;
|
|
|
|
} else if (tag == DW_TAG_base_type) {
|
|
|
|
++dest->type_count;
|
|
|
|
++type;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Resolve types
|
|
|
|
for (int v = vars_from; v < dest->var_count; ++v) {
|
|
|
|
struct mi_variable *variable = dest->variables + v;
|
|
|
|
|
|
|
|
for (int t = types_from; t < dest->type_count; ++t) {
|
|
|
|
struct mi_type *type = dest->types + t;
|
|
|
|
|
|
|
|
if (type->_offset == variable->type) {
|
|
|
|
variable->type = t;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
comp_unit->high_pc = comp_unit->low_pc + comp_unit->high_pc;
|
|
|
|
++dest->cu_count;
|
|
|
|
|
|
|
|
return(di_header.length + 4);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
parse_debug_info(u8 *file, struct mi_debuginfo *dest)
|
|
|
|
{
|
|
|
|
struct elf_section_table_entry_x64 debug_info = get_section_entry(file, ".debug_info");
|
|
|
|
printf("Found .debug_info at offset %#lx\n", debug_info.offset_in_file);
|
|
|
|
|
|
|
|
u64 debug_abbrev_offset = get_section_offset(file, ".debug_abbrev");
|
|
|
|
printf("Found .debug_abbrev at offset %#lx\n", debug_abbrev_offset);
|
|
|
|
|
|
|
|
u64 debug_str_offset = get_section_offset(file, ".debug_str");
|
|
|
|
printf("Found .debug_str at offset %#lx\n", debug_str_offset);
|
|
|
|
|
|
|
|
u64 at = debug_info.offset_in_file;
|
|
|
|
u64 read = 0;
|
|
|
|
|
|
|
|
for (;;) {
|
|
|
|
u64 size = read_debug_info_for_compilation_unit(file, dest, at, debug_abbrev_offset, debug_str_offset);
|
|
|
|
read += size;
|
|
|
|
at += size;
|
|
|
|
if (read >= debug_info.size) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static u64
|
|
|
|
read_debug_line_for_compilation_unit(u8 *file, u64 dl_offset, struct mi_compunit *unit)
|
|
|
|
{
|
|
|
|
struct mi_sourcepoint *dest = unit->source.sp_table;
|
|
|
|
int *dest_size = &unit->source.sp_count;
|
|
|
|
|
|
|
|
struct mi_sourcefile *dest_files = unit->source.source_files;
|
|
|
|
int *dest_files_size = &unit->source.source_file_count;
|
|
|
|
|
|
|
|
char **dest_directories = unit->source.source_directories;
|
|
|
|
int *dest_directories_size = &unit->source.source_dirs_count;
|
|
|
|
|
|
|
|
struct dwarf_debug_line_header_v3_x32 header = { 0 };
|
|
|
|
memcpy(&header, file + dl_offset, 15); /* all fixed-size info */
|
|
|
|
|
|
|
|
dl_offset += 15;
|
|
|
|
|
|
|
|
header.standard_opcode_lengths = malloc(header.opcode_base - 1);
|
|
|
|
memcpy(header.standard_opcode_lengths, file + dl_offset, header.opcode_base - 1);
|
|
|
|
|
|
|
|
dl_offset += header.opcode_base - 1;
|
|
|
|
|
|
|
|
/* "Each entry is a null-terminated string containing a full path name. The last entry
|
|
|
|
is followed by a single null byte." */
|
|
|
|
u8 ndirs = 0;
|
|
|
|
u8 nfiles = 0;
|
|
|
|
|
|
|
|
u8 *p = file + dl_offset;
|
|
|
|
|
|
|
|
while (*p != 0) {
|
|
|
|
if (dest_directories) {
|
|
|
|
dest_directories[ndirs] = (char *) p;
|
|
|
|
}
|
|
|
|
|
|
|
|
++ndirs;
|
|
|
|
|
|
|
|
while (*p != 0) {
|
|
|
|
++p;
|
|
|
|
}
|
|
|
|
|
|
|
|
++p;
|
|
|
|
}
|
|
|
|
|
|
|
|
header.ndirs = ndirs;
|
|
|
|
header.include_directories = 0; // malloc(ndirs * sizeof(char *));
|
|
|
|
|
|
|
|
++p;
|
|
|
|
|
|
|
|
while (*p != 0) {
|
|
|
|
/* null-terminated string */
|
|
|
|
if (dest_files) {
|
|
|
|
dest_files[nfiles].filename = (char *) p;
|
|
|
|
}
|
|
|
|
|
|
|
|
while (*p != 0) {
|
|
|
|
++p;
|
|
|
|
}
|
|
|
|
|
|
|
|
++p;
|
|
|
|
|
|
|
|
u64 offset = 0;
|
|
|
|
u32 dir_index = 0;
|
|
|
|
u32 dummy = 0;
|
|
|
|
|
|
|
|
offset += decode_leb128(p, &dir_index);
|
|
|
|
offset += decode_leb128(p, &dummy);
|
|
|
|
offset += decode_leb128(p, &dummy);
|
|
|
|
|
|
|
|
if (dest_files) {
|
|
|
|
if (dir_index) {
|
|
|
|
dest_files[nfiles].dir = dest_directories[dir_index - 1];
|
|
|
|
} else {
|
|
|
|
/* The current directory of the compilation is understood to be the zeroth entry and is not explicitly represented. */
|
|
|
|
dest_files[nfiles].dir = ".";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
++nfiles;
|
|
|
|
|
|
|
|
p += offset;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (dest_files_size) {
|
|
|
|
*dest_files_size = nfiles;
|
|
|
|
}
|
|
|
|
|
|
|
|
dl_offset = p - file + 1;
|
|
|
|
|
|
|
|
u8 opcode;
|
|
|
|
enum dwarf_lnp_opcode opcode_regular = 0;
|
|
|
|
enum dwarf_lnp_opcode_extended opcode_extended = 0;
|
|
|
|
|
|
|
|
p = file + dl_offset;
|
|
|
|
|
|
|
|
struct dwarf_line_number_state state = { 0 };
|
|
|
|
|
|
|
|
state.file = 1;
|
|
|
|
state.line = 1;
|
|
|
|
state.is_stmt = header.default_is_stmt;
|
|
|
|
|
|
|
|
u64 pc_count = 0;
|
|
|
|
|
|
|
|
do {
|
|
|
|
opcode = *p;
|
|
|
|
++p;
|
|
|
|
|
|
|
|
u8 nops = 0;
|
|
|
|
if (opcode) {
|
|
|
|
if (opcode <= header.opcode_base) {
|
|
|
|
/* standart opcode */
|
|
|
|
opcode_regular = opcode;
|
|
|
|
|
|
|
|
switch (opcode_regular) {
|
|
|
|
case DW_LNS_copy: {
|
|
|
|
state.basic_block = 0;
|
|
|
|
state.prologue_end = 0;
|
|
|
|
state.epilogue_begin = 0;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case DW_LNS_advance_pc: {
|
|
|
|
u32 operand;
|
|
|
|
p += decode_leb128(p, &operand);
|
|
|
|
operand *= header.minimum_instruction_length;
|
|
|
|
state.pc += operand;
|
|
|
|
|
|
|
|
if (dest) {
|
|
|
|
struct mi_sourcepoint *sp = dest + pc_count;
|
|
|
|
sp->pc = state.pc;
|
|
|
|
sp->line = state.line;
|
|
|
|
sp->column = state.column;
|
|
|
|
sp->file = state.file;
|
|
|
|
}
|
|
|
|
|
|
|
|
++pc_count;
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case DW_LNS_advance_line: {
|
|
|
|
s32 operand;
|
|
|
|
p += decode_leb128s(p, &operand);
|
|
|
|
state.line += operand;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case DW_LNS_set_file: {
|
|
|
|
u32 operand;
|
|
|
|
p += decode_leb128(p, &operand);
|
|
|
|
operand *= header.minimum_instruction_length;
|
|
|
|
state.file = operand;
|
|
|
|
|
|
|
|
// printf("Switch to file %s in directory %d\n", header.files[state.file - 1].name, header.files[state.file - 1].directory_index);
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case DW_LNS_set_column: {
|
|
|
|
u32 operand;
|
|
|
|
p += decode_leb128(p, &operand);
|
|
|
|
operand *= header.minimum_instruction_length;
|
|
|
|
state.column = operand;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case DW_LNS_negate_stmt: {
|
|
|
|
state.is_stmt = 1 - state.is_stmt;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case DW_LNS_set_basic_block: {
|
|
|
|
state.basic_block = 1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case DW_LNS_const_add_pc: {
|
|
|
|
u8 adjusted = 255 - header.opcode_base;
|
|
|
|
s32 address_increment = (adjusted / header.line_range) * header.minimum_instruction_length;
|
|
|
|
state.pc += address_increment;
|
|
|
|
|
|
|
|
if (dest) {
|
|
|
|
struct mi_sourcepoint *sp = dest + pc_count;
|
|
|
|
sp->pc = state.pc;
|
|
|
|
sp->line = state.line;
|
|
|
|
sp->column = state.column;
|
|
|
|
sp->file = state.file;
|
|
|
|
}
|
|
|
|
|
|
|
|
++pc_count;
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case DW_LNS_fixed_advance_pc: {
|
|
|
|
u16 operand;
|
|
|
|
memcpy(&operand, p, 2);
|
|
|
|
p += 2;
|
|
|
|
state.pc += operand;
|
|
|
|
|
|
|
|
if (dest) {
|
|
|
|
struct mi_sourcepoint *sp = dest + pc_count;
|
|
|
|
sp->pc = state.pc;
|
|
|
|
sp->line = state.line;
|
|
|
|
sp->column = state.column;
|
|
|
|
sp->file = state.file;
|
|
|
|
}
|
|
|
|
|
|
|
|
++pc_count;
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case DW_LNS_set_prologue_end: {
|
|
|
|
state.prologue_end = 1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case DW_LNS_set_epilogue_begin: {
|
|
|
|
state.epilogue_begin = 1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case DW_LNS_set_isa: {
|
|
|
|
u32 operand;
|
|
|
|
p += decode_leb128(p, &operand);
|
|
|
|
operand *= header.minimum_instruction_length;
|
|
|
|
state.isa = operand;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
/* special opcode */
|
|
|
|
u8 adjusted = opcode - header.opcode_base;
|
|
|
|
s32 address_increment = (adjusted / header.line_range) * header.minimum_instruction_length;
|
|
|
|
s32 line_increment = header.line_base + (adjusted % header.line_range);
|
|
|
|
|
|
|
|
state.pc += address_increment;
|
|
|
|
state.line += line_increment;
|
|
|
|
state.basic_block = 0;
|
|
|
|
state.prologue_end = 0;
|
|
|
|
state.epilogue_begin = 0;
|
|
|
|
state.discriminator = 0;
|
|
|
|
|
|
|
|
if (dest) {
|
|
|
|
struct mi_sourcepoint *sp = dest + pc_count;
|
|
|
|
sp->pc = state.pc;
|
|
|
|
sp->line = state.line;
|
|
|
|
sp->column = state.column;
|
|
|
|
sp->file = state.file;
|
|
|
|
}
|
|
|
|
|
|
|
|
++pc_count;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
/* extended opcode */
|
|
|
|
u32 instruction_length;
|
|
|
|
p += decode_leb128(p, &instruction_length);
|
|
|
|
opcode = *p;
|
|
|
|
opcode_extended = opcode;
|
|
|
|
|
|
|
|
++p;
|
|
|
|
|
|
|
|
switch (opcode_extended) {
|
|
|
|
case DW_LNE_end_sequence: {
|
|
|
|
state.end_sequence = 1;
|
|
|
|
//printf("END: %lx -> %d\n", state.pc, state.line);
|
|
|
|
memset(&state, 0, sizeof(state));
|
|
|
|
state.file = 1;
|
|
|
|
state.line = 1;
|
|
|
|
state.is_stmt = header.default_is_stmt;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case DW_LNE_set_address: {
|
|
|
|
u64 address;
|
|
|
|
memcpy(&address, p, 8);
|
|
|
|
state.pc = address;
|
|
|
|
p += 8;
|
|
|
|
|
|
|
|
if (dest) {
|
|
|
|
struct mi_sourcepoint *sp = dest + pc_count;
|
|
|
|
sp->pc = state.pc;
|
|
|
|
sp->line = state.line;
|
|
|
|
sp->column = state.column;
|
|
|
|
sp->file = state.file;
|
|
|
|
}
|
|
|
|
|
|
|
|
++pc_count;
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case DW_LNE_define_file: {
|
|
|
|
struct dwarf_debug_line_file_info f = { 0 };
|
|
|
|
f.name = (char *) p;
|
|
|
|
|
|
|
|
while (*p != 0) {
|
|
|
|
++p;
|
|
|
|
}
|
|
|
|
++p;
|
|
|
|
|
|
|
|
p += decode_leb128(p, &f.directory_index);
|
|
|
|
p += decode_leb128(p, &f.time_modified);
|
|
|
|
p += decode_leb128(p, &f.file_size);
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case DW_LNE_set_discriminator: {
|
|
|
|
u32 operand;
|
|
|
|
p += decode_leb128(p, &operand);
|
|
|
|
operand *= header.minimum_instruction_length;
|
|
|
|
state.discriminator = operand;
|
|
|
|
break;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//if (state.pc >= pc) {
|
|
|
|
//return(last_line);
|
|
|
|
//}
|
|
|
|
|
|
|
|
} while (opcode_extended != DW_LNE_end_sequence);
|
|
|
|
|
|
|
|
if (dest_size) {
|
|
|
|
*dest_size = pc_count;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (dest_directories_size) {
|
|
|
|
*dest_directories_size = ndirs;
|
|
|
|
}
|
|
|
|
|
|
|
|
return(header.length + 4);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
parse_debug_line(u8 *file, struct mi_debuginfo *debug)
|
|
|
|
{
|
|
|
|
struct elf_section_table_entry_x64 debug_line = get_section_entry(file, ".debug_line");
|
|
|
|
|
|
|
|
u64 at = debug_line.offset_in_file;
|
|
|
|
u64 read = 0;
|
|
|
|
|
|
|
|
struct mi_compunit *unit = debug->compilation_units;
|
|
|
|
|
|
|
|
// count
|
|
|
|
for (;;) {
|
|
|
|
u64 size = read_debug_line_for_compilation_unit(file, at, unit);
|
|
|
|
unit->source.sp_table = calloc(1, unit->source.sp_count * sizeof(struct mi_sourcepoint));
|
|
|
|
unit->source.source_directories = calloc(1, unit->source.source_dirs_count * sizeof(char *));
|
|
|
|
unit->source.source_files = calloc(1, unit->source.source_file_count * sizeof(struct mi_sourcefile));
|
|
|
|
read += size;
|
|
|
|
at += size;
|
|
|
|
++unit;
|
|
|
|
if (read >= debug_line.size) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
unit = debug->compilation_units;
|
|
|
|
at = debug_line.offset_in_file;
|
|
|
|
read = 0;
|
|
|
|
|
|
|
|
// fill
|
|
|
|
for (;;) {
|
|
|
|
u64 size = read_debug_line_for_compilation_unit(file, at, unit);
|
|
|
|
read += size;
|
|
|
|
at += size;
|
|
|
|
++unit;
|
|
|
|
if (read >= debug_line.size) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static u64
|
|
|
|
get_executable_base_address(u8 *elf_file, int pid)
|
|
|
|
{
|
|
|
|
char path[256] = { 0 };
|
|
|
|
|
|
|
|
snprintf(path, 256, "/proc/%d/maps", pid);
|
|
|
|
|
|
|
|
FILE *maps_file = fopen(path, "rb");
|
|
|
|
if (!maps_file) {
|
|
|
|
DIE("proc map not found\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
struct elf_header_x64 header = { 0 };
|
|
|
|
memcpy(&header, elf_file, sizeof(header));
|
|
|
|
|
|
|
|
u64 elf_offset = 0;
|
|
|
|
|
|
|
|
for (int i = 0; i < header.header_table_entry_count; ++i) {
|
|
|
|
struct elf_header_table_entry_x64 header_entry = { 0 };
|
|
|
|
u64 offset = header.header_table_offset + header.header_table_entry_size * i;
|
|
|
|
memcpy(&header_entry, elf_file + offset, sizeof(header_entry));
|
|
|
|
|
|
|
|
//printf("%#018lx %s\n", header_entry.segment_offset, header_entry.flags & PF_X ? "E" : "");
|
|
|
|
|
|
|
|
if (header_entry.flags & PF_X) {
|
|
|
|
elf_offset = header_entry.segment_offset;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!elf_offset) {
|
|
|
|
return(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
size_t len;
|
|
|
|
char *line = malloc(4096);
|
|
|
|
|
|
|
|
while ((len = getline(&line, &len, maps_file)) != -1UL) {
|
|
|
|
u64 base;
|
|
|
|
u64 end;
|
|
|
|
u64 offset;
|
|
|
|
|
|
|
|
char *at = line;
|
|
|
|
|
|
|
|
base = strtoll(at, &at, 16);
|
|
|
|
|
|
|
|
return(base);
|
|
|
|
|
|
|
|
end = strtoll(at + 1, &at, 16);
|
|
|
|
(void) end;
|
|
|
|
|
|
|
|
while (*at < '0' || '9' < *at) ++at;
|
|
|
|
|
|
|
|
offset = strtoll(at, &at, 16);
|
|
|
|
|
|
|
|
if (offset == elf_offset) {
|
|
|
|
return(base);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
free(line);
|
|
|
|
|
|
|
|
return(0);
|
|
|
|
}
|