diff --git a/src/link/MachO/Zld.zig b/src/link/MachO/Zld.zig index d49e379a38..579fc0f548 100644 --- a/src/link/MachO/Zld.zig +++ b/src/link/MachO/Zld.zig @@ -60,7 +60,7 @@ tlv_section_index: ?u16 = null, la_symbol_ptr_section_index: ?u16 = null, data_section_index: ?u16 = null, -locals: std.StringArrayHashMapUnmanaged(macho.nlist_64) = .{}, +locals: std.StringArrayHashMapUnmanaged(std.ArrayListUnmanaged(Symbol)) = .{}, exports: std.StringArrayHashMapUnmanaged(macho.nlist_64) = .{}, nonlazy_imports: std.StringArrayHashMapUnmanaged(Import) = .{}, lazy_imports: std.StringArrayHashMapUnmanaged(Import) = .{}, @@ -74,6 +74,18 @@ stub_helper_stubs_start_off: ?u64 = null, segments_directory: std.AutoHashMapUnmanaged([16]u8, u16) = .{}, directory: std.AutoHashMapUnmanaged(DirectoryKey, DirectoryEntry) = .{}, +const Symbol = struct { + inner: macho.nlist_64, + tt: Type, + object: *Object, + + const Type = enum { + Local, + WeakGlobal, + Global, + }; +}; + const DirectoryKey = struct { segname: [16]u8, sectname: [16]u8, @@ -198,6 +210,7 @@ pub fn deinit(self: *Zld) void { self.exports.deinit(self.allocator); for (self.locals.items()) |*entry| { self.allocator.free(entry.key); + entry.value.deinit(self.allocator); } self.locals.deinit(self.allocator); for (self.objects.items) |*object| { @@ -773,7 +786,7 @@ fn resolveSymbols(self: *Zld) !void { var next_address = std.AutoHashMap(DirectoryKey, Address).init(self.allocator); defer next_address.deinit(); - for (self.objects.items) |object| { + for (self.objects.items) |*object| { const seg = object.load_commands.items[object.segment_cmd_index.?].Segment; for (seg.sections.items) |sect| { @@ -799,11 +812,32 @@ fn resolveSymbols(self: *Zld) !void { if (isImport(&sym)) continue; const sym_name = object.getString(sym.n_strx); + const out_name = try self.allocator.dupe(u8, sym_name); + const locs = try self.locals.getOrPut(self.allocator, out_name); + defer { + if (locs.found_existing) self.allocator.free(out_name); + } - if (isLocal(&sym) and self.locals.get(sym_name) != null) { - log.warn("local symbol '{s}' defined multiple times; removing", .{sym_name}); - self.locals.swapRemoveAssertDiscard(sym_name); - continue; + if (!locs.found_existing) { + locs.entry.value = .{}; + } + + const tt: Symbol.Type = blk: { + if (isLocal(&sym)) { + break :blk .Local; + } else if (isWeakDef(&sym)) { + break :blk .WeakGlobal; + } else { + break :blk .Global; + } + }; + if (tt == .Global) { + for (locs.entry.value.items) |ss| { + if (ss.tt == .Global) { + log.err("symbol '{s}' defined multiple times", .{sym_name}); + return error.MultipleSymbolDefinitions; + } + } } const sect = seg.sections.items[sym.n_sect - 1]; @@ -813,10 +847,9 @@ fn resolveSymbols(self: *Zld) !void { }; const res = self.directory.get(key) orelse continue; - const n_strx = try self.makeString(sym_name); const n_value = sym.n_value - sect.addr + next_address.get(key).?.addr; - log.warn("resolving '{s}' as local symbol at 0x{x}", .{ sym_name, n_value }); + log.warn("resolving '{s}':{} as {s} symbol at 0x{x}", .{ sym_name, sym, tt, n_value }); var n_sect = res.sect_index + 1; for (self.load_commands.items) |sseg, i| { @@ -826,13 +859,17 @@ fn resolveSymbols(self: *Zld) !void { n_sect += @intCast(u16, sseg.Segment.sections.items.len); } - var out_name = try self.allocator.dupe(u8, sym_name); - try self.locals.putNoClobber(self.allocator, out_name, .{ - .n_strx = n_strx, - .n_value = n_value, - .n_type = macho.N_SECT, - .n_desc = sym.n_desc, - .n_sect = @intCast(u8, n_sect), + const n_strx = try self.makeString(sym_name); + try locs.entry.value.append(self.allocator, .{ + .inner = .{ + .n_strx = n_strx, + .n_value = n_value, + .n_type = macho.N_SECT, + .n_desc = sym.n_desc, + .n_sect = @intCast(u8, n_sect), + }, + .tt = tt, + .object = object, }); } } @@ -1212,8 +1249,25 @@ fn relocTargetAddr(self: *Zld, object: Object, rel: macho.relocation_info, next_ // Relocate to either the artifact's local symbol, or an import from // shared library. const sym_name = object.getString(sym.n_strx); - if (self.locals.get(sym_name)) |loc| { - break :blk loc.n_value; + if (self.locals.get(sym_name)) |locs| { + var n_value: ?u64 = null; + for (locs.items) |loc| { + switch (loc.tt) { + .Global => { + n_value = loc.inner.n_value; + break; + }, + .WeakGlobal => { + n_value = loc.inner.n_value; + }, + .Local => {}, + } + } + if (n_value) |v| { + break :blk v; + } + log.err("local symbol export '{s}' not found", .{sym_name}); + return error.LocalSymbolExportNotFound; } else if (self.lazy_imports.get(sym_name)) |ext| { const segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment; const stubs = segment.sections.items[self.stubs_section_index.?]; @@ -1710,19 +1764,37 @@ fn setEntryPoint(self: *Zld) !void { // entrypoint. For now, assume default of `_main`. const seg = self.load_commands.items[self.text_segment_cmd_index.?].Segment; const text = seg.sections.items[self.text_section_index.?]; - const entry_sym = self.locals.get("_main") orelse return error.MissingMainEntrypoint; + const entry_syms = self.locals.get("_main") orelse return error.MissingMainEntrypoint; + + var entry_sym: ?macho.nlist_64 = null; + for (entry_syms.items) |es| { + switch (es.tt) { + .Global => { + entry_sym = es.inner; + break; + }, + .WeakGlobal => { + entry_sym = es.inner; + }, + .Local => {}, + } + } + if (entry_sym == null) { + log.err("no (weak) global definition of _main found", .{}); + return error.MissingMainEntrypoint; + } const name = try self.allocator.dupe(u8, "_main"); try self.exports.putNoClobber(self.allocator, name, .{ - .n_strx = entry_sym.n_strx, - .n_value = entry_sym.n_value, + .n_strx = entry_sym.?.n_strx, + .n_value = entry_sym.?.n_value, .n_type = macho.N_SECT | macho.N_EXT, - .n_desc = entry_sym.n_desc, - .n_sect = entry_sym.n_sect, + .n_desc = entry_sym.?.n_desc, + .n_sect = entry_sym.?.n_sect, }); const ec = &self.load_commands.items[self.main_cmd_index.?].Main; - ec.entryoff = @intCast(u32, entry_sym.n_value - seg.inner.vmaddr); + ec.entryoff = @intCast(u32, entry_sym.?.n_value - seg.inner.vmaddr); } fn writeRebaseInfoTable(self: *Zld) !void { @@ -1968,9 +2040,9 @@ fn writeDebugInfo(self: *Zld) !void { var stabs = std.ArrayList(macho.nlist_64).init(self.allocator); defer stabs.deinit(); - for (self.objects.items) |object| { + for (self.objects.items) |*object| { var debug_info = blk: { - var di = try DebugInfo.parseFromObject(self.allocator, object); + var di = try DebugInfo.parseFromObject(self.allocator, object.*); break :blk di orelse continue; }; defer debug_info.deinit(self.allocator); @@ -2017,7 +2089,12 @@ fn writeDebugInfo(self: *Zld) !void { for (object.symtab.items) |source_sym| { const symname = object.getString(source_sym.n_strx); const source_addr = source_sym.n_value; - const target_sym = self.locals.get(symname) orelse continue; + const target_syms = self.locals.get(symname) orelse continue; + const target_sym: Symbol = blk: { + for (target_syms.items) |ts| { + if (ts.object == object) break :blk ts; + } else continue; + }; const maybe_size = blk: for (debug_info.inner.func_list.items) |func| { if (func.pc_range) |range| { @@ -2031,16 +2108,16 @@ fn writeDebugInfo(self: *Zld) !void { try stabs.append(.{ .n_strx = 0, .n_type = macho.N_BNSYM, - .n_sect = target_sym.n_sect, + .n_sect = target_sym.inner.n_sect, .n_desc = 0, - .n_value = target_sym.n_value, + .n_value = target_sym.inner.n_value, }); try stabs.append(.{ - .n_strx = target_sym.n_strx, + .n_strx = target_sym.inner.n_strx, .n_type = macho.N_FUN, - .n_sect = target_sym.n_sect, + .n_sect = target_sym.inner.n_sect, .n_desc = 0, - .n_value = target_sym.n_value, + .n_value = target_sym.inner.n_value, }); try stabs.append(.{ .n_strx = 0, @@ -2052,18 +2129,18 @@ fn writeDebugInfo(self: *Zld) !void { try stabs.append(.{ .n_strx = 0, .n_type = macho.N_ENSYM, - .n_sect = target_sym.n_sect, + .n_sect = target_sym.inner.n_sect, .n_desc = 0, .n_value = size, }); } else { // TODO need a way to differentiate symbols: global, static, local, etc. try stabs.append(.{ - .n_strx = target_sym.n_strx, + .n_strx = target_sym.inner.n_strx, .n_type = macho.N_STSYM, - .n_sect = target_sym.n_sect, + .n_sect = target_sym.inner.n_sect, .n_desc = 0, - .n_value = target_sym.n_value, + .n_value = target_sym.inner.n_value, }); } } @@ -2102,14 +2179,32 @@ fn writeSymbolTable(self: *Zld) !void { const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment; const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab; - const nlocals = self.locals.items().len; var locals = std.ArrayList(macho.nlist_64).init(self.allocator); defer locals.deinit(); - try locals.ensureCapacity(nlocals); - for (self.locals.items()) |entry| { - locals.appendAssumeCapacity(entry.value); + for (self.locals.items()) |entries| { + log.warn("'{s}': {} entries", .{ entries.key, entries.value.items.len }); + var symbol: ?macho.nlist_64 = null; + for (entries.value.items) |entry| { + log.warn(" | {}", .{entry.inner}); + log.warn(" | {}", .{entry.tt}); + log.warn(" | {s}", .{entry.object.name}); + switch (entry.tt) { + .Global => { + symbol = entry.inner; + break; + }, + .WeakGlobal => { + symbol = entry.inner; + }, + .Local => {}, + } + } + if (symbol) |s| { + try locals.append(s); + } } + const nlocals = locals.items.len; const nexports = self.exports.items().len; var exports = std.ArrayList(macho.nlist_64).init(self.allocator); @@ -2392,3 +2487,7 @@ fn isExtern(sym: *const macho.nlist_64) callconv(.Inline) bool { if ((sym.n_type & macho.N_EXT) == 0) return false; return (sym.n_type & macho.N_PEXT) == 0; } + +fn isWeakDef(sym: *const macho.nlist_64) callconv(.Inline) bool { + return (sym.n_desc & macho.N_WEAK_DEF) != 0; +}