Skip to content

Commit 3f533b7

Browse files
committed
macho: report special symbols if undefined
Special symbols include explictly force undefined symbols passed via -u flag, missing entry point symbol, missing 'dyld_stub_binder' symbol, or missing '_objc_msgsend' symbol.
1 parent 63b47ae commit 3f533b7

File tree

3 files changed

+89
-17
lines changed

3 files changed

+89
-17
lines changed

src/MachO.zig

+44-15
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ resolver: SymbolResolver = .{},
2424

2525
/// This table will be populated after `scanRelocs` has run.
2626
/// Key is symbol index.
27-
undefs: std.AutoArrayHashMapUnmanaged(SymbolResolver.Index, std.ArrayListUnmanaged(Ref)) = .{},
27+
undefs: std.AutoArrayHashMapUnmanaged(SymbolResolver.Index, UndefRefs) = .{},
2828
undefs_mutex: std.Thread.Mutex = .{},
2929

3030
dupes: std.AutoArrayHashMapUnmanaged(SymbolResolver.Index, std.ArrayListUnmanaged(File.Index)) = .{},
@@ -1227,6 +1227,9 @@ fn scanRelocs(self: *MachO) !void {
12271227

12281228
if (self.has_errors.swap(false, .seq_cst)) return error.FlushFailed;
12291229

1230+
if (self.getInternalObject()) |obj| {
1231+
try obj.checkUndefs(self);
1232+
}
12301233
try self.reportUndefs();
12311234

12321235
for (self.objects.items) |index| {
@@ -1292,29 +1295,43 @@ fn reportUndefs(self: *MachO) !void {
12921295
}
12931296
}.lessThan;
12941297

1295-
for (self.undefs.values()) |*refs| {
1296-
mem.sort(Ref, refs.items, {}, refLessThan);
1297-
}
1298+
for (self.undefs.values()) |*undefs| switch (undefs.*) {
1299+
.refs => |refs| mem.sort(Ref, refs.items, {}, refLessThan),
1300+
else => {},
1301+
};
12981302

12991303
for (keys.items) |key| {
13001304
const undef_sym = self.resolver.keys.items[key - 1];
13011305
const notes = self.undefs.get(key).?;
1302-
const nnotes = @min(notes.items.len, max_notes) + @intFromBool(notes.items.len > max_notes);
1306+
const nnotes = nnotes: {
1307+
const nnotes = switch (notes) {
1308+
.refs => |refs| refs.items.len,
1309+
else => 1,
1310+
};
1311+
break :nnotes @min(nnotes, max_notes) + @intFromBool(nnotes > max_notes);
1312+
};
13031313

13041314
const err = try addFn(&self.base, nnotes);
13051315
try err.addMsg("undefined symbol: {s}", .{undef_sym.getName(self)});
13061316

1307-
var inote: usize = 0;
1308-
while (inote < @min(notes.items.len, max_notes)) : (inote += 1) {
1309-
const note = notes.items[inote];
1310-
const file = self.getFile(note.file).?;
1311-
const atom = note.getAtom(self).?;
1312-
try err.addNote("referenced by {}:{s}", .{ file.fmtPath(), atom.getName(self) });
1313-
}
1317+
switch (notes) {
1318+
.force_undefined => try err.addNote("referenced with linker flag -u", .{}),
1319+
.entry => try err.addNote("referenced with linker flag -e", .{}),
1320+
.dyld_stub_binder, .objc_msgsend => try err.addNote("referenced implicitly", .{}),
1321+
.refs => |refs| {
1322+
var inote: usize = 0;
1323+
while (inote < @min(refs.items.len, max_notes)) : (inote += 1) {
1324+
const ref = refs.items[inote];
1325+
const file = self.getFile(ref.file).?;
1326+
const atom = ref.getAtom(self).?;
1327+
try err.addNote("referenced by {}:{s}", .{ file.fmtPath(), atom.getName(self) });
1328+
}
13141329

1315-
if (notes.items.len > max_notes) {
1316-
const remaining = notes.items.len - max_notes;
1317-
try err.addNote("referenced {d} more times", .{remaining});
1330+
if (refs.items.len > max_notes) {
1331+
const remaining = refs.items.len - max_notes;
1332+
try err.addNote("referenced {d} more times", .{remaining});
1333+
}
1334+
},
13181335
}
13191336
}
13201337
return error.UndefinedSymbols;
@@ -3283,6 +3300,18 @@ pub const SymbolResolver = struct {
32833300
pub const Index = u32;
32843301
};
32853302

3303+
pub const UndefRefs = union(enum) {
3304+
force_undefined,
3305+
entry,
3306+
dyld_stub_binder,
3307+
objc_msgsend,
3308+
refs: std.ArrayListUnmanaged(Ref),
3309+
3310+
pub fn deinit(self: *UndefRefs, allocator: Allocator) void {
3311+
self.refs.deinit(allocator);
3312+
}
3313+
};
3314+
32863315
pub const String = struct {
32873316
pos: u32 = 0,
32883317
len: u32 = 0,

src/MachO/Atom.zig

+2-2
Original file line numberDiff line numberDiff line change
@@ -308,9 +308,9 @@ fn reportUndefSymbol(self: Atom, rel: Relocation, macho_file: *MachO) !bool {
308308
const gpa = macho_file.base.allocator;
309309
const gop = try macho_file.undefs.getOrPut(gpa, file.getGlobals()[rel.target]);
310310
if (!gop.found_existing) {
311-
gop.value_ptr.* = .{};
311+
gop.value_ptr.* = .{ .refs = .{} };
312312
}
313-
try gop.value_ptr.append(gpa, .{ .index = self.atom_index, .file = self.file });
313+
try gop.value_ptr.refs.append(gpa, .{ .index = self.atom_index, .file = self.file });
314314
return true;
315315
}
316316

src/MachO/InternalObject.zig

+43
Original file line numberDiff line numberDiff line change
@@ -507,6 +507,42 @@ pub fn scanRelocs(self: *InternalObject, macho_file: *MachO) void {
507507
}
508508
}
509509

510+
pub fn checkUndefs(self: InternalObject, macho_file: *MachO) !void {
511+
const addUndef = struct {
512+
fn addUndef(mf: *MachO, index: MachO.SymbolResolver.Index, tag: anytype) !void {
513+
const gpa = mf.base.allocator;
514+
mf.undefs_mutex.lock();
515+
defer mf.undefs_mutex.unlock();
516+
const gop = try mf.undefs.getOrPut(gpa, index);
517+
if (!gop.found_existing) {
518+
gop.value_ptr.* = tag;
519+
}
520+
}
521+
}.addUndef;
522+
523+
for (self.force_undefined.items) |index| {
524+
const ref = self.getSymbolRef(index, macho_file);
525+
if (ref.getFile(macho_file) == null) {
526+
try addUndef(macho_file, self.globals.items[index], .force_undefined);
527+
}
528+
}
529+
if (self.getEntryRef(macho_file)) |ref| {
530+
if (ref.getFile(macho_file) == null) {
531+
try addUndef(macho_file, self.globals.items[self.entry_index.?], .entry);
532+
}
533+
}
534+
if (self.getDyldStubBinderRef(macho_file)) |ref| {
535+
if (ref.getFile(macho_file) == null and macho_file.stubs.symbols.items.len > 0) {
536+
try addUndef(macho_file, self.globals.items[self.dyld_stub_binder_index.?], .dyld_stub_binder);
537+
}
538+
}
539+
if (self.getObjcMsgSendRef(macho_file)) |ref| {
540+
if (ref.getFile(macho_file) == null and self.needsObjcMsgsendSymbol()) {
541+
try addUndef(macho_file, self.globals.items[self.objc_msg_send_index.?], .objc_msgsend);
542+
}
543+
}
544+
}
545+
510546
pub fn allocateSyntheticSymbols(self: *InternalObject, macho_file: *MachO) void {
511547
const text_seg = macho_file.getTextSegment();
512548

@@ -787,6 +823,13 @@ pub fn setSymbolExtra(self: *InternalObject, index: u32, extra: Symbol.Extra) vo
787823
}
788824
}
789825

826+
fn needsObjcMsgsendSymbol(self: InternalObject) bool {
827+
for (self.sections.items(.extra)) |extra| {
828+
if (extra.is_objc_methname or extra.is_objc_selref) return true;
829+
}
830+
return false;
831+
}
832+
790833
const FormatContext = struct {
791834
self: *InternalObject,
792835
macho_file: *MachO,

0 commit comments

Comments
 (0)