aboutsummaryrefslogtreecommitdiff
path: root/src/Compilation.zig
diff options
context:
space:
mode:
Diffstat (limited to 'src/Compilation.zig')
-rw-r--r--src/Compilation.zig76
1 files changed, 76 insertions, 0 deletions
diff --git a/src/Compilation.zig b/src/Compilation.zig
index 473006c2ea..e4bccdc2d8 100644
--- a/src/Compilation.zig
+++ b/src/Compilation.zig
@@ -79,6 +79,7 @@ implib_emit: ?Path,
docs_emit: ?Path,
root_name: [:0]const u8,
include_compiler_rt: bool,
+include_ubsan_rt: bool,
/// Resolved into known paths, any GNU ld scripts already resolved.
link_inputs: []const link.Input,
/// Needed only for passing -F args to clang.
@@ -226,6 +227,12 @@ libunwind_static_lib: ?CrtFile = null,
/// Populated when we build the TSAN library. A Job to build this is placed in the queue
/// and resolved before calling linker.flush().
tsan_lib: ?CrtFile = null,
+/// Populated when we build the UBSAN library. A Job to build this is placed in the queue
+/// and resolved before calling linker.flush().
+ubsan_rt_lib: ?CrtFile = null,
+/// Populated when we build the UBSAN object. A Job to build this is placed in the queue
+/// and resolved before calling linker.flush().
+ubsan_rt_obj: ?CrtFile = null,
/// Populated when we build the libc static library. A Job to build this is placed in the queue
/// and resolved before calling linker.flush().
libc_static_lib: ?CrtFile = null,
@@ -283,6 +290,8 @@ digest: ?[Cache.bin_digest_len]u8 = null,
const QueuedJobs = struct {
compiler_rt_lib: bool = false,
compiler_rt_obj: bool = false,
+ ubsan_rt_lib: bool = false,
+ ubsan_rt_obj: bool = false,
fuzzer_lib: bool = false,
update_builtin_zig: bool,
musl_crt_file: [@typeInfo(musl.CrtFile).@"enum".fields.len]bool = @splat(false),
@@ -789,6 +798,7 @@ pub const MiscTask = enum {
libcxx,
libcxxabi,
libtsan,
+ libubsan,
libfuzzer,
wasi_libc_crt_file,
compiler_rt,
@@ -1064,6 +1074,7 @@ pub const CreateOptions = struct {
/// Position Independent Executable. If the output mode is not an
/// executable this field is ignored.
want_compiler_rt: ?bool = null,
+ want_ubsan_rt: ?bool = null,
want_lto: ?bool = null,
function_sections: bool = false,
data_sections: bool = false,
@@ -1297,6 +1308,9 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil
const include_compiler_rt = options.want_compiler_rt orelse
(!options.skip_linker_dependencies and is_exe_or_dyn_lib);
+ const include_ubsan_rt = options.want_ubsan_rt orelse
+ (!options.skip_linker_dependencies and is_exe_or_dyn_lib);
+
if (include_compiler_rt and output_mode == .Obj) {
// For objects, this mechanism relies on essentially `_ = @import("compiler-rt");`
// injected into the object.
@@ -1323,6 +1337,26 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil
try options.root_mod.deps.putNoClobber(arena, "compiler_rt", compiler_rt_mod);
}
+ if (include_ubsan_rt and output_mode == .Obj) {
+ const ubsan_rt_mod = try Package.Module.create(arena, .{
+ .global_cache_directory = options.global_cache_directory,
+ .paths = .{
+ .root = .{
+ .root_dir = options.zig_lib_directory,
+ },
+ .root_src_path = "ubsan.zig",
+ },
+ .fully_qualified_name = "ubsan_rt",
+ .cc_argv = &.{},
+ .inherited = .{},
+ .global = options.config,
+ .parent = options.root_mod,
+ .builtin_mod = options.root_mod.getBuiltinDependency(),
+ .builtin_modules = null, // `builtin_mod` is set
+ });
+ try options.root_mod.deps.putNoClobber(arena, "ubsan_rt", ubsan_rt_mod);
+ }
+
if (options.verbose_llvm_cpu_features) {
if (options.root_mod.resolved_target.llvm_cpu_features) |cf| print: {
const target = options.root_mod.resolved_target.result;
@@ -1500,6 +1534,7 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil
.version = options.version,
.libc_installation = libc_dirs.libc_installation,
.include_compiler_rt = include_compiler_rt,
+ .include_ubsan_rt = include_ubsan_rt,
.link_inputs = options.link_inputs,
.framework_dirs = options.framework_dirs,
.llvm_opt_bisect_limit = options.llvm_opt_bisect_limit,
@@ -1885,6 +1920,16 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil
}
}
+ if (comp.include_ubsan_rt and capable_of_building_compiler_rt) {
+ if (is_exe_or_dyn_lib) {
+ log.debug("queuing a job to build ubsan_rt_lib", .{});
+ comp.job_queued_ubsan_rt_lib = true;
+ } else if (output_mode != .Obj) {
+ log.debug("queuing a job to build ubsan_rt_obj", .{});
+ comp.job_queued_ubsan_rt_obj = true;
+ }
+ }
+
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;
@@ -1937,9 +1982,16 @@ pub fn destroy(comp: *Compilation) void {
if (comp.compiler_rt_obj) |*crt_file| {
crt_file.deinit(gpa);
}
+ if (comp.ubsan_rt_lib) |*crt_file| {
+ crt_file.deinit(gpa);
+ }
+ if (comp.ubsan_rt_obj) |*crt_file| {
+ crt_file.deinit(gpa);
+ }
if (comp.fuzzer_lib) |*crt_file| {
crt_file.deinit(gpa);
}
+
if (comp.libc_static_lib) |*crt_file| {
crt_file.deinit(gpa);
}
@@ -2207,6 +2259,10 @@ pub fn update(comp: *Compilation, main_progress_node: std.Progress.Node) !void {
_ = try pt.importPkg(zcu.main_mod);
}
+ if (zcu.root_mod.deps.get("ubsan_rt")) |ubsan_rt_mod| {
+ _ = try pt.importPkg(ubsan_rt_mod);
+ }
+
if (zcu.root_mod.deps.get("compiler_rt")) |compiler_rt_mod| {
_ = try pt.importPkg(compiler_rt_mod);
}
@@ -2248,6 +2304,11 @@ pub fn update(comp: *Compilation, main_progress_node: std.Progress.Node) !void {
try comp.queueJob(.{ .analyze_mod = compiler_rt_mod });
zcu.analysis_roots.appendAssumeCapacity(compiler_rt_mod);
}
+
+ if (zcu.root_mod.deps.get("ubsan_rt")) |ubsan_rt_mod| {
+ try comp.queueJob(.{ .analyze_mod = ubsan_rt_mod });
+ zcu.analysis_roots.appendAssumeCapacity(ubsan_rt_mod);
+ }
}
try comp.performAllTheWork(main_progress_node);
@@ -2593,6 +2654,7 @@ fn addNonIncrementalStuffToCacheManifest(
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.rc_includes);
man.hash.addListOfBytes(comp.force_undefined_symbols.keys());
man.hash.addListOfBytes(comp.framework_dirs);
@@ -3683,6 +3745,14 @@ fn performAllTheWorkInner(
comp.link_task_wait_group.spawnManager(buildRt, .{ comp, "fuzzer.zig", .libfuzzer, .Lib, true, &comp.fuzzer_lib, main_progress_node });
}
+ 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 });
+ }
+
+ 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 });
+ }
+
if (comp.queued_jobs.glibc_shared_objects) {
comp.link_task_wait_group.spawnManager(buildGlibcSharedObjects, .{ comp, main_progress_node });
}
@@ -5916,7 +5986,11 @@ pub fn addCCArgs(
// These args have to be added after the `-fsanitize` arg or
// they won't take effect.
if (mod.sanitize_c) {
+ // This check requires implementing the Itanium C++ ABI.
+ // We would make it `-fsanitize-trap=vptr`, however this check requires
+ // a full runtime due to the type hashing involved.
try argv.append("-fno-sanitize=vptr");
+
// It is very common, and well-defined, for a pointer on one side of a C ABI
// to have a different but compatible element type. Examples include:
// `char*` vs `uint8_t*` on a system with 8-bit bytes
@@ -5926,6 +6000,8 @@ pub fn addCCArgs(
// function was called.
try argv.append("-fno-sanitize=function");
+ // It's recommended to use the minimal runtime in production environments
+ // due to the security implications of the full runtime.
if (mod.optimize_mode == .ReleaseSafe) {
try argv.append("-fsanitize-minimal-runtime");
}