Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

macho: report special symbols if undefined #157

Merged
merged 1 commit into from
Oct 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading