From 9f5a533af42cbcc88d02a8c5f6ea2818c831c1b2 Mon Sep 17 00:00:00 2001 From: "A.Olokhtonov" Date: Sat, 10 Jul 2021 22:54:41 +0300 Subject: [PATCH] Create line number table. Structure the code a bit --- command.c | 94 +++++++++++ common.h | 72 ++++++++ dwarf.c | 93 ++++++++--- structs.h => elf_dwarf.h | 0 main.c | 343 ++------------------------------------- util.c | 169 +++++++++++++++++++ 6 files changed, 416 insertions(+), 355 deletions(-) create mode 100644 command.c create mode 100644 common.h rename structs.h => elf_dwarf.h (100%) create mode 100644 util.c diff --git a/command.c b/command.c new file mode 100644 index 0000000..a971536 --- /dev/null +++ b/command.c @@ -0,0 +1,94 @@ +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); + struct mi_sourcepoint *sp = pc_to_sourcepoint(proc, regs.rip - proc.base_address); + print_current_line(proc, sp->line); +#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); + + struct mi_sourcepoint *sp = pc_to_sourcepoint(proc, regs.rip - proc.base_address); + struct mi_sourcepoint *next_sp; + + 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_sp = pc_to_sourcepoint(proc, regs.rip - proc.base_address); + // TODO: skip call, repXXX, etc + } while (next_sp->line == sp->line); + + print_current_line(proc, next_sp->line); +} + +static void +command_list(struct mi_process proc) +{ + struct mi_registers regs = get_process_registers(proc); + struct mi_sourcepoint *sp = pc_to_sourcepoint(proc, regs.rip - proc.base_address); + print_current_line(proc, sp->line); +} + +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 +} \ No newline at end of file diff --git a/common.h b/common.h new file mode 100644 index 0000000..b06251d --- /dev/null +++ b/common.h @@ -0,0 +1,72 @@ +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +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; + +#include "elf_dwarf.h" + +struct mi_sourcepoint { + u64 pc; + int line; + int column; + int file; +}; + +struct mi_process { + pid_t pid; + + char *source; + u64 source_size; + + u8 *elf; + u64 elf_size; + + u64 base_address; + u64 main_address; + + struct mi_sourcepoint *sp_table; + int sp_count; +}; + +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; +}; + +#define NT_PRSTATUS 1 +#define DIE(string) do { fprintf(stderr, string); exit(1); } while (0) diff --git a/dwarf.c b/dwarf.c index edf2a4a..422d3a5 100644 --- a/dwarf.c +++ b/dwarf.c @@ -294,9 +294,11 @@ find_subroutine_offset(u8 *file, u64 header_size, u8 address_size, return(0); } -static u64 -read_line_number_info(u8 *file, u64 dl_offset, u64 pc) +static void +construct_line_number_table(u8 *file, struct mi_sourcepoint *dest, int *dest_size) { + u64 dl_offset = get_section_offset(file, ".debug_line"); + struct dwarf_debug_line_header_v3_x32 header = { 0 }; memcpy(&header, file + dl_offset, 15); /* all fixed-size info */ @@ -380,8 +382,8 @@ is followed by a single null byte." */ dl_offset = p - file + 1; u8 opcode; - enum dwarf_lnp_opcode opcode_regular; - enum dwarf_lnp_opcode_extended opcode_extended; + enum dwarf_lnp_opcode opcode_regular = 0; + enum dwarf_lnp_opcode_extended opcode_extended = 0; p = file + dl_offset; @@ -391,8 +393,7 @@ is followed by a single null byte." */ state.line = 1; state.is_stmt = header.default_is_stmt; - - u64 last_line = 0; + u64 pc_count = 0; do { opcode = *p; @@ -417,13 +418,23 @@ is followed by a single null byte." */ 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); - last_line = state.line; state.line += operand; break; } @@ -461,6 +472,17 @@ is followed by a single null byte." */ 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; } @@ -469,6 +491,17 @@ is followed by a single null byte." */ 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; } @@ -496,14 +529,22 @@ is followed by a single null byte." */ 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; + + 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 */ @@ -530,6 +571,17 @@ is followed by a single null byte." */ 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; } @@ -560,14 +612,15 @@ is followed by a single null byte." */ } } - // TODO: this is off by one? - if (state.pc >= pc) { - return(last_line); - } + //if (state.pc >= pc) { + //return(last_line); + //} } while (opcode_extended != DW_LNE_end_sequence); - return(0); + if (!dest) { + *dest_size = pc_count; + } } static u64 @@ -659,17 +712,5 @@ get_address_of_subroutine(u8 *file, char *sr) 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); } \ No newline at end of file diff --git a/structs.h b/elf_dwarf.h similarity index 100% rename from structs.h rename to elf_dwarf.h diff --git a/main.c b/main.c index 65eeff7..20a1663 100644 --- a/main.c +++ b/main.c @@ -1,319 +1,8 @@ -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -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 "common.h" #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 -} +#include "util.c" +#include "command.c" int main(int argc, char *argv[]) @@ -339,35 +28,31 @@ main(int argc, char *argv[]) printf("> "); fflush(stdout); + construct_line_number_table(process.elf, 0, &process.sp_count); + process.sp_table = malloc(process.sp_count * sizeof(struct mi_sourcepoint)); + construct_line_number_table(process.elf, process.sp_table, 0); + 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) { + } else if (0 == strncmp(command, "regs\n", command_length)) { command_regs(process); - } else if (strncmp(command, "step\n", command_length) == 0) { + } else if (0 == strncmp(command, "step\n", command_length)) { command_step(process); - } else if (strncmp(command, "start\n", command_length) == 0) { + } else if (0 == strncmp(command, "start\n", command_length)) { command_start(process); - } else if (strncmp(command, "next\n", command_length) == 0) { + } else if (0 == strncmp(command, "next\n", command_length)) { command_next(process); - } else if (strncmp(command, "line\n", command_length) == 0) { + } else if (0 == strncmp(command, "line\n", command_length)) { command_list(process); - } else if (strncmp(command, "cont\n", command_length) == 0) { + } else if (0 == strncmp(command, "cont\n", command_length)) { command_cont(process); - } else if (strncmp(command, "stop\n", command_length) == 0) { + } else if (0 == strncmp(command, "stop\n", command_length)) { 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("> "); } diff --git a/util.c b/util.c new file mode 100644 index 0000000..e61584b --- /dev/null +++ b/util.c @@ -0,0 +1,169 @@ +static struct mi_sourcepoint * +pc_to_sourcepoint(struct mi_process proc, u64 pc) +{ + // NOTE: find first point BIGGER that pc, return the sourcepoint just before that + // TODO: binary search + for (int i = 0; i < proc.sp_count; ++i) { + struct mi_sourcepoint *point = proc.sp_table + i; + if (point->pc > pc) { + return(proc.sp_table + i - 1); + } + } + + return(0); +} + +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]); +} \ No newline at end of file