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