1data: std.ArrayList(u8) = .empty,
2/// Externally owned memory.
3basename: []const u8,
4index: File.Index,
5
6symtab: std.MultiArrayList(Nlist) = .empty,
7strtab: StringTable = .{},
8
9symbols: std.ArrayList(Symbol) = .empty,
10symbols_extra: std.ArrayList(u32) = .empty,
11globals: std.ArrayList(MachO.SymbolResolver.Index) = .empty,
12/// Maps string index (so name) into nlist index for the global symbol defined within this
13/// module.
14globals_lookup: std.AutoHashMapUnmanaged(u32, u32) = .empty,
15atoms: std.ArrayList(Atom) = .empty,
16atoms_indexes: std.ArrayList(Atom.Index) = .empty,
17atoms_extra: std.ArrayList(u32) = .empty,
18
19/// Table of tracked LazySymbols.
20lazy_syms: LazySymbolTable = .{},
21
22/// Table of tracked Navs.
23navs: NavTable = .{},
24
25/// Table of tracked Uavs.
26uavs: UavTable = .{},
27
28/// TLV initializers indexed by Atom.Index.
29tlv_initializers: TlvInitializerTable = .{},
30
31/// A table of relocations.
32relocs: RelocationTable = .empty,
33
34dwarf: ?Dwarf = null,
35
36output_symtab_ctx: MachO.SymtabCtx = .{},
37output_ar_state: Archive.ArState = .{},
38
39debug_strtab_dirty: bool = false,
40debug_abbrev_dirty: bool = false,
41debug_aranges_dirty: bool = false,
42debug_info_header_dirty: bool = false,
43debug_line_header_dirty: bool = false,
44
45pub fn init(self: *ZigObject, macho_file: *MachO) !void {
46 const tracy = trace(@src());
47 defer tracy.end();
48
49 const comp = macho_file.base.comp;
50 const gpa = comp.gpa;
51
52 try self.atoms.append(gpa, .{ .extra = try self.addAtomExtra(gpa, .{}) }); // null input section
53 try self.strtab.buffer.append(gpa, 0);
54
55 switch (comp.config.debug_format) {
56 .strip => {},
57 .dwarf => |v| {
58 self.dwarf = Dwarf.init(&macho_file.base, v);
59 self.debug_strtab_dirty = true;
60 self.debug_abbrev_dirty = true;
61 self.debug_aranges_dirty = true;
62 self.debug_info_header_dirty = true;
63 self.debug_line_header_dirty = true;
64 },
65 .code_view => unreachable,
66 }
67}
68
69pub fn deinit(self: *ZigObject, allocator: Allocator) void {
70 self.data.deinit(allocator);
71 self.symtab.deinit(allocator);
72 self.strtab.deinit(allocator);
73 self.symbols.deinit(allocator);
74 self.symbols_extra.deinit(allocator);
75 self.globals.deinit(allocator);
76 self.globals_lookup.deinit(allocator);
77 self.atoms.deinit(allocator);
78 self.atoms_indexes.deinit(allocator);
79 self.atoms_extra.deinit(allocator);
80
81 for (self.navs.values()) |*meta| {
82 meta.exports.deinit(allocator);
83 }
84 self.navs.deinit(allocator);
85
86 self.lazy_syms.deinit(allocator);
87
88 for (self.uavs.values()) |*meta| {
89 meta.exports.deinit(allocator);
90 }
91 self.uavs.deinit(allocator);
92
93 for (self.relocs.items) |*list| {
94 list.deinit(allocator);
95 }
96 self.relocs.deinit(allocator);
97
98 for (self.tlv_initializers.values()) |*tlv_init| {
99 tlv_init.deinit(allocator);
100 }
101 self.tlv_initializers.deinit(allocator);
102
103 if (self.dwarf) |*dwarf| {
104 dwarf.deinit();
105 }
106}
107
108fn newSymbol(self: *ZigObject, allocator: Allocator, name: MachO.String, args: struct {
109 type: u8 = macho.N_UNDF | macho.N_EXT,
110 desc: u16 = 0,
111}) !Symbol.Index {
112 try self.symtab.ensureUnusedCapacity(allocator, 1);
113 try self.symbols.ensureUnusedCapacity(allocator, 1);
114 try self.symbols_extra.ensureUnusedCapacity(allocator, @sizeOf(Symbol.Extra));
115 try self.globals.ensureUnusedCapacity(allocator, 1);
116
117 const index = self.addSymbolAssumeCapacity();
118 const symbol = &self.symbols.items[index];
119 symbol.name = name;
120 symbol.extra = self.addSymbolExtraAssumeCapacity(.{});
121
122 const nlist_idx: u32 = @intCast(self.symtab.addOneAssumeCapacity());
123 self.symtab.set(nlist_idx, .{
124 .nlist = .{
125 .n_strx = name.pos,
126 .n_type = @bitCast(args.type),
127 .n_sect = 0,
128 .n_desc = @bitCast(args.desc),
129 .n_value = 0,
130 },
131 .size = 0,
132 .atom = 0,
133 });
134 symbol.nlist_idx = nlist_idx;
135
136 self.globals.appendAssumeCapacity(0);
137
138 return index;
139}
140
141fn newAtom(self: *ZigObject, allocator: Allocator, name: MachO.String, macho_file: *MachO) !Atom.Index {
142 try self.atoms.ensureUnusedCapacity(allocator, 1);
143 try self.atoms_extra.ensureUnusedCapacity(allocator, @sizeOf(Atom.Extra));
144 try self.atoms_indexes.ensureUnusedCapacity(allocator, 1);
145 try self.relocs.ensureUnusedCapacity(allocator, 1);
146
147 const index = self.addAtomAssumeCapacity();
148 self.atoms_indexes.appendAssumeCapacity(index);
149 const atom = self.getAtom(index).?;
150 atom.name = name;
151
152 const relocs_index = @as(u32, @intCast(self.relocs.items.len));
153 self.relocs.addOneAssumeCapacity().* = .empty;
154 atom.addExtra(.{ .rel_index = relocs_index, .rel_count = 0 }, macho_file);
155
156 return index;
157}
158
159fn newSymbolWithAtom(self: *ZigObject, allocator: Allocator, name: MachO.String, macho_file: *MachO) !Symbol.Index {
160 const atom_index = try self.newAtom(allocator, name, macho_file);
161 const sym_index = try self.newSymbol(allocator, name, .{ .type = macho.N_SECT });
162 const sym = &self.symbols.items[sym_index];
163 sym.atom_ref = .{ .index = atom_index, .file = self.index };
164 self.symtab.items(.atom)[sym.nlist_idx] = atom_index;
165 return sym_index;
166}
167
168pub fn getAtomData(self: ZigObject, macho_file: *MachO, atom: Atom, buffer: []u8) !void {
169 assert(atom.file == self.index);
170 assert(atom.size == buffer.len);
171 const isec = atom.getInputSection(macho_file);
172 assert(!isec.isZerofill());
173
174 const comp = macho_file.base.comp;
175 const io = comp.io;
176
177 switch (isec.type()) {
178 macho.S_THREAD_LOCAL_REGULAR => {
179 const tlv = self.tlv_initializers.get(atom.atom_index).?;
180 @memcpy(buffer, tlv.data);
181 },
182 macho.S_THREAD_LOCAL_VARIABLES => {
183 @memset(buffer, 0);
184 },
185 else => {
186 const sect = macho_file.sections.items(.header)[atom.out_n_sect];
187 const file_offset = sect.offset + atom.value;
188 const amt = try macho_file.base.file.?.readPositionalAll(io, buffer, file_offset);
189 if (amt != buffer.len) return error.InputOutput;
190 },
191 }
192}
193
194pub fn getAtomRelocs(self: *ZigObject, atom: Atom, macho_file: *MachO) []const Relocation {
195 const extra = atom.getExtra(macho_file);
196 const relocs = self.relocs.items[extra.rel_index];
197 return relocs.items[0..extra.rel_count];
198}
199
200pub fn freeAtomRelocs(self: *ZigObject, atom: Atom, macho_file: *MachO) void {
201 const extra = atom.getExtra(macho_file);
202 self.relocs.items[extra.rel_index].clearRetainingCapacity();
203}
204
205pub fn resolveSymbols(self: *ZigObject, macho_file: *MachO) !void {
206 const tracy = trace(@src());
207 defer tracy.end();
208
209 const gpa = macho_file.base.comp.gpa;
210
211 for (self.symtab.items(.nlist), self.symtab.items(.atom), self.globals.items, 0..) |nlist, atom_index, *global, i| {
212 if (!nlist.n_type.bits.ext) continue;
213 if (nlist.n_type.bits.type == .sect) {
214 const atom = self.getAtom(atom_index).?;
215 if (!atom.isAlive()) continue;
216 }
217
218 const gop = try macho_file.resolver.getOrPut(gpa, .{
219 .index = @intCast(i),
220 .file = self.index,
221 }, macho_file);
222 if (!gop.found_existing) {
223 gop.ref.* = .{ .index = 0, .file = 0 };
224 }
225 global.* = gop.index;
226
227 if (nlist.n_type.bits.type == .undf and !nlist.tentative()) continue;
228 if (gop.ref.getFile(macho_file) == null) {
229 gop.ref.* = .{ .index = @intCast(i), .file = self.index };
230 continue;
231 }
232
233 if (self.asFile().getSymbolRank(.{
234 .archive = false,
235 .weak = nlist.n_desc.weak_def_or_ref_to_weak,
236 .tentative = nlist.tentative(),
237 }) < gop.ref.getSymbol(macho_file).?.getSymbolRank(macho_file)) {
238 gop.ref.* = .{ .index = @intCast(i), .file = self.index };
239 }
240 }
241}
242
243pub fn markLive(self: *ZigObject, macho_file: *MachO) void {
244 const tracy = trace(@src());
245 defer tracy.end();
246
247 for (0..self.symbols.items.len) |i| {
248 const nlist = self.symtab.items(.nlist)[i];
249 if (!nlist.n_type.bits.ext) continue;
250
251 const ref = self.getSymbolRef(@intCast(i), macho_file);
252 const file = ref.getFile(macho_file) orelse continue;
253 const sym = ref.getSymbol(macho_file).?;
254 const should_keep = nlist.n_type.bits.type == .undf or (nlist.tentative() and !sym.flags.tentative);
255 if (should_keep and file == .object and !file.object.alive) {
256 file.object.alive = true;
257 file.object.markLive(macho_file);
258 }
259 }
260}
261
262pub fn mergeSymbolVisibility(self: *ZigObject, macho_file: *MachO) void {
263 const tracy = trace(@src());
264 defer tracy.end();
265
266 for (self.symbols.items, 0..) |sym, i| {
267 const ref = self.getSymbolRef(@intCast(i), macho_file);
268 const global = ref.getSymbol(macho_file) orelse continue;
269 if (sym.visibility.rank() < global.visibility.rank()) {
270 global.visibility = sym.visibility;
271 }
272 if (sym.flags.weak_ref) {
273 global.flags.weak_ref = true;
274 }
275 }
276}
277
278pub fn resolveLiterals(self: *ZigObject, lp: *MachO.LiteralPool, macho_file: *MachO) !void {
279 _ = self;
280 _ = lp;
281 _ = macho_file;
282 // TODO
283}
284
285pub fn dedupLiterals(self: *ZigObject, lp: MachO.LiteralPool, macho_file: *MachO) void {
286 _ = self;
287 _ = lp;
288 _ = macho_file;
289 // TODO
290}
291
292/// This is just a temporary helper function that allows us to re-read what we wrote to file into a buffer.
293/// We need this so that we can write to an archive.
294/// TODO implement writing ZigObject data directly to a buffer instead.
295pub fn readFileContents(self: *ZigObject, macho_file: *MachO) !void {
296 const comp = macho_file.base.comp;
297 const gpa = comp.gpa;
298 const io = comp.io;
299 const diags = &comp.link_diags;
300 // Size of the output object file is always the offset + size of the strtab
301 const size = macho_file.symtab_cmd.stroff + macho_file.symtab_cmd.strsize;
302 try self.data.resize(gpa, size);
303 const amt = macho_file.base.file.?.readPositionalAll(io, self.data.items, 0) catch |err|
304 return diags.fail("failed to read output file: {s}", .{@errorName(err)});
305 if (amt != size)
306 return diags.fail("unexpected EOF reading from output file", .{});
307}
308
309pub fn updateArSymtab(self: ZigObject, ar_symtab: *Archive.ArSymtab, macho_file: *MachO) error{OutOfMemory}!void {
310 const gpa = macho_file.base.comp.gpa;
311 for (self.symbols.items, 0..) |sym, i| {
312 const ref = self.getSymbolRef(@intCast(i), macho_file);
313 const file = ref.getFile(macho_file).?;
314 assert(file.getIndex() == self.index);
315 if (!sym.flags.@"export") continue;
316 const off = try ar_symtab.strtab.insert(gpa, sym.getName(macho_file));
317 try ar_symtab.entries.append(gpa, .{ .off = off, .file = self.index });
318 }
319}
320
321pub fn updateArSize(self: *ZigObject) void {
322 self.output_ar_state.size = self.data.items.len;
323}
324
325pub fn writeAr(self: ZigObject, writer: anytype) !void {
326 // Header
327 const size = std.math.cast(usize, self.output_ar_state.size) orelse return error.Overflow;
328 try Archive.writeHeader(self.basename, size, writer);
329 // Data
330 try writer.writeAll(self.data.items);
331}
332
333pub fn claimUnresolved(self: *ZigObject, macho_file: *MachO) void {
334 const tracy = trace(@src());
335 defer tracy.end();
336
337 for (self.symbols.items, 0..) |*sym, i| {
338 const nlist = self.symtab.items(.nlist)[i];
339 if (!nlist.n_type.bits.ext) continue;
340 if (nlist.n_type.bits.type != .undf) continue;
341
342 if (self.getSymbolRef(@intCast(i), macho_file).getFile(macho_file) != null) continue;
343
344 const is_import = switch (macho_file.undefined_treatment) {
345 .@"error" => false,
346 .warn, .suppress => nlist.weakRef(),
347 .dynamic_lookup => true,
348 };
349 if (is_import) {
350 sym.value = 0;
351 sym.atom_ref = .{ .index = 0, .file = 0 };
352 sym.flags.weak = false;
353 sym.flags.weak_ref = nlist.weakRef();
354 sym.flags.import = is_import;
355 sym.visibility = .global;
356
357 const idx = self.globals.items[i];
358 macho_file.resolver.values.items[idx - 1] = .{ .index = @intCast(i), .file = self.index };
359 }
360 }
361}
362
363pub fn scanRelocs(self: *ZigObject, macho_file: *MachO) !void {
364 for (self.getAtoms()) |atom_index| {
365 const atom = self.getAtom(atom_index) orelse continue;
366 if (!atom.isAlive()) continue;
367 const sect = atom.getInputSection(macho_file);
368 if (sect.isZerofill()) continue;
369 try atom.scanRelocs(macho_file);
370 }
371}
372
373pub fn resolveRelocs(self: *ZigObject, macho_file: *MachO) !void {
374 const gpa = macho_file.base.comp.gpa;
375 const diags = &macho_file.base.comp.link_diags;
376
377 var has_error = false;
378 for (self.getAtoms()) |atom_index| {
379 const atom = self.getAtom(atom_index) orelse continue;
380 if (!atom.isAlive()) continue;
381 const sect = &macho_file.sections.items(.header)[atom.out_n_sect];
382 if (sect.isZerofill()) continue;
383 if (!macho_file.isZigSection(atom.out_n_sect)) continue; // Non-Zig sections are handled separately
384 if (atom.getRelocs(macho_file).len == 0) continue;
385 // TODO: we will resolve and write ZigObject's TLS data twice:
386 // once here, and once in writeAtoms
387 const atom_size = try macho_file.cast(usize, atom.size);
388 const code = try gpa.alloc(u8, atom_size);
389 defer gpa.free(code);
390 self.getAtomData(macho_file, atom.*, code) catch |err| {
391 switch (err) {
392 error.InputOutput => return diags.fail("fetching code for '{s}' failed", .{
393 atom.getName(macho_file),
394 }),
395 else => |e| return diags.fail("failed to fetch code for '{s}': {s}", .{
396 atom.getName(macho_file), @errorName(e),
397 }),
398 }
399 has_error = true;
400 continue;
401 };
402 const file_offset = sect.offset + atom.value;
403 atom.resolveRelocs(macho_file, code) catch |err| {
404 switch (err) {
405 error.ResolveFailed => {},
406 else => |e| return diags.fail("failed to resolve relocations: {s}", .{@errorName(e)}),
407 }
408 has_error = true;
409 continue;
410 };
411 try macho_file.pwriteAll(code, file_offset);
412 }
413
414 if (has_error) return error.ResolveFailed;
415}
416
417pub fn calcNumRelocs(self: *ZigObject, macho_file: *MachO) void {
418 for (self.getAtoms()) |atom_index| {
419 const atom = self.getAtom(atom_index) orelse continue;
420 if (!atom.isAlive()) continue;
421 const header = &macho_file.sections.items(.header)[atom.out_n_sect];
422 if (header.isZerofill()) continue;
423 if (!macho_file.isZigSection(atom.out_n_sect) and !macho_file.isDebugSection(atom.out_n_sect)) continue;
424 const nreloc = atom.calcNumRelocs(macho_file);
425 atom.addExtra(.{ .rel_out_index = header.nreloc, .rel_out_count = nreloc }, macho_file);
426 header.nreloc += nreloc;
427 }
428}
429
430pub fn writeRelocs(self: *ZigObject, macho_file: *MachO) error{ AlreadyReported, OutOfMemory }!void {
431 const gpa = macho_file.base.comp.gpa;
432 const diags = &macho_file.base.comp.link_diags;
433
434 for (self.getAtoms()) |atom_index| {
435 const atom = self.getAtom(atom_index) orelse continue;
436 if (!atom.isAlive()) continue;
437 const header = macho_file.sections.items(.header)[atom.out_n_sect];
438 const relocs = macho_file.sections.items(.relocs)[atom.out_n_sect].items;
439 if (header.isZerofill()) continue;
440 if (!macho_file.isZigSection(atom.out_n_sect) and !macho_file.isDebugSection(atom.out_n_sect)) continue;
441 if (atom.getRelocs(macho_file).len == 0) continue;
442 const extra = atom.getExtra(macho_file);
443 const atom_size = try macho_file.cast(usize, atom.size);
444 const code = try gpa.alloc(u8, atom_size);
445 defer gpa.free(code);
446 self.getAtomData(macho_file, atom.*, code) catch |err|
447 return diags.fail("failed to fetch code for '{s}': {s}", .{ atom.getName(macho_file), @errorName(err) });
448 const file_offset = header.offset + atom.value;
449 try atom.writeRelocs(macho_file, code, relocs[extra.rel_out_index..][0..extra.rel_out_count]);
450 try macho_file.pwriteAll(code, file_offset);
451 }
452}
453
454// TODO we need this because not everything gets written out incrementally.
455// For example, TLS data gets written out via traditional route.
456// Is there any better way of handling this?
457pub fn writeAtomsRelocatable(self: *ZigObject, macho_file: *MachO) !void {
458 const tracy = trace(@src());
459 defer tracy.end();
460
461 for (self.getAtoms()) |atom_index| {
462 const atom = self.getAtom(atom_index) orelse continue;
463 if (!atom.isAlive()) continue;
464 const sect = atom.getInputSection(macho_file);
465 if (sect.isZerofill()) continue;
466 if (macho_file.isZigSection(atom.out_n_sect)) continue;
467 if (atom.getRelocs(macho_file).len == 0) continue;
468 const off = try macho_file.cast(usize, atom.value);
469 const size = try macho_file.cast(usize, atom.size);
470 const buffer = macho_file.sections.items(.out)[atom.out_n_sect].items;
471 try self.getAtomData(macho_file, atom.*, buffer[off..][0..size]);
472 const relocs = macho_file.sections.items(.relocs)[atom.out_n_sect].items;
473 const extra = atom.getExtra(macho_file);
474 try atom.writeRelocs(macho_file, buffer[off..][0..size], relocs[extra.rel_out_index..][0..extra.rel_out_count]);
475 }
476}
477
478// TODO we need this because not everything gets written out incrementally.
479// For example, TLS data gets written out via traditional route.
480// Is there any better way of handling this?
481pub fn writeAtoms(self: *ZigObject, macho_file: *MachO) !void {
482 const tracy = trace(@src());
483 defer tracy.end();
484
485 for (self.getAtoms()) |atom_index| {
486 const atom = self.getAtom(atom_index) orelse continue;
487 if (!atom.isAlive()) continue;
488 const sect = atom.getInputSection(macho_file);
489 if (sect.isZerofill()) continue;
490 if (macho_file.isZigSection(atom.out_n_sect)) continue;
491 const off = try macho_file.cast(usize, atom.value);
492 const size = try macho_file.cast(usize, atom.size);
493 const buffer = macho_file.sections.items(.out)[atom.out_n_sect].items;
494 try self.getAtomData(macho_file, atom.*, buffer[off..][0..size]);
495 try atom.resolveRelocs(macho_file, buffer[off..][0..size]);
496 }
497}
498
499pub fn calcSymtabSize(self: *ZigObject, macho_file: *MachO) void {
500 const tracy = trace(@src());
501 defer tracy.end();
502
503 for (self.symbols.items, 0..) |*sym, i| {
504 const ref = self.getSymbolRef(@intCast(i), macho_file);
505 const file = ref.getFile(macho_file) orelse continue;
506 if (file.getIndex() != self.index) continue;
507 if (sym.getAtom(macho_file)) |atom| if (!atom.isAlive()) continue;
508 if (macho_file.discard_local_symbols and sym.isLocal()) continue;
509 const name = sym.getName(macho_file);
510 assert(name.len > 0);
511 sym.flags.output_symtab = true;
512 if (sym.isLocal()) {
513 sym.addExtra(.{ .symtab = self.output_symtab_ctx.nlocals }, macho_file);
514 self.output_symtab_ctx.nlocals += 1;
515 } else if (sym.flags.@"export") {
516 sym.addExtra(.{ .symtab = self.output_symtab_ctx.nexports }, macho_file);
517 self.output_symtab_ctx.nexports += 1;
518 } else {
519 assert(sym.flags.import);
520 sym.addExtra(.{ .symtab = self.output_symtab_ctx.nimports }, macho_file);
521 self.output_symtab_ctx.nimports += 1;
522 }
523 self.output_symtab_ctx.strsize += @as(u32, @intCast(name.len + 1));
524 }
525}
526
527pub fn writeSymtab(self: ZigObject, macho_file: *MachO, ctx: anytype) void {
528 const tracy = trace(@src());
529 defer tracy.end();
530
531 var n_strx = self.output_symtab_ctx.stroff;
532 for (self.symbols.items, 0..) |sym, i| {
533 const ref = self.getSymbolRef(@intCast(i), macho_file);
534 const file = ref.getFile(macho_file) orelse continue;
535 if (file.getIndex() != self.index) continue;
536 const idx = sym.getOutputSymtabIndex(macho_file) orelse continue;
537 const out_sym = &ctx.symtab.items[idx];
538 out_sym.n_strx = n_strx;
539 sym.setOutputSym(macho_file, out_sym);
540 const name = sym.getName(macho_file);
541 @memcpy(ctx.strtab.items[n_strx..][0..name.len], name);
542 n_strx += @intCast(name.len);
543 ctx.strtab.items[n_strx] = 0;
544 n_strx += 1;
545 }
546}
547
548pub fn getInputSection(self: ZigObject, atom: Atom, macho_file: *MachO) macho.section_64 {
549 _ = self;
550 var sect = macho_file.sections.items(.header)[atom.out_n_sect];
551 sect.addr = 0;
552 sect.offset = 0;
553 sect.size = atom.size;
554 sect.@"align" = atom.alignment.toLog2Units();
555 return sect;
556}
557
558pub fn flush(self: *ZigObject, macho_file: *MachO, tid: Zcu.PerThread.Id) link.Error!void {
559 const diags = &macho_file.base.comp.link_diags;
560
561 // Handle any lazy symbols that were emitted by incremental compilation.
562 if (self.lazy_syms.getPtr(.anyerror_type)) |metadata| {
563 const active = macho_file.base.comp.zcu.?.activate(tid);
564 defer active.deactivate();
565
566 // Most lazy symbols can be updated on first use, but
567 // anyerror needs to wait for everything to be flushed.
568 if (metadata.text_state != .unused) self.updateLazySymbol(
569 macho_file,
570 active.pt,
571 .{ .kind = .code, .ty = .anyerror_type },
572 metadata.text_symbol_index,
573 ) catch |err| switch (err) {
574 error.OutOfMemory, error.AlreadyReported => |e| return e,
575 else => |e| return diags.fail("failed to update lazy symbol: {s}", .{@errorName(e)}),
576 };
577 if (metadata.const_state != .unused) self.updateLazySymbol(
578 macho_file,
579 active.pt,
580 .{ .kind = .const_data, .ty = .anyerror_type },
581 metadata.const_symbol_index,
582 ) catch |err| switch (err) {
583 error.OutOfMemory, error.AlreadyReported => |e| return e,
584 else => |e| return diags.fail("failed to update lazy symbol: {s}", .{@errorName(e)}),
585 };
586 }
587 for (self.lazy_syms.values()) |*metadata| {
588 if (metadata.text_state != .unused) metadata.text_state = .flushed;
589 if (metadata.const_state != .unused) metadata.const_state = .flushed;
590 }
591
592 if (self.dwarf) |*dwarf| {
593 const active = macho_file.base.comp.zcu.?.activate(tid);
594 defer active.deactivate();
595 dwarf.flush(active.pt) catch |err| switch (err) {
596 error.OutOfMemory => |e| return e,
597 else => |e| return diags.fail("failed to flush dwarf module: {s}", .{@errorName(e)}),
598 };
599
600 self.debug_abbrev_dirty = false;
601 self.debug_aranges_dirty = false;
602 self.debug_strtab_dirty = false;
603 }
604
605 // The point of flush() is to commit changes, so in theory, nothing should
606 // be dirty after this. However, it is possible for some things to remain
607 // dirty because they fail to be written in the event of compile errors,
608 // such as debug_line_header_dirty and debug_info_header_dirty.
609 assert(!self.debug_abbrev_dirty);
610 assert(!self.debug_aranges_dirty);
611 assert(!self.debug_strtab_dirty);
612}
613
614pub fn getNavVAddr(
615 self: *ZigObject,
616 macho_file: *MachO,
617 pt: Zcu.PerThread,
618 nav_index: InternPool.Nav.Index,
619 reloc_info: link.File.RelocInfo,
620) !u64 {
621 const zcu = pt.zcu;
622 const ip = &zcu.intern_pool;
623 const nav = ip.getNav(nav_index);
624 log.debug("getNavVAddr {f}({d})", .{ nav.fqn.fmt(ip), nav_index });
625 const sym_index = if (nav.getExtern(ip)) |@"extern"| try self.getGlobalSymbol(
626 macho_file,
627 nav.name.toSlice(ip),
628 @"extern".lib_name.toSlice(ip),
629 ) else try self.getOrCreateMetadataForNav(macho_file, nav_index);
630 const sym = self.symbols.items[sym_index];
631 const vaddr = sym.getAddress(.{}, macho_file);
632 switch (reloc_info.parent) {
633 .none => unreachable,
634 .atom_index => |atom_index| {
635 const parent_atom = self.symbols.items[@backingInt(atom_index)].getAtom(macho_file).?;
636 try parent_atom.addReloc(macho_file, .{
637 .tag = .@"extern",
638 .offset = @intCast(reloc_info.offset),
639 .target = sym_index,
640 .addend = reloc_info.addend,
641 .type = .unsigned,
642 .meta = .{
643 .pcrel = false,
644 .has_subtractor = false,
645 .length = 3,
646 .symbolnum = @intCast(sym.nlist_idx),
647 },
648 });
649 },
650 .debug_output => |debug_output| try debug_output.dwarf.infoExternalReloc(.{
651 .source_off = @intCast(reloc_info.offset),
652 .target_sym = @fromBackingInt(@intCast(sym_index)),
653 .target_off = reloc_info.addend,
654 }),
655 }
656 return vaddr;
657}
658
659pub fn getUavVAddr(
660 self: *ZigObject,
661 macho_file: *MachO,
662 uav: InternPool.Index,
663 reloc_info: link.File.RelocInfo,
664) !u64 {
665 const sym_index = self.uavs.get(uav).?.symbol_index;
666 const sym = self.symbols.items[sym_index];
667 const vaddr = sym.getAddress(.{}, macho_file);
668 switch (reloc_info.parent) {
669 .none => unreachable,
670 .atom_index => |atom_index| {
671 const parent_atom = self.symbols.items[@backingInt(atom_index)].getAtom(macho_file).?;
672 try parent_atom.addReloc(macho_file, .{
673 .tag = .@"extern",
674 .offset = @intCast(reloc_info.offset),
675 .target = sym_index,
676 .addend = reloc_info.addend,
677 .type = .unsigned,
678 .meta = .{
679 .pcrel = false,
680 .has_subtractor = false,
681 .length = 3,
682 .symbolnum = @intCast(sym.nlist_idx),
683 },
684 });
685 },
686 .debug_output => |debug_output| try debug_output.dwarf.infoExternalReloc(.{
687 .source_off = @intCast(reloc_info.offset),
688 .target_sym = @fromBackingInt(@intCast(sym_index)),
689 .target_off = reloc_info.addend,
690 }),
691 }
692 return vaddr;
693}
694
695pub fn lowerUav(
696 self: *ZigObject,
697 macho_file: *MachO,
698 pt: Zcu.PerThread,
699 uav: InternPool.Index,
700 explicit_alignment: Atom.Alignment,
701) !link.File.SymbolId {
702 const zcu = pt.zcu;
703 const gpa = zcu.gpa;
704 const val = Value.fromInterned(uav);
705 const uav_alignment = switch (explicit_alignment) {
706 .none => val.typeOf(zcu).abiAlignment(zcu),
707 else => explicit_alignment,
708 };
709 if (self.uavs.get(uav)) |metadata| {
710 const sym = self.symbols.items[metadata.symbol_index];
711 const existing_alignment = sym.getAtom(macho_file).?.alignment;
712 if (uav_alignment.order(existing_alignment).compare(.lte))
713 return @fromBackingInt(@intCast(metadata.symbol_index));
714 }
715
716 var name_buf: [32]u8 = undefined;
717 const name = std.mem.print(&name_buf, "__anon_{d}", .{
718 @backingInt(uav),
719 }) catch unreachable;
720 const sym_index = self.lowerConst(
721 macho_file,
722 pt,
723 name,
724 val,
725 uav_alignment,
726 macho_file.zig_const_sect_index.?,
727 ) catch |err| switch (err) {
728 error.OutOfMemory => |e| return e,
729 else => |e| return macho_file.base.comp.link_diags.fail(
730 "failed to lower constant value: {t}",
731 .{e},
732 ),
733 };
734 try self.uavs.put(gpa, uav, .{ .symbol_index = @backingInt(sym_index) });
735 return sym_index;
736}
737
738fn freeNavMetadata(self: *ZigObject, macho_file: *MachO, sym_index: Symbol.Index) void {
739 const sym = self.symbols.items[sym_index];
740 sym.getAtom(macho_file).?.free(macho_file);
741 log.debug("adding %{d} to local symbols free list", .{sym_index});
742 // TODO redo this
743 // TODO free GOT entry here
744}
745
746pub fn freeNav(self: *ZigObject, macho_file: *MachO, nav_index: InternPool.Nav.Index) void {
747 const gpa = macho_file.base.comp.gpa;
748 log.debug("freeNav 0x{x}", .{nav_index});
749
750 if (self.navs.fetchRemove(nav_index)) |const_kv| {
751 var kv = const_kv;
752 const sym_index = kv.value.symbol_index;
753 self.freeNavMetadata(macho_file, sym_index);
754 kv.value.exports.deinit(gpa);
755 }
756
757 // TODO free decl in dSYM
758}
759
760pub fn updateFunc(
761 self: *ZigObject,
762 macho_file: *MachO,
763 pt: Zcu.PerThread,
764 func_index: InternPool.Index,
765 mir: *const codegen.AnyMir,
766) link.Error!void {
767 const tracy = trace(@src());
768 defer tracy.end();
769
770 const zcu = pt.zcu;
771 const gpa = zcu.gpa;
772 const func = zcu.funcInfo(func_index);
773
774 const sym_index = try self.getOrCreateMetadataForNav(macho_file, func.owner_nav);
775 self.symbols.items[sym_index].getAtom(macho_file).?.freeRelocs(macho_file);
776
777 var aw: std.Io.Writer.Allocating = .init(gpa);
778 defer aw.deinit();
779
780 var debug_wip_nav = if (self.dwarf) |*dwarf| try dwarf.initWipNav(pt, func.owner_nav, @fromBackingInt(@intCast(sym_index))) else null;
781 defer if (debug_wip_nav) |*wip_nav| wip_nav.deinit();
782
783 codegen.emitFunction(
784 &macho_file.base,
785 pt,
786 func_index,
787 @fromBackingInt(@intCast(sym_index)),
788 mir,
789 &aw.writer,
790 if (debug_wip_nav) |*wip_nav| .{ .dwarf = wip_nav } else .none,
791 ) catch |err| switch (err) {
792 error.WriteFailed => return error.OutOfMemory,
793 else => |e| return e,
794 };
795 const code = aw.written();
796
797 const sect_index = try self.getNavOutputSection(macho_file, zcu, func.owner_nav, code);
798 const old_rva, const old_alignment = blk: {
799 const atom = self.symbols.items[sym_index].getAtom(macho_file).?;
800 break :blk .{ atom.value, atom.alignment };
801 };
802 try self.updateNavCode(macho_file, pt, func.owner_nav, sym_index, sect_index, code);
803 const new_rva, const new_alignment = blk: {
804 const atom = self.symbols.items[sym_index].getAtom(macho_file).?;
805 break :blk .{ atom.value, atom.alignment };
806 };
807
808 if (debug_wip_nav) |*wip_nav| self.dwarf.?.finishWipNavFunc(pt, func.owner_nav, code.len, wip_nav) catch |err|
809 return macho_file.base.cgFail(func.owner_nav, "falied to finish dwarf function: {s}", .{@errorName(err)});
810
811 // Exports will be updated by `Zcu.processExports` after the update.
812 if (old_rva != new_rva and old_rva > 0) {
813 // If we had to reallocate the function, we re-use the existing slot for a trampoline.
814 // In the rare case that the function has been further overaligned we skip creating a
815 // trampoline and update all symbols referring this function.
816 if (old_alignment.order(new_alignment) == .lt) {
817 @panic("TODO update all symbols referring this function");
818 }
819
820 // Create a trampoline to the new location at `old_rva`.
821 if (!self.symbols.items[sym_index].flags.trampoline) {
822 const name = try std.fmt.allocPrint(gpa, "{s}$trampoline", .{
823 self.symbols.items[sym_index].getName(macho_file),
824 });
825 defer gpa.free(name);
826 const name_off = try self.addString(gpa, name);
827 const tr_size = trampolineSize(macho_file.getTarget().cpu.arch);
828 const tr_sym_index = try self.newSymbolWithAtom(gpa, name_off, macho_file);
829 const tr_sym = &self.symbols.items[tr_sym_index];
830 tr_sym.out_n_sect = macho_file.zig_text_sect_index.?;
831 const tr_nlist = &self.symtab.items(.nlist)[tr_sym.nlist_idx];
832 tr_nlist.n_sect = macho_file.zig_text_sect_index.? + 1;
833 const tr_atom = tr_sym.getAtom(macho_file).?;
834 tr_atom.value = old_rva;
835 tr_atom.setAlive(true);
836 tr_atom.alignment = old_alignment;
837 tr_atom.out_n_sect = macho_file.zig_text_sect_index.?;
838 tr_atom.size = tr_size;
839 self.symtab.items(.size)[tr_sym.nlist_idx] = tr_size;
840 const target_sym = &self.symbols.items[sym_index];
841 target_sym.addExtra(.{ .trampoline = tr_sym_index }, macho_file);
842 target_sym.flags.trampoline = true;
843 }
844 const target_sym = self.symbols.items[sym_index];
845 const source_sym = self.symbols.items[target_sym.getExtra(macho_file).trampoline];
846 writeTrampoline(source_sym, target_sym, macho_file) catch |err|
847 return macho_file.base.cgFail(func.owner_nav, "failed to write trampoline: {s}", .{@errorName(err)});
848 }
849}
850
851pub fn updateNav(
852 self: *ZigObject,
853 macho_file: *MachO,
854 pt: Zcu.PerThread,
855 nav_index: InternPool.Nav.Index,
856) link.Error!void {
857 const tracy = trace(@src());
858 defer tracy.end();
859
860 const zcu = pt.zcu;
861 const ip = &zcu.intern_pool;
862 const nav = ip.getNav(nav_index);
863
864 switch (ip.indexToKey(nav.resolved.?.value)) {
865 else => {},
866 .@"extern" => |@"extern"| {
867 // Extern variable gets a __got entry only
868 const name = @"extern".name.toSlice(ip);
869 const lib_name = @"extern".lib_name.toSlice(ip);
870 const sym_index = try self.getGlobalSymbol(macho_file, name, lib_name);
871 if (nav.resolved.?.@"threadlocal" and macho_file.base.comp.config.any_non_single_threaded) self.symbols.items[sym_index].flags.tlv = true;
872 if (self.dwarf) |*dwarf| {
873 var debug_wip_nav = try dwarf.initWipNav(pt, nav_index, @fromBackingInt(@intCast(sym_index)));
874 defer debug_wip_nav.deinit();
875 dwarf.finishWipNav(pt, nav_index, &debug_wip_nav) catch |err| switch (err) {
876 error.OutOfMemory, error.Canceled, error.AlreadyReported => |e| return e,
877 else => |e| return macho_file.base.cgFail(nav_index, "failed to finish dwarf nav: {s}", .{@errorName(e)}),
878 };
879 }
880 return;
881 },
882 }
883
884 if (Type.fromInterned(nav.resolved.?.type).hasRuntimeBits(zcu)) {
885 const sym_index = try self.getOrCreateMetadataForNav(macho_file, nav_index);
886 self.symbols.items[sym_index].getAtom(macho_file).?.freeRelocs(macho_file);
887
888 var aw: std.Io.Writer.Allocating = .init(zcu.gpa);
889 defer aw.deinit();
890
891 var debug_wip_nav = if (self.dwarf) |*dwarf| try dwarf.initWipNav(pt, nav_index, @fromBackingInt(@intCast(sym_index))) else null;
892 defer if (debug_wip_nav) |*wip_nav| wip_nav.deinit();
893
894 codegen.generateSymbol(
895 &macho_file.base,
896 pt,
897 .fromInterned(nav.resolved.?.value),
898 &aw.writer,
899 .{ .atom_index = @fromBackingInt(@intCast(sym_index)) },
900 ) catch |err| switch (err) {
901 error.WriteFailed => return error.OutOfMemory,
902 else => |e| return e,
903 };
904 const code = aw.written();
905
906 const sect_index = try self.getNavOutputSection(macho_file, zcu, nav_index, code);
907 if (isThreadlocal(macho_file, nav_index))
908 try self.updateTlv(macho_file, zcu, nav_index, sym_index, sect_index, code)
909 else
910 try self.updateNavCode(macho_file, pt, nav_index, sym_index, sect_index, code);
911
912 if (debug_wip_nav) |*wip_nav| self.dwarf.?.finishWipNav(pt, nav_index, wip_nav) catch |err| switch (err) {
913 error.OutOfMemory, error.Canceled, error.AlreadyReported => |e| return e,
914 else => |e| return macho_file.base.cgFail(nav_index, "failed to finish dwarf nav: {s}", .{@errorName(e)}),
915 };
916 } else if (self.dwarf) |*dwarf| try dwarf.updateComptimeNav(pt, nav_index);
917
918 // Exports will be updated by `Zcu.processExports` after the update.
919}
920
921fn updateNavCode(
922 self: *ZigObject,
923 macho_file: *MachO,
924 pt: Zcu.PerThread,
925 nav_index: InternPool.Nav.Index,
926 sym_index: Symbol.Index,
927 sect_index: u8,
928 code: []const u8,
929) link.Error!void {
930 const zcu = pt.zcu;
931 const gpa = zcu.gpa;
932 const comp = zcu.comp;
933 const io = comp.io;
934 const ip = &zcu.intern_pool;
935 const nav = ip.getNav(nav_index);
936
937 log.debug("updateNavCode {f} 0x{x}", .{ nav.fqn.fmt(ip), nav_index });
938
939 const mod = zcu.navFileScope(nav_index).mod.?;
940 const target = &mod.resolved_target.result;
941 const required_alignment = switch (nav.resolved.?.@"align") {
942 .none => switch (mod.optimize_mode) {
943 .debug, .safe, .fast => target_util.defaultFunctionAlignment(target),
944 .small => target_util.minFunctionAlignment(target),
945 },
946 else => |a| a.maxStrict(target_util.minFunctionAlignment(target)),
947 };
948
949 const sect = &macho_file.sections.items(.header)[sect_index];
950 const sym = &self.symbols.items[sym_index];
951 const nlist = &self.symtab.items(.nlist)[sym.nlist_idx];
952 const atom = sym.getAtom(macho_file).?;
953
954 sym.out_n_sect = sect_index;
955 atom.out_n_sect = sect_index;
956
957 const sym_name = try std.fmt.allocPrintSentinel(gpa, "_{s}", .{nav.fqn.toSlice(ip)}, 0);
958 defer gpa.free(sym_name);
959 sym.name = try self.addString(gpa, sym_name);
960 atom.setAlive(true);
961 atom.name = sym.name;
962 nlist.n_strx = sym.name.pos;
963 nlist.n_type = .{ .bits = .{ .ext = false, .type = .sect, .pext = false, .is_stab = 0 } };
964 nlist.n_sect = sect_index + 1;
965 self.symtab.items(.size)[sym.nlist_idx] = code.len;
966
967 const old_size = atom.size;
968 const old_vaddr = atom.value;
969 atom.alignment = required_alignment;
970 atom.size = code.len;
971
972 if (old_size > 0) {
973 const capacity = atom.capacity(macho_file);
974 const need_realloc = code.len > capacity or !required_alignment.check(atom.value);
975
976 if (need_realloc) {
977 atom.grow(macho_file) catch |err|
978 return macho_file.base.cgFail(nav_index, "failed to grow atom: {s}", .{@errorName(err)});
979 log.debug("growing {f} from 0x{x} to 0x{x}", .{ nav.fqn.fmt(ip), old_vaddr, atom.value });
980 if (old_vaddr != atom.value) {
981 sym.value = 0;
982 nlist.n_value = 0;
983 }
984 } else if (code.len < old_size) {
985 atom.shrink(macho_file);
986 } else if (self.getAtom(atom.next_index) == null) {
987 const needed_size = atom.value + code.len;
988 sect.size = needed_size;
989 }
990 } else {
991 atom.allocate(macho_file) catch |err|
992 return macho_file.base.cgFail(nav_index, "failed to allocate atom: {s}", .{@errorName(err)});
993 errdefer self.freeNavMetadata(macho_file, sym_index);
994
995 sym.value = 0;
996 nlist.n_value = 0;
997 }
998
999 if (!sect.isZerofill()) {
1000 const file_offset = sect.offset + atom.value;
1001 macho_file.base.file.?.writePositionalAll(io, code, file_offset) catch |err|
1002 return macho_file.base.cgFail(nav_index, "failed to write output file: {t}", .{err});
1003 }
1004}
1005
1006/// Lowering a TLV on macOS involves two stages:
1007/// 1. first we lower the initializer into appopriate section (__thread_data or __thread_bss)
1008/// 2. next, we create a corresponding threadlocal variable descriptor in __thread_vars
1009fn updateTlv(
1010 self: *ZigObject,
1011 macho_file: *MachO,
1012 zcu: *Zcu,
1013 nav_index: InternPool.Nav.Index,
1014 sym_index: Symbol.Index,
1015 sect_index: u8,
1016 code: []const u8,
1017) !void {
1018 const ip = &zcu.intern_pool;
1019 const nav = ip.getNav(nav_index);
1020
1021 log.debug("updateTlv {f} (0x{x})", .{ nav.fqn.fmt(ip), nav_index });
1022
1023 // 1. Lower TLV initializer
1024 const init_sym_index = try self.createTlvInitializer(
1025 macho_file,
1026 nav.fqn.toSlice(ip),
1027 zcu.navAlignment(nav_index),
1028 sect_index,
1029 code,
1030 );
1031
1032 // 2. Create TLV descriptor
1033 try self.createTlvDescriptor(macho_file, sym_index, init_sym_index, nav.fqn.toSlice(ip));
1034}
1035
1036fn createTlvInitializer(
1037 self: *ZigObject,
1038 macho_file: *MachO,
1039 name: []const u8,
1040 alignment: Atom.Alignment,
1041 sect_index: u8,
1042 code: []const u8,
1043) !Symbol.Index {
1044 const gpa = macho_file.base.comp.gpa;
1045 const sym_name = try std.fmt.allocPrint(gpa, "{s}$tlv$init", .{name});
1046 defer gpa.free(sym_name);
1047 const string = try self.addString(gpa, sym_name);
1048
1049 const sym_index = try self.newSymbolWithAtom(gpa, string, macho_file);
1050 const sym = &self.symbols.items[sym_index];
1051 const nlist = &self.symtab.items(.nlist)[sym.nlist_idx];
1052 const atom = sym.getAtom(macho_file).?;
1053 sym.out_n_sect = sect_index;
1054 atom.out_n_sect = sect_index;
1055 atom.setAlive(true);
1056 atom.alignment = alignment;
1057 atom.size = code.len;
1058 nlist.n_sect = sect_index + 1;
1059 self.symtab.items(.size)[sym.nlist_idx] = code.len;
1060
1061 const slice = macho_file.sections.slice();
1062 const header = slice.items(.header)[sect_index];
1063
1064 const gop = try self.tlv_initializers.getOrPut(gpa, atom.atom_index);
1065 assert(!gop.found_existing); // TODO incremental updates
1066 gop.value_ptr.* = .{ .symbol_index = sym_index };
1067
1068 // We only store the data for the TLV if it's non-zerofill.
1069 if (!header.isZerofill()) {
1070 gop.value_ptr.data = try gpa.dupe(u8, code);
1071 }
1072
1073 return sym_index;
1074}
1075
1076fn createTlvDescriptor(
1077 self: *ZigObject,
1078 macho_file: *MachO,
1079 sym_index: Symbol.Index,
1080 init_sym_index: Symbol.Index,
1081 name: []const u8,
1082) !void {
1083 const gpa = macho_file.base.comp.gpa;
1084
1085 const sym = &self.symbols.items[sym_index];
1086 const nlist = &self.symtab.items(.nlist)[sym.nlist_idx];
1087 const atom = sym.getAtom(macho_file).?;
1088 const alignment = Atom.Alignment.fromNonzeroByteUnits(@alignOf(u64));
1089 const size: u64 = @sizeOf(u64) * 3;
1090
1091 const sect_index = macho_file.getSectionByName("__DATA", "__thread_vars") orelse
1092 try macho_file.addSection("__DATA", "__thread_vars", .{
1093 .flags = macho.S_THREAD_LOCAL_VARIABLES,
1094 });
1095 sym.out_n_sect = sect_index;
1096 atom.out_n_sect = sect_index;
1097
1098 sym.value = 0;
1099 sym.name = try self.addString(gpa, name);
1100 atom.setAlive(true);
1101 atom.name = sym.name;
1102 nlist.n_strx = sym.name.pos;
1103 nlist.n_sect = sect_index + 1;
1104 nlist.n_type = .{ .bits = .{ .ext = false, .type = .sect, .pext = false, .is_stab = 0 } };
1105 nlist.n_value = 0;
1106 self.symtab.items(.size)[sym.nlist_idx] = size;
1107
1108 atom.alignment = alignment;
1109 atom.size = size;
1110
1111 const tlv_bootstrap_index = try self.getGlobalSymbol(macho_file, "_tlv_bootstrap", null);
1112 try atom.addReloc(macho_file, .{
1113 .tag = .@"extern",
1114 .offset = 0,
1115 .target = tlv_bootstrap_index,
1116 .addend = 0,
1117 .type = .unsigned,
1118 .meta = .{
1119 .pcrel = false,
1120 .has_subtractor = false,
1121 .length = 3,
1122 .symbolnum = @intCast(tlv_bootstrap_index),
1123 },
1124 });
1125 try atom.addReloc(macho_file, .{
1126 .tag = .@"extern",
1127 .offset = 16,
1128 .target = init_sym_index,
1129 .addend = 0,
1130 .type = .unsigned,
1131 .meta = .{
1132 .pcrel = false,
1133 .has_subtractor = false,
1134 .length = 3,
1135 .symbolnum = @intCast(init_sym_index),
1136 },
1137 });
1138}
1139
1140fn getNavOutputSection(
1141 self: *ZigObject,
1142 macho_file: *MachO,
1143 zcu: *Zcu,
1144 nav_index: InternPool.Nav.Index,
1145 code: []const u8,
1146) error{OutOfMemory}!u8 {
1147 _ = self;
1148 const ip = &zcu.intern_pool;
1149 const nav = ip.getNav(nav_index);
1150 const nav_val: Value = .fromInterned(nav.resolved.?.value);
1151 if (ip.isFunctionType(nav_val.typeOf(zcu).toIntern())) return macho_file.zig_text_sect_index.?;
1152 if (nav.resolved.?.@"threadlocal" and macho_file.base.comp.config.any_non_single_threaded) {
1153 for (code) |byte| {
1154 if (byte != 0) break;
1155 } else return macho_file.getSectionByName("__DATA", "__thread_bss") orelse try macho_file.addSection(
1156 "__DATA",
1157 "__thread_bss",
1158 .{ .flags = macho.S_THREAD_LOCAL_ZEROFILL },
1159 );
1160 return macho_file.getSectionByName("__DATA", "__thread_data") orelse try macho_file.addSection(
1161 "__DATA",
1162 "__thread_data",
1163 .{ .flags = macho.S_THREAD_LOCAL_REGULAR },
1164 );
1165 }
1166 if (nav.resolved.?.@"const") return macho_file.zig_const_sect_index.?;
1167 if (nav_val.isUndef(zcu))
1168 return switch (zcu.navFileScope(nav_index).mod.?.optimize_mode) {
1169 .debug, .safe => macho_file.zig_data_sect_index.?,
1170 .fast, .small => macho_file.zig_bss_sect_index.?,
1171 };
1172 for (code) |byte| {
1173 if (byte != 0) break;
1174 } else return macho_file.zig_bss_sect_index.?;
1175 return macho_file.zig_data_sect_index.?;
1176}
1177
1178fn lowerConst(
1179 self: *ZigObject,
1180 macho_file: *MachO,
1181 pt: Zcu.PerThread,
1182 name: []const u8,
1183 val: Value,
1184 required_alignment: Atom.Alignment,
1185 output_section_index: u8,
1186) !link.File.SymbolId {
1187 const gpa = macho_file.base.comp.gpa;
1188
1189 var aw: std.Io.Writer.Allocating = .init(gpa);
1190 defer aw.deinit();
1191
1192 const name_str = try self.addString(gpa, name);
1193 const sym_index = try self.newSymbolWithAtom(gpa, name_str, macho_file);
1194
1195 codegen.generateSymbol(
1196 &macho_file.base,
1197 pt,
1198 val,
1199 &aw.writer,
1200 .{ .atom_index = @fromBackingInt(@intCast(sym_index)) },
1201 ) catch |err| switch (err) {
1202 error.WriteFailed => return error.OutOfMemory,
1203 else => |e| return e,
1204 };
1205 const code = aw.written();
1206
1207 const sym = &self.symbols.items[sym_index];
1208 sym.out_n_sect = output_section_index;
1209
1210 const nlist = &self.symtab.items(.nlist)[sym.nlist_idx];
1211 nlist.n_sect = output_section_index + 1;
1212 self.symtab.items(.size)[sym.nlist_idx] = code.len;
1213
1214 const atom = sym.getAtom(macho_file).?;
1215 atom.setAlive(true);
1216 atom.alignment = required_alignment;
1217 atom.size = code.len;
1218 atom.out_n_sect = output_section_index;
1219
1220 try atom.allocate(macho_file);
1221 // TODO rename and re-audit this method
1222 errdefer self.freeNavMetadata(macho_file, sym_index);
1223
1224 const sect = macho_file.sections.items(.header)[output_section_index];
1225 const file_offset = sect.offset + atom.value;
1226 try macho_file.pwriteAll(code, file_offset);
1227
1228 return @fromBackingInt(@intCast(sym_index));
1229}
1230
1231pub fn updateExports(
1232 self: *ZigObject,
1233 macho_file: *MachO,
1234 pt: Zcu.PerThread,
1235 export_indices: []const Zcu.Export.Index,
1236) link.Error!void {
1237 const tracy = trace(@src());
1238 defer tracy.end();
1239
1240 const zcu = pt.zcu;
1241 const gpa = macho_file.base.comp.gpa;
1242
1243 // Delete all existing exports first
1244 for (self.navs.values()) |*metadata| {
1245 for (metadata.exports.items) |nlist_index| {
1246 const nlist = &self.symtab.items(.nlist)[nlist_index];
1247 self.symtab.items(.size)[nlist_index] = 0;
1248 _ = self.globals_lookup.remove(nlist.n_strx);
1249 // TODO actually remove the export
1250 // const sym_index = macho_file.globals.get(nlist.n_strx).?;
1251 // const sym = &self.symbols.items[sym_index];
1252 // if (sym.file == self.index) {
1253 // sym.* = .{};
1254 // }
1255 nlist.* = MachO.null_sym;
1256 }
1257 metadata.exports.clearRetainingCapacity();
1258 }
1259 for (self.uavs.values()) |*metadata| {
1260 for (metadata.exports.items) |nlist_index| {
1261 const nlist = &self.symtab.items(.nlist)[nlist_index];
1262 self.symtab.items(.size)[nlist_index] = 0;
1263 _ = self.globals_lookup.remove(nlist.n_strx);
1264 // TODO actually remove the export
1265 // const sym_index = macho_file.globals.get(nlist.n_strx).?;
1266 // const sym = &self.symbols.items[sym_index];
1267 // if (sym.file == self.index) {
1268 // sym.* = .{};
1269 // }
1270 nlist.* = MachO.null_sym;
1271 }
1272 metadata.exports.clearRetainingCapacity();
1273 }
1274
1275 for (export_indices) |export_index| {
1276 const exp = export_index.ptr(zcu);
1277
1278 const metadata = switch (exp.exported) {
1279 .nav => |nav| blk: {
1280 _ = try self.getOrCreateMetadataForNav(macho_file, nav);
1281 break :blk self.navs.getPtr(nav).?;
1282 },
1283 .uav => |uav| self.uavs.getPtr(uav) orelse blk: {
1284 _ = try self.lowerUav(macho_file, pt, uav, .none);
1285 break :blk self.uavs.getPtr(uav).?;
1286 },
1287 };
1288 const sym_index = metadata.symbol_index;
1289 const nlist_idx = self.symbols.items[sym_index].nlist_idx;
1290 const nlist = self.symtab.items(.nlist)[nlist_idx];
1291
1292 if (exp.opts.section.unwrap()) |section_name| {
1293 if (!section_name.eqlSlice("__text", &zcu.intern_pool)) {
1294 try zcu.failed_exports.ensureUnusedCapacity(zcu.gpa, 1);
1295 zcu.failed_exports.putAssumeCapacityNoClobber(export_index, try Zcu.ErrorMsg.create(
1296 gpa,
1297 exp.src,
1298 "Unimplemented: ExportOptions.section",
1299 .{},
1300 ));
1301 continue;
1302 }
1303 }
1304 if (exp.opts.linkage == .link_once) {
1305 try zcu.failed_exports.putNoClobber(zcu.gpa, export_index, try Zcu.ErrorMsg.create(
1306 gpa,
1307 exp.src,
1308 "Unimplemented: GlobalLinkage.link_once",
1309 .{},
1310 ));
1311 continue;
1312 }
1313
1314 const exp_name = exp.opts.name.toSlice(&zcu.intern_pool);
1315 const global_nlist_index = try self.getGlobalSymbol(macho_file, exp_name, null);
1316 try metadata.exports.append(gpa, global_nlist_index);
1317
1318 const global_nlist = &self.symtab.items(.nlist)[global_nlist_index];
1319 const atom_index = self.symtab.items(.atom)[nlist_idx];
1320 const global_sym = &self.symbols.items[global_nlist_index];
1321 global_nlist.n_value = nlist.n_value;
1322 global_nlist.n_sect = nlist.n_sect;
1323 global_nlist.n_type = .{ .bits = .{ .ext = true, .type = .sect, .pext = false, .is_stab = 0 } };
1324 self.symtab.items(.size)[global_nlist_index] = self.symtab.items(.size)[nlist_idx];
1325 self.symtab.items(.atom)[global_nlist_index] = atom_index;
1326 global_sym.atom_ref = .{ .index = atom_index, .file = self.index };
1327
1328 switch (exp.opts.linkage) {
1329 .internal => {
1330 // Symbol should be hidden, or in MachO lingo, private extern.
1331 global_nlist.n_type.bits.pext = true;
1332 global_sym.visibility = .hidden;
1333 },
1334 .strong => {
1335 global_sym.visibility = .global;
1336 },
1337 .weak => {
1338 // Weak linkage is specified as part of n_desc field.
1339 // Symbol's n_type is like for a symbol with strong linkage.
1340 global_nlist.n_desc.weak_def_or_ref_to_weak = true;
1341 global_sym.visibility = .global;
1342 global_sym.flags.weak = true;
1343 },
1344 else => unreachable,
1345 }
1346 }
1347}
1348
1349fn updateLazySymbol(
1350 self: *ZigObject,
1351 macho_file: *MachO,
1352 pt: Zcu.PerThread,
1353 lazy_sym: link.File.LazySymbol,
1354 symbol_index: Symbol.Index,
1355) !void {
1356 const zcu = pt.zcu;
1357 const gpa = zcu.gpa;
1358
1359 var required_alignment: Atom.Alignment = .none;
1360 var aw: std.Io.Writer.Allocating = .init(gpa);
1361 defer aw.deinit();
1362
1363 const name_str = blk: {
1364 const name = try std.fmt.allocPrint(gpa, "__lazy_{s}_{f}", .{
1365 @tagName(lazy_sym.kind),
1366 Type.fromInterned(lazy_sym.ty).fmt(pt),
1367 });
1368 defer gpa.free(name);
1369 break :blk try self.addString(gpa, name);
1370 };
1371
1372 try codegen.generateLazySymbol(
1373 &macho_file.base,
1374 pt,
1375 lazy_sym,
1376 &required_alignment,
1377 &aw.writer,
1378 .none,
1379 .{ .atom_index = @fromBackingInt(@intCast(symbol_index)) },
1380 );
1381 const code = aw.written();
1382
1383 const output_section_index = switch (lazy_sym.kind) {
1384 .code => macho_file.zig_text_sect_index.?,
1385 .const_data => macho_file.zig_const_sect_index.?,
1386 };
1387 const sym = &self.symbols.items[symbol_index];
1388 sym.name = name_str;
1389 sym.out_n_sect = output_section_index;
1390
1391 const nlist = &self.symtab.items(.nlist)[sym.nlist_idx];
1392 nlist.n_strx = name_str.pos;
1393 nlist.n_type = .{ .bits = .{ .ext = false, .type = .sect, .pext = false, .is_stab = 0 } };
1394 nlist.n_sect = output_section_index + 1;
1395 self.symtab.items(.size)[sym.nlist_idx] = code.len;
1396
1397 const atom = sym.getAtom(macho_file).?;
1398 atom.setAlive(true);
1399 atom.name = name_str;
1400 atom.alignment = required_alignment;
1401 atom.size = code.len;
1402 atom.out_n_sect = output_section_index;
1403
1404 try atom.allocate(macho_file);
1405 errdefer self.freeNavMetadata(macho_file, symbol_index);
1406
1407 sym.value = 0;
1408 nlist.n_value = 0;
1409
1410 const sect = macho_file.sections.items(.header)[output_section_index];
1411 const file_offset = sect.offset + atom.value;
1412 try macho_file.pwriteAll(code, file_offset);
1413}
1414
1415pub fn updateLineNumber(self: *ZigObject, pt: Zcu.PerThread, ti_id: InternPool.TrackedInst.Index, line: u32) link.Error!void {
1416 if (self.dwarf) |*dwarf| {
1417 const comp = dwarf.bin_file.comp;
1418 const diags = &comp.link_diags;
1419 dwarf.updateLineNumber(pt.zcu, ti_id, line) catch |err| switch (err) {
1420 error.OutOfMemory, error.Canceled, error.AlreadyReported => |e| return e,
1421 else => |e| return diags.fail("failed to update dwarf line numbers: {s}", .{@errorName(e)}),
1422 };
1423 }
1424}
1425
1426pub fn getGlobalSymbol(self: *ZigObject, macho_file: *MachO, name: []const u8, lib_name: ?[]const u8) !u32 {
1427 _ = lib_name;
1428 const gpa = macho_file.base.comp.gpa;
1429 const sym_name = try std.fmt.allocPrint(gpa, "_{s}", .{name});
1430 defer gpa.free(sym_name);
1431 const name_str = try self.addString(gpa, sym_name);
1432 const lookup_gop = try self.globals_lookup.getOrPut(gpa, name_str.pos);
1433 if (!lookup_gop.found_existing) {
1434 const sym_index = try self.newSymbol(gpa, name_str, .{});
1435 const sym = &self.symbols.items[sym_index];
1436 lookup_gop.value_ptr.* = sym.nlist_idx;
1437 }
1438 return lookup_gop.value_ptr.*;
1439}
1440
1441const max_trampoline_len = 12;
1442
1443fn trampolineSize(cpu_arch: std.Target.Cpu.Arch) u64 {
1444 const len = switch (cpu_arch) {
1445 .x86_64 => 5, // jmp rel32
1446 else => @panic("TODO implement trampoline size for this CPU arch"),
1447 };
1448 comptime assert(len <= max_trampoline_len);
1449 return len;
1450}
1451
1452fn writeTrampoline(tr_sym: Symbol, target: Symbol, macho_file: *MachO) !void {
1453 const atom = tr_sym.getAtom(macho_file).?;
1454 const header = macho_file.sections.items(.header)[atom.out_n_sect];
1455 const fileoff = header.offset + atom.value;
1456 const source_addr = tr_sym.getAddress(.{}, macho_file);
1457 const target_addr = target.getAddress(.{ .trampoline = false }, macho_file);
1458 var buf: [max_trampoline_len]u8 = undefined;
1459 const out = switch (macho_file.getTarget().cpu.arch) {
1460 .x86_64 => try x86_64.writeTrampolineCode(source_addr, target_addr, &buf),
1461 else => @panic("TODO implement write trampoline for this CPU arch"),
1462 };
1463 return macho_file.pwriteAll(out, fileoff);
1464}
1465
1466pub fn getOrCreateMetadataForNav(
1467 self: *ZigObject,
1468 macho_file: *MachO,
1469 nav_index: InternPool.Nav.Index,
1470) !Symbol.Index {
1471 const gpa = macho_file.base.comp.gpa;
1472 const gop = try self.navs.getOrPut(gpa, nav_index);
1473 if (!gop.found_existing) {
1474 const sym_index = try self.newSymbolWithAtom(gpa, .{}, macho_file);
1475 const sym = &self.symbols.items[sym_index];
1476 if (isThreadlocal(macho_file, nav_index)) {
1477 sym.flags.tlv = true;
1478 }
1479 gop.value_ptr.* = .{ .symbol_index = sym_index };
1480 }
1481 return gop.value_ptr.symbol_index;
1482}
1483
1484pub fn getOrCreateMetadataForLazySymbol(
1485 self: *ZigObject,
1486 macho_file: *MachO,
1487 pt: Zcu.PerThread,
1488 lazy_sym: link.File.LazySymbol,
1489) !Symbol.Index {
1490 const gop = try self.lazy_syms.getOrPut(pt.zcu.gpa, lazy_sym.ty);
1491 errdefer _ = if (!gop.found_existing) self.lazy_syms.pop();
1492 if (!gop.found_existing) gop.value_ptr.* = .{};
1493 const symbol_index_ptr, const state_ptr = switch (lazy_sym.kind) {
1494 .code => .{ &gop.value_ptr.text_symbol_index, &gop.value_ptr.text_state },
1495 .const_data => .{ &gop.value_ptr.const_symbol_index, &gop.value_ptr.const_state },
1496 };
1497 switch (state_ptr.*) {
1498 .unused => symbol_index_ptr.* = try self.newSymbolWithAtom(pt.zcu.gpa, .{}, macho_file),
1499 .pending_flush => return symbol_index_ptr.*,
1500 .flushed => {},
1501 }
1502 state_ptr.* = .pending_flush;
1503 const symbol_index = symbol_index_ptr.*;
1504 // anyerror needs to be deferred until flush
1505 if (lazy_sym.ty != .anyerror_type) try self.updateLazySymbol(macho_file, pt, lazy_sym, symbol_index);
1506 return symbol_index;
1507}
1508
1509fn isThreadlocal(macho_file: *MachO, nav_index: InternPool.Nav.Index) bool {
1510 if (!macho_file.base.comp.config.any_non_single_threaded)
1511 return false;
1512 const ip = &macho_file.base.comp.zcu.?.intern_pool;
1513 return ip.getNav(nav_index).resolved.?.@"threadlocal";
1514}
1515
1516fn addAtom(self: *ZigObject, allocator: Allocator) !Atom.Index {
1517 try self.atoms.ensureUnusedCapacity(allocator, 1);
1518 try self.atoms_extra.ensureUnusedCapacity(allocator, @sizeOf(Atom.Extra));
1519 return self.addAtomAssumeCapacity();
1520}
1521
1522fn addAtomAssumeCapacity(self: *ZigObject) Atom.Index {
1523 const atom_index: Atom.Index = @intCast(self.atoms.items.len);
1524 const atom = self.atoms.addOneAssumeCapacity();
1525 atom.* = .{
1526 .file = self.index,
1527 .atom_index = atom_index,
1528 .extra = self.addAtomExtraAssumeCapacity(.{}),
1529 };
1530 return atom_index;
1531}
1532
1533pub fn getAtom(self: *ZigObject, atom_index: Atom.Index) ?*Atom {
1534 if (atom_index == 0) return null;
1535 assert(atom_index < self.atoms.items.len);
1536 return &self.atoms.items[atom_index];
1537}
1538
1539pub fn getAtoms(self: *ZigObject) []const Atom.Index {
1540 return self.atoms_indexes.items;
1541}
1542
1543fn addAtomExtra(self: *ZigObject, allocator: Allocator, extra: Atom.Extra) !u32 {
1544 const field = @typeInfo(Atom.Extra).@"struct".field_names;
1545 try self.atoms_extra.ensureUnusedCapacity(allocator, field.len);
1546 return self.addAtomExtraAssumeCapacity(extra);
1547}
1548
1549fn addAtomExtraAssumeCapacity(self: *ZigObject, extra: Atom.Extra) u32 {
1550 const index = @as(u32, @intCast(self.atoms_extra.items.len));
1551 const info = @typeInfo(Atom.Extra).@"struct";
1552 const field_names = info.field_names;
1553 const field_types = info.field_types;
1554 inline for (field_names, field_types) |field_name, field_type| {
1555 self.atoms_extra.appendAssumeCapacity(switch (field_type) {
1556 u32 => @field(extra, field_name),
1557 else => @compileError("bad field type"),
1558 });
1559 }
1560 return index;
1561}
1562
1563pub fn getAtomExtra(self: ZigObject, index: u32) Atom.Extra {
1564 const info = @typeInfo(Atom.Extra).@"struct";
1565 const field_names = info.field_names;
1566 const field_types = info.field_types;
1567 var i: usize = index;
1568 var result: Atom.Extra = undefined;
1569 inline for (field_names, field_types) |field_name, field_type| {
1570 @field(result, field_name) = switch (field_type) {
1571 u32 => self.atoms_extra.items[i],
1572 else => @compileError("bad field type"),
1573 };
1574 i += 1;
1575 }
1576 return result;
1577}
1578
1579pub fn setAtomExtra(self: *ZigObject, index: u32, extra: Atom.Extra) void {
1580 assert(index > 0);
1581 const info = @typeInfo(Atom.Extra).@"struct";
1582 const field_names = info.field_names;
1583 const field_types = info.field_types;
1584 inline for (field_names, field_types, 0..) |field_name, field_type, i| {
1585 self.atoms_extra.items[index + i] = switch (field_type) {
1586 u32 => @field(extra, field_name),
1587 else => @compileError("bad field type"),
1588 };
1589 }
1590}
1591
1592fn addSymbol(self: *ZigObject, allocator: Allocator) !Symbol.Index {
1593 try self.symbols.ensureUnusedCapacity(allocator, 1);
1594 return self.addSymbolAssumeCapacity();
1595}
1596
1597fn addSymbolAssumeCapacity(self: *ZigObject) Symbol.Index {
1598 const index: Symbol.Index = @intCast(self.symbols.items.len);
1599 const symbol = self.symbols.addOneAssumeCapacity();
1600 symbol.* = .{ .file = self.index };
1601 return index;
1602}
1603
1604pub fn getSymbolRef(self: ZigObject, index: Symbol.Index, macho_file: *MachO) MachO.Ref {
1605 const global_index = self.globals.items[index];
1606 if (macho_file.resolver.get(global_index)) |ref| return ref;
1607 return .{ .index = index, .file = self.index };
1608}
1609
1610pub fn addSymbolExtra(self: *ZigObject, allocator: Allocator, extra: Symbol.Extra) !u32 {
1611 const fields = @typeInfo(Symbol.Extra).@"struct".field_names;
1612 try self.symbols_extra.ensureUnusedCapacity(allocator, fields.len);
1613 return self.addSymbolExtraAssumeCapacity(extra);
1614}
1615
1616fn addSymbolExtraAssumeCapacity(self: *ZigObject, extra: Symbol.Extra) u32 {
1617 const index = @as(u32, @intCast(self.symbols_extra.items.len));
1618 const info = @typeInfo(Symbol.Extra).@"struct";
1619 const field_names = info.field_names;
1620 const field_types = info.field_types;
1621 inline for (field_names, field_types) |field_name, field_type| {
1622 self.symbols_extra.appendAssumeCapacity(switch (field_type) {
1623 u32 => @field(extra, field_name),
1624 else => @compileError("bad field type"),
1625 });
1626 }
1627 return index;
1628}
1629
1630pub fn getSymbolExtra(self: ZigObject, index: u32) Symbol.Extra {
1631 const info = @typeInfo(Symbol.Extra).@"struct";
1632 const field_names = info.field_names;
1633 const field_types = info.field_types;
1634 var i: usize = index;
1635 var result: Symbol.Extra = undefined;
1636 inline for (field_names, field_types) |field_name, field_type| {
1637 @field(result, field_name) = switch (field_type) {
1638 u32 => self.symbols_extra.items[i],
1639 else => @compileError("bad field type"),
1640 };
1641 i += 1;
1642 }
1643 return result;
1644}
1645
1646pub fn setSymbolExtra(self: *ZigObject, index: u32, extra: Symbol.Extra) void {
1647 const info = @typeInfo(Symbol.Extra).@"struct";
1648 const field_names = info.field_names;
1649 const field_types = info.field_types;
1650 inline for (field_names, field_types, 0..) |field_name, field_type, i| {
1651 self.symbols_extra.items[index + i] = switch (field_type) {
1652 u32 => @field(extra, field_name),
1653 else => @compileError("bad field type"),
1654 };
1655 }
1656}
1657
1658fn addString(self: *ZigObject, allocator: Allocator, string: []const u8) !MachO.String {
1659 const off = try self.strtab.insert(allocator, string);
1660 return .{ .pos = off, .len = @intCast(string.len + 1) };
1661}
1662
1663pub fn getString(self: ZigObject, string: MachO.String) [:0]const u8 {
1664 if (string.len == 0) return "";
1665 return self.strtab.buffer.items[string.pos..][0 .. string.len - 1 :0];
1666}
1667
1668pub fn asFile(self: *ZigObject) File {
1669 return .{ .zig_object = self };
1670}
1671
1672pub fn fmtSymtab(self: *ZigObject, macho_file: *MachO) std.fmt.Alt(Format, Format.symtab) {
1673 return .{ .data = .{
1674 .self = self,
1675 .macho_file = macho_file,
1676 } };
1677}
1678
1679const Format = struct {
1680 self: *ZigObject,
1681 macho_file: *MachO,
1682
1683 fn symtab(f: Format, w: *Writer) Writer.Error!void {
1684 try w.writeAll(" symbols\n");
1685 const self = f.self;
1686 const macho_file = f.macho_file;
1687 for (self.symbols.items, 0..) |sym, i| {
1688 const ref = self.getSymbolRef(@intCast(i), macho_file);
1689 if (ref.getFile(macho_file) == null) {
1690 // TODO any better way of handling this?
1691 try w.print(" {s} : unclaimed\n", .{sym.getName(macho_file)});
1692 } else {
1693 try w.print(" {f}\n", .{ref.getSymbol(macho_file).?.fmt(macho_file)});
1694 }
1695 }
1696 }
1697
1698 fn atoms(f: Format, w: *Writer) Writer.Error!void {
1699 const self = f.self;
1700 const macho_file = f.macho_file;
1701 try w.writeAll(" atoms\n");
1702 for (self.getAtoms()) |atom_index| {
1703 const atom = self.getAtom(atom_index) orelse continue;
1704 try w.print(" {f}\n", .{atom.fmt(macho_file)});
1705 }
1706 }
1707};
1708
1709pub fn fmtAtoms(self: *ZigObject, macho_file: *MachO) std.fmt.Alt(Format, Format.atoms) {
1710 return .{ .data = .{
1711 .self = self,
1712 .macho_file = macho_file,
1713 } };
1714}
1715
1716const AvMetadata = struct {
1717 symbol_index: Symbol.Index,
1718 /// A list of all exports aliases of this Av.
1719 exports: std.ArrayList(Symbol.Index) = .empty,
1720};
1721
1722const LazySymbolMetadata = struct {
1723 const State = enum { unused, pending_flush, flushed };
1724 text_symbol_index: Symbol.Index = undefined,
1725 const_symbol_index: Symbol.Index = undefined,
1726 text_state: State = .unused,
1727 const_state: State = .unused,
1728};
1729
1730const TlvInitializer = struct {
1731 symbol_index: Symbol.Index,
1732 data: []const u8 = &[0]u8{},
1733
1734 fn deinit(tlv_init: *TlvInitializer, allocator: Allocator) void {
1735 allocator.free(tlv_init.data);
1736 }
1737};
1738
1739const NavTable = std.array_hash_map.Auto(InternPool.Nav.Index, AvMetadata);
1740const UavTable = std.array_hash_map.Auto(InternPool.Index, AvMetadata);
1741const LazySymbolTable = std.array_hash_map.Auto(InternPool.Index, LazySymbolMetadata);
1742const RelocationTable = std.ArrayList(std.ArrayList(Relocation));
1743const TlvInitializerTable = std.array_hash_map.Auto(Atom.Index, TlvInitializer);
1744
1745const x86_64 = struct {
1746 fn writeTrampolineCode(source_addr: u64, target_addr: u64, buf: *[max_trampoline_len]u8) ![]u8 {
1747 dev.checkAny(&.{ .llvm_backend, .x86_64_backend });
1748 const disp = @as(i64, @intCast(target_addr)) - @as(i64, @intCast(source_addr)) - 5;
1749 var bytes = [_]u8{
1750 0xe9, 0x00, 0x00, 0x00, 0x00, // jmp rel32
1751 };
1752 assert(bytes.len == trampolineSize(.x86_64));
1753 mem.writeInt(i32, bytes[1..][0..4], @intCast(disp), .little);
1754 @memcpy(buf[0..bytes.len], &bytes);
1755 return buf[0..bytes.len];
1756 }
1757};
1758
1759const assert = std.debug.assert;
1760const builtin = @import("builtin");
1761const codegen = @import("../../codegen.zig");
1762const dev = @import("../../dev.zig");
1763const link = @import("../../link.zig");
1764const log = std.log.scoped(.link);
1765const macho = std.macho;
1766const mem = std.mem;
1767const target_util = @import("../../target.zig");
1768const trace = @import("../../tracy.zig").trace;
1769const std = @import("std");
1770const Writer = std.Io.Writer;
1771
1772const Allocator = std.mem.Allocator;
1773const Archive = @import("Archive.zig");
1774const Atom = @import("Atom.zig");
1775const Dwarf = @import("../Dwarf.zig");
1776const File = @import("file.zig").File;
1777const InternPool = @import("../../InternPool.zig");
1778const MachO = @import("../MachO.zig");
1779const Nlist = Object.Nlist;
1780const Zcu = @import("../../Zcu.zig");
1781const Object = @import("Object.zig");
1782const Relocation = @import("Relocation.zig");
1783const Symbol = @import("Symbol.zig");
1784const StringTable = @import("../StringTable.zig");
1785const Type = @import("../../Type.zig");
1786const Value = @import("../../Value.zig");
1787const AnalUnit = InternPool.AnalUnit;
1788const ZigObject = @This();