From 798162e5095aff130395ee542c0c0948f95180c9 Mon Sep 17 00:00:00 2001 From: Jacob G-W Date: Tue, 1 Jun 2021 22:48:20 -0400 Subject: plan9 linker: make runnable binaries We can now run binaries! (they segfault, but still run!) --- src/codegen.zig | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) (limited to 'src/codegen.zig') diff --git a/src/codegen.zig b/src/codegen.zig index 6b9bd633d0..61607407d3 100644 --- a/src/codegen.zig +++ b/src/codegen.zig @@ -2556,9 +2556,19 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { } else { return self.fail(inst.base.src, "TODO implement calling runtime known function pointer", .{}); } - } else { - unreachable; - } + } else if (self.bin_file.cast(link.File.Plan9)) |p9| { + if (inst.func.value()) |func_value| { + if (func_value.castTag(.function)) |func_payload| { + try p9.addCallReloc(self.code, .{ + .caller = p9.cur_decl, + .callee = func_payload.data.owner_decl, + .offset_in_caller = self.code.items.len, + }); + } else return self.fail(inst.base.src, "TODO implement calling extern fn on plan9", .{}); + } else { + return self.fail(inst.base.src, "TODO implement calling runtime known function pointer", .{}); + } + } else unreachable; switch (info.return_value) { .register => |reg| { -- cgit v1.2.3 From 3e59c1502556493b29171c2d22447dd70670ca02 Mon Sep 17 00:00:00 2001 From: Jacob G-W Date: Tue, 8 Jun 2021 20:12:18 -0400 Subject: plan9 linker: do relocations --- src/codegen.zig | 60 +++++++++++++++++++++++++++++++++++++++++++++--------- src/link/Plan9.zig | 57 ++++++++++++++++++++++++++++++++++++++------------- 2 files changed, 93 insertions(+), 24 deletions(-) (limited to 'src/codegen.zig') diff --git a/src/codegen.zig b/src/codegen.zig index 61607407d3..b70c06c7d6 100644 --- a/src/codegen.zig +++ b/src/codegen.zig @@ -2557,16 +2557,56 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { return self.fail(inst.base.src, "TODO implement calling runtime known function pointer", .{}); } } else if (self.bin_file.cast(link.File.Plan9)) |p9| { - if (inst.func.value()) |func_value| { - if (func_value.castTag(.function)) |func_payload| { - try p9.addCallReloc(self.code, .{ - .caller = p9.cur_decl, - .callee = func_payload.data.owner_decl, - .offset_in_caller = self.code.items.len, - }); - } else return self.fail(inst.base.src, "TODO implement calling extern fn on plan9", .{}); - } else { - return self.fail(inst.base.src, "TODO implement calling runtime known function pointer", .{}); + switch (arch) { + .x86_64 => { + for (info.args) |mc_arg, arg_i| { + const arg = inst.args[arg_i]; + const arg_mcv = try self.resolveInst(inst.args[arg_i]); + // Here we do not use setRegOrMem even though the logic is similar, because + // the function call will move the stack pointer, so the offsets are different. + switch (mc_arg) { + .none => continue, + .register => |reg| { + try self.register_manager.getReg(reg, null); + try self.genSetReg(arg.src, arg.ty, reg, arg_mcv); + }, + .stack_offset => { + // Here we need to emit instructions like this: + // mov qword ptr [rsp + stack_offset], x + return self.fail(inst.base.src, "TODO implement calling with parameters in memory", .{}); + }, + .ptr_stack_offset => { + return self.fail(inst.base.src, "TODO implement calling with MCValue.ptr_stack_offset arg", .{}); + }, + .ptr_embedded_in_code => { + return self.fail(inst.base.src, "TODO implement calling with MCValue.ptr_embedded_in_code arg", .{}); + }, + .undef => unreachable, + .immediate => unreachable, + .unreach => unreachable, + .dead => unreachable, + .embedded_in_code => unreachable, + .memory => unreachable, + .compare_flags_signed => unreachable, + .compare_flags_unsigned => unreachable, + } + } + if (inst.func.value()) |func_value| { + if (func_value.castTag(.function)) |func_payload| { + try self.genSetReg(inst.base.src, Type.initTag(.u64), .rax, .{ .immediate = 0xdeadbeefdeadbeef }); + try p9.addCallReloc(self.code, .{ + .caller = p9.cur_decl, + .callee = func_payload.data.owner_decl, + .offset_in_caller = self.code.items.len, + }); + // call rax + try self.code.appendSlice(&.{ 0xff, 0xd0 }); + } else return self.fail(inst.base.src, "TODO implement calling extern fn on plan9", .{}); + } else { + return self.fail(inst.base.src, "TODO implement calling runtime known function pointer", .{}); + } + }, + else => return self.fail(inst.base.src, "TODO implement call on plan9 for {}", .{self.target.cpu.arch}), } } else unreachable; diff --git a/src/link/Plan9.zig b/src/link/Plan9.zig index 35c0982699..5532e056fc 100644 --- a/src/link/Plan9.zig +++ b/src/link/Plan9.zig @@ -39,11 +39,12 @@ pub const DeclBlock = struct { type: enum { text, data }, // offset in the text or data sects offset: u32, - sym_index: usize, + // offset into syms + sym_index: ?usize, pub const empty = DeclBlock{ .type = .text, .offset = 0, - .sym_index = 0, + .sym_index = null, }; }; @@ -98,6 +99,8 @@ pub fn flushModule(self: *Plan9, comp: *Compilation) !void { const tracy = trace(@src()); defer tracy.end(); + defer assert(self.hdr.entry != 0x0); + const module = self.base.options.module orelse return error.LinkingWithoutZigSourceUnimplemented; self.text_buf.items.len = 0; @@ -120,7 +123,7 @@ pub fn flushModule(self: *Plan9, comp: *Compilation) !void { .type = .data, .sym_index = decl.link.plan9.sym_index, }; - if (decl.link.plan9.sym_index == 0) { + if (decl.link.plan9.sym_index == null) { try self.syms.append(self.base.allocator, .{ .value = decl.link.plan9.offset, .type = switch (decl.link.plan9.type) { @@ -131,7 +134,7 @@ pub fn flushModule(self: *Plan9, comp: *Compilation) !void { }); decl.link.plan9.sym_index = self.syms.items.len - 1; } else { - self.syms.items[decl.link.plan9.sym_index] = .{ + self.syms.items[decl.link.plan9.sym_index.?] = .{ .value = decl.link.plan9.offset, .type = switch (decl.link.plan9.type) { .text => .t, @@ -162,18 +165,40 @@ pub fn flushModule(self: *Plan9, comp: *Compilation) !void { } } + // Do relocations. + { + for (self.call_relocs.items) |reloc| { + const l: DeclBlock = reloc.caller.link.plan9; + assert(l.sym_index != null); // we didn't process it already + const endian = self.base.options.target.cpu.arch.endian(); + if (self.ptr_width == .p32) { + const callee_offset = @truncate(u32, reloc.callee.link.plan9.offset + default_base_addr); // TODO this is different if its data + const off = reloc.offset_in_caller + l.offset; + std.mem.writeInt(u32, self.text_buf.items[off - 4 ..][0..4], callee_offset, endian); + } else { + // what we are writing + const callee_offset = reloc.callee.link.plan9.offset + default_base_addr; // TODO this is different if its data + const off = reloc.offset_in_caller + l.offset; + std.mem.writeInt(u64, self.text_buf.items[off - 8 ..][0..8], callee_offset, endian); + } + } + } + var sym_buf = std.ArrayList(u8).init(self.base.allocator); defer sym_buf.deinit(); try self.writeSyms(&sym_buf); // generate the header - self.hdr.magic = try aout.magicFromArch(self.base.options.target.cpu.arch); - self.hdr.text = @intCast(u32, self.text_buf.items.len); - self.hdr.data = @intCast(u32, self.data_buf.items.len); - self.hdr.syms = @intCast(u32, sym_buf.items.len); - self.hdr.bss = 0; - self.hdr.pcsz = 0; - self.hdr.spsz = 0; + self.hdr = .{ + .magic = try aout.magicFromArch(self.base.options.target.cpu.arch), + .text = @intCast(u32, self.text_buf.items.len), + .data = @intCast(u32, self.data_buf.items.len), + .syms = @intCast(u32, sym_buf.items.len), + .bss = 0, + .pcsz = 0, + .spsz = 0, + .entry = self.hdr.entry, + }; const file = self.base.file.?; @@ -262,22 +287,26 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio const self = try createEmpty(allocator, options); errdefer self.base.destroy(); + if (std.builtin.mode == .Debug or std.builtin.mode == .ReleaseSafe) + self.hdr.entry = 0x0; + self.base.file = file; return self; } +// tells its future self to write the addr of the callee decl into offset_in_caller. +// writes it to the {4, 8} bytes before offset_in_caller pub fn addCallReloc(self: *Plan9, code: *std.ArrayList(u8), reloc: CallReloc) !void { try self.call_relocs.append(self.base.allocator, reloc); - try code.writer().writeIntBig(u64, 0xdeadbeef); } pub fn writeSyms(self: *Plan9, buf: *std.ArrayList(u8)) !void { const writer = buf.writer(); for (self.syms.items) |sym| { if (self.ptr_width == .p32) { - try writer.writeIntBig(u32, @intCast(u32, sym.value)); + try writer.writeIntBig(u32, @intCast(u32, sym.value) + default_base_addr); } else { - try writer.writeIntBig(u64, sym.value); + try writer.writeIntBig(u64, sym.value + default_base_addr); } try writer.writeByte(@enumToInt(sym.type)); try writer.writeAll(std.mem.span(sym.name)); -- cgit v1.2.3 From db2d5b49c693b8a38743232b83a6ee1eab8e6468 Mon Sep 17 00:00:00 2001 From: Jacob G-W Date: Sun, 13 Jun 2021 21:29:32 -0400 Subject: plan9 linker: use a global offset table this simplifies stuff and makes us not have to use relocations --- src/codegen.zig | 21 ++++--- src/link.zig | 2 +- src/link/Plan9.zig | 168 ++++++++++++++++++++++++++++------------------------- 3 files changed, 104 insertions(+), 87 deletions(-) (limited to 'src/codegen.zig') diff --git a/src/codegen.zig b/src/codegen.zig index b70c06c7d6..87f287b674 100644 --- a/src/codegen.zig +++ b/src/codegen.zig @@ -2593,14 +2593,15 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { } if (inst.func.value()) |func_value| { if (func_value.castTag(.function)) |func_payload| { - try self.genSetReg(inst.base.src, Type.initTag(.u64), .rax, .{ .immediate = 0xdeadbeefdeadbeef }); - try p9.addCallReloc(self.code, .{ - .caller = p9.cur_decl, - .callee = func_payload.data.owner_decl, - .offset_in_caller = self.code.items.len, - }); - // call rax - try self.code.appendSlice(&.{ 0xff, 0xd0 }); + const ptr_bits = self.target.cpu.arch.ptrBitWidth(); + const ptr_bytes: u64 = @divExact(ptr_bits, 8); + const got_addr = p9.bases.data; + const got_index = func_payload.data.owner_decl.link.plan9.got_index.?; + // ff 14 25 xx xx xx xx call [addr] + try self.code.ensureCapacity(self.code.items.len + 7); + self.code.appendSliceAssumeCapacity(&[3]u8{ 0xff, 0x14, 0x25 }); + const fn_got_addr = got_addr + got_index * ptr_bytes; + mem.writeIntLittle(u32, self.code.addManyAsArrayAssumeCapacity(4), @intCast(u32, fn_got_addr)); } else return self.fail(inst.base.src, "TODO implement calling extern fn on plan9", .{}); } else { return self.fail(inst.base.src, "TODO implement calling runtime known function pointer", .{}); @@ -4273,6 +4274,10 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { const decl = payload.data; const got_addr = coff_file.offset_table_virtual_address + decl.link.coff.offset_table_index * ptr_bytes; return MCValue{ .memory = got_addr }; + } else if (self.bin_file.cast(link.File.Plan9)) |p9| { + const decl = payload.data; + const got_addr = p9.bases.data + decl.link.plan9.got_index.? * ptr_bytes; + return MCValue{ .memory = got_addr }; } else { return self.fail(src, "TODO codegen non-ELF const Decl pointer", .{}); } diff --git a/src/link.zig b/src/link.zig index 9447397428..2c1f048f63 100644 --- a/src/link.zig +++ b/src/link.zig @@ -348,7 +348,7 @@ pub const File = struct { .macho => return @fieldParentPtr(MachO, "base", base).allocateDeclIndexes(decl), .c => return @fieldParentPtr(C, "base", base).allocateDeclIndexes(decl), .wasm => return @fieldParentPtr(Wasm, "base", base).allocateDeclIndexes(decl), - .plan9 => {}, + .plan9 => return @fieldParentPtr(Plan9, "base", base).allocateDeclIndexes(decl), .spirv => {}, } } diff --git a/src/link/Plan9.zig b/src/link/Plan9.zig index 96ab18bd5c..5f30ee30a8 100644 --- a/src/link/Plan9.zig +++ b/src/link/Plan9.zig @@ -24,13 +24,19 @@ bases: Bases, decl_table: std.AutoArrayHashMapUnmanaged(*Module.Decl, void) = .{}, /// is just casted down when 32 bit syms: std.ArrayListUnmanaged(aout.Sym) = .{}, -call_relocs: std.ArrayListUnmanaged(CallReloc) = .{}, text_buf: std.ArrayListUnmanaged(u8) = .{}, data_buf: std.ArrayListUnmanaged(u8) = .{}, -cur_decl: *Module.Decl = undefined, hdr: aout.ExecHdr = undefined, -const Bases = struct { text: u32, data: u32 }; + +entry_decl: ?*Module.Decl = null, + +got: std.ArrayListUnmanaged(u64) = .{}, +const Bases = struct { + text: u32, + /// the addr of the got + data: u32, +}; fn getAddr(self: Plan9, addr: u32, t: aout.SymType) u32 { return addr + switch (t) { @@ -60,14 +66,17 @@ fn headerSize(self: Plan9) u32 { pub const DeclBlock = struct { type: aout.SymType, - // offset in the text or data sects - offset: u32, - // offset into syms + /// offset in the text or data sects + offset: ?u32, + /// offset into syms sym_index: ?usize, + /// offset into got + got_index: ?usize, pub const empty = DeclBlock{ .type = .t, - .offset = 0, + .offset = null, .sym_index = null, + .got_index = null, }; }; @@ -87,12 +96,6 @@ pub fn defaultBaseAddrs(arch: std.Target.Cpu.Arch) Bases { }; } -pub const CallReloc = struct { - caller: *Module.Decl, - callee: *Module.Decl, - offset_in_caller: usize, -}; - pub const PtrWidth = enum { p32, p64 }; pub fn createEmpty(gpa: *Allocator, options: link.Options) !*Plan9 { @@ -132,48 +135,89 @@ pub fn flush(self: *Plan9, comp: *Compilation) !void { } return self.flushModule(comp); } + pub fn flushModule(self: *Plan9, comp: *Compilation) !void { const tracy = trace(@src()); defer tracy.end(); + log.debug("flushModule", .{}); + defer assert(self.hdr.entry != 0x0); const module = self.base.options.module orelse return error.LinkingWithoutZigSourceUnimplemented; self.text_buf.items.len = 0; self.data_buf.items.len = 0; - self.call_relocs.items.len = 0; + // ensure space to write the got later + assert(self.got.items.len == self.decl_table.count()); + try self.data_buf.appendNTimes(self.base.allocator, 0x69, self.got.items.len * if (self.ptr_width == .p32) @as(u32, 4) else 8); // temporary buffer var code_buffer = std.ArrayList(u8).init(self.base.allocator); defer code_buffer.deinit(); { for (self.decl_table.keys()) |decl| { if (!decl.has_tv) continue; - self.cur_decl = decl; const is_fn = (decl.ty.zigTypeTag() == .Fn); + + log.debug("update the symbol table and got for decl {*} ({s})", .{ decl, decl.name }); decl.link.plan9 = if (is_fn) .{ .offset = self.getAddr(@intCast(u32, self.text_buf.items.len), .t), .type = .t, .sym_index = decl.link.plan9.sym_index, + .got_index = decl.link.plan9.got_index, } else .{ .offset = self.getAddr(@intCast(u32, self.data_buf.items.len), .d), - .type = .t, + .type = .d, .sym_index = decl.link.plan9.sym_index, + .got_index = decl.link.plan9.got_index, }; - if (decl.link.plan9.sym_index == null) { - try self.syms.append(self.base.allocator, .{ - .value = decl.link.plan9.offset, + self.got.items[decl.link.plan9.got_index.?] = decl.link.plan9.offset.?; + if (decl.link.plan9.sym_index) |s| { + self.syms.items[s] = .{ + .value = decl.link.plan9.offset.?, .type = decl.link.plan9.type, .name = mem.span(decl.name), - }); - decl.link.plan9.sym_index = self.syms.items.len - 1; + }; } else { - self.syms.items[decl.link.plan9.sym_index.?] = .{ - .value = decl.link.plan9.offset, + try self.syms.append(self.base.allocator, .{ + .value = decl.link.plan9.offset.?, .type = decl.link.plan9.type, .name = mem.span(decl.name), - }; + }); + decl.link.plan9.sym_index = self.syms.items.len - 1; + } + + if (module.decl_exports.get(decl)) |exports| { + for (exports) |exp| { + // plan9 does not support custom sections + if (exp.options.section) |section_name| { + if (!mem.eql(u8, section_name, ".text") or !mem.eql(u8, section_name, ".data")) { + try module.failed_exports.put(module.gpa, exp, try Module.ErrorMsg.create(self.base.allocator, decl.srcLoc(), "plan9 does not support extra sections", .{})); + break; + } + } + if (std.mem.eql(u8, exp.options.name, "_start")) { + std.debug.assert(decl.link.plan9.type == .t); // we tried to link a non-function as the entry + self.entry_decl = decl; + } + if (exp.link.plan9) |i| { + self.syms.items[i] = .{ + .value = decl.link.plan9.offset.?, + .type = decl.link.plan9.type.toGlobal(), + .name = exp.options.name, + }; + } else { + try self.syms.append(self.base.allocator, .{ + .value = decl.link.plan9.offset.?, + .type = decl.link.plan9.type.toGlobal(), + .name = exp.options.name, + }); + exp.link.plan9 = self.syms.items.len - 1; + } + } } + + log.debug("codegen decl {*} ({s})", .{ decl, decl.name }); const res = try codegen.generateSymbol(&self.base, decl.srcLoc(), .{ .ty = decl.ty, .val = decl.val, @@ -190,32 +234,30 @@ pub fn flushModule(self: *Plan9, comp: *Compilation) !void { }; if (is_fn) { try self.text_buf.appendSlice(self.base.allocator, code); - try code_buffer.resize(0); + code_buffer.items.len = 0; } else { try self.data_buf.appendSlice(self.base.allocator, code); - try code_buffer.resize(0); + code_buffer.items.len = 0; } } } - // Do relocations. - { - for (self.call_relocs.items) |reloc| { - const l: DeclBlock = reloc.caller.link.plan9; - assert(l.sym_index != null); // we didn't process it already - const endian = self.base.options.target.cpu.arch.endian(); - if (self.ptr_width == .p32) { - const callee_offset = @intCast(u32, reloc.callee.link.plan9.offset); - const off = self.takeAddr(@intCast(u32, reloc.offset_in_caller) + l.offset, reloc.caller.link.plan9.type); - std.mem.writeInt(u32, self.text_buf.items[off - 4 ..][0..4], callee_offset, endian); - } else { - const callee_offset = reloc.callee.link.plan9.offset; - const off = self.takeAddr(@intCast(u32, reloc.offset_in_caller) + l.offset, reloc.caller.link.plan9.type); - std.mem.writeInt(u64, self.text_buf.items[off - 8 ..][0..8], callee_offset, endian); - } + // write the got + if (self.ptr_width == .p32) { + for (self.got.items) |p, i| { + mem.writeInt(u32, self.data_buf.items[i * 4 ..][0..4], @intCast(u32, p), self.base.options.target.cpu.arch.endian()); + } + } else { + for (self.got.items) |p, i| { + mem.writeInt(u64, self.data_buf.items[i * 8 ..][0..8], p, self.base.options.target.cpu.arch.endian()); } } + if (self.entry_decl == null) { + @panic("TODO we didn't have _start"); + } + self.hdr.entry = self.entry_decl.?.link.plan9.offset.?; + // edata, end, etext self.syms.items[0].value = self.getAddr(0x0, .b); // what is this number self.syms.items[1].value = self.getAddr(0x0, .b); // what is this number @@ -267,43 +309,14 @@ pub fn updateDeclExports( decl: *Module.Decl, exports: []const *Module.Export, ) !void { - for (exports) |exp| { - if (exp.options.section) |section_name| { - if (!mem.eql(u8, section_name, ".text")) { - try module.failed_exports.ensureCapacity(module.gpa, module.failed_exports.count() + 1); - module.failed_exports.putAssumeCapacityNoClobber( - exp, - try Module.ErrorMsg.create(self.base.allocator, decl.srcLoc(), "plan9 does not support extra sections", .{}), - ); - continue; - } - } - if (std.mem.eql(u8, exp.options.name, "_start")) { - std.debug.assert(decl.link.plan9.type == .t); // we tried to link a non-function as _start - self.hdr.entry = self.bases.text + decl.link.plan9.offset; - } - if (exp.link.plan9) |i| { - self.syms.items[i] = .{ - .value = self.getAddr(decl.link.plan9.offset, decl.link.plan9.type), - .type = decl.link.plan9.type.toGlobal(), - .name = exp.options.name, - }; - } else { - try self.syms.append(self.base.allocator, .{ - .value = self.getAddr(decl.link.plan9.offset, decl.link.plan9.type), - .type = decl.link.plan9.type.toGlobal(), - .name = exp.options.name, - }); - exp.link.plan9 = self.syms.items.len - 1; - } - } + // we do all the things in flush } pub fn deinit(self: *Plan9) void { self.decl_table.deinit(self.base.allocator); - self.call_relocs.deinit(self.base.allocator); self.syms.deinit(self.base.allocator); self.text_buf.deinit(self.base.allocator); self.data_buf.deinit(self.base.allocator); + self.got.deinit(self.base.allocator); } pub const Export = ?usize; @@ -350,12 +363,6 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio return self; } -// tells its future self to write the addr of the callee decl into offset_in_caller. -// writes it to the {4, 8} bytes before offset_in_caller -pub fn addCallReloc(self: *Plan9, code: *std.ArrayList(u8), reloc: CallReloc) !void { - try self.call_relocs.append(self.base.allocator, reloc); -} - pub fn writeSyms(self: *Plan9, buf: *std.ArrayList(u8)) !void { const writer = buf.writer(); for (self.syms.items) |sym| { @@ -369,3 +376,8 @@ pub fn writeSyms(self: *Plan9, buf: *std.ArrayList(u8)) !void { try writer.writeByte(0); } } + +pub fn allocateDeclIndexes(self: *Plan9, decl: *Module.Decl) !void { + try self.got.append(self.base.allocator, 0xdeadbeef); + decl.link.plan9.got_index = self.got.items.len - 1; +} -- cgit v1.2.3 From 1c2facaf6b670863edf30d7c9d1d47368c4a90cc Mon Sep 17 00:00:00 2001 From: Jacob G-W Date: Mon, 21 Jun 2021 13:10:31 -0400 Subject: stage2: include enough inline asm support for more plan9 syscalls Also make the exit more correct by exiting 0 rather than random stuff on the stack. --- lib/std/start.zig | 8 ++++++-- src/codegen.zig | 42 ++++++++++++++++++++++++++++++++++++++---- 2 files changed, 44 insertions(+), 6 deletions(-) (limited to 'src/codegen.zig') diff --git a/lib/std/start.zig b/lib/std/start.zig index 608b3997f1..dbbab433d0 100644 --- a/lib/std/start.zig +++ b/lib/std/start.zig @@ -123,11 +123,15 @@ fn exit2(code: usize) noreturn { }, else => @compileError("TODO"), }, + // exits(0) .plan9 => switch (builtin.stage2_arch) { .x86_64 => { - asm volatile ("syscall" + asm volatile ( + \\push $0 + \\push $0 + \\syscall : - : [number] "{rbp}" (8) + : [syscall_number] "{rbp}" (8) : "rcx", "r11", "memory" ); }, diff --git a/src/codegen.zig b/src/codegen.zig index 87f287b674..96a24195ee 100644 --- a/src/codegen.zig +++ b/src/codegen.zig @@ -3330,10 +3330,44 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { try self.genSetReg(inst.base.src, arg.ty, reg, arg_mcv); } - if (mem.eql(u8, inst.asm_source, "syscall")) { - try self.code.appendSlice(&[_]u8{ 0x0f, 0x05 }); - } else if (inst.asm_source.len != 0) { - return self.fail(inst.base.src, "TODO implement support for more x86 assembly instructions", .{}); + { + var iter = std.mem.tokenize(inst.asm_source, "\n\r"); + while (iter.next()) |ins| { + if (mem.eql(u8, ins, "syscall")) { + try self.code.appendSlice(&[_]u8{ 0x0f, 0x05 }); + } else if (mem.indexOf(u8, ins, "push")) |_| { + const arg = ins[4..]; + if (mem.indexOf(u8, arg, "$")) |l| { + const n = std.fmt.parseInt(u8, ins[4 + l + 1 ..], 10) catch return self.fail(inst.base.src, "TODO implement more inline asm int parsing", .{}); + try self.code.appendSlice(&.{ 0x6a, n }); + } else if (mem.indexOf(u8, arg, "%%")) |l| { + const reg_name = ins[4 + l + 2 ..]; + const reg = parseRegName(reg_name) orelse + return self.fail(inst.base.src, "unrecognized register: '{s}'", .{reg_name}); + const low_id: u8 = reg.low_id(); + if (reg.isExtended()) { + try self.code.appendSlice(&.{ 0x41, 0b1010000 | low_id }); + } else { + try self.code.append(0b1010000 | low_id); + } + } else return self.fail(inst.base.src, "TODO more push operands", .{}); + } else if (mem.indexOf(u8, ins, "pop")) |_| { + const arg = ins[3..]; + if (mem.indexOf(u8, arg, "%%")) |l| { + const reg_name = ins[3 + l + 2 ..]; + const reg = parseRegName(reg_name) orelse + return self.fail(inst.base.src, "unrecognized register: '{s}'", .{reg_name}); + const low_id: u8 = reg.low_id(); + if (reg.isExtended()) { + try self.code.appendSlice(&.{ 0x41, 0b1011000 | low_id }); + } else { + try self.code.append(0b1011000 | low_id); + } + } else return self.fail(inst.base.src, "TODO more pop operands", .{}); + } else { + return self.fail(inst.base.src, "TODO implement support for more x86 assembly instructions", .{}); + } + } } if (inst.output_constraint) |output| { -- cgit v1.2.3 From 4eb778fc3eeec62c5c45ccc0a21631ff757d8a23 Mon Sep 17 00:00:00 2001 From: Jacob G-W Date: Wed, 23 Jun 2021 23:21:02 -0400 Subject: plan9 linker: remove unused stuff --- src/codegen.zig | 2 +- src/link/Plan9.zig | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) (limited to 'src/codegen.zig') diff --git a/src/codegen.zig b/src/codegen.zig index 96a24195ee..df65b8b08e 100644 --- a/src/codegen.zig +++ b/src/codegen.zig @@ -2556,7 +2556,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { } else { return self.fail(inst.base.src, "TODO implement calling runtime known function pointer", .{}); } - } else if (self.bin_file.cast(link.File.Plan9)) |p9| { + } else if (self.bin_file.cast(link.File.Plan9)) |_| { switch (arch) { .x86_64 => { for (info.args) |mc_arg, arg_i| { diff --git a/src/link/Plan9.zig b/src/link/Plan9.zig index 1724ec446b..f12b8af6b5 100644 --- a/src/link/Plan9.zig +++ b/src/link/Plan9.zig @@ -115,6 +115,7 @@ pub fn createEmpty(gpa: *Allocator, options: link.Options) !*Plan9 { } pub fn updateDecl(self: *Plan9, module: *Module, decl: *Module.Decl) !void { + _ = module; _ = try self.decl_table.getOrPut(self.base.allocator, decl); } @@ -131,6 +132,7 @@ pub fn flush(self: *Plan9, comp: *Compilation) !void { } pub fn flushModule(self: *Plan9, comp: *Compilation) !void { + _ = comp; const tracy = trace(@src()); defer tracy.end(); @@ -301,6 +303,10 @@ pub fn updateDeclExports( exports: []const *Module.Export, ) !void { // we do all the things in flush + _ = self; + _ = module; + _ = decl; + _ = exports; } pub fn deinit(self: *Plan9) void { self.decl_table.deinit(self.base.allocator); -- cgit v1.2.3 From 476faef97ab0f292159bb3eef42078f9b1e43dde Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 8 Jul 2021 14:24:16 -0700 Subject: plan9 cleanups * rename files to adhere to conventions * remove unnecessary function / optionality * fix merge conflict * better panic message * remove unnecessary TODO comment * proper namespacing of declarations * clean up documentation comments * no copyright header needed for a brand new zig file that is not copied from anywhere --- CMakeLists.txt | 2 +- lib/std/zig.zig | 48 ++++++++--------- src/codegen.zig | 2 +- src/link.zig | 2 +- src/link/Plan9.zig | 13 ++--- src/link/Plan9/aout.zig | 114 ++++++++++++++++++++++++++++++++++++++++ src/link/plan9/a.out.zig | 134 ----------------------------------------------- 7 files changed, 148 insertions(+), 167 deletions(-) create mode 100644 src/link/Plan9/aout.zig delete mode 100644 src/link/plan9/a.out.zig (limited to 'src/codegen.zig') diff --git a/CMakeLists.txt b/CMakeLists.txt index f1b3f767e6..44417e4159 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -574,7 +574,7 @@ set(ZIG_STAGE2_SOURCES "${CMAKE_SOURCE_DIR}/src/link/Coff.zig" "${CMAKE_SOURCE_DIR}/src/link/Elf.zig" "${CMAKE_SOURCE_DIR}/src/link/Plan9.zig" - "${CMAKE_SOURCE_DIR}/src/link/plan9/a.out.zig" + "${CMAKE_SOURCE_DIR}/src/link/Plan9/aout.zig" "${CMAKE_SOURCE_DIR}/src/link/MachO.zig" "${CMAKE_SOURCE_DIR}/src/link/MachO/Archive.zig" "${CMAKE_SOURCE_DIR}/src/link/MachO/CodeSignature.zig" diff --git a/lib/std/zig.zig b/lib/std/zig.zig index 35de030985..595dce77c2 100644 --- a/lib/std/zig.zig +++ b/lib/std/zig.zig @@ -181,32 +181,32 @@ pub fn binNameAlloc(allocator: *std.mem.Allocator, options: BinNameOptions) erro .spirv => return std.fmt.allocPrint(allocator, "{s}.spv", .{root_name}), .hex => return std.fmt.allocPrint(allocator, "{s}.ihex", .{root_name}), .raw => return std.fmt.allocPrint(allocator, "{s}.bin", .{root_name}), - .plan9 => return std.fmt.allocPrint(allocator, "{s}.{c}", .{ root_name, archToPlan9Char(target.cpu.arch) }), + .plan9 => { + // copied from 2c(1) + // 0c spim little-endian MIPS 3000 family + // 1c 68000 Motorola MC68000 + // 2c 68020 Motorola MC68020 + // 5c arm little-endian ARM + // 6c amd64 AMD64 and compatibles (e.g., Intel EM64T) + // 7c arm64 ARM64 (ARMv8) + // 8c 386 Intel i386, i486, Pentium, etc. + // kc sparc Sun SPARC + // qc power Power PC + // vc mips big-endian MIPS 3000 family + const char: u8 = switch (target.cpu.arch) { + .arm => '5', + .x86_64 => '6', + .aarch64 => '7', + .i386 => '8', + .sparc => 'k', + .powerpc, .powerpcle => 'q', + .mips, .mipsel => 'v', + else => 'X', // this arch does not have a char or maybe was not ported to plan9 so we just use X + }; + return std.fmt.allocPrint(allocator, "{s}.{c}", .{ root_name, char }); + }, } } -fn archToPlan9Char(arch: std.Target.Cpu.Arch) ?u8 { - // copied from 2c(1) - // 0c spim little-endian MIPS 3000 family - // 1c 68000 Motorola MC68000 - // 2c 68020 Motorola MC68020 - // 5c arm little-endian ARM - // 6c amd64 AMD64 and compatibles (e.g., Intel EM64T) - // 7c arm64 ARM64 (ARMv8) - // 8c 386 Intel i386, i486, Pentium, etc. - // kc sparc Sun SPARC - // qc power Power PC - // vc mips big-endian MIPS 3000 family - return switch (arch) { - .arm => '5', - .x86_64 => '6', - .aarch64 => '7', - .i386 => '8', - .sparc => 'k', - .powerpc, .powerpcle => 'q', - .mips, .mipsel => 'v', - else => 'X', // this arch does not have a char or maybe was not ported to plan9 so we just use X - }; -} pub const ParsedCharLiteral = union(enum) { success: u32, diff --git a/src/codegen.zig b/src/codegen.zig index df65b8b08e..96a24195ee 100644 --- a/src/codegen.zig +++ b/src/codegen.zig @@ -2556,7 +2556,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { } else { return self.fail(inst.base.src, "TODO implement calling runtime known function pointer", .{}); } - } else if (self.bin_file.cast(link.File.Plan9)) |_| { + } else if (self.bin_file.cast(link.File.Plan9)) |p9| { switch (arch) { .x86_64 => { for (info.args) |mc_arg, arg_i| { diff --git a/src/link.zig b/src/link.zig index 2c1f048f63..61a46e8b06 100644 --- a/src/link.zig +++ b/src/link.zig @@ -333,7 +333,7 @@ pub const File = struct { .elf => return @fieldParentPtr(Elf, "base", base).updateDeclLineNumber(module, decl), .macho => return @fieldParentPtr(MachO, "base", base).updateDeclLineNumber(module, decl), .c => return @fieldParentPtr(C, "base", base).updateDeclLineNumber(module, decl), - .plan9 => @panic("PLAN 9 DEBUG INFO"), + .plan9 => @panic("TODO: implement updateDeclLineNumber for plan9"), .wasm, .spirv => {}, } } diff --git a/src/link/Plan9.zig b/src/link/Plan9.zig index f12b8af6b5..f880b40d85 100644 --- a/src/link/Plan9.zig +++ b/src/link/Plan9.zig @@ -1,10 +1,13 @@ +//! This implementation does all the linking work in flush(). A future improvement +//! would be to add incremental linking in a similar way as ELF does. + const Plan9 = @This(); const std = @import("std"); const link = @import("../link.zig"); const Module = @import("../Module.zig"); const Compilation = @import("../Compilation.zig"); -const aout = @import("plan9/a.out.zig"); +const aout = @import("Plan9/aout.zig"); const codegen = @import("../codegen.zig"); const trace = @import("../tracy.zig").trace; const mem = std.mem; @@ -14,8 +17,6 @@ const Allocator = std.mem.Allocator; const log = std.log.scoped(.link); const assert = std.debug.assert; -// TODO use incremental compilation - base: link.File, sixtyfour_bit: bool, error_flags: File.ErrorFlags = File.ErrorFlags{}, @@ -38,7 +39,7 @@ const Bases = struct { data: u64, }; -fn getAddr(self: Plan9, addr: u64, t: aout.SymType) u64 { +fn getAddr(self: Plan9, addr: u64, t: aout.Sym.Type) u64 { return addr + switch (t) { .T, .t, .l, .L => self.bases.text, .D, .d, .B, .b => self.bases.data, @@ -46,7 +47,7 @@ fn getAddr(self: Plan9, addr: u64, t: aout.SymType) u64 { }; } /// opposite of getAddr -fn takeAddr(self: Plan9, addr: u64, t: aout.SymType) u64 { +fn takeAddr(self: Plan9, addr: u64, t: aout.Sym.Type) u64 { return addr - switch (t) { .T, .t, .l, .L => self.bases.text, .D, .d, .B, .b => self.bases.data, @@ -59,7 +60,7 @@ fn getSymAddr(self: Plan9, s: aout.Sym) u64 { } pub const DeclBlock = struct { - type: aout.SymType, + type: aout.Sym.Type, /// offset in the text or data sects offset: ?u64, /// offset into syms diff --git a/src/link/Plan9/aout.zig b/src/link/Plan9/aout.zig new file mode 100644 index 0000000000..f6dff7437c --- /dev/null +++ b/src/link/Plan9/aout.zig @@ -0,0 +1,114 @@ +const std = @import("std"); +const assert = std.debug.assert; + +/// All integers are in big-endian format (needs a byteswap). +pub const ExecHdr = extern struct { + magic: u32, + text: u32, + data: u32, + bss: u32, + syms: u32, + /// You should truncate this to 32 bits on 64 bit systems, then but the actual 8 bytes + /// in the fat header. + entry: u32, + spsz: u32, + pcsz: u32, + comptime { + assert(@sizeOf(@This()) == 32); + } + /// It is up to the caller to disgard the last 8 bytes if the header is not fat. + pub fn toU8s(self: *@This()) [40]u8 { + var buf: [40]u8 = undefined; + var i: u8 = 0; + inline for (std.meta.fields(@This())) |f| { + std.mem.writeIntSliceBig(u32, buf[i .. i + 4], @field(self, f.name)); + i += 4; + } + return buf; + } +}; + +pub const Sym = struct { + /// Big endian in the file + value: u64, + type: Type, + name: []const u8, + + /// The type field is one of the following characters with the + /// high bit set: + /// T text segment symbol + /// t static text segment symbol + /// L leaf function text segment symbol + /// l static leaf function text segment symbol + /// D data segment symbol + /// d static data segment symbol + /// B bss segment symbol + /// b static bss segment symbol + /// a automatic (local) variable symbol + /// p function parameter symbol + /// f source file name components + /// z source file name + /// Z source file line offset + /// m for '.frame' + pub const Type = enum(u8) { + T = 0x80 | 'T', + t = 0x80 | 't', + L = 0x80 | 'L', + l = 0x80 | 'l', + D = 0x80 | 'D', + d = 0x80 | 'd', + B = 0x80 | 'B', + b = 0x80 | 'b', + a = 0x80 | 'a', + p = 0x80 | 'p', + f = 0x80 | 'f', + z = 0x80 | 'z', + Z = 0x80 | 'Z', + m = 0x80 | 'm', + + pub fn toGlobal(self: Type) Type { + return switch (self) { + .t => .T, + .b => .B, + .d => .D, + else => unreachable, + }; + } + }; +}; + +pub const HDR_MAGIC = 0x00008000; +pub inline fn _MAGIC(f: anytype, b: anytype) @TypeOf(f | ((((@as(c_int, 4) * b) + @as(c_int, 0)) * b) + @as(c_int, 7))) { + return f | ((((@as(c_int, 4) * b) + @as(c_int, 0)) * b) + @as(c_int, 7)); +} +pub const A_MAGIC = _MAGIC(0, 8); // 68020 +pub const I_MAGIC = _MAGIC(0, 11); // intel 386 +pub const J_MAGIC = _MAGIC(0, 12); // intel 960 (retired) +pub const K_MAGIC = _MAGIC(0, 13); // sparc +pub const V_MAGIC = _MAGIC(0, 16); // mips 3000 BE +pub const X_MAGIC = _MAGIC(0, 17); // att dsp 3210 (retired) +pub const M_MAGIC = _MAGIC(0, 18); // mips 4000 BE +pub const D_MAGIC = _MAGIC(0, 19); // amd 29000 (retired) +pub const E_MAGIC = _MAGIC(0, 20); // arm +pub const Q_MAGIC = _MAGIC(0, 21); // powerpc +pub const N_MAGIC = _MAGIC(0, 22); // mips 4000 LE +pub const L_MAGIC = _MAGIC(0, 23); // dec alpha (retired) +pub const P_MAGIC = _MAGIC(0, 24); // mips 3000 LE +pub const U_MAGIC = _MAGIC(0, 25); // sparc64 +pub const S_MAGIC = _MAGIC(HDR_MAGIC, 26); // amd64 +pub const T_MAGIC = _MAGIC(HDR_MAGIC, 27); // powerpc64 +pub const R_MAGIC = _MAGIC(HDR_MAGIC, 28); // arm64 + +pub fn magicFromArch(arch: std.Target.Cpu.Arch) !u32 { + return switch (arch) { + .i386 => I_MAGIC, + .sparc => K_MAGIC, // TODO should sparcv9 and sparcel go here? + .mips => V_MAGIC, + .arm => E_MAGIC, + .aarch64 => R_MAGIC, + .powerpc => Q_MAGIC, + .powerpc64 => T_MAGIC, + .x86_64 => S_MAGIC, + else => error.ArchNotSupportedByPlan9, + }; +} diff --git a/src/link/plan9/a.out.zig b/src/link/plan9/a.out.zig deleted file mode 100644 index 85be2d5c36..0000000000 --- a/src/link/plan9/a.out.zig +++ /dev/null @@ -1,134 +0,0 @@ -// Copyright © 2021 Plan 9 Foundation -// Copyright © 20XX 9front authors - -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -// Idomatic translation of 9front a.out.h -const std = @import("std"); -// all integers are in big-endian format (needs a byteswap) -pub const ExecHdr = extern struct { - magic: u32, - text: u32, - data: u32, - bss: u32, - syms: u32, - /// You should truncate this to 32 bits on 64 bit systems, then but the actual 8 bytes - /// in the fat header. - entry: u32, - spsz: u32, - pcsz: u32, - comptime { - std.debug.assert(@sizeOf(@This()) == 32); - } - /// it is up to the caller to disgard the last 8 bytes if the header is not fat - pub fn toU8s(self: *@This()) [40]u8 { - var buf: [40]u8 = undefined; - var i: u8 = 0; - inline for (std.meta.fields(@This())) |f| { - std.mem.writeIntSliceBig(u32, buf[i .. i + 4], @field(self, f.name)); - i += 4; - } - return buf; - } -}; - -// uchar value[8]; -// char type; -// char name[n]; /* NUL-terminated */ -pub const Sym = struct { - value: u64, // big endian in the file - type: SymType, - name: []const u8, -}; -// The type field is one of the following characters with the -// high bit set: -// T text segment symbol -// t static text segment symbol -// L leaf function text segment symbol -// l static leaf function text segment symbol -// D data segment symbol -// d static data segment symbol -// B bss segment symbol -// b static bss segment symbol -// a automatic (local) variable symbol -// p function parameter symbol -// f source file name components -// z source file name -// Z source file line offset -// m for '.frame' -pub const SymType = enum(u8) { - T = 0x80 | 'T', - t = 0x80 | 't', - L = 0x80 | 'L', - l = 0x80 | 'l', - D = 0x80 | 'D', - d = 0x80 | 'd', - B = 0x80 | 'B', - b = 0x80 | 'b', - a = 0x80 | 'a', - p = 0x80 | 'p', - f = 0x80 | 'f', - z = 0x80 | 'z', - Z = 0x80 | 'Z', - m = 0x80 | 'm', - pub fn toGlobal(self: SymType) SymType { - return switch (self) { - .t => .T, - .b => .B, - .d => .D, - else => unreachable, - }; - } -}; - -pub const HDR_MAGIC = 0x00008000; -pub inline fn _MAGIC(f: anytype, b: anytype) @TypeOf(f | ((((@as(c_int, 4) * b) + @as(c_int, 0)) * b) + @as(c_int, 7))) { - return f | ((((@as(c_int, 4) * b) + @as(c_int, 0)) * b) + @as(c_int, 7)); -} -pub const A_MAGIC = _MAGIC(0, 8); // 68020 -pub const I_MAGIC = _MAGIC(0, 11); // intel 386 -pub const J_MAGIC = _MAGIC(0, 12); // intel 960 (retired) -pub const K_MAGIC = _MAGIC(0, 13); // sparc -pub const V_MAGIC = _MAGIC(0, 16); // mips 3000 BE -pub const X_MAGIC = _MAGIC(0, 17); // att dsp 3210 (retired) -pub const M_MAGIC = _MAGIC(0, 18); // mips 4000 BE -pub const D_MAGIC = _MAGIC(0, 19); // amd 29000 (retired) -pub const E_MAGIC = _MAGIC(0, 20); // arm -pub const Q_MAGIC = _MAGIC(0, 21); // powerpc -pub const N_MAGIC = _MAGIC(0, 22); // mips 4000 LE -pub const L_MAGIC = _MAGIC(0, 23); // dec alpha (retired) -pub const P_MAGIC = _MAGIC(0, 24); // mips 3000 LE -pub const U_MAGIC = _MAGIC(0, 25); // sparc64 -pub const S_MAGIC = _MAGIC(HDR_MAGIC, 26); // amd64 -pub const T_MAGIC = _MAGIC(HDR_MAGIC, 27); // powerpc64 -pub const R_MAGIC = _MAGIC(HDR_MAGIC, 28); // arm64 - -pub fn magicFromArch(arch: std.Target.Cpu.Arch) !u32 { - return switch (arch) { - .i386 => I_MAGIC, - .sparc => K_MAGIC, // TODO should sparcv9 and sparcel go here? - .mips => V_MAGIC, - .arm => E_MAGIC, - .aarch64 => R_MAGIC, - .powerpc => Q_MAGIC, - .powerpc64 => T_MAGIC, - .x86_64 => S_MAGIC, - else => error.ArchNotSupportedByPlan9, - }; -} -- cgit v1.2.3