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