| ... | @@ -16,9 +16,12 @@ const Options = struct { | ... | @@ -16,9 +16,12 @@ const Options = struct { |
| 16 | input_path: []const u8, | 16 | input_path: []const u8, |
| 17 | member_filters: []const []const u8 = &.{}, | 17 | member_filters: []const []const u8 = &.{}, |
| 18 | member_headers: bool, | 18 | member_headers: bool, |
| | 19 | omit_elements: std.enums.EnumArray(Element, bool), |
| | 20 | redact: std.enums.EnumArray(FieldKind, bool), |
| 19 | relocs: bool, | 21 | relocs: bool, |
| 20 | section_filters: []const []const u8 = &.{}, | 22 | section_filters: []const []const u8 = &.{}, |
| 21 | section_headers: bool, | 23 | section_headers: bool, |
| | 24 | symbol_filters: []const []const u8 = &.{}, |
| 22 | strings: bool, | 25 | strings: bool, |
| 23 | symbols: bool, | 26 | symbols: bool, |
| 24 | tls: bool, | 27 | tls: bool, |
| ... | @@ -27,6 +30,20 @@ const Options = struct { | ... | @@ -27,6 +30,20 @@ const Options = struct { |
| 27 | linker_member: ?std.coff.ArchiveMemberHeader.Kind, | 30 | linker_member: ?std.coff.ArchiveMemberHeader.Kind, |
| 28 | }; | 31 | }; |
| 29 | | 32 | |
| | 33 | const FieldKind = enum { |
| | 34 | va, |
| | 35 | rva, |
| | 36 | ord, |
| | 37 | size, |
| | 38 | }; |
| | 39 | |
| | 40 | const Element = enum { |
| | 41 | @"file-type", |
| | 42 | @"table-header", |
| | 43 | @"header-names", |
| | 44 | newlines, |
| | 45 | }; |
| | 46 | |
| 30 | pub fn main(init: std.process.Init) !void { | 47 | pub fn main(init: std.process.Init) !void { |
| 31 | const io = init.io; | 48 | const io = init.io; |
| 32 | const args = try init.minimal.args.toSlice(init.arena.allocator()); | 49 | const args = try init.minimal.args.toSlice(init.arena.allocator()); |
| ... | @@ -40,12 +57,15 @@ pub fn main(init: std.process.Init) !void { | ... | @@ -40,12 +57,15 @@ pub fn main(init: std.process.Init) !void { |
| 40 | var opt_input_path: ?[]const u8 = null; | 57 | var opt_input_path: ?[]const u8 = null; |
| 41 | var opt_linker_member: ?std.coff.ArchiveMemberHeader.Kind = null; | 58 | var opt_linker_member: ?std.coff.ArchiveMemberHeader.Kind = null; |
| 42 | var opt_member_headers: ?bool = null; | 59 | var opt_member_headers: ?bool = null; |
| | 60 | var omit_elements: @FieldType(Options, "omit_elements") = .initFill(false); |
| | 61 | var redact: @FieldType(Options, "redact") = .initFill(false); |
| 43 | var opt_relocs: ?bool = null; | 62 | var opt_relocs: ?bool = null; |
| 44 | var opt_section_headers: ?bool = null; | 63 | var opt_section_headers: ?bool = null; |
| 45 | var opt_strings: ?bool = null; | 64 | var opt_strings: ?bool = null; |
| 46 | var opt_symbols: ?bool = null; | 65 | var opt_symbols: ?bool = null; |
| 47 | var opt_tls: ?bool = null; | 66 | var opt_tls: ?bool = null; |
| 48 | var section_filters: std.ArrayList([]const u8) = .empty; | 67 | var section_filters: std.ArrayList([]const u8) = .empty; |
| | 68 | var symbol_filters: std.ArrayList([]const u8) = .empty; |
| 49 | var member_filters: std.ArrayList([]const u8) = .empty; | 69 | var member_filters: std.ArrayList([]const u8) = .empty; |
| 50 | while (i < args.len) : (i += 1) { | 70 | while (i < args.len) : (i += 1) { |
| 51 | const arg = args[i]; | 71 | const arg = args[i]; |
| ... | @@ -73,14 +93,37 @@ pub fn main(init: std.process.Init) !void { | ... | @@ -73,14 +93,37 @@ pub fn main(init: std.process.Init) !void { |
| 73 | opt_linker_member = .second_linker; | 93 | opt_linker_member = .second_linker; |
| 74 | } else if (mem.eql(u8, arg, "--member-headers")) { | 94 | } else if (mem.eql(u8, arg, "--member-headers")) { |
| 75 | opt_member_headers = true; | 95 | opt_member_headers = true; |
| 76 | } else if (mem.startsWith(u8, arg, "--only-section=")) { | 96 | } else if (mem.startsWith(u8, arg, "--omit-element=")) { |
| 77 | (try section_filters.addOne(arena)).* = try arena.dupe(u8, arg["--only-section=".len..]); | 97 | const kind = arg["--omit-element=".len..]; |
| | 98 | if (std.meta.stringToEnum(Element, kind)) |format_kind| { |
| | 99 | omit_elements.set(format_kind, true); |
| | 100 | } else if (std.mem.eql(u8, kind, "all")) { |
| | 101 | omit_elements = .initFill(true); |
| | 102 | } else { |
| | 103 | fatal("unrecognized element: {s}", .{kind}); |
| | 104 | } |
| 78 | } else if (mem.startsWith(u8, arg, "--only-member=")) { | 105 | } else if (mem.startsWith(u8, arg, "--only-member=")) { |
| 79 | (try member_filters.addOne(arena)).* = try arena.dupe(u8, arg["--only-member=".len..]); | 106 | (try member_filters.addOne(arena)).* = try arena.dupe(u8, arg["--only-member=".len..]); |
| | 107 | } else if (mem.startsWith(u8, arg, "--only-section=")) { |
| | 108 | (try section_filters.addOne(arena)).* = try arena.dupe(u8, arg["--only-section=".len..]); |
| | 109 | } else if (mem.startsWith(u8, arg, "--only-symbol=")) { |
| | 110 | (try symbol_filters.addOne(arena)).* = try arena.dupe(u8, arg["--only-symbol=".len..]); |
| | 111 | } else if (mem.startsWith(u8, arg, "--redact=")) { |
| | 112 | const kind = arg["--redact=".len..]; |
| | 113 | if (std.meta.stringToEnum(FieldKind, kind)) |field_kind| { |
| | 114 | redact.set(field_kind, true); |
| | 115 | } else if (std.mem.eql(u8, kind, "all")) { |
| | 116 | redact = .initFill(true); |
| | 117 | } else { |
| | 118 | fatal("unrecognized redaction kind: {s}", .{kind}); |
| | 119 | } |
| 80 | } else if (mem.eql(u8, arg, "--relocs")) { | 120 | } else if (mem.eql(u8, arg, "--relocs")) { |
| 81 | opt_relocs = true; | 121 | opt_relocs = true; |
| 82 | } else if (mem.eql(u8, arg, "--section-headers")) { | 122 | } else if (mem.eql(u8, arg, "--section-headers")) { |
| 83 | opt_section_headers = true; | 123 | opt_section_headers = true; |
| | 124 | } else if (mem.eql(u8, arg, "-s") or mem.eql(u8, arg, "--snapshot")) { |
| | 125 | omit_elements = .initFill(true); |
| | 126 | redact = .initFill(true); |
| 84 | } else if (mem.eql(u8, arg, "--strings")) { | 127 | } else if (mem.eql(u8, arg, "--strings")) { |
| 85 | opt_strings = true; | 128 | opt_strings = true; |
| 86 | } else if (mem.eql(u8, arg, "--symbols")) { | 129 | } else if (mem.eql(u8, arg, "--symbols")) { |
| ... | @@ -105,10 +148,13 @@ pub fn main(init: std.process.Init) !void { | ... | @@ -105,10 +148,13 @@ pub fn main(init: std.process.Init) !void { |
| 105 | .linker_member = opt_linker_member, | 148 | .linker_member = opt_linker_member, |
| 106 | .member_filters = member_filters.items, | 149 | .member_filters = member_filters.items, |
| 107 | .member_headers = opt_member_headers orelse false, | 150 | .member_headers = opt_member_headers orelse false, |
| | 151 | .omit_elements = omit_elements, |
| | 152 | .redact = redact, |
| | 153 | .relocs = opt_relocs orelse false, |
| 108 | .section_filters = section_filters.items, | 154 | .section_filters = section_filters.items, |
| 109 | .section_headers = opt_section_headers orelse false, | 155 | .section_headers = opt_section_headers orelse false, |
| 110 | .relocs = opt_relocs orelse false, | | |
| 111 | .strings = opt_strings orelse false, | 156 | .strings = opt_strings orelse false, |
| | 157 | .symbol_filters = symbol_filters.items, |
| 112 | .symbols = opt_symbols orelse false, | 158 | .symbols = opt_symbols orelse false, |
| 113 | .tls = opt_tls orelse false, | 159 | .tls = opt_tls orelse false, |
| 114 | }; | 160 | }; |
| ... | @@ -120,7 +166,15 @@ pub fn main(init: std.process.Init) !void { | ... | @@ -120,7 +166,15 @@ pub fn main(init: std.process.Init) !void { |
| 120 | var buffer: [4096]u8 = undefined; | 166 | var buffer: [4096]u8 = undefined; |
| 121 | var file_reader = file.reader(io, &buffer); | 167 | var file_reader = file.reader(io, &buffer); |
| 122 | var stdout_writer = std.Io.File.stdout().writerStreaming(io, &stdout_buffer); | 168 | var stdout_writer = std.Io.File.stdout().writerStreaming(io, &stdout_buffer); |
| 123 | dump(init.gpa, &opts, &file_reader, &stdout_writer.interface) catch |err| switch (err) { | 169 | |
| | 170 | const ctx: DumpContext = .{ |
| | 171 | .gpa = init.gpa, |
| | 172 | .opts = &opts, |
| | 173 | .fr = &file_reader, |
| | 174 | .w = &stdout_writer.interface, |
| | 175 | }; |
| | 176 | |
| | 177 | dump(&ctx) catch |err| switch (err) { |
| 124 | error.ReadFailed => return file_reader.err.?, | 178 | error.ReadFailed => return file_reader.err.?, |
| 125 | error.WriteFailed => return stdout_writer.err.?, | 179 | error.WriteFailed => return stdout_writer.err.?, |
| 126 | error.UnknownFile => fatal("unrecognized file: {s}", .{opts.input_path}), | 180 | error.UnknownFile => fatal("unrecognized file: {s}", .{opts.input_path}), |
| ... | @@ -130,60 +184,82 @@ pub fn main(init: std.process.Init) !void { | ... | @@ -130,60 +184,82 @@ pub fn main(init: std.process.Init) !void { |
| 130 | try stdout_writer.flush(); | 184 | try stdout_writer.flush(); |
| 131 | } | 185 | } |
| 132 | | 186 | |
| 133 | fn dump(gpa: std.mem.Allocator, opts: *const Options, fr: *Io.File.Reader, w: *Io.Writer) !void { | 187 | fn dump(d: *const DumpContext) !void { |
| 134 | const r = &fr.interface; | 188 | const r = &d.fr.interface; |
| 135 | try r.fill(4); | 189 | try r.fill(4); |
| 136 | elf: { | 190 | elf: { |
| 137 | if (!mem.eql(u8, r.buffered()[0..4], std.elf.MAGIC)) break :elf; | 191 | if (!mem.eql(u8, r.buffered()[0..4], std.elf.MAGIC)) break :elf; |
| 138 | return elf.dump(r, w); | 192 | return elf.dump(r, d.w); |
| 139 | } | 193 | } |
| 140 | macho: { | 194 | macho: { |
| 141 | if (mem.readInt(u32, r.buffered()[0..4], .little) != std.macho.MH_MAGIC_64) break :macho; | 195 | if (mem.readInt(u32, r.buffered()[0..4], .little) != std.macho.MH_MAGIC_64) break :macho; |
| 142 | return macho.dump(r, w); | 196 | return macho.dump(r, d.w); |
| 143 | } | 197 | } |
| 144 | wasm: { | 198 | wasm: { |
| 145 | comptime assert(std.wasm.magic.len == 4); | 199 | comptime assert(std.wasm.magic.len == 4); |
| 146 | if (!mem.eql(u8, r.buffered()[0..4], &std.wasm.magic)) break :wasm; | 200 | if (!mem.eql(u8, r.buffered()[0..4], &std.wasm.magic)) break :wasm; |
| 147 | return wasm.dump(r, w); | 201 | return wasm.dump(r, d.w); |
| 148 | } | 202 | } |
| 149 | coff: { | 203 | coff: { |
| 150 | const ext = std.fs.path.extension(opts.input_path); | 204 | const ext = std.fs.path.extension(d.opts.input_path); |
| 151 | const basename = std.fs.path.basename(opts.input_path); | 205 | const basename = std.fs.path.basename(d.opts.input_path); |
| 152 | if (std.mem.eql(u8, ext, ".exe") or std.mem.eql(u8, ext, ".dll")) { | 206 | if (std.mem.eql(u8, ext, ".exe") or std.mem.eql(u8, ext, ".dll")) { |
| 153 | if (!mem.eql(u8, r.buffered()[0..2], "MZ")) break :coff; | 207 | if (!mem.eql(u8, r.buffered()[0..2], "MZ")) break :coff; |
| 154 | try r.discardAll(std.coff.pe_pointer_offset); | 208 | try r.discardAll(std.coff.pe_pointer_offset); |
| 155 | const sig_offset = try r.takeInt(u32, .little); | 209 | const sig_offset = try r.takeInt(u32, .little); |
| 156 | try fr.seekTo(sig_offset); | 210 | try d.fr.seekTo(sig_offset); |
| 157 | const sig = try r.take(4); | 211 | const sig = try r.take(4); |
| 158 | | 212 | |
| 159 | if (!std.mem.eql(u8, sig, std.coff.pe_signature)) { | 213 | if (!std.mem.eql(u8, sig, std.coff.pe_signature)) { |
| 160 | try w.print("invalid PE signature: {x}", .{sig}); | 214 | try d.w.print("invalid PE signature: {x}", .{sig}); |
| 161 | return error.ParseFailure; | 215 | return error.ParseFailure; |
| 162 | } | 216 | } |
| 163 | | 217 | |
| 164 | try w.print("{s}: PE/COFF image\n\n", .{basename}); | 218 | if (d.element(.@"file-type")) |
| 165 | return coff.dumpObject(gpa, opts, true, basename, fr, w); | 219 | try d.w.print("{s}: PE/COFF image\n\n", .{basename}); |
| | 220 | |
| | 221 | return coff.dumpObject(d, true, basename); |
| 166 | } else if (std.mem.eql(u8, ext, ".lib")) { | 222 | } else if (std.mem.eql(u8, ext, ".lib")) { |
| 167 | r.fill(std.coff.archive_signature.len) catch break :coff; | 223 | r.fill(std.coff.archive_signature.len) catch break :coff; |
| 168 | if (!mem.eql(u8, r.buffered()[0..std.coff.archive_signature.len], std.coff.archive_signature)) break :coff; | 224 | if (!mem.eql(u8, r.buffered()[0..std.coff.archive_signature.len], std.coff.archive_signature)) break :coff; |
| 169 | try w.print("{s}: COFF archive\n\n", .{basename}); | 225 | if (d.element(.@"file-type")) |
| 170 | return coff.dumpArchive(gpa, opts, fr, w); | 226 | try d.w.print("{s}: COFF archive\n\n", .{basename}); |
| | 227 | |
| | 228 | return coff.dumpArchive(d); |
| 171 | } else if (std.mem.eql(u8, ext, ".obj")) { | 229 | } else if (std.mem.eql(u8, ext, ".obj")) { |
| 172 | try w.print("{s}: COFF object\n\n", .{basename}); | 230 | if (d.element(.@"file-type")) |
| 173 | return coff.dumpObject(gpa, opts, false, basename, fr, w); | 231 | try d.w.print("{s}: COFF object\n\n", .{basename}); |
| | 232 | |
| | 233 | return coff.dumpObject(d, false, basename); |
| 174 | } | 234 | } |
| 175 | } | 235 | } |
| 176 | return error.UnknownFile; | 236 | return error.UnknownFile; |
| 177 | } | 237 | } |
| 178 | | 238 | |
| 179 | fn failParse( | 239 | const DumpContext = struct { |
| | 240 | gpa: std.mem.Allocator, |
| 180 | opts: *const Options, | 241 | opts: *const Options, |
| 181 | comptime fmt: []const u8, | 242 | fr: *Io.File.Reader, |
| 182 | args: anytype, | 243 | w: *Io.Writer, |
| 183 | ) noreturn { | 244 | |
| 184 | std.log.err("error parsing '{s}'", .{std.fs.path.basename(opts.input_path)}); | 245 | fn element(self: *const DumpContext, e: Element) bool { |
| 185 | fatal(fmt, args); | 246 | return !self.opts.omit_elements.get(e); |
| 186 | } | 247 | } |
| | 248 | |
| | 249 | fn redacted(self: *const DumpContext, opt_kind: ?FieldKind) bool { |
| | 250 | const kind = opt_kind orelse return false; |
| | 251 | return self.opts.redact.get(kind); |
| | 252 | } |
| | 253 | |
| | 254 | fn failParse( |
| | 255 | ctx: *const DumpContext, |
| | 256 | comptime fmt: []const u8, |
| | 257 | args: anytype, |
| | 258 | ) noreturn { |
| | 259 | std.log.err("error parsing '{s}'", .{std.fs.path.basename(ctx.opts.input_path)}); |
| | 260 | fatal(fmt, args); |
| | 261 | } |
| | 262 | }; |
| 187 | | 263 | |
| 188 | const elf = struct { | 264 | const elf = struct { |
| 189 | fn dump(r: *Io.Reader, w: *Io.Writer) !void { | 265 | fn dump(r: *Io.Reader, w: *Io.Writer) !void { |
| ... | @@ -230,29 +306,33 @@ const coff = struct { | ... | @@ -230,29 +306,33 @@ const coff = struct { |
| 230 | file_mode: u24, | 306 | file_mode: u24, |
| 231 | size: u34, | 307 | size: u34, |
| 232 | | 308 | |
| 233 | pub fn fromRaw(opts: *const Options, raw_header: *const std.coff.ArchiveMemberHeader, opt_longnames: ?[]const u8) @This() { | 309 | pub fn fromRaw(d: *const DumpContext, raw_header: *const std.coff.ArchiveMemberHeader, opt_longnames: ?[]const u8) @This() { |
| 234 | const name = raw_header.parseName(opt_longnames) catch |err| switch (err) { | 310 | const name = raw_header.parseName(opt_longnames) catch |err| switch (err) { |
| 235 | error.BadName => failParse(opts, "malformed member name: '{s}'", .{&raw_header.name}), | 311 | error.BadName => d.failParse("malformed member name: '{s}'", .{&raw_header.name}), |
| 236 | error.NoLongNames => failParse(opts, "member uses a long name, but there was no longnames member", .{}), | 312 | error.NoLongNames => d.failParse("member uses a long name, but there was no longnames member", .{}), |
| 237 | }; | 313 | }; |
| 238 | | 314 | |
| 239 | return .{ | 315 | return .{ |
| 240 | .name = name, | 316 | .name = name, |
| 241 | .date = raw_header.parseDate() catch |err| | 317 | .date = raw_header.parseDate() catch |err| |
| 242 | failParse(opts, "unable to parse date '{s}' in member '{s}': {t}", .{ raw_header.date, name, err }), | 318 | d.failParse("unable to parse date '{s}' in member '{s}': {t}", .{ raw_header.date, name, err }), |
| 243 | .user_id = raw_header.parseUserId() catch |err| | 319 | .user_id = raw_header.parseUserId() catch |err| |
| 244 | failParse(opts, "unable to parse user_id '{s}' in member '{s}': {t}", .{ raw_header.user_id, name, err }), | 320 | d.failParse("unable to parse user_id '{s}' in member '{s}': {t}", .{ raw_header.user_id, name, err }), |
| 245 | .group_id = raw_header.parseGroupId() catch |err| | 321 | .group_id = raw_header.parseGroupId() catch |err| |
| 246 | failParse(opts, "unable to parse group_id '{s}' in member '{s}': {t}", .{ raw_header.group_id, name, err }), | 322 | d.failParse("unable to parse group_id '{s}' in member '{s}': {t}", .{ raw_header.group_id, name, err }), |
| 247 | .file_mode = raw_header.parseFileMode() catch |err| | 323 | .file_mode = raw_header.parseFileMode() catch |err| |
| 248 | failParse(opts, "unable to parse file_mode '{s}' in member '{s}': {t}", .{ raw_header.file_mode, name, err }), | 324 | d.failParse("unable to parse file_mode '{s}' in member '{s}': {t}", .{ raw_header.file_mode, name, err }), |
| 249 | .size = raw_header.parseSize() catch |err| | 325 | .size = raw_header.parseSize() catch |err| |
| 250 | failParse(opts, "unable to parse size '{s}' in member '{s}': {t}", .{ raw_header.size, name, err }), | 326 | d.failParse("unable to parse size '{s}' in member '{s}': {t}", .{ raw_header.size, name, err }), |
| 251 | }; | 327 | }; |
| 252 | } | 328 | } |
| 253 | }; | 329 | }; |
| 254 | | 330 | |
| 255 | fn dumpArchive(gpa: std.mem.Allocator, opts: *const Options, fr: *Io.File.Reader, w: *Io.Writer) !void { | 331 | fn dumpArchive(d: *const DumpContext) !void { |
| | 332 | const gpa = d.gpa; |
| | 333 | const fr = d.fr; |
| | 334 | const w = d.w; |
| | 335 | |
| 256 | const r = &fr.interface; | 336 | const r = &fr.interface; |
| 257 | r.toss(std.coff.archive_signature.len); | 337 | r.toss(std.coff.archive_signature.len); |
| 258 | | 338 | |
| ... | @@ -272,26 +352,26 @@ const coff = struct { | ... | @@ -272,26 +352,26 @@ const coff = struct { |
| 272 | while (pos < size) : (pos = fr.logicalPos()) { | 352 | while (pos < size) : (pos = fr.logicalPos()) { |
| 273 | if ((pos & 1) != 0) try r.discardAll(1); | 353 | if ((pos & 1) != 0) try r.discardAll(1); |
| 274 | const raw_header = try r.takeStruct(std.coff.ArchiveMemberHeader, .little); | 354 | const raw_header = try r.takeStruct(std.coff.ArchiveMemberHeader, .little); |
| 275 | const header: ArchiveHeader = .fromRaw(opts, &raw_header, opt_longnames); | 355 | const header: ArchiveHeader = .fromRaw(d, &raw_header, opt_longnames); |
| 276 | | 356 | |
| 277 | if (!std.mem.eql(u8, &raw_header.end_of_header, std.coff.archive_end_of_header)) | 357 | if (!std.mem.eql(u8, &raw_header.end_of_header, std.coff.archive_end_of_header)) |
| 278 | return failParse(opts, "malformed end-of-header field in member '{s}': {x}", .{ header.name, raw_header.end_of_header }); | 358 | return d.failParse("malformed end-of-header field in member '{s}': {x}", .{ header.name, raw_header.end_of_header }); |
| 279 | | 359 | |
| 280 | const dump_header = | 360 | const dump_header = |
| 281 | (opts.member_headers and filterMatches(opts.member_filters, header.name)) or | 361 | (d.opts.member_headers and filterMatches(d.opts.member_filters, header.name)) or |
| 282 | (opts.linker_member == opt_expected_kind); | 362 | (d.opts.linker_member == opt_expected_kind); |
| 283 | | 363 | |
| 284 | if (dump_header) | 364 | if (dump_header) |
| 285 | try dumpArchiveHeader(w, &header, @intCast(pos)); | 365 | try dumpArchiveHeader(d, &header, @intCast(pos)); |
| 286 | | 366 | |
| 287 | const member_end = fr.logicalPos() + header.size; | 367 | const member_end = fr.logicalPos() + header.size; |
| 288 | if (member_end > size) | 368 | if (member_end > size) |
| 289 | return failParse(opts, "out-of-bounds length 0x{x} in member '{s}'", .{ header.size, header.name }); | 369 | return d.failParse("out-of-bounds length 0x{x} in member '{s}'", .{ header.size, header.name }); |
| 290 | | 370 | |
| 291 | if (opt_expected_kind) |expected_kind| switch (expected_kind) { | 371 | if (opt_expected_kind) |expected_kind| switch (expected_kind) { |
| 292 | .first_linker => { | 372 | .first_linker => { |
| 293 | if (!std.mem.eql(u8, header.name, "/")) | 373 | if (!std.mem.eql(u8, header.name, "/")) |
| 294 | return failParse(opts, "expected first linker member, found '{s}'", .{header.name}); | 374 | return d.failParse("expected first linker member, found '{s}'", .{header.name}); |
| 295 | | 375 | |
| 296 | const num_symbols = try r.takeInt(u32, .big); | 376 | const num_symbols = try r.takeInt(u32, .big); |
| 297 | if (dump_header) | 377 | if (dump_header) |
| ... | @@ -301,7 +381,7 @@ const coff = struct { | ... | @@ -301,7 +381,7 @@ const coff = struct { |
| 301 | \\ | 381 | \\ |
| 302 | , .{ expected_kind, num_symbols }); | 382 | , .{ expected_kind, num_symbols }); |
| 303 | | 383 | |
| 304 | if (opts.linker_member == .first_linker) { | 384 | if (d.opts.linker_member == .first_linker) { |
| 305 | try w.writeAll( | 385 | try w.writeAll( |
| 306 | \\ | 386 | \\ |
| 307 | \\Archive symbols: | 387 | \\Archive symbols: |
| ... | @@ -314,11 +394,15 @@ const coff = struct { | ... | @@ -314,11 +394,15 @@ const coff = struct { |
| 314 | | 394 | |
| 315 | for (0..num_symbols) |symbol_i| { | 395 | for (0..num_symbols) |symbol_i| { |
| 316 | const symbol = r.takeDelimiter(0) catch |err| | 396 | const symbol = r.takeDelimiter(0) catch |err| |
| 317 | return failParse(opts, "unable to read first linker member string table: {t}", .{err}); | 397 | return d.failParse("unable to read first linker member string table: {t}", .{err}); |
| 318 | try w.print("{x: >8} {s}\n", .{ std.mem.readInt(u32, offsets[symbol_i * 4 ..][0..4], .big), symbol.? }); | 398 | const offset = std.mem.readInt(u32, offsets[symbol_i * 4 ..][0..4], .big); |
| | 399 | try w.print("{f} {s}\n", .{ |
| | 400 | fmtIntField(d, offset, .{ .kind = .va }), |
| | 401 | symbol.?, |
| | 402 | }); |
| 319 | } | 403 | } |
| 320 | } | 404 | } |
| 321 | if (dump_header) try w.writeByte('\n'); | 405 | if (dump_header and d.element(.newlines)) try w.writeByte('\n'); |
| 322 | | 406 | |
| 323 | try fr.seekTo(member_end); | 407 | try fr.seekTo(member_end); |
| 324 | opt_expected_kind = .second_linker; | 408 | opt_expected_kind = .second_linker; |
| ... | @@ -326,12 +410,12 @@ const coff = struct { | ... | @@ -326,12 +410,12 @@ const coff = struct { |
| 326 | }, | 410 | }, |
| 327 | .second_linker => { | 411 | .second_linker => { |
| 328 | if (!std.mem.eql(u8, header.name, "/")) | 412 | if (!std.mem.eql(u8, header.name, "/")) |
| 329 | return failParse(opts, "expected second linker member, found '{s}'", .{header.name}); | 413 | return d.failParse("expected second linker member, found '{s}'", .{header.name}); |
| 330 | | 414 | |
| 331 | const num_members = try r.takeInt(u32, .little); | 415 | const num_members = try r.takeInt(u32, .little); |
| 332 | pos = fr.logicalPos(); | 416 | pos = fr.logicalPos(); |
| 333 | if (pos + num_members * @sizeOf(u32) > member_end) | 417 | if (pos + num_members * @sizeOf(u32) > member_end) |
| 334 | return failParse(opts, "invalid member count 0x{x} in second linker member", .{num_members}); | 418 | return d.failParse("invalid member count 0x{x} in second linker member", .{num_members}); |
| 335 | | 419 | |
| 336 | try members.ensureTotalCapacity(gpa, num_members); | 420 | try members.ensureTotalCapacity(gpa, num_members); |
| 337 | for (0..num_members) |_| | 421 | for (0..num_members) |_| |
| ... | @@ -342,7 +426,7 @@ const coff = struct { | ... | @@ -342,7 +426,7 @@ const coff = struct { |
| 342 | const num_symbols = try r.takeInt(u32, .little); | 426 | const num_symbols = try r.takeInt(u32, .little); |
| 343 | pos = fr.logicalPos(); | 427 | pos = fr.logicalPos(); |
| 344 | if (pos + num_symbols * @sizeOf(u16) > member_end) | 428 | if (pos + num_symbols * @sizeOf(u16) > member_end) |
| 345 | return failParse(opts, "invalid symbol count 0x{x} in second linker member", .{num_symbols}); | 429 | return d.failParse("invalid symbol count 0x{x} in second linker member", .{num_symbols}); |
| 346 | | 430 | |
| 347 | if (dump_header) | 431 | if (dump_header) |
| 348 | try w.print( | 432 | try w.print( |
| ... | @@ -356,13 +440,14 @@ const coff = struct { | ... | @@ -356,13 +440,14 @@ const coff = struct { |
| 356 | for (0..num_symbols) |_| | 440 | for (0..num_symbols) |_| |
| 357 | symbol_member_indices.addOneAssumeCapacity().* = (try r.takeInt(u16, .little)) - 1; | 441 | symbol_member_indices.addOneAssumeCapacity().* = (try r.takeInt(u16, .little)) - 1; |
| 358 | | 442 | |
| 359 | if (opts.linker_member == .second_linker) { | 443 | if (d.opts.linker_member == .second_linker) { |
| 360 | try w.writeAll( | 444 | if (d.element(.@"table-header")) |
| 361 | \\ | 445 | try w.writeAll( |
| 362 | \\Archive Symbols: | 446 | \\ |
| 363 | \\& Member Symbol | 447 | \\Archive Symbols: |
| 364 | \\ | 448 | \\& Member Symbol |
| 365 | ); | 449 | \\ |
| | 450 | ); |
| 366 | | 451 | |
| 367 | pos = fr.logicalPos(); | 452 | pos = fr.logicalPos(); |
| 368 | var symbol_i: u32 = 0; | 453 | var symbol_i: u32 = 0; |
| ... | @@ -373,23 +458,26 @@ const coff = struct { | ... | @@ -373,23 +458,26 @@ const coff = struct { |
| 373 | const symbol_name = if (r.takeDelimiter(0) catch |err| switch (err) { | 458 | const symbol_name = if (r.takeDelimiter(0) catch |err| switch (err) { |
| 374 | error.StreamTooLong => null, | 459 | error.StreamTooLong => null, |
| 375 | else => |e| return e, | 460 | else => |e| return e, |
| 376 | }) |n| n else return failParse(opts, "unterminated string found in second linker member", .{}); | 461 | }) |n| n else return d.failParse("unterminated string found in second linker member", .{}); |
| 377 | | 462 | |
| 378 | try w.print("{x: >8} {s}\n", .{ | 463 | try w.print("{f} {s}\n", .{ |
| 379 | members.items[symbol_member_indices.items[symbol_i]].offset, | 464 | fmtIntField( |
| | 465 | d, |
| | 466 | members.items[symbol_member_indices.items[symbol_i]].offset, |
| | 467 | .{ .kind = .va }, |
| | 468 | ), |
| 380 | symbol_name, | 469 | symbol_name, |
| 381 | }); | 470 | }); |
| 382 | } | 471 | } |
| 383 | | 472 | |
| 384 | if (symbol_i != num_symbols) | 473 | if (symbol_i != num_symbols) |
| 385 | return failParse( | 474 | return d.failParse( |
| 386 | opts, | | |
| 387 | " expected {d} entries in second linker member string table, but found {d}", | 475 | " expected {d} entries in second linker member string table, but found {d}", |
| 388 | .{ num_symbols, symbol_i }, | 476 | .{ num_symbols, symbol_i }, |
| 389 | ); | 477 | ); |
| 390 | } | 478 | } |
| 391 | | 479 | |
| 392 | try w.writeByte('\n'); | 480 | if (d.element(.newlines)) try w.writeByte('\n'); |
| 393 | try fr.seekTo(member_end); | 481 | try fr.seekTo(member_end); |
| 394 | opt_expected_kind = .longnames; | 482 | opt_expected_kind = .longnames; |
| 395 | continue; | 483 | continue; |
| ... | @@ -401,12 +489,13 @@ const coff = struct { | ... | @@ -401,12 +489,13 @@ const coff = struct { |
| 401 | if (dump_header) | 489 | if (dump_header) |
| 402 | try w.print("{t: >16} type\n", .{expected_kind}); | 490 | try w.print("{t: >16} type\n", .{expected_kind}); |
| 403 | | 491 | |
| 404 | if (opts.linker_member == .longnames) { | 492 | if (d.opts.linker_member == .longnames) { |
| 405 | try w.print( | 493 | if (d.element(.@"table-header")) |
| 406 | \\ | 494 | try w.print( |
| 407 | \\Longnames (0x{x} bytes): | 495 | \\ |
| 408 | \\ | 496 | \\Longnames (0x{x} bytes): |
| 409 | , .{opt_longnames.?.len}); | 497 | \\ |
| | 498 | , .{opt_longnames.?.len}); |
| 410 | | 499 | |
| 411 | var lr = Io.Reader.fixed(opt_longnames.?); | 500 | var lr = Io.Reader.fixed(opt_longnames.?); |
| 412 | while (try lr.takeDelimiter(0)) |str| { | 501 | while (try lr.takeDelimiter(0)) |str| { |
| ... | @@ -415,7 +504,7 @@ const coff = struct { | ... | @@ -415,7 +504,7 @@ const coff = struct { |
| 415 | } | 504 | } |
| 416 | } | 505 | } |
| 417 | | 506 | |
| 418 | try w.writeByte('\n'); | 507 | if (d.element(.newlines)) try w.writeByte('\n'); |
| 419 | } | 508 | } |
| 420 | | 509 | |
| 421 | opt_expected_kind = null; | 510 | opt_expected_kind = null; |
| ... | @@ -426,18 +515,18 @@ const coff = struct { | ... | @@ -426,18 +515,18 @@ const coff = struct { |
| 426 | } | 515 | } |
| 427 | | 516 | |
| 428 | if (opt_expected_kind) |expected_kind| switch (expected_kind) { | 517 | if (opt_expected_kind) |expected_kind| switch (expected_kind) { |
| 429 | .first_linker => failParse(opts, "missing first linker member", .{}), | 518 | .first_linker => d.failParse("missing first linker member", .{}), |
| 430 | .second_linker => failParse(opts, "missing second linker member", .{}), | 519 | .second_linker => d.failParse("missing second linker member", .{}), |
| 431 | else => {}, | 520 | else => {}, |
| 432 | }; | 521 | }; |
| 433 | | 522 | |
| 434 | for (members.items, 0..) |member, member_i| { | 523 | for (members.items, 0..) |member, member_i| { |
| 435 | fr.seekTo(member.offset) catch |err| | 524 | fr.seekTo(member.offset) catch |err| |
| 436 | failParse(opts, "unable to read member {d} at offset 0x{x}: {t}", .{ member_i, member.offset, err }); | 525 | d.failParse("unable to read member {d} at offset 0x{x}: {t}", .{ member_i, member.offset, err }); |
| 437 | | 526 | |
| 438 | const raw_header = try r.takeStruct(std.coff.ArchiveMemberHeader, .little); | 527 | const raw_header = try r.takeStruct(std.coff.ArchiveMemberHeader, .little); |
| 439 | const header: ArchiveHeader = .fromRaw(opts, &raw_header, opt_longnames); | 528 | const header: ArchiveHeader = .fromRaw(d, &raw_header, opt_longnames); |
| 440 | if (!filterMatches(opts.member_filters, header.name)) continue; | 529 | if (!filterMatches(d.opts.member_filters, header.name)) continue; |
| 441 | | 530 | |
| 442 | const member_sig = try r.peek(4); | 531 | const member_sig = try r.peek(4); |
| 443 | const machine: std.coff.IMAGE.FILE.MACHINE = | 532 | const machine: std.coff.IMAGE.FILE.MACHINE = |
| ... | @@ -445,17 +534,17 @@ const coff = struct { | ... | @@ -445,17 +534,17 @@ const coff = struct { |
| 445 | const sig = std.mem.readInt(u16, member_sig[2..4], .little); | 534 | const sig = std.mem.readInt(u16, member_sig[2..4], .little); |
| 446 | | 535 | |
| 447 | const is_imp_lib = machine == std.coff.IMAGE.FILE.MACHINE.UNKNOWN and sig == 0xffff; | 536 | const is_imp_lib = machine == std.coff.IMAGE.FILE.MACHINE.UNKNOWN and sig == 0xffff; |
| 448 | if (opts.member_headers or (opts.exports and is_imp_lib)) { | 537 | if (d.opts.member_headers or (d.opts.exports and is_imp_lib)) { |
| 449 | try dumpArchiveHeader(w, &header, member.offset); | 538 | try dumpArchiveHeader(d, &header, member.offset); |
| 450 | if (is_imp_lib) { | 539 | if (is_imp_lib) { |
| 451 | try w.writeAll("\nImport header:\n"); | 540 | try w.writeAll("\nImport header:\n"); |
| 452 | | 541 | |
| 453 | const imp_header = try r.takeStruct(std.coff.ImportHeader, .little); | 542 | const imp_header = try r.takeStruct(std.coff.ImportHeader, .little); |
| 454 | try dumpHeader(w, std.coff.ImportHeader, &imp_header, struct { | 543 | try dumpHeader(d, std.coff.ImportHeader, &imp_header, struct { |
| 455 | pub fn sig1(_: *const std.coff.ImportHeader, _: *Io.Writer) !void {} | 544 | pub fn sig1(_: *const DumpContext, _: *const std.coff.ImportHeader) !void {} |
| 456 | pub fn sig2(_: *const std.coff.ImportHeader, _: *Io.Writer) !void {} | 545 | pub fn sig2(_: *const DumpContext, _: *const std.coff.ImportHeader) !void {} |
| 457 | pub fn types(h: *const std.coff.ImportHeader, cw: *Io.Writer) !void { | 546 | pub fn types(id: *const DumpContext, h: *const std.coff.ImportHeader) !void { |
| 458 | try cw.print( | 547 | try id.w.print( |
| 459 | \\{t: >16} import_type | 548 | \\{t: >16} import_type |
| 460 | \\{t: >16} name_type | 549 | \\{t: >16} name_type |
| 461 | \\ | 550 | \\ |
| ... | @@ -490,51 +579,53 @@ const coff = struct { | ... | @@ -490,51 +579,53 @@ const coff = struct { |
| 490 | } else { | 579 | } else { |
| 491 | try w.writeAll(" COFF object type\n"); | 580 | try w.writeAll(" COFF object type\n"); |
| 492 | } | 581 | } |
| 493 | try w.writeByte('\n'); | 582 | if (d.element(.newlines)) try w.writeByte('\n'); |
| 494 | } | 583 | } |
| 495 | | 584 | |
| 496 | if (is_imp_lib) continue; | 585 | if (is_imp_lib) continue; |
| 497 | if (opts.section_headers or | 586 | if (d.opts.section_headers or |
| 498 | opts.file_headers or | 587 | d.opts.file_headers or |
| 499 | opts.relocs or | 588 | d.opts.relocs or |
| 500 | opts.strings or | 589 | d.opts.strings or |
| 501 | opts.symbols) | 590 | d.opts.symbols) |
| 502 | { | 591 | { |
| 503 | try w.print("{s}({s}): COFF object\n\n", .{ std.fs.path.basename(opts.input_path), header.name }); | 592 | if (d.element(.@"file-type")) |
| 504 | try dumpObject(gpa, opts, false, header.name, fr, w); | 593 | try w.print("{s}({s}): COFF object\n\n", .{ std.fs.path.basename(d.opts.input_path), header.name }); |
| | 594 | try dumpObject(d, false, header.name); |
| 505 | } | 595 | } |
| 506 | } | 596 | } |
| 507 | } | 597 | } |
| 508 | | 598 | |
| 509 | fn dumpObject( | 599 | fn dumpObject( |
| 510 | gpa: std.mem.Allocator, | 600 | d: *const DumpContext, |
| 511 | opts: *const Options, | | |
| 512 | is_image: bool, | 601 | is_image: bool, |
| 513 | obj_name: []const u8, | 602 | obj_name: []const u8, |
| 514 | fr: *Io.File.Reader, | | |
| 515 | w: *Io.Writer, | | |
| 516 | ) !void { | 603 | ) !void { |
| | 604 | const gpa = d.gpa; |
| | 605 | const fr = d.fr; |
| | 606 | const w = d.w; |
| | 607 | |
| 517 | const file_location = fr.logicalPos(); | 608 | const file_location = fr.logicalPos(); |
| 518 | const r = &fr.interface; | 609 | const r = &fr.interface; |
| 519 | const header = r.takeStruct(std.coff.Header, .little) catch |err| | 610 | const header = r.takeStruct(std.coff.Header, .little) catch |err| |
| 520 | return failParse(opts, "unable to read COFF header: {t}", .{err}); | 611 | return d.failParse("unable to read COFF header: {t}", .{err}); |
| 521 | | 612 | |
| 522 | if (opts.file_headers) { | 613 | if (d.opts.file_headers) { |
| 523 | try w.writeAll("COFF Header:\n"); | 614 | if (d.element(.@"header-names")) try w.writeAll("COFF Header:\n"); |
| 524 | try dumpHeader(w, std.coff.Header, &header, struct {}); | 615 | try dumpHeader(d, std.coff.Header, &header, struct {}); |
| 525 | try w.writeByte('\n'); | 616 | if (d.element(.newlines)) try w.writeByte('\n'); |
| 526 | } | 617 | } |
| 527 | | 618 | |
| 528 | switch (header.machine) { | 619 | switch (header.machine) { |
| 529 | _ => return failParse(opts, "unknown machine type: {x}", .{header.machine}), | 620 | _ => return d.failParse("unknown machine type: {x}", .{header.machine}), |
| 530 | else => {}, | 621 | else => {}, |
| 531 | } | 622 | } |
| 532 | | 623 | |
| 533 | var known_dirs: [DIRECTORY_ENTRY.len]std.coff.ImageDataDirectory = undefined; | 624 | var known_dirs: [DIRECTORY_ENTRY.len]std.coff.ImageDataDirectory = undefined; |
| 534 | const needs_data_dirs = | 625 | const needs_data_dirs = |
| 535 | opts.exports or | 626 | d.opts.exports or |
| 536 | opts.imports or | 627 | d.opts.imports or |
| 537 | opts.tls; | 628 | d.opts.tls; |
| 538 | | 629 | |
| 539 | const ImageInfo = struct { | 630 | const ImageInfo = struct { |
| 540 | data_dirs: []const std.coff.ImageDataDirectory, | 631 | data_dirs: []const std.coff.ImageDataDirectory, |
| ... | @@ -543,12 +634,14 @@ const coff = struct { | ... | @@ -543,12 +634,14 @@ const coff = struct { |
| 543 | }; | 634 | }; |
| 544 | | 635 | |
| 545 | const image_info: ?ImageInfo = if (header.size_of_optional_header > 0) image_info: { | 636 | const image_info: ?ImageInfo = if (header.size_of_optional_header > 0) image_info: { |
| 546 | if (!opts.file_headers and !needs_data_dirs) { | 637 | if (!d.opts.file_headers and !needs_data_dirs) { |
| 547 | try fr.seekBy(header.size_of_optional_header); | 638 | try fr.seekBy(header.size_of_optional_header); |
| 548 | break :image_info null; | 639 | break :image_info null; |
| 549 | } | 640 | } |
| 550 | | 641 | |
| 551 | if (opts.file_headers) try w.writeAll("COFF Optional Header:\n"); | 642 | if (d.opts.file_headers and d.element(.@"header-names")) |
| | 643 | try w.writeAll("COFF Optional Header:\n"); |
| | 644 | |
| 552 | const magic: std.coff.OptionalHeader.Magic = @enumFromInt(try r.peekInt(u16, .little)); | 645 | const magic: std.coff.OptionalHeader.Magic = @enumFromInt(try r.peekInt(u16, .little)); |
| 553 | const num_directory_entries, const image_base = switch (magic) { | 646 | const num_directory_entries, const image_base = switch (magic) { |
| 554 | inline .PE32, .@"PE32+" => |v| num_data_dirs: { | 647 | inline .PE32, .@"PE32+" => |v| num_data_dirs: { |
| ... | @@ -558,46 +651,46 @@ const coff = struct { | ... | @@ -558,46 +651,46 @@ const coff = struct { |
| 558 | std.coff.OptionalHeader.@"PE32+"; | 651 | std.coff.OptionalHeader.@"PE32+"; |
| 559 | | 652 | |
| 560 | const optional_header = r.takeStruct(OptionalHeader, .little) catch |err| | 653 | const optional_header = r.takeStruct(OptionalHeader, .little) catch |err| |
| 561 | return failParse(opts, "unable to read optional header: {t}", .{err}); | 654 | return d.failParse("unable to read optional header: {t}", .{err}); |
| 562 | | 655 | |
| 563 | if (opts.file_headers) { | 656 | if (d.opts.file_headers) { |
| 564 | try dumpHeader(w, OptionalHeader, &optional_header, struct { | 657 | try dumpHeader(d, OptionalHeader, &optional_header, struct { |
| 565 | pub fn base_of_code(h: *const std.coff.OptionalHeader, cw: *Io.Writer) !void { | 658 | pub fn base_of_code(id: *const DumpContext, h: *const std.coff.OptionalHeader) !void { |
| 566 | const base = @as(*const OptionalHeader, @ptrCast(@alignCast(h))).image_base; | 659 | const base = @as(*const OptionalHeader, @ptrCast(@alignCast(h))).image_base; |
| 567 | try dumpRvaField(cw, @src().fn_name, h.base_of_code, base); | 660 | try dumpRvaField(id, @src().fn_name, h.base_of_code, base); |
| 568 | } | 661 | } |
| 569 | | 662 | |
| 570 | pub fn address_of_entry_point(h: *const std.coff.OptionalHeader, cw: *Io.Writer) !void { | 663 | pub fn address_of_entry_point(id: *const DumpContext, h: *const std.coff.OptionalHeader) !void { |
| 571 | const base = @as(*const OptionalHeader, @ptrCast(@alignCast(h))).image_base; | 664 | const base = @as(*const OptionalHeader, @ptrCast(@alignCast(h))).image_base; |
| 572 | try dumpRvaField(cw, @src().fn_name, h.base_of_code, base); | 665 | try dumpRvaField(id, @src().fn_name, h.base_of_code, base); |
| 573 | } | 666 | } |
| 574 | | 667 | |
| 575 | pub fn major_linker_version(h: *const std.coff.OptionalHeader, cw: *Io.Writer) !void { | 668 | pub fn major_linker_version(id: *const DumpContext, h: *const std.coff.OptionalHeader) !void { |
| 576 | try dumpVersionField(cw, "linker_version", h.major_linker_version, h.minor_linker_version); | 669 | try dumpVersionField(id.w, "linker_version", h.major_linker_version, h.minor_linker_version); |
| 577 | } | 670 | } |
| 578 | pub fn minor_linker_version(_: *const std.coff.OptionalHeader, _: *Io.Writer) !void {} | 671 | pub fn minor_linker_version(_: *const DumpContext, _: *const std.coff.OptionalHeader) !void {} |
| 579 | | 672 | |
| 580 | pub fn major_operating_system_version(h: *const OptionalHeader, cw: *Io.Writer) !void { | 673 | pub fn major_operating_system_version(id: *const DumpContext, h: *const OptionalHeader) !void { |
| 581 | try dumpVersionField( | 674 | try dumpVersionField( |
| 582 | cw, | 675 | id.w, |
| 583 | "operating_system_version", | 676 | "operating_system_version", |
| 584 | h.major_operating_system_version, | 677 | h.major_operating_system_version, |
| 585 | h.minor_operating_system_version, | 678 | h.minor_operating_system_version, |
| 586 | ); | 679 | ); |
| 587 | } | 680 | } |
| 588 | pub fn minor_operating_system_version(_: *const OptionalHeader, _: *Io.Writer) !void {} | 681 | pub fn minor_operating_system_version(_: *const DumpContext, _: *const OptionalHeader) !void {} |
| 589 | | 682 | |
| 590 | pub fn major_image_version(h: *const OptionalHeader, cw: *Io.Writer) !void { | 683 | pub fn major_image_version(id: *const DumpContext, h: *const OptionalHeader) !void { |
| 591 | try dumpVersionField(cw, "image_version", h.major_image_version, h.minor_image_version); | 684 | try dumpVersionField(id.w, "image_version", h.major_image_version, h.minor_image_version); |
| 592 | } | 685 | } |
| 593 | pub fn minor_image_version(_: *const OptionalHeader, _: *Io.Writer) !void {} | 686 | pub fn minor_image_version(_: *const DumpContext, _: *const OptionalHeader) !void {} |
| 594 | | 687 | |
| 595 | pub fn major_subsystem_version(h: *const OptionalHeader, cw: *Io.Writer) !void { | 688 | pub fn major_subsystem_version(id: *const DumpContext, h: *const OptionalHeader) !void { |
| 596 | try dumpVersionField(cw, "subsystem_version", h.major_subsystem_version, h.minor_subsystem_version); | 689 | try dumpVersionField(id.w, "subsystem_version", h.major_subsystem_version, h.minor_subsystem_version); |
| 597 | } | 690 | } |
| 598 | pub fn minor_subsystem_version(_: *const OptionalHeader, _: *Io.Writer) !void {} | 691 | pub fn minor_subsystem_version(_: *const DumpContext, _: *const OptionalHeader) !void {} |
| 599 | }); | 692 | }); |
| 600 | try w.writeByte('\n'); | 693 | if (d.element(.newlines)) try w.writeByte('\n'); |
| 601 | } | 694 | } |
| 602 | | 695 | |
| 603 | break :num_data_dirs .{ | 696 | break :num_data_dirs .{ |
| ... | @@ -605,24 +698,26 @@ const coff = struct { | ... | @@ -605,24 +698,26 @@ const coff = struct { |
| 605 | optional_header.image_base, | 698 | optional_header.image_base, |
| 606 | }; | 699 | }; |
| 607 | }, | 700 | }, |
| 608 | else => return failParse(opts, "invalid optional header magic number: {x}", .{magic}), | 701 | else => return d.failParse("invalid optional header magic number: {x}", .{magic}), |
| 609 | }; | 702 | }; |
| 610 | | 703 | |
| 611 | if (opts.file_headers) try w.writeAll("Data Directories:\n"); | 704 | if (d.opts.file_headers and d.element(.@"header-names")) |
| | 705 | try w.writeAll("Data Directories:\n"); |
| | 706 | |
| 612 | for (0..num_directory_entries) |dir_i| { | 707 | for (0..num_directory_entries) |dir_i| { |
| 613 | const dir = r.takeStruct(std.coff.ImageDataDirectory, .little) catch |err| | 708 | const dir = r.takeStruct(std.coff.ImageDataDirectory, .little) catch |err| |
| 614 | return failParse(opts, "unable to read data directory {x}: {t}", .{ dir_i, err }); | 709 | return d.failParse("unable to read data directory {x}: {t}", .{ dir_i, err }); |
| 615 | | 710 | |
| 616 | if (dir_i < known_dirs.len) | 711 | if (dir_i < known_dirs.len) |
| 617 | known_dirs[dir_i] = dir; | 712 | known_dirs[dir_i] = dir; |
| 618 | | 713 | |
| 619 | if (opts.file_headers) | 714 | if (d.opts.file_headers) |
| 620 | try w.print( | 715 | try w.print( |
| 621 | "{x: >16} {x: >8} {t}\n", | 716 | "{x: >16} {x: >8} {t}\n", |
| 622 | .{ dir.virtual_address, dir.size, @as(DIRECTORY_ENTRY, @enumFromInt(dir_i)) }, | 717 | .{ dir.virtual_address, dir.size, @as(DIRECTORY_ENTRY, @enumFromInt(dir_i)) }, |
| 623 | ); | 718 | ); |
| 624 | } | 719 | } |
| 625 | if (opts.file_headers) try w.writeByte('\n'); | 720 | if (d.opts.file_headers and d.element(.newlines)) try w.writeByte('\n'); |
| 626 | | 721 | |
| 627 | break :image_info .{ | 722 | break :image_info .{ |
| 628 | .data_dirs = known_dirs[0..@min(known_dirs.len, num_directory_entries)], | 723 | .data_dirs = known_dirs[0..@min(known_dirs.len, num_directory_entries)], |
| ... | @@ -630,32 +725,33 @@ const coff = struct { | ... | @@ -630,32 +725,33 @@ const coff = struct { |
| 630 | .image_base = image_base, | 725 | .image_base = image_base, |
| 631 | }; | 726 | }; |
| 632 | } else if (is_image) { | 727 | } else if (is_image) { |
| 633 | return failParse(opts, "image did not contain an optional header", .{}); | 728 | return d.failParse("image did not contain an optional header", .{}); |
| 634 | } else null; | 729 | } else null; |
| 635 | | 730 | |
| 636 | // Section names in images don't use the string table, as they must fit inline in the header | 731 | // Section names in images don't use the string table, as they must fit inline in the header |
| 637 | const load_string_table = (opts.strings or !is_image) and header.pointer_to_symbol_table > 0; | 732 | const load_string_table = (d.opts.strings or !is_image) and header.pointer_to_symbol_table > 0; |
| 638 | const string_table = if (load_string_table) string_table: { | 733 | const string_table = if (load_string_table) string_table: { |
| 639 | const pos = fr.logicalPos(); | 734 | const pos = fr.logicalPos(); |
| 640 | fr.seekTo(file_location + header.pointer_to_symbol_table + header.number_of_symbols * std.coff.Symbol.sizeOf()) catch |err| | 735 | fr.seekTo(file_location + header.pointer_to_symbol_table + header.number_of_symbols * std.coff.Symbol.sizeOf()) catch |err| |
| 641 | return failParse(opts, "unable to seek to string table: {t}", .{err}); | 736 | return d.failParse("unable to seek to string table: {t}", .{err}); |
| 642 | | 737 | |
| 643 | const string_table_len = r.peekInt(u32, .little) catch |err| | 738 | const string_table_len = r.peekInt(u32, .little) catch |err| |
| 644 | return failParse(opts, "unable to read string table length: {t}", .{err}); | 739 | return d.failParse("unable to read string table length: {t}", .{err}); |
| 645 | | 740 | |
| 646 | const table = r.readAlloc(gpa, string_table_len) catch |err| | 741 | const table = r.readAlloc(gpa, string_table_len) catch |err| |
| 647 | return failParse(opts, "unable to read string table: {t}", .{err}); | 742 | return d.failParse("unable to read string table: {t}", .{err}); |
| 648 | | 743 | |
| 649 | try fr.seekTo(pos); | 744 | try fr.seekTo(pos); |
| 650 | break :string_table table; | 745 | break :string_table table; |
| 651 | } else &.{}; | 746 | } else &.{}; |
| 652 | defer gpa.free(string_table); | 747 | defer gpa.free(string_table); |
| 653 | | 748 | |
| 654 | if (opts.strings) { | 749 | if (d.opts.strings) { |
| 655 | try w.print( | 750 | if (d.element(.@"table-header")) |
| 656 | \\String Table (0x{x} bytes): | 751 | try w.print( |
| 657 | \\ | 752 | \\String Table (0x{x} bytes): |
| 658 | , .{string_table.len}); | 753 | \\ |
| | 754 | , .{string_table.len}); |
| 659 | | 755 | |
| 660 | var sr = Io.Reader.fixed(string_table[4..]); | 756 | var sr = Io.Reader.fixed(string_table[4..]); |
| 661 | while (try sr.takeDelimiter(0)) |str| { | 757 | while (try sr.takeDelimiter(0)) |str| { |
| ... | @@ -663,7 +759,7 @@ const coff = struct { | ... | @@ -663,7 +759,7 @@ const coff = struct { |
| 663 | try w.writeByte('\n'); | 759 | try w.writeByte('\n'); |
| 664 | } | 760 | } |
| 665 | | 761 | |
| 666 | try w.writeByte('\n'); | 762 | if (d.element(.newlines)) try w.writeByte('\n'); |
| 667 | } | 763 | } |
| 668 | | 764 | |
| 669 | var sections: std.ArrayList(Section) = .empty; | 765 | var sections: std.ArrayList(Section) = .empty; |
| ... | @@ -671,13 +767,13 @@ const coff = struct { | ... | @@ -671,13 +767,13 @@ const coff = struct { |
| 671 | var sections_with_data: u16 = 0; | 767 | var sections_with_data: u16 = 0; |
| 672 | | 768 | |
| 673 | const load_sections = | 769 | const load_sections = |
| 674 | opts.section_headers or | 770 | d.opts.section_headers or |
| 675 | opts.symbols or | 771 | d.opts.symbols or |
| 676 | opts.relocs or | 772 | d.opts.relocs or |
| 677 | needs_data_dirs; | 773 | needs_data_dirs; |
| 678 | | 774 | |
| 679 | if (load_sections) { | 775 | if (load_sections) { |
| 680 | if (opts.section_headers) | 776 | if (d.opts.section_headers and d.element(.@"table-header")) |
| 681 | try w.print( | 777 | try w.print( |
| 682 | \\Sections in '{s}': | 778 | \\Sections in '{s}': |
| 683 | \\Num Name RVA Virt Size Data Size & Data & Relocs & Lines # Relocs # Lines Flags | 779 | \\Num Name RVA Virt Size Data Size & Data & Relocs & Lines # Relocs # Lines Flags |
| ... | @@ -687,37 +783,37 @@ const coff = struct { | ... | @@ -687,37 +783,37 @@ const coff = struct { |
| 687 | try sections.resize(gpa, header.number_of_sections); | 783 | try sections.resize(gpa, header.number_of_sections); |
| 688 | for (sections.items, 0..) |*section, section_i| { | 784 | for (sections.items, 0..) |*section, section_i| { |
| 689 | section.header = r.takeStruct(std.coff.SectionHeader, .little) catch |err| | 785 | section.header = r.takeStruct(std.coff.SectionHeader, .little) catch |err| |
| 690 | return failParse(opts, "unable to read section header {x}: {t}", .{ section_i, err }); | 786 | return d.failParse("unable to read section header {x}: {t}", .{ section_i, err }); |
| 691 | section.name = headerName(&section.header.name, string_table) catch |err| switch (err) { | 787 | section.name = headerName(&section.header.name, string_table) catch |err| switch (err) { |
| 692 | error.Overflow, | 788 | error.Overflow, |
| 693 | error.InvalidCharacter, | 789 | error.InvalidCharacter, |
| 694 | => return failParse(opts, "unable to parse section name offset '{s}': {t}", .{ | 790 | => return d.failParse("unable to parse section name offset '{s}': {t}", .{ |
| 695 | section.name, | 791 | section.name, |
| 696 | err, | 792 | err, |
| 697 | }), | 793 | }), |
| 698 | error.OutOfBounds => return failParse(opts, "section name offset '{s}' was out of bounds (>= {x})", .{ | 794 | error.OutOfBounds => return d.failParse("section name offset '{s}' was out of bounds (>= {x})", .{ |
| 699 | section.name, | 795 | section.name, |
| 700 | string_table.len, | 796 | string_table.len, |
| 701 | }), | 797 | }), |
| 702 | }; | 798 | }; |
| 703 | | 799 | |
| 704 | sections_with_data += @intFromBool(section.header.size_of_raw_data > 0); | 800 | sections_with_data += @intFromBool(section.header.size_of_raw_data > 0); |
| 705 | if (opts.section_headers) { | 801 | if (d.opts.section_headers) { |
| 706 | if (!filterMatches(opts.section_filters, section.name)) continue; | 802 | if (!filterMatches(d.opts.section_filters, section.name)) continue; |
| 707 | const raw_name = std.mem.sliceTo(&section.header.name, 0); | 803 | const raw_name = std.mem.sliceTo(&section.header.name, 0); |
| 708 | try w.print( | 804 | try w.print( |
| 709 | "{x: >3} {s: <8} {x: >8} {x: >9} {x: >9} {x: >8} {x: >8} {x: >8} {x: >8} {x: >8} {x:0>8} |", | 805 | "{x: >3} {s: <8} {f} {f} {f} {f} {f} {f} {f} {f} {x:0>8} |", |
| 710 | .{ | 806 | .{ |
| 711 | section_i + 1, | 807 | section_i + 1, |
| 712 | raw_name, | 808 | raw_name, |
| 713 | section.header.virtual_address, | 809 | fmtIntField(d, section.header.virtual_address, .{ .kind = .va }), |
| 714 | section.header.virtual_size, | 810 | fmtIntField(d, section.header.virtual_size, .{ .kind = .size, .width = 9 }), |
| 715 | section.header.size_of_raw_data, | 811 | fmtIntField(d, section.header.size_of_raw_data, .{ .kind = .size, .width = 9 }), |
| 716 | section.header.pointer_to_raw_data, | 812 | fmtIntField(d, section.header.pointer_to_raw_data, .{ .kind = .va }), |
| 717 | section.header.pointer_to_relocations, | 813 | fmtIntField(d, section.header.pointer_to_relocations, .{ .kind = .va }), |
| 718 | section.header.pointer_to_linenumbers, | 814 | fmtIntField(d, section.header.pointer_to_linenumbers, .{ .kind = .va }), |
| 719 | section.header.number_of_relocations, | 815 | fmtIntField(d, section.header.number_of_relocations, .{ .kind = .va }), |
| 720 | section.header.number_of_linenumbers, | 816 | fmtIntField(d, section.header.number_of_linenumbers, .{ .kind = .va }), |
| 721 | @as(u32, @bitCast(section.header.flags)), | 817 | @as(u32, @bitCast(section.header.flags)), |
| 722 | }, | 818 | }, |
| 723 | ); | 819 | ); |
| ... | @@ -730,7 +826,7 @@ const coff = struct { | ... | @@ -730,7 +826,7 @@ const coff = struct { |
| 730 | } | 826 | } |
| 731 | } | 827 | } |
| 732 | | 828 | |
| 733 | if (opts.section_headers) try w.writeByte('\n'); | 829 | if (d.opts.section_headers and d.element(.newlines)) try w.writeByte('\n'); |
| 734 | } | 830 | } |
| 735 | | 831 | |
| 736 | var symbols: std.ArrayList(struct { | 832 | var symbols: std.ArrayList(struct { |
| ... | @@ -738,15 +834,15 @@ const coff = struct { | ... | @@ -738,15 +834,15 @@ const coff = struct { |
| 738 | section_number: std.coff.SectionNumber, | 834 | section_number: std.coff.SectionNumber, |
| 739 | }) = .empty; | 835 | }) = .empty; |
| 740 | defer symbols.deinit(gpa); | 836 | defer symbols.deinit(gpa); |
| 741 | if (opts.relocs) | 837 | if (d.opts.relocs) |
| 742 | try symbols.ensureUnusedCapacity(gpa, header.number_of_symbols); | 838 | try symbols.ensureUnusedCapacity(gpa, header.number_of_symbols); |
| 743 | | 839 | |
| 744 | if (opts.symbols or opts.relocs) { | 840 | if (d.opts.symbols or d.opts.relocs) { |
| 745 | if (header.pointer_to_symbol_table > 0) { | 841 | if (header.pointer_to_symbol_table > 0) { |
| 746 | fr.seekTo(file_location + header.pointer_to_symbol_table) catch |err| | 842 | fr.seekTo(file_location + header.pointer_to_symbol_table) catch |err| |
| 747 | return failParse(opts, "unable to seek to symbol table: {t}", .{err}); | 843 | return d.failParse("unable to seek to symbol table: {t}", .{err}); |
| 748 | | 844 | |
| 749 | if (opts.symbols) | 845 | if (d.opts.symbols and d.element(.@"table-header")) |
| 750 | try w.print( | 846 | try w.print( |
| 751 | \\Symbols in '{s}': | 847 | \\Symbols in '{s}': |
| 752 | \\ Ord Value Sect Type Storage Name | 848 | \\ Ord Value Sect Type Storage Name |
| ... | @@ -758,7 +854,7 @@ const coff = struct { | ... | @@ -758,7 +854,7 @@ const coff = struct { |
| 758 | while (symbol_i < header.number_of_symbols) { | 854 | while (symbol_i < header.number_of_symbols) { |
| 759 | var symbol: std.coff.Symbol = undefined; | 855 | var symbol: std.coff.Symbol = undefined; |
| 760 | const symbol_bytes = r.take(symbol_size) catch |err| | 856 | const symbol_bytes = r.take(symbol_size) catch |err| |
| 761 | return failParse(opts, "unable to read symbol {x}: {t}", .{ symbol_i, err }); | 857 | return d.failParse("unable to read symbol {x}: {t}", .{ symbol_i, err }); |
| 762 | | 858 | |
| 763 | @memcpy(std.mem.asBytes(&symbol)[0..symbol_size], symbol_bytes); | 859 | @memcpy(std.mem.asBytes(&symbol)[0..symbol_size], symbol_bytes); |
| 764 | if (native_endian != .little) | 860 | if (native_endian != .little) |
| ... | @@ -773,7 +869,7 @@ const coff = struct { | ... | @@ -773,7 +869,7 @@ const coff = struct { |
| 773 | const name = std.mem.sliceTo(if (std.mem.eql(u8, symbol.name[0..4], "\x00\x00\x00\x00")) name: { | 869 | const name = std.mem.sliceTo(if (std.mem.eql(u8, symbol.name[0..4], "\x00\x00\x00\x00")) name: { |
| 774 | const index = std.mem.readInt(u32, symbol.name[4..], .little); | 870 | const index = std.mem.readInt(u32, symbol.name[4..], .little); |
| 775 | if (index >= string_table.len) | 871 | if (index >= string_table.len) |
| 776 | return failParse(opts, "invalid name offset for symbol {x} ({x} >= {x})", .{ | 872 | return d.failParse("invalid name offset for symbol {x} ({x} >= {x})", .{ |
| 777 | symbol_i, | 873 | symbol_i, |
| 778 | index, | 874 | index, |
| 779 | string_table.len, | 875 | string_table.len, |
| ... | @@ -781,16 +877,19 @@ const coff = struct { | ... | @@ -781,16 +877,19 @@ const coff = struct { |
| 781 | break :name string_table[index..]; | 877 | break :name string_table[index..]; |
| 782 | } else &symbol.name, 0); | 878 | } else &symbol.name, 0); |
| 783 | | 879 | |
| 784 | if (opts.relocs) | 880 | if (d.opts.relocs) |
| 785 | symbols.appendNTimesAssumeCapacity(.{ | 881 | symbols.appendNTimesAssumeCapacity(.{ |
| 786 | .name = name, | 882 | .name = name, |
| 787 | .section_number = symbol.section_number, | 883 | .section_number = symbol.section_number, |
| 788 | }, 1 + symbol.number_of_aux_symbols); | 884 | }, 1 + symbol.number_of_aux_symbols); |
| 789 | | 885 | |
| 790 | if (!opts.symbols) | 886 | if (!d.opts.symbols or !filterMatches(d.opts.symbol_filters, name)) |
| 791 | continue; | 887 | continue; |
| 792 | | 888 | |
| 793 | try w.print("{x: >4} {x:0>8} ", .{ symbol_i, symbol.value }); | 889 | try w.print("{f} {x:0>8} ", .{ |
| | 890 | fmtIntField(d, @as(u16, @intCast(symbol_i)), .{ .kind = .ord }), |
| | 891 | symbol.value, |
| | 892 | }); |
| 794 | try switch (symbol.section_number) { | 893 | try switch (symbol.section_number) { |
| 795 | .UNDEFINED => w.writeAll("UNDEF"), | 894 | .UNDEFINED => w.writeAll("UNDEF"), |
| 796 | .ABSOLUTE => w.writeAll(" ABS"), | 895 | .ABSOLUTE => w.writeAll(" ABS"), |
| ... | @@ -814,8 +913,7 @@ const coff = struct { | ... | @@ -814,8 +913,7 @@ const coff = struct { |
| 814 | else => null, | 913 | else => null, |
| 815 | }) |suffix| try w.writeAll(suffix) else try w.print("{x}", .{symbol.type.complex_type}); | 914 | }) |suffix| try w.writeAll(suffix) else try w.print("{x}", .{symbol.type.complex_type}); |
| 816 | | 915 | |
| 817 | try w.print("{t: >16} | {s}", .{ symbol.storage_class, name }); | 916 | try w.print("{t: >16} | {s}\n", .{ symbol.storage_class, name }); |
| 818 | try w.writeByte('\n'); | | |
| 819 | | 917 | |
| 820 | for (0..symbol.number_of_aux_symbols) |aux_i| { | 918 | for (0..symbol.number_of_aux_symbols) |aux_i| { |
| 821 | _ = aux_i; | 919 | _ = aux_i; |
| ... | @@ -838,8 +936,7 @@ const coff = struct { | ... | @@ -838,8 +936,7 @@ const coff = struct { |
| 838 | try w.writeAll("TODO bf / ef aux symbol"); | 936 | try w.writeAll("TODO bf / ef aux symbol"); |
| 839 | } else if (symbol.storage_class == .WEAK_EXTERNAL and symbol.section_number == .UNDEFINED) { | 937 | } else if (symbol.storage_class == .WEAK_EXTERNAL and symbol.section_number == .UNDEFINED) { |
| 840 | if (symbol.value != 0) | 938 | if (symbol.value != 0) |
| 841 | return failParse( | 939 | return d.failParse( |
| 842 | opts, | | |
| 843 | "invalid value 0x{x} for weak external symbol 0x{x}", | 940 | "invalid value 0x{x} for weak external symbol 0x{x}", |
| 844 | .{ symbol.value, symbol_i }, | 941 | .{ symbol.value, symbol_i }, |
| 845 | ); | 942 | ); |
| ... | @@ -850,14 +947,11 @@ const coff = struct { | ... | @@ -850,14 +947,11 @@ const coff = struct { |
| 850 | std.mem.byteSwapAllFields(std.coff.SectionDefinition, &weak_external); | 947 | std.mem.byteSwapAllFields(std.coff.SectionDefinition, &weak_external); |
| 851 | | 948 | |
| 852 | if (weak_external.tag_index >= header.number_of_symbols) | 949 | if (weak_external.tag_index >= header.number_of_symbols) |
| 853 | return failParse( | 950 | return d.failParse( |
| 854 | opts, | | |
| 855 | "invalid tag_index 0x{x} for weak external symbol 0x{x}", | 951 | "invalid tag_index 0x{x} for weak external symbol 0x{x}", |
| 856 | .{ weak_external.tag_index, symbol_i }, | 952 | .{ weak_external.tag_index, symbol_i }, |
| 857 | ); | 953 | ); |
| 858 | | 954 | |
| 859 | // TODO | | |
| 860 | | | |
| 861 | try w.print(" Weak External [falls back to {x:0>8} via {t}]", .{ | 955 | try w.print(" Weak External [falls back to {x:0>8} via {t}]", .{ |
| 862 | weak_external.tag_index, | 956 | weak_external.tag_index, |
| 863 | weak_external.flag, | 957 | weak_external.flag, |
| ... | @@ -914,8 +1008,8 @@ const coff = struct { | ... | @@ -914,8 +1008,8 @@ const coff = struct { |
| 914 | continue; | 1008 | continue; |
| 915 | } | 1009 | } |
| 916 | | 1010 | |
| 917 | try w.print(" [size {x:0>8} chksum {x:0>8} relocs {x:0>4} lines {x:0>4}]", .{ | 1011 | try w.print(" [size {f} chksum {x:0>8} relocs {x:0>4} lines {x:0>4}]", .{ |
| 918 | section_def.length, | 1012 | fmtIntField(d, section_def.length, .{ .kind = .size, .zero_fill = true }), |
| 919 | section_def.checksum, | 1013 | section_def.checksum, |
| 920 | section_def.number_of_relocations, | 1014 | section_def.number_of_relocations, |
| 921 | section_def.number_of_linenumbers, | 1015 | section_def.number_of_linenumbers, |
| ... | @@ -930,19 +1024,19 @@ const coff = struct { | ... | @@ -930,19 +1024,19 @@ const coff = struct { |
| 930 | try w.writeAll(")"); | 1024 | try w.writeAll(")"); |
| 931 | }, | 1025 | }, |
| 932 | } | 1026 | } |
| 933 | } else {} | 1027 | } |
| 934 | | 1028 | |
| 935 | try w.writeByte('\n'); | 1029 | try w.writeByte('\n'); |
| 936 | } | 1030 | } |
| 937 | } | 1031 | } |
| 938 | | 1032 | |
| 939 | if (opts.symbols) try w.writeByte('\n'); | 1033 | if (d.opts.symbols and d.element(.newlines)) try w.writeByte('\n'); |
| 940 | } else if (opts.symbols) { | 1034 | } else if (d.opts.symbols) { |
| 941 | try w.writeAll("No symbol table found\n"); | 1035 | try w.writeAll("No symbol table found\n"); |
| 942 | } | 1036 | } |
| 943 | } | 1037 | } |
| 944 | | 1038 | |
| 945 | if (opts.relocs) { | 1039 | if (d.opts.relocs) { |
| 946 | const relocation_size = std.coff.Relocation.sizeOf(); | 1040 | const relocation_size = std.coff.Relocation.sizeOf(); |
| 947 | | 1041 | |
| 948 | for (sections.items, 0..) |section, section_i| { | 1042 | for (sections.items, 0..) |section, section_i| { |
| ... | @@ -955,7 +1049,7 @@ const coff = struct { | ... | @@ -955,7 +1049,7 @@ const coff = struct { |
| 955 | , .{ section_i + 1, section.name, obj_name }); | 1049 | , .{ section_i + 1, section.name, obj_name }); |
| 956 | | 1050 | |
| 957 | fr.seekTo(file_location + section.header.pointer_to_relocations) catch |err| | 1051 | fr.seekTo(file_location + section.header.pointer_to_relocations) catch |err| |
| 958 | return failParse(opts, "unable to seek to section {x} relocation table: {t}", .{ section_i + 1, err }); | 1052 | return d.failParse("unable to seek to section {x} relocation table: {t}", .{ section_i + 1, err }); |
| 959 | | 1053 | |
| 960 | for (0..section.header.number_of_relocations) |reloc_i| { | 1054 | for (0..section.header.number_of_relocations) |reloc_i| { |
| 961 | var reloc: std.coff.Relocation = undefined; | 1055 | var reloc: std.coff.Relocation = undefined; |
| ... | @@ -963,7 +1057,13 @@ const coff = struct { | ... | @@ -963,7 +1057,13 @@ const coff = struct { |
| 963 | if (native_endian != .little) | 1057 | if (native_endian != .little) |
| 964 | std.mem.byteSwapAllFields(std.coff.Relocation, &reloc); | 1058 | std.mem.byteSwapAllFields(std.coff.Relocation, &reloc); |
| 965 | | 1059 | |
| 966 | try w.print("{x:0>8} ", .{reloc.virtual_address}); | 1060 | const sym = &symbols.items[reloc.symbol_table_index]; |
| | 1061 | if (!filterMatches(d.opts.symbol_filters, sym.name)) |
| | 1062 | continue; |
| | 1063 | |
| | 1064 | try w.print("{f} ", .{ |
| | 1065 | fmtIntField(d, reloc.virtual_address, .{ .kind = .va, .zero_fill = true }), |
| | 1066 | }); |
| 967 | switch (header.machine) { | 1067 | switch (header.machine) { |
| 968 | _ => unreachable, | 1068 | _ => unreachable, |
| 969 | inline else => |m| switch (m.RelocationType()) { | 1069 | inline else => |m| switch (m.RelocationType()) { |
| ... | @@ -976,20 +1076,18 @@ const coff = struct { | ... | @@ -976,20 +1076,18 @@ const coff = struct { |
| 976 | } | 1076 | } |
| 977 | | 1077 | |
| 978 | if (reloc.symbol_table_index >= symbols.items.len) | 1078 | if (reloc.symbol_table_index >= symbols.items.len) |
| 979 | return failParse( | 1079 | return d.failParse( |
| 980 | opts, | | |
| 981 | "reloc {x} in section {x} has out-of-bounds symbol index {x}", | 1080 | "reloc {x} in section {x} has out-of-bounds symbol index {x}", |
| 982 | .{ reloc_i, section_i + 1, reloc.symbol_table_index }, | 1081 | .{ reloc_i, section_i + 1, reloc.symbol_table_index }, |
| 983 | ); | 1082 | ); |
| 984 | | 1083 | |
| 985 | const sym = &symbols.items[reloc.symbol_table_index]; | 1084 | try w.print("{f} {f} | {s}\n", .{ |
| 986 | try w.print("{x: >8} {f} | {s}\n", .{ | 1085 | fmtIntField(d, reloc.symbol_table_index, .{ .kind = .ord }), |
| 987 | reloc.symbol_table_index, | | |
| 988 | fmtSectionNumber(sym.section_number), | 1086 | fmtSectionNumber(sym.section_number), |
| 989 | sym.name, | 1087 | sym.name, |
| 990 | }); | 1088 | }); |
| 991 | } | 1089 | } |
| 992 | try w.writeByte('\n'); | 1090 | if (d.element(.newlines)) try w.writeByte('\n'); |
| 993 | } | 1091 | } |
| 994 | } | 1092 | } |
| 995 | | 1093 | |
| ... | @@ -1026,44 +1124,40 @@ const coff = struct { | ... | @@ -1026,44 +1124,40 @@ const coff = struct { |
| 1026 | } else &.{}; | 1124 | } else &.{}; |
| 1027 | defer gpa.free(rva_index); | 1125 | defer gpa.free(rva_index); |
| 1028 | | 1126 | |
| 1029 | if (opts.exports) { | 1127 | if (d.opts.exports) { |
| 1030 | if (try seekToDataDirectory(opts, fr, w, rva_index, sections.items, image_info.?.data_dirs, .EXPORT)) |section_index| { | 1128 | if (try seekToDataDirectory(d, rva_index, sections.items, image_info.?.data_dirs, .EXPORT)) |section_index| { |
| 1031 | const export_dir = r.takeStruct(std.coff.ExportDirectoryTable, .little) catch |err| | 1129 | const export_dir = r.takeStruct(std.coff.ExportDirectoryTable, .little) catch |err| |
| 1032 | return failParse(opts, "unable to read export directory: {t}", .{err}); | 1130 | return d.failParse("unable to read export directory: {t}", .{err}); |
| 1033 | | 1131 | |
| 1034 | try w.print("Export directory:\n", .{}); | 1132 | try w.print("Export directory:\n", .{}); |
| 1035 | try dumpHeader(w, std.coff.ExportDirectoryTable, &export_dir, struct { | 1133 | try dumpHeader(d, std.coff.ExportDirectoryTable, &export_dir, struct { |
| 1036 | pub fn major_version(h: *const std.coff.ExportDirectoryTable, cw: *Io.Writer) !void { | 1134 | pub fn major_version(id: *const DumpContext, h: *const std.coff.ExportDirectoryTable) !void { |
| 1037 | try dumpVersionField(cw, "version", h.major_version, h.minor_version); | 1135 | try dumpVersionField(id.w, "version", h.major_version, h.minor_version); |
| 1038 | } | 1136 | } |
| 1039 | pub fn minor_version(_: *const std.coff.ExportDirectoryTable, _: *Io.Writer) !void {} | 1137 | pub fn minor_version(_: *const DumpContext, _: *const std.coff.ExportDirectoryTable) !void {} |
| 1040 | }); | 1138 | }); |
| 1041 | | 1139 | |
| 1042 | const section = sections.items[section_index]; | 1140 | const section = sections.items[section_index]; |
| 1043 | const name_loc = section.rvaFileOffset(export_dir.name_rva) catch | 1141 | const name_loc = section.rvaFileOffset(export_dir.name_rva) catch |
| 1044 | return failParse( | 1142 | return d.failParse( |
| 1045 | opts, | | |
| 1046 | "export name rva 0x{x} was not within the export section", | 1143 | "export name rva 0x{x} was not within the export section", |
| 1047 | .{export_dir.name_rva}, | 1144 | .{export_dir.name_rva}, |
| 1048 | ); | 1145 | ); |
| 1049 | | 1146 | |
| 1050 | const eat_loc = section.rvaFileOffset(export_dir.export_address_table_rva) catch | 1147 | const eat_loc = section.rvaFileOffset(export_dir.export_address_table_rva) catch |
| 1051 | return failParse( | 1148 | return d.failParse( |
| 1052 | opts, | | |
| 1053 | "export address table rva 0x{x} was not within the export section", | 1149 | "export address table rva 0x{x} was not within the export section", |
| 1054 | .{export_dir.export_address_table_rva}, | 1150 | .{export_dir.export_address_table_rva}, |
| 1055 | ); | 1151 | ); |
| 1056 | | 1152 | |
| 1057 | const name_pointer_loc = section.rvaFileOffset(export_dir.name_pointer_table_rva) catch | 1153 | const name_pointer_loc = section.rvaFileOffset(export_dir.name_pointer_table_rva) catch |
| 1058 | return failParse( | 1154 | return d.failParse( |
| 1059 | opts, | | |
| 1060 | "export name pointer table rva 0x{x} was not within the export section", | 1155 | "export name pointer table rva 0x{x} was not within the export section", |
| 1061 | .{export_dir.name_pointer_table_rva}, | 1156 | .{export_dir.name_pointer_table_rva}, |
| 1062 | ); | 1157 | ); |
| 1063 | | 1158 | |
| 1064 | const ord_loc = section.rvaFileOffset(export_dir.ordinal_table_rva) catch | 1159 | const ord_loc = section.rvaFileOffset(export_dir.ordinal_table_rva) catch |
| 1065 | return failParse( | 1160 | return d.failParse( |
| 1066 | opts, | | |
| 1067 | "export ordinal table rva 0x{x} was not within the export section", | 1161 | "export ordinal table rva 0x{x} was not within the export section", |
| 1068 | .{export_dir.ordinal_table_rva}, | 1162 | .{export_dir.ordinal_table_rva}, |
| 1069 | ); | 1163 | ); |
| ... | @@ -1077,12 +1171,13 @@ const coff = struct { | ... | @@ -1077,12 +1171,13 @@ const coff = struct { |
| 1077 | defer gpa.free(dir_slice); | 1171 | defer gpa.free(dir_slice); |
| 1078 | | 1172 | |
| 1079 | const dll_name = std.mem.sliceTo(dir_slice[name_loc - dir_loc ..], 0); | 1173 | const dll_name = std.mem.sliceTo(dir_slice[name_loc - dir_loc ..], 0); |
| 1080 | try w.print( | 1174 | if (d.element(.@"table-header")) |
| 1081 | \\ | 1175 | try w.print( |
| 1082 | \\Exports from {s}: | 1176 | \\ |
| 1083 | \\ Ord Hint RVA Name | 1177 | \\Exports from {s}: |
| 1084 | \\ | 1178 | \\ Ord Hint RVA Name |
| 1085 | , .{dll_name}); | 1179 | \\ |
| | 1180 | , .{dll_name}); |
| 1086 | | 1181 | |
| 1087 | const name_pointers = dir_slice[name_pointer_loc - dir_loc ..][0 .. export_dir.number_of_names * @sizeOf(u32)]; | 1182 | const name_pointers = dir_slice[name_pointer_loc - dir_loc ..][0 .. export_dir.number_of_names * @sizeOf(u32)]; |
| 1088 | const ords = dir_slice[ord_loc - dir_loc ..][0 .. export_dir.number_of_names * @sizeOf(u16)]; | 1183 | const ords = dir_slice[ord_loc - dir_loc ..][0 .. export_dir.number_of_names * @sizeOf(u16)]; |
| ... | @@ -1090,18 +1185,24 @@ const coff = struct { | ... | @@ -1090,18 +1185,24 @@ const coff = struct { |
| 1090 | const name_rva_to_offset = dir.virtual_address + @sizeOf(std.coff.ExportDirectoryTable); | 1185 | const name_rva_to_offset = dir.virtual_address + @sizeOf(std.coff.ExportDirectoryTable); |
| 1091 | for (0..export_dir.number_of_names) |name_i| { | 1186 | for (0..export_dir.number_of_names) |name_i| { |
| 1092 | const name_rva = std.mem.readInt(u32, name_pointers[name_i * @sizeOf(u32) ..][0..@sizeOf(u32)], .little); | 1187 | const name_rva = std.mem.readInt(u32, name_pointers[name_i * @sizeOf(u32) ..][0..@sizeOf(u32)], .little); |
| | 1188 | const name = std.mem.sliceTo(dir_slice[name_rva - name_rva_to_offset ..], 0); |
| | 1189 | if (!filterMatches(d.opts.symbol_filters, name)) |
| | 1190 | continue; |
| | 1191 | |
| 1093 | const ord = std.mem.readInt(u16, ords[name_i * @sizeOf(u16) ..][0..@sizeOf(u16)], .little); | 1192 | const ord = std.mem.readInt(u16, ords[name_i * @sizeOf(u16) ..][0..@sizeOf(u16)], .little); |
| 1094 | const addr = std.mem.readInt(u32, addrs[@as(u32, ord) * @sizeOf(u32) ..][0..@sizeOf(u32)], .little); | 1193 | const addr = std.mem.readInt(u32, addrs[@as(u32, ord) * @sizeOf(u32) ..][0..@sizeOf(u32)], .little); |
| 1095 | | 1194 | |
| 1096 | try w.print("{x: >4} {x: >4} ", .{ export_dir.ordinal_base + ord, name_i }); | 1195 | try w.print("{f} {f} ", .{ |
| | 1196 | fmtIntField(d, @as(u16, @intCast(export_dir.ordinal_base + ord)), .{ .kind = .ord }), |
| | 1197 | fmtIntField(d, @as(u16, @intCast(name_i)), .{ .kind = .ord }), |
| | 1198 | }); |
| 1097 | const is_forwarder = addr >= dir.virtual_address and addr < dir_end_rva; | 1199 | const is_forwarder = addr >= dir.virtual_address and addr < dir_end_rva; |
| 1098 | if (is_forwarder) { | 1200 | if (is_forwarder) { |
| 1099 | try w.writeAll("forwards"); | 1201 | try w.writeAll("forwards"); |
| 1100 | } else { | 1202 | } else { |
| 1101 | try w.print("{x: >8}", .{addr}); | 1203 | try w.print("{f}", .{fmtIntField(d, addr, .{ .kind = .rva })}); |
| 1102 | } | 1204 | } |
| 1103 | | 1205 | |
| 1104 | const name = std.mem.sliceTo(dir_slice[name_rva - name_rva_to_offset ..], 0); | | |
| 1105 | try w.print(" | {s}", .{name}); | 1206 | try w.print(" | {s}", .{name}); |
| 1106 | if (is_forwarder) | 1207 | if (is_forwarder) |
| 1107 | try w.print(" -> {s}", .{std.mem.sliceTo(dir_slice[addr - name_rva_to_offset ..], 0)}); | 1208 | try w.print(" -> {s}", .{std.mem.sliceTo(dir_slice[addr - name_rva_to_offset ..], 0)}); |
| ... | @@ -1110,11 +1211,9 @@ const coff = struct { | ... | @@ -1110,11 +1211,9 @@ const coff = struct { |
| 1110 | } | 1211 | } |
| 1111 | } | 1212 | } |
| 1112 | | 1213 | |
| 1113 | if (opts.imports) { | 1214 | if (d.opts.imports) { |
| 1114 | if (try seekToDataDirectory( | 1215 | if (try seekToDataDirectory( |
| 1115 | opts, | 1216 | d, |
| 1116 | fr, | | |
| 1117 | w, | | |
| 1118 | rva_index, | 1217 | rva_index, |
| 1119 | sections.items, | 1218 | sections.items, |
| 1120 | image_info.?.data_dirs, | 1219 | image_info.?.data_dirs, |
| ... | @@ -1125,8 +1224,7 @@ const coff = struct { | ... | @@ -1125,8 +1224,7 @@ const coff = struct { |
| 1125 | defer directory_entries.deinit(gpa); | 1224 | defer directory_entries.deinit(gpa); |
| 1126 | while (true) { | 1225 | while (true) { |
| 1127 | const entry = r.takeStruct(Entry, .little) catch |err| | 1226 | const entry = r.takeStruct(Entry, .little) catch |err| |
| 1128 | return failParse( | 1227 | return d.failParse( |
| 1129 | opts, | | |
| 1130 | "unable to read import directory entry {x}: {t}", | 1228 | "unable to read import directory entry {x}: {t}", |
| 1131 | .{ directory_entries.items.len, err }, | 1229 | .{ directory_entries.items.len, err }, |
| 1132 | ); | 1230 | ); |
| ... | @@ -1141,8 +1239,7 @@ const coff = struct { | ... | @@ -1141,8 +1239,7 @@ const coff = struct { |
| 1141 | sections.items, | 1239 | sections.items, |
| 1142 | entry.name_rva, | 1240 | entry.name_rva, |
| 1143 | ) orelse | 1241 | ) orelse |
| 1144 | return failParse( | 1242 | return d.failParse( |
| 1145 | opts, | | |
| 1146 | "import directory entry name rva 0x{x} was not found in any section", | 1243 | "import directory entry name rva 0x{x} was not found in any section", |
| 1147 | .{entry.name_rva}, | 1244 | .{entry.name_rva}, |
| 1148 | ); | 1245 | ); |
| ... | @@ -1151,8 +1248,7 @@ const coff = struct { | ... | @@ -1151,8 +1248,7 @@ const coff = struct { |
| 1151 | entry.name_rva, | 1248 | entry.name_rva, |
| 1152 | ) catch unreachable; | 1249 | ) catch unreachable; |
| 1153 | fr.seekTo(name_loc) catch |err| | 1250 | fr.seekTo(name_loc) catch |err| |
| 1154 | return failParse( | 1251 | return d.failParse( |
| 1155 | opts, | | |
| 1156 | "unable to seek to import directory entry name at 0x{x}: {t}", | 1252 | "unable to seek to import directory entry name at 0x{x}: {t}", |
| 1157 | .{ name_loc, err }, | 1253 | .{ name_loc, err }, |
| 1158 | ); | 1254 | ); |
| ... | @@ -1160,7 +1256,7 @@ const coff = struct { | ... | @@ -1160,7 +1256,7 @@ const coff = struct { |
| 1160 | const dll_name = (try r.takeDelimiter(0)).?; | 1256 | const dll_name = (try r.takeDelimiter(0)).?; |
| 1161 | | 1257 | |
| 1162 | try w.print("Import table entry for {s}:\n", .{dll_name}); | 1258 | try w.print("Import table entry for {s}:\n", .{dll_name}); |
| 1163 | try dumpHeader(w, Entry, &entry, struct {}); | 1259 | try dumpHeader(d, Entry, &entry, struct {}); |
| 1164 | | 1260 | |
| 1165 | try w.print( | 1261 | try w.print( |
| 1166 | \\ | 1262 | \\ |
| ... | @@ -1173,8 +1269,7 @@ const coff = struct { | ... | @@ -1173,8 +1269,7 @@ const coff = struct { |
| 1173 | sections.items, | 1269 | sections.items, |
| 1174 | entry.import_lookup_table_rva, | 1270 | entry.import_lookup_table_rva, |
| 1175 | ) orelse | 1271 | ) orelse |
| 1176 | return failParse( | 1272 | return d.failParse( |
| 1177 | opts, | | |
| 1178 | "import directory entry ilt rva 0x{x} was not found in any section", | 1273 | "import directory entry ilt rva 0x{x} was not found in any section", |
| 1179 | .{entry.import_lookup_table_rva}, | 1274 | .{entry.import_lookup_table_rva}, |
| 1180 | ); | 1275 | ); |
| ... | @@ -1183,8 +1278,7 @@ const coff = struct { | ... | @@ -1183,8 +1278,7 @@ const coff = struct { |
| 1183 | entry.import_lookup_table_rva, | 1278 | entry.import_lookup_table_rva, |
| 1184 | ) catch unreachable; | 1279 | ) catch unreachable; |
| 1185 | fr.seekTo(ilt_loc) catch |err| | 1280 | fr.seekTo(ilt_loc) catch |err| |
| 1186 | return failParse( | 1281 | return d.failParse( |
| 1187 | opts, | | |
| 1188 | "unable to seek to import directory ilt at 0x{x}: {t}", | 1282 | "unable to seek to import directory ilt at 0x{x}: {t}", |
| 1189 | .{ ilt_loc, err }, | 1283 | .{ ilt_loc, err }, |
| 1190 | ); | 1284 | ); |
| ... | @@ -1199,8 +1293,7 @@ const coff = struct { | ... | @@ -1199,8 +1293,7 @@ const coff = struct { |
| 1199 | defer ilt_entries.deinit(gpa); | 1293 | defer ilt_entries.deinit(gpa); |
| 1200 | while (true) { | 1294 | while (true) { |
| 1201 | const table_entry = r.takeStruct(TableEntry, .little) catch |err| | 1295 | const table_entry = r.takeStruct(TableEntry, .little) catch |err| |
| 1202 | return failParse( | 1296 | return d.failParse( |
| 1203 | opts, | | |
| 1204 | "unable to read ilt entry {s}:{x}: {t}", | 1297 | "unable to read ilt entry {s}:{x}: {t}", |
| 1205 | .{ dll_name, ilt_entries.items.len, err }, | 1298 | .{ dll_name, ilt_entries.items.len, err }, |
| 1206 | ); | 1299 | ); |
| ... | @@ -1217,8 +1310,7 @@ const coff = struct { | ... | @@ -1217,8 +1310,7 @@ const coff = struct { |
| 1217 | sections.items, | 1310 | sections.items, |
| 1218 | ilt_entry.payload.hint_name_rva, | 1311 | ilt_entry.payload.hint_name_rva, |
| 1219 | ) orelse | 1312 | ) orelse |
| 1220 | return failParse( | 1313 | return d.failParse( |
| 1221 | opts, | | |
| 1222 | "import directory ilt entry 0x{x}'s hint rva 0x{x} was not found in any section", | 1314 | "import directory ilt entry 0x{x}'s hint rva 0x{x} was not found in any section", |
| 1223 | .{ ilt_entry_i, ilt_entry.payload.hint_name_rva }, | 1315 | .{ ilt_entry_i, ilt_entry.payload.hint_name_rva }, |
| 1224 | ); | 1316 | ); |
| ... | @@ -1227,22 +1319,19 @@ const coff = struct { | ... | @@ -1227,22 +1319,19 @@ const coff = struct { |
| 1227 | ilt_entry.payload.hint_name_rva, | 1319 | ilt_entry.payload.hint_name_rva, |
| 1228 | ) catch unreachable; | 1320 | ) catch unreachable; |
| 1229 | fr.seekTo(hint_loc) catch |err| | 1321 | fr.seekTo(hint_loc) catch |err| |
| 1230 | return failParse( | 1322 | return d.failParse( |
| 1231 | opts, | | |
| 1232 | "unable to seek to ilt entry 0x{x}'s hint at 0x{x}: {t}", | 1323 | "unable to seek to ilt entry 0x{x}'s hint at 0x{x}: {t}", |
| 1233 | .{ ilt_entry_i, hint_loc, err }, | 1324 | .{ ilt_entry_i, hint_loc, err }, |
| 1234 | ); | 1325 | ); |
| 1235 | | 1326 | |
| 1236 | const hint = r.takeInt(u16, .little) catch |err| | 1327 | const hint = r.takeInt(u16, .little) catch |err| |
| 1237 | return failParse( | 1328 | return d.failParse( |
| 1238 | opts, | | |
| 1239 | "unable to read import directory ilt entry 0x{x}'s hint: {t}", | 1329 | "unable to read import directory ilt entry 0x{x}'s hint: {t}", |
| 1240 | .{ ilt_entry_i, err }, | 1330 | .{ ilt_entry_i, err }, |
| 1241 | ); | 1331 | ); |
| 1242 | | 1332 | |
| 1243 | const name = r.takeDelimiter(0) catch |err| | 1333 | const name = r.takeDelimiter(0) catch |err| |
| 1244 | return failParse( | 1334 | return d.failParse( |
| 1245 | opts, | | |
| 1246 | "unable to read import directory ilt entry 0x{x}'s name: {t}", | 1335 | "unable to read import directory ilt entry 0x{x}'s name: {t}", |
| 1247 | .{ ilt_entry_i, err }, | 1336 | .{ ilt_entry_i, err }, |
| 1248 | ); | 1337 | ); |
| ... | @@ -1250,18 +1339,16 @@ const coff = struct { | ... | @@ -1250,18 +1339,16 @@ const coff = struct { |
| 1250 | try w.print(" {x: >4} | {s}\n", .{ hint, name.? }); | 1339 | try w.print(" {x: >4} | {s}\n", .{ hint, name.? }); |
| 1251 | } | 1340 | } |
| 1252 | } | 1341 | } |
| 1253 | try w.writeByte('\n'); | 1342 | if (d.element(.newlines)) try w.writeByte('\n'); |
| 1254 | }, | 1343 | }, |
| 1255 | } | 1344 | } |
| 1256 | } | 1345 | } |
| 1257 | } | 1346 | } |
| 1258 | } | 1347 | } |
| 1259 | | 1348 | |
| 1260 | if (opts.tls) { | 1349 | if (d.opts.tls) { |
| 1261 | if (try seekToDataDirectory( | 1350 | if (try seekToDataDirectory( |
| 1262 | opts, | 1351 | d, |
| 1263 | fr, | | |
| 1264 | w, | | |
| 1265 | rva_index, | 1352 | rva_index, |
| 1266 | sections.items, | 1353 | sections.items, |
| 1267 | image_info.?.data_dirs, | 1354 | image_info.?.data_dirs, |
| ... | @@ -1272,10 +1359,10 @@ const coff = struct { | ... | @@ -1272,10 +1359,10 @@ const coff = struct { |
| 1272 | inline else => |m| { | 1359 | inline else => |m| { |
| 1273 | const TlsDirectoryEntry = std.coff.TlsDirectoryEntry(m); | 1360 | const TlsDirectoryEntry = std.coff.TlsDirectoryEntry(m); |
| 1274 | const tls_entry = r.takeStruct(TlsDirectoryEntry, .little) catch |err| | 1361 | const tls_entry = r.takeStruct(TlsDirectoryEntry, .little) catch |err| |
| 1275 | return failParse(opts, "unable to read tls directory: {t}", .{err}); | 1362 | return d.failParse("unable to read tls directory: {t}", .{err}); |
| 1276 | | 1363 | |
| 1277 | try w.writeAll("TLS Directory:\n"); | 1364 | try w.writeAll("TLS Directory:\n"); |
| 1278 | try dumpHeader(w, TlsDirectoryEntry, &tls_entry, struct {}); | 1365 | try dumpHeader(d, TlsDirectoryEntry, &tls_entry, struct {}); |
| 1279 | | 1366 | |
| 1280 | try w.writeAll(" | "); | 1367 | try w.writeAll(" | "); |
| 1281 | if (tls_entry.characteristics.alignment == .NONE) { | 1368 | if (tls_entry.characteristics.alignment == .NONE) { |
| ... | @@ -1301,8 +1388,7 @@ const coff = struct { | ... | @@ -1301,8 +1388,7 @@ const coff = struct { |
| 1301 | sections.items, | 1388 | sections.items, |
| 1302 | callbacks_rva, | 1389 | callbacks_rva, |
| 1303 | ) orelse | 1390 | ) orelse |
| 1304 | return failParse( | 1391 | return d.failParse( |
| 1305 | opts, | | |
| 1306 | "tls callbacks rva 0x{x} was not found in any section", | 1392 | "tls callbacks rva 0x{x} was not found in any section", |
| 1307 | .{callbacks_rva}, | 1393 | .{callbacks_rva}, |
| 1308 | ); | 1394 | ); |
| ... | @@ -1311,24 +1397,22 @@ const coff = struct { | ... | @@ -1311,24 +1397,22 @@ const coff = struct { |
| 1311 | .rvaFileOffset(callbacks_rva) catch unreachable; | 1397 | .rvaFileOffset(callbacks_rva) catch unreachable; |
| 1312 | | 1398 | |
| 1313 | fr.seekTo(callbacks_loc) catch |err| | 1399 | fr.seekTo(callbacks_loc) catch |err| |
| 1314 | return failParse( | 1400 | return d.failParse( |
| 1315 | opts, | | |
| 1316 | "unable to seek to tls callbacks array at offset 0x{x}: {t}", | 1401 | "unable to seek to tls callbacks array at offset 0x{x}: {t}", |
| 1317 | .{ callbacks_loc, err }, | 1402 | .{ callbacks_loc, err }, |
| 1318 | ); | 1403 | ); |
| 1319 | | 1404 | |
| 1320 | while (true) { | 1405 | while (true) { |
| 1321 | const callback_va = r.takeInt(@FieldType(TlsDirectoryEntry, "callbacks_va"), .little) catch |err| | 1406 | const callback_va = r.takeInt(@FieldType(TlsDirectoryEntry, "callbacks_va"), .little) catch |err| |
| 1322 | return failParse( | 1407 | return d.failParse( |
| 1323 | opts, | | |
| 1324 | "unable to read tls callbacks array: {t}", | 1408 | "unable to read tls callbacks array: {t}", |
| 1325 | .{err}, | 1409 | .{err}, |
| 1326 | ); | 1410 | ); |
| 1327 | | 1411 | |
| 1328 | try w.print("{x: >16} \n", .{callback_va}); | 1412 | try w.print("{f}\n", .{fmtIntField(d, callback_va, .{ .kind = .va })}); |
| 1329 | if (callback_va == 0) break; | 1413 | if (callback_va == 0) break; |
| 1330 | } | 1414 | } |
| 1331 | try w.writeByte('\n'); | 1415 | if (d.element(.newlines)) try w.writeByte('\n'); |
| 1332 | }, | 1416 | }, |
| 1333 | } | 1417 | } |
| 1334 | } | 1418 | } |
| ... | @@ -1336,9 +1420,7 @@ const coff = struct { | ... | @@ -1336,9 +1420,7 @@ const coff = struct { |
| 1336 | } | 1420 | } |
| 1337 | | 1421 | |
| 1338 | fn seekToDataDirectory( | 1422 | fn seekToDataDirectory( |
| 1339 | opts: *const Options, | 1423 | d: *const DumpContext, |
| 1340 | fr: *Io.File.Reader, | | |
| 1341 | w: *Io.Writer, | | |
| 1342 | rva_index: []const u16, | 1424 | rva_index: []const u16, |
| 1343 | sections: []const Section, | 1425 | sections: []const Section, |
| 1344 | data_dirs: []const std.coff.ImageDataDirectory, | 1426 | data_dirs: []const std.coff.ImageDataDirectory, |
| ... | @@ -1349,16 +1431,14 @@ const coff = struct { | ... | @@ -1349,16 +1431,14 @@ const coff = struct { |
| 1349 | if (rva == 0) break :blk; | 1431 | if (rva == 0) break :blk; |
| 1350 | | 1432 | |
| 1351 | const section_index = sectionContainingRva(rva_index, sections, rva) orelse | 1433 | const section_index = sectionContainingRva(rva_index, sections, rva) orelse |
| 1352 | return failParse( | 1434 | return d.failParse( |
| 1353 | opts, | | |
| 1354 | "{t} directory rva 0x{x} was not found in any section", | 1435 | "{t} directory rva 0x{x} was not found in any section", |
| 1355 | .{ entry, rva }, | 1436 | .{ entry, rva }, |
| 1356 | ); | 1437 | ); |
| 1357 | | 1438 | |
| 1358 | const file_offset = sections[section_index].rvaFileOffset(rva) catch unreachable; | 1439 | const file_offset = sections[section_index].rvaFileOffset(rva) catch unreachable; |
| 1359 | fr.seekTo(file_offset) catch |err| | 1440 | d.fr.seekTo(file_offset) catch |err| |
| 1360 | return failParse( | 1441 | return d.failParse( |
| 1361 | opts, | | |
| 1362 | "unable to seek to {t} directory at offset 0x{x}: {t}", | 1442 | "unable to seek to {t} directory at offset 0x{x}: {t}", |
| 1363 | .{ entry, file_offset, err }, | 1443 | .{ entry, file_offset, err }, |
| 1364 | ); | 1444 | ); |
| ... | @@ -1366,7 +1446,7 @@ const coff = struct { | ... | @@ -1366,7 +1446,7 @@ const coff = struct { |
| 1366 | return section_index; | 1446 | return section_index; |
| 1367 | } | 1447 | } |
| 1368 | | 1448 | |
| 1369 | try w.print("{t} directory was not present in optional header\n", .{entry}); | 1449 | try d.w.print("{t} directory was not present in optional header\n", .{entry}); |
| 1370 | return null; | 1450 | return null; |
| 1371 | } | 1451 | } |
| 1372 | | 1452 | |
| ... | @@ -1382,19 +1462,18 @@ const coff = struct { | ... | @@ -1382,19 +1462,18 @@ const coff = struct { |
| 1382 | | 1462 | |
| 1383 | fn order(ctx: @This(), section_index: u16) std.math.Order { | 1463 | fn order(ctx: @This(), section_index: u16) std.math.Order { |
| 1384 | const h = &ctx.sections[section_index].header; | 1464 | const h = &ctx.sections[section_index].header; |
| 1385 | const start = h.virtual_address; | 1465 | if (ctx.rva < h.virtual_address) return .lt; |
| 1386 | if (ctx.rva < start) return .lt; | | |
| 1387 | const end = h.virtual_address + h.size_of_raw_data; | 1466 | const end = h.virtual_address + h.size_of_raw_data; |
| 1388 | if (ctx.rva >= end) return .gt; | 1467 | if (ctx.rva >= end) return .gt; |
| 1389 | return .eq; | 1468 | return .eq; |
| 1390 | } | 1469 | } |
| 1391 | }; | 1470 | }; |
| 1392 | | 1471 | |
| 1393 | const index = std.sort.binarySearch(u16, indices, Context{ | 1472 | const indices_index = std.sort.binarySearch(u16, indices, Context{ |
| 1394 | .rva = rva, | 1473 | .rva = rva, |
| 1395 | .sections = sections, | 1474 | .sections = sections, |
| 1396 | }, Context.order) orelse return null; | 1475 | }, Context.order) orelse return null; |
| 1397 | return @intCast(index); | 1476 | return @intCast(indices[indices_index]); |
| 1398 | } | 1477 | } |
| 1399 | | 1478 | |
| 1400 | fn headerName(raw: *const [8]u8, string_table: []const u8) ![]const u8 { | 1479 | fn headerName(raw: *const [8]u8, string_table: []const u8) ![]const u8 { |
| ... | @@ -1427,6 +1506,40 @@ const coff = struct { | ... | @@ -1427,6 +1506,40 @@ const coff = struct { |
| 1427 | }; | 1506 | }; |
| 1428 | } | 1507 | } |
| 1429 | | 1508 | |
| | 1509 | const FormatIntField = struct { |
| | 1510 | val: ?u64, |
| | 1511 | width: usize, |
| | 1512 | zero_fill: bool, |
| | 1513 | }; |
| | 1514 | |
| | 1515 | fn fmtIntField( |
| | 1516 | d: *const DumpContext, |
| | 1517 | val: anytype, |
| | 1518 | params: struct { |
| | 1519 | kind: ?FieldKind = null, |
| | 1520 | width: ?usize = null, |
| | 1521 | zero_fill: bool = false, |
| | 1522 | }, |
| | 1523 | ) std.fmt.Alt(FormatIntField, intFieldString) { |
| | 1524 | return .{ |
| | 1525 | .data = .{ |
| | 1526 | .val = if (d.redacted(params.kind)) null else val, |
| | 1527 | .width = params.width orelse @typeInfo(@TypeOf(val)).int.bits / 4, |
| | 1528 | .zero_fill = params.zero_fill, |
| | 1529 | }, |
| | 1530 | }; |
| | 1531 | } |
| | 1532 | |
| | 1533 | fn intFieldString(field: FormatIntField, w: *std.Io.Writer) std.Io.Writer.Error!void { |
| | 1534 | if (field.val) |val| { |
| | 1535 | try w.printInt(val, 16, .lower, .{ |
| | 1536 | .width = field.width, |
| | 1537 | .alignment = .right, |
| | 1538 | .fill = if (field.zero_fill) '0' else ' ', |
| | 1539 | }); |
| | 1540 | } else try w.splatByteAll('x', field.width); |
| | 1541 | } |
| | 1542 | |
| 1430 | fn dumpFlags(w: *Io.Writer, comptime fmt: []const u8, comptime T: type, flags: *const T, cols: u32) !void { | 1543 | fn dumpFlags(w: *Io.Writer, comptime fmt: []const u8, comptime T: type, flags: *const T, cols: u32) !void { |
| 1431 | const s = @typeInfo(T).@"struct"; | 1544 | const s = @typeInfo(T).@"struct"; |
| 1432 | inline for (s.fields) |flag_field| { | 1545 | inline for (s.fields) |flag_field| { |
| ... | @@ -1437,33 +1550,53 @@ const coff = struct { | ... | @@ -1437,33 +1550,53 @@ const coff = struct { |
| 1437 | } | 1550 | } |
| 1438 | } | 1551 | } |
| 1439 | | 1552 | |
| 1440 | fn dumpArchiveHeader(w: *Io.Writer, header: *const ArchiveHeader, pos: u32) !void { | 1553 | fn dumpArchiveHeader(d: *const DumpContext, header: *const ArchiveHeader, pos: u32) !void { |
| 1441 | try w.print("Archive member at offset 0x{x}: '{s}'\n", .{ pos, header.name }); | 1554 | try d.w.print("Archive member at offset 0x{x}: '{s}'\n", .{ pos, header.name }); |
| 1442 | try dumpHeader(w, ArchiveHeader, header, struct { | 1555 | try dumpHeader(d, ArchiveHeader, header, struct { |
| 1443 | pub fn name(_: *const ArchiveHeader, _: *Io.Writer) !void {} | 1556 | pub fn name(_: *const DumpContext, _: *const ArchiveHeader) !void {} |
| 1444 | pub fn file_mode(h: *const ArchiveHeader, cw: *Io.Writer) !void { | 1557 | pub fn file_mode(id: *const DumpContext, h: *const ArchiveHeader) !void { |
| 1445 | try cw.print("{o: >16} file_mode\n", .{h.file_mode}); | 1558 | try id.w.print("{o: >16} file_mode\n", .{h.file_mode}); |
| 1446 | } | 1559 | } |
| 1447 | }); | 1560 | }); |
| 1448 | } | 1561 | } |
| 1449 | | 1562 | |
| 1450 | fn dumpHeader(w: *Io.Writer, comptime T: type, header: *const T, Custom: type) !void { | 1563 | fn fieldKind(name: []const u8) ?FieldKind { |
| | 1564 | if (std.mem.endsWith(u8, name, "_rva")) |
| | 1565 | return .rva; |
| | 1566 | if (std.mem.endsWith(u8, name, "_va") or |
| | 1567 | std.mem.endsWith(u8, name, "_address") or |
| | 1568 | std.mem.startsWith(u8, name, "pointer_")) |
| | 1569 | return .va; |
| | 1570 | if (std.mem.startsWith(u8, name, "number_")) |
| | 1571 | return .size; |
| | 1572 | return null; |
| | 1573 | } |
| | 1574 | |
| | 1575 | fn dumpHeader( |
| | 1576 | d: *const DumpContext, |
| | 1577 | comptime T: type, |
| | 1578 | header: *const T, |
| | 1579 | Custom: type, |
| | 1580 | ) !void { |
| 1451 | inline for (@typeInfo(T).@"struct".fields) |field| { | 1581 | inline for (@typeInfo(T).@"struct".fields) |field| { |
| 1452 | const val = &@field(header, field.name); | 1582 | const val = &@field(header, field.name); |
| 1453 | if (@hasDecl(Custom, field.name)) { | 1583 | if (@hasDecl(Custom, field.name)) { |
| 1454 | try @field(Custom, field.name)(header, w); | 1584 | try @field(Custom, field.name)(d, header); |
| 1455 | } else { | 1585 | } else { |
| 1456 | switch (@typeInfo(field.type)) { | 1586 | switch (@typeInfo(field.type)) { |
| 1457 | .int => try w.print("{x: >16} {s}\n", .{ val.*, field.name }), | 1587 | .int => try d.w.print("{f} {s}\n", .{ fmtIntField(d, val.*, .{ |
| 1458 | .@"enum" => try w.print("{x: >16} {s} ({t})\n", .{ val.*, field.name, val.* }), | 1588 | .kind = comptime fieldKind(field.name), |
| | 1589 | .width = 16, |
| | 1590 | }), field.name }), |
| | 1591 | .@"enum" => try d.w.print("{x: >16} {s} ({t})\n", .{ val.*, field.name, val.* }), |
| 1459 | .@"struct" => |s| { | 1592 | .@"struct" => |s| { |
| 1460 | switch (s.layout) { | 1593 | switch (s.layout) { |
| 1461 | .auto, | 1594 | .auto, |
| 1462 | .@"extern", | 1595 | .@"extern", |
| 1463 | => try dumpHeader(w, field.type, val, Custom), | 1596 | => try dumpHeader(d, field.type, val, Custom), |
| 1464 | .@"packed" => { | 1597 | .@"packed" => { |
| 1465 | try w.print("{x: >16} {s}\n", .{ @as(s.backing_integer.?, @bitCast(val.*)), field.name }); | 1598 | try d.w.print("{x: >16} {s}\n", .{ @as(s.backing_integer.?, @bitCast(val.*)), field.name }); |
| 1466 | try dumpFlags(w, "| {s}\n", field.type, val, 15); | 1599 | try dumpFlags(d.w, "| {s}\n", field.type, val, 15); |
| 1467 | }, | 1600 | }, |
| 1468 | } | 1601 | } |
| 1469 | }, | 1602 | }, |
| ... | @@ -1477,8 +1610,12 @@ const coff = struct { | ... | @@ -1477,8 +1610,12 @@ const coff = struct { |
| 1477 | try w.print("{d: >13}.{x:0<2} {s}\n", .{ major, minor, name }); | 1610 | try w.print("{d: >13}.{x:0<2} {s}\n", .{ major, minor, name }); |
| 1478 | } | 1611 | } |
| 1479 | | 1612 | |
| 1480 | fn dumpRvaField(w: *Io.Writer, name: []const u8, rva: u64, base: u64) !void { | 1613 | fn dumpRvaField(d: *const DumpContext, name: []const u8, rva: u64, base: u64) !void { |
| 1481 | try w.print("{x: >16} {s} ({x})\n", .{ rva, name, base + rva }); | 1614 | try d.w.print("{f} {s} ({f})\n", .{ |
| | 1615 | fmtIntField(d, rva, .{ .kind = .rva }), |
| | 1616 | name, |
| | 1617 | fmtIntField(d, base + rva, .{ .kind = .va }), |
| | 1618 | }); |
| 1482 | } | 1619 | } |
| 1483 | }; | 1620 | }; |
| 1484 | | 1621 | |
| ... | @@ -1492,18 +1629,32 @@ const usage = | ... | @@ -1492,18 +1629,32 @@ const usage = |
| 1492 | \\Usage: zig objdump [options] file | 1629 | \\Usage: zig objdump [options] file |
| 1493 | \\ | 1630 | \\ |
| 1494 | \\Options: | 1631 | \\Options: |
| 1495 | \\ -h, --help Print this help and exit | 1632 | \\ -h, --help Print this help and exit |
| 1496 | \\ --all-headers Alias for --file-headers --member-headers --section-headers --relocs --symbols | 1633 | \\ --all-headers Alias for --file-headers --member-headers --section-headers --relocs --symbols |
| 1497 | \\ --file-headers Display file-format specific headers | 1634 | \\ --file-headers Display file-format specific headers |
| 1498 | \\ --imports Display imported symbols | 1635 | \\ --imports Display imported symbols |
| 1499 | \\ --exports Display exported symbols | 1636 | \\ --exports Display exported symbols |
| 1500 | \\ --linker-member[=1|2|longnames] (Coff) Display contents of the specified linker member (default 2) | 1637 | \\ --linker-member[=1|2|longnames] (Coff) Display contents of the specified archive linker member (default 2) |
| 1501 | \\ --member-headers Display archive member headers | 1638 | \\ --member-headers Display archive member headers |
| 1502 | \\ --only-member=[name] Only consider archive members names that contain [name]. Can be specified multiple times. | 1639 | \\ --redact=[kind] Redact the specified field kind. Intended for snapshot testing. |
| 1503 | \\ --only-section=[name] Only consider section names that contain [name]. Can be specified multiple times. | 1640 | \\ rva Relative virtual addresses |
| 1504 | \\ --relocs Display relocations | 1641 | \\ va Virtual addresses and file offsets |
| 1505 | \\ --section-headers Display section headers | 1642 | \\ ord Symbol ordinals / hints |
| 1506 | \\ --strings Display string tables | 1643 | \\ size Sizes and lengths |
| 1507 | \\ --symbols Display symbol tables | 1644 | \\ all All of the above |
| 1508 | \\ --tls Display TLS information | 1645 | \\ --omit-element=[kind] Omit specific parts of the output. Intended for snapshot testing. |
| | 1646 | \\ file-type File type summary |
| | 1647 | \\ table-headers Table headers with column names |
| | 1648 | \\ header-names Name that precedes a header block |
| | 1649 | \\ newlines Newlines between output sections |
| | 1650 | \\ all All of the above |
| | 1651 | \\ --only-member=[name] Only consider archive members names that contain [name]. Can be specified multiple times. |
| | 1652 | \\ --only-section=[name] Only consider section names that contain [name]. Can be specified multiple times. |
| | 1653 | \\ --only-symbol=[name] Only consider symbol names that contain [name]. Can be specified multiple times. |
| | 1654 | \\ --relocs Display relocations |
| | 1655 | \\ -s, --snapshot Alias for --redact=all --omit-format=all |
| | 1656 | \\ --section-headers Display section headers |
| | 1657 | \\ --strings Display string tables |
| | 1658 | \\ --symbols Display symbol tables |
| | 1659 | \\ --tls Display TLS information |
| 1509 | ; | 1660 | ; |