Commit 791e38da8c

Andrew Kelley <andrew@ziglang.org>
2020-12-01 00:40:25
Merge branch 'alexnask-bundle_compiler_rt' into master
Closes #7013 Closes #6817
1 parent 1a61a9d
Changed files (5)
lib/std/build.zig
@@ -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,
@@ -1392,7 +1392,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,
@@ -2117,8 +2116,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");
src/link/Elf.zig
@@ -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);
@@ -1524,12 +1533,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.
src/Compilation.zig
@@ -93,6 +93,9 @@ libc_static_lib: ?CRTFile = null,
 /// Populated when we build the libcompiler_rt static library. A Job to build this is placed in the queue
 /// and resolved before calling linker.flush().
 compiler_rt_static_lib: ?CRTFile = null,
+/// Populated when we build the compiler_rt_obj object. A Job to build this is placed in the queue
+/// and resolved before calling linker.flush().
+compiler_rt_obj: ?CRTFile = null,
 
 glibc_so_files: ?glibc.BuiltSharedObjects = null,
 
@@ -164,8 +167,8 @@ const Job = union(enum) {
     libcxx: void,
     libcxxabi: void,
     libssp: void,
-    /// needed when producing a dynamic library or executable
-    libcompiler_rt: void,
+    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.
     zig_libc: void,
@@ -346,6 +349,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,
@@ -405,6 +409,9 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {
         .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().
@@ -568,6 +575,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: {
@@ -803,6 +812,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,
+            .include_compiler_rt = include_compiler_rt,
             .linker_script = options.linker_script,
             .version_script = options.version_script,
             .gc_sections = options.linker_gc_sections,
@@ -947,16 +957,31 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {
             try comp.work_queue.writeItem(.libcxxabi);
         }
 
-        const needs_compiler_rt_and_c = is_exe_or_dyn_lib or
-            (comp.getTarget().isWasm() and comp.bin_file.options.output_mode != .Obj);
-        if (needs_compiler_rt_and_c and build_options.is_stage1) {
-            try comp.work_queue.writeItem(.{ .libcompiler_rt = {} });
-            // MinGW provides no libssp, use our own implementation.
-            if (comp.getTarget().isMinGW()) {
-                try comp.work_queue.writeItem(.{ .libssp = {} });
+        // 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 (!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 = {} });
+                }
             }
         }
     }
@@ -1354,20 +1379,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: {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)});
             };
@@ -2531,10 +2562,16 @@ pub fn updateSubCompilation(sub_compilation: *Compilation) !void {
     }
 }
 
-fn buildStaticLibFromZig(comp: *Compilation, src_basename: []const u8, out: *?CRTFile) !void {
+fn buildOutputFromZig(
+    comp: *Compilation,
+    src_basename: []const u8,
+    output_mode: std.builtin.OutputMode,
+    out: *?CRTFile,
+) !void {
     const tracy = trace(@src());
     defer tracy.end();
 
+    std.debug.assert(output_mode != .Exe);
     const special_sub = "std" ++ std.fs.path.sep_str ++ "special";
     const special_path = try comp.zig_lib_directory.join(comp.gpa, &[_][]const u8{special_sub});
     defer comp.gpa.free(special_path);
@@ -2551,11 +2588,11 @@ fn buildStaticLibFromZig(comp: *Compilation, src_basename: []const u8, out: *?CR
     };
     const root_name = mem.split(src_basename, ".").next().?;
     const target = comp.getTarget();
-    const output_mode: std.builtin.OutputMode = if (target.cpu.arch.isWasm()) .Obj else .Lib;
+    const fixed_output_mode = if (target.cpu.arch.isWasm()) .Obj else output_mode;
     const bin_basename = try std.zig.binNameAlloc(comp.gpa, .{
         .root_name = root_name,
         .target = target,
-        .output_mode = output_mode,
+        .output_mode = fixed_output_mode,
     });
     defer comp.gpa.free(bin_basename);
 
@@ -2578,7 +2615,7 @@ fn buildStaticLibFromZig(comp: *Compilation, src_basename: []const u8, out: *?CR
         .target = target,
         .root_name = root_name,
         .root_pkg = &root_pkg,
-        .output_mode = output_mode,
+        .output_mode = fixed_output_mode,
         .rand = comp.rand,
         .libc_installation = comp.bin_file.options.libc_installation,
         .emit_bin = emit_bin,
src/link.zig
@@ -46,6 +46,7 @@ pub const Options = struct {
     entry_addr: ?u64 = null,
     stack_size_override: ?u64,
     image_base_override: ?u64,
+    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
@@ -450,6 +451,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.
@@ -466,6 +472,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();
@@ -495,7 +502,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);
+        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));
         }
@@ -505,6 +512,9 @@ pub const File = struct {
         if (module_obj_path) |p| {
             object_files.appendAssumeCapacity(try arena.dupeZ(u8, p));
         }
+        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});
         const full_out_path_z = try arena.dupeZ(u8, full_out_path);
src/main.zig
@@ -304,8 +304,12 @@ const usage_build_generic =
     \\  --version-script [path]        Provide a version .map file
     \\  --dynamic-linker [path]        Set the dynamic interpreter path (usually ld.so)
     \\  --version [ver]                Dynamic library semver
-    \\  -fsoname[=name]                (linux) Override the default SONAME value
-    \\  -fno-soname                    (linux) Disable emitting a SONAME
+    \\  -fsoname[=name]                (Linux) Override the default SONAME value
+    \\  -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 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
     \\  -feach-lib-rpath               Ensure adding rpath for each used dynamic library
@@ -469,6 +473,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;
@@ -785,6 +790,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, "-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")) {
@@ -1667,6 +1676,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,