1//! ZigObject encapsulates the state of the incrementally compiled Zig module.
2//! It stores the associated input local and global symbols, allocated atoms,
3//! and any relocations that may have been emitted.
4//! Think about this as fake in-memory Object file for the Zig module.
5
6data: std.ArrayList(u8) = .empty,
7/// Externally owned memory.
8basename: []const u8,
9index: File.Index,
10
11symtab: std.MultiArrayList(ElfSym) = .{},
12strtab: StringTable = .{},
13symbols: std.ArrayList(Symbol) = .empty,
14symbols_extra: std.ArrayList(u32) = .empty,
15symbols_resolver: std.ArrayList(Elf.SymbolResolver.Index) = .empty,
16local_symbols: std.ArrayList(Symbol.Index) = .empty,
17global_symbols: std.ArrayList(Symbol.Index) = .empty,
18globals_lookup: std.AutoHashMapUnmanaged(u32, Symbol.Index) = .empty,
19
20atoms: std.ArrayList(Atom) = .empty,
21atoms_indexes: std.ArrayList(Atom.Index) = .empty,
22atoms_extra: std.ArrayList(u32) = .empty,
23relocs: std.ArrayList(std.ArrayList(elf.Elf64_Rela)) = .empty,
24
25num_dynrelocs: u32 = 0,
26
27output_symtab_ctx: Elf.SymtabCtx = .{},
28output_ar_state: Archive.ArState = .{},
29
30dwarf: ?Dwarf = null,
31
32/// Table of tracked LazySymbols.
33lazy_syms: LazySymbolTable = .{},
34
35/// Table of tracked `Nav`s.
36navs: NavTable = .{},
37
38/// TLS variables indexed by Atom.Index.
39tls_variables: TlsTable = .{},
40
41/// Table of tracked `Uav`s.
42uavs: UavTable = .{},
43
44debug_info_section_dirty: bool = false,
45debug_abbrev_section_dirty: bool = false,
46debug_aranges_section_dirty: bool = false,
47debug_str_section_dirty: bool = false,
48debug_line_section_dirty: bool = false,
49debug_line_str_section_dirty: bool = false,
50debug_loclists_section_dirty: bool = false,
51debug_rnglists_section_dirty: bool = false,
52eh_frame_section_dirty: bool = false,
53
54text_index: ?Symbol.Index = null,
55rodata_index: ?Symbol.Index = null,
56data_relro_index: ?Symbol.Index = null,
57data_index: ?Symbol.Index = null,
58bss_index: ?Symbol.Index = null,
59tdata_index: ?Symbol.Index = null,
60tbss_index: ?Symbol.Index = null,
61eh_frame_index: ?Symbol.Index = null,
62debug_info_index: ?Symbol.Index = null,
63debug_abbrev_index: ?Symbol.Index = null,
64debug_aranges_index: ?Symbol.Index = null,
65debug_str_index: ?Symbol.Index = null,
66debug_line_index: ?Symbol.Index = null,
67debug_line_str_index: ?Symbol.Index = null,
68debug_loclists_index: ?Symbol.Index = null,
69debug_rnglists_index: ?Symbol.Index = null,
70
71pub const global_symbol_bit: u32 = 0x80000000;
72pub const symbol_mask: u32 = 0x7fffffff;
73pub const SHN_ATOM: u16 = 0x100;
74
75const InitOptions = struct {
76 symbol_count_hint: u64,
77 program_code_size_hint: u64,
78};
79
80pub fn init(self: *ZigObject, elf_file: *Elf, options: InitOptions) !void {
81 _ = options;
82 const comp = elf_file.base.comp;
83 const gpa = comp.gpa;
84 const ptr_size = elf_file.ptrWidthBytes();
85
86 try self.atoms.append(gpa, .{ .extra_index = try self.addAtomExtra(gpa, .{}) }); // null input section
87 try self.relocs.append(gpa, .empty); // null relocs section
88 try self.strtab.buffer.append(gpa, 0);
89
90 {
91 const name_off = try self.strtab.insert(gpa, self.basename);
92 const symbol_index = try self.newLocalSymbol(gpa, name_off);
93 const sym = self.symbol(symbol_index);
94 const esym = &self.symtab.items(.elf_sym)[sym.esym_index];
95 esym.st_info = elf.STT_FILE;
96 esym.st_shndx = elf.SHN_ABS;
97 }
98
99 switch (comp.config.debug_format) {
100 .strip => {},
101 .dwarf => |v| {
102 var dwarf = Dwarf.init(&elf_file.base, v);
103
104 const addSectionSymbolWithAtom = struct {
105 fn addSectionSymbolWithAtom(
106 zo: *ZigObject,
107 allocator: Allocator,
108 name: [:0]const u8,
109 alignment: Atom.Alignment,
110 shndx: u32,
111 ) !Symbol.Index {
112 const name_off = try zo.addString(allocator, name);
113 const sym_index = try zo.addSectionSymbol(allocator, name_off, shndx);
114 const sym = zo.symbol(sym_index);
115 const atom_index = try zo.newAtom(allocator, name_off);
116 const atom_ptr = zo.atom(atom_index).?;
117 atom_ptr.alignment = alignment;
118 atom_ptr.output_section_index = shndx;
119 sym.ref = .{ .index = atom_index, .file = zo.index };
120 zo.symtab.items(.shndx)[sym.esym_index] = atom_index;
121 zo.symtab.items(.elf_sym)[sym.esym_index].st_shndx = SHN_ATOM;
122 return sym_index;
123 }
124 }.addSectionSymbolWithAtom;
125
126 if (self.debug_str_index == null) {
127 const osec = try elf_file.addSection(.{
128 .name = try elf_file.insertShString(".debug_str"),
129 .flags = elf.SHF_MERGE | elf.SHF_STRINGS,
130 .entsize = 1,
131 .type = elf.SHT_PROGBITS,
132 .addralign = 1,
133 });
134 self.debug_str_section_dirty = true;
135 self.debug_str_index = try addSectionSymbolWithAtom(self, gpa, ".debug_str", .@"1", osec);
136 }
137
138 if (self.debug_info_index == null) {
139 const osec = try elf_file.addSection(.{
140 .name = try elf_file.insertShString(".debug_info"),
141 .type = elf.SHT_PROGBITS,
142 .addralign = 1,
143 });
144 self.debug_info_section_dirty = true;
145 self.debug_info_index = try addSectionSymbolWithAtom(self, gpa, ".debug_info", .@"1", osec);
146 }
147
148 if (self.debug_abbrev_index == null) {
149 const osec = try elf_file.addSection(.{
150 .name = try elf_file.insertShString(".debug_abbrev"),
151 .type = elf.SHT_PROGBITS,
152 .addralign = 1,
153 });
154 self.debug_abbrev_section_dirty = true;
155 self.debug_abbrev_index = try addSectionSymbolWithAtom(self, gpa, ".debug_abbrev", .@"1", osec);
156 }
157
158 if (self.debug_aranges_index == null) {
159 const osec = try elf_file.addSection(.{
160 .name = try elf_file.insertShString(".debug_aranges"),
161 .type = elf.SHT_PROGBITS,
162 .addralign = 16,
163 });
164 self.debug_aranges_section_dirty = true;
165 self.debug_aranges_index = try addSectionSymbolWithAtom(self, gpa, ".debug_aranges", .@"16", osec);
166 }
167
168 if (self.debug_line_index == null) {
169 const osec = try elf_file.addSection(.{
170 .name = try elf_file.insertShString(".debug_line"),
171 .type = elf.SHT_PROGBITS,
172 .addralign = 1,
173 });
174 self.debug_line_section_dirty = true;
175 self.debug_line_index = try addSectionSymbolWithAtom(self, gpa, ".debug_line", .@"1", osec);
176 }
177
178 if (self.debug_line_str_index == null) {
179 const osec = try elf_file.addSection(.{
180 .name = try elf_file.insertShString(".debug_line_str"),
181 .flags = elf.SHF_MERGE | elf.SHF_STRINGS,
182 .entsize = 1,
183 .type = elf.SHT_PROGBITS,
184 .addralign = 1,
185 });
186 self.debug_line_str_section_dirty = true;
187 self.debug_line_str_index = try addSectionSymbolWithAtom(self, gpa, ".debug_line_str", .@"1", osec);
188 }
189
190 if (self.debug_loclists_index == null) {
191 const osec = try elf_file.addSection(.{
192 .name = try elf_file.insertShString(".debug_loclists"),
193 .type = elf.SHT_PROGBITS,
194 .addralign = 1,
195 });
196 self.debug_loclists_section_dirty = true;
197 self.debug_loclists_index = try addSectionSymbolWithAtom(self, gpa, ".debug_loclists", .@"1", osec);
198 }
199
200 if (self.debug_rnglists_index == null) {
201 const osec = try elf_file.addSection(.{
202 .name = try elf_file.insertShString(".debug_rnglists"),
203 .type = elf.SHT_PROGBITS,
204 .addralign = 1,
205 });
206 self.debug_rnglists_section_dirty = true;
207 self.debug_rnglists_index = try addSectionSymbolWithAtom(self, gpa, ".debug_rnglists", .@"1", osec);
208 }
209
210 if (self.eh_frame_index == null) {
211 const osec = try elf_file.addSection(.{
212 .name = try elf_file.insertShString(".eh_frame"),
213 .type = if (elf_file.getTarget().cpu.arch == .x86_64)
214 elf.SHT_X86_64_UNWIND
215 else
216 elf.SHT_PROGBITS,
217 .flags = elf.SHF_ALLOC,
218 .addralign = ptr_size,
219 });
220 self.eh_frame_section_dirty = true;
221 self.eh_frame_index = try addSectionSymbolWithAtom(self, gpa, ".eh_frame", Atom.Alignment.fromNonzeroByteUnits(ptr_size), osec);
222 }
223
224 try dwarf.initMetadata();
225 self.dwarf = dwarf;
226 },
227 .code_view => unreachable,
228 }
229}
230
231pub fn deinit(self: *ZigObject, allocator: Allocator) void {
232 self.data.deinit(allocator);
233 self.symtab.deinit(allocator);
234 self.strtab.deinit(allocator);
235 self.symbols.deinit(allocator);
236 self.symbols_extra.deinit(allocator);
237 self.symbols_resolver.deinit(allocator);
238 self.local_symbols.deinit(allocator);
239 self.global_symbols.deinit(allocator);
240 self.globals_lookup.deinit(allocator);
241 self.atoms.deinit(allocator);
242 self.atoms_indexes.deinit(allocator);
243 self.atoms_extra.deinit(allocator);
244 for (self.relocs.items) |*list| {
245 list.deinit(allocator);
246 }
247 self.relocs.deinit(allocator);
248
249 for (self.navs.values()) |*meta| {
250 meta.exports.deinit(allocator);
251 }
252 self.navs.deinit(allocator);
253
254 self.lazy_syms.deinit(allocator);
255
256 for (self.uavs.values()) |*meta| {
257 meta.exports.deinit(allocator);
258 }
259 self.uavs.deinit(allocator);
260 self.tls_variables.deinit(allocator);
261
262 if (self.dwarf) |*dwarf| {
263 dwarf.deinit();
264 }
265}
266
267pub fn flush(self: *ZigObject, elf_file: *Elf, tid: Zcu.PerThread.Id) !void {
268 // Handle any lazy symbols that were emitted by incremental compilation.
269 if (self.lazy_syms.getPtr(.anyerror_type)) |metadata| {
270 const active = elf_file.base.comp.zcu.?.activate(tid);
271 defer active.deactivate();
272 const pt = active.pt;
273
274 // Most lazy symbols can be updated on first use, but
275 // anyerror needs to wait for everything to be flushed.
276 if (metadata.text_state != .unused) try self.updateLazySymbol(
277 elf_file,
278 pt,
279 .{ .kind = .code, .ty = .anyerror_type },
280 metadata.text_symbol_index,
281 );
282 if (metadata.rodata_state != .unused) try self.updateLazySymbol(
283 elf_file,
284 pt,
285 .{ .kind = .const_data, .ty = .anyerror_type },
286 metadata.rodata_symbol_index,
287 );
288 }
289 for (self.lazy_syms.values()) |*metadata| {
290 if (metadata.text_state != .unused) metadata.text_state = .flushed;
291 if (metadata.rodata_state != .unused) metadata.rodata_state = .flushed;
292 }
293
294 if (build_options.enable_logging) {
295 const active = elf_file.base.comp.zcu.?.activate(tid);
296 defer active.deactivate();
297 for (self.navs.keys(), self.navs.values()) |nav_index, meta| {
298 checkNavAllocated(active.pt, nav_index, meta);
299 }
300 for (self.uavs.keys(), self.uavs.values()) |uav_index, meta| {
301 checkUavAllocated(active.pt, uav_index, meta);
302 }
303 }
304
305 if (self.dwarf) |*dwarf| {
306 {
307 const active = elf_file.base.comp.zcu.?.activate(tid);
308 defer active.deactivate();
309 try dwarf.flush(active.pt);
310 }
311
312 const gpa = elf_file.base.comp.gpa;
313 const cpu_arch = elf_file.getTarget().cpu.arch;
314
315 // TODO invert this logic so that we manage the output section with the atom, not the
316 // other way around
317 for ([_]u32{
318 self.debug_info_index.?,
319 self.debug_abbrev_index.?,
320 self.debug_str_index.?,
321 self.debug_aranges_index.?,
322 self.debug_line_index.?,
323 self.debug_line_str_index.?,
324 self.debug_loclists_index.?,
325 self.debug_rnglists_index.?,
326 self.eh_frame_index.?,
327 }, [_]*Dwarf.Section{
328 &dwarf.debug_info.section,
329 &dwarf.debug_abbrev.section,
330 &dwarf.debug_str.section,
331 &dwarf.debug_aranges.section,
332 &dwarf.debug_line.section,
333 &dwarf.debug_line_str.section,
334 &dwarf.debug_loclists.section,
335 &dwarf.debug_rnglists.section,
336 &dwarf.debug_frame.section,
337 }, [_]Dwarf.Section.Index{
338 .debug_info,
339 .debug_abbrev,
340 .debug_str,
341 .debug_aranges,
342 .debug_line,
343 .debug_line_str,
344 .debug_loclists,
345 .debug_rnglists,
346 .debug_frame,
347 }) |sym_index, sect, sect_index| {
348 const sym = self.symbol(sym_index);
349 const atom_ptr = self.atom(sym.ref.index).?;
350 if (!atom_ptr.alive) continue;
351
352 const relocs = &self.relocs.items[atom_ptr.relocsShndx().?];
353 for (sect.units.items) |*unit| {
354 try relocs.ensureUnusedCapacity(gpa, unit.cross_unit_relocs.items.len +
355 unit.cross_section_relocs.items.len);
356 for (unit.cross_unit_relocs.items) |reloc| {
357 const target_unit = sect.getUnit(reloc.target_unit);
358 const r_offset = unit.off + reloc.source_off;
359 const r_addend: i64 = @intCast(target_unit.off + reloc.target_off + (if (reloc.target_entry.unwrap()) |target_entry|
360 target_unit.header_len + target_unit.getEntry(target_entry).assertNonEmpty(target_unit, sect, dwarf).off
361 else
362 0));
363 const r_type = relocation.dwarf.crossSectionRelocType(dwarf.format, cpu_arch);
364 atom_ptr.addRelocAssumeCapacity(.{
365 .r_offset = r_offset,
366 .r_addend = r_addend,
367 .r_info = (@as(u64, @intCast(sym_index)) << 32) | r_type,
368 }, self);
369 }
370 for (unit.cross_section_relocs.items) |reloc| {
371 const target_sym_index = switch (reloc.target_sec) {
372 .debug_abbrev => self.debug_abbrev_index.?,
373 .debug_aranges => self.debug_aranges_index.?,
374 .debug_frame => self.eh_frame_index.?,
375 .debug_info => self.debug_info_index.?,
376 .debug_line => self.debug_line_index.?,
377 .debug_line_str => self.debug_line_str_index.?,
378 .debug_loclists => self.debug_loclists_index.?,
379 .debug_rnglists => self.debug_rnglists_index.?,
380 .debug_str => self.debug_str_index.?,
381 };
382 const target_sec = switch (reloc.target_sec) {
383 inline else => |target_sec| &@field(dwarf, @tagName(target_sec)).section,
384 };
385 const target_unit = target_sec.getUnit(reloc.target_unit);
386 const r_offset = unit.off + reloc.source_off;
387 const r_addend: i64 = @intCast(target_unit.off + reloc.target_off + (if (reloc.target_entry.unwrap()) |target_entry|
388 target_unit.header_len + target_unit.getEntry(target_entry).assertNonEmpty(target_unit, sect, dwarf).off
389 else
390 0));
391 const r_type = relocation.dwarf.crossSectionRelocType(dwarf.format, cpu_arch);
392 atom_ptr.addRelocAssumeCapacity(.{
393 .r_offset = r_offset,
394 .r_addend = r_addend,
395 .r_info = (@as(u64, @intCast(target_sym_index)) << 32) | r_type,
396 }, self);
397 }
398
399 for (unit.entries.items) |*entry| {
400 const entry_off = unit.off + unit.header_len + entry.off;
401
402 try relocs.ensureUnusedCapacity(gpa, entry.cross_entry_relocs.items.len +
403 entry.cross_unit_relocs.items.len + entry.cross_section_relocs.items.len +
404 entry.external_relocs.items.len);
405 for (entry.cross_entry_relocs.items) |reloc| {
406 const r_offset = entry_off + reloc.source_off;
407 const r_addend: i64 = @intCast(unit.off + reloc.target_off + (if (reloc.target_entry.unwrap()) |target_entry|
408 unit.header_len + unit.getEntry(target_entry).assertNonEmpty(unit, sect, dwarf).off
409 else
410 0));
411 const r_type = relocation.dwarf.crossSectionRelocType(dwarf.format, cpu_arch);
412 atom_ptr.addRelocAssumeCapacity(.{
413 .r_offset = r_offset,
414 .r_addend = r_addend,
415 .r_info = (@as(u64, @intCast(sym_index)) << 32) | r_type,
416 }, self);
417 }
418 for (entry.cross_unit_relocs.items) |reloc| {
419 const target_unit = sect.getUnit(reloc.target_unit);
420 const r_offset = entry_off + reloc.source_off;
421 const r_addend: i64 = @intCast(target_unit.off + reloc.target_off + (if (reloc.target_entry.unwrap()) |target_entry|
422 target_unit.header_len + target_unit.getEntry(target_entry).assertNonEmpty(target_unit, sect, dwarf).off
423 else
424 0));
425 const r_type = relocation.dwarf.crossSectionRelocType(dwarf.format, cpu_arch);
426 atom_ptr.addRelocAssumeCapacity(.{
427 .r_offset = r_offset,
428 .r_addend = r_addend,
429 .r_info = (@as(u64, @intCast(sym_index)) << 32) | r_type,
430 }, self);
431 }
432 for (entry.cross_section_relocs.items) |reloc| {
433 const target_sym_index = switch (reloc.target_sec) {
434 .debug_abbrev => self.debug_abbrev_index.?,
435 .debug_aranges => self.debug_aranges_index.?,
436 .debug_frame => self.eh_frame_index.?,
437 .debug_info => self.debug_info_index.?,
438 .debug_line => self.debug_line_index.?,
439 .debug_line_str => self.debug_line_str_index.?,
440 .debug_loclists => self.debug_loclists_index.?,
441 .debug_rnglists => self.debug_rnglists_index.?,
442 .debug_str => self.debug_str_index.?,
443 };
444 const target_sec = switch (reloc.target_sec) {
445 inline else => |target_sec| &@field(dwarf, @tagName(target_sec)).section,
446 };
447 const target_unit = target_sec.getUnit(reloc.target_unit);
448 const r_offset = entry_off + reloc.source_off;
449 const r_addend: i64 = @intCast(target_unit.off + reloc.target_off + (if (reloc.target_entry.unwrap()) |target_entry|
450 target_unit.header_len + target_unit.getEntry(target_entry).assertNonEmpty(target_unit, sect, dwarf).off
451 else
452 0));
453 const r_type = relocation.dwarf.crossSectionRelocType(dwarf.format, cpu_arch);
454 atom_ptr.addRelocAssumeCapacity(.{
455 .r_offset = r_offset,
456 .r_addend = r_addend,
457 .r_info = (@as(u64, @intCast(target_sym_index)) << 32) | r_type,
458 }, self);
459 }
460 for (entry.external_relocs.items) |reloc| {
461 const target_sym = self.symbol(@backingInt(reloc.target_sym));
462 const r_offset = entry_off + reloc.source_off;
463 const r_addend: i64 = @intCast(reloc.target_off);
464 const r_type = relocation.dwarf.externalRelocType(target_sym.*, sect_index, dwarf.address_size, cpu_arch);
465 atom_ptr.addRelocAssumeCapacity(.{
466 .r_offset = r_offset,
467 .r_addend = r_addend,
468 .r_info = (@as(u64, @intCast(@backingInt(reloc.target_sym))) << 32) | r_type,
469 }, self);
470 }
471 }
472 }
473 }
474
475 self.debug_abbrev_section_dirty = false;
476 self.debug_aranges_section_dirty = false;
477 self.debug_rnglists_section_dirty = false;
478 self.debug_str_section_dirty = false;
479 }
480
481 // The point of flush() is to commit changes, so in theory, nothing should
482 // be dirty after this. However, it is possible for some things to remain
483 // dirty because they fail to be written in the event of compile errors,
484 // such as debug_line_header_dirty and debug_info_header_dirty.
485 assert(!self.debug_abbrev_section_dirty);
486 assert(!self.debug_aranges_section_dirty);
487 assert(!self.debug_rnglists_section_dirty);
488 assert(!self.debug_str_section_dirty);
489}
490
491fn newSymbol(self: *ZigObject, allocator: Allocator, name_off: u32, st_bind: u4) !Symbol.Index {
492 try self.symtab.ensureUnusedCapacity(allocator, 1);
493 try self.symbols.ensureUnusedCapacity(allocator, 1);
494 try self.symbols_extra.ensureUnusedCapacity(allocator, @sizeOf(Symbol.Extra));
495
496 const index = self.addSymbolAssumeCapacity();
497 const sym = &self.symbols.items[index];
498 sym.name_offset = name_off;
499 sym.extra_index = self.addSymbolExtraAssumeCapacity(.{});
500
501 const esym_idx: u32 = @intCast(self.symtab.addOneAssumeCapacity());
502 const esym = ElfSym{ .elf_sym = .{
503 .st_value = 0,
504 .st_name = name_off,
505 .st_info = @as(u8, @intCast(st_bind)) << 4,
506 .st_other = 0,
507 .st_size = 0,
508 .st_shndx = 0,
509 } };
510 self.symtab.set(index, esym);
511 sym.esym_index = esym_idx;
512
513 return index;
514}
515
516fn newLocalSymbol(self: *ZigObject, allocator: Allocator, name_off: u32) !Symbol.Index {
517 try self.local_symbols.ensureUnusedCapacity(allocator, 1);
518 const fake_index: Symbol.Index = @intCast(self.local_symbols.items.len);
519 const index = try self.newSymbol(allocator, name_off, elf.STB_LOCAL);
520 self.local_symbols.appendAssumeCapacity(index);
521 return fake_index;
522}
523
524fn newGlobalSymbol(self: *ZigObject, allocator: Allocator, name_off: u32) !Symbol.Index {
525 try self.global_symbols.ensureUnusedCapacity(allocator, 1);
526 try self.symbols_resolver.ensureUnusedCapacity(allocator, 1);
527 const fake_index: Symbol.Index = @intCast(self.global_symbols.items.len);
528 const index = try self.newSymbol(allocator, name_off, elf.STB_GLOBAL);
529 self.global_symbols.appendAssumeCapacity(index);
530 self.symbols_resolver.addOneAssumeCapacity().* = 0;
531 return fake_index | global_symbol_bit;
532}
533
534fn newAtom(self: *ZigObject, allocator: Allocator, name_off: u32) !Atom.Index {
535 try self.atoms.ensureUnusedCapacity(allocator, 1);
536 try self.atoms_extra.ensureUnusedCapacity(allocator, @sizeOf(Atom.Extra));
537 try self.atoms_indexes.ensureUnusedCapacity(allocator, 1);
538 try self.relocs.ensureUnusedCapacity(allocator, 1);
539
540 const index = self.addAtomAssumeCapacity();
541 self.atoms_indexes.appendAssumeCapacity(index);
542 const atom_ptr = self.atom(index).?;
543 atom_ptr.name_offset = name_off;
544
545 const relocs_index: u32 = @intCast(self.relocs.items.len);
546 self.relocs.addOneAssumeCapacity().* = .empty;
547 atom_ptr.relocs_section_index = relocs_index;
548
549 return index;
550}
551
552fn newSymbolWithAtom(self: *ZigObject, allocator: Allocator, name_off: u32) !Symbol.Index {
553 const atom_index = try self.newAtom(allocator, name_off);
554 const sym_index = try self.newLocalSymbol(allocator, name_off);
555 const sym = self.symbol(sym_index);
556 sym.ref = .{ .index = atom_index, .file = self.index };
557 self.symtab.items(.shndx)[sym.esym_index] = atom_index;
558 self.symtab.items(.elf_sym)[sym.esym_index].st_shndx = SHN_ATOM;
559 return sym_index;
560}
561
562/// TODO actually create fake input shdrs and return that instead.
563pub fn inputShdr(self: *ZigObject, atom_index: Atom.Index, elf_file: *Elf) elf.Elf64_Shdr {
564 const atom_ptr = self.atom(atom_index) orelse return Elf.null_shdr;
565 const shndx = atom_ptr.output_section_index;
566 var shdr = elf_file.sections.items(.shdr)[shndx];
567 shdr.sh_addr = 0;
568 shdr.sh_offset = 0;
569 shdr.sh_size = atom_ptr.size;
570 shdr.sh_addralign = atom_ptr.alignment.toByteUnits() orelse 1;
571 return shdr;
572}
573
574pub fn resolveSymbols(self: *ZigObject, elf_file: *Elf) !void {
575 const gpa = elf_file.base.comp.gpa;
576
577 for (self.global_symbols.items, 0..) |index, i| {
578 const global = &self.symbols.items[index];
579 const esym = global.elfSym(elf_file);
580 const shndx = self.symtab.items(.shndx)[global.esym_index];
581 const resolv = &self.symbols_resolver.items[i];
582 const gop = try elf_file.resolver.getOrPut(gpa, .{
583 .index = @intCast(i | global_symbol_bit),
584 .file = self.index,
585 }, elf_file);
586 if (!gop.found_existing) {
587 gop.ref.* = .{ .index = 0, .file = 0 };
588 }
589 resolv.* = gop.index;
590
591 if (esym.st_shndx == elf.SHN_UNDEF) continue;
592 if (esym.st_shndx != elf.SHN_ABS and esym.st_shndx != elf.SHN_COMMON) {
593 assert(esym.st_shndx == SHN_ATOM);
594 const atom_ptr = self.atom(shndx) orelse continue;
595 if (!atom_ptr.alive) continue;
596 }
597 if (elf_file.symbol(gop.ref.*) == null) {
598 gop.ref.* = .{ .index = @intCast(i | global_symbol_bit), .file = self.index };
599 continue;
600 }
601
602 if (self.asFile().symbolRank(esym, false) < elf_file.symbol(gop.ref.*).?.symbolRank(elf_file)) {
603 gop.ref.* = .{ .index = @intCast(i | global_symbol_bit), .file = self.index };
604 }
605 }
606}
607
608pub fn claimUnresolved(self: *ZigObject, elf_file: *Elf) void {
609 for (self.global_symbols.items, 0..) |index, i| {
610 const global = &self.symbols.items[index];
611 const esym = self.symtab.items(.elf_sym)[index];
612 if (esym.st_shndx != elf.SHN_UNDEF) continue;
613 if (elf_file.symbol(self.resolveSymbol(@intCast(i | global_symbol_bit), elf_file)) != null) continue;
614
615 const is_import = blk: {
616 if (!elf_file.isEffectivelyDynLib()) break :blk false;
617 const vis: elf.STV = @fromBackingInt(@intCast(@as(u3, @truncate(esym.st_other))));
618 if (vis == .HIDDEN) break :blk false;
619 break :blk true;
620 };
621
622 global.value = 0;
623 global.ref = .{ .index = 0, .file = 0 };
624 global.esym_index = @intCast(index);
625 global.file_index = self.index;
626 global.version_index = if (is_import) .LOCAL else elf_file.default_sym_version;
627 global.flags.import = is_import;
628
629 const idx = self.symbols_resolver.items[i];
630 elf_file.resolver.values.items[idx - 1] = .{ .index = @intCast(i | global_symbol_bit), .file = self.index };
631 }
632}
633
634pub fn claimUnresolvedRelocatable(self: ZigObject, elf_file: *Elf) void {
635 for (self.global_symbols.items, 0..) |index, i| {
636 const global = &self.symbols.items[index];
637 const esym = self.symtab.items(.elf_sym)[index];
638 if (esym.st_shndx != elf.SHN_UNDEF) continue;
639 if (elf_file.symbol(self.resolveSymbol(@intCast(i | global_symbol_bit), elf_file)) != null) continue;
640
641 global.value = 0;
642 global.ref = .{ .index = 0, .file = 0 };
643 global.esym_index = @intCast(index);
644 global.file_index = self.index;
645
646 const idx = self.symbols_resolver.items[i];
647 elf_file.resolver.values.items[idx - 1] = .{ .index = @intCast(i | global_symbol_bit), .file = self.index };
648 }
649}
650
651pub fn scanRelocs(self: *ZigObject, elf_file: *Elf, undefs: anytype) !void {
652 const gpa = elf_file.base.comp.gpa;
653 for (self.atoms_indexes.items) |atom_index| {
654 const atom_ptr = self.atom(atom_index) orelse continue;
655 if (!atom_ptr.alive) continue;
656 const shdr = atom_ptr.inputShdr(elf_file);
657 if (shdr.sh_flags & elf.SHF_ALLOC == 0) continue;
658 if (shdr.sh_type == elf.SHT_NOBITS) continue;
659 if (atom_ptr.scanRelocsRequiresCode(elf_file)) {
660 // TODO ideally we don't have to fetch the code here.
661 // Perhaps it would make sense to save the code until flush where we
662 // would free all of generated code?
663 const code = try self.codeAlloc(elf_file, atom_index);
664 defer gpa.free(code);
665 try atom_ptr.scanRelocs(elf_file, code, undefs);
666 } else try atom_ptr.scanRelocs(elf_file, null, undefs);
667 }
668}
669
670pub fn markLive(self: *ZigObject, elf_file: *Elf) void {
671 for (self.global_symbols.items, 0..) |index, i| {
672 const global = self.symbols.items[index];
673 const esym = self.symtab.items(.elf_sym)[index];
674 if (esym.st_bind() == elf.STB_WEAK) continue;
675
676 const ref = self.resolveSymbol(@intCast(i | global_symbol_bit), elf_file);
677 const sym = elf_file.symbol(ref) orelse continue;
678 const file = sym.file(elf_file).?;
679 const should_keep = esym.st_shndx == elf.SHN_UNDEF or
680 (esym.st_shndx == elf.SHN_COMMON and global.elfSym(elf_file).st_shndx != elf.SHN_COMMON);
681 if (should_keep and !file.isAlive()) {
682 file.setAlive();
683 file.markLive(elf_file);
684 }
685 }
686}
687
688pub fn markImportsExports(self: *ZigObject, elf_file: *Elf) void {
689 for (0..self.global_symbols.items.len) |i| {
690 const ref = self.resolveSymbol(@intCast(i | global_symbol_bit), elf_file);
691 const sym = elf_file.symbol(ref) orelse continue;
692 const file = sym.file(elf_file).?;
693 if (sym.version_index == elf.Versym.LOCAL) continue;
694 const vis: elf.STV = @fromBackingInt(@intCast(@as(u3, @truncate(sym.elfSym(elf_file).st_other))));
695 if (vis == .HIDDEN) continue;
696 if (file == .shared_object and !sym.isAbs(elf_file)) {
697 sym.flags.import = true;
698 continue;
699 }
700 if (file.index() == self.index) {
701 sym.flags.@"export" = true;
702 if (elf_file.isEffectivelyDynLib() and vis != .PROTECTED) {
703 sym.flags.import = true;
704 }
705 }
706 }
707}
708
709pub fn checkDuplicates(self: *ZigObject, dupes: anytype, elf_file: *Elf) error{OutOfMemory}!void {
710 const gpa = elf_file.base.comp.gpa;
711
712 for (self.global_symbols.items, 0..) |index, i| {
713 const esym = self.symtab.items(.elf_sym)[index];
714 const shndx = self.symtab.items(.shndx)[index];
715 const ref = self.resolveSymbol(@intCast(i | global_symbol_bit), elf_file);
716 const ref_sym = elf_file.symbol(ref) orelse continue;
717 const ref_file = ref_sym.file(elf_file).?;
718
719 if (self.index == ref_file.index() or
720 esym.st_shndx == elf.SHN_UNDEF or
721 esym.st_bind() == elf.STB_WEAK or
722 esym.st_shndx == elf.SHN_COMMON) continue;
723
724 if (esym.st_shndx == SHN_ATOM) {
725 const atom_ptr = self.atom(shndx) orelse continue;
726 if (!atom_ptr.alive) continue;
727 }
728
729 const gop = try dupes.getOrPut(gpa, self.symbols_resolver.items[i]);
730 if (!gop.found_existing) {
731 gop.value_ptr.* = .empty;
732 }
733 try gop.value_ptr.append(elf_file.base.comp.gpa, self.index);
734 }
735}
736
737/// This is just a temporary helper function that allows us to re-read what we wrote to file into a buffer.
738/// We need this so that we can write to an archive.
739/// TODO implement writing ZigObject data directly to a buffer instead.
740pub fn readFileContents(self: *ZigObject, elf_file: *Elf) !void {
741 const comp = elf_file.base.comp;
742 const gpa = comp.gpa;
743 const io = comp.io;
744 const shsize: u64 = switch (elf_file.ptr_width) {
745 .p32 => @sizeOf(elf.Elf32_Shdr),
746 .p64 => @sizeOf(elf.Elf64_Shdr),
747 };
748 var end_pos: u64 = elf_file.shdr_table_offset.? + elf_file.sections.items(.shdr).len * shsize;
749 for (elf_file.sections.items(.shdr)) |shdr| {
750 if (shdr.sh_type == elf.SHT_NOBITS) continue;
751 end_pos = @max(end_pos, shdr.sh_offset + shdr.sh_size);
752 }
753 const size = std.math.cast(usize, end_pos) orelse return error.Overflow;
754 try self.data.resize(gpa, size);
755
756 const amt = try elf_file.base.file.?.readPositionalAll(io, self.data.items, 0);
757 if (amt != size) return error.InputOutput;
758}
759
760pub fn updateArSymtab(self: ZigObject, ar_symtab: *Archive.ArSymtab, elf_file: *Elf) error{OutOfMemory}!void {
761 const gpa = elf_file.base.comp.gpa;
762
763 try ar_symtab.symtab.ensureUnusedCapacity(gpa, self.global_symbols.items.len);
764
765 for (self.global_symbols.items, 0..) |index, i| {
766 const global = self.symbols.items[index];
767 const ref = self.resolveSymbol(@intCast(i | global_symbol_bit), elf_file);
768 const sym = elf_file.symbol(ref).?;
769 assert(sym.file(elf_file).?.index() == self.index);
770 if (global.outputShndx(elf_file) == null) continue;
771
772 const off = try ar_symtab.strtab.insert(gpa, global.name(elf_file));
773 ar_symtab.symtab.appendAssumeCapacity(.{ .off = off, .file_index = self.index });
774 }
775}
776
777pub fn updateArSize(self: *ZigObject) void {
778 self.output_ar_state.size = self.data.items.len;
779}
780
781pub fn writeAr(self: ZigObject, writer: anytype) !void {
782 const name = self.basename;
783 const hdr = Archive.setArHdr(.{
784 .name = if (name.len <= Archive.max_member_name_len)
785 .{ .name = name }
786 else
787 .{ .name_off = self.output_ar_state.name_off },
788 .size = self.data.items.len,
789 });
790 try writer.writeAll(mem.asBytes(&hdr));
791 try writer.writeAll(self.data.items);
792}
793
794pub fn initRelaSections(self: *ZigObject, elf_file: *Elf) !void {
795 const gpa = elf_file.base.comp.gpa;
796 for (self.atoms_indexes.items) |atom_index| {
797 const atom_ptr = self.atom(atom_index) orelse continue;
798 if (!atom_ptr.alive) continue;
799 if (atom_ptr.output_section_index == elf_file.section_indexes.eh_frame) continue;
800 const rela_shndx = atom_ptr.relocsShndx() orelse continue;
801 // TODO this check will become obsolete when we rework our relocs mechanism at the ZigObject level
802 if (self.relocs.items[rela_shndx].items.len == 0) continue;
803 const out_shndx = atom_ptr.output_section_index;
804 const out_shdr = elf_file.sections.items(.shdr)[out_shndx];
805 if (out_shdr.sh_type == elf.SHT_NOBITS) continue;
806 const rela_sect_name = try std.fmt.allocPrintSentinel(gpa, ".rela{s}", .{
807 elf_file.getShString(out_shdr.sh_name),
808 }, 0);
809 defer gpa.free(rela_sect_name);
810 _ = elf_file.sectionByName(rela_sect_name) orelse
811 try elf_file.addRelaShdr(try elf_file.insertShString(rela_sect_name), out_shndx);
812 }
813}
814
815pub fn addAtomsToRelaSections(self: *ZigObject, elf_file: *Elf) !void {
816 const gpa = elf_file.base.comp.gpa;
817 for (self.atoms_indexes.items) |atom_index| {
818 const atom_ptr = self.atom(atom_index) orelse continue;
819 if (!atom_ptr.alive) continue;
820 if (atom_ptr.output_section_index == elf_file.section_indexes.eh_frame) continue;
821 const rela_shndx = atom_ptr.relocsShndx() orelse continue;
822 // TODO this check will become obsolete when we rework our relocs mechanism at the ZigObject level
823 if (self.relocs.items[rela_shndx].items.len == 0) continue;
824 const out_shndx = atom_ptr.output_section_index;
825 const out_shdr = elf_file.sections.items(.shdr)[out_shndx];
826 if (out_shdr.sh_type == elf.SHT_NOBITS) continue;
827 const rela_sect_name = try std.fmt.allocPrintSentinel(gpa, ".rela{s}", .{
828 elf_file.getShString(out_shdr.sh_name),
829 }, 0);
830 defer gpa.free(rela_sect_name);
831 const out_rela_shndx = elf_file.sectionByName(rela_sect_name).?;
832 const out_rela_shdr = &elf_file.sections.items(.shdr)[out_rela_shndx];
833 out_rela_shdr.sh_info = out_shndx;
834 out_rela_shdr.sh_link = elf_file.section_indexes.symtab.?;
835 const atom_list = &elf_file.sections.items(.atom_list)[out_rela_shndx];
836 try atom_list.append(gpa, .{ .index = atom_index, .file = self.index });
837 }
838}
839
840pub fn updateSymtabSize(self: *ZigObject, elf_file: *Elf) !void {
841 for (self.local_symbols.items) |index| {
842 const local = &self.symbols.items[index];
843 if (local.atom(elf_file)) |atom_ptr| if (!atom_ptr.alive) continue;
844 const name = local.name(elf_file);
845 assert(name.len > 0);
846 const esym = local.elfSym(elf_file);
847 switch (esym.st_type()) {
848 elf.STT_SECTION, elf.STT_NOTYPE => continue,
849 else => {},
850 }
851 local.flags.output_symtab = true;
852 local.addExtra(.{ .symtab = self.output_symtab_ctx.nlocals }, elf_file);
853 self.output_symtab_ctx.nlocals += 1;
854 self.output_symtab_ctx.strsize += @as(u32, @intCast(name.len)) + 1;
855 }
856
857 for (self.global_symbols.items, self.symbols_resolver.items) |index, resolv| {
858 const global = &self.symbols.items[index];
859 const ref = elf_file.resolver.values.items[resolv - 1];
860 const ref_sym = elf_file.symbol(ref) orelse continue;
861 if (ref_sym.file(elf_file).?.index() != self.index) continue;
862 if (global.atom(elf_file)) |atom_ptr| if (!atom_ptr.alive) continue;
863 global.flags.output_symtab = true;
864 if (global.isLocal(elf_file)) {
865 global.addExtra(.{ .symtab = self.output_symtab_ctx.nlocals }, elf_file);
866 self.output_symtab_ctx.nlocals += 1;
867 } else {
868 global.addExtra(.{ .symtab = self.output_symtab_ctx.nglobals }, elf_file);
869 self.output_symtab_ctx.nglobals += 1;
870 }
871 self.output_symtab_ctx.strsize += @as(u32, @intCast(global.name(elf_file).len)) + 1;
872 }
873}
874
875pub fn writeSymtab(self: ZigObject, elf_file: *Elf) void {
876 for (self.local_symbols.items) |index| {
877 const local = &self.symbols.items[index];
878 const idx = local.outputSymtabIndex(elf_file) orelse continue;
879 const out_sym = &elf_file.symtab.items[idx];
880 out_sym.st_name = @intCast(elf_file.strtab.items.len);
881 elf_file.strtab.appendSliceAssumeCapacity(local.name(elf_file));
882 elf_file.strtab.appendAssumeCapacity(0);
883 local.setOutputSym(elf_file, out_sym);
884 }
885
886 for (self.global_symbols.items, self.symbols_resolver.items) |index, resolv| {
887 const global = self.symbols.items[index];
888 const ref = elf_file.resolver.values.items[resolv - 1];
889 const ref_sym = elf_file.symbol(ref) orelse continue;
890 if (ref_sym.file(elf_file).?.index() != self.index) continue;
891 const idx = global.outputSymtabIndex(elf_file) orelse continue;
892 const st_name = @as(u32, @intCast(elf_file.strtab.items.len));
893 elf_file.strtab.appendSliceAssumeCapacity(global.name(elf_file));
894 elf_file.strtab.appendAssumeCapacity(0);
895 const out_sym = &elf_file.symtab.items[idx];
896 out_sym.st_name = st_name;
897 global.setOutputSym(elf_file, out_sym);
898 }
899}
900
901/// Returns atom's code.
902/// Caller owns the memory.
903pub fn codeAlloc(self: *ZigObject, elf_file: *Elf, atom_index: Atom.Index) ![]u8 {
904 const comp = elf_file.base.comp;
905 const gpa = comp.gpa;
906 const io = comp.io;
907 const atom_ptr = self.atom(atom_index).?;
908 const file_offset = atom_ptr.offset(elf_file);
909 const size = std.math.cast(usize, atom_ptr.size) orelse return error.Overflow;
910 const code = try gpa.alloc(u8, size);
911 errdefer gpa.free(code);
912 const amt = try elf_file.base.file.?.readPositionalAll(io, code, file_offset);
913 if (amt != code.len) {
914 log.err("fetching code for {s} failed", .{atom_ptr.name(elf_file)});
915 return error.InputOutput;
916 }
917 return code;
918}
919
920pub fn getNavVAddr(
921 self: *ZigObject,
922 elf_file: *Elf,
923 pt: Zcu.PerThread,
924 nav_index: InternPool.Nav.Index,
925 reloc_info: link.File.RelocInfo,
926) !u64 {
927 const zcu = pt.zcu;
928 const ip = &zcu.intern_pool;
929 const nav = ip.getNav(nav_index);
930 log.debug("getNavVAddr {f}({d})", .{ nav.fqn.fmt(ip), nav_index });
931 const this_sym_index = if (nav.getExtern(ip)) |@"extern"| try self.getGlobalSymbol(
932 elf_file,
933 nav.name.toSlice(ip),
934 @"extern".lib_name.toSlice(ip),
935 ) else try self.getOrCreateMetadataForNav(zcu, nav_index);
936 const this_sym = self.symbol(this_sym_index);
937 const vaddr = this_sym.address(.{}, elf_file);
938 switch (reloc_info.parent) {
939 .none => unreachable,
940 .atom_index => |atom_index| {
941 const parent_atom = self.symbol(@backingInt(atom_index)).atom(elf_file).?;
942 const r_type = relocation.encode(.abs, elf_file.getTarget().cpu.arch);
943 try parent_atom.addReloc(elf_file.base.comp.gpa, .{
944 .r_offset = reloc_info.offset,
945 .r_info = (@as(u64, @intCast(this_sym_index)) << 32) | r_type,
946 .r_addend = reloc_info.addend,
947 }, self);
948 },
949 .debug_output => |debug_output| switch (debug_output) {
950 .dwarf => |wip_nav| try wip_nav.infoExternalReloc(.{
951 .source_off = @intCast(reloc_info.offset),
952 .target_sym = @fromBackingInt(@intCast(this_sym_index)),
953 .target_off = reloc_info.addend,
954 }),
955 .none => unreachable,
956 },
957 }
958 return @intCast(vaddr);
959}
960
961pub fn getUavVAddr(
962 self: *ZigObject,
963 elf_file: *Elf,
964 uav: InternPool.Index,
965 reloc_info: link.File.RelocInfo,
966) !u64 {
967 const sym_index = self.uavs.get(uav).?.symbol_index;
968 const sym = self.symbol(sym_index);
969 const vaddr = sym.address(.{}, elf_file);
970 switch (reloc_info.parent) {
971 .none => unreachable,
972 .atom_index => |atom_index| {
973 const parent_atom = self.symbol(@backingInt(atom_index)).atom(elf_file).?;
974 const r_type = relocation.encode(.abs, elf_file.getTarget().cpu.arch);
975 try parent_atom.addReloc(elf_file.base.comp.gpa, .{
976 .r_offset = reloc_info.offset,
977 .r_info = (@as(u64, @intCast(sym_index)) << 32) | r_type,
978 .r_addend = reloc_info.addend,
979 }, self);
980 },
981 .debug_output => |debug_output| switch (debug_output) {
982 .dwarf => |wip_nav| try wip_nav.infoExternalReloc(.{
983 .source_off = @intCast(reloc_info.offset),
984 .target_sym = @fromBackingInt(@intCast(sym_index)),
985 .target_off = reloc_info.addend,
986 }),
987 .none => unreachable,
988 },
989 }
990 return @intCast(vaddr);
991}
992
993pub fn lowerUav(
994 self: *ZigObject,
995 elf_file: *Elf,
996 pt: Zcu.PerThread,
997 uav: InternPool.Index,
998 explicit_alignment: InternPool.Alignment,
999) !link.File.SymbolId {
1000 const zcu = pt.zcu;
1001 const gpa = zcu.gpa;
1002 const val = Value.fromInterned(uav);
1003 const uav_alignment = switch (explicit_alignment) {
1004 .none => val.typeOf(zcu).abiAlignment(zcu),
1005 else => explicit_alignment,
1006 };
1007 if (self.uavs.get(uav)) |metadata| {
1008 assert(metadata.allocated);
1009 const sym = self.symbol(metadata.symbol_index);
1010 const existing_alignment = sym.atom(elf_file).?.alignment;
1011 if (uav_alignment.order(existing_alignment).compare(.lte))
1012 return @fromBackingInt(@intCast(metadata.symbol_index));
1013 }
1014
1015 const osec = if (self.data_relro_index) |sym_index|
1016 self.symbol(sym_index).outputShndx(elf_file).?
1017 else osec: {
1018 const osec = try elf_file.addSection(.{
1019 .name = try elf_file.insertShString(".data.rel.ro"),
1020 .type = elf.SHT_PROGBITS,
1021 .addralign = 1,
1022 .flags = elf.SHF_ALLOC | elf.SHF_WRITE,
1023 });
1024 self.data_relro_index = try self.addSectionSymbol(gpa, try self.addString(gpa, ".data.rel.ro"), osec);
1025 break :osec osec;
1026 };
1027
1028 var name_buf: [32]u8 = undefined;
1029 const name = std.mem.print(&name_buf, "__anon_{d}", .{
1030 @backingInt(uav),
1031 }) catch unreachable;
1032 const sym_index = self.lowerConst(
1033 elf_file,
1034 pt,
1035 name,
1036 val,
1037 uav_alignment,
1038 osec,
1039 ) catch |err| switch (err) {
1040 error.OutOfMemory => |e| return e,
1041 else => |e| return elf_file.base.comp.link_diags.fail(
1042 "failed to lower constant value: {t}",
1043 .{e},
1044 ),
1045 };
1046 try self.uavs.put(gpa, uav, .{
1047 .symbol_index = @backingInt(sym_index),
1048 .allocated = true,
1049 });
1050 return sym_index;
1051}
1052
1053pub fn getOrCreateMetadataForLazySymbol(
1054 self: *ZigObject,
1055 elf_file: *Elf,
1056 pt: Zcu.PerThread,
1057 lazy_sym: link.File.LazySymbol,
1058) !Symbol.Index {
1059 const gop = try self.lazy_syms.getOrPut(pt.zcu.gpa, lazy_sym.ty);
1060 errdefer _ = if (!gop.found_existing) self.lazy_syms.pop();
1061 if (!gop.found_existing) gop.value_ptr.* = .{};
1062 const symbol_index_ptr, const state_ptr = switch (lazy_sym.kind) {
1063 .code => .{ &gop.value_ptr.text_symbol_index, &gop.value_ptr.text_state },
1064 .const_data => .{ &gop.value_ptr.rodata_symbol_index, &gop.value_ptr.rodata_state },
1065 };
1066 switch (state_ptr.*) {
1067 .unused => symbol_index_ptr.* = try self.newSymbolWithAtom(pt.zcu.gpa, 0),
1068 .pending_flush => return symbol_index_ptr.*,
1069 .flushed => {},
1070 }
1071 state_ptr.* = .pending_flush;
1072 const symbol_index = symbol_index_ptr.*;
1073 // anyerror needs to be deferred until flush
1074 if (lazy_sym.ty != .anyerror_type) try self.updateLazySymbol(elf_file, pt, lazy_sym, symbol_index);
1075 return symbol_index;
1076}
1077
1078fn freeNavMetadata(self: *ZigObject, elf_file: *Elf, sym_index: Symbol.Index) void {
1079 const sym = self.symbol(sym_index);
1080 sym.atom(elf_file).?.free(elf_file);
1081 log.debug("adding %{d} to local symbols free list", .{sym_index});
1082 self.symbols.items[sym_index] = .{};
1083 // TODO free GOT entry here
1084}
1085
1086pub fn freeNav(self: *ZigObject, elf_file: *Elf, nav_index: InternPool.Nav.Index) void {
1087 const gpa = elf_file.base.comp.gpa;
1088
1089 log.debug("freeNav ({d})", .{nav_index});
1090
1091 if (self.navs.fetchRemove(nav_index)) |const_kv| {
1092 var kv = const_kv;
1093 const sym_index = kv.value.symbol_index;
1094 self.freeNavMetadata(elf_file, sym_index);
1095 kv.value.exports.deinit(gpa);
1096 }
1097
1098 if (self.dwarf) |*dwarf| {
1099 dwarf.freeNav(nav_index);
1100 }
1101}
1102
1103pub fn getOrCreateMetadataForNav(self: *ZigObject, zcu: *Zcu, nav_index: InternPool.Nav.Index) !Symbol.Index {
1104 const gpa = zcu.gpa;
1105 const ip = &zcu.intern_pool;
1106 const gop = try self.navs.getOrPut(gpa, nav_index);
1107 if (!gop.found_existing) {
1108 const symbol_index = try self.newSymbolWithAtom(gpa, 0);
1109 const sym = self.symbol(symbol_index);
1110 if (ip.getNav(nav_index).resolved.?.@"threadlocal" and zcu.comp.config.any_non_single_threaded) {
1111 sym.flags.is_tls = true;
1112 }
1113 gop.value_ptr.* = .{ .symbol_index = symbol_index };
1114 }
1115 return gop.value_ptr.symbol_index;
1116}
1117
1118fn addSectionSymbol(self: *ZigObject, allocator: Allocator, name_off: u32, shndx: u32) !Symbol.Index {
1119 const index = try self.newLocalSymbol(allocator, name_off);
1120 const sym = self.symbol(index);
1121 const esym = &self.symtab.items(.elf_sym)[sym.esym_index];
1122 esym.st_info |= elf.STT_SECTION;
1123 // TODO create fake shdrs?
1124 // esym.st_shndx = shndx;
1125 sym.output_section_index = shndx;
1126 return index;
1127}
1128
1129fn getNavShdrIndex(
1130 self: *ZigObject,
1131 elf_file: *Elf,
1132 zcu: *Zcu,
1133 nav_index: InternPool.Nav.Index,
1134 sym_index: Symbol.Index,
1135 code: []const u8,
1136) error{OutOfMemory}!u32 {
1137 const gpa = elf_file.base.comp.gpa;
1138 const ptr_size = elf_file.ptrWidthBytes();
1139 const ip = &zcu.intern_pool;
1140 const nav = ip.getNav(nav_index);
1141 const nav_val: Value = .fromInterned(nav.resolved.?.value);
1142 const is_func = ip.isFunctionType(nav_val.typeOf(zcu).toIntern());
1143 if (ip.getNav(nav_index).resolved.?.@"linksection".unwrap()) |@"linksection"| {
1144 const section_name = @"linksection".toSlice(ip);
1145 if (elf_file.sectionByName(section_name)) |osec| {
1146 if (is_func) {
1147 elf_file.sections.items(.shdr)[osec].sh_flags |= elf.SHF_EXECINSTR;
1148 } else {
1149 elf_file.sections.items(.shdr)[osec].sh_flags |= elf.SHF_WRITE;
1150 }
1151 return osec;
1152 }
1153 const osec = try elf_file.addSection(.{
1154 .type = elf.SHT_PROGBITS,
1155 .flags = elf.SHF_ALLOC | @as(u64, if (is_func) elf.SHF_EXECINSTR else elf.SHF_WRITE),
1156 .name = try elf_file.insertShString(section_name),
1157 .addralign = 1,
1158 });
1159 const section_index = try self.addSectionSymbol(gpa, try self.addString(gpa, section_name), osec);
1160 if (std.mem.eql(u8, section_name, ".text")) {
1161 elf_file.sections.items(.shdr)[osec].sh_flags = elf.SHF_ALLOC | elf.SHF_EXECINSTR;
1162 self.text_index = section_index;
1163 } else if (std.mem.startsWith(u8, section_name, ".text.")) {
1164 elf_file.sections.items(.shdr)[osec].sh_flags = elf.SHF_ALLOC | elf.SHF_EXECINSTR;
1165 } else if (std.mem.eql(u8, section_name, ".rodata")) {
1166 elf_file.sections.items(.shdr)[osec].sh_flags = elf.SHF_ALLOC;
1167 self.rodata_index = section_index;
1168 } else if (std.mem.startsWith(u8, section_name, ".rodata.")) {
1169 elf_file.sections.items(.shdr)[osec].sh_flags = elf.SHF_ALLOC;
1170 } else if (std.mem.eql(u8, section_name, ".data.rel.ro")) {
1171 elf_file.sections.items(.shdr)[osec].sh_flags = elf.SHF_ALLOC | elf.SHF_WRITE;
1172 self.data_relro_index = section_index;
1173 } else if (std.mem.eql(u8, section_name, ".data")) {
1174 elf_file.sections.items(.shdr)[osec].sh_flags = elf.SHF_ALLOC | elf.SHF_WRITE;
1175 self.data_index = section_index;
1176 } else if (std.mem.startsWith(u8, section_name, ".data.")) {
1177 elf_file.sections.items(.shdr)[osec].sh_flags = elf.SHF_ALLOC | elf.SHF_WRITE;
1178 } else if (std.mem.eql(u8, section_name, ".bss")) {
1179 const shdr = &elf_file.sections.items(.shdr)[osec];
1180 shdr.sh_type = elf.SHT_NOBITS;
1181 shdr.sh_flags = elf.SHF_ALLOC | elf.SHF_WRITE;
1182 self.bss_index = section_index;
1183 } else if (std.mem.startsWith(u8, section_name, ".bss.")) {
1184 const shdr = &elf_file.sections.items(.shdr)[osec];
1185 shdr.sh_type = elf.SHT_NOBITS;
1186 shdr.sh_flags = elf.SHF_ALLOC | elf.SHF_WRITE;
1187 } else if (std.mem.eql(u8, section_name, ".tdata")) {
1188 elf_file.sections.items(.shdr)[osec].sh_flags = elf.SHF_ALLOC | elf.SHF_WRITE | elf.SHF_TLS;
1189 self.tdata_index = section_index;
1190 } else if (std.mem.startsWith(u8, section_name, ".tdata.")) {
1191 elf_file.sections.items(.shdr)[osec].sh_flags = elf.SHF_ALLOC | elf.SHF_WRITE | elf.SHF_TLS;
1192 } else if (std.mem.eql(u8, section_name, ".tbss")) {
1193 const shdr = &elf_file.sections.items(.shdr)[osec];
1194 shdr.sh_type = elf.SHT_NOBITS;
1195 shdr.sh_flags = elf.SHF_ALLOC | elf.SHF_WRITE | elf.SHF_TLS;
1196 self.tbss_index = section_index;
1197 } else if (std.mem.startsWith(u8, section_name, ".tbss.")) {
1198 const shdr = &elf_file.sections.items(.shdr)[osec];
1199 shdr.sh_type = elf.SHT_NOBITS;
1200 shdr.sh_flags = elf.SHF_ALLOC | elf.SHF_WRITE | elf.SHF_TLS;
1201 } else if (std.mem.eql(u8, section_name, ".eh_frame")) {
1202 const target = &zcu.navFileScope(nav_index).mod.?.resolved_target.result;
1203 const shdr = &elf_file.sections.items(.shdr)[osec];
1204 if (target.cpu.arch == .x86_64) shdr.sh_type = elf.SHT_X86_64_UNWIND;
1205 shdr.sh_flags = elf.SHF_ALLOC;
1206 self.eh_frame_index = section_index;
1207 } else if (std.mem.eql(u8, section_name, ".debug_info")) {
1208 elf_file.sections.items(.shdr)[osec].sh_flags = 0;
1209 self.debug_info_index = section_index;
1210 } else if (std.mem.eql(u8, section_name, ".debug_abbrev")) {
1211 elf_file.sections.items(.shdr)[osec].sh_flags = 0;
1212 self.debug_abbrev_index = section_index;
1213 } else if (std.mem.eql(u8, section_name, ".debug_aranges")) {
1214 elf_file.sections.items(.shdr)[osec].sh_flags = 0;
1215 self.debug_aranges_index = section_index;
1216 } else if (std.mem.eql(u8, section_name, ".debug_str")) {
1217 elf_file.sections.items(.shdr)[osec].sh_flags = 0;
1218 self.debug_str_index = section_index;
1219 } else if (std.mem.eql(u8, section_name, ".debug_line")) {
1220 elf_file.sections.items(.shdr)[osec].sh_flags = 0;
1221 self.debug_line_index = section_index;
1222 } else if (std.mem.eql(u8, section_name, ".debug_line_str")) {
1223 elf_file.sections.items(.shdr)[osec].sh_flags = 0;
1224 self.debug_line_str_index = section_index;
1225 } else if (std.mem.eql(u8, section_name, ".debug_loclists")) {
1226 elf_file.sections.items(.shdr)[osec].sh_flags = 0;
1227 self.debug_loclists_index = section_index;
1228 } else if (std.mem.eql(u8, section_name, ".debug_rnglists")) {
1229 elf_file.sections.items(.shdr)[osec].sh_flags = 0;
1230 self.debug_rnglists_index = section_index;
1231 } else if (std.mem.startsWith(u8, section_name, ".debug")) {
1232 elf_file.sections.items(.shdr)[osec].sh_flags = 0;
1233 } else if (std.mem.eql(u8, section_name, ".preinit_array") or std.mem.startsWith(u8, section_name, ".preinit_array.")) {
1234 const shdr = &elf_file.sections.items(.shdr)[osec];
1235 shdr.sh_type = elf.SHT_PREINIT_ARRAY;
1236 shdr.sh_flags = elf.SHF_ALLOC | elf.SHF_WRITE;
1237 } else if (std.mem.eql(u8, section_name, ".init_array") or std.mem.startsWith(u8, section_name, ".init_array.")) {
1238 const shdr = &elf_file.sections.items(.shdr)[osec];
1239 shdr.sh_type = elf.SHT_INIT_ARRAY;
1240 shdr.sh_flags = elf.SHF_ALLOC | elf.SHF_WRITE;
1241 } else if (std.mem.eql(u8, section_name, ".fini_array") or std.mem.startsWith(u8, section_name, ".fini_array.")) {
1242 const shdr = &elf_file.sections.items(.shdr)[osec];
1243 shdr.sh_type = elf.SHT_FINI_ARRAY;
1244 shdr.sh_flags = elf.SHF_ALLOC | elf.SHF_WRITE;
1245 }
1246 return osec;
1247 }
1248 if (is_func) {
1249 if (self.text_index) |symbol_index|
1250 return self.symbol(symbol_index).outputShndx(elf_file).?;
1251 const osec = try elf_file.addSection(.{
1252 .type = elf.SHT_PROGBITS,
1253 .flags = elf.SHF_ALLOC | elf.SHF_EXECINSTR,
1254 .name = try elf_file.insertShString(".text"),
1255 .addralign = 1,
1256 });
1257 self.text_index = try self.addSectionSymbol(gpa, try self.addString(gpa, ".text"), osec);
1258 return osec;
1259 }
1260 const has_relocs = self.symbol(sym_index).atom(elf_file).?.relocs(elf_file).len > 0;
1261 if (nav.resolved.?.@"threadlocal" and elf_file.base.comp.config.any_non_single_threaded) {
1262 const is_bss = !has_relocs and for (code) |byte| {
1263 if (byte != 0) break false;
1264 } else true;
1265 if (is_bss) {
1266 if (self.tbss_index) |symbol_index|
1267 return self.symbol(symbol_index).outputShndx(elf_file).?;
1268 const osec = try elf_file.addSection(.{
1269 .name = try elf_file.insertShString(".tbss"),
1270 .flags = elf.SHF_ALLOC | elf.SHF_WRITE | elf.SHF_TLS,
1271 .type = elf.SHT_NOBITS,
1272 .addralign = 1,
1273 });
1274 self.tbss_index = try self.addSectionSymbol(gpa, try self.addString(gpa, ".tbss"), osec);
1275 return osec;
1276 }
1277 if (self.tdata_index) |symbol_index|
1278 return self.symbol(symbol_index).outputShndx(elf_file).?;
1279 const osec = try elf_file.addSection(.{
1280 .type = elf.SHT_PROGBITS,
1281 .flags = elf.SHF_ALLOC | elf.SHF_WRITE | elf.SHF_TLS,
1282 .name = try elf_file.insertShString(".tdata"),
1283 .addralign = 1,
1284 });
1285 self.tdata_index = try self.addSectionSymbol(gpa, try self.addString(gpa, ".tdata"), osec);
1286 return osec;
1287 }
1288 if (nav.resolved.?.@"const") {
1289 if (self.data_relro_index) |symbol_index|
1290 return self.symbol(symbol_index).outputShndx(elf_file).?;
1291 const osec = try elf_file.addSection(.{
1292 .name = try elf_file.insertShString(".data.rel.ro"),
1293 .type = elf.SHT_PROGBITS,
1294 .addralign = 1,
1295 .flags = elf.SHF_ALLOC | elf.SHF_WRITE,
1296 });
1297 self.data_relro_index = try self.addSectionSymbol(gpa, try self.addString(gpa, ".data.rel.ro"), osec);
1298 return osec;
1299 }
1300 if (nav_val.isUndef(zcu))
1301 return switch (zcu.navFileScope(nav_index).mod.?.optimize_mode) {
1302 .debug, .safe => {
1303 if (self.data_index) |symbol_index|
1304 return self.symbol(symbol_index).outputShndx(elf_file).?;
1305 const osec = try elf_file.addSection(.{
1306 .name = try elf_file.insertShString(".data"),
1307 .type = elf.SHT_PROGBITS,
1308 .addralign = ptr_size,
1309 .flags = elf.SHF_ALLOC | elf.SHF_WRITE,
1310 });
1311 self.data_index = try self.addSectionSymbol(gpa, try self.addString(gpa, ".data"), osec);
1312 return osec;
1313 },
1314 .fast, .small => {
1315 if (self.bss_index) |symbol_index|
1316 return self.symbol(symbol_index).outputShndx(elf_file).?;
1317 const osec = try elf_file.addSection(.{
1318 .type = elf.SHT_NOBITS,
1319 .flags = elf.SHF_ALLOC | elf.SHF_WRITE,
1320 .name = try elf_file.insertShString(".bss"),
1321 .addralign = 1,
1322 });
1323 self.bss_index = try self.addSectionSymbol(gpa, try self.addString(gpa, ".bss"), osec);
1324 return osec;
1325 },
1326 };
1327 const is_bss = !has_relocs and for (code) |byte| {
1328 if (byte != 0) break false;
1329 } else true;
1330 if (is_bss) {
1331 if (self.bss_index) |symbol_index|
1332 return self.symbol(symbol_index).outputShndx(elf_file).?;
1333 const osec = try elf_file.addSection(.{
1334 .type = elf.SHT_NOBITS,
1335 .flags = elf.SHF_ALLOC | elf.SHF_WRITE,
1336 .name = try elf_file.insertShString(".bss"),
1337 .addralign = 1,
1338 });
1339 self.bss_index = try self.addSectionSymbol(gpa, try self.addString(gpa, ".bss"), osec);
1340 return osec;
1341 }
1342 if (self.data_index) |symbol_index|
1343 return self.symbol(symbol_index).outputShndx(elf_file).?;
1344 const osec = try elf_file.addSection(.{
1345 .name = try elf_file.insertShString(".data"),
1346 .type = elf.SHT_PROGBITS,
1347 .addralign = ptr_size,
1348 .flags = elf.SHF_ALLOC | elf.SHF_WRITE,
1349 });
1350 self.data_index = try self.addSectionSymbol(gpa, try self.addString(gpa, ".data"), osec);
1351 return osec;
1352}
1353
1354fn updateNavCode(
1355 self: *ZigObject,
1356 elf_file: *Elf,
1357 pt: Zcu.PerThread,
1358 nav_index: InternPool.Nav.Index,
1359 sym_index: Symbol.Index,
1360 shdr_index: u32,
1361 code: []const u8,
1362 stt_bits: u8,
1363) link.Error!void {
1364 const zcu = pt.zcu;
1365 const gpa = zcu.gpa;
1366 const comp = elf_file.base.comp;
1367 const io = comp.io;
1368 const ip = &zcu.intern_pool;
1369 const nav = ip.getNav(nav_index);
1370
1371 log.debug("updateNavCode {f}({d})", .{ nav.fqn.fmt(ip), nav_index });
1372
1373 const mod = zcu.navFileScope(nav_index).mod.?;
1374 const target = &mod.resolved_target.result;
1375 const required_alignment = switch (nav.resolved.?.@"align") {
1376 .none => switch (mod.optimize_mode) {
1377 .debug, .safe, .fast => target_util.defaultFunctionAlignment(target),
1378 .small => target_util.minFunctionAlignment(target),
1379 }.maxStrict(Type.fromInterned(nav.resolved.?.type).abiAlignment(zcu)),
1380 else => |a| a.maxStrict(target_util.minFunctionAlignment(target)),
1381 };
1382
1383 const sym = self.symbol(sym_index);
1384 const esym = &self.symtab.items(.elf_sym)[sym.esym_index];
1385 const atom_ptr = sym.atom(elf_file).?;
1386 const name_offset = try self.strtab.insert(gpa, nav.fqn.toSlice(ip));
1387
1388 atom_ptr.alive = true;
1389 atom_ptr.name_offset = name_offset;
1390 atom_ptr.output_section_index = shdr_index;
1391
1392 sym.name_offset = name_offset;
1393 esym.st_name = name_offset;
1394 esym.st_info |= stt_bits;
1395 esym.st_size = code.len;
1396
1397 const old_size = atom_ptr.size;
1398 const old_vaddr = atom_ptr.value;
1399 atom_ptr.alignment = required_alignment;
1400 atom_ptr.size = code.len;
1401
1402 if (old_size > 0 and elf_file.base.child_pid == null) {
1403 const capacity = atom_ptr.capacity(elf_file);
1404 const need_realloc = code.len > capacity or !required_alignment.check(@intCast(atom_ptr.value));
1405 if (need_realloc) {
1406 self.allocateAtom(atom_ptr, true, elf_file) catch |err|
1407 return elf_file.base.cgFail(nav_index, "failed to allocate atom: {s}", .{@errorName(err)});
1408
1409 log.debug("growing {f} from 0x{x} to 0x{x}", .{ nav.fqn.fmt(ip), old_vaddr, atom_ptr.value });
1410 if (old_vaddr != atom_ptr.value) {
1411 sym.value = 0;
1412 esym.st_value = 0;
1413 }
1414 } else if (code.len < old_size) {
1415 // TODO shrink section size
1416 }
1417 } else {
1418 self.allocateAtom(atom_ptr, true, elf_file) catch |err|
1419 return elf_file.base.cgFail(nav_index, "failed to allocate atom: {s}", .{@errorName(err)});
1420
1421 errdefer self.freeNavMetadata(elf_file, sym_index);
1422 sym.value = 0;
1423 esym.st_value = 0;
1424 }
1425
1426 self.navs.getPtr(nav_index).?.allocated = true;
1427
1428 if (elf_file.base.child_pid) |pid| {
1429 switch (builtin.os.tag) {
1430 .linux => {
1431 var code_vec: [1]std.posix.iovec_const = .{.{
1432 .base = code.ptr,
1433 .len = code.len,
1434 }};
1435 var remote_vec: [1]std.posix.iovec_const = .{.{
1436 .base = @as([*]u8, @ptrFromInt(@as(usize, @intCast(sym.address(.{}, elf_file))))),
1437 .len = code.len,
1438 }};
1439 const rc = std.os.linux.process_vm_writev(pid, &code_vec, &remote_vec, 0);
1440 switch (std.os.linux.errno(rc)) {
1441 .SUCCESS => assert(rc == code.len),
1442 else => |errno| log.warn("process_vm_writev failure: {s}", .{@tagName(errno)}),
1443 }
1444 },
1445 else => return elf_file.base.cgFail(nav_index, "ELF hot swap unavailable on host operating system '{s}'", .{@tagName(builtin.os.tag)}),
1446 }
1447 }
1448
1449 const shdr = elf_file.sections.items(.shdr)[shdr_index];
1450 if (shdr.sh_type != elf.SHT_NOBITS) {
1451 const file_offset = atom_ptr.offset(elf_file);
1452 elf_file.base.file.?.writePositionalAll(io, code, file_offset) catch |err|
1453 return elf_file.base.cgFail(nav_index, "failed to write to output file: {t}", .{err});
1454 log.debug("writing {f} from 0x{x} to 0x{x}", .{ nav.fqn.fmt(ip), file_offset, file_offset + code.len });
1455 }
1456}
1457
1458fn updateTlv(
1459 self: *ZigObject,
1460 elf_file: *Elf,
1461 pt: Zcu.PerThread,
1462 nav_index: InternPool.Nav.Index,
1463 sym_index: Symbol.Index,
1464 shndx: u32,
1465 code: []const u8,
1466) link.Error!void {
1467 const zcu = pt.zcu;
1468 const ip = &zcu.intern_pool;
1469 const gpa = zcu.gpa;
1470 const comp = elf_file.base.comp;
1471 const io = comp.io;
1472 const nav = ip.getNav(nav_index);
1473
1474 log.debug("updateTlv {f}({d})", .{ nav.fqn.fmt(ip), nav_index });
1475
1476 const required_alignment = zcu.navAlignment(nav_index);
1477
1478 const sym = self.symbol(sym_index);
1479 const esym = &self.symtab.items(.elf_sym)[sym.esym_index];
1480 const atom_ptr = sym.atom(elf_file).?;
1481 const name_offset = try self.strtab.insert(gpa, nav.fqn.toSlice(ip));
1482
1483 atom_ptr.alive = true;
1484 atom_ptr.name_offset = name_offset;
1485 atom_ptr.output_section_index = shndx;
1486
1487 sym.name_offset = name_offset;
1488 esym.st_name = name_offset;
1489 esym.st_info = elf.STT_TLS;
1490 esym.st_size = code.len;
1491
1492 atom_ptr.alignment = required_alignment;
1493 atom_ptr.size = code.len;
1494
1495 const gop = try self.tls_variables.getOrPut(gpa, atom_ptr.atom_index);
1496 assert(!gop.found_existing); // TODO incremental updates
1497
1498 self.allocateAtom(atom_ptr, true, elf_file) catch |err|
1499 return elf_file.base.cgFail(nav_index, "failed to allocate atom: {s}", .{@errorName(err)});
1500 sym.value = 0;
1501 esym.st_value = 0;
1502
1503 self.navs.getPtr(nav_index).?.allocated = true;
1504
1505 const shdr = elf_file.sections.items(.shdr)[shndx];
1506 if (shdr.sh_type != elf.SHT_NOBITS) {
1507 const file_offset = atom_ptr.offset(elf_file);
1508 elf_file.base.file.?.writePositionalAll(io, code, file_offset) catch |err|
1509 return elf_file.base.cgFail(nav_index, "failed to write to output file: {t}", .{err});
1510 log.debug("writing TLV {s} from 0x{x} to 0x{x}", .{
1511 atom_ptr.name(elf_file),
1512 file_offset,
1513 file_offset + code.len,
1514 });
1515 }
1516}
1517
1518pub fn updateFunc(
1519 self: *ZigObject,
1520 elf_file: *Elf,
1521 pt: Zcu.PerThread,
1522 func_index: InternPool.Index,
1523 mir: *const codegen.AnyMir,
1524) link.Error!void {
1525 const tracy = trace(@src());
1526 defer tracy.end();
1527
1528 const zcu = pt.zcu;
1529 const ip = &zcu.intern_pool;
1530 const gpa = elf_file.base.comp.gpa;
1531 const func = zcu.funcInfo(func_index);
1532
1533 log.debug("updateFunc {f}({d})", .{ ip.getNav(func.owner_nav).fqn.fmt(ip), func.owner_nav });
1534
1535 const sym_index = try self.getOrCreateMetadataForNav(zcu, func.owner_nav);
1536 self.atom(self.symbol(sym_index).ref.index).?.freeRelocs(self);
1537
1538 var aw: std.Io.Writer.Allocating = .init(gpa);
1539 defer aw.deinit();
1540
1541 var debug_wip_nav = if (self.dwarf) |*dwarf| try dwarf.initWipNav(
1542 pt,
1543 func.owner_nav,
1544 @fromBackingInt(@intCast(sym_index)),
1545 ) else null;
1546 defer if (debug_wip_nav) |*wip_nav| wip_nav.deinit();
1547
1548 codegen.emitFunction(
1549 &elf_file.base,
1550 pt,
1551 func_index,
1552 @fromBackingInt(@intCast(sym_index)),
1553 mir,
1554 &aw.writer,
1555 if (debug_wip_nav) |*dn| .{ .dwarf = dn } else .none,
1556 ) catch |err| switch (err) {
1557 error.WriteFailed => return error.OutOfMemory,
1558 else => |e| return e,
1559 };
1560 const code = aw.written();
1561
1562 const shndx = try self.getNavShdrIndex(elf_file, zcu, func.owner_nav, sym_index, code);
1563 log.debug("setting shdr({x},{s}) for {f}", .{
1564 shndx,
1565 elf_file.getShString(elf_file.sections.items(.shdr)[shndx].sh_name),
1566 ip.getNav(func.owner_nav).fqn.fmt(ip),
1567 });
1568 const old_rva, const old_alignment = blk: {
1569 const atom_ptr = self.atom(self.symbol(sym_index).ref.index).?;
1570 break :blk .{ atom_ptr.value, atom_ptr.alignment };
1571 };
1572 try self.updateNavCode(elf_file, pt, func.owner_nav, sym_index, shndx, code, elf.STT_FUNC);
1573 const new_rva, const new_alignment = blk: {
1574 const atom_ptr = self.atom(self.symbol(sym_index).ref.index).?;
1575 break :blk .{ atom_ptr.value, atom_ptr.alignment };
1576 };
1577
1578 if (debug_wip_nav) |*wip_nav| self.dwarf.?.finishWipNavFunc(pt, func.owner_nav, code.len, wip_nav) catch |err|
1579 return elf_file.base.cgFail(func.owner_nav, "failed to finish dwarf function: {s}", .{@errorName(err)});
1580
1581 // Exports will be updated by `Zcu.processExports` after the update.
1582
1583 if (old_rva != new_rva and old_rva > 0) {
1584 // If we had to reallocate the function, we re-use the existing slot for a trampoline.
1585 // In the rare case that the function has been further overaligned we skip creating a
1586 // trampoline and update all symbols referring this function.
1587 if (old_alignment.order(new_alignment) == .lt) {
1588 @panic("TODO update all symbols referring this function");
1589 }
1590
1591 // Create a trampoline to the new location at `old_rva`.
1592 if (!self.symbol(sym_index).flags.has_trampoline) {
1593 const name = try std.fmt.allocPrint(gpa, "{s}$trampoline", .{
1594 self.symbol(sym_index).name(elf_file),
1595 });
1596 defer gpa.free(name);
1597 const osec = if (self.text_index) |sect_sym_index|
1598 self.symbol(sect_sym_index).outputShndx(elf_file).?
1599 else osec: {
1600 const osec = try elf_file.addSection(.{
1601 .name = try elf_file.insertShString(".text"),
1602 .flags = elf.SHF_ALLOC | elf.SHF_EXECINSTR,
1603 .type = elf.SHT_PROGBITS,
1604 .addralign = 1,
1605 });
1606 self.text_index = try self.addSectionSymbol(gpa, try self.addString(gpa, ".text"), osec);
1607 break :osec osec;
1608 };
1609 const name_off = try self.addString(gpa, name);
1610 const tr_size = trampolineSize(elf_file.getTarget().cpu.arch);
1611 const tr_sym_index = try self.newSymbolWithAtom(gpa, name_off);
1612 const tr_sym = self.symbol(tr_sym_index);
1613 const tr_esym = &self.symtab.items(.elf_sym)[tr_sym.esym_index];
1614 tr_esym.st_info |= elf.STT_OBJECT;
1615 tr_esym.st_size = tr_size;
1616 const tr_atom_ptr = tr_sym.atom(elf_file).?;
1617 tr_atom_ptr.value = old_rva;
1618 tr_atom_ptr.alive = true;
1619 tr_atom_ptr.alignment = old_alignment;
1620 tr_atom_ptr.output_section_index = osec;
1621 tr_atom_ptr.size = tr_size;
1622 const target_sym = self.symbol(sym_index);
1623 target_sym.addExtra(.{ .trampoline = tr_sym_index }, elf_file);
1624 target_sym.flags.has_trampoline = true;
1625 }
1626 const target_sym = self.symbol(sym_index);
1627 writeTrampoline(self.symbol(target_sym.extra(elf_file).trampoline).*, target_sym.*, elf_file) catch |err|
1628 return elf_file.base.cgFail(func.owner_nav, "failed to write trampoline: {s}", .{@errorName(err)});
1629 }
1630}
1631
1632pub fn updateNav(
1633 self: *ZigObject,
1634 elf_file: *Elf,
1635 pt: Zcu.PerThread,
1636 nav_index: InternPool.Nav.Index,
1637) link.Error!void {
1638 const tracy = trace(@src());
1639 defer tracy.end();
1640
1641 const zcu = pt.zcu;
1642 const ip = &zcu.intern_pool;
1643 const nav = ip.getNav(nav_index);
1644
1645 log.debug("updateNav {f}({d})", .{ nav.fqn.fmt(ip), nav_index });
1646
1647 switch (ip.indexToKey(nav.resolved.?.value)) {
1648 else => {},
1649 .@"extern" => |@"extern"| {
1650 const sym_index = try self.getGlobalSymbol(
1651 elf_file,
1652 nav.name.toSlice(ip),
1653 @"extern".lib_name.toSlice(ip),
1654 );
1655 if (nav.resolved.?.@"threadlocal" and elf_file.base.comp.config.any_non_single_threaded) {
1656 self.symbol(sym_index).flags.is_tls = true;
1657 }
1658 if (self.dwarf) |*dwarf| {
1659 var debug_wip_nav = try dwarf.initWipNav(pt, nav_index, @fromBackingInt(@intCast(sym_index)));
1660 defer debug_wip_nav.deinit();
1661 dwarf.finishWipNav(pt, nav_index, &debug_wip_nav) catch |err| switch (err) {
1662 error.OutOfMemory, error.Canceled, error.AlreadyReported => |e| return e,
1663 else => |e| return elf_file.base.cgFail(nav_index, "failed to finish dwarf nav: {s}", .{@errorName(e)}),
1664 };
1665 }
1666 return;
1667 },
1668 }
1669
1670 if (Type.fromInterned(nav.resolved.?.type).hasRuntimeBits(zcu)) {
1671 const sym_index = try self.getOrCreateMetadataForNav(zcu, nav_index);
1672 self.symbol(sym_index).atom(elf_file).?.freeRelocs(self);
1673
1674 var aw: std.Io.Writer.Allocating = .init(zcu.gpa);
1675 defer aw.deinit();
1676
1677 var debug_wip_nav = if (self.dwarf) |*dwarf| try dwarf.initWipNav(pt, nav_index, @fromBackingInt(@intCast(sym_index))) else null;
1678 defer if (debug_wip_nav) |*wip_nav| wip_nav.deinit();
1679
1680 codegen.generateSymbol(
1681 &elf_file.base,
1682 pt,
1683 .fromInterned(nav.resolved.?.value),
1684 &aw.writer,
1685 .{ .atom_index = @fromBackingInt(@intCast(sym_index)) },
1686 ) catch |err| switch (err) {
1687 error.WriteFailed => return error.OutOfMemory,
1688 else => |e| return e,
1689 };
1690 const code = aw.written();
1691
1692 const shndx = try self.getNavShdrIndex(elf_file, zcu, nav_index, sym_index, code);
1693 log.debug("setting shdr({x},{s}) for {f}", .{
1694 shndx,
1695 elf_file.getShString(elf_file.sections.items(.shdr)[shndx].sh_name),
1696 nav.fqn.fmt(ip),
1697 });
1698 if (elf_file.sections.items(.shdr)[shndx].sh_flags & elf.SHF_TLS != 0)
1699 try self.updateTlv(elf_file, pt, nav_index, sym_index, shndx, code)
1700 else
1701 try self.updateNavCode(elf_file, pt, nav_index, sym_index, shndx, code, elf.STT_OBJECT);
1702
1703 if (debug_wip_nav) |*wip_nav| self.dwarf.?.finishWipNav(pt, nav_index, wip_nav) catch |err| switch (err) {
1704 error.OutOfMemory, error.Canceled, error.AlreadyReported => |e| return e,
1705 else => |e| return elf_file.base.cgFail(nav_index, "failed to finish dwarf nav: {s}", .{@errorName(e)}),
1706 };
1707 } else if (self.dwarf) |*dwarf| try dwarf.updateComptimeNav(pt, nav_index);
1708
1709 // Exports will be updated by `Zcu.processExports` after the update.
1710}
1711
1712pub fn updateContainerType(
1713 self: *ZigObject,
1714 pt: Zcu.PerThread,
1715 ty: InternPool.Index,
1716 success: bool,
1717) !void {
1718 const tracy = trace(@src());
1719 defer tracy.end();
1720
1721 if (self.dwarf) |*dwarf| try dwarf.updateContainerType(pt, ty, success);
1722}
1723
1724fn updateLazySymbol(
1725 self: *ZigObject,
1726 elf_file: *Elf,
1727 pt: Zcu.PerThread,
1728 sym: link.File.LazySymbol,
1729 symbol_index: Symbol.Index,
1730) !void {
1731 const zcu = pt.zcu;
1732 const gpa = zcu.gpa;
1733
1734 var required_alignment: InternPool.Alignment = .none;
1735 var aw: std.Io.Writer.Allocating = .init(gpa);
1736 defer aw.deinit();
1737
1738 const name_str_index = blk: {
1739 const name = try std.fmt.allocPrint(gpa, "__lazy_{s}_{f}", .{
1740 @tagName(sym.kind),
1741 Type.fromInterned(sym.ty).fmt(pt),
1742 });
1743 defer gpa.free(name);
1744 break :blk try self.strtab.insert(gpa, name);
1745 };
1746
1747 codegen.generateLazySymbol(
1748 &elf_file.base,
1749 pt,
1750 sym,
1751 &required_alignment,
1752 &aw.writer,
1753 .none,
1754 .{ .atom_index = @fromBackingInt(@intCast(symbol_index)) },
1755 ) catch |err| switch (err) {
1756 error.WriteFailed => return error.OutOfMemory,
1757 else => |e| return e,
1758 };
1759 const code = aw.written();
1760
1761 const output_section_index = switch (sym.kind) {
1762 .code => if (self.text_index) |sym_index|
1763 self.symbol(sym_index).outputShndx(elf_file).?
1764 else osec: {
1765 const osec = try elf_file.addSection(.{
1766 .name = try elf_file.insertShString(".text"),
1767 .type = elf.SHT_PROGBITS,
1768 .addralign = 1,
1769 .flags = elf.SHF_ALLOC | elf.SHF_EXECINSTR,
1770 });
1771 self.text_index = try self.addSectionSymbol(gpa, try self.addString(gpa, ".text"), osec);
1772 break :osec osec;
1773 },
1774 .const_data => if (self.rodata_index) |sym_index|
1775 self.symbol(sym_index).outputShndx(elf_file).?
1776 else osec: {
1777 const osec = try elf_file.addSection(.{
1778 .name = try elf_file.insertShString(".rodata"),
1779 .type = elf.SHT_PROGBITS,
1780 .addralign = 1,
1781 .flags = elf.SHF_ALLOC,
1782 });
1783 self.rodata_index = try self.addSectionSymbol(gpa, try self.addString(gpa, ".rodata"), osec);
1784 break :osec osec;
1785 },
1786 };
1787 const local_sym = self.symbol(symbol_index);
1788 local_sym.name_offset = name_str_index;
1789 const local_esym = &self.symtab.items(.elf_sym)[local_sym.esym_index];
1790 local_esym.st_name = name_str_index;
1791 local_esym.st_info |= elf.STT_OBJECT;
1792 local_esym.st_size = code.len;
1793 const atom_ptr = local_sym.atom(elf_file).?;
1794 atom_ptr.alive = true;
1795 atom_ptr.name_offset = name_str_index;
1796 atom_ptr.alignment = required_alignment;
1797 atom_ptr.size = code.len;
1798 atom_ptr.output_section_index = output_section_index;
1799
1800 try self.allocateAtom(atom_ptr, true, elf_file);
1801 errdefer self.freeNavMetadata(elf_file, symbol_index);
1802
1803 local_sym.value = 0;
1804 local_esym.st_value = 0;
1805
1806 try elf_file.pwriteAll(code, atom_ptr.offset(elf_file));
1807}
1808
1809fn lowerConst(
1810 self: *ZigObject,
1811 elf_file: *Elf,
1812 pt: Zcu.PerThread,
1813 name: []const u8,
1814 val: Value,
1815 required_alignment: InternPool.Alignment,
1816 output_section_index: u32,
1817) !link.File.SymbolId {
1818 const gpa = pt.zcu.gpa;
1819
1820 var aw: std.Io.Writer.Allocating = .init(gpa);
1821 defer aw.deinit();
1822
1823 const name_off = try self.addString(gpa, name);
1824 const sym_index = try self.newSymbolWithAtom(gpa, name_off);
1825
1826 codegen.generateSymbol(
1827 &elf_file.base,
1828 pt,
1829 val,
1830 &aw.writer,
1831 .{ .atom_index = @fromBackingInt(@intCast(sym_index)) },
1832 ) catch |err| switch (err) {
1833 error.WriteFailed => return error.OutOfMemory,
1834 else => |e| return e,
1835 };
1836 const code = aw.written();
1837
1838 const local_sym = self.symbol(sym_index);
1839 const local_esym = &self.symtab.items(.elf_sym)[local_sym.esym_index];
1840 local_esym.st_info |= elf.STT_OBJECT;
1841 local_esym.st_size = code.len;
1842 const atom_ptr = local_sym.atom(elf_file).?;
1843 atom_ptr.alive = true;
1844 atom_ptr.alignment = required_alignment;
1845 atom_ptr.size = code.len;
1846 atom_ptr.output_section_index = output_section_index;
1847
1848 try self.allocateAtom(atom_ptr, true, elf_file);
1849 errdefer self.freeNavMetadata(elf_file, sym_index);
1850
1851 try elf_file.pwriteAll(code, atom_ptr.offset(elf_file));
1852
1853 return @fromBackingInt(@intCast(sym_index));
1854}
1855
1856pub fn updateExports(
1857 self: *ZigObject,
1858 elf_file: *Elf,
1859 pt: Zcu.PerThread,
1860 export_indices: []const Zcu.Export.Index,
1861) link.Error!void {
1862 const tracy = trace(@src());
1863 defer tracy.end();
1864
1865 const zcu = pt.zcu;
1866 const gpa = elf_file.base.comp.gpa;
1867
1868 // Delete all existing exports first
1869 for (self.navs.values()) |*metadata| {
1870 for (metadata.exports.items) |sym_index| {
1871 const esym_index = self.symbol(sym_index).esym_index;
1872 const esym = &self.symtab.items(.elf_sym)[esym_index];
1873 _ = self.globals_lookup.remove(esym.st_name);
1874 esym.* = Elf.null_sym;
1875 self.symtab.items(.shndx)[esym_index] = elf.SHN_UNDEF;
1876 }
1877 metadata.exports.clearRetainingCapacity();
1878 }
1879 for (self.uavs.values()) |*metadata| {
1880 for (metadata.exports.items) |sym_index| {
1881 const esym_index = self.symbol(sym_index).esym_index;
1882 const esym = &self.symtab.items(.elf_sym)[esym_index];
1883 _ = self.globals_lookup.remove(esym.st_name);
1884 esym.* = Elf.null_sym;
1885 self.symtab.items(.shndx)[esym_index] = elf.SHN_UNDEF;
1886 }
1887 metadata.exports.clearRetainingCapacity();
1888 }
1889
1890 for (export_indices) |export_index| {
1891 const exp = export_index.ptr(zcu);
1892 const metadata = switch (exp.exported) {
1893 .nav => |nav| blk: {
1894 _ = try self.getOrCreateMetadataForNav(zcu, nav);
1895 break :blk self.navs.getPtr(nav).?;
1896 },
1897 .uav => |uav| self.uavs.getPtr(uav) orelse blk: {
1898 _ = try self.lowerUav(elf_file, pt, uav, .none);
1899 break :blk self.uavs.getPtr(uav).?;
1900 },
1901 };
1902 const sym_index = metadata.symbol_index;
1903 const esym_index = self.symbol(sym_index).esym_index;
1904 const esym = self.symtab.items(.elf_sym)[esym_index];
1905 const esym_shndx = self.symtab.items(.shndx)[esym_index];
1906 if (exp.opts.section.unwrap()) |section_name| {
1907 if (!section_name.eqlSlice(".text", &zcu.intern_pool)) {
1908 try zcu.failed_exports.ensureUnusedCapacity(gpa, 1);
1909 zcu.failed_exports.putAssumeCapacityNoClobber(export_index, try Zcu.ErrorMsg.create(
1910 gpa,
1911 exp.src,
1912 "Unimplemented: ExportOptions.section",
1913 .{},
1914 ));
1915 continue;
1916 }
1917 }
1918 const stb_bits: u8 = switch (exp.opts.linkage) {
1919 .internal => elf.STB_LOCAL,
1920 .strong => elf.STB_GLOBAL,
1921 .weak => elf.STB_WEAK,
1922 .link_once => {
1923 try zcu.failed_exports.ensureUnusedCapacity(gpa, 1);
1924 zcu.failed_exports.putAssumeCapacityNoClobber(export_index, try Zcu.ErrorMsg.create(
1925 gpa,
1926 exp.src,
1927 "Unimplemented: GlobalLinkage.LinkOnce",
1928 .{},
1929 ));
1930 continue;
1931 },
1932 };
1933 const stt_bits: u8 = @as(u4, @truncate(esym.st_info));
1934 const exp_name = exp.opts.name.toSlice(&zcu.intern_pool);
1935 const name_off = try self.strtab.insert(gpa, exp_name);
1936 const global_sym_index = try self.getGlobalSymbol(elf_file, exp_name, null);
1937 try metadata.exports.append(gpa, global_sym_index);
1938
1939 const value = self.symbol(sym_index).value;
1940 const global_sym = self.symbol(global_sym_index);
1941 global_sym.value = value;
1942 global_sym.flags.weak = exp.opts.linkage == .weak;
1943 global_sym.version_index = elf_file.default_sym_version;
1944 global_sym.ref = .{ .index = esym_shndx, .file = self.index };
1945 const global_esym = &self.symtab.items(.elf_sym)[global_sym.esym_index];
1946 global_esym.st_value = @intCast(value);
1947 global_esym.st_shndx = esym.st_shndx;
1948 global_esym.st_info = (stb_bits << 4) | stt_bits;
1949 global_esym.st_name = name_off;
1950 global_esym.st_size = esym.st_size;
1951 self.symtab.items(.shndx)[global_sym.esym_index] = esym_shndx;
1952 }
1953}
1954
1955pub fn updateLineNumber(self: *ZigObject, pt: Zcu.PerThread, ti_id: InternPool.TrackedInst.Index) link.Error!void {
1956 if (self.dwarf) |*dwarf| {
1957 const comp = dwarf.bin_file.comp;
1958 const diags = &comp.link_diags;
1959 dwarf.updateLineNumber(pt.zcu, ti_id) catch |err| switch (err) {
1960 error.OutOfMemory, error.Canceled, error.AlreadyReported => |e| return e,
1961 else => |e| return diags.fail("failed to update dwarf line numbers: {s}", .{@errorName(e)}),
1962 };
1963 }
1964}
1965
1966pub fn getGlobalSymbol(self: *ZigObject, elf_file: *Elf, name: []const u8, lib_name: ?[]const u8) !u32 {
1967 _ = lib_name;
1968 const gpa = elf_file.base.comp.gpa;
1969 const off = try self.strtab.insert(gpa, name);
1970 const lookup_gop = try self.globals_lookup.getOrPut(gpa, off);
1971 if (!lookup_gop.found_existing) {
1972 lookup_gop.value_ptr.* = try self.newGlobalSymbol(gpa, off);
1973 }
1974 return lookup_gop.value_ptr.*;
1975}
1976
1977const max_trampoline_len = 12;
1978
1979fn trampolineSize(cpu_arch: std.Target.Cpu.Arch) u64 {
1980 const len = switch (cpu_arch) {
1981 .x86_64 => 5, // jmp rel32
1982 else => @panic("TODO implement trampoline size for this CPU arch"),
1983 };
1984 comptime assert(len <= max_trampoline_len);
1985 return len;
1986}
1987
1988fn writeTrampoline(tr_sym: Symbol, target: Symbol, elf_file: *Elf) !void {
1989 const comp = elf_file.base.comp;
1990 const io = comp.io;
1991 const atom_ptr = tr_sym.atom(elf_file).?;
1992 const fileoff = atom_ptr.offset(elf_file);
1993 const source_addr = tr_sym.address(.{}, elf_file);
1994 const target_addr = target.address(.{ .trampoline = false }, elf_file);
1995 var buf: [max_trampoline_len]u8 = undefined;
1996 const out = switch (elf_file.getTarget().cpu.arch) {
1997 .x86_64 => try x86_64.writeTrampolineCode(source_addr, target_addr, &buf),
1998 else => @panic("TODO implement write trampoline for this CPU arch"),
1999 };
2000 try elf_file.base.file.?.writePositionalAll(io, out, fileoff);
2001
2002 if (elf_file.base.child_pid) |pid| {
2003 switch (builtin.os.tag) {
2004 .linux => {
2005 var local_vec: [1]std.posix.iovec_const = .{.{
2006 .base = out.ptr,
2007 .len = out.len,
2008 }};
2009 var remote_vec: [1]std.posix.iovec_const = .{.{
2010 .base = @as([*]u8, @ptrFromInt(@as(usize, @intCast(source_addr)))),
2011 .len = out.len,
2012 }};
2013 const rc = std.os.linux.process_vm_writev(pid, &local_vec, &remote_vec, 0);
2014 switch (std.os.linux.errno(rc)) {
2015 .SUCCESS => assert(rc == out.len),
2016 else => |errno| log.warn("process_vm_writev failure: {s}", .{@tagName(errno)}),
2017 }
2018 },
2019 else => return error.HotSwapUnavailableOnHostOperatingSystem,
2020 }
2021 }
2022}
2023
2024pub fn allocateAtom(self: *ZigObject, atom_ptr: *Atom, requires_padding: bool, elf_file: *Elf) !void {
2025 const slice = elf_file.sections.slice();
2026 const shdr = &slice.items(.shdr)[atom_ptr.output_section_index];
2027 const last_atom_ref = &slice.items(.last_atom)[atom_ptr.output_section_index];
2028
2029 if (last_atom_ref.eql(atom_ptr.ref())) {
2030 if (atom_ptr.prevAtom(elf_file)) |prev_atom| {
2031 prev_atom.next_atom_ref = .{};
2032 last_atom_ref.* = prev_atom.ref();
2033 } else {
2034 last_atom_ref.* = .{};
2035 }
2036 }
2037
2038 const alloc_res = try elf_file.allocateChunk(.{
2039 .shndx = atom_ptr.output_section_index,
2040 .size = atom_ptr.size,
2041 .alignment = atom_ptr.alignment,
2042 .requires_padding = requires_padding,
2043 });
2044 atom_ptr.value = @intCast(alloc_res.value);
2045 log.debug("allocated {s} at {x}\n placement {f}", .{
2046 atom_ptr.name(elf_file),
2047 atom_ptr.offset(elf_file),
2048 alloc_res.placement,
2049 });
2050
2051 const expand_section = if (elf_file.atom(alloc_res.placement)) |placement_atom|
2052 placement_atom.nextAtom(elf_file) == null
2053 else
2054 true;
2055 if (expand_section) {
2056 last_atom_ref.* = atom_ptr.ref();
2057 if (self.dwarf) |_| {
2058 // The .debug_info section has `low_pc` and `high_pc` values which is the virtual address
2059 // range of the compilation unit. When we expand the text section, this range changes,
2060 // so the DW_TAG.compile_unit tag of the .debug_info section becomes dirty.
2061 self.debug_info_section_dirty = true;
2062 // This becomes dirty for the same reason. We could potentially make this more
2063 // fine-grained with the addition of support for more compilation units. It is planned to
2064 // model each package as a different compilation unit.
2065 self.debug_aranges_section_dirty = true;
2066 self.debug_rnglists_section_dirty = true;
2067 }
2068 }
2069 shdr.sh_addralign = @max(shdr.sh_addralign, atom_ptr.alignment.toByteUnits().?);
2070
2071 // This function can also reallocate an atom.
2072 // In this case we need to "unplug" it from its previous location before
2073 // plugging it in to its new location.
2074 if (atom_ptr.prevAtom(elf_file)) |prev| {
2075 prev.next_atom_ref = atom_ptr.next_atom_ref;
2076 }
2077 if (atom_ptr.nextAtom(elf_file)) |next| {
2078 next.prev_atom_ref = atom_ptr.prev_atom_ref;
2079 }
2080
2081 if (elf_file.atom(alloc_res.placement)) |big_atom| {
2082 atom_ptr.prev_atom_ref = alloc_res.placement;
2083 atom_ptr.next_atom_ref = big_atom.next_atom_ref;
2084 big_atom.next_atom_ref = atom_ptr.ref();
2085 } else {
2086 atom_ptr.prev_atom_ref = .{ .index = 0, .file = 0 };
2087 atom_ptr.next_atom_ref = .{ .index = 0, .file = 0 };
2088 }
2089
2090 log.debug(" prev {f}, next {f}", .{ atom_ptr.prev_atom_ref, atom_ptr.next_atom_ref });
2091}
2092
2093pub fn resetShdrIndexes(self: *ZigObject, backlinks: []const u32) void {
2094 for (self.atoms_indexes.items) |atom_index| {
2095 const atom_ptr = self.atom(atom_index) orelse continue;
2096 atom_ptr.output_section_index = backlinks[atom_ptr.output_section_index];
2097 }
2098 inline for ([_]?Symbol.Index{
2099 self.text_index,
2100 self.rodata_index,
2101 self.data_relro_index,
2102 self.data_index,
2103 self.bss_index,
2104 self.tdata_index,
2105 self.tbss_index,
2106 self.eh_frame_index,
2107 self.debug_info_index,
2108 self.debug_abbrev_index,
2109 self.debug_aranges_index,
2110 self.debug_str_index,
2111 self.debug_line_index,
2112 self.debug_line_str_index,
2113 self.debug_loclists_index,
2114 self.debug_rnglists_index,
2115 }) |maybe_sym_index| {
2116 if (maybe_sym_index) |sym_index| {
2117 const sym = self.symbol(sym_index);
2118 sym.output_section_index = backlinks[sym.output_section_index];
2119 }
2120 }
2121}
2122
2123pub fn asFile(self: *ZigObject) File {
2124 return .{ .zig_object = self };
2125}
2126
2127pub fn sectionSymbol(self: *ZigObject, shndx: u32, elf_file: *Elf) ?*Symbol {
2128 inline for ([_]?Symbol.Index{
2129 self.text_index,
2130 self.rodata_index,
2131 self.data_relro_index,
2132 self.data_index,
2133 self.bss_index,
2134 self.tdata_index,
2135 self.tbss_index,
2136 self.eh_frame_index,
2137 self.debug_info_index,
2138 self.debug_abbrev_index,
2139 self.debug_aranges_index,
2140 self.debug_str_index,
2141 self.debug_line_index,
2142 self.debug_line_str_index,
2143 self.debug_loclists_index,
2144 self.debug_rnglists_index,
2145 }) |maybe_sym_index| {
2146 if (maybe_sym_index) |sym_index| {
2147 const sym = self.symbol(sym_index);
2148 if (sym.outputShndx(elf_file) == shndx) return sym;
2149 }
2150 }
2151 return null;
2152}
2153
2154pub fn addString(self: *ZigObject, allocator: Allocator, string: []const u8) !u32 {
2155 return self.strtab.insert(allocator, string);
2156}
2157
2158pub fn getString(self: ZigObject, off: u32) [:0]const u8 {
2159 return self.strtab.getAssumeExists(off);
2160}
2161
2162fn addAtom(self: *ZigObject, allocator: Allocator) !Atom.Index {
2163 try self.atoms.ensureUnusedCapacity(allocator, 1);
2164 try self.atoms_extra.ensureUnusedCapacity(allocator, @sizeOf(Atom.Extra));
2165 return self.addAtomAssumeCapacity();
2166}
2167
2168fn addAtomAssumeCapacity(self: *ZigObject) Atom.Index {
2169 const atom_index: Atom.Index = @intCast(self.atoms.items.len);
2170 const atom_ptr = self.atoms.addOneAssumeCapacity();
2171 atom_ptr.* = .{
2172 .file_index = self.index,
2173 .atom_index = atom_index,
2174 .extra_index = self.addAtomExtraAssumeCapacity(.{}),
2175 };
2176 return atom_index;
2177}
2178
2179pub fn atom(self: *ZigObject, atom_index: Atom.Index) ?*Atom {
2180 if (atom_index == 0) return null;
2181 assert(atom_index < self.atoms.items.len);
2182 return &self.atoms.items[atom_index];
2183}
2184
2185fn addAtomExtra(self: *ZigObject, allocator: Allocator, extra: Atom.Extra) !u32 {
2186 const field_count = @typeInfo(Atom.Extra).@"struct".field_names.len;
2187 try self.atoms_extra.ensureUnusedCapacity(allocator, field_count);
2188 return self.addAtomExtraAssumeCapacity(extra);
2189}
2190
2191fn addAtomExtraAssumeCapacity(self: *ZigObject, extra: Atom.Extra) u32 {
2192 const index = @as(u32, @intCast(self.atoms_extra.items.len));
2193 const info = @typeInfo(Atom.Extra).@"struct";
2194 inline for (info.field_names, info.field_types) |field_name, field_type| {
2195 self.atoms_extra.appendAssumeCapacity(switch (field_type) {
2196 u32 => @field(extra, field_name),
2197 else => @compileError("bad field type"),
2198 });
2199 }
2200 return index;
2201}
2202
2203pub fn atomExtra(self: ZigObject, index: u32) Atom.Extra {
2204 const info = @typeInfo(Atom.Extra).@"struct";
2205 var i: usize = index;
2206 var result: Atom.Extra = undefined;
2207 inline for (info.field_names, info.field_types) |field_name, field_type| {
2208 @field(result, field_name) = switch (field_type) {
2209 u32 => self.atoms_extra.items[i],
2210 else => @compileError("bad field type"),
2211 };
2212 i += 1;
2213 }
2214 return result;
2215}
2216
2217pub fn setAtomExtra(self: *ZigObject, index: u32, extra: Atom.Extra) void {
2218 assert(index > 0);
2219 const info = @typeInfo(Atom.Extra).@"struct";
2220 inline for (info.field_names, info.field_types, 0..) |field_name, field_type, i| {
2221 self.atoms_extra.items[index + i] = switch (field_type) {
2222 u32 => @field(extra, field_name),
2223 else => @compileError("bad field type"),
2224 };
2225 }
2226}
2227
2228inline fn isGlobal(index: Symbol.Index) bool {
2229 return index & global_symbol_bit != 0;
2230}
2231
2232pub fn symbol(self: *ZigObject, index: Symbol.Index) *Symbol {
2233 const actual_index = index & symbol_mask;
2234 if (isGlobal(index)) return &self.symbols.items[self.global_symbols.items[actual_index]];
2235 return &self.symbols.items[self.local_symbols.items[actual_index]];
2236}
2237
2238pub fn resolveSymbol(self: ZigObject, index: Symbol.Index, elf_file: *Elf) Elf.Ref {
2239 if (isGlobal(index)) {
2240 const resolv = self.symbols_resolver.items[index & symbol_mask];
2241 return elf_file.resolver.get(resolv).?;
2242 }
2243 return .{ .index = index, .file = self.index };
2244}
2245
2246fn addSymbol(self: *ZigObject, allocator: Allocator) !Symbol.Index {
2247 try self.symbols.ensureUnusedCapacity(allocator, 1);
2248 return self.addSymbolAssumeCapacity();
2249}
2250
2251fn addSymbolAssumeCapacity(self: *ZigObject) Symbol.Index {
2252 const index: Symbol.Index = @intCast(self.symbols.items.len);
2253 self.symbols.appendAssumeCapacity(.{ .file_index = self.index });
2254 return index;
2255}
2256
2257pub fn addSymbolExtra(self: *ZigObject, allocator: Allocator, extra: Symbol.Extra) !u32 {
2258 const field_count = @typeInfo(Symbol.Extra).@"struct".field_names.len;
2259 try self.symbols_extra.ensureUnusedCapacity(allocator, field_count);
2260 return self.addSymbolExtraAssumeCapacity(extra);
2261}
2262
2263pub fn addSymbolExtraAssumeCapacity(self: *ZigObject, extra: Symbol.Extra) u32 {
2264 const index = @as(u32, @intCast(self.symbols_extra.items.len));
2265 const info = @typeInfo(Symbol.Extra).@"struct";
2266 inline for (info.field_names, info.field_types) |field_name, field_type| {
2267 self.symbols_extra.appendAssumeCapacity(switch (field_type) {
2268 u32 => @field(extra, field_name),
2269 else => @compileError("bad field type"),
2270 });
2271 }
2272 return index;
2273}
2274
2275pub fn symbolExtra(self: *ZigObject, index: u32) Symbol.Extra {
2276 const info = @typeInfo(Symbol.Extra).@"struct";
2277 var i: usize = index;
2278 var result: Symbol.Extra = undefined;
2279 inline for (info.field_names, info.field_types) |field_name, field_type| {
2280 @field(result, field_name) = switch (field_type) {
2281 u32 => self.symbols_extra.items[i],
2282 else => @compileError("bad field type"),
2283 };
2284 i += 1;
2285 }
2286 return result;
2287}
2288
2289pub fn setSymbolExtra(self: *ZigObject, index: u32, extra: Symbol.Extra) void {
2290 const info = @typeInfo(Symbol.Extra).@"struct";
2291 inline for (info.field_names, info.field_types, 0..) |field_name, field_type, i| {
2292 self.symbols_extra.items[index + i] = switch (field_type) {
2293 u32 => @field(extra, field_name),
2294 else => @compileError("bad field type"),
2295 };
2296 }
2297}
2298
2299const Format = struct {
2300 self: *ZigObject,
2301 elf_file: *Elf,
2302
2303 fn symtab(f: Format, writer: *std.Io.Writer) std.Io.Writer.Error!void {
2304 const self = f.self;
2305 const elf_file = f.elf_file;
2306 try writer.writeAll(" locals\n");
2307 for (self.local_symbols.items) |index| {
2308 const local = self.symbols.items[index];
2309 try writer.print(" {f}\n", .{local.fmt(elf_file)});
2310 }
2311 try writer.writeAll(" globals\n");
2312 for (f.self.global_symbols.items) |index| {
2313 const global = self.symbols.items[index];
2314 try writer.print(" {f}\n", .{global.fmt(elf_file)});
2315 }
2316 }
2317
2318 fn atoms(f: Format, writer: *std.Io.Writer) std.Io.Writer.Error!void {
2319 try writer.writeAll(" atoms\n");
2320 for (f.self.atoms_indexes.items) |atom_index| {
2321 const atom_ptr = f.self.atom(atom_index) orelse continue;
2322 try writer.print(" {f}\n", .{atom_ptr.fmt(f.elf_file)});
2323 }
2324 }
2325};
2326
2327pub fn fmtSymtab(self: *ZigObject, elf_file: *Elf) std.fmt.Alt(Format, Format.symtab) {
2328 return .{ .data = .{
2329 .self = self,
2330 .elf_file = elf_file,
2331 } };
2332}
2333
2334pub fn fmtAtoms(self: *ZigObject, elf_file: *Elf) std.fmt.Alt(Format, Format.atoms) {
2335 return .{ .data = .{
2336 .self = self,
2337 .elf_file = elf_file,
2338 } };
2339}
2340
2341const ElfSym = struct {
2342 elf_sym: elf.Elf64_Sym,
2343 shndx: u32 = elf.SHN_UNDEF,
2344};
2345
2346const LazySymbolMetadata = struct {
2347 const State = enum { unused, pending_flush, flushed };
2348 text_symbol_index: Symbol.Index = undefined,
2349 rodata_symbol_index: Symbol.Index = undefined,
2350 text_state: State = .unused,
2351 rodata_state: State = .unused,
2352};
2353
2354const AvMetadata = struct {
2355 symbol_index: Symbol.Index,
2356 /// A list of all exports aliases of this Av.
2357 exports: std.ArrayList(Symbol.Index) = .empty,
2358 /// Set to true if the AV has been initialized and allocated.
2359 allocated: bool = false,
2360};
2361
2362fn checkNavAllocated(pt: Zcu.PerThread, index: InternPool.Nav.Index, meta: AvMetadata) void {
2363 if (!meta.allocated) {
2364 const zcu = pt.zcu;
2365 const ip = &zcu.intern_pool;
2366 const nav = ip.getNav(index);
2367 log.err("NAV {f}({d}) assigned symbol {d} but not allocated!", .{
2368 nav.fqn.fmt(ip),
2369 index,
2370 meta.symbol_index,
2371 });
2372 }
2373}
2374
2375fn checkUavAllocated(pt: Zcu.PerThread, index: InternPool.Index, meta: AvMetadata) void {
2376 if (!meta.allocated) {
2377 const zcu = pt.zcu;
2378 const uav = Value.fromInterned(index);
2379 const ty = uav.typeOf(zcu);
2380 log.err("UAV {f}({d}) assigned symbol {d} but not allocated!", .{
2381 ty.fmt(pt),
2382 index,
2383 meta.symbol_index,
2384 });
2385 }
2386}
2387
2388const TlsVariable = struct {
2389 symbol_index: Symbol.Index,
2390 code: []const u8 = &[0]u8{},
2391
2392 fn deinit(tlv: *TlsVariable, allocator: Allocator) void {
2393 allocator.free(tlv.code);
2394 }
2395};
2396
2397const AtomList = std.ArrayList(Atom.Index);
2398const NavTable = std.array_hash_map.Auto(InternPool.Nav.Index, AvMetadata);
2399const UavTable = std.array_hash_map.Auto(InternPool.Index, AvMetadata);
2400const LazySymbolTable = std.array_hash_map.Auto(InternPool.Index, LazySymbolMetadata);
2401const TlsTable = std.array_hash_map.Auto(Atom.Index, void);
2402
2403const x86_64 = struct {
2404 fn writeTrampolineCode(source_addr: i64, target_addr: i64, buf: *[max_trampoline_len]u8) ![]u8 {
2405 const disp = @as(i64, @intCast(target_addr)) - source_addr - 5;
2406 var bytes = [_]u8{
2407 0xe9, 0x00, 0x00, 0x00, 0x00, // jmp rel32
2408 };
2409 assert(bytes.len == trampolineSize(.x86_64));
2410 mem.writeInt(i32, bytes[1..][0..4], @intCast(disp), .little);
2411 @memcpy(buf[0..bytes.len], &bytes);
2412 return buf[0..bytes.len];
2413 }
2414};
2415
2416const assert = std.debug.assert;
2417const build_options = @import("build_options");
2418const builtin = @import("builtin");
2419const codegen = @import("../../codegen.zig");
2420const elf = std.elf;
2421const link = @import("../../link.zig");
2422const log = std.log.scoped(.link);
2423const mem = std.mem;
2424const relocation = @import("relocation.zig");
2425const target_util = @import("../../target.zig");
2426const trace = @import("../../tracy.zig").trace;
2427const std = @import("std");
2428const Allocator = std.mem.Allocator;
2429
2430const Archive = @import("Archive.zig");
2431const Atom = @import("Atom.zig");
2432const Dwarf = @import("../Dwarf.zig");
2433const Elf = @import("../Elf.zig");
2434const File = @import("file.zig").File;
2435const InternPool = @import("../../InternPool.zig");
2436const Zcu = @import("../../Zcu.zig");
2437const Object = @import("Object.zig");
2438const Symbol = @import("Symbol.zig");
2439const StringTable = @import("../StringTable.zig");
2440const Type = @import("../../Type.zig");
2441const Value = @import("../../Value.zig");
2442const AnalUnit = InternPool.AnalUnit;
2443const ZigObject = @This();