authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-09-16 17:17:37-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-09-16 17:18:13-07:00
log17d40ecb4964dbba0f3b1482f2c68253cfc69edf
tree346e786cfed2a85c19d092ba67657df21a09a561
parent17f094ec5bce9737f733de08ce0ca049f6d2a186

stage2: building libunwind.a

and put glibc shared objects on the elf linker line

6 files changed, 234 insertions(+), 32 deletions(-)

BRANCH_TODO+7-6
......@@ -1,7 +1,10 @@
1 * libunwind
2 * stage1 C++ code integration
1 * make sure zig cc works
2 - using it as a preprocessor (-E)
3 - @breakpoint(); // TODO the first arg is empty string right? skip past that.
4 - try building some software
35 * support rpaths in ELF linker code
46 * build & link against compiler-rt
7 - stage1 C++ code integration
58 * build & link againstn freestanding libc
69 * add CLI support for a way to pass extra flags to c source files
710 * implement the workaround for using LLVM to detect native CPU features
......@@ -12,10 +15,6 @@
1215 * port the stage1 os.cpp code that raises the open fd limit
1316 * use global zig-cache dir for crt files
1417 * `zig translate-c`
15 * make sure zig cc works
16 - using it as a preprocessor (-E)
17 - @breakpoint(); // TODO the first arg is empty string right? skip past that.
18 - try building some software
1918 * MachO LLD linking
2019 * COFF LLD linking
2120 * WASM LLD linking
......@@ -54,3 +53,5 @@
5453 - make it possible for Package to not openDir and reference already existing resources.
5554 * rename src/ to src/stage1/
5655 * rename src-self-hosted/ to src/
56 * improve Directory.join to only use 1 allocation in a clean way.
57 * tracy builds with lc++
build.zig+23-9
......@@ -73,6 +73,9 @@ pub fn build(b: *Builder) !void {
7373 if (only_install_lib_files)
7474 return;
7575
76 const tracy = b.option([]const u8, "tracy", "Enable Tracy integration. Supply path to Tracy source");
77 const link_libc = b.option(bool, "force-link-libc", "Force self-hosted compiler to link libc") orelse enable_llvm;
78
7679 var exe = b.addExecutable("zig", "src-self-hosted/main.zig");
7780 exe.install();
7881 exe.setBuildMode(mode);
......@@ -90,10 +93,8 @@ pub fn build(b: *Builder) !void {
9093 var ctx = parseConfigH(b, config_h_text);
9194 ctx.llvm = try findLLVM(b, ctx.llvm_config_exe);
9295
93 try configureStage2(b, exe, ctx);
96 try configureStage2(b, exe, ctx, tracy != null);
9497 }
95 const tracy = b.option([]const u8, "tracy", "Enable Tracy integration. Supply path to Tracy source");
96 const link_libc = b.option(bool, "force-link-libc", "Force self-hosted compiler to link libc") orelse enable_llvm;
9798 if (link_libc) {
9899 exe.linkLibC();
99100 test_stage2.linkLibC();
......@@ -151,7 +152,9 @@ pub fn build(b: *Builder) !void {
151152 ) catch unreachable;
152153 exe.addIncludeDir(tracy_path);
153154 exe.addCSourceFile(client_cpp, &[_][]const u8{ "-DTRACY_ENABLE=1", "-fno-sanitize=undefined" });
154 exe.linkSystemLibraryName("c++");
155 if (!enable_llvm) {
156 exe.linkSystemLibraryName("c++");
157 }
155158 exe.linkLibC();
156159 }
157160
......@@ -344,7 +347,7 @@ fn findLLVM(b: *Builder, llvm_config_exe: []const u8) !LibraryDep {
344347 return result;
345348}
346349
347fn configureStage2(b: *Builder, exe: anytype, ctx: Context) !void {
350fn configureStage2(b: *Builder, exe: anytype, ctx: Context, need_cpp_includes: bool) !void {
348351 exe.addIncludeDir("src");
349352 exe.addIncludeDir(ctx.cmake_binary_dir);
350353 addCppLib(b, exe, ctx.cmake_binary_dir, "zig_cpp");
......@@ -377,7 +380,7 @@ fn configureStage2(b: *Builder, exe: anytype, ctx: Context) !void {
377380 if (exe.target.getOsTag() == .linux) {
378381 // First we try to static link against gcc libstdc++. If that doesn't work,
379382 // we fall back to -lc++ and cross our fingers.
380 addCxxKnownPath(b, ctx, exe, "libstdc++.a", "") catch |err| switch (err) {
383 addCxxKnownPath(b, ctx, exe, "libstdc++.a", "", need_cpp_includes) catch |err| switch (err) {
381384 error.RequiredLibraryNotFound => {
382385 exe.linkSystemLibrary("c++");
383386 },
......@@ -386,12 +389,12 @@ fn configureStage2(b: *Builder, exe: anytype, ctx: Context) !void {
386389
387390 exe.linkSystemLibrary("pthread");
388391 } else if (exe.target.isFreeBSD()) {
389 try addCxxKnownPath(b, ctx, exe, "libc++.a", null);
392 try addCxxKnownPath(b, ctx, exe, "libc++.a", null, need_cpp_includes);
390393 exe.linkSystemLibrary("pthread");
391394 } else if (exe.target.isDarwin()) {
392 if (addCxxKnownPath(b, ctx, exe, "libgcc_eh.a", "")) {
395 if (addCxxKnownPath(b, ctx, exe, "libgcc_eh.a", "", need_cpp_includes)) {
393396 // Compiler is GCC.
394 try addCxxKnownPath(b, ctx, exe, "libstdc++.a", null);
397 try addCxxKnownPath(b, ctx, exe, "libstdc++.a", null, need_cpp_includes);
395398 exe.linkSystemLibrary("pthread");
396399 // TODO LLD cannot perform this link.
397400 // See https://github.com/ziglang/zig/issues/1535
......@@ -417,6 +420,7 @@ fn addCxxKnownPath(
417420 exe: anytype,
418421 objname: []const u8,
419422 errtxt: ?[]const u8,
423 need_cpp_includes: bool,
420424) !void {
421425 const path_padded = try b.exec(&[_][]const u8{
422426 ctx.cxx_compiler,
......@@ -432,6 +436,16 @@ fn addCxxKnownPath(
432436 return error.RequiredLibraryNotFound;
433437 }
434438 exe.addObjectFile(path_unpadded);
439
440 // TODO a way to integrate with system c++ include files here
441 // cc -E -Wp,-v -xc++ /dev/null
442 if (need_cpp_includes) {
443 // I used these temporarily for testing something but we obviously need a
444 // more general purpose solution here.
445 //exe.addIncludeDir("/nix/store/b3zsk4ihlpiimv3vff86bb5bxghgdzb9-gcc-9.2.0/lib/gcc/x86_64-unknown-linux-gnu/9.2.0/../../../../include/c++/9.2.0");
446 //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");
447 //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");
448 }
435449}
436450
437451const Context = struct {
src-self-hosted/Compilation.zig+38-4
......@@ -15,6 +15,7 @@ const liveness = @import("liveness.zig");
1515const build_options = @import("build_options");
1616const LibCInstallation = @import("libc_installation.zig").LibCInstallation;
1717const glibc = @import("glibc.zig");
18const libunwind = @import("libunwind.zig");
1819const fatal = @import("main.zig").fatal;
1920const Module = @import("Module.zig");
2021const Cache = @import("Cache.zig");
......@@ -63,7 +64,7 @@ libcxx_static_lib: ?[]const u8 = null,
6364libcxxabi_static_lib: ?[]const u8 = null,
6465/// Populated when we build libunwind.a. A WorkItem to build this is placed in the queue
6566/// and resolved before calling linker.flush().
66libunwind_static_lib: ?[]const u8 = null,
67libunwind_static_lib: ?CRTFile = null,
6768/// Populated when we build c.a. A WorkItem to build this is placed in the queue
6869/// and resolved before calling linker.flush().
6970libc_static_lib: ?[]const u8 = null,
......@@ -115,6 +116,8 @@ const WorkItem = union(enum) {
115116 glibc_crt_file: glibc.CRTFile,
116117 /// all of the glibc shared objects
117118 glibc_shared_objects,
119
120 libunwind: void,
118121};
119122
120123pub const CObject = struct {
......@@ -206,6 +209,17 @@ pub const Directory = struct {
206209 /// `null` means cwd.
207210 path: ?[]const u8,
208211 handle: std.fs.Dir,
212
213 pub fn join(self: Directory, allocator: *Allocator, paths: []const []const u8) ![]u8 {
214 if (self.path) |p| {
215 // TODO clean way to do this with only 1 allocation
216 const part2 = try std.fs.path.join(allocator, paths);
217 defer allocator.free(part2);
218 return std.fs.path.join(allocator, &[_][]const u8{ p, part2 });
219 } else {
220 return std.fs.path.join(allocator, paths);
221 }
222 }
209223};
210224
211225pub const EmitLoc = struct {
......@@ -274,9 +288,6 @@ pub const InitOptions = struct {
274288 version: ?std.builtin.Version = null,
275289 libc_installation: ?*const LibCInstallation = null,
276290 machine_code_model: std.builtin.CodeModel = .default,
277 /// TODO Once self-hosted Zig is capable enough, we can remove this special-case
278 /// hack in favor of more general compilation options.
279 stage1_is_dummy_so: bool = false,
280291};
281292
282293pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {
......@@ -636,6 +647,9 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {
636647 if (comp.wantBuildGLibCFromSource()) {
637648 try comp.addBuildingGLibCWorkItems();
638649 }
650 if (comp.wantBuildLibUnwindFromSource()) {
651 try comp.work_queue.writeItem(.{ .libunwind = {} });
652 }
639653
640654 return comp;
641655}
......@@ -656,6 +670,10 @@ pub fn destroy(self: *Compilation) void {
656670 self.crt_files.deinit(gpa);
657671 }
658672
673 if (self.libunwind_static_lib) |*unwind_crt_file| {
674 unwind_crt_file.deinit(gpa);
675 }
676
659677 for (self.c_object_table.items()) |entry| {
660678 entry.key.destroy(gpa);
661679 }
......@@ -936,6 +954,12 @@ pub fn performAllTheWork(self: *Compilation) error{OutOfMemory}!void {
936954 fatal("unable to build glibc shared objects: {}", .{@errorName(err)});
937955 };
938956 },
957 .libunwind => {
958 libunwind.buildStaticLib(self) catch |err| {
959 // TODO Expose this as a normal compile error rather than crashing here.
960 fatal("unable to build libunwind: {}", .{@errorName(err)});
961 };
962 },
939963 };
940964}
941965
......@@ -1597,3 +1621,13 @@ fn wantBuildGLibCFromSource(comp: *Compilation) bool {
15971621 comp.bin_file.options.libc_installation == null and
15981622 comp.bin_file.options.target.isGnuLibC();
15991623}
1624
1625fn wantBuildLibUnwindFromSource(comp: *Compilation) bool {
1626 const is_exe_or_dyn_lib = switch (comp.bin_file.options.output_mode) {
1627 .Obj => false,
1628 .Lib => comp.bin_file.options.link_mode == .Dynamic,
1629 .Exe => true,
1630 };
1631 return comp.bin_file.options.link_libc and is_exe_or_dyn_lib and
1632 comp.bin_file.options.libc_installation == null;
1633}
src-self-hosted/glibc.zig+4-1
......@@ -751,6 +751,10 @@ pub fn buildSharedObjects(comp: *Compilation) !void {
751751 const tracy = trace(@src());
752752 defer tracy.end();
753753
754 if (!build_options.have_llvm) {
755 return error.ZigCompilerNotBuiltWithLLVMExtensions;
756 }
757
754758 var arena_allocator = std.heap.ArenaAllocator.init(comp.gpa);
755759 defer arena_allocator.deinit();
756760 const arena = &arena_allocator.allocator;
......@@ -987,7 +991,6 @@ fn buildSharedLib(
987991 .debug_link = comp.bin_file.options.debug_link,
988992 .clang_passthrough_mode = comp.clang_passthrough_mode,
989993 .version = version,
990 .stage1_is_dummy_so = true,
991994 .version_script = map_file_path,
992995 .override_soname = override_soname,
993996 .c_source_files = &c_source_files,
src-self-hosted/libunwind.zig created+144
......@@ -0,0 +1,144 @@
1const std = @import("std");
2const path = std.fs.path;
3const assert = std.debug.assert;
4
5const target_util = @import("target.zig");
6const Compilation = @import("Compilation.zig");
7const build_options = @import("build_options");
8const trace = @import("tracy.zig").trace;
9
10pub fn buildStaticLib(comp: *Compilation) !void {
11 const tracy = trace(@src());
12 defer tracy.end();
13
14 if (!build_options.have_llvm) {
15 return error.ZigCompilerNotBuiltWithLLVMExtensions;
16 }
17
18 var arena_allocator = std.heap.ArenaAllocator.init(comp.gpa);
19 defer arena_allocator.deinit();
20 const arena = &arena_allocator.allocator;
21
22 const root_name = "unwind";
23 const output_mode = .Lib;
24 const link_mode = .Static;
25 const target = comp.getTarget();
26 const basename = try std.zig.binNameAlloc(arena, root_name, target, output_mode, link_mode, null);
27
28 const emit_bin = Compilation.EmitLoc{
29 .directory = null, // Put it in the cache directory.
30 .basename = basename,
31 };
32
33 const unwind_src_list = [_][]const u8{
34 "libunwind" ++ path.sep_str ++ "src" ++ path.sep_str ++ "libunwind.cpp",
35 "libunwind" ++ path.sep_str ++ "src" ++ path.sep_str ++ "Unwind-EHABI.cpp",
36 "libunwind" ++ path.sep_str ++ "src" ++ path.sep_str ++ "Unwind-seh.cpp",
37 "libunwind" ++ path.sep_str ++ "src" ++ path.sep_str ++ "UnwindLevel1.c",
38 "libunwind" ++ path.sep_str ++ "src" ++ path.sep_str ++ "UnwindLevel1-gcc-ext.c",
39 "libunwind" ++ path.sep_str ++ "src" ++ path.sep_str ++ "Unwind-sjlj.c",
40 "libunwind" ++ path.sep_str ++ "src" ++ path.sep_str ++ "UnwindRegistersRestore.S",
41 "libunwind" ++ path.sep_str ++ "src" ++ path.sep_str ++ "UnwindRegistersSave.S",
42 };
43
44 var c_source_files: [unwind_src_list.len]Compilation.CSourceFile = undefined;
45 for (unwind_src_list) |unwind_src, i| {
46 var cflags = std.ArrayList([]const u8).init(arena);
47
48 switch (Compilation.classifyFileExt(unwind_src)) {
49 .c => {
50 try cflags.append("-std=c99");
51 },
52 .cpp => {
53 try cflags.appendSlice(&[_][]const u8{
54 "-fno-rtti",
55 "-I",
56 try comp.zig_lib_directory.join(arena, &[_][]const u8{ "libcxx", "include" }),
57 });
58 },
59 .assembly => {},
60 else => unreachable, // You can see the entire list of files just above.
61 }
62 try cflags.append("-I");
63 try cflags.append(try comp.zig_lib_directory.join(arena, &[_][]const u8{ "libunwind", "include" }));
64 if (target_util.supports_fpic(target)) {
65 try cflags.append("-fPIC");
66 }
67 try cflags.append("-D_LIBUNWIND_DISABLE_VISIBILITY_ANNOTATIONS");
68 try cflags.append("-Wa,--noexecstack");
69
70 // This is intentionally always defined because the macro definition means, should it only
71 // build for the target specified by compiler defines. Since we pass -target the compiler
72 // defines will be correct.
73 try cflags.append("-D_LIBUNWIND_IS_NATIVE_ONLY");
74
75 if (comp.bin_file.options.optimize_mode == .Debug) {
76 try cflags.append("-D_DEBUG");
77 }
78 if (comp.bin_file.options.single_threaded) {
79 try cflags.append("-D_LIBUNWIND_HAS_NO_THREADS");
80 }
81 try cflags.append("-Wno-bitwise-conditional-parentheses");
82
83 c_source_files[i] = .{
84 .src_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{unwind_src}),
85 .extra_flags = cflags.items,
86 };
87 }
88 const sub_compilation = try Compilation.create(comp.gpa, .{
89 // TODO use the global cache directory here
90 .zig_cache_directory = comp.zig_cache_directory,
91 .zig_lib_directory = comp.zig_lib_directory,
92 .target = target,
93 .root_name = root_name,
94 .root_pkg = null,
95 .output_mode = output_mode,
96 .rand = comp.rand,
97 .libc_installation = comp.bin_file.options.libc_installation,
98 .emit_bin = emit_bin,
99 .optimize_mode = comp.bin_file.options.optimize_mode,
100 .link_mode = link_mode,
101 .want_sanitize_c = false,
102 .want_stack_check = false,
103 .want_valgrind = false,
104 .want_pic = comp.bin_file.options.pic,
105 .emit_h = null,
106 .strip = comp.bin_file.options.strip,
107 .is_native_os = comp.bin_file.options.is_native_os,
108 .self_exe_path = comp.self_exe_path,
109 .c_source_files = &c_source_files,
110 .debug_cc = comp.debug_cc,
111 .debug_link = comp.bin_file.options.debug_link,
112 .clang_passthrough_mode = comp.clang_passthrough_mode,
113 .link_libc = true,
114 });
115 defer sub_compilation.destroy();
116
117 try updateSubCompilation(sub_compilation);
118
119 assert(comp.libunwind_static_lib == null);
120 comp.libunwind_static_lib = Compilation.CRTFile{
121 .full_object_path = try sub_compilation.bin_file.options.directory.join(comp.gpa, &[_][]const u8{basename}),
122 .lock = sub_compilation.bin_file.toOwnedLock(),
123 };
124}
125
126fn updateSubCompilation(sub_compilation: *Compilation) !void {
127 try sub_compilation.update();
128
129 // Look for compilation errors in this sub_compilation
130 var errors = try sub_compilation.getAllErrorsAlloc();
131 defer errors.deinit(sub_compilation.gpa);
132
133 if (errors.list.len != 0) {
134 for (errors.list) |full_err_msg| {
135 std.log.err("{}:{}:{}: {}\n", .{
136 full_err_msg.src_path,
137 full_err_msg.line + 1,
138 full_err_msg.column + 1,
139 full_err_msg.msg,
140 });
141 }
142 return error.BuildingLibCObjectFailed;
143 }
144}
src-self-hosted/link/Elf.zig+18-12
......@@ -1,26 +1,28 @@
1const Elf = @This();
2
13const std = @import("std");
24const mem = std.mem;
35const assert = std.debug.assert;
46const Allocator = std.mem.Allocator;
5const ir = @import("../ir.zig");
6const Module = @import("../Module.zig");
7const Compilation = @import("../Compilation.zig");
87const fs = std.fs;
98const elf = std.elf;
10const codegen = @import("../codegen.zig");
119const log = std.log.scoped(.link);
1210const DW = std.dwarf;
13const trace = @import("../tracy.zig").trace;
1411const leb128 = std.debug.leb;
12
13const ir = @import("../ir.zig");
14const Module = @import("../Module.zig");
15const Compilation = @import("../Compilation.zig");
16const codegen = @import("../codegen.zig");
17const trace = @import("../tracy.zig").trace;
1518const Package = @import("../Package.zig");
1619const Value = @import("../value.zig").Value;
1720const Type = @import("../type.zig").Type;
1821const link = @import("../link.zig");
1922const File = link.File;
20const Elf = @This();
2123const build_options = @import("build_options");
2224const target_util = @import("../target.zig");
23const fatal = @import("main.zig").fatal;
25const glibc = @import("../glibc.zig");
2426
2527const default_entry_addr = 0x8000000;
2628
......@@ -1530,15 +1532,19 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void {
15301532 try argv.append("-lpthread");
15311533 }
15321534 } else if (target.isGnuLibC()) {
1533 try argv.append(comp.libunwind_static_lib.?);
1534 // TODO here we need to iterate over the glibc libs and add the .so files to the linker line.
1535 std.log.warn("TODO port add_glibc_libs to stage2", .{});
1535 try argv.append(comp.libunwind_static_lib.?.full_object_path);
1536 for (glibc.libs) |lib| {
1537 const lib_path = try std.fmt.allocPrint(arena, "{s}{c}lib{s}.so.{d}", .{
1538 comp.glibc_so_files.?.dir_path, fs.path.sep, lib.name, lib.sover,
1539 });
1540 try argv.append(lib_path);
1541 }
15361542 try argv.append(try comp.get_libc_crt_file(arena, "libc_nonshared.a"));
15371543 } else if (target.isMusl()) {
1538 try argv.append(comp.libunwind_static_lib.?);
1544 try argv.append(comp.libunwind_static_lib.?.full_object_path);
15391545 try argv.append(comp.libc_static_lib.?);
15401546 } else if (self.base.options.link_libcpp) {
1541 try argv.append(comp.libunwind_static_lib.?);
1547 try argv.append(comp.libunwind_static_lib.?.full_object_path);
15421548 } else {
15431549 unreachable; // Compiler was supposed to emit an error for not being able to provide libc.
15441550 }