| author | |
| committer | |
| log | ff9798eb265b02d572ecbced675efcd7c763aea9 |
| tree | a76c48fca7b7458f41c4dca14f9ec9eb30325818 |
| parent | 2fae28b6afc0e5411c9a9a9def43eeb59fba840f |
* 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 hash5 files changed, 79 insertions(+), 56 deletions(-)
lib/std/build.zig+7-4| ... | ... | @@ -1216,7 +1216,7 @@ pub const LibExeObjStep = struct { |
| 1216 | 1216 | emit_bin: bool = true, |
| 1217 | 1217 | emit_docs: bool = false, |
| 1218 | 1218 | emit_h: bool = false, |
| 1219 | bundle_compiler_rt: bool, | |
| 1219 | bundle_compiler_rt: ?bool = null, | |
| 1220 | 1220 | disable_stack_probing: bool, |
| 1221 | 1221 | disable_sanitize_c: bool, |
| 1222 | 1222 | rdynamic: bool, |
| ... | ... | @@ -1395,7 +1395,6 @@ pub const LibExeObjStep = struct { |
| 1395 | 1395 | .exec_cmd_args = null, |
| 1396 | 1396 | .name_prefix = "", |
| 1397 | 1397 | .filter = null, |
| 1398 | .bundle_compiler_rt = false, | |
| 1399 | 1398 | .disable_stack_probing = false, |
| 1400 | 1399 | .disable_sanitize_c = false, |
| 1401 | 1400 | .rdynamic = false, |
| ... | ... | @@ -2120,8 +2119,12 @@ pub const LibExeObjStep = struct { |
| 2120 | 2119 | if (self.is_dynamic) { |
| 2121 | 2120 | try zig_args.append("-dynamic"); |
| 2122 | 2121 | } |
| 2123 | if (self.bundle_compiler_rt) { | |
| 2124 | try zig_args.append("--bundle-compiler-rt"); | |
| 2122 | if (self.bundle_compiler_rt) |x| { | |
| 2123 | if (x) { | |
| 2124 | try zig_args.append("-fcompiler-rt"); | |
| 2125 | } else { | |
| 2126 | try zig_args.append("-fno-compiler-rt"); | |
| 2127 | } | |
| 2125 | 2128 | } |
| 2126 | 2129 | if (self.disable_stack_probing) { |
| 2127 | 2130 | try zig_args.append("-fno-stack-check"); |
src/Compilation.zig+38-36| ... | ... | @@ -167,9 +167,7 @@ const Job = union(enum) { |
| 167 | 167 | libcxx: void, |
| 168 | 168 | libcxxabi: void, |
| 169 | 169 | libssp: void, |
| 170 | /// needed when producing a dynamic library or executable | |
| 171 | libcompiler_rt: void, | |
| 172 | /// needed when producing a static library with bundle-compiler-rt | |
| 170 | compiler_rt_lib: void, | |
| 173 | 171 | compiler_rt_obj: void, |
| 174 | 172 | /// needed when not linking libc and using LLVM for code generation because it generates |
| 175 | 173 | /// calls to, for example, memcpy and memset. |
| ... | ... | @@ -355,6 +353,7 @@ pub const InitOptions = struct { |
| 355 | 353 | want_sanitize_c: ?bool = null, |
| 356 | 354 | want_stack_check: ?bool = null, |
| 357 | 355 | want_valgrind: ?bool = null, |
| 356 | want_compiler_rt: ?bool = null, | |
| 358 | 357 | use_llvm: ?bool = null, |
| 359 | 358 | use_lld: ?bool = null, |
| 360 | 359 | use_clang: ?bool = null, |
| ... | ... | @@ -392,7 +391,6 @@ pub const InitOptions = struct { |
| 392 | 391 | parent_compilation_link_libc: bool = false, |
| 393 | 392 | stack_size_override: ?u64 = null, |
| 394 | 393 | image_base_override: ?u64 = null, |
| 395 | bundle_compiler_rt: bool = false, | |
| 396 | 394 | self_exe_path: ?[]const u8 = null, |
| 397 | 395 | version: ?std.builtin.Version = null, |
| 398 | 396 | libc_installation: ?*const LibCInstallation = null, |
| ... | ... | @@ -410,15 +408,14 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation { |
| 410 | 408 | .Obj, .Exe => false, |
| 411 | 409 | .Lib => (options.link_mode orelse .Static) == .Dynamic, |
| 412 | 410 | }; |
| 413 | const is_static_lib = switch (options.output_mode) { | |
| 414 | .Obj, .Exe => false, | |
| 415 | .Lib => (options.link_mode orelse .Static) == .Static, | |
| 416 | }; | |
| 417 | 411 | const is_exe_or_dyn_lib = switch (options.output_mode) { |
| 418 | 412 | .Obj => false, |
| 419 | 413 | .Lib => is_dyn_lib, |
| 420 | 414 | .Exe => true, |
| 421 | 415 | }; |
| 416 | const needs_c_symbols = !options.is_compiler_rt_or_libc and | |
| 417 | (is_exe_or_dyn_lib or (options.target.isWasm() and options.output_mode != .Obj)); | |
| 418 | ||
| 422 | 419 | const comp: *Compilation = comp: { |
| 423 | 420 | // For allocations that have the same lifetime as Compilation. This arena is used only during this |
| 424 | 421 | // initialization and then is freed in deinit(). |
| ... | ... | @@ -595,6 +592,8 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation { |
| 595 | 592 | break :b options.want_valgrind orelse (options.optimize_mode == .Debug); |
| 596 | 593 | }; |
| 597 | 594 | |
| 595 | const include_compiler_rt = options.want_compiler_rt orelse needs_c_symbols; | |
| 596 | ||
| 598 | 597 | const single_threaded = options.single_threaded or target_util.isSingleThreaded(options.target); |
| 599 | 598 | |
| 600 | 599 | 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 { |
| 831 | 830 | .z_defs = options.linker_z_defs, |
| 832 | 831 | .stack_size_override = options.stack_size_override, |
| 833 | 832 | .image_base_override = options.image_base_override, |
| 834 | .bundle_compiler_rt = options.bundle_compiler_rt, | |
| 833 | .include_compiler_rt = include_compiler_rt, | |
| 835 | 834 | .linker_script = options.linker_script, |
| 836 | 835 | .version_script = options.version_script, |
| 837 | 836 | .gc_sections = options.linker_gc_sections, |
| ... | ... | @@ -978,24 +977,31 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation { |
| 978 | 977 | try comp.work_queue.writeItem(.libcxxabi); |
| 979 | 978 | } |
| 980 | 979 | |
| 981 | const needs_libc = is_exe_or_dyn_lib or | |
| 982 | (comp.getTarget().isWasm() and comp.bin_file.options.output_mode != .Obj); | |
| 983 | const needs_compiler_rt = options.bundle_compiler_rt or needs_libc; | |
| 984 | ||
| 985 | if (needs_compiler_rt and build_options.is_stage1) { | |
| 986 | if (is_static_lib) { | |
| 987 | try comp.work_queue.writeItem(.{ .compiler_rt_obj = {} }); | |
| 988 | } else { | |
| 989 | try comp.work_queue.writeItem(.{ .libcompiler_rt = {} }); | |
| 990 | } | |
| 991 | } | |
| 992 | if (needs_libc and build_options.is_stage1) { | |
| 993 | // MinGW provides no libssp, use our own implementation. | |
| 994 | if (comp.getTarget().isMinGW()) { | |
| 995 | try comp.work_queue.writeItem(.{ .libssp = {} }); | |
| 980 | // The `is_stage1` condition is here only because stage2 cannot yet build compiler-rt. | |
| 981 | // Once it is capable this condition should be removed. | |
| 982 | if (build_options.is_stage1) { | |
| 983 | if (comp.bin_file.options.include_compiler_rt) { | |
| 984 | if (is_exe_or_dyn_lib) { | |
| 985 | try comp.work_queue.writeItem(.{ .compiler_rt_lib = {} }); | |
| 986 | } else { | |
| 987 | try comp.work_queue.writeItem(.{ .compiler_rt_obj = {} }); | |
| 988 | if (comp.bin_file.options.object_format != .elf) { | |
| 989 | // For ELF we can rely on using -r to link multiple objects together into one, | |
| 990 | // but to truly support `build-obj -fcompiler-rt` will require virtually | |
| 991 | // injecting `_ = @import("compiler_rt.zig")` into the root source file of | |
| 992 | // the compilation. | |
| 993 | fatal("Embedding compiler-rt into non-ELF objects is not yet implemented.", .{}); | |
| 994 | } | |
| 995 | } | |
| 996 | 996 | } |
| 997 | if (!comp.bin_file.options.link_libc) { | |
| 998 | try comp.work_queue.writeItem(.{ .zig_libc = {} }); | |
| 997 | if (needs_c_symbols) { | |
| 998 | // MinGW provides no libssp, use our own implementation. | |
| 999 | if (comp.getTarget().isMinGW()) { | |
| 1000 | try comp.work_queue.writeItem(.{ .libssp = {} }); | |
| 1001 | } | |
| 1002 | if (!comp.bin_file.options.link_libc) { | |
| 1003 | try comp.work_queue.writeItem(.{ .zig_libc = {} }); | |
| 1004 | } | |
| 999 | 1005 | } |
| 1000 | 1006 | } |
| 1001 | 1007 | } |
| ... | ... | @@ -1393,26 +1399,26 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor |
| 1393 | 1399 | fatal("unable to build libcxxabi: {}", .{@errorName(err)}); |
| 1394 | 1400 | }; |
| 1395 | 1401 | }, |
| 1396 | .libcompiler_rt => { | |
| 1397 | self.buildStaticLibFromZig("compiler_rt.zig", &self.compiler_rt_static_lib) catch |err| { | |
| 1402 | .compiler_rt_lib => { | |
| 1403 | self.buildOutputFromZig("compiler_rt.zig", .Lib, &self.compiler_rt_static_lib) catch |err| { | |
| 1398 | 1404 | // TODO Expose this as a normal compile error rather than crashing here. |
| 1399 | fatal("unable to build compiler_rt: {}", .{@errorName(err)}); | |
| 1405 | fatal("unable to build compiler_rt: {s}", .{@errorName(err)}); | |
| 1400 | 1406 | }; |
| 1401 | 1407 | }, |
| 1402 | 1408 | .compiler_rt_obj => { |
| 1403 | 1409 | self.buildOutputFromZig("compiler_rt.zig", .Obj, &self.compiler_rt_obj) catch |err| { |
| 1404 | 1410 | // TODO Expose this as a normal compile error rather than crashing here. |
| 1405 | fatal("unable to build compiler_rt: {}", .{@errorName(err)}); | |
| 1411 | fatal("unable to build compiler_rt: {s}", .{@errorName(err)}); | |
| 1406 | 1412 | }; |
| 1407 | 1413 | }, |
| 1408 | 1414 | .libssp => { |
| 1409 | self.buildStaticLibFromZig("ssp.zig", &self.libssp_static_lib) catch |err| { | |
| 1415 | self.buildOutputFromZig("ssp.zig", .Lib, &self.libssp_static_lib) catch |err| { | |
| 1410 | 1416 | // TODO Expose this as a normal compile error rather than crashing here. |
| 1411 | 1417 | fatal("unable to build libssp: {}", .{@errorName(err)}); |
| 1412 | 1418 | }; |
| 1413 | 1419 | }, |
| 1414 | 1420 | .zig_libc => { |
| 1415 | self.buildStaticLibFromZig("c.zig", &self.libc_static_lib) catch |err| { | |
| 1421 | self.buildOutputFromZig("c.zig", .Lib, &self.libc_static_lib) catch |err| { | |
| 1416 | 1422 | // TODO Expose this as a normal compile error rather than crashing here. |
| 1417 | 1423 | fatal("unable to build zig's multitarget libc: {}", .{@errorName(err)}); |
| 1418 | 1424 | }; |
| ... | ... | @@ -2670,10 +2676,6 @@ fn buildOutputFromZig( |
| 2670 | 2676 | }; |
| 2671 | 2677 | } |
| 2672 | 2678 | |
| 2673 | fn buildStaticLibFromZig(comp: *Compilation, src_basename: []const u8, out: *?CRTFile) !void { | |
| 2674 | return buildOutputFromZig(comp, src_basename, .Lib, out); | |
| 2675 | } | |
| 2676 | ||
| 2677 | 2679 | fn updateStage1Module(comp: *Compilation, main_progress_node: *std.Progress.Node) !void { |
| 2678 | 2680 | const tracy = trace(@src()); |
| 2679 | 2681 | defer tracy.end(); |
src/link.zig+10-5| ... | ... | @@ -46,7 +46,7 @@ pub const Options = struct { |
| 46 | 46 | entry_addr: ?u64 = null, |
| 47 | 47 | stack_size_override: ?u64, |
| 48 | 48 | image_base_override: ?u64, |
| 49 | bundle_compiler_rt: bool, | |
| 49 | include_compiler_rt: bool, | |
| 50 | 50 | /// Set to `true` to omit debug info. |
| 51 | 51 | strip: bool, |
| 52 | 52 | /// If this is true then this link code is responsible for outputting an object |
| ... | ... | @@ -474,6 +474,11 @@ pub const File = struct { |
| 474 | 474 | break :blk full_obj_path; |
| 475 | 475 | } else null; |
| 476 | 476 | |
| 477 | const compiler_rt_path: ?[]const u8 = if (base.options.include_compiler_rt) | |
| 478 | comp.compiler_rt_obj.?.full_object_path | |
| 479 | else | |
| 480 | null; | |
| 481 | ||
| 477 | 482 | // This function follows the same pattern as link.Elf.linkWithLLD so if you want some |
| 478 | 483 | // insight as to what's going on here you can read that function body which is more |
| 479 | 484 | // well-commented. |
| ... | ... | @@ -490,6 +495,7 @@ pub const File = struct { |
| 490 | 495 | _ = try ch.addFile(entry.key.status.success.object_path, null); |
| 491 | 496 | } |
| 492 | 497 | try ch.addOptionalFile(module_obj_path); |
| 498 | try ch.addOptionalFile(compiler_rt_path); | |
| 493 | 499 | |
| 494 | 500 | // We don't actually care whether it's a cache hit or miss; we just need the digest and the lock. |
| 495 | 501 | _ = try ch.hit(); |
| ... | ... | @@ -519,8 +525,7 @@ pub const File = struct { |
| 519 | 525 | var object_files = std.ArrayList([*:0]const u8).init(base.allocator); |
| 520 | 526 | defer object_files.deinit(); |
| 521 | 527 | |
| 522 | try object_files.ensureCapacity(base.options.objects.len + comp.c_object_table.items().len + | |
| 523 | 1 + @boolToInt(base.options.bundle_compiler_rt)); | |
| 528 | try object_files.ensureCapacity(base.options.objects.len + comp.c_object_table.items().len + 2); | |
| 524 | 529 | for (base.options.objects) |obj_path| { |
| 525 | 530 | object_files.appendAssumeCapacity(try arena.dupeZ(u8, obj_path)); |
| 526 | 531 | } |
| ... | ... | @@ -530,8 +535,8 @@ pub const File = struct { |
| 530 | 535 | if (module_obj_path) |p| { |
| 531 | 536 | object_files.appendAssumeCapacity(try arena.dupeZ(u8, p)); |
| 532 | 537 | } |
| 533 | if (base.options.bundle_compiler_rt) { | |
| 534 | object_files.appendAssumeCapacity(try arena.dupeZ(u8, comp.compiler_rt_obj.?.full_object_path)); | |
| 538 | if (compiler_rt_path) |p| { | |
| 539 | object_files.appendAssumeCapacity(try arena.dupeZ(u8, p)); | |
| 535 | 540 | } |
| 536 | 541 | |
| 537 | 542 | const full_out_path = try directory.join(arena, &[_][]const u8{base.options.emit.?.sub_path}); |
src/link/Elf.zig+17-6| ... | ... | @@ -1260,6 +1260,13 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void { |
| 1260 | 1260 | const gc_sections = self.base.options.gc_sections orelse !is_obj; |
| 1261 | 1261 | const stack_size = self.base.options.stack_size_override orelse 16777216; |
| 1262 | 1262 | const allow_shlib_undefined = self.base.options.allow_shlib_undefined orelse !self.base.options.is_native_os; |
| 1263 | const compiler_rt_path: ?[]const u8 = if (self.base.options.include_compiler_rt) blk: { | |
| 1264 | if (is_exe_or_dyn_lib) { | |
| 1265 | break :blk comp.compiler_rt_static_lib.?.full_object_path; | |
| 1266 | } else { | |
| 1267 | break :blk comp.compiler_rt_obj.?.full_object_path; | |
| 1268 | } | |
| 1269 | } else null; | |
| 1263 | 1270 | |
| 1264 | 1271 | // Here we want to determine whether we can save time by not invoking LLD when the |
| 1265 | 1272 | // 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 { |
| 1289 | 1296 | _ = try man.addFile(entry.key.status.success.object_path, null); |
| 1290 | 1297 | } |
| 1291 | 1298 | try man.addOptionalFile(module_obj_path); |
| 1299 | try man.addOptionalFile(compiler_rt_path); | |
| 1300 | ||
| 1292 | 1301 | // We can skip hashing libc and libc++ components that we are in charge of building from Zig |
| 1293 | 1302 | // installation sources because they are always a product of the compiler version + target information. |
| 1294 | 1303 | man.hash.add(stack_size); |
| ... | ... | @@ -1531,12 +1540,14 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void { |
| 1531 | 1540 | try argv.append(p); |
| 1532 | 1541 | } |
| 1533 | 1542 | |
| 1534 | // compiler-rt and libc | |
| 1535 | if (is_exe_or_dyn_lib and !self.base.options.is_compiler_rt_or_libc) { | |
| 1536 | if (!self.base.options.link_libc) { | |
| 1537 | try argv.append(comp.libc_static_lib.?.full_object_path); | |
| 1538 | } | |
| 1539 | try argv.append(comp.compiler_rt_static_lib.?.full_object_path); | |
| 1543 | // libc | |
| 1544 | if (is_exe_or_dyn_lib and !self.base.options.is_compiler_rt_or_libc and !self.base.options.link_libc) { | |
| 1545 | try argv.append(comp.libc_static_lib.?.full_object_path); | |
| 1546 | } | |
| 1547 | ||
| 1548 | // compiler-rt | |
| 1549 | if (compiler_rt_path) |p| { | |
| 1550 | try argv.append(p); | |
| 1540 | 1551 | } |
| 1541 | 1552 | |
| 1542 | 1553 | // Shared libraries. |
src/main.zig+7-5| ... | ... | @@ -314,7 +314,7 @@ const usage_build_generic = |
| 314 | 314 | \\ -fno-soname (Linux) Disable emitting a SONAME |
| 315 | 315 | \\ -fLLD Force using LLD as the linker |
| 316 | 316 | \\ -fno-LLD Prevent using LLD as the linker |
| 317 | \\ -fcompiler-rt Always including compiler-rt symbols in output | |
| 317 | \\ -fcompiler-rt Always include compiler-rt symbols in output | |
| 318 | 318 | \\ -fno-compiler-rt Prevent including compiler-rt symbols in output |
| 319 | 319 | \\ -rdynamic Add all symbols to the dynamic symbol table |
| 320 | 320 | \\ -rpath [path] Add directory to the runtime library search path |
| ... | ... | @@ -480,6 +480,7 @@ fn buildOutputType( |
| 480 | 480 | var want_sanitize_c: ?bool = null; |
| 481 | 481 | var want_stack_check: ?bool = null; |
| 482 | 482 | var want_valgrind: ?bool = null; |
| 483 | var want_compiler_rt: ?bool = null; | |
| 483 | 484 | var rdynamic: bool = false; |
| 484 | 485 | var linker_script: ?[]const u8 = null; |
| 485 | 486 | var version_script: ?[]const u8 = null; |
| ... | ... | @@ -492,7 +493,6 @@ fn buildOutputType( |
| 492 | 493 | var test_evented_io = false; |
| 493 | 494 | var stack_size_override: ?u64 = null; |
| 494 | 495 | var image_base_override: ?u64 = null; |
| 495 | var bundle_compiler_rt = false; | |
| 496 | 496 | var use_llvm: ?bool = null; |
| 497 | 497 | var use_lld: ?bool = null; |
| 498 | 498 | var use_clang: ?bool = null; |
| ... | ... | @@ -797,8 +797,10 @@ fn buildOutputType( |
| 797 | 797 | if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg}); |
| 798 | 798 | i += 1; |
| 799 | 799 | override_lib_dir = args[i]; |
| 800 | } else if (mem.eql(u8, arg, "--bundle-compiler-rt")) { | |
| 801 | bundle_compiler_rt = true; | |
| 800 | } else if (mem.eql(u8, arg, "-fcompiler-rt")) { | |
| 801 | want_compiler_rt = true; | |
| 802 | } else if (mem.eql(u8, arg, "-fno-compiler-rt")) { | |
| 803 | want_compiler_rt = false; | |
| 802 | 804 | } else if (mem.eql(u8, arg, "-feach-lib-rpath")) { |
| 803 | 805 | each_lib_rpath = true; |
| 804 | 806 | } else if (mem.eql(u8, arg, "-fno-each-lib-rpath")) { |
| ... | ... | @@ -1693,6 +1695,7 @@ fn buildOutputType( |
| 1693 | 1695 | .want_sanitize_c = want_sanitize_c, |
| 1694 | 1696 | .want_stack_check = want_stack_check, |
| 1695 | 1697 | .want_valgrind = want_valgrind, |
| 1698 | .want_compiler_rt = want_compiler_rt, | |
| 1696 | 1699 | .use_llvm = use_llvm, |
| 1697 | 1700 | .use_lld = use_lld, |
| 1698 | 1701 | .use_clang = use_clang, |
| ... | ... | @@ -1710,7 +1713,6 @@ fn buildOutputType( |
| 1710 | 1713 | .link_emit_relocs = link_emit_relocs, |
| 1711 | 1714 | .stack_size_override = stack_size_override, |
| 1712 | 1715 | .image_base_override = image_base_override, |
| 1713 | .bundle_compiler_rt = bundle_compiler_rt, | |
| 1714 | 1716 | .strip = strip, |
| 1715 | 1717 | .single_threaded = single_threaded, |
| 1716 | 1718 | .function_sections = function_sections, |