| author | |
| committer | |
| log | 7b8a968f14a8405c209f35d8a0f904d6f722e713 |
| tree | b69e161ed8c1eac8a2ed521bd865680e86c1d654 |
| parent | b4b90af4e0a9b15a940d836d275ac9dc50198217 |
| parent | 476faef97ab0f292159bb3eef42078f9b1e43dde |
| signature | Signed by PGP key 4AEE18F83AFDEB23 |
stage2: bringup plan9 target and linker14 files changed, 703 insertions(+), 33 deletions(-)
CMakeLists.txt+2| ... | ... | @@ -573,6 +573,8 @@ set(ZIG_STAGE2_SOURCES |
| 573 | 573 | "${CMAKE_SOURCE_DIR}/src/link/C.zig" |
| 574 | 574 | "${CMAKE_SOURCE_DIR}/src/link/Coff.zig" |
| 575 | 575 | "${CMAKE_SOURCE_DIR}/src/link/Elf.zig" |
| 576 | "${CMAKE_SOURCE_DIR}/src/link/Plan9.zig" | |
| 577 | "${CMAKE_SOURCE_DIR}/src/link/Plan9/aout.zig" | |
| 576 | 578 | "${CMAKE_SOURCE_DIR}/src/link/MachO.zig" |
| 577 | 579 | "${CMAKE_SOURCE_DIR}/src/link/MachO/Archive.zig" |
| 578 | 580 | "${CMAKE_SOURCE_DIR}/src/link/MachO/CodeSignature.zig" |
lib/std/start.zig+40-23| ... | ... | @@ -95,30 +95,47 @@ fn _start2() callconv(.Naked) noreturn { |
| 95 | 95 | } |
| 96 | 96 | |
| 97 | 97 | fn 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"), | |
| 114 | 125 | }, |
| 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"), | |
| 122 | 139 | }, |
| 123 | 140 | else => @compileError("TODO"), |
| 124 | 141 | } |
lib/std/target.zig+9| ... | ... | @@ -60,6 +60,7 @@ pub const Target = struct { |
| 60 | 60 | opencl, |
| 61 | 61 | glsl450, |
| 62 | 62 | vulkan, |
| 63 | plan9, | |
| 63 | 64 | other, |
| 64 | 65 | |
| 65 | 66 | pub fn isDarwin(tag: Tag) bool { |
| ... | ... | @@ -262,6 +263,7 @@ pub const Target = struct { |
| 262 | 263 | .opencl, // TODO: OpenCL versions |
| 263 | 264 | .glsl450, // TODO: GLSL versions |
| 264 | 265 | .vulkan, |
| 266 | .plan9, | |
| 265 | 267 | .other, |
| 266 | 268 | => return .{ .none = {} }, |
| 267 | 269 | |
| ... | ... | @@ -420,6 +422,7 @@ pub const Target = struct { |
| 420 | 422 | .opencl, |
| 421 | 423 | .glsl450, |
| 422 | 424 | .vulkan, |
| 425 | .plan9, | |
| 423 | 426 | .other, |
| 424 | 427 | => false, |
| 425 | 428 | }; |
| ... | ... | @@ -515,6 +518,7 @@ pub const Target = struct { |
| 515 | 518 | .opencl, // TODO: SPIR-V ABIs with Linkage capability |
| 516 | 519 | .glsl450, |
| 517 | 520 | .vulkan, |
| 521 | .plan9, // TODO specify abi | |
| 518 | 522 | => return .none, |
| 519 | 523 | } |
| 520 | 524 | } |
| ... | ... | @@ -554,6 +558,7 @@ pub const Target = struct { |
| 554 | 558 | spirv, |
| 555 | 559 | hex, |
| 556 | 560 | raw, |
| 561 | plan9, | |
| 557 | 562 | }; |
| 558 | 563 | |
| 559 | 564 | pub const SubSystem = enum { |
| ... | ... | @@ -1359,6 +1364,8 @@ pub const Target = struct { |
| 1359 | 1364 | if (cpu_arch.isSPIRV()) { |
| 1360 | 1365 | return .spirv; |
| 1361 | 1366 | } |
| 1367 | if (os_tag == .plan9) | |
| 1368 | return .plan9; | |
| 1362 | 1369 | return .elf; |
| 1363 | 1370 | } |
| 1364 | 1371 | |
| ... | ... | @@ -1432,6 +1439,7 @@ pub const Target = struct { |
| 1432 | 1439 | .opencl, |
| 1433 | 1440 | .glsl450, |
| 1434 | 1441 | .vulkan, |
| 1442 | .plan9, | |
| 1435 | 1443 | .other, |
| 1436 | 1444 | => return false, |
| 1437 | 1445 | else => return true, |
| ... | ... | @@ -1616,6 +1624,7 @@ pub const Target = struct { |
| 1616 | 1624 | .glsl450, |
| 1617 | 1625 | .vulkan, |
| 1618 | 1626 | .other, |
| 1627 | .plan9, | |
| 1619 | 1628 | => return result, |
| 1620 | 1629 | |
| 1621 | 1630 | // 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 |
| 181 | 181 | .spirv => return std.fmt.allocPrint(allocator, "{s}.spv", .{root_name}), |
| 182 | 182 | .hex => return std.fmt.allocPrint(allocator, "{s}.ihex", .{root_name}), |
| 183 | 183 | .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 | }, | |
| 184 | 208 | } |
| 185 | 209 | } |
| 186 | 210 |
lib/std/zig/cross_target.zig+2| ... | ... | @@ -133,6 +133,7 @@ pub const CrossTarget = struct { |
| 133 | 133 | .opencl, |
| 134 | 134 | .glsl450, |
| 135 | 135 | .vulkan, |
| 136 | .plan9, | |
| 136 | 137 | .other, |
| 137 | 138 | => { |
| 138 | 139 | self.os_version_min = .{ .none = {} }; |
| ... | ... | @@ -746,6 +747,7 @@ pub const CrossTarget = struct { |
| 746 | 747 | .opencl, |
| 747 | 748 | .glsl450, |
| 748 | 749 | .vulkan, |
| 750 | .plan9, | |
| 749 | 751 | .other, |
| 750 | 752 | => return error.InvalidOperatingSystemVersion, |
| 751 | 753 |
src/Compilation.zig+3| ... | ... | @@ -3556,6 +3556,8 @@ pub fn generateBuiltinZigSource(comp: *Compilation, allocator: *Allocator) Alloc |
| 3556 | 3556 | \\pub const zig_is_stage2 = {}; |
| 3557 | 3557 | \\/// Temporary until self-hosted supports the `cpu.arch` value. |
| 3558 | 3558 | \\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 = .{}; | |
| 3559 | 3561 | \\ |
| 3560 | 3562 | \\pub const output_mode = std.builtin.OutputMode.{}; |
| 3561 | 3563 | \\pub const link_mode = std.builtin.LinkMode.{}; |
| ... | ... | @@ -3571,6 +3573,7 @@ pub fn generateBuiltinZigSource(comp: *Compilation, allocator: *Allocator) Alloc |
| 3571 | 3573 | build_options.version, |
| 3572 | 3574 | !use_stage1, |
| 3573 | 3575 | std.zig.fmtId(@tagName(target.cpu.arch)), |
| 3576 | std.zig.fmtId(@tagName(target.os.tag)), | |
| 3574 | 3577 | std.zig.fmtId(@tagName(comp.bin_file.options.output_mode)), |
| 3575 | 3578 | std.zig.fmtId(@tagName(comp.bin_file.options.link_mode)), |
| 3576 | 3579 | 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 |
| 3437 | 3437 | // in `Decl` to notice that the line number did not change. |
| 3438 | 3438 | mod.comp.work_queue.writeItemAssumeCapacity(.{ .update_line_number = decl }); |
| 3439 | 3439 | }, |
| 3440 | .plan9 => { | |
| 3441 | // TODO implement for plan9 | |
| 3442 | }, | |
| 3440 | 3443 | .c, .wasm, .spirv => {}, |
| 3441 | 3444 | } |
| 3442 | 3445 | } |
| ... | ... | @@ -3514,6 +3517,7 @@ pub fn clearDecl( |
| 3514 | 3517 | .coff => .{ .coff = link.File.Coff.TextBlock.empty }, |
| 3515 | 3518 | .elf => .{ .elf = link.File.Elf.TextBlock.empty }, |
| 3516 | 3519 | .macho => .{ .macho = link.File.MachO.TextBlock.empty }, |
| 3520 | .plan9 => .{ .plan9 = link.File.Plan9.DeclBlock.empty }, | |
| 3517 | 3521 | .c => .{ .c = link.File.C.DeclBlock.empty }, |
| 3518 | 3522 | .wasm => .{ .wasm = link.File.Wasm.DeclBlock.empty }, |
| 3519 | 3523 | .spirv => .{ .spirv = {} }, |
| ... | ... | @@ -3522,6 +3526,7 @@ pub fn clearDecl( |
| 3522 | 3526 | .coff => .{ .coff = {} }, |
| 3523 | 3527 | .elf => .{ .elf = link.File.Elf.SrcFn.empty }, |
| 3524 | 3528 | .macho => .{ .macho = link.File.MachO.SrcFn.empty }, |
| 3529 | .plan9 => .{ .plan9 = {} }, | |
| 3525 | 3530 | .c => .{ .c = link.File.C.FnBlock.empty }, |
| 3526 | 3531 | .wasm => .{ .wasm = link.File.Wasm.FnData.empty }, |
| 3527 | 3532 | .spirv => .{ .spirv = .{} }, |
| ... | ... | @@ -3689,6 +3694,7 @@ fn allocateNewDecl(mod: *Module, namespace: *Scope.Namespace, src_node: ast.Node |
| 3689 | 3694 | .coff => .{ .coff = link.File.Coff.TextBlock.empty }, |
| 3690 | 3695 | .elf => .{ .elf = link.File.Elf.TextBlock.empty }, |
| 3691 | 3696 | .macho => .{ .macho = link.File.MachO.TextBlock.empty }, |
| 3697 | .plan9 => .{ .plan9 = link.File.Plan9.DeclBlock.empty }, | |
| 3692 | 3698 | .c => .{ .c = link.File.C.DeclBlock.empty }, |
| 3693 | 3699 | .wasm => .{ .wasm = link.File.Wasm.DeclBlock.empty }, |
| 3694 | 3700 | .spirv => .{ .spirv = {} }, |
| ... | ... | @@ -3697,6 +3703,7 @@ fn allocateNewDecl(mod: *Module, namespace: *Scope.Namespace, src_node: ast.Node |
| 3697 | 3703 | .coff => .{ .coff = {} }, |
| 3698 | 3704 | .elf => .{ .elf = link.File.Elf.SrcFn.empty }, |
| 3699 | 3705 | .macho => .{ .macho = link.File.MachO.SrcFn.empty }, |
| 3706 | .plan9 => .{ .plan9 = {} }, | |
| 3700 | 3707 | .c => .{ .c = link.File.C.FnBlock.empty }, |
| 3701 | 3708 | .wasm => .{ .wasm = link.File.Wasm.FnData.empty }, |
| 3702 | 3709 | .spirv => .{ .spirv = .{} }, |
| ... | ... | @@ -3766,6 +3773,7 @@ pub fn analyzeExport( |
| 3766 | 3773 | .coff => .{ .coff = {} }, |
| 3767 | 3774 | .elf => .{ .elf = link.File.Elf.Export{} }, |
| 3768 | 3775 | .macho => .{ .macho = link.File.MachO.Export{} }, |
| 3776 | .plan9 => .{ .plan9 = null }, | |
| 3769 | 3777 | .c => .{ .c = {} }, |
| 3770 | 3778 | .wasm => .{ .wasm = {} }, |
| 3771 | 3779 | .spirv => .{ .spirv = {} }, |
src/codegen.zig+96-7| ... | ... | @@ -2556,9 +2556,60 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 2556 | 2556 | } else { |
| 2557 | 2557 | return self.fail(inst.base.src, "TODO implement calling runtime known function pointer", .{}); |
| 2558 | 2558 | } |
| 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; | |
| 2562 | 2613 | |
| 2563 | 2614 | switch (info.return_value) { |
| 2564 | 2615 | .register => |reg| { |
| ... | ... | @@ -3279,10 +3330,44 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 3279 | 3330 | try self.genSetReg(inst.base.src, arg.ty, reg, arg_mcv); |
| 3280 | 3331 | } |
| 3281 | 3332 | |
| 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 | } | |
| 3286 | 3371 | } |
| 3287 | 3372 | |
| 3288 | 3373 | if (inst.output_constraint) |output| { |
| ... | ... | @@ -4223,6 +4308,10 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 4223 | 4308 | const decl = payload.data; |
| 4224 | 4309 | const got_addr = coff_file.offset_table_virtual_address + decl.link.coff.offset_table_index * ptr_bytes; |
| 4225 | 4310 | 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 }; | |
| 4226 | 4315 | } else { |
| 4227 | 4316 | return self.fail(src, "TODO codegen non-ELF const Decl pointer", .{}); |
| 4228 | 4317 | } |
src/codegen/llvm.zig+1| ... | ... | @@ -117,6 +117,7 @@ pub fn targetTriple(allocator: *Allocator, target: std.Target) ![:0]u8 { |
| 117 | 117 | .opencl => return error.LLVMBackendDoesNotSupportOpenCL, |
| 118 | 118 | .glsl450 => return error.LLVMBackendDoesNotSupportGLSL450, |
| 119 | 119 | .vulkan => return error.LLVMBackendDoesNotSupportVulkan, |
| 120 | .plan9 => return error.LLVMBackendDoesNotSupportPlan9, | |
| 120 | 121 | .other => "unknown", |
| 121 | 122 | }; |
| 122 | 123 |
src/link.zig+24-2| ... | ... | @@ -141,6 +141,7 @@ pub const File = struct { |
| 141 | 141 | elf: Elf.TextBlock, |
| 142 | 142 | coff: Coff.TextBlock, |
| 143 | 143 | macho: MachO.TextBlock, |
| 144 | plan9: Plan9.DeclBlock, | |
| 144 | 145 | c: C.DeclBlock, |
| 145 | 146 | wasm: Wasm.DeclBlock, |
| 146 | 147 | spirv: void, |
| ... | ... | @@ -150,6 +151,7 @@ pub const File = struct { |
| 150 | 151 | elf: Elf.SrcFn, |
| 151 | 152 | coff: Coff.SrcFn, |
| 152 | 153 | macho: MachO.SrcFn, |
| 154 | plan9: void, | |
| 153 | 155 | c: C.FnBlock, |
| 154 | 156 | wasm: Wasm.FnData, |
| 155 | 157 | spirv: SpirV.FnData, |
| ... | ... | @@ -159,6 +161,7 @@ pub const File = struct { |
| 159 | 161 | elf: Elf.Export, |
| 160 | 162 | coff: void, |
| 161 | 163 | macho: MachO.Export, |
| 164 | plan9: Plan9.Export, | |
| 162 | 165 | c: void, |
| 163 | 166 | wasm: void, |
| 164 | 167 | spirv: void, |
| ... | ... | @@ -189,6 +192,7 @@ pub const File = struct { |
| 189 | 192 | .elf => &(try Elf.createEmpty(allocator, options)).base, |
| 190 | 193 | .macho => &(try MachO.createEmpty(allocator, options)).base, |
| 191 | 194 | .wasm => &(try Wasm.createEmpty(allocator, options)).base, |
| 195 | .plan9 => return &(try Plan9.createEmpty(allocator, options)).base, | |
| 192 | 196 | .c => unreachable, // Reported error earlier. |
| 193 | 197 | .spirv => &(try SpirV.createEmpty(allocator, options)).base, |
| 194 | 198 | .hex => return error.HexObjectFormatUnimplemented, |
| ... | ... | @@ -204,6 +208,7 @@ pub const File = struct { |
| 204 | 208 | .coff, .pe => &(try Coff.createEmpty(allocator, options)).base, |
| 205 | 209 | .elf => &(try Elf.createEmpty(allocator, options)).base, |
| 206 | 210 | .macho => &(try MachO.createEmpty(allocator, options)).base, |
| 211 | .plan9 => &(try Plan9.createEmpty(allocator, options)).base, | |
| 207 | 212 | .wasm => &(try Wasm.createEmpty(allocator, options)).base, |
| 208 | 213 | .c => unreachable, // Reported error earlier. |
| 209 | 214 | .spirv => &(try SpirV.createEmpty(allocator, options)).base, |
| ... | ... | @@ -220,6 +225,7 @@ pub const File = struct { |
| 220 | 225 | .coff, .pe => &(try Coff.openPath(allocator, sub_path, options)).base, |
| 221 | 226 | .elf => &(try Elf.openPath(allocator, sub_path, options)).base, |
| 222 | 227 | .macho => &(try MachO.openPath(allocator, sub_path, options)).base, |
| 228 | .plan9 => &(try Plan9.openPath(allocator, sub_path, options)).base, | |
| 223 | 229 | .wasm => &(try Wasm.openPath(allocator, sub_path, options)).base, |
| 224 | 230 | .c => &(try C.openPath(allocator, sub_path, options)).base, |
| 225 | 231 | .spirv => &(try SpirV.openPath(allocator, sub_path, options)).base, |
| ... | ... | @@ -243,7 +249,7 @@ pub const File = struct { |
| 243 | 249 | |
| 244 | 250 | pub fn makeWritable(base: *File) !void { |
| 245 | 251 | switch (base.tag) { |
| 246 | .coff, .elf, .macho => { | |
| 252 | .coff, .elf, .macho, .plan9 => { | |
| 247 | 253 | if (base.file != null) return; |
| 248 | 254 | const emit = base.options.emit orelse return; |
| 249 | 255 | base.file = try emit.directory.handle.createFile(emit.sub_path, .{ |
| ... | ... | @@ -288,7 +294,7 @@ pub const File = struct { |
| 288 | 294 | f.close(); |
| 289 | 295 | base.file = null; |
| 290 | 296 | }, |
| 291 | .coff, .elf => if (base.file) |f| { | |
| 297 | .coff, .elf, .plan9 => if (base.file) |f| { | |
| 292 | 298 | if (base.intermediary_basename != null) { |
| 293 | 299 | // The file we have open is not the final file that we want to |
| 294 | 300 | // make executable, so we don't have to close it. |
| ... | ... | @@ -313,6 +319,7 @@ pub const File = struct { |
| 313 | 319 | .c => return @fieldParentPtr(C, "base", base).updateDecl(module, decl), |
| 314 | 320 | .wasm => return @fieldParentPtr(Wasm, "base", base).updateDecl(module, decl), |
| 315 | 321 | .spirv => return @fieldParentPtr(SpirV, "base", base).updateDecl(module, decl), |
| 322 | .plan9 => return @fieldParentPtr(Plan9, "base", base).updateDecl(module, decl), | |
| 316 | 323 | } |
| 317 | 324 | } |
| 318 | 325 | |
| ... | ... | @@ -326,6 +333,7 @@ pub const File = struct { |
| 326 | 333 | .elf => return @fieldParentPtr(Elf, "base", base).updateDeclLineNumber(module, decl), |
| 327 | 334 | .macho => return @fieldParentPtr(MachO, "base", base).updateDeclLineNumber(module, decl), |
| 328 | 335 | .c => return @fieldParentPtr(C, "base", base).updateDeclLineNumber(module, decl), |
| 336 | .plan9 => @panic("TODO: implement updateDeclLineNumber for plan9"), | |
| 329 | 337 | .wasm, .spirv => {}, |
| 330 | 338 | } |
| 331 | 339 | } |
| ... | ... | @@ -340,6 +348,7 @@ pub const File = struct { |
| 340 | 348 | .macho => return @fieldParentPtr(MachO, "base", base).allocateDeclIndexes(decl), |
| 341 | 349 | .c => return @fieldParentPtr(C, "base", base).allocateDeclIndexes(decl), |
| 342 | 350 | .wasm => return @fieldParentPtr(Wasm, "base", base).allocateDeclIndexes(decl), |
| 351 | .plan9 => return @fieldParentPtr(Plan9, "base", base).allocateDeclIndexes(decl), | |
| 343 | 352 | .spirv => {}, |
| 344 | 353 | } |
| 345 | 354 | } |
| ... | ... | @@ -393,6 +402,11 @@ pub const File = struct { |
| 393 | 402 | parent.deinit(); |
| 394 | 403 | base.allocator.destroy(parent); |
| 395 | 404 | }, |
| 405 | .plan9 => { | |
| 406 | const parent = @fieldParentPtr(Plan9, "base", base); | |
| 407 | parent.deinit(); | |
| 408 | base.allocator.destroy(parent); | |
| 409 | }, | |
| 396 | 410 | } |
| 397 | 411 | } |
| 398 | 412 | |
| ... | ... | @@ -425,6 +439,7 @@ pub const File = struct { |
| 425 | 439 | .c => return @fieldParentPtr(C, "base", base).flush(comp), |
| 426 | 440 | .wasm => return @fieldParentPtr(Wasm, "base", base).flush(comp), |
| 427 | 441 | .spirv => return @fieldParentPtr(SpirV, "base", base).flush(comp), |
| 442 | .plan9 => return @fieldParentPtr(Plan9, "base", base).flush(comp), | |
| 428 | 443 | } |
| 429 | 444 | } |
| 430 | 445 | |
| ... | ... | @@ -438,6 +453,7 @@ pub const File = struct { |
| 438 | 453 | .c => return @fieldParentPtr(C, "base", base).flushModule(comp), |
| 439 | 454 | .wasm => return @fieldParentPtr(Wasm, "base", base).flushModule(comp), |
| 440 | 455 | .spirv => return @fieldParentPtr(SpirV, "base", base).flushModule(comp), |
| 456 | .plan9 => return @fieldParentPtr(Plan9, "base", base).flushModule(comp), | |
| 441 | 457 | } |
| 442 | 458 | } |
| 443 | 459 | |
| ... | ... | @@ -451,6 +467,7 @@ pub const File = struct { |
| 451 | 467 | .c => @fieldParentPtr(C, "base", base).freeDecl(decl), |
| 452 | 468 | .wasm => @fieldParentPtr(Wasm, "base", base).freeDecl(decl), |
| 453 | 469 | .spirv => @fieldParentPtr(SpirV, "base", base).freeDecl(decl), |
| 470 | .plan9 => @fieldParentPtr(Plan9, "base", base).freeDecl(decl), | |
| 454 | 471 | } |
| 455 | 472 | } |
| 456 | 473 | |
| ... | ... | @@ -459,6 +476,7 @@ pub const File = struct { |
| 459 | 476 | .coff => return @fieldParentPtr(Coff, "base", base).error_flags, |
| 460 | 477 | .elf => return @fieldParentPtr(Elf, "base", base).error_flags, |
| 461 | 478 | .macho => return @fieldParentPtr(MachO, "base", base).error_flags, |
| 479 | .plan9 => return @fieldParentPtr(Plan9, "base", base).error_flags, | |
| 462 | 480 | .c => return .{ .no_entry_point_found = false }, |
| 463 | 481 | .wasm, .spirv => return ErrorFlags{}, |
| 464 | 482 | } |
| ... | ... | @@ -481,6 +499,7 @@ pub const File = struct { |
| 481 | 499 | .c => return @fieldParentPtr(C, "base", base).updateDeclExports(module, decl, exports), |
| 482 | 500 | .wasm => return @fieldParentPtr(Wasm, "base", base).updateDeclExports(module, decl, exports), |
| 483 | 501 | .spirv => return @fieldParentPtr(SpirV, "base", base).updateDeclExports(module, decl, exports), |
| 502 | .plan9 => return @fieldParentPtr(Plan9, "base", base).updateDeclExports(module, decl, exports), | |
| 484 | 503 | } |
| 485 | 504 | } |
| 486 | 505 | |
| ... | ... | @@ -489,6 +508,7 @@ pub const File = struct { |
| 489 | 508 | .coff => return @fieldParentPtr(Coff, "base", base).getDeclVAddr(decl), |
| 490 | 509 | .elf => return @fieldParentPtr(Elf, "base", base).getDeclVAddr(decl), |
| 491 | 510 | .macho => return @fieldParentPtr(MachO, "base", base).getDeclVAddr(decl), |
| 511 | .plan9 => @panic("GET VADDR"), | |
| 492 | 512 | .c => unreachable, |
| 493 | 513 | .wasm => unreachable, |
| 494 | 514 | .spirv => unreachable, |
| ... | ... | @@ -633,6 +653,7 @@ pub const File = struct { |
| 633 | 653 | c, |
| 634 | 654 | wasm, |
| 635 | 655 | spirv, |
| 656 | plan9, | |
| 636 | 657 | }; |
| 637 | 658 | |
| 638 | 659 | pub const ErrorFlags = struct { |
| ... | ... | @@ -641,6 +662,7 @@ pub const File = struct { |
| 641 | 662 | |
| 642 | 663 | pub const C = @import("link/C.zig"); |
| 643 | 664 | pub const Coff = @import("link/Coff.zig"); |
| 665 | pub const Plan9 = @import("link/Plan9.zig"); | |
| 644 | 666 | pub const Elf = @import("link/Elf.zig"); |
| 645 | 667 | pub const MachO = @import("link/MachO.zig"); |
| 646 | 668 | 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 | ||
| 4 | const Plan9 = @This(); | |
| 5 | ||
| 6 | const std = @import("std"); | |
| 7 | const link = @import("../link.zig"); | |
| 8 | const Module = @import("../Module.zig"); | |
| 9 | const Compilation = @import("../Compilation.zig"); | |
| 10 | const aout = @import("Plan9/aout.zig"); | |
| 11 | const codegen = @import("../codegen.zig"); | |
| 12 | const trace = @import("../tracy.zig").trace; | |
| 13 | const mem = std.mem; | |
| 14 | const File = link.File; | |
| 15 | const Allocator = std.mem.Allocator; | |
| 16 | ||
| 17 | const log = std.log.scoped(.link); | |
| 18 | const assert = std.debug.assert; | |
| 19 | ||
| 20 | base: link.File, | |
| 21 | sixtyfour_bit: bool, | |
| 22 | error_flags: File.ErrorFlags = File.ErrorFlags{}, | |
| 23 | bases: Bases, | |
| 24 | ||
| 25 | decl_table: std.AutoArrayHashMapUnmanaged(*Module.Decl, void) = .{}, | |
| 26 | /// is just casted down when 32 bit | |
| 27 | syms: std.ArrayListUnmanaged(aout.Sym) = .{}, | |
| 28 | text_buf: std.ArrayListUnmanaged(u8) = .{}, | |
| 29 | data_buf: std.ArrayListUnmanaged(u8) = .{}, | |
| 30 | ||
| 31 | hdr: aout.ExecHdr = undefined, | |
| 32 | ||
| 33 | entry_decl: ?*Module.Decl = null, | |
| 34 | ||
| 35 | got: std.ArrayListUnmanaged(u64) = .{}, | |
| 36 | const Bases = struct { | |
| 37 | text: u64, | |
| 38 | /// the addr of the got | |
| 39 | data: u64, | |
| 40 | }; | |
| 41 | ||
| 42 | fn 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 | |
| 50 | fn 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 | ||
| 58 | fn getSymAddr(self: Plan9, s: aout.Sym) u64 { | |
| 59 | return self.getAddr(s.value, s.type); | |
| 60 | } | |
| 61 | ||
| 62 | pub 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 | ||
| 78 | pub 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 | ||
| 94 | pub const PtrWidth = enum { p32, p64 }; | |
| 95 | ||
| 96 | pub 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 | ||
| 118 | pub fn updateDecl(self: *Plan9, module: *Module, decl: *Module.Decl) !void { | |
| 119 | _ = module; | |
| 120 | _ = try self.decl_table.getOrPut(self.base.allocator, decl); | |
| 121 | } | |
| 122 | ||
| 123 | pub 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 | ||
| 135 | pub 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 | } | |
| 296 | pub fn freeDecl(self: *Plan9, decl: *Module.Decl) void { | |
| 297 | assert(self.decl_table.swapRemove(decl)); | |
| 298 | } | |
| 299 | ||
| 300 | pub 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 | } | |
| 312 | pub 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 | ||
| 320 | pub const Export = ?usize; | |
| 321 | pub const base_tag = .plan9; | |
| 322 | pub 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 | ||
| 361 | pub 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 | ||
| 375 | pub 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 @@ |
| 1 | const std = @import("std"); | |
| 2 | const assert = std.debug.assert; | |
| 3 | ||
| 4 | /// All integers are in big-endian format (needs a byteswap). | |
| 5 | pub 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 | ||
| 31 | pub 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 | ||
| 80 | pub const HDR_MAGIC = 0x00008000; | |
| 81 | pub 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 | } | |
| 84 | pub const A_MAGIC = _MAGIC(0, 8); // 68020 | |
| 85 | pub const I_MAGIC = _MAGIC(0, 11); // intel 386 | |
| 86 | pub const J_MAGIC = _MAGIC(0, 12); // intel 960 (retired) | |
| 87 | pub const K_MAGIC = _MAGIC(0, 13); // sparc | |
| 88 | pub const V_MAGIC = _MAGIC(0, 16); // mips 3000 BE | |
| 89 | pub const X_MAGIC = _MAGIC(0, 17); // att dsp 3210 (retired) | |
| 90 | pub const M_MAGIC = _MAGIC(0, 18); // mips 4000 BE | |
| 91 | pub const D_MAGIC = _MAGIC(0, 19); // amd 29000 (retired) | |
| 92 | pub const E_MAGIC = _MAGIC(0, 20); // arm | |
| 93 | pub const Q_MAGIC = _MAGIC(0, 21); // powerpc | |
| 94 | pub const N_MAGIC = _MAGIC(0, 22); // mips 4000 LE | |
| 95 | pub const L_MAGIC = _MAGIC(0, 23); // dec alpha (retired) | |
| 96 | pub const P_MAGIC = _MAGIC(0, 24); // mips 3000 LE | |
| 97 | pub const U_MAGIC = _MAGIC(0, 25); // sparc64 | |
| 98 | pub const S_MAGIC = _MAGIC(HDR_MAGIC, 26); // amd64 | |
| 99 | pub const T_MAGIC = _MAGIC(HDR_MAGIC, 27); // powerpc64 | |
| 100 | pub const R_MAGIC = _MAGIC(HDR_MAGIC, 28); // arm64 | |
| 101 | ||
| 102 | pub 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 { |
| 244 | 244 | |
| 245 | 245 | pub fn osToLLVM(os_tag: std.Target.Os.Tag) llvm.OSType { |
| 246 | 246 | return switch (os_tag) { |
| 247 | .freestanding, .other, .opencl, .glsl450, .vulkan => .UnknownOS, | |
| 247 | .freestanding, .other, .opencl, .glsl450, .vulkan, .plan9 => .UnknownOS, | |
| 248 | 248 | .windows, .uefi => .Win32, |
| 249 | 249 | .ananas => .Ananas, |
| 250 | 250 | .cloudabi => .CloudABI, |
src/type.zig+1| ... | ... | @@ -3290,6 +3290,7 @@ pub const CType = enum { |
| 3290 | 3290 | .openbsd, |
| 3291 | 3291 | .wasi, |
| 3292 | 3292 | .emscripten, |
| 3293 | .plan9, | |
| 3293 | 3294 | => switch (self) { |
| 3294 | 3295 | .short, |
| 3295 | 3296 | .ushort, |