diff --git a/src/link/Plan9.zig b/src/link/Plan9.zig index bc044ce414..9b123f56aa 100644 --- a/src/link/Plan9.zig +++ b/src/link/Plan9.zig @@ -25,20 +25,22 @@ sixtyfour_bit: bool, error_flags: File.ErrorFlags = File.ErrorFlags{}, bases: Bases, -decl_table: std.AutoArrayHashMapUnmanaged(*Module.Decl, void) = .{}, -/// is just casted down when 32 bit +/// A symbol's value is just casted down when compiling +/// for a 32 bit target. syms: std.ArrayListUnmanaged(aout.Sym) = .{}, -text_buf: std.ArrayListUnmanaged(u8) = .{}, -data_buf: std.ArrayListUnmanaged(u8) = .{}, + +fn_decl_table: std.AutoArrayHashMapUnmanaged(*Module.Decl, []const u8) = .{}, +data_decl_table: std.AutoArrayHashMapUnmanaged(*Module.Decl, []const u8) = .{}, hdr: aout.ExecHdr = undefined, entry_decl: ?*Module.Decl = null, -got: std.ArrayListUnmanaged(u64) = .{}, +got_len: u64 = 0, + const Bases = struct { text: u64, - /// the addr of the got + /// the Global Offset Table starts at the beginning of the data section data: u64, }; @@ -49,14 +51,6 @@ fn getAddr(self: Plan9, addr: u64, t: aout.Sym.Type) u64 { else => unreachable, }; } -/// opposite of getAddr -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, - else => unreachable, - }; -} fn getSymAddr(self: Plan9, s: aout.Sym) u64 { return self.getAddr(s.value, s.type); @@ -127,18 +121,80 @@ pub fn updateFunc(self: *Plan9, module: *Module, func: *Module.Fn, air: Air, liv if (build_options.skip_non_native and builtin.object_format != .plan9) { @panic("Attempted to compile for object format that was disabled by build configuration"); } - _ = module; - // Keep track of all decls so we can iterate over them on flush(). - _ = try self.decl_table.getOrPut(self.base.allocator, func.owner_decl); - _ = air; - _ = liveness; - @panic("TODO Plan9 needs to keep track of Air and Liveness so it can use them later"); + const decl = func.owner_decl; + log.debug("codegen decl {*} ({s})", .{ decl, decl.name }); + + var code_buffer = std.ArrayList(u8).init(self.base.allocator); + defer code_buffer.deinit(); + const res = try codegen.generateFunction(&self.base, decl.srcLoc(), func, air, liveness, &code_buffer, .{ .none = .{} }); + const code = switch (res) { + .appended => code_buffer.toOwnedSlice(), + .fail => |em| { + decl.analysis = .codegen_failure; + try module.failed_decls.put(module.gpa, decl, em); + return; + }, + }; + try self.fn_decl_table.put(self.base.allocator, decl, code); + return self.updateFinish(decl); } pub fn updateDecl(self: *Plan9, module: *Module, decl: *Module.Decl) !void { - _ = module; - _ = try self.decl_table.getOrPut(self.base.allocator, decl); + if (decl.val.tag() == .extern_fn) { + return; // TODO Should we do more when front-end analyzed extern decl? + } + if (decl.val.castTag(.variable)) |payload| { + const variable = payload.data; + if (variable.is_extern) { + return; // TODO Should we do more when front-end analyzed extern decl? + } + } + + log.debug("codegen decl {*} ({s})", .{ decl, decl.name }); + + var code_buffer = std.ArrayList(u8).init(self.base.allocator); + defer code_buffer.deinit(); + const decl_val = if (decl.val.castTag(.variable)) |payload| payload.data.init else decl.val; + const res = try codegen.generateSymbol(&self.base, decl.srcLoc(), .{ + .ty = decl.ty, + .val = decl_val, + }, &code_buffer, .{ .none = .{} }); + const code = switch (res) { + .externally_managed => |x| x, + .appended => code_buffer.items, + .fail => |em| { + decl.analysis = .codegen_failure; + try module.failed_decls.put(module.gpa, decl, em); + return; + }, + }; + var duped_code = try std.mem.dupe(self.base.allocator, u8, code); + errdefer self.base.allocator.free(duped_code); + try self.data_decl_table.put(self.base.allocator, decl, duped_code); + return self.updateFinish(decl); +} +/// called at the end of update{Decl,Func} +fn updateFinish(self: *Plan9, decl: *Module.Decl) !void { + const is_fn = (decl.ty.zigTypeTag() == .Fn); + log.debug("update the symbol table and got for decl {*} ({s})", .{ decl, decl.name }); + const sym_t: aout.Sym.Type = if (is_fn) .t else .d; + // write the internal linker metadata + decl.link.plan9.type = sym_t; + // write the symbol + // we already have the got index because that got allocated in allocateDeclIndexes + const sym: aout.Sym = .{ + .value = undefined, // the value of stuff gets filled in in flushModule + .type = decl.link.plan9.type, + .name = mem.span(decl.name), + }; + + if (decl.link.plan9.sym_index) |s| { + self.syms.items[s] = sym; + } else { + try self.syms.append(self.base.allocator, sym); + decl.link.plan9.sym_index = self.syms.items.len - 1; + } } pub fn flush(self: *Plan9, comp: *Compilation) !void { @@ -165,160 +221,107 @@ pub fn flushModule(self: *Plan9, comp: *Compilation) !void { defer assert(self.hdr.entry != 0x0); - const module = self.base.options.module orelse return error.LinkingWithoutZigSourceUnimplemented; + _ = self.base.options.module orelse return error.LinkingWithoutZigSourceUnimplemented; - self.text_buf.items.len = 0; - self.data_buf.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.sixtyfour_bit) @as(u32, 4) else 8); - // temporary buffer - var code_buffer = std.ArrayList(u8).init(self.base.allocator); - defer code_buffer.deinit(); + assert(self.got_len == self.fn_decl_table.count() + self.data_decl_table.count()); + const got_size = self.got_len * if (!self.sixtyfour_bit) @as(u32, 4) else 8; + var got_table = try self.base.allocator.alloc(u8, got_size); + defer self.base.allocator.free(got_table); + + // + 2 for header, got, symbols + var iovecs = try self.base.allocator.alloc(std.os.iovec_const, self.fn_decl_table.count() + self.data_decl_table.count() + 3); + + const file = self.base.file.?; + + var hdr_buf: [40]u8 = undefined; + // account for the fat header + const hdr_size = if (self.sixtyfour_bit) @as(usize, 40) else 32; + const hdr_slice: []u8 = hdr_buf[0..hdr_size]; + var foff = hdr_size; + iovecs[0] = .{ .iov_base = hdr_slice.ptr, .iov_len = hdr_slice.len }; + var iovecs_i: u64 = 1; + var text_i: u64 = 0; + // text { - for (self.decl_table.keys()) |decl| { - if (!decl.has_tv) continue; - 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(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(self.data_buf.items.len, .d), - .type = .d, - .sym_index = decl.link.plan9.sym_index, - .got_index = decl.link.plan9.got_index, - }; - 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), - }; + var it = self.fn_decl_table.iterator(); + while (it.next()) |entry| { + const decl = entry.key_ptr.*; + const code = entry.value_ptr.*; + foff += code.len; + text_i += code.len; + iovecs[iovecs_i] = .{ .iov_base = code.ptr, .iov_len = code.len }; + iovecs_i += 1; + const off = self.getAddr(text_i, .t); + decl.link.plan9.offset = off; + if (!self.sixtyfour_bit) { + mem.writeIntNative(u32, got_table[decl.link.plan9.got_index.? * 4 ..][0..4], @intCast(u32, off)); + mem.writeInt(u32, got_table[decl.link.plan9.got_index.? * 4 ..][0..4], @intCast(u32, off), self.base.options.target.cpu.arch.endian()); } else { - 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; + mem.writeInt(u64, got_table[decl.link.plan9.got_index.? * 8 ..][0..8], off, self.base.options.target.cpu.arch.endian()); } - - 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")) { - 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, - }, &code_buffer, .{ .none = {} }); - const code = switch (res) { - .externally_managed => |x| x, - .appended => code_buffer.items, - .fail => |em| { - decl.analysis = .codegen_failure; - try module.failed_decls.put(module.gpa, decl, em); - // TODO try to do more decls - return; - }, - }; - if (is_fn) { - try self.text_buf.appendSlice(self.base.allocator, code); - code_buffer.items.len = 0; + self.syms.items[decl.link.plan9.sym_index.?].value = off; + } + // etext symbol + self.syms.items[2].value = self.getAddr(text_i, .t); + } + // data + var data_i: u64 = got_size; + { + var it = self.data_decl_table.iterator(); + while (it.next()) |entry| { + const decl = entry.key_ptr.*; + const code = entry.value_ptr.*; + foff += code.len; + data_i += code.len; + iovecs[iovecs_i] = .{ .iov_base = code.ptr, .iov_len = code.len }; + iovecs_i += 1; + const off = self.getAddr(data_i, .d); + decl.link.plan9.offset = off; + if (!self.sixtyfour_bit) { + mem.writeInt(u32, got_table[decl.link.plan9.got_index.? * 4 ..][0..4], @intCast(u32, off), self.base.options.target.cpu.arch.endian()); } else { - try self.data_buf.appendSlice(self.base.allocator, code); - code_buffer.items.len = 0; + mem.writeInt(u64, got_table[decl.link.plan9.got_index.? * 8 ..][0..8], off, self.base.options.target.cpu.arch.endian()); } + self.syms.items[decl.link.plan9.sym_index.?].value = off; } + // edata symbol + self.syms.items[0].value = self.getAddr(data_i, .b); } - - // write the got - if (!self.sixtyfour_bit) { - 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()); - } - } - - self.hdr.entry = @truncate(u32, self.entry_decl.?.link.plan9.offset.?); - - // edata, end, etext - self.syms.items[0].value = self.getAddr(0x0, .b); + // edata self.syms.items[1].value = self.getAddr(0x0, .b); - self.syms.items[2].value = self.getAddr(self.text_buf.items.len, .t); - var sym_buf = std.ArrayList(u8).init(self.base.allocator); defer sym_buf.deinit(); try self.writeSyms(&sym_buf); - + iovecs[iovecs_i] = .{ .iov_base = got_table.ptr, .iov_len = got_table.len }; + iovecs_i += 1; + assert(2 + self.fn_decl_table.count() + self.data_decl_table.count() == iovecs_i); // we didn't write all the decls + iovecs[iovecs_i] = .{ .iov_base = sym_buf.items.ptr, .iov_len = sym_buf.items.len }; + iovecs_i += 1; // generate the header 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), + .text = @intCast(u32, text_i), + .data = @intCast(u32, data_i), .syms = @intCast(u32, sym_buf.items.len), .bss = 0, .pcsz = 0, .spsz = 0, - .entry = self.hdr.entry, + .entry = @intCast(u32, self.entry_decl.?.link.plan9.offset.?), }; - - const file = self.base.file.?; - - var hdr_buf = self.hdr.toU8s(); - const hdr_slice: []const u8 = &hdr_buf; - // account for the fat header - const hdr_size: u8 = if (!self.sixtyfour_bit) 32 else 40; + std.mem.copy(u8, hdr_slice, self.hdr.toU8s()[0..hdr_size]); // write the fat header for 64 bit entry points if (self.sixtyfour_bit) { - mem.writeIntSliceBig(u64, hdr_buf[32..40], self.hdr.entry); + mem.writeIntSliceBig(u64, hdr_buf[32..40], self.entry_decl.?.link.plan9.offset.?); } // write it all! - var vectors: [4]std.os.iovec_const = .{ - .{ .iov_base = hdr_slice.ptr, .iov_len = hdr_size }, - .{ .iov_base = self.text_buf.items.ptr, .iov_len = self.text_buf.items.len }, - .{ .iov_base = self.data_buf.items.ptr, .iov_len = self.data_buf.items.len }, - .{ .iov_base = sym_buf.items.ptr, .iov_len = sym_buf.items.len }, - // TODO spsz, pcsz - }; - try file.pwritevAll(&vectors, 0); + try file.pwritevAll(iovecs, 0); } pub fn freeDecl(self: *Plan9, decl: *Module.Decl) void { - assert(self.decl_table.swapRemove(decl)); + const is_fn = (decl.ty.zigTypeTag() == .Fn); + if (is_fn) + assert(self.fn_decl_table.swapRemove(decl)) + else + assert(self.data_decl_table.swapRemove(decl)); } pub fn updateDeclExports( @@ -334,11 +337,17 @@ pub fn updateDeclExports( _ = exports; } pub fn deinit(self: *Plan9) void { - self.decl_table.deinit(self.base.allocator); + var itf = self.fn_decl_table.iterator(); + while (itf.next()) |entry| { + self.base.allocator.free(entry.value_ptr.*); + } + self.fn_decl_table.deinit(self.base.allocator); + var itd = self.data_decl_table.iterator(); + while (itd.next()) |entry| { + self.base.allocator.free(entry.value_ptr.*); + } + self.data_decl_table.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; @@ -397,6 +406,8 @@ pub fn writeSyms(self: *Plan9, buf: *std.ArrayList(u8)) !void { } 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; + if (decl.link.plan9.got_index != null) { + self.got_len += 1; + decl.link.plan9.got_index = self.got_len - 1; + } }