| ... | ... | @@ -21,6 +21,7 @@ const Options = struct { |
| 21 | 21 | section_headers: bool, |
| 22 | 22 | strings: bool, |
| 23 | 23 | symbols: bool, |
| 24 | tls: bool, |
| 24 | 25 | |
| 25 | 26 | // Coff-specific |
| 26 | 27 | linker_member: ?std.coff.ArchiveMemberHeader.Kind, |
| ... | ... | @@ -43,6 +44,7 @@ pub fn main(init: std.process.Init) !void { |
| 43 | 44 | var opt_section_headers: ?bool = null; |
| 44 | 45 | var opt_strings: ?bool = null; |
| 45 | 46 | var opt_symbols: ?bool = null; |
| 47 | var opt_tls: ?bool = null; |
| 46 | 48 | var section_filters: std.ArrayList([]const u8) = .empty; |
| 47 | 49 | var member_filters: std.ArrayList([]const u8) = .empty; |
| 48 | 50 | while (i < args.len) : (i += 1) { |
| ... | ... | @@ -83,6 +85,8 @@ pub fn main(init: std.process.Init) !void { |
| 83 | 85 | opt_strings = true; |
| 84 | 86 | } else if (mem.eql(u8, arg, "--symbols")) { |
| 85 | 87 | opt_symbols = true; |
| 88 | } else if (mem.eql(u8, arg, "--tls")) { |
| 89 | opt_tls = true; |
| 86 | 90 | } else { |
| 87 | 91 | fatal("unrecognized argument: {s}", .{arg}); |
| 88 | 92 | } |
| ... | ... | @@ -98,14 +102,15 @@ pub fn main(init: std.process.Init) !void { |
| 98 | 102 | .exports = opt_exports orelse false, |
| 99 | 103 | .file_headers = opt_file_headers orelse false, |
| 100 | 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 | 108 | .section_filters = section_filters.items, |
| 102 | 109 | .section_headers = opt_section_headers orelse false, |
| 103 | 110 | .relocs = opt_relocs orelse false, |
| 104 | 111 | .strings = opt_strings orelse false, |
| 105 | 112 | .symbols = opt_symbols orelse false, |
| 106 | | .member_filters = member_filters.items, |
| 107 | | .member_headers = opt_member_headers orelse false, |
| 108 | | .linker_member = opt_linker_member, |
| 113 | .tls = opt_tls orelse false, |
| 109 | 114 | }; |
| 110 | 115 | |
| 111 | 116 | var file = std.Io.Dir.cwd().openFile(io, opts.input_path, .{}) catch |err| |
| ... | ... | @@ -202,6 +207,21 @@ const wasm = struct { |
| 202 | 207 | }; |
| 203 | 208 | |
| 204 | 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 | 225 | const ArchiveHeader = struct { |
| 206 | 226 | name: []const u8, |
| 207 | 227 | date: u40, |
| ... | ... | @@ -308,8 +328,6 @@ const coff = struct { |
| 308 | 328 | if (!std.mem.eql(u8, header.name, "/")) |
| 309 | 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 | 331 | const num_members = try r.takeInt(u32, .little); |
| 314 | 332 | pos = fr.logicalPos(); |
| 315 | 333 | if (pos + num_members * @sizeOf(u32) > member_end) |
| ... | ... | @@ -511,16 +529,22 @@ const coff = struct { |
| 511 | 529 | else => {}, |
| 512 | 530 | } |
| 513 | 531 | |
| 514 | | if (header.size_of_optional_header > 0) opt_header: { |
| 515 | | if (!opts.file_headers) { |
| 532 | var known_dirs: [DIRECTORY_ENTRY.len]std.coff.ImageDataDirectory = undefined; |
| 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 | 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 | 545 | const magic: std.coff.OptionalHeader.Magic = @enumFromInt(try r.peekInt(u16, .little)); |
| 522 | 546 | const num_directory_entries = switch (magic) { |
| 523 | | inline .PE32, .@"PE32+" => |v| data_dirs: { |
| 547 | inline .PE32, .@"PE32+" => |v| num_data_dirs: { |
| 524 | 548 | const OptionalHeader = if (v == .PE32) |
| 525 | 549 | std.coff.OptionalHeader.PE32 |
| 526 | 550 | else |
| ... | ... | @@ -529,63 +553,71 @@ const coff = struct { |
| 529 | 553 | const optional_header = r.takeStruct(OptionalHeader, .little) catch |err| |
| 530 | 554 | return failParse(opts, "unable to read optional header: {t}", .{err}); |
| 531 | 555 | |
| 532 | | try dumpHeader(w, OptionalHeader, &optional_header, struct { |
| 533 | | pub fn base_of_code(h: *const std.coff.OptionalHeader, cw: *Io.Writer) !void { |
| 534 | | const base = @as(*const OptionalHeader, @ptrCast(@alignCast(h))).image_base; |
| 535 | | try dumpRvaField(cw, @src().fn_name, h.base_of_code, base); |
| 536 | | } |
| 556 | if (opts.file_headers) { |
| 557 | try dumpHeader(w, OptionalHeader, &optional_header, struct { |
| 558 | pub fn base_of_code(h: *const std.coff.OptionalHeader, cw: *Io.Writer) !void { |
| 559 | const base = @as(*const OptionalHeader, @ptrCast(@alignCast(h))).image_base; |
| 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 { |
| 539 | | const base = @as(*const OptionalHeader, @ptrCast(@alignCast(h))).image_base; |
| 540 | | try dumpRvaField(cw, @src().fn_name, h.base_of_code, base); |
| 541 | | } |
| 563 | pub fn address_of_entry_point(h: *const std.coff.OptionalHeader, cw: *Io.Writer) !void { |
| 564 | const base = @as(*const OptionalHeader, @ptrCast(@alignCast(h))).image_base; |
| 565 | try dumpRvaField(cw, @src().fn_name, h.base_of_code, base); |
| 566 | } |
| 542 | 567 | |
| 543 | | 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); |
| 545 | | } |
| 546 | | pub fn minor_linker_version(_: *const std.coff.OptionalHeader, _: *Io.Writer) !void {} |
| 547 | | |
| 548 | | pub fn major_operating_system_version(h: *const OptionalHeader, cw: *Io.Writer) !void { |
| 549 | | try dumpVersionField( |
| 550 | | cw, |
| 551 | | "operating_system_version", |
| 552 | | h.major_operating_system_version, |
| 553 | | h.minor_operating_system_version, |
| 554 | | ); |
| 555 | | } |
| 556 | | pub fn minor_operating_system_version(_: *const OptionalHeader, _: *Io.Writer) !void {} |
| 568 | pub fn major_linker_version(h: *const std.coff.OptionalHeader, cw: *Io.Writer) !void { |
| 569 | try dumpVersionField(cw, "linker_version", h.major_linker_version, h.minor_linker_version); |
| 570 | } |
| 571 | pub fn minor_linker_version(_: *const std.coff.OptionalHeader, _: *Io.Writer) !void {} |
| 572 | |
| 573 | pub fn major_operating_system_version(h: *const OptionalHeader, cw: *Io.Writer) !void { |
| 574 | try dumpVersionField( |
| 575 | cw, |
| 576 | "operating_system_version", |
| 577 | h.major_operating_system_version, |
| 578 | h.minor_operating_system_version, |
| 579 | ); |
| 580 | } |
| 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 { |
| 559 | | try dumpVersionField(cw, "image_version", h.major_image_version, h.minor_image_version); |
| 560 | | } |
| 561 | | pub fn minor_image_version(_: *const OptionalHeader, _: *Io.Writer) !void {} |
| 583 | pub fn major_image_version(h: *const OptionalHeader, cw: *Io.Writer) !void { |
| 584 | try dumpVersionField(cw, "image_version", h.major_image_version, h.minor_image_version); |
| 585 | } |
| 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 { |
| 564 | | try dumpVersionField(cw, "subsystem_version", h.major_subsystem_version, h.minor_subsystem_version); |
| 565 | | } |
| 566 | | pub fn minor_subsystem_version(_: *const OptionalHeader, _: *Io.Writer) !void {} |
| 567 | | }); |
| 568 | | try w.writeByte('\n'); |
| 588 | pub fn major_subsystem_version(h: *const OptionalHeader, cw: *Io.Writer) !void { |
| 589 | try dumpVersionField(cw, "subsystem_version", h.major_subsystem_version, h.minor_subsystem_version); |
| 590 | } |
| 591 | pub fn minor_subsystem_version(_: *const OptionalHeader, _: *Io.Writer) !void {} |
| 592 | }); |
| 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 | 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 | 602 | for (0..num_directory_entries) |dir_i| { |
| 577 | 603 | const dir = r.takeStruct(std.coff.ImageDataDirectory, .little) catch |err| |
| 578 | 604 | return failParse(opts, "unable to read data directory {x}: {t}", .{ dir_i, err }); |
| 579 | 605 | |
| 580 | | try w.print( |
| 581 | | "{x: >16} {x: >8} {t}\n", |
| 582 | | .{ dir.virtual_address, dir.size, @as(std.coff.IMAGE.DIRECTORY_ENTRY, @enumFromInt(dir_i)) }, |
| 583 | | ); |
| 606 | if (dir_i < known_dirs.len) |
| 607 | known_dirs[dir_i] = dir; |
| 608 | |
| 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 | 618 | } else if (is_image) { |
| 587 | 619 | return failParse(opts, "image did not contain an optional header", .{}); |
| 588 | | } |
| 620 | } else &.{}; |
| 589 | 621 | |
| 590 | 622 | // Section names in images don't use the string table, as they must fit inline in the header |
| 591 | 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 | 652 | try w.writeByte('\n'); |
| 621 | 653 | } |
| 622 | 654 | |
| 623 | | var sections: std.ArrayList(struct { |
| 624 | | header: std.coff.SectionHeader, |
| 625 | | name: []const u8, |
| 626 | | }) = .empty; |
| 655 | var sections: std.ArrayList(Section) = .empty; |
| 627 | 656 | defer sections.deinit(gpa); |
| 657 | var sections_with_data: u16 = 0; |
| 628 | 658 | |
| 629 | 659 | const load_sections = |
| 630 | 660 | opts.section_headers or |
| 631 | 661 | opts.symbols or |
| 632 | | opts.relocs; |
| 662 | opts.relocs or |
| 663 | needs_data_dirs; |
| 633 | 664 | |
| 634 | 665 | if (load_sections) { |
| 635 | 666 | if (opts.section_headers) |
| ... | ... | @@ -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 | 691 | if (opts.section_headers) { |
| 660 | 692 | if (!filterMatches(opts.section_filters, section.name)) continue; |
| 661 | 693 | const raw_name = std.mem.sliceTo(&section.header.name, 0); |
| 662 | 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 | 697 | section_i + 1, |
| 666 | 698 | raw_name, |
| ... | ... | @@ -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 | 712 | if (section.name.len > 8) |
| 681 | 713 | try w.print("| {s}", .{section.name}); |
| 682 | 714 | |
| ... | ... | @@ -790,10 +822,7 @@ const coff = struct { |
| 790 | 822 | (std.mem.eql(u8, name, ".bf") or std.mem.eql(u8, name, ".ef"))) |
| 791 | 823 | { |
| 792 | 824 | try w.writeAll("TODO bf / ef aux symbol"); |
| 793 | | } else if (symbol.storage_class == .EXTERNAL and |
| 794 | | symbol.section_number == .UNDEFINED and |
| 795 | | symbol.value == 0) |
| 796 | | { |
| 825 | } else if (symbol.storage_class == .WEAK_EXTERNAL and symbol.section_number == .UNDEFINED) { |
| 797 | 826 | if (symbol.value != 0) |
| 798 | 827 | return failParse( |
| 799 | 828 | opts, |
| ... | ... | @@ -815,6 +844,10 @@ const coff = struct { |
| 815 | 844 | |
| 816 | 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 | 851 | } else if (symbol.storage_class == .FILE) { |
| 819 | 852 | if (!std.mem.eql(u8, name, ".file")) { |
| 820 | 853 | try w.print(" !! unexpected symbol name '{s}' for file symbol 0x{x}", .{ name, symbol_i }); |
| ... | ... | @@ -824,6 +857,7 @@ const coff = struct { |
| 824 | 857 | var file: std.coff.FileDefinition = undefined; |
| 825 | 858 | @memcpy(std.mem.asBytes(&file)[0..symbol_size], aux_symbols[0..symbol_size]); |
| 826 | 859 | |
| 860 | // TODO |
| 827 | 861 | _ = file.getFileName(); |
| 828 | 862 | } else if (symbol.storage_class == .STATIC and |
| 829 | 863 | symbol.type == std.coff.SymType{ |
| ... | ... | @@ -934,16 +968,205 @@ const coff = struct { |
| 934 | 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 | 971 | const sym = &symbols.items[reloc.symbol_table_index]; |
| 940 | | try sectionNumberString(sym.section_number, w); |
| 941 | | |
| 942 | | try w.print(" | {s}\n", .{sym.name}); |
| 972 | try w.print("{x: >8} {f} | {s}\n", .{ |
| 973 | reloc.symbol_table_index, |
| 974 | fmtSectionNumber(sym.section_number), |
| 975 | sym.name, |
| 976 | }); |
| 943 | 977 | } |
| 944 | 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 | 1172 | fn headerName(raw: *const [8]u8, string_table: []const u8) ![]const u8 { |
| ... | ... | @@ -956,21 +1179,6 @@ const coff = struct { |
| 956 | 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 | 1182 | fn fmtSectionNumber(section_number: std.coff.SectionNumber) std.fmt.Alt(std.coff.SectionNumber, sectionNumberString) { |
| 975 | 1183 | return .{ .data = section_number }; |
| 976 | 1184 | } |
| ... | ... | @@ -1003,8 +1211,6 @@ const coff = struct { |
| 1003 | 1211 | |
| 1004 | 1212 | fn dumpArchiveHeader(w: *Io.Writer, header: *const ArchiveHeader, pos: u32) !void { |
| 1005 | 1213 | try w.print("Archive member at offset 0x{x}: '{s}'\n", .{ pos, header.name }); |
| 1006 | | |
| 1007 | | // TODO: Date formatter |
| 1008 | 1214 | try dumpHeader(w, ArchiveHeader, header, struct { |
| 1009 | 1215 | pub fn name(_: *const ArchiveHeader, _: *Io.Writer) !void {} |
| 1010 | 1216 | pub fn file_mode(h: *const ArchiveHeader, cw: *Io.Writer) !void { |
| ... | ... | @@ -1065,10 +1271,11 @@ const usage = |
| 1065 | 1271 | \\ --exports Display exported symbols |
| 1066 | 1272 | \\ --linker-member[=1|2|longnames] (Coff) Display contents of the specified linker member (default 2) |
| 1067 | 1273 | \\ --member-headers Display archive member headers |
| 1068 | | \\ --only-member=[name] Only consider archive members that contain [name]. Can be specified multiple times. |
| 1069 | | \\ --only-section=[name] Only consider sections 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. |
| 1275 | \\ --only-section=[name] Only consider section names that contain [name]. Can be specified multiple times. |
| 1276 | \\ --relocs Display relocations |
| 1070 | 1277 | \\ --section-headers Display section headers |
| 1071 | 1278 | \\ --strings Display string tables |
| 1072 | 1279 | \\ --symbols Display symbol tables |
| 1073 | | \\ --relocs Display relocations |
| 1280 | \\ --tls Display TLS information |
| 1074 | 1281 | ; |