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| switch (debug_output) {
651 .dwarf => |wip_nav| try wip_nav.infoExternalReloc(.{
652 .source_off = @intCast(reloc_info.offset),
653 .target_sym = @fromBackingInt(@intCast(sym_index)),
654 .target_off = reloc_info.addend,
655 }),
656 .none => unreachable,
657 },
658 }
659 return vaddr;
660}
661
662pub fn getUavVAddr(
663 self: *ZigObject,
664 macho_file: *MachO,
665 uav: InternPool.Index,
666 reloc_info: link.File.RelocInfo,
667) !u64 {
668 const sym_index = self.uavs.get(uav).?.symbol_index;
669 const sym = self.symbols.items[sym_index];
670 const vaddr = sym.getAddress(.{}, macho_file);
671 switch (reloc_info.parent) {
672 .none => unreachable,
673 .atom_index => |atom_index| {
674 const parent_atom = self.symbols.items[@backingInt(atom_index)].getAtom(macho_file).?;
675 try parent_atom.addReloc(macho_file, .{
676 .tag = .@"extern",
677 .offset = @intCast(reloc_info.offset),
678 .target = sym_index,
679 .addend = reloc_info.addend,
680 .type = .unsigned,
681 .meta = .{
682 .pcrel = false,
683 .has_subtractor = false,
684 .length = 3,
685 .symbolnum = @intCast(sym.nlist_idx),
686 },
687 });
688 },
689 .debug_output => |debug_output| switch (debug_output) {
690 .dwarf => |wip_nav| try wip_nav.infoExternalReloc(.{
691 .source_off = @intCast(reloc_info.offset),
692 .target_sym = @fromBackingInt(@intCast(sym_index)),
693 .target_off = reloc_info.addend,
694 }),
695 .none => unreachable,
696 },
697 }
698 return vaddr;
699}
700
701pub fn lowerUav(
702 self: *ZigObject,
703 macho_file: *MachO,
704 pt: Zcu.PerThread,
705 uav: InternPool.Index,
706 explicit_alignment: Atom.Alignment,
707) !link.File.SymbolId {
708 const zcu = pt.zcu;
709 const gpa = zcu.gpa;
710 const val = Value.fromInterned(uav);
711 const uav_alignment = switch (explicit_alignment) {
712 .none => val.typeOf(zcu).abiAlignment(zcu),
713 else => explicit_alignment,
714 };
715 if (self.uavs.get(uav)) |metadata| {
716 const sym = self.symbols.items[metadata.symbol_index];
717 const existing_alignment = sym.getAtom(macho_file).?.alignment;
718 if (uav_alignment.order(existing_alignment).compare(.lte))
719 return @fromBackingInt(@intCast(metadata.symbol_index));
720 }
721
722 var name_buf: [32]u8 = undefined;
723 const name = std.mem.print(&name_buf, "__anon_{d}", .{
724 @backingInt(uav),
725 }) catch unreachable;
726 const sym_index = self.lowerConst(
727 macho_file,
728 pt,
729 name,
730 val,
731 uav_alignment,
732 macho_file.zig_const_sect_index.?,
733 ) catch |err| switch (err) {
734 error.OutOfMemory => |e| return e,
735 else => |e| return macho_file.base.comp.link_diags.fail(
736 "failed to lower constant value: {t}",
737 .{e},
738 ),
739 };
740 try self.uavs.put(gpa, uav, .{ .symbol_index = @backingInt(sym_index) });
741 return sym_index;
742}
743
744fn freeNavMetadata(self: *ZigObject, macho_file: *MachO, sym_index: Symbol.Index) void {
745 const sym = self.symbols.items[sym_index];
746 sym.getAtom(macho_file).?.free(macho_file);
747 log.debug("adding %{d} to local symbols free list", .{sym_index});
748 // TODO redo this
749 // TODO free GOT entry here
750}
751
752pub fn freeNav(self: *ZigObject, macho_file: *MachO, nav_index: InternPool.Nav.Index) void {
753 const gpa = macho_file.base.comp.gpa;
754 log.debug("freeNav 0x{x}", .{nav_index});
755
756 if (self.navs.fetchRemove(nav_index)) |const_kv| {
757 var kv = const_kv;
758 const sym_index = kv.value.symbol_index;
759 self.freeNavMetadata(macho_file, sym_index);
760 kv.value.exports.deinit(gpa);
761 }
762
763 // TODO free decl in dSYM
764}
765
766pub fn updateFunc(
767 self: *ZigObject,
768 macho_file: *MachO,
769 pt: Zcu.PerThread,
770 func_index: InternPool.Index,
771 mir: *const codegen.AnyMir,
772) link.Error!void {
773 const tracy = trace(@src());
774 defer tracy.end();
775
776 const zcu = pt.zcu;
777 const gpa = zcu.gpa;
778 const func = zcu.funcInfo(func_index);
779
780 const sym_index = try self.getOrCreateMetadataForNav(macho_file, func.owner_nav);
781 self.symbols.items[sym_index].getAtom(macho_file).?.freeRelocs(macho_file);
782
783 var aw: std.Io.Writer.Allocating = .init(gpa);
784 defer aw.deinit();
785
786 var debug_wip_nav = if (self.dwarf) |*dwarf| try dwarf.initWipNav(pt, func.owner_nav, @fromBackingInt(@intCast(sym_index))) else null;
787 defer if (debug_wip_nav) |*wip_nav| wip_nav.deinit();
788
789 codegen.emitFunction(
790 &macho_file.base,
791 pt,
792 func_index,
793 @fromBackingInt(@intCast(sym_index)),
794 mir,
795 &aw.writer,
796 if (debug_wip_nav) |*wip_nav| .{ .dwarf = wip_nav } else .none,
797 ) catch |err| switch (err) {
798 error.WriteFailed => return error.OutOfMemory,
799 else => |e| return e,
800 };
801 const code = aw.written();
802
803 const sect_index = try self.getNavOutputSection(macho_file, zcu, func.owner_nav, code);
804 const old_rva, const old_alignment = blk: {
805 const atom = self.symbols.items[sym_index].getAtom(macho_file).?;
806 break :blk .{ atom.value, atom.alignment };
807 };
808 try self.updateNavCode(macho_file, pt, func.owner_nav, sym_index, sect_index, code);
809 const new_rva, const new_alignment = blk: {
810 const atom = self.symbols.items[sym_index].getAtom(macho_file).?;
811 break :blk .{ atom.value, atom.alignment };
812 };
813
814 if (debug_wip_nav) |*wip_nav| self.dwarf.?.finishWipNavFunc(pt, func.owner_nav, code.len, wip_nav) catch |err|
815 return macho_file.base.cgFail(func.owner_nav, "falied to finish dwarf function: {s}", .{@errorName(err)});
816
817 // Exports will be updated by `Zcu.processExports` after the update.
818 if (old_rva != new_rva and old_rva > 0) {
819 // If we had to reallocate the function, we re-use the existing slot for a trampoline.
820 // In the rare case that the function has been further overaligned we skip creating a
821 // trampoline and update all symbols referring this function.
822 if (old_alignment.order(new_alignment) == .lt) {
823 @panic("TODO update all symbols referring this function");
824 }
825
826 // Create a trampoline to the new location at `old_rva`.
827 if (!self.symbols.items[sym_index].flags.trampoline) {
828 const name = try std.fmt.allocPrint(gpa, "{s}$trampoline", .{
829 self.symbols.items[sym_index].getName(macho_file),
830 });
831 defer gpa.free(name);
832 const name_off = try self.addString(gpa, name);
833 const tr_size = trampolineSize(macho_file.getTarget().cpu.arch);
834 const tr_sym_index = try self.newSymbolWithAtom(gpa, name_off, macho_file);
835 const tr_sym = &self.symbols.items[tr_sym_index];
836 tr_sym.out_n_sect = macho_file.zig_text_sect_index.?;
837 const tr_nlist = &self.symtab.items(.nlist)[tr_sym.nlist_idx];
838 tr_nlist.n_sect = macho_file.zig_text_sect_index.? + 1;
839 const tr_atom = tr_sym.getAtom(macho_file).?;
840 tr_atom.value = old_rva;
841 tr_atom.setAlive(true);
842 tr_atom.alignment = old_alignment;
843 tr_atom.out_n_sect = macho_file.zig_text_sect_index.?;
844 tr_atom.size = tr_size;
845 self.symtab.items(.size)[tr_sym.nlist_idx] = tr_size;
846 const target_sym = &self.symbols.items[sym_index];
847 target_sym.addExtra(.{ .trampoline = tr_sym_index }, macho_file);
848 target_sym.flags.trampoline = true;
849 }
850 const target_sym = self.symbols.items[sym_index];
851 const source_sym = self.symbols.items[target_sym.getExtra(macho_file).trampoline];
852 writeTrampoline(source_sym, target_sym, macho_file) catch |err|
853 return macho_file.base.cgFail(func.owner_nav, "failed to write trampoline: {s}", .{@errorName(err)});
854 }
855}
856
857pub fn updateNav(
858 self: *ZigObject,
859 macho_file: *MachO,
860 pt: Zcu.PerThread,
861 nav_index: InternPool.Nav.Index,
862) link.Error!void {
863 const tracy = trace(@src());
864 defer tracy.end();
865
866 const zcu = pt.zcu;
867 const ip = &zcu.intern_pool;
868 const nav = ip.getNav(nav_index);
869
870 switch (ip.indexToKey(nav.resolved.?.value)) {
871 else => {},
872 .@"extern" => |@"extern"| {
873 // Extern variable gets a __got entry only
874 const name = @"extern".name.toSlice(ip);
875 const lib_name = @"extern".lib_name.toSlice(ip);
876 const sym_index = try self.getGlobalSymbol(macho_file, name, lib_name);
877 if (nav.resolved.?.@"threadlocal" and macho_file.base.comp.config.any_non_single_threaded) self.symbols.items[sym_index].flags.tlv = true;
878 if (self.dwarf) |*dwarf| {
879 var debug_wip_nav = try dwarf.initWipNav(pt, nav_index, @fromBackingInt(@intCast(sym_index)));
880 defer debug_wip_nav.deinit();
881 dwarf.finishWipNav(pt, nav_index, &debug_wip_nav) catch |err| switch (err) {
882 error.OutOfMemory, error.Canceled, error.AlreadyReported => |e| return e,
883 else => |e| return macho_file.base.cgFail(nav_index, "failed to finish dwarf nav: {s}", .{@errorName(e)}),
884 };
885 }
886 return;
887 },
888 }
889
890 if (Type.fromInterned(nav.resolved.?.type).hasRuntimeBits(zcu)) {
891 const sym_index = try self.getOrCreateMetadataForNav(macho_file, nav_index);
892 self.symbols.items[sym_index].getAtom(macho_file).?.freeRelocs(macho_file);
893
894 var aw: std.Io.Writer.Allocating = .init(zcu.gpa);
895 defer aw.deinit();
896
897 var debug_wip_nav = if (self.dwarf) |*dwarf| try dwarf.initWipNav(pt, nav_index, @fromBackingInt(@intCast(sym_index))) else null;
898 defer if (debug_wip_nav) |*wip_nav| wip_nav.deinit();
899
900 codegen.generateSymbol(
901 &macho_file.base,
902 pt,
903 .fromInterned(nav.resolved.?.value),
904 &aw.writer,
905 .{ .atom_index = @fromBackingInt(@intCast(sym_index)) },
906 ) catch |err| switch (err) {
907 error.WriteFailed => return error.OutOfMemory,
908 else => |e| return e,
909 };
910 const code = aw.written();
911
912 const sect_index = try self.getNavOutputSection(macho_file, zcu, nav_index, code);
913 if (isThreadlocal(macho_file, nav_index))
914 try self.updateTlv(macho_file, zcu, nav_index, sym_index, sect_index, code)
915 else
916 try self.updateNavCode(macho_file, pt, nav_index, sym_index, sect_index, code);
917
918 if (debug_wip_nav) |*wip_nav| self.dwarf.?.finishWipNav(pt, nav_index, wip_nav) catch |err| switch (err) {
919 error.OutOfMemory, error.Canceled, error.AlreadyReported => |e| return e,
920 else => |e| return macho_file.base.cgFail(nav_index, "failed to finish dwarf nav: {s}", .{@errorName(e)}),
921 };
922 } else if (self.dwarf) |*dwarf| try dwarf.updateComptimeNav(pt, nav_index);
923
924 // Exports will be updated by `Zcu.processExports` after the update.
925}
926
927fn updateNavCode(
928 self: *ZigObject,
929 macho_file: *MachO,
930 pt: Zcu.PerThread,
931 nav_index: InternPool.Nav.Index,
932 sym_index: Symbol.Index,
933 sect_index: u8,
934 code: []const u8,
935) link.Error!void {
936 const zcu = pt.zcu;
937 const gpa = zcu.gpa;
938 const comp = zcu.comp;
939 const io = comp.io;
940 const ip = &zcu.intern_pool;
941 const nav = ip.getNav(nav_index);
942
943 log.debug("updateNavCode {f} 0x{x}", .{ nav.fqn.fmt(ip), nav_index });
944
945 const mod = zcu.navFileScope(nav_index).mod.?;
946 const target = &mod.resolved_target.result;
947 const required_alignment = switch (nav.resolved.?.@"align") {
948 .none => switch (mod.optimize_mode) {
949 .debug, .safe, .fast => target_util.defaultFunctionAlignment(target),
950 .small => target_util.minFunctionAlignment(target),
951 },
952 else => |a| a.maxStrict(target_util.minFunctionAlignment(target)),
953 };
954
955 const sect = &macho_file.sections.items(.header)[sect_index];
956 const sym = &self.symbols.items[sym_index];
957 const nlist = &self.symtab.items(.nlist)[sym.nlist_idx];
958 const atom = sym.getAtom(macho_file).?;
959
960 sym.out_n_sect = sect_index;
961 atom.out_n_sect = sect_index;
962
963 const sym_name = try std.fmt.allocPrintSentinel(gpa, "_{s}", .{nav.fqn.toSlice(ip)}, 0);
964 defer gpa.free(sym_name);
965 sym.name = try self.addString(gpa, sym_name);
966 atom.setAlive(true);
967 atom.name = sym.name;
968 nlist.n_strx = sym.name.pos;
969 nlist.n_type = .{ .bits = .{ .ext = false, .type = .sect, .pext = false, .is_stab = 0 } };
970 nlist.n_sect = sect_index + 1;
971 self.symtab.items(.size)[sym.nlist_idx] = code.len;
972
973 const old_size = atom.size;
974 const old_vaddr = atom.value;
975 atom.alignment = required_alignment;
976 atom.size = code.len;
977
978 if (old_size > 0) {
979 const capacity = atom.capacity(macho_file);
980 const need_realloc = code.len > capacity or !required_alignment.check(atom.value);
981
982 if (need_realloc) {
983 atom.grow(macho_file) catch |err|
984 return macho_file.base.cgFail(nav_index, "failed to grow atom: {s}", .{@errorName(err)});
985 log.debug("growing {f} from 0x{x} to 0x{x}", .{ nav.fqn.fmt(ip), old_vaddr, atom.value });
986 if (old_vaddr != atom.value) {
987 sym.value = 0;
988 nlist.n_value = 0;
989 }
990 } else if (code.len < old_size) {
991 atom.shrink(macho_file);
992 } else if (self.getAtom(atom.next_index) == null) {
993 const needed_size = atom.value + code.len;
994 sect.size = needed_size;
995 }
996 } else {
997 atom.allocate(macho_file) catch |err|
998 return macho_file.base.cgFail(nav_index, "failed to allocate atom: {s}", .{@errorName(err)});
999 errdefer self.freeNavMetadata(macho_file, sym_index);
1000
1001 sym.value = 0;
1002 nlist.n_value = 0;
1003 }
1004
1005 if (!sect.isZerofill()) {
1006 const file_offset = sect.offset + atom.value;
1007 macho_file.base.file.?.writePositionalAll(io, code, file_offset) catch |err|
1008 return macho_file.base.cgFail(nav_index, "failed to write output file: {t}", .{err});
1009 }
1010}
1011
1012/// Lowering a TLV on macOS involves two stages:
1013/// 1. first we lower the initializer into appopriate section (__thread_data or __thread_bss)
1014/// 2. next, we create a corresponding threadlocal variable descriptor in __thread_vars
1015fn updateTlv(
1016 self: *ZigObject,
1017 macho_file: *MachO,
1018 zcu: *Zcu,
1019 nav_index: InternPool.Nav.Index,
1020 sym_index: Symbol.Index,
1021 sect_index: u8,
1022 code: []const u8,
1023) !void {
1024 const ip = &zcu.intern_pool;
1025 const nav = ip.getNav(nav_index);
1026
1027 log.debug("updateTlv {f} (0x{x})", .{ nav.fqn.fmt(ip), nav_index });
1028
1029 // 1. Lower TLV initializer
1030 const init_sym_index = try self.createTlvInitializer(
1031 macho_file,
1032 nav.fqn.toSlice(ip),
1033 zcu.navAlignment(nav_index),
1034 sect_index,
1035 code,
1036 );
1037
1038 // 2. Create TLV descriptor
1039 try self.createTlvDescriptor(macho_file, sym_index, init_sym_index, nav.fqn.toSlice(ip));
1040}
1041
1042fn createTlvInitializer(
1043 self: *ZigObject,
1044 macho_file: *MachO,
1045 name: []const u8,
1046 alignment: Atom.Alignment,
1047 sect_index: u8,
1048 code: []const u8,
1049) !Symbol.Index {
1050 const gpa = macho_file.base.comp.gpa;
1051 const sym_name = try std.fmt.allocPrint(gpa, "{s}$tlv$init", .{name});
1052 defer gpa.free(sym_name);
1053 const string = try self.addString(gpa, sym_name);
1054
1055 const sym_index = try self.newSymbolWithAtom(gpa, string, macho_file);
1056 const sym = &self.symbols.items[sym_index];
1057 const nlist = &self.symtab.items(.nlist)[sym.nlist_idx];
1058 const atom = sym.getAtom(macho_file).?;
1059 sym.out_n_sect = sect_index;
1060 atom.out_n_sect = sect_index;
1061 atom.setAlive(true);
1062 atom.alignment = alignment;
1063 atom.size = code.len;
1064 nlist.n_sect = sect_index + 1;
1065 self.symtab.items(.size)[sym.nlist_idx] = code.len;
1066
1067 const slice = macho_file.sections.slice();
1068 const header = slice.items(.header)[sect_index];
1069
1070 const gop = try self.tlv_initializers.getOrPut(gpa, atom.atom_index);
1071 assert(!gop.found_existing); // TODO incremental updates
1072 gop.value_ptr.* = .{ .symbol_index = sym_index };
1073
1074 // We only store the data for the TLV if it's non-zerofill.
1075 if (!header.isZerofill()) {
1076 gop.value_ptr.data = try gpa.dupe(u8, code);
1077 }
1078
1079 return sym_index;
1080}
1081
1082fn createTlvDescriptor(
1083 self: *ZigObject,
1084 macho_file: *MachO,
1085 sym_index: Symbol.Index,
1086 init_sym_index: Symbol.Index,
1087 name: []const u8,
1088) !void {
1089 const gpa = macho_file.base.comp.gpa;
1090
1091 const sym = &self.symbols.items[sym_index];
1092 const nlist = &self.symtab.items(.nlist)[sym.nlist_idx];
1093 const atom = sym.getAtom(macho_file).?;
1094 const alignment = Atom.Alignment.fromNonzeroByteUnits(@alignOf(u64));
1095 const size: u64 = @sizeOf(u64) * 3;
1096
1097 const sect_index = macho_file.getSectionByName("__DATA", "__thread_vars") orelse
1098 try macho_file.addSection("__DATA", "__thread_vars", .{
1099 .flags = macho.S_THREAD_LOCAL_VARIABLES,
1100 });
1101 sym.out_n_sect = sect_index;
1102 atom.out_n_sect = sect_index;
1103
1104 sym.value = 0;
1105 sym.name = try self.addString(gpa, name);
1106 atom.setAlive(true);
1107 atom.name = sym.name;
1108 nlist.n_strx = sym.name.pos;
1109 nlist.n_sect = sect_index + 1;
1110 nlist.n_type = .{ .bits = .{ .ext = false, .type = .sect, .pext = false, .is_stab = 0 } };
1111 nlist.n_value = 0;
1112 self.symtab.items(.size)[sym.nlist_idx] = size;
1113
1114 atom.alignment = alignment;
1115 atom.size = size;
1116
1117 const tlv_bootstrap_index = try self.getGlobalSymbol(macho_file, "_tlv_bootstrap", null);
1118 try atom.addReloc(macho_file, .{
1119 .tag = .@"extern",
1120 .offset = 0,
1121 .target = tlv_bootstrap_index,
1122 .addend = 0,
1123 .type = .unsigned,
1124 .meta = .{
1125 .pcrel = false,
1126 .has_subtractor = false,
1127 .length = 3,
1128 .symbolnum = @intCast(tlv_bootstrap_index),
1129 },
1130 });
1131 try atom.addReloc(macho_file, .{
1132 .tag = .@"extern",
1133 .offset = 16,
1134 .target = init_sym_index,
1135 .addend = 0,
1136 .type = .unsigned,
1137 .meta = .{
1138 .pcrel = false,
1139 .has_subtractor = false,
1140 .length = 3,
1141 .symbolnum = @intCast(init_sym_index),
1142 },
1143 });
1144}
1145
1146fn getNavOutputSection(
1147 self: *ZigObject,
1148 macho_file: *MachO,
1149 zcu: *Zcu,
1150 nav_index: InternPool.Nav.Index,
1151 code: []const u8,
1152) error{OutOfMemory}!u8 {
1153 _ = self;
1154 const ip = &zcu.intern_pool;
1155 const nav = ip.getNav(nav_index);
1156 const nav_val: Value = .fromInterned(nav.resolved.?.value);
1157 if (ip.isFunctionType(nav_val.typeOf(zcu).toIntern())) return macho_file.zig_text_sect_index.?;
1158 if (nav.resolved.?.@"threadlocal" and macho_file.base.comp.config.any_non_single_threaded) {
1159 for (code) |byte| {
1160 if (byte != 0) break;
1161 } else return macho_file.getSectionByName("__DATA", "__thread_bss") orelse try macho_file.addSection(
1162 "__DATA",
1163 "__thread_bss",
1164 .{ .flags = macho.S_THREAD_LOCAL_ZEROFILL },
1165 );
1166 return macho_file.getSectionByName("__DATA", "__thread_data") orelse try macho_file.addSection(
1167 "__DATA",
1168 "__thread_data",
1169 .{ .flags = macho.S_THREAD_LOCAL_REGULAR },
1170 );
1171 }
1172 if (nav.resolved.?.@"const") return macho_file.zig_const_sect_index.?;
1173 if (nav_val.isUndef(zcu))
1174 return switch (zcu.navFileScope(nav_index).mod.?.optimize_mode) {
1175 .debug, .safe => macho_file.zig_data_sect_index.?,
1176 .fast, .small => macho_file.zig_bss_sect_index.?,
1177 };
1178 for (code) |byte| {
1179 if (byte != 0) break;
1180 } else return macho_file.zig_bss_sect_index.?;
1181 return macho_file.zig_data_sect_index.?;
1182}
1183
1184fn lowerConst(
1185 self: *ZigObject,
1186 macho_file: *MachO,
1187 pt: Zcu.PerThread,
1188 name: []const u8,
1189 val: Value,
1190 required_alignment: Atom.Alignment,
1191 output_section_index: u8,
1192) !link.File.SymbolId {
1193 const gpa = macho_file.base.comp.gpa;
1194
1195 var aw: std.Io.Writer.Allocating = .init(gpa);
1196 defer aw.deinit();
1197
1198 const name_str = try self.addString(gpa, name);
1199 const sym_index = try self.newSymbolWithAtom(gpa, name_str, macho_file);
1200
1201 codegen.generateSymbol(
1202 &macho_file.base,
1203 pt,
1204 val,
1205 &aw.writer,
1206 .{ .atom_index = @fromBackingInt(@intCast(sym_index)) },
1207 ) catch |err| switch (err) {
1208 error.WriteFailed => return error.OutOfMemory,
1209 else => |e| return e,
1210 };
1211 const code = aw.written();
1212
1213 const sym = &self.symbols.items[sym_index];
1214 sym.out_n_sect = output_section_index;
1215
1216 const nlist = &self.symtab.items(.nlist)[sym.nlist_idx];
1217 nlist.n_sect = output_section_index + 1;
1218 self.symtab.items(.size)[sym.nlist_idx] = code.len;
1219
1220 const atom = sym.getAtom(macho_file).?;
1221 atom.setAlive(true);
1222 atom.alignment = required_alignment;
1223 atom.size = code.len;
1224 atom.out_n_sect = output_section_index;
1225
1226 try atom.allocate(macho_file);
1227 // TODO rename and re-audit this method
1228 errdefer self.freeNavMetadata(macho_file, sym_index);
1229
1230 const sect = macho_file.sections.items(.header)[output_section_index];
1231 const file_offset = sect.offset + atom.value;
1232 try macho_file.pwriteAll(code, file_offset);
1233
1234 return @fromBackingInt(@intCast(sym_index));
1235}
1236
1237pub fn updateExports(
1238 self: *ZigObject,
1239 macho_file: *MachO,
1240 pt: Zcu.PerThread,
1241 export_indices: []const Zcu.Export.Index,
1242) link.Error!void {
1243 const tracy = trace(@src());
1244 defer tracy.end();
1245
1246 const zcu = pt.zcu;
1247 const gpa = macho_file.base.comp.gpa;
1248
1249 // Delete all existing exports first
1250 for (self.navs.values()) |*metadata| {
1251 for (metadata.exports.items) |nlist_index| {
1252 const nlist = &self.symtab.items(.nlist)[nlist_index];
1253 self.symtab.items(.size)[nlist_index] = 0;
1254 _ = self.globals_lookup.remove(nlist.n_strx);
1255 // TODO actually remove the export
1256 // const sym_index = macho_file.globals.get(nlist.n_strx).?;
1257 // const sym = &self.symbols.items[sym_index];
1258 // if (sym.file == self.index) {
1259 // sym.* = .{};
1260 // }
1261 nlist.* = MachO.null_sym;
1262 }
1263 metadata.exports.clearRetainingCapacity();
1264 }
1265 for (self.uavs.values()) |*metadata| {
1266 for (metadata.exports.items) |nlist_index| {
1267 const nlist = &self.symtab.items(.nlist)[nlist_index];
1268 self.symtab.items(.size)[nlist_index] = 0;
1269 _ = self.globals_lookup.remove(nlist.n_strx);
1270 // TODO actually remove the export
1271 // const sym_index = macho_file.globals.get(nlist.n_strx).?;
1272 // const sym = &self.symbols.items[sym_index];
1273 // if (sym.file == self.index) {
1274 // sym.* = .{};
1275 // }
1276 nlist.* = MachO.null_sym;
1277 }
1278 metadata.exports.clearRetainingCapacity();
1279 }
1280
1281 for (export_indices) |export_index| {
1282 const exp = export_index.ptr(zcu);
1283
1284 const metadata = switch (exp.exported) {
1285 .nav => |nav| blk: {
1286 _ = try self.getOrCreateMetadataForNav(macho_file, nav);
1287 break :blk self.navs.getPtr(nav).?;
1288 },
1289 .uav => |uav| self.uavs.getPtr(uav) orelse blk: {
1290 _ = try self.lowerUav(macho_file, pt, uav, .none);
1291 break :blk self.uavs.getPtr(uav).?;
1292 },
1293 };
1294 const sym_index = metadata.symbol_index;
1295 const nlist_idx = self.symbols.items[sym_index].nlist_idx;
1296 const nlist = self.symtab.items(.nlist)[nlist_idx];
1297
1298 if (exp.opts.section.unwrap()) |section_name| {
1299 if (!section_name.eqlSlice("__text", &zcu.intern_pool)) {
1300 try zcu.failed_exports.ensureUnusedCapacity(zcu.gpa, 1);
1301 zcu.failed_exports.putAssumeCapacityNoClobber(export_index, try Zcu.ErrorMsg.create(
1302 gpa,
1303 exp.src,
1304 "Unimplemented: ExportOptions.section",
1305 .{},
1306 ));
1307 continue;
1308 }
1309 }
1310 if (exp.opts.linkage == .link_once) {
1311 try zcu.failed_exports.putNoClobber(zcu.gpa, export_index, try Zcu.ErrorMsg.create(
1312 gpa,
1313 exp.src,
1314 "Unimplemented: GlobalLinkage.link_once",
1315 .{},
1316 ));
1317 continue;
1318 }
1319
1320 const exp_name = exp.opts.name.toSlice(&zcu.intern_pool);
1321 const global_nlist_index = try self.getGlobalSymbol(macho_file, exp_name, null);
1322 try metadata.exports.append(gpa, global_nlist_index);
1323
1324 const global_nlist = &self.symtab.items(.nlist)[global_nlist_index];
1325 const atom_index = self.symtab.items(.atom)[nlist_idx];
1326 const global_sym = &self.symbols.items[global_nlist_index];
1327 global_nlist.n_value = nlist.n_value;
1328 global_nlist.n_sect = nlist.n_sect;
1329 global_nlist.n_type = .{ .bits = .{ .ext = true, .type = .sect, .pext = false, .is_stab = 0 } };
1330 self.symtab.items(.size)[global_nlist_index] = self.symtab.items(.size)[nlist_idx];
1331 self.symtab.items(.atom)[global_nlist_index] = atom_index;
1332 global_sym.atom_ref = .{ .index = atom_index, .file = self.index };
1333
1334 switch (exp.opts.linkage) {
1335 .internal => {
1336 // Symbol should be hidden, or in MachO lingo, private extern.
1337 global_nlist.n_type.bits.pext = true;
1338 global_sym.visibility = .hidden;
1339 },
1340 .strong => {
1341 global_sym.visibility = .global;
1342 },
1343 .weak => {
1344 // Weak linkage is specified as part of n_desc field.
1345 // Symbol's n_type is like for a symbol with strong linkage.
1346 global_nlist.n_desc.weak_def_or_ref_to_weak = true;
1347 global_sym.visibility = .global;
1348 global_sym.flags.weak = true;
1349 },
1350 else => unreachable,
1351 }
1352 }
1353}
1354
1355fn updateLazySymbol(
1356 self: *ZigObject,
1357 macho_file: *MachO,
1358 pt: Zcu.PerThread,
1359 lazy_sym: link.File.LazySymbol,
1360 symbol_index: Symbol.Index,
1361) !void {
1362 const zcu = pt.zcu;
1363 const gpa = zcu.gpa;
1364
1365 var required_alignment: Atom.Alignment = .none;
1366 var aw: std.Io.Writer.Allocating = .init(gpa);
1367 defer aw.deinit();
1368
1369 const name_str = blk: {
1370 const name = try std.fmt.allocPrint(gpa, "__lazy_{s}_{f}", .{
1371 @tagName(lazy_sym.kind),
1372 Type.fromInterned(lazy_sym.ty).fmt(pt),
1373 });
1374 defer gpa.free(name);
1375 break :blk try self.addString(gpa, name);
1376 };
1377
1378 try codegen.generateLazySymbol(
1379 &macho_file.base,
1380 pt,
1381 lazy_sym,
1382 &required_alignment,
1383 &aw.writer,
1384 .none,
1385 .{ .atom_index = @fromBackingInt(@intCast(symbol_index)) },
1386 );
1387 const code = aw.written();
1388
1389 const output_section_index = switch (lazy_sym.kind) {
1390 .code => macho_file.zig_text_sect_index.?,
1391 .const_data => macho_file.zig_const_sect_index.?,
1392 };
1393 const sym = &self.symbols.items[symbol_index];
1394 sym.name = name_str;
1395 sym.out_n_sect = output_section_index;
1396
1397 const nlist = &self.symtab.items(.nlist)[sym.nlist_idx];
1398 nlist.n_strx = name_str.pos;
1399 nlist.n_type = .{ .bits = .{ .ext = false, .type = .sect, .pext = false, .is_stab = 0 } };
1400 nlist.n_sect = output_section_index + 1;
1401 self.symtab.items(.size)[sym.nlist_idx] = code.len;
1402
1403 const atom = sym.getAtom(macho_file).?;
1404 atom.setAlive(true);
1405 atom.name = name_str;
1406 atom.alignment = required_alignment;
1407 atom.size = code.len;
1408 atom.out_n_sect = output_section_index;
1409
1410 try atom.allocate(macho_file);
1411 errdefer self.freeNavMetadata(macho_file, symbol_index);
1412
1413 sym.value = 0;
1414 nlist.n_value = 0;
1415
1416 const sect = macho_file.sections.items(.header)[output_section_index];
1417 const file_offset = sect.offset + atom.value;
1418 try macho_file.pwriteAll(code, file_offset);
1419}
1420
1421pub fn updateLineNumber(self: *ZigObject, pt: Zcu.PerThread, ti_id: InternPool.TrackedInst.Index) link.Error!void {
1422 if (self.dwarf) |*dwarf| {
1423 const comp = dwarf.bin_file.comp;
1424 const diags = &comp.link_diags;
1425 dwarf.updateLineNumber(pt.zcu, ti_id) catch |err| switch (err) {
1426 error.OutOfMemory, error.Canceled, error.AlreadyReported => |e| return e,
1427 else => |e| return diags.fail("failed to update dwarf line numbers: {s}", .{@errorName(e)}),
1428 };
1429 }
1430}
1431
1432pub fn getGlobalSymbol(self: *ZigObject, macho_file: *MachO, name: []const u8, lib_name: ?[]const u8) !u32 {
1433 _ = lib_name;
1434 const gpa = macho_file.base.comp.gpa;
1435 const sym_name = try std.fmt.allocPrint(gpa, "_{s}", .{name});
1436 defer gpa.free(sym_name);
1437 const name_str = try self.addString(gpa, sym_name);
1438 const lookup_gop = try self.globals_lookup.getOrPut(gpa, name_str.pos);
1439 if (!lookup_gop.found_existing) {
1440 const sym_index = try self.newSymbol(gpa, name_str, .{});
1441 const sym = &self.symbols.items[sym_index];
1442 lookup_gop.value_ptr.* = sym.nlist_idx;
1443 }
1444 return lookup_gop.value_ptr.*;
1445}
1446
1447const max_trampoline_len = 12;
1448
1449fn trampolineSize(cpu_arch: std.Target.Cpu.Arch) u64 {
1450 const len = switch (cpu_arch) {
1451 .x86_64 => 5, // jmp rel32
1452 else => @panic("TODO implement trampoline size for this CPU arch"),
1453 };
1454 comptime assert(len <= max_trampoline_len);
1455 return len;
1456}
1457
1458fn writeTrampoline(tr_sym: Symbol, target: Symbol, macho_file: *MachO) !void {
1459 const atom = tr_sym.getAtom(macho_file).?;
1460 const header = macho_file.sections.items(.header)[atom.out_n_sect];
1461 const fileoff = header.offset + atom.value;
1462 const source_addr = tr_sym.getAddress(.{}, macho_file);
1463 const target_addr = target.getAddress(.{ .trampoline = false }, macho_file);
1464 var buf: [max_trampoline_len]u8 = undefined;
1465 const out = switch (macho_file.getTarget().cpu.arch) {
1466 .x86_64 => try x86_64.writeTrampolineCode(source_addr, target_addr, &buf),
1467 else => @panic("TODO implement write trampoline for this CPU arch"),
1468 };
1469 return macho_file.pwriteAll(out, fileoff);
1470}
1471
1472pub fn getOrCreateMetadataForNav(
1473 self: *ZigObject,
1474 macho_file: *MachO,
1475 nav_index: InternPool.Nav.Index,
1476) !Symbol.Index {
1477 const gpa = macho_file.base.comp.gpa;
1478 const gop = try self.navs.getOrPut(gpa, nav_index);
1479 if (!gop.found_existing) {
1480 const sym_index = try self.newSymbolWithAtom(gpa, .{}, macho_file);
1481 const sym = &self.symbols.items[sym_index];
1482 if (isThreadlocal(macho_file, nav_index)) {
1483 sym.flags.tlv = true;
1484 }
1485 gop.value_ptr.* = .{ .symbol_index = sym_index };
1486 }
1487 return gop.value_ptr.symbol_index;
1488}
1489
1490pub fn getOrCreateMetadataForLazySymbol(
1491 self: *ZigObject,
1492 macho_file: *MachO,
1493 pt: Zcu.PerThread,
1494 lazy_sym: link.File.LazySymbol,
1495) !Symbol.Index {
1496 const gop = try self.lazy_syms.getOrPut(pt.zcu.gpa, lazy_sym.ty);
1497 errdefer _ = if (!gop.found_existing) self.lazy_syms.pop();
1498 if (!gop.found_existing) gop.value_ptr.* = .{};
1499 const symbol_index_ptr, const state_ptr = switch (lazy_sym.kind) {
1500 .code => .{ &gop.value_ptr.text_symbol_index, &gop.value_ptr.text_state },
1501 .const_data => .{ &gop.value_ptr.const_symbol_index, &gop.value_ptr.const_state },
1502 };
1503 switch (state_ptr.*) {
1504 .unused => symbol_index_ptr.* = try self.newSymbolWithAtom(pt.zcu.gpa, .{}, macho_file),
1505 .pending_flush => return symbol_index_ptr.*,
1506 .flushed => {},
1507 }
1508 state_ptr.* = .pending_flush;
1509 const symbol_index = symbol_index_ptr.*;
1510 // anyerror needs to be deferred until flush
1511 if (lazy_sym.ty != .anyerror_type) try self.updateLazySymbol(macho_file, pt, lazy_sym, symbol_index);
1512 return symbol_index;
1513}
1514
1515fn isThreadlocal(macho_file: *MachO, nav_index: InternPool.Nav.Index) bool {
1516 if (!macho_file.base.comp.config.any_non_single_threaded)
1517 return false;
1518 const ip = &macho_file.base.comp.zcu.?.intern_pool;
1519 return ip.getNav(nav_index).resolved.?.@"threadlocal";
1520}
1521
1522fn addAtom(self: *ZigObject, allocator: Allocator) !Atom.Index {
1523 try self.atoms.ensureUnusedCapacity(allocator, 1);
1524 try self.atoms_extra.ensureUnusedCapacity(allocator, @sizeOf(Atom.Extra));
1525 return self.addAtomAssumeCapacity();
1526}
1527
1528fn addAtomAssumeCapacity(self: *ZigObject) Atom.Index {
1529 const atom_index: Atom.Index = @intCast(self.atoms.items.len);
1530 const atom = self.atoms.addOneAssumeCapacity();
1531 atom.* = .{
1532 .file = self.index,
1533 .atom_index = atom_index,
1534 .extra = self.addAtomExtraAssumeCapacity(.{}),
1535 };
1536 return atom_index;
1537}
1538
1539pub fn getAtom(self: *ZigObject, atom_index: Atom.Index) ?*Atom {
1540 if (atom_index == 0) return null;
1541 assert(atom_index < self.atoms.items.len);
1542 return &self.atoms.items[atom_index];
1543}
1544
1545pub fn getAtoms(self: *ZigObject) []const Atom.Index {
1546 return self.atoms_indexes.items;
1547}
1548
1549fn addAtomExtra(self: *ZigObject, allocator: Allocator, extra: Atom.Extra) !u32 {
1550 const field = @typeInfo(Atom.Extra).@"struct".field_names;
1551 try self.atoms_extra.ensureUnusedCapacity(allocator, field.len);
1552 return self.addAtomExtraAssumeCapacity(extra);
1553}
1554
1555fn addAtomExtraAssumeCapacity(self: *ZigObject, extra: Atom.Extra) u32 {
1556 const index = @as(u32, @intCast(self.atoms_extra.items.len));
1557 const info = @typeInfo(Atom.Extra).@"struct";
1558 const field_names = info.field_names;
1559 const field_types = info.field_types;
1560 inline for (field_names, field_types) |field_name, field_type| {
1561 self.atoms_extra.appendAssumeCapacity(switch (field_type) {
1562 u32 => @field(extra, field_name),
1563 else => @compileError("bad field type"),
1564 });
1565 }
1566 return index;
1567}
1568
1569pub fn getAtomExtra(self: ZigObject, index: u32) Atom.Extra {
1570 const info = @typeInfo(Atom.Extra).@"struct";
1571 const field_names = info.field_names;
1572 const field_types = info.field_types;
1573 var i: usize = index;
1574 var result: Atom.Extra = undefined;
1575 inline for (field_names, field_types) |field_name, field_type| {
1576 @field(result, field_name) = switch (field_type) {
1577 u32 => self.atoms_extra.items[i],
1578 else => @compileError("bad field type"),
1579 };
1580 i += 1;
1581 }
1582 return result;
1583}
1584
1585pub fn setAtomExtra(self: *ZigObject, index: u32, extra: Atom.Extra) void {
1586 assert(index > 0);
1587 const info = @typeInfo(Atom.Extra).@"struct";
1588 const field_names = info.field_names;
1589 const field_types = info.field_types;
1590 inline for (field_names, field_types, 0..) |field_name, field_type, i| {
1591 self.atoms_extra.items[index + i] = switch (field_type) {
1592 u32 => @field(extra, field_name),
1593 else => @compileError("bad field type"),
1594 };
1595 }
1596}
1597
1598fn addSymbol(self: *ZigObject, allocator: Allocator) !Symbol.Index {
1599 try self.symbols.ensureUnusedCapacity(allocator, 1);
1600 return self.addSymbolAssumeCapacity();
1601}
1602
1603fn addSymbolAssumeCapacity(self: *ZigObject) Symbol.Index {
1604 const index: Symbol.Index = @intCast(self.symbols.items.len);
1605 const symbol = self.symbols.addOneAssumeCapacity();
1606 symbol.* = .{ .file = self.index };
1607 return index;
1608}
1609
1610pub fn getSymbolRef(self: ZigObject, index: Symbol.Index, macho_file: *MachO) MachO.Ref {
1611 const global_index = self.globals.items[index];
1612 if (macho_file.resolver.get(global_index)) |ref| return ref;
1613 return .{ .index = index, .file = self.index };
1614}
1615
1616pub fn addSymbolExtra(self: *ZigObject, allocator: Allocator, extra: Symbol.Extra) !u32 {
1617 const fields = @typeInfo(Symbol.Extra).@"struct".field_names;
1618 try self.symbols_extra.ensureUnusedCapacity(allocator, fields.len);
1619 return self.addSymbolExtraAssumeCapacity(extra);
1620}
1621
1622fn addSymbolExtraAssumeCapacity(self: *ZigObject, extra: Symbol.Extra) u32 {
1623 const index = @as(u32, @intCast(self.symbols_extra.items.len));
1624 const info = @typeInfo(Symbol.Extra).@"struct";
1625 const field_names = info.field_names;
1626 const field_types = info.field_types;
1627 inline for (field_names, field_types) |field_name, field_type| {
1628 self.symbols_extra.appendAssumeCapacity(switch (field_type) {
1629 u32 => @field(extra, field_name),
1630 else => @compileError("bad field type"),
1631 });
1632 }
1633 return index;
1634}
1635
1636pub fn getSymbolExtra(self: ZigObject, index: u32) Symbol.Extra {
1637 const info = @typeInfo(Symbol.Extra).@"struct";
1638 const field_names = info.field_names;
1639 const field_types = info.field_types;
1640 var i: usize = index;
1641 var result: Symbol.Extra = undefined;
1642 inline for (field_names, field_types) |field_name, field_type| {
1643 @field(result, field_name) = switch (field_type) {
1644 u32 => self.symbols_extra.items[i],
1645 else => @compileError("bad field type"),
1646 };
1647 i += 1;
1648 }
1649 return result;
1650}
1651
1652pub fn setSymbolExtra(self: *ZigObject, index: u32, extra: Symbol.Extra) void {
1653 const info = @typeInfo(Symbol.Extra).@"struct";
1654 const field_names = info.field_names;
1655 const field_types = info.field_types;
1656 inline for (field_names, field_types, 0..) |field_name, field_type, i| {
1657 self.symbols_extra.items[index + i] = switch (field_type) {
1658 u32 => @field(extra, field_name),
1659 else => @compileError("bad field type"),
1660 };
1661 }
1662}
1663
1664fn addString(self: *ZigObject, allocator: Allocator, string: []const u8) !MachO.String {
1665 const off = try self.strtab.insert(allocator, string);
1666 return .{ .pos = off, .len = @intCast(string.len + 1) };
1667}
1668
1669pub fn getString(self: ZigObject, string: MachO.String) [:0]const u8 {
1670 if (string.len == 0) return "";
1671 return self.strtab.buffer.items[string.pos..][0 .. string.len - 1 :0];
1672}
1673
1674pub fn asFile(self: *ZigObject) File {
1675 return .{ .zig_object = self };
1676}
1677
1678pub fn fmtSymtab(self: *ZigObject, macho_file: *MachO) std.fmt.Alt(Format, Format.symtab) {
1679 return .{ .data = .{
1680 .self = self,
1681 .macho_file = macho_file,
1682 } };
1683}
1684
1685const Format = struct {
1686 self: *ZigObject,
1687 macho_file: *MachO,
1688
1689 fn symtab(f: Format, w: *Writer) Writer.Error!void {
1690 try w.writeAll(" symbols\n");
1691 const self = f.self;
1692 const macho_file = f.macho_file;
1693 for (self.symbols.items, 0..) |sym, i| {
1694 const ref = self.getSymbolRef(@intCast(i), macho_file);
1695 if (ref.getFile(macho_file) == null) {
1696 // TODO any better way of handling this?
1697 try w.print(" {s} : unclaimed\n", .{sym.getName(macho_file)});
1698 } else {
1699 try w.print(" {f}\n", .{ref.getSymbol(macho_file).?.fmt(macho_file)});
1700 }
1701 }
1702 }
1703
1704 fn atoms(f: Format, w: *Writer) Writer.Error!void {
1705 const self = f.self;
1706 const macho_file = f.macho_file;
1707 try w.writeAll(" atoms\n");
1708 for (self.getAtoms()) |atom_index| {
1709 const atom = self.getAtom(atom_index) orelse continue;
1710 try w.print(" {f}\n", .{atom.fmt(macho_file)});
1711 }
1712 }
1713};
1714
1715pub fn fmtAtoms(self: *ZigObject, macho_file: *MachO) std.fmt.Alt(Format, Format.atoms) {
1716 return .{ .data = .{
1717 .self = self,
1718 .macho_file = macho_file,
1719 } };
1720}
1721
1722const AvMetadata = struct {
1723 symbol_index: Symbol.Index,
1724 /// A list of all exports aliases of this Av.
1725 exports: std.ArrayList(Symbol.Index) = .empty,
1726};
1727
1728const LazySymbolMetadata = struct {
1729 const State = enum { unused, pending_flush, flushed };
1730 text_symbol_index: Symbol.Index = undefined,
1731 const_symbol_index: Symbol.Index = undefined,
1732 text_state: State = .unused,
1733 const_state: State = .unused,
1734};
1735
1736const TlvInitializer = struct {
1737 symbol_index: Symbol.Index,
1738 data: []const u8 = &[0]u8{},
1739
1740 fn deinit(tlv_init: *TlvInitializer, allocator: Allocator) void {
1741 allocator.free(tlv_init.data);
1742 }
1743};
1744
1745const NavTable = std.array_hash_map.Auto(InternPool.Nav.Index, AvMetadata);
1746const UavTable = std.array_hash_map.Auto(InternPool.Index, AvMetadata);
1747const LazySymbolTable = std.array_hash_map.Auto(InternPool.Index, LazySymbolMetadata);
1748const RelocationTable = std.ArrayList(std.ArrayList(Relocation));
1749const TlvInitializerTable = std.array_hash_map.Auto(Atom.Index, TlvInitializer);
1750
1751const x86_64 = struct {
1752 fn writeTrampolineCode(source_addr: u64, target_addr: u64, buf: *[max_trampoline_len]u8) ![]u8 {
1753 const disp = @as(i64, @intCast(target_addr)) - @as(i64, @intCast(source_addr)) - 5;
1754 var bytes = [_]u8{
1755 0xe9, 0x00, 0x00, 0x00, 0x00, // jmp rel32
1756 };
1757 assert(bytes.len == trampolineSize(.x86_64));
1758 mem.writeInt(i32, bytes[1..][0..4], @intCast(disp), .little);
1759 @memcpy(buf[0..bytes.len], &bytes);
1760 return buf[0..bytes.len];
1761 }
1762};
1763
1764const assert = std.debug.assert;
1765const builtin = @import("builtin");
1766const codegen = @import("../../codegen.zig");
1767const link = @import("../../link.zig");
1768const log = std.log.scoped(.link);
1769const macho = std.macho;
1770const mem = std.mem;
1771const target_util = @import("../../target.zig");
1772const trace = @import("../../tracy.zig").trace;
1773const std = @import("std");
1774const Writer = std.Io.Writer;
1775
1776const Allocator = std.mem.Allocator;
1777const Archive = @import("Archive.zig");
1778const Atom = @import("Atom.zig");
1779const Dwarf = @import("../Dwarf.zig");
1780const File = @import("file.zig").File;
1781const InternPool = @import("../../InternPool.zig");
1782const MachO = @import("../MachO.zig");
1783const Nlist = Object.Nlist;
1784const Zcu = @import("../../Zcu.zig");
1785const Object = @import("Object.zig");
1786const Relocation = @import("Relocation.zig");
1787const Symbol = @import("Symbol.zig");
1788const StringTable = @import("../StringTable.zig");
1789const Type = @import("../../Type.zig");
1790const Value = @import("../../Value.zig");
1791const AnalUnit = InternPool.AnalUnit;
1792const ZigObject = @This();