Commit 17d40ecb49
Changed files (6)
src-self-hosted/link/Elf.zig
@@ -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 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 ir = @import("../ir.zig");
+const Module = @import("../Module.zig");
+const Compilation = @import("../Compilation.zig");
+const codegen = @import("../codegen.zig");
+const trace = @import("../tracy.zig").trace;
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.
}
src-self-hosted/Compilation.zig
@@ -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;
+}
src-self-hosted/glibc.zig
@@ -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,
src-self-hosted/libunwind.zig
@@ -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;
+ }
+}
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++
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 {