authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2024-01-11 19:42:14+01:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2024-01-24 12:34:39+01:00
logf0119ce37339ef72acc527e28b2b611712cf24f0
tree5220bdaf90599e07f94bf34f9446ada383c2b917
parent40e1bb11f87d97a89b5d5a6414c13af5f2d0d86b

macho: report undefined symbols to the user


2 files changed, 148 insertions(+), 32 deletions(-)

src/link/MachO.zig+142-27
......@@ -81,6 +81,10 @@ lazy_bind: LazyBindSection = .{},
8181export_trie: ExportTrieSection = .{},
8282unwind_info: UnwindInfo = .{},
8383
84has_tlv: bool = false,
85binds_to_weak: bool = false,
86weak_defines: bool = false,
87
8488/// Options
8589/// SDK layout
8690sdk_layout: ?SdkLayout,
......@@ -513,6 +517,14 @@ pub fn flushModule(self: *MachO, arena: Allocator, prog_node: *std.Progress.Node
513517 dylib.ordinal = @intCast(ord);
514518 }
515519
520 self.scanRelocs() catch |err| switch (err) {
521 error.HasUndefinedSymbols => return error.FlushFailure,
522 else => |e| {
523 try self.reportUnexpectedError("unexpected error while scanning relocations", .{});
524 return e;
525 },
526 };
527
516528 state_log.debug("{}", .{self.dumpState()});
517529
518530 @panic("TODO");
......@@ -1418,6 +1430,132 @@ fn deadStripDylibs(self: *MachO) void {
14181430 }
14191431}
14201432
1433fn scanRelocs(self: *MachO) !void {
1434 const tracy = trace(@src());
1435 defer tracy.end();
1436
1437 for (self.objects.items) |index| {
1438 try self.getFile(index).?.object.scanRelocs(self);
1439 }
1440
1441 try self.reportUndefs();
1442
1443 if (self.entry_index) |index| {
1444 const sym = self.getSymbol(index);
1445 if (sym.getFile(self) != null) {
1446 if (sym.flags.import) sym.flags.stubs = true;
1447 }
1448 }
1449
1450 if (self.dyld_stub_binder_index) |index| {
1451 const sym = self.getSymbol(index);
1452 if (sym.getFile(self) != null) sym.flags.got = true;
1453 }
1454
1455 if (self.objc_msg_send_index) |index| {
1456 const sym = self.getSymbol(index);
1457 if (sym.getFile(self) != null)
1458 sym.flags.got = true; // TODO is it always needed, or only if we are synthesising fast stubs?
1459 }
1460
1461 for (self.symbols.items, 0..) |*symbol, i| {
1462 const index = @as(Symbol.Index, @intCast(i));
1463 if (symbol.flags.got) {
1464 log.debug("'{s}' needs GOT", .{symbol.getName(self)});
1465 try self.got.addSymbol(index, self);
1466 }
1467 if (symbol.flags.stubs) {
1468 log.debug("'{s}' needs STUBS", .{symbol.getName(self)});
1469 try self.stubs.addSymbol(index, self);
1470 }
1471 if (symbol.flags.tlv_ptr) {
1472 log.debug("'{s}' needs TLV pointer", .{symbol.getName(self)});
1473 try self.tlv_ptr.addSymbol(index, self);
1474 }
1475 if (symbol.flags.objc_stubs) {
1476 log.debug("'{s}' needs OBJC STUBS", .{symbol.getName(self)});
1477 try self.objc_stubs.addSymbol(index, self);
1478 }
1479 }
1480}
1481
1482fn reportUndefs(self: *MachO) !void {
1483 const tracy = trace(@src());
1484 defer tracy.end();
1485
1486 switch (self.undefined_treatment) {
1487 .dynamic_lookup, .suppress => return,
1488 .@"error", .warn => {},
1489 }
1490
1491 const max_notes = 4;
1492
1493 var has_undefs = false;
1494 var it = self.undefs.iterator();
1495 while (it.next()) |entry| {
1496 const undef_sym = self.getSymbol(entry.key_ptr.*);
1497 const notes = entry.value_ptr.*;
1498 const nnotes = @min(notes.items.len, max_notes) + @intFromBool(notes.items.len > max_notes);
1499
1500 var err = try self.addErrorWithNotes(nnotes);
1501 try err.addMsg(self, "undefined symbol: {s}", .{undef_sym.getName(self)});
1502 has_undefs = true;
1503
1504 var inote: usize = 0;
1505 while (inote < @min(notes.items.len, max_notes)) : (inote += 1) {
1506 const atom = self.getAtom(notes.items[inote]).?;
1507 const file = atom.getFile(self);
1508 try err.addNote(self, "referenced by {}:{s}", .{ file.fmtPath(), atom.getName(self) });
1509 }
1510
1511 if (notes.items.len > max_notes) {
1512 const remaining = notes.items.len - max_notes;
1513 try err.addNote(self, "referenced {d} more times", .{remaining});
1514 }
1515 }
1516
1517 for (self.undefined_symbols.items) |index| {
1518 const sym = self.getSymbol(index);
1519 if (sym.getFile(self) != null) continue; // If undefined in an object file, will be reported above
1520 has_undefs = true;
1521 var err = try self.addErrorWithNotes(1);
1522 try err.addMsg(self, "undefined symbol: {s}", .{sym.getName(self)});
1523 try err.addNote(self, "-u command line option", .{});
1524 }
1525
1526 if (self.entry_index) |index| {
1527 const sym = self.getSymbol(index);
1528 if (sym.getFile(self) == null) {
1529 has_undefs = true;
1530 var err = try self.addErrorWithNotes(1);
1531 try err.addMsg(self, "undefined symbol: {s}", .{sym.getName(self)});
1532 try err.addNote(self, "implicit entry/start for main executable", .{});
1533 }
1534 }
1535
1536 if (self.dyld_stub_binder_index) |index| {
1537 const sym = self.getSymbol(index);
1538 if (sym.getFile(self) == null and self.stubs_sect_index != null) {
1539 has_undefs = true;
1540 var err = try self.addErrorWithNotes(1);
1541 try err.addMsg(self, "undefined symbol: {s}", .{sym.getName(self)});
1542 try err.addNote(self, "implicit -u command line option", .{});
1543 }
1544 }
1545
1546 if (self.objc_msg_send_index) |index| {
1547 const sym = self.getSymbol(index);
1548 if (sym.getFile(self) == null and self.objc_stubs_sect_index != null) {
1549 has_undefs = true;
1550 var err = try self.addErrorWithNotes(1);
1551 try err.addMsg(self, "undefined symbol: {s}", .{sym.getName(self)});
1552 try err.addNote(self, "implicit -u command line option", .{});
1553 }
1554 }
1555
1556 if (has_undefs) return error.HasUndefinedSymbols;
1557}
1558
14211559fn shrinkAtom(self: *MachO, atom_index: Atom.Index, new_block_size: u64) void {
14221560 _ = self;
14231561 _ = atom_index;
......@@ -1899,33 +2037,10 @@ fn reportDependencyError(
18992037 });
19002038}
19012039
1902pub fn reportUndefined(self: *MachO) error{OutOfMemory}!void {
1903 const comp = self.base.comp;
1904 const gpa = comp.gpa;
1905 const count = self.unresolved.count();
1906 try comp.link_errors.ensureUnusedCapacity(gpa, count);
1907
1908 for (self.unresolved.keys()) |global_index| {
1909 const global = self.globals.items[global_index];
1910 const sym_name = self.getSymbolName(global);
1911
1912 var notes = try std.ArrayList(link.File.ErrorMsg).initCapacity(gpa, 1);
1913 defer notes.deinit();
1914
1915 if (global.getFile()) |file| {
1916 const note = try std.fmt.allocPrint(gpa, "referenced in {s}", .{
1917 self.objects.items[file].name,
1918 });
1919 notes.appendAssumeCapacity(.{ .msg = note });
1920 }
1921
1922 var err_msg = link.File.ErrorMsg{
1923 .msg = try std.fmt.allocPrint(gpa, "undefined reference to symbol {s}", .{sym_name}),
1924 };
1925 err_msg.notes = try notes.toOwnedSlice();
1926
1927 comp.link_errors.appendAssumeCapacity(err_msg);
1928 }
2040fn reportUnexpectedError(self: *MachO, comptime format: []const u8, args: anytype) error{OutOfMemory}!void {
2041 var err = try self.addErrorWithNotes(1);
2042 try err.addMsg(self, format, args);
2043 try err.addNote(self, "please report this as a linker bug on https://github.com/ziglang/zig/issues/new/choose", .{});
19292044}
19302045
19312046// fn reportSymbolCollision(
src/link/MachO/Atom.zig+6-5
......@@ -200,7 +200,7 @@ pub fn scanRelocs(self: Atom, macho_file: *MachO) !void {
200200 const symbol = rel.getTargetSymbol(macho_file);
201201 if (symbol.flags.import or
202202 (symbol.flags.@"export" and (symbol.flags.weak or symbol.flags.interposable)) or
203 macho_file.options.cpu_arch.? == .aarch64) // TODO relax on arm64
203 macho_file.getTarget().cpu.arch == .aarch64) // TODO relax on arm64
204204 {
205205 symbol.flags.got = true;
206206 if (symbol.flags.weak) {
......@@ -219,9 +219,10 @@ pub fn scanRelocs(self: Atom, macho_file: *MachO) !void {
219219 => {
220220 const symbol = rel.getTargetSymbol(macho_file);
221221 if (!symbol.flags.tlv) {
222 macho_file.base.fatal(
223 "{}: {s}: illegal thread-local variable reference to regular symbol {s}",
224 .{ object.fmtPath(), self.getName(macho_file), symbol.getName(macho_file) },
222 try macho_file.reportParseError2(
223 object.index,
224 "{s}: illegal thread-local variable reference to regular symbol {s}",
225 .{ self.getName(macho_file), symbol.getName(macho_file) },
225226 );
226227 }
227228 if (symbol.flags.import or (symbol.flags.@"export" and (symbol.flags.weak or symbol.flags.interposable))) {
......@@ -271,7 +272,7 @@ fn reportUndefSymbol(self: Atom, rel: Relocation, macho_file: *MachO) !bool {
271272
272273 const sym = rel.getTargetSymbol(macho_file);
273274 if (sym.getFile(macho_file) == null) {
274 const gpa = macho_file.base.allocator;
275 const gpa = macho_file.base.comp.gpa;
275276 const gop = try macho_file.undefs.getOrPut(gpa, rel.target);
276277 if (!gop.found_existing) {
277278 gop.value_ptr.* = .{};