mirror of
https://github.com/ziglang/zig.git
synced 2025-12-06 14:23:09 +00:00
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.
This commit is contained in:
parent
22661f3d67
commit
90aa68cbfc
@ -27,7 +27,7 @@ sections: std.MultiArrayList(Section) = .{},
|
|||||||
resolver: SymbolResolver = .{},
|
resolver: SymbolResolver = .{},
|
||||||
/// This table will be populated after `scanRelocs` has run.
|
/// This table will be populated after `scanRelocs` has run.
|
||||||
/// Key is symbol index.
|
/// Key is symbol index.
|
||||||
undefs: std.AutoArrayHashMapUnmanaged(SymbolResolver.Index, std.ArrayListUnmanaged(Ref)) = .empty,
|
undefs: std.AutoArrayHashMapUnmanaged(SymbolResolver.Index, UndefRefs) = .empty,
|
||||||
undefs_mutex: std.Thread.Mutex = .{},
|
undefs_mutex: std.Thread.Mutex = .{},
|
||||||
dupes: std.AutoArrayHashMapUnmanaged(SymbolResolver.Index, std.ArrayListUnmanaged(File.Index)) = .empty,
|
dupes: std.AutoArrayHashMapUnmanaged(SymbolResolver.Index, std.ArrayListUnmanaged(File.Index)) = .empty,
|
||||||
dupes_mutex: std.Thread.Mutex = .{},
|
dupes_mutex: std.Thread.Mutex = .{},
|
||||||
@ -1470,6 +1470,9 @@ fn scanRelocs(self: *MachO) !void {
|
|||||||
|
|
||||||
if (self.has_errors.swap(false, .seq_cst)) return error.FlushFailure;
|
if (self.has_errors.swap(false, .seq_cst)) return error.FlushFailure;
|
||||||
|
|
||||||
|
if (self.getInternalObject()) |obj| {
|
||||||
|
try obj.checkUndefs(self);
|
||||||
|
}
|
||||||
try self.reportUndefs();
|
try self.reportUndefs();
|
||||||
|
|
||||||
if (self.getZigObject()) |zo| {
|
if (self.getZigObject()) |zo| {
|
||||||
@ -1530,29 +1533,43 @@ fn reportUndefs(self: *MachO) !void {
|
|||||||
}
|
}
|
||||||
}.lessThan;
|
}.lessThan;
|
||||||
|
|
||||||
for (self.undefs.values()) |*refs| {
|
for (self.undefs.values()) |*undefs| switch (undefs.*) {
|
||||||
mem.sort(Ref, refs.items, {}, refLessThan);
|
.refs => |refs| mem.sort(Ref, refs.items, {}, refLessThan),
|
||||||
}
|
else => {},
|
||||||
|
};
|
||||||
|
|
||||||
for (keys.items) |key| {
|
for (keys.items) |key| {
|
||||||
const undef_sym = self.resolver.keys.items[key - 1];
|
const undef_sym = self.resolver.keys.items[key - 1];
|
||||||
const notes = self.undefs.get(key).?;
|
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);
|
||||||
|
};
|
||||||
|
|
||||||
var err = try self.base.addErrorWithNotes(nnotes);
|
var err = try self.base.addErrorWithNotes(nnotes);
|
||||||
try err.addMsg("undefined symbol: {s}", .{undef_sym.getName(self)});
|
try err.addMsg("undefined symbol: {s}", .{undef_sym.getName(self)});
|
||||||
|
|
||||||
var inote: usize = 0;
|
switch (notes) {
|
||||||
while (inote < @min(notes.items.len, max_notes)) : (inote += 1) {
|
.force_undefined => try err.addNote("referenced with linker flag -u", .{}),
|
||||||
const note = notes.items[inote];
|
.entry => try err.addNote("referenced with linker flag -e", .{}),
|
||||||
const file = self.getFile(note.file).?;
|
.dyld_stub_binder, .objc_msgsend => try err.addNote("referenced implicitly", .{}),
|
||||||
const atom = note.getAtom(self).?;
|
.refs => |refs| {
|
||||||
try err.addNote("referenced by {}:{s}", .{ file.fmtPath(), atom.getName(self) });
|
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) {
|
if (refs.items.len > max_notes) {
|
||||||
const remaining = notes.items.len - max_notes;
|
const remaining = refs.items.len - max_notes;
|
||||||
try err.addNote("referenced {d} more times", .{remaining});
|
try err.addNote("referenced {d} more times", .{remaining});
|
||||||
|
}
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -4584,78 +4601,20 @@ pub const String = struct {
|
|||||||
len: u32 = 0,
|
len: u32 = 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
const MachO = @This();
|
pub const UndefRefs = union(enum) {
|
||||||
|
force_undefined,
|
||||||
|
entry,
|
||||||
|
dyld_stub_binder,
|
||||||
|
objc_msgsend,
|
||||||
|
refs: std.ArrayListUnmanaged(Ref),
|
||||||
|
|
||||||
const std = @import("std");
|
pub fn deinit(self: *UndefRefs, allocator: Allocator) void {
|
||||||
const build_options = @import("build_options");
|
switch (self.*) {
|
||||||
const builtin = @import("builtin");
|
.refs => |*refs| refs.deinit(allocator),
|
||||||
const assert = std.debug.assert;
|
else => {},
|
||||||
const fs = std.fs;
|
}
|
||||||
const log = std.log.scoped(.link);
|
}
|
||||||
const state_log = std.log.scoped(.link_state);
|
};
|
||||||
const macho = std.macho;
|
|
||||||
const math = std.math;
|
|
||||||
const mem = std.mem;
|
|
||||||
const meta = std.meta;
|
|
||||||
|
|
||||||
const aarch64 = @import("../arch/aarch64/bits.zig");
|
|
||||||
const bind = @import("MachO/dyld_info/bind.zig");
|
|
||||||
const calcUuid = @import("MachO/uuid.zig").calcUuid;
|
|
||||||
const codegen = @import("../codegen.zig");
|
|
||||||
const dead_strip = @import("MachO/dead_strip.zig");
|
|
||||||
const eh_frame = @import("MachO/eh_frame.zig");
|
|
||||||
const fat = @import("MachO/fat.zig");
|
|
||||||
const link = @import("../link.zig");
|
|
||||||
const load_commands = @import("MachO/load_commands.zig");
|
|
||||||
const relocatable = @import("MachO/relocatable.zig");
|
|
||||||
const tapi = @import("tapi.zig");
|
|
||||||
const target_util = @import("../target.zig");
|
|
||||||
const trace = @import("../tracy.zig").trace;
|
|
||||||
const synthetic = @import("MachO/synthetic.zig");
|
|
||||||
|
|
||||||
const Air = @import("../Air.zig");
|
|
||||||
const Alignment = Atom.Alignment;
|
|
||||||
const Allocator = mem.Allocator;
|
|
||||||
const Archive = @import("MachO/Archive.zig");
|
|
||||||
pub const Atom = @import("MachO/Atom.zig");
|
|
||||||
const AtomicBool = std.atomic.Value(bool);
|
|
||||||
const Bind = bind.Bind;
|
|
||||||
const Cache = std.Build.Cache;
|
|
||||||
const Path = Cache.Path;
|
|
||||||
const CodeSignature = @import("MachO/CodeSignature.zig");
|
|
||||||
const Compilation = @import("../Compilation.zig");
|
|
||||||
const DataInCode = synthetic.DataInCode;
|
|
||||||
pub const DebugSymbols = @import("MachO/DebugSymbols.zig");
|
|
||||||
const Dylib = @import("MachO/Dylib.zig");
|
|
||||||
const ExportTrie = @import("MachO/dyld_info/Trie.zig");
|
|
||||||
const File = @import("MachO/file.zig").File;
|
|
||||||
const GotSection = synthetic.GotSection;
|
|
||||||
const Hash = std.hash.Wyhash;
|
|
||||||
const Indsymtab = synthetic.Indsymtab;
|
|
||||||
const InternalObject = @import("MachO/InternalObject.zig");
|
|
||||||
const ObjcStubsSection = synthetic.ObjcStubsSection;
|
|
||||||
const Object = @import("MachO/Object.zig");
|
|
||||||
const LazyBind = bind.LazyBind;
|
|
||||||
const LaSymbolPtrSection = synthetic.LaSymbolPtrSection;
|
|
||||||
const Liveness = @import("../Liveness.zig");
|
|
||||||
const LlvmObject = @import("../codegen/llvm.zig").Object;
|
|
||||||
const Md5 = std.crypto.hash.Md5;
|
|
||||||
const Zcu = @import("../Zcu.zig");
|
|
||||||
const InternPool = @import("../InternPool.zig");
|
|
||||||
const Rebase = @import("MachO/dyld_info/Rebase.zig");
|
|
||||||
pub const Relocation = @import("MachO/Relocation.zig");
|
|
||||||
const StringTable = @import("StringTable.zig");
|
|
||||||
const StubsSection = synthetic.StubsSection;
|
|
||||||
const StubsHelperSection = synthetic.StubsHelperSection;
|
|
||||||
const Symbol = @import("MachO/Symbol.zig");
|
|
||||||
const Thunk = @import("MachO/Thunk.zig");
|
|
||||||
const TlvPtrSection = synthetic.TlvPtrSection;
|
|
||||||
const Value = @import("../Value.zig");
|
|
||||||
const UnwindInfo = @import("MachO/UnwindInfo.zig");
|
|
||||||
const WaitGroup = std.Thread.WaitGroup;
|
|
||||||
const WeakBind = bind.WeakBind;
|
|
||||||
const ZigObject = @import("MachO/ZigObject.zig");
|
|
||||||
const dev = @import("../dev.zig");
|
|
||||||
|
|
||||||
pub const MachError = error{
|
pub const MachError = error{
|
||||||
/// Not enough permissions held to perform the requested kernel
|
/// Not enough permissions held to perform the requested kernel
|
||||||
@ -5392,3 +5351,76 @@ const max_distance = (1 << (jump_bits - 1));
|
|||||||
/// mold uses 5MiB margin, while ld64 uses 4MiB margin. We will follow mold
|
/// mold uses 5MiB margin, while ld64 uses 4MiB margin. We will follow mold
|
||||||
/// and assume margin to be 5MiB.
|
/// and assume margin to be 5MiB.
|
||||||
const max_allowed_distance = max_distance - 0x500_000;
|
const max_allowed_distance = max_distance - 0x500_000;
|
||||||
|
|
||||||
|
const MachO = @This();
|
||||||
|
|
||||||
|
const std = @import("std");
|
||||||
|
const build_options = @import("build_options");
|
||||||
|
const builtin = @import("builtin");
|
||||||
|
const assert = std.debug.assert;
|
||||||
|
const fs = std.fs;
|
||||||
|
const log = std.log.scoped(.link);
|
||||||
|
const state_log = std.log.scoped(.link_state);
|
||||||
|
const macho = std.macho;
|
||||||
|
const math = std.math;
|
||||||
|
const mem = std.mem;
|
||||||
|
const meta = std.meta;
|
||||||
|
|
||||||
|
const aarch64 = @import("../arch/aarch64/bits.zig");
|
||||||
|
const bind = @import("MachO/dyld_info/bind.zig");
|
||||||
|
const calcUuid = @import("MachO/uuid.zig").calcUuid;
|
||||||
|
const codegen = @import("../codegen.zig");
|
||||||
|
const dead_strip = @import("MachO/dead_strip.zig");
|
||||||
|
const eh_frame = @import("MachO/eh_frame.zig");
|
||||||
|
const fat = @import("MachO/fat.zig");
|
||||||
|
const link = @import("../link.zig");
|
||||||
|
const load_commands = @import("MachO/load_commands.zig");
|
||||||
|
const relocatable = @import("MachO/relocatable.zig");
|
||||||
|
const tapi = @import("tapi.zig");
|
||||||
|
const target_util = @import("../target.zig");
|
||||||
|
const trace = @import("../tracy.zig").trace;
|
||||||
|
const synthetic = @import("MachO/synthetic.zig");
|
||||||
|
|
||||||
|
const Air = @import("../Air.zig");
|
||||||
|
const Alignment = Atom.Alignment;
|
||||||
|
const Allocator = mem.Allocator;
|
||||||
|
const Archive = @import("MachO/Archive.zig");
|
||||||
|
pub const Atom = @import("MachO/Atom.zig");
|
||||||
|
const AtomicBool = std.atomic.Value(bool);
|
||||||
|
const Bind = bind.Bind;
|
||||||
|
const Cache = std.Build.Cache;
|
||||||
|
const Path = Cache.Path;
|
||||||
|
const CodeSignature = @import("MachO/CodeSignature.zig");
|
||||||
|
const Compilation = @import("../Compilation.zig");
|
||||||
|
const DataInCode = synthetic.DataInCode;
|
||||||
|
pub const DebugSymbols = @import("MachO/DebugSymbols.zig");
|
||||||
|
const Dylib = @import("MachO/Dylib.zig");
|
||||||
|
const ExportTrie = @import("MachO/dyld_info/Trie.zig");
|
||||||
|
const File = @import("MachO/file.zig").File;
|
||||||
|
const GotSection = synthetic.GotSection;
|
||||||
|
const Hash = std.hash.Wyhash;
|
||||||
|
const Indsymtab = synthetic.Indsymtab;
|
||||||
|
const InternalObject = @import("MachO/InternalObject.zig");
|
||||||
|
const ObjcStubsSection = synthetic.ObjcStubsSection;
|
||||||
|
const Object = @import("MachO/Object.zig");
|
||||||
|
const LazyBind = bind.LazyBind;
|
||||||
|
const LaSymbolPtrSection = synthetic.LaSymbolPtrSection;
|
||||||
|
const Liveness = @import("../Liveness.zig");
|
||||||
|
const LlvmObject = @import("../codegen/llvm.zig").Object;
|
||||||
|
const Md5 = std.crypto.hash.Md5;
|
||||||
|
const Zcu = @import("../Zcu.zig");
|
||||||
|
const InternPool = @import("../InternPool.zig");
|
||||||
|
const Rebase = @import("MachO/dyld_info/Rebase.zig");
|
||||||
|
pub const Relocation = @import("MachO/Relocation.zig");
|
||||||
|
const StringTable = @import("StringTable.zig");
|
||||||
|
const StubsSection = synthetic.StubsSection;
|
||||||
|
const StubsHelperSection = synthetic.StubsHelperSection;
|
||||||
|
const Symbol = @import("MachO/Symbol.zig");
|
||||||
|
const Thunk = @import("MachO/Thunk.zig");
|
||||||
|
const TlvPtrSection = synthetic.TlvPtrSection;
|
||||||
|
const Value = @import("../Value.zig");
|
||||||
|
const UnwindInfo = @import("MachO/UnwindInfo.zig");
|
||||||
|
const WaitGroup = std.Thread.WaitGroup;
|
||||||
|
const WeakBind = bind.WeakBind;
|
||||||
|
const ZigObject = @import("MachO/ZigObject.zig");
|
||||||
|
const dev = @import("../dev.zig");
|
||||||
|
|||||||
@ -560,9 +560,9 @@ fn reportUndefSymbol(self: Atom, rel: Relocation, macho_file: *MachO) !bool {
|
|||||||
const gpa = macho_file.base.comp.gpa;
|
const gpa = macho_file.base.comp.gpa;
|
||||||
const gop = try macho_file.undefs.getOrPut(gpa, file.getGlobals()[rel.target]);
|
const gop = try macho_file.undefs.getOrPut(gpa, file.getGlobals()[rel.target]);
|
||||||
if (!gop.found_existing) {
|
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;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -507,6 +507,41 @@ 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.comp.gpa;
|
||||||
|
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 {
|
pub fn allocateSyntheticSymbols(self: *InternalObject, macho_file: *MachO) void {
|
||||||
const text_seg = macho_file.getTextSegment();
|
const text_seg = macho_file.getTextSegment();
|
||||||
|
|
||||||
@ -791,6 +826,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 {
|
const FormatContext = struct {
|
||||||
self: *InternalObject,
|
self: *InternalObject,
|
||||||
macho_file: *MachO,
|
macho_file: *MachO,
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user