diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index e464dd3cf9..e5c7c99501 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -118,6 +118,10 @@ pub const MCValue = union(enum) { /// The value is in memory at a hard-coded address. /// If the type is a pointer, it means the pointer address is at this memory location. memory: u64, + /// The value is in memory but not allocated an address yet by the linker, so we store + /// the symbol index instead. + /// If the type is a pointer, it means the pointer is the symbol. + linker_sym_index: u32, /// The value is one of the stack variables. /// If the type is a pointer, it means the pointer address is in the stack at this offset. stack_offset: i32, @@ -1686,8 +1690,10 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo else => return self.fail("TODO implement loading from register into {}", .{dst_mcv}), } }, - .memory => |addr| { - const reg = try self.copyToTmpRegister(ptr_ty, .{ .memory = addr }); + .memory, + .linker_sym_index, + => { + const reg = try self.copyToTmpRegister(ptr_ty, ptr); try self.load(dst_mcv, .{ .register = reg }, ptr_ty); }, .stack_offset => { @@ -1817,27 +1823,33 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type }, } }, - .memory => |addr| { + .linker_sym_index, + .memory, + => { value.freezeIfRegister(&self.register_manager); defer value.unfreezeIfRegister(&self.register_manager); const addr_reg: Register = blk: { - if (self.bin_file.options.pie) { - const addr_reg = try self.register_manager.allocReg(null); - _ = try self.addInst(.{ - .tag = .lea, - .ops = (Mir.Ops{ - .reg1 = addr_reg.to64(), - .flags = 0b10, - }).encode(), - .data = .{ .got_entry = @truncate(u32, addr) }, - }); - break :blk addr_reg; - } else { - // TODO: in case the address fits in an imm32 we can use [ds:imm32] - // instead of wasting an instruction copying the address to a register - const addr_reg = try self.copyToTmpRegister(ptr_ty, .{ .immediate = addr }); - break :blk addr_reg; + switch (ptr) { + .linker_sym_index => |sym_index| { + const addr_reg = try self.register_manager.allocReg(null); + _ = try self.addInst(.{ + .tag = .lea, + .ops = (Mir.Ops{ + .reg1 = addr_reg.to64(), + .flags = 0b10, + }).encode(), + .data = .{ .got_entry = sym_index }, + }); + break :blk addr_reg; + }, + .memory => |addr| { + // TODO: in case the address fits in an imm32 we can use [ds:imm32] + // instead of wasting an instruction copying the address to a register + const addr_reg = try self.copyToTmpRegister(ptr_ty, .{ .immediate = addr }); + break :blk addr_reg; + }, + else => unreachable, } }; @@ -2148,6 +2160,9 @@ fn genBinMathOpMir(self: *Self, mir_tag: Mir.Inst.Tag, dst_ty: Type, dst_mcv: MC .embedded_in_code, .memory => { return self.fail("TODO implement x86 ADD/SUB/CMP source memory", .{}); }, + .linker_sym_index => { + return self.fail("TODO implement x86 ADD/SUB/CMP source symbol at index in linker", .{}); + }, .stack_offset => |off| { if (off > math.maxInt(i32)) { return self.fail("stack offset too large", .{}); @@ -2232,6 +2247,9 @@ fn genBinMathOpMir(self: *Self, mir_tag: Mir.Inst.Tag, dst_ty: Type, dst_mcv: MC .embedded_in_code, .memory, .stack_offset => { return self.fail("TODO implement x86 ADD/SUB/CMP source memory", .{}); }, + .linker_sym_index => { + return self.fail("TODO implement x86 ADD/SUB/CMP source symbol at index in linker", .{}); + }, .compare_flags_unsigned => { return self.fail("TODO implement x86 ADD/SUB/CMP source compare flag (unsigned)", .{}); }, @@ -2243,6 +2261,9 @@ fn genBinMathOpMir(self: *Self, mir_tag: Mir.Inst.Tag, dst_ty: Type, dst_mcv: MC .embedded_in_code, .memory => { return self.fail("TODO implement x86 ADD/SUB/CMP destination memory", .{}); }, + .linker_sym_index => { + return self.fail("TODO implement x86 ADD/SUB/CMP destination symbol at index", .{}); + }, } } @@ -2296,6 +2317,9 @@ fn genIMulOpMir(self: *Self, dst_ty: Type, dst_mcv: MCValue, src_mcv: MCValue) ! .embedded_in_code, .memory, .stack_offset => { return self.fail("TODO implement x86 multiply source memory", .{}); }, + .linker_sym_index => { + return self.fail("TODO implement x86 multiply source symbol at index in linker", .{}); + }, .compare_flags_unsigned => { return self.fail("TODO implement x86 multiply source compare flag (unsigned)", .{}); }, @@ -2334,6 +2358,9 @@ fn genIMulOpMir(self: *Self, dst_ty: Type, dst_mcv: MCValue, src_mcv: MCValue) ! .embedded_in_code, .memory, .stack_offset => { return self.fail("TODO implement x86 multiply source memory", .{}); }, + .linker_sym_index => { + return self.fail("TODO implement x86 multiply source symbol at index in linker", .{}); + }, .compare_flags_unsigned => { return self.fail("TODO implement x86 multiply source compare flag (unsigned)", .{}); }, @@ -2345,6 +2372,9 @@ fn genIMulOpMir(self: *Self, dst_ty: Type, dst_mcv: MCValue, src_mcv: MCValue) ! .embedded_in_code, .memory => { return self.fail("TODO implement x86 multiply destination memory", .{}); }, + .linker_sym_index => { + return self.fail("TODO implement x86 multiply destination symbol at index in linker", .{}); + }, } } @@ -2448,6 +2478,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index) !void { .dead => unreachable, .embedded_in_code => unreachable, .memory => unreachable, + .linker_sym_index => unreachable, .compare_flags_signed => unreachable, .compare_flags_unsigned => unreachable, } @@ -2508,10 +2539,8 @@ fn airCall(self: *Self, inst: Air.Inst.Index) !void { if (self.air.value(callee)) |func_value| { if (func_value.castTag(.function)) |func_payload| { const func = func_payload.data; - // TODO I'm hacking my way through here by repurposing .memory for storing - // index to the GOT target symbol index. try self.genSetReg(Type.initTag(.usize), .rax, .{ - .memory = func.owner_decl.link.macho.local_sym_index, + .linker_sym_index = func.owner_decl.link.macho.local_sym_index, }); // callq *%rax _ = try self.addInst(.{ @@ -3547,6 +3576,7 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue) InnerErro }, .memory, .embedded_in_code, + .linker_sym_index, => { if (ty.abiSize(self.target.*) <= 8) { const reg = try self.copyToTmpRegister(ty, mcv); @@ -3952,29 +3982,28 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void .data = undefined, }); }, + .linker_sym_index => |sym_index| { + _ = try self.addInst(.{ + .tag = .lea, + .ops = (Mir.Ops{ + .reg1 = reg, + .flags = 0b10, + }).encode(), + .data = .{ .got_entry = sym_index }, + }); + // MOV reg, [reg] + _ = try self.addInst(.{ + .tag = .mov, + .ops = (Mir.Ops{ + .reg1 = reg, + .reg2 = reg, + .flags = 0b01, + }).encode(), + .data = .{ .imm = 0 }, + }); + }, .memory => |x| { - // TODO can we move this entire logic into Emit.zig like with aarch64? - if (self.bin_file.options.pie) { - // TODO we should flag up `x` as GOT symbol entry explicitly rather than as a hack. - _ = try self.addInst(.{ - .tag = .lea, - .ops = (Mir.Ops{ - .reg1 = reg, - .flags = 0b10, - }).encode(), - .data = .{ .got_entry = @truncate(u32, x) }, - }); - // MOV reg, [reg] - _ = try self.addInst(.{ - .tag = .mov, - .ops = (Mir.Ops{ - .reg1 = reg, - .reg2 = reg, - .flags = 0b01, - }).encode(), - .data = .{ .imm = 0 }, - }); - } else if (x <= math.maxInt(i32)) { + if (x <= math.maxInt(i32)) { // mov reg, [ds:imm32] _ = try self.addInst(.{ .tag = .mov, @@ -4285,9 +4314,9 @@ fn lowerDeclRef(self: *Self, tv: TypedValue, decl: *Module.Decl) InnerError!MCVa const got_addr = got.p_vaddr + decl.link.elf.offset_table_index * ptr_bytes; return MCValue{ .memory = got_addr }; } else if (self.bin_file.cast(link.File.MachO)) |_| { - // TODO I'm hacking my way through here by repurposing .memory for storing - // index to the GOT target symbol index. - return MCValue{ .memory = decl.link.macho.local_sym_index }; + // Because MachO is PIE-always-on, we defer memory address resolution until + // the linker has enough info to perform relocations. + return MCValue{ .linker_sym_index = decl.link.macho.local_sym_index }; } else if (self.bin_file.cast(link.File.Coff)) |coff_file| { const got_addr = coff_file.offset_table_virtual_address + decl.link.coff.offset_table_index * ptr_bytes; return MCValue{ .memory = got_addr };