| ... | ... | @@ -247,15 +247,27 @@ const ComputeCompareExpected = struct { |
| 247 | 247 | |
| 248 | 248 | const Check = struct { |
| 249 | 249 | kind: Kind, |
| 250 | payload: Payload, |
| 251 | data: std.ArrayList(u8), |
| 250 | 252 | actions: std.ArrayList(Action), |
| 251 | 253 | |
| 252 | 254 | fn create(allocator: Allocator, kind: Kind) Check { |
| 253 | 255 | return .{ |
| 254 | 256 | .kind = kind, |
| 257 | .payload = .{ .none = {} }, |
| 258 | .data = std.ArrayList(u8).init(allocator), |
| 255 | 259 | .actions = std.ArrayList(Action).init(allocator), |
| 256 | 260 | }; |
| 257 | 261 | } |
| 258 | 262 | |
| 263 | fn dumpSection(allocator: Allocator, name: [:0]const u8) Check { |
| 264 | var check = Check.create(allocator, .dump_section); |
| 265 | const off: u32 = @intCast(check.data.items.len); |
| 266 | check.data.writer().print("{s}\x00", .{name}) catch @panic("OOM"); |
| 267 | check.payload = .{ .dump_section = off }; |
| 268 | return check; |
| 269 | } |
| 270 | |
| 259 | 271 | fn extract(self: *Check, phrase: SearchPhrase) void { |
| 260 | 272 | self.actions.append(.{ |
| 261 | 273 | .tag = .extract, |
| ... | ... | @@ -305,6 +317,13 @@ const Check = struct { |
| 305 | 317 | dyld_lazy_bind, |
| 306 | 318 | exports, |
| 307 | 319 | compute_compare, |
| 320 | dump_section, |
| 321 | }; |
| 322 | |
| 323 | const Payload = union { |
| 324 | none: void, |
| 325 | /// Null-delimited string in the 'data' buffer. |
| 326 | dump_section: u32, |
| 308 | 327 | }; |
| 309 | 328 | }; |
| 310 | 329 | |
| ... | ... | @@ -513,6 +532,11 @@ pub fn checkInArchiveSymtab(self: *CheckObject) void { |
| 513 | 532 | self.checkExact(label); |
| 514 | 533 | } |
| 515 | 534 | |
| 535 | pub fn dumpSection(self: *CheckObject, name: [:0]const u8) void { |
| 536 | const new_check = Check.dumpSection(self.step.owner.allocator, name); |
| 537 | self.checks.append(new_check) catch @panic("OOM"); |
| 538 | } |
| 539 | |
| 516 | 540 | /// Creates a new standalone, singular check which allows running simple binary operations |
| 517 | 541 | /// on the extracted variables. It will then compare the reduced program with the value of |
| 518 | 542 | /// the expected variable. |
| ... | ... | @@ -564,13 +588,44 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { |
| 564 | 588 | } |
| 565 | 589 | |
| 566 | 590 | const output = switch (self.obj_format) { |
| 567 | | .macho => try MachODumper.parseAndDump(step, chk.kind, contents), |
| 568 | | .elf => try ElfDumper.parseAndDump(step, chk.kind, contents), |
| 591 | .macho => try MachODumper.parseAndDump(step, chk, contents), |
| 592 | .elf => try ElfDumper.parseAndDump(step, chk, contents), |
| 569 | 593 | .coff => return step.fail("TODO coff parser", .{}), |
| 570 | | .wasm => try WasmDumper.parseAndDump(step, chk.kind, contents), |
| 594 | .wasm => try WasmDumper.parseAndDump(step, chk, contents), |
| 571 | 595 | else => unreachable, |
| 572 | 596 | }; |
| 573 | 597 | |
| 598 | // Depending on whether we requested dumping section verbatim or not, |
| 599 | // we either format message string with escaped codes, or not to aid debugging |
| 600 | // the failed test. |
| 601 | const fmtMessageString = struct { |
| 602 | fn fmtMessageString(kind: Check.Kind, msg: []const u8) std.fmt.Formatter(formatMessageString) { |
| 603 | return .{ .data = .{ |
| 604 | .kind = kind, |
| 605 | .msg = msg, |
| 606 | } }; |
| 607 | } |
| 608 | |
| 609 | const Ctx = struct { |
| 610 | kind: Check.Kind, |
| 611 | msg: []const u8, |
| 612 | }; |
| 613 | |
| 614 | fn formatMessageString( |
| 615 | ctx: Ctx, |
| 616 | comptime unused_fmt_string: []const u8, |
| 617 | options: std.fmt.FormatOptions, |
| 618 | writer: anytype, |
| 619 | ) !void { |
| 620 | _ = unused_fmt_string; |
| 621 | _ = options; |
| 622 | switch (ctx.kind) { |
| 623 | .dump_section => try writer.print("{s}", .{std.fmt.fmtSliceEscapeLower(ctx.msg)}), |
| 624 | else => try writer.writeAll(ctx.msg), |
| 625 | } |
| 626 | } |
| 627 | }.fmtMessageString; |
| 628 | |
| 574 | 629 | var it = mem.tokenizeAny(u8, output, "\r\n"); |
| 575 | 630 | for (chk.actions.items) |act| { |
| 576 | 631 | switch (act.tag) { |
| ... | ... | @@ -585,7 +640,7 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { |
| 585 | 640 | \\========= but parsed file does not contain it: ======= |
| 586 | 641 | \\{s} |
| 587 | 642 | \\====================================================== |
| 588 | | , .{ act.phrase.resolve(b, step), output }); |
| 643 | , .{ fmtMessageString(chk.kind, act.phrase.resolve(b, step)), fmtMessageString(chk.kind, output) }); |
| 589 | 644 | } |
| 590 | 645 | }, |
| 591 | 646 | |
| ... | ... | @@ -600,7 +655,7 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { |
| 600 | 655 | \\========= but parsed file does not contain it: ======= |
| 601 | 656 | \\{s} |
| 602 | 657 | \\====================================================== |
| 603 | | , .{ act.phrase.resolve(b, step), output }); |
| 658 | , .{ fmtMessageString(chk.kind, act.phrase.resolve(b, step)), fmtMessageString(chk.kind, output) }); |
| 604 | 659 | } |
| 605 | 660 | }, |
| 606 | 661 | |
| ... | ... | @@ -614,7 +669,7 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { |
| 614 | 669 | \\========= but parsed file does contain it: ======== |
| 615 | 670 | \\{s} |
| 616 | 671 | \\=================================================== |
| 617 | | , .{ act.phrase.resolve(b, step), output }); |
| 672 | , .{ fmtMessageString(chk.kind, act.phrase.resolve(b, step)), fmtMessageString(chk.kind, output) }); |
| 618 | 673 | } |
| 619 | 674 | }, |
| 620 | 675 | |
| ... | ... | @@ -629,7 +684,7 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { |
| 629 | 684 | \\========= but parsed file does not contain it: ======= |
| 630 | 685 | \\{s} |
| 631 | 686 | \\====================================================== |
| 632 | | , .{ act.phrase.resolve(b, step), output }); |
| 687 | , .{ act.phrase.resolve(b, step), fmtMessageString(chk.kind, output) }); |
| 633 | 688 | } |
| 634 | 689 | }, |
| 635 | 690 | |
| ... | ... | @@ -660,7 +715,7 @@ const MachODumper = struct { |
| 660 | 715 | } |
| 661 | 716 | }; |
| 662 | 717 | |
| 663 | | fn parseAndDump(step: *Step, kind: Check.Kind, bytes: []const u8) ![]const u8 { |
| 718 | fn parseAndDump(step: *Step, check: Check, bytes: []const u8) ![]const u8 { |
| 664 | 719 | const gpa = step.owner.allocator; |
| 665 | 720 | var stream = std.io.fixedBufferStream(bytes); |
| 666 | 721 | const reader = stream.reader(); |
| ... | ... | @@ -731,7 +786,7 @@ const MachODumper = struct { |
| 731 | 786 | } |
| 732 | 787 | } |
| 733 | 788 | |
| 734 | | switch (kind) { |
| 789 | switch (check.kind) { |
| 735 | 790 | .headers => { |
| 736 | 791 | try dumpHeader(hdr, writer); |
| 737 | 792 | |
| ... | ... | @@ -764,7 +819,7 @@ const MachODumper = struct { |
| 764 | 819 | if (dyld_info_lc == null) return step.fail("no dyld info found", .{}); |
| 765 | 820 | const lc = dyld_info_lc.?; |
| 766 | 821 | |
| 767 | | switch (kind) { |
| 822 | switch (check.kind) { |
| 768 | 823 | .dyld_rebase => if (lc.rebase_size > 0) { |
| 769 | 824 | const data = bytes[lc.rebase_off..][0..lc.rebase_size]; |
| 770 | 825 | try writer.writeAll(dyld_rebase_label ++ "\n"); |
| ... | ... | @@ -805,7 +860,7 @@ const MachODumper = struct { |
| 805 | 860 | return step.fail("no exports data found", .{}); |
| 806 | 861 | }, |
| 807 | 862 | |
| 808 | | else => return step.fail("invalid check kind for MachO file format: {s}", .{@tagName(kind)}), |
| 863 | else => return step.fail("invalid check kind for MachO file format: {s}", .{@tagName(check.kind)}), |
| 809 | 864 | } |
| 810 | 865 | |
| 811 | 866 | return output.toOwnedSlice(); |
| ... | ... | @@ -1633,14 +1688,14 @@ const ElfDumper = struct { |
| 1633 | 1688 | const dynamic_section_label = "dynamic section"; |
| 1634 | 1689 | const archive_symtab_label = "archive symbol table"; |
| 1635 | 1690 | |
| 1636 | | fn parseAndDump(step: *Step, kind: Check.Kind, bytes: []const u8) ![]const u8 { |
| 1637 | | return parseAndDumpArchive(step, kind, bytes) catch |err| switch (err) { |
| 1638 | | error.InvalidArchiveMagicNumber => try parseAndDumpObject(step, kind, bytes), |
| 1691 | fn parseAndDump(step: *Step, check: Check, bytes: []const u8) ![]const u8 { |
| 1692 | return parseAndDumpArchive(step, check, bytes) catch |err| switch (err) { |
| 1693 | error.InvalidArchiveMagicNumber => try parseAndDumpObject(step, check, bytes), |
| 1639 | 1694 | else => |e| return e, |
| 1640 | 1695 | }; |
| 1641 | 1696 | } |
| 1642 | 1697 | |
| 1643 | | fn parseAndDumpArchive(step: *Step, kind: Check.Kind, bytes: []const u8) ![]const u8 { |
| 1698 | fn parseAndDumpArchive(step: *Step, check: Check, bytes: []const u8) ![]const u8 { |
| 1644 | 1699 | const gpa = step.owner.allocator; |
| 1645 | 1700 | var stream = std.io.fixedBufferStream(bytes); |
| 1646 | 1701 | const reader = stream.reader(); |
| ... | ... | @@ -1702,13 +1757,13 @@ const ElfDumper = struct { |
| 1702 | 1757 | var output = std.ArrayList(u8).init(gpa); |
| 1703 | 1758 | const writer = output.writer(); |
| 1704 | 1759 | |
| 1705 | | switch (kind) { |
| 1760 | switch (check.kind) { |
| 1706 | 1761 | .archive_symtab => if (ctx.symtab.items.len > 0) { |
| 1707 | 1762 | try ctx.dumpSymtab(writer); |
| 1708 | 1763 | } else return step.fail("no archive symbol table found", .{}), |
| 1709 | 1764 | |
| 1710 | 1765 | else => if (ctx.objects.items.len > 0) { |
| 1711 | | try ctx.dumpObjects(step, kind, writer); |
| 1766 | try ctx.dumpObjects(step, check, writer); |
| 1712 | 1767 | } else return step.fail("empty archive", .{}), |
| 1713 | 1768 | } |
| 1714 | 1769 | |
| ... | ... | @@ -1785,10 +1840,10 @@ const ElfDumper = struct { |
| 1785 | 1840 | } |
| 1786 | 1841 | } |
| 1787 | 1842 | |
| 1788 | | fn dumpObjects(ctx: ArchiveContext, step: *Step, kind: Check.Kind, writer: anytype) !void { |
| 1843 | fn dumpObjects(ctx: ArchiveContext, step: *Step, check: Check, writer: anytype) !void { |
| 1789 | 1844 | for (ctx.objects.items) |object| { |
| 1790 | 1845 | try writer.print("object {s}\n", .{object.name}); |
| 1791 | | const output = try parseAndDumpObject(step, kind, ctx.data[object.off..][0..object.len]); |
| 1846 | const output = try parseAndDumpObject(step, check, ctx.data[object.off..][0..object.len]); |
| 1792 | 1847 | defer ctx.gpa.free(output); |
| 1793 | 1848 | try writer.print("{s}\n", .{output}); |
| 1794 | 1849 | } |
| ... | ... | @@ -1806,7 +1861,7 @@ const ElfDumper = struct { |
| 1806 | 1861 | }; |
| 1807 | 1862 | }; |
| 1808 | 1863 | |
| 1809 | | fn parseAndDumpObject(step: *Step, kind: Check.Kind, bytes: []const u8) ![]const u8 { |
| 1864 | fn parseAndDumpObject(step: *Step, check: Check, bytes: []const u8) ![]const u8 { |
| 1810 | 1865 | const gpa = step.owner.allocator; |
| 1811 | 1866 | var stream = std.io.fixedBufferStream(bytes); |
| 1812 | 1867 | const reader = stream.reader(); |
| ... | ... | @@ -1859,7 +1914,7 @@ const ElfDumper = struct { |
| 1859 | 1914 | var output = std.ArrayList(u8).init(gpa); |
| 1860 | 1915 | const writer = output.writer(); |
| 1861 | 1916 | |
| 1862 | | switch (kind) { |
| 1917 | switch (check.kind) { |
| 1863 | 1918 | .headers => { |
| 1864 | 1919 | try ctx.dumpHeader(writer); |
| 1865 | 1920 | try ctx.dumpShdrs(writer); |
| ... | ... | @@ -1878,7 +1933,13 @@ const ElfDumper = struct { |
| 1878 | 1933 | try ctx.dumpDynamicSection(shndx, writer); |
| 1879 | 1934 | } else return step.fail("no .dynamic section found", .{}), |
| 1880 | 1935 | |
| 1881 | | else => return step.fail("invalid check kind for ELF file format: {s}", .{@tagName(kind)}), |
| 1936 | .dump_section => { |
| 1937 | const name = mem.sliceTo(@as([*:0]const u8, @ptrCast(check.data.items.ptr + check.payload.dump_section)), 0); |
| 1938 | const shndx = ctx.getSectionByName(name) orelse return step.fail("no '{s}' section found", .{name}); |
| 1939 | try ctx.dumpSection(shndx, writer); |
| 1940 | }, |
| 1941 | |
| 1942 | else => return step.fail("invalid check kind for ELF file format: {s}", .{@tagName(check.kind)}), |
| 1882 | 1943 | } |
| 1883 | 1944 | |
| 1884 | 1945 | return output.toOwnedSlice(); |
| ... | ... | @@ -2176,6 +2237,11 @@ const ElfDumper = struct { |
| 2176 | 2237 | } |
| 2177 | 2238 | } |
| 2178 | 2239 | |
| 2240 | fn dumpSection(ctx: ObjectContext, shndx: usize, writer: anytype) !void { |
| 2241 | const data = ctx.getSectionContents(shndx); |
| 2242 | try writer.print("{s}", .{data}); |
| 2243 | } |
| 2244 | |
| 2179 | 2245 | inline fn getSectionName(ctx: ObjectContext, shndx: usize) []const u8 { |
| 2180 | 2246 | const shdr = ctx.shdrs[shndx]; |
| 2181 | 2247 | return getString(ctx.shstrtab, shdr.sh_name); |
| ... | ... | @@ -2300,7 +2366,7 @@ const ElfDumper = struct { |
| 2300 | 2366 | const WasmDumper = struct { |
| 2301 | 2367 | const symtab_label = "symbols"; |
| 2302 | 2368 | |
| 2303 | | fn parseAndDump(step: *Step, kind: Check.Kind, bytes: []const u8) ![]const u8 { |
| 2369 | fn parseAndDump(step: *Step, check: Check, bytes: []const u8) ![]const u8 { |
| 2304 | 2370 | const gpa = step.owner.allocator; |
| 2305 | 2371 | var fbs = std.io.fixedBufferStream(bytes); |
| 2306 | 2372 | const reader = fbs.reader(); |
| ... | ... | @@ -2317,7 +2383,7 @@ const WasmDumper = struct { |
| 2317 | 2383 | errdefer output.deinit(); |
| 2318 | 2384 | const writer = output.writer(); |
| 2319 | 2385 | |
| 2320 | | switch (kind) { |
| 2386 | switch (check.kind) { |
| 2321 | 2387 | .headers => { |
| 2322 | 2388 | while (reader.readByte()) |current_byte| { |
| 2323 | 2389 | const section = std.meta.intToEnum(std.wasm.Section, current_byte) catch { |
| ... | ... | @@ -2330,7 +2396,7 @@ const WasmDumper = struct { |
| 2330 | 2396 | } else |_| {} // reached end of stream |
| 2331 | 2397 | }, |
| 2332 | 2398 | |
| 2333 | | else => return step.fail("invalid check kind for Wasm file format: {s}", .{@tagName(kind)}), |
| 2399 | else => return step.fail("invalid check kind for Wasm file format: {s}", .{@tagName(check.kind)}), |
| 2334 | 2400 | } |
| 2335 | 2401 | |
| 2336 | 2402 | return output.toOwnedSlice(); |
| ... | ... | @@ -2364,7 +2430,7 @@ const WasmDumper = struct { |
| 2364 | 2430 | => { |
| 2365 | 2431 | const entries = try std.leb.readULEB128(u32, reader); |
| 2366 | 2432 | try writer.print("\nentries {d}\n", .{entries}); |
| 2367 | | try dumpSection(step, section, data[fbs.pos..], entries, writer); |
| 2433 | try parseSection(step, section, data[fbs.pos..], entries, writer); |
| 2368 | 2434 | }, |
| 2369 | 2435 | .custom => { |
| 2370 | 2436 | const name_length = try std.leb.readULEB128(u32, reader); |
| ... | ... | @@ -2393,7 +2459,7 @@ const WasmDumper = struct { |
| 2393 | 2459 | } |
| 2394 | 2460 | } |
| 2395 | 2461 | |
| 2396 | | fn dumpSection(step: *Step, section: std.wasm.Section, data: []const u8, entries: u32, writer: anytype) !void { |
| 2462 | fn parseSection(step: *Step, section: std.wasm.Section, data: []const u8, entries: u32, writer: anytype) !void { |
| 2397 | 2463 | var fbs = std.io.fixedBufferStream(data); |
| 2398 | 2464 | const reader = fbs.reader(); |
| 2399 | 2465 | |