| ... | @@ -1,1426 +0,0 @@ |
| 1 | //! This implementation does all the linking work in flush(). A future improvement |
| 2 | //! would be to add incremental linking in a similar way as ELF does. |
| 3 | |
| 4 | const Plan9 = @This(); |
| 5 | const link = @import("../link.zig"); |
| 6 | const Zcu = @import("../Zcu.zig"); |
| 7 | const InternPool = @import("../InternPool.zig"); |
| 8 | const Compilation = @import("../Compilation.zig"); |
| 9 | const aout = @import("Plan9/aout.zig"); |
| 10 | const codegen = @import("../codegen.zig"); |
| 11 | const trace = @import("../tracy.zig").trace; |
| 12 | const File = link.File; |
| 13 | const build_options = @import("build_options"); |
| 14 | const Air = @import("../Air.zig"); |
| 15 | const Type = @import("../Type.zig"); |
| 16 | const Value = @import("../Value.zig"); |
| 17 | const AnalUnit = InternPool.AnalUnit; |
| 18 | |
| 19 | const std = @import("std"); |
| 20 | const builtin = @import("builtin"); |
| 21 | const mem = std.mem; |
| 22 | const Allocator = std.mem.Allocator; |
| 23 | const log = std.log.scoped(.link); |
| 24 | const assert = std.debug.assert; |
| 25 | const Path = std.Build.Cache.Path; |
| 26 | |
| 27 | base: link.File, |
| 28 | sixtyfour_bit: bool, |
| 29 | bases: Bases, |
| 30 | |
| 31 | /// A symbol's value is just casted down when compiling |
| 32 | /// for a 32 bit target. |
| 33 | /// Does not represent the order or amount of symbols in the file |
| 34 | /// it is just useful for storing symbols. Some other symbols are in |
| 35 | /// file_segments. |
| 36 | syms: std.ArrayListUnmanaged(aout.Sym) = .empty, |
| 37 | |
| 38 | /// The plan9 a.out format requires segments of |
| 39 | /// filenames to be deduplicated, so we use this map to |
| 40 | /// de duplicate it. The value is the value of the path |
| 41 | /// component |
| 42 | file_segments: std.StringArrayHashMapUnmanaged(u16) = .empty, |
| 43 | /// The value of a 'f' symbol increments by 1 every time, so that no 2 'f' |
| 44 | /// symbols have the same value. |
| 45 | file_segments_i: u16 = 1, |
| 46 | |
| 47 | path_arena: std.heap.ArenaAllocator, |
| 48 | |
| 49 | /// maps a file scope to a hash map of decl to codegen output |
| 50 | /// this is useful for line debuginfo, since it makes sense to sort by file |
| 51 | /// The debugger looks for the first file (aout.Sym.Type.z) preceeding the text symbol |
| 52 | /// of the function to know what file it came from. |
| 53 | /// If we group the decls by file, it makes it really easy to do this (put the symbol in the correct place) |
| 54 | fn_nav_table: std.AutoArrayHashMapUnmanaged( |
| 55 | Zcu.File.Index, |
| 56 | struct { sym_index: u32, functions: std.AutoArrayHashMapUnmanaged(InternPool.Nav.Index, FnNavOutput) = .empty }, |
| 57 | ) = .{}, |
| 58 | /// the code is modified when relocated, so that is why it is mutable |
| 59 | data_nav_table: std.AutoArrayHashMapUnmanaged(InternPool.Nav.Index, []u8) = .empty, |
| 60 | /// When `updateExports` is called, we store the export indices here, to be used |
| 61 | /// during flush. |
| 62 | nav_exports: std.AutoArrayHashMapUnmanaged(InternPool.Nav.Index, []Zcu.Export.Index) = .empty, |
| 63 | |
| 64 | lazy_syms: LazySymbolTable = .{}, |
| 65 | |
| 66 | uavs: std.AutoHashMapUnmanaged(InternPool.Index, Atom.Index) = .empty, |
| 67 | |
| 68 | relocs: std.AutoHashMapUnmanaged(Atom.Index, std.ArrayListUnmanaged(Reloc)) = .empty, |
| 69 | hdr: aout.ExecHdr = undefined, |
| 70 | |
| 71 | // relocs: std. |
| 72 | magic: u32, |
| 73 | |
| 74 | entry_val: ?u64 = null, |
| 75 | |
| 76 | got_len: usize = 0, |
| 77 | // A list of all the free got indexes, so when making a new decl |
| 78 | // don't make a new one, just use one from here. |
| 79 | got_index_free_list: std.ArrayListUnmanaged(usize) = .empty, |
| 80 | |
| 81 | syms_index_free_list: std.ArrayListUnmanaged(usize) = .empty, |
| 82 | |
| 83 | atoms: std.ArrayListUnmanaged(Atom) = .empty, |
| 84 | navs: std.AutoHashMapUnmanaged(InternPool.Nav.Index, NavMetadata) = .empty, |
| 85 | |
| 86 | /// Indices of the three "special" symbols into atoms |
| 87 | etext_edata_end_atom_indices: [3]?Atom.Index = .{ null, null, null }, |
| 88 | |
| 89 | const Reloc = struct { |
| 90 | target: Atom.Index, |
| 91 | offset: u64, |
| 92 | addend: u32, |
| 93 | type: enum { |
| 94 | pcrel, |
| 95 | nonpcrel, |
| 96 | // for getting the value of the etext symbol; we ignore target |
| 97 | special_etext, |
| 98 | // for getting the value of the edata symbol; we ignore target |
| 99 | special_edata, |
| 100 | // for getting the value of the end symbol; we ignore target |
| 101 | special_end, |
| 102 | } = .nonpcrel, |
| 103 | }; |
| 104 | |
| 105 | const Bases = struct { |
| 106 | text: u64, |
| 107 | /// the Global Offset Table starts at the beginning of the data section |
| 108 | data: u64, |
| 109 | }; |
| 110 | |
| 111 | const LazySymbolTable = std.AutoArrayHashMapUnmanaged(InternPool.Index, LazySymbolMetadata); |
| 112 | |
| 113 | const LazySymbolMetadata = struct { |
| 114 | const State = enum { unused, pending_flush, flushed }; |
| 115 | text_atom: Atom.Index = undefined, |
| 116 | rodata_atom: Atom.Index = undefined, |
| 117 | text_state: State = .unused, |
| 118 | rodata_state: State = .unused, |
| 119 | |
| 120 | fn numberOfAtoms(self: LazySymbolMetadata) u32 { |
| 121 | var n: u32 = 0; |
| 122 | if (self.text_state != .unused) n += 1; |
| 123 | if (self.rodata_state != .unused) n += 1; |
| 124 | return n; |
| 125 | } |
| 126 | }; |
| 127 | |
| 128 | pub const PtrWidth = enum { p32, p64 }; |
| 129 | |
| 130 | pub const Atom = struct { |
| 131 | type: aout.Sym.Type, |
| 132 | /// offset in the text or data sects |
| 133 | offset: ?u64, |
| 134 | /// offset into syms |
| 135 | sym_index: ?usize, |
| 136 | /// offset into got |
| 137 | got_index: ?usize, |
| 138 | /// We include the code here to be use in relocs |
| 139 | /// In the case of lazy_syms, this atom owns the code. |
| 140 | /// But, in the case of function and data decls, they own the code and this field |
| 141 | /// is just a pointer for convience. |
| 142 | code: CodePtr, |
| 143 | |
| 144 | const CodePtr = struct { |
| 145 | code_ptr: ?[*]u8, |
| 146 | other: union { |
| 147 | code_len: usize, |
| 148 | nav_index: InternPool.Nav.Index, |
| 149 | }, |
| 150 | fn fromSlice(slice: []u8) CodePtr { |
| 151 | return .{ .code_ptr = slice.ptr, .other = .{ .code_len = slice.len } }; |
| 152 | } |
| 153 | fn getCode(self: CodePtr, plan9: *const Plan9) []u8 { |
| 154 | const zcu = plan9.base.comp.zcu.?; |
| 155 | const ip = &zcu.intern_pool; |
| 156 | return if (self.code_ptr) |p| p[0..self.other.code_len] else blk: { |
| 157 | const nav_index = self.other.nav_index; |
| 158 | const nav = ip.getNav(nav_index); |
| 159 | if (ip.isFunctionType(nav.typeOf(ip))) { |
| 160 | const table = plan9.fn_nav_table.get(zcu.navFileScopeIndex(nav_index)).?.functions; |
| 161 | const output = table.get(nav_index).?; |
| 162 | break :blk output.code; |
| 163 | } else { |
| 164 | break :blk plan9.data_nav_table.get(nav_index).?; |
| 165 | } |
| 166 | }; |
| 167 | } |
| 168 | fn getOwnedCode(self: CodePtr) ?[]u8 { |
| 169 | return if (self.code_ptr) |p| p[0..self.other.code_len] else null; |
| 170 | } |
| 171 | }; |
| 172 | |
| 173 | pub const Index = u32; |
| 174 | |
| 175 | pub fn getOrCreateOffsetTableEntry(self: *Atom, plan9: *Plan9) usize { |
| 176 | if (self.got_index == null) self.got_index = plan9.allocateGotIndex(); |
| 177 | return self.got_index.?; |
| 178 | } |
| 179 | |
| 180 | pub fn getOrCreateSymbolTableEntry(self: *Atom, plan9: *Plan9) !usize { |
| 181 | if (self.sym_index == null) self.sym_index = try plan9.allocateSymbolIndex(); |
| 182 | return self.sym_index.?; |
| 183 | } |
| 184 | |
| 185 | // asserts that self.got_index != null |
| 186 | pub fn getOffsetTableAddress(self: Atom, plan9: *Plan9) u64 { |
| 187 | const target = &plan9.base.comp.root_mod.resolved_target.result; |
| 188 | const ptr_bytes = @divExact(target.ptrBitWidth(), 8); |
| 189 | const got_addr = plan9.bases.data; |
| 190 | const got_index = self.got_index.?; |
| 191 | return got_addr + got_index * ptr_bytes; |
| 192 | } |
| 193 | }; |
| 194 | |
| 195 | /// the plan9 debuginfo output is a bytecode with 4 opcodes |
| 196 | /// assume all numbers/variables are bytes |
| 197 | /// 0 w x y z -> interpret w x y z as a big-endian i32, and add it to the line offset |
| 198 | /// x when x < 65 -> add x to line offset |
| 199 | /// x when x < 129 -> subtract 64 from x and subtract it from the line offset |
| 200 | /// x -> subtract 129 from x, multiply it by the quanta of the instruction size |
| 201 | /// (1 on x86_64), and add it to the pc |
| 202 | /// after every opcode, add the quanta of the instruction size to the pc |
| 203 | pub const DebugInfoOutput = struct { |
| 204 | /// the actual opcodes |
| 205 | dbg_line: std.ArrayList(u8), |
| 206 | /// what line the debuginfo starts on |
| 207 | /// this helps because the linker might have to insert some opcodes to make sure that the line count starts at the right amount for the next decl |
| 208 | start_line: ?u32, |
| 209 | /// what the line count ends on after codegen |
| 210 | /// this helps because the linker might have to insert some opcodes to make sure that the line count starts at the right amount for the next decl |
| 211 | end_line: u32, |
| 212 | /// the last pc change op |
| 213 | /// This is very useful for adding quanta |
| 214 | /// to it if its not actually the last one. |
| 215 | pcop_change_index: ?u32, |
| 216 | /// cached pc quanta |
| 217 | pc_quanta: u8, |
| 218 | }; |
| 219 | |
| 220 | const NavMetadata = struct { |
| 221 | index: Atom.Index, |
| 222 | exports: std.ArrayListUnmanaged(usize) = .empty, |
| 223 | |
| 224 | fn getExport(m: NavMetadata, p9: *const Plan9, name: []const u8) ?usize { |
| 225 | for (m.exports.items) |exp| { |
| 226 | const sym = p9.syms.items[exp]; |
| 227 | if (mem.eql(u8, name, sym.name)) return exp; |
| 228 | } |
| 229 | return null; |
| 230 | } |
| 231 | }; |
| 232 | |
| 233 | const FnNavOutput = struct { |
| 234 | /// this code is modified when relocated so it is mutable |
| 235 | code: []u8, |
| 236 | /// this might have to be modified in the linker, so thats why its mutable |
| 237 | lineinfo: []u8, |
| 238 | start_line: u32, |
| 239 | end_line: u32, |
| 240 | }; |
| 241 | |
| 242 | fn getAddr(self: Plan9, addr: u64, t: aout.Sym.Type) u64 { |
| 243 | return addr + switch (t) { |
| 244 | .T, .t, .l, .L => self.bases.text, |
| 245 | .D, .d, .B, .b => self.bases.data, |
| 246 | else => unreachable, |
| 247 | }; |
| 248 | } |
| 249 | |
| 250 | fn getSymAddr(self: Plan9, s: aout.Sym) u64 { |
| 251 | return self.getAddr(s.value, s.type); |
| 252 | } |
| 253 | |
| 254 | pub fn defaultBaseAddrs(arch: std.Target.Cpu.Arch) Bases { |
| 255 | return switch (arch) { |
| 256 | .x86_64 => .{ |
| 257 | // header size => 40 => 0x28 |
| 258 | .text = 0x200028, |
| 259 | .data = 0x400000, |
| 260 | }, |
| 261 | .x86 => .{ |
| 262 | // header size => 32 => 0x20 |
| 263 | .text = 0x200020, |
| 264 | .data = 0x400000, |
| 265 | }, |
| 266 | .aarch64 => .{ |
| 267 | // header size => 40 => 0x28 |
| 268 | .text = 0x10028, |
| 269 | .data = 0x20000, |
| 270 | }, |
| 271 | else => std.debug.panic("find default base address for {}", .{arch}), |
| 272 | }; |
| 273 | } |
| 274 | |
| 275 | pub fn createEmpty( |
| 276 | arena: Allocator, |
| 277 | comp: *Compilation, |
| 278 | emit: Path, |
| 279 | options: link.File.OpenOptions, |
| 280 | ) !*Plan9 { |
| 281 | const target = &comp.root_mod.resolved_target.result; |
| 282 | const gpa = comp.gpa; |
| 283 | const optimize_mode = comp.root_mod.optimize_mode; |
| 284 | const output_mode = comp.config.output_mode; |
| 285 | |
| 286 | const sixtyfour_bit: bool = switch (target.ptrBitWidth()) { |
| 287 | 0...32 => false, |
| 288 | 33...64 => true, |
| 289 | else => return error.UnsupportedP9Architecture, |
| 290 | }; |
| 291 | |
| 292 | const self = try arena.create(Plan9); |
| 293 | self.* = .{ |
| 294 | .path_arena = std.heap.ArenaAllocator.init(gpa), |
| 295 | .base = .{ |
| 296 | .tag = .plan9, |
| 297 | .comp = comp, |
| 298 | .emit = emit, |
| 299 | .gc_sections = options.gc_sections orelse (optimize_mode != .Debug and output_mode != .Obj), |
| 300 | .print_gc_sections = options.print_gc_sections, |
| 301 | .stack_size = options.stack_size orelse 16777216, |
| 302 | .allow_shlib_undefined = options.allow_shlib_undefined orelse false, |
| 303 | .file = null, |
| 304 | .build_id = options.build_id, |
| 305 | }, |
| 306 | .sixtyfour_bit = sixtyfour_bit, |
| 307 | .bases = undefined, |
| 308 | .magic = try aout.magicFromArch(target.cpu.arch), |
| 309 | }; |
| 310 | // a / will always be in a file path |
| 311 | try self.file_segments.put(gpa, "/", 1); |
| 312 | return self; |
| 313 | } |
| 314 | |
| 315 | fn putFn(self: *Plan9, nav_index: InternPool.Nav.Index, out: FnNavOutput) !void { |
| 316 | const comp = self.base.comp; |
| 317 | const gpa = comp.gpa; |
| 318 | const zcu = comp.zcu.?; |
| 319 | const file_scope = zcu.navFileScopeIndex(nav_index); |
| 320 | const fn_map_res = try self.fn_nav_table.getOrPut(gpa, file_scope); |
| 321 | if (fn_map_res.found_existing) { |
| 322 | if (try fn_map_res.value_ptr.functions.fetchPut(gpa, nav_index, out)) |old_entry| { |
| 323 | gpa.free(old_entry.value.code); |
| 324 | gpa.free(old_entry.value.lineinfo); |
| 325 | } |
| 326 | } else { |
| 327 | const file = zcu.fileByIndex(file_scope); |
| 328 | const arena = self.path_arena.allocator(); |
| 329 | // each file gets a symbol |
| 330 | fn_map_res.value_ptr.* = .{ |
| 331 | .sym_index = blk: { |
| 332 | try self.syms.append(gpa, undefined); |
| 333 | try self.syms.append(gpa, undefined); |
| 334 | break :blk @as(u32, @intCast(self.syms.items.len - 1)); |
| 335 | }, |
| 336 | }; |
| 337 | try fn_map_res.value_ptr.functions.put(gpa, nav_index, out); |
| 338 | |
| 339 | var a = std.ArrayList(u8).init(arena); |
| 340 | errdefer a.deinit(); |
| 341 | // every 'z' starts with 0 |
| 342 | try a.append(0); |
| 343 | // path component value of '/' |
| 344 | try a.writer().writeInt(u16, 1, .big); |
| 345 | |
| 346 | // getting the full file path |
| 347 | { |
| 348 | const full_path = try file.path.toAbsolute(comp.dirs, gpa); |
| 349 | defer gpa.free(full_path); |
| 350 | try self.addPathComponents(full_path, &a); |
| 351 | } |
| 352 | |
| 353 | // null terminate |
| 354 | try a.append(0); |
| 355 | const final = try a.toOwnedSlice(); |
| 356 | self.syms.items[fn_map_res.value_ptr.sym_index - 1] = .{ |
| 357 | .type = .z, |
| 358 | .value = 1, |
| 359 | .name = final, |
| 360 | }; |
| 361 | self.syms.items[fn_map_res.value_ptr.sym_index] = .{ |
| 362 | .type = .z, |
| 363 | // just put a giant number, no source file will have this many newlines |
| 364 | .value = std.math.maxInt(u31), |
| 365 | .name = &.{ 0, 0 }, |
| 366 | }; |
| 367 | } |
| 368 | } |
| 369 | |
| 370 | fn addPathComponents(self: *Plan9, path: []const u8, a: *std.ArrayList(u8)) !void { |
| 371 | const gpa = self.base.comp.gpa; |
| 372 | const sep = std.fs.path.sep; |
| 373 | var it = std.mem.tokenizeScalar(u8, path, sep); |
| 374 | while (it.next()) |component| { |
| 375 | if (self.file_segments.get(component)) |num| { |
| 376 | try a.writer().writeInt(u16, num, .big); |
| 377 | } else { |
| 378 | self.file_segments_i += 1; |
| 379 | try self.file_segments.put(gpa, component, self.file_segments_i); |
| 380 | try a.writer().writeInt(u16, self.file_segments_i, .big); |
| 381 | } |
| 382 | } |
| 383 | } |
| 384 | |
| 385 | pub fn updateFunc( |
| 386 | self: *Plan9, |
| 387 | pt: Zcu.PerThread, |
| 388 | func_index: InternPool.Index, |
| 389 | mir: *const codegen.AnyMir, |
| 390 | ) link.File.UpdateNavError!void { |
| 391 | if (build_options.skip_non_native and builtin.object_format != .plan9) { |
| 392 | @panic("Attempted to compile for object format that was disabled by build configuration"); |
| 393 | } |
| 394 | |
| 395 | const zcu = pt.zcu; |
| 396 | const gpa = zcu.gpa; |
| 397 | const target = &self.base.comp.root_mod.resolved_target.result; |
| 398 | const func = zcu.funcInfo(func_index); |
| 399 | |
| 400 | const atom_idx = try self.seeNav(pt, func.owner_nav); |
| 401 | |
| 402 | var code_buffer: std.ArrayListUnmanaged(u8) = .empty; |
| 403 | defer code_buffer.deinit(gpa); |
| 404 | var dbg_info_output: DebugInfoOutput = .{ |
| 405 | .dbg_line = std.ArrayList(u8).init(gpa), |
| 406 | .start_line = null, |
| 407 | .end_line = undefined, |
| 408 | .pcop_change_index = null, |
| 409 | // we have already checked the target in the linker to make sure it is compatable |
| 410 | .pc_quanta = aout.getPCQuant(target.cpu.arch) catch unreachable, |
| 411 | }; |
| 412 | defer dbg_info_output.dbg_line.deinit(); |
| 413 | |
| 414 | try codegen.emitFunction( |
| 415 | &self.base, |
| 416 | pt, |
| 417 | zcu.navSrcLoc(func.owner_nav), |
| 418 | func_index, |
| 419 | mir, |
| 420 | &code_buffer, |
| 421 | .{ .plan9 = &dbg_info_output }, |
| 422 | ); |
| 423 | const code = try code_buffer.toOwnedSlice(gpa); |
| 424 | self.getAtomPtr(atom_idx).code = .{ |
| 425 | .code_ptr = null, |
| 426 | .other = .{ .nav_index = func.owner_nav }, |
| 427 | }; |
| 428 | const out: FnNavOutput = .{ |
| 429 | .code = code, |
| 430 | .lineinfo = try dbg_info_output.dbg_line.toOwnedSlice(), |
| 431 | .start_line = dbg_info_output.start_line.?, |
| 432 | .end_line = dbg_info_output.end_line, |
| 433 | }; |
| 434 | try self.putFn(func.owner_nav, out); |
| 435 | return self.updateFinish(pt, func.owner_nav); |
| 436 | } |
| 437 | |
| 438 | pub fn updateNav(self: *Plan9, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) link.File.UpdateNavError!void { |
| 439 | const zcu = pt.zcu; |
| 440 | const gpa = zcu.gpa; |
| 441 | const ip = &zcu.intern_pool; |
| 442 | const nav = ip.getNav(nav_index); |
| 443 | const nav_val = zcu.navValue(nav_index); |
| 444 | const nav_init = switch (ip.indexToKey(nav_val.toIntern())) { |
| 445 | .func => return, |
| 446 | .variable => |variable| Value.fromInterned(variable.init), |
| 447 | .@"extern" => { |
| 448 | log.debug("found extern decl: {f}", .{nav.name.fmt(ip)}); |
| 449 | return; |
| 450 | }, |
| 451 | else => nav_val, |
| 452 | }; |
| 453 | |
| 454 | if (nav_init.typeOf(zcu).hasRuntimeBits(zcu)) { |
| 455 | const atom_idx = try self.seeNav(pt, nav_index); |
| 456 | |
| 457 | var code_buffer: std.ArrayListUnmanaged(u8) = .empty; |
| 458 | defer code_buffer.deinit(gpa); |
| 459 | // TODO we need the symbol index for symbol in the table of locals for the containing atom |
| 460 | try codegen.generateSymbol( |
| 461 | &self.base, |
| 462 | pt, |
| 463 | zcu.navSrcLoc(nav_index), |
| 464 | nav_init, |
| 465 | &code_buffer, |
| 466 | .{ .atom_index = @intCast(atom_idx) }, |
| 467 | ); |
| 468 | const code = code_buffer.items; |
| 469 | try self.data_nav_table.ensureUnusedCapacity(gpa, 1); |
| 470 | const duped_code = try gpa.dupe(u8, code); |
| 471 | self.getAtomPtr(self.navs.get(nav_index).?.index).code = .{ .code_ptr = null, .other = .{ .nav_index = nav_index } }; |
| 472 | if (self.data_nav_table.fetchPutAssumeCapacity(nav_index, duped_code)) |old_entry| { |
| 473 | gpa.free(old_entry.value); |
| 474 | } |
| 475 | try self.updateFinish(pt, nav_index); |
| 476 | } |
| 477 | } |
| 478 | |
| 479 | /// called at the end of update{Decl,Func} |
| 480 | fn updateFinish(self: *Plan9, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) !void { |
| 481 | const zcu = pt.zcu; |
| 482 | const gpa = zcu.gpa; |
| 483 | const ip = &zcu.intern_pool; |
| 484 | const nav = ip.getNav(nav_index); |
| 485 | const is_fn = ip.isFunctionType(nav.typeOf(ip)); |
| 486 | const sym_t: aout.Sym.Type = if (is_fn) .t else .d; |
| 487 | |
| 488 | const atom = self.getAtomPtr(self.navs.get(nav_index).?.index); |
| 489 | // write the internal linker metadata |
| 490 | atom.type = sym_t; |
| 491 | // write the symbol |
| 492 | // we already have the got index |
| 493 | const sym: aout.Sym = .{ |
| 494 | .value = undefined, // the value of stuff gets filled in in flush |
| 495 | .type = atom.type, |
| 496 | .name = try gpa.dupe(u8, nav.name.toSlice(ip)), |
| 497 | }; |
| 498 | |
| 499 | if (atom.sym_index) |s| { |
| 500 | self.syms.items[s] = sym; |
| 501 | } else { |
| 502 | const s = try self.allocateSymbolIndex(); |
| 503 | atom.sym_index = s; |
| 504 | self.syms.items[s] = sym; |
| 505 | } |
| 506 | } |
| 507 | |
| 508 | fn allocateSymbolIndex(self: *Plan9) !usize { |
| 509 | const gpa = self.base.comp.gpa; |
| 510 | if (self.syms_index_free_list.pop()) |i| { |
| 511 | return i; |
| 512 | } else { |
| 513 | _ = try self.syms.addOne(gpa); |
| 514 | return self.syms.items.len - 1; |
| 515 | } |
| 516 | } |
| 517 | |
| 518 | fn allocateGotIndex(self: *Plan9) usize { |
| 519 | if (self.got_index_free_list.pop()) |i| { |
| 520 | return i; |
| 521 | } else { |
| 522 | self.got_len += 1; |
| 523 | return self.got_len - 1; |
| 524 | } |
| 525 | } |
| 526 | |
| 527 | pub fn changeLine(l: *std.ArrayList(u8), delta_line: i32) !void { |
| 528 | if (delta_line > 0 and delta_line < 65) { |
| 529 | const toappend = @as(u8, @intCast(delta_line)); |
| 530 | try l.append(toappend); |
| 531 | } else if (delta_line < 0 and delta_line > -65) { |
| 532 | const toadd: u8 = @as(u8, @intCast(-delta_line + 64)); |
| 533 | try l.append(toadd); |
| 534 | } else if (delta_line != 0) { |
| 535 | try l.append(0); |
| 536 | try l.writer().writeInt(i32, delta_line, .big); |
| 537 | } |
| 538 | } |
| 539 | |
| 540 | fn externCount(self: *Plan9) usize { |
| 541 | var extern_atom_count: usize = 0; |
| 542 | for (self.etext_edata_end_atom_indices) |idx| { |
| 543 | if (idx != null) extern_atom_count += 1; |
| 544 | } |
| 545 | return extern_atom_count; |
| 546 | } |
| 547 | // counts decls, and lazy syms |
| 548 | fn atomCount(self: *Plan9) usize { |
| 549 | var fn_nav_count: usize = 0; |
| 550 | var itf_files = self.fn_nav_table.iterator(); |
| 551 | while (itf_files.next()) |ent| { |
| 552 | // get the submap |
| 553 | var submap = ent.value_ptr.functions; |
| 554 | fn_nav_count += submap.count(); |
| 555 | } |
| 556 | const data_nav_count = self.data_nav_table.count(); |
| 557 | var lazy_atom_count: usize = 0; |
| 558 | var it_lazy = self.lazy_syms.iterator(); |
| 559 | while (it_lazy.next()) |kv| { |
| 560 | lazy_atom_count += kv.value_ptr.numberOfAtoms(); |
| 561 | } |
| 562 | const uav_atom_count = self.uavs.count(); |
| 563 | const extern_atom_count = self.externCount(); |
| 564 | return data_nav_count + fn_nav_count + lazy_atom_count + extern_atom_count + uav_atom_count; |
| 565 | } |
| 566 | |
| 567 | pub fn flush( |
| 568 | self: *Plan9, |
| 569 | arena: Allocator, |
| 570 | /// TODO: stop using this |
| 571 | tid: Zcu.PerThread.Id, |
| 572 | prog_node: std.Progress.Node, |
| 573 | ) link.File.FlushError!void { |
| 574 | if (build_options.skip_non_native and builtin.object_format != .plan9) { |
| 575 | @panic("Attempted to compile for object format that was disabled by build configuration"); |
| 576 | } |
| 577 | |
| 578 | const tracy = trace(@src()); |
| 579 | defer tracy.end(); |
| 580 | |
| 581 | _ = arena; // Has the same lifetime as the call to Compilation.update. |
| 582 | |
| 583 | const comp = self.base.comp; |
| 584 | const diags = &comp.link_diags; |
| 585 | const gpa = comp.gpa; |
| 586 | const target = &comp.root_mod.resolved_target.result; |
| 587 | |
| 588 | switch (comp.config.output_mode) { |
| 589 | .Exe => {}, |
| 590 | .Obj => return diags.fail("writing plan9 object files unimplemented", .{}), |
| 591 | .Lib => return diags.fail("writing plan9 lib files unimplemented", .{}), |
| 592 | } |
| 593 | |
| 594 | const sub_prog_node = prog_node.start("Flush Module", 0); |
| 595 | defer sub_prog_node.end(); |
| 596 | |
| 597 | log.debug("flush", .{}); |
| 598 | |
| 599 | defer assert(self.hdr.entry != 0x0); |
| 600 | |
| 601 | const pt: Zcu.PerThread = .activate( |
| 602 | self.base.comp.zcu orelse return diags.fail("linking without zig source unimplemented", .{}), |
| 603 | tid, |
| 604 | ); |
| 605 | defer pt.deactivate(); |
| 606 | |
| 607 | // finish up the lazy syms |
| 608 | if (self.lazy_syms.getPtr(.none)) |metadata| { |
| 609 | // Most lazy symbols can be updated on first use, but |
| 610 | // anyerror needs to wait for everything to be flushed. |
| 611 | if (metadata.text_state != .unused) try self.updateLazySymbolAtom( |
| 612 | pt, |
| 613 | .{ .kind = .code, .ty = .anyerror_type }, |
| 614 | metadata.text_atom, |
| 615 | ); |
| 616 | if (metadata.rodata_state != .unused) try self.updateLazySymbolAtom( |
| 617 | pt, |
| 618 | .{ .kind = .const_data, .ty = .anyerror_type }, |
| 619 | metadata.rodata_atom, |
| 620 | ); |
| 621 | } |
| 622 | for (self.lazy_syms.values()) |*metadata| { |
| 623 | if (metadata.text_state != .unused) metadata.text_state = .flushed; |
| 624 | if (metadata.rodata_state != .unused) metadata.rodata_state = .flushed; |
| 625 | } |
| 626 | // make sure the got table is good |
| 627 | const atom_count = self.atomCount(); |
| 628 | assert(self.got_len == atom_count + self.got_index_free_list.items.len); |
| 629 | const got_size = self.got_len * if (!self.sixtyfour_bit) @as(u32, 4) else 8; |
| 630 | var got_table = try gpa.alloc(u8, got_size); |
| 631 | defer gpa.free(got_table); |
| 632 | |
| 633 | // + 4 for header, got, symbols, linecountinfo |
| 634 | var iovecs = try gpa.alloc(std.posix.iovec_const, self.atomCount() + 4 - self.externCount()); |
| 635 | defer gpa.free(iovecs); |
| 636 | |
| 637 | const file = self.base.file.?; |
| 638 | |
| 639 | var hdr_buf: [40]u8 = undefined; |
| 640 | // account for the fat header |
| 641 | const hdr_size: usize = if (self.sixtyfour_bit) 40 else 32; |
| 642 | const hdr_slice: []u8 = hdr_buf[0..hdr_size]; |
| 643 | var foff = hdr_size; |
| 644 | iovecs[0] = .{ .base = hdr_slice.ptr, .len = hdr_slice.len }; |
| 645 | var iovecs_i: usize = 1; |
| 646 | var text_i: u64 = 0; |
| 647 | |
| 648 | var linecountinfo = std.ArrayList(u8).init(gpa); |
| 649 | defer linecountinfo.deinit(); |
| 650 | // text |
| 651 | { |
| 652 | var linecount: i64 = -1; |
| 653 | var it_file = self.fn_nav_table.iterator(); |
| 654 | while (it_file.next()) |fentry| { |
| 655 | var it = fentry.value_ptr.functions.iterator(); |
| 656 | while (it.next()) |entry| { |
| 657 | const nav_index = entry.key_ptr.*; |
| 658 | const nav = pt.zcu.intern_pool.getNav(nav_index); |
| 659 | const atom = self.getAtomPtr(self.navs.get(nav_index).?.index); |
| 660 | const out = entry.value_ptr.*; |
| 661 | { |
| 662 | // connect the previous decl to the next |
| 663 | const delta_line = @as(i32, @intCast(out.start_line)) - @as(i32, @intCast(linecount)); |
| 664 | |
| 665 | try changeLine(&linecountinfo, delta_line); |
| 666 | // TODO change the pc too (maybe?) |
| 667 | |
| 668 | // write out the actual info that was generated in codegen now |
| 669 | try linecountinfo.appendSlice(out.lineinfo); |
| 670 | linecount = out.end_line; |
| 671 | } |
| 672 | foff += out.code.len; |
| 673 | iovecs[iovecs_i] = .{ .base = out.code.ptr, .len = out.code.len }; |
| 674 | iovecs_i += 1; |
| 675 | const off = self.getAddr(text_i, .t); |
| 676 | text_i += out.code.len; |
| 677 | atom.offset = off; |
| 678 | log.debug("write text nav 0x{x} ({f}), lines {d} to {d}.;__GOT+0x{x} vaddr: 0x{x}", .{ nav_index, nav.name.fmt(&pt.zcu.intern_pool), out.start_line + 1, out.end_line, atom.got_index.? * 8, off }); |
| 679 | if (!self.sixtyfour_bit) { |
| 680 | mem.writeInt(u32, got_table[atom.got_index.? * 4 ..][0..4], @intCast(off), target.cpu.arch.endian()); |
| 681 | } else { |
| 682 | mem.writeInt(u64, got_table[atom.got_index.? * 8 ..][0..8], off, target.cpu.arch.endian()); |
| 683 | } |
| 684 | self.syms.items[atom.sym_index.?].value = off; |
| 685 | if (self.nav_exports.get(nav_index)) |export_indices| { |
| 686 | try self.addNavExports(pt.zcu, nav_index, export_indices); |
| 687 | } |
| 688 | } |
| 689 | } |
| 690 | if (linecountinfo.items.len & 1 == 1) { |
| 691 | // just a nop to make it even, the plan9 linker does this |
| 692 | try linecountinfo.append(129); |
| 693 | } |
| 694 | } |
| 695 | // the text lazy symbols |
| 696 | { |
| 697 | var it = self.lazy_syms.iterator(); |
| 698 | while (it.next()) |kv| { |
| 699 | const meta = kv.value_ptr; |
| 700 | const text_atom = if (meta.text_state != .unused) self.getAtomPtr(meta.text_atom) else continue; |
| 701 | const code = text_atom.code.getOwnedCode().?; |
| 702 | foff += code.len; |
| 703 | iovecs[iovecs_i] = .{ .base = code.ptr, .len = code.len }; |
| 704 | iovecs_i += 1; |
| 705 | const off = self.getAddr(text_i, .t); |
| 706 | text_i += code.len; |
| 707 | text_atom.offset = off; |
| 708 | if (!self.sixtyfour_bit) { |
| 709 | mem.writeInt(u32, got_table[text_atom.got_index.? * 4 ..][0..4], @as(u32, @intCast(off)), target.cpu.arch.endian()); |
| 710 | } else { |
| 711 | mem.writeInt(u64, got_table[text_atom.got_index.? * 8 ..][0..8], off, target.cpu.arch.endian()); |
| 712 | } |
| 713 | self.syms.items[text_atom.sym_index.?].value = off; |
| 714 | } |
| 715 | } |
| 716 | // fix the sym for etext |
| 717 | if (self.etext_edata_end_atom_indices[0]) |etext_atom_idx| { |
| 718 | const etext_atom = self.getAtom(etext_atom_idx); |
| 719 | const val = self.getAddr(text_i, .t); |
| 720 | self.syms.items[etext_atom.sym_index.?].value = val; |
| 721 | if (!self.sixtyfour_bit) { |
| 722 | mem.writeInt(u32, got_table[etext_atom.got_index.? * 4 ..][0..4], @as(u32, @intCast(val)), target.cpu.arch.endian()); |
| 723 | } else { |
| 724 | mem.writeInt(u64, got_table[etext_atom.got_index.? * 8 ..][0..8], val, target.cpu.arch.endian()); |
| 725 | } |
| 726 | } |
| 727 | // global offset table is in data |
| 728 | iovecs[iovecs_i] = .{ .base = got_table.ptr, .len = got_table.len }; |
| 729 | iovecs_i += 1; |
| 730 | // data |
| 731 | var data_i: u64 = got_size; |
| 732 | { |
| 733 | var it = self.data_nav_table.iterator(); |
| 734 | while (it.next()) |entry| { |
| 735 | const nav_index = entry.key_ptr.*; |
| 736 | const atom = self.getAtomPtr(self.navs.get(nav_index).?.index); |
| 737 | const code = entry.value_ptr.*; |
| 738 | |
| 739 | foff += code.len; |
| 740 | iovecs[iovecs_i] = .{ .base = code.ptr, .len = code.len }; |
| 741 | iovecs_i += 1; |
| 742 | const off = self.getAddr(data_i, .d); |
| 743 | data_i += code.len; |
| 744 | atom.offset = off; |
| 745 | if (!self.sixtyfour_bit) { |
| 746 | mem.writeInt(u32, got_table[atom.got_index.? * 4 ..][0..4], @as(u32, @intCast(off)), target.cpu.arch.endian()); |
| 747 | } else { |
| 748 | mem.writeInt(u64, got_table[atom.got_index.? * 8 ..][0..8], off, target.cpu.arch.endian()); |
| 749 | } |
| 750 | self.syms.items[atom.sym_index.?].value = off; |
| 751 | if (self.nav_exports.get(nav_index)) |export_indices| { |
| 752 | try self.addNavExports(pt.zcu, nav_index, export_indices); |
| 753 | } |
| 754 | } |
| 755 | { |
| 756 | var it_uav = self.uavs.iterator(); |
| 757 | while (it_uav.next()) |kv| { |
| 758 | const atom = self.getAtomPtr(kv.value_ptr.*); |
| 759 | const code = atom.code.getOwnedCode().?; |
| 760 | log.debug("write anon decl: {s}", .{self.syms.items[atom.sym_index.?].name}); |
| 761 | foff += code.len; |
| 762 | iovecs[iovecs_i] = .{ .base = code.ptr, .len = code.len }; |
| 763 | iovecs_i += 1; |
| 764 | const off = self.getAddr(data_i, .d); |
| 765 | data_i += code.len; |
| 766 | atom.offset = off; |
| 767 | if (!self.sixtyfour_bit) { |
| 768 | mem.writeInt(u32, got_table[atom.got_index.? * 4 ..][0..4], @as(u32, @intCast(off)), target.cpu.arch.endian()); |
| 769 | } else { |
| 770 | mem.writeInt(u64, got_table[atom.got_index.? * 8 ..][0..8], off, target.cpu.arch.endian()); |
| 771 | } |
| 772 | self.syms.items[atom.sym_index.?].value = off; |
| 773 | } |
| 774 | } |
| 775 | // the lazy data symbols |
| 776 | var it_lazy = self.lazy_syms.iterator(); |
| 777 | while (it_lazy.next()) |kv| { |
| 778 | const meta = kv.value_ptr; |
| 779 | const data_atom = if (meta.rodata_state != .unused) self.getAtomPtr(meta.rodata_atom) else continue; |
| 780 | const code = data_atom.code.getOwnedCode().?; // lazy symbols must own their code |
| 781 | foff += code.len; |
| 782 | iovecs[iovecs_i] = .{ .base = code.ptr, .len = code.len }; |
| 783 | iovecs_i += 1; |
| 784 | const off = self.getAddr(data_i, .d); |
| 785 | data_i += code.len; |
| 786 | data_atom.offset = off; |
| 787 | if (!self.sixtyfour_bit) { |
| 788 | mem.writeInt(u32, got_table[data_atom.got_index.? * 4 ..][0..4], @as(u32, @intCast(off)), target.cpu.arch.endian()); |
| 789 | } else { |
| 790 | mem.writeInt(u64, got_table[data_atom.got_index.? * 8 ..][0..8], off, target.cpu.arch.endian()); |
| 791 | } |
| 792 | self.syms.items[data_atom.sym_index.?].value = off; |
| 793 | } |
| 794 | // edata symbol |
| 795 | if (self.etext_edata_end_atom_indices[1]) |edata_atom_idx| { |
| 796 | const edata_atom = self.getAtom(edata_atom_idx); |
| 797 | const val = self.getAddr(data_i, .b); |
| 798 | self.syms.items[edata_atom.sym_index.?].value = val; |
| 799 | if (!self.sixtyfour_bit) { |
| 800 | mem.writeInt(u32, got_table[edata_atom.got_index.? * 4 ..][0..4], @as(u32, @intCast(val)), target.cpu.arch.endian()); |
| 801 | } else { |
| 802 | mem.writeInt(u64, got_table[edata_atom.got_index.? * 8 ..][0..8], val, target.cpu.arch.endian()); |
| 803 | } |
| 804 | } |
| 805 | // end symbol (same as edata because native backends don't do .bss yet) |
| 806 | if (self.etext_edata_end_atom_indices[2]) |end_atom_idx| { |
| 807 | const end_atom = self.getAtom(end_atom_idx); |
| 808 | const val = self.getAddr(data_i, .b); |
| 809 | self.syms.items[end_atom.sym_index.?].value = val; |
| 810 | if (!self.sixtyfour_bit) { |
| 811 | mem.writeInt(u32, got_table[end_atom.got_index.? * 4 ..][0..4], @as(u32, @intCast(val)), target.cpu.arch.endian()); |
| 812 | } else { |
| 813 | log.debug("write end (got_table[0x{x}] = 0x{x})", .{ end_atom.got_index.? * 8, val }); |
| 814 | mem.writeInt(u64, got_table[end_atom.got_index.? * 8 ..][0..8], val, target.cpu.arch.endian()); |
| 815 | } |
| 816 | } |
| 817 | } |
| 818 | var sym_buf = std.ArrayList(u8).init(gpa); |
| 819 | try self.writeSyms(&sym_buf); |
| 820 | const syms = try sym_buf.toOwnedSlice(); |
| 821 | defer gpa.free(syms); |
| 822 | assert(2 + self.atomCount() - self.externCount() == iovecs_i); // we didn't write all the decls |
| 823 | iovecs[iovecs_i] = .{ .base = syms.ptr, .len = syms.len }; |
| 824 | iovecs_i += 1; |
| 825 | iovecs[iovecs_i] = .{ .base = linecountinfo.items.ptr, .len = linecountinfo.items.len }; |
| 826 | iovecs_i += 1; |
| 827 | // generate the header |
| 828 | self.hdr = .{ |
| 829 | .magic = self.magic, |
| 830 | .text = @as(u32, @intCast(text_i)), |
| 831 | .data = @as(u32, @intCast(data_i)), |
| 832 | .syms = @as(u32, @intCast(syms.len)), |
| 833 | .bss = 0, |
| 834 | .spsz = 0, |
| 835 | .pcsz = @as(u32, @intCast(linecountinfo.items.len)), |
| 836 | .entry = @as(u32, @intCast(self.entry_val.?)), |
| 837 | }; |
| 838 | @memcpy(hdr_slice, self.hdr.toU8s()[0..hdr_size]); |
| 839 | // write the fat header for 64 bit entry points |
| 840 | if (self.sixtyfour_bit) { |
| 841 | mem.writeInt(u64, hdr_buf[32..40], self.entry_val.?, .big); |
| 842 | } |
| 843 | // perform the relocs |
| 844 | { |
| 845 | var it = self.relocs.iterator(); |
| 846 | while (it.next()) |kv| { |
| 847 | const source_atom_index = kv.key_ptr.*; |
| 848 | const source_atom = self.getAtom(source_atom_index); |
| 849 | const source_atom_symbol = self.syms.items[source_atom.sym_index.?]; |
| 850 | const code = source_atom.code.getCode(self); |
| 851 | const endian = target.cpu.arch.endian(); |
| 852 | for (kv.value_ptr.items) |reloc| { |
| 853 | const offset = reloc.offset; |
| 854 | const addend = reloc.addend; |
| 855 | if (reloc.type == .pcrel or reloc.type == .nonpcrel) { |
| 856 | const target_atom_index = reloc.target; |
| 857 | const target_atom = self.getAtomPtr(target_atom_index); |
| 858 | const target_symbol = self.syms.items[target_atom.sym_index.?]; |
| 859 | const target_offset = target_atom.offset.?; |
| 860 | |
| 861 | switch (reloc.type) { |
| 862 | .pcrel => { |
| 863 | const disp = @as(i32, @intCast(target_offset)) - @as(i32, @intCast(source_atom.offset.?)) - 4 - @as(i32, @intCast(offset)); |
| 864 | mem.writeInt(i32, code[@as(usize, @intCast(offset))..][0..4], @as(i32, @intCast(disp)), endian); |
| 865 | }, |
| 866 | .nonpcrel => { |
| 867 | if (!self.sixtyfour_bit) { |
| 868 | mem.writeInt(u32, code[@intCast(offset)..][0..4], @as(u32, @intCast(target_offset + addend)), endian); |
| 869 | } else { |
| 870 | mem.writeInt(u64, code[@intCast(offset)..][0..8], target_offset + addend, endian); |
| 871 | } |
| 872 | }, |
| 873 | else => unreachable, |
| 874 | } |
| 875 | log.debug("relocating the address of '{s}' + {d} into '{s}' + {d} (({s}[{d}] = 0x{x} + 0x{x})", .{ target_symbol.name, addend, source_atom_symbol.name, offset, source_atom_symbol.name, offset, target_offset, addend }); |
| 876 | } else { |
| 877 | const addr = switch (reloc.type) { |
| 878 | .special_etext => self.syms.items[self.getAtom(self.etext_edata_end_atom_indices[0].?).sym_index.?].value, |
| 879 | .special_edata => self.syms.items[self.getAtom(self.etext_edata_end_atom_indices[1].?).sym_index.?].value, |
| 880 | .special_end => self.syms.items[self.getAtom(self.etext_edata_end_atom_indices[2].?).sym_index.?].value, |
| 881 | else => unreachable, |
| 882 | }; |
| 883 | if (!self.sixtyfour_bit) { |
| 884 | mem.writeInt(u32, code[@intCast(offset)..][0..4], @as(u32, @intCast(addr + addend)), endian); |
| 885 | } else { |
| 886 | mem.writeInt(u64, code[@intCast(offset)..][0..8], addr + addend, endian); |
| 887 | } |
| 888 | log.debug("relocating the address of '{s}' + {d} into '{s}' + {d} (({s}[{d}] = 0x{x} + 0x{x})", .{ @tagName(reloc.type), addend, source_atom_symbol.name, offset, source_atom_symbol.name, offset, addr, addend }); |
| 889 | } |
| 890 | } |
| 891 | } |
| 892 | } |
| 893 | file.pwritevAll(iovecs, 0) catch |err| return diags.fail("failed to write file: {s}", .{@errorName(err)}); |
| 894 | } |
| 895 | fn addNavExports( |
| 896 | self: *Plan9, |
| 897 | zcu: *Zcu, |
| 898 | nav_index: InternPool.Nav.Index, |
| 899 | export_indices: []const Zcu.Export.Index, |
| 900 | ) !void { |
| 901 | const gpa = self.base.comp.gpa; |
| 902 | const metadata = self.navs.getPtr(nav_index).?; |
| 903 | const atom = self.getAtom(metadata.index); |
| 904 | |
| 905 | for (export_indices) |export_idx| { |
| 906 | const exp = export_idx.ptr(zcu); |
| 907 | const exp_name = exp.opts.name.toSlice(&zcu.intern_pool); |
| 908 | // plan9 does not support custom sections |
| 909 | if (exp.opts.section.unwrap()) |section_name| { |
| 910 | if (!section_name.eqlSlice(".text", &zcu.intern_pool) and |
| 911 | !section_name.eqlSlice(".data", &zcu.intern_pool)) |
| 912 | { |
| 913 | try zcu.failed_exports.put(zcu.gpa, export_idx, try Zcu.ErrorMsg.create( |
| 914 | gpa, |
| 915 | zcu.navSrcLoc(nav_index), |
| 916 | "plan9 does not support extra sections", |
| 917 | .{}, |
| 918 | )); |
| 919 | break; |
| 920 | } |
| 921 | } |
| 922 | const sym: aout.Sym = .{ |
| 923 | .value = atom.offset.?, |
| 924 | .type = atom.type.toGlobal(), |
| 925 | .name = try gpa.dupe(u8, exp_name), |
| 926 | }; |
| 927 | |
| 928 | if (metadata.getExport(self, exp_name)) |i| { |
| 929 | self.syms.items[i] = sym; |
| 930 | } else { |
| 931 | try self.syms.append(gpa, sym); |
| 932 | try metadata.exports.append(gpa, self.syms.items.len - 1); |
| 933 | } |
| 934 | } |
| 935 | } |
| 936 | |
| 937 | fn createAtom(self: *Plan9) !Atom.Index { |
| 938 | const gpa = self.base.comp.gpa; |
| 939 | const index = @as(Atom.Index, @intCast(self.atoms.items.len)); |
| 940 | const atom = try self.atoms.addOne(gpa); |
| 941 | atom.* = .{ |
| 942 | .type = .t, |
| 943 | .offset = null, |
| 944 | .sym_index = null, |
| 945 | .got_index = null, |
| 946 | .code = undefined, |
| 947 | }; |
| 948 | return index; |
| 949 | } |
| 950 | |
| 951 | pub fn seeNav(self: *Plan9, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) !Atom.Index { |
| 952 | const zcu = pt.zcu; |
| 953 | const ip = &zcu.intern_pool; |
| 954 | const gpa = zcu.gpa; |
| 955 | const gop = try self.navs.getOrPut(gpa, nav_index); |
| 956 | if (!gop.found_existing) { |
| 957 | const index = try self.createAtom(); |
| 958 | self.getAtomPtr(index).got_index = self.allocateGotIndex(); |
| 959 | gop.value_ptr.* = .{ |
| 960 | .index = index, |
| 961 | .exports = .{}, |
| 962 | }; |
| 963 | } |
| 964 | const atom_idx = gop.value_ptr.index; |
| 965 | // handle externs here because they might not get updateDecl called on them |
| 966 | const nav = ip.getNav(nav_index); |
| 967 | if (nav.getExtern(ip) != null) { |
| 968 | // this is a "phantom atom" - it is never actually written to disk, just convenient for us to store stuff about externs |
| 969 | if (nav.name.eqlSlice("etext", ip)) { |
| 970 | self.etext_edata_end_atom_indices[0] = atom_idx; |
| 971 | } else if (nav.name.eqlSlice("edata", ip)) { |
| 972 | self.etext_edata_end_atom_indices[1] = atom_idx; |
| 973 | } else if (nav.name.eqlSlice("end", ip)) { |
| 974 | self.etext_edata_end_atom_indices[2] = atom_idx; |
| 975 | } |
| 976 | try self.updateFinish(pt, nav_index); |
| 977 | log.debug("seeNav(extern) for {f} (got_addr=0x{x})", .{ |
| 978 | nav.name.fmt(ip), |
| 979 | self.getAtom(atom_idx).getOffsetTableAddress(self), |
| 980 | }); |
| 981 | } else log.debug("seeNav for {f}", .{nav.name.fmt(ip)}); |
| 982 | return atom_idx; |
| 983 | } |
| 984 | |
| 985 | pub fn updateExports( |
| 986 | self: *Plan9, |
| 987 | pt: Zcu.PerThread, |
| 988 | exported: Zcu.Exported, |
| 989 | export_indices: []const Zcu.Export.Index, |
| 990 | ) !void { |
| 991 | const gpa = self.base.comp.gpa; |
| 992 | switch (exported) { |
| 993 | .uav => @panic("TODO: plan9 updateExports handling values"), |
| 994 | .nav => |nav| { |
| 995 | _ = try self.seeNav(pt, nav); |
| 996 | if (self.nav_exports.fetchSwapRemove(nav)) |kv| { |
| 997 | gpa.free(kv.value); |
| 998 | } |
| 999 | try self.nav_exports.ensureUnusedCapacity(gpa, 1); |
| 1000 | const duped_indices = try gpa.dupe(Zcu.Export.Index, export_indices); |
| 1001 | self.nav_exports.putAssumeCapacityNoClobber(nav, duped_indices); |
| 1002 | }, |
| 1003 | } |
| 1004 | // all proper work is done in flush |
| 1005 | } |
| 1006 | |
| 1007 | pub fn getOrCreateAtomForLazySymbol(self: *Plan9, pt: Zcu.PerThread, lazy_sym: File.LazySymbol) !Atom.Index { |
| 1008 | const gop = try self.lazy_syms.getOrPut(pt.zcu.gpa, lazy_sym.ty); |
| 1009 | errdefer _ = if (!gop.found_existing) self.lazy_syms.pop(); |
| 1010 | |
| 1011 | if (!gop.found_existing) gop.value_ptr.* = .{}; |
| 1012 | |
| 1013 | const atom_ptr, const state_ptr = switch (lazy_sym.kind) { |
| 1014 | .code => .{ &gop.value_ptr.text_atom, &gop.value_ptr.text_state }, |
| 1015 | .const_data => .{ &gop.value_ptr.rodata_atom, &gop.value_ptr.rodata_state }, |
| 1016 | }; |
| 1017 | switch (state_ptr.*) { |
| 1018 | .unused => atom_ptr.* = try self.createAtom(), |
| 1019 | .pending_flush => return atom_ptr.*, |
| 1020 | .flushed => {}, |
| 1021 | } |
| 1022 | state_ptr.* = .pending_flush; |
| 1023 | const atom = atom_ptr.*; |
| 1024 | _ = try self.getAtomPtr(atom).getOrCreateSymbolTableEntry(self); |
| 1025 | _ = self.getAtomPtr(atom).getOrCreateOffsetTableEntry(self); |
| 1026 | // anyerror needs to be deferred until flush |
| 1027 | if (lazy_sym.ty != .anyerror_type) try self.updateLazySymbolAtom(pt, lazy_sym, atom); |
| 1028 | return atom; |
| 1029 | } |
| 1030 | |
| 1031 | fn updateLazySymbolAtom( |
| 1032 | self: *Plan9, |
| 1033 | pt: Zcu.PerThread, |
| 1034 | sym: File.LazySymbol, |
| 1035 | atom_index: Atom.Index, |
| 1036 | ) error{ LinkFailure, OutOfMemory }!void { |
| 1037 | const gpa = pt.zcu.gpa; |
| 1038 | const comp = self.base.comp; |
| 1039 | const diags = &comp.link_diags; |
| 1040 | |
| 1041 | var required_alignment: InternPool.Alignment = .none; |
| 1042 | var code_buffer: std.ArrayListUnmanaged(u8) = .empty; |
| 1043 | defer code_buffer.deinit(gpa); |
| 1044 | |
| 1045 | // create the symbol for the name |
| 1046 | const name = try std.fmt.allocPrint(gpa, "__lazy_{s}_{f}", .{ |
| 1047 | @tagName(sym.kind), |
| 1048 | Type.fromInterned(sym.ty).fmt(pt), |
| 1049 | }); |
| 1050 | |
| 1051 | const symbol: aout.Sym = .{ |
| 1052 | .value = undefined, |
| 1053 | .type = if (sym.kind == .code) .t else .d, |
| 1054 | .name = name, |
| 1055 | }; |
| 1056 | self.syms.items[self.getAtomPtr(atom_index).sym_index.?] = symbol; |
| 1057 | |
| 1058 | // generate the code |
| 1059 | const src = Type.fromInterned(sym.ty).srcLocOrNull(pt.zcu) orelse Zcu.LazySrcLoc.unneeded; |
| 1060 | codegen.generateLazySymbol( |
| 1061 | &self.base, |
| 1062 | pt, |
| 1063 | src, |
| 1064 | sym, |
| 1065 | &required_alignment, |
| 1066 | &code_buffer, |
| 1067 | .none, |
| 1068 | .{ .atom_index = @intCast(atom_index) }, |
| 1069 | ) catch |err| switch (err) { |
| 1070 | error.OutOfMemory => return error.OutOfMemory, |
| 1071 | error.CodegenFail => return error.LinkFailure, |
| 1072 | error.Overflow, |
| 1073 | error.RelocationNotByteAligned, |
| 1074 | => return diags.fail("unable to codegen: {s}", .{@errorName(err)}), |
| 1075 | }; |
| 1076 | const code = code_buffer.items; |
| 1077 | // duped_code is freed when the atom is freed |
| 1078 | const duped_code = try gpa.dupe(u8, code); |
| 1079 | errdefer gpa.free(duped_code); |
| 1080 | self.getAtomPtr(atom_index).code = .{ |
| 1081 | .code_ptr = duped_code.ptr, |
| 1082 | .other = .{ .code_len = duped_code.len }, |
| 1083 | }; |
| 1084 | } |
| 1085 | |
| 1086 | pub fn deinit(self: *Plan9) void { |
| 1087 | const gpa = self.base.comp.gpa; |
| 1088 | { |
| 1089 | var it = self.relocs.valueIterator(); |
| 1090 | while (it.next()) |relocs| { |
| 1091 | relocs.deinit(gpa); |
| 1092 | } |
| 1093 | self.relocs.deinit(gpa); |
| 1094 | } |
| 1095 | var it_lzc = self.lazy_syms.iterator(); |
| 1096 | while (it_lzc.next()) |kv| { |
| 1097 | if (kv.value_ptr.text_state != .unused) |
| 1098 | gpa.free(self.syms.items[self.getAtom(kv.value_ptr.text_atom).sym_index.?].name); |
| 1099 | if (kv.value_ptr.rodata_state != .unused) |
| 1100 | gpa.free(self.syms.items[self.getAtom(kv.value_ptr.rodata_atom).sym_index.?].name); |
| 1101 | } |
| 1102 | self.lazy_syms.deinit(gpa); |
| 1103 | var itf_files = self.fn_nav_table.iterator(); |
| 1104 | while (itf_files.next()) |ent| { |
| 1105 | // get the submap |
| 1106 | var submap = ent.value_ptr.functions; |
| 1107 | defer submap.deinit(gpa); |
| 1108 | var itf = submap.iterator(); |
| 1109 | while (itf.next()) |entry| { |
| 1110 | gpa.free(entry.value_ptr.code); |
| 1111 | gpa.free(entry.value_ptr.lineinfo); |
| 1112 | } |
| 1113 | } |
| 1114 | self.fn_nav_table.deinit(gpa); |
| 1115 | var itd = self.data_nav_table.iterator(); |
| 1116 | while (itd.next()) |entry| { |
| 1117 | gpa.free(entry.value_ptr.*); |
| 1118 | } |
| 1119 | var it_uav = self.uavs.iterator(); |
| 1120 | while (it_uav.next()) |entry| { |
| 1121 | const sym_index = self.getAtom(entry.value_ptr.*).sym_index.?; |
| 1122 | gpa.free(self.syms.items[sym_index].name); |
| 1123 | } |
| 1124 | self.data_nav_table.deinit(gpa); |
| 1125 | for (self.nav_exports.values()) |export_indices| { |
| 1126 | gpa.free(export_indices); |
| 1127 | } |
| 1128 | self.nav_exports.deinit(gpa); |
| 1129 | self.syms.deinit(gpa); |
| 1130 | self.got_index_free_list.deinit(gpa); |
| 1131 | self.syms_index_free_list.deinit(gpa); |
| 1132 | self.file_segments.deinit(gpa); |
| 1133 | self.path_arena.deinit(); |
| 1134 | for (self.atoms.items) |a| { |
| 1135 | if (a.code.getOwnedCode()) |c| { |
| 1136 | gpa.free(c); |
| 1137 | } |
| 1138 | } |
| 1139 | self.atoms.deinit(gpa); |
| 1140 | |
| 1141 | { |
| 1142 | var it = self.navs.iterator(); |
| 1143 | while (it.next()) |entry| { |
| 1144 | entry.value_ptr.exports.deinit(gpa); |
| 1145 | } |
| 1146 | self.navs.deinit(gpa); |
| 1147 | } |
| 1148 | } |
| 1149 | |
| 1150 | pub fn open( |
| 1151 | arena: Allocator, |
| 1152 | comp: *Compilation, |
| 1153 | emit: Path, |
| 1154 | options: link.File.OpenOptions, |
| 1155 | ) !*Plan9 { |
| 1156 | const target = &comp.root_mod.resolved_target.result; |
| 1157 | const use_lld = build_options.have_llvm and comp.config.use_lld; |
| 1158 | const use_llvm = comp.config.use_llvm; |
| 1159 | |
| 1160 | assert(!use_llvm); // Caught by Compilation.Config.resolve. |
| 1161 | assert(!use_lld); // Caught by Compilation.Config.resolve. |
| 1162 | assert(target.ofmt == .plan9); |
| 1163 | |
| 1164 | const self = try createEmpty(arena, comp, emit, options); |
| 1165 | errdefer self.base.destroy(); |
| 1166 | |
| 1167 | const file = try emit.root_dir.handle.createFile(emit.sub_path, .{ |
| 1168 | .read = true, |
| 1169 | .mode = link.File.determineMode(comp.config.output_mode, comp.config.link_mode), |
| 1170 | }); |
| 1171 | errdefer file.close(); |
| 1172 | self.base.file = file; |
| 1173 | |
| 1174 | self.bases = defaultBaseAddrs(target.cpu.arch); |
| 1175 | |
| 1176 | const gpa = comp.gpa; |
| 1177 | |
| 1178 | try self.syms.appendSlice(gpa, &.{ |
| 1179 | // we include the global offset table to make it easier for debugging |
| 1180 | .{ |
| 1181 | .value = self.getAddr(0, .d), // the global offset table starts at 0 |
| 1182 | .type = .d, |
| 1183 | .name = "__GOT", |
| 1184 | }, |
| 1185 | }); |
| 1186 | |
| 1187 | return self; |
| 1188 | } |
| 1189 | |
| 1190 | pub fn writeSym(self: *Plan9, w: anytype, sym: aout.Sym) !void { |
| 1191 | // log.debug("write sym{{name: {s}, value: {x}}}", .{ sym.name, sym.value }); |
| 1192 | if (sym.type == .bad) return; // we don't want to write free'd symbols |
| 1193 | if (!self.sixtyfour_bit) { |
| 1194 | try w.writeInt(u32, @as(u32, @intCast(sym.value)), .big); |
| 1195 | } else { |
| 1196 | try w.writeInt(u64, sym.value, .big); |
| 1197 | } |
| 1198 | try w.writeByte(@intFromEnum(sym.type)); |
| 1199 | try w.writeAll(sym.name); |
| 1200 | try w.writeByte(0); |
| 1201 | } |
| 1202 | |
| 1203 | pub fn writeSyms(self: *Plan9, buf: *std.ArrayList(u8)) !void { |
| 1204 | const zcu = self.base.comp.zcu.?; |
| 1205 | const ip = &zcu.intern_pool; |
| 1206 | const writer = buf.writer(); |
| 1207 | // write __GOT |
| 1208 | try self.writeSym(writer, self.syms.items[0]); |
| 1209 | // write the f symbols |
| 1210 | { |
| 1211 | var it = self.file_segments.iterator(); |
| 1212 | while (it.next()) |entry| { |
| 1213 | try self.writeSym(writer, .{ |
| 1214 | .type = .f, |
| 1215 | .value = entry.value_ptr.*, |
| 1216 | .name = entry.key_ptr.*, |
| 1217 | }); |
| 1218 | } |
| 1219 | } |
| 1220 | |
| 1221 | // write the data symbols |
| 1222 | { |
| 1223 | var it = self.data_nav_table.iterator(); |
| 1224 | while (it.next()) |entry| { |
| 1225 | const nav_index = entry.key_ptr.*; |
| 1226 | const nav_metadata = self.navs.get(nav_index).?; |
| 1227 | const atom = self.getAtom(nav_metadata.index); |
| 1228 | const sym = self.syms.items[atom.sym_index.?]; |
| 1229 | try self.writeSym(writer, sym); |
| 1230 | if (self.nav_exports.get(nav_index)) |export_indices| { |
| 1231 | for (export_indices) |export_idx| { |
| 1232 | const exp = export_idx.ptr(zcu); |
| 1233 | if (nav_metadata.getExport(self, exp.opts.name.toSlice(ip))) |exp_i| { |
| 1234 | try self.writeSym(writer, self.syms.items[exp_i]); |
| 1235 | } |
| 1236 | } |
| 1237 | } |
| 1238 | } |
| 1239 | } |
| 1240 | // the data lazy symbols |
| 1241 | { |
| 1242 | var it = self.lazy_syms.iterator(); |
| 1243 | while (it.next()) |kv| { |
| 1244 | const meta = kv.value_ptr; |
| 1245 | const data_atom = if (meta.rodata_state != .unused) self.getAtomPtr(meta.rodata_atom) else continue; |
| 1246 | const sym = self.syms.items[data_atom.sym_index.?]; |
| 1247 | try self.writeSym(writer, sym); |
| 1248 | } |
| 1249 | } |
| 1250 | // text symbols are the hardest: |
| 1251 | // the file of a text symbol is the .z symbol before it |
| 1252 | // so we have to write everything in the right order |
| 1253 | { |
| 1254 | var it_file = self.fn_nav_table.iterator(); |
| 1255 | while (it_file.next()) |fentry| { |
| 1256 | var symidx_and_submap = fentry.value_ptr; |
| 1257 | // write the z symbols |
| 1258 | try self.writeSym(writer, self.syms.items[symidx_and_submap.sym_index - 1]); |
| 1259 | try self.writeSym(writer, self.syms.items[symidx_and_submap.sym_index]); |
| 1260 | |
| 1261 | // write all the decls come from the file of the z symbol |
| 1262 | var submap_it = symidx_and_submap.functions.iterator(); |
| 1263 | while (submap_it.next()) |entry| { |
| 1264 | const nav_index = entry.key_ptr.*; |
| 1265 | const nav_metadata = self.navs.get(nav_index).?; |
| 1266 | const atom = self.getAtom(nav_metadata.index); |
| 1267 | const sym = self.syms.items[atom.sym_index.?]; |
| 1268 | try self.writeSym(writer, sym); |
| 1269 | if (self.nav_exports.get(nav_index)) |export_indices| { |
| 1270 | for (export_indices) |export_idx| { |
| 1271 | const exp = export_idx.ptr(zcu); |
| 1272 | if (nav_metadata.getExport(self, exp.opts.name.toSlice(ip))) |exp_i| { |
| 1273 | const s = self.syms.items[exp_i]; |
| 1274 | if (mem.eql(u8, s.name, "_start")) |
| 1275 | self.entry_val = s.value; |
| 1276 | try self.writeSym(writer, s); |
| 1277 | } |
| 1278 | } |
| 1279 | } |
| 1280 | } |
| 1281 | } |
| 1282 | // the text lazy symbols |
| 1283 | { |
| 1284 | var it = self.lazy_syms.iterator(); |
| 1285 | while (it.next()) |kv| { |
| 1286 | const meta = kv.value_ptr; |
| 1287 | const text_atom = if (meta.text_state != .unused) self.getAtomPtr(meta.text_atom) else continue; |
| 1288 | const sym = self.syms.items[text_atom.sym_index.?]; |
| 1289 | try self.writeSym(writer, sym); |
| 1290 | } |
| 1291 | } |
| 1292 | } |
| 1293 | // special symbols |
| 1294 | for (self.etext_edata_end_atom_indices) |idx| { |
| 1295 | if (idx) |atom_idx| { |
| 1296 | const atom = self.getAtom(atom_idx); |
| 1297 | const sym = self.syms.items[atom.sym_index.?]; |
| 1298 | try self.writeSym(writer, sym); |
| 1299 | } |
| 1300 | } |
| 1301 | } |
| 1302 | |
| 1303 | pub fn updateLineNumber(self: *Plan9, pt: Zcu.PerThread, ti_id: InternPool.TrackedInst.Index) !void { |
| 1304 | _ = self; |
| 1305 | _ = pt; |
| 1306 | _ = ti_id; |
| 1307 | } |
| 1308 | |
| 1309 | pub fn getNavVAddr( |
| 1310 | self: *Plan9, |
| 1311 | pt: Zcu.PerThread, |
| 1312 | nav_index: InternPool.Nav.Index, |
| 1313 | reloc_info: link.File.RelocInfo, |
| 1314 | ) !u64 { |
| 1315 | const ip = &pt.zcu.intern_pool; |
| 1316 | const nav = ip.getNav(nav_index); |
| 1317 | log.debug("getDeclVAddr for {f}", .{nav.name.fmt(ip)}); |
| 1318 | if (nav.getExtern(ip) != null) { |
| 1319 | if (nav.name.eqlSlice("etext", ip)) { |
| 1320 | try self.addReloc(reloc_info.parent.atom_index, .{ |
| 1321 | .target = undefined, |
| 1322 | .offset = reloc_info.offset, |
| 1323 | .addend = reloc_info.addend, |
| 1324 | .type = .special_etext, |
| 1325 | }); |
| 1326 | } else if (nav.name.eqlSlice("edata", ip)) { |
| 1327 | try self.addReloc(reloc_info.parent.atom_index, .{ |
| 1328 | .target = undefined, |
| 1329 | .offset = reloc_info.offset, |
| 1330 | .addend = reloc_info.addend, |
| 1331 | .type = .special_edata, |
| 1332 | }); |
| 1333 | } else if (nav.name.eqlSlice("end", ip)) { |
| 1334 | try self.addReloc(reloc_info.parent.atom_index, .{ |
| 1335 | .target = undefined, |
| 1336 | .offset = reloc_info.offset, |
| 1337 | .addend = reloc_info.addend, |
| 1338 | .type = .special_end, |
| 1339 | }); |
| 1340 | } |
| 1341 | // TODO handle other extern variables and functions |
| 1342 | return undefined; |
| 1343 | } |
| 1344 | // otherwise, we just add a relocation |
| 1345 | const atom_index = try self.seeNav(pt, nav_index); |
| 1346 | // the parent_atom_index in this case is just the decl_index of the parent |
| 1347 | try self.addReloc(reloc_info.parent.atom_index, .{ |
| 1348 | .target = atom_index, |
| 1349 | .offset = reloc_info.offset, |
| 1350 | .addend = reloc_info.addend, |
| 1351 | }); |
| 1352 | return undefined; |
| 1353 | } |
| 1354 | |
| 1355 | pub fn lowerUav( |
| 1356 | self: *Plan9, |
| 1357 | pt: Zcu.PerThread, |
| 1358 | uav: InternPool.Index, |
| 1359 | explicit_alignment: InternPool.Alignment, |
| 1360 | src_loc: Zcu.LazySrcLoc, |
| 1361 | ) !codegen.SymbolResult { |
| 1362 | _ = explicit_alignment; |
| 1363 | // example: |
| 1364 | // const ty = mod.intern_pool.typeOf(decl_val).toType(); |
| 1365 | // const val = decl_val.toValue(); |
| 1366 | // The symbol name can be something like `__anon_{d}` with `@intFromEnum(decl_val)`. |
| 1367 | // It doesn't have an owner decl because it's just an unnamed constant that might |
| 1368 | // be used by more than one function, however, its address is being used so we need |
| 1369 | // to put it in some location. |
| 1370 | // ... |
| 1371 | const gpa = self.base.comp.gpa; |
| 1372 | const gop = try self.uavs.getOrPut(gpa, uav); |
| 1373 | if (gop.found_existing) return .{ .sym_index = gop.value_ptr.* }; |
| 1374 | const val = Value.fromInterned(uav); |
| 1375 | const name = try std.fmt.allocPrint(gpa, "__anon_{d}", .{@intFromEnum(uav)}); |
| 1376 | |
| 1377 | const index = try self.createAtom(); |
| 1378 | const got_index = self.allocateGotIndex(); |
| 1379 | gop.value_ptr.* = index; |
| 1380 | // we need to free name latex |
| 1381 | var code_buffer: std.ArrayListUnmanaged(u8) = .empty; |
| 1382 | defer code_buffer.deinit(gpa); |
| 1383 | try codegen.generateSymbol(&self.base, pt, src_loc, val, &code_buffer, .{ .atom_index = index }); |
| 1384 | const atom_ptr = self.getAtomPtr(index); |
| 1385 | atom_ptr.* = .{ |
| 1386 | .type = .d, |
| 1387 | .offset = undefined, |
| 1388 | .sym_index = null, |
| 1389 | .got_index = got_index, |
| 1390 | .code = Atom.CodePtr.fromSlice(try code_buffer.toOwnedSlice(gpa)), |
| 1391 | }; |
| 1392 | _ = try atom_ptr.getOrCreateSymbolTableEntry(self); |
| 1393 | self.syms.items[atom_ptr.sym_index.?] = .{ |
| 1394 | .type = .d, |
| 1395 | .value = undefined, |
| 1396 | .name = name, |
| 1397 | }; |
| 1398 | return .{ .sym_index = index }; |
| 1399 | } |
| 1400 | |
| 1401 | pub fn getUavVAddr(self: *Plan9, uav: InternPool.Index, reloc_info: link.File.RelocInfo) !u64 { |
| 1402 | const atom_index = self.uavs.get(uav).?; |
| 1403 | try self.addReloc(reloc_info.parent.atom_index, .{ |
| 1404 | .target = atom_index, |
| 1405 | .offset = reloc_info.offset, |
| 1406 | .addend = reloc_info.addend, |
| 1407 | }); |
| 1408 | return undefined; |
| 1409 | } |
| 1410 | |
| 1411 | pub fn addReloc(self: *Plan9, parent_index: Atom.Index, reloc: Reloc) !void { |
| 1412 | const gpa = self.base.comp.gpa; |
| 1413 | const gop = try self.relocs.getOrPut(gpa, parent_index); |
| 1414 | if (!gop.found_existing) { |
| 1415 | gop.value_ptr.* = .{}; |
| 1416 | } |
| 1417 | try gop.value_ptr.append(gpa, reloc); |
| 1418 | } |
| 1419 | |
| 1420 | pub fn getAtom(self: *const Plan9, index: Atom.Index) Atom { |
| 1421 | return self.atoms.items[index]; |
| 1422 | } |
| 1423 | |
| 1424 | fn getAtomPtr(self: *Plan9, index: Atom.Index) *Atom { |
| 1425 | return &self.atoms.items[index]; |
| 1426 | } |