authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-11-30 12:52:00-08:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2021-11-30 12:52:00-08:00
log51df63a44e6d6f78676e0d43b6723d21e1cb9235
tree603cb1f5d80a01b11fea21365a9a8b61c5210364
parent2873e19366400b2f11d3a99b10ea17e4f06c6b4b
parent28623544ce78c5c559af8a47c62e688db9ce5e4a
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #10251 from ziglang/fix-10225

macos: fix stack traces for Zig linked with any linker including Apple's ld64

6 files changed, 212 insertions(+), 134 deletions(-)

lib/std/debug.zig+142-62
...@@ -559,7 +559,7 @@ pub const TTY = struct {...@@ -559,7 +559,7 @@ pub const TTY = struct {
559559
560fn machoSearchSymbols(symbols: []const MachoSymbol, address: usize) ?*const MachoSymbol {560fn machoSearchSymbols(symbols: []const MachoSymbol, address: usize) ?*const MachoSymbol {
561 var min: usize = 0;561 var min: usize = 0;
562 var max: usize = symbols.len - 1; // Exclude sentinel.562 var max: usize = symbols.len;
563 while (min < max) {563 while (min < max) {
564 const mid = min + (max - min) / 2;564 const mid = min + (max - min) / 2;
565 const curr = &symbols[mid];565 const curr = &symbols[mid];
...@@ -850,51 +850,91 @@ fn readMachODebugInfo(allocator: *mem.Allocator, macho_file: File) !ModuleDebugI...@@ -850,51 +850,91 @@ fn readMachODebugInfo(allocator: *mem.Allocator, macho_file: File) !ModuleDebugI
850 } else {850 } else {
851 return error.MissingDebugInfo;851 return error.MissingDebugInfo;
852 };852 };
853 const syms = @ptrCast([*]const macho.nlist_64, @alignCast(@alignOf(macho.nlist_64), hdr_base + symtab.symoff))[0..symtab.nsyms];853 const syms = @ptrCast(
854 [*]const macho.nlist_64,
855 @alignCast(@alignOf(macho.nlist_64), hdr_base + symtab.symoff),
856 )[0..symtab.nsyms];
854 const strings = @ptrCast([*]const u8, hdr_base + symtab.stroff)[0 .. symtab.strsize - 1 :0];857 const strings = @ptrCast([*]const u8, hdr_base + symtab.stroff)[0 .. symtab.strsize - 1 :0];
855858
856 const symbols_buf = try allocator.alloc(MachoSymbol, syms.len);859 const symbols_buf = try allocator.alloc(MachoSymbol, syms.len);
857860
858 var ofile: ?*const macho.nlist_64 = null;861 var ofile: u32 = undefined;
859 var reloc: u64 = 0;862 var last_sym: MachoSymbol = undefined;
860 var symbol_index: usize = 0;863 var symbol_index: usize = 0;
861 var last_len: u64 = 0;864 var state: enum {
865 init,
866 oso_open,
867 oso_close,
868 bnsym,
869 fun_strx,
870 fun_size,
871 ensym,
872 } = .init;
873
862 for (syms) |*sym| {874 for (syms) |*sym| {
863 if (sym.n_type & std.macho.N_STAB != 0) {875 if (!sym.stab()) continue;
864 switch (sym.n_type) {876
865 std.macho.N_OSO => {877 // TODO handle globals N_GSYM, and statics N_STSYM
866 ofile = sym;878 switch (sym.n_type) {
867 reloc = 0;879 macho.N_OSO => {
868 },880 switch (state) {
869 std.macho.N_FUN => {881 .init, .oso_close => {
870 if (sym.n_sect == 0) {882 state = .oso_open;
871 last_len = sym.n_value;883 ofile = sym.n_strx;
872 } else {884 },
873 symbols_buf[symbol_index] = MachoSymbol{885 else => return error.InvalidDebugInfo,
874 .nlist = sym,886 }
887 },
888 macho.N_BNSYM => {
889 switch (state) {
890 .oso_open, .ensym => {
891 state = .bnsym;
892 last_sym = .{
893 .strx = 0,
894 .addr = sym.n_value,
895 .size = 0,
875 .ofile = ofile,896 .ofile = ofile,
876 .reloc = reloc,
877 };897 };
898 },
899 else => return error.InvalidDebugInfo,
900 }
901 },
902 macho.N_FUN => {
903 switch (state) {
904 .bnsym => {
905 state = .fun_strx;
906 last_sym.strx = sym.n_strx;
907 },
908 .fun_strx => {
909 state = .fun_size;
910 last_sym.size = @intCast(u32, sym.n_value);
911 },
912 else => return error.InvalidDebugInfo,
913 }
914 },
915 macho.N_ENSYM => {
916 switch (state) {
917 .fun_size => {
918 state = .ensym;
919 symbols_buf[symbol_index] = last_sym;
878 symbol_index += 1;920 symbol_index += 1;
879 }921 },
880 },922 else => return error.InvalidDebugInfo,
881 std.macho.N_BNSYM => {923 }
882 if (reloc == 0) {924 },
883 reloc = sym.n_value;925 macho.N_SO => {
884 }926 switch (state) {
885 },927 .init, .oso_close => {},
886 else => continue,928 .oso_open, .ensym => {
887 }929 state = .oso_close;
930 },
931 else => return error.InvalidDebugInfo,
932 }
933 },
934 else => {},
888 }935 }
889 }936 }
890 const sentinel = try allocator.create(macho.nlist_64);937 assert(state == .oso_close);
891 sentinel.* = macho.nlist_64{
892 .n_strx = 0,
893 .n_type = 36,
894 .n_sect = 0,
895 .n_desc = 0,
896 .n_value = symbols_buf[symbol_index - 1].nlist.n_value + last_len,
897 };
898938
899 const symbols = allocator.shrink(symbols_buf, symbol_index);939 const symbols = allocator.shrink(symbols_buf, symbol_index);
900940
...@@ -946,18 +986,19 @@ fn printLineFromFileAnyOs(out_stream: anytype, line_info: LineInfo) !void {...@@ -946,18 +986,19 @@ fn printLineFromFileAnyOs(out_stream: anytype, line_info: LineInfo) !void {
946}986}
947987
948const MachoSymbol = struct {988const MachoSymbol = struct {
949 nlist: *const macho.nlist_64,989 strx: u32,
950 ofile: ?*const macho.nlist_64,990 addr: u64,
951 reloc: u64,991 size: u32,
992 ofile: u32,
952993
953 /// Returns the address from the macho file994 /// Returns the address from the macho file
954 fn address(self: MachoSymbol) u64 {995 fn address(self: MachoSymbol) u64 {
955 return self.nlist.n_value;996 return self.addr;
956 }997 }
957998
958 fn addressLessThan(context: void, lhs: MachoSymbol, rhs: MachoSymbol) bool {999 fn addressLessThan(context: void, lhs: MachoSymbol, rhs: MachoSymbol) bool {
959 _ = context;1000 _ = context;
960 return lhs.address() < rhs.address();1001 return lhs.addr < rhs.addr;
961 }1002 }
962};1003};
9631004
...@@ -1231,13 +1272,17 @@ pub const ModuleDebugInfo = switch (native_os) {...@@ -1231,13 +1272,17 @@ pub const ModuleDebugInfo = switch (native_os) {
1231 strings: [:0]const u8,1272 strings: [:0]const u8,
1232 ofiles: OFileTable,1273 ofiles: OFileTable,
12331274
1234 const OFileTable = std.StringHashMap(DW.DwarfInfo);1275 const OFileTable = std.StringHashMap(OFileInfo);
1276 const OFileInfo = struct {
1277 di: DW.DwarfInfo,
1278 addr_table: std.StringHashMap(u64),
1279 };
12351280
1236 pub fn allocator(self: @This()) *mem.Allocator {1281 pub fn allocator(self: @This()) *mem.Allocator {
1237 return self.ofiles.allocator;1282 return self.ofiles.allocator;
1238 }1283 }
12391284
1240 fn loadOFile(self: *@This(), o_file_path: []const u8) !DW.DwarfInfo {1285 fn loadOFile(self: *@This(), o_file_path: []const u8) !OFileInfo {
1241 const o_file = try fs.cwd().openFile(o_file_path, .{ .intended_io_mode = .blocking });1286 const o_file = try fs.cwd().openFile(o_file_path, .{ .intended_io_mode = .blocking });
1242 const mapped_mem = try mapWholeFile(o_file);1287 const mapped_mem = try mapWholeFile(o_file);
12431288
...@@ -1250,22 +1295,54 @@ pub const ModuleDebugInfo = switch (native_os) {...@@ -1250,22 +1295,54 @@ pub const ModuleDebugInfo = switch (native_os) {
12501295
1251 const hdr_base = @ptrCast([*]const u8, hdr);1296 const hdr_base = @ptrCast([*]const u8, hdr);
1252 var ptr = hdr_base + @sizeOf(macho.mach_header_64);1297 var ptr = hdr_base + @sizeOf(macho.mach_header_64);
1298 var segptr = ptr;
1253 var ncmd: u32 = hdr.ncmds;1299 var ncmd: u32 = hdr.ncmds;
1254 const segcmd = while (ncmd != 0) : (ncmd -= 1) {1300 var segcmd: ?*const macho.segment_command_64 = null;
1301 var symtabcmd: ?*const macho.symtab_command = null;
1302
1303 while (ncmd != 0) : (ncmd -= 1) {
1255 const lc = @ptrCast(*const std.macho.load_command, ptr);1304 const lc = @ptrCast(*const std.macho.load_command, ptr);
1256 switch (lc.cmd) {1305 switch (lc.cmd) {
1257 std.macho.LC_SEGMENT_64 => {1306 std.macho.LC_SEGMENT_64 => {
1258 break @ptrCast(1307 segcmd = @ptrCast(
1259 *const std.macho.segment_command_64,1308 *const std.macho.segment_command_64,
1260 @alignCast(@alignOf(std.macho.segment_command_64), ptr),1309 @alignCast(@alignOf(std.macho.segment_command_64), ptr),
1261 );1310 );
1311 segptr = ptr;
1312 },
1313 std.macho.LC_SYMTAB => {
1314 symtabcmd = @ptrCast(
1315 *const std.macho.symtab_command,
1316 @alignCast(@alignOf(std.macho.symtab_command), ptr),
1317 );
1262 },1318 },
1263 else => {},1319 else => {},
1264 }1320 }
1265 ptr = @alignCast(@alignOf(std.macho.load_command), ptr + lc.cmdsize);1321 ptr = @alignCast(@alignOf(std.macho.load_command), ptr + lc.cmdsize);
1266 } else {1322 }
1267 return error.MissingDebugInfo;1323
1268 };1324 if (segcmd == null or symtabcmd == null) return error.MissingDebugInfo;
1325
1326 // Parse symbols
1327 const strtab = @ptrCast(
1328 [*]const u8,
1329 hdr_base + symtabcmd.?.stroff,
1330 )[0 .. symtabcmd.?.strsize - 1 :0];
1331 const symtab = @ptrCast(
1332 [*]const macho.nlist_64,
1333 @alignCast(@alignOf(macho.nlist_64), hdr_base + symtabcmd.?.symoff),
1334 )[0..symtabcmd.?.nsyms];
1335
1336 // TODO handle tentative (common) symbols
1337 var addr_table = std.StringHashMap(u64).init(self.allocator());
1338 try addr_table.ensureTotalCapacity(@intCast(u32, symtab.len));
1339 for (symtab) |sym| {
1340 if (sym.n_strx == 0) continue;
1341 if (sym.undf() or sym.tentative() or sym.abs()) continue;
1342 const sym_name = mem.sliceTo(strtab[sym.n_strx..], 0);
1343 // TODO is it possible to have a symbol collision?
1344 addr_table.putAssumeCapacityNoClobber(sym_name, sym.n_value);
1345 }
12691346
1270 var opt_debug_line: ?*const macho.section_64 = null;1347 var opt_debug_line: ?*const macho.section_64 = null;
1271 var opt_debug_info: ?*const macho.section_64 = null;1348 var opt_debug_info: ?*const macho.section_64 = null;
...@@ -1275,8 +1352,8 @@ pub const ModuleDebugInfo = switch (native_os) {...@@ -1275,8 +1352,8 @@ pub const ModuleDebugInfo = switch (native_os) {
12751352
1276 const sections = @ptrCast(1353 const sections = @ptrCast(
1277 [*]const macho.section_64,1354 [*]const macho.section_64,
1278 @alignCast(@alignOf(macho.section_64), ptr + @sizeOf(std.macho.segment_command_64)),1355 @alignCast(@alignOf(macho.section_64), segptr + @sizeOf(std.macho.segment_command_64)),
1279 )[0..segcmd.nsects];1356 )[0..segcmd.?.nsects];
1280 for (sections) |*sect| {1357 for (sections) |*sect| {
1281 // The section name may not exceed 16 chars and a trailing null may1358 // The section name may not exceed 16 chars and a trailing null may
1282 // not be present1359 // not be present
...@@ -1320,34 +1397,34 @@ pub const ModuleDebugInfo = switch (native_os) {...@@ -1320,34 +1397,34 @@ pub const ModuleDebugInfo = switch (native_os) {
1320 };1397 };
13211398
1322 try DW.openDwarfDebugInfo(&di, self.allocator());1399 try DW.openDwarfDebugInfo(&di, self.allocator());
1400 var info = OFileInfo{
1401 .di = di,
1402 .addr_table = addr_table,
1403 };
13231404
1324 // Add the debug info to the cache1405 // Add the debug info to the cache
1325 try self.ofiles.putNoClobber(o_file_path, di);1406 try self.ofiles.putNoClobber(o_file_path, info);
13261407
1327 return di;1408 return info;
1328 }1409 }
13291410
1330 pub fn getSymbolAtAddress(self: *@This(), address: usize) !SymbolInfo {1411 pub fn getSymbolAtAddress(self: *@This(), address: usize) !SymbolInfo {
1331 nosuspend {1412 nosuspend {
1332 // Translate the VA into an address into this object1413 // Translate the VA into an address into this object
1333 const relocated_address = address - self.base_address;1414 const relocated_address = address - self.base_address;
1334 assert(relocated_address >= 0x100000000);
13351415
1336 // Find the .o file where this symbol is defined1416 // Find the .o file where this symbol is defined
1337 const symbol = machoSearchSymbols(self.symbols, relocated_address) orelse1417 const symbol = machoSearchSymbols(self.symbols, relocated_address) orelse
1338 return SymbolInfo{};1418 return SymbolInfo{};
1419 const addr_off = relocated_address - symbol.addr;
13391420
1340 // Take the symbol name from the N_FUN STAB entry, we're going to1421 // Take the symbol name from the N_FUN STAB entry, we're going to
1341 // use it if we fail to find the DWARF infos1422 // use it if we fail to find the DWARF infos
1342 const stab_symbol = mem.sliceTo(self.strings[symbol.nlist.n_strx..], 0);1423 const stab_symbol = mem.sliceTo(self.strings[symbol.strx..], 0);
13431424 const o_file_path = mem.sliceTo(self.strings[symbol.ofile..], 0);
1344 if (symbol.ofile == null)
1345 return SymbolInfo{ .symbol_name = stab_symbol };
1346
1347 const o_file_path = mem.sliceTo(self.strings[symbol.ofile.?.n_strx..], 0);
13481425
1349 // Check if its debug infos are already in the cache1426 // Check if its debug infos are already in the cache
1350 var o_file_di = self.ofiles.get(o_file_path) orelse1427 var o_file_info = self.ofiles.get(o_file_path) orelse
1351 (self.loadOFile(o_file_path) catch |err| switch (err) {1428 (self.loadOFile(o_file_path) catch |err| switch (err) {
1352 error.FileNotFound,1429 error.FileNotFound,
1353 error.MissingDebugInfo,1430 error.MissingDebugInfo,
...@@ -1357,19 +1434,22 @@ pub const ModuleDebugInfo = switch (native_os) {...@@ -1357,19 +1434,22 @@ pub const ModuleDebugInfo = switch (native_os) {
1357 },1434 },
1358 else => return err,1435 else => return err,
1359 });1436 });
1437 const o_file_di = &o_file_info.di;
13601438
1361 // Translate again the address, this time into an address inside the1439 // Translate again the address, this time into an address inside the
1362 // .o file1440 // .o file
1363 const relocated_address_o = relocated_address - symbol.reloc;1441 const relocated_address_o = o_file_info.addr_table.get(stab_symbol) orelse return SymbolInfo{
1442 .symbol_name = "???",
1443 };
13641444
1365 if (o_file_di.findCompileUnit(relocated_address_o)) |compile_unit| {1445 if (o_file_di.findCompileUnit(relocated_address_o)) |compile_unit| {
1366 return SymbolInfo{1446 return SymbolInfo{
1367 .symbol_name = o_file_di.getSymbolName(relocated_address_o) orelse "???",1447 .symbol_name = o_file_di.getSymbolName(relocated_address_o) orelse "???",
1368 .compile_unit_name = compile_unit.die.getAttrString(&o_file_di, DW.AT.name) catch |err| switch (err) {1448 .compile_unit_name = compile_unit.die.getAttrString(o_file_di, DW.AT.name) catch |err| switch (err) {
1369 error.MissingDebugInfo, error.InvalidDebugInfo => "???",1449 error.MissingDebugInfo, error.InvalidDebugInfo => "???",
1370 else => return err,1450 else => return err,
1371 },1451 },
1372 .line_info = o_file_di.getLineNumberInfo(compile_unit.*, relocated_address_o) catch |err| switch (err) {1452 .line_info = o_file_di.getLineNumberInfo(compile_unit.*, relocated_address_o + addr_off) catch |err| switch (err) {
1373 error.MissingDebugInfo, error.InvalidDebugInfo => null,1453 error.MissingDebugInfo, error.InvalidDebugInfo => null,
1374 else => return err,1454 else => return err,
1375 },1455 },
lib/std/macho.zig+49
...@@ -744,6 +744,55 @@ pub const nlist_64 = extern struct {...@@ -744,6 +744,55 @@ pub const nlist_64 = extern struct {
744 n_sect: u8,744 n_sect: u8,
745 n_desc: u16,745 n_desc: u16,
746 n_value: u64,746 n_value: u64,
747
748 pub fn stab(sym: nlist_64) bool {
749 return (N_STAB & sym.n_type) != 0;
750 }
751
752 pub fn pext(sym: nlist_64) bool {
753 return (N_PEXT & sym.n_type) != 0;
754 }
755
756 pub fn ext(sym: nlist_64) bool {
757 return (N_EXT & sym.n_type) != 0;
758 }
759
760 pub fn sect(sym: nlist_64) bool {
761 const type_ = N_TYPE & sym.n_type;
762 return type_ == N_SECT;
763 }
764
765 pub fn undf(sym: nlist_64) bool {
766 const type_ = N_TYPE & sym.n_type;
767 return type_ == N_UNDF;
768 }
769
770 pub fn indr(sym: nlist_64) bool {
771 const type_ = N_TYPE & sym.n_type;
772 return type_ == N_INDR;
773 }
774
775 pub fn abs(sym: nlist_64) bool {
776 const type_ = N_TYPE & sym.n_type;
777 return type_ == N_ABS;
778 }
779
780 pub fn weakDef(sym: nlist_64) bool {
781 return (sym.n_desc & N_WEAK_DEF) != 0;
782 }
783
784 pub fn weakRef(sym: nlist_64) bool {
785 return (sym.n_desc & N_WEAK_REF) != 0;
786 }
787
788 pub fn discarded(sym: nlist_64) bool {
789 return (sym.n_desc & N_DESC_DISCARDED) != 0;
790 }
791
792 pub fn tentative(sym: nlist_64) bool {
793 if (!sym.undf()) return false;
794 return sym.n_value != 0;
795 }
747};796};
748797
749/// Format of a relocation entry of a Mach-O file. Modified from the 4.3BSD798/// Format of a relocation entry of a Mach-O file. Modified from the 4.3BSD
src/link/MachO.zig+15-66
...@@ -885,7 +885,7 @@ pub fn flushModule(self: *MachO, comp: *Compilation) !void {...@@ -885,7 +885,7 @@ pub fn flushModule(self: *MachO, comp: *Compilation) !void {
885 const sym_name = self.getString(sym.n_strx);885 const sym_name = self.getString(sym.n_strx);
886 const resolv = self.symbol_resolver.get(sym.n_strx) orelse unreachable;886 const resolv = self.symbol_resolver.get(sym.n_strx) orelse unreachable;
887887
888 if (symbolIsDiscarded(sym.*)) {888 if (sym.discarded()) {
889 sym.* = .{889 sym.* = .{
890 .n_strx = 0,890 .n_strx = 0,
891 .n_type = macho.N_UNDF,891 .n_type = macho.N_UNDF,
...@@ -2445,21 +2445,21 @@ fn resolveSymbolsInObject(self: *MachO, object_id: u16) !void {...@@ -2445,21 +2445,21 @@ fn resolveSymbolsInObject(self: *MachO, object_id: u16) !void {
2445 const sym_id = @intCast(u32, id);2445 const sym_id = @intCast(u32, id);
2446 const sym_name = object.getString(sym.n_strx);2446 const sym_name = object.getString(sym.n_strx);
24472447
2448 if (symbolIsStab(sym)) {2448 if (sym.stab()) {
2449 log.err("unhandled symbol type: stab", .{});2449 log.err("unhandled symbol type: stab", .{});
2450 log.err(" symbol '{s}'", .{sym_name});2450 log.err(" symbol '{s}'", .{sym_name});
2451 log.err(" first definition in '{s}'", .{object.name});2451 log.err(" first definition in '{s}'", .{object.name});
2452 return error.UnhandledSymbolType;2452 return error.UnhandledSymbolType;
2453 }2453 }
24542454
2455 if (symbolIsIndr(sym)) {2455 if (sym.indr()) {
2456 log.err("unhandled symbol type: indirect", .{});2456 log.err("unhandled symbol type: indirect", .{});
2457 log.err(" symbol '{s}'", .{sym_name});2457 log.err(" symbol '{s}'", .{sym_name});
2458 log.err(" first definition in '{s}'", .{object.name});2458 log.err(" first definition in '{s}'", .{object.name});
2459 return error.UnhandledSymbolType;2459 return error.UnhandledSymbolType;
2460 }2460 }
24612461
2462 if (symbolIsAbs(sym)) {2462 if (sym.abs()) {
2463 log.err("unhandled symbol type: absolute", .{});2463 log.err("unhandled symbol type: absolute", .{});
2464 log.err(" symbol '{s}'", .{sym_name});2464 log.err(" symbol '{s}'", .{sym_name});
2465 log.err(" first definition in '{s}'", .{object.name});2465 log.err(" first definition in '{s}'", .{object.name});
...@@ -2467,7 +2467,7 @@ fn resolveSymbolsInObject(self: *MachO, object_id: u16) !void {...@@ -2467,7 +2467,7 @@ fn resolveSymbolsInObject(self: *MachO, object_id: u16) !void {
2467 }2467 }
24682468
2469 const n_strx = try self.makeString(sym_name);2469 const n_strx = try self.makeString(sym_name);
2470 if (symbolIsSect(sym)) {2470 if (sym.sect()) {
2471 // Defined symbol regardless of scope lands in the locals symbol table.2471 // Defined symbol regardless of scope lands in the locals symbol table.
2472 const local_sym_index = @intCast(u32, self.locals.items.len);2472 const local_sym_index = @intCast(u32, self.locals.items.len);
2473 try self.locals.append(self.base.allocator, .{2473 try self.locals.append(self.base.allocator, .{
...@@ -2482,7 +2482,7 @@ fn resolveSymbolsInObject(self: *MachO, object_id: u16) !void {...@@ -2482,7 +2482,7 @@ fn resolveSymbolsInObject(self: *MachO, object_id: u16) !void {
24822482
2483 // If the symbol's scope is not local aka translation unit, then we need work out2483 // If the symbol's scope is not local aka translation unit, then we need work out
2484 // if we should save the symbol as a global, or potentially flag the error.2484 // if we should save the symbol as a global, or potentially flag the error.
2485 if (!symbolIsExt(sym)) continue;2485 if (!sym.ext()) continue;
24862486
2487 const local = self.locals.items[local_sym_index];2487 const local = self.locals.items[local_sym_index];
2488 const resolv = self.symbol_resolver.getPtr(n_strx) orelse {2488 const resolv = self.symbol_resolver.getPtr(n_strx) orelse {
...@@ -2507,18 +2507,16 @@ fn resolveSymbolsInObject(self: *MachO, object_id: u16) !void {...@@ -2507,18 +2507,16 @@ fn resolveSymbolsInObject(self: *MachO, object_id: u16) !void {
2507 .global => {2507 .global => {
2508 const global = &self.globals.items[resolv.where_index];2508 const global = &self.globals.items[resolv.where_index];
25092509
2510 if (symbolIsTentative(global.*)) {2510 if (global.tentative()) {
2511 assert(self.tentatives.swapRemove(resolv.where_index));2511 assert(self.tentatives.swapRemove(resolv.where_index));
2512 } else if (!(symbolIsWeakDef(sym) or symbolIsPext(sym)) and2512 } else if (!(sym.weakDef() or sym.pext()) and !(global.weakDef() or global.pext())) {
2513 !(symbolIsWeakDef(global.*) or symbolIsPext(global.*)))
2514 {
2515 log.err("symbol '{s}' defined multiple times", .{sym_name});2513 log.err("symbol '{s}' defined multiple times", .{sym_name});
2516 if (resolv.file) |file| {2514 if (resolv.file) |file| {
2517 log.err(" first definition in '{s}'", .{self.objects.items[file].name});2515 log.err(" first definition in '{s}'", .{self.objects.items[file].name});
2518 }2516 }
2519 log.err(" next definition in '{s}'", .{object.name});2517 log.err(" next definition in '{s}'", .{object.name});
2520 return error.MultipleSymbolDefinitions;2518 return error.MultipleSymbolDefinitions;
2521 } else if (symbolIsWeakDef(sym) or symbolIsPext(sym)) continue; // Current symbol is weak, so skip it.2519 } else if (sym.weakDef() or sym.pext()) continue; // Current symbol is weak, so skip it.
25222520
2523 // Otherwise, update the resolver and the global symbol.2521 // Otherwise, update the resolver and the global symbol.
2524 global.n_type = sym.n_type;2522 global.n_type = sym.n_type;
...@@ -2554,7 +2552,7 @@ fn resolveSymbolsInObject(self: *MachO, object_id: u16) !void {...@@ -2554,7 +2552,7 @@ fn resolveSymbolsInObject(self: *MachO, object_id: u16) !void {
2554 .local_sym_index = local_sym_index,2552 .local_sym_index = local_sym_index,
2555 .file = object_id,2553 .file = object_id,
2556 };2554 };
2557 } else if (symbolIsTentative(sym)) {2555 } else if (sym.tentative()) {
2558 // Symbol is a tentative definition.2556 // Symbol is a tentative definition.
2559 const resolv = self.symbol_resolver.getPtr(n_strx) orelse {2557 const resolv = self.symbol_resolver.getPtr(n_strx) orelse {
2560 const global_sym_index = @intCast(u32, self.globals.items.len);2558 const global_sym_index = @intCast(u32, self.globals.items.len);
...@@ -2577,7 +2575,7 @@ fn resolveSymbolsInObject(self: *MachO, object_id: u16) !void {...@@ -2577,7 +2575,7 @@ fn resolveSymbolsInObject(self: *MachO, object_id: u16) !void {
2577 switch (resolv.where) {2575 switch (resolv.where) {
2578 .global => {2576 .global => {
2579 const global = &self.globals.items[resolv.where_index];2577 const global = &self.globals.items[resolv.where_index];
2580 if (!symbolIsTentative(global.*)) continue;2578 if (!global.tentative()) continue;
2581 if (global.n_value >= sym.n_value) continue;2579 if (global.n_value >= sym.n_value) continue;
25822580
2583 global.n_desc = sym.n_desc;2581 global.n_desc = sym.n_desc;
...@@ -3575,9 +3573,9 @@ pub fn updateDeclExports(...@@ -3575,9 +3573,9 @@ pub fn updateDeclExports(
35753573
3576 const sym = &self.globals.items[resolv.where_index];3574 const sym = &self.globals.items[resolv.where_index];
35773575
3578 if (symbolIsTentative(sym.*)) {3576 if (sym.tentative()) {
3579 assert(self.tentatives.swapRemove(resolv.where_index));3577 assert(self.tentatives.swapRemove(resolv.where_index));
3580 } else if (!is_weak and !(symbolIsWeakDef(sym.*) or symbolIsPext(sym.*))) {3578 } else if (!is_weak and !(sym.weakDef() or sym.pext())) {
3581 _ = try module.failed_exports.put(3579 _ = try module.failed_exports.put(
3582 module.gpa,3580 module.gpa,
3583 exp,3581 exp,
...@@ -5316,58 +5314,9 @@ pub fn getString(self: *MachO, off: u32) []const u8 {...@@ -5316,58 +5314,9 @@ pub fn getString(self: *MachO, off: u32) []const u8 {
5316 return mem.sliceTo(@ptrCast([*:0]const u8, self.strtab.items.ptr + off), 0);5314 return mem.sliceTo(@ptrCast([*:0]const u8, self.strtab.items.ptr + off), 0);
5317}5315}
53185316
5319pub fn symbolIsStab(sym: macho.nlist_64) bool {
5320 return (macho.N_STAB & sym.n_type) != 0;
5321}
5322
5323pub fn symbolIsPext(sym: macho.nlist_64) bool {
5324 return (macho.N_PEXT & sym.n_type) != 0;
5325}
5326
5327pub fn symbolIsExt(sym: macho.nlist_64) bool {
5328 return (macho.N_EXT & sym.n_type) != 0;
5329}
5330
5331pub fn symbolIsSect(sym: macho.nlist_64) bool {
5332 const type_ = macho.N_TYPE & sym.n_type;
5333 return type_ == macho.N_SECT;
5334}
5335
5336pub fn symbolIsUndf(sym: macho.nlist_64) bool {
5337 const type_ = macho.N_TYPE & sym.n_type;
5338 return type_ == macho.N_UNDF;
5339}
5340
5341pub fn symbolIsIndr(sym: macho.nlist_64) bool {
5342 const type_ = macho.N_TYPE & sym.n_type;
5343 return type_ == macho.N_INDR;
5344}
5345
5346pub fn symbolIsAbs(sym: macho.nlist_64) bool {
5347 const type_ = macho.N_TYPE & sym.n_type;
5348 return type_ == macho.N_ABS;
5349}
5350
5351pub fn symbolIsWeakDef(sym: macho.nlist_64) bool {
5352 return (sym.n_desc & macho.N_WEAK_DEF) != 0;
5353}
5354
5355pub fn symbolIsWeakRef(sym: macho.nlist_64) bool {
5356 return (sym.n_desc & macho.N_WEAK_REF) != 0;
5357}
5358
5359pub fn symbolIsDiscarded(sym: macho.nlist_64) bool {
5360 return (sym.n_desc & macho.N_DESC_DISCARDED) != 0;
5361}
5362
5363pub fn symbolIsTentative(sym: macho.nlist_64) bool {
5364 if (!symbolIsUndf(sym)) return false;
5365 return sym.n_value != 0;
5366}
5367
5368pub fn symbolIsTemp(sym: macho.nlist_64, sym_name: []const u8) bool {5317pub fn symbolIsTemp(sym: macho.nlist_64, sym_name: []const u8) bool {
5369 if (!symbolIsSect(sym)) return false;5318 if (!sym.sect()) return false;
5370 if (symbolIsExt(sym)) return false;5319 if (sym.ext()) return false;
5371 return mem.startsWith(u8, sym_name, "l") or mem.startsWith(u8, sym_name, "L");5320 return mem.startsWith(u8, sym_name, "l") or mem.startsWith(u8, sym_name, "L");
5372}5321}
53735322
src/link/MachO/Atom.zig+2-2
...@@ -295,7 +295,7 @@ pub fn parseRelocs(self: *Atom, relocs: []macho.relocation_info, context: RelocC...@@ -295,7 +295,7 @@ pub fn parseRelocs(self: *Atom, relocs: []macho.relocation_info, context: RelocC
295295
296 assert(subtractor == null);296 assert(subtractor == null);
297 const sym = context.object.symtab.items[rel.r_symbolnum];297 const sym = context.object.symtab.items[rel.r_symbolnum];
298 if (MachO.symbolIsSect(sym) and !MachO.symbolIsExt(sym)) {298 if (sym.sect() and !sym.ext()) {
299 subtractor = context.object.symbol_mapping.get(rel.r_symbolnum).?;299 subtractor = context.object.symbol_mapping.get(rel.r_symbolnum).?;
300 } else {300 } else {
301 const sym_name = context.object.getString(sym.n_strx);301 const sym_name = context.object.getString(sym.n_strx);
...@@ -362,7 +362,7 @@ pub fn parseRelocs(self: *Atom, relocs: []macho.relocation_info, context: RelocC...@@ -362,7 +362,7 @@ pub fn parseRelocs(self: *Atom, relocs: []macho.relocation_info, context: RelocC
362 const sym = context.object.symtab.items[rel.r_symbolnum];362 const sym = context.object.symtab.items[rel.r_symbolnum];
363 const sym_name = context.object.getString(sym.n_strx);363 const sym_name = context.object.getString(sym.n_strx);
364364
365 if (MachO.symbolIsSect(sym) and !MachO.symbolIsExt(sym)) {365 if (sym.sect() and !sym.ext()) {
366 const sym_index = context.object.symbol_mapping.get(rel.r_symbolnum) orelse unreachable;366 const sym_index = context.object.symbol_mapping.get(rel.r_symbolnum) orelse unreachable;
367 break :target Relocation.Target{ .local = sym_index };367 break :target Relocation.Target{ .local = sym_index };
368 }368 }
src/link/MachO/Dylib.zig+1-1
...@@ -226,7 +226,7 @@ fn parseSymbols(self: *Dylib, allocator: *Allocator) !void {...@@ -226,7 +226,7 @@ fn parseSymbols(self: *Dylib, allocator: *Allocator) !void {
226 _ = try self.file.preadAll(strtab, symtab_cmd.stroff + self.library_offset);226 _ = try self.file.preadAll(strtab, symtab_cmd.stroff + self.library_offset);
227227
228 for (slice) |sym| {228 for (slice) |sym| {
229 const add_to_symtab = MachO.symbolIsExt(sym) and (MachO.symbolIsSect(sym) or MachO.symbolIsIndr(sym));229 const add_to_symtab = sym.ext() and (sym.sect() or sym.indr());
230230
231 if (!add_to_symtab) continue;231 if (!add_to_symtab) continue;
232232
src/link/MachO/Object.zig+3-3
...@@ -338,8 +338,8 @@ const NlistWithIndex = struct {...@@ -338,8 +338,8 @@ const NlistWithIndex = struct {
338 // afterwards by address in each group. Normally, dysymtab should338 // afterwards by address in each group. Normally, dysymtab should
339 // be enough to guarantee the sort, but turns out not every compiler339 // be enough to guarantee the sort, but turns out not every compiler
340 // is kind enough to specify the symbols in the correct order.340 // is kind enough to specify the symbols in the correct order.
341 if (MachO.symbolIsSect(lhs.nlist)) {341 if (lhs.nlist.sect()) {
342 if (MachO.symbolIsSect(rhs.nlist)) {342 if (rhs.nlist.sect()) {
343 // Same group, sort by address.343 // Same group, sort by address.
344 return lhs.nlist.n_value < rhs.nlist.n_value;344 return lhs.nlist.n_value < rhs.nlist.n_value;
345 } else {345 } else {
...@@ -414,7 +414,7 @@ pub fn parseIntoAtoms(self: *Object, allocator: *Allocator, macho_file: *MachO)...@@ -414,7 +414,7 @@ pub fn parseIntoAtoms(self: *Object, allocator: *Allocator, macho_file: *MachO)
414 var iundefsym: usize = sorted_all_nlists.items.len;414 var iundefsym: usize = sorted_all_nlists.items.len;
415 while (iundefsym > 0) : (iundefsym -= 1) {415 while (iundefsym > 0) : (iundefsym -= 1) {
416 const nlist = sorted_all_nlists.items[iundefsym];416 const nlist = sorted_all_nlists.items[iundefsym];
417 if (MachO.symbolIsSect(nlist.nlist)) break;417 if (nlist.nlist.sect()) break;
418 }418 }
419 break :blk iundefsym;419 break :blk iundefsym;
420 };420 };