authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2024-05-14 07:11:41+02:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2024-05-14 07:11:41+02:00
log6a65561e3e5f82f126ec4795e5cd9c07392b457b
treecd0cfc7183a2c550542c81f486305b14668df66c
parente2ec54bb38eb7b157667f0a87c9eb929017c3710
parent2d537da6bd0faead48d7b230e694ccdb76560e4e
signaturebadge-check Signed by PGP key B5690EEEBB952194

Merge pull request #19958 from ziglang/macho-check-object

Step.CheckObject: add support for dumping raw section contents for MachO

2 files changed, 868 insertions(+), 861 deletions(-)

lib/std/Build/Step/CheckObject.zig+865-858
...@@ -695,7 +695,6 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {...@@ -695,7 +695,6 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
695}695}
696696
697const MachODumper = struct {697const MachODumper = struct {
698 const LoadCommandIterator = macho.LoadCommandIterator;
699 const dyld_rebase_label = "dyld rebase data";698 const dyld_rebase_label = "dyld rebase data";
700 const dyld_bind_label = "dyld bind data";699 const dyld_bind_label = "dyld bind data";
701 const dyld_weak_bind_label = "dyld weak bind data";700 const dyld_weak_bind_label = "dyld weak bind data";
...@@ -704,80 +703,52 @@ const MachODumper = struct {...@@ -704,80 +703,52 @@ const MachODumper = struct {
704 const symtab_label = "symbol table";703 const symtab_label = "symbol table";
705 const indirect_symtab_label = "indirect symbol table";704 const indirect_symtab_label = "indirect symbol table";
706705
707 const Symtab = struct {
708 symbols: []align(1) const macho.nlist_64 = &[0]macho.nlist_64{},
709 strings: []const u8 = &[0]u8{},
710 indirect_symbols: []align(1) const u32 = &[0]u32{},
711
712 fn getString(symtab: Symtab, off: u32) []const u8 {
713 assert(off < symtab.strings.len);
714 return mem.sliceTo(@as([*:0]const u8, @ptrCast(symtab.strings.ptr + off)), 0);
715 }
716 };
717
718 fn parseAndDump(step: *Step, check: Check, bytes: []const u8) ![]const u8 {706 fn parseAndDump(step: *Step, check: Check, bytes: []const u8) ![]const u8 {
719 const gpa = step.owner.allocator;707 // TODO: handle archives and fat files
720 var stream = std.io.fixedBufferStream(bytes);708 return parseAndDumpObject(step, check, bytes);
721 const reader = stream.reader();709 }
722
723 const hdr = try reader.readStruct(macho.mach_header_64);
724 if (hdr.magic != macho.MH_MAGIC_64) {
725 return error.InvalidMagicNumber;
726 }
727
728 var output = std.ArrayList(u8).init(gpa);
729 const writer = output.writer();
730710
731 var symtab: Symtab = .{};711 const ObjectContext = struct {
732 var segments = std.ArrayList(macho.segment_command_64).init(gpa);712 gpa: Allocator,
733 defer segments.deinit();713 data: []const u8,
734 var sections = std.ArrayList(macho.section_64).init(gpa);714 header: macho.mach_header_64,
735 defer sections.deinit();715 segments: std.ArrayListUnmanaged(macho.segment_command_64) = .{},
736 var imports = std.ArrayList([]const u8).init(gpa);716 sections: std.ArrayListUnmanaged(macho.section_64) = .{},
737 defer imports.deinit();717 symtab: std.ArrayListUnmanaged(macho.nlist_64) = .{},
738 var text_seg: ?u8 = null;718 strtab: std.ArrayListUnmanaged(u8) = .{},
739 var dyld_info_lc: ?macho.dyld_info_command = null;719 indsymtab: std.ArrayListUnmanaged(u32) = .{},
740720 imports: std.ArrayListUnmanaged([]const u8) = .{},
741 {721
742 var it: LoadCommandIterator = .{722 fn parse(ctx: *ObjectContext) !void {
743 .ncmds = hdr.ncmds,723 var it = ctx.getLoadCommandIterator();
744 .buffer = bytes[@sizeOf(macho.mach_header_64)..][0..hdr.sizeofcmds],
745 };
746 var i: usize = 0;724 var i: usize = 0;
747 while (it.next()) |cmd| {725 while (it.next()) |cmd| {
748 switch (cmd.cmd()) {726 switch (cmd.cmd()) {
749 .SEGMENT_64 => {727 .SEGMENT_64 => {
750 const seg = cmd.cast(macho.segment_command_64).?;728 const seg = cmd.cast(macho.segment_command_64).?;
751 try sections.ensureUnusedCapacity(seg.nsects);729 try ctx.segments.append(ctx.gpa, seg);
730 try ctx.sections.ensureUnusedCapacity(ctx.gpa, seg.nsects);
752 for (cmd.getSections()) |sect| {731 for (cmd.getSections()) |sect| {
753 sections.appendAssumeCapacity(sect);732 ctx.sections.appendAssumeCapacity(sect);
754 }
755 const seg_id: u8 = @intCast(segments.items.len);
756 try segments.append(seg);
757 if (mem.eql(u8, seg.segName(), "__TEXT")) {
758 text_seg = seg_id;
759 }733 }
760 },734 },
761 .SYMTAB => {735 .SYMTAB => {
762 const lc = cmd.cast(macho.symtab_command).?;736 const lc = cmd.cast(macho.symtab_command).?;
763 const symbols = @as([*]align(1) const macho.nlist_64, @ptrCast(bytes.ptr + lc.symoff))[0..lc.nsyms];737 const symtab = @as([*]align(1) const macho.nlist_64, @ptrCast(ctx.data.ptr + lc.symoff))[0..lc.nsyms];
764 const strings = bytes[lc.stroff..][0..lc.strsize];738 const strtab = ctx.data[lc.stroff..][0..lc.strsize];
765 symtab.symbols = symbols;739 try ctx.symtab.appendUnalignedSlice(ctx.gpa, symtab);
766 symtab.strings = strings;740 try ctx.strtab.appendSlice(ctx.gpa, strtab);
767 },741 },
768 .DYSYMTAB => {742 .DYSYMTAB => {
769 const lc = cmd.cast(macho.dysymtab_command).?;743 const lc = cmd.cast(macho.dysymtab_command).?;
770 const indexes = @as([*]align(1) const u32, @ptrCast(bytes.ptr + lc.indirectsymoff))[0..lc.nindirectsyms];744 const indexes = @as([*]align(1) const u32, @ptrCast(ctx.data.ptr + lc.indirectsymoff))[0..lc.nindirectsyms];
771 symtab.indirect_symbols = indexes;745 try ctx.indsymtab.appendUnalignedSlice(ctx.gpa, indexes);
772 },746 },
773 .LOAD_DYLIB,747 .LOAD_DYLIB,
774 .LOAD_WEAK_DYLIB,748 .LOAD_WEAK_DYLIB,
775 .REEXPORT_DYLIB,749 .REEXPORT_DYLIB,
776 => {750 => {
777 try imports.append(cmd.getDylibPathName());751 try ctx.imports.append(ctx.gpa, cmd.getDylibPathName());
778 },
779 .DYLD_INFO_ONLY => {
780 dyld_info_lc = cmd.cast(macho.dyld_info_command).?;
781 },752 },
782 else => {},753 else => {},
783 }754 }
...@@ -786,899 +757,935 @@ const MachODumper = struct {...@@ -786,899 +757,935 @@ const MachODumper = struct {
786 }757 }
787 }758 }
788759
789 switch (check.kind) {760 fn getString(ctx: ObjectContext, off: u32) [:0]const u8 {
790 .headers => {761 assert(off < ctx.strtab.items.len);
791 try dumpHeader(hdr, writer);762 return mem.sliceTo(@as([*:0]const u8, @ptrCast(ctx.strtab.items.ptr + off)), 0);
792763 }
793 var it: LoadCommandIterator = .{
794 .ncmds = hdr.ncmds,
795 .buffer = bytes[@sizeOf(macho.mach_header_64)..][0..hdr.sizeofcmds],
796 };
797 var i: usize = 0;
798 while (it.next()) |cmd| {
799 try dumpLoadCommand(cmd, i, writer);
800 try writer.writeByte('\n');
801
802 i += 1;
803 }
804 },
805
806 .symtab => if (symtab.symbols.len > 0) {
807 try dumpSymtab(sections.items, imports.items, symtab, writer);
808 } else return step.fail("no symbol table found", .{}),
809
810 .indirect_symtab => if (symtab.symbols.len > 0 and symtab.indirect_symbols.len > 0) {
811 try dumpIndirectSymtab(gpa, sections.items, symtab, writer);
812 } else return step.fail("no indirect symbol table found", .{}),
813
814 .dyld_rebase,
815 .dyld_bind,
816 .dyld_weak_bind,
817 .dyld_lazy_bind,
818 => {
819 if (dyld_info_lc == null) return step.fail("no dyld info found", .{});
820 const lc = dyld_info_lc.?;
821
822 switch (check.kind) {
823 .dyld_rebase => if (lc.rebase_size > 0) {
824 const data = bytes[lc.rebase_off..][0..lc.rebase_size];
825 try writer.writeAll(dyld_rebase_label ++ "\n");
826 try dumpRebaseInfo(gpa, data, segments.items, writer);
827 } else return step.fail("no rebase data found", .{}),
828
829 .dyld_bind => if (lc.bind_size > 0) {
830 const data = bytes[lc.bind_off..][0..lc.bind_size];
831 try writer.writeAll(dyld_bind_label ++ "\n");
832 try dumpBindInfo(gpa, data, segments.items, imports.items, writer);
833 } else return step.fail("no bind data found", .{}),
834
835 .dyld_weak_bind => if (lc.weak_bind_size > 0) {
836 const data = bytes[lc.weak_bind_off..][0..lc.weak_bind_size];
837 try writer.writeAll(dyld_weak_bind_label ++ "\n");
838 try dumpBindInfo(gpa, data, segments.items, imports.items, writer);
839 } else return step.fail("no weak bind data found", .{}),
840764
841 .dyld_lazy_bind => if (lc.lazy_bind_size > 0) {765 fn getLoadCommandIterator(ctx: ObjectContext) macho.LoadCommandIterator {
842 const data = bytes[lc.lazy_bind_off..][0..lc.lazy_bind_size];766 const data = ctx.data[@sizeOf(macho.mach_header_64)..][0..ctx.header.sizeofcmds];
843 try writer.writeAll(dyld_lazy_bind_label ++ "\n");767 return .{ .ncmds = ctx.header.ncmds, .buffer = data };
844 try dumpBindInfo(gpa, data, segments.items, imports.items, writer);768 }
845 } else return step.fail("no lazy bind data found", .{}),
846769
847 else => unreachable,770 fn getLoadCommand(ctx: ObjectContext, cmd: macho.LC) ?macho.LoadCommandIterator.LoadCommand {
848 }771 var it = ctx.getLoadCommandIterator();
849 },772 while (it.next()) |lc| if (lc.cmd() == cmd) {
773 return lc;
774 };
775 return null;
776 }
850777
851 .exports => blk: {778 fn getSegmentByName(ctx: ObjectContext, name: []const u8) ?macho.segment_command_64 {
852 if (dyld_info_lc) |lc| {779 for (ctx.segments.items) |seg| {
853 if (lc.export_size > 0) {780 if (mem.eql(u8, seg.segName(), name)) return seg;
854 const data = bytes[lc.export_off..][0..lc.export_size];781 }
855 try writer.writeAll(exports_label ++ "\n");782 return null;
856 try dumpExportsTrie(gpa, data, segments.items[text_seg.?], writer);783 }
857 break :blk;
858 }
859 }
860 return step.fail("no exports data found", .{});
861 },
862784
863 else => return step.fail("invalid check kind for MachO file format: {s}", .{@tagName(check.kind)}),785 fn getSectionByName(ctx: ObjectContext, segname: []const u8, sectname: []const u8) ?macho.section_64 {
786 for (ctx.sections.items) |sect| {
787 if (mem.eql(u8, sect.segName(), segname) and mem.eql(u8, sect.sectName(), sectname)) return sect;
788 }
789 return null;
864 }790 }
865791
866 return output.toOwnedSlice();792 fn dumpHeader(hdr: macho.mach_header_64, writer: anytype) !void {
867 }793 const cputype = switch (hdr.cputype) {
794 macho.CPU_TYPE_ARM64 => "ARM64",
795 macho.CPU_TYPE_X86_64 => "X86_64",
796 else => "Unknown",
797 };
798 const filetype = switch (hdr.filetype) {
799 macho.MH_OBJECT => "MH_OBJECT",
800 macho.MH_EXECUTE => "MH_EXECUTE",
801 macho.MH_FVMLIB => "MH_FVMLIB",
802 macho.MH_CORE => "MH_CORE",
803 macho.MH_PRELOAD => "MH_PRELOAD",
804 macho.MH_DYLIB => "MH_DYLIB",
805 macho.MH_DYLINKER => "MH_DYLINKER",
806 macho.MH_BUNDLE => "MH_BUNDLE",
807 macho.MH_DYLIB_STUB => "MH_DYLIB_STUB",
808 macho.MH_DSYM => "MH_DSYM",
809 macho.MH_KEXT_BUNDLE => "MH_KEXT_BUNDLE",
810 else => "Unknown",
811 };
868812
869 fn dumpHeader(hdr: macho.mach_header_64, writer: anytype) !void {813 try writer.print(
870 const cputype = switch (hdr.cputype) {814 \\header
871 macho.CPU_TYPE_ARM64 => "ARM64",815 \\cputype {s}
872 macho.CPU_TYPE_X86_64 => "X86_64",816 \\filetype {s}
873 else => "Unknown",817 \\ncmds {d}
874 };818 \\sizeofcmds {x}
875 const filetype = switch (hdr.filetype) {819 \\flags
876 macho.MH_OBJECT => "MH_OBJECT",820 , .{
877 macho.MH_EXECUTE => "MH_EXECUTE",821 cputype,
878 macho.MH_FVMLIB => "MH_FVMLIB",822 filetype,
879 macho.MH_CORE => "MH_CORE",823 hdr.ncmds,
880 macho.MH_PRELOAD => "MH_PRELOAD",824 hdr.sizeofcmds,
881 macho.MH_DYLIB => "MH_DYLIB",825 });
882 macho.MH_DYLINKER => "MH_DYLINKER",826
883 macho.MH_BUNDLE => "MH_BUNDLE",827 if (hdr.flags > 0) {
884 macho.MH_DYLIB_STUB => "MH_DYLIB_STUB",828 if (hdr.flags & macho.MH_NOUNDEFS != 0) try writer.writeAll(" NOUNDEFS");
885 macho.MH_DSYM => "MH_DSYM",829 if (hdr.flags & macho.MH_INCRLINK != 0) try writer.writeAll(" INCRLINK");
886 macho.MH_KEXT_BUNDLE => "MH_KEXT_BUNDLE",830 if (hdr.flags & macho.MH_DYLDLINK != 0) try writer.writeAll(" DYLDLINK");
887 else => "Unknown",831 if (hdr.flags & macho.MH_BINDATLOAD != 0) try writer.writeAll(" BINDATLOAD");
888 };832 if (hdr.flags & macho.MH_PREBOUND != 0) try writer.writeAll(" PREBOUND");
833 if (hdr.flags & macho.MH_SPLIT_SEGS != 0) try writer.writeAll(" SPLIT_SEGS");
834 if (hdr.flags & macho.MH_LAZY_INIT != 0) try writer.writeAll(" LAZY_INIT");
835 if (hdr.flags & macho.MH_TWOLEVEL != 0) try writer.writeAll(" TWOLEVEL");
836 if (hdr.flags & macho.MH_FORCE_FLAT != 0) try writer.writeAll(" FORCE_FLAT");
837 if (hdr.flags & macho.MH_NOMULTIDEFS != 0) try writer.writeAll(" NOMULTIDEFS");
838 if (hdr.flags & macho.MH_NOFIXPREBINDING != 0) try writer.writeAll(" NOFIXPREBINDING");
839 if (hdr.flags & macho.MH_PREBINDABLE != 0) try writer.writeAll(" PREBINDABLE");
840 if (hdr.flags & macho.MH_ALLMODSBOUND != 0) try writer.writeAll(" ALLMODSBOUND");
841 if (hdr.flags & macho.MH_SUBSECTIONS_VIA_SYMBOLS != 0) try writer.writeAll(" SUBSECTIONS_VIA_SYMBOLS");
842 if (hdr.flags & macho.MH_CANONICAL != 0) try writer.writeAll(" CANONICAL");
843 if (hdr.flags & macho.MH_WEAK_DEFINES != 0) try writer.writeAll(" WEAK_DEFINES");
844 if (hdr.flags & macho.MH_BINDS_TO_WEAK != 0) try writer.writeAll(" BINDS_TO_WEAK");
845 if (hdr.flags & macho.MH_ALLOW_STACK_EXECUTION != 0) try writer.writeAll(" ALLOW_STACK_EXECUTION");
846 if (hdr.flags & macho.MH_ROOT_SAFE != 0) try writer.writeAll(" ROOT_SAFE");
847 if (hdr.flags & macho.MH_SETUID_SAFE != 0) try writer.writeAll(" SETUID_SAFE");
848 if (hdr.flags & macho.MH_NO_REEXPORTED_DYLIBS != 0) try writer.writeAll(" NO_REEXPORTED_DYLIBS");
849 if (hdr.flags & macho.MH_PIE != 0) try writer.writeAll(" PIE");
850 if (hdr.flags & macho.MH_DEAD_STRIPPABLE_DYLIB != 0) try writer.writeAll(" DEAD_STRIPPABLE_DYLIB");
851 if (hdr.flags & macho.MH_HAS_TLV_DESCRIPTORS != 0) try writer.writeAll(" HAS_TLV_DESCRIPTORS");
852 if (hdr.flags & macho.MH_NO_HEAP_EXECUTION != 0) try writer.writeAll(" NO_HEAP_EXECUTION");
853 if (hdr.flags & macho.MH_APP_EXTENSION_SAFE != 0) try writer.writeAll(" APP_EXTENSION_SAFE");
854 if (hdr.flags & macho.MH_NLIST_OUTOFSYNC_WITH_DYLDINFO != 0) try writer.writeAll(" NLIST_OUTOFSYNC_WITH_DYLDINFO");
855 }
889856
890 try writer.print(857 try writer.writeByte('\n');
891 \\header
892 \\cputype {s}
893 \\filetype {s}
894 \\ncmds {d}
895 \\sizeofcmds {x}
896 \\flags
897 , .{
898 cputype,
899 filetype,
900 hdr.ncmds,
901 hdr.sizeofcmds,
902 });
903
904 if (hdr.flags > 0) {
905 if (hdr.flags & macho.MH_NOUNDEFS != 0) try writer.writeAll(" NOUNDEFS");
906 if (hdr.flags & macho.MH_INCRLINK != 0) try writer.writeAll(" INCRLINK");
907 if (hdr.flags & macho.MH_DYLDLINK != 0) try writer.writeAll(" DYLDLINK");
908 if (hdr.flags & macho.MH_BINDATLOAD != 0) try writer.writeAll(" BINDATLOAD");
909 if (hdr.flags & macho.MH_PREBOUND != 0) try writer.writeAll(" PREBOUND");
910 if (hdr.flags & macho.MH_SPLIT_SEGS != 0) try writer.writeAll(" SPLIT_SEGS");
911 if (hdr.flags & macho.MH_LAZY_INIT != 0) try writer.writeAll(" LAZY_INIT");
912 if (hdr.flags & macho.MH_TWOLEVEL != 0) try writer.writeAll(" TWOLEVEL");
913 if (hdr.flags & macho.MH_FORCE_FLAT != 0) try writer.writeAll(" FORCE_FLAT");
914 if (hdr.flags & macho.MH_NOMULTIDEFS != 0) try writer.writeAll(" NOMULTIDEFS");
915 if (hdr.flags & macho.MH_NOFIXPREBINDING != 0) try writer.writeAll(" NOFIXPREBINDING");
916 if (hdr.flags & macho.MH_PREBINDABLE != 0) try writer.writeAll(" PREBINDABLE");
917 if (hdr.flags & macho.MH_ALLMODSBOUND != 0) try writer.writeAll(" ALLMODSBOUND");
918 if (hdr.flags & macho.MH_SUBSECTIONS_VIA_SYMBOLS != 0) try writer.writeAll(" SUBSECTIONS_VIA_SYMBOLS");
919 if (hdr.flags & macho.MH_CANONICAL != 0) try writer.writeAll(" CANONICAL");
920 if (hdr.flags & macho.MH_WEAK_DEFINES != 0) try writer.writeAll(" WEAK_DEFINES");
921 if (hdr.flags & macho.MH_BINDS_TO_WEAK != 0) try writer.writeAll(" BINDS_TO_WEAK");
922 if (hdr.flags & macho.MH_ALLOW_STACK_EXECUTION != 0) try writer.writeAll(" ALLOW_STACK_EXECUTION");
923 if (hdr.flags & macho.MH_ROOT_SAFE != 0) try writer.writeAll(" ROOT_SAFE");
924 if (hdr.flags & macho.MH_SETUID_SAFE != 0) try writer.writeAll(" SETUID_SAFE");
925 if (hdr.flags & macho.MH_NO_REEXPORTED_DYLIBS != 0) try writer.writeAll(" NO_REEXPORTED_DYLIBS");
926 if (hdr.flags & macho.MH_PIE != 0) try writer.writeAll(" PIE");
927 if (hdr.flags & macho.MH_DEAD_STRIPPABLE_DYLIB != 0) try writer.writeAll(" DEAD_STRIPPABLE_DYLIB");
928 if (hdr.flags & macho.MH_HAS_TLV_DESCRIPTORS != 0) try writer.writeAll(" HAS_TLV_DESCRIPTORS");
929 if (hdr.flags & macho.MH_NO_HEAP_EXECUTION != 0) try writer.writeAll(" NO_HEAP_EXECUTION");
930 if (hdr.flags & macho.MH_APP_EXTENSION_SAFE != 0) try writer.writeAll(" APP_EXTENSION_SAFE");
931 if (hdr.flags & macho.MH_NLIST_OUTOFSYNC_WITH_DYLDINFO != 0) try writer.writeAll(" NLIST_OUTOFSYNC_WITH_DYLDINFO");
932 }858 }
933859
934 try writer.writeByte('\n');860 fn dumpLoadCommand(lc: macho.LoadCommandIterator.LoadCommand, index: usize, writer: anytype) !void {
935 }861 // print header first
862 try writer.print(
863 \\LC {d}
864 \\cmd {s}
865 \\cmdsize {d}
866 , .{ index, @tagName(lc.cmd()), lc.cmdsize() });
867
868 switch (lc.cmd()) {
869 .SEGMENT_64 => {
870 const seg = lc.cast(macho.segment_command_64).?;
871 try writer.writeByte('\n');
872 try writer.print(
873 \\segname {s}
874 \\vmaddr {x}
875 \\vmsize {x}
876 \\fileoff {x}
877 \\filesz {x}
878 , .{
879 seg.segName(),
880 seg.vmaddr,
881 seg.vmsize,
882 seg.fileoff,
883 seg.filesize,
884 });
936885
937 fn dumpLoadCommand(lc: macho.LoadCommandIterator.LoadCommand, index: usize, writer: anytype) !void {886 for (lc.getSections()) |sect| {
938 // print header first887 try writer.writeByte('\n');
939 try writer.print(888 try writer.print(
940 \\LC {d}889 \\sectname {s}
941 \\cmd {s}890 \\addr {x}
942 \\cmdsize {d}891 \\size {x}
943 , .{ index, @tagName(lc.cmd()), lc.cmdsize() });892 \\offset {x}
944893 \\align {x}
945 switch (lc.cmd()) {894 , .{
946 .SEGMENT_64 => {895 sect.sectName(),
947 const seg = lc.cast(macho.segment_command_64).?;896 sect.addr,
948 try writer.writeByte('\n');897 sect.size,
949 try writer.print(898 sect.offset,
950 \\segname {s}899 sect.@"align",
951 \\vmaddr {x}900 });
952 \\vmsize {x}901 }
953 \\fileoff {x}902 },
954 \\filesz {x}
955 , .{
956 seg.segName(),
957 seg.vmaddr,
958 seg.vmsize,
959 seg.fileoff,
960 seg.filesize,
961 });
962903
963 for (lc.getSections()) |sect| {904 .ID_DYLIB,
905 .LOAD_DYLIB,
906 .LOAD_WEAK_DYLIB,
907 .REEXPORT_DYLIB,
908 => {
909 const dylib = lc.cast(macho.dylib_command).?;
964 try writer.writeByte('\n');910 try writer.writeByte('\n');
965 try writer.print(911 try writer.print(
966 \\sectname {s}912 \\name {s}
967 \\addr {x}913 \\timestamp {d}
968 \\size {x}914 \\current version {x}
969 \\offset {x}915 \\compatibility version {x}
970 \\align {x}
971 , .{916 , .{
972 sect.sectName(),917 lc.getDylibPathName(),
973 sect.addr,918 dylib.dylib.timestamp,
974 sect.size,919 dylib.dylib.current_version,
975 sect.offset,920 dylib.dylib.compatibility_version,
976 sect.@"align",
977 });921 });
978 }922 },
979 },
980
981 .ID_DYLIB,
982 .LOAD_DYLIB,
983 .LOAD_WEAK_DYLIB,
984 .REEXPORT_DYLIB,
985 => {
986 const dylib = lc.cast(macho.dylib_command).?;
987 try writer.writeByte('\n');
988 try writer.print(
989 \\name {s}
990 \\timestamp {d}
991 \\current version {x}
992 \\compatibility version {x}
993 , .{
994 lc.getDylibPathName(),
995 dylib.dylib.timestamp,
996 dylib.dylib.current_version,
997 dylib.dylib.compatibility_version,
998 });
999 },
1000923
1001 .MAIN => {924 .MAIN => {
1002 const main = lc.cast(macho.entry_point_command).?;925 const main = lc.cast(macho.entry_point_command).?;
1003 try writer.writeByte('\n');926 try writer.writeByte('\n');
1004 try writer.print(927 try writer.print(
1005 \\entryoff {x}928 \\entryoff {x}
1006 \\stacksize {x}929 \\stacksize {x}
1007 , .{ main.entryoff, main.stacksize });930 , .{ main.entryoff, main.stacksize });
1008 },931 },
1009932
1010 .RPATH => {933 .RPATH => {
1011 try writer.writeByte('\n');934 try writer.writeByte('\n');
1012 try writer.print(935 try writer.print(
1013 \\path {s}936 \\path {s}
1014 , .{937 , .{
1015 lc.getRpathPathName(),938 lc.getRpathPathName(),
1016 });939 });
1017 },940 },
1018941
1019 .UUID => {942 .UUID => {
1020 const uuid = lc.cast(macho.uuid_command).?;943 const uuid = lc.cast(macho.uuid_command).?;
1021 try writer.writeByte('\n');944 try writer.writeByte('\n');
1022 try writer.print("uuid {x}", .{std.fmt.fmtSliceHexLower(&uuid.uuid)});945 try writer.print("uuid {x}", .{std.fmt.fmtSliceHexLower(&uuid.uuid)});
1023 },946 },
1024947
1025 .DATA_IN_CODE,948 .DATA_IN_CODE,
1026 .FUNCTION_STARTS,949 .FUNCTION_STARTS,
1027 .CODE_SIGNATURE,950 .CODE_SIGNATURE,
1028 => {951 => {
1029 const llc = lc.cast(macho.linkedit_data_command).?;952 const llc = lc.cast(macho.linkedit_data_command).?;
1030 try writer.writeByte('\n');953 try writer.writeByte('\n');
1031 try writer.print(954 try writer.print(
1032 \\dataoff {x}955 \\dataoff {x}
1033 \\datasize {x}956 \\datasize {x}
1034 , .{ llc.dataoff, llc.datasize });957 , .{ llc.dataoff, llc.datasize });
1035 },958 },
1036959
1037 .DYLD_INFO_ONLY => {960 .DYLD_INFO_ONLY => {
1038 const dlc = lc.cast(macho.dyld_info_command).?;961 const dlc = lc.cast(macho.dyld_info_command).?;
1039 try writer.writeByte('\n');962 try writer.writeByte('\n');
1040 try writer.print(963 try writer.print(
1041 \\rebaseoff {x}964 \\rebaseoff {x}
1042 \\rebasesize {x}965 \\rebasesize {x}
1043 \\bindoff {x}966 \\bindoff {x}
1044 \\bindsize {x}967 \\bindsize {x}
1045 \\weakbindoff {x}968 \\weakbindoff {x}
1046 \\weakbindsize {x}969 \\weakbindsize {x}
1047 \\lazybindoff {x}970 \\lazybindoff {x}
1048 \\lazybindsize {x}971 \\lazybindsize {x}
1049 \\exportoff {x}972 \\exportoff {x}
1050 \\exportsize {x}973 \\exportsize {x}
1051 , .{974 , .{
1052 dlc.rebase_off,975 dlc.rebase_off,
1053 dlc.rebase_size,976 dlc.rebase_size,
1054 dlc.bind_off,977 dlc.bind_off,
1055 dlc.bind_size,978 dlc.bind_size,
1056 dlc.weak_bind_off,979 dlc.weak_bind_off,
1057 dlc.weak_bind_size,980 dlc.weak_bind_size,
1058 dlc.lazy_bind_off,981 dlc.lazy_bind_off,
1059 dlc.lazy_bind_size,982 dlc.lazy_bind_size,
1060 dlc.export_off,983 dlc.export_off,
1061 dlc.export_size,984 dlc.export_size,
1062 });985 });
1063 },986 },
1064987
1065 .SYMTAB => {988 .SYMTAB => {
1066 const slc = lc.cast(macho.symtab_command).?;989 const slc = lc.cast(macho.symtab_command).?;
1067 try writer.writeByte('\n');990 try writer.writeByte('\n');
1068 try writer.print(991 try writer.print(
1069 \\symoff {x}992 \\symoff {x}
1070 \\nsyms {x}993 \\nsyms {x}
1071 \\stroff {x}994 \\stroff {x}
1072 \\strsize {x}995 \\strsize {x}
1073 , .{996 , .{
1074 slc.symoff,997 slc.symoff,
1075 slc.nsyms,998 slc.nsyms,
1076 slc.stroff,999 slc.stroff,
1077 slc.strsize,1000 slc.strsize,
1078 });1001 });
1079 },1002 },
10801003
1081 .DYSYMTAB => {1004 .DYSYMTAB => {
1082 const dlc = lc.cast(macho.dysymtab_command).?;1005 const dlc = lc.cast(macho.dysymtab_command).?;
1083 try writer.writeByte('\n');1006 try writer.writeByte('\n');
1084 try writer.print(1007 try writer.print(
1085 \\ilocalsym {x}1008 \\ilocalsym {x}
1086 \\nlocalsym {x}1009 \\nlocalsym {x}
1087 \\iextdefsym {x}1010 \\iextdefsym {x}
1088 \\nextdefsym {x}1011 \\nextdefsym {x}
1089 \\iundefsym {x}1012 \\iundefsym {x}
1090 \\nundefsym {x}1013 \\nundefsym {x}
1091 \\indirectsymoff {x}1014 \\indirectsymoff {x}
1092 \\nindirectsyms {x}1015 \\nindirectsyms {x}
1093 , .{1016 , .{
1094 dlc.ilocalsym,1017 dlc.ilocalsym,
1095 dlc.nlocalsym,1018 dlc.nlocalsym,
1096 dlc.iextdefsym,1019 dlc.iextdefsym,
1097 dlc.nextdefsym,1020 dlc.nextdefsym,
1098 dlc.iundefsym,1021 dlc.iundefsym,
1099 dlc.nundefsym,1022 dlc.nundefsym,
1100 dlc.indirectsymoff,1023 dlc.indirectsymoff,
1101 dlc.nindirectsyms,1024 dlc.nindirectsyms,
1102 });1025 });
1103 },1026 },
11041027
1105 .BUILD_VERSION => {1028 .BUILD_VERSION => {
1106 const blc = lc.cast(macho.build_version_command).?;1029 const blc = lc.cast(macho.build_version_command).?;
1107 try writer.writeByte('\n');
1108 try writer.print(
1109 \\platform {s}
1110 \\minos {d}.{d}.{d}
1111 \\sdk {d}.{d}.{d}
1112 \\ntools {d}
1113 , .{
1114 @tagName(blc.platform),
1115 blc.minos >> 16,
1116 @as(u8, @truncate(blc.minos >> 8)),
1117 @as(u8, @truncate(blc.minos)),
1118 blc.sdk >> 16,
1119 @as(u8, @truncate(blc.sdk >> 8)),
1120 @as(u8, @truncate(blc.sdk)),
1121 blc.ntools,
1122 });
1123 for (lc.getBuildVersionTools()) |tool| {
1124 try writer.writeByte('\n');1030 try writer.writeByte('\n');
1125 switch (tool.tool) {1031 try writer.print(
1126 .CLANG, .SWIFT, .LD, .LLD, .ZIG => try writer.print("tool {s}\n", .{@tagName(tool.tool)}),1032 \\platform {s}
1127 else => |x| try writer.print("tool {d}\n", .{@intFromEnum(x)}),1033 \\minos {d}.{d}.{d}
1034 \\sdk {d}.{d}.{d}
1035 \\ntools {d}
1036 , .{
1037 @tagName(blc.platform),
1038 blc.minos >> 16,
1039 @as(u8, @truncate(blc.minos >> 8)),
1040 @as(u8, @truncate(blc.minos)),
1041 blc.sdk >> 16,
1042 @as(u8, @truncate(blc.sdk >> 8)),
1043 @as(u8, @truncate(blc.sdk)),
1044 blc.ntools,
1045 });
1046 for (lc.getBuildVersionTools()) |tool| {
1047 try writer.writeByte('\n');
1048 switch (tool.tool) {
1049 .CLANG, .SWIFT, .LD, .LLD, .ZIG => try writer.print("tool {s}\n", .{@tagName(tool.tool)}),
1050 else => |x| try writer.print("tool {d}\n", .{@intFromEnum(x)}),
1051 }
1052 try writer.print(
1053 \\version {d}.{d}.{d}
1054 , .{
1055 tool.version >> 16,
1056 @as(u8, @truncate(tool.version >> 8)),
1057 @as(u8, @truncate(tool.version)),
1058 });
1128 }1059 }
1060 },
1061
1062 .VERSION_MIN_MACOSX,
1063 .VERSION_MIN_IPHONEOS,
1064 .VERSION_MIN_WATCHOS,
1065 .VERSION_MIN_TVOS,
1066 => {
1067 const vlc = lc.cast(macho.version_min_command).?;
1068 try writer.writeByte('\n');
1129 try writer.print(1069 try writer.print(
1130 \\version {d}.{d}.{d}1070 \\version {d}.{d}.{d}
1071 \\sdk {d}.{d}.{d}
1131 , .{1072 , .{
1132 tool.version >> 16,1073 vlc.version >> 16,
1133 @as(u8, @truncate(tool.version >> 8)),1074 @as(u8, @truncate(vlc.version >> 8)),
1134 @as(u8, @truncate(tool.version)),1075 @as(u8, @truncate(vlc.version)),
1076 vlc.sdk >> 16,
1077 @as(u8, @truncate(vlc.sdk >> 8)),
1078 @as(u8, @truncate(vlc.sdk)),
1135 });1079 });
1136 }1080 },
1137 },
1138
1139 .VERSION_MIN_MACOSX,
1140 .VERSION_MIN_IPHONEOS,
1141 .VERSION_MIN_WATCHOS,
1142 .VERSION_MIN_TVOS,
1143 => {
1144 const vlc = lc.cast(macho.version_min_command).?;
1145 try writer.writeByte('\n');
1146 try writer.print(
1147 \\version {d}.{d}.{d}
1148 \\sdk {d}.{d}.{d}
1149 , .{
1150 vlc.version >> 16,
1151 @as(u8, @truncate(vlc.version >> 8)),
1152 @as(u8, @truncate(vlc.version)),
1153 vlc.sdk >> 16,
1154 @as(u8, @truncate(vlc.sdk >> 8)),
1155 @as(u8, @truncate(vlc.sdk)),
1156 });
1157 },
11581081
1159 else => {},1082 else => {},
1083 }
1160 }1084 }
1161 }
11621085
1163 fn dumpSymtab(1086 fn dumpSymtab(ctx: ObjectContext, writer: anytype) !void {
1164 sections: []const macho.section_64,1087 try writer.writeAll(symtab_label ++ "\n");
1165 imports: []const []const u8,1088
1166 symtab: Symtab,1089 for (ctx.symtab.items) |sym| {
1167 writer: anytype,1090 if (sym.stab()) continue;
1168 ) !void {1091 const sym_name = ctx.getString(sym.n_strx);
1169 try writer.writeAll(symtab_label ++ "\n");1092 if (sym.sect()) {
11701093 const sect = ctx.sections.items[sym.n_sect - 1];
1171 for (symtab.symbols) |sym| {1094 try writer.print("{x} ({s},{s})", .{
1172 if (sym.stab()) continue;1095 sym.n_value,
1173 const sym_name = symtab.getString(sym.n_strx);1096 sect.segName(),
1174 if (sym.sect()) {1097 sect.sectName(),
1175 const sect = sections[sym.n_sect - 1];1098 });
1176 try writer.print("{x} ({s},{s})", .{1099 if (sym.n_desc & macho.REFERENCED_DYNAMICALLY != 0) try writer.writeAll(" [referenced dynamically]");
1177 sym.n_value,1100 if (sym.weakDef()) try writer.writeAll(" weak");
1178 sect.segName(),1101 if (sym.weakRef()) try writer.writeAll(" weakref");
1179 sect.sectName(),1102 if (sym.ext()) {
1180 });1103 if (sym.pext()) try writer.writeAll(" private");
1181 if (sym.n_desc & macho.REFERENCED_DYNAMICALLY != 0) try writer.writeAll(" [referenced dynamically]");1104 try writer.writeAll(" external");
1182 if (sym.weakDef()) try writer.writeAll(" weak");1105 } else if (sym.pext()) try writer.writeAll(" (was private external)");
1183 if (sym.weakRef()) try writer.writeAll(" weakref");1106 try writer.print(" {s}\n", .{sym_name});
1184 if (sym.ext()) {1107 } else if (sym.tentative()) {
1185 if (sym.pext()) try writer.writeAll(" private");1108 const alignment = (sym.n_desc >> 8) & 0x0F;
1186 try writer.writeAll(" external");1109 try writer.print(" 0x{x:0>16} (common) (alignment 2^{d})", .{ sym.n_value, alignment });
1187 } else if (sym.pext()) try writer.writeAll(" (was private external)");1110 if (sym.ext()) try writer.writeAll(" external");
1188 try writer.print(" {s}\n", .{sym_name});1111 try writer.print(" {s}\n", .{sym_name});
1189 } else if (sym.tentative()) {1112 } else if (sym.undf()) {
1190 const alignment = (sym.n_desc >> 8) & 0x0F;1113 const ordinal = @divFloor(@as(i16, @bitCast(sym.n_desc)), macho.N_SYMBOL_RESOLVER);
1191 try writer.print(" 0x{x:0>16} (common) (alignment 2^{d})", .{ sym.n_value, alignment });1114 const import_name = blk: {
1192 if (sym.ext()) try writer.writeAll(" external");1115 if (ordinal <= 0) {
1193 try writer.print(" {s}\n", .{sym_name});1116 if (ordinal == macho.BIND_SPECIAL_DYLIB_SELF)
1194 } else if (sym.undf()) {1117 break :blk "self import";
1195 const ordinal = @divFloor(@as(i16, @bitCast(sym.n_desc)), macho.N_SYMBOL_RESOLVER);1118 if (ordinal == macho.BIND_SPECIAL_DYLIB_MAIN_EXECUTABLE)
1196 const import_name = blk: {1119 break :blk "main executable";
1197 if (ordinal <= 0) {1120 if (ordinal == macho.BIND_SPECIAL_DYLIB_FLAT_LOOKUP)
1198 if (ordinal == macho.BIND_SPECIAL_DYLIB_SELF)1121 break :blk "flat lookup";
1199 break :blk "self import";1122 unreachable;
1200 if (ordinal == macho.BIND_SPECIAL_DYLIB_MAIN_EXECUTABLE)1123 }
1201 break :blk "main executable";1124 const full_path = ctx.imports.items[@as(u16, @bitCast(ordinal)) - 1];
1202 if (ordinal == macho.BIND_SPECIAL_DYLIB_FLAT_LOOKUP)1125 const basename = fs.path.basename(full_path);
1203 break :blk "flat lookup";1126 assert(basename.len > 0);
1204 unreachable;1127 const ext = mem.lastIndexOfScalar(u8, basename, '.') orelse basename.len;
1205 }1128 break :blk basename[0..ext];
1206 const full_path = imports[@as(u16, @bitCast(ordinal)) - 1];1129 };
1207 const basename = fs.path.basename(full_path);1130 try writer.writeAll("(undefined)");
1208 assert(basename.len > 0);1131 if (sym.weakRef()) try writer.writeAll(" weakref");
1209 const ext = mem.lastIndexOfScalar(u8, basename, '.') orelse basename.len;1132 if (sym.ext()) try writer.writeAll(" external");
1210 break :blk basename[0..ext];1133 try writer.print(" {s} (from {s})\n", .{
1211 };1134 sym_name,
1212 try writer.writeAll("(undefined)");1135 import_name,
1213 if (sym.weakRef()) try writer.writeAll(" weakref");1136 });
1214 if (sym.ext()) try writer.writeAll(" external");1137 }
1215 try writer.print(" {s} (from {s})\n", .{
1216 sym_name,
1217 import_name,
1218 });
1219 }1138 }
1220 }1139 }
1221 }
12221140
1223 fn dumpIndirectSymtab(1141 fn dumpIndirectSymtab(ctx: ObjectContext, writer: anytype) !void {
1224 gpa: Allocator,1142 try writer.writeAll(indirect_symtab_label ++ "\n");
1225 sections: []const macho.section_64,
1226 symtab: Symtab,
1227 writer: anytype,
1228 ) !void {
1229 try writer.writeAll(indirect_symtab_label ++ "\n");
12301143
1231 var sects = std.ArrayList(macho.section_64).init(gpa);1144 var sects_buffer: [3]macho.section_64 = undefined;
1232 defer sects.deinit();1145 const sects = blk: {
1233 try sects.ensureUnusedCapacity(3);1146 var count: usize = 0;
1147 if (ctx.getSectionByName("__TEXT", "__stubs")) |sect| {
1148 sects_buffer[count] = sect;
1149 count += 1;
1150 }
1151 if (ctx.getSectionByName("__DATA_CONST", "__got")) |sect| {
1152 sects_buffer[count] = sect;
1153 count += 1;
1154 }
1155 if (ctx.getSectionByName("__DATA", "__la_symbol_ptr")) |sect| {
1156 sects_buffer[count] = sect;
1157 count += 1;
1158 }
1159 break :blk sects_buffer[0..count];
1160 };
12341161
1235 for (sections) |sect| {1162 const sortFn = struct {
1236 if (mem.eql(u8, sect.sectName(), "__stubs")) sects.appendAssumeCapacity(sect);1163 fn sortFn(c: void, lhs: macho.section_64, rhs: macho.section_64) bool {
1237 if (mem.eql(u8, sect.sectName(), "__got")) sects.appendAssumeCapacity(sect);1164 _ = c;
1238 if (mem.eql(u8, sect.sectName(), "__la_symbol_ptr")) sects.appendAssumeCapacity(sect);1165 return lhs.reserved1 < rhs.reserved1;
1239 }1166 }
1167 }.sortFn;
1168 mem.sort(macho.section_64, sects, {}, sortFn);
12401169
1241 const sortFn = struct {1170 var i: usize = 0;
1242 fn sortFn(ctx: void, lhs: macho.section_64, rhs: macho.section_64) bool {1171 while (i < sects.len) : (i += 1) {
1243 _ = ctx;1172 const sect = sects[i];
1244 return lhs.reserved1 < rhs.reserved1;1173 const start = sect.reserved1;
1245 }1174 const end = if (i + 1 >= sects.len) ctx.indsymtab.items.len else sects[i + 1].reserved1;
1246 }.sortFn;1175 const entry_size = blk: {
1247 mem.sort(macho.section_64, sects.items, {}, sortFn);1176 if (mem.eql(u8, sect.sectName(), "__stubs")) break :blk sect.reserved2;
12481177 break :blk @sizeOf(u64);
1249 var i: usize = 0;1178 };
1250 while (i < sects.items.len) : (i += 1) {
1251 const sect = sects.items[i];
1252 const start = sect.reserved1;
1253 const end = if (i + 1 >= sects.items.len) symtab.indirect_symbols.len else sects.items[i + 1].reserved1;
1254 const entry_size = blk: {
1255 if (mem.eql(u8, sect.sectName(), "__stubs")) break :blk sect.reserved2;
1256 break :blk @sizeOf(u64);
1257 };
12581179
1259 try writer.print("{s},{s}\n", .{ sect.segName(), sect.sectName() });1180 try writer.print("{s},{s}\n", .{ sect.segName(), sect.sectName() });
1260 try writer.print("nentries {d}\n", .{end - start});1181 try writer.print("nentries {d}\n", .{end - start});
1261 for (symtab.indirect_symbols[start..end], 0..) |index, j| {1182 for (ctx.indsymtab.items[start..end], 0..) |index, j| {
1262 const sym = symtab.symbols[index];1183 const sym = ctx.symtab.items[index];
1263 const addr = sect.addr + entry_size * j;1184 const addr = sect.addr + entry_size * j;
1264 try writer.print("0x{x} {d} {s}\n", .{ addr, index, symtab.getString(sym.n_strx) });1185 try writer.print("0x{x} {d} {s}\n", .{ addr, index, ctx.getString(sym.n_strx) });
1186 }
1265 }1187 }
1266 }1188 }
1267 }
12681189
1269 fn dumpRebaseInfo(1190 fn dumpRebaseInfo(ctx: ObjectContext, data: []const u8, writer: anytype) !void {
1270 gpa: Allocator,1191 var rebases = std.ArrayList(u64).init(ctx.gpa);
1271 data: []const u8,1192 defer rebases.deinit();
1272 segments: []const macho.segment_command_64,1193 try ctx.parseRebaseInfo(data, &rebases);
1273 writer: anytype,1194 mem.sort(u64, rebases.items, {}, std.sort.asc(u64));
1274 ) !void {1195 for (rebases.items) |addr| {
1275 var rebases = std.ArrayList(u64).init(gpa);1196 try writer.print("0x{x}\n", .{addr});
1276 defer rebases.deinit();1197 }
1277 try parseRebaseInfo(data, segments, &rebases);
1278 mem.sort(u64, rebases.items, {}, std.sort.asc(u64));
1279 for (rebases.items) |addr| {
1280 try writer.print("0x{x}\n", .{addr});
1281 }1198 }
1282 }
12831199
1284 fn parseRebaseInfo(1200 fn parseRebaseInfo(ctx: ObjectContext, data: []const u8, rebases: *std.ArrayList(u64)) !void {
1285 data: []const u8,1201 var stream = std.io.fixedBufferStream(data);
1286 segments: []const macho.segment_command_64,1202 var creader = std.io.countingReader(stream.reader());
1287 rebases: *std.ArrayList(u64),1203 const reader = creader.reader();
1288 ) !void {
1289 var stream = std.io.fixedBufferStream(data);
1290 var creader = std.io.countingReader(stream.reader());
1291 const reader = creader.reader();
12921204
1293 var seg_id: ?u8 = null;1205 var seg_id: ?u8 = null;
1294 var offset: u64 = 0;1206 var offset: u64 = 0;
1295 while (true) {1207 while (true) {
1296 const byte = reader.readByte() catch break;1208 const byte = reader.readByte() catch break;
1297 const opc = byte & macho.REBASE_OPCODE_MASK;1209 const opc = byte & macho.REBASE_OPCODE_MASK;
1298 const imm = byte & macho.REBASE_IMMEDIATE_MASK;1210 const imm = byte & macho.REBASE_IMMEDIATE_MASK;
1299 switch (opc) {1211 switch (opc) {
1300 macho.REBASE_OPCODE_DONE => break,1212 macho.REBASE_OPCODE_DONE => break,
1301 macho.REBASE_OPCODE_SET_TYPE_IMM => {},1213 macho.REBASE_OPCODE_SET_TYPE_IMM => {},
1302 macho.REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB => {1214 macho.REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB => {
1303 seg_id = imm;1215 seg_id = imm;
1304 offset = try std.leb.readULEB128(u64, reader);1216 offset = try std.leb.readULEB128(u64, reader);
1305 },1217 },
1306 macho.REBASE_OPCODE_ADD_ADDR_IMM_SCALED => {1218 macho.REBASE_OPCODE_ADD_ADDR_IMM_SCALED => {
1307 offset += imm * @sizeOf(u64);1219 offset += imm * @sizeOf(u64);
1308 },1220 },
1309 macho.REBASE_OPCODE_ADD_ADDR_ULEB => {1221 macho.REBASE_OPCODE_ADD_ADDR_ULEB => {
1310 const addend = try std.leb.readULEB128(u64, reader);1222 const addend = try std.leb.readULEB128(u64, reader);
1311 offset += addend;1223 offset += addend;
1312 },1224 },
1313 macho.REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB => {1225 macho.REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB => {
1314 const addend = try std.leb.readULEB128(u64, reader);1226 const addend = try std.leb.readULEB128(u64, reader);
1315 const seg = segments[seg_id.?];1227 const seg = ctx.segments.items[seg_id.?];
1316 const addr = seg.vmaddr + offset;1228 const addr = seg.vmaddr + offset;
1317 try rebases.append(addr);
1318 offset += addend + @sizeOf(u64);
1319 },
1320 macho.REBASE_OPCODE_DO_REBASE_IMM_TIMES,
1321 macho.REBASE_OPCODE_DO_REBASE_ULEB_TIMES,
1322 macho.REBASE_OPCODE_DO_REBASE_ULEB_TIMES_SKIPPING_ULEB,
1323 => {
1324 var ntimes: u64 = 1;
1325 var skip: u64 = 0;
1326 switch (opc) {
1327 macho.REBASE_OPCODE_DO_REBASE_IMM_TIMES => {
1328 ntimes = imm;
1329 },
1330 macho.REBASE_OPCODE_DO_REBASE_ULEB_TIMES => {
1331 ntimes = try std.leb.readULEB128(u64, reader);
1332 },
1333 macho.REBASE_OPCODE_DO_REBASE_ULEB_TIMES_SKIPPING_ULEB => {
1334 ntimes = try std.leb.readULEB128(u64, reader);
1335 skip = try std.leb.readULEB128(u64, reader);
1336 },
1337 else => unreachable,
1338 }
1339 const seg = segments[seg_id.?];
1340 const base_addr = seg.vmaddr;
1341 var count: usize = 0;
1342 while (count < ntimes) : (count += 1) {
1343 const addr = base_addr + offset;
1344 try rebases.append(addr);1229 try rebases.append(addr);
1345 offset += skip + @sizeOf(u64);1230 offset += addend + @sizeOf(u64);
1346 }1231 },
1347 },1232 macho.REBASE_OPCODE_DO_REBASE_IMM_TIMES,
1348 else => break,1233 macho.REBASE_OPCODE_DO_REBASE_ULEB_TIMES,
1234 macho.REBASE_OPCODE_DO_REBASE_ULEB_TIMES_SKIPPING_ULEB,
1235 => {
1236 var ntimes: u64 = 1;
1237 var skip: u64 = 0;
1238 switch (opc) {
1239 macho.REBASE_OPCODE_DO_REBASE_IMM_TIMES => {
1240 ntimes = imm;
1241 },
1242 macho.REBASE_OPCODE_DO_REBASE_ULEB_TIMES => {
1243 ntimes = try std.leb.readULEB128(u64, reader);
1244 },
1245 macho.REBASE_OPCODE_DO_REBASE_ULEB_TIMES_SKIPPING_ULEB => {
1246 ntimes = try std.leb.readULEB128(u64, reader);
1247 skip = try std.leb.readULEB128(u64, reader);
1248 },
1249 else => unreachable,
1250 }
1251 const seg = ctx.segments.items[seg_id.?];
1252 const base_addr = seg.vmaddr;
1253 var count: usize = 0;
1254 while (count < ntimes) : (count += 1) {
1255 const addr = base_addr + offset;
1256 try rebases.append(addr);
1257 offset += skip + @sizeOf(u64);
1258 }
1259 },
1260 else => break,
1261 }
1349 }1262 }
1350 }1263 }
1351 }
13521264
1353 const Binding = struct {1265 const Binding = struct {
1354 address: u64,1266 address: u64,
1355 addend: i64,1267 addend: i64,
1356 ordinal: u16,1268 ordinal: u16,
1357 tag: Tag,1269 tag: Tag,
1358 name: []const u8,1270 name: []const u8,
13591271
1360 fn deinit(binding: *Binding, gpa: Allocator) void {1272 fn deinit(binding: *Binding, gpa: Allocator) void {
1361 gpa.free(binding.name);1273 gpa.free(binding.name);
1362 }1274 }
13631275
1364 fn lessThan(ctx: void, lhs: Binding, rhs: Binding) bool {1276 fn lessThan(ctx: void, lhs: Binding, rhs: Binding) bool {
1365 _ = ctx;1277 _ = ctx;
1366 return lhs.address < rhs.address;1278 return lhs.address < rhs.address;
1367 }1279 }
13681280
1369 const Tag = enum {1281 const Tag = enum {
1370 ord,1282 ord,
1371 self,1283 self,
1372 exe,1284 exe,
1373 flat,1285 flat,
1286 };
1374 };1287 };
1375 };
13761288
1377 fn dumpBindInfo(1289 fn dumpBindInfo(ctx: ObjectContext, data: []const u8, writer: anytype) !void {
1378 gpa: Allocator,1290 var bindings = std.ArrayList(Binding).init(ctx.gpa);
1379 data: []const u8,1291 defer {
1380 segments: []const macho.segment_command_64,1292 for (bindings.items) |*b| {
1381 dylibs: []const []const u8,1293 b.deinit(ctx.gpa);
1382 writer: anytype,1294 }
1383 ) !void {1295 bindings.deinit();
1384 var bindings = std.ArrayList(Binding).init(gpa);1296 }
1385 defer {1297 try ctx.parseBindInfo(data, &bindings);
1386 for (bindings.items) |*b| {1298 mem.sort(Binding, bindings.items, {}, Binding.lessThan);
1387 b.deinit(gpa);1299 for (bindings.items) |binding| {
1300 try writer.print("0x{x} [addend: {d}]", .{ binding.address, binding.addend });
1301 try writer.writeAll(" (");
1302 switch (binding.tag) {
1303 .self => try writer.writeAll("self"),
1304 .exe => try writer.writeAll("main executable"),
1305 .flat => try writer.writeAll("flat lookup"),
1306 .ord => try writer.writeAll(std.fs.path.basename(ctx.imports.items[binding.ordinal - 1])),
1307 }
1308 try writer.print(") {s}\n", .{binding.name});
1388 }1309 }
1389 bindings.deinit();
1390 }1310 }
1391 try parseBindInfo(gpa, data, segments, &bindings);1311
1392 mem.sort(Binding, bindings.items, {}, Binding.lessThan);1312 fn parseBindInfo(ctx: ObjectContext, data: []const u8, bindings: *std.ArrayList(Binding)) !void {
1393 for (bindings.items) |binding| {1313 var stream = std.io.fixedBufferStream(data);
1394 try writer.print("0x{x} [addend: {d}]", .{ binding.address, binding.addend });1314 var creader = std.io.countingReader(stream.reader());
1395 try writer.writeAll(" (");1315 const reader = creader.reader();
1396 switch (binding.tag) {1316
1397 .self => try writer.writeAll("self"),1317 var seg_id: ?u8 = null;
1398 .exe => try writer.writeAll("main executable"),1318 var tag: Binding.Tag = .self;
1399 .flat => try writer.writeAll("flat lookup"),1319 var ordinal: u16 = 0;
1400 .ord => try writer.writeAll(std.fs.path.basename(dylibs[binding.ordinal - 1])),1320 var offset: u64 = 0;
1321 var addend: i64 = 0;
1322
1323 var name_buf = std.ArrayList(u8).init(ctx.gpa);
1324 defer name_buf.deinit();
1325
1326 while (true) {
1327 const byte = reader.readByte() catch break;
1328 const opc = byte & macho.BIND_OPCODE_MASK;
1329 const imm = byte & macho.BIND_IMMEDIATE_MASK;
1330 switch (opc) {
1331 macho.BIND_OPCODE_DONE,
1332 macho.BIND_OPCODE_SET_TYPE_IMM,
1333 => {},
1334 macho.BIND_OPCODE_SET_DYLIB_ORDINAL_IMM => {
1335 tag = .ord;
1336 ordinal = imm;
1337 },
1338 macho.BIND_OPCODE_SET_DYLIB_SPECIAL_IMM => {
1339 switch (imm) {
1340 0 => tag = .self,
1341 0xf => tag = .exe,
1342 0xe => tag = .flat,
1343 else => unreachable,
1344 }
1345 },
1346 macho.BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB => {
1347 seg_id = imm;
1348 offset = try std.leb.readULEB128(u64, reader);
1349 },
1350 macho.BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM => {
1351 name_buf.clearRetainingCapacity();
1352 try reader.readUntilDelimiterArrayList(&name_buf, 0, std.math.maxInt(u32));
1353 try name_buf.append(0);
1354 },
1355 macho.BIND_OPCODE_SET_ADDEND_SLEB => {
1356 addend = try std.leb.readILEB128(i64, reader);
1357 },
1358 macho.BIND_OPCODE_ADD_ADDR_ULEB => {
1359 const x = try std.leb.readULEB128(u64, reader);
1360 offset = @intCast(@as(i64, @intCast(offset)) + @as(i64, @bitCast(x)));
1361 },
1362 macho.BIND_OPCODE_DO_BIND,
1363 macho.BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB,
1364 macho.BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED,
1365 macho.BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB,
1366 => {
1367 var add_addr: u64 = 0;
1368 var count: u64 = 1;
1369 var skip: u64 = 0;
1370
1371 switch (opc) {
1372 macho.BIND_OPCODE_DO_BIND => {},
1373 macho.BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB => {
1374 add_addr = try std.leb.readULEB128(u64, reader);
1375 },
1376 macho.BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED => {
1377 add_addr = imm * @sizeOf(u64);
1378 },
1379 macho.BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB => {
1380 count = try std.leb.readULEB128(u64, reader);
1381 skip = try std.leb.readULEB128(u64, reader);
1382 },
1383 else => unreachable,
1384 }
1385
1386 const seg = ctx.segments.items[seg_id.?];
1387 var i: u64 = 0;
1388 while (i < count) : (i += 1) {
1389 const addr: u64 = @intCast(@as(i64, @intCast(seg.vmaddr + offset)));
1390 try bindings.append(.{
1391 .address = addr,
1392 .addend = addend,
1393 .tag = tag,
1394 .ordinal = ordinal,
1395 .name = try ctx.gpa.dupe(u8, name_buf.items),
1396 });
1397 offset += skip + @sizeOf(u64) + add_addr;
1398 }
1399 },
1400 else => break,
1401 }
1401 }1402 }
1402 try writer.print(") {s}\n", .{binding.name});
1403 }1403 }
1404 }
14051404
1406 fn parseBindInfo(1405 fn dumpExportsTrie(ctx: ObjectContext, data: []const u8, writer: anytype) !void {
1407 gpa: Allocator,1406 const seg = ctx.getSegmentByName("__TEXT") orelse return;
1408 data: []const u8,
1409 segments: []const macho.segment_command_64,
1410 bindings: *std.ArrayList(Binding),
1411 ) !void {
1412 var stream = std.io.fixedBufferStream(data);
1413 var creader = std.io.countingReader(stream.reader());
1414 const reader = creader.reader();
14151407
1416 var seg_id: ?u8 = null;1408 var arena = std.heap.ArenaAllocator.init(ctx.gpa);
1417 var tag: Binding.Tag = .self;1409 defer arena.deinit();
1418 var ordinal: u16 = 0;
1419 var offset: u64 = 0;
1420 var addend: i64 = 0;
14211410
1422 var name_buf = std.ArrayList(u8).init(gpa);1411 var exports = std.ArrayList(Export).init(arena.allocator());
1423 defer name_buf.deinit();1412 var it = TrieIterator{ .data = data };
1413 try parseTrieNode(arena.allocator(), &it, "", &exports);
14241414
1425 while (true) {1415 mem.sort(Export, exports.items, {}, Export.lessThan);
1426 const byte = reader.readByte() catch break;
1427 const opc = byte & macho.BIND_OPCODE_MASK;
1428 const imm = byte & macho.BIND_IMMEDIATE_MASK;
1429 switch (opc) {
1430 macho.BIND_OPCODE_DONE,
1431 macho.BIND_OPCODE_SET_TYPE_IMM,
1432 => {},
1433 macho.BIND_OPCODE_SET_DYLIB_ORDINAL_IMM => {
1434 tag = .ord;
1435 ordinal = imm;
1436 },
1437 macho.BIND_OPCODE_SET_DYLIB_SPECIAL_IMM => {
1438 switch (imm) {
1439 0 => tag = .self,
1440 0xf => tag = .exe,
1441 0xe => tag = .flat,
1442 else => unreachable,
1443 }
1444 },
1445 macho.BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB => {
1446 seg_id = imm;
1447 offset = try std.leb.readULEB128(u64, reader);
1448 },
1449 macho.BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM => {
1450 name_buf.clearRetainingCapacity();
1451 try reader.readUntilDelimiterArrayList(&name_buf, 0, std.math.maxInt(u32));
1452 try name_buf.append(0);
1453 },
1454 macho.BIND_OPCODE_SET_ADDEND_SLEB => {
1455 addend = try std.leb.readILEB128(i64, reader);
1456 },
1457 macho.BIND_OPCODE_ADD_ADDR_ULEB => {
1458 const x = try std.leb.readULEB128(u64, reader);
1459 offset = @intCast(@as(i64, @intCast(offset)) + @as(i64, @bitCast(x)));
1460 },
1461 macho.BIND_OPCODE_DO_BIND,
1462 macho.BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB,
1463 macho.BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED,
1464 macho.BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB,
1465 => {
1466 var add_addr: u64 = 0;
1467 var count: u64 = 1;
1468 var skip: u64 = 0;
1469
1470 switch (opc) {
1471 macho.BIND_OPCODE_DO_BIND => {},
1472 macho.BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB => {
1473 add_addr = try std.leb.readULEB128(u64, reader);
1474 },
1475 macho.BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED => {
1476 add_addr = imm * @sizeOf(u64);
1477 },
1478 macho.BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB => {
1479 count = try std.leb.readULEB128(u64, reader);
1480 skip = try std.leb.readULEB128(u64, reader);
1481 },
1482 else => unreachable,
1483 }
14841416
1485 const seg = segments[seg_id.?];1417 for (exports.items) |exp| {
1486 var i: u64 = 0;1418 switch (exp.tag) {
1487 while (i < count) : (i += 1) {1419 .@"export" => {
1488 const addr: u64 = @intCast(@as(i64, @intCast(seg.vmaddr + offset)));1420 const info = exp.data.@"export";
1489 try bindings.append(.{1421 if (info.kind != .regular or info.weak) {
1490 .address = addr,1422 try writer.writeByte('[');
1491 .addend = addend,1423 }
1492 .tag = tag,1424 switch (info.kind) {
1493 .ordinal = ordinal,1425 .regular => {},
1494 .name = try gpa.dupe(u8, name_buf.items),1426 .absolute => try writer.writeAll("ABS, "),
1495 });1427 .tlv => try writer.writeAll("THREAD_LOCAL, "),
1496 offset += skip + @sizeOf(u64) + add_addr;1428 }
1497 }1429 if (info.weak) try writer.writeAll("WEAK");
1498 },1430 if (info.kind != .regular or info.weak) {
1499 else => break,1431 try writer.writeAll("] ");
1432 }
1433 try writer.print("{x} ", .{seg.vmaddr + info.vmoffset});
1434 },
1435 else => {},
1436 }
1437
1438 try writer.print("{s}\n", .{exp.name});
1500 }1439 }
1501 }1440 }
1502 }
1503
1504 fn dumpExportsTrie(
1505 gpa: Allocator,
1506 data: []const u8,
1507 seg: macho.segment_command_64,
1508 writer: anytype,
1509 ) !void {
1510 var arena = std.heap.ArenaAllocator.init(gpa);
1511 defer arena.deinit();
15121441
1513 var exports = std.ArrayList(Export).init(arena.allocator());1442 const TrieIterator = struct {
1514 var it = TrieIterator{ .data = data };1443 data: []const u8,
1515 try parseTrieNode(arena.allocator(), &it, "", &exports);1444 pos: usize = 0,
15161445
1517 mem.sort(Export, exports.items, {}, Export.lessThan);1446 fn getStream(it: *TrieIterator) std.io.FixedBufferStream([]const u8) {
1447 return std.io.fixedBufferStream(it.data[it.pos..]);
1448 }
15181449
1519 for (exports.items) |exp| {1450 fn readULEB128(it: *TrieIterator) !u64 {
1520 switch (exp.tag) {1451 var stream = it.getStream();
1521 .@"export" => {1452 var creader = std.io.countingReader(stream.reader());
1522 const info = exp.data.@"export";1453 const reader = creader.reader();
1523 if (info.kind != .regular or info.weak) {1454 const value = try std.leb.readULEB128(u64, reader);
1524 try writer.writeByte('[');1455 it.pos += creader.bytes_read;
1525 }1456 return value;
1526 switch (info.kind) {
1527 .regular => {},
1528 .absolute => try writer.writeAll("ABS, "),
1529 .tlv => try writer.writeAll("THREAD_LOCAL, "),
1530 }
1531 if (info.weak) try writer.writeAll("WEAK");
1532 if (info.kind != .regular or info.weak) {
1533 try writer.writeAll("] ");
1534 }
1535 try writer.print("{x} ", .{seg.vmaddr + info.vmoffset});
1536 },
1537 else => {},
1538 }1457 }
15391458
1540 try writer.print("{s}\n", .{exp.name});1459 fn readString(it: *TrieIterator) ![:0]const u8 {
1541 }1460 var stream = it.getStream();
1542 }1461 const reader = stream.reader();
15431462
1544 const TrieIterator = struct {1463 var count: usize = 0;
1545 data: []const u8,1464 while (true) : (count += 1) {
1546 pos: usize = 0,1465 const byte = try reader.readByte();
1466 if (byte == 0) break;
1467 }
15471468
1548 fn getStream(it: *TrieIterator) std.io.FixedBufferStream([]const u8) {1469 const str = @as([*:0]const u8, @ptrCast(it.data.ptr + it.pos))[0..count :0];
1549 return std.io.fixedBufferStream(it.data[it.pos..]);1470 it.pos += count + 1;
1550 }1471 return str;
1472 }
15511473
1552 fn readULEB128(it: *TrieIterator) !u64 {1474 fn readByte(it: *TrieIterator) !u8 {
1553 var stream = it.getStream();1475 var stream = it.getStream();
1554 var creader = std.io.countingReader(stream.reader());1476 const value = try stream.reader().readByte();
1555 const reader = creader.reader();1477 it.pos += 1;
1556 const value = try std.leb.readULEB128(u64, reader);1478 return value;
1557 it.pos += creader.bytes_read;1479 }
1558 return value;1480 };
1559 }
15601481
1561 fn readString(it: *TrieIterator) ![:0]const u8 {1482 const Export = struct {
1562 var stream = it.getStream();1483 name: []const u8,
1563 const reader = stream.reader();1484 tag: enum { @"export", reexport, stub_resolver },
1485 data: union {
1486 @"export": struct {
1487 kind: enum { regular, absolute, tlv },
1488 weak: bool = false,
1489 vmoffset: u64,
1490 },
1491 reexport: u64,
1492 stub_resolver: struct {
1493 stub_offset: u64,
1494 resolver_offset: u64,
1495 },
1496 },
1497
1498 inline fn rankByTag(@"export": Export) u3 {
1499 return switch (@"export".tag) {
1500 .@"export" => 1,
1501 .reexport => 2,
1502 .stub_resolver => 3,
1503 };
1504 }
1505
1506 fn lessThan(ctx: void, lhs: Export, rhs: Export) bool {
1507 _ = ctx;
1508 if (lhs.rankByTag() == rhs.rankByTag()) {
1509 return switch (lhs.tag) {
1510 .@"export" => lhs.data.@"export".vmoffset < rhs.data.@"export".vmoffset,
1511 .reexport => lhs.data.reexport < rhs.data.reexport,
1512 .stub_resolver => lhs.data.stub_resolver.stub_offset < rhs.data.stub_resolver.stub_offset,
1513 };
1514 }
1515 return lhs.rankByTag() < rhs.rankByTag();
1516 }
1517 };
15641518
1565 var count: usize = 0;1519 fn parseTrieNode(
1566 while (true) : (count += 1) {1520 arena: Allocator,
1567 const byte = try reader.readByte();1521 it: *TrieIterator,
1568 if (byte == 0) break;1522 prefix: []const u8,
1523 exports: *std.ArrayList(Export),
1524 ) !void {
1525 const size = try it.readULEB128();
1526 if (size > 0) {
1527 const flags = try it.readULEB128();
1528 switch (flags) {
1529 macho.EXPORT_SYMBOL_FLAGS_REEXPORT => {
1530 const ord = try it.readULEB128();
1531 const name = try arena.dupe(u8, try it.readString());
1532 try exports.append(.{
1533 .name = if (name.len > 0) name else prefix,
1534 .tag = .reexport,
1535 .data = .{ .reexport = ord },
1536 });
1537 },
1538 macho.EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER => {
1539 const stub_offset = try it.readULEB128();
1540 const resolver_offset = try it.readULEB128();
1541 try exports.append(.{
1542 .name = prefix,
1543 .tag = .stub_resolver,
1544 .data = .{ .stub_resolver = .{
1545 .stub_offset = stub_offset,
1546 .resolver_offset = resolver_offset,
1547 } },
1548 });
1549 },
1550 else => {
1551 const vmoff = try it.readULEB128();
1552 try exports.append(.{
1553 .name = prefix,
1554 .tag = .@"export",
1555 .data = .{ .@"export" = .{
1556 .kind = switch (flags & macho.EXPORT_SYMBOL_FLAGS_KIND_MASK) {
1557 macho.EXPORT_SYMBOL_FLAGS_KIND_REGULAR => .regular,
1558 macho.EXPORT_SYMBOL_FLAGS_KIND_ABSOLUTE => .absolute,
1559 macho.EXPORT_SYMBOL_FLAGS_KIND_THREAD_LOCAL => .tlv,
1560 else => unreachable,
1561 },
1562 .weak = flags & macho.EXPORT_SYMBOL_FLAGS_WEAK_DEFINITION != 0,
1563 .vmoffset = vmoff,
1564 } },
1565 });
1566 },
1567 }
1569 }1568 }
15701569
1571 const str = @as([*:0]const u8, @ptrCast(it.data.ptr + it.pos))[0..count :0];1570 const nedges = try it.readByte();
1572 it.pos += count + 1;1571 for (0..nedges) |_| {
1573 return str;1572 const label = try it.readString();
1573 const off = try it.readULEB128();
1574 const prefix_label = try std.fmt.allocPrint(arena, "{s}{s}", .{ prefix, label });
1575 const curr = it.pos;
1576 it.pos = off;
1577 try parseTrieNode(arena, it, prefix_label, exports);
1578 it.pos = curr;
1579 }
1574 }1580 }
15751581
1576 fn readByte(it: *TrieIterator) !u8 {1582 fn dumpSection(ctx: ObjectContext, sect: macho.section_64, writer: anytype) !void {
1577 var stream = it.getStream();1583 const data = ctx.data[sect.offset..][0..sect.size];
1578 const value = try stream.reader().readByte();1584 try writer.print("{s}", .{data});
1579 it.pos += 1;
1580 return value;
1581 }1585 }
1582 };1586 };
15831587
1584 const Export = struct {1588 fn parseAndDumpObject(step: *Step, check: Check, bytes: []const u8) ![]const u8 {
1585 name: []const u8,1589 const gpa = step.owner.allocator;
1586 tag: enum { @"export", reexport, stub_resolver },1590 const hdr = @as(*align(1) const macho.mach_header_64, @ptrCast(bytes.ptr)).*;
1587 data: union {1591 if (hdr.magic != macho.MH_MAGIC_64) {
1588 @"export": struct {1592 return error.InvalidMagicNumber;
1589 kind: enum { regular, absolute, tlv },1593 }
1590 weak: bool = false,1594
1591 vmoffset: u64,1595 var ctx = ObjectContext{ .gpa = gpa, .data = bytes, .header = hdr };
1596 try ctx.parse();
1597
1598 var output = std.ArrayList(u8).init(gpa);
1599 const writer = output.writer();
1600
1601 switch (check.kind) {
1602 .headers => {
1603 try ObjectContext.dumpHeader(ctx.header, writer);
1604
1605 var it = ctx.getLoadCommandIterator();
1606 var i: usize = 0;
1607 while (it.next()) |cmd| {
1608 try ObjectContext.dumpLoadCommand(cmd, i, writer);
1609 try writer.writeByte('\n');
1610
1611 i += 1;
1612 }
1592 },1613 },
1593 reexport: u64,1614
1594 stub_resolver: struct {1615 .symtab => if (ctx.symtab.items.len > 0) {
1595 stub_offset: u64,1616 try ctx.dumpSymtab(writer);
1596 resolver_offset: u64,1617 } else return step.fail("no symbol table found", .{}),
1618
1619 .indirect_symtab => if (ctx.symtab.items.len > 0 and ctx.indsymtab.items.len > 0) {
1620 try ctx.dumpIndirectSymtab(writer);
1621 } else return step.fail("no indirect symbol table found", .{}),
1622
1623 .dyld_rebase,
1624 .dyld_bind,
1625 .dyld_weak_bind,
1626 .dyld_lazy_bind,
1627 => {
1628 const cmd = ctx.getLoadCommand(.DYLD_INFO_ONLY) orelse
1629 return step.fail("no dyld info found", .{});
1630 const lc = cmd.cast(macho.dyld_info_command).?;
1631
1632 switch (check.kind) {
1633 .dyld_rebase => if (lc.rebase_size > 0) {
1634 const data = ctx.data[lc.rebase_off..][0..lc.rebase_size];
1635 try writer.writeAll(dyld_rebase_label ++ "\n");
1636 try ctx.dumpRebaseInfo(data, writer);
1637 } else return step.fail("no rebase data found", .{}),
1638
1639 .dyld_bind => if (lc.bind_size > 0) {
1640 const data = ctx.data[lc.bind_off..][0..lc.bind_size];
1641 try writer.writeAll(dyld_bind_label ++ "\n");
1642 try ctx.dumpBindInfo(data, writer);
1643 } else return step.fail("no bind data found", .{}),
1644
1645 .dyld_weak_bind => if (lc.weak_bind_size > 0) {
1646 const data = ctx.data[lc.weak_bind_off..][0..lc.weak_bind_size];
1647 try writer.writeAll(dyld_weak_bind_label ++ "\n");
1648 try ctx.dumpBindInfo(data, writer);
1649 } else return step.fail("no weak bind data found", .{}),
1650
1651 .dyld_lazy_bind => if (lc.lazy_bind_size > 0) {
1652 const data = ctx.data[lc.lazy_bind_off..][0..lc.lazy_bind_size];
1653 try writer.writeAll(dyld_lazy_bind_label ++ "\n");
1654 try ctx.dumpBindInfo(data, writer);
1655 } else return step.fail("no lazy bind data found", .{}),
1656
1657 else => unreachable,
1658 }
1597 },1659 },
1598 },
15991660
1600 inline fn rankByTag(@"export": Export) u3 {1661 .exports => blk: {
1601 return switch (@"export".tag) {1662 if (ctx.getLoadCommand(.DYLD_INFO_ONLY)) |cmd| {
1602 .@"export" => 1,1663 const lc = cmd.cast(macho.dyld_info_command).?;
1603 .reexport => 2,1664 if (lc.export_size > 0) {
1604 .stub_resolver => 3,1665 const data = ctx.data[lc.export_off..][0..lc.export_size];
1605 };1666 try writer.writeAll(exports_label ++ "\n");
1606 }1667 try ctx.dumpExportsTrie(data, writer);
1668 break :blk;
1669 }
1670 }
1671 return step.fail("no exports data found", .{});
1672 },
16071673
1608 fn lessThan(ctx: void, lhs: Export, rhs: Export) bool {1674 .dump_section => {
1609 _ = ctx;1675 const name = mem.sliceTo(@as([*:0]const u8, @ptrCast(check.data.items.ptr + check.payload.dump_section)), 0);
1610 if (lhs.rankByTag() == rhs.rankByTag()) {1676 const sep_index = mem.indexOfScalar(u8, name, ',') orelse
1611 return switch (lhs.tag) {1677 return step.fail("invalid section name: {s}", .{name});
1612 .@"export" => lhs.data.@"export".vmoffset < rhs.data.@"export".vmoffset,1678 const segname = name[0..sep_index];
1613 .reexport => lhs.data.reexport < rhs.data.reexport,1679 const sectname = name[sep_index + 1 ..];
1614 .stub_resolver => lhs.data.stub_resolver.stub_offset < rhs.data.stub_resolver.stub_offset,1680 const sect = ctx.getSectionByName(segname, sectname) orelse
1615 };1681 return step.fail("section '{s}' not found", .{name});
1616 }1682 try ctx.dumpSection(sect, writer);
1617 return lhs.rankByTag() < rhs.rankByTag();1683 },
1618 }
1619 };
16201684
1621 fn parseTrieNode(1685 else => return step.fail("invalid check kind for MachO file format: {s}", .{@tagName(check.kind)}),
1622 arena: Allocator,
1623 it: *TrieIterator,
1624 prefix: []const u8,
1625 exports: *std.ArrayList(Export),
1626 ) !void {
1627 const size = try it.readULEB128();
1628 if (size > 0) {
1629 const flags = try it.readULEB128();
1630 switch (flags) {
1631 macho.EXPORT_SYMBOL_FLAGS_REEXPORT => {
1632 const ord = try it.readULEB128();
1633 const name = try arena.dupe(u8, try it.readString());
1634 try exports.append(.{
1635 .name = if (name.len > 0) name else prefix,
1636 .tag = .reexport,
1637 .data = .{ .reexport = ord },
1638 });
1639 },
1640 macho.EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER => {
1641 const stub_offset = try it.readULEB128();
1642 const resolver_offset = try it.readULEB128();
1643 try exports.append(.{
1644 .name = prefix,
1645 .tag = .stub_resolver,
1646 .data = .{ .stub_resolver = .{
1647 .stub_offset = stub_offset,
1648 .resolver_offset = resolver_offset,
1649 } },
1650 });
1651 },
1652 else => {
1653 const vmoff = try it.readULEB128();
1654 try exports.append(.{
1655 .name = prefix,
1656 .tag = .@"export",
1657 .data = .{ .@"export" = .{
1658 .kind = switch (flags & macho.EXPORT_SYMBOL_FLAGS_KIND_MASK) {
1659 macho.EXPORT_SYMBOL_FLAGS_KIND_REGULAR => .regular,
1660 macho.EXPORT_SYMBOL_FLAGS_KIND_ABSOLUTE => .absolute,
1661 macho.EXPORT_SYMBOL_FLAGS_KIND_THREAD_LOCAL => .tlv,
1662 else => unreachable,
1663 },
1664 .weak = flags & macho.EXPORT_SYMBOL_FLAGS_WEAK_DEFINITION != 0,
1665 .vmoffset = vmoff,
1666 } },
1667 });
1668 },
1669 }
1670 }1686 }
16711687
1672 const nedges = try it.readByte();1688 return output.toOwnedSlice();
1673 for (0..nedges) |_| {
1674 const label = try it.readString();
1675 const off = try it.readULEB128();
1676 const prefix_label = try std.fmt.allocPrint(arena, "{s}{s}", .{ prefix, label });
1677 const curr = it.pos;
1678 it.pos = off;
1679 try parseTrieNode(arena, it, prefix_label, exports);
1680 it.pos = curr;
1681 }
1682 }1689 }
1683};1690};
16841691
test/standalone/ios/build.zig+3-3
...@@ -22,9 +22,9 @@ pub fn build(b: *std.Build) void {...@@ -22,9 +22,9 @@ pub fn build(b: *std.Build) void {
22 .target = target,22 .target = target,
23 });23 });
24 exe.addCSourceFile(.{ .file = b.path("main.m"), .flags = &.{} });24 exe.addCSourceFile(.{ .file = b.path("main.m"), .flags = &.{} });
25 exe.addSystemIncludePath(b.path(b.pathJoin(&.{ sdk, "/usr/include" })));25 exe.addSystemIncludePath(.{ .cwd_relative = b.pathJoin(&.{ sdk, "/usr/include" }) });
26 exe.addSystemFrameworkPath(b.path(b.pathJoin(&.{ sdk, "/System/Library/Frameworks" })));26 exe.addSystemFrameworkPath(.{ .cwd_relative = b.pathJoin(&.{ sdk, "/System/Library/Frameworks" }) });
27 exe.addLibraryPath(b.path(b.pathJoin(&.{ sdk, "/usr/lib" })));27 exe.addLibraryPath(.{ .cwd_relative = b.pathJoin(&.{ sdk, "/usr/lib" }) });
28 exe.linkFramework("Foundation");28 exe.linkFramework("Foundation");
29 exe.linkFramework("UIKit");29 exe.linkFramework("UIKit");
30 exe.linkLibC();30 exe.linkLibC();