| author | |
| committer | |
| log | e05ecbf165931d14440e6e5d089b64788b82d14f |
| tree | 93ec39e42c1fdc0f0949d75de95363477c6a62c8 |
| parent | 5746a8658ea52dee4bf310c1290d76b1255cb5ec |
* 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 -static7 files changed, 634 insertions(+), 64 deletions(-)
src-self-hosted/Module.zig+167-26| ... | ... | @@ -24,6 +24,7 @@ const liveness = @import("liveness.zig"); |
| 24 | 24 | const astgen = @import("astgen.zig"); |
| 25 | 25 | const zir_sema = @import("zir_sema.zig"); |
| 26 | 26 | const build_options = @import("build_options"); |
| 27 | const LibCInstallation = @import("libc_installation.zig").LibCInstallation; | |
| 27 | 28 | |
| 28 | 29 | /// General-purpose allocator. Used for both temporary and long-term storage. |
| 29 | 30 | gpa: *Allocator, |
| ... | ... | @@ -82,8 +83,6 @@ next_anon_name_index: usize = 0, |
| 82 | 83 | /// contains Decls that need to be deleted if they end up having no references to them. |
| 83 | 84 | deletion_set: std.ArrayListUnmanaged(*Decl) = .{}, |
| 84 | 85 | |
| 85 | /// Owned by Module. | |
| 86 | root_name: []u8, | |
| 87 | 86 | keep_source_files_loaded: bool, |
| 88 | 87 | use_clang: bool, |
| 89 | 88 | sanitize_c: bool, |
| ... | ... | @@ -106,6 +105,19 @@ zig_cache_dir_path: []const u8, |
| 106 | 105 | libc_include_dir_list: []const []const u8, |
| 107 | 106 | rand: *std.rand.Random, |
| 108 | 107 | |
| 108 | /// Populated when we build libc++.a. A WorkItem to build this is placed in the queue | |
| 109 | /// and resolved before calling linker.flush(). | |
| 110 | libcxx_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(). | |
| 113 | libcxxabi_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(). | |
| 116 | libunwind_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(). | |
| 119 | libc_static_lib: ?[]const u8 = null, | |
| 120 | ||
| 109 | 121 | pub const InnerError = error{ OutOfMemory, AnalysisFail }; |
| 110 | 122 | |
| 111 | 123 | const WorkItem = union(enum) { |
| ... | ... | @@ -932,6 +944,7 @@ pub const InitOptions = struct { |
| 932 | 944 | root_pkg: ?*Package, |
| 933 | 945 | output_mode: std.builtin.OutputMode, |
| 934 | 946 | rand: *std.rand.Random, |
| 947 | dynamic_linker: ?[]const u8 = null, | |
| 935 | 948 | bin_file_dir_path: ?[]const u8 = null, |
| 936 | 949 | bin_file_dir: ?std.fs.Dir = null, |
| 937 | 950 | bin_file_path: []const u8, |
| ... | ... | @@ -941,6 +954,7 @@ pub const InitOptions = struct { |
| 941 | 954 | optimize_mode: std.builtin.Mode = .Debug, |
| 942 | 955 | keep_source_files_loaded: bool = false, |
| 943 | 956 | clang_argv: []const []const u8 = &[0][]const u8{}, |
| 957 | lld_argv: []const []const u8 = &[0][]const u8{}, | |
| 944 | 958 | lib_dirs: []const []const u8 = &[0][]const u8{}, |
| 945 | 959 | rpath_list: []const []const u8 = &[0][]const u8{}, |
| 946 | 960 | c_source_files: []const []const u8 = &[0][]const u8{}, |
| ... | ... | @@ -957,10 +971,11 @@ pub const InitOptions = struct { |
| 957 | 971 | use_clang: ?bool = null, |
| 958 | 972 | rdynamic: bool = false, |
| 959 | 973 | strip: bool = false, |
| 974 | is_native_os: bool, | |
| 975 | link_eh_frame_hdr: bool = false, | |
| 960 | 976 | linker_script: ?[]const u8 = null, |
| 961 | 977 | version_script: ?[]const u8 = null, |
| 962 | 978 | override_soname: ?[]const u8 = null, |
| 963 | linker_optimization: ?[]const u8 = null, | |
| 964 | 979 | linker_gc_sections: ?bool = null, |
| 965 | 980 | function_sections: ?bool = null, |
| 966 | 981 | linker_allow_shlib_undefined: ?bool = null, |
| ... | ... | @@ -969,8 +984,10 @@ pub const InitOptions = struct { |
| 969 | 984 | linker_z_nodelete: bool = false, |
| 970 | 985 | linker_z_defs: bool = false, |
| 971 | 986 | clang_passthrough_mode: bool = false, |
| 972 | stack_size_override: u64 = 0, | |
| 987 | stack_size_override: ?u64 = null, | |
| 973 | 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 | }; |
| 975 | 992 | |
| 976 | 993 | pub fn create(gpa: *Allocator, options: InitOptions) !*Module { |
| ... | ... | @@ -1002,6 +1019,7 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Module { |
| 1002 | 1019 | options.frameworks.len != 0 or |
| 1003 | 1020 | options.system_libs.len != 0 or |
| 1004 | 1021 | options.link_libc or options.link_libcpp or |
| 1022 | options.link_eh_frame_hdr or | |
| 1005 | 1023 | options.linker_script != null or options.version_script != null) |
| 1006 | 1024 | { |
| 1007 | 1025 | break :blk true; |
| ... | ... | @@ -1017,6 +1035,35 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Module { |
| 1017 | 1035 | break :blk false; |
| 1018 | 1036 | }; |
| 1019 | 1037 | |
| 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 | 1067 | const bin_file = try link.File.openPath(gpa, .{ |
| 1021 | 1068 | .dir = options.bin_file_dir orelse std.fs.cwd(), |
| 1022 | 1069 | .dir_path = options.bin_file_dir_path, |
| ... | ... | @@ -1024,8 +1071,9 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Module { |
| 1024 | 1071 | .root_name = root_name, |
| 1025 | 1072 | .root_pkg = options.root_pkg, |
| 1026 | 1073 | .target = options.target, |
| 1074 | .dynamic_linker = options.dynamic_linker, | |
| 1027 | 1075 | .output_mode = options.output_mode, |
| 1028 | .link_mode = options.link_mode orelse .Static, | |
| 1076 | .link_mode = link_mode, | |
| 1029 | 1077 | .object_format = ofmt, |
| 1030 | 1078 | .optimize_mode = options.optimize_mode, |
| 1031 | 1079 | .use_lld = use_lld, |
| ... | ... | @@ -1039,7 +1087,22 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Module { |
| 1039 | 1087 | .lib_dirs = options.lib_dirs, |
| 1040 | 1088 | .rpath_list = options.rpath_list, |
| 1041 | 1089 | .strip = options.strip, |
| 1090 | .is_native_os = options.is_native_os, | |
| 1042 | 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 | 1107 | errdefer bin_file.destroy(); |
| 1045 | 1108 | |
| ... | ... | @@ -1146,13 +1209,6 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Module { |
| 1146 | 1209 | break :blk true; |
| 1147 | 1210 | }; |
| 1148 | 1211 | |
| 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 | 1212 | const sanitize_c: bool = options.want_sanitize_c orelse switch (options.optimize_mode) { |
| 1157 | 1213 | .Debug, .ReleaseSafe => true, |
| 1158 | 1214 | .ReleaseSmall, .ReleaseFast => false, |
| ... | ... | @@ -1163,7 +1219,6 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Module { |
| 1163 | 1219 | .arena_state = arena_allocator.state, |
| 1164 | 1220 | .zig_lib_dir = options.zig_lib_dir, |
| 1165 | 1221 | .zig_cache_dir_path = zig_cache_dir_path, |
| 1166 | .root_name = root_name, | |
| 1167 | 1222 | .root_pkg = options.root_pkg, |
| 1168 | 1223 | .root_scope = root_scope, |
| 1169 | 1224 | .bin_file = bin_file, |
| ... | ... | @@ -1174,7 +1229,7 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Module { |
| 1174 | 1229 | .c_source_files = options.c_source_files, |
| 1175 | 1230 | .cache = cache, |
| 1176 | 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 | 1233 | .sanitize_c = sanitize_c, |
| 1179 | 1234 | .rand = options.rand, |
| 1180 | 1235 | .clang_passthrough_mode = options.clang_passthrough_mode, |
| ... | ... | @@ -1544,7 +1599,10 @@ fn buildCObject(mod: *Module, c_object: *CObject) !void { |
| 1544 | 1599 | // directly to the output file. |
| 1545 | 1600 | const direct_o = mod.c_source_files.len == 1 and mod.root_pkg == null and |
| 1546 | 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 | 1606 | const o_basename = try std.fmt.allocPrint(arena, "{}{}", .{ o_basename_noext, mod.getTarget().oFileExt() }); |
| 1549 | 1607 | |
| 1550 | 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 | 1807 | try argv.append(p); |
| 1750 | 1808 | } |
| 1751 | 1809 | }, |
| 1752 | .assembly, .ll, .bc, .unknown => {}, | |
| 1810 | .so, .assembly, .ll, .bc, .unknown => {}, | |
| 1753 | 1811 | } |
| 1754 | 1812 | // TODO CLI args for cpu features when compiling assembly |
| 1755 | 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 | 4317 | ll, |
| 4260 | 4318 | bc, |
| 4261 | 4319 | assembly, |
| 4320 | so, | |
| 4262 | 4321 | unknown, |
| 4263 | 4322 | }; |
| 4264 | 4323 | |
| ... | ... | @@ -4290,10 +4349,36 @@ pub fn classifyFileExt(filename: []const u8) FileExt { |
| 4290 | 4349 | return .assembly; |
| 4291 | 4350 | } else if (mem.endsWith(u8, filename, ".h")) { |
| 4292 | 4351 | 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; | |
| 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 | ||
| 4374 | test "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 | } |
| 4298 | 4383 | |
| 4299 | 4384 | fn haveFramePointer(mod: *Module) bool { |
| ... | ... | @@ -4303,16 +4388,29 @@ fn haveFramePointer(mod: *Module) bool { |
| 4303 | 4388 | }; |
| 4304 | 4389 | } |
| 4305 | 4390 | |
| 4391 | const LibCDirs = struct { | |
| 4392 | libc_include_dir_list: []const []const u8, | |
| 4393 | libc_installation: ?*const LibCInstallation, | |
| 4394 | }; | |
| 4395 | ||
| 4306 | 4396 | fn detectLibCIncludeDirs( |
| 4307 | 4397 | arena: *Allocator, |
| 4308 | 4398 | zig_lib_dir: []const u8, |
| 4309 | 4399 | target: Target, |
| 4400 | is_native_os: bool, | |
| 4310 | 4401 | 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 | } | |
| 4313 | 4410 | |
| 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 | } | |
| 4316 | 4414 | |
| 4317 | 4415 | if (target_util.canBuildLibC(target)) { |
| 4318 | 4416 | const generic_name = target_util.libCGenericName(target); |
| ... | ... | @@ -4348,9 +4446,52 @@ fn detectLibCIncludeDirs( |
| 4348 | 4446 | list[1] = generic_include_dir; |
| 4349 | 4447 | list[2] = arch_os_include_dir; |
| 4350 | 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 | ||
| 4467 | fn 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 | } | |
| 4353 | 4490 | |
| 4354 | // TODO finish porting detect_libc from codegen.cpp | |
| 4355 | return error.LibCDetectionUnimplemented; | |
| 4491 | pub 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; | |
| 4356 | 4497 | } |
src-self-hosted/libc_installation.zig+2| ... | ... | @@ -11,6 +11,8 @@ const is_gnu = Target.current.isGnu(); |
| 11 | 11 | |
| 12 | 12 | usingnamespace @import("windows_sdk.zig"); |
| 13 | 13 | |
| 14 | // TODO Rework this abstraction to use std.log instead of taking a stderr stream. | |
| 15 | ||
| 14 | 16 | /// See the render function implementation for documentation of the fields. |
| 15 | 17 | pub const LibCInstallation = struct { |
| 16 | 18 | include_dir: ?[]const u8 = null, |
src-self-hosted/link.zig+19| ... | ... | @@ -6,6 +6,7 @@ const trace = @import("tracy.zig").trace; |
| 6 | 6 | const Package = @import("Package.zig"); |
| 7 | 7 | const Type = @import("type.zig").Type; |
| 8 | 8 | const build_options = @import("build_options"); |
| 9 | const LibCInstallation = @import("libc_installation.zig").LibCInstallation; | |
| 9 | 10 | |
| 10 | 11 | pub const producer_string = if (std.builtin.is_test) "zig test" else "zig " ++ build_options.version; |
| 11 | 12 | |
| ... | ... | @@ -23,6 +24,7 @@ pub const Options = struct { |
| 23 | 24 | optimize_mode: std.builtin.Mode, |
| 24 | 25 | root_name: []const u8, |
| 25 | 26 | root_pkg: ?*const Package, |
| 27 | dynamic_linker: ?[]const u8 = null, | |
| 26 | 28 | /// Used for calculating how much space to reserve for symbols in case the binary file |
| 27 | 29 | /// does not already have a symbol table. |
| 28 | 30 | symbol_count_hint: u64 = 32, |
| ... | ... | @@ -30,6 +32,7 @@ pub const Options = struct { |
| 30 | 32 | /// the binary file does not already have such a section. |
| 31 | 33 | program_code_size_hint: u64 = 256 * 1024, |
| 32 | 34 | entry_addr: ?u64 = null, |
| 35 | stack_size_override: ?u64 = null, | |
| 33 | 36 | /// Set to `true` to omit debug info. |
| 34 | 37 | strip: bool = false, |
| 35 | 38 | /// If this is true then this link code is responsible for outputting an object |
| ... | ... | @@ -44,6 +47,19 @@ pub const Options = struct { |
| 44 | 47 | link_libc: bool = false, |
| 45 | 48 | link_libcpp: bool = false, |
| 46 | 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, | |
| 47 | 63 | |
| 48 | 64 | objects: []const []const u8 = &[0][]const u8{}, |
| 49 | 65 | framework_dirs: []const []const u8 = &[0][]const u8{}, |
| ... | ... | @@ -52,6 +68,9 @@ pub const Options = struct { |
| 52 | 68 | lib_dirs: []const []const u8 = &[0][]const u8{}, |
| 53 | 69 | rpath_list: []const []const u8 = &[0][]const u8{}, |
| 54 | 70 | |
| 71 | version: std.builtin.Version, | |
| 72 | libc_installation: ?*const LibCInstallation, | |
| 73 | ||
| 55 | 74 | pub fn effectiveOutputMode(options: Options) std.builtin.OutputMode { |
| 56 | 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 | 18 | const File = link.File; |
| 19 | 19 | const Elf = @This(); |
| 20 | 20 | const build_options = @import("build_options"); |
| 21 | const target_util = @import("../target.zig"); | |
| 21 | 22 | |
| 22 | 23 | const default_entry_addr = 0x8000000; |
| 23 | 24 | |
| ... | ... | @@ -709,12 +710,7 @@ pub const abbrev_parameter = 6; |
| 709 | 710 | |
| 710 | 711 | pub fn flush(self: *Elf, module: *Module) !void { |
| 711 | 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 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); | |
| 718 | 714 | } else { |
| 719 | 715 | switch (self.base.options.effectiveOutputMode()) { |
| 720 | 716 | .Exe, .Obj => {}, |
| ... | ... | @@ -1202,6 +1198,275 @@ fn flushInner(self: *Elf, module: *Module) !void { |
| 1202 | 1198 | assert(!self.debug_strtab_dirty); |
| 1203 | 1199 | } |
| 1204 | 1200 | |
| 1201 | fn 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 | ||
| 1205 | 1470 | fn writeDwarfAddrAssumeCapacity(self: *Elf, buf: *std.ArrayList(u8), addr: u64) void { |
| 1206 | 1471 | const target_endian = self.base.options.target.cpu.arch.endian(); |
| 1207 | 1472 | switch (self.ptr_width) { |
| ... | ... | @@ -2616,3 +2881,36 @@ fn sectHeaderTo32(shdr: elf.Elf64_Shdr) elf.Elf32_Shdr { |
| 2616 | 2881 | .sh_entsize = @intCast(u32, shdr.sh_entsize), |
| 2617 | 2882 | }; |
| 2618 | 2883 | } |
| 2884 | ||
| 2885 | fn 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 | 14 | const build_options = @import("build_options"); |
| 15 | 15 | const warn = std.log.warn; |
| 16 | 16 | const introspect = @import("introspect.zig"); |
| 17 | const LibCInstallation = @import("libc_installation.zig").LibCInstallation; | |
| 17 | 18 | |
| 18 | 19 | fn fatal(comptime format: []const u8, args: anytype) noreturn { |
| 19 | 20 | std.log.emerg(format, args); |
| ... | ... | @@ -33,18 +34,22 @@ const usage = |
| 33 | 34 | \\ |
| 34 | 35 | \\Commands: |
| 35 | 36 | \\ |
| 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 | |
| 47 | 49 | \\ |
| 50 | \\General Options: | |
| 51 | \\ | |
| 52 | \\ --help Print command-specific usage | |
| 48 | 53 | \\ |
| 49 | 54 | ; |
| 50 | 55 | |
| ... | ... | @@ -126,6 +131,8 @@ pub fn main() !void { |
| 126 | 131 | return punt_to_clang(arena, args); |
| 127 | 132 | } else if (mem.eql(u8, cmd, "fmt")) { |
| 128 | 133 | return cmdFmt(gpa, cmd_args); |
| 134 | } else if (mem.eql(u8, cmd, "libc")) { | |
| 135 | return cmdLibC(gpa, cmd_args); | |
| 129 | 136 | } else if (mem.eql(u8, cmd, "targets")) { |
| 130 | 137 | const info = try std.zig.system.NativeTargetInfo.detect(arena, .{}); |
| 131 | 138 | const stdout = io.getStdOut().outStream(); |
| ... | ... | @@ -184,7 +191,6 @@ const usage_build_generic = |
| 184 | 191 | \\ ReleaseSmall Optimize for small binary, safety off |
| 185 | 192 | \\ -fPIC Force-enable Position Independent Code |
| 186 | 193 | \\ -fno-PIC Force-disable Position Independent Code |
| 187 | \\ --dynamic Force output to be dynamically linked | |
| 188 | 194 | \\ --strip Exclude debug symbols |
| 189 | 195 | \\ -ofmt=[mode] Override target object format |
| 190 | 196 | \\ elf Executable and Linking Format |
| ... | ... | @@ -199,6 +205,7 @@ const usage_build_generic = |
| 199 | 205 | \\ -isystem [dir] Add directory to SYSTEM include search path |
| 200 | 206 | \\ -I[dir] Add directory to include search path |
| 201 | 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 | 210 | \\Link Options: |
| 204 | 211 | \\ -l[lib], --library [lib] Link against system library |
| ... | ... | @@ -208,6 +215,9 @@ const usage_build_generic = |
| 208 | 215 | \\ --version [ver] Dynamic library semver |
| 209 | 216 | \\ -rdynamic Add all symbols to the dynamic symbol table |
| 210 | 217 | \\ -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 | 222 | \\Debug Options (Zig Compiler Development): |
| 213 | 223 | \\ -ftime-report Print timing diagnostics |
| ... | ... | @@ -220,6 +230,14 @@ const usage_build_generic = |
| 220 | 230 | \\ |
| 221 | 231 | ; |
| 222 | 232 | |
| 233 | const 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 | ||
| 223 | 241 | const Emit = union(enum) { |
| 224 | 242 | no, |
| 225 | 243 | yes_default_path, |
| ... | ... | @@ -275,16 +293,17 @@ pub fn buildOutputType( |
| 275 | 293 | var version_script: ?[]const u8 = null; |
| 276 | 294 | var disable_c_depfile = false; |
| 277 | 295 | var override_soname: ?[]const u8 = null; |
| 278 | var linker_optimization: ?[]const u8 = null; | |
| 279 | 296 | var linker_gc_sections: ?bool = null; |
| 280 | 297 | var linker_allow_shlib_undefined: ?bool = null; |
| 281 | 298 | var linker_bind_global_refs_locally: ?bool = null; |
| 282 | 299 | var linker_z_nodelete = false; |
| 283 | 300 | var linker_z_defs = false; |
| 284 | var stack_size_override: u64 = 0; | |
| 301 | var stack_size_override: ?u64 = null; | |
| 285 | 302 | var use_llvm: ?bool = null; |
| 286 | 303 | var use_lld: ?bool = null; |
| 287 | 304 | var use_clang: ?bool = null; |
| 305 | var link_eh_frame_hdr = false; | |
| 306 | var libc_paths_file: ?[]const u8 = null; | |
| 288 | 307 | |
| 289 | 308 | var system_libs = std.ArrayList([]const u8).init(gpa); |
| 290 | 309 | defer system_libs.deinit(); |
| ... | ... | @@ -292,6 +311,9 @@ pub fn buildOutputType( |
| 292 | 311 | var clang_argv = std.ArrayList([]const u8).init(gpa); |
| 293 | 312 | defer clang_argv.deinit(); |
| 294 | 313 | |
| 314 | var lld_argv = std.ArrayList([]const u8).init(gpa); | |
| 315 | defer lld_argv.deinit(); | |
| 316 | ||
| 295 | 317 | var lib_dirs = std.ArrayList([]const u8).init(gpa); |
| 296 | 318 | defer lib_dirs.deinit(); |
| 297 | 319 | |
| ... | ... | @@ -414,15 +436,11 @@ pub fn buildOutputType( |
| 414 | 436 | fatal("unable to parse --version '{}': {}", .{ args[i], @errorName(err) }); |
| 415 | 437 | }; |
| 416 | 438 | } 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}); | |
| 420 | 440 | i += 1; |
| 421 | 441 | target_arch_os_abi = args[i]; |
| 422 | 442 | } 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}); | |
| 426 | 444 | i += 1; |
| 427 | 445 | target_mcpu = args[i]; |
| 428 | 446 | } else if (mem.startsWith(u8, arg, "-ofmt=")) { |
| ... | ... | @@ -430,11 +448,13 @@ pub fn buildOutputType( |
| 430 | 448 | } else if (mem.startsWith(u8, arg, "-mcpu=")) { |
| 431 | 449 | target_mcpu = arg["-mcpu=".len..]; |
| 432 | 450 | } 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}); | |
| 436 | 452 | i += 1; |
| 437 | 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 | 458 | } else if (mem.eql(u8, arg, "--watch")) { |
| 439 | 459 | watch = true; |
| 440 | 460 | } else if (mem.eql(u8, arg, "-ftime-report")) { |
| ... | ... | @@ -481,6 +501,8 @@ pub fn buildOutputType( |
| 481 | 501 | link_mode = .Static; |
| 482 | 502 | } else if (mem.eql(u8, arg, "--strip")) { |
| 483 | 503 | strip = true; |
| 504 | } else if (mem.eql(u8, arg, "--eh-frame-hdr")) { | |
| 505 | link_eh_frame_hdr = true; | |
| 484 | 506 | } else if (mem.eql(u8, arg, "-Bsymbolic")) { |
| 485 | 507 | linker_bind_global_refs_locally = true; |
| 486 | 508 | } else if (mem.eql(u8, arg, "--debug-tokenize")) { |
| ... | ... | @@ -565,7 +587,7 @@ pub fn buildOutputType( |
| 565 | 587 | const file_ext = Module.classifyFileExt(mem.spanZ(it.only_arg)); |
| 566 | 588 | switch (file_ext) { |
| 567 | 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 | 593 | .l => { |
| ... | ... | @@ -716,7 +738,7 @@ pub fn buildOutputType( |
| 716 | 738 | } |
| 717 | 739 | version_script = linker_args.items[i]; |
| 718 | 740 | } else if (mem.startsWith(u8, arg, "-O")) { |
| 719 | linker_optimization = arg; | |
| 741 | try lld_argv.append(arg); | |
| 720 | 742 | } else if (mem.eql(u8, arg, "--gc-sections")) { |
| 721 | 743 | linker_gc_sections = true; |
| 722 | 744 | } else if (mem.eql(u8, arg, "--no-gc-sections")) { |
| ... | ... | @@ -994,10 +1016,21 @@ pub fn buildOutputType( |
| 994 | 1016 | }; |
| 995 | 1017 | var default_prng = std.rand.DefaultPrng.init(random_seed); |
| 996 | 1018 | |
| 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 | 1028 | const module = Module.create(gpa, .{ |
| 998 | 1029 | .zig_lib_dir = zig_lib_dir, |
| 999 | 1030 | .root_name = root_name, |
| 1000 | 1031 | .target = target_info.target, |
| 1032 | .is_native_os = cross_target.isNativeOs(), | |
| 1033 | .dynamic_linker = target_info.dynamic_linker.get(), | |
| 1001 | 1034 | .output_mode = output_mode, |
| 1002 | 1035 | .root_pkg = root_pkg, |
| 1003 | 1036 | .bin_file_dir_path = null, |
| ... | ... | @@ -1008,6 +1041,7 @@ pub fn buildOutputType( |
| 1008 | 1041 | .optimize_mode = build_mode, |
| 1009 | 1042 | .keep_source_files_loaded = zir_out_path != null, |
| 1010 | 1043 | .clang_argv = clang_argv.items, |
| 1044 | .lld_argv = lld_argv.items, | |
| 1011 | 1045 | .lib_dirs = lib_dirs.items, |
| 1012 | 1046 | .rpath_list = rpath_list.items, |
| 1013 | 1047 | .c_source_files = c_source_files.items, |
| ... | ... | @@ -1028,17 +1062,19 @@ pub fn buildOutputType( |
| 1028 | 1062 | .version_script = version_script, |
| 1029 | 1063 | .disable_c_depfile = disable_c_depfile, |
| 1030 | 1064 | .override_soname = override_soname, |
| 1031 | .linker_optimization = linker_optimization, | |
| 1032 | 1065 | .linker_gc_sections = linker_gc_sections, |
| 1033 | 1066 | .linker_allow_shlib_undefined = linker_allow_shlib_undefined, |
| 1034 | 1067 | .linker_bind_global_refs_locally = linker_bind_global_refs_locally, |
| 1035 | 1068 | .linker_z_nodelete = linker_z_nodelete, |
| 1036 | 1069 | .linker_z_defs = linker_z_defs, |
| 1070 | .link_eh_frame_hdr = link_eh_frame_hdr, | |
| 1037 | 1071 | .stack_size_override = stack_size_override, |
| 1038 | 1072 | .strip = strip, |
| 1039 | 1073 | .self_exe_path = self_exe_path, |
| 1040 | 1074 | .rand = &default_prng.random, |
| 1041 | 1075 | .clang_passthrough_mode = arg_mode != .build, |
| 1076 | .version = version, | |
| 1077 | .libc_installation = if (libc_installation) |*lci| lci else null, | |
| 1042 | 1078 | }) catch |err| { |
| 1043 | 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 | 1152 | } |
| 1117 | 1153 | } |
| 1118 | 1154 | |
| 1119 | const 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 | |
| 1155 | pub 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. | |
| 1124 | 1166 | \\ |
| 1125 | 1167 | ; |
| 1126 | 1168 | |
| 1169 | pub 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 | ||
| 1127 | 1211 | pub const usage_fmt = |
| 1128 | \\usage: zig fmt [file]... | |
| 1212 | \\Usage: zig fmt [file]... | |
| 1129 | 1213 | \\ |
| 1130 | 1214 | \\ Formats the input files and modifies them in-place. |
| 1131 | 1215 | \\ 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 | 109 | } |
| 110 | 110 | return false; |
| 111 | 111 | } |
| 112 | ||
| 113 | pub fn cannotDynamicLink(target: std.Target) bool { | |
| 114 | return switch (target.os.tag) { | |
| 115 | .freestanding, .other => true, | |
| 116 | else => false, | |
| 117 | }; | |
| 118 | } | |
| 119 | ||
| 120 | pub 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 | ||
| 130 | pub fn requiresPIE(target: std.Target) bool { | |
| 131 | return target.isAndroid(); | |
| 132 | } | |
| 133 | ||
| 134 | pub 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 | 472 | .root_pkg = root_pkg, |
| 473 | 473 | .keep_source_files_loaded = true, |
| 474 | 474 | .object_format = ofmt, |
| 475 | .is_native_os = case.target.isNativeOs(), | |
| 475 | 476 | }); |
| 476 | 477 | defer module.destroy(); |
| 477 | 478 |