From f132f426b9a11774323eafc669da0c9731deb85b Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Sat, 5 Feb 2022 19:09:20 +0100 Subject: [PATCH] x86_64: add distinct MCValue representing symbol index in the linker For PIE targets, we defer getting an address of value until the linker has allocated all atoms and performed the relocations. In codegen, we represent this via `MCValue.linker_sym_index` value. --- src/arch/x86_64/CodeGen.zig | 123 ++++++++++++++++++++++-------------- 1 file changed, 76 insertions(+), 47 deletions(-) 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 };