authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2023-12-13 11:22:46+01:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2023-12-13 11:22:46+01:00
log2492488501ee04e132cb552fd11a3025990ea047
treef4f68b8b7a8a32e417ad5244633896544bf02ce7
parenta38af5f542199378483aba9f4598634ebf012b7b

lib/std/Build/CheckObject: introduce scoped checks; implement for MachO


1 files changed, 234 insertions(+), 122 deletions(-)

lib/std/Build/Step/CheckObject.zig+234-122
...@@ -246,10 +246,12 @@ const ComputeCompareExpected = struct {...@@ -246,10 +246,12 @@ const ComputeCompareExpected = struct {
246};246};
247247
248const Check = struct {248const Check = struct {
249 kind: Kind,
249 actions: std.ArrayList(Action),250 actions: std.ArrayList(Action),
250251
251 fn create(allocator: Allocator) Check {252 fn create(allocator: Allocator, kind: Kind) Check {
252 return .{253 return .{
254 .kind = kind,
253 .actions = std.ArrayList(Action).init(allocator),255 .actions = std.ArrayList(Action).init(allocator),
254 };256 };
255 }257 }
...@@ -289,15 +291,26 @@ const Check = struct {...@@ -289,15 +291,26 @@ const Check = struct {
289 .expected = expected,291 .expected = expected,
290 }) catch @panic("OOM");292 }) catch @panic("OOM");
291 }293 }
294
295 const Kind = enum {
296 headers,
297 symtab,
298 indirect_symtab,
299 dynamic_symtab,
300 archive_symtab,
301 dynamic_section,
302 dyld_info,
303 compute_compare,
304 };
292};305};
293306
294/// Creates a new empty sequence of actions.307/// Creates a new empty sequence of actions.
295pub fn checkStart(self: *CheckObject) void {308fn checkStart(self: *CheckObject, kind: Check.Kind) void {
296 const new_check = Check.create(self.step.owner.allocator);309 const new_check = Check.create(self.step.owner.allocator, kind);
297 self.checks.append(new_check) catch @panic("OOM");310 self.checks.append(new_check) catch @panic("OOM");
298}311}
299312
300/// Adds an exact match phrase to the latest created Check with `CheckObject.checkStart()`.313/// Adds an exact match phrase to the latest created Check.
301pub fn checkExact(self: *CheckObject, phrase: []const u8) void {314pub fn checkExact(self: *CheckObject, phrase: []const u8) void {
302 self.checkExactInner(phrase, null);315 self.checkExactInner(phrase, null);
303}316}
...@@ -314,7 +327,7 @@ fn checkExactInner(self: *CheckObject, phrase: []const u8, file_source: ?std.Bui...@@ -314,7 +327,7 @@ fn checkExactInner(self: *CheckObject, phrase: []const u8, file_source: ?std.Bui
314 last.exact(.{ .string = self.step.owner.dupe(phrase), .file_source = file_source });327 last.exact(.{ .string = self.step.owner.dupe(phrase), .file_source = file_source });
315}328}
316329
317/// Adds a fuzzy match phrase to the latest created Check with `CheckObject.checkStart()`.330/// Adds a fuzzy match phrase to the latest created Check.
318pub fn checkContains(self: *CheckObject, phrase: []const u8) void {331pub fn checkContains(self: *CheckObject, phrase: []const u8) void {
319 self.checkContainsInner(phrase, null);332 self.checkContainsInner(phrase, null);
320}333}
...@@ -331,8 +344,7 @@ fn checkContainsInner(self: *CheckObject, phrase: []const u8, file_source: ?std....@@ -331,8 +344,7 @@ fn checkContainsInner(self: *CheckObject, phrase: []const u8, file_source: ?std.
331 last.contains(.{ .string = self.step.owner.dupe(phrase), .file_source = file_source });344 last.contains(.{ .string = self.step.owner.dupe(phrase), .file_source = file_source });
332}345}
333346
334/// Adds an exact match phrase with variable extractor to the latest created Check347/// Adds an exact match phrase with variable extractor to the latest created Check.
335/// with `CheckObject.checkStart()`.
336pub fn checkExtract(self: *CheckObject, phrase: []const u8) void {348pub fn checkExtract(self: *CheckObject, phrase: []const u8) void {
337 self.checkExtractInner(phrase, null);349 self.checkExtractInner(phrase, null);
338}350}
...@@ -349,7 +361,7 @@ fn checkExtractInner(self: *CheckObject, phrase: []const u8, file_source: ?std.B...@@ -349,7 +361,7 @@ fn checkExtractInner(self: *CheckObject, phrase: []const u8, file_source: ?std.B
349 last.extract(.{ .string = self.step.owner.dupe(phrase), .file_source = file_source });361 last.extract(.{ .string = self.step.owner.dupe(phrase), .file_source = file_source });
350}362}
351363
352/// Adds another searched phrase to the latest created Check with `CheckObject.checkStart(...)`364/// Adds another searched phrase to the latest created Check
353/// however ensures there is no matching phrase in the output.365/// however ensures there is no matching phrase in the output.
354pub fn checkNotPresent(self: *CheckObject, phrase: []const u8) void {366pub fn checkNotPresent(self: *CheckObject, phrase: []const u8) void {
355 self.checkNotPresentInner(phrase, null);367 self.checkNotPresentInner(phrase, null);
...@@ -367,6 +379,11 @@ fn checkNotPresentInner(self: *CheckObject, phrase: []const u8, file_source: ?st...@@ -367,6 +379,11 @@ fn checkNotPresentInner(self: *CheckObject, phrase: []const u8, file_source: ?st
367 last.notPresent(.{ .string = self.step.owner.dupe(phrase), .file_source = file_source });379 last.notPresent(.{ .string = self.step.owner.dupe(phrase), .file_source = file_source });
368}380}
369381
382/// Creates a new check checking in the file headers (section, program headers, etc.).
383pub fn checkInHeaders(self: *CheckObject) void {
384 self.checkStart(.headers);
385}
386
370/// Creates a new check checking specifically symbol table parsed and dumped from the object387/// Creates a new check checking specifically symbol table parsed and dumped from the object
371/// file.388/// file.
372pub fn checkInSymtab(self: *CheckObject) void {389pub fn checkInSymtab(self: *CheckObject) void {
...@@ -377,7 +394,7 @@ pub fn checkInSymtab(self: *CheckObject) void {...@@ -377,7 +394,7 @@ pub fn checkInSymtab(self: *CheckObject) void {
377 .coff => @panic("TODO symtab for coff"),394 .coff => @panic("TODO symtab for coff"),
378 else => @panic("TODO other file formats"),395 else => @panic("TODO other file formats"),
379 };396 };
380 self.checkStart();397 self.checkStart(.symtab);
381 self.checkExact(label);398 self.checkExact(label);
382}399}
383400
...@@ -389,7 +406,19 @@ pub fn checkInDyldInfo(self: *CheckObject) void {...@@ -389,7 +406,19 @@ pub fn checkInDyldInfo(self: *CheckObject) void {
389 .macho => MachODumper.dyld_info_label,406 .macho => MachODumper.dyld_info_label,
390 else => @panic("Unsupported target platform"),407 else => @panic("Unsupported target platform"),
391 };408 };
392 self.checkStart();409 self.checkStart(.dyld_info);
410 self.checkExact(label);
411}
412
413/// Creates a new check checking specifically indirect symbol table parsed and dumped
414/// from the object file.
415/// This check is target-dependent and applicable to MachO only.
416pub fn checkInIndirectSymtab(self: *CheckObject) void {
417 const label = switch (self.obj_format) {
418 .macho => MachODumper.indirect_symtab_label,
419 else => @panic("Unsupported target platform"),
420 };
421 self.checkStart(.indirect_symtab);
393 self.checkExact(label);422 self.checkExact(label);
394}423}
395424
...@@ -401,7 +430,7 @@ pub fn checkInDynamicSymtab(self: *CheckObject) void {...@@ -401,7 +430,7 @@ pub fn checkInDynamicSymtab(self: *CheckObject) void {
401 .elf => ElfDumper.dynamic_symtab_label,430 .elf => ElfDumper.dynamic_symtab_label,
402 else => @panic("Unsupported target platform"),431 else => @panic("Unsupported target platform"),
403 };432 };
404 self.checkStart();433 self.checkStart(.dynamic_symtab);
405 self.checkExact(label);434 self.checkExact(label);
406}435}
407436
...@@ -413,7 +442,7 @@ pub fn checkInDynamicSection(self: *CheckObject) void {...@@ -413,7 +442,7 @@ pub fn checkInDynamicSection(self: *CheckObject) void {
413 .elf => ElfDumper.dynamic_section_label,442 .elf => ElfDumper.dynamic_section_label,
414 else => @panic("Unsupported target platform"),443 else => @panic("Unsupported target platform"),
415 };444 };
416 self.checkStart();445 self.checkStart(.dynamic_section);
417 self.checkExact(label);446 self.checkExact(label);
418}447}
419448
...@@ -424,7 +453,7 @@ pub fn checkInArchiveSymtab(self: *CheckObject) void {...@@ -424,7 +453,7 @@ pub fn checkInArchiveSymtab(self: *CheckObject) void {
424 .elf => ElfDumper.archive_symtab_label,453 .elf => ElfDumper.archive_symtab_label,
425 else => @panic("TODO other file formats"),454 else => @panic("TODO other file formats"),
426 };455 };
427 self.checkStart();456 self.checkStart(.archive_symtab);
428 self.checkExact(label);457 self.checkExact(label);
429}458}
430459
...@@ -436,7 +465,7 @@ pub fn checkComputeCompare(...@@ -436,7 +465,7 @@ pub fn checkComputeCompare(
436 program: []const u8,465 program: []const u8,
437 expected: ComputeCompareExpected,466 expected: ComputeCompareExpected,
438) void {467) void {
439 var new_check = Check.create(self.step.owner.allocator);468 var new_check = Check.create(self.step.owner.allocator, .compute_compare);
440 new_check.computeCmp(.{ .string = self.step.owner.dupe(program) }, expected);469 new_check.computeCmp(.{ .string = self.step.owner.dupe(program) }, expected);
441 self.checks.append(new_check) catch @panic("OOM");470 self.checks.append(new_check) catch @panic("OOM");
442}471}
...@@ -457,17 +486,35 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {...@@ -457,17 +486,35 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
457 null,486 null,
458 ) catch |err| return step.fail("unable to read '{s}': {s}", .{ src_path, @errorName(err) });487 ) catch |err| return step.fail("unable to read '{s}': {s}", .{ src_path, @errorName(err) });
459488
460 const output = switch (self.obj_format) {
461 .macho => try MachODumper.parseAndDump(step, contents),
462 .elf => try ElfDumper.parseAndDump(step, contents),
463 .coff => @panic("TODO coff parser"),
464 .wasm => try WasmDumper.parseAndDump(step, contents),
465 else => unreachable,
466 };
467
468 var vars = std.StringHashMap(u64).init(gpa);489 var vars = std.StringHashMap(u64).init(gpa);
469
470 for (self.checks.items) |chk| {490 for (self.checks.items) |chk| {
491 if (chk.kind == .compute_compare) {
492 assert(chk.actions.items.len == 1);
493 const act = chk.actions.items[0];
494 assert(act.tag == .compute_cmp);
495 const res = act.computeCmp(b, step, vars) catch |err| switch (err) {
496 error.UnknownVariable => return step.fail("Unknown variable", .{}),
497 else => |e| return e,
498 };
499 if (!res) {
500 return step.fail(
501 \\
502 \\========= comparison failed for action: ===========
503 \\{s} {}
504 \\===================================================
505 , .{ act.phrase.resolve(b, step), act.expected.? });
506 }
507 continue;
508 }
509
510 const output = switch (self.obj_format) {
511 .macho => try MachODumper.parseAndDump(step, chk.kind, contents),
512 .elf => try ElfDumper.parseAndDump(step, chk.kind, contents),
513 .coff => return step.fail("TODO coff parser", .{}),
514 .wasm => try WasmDumper.parseAndDump(step, chk.kind, contents),
515 else => unreachable,
516 };
517
471 var it = mem.tokenizeAny(u8, output, "\r\n");518 var it = mem.tokenizeAny(u8, output, "\r\n");
472 for (chk.actions.items) |act| {519 for (chk.actions.items) |act| {
473 switch (act.tag) {520 switch (act.tag) {
...@@ -485,6 +532,7 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {...@@ -485,6 +532,7 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
485 , .{ act.phrase.resolve(b, step), output });532 , .{ act.phrase.resolve(b, step), output });
486 }533 }
487 },534 },
535
488 .contains => {536 .contains => {
489 while (it.next()) |line| {537 while (it.next()) |line| {
490 if (act.contains(b, step, line)) break;538 if (act.contains(b, step, line)) break;
...@@ -499,6 +547,7 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {...@@ -499,6 +547,7 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
499 , .{ act.phrase.resolve(b, step), output });547 , .{ act.phrase.resolve(b, step), output });
500 }548 }
501 },549 },
550
502 .not_present => {551 .not_present => {
503 while (it.next()) |line| {552 while (it.next()) |line| {
504 if (act.notPresent(b, step, line)) continue;553 if (act.notPresent(b, step, line)) continue;
...@@ -512,6 +561,7 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {...@@ -512,6 +561,7 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
512 , .{ act.phrase.resolve(b, step), output });561 , .{ act.phrase.resolve(b, step), output });
513 }562 }
514 },563 },
564
515 .extract => {565 .extract => {
516 while (it.next()) |line| {566 while (it.next()) |line| {
517 if (try act.extract(b, step, line, &vars)) break;567 if (try act.extract(b, step, line, &vars)) break;
...@@ -526,28 +576,8 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {...@@ -526,28 +576,8 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
526 , .{ act.phrase.resolve(b, step), output });576 , .{ act.phrase.resolve(b, step), output });
527 }577 }
528 },578 },
529 .compute_cmp => {579
530 const res = act.computeCmp(b, step, vars) catch |err| switch (err) {580 .compute_cmp => unreachable,
531 error.UnknownVariable => {
532 return step.fail(
533 \\========= from parsed file: =====================
534 \\{s}
535 \\=================================================
536 , .{output});
537 },
538 else => |e| return e,
539 };
540 if (!res) {
541 return step.fail(
542 \\
543 \\========= comparison failed for action: ===========
544 \\{s} {}
545 \\========= from parsed file: =======================
546 \\{s}
547 \\===================================================
548 , .{ act.phrase.resolve(b, step), act.expected.?, output });
549 }
550 },
551 }581 }
552 }582 }
553 }583 }
...@@ -557,13 +587,20 @@ const MachODumper = struct {...@@ -557,13 +587,20 @@ const MachODumper = struct {
557 const LoadCommandIterator = macho.LoadCommandIterator;587 const LoadCommandIterator = macho.LoadCommandIterator;
558 const dyld_info_label = "dyld info data";588 const dyld_info_label = "dyld info data";
559 const symtab_label = "symbol table";589 const symtab_label = "symbol table";
590 const indirect_symtab_label = "indirect symbol table";
560591
561 const Symtab = struct {592 const Symtab = struct {
562 symbols: []align(1) const macho.nlist_64,593 symbols: []align(1) const macho.nlist_64 = &[0]macho.nlist_64{},
563 strings: []const u8,594 strings: []const u8 = &[0]u8{},
595 indirect_symbols: []align(1) const u32 = &[0]u32{},
596
597 fn getString(symtab: Symtab, off: u32) []const u8 {
598 assert(off < symtab.strings.len);
599 return mem.sliceTo(@as([*:0]const u8, @ptrCast(symtab.strings.ptr + off)), 0);
600 }
564 };601 };
565602
566 fn parseAndDump(step: *Step, bytes: []const u8) ![]const u8 {603 fn parseAndDump(step: *Step, kind: Check.Kind, bytes: []const u8) ![]const u8 {
567 const gpa = step.owner.allocator;604 const gpa = step.owner.allocator;
568 var stream = std.io.fixedBufferStream(bytes);605 var stream = std.io.fixedBufferStream(bytes);
569 const reader = stream.reader();606 const reader = stream.reader();
...@@ -576,7 +613,7 @@ const MachODumper = struct {...@@ -576,7 +613,7 @@ const MachODumper = struct {
576 var output = std.ArrayList(u8).init(gpa);613 var output = std.ArrayList(u8).init(gpa);
577 const writer = output.writer();614 const writer = output.writer();
578615
579 var symtab: ?Symtab = null;616 var symtab: Symtab = .{};
580 var segments = std.ArrayList(macho.segment_command_64).init(gpa);617 var segments = std.ArrayList(macho.segment_command_64).init(gpa);
581 defer segments.deinit();618 defer segments.deinit();
582 var sections = std.ArrayList(macho.section_64).init(gpa);619 var sections = std.ArrayList(macho.section_64).init(gpa);
...@@ -586,82 +623,109 @@ const MachODumper = struct {...@@ -586,82 +623,109 @@ const MachODumper = struct {
586 var text_seg: ?u8 = null;623 var text_seg: ?u8 = null;
587 var dyld_info_lc: ?macho.dyld_info_command = null;624 var dyld_info_lc: ?macho.dyld_info_command = null;
588625
589 try dumpHeader(hdr, writer);626 {
627 var it: LoadCommandIterator = .{
628 .ncmds = hdr.ncmds,
629 .buffer = bytes[@sizeOf(macho.mach_header_64)..][0..hdr.sizeofcmds],
630 };
631 var i: usize = 0;
632 while (it.next()) |cmd| {
633 switch (cmd.cmd()) {
634 .SEGMENT_64 => {
635 const seg = cmd.cast(macho.segment_command_64).?;
636 try sections.ensureUnusedCapacity(seg.nsects);
637 for (cmd.getSections()) |sect| {
638 sections.appendAssumeCapacity(sect);
639 }
640 const seg_id: u8 = @intCast(segments.items.len);
641 try segments.append(seg);
642 if (mem.eql(u8, seg.segName(), "__TEXT")) {
643 text_seg = seg_id;
644 }
645 },
646 .SYMTAB => {
647 const lc = cmd.cast(macho.symtab_command).?;
648 const symbols = @as([*]align(1) const macho.nlist_64, @ptrCast(bytes.ptr + lc.symoff))[0..lc.nsyms];
649 const strings = bytes[lc.stroff..][0..lc.strsize];
650 symtab.symbols = symbols;
651 symtab.strings = strings;
652 },
653 .DYSYMTAB => {
654 const lc = cmd.cast(macho.dysymtab_command).?;
655 const indexes = @as([*]align(1) const u32, @ptrCast(bytes.ptr + lc.indirectsymoff))[0..lc.nindirectsyms];
656 symtab.indirect_symbols = indexes;
657 },
658 .LOAD_DYLIB,
659 .LOAD_WEAK_DYLIB,
660 .REEXPORT_DYLIB,
661 => {
662 try imports.append(cmd.getDylibPathName());
663 },
664 .DYLD_INFO_ONLY => {
665 dyld_info_lc = cmd.cast(macho.dyld_info_command).?;
666 },
667 else => {},
668 }
590669
591 var it: LoadCommandIterator = .{670 i += 1;
592 .ncmds = hdr.ncmds,
593 .buffer = bytes[@sizeOf(macho.mach_header_64)..][0..hdr.sizeofcmds],
594 };
595 var i: usize = 0;
596 while (it.next()) |cmd| {
597 switch (cmd.cmd()) {
598 .SEGMENT_64 => {
599 const seg = cmd.cast(macho.segment_command_64).?;
600 try sections.ensureUnusedCapacity(seg.nsects);
601 for (cmd.getSections()) |sect| {
602 sections.appendAssumeCapacity(sect);
603 }
604 const seg_id: u8 = @intCast(segments.items.len);
605 try segments.append(seg);
606 if (mem.eql(u8, seg.segName(), "__TEXT")) {
607 text_seg = seg_id;
608 }
609 },
610 .SYMTAB => {
611 const lc = cmd.cast(macho.symtab_command).?;
612 const symbols = @as([*]align(1) const macho.nlist_64, @ptrCast(bytes.ptr + lc.symoff))[0..lc.nsyms];
613 const strings = bytes[lc.stroff..][0..lc.strsize];
614 symtab = .{ .symbols = symbols, .strings = strings };
615 },
616 .LOAD_DYLIB,
617 .LOAD_WEAK_DYLIB,
618 .REEXPORT_DYLIB,
619 => {
620 try imports.append(cmd.getDylibPathName());
621 },
622 .DYLD_INFO_ONLY => {
623 dyld_info_lc = cmd.cast(macho.dyld_info_command).?;
624 },
625 else => {},
626 }671 }
672 }
627673
628 try dumpLoadCommand(cmd, i, writer);674 switch (kind) {
629 try writer.writeByte('\n');675 .headers => {
676 try dumpHeader(hdr, writer);
630677
631 i += 1;678 var it: LoadCommandIterator = .{
632 }679 .ncmds = hdr.ncmds,
680 .buffer = bytes[@sizeOf(macho.mach_header_64)..][0..hdr.sizeofcmds],
681 };
682 var i: usize = 0;
683 while (it.next()) |cmd| {
684 try dumpLoadCommand(cmd, i, writer);
685 try writer.writeByte('\n');
633686
634 if (symtab) |stab| {687 i += 1;
635 try dumpSymtab(sections.items, imports.items, stab, writer);688 }
636 }689 },
637690
638 if (dyld_info_lc) |lc| {691 .symtab => if (symtab.symbols.len > 0) {
639 try writer.writeAll(dyld_info_label ++ "\n");692 try dumpSymtab(sections.items, imports.items, symtab, writer);
640 if (lc.rebase_size > 0) {693 } else return step.fail("no symbol table found", .{}),
641 const data = bytes[lc.rebase_off..][0..lc.rebase_size];694
642 try writer.writeAll("rebase info\n");695 .indirect_symtab => if (symtab.symbols.len > 0 and symtab.indirect_symbols.len > 0) {
643 try dumpRebaseInfo(gpa, data, segments.items, writer);696 try dumpIndirectSymtab(gpa, sections.items, symtab, writer);
644 }697 } else return step.fail("no indirect symbol table found", .{}),
645 if (lc.bind_size > 0) {698
646 const data = bytes[lc.bind_off..][0..lc.bind_size];699 .dyld_info => if (dyld_info_lc) |lc| {
647 try writer.writeAll("bind info\n");700 try writer.writeAll(dyld_info_label ++ "\n");
648 try dumpBindInfo(gpa, data, segments.items, imports.items, writer);701 if (lc.rebase_size > 0) {
649 }702 const data = bytes[lc.rebase_off..][0..lc.rebase_size];
650 if (lc.weak_bind_size > 0) {703 try writer.writeAll("rebase info\n");
651 const data = bytes[lc.weak_bind_off..][0..lc.weak_bind_size];704 try dumpRebaseInfo(gpa, data, segments.items, writer);
652 try writer.writeAll("weak bind info\n");705 }
653 try dumpBindInfo(gpa, data, segments.items, imports.items, writer);706 if (lc.bind_size > 0) {
654 }707 const data = bytes[lc.bind_off..][0..lc.bind_size];
655 if (lc.lazy_bind_size > 0) {708 try writer.writeAll("bind info\n");
656 const data = bytes[lc.lazy_bind_off..][0..lc.lazy_bind_size];709 try dumpBindInfo(gpa, data, segments.items, imports.items, writer);
657 try writer.writeAll("lazy bind info\n");710 }
658 try dumpBindInfo(gpa, data, segments.items, imports.items, writer);711 if (lc.weak_bind_size > 0) {
659 }712 const data = bytes[lc.weak_bind_off..][0..lc.weak_bind_size];
660 if (lc.export_size > 0) {713 try writer.writeAll("weak bind info\n");
661 const data = bytes[lc.export_off..][0..lc.export_size];714 try dumpBindInfo(gpa, data, segments.items, imports.items, writer);
662 try writer.writeAll("exports\n");715 }
663 try dumpExportsTrie(gpa, data, segments.items[text_seg.?], writer);716 if (lc.lazy_bind_size > 0) {
664 }717 const data = bytes[lc.lazy_bind_off..][0..lc.lazy_bind_size];
718 try writer.writeAll("lazy bind info\n");
719 try dumpBindInfo(gpa, data, segments.items, imports.items, writer);
720 }
721 if (lc.export_size > 0) {
722 const data = bytes[lc.export_off..][0..lc.export_size];
723 try writer.writeAll("exports\n");
724 try dumpExportsTrie(gpa, data, segments.items[text_seg.?], writer);
725 }
726 } else return step.fail("no dyld info found", .{}),
727
728 else => return step.fail("invalid check kind for MachO file format: {s}", .{@tagName(kind)}),
665 }729 }
666730
667 return output.toOwnedSlice();731 return output.toOwnedSlice();
...@@ -971,7 +1035,7 @@ const MachODumper = struct {...@@ -971,7 +1035,7 @@ const MachODumper = struct {
9711035
972 for (symtab.symbols) |sym| {1036 for (symtab.symbols) |sym| {
973 if (sym.stab()) continue;1037 if (sym.stab()) continue;
974 const sym_name = mem.sliceTo(@as([*:0]const u8, @ptrCast(symtab.strings.ptr + sym.n_strx)), 0);1038 const sym_name = symtab.getString(sym.n_strx);
975 if (sym.sect()) {1039 if (sym.sect()) {
976 const sect = sections[sym.n_sect - 1];1040 const sect = sections[sym.n_sect - 1];
977 try writer.print("{x} ({s},{s})", .{1041 try writer.print("{x} ({s},{s})", .{
...@@ -1021,6 +1085,52 @@ const MachODumper = struct {...@@ -1021,6 +1085,52 @@ const MachODumper = struct {
1021 }1085 }
1022 }1086 }
10231087
1088 fn dumpIndirectSymtab(
1089 gpa: Allocator,
1090 sections: []const macho.section_64,
1091 symtab: Symtab,
1092 writer: anytype,
1093 ) !void {
1094 try writer.writeAll(indirect_symtab_label ++ "\n");
1095
1096 var sects = std.ArrayList(macho.section_64).init(gpa);
1097 defer sects.deinit();
1098 try sects.ensureUnusedCapacity(3);
1099
1100 for (sections) |sect| {
1101 if (mem.eql(u8, sect.sectName(), "__stubs")) sects.appendAssumeCapacity(sect);
1102 if (mem.eql(u8, sect.sectName(), "__got")) sects.appendAssumeCapacity(sect);
1103 if (mem.eql(u8, sect.sectName(), "__la_symbol_ptr")) sects.appendAssumeCapacity(sect);
1104 }
1105
1106 const sortFn = struct {
1107 fn sortFn(ctx: void, lhs: macho.section_64, rhs: macho.section_64) bool {
1108 _ = ctx;
1109 return lhs.reserved1 < rhs.reserved1;
1110 }
1111 }.sortFn;
1112 mem.sort(macho.section_64, sects.items, {}, sortFn);
1113
1114 var i: usize = 0;
1115 while (i < sects.items.len) : (i += 1) {
1116 const sect = sects.items[i];
1117 const start = sect.reserved1;
1118 const end = if (i + 1 >= sects.items.len) symtab.indirect_symbols.len else sects.items[i + 1].reserved1;
1119 const entry_size = blk: {
1120 if (mem.eql(u8, sect.sectName(), "__stubs")) break :blk sect.reserved2;
1121 break :blk @sizeOf(u64);
1122 };
1123
1124 try writer.print("{s},{s}\n", .{ sect.segName(), sect.sectName() });
1125 try writer.print("nentries {d}\n", .{end - start});
1126 for (symtab.indirect_symbols[start..end], 0..) |index, j| {
1127 const sym = symtab.symbols[index];
1128 const addr = sect.addr + entry_size * j;
1129 try writer.print("0x{x} {d} {s}\n", .{ addr, index, symtab.getString(sym.n_strx) });
1130 }
1131 }
1132 }
1133
1024 fn dumpRebaseInfo(1134 fn dumpRebaseInfo(
1025 gpa: Allocator,1135 gpa: Allocator,
1026 data: []const u8,1136 data: []const u8,
...@@ -1443,7 +1553,8 @@ const ElfDumper = struct {...@@ -1443,7 +1553,8 @@ const ElfDumper = struct {
1443 const dynamic_section_label = "dynamic section";1553 const dynamic_section_label = "dynamic section";
1444 const archive_symtab_label = "archive symbol table";1554 const archive_symtab_label = "archive symbol table";
14451555
1446 fn parseAndDump(step: *Step, bytes: []const u8) ![]const u8 {1556 fn parseAndDump(step: *Step, kind: Check.Kind, bytes: []const u8) ![]const u8 {
1557 _ = kind;
1447 const gpa = step.owner.allocator;1558 const gpa = step.owner.allocator;
1448 return parseAndDumpArchive(gpa, bytes) catch |err| switch (err) {1559 return parseAndDumpArchive(gpa, bytes) catch |err| switch (err) {
1449 error.InvalidArchiveMagicNumber => try parseAndDumpObject(gpa, bytes),1560 error.InvalidArchiveMagicNumber => try parseAndDumpObject(gpa, bytes),
...@@ -2090,7 +2201,8 @@ const ElfDumper = struct {...@@ -2090,7 +2201,8 @@ const ElfDumper = struct {
2090const WasmDumper = struct {2201const WasmDumper = struct {
2091 const symtab_label = "symbols";2202 const symtab_label = "symbols";
20922203
2093 fn parseAndDump(step: *Step, bytes: []const u8) ![]const u8 {2204 fn parseAndDump(step: *Step, kind: Check.Kind, bytes: []const u8) ![]const u8 {
2205 _ = kind;
2094 const gpa = step.owner.allocator;2206 const gpa = step.owner.allocator;
2095 var fbs = std.io.fixedBufferStream(bytes);2207 var fbs = std.io.fixedBufferStream(bytes);
2096 const reader = fbs.reader();2208 const reader = fbs.reader();