mirror of
https://github.com/ziglang/zig.git
synced 2025-12-06 06:13:07 +00:00
link: Stub out GOFF/XCOFF linker code based on LLVM.
This allows emitting object files for s390x-zos (GOFF) and powerpc(64)-aix (XCOFF). Note that GOFF emission in LLVM is still being worked on upstream for LLVM 21; the resulting object files are useless right now. Also, -fstrip is required, or LLVM will SIGSEGV during DWARF emission.
This commit is contained in:
parent
5668c8b7ba
commit
30e254fc31
@ -612,6 +612,7 @@ set(ZIG_STAGE2_SOURCES
|
|||||||
src/link/Elf/relocatable.zig
|
src/link/Elf/relocatable.zig
|
||||||
src/link/Elf/relocation.zig
|
src/link/Elf/relocation.zig
|
||||||
src/link/Elf/synthetic_sections.zig
|
src/link/Elf/synthetic_sections.zig
|
||||||
|
src/link/Goff.zig
|
||||||
src/link/LdScript.zig
|
src/link/LdScript.zig
|
||||||
src/link/MachO.zig
|
src/link/MachO.zig
|
||||||
src/link/MachO/Archive.zig
|
src/link/MachO/Archive.zig
|
||||||
@ -652,6 +653,7 @@ set(ZIG_STAGE2_SOURCES
|
|||||||
src/link/Wasm/Archive.zig
|
src/link/Wasm/Archive.zig
|
||||||
src/link/Wasm/Flush.zig
|
src/link/Wasm/Flush.zig
|
||||||
src/link/Wasm/Object.zig
|
src/link/Wasm/Object.zig
|
||||||
|
src/link/Xcoff.zig
|
||||||
src/link/aarch64.zig
|
src/link/aarch64.zig
|
||||||
src/link/riscv.zig
|
src/link/riscv.zig
|
||||||
src/link/table_section.zig
|
src/link/table_section.zig
|
||||||
|
|||||||
@ -82,6 +82,8 @@ pub const Env = enum {
|
|||||||
.spirv_linker,
|
.spirv_linker,
|
||||||
.plan9_linker,
|
.plan9_linker,
|
||||||
.nvptx_linker,
|
.nvptx_linker,
|
||||||
|
.goff_linker,
|
||||||
|
.xcoff_linker,
|
||||||
=> true,
|
=> true,
|
||||||
.cc_command,
|
.cc_command,
|
||||||
.translate_c_command,
|
.translate_c_command,
|
||||||
@ -228,6 +230,8 @@ pub const Feature = enum {
|
|||||||
spirv_linker,
|
spirv_linker,
|
||||||
plan9_linker,
|
plan9_linker,
|
||||||
nvptx_linker,
|
nvptx_linker,
|
||||||
|
goff_linker,
|
||||||
|
xcoff_linker,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Makes the code following the call to this function unreachable if `feature` is disabled.
|
/// Makes the code following the call to this function unreachable if `feature` is disabled.
|
||||||
|
|||||||
24
src/link.zig
24
src/link.zig
@ -556,9 +556,9 @@ pub const File = struct {
|
|||||||
const comp = base.comp;
|
const comp = base.comp;
|
||||||
const gpa = comp.gpa;
|
const gpa = comp.gpa;
|
||||||
switch (base.tag) {
|
switch (base.tag) {
|
||||||
.coff, .elf, .macho, .plan9, .wasm => {
|
.coff, .elf, .macho, .plan9, .wasm, .goff, .xcoff => {
|
||||||
if (base.file != null) return;
|
if (base.file != null) return;
|
||||||
dev.checkAny(&.{ .coff_linker, .elf_linker, .macho_linker, .plan9_linker, .wasm_linker });
|
dev.checkAny(&.{ .coff_linker, .elf_linker, .macho_linker, .plan9_linker, .wasm_linker, .goff_linker, .xcoff_linker });
|
||||||
const emit = base.emit;
|
const emit = base.emit;
|
||||||
if (base.child_pid) |pid| {
|
if (base.child_pid) |pid| {
|
||||||
if (builtin.os.tag == .windows) {
|
if (builtin.os.tag == .windows) {
|
||||||
@ -650,8 +650,8 @@ pub const File = struct {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
.coff, .macho, .plan9, .wasm => if (base.file) |f| {
|
.coff, .macho, .plan9, .wasm, .goff, .xcoff => if (base.file) |f| {
|
||||||
dev.checkAny(&.{ .coff_linker, .macho_linker, .plan9_linker, .wasm_linker });
|
dev.checkAny(&.{ .coff_linker, .macho_linker, .plan9_linker, .wasm_linker, .goff_linker, .xcoff_linker });
|
||||||
if (base.zcu_object_sub_path != null) {
|
if (base.zcu_object_sub_path != null) {
|
||||||
// The file we have open is not the final file that we want to
|
// The file we have open is not the final file that we want to
|
||||||
// make executable, so we don't have to close it.
|
// make executable, so we don't have to close it.
|
||||||
@ -767,6 +767,7 @@ pub const File = struct {
|
|||||||
|
|
||||||
switch (base.tag) {
|
switch (base.tag) {
|
||||||
.spirv, .nvptx => {},
|
.spirv, .nvptx => {},
|
||||||
|
.goff, .xcoff => {},
|
||||||
inline else => |tag| {
|
inline else => |tag| {
|
||||||
dev.check(tag.devFeature());
|
dev.check(tag.devFeature());
|
||||||
return @as(*tag.Type(), @fieldParentPtr("base", base)).updateLineNumber(pt, ti_id);
|
return @as(*tag.Type(), @fieldParentPtr("base", base)).updateLineNumber(pt, ti_id);
|
||||||
@ -902,6 +903,7 @@ pub const File = struct {
|
|||||||
.spirv => unreachable,
|
.spirv => unreachable,
|
||||||
.nvptx => unreachable,
|
.nvptx => unreachable,
|
||||||
.wasm => unreachable,
|
.wasm => unreachable,
|
||||||
|
.goff, .xcoff => unreachable,
|
||||||
inline else => |tag| {
|
inline else => |tag| {
|
||||||
dev.check(tag.devFeature());
|
dev.check(tag.devFeature());
|
||||||
return @as(*tag.Type(), @fieldParentPtr("base", base)).getNavVAddr(pt, nav_index, reloc_info);
|
return @as(*tag.Type(), @fieldParentPtr("base", base)).getNavVAddr(pt, nav_index, reloc_info);
|
||||||
@ -921,6 +923,7 @@ pub const File = struct {
|
|||||||
.spirv => unreachable,
|
.spirv => unreachable,
|
||||||
.nvptx => unreachable,
|
.nvptx => unreachable,
|
||||||
.wasm => unreachable,
|
.wasm => unreachable,
|
||||||
|
.goff, .xcoff => unreachable,
|
||||||
inline else => |tag| {
|
inline else => |tag| {
|
||||||
dev.check(tag.devFeature());
|
dev.check(tag.devFeature());
|
||||||
return @as(*tag.Type(), @fieldParentPtr("base", base)).lowerUav(pt, decl_val, decl_align, src_loc);
|
return @as(*tag.Type(), @fieldParentPtr("base", base)).lowerUav(pt, decl_val, decl_align, src_loc);
|
||||||
@ -934,6 +937,7 @@ pub const File = struct {
|
|||||||
.spirv => unreachable,
|
.spirv => unreachable,
|
||||||
.nvptx => unreachable,
|
.nvptx => unreachable,
|
||||||
.wasm => unreachable,
|
.wasm => unreachable,
|
||||||
|
.goff, .xcoff => unreachable,
|
||||||
inline else => |tag| {
|
inline else => |tag| {
|
||||||
dev.check(tag.devFeature());
|
dev.check(tag.devFeature());
|
||||||
return @as(*tag.Type(), @fieldParentPtr("base", base)).getUavVAddr(decl_val, reloc_info);
|
return @as(*tag.Type(), @fieldParentPtr("base", base)).getUavVAddr(decl_val, reloc_info);
|
||||||
@ -950,6 +954,8 @@ pub const File = struct {
|
|||||||
.plan9,
|
.plan9,
|
||||||
.spirv,
|
.spirv,
|
||||||
.nvptx,
|
.nvptx,
|
||||||
|
.goff,
|
||||||
|
.xcoff,
|
||||||
=> {},
|
=> {},
|
||||||
|
|
||||||
inline else => |tag| {
|
inline else => |tag| {
|
||||||
@ -1246,6 +1252,8 @@ pub const File = struct {
|
|||||||
spirv,
|
spirv,
|
||||||
plan9,
|
plan9,
|
||||||
nvptx,
|
nvptx,
|
||||||
|
goff,
|
||||||
|
xcoff,
|
||||||
|
|
||||||
pub fn Type(comptime tag: Tag) type {
|
pub fn Type(comptime tag: Tag) type {
|
||||||
return switch (tag) {
|
return switch (tag) {
|
||||||
@ -1257,6 +1265,8 @@ pub const File = struct {
|
|||||||
.spirv => SpirV,
|
.spirv => SpirV,
|
||||||
.plan9 => Plan9,
|
.plan9 => Plan9,
|
||||||
.nvptx => NvPtx,
|
.nvptx => NvPtx,
|
||||||
|
.goff => Goff,
|
||||||
|
.xcoff => Xcoff,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1270,8 +1280,8 @@ pub const File = struct {
|
|||||||
.c => .c,
|
.c => .c,
|
||||||
.spirv => .spirv,
|
.spirv => .spirv,
|
||||||
.nvptx => .nvptx,
|
.nvptx => .nvptx,
|
||||||
.goff => @panic("TODO implement goff object format"),
|
.goff => .goff,
|
||||||
.xcoff => @panic("TODO implement xcoff object format"),
|
.xcoff => .xcoff,
|
||||||
.hex => @panic("TODO implement hex object format"),
|
.hex => @panic("TODO implement hex object format"),
|
||||||
.raw => @panic("TODO implement raw object format"),
|
.raw => @panic("TODO implement raw object format"),
|
||||||
};
|
};
|
||||||
@ -1377,6 +1387,8 @@ pub const File = struct {
|
|||||||
pub const SpirV = @import("link/SpirV.zig");
|
pub const SpirV = @import("link/SpirV.zig");
|
||||||
pub const Wasm = @import("link/Wasm.zig");
|
pub const Wasm = @import("link/Wasm.zig");
|
||||||
pub const NvPtx = @import("link/NvPtx.zig");
|
pub const NvPtx = @import("link/NvPtx.zig");
|
||||||
|
pub const Goff = @import("link/Goff.zig");
|
||||||
|
pub const Xcoff = @import("link/Xcoff.zig");
|
||||||
pub const Dwarf = @import("link/Dwarf.zig");
|
pub const Dwarf = @import("link/Dwarf.zig");
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
120
src/link/Goff.zig
Normal file
120
src/link/Goff.zig
Normal file
@ -0,0 +1,120 @@
|
|||||||
|
//! Stub linker support for GOFF based on LLVM.
|
||||||
|
|
||||||
|
const Goff = @This();
|
||||||
|
|
||||||
|
const std = @import("std");
|
||||||
|
const builtin = @import("builtin");
|
||||||
|
|
||||||
|
const Allocator = std.mem.Allocator;
|
||||||
|
const assert = std.debug.assert;
|
||||||
|
const log = std.log.scoped(.link);
|
||||||
|
const Path = std.Build.Cache.Path;
|
||||||
|
|
||||||
|
const Zcu = @import("../Zcu.zig");
|
||||||
|
const InternPool = @import("../InternPool.zig");
|
||||||
|
const Compilation = @import("../Compilation.zig");
|
||||||
|
const link = @import("../link.zig");
|
||||||
|
const trace = @import("../tracy.zig").trace;
|
||||||
|
const build_options = @import("build_options");
|
||||||
|
const Air = @import("../Air.zig");
|
||||||
|
const Liveness = @import("../Liveness.zig");
|
||||||
|
const LlvmObject = @import("../codegen/llvm.zig").Object;
|
||||||
|
|
||||||
|
base: link.File,
|
||||||
|
llvm_object: LlvmObject.Ptr,
|
||||||
|
|
||||||
|
pub fn createEmpty(
|
||||||
|
arena: Allocator,
|
||||||
|
comp: *Compilation,
|
||||||
|
emit: Path,
|
||||||
|
options: link.File.OpenOptions,
|
||||||
|
) !*Goff {
|
||||||
|
const target = comp.root_mod.resolved_target.result;
|
||||||
|
const use_lld = build_options.have_llvm and comp.config.use_lld;
|
||||||
|
const use_llvm = comp.config.use_llvm;
|
||||||
|
|
||||||
|
assert(use_llvm); // Caught by Compilation.Config.resolve.
|
||||||
|
assert(!use_lld); // Caught by Compilation.Config.resolve.
|
||||||
|
assert(target.os.tag == .zos); // Caught by Compilation.Config.resolve.
|
||||||
|
|
||||||
|
const llvm_object = try LlvmObject.create(arena, comp);
|
||||||
|
const goff = try arena.create(Goff);
|
||||||
|
goff.* = .{
|
||||||
|
.base = .{
|
||||||
|
.tag = .goff,
|
||||||
|
.comp = comp,
|
||||||
|
.emit = emit,
|
||||||
|
.zcu_object_sub_path = emit.sub_path,
|
||||||
|
.gc_sections = options.gc_sections orelse false,
|
||||||
|
.print_gc_sections = options.print_gc_sections,
|
||||||
|
.stack_size = options.stack_size orelse 0,
|
||||||
|
.allow_shlib_undefined = options.allow_shlib_undefined orelse false,
|
||||||
|
.file = null,
|
||||||
|
.disable_lld_caching = options.disable_lld_caching,
|
||||||
|
.build_id = options.build_id,
|
||||||
|
},
|
||||||
|
.llvm_object = llvm_object,
|
||||||
|
};
|
||||||
|
|
||||||
|
return goff;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn open(
|
||||||
|
arena: Allocator,
|
||||||
|
comp: *Compilation,
|
||||||
|
emit: Path,
|
||||||
|
options: link.File.OpenOptions,
|
||||||
|
) !*Goff {
|
||||||
|
const target = comp.root_mod.resolved_target.result;
|
||||||
|
assert(target.ofmt == .goff);
|
||||||
|
return createEmpty(arena, comp, emit, options);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn deinit(self: *Goff) void {
|
||||||
|
self.llvm_object.deinit();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn updateFunc(
|
||||||
|
self: *Goff,
|
||||||
|
pt: Zcu.PerThread,
|
||||||
|
func_index: InternPool.Index,
|
||||||
|
air: Air,
|
||||||
|
liveness: Liveness,
|
||||||
|
) link.File.UpdateNavError!void {
|
||||||
|
if (build_options.skip_non_native and builtin.object_format != .goff)
|
||||||
|
@panic("Attempted to compile for object format that was disabled by build configuration");
|
||||||
|
|
||||||
|
try self.llvm_object.updateFunc(pt, func_index, air, liveness);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn updateNav(self: *Goff, pt: Zcu.PerThread, nav: InternPool.Nav.Index) link.File.UpdateNavError!void {
|
||||||
|
if (build_options.skip_non_native and builtin.object_format != .goff)
|
||||||
|
@panic("Attempted to compile for object format that was disabled by build configuration");
|
||||||
|
|
||||||
|
return self.llvm_object.updateNav(pt, nav);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn updateExports(
|
||||||
|
self: *Goff,
|
||||||
|
pt: Zcu.PerThread,
|
||||||
|
exported: Zcu.Exported,
|
||||||
|
export_indices: []const Zcu.Export.Index,
|
||||||
|
) !void {
|
||||||
|
if (build_options.skip_non_native and builtin.object_format != .goff)
|
||||||
|
@panic("Attempted to compile for object format that was disabled by build configuration");
|
||||||
|
|
||||||
|
return self.llvm_object.updateExports(pt, exported, export_indices);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn flush(self: *Goff, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: std.Progress.Node) link.File.FlushError!void {
|
||||||
|
return self.flushModule(arena, tid, prog_node);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn flushModule(self: *Goff, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: std.Progress.Node) link.File.FlushError!void {
|
||||||
|
if (build_options.skip_non_native and builtin.object_format != .goff)
|
||||||
|
@panic("Attempted to compile for object format that was disabled by build configuration");
|
||||||
|
|
||||||
|
_ = tid;
|
||||||
|
|
||||||
|
try self.base.emitLlvmObject(arena, self.llvm_object, prog_node);
|
||||||
|
}
|
||||||
120
src/link/Xcoff.zig
Normal file
120
src/link/Xcoff.zig
Normal file
@ -0,0 +1,120 @@
|
|||||||
|
//! Stub linker support for GOFF based on LLVM.
|
||||||
|
|
||||||
|
const Xcoff = @This();
|
||||||
|
|
||||||
|
const std = @import("std");
|
||||||
|
const builtin = @import("builtin");
|
||||||
|
|
||||||
|
const Allocator = std.mem.Allocator;
|
||||||
|
const assert = std.debug.assert;
|
||||||
|
const log = std.log.scoped(.link);
|
||||||
|
const Path = std.Build.Cache.Path;
|
||||||
|
|
||||||
|
const Zcu = @import("../Zcu.zig");
|
||||||
|
const InternPool = @import("../InternPool.zig");
|
||||||
|
const Compilation = @import("../Compilation.zig");
|
||||||
|
const link = @import("../link.zig");
|
||||||
|
const trace = @import("../tracy.zig").trace;
|
||||||
|
const build_options = @import("build_options");
|
||||||
|
const Air = @import("../Air.zig");
|
||||||
|
const Liveness = @import("../Liveness.zig");
|
||||||
|
const LlvmObject = @import("../codegen/llvm.zig").Object;
|
||||||
|
|
||||||
|
base: link.File,
|
||||||
|
llvm_object: LlvmObject.Ptr,
|
||||||
|
|
||||||
|
pub fn createEmpty(
|
||||||
|
arena: Allocator,
|
||||||
|
comp: *Compilation,
|
||||||
|
emit: Path,
|
||||||
|
options: link.File.OpenOptions,
|
||||||
|
) !*Xcoff {
|
||||||
|
const target = comp.root_mod.resolved_target.result;
|
||||||
|
const use_lld = build_options.have_llvm and comp.config.use_lld;
|
||||||
|
const use_llvm = comp.config.use_llvm;
|
||||||
|
|
||||||
|
assert(use_llvm); // Caught by Compilation.Config.resolve.
|
||||||
|
assert(!use_lld); // Caught by Compilation.Config.resolve.
|
||||||
|
assert(target.os.tag == .aix); // Caught by Compilation.Config.resolve.
|
||||||
|
|
||||||
|
const llvm_object = try LlvmObject.create(arena, comp);
|
||||||
|
const xcoff = try arena.create(Xcoff);
|
||||||
|
xcoff.* = .{
|
||||||
|
.base = .{
|
||||||
|
.tag = .xcoff,
|
||||||
|
.comp = comp,
|
||||||
|
.emit = emit,
|
||||||
|
.zcu_object_sub_path = emit.sub_path,
|
||||||
|
.gc_sections = options.gc_sections orelse false,
|
||||||
|
.print_gc_sections = options.print_gc_sections,
|
||||||
|
.stack_size = options.stack_size orelse 0,
|
||||||
|
.allow_shlib_undefined = options.allow_shlib_undefined orelse false,
|
||||||
|
.file = null,
|
||||||
|
.disable_lld_caching = options.disable_lld_caching,
|
||||||
|
.build_id = options.build_id,
|
||||||
|
},
|
||||||
|
.llvm_object = llvm_object,
|
||||||
|
};
|
||||||
|
|
||||||
|
return xcoff;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn open(
|
||||||
|
arena: Allocator,
|
||||||
|
comp: *Compilation,
|
||||||
|
emit: Path,
|
||||||
|
options: link.File.OpenOptions,
|
||||||
|
) !*Xcoff {
|
||||||
|
const target = comp.root_mod.resolved_target.result;
|
||||||
|
assert(target.ofmt == .xcoff);
|
||||||
|
return createEmpty(arena, comp, emit, options);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn deinit(self: *Xcoff) void {
|
||||||
|
self.llvm_object.deinit();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn updateFunc(
|
||||||
|
self: *Xcoff,
|
||||||
|
pt: Zcu.PerThread,
|
||||||
|
func_index: InternPool.Index,
|
||||||
|
air: Air,
|
||||||
|
liveness: Liveness,
|
||||||
|
) link.File.UpdateNavError!void {
|
||||||
|
if (build_options.skip_non_native and builtin.object_format != .xcoff)
|
||||||
|
@panic("Attempted to compile for object format that was disabled by build configuration");
|
||||||
|
|
||||||
|
try self.llvm_object.updateFunc(pt, func_index, air, liveness);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn updateNav(self: *Xcoff, pt: Zcu.PerThread, nav: InternPool.Nav.Index) link.File.UpdateNavError!void {
|
||||||
|
if (build_options.skip_non_native and builtin.object_format != .xcoff)
|
||||||
|
@panic("Attempted to compile for object format that was disabled by build configuration");
|
||||||
|
|
||||||
|
return self.llvm_object.updateNav(pt, nav);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn updateExports(
|
||||||
|
self: *Xcoff,
|
||||||
|
pt: Zcu.PerThread,
|
||||||
|
exported: Zcu.Exported,
|
||||||
|
export_indices: []const Zcu.Export.Index,
|
||||||
|
) !void {
|
||||||
|
if (build_options.skip_non_native and builtin.object_format != .xcoff)
|
||||||
|
@panic("Attempted to compile for object format that was disabled by build configuration");
|
||||||
|
|
||||||
|
return self.llvm_object.updateExports(pt, exported, export_indices);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn flush(self: *Xcoff, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: std.Progress.Node) link.File.FlushError!void {
|
||||||
|
return self.flushModule(arena, tid, prog_node);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn flushModule(self: *Xcoff, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: std.Progress.Node) link.File.FlushError!void {
|
||||||
|
if (build_options.skip_non_native and builtin.object_format != .xcoff)
|
||||||
|
@panic("Attempted to compile for object format that was disabled by build configuration");
|
||||||
|
|
||||||
|
_ = tid;
|
||||||
|
|
||||||
|
try self.base.emitLlvmObject(arena, self.llvm_object, prog_node);
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user