diff options
| author | Jakub Konka <kubkon@jakubkonka.com> | 2024-04-15 12:36:32 +0200 |
|---|---|---|
| committer | Jakub Konka <kubkon@jakubkonka.com> | 2024-04-15 18:06:44 +0200 |
| commit | 1e5075f81296cccd469a0829259231cb34337a02 (patch) | |
| tree | ab9d2e63916b044def5314c6608f4ef69ba53b99 /lib/std/Build/Step/CheckObject.zig | |
| parent | d483ba725028375cb1e290f5196daed2929e6c6c (diff) | |
| download | zig-1e5075f81296cccd469a0829259231cb34337a02.tar.gz zig-1e5075f81296cccd469a0829259231cb34337a02.zip | |
lib/std/Build/Step/CheckObject: dump section as string
Diffstat (limited to 'lib/std/Build/Step/CheckObject.zig')
| -rw-r--r-- | lib/std/Build/Step/CheckObject.zig | 120 |
1 files changed, 93 insertions, 27 deletions
diff --git a/lib/std/Build/Step/CheckObject.zig b/lib/std/Build/Step/CheckObject.zig index c39a487649..f8f0fcd584 100644 --- a/lib/std/Build/Step/CheckObject.zig +++ b/lib/std/Build/Step/CheckObject.zig @@ -247,15 +247,27 @@ const ComputeCompareExpected = struct { const Check = struct { kind: Kind, + payload: Payload, + data: std.ArrayList(u8), actions: std.ArrayList(Action), fn create(allocator: Allocator, kind: Kind) Check { return .{ .kind = kind, + .payload = .{ .none = {} }, + .data = std.ArrayList(u8).init(allocator), .actions = std.ArrayList(Action).init(allocator), }; } + fn dumpSection(allocator: Allocator, name: [:0]const u8) Check { + var check = Check.create(allocator, .dump_section); + const off: u32 = @intCast(check.data.items.len); + check.data.writer().print("{s}\x00", .{name}) catch @panic("OOM"); + check.payload = .{ .dump_section = off }; + return check; + } + fn extract(self: *Check, phrase: SearchPhrase) void { self.actions.append(.{ .tag = .extract, @@ -305,6 +317,13 @@ const Check = struct { dyld_lazy_bind, exports, compute_compare, + dump_section, + }; + + const Payload = union { + none: void, + /// Null-delimited string in the 'data' buffer. + dump_section: u32, }; }; @@ -513,6 +532,11 @@ pub fn checkInArchiveSymtab(self: *CheckObject) void { self.checkExact(label); } +pub fn dumpSection(self: *CheckObject, name: [:0]const u8) void { + const new_check = Check.dumpSection(self.step.owner.allocator, name); + self.checks.append(new_check) catch @panic("OOM"); +} + /// Creates a new standalone, singular check which allows running simple binary operations /// on the extracted variables. It will then compare the reduced program with the value of /// the expected variable. @@ -564,13 +588,44 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { } const output = switch (self.obj_format) { - .macho => try MachODumper.parseAndDump(step, chk.kind, contents), - .elf => try ElfDumper.parseAndDump(step, chk.kind, contents), + .macho => try MachODumper.parseAndDump(step, chk, contents), + .elf => try ElfDumper.parseAndDump(step, chk, contents), .coff => return step.fail("TODO coff parser", .{}), - .wasm => try WasmDumper.parseAndDump(step, chk.kind, contents), + .wasm => try WasmDumper.parseAndDump(step, chk, contents), else => unreachable, }; + // Depending on whether we requested dumping section verbatim or not, + // we either format message string with escaped codes, or not to aid debugging + // the failed test. + const fmtMessageString = struct { + fn fmtMessageString(kind: Check.Kind, msg: []const u8) std.fmt.Formatter(formatMessageString) { + return .{ .data = .{ + .kind = kind, + .msg = msg, + } }; + } + + const Ctx = struct { + kind: Check.Kind, + msg: []const u8, + }; + + fn formatMessageString( + ctx: Ctx, + comptime unused_fmt_string: []const u8, + options: std.fmt.FormatOptions, + writer: anytype, + ) !void { + _ = unused_fmt_string; + _ = options; + switch (ctx.kind) { + .dump_section => try writer.print("{s}", .{std.fmt.fmtSliceEscapeLower(ctx.msg)}), + else => try writer.writeAll(ctx.msg), + } + } + }.fmtMessageString; + var it = mem.tokenizeAny(u8, output, "\r\n"); for (chk.actions.items) |act| { switch (act.tag) { @@ -585,7 +640,7 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { \\========= but parsed file does not contain it: ======= \\{s} \\====================================================== - , .{ act.phrase.resolve(b, step), output }); + , .{ fmtMessageString(chk.kind, act.phrase.resolve(b, step)), fmtMessageString(chk.kind, output) }); } }, @@ -600,7 +655,7 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { \\========= but parsed file does not contain it: ======= \\{s} \\====================================================== - , .{ act.phrase.resolve(b, step), output }); + , .{ fmtMessageString(chk.kind, act.phrase.resolve(b, step)), fmtMessageString(chk.kind, output) }); } }, @@ -614,7 +669,7 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { \\========= but parsed file does contain it: ======== \\{s} \\=================================================== - , .{ act.phrase.resolve(b, step), output }); + , .{ fmtMessageString(chk.kind, act.phrase.resolve(b, step)), fmtMessageString(chk.kind, output) }); } }, @@ -629,7 +684,7 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { \\========= but parsed file does not contain it: ======= \\{s} \\====================================================== - , .{ act.phrase.resolve(b, step), output }); + , .{ act.phrase.resolve(b, step), fmtMessageString(chk.kind, output) }); } }, @@ -660,7 +715,7 @@ const MachODumper = struct { } }; - fn parseAndDump(step: *Step, kind: Check.Kind, bytes: []const u8) ![]const u8 { + fn parseAndDump(step: *Step, check: Check, bytes: []const u8) ![]const u8 { const gpa = step.owner.allocator; var stream = std.io.fixedBufferStream(bytes); const reader = stream.reader(); @@ -731,7 +786,7 @@ const MachODumper = struct { } } - switch (kind) { + switch (check.kind) { .headers => { try dumpHeader(hdr, writer); @@ -764,7 +819,7 @@ const MachODumper = struct { if (dyld_info_lc == null) return step.fail("no dyld info found", .{}); const lc = dyld_info_lc.?; - switch (kind) { + switch (check.kind) { .dyld_rebase => if (lc.rebase_size > 0) { const data = bytes[lc.rebase_off..][0..lc.rebase_size]; try writer.writeAll(dyld_rebase_label ++ "\n"); @@ -805,7 +860,7 @@ const MachODumper = struct { return step.fail("no exports data found", .{}); }, - else => return step.fail("invalid check kind for MachO file format: {s}", .{@tagName(kind)}), + else => return step.fail("invalid check kind for MachO file format: {s}", .{@tagName(check.kind)}), } return output.toOwnedSlice(); @@ -1633,14 +1688,14 @@ const ElfDumper = struct { const dynamic_section_label = "dynamic section"; const archive_symtab_label = "archive symbol table"; - fn parseAndDump(step: *Step, kind: Check.Kind, bytes: []const u8) ![]const u8 { - return parseAndDumpArchive(step, kind, bytes) catch |err| switch (err) { - error.InvalidArchiveMagicNumber => try parseAndDumpObject(step, kind, bytes), + fn parseAndDump(step: *Step, check: Check, bytes: []const u8) ![]const u8 { + return parseAndDumpArchive(step, check, bytes) catch |err| switch (err) { + error.InvalidArchiveMagicNumber => try parseAndDumpObject(step, check, bytes), else => |e| return e, }; } - fn parseAndDumpArchive(step: *Step, kind: Check.Kind, bytes: []const u8) ![]const u8 { + fn parseAndDumpArchive(step: *Step, check: Check, bytes: []const u8) ![]const u8 { const gpa = step.owner.allocator; var stream = std.io.fixedBufferStream(bytes); const reader = stream.reader(); @@ -1702,13 +1757,13 @@ const ElfDumper = struct { var output = std.ArrayList(u8).init(gpa); const writer = output.writer(); - switch (kind) { + switch (check.kind) { .archive_symtab => if (ctx.symtab.items.len > 0) { try ctx.dumpSymtab(writer); } else return step.fail("no archive symbol table found", .{}), else => if (ctx.objects.items.len > 0) { - try ctx.dumpObjects(step, kind, writer); + try ctx.dumpObjects(step, check, writer); } else return step.fail("empty archive", .{}), } @@ -1785,10 +1840,10 @@ const ElfDumper = struct { } } - fn dumpObjects(ctx: ArchiveContext, step: *Step, kind: Check.Kind, writer: anytype) !void { + fn dumpObjects(ctx: ArchiveContext, step: *Step, check: Check, writer: anytype) !void { for (ctx.objects.items) |object| { try writer.print("object {s}\n", .{object.name}); - const output = try parseAndDumpObject(step, kind, ctx.data[object.off..][0..object.len]); + const output = try parseAndDumpObject(step, check, ctx.data[object.off..][0..object.len]); defer ctx.gpa.free(output); try writer.print("{s}\n", .{output}); } @@ -1806,7 +1861,7 @@ const ElfDumper = struct { }; }; - fn parseAndDumpObject(step: *Step, kind: Check.Kind, bytes: []const u8) ![]const u8 { + fn parseAndDumpObject(step: *Step, check: Check, bytes: []const u8) ![]const u8 { const gpa = step.owner.allocator; var stream = std.io.fixedBufferStream(bytes); const reader = stream.reader(); @@ -1859,7 +1914,7 @@ const ElfDumper = struct { var output = std.ArrayList(u8).init(gpa); const writer = output.writer(); - switch (kind) { + switch (check.kind) { .headers => { try ctx.dumpHeader(writer); try ctx.dumpShdrs(writer); @@ -1878,7 +1933,13 @@ const ElfDumper = struct { try ctx.dumpDynamicSection(shndx, writer); } else return step.fail("no .dynamic section found", .{}), - else => return step.fail("invalid check kind for ELF file format: {s}", .{@tagName(kind)}), + .dump_section => { + const name = mem.sliceTo(@as([*:0]const u8, @ptrCast(check.data.items.ptr + check.payload.dump_section)), 0); + const shndx = ctx.getSectionByName(name) orelse return step.fail("no '{s}' section found", .{name}); + try ctx.dumpSection(shndx, writer); + }, + + else => return step.fail("invalid check kind for ELF file format: {s}", .{@tagName(check.kind)}), } return output.toOwnedSlice(); @@ -2176,6 +2237,11 @@ const ElfDumper = struct { } } + fn dumpSection(ctx: ObjectContext, shndx: usize, writer: anytype) !void { + const data = ctx.getSectionContents(shndx); + try writer.print("{s}", .{data}); + } + inline fn getSectionName(ctx: ObjectContext, shndx: usize) []const u8 { const shdr = ctx.shdrs[shndx]; return getString(ctx.shstrtab, shdr.sh_name); @@ -2300,7 +2366,7 @@ const ElfDumper = struct { const WasmDumper = struct { const symtab_label = "symbols"; - fn parseAndDump(step: *Step, kind: Check.Kind, bytes: []const u8) ![]const u8 { + fn parseAndDump(step: *Step, check: Check, bytes: []const u8) ![]const u8 { const gpa = step.owner.allocator; var fbs = std.io.fixedBufferStream(bytes); const reader = fbs.reader(); @@ -2317,7 +2383,7 @@ const WasmDumper = struct { errdefer output.deinit(); const writer = output.writer(); - switch (kind) { + switch (check.kind) { .headers => { while (reader.readByte()) |current_byte| { const section = std.meta.intToEnum(std.wasm.Section, current_byte) catch { @@ -2330,7 +2396,7 @@ const WasmDumper = struct { } else |_| {} // reached end of stream }, - else => return step.fail("invalid check kind for Wasm file format: {s}", .{@tagName(kind)}), + else => return step.fail("invalid check kind for Wasm file format: {s}", .{@tagName(check.kind)}), } return output.toOwnedSlice(); @@ -2364,7 +2430,7 @@ const WasmDumper = struct { => { const entries = try std.leb.readULEB128(u32, reader); try writer.print("\nentries {d}\n", .{entries}); - try dumpSection(step, section, data[fbs.pos..], entries, writer); + try parseSection(step, section, data[fbs.pos..], entries, writer); }, .custom => { const name_length = try std.leb.readULEB128(u32, reader); @@ -2393,7 +2459,7 @@ const WasmDumper = struct { } } - fn dumpSection(step: *Step, section: std.wasm.Section, data: []const u8, entries: u32, writer: anytype) !void { + fn parseSection(step: *Step, section: std.wasm.Section, data: []const u8, entries: u32, writer: anytype) !void { var fbs = std.io.fixedBufferStream(data); const reader = fbs.reader(); |
