| ... | ... | @@ -16,7 +16,7 @@ const Options = struct { |
| 16 | 16 | input_path: []const u8, |
| 17 | 17 | member_filters: []const []const u8 = &.{}, |
| 18 | 18 | member_headers: bool, |
| 19 | | omit_elements: std.enums.EnumArray(Element, bool), |
| 19 | elements: std.enums.EnumArray(Element, bool), |
| 20 | 20 | redact: std.enums.EnumArray(FieldKind, bool), |
| 21 | 21 | relocs: bool, |
| 22 | 22 | section_filters: []const []const u8 = &.{}, |
| ... | ... | @@ -39,9 +39,10 @@ const FieldKind = enum { |
| 39 | 39 | |
| 40 | 40 | const Element = enum { |
| 41 | 41 | @"file-type", |
| 42 | | @"table-header", |
| 43 | | @"header-names", |
| 42 | @"header-name", |
| 43 | @"member-path", |
| 44 | 44 | newlines, |
| 45 | @"table-header", |
| 45 | 46 | }; |
| 46 | 47 | |
| 47 | 48 | pub fn main(init: std.process.Init) !void { |
| ... | ... | @@ -57,7 +58,8 @@ pub fn main(init: std.process.Init) !void { |
| 57 | 58 | var opt_input_path: ?[]const u8 = null; |
| 58 | 59 | var opt_linker_member: ?std.coff.ArchiveMemberHeader.Kind = null; |
| 59 | 60 | var opt_member_headers: ?bool = null; |
| 60 | | var omit_elements: @FieldType(Options, "omit_elements") = .initFill(false); |
| 61 | var any_elements = false; |
| 62 | var elements: ?@FieldType(Options, "elements") = null; |
| 61 | 63 | var redact: @FieldType(Options, "redact") = .initFill(false); |
| 62 | 64 | var opt_relocs: ?bool = null; |
| 63 | 65 | var opt_section_headers: ?bool = null; |
| ... | ... | @@ -93,14 +95,23 @@ pub fn main(init: std.process.Init) !void { |
| 93 | 95 | opt_linker_member = .second_linker; |
| 94 | 96 | } else if (mem.eql(u8, arg, "--member-headers")) { |
| 95 | 97 | opt_member_headers = true; |
| 96 | | } else if (mem.startsWith(u8, arg, "--omit-element=")) { |
| 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}); |
| 98 | } else if (mem.startsWith(u8, arg, "--elements=")) { |
| 99 | any_elements = true; |
| 100 | var split = std.mem.splitScalar(u8, arg["--elements=".len..], ','); |
| 101 | while (split.next()) |element| { |
| 102 | const kind, const add = if (element.len > 0 and element[0] == '-') |
| 103 | .{ element[1..], false } |
| 104 | else |
| 105 | .{ element, true }; |
| 106 | |
| 107 | if (elements == null) elements = .initFill(false); |
| 108 | if (std.meta.stringToEnum(Element, kind)) |format_kind| { |
| 109 | elements.?.set(format_kind, add); |
| 110 | } else if (std.mem.eql(u8, kind, "all")) { |
| 111 | elements.? = .initFill(add); |
| 112 | } else { |
| 113 | fatal("unrecognized element: '{s}'", .{kind}); |
| 114 | } |
| 104 | 115 | } |
| 105 | 116 | } else if (mem.startsWith(u8, arg, "--only-member=")) { |
| 106 | 117 | (try member_filters.addOne(arena)).* = try arena.dupe(u8, arg["--only-member=".len..]); |
| ... | ... | @@ -122,7 +133,7 @@ pub fn main(init: std.process.Init) !void { |
| 122 | 133 | } else if (mem.eql(u8, arg, "--section-headers")) { |
| 123 | 134 | opt_section_headers = true; |
| 124 | 135 | } else if (mem.eql(u8, arg, "-s") or mem.eql(u8, arg, "--snapshot")) { |
| 125 | | omit_elements = .initFill(true); |
| 136 | elements = .initFill(false); |
| 126 | 137 | redact = .initFill(true); |
| 127 | 138 | } else if (mem.eql(u8, arg, "--strings")) { |
| 128 | 139 | opt_strings = true; |
| ... | ... | @@ -148,7 +159,7 @@ pub fn main(init: std.process.Init) !void { |
| 148 | 159 | .linker_member = opt_linker_member, |
| 149 | 160 | .member_filters = member_filters.items, |
| 150 | 161 | .member_headers = opt_member_headers orelse false, |
| 151 | | .omit_elements = omit_elements, |
| 162 | .elements = elements orelse .initFill(true), |
| 152 | 163 | .redact = redact, |
| 153 | 164 | .relocs = opt_relocs orelse false, |
| 154 | 165 | .section_filters = section_filters.items, |
| ... | ... | @@ -215,20 +226,26 @@ fn dump(d: *const DumpContext) !void { |
| 215 | 226 | return error.ParseFailure; |
| 216 | 227 | } |
| 217 | 228 | |
| 218 | | if (d.element(.@"file-type")) |
| 229 | if (d.element(.@"file-type")) { |
| 219 | 230 | try d.w.print("{s}: PE/COFF image\n\n", .{basename}); |
| 231 | if (d.element(.newlines)) try d.w.writeByte('\n'); |
| 232 | } |
| 220 | 233 | |
| 221 | 234 | return coff.dumpObject(d, true, basename); |
| 222 | 235 | } else if (std.mem.eql(u8, ext, ".lib")) { |
| 223 | 236 | r.fill(std.coff.archive_signature.len) catch break :coff; |
| 224 | 237 | if (!mem.eql(u8, r.buffered()[0..std.coff.archive_signature.len], std.coff.archive_signature)) break :coff; |
| 225 | | if (d.element(.@"file-type")) |
| 226 | | try d.w.print("{s}: COFF archive\n\n", .{basename}); |
| 238 | if (d.element(.@"file-type")) { |
| 239 | try d.w.print("{s}: COFF archive\n", .{basename}); |
| 240 | if (d.element(.newlines)) try d.w.writeByte('\n'); |
| 241 | } |
| 227 | 242 | |
| 228 | 243 | return coff.dumpArchive(d); |
| 229 | 244 | } else if (std.mem.eql(u8, ext, ".obj")) { |
| 230 | | if (d.element(.@"file-type")) |
| 231 | | try d.w.print("{s}: COFF object\n\n", .{basename}); |
| 245 | if (d.element(.@"file-type")) { |
| 246 | try d.w.print("{s}: COFF object\n", .{basename}); |
| 247 | if (d.element(.newlines)) try d.w.writeByte('\n'); |
| 248 | } |
| 232 | 249 | |
| 233 | 250 | return coff.dumpObject(d, false, basename); |
| 234 | 251 | } |
| ... | ... | @@ -243,7 +260,7 @@ const DumpContext = struct { |
| 243 | 260 | w: *Io.Writer, |
| 244 | 261 | |
| 245 | 262 | fn element(self: *const DumpContext, e: Element) bool { |
| 246 | | return !self.opts.omit_elements.get(e); |
| 263 | return self.opts.elements.get(e); |
| 247 | 264 | } |
| 248 | 265 | |
| 249 | 266 | fn redacted(self: *const DumpContext, opt_kind: ?FieldKind) bool { |
| ... | ... | @@ -589,9 +606,19 @@ const coff = struct { |
| 589 | 606 | d.opts.strings or |
| 590 | 607 | d.opts.symbols) |
| 591 | 608 | { |
| 592 | | if (d.element(.@"file-type")) |
| 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); |
| 609 | const member_name = if (d.element(.@"member-path")) |
| 610 | header.name |
| 611 | else |
| 612 | std.fs.path.basename(header.name); |
| 613 | |
| 614 | if (d.element(.@"file-type")) { |
| 615 | try w.print("{s}({s}): COFF object\n", .{ |
| 616 | std.fs.path.basename(d.opts.input_path), |
| 617 | member_name, |
| 618 | }); |
| 619 | if (d.element(.newlines)) try w.writeByte('\n'); |
| 620 | } |
| 621 | try dumpObject(d, false, member_name); |
| 595 | 622 | } |
| 596 | 623 | } |
| 597 | 624 | } |
| ... | ... | @@ -611,7 +638,7 @@ const coff = struct { |
| 611 | 638 | return d.failParse("unable to read COFF header: {t}", .{err}); |
| 612 | 639 | |
| 613 | 640 | if (d.opts.file_headers) { |
| 614 | | if (d.element(.@"header-names")) try w.writeAll("COFF Header:\n"); |
| 641 | if (d.element(.@"header-name")) try w.writeAll("COFF Header:\n"); |
| 615 | 642 | try dumpHeader(d, std.coff.Header, &header, struct {}); |
| 616 | 643 | if (d.element(.newlines)) try w.writeByte('\n'); |
| 617 | 644 | } |
| ... | ... | @@ -639,7 +666,7 @@ const coff = struct { |
| 639 | 666 | break :image_info null; |
| 640 | 667 | } |
| 641 | 668 | |
| 642 | | if (d.opts.file_headers and d.element(.@"header-names")) |
| 669 | if (d.opts.file_headers and d.element(.@"header-name")) |
| 643 | 670 | try w.writeAll("COFF Optional Header:\n"); |
| 644 | 671 | |
| 645 | 672 | const magic: std.coff.OptionalHeader.Magic = @enumFromInt(try r.peekInt(u16, .little)); |
| ... | ... | @@ -701,7 +728,7 @@ const coff = struct { |
| 701 | 728 | else => return d.failParse("invalid optional header magic number: {x}", .{magic}), |
| 702 | 729 | }; |
| 703 | 730 | |
| 704 | | if (d.opts.file_headers and d.element(.@"header-names")) |
| 731 | if (d.opts.file_headers and d.element(.@"header-name")) |
| 705 | 732 | try w.writeAll("Data Directories:\n"); |
| 706 | 733 | |
| 707 | 734 | for (0..num_directory_entries) |dir_i| { |
| ... | ... | @@ -753,7 +780,7 @@ const coff = struct { |
| 753 | 780 | \\ |
| 754 | 781 | , .{string_table.len}); |
| 755 | 782 | |
| 756 | | var sr = Io.Reader.fixed(string_table[4..]); |
| 783 | var sr = Io.Reader.fixed(string_table[@sizeOf(u32)..]); |
| 757 | 784 | while (try sr.takeDelimiter(0)) |str| { |
| 758 | 785 | try w.writeAll(str); |
| 759 | 786 | try w.writeByte('\n'); |
| ... | ... | @@ -1631,28 +1658,29 @@ const usage = |
| 1631 | 1658 | \\Options: |
| 1632 | 1659 | \\ -h, --help Print this help and exit |
| 1633 | 1660 | \\ --all-headers Alias for --file-headers --member-headers --section-headers --relocs --symbols |
| 1661 | \\ --exports Display exported symbols |
| 1634 | 1662 | \\ --file-headers Display file-format specific headers |
| 1635 | 1663 | \\ --imports Display imported symbols |
| 1636 | | \\ --exports Display exported symbols |
| 1637 | 1664 | \\ --linker-member[=1|2|longnames] (Coff) Display contents of the specified archive linker member (default 2) |
| 1638 | 1665 | \\ --member-headers Display archive member headers |
| 1666 | \\ --elements=[e1],[e2],-[e3],... Select which formatting elements are displayed. Intended for snapshot testing. |
| 1667 | \\ file-type File type summary |
| 1668 | \\ header-name Name that precedes a header block |
| 1669 | \\ member-path Display full member paths. If removed, only basenames will be used. |
| 1670 | \\ newlines Newlines between output sections |
| 1671 | \\ table-header Table headers with column names |
| 1672 | \\ all (default) All of the above |
| 1673 | \\ --only-member=[name] Only consider archive members names that contain [name]. Can be specified multiple times. |
| 1674 | \\ --only-section=[name] Only consider section names that contain [name]. Can be specified multiple times. |
| 1675 | \\ --only-symbol=[name] Only consider symbol names that contain [name]. Can be specified multiple times. |
| 1639 | 1676 | \\ --redact=[kind] Redact the specified field kind. Intended for snapshot testing. |
| 1640 | 1677 | \\ rva Relative virtual addresses |
| 1641 | 1678 | \\ va Virtual addresses and file offsets |
| 1642 | 1679 | \\ ord Symbol ordinals / hints |
| 1643 | 1680 | \\ size Sizes and lengths |
| 1644 | 1681 | \\ all All of the above |
| 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 | 1682 | \\ --relocs Display relocations |
| 1655 | | \\ -s, --snapshot Alias for --redact=all --omit-format=all |
| 1683 | \\ -s, --snapshot Alias for --redact=all --elements=-all |
| 1656 | 1684 | \\ --section-headers Display section headers |
| 1657 | 1685 | \\ --strings Display string tables |
| 1658 | 1686 | \\ --symbols Display symbol tables |