| 1 | //! This file contains the functionality for emitting x86_64 MIR as machine code |
| 2 | |
| 3 | lower: Lower, |
| 4 | bin_file: *link.File, |
| 5 | pt: Zcu.PerThread, |
| 6 | pic: bool, |
| 7 | atom_id: link.File.AtomId, |
| 8 | debug_output: link.File.DebugInfoOutput, |
| 9 | w: *std.Io.Writer, |
| 10 | |
| 11 | prev_di_loc: Loc, |
| 12 | /// Relative to the beginning of `code`. |
| 13 | prev_di_pc: usize, |
| 14 | |
| 15 | code_offset_mapping: std.ArrayList(u32), |
| 16 | relocs: std.ArrayList(Reloc), |
| 17 | table_relocs: std.ArrayList(TableReloc), |
| 18 | |
| 19 | pub const Error = Lower.Error || codegen.Error || std.Io.Writer.Error || error{ |
| 20 | EmitFail, |
| 21 | } || std.posix.MMapError || std.posix.MRemapError || link.File.UpdateDebugInfoError; |
| 22 | |
| 23 | pub fn emitMir(emit: *Emit) Error!void { |
| 24 | const comp = emit.bin_file.comp; |
| 25 | const gpa = comp.gpa; |
| 26 | try emit.code_offset_mapping.resize(gpa, emit.lower.mir.instructions.len); |
| 27 | emit.relocs.clearRetainingCapacity(); |
| 28 | emit.table_relocs.clearRetainingCapacity(); |
| 29 | var local_index: usize = 0; |
| 30 | for (0..emit.lower.mir.instructions.len) |mir_i| { |
| 31 | const mir_index: Mir.Inst.Index = @intCast(mir_i); |
| 32 | emit.code_offset_mapping.items[mir_index] = @intCast(emit.w.end); |
| 33 | const lowered = try emit.lower.lowerMir(mir_index); |
| 34 | var lowered_relocs = lowered.relocs; |
| 35 | lowered_inst: for (lowered.insts, 0..) |lowered_inst, lowered_index| { |
| 36 | if (lowered_inst.prefix == .directive) { |
| 37 | const start_offset: u32 = @intCast(emit.w.end); |
| 38 | switch (emit.debug_output) { |
| 39 | inline .dwarf, .dwarf2, .eh_frame => |dwarf| switch (lowered_inst.encoding.mnemonic) { |
| 40 | .@".cfi_def_cfa" => try dwarf.genDebugFrame(start_offset, .{ .def_cfa = .{ |
| 41 | .reg = lowered_inst.ops[0].reg.dwarfNum(), |
| 42 | .off = lowered_inst.ops[1].imm.signed, |
| 43 | } }), |
| 44 | .@".cfi_def_cfa_register" => try dwarf.genDebugFrame(start_offset, .{ |
| 45 | .def_cfa_register = lowered_inst.ops[0].reg.dwarfNum(), |
| 46 | }), |
| 47 | .@".cfi_def_cfa_offset" => try dwarf.genDebugFrame(start_offset, .{ |
| 48 | .def_cfa_offset = lowered_inst.ops[0].imm.signed, |
| 49 | }), |
| 50 | .@".cfi_adjust_cfa_offset" => try dwarf.genDebugFrame(start_offset, .{ |
| 51 | .adjust_cfa_offset = lowered_inst.ops[0].imm.signed, |
| 52 | }), |
| 53 | .@".cfi_offset" => try dwarf.genDebugFrame(start_offset, .{ .offset = .{ |
| 54 | .reg = lowered_inst.ops[0].reg.dwarfNum(), |
| 55 | .off = lowered_inst.ops[1].imm.signed, |
| 56 | } }), |
| 57 | .@".cfi_val_offset" => try dwarf.genDebugFrame(start_offset, .{ .val_offset = .{ |
| 58 | .reg = lowered_inst.ops[0].reg.dwarfNum(), |
| 59 | .off = lowered_inst.ops[1].imm.signed, |
| 60 | } }), |
| 61 | .@".cfi_rel_offset" => try dwarf.genDebugFrame(start_offset, .{ .rel_offset = .{ |
| 62 | .reg = lowered_inst.ops[0].reg.dwarfNum(), |
| 63 | .off = lowered_inst.ops[1].imm.signed, |
| 64 | } }), |
| 65 | .@".cfi_register" => try dwarf.genDebugFrame(start_offset, .{ .register = .{ |
| 66 | lowered_inst.ops[0].reg.dwarfNum(), |
| 67 | lowered_inst.ops[1].reg.dwarfNum(), |
| 68 | } }), |
| 69 | .@".cfi_restore" => try dwarf.genDebugFrame(start_offset, .{ |
| 70 | .restore = lowered_inst.ops[0].reg.dwarfNum(), |
| 71 | }), |
| 72 | .@".cfi_undefined" => try dwarf.genDebugFrame(start_offset, .{ |
| 73 | .undefined = lowered_inst.ops[0].reg.dwarfNum(), |
| 74 | }), |
| 75 | .@".cfi_same_value" => try dwarf.genDebugFrame(start_offset, .{ |
| 76 | .same_value = lowered_inst.ops[0].reg.dwarfNum(), |
| 77 | }), |
| 78 | .@".cfi_remember_state" => try dwarf.genDebugFrame(start_offset, .remember_state), |
| 79 | .@".cfi_restore_state" => try dwarf.genDebugFrame(start_offset, .restore_state), |
| 80 | .@".cfi_escape" => try dwarf.genDebugFrame(start_offset, .{ |
| 81 | .escape = lowered_inst.ops[0].bytes, |
| 82 | }), |
| 83 | else => unreachable, |
| 84 | }, |
| 85 | .none => {}, |
| 86 | } |
| 87 | continue; |
| 88 | } |
| 89 | var reloc_info_buf: [2]RelocInfo = undefined; |
| 90 | var reloc_info_index: usize = 0; |
| 91 | const ip = &emit.pt.zcu.intern_pool; |
| 92 | while (lowered_relocs.len > 0 and |
| 93 | lowered_relocs[0].lowered_inst_index == lowered_index) : ({ |
| 94 | lowered_relocs = lowered_relocs[1..]; |
| 95 | reloc_info_index += 1; |
| 96 | }) reloc_info_buf[reloc_info_index] = .{ |
| 97 | .op_index = lowered_relocs[0].op_index, |
| 98 | .off = lowered_relocs[0].off, |
| 99 | .target = target: switch (lowered_relocs[0].target) { |
| 100 | .inst => |inst| .{ .inst = inst }, |
| 101 | .table => .table, |
| 102 | .nav => |nav| { |
| 103 | const symbol_id = try codegen.genNavRef( |
| 104 | emit.bin_file, |
| 105 | emit.pt, |
| 106 | nav, |
| 107 | ); |
| 108 | const target_symbol: RelocInfo.Target.Symbol = if (ip.getNav(nav).getExtern(ip)) |@"extern"| .{ |
| 109 | .symbol = symbol_id, |
| 110 | .is_extern = switch (@"extern".visibility) { |
| 111 | .default => true, |
| 112 | .hidden, .protected => false, |
| 113 | }, |
| 114 | .is_dll_import = @"extern".is_dll_import, |
| 115 | .force_pcrel_direct = switch (@"extern".relocation) { |
| 116 | .any => false, |
| 117 | .pcrel => true, |
| 118 | }, |
| 119 | } else .{ .symbol = symbol_id, .is_extern = false }; |
| 120 | if (ip.getNav(nav).resolved.?.@"threadlocal" and comp.config.any_non_single_threaded) { |
| 121 | break :target .{ .tlv = target_symbol }; |
| 122 | } else { |
| 123 | break :target .{ .symbol = target_symbol }; |
| 124 | } |
| 125 | }, |
| 126 | .uav => |uav| .{ .symbol = .{ |
| 127 | .symbol = try emit.bin_file.lowerUav( |
| 128 | emit.pt, |
| 129 | uav.val, |
| 130 | Type.fromInterned(uav.orig_ty).ptrAlignment(emit.pt.zcu), |
| 131 | ), |
| 132 | .is_extern = false, |
| 133 | } }, |
| 134 | .lazy_sym => |lazy_sym| .{ .symbol = .{ |
| 135 | .symbol = if (emit.bin_file.cast(.elf)) |elf_file| |
| 136 | @fromBackingInt(@intCast( |
| 137 | elf_file.zigObjectPtr().?.getOrCreateMetadataForLazySymbol(elf_file, emit.pt, lazy_sym) catch |err| |
| 138 | return emit.fail("{s} creating lazy symbol", .{@errorName(err)}), |
| 139 | )) |
| 140 | else if (emit.bin_file.cast(.elf2)) |elf| |
| 141 | try elf.lazySymbol(lazy_sym) |
| 142 | else if (emit.bin_file.cast(.macho)) |macho_file| |
| 143 | @fromBackingInt(@intCast(macho_file.getZigObject().?.getOrCreateMetadataForLazySymbol(macho_file, emit.pt, lazy_sym) catch |err| |
| 144 | return emit.fail("{s} creating lazy symbol", .{@errorName(err)}))) |
| 145 | else if (emit.bin_file.cast(.coff2)) |coff| |
| 146 | @fromBackingInt(@intCast(@backingInt(try coff.lazySymbol(lazy_sym)))) |
| 147 | else |
| 148 | return emit.fail("lazy symbols unimplemented for {s}", .{@tagName(emit.bin_file.tag)}), |
| 149 | .is_extern = false, |
| 150 | } }, |
| 151 | .extern_func => |extern_func| .{ .symbol = .{ |
| 152 | .symbol = if (emit.bin_file.cast(.elf)) |elf_file| |
| 153 | @fromBackingInt(@intCast(try elf_file.getGlobalSymbol(extern_func.toSlice(&emit.lower.mir).?, null))) |
| 154 | else if (emit.bin_file.cast(.elf2)) |elf| try elf.externSymbol(.{ |
| 155 | .name = extern_func.toSlice(&emit.lower.mir).?, |
| 156 | .lib_name = null, |
| 157 | .type = .FUNC, |
| 158 | }) else if (emit.bin_file.cast(.macho)) |macho_file| |
| 159 | @fromBackingInt(@intCast(try macho_file.getGlobalSymbol(extern_func.toSlice(&emit.lower.mir).?, null))) |
| 160 | else if (emit.bin_file.cast(.coff2)) |coff| @fromBackingInt(@intCast(@backingInt(try coff.globalSymbol(.{ |
| 161 | .name = extern_func.toSlice(&emit.lower.mir).?, |
| 162 | })))) else return emit.fail("external symbol unimplemented for {s}", .{@tagName(emit.bin_file.tag)}), |
| 163 | .is_extern = true, |
| 164 | } }, |
| 165 | }, |
| 166 | }; |
| 167 | const reloc_info = reloc_info_buf[0..reloc_info_index]; |
| 168 | for (reloc_info) |*reloc| switch (reloc.target) { |
| 169 | .inst, .table => {}, |
| 170 | .symbol => |target| { |
| 171 | switch (lowered_inst.encoding.mnemonic) { |
| 172 | .call => { |
| 173 | reloc.target = .{ .branch = target }; |
| 174 | if (target.is_dll_import and emit.bin_file.cast(.coff2) != null) { |
| 175 | try emit.encodeInst(try .new(.none, .call, &.{ |
| 176 | .{ .mem = .initRip(.ptr, 0) }, |
| 177 | }, emit.lower.target), reloc_info); |
| 178 | } else { |
| 179 | try emit.encodeInst(lowered_inst, reloc_info); |
| 180 | } |
| 181 | continue :lowered_inst; |
| 182 | }, |
| 183 | else => {}, |
| 184 | } |
| 185 | if (emit.bin_file.cast(.elf) != null or emit.bin_file.cast(.elf2) != null) { |
| 186 | if (!emit.pic) switch (lowered_inst.encoding.mnemonic) { |
| 187 | .lea => try emit.encodeInst(try .new(.none, .mov, &.{ |
| 188 | lowered_inst.ops[0], |
| 189 | .{ .imm = .s(0) }, |
| 190 | }, emit.lower.target), reloc_info), |
| 191 | .mov => try emit.encodeInst(try .new(.none, .mov, &.{ |
| 192 | lowered_inst.ops[0], |
| 193 | .{ .mem = .initSib(lowered_inst.ops[reloc.op_index].mem.sib.ptr_size, .{}) }, |
| 194 | }, emit.lower.target), reloc_info), |
| 195 | else => unreachable, |
| 196 | } else if (target.is_extern) switch (lowered_inst.encoding.mnemonic) { |
| 197 | .lea => try emit.encodeInst(try .new(.none, .mov, &.{ |
| 198 | lowered_inst.ops[0], |
| 199 | .{ .mem = .initRip(.ptr, 0) }, |
| 200 | }, emit.lower.target), reloc_info), |
| 201 | .mov => { |
| 202 | try emit.encodeInst(try .new(.none, .mov, &.{ |
| 203 | lowered_inst.ops[0], |
| 204 | .{ .mem = .initRip(.ptr, 0) }, |
| 205 | }, emit.lower.target), reloc_info); |
| 206 | try emit.encodeInst(try .new(.none, .mov, &.{ |
| 207 | lowered_inst.ops[0], |
| 208 | .{ .mem = .initSib(lowered_inst.ops[reloc.op_index].mem.sib.ptr_size, .{ .base = .{ |
| 209 | .reg = lowered_inst.ops[0].reg.to64(), |
| 210 | } }) }, |
| 211 | }, emit.lower.target), &.{}); |
| 212 | }, |
| 213 | else => unreachable, |
| 214 | } else switch (lowered_inst.encoding.mnemonic) { |
| 215 | .lea => try emit.encodeInst(try .new(.none, .lea, &.{ |
| 216 | lowered_inst.ops[0], |
| 217 | .{ .mem = .initRip(.none, 0) }, |
| 218 | }, emit.lower.target), reloc_info), |
| 219 | .mov => try emit.encodeInst(try .new(.none, .mov, &.{ |
| 220 | lowered_inst.ops[0], |
| 221 | .{ .mem = .initRip(lowered_inst.ops[reloc.op_index].mem.sib.ptr_size, 0) }, |
| 222 | }, emit.lower.target), reloc_info), |
| 223 | else => unreachable, |
| 224 | } |
| 225 | } else if (emit.bin_file.cast(.macho)) |_| { |
| 226 | if (target.is_extern) switch (lowered_inst.encoding.mnemonic) { |
| 227 | .lea => try emit.encodeInst(try .new(.none, .mov, &.{ |
| 228 | lowered_inst.ops[0], |
| 229 | .{ .mem = .initRip(.ptr, 0) }, |
| 230 | }, emit.lower.target), reloc_info), |
| 231 | .mov => { |
| 232 | try emit.encodeInst(try .new(.none, .mov, &.{ |
| 233 | lowered_inst.ops[0], |
| 234 | .{ .mem = .initRip(.ptr, 0) }, |
| 235 | }, emit.lower.target), reloc_info); |
| 236 | try emit.encodeInst(try .new(.none, .mov, &.{ |
| 237 | lowered_inst.ops[0], |
| 238 | .{ .mem = .initSib(lowered_inst.ops[reloc.op_index].mem.sib.ptr_size, .{ .base = .{ |
| 239 | .reg = lowered_inst.ops[0].reg.to64(), |
| 240 | } }) }, |
| 241 | }, emit.lower.target), &.{}); |
| 242 | }, |
| 243 | else => unreachable, |
| 244 | } else switch (lowered_inst.encoding.mnemonic) { |
| 245 | .lea => try emit.encodeInst(try .new(.none, .lea, &.{ |
| 246 | lowered_inst.ops[0], |
| 247 | .{ .mem = .initRip(.none, 0) }, |
| 248 | }, emit.lower.target), reloc_info), |
| 249 | .mov => try emit.encodeInst(try .new(.none, .mov, &.{ |
| 250 | lowered_inst.ops[0], |
| 251 | .{ .mem = .initRip(lowered_inst.ops[reloc.op_index].mem.sib.ptr_size, 0) }, |
| 252 | }, emit.lower.target), reloc_info), |
| 253 | else => unreachable, |
| 254 | } |
| 255 | } else if (emit.bin_file.cast(.coff2)) |_| { |
| 256 | if (target.is_dll_import) switch (lowered_inst.encoding.mnemonic) { |
| 257 | .lea => try emit.encodeInst(try .new(.none, .mov, &.{ |
| 258 | lowered_inst.ops[0], |
| 259 | .{ .mem = .initRip(.ptr, 0) }, |
| 260 | }, emit.lower.target), reloc_info), |
| 261 | .mov => { |
| 262 | try emit.encodeInst(try .new(.none, .mov, &.{ |
| 263 | lowered_inst.ops[0], |
| 264 | .{ .mem = .initRip(.ptr, 0) }, |
| 265 | }, emit.lower.target), reloc_info); |
| 266 | try emit.encodeInst(try .new(.none, .mov, &.{ |
| 267 | lowered_inst.ops[0], |
| 268 | .{ .mem = .initSib(lowered_inst.ops[reloc.op_index].mem.sib.ptr_size, .{ .base = .{ |
| 269 | .reg = lowered_inst.ops[0].reg.to64(), |
| 270 | } }) }, |
| 271 | }, emit.lower.target), &.{}); |
| 272 | }, |
| 273 | else => unreachable, |
| 274 | } else switch (lowered_inst.encoding.mnemonic) { |
| 275 | .lea => try emit.encodeInst(try .new(.none, .lea, &.{ |
| 276 | lowered_inst.ops[0], |
| 277 | .{ .mem = .initRip(.none, 0) }, |
| 278 | }, emit.lower.target), reloc_info), |
| 279 | .mov => try emit.encodeInst(try .new(.none, .mov, &.{ |
| 280 | lowered_inst.ops[0], |
| 281 | .{ .mem = .initRip(lowered_inst.ops[reloc.op_index].mem.sib.ptr_size, 0) }, |
| 282 | }, emit.lower.target), reloc_info), |
| 283 | else => unreachable, |
| 284 | } |
| 285 | } else return emit.fail("TODO implement relocs for {s}", .{ |
| 286 | @tagName(emit.bin_file.tag), |
| 287 | }); |
| 288 | continue :lowered_inst; |
| 289 | }, |
| 290 | .branch, .tls => unreachable, |
| 291 | .tlv => |target| { |
| 292 | if (emit.bin_file.cast(.elf) != null or emit.bin_file.cast(.elf2) != null) { |
| 293 | // TODO handle extern TLS vars, i.e., emit GD model |
| 294 | if (emit.pic) switch (lowered_inst.encoding.mnemonic) { |
| 295 | .lea, .mov => { |
| 296 | // Here, we currently assume local dynamic TLS vars, and so |
| 297 | // we emit LD model. |
| 298 | try emit.encodeInst(try .new(.none, .lea, &.{ |
| 299 | .{ .reg = .rdi }, |
| 300 | .{ .mem = .initRip(.none, 0) }, |
| 301 | }, emit.lower.target), &.{.{ |
| 302 | .op_index = 1, |
| 303 | .target = .{ .tls = target.symbol }, |
| 304 | }}); |
| 305 | try emit.encodeInst(try .new(.none, .call, &.{ |
| 306 | .{ .imm = .s(0) }, |
| 307 | }, emit.lower.target), &.{.{ |
| 308 | .op_index = 0, |
| 309 | .target = .{ .branch = .{ |
| 310 | .symbol = if (emit.bin_file.cast(.elf)) |elf_file| @fromBackingInt(@intCast(try elf_file.getGlobalSymbol( |
| 311 | "__tls_get_addr", |
| 312 | if (comp.config.link_libc) "c" else null, |
| 313 | ))) else if (emit.bin_file.cast(.elf2)) |elf| try elf.externSymbol(.{ |
| 314 | .name = "__tls_get_addr", |
| 315 | .lib_name = if (comp.config.link_libc) "c" else null, |
| 316 | .type = .FUNC, |
| 317 | }) else unreachable, |
| 318 | .is_extern = true, |
| 319 | } }, |
| 320 | }}); |
| 321 | try emit.encodeInst(try .new(.none, lowered_inst.encoding.mnemonic, &.{ |
| 322 | lowered_inst.ops[0], |
| 323 | .{ .mem = .initSib(.none, .{ |
| 324 | .base = .{ .reg = .rax }, |
| 325 | .disp = std.math.minInt(i32), |
| 326 | }) }, |
| 327 | }, emit.lower.target), reloc_info); |
| 328 | }, |
| 329 | else => unreachable, |
| 330 | } else switch (lowered_inst.encoding.mnemonic) { |
| 331 | .lea, .mov => { |
| 332 | // Since we are linking statically, we emit LE model directly. |
| 333 | try emit.encodeInst(try .new(.none, .mov, &.{ |
| 334 | .{ .reg = .rax }, |
| 335 | .{ .mem = .initSib(.qword, .{ .base = .{ .reg = .fs } }) }, |
| 336 | }, emit.lower.target), &.{}); |
| 337 | try emit.encodeInst(try .new(.none, lowered_inst.encoding.mnemonic, &.{ |
| 338 | lowered_inst.ops[0], |
| 339 | .{ .mem = .initSib(.none, .{ |
| 340 | .base = .{ .reg = .rax }, |
| 341 | .disp = std.math.minInt(i32), |
| 342 | }) }, |
| 343 | }, emit.lower.target), reloc_info); |
| 344 | }, |
| 345 | else => unreachable, |
| 346 | } |
| 347 | } else if (emit.bin_file.cast(.macho)) |_| switch (lowered_inst.encoding.mnemonic) { |
| 348 | .lea => { |
| 349 | try emit.encodeInst(try .new(.none, .mov, &.{ |
| 350 | .{ .reg = .rdi }, |
| 351 | .{ .mem = .initRip(.ptr, 0) }, |
| 352 | }, emit.lower.target), reloc_info); |
| 353 | try emit.encodeInst(try .new(.none, .call, &.{ |
| 354 | .{ .mem = .initSib(.qword, .{ .base = .{ .reg = .rdi } }) }, |
| 355 | }, emit.lower.target), &.{}); |
| 356 | try emit.encodeInst(try .new(.none, .mov, &.{ |
| 357 | lowered_inst.ops[0], |
| 358 | .{ .reg = .rax }, |
| 359 | }, emit.lower.target), &.{}); |
| 360 | }, |
| 361 | .mov => { |
| 362 | try emit.encodeInst(try .new(.none, .mov, &.{ |
| 363 | .{ .reg = .rdi }, |
| 364 | .{ .mem = .initRip(.ptr, 0) }, |
| 365 | }, emit.lower.target), reloc_info); |
| 366 | try emit.encodeInst(try .new(.none, .call, &.{ |
| 367 | .{ .mem = .initSib(.qword, .{ .base = .{ .reg = .rdi } }) }, |
| 368 | }, emit.lower.target), &.{}); |
| 369 | try emit.encodeInst(try .new(.none, .mov, &.{ |
| 370 | lowered_inst.ops[0], |
| 371 | .{ .mem = .initSib(.qword, .{ .base = .{ .reg = .rax } }) }, |
| 372 | }, emit.lower.target), &.{}); |
| 373 | }, |
| 374 | else => unreachable, |
| 375 | } else if (emit.bin_file.cast(.coff2)) |coff| { |
| 376 | switch (emit.lower.target.cpu.arch) { |
| 377 | else => unreachable, |
| 378 | .x86 => { |
| 379 | try emit.encodeInst(try .new(.none, .mov, &.{ |
| 380 | .{ .reg = .eax }, |
| 381 | .{ .mem = .initSib(.qword, .{ |
| 382 | .base = .{ .reg = .fs }, |
| 383 | .disp = 4 * 11, |
| 384 | }) }, |
| 385 | }, emit.lower.target), &.{}); |
| 386 | try emit.encodeInst(try .new(.none, .mov, &.{ |
| 387 | .{ .reg = .edi }, |
| 388 | .{ .mem = .initSib(.dword, .{}) }, |
| 389 | }, emit.lower.target), &.{.{ |
| 390 | .op_index = 1, |
| 391 | .target = .{ .symbol = .{ |
| 392 | .symbol = @fromBackingInt(@intCast(@backingInt( |
| 393 | try coff.globalSymbol(.{ .name = "__tls_index" }), |
| 394 | ))), |
| 395 | .is_extern = false, |
| 396 | } }, |
| 397 | }}); |
| 398 | try emit.encodeInst(try .new(.none, .mov, &.{ |
| 399 | .{ .reg = .eax }, |
| 400 | .{ .mem = .initSib(.dword, .{ |
| 401 | .base = .{ .reg = .eax }, |
| 402 | .scale_index = .{ .index = .edi, .scale = 4 }, |
| 403 | }) }, |
| 404 | }, emit.lower.target), &.{}); |
| 405 | try emit.encodeInst(try .new(.none, lowered_inst.encoding.mnemonic, &.{ |
| 406 | lowered_inst.ops[0], |
| 407 | .{ .mem = .initSib(lowered_inst.ops[1].mem.sib.ptr_size, .{ |
| 408 | .base = .{ .reg = .eax }, |
| 409 | .disp = std.math.minInt(i32), |
| 410 | }) }, |
| 411 | }, emit.lower.target), reloc_info); |
| 412 | }, |
| 413 | .x86_64 => { |
| 414 | try emit.encodeInst(try .new(.none, .mov, &.{ |
| 415 | .{ .reg = .rax }, |
| 416 | .{ .mem = .initSib(.qword, .{ |
| 417 | .base = .{ .reg = .gs }, |
| 418 | .disp = 8 * 11, |
| 419 | }) }, |
| 420 | }, emit.lower.target), &.{}); |
| 421 | try emit.encodeInst(try .new(.none, .mov, &.{ |
| 422 | .{ .reg = .edi }, |
| 423 | .{ .mem = .initRip(.dword, 0) }, |
| 424 | }, emit.lower.target), &.{.{ |
| 425 | .op_index = 1, |
| 426 | .target = .{ .symbol = .{ |
| 427 | .symbol = @fromBackingInt(@intCast(@backingInt( |
| 428 | try coff.globalSymbol(.{ .name = "_tls_index" }), |
| 429 | ))), |
| 430 | .is_extern = false, |
| 431 | } }, |
| 432 | }}); |
| 433 | try emit.encodeInst(try .new(.none, .mov, &.{ |
| 434 | .{ .reg = .rax }, |
| 435 | .{ .mem = .initSib(.qword, .{ |
| 436 | .base = .{ .reg = .rax }, |
| 437 | .scale_index = .{ .index = .rdi, .scale = 8 }, |
| 438 | }) }, |
| 439 | }, emit.lower.target), &.{}); |
| 440 | try emit.encodeInst(try .new(.none, lowered_inst.encoding.mnemonic, &.{ |
| 441 | lowered_inst.ops[0], |
| 442 | .{ .mem = .initSib(lowered_inst.ops[1].mem.sib.ptr_size, .{ |
| 443 | .base = .{ .reg = .rax }, |
| 444 | .disp = std.math.minInt(i32), |
| 445 | }) }, |
| 446 | }, emit.lower.target), reloc_info); |
| 447 | }, |
| 448 | } |
| 449 | } else return emit.fail("TODO implement relocs for {s}", .{ |
| 450 | @tagName(emit.bin_file.tag), |
| 451 | }); |
| 452 | continue :lowered_inst; |
| 453 | }, |
| 454 | }; |
| 455 | try emit.encodeInst(lowered_inst, reloc_info); |
| 456 | } |
| 457 | assert(lowered_relocs.len == 0); |
| 458 | |
| 459 | if (lowered.insts.len == 0) { |
| 460 | const mir_inst = emit.lower.mir.instructions.get(mir_index); |
| 461 | assert(mir_inst.tag == .pseudo); |
| 462 | switch (mir_inst.ops) { |
| 463 | else => unreachable, |
| 464 | .pseudo_dbg_prologue_end_none => switch (emit.debug_output) { |
| 465 | inline .dwarf, .dwarf2 => |dwarf| { |
| 466 | try dwarf.setPrologueEnd(); |
| 467 | log.debug("mirDbgPrologueEnd (line={d}, col={d})", .{ |
| 468 | emit.prev_di_loc.line, emit.prev_di_loc.column, |
| 469 | }); |
| 470 | }, |
| 471 | .eh_frame, .none => {}, |
| 472 | }, |
| 473 | .pseudo_dbg_line_stmt_line_column => try emit.dbgAdvanceLineAndPc(.{ |
| 474 | .line = mir_inst.data.line_column.line, |
| 475 | .column = mir_inst.data.line_column.column, |
| 476 | .is_stmt = true, |
| 477 | }), |
| 478 | .pseudo_dbg_line_line_column => try emit.dbgAdvanceLineAndPc(.{ |
| 479 | .line = mir_inst.data.line_column.line, |
| 480 | .column = mir_inst.data.line_column.column, |
| 481 | .is_stmt = false, |
| 482 | }), |
| 483 | .pseudo_dbg_epilogue_begin_line_column => { |
| 484 | switch (emit.debug_output) { |
| 485 | inline .dwarf, .dwarf2 => |dwarf| { |
| 486 | try dwarf.setEpilogueBegin(); |
| 487 | log.debug("mirDbgEpilogueBegin (line={d}, col={d})", .{ |
| 488 | emit.prev_di_loc.line, emit.prev_di_loc.column, |
| 489 | }); |
| 490 | }, |
| 491 | .eh_frame, .none => {}, |
| 492 | } |
| 493 | try emit.dbgAdvanceLineAndPc(.{ |
| 494 | .line = mir_inst.data.line_column.line, |
| 495 | .column = mir_inst.data.line_column.column, |
| 496 | }); |
| 497 | }, |
| 498 | .pseudo_dbg_enter_block_none => switch (emit.debug_output) { |
| 499 | inline .dwarf, .dwarf2 => |dwarf| { |
| 500 | log.debug("mirDbgEnterBlock (line={d}, col={d})", .{ |
| 501 | emit.prev_di_loc.line, emit.prev_di_loc.column, |
| 502 | }); |
| 503 | try dwarf.enterBlock(emit.w.end); |
| 504 | }, |
| 505 | .eh_frame, .none => {}, |
| 506 | }, |
| 507 | .pseudo_dbg_leave_block_none => switch (emit.debug_output) { |
| 508 | inline .dwarf, .dwarf2 => |dwarf| { |
| 509 | log.debug("mirDbgLeaveBlock (line={d}, col={d})", .{ |
| 510 | emit.prev_di_loc.line, emit.prev_di_loc.column, |
| 511 | }); |
| 512 | try dwarf.leaveBlock(emit.w.end); |
| 513 | }, |
| 514 | .eh_frame, .none => {}, |
| 515 | }, |
| 516 | .pseudo_dbg_enter_inline_func => switch (emit.debug_output) { |
| 517 | inline .dwarf, .dwarf2 => |dwarf| { |
| 518 | log.debug("mirDbgEnterInline (line={d}, col={d})", .{ |
| 519 | emit.prev_di_loc.line, emit.prev_di_loc.column, |
| 520 | }); |
| 521 | try dwarf.enterInlineFunc(mir_inst.data.ip_index, emit.w.end, emit.prev_di_loc.line, emit.prev_di_loc.column); |
| 522 | }, |
| 523 | .eh_frame, .none => {}, |
| 524 | }, |
| 525 | .pseudo_dbg_leave_inline_func => switch (emit.debug_output) { |
| 526 | inline .dwarf, .dwarf2 => |dwarf| { |
| 527 | log.debug("mirDbgLeaveInline (line={d}, col={d})", .{ |
| 528 | emit.prev_di_loc.line, emit.prev_di_loc.column, |
| 529 | }); |
| 530 | try dwarf.leaveInlineFunc(mir_inst.data.ip_index, emit.w.end); |
| 531 | }, |
| 532 | .eh_frame, .none => {}, |
| 533 | }, |
| 534 | .pseudo_dbg_end_none => try emit.dbgAdvanceLineAndPc(.{ |
| 535 | .line = emit.prev_di_loc.line, |
| 536 | .column = emit.prev_di_loc.column, |
| 537 | .end = true, |
| 538 | }), |
| 539 | .pseudo_dbg_arg_none, |
| 540 | .pseudo_dbg_arg_i_s, |
| 541 | .pseudo_dbg_arg_i_u, |
| 542 | .pseudo_dbg_arg_i_64, |
| 543 | .pseudo_dbg_arg_ro, |
| 544 | .pseudo_dbg_arg_fa, |
| 545 | .pseudo_dbg_arg_m, |
| 546 | .pseudo_dbg_var_none, |
| 547 | .pseudo_dbg_var_i_s, |
| 548 | .pseudo_dbg_var_i_u, |
| 549 | .pseudo_dbg_var_i_64, |
| 550 | .pseudo_dbg_var_ro, |
| 551 | .pseudo_dbg_var_fa, |
| 552 | .pseudo_dbg_var_m, |
| 553 | => switch (emit.debug_output) { |
| 554 | inline .dwarf, .dwarf2 => |dwarf, tag| { |
| 555 | const DwarfLoc = switch (tag) { |
| 556 | .dwarf => link.File.Dwarf.Loc, |
| 557 | .dwarf2 => link.File.Dwarf2.Loc, |
| 558 | .eh_frame, .none => comptime unreachable, |
| 559 | }; |
| 560 | var loc_buf: [2]DwarfLoc = undefined; |
| 561 | const loc: DwarfLoc = loc: switch (mir_inst.ops) { |
| 562 | else => unreachable, |
| 563 | .pseudo_dbg_arg_none, .pseudo_dbg_var_none => .empty, |
| 564 | .pseudo_dbg_arg_i_s, |
| 565 | .pseudo_dbg_arg_i_u, |
| 566 | .pseudo_dbg_var_i_s, |
| 567 | .pseudo_dbg_var_i_u, |
| 568 | => .{ .stack_value = stack_value: { |
| 569 | loc_buf[0] = switch (emit.lower.imm(mir_inst.ops, mir_inst.data.i.i)) { |
| 570 | .signed => |s| .{ .consts = s }, |
| 571 | .unsigned => |u| .{ .constu = u }, |
| 572 | }; |
| 573 | break :stack_value &loc_buf[0]; |
| 574 | } }, |
| 575 | .pseudo_dbg_arg_i_64, .pseudo_dbg_var_i_64 => .{ .stack_value = stack_value: { |
| 576 | loc_buf[0] = .{ .constu = mir_inst.data.i64 }; |
| 577 | break :stack_value &loc_buf[0]; |
| 578 | } }, |
| 579 | .pseudo_dbg_arg_fa, .pseudo_dbg_var_fa => { |
| 580 | const reg_off = emit.lower.mir.resolveFrameAddr(mir_inst.data.fa); |
| 581 | break :loc .{ .plus = .{ |
| 582 | reg: { |
| 583 | loc_buf[0] = .{ .breg = reg_off.reg.dwarfNum() }; |
| 584 | break :reg &loc_buf[0]; |
| 585 | }, |
| 586 | off: { |
| 587 | loc_buf[1] = .{ .consts = reg_off.off }; |
| 588 | break :off &loc_buf[1]; |
| 589 | }, |
| 590 | } }; |
| 591 | }, |
| 592 | .pseudo_dbg_arg_m, .pseudo_dbg_var_m => { |
| 593 | const mem = emit.lower.mir.resolveMemoryExtra(mir_inst.data.x.payload).decode(); |
| 594 | break :loc .{ .plus = .{ |
| 595 | base: { |
| 596 | loc_buf[0] = switch (mem.base()) { |
| 597 | .none => .{ .constu = 0 }, |
| 598 | .reg => |reg| .{ .breg = reg.dwarfNum() }, |
| 599 | .frame, .table, .rip_inst => unreachable, |
| 600 | .nav => |nav| .{ .addr_reloc = try codegen.genNavRef( |
| 601 | emit.bin_file, |
| 602 | emit.pt, |
| 603 | nav, |
| 604 | ) }, |
| 605 | .uav => |uav| .{ .addr_reloc = try emit.bin_file.lowerUav( |
| 606 | emit.pt, |
| 607 | uav.val, |
| 608 | Type.fromInterned(uav.orig_ty).ptrAlignment(emit.pt.zcu), |
| 609 | ) }, |
| 610 | .lazy_sym, .extern_func => unreachable, |
| 611 | }; |
| 612 | break :base &loc_buf[0]; |
| 613 | }, |
| 614 | disp: { |
| 615 | loc_buf[1] = switch (mem.disp()) { |
| 616 | .signed => |s| .{ .consts = s }, |
| 617 | .unsigned => |u| .{ .constu = u }, |
| 618 | }; |
| 619 | break :disp &loc_buf[1]; |
| 620 | }, |
| 621 | } }; |
| 622 | }, |
| 623 | }; |
| 624 | |
| 625 | const local = &emit.lower.mir.locals[local_index]; |
| 626 | local_index += 1; |
| 627 | try dwarf.genLocalVarDebugInfo( |
| 628 | switch (mir_inst.ops) { |
| 629 | else => unreachable, |
| 630 | .pseudo_dbg_arg_none, |
| 631 | .pseudo_dbg_arg_i_s, |
| 632 | .pseudo_dbg_arg_i_u, |
| 633 | .pseudo_dbg_arg_i_64, |
| 634 | .pseudo_dbg_arg_ro, |
| 635 | .pseudo_dbg_arg_fa, |
| 636 | .pseudo_dbg_arg_m, |
| 637 | .pseudo_dbg_arg_val, |
| 638 | => .arg, |
| 639 | .pseudo_dbg_var_none, |
| 640 | .pseudo_dbg_var_i_s, |
| 641 | .pseudo_dbg_var_i_u, |
| 642 | .pseudo_dbg_var_i_64, |
| 643 | .pseudo_dbg_var_ro, |
| 644 | .pseudo_dbg_var_fa, |
| 645 | .pseudo_dbg_var_m, |
| 646 | .pseudo_dbg_var_val, |
| 647 | => .local_var, |
| 648 | }, |
| 649 | local.name.toSlice(&emit.lower.mir), |
| 650 | .fromInterned(local.type), |
| 651 | loc, |
| 652 | ); |
| 653 | }, |
| 654 | .eh_frame, .none => local_index += 1, |
| 655 | }, |
| 656 | .pseudo_dbg_arg_val, .pseudo_dbg_var_val => switch (emit.debug_output) { |
| 657 | inline .dwarf, .dwarf2 => |dwarf| { |
| 658 | const local = &emit.lower.mir.locals[local_index]; |
| 659 | local_index += 1; |
| 660 | try dwarf.genLocalConstDebugInfo( |
| 661 | switch (mir_inst.ops) { |
| 662 | else => unreachable, |
| 663 | .pseudo_dbg_arg_val => .comptime_arg, |
| 664 | .pseudo_dbg_var_val => .local_const, |
| 665 | }, |
| 666 | local.name.toSlice(&emit.lower.mir), |
| 667 | .fromInterned(mir_inst.data.ip_index), |
| 668 | ); |
| 669 | }, |
| 670 | .eh_frame, .none => local_index += 1, |
| 671 | }, |
| 672 | .pseudo_dbg_var_args_none => switch (emit.debug_output) { |
| 673 | inline .dwarf, .dwarf2 => |dwarf| try dwarf.genVarArgsDebugInfo(), |
| 674 | .eh_frame, .none => {}, |
| 675 | }, |
| 676 | .pseudo_dead_none => {}, |
| 677 | } |
| 678 | } |
| 679 | } |
| 680 | for (emit.relocs.items) |reloc| { |
| 681 | const target = emit.code_offset_mapping.items[reloc.target]; |
| 682 | const disp = @as(i64, @intCast(target)) - @as(i64, @intCast(reloc.inst_offset + reloc.inst_length)) + reloc.target_offset; |
| 683 | const inst_bytes = emit.w.buffered()[reloc.inst_offset..][0..reloc.inst_length]; |
| 684 | switch (reloc.source_length) { |
| 685 | else => unreachable, |
| 686 | inline 1, 4 => |source_length| std.mem.writeInt( |
| 687 | @Int(.signed, @as(u16, 8) * source_length), |
| 688 | inst_bytes[reloc.source_offset..][0..source_length], |
| 689 | @intCast(disp), |
| 690 | .little, |
| 691 | ), |
| 692 | } |
| 693 | } |
| 694 | if (emit.lower.mir.table.len > 0) { |
| 695 | const ptr_size = @divExact(emit.lower.target.ptrBitWidth(), 8); |
| 696 | var table_offset = std.mem.alignForward(u32, @intCast(emit.w.end), ptr_size); |
| 697 | if (emit.bin_file.cast(.elf)) |elf_file| { |
| 698 | const zo = elf_file.zigObjectPtr().?; |
| 699 | const atom = zo.symbol(@backingInt(emit.atom_id)).atom(elf_file).?; |
| 700 | |
| 701 | for (emit.table_relocs.items) |table_reloc| try atom.addReloc(gpa, .{ |
| 702 | .r_offset = table_reloc.source_offset, |
| 703 | .r_info = @as(u64, @backingInt(emit.atom_id)) << 32 | @backingInt(std.elf.R_X86_64.@"32S"), |
| 704 | .r_addend = @as(i64, table_offset) + table_reloc.target_offset, |
| 705 | }, zo); |
| 706 | for (emit.lower.mir.table) |entry| { |
| 707 | try atom.addReloc(gpa, .{ |
| 708 | .r_offset = table_offset, |
| 709 | .r_info = @as(u64, @backingInt(emit.atom_id)) << 32 | @backingInt(std.elf.R_X86_64.@"64"), |
| 710 | .r_addend = emit.code_offset_mapping.items[entry], |
| 711 | }, zo); |
| 712 | table_offset += ptr_size; |
| 713 | } |
| 714 | try emit.w.splatByteAll(0, table_offset - emit.w.end); |
| 715 | } else if (emit.bin_file.cast(.elf2)) |elf| { |
| 716 | for (emit.table_relocs.items) |table_reloc| try elf.addReloc( |
| 717 | emit.atom_id, |
| 718 | table_reloc.source_offset, |
| 719 | elf.symbolForAtom(emit.atom_id), |
| 720 | @as(i64, table_offset) + table_reloc.target_offset, |
| 721 | .{ .X86_64 = .@"32S" }, |
| 722 | ); |
| 723 | for (emit.lower.mir.table) |entry| { |
| 724 | try elf.addReloc( |
| 725 | emit.atom_id, |
| 726 | table_offset, |
| 727 | elf.symbolForAtom(emit.atom_id), |
| 728 | emit.code_offset_mapping.items[entry], |
| 729 | .{ .X86_64 = .@"64" }, |
| 730 | ); |
| 731 | table_offset += ptr_size; |
| 732 | } |
| 733 | try emit.w.splatByteAll(0, table_offset - emit.w.end); |
| 734 | } else unreachable; |
| 735 | } |
| 736 | } |
| 737 | |
| 738 | pub fn deinit(emit: *Emit) void { |
| 739 | const gpa = emit.bin_file.comp.gpa; |
| 740 | emit.code_offset_mapping.deinit(gpa); |
| 741 | emit.relocs.deinit(gpa); |
| 742 | emit.table_relocs.deinit(gpa); |
| 743 | emit.* = undefined; |
| 744 | } |
| 745 | |
| 746 | const RelocInfo = struct { |
| 747 | op_index: Lower.InstOpIndex, |
| 748 | off: i32 = 0, |
| 749 | target: Target, |
| 750 | |
| 751 | const Target = union(enum) { |
| 752 | inst: Mir.Inst.Index, |
| 753 | table, |
| 754 | branch: Symbol, |
| 755 | symbol: Symbol, |
| 756 | tlv: Symbol, |
| 757 | tls: link.File.SymbolId, |
| 758 | |
| 759 | const Symbol = struct { |
| 760 | symbol: link.File.SymbolId, |
| 761 | is_extern: bool, |
| 762 | is_dll_import: bool = false, |
| 763 | force_pcrel_direct: bool = false, |
| 764 | }; |
| 765 | }; |
| 766 | }; |
| 767 | |
| 768 | fn encodeInst(emit: *Emit, lowered_inst: Instruction, reloc_info: []const RelocInfo) Error!void { |
| 769 | const comp = emit.bin_file.comp; |
| 770 | const gpa = comp.gpa; |
| 771 | const start_offset: u32 = @intCast(emit.w.end); |
| 772 | lowered_inst.encode(emit.w, .{}) catch |err| switch (err) { |
| 773 | error.WriteFailed => return error.OutOfMemory, |
| 774 | else => |e| return e, |
| 775 | }; |
| 776 | const end_offset: u32 = @intCast(emit.w.end); |
| 777 | for (reloc_info) |reloc| switch (reloc.target) { |
| 778 | .inst => |target_inst| { |
| 779 | const inst_length: u4 = @intCast(end_offset - start_offset); |
| 780 | const reloc_offset, const reloc_length = reloc_offset_length: { |
| 781 | var reloc_offset = inst_length; |
| 782 | var op_index: usize = lowered_inst.ops.len; |
| 783 | while (true) { |
| 784 | op_index -= 1; |
| 785 | const op = lowered_inst.encoding.data.ops[op_index]; |
| 786 | if (op == .none) continue; |
| 787 | const is_mem = op.isMemory(); |
| 788 | const enc_length: u4 = if (is_mem) switch (lowered_inst.ops[op_index].mem.sib.base) { |
| 789 | .rip_inst => 4, |
| 790 | else => unreachable, |
| 791 | } else @intCast(@divCeil(op.immBitSize(), 8)); |
| 792 | reloc_offset -= enc_length; |
| 793 | if (op_index == reloc.op_index) break :reloc_offset_length .{ reloc_offset, enc_length }; |
| 794 | assert(!is_mem); |
| 795 | } |
| 796 | }; |
| 797 | try emit.relocs.append(emit.lower.allocator, .{ |
| 798 | .inst_offset = start_offset, |
| 799 | .inst_length = inst_length, |
| 800 | .source_offset = reloc_offset, |
| 801 | .source_length = reloc_length, |
| 802 | .target = target_inst, |
| 803 | .target_offset = reloc.off, |
| 804 | }); |
| 805 | }, |
| 806 | .table => try emit.table_relocs.append(emit.lower.allocator, .{ |
| 807 | .source_offset = end_offset - 4, |
| 808 | .target_offset = reloc.off, |
| 809 | }), |
| 810 | .symbol => |target| if (emit.bin_file.cast(.elf)) |elf_file| { |
| 811 | const zo = elf_file.zigObjectPtr().?; |
| 812 | const atom = zo.symbol(@backingInt(emit.atom_id)).atom(elf_file).?; |
| 813 | const r_type: std.elf.R_X86_64 = if (!emit.pic) |
| 814 | .@"32S" |
| 815 | else if (target.is_extern and !target.force_pcrel_direct) |
| 816 | .GOTPCREL |
| 817 | else |
| 818 | .PC32; |
| 819 | try atom.addReloc(gpa, .{ |
| 820 | .r_offset = end_offset - 4, |
| 821 | .r_info = @as(u64, @backingInt(target.symbol)) << 32 | @backingInt(r_type), |
| 822 | .r_addend = if (emit.pic) reloc.off - 4 else reloc.off, |
| 823 | }, zo); |
| 824 | } else if (emit.bin_file.cast(.macho)) |macho_file| { |
| 825 | const zo = macho_file.getZigObject().?; |
| 826 | const atom = zo.symbols.items[@backingInt(emit.atom_id)].getAtom(macho_file).?; |
| 827 | try atom.addReloc(macho_file, .{ |
| 828 | .tag = .@"extern", |
| 829 | .offset = end_offset - 4, |
| 830 | .target = @backingInt(target.symbol), |
| 831 | .addend = reloc.off, |
| 832 | .type = if (target.is_extern and !target.force_pcrel_direct) .got_load else .signed, |
| 833 | .meta = .{ |
| 834 | .pcrel = true, |
| 835 | .has_subtractor = false, |
| 836 | .length = 2, |
| 837 | .symbolnum = @intCast(@backingInt(target.symbol)), |
| 838 | }, |
| 839 | }); |
| 840 | } else if (emit.bin_file.cast(.elf2)) |elf| try elf.addReloc( |
| 841 | emit.atom_id, |
| 842 | end_offset - 4, |
| 843 | target.symbol, |
| 844 | if (emit.pic) reloc.off - 4 else reloc.off, |
| 845 | .{ .X86_64 = rt: { |
| 846 | if (!emit.pic) break :rt .@"32S"; |
| 847 | if (target.is_extern and !target.force_pcrel_direct) break :rt .GOTPCREL; |
| 848 | break :rt .PC32; |
| 849 | } }, |
| 850 | ) else if (emit.bin_file.cast(.coff2)) |coff| try coff.addReloc( |
| 851 | @fromBackingInt(@intCast(@backingInt(emit.atom_id))), |
| 852 | end_offset - 4, |
| 853 | @fromBackingInt(@intCast(@backingInt(target.symbol))), |
| 854 | .{ .known = reloc.off }, |
| 855 | .{ .AMD64 = .REL32 }, |
| 856 | ) else unreachable, |
| 857 | .branch => |target| if (emit.bin_file.cast(.elf)) |elf_file| { |
| 858 | const zo = elf_file.zigObjectPtr().?; |
| 859 | const atom = zo.symbol(@backingInt(emit.atom_id)).atom(elf_file).?; |
| 860 | const r_type: std.elf.R_X86_64 = .PLT32; |
| 861 | try atom.addReloc(gpa, .{ |
| 862 | .r_offset = end_offset - 4, |
| 863 | .r_info = @as(u64, @backingInt(target.symbol)) << 32 | @backingInt(r_type), |
| 864 | .r_addend = reloc.off - 4, |
| 865 | }, zo); |
| 866 | } else if (emit.bin_file.cast(.elf2)) |elf| try elf.addReloc( |
| 867 | emit.atom_id, |
| 868 | end_offset - 4, |
| 869 | target.symbol, |
| 870 | reloc.off - 4, |
| 871 | .{ .X86_64 = .PLT32 }, |
| 872 | ) else if (emit.bin_file.cast(.macho)) |macho_file| { |
| 873 | const zo = macho_file.getZigObject().?; |
| 874 | const atom = zo.symbols.items[@backingInt(emit.atom_id)].getAtom(macho_file).?; |
| 875 | try atom.addReloc(macho_file, .{ |
| 876 | .tag = .@"extern", |
| 877 | .offset = end_offset - 4, |
| 878 | .target = @backingInt(target.symbol), |
| 879 | .addend = reloc.off, |
| 880 | .type = .branch, |
| 881 | .meta = .{ |
| 882 | .pcrel = true, |
| 883 | .has_subtractor = false, |
| 884 | .length = 2, |
| 885 | .symbolnum = @intCast(@backingInt(target.symbol)), |
| 886 | }, |
| 887 | }); |
| 888 | } else if (emit.bin_file.cast(.coff2)) |coff| try coff.addReloc( |
| 889 | @fromBackingInt(@intCast(@backingInt(emit.atom_id))), |
| 890 | end_offset - 4, |
| 891 | @fromBackingInt(@intCast(@backingInt(target.symbol))), |
| 892 | .{ .known = reloc.off }, |
| 893 | .{ .AMD64 = .REL32 }, |
| 894 | ) else return emit.fail("TODO implement {s} reloc for {s}", .{ |
| 895 | @tagName(reloc.target), @tagName(emit.bin_file.tag), |
| 896 | }), |
| 897 | .tls => |target_symbol| if (emit.bin_file.cast(.elf)) |elf_file| { |
| 898 | const zo = elf_file.zigObjectPtr().?; |
| 899 | const atom = zo.symbol(@backingInt(emit.atom_id)).atom(elf_file).?; |
| 900 | const r_type: std.elf.R_X86_64 = if (emit.pic) .TLSLD else unreachable; |
| 901 | try atom.addReloc(gpa, .{ |
| 902 | .r_offset = end_offset - 4, |
| 903 | .r_info = @as(u64, @backingInt(target_symbol)) << 32 | @backingInt(r_type), |
| 904 | .r_addend = reloc.off - 4, |
| 905 | }, zo); |
| 906 | } else if (emit.bin_file.cast(.elf2)) |elf| try elf.addReloc( |
| 907 | emit.atom_id, |
| 908 | end_offset - 4, |
| 909 | target_symbol, |
| 910 | reloc.off - 4, |
| 911 | .{ .X86_64 = if (emit.pic) .TLSLD else unreachable }, |
| 912 | ) else return emit.fail("TODO implement {s} reloc for {s}", .{ |
| 913 | @tagName(reloc.target), @tagName(emit.bin_file.tag), |
| 914 | }), |
| 915 | .tlv => |target| if (emit.bin_file.cast(.elf)) |elf_file| { |
| 916 | const zo = elf_file.zigObjectPtr().?; |
| 917 | const atom = zo.symbol(@backingInt(emit.atom_id)).atom(elf_file).?; |
| 918 | const r_type: std.elf.R_X86_64 = if (emit.pic) .DTPOFF32 else .TPOFF32; |
| 919 | try atom.addReloc(gpa, .{ |
| 920 | .r_offset = end_offset - 4, |
| 921 | .r_info = @as(u64, @backingInt(target.symbol)) << 32 | @backingInt(r_type), |
| 922 | .r_addend = reloc.off, |
| 923 | }, zo); |
| 924 | } else if (emit.bin_file.cast(.elf2)) |elf| try elf.addReloc( |
| 925 | emit.atom_id, |
| 926 | end_offset - 4, |
| 927 | target.symbol, |
| 928 | reloc.off, |
| 929 | .{ .X86_64 = if (emit.pic) .DTPOFF32 else .TPOFF32 }, |
| 930 | ) else if (emit.bin_file.cast(.macho)) |macho_file| { |
| 931 | const zo = macho_file.getZigObject().?; |
| 932 | const atom = zo.symbols.items[@backingInt(emit.atom_id)].getAtom(macho_file).?; |
| 933 | try atom.addReloc(macho_file, .{ |
| 934 | .tag = .@"extern", |
| 935 | .offset = end_offset - 4, |
| 936 | .target = @backingInt(target.symbol), |
| 937 | .addend = reloc.off, |
| 938 | .type = .tlv, |
| 939 | .meta = .{ |
| 940 | .pcrel = true, |
| 941 | .has_subtractor = false, |
| 942 | .length = 2, |
| 943 | .symbolnum = @intCast(@backingInt(target.symbol)), |
| 944 | }, |
| 945 | }); |
| 946 | } else if (emit.bin_file.cast(.coff2)) |coff| try coff.addReloc( |
| 947 | @fromBackingInt(@intCast(@backingInt(emit.atom_id))), |
| 948 | end_offset - 4, |
| 949 | @fromBackingInt(@intCast(@backingInt(target.symbol))), |
| 950 | .{ .known = reloc.off }, |
| 951 | .{ .AMD64 = .SECREL }, |
| 952 | ) else return emit.fail("TODO implement {s} reloc for {s}", .{ |
| 953 | @tagName(reloc.target), @tagName(emit.bin_file.tag), |
| 954 | }), |
| 955 | }; |
| 956 | } |
| 957 | |
| 958 | fn fail(emit: *Emit, comptime format: []const u8, args: anytype) Error { |
| 959 | return switch (emit.lower.fail(format, args)) { |
| 960 | error.LowerFail => error.EmitFail, |
| 961 | else => |e| e, |
| 962 | }; |
| 963 | } |
| 964 | |
| 965 | const Reloc = struct { |
| 966 | /// Offset of the instruction. |
| 967 | inst_offset: u32, |
| 968 | /// Length of the instruction. |
| 969 | inst_length: u4, |
| 970 | /// Offset of the relocation within the instruction. |
| 971 | source_offset: u4, |
| 972 | /// Length of the relocation. |
| 973 | source_length: u4, |
| 974 | /// Target of the relocation. |
| 975 | target: Mir.Inst.Index, |
| 976 | /// Offset from the target. |
| 977 | target_offset: i32, |
| 978 | }; |
| 979 | |
| 980 | const TableReloc = struct { |
| 981 | /// Offset of the relocation. |
| 982 | source_offset: u32, |
| 983 | /// Offset from the start of the table. |
| 984 | target_offset: i32, |
| 985 | }; |
| 986 | |
| 987 | const Loc = struct { |
| 988 | line: u32, |
| 989 | column: u32, |
| 990 | is_stmt: ?bool = null, |
| 991 | end: bool = false, |
| 992 | }; |
| 993 | |
| 994 | fn dbgAdvanceLineAndPc(emit: *Emit, loc: Loc) Error!void { |
| 995 | switch (emit.debug_output) { |
| 996 | inline .dwarf, .dwarf2 => |dwarf| { |
| 997 | const delta_line = @as(i33, loc.line) - @as(i33, emit.prev_di_loc.line); |
| 998 | const delta_pc: usize = emit.w.end - emit.prev_di_pc; |
| 999 | log.debug(" (advance pc={d} and line={d})", .{ delta_pc, delta_line }); |
| 1000 | if (loc.is_stmt) |is_stmt| if (is_stmt != emit.prev_di_loc.is_stmt) try dwarf.negateStmt(); |
| 1001 | if (loc.column != emit.prev_di_loc.column) try dwarf.setColumn(loc.column); |
| 1002 | try dwarf.advanceLineAndPc(delta_line, delta_pc, loc.end); |
| 1003 | emit.prev_di_loc = loc; |
| 1004 | emit.prev_di_pc = emit.w.end; |
| 1005 | }, |
| 1006 | .eh_frame, .none => {}, |
| 1007 | } |
| 1008 | } |
| 1009 | |
| 1010 | const assert = std.debug.assert; |
| 1011 | const bits = @import("bits.zig"); |
| 1012 | const codegen = @import("../../codegen.zig"); |
| 1013 | const Emit = @This(); |
| 1014 | const encoder = @import("encoder.zig"); |
| 1015 | const Instruction = encoder.Instruction; |
| 1016 | const InternPool = @import("../../InternPool.zig"); |
| 1017 | const link = @import("../../link.zig"); |
| 1018 | const log = std.log.scoped(.emit); |
| 1019 | const Lower = @import("Lower.zig"); |
| 1020 | const Mir = @import("Mir.zig"); |
| 1021 | const std = @import("std"); |
| 1022 | const Type = @import("../../Type.zig"); |
| 1023 | const Zcu = @import("../../Zcu.zig"); |