Commit 931178494f

David Rubin <daviru007@icloud.com>
2025-01-01 12:47:17
Compilation: correct when to include ubsan
1 parent 9432a9b
Changed files (5)
lib/ubsan.zig → lib/ubsan_rt.zig
@@ -59,7 +59,7 @@ const Value = extern struct {
         return switch (size) {
             64 => @as(*const u64, @alignCast(@ptrCast(value.handle))).*,
             128 => @as(*const u128, @alignCast(@ptrCast(value.handle))).*,
-            else => unreachable,
+            else => @trap(),
         };
     }
 
@@ -186,8 +186,8 @@ fn divRemHandler(
     lhs_handle: ValueHandle,
     rhs_handle: ValueHandle,
 ) callconv(.c) noreturn {
-    const lhs: Value = .{ .handle = lhs_handle, .td = data.lhs_type };
-    const rhs: Value = .{ .handle = rhs_handle, .td = data.rhs_type };
+    const lhs: Value = .{ .handle = lhs_handle, .td = data.td };
+    const rhs: Value = .{ .handle = rhs_handle, .td = data.td };
 
     if (rhs.isMinusOne()) {
         logMessage(
@@ -533,26 +533,10 @@ fn exportHandler(
     }
 }
 
-fn exportMinimal(
-    comptime err_name: []const u8,
-    comptime sym_name: []const u8,
-    comptime abort: bool,
-) void {
-    const S = struct {
-        fn handler() callconv(.c) noreturn {
-            logMessage("{s}", .{err_name});
-        }
-    };
-    const linkage = if (builtin.is_test) .internal else .weak;
-    {
-        const N = "__ubsan_handle_" ++ sym_name ++ "_minimal";
-        @export(&S.handler, .{ .name = N, .linkage = linkage });
-    }
-    if (abort) {
-        const N = "__ubsan_handle_" ++ sym_name ++ "_minimal_abort";
-        @export(&S.handler, .{ .name = N, .linkage = linkage });
-    }
-}
+const can_build_ubsan = switch (builtin.zig_backend) {
+    .stage2_riscv64 => false,
+    else => true,
+};
 
 comptime {
     overflowHandler("add_overflow", "+");
@@ -574,24 +558,6 @@ comptime {
     exportHandler(&typeMismatch, "type_mismatch_v1", true);
     exportHandler(&vlaBoundNotPositive, "vla_bound_not_positive", true);
 
-    exportMinimal("add-overflow", "add_overflow", true);
-    exportMinimal("sub-overflow", "sub_overflow", true);
-    exportMinimal("mul-overflow", "mul_overflow", true);
-    exportMinimal("alignment-assumption-handler", "alignment_assumption", true);
-    exportMinimal("builtin-unreachable", "builtin_unreachable", false);
-    exportMinimal("divrem-handler", "divrem_overflow", true);
-    exportMinimal("float-cast-overflow", "float_cast_overflow", true);
-    exportMinimal("invalid-builtin", "invalid_builtin", true);
-    exportMinimal("load-invalid-value", "load_invalid_value", true);
-    exportMinimal("missing-return", "missing_return", true);
-    exportMinimal("negation-handler", "negate_overflow", true);
-    exportMinimal("nonnull-arg", "nonnull_arg", true);
-    exportMinimal("out-of-bounds", "out_of_bounds", true);
-    exportMinimal("pointer-overflow", "pointer_overflow", true);
-    exportMinimal("shift-oob", "shift_out_of_bounds", true);
-    exportMinimal("type-mismatch", "type_mismatch", true);
-    exportMinimal("vla-bound-not-positive", "vla_bound_not_positive", true);
-
     // these checks are nearly impossible to duplicate in zig, as they rely on nuances
     // in the Itanium C++ ABI.
     // exportHelper("dynamic_type_cache_miss", "dynamic-type-cache-miss", true);
src/Compilation/Config.zig
@@ -32,6 +32,7 @@ any_non_single_threaded: bool,
 /// per-Module setting.
 any_error_tracing: bool,
 any_sanitize_thread: bool,
+any_sanitize_c: bool,
 any_fuzz: bool,
 pie: bool,
 /// If this is true then linker code is responsible for making an LLVM IR
@@ -87,6 +88,7 @@ pub const Options = struct {
     ensure_libcpp_on_non_freestanding: bool = false,
     any_non_single_threaded: bool = false,
     any_sanitize_thread: bool = false,
+    any_sanitize_c: bool = false,
     any_fuzz: bool = false,
     any_unwind_tables: bool = false,
     any_dyn_libs: bool = false,
@@ -476,6 +478,7 @@ pub fn resolve(options: Options) ResolveError!Config {
         .any_non_single_threaded = options.any_non_single_threaded,
         .any_error_tracing = any_error_tracing,
         .any_sanitize_thread = options.any_sanitize_thread,
+        .any_sanitize_c = options.any_sanitize_c,
         .any_fuzz = options.any_fuzz,
         .san_cov_trace_pc_guard = options.san_cov_trace_pc_guard,
         .root_error_tracing = root_error_tracing,
src/link/MachO/relocatable.zig
@@ -93,11 +93,11 @@ pub fn flushStaticLib(macho_file: *MachO, comp: *Compilation, module_obj_path: ?
 
     if (module_obj_path) |path| try positionals.append(try link.openObjectInput(diags, path));
 
-    if (comp.include_compiler_rt) {
+    if (comp.compiler_rt_strat == .obj) {
         try positionals.append(try link.openObjectInput(diags, comp.compiler_rt_obj.?.full_object_path));
     }
 
-    if (comp.include_ubsan_rt) {
+    if (comp.ubsan_rt_strat == .obj) {
         try positionals.append(try link.openObjectInput(diags, comp.ubsan_rt_obj.?.full_object_path));
     }
 
src/Compilation.zig
@@ -78,8 +78,8 @@ implib_emit: ?Path,
 /// This is non-null when `-femit-docs` is provided.
 docs_emit: ?Path,
 root_name: [:0]const u8,
-include_compiler_rt: bool,
-include_ubsan_rt: bool,
+compiler_rt_strat: RtStrat,
+ubsan_rt_strat: RtStrat,
 /// Resolved into known paths, any GNU ld scripts already resolved.
 link_inputs: []const link.Input,
 /// Needed only for passing -F args to clang.
@@ -1256,6 +1256,8 @@ fn addModuleTableToCacheHash(
     }
 }
 
+const RtStrat = enum { none, lib, obj, zcu };
+
 pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compilation {
     const output_mode = options.config.output_mode;
     const is_dyn_lib = switch (output_mode) {
@@ -1287,6 +1289,7 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil
         const any_unwind_tables = options.config.any_unwind_tables or options.root_mod.unwind_tables != .none;
         const any_non_single_threaded = options.config.any_non_single_threaded or !options.root_mod.single_threaded;
         const any_sanitize_thread = options.config.any_sanitize_thread or options.root_mod.sanitize_thread;
+        const any_sanitize_c = options.config.any_sanitize_c or options.root_mod.sanitize_c;
         const any_fuzz = options.config.any_fuzz or options.root_mod.fuzz;
 
         const link_eh_frame_hdr = options.link_eh_frame_hdr or any_unwind_tables;
@@ -1305,13 +1308,25 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil
 
         const sysroot = options.sysroot orelse libc_dirs.sysroot;
 
-        const include_compiler_rt = options.want_compiler_rt orelse
-            (!options.skip_linker_dependencies and is_exe_or_dyn_lib);
+        const compiler_rt_strat: RtStrat = s: {
+            if (options.skip_linker_dependencies) break :s .none;
+            const want = options.want_compiler_rt orelse is_exe_or_dyn_lib;
+            if (!want) break :s .none;
+            if (have_zcu and output_mode == .Obj) break :s .zcu;
+            if (is_exe_or_dyn_lib) break :s .lib;
+            break :s .obj;
+        };
 
-        const include_ubsan_rt = options.want_ubsan_rt orelse
-            (!options.skip_linker_dependencies and is_exe_or_dyn_lib and !have_zcu);
+        const ubsan_rt_strat: RtStrat = s: {
+            const want_ubsan_rt = options.want_ubsan_rt orelse (any_sanitize_c and output_mode != .Obj);
+            if (!want_ubsan_rt) break :s .none;
+            if (options.skip_linker_dependencies) break :s .none;
+            if (have_zcu) break :s .zcu;
+            if (is_exe_or_dyn_lib) break :s .lib;
+            break :s .obj;
+        };
 
-        if (include_compiler_rt and output_mode == .Obj) {
+        if (compiler_rt_strat == .zcu) {
             // For objects, this mechanism relies on essentially `_ = @import("compiler-rt");`
             // injected into the object.
             const compiler_rt_mod = try Package.Module.create(arena, .{
@@ -1340,7 +1355,7 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil
         // unlike compiler_rt, we always want to go through the `_ = @import("ubsan-rt")`
         // approach, since the ubsan runtime uses quite a lot of the standard library
         // and this reduces unnecessary bloat.
-        if (!options.skip_linker_dependencies and have_zcu) {
+        if (ubsan_rt_strat == .zcu) {
             const ubsan_rt_mod = try Package.Module.create(arena, .{
                 .global_cache_directory = options.global_cache_directory,
                 .paths = .{
@@ -1536,8 +1551,8 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil
             .windows_libs = windows_libs,
             .version = options.version,
             .libc_installation = libc_dirs.libc_installation,
-            .include_compiler_rt = include_compiler_rt,
-            .include_ubsan_rt = include_ubsan_rt,
+            .compiler_rt_strat = compiler_rt_strat,
+            .ubsan_rt_strat = ubsan_rt_strat,
             .link_inputs = options.link_inputs,
             .framework_dirs = options.framework_dirs,
             .llvm_opt_bisect_limit = options.llvm_opt_bisect_limit,
@@ -1563,6 +1578,7 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil
         comp.config.any_unwind_tables = any_unwind_tables;
         comp.config.any_non_single_threaded = any_non_single_threaded;
         comp.config.any_sanitize_thread = any_sanitize_thread;
+        comp.config.any_sanitize_c = any_sanitize_c;
         comp.config.any_fuzz = any_fuzz;
 
         const lf_open_opts: link.File.OpenOptions = .{
@@ -1909,34 +1925,51 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil
                 comp.remaining_prelink_tasks += 1;
             }
 
+<<<<<<< HEAD
             if (comp.include_compiler_rt and capable_of_building_compiler_rt) {
                 if (is_exe_or_dyn_lib) {
+=======
+            if (target.isMinGW() and comp.config.any_non_single_threaded) {
+                // LLD might drop some symbols as unused during LTO and GCing, therefore,
+                // we force mark them for resolution here.
+
+                const tls_index_sym = switch (target.cpu.arch) {
+                    .x86 => "__tls_index",
+                    else => "_tls_index",
+                };
+
+                try comp.force_undefined_symbols.put(comp.gpa, tls_index_sym, {});
+            }
+
+            if (capable_of_building_compiler_rt) {
+                if (comp.compiler_rt_strat == .lib) {
+>>>>>>> 050e3e69ac (Compilation: correct when to include ubsan)
                     log.debug("queuing a job to build compiler_rt_lib", .{});
                     comp.queued_jobs.compiler_rt_lib = true;
                     comp.remaining_prelink_tasks += 1;
-                } else if (output_mode != .Obj) {
+                } else if (comp.compiler_rt_strat == .obj) {
                     log.debug("queuing a job to build compiler_rt_obj", .{});
                     // In this case we are making a static library, so we ask
                     // for a compiler-rt object to put in it.
                     comp.queued_jobs.compiler_rt_obj = true;
                     comp.remaining_prelink_tasks += 1;
                 }
-            }
 
-            if (comp.include_ubsan_rt and capable_of_building_compiler_rt) {
-                if (is_exe_or_dyn_lib) {
+                if (comp.ubsan_rt_strat == .lib) {
                     log.debug("queuing a job to build ubsan_rt_lib", .{});
                     comp.job_queued_ubsan_rt_lib = true;
-                } else if (output_mode != .Obj) {
+                    comp.remaining_prelink_tasks += 1;
+                } else if (comp.ubsan_rt_strat == .obj) {
                     log.debug("queuing a job to build ubsan_rt_obj", .{});
                     comp.job_queued_ubsan_rt_obj = true;
+                    comp.remaining_prelink_tasks += 1;
                 }
-            }
 
-            if (is_exe_or_dyn_lib and comp.config.any_fuzz and capable_of_building_compiler_rt) {
-                log.debug("queuing a job to build libfuzzer", .{});
-                comp.queued_jobs.fuzzer_lib = true;
-                comp.remaining_prelink_tasks += 1;
+                if (is_exe_or_dyn_lib and comp.config.any_fuzz) {
+                    log.debug("queuing a job to build libfuzzer", .{});
+                    comp.queued_jobs.fuzzer_lib = true;
+                    comp.remaining_prelink_tasks += 1;
+                }
             }
         }
 
@@ -2656,8 +2689,8 @@ fn addNonIncrementalStuffToCacheManifest(
     man.hash.addOptional(comp.version);
     man.hash.add(comp.link_eh_frame_hdr);
     man.hash.add(comp.skip_linker_dependencies);
-    man.hash.add(comp.include_compiler_rt);
-    man.hash.add(comp.include_ubsan_rt);
+    man.hash.add(comp.compiler_rt_strat);
+    man.hash.add(comp.ubsan_rt_strat);
     man.hash.add(comp.rc_includes);
     man.hash.addListOfBytes(comp.force_undefined_symbols.keys());
     man.hash.addListOfBytes(comp.framework_dirs);
@@ -3749,11 +3782,11 @@ fn performAllTheWorkInner(
     }
 
     if (comp.queued_jobs.ubsan_rt_lib and comp.ubsan_rt_lib == null) {
-        comp.link_task_wait_group.spawnManager(buildRt, .{ comp, "ubsan.zig", .libubsan, .Lib, &comp.ubsan_rt_lib, main_progress_node });
+        comp.link_task_wait_group.spawnManager(buildRt, .{ comp, "ubsan_rt.zig", .libubsan, .Lib, &comp.ubsan_rt_lib, main_progress_node });
     }
 
     if (comp.queued_jobs.ubsan_rt_obj and comp.ubsan_rt_obj == null) {
-        comp.link_task_wait_group.spawnManager(buildRt, .{ comp, "ubsan.zig", .libubsan, .Obj, &comp.ubsan_rt_obj, main_progress_node });
+        comp.link_task_wait_group.spawnManager(buildRt, .{ comp, "ubsan_rt.zig", .libubsan, .Obj, &comp.ubsan_rt_obj, main_progress_node });
     }
 
     if (comp.queued_jobs.glibc_shared_objects) {
src/link.zig
@@ -1102,12 +1102,12 @@ pub const File = struct {
 
         log.debug("zcu_obj_path={s}", .{if (zcu_obj_path) |s| s else "(null)"});
 
-        const compiler_rt_path: ?Path = if (comp.include_compiler_rt)
+        const compiler_rt_path: ?Path = if (comp.compiler_rt_strat == .obj)
             comp.compiler_rt_obj.?.full_object_path
         else
             null;
 
-        const ubsan_rt_path: ?Path = if (comp.include_ubsan_rt)
+        const ubsan_rt_path: ?Path = if (comp.ubsan_rt_strat == .obj)
             comp.ubsan_rt_obj.?.full_object_path
         else
             null;