Commit 798162e509
Changed files (5)
src
src/link/plan9/a.out.zig
@@ -0,0 +1,122 @@
+// 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 = struct {
+ magic: u32,
+ text: u32,
+ data: u32,
+ bss: u32,
+ syms: u32,
+ entry: u32,
+ spsz: u32,
+ pcsz: u32,
+ pub fn size() u8 {
+ return 32;
+ }
+};
+
+// uchar value[4];
+// char type;
+// char name[n]; /* NUL-terminated */
+pub const Sym32 = struct {
+ value: u32, // big endian in the file
+ type: SymType,
+ name: [*:0]const u8,
+};
+// uchar value[8];
+// char type;
+// char name[n]; /* NUL-terminated */
+pub const Sym64 = struct {
+ value: u64, // big endian in the file
+ type: SymType,
+ name: [*:0]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 const HDR_MAGIC = @import("std").meta.promoteIntLiteral(c_int, 0x00008000, .hexadecimal);
+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(@as(c_int, 0), @as(c_int, 8)); // 68020
+pub const I_MAGIC = _MAGIC(@as(c_int, 0), @as(c_int, 11)); // intel 386
+pub const J_MAGIC = _MAGIC(@as(c_int, 0), @as(c_int, 12)); // intel 960 (retired)
+pub const K_MAGIC = _MAGIC(@as(c_int, 0), @as(c_int, 13)); // sparc
+pub const V_MAGIC = _MAGIC(@as(c_int, 0), @as(c_int, 16)); // mips 3000 BE
+pub const X_MAGIC = _MAGIC(@as(c_int, 0), @as(c_int, 17)); // att dsp 3210 (retired)
+pub const M_MAGIC = _MAGIC(@as(c_int, 0), @as(c_int, 18)); // mips 4000 BE
+pub const D_MAGIC = _MAGIC(@as(c_int, 0), @as(c_int, 19)); // amd 29000 (retired)
+pub const E_MAGIC = _MAGIC(@as(c_int, 0), @as(c_int, 20)); // arm
+pub const Q_MAGIC = _MAGIC(@as(c_int, 0), @as(c_int, 21)); // powerpc
+pub const N_MAGIC = _MAGIC(@as(c_int, 0), @as(c_int, 22)); // mips 4000 LE
+pub const L_MAGIC = _MAGIC(@as(c_int, 0), @as(c_int, 23)); // dec alpha (retired)
+pub const P_MAGIC = _MAGIC(@as(c_int, 0), @as(c_int, 24)); // mips 3000 LE
+pub const U_MAGIC = _MAGIC(@as(c_int, 0), @as(c_int, 25)); // sparc64
+pub const S_MAGIC = _MAGIC(HDR_MAGIC, @as(c_int, 26)); // amd64
+pub const T_MAGIC = _MAGIC(HDR_MAGIC, @as(c_int, 27)); // powerpc64
+pub const R_MAGIC = _MAGIC(HDR_MAGIC, @as(c_int, 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,
+ };
+}
src/link/Plan9.zig
@@ -4,35 +4,66 @@ 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 codegen = @import("../codegen.zig");
+const trace = @import("../tracy.zig").trace;
+const mem = std.mem;
const File = link.File;
const Allocator = std.mem.Allocator;
const log = std.log.scoped(.link);
+const assert = std.debug.assert;
+
+// TODO use incremental compilation
base: link.File,
+ptr_width: PtrWidth,
error_flags: File.ErrorFlags = File.ErrorFlags{},
-pub const SrcFn = struct {
- /// Offset from the beginning of the Debug Line Program header that contains this function.
- off: u32,
- /// Size of the line number program component belonging to this function, not
- /// including padding.
- len: u32,
-
- /// Points to the previous and next neighbors, based on the offset from .debug_line.
- /// This can be used to find, for example, the capacity of this `SrcFn`.
- prev: ?*SrcFn,
- next: ?*SrcFn,
-
- pub const empty: SrcFn = .{
- .off = 0,
- .len = 0,
- .prev = null,
- .next = null,
+decl_table: std.AutoArrayHashMapUnmanaged(*Module.Decl, void) = .{},
+/// is just casted down when 32 bit
+syms: std.ArrayListUnmanaged(aout.Sym64) = .{},
+call_relocs: std.ArrayListUnmanaged(CallReloc) = .{},
+text_buf: std.ArrayListUnmanaged(u8) = .{},
+data_buf: std.ArrayListUnmanaged(u8) = .{},
+
+cur_decl: *Module.Decl = undefined,
+hdr: aout.ExecHdr = undefined,
+
+fn headerSize(self: Plan9) u32 {
+ // fat header (currently unused)
+ const fat: u4 = if (self.ptr_width == .p64) 8 else 0;
+ return aout.ExecHdr.size() + fat;
+}
+pub const DeclBlock = struct {
+ type: enum { text, data },
+ // offset in the text or data sects
+ offset: u32,
+ pub const empty = DeclBlock{
+ .type = .text,
+ .offset = 0,
};
};
+// TODO change base addr based on target (right now it just works on amd64)
+const default_base_addr = 0x00200000;
+
+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 {
+ if (options.use_llvm)
+ return error.LLVMBackendDoesNotSupportPlan9;
+ const ptr_width: PtrWidth = switch (options.target.cpu.arch.ptrBitWidth()) {
+ 0...32 => .p32,
+ 33...64 => .p64,
+ else => return error.UnsupportedELFArchitecture,
+ };
const self = try gpa.create(Plan9);
self.* = .{
.base = .{
@@ -41,25 +72,157 @@ pub fn createEmpty(gpa: *Allocator, options: link.Options) !*Plan9 {
.allocator = gpa,
.file = null,
},
+ .ptr_width = ptr_width,
};
return self;
}
-pub fn updateDecl(self: *Plan9, module: *Module, decl: *Module.Decl) !void {}
+pub fn updateDecl(self: *Plan9, module: *Module, decl: *Module.Decl) !void {
+ _ = try self.decl_table.getOrPut(self.base.allocator, decl);
+}
+
+pub fn flush(self: *Plan9, comp: *Compilation) !void {
+ assert(!self.base.options.use_lld);
+
+ switch (self.base.options.effectiveOutputMode()) {
+ .Exe => {},
+ // plan9 object files are totally different
+ .Obj => return error.TODOImplementPlan9Objs,
+ .Lib => return error.TODOImplementWritingLibFiles,
+ }
+ return self.flushModule(comp);
+}
+pub fn flushModule(self: *Plan9, comp: *Compilation) !void {
+ const module = self.base.options.module orelse return error.LinkingWithoutZigSourceUnimplemented;
-pub fn allocateDeclIndexes(self: *Plan9, decl: *Module.Decl) !void {}
+ // generate the header
+ self.hdr.magic = try aout.magicFromArch(self.base.options.target.cpu.arch);
+ const file = self.base.file.?;
+ try file.seekTo(self.headerSize());
+
+ // 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);
+ decl.link.plan9 = if (is_fn) .{
+ .offset = @intCast(u32, self.text_buf.items.len),
+ .type = .text,
+ } else .{
+ .offset = @intCast(u32, self.data_buf.items.len),
+ .type = .data,
+ };
+ 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)
+ else
+ try self.data_buf.appendSlice(self.base.allocator, code);
+ code_buffer.items.len = 0;
+ }
+ }
+ try file.writeAll(self.text_buf.items);
+ try file.writeAll(self.data_buf.items);
+ try file.seekTo(0);
+ self.hdr.text = @intCast(u32, self.text_buf.items.len);
+ self.hdr.data = @intCast(u32, self.data_buf.items.len);
+ self.hdr.pcsz = 0;
+ self.hdr.spsz = 0;
+ inline for (std.meta.fields(aout.ExecHdr)) |f| {
+ try file.writer().writeIntBig(f.field_type, @field(self.hdr, f.name));
+ }
+}
+pub fn freeDecl(self: *Plan9, decl: *Module.Decl) void {
+ assert(self.decl_table.swapRemove(decl));
+}
-pub fn flush(self: *Plan9, comp: *Compilation) !void {}
-pub fn flushModule(self: *Plan9, comp: *Compilation) !void {}
-pub fn freeDecl(self: *Plan9, decl: *Module.Decl) void {}
pub fn updateDeclExports(
self: *Plan9,
module: *Module,
decl: *Module.Decl,
exports: []const *Module.Export,
-) !void {}
-pub fn deinit(self: *Plan9) void {}
+) !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 == .text); // we tried to link a non-function as _start
+ self.hdr.entry = Plan9.default_base_addr + self.headerSize() + decl.link.plan9.offset;
+ }
+ if (exp.link.plan9) |i| {
+ const sym = &self.syms.items[i];
+ sym.* = .{
+ .value = decl.link.plan9.offset,
+ .type = switch (decl.link.plan9.type) {
+ .text => .T,
+ .data => .D,
+ },
+ .name = decl.name,
+ };
+ } else {
+ try self.syms.append(self.base.allocator, .{
+ .value = decl.link.plan9.offset,
+ .type = switch (decl.link.plan9.type) {
+ .text => .T,
+ .data => .D,
+ },
+ .name = decl.name,
+ });
+ }
+ }
+}
+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);
+}
-pub const Export = struct {
- sym_index: ?u32 = null,
-};
+pub const Export = ?usize;
+pub const base_tag = .plan9;
+pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Options) !*Plan9 {
+ if (options.use_llvm)
+ return error.LLVMBackendDoesNotSupportPlan9;
+ assert(options.object_format == .plan9);
+ const file = try options.emit.?.directory.handle.createFile(sub_path, .{
+ .truncate = false,
+ .read = true,
+ .mode = link.determineMode(options),
+ });
+ errdefer file.close();
+
+ const self = try createEmpty(allocator, options);
+ errdefer self.base.destroy();
+
+ self.base.file = file;
+ return self;
+}
+
+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);
+}
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| {
src/link.zig
@@ -141,6 +141,7 @@ pub const File = struct {
elf: Elf.TextBlock,
coff: Coff.TextBlock,
macho: MachO.TextBlock,
+ plan9: Plan9.DeclBlock,
c: C.DeclBlock,
wasm: Wasm.DeclBlock,
spirv: void,
@@ -150,7 +151,7 @@ pub const File = struct {
elf: Elf.SrcFn,
coff: Coff.SrcFn,
macho: MachO.SrcFn,
- plan9: Plan9.SrcFn,
+ plan9: void,
c: C.FnBlock,
wasm: Wasm.FnData,
spirv: SpirV.FnData,
@@ -207,12 +208,12 @@ pub const File = struct {
.coff, .pe => &(try Coff.createEmpty(allocator, options)).base,
.elf => &(try Elf.createEmpty(allocator, options)).base,
.macho => &(try MachO.createEmpty(allocator, options)).base,
+ .plan9 => &(try Plan9.createEmpty(allocator, options)).base,
.wasm => &(try Wasm.createEmpty(allocator, options)).base,
.c => unreachable, // Reported error earlier.
.spirv => &(try SpirV.createEmpty(allocator, options)).base,
.hex => return error.HexObjectFormatUnimplemented,
.raw => return error.RawObjectFormatUnimplemented,
- .plan9 => return error.Plan9ObjectFormatUnimplemented,
};
}
// Open a temporary object file, not the final output file because we want to link with LLD.
@@ -224,12 +225,12 @@ pub const File = struct {
.coff, .pe => &(try Coff.openPath(allocator, sub_path, options)).base,
.elf => &(try Elf.openPath(allocator, sub_path, options)).base,
.macho => &(try MachO.openPath(allocator, sub_path, options)).base,
+ .plan9 => &(try Plan9.openPath(allocator, sub_path, options)).base,
.wasm => &(try Wasm.openPath(allocator, sub_path, options)).base,
.c => &(try C.openPath(allocator, sub_path, options)).base,
.spirv => &(try SpirV.openPath(allocator, sub_path, options)).base,
.hex => return error.HexObjectFormatUnimplemented,
.raw => return error.RawObjectFormatUnimplemented,
- .plan9 => return error.Plan9ObjectFormatUnimplemented,
};
if (use_lld) {
@@ -347,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 => return @fieldParentPtr(Plan9, "base", base).allocateDeclIndexes(decl),
+ .plan9 => {},
.spirv => {},
}
}
src/Module.zig
@@ -3517,7 +3517,7 @@ pub fn clearDecl(
.coff => .{ .coff = link.File.Coff.TextBlock.empty },
.elf => .{ .elf = link.File.Elf.TextBlock.empty },
.macho => .{ .macho = link.File.MachO.TextBlock.empty },
- .plan9 => @panic("plan9 link"),
+ .plan9 => .{ .plan9 = link.File.Plan9.DeclBlock.empty },
.c => .{ .c = link.File.C.DeclBlock.empty },
.wasm => .{ .wasm = link.File.Wasm.DeclBlock.empty },
.spirv => .{ .spirv = {} },
@@ -3526,7 +3526,7 @@ pub fn clearDecl(
.coff => .{ .coff = {} },
.elf => .{ .elf = link.File.Elf.SrcFn.empty },
.macho => .{ .macho = link.File.MachO.SrcFn.empty },
- .plan9 => @panic("plan9 fn_link"),
+ .plan9 => .{ .plan9 = {} },
.c => .{ .c = link.File.C.FnBlock.empty },
.wasm => .{ .wasm = link.File.Wasm.FnData.empty },
.spirv => .{ .spirv = .{} },
@@ -3694,7 +3694,7 @@ fn allocateNewDecl(mod: *Module, namespace: *Scope.Namespace, src_node: ast.Node
.coff => .{ .coff = link.File.Coff.TextBlock.empty },
.elf => .{ .elf = link.File.Elf.TextBlock.empty },
.macho => .{ .macho = link.File.MachO.TextBlock.empty },
- .plan9 => @panic("PLan9 export"),
+ .plan9 => .{ .plan9 = link.File.Plan9.DeclBlock.empty },
.c => .{ .c = link.File.C.DeclBlock.empty },
.wasm => .{ .wasm = link.File.Wasm.DeclBlock.empty },
.spirv => .{ .spirv = {} },
@@ -3703,7 +3703,7 @@ fn allocateNewDecl(mod: *Module, namespace: *Scope.Namespace, src_node: ast.Node
.coff => .{ .coff = {} },
.elf => .{ .elf = link.File.Elf.SrcFn.empty },
.macho => .{ .macho = link.File.MachO.SrcFn.empty },
- .plan9 => .{ .plan9 = link.File.Plan9.SrcFn.empty },
+ .plan9 => .{ .plan9 = {} },
.c => .{ .c = link.File.C.FnBlock.empty },
.wasm => .{ .wasm = link.File.Wasm.FnData.empty },
.spirv => .{ .spirv = .{} },
@@ -3773,7 +3773,7 @@ pub fn analyzeExport(
.coff => .{ .coff = {} },
.elf => .{ .elf = link.File.Elf.Export{} },
.macho => .{ .macho = link.File.MachO.Export{} },
- .plan9 => @panic("plan9 link"),
+ .plan9 => .{ .plan9 = null },
.c => .{ .c = {} },
.wasm => .{ .wasm = {} },
.spirv => .{ .spirv = {} },