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); }