diff options
| author | Jakub Konka <kubkon@jakubkonka.com> | 2024-01-17 20:09:12 +0100 |
|---|---|---|
| committer | Jakub Konka <kubkon@jakubkonka.com> | 2024-01-24 12:34:41 +0100 |
| commit | 8c578ba02ccff63a64093b5acdefcf7b95cc8c46 (patch) | |
| tree | aad7a4e9fc7a29863f1e747151613ba1a8e44081 /src | |
| parent | c7de5e511125a738269a802318695b98a88b2791 (diff) | |
| download | zig-8c578ba02ccff63a64093b5acdefcf7b95cc8c46.tar.gz zig-8c578ba02ccff63a64093b5acdefcf7b95cc8c46.zip | |
macho: add __zig_got section implementation
Diffstat (limited to 'src')
| -rw-r--r-- | src/link/MachO.zig | 13 | ||||
| -rw-r--r-- | src/link/MachO/Atom.zig | 153 | ||||
| -rw-r--r-- | src/link/MachO/Symbol.zig | 22 | ||||
| -rw-r--r-- | src/link/MachO/ZigObject.zig | 182 | ||||
| -rw-r--r-- | src/link/MachO/synthetic.zig | 118 |
5 files changed, 464 insertions, 24 deletions
diff --git a/src/link/MachO.zig b/src/link/MachO.zig index 912ac6a43e..035c59ac21 100644 --- a/src/link/MachO.zig +++ b/src/link/MachO.zig @@ -70,6 +70,7 @@ symtab: std.ArrayListUnmanaged(macho.nlist_64) = .{}, strtab: std.ArrayListUnmanaged(u8) = .{}, indsymtab: Indsymtab = .{}, got: GotSection = .{}, +zig_got: ZigGotSection = .{}, stubs: StubsSection = .{}, stubs_helper: StubsHelperSection = .{}, objc_stubs: ObjcStubsSection = .{}, @@ -337,6 +338,7 @@ pub fn deinit(self: *MachO) void { self.symtab.deinit(gpa); self.strtab.deinit(gpa); self.got.deinit(gpa); + self.zig_got.deinit(gpa); self.stubs.deinit(gpa); self.objc_stubs.deinit(gpa); self.tlv_ptr.deinit(gpa); @@ -3157,6 +3159,13 @@ fn initMetadata(self: *MachO, options: InitMetadataOptions) !void { } } +pub fn growSection(self: *MachO, sect_index: u8, size: u64) !void { + _ = self; + _ = sect_index; + _ = size; + @panic("TODO growSection"); +} + pub fn getTarget(self: MachO) std.Target { return self.base.comp.root_mod.resolved_target.result; } @@ -3657,6 +3666,7 @@ fn fmtDumpState( try writer.print("stubs\n{}\n", .{self.stubs.fmt(self)}); try writer.print("objc_stubs\n{}\n", .{self.objc_stubs.fmt(self)}); try writer.print("got\n{}\n", .{self.got.fmt(self)}); + try writer.print("zig_got\n{}\n", .{self.zig_got.fmt(self)}); try writer.print("tlv_ptr\n{}\n", .{self.tlv_ptr.fmt(self)}); try writer.writeByte('\n'); try writer.print("sections\n{}\n", .{self.fmtSections()}); @@ -3759,6 +3769,8 @@ const Section = struct { header: macho.section_64, segment_id: u8, atoms: std.ArrayListUnmanaged(Atom.Index) = .{}, + free_list: std.ArrayListUnmanaged(Atom.Index) = .{}, + last_atom_index: Atom.Index = 0, }; const HotUpdateState = struct { @@ -4125,4 +4137,5 @@ const TlvPtrSection = synthetic.TlvPtrSection; const TypedValue = @import("../TypedValue.zig"); const UnwindInfo = @import("MachO/UnwindInfo.zig"); const WeakBindSection = synthetic.WeakBindSection; +const ZigGotSection = synthetic.ZigGotSection; const ZigObject = @import("MachO/ZigObject.zig"); diff --git a/src/link/MachO/Atom.zig b/src/link/MachO/Atom.zig index 98223a9ad2..374a98021f 100644 --- a/src/link/MachO/Atom.zig +++ b/src/link/MachO/Atom.zig @@ -37,6 +37,11 @@ unwind_records: Loc = .{}, flags: Flags = .{}, +/// Points to the previous and next neighbors, based on the `text_offset`. +/// This can be used to find, for example, the capacity of this `TextBlock`. +prev_index: Index = 0, +next_index: Index = 0, + pub fn getName(self: Atom, macho_file: *MachO) [:0]const u8 { return macho_file.strings.getAssumeExists(self.name); } @@ -171,6 +176,154 @@ pub fn initOutputSection(sect: macho.section_64, macho_file: *MachO) !u8 { return osec; } +/// Returns how much room there is to grow in virtual address space. +/// File offset relocation happens transparently, so it is not included in +/// this calculation. +pub fn capacity(self: Atom, macho_file: *MachO) u64 { + const next_value = if (macho_file.getAtom(self.next_index)) |next| next.value else std.math.maxInt(u32); + return next_value - self.value; +} + +pub fn freeListEligible(self: Atom, macho_file: *MachO) bool { + // No need to keep a free list node for the last block. + const next = macho_file.getAtom(self.next_index) orelse return false; + const cap = next.value - self.value; + const ideal_cap = MachO.padToIdeal(self.size); + if (cap <= ideal_cap) return false; + const surplus = cap - ideal_cap; + return surplus >= MachO.min_text_capacity; +} + +pub fn allocate(self: *Atom, macho_file: *MachO) !void { + const sect = &macho_file.sections.items(.header)[self.out_n_sect]; + const free_list = &macho_file.sections.items(.free_list)[self.out_n_sect]; + const last_atom_index = &macho_file.sections.items(.last_atom_index)[self.out_n_sect]; + const new_atom_ideal_capacity = MachO.padToIdeal(self.size); + + // We use these to indicate our intention to update metadata, placing the new atom, + // and possibly removing a free list node. + // It would be simpler to do it inside the for loop below, but that would cause a + // problem if an error was returned later in the function. So this action + // is actually carried out at the end of the function, when errors are no longer possible. + var atom_placement: ?Atom.Index = null; + var free_list_removal: ?usize = null; + + // First we look for an appropriately sized free list node. + // The list is unordered. We'll just take the first thing that works. + self.value = blk: { + var i: usize = free_list.items.len; + while (i < free_list.items.len) { + const big_atom_index = free_list.items[i]; + const big_atom = macho_file.getAtom(big_atom_index).?; + // We now have a pointer to a live atom that has too much capacity. + // Is it enough that we could fit this new atom? + const cap = big_atom.capacity(macho_file); + const ideal_capacity = MachO.padToIdeal(cap); + const ideal_capacity_end_vaddr = std.math.add(u64, big_atom.value, ideal_capacity) catch ideal_capacity; + const capacity_end_vaddr = big_atom.value + cap; + const new_start_vaddr_unaligned = capacity_end_vaddr - new_atom_ideal_capacity; + const new_start_vaddr = self.alignment.backward(new_start_vaddr_unaligned); + if (new_start_vaddr < ideal_capacity_end_vaddr) { + // Additional bookkeeping here to notice if this free list node + // should be deleted because the block that it points to has grown to take up + // more of the extra capacity. + if (!big_atom.freeListEligible(macho_file)) { + _ = free_list.swapRemove(i); + } else { + i += 1; + } + continue; + } + // At this point we know that we will place the new block here. But the + // remaining question is whether there is still yet enough capacity left + // over for there to still be a free list node. + const remaining_capacity = new_start_vaddr - ideal_capacity_end_vaddr; + const keep_free_list_node = remaining_capacity >= MachO.min_text_capacity; + + // Set up the metadata to be updated, after errors are no longer possible. + atom_placement = big_atom_index; + if (!keep_free_list_node) { + free_list_removal = i; + } + break :blk new_start_vaddr; + } else if (macho_file.getAtom(last_atom_index.*)) |last| { + const ideal_capacity = MachO.padToIdeal(last.size); + const ideal_capacity_end_vaddr = last.value + ideal_capacity; + const new_start_vaddr = self.alignment.forward(ideal_capacity_end_vaddr); + // Set up the metadata to be updated, after errors are no longer possible. + atom_placement = last.atom_index; + break :blk new_start_vaddr; + } else { + break :blk sect.addr; + } + }; + + log.debug("allocated atom({d}) : '{s}' at 0x{x} to 0x{x}", .{ + self.atom_index, + self.getName(macho_file), + self.value, + self.value + self.size, + }); + + const expand_section = if (atom_placement) |placement_index| + macho_file.getAtom(placement_index).?.next_index == 0 + else + true; + if (expand_section) { + const needed_size = (self.value + self.size) - sect.addr; + try macho_file.growSection(self.out_n_sect, needed_size); + last_atom_index.* = self.atom_index; + + // const zig_object = macho_file_file.getZigObject().?; + // if (zig_object.dwarf) |_| { + // // The .debug_info section has `low_pc` and `high_pc` values which is the virtual address + // // range of the compilation unit. When we expand the text section, this range changes, + // // so the DW_TAG.compile_unit tag of the .debug_info section becomes dirty. + // zig_object.debug_info_header_dirty = true; + // // This becomes dirty for the same reason. We could potentially make this more + // // fine-grained with the addition of support for more compilation units. It is planned to + // // model each package as a different compilation unit. + // zig_object.debug_aranges_section_dirty = true; + // } + } + sect.@"align" = @max(sect.@"align", self.alignment.toLog2Units()); + + // This function can also reallocate an atom. + // In this case we need to "unplug" it from its previous location before + // plugging it in to its new location. + if (macho_file.getAtom(self.prev_index)) |prev| { + prev.next_index = self.next_index; + } + if (macho_file.getAtom(self.next_index)) |next| { + next.prev_index = self.prev_index; + } + + if (atom_placement) |big_atom_index| { + const big_atom = macho_file.getAtom(big_atom_index).?; + self.prev_index = big_atom_index; + self.next_index = big_atom.next_index; + big_atom.next_index = self.atom_index; + } else { + self.prev_index = 0; + self.next_index = 0; + } + if (free_list_removal) |i| { + _ = free_list.swapRemove(i); + } + + self.flags.alive = true; +} + +pub fn shrink(self: *Atom, macho_file: *MachO) void { + _ = self; + _ = macho_file; +} + +pub fn grow(self: *Atom, macho_file: *MachO) !void { + if (!self.alignment.check(self.value) or self.size > self.capacity(macho_file)) + try self.allocate(macho_file); +} + pub fn scanRelocs(self: Atom, macho_file: *MachO) !void { const tracy = trace(@src()); defer tracy.end(); diff --git a/src/link/MachO/Symbol.zig b/src/link/MachO/Symbol.zig index a86aea80b5..5ffbabe1e2 100644 --- a/src/link/MachO/Symbol.zig +++ b/src/link/MachO/Symbol.zig @@ -149,6 +149,25 @@ pub fn getTlvPtrAddress(symbol: Symbol, macho_file: *MachO) u64 { return macho_file.tlv_ptr.getAddress(extra.tlv_ptr, macho_file); } +const GetOrCreateZigGotEntryResult = struct { + found_existing: bool, + index: ZigGotSection.Index, +}; + +pub fn getOrCreateZigGotEntry(symbol: *Symbol, symbol_index: Index, macho_file: *MachO) !GetOrCreateZigGotEntryResult { + assert(!macho_file.base.isRelocatable()); + assert(symbol.flags.needs_zig_got); + if (symbol.flags.has_zig_got) return .{ .found_existing = true, .index = symbol.getExtra(macho_file).?.zig_got }; + const index = try macho_file.zig_got.addSymbol(symbol_index, macho_file); + return .{ .found_existing = false, .index = index }; +} + +pub fn zigGotAddress(symbol: Symbol, macho_file: *MachO) u64 { + if (!symbol.flags.has_zig_got) return 0; + const extras = symbol.getExtra(macho_file).?; + return macho_file.zig_got.entryAddress(extras.zig_got, macho_file); +} + pub fn getOutputSymtabIndex(symbol: Symbol, macho_file: *MachO) ?u32 { if (!symbol.flags.output_symtab) return null; assert(!symbol.isSymbolStab(macho_file)); @@ -170,6 +189,7 @@ pub fn getOutputSymtabIndex(symbol: Symbol, macho_file: *MachO) ?u32 { const AddExtraOpts = struct { got: ?u32 = null, + zig_got: ?u32 = null, stubs: ?u32 = null, objc_stubs: ?u32 = null, objc_selrefs: ?u32 = null, @@ -374,6 +394,7 @@ pub const Visibility = enum { pub const Extra = struct { got: u32 = 0, + zig_got: u32 = 0, stubs: u32 = 0, objc_stubs: u32 = 0, objc_selrefs: u32 = 0, @@ -393,3 +414,4 @@ const MachO = @import("../MachO.zig"); const Nlist = Object.Nlist; const Object = @import("Object.zig"); const Symbol = @This(); +const ZigGotSection = @import("synthetic.zig").ZigGotSection; diff --git a/src/link/MachO/ZigObject.zig b/src/link/MachO/ZigObject.zig index 007df845f6..4e0cb76a40 100644 --- a/src/link/MachO/ZigObject.zig +++ b/src/link/MachO/ZigObject.zig @@ -7,9 +7,36 @@ symtab: std.MultiArrayList(Nlist) = .{}, symbols: std.ArrayListUnmanaged(Symbol.Index) = .{}, atoms: std.ArrayListUnmanaged(Atom.Index) = .{}, +/// Table of tracked LazySymbols. +lazy_syms: LazySymbolTable = .{}, + /// Table of tracked Decls. decls: DeclTable = .{}, +/// Table of unnamed constants associated with a parent `Decl`. +/// We store them here so that we can free the constants whenever the `Decl` +/// needs updating or is freed. +/// +/// For example, +/// +/// ```zig +/// const Foo = struct{ +/// a: u8, +/// }; +/// +/// pub fn main() void { +/// var foo = Foo{ .a = 1 }; +/// _ = foo; +/// } +/// ``` +/// +/// value assigned to label `foo` is an unnamed constant belonging/associated +/// with `Decl` `main`, and lives as long as that `Decl`. +unnamed_consts: UnnamedConstTable = .{}, + +/// Table of tracked AnonDecls. +anon_decls: AnonDeclTable = .{}, + /// A table of relocations. relocs: RelocationTable = .{}, @@ -35,6 +62,24 @@ pub fn deinit(self: *ZigObject, allocator: Allocator) void { self.decls.deinit(allocator); } + self.lazy_syms.deinit(allocator); + + { + var it = self.unnamed_consts.valueIterator(); + while (it.next()) |syms| { + syms.deinit(allocator); + } + self.unnamed_consts.deinit(allocator); + } + + { + var it = self.anon_decls.iterator(); + while (it.next()) |entry| { + entry.value_ptr.exports.deinit(allocator); + } + self.anon_decls.deinit(allocator); + } + for (self.relocs.items) |*list| { list.deinit(allocator); } @@ -296,7 +341,7 @@ pub fn updateDecl( }, }; const sect_index = try self.getDeclOutputSection(macho_file, decl, code); - const is_threadlocal = switch (macho_file.sections.items(.header[sect_index].type())) { + const is_threadlocal = switch (macho_file.sections.items(.header)[sect_index].type()) { macho.S_THREAD_LOCAL_ZEROFILL, macho.S_THREAD_LOCAL_REGULAR => true, else => false, }; @@ -317,21 +362,21 @@ pub fn updateDecl( // ); // } - // // Since we updated the vaddr and the size, each corresponding export symbol also - // // needs to be updated. - // try self.updateExports(mod, .{ .decl_index = decl_index }, mod.getDeclExports(decl_index)); + // Since we updated the vaddr and the size, each corresponding export symbol also + // needs to be updated. + try self.updateExports(macho_file, mod, .{ .decl_index = decl_index }, mod.getDeclExports(decl_index)); } fn updateDeclCode( self: *ZigObject, macho_file: *MachO, - decl_index: Module.Decl.Index, + decl_index: InternPool.DeclIndex, sym_index: Symbol.Index, sect_index: u8, code: []const u8, ) !void { - const gpa = self.base.comp.gpa; - const mod = self.base.comp.module.?; + const gpa = macho_file.base.comp.gpa; + const mod = macho_file.base.comp.module.?; const decl = mod.declPtr(decl_index); const decl_name = mod.intern_pool.stringToSlice(try decl.getFullyQualifiedName(mod)); @@ -342,7 +387,6 @@ fn updateDeclCode( const sect = &macho_file.sections.items(.header)[sect_index]; const sym = macho_file.getSymbol(sym_index); const nlist = &self.symtab.items(.nlist)[sym.nlist_idx]; - const size = &self.symtab.items(.size)[sym.nlist_idx]; const atom = sym.getAtom(macho_file).?; sym.out_n_sect = sect_index; @@ -354,7 +398,7 @@ fn updateDeclCode( nlist.n_strx = sym.name; nlist.n_type = macho.N_SECT; nlist.n_sect = sect_index + 1; - size = code.len; + self.symtab.items(.size)[sym.nlist_idx] = code.len; const old_size = atom.size; const old_vaddr = atom.value; @@ -363,14 +407,14 @@ fn updateDeclCode( if (old_size > 0) { const capacity = atom.capacity(macho_file); - const need_realloc = code.len > capacity or !required_alignment.check(sym.value); + const need_realloc = code.len > capacity or !required_alignment.check(sym.getAddress(.{}, macho_file)); if (need_realloc) { try atom.grow(macho_file); log.debug("growing {s} from 0x{x} to 0x{x}", .{ decl_name, old_vaddr, atom.value }); if (old_vaddr != atom.value) { - sym.value = atom.value; - nlist.n_value = atom.value; + sym.value = 0; + nlist.n_value = 0; if (!macho_file.base.isRelocatable()) { log.debug(" (updating offset table entry)", .{}); @@ -381,17 +425,17 @@ fn updateDeclCode( } } else if (code.len < old_size) { atom.shrink(macho_file); - } else if (atom.next_index == null) { - const needed_size = (sym.value + code.len) - sect.addr; + } else if (macho_file.getAtom(atom.next_index) == null) { + const needed_size = (sym.getAddress(.{}, macho_file) + code.len) - sect.addr; sect.size = needed_size; } } else { try atom.allocate(macho_file); // TODO: freeDeclMetadata in case of error - sym.value = atom.value; + sym.value = 0; sym.flags.needs_zig_got = true; - nlist.n_value = atom.value; + nlist.n_value = 0; if (!macho_file.base.isRelocatable()) { const gop = try sym.getOrCreateZigGotEntry(sym_index, macho_file); @@ -400,7 +444,7 @@ fn updateDeclCode( } if (!sect.isZerofill()) { - const file_offset = sect.offset + sym.value - sect.addr; + const file_offset = sect.offset + sym.getAddress(.{}, macho_file) - sect.addr; try macho_file.base.file.?.pwriteAll(code, file_offset); } } @@ -478,12 +522,91 @@ pub fn updateExports( exported: Module.Exported, exports: []const *Module.Export, ) link.File.UpdateExportsError!void { - _ = self; - _ = macho_file; - _ = mod; - _ = exported; - _ = exports; - @panic("TODO updateExports"); + const tracy = trace(@src()); + defer tracy.end(); + + const gpa = macho_file.base.comp.gpa; + const metadata = switch (exported) { + .decl_index => |decl_index| blk: { + _ = try self.getOrCreateMetadataForDecl(macho_file, decl_index); + break :blk self.decls.getPtr(decl_index).?; + }, + .value => |value| self.anon_decls.getPtr(value) orelse blk: { + const first_exp = exports[0]; + const res = try self.lowerAnonDecl(macho_file, value, .none, first_exp.getSrcLoc(mod)); + switch (res) { + .ok => {}, + .fail => |em| { + // TODO maybe it's enough to return an error here and let Module.processExportsInner + // handle the error? + try mod.failed_exports.ensureUnusedCapacity(mod.gpa, 1); + mod.failed_exports.putAssumeCapacityNoClobber(first_exp, em); + return; + }, + } + break :blk self.anon_decls.getPtr(value).?; + }, + }; + const sym_index = metadata.symbol_index; + const nlist_idx = macho_file.getSymbol(sym_index).nlist_idx; + const nlist = self.symtab.items(.nlist)[nlist_idx]; + + for (exports) |exp| { + if (exp.opts.section.unwrap()) |section_name| { + if (!mod.intern_pool.stringEqlSlice(section_name, "__text")) { + try mod.failed_exports.ensureUnusedCapacity(mod.gpa, 1); + mod.failed_exports.putAssumeCapacityNoClobber(exp, try Module.ErrorMsg.create( + gpa, + exp.getSrcLoc(mod), + "Unimplemented: ExportOptions.section", + .{}, + )); + continue; + } + } + if (exp.opts.linkage == .LinkOnce) { + try mod.failed_exports.putNoClobber(mod.gpa, exp, try Module.ErrorMsg.create( + gpa, + exp.getSrcLoc(mod), + "Unimplemented: GlobalLinkage.LinkOnce", + .{}, + )); + continue; + } + + const exp_name = try std.fmt.allocPrint(gpa, "_{}", .{exp.opts.name.fmt(&mod.intern_pool)}); + defer gpa.free(exp_name); + + const name_off = try macho_file.strings.insert(gpa, exp_name); + const global_nlist_index = if (metadata.@"export"(self, macho_file, exp_name)) |exp_index| + exp_index.* + else blk: { + const global_nlist_index = try self.getGlobalSymbol(macho_file, exp_name, null); + try metadata.exports.append(gpa, global_nlist_index); + break :blk global_nlist_index; + }; + const global_nlist = &self.symtab.items(.nlist)[global_nlist_index]; + global_nlist.n_strx = name_off; + global_nlist.n_value = nlist.n_value; + global_nlist.n_sect = nlist.n_sect; + global_nlist.n_type = macho.N_EXT | macho.N_SECT; + self.symtab.items(.size)[global_nlist_index] = self.symtab.items(.size)[nlist_idx]; + self.symtab.items(.atom)[global_nlist_index] = self.symtab.items(.atom)[nlist_idx]; + + switch (exp.opts.linkage) { + .Internal => { + // Symbol should be hidden, or in MachO lingo, private extern. + global_nlist.n_type |= macho.N_PEXT; + }, + .Strong => {}, + .Weak => { + // Weak linkage is specified as part of n_desc field. + // Symbol's n_type is like for a symbol with strong linkage. + global_nlist.n_desc |= macho.N_WEAK_DEF; + }, + else => unreachable, + } + } } /// Must be called only after a successful call to `updateDecl`. @@ -612,8 +735,19 @@ const DeclMetadata = struct { return null; } }; -const DeclTable = std.AutoHashMapUnmanaged(InternPool.DeclIndex, DeclMetadata); +const LazySymbolMetadata = struct { + const State = enum { unused, pending_flush, flushed }; + text_symbol_index: Symbol.Index = undefined, + data_const_symbol_index: Symbol.Index = undefined, + text_state: State = .unused, + rodata_state: State = .unused, +}; + +const DeclTable = std.AutoHashMapUnmanaged(InternPool.DeclIndex, DeclMetadata); +const UnnamedConstTable = std.AutoHashMapUnmanaged(InternPool.DeclIndex, std.ArrayListUnmanaged(Symbol.Index)); +const AnonDeclTable = std.AutoHashMapUnmanaged(InternPool.Index, DeclMetadata); +const LazySymbolTable = std.AutoArrayHashMapUnmanaged(InternPool.OptionalDeclIndex, LazySymbolMetadata); const RelocationTable = std.ArrayListUnmanaged(std.ArrayListUnmanaged(Relocation)); const assert = std.debug.assert; diff --git a/src/link/MachO/synthetic.zig b/src/link/MachO/synthetic.zig index cc5643c7ec..5a6e316da5 100644 --- a/src/link/MachO/synthetic.zig +++ b/src/link/MachO/synthetic.zig @@ -1,3 +1,121 @@ +pub const ZigGotSection = struct { + entries: std.ArrayListUnmanaged(Symbol.Index) = .{}, + dirty: bool = false, + + pub const Index = u32; + + pub fn deinit(zig_got: *ZigGotSection, allocator: Allocator) void { + zig_got.entries.deinit(allocator); + } + + fn allocateEntry(zig_got: *ZigGotSection, allocator: Allocator) !Index { + try zig_got.entries.ensureUnusedCapacity(allocator, 1); + // TODO add free list + const index = @as(Index, @intCast(zig_got.entries.items.len)); + _ = zig_got.entries.addOneAssumeCapacity(); + zig_got.dirty = true; + return index; + } + + pub fn addSymbol(zig_got: *ZigGotSection, sym_index: Symbol.Index, macho_file: *MachO) !Index { + const comp = macho_file.base.comp; + const gpa = comp.gpa; + const index = try zig_got.allocateEntry(gpa); + const entry = &zig_got.entries.items[index]; + entry.* = sym_index; + const symbol = macho_file.getSymbol(sym_index); + symbol.flags.has_zig_got = true; + try symbol.addExtra(.{ .zig_got = index }, macho_file); + return index; + } + + pub fn entryOffset(zig_got: ZigGotSection, index: Index, macho_file: *MachO) u64 { + _ = zig_got; + const sect = macho_file.sections.items(.header)[macho_file.zig_got_section_index.?]; + return sect.offset + @sizeOf(u64) * index; + } + + pub fn entryAddress(zig_got: ZigGotSection, index: Index, macho_file: *MachO) u64 { + _ = zig_got; + const sect = macho_file.sections.items(.header)[macho_file.zig_got_section_index.?]; + return sect.addr + @sizeOf(u64) * index; + } + + pub fn size(zig_got: ZigGotSection, macho_file: *MachO) usize { + _ = macho_file; + return @sizeOf(u64) * zig_got.entries.items.len; + } + + pub fn writeOne(zig_got: *ZigGotSection, macho_file: *MachO, index: Index) !void { + if (zig_got.dirty) { + const needed_size = zig_got.size(macho_file); + try macho_file.growSection(macho_file.zig_got_section_index.?, needed_size); + zig_got.dirty = false; + } + const off = zig_got.entryOffset(index, macho_file); + const entry = zig_got.entries.items[index]; + const value = macho_file.getSymbol(entry).getAddress(.{ .stubs = false }, macho_file); + + var buf: [8]u8 = undefined; + std.mem.writeInt(u64, &buf, value, .little); + try macho_file.base.file.?.pwriteAll(&buf, off); + } + + pub fn writeAll(zig_got: ZigGotSection, macho_file: *MachO, writer: anytype) !void { + for (zig_got.entries.items) |entry| { + const symbol = macho_file.getSymbol(entry); + const value = symbol.address(.{ .stubs = false }, macho_file); + try writer.writeInt(u64, value, .little); + } + } + + pub fn addDyldRelocs(zig_got: ZigGotSection, macho_file: *MachO) !void { + const tracy = trace(@src()); + defer tracy.end(); + const gpa = macho_file.base.comp.gpa; + const seg_id = macho_file.sections.items(.segment_id)[macho_file.zig_got_sect_index.?]; + const seg = macho_file.segments.items[seg_id]; + + for (0..zig_got.symbols.items.len) |idx| { + const addr = zig_got.entryAddress(@intCast(idx), macho_file); + try macho_file.rebase.entries.append(gpa, .{ + .offset = addr - seg.vmaddr, + .segment_id = seg_id, + }); + } + } + + const FormatCtx = struct { + zig_got: ZigGotSection, + macho_file: *MachO, + }; + + pub fn fmt(zig_got: ZigGotSection, macho_file: *MachO) std.fmt.Formatter(format2) { + return .{ .data = .{ .zig_got = zig_got, .macho_file = macho_file } }; + } + + pub fn format2( + ctx: FormatCtx, + comptime unused_fmt_string: []const u8, + options: std.fmt.FormatOptions, + writer: anytype, + ) !void { + _ = options; + _ = unused_fmt_string; + try writer.writeAll("__zig_got\n"); + for (ctx.zig_got.entries.items, 0..) |entry, index| { + const symbol = ctx.macho_file.getSymbol(entry); + try writer.print(" {d}@0x{x} => {d}@0x{x} ({s})\n", .{ + index, + ctx.zig_got.entryAddress(@intCast(index), ctx.macho_file), + entry, + symbol.getAddress(.{}, ctx.macho_file), + symbol.getName(ctx.macho_file), + }); + } + } +}; + pub const GotSection = struct { symbols: std.ArrayListUnmanaged(Symbol.Index) = .{}, |
