mirror of
https://codeberg.org/ziglang/zig.git
synced 2025-12-06 13:54:21 +00:00
stage2: building libunwind.a
and put glibc shared objects on the elf linker line
This commit is contained in:
parent
17f094ec5b
commit
17d40ecb49
6 changed files with 234 additions and 32 deletions
13
BRANCH_TODO
13
BRANCH_TODO
|
|
@ -1,7 +1,10 @@
|
|||
* libunwind
|
||||
* stage1 C++ code integration
|
||||
* make sure zig cc works
|
||||
- using it as a preprocessor (-E)
|
||||
- @breakpoint(); // TODO the first arg is empty string right? skip past that.
|
||||
- try building some software
|
||||
* support rpaths in ELF linker code
|
||||
* build & link against compiler-rt
|
||||
- stage1 C++ code integration
|
||||
* build & link againstn freestanding libc
|
||||
* add CLI support for a way to pass extra flags to c source files
|
||||
* implement the workaround for using LLVM to detect native CPU features
|
||||
|
|
@ -12,10 +15,6 @@
|
|||
* port the stage1 os.cpp code that raises the open fd limit
|
||||
* use global zig-cache dir for crt files
|
||||
* `zig translate-c`
|
||||
* make sure zig cc works
|
||||
- using it as a preprocessor (-E)
|
||||
- @breakpoint(); // TODO the first arg is empty string right? skip past that.
|
||||
- try building some software
|
||||
* MachO LLD linking
|
||||
* COFF LLD linking
|
||||
* WASM LLD linking
|
||||
|
|
@ -54,3 +53,5 @@
|
|||
- make it possible for Package to not openDir and reference already existing resources.
|
||||
* rename src/ to src/stage1/
|
||||
* rename src-self-hosted/ to src/
|
||||
* improve Directory.join to only use 1 allocation in a clean way.
|
||||
* tracy builds with lc++
|
||||
|
|
|
|||
32
build.zig
32
build.zig
|
|
@ -73,6 +73,9 @@ pub fn build(b: *Builder) !void {
|
|||
if (only_install_lib_files)
|
||||
return;
|
||||
|
||||
const tracy = b.option([]const u8, "tracy", "Enable Tracy integration. Supply path to Tracy source");
|
||||
const link_libc = b.option(bool, "force-link-libc", "Force self-hosted compiler to link libc") orelse enable_llvm;
|
||||
|
||||
var exe = b.addExecutable("zig", "src-self-hosted/main.zig");
|
||||
exe.install();
|
||||
exe.setBuildMode(mode);
|
||||
|
|
@ -90,10 +93,8 @@ pub fn build(b: *Builder) !void {
|
|||
var ctx = parseConfigH(b, config_h_text);
|
||||
ctx.llvm = try findLLVM(b, ctx.llvm_config_exe);
|
||||
|
||||
try configureStage2(b, exe, ctx);
|
||||
try configureStage2(b, exe, ctx, tracy != null);
|
||||
}
|
||||
const tracy = b.option([]const u8, "tracy", "Enable Tracy integration. Supply path to Tracy source");
|
||||
const link_libc = b.option(bool, "force-link-libc", "Force self-hosted compiler to link libc") orelse enable_llvm;
|
||||
if (link_libc) {
|
||||
exe.linkLibC();
|
||||
test_stage2.linkLibC();
|
||||
|
|
@ -151,7 +152,9 @@ pub fn build(b: *Builder) !void {
|
|||
) catch unreachable;
|
||||
exe.addIncludeDir(tracy_path);
|
||||
exe.addCSourceFile(client_cpp, &[_][]const u8{ "-DTRACY_ENABLE=1", "-fno-sanitize=undefined" });
|
||||
exe.linkSystemLibraryName("c++");
|
||||
if (!enable_llvm) {
|
||||
exe.linkSystemLibraryName("c++");
|
||||
}
|
||||
exe.linkLibC();
|
||||
}
|
||||
|
||||
|
|
@ -344,7 +347,7 @@ fn findLLVM(b: *Builder, llvm_config_exe: []const u8) !LibraryDep {
|
|||
return result;
|
||||
}
|
||||
|
||||
fn configureStage2(b: *Builder, exe: anytype, ctx: Context) !void {
|
||||
fn configureStage2(b: *Builder, exe: anytype, ctx: Context, need_cpp_includes: bool) !void {
|
||||
exe.addIncludeDir("src");
|
||||
exe.addIncludeDir(ctx.cmake_binary_dir);
|
||||
addCppLib(b, exe, ctx.cmake_binary_dir, "zig_cpp");
|
||||
|
|
@ -377,7 +380,7 @@ fn configureStage2(b: *Builder, exe: anytype, ctx: Context) !void {
|
|||
if (exe.target.getOsTag() == .linux) {
|
||||
// First we try to static link against gcc libstdc++. If that doesn't work,
|
||||
// we fall back to -lc++ and cross our fingers.
|
||||
addCxxKnownPath(b, ctx, exe, "libstdc++.a", "") catch |err| switch (err) {
|
||||
addCxxKnownPath(b, ctx, exe, "libstdc++.a", "", need_cpp_includes) catch |err| switch (err) {
|
||||
error.RequiredLibraryNotFound => {
|
||||
exe.linkSystemLibrary("c++");
|
||||
},
|
||||
|
|
@ -386,12 +389,12 @@ fn configureStage2(b: *Builder, exe: anytype, ctx: Context) !void {
|
|||
|
||||
exe.linkSystemLibrary("pthread");
|
||||
} else if (exe.target.isFreeBSD()) {
|
||||
try addCxxKnownPath(b, ctx, exe, "libc++.a", null);
|
||||
try addCxxKnownPath(b, ctx, exe, "libc++.a", null, need_cpp_includes);
|
||||
exe.linkSystemLibrary("pthread");
|
||||
} else if (exe.target.isDarwin()) {
|
||||
if (addCxxKnownPath(b, ctx, exe, "libgcc_eh.a", "")) {
|
||||
if (addCxxKnownPath(b, ctx, exe, "libgcc_eh.a", "", need_cpp_includes)) {
|
||||
// Compiler is GCC.
|
||||
try addCxxKnownPath(b, ctx, exe, "libstdc++.a", null);
|
||||
try addCxxKnownPath(b, ctx, exe, "libstdc++.a", null, need_cpp_includes);
|
||||
exe.linkSystemLibrary("pthread");
|
||||
// TODO LLD cannot perform this link.
|
||||
// See https://github.com/ziglang/zig/issues/1535
|
||||
|
|
@ -417,6 +420,7 @@ fn addCxxKnownPath(
|
|||
exe: anytype,
|
||||
objname: []const u8,
|
||||
errtxt: ?[]const u8,
|
||||
need_cpp_includes: bool,
|
||||
) !void {
|
||||
const path_padded = try b.exec(&[_][]const u8{
|
||||
ctx.cxx_compiler,
|
||||
|
|
@ -432,6 +436,16 @@ fn addCxxKnownPath(
|
|||
return error.RequiredLibraryNotFound;
|
||||
}
|
||||
exe.addObjectFile(path_unpadded);
|
||||
|
||||
// TODO a way to integrate with system c++ include files here
|
||||
// cc -E -Wp,-v -xc++ /dev/null
|
||||
if (need_cpp_includes) {
|
||||
// I used these temporarily for testing something but we obviously need a
|
||||
// more general purpose solution here.
|
||||
//exe.addIncludeDir("/nix/store/b3zsk4ihlpiimv3vff86bb5bxghgdzb9-gcc-9.2.0/lib/gcc/x86_64-unknown-linux-gnu/9.2.0/../../../../include/c++/9.2.0");
|
||||
//exe.addIncludeDir("/nix/store/b3zsk4ihlpiimv3vff86bb5bxghgdzb9-gcc-9.2.0/lib/gcc/x86_64-unknown-linux-gnu/9.2.0/../../../../include/c++/9.2.0/x86_64-unknown-linux-gnu");
|
||||
//exe.addIncludeDir("/nix/store/b3zsk4ihlpiimv3vff86bb5bxghgdzb9-gcc-9.2.0/lib/gcc/x86_64-unknown-linux-gnu/9.2.0/../../../../include/c++/9.2.0/backward");
|
||||
}
|
||||
}
|
||||
|
||||
const Context = struct {
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ const liveness = @import("liveness.zig");
|
|||
const build_options = @import("build_options");
|
||||
const LibCInstallation = @import("libc_installation.zig").LibCInstallation;
|
||||
const glibc = @import("glibc.zig");
|
||||
const libunwind = @import("libunwind.zig");
|
||||
const fatal = @import("main.zig").fatal;
|
||||
const Module = @import("Module.zig");
|
||||
const Cache = @import("Cache.zig");
|
||||
|
|
@ -63,7 +64,7 @@ libcxx_static_lib: ?[]const u8 = null,
|
|||
libcxxabi_static_lib: ?[]const u8 = null,
|
||||
/// Populated when we build libunwind.a. A WorkItem to build this is placed in the queue
|
||||
/// and resolved before calling linker.flush().
|
||||
libunwind_static_lib: ?[]const u8 = null,
|
||||
libunwind_static_lib: ?CRTFile = null,
|
||||
/// Populated when we build c.a. A WorkItem to build this is placed in the queue
|
||||
/// and resolved before calling linker.flush().
|
||||
libc_static_lib: ?[]const u8 = null,
|
||||
|
|
@ -115,6 +116,8 @@ const WorkItem = union(enum) {
|
|||
glibc_crt_file: glibc.CRTFile,
|
||||
/// all of the glibc shared objects
|
||||
glibc_shared_objects,
|
||||
|
||||
libunwind: void,
|
||||
};
|
||||
|
||||
pub const CObject = struct {
|
||||
|
|
@ -206,6 +209,17 @@ pub const Directory = struct {
|
|||
/// `null` means cwd.
|
||||
path: ?[]const u8,
|
||||
handle: std.fs.Dir,
|
||||
|
||||
pub fn join(self: Directory, allocator: *Allocator, paths: []const []const u8) ![]u8 {
|
||||
if (self.path) |p| {
|
||||
// TODO clean way to do this with only 1 allocation
|
||||
const part2 = try std.fs.path.join(allocator, paths);
|
||||
defer allocator.free(part2);
|
||||
return std.fs.path.join(allocator, &[_][]const u8{ p, part2 });
|
||||
} else {
|
||||
return std.fs.path.join(allocator, paths);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
pub const EmitLoc = struct {
|
||||
|
|
@ -274,9 +288,6 @@ pub const InitOptions = struct {
|
|||
version: ?std.builtin.Version = null,
|
||||
libc_installation: ?*const LibCInstallation = null,
|
||||
machine_code_model: std.builtin.CodeModel = .default,
|
||||
/// TODO Once self-hosted Zig is capable enough, we can remove this special-case
|
||||
/// hack in favor of more general compilation options.
|
||||
stage1_is_dummy_so: bool = false,
|
||||
};
|
||||
|
||||
pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {
|
||||
|
|
@ -636,6 +647,9 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {
|
|||
if (comp.wantBuildGLibCFromSource()) {
|
||||
try comp.addBuildingGLibCWorkItems();
|
||||
}
|
||||
if (comp.wantBuildLibUnwindFromSource()) {
|
||||
try comp.work_queue.writeItem(.{ .libunwind = {} });
|
||||
}
|
||||
|
||||
return comp;
|
||||
}
|
||||
|
|
@ -656,6 +670,10 @@ pub fn destroy(self: *Compilation) void {
|
|||
self.crt_files.deinit(gpa);
|
||||
}
|
||||
|
||||
if (self.libunwind_static_lib) |*unwind_crt_file| {
|
||||
unwind_crt_file.deinit(gpa);
|
||||
}
|
||||
|
||||
for (self.c_object_table.items()) |entry| {
|
||||
entry.key.destroy(gpa);
|
||||
}
|
||||
|
|
@ -936,6 +954,12 @@ pub fn performAllTheWork(self: *Compilation) error{OutOfMemory}!void {
|
|||
fatal("unable to build glibc shared objects: {}", .{@errorName(err)});
|
||||
};
|
||||
},
|
||||
.libunwind => {
|
||||
libunwind.buildStaticLib(self) catch |err| {
|
||||
// TODO Expose this as a normal compile error rather than crashing here.
|
||||
fatal("unable to build libunwind: {}", .{@errorName(err)});
|
||||
};
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -1597,3 +1621,13 @@ fn wantBuildGLibCFromSource(comp: *Compilation) bool {
|
|||
comp.bin_file.options.libc_installation == null and
|
||||
comp.bin_file.options.target.isGnuLibC();
|
||||
}
|
||||
|
||||
fn wantBuildLibUnwindFromSource(comp: *Compilation) bool {
|
||||
const is_exe_or_dyn_lib = switch (comp.bin_file.options.output_mode) {
|
||||
.Obj => false,
|
||||
.Lib => comp.bin_file.options.link_mode == .Dynamic,
|
||||
.Exe => true,
|
||||
};
|
||||
return comp.bin_file.options.link_libc and is_exe_or_dyn_lib and
|
||||
comp.bin_file.options.libc_installation == null;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -751,6 +751,10 @@ pub fn buildSharedObjects(comp: *Compilation) !void {
|
|||
const tracy = trace(@src());
|
||||
defer tracy.end();
|
||||
|
||||
if (!build_options.have_llvm) {
|
||||
return error.ZigCompilerNotBuiltWithLLVMExtensions;
|
||||
}
|
||||
|
||||
var arena_allocator = std.heap.ArenaAllocator.init(comp.gpa);
|
||||
defer arena_allocator.deinit();
|
||||
const arena = &arena_allocator.allocator;
|
||||
|
|
@ -987,7 +991,6 @@ fn buildSharedLib(
|
|||
.debug_link = comp.bin_file.options.debug_link,
|
||||
.clang_passthrough_mode = comp.clang_passthrough_mode,
|
||||
.version = version,
|
||||
.stage1_is_dummy_so = true,
|
||||
.version_script = map_file_path,
|
||||
.override_soname = override_soname,
|
||||
.c_source_files = &c_source_files,
|
||||
|
|
|
|||
144
src-self-hosted/libunwind.zig
Normal file
144
src-self-hosted/libunwind.zig
Normal file
|
|
@ -0,0 +1,144 @@
|
|||
const std = @import("std");
|
||||
const path = std.fs.path;
|
||||
const assert = std.debug.assert;
|
||||
|
||||
const target_util = @import("target.zig");
|
||||
const Compilation = @import("Compilation.zig");
|
||||
const build_options = @import("build_options");
|
||||
const trace = @import("tracy.zig").trace;
|
||||
|
||||
pub fn buildStaticLib(comp: *Compilation) !void {
|
||||
const tracy = trace(@src());
|
||||
defer tracy.end();
|
||||
|
||||
if (!build_options.have_llvm) {
|
||||
return error.ZigCompilerNotBuiltWithLLVMExtensions;
|
||||
}
|
||||
|
||||
var arena_allocator = std.heap.ArenaAllocator.init(comp.gpa);
|
||||
defer arena_allocator.deinit();
|
||||
const arena = &arena_allocator.allocator;
|
||||
|
||||
const root_name = "unwind";
|
||||
const output_mode = .Lib;
|
||||
const link_mode = .Static;
|
||||
const target = comp.getTarget();
|
||||
const basename = try std.zig.binNameAlloc(arena, root_name, target, output_mode, link_mode, null);
|
||||
|
||||
const emit_bin = Compilation.EmitLoc{
|
||||
.directory = null, // Put it in the cache directory.
|
||||
.basename = basename,
|
||||
};
|
||||
|
||||
const unwind_src_list = [_][]const u8{
|
||||
"libunwind" ++ path.sep_str ++ "src" ++ path.sep_str ++ "libunwind.cpp",
|
||||
"libunwind" ++ path.sep_str ++ "src" ++ path.sep_str ++ "Unwind-EHABI.cpp",
|
||||
"libunwind" ++ path.sep_str ++ "src" ++ path.sep_str ++ "Unwind-seh.cpp",
|
||||
"libunwind" ++ path.sep_str ++ "src" ++ path.sep_str ++ "UnwindLevel1.c",
|
||||
"libunwind" ++ path.sep_str ++ "src" ++ path.sep_str ++ "UnwindLevel1-gcc-ext.c",
|
||||
"libunwind" ++ path.sep_str ++ "src" ++ path.sep_str ++ "Unwind-sjlj.c",
|
||||
"libunwind" ++ path.sep_str ++ "src" ++ path.sep_str ++ "UnwindRegistersRestore.S",
|
||||
"libunwind" ++ path.sep_str ++ "src" ++ path.sep_str ++ "UnwindRegistersSave.S",
|
||||
};
|
||||
|
||||
var c_source_files: [unwind_src_list.len]Compilation.CSourceFile = undefined;
|
||||
for (unwind_src_list) |unwind_src, i| {
|
||||
var cflags = std.ArrayList([]const u8).init(arena);
|
||||
|
||||
switch (Compilation.classifyFileExt(unwind_src)) {
|
||||
.c => {
|
||||
try cflags.append("-std=c99");
|
||||
},
|
||||
.cpp => {
|
||||
try cflags.appendSlice(&[_][]const u8{
|
||||
"-fno-rtti",
|
||||
"-I",
|
||||
try comp.zig_lib_directory.join(arena, &[_][]const u8{ "libcxx", "include" }),
|
||||
});
|
||||
},
|
||||
.assembly => {},
|
||||
else => unreachable, // You can see the entire list of files just above.
|
||||
}
|
||||
try cflags.append("-I");
|
||||
try cflags.append(try comp.zig_lib_directory.join(arena, &[_][]const u8{ "libunwind", "include" }));
|
||||
if (target_util.supports_fpic(target)) {
|
||||
try cflags.append("-fPIC");
|
||||
}
|
||||
try cflags.append("-D_LIBUNWIND_DISABLE_VISIBILITY_ANNOTATIONS");
|
||||
try cflags.append("-Wa,--noexecstack");
|
||||
|
||||
// This is intentionally always defined because the macro definition means, should it only
|
||||
// build for the target specified by compiler defines. Since we pass -target the compiler
|
||||
// defines will be correct.
|
||||
try cflags.append("-D_LIBUNWIND_IS_NATIVE_ONLY");
|
||||
|
||||
if (comp.bin_file.options.optimize_mode == .Debug) {
|
||||
try cflags.append("-D_DEBUG");
|
||||
}
|
||||
if (comp.bin_file.options.single_threaded) {
|
||||
try cflags.append("-D_LIBUNWIND_HAS_NO_THREADS");
|
||||
}
|
||||
try cflags.append("-Wno-bitwise-conditional-parentheses");
|
||||
|
||||
c_source_files[i] = .{
|
||||
.src_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{unwind_src}),
|
||||
.extra_flags = cflags.items,
|
||||
};
|
||||
}
|
||||
const sub_compilation = try Compilation.create(comp.gpa, .{
|
||||
// TODO use the global cache directory here
|
||||
.zig_cache_directory = comp.zig_cache_directory,
|
||||
.zig_lib_directory = comp.zig_lib_directory,
|
||||
.target = target,
|
||||
.root_name = root_name,
|
||||
.root_pkg = null,
|
||||
.output_mode = output_mode,
|
||||
.rand = comp.rand,
|
||||
.libc_installation = comp.bin_file.options.libc_installation,
|
||||
.emit_bin = emit_bin,
|
||||
.optimize_mode = comp.bin_file.options.optimize_mode,
|
||||
.link_mode = link_mode,
|
||||
.want_sanitize_c = false,
|
||||
.want_stack_check = false,
|
||||
.want_valgrind = false,
|
||||
.want_pic = comp.bin_file.options.pic,
|
||||
.emit_h = null,
|
||||
.strip = comp.bin_file.options.strip,
|
||||
.is_native_os = comp.bin_file.options.is_native_os,
|
||||
.self_exe_path = comp.self_exe_path,
|
||||
.c_source_files = &c_source_files,
|
||||
.debug_cc = comp.debug_cc,
|
||||
.debug_link = comp.bin_file.options.debug_link,
|
||||
.clang_passthrough_mode = comp.clang_passthrough_mode,
|
||||
.link_libc = true,
|
||||
});
|
||||
defer sub_compilation.destroy();
|
||||
|
||||
try updateSubCompilation(sub_compilation);
|
||||
|
||||
assert(comp.libunwind_static_lib == null);
|
||||
comp.libunwind_static_lib = Compilation.CRTFile{
|
||||
.full_object_path = try sub_compilation.bin_file.options.directory.join(comp.gpa, &[_][]const u8{basename}),
|
||||
.lock = sub_compilation.bin_file.toOwnedLock(),
|
||||
};
|
||||
}
|
||||
|
||||
fn updateSubCompilation(sub_compilation: *Compilation) !void {
|
||||
try sub_compilation.update();
|
||||
|
||||
// Look for compilation errors in this sub_compilation
|
||||
var errors = try sub_compilation.getAllErrorsAlloc();
|
||||
defer errors.deinit(sub_compilation.gpa);
|
||||
|
||||
if (errors.list.len != 0) {
|
||||
for (errors.list) |full_err_msg| {
|
||||
std.log.err("{}:{}:{}: {}\n", .{
|
||||
full_err_msg.src_path,
|
||||
full_err_msg.line + 1,
|
||||
full_err_msg.column + 1,
|
||||
full_err_msg.msg,
|
||||
});
|
||||
}
|
||||
return error.BuildingLibCObjectFailed;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,26 +1,28 @@
|
|||
const Elf = @This();
|
||||
|
||||
const std = @import("std");
|
||||
const mem = std.mem;
|
||||
const assert = std.debug.assert;
|
||||
const Allocator = std.mem.Allocator;
|
||||
const fs = std.fs;
|
||||
const elf = std.elf;
|
||||
const log = std.log.scoped(.link);
|
||||
const DW = std.dwarf;
|
||||
const leb128 = std.debug.leb;
|
||||
|
||||
const ir = @import("../ir.zig");
|
||||
const Module = @import("../Module.zig");
|
||||
const Compilation = @import("../Compilation.zig");
|
||||
const fs = std.fs;
|
||||
const elf = std.elf;
|
||||
const codegen = @import("../codegen.zig");
|
||||
const log = std.log.scoped(.link);
|
||||
const DW = std.dwarf;
|
||||
const trace = @import("../tracy.zig").trace;
|
||||
const leb128 = std.debug.leb;
|
||||
const Package = @import("../Package.zig");
|
||||
const Value = @import("../value.zig").Value;
|
||||
const Type = @import("../type.zig").Type;
|
||||
const link = @import("../link.zig");
|
||||
const File = link.File;
|
||||
const Elf = @This();
|
||||
const build_options = @import("build_options");
|
||||
const target_util = @import("../target.zig");
|
||||
const fatal = @import("main.zig").fatal;
|
||||
const glibc = @import("../glibc.zig");
|
||||
|
||||
const default_entry_addr = 0x8000000;
|
||||
|
||||
|
|
@ -1530,15 +1532,19 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void {
|
|||
try argv.append("-lpthread");
|
||||
}
|
||||
} else if (target.isGnuLibC()) {
|
||||
try argv.append(comp.libunwind_static_lib.?);
|
||||
// TODO here we need to iterate over the glibc libs and add the .so files to the linker line.
|
||||
std.log.warn("TODO port add_glibc_libs to stage2", .{});
|
||||
try argv.append(comp.libunwind_static_lib.?.full_object_path);
|
||||
for (glibc.libs) |lib| {
|
||||
const lib_path = try std.fmt.allocPrint(arena, "{s}{c}lib{s}.so.{d}", .{
|
||||
comp.glibc_so_files.?.dir_path, fs.path.sep, lib.name, lib.sover,
|
||||
});
|
||||
try argv.append(lib_path);
|
||||
}
|
||||
try argv.append(try comp.get_libc_crt_file(arena, "libc_nonshared.a"));
|
||||
} else if (target.isMusl()) {
|
||||
try argv.append(comp.libunwind_static_lib.?);
|
||||
try argv.append(comp.libunwind_static_lib.?.full_object_path);
|
||||
try argv.append(comp.libc_static_lib.?);
|
||||
} else if (self.base.options.link_libcpp) {
|
||||
try argv.append(comp.libunwind_static_lib.?);
|
||||
try argv.append(comp.libunwind_static_lib.?.full_object_path);
|
||||
} else {
|
||||
unreachable; // Compiler was supposed to emit an error for not being able to provide libc.
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue