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) { + 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), + }; + } else { try self.syms.append(self.base.allocator, .{ - .value = decl.link.plan9.offset, + .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, - .type = decl.link.plan9.type, - .name = mem.span(decl.name), - }; } + + 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; +}