| ... | @@ -21,6 +21,7 @@ const Options = struct { | ... | @@ -21,6 +21,7 @@ const Options = struct { |
| 21 | section_headers: bool, | 21 | section_headers: bool, |
| 22 | strings: bool, | 22 | strings: bool, |
| 23 | symbols: bool, | 23 | symbols: bool, |
| | 24 | tls: bool, |
| 24 | | 25 | |
| 25 | // Coff-specific | 26 | // Coff-specific |
| 26 | linker_member: ?std.coff.ArchiveMemberHeader.Kind, | 27 | linker_member: ?std.coff.ArchiveMemberHeader.Kind, |
| ... | @@ -43,6 +44,7 @@ pub fn main(init: std.process.Init) !void { | ... | @@ -43,6 +44,7 @@ pub fn main(init: std.process.Init) !void { |
| 43 | var opt_section_headers: ?bool = null; | 44 | var opt_section_headers: ?bool = null; |
| 44 | var opt_strings: ?bool = null; | 45 | var opt_strings: ?bool = null; |
| 45 | var opt_symbols: ?bool = null; | 46 | var opt_symbols: ?bool = null; |
| | 47 | var opt_tls: ?bool = null; |
| 46 | var section_filters: std.ArrayList([]const u8) = .empty; | 48 | var section_filters: std.ArrayList([]const u8) = .empty; |
| 47 | var member_filters: std.ArrayList([]const u8) = .empty; | 49 | var member_filters: std.ArrayList([]const u8) = .empty; |
| 48 | while (i < args.len) : (i += 1) { | 50 | while (i < args.len) : (i += 1) { |
| ... | @@ -83,6 +85,8 @@ pub fn main(init: std.process.Init) !void { | ... | @@ -83,6 +85,8 @@ pub fn main(init: std.process.Init) !void { |
| 83 | opt_strings = true; | 85 | opt_strings = true; |
| 84 | } else if (mem.eql(u8, arg, "--symbols")) { | 86 | } else if (mem.eql(u8, arg, "--symbols")) { |
| 85 | opt_symbols = true; | 87 | opt_symbols = true; |
| | 88 | } else if (mem.eql(u8, arg, "--tls")) { |
| | 89 | opt_tls = true; |
| 86 | } else { | 90 | } else { |
| 87 | fatal("unrecognized argument: {s}", .{arg}); | 91 | fatal("unrecognized argument: {s}", .{arg}); |
| 88 | } | 92 | } |
| ... | @@ -98,14 +102,15 @@ pub fn main(init: std.process.Init) !void { | ... | @@ -98,14 +102,15 @@ pub fn main(init: std.process.Init) !void { |
| 98 | .exports = opt_exports orelse false, | 102 | .exports = opt_exports orelse false, |
| 99 | .file_headers = opt_file_headers orelse false, | 103 | .file_headers = opt_file_headers orelse false, |
| 100 | .imports = opt_imports orelse false, | 104 | .imports = opt_imports orelse false, |
| | 105 | .linker_member = opt_linker_member, |
| | 106 | .member_filters = member_filters.items, |
| | 107 | .member_headers = opt_member_headers orelse false, |
| 101 | .section_filters = section_filters.items, | 108 | .section_filters = section_filters.items, |
| 102 | .section_headers = opt_section_headers orelse false, | 109 | .section_headers = opt_section_headers orelse false, |
| 103 | .relocs = opt_relocs orelse false, | 110 | .relocs = opt_relocs orelse false, |
| 104 | .strings = opt_strings orelse false, | 111 | .strings = opt_strings orelse false, |
| 105 | .symbols = opt_symbols orelse false, | 112 | .symbols = opt_symbols orelse false, |
| 106 | .member_filters = member_filters.items, | 113 | .tls = opt_tls orelse false, |
| 107 | .member_headers = opt_member_headers orelse false, | | |
| 108 | .linker_member = opt_linker_member, | | |
| 109 | }; | 114 | }; |
| 110 | | 115 | |
| 111 | var file = std.Io.Dir.cwd().openFile(io, opts.input_path, .{}) catch |err| | 116 | var file = std.Io.Dir.cwd().openFile(io, opts.input_path, .{}) catch |err| |
| ... | @@ -202,6 +207,21 @@ const wasm = struct { | ... | @@ -202,6 +207,21 @@ const wasm = struct { |
| 202 | }; | 207 | }; |
| 203 | | 208 | |
| 204 | const coff = struct { | 209 | const coff = struct { |
| | 210 | const DIRECTORY_ENTRY = std.coff.IMAGE.DIRECTORY_ENTRY; |
| | 211 | |
| | 212 | const Section = struct { |
| | 213 | header: std.coff.SectionHeader, |
| | 214 | name: []const u8, |
| | 215 | |
| | 216 | fn rvaFileOffset(section: *const Section, rva: u32) !u32 { |
| | 217 | if (rva < section.header.virtual_address or |
| | 218 | rva >= section.header.virtual_address + section.header.size_of_raw_data) |
| | 219 | return error.OutOfBounds; |
| | 220 | |
| | 221 | return section.header.pointer_to_raw_data + (rva - section.header.virtual_address); |
| | 222 | } |
| | 223 | }; |
| | 224 | |
| 205 | const ArchiveHeader = struct { | 225 | const ArchiveHeader = struct { |
| 206 | name: []const u8, | 226 | name: []const u8, |
| 207 | date: u40, | 227 | date: u40, |
| ... | @@ -308,8 +328,6 @@ const coff = struct { | ... | @@ -308,8 +328,6 @@ const coff = struct { |
| 308 | if (!std.mem.eql(u8, header.name, "/")) | 328 | if (!std.mem.eql(u8, header.name, "/")) |
| 309 | return failParse(opts, "expected second linker member, found '{s}'", .{header.name}); | 329 | return failParse(opts, "expected second linker member, found '{s}'", .{header.name}); |
| 310 | | 330 | |
| 311 | // TODO: Figure out what endianness is actually used, there are no headers to say yet? | | |
| 312 | | | |
| 313 | const num_members = try r.takeInt(u32, .little); | 331 | const num_members = try r.takeInt(u32, .little); |
| 314 | pos = fr.logicalPos(); | 332 | pos = fr.logicalPos(); |
| 315 | if (pos + num_members * @sizeOf(u32) > member_end) | 333 | if (pos + num_members * @sizeOf(u32) > member_end) |
| ... | @@ -511,16 +529,22 @@ const coff = struct { | ... | @@ -511,16 +529,22 @@ const coff = struct { |
| 511 | else => {}, | 529 | else => {}, |
| 512 | } | 530 | } |
| 513 | | 531 | |
| 514 | if (header.size_of_optional_header > 0) opt_header: { | 532 | var known_dirs: [DIRECTORY_ENTRY.len]std.coff.ImageDataDirectory = undefined; |
| 515 | if (!opts.file_headers) { | 533 | const needs_data_dirs = |
| | 534 | opts.exports or |
| | 535 | opts.imports or |
| | 536 | opts.tls; |
| | 537 | |
| | 538 | const data_dirs = if (header.size_of_optional_header > 0) data_dirs: { |
| | 539 | if (!opts.file_headers and !needs_data_dirs) { |
| 516 | try fr.seekBy(header.size_of_optional_header); | 540 | try fr.seekBy(header.size_of_optional_header); |
| 517 | break :opt_header; | 541 | break :data_dirs &.{}; |
| 518 | } | 542 | } |
| 519 | | 543 | |
| 520 | try w.writeAll("COFF Optional Header:\n"); | 544 | if (opts.file_headers) try w.writeAll("COFF Optional Header:\n"); |
| 521 | const magic: std.coff.OptionalHeader.Magic = @enumFromInt(try r.peekInt(u16, .little)); | 545 | const magic: std.coff.OptionalHeader.Magic = @enumFromInt(try r.peekInt(u16, .little)); |
| 522 | const num_directory_entries = switch (magic) { | 546 | const num_directory_entries = switch (magic) { |
| 523 | inline .PE32, .@"PE32+" => |v| data_dirs: { | 547 | inline .PE32, .@"PE32+" => |v| num_data_dirs: { |
| 524 | const OptionalHeader = if (v == .PE32) | 548 | const OptionalHeader = if (v == .PE32) |
| 525 | std.coff.OptionalHeader.PE32 | 549 | std.coff.OptionalHeader.PE32 |
| 526 | else | 550 | else |
| ... | @@ -529,63 +553,71 @@ const coff = struct { | ... | @@ -529,63 +553,71 @@ const coff = struct { |
| 529 | const optional_header = r.takeStruct(OptionalHeader, .little) catch |err| | 553 | const optional_header = r.takeStruct(OptionalHeader, .little) catch |err| |
| 530 | return failParse(opts, "unable to read optional header: {t}", .{err}); | 554 | return failParse(opts, "unable to read optional header: {t}", .{err}); |
| 531 | | 555 | |
| 532 | try dumpHeader(w, OptionalHeader, &optional_header, struct { | 556 | if (opts.file_headers) { |
| 533 | pub fn base_of_code(h: *const std.coff.OptionalHeader, cw: *Io.Writer) !void { | 557 | try dumpHeader(w, OptionalHeader, &optional_header, struct { |
| 534 | const base = @as(*const OptionalHeader, @ptrCast(@alignCast(h))).image_base; | 558 | pub fn base_of_code(h: *const std.coff.OptionalHeader, cw: *Io.Writer) !void { |
| 535 | try dumpRvaField(cw, @src().fn_name, h.base_of_code, base); | 559 | const base = @as(*const OptionalHeader, @ptrCast(@alignCast(h))).image_base; |
| 536 | } | 560 | try dumpRvaField(cw, @src().fn_name, h.base_of_code, base); |
| | 561 | } |
| 537 | | 562 | |
| 538 | pub fn address_of_entry_point(h: *const std.coff.OptionalHeader, cw: *Io.Writer) !void { | 563 | pub fn address_of_entry_point(h: *const std.coff.OptionalHeader, cw: *Io.Writer) !void { |
| 539 | const base = @as(*const OptionalHeader, @ptrCast(@alignCast(h))).image_base; | 564 | const base = @as(*const OptionalHeader, @ptrCast(@alignCast(h))).image_base; |
| 540 | try dumpRvaField(cw, @src().fn_name, h.base_of_code, base); | 565 | try dumpRvaField(cw, @src().fn_name, h.base_of_code, base); |
| 541 | } | 566 | } |
| 542 | | 567 | |
| 543 | pub fn major_linker_version(h: *const std.coff.OptionalHeader, cw: *Io.Writer) !void { | 568 | pub fn major_linker_version(h: *const std.coff.OptionalHeader, cw: *Io.Writer) !void { |
| 544 | try dumpVersionField(cw, "linker_version", h.major_linker_version, h.minor_linker_version); | 569 | try dumpVersionField(cw, "linker_version", h.major_linker_version, h.minor_linker_version); |
| 545 | } | 570 | } |
| 546 | pub fn minor_linker_version(_: *const std.coff.OptionalHeader, _: *Io.Writer) !void {} | 571 | pub fn minor_linker_version(_: *const std.coff.OptionalHeader, _: *Io.Writer) !void {} |
| 547 | | 572 | |
| 548 | pub fn major_operating_system_version(h: *const OptionalHeader, cw: *Io.Writer) !void { | 573 | pub fn major_operating_system_version(h: *const OptionalHeader, cw: *Io.Writer) !void { |
| 549 | try dumpVersionField( | 574 | try dumpVersionField( |
| 550 | cw, | 575 | cw, |
| 551 | "operating_system_version", | 576 | "operating_system_version", |
| 552 | h.major_operating_system_version, | 577 | h.major_operating_system_version, |
| 553 | h.minor_operating_system_version, | 578 | h.minor_operating_system_version, |
| 554 | ); | 579 | ); |
| 555 | } | 580 | } |
| 556 | pub fn minor_operating_system_version(_: *const OptionalHeader, _: *Io.Writer) !void {} | 581 | pub fn minor_operating_system_version(_: *const OptionalHeader, _: *Io.Writer) !void {} |
| 557 | | 582 | |
| 558 | pub fn major_image_version(h: *const OptionalHeader, cw: *Io.Writer) !void { | 583 | pub fn major_image_version(h: *const OptionalHeader, cw: *Io.Writer) !void { |
| 559 | try dumpVersionField(cw, "image_version", h.major_image_version, h.minor_image_version); | 584 | try dumpVersionField(cw, "image_version", h.major_image_version, h.minor_image_version); |
| 560 | } | 585 | } |
| 561 | pub fn minor_image_version(_: *const OptionalHeader, _: *Io.Writer) !void {} | 586 | pub fn minor_image_version(_: *const OptionalHeader, _: *Io.Writer) !void {} |
| 562 | | 587 | |
| 563 | pub fn major_subsystem_version(h: *const OptionalHeader, cw: *Io.Writer) !void { | 588 | pub fn major_subsystem_version(h: *const OptionalHeader, cw: *Io.Writer) !void { |
| 564 | try dumpVersionField(cw, "subsystem_version", h.major_subsystem_version, h.minor_subsystem_version); | 589 | try dumpVersionField(cw, "subsystem_version", h.major_subsystem_version, h.minor_subsystem_version); |
| 565 | } | 590 | } |
| 566 | pub fn minor_subsystem_version(_: *const OptionalHeader, _: *Io.Writer) !void {} | 591 | pub fn minor_subsystem_version(_: *const OptionalHeader, _: *Io.Writer) !void {} |
| 567 | }); | 592 | }); |
| 568 | try w.writeByte('\n'); | 593 | try w.writeByte('\n'); |
| | 594 | } |
| 569 | | 595 | |
| 570 | break :data_dirs optional_header.number_of_rva_and_sizes; | 596 | break :num_data_dirs optional_header.number_of_rva_and_sizes; |
| 571 | }, | 597 | }, |
| 572 | else => return failParse(opts, "invalid optional header magic number: {x}", .{magic}), | 598 | else => return failParse(opts, "invalid optional header magic number: {x}", .{magic}), |
| 573 | }; | 599 | }; |
| 574 | | 600 | |
| 575 | try w.writeAll("Data Directories:\n"); | 601 | if (opts.file_headers) try w.writeAll("Data Directories:\n"); |
| 576 | for (0..num_directory_entries) |dir_i| { | 602 | for (0..num_directory_entries) |dir_i| { |
| 577 | const dir = r.takeStruct(std.coff.ImageDataDirectory, .little) catch |err| | 603 | const dir = r.takeStruct(std.coff.ImageDataDirectory, .little) catch |err| |
| 578 | return failParse(opts, "unable to read data directory {x}: {t}", .{ dir_i, err }); | 604 | return failParse(opts, "unable to read data directory {x}: {t}", .{ dir_i, err }); |
| 579 | | 605 | |
| 580 | try w.print( | 606 | if (dir_i < known_dirs.len) |
| 581 | "{x: >16} {x: >8} {t}\n", | 607 | known_dirs[dir_i] = dir; |
| 582 | .{ dir.virtual_address, dir.size, @as(std.coff.IMAGE.DIRECTORY_ENTRY, @enumFromInt(dir_i)) }, | 608 | |
| 583 | ); | 609 | if (opts.file_headers) |
| | 610 | try w.print( |
| | 611 | "{x: >16} {x: >8} {t}\n", |
| | 612 | .{ dir.virtual_address, dir.size, @as(DIRECTORY_ENTRY, @enumFromInt(dir_i)) }, |
| | 613 | ); |
| 584 | } | 614 | } |
| 585 | try w.writeByte('\n'); | 615 | if (opts.file_headers) try w.writeByte('\n'); |
| | 616 | |
| | 617 | break :data_dirs known_dirs[0..@min(known_dirs.len, num_directory_entries)]; |
| 586 | } else if (is_image) { | 618 | } else if (is_image) { |
| 587 | return failParse(opts, "image did not contain an optional header", .{}); | 619 | return failParse(opts, "image did not contain an optional header", .{}); |
| 588 | } | 620 | } else &.{}; |
| 589 | | 621 | |
| 590 | // Section names in images don't use the string table, as they must fit inline in the header | 622 | // Section names in images don't use the string table, as they must fit inline in the header |
| 591 | const load_string_table = (opts.strings or !is_image) and header.pointer_to_symbol_table > 0; | 623 | const load_string_table = (opts.strings or !is_image) and header.pointer_to_symbol_table > 0; |
| ... | @@ -620,16 +652,15 @@ const coff = struct { | ... | @@ -620,16 +652,15 @@ const coff = struct { |
| 620 | try w.writeByte('\n'); | 652 | try w.writeByte('\n'); |
| 621 | } | 653 | } |
| 622 | | 654 | |
| 623 | var sections: std.ArrayList(struct { | 655 | var sections: std.ArrayList(Section) = .empty; |
| 624 | header: std.coff.SectionHeader, | | |
| 625 | name: []const u8, | | |
| 626 | }) = .empty; | | |
| 627 | defer sections.deinit(gpa); | 656 | defer sections.deinit(gpa); |
| | 657 | var sections_with_data: u16 = 0; |
| 628 | | 658 | |
| 629 | const load_sections = | 659 | const load_sections = |
| 630 | opts.section_headers or | 660 | opts.section_headers or |
| 631 | opts.symbols or | 661 | opts.symbols or |
| 632 | opts.relocs; | 662 | opts.relocs or |
| | 663 | needs_data_dirs; |
| 633 | | 664 | |
| 634 | if (load_sections) { | 665 | if (load_sections) { |
| 635 | if (opts.section_headers) | 666 | if (opts.section_headers) |
| ... | @@ -656,11 +687,12 @@ const coff = struct { | ... | @@ -656,11 +687,12 @@ const coff = struct { |
| 656 | }), | 687 | }), |
| 657 | }; | 688 | }; |
| 658 | | 689 | |
| | 690 | sections_with_data += @intFromBool(section.header.size_of_raw_data > 0); |
| 659 | if (opts.section_headers) { | 691 | if (opts.section_headers) { |
| 660 | if (!filterMatches(opts.section_filters, section.name)) continue; | 692 | if (!filterMatches(opts.section_filters, section.name)) continue; |
| 661 | const raw_name = std.mem.sliceTo(&section.header.name, 0); | 693 | const raw_name = std.mem.sliceTo(&section.header.name, 0); |
| 662 | try w.print( | 694 | try w.print( |
| 663 | "{x: >3} {s: <8} {x: >8} {x: >9} {x: >9} {x: >8} {x: >8} {x: >8} {x: >8} {x: >8} {x:0>8} | ", | 695 | "{x: >3} {s: <8} {x: >8} {x: >9} {x: >9} {x: >8} {x: >8} {x: >8} {x: >8} {x: >8} {x:0>8} |", |
| 664 | .{ | 696 | .{ |
| 665 | section_i + 1, | 697 | section_i + 1, |
| 666 | raw_name, | 698 | raw_name, |
| ... | @@ -676,7 +708,7 @@ const coff = struct { | ... | @@ -676,7 +708,7 @@ const coff = struct { |
| 676 | }, | 708 | }, |
| 677 | ); | 709 | ); |
| 678 | | 710 | |
| 679 | try dumpFlags(w, "{s} ", std.coff.SectionHeader.Flags, &section.header.flags, 1); | 711 | try dumpFlags(w, "{s}", std.coff.SectionHeader.Flags, &section.header.flags, 1); |
| 680 | if (section.name.len > 8) | 712 | if (section.name.len > 8) |
| 681 | try w.print("| {s}", .{section.name}); | 713 | try w.print("| {s}", .{section.name}); |
| 682 | | 714 | |
| ... | @@ -790,10 +822,7 @@ const coff = struct { | ... | @@ -790,10 +822,7 @@ const coff = struct { |
| 790 | (std.mem.eql(u8, name, ".bf") or std.mem.eql(u8, name, ".ef"))) | 822 | (std.mem.eql(u8, name, ".bf") or std.mem.eql(u8, name, ".ef"))) |
| 791 | { | 823 | { |
| 792 | try w.writeAll("TODO bf / ef aux symbol"); | 824 | try w.writeAll("TODO bf / ef aux symbol"); |
| 793 | } else if (symbol.storage_class == .EXTERNAL and | 825 | } else if (symbol.storage_class == .WEAK_EXTERNAL and symbol.section_number == .UNDEFINED) { |
| 794 | symbol.section_number == .UNDEFINED and | | |
| 795 | symbol.value == 0) | | |
| 796 | { | | |
| 797 | if (symbol.value != 0) | 826 | if (symbol.value != 0) |
| 798 | return failParse( | 827 | return failParse( |
| 799 | opts, | 828 | opts, |
| ... | @@ -815,6 +844,10 @@ const coff = struct { | ... | @@ -815,6 +844,10 @@ const coff = struct { |
| 815 | | 844 | |
| 816 | // TODO | 845 | // TODO |
| 817 | | 846 | |
| | 847 | try w.print(" Weak External [falls back to {x:0>8} via {t}]", .{ |
| | 848 | weak_external.tag_index, |
| | 849 | weak_external.flag, |
| | 850 | }); |
| 818 | } else if (symbol.storage_class == .FILE) { | 851 | } else if (symbol.storage_class == .FILE) { |
| 819 | if (!std.mem.eql(u8, name, ".file")) { | 852 | if (!std.mem.eql(u8, name, ".file")) { |
| 820 | try w.print(" !! unexpected symbol name '{s}' for file symbol 0x{x}", .{ name, symbol_i }); | 853 | try w.print(" !! unexpected symbol name '{s}' for file symbol 0x{x}", .{ name, symbol_i }); |
| ... | @@ -824,6 +857,7 @@ const coff = struct { | ... | @@ -824,6 +857,7 @@ const coff = struct { |
| 824 | var file: std.coff.FileDefinition = undefined; | 857 | var file: std.coff.FileDefinition = undefined; |
| 825 | @memcpy(std.mem.asBytes(&file)[0..symbol_size], aux_symbols[0..symbol_size]); | 858 | @memcpy(std.mem.asBytes(&file)[0..symbol_size], aux_symbols[0..symbol_size]); |
| 826 | | 859 | |
| | 860 | // TODO |
| 827 | _ = file.getFileName(); | 861 | _ = file.getFileName(); |
| 828 | } else if (symbol.storage_class == .STATIC and | 862 | } else if (symbol.storage_class == .STATIC and |
| 829 | symbol.type == std.coff.SymType{ | 863 | symbol.type == std.coff.SymType{ |
| ... | @@ -934,16 +968,205 @@ const coff = struct { | ... | @@ -934,16 +968,205 @@ const coff = struct { |
| 934 | .{ reloc_i, section_i + 1, reloc.symbol_table_index }, | 968 | .{ reloc_i, section_i + 1, reloc.symbol_table_index }, |
| 935 | ); | 969 | ); |
| 936 | | 970 | |
| 937 | try w.print("{x: >8} ", .{reloc.symbol_table_index}); | | |
| 938 | | | |
| 939 | const sym = &symbols.items[reloc.symbol_table_index]; | 971 | const sym = &symbols.items[reloc.symbol_table_index]; |
| 940 | try sectionNumberString(sym.section_number, w); | 972 | try w.print("{x: >8} {f} | {s}\n", .{ |
| 941 | | 973 | reloc.symbol_table_index, |
| 942 | try w.print(" | {s}\n", .{sym.name}); | 974 | fmtSectionNumber(sym.section_number), |
| | 975 | sym.name, |
| | 976 | }); |
| 943 | } | 977 | } |
| 944 | try w.writeByte('\n'); | 978 | try w.writeByte('\n'); |
| 945 | } | 979 | } |
| 946 | } | 980 | } |
| | 981 | |
| | 982 | // Sections indices with raw data, sorted by RVA |
| | 983 | const rva_index = if (needs_data_dirs) rva_index: { |
| | 984 | const rva_index = try gpa.alloc(u16, sections_with_data); |
| | 985 | var indices_i: u16 = 0; |
| | 986 | for (sections.items, 0..) |*section, i| { |
| | 987 | if (section.header.size_of_raw_data == 0) continue; |
| | 988 | rva_index[indices_i] = @intCast(i); |
| | 989 | indices_i += 1; |
| | 990 | } |
| | 991 | |
| | 992 | const Context = struct { |
| | 993 | indices: []u16, |
| | 994 | sections: []const Section, |
| | 995 | |
| | 996 | pub fn lessThan(ctx: @This(), lhs: usize, rhs: usize) bool { |
| | 997 | return ctx.sections[ctx.indices[lhs]].header.virtual_address < |
| | 998 | ctx.sections[ctx.indices[rhs]].header.virtual_address; |
| | 999 | } |
| | 1000 | |
| | 1001 | pub fn swap(ctx: @This(), lhs: usize, rhs: usize) void { |
| | 1002 | std.mem.swap(u16, &ctx.indices[lhs], &ctx.indices[rhs]); |
| | 1003 | } |
| | 1004 | }; |
| | 1005 | |
| | 1006 | std.sort.pdqContext(0, rva_index.len, Context{ |
| | 1007 | .indices = rva_index, |
| | 1008 | .sections = sections.items, |
| | 1009 | }); |
| | 1010 | |
| | 1011 | break :rva_index rva_index; |
| | 1012 | } else &.{}; |
| | 1013 | defer gpa.free(rva_index); |
| | 1014 | |
| | 1015 | if (opts.exports) { |
| | 1016 | if (try seekToDataDirectory(opts, fr, w, rva_index, sections.items, data_dirs, .EXPORT)) |section_index| { |
| | 1017 | const export_dir = r.takeStruct(std.coff.ExportDirectoryTable, .little) catch |err| |
| | 1018 | return failParse(opts, "unable to read export directory: {t}", .{err}); |
| | 1019 | |
| | 1020 | try w.print("Export directory:\n", .{}); |
| | 1021 | try dumpHeader(w, std.coff.ExportDirectoryTable, &export_dir, struct { |
| | 1022 | pub fn major_version(h: *const std.coff.ExportDirectoryTable, cw: *Io.Writer) !void { |
| | 1023 | try dumpVersionField(cw, "version", h.major_version, h.minor_version); |
| | 1024 | } |
| | 1025 | pub fn minor_version(_: *const std.coff.ExportDirectoryTable, _: *Io.Writer) !void {} |
| | 1026 | }); |
| | 1027 | |
| | 1028 | const section = sections.items[section_index]; |
| | 1029 | const name_loc = section.rvaFileOffset(export_dir.name_rva) catch |
| | 1030 | return failParse( |
| | 1031 | opts, |
| | 1032 | "export name rva 0x{x} was not within the export section", |
| | 1033 | .{export_dir.name_rva}, |
| | 1034 | ); |
| | 1035 | |
| | 1036 | const eat_loc = section.rvaFileOffset(export_dir.export_address_table_rva) catch |
| | 1037 | return failParse( |
| | 1038 | opts, |
| | 1039 | "export address table rva 0x{x} was not within the export section", |
| | 1040 | .{export_dir.export_address_table_rva}, |
| | 1041 | ); |
| | 1042 | |
| | 1043 | const name_pointer_loc = section.rvaFileOffset(export_dir.name_pointer_table_rva) catch |
| | 1044 | return failParse( |
| | 1045 | opts, |
| | 1046 | "export name pointer table rva 0x{x} was not within the export section", |
| | 1047 | .{export_dir.name_pointer_table_rva}, |
| | 1048 | ); |
| | 1049 | |
| | 1050 | const ord_loc = section.rvaFileOffset(export_dir.ordinal_table_rva) catch |
| | 1051 | return failParse( |
| | 1052 | opts, |
| | 1053 | "export ordinal table rva 0x{x} was not within the export section", |
| | 1054 | .{export_dir.ordinal_table_rva}, |
| | 1055 | ); |
| | 1056 | |
| | 1057 | // All the variable length fields should be contained within this directory. |
| | 1058 | // Read it entirely to avoid needing to seek per-name when iterating. |
| | 1059 | const dir = data_dirs[@intFromEnum(DIRECTORY_ENTRY.EXPORT)]; |
| | 1060 | const dir_end_rva = dir.virtual_address + dir.size; |
| | 1061 | const dir_loc = fr.logicalPos(); |
| | 1062 | const dir_slice = try r.readAlloc(gpa, dir.size); |
| | 1063 | defer gpa.free(dir_slice); |
| | 1064 | |
| | 1065 | const dll_name = std.mem.sliceTo(dir_slice[name_loc - dir_loc ..], 0); |
| | 1066 | try w.print( |
| | 1067 | \\ |
| | 1068 | \\Exports from {s}: |
| | 1069 | \\ Ord Hint RVA Name |
| | 1070 | \\ |
| | 1071 | , .{dll_name}); |
| | 1072 | |
| | 1073 | const name_pointers = dir_slice[name_pointer_loc - dir_loc ..][0 .. export_dir.number_of_names * @sizeOf(u32)]; |
| | 1074 | const ords = dir_slice[ord_loc - dir_loc ..][0 .. export_dir.number_of_names * @sizeOf(u16)]; |
| | 1075 | const addrs = dir_slice[eat_loc - dir_loc ..][0 .. export_dir.number_of_entries * @sizeOf(u32)]; |
| | 1076 | const name_rva_to_offset = dir.virtual_address + @sizeOf(std.coff.ExportDirectoryTable); |
| | 1077 | for (0..export_dir.number_of_names) |name_i| { |
| | 1078 | const name_rva = std.mem.readInt(u32, name_pointers[name_i * @sizeOf(u32) ..][0..@sizeOf(u32)], .little); |
| | 1079 | const ord = std.mem.readInt(u16, ords[name_i * @sizeOf(u16) ..][0..@sizeOf(u16)], .little); |
| | 1080 | const addr = std.mem.readInt(u32, addrs[@as(u32, ord) * @sizeOf(u32) ..][0..@sizeOf(u32)], .little); |
| | 1081 | |
| | 1082 | try w.print("{x: >4} {x: >4} ", .{ export_dir.ordinal_base + ord, name_i }); |
| | 1083 | const is_forwarder = addr >= dir.virtual_address and addr < dir_end_rva; |
| | 1084 | if (is_forwarder) { |
| | 1085 | try w.writeAll("forwards"); |
| | 1086 | } else { |
| | 1087 | try w.print("{x: >8}", .{addr}); |
| | 1088 | } |
| | 1089 | |
| | 1090 | const name = std.mem.sliceTo(dir_slice[name_rva - name_rva_to_offset ..], 0); |
| | 1091 | try w.print(" | {s}", .{name}); |
| | 1092 | if (is_forwarder) |
| | 1093 | try w.print(" -> {s}", .{std.mem.sliceTo(dir_slice[addr - name_rva_to_offset ..], 0)}); |
| | 1094 | try w.writeByte('\n'); |
| | 1095 | } |
| | 1096 | } |
| | 1097 | } |
| | 1098 | |
| | 1099 | if (opts.imports) { |
| | 1100 | if (try seekToDataDirectory(opts, fr, w, rva_index, sections.items, data_dirs, .IMPORT)) |_| { |
| | 1101 | // TODO |
| | 1102 | } |
| | 1103 | } |
| | 1104 | |
| | 1105 | if (opts.tls) { |
| | 1106 | if (try seekToDataDirectory(opts, fr, w, rva_index, sections.items, data_dirs, .TLS)) |_| { |
| | 1107 | // TODO |
| | 1108 | } |
| | 1109 | } |
| | 1110 | } |
| | 1111 | |
| | 1112 | fn seekToDataDirectory( |
| | 1113 | opts: *const Options, |
| | 1114 | fr: *Io.File.Reader, |
| | 1115 | w: *Io.Writer, |
| | 1116 | rva_index: []const u16, |
| | 1117 | sections: []const Section, |
| | 1118 | data_dirs: []const std.coff.ImageDataDirectory, |
| | 1119 | entry: DIRECTORY_ENTRY, |
| | 1120 | ) !?u16 { |
| | 1121 | if (@intFromEnum(entry) < data_dirs.len) { |
| | 1122 | const rva = data_dirs[@intFromEnum(entry)].virtual_address; |
| | 1123 | const section_index = sectionContainingRva(rva_index, sections, rva) orelse |
| | 1124 | return failParse( |
| | 1125 | opts, |
| | 1126 | "{t} directory rva 0x{x} was not found in any section", |
| | 1127 | .{ entry, rva }, |
| | 1128 | ); |
| | 1129 | |
| | 1130 | const file_offset = sections[section_index].rvaFileOffset(rva) catch unreachable; |
| | 1131 | fr.seekTo(file_offset) catch |err| |
| | 1132 | return failParse( |
| | 1133 | opts, |
| | 1134 | "unable to seek to {t} directory at offset 0x{x}: {t}", |
| | 1135 | .{ entry, file_offset, err }, |
| | 1136 | ); |
| | 1137 | |
| | 1138 | return section_index; |
| | 1139 | } |
| | 1140 | |
| | 1141 | try w.print("{t} directory was not present in optional header\n", .{entry}); |
| | 1142 | return null; |
| | 1143 | } |
| | 1144 | |
| | 1145 | fn sectionContainingRva( |
| | 1146 | /// Indices into `sections` sorted by rva |
| | 1147 | indices: []const u16, |
| | 1148 | sections: []const Section, |
| | 1149 | rva: u32, |
| | 1150 | ) ?u16 { |
| | 1151 | const Context = struct { |
| | 1152 | rva: u32, |
| | 1153 | sections: []const Section, |
| | 1154 | |
| | 1155 | fn order(ctx: @This(), section_index: u16) std.math.Order { |
| | 1156 | const h = &ctx.sections[section_index].header; |
| | 1157 | const start = h.virtual_address; |
| | 1158 | if (ctx.rva < start) return .lt; |
| | 1159 | const end = h.virtual_address + h.size_of_raw_data; |
| | 1160 | if (ctx.rva >= end) return .gt; |
| | 1161 | return .eq; |
| | 1162 | } |
| | 1163 | }; |
| | 1164 | |
| | 1165 | const index = std.sort.binarySearch(u16, indices, Context{ |
| | 1166 | .rva = rva, |
| | 1167 | .sections = sections, |
| | 1168 | }, Context.order) orelse return null; |
| | 1169 | return @intCast(index); |
| 947 | } | 1170 | } |
| 948 | | 1171 | |
| 949 | fn headerName(raw: *const [8]u8, string_table: []const u8) ![]const u8 { | 1172 | fn headerName(raw: *const [8]u8, string_table: []const u8) ![]const u8 { |
| ... | @@ -956,21 +1179,6 @@ const coff = struct { | ... | @@ -956,21 +1179,6 @@ const coff = struct { |
| 956 | } else std.mem.sliceTo(raw, 0); | 1179 | } else std.mem.sliceTo(raw, 0); |
| 957 | } | 1180 | } |
| 958 | | 1181 | |
| 959 | fn fmtSymbolType(sym_type: std.coff.SymType) std.fmt.Alt(std.coff.SymType, symbolTypeString) { | | |
| 960 | return .{ .data = sym_type }; | | |
| 961 | } | | |
| 962 | | | |
| 963 | fn symbolTypeString(sym_type: std.coff.SymType, w: *std.Io.Writer) std.Io.Writer.Error!void { | | |
| 964 | try w.print("{t: >5}", .{sym_type.base_type}); | | |
| 965 | if (try switch (sym_type.complex_type) { | | |
| 966 | .NULL => " ", | | |
| 967 | .POINTER => "* ", | | |
| 968 | .FUNCTION => "()", | | |
| 969 | .ARRAY => "[]", | | |
| 970 | else => null, | | |
| 971 | }) |suffix| try .printAll(suffix) else w.print("{x}", .{sym_type.complex_type}); | | |
| 972 | } | | |
| 973 | | | |
| 974 | fn fmtSectionNumber(section_number: std.coff.SectionNumber) std.fmt.Alt(std.coff.SectionNumber, sectionNumberString) { | 1182 | fn fmtSectionNumber(section_number: std.coff.SectionNumber) std.fmt.Alt(std.coff.SectionNumber, sectionNumberString) { |
| 975 | return .{ .data = section_number }; | 1183 | return .{ .data = section_number }; |
| 976 | } | 1184 | } |
| ... | @@ -1003,8 +1211,6 @@ const coff = struct { | ... | @@ -1003,8 +1211,6 @@ const coff = struct { |
| 1003 | | 1211 | |
| 1004 | fn dumpArchiveHeader(w: *Io.Writer, header: *const ArchiveHeader, pos: u32) !void { | 1212 | fn dumpArchiveHeader(w: *Io.Writer, header: *const ArchiveHeader, pos: u32) !void { |
| 1005 | try w.print("Archive member at offset 0x{x}: '{s}'\n", .{ pos, header.name }); | 1213 | try w.print("Archive member at offset 0x{x}: '{s}'\n", .{ pos, header.name }); |
| 1006 | | | |
| 1007 | // TODO: Date formatter | | |
| 1008 | try dumpHeader(w, ArchiveHeader, header, struct { | 1214 | try dumpHeader(w, ArchiveHeader, header, struct { |
| 1009 | pub fn name(_: *const ArchiveHeader, _: *Io.Writer) !void {} | 1215 | pub fn name(_: *const ArchiveHeader, _: *Io.Writer) !void {} |
| 1010 | pub fn file_mode(h: *const ArchiveHeader, cw: *Io.Writer) !void { | 1216 | pub fn file_mode(h: *const ArchiveHeader, cw: *Io.Writer) !void { |
| ... | @@ -1065,10 +1271,11 @@ const usage = | ... | @@ -1065,10 +1271,11 @@ const usage = |
| 1065 | \\ --exports Display exported symbols | 1271 | \\ --exports Display exported symbols |
| 1066 | \\ --linker-member[=1|2|longnames] (Coff) Display contents of the specified linker member (default 2) | 1272 | \\ --linker-member[=1|2|longnames] (Coff) Display contents of the specified linker member (default 2) |
| 1067 | \\ --member-headers Display archive member headers | 1273 | \\ --member-headers Display archive member headers |
| 1068 | \\ --only-member=[name] Only consider archive members that contain [name]. Can be specified multiple times. | 1274 | \\ --only-member=[name] Only consider archive members names that contain [name]. Can be specified multiple times. |
| 1069 | \\ --only-section=[name] Only consider sections that contain [name]. Can be specified multiple times. | 1275 | \\ --only-section=[name] Only consider section names that contain [name]. Can be specified multiple times. |
| | 1276 | \\ --relocs Display relocations |
| 1070 | \\ --section-headers Display section headers | 1277 | \\ --section-headers Display section headers |
| 1071 | \\ --strings Display string tables | 1278 | \\ --strings Display string tables |
| 1072 | \\ --symbols Display symbol tables | 1279 | \\ --symbols Display symbol tables |
| 1073 | \\ --relocs Display relocations | 1280 | \\ --tls Display TLS information |
| 1074 | ; | 1281 | ; |