diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/elfo-pretty.zig | 181 |
1 files changed, 181 insertions, 0 deletions
diff --git a/src/elfo-pretty.zig b/src/elfo-pretty.zig new file mode 100644 index 0000000..d4710ad --- /dev/null +++ b/src/elfo-pretty.zig @@ -0,0 +1,181 @@ +const std = @import("std"); +const cs = @import("capstone"); + +pub fn main() !void { + var args = std.process.args(); + _ = args.skip(); // skip argv[0] + + const bw = std.debug.lockStderrWriter(&.{}); + defer std.debug.unlockStderrWriter(); + const ttyconf = std.io.tty.detectConfig(.stderr()); + + var gpa: std.heap.GeneralPurposeAllocator(.{}) = .init; + const allocator = gpa.allocator(); + try printElf(allocator, args.next() orelse "./study-samples/split", bw, ttyconf); +} + +pub fn printElf( + allocator: std.mem.Allocator, + path: []const u8, + bw: *std.Io.Writer, + ttyconf: std.io.tty.Config, +) !void { + const f = try std.fs.cwd().openFile(path, .{ .mode = .read_only }); + var buffer = try allocator.alloc(u8, 1024 * 100); + // const buffer = try allocator.alloc(u8, 1024 * 10000000); + var reader = f.reader(buffer); + const header = try std.elf.Header.read(&reader.interface); + + var handle: usize = undefined; + std.debug.assert(cs.cs_open(cs.CS_ARCH_X86, cs.CS_MODE_64, @ptrCast(&handle)) == cs.CS_ERR_OK); + std.debug.print("capstone handle {x}\n", .{handle}); + + const shstr = blk: { + var section_it = header.iterateSectionHeaders(&reader); + var section_idx: u32 = 0; + while (try section_it.next()) |s| { + defer section_idx += 1; + if (section_idx == header.shstrndx) { + std.debug.assert(s.sh_type == std.elf.SHT_STRTAB); + break :blk s; + } + } + break :blk null; + }; + // during the program's runtime, how will be this information accessed? + + const elf_strtab_slice = blk: { + try reader.seekTo(shstr.?.sh_offset); + const slice = try reader.interface.take(shstr.?.sh_size); + const owned_slice = try allocator.alloc(u8, slice.len); + @memcpy(owned_slice, slice); + break :blk owned_slice; + }; + + var strs: std.ArrayList([]const u8) = try .initCapacity(allocator, 8); + { + var str_it = std.mem.splitScalar(u8, elf_strtab_slice, 0); + while (str_it.next()) |str| { + const owned_str = try allocator.alloc(u8, str.len); + @memcpy(owned_str, str); + try strs.append(allocator, owned_str); + } + } + + var sections: std.ArrayList(std.elf.Elf64_Shdr) = try .initCapacity(allocator, 8); + { + var section_it = header.iterateSectionHeaders(&reader); + while (try section_it.next()) |section| { + try sections.append(allocator, section); + } + std.mem.sort(std.elf.Elf64_Shdr, sections.items, {}, struct { + pub fn inner(_: void, x: std.elf.Elf64_Shdr, y: std.elf.Elf64_Shdr) bool { + // NOTE: use the running mem or the static elf mem? + // return x.sh_offset < y.sh_offset; + return x.sh_addr < y.sh_addr; + } + }.inner); + } + + for (sections.items) |section| { + if (section.sh_size > 0 and section.sh_addr > 0) { + try ttyconf.setColor(bw, .bright_green); + try bw.print("\n{s}", .{std.mem.sliceTo(elf_strtab_slice[section.sh_name..], 0)}); + try ttyconf.setColor(bw, .reset); + try ttyconf.setColor(bw, .dim); + try bw.print(" -- {x}-{x}\n", .{ + section.sh_addr, + section.sh_addr + section.sh_size, + // section, + }); + try ttyconf.setColor(bw, .reset); + try reader.seekTo(section.sh_offset); + + if (buffer.len < section.sh_size) { + buffer = try allocator.realloc(buffer, section.sh_size); + reader = f.reader(buffer); + } + // FIXME: this is buggy + const section_slice = try reader.interface.take(section.sh_size); + if (section.sh_type == std.elf.SHT_PROGBITS) { + const instrs: []cs.cs_insn = blk: { + var insn: [*]cs.cs_insn = undefined; + const count = cs.cs_disasm(handle, section_slice.ptr, section_slice.len, section.sh_addr, 0, @ptrCast(&insn)); + break :blk insn[0..count]; + }; + + try dumpInstr(bw, ttyconf, instrs); + } else { + std.debug.print("section pointer {x}\n", .{@intFromPtr(section_slice.ptr)}); + try dumpHexFallible(u64, bw, ttyconf, section_slice, section.sh_addr); + } + } + } +} + +fn dumpInstr( + bw: *std.Io.Writer, + ttyconf: std.io.tty.Config, + instrs: []cs.cs_insn, +) !void { + for (instrs) |instr| { + try ttyconf.setColor(bw, .dim); + try bw.print("{x:0>[1]} ", .{ instr.address, @sizeOf(usize) * 2 }); + try ttyconf.setColor(bw, .reset); + try bw.print("{s} {s}\n", .{ instr.mnemonic, instr.op_str }); + } +} + +/// Prints a hexadecimal view of the bytes, returning any error that occurs. +pub fn dumpHexFallible(_: type, bw: *std.Io.Writer, ttyconf: std.io.tty.Config, bytes: []const u8, offset: u64) !void { + // @breakpoint(); + const nbytes = 16; + var chunks = std.mem.window(u8, @ptrCast(@alignCast(bytes)), nbytes, nbytes); + while (chunks.next()) |window| { + // 1. Print the address. + const address = ((0x10 * (std.math.divCeil(usize, chunks.index orelse bytes.len, nbytes) catch unreachable)) - 0x10) + offset; + try ttyconf.setColor(bw, .dim); + // We print the address in lowercase and the bytes in uppercase hexadecimal to distinguish them more. + // Also, make sure all lines are aligned by padding the address. + try bw.print("{x:0>[1]} ", .{ address, @sizeOf(usize) * 2 }); + try ttyconf.setColor(bw, .reset); + + // 2. Print the bytes. + for (window, 0..) |byte, index| { + try bw.print("{X:0>2} ", .{byte}); + if (index == 7) try bw.writeByte(' '); + } + try bw.writeByte(' '); + if (window.len < 16) { + var missing_columns = (16 - window.len) * 3; + if (window.len < 8) missing_columns += 1; + try bw.splatByteAll(' ', missing_columns); + } + + const window_bytes: []const u8 = @ptrCast(@alignCast(window)); + + // 3. Print the characters. + for (window_bytes) |byte| { + if (std.ascii.isPrint(byte)) { + try bw.writeByte(byte); + } else { + // Related: https://github.com/ziglang/zig/issues/7600 + if (ttyconf == .windows_api) { + try bw.writeByte('.'); + continue; + } + + // Let's print some common control codes as graphical Unicode symbols. + // We don't want to do this for all control codes because most control codes apart from + // the ones that Zig has escape sequences for are likely not very useful to print as symbols. + switch (byte) { + '\n' => try bw.writeAll("␊"), + '\r' => try bw.writeAll("␍"), + '\t' => try bw.writeAll("␉"), + else => try bw.writeByte('.'), + } + } + } + try bw.writeByte('\n'); + } +} |
