authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2024-01-17 20:09:12+01:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2024-01-24 12:34:41+01:00
log8c578ba02ccff63a64093b5acdefcf7b95cc8c46
treeaad7a4e9fc7a29863f1e747151613ba1a8e44081
parentc7de5e511125a738269a802318695b98a88b2791

macho: add __zig_got section implementation


5 files changed, 464 insertions(+), 24 deletions(-)

src/link/MachO.zig+13
......@@ -70,6 +70,7 @@ symtab: std.ArrayListUnmanaged(macho.nlist_64) = .{},
7070strtab: std.ArrayListUnmanaged(u8) = .{},
7171indsymtab: Indsymtab = .{},
7272got: GotSection = .{},
73zig_got: ZigGotSection = .{},
7374stubs: StubsSection = .{},
7475stubs_helper: StubsHelperSection = .{},
7576objc_stubs: ObjcStubsSection = .{},
......@@ -337,6 +338,7 @@ pub fn deinit(self: *MachO) void {
337338 self.symtab.deinit(gpa);
338339 self.strtab.deinit(gpa);
339340 self.got.deinit(gpa);
341 self.zig_got.deinit(gpa);
340342 self.stubs.deinit(gpa);
341343 self.objc_stubs.deinit(gpa);
342344 self.tlv_ptr.deinit(gpa);
......@@ -3157,6 +3159,13 @@ fn initMetadata(self: *MachO, options: InitMetadataOptions) !void {
31573159 }
31583160}
31593161
3162pub fn growSection(self: *MachO, sect_index: u8, size: u64) !void {
3163 _ = self;
3164 _ = sect_index;
3165 _ = size;
3166 @panic("TODO growSection");
3167}
3168
31603169pub fn getTarget(self: MachO) std.Target {
31613170 return self.base.comp.root_mod.resolved_target.result;
31623171}
......@@ -3657,6 +3666,7 @@ fn fmtDumpState(
36573666 try writer.print("stubs\n{}\n", .{self.stubs.fmt(self)});
36583667 try writer.print("objc_stubs\n{}\n", .{self.objc_stubs.fmt(self)});
36593668 try writer.print("got\n{}\n", .{self.got.fmt(self)});
3669 try writer.print("zig_got\n{}\n", .{self.zig_got.fmt(self)});
36603670 try writer.print("tlv_ptr\n{}\n", .{self.tlv_ptr.fmt(self)});
36613671 try writer.writeByte('\n');
36623672 try writer.print("sections\n{}\n", .{self.fmtSections()});
......@@ -3759,6 +3769,8 @@ const Section = struct {
37593769 header: macho.section_64,
37603770 segment_id: u8,
37613771 atoms: std.ArrayListUnmanaged(Atom.Index) = .{},
3772 free_list: std.ArrayListUnmanaged(Atom.Index) = .{},
3773 last_atom_index: Atom.Index = 0,
37623774};
37633775
37643776const HotUpdateState = struct {
......@@ -4125,4 +4137,5 @@ const TlvPtrSection = synthetic.TlvPtrSection;
41254137const TypedValue = @import("../TypedValue.zig");
41264138const UnwindInfo = @import("MachO/UnwindInfo.zig");
41274139const WeakBindSection = synthetic.WeakBindSection;
4140const ZigGotSection = synthetic.ZigGotSection;
41284141const ZigObject = @import("MachO/ZigObject.zig");
src/link/MachO/Atom.zig+153
......@@ -37,6 +37,11 @@ unwind_records: Loc = .{},
3737
3838flags: Flags = .{},
3939
40/// Points to the previous and next neighbors, based on the `text_offset`.
41/// This can be used to find, for example, the capacity of this `TextBlock`.
42prev_index: Index = 0,
43next_index: Index = 0,
44
4045pub fn getName(self: Atom, macho_file: *MachO) [:0]const u8 {
4146 return macho_file.strings.getAssumeExists(self.name);
4247}
......@@ -171,6 +176,154 @@ pub fn initOutputSection(sect: macho.section_64, macho_file: *MachO) !u8 {
171176 return osec;
172177}
173178
179/// Returns how much room there is to grow in virtual address space.
180/// File offset relocation happens transparently, so it is not included in
181/// this calculation.
182pub fn capacity(self: Atom, macho_file: *MachO) u64 {
183 const next_value = if (macho_file.getAtom(self.next_index)) |next| next.value else std.math.maxInt(u32);
184 return next_value - self.value;
185}
186
187pub fn freeListEligible(self: Atom, macho_file: *MachO) bool {
188 // No need to keep a free list node for the last block.
189 const next = macho_file.getAtom(self.next_index) orelse return false;
190 const cap = next.value - self.value;
191 const ideal_cap = MachO.padToIdeal(self.size);
192 if (cap <= ideal_cap) return false;
193 const surplus = cap - ideal_cap;
194 return surplus >= MachO.min_text_capacity;
195}
196
197pub fn allocate(self: *Atom, macho_file: *MachO) !void {
198 const sect = &macho_file.sections.items(.header)[self.out_n_sect];
199 const free_list = &macho_file.sections.items(.free_list)[self.out_n_sect];
200 const last_atom_index = &macho_file.sections.items(.last_atom_index)[self.out_n_sect];
201 const new_atom_ideal_capacity = MachO.padToIdeal(self.size);
202
203 // We use these to indicate our intention to update metadata, placing the new atom,
204 // and possibly removing a free list node.
205 // It would be simpler to do it inside the for loop below, but that would cause a
206 // problem if an error was returned later in the function. So this action
207 // is actually carried out at the end of the function, when errors are no longer possible.
208 var atom_placement: ?Atom.Index = null;
209 var free_list_removal: ?usize = null;
210
211 // First we look for an appropriately sized free list node.
212 // The list is unordered. We'll just take the first thing that works.
213 self.value = blk: {
214 var i: usize = free_list.items.len;
215 while (i < free_list.items.len) {
216 const big_atom_index = free_list.items[i];
217 const big_atom = macho_file.getAtom(big_atom_index).?;
218 // We now have a pointer to a live atom that has too much capacity.
219 // Is it enough that we could fit this new atom?
220 const cap = big_atom.capacity(macho_file);
221 const ideal_capacity = MachO.padToIdeal(cap);
222 const ideal_capacity_end_vaddr = std.math.add(u64, big_atom.value, ideal_capacity) catch ideal_capacity;
223 const capacity_end_vaddr = big_atom.value + cap;
224 const new_start_vaddr_unaligned = capacity_end_vaddr - new_atom_ideal_capacity;
225 const new_start_vaddr = self.alignment.backward(new_start_vaddr_unaligned);
226 if (new_start_vaddr < ideal_capacity_end_vaddr) {
227 // Additional bookkeeping here to notice if this free list node
228 // should be deleted because the block that it points to has grown to take up
229 // more of the extra capacity.
230 if (!big_atom.freeListEligible(macho_file)) {
231 _ = free_list.swapRemove(i);
232 } else {
233 i += 1;
234 }
235 continue;
236 }
237 // At this point we know that we will place the new block here. But the
238 // remaining question is whether there is still yet enough capacity left
239 // over for there to still be a free list node.
240 const remaining_capacity = new_start_vaddr - ideal_capacity_end_vaddr;
241 const keep_free_list_node = remaining_capacity >= MachO.min_text_capacity;
242
243 // Set up the metadata to be updated, after errors are no longer possible.
244 atom_placement = big_atom_index;
245 if (!keep_free_list_node) {
246 free_list_removal = i;
247 }
248 break :blk new_start_vaddr;
249 } else if (macho_file.getAtom(last_atom_index.*)) |last| {
250 const ideal_capacity = MachO.padToIdeal(last.size);
251 const ideal_capacity_end_vaddr = last.value + ideal_capacity;
252 const new_start_vaddr = self.alignment.forward(ideal_capacity_end_vaddr);
253 // Set up the metadata to be updated, after errors are no longer possible.
254 atom_placement = last.atom_index;
255 break :blk new_start_vaddr;
256 } else {
257 break :blk sect.addr;
258 }
259 };
260
261 log.debug("allocated atom({d}) : '{s}' at 0x{x} to 0x{x}", .{
262 self.atom_index,
263 self.getName(macho_file),
264 self.value,
265 self.value + self.size,
266 });
267
268 const expand_section = if (atom_placement) |placement_index|
269 macho_file.getAtom(placement_index).?.next_index == 0
270 else
271 true;
272 if (expand_section) {
273 const needed_size = (self.value + self.size) - sect.addr;
274 try macho_file.growSection(self.out_n_sect, needed_size);
275 last_atom_index.* = self.atom_index;
276
277 // const zig_object = macho_file_file.getZigObject().?;
278 // if (zig_object.dwarf) |_| {
279 // // The .debug_info section has `low_pc` and `high_pc` values which is the virtual address
280 // // range of the compilation unit. When we expand the text section, this range changes,
281 // // so the DW_TAG.compile_unit tag of the .debug_info section becomes dirty.
282 // zig_object.debug_info_header_dirty = true;
283 // // This becomes dirty for the same reason. We could potentially make this more
284 // // fine-grained with the addition of support for more compilation units. It is planned to
285 // // model each package as a different compilation unit.
286 // zig_object.debug_aranges_section_dirty = true;
287 // }
288 }
289 sect.@"align" = @max(sect.@"align", self.alignment.toLog2Units());
290
291 // This function can also reallocate an atom.
292 // In this case we need to "unplug" it from its previous location before
293 // plugging it in to its new location.
294 if (macho_file.getAtom(self.prev_index)) |prev| {
295 prev.next_index = self.next_index;
296 }
297 if (macho_file.getAtom(self.next_index)) |next| {
298 next.prev_index = self.prev_index;
299 }
300
301 if (atom_placement) |big_atom_index| {
302 const big_atom = macho_file.getAtom(big_atom_index).?;
303 self.prev_index = big_atom_index;
304 self.next_index = big_atom.next_index;
305 big_atom.next_index = self.atom_index;
306 } else {
307 self.prev_index = 0;
308 self.next_index = 0;
309 }
310 if (free_list_removal) |i| {
311 _ = free_list.swapRemove(i);
312 }
313
314 self.flags.alive = true;
315}
316
317pub fn shrink(self: *Atom, macho_file: *MachO) void {
318 _ = self;
319 _ = macho_file;
320}
321
322pub fn grow(self: *Atom, macho_file: *MachO) !void {
323 if (!self.alignment.check(self.value) or self.size > self.capacity(macho_file))
324 try self.allocate(macho_file);
325}
326
174327pub fn scanRelocs(self: Atom, macho_file: *MachO) !void {
175328 const tracy = trace(@src());
176329 defer tracy.end();
src/link/MachO/Symbol.zig+22
......@@ -149,6 +149,25 @@ pub fn getTlvPtrAddress(symbol: Symbol, macho_file: *MachO) u64 {
149149 return macho_file.tlv_ptr.getAddress(extra.tlv_ptr, macho_file);
150150}
151151
152const GetOrCreateZigGotEntryResult = struct {
153 found_existing: bool,
154 index: ZigGotSection.Index,
155};
156
157pub fn getOrCreateZigGotEntry(symbol: *Symbol, symbol_index: Index, macho_file: *MachO) !GetOrCreateZigGotEntryResult {
158 assert(!macho_file.base.isRelocatable());
159 assert(symbol.flags.needs_zig_got);
160 if (symbol.flags.has_zig_got) return .{ .found_existing = true, .index = symbol.getExtra(macho_file).?.zig_got };
161 const index = try macho_file.zig_got.addSymbol(symbol_index, macho_file);
162 return .{ .found_existing = false, .index = index };
163}
164
165pub fn zigGotAddress(symbol: Symbol, macho_file: *MachO) u64 {
166 if (!symbol.flags.has_zig_got) return 0;
167 const extras = symbol.getExtra(macho_file).?;
168 return macho_file.zig_got.entryAddress(extras.zig_got, macho_file);
169}
170
152171pub fn getOutputSymtabIndex(symbol: Symbol, macho_file: *MachO) ?u32 {
153172 if (!symbol.flags.output_symtab) return null;
154173 assert(!symbol.isSymbolStab(macho_file));
......@@ -170,6 +189,7 @@ pub fn getOutputSymtabIndex(symbol: Symbol, macho_file: *MachO) ?u32 {
170189
171190const AddExtraOpts = struct {
172191 got: ?u32 = null,
192 zig_got: ?u32 = null,
173193 stubs: ?u32 = null,
174194 objc_stubs: ?u32 = null,
175195 objc_selrefs: ?u32 = null,
......@@ -374,6 +394,7 @@ pub const Visibility = enum {
374394
375395pub const Extra = struct {
376396 got: u32 = 0,
397 zig_got: u32 = 0,
377398 stubs: u32 = 0,
378399 objc_stubs: u32 = 0,
379400 objc_selrefs: u32 = 0,
......@@ -393,3 +414,4 @@ const MachO = @import("../MachO.zig");
393414const Nlist = Object.Nlist;
394415const Object = @import("Object.zig");
395416const Symbol = @This();
417const ZigGotSection = @import("synthetic.zig").ZigGotSection;
src/link/MachO/ZigObject.zig+158-24
......@@ -7,9 +7,36 @@ symtab: std.MultiArrayList(Nlist) = .{},
77symbols: std.ArrayListUnmanaged(Symbol.Index) = .{},
88atoms: std.ArrayListUnmanaged(Atom.Index) = .{},
99
10/// Table of tracked LazySymbols.
11lazy_syms: LazySymbolTable = .{},
12
1013/// Table of tracked Decls.
1114decls: DeclTable = .{},
1215
16/// Table of unnamed constants associated with a parent `Decl`.
17/// We store them here so that we can free the constants whenever the `Decl`
18/// needs updating or is freed.
19///
20/// For example,
21///
22/// ```zig
23/// const Foo = struct{
24/// a: u8,
25/// };
26///
27/// pub fn main() void {
28/// var foo = Foo{ .a = 1 };
29/// _ = foo;
30/// }
31/// ```
32///
33/// value assigned to label `foo` is an unnamed constant belonging/associated
34/// with `Decl` `main`, and lives as long as that `Decl`.
35unnamed_consts: UnnamedConstTable = .{},
36
37/// Table of tracked AnonDecls.
38anon_decls: AnonDeclTable = .{},
39
1340/// A table of relocations.
1441relocs: RelocationTable = .{},
1542
......@@ -35,6 +62,24 @@ pub fn deinit(self: *ZigObject, allocator: Allocator) void {
3562 self.decls.deinit(allocator);
3663 }
3764
65 self.lazy_syms.deinit(allocator);
66
67 {
68 var it = self.unnamed_consts.valueIterator();
69 while (it.next()) |syms| {
70 syms.deinit(allocator);
71 }
72 self.unnamed_consts.deinit(allocator);
73 }
74
75 {
76 var it = self.anon_decls.iterator();
77 while (it.next()) |entry| {
78 entry.value_ptr.exports.deinit(allocator);
79 }
80 self.anon_decls.deinit(allocator);
81 }
82
3883 for (self.relocs.items) |*list| {
3984 list.deinit(allocator);
4085 }
......@@ -296,7 +341,7 @@ pub fn updateDecl(
296341 },
297342 };
298343 const sect_index = try self.getDeclOutputSection(macho_file, decl, code);
299 const is_threadlocal = switch (macho_file.sections.items(.header[sect_index].type())) {
344 const is_threadlocal = switch (macho_file.sections.items(.header)[sect_index].type()) {
300345 macho.S_THREAD_LOCAL_ZEROFILL, macho.S_THREAD_LOCAL_REGULAR => true,
301346 else => false,
302347 };
......@@ -317,21 +362,21 @@ pub fn updateDecl(
317362 // );
318363 // }
319364
320 // // Since we updated the vaddr and the size, each corresponding export symbol also
321 // // needs to be updated.
322 // try self.updateExports(mod, .{ .decl_index = decl_index }, mod.getDeclExports(decl_index));
365 // Since we updated the vaddr and the size, each corresponding export symbol also
366 // needs to be updated.
367 try self.updateExports(macho_file, mod, .{ .decl_index = decl_index }, mod.getDeclExports(decl_index));
323368}
324369
325370fn updateDeclCode(
326371 self: *ZigObject,
327372 macho_file: *MachO,
328 decl_index: Module.Decl.Index,
373 decl_index: InternPool.DeclIndex,
329374 sym_index: Symbol.Index,
330375 sect_index: u8,
331376 code: []const u8,
332377) !void {
333 const gpa = self.base.comp.gpa;
334 const mod = self.base.comp.module.?;
378 const gpa = macho_file.base.comp.gpa;
379 const mod = macho_file.base.comp.module.?;
335380 const decl = mod.declPtr(decl_index);
336381 const decl_name = mod.intern_pool.stringToSlice(try decl.getFullyQualifiedName(mod));
337382
......@@ -342,7 +387,6 @@ fn updateDeclCode(
342387 const sect = &macho_file.sections.items(.header)[sect_index];
343388 const sym = macho_file.getSymbol(sym_index);
344389 const nlist = &self.symtab.items(.nlist)[sym.nlist_idx];
345 const size = &self.symtab.items(.size)[sym.nlist_idx];
346390 const atom = sym.getAtom(macho_file).?;
347391
348392 sym.out_n_sect = sect_index;
......@@ -354,7 +398,7 @@ fn updateDeclCode(
354398 nlist.n_strx = sym.name;
355399 nlist.n_type = macho.N_SECT;
356400 nlist.n_sect = sect_index + 1;
357 size = code.len;
401 self.symtab.items(.size)[sym.nlist_idx] = code.len;
358402
359403 const old_size = atom.size;
360404 const old_vaddr = atom.value;
......@@ -363,14 +407,14 @@ fn updateDeclCode(
363407
364408 if (old_size > 0) {
365409 const capacity = atom.capacity(macho_file);
366 const need_realloc = code.len > capacity or !required_alignment.check(sym.value);
410 const need_realloc = code.len > capacity or !required_alignment.check(sym.getAddress(.{}, macho_file));
367411
368412 if (need_realloc) {
369413 try atom.grow(macho_file);
370414 log.debug("growing {s} from 0x{x} to 0x{x}", .{ decl_name, old_vaddr, atom.value });
371415 if (old_vaddr != atom.value) {
372 sym.value = atom.value;
373 nlist.n_value = atom.value;
416 sym.value = 0;
417 nlist.n_value = 0;
374418
375419 if (!macho_file.base.isRelocatable()) {
376420 log.debug(" (updating offset table entry)", .{});
......@@ -381,17 +425,17 @@ fn updateDeclCode(
381425 }
382426 } else if (code.len < old_size) {
383427 atom.shrink(macho_file);
384 } else if (atom.next_index == null) {
385 const needed_size = (sym.value + code.len) - sect.addr;
428 } else if (macho_file.getAtom(atom.next_index) == null) {
429 const needed_size = (sym.getAddress(.{}, macho_file) + code.len) - sect.addr;
386430 sect.size = needed_size;
387431 }
388432 } else {
389433 try atom.allocate(macho_file);
390434 // TODO: freeDeclMetadata in case of error
391435
392 sym.value = atom.value;
436 sym.value = 0;
393437 sym.flags.needs_zig_got = true;
394 nlist.n_value = atom.value;
438 nlist.n_value = 0;
395439
396440 if (!macho_file.base.isRelocatable()) {
397441 const gop = try sym.getOrCreateZigGotEntry(sym_index, macho_file);
......@@ -400,7 +444,7 @@ fn updateDeclCode(
400444 }
401445
402446 if (!sect.isZerofill()) {
403 const file_offset = sect.offset + sym.value - sect.addr;
447 const file_offset = sect.offset + sym.getAddress(.{}, macho_file) - sect.addr;
404448 try macho_file.base.file.?.pwriteAll(code, file_offset);
405449 }
406450}
......@@ -478,12 +522,91 @@ pub fn updateExports(
478522 exported: Module.Exported,
479523 exports: []const *Module.Export,
480524) link.File.UpdateExportsError!void {
481 _ = self;
482 _ = macho_file;
483 _ = mod;
484 _ = exported;
485 _ = exports;
486 @panic("TODO updateExports");
525 const tracy = trace(@src());
526 defer tracy.end();
527
528 const gpa = macho_file.base.comp.gpa;
529 const metadata = switch (exported) {
530 .decl_index => |decl_index| blk: {
531 _ = try self.getOrCreateMetadataForDecl(macho_file, decl_index);
532 break :blk self.decls.getPtr(decl_index).?;
533 },
534 .value => |value| self.anon_decls.getPtr(value) orelse blk: {
535 const first_exp = exports[0];
536 const res = try self.lowerAnonDecl(macho_file, value, .none, first_exp.getSrcLoc(mod));
537 switch (res) {
538 .ok => {},
539 .fail => |em| {
540 // TODO maybe it's enough to return an error here and let Module.processExportsInner
541 // handle the error?
542 try mod.failed_exports.ensureUnusedCapacity(mod.gpa, 1);
543 mod.failed_exports.putAssumeCapacityNoClobber(first_exp, em);
544 return;
545 },
546 }
547 break :blk self.anon_decls.getPtr(value).?;
548 },
549 };
550 const sym_index = metadata.symbol_index;
551 const nlist_idx = macho_file.getSymbol(sym_index).nlist_idx;
552 const nlist = self.symtab.items(.nlist)[nlist_idx];
553
554 for (exports) |exp| {
555 if (exp.opts.section.unwrap()) |section_name| {
556 if (!mod.intern_pool.stringEqlSlice(section_name, "__text")) {
557 try mod.failed_exports.ensureUnusedCapacity(mod.gpa, 1);
558 mod.failed_exports.putAssumeCapacityNoClobber(exp, try Module.ErrorMsg.create(
559 gpa,
560 exp.getSrcLoc(mod),
561 "Unimplemented: ExportOptions.section",
562 .{},
563 ));
564 continue;
565 }
566 }
567 if (exp.opts.linkage == .LinkOnce) {
568 try mod.failed_exports.putNoClobber(mod.gpa, exp, try Module.ErrorMsg.create(
569 gpa,
570 exp.getSrcLoc(mod),
571 "Unimplemented: GlobalLinkage.LinkOnce",
572 .{},
573 ));
574 continue;
575 }
576
577 const exp_name = try std.fmt.allocPrint(gpa, "_{}", .{exp.opts.name.fmt(&mod.intern_pool)});
578 defer gpa.free(exp_name);
579
580 const name_off = try macho_file.strings.insert(gpa, exp_name);
581 const global_nlist_index = if (metadata.@"export"(self, macho_file, exp_name)) |exp_index|
582 exp_index.*
583 else blk: {
584 const global_nlist_index = try self.getGlobalSymbol(macho_file, exp_name, null);
585 try metadata.exports.append(gpa, global_nlist_index);
586 break :blk global_nlist_index;
587 };
588 const global_nlist = &self.symtab.items(.nlist)[global_nlist_index];
589 global_nlist.n_strx = name_off;
590 global_nlist.n_value = nlist.n_value;
591 global_nlist.n_sect = nlist.n_sect;
592 global_nlist.n_type = macho.N_EXT | macho.N_SECT;
593 self.symtab.items(.size)[global_nlist_index] = self.symtab.items(.size)[nlist_idx];
594 self.symtab.items(.atom)[global_nlist_index] = self.symtab.items(.atom)[nlist_idx];
595
596 switch (exp.opts.linkage) {
597 .Internal => {
598 // Symbol should be hidden, or in MachO lingo, private extern.
599 global_nlist.n_type |= macho.N_PEXT;
600 },
601 .Strong => {},
602 .Weak => {
603 // Weak linkage is specified as part of n_desc field.
604 // Symbol's n_type is like for a symbol with strong linkage.
605 global_nlist.n_desc |= macho.N_WEAK_DEF;
606 },
607 else => unreachable,
608 }
609 }
487610}
488611
489612/// Must be called only after a successful call to `updateDecl`.
......@@ -612,8 +735,19 @@ const DeclMetadata = struct {
612735 return null;
613736 }
614737};
615const DeclTable = std.AutoHashMapUnmanaged(InternPool.DeclIndex, DeclMetadata);
616738
739const LazySymbolMetadata = struct {
740 const State = enum { unused, pending_flush, flushed };
741 text_symbol_index: Symbol.Index = undefined,
742 data_const_symbol_index: Symbol.Index = undefined,
743 text_state: State = .unused,
744 rodata_state: State = .unused,
745};
746
747const DeclTable = std.AutoHashMapUnmanaged(InternPool.DeclIndex, DeclMetadata);
748const UnnamedConstTable = std.AutoHashMapUnmanaged(InternPool.DeclIndex, std.ArrayListUnmanaged(Symbol.Index));
749const AnonDeclTable = std.AutoHashMapUnmanaged(InternPool.Index, DeclMetadata);
750const LazySymbolTable = std.AutoArrayHashMapUnmanaged(InternPool.OptionalDeclIndex, LazySymbolMetadata);
617751const RelocationTable = std.ArrayListUnmanaged(std.ArrayListUnmanaged(Relocation));
618752
619753const assert = std.debug.assert;
src/link/MachO/synthetic.zig+118
......@@ -1,3 +1,121 @@
1pub const ZigGotSection = struct {
2 entries: std.ArrayListUnmanaged(Symbol.Index) = .{},
3 dirty: bool = false,
4
5 pub const Index = u32;
6
7 pub fn deinit(zig_got: *ZigGotSection, allocator: Allocator) void {
8 zig_got.entries.deinit(allocator);
9 }
10
11 fn allocateEntry(zig_got: *ZigGotSection, allocator: Allocator) !Index {
12 try zig_got.entries.ensureUnusedCapacity(allocator, 1);
13 // TODO add free list
14 const index = @as(Index, @intCast(zig_got.entries.items.len));
15 _ = zig_got.entries.addOneAssumeCapacity();
16 zig_got.dirty = true;
17 return index;
18 }
19
20 pub fn addSymbol(zig_got: *ZigGotSection, sym_index: Symbol.Index, macho_file: *MachO) !Index {
21 const comp = macho_file.base.comp;
22 const gpa = comp.gpa;
23 const index = try zig_got.allocateEntry(gpa);
24 const entry = &zig_got.entries.items[index];
25 entry.* = sym_index;
26 const symbol = macho_file.getSymbol(sym_index);
27 symbol.flags.has_zig_got = true;
28 try symbol.addExtra(.{ .zig_got = index }, macho_file);
29 return index;
30 }
31
32 pub fn entryOffset(zig_got: ZigGotSection, index: Index, macho_file: *MachO) u64 {
33 _ = zig_got;
34 const sect = macho_file.sections.items(.header)[macho_file.zig_got_section_index.?];
35 return sect.offset + @sizeOf(u64) * index;
36 }
37
38 pub fn entryAddress(zig_got: ZigGotSection, index: Index, macho_file: *MachO) u64 {
39 _ = zig_got;
40 const sect = macho_file.sections.items(.header)[macho_file.zig_got_section_index.?];
41 return sect.addr + @sizeOf(u64) * index;
42 }
43
44 pub fn size(zig_got: ZigGotSection, macho_file: *MachO) usize {
45 _ = macho_file;
46 return @sizeOf(u64) * zig_got.entries.items.len;
47 }
48
49 pub fn writeOne(zig_got: *ZigGotSection, macho_file: *MachO, index: Index) !void {
50 if (zig_got.dirty) {
51 const needed_size = zig_got.size(macho_file);
52 try macho_file.growSection(macho_file.zig_got_section_index.?, needed_size);
53 zig_got.dirty = false;
54 }
55 const off = zig_got.entryOffset(index, macho_file);
56 const entry = zig_got.entries.items[index];
57 const value = macho_file.getSymbol(entry).getAddress(.{ .stubs = false }, macho_file);
58
59 var buf: [8]u8 = undefined;
60 std.mem.writeInt(u64, &buf, value, .little);
61 try macho_file.base.file.?.pwriteAll(&buf, off);
62 }
63
64 pub fn writeAll(zig_got: ZigGotSection, macho_file: *MachO, writer: anytype) !void {
65 for (zig_got.entries.items) |entry| {
66 const symbol = macho_file.getSymbol(entry);
67 const value = symbol.address(.{ .stubs = false }, macho_file);
68 try writer.writeInt(u64, value, .little);
69 }
70 }
71
72 pub fn addDyldRelocs(zig_got: ZigGotSection, macho_file: *MachO) !void {
73 const tracy = trace(@src());
74 defer tracy.end();
75 const gpa = macho_file.base.comp.gpa;
76 const seg_id = macho_file.sections.items(.segment_id)[macho_file.zig_got_sect_index.?];
77 const seg = macho_file.segments.items[seg_id];
78
79 for (0..zig_got.symbols.items.len) |idx| {
80 const addr = zig_got.entryAddress(@intCast(idx), macho_file);
81 try macho_file.rebase.entries.append(gpa, .{
82 .offset = addr - seg.vmaddr,
83 .segment_id = seg_id,
84 });
85 }
86 }
87
88 const FormatCtx = struct {
89 zig_got: ZigGotSection,
90 macho_file: *MachO,
91 };
92
93 pub fn fmt(zig_got: ZigGotSection, macho_file: *MachO) std.fmt.Formatter(format2) {
94 return .{ .data = .{ .zig_got = zig_got, .macho_file = macho_file } };
95 }
96
97 pub fn format2(
98 ctx: FormatCtx,
99 comptime unused_fmt_string: []const u8,
100 options: std.fmt.FormatOptions,
101 writer: anytype,
102 ) !void {
103 _ = options;
104 _ = unused_fmt_string;
105 try writer.writeAll("__zig_got\n");
106 for (ctx.zig_got.entries.items, 0..) |entry, index| {
107 const symbol = ctx.macho_file.getSymbol(entry);
108 try writer.print(" {d}@0x{x} => {d}@0x{x} ({s})\n", .{
109 index,
110 ctx.zig_got.entryAddress(@intCast(index), ctx.macho_file),
111 entry,
112 symbol.getAddress(.{}, ctx.macho_file),
113 symbol.getName(ctx.macho_file),
114 });
115 }
116 }
117};
118
1119pub const GotSection = struct {
2120 symbols: std.ArrayListUnmanaged(Symbol.Index) = .{},
3121