Skip to content

Commit

Permalink
macho: report special symbols if undefined
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
kubkon committed Oct 9, 2024
1 parent a6a0819 commit cf0c3b1
Show file tree
Hide file tree
Showing 3 changed files with 89 additions and 17 deletions.
59 changes: 44 additions & 15 deletions src/MachO.zig
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ resolver: SymbolResolver = .{},

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

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

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

if (self.getInternalObject()) |obj| {
try obj.checkUndefs(self);
}
try self.reportUndefs();

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

for (self.undefs.values()) |*refs| {
mem.sort(Ref, refs.items, {}, refLessThan);
}
for (self.undefs.values()) |*undefs| switch (undefs.*) {
.refs => |refs| mem.sort(Ref, refs.items, {}, refLessThan),
else => {},
};

for (keys.items) |key| {
const undef_sym = self.resolver.keys.items[key - 1];
const notes = self.undefs.get(key).?;
const nnotes = @min(notes.items.len, max_notes) + @intFromBool(notes.items.len > max_notes);
const nnotes = nnotes: {
const nnotes = switch (notes) {
.refs => |refs| refs.items.len,
else => 1,
};
break :nnotes @min(nnotes, max_notes) + @intFromBool(nnotes > max_notes);
};

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

var inote: usize = 0;
while (inote < @min(notes.items.len, max_notes)) : (inote += 1) {
const note = notes.items[inote];
const file = self.getFile(note.file).?;
const atom = note.getAtom(self).?;
try err.addNote("referenced by {}:{s}", .{ file.fmtPath(), atom.getName(self) });
}
switch (notes) {
.force_undefined => try err.addNote("referenced with linker flag -u", .{}),
.entry => try err.addNote("referenced with linker flag -e", .{}),
.dyld_stub_binder, .objc_msgsend => try err.addNote("referenced implicitly", .{}),
.refs => |refs| {
var inote: usize = 0;
while (inote < @min(refs.items.len, max_notes)) : (inote += 1) {
const ref = refs.items[inote];
const file = self.getFile(ref.file).?;
const atom = ref.getAtom(self).?;
try err.addNote("referenced by {}:{s}", .{ file.fmtPath(), atom.getName(self) });
}

if (notes.items.len > max_notes) {
const remaining = notes.items.len - max_notes;
try err.addNote("referenced {d} more times", .{remaining});
if (refs.items.len > max_notes) {
const remaining = refs.items.len - max_notes;
try err.addNote("referenced {d} more times", .{remaining});
}
},
}
}
return error.UndefinedSymbols;
Expand Down Expand Up @@ -3283,6 +3300,18 @@ pub const SymbolResolver = struct {
pub const Index = u32;
};

pub const UndefRefs = union(enum) {
force_undefined,
entry,
dyld_stub_binder,
objc_msgsend,
refs: std.ArrayListUnmanaged(Ref),

pub fn deinit(self: *UndefRefs, allocator: Allocator) void {
self.refs.deinit(allocator);
}
};

pub const String = struct {
pos: u32 = 0,
len: u32 = 0,
Expand Down
4 changes: 2 additions & 2 deletions src/MachO/Atom.zig
Original file line number Diff line number Diff line change
Expand Up @@ -308,9 +308,9 @@ fn reportUndefSymbol(self: Atom, rel: Relocation, macho_file: *MachO) !bool {
const gpa = macho_file.base.allocator;
const gop = try macho_file.undefs.getOrPut(gpa, file.getGlobals()[rel.target]);
if (!gop.found_existing) {
gop.value_ptr.* = .{};
gop.value_ptr.* = .{ .refs = .{} };
}
try gop.value_ptr.append(gpa, .{ .index = self.atom_index, .file = self.file });
try gop.value_ptr.refs.append(gpa, .{ .index = self.atom_index, .file = self.file });
return true;
}

Expand Down
43 changes: 43 additions & 0 deletions src/MachO/InternalObject.zig
Original file line number Diff line number Diff line change
Expand Up @@ -507,6 +507,42 @@ pub fn scanRelocs(self: *InternalObject, macho_file: *MachO) void {
}
}

pub fn checkUndefs(self: InternalObject, macho_file: *MachO) !void {
const addUndef = struct {
fn addUndef(mf: *MachO, index: MachO.SymbolResolver.Index, tag: anytype) !void {
const gpa = mf.base.allocator;
mf.undefs_mutex.lock();
defer mf.undefs_mutex.unlock();
const gop = try mf.undefs.getOrPut(gpa, index);
if (!gop.found_existing) {
gop.value_ptr.* = tag;
}
}
}.addUndef;

for (self.force_undefined.items) |index| {
const ref = self.getSymbolRef(index, macho_file);
if (ref.getFile(macho_file) == null) {
try addUndef(macho_file, self.globals.items[index], .force_undefined);
}
}
if (self.getEntryRef(macho_file)) |ref| {
if (ref.getFile(macho_file) == null) {
try addUndef(macho_file, self.globals.items[self.entry_index.?], .entry);
}
}
if (self.getDyldStubBinderRef(macho_file)) |ref| {
if (ref.getFile(macho_file) == null and macho_file.stubs.symbols.items.len > 0) {
try addUndef(macho_file, self.globals.items[self.dyld_stub_binder_index.?], .dyld_stub_binder);
}
}
if (self.getObjcMsgSendRef(macho_file)) |ref| {
if (ref.getFile(macho_file) == null and self.needsObjcMsgsendSymbol()) {
try addUndef(macho_file, self.globals.items[self.objc_msg_send_index.?], .objc_msgsend);
}
}
}

pub fn allocateSyntheticSymbols(self: *InternalObject, macho_file: *MachO) void {
const text_seg = macho_file.getTextSegment();

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

fn needsObjcMsgsendSymbol(self: InternalObject) bool {
for (self.sections.items(.extra)) |extra| {
if (extra.is_objc_methname or extra.is_objc_selref) return true;
}
return false;
}

const FormatContext = struct {
self: *InternalObject,
macho_file: *MachO,
Expand Down

0 comments on commit cf0c3b1

Please sign in to comment.