diff options
| author | Jakub Konka <kubkon@jakubkonka.com> | 2023-12-13 18:47:09 +0100 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2023-12-13 18:47:09 +0100 |
| commit | 4574dea13a4b1d38c97fe73c257d895f605b78ca (patch) | |
| tree | 0a1af823ce5c7c2ff0546c32f37cfe45b1f5ea6f /lib/std/Build/Step | |
| parent | a38af5f542199378483aba9f4598634ebf012b7b (diff) | |
| parent | 5d12622469db5c53fe4857dfd95be033c16b4056 (diff) | |
| download | zig-4574dea13a4b1d38c97fe73c257d895f605b78ca.tar.gz zig-4574dea13a4b1d38c97fe73c257d895f605b78ca.zip | |
Merge pull request #18271 from ziglang/check-object-scoped-checks
lib/std/Build/CheckObject: introduce scoped checks
Diffstat (limited to 'lib/std/Build/Step')
| -rw-r--r-- | lib/std/Build/Step/CheckObject.zig | 528 |
1 files changed, 370 insertions, 158 deletions
diff --git a/lib/std/Build/Step/CheckObject.zig b/lib/std/Build/Step/CheckObject.zig index e1597d601b..3be9cdc83c 100644 --- a/lib/std/Build/Step/CheckObject.zig +++ b/lib/std/Build/Step/CheckObject.zig @@ -246,10 +246,12 @@ const ComputeCompareExpected = struct { }; const Check = struct { + kind: Kind, actions: std.ArrayList(Action), - fn create(allocator: Allocator) Check { + fn create(allocator: Allocator, kind: Kind) Check { return .{ + .kind = kind, .actions = std.ArrayList(Action).init(allocator), }; } @@ -289,15 +291,30 @@ const Check = struct { .expected = expected, }) catch @panic("OOM"); } + + const Kind = enum { + headers, + symtab, + indirect_symtab, + dynamic_symtab, + archive_symtab, + dynamic_section, + dyld_rebase, + dyld_bind, + dyld_weak_bind, + dyld_lazy_bind, + exports, + compute_compare, + }; }; /// Creates a new empty sequence of actions. -pub fn checkStart(self: *CheckObject) void { - const new_check = Check.create(self.step.owner.allocator); +fn checkStart(self: *CheckObject, kind: Check.Kind) void { + const new_check = Check.create(self.step.owner.allocator, kind); self.checks.append(new_check) catch @panic("OOM"); } -/// Adds an exact match phrase to the latest created Check with `CheckObject.checkStart()`. +/// Adds an exact match phrase to the latest created Check. pub fn checkExact(self: *CheckObject, phrase: []const u8) void { self.checkExactInner(phrase, null); } @@ -314,7 +331,7 @@ fn checkExactInner(self: *CheckObject, phrase: []const u8, file_source: ?std.Bui last.exact(.{ .string = self.step.owner.dupe(phrase), .file_source = file_source }); } -/// Adds a fuzzy match phrase to the latest created Check with `CheckObject.checkStart()`. +/// Adds a fuzzy match phrase to the latest created Check. pub fn checkContains(self: *CheckObject, phrase: []const u8) void { self.checkContainsInner(phrase, null); } @@ -331,8 +348,7 @@ fn checkContainsInner(self: *CheckObject, phrase: []const u8, file_source: ?std. last.contains(.{ .string = self.step.owner.dupe(phrase), .file_source = file_source }); } -/// Adds an exact match phrase with variable extractor to the latest created Check -/// with `CheckObject.checkStart()`. +/// Adds an exact match phrase with variable extractor to the latest created Check. pub fn checkExtract(self: *CheckObject, phrase: []const u8) void { self.checkExtractInner(phrase, null); } @@ -349,7 +365,7 @@ fn checkExtractInner(self: *CheckObject, phrase: []const u8, file_source: ?std.B last.extract(.{ .string = self.step.owner.dupe(phrase), .file_source = file_source }); } -/// Adds another searched phrase to the latest created Check with `CheckObject.checkStart(...)` +/// Adds another searched phrase to the latest created Check /// however ensures there is no matching phrase in the output. pub fn checkNotPresent(self: *CheckObject, phrase: []const u8) void { self.checkNotPresentInner(phrase, null); @@ -367,6 +383,11 @@ fn checkNotPresentInner(self: *CheckObject, phrase: []const u8, file_source: ?st last.notPresent(.{ .string = self.step.owner.dupe(phrase), .file_source = file_source }); } +/// Creates a new check checking in the file headers (section, program headers, etc.). +pub fn checkInHeaders(self: *CheckObject) void { + self.checkStart(.headers); +} + /// Creates a new check checking specifically symbol table parsed and dumped from the object /// file. pub fn checkInSymtab(self: *CheckObject) void { @@ -377,19 +398,79 @@ pub fn checkInSymtab(self: *CheckObject) void { .coff => @panic("TODO symtab for coff"), else => @panic("TODO other file formats"), }; - self.checkStart(); + self.checkStart(.symtab); + self.checkExact(label); +} + +/// Creates a new check checking specifically dyld rebase opcodes contents parsed and dumped +/// from the object file. +/// This check is target-dependent and applicable to MachO only. +pub fn checkInDyldRebase(self: *CheckObject) void { + const label = switch (self.obj_format) { + .macho => MachODumper.dyld_rebase_label, + else => @panic("Unsupported target platform"), + }; + self.checkStart(.dyld_rebase); + self.checkExact(label); +} + +/// Creates a new check checking specifically dyld bind opcodes contents parsed and dumped +/// from the object file. +/// This check is target-dependent and applicable to MachO only. +pub fn checkInDyldBind(self: *CheckObject) void { + const label = switch (self.obj_format) { + .macho => MachODumper.dyld_bind_label, + else => @panic("Unsupported target platform"), + }; + self.checkStart(.dyld_bind); + self.checkExact(label); +} + +/// Creates a new check checking specifically dyld weak bind opcodes contents parsed and dumped +/// from the object file. +/// This check is target-dependent and applicable to MachO only. +pub fn checkInDyldWeakBind(self: *CheckObject) void { + const label = switch (self.obj_format) { + .macho => MachODumper.dyld_weak_bind_label, + else => @panic("Unsupported target platform"), + }; + self.checkStart(.dyld_weak_bind); self.checkExact(label); } -/// Creates a new check checking specifically dyld_info_only contents parsed and dumped +/// Creates a new check checking specifically dyld lazy bind opcodes contents parsed and dumped /// from the object file. /// This check is target-dependent and applicable to MachO only. -pub fn checkInDyldInfo(self: *CheckObject) void { +pub fn checkInDyldLazyBind(self: *CheckObject) void { const label = switch (self.obj_format) { - .macho => MachODumper.dyld_info_label, + .macho => MachODumper.dyld_lazy_bind_label, else => @panic("Unsupported target platform"), }; - self.checkStart(); + self.checkStart(.dyld_lazy_bind); + self.checkExact(label); +} + +/// Creates a new check checking specifically exports info contents parsed and dumped +/// from the object file. +/// This check is target-dependent and applicable to MachO only. +pub fn checkInExports(self: *CheckObject) void { + const label = switch (self.obj_format) { + .macho => MachODumper.exports_label, + else => @panic("Unsupported target platform"), + }; + self.checkStart(.exports); + self.checkExact(label); +} + +/// Creates a new check checking specifically indirect symbol table parsed and dumped +/// from the object file. +/// This check is target-dependent and applicable to MachO only. +pub fn checkInIndirectSymtab(self: *CheckObject) void { + const label = switch (self.obj_format) { + .macho => MachODumper.indirect_symtab_label, + else => @panic("Unsupported target platform"), + }; + self.checkStart(.indirect_symtab); self.checkExact(label); } @@ -401,7 +482,7 @@ pub fn checkInDynamicSymtab(self: *CheckObject) void { .elf => ElfDumper.dynamic_symtab_label, else => @panic("Unsupported target platform"), }; - self.checkStart(); + self.checkStart(.dynamic_symtab); self.checkExact(label); } @@ -413,7 +494,7 @@ pub fn checkInDynamicSection(self: *CheckObject) void { .elf => ElfDumper.dynamic_section_label, else => @panic("Unsupported target platform"), }; - self.checkStart(); + self.checkStart(.dynamic_section); self.checkExact(label); } @@ -424,7 +505,7 @@ pub fn checkInArchiveSymtab(self: *CheckObject) void { .elf => ElfDumper.archive_symtab_label, else => @panic("TODO other file formats"), }; - self.checkStart(); + self.checkStart(.archive_symtab); self.checkExact(label); } @@ -436,7 +517,7 @@ pub fn checkComputeCompare( program: []const u8, expected: ComputeCompareExpected, ) void { - var new_check = Check.create(self.step.owner.allocator); + var new_check = Check.create(self.step.owner.allocator, .compute_compare); new_check.computeCmp(.{ .string = self.step.owner.dupe(program) }, expected); self.checks.append(new_check) catch @panic("OOM"); } @@ -457,17 +538,35 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { null, ) catch |err| return step.fail("unable to read '{s}': {s}", .{ src_path, @errorName(err) }); - const output = switch (self.obj_format) { - .macho => try MachODumper.parseAndDump(step, contents), - .elf => try ElfDumper.parseAndDump(step, contents), - .coff => @panic("TODO coff parser"), - .wasm => try WasmDumper.parseAndDump(step, contents), - else => unreachable, - }; - var vars = std.StringHashMap(u64).init(gpa); - for (self.checks.items) |chk| { + if (chk.kind == .compute_compare) { + assert(chk.actions.items.len == 1); + const act = chk.actions.items[0]; + assert(act.tag == .compute_cmp); + const res = act.computeCmp(b, step, vars) catch |err| switch (err) { + error.UnknownVariable => return step.fail("Unknown variable", .{}), + else => |e| return e, + }; + if (!res) { + return step.fail( + \\ + \\========= comparison failed for action: =========== + \\{s} {} + \\=================================================== + , .{ act.phrase.resolve(b, step), act.expected.? }); + } + continue; + } + + const output = switch (self.obj_format) { + .macho => try MachODumper.parseAndDump(step, chk.kind, contents), + .elf => try ElfDumper.parseAndDump(step, chk.kind, contents), + .coff => return step.fail("TODO coff parser", .{}), + .wasm => try WasmDumper.parseAndDump(step, chk.kind, contents), + else => unreachable, + }; + var it = mem.tokenizeAny(u8, output, "\r\n"); for (chk.actions.items) |act| { switch (act.tag) { @@ -485,6 +584,7 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { , .{ act.phrase.resolve(b, step), output }); } }, + .contains => { while (it.next()) |line| { if (act.contains(b, step, line)) break; @@ -499,6 +599,7 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { , .{ act.phrase.resolve(b, step), output }); } }, + .not_present => { while (it.next()) |line| { if (act.notPresent(b, step, line)) continue; @@ -512,6 +613,7 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { , .{ act.phrase.resolve(b, step), output }); } }, + .extract => { while (it.next()) |line| { if (try act.extract(b, step, line, &vars)) break; @@ -526,28 +628,8 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { , .{ act.phrase.resolve(b, step), output }); } }, - .compute_cmp => { - const res = act.computeCmp(b, step, vars) catch |err| switch (err) { - error.UnknownVariable => { - return step.fail( - \\========= from parsed file: ===================== - \\{s} - \\================================================= - , .{output}); - }, - else => |e| return e, - }; - if (!res) { - return step.fail( - \\ - \\========= comparison failed for action: =========== - \\{s} {} - \\========= from parsed file: ======================= - \\{s} - \\=================================================== - , .{ act.phrase.resolve(b, step), act.expected.?, output }); - } - }, + + .compute_cmp => unreachable, } } } @@ -555,15 +637,26 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { const MachODumper = struct { const LoadCommandIterator = macho.LoadCommandIterator; - const dyld_info_label = "dyld info data"; + const dyld_rebase_label = "dyld rebase data"; + const dyld_bind_label = "dyld bind data"; + const dyld_weak_bind_label = "dyld weak bind data"; + const dyld_lazy_bind_label = "dyld lazy bind data"; + const exports_label = "exports data"; const symtab_label = "symbol table"; + const indirect_symtab_label = "indirect symbol table"; const Symtab = struct { - symbols: []align(1) const macho.nlist_64, - strings: []const u8, + symbols: []align(1) const macho.nlist_64 = &[0]macho.nlist_64{}, + strings: []const u8 = &[0]u8{}, + indirect_symbols: []align(1) const u32 = &[0]u32{}, + + fn getString(symtab: Symtab, off: u32) []const u8 { + assert(off < symtab.strings.len); + return mem.sliceTo(@as([*:0]const u8, @ptrCast(symtab.strings.ptr + off)), 0); + } }; - fn parseAndDump(step: *Step, bytes: []const u8) ![]const u8 { + fn parseAndDump(step: *Step, kind: Check.Kind, bytes: []const u8) ![]const u8 { const gpa = step.owner.allocator; var stream = std.io.fixedBufferStream(bytes); const reader = stream.reader(); @@ -576,7 +669,7 @@ const MachODumper = struct { var output = std.ArrayList(u8).init(gpa); const writer = output.writer(); - var symtab: ?Symtab = null; + var symtab: Symtab = .{}; var segments = std.ArrayList(macho.segment_command_64).init(gpa); defer segments.deinit(); var sections = std.ArrayList(macho.section_64).init(gpa); @@ -586,82 +679,129 @@ const MachODumper = struct { var text_seg: ?u8 = null; var dyld_info_lc: ?macho.dyld_info_command = null; - try dumpHeader(hdr, writer); + { + var it: LoadCommandIterator = .{ + .ncmds = hdr.ncmds, + .buffer = bytes[@sizeOf(macho.mach_header_64)..][0..hdr.sizeofcmds], + }; + var i: usize = 0; + while (it.next()) |cmd| { + switch (cmd.cmd()) { + .SEGMENT_64 => { + const seg = cmd.cast(macho.segment_command_64).?; + try sections.ensureUnusedCapacity(seg.nsects); + for (cmd.getSections()) |sect| { + sections.appendAssumeCapacity(sect); + } + const seg_id: u8 = @intCast(segments.items.len); + try segments.append(seg); + if (mem.eql(u8, seg.segName(), "__TEXT")) { + text_seg = seg_id; + } + }, + .SYMTAB => { + const lc = cmd.cast(macho.symtab_command).?; + const symbols = @as([*]align(1) const macho.nlist_64, @ptrCast(bytes.ptr + lc.symoff))[0..lc.nsyms]; + const strings = bytes[lc.stroff..][0..lc.strsize]; + symtab.symbols = symbols; + symtab.strings = strings; + }, + .DYSYMTAB => { + const lc = cmd.cast(macho.dysymtab_command).?; + const indexes = @as([*]align(1) const u32, @ptrCast(bytes.ptr + lc.indirectsymoff))[0..lc.nindirectsyms]; + symtab.indirect_symbols = indexes; + }, + .LOAD_DYLIB, + .LOAD_WEAK_DYLIB, + .REEXPORT_DYLIB, + => { + try imports.append(cmd.getDylibPathName()); + }, + .DYLD_INFO_ONLY => { + dyld_info_lc = cmd.cast(macho.dyld_info_command).?; + }, + else => {}, + } - var it: LoadCommandIterator = .{ - .ncmds = hdr.ncmds, - .buffer = bytes[@sizeOf(macho.mach_header_64)..][0..hdr.sizeofcmds], - }; - var i: usize = 0; - while (it.next()) |cmd| { - switch (cmd.cmd()) { - .SEGMENT_64 => { - const seg = cmd.cast(macho.segment_command_64).?; - try sections.ensureUnusedCapacity(seg.nsects); - for (cmd.getSections()) |sect| { - sections.appendAssumeCapacity(sect); - } - const seg_id: u8 = @intCast(segments.items.len); - try segments.append(seg); - if (mem.eql(u8, seg.segName(), "__TEXT")) { - text_seg = seg_id; - } - }, - .SYMTAB => { - const lc = cmd.cast(macho.symtab_command).?; - const symbols = @as([*]align(1) const macho.nlist_64, @ptrCast(bytes.ptr + lc.symoff))[0..lc.nsyms]; - const strings = bytes[lc.stroff..][0..lc.strsize]; - symtab = .{ .symbols = symbols, .strings = strings }; - }, - .LOAD_DYLIB, - .LOAD_WEAK_DYLIB, - .REEXPORT_DYLIB, - => { - try imports.append(cmd.getDylibPathName()); - }, - .DYLD_INFO_ONLY => { - dyld_info_lc = cmd.cast(macho.dyld_info_command).?; - }, - else => {}, + i += 1; } + } - try dumpLoadCommand(cmd, i, writer); - try writer.writeByte('\n'); + switch (kind) { + .headers => { + try dumpHeader(hdr, writer); - i += 1; - } + var it: LoadCommandIterator = .{ + .ncmds = hdr.ncmds, + .buffer = bytes[@sizeOf(macho.mach_header_64)..][0..hdr.sizeofcmds], + }; + var i: usize = 0; + while (it.next()) |cmd| { + try dumpLoadCommand(cmd, i, writer); + try writer.writeByte('\n'); - if (symtab) |stab| { - try dumpSymtab(sections.items, imports.items, stab, writer); - } + i += 1; + } + }, - if (dyld_info_lc) |lc| { - try writer.writeAll(dyld_info_label ++ "\n"); - if (lc.rebase_size > 0) { - const data = bytes[lc.rebase_off..][0..lc.rebase_size]; - try writer.writeAll("rebase info\n"); - try dumpRebaseInfo(gpa, data, segments.items, writer); - } - if (lc.bind_size > 0) { - const data = bytes[lc.bind_off..][0..lc.bind_size]; - try writer.writeAll("bind info\n"); - try dumpBindInfo(gpa, data, segments.items, imports.items, writer); - } - if (lc.weak_bind_size > 0) { - const data = bytes[lc.weak_bind_off..][0..lc.weak_bind_size]; - try writer.writeAll("weak bind info\n"); - try dumpBindInfo(gpa, data, segments.items, imports.items, writer); - } - if (lc.lazy_bind_size > 0) { - const data = bytes[lc.lazy_bind_off..][0..lc.lazy_bind_size]; - try writer.writeAll("lazy bind info\n"); - try dumpBindInfo(gpa, data, segments.items, imports.items, writer); - } - if (lc.export_size > 0) { - const data = bytes[lc.export_off..][0..lc.export_size]; - try writer.writeAll("exports\n"); - try dumpExportsTrie(gpa, data, segments.items[text_seg.?], writer); - } + .symtab => if (symtab.symbols.len > 0) { + try dumpSymtab(sections.items, imports.items, symtab, writer); + } else return step.fail("no symbol table found", .{}), + + .indirect_symtab => if (symtab.symbols.len > 0 and symtab.indirect_symbols.len > 0) { + try dumpIndirectSymtab(gpa, sections.items, symtab, writer); + } else return step.fail("no indirect symbol table found", .{}), + + .dyld_rebase, + .dyld_bind, + .dyld_weak_bind, + .dyld_lazy_bind, + => { + if (dyld_info_lc == null) return step.fail("no dyld info found", .{}); + const lc = dyld_info_lc.?; + + switch (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"); + try dumpRebaseInfo(gpa, data, segments.items, writer); + } else return step.fail("no rebase data found", .{}), + + .dyld_bind => if (lc.bind_size > 0) { + const data = bytes[lc.bind_off..][0..lc.bind_size]; + try writer.writeAll(dyld_bind_label ++ "\n"); + try dumpBindInfo(gpa, data, segments.items, imports.items, writer); + } else return step.fail("no bind data found", .{}), + + .dyld_weak_bind => if (lc.weak_bind_size > 0) { + const data = bytes[lc.weak_bind_off..][0..lc.weak_bind_size]; + try writer.writeAll(dyld_weak_bind_label ++ "\n"); + try dumpBindInfo(gpa, data, segments.items, imports.items, writer); + } else return step.fail("no weak bind data found", .{}), + + .dyld_lazy_bind => if (lc.lazy_bind_size > 0) { + const data = bytes[lc.lazy_bind_off..][0..lc.lazy_bind_size]; + try writer.writeAll(dyld_lazy_bind_label ++ "\n"); + try dumpBindInfo(gpa, data, segments.items, imports.items, writer); + } else return step.fail("no lazy bind data found", .{}), + + else => unreachable, + } + }, + + .exports => blk: { + if (dyld_info_lc) |lc| { + if (lc.export_size > 0) { + const data = bytes[lc.export_off..][0..lc.export_size]; + try writer.writeAll(exports_label ++ "\n"); + try dumpExportsTrie(gpa, data, segments.items[text_seg.?], writer); + break :blk; + } + } + return step.fail("no exports data found", .{}); + }, + + else => return step.fail("invalid check kind for MachO file format: {s}", .{@tagName(kind)}), } return output.toOwnedSlice(); @@ -971,7 +1111,7 @@ const MachODumper = struct { for (symtab.symbols) |sym| { if (sym.stab()) continue; - const sym_name = mem.sliceTo(@as([*:0]const u8, @ptrCast(symtab.strings.ptr + sym.n_strx)), 0); + const sym_name = symtab.getString(sym.n_strx); if (sym.sect()) { const sect = sections[sym.n_sect - 1]; try writer.print("{x} ({s},{s})", .{ @@ -1021,6 +1161,52 @@ const MachODumper = struct { } } + fn dumpIndirectSymtab( + gpa: Allocator, + sections: []const macho.section_64, + symtab: Symtab, + writer: anytype, + ) !void { + try writer.writeAll(indirect_symtab_label ++ "\n"); + + var sects = std.ArrayList(macho.section_64).init(gpa); + defer sects.deinit(); + try sects.ensureUnusedCapacity(3); + + for (sections) |sect| { + if (mem.eql(u8, sect.sectName(), "__stubs")) sects.appendAssumeCapacity(sect); + if (mem.eql(u8, sect.sectName(), "__got")) sects.appendAssumeCapacity(sect); + if (mem.eql(u8, sect.sectName(), "__la_symbol_ptr")) sects.appendAssumeCapacity(sect); + } + + const sortFn = struct { + fn sortFn(ctx: void, lhs: macho.section_64, rhs: macho.section_64) bool { + _ = ctx; + return lhs.reserved1 < rhs.reserved1; + } + }.sortFn; + mem.sort(macho.section_64, sects.items, {}, sortFn); + + var i: usize = 0; + while (i < sects.items.len) : (i += 1) { + const sect = sects.items[i]; + const start = sect.reserved1; + const end = if (i + 1 >= sects.items.len) symtab.indirect_symbols.len else sects.items[i + 1].reserved1; + const entry_size = blk: { + if (mem.eql(u8, sect.sectName(), "__stubs")) break :blk sect.reserved2; + break :blk @sizeOf(u64); + }; + + try writer.print("{s},{s}\n", .{ sect.segName(), sect.sectName() }); + try writer.print("nentries {d}\n", .{end - start}); + for (symtab.indirect_symbols[start..end], 0..) |index, j| { + const sym = symtab.symbols[index]; + const addr = sect.addr + entry_size * j; + try writer.print("0x{x} {d} {s}\n", .{ addr, index, symtab.getString(sym.n_strx) }); + } + } + } + fn dumpRebaseInfo( gpa: Allocator, data: []const u8, @@ -1443,15 +1629,15 @@ const ElfDumper = struct { const dynamic_section_label = "dynamic section"; const archive_symtab_label = "archive symbol table"; - fn parseAndDump(step: *Step, bytes: []const u8) ![]const u8 { - const gpa = step.owner.allocator; - return parseAndDumpArchive(gpa, bytes) catch |err| switch (err) { - error.InvalidArchiveMagicNumber => try parseAndDumpObject(gpa, bytes), + 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), else => |e| return e, }; } - fn parseAndDumpArchive(gpa: Allocator, bytes: []const u8) ![]const u8 { + fn parseAndDumpArchive(step: *Step, kind: Check.Kind, bytes: []const u8) ![]const u8 { + const gpa = step.owner.allocator; var stream = std.io.fixedBufferStream(bytes); const reader = stream.reader(); @@ -1512,8 +1698,15 @@ const ElfDumper = struct { var output = std.ArrayList(u8).init(gpa); const writer = output.writer(); - try ctx.dumpSymtab(writer); - try ctx.dumpObjects(writer); + switch (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); + } else return step.fail("empty archive", .{}), + } return output.toOwnedSlice(); } @@ -1555,8 +1748,6 @@ const ElfDumper = struct { } fn dumpSymtab(ctx: ArchiveContext, writer: anytype) !void { - if (ctx.symtab.items.len == 0) return; - var files = std.AutoHashMap(usize, []const u8).init(ctx.gpa); defer files.deinit(); try files.ensureUnusedCapacity(@intCast(ctx.objects.items.len)); @@ -1590,10 +1781,10 @@ const ElfDumper = struct { } } - fn dumpObjects(ctx: ArchiveContext, writer: anytype) !void { + fn dumpObjects(ctx: ArchiveContext, step: *Step, kind: Check.Kind, writer: anytype) !void { for (ctx.objects.items) |object| { try writer.print("object {s}\n", .{object.name}); - const output = try parseAndDumpObject(ctx.gpa, ctx.data[object.off..][0..object.len]); + const output = try parseAndDumpObject(step, kind, ctx.data[object.off..][0..object.len]); defer ctx.gpa.free(output); try writer.print("{s}\n", .{output}); } @@ -1611,7 +1802,8 @@ const ElfDumper = struct { }; }; - fn parseAndDumpObject(gpa: Allocator, bytes: []const u8) ![]const u8 { + fn parseAndDumpObject(step: *Step, kind: Check.Kind, bytes: []const u8) ![]const u8 { + const gpa = step.owner.allocator; var stream = std.io.fixedBufferStream(bytes); const reader = stream.reader(); @@ -1663,12 +1855,27 @@ const ElfDumper = struct { var output = std.ArrayList(u8).init(gpa); const writer = output.writer(); - try ctx.dumpHeader(writer); - try ctx.dumpShdrs(writer); - try ctx.dumpPhdrs(writer); - try ctx.dumpDynamicSection(writer); - try ctx.dumpSymtab(.symtab, writer); - try ctx.dumpSymtab(.dysymtab, writer); + switch (kind) { + .headers => { + try ctx.dumpHeader(writer); + try ctx.dumpShdrs(writer); + try ctx.dumpPhdrs(writer); + }, + + .symtab => if (ctx.symtab.symbols.len > 0) { + try ctx.dumpSymtab(.symtab, writer); + } else return step.fail("no symbol table found", .{}), + + .dynamic_symtab => if (ctx.dysymtab.symbols.len > 0) { + try ctx.dumpSymtab(.dysymtab, writer); + } else return step.fail("no dynamic symbol table found", .{}), + + .dynamic_section => if (ctx.getSectionByName(".dynamic")) |shndx| { + 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)}), + } return output.toOwnedSlice(); } @@ -1680,8 +1887,8 @@ const ElfDumper = struct { shdrs: []align(1) const elf.Elf64_Shdr, phdrs: []align(1) const elf.Elf64_Phdr, shstrtab: []const u8, - symtab: ?Symtab = null, - dysymtab: ?Symtab = null, + symtab: Symtab = .{}, + dysymtab: Symtab = .{}, fn dumpHeader(ctx: ObjectContext, writer: anytype) !void { try writer.writeAll("header\n"); @@ -1745,8 +1952,7 @@ const ElfDumper = struct { } } - fn dumpDynamicSection(ctx: ObjectContext, writer: anytype) !void { - const shndx = ctx.getSectionByName(".dynamic") orelse return; + fn dumpDynamicSection(ctx: ObjectContext, shndx: usize, writer: anytype) !void { const shdr = ctx.shdrs[shndx]; const strtab = ctx.getSectionContents(shdr.sh_link); const data = ctx.getSectionContents(shndx); @@ -1888,7 +2094,7 @@ const ElfDumper = struct { const symtab = switch (@"type") { .symtab => ctx.symtab, .dysymtab => ctx.dysymtab, - } orelse return; + }; try writer.writeAll(switch (@"type") { .symtab => symtab_label, @@ -1986,8 +2192,8 @@ const ElfDumper = struct { }; const Symtab = struct { - symbols: []align(1) const elf.Elf64_Sym, - strings: []const u8, + symbols: []align(1) const elf.Elf64_Sym = &[0]elf.Elf64_Sym{}, + strings: []const u8 = &[0]u8{}, fn get(st: Symtab, index: usize) ?elf.Elf64_Sym { if (index >= st.symbols.len) return null; @@ -2090,7 +2296,7 @@ const ElfDumper = struct { const WasmDumper = struct { const symtab_label = "symbols"; - fn parseAndDump(step: *Step, bytes: []const u8) ![]const u8 { + fn parseAndDump(step: *Step, kind: Check.Kind, bytes: []const u8) ![]const u8 { const gpa = step.owner.allocator; var fbs = std.io.fixedBufferStream(bytes); const reader = fbs.reader(); @@ -2107,15 +2313,21 @@ const WasmDumper = struct { errdefer output.deinit(); const writer = output.writer(); - while (reader.readByte()) |current_byte| { - const section = std.meta.intToEnum(std.wasm.Section, current_byte) catch { - return step.fail("Found invalid section id '{d}'", .{current_byte}); - }; + switch (kind) { + .headers => { + while (reader.readByte()) |current_byte| { + const section = std.meta.intToEnum(std.wasm.Section, current_byte) catch { + return step.fail("Found invalid section id '{d}'", .{current_byte}); + }; + + const section_length = try std.leb.readULEB128(u32, reader); + try parseAndDumpSection(step, section, bytes[fbs.pos..][0..section_length], writer); + fbs.pos += section_length; + } else |_| {} // reached end of stream + }, - const section_length = try std.leb.readULEB128(u32, reader); - try parseAndDumpSection(step, section, bytes[fbs.pos..][0..section_length], writer); - fbs.pos += section_length; - } else |_| {} // reached end of stream + else => return step.fail("invalid check kind for Wasm file format: {s}", .{@tagName(kind)}), + } return output.toOwnedSlice(); } |
