| 1 | const std = @import("std"); |
| 2 | const path = std.fs.path; |
| 3 | const assert = std.debug.assert; |
| 4 | |
| 5 | const target_util = @import("../target.zig"); |
| 6 | const Compilation = @import("../Compilation.zig"); |
| 7 | const Module = @import("../Module.zig"); |
| 8 | const build_options = @import("build_options"); |
| 9 | const trace = @import("../tracy.zig").trace; |
| 10 | |
| 11 | pub const BuildError = error{ |
| 12 | OutOfMemory, |
| 13 | AlreadyReported, |
| 14 | ZigCompilerNotBuiltWithLLVMExtensions, |
| 15 | } || std.Io.Cancelable; |
| 16 | |
| 17 | pub fn buildStaticLib(comp: *Compilation, prog_node: std.Progress.Node) BuildError!void { |
| 18 | if (!build_options.have_llvm) { |
| 19 | return error.ZigCompilerNotBuiltWithLLVMExtensions; |
| 20 | } |
| 21 | |
| 22 | const tracy = trace(@src()); |
| 23 | defer tracy.end(); |
| 24 | |
| 25 | var arena_allocator = std.heap.ArenaAllocator.init(comp.gpa); |
| 26 | defer arena_allocator.deinit(); |
| 27 | const arena = arena_allocator.allocator(); |
| 28 | |
| 29 | const io = comp.io; |
| 30 | const output_mode = .Lib; |
| 31 | const target = &comp.root_mod.resolved_target.result; |
| 32 | const unwind_tables: std.lang.UnwindTables = |
| 33 | if (target.cpu.arch == .x86 and target.os.tag == .windows) .none else .async; |
| 34 | const config = Compilation.Config.resolve(.{ |
| 35 | .output_mode = output_mode, |
| 36 | .resolved_target = comp.root_mod.resolved_target, |
| 37 | .is_test = false, |
| 38 | .have_zcu = false, |
| 39 | .emit_bin = true, |
| 40 | .root_optimize_mode = comp.compilerRtOptMode(), |
| 41 | .root_strip = comp.compilerRtStrip(), |
| 42 | .link_libc = true, |
| 43 | .any_unwind_tables = unwind_tables != .none, |
| 44 | .lto = comp.config.lto, |
| 45 | }) catch |err| { |
| 46 | comp.lockAndSetMiscFailure( |
| 47 | .libunwind, |
| 48 | "unable to build libunwind: resolving configuration failed: {s}", |
| 49 | .{@errorName(err)}, |
| 50 | ); |
| 51 | return error.AlreadyReported; |
| 52 | }; |
| 53 | const root_mod = Module.create(arena, .{ |
| 54 | .paths = .{ |
| 55 | .root = .zig_lib_root, |
| 56 | .root_src_path = "", |
| 57 | }, |
| 58 | .fully_qualified_name = "root", |
| 59 | .inherited = .{ |
| 60 | .resolved_target = comp.root_mod.resolved_target, |
| 61 | .strip = comp.compilerRtStrip(), |
| 62 | .stack_check = false, |
| 63 | .stack_protector = 0, |
| 64 | .red_zone = comp.root_mod.red_zone, |
| 65 | .omit_frame_pointer = comp.root_mod.omit_frame_pointer, |
| 66 | .valgrind = false, |
| 67 | .sanitize_c = .off, |
| 68 | .sanitize_thread = false, |
| 69 | // necessary so that libunwind can unwind through its own stack frames |
| 70 | // The old 32-bit x86 variant of SEH doesn't use tables. |
| 71 | .unwind_tables = unwind_tables, |
| 72 | .pic = if (target_util.supports_fpic(target)) true else null, |
| 73 | .optimize_mode = comp.compilerRtOptMode(), |
| 74 | .code_model = comp.root_mod.code_model, |
| 75 | }, |
| 76 | .global = config, |
| 77 | .cc_argv = &.{}, |
| 78 | .parent = null, |
| 79 | }) catch |err| { |
| 80 | comp.lockAndSetMiscFailure( |
| 81 | .libunwind, |
| 82 | "unable to build libunwind: creating module failed: {s}", |
| 83 | .{@errorName(err)}, |
| 84 | ); |
| 85 | return error.AlreadyReported; |
| 86 | }; |
| 87 | |
| 88 | const root_name = "unwind"; |
| 89 | var c_source_files: [unwind_src_list.len]Compilation.CSourceFile = undefined; |
| 90 | for (unwind_src_list, 0..) |unwind_src, i| { |
| 91 | var cflags = std.array_list.Managed([]const u8).init(arena); |
| 92 | |
| 93 | switch (Compilation.classifyFileExt(unwind_src)) { |
| 94 | .c => { |
| 95 | try cflags.appendSlice(&.{ |
| 96 | "-std=c99", |
| 97 | "-fexceptions", |
| 98 | }); |
| 99 | }, |
| 100 | .cpp => { |
| 101 | try cflags.append("-fno-exceptions"); |
| 102 | try cflags.append("-fno-rtti"); |
| 103 | }, |
| 104 | .assembly_with_cpp => {}, |
| 105 | else => unreachable, // See `unwind_src_list`. |
| 106 | } |
| 107 | try cflags.append("-w"); // Disable all warnings. |
| 108 | try cflags.append("-I"); |
| 109 | try cflags.append(try comp.dirs.zig_lib.join(arena, &.{ "libunwind", "include" })); |
| 110 | try cflags.append("-D_LIBUNWIND_HIDE_SYMBOLS"); |
| 111 | try cflags.append("-Wa,--noexecstack"); |
| 112 | try cflags.append("-fvisibility=hidden"); |
| 113 | try cflags.append("-fvisibility-inlines-hidden"); |
| 114 | try cflags.append("-fvisibility-global-new-delete=force-hidden"); |
| 115 | |
| 116 | // This is intentionally always defined because the macro definition means, should it only |
| 117 | // build for the target specified by compiler defines. Since we pass -target the compiler |
| 118 | // defines will be correct. |
| 119 | try cflags.append("-D_LIBUNWIND_IS_NATIVE_ONLY"); |
| 120 | |
| 121 | if (comp.root_mod.optimize_mode == .debug) { |
| 122 | try cflags.append("-D_DEBUG"); |
| 123 | } |
| 124 | if (!comp.config.any_non_single_threaded) { |
| 125 | try cflags.append("-D_LIBUNWIND_HAS_NO_THREADS"); |
| 126 | } |
| 127 | if (target.cpu.arch.isArm() and target.abi.float() == .hard) { |
| 128 | try cflags.append("-DCOMPILER_RT_ARMHF_TARGET"); |
| 129 | } |
| 130 | |
| 131 | c_source_files[i] = .{ |
| 132 | .src_path = try comp.dirs.zig_lib.join(arena, &.{unwind_src}), |
| 133 | .extra_flags = cflags.items, |
| 134 | .owner = root_mod, |
| 135 | }; |
| 136 | } |
| 137 | |
| 138 | const misc_task: Compilation.MiscTask = .libunwind; |
| 139 | |
| 140 | var sub_create_diag: Compilation.CreateDiagnostic = undefined; |
| 141 | const sub_compilation = Compilation.create(comp.gpa, arena, io, &sub_create_diag, .{ |
| 142 | .thread_limit = comp.thread_limit, |
| 143 | .dirs = comp.dirs.withoutLocalCache(), |
| 144 | .self_exe_path = comp.self_exe_path, |
| 145 | .config = config, |
| 146 | .root_mod = root_mod, |
| 147 | .cache_mode = .whole, |
| 148 | .root_name = root_name, |
| 149 | .main_mod = null, |
| 150 | .libc_installation = comp.libc_installation, |
| 151 | .emit_bin = .yes_cache, |
| 152 | .function_sections = true, |
| 153 | .data_sections = true, |
| 154 | .c_source_files = &c_source_files, |
| 155 | .verbose_cc = comp.verbose_cc, |
| 156 | .verbose_link = comp.verbose_link, |
| 157 | .verbose_air = comp.verbose_air, |
| 158 | .verbose_llvm_ir = comp.verbose_llvm_ir, |
| 159 | .verbose_llvm_bc = comp.verbose_llvm_bc, |
| 160 | .verbose_llvm_cpu_features = comp.verbose_llvm_cpu_features, |
| 161 | .clang_passthrough_mode = comp.clang_passthrough_mode, |
| 162 | .skip_linker_dependencies = true, |
| 163 | .environ_map = comp.environ_map, |
| 164 | }) catch |err| { |
| 165 | switch (err) { |
| 166 | else => comp.lockAndSetMiscFailure(misc_task, "unable to build {t}: create compilation failed: {t}", .{ misc_task, err }), |
| 167 | error.CreateFail => comp.lockAndSetMiscFailure(misc_task, "unable to build {t}: create compilation failed: {f}", .{ misc_task, sub_create_diag }), |
| 168 | } |
| 169 | return error.AlreadyReported; |
| 170 | }; |
| 171 | defer sub_compilation.destroy(); |
| 172 | |
| 173 | comp.updateSubCompilation(sub_compilation, misc_task, prog_node) catch |err| switch (err) { |
| 174 | error.AlreadyReported => |e| return e, |
| 175 | else => |e| { |
| 176 | comp.lockAndSetMiscFailure(misc_task, "unable to build {t}: compilation failed: {s}", .{ misc_task, @errorName(e) }); |
| 177 | return error.AlreadyReported; |
| 178 | }, |
| 179 | }; |
| 180 | |
| 181 | const crt_file = try sub_compilation.toCrtFile(); |
| 182 | try comp.queuePrelinkTaskMode(crt_file.full_object_path, false, &config); |
| 183 | assert(comp.libunwind_static_lib == null); |
| 184 | comp.libunwind_static_lib = crt_file; |
| 185 | } |
| 186 | |
| 187 | const unwind_src_list = [_][]const u8{ |
| 188 | "libunwind" ++ path.sep_str ++ "src" ++ path.sep_str ++ "libunwind.cpp", |
| 189 | "libunwind" ++ path.sep_str ++ "src" ++ path.sep_str ++ "Unwind-EHABI.cpp", |
| 190 | "libunwind" ++ path.sep_str ++ "src" ++ path.sep_str ++ "Unwind-seh.cpp", |
| 191 | "libunwind" ++ path.sep_str ++ "src" ++ path.sep_str ++ "UnwindLevel1.c", |
| 192 | "libunwind" ++ path.sep_str ++ "src" ++ path.sep_str ++ "UnwindLevel1-gcc-ext.c", |
| 193 | "libunwind" ++ path.sep_str ++ "src" ++ path.sep_str ++ "Unwind-sjlj.c", |
| 194 | "libunwind" ++ path.sep_str ++ "src" ++ path.sep_str ++ "Unwind-wasm.c", |
| 195 | "libunwind" ++ path.sep_str ++ "src" ++ path.sep_str ++ "UnwindRegistersRestore.S", |
| 196 | "libunwind" ++ path.sep_str ++ "src" ++ path.sep_str ++ "UnwindRegistersSave.S", |
| 197 | "libunwind" ++ path.sep_str ++ "src" ++ path.sep_str ++ "gcc_personality_v0.c", |
| 198 | }; |