| ... | @@ -10,10 +10,13 @@ const native_endian = builtin.cpu.arch.endian(); | ... | @@ -10,10 +10,13 @@ const native_endian = builtin.cpu.arch.endian(); |
| 10 | var stdout_buffer: [4000]u8 = undefined; | 10 | var stdout_buffer: [4000]u8 = undefined; |
| 11 | | 11 | |
| 12 | const Options = struct { | 12 | const Options = struct { |
| 13 | input_path: []const u8, | 13 | exports: bool, |
| 14 | file_headers: bool, | 14 | file_headers: bool, |
| | 15 | imports: bool, |
| | 16 | input_path: []const u8, |
| 15 | member_filters: []const []const u8 = &.{}, | 17 | member_filters: []const []const u8 = &.{}, |
| 16 | member_headers: bool, | 18 | member_headers: bool, |
| | 19 | relocs: bool, |
| 17 | section_filters: []const []const u8 = &.{}, | 20 | section_filters: []const []const u8 = &.{}, |
| 18 | section_headers: bool, | 21 | section_headers: bool, |
| 19 | strings: bool, | 22 | strings: bool, |
| ... | @@ -30,14 +33,16 @@ pub fn main(init: std.process.Init) !void { | ... | @@ -30,14 +33,16 @@ pub fn main(init: std.process.Init) !void { |
| 30 | | 33 | |
| 31 | var i: usize = 1; | 34 | var i: usize = 1; |
| 32 | | 35 | |
| 33 | var opt_input_path: ?[]const u8 = null; | 36 | var opt_exports: ?bool = null; |
| 34 | var opt_file_headers: ?bool = null; | 37 | var opt_file_headers: ?bool = null; |
| | 38 | var opt_imports: ?bool = null; |
| | 39 | var opt_input_path: ?[]const u8 = null; |
| 35 | var opt_linker_member: ?std.coff.ArchiveMemberHeader.Kind = null; | 40 | var opt_linker_member: ?std.coff.ArchiveMemberHeader.Kind = null; |
| 36 | var opt_member_headers: ?bool = null; | 41 | var opt_member_headers: ?bool = null; |
| | 42 | var opt_relocs: ?bool = null; |
| 37 | var opt_section_headers: ?bool = null; | 43 | var opt_section_headers: ?bool = null; |
| 38 | var opt_strings: ?bool = null; | 44 | var opt_strings: ?bool = null; |
| 39 | var opt_symbols: ?bool = null; | 45 | var opt_symbols: ?bool = null; |
| 40 | var opt_relocs: ?bool = null; | | |
| 41 | var section_filters: std.ArrayList([]const u8) = .empty; | 46 | var section_filters: std.ArrayList([]const u8) = .empty; |
| 42 | var member_filters: std.ArrayList([]const u8) = .empty; | 47 | var member_filters: std.ArrayList([]const u8) = .empty; |
| 43 | while (i < args.len) : (i += 1) { | 48 | while (i < args.len) : (i += 1) { |
| ... | @@ -51,11 +56,17 @@ pub fn main(init: std.process.Init) !void { | ... | @@ -51,11 +56,17 @@ pub fn main(init: std.process.Init) !void { |
| 51 | opt_section_headers = true; | 56 | opt_section_headers = true; |
| 52 | opt_symbols = true; | 57 | opt_symbols = true; |
| 53 | opt_relocs = true; | 58 | opt_relocs = true; |
| | 59 | } else if (mem.eql(u8, arg, "--exports")) { |
| | 60 | opt_exports = true; |
| 54 | } else if (mem.eql(u8, arg, "--file-headers")) { | 61 | } else if (mem.eql(u8, arg, "--file-headers")) { |
| 55 | opt_file_headers = true; | 62 | opt_file_headers = true; |
| | 63 | } else if (mem.eql(u8, arg, "--imports")) { |
| | 64 | opt_imports = true; |
| 56 | } else if (mem.startsWith(u8, arg, "--linker-member")) { | 65 | } else if (mem.startsWith(u8, arg, "--linker-member")) { |
| 57 | if (mem.eql(u8, arg["--linker-member".len..], "=1")) | 66 | if (mem.eql(u8, arg["--linker-member".len..], "=1")) |
| 58 | opt_linker_member = .first_linker | 67 | opt_linker_member = .first_linker |
| | 68 | else if (mem.eql(u8, arg["--linker-member".len..], "=longnames")) |
| | 69 | opt_linker_member = .longnames |
| 59 | else | 70 | else |
| 60 | opt_linker_member = .second_linker; | 71 | opt_linker_member = .second_linker; |
| 61 | } else if (mem.eql(u8, arg, "--member-headers")) { | 72 | } else if (mem.eql(u8, arg, "--member-headers")) { |
| ... | @@ -84,9 +95,12 @@ pub fn main(init: std.process.Init) !void { | ... | @@ -84,9 +95,12 @@ pub fn main(init: std.process.Init) !void { |
| 84 | | 95 | |
| 85 | const opts: Options = .{ | 96 | const opts: Options = .{ |
| 86 | .input_path = opt_input_path orelse fatal("missing input file path positional argument", .{}), | 97 | .input_path = opt_input_path orelse fatal("missing input file path positional argument", .{}), |
| | 98 | .exports = opt_exports orelse false, |
| 87 | .file_headers = opt_file_headers orelse false, | 99 | .file_headers = opt_file_headers orelse false, |
| | 100 | .imports = opt_imports orelse false, |
| 88 | .section_filters = section_filters.items, | 101 | .section_filters = section_filters.items, |
| 89 | .section_headers = opt_section_headers orelse false, | 102 | .section_headers = opt_section_headers orelse false, |
| | 103 | .relocs = opt_relocs orelse false, |
| 90 | .strings = opt_strings orelse false, | 104 | .strings = opt_strings orelse false, |
| 91 | .symbols = opt_symbols orelse false, | 105 | .symbols = opt_symbols orelse false, |
| 92 | .member_filters = member_filters.items, | 106 | .member_filters = member_filters.items, |
| ... | @@ -129,6 +143,7 @@ fn dump(gpa: std.mem.Allocator, opts: *const Options, fr: *Io.File.Reader, w: *I | ... | @@ -129,6 +143,7 @@ fn dump(gpa: std.mem.Allocator, opts: *const Options, fr: *Io.File.Reader, w: *I |
| 129 | } | 143 | } |
| 130 | coff: { | 144 | coff: { |
| 131 | const ext = std.fs.path.extension(opts.input_path); | 145 | const ext = std.fs.path.extension(opts.input_path); |
| | 146 | const basename = std.fs.path.basename(opts.input_path); |
| 132 | if (std.mem.eql(u8, ext, ".exe") or std.mem.eql(u8, ext, ".dll")) { | 147 | if (std.mem.eql(u8, ext, ".exe") or std.mem.eql(u8, ext, ".dll")) { |
| 133 | if (!mem.eql(u8, r.buffered()[0..2], "MZ")) break :coff; | 148 | if (!mem.eql(u8, r.buffered()[0..2], "MZ")) break :coff; |
| 134 | try r.discardAll(std.coff.pe_pointer_offset); | 149 | try r.discardAll(std.coff.pe_pointer_offset); |
| ... | @@ -141,16 +156,16 @@ fn dump(gpa: std.mem.Allocator, opts: *const Options, fr: *Io.File.Reader, w: *I | ... | @@ -141,16 +156,16 @@ fn dump(gpa: std.mem.Allocator, opts: *const Options, fr: *Io.File.Reader, w: *I |
| 141 | return error.ParseFailure; | 156 | return error.ParseFailure; |
| 142 | } | 157 | } |
| 143 | | 158 | |
| 144 | try w.print("{s}: PE/COFF image\n\n", .{std.fs.path.basename(opts.input_path)}); | 159 | try w.print("{s}: PE/COFF image\n\n", .{basename}); |
| 145 | return coff.dumpObject(gpa, opts, true, fr, w); | 160 | return coff.dumpObject(gpa, opts, true, basename, fr, w); |
| 146 | } else if (std.mem.eql(u8, ext, ".lib")) { | 161 | } else if (std.mem.eql(u8, ext, ".lib")) { |
| 147 | r.fill(std.coff.archive_signature.len) catch break :coff; | 162 | r.fill(std.coff.archive_signature.len) catch break :coff; |
| 148 | if (!mem.eql(u8, r.buffered()[0..std.coff.archive_signature.len], std.coff.archive_signature)) break :coff; | 163 | if (!mem.eql(u8, r.buffered()[0..std.coff.archive_signature.len], std.coff.archive_signature)) break :coff; |
| 149 | try w.print("{s}: COFF archive\n\n", .{std.fs.path.basename(opts.input_path)}); | 164 | try w.print("{s}: COFF archive\n\n", .{basename}); |
| 150 | return coff.dumpArchive(gpa, opts, fr, w); | 165 | return coff.dumpArchive(gpa, opts, fr, w); |
| 151 | } else if (std.mem.eql(u8, ext, ".obj")) { | 166 | } else if (std.mem.eql(u8, ext, ".obj")) { |
| 152 | try w.print("{s}: COFF object\n\n", .{std.fs.path.basename(opts.input_path)}); | 167 | try w.print("{s}: COFF object\n\n", .{basename}); |
| 153 | return coff.dumpObject(gpa, opts, false, fr, w); | 168 | return coff.dumpObject(gpa, opts, false, basename, fr, w); |
| 154 | } | 169 | } |
| 155 | } | 170 | } |
| 156 | return error.UnknownFile; | 171 | return error.UnknownFile; |
| ... | @@ -242,7 +257,10 @@ const coff = struct { | ... | @@ -242,7 +257,10 @@ const coff = struct { |
| 242 | if (!std.mem.eql(u8, &raw_header.end_of_header, std.coff.archive_end_of_header)) | 257 | if (!std.mem.eql(u8, &raw_header.end_of_header, std.coff.archive_end_of_header)) |
| 243 | return failParse(opts, "malformed end-of-header field in member '{s}': {x}", .{ header.name, raw_header.end_of_header }); | 258 | return failParse(opts, "malformed end-of-header field in member '{s}': {x}", .{ header.name, raw_header.end_of_header }); |
| 244 | | 259 | |
| 245 | const dump_header = opts.member_headers and filterMatches(opts.member_filters, header.name); | 260 | const dump_header = |
| | 261 | (opts.member_headers and filterMatches(opts.member_filters, header.name)) or |
| | 262 | (opts.linker_member == opt_expected_kind); |
| | 263 | |
| 246 | if (dump_header) | 264 | if (dump_header) |
| 247 | try dumpArchiveHeader(w, &header, @intCast(pos)); | 265 | try dumpArchiveHeader(w, &header, @intCast(pos)); |
| 248 | | 266 | |
| ... | @@ -264,11 +282,12 @@ const coff = struct { | ... | @@ -264,11 +282,12 @@ const coff = struct { |
| 264 | , .{ expected_kind, num_symbols }); | 282 | , .{ expected_kind, num_symbols }); |
| 265 | | 283 | |
| 266 | if (opts.linker_member == .first_linker) { | 284 | if (opts.linker_member == .first_linker) { |
| 267 | try w.print( | 285 | try w.writeAll( |
| 268 | \\Archives symbols ({d}): | 286 | \\ |
| | 287 | \\Archive symbols: |
| 269 | \\& Member Symbol | 288 | \\& Member Symbol |
| 270 | \\ | 289 | \\ |
| 271 | , .{num_symbols}); | 290 | ); |
| 272 | | 291 | |
| 273 | const offsets = try r.readAlloc(gpa, num_symbols * 4); | 292 | const offsets = try r.readAlloc(gpa, num_symbols * 4); |
| 274 | defer gpa.free(offsets); | 293 | defer gpa.free(offsets); |
| ... | @@ -320,11 +339,12 @@ const coff = struct { | ... | @@ -320,11 +339,12 @@ const coff = struct { |
| 320 | symbol_member_indices.addOneAssumeCapacity().* = (try r.takeInt(u16, .little)) - 1; | 339 | symbol_member_indices.addOneAssumeCapacity().* = (try r.takeInt(u16, .little)) - 1; |
| 321 | | 340 | |
| 322 | if (opts.linker_member == .second_linker) { | 341 | if (opts.linker_member == .second_linker) { |
| 323 | try w.print( | 342 | try w.writeAll( |
| 324 | \\Archive symbols ({d} members, {d} symbols): | 343 | \\ |
| | 344 | \\Archive Symbols: |
| 325 | \\& Member Symbol | 345 | \\& Member Symbol |
| 326 | \\ | 346 | \\ |
| 327 | , .{ num_members, num_symbols }); | 347 | ); |
| 328 | | 348 | |
| 329 | pos = fr.logicalPos(); | 349 | pos = fr.logicalPos(); |
| 330 | var symbol_i: u32 = 0; | 350 | var symbol_i: u32 = 0; |
| ... | @@ -362,6 +382,21 @@ const coff = struct { | ... | @@ -362,6 +382,21 @@ const coff = struct { |
| 362 | opt_longnames = try r.readAlloc(gpa, header.size); | 382 | opt_longnames = try r.readAlloc(gpa, header.size); |
| 363 | if (dump_header) | 383 | if (dump_header) |
| 364 | try w.print("{t: >16} type\n", .{expected_kind}); | 384 | try w.print("{t: >16} type\n", .{expected_kind}); |
| | 385 | |
| | 386 | if (opts.linker_member == .longnames) { |
| | 387 | try w.print( |
| | 388 | \\ |
| | 389 | \\Longnames (0x{x} bytes): |
| | 390 | \\ |
| | 391 | , .{opt_longnames.?.len}); |
| | 392 | |
| | 393 | var lr = Io.Reader.fixed(opt_longnames.?); |
| | 394 | while (try lr.takeDelimiter(0)) |str| { |
| | 395 | try w.writeAll(str); |
| | 396 | try w.writeByte('\n'); |
| | 397 | } |
| | 398 | try w.writeByte('\n'); |
| | 399 | } |
| 365 | } | 400 | } |
| 366 | | 401 | |
| 367 | opt_expected_kind = null; | 402 | opt_expected_kind = null; |
| ... | @@ -390,13 +425,50 @@ const coff = struct { | ... | @@ -390,13 +425,50 @@ const coff = struct { |
| 390 | @enumFromInt(std.mem.readInt(u16, member_sig[0..2], .little)); | 425 | @enumFromInt(std.mem.readInt(u16, member_sig[0..2], .little)); |
| 391 | const sig = std.mem.readInt(u16, member_sig[2..4], .little); | 426 | const sig = std.mem.readInt(u16, member_sig[2..4], .little); |
| 392 | | 427 | |
| 393 | const is_import = machine == std.coff.IMAGE.FILE.MACHINE.UNKNOWN and sig == 0xffff; | 428 | const is_imp_lib = machine == std.coff.IMAGE.FILE.MACHINE.UNKNOWN and sig == 0xffff; |
| 394 | | 429 | |
| 395 | if (opts.member_headers) { | 430 | if (opts.member_headers or (opts.exports and is_imp_lib)) { |
| 396 | try dumpArchiveHeader(w, &header, member.offset); | 431 | try dumpArchiveHeader(w, &header, member.offset); |
| 397 | if (is_import) { | 432 | if (is_imp_lib) { |
| 398 | try w.writeAll(" Import header type\n"); | 433 | try w.writeAll("\nImport header:\n"); |
| 399 | // TODO: Dump import header | 434 | |
| | 435 | const imp_header = try r.takeStruct(std.coff.ImportHeader, .little); |
| | 436 | try dumpHeader(w, std.coff.ImportHeader, &imp_header, struct { |
| | 437 | pub fn sig1(_: *const std.coff.ImportHeader, _: *Io.Writer) !void {} |
| | 438 | pub fn sig2(_: *const std.coff.ImportHeader, _: *Io.Writer) !void {} |
| | 439 | pub fn types(h: *const std.coff.ImportHeader, cw: *Io.Writer) !void { |
| | 440 | try cw.print( |
| | 441 | \\{t: >16} import_type |
| | 442 | \\{t: >16} name_type |
| | 443 | \\ |
| | 444 | , .{ h.types.type, h.types.name_type }); |
| | 445 | } |
| | 446 | }); |
| | 447 | |
| | 448 | const sym_name = (try r.takeDelimiter(0)).?; |
| | 449 | const imp_dll = (try r.takeDelimiter(0)).?; |
| | 450 | const imp_name = imp_name: switch (imp_header.types.name_type) { |
| | 451 | .NAME_NOPREFIX, |
| | 452 | .NAME_UNDECORATE, |
| | 453 | => |tag| { |
| | 454 | var imp_name = std.mem.trimStart(u8, sym_name, "?@_"); |
| | 455 | if (tag == .NAME_UNDECORATE) |
| | 456 | imp_name = std.mem.sliceTo(imp_name, '@'); |
| | 457 | break :imp_name imp_name; |
| | 458 | }, |
| | 459 | else => sym_name, |
| | 460 | }; |
| | 461 | |
| | 462 | try w.print( |
| | 463 | \\ symbol name | {s} |
| | 464 | \\ import name | {s} |
| | 465 | \\ dll | {s} |
| | 466 | \\ |
| | 467 | , .{ |
| | 468 | sym_name, |
| | 469 | imp_name, |
| | 470 | imp_dll, |
| | 471 | }); |
| 400 | } else { | 472 | } else { |
| 401 | try w.writeAll(" COFF object type\n"); | 473 | try w.writeAll(" COFF object type\n"); |
| 402 | } | 474 | } |
| ... | @@ -409,7 +481,7 @@ const coff = struct { | ... | @@ -409,7 +481,7 @@ const coff = struct { |
| 409 | opts.symbols) | 481 | opts.symbols) |
| 410 | { | 482 | { |
| 411 | try w.print("{s}({s}): COFF object\n\n", .{ std.fs.path.basename(opts.input_path), header.name }); | 483 | try w.print("{s}({s}): COFF object\n\n", .{ std.fs.path.basename(opts.input_path), header.name }); |
| 412 | try dumpObject(gpa, opts, false, fr, w); | 484 | try dumpObject(gpa, opts, false, header.name, fr, w); |
| 413 | } | 485 | } |
| 414 | } | 486 | } |
| 415 | } | 487 | } |
| ... | @@ -418,6 +490,7 @@ const coff = struct { | ... | @@ -418,6 +490,7 @@ const coff = struct { |
| 418 | gpa: std.mem.Allocator, | 490 | gpa: std.mem.Allocator, |
| 419 | opts: *const Options, | 491 | opts: *const Options, |
| 420 | is_image: bool, | 492 | is_image: bool, |
| | 493 | obj_name: []const u8, |
| 421 | fr: *Io.File.Reader, | 494 | fr: *Io.File.Reader, |
| 422 | w: *Io.Writer, | 495 | w: *Io.Writer, |
| 423 | ) !void { | 496 | ) !void { |
| ... | @@ -432,6 +505,11 @@ const coff = struct { | ... | @@ -432,6 +505,11 @@ const coff = struct { |
| 432 | try w.writeByte('\n'); | 505 | try w.writeByte('\n'); |
| 433 | } | 506 | } |
| 434 | | 507 | |
| | 508 | switch (header.machine) { |
| | 509 | _ => return failParse(opts, "unknown machine type: {x}", .{header.machine}), |
| | 510 | else => {}, |
| | 511 | } |
| | 512 | |
| 435 | if (header.size_of_optional_header > 0) opt_header: { | 513 | if (header.size_of_optional_header > 0) opt_header: { |
| 436 | if (!opts.file_headers) { | 514 | if (!opts.file_headers) { |
| 437 | try fr.seekBy(header.size_of_optional_header); | 515 | try fr.seekBy(header.size_of_optional_header); |
| ... | @@ -541,59 +619,65 @@ const coff = struct { | ... | @@ -541,59 +619,65 @@ const coff = struct { |
| 541 | try w.writeByte('\n'); | 619 | try w.writeByte('\n'); |
| 542 | } | 620 | } |
| 543 | | 621 | |
| 544 | var sections: std.ArrayList(std.coff.SectionHeader) = .empty; | 622 | var sections: std.ArrayList(struct { |
| | 623 | header: std.coff.SectionHeader, |
| | 624 | name: []const u8, |
| | 625 | }) = .empty; |
| 545 | defer sections.deinit(gpa); | 626 | defer sections.deinit(gpa); |
| 546 | | 627 | |
| 547 | const load_sections = opts.section_headers or opts.symbols; | 628 | const load_sections = |
| | 629 | opts.section_headers or |
| | 630 | opts.symbols or |
| | 631 | opts.relocs; |
| | 632 | |
| 548 | if (load_sections) { | 633 | if (load_sections) { |
| 549 | if (opts.section_headers) | 634 | if (opts.section_headers) |
| 550 | try w.writeAll( | 635 | try w.print( |
| 551 | \\Section Table: | 636 | \\Sections ({s}): |
| 552 | \\Num Name RVA Virt Size Data Size & Data & Relocs & Lines # Relocs # Lines Flags | 637 | \\Num Name RVA Virt Size Data Size & Data & Relocs & Lines # Relocs # Lines Flags |
| 553 | \\ | 638 | \\ |
| 554 | ); | 639 | , .{obj_name}); |
| 555 | | 640 | |
| 556 | try sections.resize(gpa, header.number_of_sections); | 641 | try sections.resize(gpa, header.number_of_sections); |
| 557 | for (sections.items, 0..) |*section, section_i| { | 642 | for (sections.items, 0..) |*section, section_i| { |
| 558 | section.* = r.takeStruct(std.coff.SectionHeader, .little) catch |err| | 643 | section.header = r.takeStruct(std.coff.SectionHeader, .little) catch |err| |
| 559 | return failParse(opts, "unable to read section header {x}: {t}", .{ section_i, err }); | 644 | return failParse(opts, "unable to read section header {x}: {t}", .{ section_i, err }); |
| | 645 | section.name = headerName(&section.header.name, string_table) catch |err| switch (err) { |
| | 646 | error.Overflow, |
| | 647 | error.InvalidCharacter, |
| | 648 | => return failParse(opts, "unable to parse section name offset '{s}': {t}", .{ |
| | 649 | section.name, |
| | 650 | err, |
| | 651 | }), |
| | 652 | error.OutOfBounds => return failParse(opts, "section name offset '{s}' was out of bounds (>= {x})", .{ |
| | 653 | section.name, |
| | 654 | string_table.len, |
| | 655 | }), |
| | 656 | }; |
| 560 | | 657 | |
| 561 | if (opts.section_headers) { | 658 | if (opts.section_headers) { |
| 562 | const name = headerName(&section.name, string_table) catch |err| switch (err) { | 659 | if (!filterMatches(opts.section_filters, section.name)) continue; |
| 563 | error.Overflow, | 660 | const raw_name = std.mem.sliceTo(&section.header.name, 0); |
| 564 | error.InvalidCharacter, | | |
| 565 | => return failParse(opts, "unable to parse section name offset '{s}': {t}", .{ | | |
| 566 | section.name, | | |
| 567 | err, | | |
| 568 | }), | | |
| 569 | error.OutOfBounds => return failParse(opts, "section name offset '{s}' was out of bounds (>= {x})", .{ | | |
| 570 | section.name, | | |
| 571 | string_table.len, | | |
| 572 | }), | | |
| 573 | }; | | |
| 574 | | | |
| 575 | if (!filterMatches(opts.section_filters, name)) continue; | | |
| 576 | const raw_name = std.mem.sliceTo(&section.name, 0); | | |
| 577 | try w.print( | 661 | try w.print( |
| 578 | "{x: >3} {s: <8} {x: >8} {x: >9} {x: >9} {x: >8} {x: >8} {x: >8} {x: >8} {x: >8} {x:0>8} | ", | 662 | "{x: >3} {s: <8} {x: >8} {x: >9} {x: >9} {x: >8} {x: >8} {x: >8} {x: >8} {x: >8} {x:0>8} | ", |
| 579 | .{ | 663 | .{ |
| 580 | section_i + 1, | 664 | section_i + 1, |
| 581 | raw_name, | 665 | raw_name, |
| 582 | section.virtual_address, | 666 | section.header.virtual_address, |
| 583 | section.virtual_size, | 667 | section.header.virtual_size, |
| 584 | section.size_of_raw_data, | 668 | section.header.size_of_raw_data, |
| 585 | section.pointer_to_raw_data, | 669 | section.header.pointer_to_raw_data, |
| 586 | section.pointer_to_relocations, | 670 | section.header.pointer_to_relocations, |
| 587 | section.pointer_to_linenumbers, | 671 | section.header.pointer_to_linenumbers, |
| 588 | section.number_of_relocations, | 672 | section.header.number_of_relocations, |
| 589 | section.number_of_linenumbers, | 673 | section.header.number_of_linenumbers, |
| 590 | @as(u32, @bitCast(section.flags)), | 674 | @as(u32, @bitCast(section.header.flags)), |
| 591 | }, | 675 | }, |
| 592 | ); | 676 | ); |
| 593 | | 677 | |
| 594 | try dumpFlags(w, "{s} ", std.coff.SectionHeader.Flags, &section.flags, 0); | 678 | try dumpFlags(w, "{s} ", std.coff.SectionHeader.Flags, &section.header.flags, 1); |
| 595 | if (name.len > 8) | 679 | if (section.name.len > 8) |
| 596 | try w.print("| {s}", .{name}); | 680 | try w.print("| {s}", .{section.name}); |
| 597 | | 681 | |
| 598 | try w.writeByte('\n'); | 682 | try w.writeByte('\n'); |
| 599 | } | 683 | } |
| ... | @@ -602,16 +686,21 @@ const coff = struct { | ... | @@ -602,16 +686,21 @@ const coff = struct { |
| 602 | if (opts.section_headers) try w.writeByte('\n'); | 686 | if (opts.section_headers) try w.writeByte('\n'); |
| 603 | } | 687 | } |
| 604 | | 688 | |
| | 689 | var symbol_names: std.ArrayList([]const u8) = .empty; |
| | 690 | defer symbol_names.deinit(gpa); |
| | 691 | if (opts.relocs) |
| | 692 | try symbol_names.ensureUnusedCapacity(gpa, header.number_of_symbols); |
| | 693 | |
| 605 | if (opts.symbols) { | 694 | if (opts.symbols) { |
| 606 | if (header.pointer_to_symbol_table > 0) { | 695 | if (header.pointer_to_symbol_table > 0) { |
| 607 | fr.seekTo(file_location + header.pointer_to_symbol_table) catch |err| | 696 | fr.seekTo(file_location + header.pointer_to_symbol_table) catch |err| |
| 608 | return failParse(opts, "unable to seek to symbol table: {t}", .{err}); | 697 | return failParse(opts, "unable to seek to symbol table: {t}", .{err}); |
| 609 | | 698 | |
| 610 | try w.writeAll( | 699 | try w.print( |
| 611 | \\Symbol Table: | 700 | \\Symbols ({s}): |
| 612 | \\ Ord Value Sect Type Storage Name | 701 | \\ Ord Value Sect Type Storage Name |
| 613 | \\ | 702 | \\ |
| 614 | ); | 703 | , .{obj_name}); |
| 615 | | 704 | |
| 616 | const symbol_size = std.coff.Symbol.sizeOf(); | 705 | const symbol_size = std.coff.Symbol.sizeOf(); |
| 617 | var symbol_i: u32 = 0; | 706 | var symbol_i: u32 = 0; |
| ... | @@ -633,10 +722,17 @@ const coff = struct { | ... | @@ -633,10 +722,17 @@ const coff = struct { |
| 633 | const name = std.mem.sliceTo(if (std.mem.eql(u8, symbol.name[0..4], "\x00\x00\x00\x00")) name: { | 722 | const name = std.mem.sliceTo(if (std.mem.eql(u8, symbol.name[0..4], "\x00\x00\x00\x00")) name: { |
| 634 | const index = std.mem.readInt(u32, symbol.name[4..], .little); | 723 | const index = std.mem.readInt(u32, symbol.name[4..], .little); |
| 635 | if (index >= string_table.len) | 724 | if (index >= string_table.len) |
| 636 | return failParse(opts, "invalid name offset for symbol {x} ({x} >= {x})", .{ symbol_i, index, string_table.len }); | 725 | return failParse(opts, "invalid name offset for symbol {x} ({x} >= {x})", .{ |
| | 726 | symbol_i, |
| | 727 | index, |
| | 728 | string_table.len, |
| | 729 | }); |
| 637 | break :name string_table[index..]; | 730 | break :name string_table[index..]; |
| 638 | } else &symbol.name, 0); | 731 | } else &symbol.name, 0); |
| 639 | | 732 | |
| | 733 | if (opts.relocs) |
| | 734 | symbol_names.appendNTimesAssumeCapacity(name, 1 + symbol.number_of_aux_symbols); |
| | 735 | |
| 640 | try w.print("{x: >4} {x:0>8} ", .{ symbol_i, symbol.value }); | 736 | try w.print("{x: >4} {x:0>8} ", .{ symbol_i, symbol.value }); |
| 641 | try switch (symbol.section_number) { | 737 | try switch (symbol.section_number) { |
| 642 | .UNDEFINED => w.writeAll("UNDEF"), | 738 | .UNDEFINED => w.writeAll("UNDEF"), |
| ... | @@ -743,18 +839,18 @@ const coff = struct { | ... | @@ -743,18 +839,18 @@ const coff = struct { |
| 743 | std.mem.byteSwapAllFields(std.coff.SectionDefinition, &section_def); | 839 | std.mem.byteSwapAllFields(std.coff.SectionDefinition, &section_def); |
| 744 | | 840 | |
| 745 | const section = &sections.items[section_i]; | 841 | const section = &sections.items[section_i]; |
| 746 | if (section_def.number_of_relocations != section.number_of_relocations) { | 842 | if (section_def.number_of_relocations != section.header.number_of_relocations) { |
| 747 | try w.print( | 843 | try w.print( |
| 748 | " !! relocation count did not match section header: {d} vs {d}", | 844 | " !! relocation count did not match section header: {d} vs {d}", |
| 749 | .{ section_def.number_of_relocations, section.number_of_relocations }, | 845 | .{ section_def.number_of_relocations, section.header.number_of_relocations }, |
| 750 | ); | 846 | ); |
| 751 | continue; | 847 | continue; |
| 752 | } | 848 | } |
| 753 | | 849 | |
| 754 | if (section_def.number_of_linenumbers != section.number_of_linenumbers) { | 850 | if (section_def.number_of_linenumbers != section.header.number_of_linenumbers) { |
| 755 | try w.print( | 851 | try w.print( |
| 756 | " !! line number count did not match section header: {d} vs {d}", | 852 | " !! line number count did not match section header: {d} vs {d}", |
| 757 | .{ section_def.number_of_linenumbers, section.number_of_linenumbers }, | 853 | .{ section_def.number_of_linenumbers, section.header.number_of_linenumbers }, |
| 758 | ); | 854 | ); |
| 759 | continue; | 855 | continue; |
| 760 | } | 856 | } |
| ... | @@ -786,6 +882,52 @@ const coff = struct { | ... | @@ -786,6 +882,52 @@ const coff = struct { |
| 786 | try w.writeAll("No symbol table found\n"); | 882 | try w.writeAll("No symbol table found\n"); |
| 787 | } | 883 | } |
| 788 | } | 884 | } |
| | 885 | |
| | 886 | if (opts.relocs) { |
| | 887 | const relocation_size = std.coff.Relocation.sizeOf(); |
| | 888 | |
| | 889 | for (sections.items, 0..) |section, section_i| { |
| | 890 | if (section.header.pointer_to_relocations == 0) continue; |
| | 891 | |
| | 892 | try w.print( |
| | 893 | \\Relocs for section {x} '{s}' in {s}: |
| | 894 | \\ Offset Type Symbol Name |
| | 895 | \\ |
| | 896 | , .{ section_i + 1, section.name, obj_name }); |
| | 897 | |
| | 898 | fr.seekTo(file_location + section.header.pointer_to_relocations) catch |err| |
| | 899 | return failParse(opts, "unable to seek to section {x} relocation table: {t}", .{ section_i + 1, err }); |
| | 900 | |
| | 901 | for (0..section.header.number_of_relocations) |reloc_i| { |
| | 902 | var reloc: std.coff.Relocation = undefined; |
| | 903 | @memcpy(std.mem.asBytes(&reloc)[0..relocation_size], try r.take(relocation_size)); |
| | 904 | if (native_endian != .little) |
| | 905 | std.mem.byteSwapAllFields(std.coff.Relocation, &reloc); |
| | 906 | |
| | 907 | try w.print("{x:0>8} ", .{reloc.virtual_address}); |
| | 908 | switch (header.machine) { |
| | 909 | _ => unreachable, |
| | 910 | inline else => |m| switch (m.RelocationType()) { |
| | 911 | void => try w.writeAll("(unknown arch)"), |
| | 912 | else => |RelocationType| try w.print( |
| | 913 | "{t: <17} ", |
| | 914 | .{@as(RelocationType, @enumFromInt(reloc.type))}, |
| | 915 | ), |
| | 916 | }, |
| | 917 | } |
| | 918 | |
| | 919 | if (reloc.symbol_table_index >= symbol_names.items.len) |
| | 920 | return failParse( |
| | 921 | opts, |
| | 922 | "reloc {x} in section {x} has out-of-bounds symbol index {x}", |
| | 923 | .{ reloc_i, section_i + 1, reloc.symbol_table_index }, |
| | 924 | ); |
| | 925 | |
| | 926 | try w.print("{x: >8} | {s}\n", .{ reloc.symbol_table_index, symbol_names.items[reloc.symbol_table_index] }); |
| | 927 | } |
| | 928 | try w.writeByte('\n'); |
| | 929 | } |
| | 930 | } |
| 789 | } | 931 | } |
| 790 | | 932 | |
| 791 | fn headerName(raw: *const [8]u8, string_table: []const u8) ![]const u8 { | 933 | fn headerName(raw: *const [8]u8, string_table: []const u8) ![]const u8 { |
| ... | @@ -903,12 +1045,14 @@ const usage = | ... | @@ -903,12 +1045,14 @@ const usage = |
| 903 | \\ -h, --help Print this help and exit | 1045 | \\ -h, --help Print this help and exit |
| 904 | \\ --all-headers Alias for --file-headers --member-headers --section-headers --relocs --symbols | 1046 | \\ --all-headers Alias for --file-headers --member-headers --section-headers --relocs --symbols |
| 905 | \\ --file-headers Display file-format specific headers | 1047 | \\ --file-headers Display file-format specific headers |
| 906 | \\ --linker-member[=1|2] (Coff) Display contents of the specified linker member (default 2) | 1048 | \\ --imports Display imported symbols |
| | 1049 | \\ --exports Display exported symbols |
| | 1050 | \\ --linker-member[=1|2|longnames] (Coff) Display contents of the specified linker member (default 2) |
| 907 | \\ --member-headers Display archive member headers | 1051 | \\ --member-headers Display archive member headers |
| 908 | \\ --only-member=[name] Only consider archive members that contain [name]. Can be specified multiple times. | 1052 | \\ --only-member=[name] Only consider archive members that contain [name]. Can be specified multiple times. |
| 909 | \\ --only-section=[name] Only consider sections that contain [name]. Can be specified multiple times. | 1053 | \\ --only-section=[name] Only consider sections that contain [name]. Can be specified multiple times. |
| 910 | \\ --section-headers Display section headers | 1054 | \\ --section-headers Display section headers |
| 911 | \\ --strings Display string table | 1055 | \\ --strings Display string tables |
| 912 | \\ --symbols Display symbol tables | 1056 | \\ --symbols Display symbol tables |
| 913 | \\ --relocs Display relocations | 1057 | \\ --relocs Display relocations |
| 914 | ; | 1058 | ; |