Browse Source

Create line number table. Structure the code a bit

master
A.Olokhtonov 3 years ago
parent
commit
9f5a533af4
  1. 94
      command.c
  2. 72
      common.h
  3. 93
      dwarf.c
  4. 0
      elf_dwarf.h
  5. 343
      main.c
  6. 169
      util.c

94
command.c

@ -0,0 +1,94 @@ @@ -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
}

72
common.h

@ -0,0 +1,72 @@ @@ -0,0 +1,72 @@
#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;
#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)

93
dwarf.c

@ -294,9 +294,11 @@ find_subroutine_offset(u8 *file, u64 header_size, u8 address_size, @@ -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." */ @@ -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." */ @@ -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." */ @@ -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." */ @@ -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." */ @@ -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." */ @@ -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." */ @@ -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." */ @@ -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) @@ -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);
}

0
structs.h → elf_dwarf.h

343
main.c

@ -1,319 +1,8 @@ @@ -1,319 +1,8 @@
#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 "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 = { &regs, 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 = { &regs._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[]) @@ -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("> ");
}

169
util.c

@ -0,0 +1,169 @@ @@ -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 = { &regs, 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 = { &regs._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]);
}
Loading…
Cancel
Save