A.Olokhtonov
3 years ago
commit
edd1067fde
7 changed files with 1703 additions and 0 deletions
@ -0,0 +1,2 @@ |
|||||||
|
debug: |
||||||
|
gcc main.c -g -O0 -o build/trace -Wall -Wextra -Wno-unused-variable -Wno-unused-function -Wno-switch |
@ -0,0 +1,675 @@ |
|||||||
|
static u64 |
||||||
|
get_section_offset(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)); |
||||||
|
int a = 1; |
||||||
|
} |
||||||
|
|
||||||
|
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; |
||||||
|
u64 debug_info_offset = 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) { |
||||||
|
debug_info_offset = section_entry.offset_in_file; |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
return(debug_info_offset); |
||||||
|
} |
||||||
|
|
||||||
|
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; |
||||||
|
|
||||||
|
while (1) { |
||||||
|
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; |
||||||
|
|
||||||
|
while (1) { |
||||||
|
byte = at[offset++]; |
||||||
|
|
||||||
|
result |= ((byte & 127) << shift); |
||||||
|
|
||||||
|
if ((byte & 128) == 0) { |
||||||
|
break; |
||||||
|
} |
||||||
|
|
||||||
|
shift += 7; |
||||||
|
} |
||||||
|
|
||||||
|
if ((shift < size) && (byte & 128)) { |
||||||
|
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++]; |
||||||
|
|
||||||
|
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 u64 |
||||||
|
find_subroutine_offset(u8 *file, u64 header_size, u8 address_size, |
||||||
|
u64 string_offset, u64 abbrev_offset, u64 data_offset, |
||||||
|
char *subroutine) |
||||||
|
{ |
||||||
|
u32 code, tag; |
||||||
|
u64 schema_offset; |
||||||
|
u32 depth = 0; |
||||||
|
u64 original_data_offset = data_offset; |
||||||
|
|
||||||
|
int found_sr = 0; |
||||||
|
|
||||||
|
do { |
||||||
|
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); |
||||||
|
|
||||||
|
if (attribute) { |
||||||
|
//printf("\t%s ", attribute_to_str(attribute));
|
||||||
|
} |
||||||
|
|
||||||
|
switch (form) { |
||||||
|
case DW_FORM_sec_offset: |
||||||
|
case DW_FORM_strp: { |
||||||
|
u32 data = file[data_offset]; |
||||||
|
data_offset += 4; // 8 bytes for x64 DWARF!
|
||||||
|
|
||||||
|
if (form == DW_FORM_strp) { |
||||||
|
char *str = (char *) file + string_offset + data; |
||||||
|
//printf("(indirect string, offset: %#x): %s\n", data, str);
|
||||||
|
if (tag == DW_TAG_subprogram) { |
||||||
|
if (strcmp(str, subroutine) == 0) { |
||||||
|
found_sr = 1; |
||||||
|
} |
||||||
|
} |
||||||
|
} else { |
||||||
|
//printf("%#x\n", data);
|
||||||
|
} |
||||||
|
|
||||||
|
break; |
||||||
|
} |
||||||
|
|
||||||
|
case DW_FORM_addr: { |
||||||
|
u64 data = 0; |
||||||
|
memcpy(&data, file + data_offset, address_size); |
||||||
|
data_offset += address_size; |
||||||
|
//printf("%#lx\n", data);
|
||||||
|
|
||||||
|
if (tag == DW_TAG_subprogram && found_sr == 1 && attribute == DW_AT_low_pc) { |
||||||
|
return(data); |
||||||
|
} |
||||||
|
|
||||||
|
break; |
||||||
|
}; |
||||||
|
|
||||||
|
case DW_FORM_string: { |
||||||
|
char *data = (char *) file + data_offset; |
||||||
|
data_offset += strlen(data) + 1; |
||||||
|
//printf("%s\n", data);
|
||||||
|
break; |
||||||
|
} |
||||||
|
|
||||||
|
case DW_FORM_flag_present: { |
||||||
|
int data = 1; |
||||||
|
//printf("Flag = 1\n");
|
||||||
|
break; |
||||||
|
} |
||||||
|
|
||||||
|
case DW_FORM_ref4: { |
||||||
|
u32 data = file[data_offset]; |
||||||
|
data_offset += 4; |
||||||
|
//printf("%#x\n", data);
|
||||||
|
u32 referenced_data = file[original_data_offset - header_size + data]; |
||||||
|
break; |
||||||
|
} |
||||||
|
|
||||||
|
case DW_FORM_exprloc: { |
||||||
|
u32 length; |
||||||
|
data_offset += decode_leb128(file + data_offset, &length); |
||||||
|
//printf("%d byte block:", length);
|
||||||
|
|
||||||
|
for (u32 i = 0; i < length; ++i) { |
||||||
|
//printf(" %x", file[data_offset + i]);
|
||||||
|
} |
||||||
|
//printf("\n");
|
||||||
|
|
||||||
|
data_offset += length; |
||||||
|
|
||||||
|
break; |
||||||
|
} |
||||||
|
|
||||||
|
case DW_FORM_data1: { |
||||||
|
u8 data = file[data_offset]; |
||||||
|
data_offset += 1; |
||||||
|
//printf("%#x\n", data);
|
||||||
|
break; |
||||||
|
}; |
||||||
|
|
||||||
|
case DW_FORM_data2: { |
||||||
|
u16 data = file[data_offset]; |
||||||
|
data_offset += 2; |
||||||
|
//printf("%#x\n", data);
|
||||||
|
break; |
||||||
|
}; |
||||||
|
|
||||||
|
case DW_FORM_data4: { |
||||||
|
u32 data = file[data_offset]; |
||||||
|
data_offset += 4; |
||||||
|
//printf("%#x\n", data);
|
||||||
|
break; |
||||||
|
}; |
||||||
|
|
||||||
|
case DW_FORM_data8: { |
||||||
|
u64 data = file[data_offset]; |
||||||
|
data_offset += 8; |
||||||
|
//printf("%#lx\n", data);
|
||||||
|
break; |
||||||
|
}; |
||||||
|
|
||||||
|
default: { |
||||||
|
if (form) { |
||||||
|
//printf("unknown attribute form %d\n", form);
|
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} while (attribute != 0 || form != 0); |
||||||
|
} while (1); |
||||||
|
|
||||||
|
return(0); |
||||||
|
} |
||||||
|
|
||||||
|
static u64 |
||||||
|
read_line_number_info(u8 *file, u64 dl_offset, u64 pc) |
||||||
|
{ |
||||||
|
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) { |
||||||
|
++ndirs; |
||||||
|
while (*p != 0) { |
||||||
|
++p; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
header.ndirs = ndirs; |
||||||
|
header.include_directories = 0; // malloc(ndirs * sizeof(char *));
|
||||||
|
|
||||||
|
dl_offset += (p - (file + dl_offset)) + 1; |
||||||
|
|
||||||
|
|
||||||
|
p = file + dl_offset; |
||||||
|
while (*p != 0) { |
||||||
|
++nfiles; |
||||||
|
|
||||||
|
/* null-terminated string */ |
||||||
|
while (*p != 0) { |
||||||
|
++p; |
||||||
|
} |
||||||
|
|
||||||
|
++p; |
||||||
|
|
||||||
|
u64 offset = 0; |
||||||
|
u32 dummy = 0; |
||||||
|
|
||||||
|
offset += decode_leb128(p, &dummy); |
||||||
|
offset += decode_leb128(p, &dummy); |
||||||
|
offset += decode_leb128(p, &dummy); |
||||||
|
|
||||||
|
p += offset; |
||||||
|
} |
||||||
|
|
||||||
|
header.files = malloc(nfiles * sizeof(struct dwarf_debug_line_file_info)); |
||||||
|
header.nfiles = nfiles; |
||||||
|
|
||||||
|
struct dwarf_debug_line_file_info *f = header.files; |
||||||
|
|
||||||
|
p = file + dl_offset; |
||||||
|
while (*p != 0) { |
||||||
|
|
||||||
|
/* null-terminated string */ |
||||||
|
f->name = (char *) p; |
||||||
|
|
||||||
|
while (*p != 0) { |
||||||
|
++p; |
||||||
|
} |
||||||
|
|
||||||
|
++p; |
||||||
|
|
||||||
|
u64 offset = 0; |
||||||
|
u32 dummy = 0; |
||||||
|
|
||||||
|
offset += decode_leb128(p, &f->directory_index); |
||||||
|
offset += decode_leb128(p, &f->time_modified); |
||||||
|
offset += decode_leb128(p, &f->file_size); |
||||||
|
|
||||||
|
p += offset; |
||||||
|
|
||||||
|
++f; |
||||||
|
} |
||||||
|
|
||||||
|
dl_offset = p - file + 1; |
||||||
|
|
||||||
|
u8 opcode; |
||||||
|
enum dwarf_lnp_opcode opcode_regular; |
||||||
|
enum dwarf_lnp_opcode_extended opcode_extended; |
||||||
|
|
||||||
|
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 last_line = 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; |
||||||
|
break; |
||||||
|
} |
||||||
|
|
||||||
|
case DW_LNS_advance_line: { |
||||||
|
s32 operand; |
||||||
|
p += decode_leb128s(p, &operand); |
||||||
|
last_line = state.line; |
||||||
|
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; |
||||||
|
break; |
||||||
|
} |
||||||
|
|
||||||
|
case DW_LNS_fixed_advance_pc: { |
||||||
|
u16 operand; |
||||||
|
memcpy(&operand, p, 2); |
||||||
|
p += 2; |
||||||
|
state.pc += operand; |
||||||
|
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); |
||||||
|
|
||||||
|
last_line = state.line; |
||||||
|
|
||||||
|
state.pc += address_increment; |
||||||
|
state.line += line_increment; |
||||||
|
state.basic_block = 0; |
||||||
|
state.prologue_end = 0; |
||||||
|
state.epilogue_begin = 0; |
||||||
|
state.discriminator = 0; |
||||||
|
} |
||||||
|
} 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; |
||||||
|
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; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// TODO: this is off by one?
|
||||||
|
if (state.pc >= pc) { |
||||||
|
return(last_line); |
||||||
|
} |
||||||
|
|
||||||
|
} while (opcode_extended != DW_LNE_end_sequence); |
||||||
|
|
||||||
|
return(0); |
||||||
|
} |
||||||
|
|
||||||
|
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' || *at > '9') ++at; |
||||||
|
|
||||||
|
offset = strtoll(at, &at, 16); |
||||||
|
|
||||||
|
if (offset == elf_offset) { |
||||||
|
return(base); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
free(line); |
||||||
|
|
||||||
|
return(0); |
||||||
|
} |
||||||
|
|
||||||
|
static u64 |
||||||
|
get_address_of_subroutine(u8 *file, char *sr) |
||||||
|
{ |
||||||
|
u64 debug_info_offset = get_section_offset(file, ".debug_info"); |
||||||
|
printf("Found .debug_info at offset %#lx\n", debug_info_offset); |
||||||
|
|
||||||
|
u64 debug_line_offset = get_section_offset(file, ".debug_line"); |
||||||
|
printf("Found .debug_line at offset %#lx\n", debug_line_offset); |
||||||
|
|
||||||
|
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); |
||||||
|
|
||||||
|
struct dwarf_debug_info_header_x32 di_header = { 0 }; |
||||||
|
memcpy(&di_header, file + debug_info_offset, sizeof(di_header)); |
||||||
|
|
||||||
|
u64 abbrev_offset = debug_abbrev_offset + di_header.debug_abbrev_offset; |
||||||
|
u64 data_offset = debug_info_offset + sizeof(di_header); |
||||||
|
|
||||||
|
u64 result = find_subroutine_offset(file, sizeof(di_header), di_header.address_size, |
||||||
|
debug_str_offset, abbrev_offset, data_offset, sr); |
||||||
|
|
||||||
|
//read_line_number_info(file, debug_line_offset);
|
||||||
|
|
||||||
|
|
||||||
|
return(result); |
||||||
|
} |
||||||
|
|
||||||
|
static u64 |
||||||
|
pc_to_line_number(u8 *file, u64 pc) |
||||||
|
{ |
||||||
|
u64 debug_line_offset = get_section_offset(file, ".debug_line"); |
||||||
|
//printf("Found .debug_line at offset %#lx\n", debug_line_offset);
|
||||||
|
u64 result = read_line_number_info(file, debug_line_offset, pc); |
||||||
|
return(result); |
||||||
|
} |
@ -0,0 +1,375 @@ |
|||||||
|
#include <stdio.h> |
||||||
|
#include <string.h> |
||||||
|
#include <stdlib.h> |
||||||
|
#include <stdint.h> |
||||||
|
#include <signal.h> |
||||||
|
#include <syscall.h> |
||||||
|
|
||||||
|
#include <unistd.h> |
||||||
|
#include <errno.h> |
||||||
|
#include <assert.h> |
||||||
|
#include <fcntl.h> |
||||||
|
|
||||||
|
#include <sys/uio.h> |
||||||
|
#include <sys/types.h> |
||||||
|
#include <sys/user.h> |
||||||
|
#include <sys/ptrace.h> |
||||||
|
#include <sys/wait.h> |
||||||
|
#include <sys/stat.h> |
||||||
|
#include <sys/mman.h> |
||||||
|
#include <sys/personality.h> |
||||||
|
|
||||||
|
typedef uint8_t u8; |
||||||
|
typedef uint16_t u16; |
||||||
|
typedef uint32_t u32; |
||||||
|
typedef uint64_t u64; |
||||||
|
|
||||||
|
typedef int8_t s8; |
||||||
|
typedef int16_t s16; |
||||||
|
typedef int32_t s32; |
||||||
|
typedef int64_t s64; |
||||||
|
|
||||||
|
#define NT_PRSTATUS 1 |
||||||
|
#define DIE(string) do { fprintf(stderr, string); exit(1); } while (0) |
||||||
|
|
||||||
|
#include "structs.h" |
||||||
|
|
||||||
|
struct mi_process { |
||||||
|
pid_t pid; |
||||||
|
|
||||||
|
char *source; |
||||||
|
u64 source_size; |
||||||
|
|
||||||
|
u8 *elf; |
||||||
|
u64 elf_size; |
||||||
|
|
||||||
|
u64 base_address; |
||||||
|
u64 main_address; |
||||||
|
}; |
||||||
|
|
||||||
|
struct mi_registers { |
||||||
|
u64 rbp; |
||||||
|
u64 rbx; |
||||||
|
u64 rax; |
||||||
|
u64 rcx; |
||||||
|
u64 rdx; |
||||||
|
u64 rsi; |
||||||
|
u64 rdi; |
||||||
|
u64 rip; |
||||||
|
u64 rsp; |
||||||
|
|
||||||
|
struct user_regs_struct _sys; |
||||||
|
}; |
||||||
|
|
||||||
|
#include "dwarf.c" |
||||||
|
|
||||||
|
|
||||||
|
static void |
||||||
|
print_word_at_address(int child, u64 address) |
||||||
|
{ |
||||||
|
long word = ptrace(PTRACE_PEEKDATA, child, address, NULL); |
||||||
|
u8 nb[4]; |
||||||
|
memcpy(nb, &word, 4); |
||||||
|
printf("word at %#018lx: {%x %x %x %x}\n", address, nb[0], nb[1], nb[2], nb[3]); |
||||||
|
} |
||||||
|
|
||||||
|
static void |
||||||
|
print_current_line(struct mi_process proc, u64 line) |
||||||
|
{ |
||||||
|
char *source = proc.source; |
||||||
|
u64 size = proc.source_size; |
||||||
|
u64 current_line = 1; |
||||||
|
|
||||||
|
for (u32 i = 0; i < size; ++i) { |
||||||
|
if (current_line == line) { |
||||||
|
int len = 0; |
||||||
|
int start = 0; |
||||||
|
int leading = 1; |
||||||
|
|
||||||
|
for (u32 j = i; j < size; ++j) { |
||||||
|
if (leading && (source[j] == '\t' || source[j] == ' ')) { |
||||||
|
++start; |
||||||
|
} else { |
||||||
|
leading = 0; |
||||||
|
} |
||||||
|
|
||||||
|
if (source[j] == '\n') { |
||||||
|
break; |
||||||
|
} |
||||||
|
|
||||||
|
++len; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
printf("%.*s\n", len - start, source + i + start); |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
if (source[i] == '\n') { |
||||||
|
current_line++; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
static struct mi_process |
||||||
|
process_create(char *path) |
||||||
|
{ |
||||||
|
struct mi_process result = { 0 }; |
||||||
|
|
||||||
|
/* disable ASLR */ |
||||||
|
syscall(SYS_personality, ADDR_NO_RANDOMIZE); |
||||||
|
|
||||||
|
pid_t pid = fork(); |
||||||
|
|
||||||
|
if (pid == -1) { |
||||||
|
DIE("fork error\n"); |
||||||
|
} |
||||||
|
|
||||||
|
if (pid == 0) { |
||||||
|
int rt = ptrace(PTRACE_TRACEME, 0, 0, 0); |
||||||
|
assert(rt == 0); |
||||||
|
char *args[] = { path, NULL }; |
||||||
|
rt = execvp(path, args); |
||||||
|
if (rt == -1) { |
||||||
|
DIE("exevp error\n"); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
u64 source_size; |
||||||
|
u64 elf_size; |
||||||
|
u8 *elf; |
||||||
|
char *source; |
||||||
|
struct stat s; |
||||||
|
int fd; |
||||||
|
|
||||||
|
{ |
||||||
|
fd = open(path, O_RDONLY); |
||||||
|
assert(fd != -1); |
||||||
|
fstat(fd, &s); |
||||||
|
elf_size = s.st_size; |
||||||
|
elf = mmap(0, s.st_size, PROT_READ, MAP_PRIVATE, fd, 0); |
||||||
|
close(fd); |
||||||
|
} |
||||||
|
|
||||||
|
{ |
||||||
|
fd = open("/code/trace-test/traceme.c", O_RDONLY); // TODO
|
||||||
|
assert(fd != -1); |
||||||
|
fstat(fd, &s); |
||||||
|
source_size = s.st_size; |
||||||
|
source = mmap(0, source_size, PROT_READ, MAP_PRIVATE, fd, 0); |
||||||
|
close(fd); |
||||||
|
} |
||||||
|
|
||||||
|
result.elf = elf; |
||||||
|
result.elf_size = elf_size; |
||||||
|
result.source = source; |
||||||
|
result.source_size = source_size; |
||||||
|
result.pid = pid; |
||||||
|
|
||||||
|
return(result); |
||||||
|
} |
||||||
|
|
||||||
|
static struct mi_registers |
||||||
|
get_process_registers(struct mi_process proc) |
||||||
|
{ |
||||||
|
struct user_regs_struct regs = { 0 }; |
||||||
|
struct iovec iov = { ®s, sizeof(regs) }; |
||||||
|
struct mi_registers result = { 0 }; |
||||||
|
|
||||||
|
ptrace(PTRACE_GETREGSET, proc.pid, NT_PRSTATUS, &iov); |
||||||
|
|
||||||
|
result.rax = regs.rax; |
||||||
|
result.rip = regs.rip; |
||||||
|
result.rbp = regs.rbp; |
||||||
|
result.rbx = regs.rbx; |
||||||
|
result.rcx = regs.rcx; |
||||||
|
result.rdx = regs.rdx; |
||||||
|
result.rsi = regs.rsi; |
||||||
|
result.rdi = regs.rdi; |
||||||
|
result.rsp = regs.rsp; |
||||||
|
|
||||||
|
result._sys = regs; |
||||||
|
|
||||||
|
return(result); |
||||||
|
} |
||||||
|
|
||||||
|
static void |
||||||
|
set_process_registers(struct mi_process proc, struct mi_registers regs) |
||||||
|
{ |
||||||
|
struct iovec iov = { ®s._sys, sizeof(regs._sys) }; |
||||||
|
ptrace(PTRACE_GETREGSET, proc.pid, NT_PRSTATUS, &iov); |
||||||
|
} |
||||||
|
|
||||||
|
static void |
||||||
|
print_current_instruction(struct mi_process proc) |
||||||
|
{ |
||||||
|
struct mi_registers regs = get_process_registers(proc); |
||||||
|
|
||||||
|
long wordb = ptrace(PTRACE_PEEKDATA, proc.pid, regs.rip - 4, NULL); |
||||||
|
long word = ptrace(PTRACE_PEEKDATA, proc.pid, regs.rip, NULL); |
||||||
|
long worda = ptrace(PTRACE_PEEKDATA, proc.pid, regs.rip + 4, NULL); |
||||||
|
|
||||||
|
u8 nb[12]; |
||||||
|
memcpy(nb, &wordb, 4); |
||||||
|
memcpy(nb + 4, &word, 4); |
||||||
|
memcpy(nb + 8, &worda, 4); |
||||||
|
|
||||||
|
printf("PC = %#018lx: %x %x %x %x [%x] %x %x %x %x %x %x %x\n", regs.rip, |
||||||
|
nb[0], nb[1], nb[2], nb[3], nb[4], nb[5], nb[6], nb[7], |
||||||
|
nb[8], nb[9], nb[10], nb[11]); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
static void |
||||||
|
command_regs(struct mi_process proc) |
||||||
|
{ |
||||||
|
struct mi_registers regs = get_process_registers(proc); |
||||||
|
printf("rax = %#018lx rcx = %#018lx\n" |
||||||
|
"rbp = %#018lx rbx = %#018lx\n" |
||||||
|
"rdx = %#018lx rsi = %#018lx\n" |
||||||
|
"rdi = %#018lx rip = %#018lx\n" |
||||||
|
"rsp = %#018lx\n", |
||||||
|
regs.rax, regs.rcx, regs.rbp, regs.rbx, |
||||||
|
regs.rdx, regs.rsi, regs.rdi, regs.rip, |
||||||
|
regs.rsp); |
||||||
|
} |
||||||
|
|
||||||
|
static void |
||||||
|
command_step(struct mi_process proc) |
||||||
|
{ |
||||||
|
ptrace(PTRACE_SINGLESTEP, proc.pid, 0, 0); |
||||||
|
waitpid(proc.pid, 0, 0); |
||||||
|
|
||||||
|
#if 1 |
||||||
|
print_current_instruction(proc); |
||||||
|
struct mi_registers regs = get_process_registers(proc); |
||||||
|
u64 line_number = pc_to_line_number(proc.elf, regs.rip - proc.base_address); |
||||||
|
print_current_line(proc, line_number); |
||||||
|
#endif |
||||||
|
} |
||||||
|
|
||||||
|
static void |
||||||
|
command_start(struct mi_process proc) |
||||||
|
{ |
||||||
|
u64 main_address = proc.base_address + proc.main_address; |
||||||
|
|
||||||
|
long saved_instruction = ptrace(PTRACE_PEEKDATA, proc.pid, main_address, NULL); |
||||||
|
long int3_byte = 0x000000cc; |
||||||
|
long int3_word = (saved_instruction & 0xFFFFFF00) | int3_byte; |
||||||
|
|
||||||
|
/* write 0xcc */ |
||||||
|
ptrace(PTRACE_POKEDATA, proc.pid, main_address, int3_word); |
||||||
|
|
||||||
|
/* wait till child hits the interrupt */ |
||||||
|
ptrace(PTRACE_CONT, proc.pid, 0, 0); |
||||||
|
waitpid(proc.pid, 0, 0); |
||||||
|
|
||||||
|
/* restore original instrucion */ |
||||||
|
ptrace(PTRACE_POKEDATA, proc.pid, main_address, saved_instruction); |
||||||
|
|
||||||
|
/* step back to original instruction */ |
||||||
|
struct mi_registers regs = get_process_registers(proc); |
||||||
|
regs.rip -= 1; |
||||||
|
set_process_registers(proc, regs); |
||||||
|
} |
||||||
|
|
||||||
|
static void |
||||||
|
command_next(struct mi_process proc) |
||||||
|
{ |
||||||
|
struct mi_registers regs = get_process_registers(proc); |
||||||
|
|
||||||
|
u64 line_number = pc_to_line_number(proc.elf, regs.rip - proc.base_address); |
||||||
|
u64 next_line_number; |
||||||
|
|
||||||
|
do { |
||||||
|
ptrace(PTRACE_SINGLESTEP, proc.pid, 0, 0); |
||||||
|
waitpid(proc.pid, 0, 0); |
||||||
|
regs = get_process_registers(proc); |
||||||
|
//printf("%#lx\n", regs.rip - proc.base_address);
|
||||||
|
next_line_number= pc_to_line_number(proc.elf, regs.rip - proc.base_address); |
||||||
|
// TODO: skip call, repXXX, etc
|
||||||
|
} while (next_line_number == line_number); |
||||||
|
|
||||||
|
print_current_line(proc, next_line_number); |
||||||
|
} |
||||||
|
|
||||||
|
static void |
||||||
|
command_list(struct mi_process proc) |
||||||
|
{ |
||||||
|
struct mi_registers regs = get_process_registers(proc); |
||||||
|
u64 line_number = pc_to_line_number(proc.elf, regs.rip - proc.base_address); |
||||||
|
print_current_line(proc, line_number); |
||||||
|
} |
||||||
|
|
||||||
|
static void |
||||||
|
command_cont(struct mi_process proc) |
||||||
|
{ |
||||||
|
ptrace(PTRACE_CONT, proc.pid, 0, 0); |
||||||
|
// TODO
|
||||||
|
} |
||||||
|
|
||||||
|
static void |
||||||
|
command_stop(struct mi_process proc) |
||||||
|
{ |
||||||
|
kill(proc.pid, SIGINT); |
||||||
|
// TODO
|
||||||
|
} |
||||||
|
|
||||||
|
int |
||||||
|
main(int argc, char *argv[]) |
||||||
|
{ |
||||||
|
if (argc != 2) { |
||||||
|
printf("Usage: %s executable\n", argv[0]); |
||||||
|
return(1); |
||||||
|
} |
||||||
|
|
||||||
|
struct mi_process process = process_create(argv[1]); |
||||||
|
printf("pid of child = %d\n", process.pid); |
||||||
|
|
||||||
|
size_t max_command_length = 1023; |
||||||
|
int command_length = 0; |
||||||
|
char *command = malloc(max_command_length + 1); |
||||||
|
|
||||||
|
process.base_address = 0x555555554000UL; // get_executable_base_address(file, proc.pid);
|
||||||
|
process.main_address = get_address_of_subroutine(process.elf, "main"); |
||||||
|
|
||||||
|
printf("Base address: %#lx\n", process.base_address); |
||||||
|
printf("Main address: %#lx\n", process.main_address); |
||||||
|
|
||||||
|
printf("> "); |
||||||
|
fflush(stdout); |
||||||
|
|
||||||
|
while ((command_length = getline(&command, &max_command_length, stdin))) { |
||||||
|
if (0 == strncmp(command, "exit\n", command_length) || 0 == strncmp(command, "q\n", command_length)) { |
||||||
|
break; |
||||||
|
} else if (strncmp(command, "regs\n", command_length) == 0) { |
||||||
|
command_regs(process); |
||||||
|
} else if (strncmp(command, "step\n", command_length) == 0) { |
||||||
|
command_step(process); |
||||||
|
} else if (strncmp(command, "start\n", command_length) == 0) { |
||||||
|
command_start(process); |
||||||
|
} else if (strncmp(command, "next\n", command_length) == 0) { |
||||||
|
command_next(process); |
||||||
|
} else if (strncmp(command, "line\n", command_length) == 0) { |
||||||
|
command_list(process); |
||||||
|
} else if (strncmp(command, "cont\n", command_length) == 0) { |
||||||
|
command_cont(process); |
||||||
|
} else if (strncmp(command, "stop\n", command_length) == 0) { |
||||||
|
command_stop(process); |
||||||
|
} else { |
||||||
|
printf("Unknown command: %*s", command_length, command); |
||||||
|
} |
||||||
|
|
||||||
|
int wstatus; |
||||||
|
int rt = waitpid(process.pid, &wstatus, WNOHANG); |
||||||
|
|
||||||
|
if (rt > 0 && WIFEXITED(wstatus)) { |
||||||
|
printf("Child exited\n"); |
||||||
|
break; |
||||||
|
} |
||||||
|
|
||||||
|
printf("> "); |
||||||
|
} |
||||||
|
|
||||||
|
return(0); |
||||||
|
} |
@ -0,0 +1,37 @@ |
|||||||
|
version(1); |
||||||
|
|
||||||
|
project_name = "trace-test"; |
||||||
|
|
||||||
|
patterns = { |
||||||
|
"*.c", |
||||||
|
"*.h", |
||||||
|
"*.4coder", |
||||||
|
}; |
||||||
|
|
||||||
|
blacklist_patterns = { |
||||||
|
".*", |
||||||
|
"res", |
||||||
|
"lib", |
||||||
|
"build/*" |
||||||
|
}; |
||||||
|
|
||||||
|
load_paths_base = { |
||||||
|
{ ".", .relative = true, .recursive = true, } |
||||||
|
}; |
||||||
|
|
||||||
|
load_paths = { |
||||||
|
{ load_paths_base, .os = "linux", } |
||||||
|
}; |
||||||
|
|
||||||
|
command_list = { |
||||||
|
{ |
||||||
|
.name = "build debug", |
||||||
|
.out = "*compilation*", |
||||||
|
.footer_panel = false, |
||||||
|
.save_dirty_files = true, |
||||||
|
.cursor_at_end = true, |
||||||
|
.cmd = {{ "make debug", .os = "linux" }}, |
||||||
|
}, |
||||||
|
}; |
||||||
|
|
||||||
|
fkey_command[1] = "build debug"; |
@ -0,0 +1,586 @@ |
|||||||
|
#define PF_X (1 << 0) |
||||||
|
#define PF_W (1 << 1) |
||||||
|
#define PF_R (1 << 2) |
||||||
|
|
||||||
|
enum elf_abi { |
||||||
|
SYSTEM_V = 0x00, |
||||||
|
HP_UX = 0x01, |
||||||
|
NETBSD = 0x02, |
||||||
|
LINUX = 0x03, |
||||||
|
GNU_HURD = 0x04, |
||||||
|
SOLARIS = 0x06, |
||||||
|
AIX = 0x07, |
||||||
|
IRIX = 0x08, |
||||||
|
FREEBSD = 0x09, |
||||||
|
TRU64 = 0x0A, |
||||||
|
NOVELL_MODESTO = 0x0B, |
||||||
|
OPENBSD = 0x0D, |
||||||
|
OPENVMS = 0x0E, |
||||||
|
NONTSTOP_KERNEL = 0x0E, |
||||||
|
AROS = 0x0F, |
||||||
|
FENIX_OS = 0x10, |
||||||
|
CLOUDABI = 0x11, |
||||||
|
STRATUS_TECHNOLOGIES_OPENVOS = 0x12 |
||||||
|
}; |
||||||
|
|
||||||
|
enum elf_file_type { |
||||||
|
ET_NONE = 0x00, |
||||||
|
ET_REL = 0x01, |
||||||
|
ET_EXEC = 0x02, |
||||||
|
ET_DYN = 0x03, |
||||||
|
ET_CORE = 0x04, |
||||||
|
ET_LOOS = 0xFE00, |
||||||
|
ET_HIOS = 0xFEFF, |
||||||
|
ET_LOPROC = 0xFF00, |
||||||
|
ET_HIPROC = 0xFFFF |
||||||
|
}; |
||||||
|
|
||||||
|
enum elf_isa { |
||||||
|
NO_SPECIFIC_INSTRUCTION_SET = 0x00, |
||||||
|
ATNT_WE_32100 = 0x01, |
||||||
|
SPARC = 0x02, |
||||||
|
X86 = 0x03, |
||||||
|
MOTOROLA_68000 = 0x04, |
||||||
|
MOTOROLA_88000 = 0x05, |
||||||
|
INTEL_MCU = 0x06, |
||||||
|
INTEL_80860 = 0x07, |
||||||
|
MIPS = 0x08, |
||||||
|
IBM_SYSTEM_370 = 0x09, |
||||||
|
MIPS_RS3000_LITTLE_ENDIAN = 0x0A, |
||||||
|
HEWLETT_PACKARD_PA_RISC = 0x0E, |
||||||
|
INTEL_80960 = 0x13, |
||||||
|
POWERPC = 0x14, |
||||||
|
POWERPC_64 = 0x15, |
||||||
|
S390_S390X = 0x16, |
||||||
|
ARM = 0x28, |
||||||
|
SUPERH = 0x2A, |
||||||
|
IA_64 = 0x32, |
||||||
|
AMD64 = 0x3E, |
||||||
|
TMS320C6000_FAMILY = 0x8C, |
||||||
|
ARM_64 = 0xB7, |
||||||
|
RISC_V = 0xF3, |
||||||
|
BERKELEY_PACKET_FILTER = 0xF7, |
||||||
|
WDC_65C816 = 0x10 |
||||||
|
}; |
||||||
|
|
||||||
|
enum elf_section_type { |
||||||
|
SHT_NULL = 0, // No associated section (inactive entry).
|
||||||
|
SHT_PROGBITS = 1, // Program-defined contents.
|
||||||
|
SHT_SYMTAB = 2, // Symbol table.
|
||||||
|
SHT_STRTAB = 3, // String table.
|
||||||
|
SHT_RELA = 4, // Relocation entries; explicit addends.
|
||||||
|
SHT_HASH = 5, // Symbol hash table.
|
||||||
|
SHT_DYNAMIC = 6, // Information for dynamic linking.
|
||||||
|
SHT_NOTE = 7, // Information about the file.
|
||||||
|
SHT_NOBITS = 8, // Data occupies no space in the file.
|
||||||
|
SHT_REL = 9, // Relocation entries; no explicit addends.
|
||||||
|
SHT_SHLIB = 10, // Reserved.
|
||||||
|
SHT_DYNSYM = 11, // Symbol table.
|
||||||
|
SHT_INIT_ARRAY = 14, // Pointers to initialization functions.
|
||||||
|
SHT_FINI_ARRAY = 15, // Pointers to termination functions.
|
||||||
|
SHT_PREINIT_ARRAY = 16, // Pointers to pre-init functions.
|
||||||
|
SHT_GROUP = 17, // Section group.
|
||||||
|
SHT_SYMTAB_SHNDX = 18, // Indices for SHN_XINDEX entries.
|
||||||
|
|
||||||
|
SHT_RELR = 19, // Relocation entries; only offsets.
|
||||||
|
SHT_LOOS = 0x60000000, // Lowest operating system-specific type.
|
||||||
|
|
||||||
|
SHT_HIOS = 0x6fffffff, // Highest operating system-specific type.
|
||||||
|
SHT_LOPROC = 0x70000000, // Lowest processor arch-specific type.
|
||||||
|
SHT_HEX_ORDERED = 0x70000000, |
||||||
|
SHT_X86_64_UNWIND = 0x70000001, // Unwind information
|
||||||
|
|
||||||
|
SHT_HIPROC = 0x7fffffff, // Highest processor arch-specific type.
|
||||||
|
SHT_LOUSER = 0x80000000, // Lowest type reserved for applications.
|
||||||
|
SHT_HIUSER = 0xffffffff // Highest type reserved for applications.
|
||||||
|
}; |
||||||
|
|
||||||
|
enum dwarf_die_tag { |
||||||
|
DW_TAG_array_type = 0x01, |
||||||
|
DW_TAG_class_type = 0x02, |
||||||
|
DW_TAG_entry_point = 0x03, |
||||||
|
DW_TAG_enumeration_type = 0x04, |
||||||
|
DW_TAG_formal_parameter = 0x05, |
||||||
|
DW_TAG_imported_declaration = 0x08, |
||||||
|
DW_TAG_label = 0x0a, |
||||||
|
DW_TAG_lexical_block = 0x0b, |
||||||
|
DW_TAG_member = 0x0d, |
||||||
|
DW_TAG_pointer_type = 0x0f, |
||||||
|
DW_TAG_reference_type = 0x10, |
||||||
|
DW_TAG_compile_unit = 0x11, |
||||||
|
DW_TAG_string_type = 0x12, |
||||||
|
DW_TAG_structure_type = 0x13, |
||||||
|
DW_TAG_subroutine_type = 0x15, |
||||||
|
DW_TAG_typedef = 0x16, |
||||||
|
DW_TAG_union_type = 0x17, |
||||||
|
DW_TAG_unspecified_parameters = 0x18, |
||||||
|
DW_TAG_variant = 0x19, |
||||||
|
DW_TAG_common_block = 0x1a, |
||||||
|
DW_TAG_common_inclusion = 0x1b, |
||||||
|
DW_TAG_inheritance = 0x1c, |
||||||
|
DW_TAG_inlined_subroutine = 0x1d, |
||||||
|
DW_TAG_module = 0x1e, |
||||||
|
DW_TAG_ptr_to_member_type = 0x1f, |
||||||
|
DW_TAG_set_type = 0x20, |
||||||
|
DW_TAG_subrange_type = 0x21, |
||||||
|
DW_TAG_with_stmt = 0x22, |
||||||
|
DW_TAG_access_declaration = 0x23, |
||||||
|
DW_TAG_base_type = 0x24, |
||||||
|
DW_TAG_catch_block = 0x25, |
||||||
|
DW_TAG_const_type = 0x26, |
||||||
|
DW_TAG_constant = 0x27, |
||||||
|
DW_TAG_enumerator = 0x28, |
||||||
|
DW_TAG_file_type = 0x29, |
||||||
|
DW_TAG_friend = 0x2a, |
||||||
|
DW_TAG_namelist = 0x2b, |
||||||
|
DW_TAG_namelist_item = 0x2c, |
||||||
|
DW_TAG_namelist_items = 0x2c, |
||||||
|
DW_TAG_packed_type = 0x2d, |
||||||
|
DW_TAG_subprogram = 0x2e, |
||||||
|
DW_TAG_thrown_type = 0x31, |
||||||
|
DW_TAG_try_block = 0x32, |
||||||
|
DW_TAG_variant_part = 0x33, |
||||||
|
DW_TAG_variable = 0x34, |
||||||
|
DW_TAG_volatile_type = 0x35, |
||||||
|
DW_TAG_lo_user = 0x4080, |
||||||
|
DW_TAG_MIPS_loop = 0x4081, |
||||||
|
DW_TAG_hi_user = 0xffff |
||||||
|
}; |
||||||
|
|
||||||
|
enum dwarf_die_attribute { |
||||||
|
DW_AT_sibling = 0x01, |
||||||
|
DW_AT_location = 0x02, |
||||||
|
DW_AT_name = 0x03, |
||||||
|
DW_AT_ordering = 0x09, |
||||||
|
DW_AT_subscr_data = 0x0a, |
||||||
|
DW_AT_byte_size = 0x0b, |
||||||
|
DW_AT_bit_offset = 0x0c, |
||||||
|
DW_AT_bit_size = 0x0d, |
||||||
|
DW_AT_element_list = 0x0f, |
||||||
|
DW_AT_stmt_list = 0x10, |
||||||
|
DW_AT_low_pc = 0x11, |
||||||
|
DW_AT_high_pc = 0x12, |
||||||
|
DW_AT_language = 0x13, |
||||||
|
DW_AT_member = 0x14, |
||||||
|
DW_AT_discr = 0x15, |
||||||
|
DW_AT_discr_value = 0x16, |
||||||
|
DW_AT_visibility = 0x17, |
||||||
|
DW_AT_import = 0x18, |
||||||
|
DW_AT_string_length = 0x19, |
||||||
|
DW_AT_common_reference = 0x1a, |
||||||
|
DW_AT_comp_dir = 0x1b, |
||||||
|
DW_AT_const_value = 0x1c, |
||||||
|
DW_AT_containing_type = 0x1d, |
||||||
|
DW_AT_default_value = 0x1e, |
||||||
|
DW_AT_inline = 0x20, |
||||||
|
DW_AT_is_optional = 0x21, |
||||||
|
DW_AT_lower_bound = 0x22, |
||||||
|
DW_AT_producer = 0x25, |
||||||
|
DW_AT_prototyped = 0x27, |
||||||
|
DW_AT_return_addr = 0x2a, |
||||||
|
DW_AT_start_scope = 0x2c, |
||||||
|
DW_AT_upper_bound = 0x2f, |
||||||
|
DW_AT_abstract_origin = 0x31, |
||||||
|
DW_AT_accessibility = 0x32, |
||||||
|
DW_AT_address_class = 0x33, |
||||||
|
DW_AT_artificial = 0x34, |
||||||
|
DW_AT_base_types = 0x35, |
||||||
|
DW_AT_calling_convention = 0x36, |
||||||
|
DW_AT_count = 0x37, |
||||||
|
DW_AT_data_member_location = 0x38, |
||||||
|
DW_AT_decl_column = 0x39, |
||||||
|
DW_AT_decl_file = 0x3a, |
||||||
|
DW_AT_decl_line = 0x3b, |
||||||
|
DW_AT_declaration = 0x3c, |
||||||
|
DW_AT_encoding = 0x3e, |
||||||
|
DW_AT_external = 0x3f, |
||||||
|
DW_AT_frame_base = 0x40, |
||||||
|
DW_AT_friend = 0x41, |
||||||
|
DW_AT_identifier_case = 0x42, |
||||||
|
DW_AT_macro_info = 0x43, |
||||||
|
DW_AT_namelist_item = 0x44, |
||||||
|
DW_AT_priority = 0x45, |
||||||
|
DW_AT_segment = 0x46, |
||||||
|
DW_AT_specification = 0x47, |
||||||
|
DW_AT_static_link = 0x48, |
||||||
|
DW_AT_type = 0x49, |
||||||
|
DW_AT_use_location = 0x4a, |
||||||
|
DW_AT_variable_parameter = 0x4b, |
||||||
|
DW_AT_virtuality = 0x4c, |
||||||
|
DW_AT_vtable_elem_location = 0x4d, |
||||||
|
DW_AT_signature = 0x69, |
||||||
|
DW_AT_main_subprogram = 0x6a, |
||||||
|
DW_AT_data_bit_offset = 0x6b, |
||||||
|
DW_AT_const_expr = 0x6c, |
||||||
|
DW_AT_enum_class = 0x6d, |
||||||
|
DW_AT_linkage_name = 0x6e, |
||||||
|
DW_AT_string_length_bit_size = 0x6f, |
||||||
|
DW_AT_string_length_byte_size = 0x70, |
||||||
|
DW_AT_rank = 0x71, |
||||||
|
DW_AT_str_offsets_base = 0x72, |
||||||
|
DW_AT_addr_base = 0x73, |
||||||
|
DW_AT_rnglists_base = 0x74, |
||||||
|
DW_AT_dwo_id = 0x75, |
||||||
|
DW_AT_dwo_name = 0x76, |
||||||
|
DW_AT_reference = 0x77, |
||||||
|
DW_AT_rvalue_reference = 0x78, |
||||||
|
DW_AT_macros = 0x79, |
||||||
|
DW_AT_call_all_calls = 0x7a, |
||||||
|
DW_AT_call_all_source_calls = 0x7b, |
||||||
|
DW_AT_call_all_tail_calls = 0x7c, |
||||||
|
DW_AT_call_return_pc = 0x7d, |
||||||
|
DW_AT_call_value = 0x7e, |
||||||
|
DW_AT_call_origin = 0x7f, |
||||||
|
DW_AT_call_parameter = 0x80, |
||||||
|
DW_AT_call_pc = 0x81, |
||||||
|
DW_AT_call_tail_call = 0x82, |
||||||
|
DW_AT_call_target = 0x83, |
||||||
|
DW_AT_call_target_clobbered = 0x84, |
||||||
|
DW_AT_call_data_location = 0x85, |
||||||
|
DW_AT_call_data_value = 0x86, |
||||||
|
DW_AT_noreturn = 0x87, |
||||||
|
DW_AT_alignment = 0x88, |
||||||
|
DW_AT_export_symbols = 0x89, |
||||||
|
DW_AT_deleted = 0x8a, |
||||||
|
DW_AT_defaulted = 0x8b, |
||||||
|
DW_AT_loclists_base = 0x8c |
||||||
|
}; |
||||||
|
|
||||||
|
enum dwarf_attribute_form { |
||||||
|
DW_FORM_addr = 0x01, |
||||||
|
DW_FORM_block2 = 0x03, |
||||||
|
DW_FORM_block4 = 0x04, |
||||||
|
DW_FORM_data2 = 0x05, |
||||||
|
DW_FORM_data4 = 0x06, |
||||||
|
DW_FORM_data8 = 0x07, |
||||||
|
DW_FORM_string = 0x08, |
||||||
|
DW_FORM_block = 0x09, |
||||||
|
DW_FORM_block1 = 0x0a, |
||||||
|
DW_FORM_data1 = 0x0b, |
||||||
|
DW_FORM_flag = 0x0c, |
||||||
|
DW_FORM_sdata = 0x0d, |
||||||
|
DW_FORM_strp = 0x0e, |
||||||
|
DW_FORM_udata = 0x0f, |
||||||
|
DW_FORM_ref_addr = 0x10, |
||||||
|
DW_FORM_ref1 = 0x11, |
||||||
|
DW_FORM_ref2 = 0x12, |
||||||
|
DW_FORM_ref4 = 0x13, |
||||||
|
DW_FORM_ref8 = 0x14, |
||||||
|
DW_FORM_ref_udata = 0x15, |
||||||
|
DW_FORM_indirect = 0x16, |
||||||
|
DW_FORM_sec_offset = 0x17, |
||||||
|
DW_FORM_exprloc = 0x18, |
||||||
|
DW_FORM_flag_present = 0x19, |
||||||
|
DW_FORM_strx = 0x1a, |
||||||
|
DW_FORM_addrx = 0x1b, |
||||||
|
DW_FORM_ref_sup4 = 0x1c, |
||||||
|
DW_FORM_strp_sup = 0x1d, |
||||||
|
DW_FORM_data16 = 0x1e, |
||||||
|
DW_FORM_line_strp = 0x1f, |
||||||
|
DW_FORM_ref_sig8 = 0x20, |
||||||
|
DW_FORM_implicit_const = 0x21, |
||||||
|
DW_FORM_loclistx = 0x22, |
||||||
|
DW_FORM_rnglistx = 0x23, |
||||||
|
DW_FORM_ref_sup8 = 0x24, |
||||||
|
DW_FORM_strx1 = 0x25, |
||||||
|
DW_FORM_strx2 = 0x26, |
||||||
|
DW_FORM_strx3 = 0x27, |
||||||
|
DW_FORM_strx4 = 0x28, |
||||||
|
DW_FORM_addrx1 = 0x29, |
||||||
|
DW_FORM_addrx2 = 0x2a, |
||||||
|
DW_FORM_addrx3 = 0x2b, |
||||||
|
DW_FORM_addrx4 = 0x2c |
||||||
|
}; |
||||||
|
|
||||||
|
enum dwarf_lnp_opcode { |
||||||
|
DW_LNS_copy = 0x01, |
||||||
|
DW_LNS_advance_pc = 0x02, |
||||||
|
DW_LNS_advance_line = 0x03, |
||||||
|
DW_LNS_set_file = 0x04, |
||||||
|
DW_LNS_set_column = 0x05, |
||||||
|
DW_LNS_negate_stmt = 0x06, |
||||||
|
DW_LNS_set_basic_block = 0x07, |
||||||
|
DW_LNS_const_add_pc = 0x08, |
||||||
|
DW_LNS_fixed_advance_pc = 0x09, |
||||||
|
DW_LNS_set_prologue_end = 0x0a, |
||||||
|
DW_LNS_set_epilogue_begin = 0x0b, |
||||||
|
DW_LNS_set_isa = 0x0c, |
||||||
|
}; |
||||||
|
|
||||||
|
enum dwarf_lnp_opcode_extended { |
||||||
|
DW_LNE_end_sequence = 0x01, |
||||||
|
DW_LNE_set_address = 0x02, |
||||||
|
DW_LNE_define_file = 0x03, |
||||||
|
DW_LNE_set_discriminator = 0x04, |
||||||
|
}; |
||||||
|
|
||||||
|
#pragma pack(1) |
||||||
|
struct elf_header_x64 { |
||||||
|
char magic[4]; |
||||||
|
u8 bitness; |
||||||
|
u8 endianness; |
||||||
|
u8 ei_version; |
||||||
|
u8 abi; |
||||||
|
u8 abi_version; |
||||||
|
u8 pad[7]; |
||||||
|
u16 file_type; |
||||||
|
u16 isa; |
||||||
|
u32 version; |
||||||
|
u64 entry_point_offset; |
||||||
|
u64 header_table_offset; |
||||||
|
u64 section_table_offset; |
||||||
|
u32 flags; |
||||||
|
u16 this_header_size; |
||||||
|
u16 header_table_entry_size; |
||||||
|
u16 header_table_entry_count; |
||||||
|
u16 section_table_entry_size; |
||||||
|
u16 section_table_entry_count; |
||||||
|
u16 section_names_table_index; |
||||||
|
}; |
||||||
|
|
||||||
|
#pragma pack(1) |
||||||
|
struct elf_header_table_entry_x64 { |
||||||
|
u32 type; |
||||||
|
u32 flags; |
||||||
|
u64 segment_offset; |
||||||
|
u64 virtual_address; |
||||||
|
u64 reserved_physical_address; |
||||||
|
u64 segment_file_size; |
||||||
|
u64 segment_memory_size; |
||||||
|
u64 alignment; |
||||||
|
}; |
||||||
|
|
||||||
|
#pragma pack(1) |
||||||
|
struct elf_section_table_entry_x64 { |
||||||
|
u32 name_offset; |
||||||
|
u32 type; |
||||||
|
u64 flags; |
||||||
|
u64 virtual_address; |
||||||
|
u64 offset_in_file; |
||||||
|
u64 size; |
||||||
|
u32 link; |
||||||
|
u32 info; |
||||||
|
u64 alignment; |
||||||
|
u64 entry_size; |
||||||
|
}; |
||||||
|
|
||||||
|
#pragma pack(1) |
||||||
|
struct dwarf_debug_info_header_x64 { |
||||||
|
u32 pad; |
||||||
|
u64 length; |
||||||
|
u16 version; |
||||||
|
u64 debug_abbrev_offset; |
||||||
|
u8 adress_size; |
||||||
|
}; |
||||||
|
|
||||||
|
#pragma pack(1) |
||||||
|
struct dwarf_debug_info_header_x32 { |
||||||
|
u32 length; |
||||||
|
u16 version; |
||||||
|
u32 debug_abbrev_offset; |
||||||
|
u8 address_size; |
||||||
|
}; |
||||||
|
|
||||||
|
struct dwarf_debug_line_file_info { |
||||||
|
char *name; |
||||||
|
u32 directory_index; |
||||||
|
u32 time_modified; |
||||||
|
u32 file_size; |
||||||
|
}; |
||||||
|
|
||||||
|
#pragma pack(1) |
||||||
|
struct dwarf_debug_line_header_v3_x32 { |
||||||
|
u32 length; |
||||||
|
u16 version; |
||||||
|
u32 header_length; |
||||||
|
u8 minimum_instruction_length; |
||||||
|
u8 default_is_stmt; |
||||||
|
s8 line_base; |
||||||
|
u8 line_range; |
||||||
|
u8 opcode_base; |
||||||
|
u8 *standard_opcode_lengths; |
||||||
|
u8 ndirs; |
||||||
|
u8 nfiles; |
||||||
|
char *include_directories; |
||||||
|
struct dwarf_debug_line_file_info *files; |
||||||
|
}; |
||||||
|
|
||||||
|
struct dwarf_line_number_state { |
||||||
|
u64 pc; |
||||||
|
u32 file; |
||||||
|
u32 line; |
||||||
|
u32 column; |
||||||
|
|
||||||
|
u8 is_stmt; |
||||||
|
u8 basic_block; |
||||||
|
u8 end_sequence; |
||||||
|
u8 prologue_end; |
||||||
|
u8 epilogue_begin; |
||||||
|
|
||||||
|
u32 isa; |
||||||
|
u32 discriminator; |
||||||
|
}; |
||||||
|
|
||||||
|
|
||||||
|
static char * |
||||||
|
attribute_to_str(enum dwarf_die_tag attr) |
||||||
|
{ |
||||||
|
switch (attr) { |
||||||
|
case DW_AT_sibling: { return "DW_AT_sibling"; } |
||||||
|
case DW_AT_location: { return "DW_AT_location"; } |
||||||
|
case DW_AT_name: { return "DW_AT_name"; } |
||||||
|
case DW_AT_ordering: { return "DW_AT_ordering"; } |
||||||
|
case DW_AT_subscr_data: { return "DW_AT_subscr_data"; } |
||||||
|
case DW_AT_byte_size: { return "DW_AT_byte_size"; } |
||||||
|
case DW_AT_bit_offset: { return "DW_AT_bit_offset"; } |
||||||
|
case DW_AT_bit_size: { return "DW_AT_bit_size"; } |
||||||
|
case DW_AT_element_list: { return "DW_AT_element_list"; } |
||||||
|
case DW_AT_stmt_list: { return "DW_AT_stmt_list"; } |
||||||
|
case DW_AT_low_pc: { return "DW_AT_low_pc"; } |
||||||
|
case DW_AT_high_pc: { return "DW_AT_high_pc"; } |
||||||
|
case DW_AT_language: { return "DW_AT_language"; } |
||||||
|
case DW_AT_member: { return "DW_AT_member"; } |
||||||
|
case DW_AT_discr: { return "DW_AT_discr"; } |
||||||
|
case DW_AT_discr_value: { return "DW_AT_discr_value"; } |
||||||
|
case DW_AT_visibility: { return "DW_AT_visibility"; } |
||||||
|
case DW_AT_import: { return "DW_AT_import"; } |
||||||
|
case DW_AT_string_length: { return "DW_AT_string_length"; } |
||||||
|
case DW_AT_common_reference: { return "DW_AT_common_reference"; } |
||||||
|
case DW_AT_comp_dir: { return "DW_AT_comp_dir"; } |
||||||
|
case DW_AT_const_value: { return "DW_AT_const_value"; } |
||||||
|
case DW_AT_containing_type: { return "DW_AT_containing_type"; } |
||||||
|
case DW_AT_default_value: { return "DW_AT_default_value"; } |
||||||
|
case DW_AT_inline: { return "DW_AT_inline"; } |
||||||
|
case DW_AT_is_optional: { return "DW_AT_is_optional"; } |
||||||
|
case DW_AT_lower_bound: { return "DW_AT_lower_bound"; } |
||||||
|
case DW_AT_producer: { return "DW_AT_producer"; } |
||||||
|
case DW_AT_prototyped: { return "DW_AT_prototyped"; } |
||||||
|
case DW_AT_return_addr: { return "DW_AT_return_addr"; } |
||||||
|
case DW_AT_start_scope: { return "DW_AT_start_scope"; } |
||||||
|
case DW_AT_upper_bound: { return "DW_AT_upper_bound"; } |
||||||
|
case DW_AT_abstract_origin: { return "DW_AT_abstract_origin"; } |
||||||
|
case DW_AT_accessibility: { return "DW_AT_accessibility"; } |
||||||
|
case DW_AT_address_class: { return "DW_AT_address_class"; } |
||||||
|
case DW_AT_artificial: { return "DW_AT_artificial"; } |
||||||
|
case DW_AT_base_types: { return "DW_AT_base_types"; } |
||||||
|
case DW_AT_calling_convention: { return "DW_AT_calling_convention"; } |
||||||
|
case DW_AT_count: { return "DW_AT_count"; } |
||||||
|
case DW_AT_data_member_location: { return "DW_AT_data_member_location"; } |
||||||
|
case DW_AT_decl_column: { return "DW_AT_decl_column"; } |
||||||
|
case DW_AT_decl_file: { return "DW_AT_decl_file"; } |
||||||
|
case DW_AT_decl_line: { return "DW_AT_decl_line"; } |
||||||
|
case DW_AT_declaration: { return "DW_AT_declaration"; } |
||||||
|
case DW_AT_encoding: { return "DW_AT_encoding"; } |
||||||
|
case DW_AT_external: { return "DW_AT_external"; } |
||||||
|
case DW_AT_frame_base: { return "DW_AT_frame_base"; } |
||||||
|
case DW_AT_friend: { return "DW_AT_friend"; } |
||||||
|
case DW_AT_identifier_case: { return "DW_AT_identifier_case"; } |
||||||
|
case DW_AT_macro_info: { return "DW_AT_macro_info"; } |
||||||
|
case DW_AT_namelist_item: { return "DW_AT_namelist_item"; } |
||||||
|
case DW_AT_priority: { return "DW_AT_priority"; } |
||||||
|
case DW_AT_segment: { return "DW_AT_segment"; } |
||||||
|
case DW_AT_specification: { return "DW_AT_specification"; } |
||||||
|
case DW_AT_static_link: { return "DW_AT_static_link"; } |
||||||
|
case DW_AT_type: { return "DW_AT_type"; } |
||||||
|
case DW_AT_use_location: { return "DW_AT_use_location"; } |
||||||
|
case DW_AT_variable_parameter: { return "DW_AT_variable_parameter"; } |
||||||
|
case DW_AT_virtuality: { return "DW_AT_virtuality"; } |
||||||
|
case DW_AT_vtable_elem_location: { return "DW_AT_vtable_elem_location"; } |
||||||
|
case DW_AT_signature: { return "DW_AT_signature"; } |
||||||
|
case DW_AT_main_subprogram: { return "DW_AT_main_subprogram"; } |
||||||
|
case DW_AT_data_bit_offset: { return "DW_AT_data_bit_offset"; } |
||||||
|
case DW_AT_const_expr: { return "DW_AT_const_expr"; } |
||||||
|
case DW_AT_enum_class: { return "DW_AT_enum_class"; } |
||||||
|
case DW_AT_linkage_name: { return "DW_AT_linkage_name"; } |
||||||
|
case DW_AT_string_length_bit_size: { return "DW_AT_string_length_bit_size"; } |
||||||
|
case DW_AT_string_length_byte_size: { return "DW_AT_string_length_byte_size"; } |
||||||
|
case DW_AT_rank: { return "DW_AT_rank"; } |
||||||
|
case DW_AT_str_offsets_base: { return "DW_AT_str_offsets_base"; } |
||||||
|
case DW_AT_addr_base: { return "DW_AT_addr_base"; } |
||||||
|
case DW_AT_rnglists_base: { return "DW_AT_rnglists_base"; } |
||||||
|
case DW_AT_dwo_id: { return "DW_AT_dwo_id"; } |
||||||
|
case DW_AT_dwo_name: { return "DW_AT_dwo_name"; } |
||||||
|
case DW_AT_reference: { return "DW_AT_reference"; } |
||||||
|
case DW_AT_rvalue_reference: { return "DW_AT_rvalue_reference"; } |
||||||
|
case DW_AT_macros: { return "DW_AT_macros"; } |
||||||
|
case DW_AT_call_all_calls: { return "DW_AT_call_all_calls"; } |
||||||
|
case DW_AT_call_all_source_calls: { return "DW_AT_call_all_source_calls"; } |
||||||
|
case DW_AT_call_all_tail_calls: { return "DW_AT_call_all_tail_calls"; } |
||||||
|
case DW_AT_call_return_pc: { return "DW_AT_call_return_pc"; } |
||||||
|
case DW_AT_call_value: { return "DW_AT_call_value"; } |
||||||
|
case DW_AT_call_origin: { return "DW_AT_call_origin"; } |
||||||
|
case DW_AT_call_parameter: { return "DW_AT_call_parameter"; } |
||||||
|
case DW_AT_call_pc: { return "DW_AT_call_pc"; } |
||||||
|
case DW_AT_call_tail_call: { return "DW_AT_call_tail_call"; } |
||||||
|
case DW_AT_call_target: { return "DW_AT_call_target"; } |
||||||
|
case DW_AT_call_target_clobbered: { return "DW_AT_call_target_clobbered"; } |
||||||
|
case DW_AT_call_data_location: { return "DW_AT_call_data_location"; } |
||||||
|
case DW_AT_call_data_value: { return "DW_AT_call_data_value"; } |
||||||
|
case DW_AT_noreturn: { return "DW_AT_noreturn"; } |
||||||
|
case DW_AT_alignment: { return "DW_AT_alignment"; } |
||||||
|
case DW_AT_export_symbols: { return "DW_AT_export_symbols"; } |
||||||
|
case DW_AT_deleted: { return "DW_AT_deleted"; } |
||||||
|
case DW_AT_defaulted: { return "DW_AT_defaulted"; } |
||||||
|
case DW_AT_loclists_base: { return "DW_AT_loclists_base"; } |
||||||
|
} |
||||||
|
|
||||||
|
return(NULL); |
||||||
|
} |
||||||
|
|
||||||
|
static char * |
||||||
|
tag_to_str(enum dwarf_die_tag tag) { |
||||||
|
switch (tag) { |
||||||
|
case DW_TAG_array_type: { return "DW_TAG_array_type"; } |
||||||
|
case DW_TAG_class_type: { return "DW_TAG_class_type"; } |
||||||
|
case DW_TAG_entry_point: { return "DW_TAG_entry_point"; } |
||||||
|
case DW_TAG_enumeration_type: { return "DW_TAG_enumeration_type"; } |
||||||
|
case DW_TAG_formal_parameter: { return "DW_TAG_formal_parameter"; } |
||||||
|
case DW_TAG_imported_declaration: { return "DW_TAG_imported_declaration"; } |
||||||
|
case DW_TAG_label: { return "DW_TAG_label"; } |
||||||
|
case DW_TAG_lexical_block: { return "DW_TAG_lexical_block"; } |
||||||
|
case DW_TAG_member: { return "DW_TAG_member"; } |
||||||
|
case DW_TAG_pointer_type: { return "DW_TAG_pointer_type"; } |
||||||
|
case DW_TAG_reference_type: { return "DW_TAG_reference_type"; } |
||||||
|
case DW_TAG_compile_unit: { return "DW_TAG_compile_unit"; } |
||||||
|
case DW_TAG_string_type: { return "DW_TAG_string_type"; } |
||||||
|
case DW_TAG_structure_type: { return "DW_TAG_structure_type"; } |
||||||
|
case DW_TAG_subroutine_type: { return "DW_TAG_subroutine_type"; } |
||||||
|
case DW_TAG_typedef: { return "DW_TAG_typedef"; } |
||||||
|
case DW_TAG_union_type: { return "DW_TAG_union_type"; } |
||||||
|
case DW_TAG_unspecified_parameters: { return "DW_TAG_unspecified_parameters"; } |
||||||
|
case DW_TAG_variant: { return "DW_TAG_variant"; } |
||||||
|
case DW_TAG_common_block: { return "DW_TAG_common_block"; } |
||||||
|
case DW_TAG_common_inclusion: { return "DW_TAG_common_inclusion"; } |
||||||
|
case DW_TAG_inheritance: { return "DW_TAG_inheritance"; } |
||||||
|
case DW_TAG_inlined_subroutine: { return "DW_TAG_inlined_subroutine"; } |
||||||
|
case DW_TAG_module: { return "DW_TAG_module"; } |
||||||
|
case DW_TAG_ptr_to_member_type: { return "DW_TAG_ptr_to_member_type"; } |
||||||
|
case DW_TAG_set_type: { return "DW_TAG_set_type"; } |
||||||
|
case DW_TAG_subrange_type: { return "DW_TAG_subrange_type"; } |
||||||
|
case DW_TAG_with_stmt: { return "DW_TAG_with_stmt"; } |
||||||
|
case DW_TAG_access_declaration: { return "DW_TAG_access_declaration"; } |
||||||
|
case DW_TAG_base_type: { return "DW_TAG_base_type"; } |
||||||
|
case DW_TAG_catch_block: { return "DW_TAG_catch_block"; } |
||||||
|
case DW_TAG_const_type: { return "DW_TAG_const_type"; } |
||||||
|
case DW_TAG_constant: { return "DW_TAG_constant"; } |
||||||
|
case DW_TAG_enumerator: { return "DW_TAG_enumerator"; } |
||||||
|
case DW_TAG_file_type: { return "DW_TAG_file_type"; } |
||||||
|
case DW_TAG_friend: { return "DW_TAG_friend"; } |
||||||
|
case DW_TAG_namelist: { return "DW_TAG_namelist"; } |
||||||
|
case DW_TAG_namelist_item: { return "DW_TAG_namelist_item"; } |
||||||
|
case DW_TAG_packed_type: { return "DW_TAG_packed_type"; } |
||||||
|
case DW_TAG_subprogram: { return "DW_TAG_subprogram"; } |
||||||
|
case DW_TAG_thrown_type: { return "DW_TAG_thrown_type"; } |
||||||
|
case DW_TAG_try_block: { return "DW_TAG_try_block"; } |
||||||
|
case DW_TAG_variant_part: { return "DW_TAG_variant_part"; } |
||||||
|
case DW_TAG_variable: { return "DW_TAG_variable"; } |
||||||
|
case DW_TAG_volatile_type: { return "DW_TAG_volatile_type"; } |
||||||
|
case DW_TAG_lo_user: { return "DW_TAG_lo_user"; } |
||||||
|
case DW_TAG_MIPS_loop: { return "DW_TAG_MIPS_loop"; } |
||||||
|
default: { return "unknown tag"; } |
||||||
|
} |
||||||
|
|
||||||
|
assert(0); |
||||||
|
|
||||||
|
return(NULL); |
||||||
|
} |
@ -0,0 +1,5 @@ |
|||||||
|
int foo(int a) |
||||||
|
{ |
||||||
|
int b = a + 1; |
||||||
|
return(b / 10); |
||||||
|
} |
@ -0,0 +1,23 @@ |
|||||||
|
#include <stdio.h> |
||||||
|
#include <unistd.h> |
||||||
|
|
||||||
|
#include "trace_include.c" |
||||||
|
|
||||||
|
int main() |
||||||
|
{ |
||||||
|
int i; |
||||||
|
int arr[10]; |
||||||
|
|
||||||
|
for(i = 0;i < 10; ++i) { |
||||||
|
arr[i] = i * i * i / 10; |
||||||
|
} |
||||||
|
|
||||||
|
int pipa = 0; |
||||||
|
int boba = 1; |
||||||
|
int peppa = pipa / boba + 2 * (boba / 2) - 1 |
||||||
|
+ pipa * pipa / boba; |
||||||
|
|
||||||
|
foo(3); |
||||||
|
|
||||||
|
return 0; |
||||||
|
} |
Loading…
Reference in new issue