Skip to content

Commit

Permalink
feat: add debuggee restart
Browse files Browse the repository at this point in the history
  • Loading branch information
tensorush committed Jul 20, 2024
1 parent 5c2e5ab commit 7c7374d
Show file tree
Hide file tree
Showing 5 changed files with 142 additions and 69 deletions.
6 changes: 3 additions & 3 deletions src/Breakpoint.zig
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ pub const Location = struct {
line_num: u32,
};

saved_inst: abi.bp_inst_width_t = undefined,
saved_inst: abi.trap_inst_width_t = undefined,
loc: ?Location = null,
is_set: bool = false,
pid: std.posix.pid_t,
Expand All @@ -32,15 +32,15 @@ pub fn initLocation(pid: std.posix.pid_t, addr: usize, loc: Location) !Breakpoin
pub fn set(self: *Breakpoint) !void {
var inst: usize = undefined;
try ptrace.readAddress(self.pid, self.addr, &inst);
try ptrace.writeAddress(self.pid, self.addr, (inst & abi.BP_INST_MASK) | abi.BP_INST);
try ptrace.writeAddress(self.pid, self.addr, (inst & abi.TRAP_INST_MASK) | abi.TRAP_INST);
self.saved_inst = @truncate(inst);
self.is_set = true;
}

pub fn unset(self: *Breakpoint) !void {
var inst: usize = undefined;
try ptrace.readAddress(self.pid, self.addr, &inst);
try ptrace.writeAddress(self.pid, self.addr, (inst & abi.BP_INST_MASK) | self.saved_inst);
try ptrace.writeAddress(self.pid, self.addr, (inst & abi.TRAP_INST_MASK) | self.saved_inst);
self.saved_inst = undefined;
self.is_set = false;
}
Expand Down
78 changes: 69 additions & 9 deletions src/abi.zig
Original file line number Diff line number Diff line change
Expand Up @@ -7,22 +7,22 @@ pub const PC: Register = switch (builtin.cpu.arch) {
else => unreachable,
};

pub const BP_INST: bp_inst_width_t = switch (builtin.cpu.arch) {
pub const TRAP_INST: trap_inst_width_t = switch (builtin.cpu.arch) {
.x86_64 => 0xCC, // "int3"
.aarch64 => 0xD420_0000, // "brk #0"
else => unreachable,
};

pub const BP_INST_MASK: usize = ~@as(usize, std.math.maxInt(bp_inst_width_t));
pub const TRAP_INST_MASK: usize = ~@as(usize, std.math.maxInt(trap_inst_width_t));

pub const bp_inst_width_t = switch (builtin.cpu.arch) {
pub const trap_inst_width_t = switch (builtin.cpu.arch) {
.x86_64 => u8,
.aarch64 => u32,
else => unreachable,
};

pub const Register = switch (builtin.cpu.arch) {
.x86_64 => enum {
.x86_64 => enum(u5) {
rax,
rdx,
rcx,
Expand All @@ -40,16 +40,76 @@ pub const Register = switch (builtin.cpu.arch) {
r14,
r15,
rip,
eflags,
xmm0,
xmm1,
xmm2,
xmm3,
xmm4,
xmm5,
xmm6,
xmm7,
xmm8,
xmm9,
xmm10,
xmm11,
xmm12,
xmm13,
xmm14,
xmm15,
st0,
st1,
st2,
st3,
st4,
st5,
st6,
st7,
mm0,
mm1,
mm2,
mm3,
mm4,
mm5,
mm6,
mm7,
rflags,
es,
cs,
ss,
fs_base,
gs_base,
ds,
es,
fs,
gs,
orig_rax,
fs_base = 58,
gs_base,
tr = 62,
ldtr,
mxcsr,
fcw,
fsw,
xmm16,
xmm17,
xmm18,
xmm19,
xmm20,
xmm21,
xmm22,
xmm23,
xmm24,
xmm25,
xmm26,
xmm27,
xmm28,
xmm29,
xmm30,
xmm31,
k0 = 118,
k1,
k2,
k3,
k4,
k5,
k6,
k7,
},
.aarch64 => enum {
r0,
Expand Down
91 changes: 46 additions & 45 deletions src/dobby.zig
Original file line number Diff line number Diff line change
Expand Up @@ -6,36 +6,26 @@ const LineTable = @import("LineTable.zig");
const Breakpoint = @import("Breakpoint.zig");

const HELP =
\\b <str> <int> - toggle breakpoint in file <str> at line <int>
\\a - assembly single step
\\s - source single step
\\l - list breakpoints
\\p - print variables
\\d - dump registers
\\v - step over
\\c - continue
\\o - step out
\\i - step in
\\r - restart
\\h - help
\\q - quit
\\ b <str>:<uint> - toggle breakpoint in file <str> at line <uint>
\\ TODO: w <str> - toggle watchpoint at variable <str>
\\ TODO: l - list breakpoints, watchpoints, threads
\\ TODO: t <uint> - switch to thread <uint>
\\ a - assembly-level single step
\\ TODO: s - source-level single step
\\ TODO: p - print variables
\\ d - dump registers
\\ TODO: v - step over
\\ o - step out
\\ c - continue
\\ r - restart
\\ h - help
\\ q - quit
\\
;

pub fn debug(allocator: std.mem.Allocator, reader: anytype, writer: anytype, elf_file_path: []const u8) !void {
// Spawn debuggee
const pid = try std.posix.fork();

// Branch into debuggee
if (pid == 0) {
try std.posix.ptrace(std.os.linux.PTRACE.TRACEME, pid, 0, 0);
const posix_exe_file_path = try std.posix.toPosixPath(elf_file_path);
std.posix.execveZ(&posix_exe_file_path, @ptrCast(std.os.argv.ptr), @ptrCast(std.os.environ.ptr)) catch unreachable;
}

// Wait for debuggee to be trapped
ptrace.waitForTrapSignal(pid);
try ptrace.killOnExit(pid);
// Trace debuggee
var pid = try ptrace.traceDebuggee(elf_file_path);

// Read user program's DWARF information
var sections: std.dwarf.DwarfInfo.SectionArray = std.dwarf.DwarfInfo.null_section_array;
Expand All @@ -52,7 +42,7 @@ pub fn debug(allocator: std.mem.Allocator, reader: anytype, writer: anytype, elf
try writer.writeAll(HELP);

// Prompt user for input
try writer.writeAll("<dobby> \n");
try writer.writeAll("<dobby> ");

// Handle user commands
var line_buf: [config.MAX_LINE_LEN]u8 = undefined;
Expand Down Expand Up @@ -86,6 +76,16 @@ pub fn debug(allocator: std.mem.Allocator, reader: anytype, writer: anytype, elf
);
}
},
'w' => @panic("TODO"),
'l' => {
var bps_iter = bps.valueIterator();
while (bps_iter.next()) |bp| {
try writer.print("{}\n", .{bp.loc.?});
}
//TODO: Add watchpoints
//TODO: Add threads
},
't' => @panic("TODO"),
'a' => {
const pc = try ptrace.readRegister(pid, abi.PC);
if (bps.getPtr(pc)) |bp| {
Expand All @@ -96,28 +96,13 @@ pub fn debug(allocator: std.mem.Allocator, reader: anytype, writer: anytype, elf
try LineTable.printSource(dwarf_info, allocator, writer, pc);
},
's' => @panic("TODO"),
'l' => {
var bps_iter = bps.valueIterator();
while (bps_iter.next()) |bp| {
try writer.print("{}\n", .{bp.loc.?});
}
},
'p' => @panic("TODO"),
'd' => {
inline for (comptime std.enums.values(abi.Register)) |reg| {
try writer.print("{} = {}", .{ reg, try ptrace.readRegister(pid, reg) });
}
},
'v' => @panic("TODO"),
'c' => {
if (bps.getPtr(try ptrace.readRegister(pid, abi.PC))) |bp| {
if (bp.is_set) {
try bp.reset();
}
}
const pc = try ptrace.continueExecution(pid);
try LineTable.printSource(dwarf_info, allocator, writer, pc);
},
'o' => {
const fp = try ptrace.readRegister(pid, .rbp);
var ret_addr: usize = undefined;
Expand All @@ -140,13 +125,29 @@ pub fn debug(allocator: std.mem.Allocator, reader: anytype, writer: anytype, elf
try some_bp.unset();
}
},
'i' => @panic("TODO"),
'r' => @panic("TODO"),
'c' => {
if (bps.getPtr(try ptrace.readRegister(pid, abi.PC))) |bp| {
if (bp.is_set) {
try bp.reset();
}
}
const pc = try ptrace.continueExecution(pid);
try LineTable.printSource(dwarf_info, allocator, writer, pc);
},
'r' => {
try std.posix.kill(pid, std.posix.SIG.KILL);
pid = try ptrace.traceDebuggee(elf_file_path);

var bps_iter = bps.valueIterator();
while (bps_iter.next()) |bp| {
bp.pid = pid;
}
},
'h' => try writer.writeAll(HELP),
'q' => break,
else => |command| try writer.print("Unknown command: '{c}'\n", .{command}),
}

try writer.writeAll("<dobby> \n");
try writer.writeAll("<dobby> ");
}
}
18 changes: 6 additions & 12 deletions src/main.zig
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@ const clap = @import("clap");
const dobby = @import("dobby.zig");

const PARAMS = clap.parseParamsComptime(
\\-h, --help Display help menu.
\\<str> ELF file path.
\\-a, --args <str> Debuggee CLI arguments.
\\-h, --help Display help.
\\<str> Debugee ELF file path.
\\
);

Expand All @@ -15,21 +16,16 @@ pub fn main() !void {
};

var arena = std.heap.ArenaAllocator.init(gpa.allocator());
defer arena.deinit();
const allocator = arena.allocator();
defer arena.deinit();

const std_in = std.io.getStdIn();
const reader = std_in.reader();

const std_out = std.io.getStdOut();
var buf_writer = std.io.bufferedWriter(std_out.writer());
const writer = buf_writer.writer();
const writer = std_out.writer();

var diag = clap.Diagnostic{};
var res = clap.parse(clap.Help, &PARAMS, clap.parsers.default, .{ .allocator = allocator, .diagnostic = &diag }) catch |err| {
diag.report(std.io.getStdErr().writer(), err) catch {};
return err;
};
var res = try clap.parse(clap.Help, &PARAMS, clap.parsers.default, .{ .allocator = allocator });
defer res.deinit();

var elf_file_path: []const u8 = "zig-out/bin/example";
Expand All @@ -43,6 +39,4 @@ pub fn main() !void {
}

try dobby.debug(allocator, reader, writer, elf_file_path);

try buf_writer.flush();
}
18 changes: 18 additions & 0 deletions src/ptrace.zig
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,26 @@ const abi = @import("abi.zig");

const c = @cImport({
@cInclude("sys/user.h");
// @cInclude("sys/personality.h");
});

pub fn traceDebuggee(elf_file_path: []const u8) std.posix.pid_t {
const pid = try std.posix.fork();
if (pid == 0) {
// c.personality(ADDR_NO_RANDOMIZE);
try std.posix.ptrace(std.os.linux.PTRACE.TRACEME, pid, 0, 0);
const posix_exe_file_path = try std.posix.toPosixPath(elf_file_path);
std.posix.execveZ(
&posix_exe_file_path,
@ptrCast(std.os.argv.ptr),
@ptrCast(std.os.environ.ptr),
) catch @panic("Failed to execute debuggee!");
}
waitForTrapSignal(pid);
try killOnExit(pid);
return pid;
}

pub fn continueExecution(pid: std.posix.pid_t) !usize {
try std.posix.ptrace(std.os.linux.PTRACE.CONT, pid, 0, 0);
waitForTrapSignal(pid);
Expand Down

0 comments on commit 7c7374d

Please sign in to comment.