diff options
| author | Jakub Konka <kubkon@jakubkonka.com> | 2020-11-27 21:49:06 +0100 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2020-11-27 21:49:06 +0100 |
| commit | 3bc1c719bd632e6baccb574bf75cb93f0bdef08f (patch) | |
| tree | 288fea5f073ec13c4b435a853e40f7c1d1e90ab4 /src/codegen.zig | |
| parent | daf6c0da5b2d96530dea2d2a907f2b2ba55c8ba6 (diff) | |
| parent | 5ed76268c9f0ecca8c5d62cb7e56da05aa8f1f7c (diff) | |
| download | zig-3bc1c719bd632e6baccb574bf75cb93f0bdef08f.tar.gz zig-3bc1c719bd632e6baccb574bf75cb93f0bdef08f.zip | |
Merge pull request #7231 from kubkon/stage2-arm-macos
stage2: Hello, Silicon!\n
Diffstat (limited to 'src/codegen.zig')
| -rw-r--r-- | src/codegen.zig | 143 |
1 files changed, 135 insertions, 8 deletions
diff --git a/src/codegen.zig b/src/codegen.zig index 48fda6f0d5..74800aa22e 100644 --- a/src/codegen.zig +++ b/src/codegen.zig @@ -1683,15 +1683,13 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { const got_addr = got.addr + func.owner_decl.link.macho.offset_table_index * @sizeOf(u64); switch (arch) { .x86_64 => { - // Here, we store the got address in %rax, and then call %rax - // movabsq [addr], %rax try self.genSetReg(inst.base.src, .rax, .{ .memory = got_addr }); // callq *%rax - try self.code.ensureCapacity(self.code.items.len + 2); self.code.appendSliceAssumeCapacity(&[2]u8{ 0xff, 0xd0 }); }, .aarch64 => { try self.genSetReg(inst.base.src, .x30, .{ .memory = got_addr }); + // blr x30 writeInt(u32, try self.code.addManyAsArray(4), Instruction.blr(.x30).toU32()); }, else => unreachable, // unsupported architecture on MachO @@ -2586,10 +2584,82 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { }, .register => return self.fail(src, "TODO implement genSetReg for aarch64 {}", .{mcv}), .memory => |addr| { - // 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. - try self.genSetReg(src, reg, .{ .immediate = addr }); - mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.ldr(reg, .{ .rn = reg }).toU32()); + if (self.bin_file.options.pie) { + // For MachO, the binary, with the exception of object files, has to be a PIE. + // Therefore we cannot load an absolute address. + // Instead, we need to make use of PC-relative addressing. + // TODO This needs to be optimised in the stack usage (perhaps use a shadow stack + // like described here: + // https://community.arm.com/developer/ip-products/processors/b/processors-ip-blog/posts/using-the-stack-in-aarch64-implementing-push-and-pop) + // TODO As far as branching is concerned, instead of saving the return address + // in a register, I'm thinking here of immitating x86_64, and having the address + // passed on the stack. + if (reg.id() == 0) { // x0 is special-cased + // str x28, [sp, #-16] + mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.str(.x28, Register.sp, .{ + .offset = Instruction.Offset.imm_pre_index(-16), + }).toU32()); + // adr x28, #8 + mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.adr(.x28, 8).toU32()); + if (self.bin_file.cast(link.File.MachO)) |macho_file| { + try macho_file.pie_fixups.append(self.bin_file.allocator, .{ + .address = addr, + .start = self.code.items.len, + .len = 4, + }); + } else { + return self.fail(src, "TODO implement genSetReg for PIE on this platform", .{}); + } + // b [label] + mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.b(0).toU32()); + // mov r, x0 + mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.orr(reg, .x0, Instruction.RegisterShift.none()).toU32()); + // ldr x28, [sp], #16 + mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.ldr(.x28, .{ + .rn = Register.sp, + .offset = Instruction.Offset.imm_post_index(16), + }).toU32()); + } else { + // str x28, [sp, #-16] + mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.str(.x28, Register.sp, .{ + .offset = Instruction.Offset.imm_pre_index(-16), + }).toU32()); + // str x0, [sp, #-16] + mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.str(.x0, Register.sp, .{ + .offset = Instruction.Offset.imm_pre_index(-16), + }).toU32()); + // adr x28, #8 + mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.adr(.x28, 8).toU32()); + if (self.bin_file.cast(link.File.MachO)) |macho_file| { + try macho_file.pie_fixups.append(self.bin_file.allocator, .{ + .address = addr, + .start = self.code.items.len, + .len = 4, + }); + } else { + return self.fail(src, "TODO implement genSetReg for PIE on this platform", .{}); + } + // b [label] + mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.b(0).toU32()); + // mov r, x0 + mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.orr(reg, .x0, Instruction.RegisterShift.none()).toU32()); + // ldr x0, [sp], #16 + mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.ldr(.x0, .{ + .rn = Register.sp, + .offset = Instruction.Offset.imm_post_index(16), + }).toU32()); + // ldr x28, [sp], #16 + mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.ldr(.x28, .{ + .rn = Register.sp, + .offset = Instruction.Offset.imm_post_index(16), + }).toU32()); + } + } else { + // 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. + try self.genSetReg(src, reg, .{ .immediate = addr }); + mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.ldr(reg, .{ .rn = reg }).toU32()); + } }, else => return self.fail(src, "TODO implement genSetReg for aarch64 {}", .{mcv}), }, @@ -2766,7 +2836,64 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { self.code.appendSliceAssumeCapacity(&[_]u8{ 0x8B, R }); }, .memory => |x| { - if (x <= math.maxInt(u32)) { + if (self.bin_file.options.pie) { + // For MachO, the binary, with the exception of object files, has to be a PIE. + // Therefore, we cannot load an absolute address. + assert(x > math.maxInt(u32)); // 32bit direct addressing is not supported by MachO. + // The plan here is to use unconditional relative jump to GOT entry, where we store + // pre-calculated and stored effective address to load into the target register. + // We leave the actual displacement information empty (0-padded) and fixing it up + // later in the linker. + if (reg.id() == 0) { // %rax is special-cased + try self.code.ensureCapacity(self.code.items.len + 5); + if (self.bin_file.cast(link.File.MachO)) |macho_file| { + try macho_file.pie_fixups.append(self.bin_file.allocator, .{ + .address = x, + .start = self.code.items.len, + .len = 5, + }); + } else { + return self.fail(src, "TODO implement genSetReg for PIE on this platform", .{}); + } + // call [label] + self.code.appendSliceAssumeCapacity(&[_]u8{ + 0xE8, + 0x0, + 0x0, + 0x0, + 0x0, + }); + } else { + try self.code.ensureCapacity(self.code.items.len + 10); + // push %rax + self.code.appendSliceAssumeCapacity(&[_]u8{0x50}); + if (self.bin_file.cast(link.File.MachO)) |macho_file| { + try macho_file.pie_fixups.append(self.bin_file.allocator, .{ + .address = x, + .start = self.code.items.len, + .len = 5, + }); + } else { + return self.fail(src, "TODO implement genSetReg for PIE on this platform", .{}); + } + // call [label] + self.code.appendSliceAssumeCapacity(&[_]u8{ + 0xE8, + 0x0, + 0x0, + 0x0, + 0x0, + }); + // mov %r, %rax + self.code.appendSliceAssumeCapacity(&[_]u8{ + 0x48, + 0x89, + 0xC0 | @as(u8, reg.id()), + }); + // pop %rax + self.code.appendSliceAssumeCapacity(&[_]u8{0x58}); + } + } else if (x <= math.maxInt(u32)) { // Moving from memory to a register is a variant of `8B /r`. // Since we're using 64-bit moves, we require a REX. // This variant also requires a SIB, as it would otherwise be RIP-relative. |
