authorgravatar for jacoblevgw@gmail.comJacob G-W <jacoblevgw@gmail.com> 2021-06-01 22:48:20-04:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-07-08 14:10:49-07:00
log798162e5095aff130395ee542c0c0948f95180c9
treebaa971347a8bad4f2df54dff23f750c6066eff84
parent34c21affa2429cf07042eb2f225ee59194998802

plan9 linker: make runnable binaries

We can now run binaries! (they segfault, but still run!)

5 files changed, 335 insertions(+), 39 deletions(-)

src/Module.zig+5-5
......@@ -3517,7 +3517,7 @@ pub fn clearDecl(
35173517 .coff => .{ .coff = link.File.Coff.TextBlock.empty },
35183518 .elf => .{ .elf = link.File.Elf.TextBlock.empty },
35193519 .macho => .{ .macho = link.File.MachO.TextBlock.empty },
3520 .plan9 => @panic("plan9 link"),
3520 .plan9 => .{ .plan9 = link.File.Plan9.DeclBlock.empty },
35213521 .c => .{ .c = link.File.C.DeclBlock.empty },
35223522 .wasm => .{ .wasm = link.File.Wasm.DeclBlock.empty },
35233523 .spirv => .{ .spirv = {} },
......@@ -3526,7 +3526,7 @@ pub fn clearDecl(
35263526 .coff => .{ .coff = {} },
35273527 .elf => .{ .elf = link.File.Elf.SrcFn.empty },
35283528 .macho => .{ .macho = link.File.MachO.SrcFn.empty },
3529 .plan9 => @panic("plan9 fn_link"),
3529 .plan9 => .{ .plan9 = {} },
35303530 .c => .{ .c = link.File.C.FnBlock.empty },
35313531 .wasm => .{ .wasm = link.File.Wasm.FnData.empty },
35323532 .spirv => .{ .spirv = .{} },
......@@ -3694,7 +3694,7 @@ fn allocateNewDecl(mod: *Module, namespace: *Scope.Namespace, src_node: ast.Node
36943694 .coff => .{ .coff = link.File.Coff.TextBlock.empty },
36953695 .elf => .{ .elf = link.File.Elf.TextBlock.empty },
36963696 .macho => .{ .macho = link.File.MachO.TextBlock.empty },
3697 .plan9 => @panic("PLan9 export"),
3697 .plan9 => .{ .plan9 = link.File.Plan9.DeclBlock.empty },
36983698 .c => .{ .c = link.File.C.DeclBlock.empty },
36993699 .wasm => .{ .wasm = link.File.Wasm.DeclBlock.empty },
37003700 .spirv => .{ .spirv = {} },
......@@ -3703,7 +3703,7 @@ fn allocateNewDecl(mod: *Module, namespace: *Scope.Namespace, src_node: ast.Node
37033703 .coff => .{ .coff = {} },
37043704 .elf => .{ .elf = link.File.Elf.SrcFn.empty },
37053705 .macho => .{ .macho = link.File.MachO.SrcFn.empty },
3706 .plan9 => .{ .plan9 = link.File.Plan9.SrcFn.empty },
3706 .plan9 => .{ .plan9 = {} },
37073707 .c => .{ .c = link.File.C.FnBlock.empty },
37083708 .wasm => .{ .wasm = link.File.Wasm.FnData.empty },
37093709 .spirv => .{ .spirv = .{} },
......@@ -3773,7 +3773,7 @@ pub fn analyzeExport(
37733773 .coff => .{ .coff = {} },
37743774 .elf => .{ .elf = link.File.Elf.Export{} },
37753775 .macho => .{ .macho = link.File.MachO.Export{} },
3776 .plan9 => @panic("plan9 link"),
3776 .plan9 => .{ .plan9 = null },
37773777 .c => .{ .c = {} },
37783778 .wasm => .{ .wasm = {} },
37793779 .spirv => .{ .spirv = {} },
src/codegen.zig+13-3
......@@ -2556,9 +2556,19 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
25562556 } else {
25572557 return self.fail(inst.base.src, "TODO implement calling runtime known function pointer", .{});
25582558 }
2559 } else {
2560 unreachable;
2561 }
2559 } else if (self.bin_file.cast(link.File.Plan9)) |p9| {
2560 if (inst.func.value()) |func_value| {
2561 if (func_value.castTag(.function)) |func_payload| {
2562 try p9.addCallReloc(self.code, .{
2563 .caller = p9.cur_decl,
2564 .callee = func_payload.data.owner_decl,
2565 .offset_in_caller = self.code.items.len,
2566 });
2567 } else return self.fail(inst.base.src, "TODO implement calling extern fn on plan9", .{});
2568 } else {
2569 return self.fail(inst.base.src, "TODO implement calling runtime known function pointer", .{});
2570 }
2571 } else unreachable;
25622572
25632573 switch (info.return_value) {
25642574 .register => |reg| {
src/link.zig+5-4
......@@ -141,6 +141,7 @@ pub const File = struct {
141141 elf: Elf.TextBlock,
142142 coff: Coff.TextBlock,
143143 macho: MachO.TextBlock,
144 plan9: Plan9.DeclBlock,
144145 c: C.DeclBlock,
145146 wasm: Wasm.DeclBlock,
146147 spirv: void,
......@@ -150,7 +151,7 @@ pub const File = struct {
150151 elf: Elf.SrcFn,
151152 coff: Coff.SrcFn,
152153 macho: MachO.SrcFn,
153 plan9: Plan9.SrcFn,
154 plan9: void,
154155 c: C.FnBlock,
155156 wasm: Wasm.FnData,
156157 spirv: SpirV.FnData,
......@@ -207,12 +208,12 @@ pub const File = struct {
207208 .coff, .pe => &(try Coff.createEmpty(allocator, options)).base,
208209 .elf => &(try Elf.createEmpty(allocator, options)).base,
209210 .macho => &(try MachO.createEmpty(allocator, options)).base,
211 .plan9 => &(try Plan9.createEmpty(allocator, options)).base,
210212 .wasm => &(try Wasm.createEmpty(allocator, options)).base,
211213 .c => unreachable, // Reported error earlier.
212214 .spirv => &(try SpirV.createEmpty(allocator, options)).base,
213215 .hex => return error.HexObjectFormatUnimplemented,
214216 .raw => return error.RawObjectFormatUnimplemented,
215 .plan9 => return error.Plan9ObjectFormatUnimplemented,
216217 };
217218 }
218219 // 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 {
224225 .coff, .pe => &(try Coff.openPath(allocator, sub_path, options)).base,
225226 .elf => &(try Elf.openPath(allocator, sub_path, options)).base,
226227 .macho => &(try MachO.openPath(allocator, sub_path, options)).base,
228 .plan9 => &(try Plan9.openPath(allocator, sub_path, options)).base,
227229 .wasm => &(try Wasm.openPath(allocator, sub_path, options)).base,
228230 .c => &(try C.openPath(allocator, sub_path, options)).base,
229231 .spirv => &(try SpirV.openPath(allocator, sub_path, options)).base,
230232 .hex => return error.HexObjectFormatUnimplemented,
231233 .raw => return error.RawObjectFormatUnimplemented,
232 .plan9 => return error.Plan9ObjectFormatUnimplemented,
233234 };
234235
235236 if (use_lld) {
......@@ -347,7 +348,7 @@ pub const File = struct {
347348 .macho => return @fieldParentPtr(MachO, "base", base).allocateDeclIndexes(decl),
348349 .c => return @fieldParentPtr(C, "base", base).allocateDeclIndexes(decl),
349350 .wasm => return @fieldParentPtr(Wasm, "base", base).allocateDeclIndexes(decl),
350 .plan9 => return @fieldParentPtr(Plan9, "base", base).allocateDeclIndexes(decl),
351 .plan9 => {},
351352 .spirv => {},
352353 }
353354 }
src/link/Plan9.zig+190-27
......@@ -4,35 +4,66 @@ const std = @import("std");
44const link = @import("../link.zig");
55const Module = @import("../Module.zig");
66const Compilation = @import("../Compilation.zig");
7const aout = @import("plan9/a.out.zig");
8const codegen = @import("../codegen.zig");
9const trace = @import("../tracy.zig").trace;
10const mem = std.mem;
711const File = link.File;
812const Allocator = std.mem.Allocator;
913
1014const log = std.log.scoped(.link);
15const assert = std.debug.assert;
16
17// TODO use incremental compilation
1118
1219base: link.File,
20ptr_width: PtrWidth,
1321error_flags: File.ErrorFlags = File.ErrorFlags{},
1422
15pub const SrcFn = struct {
16 /// Offset from the beginning of the Debug Line Program header that contains this function.
17 off: u32,
18 /// Size of the line number program component belonging to this function, not
19 /// including padding.
20 len: u32,
21
22 /// Points to the previous and next neighbors, based on the offset from .debug_line.
23 /// This can be used to find, for example, the capacity of this `SrcFn`.
24 prev: ?*SrcFn,
25 next: ?*SrcFn,
26
27 pub const empty: SrcFn = .{
28 .off = 0,
29 .len = 0,
30 .prev = null,
31 .next = null,
23decl_table: std.AutoArrayHashMapUnmanaged(*Module.Decl, void) = .{},
24/// is just casted down when 32 bit
25syms: std.ArrayListUnmanaged(aout.Sym64) = .{},
26call_relocs: std.ArrayListUnmanaged(CallReloc) = .{},
27text_buf: std.ArrayListUnmanaged(u8) = .{},
28data_buf: std.ArrayListUnmanaged(u8) = .{},
29
30cur_decl: *Module.Decl = undefined,
31hdr: aout.ExecHdr = undefined,
32
33fn headerSize(self: Plan9) u32 {
34 // fat header (currently unused)
35 const fat: u4 = if (self.ptr_width == .p64) 8 else 0;
36 return aout.ExecHdr.size() + fat;
37}
38pub const DeclBlock = struct {
39 type: enum { text, data },
40 // offset in the text or data sects
41 offset: u32,
42 pub const empty = DeclBlock{
43 .type = .text,
44 .offset = 0,
3245 };
3346};
3447
48// TODO change base addr based on target (right now it just works on amd64)
49const default_base_addr = 0x00200000;
50
51pub const CallReloc = struct {
52 caller: *Module.Decl,
53 callee: *Module.Decl,
54 offset_in_caller: usize,
55};
56
57pub const PtrWidth = enum { p32, p64 };
58
3559pub fn createEmpty(gpa: *Allocator, options: link.Options) !*Plan9 {
60 if (options.use_llvm)
61 return error.LLVMBackendDoesNotSupportPlan9;
62 const ptr_width: PtrWidth = switch (options.target.cpu.arch.ptrBitWidth()) {
63 0...32 => .p32,
64 33...64 => .p64,
65 else => return error.UnsupportedELFArchitecture,
66 };
3667 const self = try gpa.create(Plan9);
3768 self.* = .{
3869 .base = .{
......@@ -41,25 +72,157 @@ pub fn createEmpty(gpa: *Allocator, options: link.Options) !*Plan9 {
4172 .allocator = gpa,
4273 .file = null,
4374 },
75 .ptr_width = ptr_width,
4476 };
4577 return self;
4678}
4779
48pub fn updateDecl(self: *Plan9, module: *Module, decl: *Module.Decl) !void {}
80pub fn updateDecl(self: *Plan9, module: *Module, decl: *Module.Decl) !void {
81 _ = try self.decl_table.getOrPut(self.base.allocator, decl);
82}
83
84pub fn flush(self: *Plan9, comp: *Compilation) !void {
85 assert(!self.base.options.use_lld);
86
87 switch (self.base.options.effectiveOutputMode()) {
88 .Exe => {},
89 // plan9 object files are totally different
90 .Obj => return error.TODOImplementPlan9Objs,
91 .Lib => return error.TODOImplementWritingLibFiles,
92 }
93 return self.flushModule(comp);
94}
95pub fn flushModule(self: *Plan9, comp: *Compilation) !void {
96 const module = self.base.options.module orelse return error.LinkingWithoutZigSourceUnimplemented;
4997
50pub fn allocateDeclIndexes(self: *Plan9, decl: *Module.Decl) !void {}
98 // generate the header
99 self.hdr.magic = try aout.magicFromArch(self.base.options.target.cpu.arch);
100 const file = self.base.file.?;
101 try file.seekTo(self.headerSize());
102
103 // temporary buffer
104 var code_buffer = std.ArrayList(u8).init(self.base.allocator);
105 defer code_buffer.deinit();
106 {
107 for (self.decl_table.keys()) |decl| {
108 if (!decl.has_tv) continue;
109 self.cur_decl = decl;
110 const is_fn = (decl.ty.zigTypeTag() == .Fn);
111 decl.link.plan9 = if (is_fn) .{
112 .offset = @intCast(u32, self.text_buf.items.len),
113 .type = .text,
114 } else .{
115 .offset = @intCast(u32, self.data_buf.items.len),
116 .type = .data,
117 };
118 const res = try codegen.generateSymbol(&self.base, decl.srcLoc(), .{
119 .ty = decl.ty,
120 .val = decl.val,
121 }, &code_buffer, .{ .none = {} });
122 const code = switch (res) {
123 .externally_managed => |x| x,
124 .appended => code_buffer.items,
125 .fail => |em| {
126 decl.analysis = .codegen_failure;
127 try module.failed_decls.put(module.gpa, decl, em);
128 // TODO try to do more decls
129 return;
130 },
131 };
132 if (is_fn)
133 try self.text_buf.appendSlice(self.base.allocator, code)
134 else
135 try self.data_buf.appendSlice(self.base.allocator, code);
136 code_buffer.items.len = 0;
137 }
138 }
139 try file.writeAll(self.text_buf.items);
140 try file.writeAll(self.data_buf.items);
141 try file.seekTo(0);
142 self.hdr.text = @intCast(u32, self.text_buf.items.len);
143 self.hdr.data = @intCast(u32, self.data_buf.items.len);
144 self.hdr.pcsz = 0;
145 self.hdr.spsz = 0;
146 inline for (std.meta.fields(aout.ExecHdr)) |f| {
147 try file.writer().writeIntBig(f.field_type, @field(self.hdr, f.name));
148 }
149}
150pub fn freeDecl(self: *Plan9, decl: *Module.Decl) void {
151 assert(self.decl_table.swapRemove(decl));
152}
51153
52pub fn flush(self: *Plan9, comp: *Compilation) !void {}
53pub fn flushModule(self: *Plan9, comp: *Compilation) !void {}
54pub fn freeDecl(self: *Plan9, decl: *Module.Decl) void {}
55154pub fn updateDeclExports(
56155 self: *Plan9,
57156 module: *Module,
58157 decl: *Module.Decl,
59158 exports: []const *Module.Export,
60) !void {}
61pub fn deinit(self: *Plan9) void {}
159) !void {
160 for (exports) |exp| {
161 if (exp.options.section) |section_name| {
162 if (!mem.eql(u8, section_name, ".text")) {
163 try module.failed_exports.ensureCapacity(module.gpa, module.failed_exports.count() + 1);
164 module.failed_exports.putAssumeCapacityNoClobber(
165 exp,
166 try Module.ErrorMsg.create(self.base.allocator, decl.srcLoc(), "plan9 does not support extra sections", .{}),
167 );
168 continue;
169 }
170 }
171 if (std.mem.eql(u8, exp.options.name, "_start")) {
172 std.debug.assert(decl.link.plan9.type == .text); // we tried to link a non-function as _start
173 self.hdr.entry = Plan9.default_base_addr + self.headerSize() + decl.link.plan9.offset;
174 }
175 if (exp.link.plan9) |i| {
176 const sym = &self.syms.items[i];
177 sym.* = .{
178 .value = decl.link.plan9.offset,
179 .type = switch (decl.link.plan9.type) {
180 .text => .T,
181 .data => .D,
182 },
183 .name = decl.name,
184 };
185 } else {
186 try self.syms.append(self.base.allocator, .{
187 .value = decl.link.plan9.offset,
188 .type = switch (decl.link.plan9.type) {
189 .text => .T,
190 .data => .D,
191 },
192 .name = decl.name,
193 });
194 }
195 }
196}
197pub fn deinit(self: *Plan9) void {
198 self.decl_table.deinit(self.base.allocator);
199 self.call_relocs.deinit(self.base.allocator);
200 self.syms.deinit(self.base.allocator);
201 self.text_buf.deinit(self.base.allocator);
202 self.data_buf.deinit(self.base.allocator);
203}
62204
63pub const Export = struct {
64 sym_index: ?u32 = null,
65};
205pub const Export = ?usize;
206pub const base_tag = .plan9;
207pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Options) !*Plan9 {
208 if (options.use_llvm)
209 return error.LLVMBackendDoesNotSupportPlan9;
210 assert(options.object_format == .plan9);
211 const file = try options.emit.?.directory.handle.createFile(sub_path, .{
212 .truncate = false,
213 .read = true,
214 .mode = link.determineMode(options),
215 });
216 errdefer file.close();
217
218 const self = try createEmpty(allocator, options);
219 errdefer self.base.destroy();
220
221 self.base.file = file;
222 return self;
223}
224
225pub fn addCallReloc(self: *Plan9, code: *std.ArrayList(u8), reloc: CallReloc) !void {
226 try self.call_relocs.append(self.base.allocator, reloc);
227 try code.writer().writeIntBig(u64, 0xdeadbeef);
228}
src/link/plan9/a.out.zig created+122
......@@ -0,0 +1,122 @@
1// Copyright © 2021 Plan 9 Foundation
2// Copyright © 20XX 9front authors
3
4// Permission is hereby granted, free of charge, to any person obtaining a copy
5// of this software and associated documentation files (the "Software"), to deal
6// in the Software without restriction, including without limitation the rights
7// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8// copies of the Software, and to permit persons to whom the Software is
9// furnished to do so, subject to the following conditions:
10
11// The above copyright notice and this permission notice shall be included in all
12// copies or substantial portions of the Software.
13
14// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20// SOFTWARE.
21
22// Idomatic translation of 9front a.out.h
23const std = @import("std");
24// all integers are in big-endian format (needs a byteswap)
25pub const ExecHdr = struct {
26 magic: u32,
27 text: u32,
28 data: u32,
29 bss: u32,
30 syms: u32,
31 entry: u32,
32 spsz: u32,
33 pcsz: u32,
34 pub fn size() u8 {
35 return 32;
36 }
37};
38
39// uchar value[4];
40// char type;
41// char name[n]; /* NUL-terminated */
42pub const Sym32 = struct {
43 value: u32, // big endian in the file
44 type: SymType,
45 name: [*:0]const u8,
46};
47// uchar value[8];
48// char type;
49// char name[n]; /* NUL-terminated */
50pub const Sym64 = struct {
51 value: u64, // big endian in the file
52 type: SymType,
53 name: [*:0]const u8,
54};
55// The type field is one of the following characters with the
56// high bit set:
57// T text segment symbol
58// t static text segment symbol
59// L leaf function text segment symbol
60// l static leaf function text segment symbol
61// D data segment symbol
62// d static data segment symbol
63// B bss segment symbol
64// b static bss segment symbol
65// a automatic (local) variable symbol
66// p function parameter symbol
67// f source file name components
68// z source file name
69// Z source file line offset
70// m for '.frame'
71pub const SymType = enum(u8) {
72 T = 0x80 | 'T',
73 t = 0x80 | 't',
74 L = 0x80 | 'L',
75 l = 0x80 | 'l',
76 D = 0x80 | 'D',
77 d = 0x80 | 'd',
78 B = 0x80 | 'B',
79 b = 0x80 | 'b',
80 a = 0x80 | 'a',
81 p = 0x80 | 'p',
82 f = 0x80 | 'f',
83 z = 0x80 | 'z',
84 Z = 0x80 | 'Z',
85 m = 0x80 | 'm',
86};
87
88pub const HDR_MAGIC = @import("std").meta.promoteIntLiteral(c_int, 0x00008000, .hexadecimal);
89pub inline fn _MAGIC(f: anytype, b: anytype) @TypeOf(f | ((((@as(c_int, 4) * b) + @as(c_int, 0)) * b) + @as(c_int, 7))) {
90 return f | ((((@as(c_int, 4) * b) + @as(c_int, 0)) * b) + @as(c_int, 7));
91}
92pub const A_MAGIC = _MAGIC(@as(c_int, 0), @as(c_int, 8)); // 68020
93pub const I_MAGIC = _MAGIC(@as(c_int, 0), @as(c_int, 11)); // intel 386
94pub const J_MAGIC = _MAGIC(@as(c_int, 0), @as(c_int, 12)); // intel 960 (retired)
95pub const K_MAGIC = _MAGIC(@as(c_int, 0), @as(c_int, 13)); // sparc
96pub const V_MAGIC = _MAGIC(@as(c_int, 0), @as(c_int, 16)); // mips 3000 BE
97pub const X_MAGIC = _MAGIC(@as(c_int, 0), @as(c_int, 17)); // att dsp 3210 (retired)
98pub const M_MAGIC = _MAGIC(@as(c_int, 0), @as(c_int, 18)); // mips 4000 BE
99pub const D_MAGIC = _MAGIC(@as(c_int, 0), @as(c_int, 19)); // amd 29000 (retired)
100pub const E_MAGIC = _MAGIC(@as(c_int, 0), @as(c_int, 20)); // arm
101pub const Q_MAGIC = _MAGIC(@as(c_int, 0), @as(c_int, 21)); // powerpc
102pub const N_MAGIC = _MAGIC(@as(c_int, 0), @as(c_int, 22)); // mips 4000 LE
103pub const L_MAGIC = _MAGIC(@as(c_int, 0), @as(c_int, 23)); // dec alpha (retired)
104pub const P_MAGIC = _MAGIC(@as(c_int, 0), @as(c_int, 24)); // mips 3000 LE
105pub const U_MAGIC = _MAGIC(@as(c_int, 0), @as(c_int, 25)); // sparc64
106pub const S_MAGIC = _MAGIC(HDR_MAGIC, @as(c_int, 26)); // amd64
107pub const T_MAGIC = _MAGIC(HDR_MAGIC, @as(c_int, 27)); // powerpc64
108pub const R_MAGIC = _MAGIC(HDR_MAGIC, @as(c_int, 28)); // arm64
109
110pub fn magicFromArch(arch: std.Target.Cpu.Arch) !u32 {
111 return switch (arch) {
112 .i386 => I_MAGIC,
113 .sparc => K_MAGIC, // TODO should sparcv9 and sparcel go here?
114 .mips => V_MAGIC,
115 .arm => E_MAGIC,
116 .aarch64 => R_MAGIC,
117 .powerpc => Q_MAGIC,
118 .powerpc64 => T_MAGIC,
119 .x86_64 => S_MAGIC,
120 else => error.ArchNotSupportedByPlan9,
121 };
122}