From 34c21affa2429cf07042eb2f225ee59194998802 Mon Sep 17 00:00:00 2001 From: jacob gw Date: Tue, 1 Jun 2021 16:07:08 -0400 Subject: [PATCH 01/11] initial plan9 boilerplate The code now compiles and fails with Plan9ObjectFormatUnimplemented --- CMakeLists.txt | 2 ++ lib/std/target.zig | 9 +++++ lib/std/zig.zig | 2 ++ lib/std/zig/cross_target.zig | 2 ++ src/Module.zig | 8 +++++ src/codegen/llvm.zig | 1 + src/link.zig | 25 ++++++++++++-- src/link/Plan9.zig | 65 ++++++++++++++++++++++++++++++++++++ src/target.zig | 2 +- src/type.zig | 1 + 10 files changed, 114 insertions(+), 3 deletions(-) create mode 100644 src/link/Plan9.zig diff --git a/CMakeLists.txt b/CMakeLists.txt index f279990e27..f1b3f767e6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -573,6 +573,8 @@ set(ZIG_STAGE2_SOURCES "${CMAKE_SOURCE_DIR}/src/link/C.zig" "${CMAKE_SOURCE_DIR}/src/link/Coff.zig" "${CMAKE_SOURCE_DIR}/src/link/Elf.zig" + "${CMAKE_SOURCE_DIR}/src/link/Plan9.zig" + "${CMAKE_SOURCE_DIR}/src/link/plan9/a.out.zig" "${CMAKE_SOURCE_DIR}/src/link/MachO.zig" "${CMAKE_SOURCE_DIR}/src/link/MachO/Archive.zig" "${CMAKE_SOURCE_DIR}/src/link/MachO/CodeSignature.zig" diff --git a/lib/std/target.zig b/lib/std/target.zig index 507f30c2e0..70626f5051 100644 --- a/lib/std/target.zig +++ b/lib/std/target.zig @@ -60,6 +60,7 @@ pub const Target = struct { opencl, glsl450, vulkan, + plan9, other, pub fn isDarwin(tag: Tag) bool { @@ -262,6 +263,7 @@ pub const Target = struct { .opencl, // TODO: OpenCL versions .glsl450, // TODO: GLSL versions .vulkan, + .plan9, .other, => return .{ .none = {} }, @@ -420,6 +422,7 @@ pub const Target = struct { .opencl, .glsl450, .vulkan, + .plan9, .other, => false, }; @@ -515,6 +518,7 @@ pub const Target = struct { .opencl, // TODO: SPIR-V ABIs with Linkage capability .glsl450, .vulkan, + .plan9, // TODO specify abi => return .none, } } @@ -554,6 +558,7 @@ pub const Target = struct { spirv, hex, raw, + plan9, }; pub const SubSystem = enum { @@ -1359,6 +1364,8 @@ pub const Target = struct { if (cpu_arch.isSPIRV()) { return .spirv; } + if (os_tag == .plan9) + return .plan9; return .elf; } @@ -1432,6 +1439,7 @@ pub const Target = struct { .opencl, .glsl450, .vulkan, + .plan9, .other, => return false, else => return true, @@ -1616,6 +1624,7 @@ pub const Target = struct { .glsl450, .vulkan, .other, + .plan9, => return result, // TODO revisit when multi-arch for Haiku is available diff --git a/lib/std/zig.zig b/lib/std/zig.zig index 4adbbe0b6a..65db02083b 100644 --- a/lib/std/zig.zig +++ b/lib/std/zig.zig @@ -181,6 +181,8 @@ pub fn binNameAlloc(allocator: *std.mem.Allocator, options: BinNameOptions) erro .spirv => return std.fmt.allocPrint(allocator, "{s}.spv", .{root_name}), .hex => return std.fmt.allocPrint(allocator, "{s}.ihex", .{root_name}), .raw => return std.fmt.allocPrint(allocator, "{s}.bin", .{root_name}), + // TODO change this to the arbitrary character for plan9 output, eg '6' for amd64 + .plan9 => return std.fmt.allocPrint(allocator, "{s}.out.p9", .{root_name}), } } diff --git a/lib/std/zig/cross_target.zig b/lib/std/zig/cross_target.zig index 488707f113..e6ca0a2baa 100644 --- a/lib/std/zig/cross_target.zig +++ b/lib/std/zig/cross_target.zig @@ -133,6 +133,7 @@ pub const CrossTarget = struct { .opencl, .glsl450, .vulkan, + .plan9, .other, => { self.os_version_min = .{ .none = {} }; @@ -746,6 +747,7 @@ pub const CrossTarget = struct { .opencl, .glsl450, .vulkan, + .plan9, .other, => return error.InvalidOperatingSystemVersion, diff --git a/src/Module.zig b/src/Module.zig index 8ae184a377..e95d6bf245 100644 --- a/src/Module.zig +++ b/src/Module.zig @@ -3437,6 +3437,9 @@ fn scanDecl(iter: *ScanDeclIter, decl_sub_index: usize, flags: u4) InnerError!vo // in `Decl` to notice that the line number did not change. mod.comp.work_queue.writeItemAssumeCapacity(.{ .update_line_number = decl }); }, + .plan9 => { + // TODO implement for plan9 + }, .c, .wasm, .spirv => {}, } } @@ -3514,6 +3517,7 @@ pub fn clearDecl( .coff => .{ .coff = link.File.Coff.TextBlock.empty }, .elf => .{ .elf = link.File.Elf.TextBlock.empty }, .macho => .{ .macho = link.File.MachO.TextBlock.empty }, + .plan9 => @panic("plan9 link"), .c => .{ .c = link.File.C.DeclBlock.empty }, .wasm => .{ .wasm = link.File.Wasm.DeclBlock.empty }, .spirv => .{ .spirv = {} }, @@ -3522,6 +3526,7 @@ pub fn clearDecl( .coff => .{ .coff = {} }, .elf => .{ .elf = link.File.Elf.SrcFn.empty }, .macho => .{ .macho = link.File.MachO.SrcFn.empty }, + .plan9 => @panic("plan9 fn_link"), .c => .{ .c = link.File.C.FnBlock.empty }, .wasm => .{ .wasm = link.File.Wasm.FnData.empty }, .spirv => .{ .spirv = .{} }, @@ -3689,6 +3694,7 @@ fn allocateNewDecl(mod: *Module, namespace: *Scope.Namespace, src_node: ast.Node .coff => .{ .coff = link.File.Coff.TextBlock.empty }, .elf => .{ .elf = link.File.Elf.TextBlock.empty }, .macho => .{ .macho = link.File.MachO.TextBlock.empty }, + .plan9 => @panic("PLan9 export"), .c => .{ .c = link.File.C.DeclBlock.empty }, .wasm => .{ .wasm = link.File.Wasm.DeclBlock.empty }, .spirv => .{ .spirv = {} }, @@ -3697,6 +3703,7 @@ fn allocateNewDecl(mod: *Module, namespace: *Scope.Namespace, src_node: ast.Node .coff => .{ .coff = {} }, .elf => .{ .elf = link.File.Elf.SrcFn.empty }, .macho => .{ .macho = link.File.MachO.SrcFn.empty }, + .plan9 => .{ .plan9 = link.File.Plan9.SrcFn.empty }, .c => .{ .c = link.File.C.FnBlock.empty }, .wasm => .{ .wasm = link.File.Wasm.FnData.empty }, .spirv => .{ .spirv = .{} }, @@ -3766,6 +3773,7 @@ pub fn analyzeExport( .coff => .{ .coff = {} }, .elf => .{ .elf = link.File.Elf.Export{} }, .macho => .{ .macho = link.File.MachO.Export{} }, + .plan9 => @panic("plan9 link"), .c => .{ .c = {} }, .wasm => .{ .wasm = {} }, .spirv => .{ .spirv = {} }, diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index 2baedf8c9d..b8f96891f4 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -117,6 +117,7 @@ pub fn targetTriple(allocator: *Allocator, target: std.Target) ![:0]u8 { .opencl => return error.LLVMBackendDoesNotSupportOpenCL, .glsl450 => return error.LLVMBackendDoesNotSupportGLSL450, .vulkan => return error.LLVMBackendDoesNotSupportVulkan, + .plan9 => return error.LLVMBackendDoesNotSupportPlan9, .other => "unknown", }; diff --git a/src/link.zig b/src/link.zig index 9c1be16b9c..e5eb45ec91 100644 --- a/src/link.zig +++ b/src/link.zig @@ -150,6 +150,7 @@ pub const File = struct { elf: Elf.SrcFn, coff: Coff.SrcFn, macho: MachO.SrcFn, + plan9: Plan9.SrcFn, c: C.FnBlock, wasm: Wasm.FnData, spirv: SpirV.FnData, @@ -159,6 +160,7 @@ pub const File = struct { elf: Elf.Export, coff: void, macho: MachO.Export, + plan9: Plan9.Export, c: void, wasm: void, spirv: void, @@ -189,6 +191,7 @@ pub const File = struct { .elf => &(try Elf.createEmpty(allocator, options)).base, .macho => &(try MachO.createEmpty(allocator, options)).base, .wasm => &(try Wasm.createEmpty(allocator, options)).base, + .plan9 => return &(try Plan9.createEmpty(allocator, options)).base, .c => unreachable, // Reported error earlier. .spirv => &(try SpirV.createEmpty(allocator, options)).base, .hex => return error.HexObjectFormatUnimplemented, @@ -209,6 +212,7 @@ pub const File = struct { .spirv => &(try SpirV.createEmpty(allocator, options)).base, .hex => return error.HexObjectFormatUnimplemented, .raw => return error.RawObjectFormatUnimplemented, + .plan9 => return error.Plan9ObjectFormatUnimplemented, }; } // Open a temporary object file, not the final output file because we want to link with LLD. @@ -225,6 +229,7 @@ pub const File = struct { .spirv => &(try SpirV.openPath(allocator, sub_path, options)).base, .hex => return error.HexObjectFormatUnimplemented, .raw => return error.RawObjectFormatUnimplemented, + .plan9 => return error.Plan9ObjectFormatUnimplemented, }; if (use_lld) { @@ -243,7 +248,7 @@ pub const File = struct { pub fn makeWritable(base: *File) !void { switch (base.tag) { - .coff, .elf, .macho => { + .coff, .elf, .macho, .plan9 => { if (base.file != null) return; const emit = base.options.emit orelse return; base.file = try emit.directory.handle.createFile(emit.sub_path, .{ @@ -288,7 +293,7 @@ pub const File = struct { f.close(); base.file = null; }, - .coff, .elf => if (base.file) |f| { + .coff, .elf, .plan9 => if (base.file) |f| { if (base.intermediary_basename != null) { // The file we have open is not the final file that we want to // make executable, so we don't have to close it. @@ -313,6 +318,7 @@ pub const File = struct { .c => return @fieldParentPtr(C, "base", base).updateDecl(module, decl), .wasm => return @fieldParentPtr(Wasm, "base", base).updateDecl(module, decl), .spirv => return @fieldParentPtr(SpirV, "base", base).updateDecl(module, decl), + .plan9 => return @fieldParentPtr(Plan9, "base", base).updateDecl(module, decl), } } @@ -326,6 +332,7 @@ pub const File = struct { .elf => return @fieldParentPtr(Elf, "base", base).updateDeclLineNumber(module, decl), .macho => return @fieldParentPtr(MachO, "base", base).updateDeclLineNumber(module, decl), .c => return @fieldParentPtr(C, "base", base).updateDeclLineNumber(module, decl), + .plan9 => @panic("PLAN 9 DEBUG INFO"), .wasm, .spirv => {}, } } @@ -340,6 +347,7 @@ pub const File = struct { .macho => return @fieldParentPtr(MachO, "base", base).allocateDeclIndexes(decl), .c => return @fieldParentPtr(C, "base", base).allocateDeclIndexes(decl), .wasm => return @fieldParentPtr(Wasm, "base", base).allocateDeclIndexes(decl), + .plan9 => return @fieldParentPtr(Plan9, "base", base).allocateDeclIndexes(decl), .spirv => {}, } } @@ -393,6 +401,11 @@ pub const File = struct { parent.deinit(); base.allocator.destroy(parent); }, + .plan9 => { + const parent = @fieldParentPtr(Plan9, "base", base); + parent.deinit(); + base.allocator.destroy(parent); + }, } } @@ -425,6 +438,7 @@ pub const File = struct { .c => return @fieldParentPtr(C, "base", base).flush(comp), .wasm => return @fieldParentPtr(Wasm, "base", base).flush(comp), .spirv => return @fieldParentPtr(SpirV, "base", base).flush(comp), + .plan9 => return @fieldParentPtr(Plan9, "base", base).flush(comp), } } @@ -438,6 +452,7 @@ pub const File = struct { .c => return @fieldParentPtr(C, "base", base).flushModule(comp), .wasm => return @fieldParentPtr(Wasm, "base", base).flushModule(comp), .spirv => return @fieldParentPtr(SpirV, "base", base).flushModule(comp), + .plan9 => return @fieldParentPtr(Plan9, "base", base).flushModule(comp), } } @@ -451,6 +466,7 @@ pub const File = struct { .c => @fieldParentPtr(C, "base", base).freeDecl(decl), .wasm => @fieldParentPtr(Wasm, "base", base).freeDecl(decl), .spirv => @fieldParentPtr(SpirV, "base", base).freeDecl(decl), + .plan9 => @fieldParentPtr(Plan9, "base", base).freeDecl(decl), } } @@ -459,6 +475,7 @@ pub const File = struct { .coff => return @fieldParentPtr(Coff, "base", base).error_flags, .elf => return @fieldParentPtr(Elf, "base", base).error_flags, .macho => return @fieldParentPtr(MachO, "base", base).error_flags, + .plan9 => return @fieldParentPtr(Plan9, "base", base).error_flags, .c => return .{ .no_entry_point_found = false }, .wasm, .spirv => return ErrorFlags{}, } @@ -481,6 +498,7 @@ pub const File = struct { .c => return @fieldParentPtr(C, "base", base).updateDeclExports(module, decl, exports), .wasm => return @fieldParentPtr(Wasm, "base", base).updateDeclExports(module, decl, exports), .spirv => return @fieldParentPtr(SpirV, "base", base).updateDeclExports(module, decl, exports), + .plan9 => return @fieldParentPtr(Plan9, "base", base).updateDeclExports(module, decl, exports), } } @@ -489,6 +507,7 @@ pub const File = struct { .coff => return @fieldParentPtr(Coff, "base", base).getDeclVAddr(decl), .elf => return @fieldParentPtr(Elf, "base", base).getDeclVAddr(decl), .macho => return @fieldParentPtr(MachO, "base", base).getDeclVAddr(decl), + .plan9 => @panic("GET VADDR"), .c => unreachable, .wasm => unreachable, .spirv => unreachable, @@ -633,6 +652,7 @@ pub const File = struct { c, wasm, spirv, + plan9, }; pub const ErrorFlags = struct { @@ -641,6 +661,7 @@ pub const File = struct { pub const C = @import("link/C.zig"); pub const Coff = @import("link/Coff.zig"); + pub const Plan9 = @import("link/Plan9.zig"); pub const Elf = @import("link/Elf.zig"); pub const MachO = @import("link/MachO.zig"); pub const SpirV = @import("link/SpirV.zig"); diff --git a/src/link/Plan9.zig b/src/link/Plan9.zig new file mode 100644 index 0000000000..4459288f58 --- /dev/null +++ b/src/link/Plan9.zig @@ -0,0 +1,65 @@ +const Plan9 = @This(); + +const std = @import("std"); +const link = @import("../link.zig"); +const Module = @import("../Module.zig"); +const Compilation = @import("../Compilation.zig"); +const File = link.File; +const Allocator = std.mem.Allocator; + +const log = std.log.scoped(.link); + +base: link.File, +error_flags: File.ErrorFlags = File.ErrorFlags{}, + +pub const SrcFn = struct { + /// Offset from the beginning of the Debug Line Program header that contains this function. + off: u32, + /// Size of the line number program component belonging to this function, not + /// including padding. + len: u32, + + /// Points to the previous and next neighbors, based on the offset from .debug_line. + /// This can be used to find, for example, the capacity of this `SrcFn`. + prev: ?*SrcFn, + next: ?*SrcFn, + + pub const empty: SrcFn = .{ + .off = 0, + .len = 0, + .prev = null, + .next = null, + }; +}; + +pub fn createEmpty(gpa: *Allocator, options: link.Options) !*Plan9 { + const self = try gpa.create(Plan9); + self.* = .{ + .base = .{ + .tag = .plan9, + .options = options, + .allocator = gpa, + .file = null, + }, + }; + return self; +} + +pub fn updateDecl(self: *Plan9, module: *Module, decl: *Module.Decl) !void {} + +pub fn allocateDeclIndexes(self: *Plan9, decl: *Module.Decl) !void {} + +pub fn flush(self: *Plan9, comp: *Compilation) !void {} +pub fn flushModule(self: *Plan9, comp: *Compilation) !void {} +pub fn freeDecl(self: *Plan9, decl: *Module.Decl) void {} +pub fn updateDeclExports( + self: *Plan9, + module: *Module, + decl: *Module.Decl, + exports: []const *Module.Export, +) !void {} +pub fn deinit(self: *Plan9) void {} + +pub const Export = struct { + sym_index: ?u32 = null, +}; diff --git a/src/target.zig b/src/target.zig index a61bbe7f09..06483d68ff 100644 --- a/src/target.zig +++ b/src/target.zig @@ -244,7 +244,7 @@ pub fn supportsStackProbing(target: std.Target) bool { pub fn osToLLVM(os_tag: std.Target.Os.Tag) llvm.OSType { return switch (os_tag) { - .freestanding, .other, .opencl, .glsl450, .vulkan => .UnknownOS, + .freestanding, .other, .opencl, .glsl450, .vulkan, .plan9 => .UnknownOS, .windows, .uefi => .Win32, .ananas => .Ananas, .cloudabi => .CloudABI, diff --git a/src/type.zig b/src/type.zig index 34b7dc178b..8ded2ee906 100644 --- a/src/type.zig +++ b/src/type.zig @@ -3290,6 +3290,7 @@ pub const CType = enum { .openbsd, .wasi, .emscripten, + .plan9, => switch (self) { .short, .ushort, From 798162e5095aff130395ee542c0c0948f95180c9 Mon Sep 17 00:00:00 2001 From: Jacob G-W Date: Tue, 1 Jun 2021 22:48:20 -0400 Subject: [PATCH 02/11] plan9 linker: make runnable binaries We can now run binaries! (they segfault, but still run!) --- src/Module.zig | 10 +- src/codegen.zig | 16 ++- src/link.zig | 9 +- src/link/Plan9.zig | 213 ++++++++++++++++++++++++++++++++++----- src/link/plan9/a.out.zig | 122 ++++++++++++++++++++++ 5 files changed, 333 insertions(+), 37 deletions(-) create mode 100644 src/link/plan9/a.out.zig diff --git a/src/Module.zig b/src/Module.zig index e95d6bf245..a1f6887fbd 100644 --- a/src/Module.zig +++ b/src/Module.zig @@ -3517,7 +3517,7 @@ pub fn clearDecl( .coff => .{ .coff = link.File.Coff.TextBlock.empty }, .elf => .{ .elf = link.File.Elf.TextBlock.empty }, .macho => .{ .macho = link.File.MachO.TextBlock.empty }, - .plan9 => @panic("plan9 link"), + .plan9 => .{ .plan9 = link.File.Plan9.DeclBlock.empty }, .c => .{ .c = link.File.C.DeclBlock.empty }, .wasm => .{ .wasm = link.File.Wasm.DeclBlock.empty }, .spirv => .{ .spirv = {} }, @@ -3526,7 +3526,7 @@ pub fn clearDecl( .coff => .{ .coff = {} }, .elf => .{ .elf = link.File.Elf.SrcFn.empty }, .macho => .{ .macho = link.File.MachO.SrcFn.empty }, - .plan9 => @panic("plan9 fn_link"), + .plan9 => .{ .plan9 = {} }, .c => .{ .c = link.File.C.FnBlock.empty }, .wasm => .{ .wasm = link.File.Wasm.FnData.empty }, .spirv => .{ .spirv = .{} }, @@ -3694,7 +3694,7 @@ fn allocateNewDecl(mod: *Module, namespace: *Scope.Namespace, src_node: ast.Node .coff => .{ .coff = link.File.Coff.TextBlock.empty }, .elf => .{ .elf = link.File.Elf.TextBlock.empty }, .macho => .{ .macho = link.File.MachO.TextBlock.empty }, - .plan9 => @panic("PLan9 export"), + .plan9 => .{ .plan9 = link.File.Plan9.DeclBlock.empty }, .c => .{ .c = link.File.C.DeclBlock.empty }, .wasm => .{ .wasm = link.File.Wasm.DeclBlock.empty }, .spirv => .{ .spirv = {} }, @@ -3703,7 +3703,7 @@ fn allocateNewDecl(mod: *Module, namespace: *Scope.Namespace, src_node: ast.Node .coff => .{ .coff = {} }, .elf => .{ .elf = link.File.Elf.SrcFn.empty }, .macho => .{ .macho = link.File.MachO.SrcFn.empty }, - .plan9 => .{ .plan9 = link.File.Plan9.SrcFn.empty }, + .plan9 => .{ .plan9 = {} }, .c => .{ .c = link.File.C.FnBlock.empty }, .wasm => .{ .wasm = link.File.Wasm.FnData.empty }, .spirv => .{ .spirv = .{} }, @@ -3773,7 +3773,7 @@ pub fn analyzeExport( .coff => .{ .coff = {} }, .elf => .{ .elf = link.File.Elf.Export{} }, .macho => .{ .macho = link.File.MachO.Export{} }, - .plan9 => @panic("plan9 link"), + .plan9 => .{ .plan9 = null }, .c => .{ .c = {} }, .wasm => .{ .wasm = {} }, .spirv => .{ .spirv = {} }, diff --git a/src/codegen.zig b/src/codegen.zig index 6b9bd633d0..61607407d3 100644 --- a/src/codegen.zig +++ b/src/codegen.zig @@ -2556,9 +2556,19 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { } else { return self.fail(inst.base.src, "TODO implement calling runtime known function pointer", .{}); } - } else { - unreachable; - } + } else if (self.bin_file.cast(link.File.Plan9)) |p9| { + if (inst.func.value()) |func_value| { + if (func_value.castTag(.function)) |func_payload| { + try p9.addCallReloc(self.code, .{ + .caller = p9.cur_decl, + .callee = func_payload.data.owner_decl, + .offset_in_caller = self.code.items.len, + }); + } else return self.fail(inst.base.src, "TODO implement calling extern fn on plan9", .{}); + } else { + return self.fail(inst.base.src, "TODO implement calling runtime known function pointer", .{}); + } + } else unreachable; switch (info.return_value) { .register => |reg| { diff --git a/src/link.zig b/src/link.zig index e5eb45ec91..9447397428 100644 --- a/src/link.zig +++ b/src/link.zig @@ -141,6 +141,7 @@ pub const File = struct { elf: Elf.TextBlock, coff: Coff.TextBlock, macho: MachO.TextBlock, + plan9: Plan9.DeclBlock, c: C.DeclBlock, wasm: Wasm.DeclBlock, spirv: void, @@ -150,7 +151,7 @@ pub const File = struct { elf: Elf.SrcFn, coff: Coff.SrcFn, macho: MachO.SrcFn, - plan9: Plan9.SrcFn, + plan9: void, c: C.FnBlock, wasm: Wasm.FnData, spirv: SpirV.FnData, @@ -207,12 +208,12 @@ pub const File = struct { .coff, .pe => &(try Coff.createEmpty(allocator, options)).base, .elf => &(try Elf.createEmpty(allocator, options)).base, .macho => &(try MachO.createEmpty(allocator, options)).base, + .plan9 => &(try Plan9.createEmpty(allocator, options)).base, .wasm => &(try Wasm.createEmpty(allocator, options)).base, .c => unreachable, // Reported error earlier. .spirv => &(try SpirV.createEmpty(allocator, options)).base, .hex => return error.HexObjectFormatUnimplemented, .raw => return error.RawObjectFormatUnimplemented, - .plan9 => return error.Plan9ObjectFormatUnimplemented, }; } // Open a temporary object file, not the final output file because we want to link with LLD. @@ -224,12 +225,12 @@ pub const File = struct { .coff, .pe => &(try Coff.openPath(allocator, sub_path, options)).base, .elf => &(try Elf.openPath(allocator, sub_path, options)).base, .macho => &(try MachO.openPath(allocator, sub_path, options)).base, + .plan9 => &(try Plan9.openPath(allocator, sub_path, options)).base, .wasm => &(try Wasm.openPath(allocator, sub_path, options)).base, .c => &(try C.openPath(allocator, sub_path, options)).base, .spirv => &(try SpirV.openPath(allocator, sub_path, options)).base, .hex => return error.HexObjectFormatUnimplemented, .raw => return error.RawObjectFormatUnimplemented, - .plan9 => return error.Plan9ObjectFormatUnimplemented, }; if (use_lld) { @@ -347,7 +348,7 @@ pub const File = struct { .macho => return @fieldParentPtr(MachO, "base", base).allocateDeclIndexes(decl), .c => return @fieldParentPtr(C, "base", base).allocateDeclIndexes(decl), .wasm => return @fieldParentPtr(Wasm, "base", base).allocateDeclIndexes(decl), - .plan9 => return @fieldParentPtr(Plan9, "base", base).allocateDeclIndexes(decl), + .plan9 => {}, .spirv => {}, } } diff --git a/src/link/Plan9.zig b/src/link/Plan9.zig index 4459288f58..d179836fa6 100644 --- a/src/link/Plan9.zig +++ b/src/link/Plan9.zig @@ -4,35 +4,66 @@ const std = @import("std"); const link = @import("../link.zig"); const Module = @import("../Module.zig"); const Compilation = @import("../Compilation.zig"); +const aout = @import("plan9/a.out.zig"); +const codegen = @import("../codegen.zig"); +const trace = @import("../tracy.zig").trace; +const mem = std.mem; const File = link.File; const Allocator = std.mem.Allocator; const log = std.log.scoped(.link); +const assert = std.debug.assert; + +// TODO use incremental compilation base: link.File, +ptr_width: PtrWidth, error_flags: File.ErrorFlags = File.ErrorFlags{}, -pub const SrcFn = struct { - /// Offset from the beginning of the Debug Line Program header that contains this function. - off: u32, - /// Size of the line number program component belonging to this function, not - /// including padding. - len: u32, +decl_table: std.AutoArrayHashMapUnmanaged(*Module.Decl, void) = .{}, +/// is just casted down when 32 bit +syms: std.ArrayListUnmanaged(aout.Sym64) = .{}, +call_relocs: std.ArrayListUnmanaged(CallReloc) = .{}, +text_buf: std.ArrayListUnmanaged(u8) = .{}, +data_buf: std.ArrayListUnmanaged(u8) = .{}, - /// Points to the previous and next neighbors, based on the offset from .debug_line. - /// This can be used to find, for example, the capacity of this `SrcFn`. - prev: ?*SrcFn, - next: ?*SrcFn, +cur_decl: *Module.Decl = undefined, +hdr: aout.ExecHdr = undefined, - pub const empty: SrcFn = .{ - .off = 0, - .len = 0, - .prev = null, - .next = null, +fn headerSize(self: Plan9) u32 { + // fat header (currently unused) + const fat: u4 = if (self.ptr_width == .p64) 8 else 0; + return aout.ExecHdr.size() + fat; +} +pub const DeclBlock = struct { + type: enum { text, data }, + // offset in the text or data sects + offset: u32, + pub const empty = DeclBlock{ + .type = .text, + .offset = 0, }; }; +// TODO change base addr based on target (right now it just works on amd64) +const default_base_addr = 0x00200000; + +pub const CallReloc = struct { + caller: *Module.Decl, + callee: *Module.Decl, + offset_in_caller: usize, +}; + +pub const PtrWidth = enum { p32, p64 }; + pub fn createEmpty(gpa: *Allocator, options: link.Options) !*Plan9 { + if (options.use_llvm) + return error.LLVMBackendDoesNotSupportPlan9; + const ptr_width: PtrWidth = switch (options.target.cpu.arch.ptrBitWidth()) { + 0...32 => .p32, + 33...64 => .p64, + else => return error.UnsupportedELFArchitecture, + }; const self = try gpa.create(Plan9); self.* = .{ .base = .{ @@ -41,25 +72,157 @@ pub fn createEmpty(gpa: *Allocator, options: link.Options) !*Plan9 { .allocator = gpa, .file = null, }, + .ptr_width = ptr_width, }; return self; } -pub fn updateDecl(self: *Plan9, module: *Module, decl: *Module.Decl) !void {} +pub fn updateDecl(self: *Plan9, module: *Module, decl: *Module.Decl) !void { + _ = try self.decl_table.getOrPut(self.base.allocator, decl); +} -pub fn allocateDeclIndexes(self: *Plan9, decl: *Module.Decl) !void {} +pub fn flush(self: *Plan9, comp: *Compilation) !void { + assert(!self.base.options.use_lld); + + switch (self.base.options.effectiveOutputMode()) { + .Exe => {}, + // plan9 object files are totally different + .Obj => return error.TODOImplementPlan9Objs, + .Lib => return error.TODOImplementWritingLibFiles, + } + return self.flushModule(comp); +} +pub fn flushModule(self: *Plan9, comp: *Compilation) !void { + const module = self.base.options.module orelse return error.LinkingWithoutZigSourceUnimplemented; + + // generate the header + self.hdr.magic = try aout.magicFromArch(self.base.options.target.cpu.arch); + const file = self.base.file.?; + try file.seekTo(self.headerSize()); + + // temporary buffer + var code_buffer = std.ArrayList(u8).init(self.base.allocator); + defer code_buffer.deinit(); + { + for (self.decl_table.keys()) |decl| { + if (!decl.has_tv) continue; + self.cur_decl = decl; + const is_fn = (decl.ty.zigTypeTag() == .Fn); + decl.link.plan9 = if (is_fn) .{ + .offset = @intCast(u32, self.text_buf.items.len), + .type = .text, + } else .{ + .offset = @intCast(u32, self.data_buf.items.len), + .type = .data, + }; + const res = try codegen.generateSymbol(&self.base, decl.srcLoc(), .{ + .ty = decl.ty, + .val = decl.val, + }, &code_buffer, .{ .none = {} }); + const code = switch (res) { + .externally_managed => |x| x, + .appended => code_buffer.items, + .fail => |em| { + decl.analysis = .codegen_failure; + try module.failed_decls.put(module.gpa, decl, em); + // TODO try to do more decls + return; + }, + }; + if (is_fn) + try self.text_buf.appendSlice(self.base.allocator, code) + else + try self.data_buf.appendSlice(self.base.allocator, code); + code_buffer.items.len = 0; + } + } + try file.writeAll(self.text_buf.items); + try file.writeAll(self.data_buf.items); + try file.seekTo(0); + self.hdr.text = @intCast(u32, self.text_buf.items.len); + self.hdr.data = @intCast(u32, self.data_buf.items.len); + self.hdr.pcsz = 0; + self.hdr.spsz = 0; + inline for (std.meta.fields(aout.ExecHdr)) |f| { + try file.writer().writeIntBig(f.field_type, @field(self.hdr, f.name)); + } +} +pub fn freeDecl(self: *Plan9, decl: *Module.Decl) void { + assert(self.decl_table.swapRemove(decl)); +} -pub fn flush(self: *Plan9, comp: *Compilation) !void {} -pub fn flushModule(self: *Plan9, comp: *Compilation) !void {} -pub fn freeDecl(self: *Plan9, decl: *Module.Decl) void {} pub fn updateDeclExports( self: *Plan9, module: *Module, decl: *Module.Decl, exports: []const *Module.Export, -) !void {} -pub fn deinit(self: *Plan9) void {} +) !void { + for (exports) |exp| { + if (exp.options.section) |section_name| { + if (!mem.eql(u8, section_name, ".text")) { + try module.failed_exports.ensureCapacity(module.gpa, module.failed_exports.count() + 1); + module.failed_exports.putAssumeCapacityNoClobber( + exp, + try Module.ErrorMsg.create(self.base.allocator, decl.srcLoc(), "plan9 does not support extra sections", .{}), + ); + continue; + } + } + if (std.mem.eql(u8, exp.options.name, "_start")) { + std.debug.assert(decl.link.plan9.type == .text); // we tried to link a non-function as _start + self.hdr.entry = Plan9.default_base_addr + self.headerSize() + decl.link.plan9.offset; + } + if (exp.link.plan9) |i| { + const sym = &self.syms.items[i]; + sym.* = .{ + .value = decl.link.plan9.offset, + .type = switch (decl.link.plan9.type) { + .text => .T, + .data => .D, + }, + .name = decl.name, + }; + } else { + try self.syms.append(self.base.allocator, .{ + .value = decl.link.plan9.offset, + .type = switch (decl.link.plan9.type) { + .text => .T, + .data => .D, + }, + .name = decl.name, + }); + } + } +} +pub fn deinit(self: *Plan9) void { + self.decl_table.deinit(self.base.allocator); + self.call_relocs.deinit(self.base.allocator); + self.syms.deinit(self.base.allocator); + self.text_buf.deinit(self.base.allocator); + self.data_buf.deinit(self.base.allocator); +} -pub const Export = struct { - sym_index: ?u32 = null, -}; +pub const Export = ?usize; +pub const base_tag = .plan9; +pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Options) !*Plan9 { + if (options.use_llvm) + return error.LLVMBackendDoesNotSupportPlan9; + assert(options.object_format == .plan9); + const file = try options.emit.?.directory.handle.createFile(sub_path, .{ + .truncate = false, + .read = true, + .mode = link.determineMode(options), + }); + errdefer file.close(); + + const self = try createEmpty(allocator, options); + errdefer self.base.destroy(); + + self.base.file = file; + return self; +} + +pub fn addCallReloc(self: *Plan9, code: *std.ArrayList(u8), reloc: CallReloc) !void { + try self.call_relocs.append(self.base.allocator, reloc); + try code.writer().writeIntBig(u64, 0xdeadbeef); +} diff --git a/src/link/plan9/a.out.zig b/src/link/plan9/a.out.zig new file mode 100644 index 0000000000..724bd4fa25 --- /dev/null +++ b/src/link/plan9/a.out.zig @@ -0,0 +1,122 @@ +// Copyright © 2021 Plan 9 Foundation +// Copyright © 20XX 9front authors + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: + +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. + +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +// Idomatic translation of 9front a.out.h +const std = @import("std"); +// all integers are in big-endian format (needs a byteswap) +pub const ExecHdr = struct { + magic: u32, + text: u32, + data: u32, + bss: u32, + syms: u32, + entry: u32, + spsz: u32, + pcsz: u32, + pub fn size() u8 { + return 32; + } +}; + +// uchar value[4]; +// char type; +// char name[n]; /* NUL-terminated */ +pub const Sym32 = struct { + value: u32, // big endian in the file + type: SymType, + name: [*:0]const u8, +}; +// uchar value[8]; +// char type; +// char name[n]; /* NUL-terminated */ +pub const Sym64 = struct { + value: u64, // big endian in the file + type: SymType, + name: [*:0]const u8, +}; +// The type field is one of the following characters with the +// high bit set: +// T text segment symbol +// t static text segment symbol +// L leaf function text segment symbol +// l static leaf function text segment symbol +// D data segment symbol +// d static data segment symbol +// B bss segment symbol +// b static bss segment symbol +// a automatic (local) variable symbol +// p function parameter symbol +// f source file name components +// z source file name +// Z source file line offset +// m for '.frame' +pub const SymType = enum(u8) { + T = 0x80 | 'T', + t = 0x80 | 't', + L = 0x80 | 'L', + l = 0x80 | 'l', + D = 0x80 | 'D', + d = 0x80 | 'd', + B = 0x80 | 'B', + b = 0x80 | 'b', + a = 0x80 | 'a', + p = 0x80 | 'p', + f = 0x80 | 'f', + z = 0x80 | 'z', + Z = 0x80 | 'Z', + m = 0x80 | 'm', +}; + +pub const HDR_MAGIC = @import("std").meta.promoteIntLiteral(c_int, 0x00008000, .hexadecimal); +pub inline fn _MAGIC(f: anytype, b: anytype) @TypeOf(f | ((((@as(c_int, 4) * b) + @as(c_int, 0)) * b) + @as(c_int, 7))) { + return f | ((((@as(c_int, 4) * b) + @as(c_int, 0)) * b) + @as(c_int, 7)); +} +pub const A_MAGIC = _MAGIC(@as(c_int, 0), @as(c_int, 8)); // 68020 +pub const I_MAGIC = _MAGIC(@as(c_int, 0), @as(c_int, 11)); // intel 386 +pub const J_MAGIC = _MAGIC(@as(c_int, 0), @as(c_int, 12)); // intel 960 (retired) +pub const K_MAGIC = _MAGIC(@as(c_int, 0), @as(c_int, 13)); // sparc +pub const V_MAGIC = _MAGIC(@as(c_int, 0), @as(c_int, 16)); // mips 3000 BE +pub const X_MAGIC = _MAGIC(@as(c_int, 0), @as(c_int, 17)); // att dsp 3210 (retired) +pub const M_MAGIC = _MAGIC(@as(c_int, 0), @as(c_int, 18)); // mips 4000 BE +pub const D_MAGIC = _MAGIC(@as(c_int, 0), @as(c_int, 19)); // amd 29000 (retired) +pub const E_MAGIC = _MAGIC(@as(c_int, 0), @as(c_int, 20)); // arm +pub const Q_MAGIC = _MAGIC(@as(c_int, 0), @as(c_int, 21)); // powerpc +pub const N_MAGIC = _MAGIC(@as(c_int, 0), @as(c_int, 22)); // mips 4000 LE +pub const L_MAGIC = _MAGIC(@as(c_int, 0), @as(c_int, 23)); // dec alpha (retired) +pub const P_MAGIC = _MAGIC(@as(c_int, 0), @as(c_int, 24)); // mips 3000 LE +pub const U_MAGIC = _MAGIC(@as(c_int, 0), @as(c_int, 25)); // sparc64 +pub const S_MAGIC = _MAGIC(HDR_MAGIC, @as(c_int, 26)); // amd64 +pub const T_MAGIC = _MAGIC(HDR_MAGIC, @as(c_int, 27)); // powerpc64 +pub const R_MAGIC = _MAGIC(HDR_MAGIC, @as(c_int, 28)); // arm64 + +pub fn magicFromArch(arch: std.Target.Cpu.Arch) !u32 { + return switch (arch) { + .i386 => I_MAGIC, + .sparc => K_MAGIC, // TODO should sparcv9 and sparcel go here? + .mips => V_MAGIC, + .arm => E_MAGIC, + .aarch64 => R_MAGIC, + .powerpc => Q_MAGIC, + .powerpc64 => T_MAGIC, + .x86_64 => S_MAGIC, + else => error.ArchNotSupportedByPlan9, + }; +} From c114474190fd6fe48b8c74becfb39ed82287d059 Mon Sep 17 00:00:00 2001 From: Jacob G-W Date: Sun, 6 Jun 2021 10:27:12 -0400 Subject: [PATCH 03/11] plan9 linker: write symbol table Also switch to iovecs because gotta go fast. --- src/link/Plan9.zig | 94 ++++++++++++++++++++++++++++++++-------- src/link/plan9/a.out.zig | 56 ++++++++++++++---------- 2 files changed, 109 insertions(+), 41 deletions(-) diff --git a/src/link/Plan9.zig b/src/link/Plan9.zig index d179836fa6..35c0982699 100644 --- a/src/link/Plan9.zig +++ b/src/link/Plan9.zig @@ -32,16 +32,18 @@ hdr: aout.ExecHdr = undefined, fn headerSize(self: Plan9) u32 { // fat header (currently unused) - const fat: u4 = if (self.ptr_width == .p64) 8 else 0; - return aout.ExecHdr.size() + fat; + const fat: u8 = if (self.ptr_width == .p64) 8 else 0; + return @sizeOf(aout.ExecHdr) + fat; } pub const DeclBlock = struct { type: enum { text, data }, // offset in the text or data sects offset: u32, + sym_index: usize, pub const empty = DeclBlock{ .type = .text, .offset = 0, + .sym_index = 0, }; }; @@ -62,7 +64,7 @@ pub fn createEmpty(gpa: *Allocator, options: link.Options) !*Plan9 { const ptr_width: PtrWidth = switch (options.target.cpu.arch.ptrBitWidth()) { 0...32 => .p32, 33...64 => .p64, - else => return error.UnsupportedELFArchitecture, + else => return error.UnsupportedP9Architecture, }; const self = try gpa.create(Plan9); self.* = .{ @@ -93,13 +95,14 @@ pub fn flush(self: *Plan9, comp: *Compilation) !void { return self.flushModule(comp); } pub fn flushModule(self: *Plan9, comp: *Compilation) !void { + const tracy = trace(@src()); + defer tracy.end(); + const module = self.base.options.module orelse return error.LinkingWithoutZigSourceUnimplemented; - // generate the header - self.hdr.magic = try aout.magicFromArch(self.base.options.target.cpu.arch); - const file = self.base.file.?; - try file.seekTo(self.headerSize()); - + self.text_buf.items.len = 0; + self.data_buf.items.len = 0; + self.call_relocs.items.len = 0; // temporary buffer var code_buffer = std.ArrayList(u8).init(self.base.allocator); defer code_buffer.deinit(); @@ -111,10 +114,32 @@ pub fn flushModule(self: *Plan9, comp: *Compilation) !void { decl.link.plan9 = if (is_fn) .{ .offset = @intCast(u32, self.text_buf.items.len), .type = .text, + .sym_index = decl.link.plan9.sym_index, } else .{ .offset = @intCast(u32, self.data_buf.items.len), .type = .data, + .sym_index = decl.link.plan9.sym_index, }; + if (decl.link.plan9.sym_index == 0) { + try self.syms.append(self.base.allocator, .{ + .value = decl.link.plan9.offset, + .type = switch (decl.link.plan9.type) { + .text => .t, + .data => .d, + }, + .name = mem.span(decl.name), + }); + decl.link.plan9.sym_index = self.syms.items.len - 1; + } else { + self.syms.items[decl.link.plan9.sym_index] = .{ + .value = decl.link.plan9.offset, + .type = switch (decl.link.plan9.type) { + .text => .t, + .data => .d, + }, + .name = mem.span(decl.name), + }; + } const res = try codegen.generateSymbol(&self.base, decl.srcLoc(), .{ .ty = decl.ty, .val = decl.val, @@ -136,16 +161,35 @@ pub fn flushModule(self: *Plan9, comp: *Compilation) !void { code_buffer.items.len = 0; } } - try file.writeAll(self.text_buf.items); - try file.writeAll(self.data_buf.items); - try file.seekTo(0); + + var sym_buf = std.ArrayList(u8).init(self.base.allocator); + defer sym_buf.deinit(); + try self.writeSyms(&sym_buf); + + // generate the header + self.hdr.magic = try aout.magicFromArch(self.base.options.target.cpu.arch); self.hdr.text = @intCast(u32, self.text_buf.items.len); self.hdr.data = @intCast(u32, self.data_buf.items.len); + self.hdr.syms = @intCast(u32, sym_buf.items.len); + self.hdr.bss = 0; self.hdr.pcsz = 0; self.hdr.spsz = 0; - inline for (std.meta.fields(aout.ExecHdr)) |f| { - try file.writer().writeIntBig(f.field_type, @field(self.hdr, f.name)); - } + + const file = self.base.file.?; + + const hdr_buf = self.hdr.toU8s(); + const hdr_slice: []const u8 = &hdr_buf; + // account for the fat header + const hdr_size: u8 = if (self.ptr_width == .p32) 32 else 40; + // write it all! + var vectors: [4]std.os.iovec_const = .{ + .{ .iov_base = hdr_slice.ptr, .iov_len = hdr_size }, + .{ .iov_base = self.text_buf.items.ptr, .iov_len = self.text_buf.items.len }, + .{ .iov_base = self.data_buf.items.ptr, .iov_len = self.data_buf.items.len }, + .{ .iov_base = sym_buf.items.ptr, .iov_len = sym_buf.items.len }, + // TODO spsz, pcsz + }; + try file.pwritevAll(&vectors, 0); } pub fn freeDecl(self: *Plan9, decl: *Module.Decl) void { assert(self.decl_table.swapRemove(decl)); @@ -173,14 +217,13 @@ pub fn updateDeclExports( self.hdr.entry = Plan9.default_base_addr + self.headerSize() + decl.link.plan9.offset; } if (exp.link.plan9) |i| { - const sym = &self.syms.items[i]; - sym.* = .{ + self.syms.items[i] = .{ .value = decl.link.plan9.offset, .type = switch (decl.link.plan9.type) { .text => .T, .data => .D, }, - .name = decl.name, + .name = exp.options.name, }; } else { try self.syms.append(self.base.allocator, .{ @@ -189,8 +232,9 @@ pub fn updateDeclExports( .text => .T, .data => .D, }, - .name = decl.name, + .name = exp.options.name, }); + exp.link.plan9 = self.syms.items.len - 1; } } } @@ -226,3 +270,17 @@ pub fn addCallReloc(self: *Plan9, code: *std.ArrayList(u8), reloc: CallReloc) !v try self.call_relocs.append(self.base.allocator, reloc); try code.writer().writeIntBig(u64, 0xdeadbeef); } + +pub fn writeSyms(self: *Plan9, buf: *std.ArrayList(u8)) !void { + const writer = buf.writer(); + for (self.syms.items) |sym| { + if (self.ptr_width == .p32) { + try writer.writeIntBig(u32, @intCast(u32, sym.value)); + } else { + try writer.writeIntBig(u64, sym.value); + } + try writer.writeByte(@enumToInt(sym.type)); + try writer.writeAll(std.mem.span(sym.name)); + try writer.writeByte(0); + } +} diff --git a/src/link/plan9/a.out.zig b/src/link/plan9/a.out.zig index 724bd4fa25..3c41862593 100644 --- a/src/link/plan9/a.out.zig +++ b/src/link/plan9/a.out.zig @@ -22,7 +22,7 @@ // Idomatic translation of 9front a.out.h const std = @import("std"); // all integers are in big-endian format (needs a byteswap) -pub const ExecHdr = struct { +pub const ExecHdr = extern struct { magic: u32, text: u32, data: u32, @@ -31,8 +31,18 @@ pub const ExecHdr = struct { entry: u32, spsz: u32, pcsz: u32, - pub fn size() u8 { - return 32; + comptime { + std.debug.assert(@sizeOf(@This()) == 32); + } + /// it is up to the caller to disgard the last 8 bytes if the header is not fat + pub fn toU8s(self: *@This()) [40]u8 { + var buf: [40]u8 = undefined; + var i: u8 = 0; + inline for (std.meta.fields(@This())) |f| { + std.mem.writeIntSliceBig(u32, buf[i .. i + 4], @field(self, f.name)); + i += 4; + } + return buf; } }; @@ -42,7 +52,7 @@ pub const ExecHdr = struct { pub const Sym32 = struct { value: u32, // big endian in the file type: SymType, - name: [*:0]const u8, + name: []const u8, }; // uchar value[8]; // char type; @@ -50,7 +60,7 @@ pub const Sym32 = struct { pub const Sym64 = struct { value: u64, // big endian in the file type: SymType, - name: [*:0]const u8, + name: []const u8, }; // The type field is one of the following characters with the // high bit set: @@ -85,27 +95,27 @@ pub const SymType = enum(u8) { m = 0x80 | 'm', }; -pub const HDR_MAGIC = @import("std").meta.promoteIntLiteral(c_int, 0x00008000, .hexadecimal); +pub const HDR_MAGIC = 0x00008000; pub inline fn _MAGIC(f: anytype, b: anytype) @TypeOf(f | ((((@as(c_int, 4) * b) + @as(c_int, 0)) * b) + @as(c_int, 7))) { return f | ((((@as(c_int, 4) * b) + @as(c_int, 0)) * b) + @as(c_int, 7)); } -pub const A_MAGIC = _MAGIC(@as(c_int, 0), @as(c_int, 8)); // 68020 -pub const I_MAGIC = _MAGIC(@as(c_int, 0), @as(c_int, 11)); // intel 386 -pub const J_MAGIC = _MAGIC(@as(c_int, 0), @as(c_int, 12)); // intel 960 (retired) -pub const K_MAGIC = _MAGIC(@as(c_int, 0), @as(c_int, 13)); // sparc -pub const V_MAGIC = _MAGIC(@as(c_int, 0), @as(c_int, 16)); // mips 3000 BE -pub const X_MAGIC = _MAGIC(@as(c_int, 0), @as(c_int, 17)); // att dsp 3210 (retired) -pub const M_MAGIC = _MAGIC(@as(c_int, 0), @as(c_int, 18)); // mips 4000 BE -pub const D_MAGIC = _MAGIC(@as(c_int, 0), @as(c_int, 19)); // amd 29000 (retired) -pub const E_MAGIC = _MAGIC(@as(c_int, 0), @as(c_int, 20)); // arm -pub const Q_MAGIC = _MAGIC(@as(c_int, 0), @as(c_int, 21)); // powerpc -pub const N_MAGIC = _MAGIC(@as(c_int, 0), @as(c_int, 22)); // mips 4000 LE -pub const L_MAGIC = _MAGIC(@as(c_int, 0), @as(c_int, 23)); // dec alpha (retired) -pub const P_MAGIC = _MAGIC(@as(c_int, 0), @as(c_int, 24)); // mips 3000 LE -pub const U_MAGIC = _MAGIC(@as(c_int, 0), @as(c_int, 25)); // sparc64 -pub const S_MAGIC = _MAGIC(HDR_MAGIC, @as(c_int, 26)); // amd64 -pub const T_MAGIC = _MAGIC(HDR_MAGIC, @as(c_int, 27)); // powerpc64 -pub const R_MAGIC = _MAGIC(HDR_MAGIC, @as(c_int, 28)); // arm64 +pub const A_MAGIC = _MAGIC(0, 8); // 68020 +pub const I_MAGIC = _MAGIC(0, 11); // intel 386 +pub const J_MAGIC = _MAGIC(0, 12); // intel 960 (retired) +pub const K_MAGIC = _MAGIC(0, 13); // sparc +pub const V_MAGIC = _MAGIC(0, 16); // mips 3000 BE +pub const X_MAGIC = _MAGIC(0, 17); // att dsp 3210 (retired) +pub const M_MAGIC = _MAGIC(0, 18); // mips 4000 BE +pub const D_MAGIC = _MAGIC(0, 19); // amd 29000 (retired) +pub const E_MAGIC = _MAGIC(0, 20); // arm +pub const Q_MAGIC = _MAGIC(0, 21); // powerpc +pub const N_MAGIC = _MAGIC(0, 22); // mips 4000 LE +pub const L_MAGIC = _MAGIC(0, 23); // dec alpha (retired) +pub const P_MAGIC = _MAGIC(0, 24); // mips 3000 LE +pub const U_MAGIC = _MAGIC(0, 25); // sparc64 +pub const S_MAGIC = _MAGIC(HDR_MAGIC, 26); // amd64 +pub const T_MAGIC = _MAGIC(HDR_MAGIC, 27); // powerpc64 +pub const R_MAGIC = _MAGIC(HDR_MAGIC, 28); // arm64 pub fn magicFromArch(arch: std.Target.Cpu.Arch) !u32 { return switch (arch) { From 3e59c1502556493b29171c2d22447dd70670ca02 Mon Sep 17 00:00:00 2001 From: Jacob G-W Date: Tue, 8 Jun 2021 20:12:18 -0400 Subject: [PATCH 04/11] plan9 linker: do relocations --- src/codegen.zig | 60 ++++++++++++++++++++++++++++++++++++++-------- src/link/Plan9.zig | 57 ++++++++++++++++++++++++++++++++----------- 2 files changed, 93 insertions(+), 24 deletions(-) diff --git a/src/codegen.zig b/src/codegen.zig index 61607407d3..b70c06c7d6 100644 --- a/src/codegen.zig +++ b/src/codegen.zig @@ -2557,16 +2557,56 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { return self.fail(inst.base.src, "TODO implement calling runtime known function pointer", .{}); } } else if (self.bin_file.cast(link.File.Plan9)) |p9| { - if (inst.func.value()) |func_value| { - if (func_value.castTag(.function)) |func_payload| { - try p9.addCallReloc(self.code, .{ - .caller = p9.cur_decl, - .callee = func_payload.data.owner_decl, - .offset_in_caller = self.code.items.len, - }); - } else return self.fail(inst.base.src, "TODO implement calling extern fn on plan9", .{}); - } else { - return self.fail(inst.base.src, "TODO implement calling runtime known function pointer", .{}); + switch (arch) { + .x86_64 => { + for (info.args) |mc_arg, arg_i| { + const arg = inst.args[arg_i]; + const arg_mcv = try self.resolveInst(inst.args[arg_i]); + // Here we do not use setRegOrMem even though the logic is similar, because + // the function call will move the stack pointer, so the offsets are different. + switch (mc_arg) { + .none => continue, + .register => |reg| { + try self.register_manager.getReg(reg, null); + try self.genSetReg(arg.src, arg.ty, reg, arg_mcv); + }, + .stack_offset => { + // Here we need to emit instructions like this: + // mov qword ptr [rsp + stack_offset], x + return self.fail(inst.base.src, "TODO implement calling with parameters in memory", .{}); + }, + .ptr_stack_offset => { + return self.fail(inst.base.src, "TODO implement calling with MCValue.ptr_stack_offset arg", .{}); + }, + .ptr_embedded_in_code => { + return self.fail(inst.base.src, "TODO implement calling with MCValue.ptr_embedded_in_code arg", .{}); + }, + .undef => unreachable, + .immediate => unreachable, + .unreach => unreachable, + .dead => unreachable, + .embedded_in_code => unreachable, + .memory => unreachable, + .compare_flags_signed => unreachable, + .compare_flags_unsigned => unreachable, + } + } + if (inst.func.value()) |func_value| { + if (func_value.castTag(.function)) |func_payload| { + try self.genSetReg(inst.base.src, Type.initTag(.u64), .rax, .{ .immediate = 0xdeadbeefdeadbeef }); + try p9.addCallReloc(self.code, .{ + .caller = p9.cur_decl, + .callee = func_payload.data.owner_decl, + .offset_in_caller = self.code.items.len, + }); + // call rax + try self.code.appendSlice(&.{ 0xff, 0xd0 }); + } else return self.fail(inst.base.src, "TODO implement calling extern fn on plan9", .{}); + } else { + return self.fail(inst.base.src, "TODO implement calling runtime known function pointer", .{}); + } + }, + else => return self.fail(inst.base.src, "TODO implement call on plan9 for {}", .{self.target.cpu.arch}), } } else unreachable; diff --git a/src/link/Plan9.zig b/src/link/Plan9.zig index 35c0982699..5532e056fc 100644 --- a/src/link/Plan9.zig +++ b/src/link/Plan9.zig @@ -39,11 +39,12 @@ pub const DeclBlock = struct { type: enum { text, data }, // offset in the text or data sects offset: u32, - sym_index: usize, + // offset into syms + sym_index: ?usize, pub const empty = DeclBlock{ .type = .text, .offset = 0, - .sym_index = 0, + .sym_index = null, }; }; @@ -98,6 +99,8 @@ pub fn flushModule(self: *Plan9, comp: *Compilation) !void { const tracy = trace(@src()); defer tracy.end(); + defer assert(self.hdr.entry != 0x0); + const module = self.base.options.module orelse return error.LinkingWithoutZigSourceUnimplemented; self.text_buf.items.len = 0; @@ -120,7 +123,7 @@ pub fn flushModule(self: *Plan9, comp: *Compilation) !void { .type = .data, .sym_index = decl.link.plan9.sym_index, }; - if (decl.link.plan9.sym_index == 0) { + if (decl.link.plan9.sym_index == null) { try self.syms.append(self.base.allocator, .{ .value = decl.link.plan9.offset, .type = switch (decl.link.plan9.type) { @@ -131,7 +134,7 @@ pub fn flushModule(self: *Plan9, comp: *Compilation) !void { }); decl.link.plan9.sym_index = self.syms.items.len - 1; } else { - self.syms.items[decl.link.plan9.sym_index] = .{ + self.syms.items[decl.link.plan9.sym_index.?] = .{ .value = decl.link.plan9.offset, .type = switch (decl.link.plan9.type) { .text => .t, @@ -162,18 +165,40 @@ pub fn flushModule(self: *Plan9, comp: *Compilation) !void { } } + // Do relocations. + { + for (self.call_relocs.items) |reloc| { + const l: DeclBlock = reloc.caller.link.plan9; + assert(l.sym_index != null); // we didn't process it already + const endian = self.base.options.target.cpu.arch.endian(); + if (self.ptr_width == .p32) { + const callee_offset = @truncate(u32, reloc.callee.link.plan9.offset + default_base_addr); // TODO this is different if its data + const off = reloc.offset_in_caller + l.offset; + std.mem.writeInt(u32, self.text_buf.items[off - 4 ..][0..4], callee_offset, endian); + } else { + // what we are writing + const callee_offset = reloc.callee.link.plan9.offset + default_base_addr; // TODO this is different if its data + const off = reloc.offset_in_caller + l.offset; + std.mem.writeInt(u64, self.text_buf.items[off - 8 ..][0..8], callee_offset, endian); + } + } + } + var sym_buf = std.ArrayList(u8).init(self.base.allocator); defer sym_buf.deinit(); try self.writeSyms(&sym_buf); // generate the header - self.hdr.magic = try aout.magicFromArch(self.base.options.target.cpu.arch); - self.hdr.text = @intCast(u32, self.text_buf.items.len); - self.hdr.data = @intCast(u32, self.data_buf.items.len); - self.hdr.syms = @intCast(u32, sym_buf.items.len); - self.hdr.bss = 0; - self.hdr.pcsz = 0; - self.hdr.spsz = 0; + self.hdr = .{ + .magic = try aout.magicFromArch(self.base.options.target.cpu.arch), + .text = @intCast(u32, self.text_buf.items.len), + .data = @intCast(u32, self.data_buf.items.len), + .syms = @intCast(u32, sym_buf.items.len), + .bss = 0, + .pcsz = 0, + .spsz = 0, + .entry = self.hdr.entry, + }; const file = self.base.file.?; @@ -262,22 +287,26 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio const self = try createEmpty(allocator, options); errdefer self.base.destroy(); + if (std.builtin.mode == .Debug or std.builtin.mode == .ReleaseSafe) + self.hdr.entry = 0x0; + self.base.file = file; return self; } +// tells its future self to write the addr of the callee decl into offset_in_caller. +// writes it to the {4, 8} bytes before offset_in_caller pub fn addCallReloc(self: *Plan9, code: *std.ArrayList(u8), reloc: CallReloc) !void { try self.call_relocs.append(self.base.allocator, reloc); - try code.writer().writeIntBig(u64, 0xdeadbeef); } pub fn writeSyms(self: *Plan9, buf: *std.ArrayList(u8)) !void { const writer = buf.writer(); for (self.syms.items) |sym| { if (self.ptr_width == .p32) { - try writer.writeIntBig(u32, @intCast(u32, sym.value)); + try writer.writeIntBig(u32, @intCast(u32, sym.value) + default_base_addr); } else { - try writer.writeIntBig(u64, sym.value); + try writer.writeIntBig(u64, sym.value + default_base_addr); } try writer.writeByte(@enumToInt(sym.type)); try writer.writeAll(std.mem.span(sym.name)); From 72bb6bb1430f0b3b32f0cb5e52054293a59abca6 Mon Sep 17 00:00:00 2001 From: Jacob G-W Date: Sat, 12 Jun 2021 16:51:51 -0400 Subject: [PATCH 05/11] plan9 linker: produce an object file that can actually work!!! --- lib/std/start.zig | 59 +++++++++++++++++++++++++++------------------ src/Compilation.zig | 3 +++ src/link/Plan9.zig | 45 ++++++++++++++++++++++++++++------ 3 files changed, 76 insertions(+), 31 deletions(-) diff --git a/lib/std/start.zig b/lib/std/start.zig index 0a6c0320d6..608b3997f1 100644 --- a/lib/std/start.zig +++ b/lib/std/start.zig @@ -95,30 +95,43 @@ fn _start2() callconv(.Naked) noreturn { } fn exit2(code: usize) noreturn { - switch (builtin.stage2_arch) { - .x86_64 => { - asm volatile ("syscall" - : - : [number] "{rax}" (231), - [arg1] "{rdi}" (code) - : "rcx", "r11", "memory" - ); + switch (builtin.stage2_os) { + .linux => switch (builtin.stage2_arch) { + .x86_64 => { + asm volatile ("syscall" + : + : [number] "{rax}" (231), + [arg1] "{rdi}" (code) + : "rcx", "r11", "memory" + ); + }, + .arm => { + asm volatile ("svc #0" + : + : [number] "{r7}" (1), + [arg1] "{r0}" (code) + : "memory" + ); + }, + .aarch64 => { + asm volatile ("svc #0" + : + : [number] "{x8}" (93), + [arg1] "{x0}" (code) + : "memory", "cc" + ); + }, + else => @compileError("TODO"), }, - .arm => { - asm volatile ("svc #0" - : - : [number] "{r7}" (1), - [arg1] "{r0}" (code) - : "memory" - ); - }, - .aarch64 => { - asm volatile ("svc #0" - : - : [number] "{x8}" (93), - [arg1] "{x0}" (code) - : "memory", "cc" - ); + .plan9 => switch (builtin.stage2_arch) { + .x86_64 => { + asm volatile ("syscall" + : + : [number] "{rbp}" (8) + : "rcx", "r11", "memory" + ); + }, + else => @compileError("TODO"), }, else => @compileError("TODO"), } diff --git a/src/Compilation.zig b/src/Compilation.zig index 1cf4151c62..58425197c1 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -3556,6 +3556,8 @@ pub fn generateBuiltinZigSource(comp: *Compilation, allocator: *Allocator) Alloc \\pub const zig_is_stage2 = {}; \\/// Temporary until self-hosted supports the `cpu.arch` value. \\pub const stage2_arch: std.Target.Cpu.Arch = .{}; + \\/// Temporary until self-hosted supports the `os.tag` value. + \\pub const stage2_os: std.Target.Os.Tag = .{}; \\ \\pub const output_mode = std.builtin.OutputMode.{}; \\pub const link_mode = std.builtin.LinkMode.{}; @@ -3571,6 +3573,7 @@ pub fn generateBuiltinZigSource(comp: *Compilation, allocator: *Allocator) Alloc build_options.version, !use_stage1, std.zig.fmtId(@tagName(target.cpu.arch)), + std.zig.fmtId(@tagName(target.os.tag)), std.zig.fmtId(@tagName(comp.bin_file.options.output_mode)), std.zig.fmtId(@tagName(comp.bin_file.options.link_mode)), comp.bin_file.options.is_test, diff --git a/src/link/Plan9.zig b/src/link/Plan9.zig index 5532e056fc..ffd88ddc77 100644 --- a/src/link/Plan9.zig +++ b/src/link/Plan9.zig @@ -48,8 +48,8 @@ pub const DeclBlock = struct { }; }; -// TODO change base addr based on target (right now it just works on amd64) -const default_base_addr = 0x00200000; +// TODO change base addr based on target (and section?) (right now it just works on amd64) +const default_base_addr = 0x00200028; pub const CallReloc = struct { caller: *Module.Decl, @@ -157,11 +157,13 @@ pub fn flushModule(self: *Plan9, comp: *Compilation) !void { return; }, }; - if (is_fn) - try self.text_buf.appendSlice(self.base.allocator, code) - else + if (is_fn) { + try self.text_buf.appendSlice(self.base.allocator, code); + try code_buffer.resize(0); + } else { try self.data_buf.appendSlice(self.base.allocator, code); - code_buffer.items.len = 0; + try code_buffer.resize(0); + } } } @@ -176,7 +178,6 @@ pub fn flushModule(self: *Plan9, comp: *Compilation) !void { const off = reloc.offset_in_caller + l.offset; std.mem.writeInt(u32, self.text_buf.items[off - 4 ..][0..4], callee_offset, endian); } else { - // what we are writing const callee_offset = reloc.callee.link.plan9.offset + default_base_addr; // TODO this is different if its data const off = reloc.offset_in_caller + l.offset; std.mem.writeInt(u64, self.text_buf.items[off - 8 ..][0..8], callee_offset, endian); @@ -184,6 +185,11 @@ pub fn flushModule(self: *Plan9, comp: *Compilation) !void { } } + // edata, end, etext + self.syms.items[0].value = 0x200000; // TODO make this number other place, and what is it? + self.syms.items[1].value = 0x200000; // TODO make this number other place, and what is it? + self.syms.items[2].value = self.text_buf.items.len; + var sym_buf = std.ArrayList(u8).init(self.base.allocator); defer sym_buf.deinit(); try self.writeSyms(&sym_buf); @@ -202,10 +208,14 @@ pub fn flushModule(self: *Plan9, comp: *Compilation) !void { const file = self.base.file.?; - const hdr_buf = self.hdr.toU8s(); + var hdr_buf = self.hdr.toU8s(); const hdr_slice: []const u8 = &hdr_buf; // account for the fat header const hdr_size: u8 = if (self.ptr_width == .p32) 32 else 40; + // write the fat header for debug info + if (self.ptr_width == .p64) { + mem.writeIntSliceBig(u64, hdr_buf[32..40], self.hdr.entry); + } // write it all! var vectors: [4]std.os.iovec_const = .{ .{ .iov_base = hdr_slice.ptr, .iov_len = hdr_size }, @@ -290,6 +300,25 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio if (std.builtin.mode == .Debug or std.builtin.mode == .ReleaseSafe) self.hdr.entry = 0x0; + // first 3 symbols in our table are edata, end, etext + try self.syms.appendSlice(self.base.allocator, &.{ + .{ + .value = 0xcafebabe, + .type = .B, + .name = "edata", + }, + .{ + .value = 0xcafebabe, + .type = .B, + .name = "end", + }, + .{ + .value = 0xcafebabe, + .type = .T, + .name = "etext", + }, + }); + self.base.file = file; return self; } From 166dffb08811cb4ad7ea8ca65ef99b49d2986b94 Mon Sep 17 00:00:00 2001 From: Jacob G-W Date: Sun, 13 Jun 2021 00:07:27 -0400 Subject: [PATCH 06/11] plan9 linker: correct runtime vs file offset converting code --- src/link/Plan9.zig | 103 ++++++++++++++++++++++++--------------- src/link/plan9/a.out.zig | 18 +++---- 2 files changed, 74 insertions(+), 47 deletions(-) diff --git a/src/link/Plan9.zig b/src/link/Plan9.zig index ffd88ddc77..96ab18bd5c 100644 --- a/src/link/Plan9.zig +++ b/src/link/Plan9.zig @@ -19,37 +19,73 @@ const assert = std.debug.assert; base: link.File, ptr_width: PtrWidth, error_flags: File.ErrorFlags = File.ErrorFlags{}, +bases: Bases, decl_table: std.AutoArrayHashMapUnmanaged(*Module.Decl, void) = .{}, /// is just casted down when 32 bit -syms: std.ArrayListUnmanaged(aout.Sym64) = .{}, +syms: std.ArrayListUnmanaged(aout.Sym) = .{}, call_relocs: std.ArrayListUnmanaged(CallReloc) = .{}, text_buf: std.ArrayListUnmanaged(u8) = .{}, data_buf: std.ArrayListUnmanaged(u8) = .{}, cur_decl: *Module.Decl = undefined, hdr: aout.ExecHdr = undefined, +const Bases = struct { text: u32, data: u32 }; + +fn getAddr(self: Plan9, addr: u32, t: aout.SymType) u32 { + return addr + switch (t) { + .T, .t => self.bases.text, + .D, .d, .B, .b => self.bases.data, + else => @panic("get address for more symbol types"), + }; +} +/// opposite of getAddr +fn takeAddr(self: Plan9, addr: u32, t: aout.SymType) u32 { + return addr - switch (t) { + .T, .t => self.bases.text, + .D, .d, .B, .b => self.bases.data, // TODO is .b correct here? + else => @panic("take address for more symbol types"), + }; +} + +fn getSymAddr(self: Plan9, s: aout.Sym) u32 { + return self.getAddr(@intCast(u32, s.value), s.type); +} fn headerSize(self: Plan9) u32 { // fat header (currently unused) const fat: u8 = if (self.ptr_width == .p64) 8 else 0; return @sizeOf(aout.ExecHdr) + fat; } + pub const DeclBlock = struct { - type: enum { text, data }, + type: aout.SymType, // offset in the text or data sects offset: u32, // offset into syms sym_index: ?usize, pub const empty = DeclBlock{ - .type = .text, + .type = .t, .offset = 0, .sym_index = null, }; }; -// TODO change base addr based on target (and section?) (right now it just works on amd64) -const default_base_addr = 0x00200028; +pub fn defaultBaseAddrs(arch: std.Target.Cpu.Arch) Bases { + return switch (arch) { + .x86_64 => .{ + // 0x28 => 40 == header size + .text = 0x200028, + .data = 0x400000, + }, + .i386 => .{ + // 0x20 => 32 == header size + .text = 0x200020, + .data = 0x400000, + }, + else => std.debug.panic("find default base address for {}", .{arch}), + }; +} pub const CallReloc = struct { caller: *Module.Decl, @@ -76,6 +112,7 @@ pub fn createEmpty(gpa: *Allocator, options: link.Options) !*Plan9 { .file = null, }, .ptr_width = ptr_width, + .bases = undefined, }; return self; } @@ -115,31 +152,25 @@ pub fn flushModule(self: *Plan9, comp: *Compilation) !void { self.cur_decl = decl; const is_fn = (decl.ty.zigTypeTag() == .Fn); decl.link.plan9 = if (is_fn) .{ - .offset = @intCast(u32, self.text_buf.items.len), - .type = .text, + .offset = self.getAddr(@intCast(u32, self.text_buf.items.len), .t), + .type = .t, .sym_index = decl.link.plan9.sym_index, } else .{ - .offset = @intCast(u32, self.data_buf.items.len), - .type = .data, + .offset = self.getAddr(@intCast(u32, self.data_buf.items.len), .d), + .type = .t, .sym_index = decl.link.plan9.sym_index, }; if (decl.link.plan9.sym_index == null) { try self.syms.append(self.base.allocator, .{ .value = decl.link.plan9.offset, - .type = switch (decl.link.plan9.type) { - .text => .t, - .data => .d, - }, + .type = decl.link.plan9.type, .name = mem.span(decl.name), }); decl.link.plan9.sym_index = self.syms.items.len - 1; } else { self.syms.items[decl.link.plan9.sym_index.?] = .{ .value = decl.link.plan9.offset, - .type = switch (decl.link.plan9.type) { - .text => .t, - .data => .d, - }, + .type = decl.link.plan9.type, .name = mem.span(decl.name), }; } @@ -174,21 +205,21 @@ pub fn flushModule(self: *Plan9, comp: *Compilation) !void { assert(l.sym_index != null); // we didn't process it already const endian = self.base.options.target.cpu.arch.endian(); if (self.ptr_width == .p32) { - const callee_offset = @truncate(u32, reloc.callee.link.plan9.offset + default_base_addr); // TODO this is different if its data - const off = reloc.offset_in_caller + l.offset; + const callee_offset = @intCast(u32, reloc.callee.link.plan9.offset); + const off = self.takeAddr(@intCast(u32, reloc.offset_in_caller) + l.offset, reloc.caller.link.plan9.type); std.mem.writeInt(u32, self.text_buf.items[off - 4 ..][0..4], callee_offset, endian); } else { - const callee_offset = reloc.callee.link.plan9.offset + default_base_addr; // TODO this is different if its data - const off = reloc.offset_in_caller + l.offset; + const callee_offset = reloc.callee.link.plan9.offset; + const off = self.takeAddr(@intCast(u32, reloc.offset_in_caller) + l.offset, reloc.caller.link.plan9.type); std.mem.writeInt(u64, self.text_buf.items[off - 8 ..][0..8], callee_offset, endian); } } } // edata, end, etext - self.syms.items[0].value = 0x200000; // TODO make this number other place, and what is it? - self.syms.items[1].value = 0x200000; // TODO make this number other place, and what is it? - self.syms.items[2].value = self.text_buf.items.len; + self.syms.items[0].value = self.getAddr(0x0, .b); // what is this number + self.syms.items[1].value = self.getAddr(0x0, .b); // what is this number + self.syms.items[2].value = self.getAddr(@intCast(u32, self.text_buf.items.len), .t); var sym_buf = std.ArrayList(u8).init(self.base.allocator); defer sym_buf.deinit(); @@ -248,25 +279,19 @@ pub fn updateDeclExports( } } if (std.mem.eql(u8, exp.options.name, "_start")) { - std.debug.assert(decl.link.plan9.type == .text); // we tried to link a non-function as _start - self.hdr.entry = Plan9.default_base_addr + self.headerSize() + decl.link.plan9.offset; + std.debug.assert(decl.link.plan9.type == .t); // we tried to link a non-function as _start + self.hdr.entry = self.bases.text + decl.link.plan9.offset; } if (exp.link.plan9) |i| { self.syms.items[i] = .{ - .value = decl.link.plan9.offset, - .type = switch (decl.link.plan9.type) { - .text => .T, - .data => .D, - }, + .value = self.getAddr(decl.link.plan9.offset, decl.link.plan9.type), + .type = decl.link.plan9.type.toGlobal(), .name = exp.options.name, }; } else { try self.syms.append(self.base.allocator, .{ - .value = decl.link.plan9.offset, - .type = switch (decl.link.plan9.type) { - .text => .T, - .data => .D, - }, + .value = self.getAddr(decl.link.plan9.offset, decl.link.plan9.type), + .type = decl.link.plan9.type.toGlobal(), .name = exp.options.name, }); exp.link.plan9 = self.syms.items.len - 1; @@ -300,6 +325,8 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio if (std.builtin.mode == .Debug or std.builtin.mode == .ReleaseSafe) self.hdr.entry = 0x0; + self.bases = defaultBaseAddrs(options.target.cpu.arch); + // first 3 symbols in our table are edata, end, etext try self.syms.appendSlice(self.base.allocator, &.{ .{ @@ -333,9 +360,9 @@ pub fn writeSyms(self: *Plan9, buf: *std.ArrayList(u8)) !void { const writer = buf.writer(); for (self.syms.items) |sym| { if (self.ptr_width == .p32) { - try writer.writeIntBig(u32, @intCast(u32, sym.value) + default_base_addr); + try writer.writeIntBig(u32, @intCast(u32, sym.value)); } else { - try writer.writeIntBig(u64, sym.value + default_base_addr); + try writer.writeIntBig(u64, sym.value); } try writer.writeByte(@enumToInt(sym.type)); try writer.writeAll(std.mem.span(sym.name)); diff --git a/src/link/plan9/a.out.zig b/src/link/plan9/a.out.zig index 3c41862593..daf4acc6ea 100644 --- a/src/link/plan9/a.out.zig +++ b/src/link/plan9/a.out.zig @@ -46,18 +46,10 @@ pub const ExecHdr = extern struct { } }; -// uchar value[4]; -// char type; -// char name[n]; /* NUL-terminated */ -pub const Sym32 = struct { - value: u32, // big endian in the file - type: SymType, - name: []const u8, -}; // uchar value[8]; // char type; // char name[n]; /* NUL-terminated */ -pub const Sym64 = struct { +pub const Sym = struct { value: u64, // big endian in the file type: SymType, name: []const u8, @@ -93,6 +85,14 @@ pub const SymType = enum(u8) { z = 0x80 | 'z', Z = 0x80 | 'Z', m = 0x80 | 'm', + pub fn toGlobal(self: SymType) SymType { + return switch (self) { + .t => .T, + .b => .B, + .d => .D, + else => unreachable, + }; + } }; pub const HDR_MAGIC = 0x00008000; From db2d5b49c693b8a38743232b83a6ee1eab8e6468 Mon Sep 17 00:00:00 2001 From: Jacob G-W Date: Sun, 13 Jun 2021 21:29:32 -0400 Subject: [PATCH 07/11] plan9 linker: use a global offset table this simplifies stuff and makes us not have to use relocations --- src/codegen.zig | 21 +++--- src/link.zig | 2 +- src/link/Plan9.zig | 168 ++++++++++++++++++++++++--------------------- 3 files changed, 104 insertions(+), 87 deletions(-) diff --git a/src/codegen.zig b/src/codegen.zig index b70c06c7d6..87f287b674 100644 --- a/src/codegen.zig +++ b/src/codegen.zig @@ -2593,14 +2593,15 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { } if (inst.func.value()) |func_value| { if (func_value.castTag(.function)) |func_payload| { - try self.genSetReg(inst.base.src, Type.initTag(.u64), .rax, .{ .immediate = 0xdeadbeefdeadbeef }); - try p9.addCallReloc(self.code, .{ - .caller = p9.cur_decl, - .callee = func_payload.data.owner_decl, - .offset_in_caller = self.code.items.len, - }); - // call rax - try self.code.appendSlice(&.{ 0xff, 0xd0 }); + const ptr_bits = self.target.cpu.arch.ptrBitWidth(); + const ptr_bytes: u64 = @divExact(ptr_bits, 8); + const got_addr = p9.bases.data; + const got_index = func_payload.data.owner_decl.link.plan9.got_index.?; + // ff 14 25 xx xx xx xx call [addr] + try self.code.ensureCapacity(self.code.items.len + 7); + self.code.appendSliceAssumeCapacity(&[3]u8{ 0xff, 0x14, 0x25 }); + const fn_got_addr = got_addr + got_index * ptr_bytes; + mem.writeIntLittle(u32, self.code.addManyAsArrayAssumeCapacity(4), @intCast(u32, fn_got_addr)); } else return self.fail(inst.base.src, "TODO implement calling extern fn on plan9", .{}); } else { return self.fail(inst.base.src, "TODO implement calling runtime known function pointer", .{}); @@ -4273,6 +4274,10 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { const decl = payload.data; const got_addr = coff_file.offset_table_virtual_address + decl.link.coff.offset_table_index * ptr_bytes; return MCValue{ .memory = got_addr }; + } else if (self.bin_file.cast(link.File.Plan9)) |p9| { + const decl = payload.data; + const got_addr = p9.bases.data + decl.link.plan9.got_index.? * ptr_bytes; + return MCValue{ .memory = got_addr }; } else { return self.fail(src, "TODO codegen non-ELF const Decl pointer", .{}); } diff --git a/src/link.zig b/src/link.zig index 9447397428..2c1f048f63 100644 --- a/src/link.zig +++ b/src/link.zig @@ -348,7 +348,7 @@ pub const File = struct { .macho => return @fieldParentPtr(MachO, "base", base).allocateDeclIndexes(decl), .c => return @fieldParentPtr(C, "base", base).allocateDeclIndexes(decl), .wasm => return @fieldParentPtr(Wasm, "base", base).allocateDeclIndexes(decl), - .plan9 => {}, + .plan9 => return @fieldParentPtr(Plan9, "base", base).allocateDeclIndexes(decl), .spirv => {}, } } diff --git a/src/link/Plan9.zig b/src/link/Plan9.zig index 96ab18bd5c..5f30ee30a8 100644 --- a/src/link/Plan9.zig +++ b/src/link/Plan9.zig @@ -24,13 +24,19 @@ bases: Bases, decl_table: std.AutoArrayHashMapUnmanaged(*Module.Decl, void) = .{}, /// is just casted down when 32 bit syms: std.ArrayListUnmanaged(aout.Sym) = .{}, -call_relocs: std.ArrayListUnmanaged(CallReloc) = .{}, text_buf: std.ArrayListUnmanaged(u8) = .{}, data_buf: std.ArrayListUnmanaged(u8) = .{}, -cur_decl: *Module.Decl = undefined, hdr: aout.ExecHdr = undefined, -const Bases = struct { text: u32, data: u32 }; + +entry_decl: ?*Module.Decl = null, + +got: std.ArrayListUnmanaged(u64) = .{}, +const Bases = struct { + text: u32, + /// the addr of the got + data: u32, +}; fn getAddr(self: Plan9, addr: u32, t: aout.SymType) u32 { return addr + switch (t) { @@ -60,14 +66,17 @@ fn headerSize(self: Plan9) u32 { pub const DeclBlock = struct { type: aout.SymType, - // offset in the text or data sects - offset: u32, - // offset into syms + /// offset in the text or data sects + offset: ?u32, + /// offset into syms sym_index: ?usize, + /// offset into got + got_index: ?usize, pub const empty = DeclBlock{ .type = .t, - .offset = 0, + .offset = null, .sym_index = null, + .got_index = null, }; }; @@ -87,12 +96,6 @@ pub fn defaultBaseAddrs(arch: std.Target.Cpu.Arch) Bases { }; } -pub const CallReloc = struct { - caller: *Module.Decl, - callee: *Module.Decl, - offset_in_caller: usize, -}; - pub const PtrWidth = enum { p32, p64 }; pub fn createEmpty(gpa: *Allocator, options: link.Options) !*Plan9 { @@ -132,48 +135,89 @@ pub fn flush(self: *Plan9, comp: *Compilation) !void { } return self.flushModule(comp); } + pub fn flushModule(self: *Plan9, comp: *Compilation) !void { const tracy = trace(@src()); defer tracy.end(); + log.debug("flushModule", .{}); + defer assert(self.hdr.entry != 0x0); const module = self.base.options.module orelse return error.LinkingWithoutZigSourceUnimplemented; self.text_buf.items.len = 0; self.data_buf.items.len = 0; - self.call_relocs.items.len = 0; + // ensure space to write the got later + assert(self.got.items.len == self.decl_table.count()); + try self.data_buf.appendNTimes(self.base.allocator, 0x69, self.got.items.len * if (self.ptr_width == .p32) @as(u32, 4) else 8); // temporary buffer var code_buffer = std.ArrayList(u8).init(self.base.allocator); defer code_buffer.deinit(); { for (self.decl_table.keys()) |decl| { if (!decl.has_tv) continue; - self.cur_decl = decl; const is_fn = (decl.ty.zigTypeTag() == .Fn); + + log.debug("update the symbol table and got for decl {*} ({s})", .{ decl, decl.name }); decl.link.plan9 = if (is_fn) .{ .offset = self.getAddr(@intCast(u32, self.text_buf.items.len), .t), .type = .t, .sym_index = decl.link.plan9.sym_index, + .got_index = decl.link.plan9.got_index, } else .{ .offset = self.getAddr(@intCast(u32, self.data_buf.items.len), .d), - .type = .t, + .type = .d, .sym_index = decl.link.plan9.sym_index, + .got_index = decl.link.plan9.got_index, }; - if (decl.link.plan9.sym_index == null) { + self.got.items[decl.link.plan9.got_index.?] = decl.link.plan9.offset.?; + if (decl.link.plan9.sym_index) |s| { + self.syms.items[s] = .{ + .value = decl.link.plan9.offset.?, + .type = decl.link.plan9.type, + .name = mem.span(decl.name), + }; + } else { try self.syms.append(self.base.allocator, .{ - .value = decl.link.plan9.offset, + .value = decl.link.plan9.offset.?, .type = decl.link.plan9.type, .name = mem.span(decl.name), }); decl.link.plan9.sym_index = self.syms.items.len - 1; - } else { - self.syms.items[decl.link.plan9.sym_index.?] = .{ - .value = decl.link.plan9.offset, - .type = decl.link.plan9.type, - .name = mem.span(decl.name), - }; } + + if (module.decl_exports.get(decl)) |exports| { + for (exports) |exp| { + // plan9 does not support custom sections + if (exp.options.section) |section_name| { + if (!mem.eql(u8, section_name, ".text") or !mem.eql(u8, section_name, ".data")) { + try module.failed_exports.put(module.gpa, exp, try Module.ErrorMsg.create(self.base.allocator, decl.srcLoc(), "plan9 does not support extra sections", .{})); + break; + } + } + if (std.mem.eql(u8, exp.options.name, "_start")) { + std.debug.assert(decl.link.plan9.type == .t); // we tried to link a non-function as the entry + self.entry_decl = decl; + } + if (exp.link.plan9) |i| { + self.syms.items[i] = .{ + .value = decl.link.plan9.offset.?, + .type = decl.link.plan9.type.toGlobal(), + .name = exp.options.name, + }; + } else { + try self.syms.append(self.base.allocator, .{ + .value = decl.link.plan9.offset.?, + .type = decl.link.plan9.type.toGlobal(), + .name = exp.options.name, + }); + exp.link.plan9 = self.syms.items.len - 1; + } + } + } + + log.debug("codegen decl {*} ({s})", .{ decl, decl.name }); const res = try codegen.generateSymbol(&self.base, decl.srcLoc(), .{ .ty = decl.ty, .val = decl.val, @@ -190,32 +234,30 @@ pub fn flushModule(self: *Plan9, comp: *Compilation) !void { }; if (is_fn) { try self.text_buf.appendSlice(self.base.allocator, code); - try code_buffer.resize(0); + code_buffer.items.len = 0; } else { try self.data_buf.appendSlice(self.base.allocator, code); - try code_buffer.resize(0); + code_buffer.items.len = 0; } } } - // Do relocations. - { - for (self.call_relocs.items) |reloc| { - const l: DeclBlock = reloc.caller.link.plan9; - assert(l.sym_index != null); // we didn't process it already - const endian = self.base.options.target.cpu.arch.endian(); - if (self.ptr_width == .p32) { - const callee_offset = @intCast(u32, reloc.callee.link.plan9.offset); - const off = self.takeAddr(@intCast(u32, reloc.offset_in_caller) + l.offset, reloc.caller.link.plan9.type); - std.mem.writeInt(u32, self.text_buf.items[off - 4 ..][0..4], callee_offset, endian); - } else { - const callee_offset = reloc.callee.link.plan9.offset; - const off = self.takeAddr(@intCast(u32, reloc.offset_in_caller) + l.offset, reloc.caller.link.plan9.type); - std.mem.writeInt(u64, self.text_buf.items[off - 8 ..][0..8], callee_offset, endian); - } + // write the got + if (self.ptr_width == .p32) { + for (self.got.items) |p, i| { + mem.writeInt(u32, self.data_buf.items[i * 4 ..][0..4], @intCast(u32, p), self.base.options.target.cpu.arch.endian()); + } + } else { + for (self.got.items) |p, i| { + mem.writeInt(u64, self.data_buf.items[i * 8 ..][0..8], p, self.base.options.target.cpu.arch.endian()); } } + if (self.entry_decl == null) { + @panic("TODO we didn't have _start"); + } + self.hdr.entry = self.entry_decl.?.link.plan9.offset.?; + // edata, end, etext self.syms.items[0].value = self.getAddr(0x0, .b); // what is this number self.syms.items[1].value = self.getAddr(0x0, .b); // what is this number @@ -267,43 +309,14 @@ pub fn updateDeclExports( decl: *Module.Decl, exports: []const *Module.Export, ) !void { - for (exports) |exp| { - if (exp.options.section) |section_name| { - if (!mem.eql(u8, section_name, ".text")) { - try module.failed_exports.ensureCapacity(module.gpa, module.failed_exports.count() + 1); - module.failed_exports.putAssumeCapacityNoClobber( - exp, - try Module.ErrorMsg.create(self.base.allocator, decl.srcLoc(), "plan9 does not support extra sections", .{}), - ); - continue; - } - } - if (std.mem.eql(u8, exp.options.name, "_start")) { - std.debug.assert(decl.link.plan9.type == .t); // we tried to link a non-function as _start - self.hdr.entry = self.bases.text + decl.link.plan9.offset; - } - if (exp.link.plan9) |i| { - self.syms.items[i] = .{ - .value = self.getAddr(decl.link.plan9.offset, decl.link.plan9.type), - .type = decl.link.plan9.type.toGlobal(), - .name = exp.options.name, - }; - } else { - try self.syms.append(self.base.allocator, .{ - .value = self.getAddr(decl.link.plan9.offset, decl.link.plan9.type), - .type = decl.link.plan9.type.toGlobal(), - .name = exp.options.name, - }); - exp.link.plan9 = self.syms.items.len - 1; - } - } + // we do all the things in flush } pub fn deinit(self: *Plan9) void { self.decl_table.deinit(self.base.allocator); - self.call_relocs.deinit(self.base.allocator); self.syms.deinit(self.base.allocator); self.text_buf.deinit(self.base.allocator); self.data_buf.deinit(self.base.allocator); + self.got.deinit(self.base.allocator); } pub const Export = ?usize; @@ -350,12 +363,6 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio return self; } -// tells its future self to write the addr of the callee decl into offset_in_caller. -// writes it to the {4, 8} bytes before offset_in_caller -pub fn addCallReloc(self: *Plan9, code: *std.ArrayList(u8), reloc: CallReloc) !void { - try self.call_relocs.append(self.base.allocator, reloc); -} - pub fn writeSyms(self: *Plan9, buf: *std.ArrayList(u8)) !void { const writer = buf.writer(); for (self.syms.items) |sym| { @@ -369,3 +376,8 @@ pub fn writeSyms(self: *Plan9, buf: *std.ArrayList(u8)) !void { try writer.writeByte(0); } } + +pub fn allocateDeclIndexes(self: *Plan9, decl: *Module.Decl) !void { + try self.got.append(self.base.allocator, 0xdeadbeef); + decl.link.plan9.got_index = self.got.items.len - 1; +} From f1aadfa77b2907f0cf93d03d362ee921b0313224 Mon Sep 17 00:00:00 2001 From: Jacob G-W Date: Tue, 15 Jun 2021 16:37:18 -0400 Subject: [PATCH 08/11] plan9 linker: remove panics and improve 64 bit support Don't @intCast when we do not need to, and change some panics to unreachable when they can never happen. --- lib/std/zig.zig | 26 +++++++++++++-- src/link/Plan9.zig | 70 +++++++++++++++++----------------------- src/link/plan9/a.out.zig | 2 ++ 3 files changed, 55 insertions(+), 43 deletions(-) diff --git a/lib/std/zig.zig b/lib/std/zig.zig index 65db02083b..35de030985 100644 --- a/lib/std/zig.zig +++ b/lib/std/zig.zig @@ -181,10 +181,32 @@ pub fn binNameAlloc(allocator: *std.mem.Allocator, options: BinNameOptions) erro .spirv => return std.fmt.allocPrint(allocator, "{s}.spv", .{root_name}), .hex => return std.fmt.allocPrint(allocator, "{s}.ihex", .{root_name}), .raw => return std.fmt.allocPrint(allocator, "{s}.bin", .{root_name}), - // TODO change this to the arbitrary character for plan9 output, eg '6' for amd64 - .plan9 => return std.fmt.allocPrint(allocator, "{s}.out.p9", .{root_name}), + .plan9 => return std.fmt.allocPrint(allocator, "{s}.{c}", .{ root_name, archToPlan9Char(target.cpu.arch) }), } } +fn archToPlan9Char(arch: std.Target.Cpu.Arch) ?u8 { + // copied from 2c(1) + // 0c spim little-endian MIPS 3000 family + // 1c 68000 Motorola MC68000 + // 2c 68020 Motorola MC68020 + // 5c arm little-endian ARM + // 6c amd64 AMD64 and compatibles (e.g., Intel EM64T) + // 7c arm64 ARM64 (ARMv8) + // 8c 386 Intel i386, i486, Pentium, etc. + // kc sparc Sun SPARC + // qc power Power PC + // vc mips big-endian MIPS 3000 family + return switch (arch) { + .arm => '5', + .x86_64 => '6', + .aarch64 => '7', + .i386 => '8', + .sparc => 'k', + .powerpc, .powerpcle => 'q', + .mips, .mipsel => 'v', + else => 'X', // this arch does not have a char or maybe was not ported to plan9 so we just use X + }; +} pub const ParsedCharLiteral = union(enum) { success: u32, diff --git a/src/link/Plan9.zig b/src/link/Plan9.zig index 5f30ee30a8..1724ec446b 100644 --- a/src/link/Plan9.zig +++ b/src/link/Plan9.zig @@ -17,7 +17,7 @@ const assert = std.debug.assert; // TODO use incremental compilation base: link.File, -ptr_width: PtrWidth, +sixtyfour_bit: bool, error_flags: File.ErrorFlags = File.ErrorFlags{}, bases: Bases, @@ -33,41 +33,35 @@ entry_decl: ?*Module.Decl = null, got: std.ArrayListUnmanaged(u64) = .{}, const Bases = struct { - text: u32, + text: u64, /// the addr of the got - data: u32, + data: u64, }; -fn getAddr(self: Plan9, addr: u32, t: aout.SymType) u32 { +fn getAddr(self: Plan9, addr: u64, t: aout.SymType) u64 { return addr + switch (t) { - .T, .t => self.bases.text, + .T, .t, .l, .L => self.bases.text, .D, .d, .B, .b => self.bases.data, - else => @panic("get address for more symbol types"), + else => unreachable, }; } /// opposite of getAddr -fn takeAddr(self: Plan9, addr: u32, t: aout.SymType) u32 { +fn takeAddr(self: Plan9, addr: u64, t: aout.SymType) u64 { return addr - switch (t) { - .T, .t => self.bases.text, - .D, .d, .B, .b => self.bases.data, // TODO is .b correct here? - else => @panic("take address for more symbol types"), + .T, .t, .l, .L => self.bases.text, + .D, .d, .B, .b => self.bases.data, + else => unreachable, }; } -fn getSymAddr(self: Plan9, s: aout.Sym) u32 { - return self.getAddr(@intCast(u32, s.value), s.type); -} - -fn headerSize(self: Plan9) u32 { - // fat header (currently unused) - const fat: u8 = if (self.ptr_width == .p64) 8 else 0; - return @sizeOf(aout.ExecHdr) + fat; +fn getSymAddr(self: Plan9, s: aout.Sym) u64 { + return self.getAddr(s.value, s.type); } pub const DeclBlock = struct { type: aout.SymType, /// offset in the text or data sects - offset: ?u32, + offset: ?u64, /// offset into syms sym_index: ?usize, /// offset into got @@ -101,9 +95,9 @@ pub const PtrWidth = enum { p32, p64 }; pub fn createEmpty(gpa: *Allocator, options: link.Options) !*Plan9 { if (options.use_llvm) return error.LLVMBackendDoesNotSupportPlan9; - const ptr_width: PtrWidth = switch (options.target.cpu.arch.ptrBitWidth()) { - 0...32 => .p32, - 33...64 => .p64, + const sixtyfour_bit: bool = switch (options.target.cpu.arch.ptrBitWidth()) { + 0...32 => false, + 33...64 => true, else => return error.UnsupportedP9Architecture, }; const self = try gpa.create(Plan9); @@ -114,7 +108,7 @@ pub fn createEmpty(gpa: *Allocator, options: link.Options) !*Plan9 { .allocator = gpa, .file = null, }, - .ptr_width = ptr_width, + .sixtyfour_bit = sixtyfour_bit, .bases = undefined, }; return self; @@ -150,7 +144,7 @@ pub fn flushModule(self: *Plan9, comp: *Compilation) !void { self.data_buf.items.len = 0; // ensure space to write the got later assert(self.got.items.len == self.decl_table.count()); - try self.data_buf.appendNTimes(self.base.allocator, 0x69, self.got.items.len * if (self.ptr_width == .p32) @as(u32, 4) else 8); + try self.data_buf.appendNTimes(self.base.allocator, 0x69, self.got.items.len * if (!self.sixtyfour_bit) @as(u32, 4) else 8); // temporary buffer var code_buffer = std.ArrayList(u8).init(self.base.allocator); defer code_buffer.deinit(); @@ -161,12 +155,12 @@ pub fn flushModule(self: *Plan9, comp: *Compilation) !void { log.debug("update the symbol table and got for decl {*} ({s})", .{ decl, decl.name }); decl.link.plan9 = if (is_fn) .{ - .offset = self.getAddr(@intCast(u32, self.text_buf.items.len), .t), + .offset = self.getAddr(self.text_buf.items.len, .t), .type = .t, .sym_index = decl.link.plan9.sym_index, .got_index = decl.link.plan9.got_index, } else .{ - .offset = self.getAddr(@intCast(u32, self.data_buf.items.len), .d), + .offset = self.getAddr(self.data_buf.items.len, .d), .type = .d, .sym_index = decl.link.plan9.sym_index, .got_index = decl.link.plan9.got_index, @@ -243,7 +237,7 @@ pub fn flushModule(self: *Plan9, comp: *Compilation) !void { } // write the got - if (self.ptr_width == .p32) { + if (!self.sixtyfour_bit) { for (self.got.items) |p, i| { mem.writeInt(u32, self.data_buf.items[i * 4 ..][0..4], @intCast(u32, p), self.base.options.target.cpu.arch.endian()); } @@ -253,15 +247,12 @@ pub fn flushModule(self: *Plan9, comp: *Compilation) !void { } } - if (self.entry_decl == null) { - @panic("TODO we didn't have _start"); - } - self.hdr.entry = self.entry_decl.?.link.plan9.offset.?; + self.hdr.entry = @truncate(u32, self.entry_decl.?.link.plan9.offset.?); // edata, end, etext - self.syms.items[0].value = self.getAddr(0x0, .b); // what is this number - self.syms.items[1].value = self.getAddr(0x0, .b); // what is this number - self.syms.items[2].value = self.getAddr(@intCast(u32, self.text_buf.items.len), .t); + self.syms.items[0].value = self.getAddr(0x0, .b); + self.syms.items[1].value = self.getAddr(0x0, .b); + self.syms.items[2].value = self.getAddr(self.text_buf.items.len, .t); var sym_buf = std.ArrayList(u8).init(self.base.allocator); defer sym_buf.deinit(); @@ -284,9 +275,9 @@ pub fn flushModule(self: *Plan9, comp: *Compilation) !void { var hdr_buf = self.hdr.toU8s(); const hdr_slice: []const u8 = &hdr_buf; // account for the fat header - const hdr_size: u8 = if (self.ptr_width == .p32) 32 else 40; - // write the fat header for debug info - if (self.ptr_width == .p64) { + const hdr_size: u8 = if (!self.sixtyfour_bit) 32 else 40; + // write the fat header for 64 bit entry points + if (self.sixtyfour_bit) { mem.writeIntSliceBig(u64, hdr_buf[32..40], self.hdr.entry); } // write it all! @@ -335,9 +326,6 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio const self = try createEmpty(allocator, options); errdefer self.base.destroy(); - if (std.builtin.mode == .Debug or std.builtin.mode == .ReleaseSafe) - self.hdr.entry = 0x0; - self.bases = defaultBaseAddrs(options.target.cpu.arch); // first 3 symbols in our table are edata, end, etext @@ -366,7 +354,7 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio pub fn writeSyms(self: *Plan9, buf: *std.ArrayList(u8)) !void { const writer = buf.writer(); for (self.syms.items) |sym| { - if (self.ptr_width == .p32) { + if (!self.sixtyfour_bit) { try writer.writeIntBig(u32, @intCast(u32, sym.value)); } else { try writer.writeIntBig(u64, sym.value); diff --git a/src/link/plan9/a.out.zig b/src/link/plan9/a.out.zig index daf4acc6ea..85be2d5c36 100644 --- a/src/link/plan9/a.out.zig +++ b/src/link/plan9/a.out.zig @@ -28,6 +28,8 @@ pub const ExecHdr = extern struct { data: u32, bss: u32, syms: u32, + /// You should truncate this to 32 bits on 64 bit systems, then but the actual 8 bytes + /// in the fat header. entry: u32, spsz: u32, pcsz: u32, From 1c2facaf6b670863edf30d7c9d1d47368c4a90cc Mon Sep 17 00:00:00 2001 From: Jacob G-W Date: Mon, 21 Jun 2021 13:10:31 -0400 Subject: [PATCH 09/11] stage2: include enough inline asm support for more plan9 syscalls Also make the exit more correct by exiting 0 rather than random stuff on the stack. --- lib/std/start.zig | 8 ++++++-- src/codegen.zig | 42 ++++++++++++++++++++++++++++++++++++++---- 2 files changed, 44 insertions(+), 6 deletions(-) diff --git a/lib/std/start.zig b/lib/std/start.zig index 608b3997f1..dbbab433d0 100644 --- a/lib/std/start.zig +++ b/lib/std/start.zig @@ -123,11 +123,15 @@ fn exit2(code: usize) noreturn { }, else => @compileError("TODO"), }, + // exits(0) .plan9 => switch (builtin.stage2_arch) { .x86_64 => { - asm volatile ("syscall" + asm volatile ( + \\push $0 + \\push $0 + \\syscall : - : [number] "{rbp}" (8) + : [syscall_number] "{rbp}" (8) : "rcx", "r11", "memory" ); }, diff --git a/src/codegen.zig b/src/codegen.zig index 87f287b674..96a24195ee 100644 --- a/src/codegen.zig +++ b/src/codegen.zig @@ -3330,10 +3330,44 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { try self.genSetReg(inst.base.src, arg.ty, reg, arg_mcv); } - if (mem.eql(u8, inst.asm_source, "syscall")) { - try self.code.appendSlice(&[_]u8{ 0x0f, 0x05 }); - } else if (inst.asm_source.len != 0) { - return self.fail(inst.base.src, "TODO implement support for more x86 assembly instructions", .{}); + { + var iter = std.mem.tokenize(inst.asm_source, "\n\r"); + while (iter.next()) |ins| { + if (mem.eql(u8, ins, "syscall")) { + try self.code.appendSlice(&[_]u8{ 0x0f, 0x05 }); + } else if (mem.indexOf(u8, ins, "push")) |_| { + const arg = ins[4..]; + if (mem.indexOf(u8, arg, "$")) |l| { + 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", .{}); + try self.code.appendSlice(&.{ 0x6a, n }); + } else if (mem.indexOf(u8, arg, "%%")) |l| { + const reg_name = ins[4 + l + 2 ..]; + const reg = parseRegName(reg_name) orelse + return self.fail(inst.base.src, "unrecognized register: '{s}'", .{reg_name}); + const low_id: u8 = reg.low_id(); + if (reg.isExtended()) { + try self.code.appendSlice(&.{ 0x41, 0b1010000 | low_id }); + } else { + try self.code.append(0b1010000 | low_id); + } + } else return self.fail(inst.base.src, "TODO more push operands", .{}); + } else if (mem.indexOf(u8, ins, "pop")) |_| { + const arg = ins[3..]; + if (mem.indexOf(u8, arg, "%%")) |l| { + const reg_name = ins[3 + l + 2 ..]; + const reg = parseRegName(reg_name) orelse + return self.fail(inst.base.src, "unrecognized register: '{s}'", .{reg_name}); + const low_id: u8 = reg.low_id(); + if (reg.isExtended()) { + try self.code.appendSlice(&.{ 0x41, 0b1011000 | low_id }); + } else { + try self.code.append(0b1011000 | low_id); + } + } else return self.fail(inst.base.src, "TODO more pop operands", .{}); + } else { + return self.fail(inst.base.src, "TODO implement support for more x86 assembly instructions", .{}); + } + } } if (inst.output_constraint) |output| { From 4eb778fc3eeec62c5c45ccc0a21631ff757d8a23 Mon Sep 17 00:00:00 2001 From: Jacob G-W Date: Wed, 23 Jun 2021 23:21:02 -0400 Subject: [PATCH 10/11] plan9 linker: remove unused stuff --- src/codegen.zig | 2 +- src/link/Plan9.zig | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/codegen.zig b/src/codegen.zig index 96a24195ee..df65b8b08e 100644 --- a/src/codegen.zig +++ b/src/codegen.zig @@ -2556,7 +2556,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { } else { return self.fail(inst.base.src, "TODO implement calling runtime known function pointer", .{}); } - } else if (self.bin_file.cast(link.File.Plan9)) |p9| { + } else if (self.bin_file.cast(link.File.Plan9)) |_| { switch (arch) { .x86_64 => { for (info.args) |mc_arg, arg_i| { diff --git a/src/link/Plan9.zig b/src/link/Plan9.zig index 1724ec446b..f12b8af6b5 100644 --- a/src/link/Plan9.zig +++ b/src/link/Plan9.zig @@ -115,6 +115,7 @@ pub fn createEmpty(gpa: *Allocator, options: link.Options) !*Plan9 { } pub fn updateDecl(self: *Plan9, module: *Module, decl: *Module.Decl) !void { + _ = module; _ = try self.decl_table.getOrPut(self.base.allocator, decl); } @@ -131,6 +132,7 @@ pub fn flush(self: *Plan9, comp: *Compilation) !void { } pub fn flushModule(self: *Plan9, comp: *Compilation) !void { + _ = comp; const tracy = trace(@src()); defer tracy.end(); @@ -301,6 +303,10 @@ pub fn updateDeclExports( exports: []const *Module.Export, ) !void { // we do all the things in flush + _ = self; + _ = module; + _ = decl; + _ = exports; } pub fn deinit(self: *Plan9) void { self.decl_table.deinit(self.base.allocator); From 476faef97ab0f292159bb3eef42078f9b1e43dde Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 8 Jul 2021 14:24:16 -0700 Subject: [PATCH 11/11] plan9 cleanups * rename files to adhere to conventions * remove unnecessary function / optionality * fix merge conflict * better panic message * remove unnecessary TODO comment * proper namespacing of declarations * clean up documentation comments * no copyright header needed for a brand new zig file that is not copied from anywhere --- CMakeLists.txt | 2 +- lib/std/zig.zig | 48 +++++++------- src/codegen.zig | 2 +- src/link.zig | 2 +- src/link/Plan9.zig | 13 ++-- src/link/Plan9/aout.zig | 114 +++++++++++++++++++++++++++++++++ src/link/plan9/a.out.zig | 134 --------------------------------------- 7 files changed, 148 insertions(+), 167 deletions(-) create mode 100644 src/link/Plan9/aout.zig delete mode 100644 src/link/plan9/a.out.zig diff --git a/CMakeLists.txt b/CMakeLists.txt index f1b3f767e6..44417e4159 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -574,7 +574,7 @@ set(ZIG_STAGE2_SOURCES "${CMAKE_SOURCE_DIR}/src/link/Coff.zig" "${CMAKE_SOURCE_DIR}/src/link/Elf.zig" "${CMAKE_SOURCE_DIR}/src/link/Plan9.zig" - "${CMAKE_SOURCE_DIR}/src/link/plan9/a.out.zig" + "${CMAKE_SOURCE_DIR}/src/link/Plan9/aout.zig" "${CMAKE_SOURCE_DIR}/src/link/MachO.zig" "${CMAKE_SOURCE_DIR}/src/link/MachO/Archive.zig" "${CMAKE_SOURCE_DIR}/src/link/MachO/CodeSignature.zig" diff --git a/lib/std/zig.zig b/lib/std/zig.zig index 35de030985..595dce77c2 100644 --- a/lib/std/zig.zig +++ b/lib/std/zig.zig @@ -181,32 +181,32 @@ pub fn binNameAlloc(allocator: *std.mem.Allocator, options: BinNameOptions) erro .spirv => return std.fmt.allocPrint(allocator, "{s}.spv", .{root_name}), .hex => return std.fmt.allocPrint(allocator, "{s}.ihex", .{root_name}), .raw => return std.fmt.allocPrint(allocator, "{s}.bin", .{root_name}), - .plan9 => return std.fmt.allocPrint(allocator, "{s}.{c}", .{ root_name, archToPlan9Char(target.cpu.arch) }), + .plan9 => { + // copied from 2c(1) + // 0c spim little-endian MIPS 3000 family + // 1c 68000 Motorola MC68000 + // 2c 68020 Motorola MC68020 + // 5c arm little-endian ARM + // 6c amd64 AMD64 and compatibles (e.g., Intel EM64T) + // 7c arm64 ARM64 (ARMv8) + // 8c 386 Intel i386, i486, Pentium, etc. + // kc sparc Sun SPARC + // qc power Power PC + // vc mips big-endian MIPS 3000 family + const char: u8 = switch (target.cpu.arch) { + .arm => '5', + .x86_64 => '6', + .aarch64 => '7', + .i386 => '8', + .sparc => 'k', + .powerpc, .powerpcle => 'q', + .mips, .mipsel => 'v', + else => 'X', // this arch does not have a char or maybe was not ported to plan9 so we just use X + }; + return std.fmt.allocPrint(allocator, "{s}.{c}", .{ root_name, char }); + }, } } -fn archToPlan9Char(arch: std.Target.Cpu.Arch) ?u8 { - // copied from 2c(1) - // 0c spim little-endian MIPS 3000 family - // 1c 68000 Motorola MC68000 - // 2c 68020 Motorola MC68020 - // 5c arm little-endian ARM - // 6c amd64 AMD64 and compatibles (e.g., Intel EM64T) - // 7c arm64 ARM64 (ARMv8) - // 8c 386 Intel i386, i486, Pentium, etc. - // kc sparc Sun SPARC - // qc power Power PC - // vc mips big-endian MIPS 3000 family - return switch (arch) { - .arm => '5', - .x86_64 => '6', - .aarch64 => '7', - .i386 => '8', - .sparc => 'k', - .powerpc, .powerpcle => 'q', - .mips, .mipsel => 'v', - else => 'X', // this arch does not have a char or maybe was not ported to plan9 so we just use X - }; -} pub const ParsedCharLiteral = union(enum) { success: u32, diff --git a/src/codegen.zig b/src/codegen.zig index df65b8b08e..96a24195ee 100644 --- a/src/codegen.zig +++ b/src/codegen.zig @@ -2556,7 +2556,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { } else { return self.fail(inst.base.src, "TODO implement calling runtime known function pointer", .{}); } - } else if (self.bin_file.cast(link.File.Plan9)) |_| { + } else if (self.bin_file.cast(link.File.Plan9)) |p9| { switch (arch) { .x86_64 => { for (info.args) |mc_arg, arg_i| { diff --git a/src/link.zig b/src/link.zig index 2c1f048f63..61a46e8b06 100644 --- a/src/link.zig +++ b/src/link.zig @@ -333,7 +333,7 @@ pub const File = struct { .elf => return @fieldParentPtr(Elf, "base", base).updateDeclLineNumber(module, decl), .macho => return @fieldParentPtr(MachO, "base", base).updateDeclLineNumber(module, decl), .c => return @fieldParentPtr(C, "base", base).updateDeclLineNumber(module, decl), - .plan9 => @panic("PLAN 9 DEBUG INFO"), + .plan9 => @panic("TODO: implement updateDeclLineNumber for plan9"), .wasm, .spirv => {}, } } diff --git a/src/link/Plan9.zig b/src/link/Plan9.zig index f12b8af6b5..f880b40d85 100644 --- a/src/link/Plan9.zig +++ b/src/link/Plan9.zig @@ -1,10 +1,13 @@ +//! This implementation does all the linking work in flush(). A future improvement +//! would be to add incremental linking in a similar way as ELF does. + const Plan9 = @This(); const std = @import("std"); const link = @import("../link.zig"); const Module = @import("../Module.zig"); const Compilation = @import("../Compilation.zig"); -const aout = @import("plan9/a.out.zig"); +const aout = @import("Plan9/aout.zig"); const codegen = @import("../codegen.zig"); const trace = @import("../tracy.zig").trace; const mem = std.mem; @@ -14,8 +17,6 @@ const Allocator = std.mem.Allocator; const log = std.log.scoped(.link); const assert = std.debug.assert; -// TODO use incremental compilation - base: link.File, sixtyfour_bit: bool, error_flags: File.ErrorFlags = File.ErrorFlags{}, @@ -38,7 +39,7 @@ const Bases = struct { data: u64, }; -fn getAddr(self: Plan9, addr: u64, t: aout.SymType) u64 { +fn getAddr(self: Plan9, addr: u64, t: aout.Sym.Type) u64 { return addr + switch (t) { .T, .t, .l, .L => self.bases.text, .D, .d, .B, .b => self.bases.data, @@ -46,7 +47,7 @@ fn getAddr(self: Plan9, addr: u64, t: aout.SymType) u64 { }; } /// opposite of getAddr -fn takeAddr(self: Plan9, addr: u64, t: aout.SymType) u64 { +fn takeAddr(self: Plan9, addr: u64, t: aout.Sym.Type) u64 { return addr - switch (t) { .T, .t, .l, .L => self.bases.text, .D, .d, .B, .b => self.bases.data, @@ -59,7 +60,7 @@ fn getSymAddr(self: Plan9, s: aout.Sym) u64 { } pub const DeclBlock = struct { - type: aout.SymType, + type: aout.Sym.Type, /// offset in the text or data sects offset: ?u64, /// offset into syms diff --git a/src/link/Plan9/aout.zig b/src/link/Plan9/aout.zig new file mode 100644 index 0000000000..f6dff7437c --- /dev/null +++ b/src/link/Plan9/aout.zig @@ -0,0 +1,114 @@ +const std = @import("std"); +const assert = std.debug.assert; + +/// All integers are in big-endian format (needs a byteswap). +pub const ExecHdr = extern struct { + magic: u32, + text: u32, + data: u32, + bss: u32, + syms: u32, + /// You should truncate this to 32 bits on 64 bit systems, then but the actual 8 bytes + /// in the fat header. + entry: u32, + spsz: u32, + pcsz: u32, + comptime { + assert(@sizeOf(@This()) == 32); + } + /// It is up to the caller to disgard the last 8 bytes if the header is not fat. + pub fn toU8s(self: *@This()) [40]u8 { + var buf: [40]u8 = undefined; + var i: u8 = 0; + inline for (std.meta.fields(@This())) |f| { + std.mem.writeIntSliceBig(u32, buf[i .. i + 4], @field(self, f.name)); + i += 4; + } + return buf; + } +}; + +pub const Sym = struct { + /// Big endian in the file + value: u64, + type: Type, + name: []const u8, + + /// The type field is one of the following characters with the + /// high bit set: + /// T text segment symbol + /// t static text segment symbol + /// L leaf function text segment symbol + /// l static leaf function text segment symbol + /// D data segment symbol + /// d static data segment symbol + /// B bss segment symbol + /// b static bss segment symbol + /// a automatic (local) variable symbol + /// p function parameter symbol + /// f source file name components + /// z source file name + /// Z source file line offset + /// m for '.frame' + pub const Type = enum(u8) { + T = 0x80 | 'T', + t = 0x80 | 't', + L = 0x80 | 'L', + l = 0x80 | 'l', + D = 0x80 | 'D', + d = 0x80 | 'd', + B = 0x80 | 'B', + b = 0x80 | 'b', + a = 0x80 | 'a', + p = 0x80 | 'p', + f = 0x80 | 'f', + z = 0x80 | 'z', + Z = 0x80 | 'Z', + m = 0x80 | 'm', + + pub fn toGlobal(self: Type) Type { + return switch (self) { + .t => .T, + .b => .B, + .d => .D, + else => unreachable, + }; + } + }; +}; + +pub const HDR_MAGIC = 0x00008000; +pub inline fn _MAGIC(f: anytype, b: anytype) @TypeOf(f | ((((@as(c_int, 4) * b) + @as(c_int, 0)) * b) + @as(c_int, 7))) { + return f | ((((@as(c_int, 4) * b) + @as(c_int, 0)) * b) + @as(c_int, 7)); +} +pub const A_MAGIC = _MAGIC(0, 8); // 68020 +pub const I_MAGIC = _MAGIC(0, 11); // intel 386 +pub const J_MAGIC = _MAGIC(0, 12); // intel 960 (retired) +pub const K_MAGIC = _MAGIC(0, 13); // sparc +pub const V_MAGIC = _MAGIC(0, 16); // mips 3000 BE +pub const X_MAGIC = _MAGIC(0, 17); // att dsp 3210 (retired) +pub const M_MAGIC = _MAGIC(0, 18); // mips 4000 BE +pub const D_MAGIC = _MAGIC(0, 19); // amd 29000 (retired) +pub const E_MAGIC = _MAGIC(0, 20); // arm +pub const Q_MAGIC = _MAGIC(0, 21); // powerpc +pub const N_MAGIC = _MAGIC(0, 22); // mips 4000 LE +pub const L_MAGIC = _MAGIC(0, 23); // dec alpha (retired) +pub const P_MAGIC = _MAGIC(0, 24); // mips 3000 LE +pub const U_MAGIC = _MAGIC(0, 25); // sparc64 +pub const S_MAGIC = _MAGIC(HDR_MAGIC, 26); // amd64 +pub const T_MAGIC = _MAGIC(HDR_MAGIC, 27); // powerpc64 +pub const R_MAGIC = _MAGIC(HDR_MAGIC, 28); // arm64 + +pub fn magicFromArch(arch: std.Target.Cpu.Arch) !u32 { + return switch (arch) { + .i386 => I_MAGIC, + .sparc => K_MAGIC, // TODO should sparcv9 and sparcel go here? + .mips => V_MAGIC, + .arm => E_MAGIC, + .aarch64 => R_MAGIC, + .powerpc => Q_MAGIC, + .powerpc64 => T_MAGIC, + .x86_64 => S_MAGIC, + else => error.ArchNotSupportedByPlan9, + }; +} diff --git a/src/link/plan9/a.out.zig b/src/link/plan9/a.out.zig deleted file mode 100644 index 85be2d5c36..0000000000 --- a/src/link/plan9/a.out.zig +++ /dev/null @@ -1,134 +0,0 @@ -// Copyright © 2021 Plan 9 Foundation -// Copyright © 20XX 9front authors - -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -// Idomatic translation of 9front a.out.h -const std = @import("std"); -// all integers are in big-endian format (needs a byteswap) -pub const ExecHdr = extern struct { - magic: u32, - text: u32, - data: u32, - bss: u32, - syms: u32, - /// You should truncate this to 32 bits on 64 bit systems, then but the actual 8 bytes - /// in the fat header. - entry: u32, - spsz: u32, - pcsz: u32, - comptime { - std.debug.assert(@sizeOf(@This()) == 32); - } - /// it is up to the caller to disgard the last 8 bytes if the header is not fat - pub fn toU8s(self: *@This()) [40]u8 { - var buf: [40]u8 = undefined; - var i: u8 = 0; - inline for (std.meta.fields(@This())) |f| { - std.mem.writeIntSliceBig(u32, buf[i .. i + 4], @field(self, f.name)); - i += 4; - } - return buf; - } -}; - -// uchar value[8]; -// char type; -// char name[n]; /* NUL-terminated */ -pub const Sym = struct { - value: u64, // big endian in the file - type: SymType, - name: []const u8, -}; -// The type field is one of the following characters with the -// high bit set: -// T text segment symbol -// t static text segment symbol -// L leaf function text segment symbol -// l static leaf function text segment symbol -// D data segment symbol -// d static data segment symbol -// B bss segment symbol -// b static bss segment symbol -// a automatic (local) variable symbol -// p function parameter symbol -// f source file name components -// z source file name -// Z source file line offset -// m for '.frame' -pub const SymType = enum(u8) { - T = 0x80 | 'T', - t = 0x80 | 't', - L = 0x80 | 'L', - l = 0x80 | 'l', - D = 0x80 | 'D', - d = 0x80 | 'd', - B = 0x80 | 'B', - b = 0x80 | 'b', - a = 0x80 | 'a', - p = 0x80 | 'p', - f = 0x80 | 'f', - z = 0x80 | 'z', - Z = 0x80 | 'Z', - m = 0x80 | 'm', - pub fn toGlobal(self: SymType) SymType { - return switch (self) { - .t => .T, - .b => .B, - .d => .D, - else => unreachable, - }; - } -}; - -pub const HDR_MAGIC = 0x00008000; -pub inline fn _MAGIC(f: anytype, b: anytype) @TypeOf(f | ((((@as(c_int, 4) * b) + @as(c_int, 0)) * b) + @as(c_int, 7))) { - return f | ((((@as(c_int, 4) * b) + @as(c_int, 0)) * b) + @as(c_int, 7)); -} -pub const A_MAGIC = _MAGIC(0, 8); // 68020 -pub const I_MAGIC = _MAGIC(0, 11); // intel 386 -pub const J_MAGIC = _MAGIC(0, 12); // intel 960 (retired) -pub const K_MAGIC = _MAGIC(0, 13); // sparc -pub const V_MAGIC = _MAGIC(0, 16); // mips 3000 BE -pub const X_MAGIC = _MAGIC(0, 17); // att dsp 3210 (retired) -pub const M_MAGIC = _MAGIC(0, 18); // mips 4000 BE -pub const D_MAGIC = _MAGIC(0, 19); // amd 29000 (retired) -pub const E_MAGIC = _MAGIC(0, 20); // arm -pub const Q_MAGIC = _MAGIC(0, 21); // powerpc -pub const N_MAGIC = _MAGIC(0, 22); // mips 4000 LE -pub const L_MAGIC = _MAGIC(0, 23); // dec alpha (retired) -pub const P_MAGIC = _MAGIC(0, 24); // mips 3000 LE -pub const U_MAGIC = _MAGIC(0, 25); // sparc64 -pub const S_MAGIC = _MAGIC(HDR_MAGIC, 26); // amd64 -pub const T_MAGIC = _MAGIC(HDR_MAGIC, 27); // powerpc64 -pub const R_MAGIC = _MAGIC(HDR_MAGIC, 28); // arm64 - -pub fn magicFromArch(arch: std.Target.Cpu.Arch) !u32 { - return switch (arch) { - .i386 => I_MAGIC, - .sparc => K_MAGIC, // TODO should sparcv9 and sparcel go here? - .mips => V_MAGIC, - .arm => E_MAGIC, - .aarch64 => R_MAGIC, - .powerpc => Q_MAGIC, - .powerpc64 => T_MAGIC, - .x86_64 => S_MAGIC, - else => error.ArchNotSupportedByPlan9, - }; -}