diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..733fb82 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +.gdb_history +build/ +res/001-simple +res/002-compilation-units diff --git a/command.c b/command.c index e2dc883..56d01be 100644 --- a/command.c +++ b/command.c @@ -15,15 +15,22 @@ command_regs(struct mi_process proc) static void command_step(struct mi_process proc) { - ptrace(PTRACE_SINGLESTEP, proc.pid, 0, 0); - waitpid(proc.pid, 0, 0); + int comp_unit; -#if 1 - print_current_instruction(proc); struct mi_registers regs = get_process_registers(proc); - struct mi_sourcepoint *sp = pc_to_sourcepoint(proc, regs.rip - proc.base_address); - print_sourcepoint(proc, sp); -#endif + struct mi_sourcepoint *sp = pc_to_sourcepoint(proc, regs.rip - proc.base_address, &comp_unit); + struct mi_sourcepoint *next_sp; + + do { + ptrace(PTRACE_SINGLESTEP, proc.pid, 0, 0); + waitpid(proc.pid, 0, 0); + regs = get_process_registers(proc); + long instruction = ptrace(PTRACE_PEEKDATA, proc.pid, regs.rip, NULL); + next_sp = pc_to_sourcepoint(proc, regs.rip - proc.base_address, &comp_unit); + if (!next_sp) break; + } while (next_sp->line == sp->line); + + print_sourcepoint(proc, comp_unit, next_sp); } static void @@ -37,9 +44,10 @@ command_start(struct mi_process proc) static void command_next(struct mi_process proc) { - struct mi_registers regs = get_process_registers(proc); + int comp_unit; - struct mi_sourcepoint *sp = pc_to_sourcepoint(proc, regs.rip - proc.base_address); + struct mi_registers regs = get_process_registers(proc); + struct mi_sourcepoint *sp = pc_to_sourcepoint(proc, regs.rip - proc.base_address, &comp_unit); struct mi_sourcepoint *next_sp; do { @@ -63,23 +71,24 @@ command_next(struct mi_process proc) regs = get_process_registers(proc); } - // TODO: skip call, repXXX + // TODO: repXXX //printf("%#lx\n", regs.rip - proc.base_address); - next_sp = pc_to_sourcepoint(proc, regs.rip - proc.base_address); + next_sp = pc_to_sourcepoint(proc, regs.rip - proc.base_address, &comp_unit); if (!next_sp) break; } while (next_sp->line == sp->line); - print_sourcepoint(proc, next_sp); + print_sourcepoint(proc, comp_unit, next_sp); } static void command_list(struct mi_process proc) { + int comp_unit; struct mi_registers regs = get_process_registers(proc); - struct mi_sourcepoint *sp = pc_to_sourcepoint(proc, regs.rip - proc.base_address); - print_sourcepoint(proc, sp); + struct mi_sourcepoint *sp = pc_to_sourcepoint(proc, regs.rip - proc.base_address, &comp_unit); + print_sourcepoint(proc, comp_unit, sp); } static void diff --git a/common.h b/common.h index 7f78f4a..e34dcf1 100644 --- a/common.h +++ b/common.h @@ -45,23 +45,39 @@ struct mi_sourcepoint { struct mi_function { char *name; - u64 offset; + int comp_unit; + u64 low_pc; + u64 high_pc; }; -struct mi_debuginfo { +struct mi_sourcefile { + char *filename; + char *dir; + struct mi_buffer file; +}; + +struct mi_compunit { + u64 low_pc; + u64 high_pc; + struct mi_sourcepoint *sp_table; int sp_count; - struct mi_function functions[64]; // TODO - int func_count; + int source_file_count; + struct mi_sourcefile *source_files; + + char **source_directories; + int source_dirs_count; char *comp_dir; }; -struct mi_sourcefile { - char *filename; - char *dir; - struct mi_buffer file; +struct mi_debuginfo { + struct mi_compunit compilation_units[16]; // TODO + int cu_count; + + struct mi_function functions[64]; // TODO + int func_count; }; struct mi_process { @@ -74,11 +90,6 @@ struct mi_process { u64 main_address; struct mi_debuginfo debug; - - int source_file_count; - int source_dirs_count; - struct mi_sourcefile *source_files; - char **source_directories; }; struct mi_registers { diff --git a/dwarf.c b/dwarf.c index 10aa800..ed708da 100644 --- a/dwarf.c +++ b/dwarf.c @@ -1,5 +1,5 @@ -static u64 -get_section_offset(u8 *file, char *name) +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)); @@ -16,7 +16,8 @@ get_section_offset(u8 *file, char *name) memcpy(&shstrtab_header, file + shstrtab_header_offset, sizeof(shstrtab_header)); u64 shstrtab_offset = shstrtab_header.offset_in_file; - u64 debug_info_offset = 0; + + 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 }; @@ -25,12 +26,21 @@ get_section_offset(u8 *file, char *name) 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; + result = section_entry; + break; } } - return(debug_info_offset); + 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 @@ -232,18 +242,10 @@ read_actual_debug_data(u8 *file, u64 string_offset, u32 address_size, u64 base_d return(increment); } -static void -parse_debug_info(u8 *file, struct mi_debuginfo *dest) +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) { - u64 debug_info_offset = get_section_offset(file, ".debug_info"); - printf("Found .debug_info at offset %#lx\n", debug_info_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 }; u32 header_size = sizeof(di_header); memcpy(&di_header, file + debug_info_offset, header_size); @@ -258,7 +260,8 @@ parse_debug_info(u8 *file, struct mi_debuginfo *dest) int found_sr = 0; - struct mi_function *func = dest->functions; + struct mi_function *func = dest->functions + dest->func_count; + struct mi_compunit *comp_unit = dest->compilation_units + dest->cu_count; for (;;) { data_offset += decode_leb128(file + data_offset, &code); @@ -294,31 +297,77 @@ parse_debug_info(u8 *file, struct mi_debuginfo *dest) data_offset += read_actual_debug_data(file, debug_str_offset, di_header.address_size, base_data_offset, form, data_offset, &value); if (tag == DW_TAG_compile_unit) { - if (attribute == DW_AT_comp_dir) { - dest->comp_dir = (char *) value; + 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->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->offset = value; + func->low_pc = value; + } else if (attribute == DW_AT_high_pc) { + func->high_pc = value; } } } while (attribute != 0 || form != 0); + if (tag == DW_TAG_compile_unit) { + comp_unit->high_pc = comp_unit->low_pc + comp_unit->high_pc; + ++comp_unit; + ++dest->cu_count; + } + if (tag == DW_TAG_subprogram) { + func->high_pc = func->low_pc + func->high_pc; + func->comp_unit = dest->cu_count; ++func; ++dest->func_count; } } + + return(di_header.length + 4); } static void -parse_debug_line(u8 *file, - struct mi_sourcepoint *dest, struct mi_sourcefile *dest_files, char **dest_directories, - int *dest_size, int *dest_files_size, int *dest_directories_size) +parse_debug_info(u8 *file, struct mi_debuginfo *dest) { - u64 dl_offset = get_section_offset(file, ".debug_line"); + 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->sp_table; + int *dest_size = &unit->sp_count; + + struct mi_sourcefile *dest_files = unit->source_files; + int *dest_files_size = &unit->source_file_count; + + char **dest_directories = unit->source_directories; + int *dest_directories_size = &unit->source_dirs_count; struct dwarf_debug_line_header_v3_x32 header = { 0 }; memcpy(&header, file + dl_offset, 15); /* all fixed-size info */ @@ -640,6 +689,48 @@ is followed by a single null byte." */ 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->sp_table = calloc(1, unit->sp_count * sizeof(struct mi_sourcepoint)); + unit->source_directories = calloc(1, unit->source_dirs_count * sizeof(char *)); + unit->source_files = calloc(1, unit->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 diff --git a/main.c b/main.c index 3ab2a48..f5cbeb5 100644 --- a/main.c +++ b/main.c @@ -31,11 +31,7 @@ main(int argc, char *argv[]) printf("> "); fflush(stdout); - parse_debug_line(process.elf, 0, 0, 0, &process.debug.sp_count, &process.source_file_count, &process.source_dirs_count); - process.debug.sp_table = calloc(1, process.debug.sp_count * sizeof(struct mi_sourcepoint)); - process.source_directories = calloc(1, process.source_dirs_count * sizeof(char *)); - process.source_files = calloc(1, process.source_file_count * sizeof(struct mi_sourcefile)); - parse_debug_line(process.elf, process.debug.sp_table, process.source_files, process.source_directories, 0, 0, 0); + parse_debug_line(process.elf, &process.debug); while ((command_length = getline(&command, &max_command_length, stdin))) { if (command_length == 1) { diff --git a/trace_include.c b/res/001-simple-inc.c similarity index 100% rename from trace_include.c rename to res/001-simple-inc.c diff --git a/traceme.c b/res/001-simple.c similarity index 92% rename from traceme.c rename to res/001-simple.c index 503f53d..9a0210d 100644 --- a/traceme.c +++ b/res/001-simple.c @@ -1,7 +1,7 @@ #include #include -#include "trace_include.c" +#include "001-simple-inc.c" int main() { diff --git a/res/002-compilation-units-impl.c b/res/002-compilation-units-impl.c new file mode 100644 index 0000000..6ea94bb --- /dev/null +++ b/res/002-compilation-units-impl.c @@ -0,0 +1,8 @@ +#include "002-compilation-units.h" + +int foo(int arg) +{ + int sq = arg * arg; + int sq2 = sq * 2; + return(sq2); +} diff --git a/res/002-compilation-units.c b/res/002-compilation-units.c new file mode 100644 index 0000000..09b0031 --- /dev/null +++ b/res/002-compilation-units.c @@ -0,0 +1,9 @@ +#include "002-compilation-units.h" + +int +main(void) +{ + int a = 3; + int b = foo(a); + return(b); +} diff --git a/res/002-compilation-units.h b/res/002-compilation-units.h new file mode 100644 index 0000000..f243ad9 --- /dev/null +++ b/res/002-compilation-units.h @@ -0,0 +1 @@ +int foo(int arg); diff --git a/res/Makefile b/res/Makefile new file mode 100644 index 0000000..159ef78 --- /dev/null +++ b/res/Makefile @@ -0,0 +1,3 @@ +all: + gcc -g -o 001-simple 001-simple.c + gcc -g -o 002-compilation-units 002-compilation-units.c 002-compilation-units-impl.c diff --git a/res/traceme b/res/traceme new file mode 100755 index 0000000..fdc818f Binary files /dev/null and b/res/traceme differ diff --git a/util.c b/util.c index ddd9340..ea79392 100644 --- a/util.c +++ b/util.c @@ -98,7 +98,7 @@ get_address_of_subroutine(struct mi_process proc, char *sr) for (int i = 0; i < proc.debug.func_count; ++i) { struct mi_function *func = proc.debug.functions + i; if (0 == strcmp(func->name, sr)) { - return(func->offset); + return(func->low_pc); } } @@ -106,14 +106,21 @@ get_address_of_subroutine(struct mi_process proc, char *sr) } static struct mi_sourcepoint * -pc_to_sourcepoint(struct mi_process proc, u64 pc) +pc_to_sourcepoint(struct mi_process proc, u64 pc, int *comp_unit) { // NOTE: find first point BIGGER that pc, return the sourcepoint just before that // TODO: binary search - for (int i = 0; i < proc.debug.sp_count; ++i) { - struct mi_sourcepoint *point = proc.debug.sp_table + i; - if (point->pc > pc) { - return(proc.debug.sp_table + i - 1); + + for (int c = 0; c < proc.debug.cu_count; ++c) { + struct mi_compunit unit = proc.debug.compilation_units[c]; + if (unit.low_pc <= pc && pc < unit.high_pc) { + for (int i = 0; i < unit.sp_count; ++i) { + struct mi_sourcepoint *point = unit.sp_table + i; + if (point->pc > pc) { + *comp_unit = c; + return(unit.sp_table + i - 1); + } + } } } @@ -155,27 +162,29 @@ read_file_mmap(char *path) } static void -print_sourcepoint(struct mi_process proc, struct mi_sourcepoint *sp) +print_sourcepoint(struct mi_process proc, int comp_unit, struct mi_sourcepoint *sp) { if (!sp) { printf("Unknown location\n"); return; } + struct mi_compunit unit = proc.debug.compilation_units[comp_unit]; + // NOTE: sourcepoint file indices are 1-based - if (proc.source_files[sp->file - 1].file.data == 0) { - char *file_filename = proc.source_files[sp->file - 1].filename; - char *file_dirname = proc.source_files[sp->file - 1].dir; - char *comp_dir = proc.debug.comp_dir; + if (unit.source_files[sp->file - 1].file.data == 0) { + char *file_filename = unit.source_files[sp->file - 1].filename; + char *file_dirname = unit.source_files[sp->file - 1].dir; + char *comp_dir = unit.comp_dir; char full_path[512] = { 0 }; snprintf(full_path, 511, "%s/%s/%s", comp_dir, file_dirname, file_filename); struct mi_buffer file = read_file_mmap(full_path); - proc.source_files[sp->file - 1].file = file; + unit.source_files[sp->file - 1].file = file; } - struct mi_buffer file = proc.source_files[sp->file - 1].file; + struct mi_buffer file = unit.source_files[sp->file - 1].file; char *source = (char *) file.data; u64 size = file.size;