From 3f533b79e29b9572f1f5932488728f0c615f9b36 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Wed, 9 Oct 2024 14:43:47 +0200 Subject: [PATCH] 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. --- src/MachO.zig | 59 +++++++++++++++++++++++++++--------- src/MachO/Atom.zig | 4 +-- src/MachO/InternalObject.zig | 43 ++++++++++++++++++++++++++ 3 files changed, 89 insertions(+), 17 deletions(-) diff --git a/src/MachO.zig b/src/MachO.zig index ad235fb1..7372f64e 100644 --- a/src/MachO.zig +++ b/src/MachO.zig @@ -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)) = .{}, @@ -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| { @@ -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; @@ -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, diff --git a/src/MachO/Atom.zig b/src/MachO/Atom.zig index 4e8aaea8..480317c4 100644 --- a/src/MachO/Atom.zig +++ b/src/MachO/Atom.zig @@ -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; } diff --git a/src/MachO/InternalObject.zig b/src/MachO/InternalObject.zig index 1e1401f3..552480a4 100644 --- a/src/MachO/InternalObject.zig +++ b/src/MachO/InternalObject.zig @@ -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(); @@ -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,