authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2022-02-05 19:09:20+01:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2022-02-06 00:34:24+01:00
logf132f426b9a11774323eafc669da0c9731deb85b
tree7c00096b199fc33087c0c0bfbda0d684cf38c30b
parent4b3b487627d71fa082b0316383344b49c95bab0e

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.

1 files changed, 76 insertions(+), 47 deletions(-)

src/arch/x86_64/CodeGen.zig+76-47
...@@ -118,6 +118,10 @@ pub const MCValue = union(enum) {...@@ -118,6 +118,10 @@ pub const MCValue = union(enum) {
118 /// The value is in memory at a hard-coded address.118 /// The value is in memory at a hard-coded address.
119 /// If the type is a pointer, it means the pointer address is at this memory location.119 /// If the type is a pointer, it means the pointer address is at this memory location.
120 memory: u64,120 memory: u64,
121 /// The value is in memory but not allocated an address yet by the linker, so we store
122 /// the symbol index instead.
123 /// If the type is a pointer, it means the pointer is the symbol.
124 linker_sym_index: u32,
121 /// The value is one of the stack variables.125 /// The value is one of the stack variables.
122 /// If the type is a pointer, it means the pointer address is in the stack at this offset.126 /// If the type is a pointer, it means the pointer address is in the stack at this offset.
123 stack_offset: i32,127 stack_offset: i32,
...@@ -1686,8 +1690,10 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo...@@ -1686,8 +1690,10 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo
1686 else => return self.fail("TODO implement loading from register into {}", .{dst_mcv}),1690 else => return self.fail("TODO implement loading from register into {}", .{dst_mcv}),
1687 }1691 }
1688 },1692 },
1689 .memory => |addr| {1693 .memory,
1690 const reg = try self.copyToTmpRegister(ptr_ty, .{ .memory = addr });1694 .linker_sym_index,
1695 => {
1696 const reg = try self.copyToTmpRegister(ptr_ty, ptr);
1691 try self.load(dst_mcv, .{ .register = reg }, ptr_ty);1697 try self.load(dst_mcv, .{ .register = reg }, ptr_ty);
1692 },1698 },
1693 .stack_offset => {1699 .stack_offset => {
...@@ -1817,27 +1823,33 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type...@@ -1817,27 +1823,33 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type
1817 },1823 },
1818 }1824 }
1819 },1825 },
1820 .memory => |addr| {1826 .linker_sym_index,
1827 .memory,
1828 => {
1821 value.freezeIfRegister(&self.register_manager);1829 value.freezeIfRegister(&self.register_manager);
1822 defer value.unfreezeIfRegister(&self.register_manager);1830 defer value.unfreezeIfRegister(&self.register_manager);
18231831
1824 const addr_reg: Register = blk: {1832 const addr_reg: Register = blk: {
1825 if (self.bin_file.options.pie) {1833 switch (ptr) {
1826 const addr_reg = try self.register_manager.allocReg(null);1834 .linker_sym_index => |sym_index| {
1827 _ = try self.addInst(.{1835 const addr_reg = try self.register_manager.allocReg(null);
1828 .tag = .lea,1836 _ = try self.addInst(.{
1829 .ops = (Mir.Ops{1837 .tag = .lea,
1830 .reg1 = addr_reg.to64(),1838 .ops = (Mir.Ops{
1831 .flags = 0b10,1839 .reg1 = addr_reg.to64(),
1832 }).encode(),1840 .flags = 0b10,
1833 .data = .{ .got_entry = @truncate(u32, addr) },1841 }).encode(),
1834 });1842 .data = .{ .got_entry = sym_index },
1835 break :blk addr_reg;1843 });
1836 } else {1844 break :blk addr_reg;
1837 // TODO: in case the address fits in an imm32 we can use [ds:imm32]1845 },
1838 // instead of wasting an instruction copying the address to a register1846 .memory => |addr| {
1839 const addr_reg = try self.copyToTmpRegister(ptr_ty, .{ .immediate = addr });1847 // TODO: in case the address fits in an imm32 we can use [ds:imm32]
1840 break :blk addr_reg;1848 // instead of wasting an instruction copying the address to a register
1849 const addr_reg = try self.copyToTmpRegister(ptr_ty, .{ .immediate = addr });
1850 break :blk addr_reg;
1851 },
1852 else => unreachable,
1841 }1853 }
1842 };1854 };
18431855
...@@ -2148,6 +2160,9 @@ fn genBinMathOpMir(self: *Self, mir_tag: Mir.Inst.Tag, dst_ty: Type, dst_mcv: MC...@@ -2148,6 +2160,9 @@ fn genBinMathOpMir(self: *Self, mir_tag: Mir.Inst.Tag, dst_ty: Type, dst_mcv: MC
2148 .embedded_in_code, .memory => {2160 .embedded_in_code, .memory => {
2149 return self.fail("TODO implement x86 ADD/SUB/CMP source memory", .{});2161 return self.fail("TODO implement x86 ADD/SUB/CMP source memory", .{});
2150 },2162 },
2163 .linker_sym_index => {
2164 return self.fail("TODO implement x86 ADD/SUB/CMP source symbol at index in linker", .{});
2165 },
2151 .stack_offset => |off| {2166 .stack_offset => |off| {
2152 if (off > math.maxInt(i32)) {2167 if (off > math.maxInt(i32)) {
2153 return self.fail("stack offset too large", .{});2168 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...@@ -2232,6 +2247,9 @@ fn genBinMathOpMir(self: *Self, mir_tag: Mir.Inst.Tag, dst_ty: Type, dst_mcv: MC
2232 .embedded_in_code, .memory, .stack_offset => {2247 .embedded_in_code, .memory, .stack_offset => {
2233 return self.fail("TODO implement x86 ADD/SUB/CMP source memory", .{});2248 return self.fail("TODO implement x86 ADD/SUB/CMP source memory", .{});
2234 },2249 },
2250 .linker_sym_index => {
2251 return self.fail("TODO implement x86 ADD/SUB/CMP source symbol at index in linker", .{});
2252 },
2235 .compare_flags_unsigned => {2253 .compare_flags_unsigned => {
2236 return self.fail("TODO implement x86 ADD/SUB/CMP source compare flag (unsigned)", .{});2254 return self.fail("TODO implement x86 ADD/SUB/CMP source compare flag (unsigned)", .{});
2237 },2255 },
...@@ -2243,6 +2261,9 @@ fn genBinMathOpMir(self: *Self, mir_tag: Mir.Inst.Tag, dst_ty: Type, dst_mcv: MC...@@ -2243,6 +2261,9 @@ fn genBinMathOpMir(self: *Self, mir_tag: Mir.Inst.Tag, dst_ty: Type, dst_mcv: MC
2243 .embedded_in_code, .memory => {2261 .embedded_in_code, .memory => {
2244 return self.fail("TODO implement x86 ADD/SUB/CMP destination memory", .{});2262 return self.fail("TODO implement x86 ADD/SUB/CMP destination memory", .{});
2245 },2263 },
2264 .linker_sym_index => {
2265 return self.fail("TODO implement x86 ADD/SUB/CMP destination symbol at index", .{});
2266 },
2246 }2267 }
2247}2268}
22482269
...@@ -2296,6 +2317,9 @@ fn genIMulOpMir(self: *Self, dst_ty: Type, dst_mcv: MCValue, src_mcv: MCValue) !...@@ -2296,6 +2317,9 @@ fn genIMulOpMir(self: *Self, dst_ty: Type, dst_mcv: MCValue, src_mcv: MCValue) !
2296 .embedded_in_code, .memory, .stack_offset => {2317 .embedded_in_code, .memory, .stack_offset => {
2297 return self.fail("TODO implement x86 multiply source memory", .{});2318 return self.fail("TODO implement x86 multiply source memory", .{});
2298 },2319 },
2320 .linker_sym_index => {
2321 return self.fail("TODO implement x86 multiply source symbol at index in linker", .{});
2322 },
2299 .compare_flags_unsigned => {2323 .compare_flags_unsigned => {
2300 return self.fail("TODO implement x86 multiply source compare flag (unsigned)", .{});2324 return self.fail("TODO implement x86 multiply source compare flag (unsigned)", .{});
2301 },2325 },
...@@ -2334,6 +2358,9 @@ fn genIMulOpMir(self: *Self, dst_ty: Type, dst_mcv: MCValue, src_mcv: MCValue) !...@@ -2334,6 +2358,9 @@ fn genIMulOpMir(self: *Self, dst_ty: Type, dst_mcv: MCValue, src_mcv: MCValue) !
2334 .embedded_in_code, .memory, .stack_offset => {2358 .embedded_in_code, .memory, .stack_offset => {
2335 return self.fail("TODO implement x86 multiply source memory", .{});2359 return self.fail("TODO implement x86 multiply source memory", .{});
2336 },2360 },
2361 .linker_sym_index => {
2362 return self.fail("TODO implement x86 multiply source symbol at index in linker", .{});
2363 },
2337 .compare_flags_unsigned => {2364 .compare_flags_unsigned => {
2338 return self.fail("TODO implement x86 multiply source compare flag (unsigned)", .{});2365 return self.fail("TODO implement x86 multiply source compare flag (unsigned)", .{});
2339 },2366 },
...@@ -2345,6 +2372,9 @@ fn genIMulOpMir(self: *Self, dst_ty: Type, dst_mcv: MCValue, src_mcv: MCValue) !...@@ -2345,6 +2372,9 @@ fn genIMulOpMir(self: *Self, dst_ty: Type, dst_mcv: MCValue, src_mcv: MCValue) !
2345 .embedded_in_code, .memory => {2372 .embedded_in_code, .memory => {
2346 return self.fail("TODO implement x86 multiply destination memory", .{});2373 return self.fail("TODO implement x86 multiply destination memory", .{});
2347 },2374 },
2375 .linker_sym_index => {
2376 return self.fail("TODO implement x86 multiply destination symbol at index in linker", .{});
2377 },
2348 }2378 }
2349}2379}
23502380
...@@ -2448,6 +2478,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index) !void {...@@ -2448,6 +2478,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index) !void {
2448 .dead => unreachable,2478 .dead => unreachable,
2449 .embedded_in_code => unreachable,2479 .embedded_in_code => unreachable,
2450 .memory => unreachable,2480 .memory => unreachable,
2481 .linker_sym_index => unreachable,
2451 .compare_flags_signed => unreachable,2482 .compare_flags_signed => unreachable,
2452 .compare_flags_unsigned => unreachable,2483 .compare_flags_unsigned => unreachable,
2453 }2484 }
...@@ -2508,10 +2539,8 @@ fn airCall(self: *Self, inst: Air.Inst.Index) !void {...@@ -2508,10 +2539,8 @@ fn airCall(self: *Self, inst: Air.Inst.Index) !void {
2508 if (self.air.value(callee)) |func_value| {2539 if (self.air.value(callee)) |func_value| {
2509 if (func_value.castTag(.function)) |func_payload| {2540 if (func_value.castTag(.function)) |func_payload| {
2510 const func = func_payload.data;2541 const func = func_payload.data;
2511 // TODO I'm hacking my way through here by repurposing .memory for storing
2512 // index to the GOT target symbol index.
2513 try self.genSetReg(Type.initTag(.usize), .rax, .{2542 try self.genSetReg(Type.initTag(.usize), .rax, .{
2514 .memory = func.owner_decl.link.macho.local_sym_index,2543 .linker_sym_index = func.owner_decl.link.macho.local_sym_index,
2515 });2544 });
2516 // callq *%rax2545 // callq *%rax
2517 _ = try self.addInst(.{2546 _ = try self.addInst(.{
...@@ -3547,6 +3576,7 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue) InnerErro...@@ -3547,6 +3576,7 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue) InnerErro
3547 },3576 },
3548 .memory,3577 .memory,
3549 .embedded_in_code,3578 .embedded_in_code,
3579 .linker_sym_index,
3550 => {3580 => {
3551 if (ty.abiSize(self.target.*) <= 8) {3581 if (ty.abiSize(self.target.*) <= 8) {
3552 const reg = try self.copyToTmpRegister(ty, mcv);3582 const reg = try self.copyToTmpRegister(ty, mcv);
...@@ -3952,29 +3982,28 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void...@@ -3952,29 +3982,28 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void
3952 .data = undefined,3982 .data = undefined,
3953 });3983 });
3954 },3984 },
3985 .linker_sym_index => |sym_index| {
3986 _ = try self.addInst(.{
3987 .tag = .lea,
3988 .ops = (Mir.Ops{
3989 .reg1 = reg,
3990 .flags = 0b10,
3991 }).encode(),
3992 .data = .{ .got_entry = sym_index },
3993 });
3994 // MOV reg, [reg]
3995 _ = try self.addInst(.{
3996 .tag = .mov,
3997 .ops = (Mir.Ops{
3998 .reg1 = reg,
3999 .reg2 = reg,
4000 .flags = 0b01,
4001 }).encode(),
4002 .data = .{ .imm = 0 },
4003 });
4004 },
3955 .memory => |x| {4005 .memory => |x| {
3956 // TODO can we move this entire logic into Emit.zig like with aarch64?4006 if (x <= math.maxInt(i32)) {
3957 if (self.bin_file.options.pie) {
3958 // TODO we should flag up `x` as GOT symbol entry explicitly rather than as a hack.
3959 _ = try self.addInst(.{
3960 .tag = .lea,
3961 .ops = (Mir.Ops{
3962 .reg1 = reg,
3963 .flags = 0b10,
3964 }).encode(),
3965 .data = .{ .got_entry = @truncate(u32, x) },
3966 });
3967 // MOV reg, [reg]
3968 _ = try self.addInst(.{
3969 .tag = .mov,
3970 .ops = (Mir.Ops{
3971 .reg1 = reg,
3972 .reg2 = reg,
3973 .flags = 0b01,
3974 }).encode(),
3975 .data = .{ .imm = 0 },
3976 });
3977 } else if (x <= math.maxInt(i32)) {
3978 // mov reg, [ds:imm32]4007 // mov reg, [ds:imm32]
3979 _ = try self.addInst(.{4008 _ = try self.addInst(.{
3980 .tag = .mov,4009 .tag = .mov,
...@@ -4285,9 +4314,9 @@ fn lowerDeclRef(self: *Self, tv: TypedValue, decl: *Module.Decl) InnerError!MCVa...@@ -4285,9 +4314,9 @@ fn lowerDeclRef(self: *Self, tv: TypedValue, decl: *Module.Decl) InnerError!MCVa
4285 const got_addr = got.p_vaddr + decl.link.elf.offset_table_index * ptr_bytes;4314 const got_addr = got.p_vaddr + decl.link.elf.offset_table_index * ptr_bytes;
4286 return MCValue{ .memory = got_addr };4315 return MCValue{ .memory = got_addr };
4287 } else if (self.bin_file.cast(link.File.MachO)) |_| {4316 } else if (self.bin_file.cast(link.File.MachO)) |_| {
4288 // TODO I'm hacking my way through here by repurposing .memory for storing4317 // Because MachO is PIE-always-on, we defer memory address resolution until
4289 // index to the GOT target symbol index.4318 // the linker has enough info to perform relocations.
4290 return MCValue{ .memory = decl.link.macho.local_sym_index };4319 return MCValue{ .linker_sym_index = decl.link.macho.local_sym_index };
4291 } else if (self.bin_file.cast(link.File.Coff)) |coff_file| {4320 } else if (self.bin_file.cast(link.File.Coff)) |coff_file| {
4292 const got_addr = coff_file.offset_table_virtual_address + decl.link.coff.offset_table_index * ptr_bytes;4321 const got_addr = coff_file.offset_table_virtual_address + decl.link.coff.offset_table_index * ptr_bytes;
4293 return MCValue{ .memory = got_addr };4322 return MCValue{ .memory = got_addr };