| ... | ... | @@ -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 | | } |