Skip to content

stage2: Hello, Silicon!\n #7231

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 37 commits into from
Nov 27, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
72310db
stage2 MachO: add min OS version load cmd
kubkon Nov 12, 2020
1bec531
stage2 MachO: add source version load cmd
kubkon Nov 12, 2020
7dd5ce2
stage2 macOS: make exe flagged as pie
kubkon Nov 13, 2020
b3fdfe5
stage2 MachO: clean up segment protection flags
kubkon Nov 13, 2020
6ac7e99
Write local symbols when flushing
kubkon Nov 16, 2020
7a40724
stage2 MachO: remove discontinuities between segments
kubkon Nov 16, 2020
4a3d757
stage2 MachO: reduce size of __TEXT segment
kubkon Nov 16, 2020
8450e6f
stage2 macho: pages need to be 16kb aligned!
kubkon Nov 16, 2020
4e3520a
stage2 macho: make page size target cpu arch dependent
kubkon Nov 17, 2020
2972f63
stage2 macho: start reverting some tweaks
kubkon Nov 17, 2020
e8dd62a
stage2 macho: fix incorrect rebase
kubkon Nov 17, 2020
be0d557
stage2 macho: revert required alignment always at 4
kubkon Nov 17, 2020
e1b65ff
stage2 macho: cleanup minimum version command
kubkon Nov 17, 2020
a2e0e33
stage2 macho: bring back incremental symbol commits
kubkon Nov 17, 2020
bbc4ee3
stage2 macho: refactor
kubkon Nov 17, 2020
403dc50
stage2 macho: preallocate empty code sig space
kubkon Nov 19, 2020
2bd963a
stage2 macho: don't pad out holes between sections
kubkon Nov 19, 2020
3ac8046
stage2 macho: write out constants in CS
kubkon Nov 19, 2020
d14cd59
stage2 macho: move code signature logic into struct
kubkon Nov 20, 2020
79381dc
stage2 macho: add empty CodeDirectory blob
kubkon Nov 20, 2020
9dcf7ee
stage2 macho: add info about __TEXT segment
kubkon Nov 20, 2020
a6e93da
stage2 macho: generate a code sig (not valid yet)
kubkon Nov 20, 2020
cd79c6d
stage2 macho: fix issues with codesigning
kubkon Nov 20, 2020
e7db372
stage2 macho: cleanup logs
kubkon Nov 20, 2020
59fe3d4
stage2 macho: make file structure compatible with codesign tool
kubkon Nov 22, 2020
80b1041
stage2 macho: use RIP-relative for memory-set regs x86_64
kubkon Nov 23, 2020
ef5132c
stage2 macho: first, rough draft at trampolining
kubkon Nov 24, 2020
2cd84b1
stage2 macho: refactor PIE generation on x86_64
kubkon Nov 24, 2020
10942e3
stage2 macho: Hello, Silicon!
kubkon Nov 25, 2020
c749b78
stage2 macho: add orr and orn instructions
kubkon Nov 25, 2020
64eae8f
stage2 macho: move PIE fixups to link file; fix tests
kubkon Nov 25, 2020
7e8f7da
stage2 macho: rename inodes to prevent SIGKILL
kubkon Nov 26, 2020
f125c4f
stage2 macho: enable end-to-end incremental linking tests on aarch64
kubkon Nov 26, 2020
a8f8d82
stage2 macho: use Dir.copyFile instead of manual create+copy
kubkon Nov 26, 2020
ebb2f20
stage2 macho: Dir.copyFile does the rename for us!
kubkon Nov 26, 2020
02baaac
Update src/codegen.zig
kubkon Nov 27, 2020
5ed7626
stage2 macho: apply more review comments
kubkon Nov 27, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 21 additions & 11 deletions lib/std/macho.zig
Original file line number Diff line number Diff line change
Expand Up @@ -1384,10 +1384,10 @@ pub const CSTYPE_INDEX_REQUIREMENTS: u32 = 0x00000002;
/// Compat with amfi
pub const CSTYPE_INDEX_ENTITLEMENTS: u32 = 0x00000005;

pub const CS_HASHTYPE_SHA1: u32 = 1;
pub const CS_HASHTYPE_SHA256: u32 = 2;
pub const CS_HASHTYPE_SHA256_TRUNCATED: u32 = 3;
pub const CS_HASHTYPE_SHA384: u32 = 4;
pub const CS_HASHTYPE_SHA1: u8 = 1;
pub const CS_HASHTYPE_SHA256: u8 = 2;
pub const CS_HASHTYPE_SHA256_TRUNCATED: u8 = 3;
pub const CS_HASHTYPE_SHA384: u8 = 4;

pub const CS_SHA1_LEN: u32 = 20;
pub const CS_SHA256_LEN: u32 = 32;
Expand All @@ -1402,6 +1402,10 @@ pub const CS_SIGNER_TYPE_UNKNOWN: u32 = 0;
pub const CS_SIGNER_TYPE_LEGACYVPN: u32 = 5;
pub const CS_SIGNER_TYPE_MAC_APP_STORE: u32 = 6;

pub const CS_ADHOC: u32 = 0x2;

pub const CS_EXECSEG_MAIN_BINARY: u32 = 0x1;

/// This CodeDirectory is tailored specfically at version 0x20400.
pub const CodeDirectory = extern struct {
/// Magic number (CSMAGIC_CODEDIRECTORY)
Expand Down Expand Up @@ -1446,16 +1450,26 @@ pub const CodeDirectory = extern struct {
/// Unused (must be zero)
spare2: u32,

///
scatterOffset: u32,

///
teamOffset: u32,

///
spare3: u32,

///
codeLimit64: u64,

/// Offset of executable segment
execSegBase: u64,

/// Limit of executable segment
execSegLimit: u64,

/// Executable segment flags
execSegFlags,

// end_withExecSeg: [*]u8,
execSegFlags: u64,
};

/// Structure of an embedded-signature SuperBlob
Expand All @@ -1478,8 +1492,6 @@ pub const SuperBlob = extern struct {

/// Number of index BlobIndex entries following this struct
count: u32,

// index: []const BlobIndex,
};

pub const GenericBlob = extern struct {
Expand All @@ -1488,6 +1500,4 @@ pub const GenericBlob = extern struct {

/// Total length of blob
length: u32,

// data: []const u8,
};
143 changes: 135 additions & 8 deletions src/codegen.zig
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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.
Comment on lines 2586 to -2589
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The doc comments for the memory tag of the MCValue enum says:

            /// 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.

I don't think it makes sense to put the logic here - instead of making it work differently for PIE targets and violating the doc comments for this enum tag, the MCValue should be populated with a different tag other than memory when doing PIE, at the location that sets the value. You can introduce a new MCValue tag with a special meaning if you need to.

I'd be ok with making this review comment a follow-up issue.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I totally agree with you that this should really be a different tag, I just didn't wanna change too many things at once before we get something working on the Silicon. I'll be more than happy to work on this in a subsequent PR(s), as I'd love to generalise and clean it up!

// 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}),
},
Expand Down Expand Up @@ -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.
Expand Down
89 changes: 89 additions & 0 deletions src/codegen/aarch64.zig
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ pub const Register = enum(u6) {
w16, w17, w18, w19, w20, w21, w22, w23,
w24, w25, w26, w27, w28, w29, w30, wzr,

pub const sp = .xzr;

pub fn id(self: Register) u5 {
return @truncate(u5, @enumToInt(self));
}
Expand Down Expand Up @@ -195,6 +197,17 @@ test "FloatingPointRegister.toX" {

/// Represents an instruction in the AArch64 instruction set
pub const Instruction = union(enum) {
OrShiftedRegister: packed struct {
rd: u5,
rn: u5,
imm6: u6,
rm: u5,
n: u1,
shift: u2,
fixed: u5 = 0b01010,
opc: u2 = 0b01,
sf: u1,
},
MoveWideImmediate: packed struct {
rd: u5,
imm16: u16,
Expand Down Expand Up @@ -251,6 +264,7 @@ pub const Instruction = union(enum) {

pub fn toU32(self: Instruction) u32 {
return switch (self) {
.OrShiftedRegister => |v| @bitCast(u32, v),
.MoveWideImmediate => |v| @bitCast(u32, v),
.PCRelativeAddress => |v| @bitCast(u32, v),
.LoadStoreRegister => |v| @bitCast(u32, v),
Expand Down Expand Up @@ -379,8 +393,65 @@ pub const Instruction = union(enum) {
}
};

pub const RegisterShift = struct {
rn: u5,
imm6: u6,
shift: enum(u2) {
Lsl = 0,
Lsr = 1,
Asr = 2,
Ror = 3,
},

pub fn none() RegisterShift {
return .{
.rn = 0b11111,
.imm6 = 0,
.shift = .Lsl,
};
}
};

// Helper functions for assembly syntax functions

fn orShiftedRegister(
rd: Register,
rm: Register,
shift: RegisterShift,
invert: bool,
) Instruction {
const n: u1 = if (invert) 1 else 0;
switch (rd.size()) {
32 => {
return Instruction{
.OrShiftedRegister = .{
.rd = rd.id(),
.rn = shift.rn,
.imm6 = shift.imm6,
.rm = rm.id(),
.n = n,
.shift = @enumToInt(shift.shift),
.sf = 0,
},
};
},
64 => {
return Instruction{
.OrShiftedRegister = .{
.rd = rd.id(),
.rn = shift.rn,
.imm6 = shift.imm6,
.rm = rm.id(),
.n = n,
.shift = @enumToInt(shift.shift),
.sf = 1,
},
};
},
else => unreachable, // unexpected register size
}
}

fn moveWideImmediate(
opc: u2,
rd: Register,
Expand Down Expand Up @@ -543,6 +614,16 @@ pub const Instruction = union(enum) {
};
}

// Bitwise (inclusive) OR of a register value

pub fn orr(rd: Register, rm: Register, shift: RegisterShift) Instruction {
return orShiftedRegister(rd, rm, shift, false);
}

pub fn orn(rd: Register, rm: Register, shift: RegisterShift) Instruction {
return orShiftedRegister(rd, rm, shift, true);
}

// Move wide (immediate)

pub fn movn(rd: Register, imm16: u16, shift: u6) Instruction {
Expand Down Expand Up @@ -653,6 +734,14 @@ test "serialize instructions" {
};

const testcases = [_]Testcase{
.{ // orr x0 x1
.inst = Instruction.orr(.x0, .x1, Instruction.RegisterShift.none()),
.expected = 0b1_01_01010_00_0_00001_000000_11111_00000,
},
.{ // orn x0 x1
.inst = Instruction.orn(.x0, .x1, Instruction.RegisterShift.none()),
.expected = 0b1_01_01010_00_1_00001_000000_11111_00000,
},
.{ // movz x1 #4
.inst = Instruction.movz(.x1, 4, 0),
.expected = 0b1_10_100101_00_0000000000000100_00001,
Expand Down
Loading