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");...@@ -24,6 +24,7 @@ const liveness = @import("liveness.zig");
24const astgen = @import("astgen.zig");24const astgen = @import("astgen.zig");
25const zir_sema = @import("zir_sema.zig");25const zir_sema = @import("zir_sema.zig");
26const build_options = @import("build_options");26const build_options = @import("build_options");
27const LibCInstallation = @import("libc_installation.zig").LibCInstallation;
2728
28/// General-purpose allocator. Used for both temporary and long-term storage.29/// General-purpose allocator. Used for both temporary and long-term storage.
29gpa: *Allocator,30gpa: *Allocator,
...@@ -82,8 +83,6 @@ next_anon_name_index: usize = 0,...@@ -82,8 +83,6 @@ next_anon_name_index: usize = 0,
82/// contains Decls that need to be deleted if they end up having no references to them.83/// contains Decls that need to be deleted if they end up having no references to them.
83deletion_set: std.ArrayListUnmanaged(*Decl) = .{},84deletion_set: std.ArrayListUnmanaged(*Decl) = .{},
8485
85/// Owned by Module.
86root_name: []u8,
87keep_source_files_loaded: bool,86keep_source_files_loaded: bool,
88use_clang: bool,87use_clang: bool,
89sanitize_c: bool,88sanitize_c: bool,
...@@ -106,6 +105,19 @@ zig_cache_dir_path: []const u8,...@@ -106,6 +105,19 @@ zig_cache_dir_path: []const u8,
106libc_include_dir_list: []const []const u8,105libc_include_dir_list: []const []const u8,
107rand: *std.rand.Random,106rand: *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
109pub const InnerError = error{ OutOfMemory, AnalysisFail };121pub const InnerError = error{ OutOfMemory, AnalysisFail };
110122
111const WorkItem = union(enum) {123const WorkItem = union(enum) {
...@@ -932,6 +944,7 @@ pub const InitOptions = struct {...@@ -932,6 +944,7 @@ pub const InitOptions = struct {
932 root_pkg: ?*Package,944 root_pkg: ?*Package,
933 output_mode: std.builtin.OutputMode,945 output_mode: std.builtin.OutputMode,
934 rand: *std.rand.Random,946 rand: *std.rand.Random,
947 dynamic_linker: ?[]const u8 = null,
935 bin_file_dir_path: ?[]const u8 = null,948 bin_file_dir_path: ?[]const u8 = null,
936 bin_file_dir: ?std.fs.Dir = null,949 bin_file_dir: ?std.fs.Dir = null,
937 bin_file_path: []const u8,950 bin_file_path: []const u8,
...@@ -941,6 +954,7 @@ pub const InitOptions = struct {...@@ -941,6 +954,7 @@ pub const InitOptions = struct {
941 optimize_mode: std.builtin.Mode = .Debug,954 optimize_mode: std.builtin.Mode = .Debug,
942 keep_source_files_loaded: bool = false,955 keep_source_files_loaded: bool = false,
943 clang_argv: []const []const u8 = &[0][]const u8{},956 clang_argv: []const []const u8 = &[0][]const u8{},
957 lld_argv: []const []const u8 = &[0][]const u8{},
944 lib_dirs: []const []const u8 = &[0][]const u8{},958 lib_dirs: []const []const u8 = &[0][]const u8{},
945 rpath_list: []const []const u8 = &[0][]const u8{},959 rpath_list: []const []const u8 = &[0][]const u8{},
946 c_source_files: []const []const u8 = &[0][]const u8{},960 c_source_files: []const []const u8 = &[0][]const u8{},
...@@ -957,10 +971,11 @@ pub const InitOptions = struct {...@@ -957,10 +971,11 @@ pub const InitOptions = struct {
957 use_clang: ?bool = null,971 use_clang: ?bool = null,
958 rdynamic: bool = false,972 rdynamic: bool = false,
959 strip: bool = false,973 strip: bool = false,
974 is_native_os: bool,
975 link_eh_frame_hdr: bool = false,
960 linker_script: ?[]const u8 = null,976 linker_script: ?[]const u8 = null,
961 version_script: ?[]const u8 = null,977 version_script: ?[]const u8 = null,
962 override_soname: ?[]const u8 = null,978 override_soname: ?[]const u8 = null,
963 linker_optimization: ?[]const u8 = null,
964 linker_gc_sections: ?bool = null,979 linker_gc_sections: ?bool = null,
965 function_sections: ?bool = null,980 function_sections: ?bool = null,
966 linker_allow_shlib_undefined: ?bool = null,981 linker_allow_shlib_undefined: ?bool = null,
...@@ -969,8 +984,10 @@ pub const InitOptions = struct {...@@ -969,8 +984,10 @@ pub const InitOptions = struct {
969 linker_z_nodelete: bool = false,984 linker_z_nodelete: bool = false,
970 linker_z_defs: bool = false,985 linker_z_defs: bool = false,
971 clang_passthrough_mode: bool = false,986 clang_passthrough_mode: bool = false,
972 stack_size_override: u64 = 0,987 stack_size_override: ?u64 = null,
973 self_exe_path: ?[]const u8 = null,988 self_exe_path: ?[]const u8 = null,
989 version: std.builtin.Version = .{ .major = 0, .minor = 0, .patch = 0 },
990 libc_installation: ?*const LibCInstallation = null,
974};991};
975992
976pub fn create(gpa: *Allocator, options: InitOptions) !*Module {993pub fn create(gpa: *Allocator, options: InitOptions) !*Module {
...@@ -1002,6 +1019,7 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Module {...@@ -1002,6 +1019,7 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Module {
1002 options.frameworks.len != 0 or1019 options.frameworks.len != 0 or
1003 options.system_libs.len != 0 or1020 options.system_libs.len != 0 or
1004 options.link_libc or options.link_libcpp or1021 options.link_libc or options.link_libcpp or
1022 options.link_eh_frame_hdr or
1005 options.linker_script != null or options.version_script != null)1023 options.linker_script != null or options.version_script != null)
1006 {1024 {
1007 break :blk true;1025 break :blk true;
...@@ -1017,6 +1035,35 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Module {...@@ -1017,6 +1035,35 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Module {
1017 break :blk false;1035 break :blk false;
1018 };1036 };
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
1020 const bin_file = try link.File.openPath(gpa, .{1067 const bin_file = try link.File.openPath(gpa, .{
1021 .dir = options.bin_file_dir orelse std.fs.cwd(),1068 .dir = options.bin_file_dir orelse std.fs.cwd(),
1022 .dir_path = options.bin_file_dir_path,1069 .dir_path = options.bin_file_dir_path,
...@@ -1024,8 +1071,9 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Module {...@@ -1024,8 +1071,9 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Module {
1024 .root_name = root_name,1071 .root_name = root_name,
1025 .root_pkg = options.root_pkg,1072 .root_pkg = options.root_pkg,
1026 .target = options.target,1073 .target = options.target,
1074 .dynamic_linker = options.dynamic_linker,
1027 .output_mode = options.output_mode,1075 .output_mode = options.output_mode,
1028 .link_mode = options.link_mode orelse .Static,1076 .link_mode = link_mode,
1029 .object_format = ofmt,1077 .object_format = ofmt,
1030 .optimize_mode = options.optimize_mode,1078 .optimize_mode = options.optimize_mode,
1031 .use_lld = use_lld,1079 .use_lld = use_lld,
...@@ -1039,7 +1087,22 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Module {...@@ -1039,7 +1087,22 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Module {
1039 .lib_dirs = options.lib_dirs,1087 .lib_dirs = options.lib_dirs,
1040 .rpath_list = options.rpath_list,1088 .rpath_list = options.rpath_list,
1041 .strip = options.strip,1089 .strip = options.strip,
1090 .is_native_os = options.is_native_os,
1042 .function_sections = options.function_sections orelse false,1091 .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,
1043 });1106 });
1044 errdefer bin_file.destroy();1107 errdefer bin_file.destroy();
10451108
...@@ -1146,13 +1209,6 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Module {...@@ -1146,13 +1209,6 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Module {
1146 break :blk true;1209 break :blk true;
1147 };1210 };
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
1156 const sanitize_c: bool = options.want_sanitize_c orelse switch (options.optimize_mode) {1212 const sanitize_c: bool = options.want_sanitize_c orelse switch (options.optimize_mode) {
1157 .Debug, .ReleaseSafe => true,1213 .Debug, .ReleaseSafe => true,
1158 .ReleaseSmall, .ReleaseFast => false,1214 .ReleaseSmall, .ReleaseFast => false,
...@@ -1163,7 +1219,6 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Module {...@@ -1163,7 +1219,6 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Module {
1163 .arena_state = arena_allocator.state,1219 .arena_state = arena_allocator.state,
1164 .zig_lib_dir = options.zig_lib_dir,1220 .zig_lib_dir = options.zig_lib_dir,
1165 .zig_cache_dir_path = zig_cache_dir_path,1221 .zig_cache_dir_path = zig_cache_dir_path,
1166 .root_name = root_name,
1167 .root_pkg = options.root_pkg,1222 .root_pkg = options.root_pkg,
1168 .root_scope = root_scope,1223 .root_scope = root_scope,
1169 .bin_file = bin_file,1224 .bin_file = bin_file,
...@@ -1174,7 +1229,7 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Module {...@@ -1174,7 +1229,7 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Module {
1174 .c_source_files = options.c_source_files,1229 .c_source_files = options.c_source_files,
1175 .cache = cache,1230 .cache = cache,
1176 .self_exe_path = options.self_exe_path,1231 .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,
1178 .sanitize_c = sanitize_c,1233 .sanitize_c = sanitize_c,
1179 .rand = options.rand,1234 .rand = options.rand,
1180 .clang_passthrough_mode = options.clang_passthrough_mode,1235 .clang_passthrough_mode = options.clang_passthrough_mode,
...@@ -1544,7 +1599,10 @@ fn buildCObject(mod: *Module, c_object: *CObject) !void {...@@ -1544,7 +1599,10 @@ fn buildCObject(mod: *Module, c_object: *CObject) !void {
1544 // directly to the output file.1599 // directly to the output file.
1545 const direct_o = mod.c_source_files.len == 1 and mod.root_pkg == null and1600 const direct_o = mod.c_source_files.len == 1 and mod.root_pkg == null and
1546 mod.bin_file.options.output_mode == .Obj and mod.bin_file.options.objects.len == 0;1601 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().?;
1548 const o_basename = try std.fmt.allocPrint(arena, "{}{}", .{ o_basename_noext, mod.getTarget().oFileExt() });1606 const o_basename = try std.fmt.allocPrint(arena, "{}{}", .{ o_basename_noext, mod.getTarget().oFileExt() });
15491607
1550 // We can't know the digest until we do the C compiler invocation, so we need a temporary filename.1608 // 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(...@@ -1749,7 +1807,7 @@ fn addCCArgs(
1749 try argv.append(p);1807 try argv.append(p);
1750 }1808 }
1751 },1809 },
1752 .assembly, .ll, .bc, .unknown => {},1810 .so, .assembly, .ll, .bc, .unknown => {},
1753 }1811 }
1754 // TODO CLI args for cpu features when compiling assembly1812 // TODO CLI args for cpu features when compiling assembly
1755 //for (size_t i = 0; i < g->zig_target->llvm_cpu_features_asm_len; i += 1) {1813 //for (size_t i = 0; i < g->zig_target->llvm_cpu_features_asm_len; i += 1) {
...@@ -4259,6 +4317,7 @@ pub const FileExt = enum {...@@ -4259,6 +4317,7 @@ pub const FileExt = enum {
4259 ll,4317 ll,
4260 bc,4318 bc,
4261 assembly,4319 assembly,
4320 so,
4262 unknown,4321 unknown,
4263};4322};
42644323
...@@ -4290,10 +4349,36 @@ pub fn classifyFileExt(filename: []const u8) FileExt {...@@ -4290,10 +4349,36 @@ pub fn classifyFileExt(filename: []const u8) FileExt {
4290 return .assembly;4349 return .assembly;
4291 } else if (mem.endsWith(u8, filename, ".h")) {4350 } else if (mem.endsWith(u8, filename, ".h")) {
4292 return .h;4351 return .h;
4293 } else {4352 } else if (mem.endsWith(u8, filename, ".so")) {
4294 // TODO look for .so, .so.X, .so.X.Y, .so.X.Y.Z4353 return .so;
4295 return .unknown;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;
4296 }4361 }
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~"));
4297}4382}
42984383
4299fn haveFramePointer(mod: *Module) bool {4384fn haveFramePointer(mod: *Module) bool {
...@@ -4303,16 +4388,29 @@ fn haveFramePointer(mod: *Module) bool {...@@ -4303,16 +4388,29 @@ fn haveFramePointer(mod: *Module) bool {
4303 };4388 };
4304}4389}
43054390
4391const LibCDirs = struct {
4392 libc_include_dir_list: []const []const u8,
4393 libc_installation: ?*const LibCInstallation,
4394};
4395
4306fn detectLibCIncludeDirs(4396fn detectLibCIncludeDirs(
4307 arena: *Allocator,4397 arena: *Allocator,
4308 zig_lib_dir: []const u8,4398 zig_lib_dir: []const u8,
4309 target: Target,4399 target: Target,
4400 is_native_os: bool,
4310 link_libc: bool,4401 link_libc: bool,
4311) ![]const []const u8 {4402 libc_installation: ?*const LibCInstallation,
4312 if (!link_libc) return &[0][]u8{};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 off4411 if (libc_installation) |lci| {
4315 // deleting that feature.4412 return detectLibCFromLibCInstallation(arena, target, lci);
4413 }
43164414
4317 if (target_util.canBuildLibC(target)) {4415 if (target_util.canBuildLibC(target)) {
4318 const generic_name = target_util.libCGenericName(target);4416 const generic_name = target_util.libCGenericName(target);
...@@ -4348,9 +4446,52 @@ fn detectLibCIncludeDirs(...@@ -4348,9 +4446,52 @@ fn detectLibCIncludeDirs(
4348 list[1] = generic_include_dir;4446 list[1] = generic_include_dir;
4349 list[2] = arch_os_include_dir;4447 list[2] = arch_os_include_dir;
4350 list[3] = generic_os_include_dir;4448 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 }
4352 }4484 }
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.cpp4491pub fn get_libc_crt_file(mod: *Module, arena: *Allocator, basename: []const u8) ![]const u8 {
4355 return error.LibCDetectionUnimplemented;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;
4356}4497}
src-self-hosted/libc_installation.zig+2
...@@ -11,6 +11,8 @@ const is_gnu = Target.current.isGnu();...@@ -11,6 +11,8 @@ const is_gnu = Target.current.isGnu();
1111
12usingnamespace @import("windows_sdk.zig");12usingnamespace @import("windows_sdk.zig");
1313
14// TODO Rework this abstraction to use std.log instead of taking a stderr stream.
15
14/// See the render function implementation for documentation of the fields.16/// See the render function implementation for documentation of the fields.
15pub const LibCInstallation = struct {17pub const LibCInstallation = struct {
16 include_dir: ?[]const u8 = null,18 include_dir: ?[]const u8 = null,
src-self-hosted/link.zig+19
...@@ -6,6 +6,7 @@ const trace = @import("tracy.zig").trace;...@@ -6,6 +6,7 @@ const trace = @import("tracy.zig").trace;
6const Package = @import("Package.zig");6const Package = @import("Package.zig");
7const Type = @import("type.zig").Type;7const Type = @import("type.zig").Type;
8const build_options = @import("build_options");8const build_options = @import("build_options");
9const LibCInstallation = @import("libc_installation.zig").LibCInstallation;
910
10pub const producer_string = if (std.builtin.is_test) "zig test" else "zig " ++ build_options.version;11pub const producer_string = if (std.builtin.is_test) "zig test" else "zig " ++ build_options.version;
1112
...@@ -23,6 +24,7 @@ pub const Options = struct {...@@ -23,6 +24,7 @@ pub const Options = struct {
23 optimize_mode: std.builtin.Mode,24 optimize_mode: std.builtin.Mode,
24 root_name: []const u8,25 root_name: []const u8,
25 root_pkg: ?*const Package,26 root_pkg: ?*const Package,
27 dynamic_linker: ?[]const u8 = null,
26 /// Used for calculating how much space to reserve for symbols in case the binary file28 /// Used for calculating how much space to reserve for symbols in case the binary file
27 /// does not already have a symbol table.29 /// does not already have a symbol table.
28 symbol_count_hint: u64 = 32,30 symbol_count_hint: u64 = 32,
...@@ -30,6 +32,7 @@ pub const Options = struct {...@@ -30,6 +32,7 @@ pub const Options = struct {
30 /// the binary file does not already have such a section.32 /// the binary file does not already have such a section.
31 program_code_size_hint: u64 = 256 * 1024,33 program_code_size_hint: u64 = 256 * 1024,
32 entry_addr: ?u64 = null,34 entry_addr: ?u64 = null,
35 stack_size_override: ?u64 = null,
33 /// Set to `true` to omit debug info.36 /// Set to `true` to omit debug info.
34 strip: bool = false,37 strip: bool = false,
35 /// If this is true then this link code is responsible for outputting an object38 /// If this is true then this link code is responsible for outputting an object
...@@ -44,6 +47,19 @@ pub const Options = struct {...@@ -44,6 +47,19 @@ pub const Options = struct {
44 link_libc: bool = false,47 link_libc: bool = false,
45 link_libcpp: bool = false,48 link_libcpp: bool = false,
46 function_sections: bool = false,49 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
48 objects: []const []const u8 = &[0][]const u8{},64 objects: []const []const u8 = &[0][]const u8{},
49 framework_dirs: []const []const u8 = &[0][]const u8{},65 framework_dirs: []const []const u8 = &[0][]const u8{},
...@@ -52,6 +68,9 @@ pub const Options = struct {...@@ -52,6 +68,9 @@ pub const Options = struct {
52 lib_dirs: []const []const u8 = &[0][]const u8{},68 lib_dirs: []const []const u8 = &[0][]const u8{},
53 rpath_list: []const []const u8 = &[0][]const u8{},69 rpath_list: []const []const u8 = &[0][]const u8{},
5470
71 version: std.builtin.Version,
72 libc_installation: ?*const LibCInstallation,
73
55 pub fn effectiveOutputMode(options: Options) std.builtin.OutputMode {74 pub fn effectiveOutputMode(options: Options) std.builtin.OutputMode {
56 return if (options.use_lld) .Obj else options.output_mode;75 return if (options.use_lld) .Obj else options.output_mode;
57 }76 }
src-self-hosted/link/Elf.zig+304-6
...@@ -18,6 +18,7 @@ const link = @import("../link.zig");...@@ -18,6 +18,7 @@ const link = @import("../link.zig");
18const File = link.File;18const File = link.File;
19const Elf = @This();19const Elf = @This();
20const build_options = @import("build_options");20const build_options = @import("build_options");
21const target_util = @import("../target.zig");
2122
22const default_entry_addr = 0x8000000;23const default_entry_addr = 0x8000000;
2324
...@@ -709,12 +710,7 @@ pub const abbrev_parameter = 6;...@@ -709,12 +710,7 @@ pub const abbrev_parameter = 6;
709710
710pub fn flush(self: *Elf, module: *Module) !void {711pub fn flush(self: *Elf, module: *Module) !void {
711 if (build_options.have_llvm and self.base.options.use_lld) {712 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 it713 return self.linkWithLLD(module);
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", .{});
718 } else {714 } else {
719 switch (self.base.options.effectiveOutputMode()) {715 switch (self.base.options.effectiveOutputMode()) {
720 .Exe, .Obj => {},716 .Exe, .Obj => {},
...@@ -1202,6 +1198,275 @@ fn flushInner(self: *Elf, module: *Module) !void {...@@ -1202,6 +1198,275 @@ fn flushInner(self: *Elf, module: *Module) !void {
1202 assert(!self.debug_strtab_dirty);1198 assert(!self.debug_strtab_dirty);
1203}1199}
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
1205fn writeDwarfAddrAssumeCapacity(self: *Elf, buf: *std.ArrayList(u8), addr: u64) void {1470fn writeDwarfAddrAssumeCapacity(self: *Elf, buf: *std.ArrayList(u8), addr: u64) void {
1206 const target_endian = self.base.options.target.cpu.arch.endian();1471 const target_endian = self.base.options.target.cpu.arch.endian();
1207 switch (self.ptr_width) {1472 switch (self.ptr_width) {
...@@ -2616,3 +2881,36 @@ fn sectHeaderTo32(shdr: elf.Elf64_Shdr) elf.Elf32_Shdr {...@@ -2616,3 +2881,36 @@ fn sectHeaderTo32(shdr: elf.Elf64_Shdr) elf.Elf32_Shdr {
2616 .sh_entsize = @intCast(u32, shdr.sh_entsize),2881 .sh_entsize = @intCast(u32, shdr.sh_entsize),
2617 };2882 };
2618}2883}
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");...@@ -14,6 +14,7 @@ const zir = @import("zir.zig");
14const build_options = @import("build_options");14const build_options = @import("build_options");
15const warn = std.log.warn;15const warn = std.log.warn;
16const introspect = @import("introspect.zig");16const introspect = @import("introspect.zig");
17const LibCInstallation = @import("libc_installation.zig").LibCInstallation;
1718
18fn fatal(comptime format: []const u8, args: anytype) noreturn {19fn fatal(comptime format: []const u8, args: anytype) noreturn {
19 std.log.emerg(format, args);20 std.log.emerg(format, args);
...@@ -33,18 +34,22 @@ const usage =...@@ -33,18 +34,22 @@ const usage =
33 \\34 \\
34 \\Commands:35 \\Commands:
35 \\36 \\
36 \\ build-exe [source] Create executable from source or object files37 \\ build-exe Create executable from source or object files
37 \\ build-lib [source] Create library from source or object files38 \\ build-lib Create library from source or object files
38 \\ build-obj [source] Create object from source or assembly39 \\ build-obj Create object from source or assembly
39 \\ cc Use Zig as a drop-in C compiler40 \\ cc Use Zig as a drop-in C compiler
40 \\ c++ Use Zig as a drop-in C++ compiler41 \\ c++ Use Zig as a drop-in C++ compiler
41 \\ env Print lib path, std path, compiler id and version42 \\ env Print lib path, std path, compiler id and version
42 \\ fmt [source] Parse file and render in canonical zig format43 \\ fmt Parse file and render in canonical zig format
43 \\ translate-c [source] Convert C code to Zig code44 \\ libc Display native libc paths file or validate one
44 \\ targets List available compilation targets45 \\ translate-c Convert C code to Zig code
45 \\ version Print version number and exit46 \\ targets List available compilation targets
46 \\ zen Print zen of zig and exit47 \\ version Print version number and exit
48 \\ zen Print zen of zig and exit
47 \\49 \\
50 \\General Options:
51 \\
52 \\ --help Print command-specific usage
48 \\53 \\
49;54;
5055
...@@ -126,6 +131,8 @@ pub fn main() !void {...@@ -126,6 +131,8 @@ pub fn main() !void {
126 return punt_to_clang(arena, args);131 return punt_to_clang(arena, args);
127 } else if (mem.eql(u8, cmd, "fmt")) {132 } else if (mem.eql(u8, cmd, "fmt")) {
128 return cmdFmt(gpa, cmd_args);133 return cmdFmt(gpa, cmd_args);
134 } else if (mem.eql(u8, cmd, "libc")) {
135 return cmdLibC(gpa, cmd_args);
129 } else if (mem.eql(u8, cmd, "targets")) {136 } else if (mem.eql(u8, cmd, "targets")) {
130 const info = try std.zig.system.NativeTargetInfo.detect(arena, .{});137 const info = try std.zig.system.NativeTargetInfo.detect(arena, .{});
131 const stdout = io.getStdOut().outStream();138 const stdout = io.getStdOut().outStream();
...@@ -184,7 +191,6 @@ const usage_build_generic =...@@ -184,7 +191,6 @@ const usage_build_generic =
184 \\ ReleaseSmall Optimize for small binary, safety off191 \\ ReleaseSmall Optimize for small binary, safety off
185 \\ -fPIC Force-enable Position Independent Code192 \\ -fPIC Force-enable Position Independent Code
186 \\ -fno-PIC Force-disable Position Independent Code193 \\ -fno-PIC Force-disable Position Independent Code
187 \\ --dynamic Force output to be dynamically linked
188 \\ --strip Exclude debug symbols194 \\ --strip Exclude debug symbols
189 \\ -ofmt=[mode] Override target object format195 \\ -ofmt=[mode] Override target object format
190 \\ elf Executable and Linking Format196 \\ elf Executable and Linking Format
...@@ -199,6 +205,7 @@ const usage_build_generic =...@@ -199,6 +205,7 @@ const usage_build_generic =
199 \\ -isystem [dir] Add directory to SYSTEM include search path205 \\ -isystem [dir] Add directory to SYSTEM include search path
200 \\ -I[dir] Add directory to include search path206 \\ -I[dir] Add directory to include search path
201 \\ -D[macro]=[value] Define C [macro] to [value] (1 if [value] omitted)207 \\ -D[macro]=[value] Define C [macro] to [value] (1 if [value] omitted)
208 \\ --libc [file] Provide a file which specifies libc paths
202 \\209 \\
203 \\Link Options:210 \\Link Options:
204 \\ -l[lib], --library [lib] Link against system library211 \\ -l[lib], --library [lib] Link against system library
...@@ -208,6 +215,9 @@ const usage_build_generic =...@@ -208,6 +215,9 @@ const usage_build_generic =
208 \\ --version [ver] Dynamic library semver215 \\ --version [ver] Dynamic library semver
209 \\ -rdynamic Add all symbols to the dynamic symbol table216 \\ -rdynamic Add all symbols to the dynamic symbol table
210 \\ -rpath [path] Add directory to the runtime library search path217 \\ -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
211 \\221 \\
212 \\Debug Options (Zig Compiler Development):222 \\Debug Options (Zig Compiler Development):
213 \\ -ftime-report Print timing diagnostics223 \\ -ftime-report Print timing diagnostics
...@@ -220,6 +230,14 @@ const usage_build_generic =...@@ -220,6 +230,14 @@ const usage_build_generic =
220 \\230 \\
221;231;
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
223const Emit = union(enum) {241const Emit = union(enum) {
224 no,242 no,
225 yes_default_path,243 yes_default_path,
...@@ -275,16 +293,17 @@ pub fn buildOutputType(...@@ -275,16 +293,17 @@ pub fn buildOutputType(
275 var version_script: ?[]const u8 = null;293 var version_script: ?[]const u8 = null;
276 var disable_c_depfile = false;294 var disable_c_depfile = false;
277 var override_soname: ?[]const u8 = null;295 var override_soname: ?[]const u8 = null;
278 var linker_optimization: ?[]const u8 = null;
279 var linker_gc_sections: ?bool = null;296 var linker_gc_sections: ?bool = null;
280 var linker_allow_shlib_undefined: ?bool = null;297 var linker_allow_shlib_undefined: ?bool = null;
281 var linker_bind_global_refs_locally: ?bool = null;298 var linker_bind_global_refs_locally: ?bool = null;
282 var linker_z_nodelete = false;299 var linker_z_nodelete = false;
283 var linker_z_defs = false;300 var linker_z_defs = false;
284 var stack_size_override: u64 = 0;301 var stack_size_override: ?u64 = null;
285 var use_llvm: ?bool = null;302 var use_llvm: ?bool = null;
286 var use_lld: ?bool = null;303 var use_lld: ?bool = null;
287 var use_clang: ?bool = null;304 var use_clang: ?bool = null;
305 var link_eh_frame_hdr = false;
306 var libc_paths_file: ?[]const u8 = null;
288307
289 var system_libs = std.ArrayList([]const u8).init(gpa);308 var system_libs = std.ArrayList([]const u8).init(gpa);
290 defer system_libs.deinit();309 defer system_libs.deinit();
...@@ -292,6 +311,9 @@ pub fn buildOutputType(...@@ -292,6 +311,9 @@ pub fn buildOutputType(
292 var clang_argv = std.ArrayList([]const u8).init(gpa);311 var clang_argv = std.ArrayList([]const u8).init(gpa);
293 defer clang_argv.deinit();312 defer clang_argv.deinit();
294313
314 var lld_argv = std.ArrayList([]const u8).init(gpa);
315 defer lld_argv.deinit();
316
295 var lib_dirs = std.ArrayList([]const u8).init(gpa);317 var lib_dirs = std.ArrayList([]const u8).init(gpa);
296 defer lib_dirs.deinit();318 defer lib_dirs.deinit();
297319
...@@ -414,15 +436,11 @@ pub fn buildOutputType(...@@ -414,15 +436,11 @@ pub fn buildOutputType(
414 fatal("unable to parse --version '{}': {}", .{ args[i], @errorName(err) });436 fatal("unable to parse --version '{}': {}", .{ args[i], @errorName(err) });
415 };437 };
416 } else if (mem.eql(u8, arg, "-target")) {438 } else if (mem.eql(u8, arg, "-target")) {
417 if (i + 1 >= args.len) {439 if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg});
418 fatal("expected parameter after -target", .{});
419 }
420 i += 1;440 i += 1;
421 target_arch_os_abi = args[i];441 target_arch_os_abi = args[i];
422 } else if (mem.eql(u8, arg, "-mcpu")) {442 } else if (mem.eql(u8, arg, "-mcpu")) {
423 if (i + 1 >= args.len) {443 if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg});
424 fatal("expected parameter after -mcpu", .{});
425 }
426 i += 1;444 i += 1;
427 target_mcpu = args[i];445 target_mcpu = args[i];
428 } else if (mem.startsWith(u8, arg, "-ofmt=")) {446 } else if (mem.startsWith(u8, arg, "-ofmt=")) {
...@@ -430,11 +448,13 @@ pub fn buildOutputType(...@@ -430,11 +448,13 @@ pub fn buildOutputType(
430 } else if (mem.startsWith(u8, arg, "-mcpu=")) {448 } else if (mem.startsWith(u8, arg, "-mcpu=")) {
431 target_mcpu = arg["-mcpu=".len..];449 target_mcpu = arg["-mcpu=".len..];
432 } else if (mem.eql(u8, arg, "--dynamic-linker")) {450 } else if (mem.eql(u8, arg, "--dynamic-linker")) {
433 if (i + 1 >= args.len) {451 if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg});
434 fatal("expected parameter after --dynamic-linker", .{});
435 }
436 i += 1;452 i += 1;
437 target_dynamic_linker = args[i];453 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];
438 } else if (mem.eql(u8, arg, "--watch")) {458 } else if (mem.eql(u8, arg, "--watch")) {
439 watch = true;459 watch = true;
440 } else if (mem.eql(u8, arg, "-ftime-report")) {460 } else if (mem.eql(u8, arg, "-ftime-report")) {
...@@ -481,6 +501,8 @@ pub fn buildOutputType(...@@ -481,6 +501,8 @@ pub fn buildOutputType(
481 link_mode = .Static;501 link_mode = .Static;
482 } else if (mem.eql(u8, arg, "--strip")) {502 } else if (mem.eql(u8, arg, "--strip")) {
483 strip = true;503 strip = true;
504 } else if (mem.eql(u8, arg, "--eh-frame-hdr")) {
505 link_eh_frame_hdr = true;
484 } else if (mem.eql(u8, arg, "-Bsymbolic")) {506 } else if (mem.eql(u8, arg, "-Bsymbolic")) {
485 linker_bind_global_refs_locally = true;507 linker_bind_global_refs_locally = true;
486 } else if (mem.eql(u8, arg, "--debug-tokenize")) {508 } else if (mem.eql(u8, arg, "--debug-tokenize")) {
...@@ -565,7 +587,7 @@ pub fn buildOutputType(...@@ -565,7 +587,7 @@ pub fn buildOutputType(
565 const file_ext = Module.classifyFileExt(mem.spanZ(it.only_arg));587 const file_ext = Module.classifyFileExt(mem.spanZ(it.only_arg));
566 switch (file_ext) {588 switch (file_ext) {
567 .assembly, .c, .cpp, .ll, .bc, .h => try c_source_files.append(it.only_arg),589 .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),
569 }591 }
570 },592 },
571 .l => {593 .l => {
...@@ -716,7 +738,7 @@ pub fn buildOutputType(...@@ -716,7 +738,7 @@ pub fn buildOutputType(
716 }738 }
717 version_script = linker_args.items[i];739 version_script = linker_args.items[i];
718 } else if (mem.startsWith(u8, arg, "-O")) {740 } else if (mem.startsWith(u8, arg, "-O")) {
719 linker_optimization = arg;741 try lld_argv.append(arg);
720 } else if (mem.eql(u8, arg, "--gc-sections")) {742 } else if (mem.eql(u8, arg, "--gc-sections")) {
721 linker_gc_sections = true;743 linker_gc_sections = true;
722 } else if (mem.eql(u8, arg, "--no-gc-sections")) {744 } else if (mem.eql(u8, arg, "--no-gc-sections")) {
...@@ -994,10 +1016,21 @@ pub fn buildOutputType(...@@ -994,10 +1016,21 @@ pub fn buildOutputType(
994 };1016 };
995 var default_prng = std.rand.DefaultPrng.init(random_seed);1017 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
997 const module = Module.create(gpa, .{1028 const module = Module.create(gpa, .{
998 .zig_lib_dir = zig_lib_dir,1029 .zig_lib_dir = zig_lib_dir,
999 .root_name = root_name,1030 .root_name = root_name,
1000 .target = target_info.target,1031 .target = target_info.target,
1032 .is_native_os = cross_target.isNativeOs(),
1033 .dynamic_linker = target_info.dynamic_linker.get(),
1001 .output_mode = output_mode,1034 .output_mode = output_mode,
1002 .root_pkg = root_pkg,1035 .root_pkg = root_pkg,
1003 .bin_file_dir_path = null,1036 .bin_file_dir_path = null,
...@@ -1008,6 +1041,7 @@ pub fn buildOutputType(...@@ -1008,6 +1041,7 @@ pub fn buildOutputType(
1008 .optimize_mode = build_mode,1041 .optimize_mode = build_mode,
1009 .keep_source_files_loaded = zir_out_path != null,1042 .keep_source_files_loaded = zir_out_path != null,
1010 .clang_argv = clang_argv.items,1043 .clang_argv = clang_argv.items,
1044 .lld_argv = lld_argv.items,
1011 .lib_dirs = lib_dirs.items,1045 .lib_dirs = lib_dirs.items,
1012 .rpath_list = rpath_list.items,1046 .rpath_list = rpath_list.items,
1013 .c_source_files = c_source_files.items,1047 .c_source_files = c_source_files.items,
...@@ -1028,17 +1062,19 @@ pub fn buildOutputType(...@@ -1028,17 +1062,19 @@ pub fn buildOutputType(
1028 .version_script = version_script,1062 .version_script = version_script,
1029 .disable_c_depfile = disable_c_depfile,1063 .disable_c_depfile = disable_c_depfile,
1030 .override_soname = override_soname,1064 .override_soname = override_soname,
1031 .linker_optimization = linker_optimization,
1032 .linker_gc_sections = linker_gc_sections,1065 .linker_gc_sections = linker_gc_sections,
1033 .linker_allow_shlib_undefined = linker_allow_shlib_undefined,1066 .linker_allow_shlib_undefined = linker_allow_shlib_undefined,
1034 .linker_bind_global_refs_locally = linker_bind_global_refs_locally,1067 .linker_bind_global_refs_locally = linker_bind_global_refs_locally,
1035 .linker_z_nodelete = linker_z_nodelete,1068 .linker_z_nodelete = linker_z_nodelete,
1036 .linker_z_defs = linker_z_defs,1069 .linker_z_defs = linker_z_defs,
1070 .link_eh_frame_hdr = link_eh_frame_hdr,
1037 .stack_size_override = stack_size_override,1071 .stack_size_override = stack_size_override,
1038 .strip = strip,1072 .strip = strip,
1039 .self_exe_path = self_exe_path,1073 .self_exe_path = self_exe_path,
1040 .rand = &default_prng.random,1074 .rand = &default_prng.random,
1041 .clang_passthrough_mode = arg_mode != .build,1075 .clang_passthrough_mode = arg_mode != .build,
1076 .version = version,
1077 .libc_installation = if (libc_installation) |*lci| lci else null,
1042 }) catch |err| {1078 }) catch |err| {
1043 fatal("unable to create module: {}", .{@errorName(err)});1079 fatal("unable to create module: {}", .{@errorName(err)});
1044 };1080 };
...@@ -1116,16 +1152,64 @@ fn updateModule(gpa: *Allocator, module: *Module, zir_out_path: ?[]const u8) !vo...@@ -1116,16 +1152,64 @@ fn updateModule(gpa: *Allocator, module: *Module, zir_out_path: ?[]const u8) !vo
1116 }1152 }
1117}1153}
11181154
1119const repl_help =1155pub const usage_libc =
1120 \\Commands:1156 \\Usage: zig libc
1121 \\ update Detect changes to source files and update output files.1157 \\
1122 \\ help Print this text1158 \\ Detect the native libc installation and print the resulting
1123 \\ exit Quit this repl1159 \\ 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.
1124 \\1166 \\
1125;1167;
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
1127pub const usage_fmt =1211pub const usage_fmt =
1128 \\usage: zig fmt [file]...1212 \\Usage: zig fmt [file]...
1129 \\1213 \\
1130 \\ Formats the input files and modifies them in-place.1214 \\ Formats the input files and modifies them in-place.
1131 \\ Arguments can be files or directories, which are searched1215 \\ 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 {...@@ -109,3 +109,28 @@ pub fn canBuildLibC(target: std.Target) bool {
109 }109 }
110 return false;110 return false;
111}111}
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 {...@@ -472,6 +472,7 @@ pub const TestContext = struct {
472 .root_pkg = root_pkg,472 .root_pkg = root_pkg,
473 .keep_source_files_loaded = true,473 .keep_source_files_loaded = true,
474 .object_format = ofmt,474 .object_format = ofmt,
475 .is_native_os = case.target.isNativeOs(),
475 });476 });
476 defer module.destroy();477 defer module.destroy();
477478