authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-09-09 22:24:17-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-09-09 22:29:41-07:00
loge05ecbf165931d14440e6e5d089b64788b82d14f
tree93ec39e42c1fdc0f0949d75de95363477c6a62c8
parent5746a8658ea52dee4bf310c1290d76b1255cb5ec

stage2: progress towards LLD linking

* add `zig libc` command * add `--libc` CLI and integrate it with Module and linker code * implement libc detection and paths resolution * port LLD ELF linker line construction to stage2 * integrate dynamic linker option into Module and linker code * implement default link_mode detection and error handling if user requests static when it cannot be fulfilled * integrate more linker options * implement detection of .so.X.Y.Z file extension as a shared object file. nice try, you can't fool me. * correct usage text for -dynamic and -static

7 files changed, 634 insertions(+), 64 deletions(-)

src-self-hosted/Module.zig+167-26
......@@ -24,6 +24,7 @@ const liveness = @import("liveness.zig");
2424const astgen = @import("astgen.zig");
2525const zir_sema = @import("zir_sema.zig");
2626const build_options = @import("build_options");
27const LibCInstallation = @import("libc_installation.zig").LibCInstallation;
2728
2829/// General-purpose allocator. Used for both temporary and long-term storage.
2930gpa: *Allocator,
......@@ -82,8 +83,6 @@ next_anon_name_index: usize = 0,
8283/// contains Decls that need to be deleted if they end up having no references to them.
8384deletion_set: std.ArrayListUnmanaged(*Decl) = .{},
8485
85/// Owned by Module.
86root_name: []u8,
8786keep_source_files_loaded: bool,
8887use_clang: bool,
8988sanitize_c: bool,
......@@ -106,6 +105,19 @@ zig_cache_dir_path: []const u8,
106105libc_include_dir_list: []const []const u8,
107106rand: *std.rand.Random,
108107
108/// Populated when we build libc++.a. A WorkItem to build this is placed in the queue
109/// and resolved before calling linker.flush().
110libcxx_static_lib: ?[]const u8 = null,
111/// Populated when we build libc++abi.a. A WorkItem to build this is placed in the queue
112/// and resolved before calling linker.flush().
113libcxxabi_static_lib: ?[]const u8 = null,
114/// Populated when we build libunwind.a. A WorkItem to build this is placed in the queue
115/// and resolved before calling linker.flush().
116libunwind_static_lib: ?[]const u8 = null,
117/// Populated when we build c.a. A WorkItem to build this is placed in the queue
118/// and resolved before calling linker.flush().
119libc_static_lib: ?[]const u8 = null,
120
109121pub const InnerError = error{ OutOfMemory, AnalysisFail };
110122
111123const WorkItem = union(enum) {
......@@ -932,6 +944,7 @@ pub const InitOptions = struct {
932944 root_pkg: ?*Package,
933945 output_mode: std.builtin.OutputMode,
934946 rand: *std.rand.Random,
947 dynamic_linker: ?[]const u8 = null,
935948 bin_file_dir_path: ?[]const u8 = null,
936949 bin_file_dir: ?std.fs.Dir = null,
937950 bin_file_path: []const u8,
......@@ -941,6 +954,7 @@ pub const InitOptions = struct {
941954 optimize_mode: std.builtin.Mode = .Debug,
942955 keep_source_files_loaded: bool = false,
943956 clang_argv: []const []const u8 = &[0][]const u8{},
957 lld_argv: []const []const u8 = &[0][]const u8{},
944958 lib_dirs: []const []const u8 = &[0][]const u8{},
945959 rpath_list: []const []const u8 = &[0][]const u8{},
946960 c_source_files: []const []const u8 = &[0][]const u8{},
......@@ -957,10 +971,11 @@ pub const InitOptions = struct {
957971 use_clang: ?bool = null,
958972 rdynamic: bool = false,
959973 strip: bool = false,
974 is_native_os: bool,
975 link_eh_frame_hdr: bool = false,
960976 linker_script: ?[]const u8 = null,
961977 version_script: ?[]const u8 = null,
962978 override_soname: ?[]const u8 = null,
963 linker_optimization: ?[]const u8 = null,
964979 linker_gc_sections: ?bool = null,
965980 function_sections: ?bool = null,
966981 linker_allow_shlib_undefined: ?bool = null,
......@@ -969,8 +984,10 @@ pub const InitOptions = struct {
969984 linker_z_nodelete: bool = false,
970985 linker_z_defs: bool = false,
971986 clang_passthrough_mode: bool = false,
972 stack_size_override: u64 = 0,
987 stack_size_override: ?u64 = null,
973988 self_exe_path: ?[]const u8 = null,
989 version: std.builtin.Version = .{ .major = 0, .minor = 0, .patch = 0 },
990 libc_installation: ?*const LibCInstallation = null,
974991};
975992
976993pub fn create(gpa: *Allocator, options: InitOptions) !*Module {
......@@ -1002,6 +1019,7 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Module {
10021019 options.frameworks.len != 0 or
10031020 options.system_libs.len != 0 or
10041021 options.link_libc or options.link_libcpp or
1022 options.link_eh_frame_hdr or
10051023 options.linker_script != null or options.version_script != null)
10061024 {
10071025 break :blk true;
......@@ -1017,6 +1035,35 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Module {
10171035 break :blk false;
10181036 };
10191037
1038 const must_dynamic_link = dl: {
1039 if (target_util.cannotDynamicLink(options.target))
1040 break :dl false;
1041 if (target_util.osRequiresLibC(options.target))
1042 break :dl true;
1043 if (options.link_libc and options.target.isGnuLibC())
1044 break :dl true;
1045 if (options.system_libs.len != 0)
1046 break :dl true;
1047
1048 break :dl false;
1049 };
1050 const default_link_mode: std.builtin.LinkMode = if (must_dynamic_link) .Dynamic else .Static;
1051 const link_mode: std.builtin.LinkMode = if (options.link_mode) |lm| blk: {
1052 if (lm == .Static and must_dynamic_link) {
1053 return error.UnableToStaticLink;
1054 }
1055 break :blk lm;
1056 } else default_link_mode;
1057
1058 const libc_dirs = try detectLibCIncludeDirs(
1059 arena,
1060 options.zig_lib_dir,
1061 options.target,
1062 options.is_native_os,
1063 options.link_libc,
1064 options.libc_installation,
1065 );
1066
10201067 const bin_file = try link.File.openPath(gpa, .{
10211068 .dir = options.bin_file_dir orelse std.fs.cwd(),
10221069 .dir_path = options.bin_file_dir_path,
......@@ -1024,8 +1071,9 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Module {
10241071 .root_name = root_name,
10251072 .root_pkg = options.root_pkg,
10261073 .target = options.target,
1074 .dynamic_linker = options.dynamic_linker,
10271075 .output_mode = options.output_mode,
1028 .link_mode = options.link_mode orelse .Static,
1076 .link_mode = link_mode,
10291077 .object_format = ofmt,
10301078 .optimize_mode = options.optimize_mode,
10311079 .use_lld = use_lld,
......@@ -1039,7 +1087,22 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Module {
10391087 .lib_dirs = options.lib_dirs,
10401088 .rpath_list = options.rpath_list,
10411089 .strip = options.strip,
1090 .is_native_os = options.is_native_os,
10421091 .function_sections = options.function_sections orelse false,
1092 .allow_shlib_undefined = options.linker_allow_shlib_undefined,
1093 .bind_global_refs_locally = options.linker_bind_global_refs_locally orelse false,
1094 .z_nodelete = options.linker_z_nodelete,
1095 .z_defs = options.linker_z_defs,
1096 .stack_size_override = options.stack_size_override,
1097 .linker_script = options.linker_script,
1098 .version_script = options.version_script,
1099 .gc_sections = options.linker_gc_sections,
1100 .eh_frame_hdr = options.link_eh_frame_hdr,
1101 .rdynamic = options.rdynamic,
1102 .extra_lld_args = options.lld_argv,
1103 .override_soname = options.override_soname,
1104 .version = options.version,
1105 .libc_installation = libc_dirs.libc_installation,
10431106 });
10441107 errdefer bin_file.destroy();
10451108
......@@ -1146,13 +1209,6 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Module {
11461209 break :blk true;
11471210 };
11481211
1149 const libc_include_dir_list = try detectLibCIncludeDirs(
1150 arena,
1151 options.zig_lib_dir,
1152 options.target,
1153 options.link_libc,
1154 );
1155
11561212 const sanitize_c: bool = options.want_sanitize_c orelse switch (options.optimize_mode) {
11571213 .Debug, .ReleaseSafe => true,
11581214 .ReleaseSmall, .ReleaseFast => false,
......@@ -1163,7 +1219,6 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Module {
11631219 .arena_state = arena_allocator.state,
11641220 .zig_lib_dir = options.zig_lib_dir,
11651221 .zig_cache_dir_path = zig_cache_dir_path,
1166 .root_name = root_name,
11671222 .root_pkg = options.root_pkg,
11681223 .root_scope = root_scope,
11691224 .bin_file = bin_file,
......@@ -1174,7 +1229,7 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Module {
11741229 .c_source_files = options.c_source_files,
11751230 .cache = cache,
11761231 .self_exe_path = options.self_exe_path,
1177 .libc_include_dir_list = libc_include_dir_list,
1232 .libc_include_dir_list = libc_dirs.libc_include_dir_list,
11781233 .sanitize_c = sanitize_c,
11791234 .rand = options.rand,
11801235 .clang_passthrough_mode = options.clang_passthrough_mode,
......@@ -1544,7 +1599,10 @@ fn buildCObject(mod: *Module, c_object: *CObject) !void {
15441599 // directly to the output file.
15451600 const direct_o = mod.c_source_files.len == 1 and mod.root_pkg == null and
15461601 mod.bin_file.options.output_mode == .Obj and mod.bin_file.options.objects.len == 0;
1547 const o_basename_noext = if (direct_o) mod.root_name else mem.split(c_source_basename, ".").next().?;
1602 const o_basename_noext = if (direct_o)
1603 mod.bin_file.options.root_name
1604 else
1605 mem.split(c_source_basename, ".").next().?;
15481606 const o_basename = try std.fmt.allocPrint(arena, "{}{}", .{ o_basename_noext, mod.getTarget().oFileExt() });
15491607
15501608 // We can't know the digest until we do the C compiler invocation, so we need a temporary filename.
......@@ -1749,7 +1807,7 @@ fn addCCArgs(
17491807 try argv.append(p);
17501808 }
17511809 },
1752 .assembly, .ll, .bc, .unknown => {},
1810 .so, .assembly, .ll, .bc, .unknown => {},
17531811 }
17541812 // TODO CLI args for cpu features when compiling assembly
17551813 //for (size_t i = 0; i < g->zig_target->llvm_cpu_features_asm_len; i += 1) {
......@@ -4259,6 +4317,7 @@ pub const FileExt = enum {
42594317 ll,
42604318 bc,
42614319 assembly,
4320 so,
42624321 unknown,
42634322};
42644323
......@@ -4290,10 +4349,36 @@ pub fn classifyFileExt(filename: []const u8) FileExt {
42904349 return .assembly;
42914350 } else if (mem.endsWith(u8, filename, ".h")) {
42924351 return .h;
4293 } else {
4294 // TODO look for .so, .so.X, .so.X.Y, .so.X.Y.Z
4295 return .unknown;
4352 } else if (mem.endsWith(u8, filename, ".so")) {
4353 return .so;
4354 }
4355 // Look for .so.X, .so.X.Y, .so.X.Y.Z
4356 var it = mem.split(filename, ".");
4357 _ = it.next().?;
4358 var so_txt = it.next() orelse return .unknown;
4359 while (!mem.eql(u8, so_txt, "so")) {
4360 so_txt = it.next() orelse return .unknown;
42964361 }
4362 const n1 = it.next() orelse return .unknown;
4363 const n2 = it.next();
4364 const n3 = it.next();
4365
4366 _ = std.fmt.parseInt(u32, n1, 10) catch return .unknown;
4367 if (n2) |x| _ = std.fmt.parseInt(u32, x, 10) catch return .unknown;
4368 if (n3) |x| _ = std.fmt.parseInt(u32, x, 10) catch return .unknown;
4369 if (it.next() != null) return .unknown;
4370
4371 return .so;
4372}
4373
4374test "classifyFileExt" {
4375 std.testing.expectEqual(FileExt.cpp, classifyFileExt("foo.cc"));
4376 std.testing.expectEqual(FileExt.unknown, classifyFileExt("foo.nim"));
4377 std.testing.expectEqual(FileExt.so, classifyFileExt("foo.so"));
4378 std.testing.expectEqual(FileExt.so, classifyFileExt("foo.so.1"));
4379 std.testing.expectEqual(FileExt.so, classifyFileExt("foo.so.1.2"));
4380 std.testing.expectEqual(FileExt.so, classifyFileExt("foo.so.1.2.3"));
4381 std.testing.expectEqual(FileExt.unknown, classifyFileExt("foo.so.1.2.3~"));
42974382}
42984383
42994384fn haveFramePointer(mod: *Module) bool {
......@@ -4303,16 +4388,29 @@ fn haveFramePointer(mod: *Module) bool {
43034388 };
43044389}
43054390
4391const LibCDirs = struct {
4392 libc_include_dir_list: []const []const u8,
4393 libc_installation: ?*const LibCInstallation,
4394};
4395
43064396fn detectLibCIncludeDirs(
43074397 arena: *Allocator,
43084398 zig_lib_dir: []const u8,
43094399 target: Target,
4400 is_native_os: bool,
43104401 link_libc: bool,
4311) ![]const []const u8 {
4312 if (!link_libc) return &[0][]u8{};
4402 libc_installation: ?*const LibCInstallation,
4403) !LibCDirs {
4404 if (!link_libc) {
4405 return LibCDirs{
4406 .libc_include_dir_list = &[0][]u8{},
4407 .libc_installation = null,
4408 };
4409 }
43134410
4314 // TODO Support --libc file explicitly providing libc paths. Or not? Maybe we are better off
4315 // deleting that feature.
4411 if (libc_installation) |lci| {
4412 return detectLibCFromLibCInstallation(arena, target, lci);
4413 }
43164414
43174415 if (target_util.canBuildLibC(target)) {
43184416 const generic_name = target_util.libCGenericName(target);
......@@ -4348,9 +4446,52 @@ fn detectLibCIncludeDirs(
43484446 list[1] = generic_include_dir;
43494447 list[2] = arch_os_include_dir;
43504448 list[3] = generic_os_include_dir;
4351 return list;
4449 return LibCDirs{
4450 .libc_include_dir_list = list,
4451 .libc_installation = null,
4452 };
4453 }
4454
4455 if (is_native_os) {
4456 const libc = try arena.create(LibCInstallation);
4457 libc.* = try LibCInstallation.findNative(.{ .allocator = arena });
4458 return detectLibCFromLibCInstallation(arena, target, libc);
4459 }
4460
4461 return LibCDirs{
4462 .libc_include_dir_list = &[0][]u8{},
4463 .libc_installation = null,
4464 };
4465}
4466
4467fn detectLibCFromLibCInstallation(arena: *Allocator, target: Target, lci: *const LibCInstallation) !LibCDirs {
4468 var list = std.ArrayList([]const u8).init(arena);
4469 try list.ensureCapacity(4);
4470
4471 list.appendAssumeCapacity(lci.include_dir.?);
4472
4473 const is_redundant = mem.eql(u8, lci.sys_include_dir.?, lci.include_dir.?);
4474 if (!is_redundant) list.appendAssumeCapacity(lci.sys_include_dir.?);
4475
4476 if (target.os.tag == .windows) {
4477 if (std.fs.path.dirname(lci.include_dir.?)) |include_dir_parent| {
4478 const um_dir = try std.fs.path.join(arena, &[_][]const u8{ include_dir_parent, "um" });
4479 list.appendAssumeCapacity(um_dir);
4480
4481 const shared_dir = try std.fs.path.join(arena, &[_][]const u8{ include_dir_parent, "shared" });
4482 list.appendAssumeCapacity(shared_dir);
4483 }
43524484 }
4485 return LibCDirs{
4486 .libc_include_dir_list = list.items,
4487 .libc_installation = lci,
4488 };
4489}
43534490
4354 // TODO finish porting detect_libc from codegen.cpp
4355 return error.LibCDetectionUnimplemented;
4491pub fn get_libc_crt_file(mod: *Module, arena: *Allocator, basename: []const u8) ![]const u8 {
4492 // TODO port support for building crt files from stage1
4493 const lci = mod.bin_file.options.libc_installation orelse return error.LibCInstallationNotAvailable;
4494 const crt_dir_path = lci.crt_dir orelse return error.LibCInstallationMissingCRTDir;
4495 const full_path = try std.fs.path.join(arena, &[_][]const u8{ crt_dir_path, basename });
4496 return full_path;
43564497}
src-self-hosted/libc_installation.zig+2
......@@ -11,6 +11,8 @@ const is_gnu = Target.current.isGnu();
1111
1212usingnamespace @import("windows_sdk.zig");
1313
14// TODO Rework this abstraction to use std.log instead of taking a stderr stream.
15
1416/// See the render function implementation for documentation of the fields.
1517pub const LibCInstallation = struct {
1618 include_dir: ?[]const u8 = null,
src-self-hosted/link.zig+19
......@@ -6,6 +6,7 @@ const trace = @import("tracy.zig").trace;
66const Package = @import("Package.zig");
77const Type = @import("type.zig").Type;
88const build_options = @import("build_options");
9const LibCInstallation = @import("libc_installation.zig").LibCInstallation;
910
1011pub const producer_string = if (std.builtin.is_test) "zig test" else "zig " ++ build_options.version;
1112
......@@ -23,6 +24,7 @@ pub const Options = struct {
2324 optimize_mode: std.builtin.Mode,
2425 root_name: []const u8,
2526 root_pkg: ?*const Package,
27 dynamic_linker: ?[]const u8 = null,
2628 /// Used for calculating how much space to reserve for symbols in case the binary file
2729 /// does not already have a symbol table.
2830 symbol_count_hint: u64 = 32,
......@@ -30,6 +32,7 @@ pub const Options = struct {
3032 /// the binary file does not already have such a section.
3133 program_code_size_hint: u64 = 256 * 1024,
3234 entry_addr: ?u64 = null,
35 stack_size_override: ?u64 = null,
3336 /// Set to `true` to omit debug info.
3437 strip: bool = false,
3538 /// If this is true then this link code is responsible for outputting an object
......@@ -44,6 +47,19 @@ pub const Options = struct {
4447 link_libc: bool = false,
4548 link_libcpp: bool = false,
4649 function_sections: bool = false,
50 eh_frame_hdr: bool = false,
51 rdynamic: bool = false,
52 z_nodelete: bool = false,
53 z_defs: bool = false,
54 bind_global_refs_locally: bool,
55 is_native_os: bool,
56 gc_sections: ?bool = null,
57 allow_shlib_undefined: ?bool = null,
58 linker_script: ?[]const u8 = null,
59 version_script: ?[]const u8 = null,
60 override_soname: ?[]const u8 = null,
61 /// Extra args passed directly to LLD. Ignored when not linking with LLD.
62 extra_lld_args: []const []const u8 = &[0][]const u8,
4763
4864 objects: []const []const u8 = &[0][]const u8{},
4965 framework_dirs: []const []const u8 = &[0][]const u8{},
......@@ -52,6 +68,9 @@ pub const Options = struct {
5268 lib_dirs: []const []const u8 = &[0][]const u8{},
5369 rpath_list: []const []const u8 = &[0][]const u8{},
5470
71 version: std.builtin.Version,
72 libc_installation: ?*const LibCInstallation,
73
5574 pub fn effectiveOutputMode(options: Options) std.builtin.OutputMode {
5675 return if (options.use_lld) .Obj else options.output_mode;
5776 }
src-self-hosted/link/Elf.zig+304-6
......@@ -18,6 +18,7 @@ const link = @import("../link.zig");
1818const File = link.File;
1919const Elf = @This();
2020const build_options = @import("build_options");
21const target_util = @import("../target.zig");
2122
2223const default_entry_addr = 0x8000000;
2324
......@@ -709,12 +710,7 @@ pub const abbrev_parameter = 6;
709710
710711pub fn flush(self: *Elf, module: *Module) !void {
711712 if (build_options.have_llvm and self.base.options.use_lld) {
712 // If there is no Zig code to compile, then we should skip flushing the output file because it
713 // will not be part of the linker line anyway.
714 if (module.root_pkg != null) {
715 try self.flushInner(module);
716 }
717 std.debug.print("TODO create an LLD command line and invoke it\n", .{});
713 return self.linkWithLLD(module);
718714 } else {
719715 switch (self.base.options.effectiveOutputMode()) {
720716 .Exe, .Obj => {},
......@@ -1202,6 +1198,275 @@ fn flushInner(self: *Elf, module: *Module) !void {
12021198 assert(!self.debug_strtab_dirty);
12031199}
12041200
1201fn linkWithLLD(self: *Elf, module: *Module) !void {
1202 // If there is no Zig code to compile, then we should skip flushing the output file because it
1203 // will not be part of the linker line anyway.
1204 if (module.root_pkg != null) {
1205 try self.flushInner(module);
1206 }
1207 var arena_allocator = std.heap.ArenaAllocator.init(self.base.allocator);
1208 defer arena_allocator.deinit();
1209 const arena = &arena_allocator.allocator;
1210
1211 const target = self.base.options.target;
1212 const is_obj = self.base.options.output_mode == .Obj;
1213
1214 // Create an LLD command line and invoke it.
1215 var argv = std.ArrayList([]const u8).init(self.base.allocator);
1216 defer argv.deinit();
1217 // Even though we're calling LLD as a library it thinks the first argument is its own exe name.
1218 try argv.append("lld");
1219 if (is_obj) {
1220 try argv.append("-r");
1221 }
1222 if (self.base.options.output_mode == .Lib and
1223 self.base.options.link_mode == .Static and
1224 !target.isWasm())
1225 {
1226 // TODO port the code from link.cpp
1227 return error.TODOMakeArchive;
1228 }
1229 const link_in_crt = self.base.options.link_libc and self.base.options.output_mode == .Exe;
1230
1231 try argv.append("-error-limit=0");
1232
1233 if (self.base.options.output_mode == .Exe) {
1234 try argv.append("-z");
1235 const stack_size = self.base.options.stack_size_override orelse 16777216;
1236 const arg = try std.fmt.allocPrint(arena, "stack-size={}", .{stack_size});
1237 try argv.append(arg);
1238 }
1239
1240 if (self.base.options.linker_script) |linker_script| {
1241 try argv.append("-T");
1242 try argv.append(linker_script);
1243 }
1244
1245 const gc_sections = self.base.options.gc_sections orelse !is_obj;
1246 if (gc_sections) {
1247 try argv.append("--gc-sections");
1248 }
1249
1250 if (self.base.options.eh_frame_hdr) {
1251 try argv.append("--eh-frame-hdr");
1252 }
1253
1254 if (self.base.options.rdynamic) {
1255 try argv.append("--export-dynamic");
1256 }
1257
1258 try argv.appendSlice(self.base.options.extra_lld_args);
1259
1260 if (self.base.options.z_nodelete) {
1261 try argv.append("-z");
1262 try argv.append("nodelete");
1263 }
1264 if (self.base.options.z_defs) {
1265 try argv.append("-z");
1266 try argv.append("defs");
1267 }
1268
1269 if (getLDMOption(target)) |ldm| {
1270 // Any target ELF will use the freebsd osabi if suffixed with "_fbsd".
1271 const arg = if (target.os.tag == .freebsd)
1272 try std.fmt.allocPrint(arena, "{}_fbsd", .{ldm})
1273 else
1274 ldm;
1275 try argv.append("-m");
1276 try argv.append(arg);
1277 }
1278
1279 const is_lib = self.base.options.output_mode == .Lib;
1280 const is_dyn_lib = self.base.options.link_mode == .Dynamic and is_lib;
1281 if (self.base.options.link_mode == .Static) {
1282 if (target.cpu.arch.isARM() or target.cpu.arch.isThumb()) {
1283 try argv.append("-Bstatic");
1284 } else {
1285 try argv.append("-static");
1286 }
1287 } else if (is_dyn_lib) {
1288 try argv.append("-shared");
1289 }
1290
1291 if (target_util.requiresPIE(target) and self.base.options.output_mode == .Exe) {
1292 try argv.append("-pie");
1293 }
1294
1295 const full_out_path = if (self.base.options.dir_path) |dir_path|
1296 try std.fs.path.join(arena, &[_][]const u8{dir_path, self.base.options.sub_path})
1297 else
1298 self.base.options.sub_path;
1299 try argv.append("-o");
1300 try argv.append(full_out_path);
1301
1302 if (link_in_crt) {
1303 const crt1o: []const u8 = o: {
1304 if (target.os.tag == .netbsd) {
1305 break :o "crt0.o";
1306 } else if (target.isAndroid()) {
1307 if (self.base.options.link_mode == .Dynamic) {
1308 break :o "crtbegin_dynamic.o";
1309 } else {
1310 break :o "crtbegin_static.o";
1311 }
1312 } else if (self.base.options.link_mode == .Static) {
1313 break :o "crt1.o";
1314 } else {
1315 break :o "Scrt1.o";
1316 }
1317 };
1318 try argv.append(try module.get_libc_crt_file(arena, crt1o));
1319 if (target_util.libc_needs_crti_crtn(target)) {
1320 try argv.append(try module.get_libc_crt_file(arena, "crti.o"));
1321 }
1322 }
1323
1324 // TODO rpaths
1325 //for (size_t i = 0; i < g->rpath_list.length; i += 1) {
1326 // Buf *rpath = g->rpath_list.at(i);
1327 // add_rpath(lj, rpath);
1328 //}
1329 //if (g->each_lib_rpath) {
1330 // for (size_t i = 0; i < g->lib_dirs.length; i += 1) {
1331 // const char *lib_dir = g->lib_dirs.at(i);
1332 // for (size_t i = 0; i < g->link_libs_list.length; i += 1) {
1333 // LinkLib *link_lib = g->link_libs_list.at(i);
1334 // if (buf_eql_str(link_lib->name, "c")) {
1335 // continue;
1336 // }
1337 // bool does_exist;
1338 // Buf *test_path = buf_sprintf("%s/lib%s.so", lib_dir, buf_ptr(link_lib->name));
1339 // if (os_file_exists(test_path, &does_exist) != ErrorNone) {
1340 // zig_panic("link: unable to check if file exists: %s", buf_ptr(test_path));
1341 // }
1342 // if (does_exist) {
1343 // add_rpath(lj, buf_create_from_str(lib_dir));
1344 // break;
1345 // }
1346 // }
1347 // }
1348 //}
1349
1350 for (self.base.options.lib_dirs) |lib_dir| {
1351 try argv.append("-L");
1352 try argv.append(lib_dir);
1353 }
1354
1355 if (self.base.options.link_libc) {
1356 if (self.base.options.libc_installation) |libc_installation| {
1357 try argv.append("-L");
1358 try argv.append(libc_installation.crt_dir.?);
1359 }
1360
1361 if (self.base.options.link_mode == .Dynamic and (is_dyn_lib or self.base.options.output_mode == .Exe)) {
1362 if (self.base.options.dynamic_linker) |dynamic_linker| {
1363 try argv.append("-dynamic-linker");
1364 try argv.append(dynamic_linker);
1365 }
1366 }
1367 }
1368
1369 if (is_dyn_lib) {
1370 const soname = self.base.options.override_soname orelse
1371 try std.fmt.allocPrint(arena, "lib{}.so.{}", .{self.base.options.root_name,
1372 self.base.options.version.major,});
1373 try argv.append("-soname");
1374 try argv.append(soname);
1375
1376 if (self.base.options.version_script) |version_script| {
1377 try argv.append("-version-script");
1378 try argv.append(version_script);
1379 }
1380 }
1381
1382 // Positional arguments to the linker such as object files.
1383 try argv.appendSlice(self.base.options.objects);
1384
1385 // TODO compiler-rt and libc
1386 //if (!g->is_dummy_so && (g->out_type == OutTypeExe || is_dyn_lib)) {
1387 // if (g->libc_link_lib == nullptr) {
1388 // Buf *libc_a_path = build_c(g, OutTypeLib, lj->build_dep_prog_node);
1389 // try argv.append(buf_ptr(libc_a_path));
1390 // }
1391
1392 // Buf *compiler_rt_o_path = build_compiler_rt(g, OutTypeLib, lj->build_dep_prog_node);
1393 // try argv.append(buf_ptr(compiler_rt_o_path));
1394 //}
1395
1396 // Shared libraries.
1397 try argv.ensureCapacity(argv.items.len + self.base.options.system_libs.len);
1398 for (self.base.options.system_libs) |link_lib| {
1399 // By this time, we depend on these libs being dynamically linked libraries and not static libraries
1400 // (the check for that needs to be earlier), but they could be full paths to .so files, in which
1401 // case we want to avoid prepending "-l".
1402 const ext = Module.classifyFileExt(link_lib);
1403 const arg = if (ext == .so) link_lib else try std.fmt.allocPrint(arena, "-l{}", .{link_lib});
1404 argv.appendAssumeCapacity(arg);
1405 }
1406
1407 if (!is_obj) {
1408 // libc++ dep
1409 if (self.base.options.link_libcpp) {
1410 try argv.append(module.libcxxabi_static_lib.?);
1411 try argv.append(module.libcxx_static_lib.?);
1412 }
1413
1414 // libc dep
1415 if (self.base.options.link_libc) {
1416 if (self.base.options.libc_installation != null) {
1417 if (self.base.options.link_mode == .Static) {
1418 try argv.append("--start-group");
1419 try argv.append("-lc");
1420 try argv.append("-lm");
1421 try argv.append("--end-group");
1422 } else {
1423 try argv.append("-lc");
1424 try argv.append("-lm");
1425 }
1426
1427 if (target.os.tag == .freebsd or target.os.tag == .netbsd) {
1428 try argv.append("-lpthread");
1429 }
1430 } else if (target.isGnuLibC()) {
1431 try argv.append(module.libunwind_static_lib.?);
1432 // TODO here we need to iterate over the glibc libs and add the .so files to the linker line.
1433 std.log.warn("TODO port add_glibc_libs to stage2", .{});
1434 try argv.append(try module.get_libc_crt_file(arena, "libc_nonshared.a"));
1435 } else if (target.isMusl()) {
1436 try argv.append(module.libunwind_static_lib.?);
1437 try argv.append(module.libc_static_lib.?);
1438 } else if (self.base.options.link_libcpp) {
1439 try argv.append(module.libunwind_static_lib.?);
1440 } else {
1441 unreachable; // Compiler was supposed to emit an error for not being able to provide libc.
1442 }
1443 }
1444 }
1445
1446 // crt end
1447 if (link_in_crt) {
1448 if (target.isAndroid()) {
1449 try argv.append(try module.get_libc_crt_file(arena, "crtend_android.o"));
1450 } else if (target_util.libc_needs_crti_crtn(target)) {
1451 try argv.append(try module.get_libc_crt_file(arena, "crtn.o"));
1452 }
1453 }
1454
1455 const allow_shlib_undefined = self.base.options.allow_shlib_undefined orelse !self.base.options.is_native_os;
1456 if (allow_shlib_undefined) {
1457 try argv.append("--allow-shlib-undefined");
1458 }
1459
1460 if (self.base.options.bind_global_refs_locally) {
1461 try argv.append("-Bsymbolic");
1462 }
1463
1464 for (argv.items) |arg| {
1465 std.debug.print("{} ", .{arg});
1466 }
1467 @panic("invoke LLD");
1468}
1469
12051470fn writeDwarfAddrAssumeCapacity(self: *Elf, buf: *std.ArrayList(u8), addr: u64) void {
12061471 const target_endian = self.base.options.target.cpu.arch.endian();
12071472 switch (self.ptr_width) {
......@@ -2616,3 +2881,36 @@ fn sectHeaderTo32(shdr: elf.Elf64_Shdr) elf.Elf32_Shdr {
26162881 .sh_entsize = @intCast(u32, shdr.sh_entsize),
26172882 };
26182883}
2884
2885fn getLDMOption(target: std.Target) ?[]const u8 {
2886 switch (target.cpu.arch) {
2887 .i386 => return "elf_i386",
2888 .aarch64 => return "aarch64linux",
2889 .aarch64_be => return "aarch64_be_linux",
2890 .arm, .thumb => return "armelf_linux_eabi",
2891 .armeb, .thumbeb => return "armebelf_linux_eabi",
2892 .powerpc => return "elf32ppclinux",
2893 .powerpc64 => return "elf64ppc",
2894 .powerpc64le => return "elf64lppc",
2895 .sparc, .sparcel => return "elf32_sparc",
2896 .sparcv9 => return "elf64_sparc",
2897 .mips => return "elf32btsmip",
2898 .mipsel => return "elf32ltsmip",
2899 .mips64 => return "elf64btsmip",
2900 .mips64el => return "elf64ltsmip",
2901 .s390x => return "elf64_s390",
2902 .x86_64 => {
2903 if (target.abi == .gnux32) {
2904 return "elf32_x86_64";
2905 }
2906 // Any target elf will use the freebsd osabi if suffixed with "_fbsd".
2907 if (target.os.tag == .freebsd) {
2908 return "elf_x86_64_fbsd";
2909 }
2910 return "elf_x86_64";
2911 },
2912 .riscv32 => return "elf32lriscv",
2913 .riscv64 => return "elf64lriscv",
2914 else => return null,
2915 }
2916}
src-self-hosted/main.zig+116-32
......@@ -14,6 +14,7 @@ const zir = @import("zir.zig");
1414const build_options = @import("build_options");
1515const warn = std.log.warn;
1616const introspect = @import("introspect.zig");
17const LibCInstallation = @import("libc_installation.zig").LibCInstallation;
1718
1819fn fatal(comptime format: []const u8, args: anytype) noreturn {
1920 std.log.emerg(format, args);
......@@ -33,18 +34,22 @@ const usage =
3334 \\
3435 \\Commands:
3536 \\
36 \\ build-exe [source] Create executable from source or object files
37 \\ build-lib [source] Create library from source or object files
38 \\ build-obj [source] Create object from source or assembly
39 \\ cc Use Zig as a drop-in C compiler
40 \\ c++ Use Zig as a drop-in C++ compiler
41 \\ env Print lib path, std path, compiler id and version
42 \\ fmt [source] Parse file and render in canonical zig format
43 \\ translate-c [source] Convert C code to Zig code
44 \\ targets List available compilation targets
45 \\ version Print version number and exit
46 \\ zen Print zen of zig and exit
37 \\ build-exe Create executable from source or object files
38 \\ build-lib Create library from source or object files
39 \\ build-obj Create object from source or assembly
40 \\ cc Use Zig as a drop-in C compiler
41 \\ c++ Use Zig as a drop-in C++ compiler
42 \\ env Print lib path, std path, compiler id and version
43 \\ fmt Parse file and render in canonical zig format
44 \\ libc Display native libc paths file or validate one
45 \\ translate-c Convert C code to Zig code
46 \\ targets List available compilation targets
47 \\ version Print version number and exit
48 \\ zen Print zen of zig and exit
4749 \\
50 \\General Options:
51 \\
52 \\ --help Print command-specific usage
4853 \\
4954;
5055
......@@ -126,6 +131,8 @@ pub fn main() !void {
126131 return punt_to_clang(arena, args);
127132 } else if (mem.eql(u8, cmd, "fmt")) {
128133 return cmdFmt(gpa, cmd_args);
134 } else if (mem.eql(u8, cmd, "libc")) {
135 return cmdLibC(gpa, cmd_args);
129136 } else if (mem.eql(u8, cmd, "targets")) {
130137 const info = try std.zig.system.NativeTargetInfo.detect(arena, .{});
131138 const stdout = io.getStdOut().outStream();
......@@ -184,7 +191,6 @@ const usage_build_generic =
184191 \\ ReleaseSmall Optimize for small binary, safety off
185192 \\ -fPIC Force-enable Position Independent Code
186193 \\ -fno-PIC Force-disable Position Independent Code
187 \\ --dynamic Force output to be dynamically linked
188194 \\ --strip Exclude debug symbols
189195 \\ -ofmt=[mode] Override target object format
190196 \\ elf Executable and Linking Format
......@@ -199,6 +205,7 @@ const usage_build_generic =
199205 \\ -isystem [dir] Add directory to SYSTEM include search path
200206 \\ -I[dir] Add directory to include search path
201207 \\ -D[macro]=[value] Define C [macro] to [value] (1 if [value] omitted)
208 \\ --libc [file] Provide a file which specifies libc paths
202209 \\
203210 \\Link Options:
204211 \\ -l[lib], --library [lib] Link against system library
......@@ -208,6 +215,9 @@ const usage_build_generic =
208215 \\ --version [ver] Dynamic library semver
209216 \\ -rdynamic Add all symbols to the dynamic symbol table
210217 \\ -rpath [path] Add directory to the runtime library search path
218 \\ --eh-frame-hdr Enable C++ exception handling by passing --eh-frame-hdr to linker
219 \\ -dynamic Force output to be dynamically linked
220 \\ -static Force output to be statically linked
211221 \\
212222 \\Debug Options (Zig Compiler Development):
213223 \\ -ftime-report Print timing diagnostics
......@@ -220,6 +230,14 @@ const usage_build_generic =
220230 \\
221231;
222232
233const repl_help =
234 \\Commands:
235 \\ update Detect changes to source files and update output files.
236 \\ help Print this text
237 \\ exit Quit this repl
238 \\
239;
240
223241const Emit = union(enum) {
224242 no,
225243 yes_default_path,
......@@ -275,16 +293,17 @@ pub fn buildOutputType(
275293 var version_script: ?[]const u8 = null;
276294 var disable_c_depfile = false;
277295 var override_soname: ?[]const u8 = null;
278 var linker_optimization: ?[]const u8 = null;
279296 var linker_gc_sections: ?bool = null;
280297 var linker_allow_shlib_undefined: ?bool = null;
281298 var linker_bind_global_refs_locally: ?bool = null;
282299 var linker_z_nodelete = false;
283300 var linker_z_defs = false;
284 var stack_size_override: u64 = 0;
301 var stack_size_override: ?u64 = null;
285302 var use_llvm: ?bool = null;
286303 var use_lld: ?bool = null;
287304 var use_clang: ?bool = null;
305 var link_eh_frame_hdr = false;
306 var libc_paths_file: ?[]const u8 = null;
288307
289308 var system_libs = std.ArrayList([]const u8).init(gpa);
290309 defer system_libs.deinit();
......@@ -292,6 +311,9 @@ pub fn buildOutputType(
292311 var clang_argv = std.ArrayList([]const u8).init(gpa);
293312 defer clang_argv.deinit();
294313
314 var lld_argv = std.ArrayList([]const u8).init(gpa);
315 defer lld_argv.deinit();
316
295317 var lib_dirs = std.ArrayList([]const u8).init(gpa);
296318 defer lib_dirs.deinit();
297319
......@@ -414,15 +436,11 @@ pub fn buildOutputType(
414436 fatal("unable to parse --version '{}': {}", .{ args[i], @errorName(err) });
415437 };
416438 } else if (mem.eql(u8, arg, "-target")) {
417 if (i + 1 >= args.len) {
418 fatal("expected parameter after -target", .{});
419 }
439 if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg});
420440 i += 1;
421441 target_arch_os_abi = args[i];
422442 } else if (mem.eql(u8, arg, "-mcpu")) {
423 if (i + 1 >= args.len) {
424 fatal("expected parameter after -mcpu", .{});
425 }
443 if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg});
426444 i += 1;
427445 target_mcpu = args[i];
428446 } else if (mem.startsWith(u8, arg, "-ofmt=")) {
......@@ -430,11 +448,13 @@ pub fn buildOutputType(
430448 } else if (mem.startsWith(u8, arg, "-mcpu=")) {
431449 target_mcpu = arg["-mcpu=".len..];
432450 } else if (mem.eql(u8, arg, "--dynamic-linker")) {
433 if (i + 1 >= args.len) {
434 fatal("expected parameter after --dynamic-linker", .{});
435 }
451 if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg});
436452 i += 1;
437453 target_dynamic_linker = args[i];
454 } else if (mem.eql(u8, arg, "--libc")) {
455 if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg});
456 i += 1;
457 libc_paths_file = args[i];
438458 } else if (mem.eql(u8, arg, "--watch")) {
439459 watch = true;
440460 } else if (mem.eql(u8, arg, "-ftime-report")) {
......@@ -481,6 +501,8 @@ pub fn buildOutputType(
481501 link_mode = .Static;
482502 } else if (mem.eql(u8, arg, "--strip")) {
483503 strip = true;
504 } else if (mem.eql(u8, arg, "--eh-frame-hdr")) {
505 link_eh_frame_hdr = true;
484506 } else if (mem.eql(u8, arg, "-Bsymbolic")) {
485507 linker_bind_global_refs_locally = true;
486508 } else if (mem.eql(u8, arg, "--debug-tokenize")) {
......@@ -565,7 +587,7 @@ pub fn buildOutputType(
565587 const file_ext = Module.classifyFileExt(mem.spanZ(it.only_arg));
566588 switch (file_ext) {
567589 .assembly, .c, .cpp, .ll, .bc, .h => try c_source_files.append(it.only_arg),
568 .unknown => try link_objects.append(it.only_arg),
590 .unknown, .so => try link_objects.append(it.only_arg),
569591 }
570592 },
571593 .l => {
......@@ -716,7 +738,7 @@ pub fn buildOutputType(
716738 }
717739 version_script = linker_args.items[i];
718740 } else if (mem.startsWith(u8, arg, "-O")) {
719 linker_optimization = arg;
741 try lld_argv.append(arg);
720742 } else if (mem.eql(u8, arg, "--gc-sections")) {
721743 linker_gc_sections = true;
722744 } else if (mem.eql(u8, arg, "--no-gc-sections")) {
......@@ -994,10 +1016,21 @@ pub fn buildOutputType(
9941016 };
9951017 var default_prng = std.rand.DefaultPrng.init(random_seed);
9961018
1019 var libc_installation: ?LibCInstallation = null;
1020 defer if (libc_installation) |*l| l.deinit(gpa);
1021
1022 if (libc_paths_file) |paths_file| {
1023 libc_installation = LibCInstallation.parse(gpa, paths_file, io.getStdErr().writer()) catch |err| {
1024 fatal("unable to parse libc paths file: {}", .{@errorName(err)});
1025 };
1026 }
1027
9971028 const module = Module.create(gpa, .{
9981029 .zig_lib_dir = zig_lib_dir,
9991030 .root_name = root_name,
10001031 .target = target_info.target,
1032 .is_native_os = cross_target.isNativeOs(),
1033 .dynamic_linker = target_info.dynamic_linker.get(),
10011034 .output_mode = output_mode,
10021035 .root_pkg = root_pkg,
10031036 .bin_file_dir_path = null,
......@@ -1008,6 +1041,7 @@ pub fn buildOutputType(
10081041 .optimize_mode = build_mode,
10091042 .keep_source_files_loaded = zir_out_path != null,
10101043 .clang_argv = clang_argv.items,
1044 .lld_argv = lld_argv.items,
10111045 .lib_dirs = lib_dirs.items,
10121046 .rpath_list = rpath_list.items,
10131047 .c_source_files = c_source_files.items,
......@@ -1028,17 +1062,19 @@ pub fn buildOutputType(
10281062 .version_script = version_script,
10291063 .disable_c_depfile = disable_c_depfile,
10301064 .override_soname = override_soname,
1031 .linker_optimization = linker_optimization,
10321065 .linker_gc_sections = linker_gc_sections,
10331066 .linker_allow_shlib_undefined = linker_allow_shlib_undefined,
10341067 .linker_bind_global_refs_locally = linker_bind_global_refs_locally,
10351068 .linker_z_nodelete = linker_z_nodelete,
10361069 .linker_z_defs = linker_z_defs,
1070 .link_eh_frame_hdr = link_eh_frame_hdr,
10371071 .stack_size_override = stack_size_override,
10381072 .strip = strip,
10391073 .self_exe_path = self_exe_path,
10401074 .rand = &default_prng.random,
10411075 .clang_passthrough_mode = arg_mode != .build,
1076 .version = version,
1077 .libc_installation = if (libc_installation) |*lci| lci else null,
10421078 }) catch |err| {
10431079 fatal("unable to create module: {}", .{@errorName(err)});
10441080 };
......@@ -1116,16 +1152,64 @@ fn updateModule(gpa: *Allocator, module: *Module, zir_out_path: ?[]const u8) !vo
11161152 }
11171153}
11181154
1119const repl_help =
1120 \\Commands:
1121 \\ update Detect changes to source files and update output files.
1122 \\ help Print this text
1123 \\ exit Quit this repl
1155pub const usage_libc =
1156 \\Usage: zig libc
1157 \\
1158 \\ Detect the native libc installation and print the resulting
1159 \\ paths to stdout. You can save this into a file and then edit
1160 \\ the paths to create a cross compilation libc kit. Then you
1161 \\ can pass `--libc [file]` for Zig to use it.
1162 \\
1163 \\Usage: zig libc [paths_file]
1164 \\
1165 \\ Parse a libc installation text file and validate it.
11241166 \\
11251167;
11261168
1169pub fn cmdLibC(gpa: *Allocator, args: []const []const u8) !void {
1170 var input_file: ?[]const u8 = null;
1171 {
1172 var i: usize = 0;
1173 while (i < args.len) : (i += 1) {
1174 const arg = args[i];
1175 if (mem.startsWith(u8, arg, "-")) {
1176 if (mem.eql(u8, arg, "--help")) {
1177 const stdout = io.getStdOut().writer();
1178 try stdout.writeAll(usage_libc);
1179 process.exit(0);
1180 } else {
1181 fatal("unrecognized parameter: '{}'", .{arg});
1182 }
1183 } else if (input_file != null) {
1184 fatal("unexpected extra parameter: '{}'", .{arg});
1185 } else {
1186 input_file = arg;
1187 }
1188 }
1189 }
1190 if (input_file) |libc_file| {
1191 const stderr = std.io.getStdErr().writer();
1192 var libc = LibCInstallation.parse(gpa, libc_file, stderr) catch |err| {
1193 fatal("unable to parse libc file: {}", .{@errorName(err)});
1194 };
1195 defer libc.deinit(gpa);
1196 } else {
1197 var libc = LibCInstallation.findNative(.{
1198 .allocator = gpa,
1199 .verbose = true,
1200 }) catch |err| {
1201 fatal("unable to detect native libc: {}", .{@errorName(err)});
1202 };
1203 defer libc.deinit(gpa);
1204
1205 var bos = io.bufferedOutStream(io.getStdOut().writer());
1206 try libc.render(bos.writer());
1207 try bos.flush();
1208 }
1209}
1210
11271211pub const usage_fmt =
1128 \\usage: zig fmt [file]...
1212 \\Usage: zig fmt [file]...
11291213 \\
11301214 \\ Formats the input files and modifies them in-place.
11311215 \\ Arguments can be files or directories, which are searched
src-self-hosted/target.zig+25
......@@ -109,3 +109,28 @@ pub fn canBuildLibC(target: std.Target) bool {
109109 }
110110 return false;
111111}
112
113pub fn cannotDynamicLink(target: std.Target) bool {
114 return switch (target.os.tag) {
115 .freestanding, .other => true,
116 else => false,
117 };
118}
119
120pub fn osRequiresLibC(target: std.Target) bool {
121 // On Darwin, we always link libSystem which contains libc.
122 // Similarly on FreeBSD and NetBSD we always link system libc
123 // since this is the stable syscall interface.
124 return switch (target.os.tag) {
125 .freebsd, .netbsd, .dragonfly, .macosx, .ios, .watchos, .tvos => true,
126 else => false,
127 };
128}
129
130pub fn requiresPIE(target: std.Target) bool {
131 return target.isAndroid();
132}
133
134pub fn libc_needs_crti_crtn(target: std.Target) bool {
135 return !(target.cpu.arch.isRISCV() or target.isAndroid());
136}
src-self-hosted/test.zig+1
......@@ -472,6 +472,7 @@ pub const TestContext = struct {
472472 .root_pkg = root_pkg,
473473 .keep_source_files_loaded = true,
474474 .object_format = ofmt,
475 .is_native_os = case.target.isNativeOs(),
475476 });
476477 defer module.destroy();
477478