macho: add __zig_got section implementation

This commit is contained in:
Jakub Konka
2024-01-17 20:09:12 +01:00
parent c7de5e5111
commit 8c578ba02c
5 changed files with 464 additions and 24 deletions

View File

@@ -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");

View File

@@ -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();

View File

@@ -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;

View File

@@ -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;

View File

@@ -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) = .{},