authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2023-12-10 09:48:50+01:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2023-12-10 09:48:50+01:00
log5c0d58b71f16ba569215686ba95b751573d658e9
tree45d8fdb6f05fe24aa1683cdfc3b387658d1e8ee6
parent69195d0cd43468f213332c5792df04c941f8313d
parenta2d8e039312a1b5505a8e8d0e114da6abc3fc7e6
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #18243 from ziglang/check-macho-more

lib/std/Build/CheckObject: dump more Mach-O info

3 files changed, 543 insertions(+), 11 deletions(-)

lib/std/Build/Step/CheckObject.zig+540-8
......@@ -381,6 +381,18 @@ pub fn checkInSymtab(self: *CheckObject) void {
381381 self.checkExact(label);
382382}
383383
384/// Creates a new check checking specifically dyld_info_only contents parsed and dumped
385/// from the object file.
386/// This check is target-dependent and applicable to MachO only.
387pub fn checkInDyldInfo(self: *CheckObject) void {
388 const label = switch (self.obj_format) {
389 .macho => MachODumper.dyld_info_label,
390 else => @panic("Unsupported target platform"),
391 };
392 self.checkStart();
393 self.checkExact(label);
394}
395
384396/// Creates a new check checking specifically dynamic symbol table parsed and dumped from the object
385397/// file.
386398/// This check is target-dependent and applicable to ELF only.
......@@ -543,6 +555,7 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
543555
544556const MachODumper = struct {
545557 const LoadCommandIterator = macho.LoadCommandIterator;
558 const dyld_info_label = "dyld info data";
546559 const symtab_label = "symbol table";
547560
548561 const Symtab = struct {
......@@ -564,8 +577,16 @@ const MachODumper = struct {
564577 const writer = output.writer();
565578
566579 var symtab: ?Symtab = null;
580 var segments = std.ArrayList(macho.segment_command_64).init(gpa);
581 defer segments.deinit();
567582 var sections = std.ArrayList(macho.section_64).init(gpa);
583 defer sections.deinit();
568584 var imports = std.ArrayList([]const u8).init(gpa);
585 defer imports.deinit();
586 var text_seg: ?u8 = null;
587 var dyld_info_lc: ?macho.dyld_info_command = null;
588
589 try dumpHeader(hdr, writer);
569590
570591 var it: LoadCommandIterator = .{
571592 .ncmds = hdr.ncmds,
......@@ -580,6 +601,11 @@ const MachODumper = struct {
580601 for (cmd.getSections()) |sect| {
581602 sections.appendAssumeCapacity(sect);
582603 }
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 }
583609 },
584610 .SYMTAB => {
585611 const lc = cmd.cast(macho.symtab_command).?;
......@@ -593,6 +619,9 @@ const MachODumper = struct {
593619 => {
594620 try imports.append(cmd.getDylibPathName());
595621 },
622 .DYLD_INFO_ONLY => {
623 dyld_info_lc = cmd.cast(macho.dyld_info_command).?;
624 },
596625 else => {},
597626 }
598627
......@@ -606,9 +635,106 @@ const MachODumper = struct {
606635 try dumpSymtab(sections.items, imports.items, stab, writer);
607636 }
608637
638 if (dyld_info_lc) |lc| {
639 try writer.writeAll(dyld_info_label ++ "\n");
640 if (lc.rebase_size > 0) {
641 const data = bytes[lc.rebase_off..][0..lc.rebase_size];
642 try writer.writeAll("rebase info\n");
643 try dumpRebaseInfo(gpa, data, segments.items, writer);
644 }
645 if (lc.bind_size > 0) {
646 const data = bytes[lc.bind_off..][0..lc.bind_size];
647 try writer.writeAll("bind info\n");
648 try dumpBindInfo(gpa, data, segments.items, imports.items, false, writer);
649 }
650 if (lc.weak_bind_size > 0) {
651 const data = bytes[lc.weak_bind_off..][0..lc.weak_bind_size];
652 try writer.writeAll("weak bind info\n");
653 try dumpBindInfo(gpa, data, segments.items, imports.items, false, writer);
654 }
655 if (lc.lazy_bind_size > 0) {
656 const data = bytes[lc.lazy_bind_off..][0..lc.lazy_bind_size];
657 try writer.writeAll("lazy bind info\n");
658 try dumpBindInfo(gpa, data, segments.items, imports.items, true, writer);
659 }
660 if (lc.export_size > 0) {
661 const data = bytes[lc.export_off..][0..lc.export_size];
662 try writer.writeAll("exports\n");
663 try dumpExportsTrie(gpa, data, segments.items[text_seg.?], writer);
664 }
665 }
666
609667 return output.toOwnedSlice();
610668 }
611669
670 fn dumpHeader(hdr: macho.mach_header_64, writer: anytype) !void {
671 const cputype = switch (hdr.cputype) {
672 macho.CPU_TYPE_ARM64 => "ARM64",
673 macho.CPU_TYPE_X86_64 => "X86_64",
674 else => "Unknown",
675 };
676 const filetype = switch (hdr.filetype) {
677 macho.MH_OBJECT => "MH_OBJECT",
678 macho.MH_EXECUTE => "MH_EXECUTE",
679 macho.MH_FVMLIB => "MH_FVMLIB",
680 macho.MH_CORE => "MH_CORE",
681 macho.MH_PRELOAD => "MH_PRELOAD",
682 macho.MH_DYLIB => "MH_DYLIB",
683 macho.MH_DYLINKER => "MH_DYLINKER",
684 macho.MH_BUNDLE => "MH_BUNDLE",
685 macho.MH_DYLIB_STUB => "MH_DYLIB_STUB",
686 macho.MH_DSYM => "MH_DSYM",
687 macho.MH_KEXT_BUNDLE => "MH_KEXT_BUNDLE",
688 else => "Unknown",
689 };
690
691 try writer.print(
692 \\header
693 \\cputype {s}
694 \\filetype {s}
695 \\ncmds {d}
696 \\sizeofcmds {x}
697 \\flags
698 , .{
699 cputype,
700 filetype,
701 hdr.ncmds,
702 hdr.sizeofcmds,
703 });
704
705 if (hdr.flags > 0) {
706 if (hdr.flags & macho.MH_NOUNDEFS != 0) try writer.writeAll(" NOUNDEFS");
707 if (hdr.flags & macho.MH_INCRLINK != 0) try writer.writeAll(" INCRLINK");
708 if (hdr.flags & macho.MH_DYLDLINK != 0) try writer.writeAll(" DYLDLINK");
709 if (hdr.flags & macho.MH_BINDATLOAD != 0) try writer.writeAll(" BINDATLOAD");
710 if (hdr.flags & macho.MH_PREBOUND != 0) try writer.writeAll(" PREBOUND");
711 if (hdr.flags & macho.MH_SPLIT_SEGS != 0) try writer.writeAll(" SPLIT_SEGS");
712 if (hdr.flags & macho.MH_LAZY_INIT != 0) try writer.writeAll(" LAZY_INIT");
713 if (hdr.flags & macho.MH_TWOLEVEL != 0) try writer.writeAll(" TWOLEVEL");
714 if (hdr.flags & macho.MH_FORCE_FLAT != 0) try writer.writeAll(" FORCE_FLAT");
715 if (hdr.flags & macho.MH_NOMULTIDEFS != 0) try writer.writeAll(" NOMULTIDEFS");
716 if (hdr.flags & macho.MH_NOFIXPREBINDING != 0) try writer.writeAll(" NOFIXPREBINDING");
717 if (hdr.flags & macho.MH_PREBINDABLE != 0) try writer.writeAll(" PREBINDABLE");
718 if (hdr.flags & macho.MH_ALLMODSBOUND != 0) try writer.writeAll(" ALLMODSBOUND");
719 if (hdr.flags & macho.MH_SUBSECTIONS_VIA_SYMBOLS != 0) try writer.writeAll(" SUBSECTIONS_VIA_SYMBOLS");
720 if (hdr.flags & macho.MH_CANONICAL != 0) try writer.writeAll(" CANONICAL");
721 if (hdr.flags & macho.MH_WEAK_DEFINES != 0) try writer.writeAll(" WEAK_DEFINES");
722 if (hdr.flags & macho.MH_BINDS_TO_WEAK != 0) try writer.writeAll(" BINDS_TO_WEAK");
723 if (hdr.flags & macho.MH_ALLOW_STACK_EXECUTION != 0) try writer.writeAll(" ALLOW_STACK_EXECUTION");
724 if (hdr.flags & macho.MH_ROOT_SAFE != 0) try writer.writeAll(" ROOT_SAFE");
725 if (hdr.flags & macho.MH_SETUID_SAFE != 0) try writer.writeAll(" SETUID_SAFE");
726 if (hdr.flags & macho.MH_NO_REEXPORTED_DYLIBS != 0) try writer.writeAll(" NO_REEXPORTED_DYLIBS");
727 if (hdr.flags & macho.MH_PIE != 0) try writer.writeAll(" PIE");
728 if (hdr.flags & macho.MH_DEAD_STRIPPABLE_DYLIB != 0) try writer.writeAll(" DEAD_STRIPPABLE_DYLIB");
729 if (hdr.flags & macho.MH_HAS_TLV_DESCRIPTORS != 0) try writer.writeAll(" HAS_TLV_DESCRIPTORS");
730 if (hdr.flags & macho.MH_NO_HEAP_EXECUTION != 0) try writer.writeAll(" NO_HEAP_EXECUTION");
731 if (hdr.flags & macho.MH_APP_EXTENSION_SAFE != 0) try writer.writeAll(" APP_EXTENSION_SAFE");
732 if (hdr.flags & macho.MH_NLIST_OUTOFSYNC_WITH_DYLDINFO != 0) try writer.writeAll(" NLIST_OUTOFSYNC_WITH_DYLDINFO");
733 }
734
735 try writer.writeByte('\n');
736 }
737
612738 fn dumpLoadCommand(lc: macho.LoadCommandIterator.LoadCommand, index: usize, writer: anytype) !void {
613739 // print header first
614740 try writer.print(
......@@ -853,9 +979,18 @@ const MachODumper = struct {
853979 sect.segName(),
854980 sect.sectName(),
855981 });
982 if (sym.n_desc & macho.REFERENCED_DYNAMICALLY != 0) try writer.writeAll(" [referenced dynamically]");
983 if (sym.weakDef()) try writer.writeAll(" weak");
984 if (sym.weakRef()) try writer.writeAll(" weakref");
856985 if (sym.ext()) {
986 if (sym.pext()) try writer.writeAll(" private");
857987 try writer.writeAll(" external");
858 }
988 } else if (sym.pext()) try writer.writeAll(" (was private external)");
989 try writer.print(" {s}\n", .{sym_name});
990 } else if (sym.tentative()) {
991 const alignment = (sym.n_desc >> 8) & 0x0F;
992 try writer.print(" 0x{x:0>16} (common) (alignment 2^{d})", .{ sym.n_value, alignment });
993 if (sym.ext()) try writer.writeAll(" external");
859994 try writer.print(" {s}\n", .{sym_name});
860995 } else if (sym.undf()) {
861996 const ordinal = @divTrunc(@as(i16, @bitCast(sym.n_desc)), macho.N_SYMBOL_RESOLVER);
......@@ -876,17 +1011,414 @@ const MachODumper = struct {
8761011 break :blk basename[0..ext];
8771012 };
8781013 try writer.writeAll("(undefined)");
879 if (sym.weakRef()) {
880 try writer.writeAll(" weak");
881 }
882 if (sym.ext()) {
883 try writer.writeAll(" external");
884 }
1014 if (sym.weakRef()) try writer.writeAll(" weakref");
1015 if (sym.ext()) try writer.writeAll(" external");
8851016 try writer.print(" {s} (from {s})\n", .{
8861017 sym_name,
8871018 import_name,
8881019 });
889 } else unreachable;
1020 }
1021 }
1022 }
1023
1024 fn dumpRebaseInfo(
1025 gpa: Allocator,
1026 data: []const u8,
1027 segments: []const macho.segment_command_64,
1028 writer: anytype,
1029 ) !void {
1030 var rebases = std.ArrayList(u64).init(gpa);
1031 defer rebases.deinit();
1032 try parseRebaseInfo(data, segments, &rebases);
1033 mem.sort(u64, rebases.items, {}, std.sort.asc(u64));
1034 for (rebases.items) |addr| {
1035 try writer.print("0x{x}\n", .{addr});
1036 }
1037 }
1038
1039 fn parseRebaseInfo(
1040 data: []const u8,
1041 segments: []const macho.segment_command_64,
1042 rebases: *std.ArrayList(u64),
1043 ) !void {
1044 var stream = std.io.fixedBufferStream(data);
1045 var creader = std.io.countingReader(stream.reader());
1046 const reader = creader.reader();
1047
1048 var seg_id: ?u8 = null;
1049 var offset: u64 = 0;
1050 while (true) {
1051 const byte = reader.readByte() catch break;
1052 const opc = byte & macho.REBASE_OPCODE_MASK;
1053 const imm = byte & macho.REBASE_IMMEDIATE_MASK;
1054 switch (opc) {
1055 macho.REBASE_OPCODE_DONE => break,
1056 macho.REBASE_OPCODE_SET_TYPE_IMM => {},
1057 macho.REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB => {
1058 seg_id = imm;
1059 offset = try std.leb.readULEB128(u64, reader);
1060 },
1061 macho.REBASE_OPCODE_ADD_ADDR_IMM_SCALED => {
1062 offset += imm * @sizeOf(u64);
1063 },
1064 macho.REBASE_OPCODE_ADD_ADDR_ULEB => {
1065 const addend = try std.leb.readULEB128(u64, reader);
1066 offset += addend;
1067 },
1068 macho.REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB => {
1069 const addend = try std.leb.readULEB128(u64, reader);
1070 const seg = segments[seg_id.?];
1071 const addr = seg.vmaddr + offset;
1072 try rebases.append(addr);
1073 offset += addend + @sizeOf(u64);
1074 },
1075 macho.REBASE_OPCODE_DO_REBASE_IMM_TIMES,
1076 macho.REBASE_OPCODE_DO_REBASE_ULEB_TIMES,
1077 macho.REBASE_OPCODE_DO_REBASE_ULEB_TIMES_SKIPPING_ULEB,
1078 => {
1079 var ntimes: u64 = 1;
1080 var skip: u64 = 0;
1081 switch (opc) {
1082 macho.REBASE_OPCODE_DO_REBASE_IMM_TIMES => {
1083 ntimes = imm;
1084 },
1085 macho.REBASE_OPCODE_DO_REBASE_ULEB_TIMES => {
1086 ntimes = try std.leb.readULEB128(u64, reader);
1087 },
1088 macho.REBASE_OPCODE_DO_REBASE_ULEB_TIMES_SKIPPING_ULEB => {
1089 ntimes = try std.leb.readULEB128(u64, reader);
1090 skip = try std.leb.readULEB128(u64, reader);
1091 },
1092 else => unreachable,
1093 }
1094 const seg = segments[seg_id.?];
1095 const base_addr = seg.vmaddr;
1096 var count: usize = 0;
1097 while (count < ntimes) : (count += 1) {
1098 const addr = base_addr + offset;
1099 try rebases.append(addr);
1100 offset += skip + @sizeOf(u64);
1101 }
1102 },
1103 else => break,
1104 }
1105 }
1106 }
1107
1108 const Binding = struct {
1109 address: u64,
1110 addend: i64,
1111 ordinal: ?u16,
1112 name: []const u8,
1113
1114 fn deinit(binding: *Binding, gpa: Allocator) void {
1115 gpa.free(binding.name);
1116 }
1117
1118 fn lessThan(ctx: void, lhs: Binding, rhs: Binding) bool {
1119 _ = ctx;
1120 return lhs.address < rhs.address;
1121 }
1122 };
1123
1124 fn dumpBindInfo(
1125 gpa: Allocator,
1126 data: []const u8,
1127 segments: []const macho.segment_command_64,
1128 dylibs: []const []const u8,
1129 is_lazy: bool,
1130 writer: anytype,
1131 ) !void {
1132 var bindings = std.ArrayList(Binding).init(gpa);
1133 defer {
1134 for (bindings.items) |*b| {
1135 b.deinit(gpa);
1136 }
1137 bindings.deinit();
1138 }
1139 try parseBindInfo(gpa, data, segments, &bindings, is_lazy);
1140 mem.sort(Binding, bindings.items, {}, Binding.lessThan);
1141 for (bindings.items) |binding| {
1142 try writer.print("0x{x} [addend: {d}]", .{ binding.address, binding.addend });
1143 if (binding.ordinal) |ord| {
1144 try writer.print(" ({s})", .{std.fs.path.basename(dylibs[ord - 1])});
1145 }
1146 try writer.print(" {s}\n", .{binding.name});
1147 }
1148 }
1149
1150 fn parseBindInfo(
1151 gpa: Allocator,
1152 data: []const u8,
1153 segments: []const macho.segment_command_64,
1154 bindings: *std.ArrayList(Binding),
1155 lazy_ops: bool,
1156 ) !void {
1157 var stream = std.io.fixedBufferStream(data);
1158 var creader = std.io.countingReader(stream.reader());
1159 const reader = creader.reader();
1160
1161 var seg_id: ?u8 = null;
1162 var dylib_id: ?u16 = null;
1163 var offset: u64 = 0;
1164 var addend: i64 = 0;
1165
1166 var name_buf = std.ArrayList(u8).init(gpa);
1167 defer name_buf.deinit();
1168
1169 while (true) {
1170 const byte = reader.readByte() catch break;
1171 const opc = byte & macho.BIND_OPCODE_MASK;
1172 const imm = byte & macho.BIND_IMMEDIATE_MASK;
1173 switch (opc) {
1174 macho.BIND_OPCODE_DONE => {
1175 if (!lazy_ops) break;
1176 },
1177 macho.BIND_OPCODE_SET_TYPE_IMM => {
1178 if (lazy_ops) break;
1179 },
1180 macho.BIND_OPCODE_SET_DYLIB_ORDINAL_IMM => {
1181 dylib_id = imm;
1182 },
1183 macho.BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB => {
1184 seg_id = imm;
1185 offset = try std.leb.readULEB128(u64, reader);
1186 },
1187 macho.BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM => {
1188 name_buf.clearRetainingCapacity();
1189 try reader.readUntilDelimiterArrayList(&name_buf, 0, std.math.maxInt(u32));
1190 try name_buf.append(0);
1191 },
1192 macho.BIND_OPCODE_SET_ADDEND_SLEB => {
1193 addend = try std.leb.readILEB128(i64, reader);
1194 },
1195 macho.BIND_OPCODE_ADD_ADDR_ULEB => {
1196 if (lazy_ops) break;
1197 const x = try std.leb.readULEB128(u64, reader);
1198 offset = @intCast(@as(i64, @intCast(offset)) + @as(i64, @bitCast(x)));
1199 },
1200 macho.BIND_OPCODE_DO_BIND,
1201 macho.BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB,
1202 macho.BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED,
1203 macho.BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB,
1204 => {
1205 var add_addr: u64 = 0;
1206 var count: u64 = 1;
1207 var skip: u64 = 0;
1208
1209 switch (opc) {
1210 macho.BIND_OPCODE_DO_BIND => {},
1211 macho.BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB => {
1212 if (lazy_ops) break;
1213 add_addr = try std.leb.readULEB128(u64, reader);
1214 },
1215 macho.BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED => {
1216 if (lazy_ops) break;
1217 add_addr = imm * @sizeOf(u64);
1218 },
1219 macho.BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB => {
1220 if (lazy_ops) break;
1221 count = try std.leb.readULEB128(u64, reader);
1222 skip = try std.leb.readULEB128(u64, reader);
1223 },
1224 else => unreachable,
1225 }
1226
1227 const seg = segments[seg_id.?];
1228 var i: u64 = 0;
1229 while (i < count) : (i += 1) {
1230 const addr: u64 = @intCast(@as(i64, @intCast(seg.vmaddr + offset)));
1231 try bindings.append(.{
1232 .address = addr,
1233 .addend = addend,
1234 .ordinal = dylib_id,
1235 .name = try gpa.dupe(u8, name_buf.items),
1236 });
1237 offset += skip + @sizeOf(u64) + add_addr;
1238 }
1239 },
1240 else => break,
1241 }
1242 }
1243 }
1244
1245 fn dumpExportsTrie(
1246 gpa: Allocator,
1247 data: []const u8,
1248 seg: macho.segment_command_64,
1249 writer: anytype,
1250 ) !void {
1251 var arena = std.heap.ArenaAllocator.init(gpa);
1252 defer arena.deinit();
1253
1254 var exports = std.ArrayList(Export).init(arena.allocator());
1255 var it = TrieIterator{ .data = data };
1256 try parseTrieNode(arena.allocator(), &it, "", &exports);
1257
1258 mem.sort(Export, exports.items, {}, Export.lessThan);
1259
1260 for (exports.items) |exp| {
1261 switch (exp.tag) {
1262 .@"export" => {
1263 const info = exp.data.@"export";
1264 if (info.kind != .regular or info.weak) {
1265 try writer.writeByte('[');
1266 }
1267 switch (info.kind) {
1268 .regular => {},
1269 .absolute => try writer.writeAll("ABS, "),
1270 .tlv => try writer.writeAll("THREAD_LOCAL, "),
1271 }
1272 if (info.weak) try writer.writeAll("WEAK");
1273 if (info.kind != .regular or info.weak) {
1274 try writer.writeAll("] ");
1275 }
1276 try writer.print("{x} ", .{seg.vmaddr + info.vmoffset});
1277 },
1278 else => {},
1279 }
1280
1281 try writer.print("{s}\n", .{exp.name});
1282 }
1283 }
1284
1285 const TrieIterator = struct {
1286 data: []const u8,
1287 pos: usize = 0,
1288
1289 fn getStream(it: *TrieIterator) std.io.FixedBufferStream([]const u8) {
1290 return std.io.fixedBufferStream(it.data[it.pos..]);
1291 }
1292
1293 fn readULEB128(it: *TrieIterator) !u64 {
1294 var stream = it.getStream();
1295 var creader = std.io.countingReader(stream.reader());
1296 const reader = creader.reader();
1297 const value = try std.leb.readULEB128(u64, reader);
1298 it.pos += creader.bytes_read;
1299 return value;
1300 }
1301
1302 fn readString(it: *TrieIterator) ![:0]const u8 {
1303 var stream = it.getStream();
1304 const reader = stream.reader();
1305
1306 var count: usize = 0;
1307 while (true) : (count += 1) {
1308 const byte = try reader.readByte();
1309 if (byte == 0) break;
1310 }
1311
1312 const str = @as([*:0]const u8, @ptrCast(it.data.ptr + it.pos))[0..count :0];
1313 it.pos += count + 1;
1314 return str;
1315 }
1316
1317 fn readByte(it: *TrieIterator) !u8 {
1318 var stream = it.getStream();
1319 const value = try stream.reader().readByte();
1320 it.pos += 1;
1321 return value;
1322 }
1323 };
1324
1325 const Export = struct {
1326 name: []const u8,
1327 tag: enum { @"export", reexport, stub_resolver },
1328 data: union {
1329 @"export": struct {
1330 kind: enum { regular, absolute, tlv },
1331 weak: bool = false,
1332 vmoffset: u64,
1333 },
1334 reexport: u64,
1335 stub_resolver: struct {
1336 stub_offset: u64,
1337 resolver_offset: u64,
1338 },
1339 },
1340
1341 inline fn rankByTag(self: Export) u3 {
1342 return switch (self.tag) {
1343 .@"export" => 1,
1344 .reexport => 2,
1345 .stub_resolver => 3,
1346 };
1347 }
1348
1349 fn lessThan(ctx: void, lhs: Export, rhs: Export) bool {
1350 _ = ctx;
1351 if (lhs.rankByTag() == rhs.rankByTag()) {
1352 return switch (lhs.tag) {
1353 .@"export" => lhs.data.@"export".vmoffset < rhs.data.@"export".vmoffset,
1354 .reexport => lhs.data.reexport < rhs.data.reexport,
1355 .stub_resolver => lhs.data.stub_resolver.stub_offset < rhs.data.stub_resolver.stub_offset,
1356 };
1357 }
1358 return lhs.rankByTag() < rhs.rankByTag();
1359 }
1360 };
1361
1362 fn parseTrieNode(
1363 arena: Allocator,
1364 it: *TrieIterator,
1365 prefix: []const u8,
1366 exports: *std.ArrayList(Export),
1367 ) !void {
1368 const size = try it.readULEB128();
1369 if (size > 0) {
1370 const flags = try it.readULEB128();
1371 switch (flags) {
1372 macho.EXPORT_SYMBOL_FLAGS_REEXPORT => {
1373 const ord = try it.readULEB128();
1374 const name = try arena.dupe(u8, try it.readString());
1375 try exports.append(.{
1376 .name = if (name.len > 0) name else prefix,
1377 .tag = .reexport,
1378 .data = .{ .reexport = ord },
1379 });
1380 },
1381 macho.EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER => {
1382 const stub_offset = try it.readULEB128();
1383 const resolver_offset = try it.readULEB128();
1384 try exports.append(.{
1385 .name = prefix,
1386 .tag = .stub_resolver,
1387 .data = .{ .stub_resolver = .{
1388 .stub_offset = stub_offset,
1389 .resolver_offset = resolver_offset,
1390 } },
1391 });
1392 },
1393 else => {
1394 const vmoff = try it.readULEB128();
1395 try exports.append(.{
1396 .name = prefix,
1397 .tag = .@"export",
1398 .data = .{ .@"export" = .{
1399 .kind = switch (flags & macho.EXPORT_SYMBOL_FLAGS_KIND_MASK) {
1400 macho.EXPORT_SYMBOL_FLAGS_KIND_REGULAR => .regular,
1401 macho.EXPORT_SYMBOL_FLAGS_KIND_ABSOLUTE => .absolute,
1402 macho.EXPORT_SYMBOL_FLAGS_KIND_THREAD_LOCAL => .tlv,
1403 else => unreachable,
1404 },
1405 .weak = flags & macho.EXPORT_SYMBOL_FLAGS_WEAK_DEFINITION != 0,
1406 .vmoffset = vmoff,
1407 } },
1408 });
1409 },
1410 }
1411 }
1412
1413 const nedges = try it.readByte();
1414 for (0..nedges) |_| {
1415 const label = try it.readString();
1416 const off = try it.readULEB128();
1417 const prefix_label = try std.fmt.allocPrint(arena, "{s}{s}", .{ prefix, label });
1418 const curr = it.pos;
1419 it.pos = off;
1420 try parseTrieNode(arena, it, prefix_label, exports);
1421 it.pos = curr;
8901422 }
8911423 }
8921424};
test/link/macho/unwind_info/build.zig+1-1
......@@ -46,7 +46,7 @@ fn testUnwindInfo(
4646 }
4747
4848 check.checkInSymtab();
49 check.checkContains("(__TEXT,__text) external ___gxx_personality_v0");
49 check.checkContains("(__TEXT,__text) private external ___gxx_personality_v0");
5050 test_step.dependOn(&check.step);
5151
5252 const run = b.addRunArtifact(exe);
test/link/macho/weak_library/build.zig+2-2
......@@ -42,10 +42,10 @@ fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.Optimize
4242 check.checkExact("name @rpath/liba.dylib");
4343
4444 check.checkInSymtab();
45 check.checkExact("(undefined) weak external _a (from liba)");
45 check.checkExact("(undefined) weakref external _a (from liba)");
4646
4747 check.checkInSymtab();
48 check.checkExact("(undefined) weak external _asStr (from liba)");
48 check.checkExact("(undefined) weakref external _asStr (from liba)");
4949 test_step.dependOn(&check.step);
5050
5151 const run = b.addRunArtifact(exe);