aboutsummaryrefslogtreecommitdiff
path: root/src/codegen.zig
diff options
context:
space:
mode:
authorJakub Konka <kubkon@jakubkonka.com>2020-11-27 21:49:06 +0100
committerGitHub <noreply@github.com>2020-11-27 21:49:06 +0100
commit3bc1c719bd632e6baccb574bf75cb93f0bdef08f (patch)
tree288fea5f073ec13c4b435a853e40f7c1d1e90ab4 /src/codegen.zig
parentdaf6c0da5b2d96530dea2d2a907f2b2ba55c8ba6 (diff)
parent5ed76268c9f0ecca8c5d62cb7e56da05aa8f1f7c (diff)
downloadzig-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.zig143
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.