authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2024-04-15 12:36:32+02:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2024-04-15 18:06:44+02:00
log1e5075f81296cccd469a0829259231cb34337a02
treeab9d2e63916b044def5314c6608f4ef69ba53b99
parentd483ba725028375cb1e290f5196daed2929e6c6c

lib/std/Build/Step/CheckObject: dump section as string


1 files changed, 93 insertions(+), 27 deletions(-)

lib/std/Build/Step/CheckObject.zig+93-27
...@@ -247,15 +247,27 @@ const ComputeCompareExpected = struct {...@@ -247,15 +247,27 @@ const ComputeCompareExpected = struct {
247247
248const Check = struct {248const Check = struct {
249 kind: Kind,249 kind: Kind,
250 payload: Payload,
251 data: std.ArrayList(u8),
250 actions: std.ArrayList(Action),252 actions: std.ArrayList(Action),
251253
252 fn create(allocator: Allocator, kind: Kind) Check {254 fn create(allocator: Allocator, kind: Kind) Check {
253 return .{255 return .{
254 .kind = kind,256 .kind = kind,
257 .payload = .{ .none = {} },
258 .data = std.ArrayList(u8).init(allocator),
255 .actions = std.ArrayList(Action).init(allocator),259 .actions = std.ArrayList(Action).init(allocator),
256 };260 };
257 }261 }
258262
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 fn extract(self: *Check, phrase: SearchPhrase) void {271 fn extract(self: *Check, phrase: SearchPhrase) void {
260 self.actions.append(.{272 self.actions.append(.{
261 .tag = .extract,273 .tag = .extract,
...@@ -305,6 +317,13 @@ const Check = struct {...@@ -305,6 +317,13 @@ const Check = struct {
305 dyld_lazy_bind,317 dyld_lazy_bind,
306 exports,318 exports,
307 compute_compare,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};
310329
...@@ -513,6 +532,11 @@ pub fn checkInArchiveSymtab(self: *CheckObject) void {...@@ -513,6 +532,11 @@ pub fn checkInArchiveSymtab(self: *CheckObject) void {
513 self.checkExact(label);532 self.checkExact(label);
514}533}
515534
535pub 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/// Creates a new standalone, singular check which allows running simple binary operations540/// Creates a new standalone, singular check which allows running simple binary operations
517/// on the extracted variables. It will then compare the reduced program with the value of541/// on the extracted variables. It will then compare the reduced program with the value of
518/// the expected variable.542/// the expected variable.
...@@ -564,13 +588,44 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {...@@ -564,13 +588,44 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
564 }588 }
565589
566 const output = switch (self.obj_format) {590 const output = switch (self.obj_format) {
567 .macho => try MachODumper.parseAndDump(step, chk.kind, contents),591 .macho => try MachODumper.parseAndDump(step, chk, contents),
568 .elf => try ElfDumper.parseAndDump(step, chk.kind, contents),592 .elf => try ElfDumper.parseAndDump(step, chk, contents),
569 .coff => return step.fail("TODO coff parser", .{}),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 else => unreachable,595 else => unreachable,
572 };596 };
573597
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 var it = mem.tokenizeAny(u8, output, "\r\n");629 var it = mem.tokenizeAny(u8, output, "\r\n");
575 for (chk.actions.items) |act| {630 for (chk.actions.items) |act| {
576 switch (act.tag) {631 switch (act.tag) {
...@@ -585,7 +640,7 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {...@@ -585,7 +640,7 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
585 \\========= but parsed file does not contain it: =======640 \\========= but parsed file does not contain it: =======
586 \\{s}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 },
591646
...@@ -600,7 +655,7 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {...@@ -600,7 +655,7 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
600 \\========= but parsed file does not contain it: =======655 \\========= but parsed file does not contain it: =======
601 \\{s}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 },
606661
...@@ -614,7 +669,7 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {...@@ -614,7 +669,7 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
614 \\========= but parsed file does contain it: ========669 \\========= but parsed file does contain it: ========
615 \\{s}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 },
620675
...@@ -629,7 +684,7 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {...@@ -629,7 +684,7 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
629 \\========= but parsed file does not contain it: =======684 \\========= but parsed file does not contain it: =======
630 \\{s}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 },
635690
...@@ -660,7 +715,7 @@ const MachODumper = struct {...@@ -660,7 +715,7 @@ const MachODumper = struct {
660 }715 }
661 };716 };
662717
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 const gpa = step.owner.allocator;719 const gpa = step.owner.allocator;
665 var stream = std.io.fixedBufferStream(bytes);720 var stream = std.io.fixedBufferStream(bytes);
666 const reader = stream.reader();721 const reader = stream.reader();
...@@ -731,7 +786,7 @@ const MachODumper = struct {...@@ -731,7 +786,7 @@ const MachODumper = struct {
731 }786 }
732 }787 }
733788
734 switch (kind) {789 switch (check.kind) {
735 .headers => {790 .headers => {
736 try dumpHeader(hdr, writer);791 try dumpHeader(hdr, writer);
737792
...@@ -764,7 +819,7 @@ const MachODumper = struct {...@@ -764,7 +819,7 @@ const MachODumper = struct {
764 if (dyld_info_lc == null) return step.fail("no dyld info found", .{});819 if (dyld_info_lc == null) return step.fail("no dyld info found", .{});
765 const lc = dyld_info_lc.?;820 const lc = dyld_info_lc.?;
766821
767 switch (kind) {822 switch (check.kind) {
768 .dyld_rebase => if (lc.rebase_size > 0) {823 .dyld_rebase => if (lc.rebase_size > 0) {
769 const data = bytes[lc.rebase_off..][0..lc.rebase_size];824 const data = bytes[lc.rebase_off..][0..lc.rebase_size];
770 try writer.writeAll(dyld_rebase_label ++ "\n");825 try writer.writeAll(dyld_rebase_label ++ "\n");
...@@ -805,7 +860,7 @@ const MachODumper = struct {...@@ -805,7 +860,7 @@ const MachODumper = struct {
805 return step.fail("no exports data found", .{});860 return step.fail("no exports data found", .{});
806 },861 },
807862
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 }
810865
811 return output.toOwnedSlice();866 return output.toOwnedSlice();
...@@ -1633,14 +1688,14 @@ const ElfDumper = struct {...@@ -1633,14 +1688,14 @@ const ElfDumper = struct {
1633 const dynamic_section_label = "dynamic section";1688 const dynamic_section_label = "dynamic section";
1634 const archive_symtab_label = "archive symbol table";1689 const archive_symtab_label = "archive symbol table";
16351690
1636 fn parseAndDump(step: *Step, kind: Check.Kind, bytes: []const u8) ![]const u8 {1691 fn parseAndDump(step: *Step, check: Check, bytes: []const u8) ![]const u8 {
1637 return parseAndDumpArchive(step, kind, bytes) catch |err| switch (err) {1692 return parseAndDumpArchive(step, check, bytes) catch |err| switch (err) {
1638 error.InvalidArchiveMagicNumber => try parseAndDumpObject(step, kind, bytes),1693 error.InvalidArchiveMagicNumber => try parseAndDumpObject(step, check, bytes),
1639 else => |e| return e,1694 else => |e| return e,
1640 };1695 };
1641 }1696 }
16421697
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 const gpa = step.owner.allocator;1699 const gpa = step.owner.allocator;
1645 var stream = std.io.fixedBufferStream(bytes);1700 var stream = std.io.fixedBufferStream(bytes);
1646 const reader = stream.reader();1701 const reader = stream.reader();
...@@ -1702,13 +1757,13 @@ const ElfDumper = struct {...@@ -1702,13 +1757,13 @@ const ElfDumper = struct {
1702 var output = std.ArrayList(u8).init(gpa);1757 var output = std.ArrayList(u8).init(gpa);
1703 const writer = output.writer();1758 const writer = output.writer();
17041759
1705 switch (kind) {1760 switch (check.kind) {
1706 .archive_symtab => if (ctx.symtab.items.len > 0) {1761 .archive_symtab => if (ctx.symtab.items.len > 0) {
1707 try ctx.dumpSymtab(writer);1762 try ctx.dumpSymtab(writer);
1708 } else return step.fail("no archive symbol table found", .{}),1763 } else return step.fail("no archive symbol table found", .{}),
17091764
1710 else => if (ctx.objects.items.len > 0) {1765 else => if (ctx.objects.items.len > 0) {
1711 try ctx.dumpObjects(step, kind, writer);1766 try ctx.dumpObjects(step, check, writer);
1712 } else return step.fail("empty archive", .{}),1767 } else return step.fail("empty archive", .{}),
1713 }1768 }
17141769
...@@ -1785,10 +1840,10 @@ const ElfDumper = struct {...@@ -1785,10 +1840,10 @@ const ElfDumper = struct {
1785 }1840 }
1786 }1841 }
17871842
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 for (ctx.objects.items) |object| {1844 for (ctx.objects.items) |object| {
1790 try writer.print("object {s}\n", .{object.name});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 defer ctx.gpa.free(output);1847 defer ctx.gpa.free(output);
1793 try writer.print("{s}\n", .{output});1848 try writer.print("{s}\n", .{output});
1794 }1849 }
...@@ -1806,7 +1861,7 @@ const ElfDumper = struct {...@@ -1806,7 +1861,7 @@ const ElfDumper = struct {
1806 };1861 };
1807 };1862 };
18081863
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 const gpa = step.owner.allocator;1865 const gpa = step.owner.allocator;
1811 var stream = std.io.fixedBufferStream(bytes);1866 var stream = std.io.fixedBufferStream(bytes);
1812 const reader = stream.reader();1867 const reader = stream.reader();
...@@ -1859,7 +1914,7 @@ const ElfDumper = struct {...@@ -1859,7 +1914,7 @@ const ElfDumper = struct {
1859 var output = std.ArrayList(u8).init(gpa);1914 var output = std.ArrayList(u8).init(gpa);
1860 const writer = output.writer();1915 const writer = output.writer();
18611916
1862 switch (kind) {1917 switch (check.kind) {
1863 .headers => {1918 .headers => {
1864 try ctx.dumpHeader(writer);1919 try ctx.dumpHeader(writer);
1865 try ctx.dumpShdrs(writer);1920 try ctx.dumpShdrs(writer);
...@@ -1878,7 +1933,13 @@ const ElfDumper = struct {...@@ -1878,7 +1933,13 @@ const ElfDumper = struct {
1878 try ctx.dumpDynamicSection(shndx, writer);1933 try ctx.dumpDynamicSection(shndx, writer);
1879 } else return step.fail("no .dynamic section found", .{}),1934 } else return step.fail("no .dynamic section found", .{}),
18801935
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 }
18831944
1884 return output.toOwnedSlice();1945 return output.toOwnedSlice();
...@@ -2176,6 +2237,11 @@ const ElfDumper = struct {...@@ -2176,6 +2237,11 @@ const ElfDumper = struct {
2176 }2237 }
2177 }2238 }
21782239
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 inline fn getSectionName(ctx: ObjectContext, shndx: usize) []const u8 {2245 inline fn getSectionName(ctx: ObjectContext, shndx: usize) []const u8 {
2180 const shdr = ctx.shdrs[shndx];2246 const shdr = ctx.shdrs[shndx];
2181 return getString(ctx.shstrtab, shdr.sh_name);2247 return getString(ctx.shstrtab, shdr.sh_name);
...@@ -2300,7 +2366,7 @@ const ElfDumper = struct {...@@ -2300,7 +2366,7 @@ const ElfDumper = struct {
2300const WasmDumper = struct {2366const WasmDumper = struct {
2301 const symtab_label = "symbols";2367 const symtab_label = "symbols";
23022368
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 const gpa = step.owner.allocator;2370 const gpa = step.owner.allocator;
2305 var fbs = std.io.fixedBufferStream(bytes);2371 var fbs = std.io.fixedBufferStream(bytes);
2306 const reader = fbs.reader();2372 const reader = fbs.reader();
...@@ -2317,7 +2383,7 @@ const WasmDumper = struct {...@@ -2317,7 +2383,7 @@ const WasmDumper = struct {
2317 errdefer output.deinit();2383 errdefer output.deinit();
2318 const writer = output.writer();2384 const writer = output.writer();
23192385
2320 switch (kind) {2386 switch (check.kind) {
2321 .headers => {2387 .headers => {
2322 while (reader.readByte()) |current_byte| {2388 while (reader.readByte()) |current_byte| {
2323 const section = std.meta.intToEnum(std.wasm.Section, current_byte) catch {2389 const section = std.meta.intToEnum(std.wasm.Section, current_byte) catch {
...@@ -2330,7 +2396,7 @@ const WasmDumper = struct {...@@ -2330,7 +2396,7 @@ const WasmDumper = struct {
2330 } else |_| {} // reached end of stream2396 } else |_| {} // reached end of stream
2331 },2397 },
23322398
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 }
23352401
2336 return output.toOwnedSlice();2402 return output.toOwnedSlice();
...@@ -2364,7 +2430,7 @@ const WasmDumper = struct {...@@ -2364,7 +2430,7 @@ const WasmDumper = struct {
2364 => {2430 => {
2365 const entries = try std.leb.readULEB128(u32, reader);2431 const entries = try std.leb.readULEB128(u32, reader);
2366 try writer.print("\nentries {d}\n", .{entries});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 .custom => {2435 .custom => {
2370 const name_length = try std.leb.readULEB128(u32, reader);2436 const name_length = try std.leb.readULEB128(u32, reader);
...@@ -2393,7 +2459,7 @@ const WasmDumper = struct {...@@ -2393,7 +2459,7 @@ const WasmDumper = struct {
2393 }2459 }
2394 }2460 }
23952461
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 var fbs = std.io.fixedBufferStream(data);2463 var fbs = std.io.fixedBufferStream(data);
2398 const reader = fbs.reader();2464 const reader = fbs.reader();
23992465