mirror of
https://github.com/ziglang/zig.git
synced 2026-02-12 20:37:54 +00:00
rework the bundle compiler-rt feature
* it is now -fcompiler-rt and -fno-compiler-rt to override the (quite
reasonable) default of bundling compiler-rt only for executables and
dynamic libraries.
- the build.zig API is still called bundle_compiler_rt however it is
now an optional bool instead of a bool. leaving it as `null` means
to use the compiler default.
* renamed some internal identifiers to make the source more readable
* additionally support -fcompiler-rt when doing build-obj for ELF files
since that target already supports linking multiple objects into one.
- includes an error message when attempting this for non-ELF. in the
future this could additionally be supported with a more advanced
implementation that does not rely on the linker.
* properly populate the linker cache hash
This commit is contained in:
parent
2fae28b6af
commit
ff9798eb26
@ -1216,7 +1216,7 @@ pub const LibExeObjStep = struct {
|
||||
emit_bin: bool = true,
|
||||
emit_docs: bool = false,
|
||||
emit_h: bool = false,
|
||||
bundle_compiler_rt: bool,
|
||||
bundle_compiler_rt: ?bool = null,
|
||||
disable_stack_probing: bool,
|
||||
disable_sanitize_c: bool,
|
||||
rdynamic: bool,
|
||||
@ -1395,7 +1395,6 @@ pub const LibExeObjStep = struct {
|
||||
.exec_cmd_args = null,
|
||||
.name_prefix = "",
|
||||
.filter = null,
|
||||
.bundle_compiler_rt = false,
|
||||
.disable_stack_probing = false,
|
||||
.disable_sanitize_c = false,
|
||||
.rdynamic = false,
|
||||
@ -2120,8 +2119,12 @@ pub const LibExeObjStep = struct {
|
||||
if (self.is_dynamic) {
|
||||
try zig_args.append("-dynamic");
|
||||
}
|
||||
if (self.bundle_compiler_rt) {
|
||||
try zig_args.append("--bundle-compiler-rt");
|
||||
if (self.bundle_compiler_rt) |x| {
|
||||
if (x) {
|
||||
try zig_args.append("-fcompiler-rt");
|
||||
} else {
|
||||
try zig_args.append("-fno-compiler-rt");
|
||||
}
|
||||
}
|
||||
if (self.disable_stack_probing) {
|
||||
try zig_args.append("-fno-stack-check");
|
||||
|
||||
@ -167,9 +167,7 @@ const Job = union(enum) {
|
||||
libcxx: void,
|
||||
libcxxabi: void,
|
||||
libssp: void,
|
||||
/// needed when producing a dynamic library or executable
|
||||
libcompiler_rt: void,
|
||||
/// needed when producing a static library with bundle-compiler-rt
|
||||
compiler_rt_lib: void,
|
||||
compiler_rt_obj: void,
|
||||
/// needed when not linking libc and using LLVM for code generation because it generates
|
||||
/// calls to, for example, memcpy and memset.
|
||||
@ -355,6 +353,7 @@ pub const InitOptions = struct {
|
||||
want_sanitize_c: ?bool = null,
|
||||
want_stack_check: ?bool = null,
|
||||
want_valgrind: ?bool = null,
|
||||
want_compiler_rt: ?bool = null,
|
||||
use_llvm: ?bool = null,
|
||||
use_lld: ?bool = null,
|
||||
use_clang: ?bool = null,
|
||||
@ -392,7 +391,6 @@ pub const InitOptions = struct {
|
||||
parent_compilation_link_libc: bool = false,
|
||||
stack_size_override: ?u64 = null,
|
||||
image_base_override: ?u64 = null,
|
||||
bundle_compiler_rt: bool = false,
|
||||
self_exe_path: ?[]const u8 = null,
|
||||
version: ?std.builtin.Version = null,
|
||||
libc_installation: ?*const LibCInstallation = null,
|
||||
@ -410,15 +408,14 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {
|
||||
.Obj, .Exe => false,
|
||||
.Lib => (options.link_mode orelse .Static) == .Dynamic,
|
||||
};
|
||||
const is_static_lib = switch (options.output_mode) {
|
||||
.Obj, .Exe => false,
|
||||
.Lib => (options.link_mode orelse .Static) == .Static,
|
||||
};
|
||||
const is_exe_or_dyn_lib = switch (options.output_mode) {
|
||||
.Obj => false,
|
||||
.Lib => is_dyn_lib,
|
||||
.Exe => true,
|
||||
};
|
||||
const needs_c_symbols = !options.is_compiler_rt_or_libc and
|
||||
(is_exe_or_dyn_lib or (options.target.isWasm() and options.output_mode != .Obj));
|
||||
|
||||
const comp: *Compilation = comp: {
|
||||
// For allocations that have the same lifetime as Compilation. This arena is used only during this
|
||||
// initialization and then is freed in deinit().
|
||||
@ -595,6 +592,8 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {
|
||||
break :b options.want_valgrind orelse (options.optimize_mode == .Debug);
|
||||
};
|
||||
|
||||
const include_compiler_rt = options.want_compiler_rt orelse needs_c_symbols;
|
||||
|
||||
const single_threaded = options.single_threaded or target_util.isSingleThreaded(options.target);
|
||||
|
||||
const llvm_cpu_features: ?[*:0]const u8 = if (build_options.have_llvm and use_llvm) blk: {
|
||||
@ -831,7 +830,7 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {
|
||||
.z_defs = options.linker_z_defs,
|
||||
.stack_size_override = options.stack_size_override,
|
||||
.image_base_override = options.image_base_override,
|
||||
.bundle_compiler_rt = options.bundle_compiler_rt,
|
||||
.include_compiler_rt = include_compiler_rt,
|
||||
.linker_script = options.linker_script,
|
||||
.version_script = options.version_script,
|
||||
.gc_sections = options.linker_gc_sections,
|
||||
@ -978,24 +977,31 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {
|
||||
try comp.work_queue.writeItem(.libcxxabi);
|
||||
}
|
||||
|
||||
const needs_libc = is_exe_or_dyn_lib or
|
||||
(comp.getTarget().isWasm() and comp.bin_file.options.output_mode != .Obj);
|
||||
const needs_compiler_rt = options.bundle_compiler_rt or needs_libc;
|
||||
|
||||
if (needs_compiler_rt and build_options.is_stage1) {
|
||||
if (is_static_lib) {
|
||||
try comp.work_queue.writeItem(.{ .compiler_rt_obj = {} });
|
||||
} else {
|
||||
try comp.work_queue.writeItem(.{ .libcompiler_rt = {} });
|
||||
// The `is_stage1` condition is here only because stage2 cannot yet build compiler-rt.
|
||||
// Once it is capable this condition should be removed.
|
||||
if (build_options.is_stage1) {
|
||||
if (comp.bin_file.options.include_compiler_rt) {
|
||||
if (is_exe_or_dyn_lib) {
|
||||
try comp.work_queue.writeItem(.{ .compiler_rt_lib = {} });
|
||||
} else {
|
||||
try comp.work_queue.writeItem(.{ .compiler_rt_obj = {} });
|
||||
if (comp.bin_file.options.object_format != .elf) {
|
||||
// For ELF we can rely on using -r to link multiple objects together into one,
|
||||
// but to truly support `build-obj -fcompiler-rt` will require virtually
|
||||
// injecting `_ = @import("compiler_rt.zig")` into the root source file of
|
||||
// the compilation.
|
||||
fatal("Embedding compiler-rt into non-ELF objects is not yet implemented.", .{});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (needs_libc and build_options.is_stage1) {
|
||||
// MinGW provides no libssp, use our own implementation.
|
||||
if (comp.getTarget().isMinGW()) {
|
||||
try comp.work_queue.writeItem(.{ .libssp = {} });
|
||||
}
|
||||
if (!comp.bin_file.options.link_libc) {
|
||||
try comp.work_queue.writeItem(.{ .zig_libc = {} });
|
||||
if (needs_c_symbols) {
|
||||
// MinGW provides no libssp, use our own implementation.
|
||||
if (comp.getTarget().isMinGW()) {
|
||||
try comp.work_queue.writeItem(.{ .libssp = {} });
|
||||
}
|
||||
if (!comp.bin_file.options.link_libc) {
|
||||
try comp.work_queue.writeItem(.{ .zig_libc = {} });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1393,26 +1399,26 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor
|
||||
fatal("unable to build libcxxabi: {}", .{@errorName(err)});
|
||||
};
|
||||
},
|
||||
.libcompiler_rt => {
|
||||
self.buildStaticLibFromZig("compiler_rt.zig", &self.compiler_rt_static_lib) catch |err| {
|
||||
.compiler_rt_lib => {
|
||||
self.buildOutputFromZig("compiler_rt.zig", .Lib, &self.compiler_rt_static_lib) catch |err| {
|
||||
// TODO Expose this as a normal compile error rather than crashing here.
|
||||
fatal("unable to build compiler_rt: {}", .{@errorName(err)});
|
||||
fatal("unable to build compiler_rt: {s}", .{@errorName(err)});
|
||||
};
|
||||
},
|
||||
.compiler_rt_obj => {
|
||||
self.buildOutputFromZig("compiler_rt.zig", .Obj, &self.compiler_rt_obj) catch |err| {
|
||||
// TODO Expose this as a normal compile error rather than crashing here.
|
||||
fatal("unable to build compiler_rt: {}", .{@errorName(err)});
|
||||
fatal("unable to build compiler_rt: {s}", .{@errorName(err)});
|
||||
};
|
||||
},
|
||||
.libssp => {
|
||||
self.buildStaticLibFromZig("ssp.zig", &self.libssp_static_lib) catch |err| {
|
||||
self.buildOutputFromZig("ssp.zig", .Lib, &self.libssp_static_lib) catch |err| {
|
||||
// TODO Expose this as a normal compile error rather than crashing here.
|
||||
fatal("unable to build libssp: {}", .{@errorName(err)});
|
||||
};
|
||||
},
|
||||
.zig_libc => {
|
||||
self.buildStaticLibFromZig("c.zig", &self.libc_static_lib) catch |err| {
|
||||
self.buildOutputFromZig("c.zig", .Lib, &self.libc_static_lib) catch |err| {
|
||||
// TODO Expose this as a normal compile error rather than crashing here.
|
||||
fatal("unable to build zig's multitarget libc: {}", .{@errorName(err)});
|
||||
};
|
||||
@ -2670,10 +2676,6 @@ fn buildOutputFromZig(
|
||||
};
|
||||
}
|
||||
|
||||
fn buildStaticLibFromZig(comp: *Compilation, src_basename: []const u8, out: *?CRTFile) !void {
|
||||
return buildOutputFromZig(comp, src_basename, .Lib, out);
|
||||
}
|
||||
|
||||
fn updateStage1Module(comp: *Compilation, main_progress_node: *std.Progress.Node) !void {
|
||||
const tracy = trace(@src());
|
||||
defer tracy.end();
|
||||
|
||||
15
src/link.zig
15
src/link.zig
@ -46,7 +46,7 @@ pub const Options = struct {
|
||||
entry_addr: ?u64 = null,
|
||||
stack_size_override: ?u64,
|
||||
image_base_override: ?u64,
|
||||
bundle_compiler_rt: bool,
|
||||
include_compiler_rt: bool,
|
||||
/// Set to `true` to omit debug info.
|
||||
strip: bool,
|
||||
/// If this is true then this link code is responsible for outputting an object
|
||||
@ -474,6 +474,11 @@ pub const File = struct {
|
||||
break :blk full_obj_path;
|
||||
} else null;
|
||||
|
||||
const compiler_rt_path: ?[]const u8 = if (base.options.include_compiler_rt)
|
||||
comp.compiler_rt_obj.?.full_object_path
|
||||
else
|
||||
null;
|
||||
|
||||
// This function follows the same pattern as link.Elf.linkWithLLD so if you want some
|
||||
// insight as to what's going on here you can read that function body which is more
|
||||
// well-commented.
|
||||
@ -490,6 +495,7 @@ pub const File = struct {
|
||||
_ = try ch.addFile(entry.key.status.success.object_path, null);
|
||||
}
|
||||
try ch.addOptionalFile(module_obj_path);
|
||||
try ch.addOptionalFile(compiler_rt_path);
|
||||
|
||||
// We don't actually care whether it's a cache hit or miss; we just need the digest and the lock.
|
||||
_ = try ch.hit();
|
||||
@ -519,8 +525,7 @@ pub const File = struct {
|
||||
var object_files = std.ArrayList([*:0]const u8).init(base.allocator);
|
||||
defer object_files.deinit();
|
||||
|
||||
try object_files.ensureCapacity(base.options.objects.len + comp.c_object_table.items().len +
|
||||
1 + @boolToInt(base.options.bundle_compiler_rt));
|
||||
try object_files.ensureCapacity(base.options.objects.len + comp.c_object_table.items().len + 2);
|
||||
for (base.options.objects) |obj_path| {
|
||||
object_files.appendAssumeCapacity(try arena.dupeZ(u8, obj_path));
|
||||
}
|
||||
@ -530,8 +535,8 @@ pub const File = struct {
|
||||
if (module_obj_path) |p| {
|
||||
object_files.appendAssumeCapacity(try arena.dupeZ(u8, p));
|
||||
}
|
||||
if (base.options.bundle_compiler_rt) {
|
||||
object_files.appendAssumeCapacity(try arena.dupeZ(u8, comp.compiler_rt_obj.?.full_object_path));
|
||||
if (compiler_rt_path) |p| {
|
||||
object_files.appendAssumeCapacity(try arena.dupeZ(u8, p));
|
||||
}
|
||||
|
||||
const full_out_path = try directory.join(arena, &[_][]const u8{base.options.emit.?.sub_path});
|
||||
|
||||
@ -1260,6 +1260,13 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void {
|
||||
const gc_sections = self.base.options.gc_sections orelse !is_obj;
|
||||
const stack_size = self.base.options.stack_size_override orelse 16777216;
|
||||
const allow_shlib_undefined = self.base.options.allow_shlib_undefined orelse !self.base.options.is_native_os;
|
||||
const compiler_rt_path: ?[]const u8 = if (self.base.options.include_compiler_rt) blk: {
|
||||
if (is_exe_or_dyn_lib) {
|
||||
break :blk comp.compiler_rt_static_lib.?.full_object_path;
|
||||
} else {
|
||||
break :blk comp.compiler_rt_obj.?.full_object_path;
|
||||
}
|
||||
} else null;
|
||||
|
||||
// Here we want to determine whether we can save time by not invoking LLD when the
|
||||
// output is unchanged. None of the linker options or the object files that are being
|
||||
@ -1289,6 +1296,8 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void {
|
||||
_ = try man.addFile(entry.key.status.success.object_path, null);
|
||||
}
|
||||
try man.addOptionalFile(module_obj_path);
|
||||
try man.addOptionalFile(compiler_rt_path);
|
||||
|
||||
// We can skip hashing libc and libc++ components that we are in charge of building from Zig
|
||||
// installation sources because they are always a product of the compiler version + target information.
|
||||
man.hash.add(stack_size);
|
||||
@ -1531,12 +1540,14 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void {
|
||||
try argv.append(p);
|
||||
}
|
||||
|
||||
// compiler-rt and libc
|
||||
if (is_exe_or_dyn_lib and !self.base.options.is_compiler_rt_or_libc) {
|
||||
if (!self.base.options.link_libc) {
|
||||
try argv.append(comp.libc_static_lib.?.full_object_path);
|
||||
}
|
||||
try argv.append(comp.compiler_rt_static_lib.?.full_object_path);
|
||||
// libc
|
||||
if (is_exe_or_dyn_lib and !self.base.options.is_compiler_rt_or_libc and !self.base.options.link_libc) {
|
||||
try argv.append(comp.libc_static_lib.?.full_object_path);
|
||||
}
|
||||
|
||||
// compiler-rt
|
||||
if (compiler_rt_path) |p| {
|
||||
try argv.append(p);
|
||||
}
|
||||
|
||||
// Shared libraries.
|
||||
|
||||
12
src/main.zig
12
src/main.zig
@ -314,7 +314,7 @@ const usage_build_generic =
|
||||
\\ -fno-soname (Linux) Disable emitting a SONAME
|
||||
\\ -fLLD Force using LLD as the linker
|
||||
\\ -fno-LLD Prevent using LLD as the linker
|
||||
\\ -fcompiler-rt Always including compiler-rt symbols in output
|
||||
\\ -fcompiler-rt Always include compiler-rt symbols in output
|
||||
\\ -fno-compiler-rt Prevent including compiler-rt symbols in output
|
||||
\\ -rdynamic Add all symbols to the dynamic symbol table
|
||||
\\ -rpath [path] Add directory to the runtime library search path
|
||||
@ -480,6 +480,7 @@ fn buildOutputType(
|
||||
var want_sanitize_c: ?bool = null;
|
||||
var want_stack_check: ?bool = null;
|
||||
var want_valgrind: ?bool = null;
|
||||
var want_compiler_rt: ?bool = null;
|
||||
var rdynamic: bool = false;
|
||||
var linker_script: ?[]const u8 = null;
|
||||
var version_script: ?[]const u8 = null;
|
||||
@ -492,7 +493,6 @@ fn buildOutputType(
|
||||
var test_evented_io = false;
|
||||
var stack_size_override: ?u64 = null;
|
||||
var image_base_override: ?u64 = null;
|
||||
var bundle_compiler_rt = false;
|
||||
var use_llvm: ?bool = null;
|
||||
var use_lld: ?bool = null;
|
||||
var use_clang: ?bool = null;
|
||||
@ -797,8 +797,10 @@ fn buildOutputType(
|
||||
if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg});
|
||||
i += 1;
|
||||
override_lib_dir = args[i];
|
||||
} else if (mem.eql(u8, arg, "--bundle-compiler-rt")) {
|
||||
bundle_compiler_rt = true;
|
||||
} else if (mem.eql(u8, arg, "-fcompiler-rt")) {
|
||||
want_compiler_rt = true;
|
||||
} else if (mem.eql(u8, arg, "-fno-compiler-rt")) {
|
||||
want_compiler_rt = false;
|
||||
} else if (mem.eql(u8, arg, "-feach-lib-rpath")) {
|
||||
each_lib_rpath = true;
|
||||
} else if (mem.eql(u8, arg, "-fno-each-lib-rpath")) {
|
||||
@ -1693,6 +1695,7 @@ fn buildOutputType(
|
||||
.want_sanitize_c = want_sanitize_c,
|
||||
.want_stack_check = want_stack_check,
|
||||
.want_valgrind = want_valgrind,
|
||||
.want_compiler_rt = want_compiler_rt,
|
||||
.use_llvm = use_llvm,
|
||||
.use_lld = use_lld,
|
||||
.use_clang = use_clang,
|
||||
@ -1710,7 +1713,6 @@ fn buildOutputType(
|
||||
.link_emit_relocs = link_emit_relocs,
|
||||
.stack_size_override = stack_size_override,
|
||||
.image_base_override = image_base_override,
|
||||
.bundle_compiler_rt = bundle_compiler_rt,
|
||||
.strip = strip,
|
||||
.single_threaded = single_threaded,
|
||||
.function_sections = function_sections,
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user