diff --git a/src/link/MachO/Symbol.zig b/src/link/MachO/Symbol.zig index 5a0bfe97624739e2813d86512e193a48850abe3d..9c17c5c833e1699389af8b56e446e2b49ca678f0 100644 --- a/src/link/MachO/Symbol.zig +++ b/src/link/MachO/Symbol.zig @@ -58,13 +58,6 @@ pub const Regular = struct { global, }; - pub fn isTemp(regular: Regular) bool { - if (regular.linkage == .translation_unit) { - return mem.startsWith(u8, regular.base.name, "l") or mem.startsWith(u8, regular.base.name, "L"); - } - return false; - } - pub fn format(self: Regular, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void { try std.fmt.format(writer, "Regular {{ ", .{}); try std.fmt.format(writer, ".linkage = {s}, ", .{self.linkage}); @@ -164,7 +157,19 @@ pub fn new(allocator: *Allocator, name: []const u8) !*Symbol { return new_sym; } -pub fn asNlist(symbol: *Symbol, strtab: *StringTable) macho.nlist_64 { +pub fn isTemp(symbol: Symbol) bool { + switch (symbol.payload) { + .regular => |regular| { + if (regular.linkage == .translation_unit) { + return mem.startsWith(u8, symbol.name, "l") or mem.startsWith(u8, symbol.name, "L"); + } + }, + else => {}, + } + return false; +} + +pub fn asNlist(symbol: *Symbol, strtab: *StringTable) !macho.nlist_64 { const n_strx = try strtab.getOrPut(symbol.name); const nlist = nlist: { switch (symbol.payload) { diff --git a/src/link/MachO/Zld.zig b/src/link/MachO/Zld.zig index 7f12c5ed251c2152e3e58113f1faad9c3057c8d9..f37bf7b696cbba37e659a23d94b29521ff75b5a4 100644 --- a/src/link/MachO/Zld.zig +++ b/src/link/MachO/Zld.zig @@ -237,20 +237,19 @@ pub fn link(self: *Zld, files: []const []const u8, output: Output, args: LinkArg try self.allocateTentativeSymbols(); try self.allocateProxyBindAddresses(); - log.warn("globals", .{}); - for (self.globals.values()) |value| { - log.warn(" | {s}: {}", .{ value.name, value.payload }); - } + // log.warn("globals", .{}); + // for (self.globals.values()) |value| { + // log.warn(" | {s}: {}", .{ value.name, value.payload }); + // } - for (self.objects.items) |object| { - log.warn("object {s}", .{object.name.?}); - for (object.symbols.items) |sym| { - log.warn(" | {s}: {}", .{ sym.name, sym.payload }); - } - } + // for (self.objects.items) |object| { + // log.warn("object {s}", .{object.name.?}); + // for (object.symbols.items) |sym| { + // log.warn(" | {s}: {}", .{ sym.name, sym.payload }); + // } + // } - return error.TODO; - // try self.flush(); + try self.flush(); } fn parseInputFiles(self: *Zld, files: []const []const u8, syslibroot: ?[]const u8) !void { @@ -1226,7 +1225,7 @@ fn writeStubHelperCommon(self: *Zld) !void { code[9] = 0xff; code[10] = 0x25; { - const dyld_stub_binder = self.imports.get("dyld_stub_binder").?; + const dyld_stub_binder = self.globals.get("dyld_stub_binder").?; const addr = (got.addr + dyld_stub_binder.got_index.? * @sizeOf(u64)); const displacement = try math.cast(u32, addr - stub_helper.addr - code_size); mem.writeIntLittle(u32, code[11..], displacement); @@ -1270,7 +1269,7 @@ fn writeStubHelperCommon(self: *Zld) !void { code[10] = 0xbf; code[11] = 0xa9; binder_blk_outer: { - const dyld_stub_binder = self.imports.get("dyld_stub_binder").?; + const dyld_stub_binder = self.globals.get("dyld_stub_binder").?; const this_addr = stub_helper.addr + 3 * @sizeOf(u32); const target_addr = (got.addr + dyld_stub_binder.got_index.? * @sizeOf(u64)); binder_blk: { @@ -1789,8 +1788,8 @@ fn resolveRelocsAndWriteSections(self: *Zld) !void { break :rebase false; } if (rel.target == .symbol) { - const final = object.symbols.items[rel.target.symbol].getTopmostAlias(); - if (final.cast(Symbol.Proxy)) |_| { + const sym = object.symbols.items[rel.target.symbol]; + if (sym.payload == .proxy) { break :rebase false; } } @@ -1832,9 +1831,8 @@ fn resolveRelocsAndWriteSections(self: *Zld) !void { const dc_seg = self.load_commands.items[self.data_const_segment_cmd_index.?].Segment; const got = dc_seg.sections.items[self.got_section_index.?]; const sym = object.symbols.items[rel.target.symbol]; - const final = sym.getTopmostAlias(); - const got_index = final.got_index orelse { - log.err("expected GOT index relocating symbol '{s}'", .{final.name}); + const got_index = sym.got_index orelse { + log.err("expected GOT index relocating symbol '{s}'", .{sym.name}); log.err("this is an internal linker error", .{}); return error.FailedToResolveRelocationTarget; }; @@ -1890,37 +1888,40 @@ fn relocTargetAddr(self: *Zld, object: *const Object, target: reloc.Relocation.T switch (target) { .symbol => |sym_id| { const sym = object.symbols.items[sym_id]; - const final = sym.getTopmostAlias(); - if (final.cast(Symbol.Regular)) |reg| { - log.debug(" | regular '{s}'", .{sym.name}); - break :blk reg.address; - } else if (final.cast(Symbol.Proxy)) |proxy| { - if (mem.eql(u8, sym.name, "__tlv_bootstrap")) { - log.debug(" | symbol '__tlv_bootstrap'", .{}); - const segment = self.load_commands.items[self.data_segment_cmd_index.?].Segment; - const tlv = segment.sections.items[self.tlv_section_index.?]; - break :blk tlv.addr; - } - - log.debug(" | symbol stub '{s}'", .{sym.name}); - const segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment; - const stubs = segment.sections.items[self.stubs_section_index.?]; - const stubs_index = proxy.base.stubs_index orelse { - if (proxy.bind_info.items.len > 0) { - break :blk 0; // Dynamically bound by dyld. + switch (sym.payload) { + .regular => |reg| { + log.debug(" | regular '{s}'", .{sym.name}); + break :blk reg.address; + }, + .proxy => |proxy| { + if (mem.eql(u8, sym.name, "__tlv_bootstrap")) { + log.debug(" | symbol '__tlv_bootstrap'", .{}); + const segment = self.load_commands.items[self.data_segment_cmd_index.?].Segment; + const tlv = segment.sections.items[self.tlv_section_index.?]; + break :blk tlv.addr; } - log.err( - "expected stubs index or dynamic bind address when relocating symbol '{s}'", - .{final.name}, - ); + + log.debug(" | symbol stub '{s}'", .{sym.name}); + const segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment; + const stubs = segment.sections.items[self.stubs_section_index.?]; + const stubs_index = sym.stubs_index orelse { + if (proxy.bind_info.items.len > 0) { + break :blk 0; // Dynamically bound by dyld. + } + log.err( + "expected stubs index or dynamic bind address when relocating symbol '{s}'", + .{sym.name}, + ); + log.err("this is an internal linker error", .{}); + return error.FailedToResolveRelocationTarget; + }; + break :blk stubs.addr + stubs_index * stubs.reserved2; + }, + else => { + log.err("failed to resolve symbol '{s}' as a relocation target", .{sym.name}); log.err("this is an internal linker error", .{}); return error.FailedToResolveRelocationTarget; - }; - break :blk stubs.addr + stubs_index * stubs.reserved2; - } else { - log.err("failed to resolve symbol '{s}' as a relocation target", .{sym.name}); - log.err("this is an internal linker error", .{}); - return error.FailedToResolveRelocationTarget; + }, } }, .section => |sect_id| { @@ -2320,8 +2321,8 @@ fn flush(self: *Zld) !void { defer initializers.deinit(); for (self.objects.items) |object| { - for (object.initializers.items) |initializer| { - const address = initializer.cast(Symbol.Regular).?.address; + for (object.initializers.items) |sym_id| { + const address = object.symbols.items[sym_id].payload.regular.address; try initializers.append(address); } } @@ -2381,7 +2382,10 @@ fn writeGotEntries(self: *Zld) !void { var writer = stream.writer(); for (self.got_entries.items) |sym| { - const address: u64 = if (sym.cast(Symbol.Regular)) |reg| reg.address else 0; + const address: u64 = switch (sym.payload) { + .regular => |reg| reg.address, + else => 0, + }; try writer.writeIntLittle(u64, address); } @@ -2397,9 +2401,8 @@ 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 sym = self.globals.get("_main") orelse return error.MissingMainEntrypoint; - const entry_sym = sym.cast(Symbol.Regular) orelse unreachable; const ec = &self.load_commands.items[self.main_cmd_index.?].Main; - ec.entryoff = @intCast(u32, entry_sym.address - seg.inner.vmaddr); + ec.entryoff = @intCast(u32, sym.payload.regular.address - seg.inner.vmaddr); ec.stacksize = self.stack_size; } @@ -2417,7 +2420,8 @@ fn writeRebaseInfoTable(self: *Zld) !void { const segment_id = @intCast(u16, self.data_const_segment_cmd_index.?); for (self.got_entries.items) |sym| { - if (sym.@"type" == .proxy) continue; + if (sym.payload == .proxy) continue; + try pointers.append(.{ .offset = base_offset + sym.got_index.? * @sizeOf(u64), .segment_id = segment_id, @@ -2489,28 +2493,30 @@ fn writeBindInfoTable(self: *Zld) !void { const segment_id = @intCast(u16, self.data_const_segment_cmd_index.?); for (self.got_entries.items) |sym| { - if (sym.cast(Symbol.Proxy)) |proxy| { - try pointers.append(.{ - .offset = base_offset + proxy.base.got_index.? * @sizeOf(u64), - .segment_id = segment_id, - .dylib_ordinal = proxy.dylibOrdinal(), - .name = proxy.base.name, - }); - } + if (sym.payload != .proxy) continue; + + const proxy = sym.payload.proxy; + try pointers.append(.{ + .offset = base_offset + sym.got_index.? * @sizeOf(u64), + .segment_id = segment_id, + .dylib_ordinal = proxy.dylibOrdinal(), + .name = sym.name, + }); } } - for (self.imports.values()) |sym| { - if (sym.cast(Symbol.Proxy)) |proxy| { - for (proxy.bind_info.items) |info| { - const seg = self.load_commands.items[info.segment_id].Segment; - try pointers.append(.{ - .offset = info.address - seg.inner.vmaddr, - .segment_id = info.segment_id, - .dylib_ordinal = proxy.dylibOrdinal(), - .name = proxy.base.name, - }); - } + for (self.globals.values()) |sym| { + if (sym.payload != .proxy) continue; + + const proxy = sym.payload.proxy; + for (proxy.bind_info.items) |info| { + const seg = self.load_commands.items[info.segment_id].Segment; + try pointers.append(.{ + .offset = info.address - seg.inner.vmaddr, + .segment_id = info.segment_id, + .dylib_ordinal = proxy.dylibOrdinal(), + .name = sym.name, + }); } } @@ -2520,14 +2526,13 @@ fn writeBindInfoTable(self: *Zld) !void { const base_offset = sect.addr - seg.inner.vmaddr; const segment_id = @intCast(u16, self.data_segment_cmd_index.?); - const sym = self.imports.get("__tlv_bootstrap") orelse unreachable; - const proxy = sym.cast(Symbol.Proxy) orelse unreachable; - + const sym = self.globals.get("__tlv_bootstrap") orelse unreachable; + const proxy = sym.payload.proxy; try pointers.append(.{ .offset = base_offset, .segment_id = segment_id, .dylib_ordinal = proxy.dylibOrdinal(), - .name = proxy.base.name, + .name = sym.name, }); } @@ -2562,7 +2567,7 @@ fn writeLazyBindInfoTable(self: *Zld) !void { try pointers.ensureCapacity(self.stubs.items.len); for (self.stubs.items) |sym| { - const proxy = sym.cast(Symbol.Proxy) orelse unreachable; + const proxy = sym.payload.proxy; pointers.appendAssumeCapacity(.{ .offset = base_offset + sym.stubs_index.? * @sizeOf(u64), .segment_id = segment_id, @@ -2676,7 +2681,8 @@ fn writeExportInfo(self: *Zld) !void { defer sorted_globals.deinit(); for (self.globals.values()) |sym| { - const reg = sym.cast(Symbol.Regular) orelse continue; + if (sym.payload != .regular) continue; + const reg = sym.payload.regular; if (reg.linkage != .global) continue; try sorted_globals.append(sym.name); } @@ -2685,9 +2691,9 @@ fn writeExportInfo(self: *Zld) !void { for (sorted_globals.items) |sym_name| { const sym = self.globals.get(sym_name) orelse unreachable; - const reg = sym.cast(Symbol.Regular) orelse unreachable; + const reg = sym.payload.regular; - log.debug(" | putting '{s}' defined at 0x{x}", .{ reg.base.name, reg.address }); + log.debug(" | putting '{s}' defined at 0x{x}", .{ sym.name, reg.address }); try trie.put(.{ .name = sym.name, @@ -2722,50 +2728,33 @@ fn writeSymbolTable(self: *Zld) !void { var locals = std.ArrayList(macho.nlist_64).init(self.allocator); defer locals.deinit(); + try locals.ensureTotalCapacity(self.locals.items.len); + + for (self.locals.items) |symbol| { + if (symbol.isTemp()) continue; // TODO when merging codepaths, this should go into freelist + const nlist = try symbol.asNlist(&self.strtab); + locals.appendAssumeCapacity(nlist); + } var exports = std.ArrayList(macho.nlist_64).init(self.allocator); defer exports.deinit(); - for (self.objects.items) |object| { - for (object.stabs.items) |sym| { - const stab = sym.cast(Symbol.Stab) orelse unreachable; - - const nlists = try stab.asNlists(self.allocator, &self.strtab); - defer self.allocator.free(nlists); - - try locals.appendSlice(nlists); - } - - for (object.symbols.items) |sym| { - const final = sym.getTopmostAlias(); - if (final.@"type" != .regular) continue; - - const reg = final.cast(Symbol.Regular) orelse unreachable; - if (reg.isTemp()) continue; - if (reg.visited) continue; - - const nlist = try reg.asNlist(&self.strtab); - - switch (reg.linkage) { - .translation_unit => { - try locals.append(nlist); - }, - else => { - try exports.append(nlist); - }, - } - - reg.visited = true; - } - } - var undefs = std.ArrayList(macho.nlist_64).init(self.allocator); defer undefs.deinit(); + var undef_dir = std.StringHashMap(u32).init(self.allocator); + defer undef_dir.deinit(); - for (self.imports.values()) |sym| { - const proxy = sym.cast(Symbol.Proxy) orelse unreachable; - const nlist = try proxy.asNlist(&self.strtab); - try undefs.append(nlist); + for (self.globals.values()) |sym| { + const nlist = try sym.asNlist(&self.strtab); + switch (sym.payload) { + .regular => try exports.append(nlist), + .proxy => { + const id = @intCast(u32, undefs.items.len); + try undefs.append(nlist); + try undef_dir.putNoClobber(sym.name, id); + }, + else => unreachable, + } } const nlocals = locals.items.len; @@ -2827,24 +2816,27 @@ fn writeSymbolTable(self: *Zld) !void { stubs.reserved1 = 0; for (self.stubs.items) |sym| { - const id = self.imports.getIndex(sym.name) orelse unreachable; - try writer.writeIntLittle(u32, dysymtab.iundefsym + @intCast(u32, id)); + const id = undef_dir.get(sym.name) orelse unreachable; + try writer.writeIntLittle(u32, dysymtab.iundefsym + id); } got.reserved1 = nstubs; for (self.got_entries.items) |sym| { - if (sym.@"type" == .proxy) { - const id = self.imports.getIndex(sym.name) orelse unreachable; - try writer.writeIntLittle(u32, dysymtab.iundefsym + @intCast(u32, id)); - } else { - try writer.writeIntLittle(u32, macho.INDIRECT_SYMBOL_LOCAL); + switch (sym.payload) { + .proxy => { + const id = undef_dir.get(sym.name) orelse unreachable; + try writer.writeIntLittle(u32, dysymtab.iundefsym + id); + }, + else => { + try writer.writeIntLittle(u32, macho.INDIRECT_SYMBOL_LOCAL); + }, } } la_symbol_ptr.reserved1 = got.reserved1 + ngot_entries; for (self.stubs.items) |sym| { - const id = self.imports.getIndex(sym.name) orelse unreachable; - try writer.writeIntLittle(u32, dysymtab.iundefsym + @intCast(u32, id)); + const id = undef_dir.get(sym.name) orelse unreachable; + try writer.writeIntLittle(u32, dysymtab.iundefsym + id); } try self.file.?.pwriteAll(buf, dysymtab.indirectsymoff);