authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-07-08 17:30:28-04:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2021-07-08 17:30:28-04:00
log7b8a968f14a8405c209f35d8a0f904d6f722e713
treeb69e161ed8c1eac8a2ed521bd865680e86c1d654
parentb4b90af4e0a9b15a940d836d275ac9dc50198217
parent476faef97ab0f292159bb3eef42078f9b1e43dde
signature Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #9105 from g-w1/plan9

stage2: bringup plan9 target and linker

14 files changed, 703 insertions(+), 33 deletions(-)

CMakeLists.txt+2
......@@ -573,6 +573,8 @@ set(ZIG_STAGE2_SOURCES
573573 "${CMAKE_SOURCE_DIR}/src/link/C.zig"
574574 "${CMAKE_SOURCE_DIR}/src/link/Coff.zig"
575575 "${CMAKE_SOURCE_DIR}/src/link/Elf.zig"
576 "${CMAKE_SOURCE_DIR}/src/link/Plan9.zig"
577 "${CMAKE_SOURCE_DIR}/src/link/Plan9/aout.zig"
576578 "${CMAKE_SOURCE_DIR}/src/link/MachO.zig"
577579 "${CMAKE_SOURCE_DIR}/src/link/MachO/Archive.zig"
578580 "${CMAKE_SOURCE_DIR}/src/link/MachO/CodeSignature.zig"
lib/std/start.zig+40-23
......@@ -95,30 +95,47 @@ fn _start2() callconv(.Naked) noreturn {
9595}
9696
9797fn exit2(code: usize) noreturn {
98 switch (builtin.stage2_arch) {
99 .x86_64 => {
100 asm volatile ("syscall"
101 :
102 : [number] "{rax}" (231),
103 [arg1] "{rdi}" (code)
104 : "rcx", "r11", "memory"
105 );
106 },
107 .arm => {
108 asm volatile ("svc #0"
109 :
110 : [number] "{r7}" (1),
111 [arg1] "{r0}" (code)
112 : "memory"
113 );
98 switch (builtin.stage2_os) {
99 .linux => switch (builtin.stage2_arch) {
100 .x86_64 => {
101 asm volatile ("syscall"
102 :
103 : [number] "{rax}" (231),
104 [arg1] "{rdi}" (code)
105 : "rcx", "r11", "memory"
106 );
107 },
108 .arm => {
109 asm volatile ("svc #0"
110 :
111 : [number] "{r7}" (1),
112 [arg1] "{r0}" (code)
113 : "memory"
114 );
115 },
116 .aarch64 => {
117 asm volatile ("svc #0"
118 :
119 : [number] "{x8}" (93),
120 [arg1] "{x0}" (code)
121 : "memory", "cc"
122 );
123 },
124 else => @compileError("TODO"),
114125 },
115 .aarch64 => {
116 asm volatile ("svc #0"
117 :
118 : [number] "{x8}" (93),
119 [arg1] "{x0}" (code)
120 : "memory", "cc"
121 );
126 // exits(0)
127 .plan9 => switch (builtin.stage2_arch) {
128 .x86_64 => {
129 asm volatile (
130 \\push $0
131 \\push $0
132 \\syscall
133 :
134 : [syscall_number] "{rbp}" (8)
135 : "rcx", "r11", "memory"
136 );
137 },
138 else => @compileError("TODO"),
122139 },
123140 else => @compileError("TODO"),
124141 }
lib/std/target.zig+9
......@@ -60,6 +60,7 @@ pub const Target = struct {
6060 opencl,
6161 glsl450,
6262 vulkan,
63 plan9,
6364 other,
6465
6566 pub fn isDarwin(tag: Tag) bool {
......@@ -262,6 +263,7 @@ pub const Target = struct {
262263 .opencl, // TODO: OpenCL versions
263264 .glsl450, // TODO: GLSL versions
264265 .vulkan,
266 .plan9,
265267 .other,
266268 => return .{ .none = {} },
267269
......@@ -420,6 +422,7 @@ pub const Target = struct {
420422 .opencl,
421423 .glsl450,
422424 .vulkan,
425 .plan9,
423426 .other,
424427 => false,
425428 };
......@@ -515,6 +518,7 @@ pub const Target = struct {
515518 .opencl, // TODO: SPIR-V ABIs with Linkage capability
516519 .glsl450,
517520 .vulkan,
521 .plan9, // TODO specify abi
518522 => return .none,
519523 }
520524 }
......@@ -554,6 +558,7 @@ pub const Target = struct {
554558 spirv,
555559 hex,
556560 raw,
561 plan9,
557562 };
558563
559564 pub const SubSystem = enum {
......@@ -1359,6 +1364,8 @@ pub const Target = struct {
13591364 if (cpu_arch.isSPIRV()) {
13601365 return .spirv;
13611366 }
1367 if (os_tag == .plan9)
1368 return .plan9;
13621369 return .elf;
13631370 }
13641371
......@@ -1432,6 +1439,7 @@ pub const Target = struct {
14321439 .opencl,
14331440 .glsl450,
14341441 .vulkan,
1442 .plan9,
14351443 .other,
14361444 => return false,
14371445 else => return true,
......@@ -1616,6 +1624,7 @@ pub const Target = struct {
16161624 .glsl450,
16171625 .vulkan,
16181626 .other,
1627 .plan9,
16191628 => return result,
16201629
16211630 // TODO revisit when multi-arch for Haiku is available
lib/std/zig.zig+24
......@@ -181,6 +181,30 @@ pub fn binNameAlloc(allocator: *std.mem.Allocator, options: BinNameOptions) erro
181181 .spirv => return std.fmt.allocPrint(allocator, "{s}.spv", .{root_name}),
182182 .hex => return std.fmt.allocPrint(allocator, "{s}.ihex", .{root_name}),
183183 .raw => return std.fmt.allocPrint(allocator, "{s}.bin", .{root_name}),
184 .plan9 => {
185 // copied from 2c(1)
186 // 0c spim little-endian MIPS 3000 family
187 // 1c 68000 Motorola MC68000
188 // 2c 68020 Motorola MC68020
189 // 5c arm little-endian ARM
190 // 6c amd64 AMD64 and compatibles (e.g., Intel EM64T)
191 // 7c arm64 ARM64 (ARMv8)
192 // 8c 386 Intel i386, i486, Pentium, etc.
193 // kc sparc Sun SPARC
194 // qc power Power PC
195 // vc mips big-endian MIPS 3000 family
196 const char: u8 = switch (target.cpu.arch) {
197 .arm => '5',
198 .x86_64 => '6',
199 .aarch64 => '7',
200 .i386 => '8',
201 .sparc => 'k',
202 .powerpc, .powerpcle => 'q',
203 .mips, .mipsel => 'v',
204 else => 'X', // this arch does not have a char or maybe was not ported to plan9 so we just use X
205 };
206 return std.fmt.allocPrint(allocator, "{s}.{c}", .{ root_name, char });
207 },
184208 }
185209}
186210
lib/std/zig/cross_target.zig+2
......@@ -133,6 +133,7 @@ pub const CrossTarget = struct {
133133 .opencl,
134134 .glsl450,
135135 .vulkan,
136 .plan9,
136137 .other,
137138 => {
138139 self.os_version_min = .{ .none = {} };
......@@ -746,6 +747,7 @@ pub const CrossTarget = struct {
746747 .opencl,
747748 .glsl450,
748749 .vulkan,
750 .plan9,
749751 .other,
750752 => return error.InvalidOperatingSystemVersion,
751753
src/Compilation.zig+3
......@@ -3556,6 +3556,8 @@ pub fn generateBuiltinZigSource(comp: *Compilation, allocator: *Allocator) Alloc
35563556 \\pub const zig_is_stage2 = {};
35573557 \\/// Temporary until self-hosted supports the `cpu.arch` value.
35583558 \\pub const stage2_arch: std.Target.Cpu.Arch = .{};
3559 \\/// Temporary until self-hosted supports the `os.tag` value.
3560 \\pub const stage2_os: std.Target.Os.Tag = .{};
35593561 \\
35603562 \\pub const output_mode = std.builtin.OutputMode.{};
35613563 \\pub const link_mode = std.builtin.LinkMode.{};
......@@ -3571,6 +3573,7 @@ pub fn generateBuiltinZigSource(comp: *Compilation, allocator: *Allocator) Alloc
35713573 build_options.version,
35723574 !use_stage1,
35733575 std.zig.fmtId(@tagName(target.cpu.arch)),
3576 std.zig.fmtId(@tagName(target.os.tag)),
35743577 std.zig.fmtId(@tagName(comp.bin_file.options.output_mode)),
35753578 std.zig.fmtId(@tagName(comp.bin_file.options.link_mode)),
35763579 comp.bin_file.options.is_test,
src/Module.zig+8
......@@ -3437,6 +3437,9 @@ fn scanDecl(iter: *ScanDeclIter, decl_sub_index: usize, flags: u4) InnerError!vo
34373437 // in `Decl` to notice that the line number did not change.
34383438 mod.comp.work_queue.writeItemAssumeCapacity(.{ .update_line_number = decl });
34393439 },
3440 .plan9 => {
3441 // TODO implement for plan9
3442 },
34403443 .c, .wasm, .spirv => {},
34413444 }
34423445 }
......@@ -3514,6 +3517,7 @@ pub fn clearDecl(
35143517 .coff => .{ .coff = link.File.Coff.TextBlock.empty },
35153518 .elf => .{ .elf = link.File.Elf.TextBlock.empty },
35163519 .macho => .{ .macho = link.File.MachO.TextBlock.empty },
3520 .plan9 => .{ .plan9 = link.File.Plan9.DeclBlock.empty },
35173521 .c => .{ .c = link.File.C.DeclBlock.empty },
35183522 .wasm => .{ .wasm = link.File.Wasm.DeclBlock.empty },
35193523 .spirv => .{ .spirv = {} },
......@@ -3522,6 +3526,7 @@ pub fn clearDecl(
35223526 .coff => .{ .coff = {} },
35233527 .elf => .{ .elf = link.File.Elf.SrcFn.empty },
35243528 .macho => .{ .macho = link.File.MachO.SrcFn.empty },
3529 .plan9 => .{ .plan9 = {} },
35253530 .c => .{ .c = link.File.C.FnBlock.empty },
35263531 .wasm => .{ .wasm = link.File.Wasm.FnData.empty },
35273532 .spirv => .{ .spirv = .{} },
......@@ -3689,6 +3694,7 @@ fn allocateNewDecl(mod: *Module, namespace: *Scope.Namespace, src_node: ast.Node
36893694 .coff => .{ .coff = link.File.Coff.TextBlock.empty },
36903695 .elf => .{ .elf = link.File.Elf.TextBlock.empty },
36913696 .macho => .{ .macho = link.File.MachO.TextBlock.empty },
3697 .plan9 => .{ .plan9 = link.File.Plan9.DeclBlock.empty },
36923698 .c => .{ .c = link.File.C.DeclBlock.empty },
36933699 .wasm => .{ .wasm = link.File.Wasm.DeclBlock.empty },
36943700 .spirv => .{ .spirv = {} },
......@@ -3697,6 +3703,7 @@ fn allocateNewDecl(mod: *Module, namespace: *Scope.Namespace, src_node: ast.Node
36973703 .coff => .{ .coff = {} },
36983704 .elf => .{ .elf = link.File.Elf.SrcFn.empty },
36993705 .macho => .{ .macho = link.File.MachO.SrcFn.empty },
3706 .plan9 => .{ .plan9 = {} },
37003707 .c => .{ .c = link.File.C.FnBlock.empty },
37013708 .wasm => .{ .wasm = link.File.Wasm.FnData.empty },
37023709 .spirv => .{ .spirv = .{} },
......@@ -3766,6 +3773,7 @@ pub fn analyzeExport(
37663773 .coff => .{ .coff = {} },
37673774 .elf => .{ .elf = link.File.Elf.Export{} },
37683775 .macho => .{ .macho = link.File.MachO.Export{} },
3776 .plan9 => .{ .plan9 = null },
37693777 .c => .{ .c = {} },
37703778 .wasm => .{ .wasm = {} },
37713779 .spirv => .{ .spirv = {} },
src/codegen.zig+96-7
......@@ -2556,9 +2556,60 @@ 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 switch (arch) {
2561 .x86_64 => {
2562 for (info.args) |mc_arg, arg_i| {
2563 const arg = inst.args[arg_i];
2564 const arg_mcv = try self.resolveInst(inst.args[arg_i]);
2565 // Here we do not use setRegOrMem even though the logic is similar, because
2566 // the function call will move the stack pointer, so the offsets are different.
2567 switch (mc_arg) {
2568 .none => continue,
2569 .register => |reg| {
2570 try self.register_manager.getReg(reg, null);
2571 try self.genSetReg(arg.src, arg.ty, reg, arg_mcv);
2572 },
2573 .stack_offset => {
2574 // Here we need to emit instructions like this:
2575 // mov qword ptr [rsp + stack_offset], x
2576 return self.fail(inst.base.src, "TODO implement calling with parameters in memory", .{});
2577 },
2578 .ptr_stack_offset => {
2579 return self.fail(inst.base.src, "TODO implement calling with MCValue.ptr_stack_offset arg", .{});
2580 },
2581 .ptr_embedded_in_code => {
2582 return self.fail(inst.base.src, "TODO implement calling with MCValue.ptr_embedded_in_code arg", .{});
2583 },
2584 .undef => unreachable,
2585 .immediate => unreachable,
2586 .unreach => unreachable,
2587 .dead => unreachable,
2588 .embedded_in_code => unreachable,
2589 .memory => unreachable,
2590 .compare_flags_signed => unreachable,
2591 .compare_flags_unsigned => unreachable,
2592 }
2593 }
2594 if (inst.func.value()) |func_value| {
2595 if (func_value.castTag(.function)) |func_payload| {
2596 const ptr_bits = self.target.cpu.arch.ptrBitWidth();
2597 const ptr_bytes: u64 = @divExact(ptr_bits, 8);
2598 const got_addr = p9.bases.data;
2599 const got_index = func_payload.data.owner_decl.link.plan9.got_index.?;
2600 // ff 14 25 xx xx xx xx call [addr]
2601 try self.code.ensureCapacity(self.code.items.len + 7);
2602 self.code.appendSliceAssumeCapacity(&[3]u8{ 0xff, 0x14, 0x25 });
2603 const fn_got_addr = got_addr + got_index * ptr_bytes;
2604 mem.writeIntLittle(u32, self.code.addManyAsArrayAssumeCapacity(4), @intCast(u32, fn_got_addr));
2605 } else return self.fail(inst.base.src, "TODO implement calling extern fn on plan9", .{});
2606 } else {
2607 return self.fail(inst.base.src, "TODO implement calling runtime known function pointer", .{});
2608 }
2609 },
2610 else => return self.fail(inst.base.src, "TODO implement call on plan9 for {}", .{self.target.cpu.arch}),
2611 }
2612 } else unreachable;
25622613
25632614 switch (info.return_value) {
25642615 .register => |reg| {
......@@ -3279,10 +3330,44 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
32793330 try self.genSetReg(inst.base.src, arg.ty, reg, arg_mcv);
32803331 }
32813332
3282 if (mem.eql(u8, inst.asm_source, "syscall")) {
3283 try self.code.appendSlice(&[_]u8{ 0x0f, 0x05 });
3284 } else if (inst.asm_source.len != 0) {
3285 return self.fail(inst.base.src, "TODO implement support for more x86 assembly instructions", .{});
3333 {
3334 var iter = std.mem.tokenize(inst.asm_source, "\n\r");
3335 while (iter.next()) |ins| {
3336 if (mem.eql(u8, ins, "syscall")) {
3337 try self.code.appendSlice(&[_]u8{ 0x0f, 0x05 });
3338 } else if (mem.indexOf(u8, ins, "push")) |_| {
3339 const arg = ins[4..];
3340 if (mem.indexOf(u8, arg, "$")) |l| {
3341 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", .{});
3342 try self.code.appendSlice(&.{ 0x6a, n });
3343 } else if (mem.indexOf(u8, arg, "%%")) |l| {
3344 const reg_name = ins[4 + l + 2 ..];
3345 const reg = parseRegName(reg_name) orelse
3346 return self.fail(inst.base.src, "unrecognized register: '{s}'", .{reg_name});
3347 const low_id: u8 = reg.low_id();
3348 if (reg.isExtended()) {
3349 try self.code.appendSlice(&.{ 0x41, 0b1010000 | low_id });
3350 } else {
3351 try self.code.append(0b1010000 | low_id);
3352 }
3353 } else return self.fail(inst.base.src, "TODO more push operands", .{});
3354 } else if (mem.indexOf(u8, ins, "pop")) |_| {
3355 const arg = ins[3..];
3356 if (mem.indexOf(u8, arg, "%%")) |l| {
3357 const reg_name = ins[3 + l + 2 ..];
3358 const reg = parseRegName(reg_name) orelse
3359 return self.fail(inst.base.src, "unrecognized register: '{s}'", .{reg_name});
3360 const low_id: u8 = reg.low_id();
3361 if (reg.isExtended()) {
3362 try self.code.appendSlice(&.{ 0x41, 0b1011000 | low_id });
3363 } else {
3364 try self.code.append(0b1011000 | low_id);
3365 }
3366 } else return self.fail(inst.base.src, "TODO more pop operands", .{});
3367 } else {
3368 return self.fail(inst.base.src, "TODO implement support for more x86 assembly instructions", .{});
3369 }
3370 }
32863371 }
32873372
32883373 if (inst.output_constraint) |output| {
......@@ -4223,6 +4308,10 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
42234308 const decl = payload.data;
42244309 const got_addr = coff_file.offset_table_virtual_address + decl.link.coff.offset_table_index * ptr_bytes;
42254310 return MCValue{ .memory = got_addr };
4311 } else if (self.bin_file.cast(link.File.Plan9)) |p9| {
4312 const decl = payload.data;
4313 const got_addr = p9.bases.data + decl.link.plan9.got_index.? * ptr_bytes;
4314 return MCValue{ .memory = got_addr };
42264315 } else {
42274316 return self.fail(src, "TODO codegen non-ELF const Decl pointer", .{});
42284317 }
src/codegen/llvm.zig+1
......@@ -117,6 +117,7 @@ pub fn targetTriple(allocator: *Allocator, target: std.Target) ![:0]u8 {
117117 .opencl => return error.LLVMBackendDoesNotSupportOpenCL,
118118 .glsl450 => return error.LLVMBackendDoesNotSupportGLSL450,
119119 .vulkan => return error.LLVMBackendDoesNotSupportVulkan,
120 .plan9 => return error.LLVMBackendDoesNotSupportPlan9,
120121 .other => "unknown",
121122 };
122123
src/link.zig+24-2
......@@ -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,6 +151,7 @@ pub const File = struct {
150151 elf: Elf.SrcFn,
151152 coff: Coff.SrcFn,
152153 macho: MachO.SrcFn,
154 plan9: void,
153155 c: C.FnBlock,
154156 wasm: Wasm.FnData,
155157 spirv: SpirV.FnData,
......@@ -159,6 +161,7 @@ pub const File = struct {
159161 elf: Elf.Export,
160162 coff: void,
161163 macho: MachO.Export,
164 plan9: Plan9.Export,
162165 c: void,
163166 wasm: void,
164167 spirv: void,
......@@ -189,6 +192,7 @@ pub const File = struct {
189192 .elf => &(try Elf.createEmpty(allocator, options)).base,
190193 .macho => &(try MachO.createEmpty(allocator, options)).base,
191194 .wasm => &(try Wasm.createEmpty(allocator, options)).base,
195 .plan9 => return &(try Plan9.createEmpty(allocator, options)).base,
192196 .c => unreachable, // Reported error earlier.
193197 .spirv => &(try SpirV.createEmpty(allocator, options)).base,
194198 .hex => return error.HexObjectFormatUnimplemented,
......@@ -204,6 +208,7 @@ pub const File = struct {
204208 .coff, .pe => &(try Coff.createEmpty(allocator, options)).base,
205209 .elf => &(try Elf.createEmpty(allocator, options)).base,
206210 .macho => &(try MachO.createEmpty(allocator, options)).base,
211 .plan9 => &(try Plan9.createEmpty(allocator, options)).base,
207212 .wasm => &(try Wasm.createEmpty(allocator, options)).base,
208213 .c => unreachable, // Reported error earlier.
209214 .spirv => &(try SpirV.createEmpty(allocator, options)).base,
......@@ -220,6 +225,7 @@ pub const File = struct {
220225 .coff, .pe => &(try Coff.openPath(allocator, sub_path, options)).base,
221226 .elf => &(try Elf.openPath(allocator, sub_path, options)).base,
222227 .macho => &(try MachO.openPath(allocator, sub_path, options)).base,
228 .plan9 => &(try Plan9.openPath(allocator, sub_path, options)).base,
223229 .wasm => &(try Wasm.openPath(allocator, sub_path, options)).base,
224230 .c => &(try C.openPath(allocator, sub_path, options)).base,
225231 .spirv => &(try SpirV.openPath(allocator, sub_path, options)).base,
......@@ -243,7 +249,7 @@ pub const File = struct {
243249
244250 pub fn makeWritable(base: *File) !void {
245251 switch (base.tag) {
246 .coff, .elf, .macho => {
252 .coff, .elf, .macho, .plan9 => {
247253 if (base.file != null) return;
248254 const emit = base.options.emit orelse return;
249255 base.file = try emit.directory.handle.createFile(emit.sub_path, .{
......@@ -288,7 +294,7 @@ pub const File = struct {
288294 f.close();
289295 base.file = null;
290296 },
291 .coff, .elf => if (base.file) |f| {
297 .coff, .elf, .plan9 => if (base.file) |f| {
292298 if (base.intermediary_basename != null) {
293299 // The file we have open is not the final file that we want to
294300 // make executable, so we don't have to close it.
......@@ -313,6 +319,7 @@ pub const File = struct {
313319 .c => return @fieldParentPtr(C, "base", base).updateDecl(module, decl),
314320 .wasm => return @fieldParentPtr(Wasm, "base", base).updateDecl(module, decl),
315321 .spirv => return @fieldParentPtr(SpirV, "base", base).updateDecl(module, decl),
322 .plan9 => return @fieldParentPtr(Plan9, "base", base).updateDecl(module, decl),
316323 }
317324 }
318325
......@@ -326,6 +333,7 @@ pub const File = struct {
326333 .elf => return @fieldParentPtr(Elf, "base", base).updateDeclLineNumber(module, decl),
327334 .macho => return @fieldParentPtr(MachO, "base", base).updateDeclLineNumber(module, decl),
328335 .c => return @fieldParentPtr(C, "base", base).updateDeclLineNumber(module, decl),
336 .plan9 => @panic("TODO: implement updateDeclLineNumber for plan9"),
329337 .wasm, .spirv => {},
330338 }
331339 }
......@@ -340,6 +348,7 @@ pub const File = struct {
340348 .macho => return @fieldParentPtr(MachO, "base", base).allocateDeclIndexes(decl),
341349 .c => return @fieldParentPtr(C, "base", base).allocateDeclIndexes(decl),
342350 .wasm => return @fieldParentPtr(Wasm, "base", base).allocateDeclIndexes(decl),
351 .plan9 => return @fieldParentPtr(Plan9, "base", base).allocateDeclIndexes(decl),
343352 .spirv => {},
344353 }
345354 }
......@@ -393,6 +402,11 @@ pub const File = struct {
393402 parent.deinit();
394403 base.allocator.destroy(parent);
395404 },
405 .plan9 => {
406 const parent = @fieldParentPtr(Plan9, "base", base);
407 parent.deinit();
408 base.allocator.destroy(parent);
409 },
396410 }
397411 }
398412
......@@ -425,6 +439,7 @@ pub const File = struct {
425439 .c => return @fieldParentPtr(C, "base", base).flush(comp),
426440 .wasm => return @fieldParentPtr(Wasm, "base", base).flush(comp),
427441 .spirv => return @fieldParentPtr(SpirV, "base", base).flush(comp),
442 .plan9 => return @fieldParentPtr(Plan9, "base", base).flush(comp),
428443 }
429444 }
430445
......@@ -438,6 +453,7 @@ pub const File = struct {
438453 .c => return @fieldParentPtr(C, "base", base).flushModule(comp),
439454 .wasm => return @fieldParentPtr(Wasm, "base", base).flushModule(comp),
440455 .spirv => return @fieldParentPtr(SpirV, "base", base).flushModule(comp),
456 .plan9 => return @fieldParentPtr(Plan9, "base", base).flushModule(comp),
441457 }
442458 }
443459
......@@ -451,6 +467,7 @@ pub const File = struct {
451467 .c => @fieldParentPtr(C, "base", base).freeDecl(decl),
452468 .wasm => @fieldParentPtr(Wasm, "base", base).freeDecl(decl),
453469 .spirv => @fieldParentPtr(SpirV, "base", base).freeDecl(decl),
470 .plan9 => @fieldParentPtr(Plan9, "base", base).freeDecl(decl),
454471 }
455472 }
456473
......@@ -459,6 +476,7 @@ pub const File = struct {
459476 .coff => return @fieldParentPtr(Coff, "base", base).error_flags,
460477 .elf => return @fieldParentPtr(Elf, "base", base).error_flags,
461478 .macho => return @fieldParentPtr(MachO, "base", base).error_flags,
479 .plan9 => return @fieldParentPtr(Plan9, "base", base).error_flags,
462480 .c => return .{ .no_entry_point_found = false },
463481 .wasm, .spirv => return ErrorFlags{},
464482 }
......@@ -481,6 +499,7 @@ pub const File = struct {
481499 .c => return @fieldParentPtr(C, "base", base).updateDeclExports(module, decl, exports),
482500 .wasm => return @fieldParentPtr(Wasm, "base", base).updateDeclExports(module, decl, exports),
483501 .spirv => return @fieldParentPtr(SpirV, "base", base).updateDeclExports(module, decl, exports),
502 .plan9 => return @fieldParentPtr(Plan9, "base", base).updateDeclExports(module, decl, exports),
484503 }
485504 }
486505
......@@ -489,6 +508,7 @@ pub const File = struct {
489508 .coff => return @fieldParentPtr(Coff, "base", base).getDeclVAddr(decl),
490509 .elf => return @fieldParentPtr(Elf, "base", base).getDeclVAddr(decl),
491510 .macho => return @fieldParentPtr(MachO, "base", base).getDeclVAddr(decl),
511 .plan9 => @panic("GET VADDR"),
492512 .c => unreachable,
493513 .wasm => unreachable,
494514 .spirv => unreachable,
......@@ -633,6 +653,7 @@ pub const File = struct {
633653 c,
634654 wasm,
635655 spirv,
656 plan9,
636657 };
637658
638659 pub const ErrorFlags = struct {
......@@ -641,6 +662,7 @@ pub const File = struct {
641662
642663 pub const C = @import("link/C.zig");
643664 pub const Coff = @import("link/Coff.zig");
665 pub const Plan9 = @import("link/Plan9.zig");
644666 pub const Elf = @import("link/Elf.zig");
645667 pub const MachO = @import("link/MachO.zig");
646668 pub const SpirV = @import("link/SpirV.zig");
src/link/Plan9.zig created+378
......@@ -0,0 +1,378 @@
1//! This implementation does all the linking work in flush(). A future improvement
2//! would be to add incremental linking in a similar way as ELF does.
3
4const Plan9 = @This();
5
6const std = @import("std");
7const link = @import("../link.zig");
8const Module = @import("../Module.zig");
9const Compilation = @import("../Compilation.zig");
10const aout = @import("Plan9/aout.zig");
11const codegen = @import("../codegen.zig");
12const trace = @import("../tracy.zig").trace;
13const mem = std.mem;
14const File = link.File;
15const Allocator = std.mem.Allocator;
16
17const log = std.log.scoped(.link);
18const assert = std.debug.assert;
19
20base: link.File,
21sixtyfour_bit: bool,
22error_flags: File.ErrorFlags = File.ErrorFlags{},
23bases: Bases,
24
25decl_table: std.AutoArrayHashMapUnmanaged(*Module.Decl, void) = .{},
26/// is just casted down when 32 bit
27syms: std.ArrayListUnmanaged(aout.Sym) = .{},
28text_buf: std.ArrayListUnmanaged(u8) = .{},
29data_buf: std.ArrayListUnmanaged(u8) = .{},
30
31hdr: aout.ExecHdr = undefined,
32
33entry_decl: ?*Module.Decl = null,
34
35got: std.ArrayListUnmanaged(u64) = .{},
36const Bases = struct {
37 text: u64,
38 /// the addr of the got
39 data: u64,
40};
41
42fn getAddr(self: Plan9, addr: u64, t: aout.Sym.Type) u64 {
43 return addr + switch (t) {
44 .T, .t, .l, .L => self.bases.text,
45 .D, .d, .B, .b => self.bases.data,
46 else => unreachable,
47 };
48}
49/// opposite of getAddr
50fn takeAddr(self: Plan9, addr: u64, t: aout.Sym.Type) u64 {
51 return addr - switch (t) {
52 .T, .t, .l, .L => self.bases.text,
53 .D, .d, .B, .b => self.bases.data,
54 else => unreachable,
55 };
56}
57
58fn getSymAddr(self: Plan9, s: aout.Sym) u64 {
59 return self.getAddr(s.value, s.type);
60}
61
62pub const DeclBlock = struct {
63 type: aout.Sym.Type,
64 /// offset in the text or data sects
65 offset: ?u64,
66 /// offset into syms
67 sym_index: ?usize,
68 /// offset into got
69 got_index: ?usize,
70 pub const empty = DeclBlock{
71 .type = .t,
72 .offset = null,
73 .sym_index = null,
74 .got_index = null,
75 };
76};
77
78pub fn defaultBaseAddrs(arch: std.Target.Cpu.Arch) Bases {
79 return switch (arch) {
80 .x86_64 => .{
81 // 0x28 => 40 == header size
82 .text = 0x200028,
83 .data = 0x400000,
84 },
85 .i386 => .{
86 // 0x20 => 32 == header size
87 .text = 0x200020,
88 .data = 0x400000,
89 },
90 else => std.debug.panic("find default base address for {}", .{arch}),
91 };
92}
93
94pub const PtrWidth = enum { p32, p64 };
95
96pub fn createEmpty(gpa: *Allocator, options: link.Options) !*Plan9 {
97 if (options.use_llvm)
98 return error.LLVMBackendDoesNotSupportPlan9;
99 const sixtyfour_bit: bool = switch (options.target.cpu.arch.ptrBitWidth()) {
100 0...32 => false,
101 33...64 => true,
102 else => return error.UnsupportedP9Architecture,
103 };
104 const self = try gpa.create(Plan9);
105 self.* = .{
106 .base = .{
107 .tag = .plan9,
108 .options = options,
109 .allocator = gpa,
110 .file = null,
111 },
112 .sixtyfour_bit = sixtyfour_bit,
113 .bases = undefined,
114 };
115 return self;
116}
117
118pub fn updateDecl(self: *Plan9, module: *Module, decl: *Module.Decl) !void {
119 _ = module;
120 _ = try self.decl_table.getOrPut(self.base.allocator, decl);
121}
122
123pub fn flush(self: *Plan9, comp: *Compilation) !void {
124 assert(!self.base.options.use_lld);
125
126 switch (self.base.options.effectiveOutputMode()) {
127 .Exe => {},
128 // plan9 object files are totally different
129 .Obj => return error.TODOImplementPlan9Objs,
130 .Lib => return error.TODOImplementWritingLibFiles,
131 }
132 return self.flushModule(comp);
133}
134
135pub fn flushModule(self: *Plan9, comp: *Compilation) !void {
136 _ = comp;
137 const tracy = trace(@src());
138 defer tracy.end();
139
140 log.debug("flushModule", .{});
141
142 defer assert(self.hdr.entry != 0x0);
143
144 const module = self.base.options.module orelse return error.LinkingWithoutZigSourceUnimplemented;
145
146 self.text_buf.items.len = 0;
147 self.data_buf.items.len = 0;
148 // ensure space to write the got later
149 assert(self.got.items.len == self.decl_table.count());
150 try self.data_buf.appendNTimes(self.base.allocator, 0x69, self.got.items.len * if (!self.sixtyfour_bit) @as(u32, 4) else 8);
151 // temporary buffer
152 var code_buffer = std.ArrayList(u8).init(self.base.allocator);
153 defer code_buffer.deinit();
154 {
155 for (self.decl_table.keys()) |decl| {
156 if (!decl.has_tv) continue;
157 const is_fn = (decl.ty.zigTypeTag() == .Fn);
158
159 log.debug("update the symbol table and got for decl {*} ({s})", .{ decl, decl.name });
160 decl.link.plan9 = if (is_fn) .{
161 .offset = self.getAddr(self.text_buf.items.len, .t),
162 .type = .t,
163 .sym_index = decl.link.plan9.sym_index,
164 .got_index = decl.link.plan9.got_index,
165 } else .{
166 .offset = self.getAddr(self.data_buf.items.len, .d),
167 .type = .d,
168 .sym_index = decl.link.plan9.sym_index,
169 .got_index = decl.link.plan9.got_index,
170 };
171 self.got.items[decl.link.plan9.got_index.?] = decl.link.plan9.offset.?;
172 if (decl.link.plan9.sym_index) |s| {
173 self.syms.items[s] = .{
174 .value = decl.link.plan9.offset.?,
175 .type = decl.link.plan9.type,
176 .name = mem.span(decl.name),
177 };
178 } else {
179 try self.syms.append(self.base.allocator, .{
180 .value = decl.link.plan9.offset.?,
181 .type = decl.link.plan9.type,
182 .name = mem.span(decl.name),
183 });
184 decl.link.plan9.sym_index = self.syms.items.len - 1;
185 }
186
187 if (module.decl_exports.get(decl)) |exports| {
188 for (exports) |exp| {
189 // plan9 does not support custom sections
190 if (exp.options.section) |section_name| {
191 if (!mem.eql(u8, section_name, ".text") or !mem.eql(u8, section_name, ".data")) {
192 try module.failed_exports.put(module.gpa, exp, try Module.ErrorMsg.create(self.base.allocator, decl.srcLoc(), "plan9 does not support extra sections", .{}));
193 break;
194 }
195 }
196 if (std.mem.eql(u8, exp.options.name, "_start")) {
197 std.debug.assert(decl.link.plan9.type == .t); // we tried to link a non-function as the entry
198 self.entry_decl = decl;
199 }
200 if (exp.link.plan9) |i| {
201 self.syms.items[i] = .{
202 .value = decl.link.plan9.offset.?,
203 .type = decl.link.plan9.type.toGlobal(),
204 .name = exp.options.name,
205 };
206 } else {
207 try self.syms.append(self.base.allocator, .{
208 .value = decl.link.plan9.offset.?,
209 .type = decl.link.plan9.type.toGlobal(),
210 .name = exp.options.name,
211 });
212 exp.link.plan9 = self.syms.items.len - 1;
213 }
214 }
215 }
216
217 log.debug("codegen decl {*} ({s})", .{ decl, decl.name });
218 const res = try codegen.generateSymbol(&self.base, decl.srcLoc(), .{
219 .ty = decl.ty,
220 .val = decl.val,
221 }, &code_buffer, .{ .none = {} });
222 const code = switch (res) {
223 .externally_managed => |x| x,
224 .appended => code_buffer.items,
225 .fail => |em| {
226 decl.analysis = .codegen_failure;
227 try module.failed_decls.put(module.gpa, decl, em);
228 // TODO try to do more decls
229 return;
230 },
231 };
232 if (is_fn) {
233 try self.text_buf.appendSlice(self.base.allocator, code);
234 code_buffer.items.len = 0;
235 } else {
236 try self.data_buf.appendSlice(self.base.allocator, code);
237 code_buffer.items.len = 0;
238 }
239 }
240 }
241
242 // write the got
243 if (!self.sixtyfour_bit) {
244 for (self.got.items) |p, i| {
245 mem.writeInt(u32, self.data_buf.items[i * 4 ..][0..4], @intCast(u32, p), self.base.options.target.cpu.arch.endian());
246 }
247 } else {
248 for (self.got.items) |p, i| {
249 mem.writeInt(u64, self.data_buf.items[i * 8 ..][0..8], p, self.base.options.target.cpu.arch.endian());
250 }
251 }
252
253 self.hdr.entry = @truncate(u32, self.entry_decl.?.link.plan9.offset.?);
254
255 // edata, end, etext
256 self.syms.items[0].value = self.getAddr(0x0, .b);
257 self.syms.items[1].value = self.getAddr(0x0, .b);
258 self.syms.items[2].value = self.getAddr(self.text_buf.items.len, .t);
259
260 var sym_buf = std.ArrayList(u8).init(self.base.allocator);
261 defer sym_buf.deinit();
262 try self.writeSyms(&sym_buf);
263
264 // generate the header
265 self.hdr = .{
266 .magic = try aout.magicFromArch(self.base.options.target.cpu.arch),
267 .text = @intCast(u32, self.text_buf.items.len),
268 .data = @intCast(u32, self.data_buf.items.len),
269 .syms = @intCast(u32, sym_buf.items.len),
270 .bss = 0,
271 .pcsz = 0,
272 .spsz = 0,
273 .entry = self.hdr.entry,
274 };
275
276 const file = self.base.file.?;
277
278 var hdr_buf = self.hdr.toU8s();
279 const hdr_slice: []const u8 = &hdr_buf;
280 // account for the fat header
281 const hdr_size: u8 = if (!self.sixtyfour_bit) 32 else 40;
282 // write the fat header for 64 bit entry points
283 if (self.sixtyfour_bit) {
284 mem.writeIntSliceBig(u64, hdr_buf[32..40], self.hdr.entry);
285 }
286 // write it all!
287 var vectors: [4]std.os.iovec_const = .{
288 .{ .iov_base = hdr_slice.ptr, .iov_len = hdr_size },
289 .{ .iov_base = self.text_buf.items.ptr, .iov_len = self.text_buf.items.len },
290 .{ .iov_base = self.data_buf.items.ptr, .iov_len = self.data_buf.items.len },
291 .{ .iov_base = sym_buf.items.ptr, .iov_len = sym_buf.items.len },
292 // TODO spsz, pcsz
293 };
294 try file.pwritevAll(&vectors, 0);
295}
296pub fn freeDecl(self: *Plan9, decl: *Module.Decl) void {
297 assert(self.decl_table.swapRemove(decl));
298}
299
300pub fn updateDeclExports(
301 self: *Plan9,
302 module: *Module,
303 decl: *Module.Decl,
304 exports: []const *Module.Export,
305) !void {
306 // we do all the things in flush
307 _ = self;
308 _ = module;
309 _ = decl;
310 _ = exports;
311}
312pub fn deinit(self: *Plan9) void {
313 self.decl_table.deinit(self.base.allocator);
314 self.syms.deinit(self.base.allocator);
315 self.text_buf.deinit(self.base.allocator);
316 self.data_buf.deinit(self.base.allocator);
317 self.got.deinit(self.base.allocator);
318}
319
320pub const Export = ?usize;
321pub const base_tag = .plan9;
322pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Options) !*Plan9 {
323 if (options.use_llvm)
324 return error.LLVMBackendDoesNotSupportPlan9;
325 assert(options.object_format == .plan9);
326 const file = try options.emit.?.directory.handle.createFile(sub_path, .{
327 .truncate = false,
328 .read = true,
329 .mode = link.determineMode(options),
330 });
331 errdefer file.close();
332
333 const self = try createEmpty(allocator, options);
334 errdefer self.base.destroy();
335
336 self.bases = defaultBaseAddrs(options.target.cpu.arch);
337
338 // first 3 symbols in our table are edata, end, etext
339 try self.syms.appendSlice(self.base.allocator, &.{
340 .{
341 .value = 0xcafebabe,
342 .type = .B,
343 .name = "edata",
344 },
345 .{
346 .value = 0xcafebabe,
347 .type = .B,
348 .name = "end",
349 },
350 .{
351 .value = 0xcafebabe,
352 .type = .T,
353 .name = "etext",
354 },
355 });
356
357 self.base.file = file;
358 return self;
359}
360
361pub fn writeSyms(self: *Plan9, buf: *std.ArrayList(u8)) !void {
362 const writer = buf.writer();
363 for (self.syms.items) |sym| {
364 if (!self.sixtyfour_bit) {
365 try writer.writeIntBig(u32, @intCast(u32, sym.value));
366 } else {
367 try writer.writeIntBig(u64, sym.value);
368 }
369 try writer.writeByte(@enumToInt(sym.type));
370 try writer.writeAll(std.mem.span(sym.name));
371 try writer.writeByte(0);
372 }
373}
374
375pub fn allocateDeclIndexes(self: *Plan9, decl: *Module.Decl) !void {
376 try self.got.append(self.base.allocator, 0xdeadbeef);
377 decl.link.plan9.got_index = self.got.items.len - 1;
378}
src/link/Plan9/aout.zig created+114
......@@ -0,0 +1,114 @@
1const std = @import("std");
2const assert = std.debug.assert;
3
4/// All integers are in big-endian format (needs a byteswap).
5pub const ExecHdr = extern struct {
6 magic: u32,
7 text: u32,
8 data: u32,
9 bss: u32,
10 syms: u32,
11 /// You should truncate this to 32 bits on 64 bit systems, then but the actual 8 bytes
12 /// in the fat header.
13 entry: u32,
14 spsz: u32,
15 pcsz: u32,
16 comptime {
17 assert(@sizeOf(@This()) == 32);
18 }
19 /// It is up to the caller to disgard the last 8 bytes if the header is not fat.
20 pub fn toU8s(self: *@This()) [40]u8 {
21 var buf: [40]u8 = undefined;
22 var i: u8 = 0;
23 inline for (std.meta.fields(@This())) |f| {
24 std.mem.writeIntSliceBig(u32, buf[i .. i + 4], @field(self, f.name));
25 i += 4;
26 }
27 return buf;
28 }
29};
30
31pub const Sym = struct {
32 /// Big endian in the file
33 value: u64,
34 type: Type,
35 name: []const u8,
36
37 /// The type field is one of the following characters with the
38 /// high bit set:
39 /// T text segment symbol
40 /// t static text segment symbol
41 /// L leaf function text segment symbol
42 /// l static leaf function text segment symbol
43 /// D data segment symbol
44 /// d static data segment symbol
45 /// B bss segment symbol
46 /// b static bss segment symbol
47 /// a automatic (local) variable symbol
48 /// p function parameter symbol
49 /// f source file name components
50 /// z source file name
51 /// Z source file line offset
52 /// m for '.frame'
53 pub const Type = enum(u8) {
54 T = 0x80 | 'T',
55 t = 0x80 | 't',
56 L = 0x80 | 'L',
57 l = 0x80 | 'l',
58 D = 0x80 | 'D',
59 d = 0x80 | 'd',
60 B = 0x80 | 'B',
61 b = 0x80 | 'b',
62 a = 0x80 | 'a',
63 p = 0x80 | 'p',
64 f = 0x80 | 'f',
65 z = 0x80 | 'z',
66 Z = 0x80 | 'Z',
67 m = 0x80 | 'm',
68
69 pub fn toGlobal(self: Type) Type {
70 return switch (self) {
71 .t => .T,
72 .b => .B,
73 .d => .D,
74 else => unreachable,
75 };
76 }
77 };
78};
79
80pub const HDR_MAGIC = 0x00008000;
81pub inline fn _MAGIC(f: anytype, b: anytype) @TypeOf(f | ((((@as(c_int, 4) * b) + @as(c_int, 0)) * b) + @as(c_int, 7))) {
82 return f | ((((@as(c_int, 4) * b) + @as(c_int, 0)) * b) + @as(c_int, 7));
83}
84pub const A_MAGIC = _MAGIC(0, 8); // 68020
85pub const I_MAGIC = _MAGIC(0, 11); // intel 386
86pub const J_MAGIC = _MAGIC(0, 12); // intel 960 (retired)
87pub const K_MAGIC = _MAGIC(0, 13); // sparc
88pub const V_MAGIC = _MAGIC(0, 16); // mips 3000 BE
89pub const X_MAGIC = _MAGIC(0, 17); // att dsp 3210 (retired)
90pub const M_MAGIC = _MAGIC(0, 18); // mips 4000 BE
91pub const D_MAGIC = _MAGIC(0, 19); // amd 29000 (retired)
92pub const E_MAGIC = _MAGIC(0, 20); // arm
93pub const Q_MAGIC = _MAGIC(0, 21); // powerpc
94pub const N_MAGIC = _MAGIC(0, 22); // mips 4000 LE
95pub const L_MAGIC = _MAGIC(0, 23); // dec alpha (retired)
96pub const P_MAGIC = _MAGIC(0, 24); // mips 3000 LE
97pub const U_MAGIC = _MAGIC(0, 25); // sparc64
98pub const S_MAGIC = _MAGIC(HDR_MAGIC, 26); // amd64
99pub const T_MAGIC = _MAGIC(HDR_MAGIC, 27); // powerpc64
100pub const R_MAGIC = _MAGIC(HDR_MAGIC, 28); // arm64
101
102pub fn magicFromArch(arch: std.Target.Cpu.Arch) !u32 {
103 return switch (arch) {
104 .i386 => I_MAGIC,
105 .sparc => K_MAGIC, // TODO should sparcv9 and sparcel go here?
106 .mips => V_MAGIC,
107 .arm => E_MAGIC,
108 .aarch64 => R_MAGIC,
109 .powerpc => Q_MAGIC,
110 .powerpc64 => T_MAGIC,
111 .x86_64 => S_MAGIC,
112 else => error.ArchNotSupportedByPlan9,
113 };
114}
src/target.zig+1-1
......@@ -244,7 +244,7 @@ pub fn supportsStackProbing(target: std.Target) bool {
244244
245245pub fn osToLLVM(os_tag: std.Target.Os.Tag) llvm.OSType {
246246 return switch (os_tag) {
247 .freestanding, .other, .opencl, .glsl450, .vulkan => .UnknownOS,
247 .freestanding, .other, .opencl, .glsl450, .vulkan, .plan9 => .UnknownOS,
248248 .windows, .uefi => .Win32,
249249 .ananas => .Ananas,
250250 .cloudabi => .CloudABI,
src/type.zig+1
......@@ -3290,6 +3290,7 @@ pub const CType = enum {
32903290 .openbsd,
32913291 .wasi,
32923292 .emscripten,
3293 .plan9,
32933294 => switch (self) {
32943295 .short,
32953296 .ushort,