authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-09-25 18:01:35-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-09-25 18:01:35-07:00
log70d7d7e919d7f297e63ca421f6be5925259136e2
tree1635b864f21a78a8d273fa9a946191a8b9796e8b
parent670e7d456c24a8597af6b3809bf1e9ea68746ade

stage2: disable lld caching when output dir is owned by user

Normally when using LLD to link, Zig uses a file named "lld.id" in the same directory as the output binary which contains the hash of the link operation, allowing Zig to skip linking when the hash would be unchanged. In the case that the output binary is being emitted into a directory which is externally modified - essentially anything other than zig-cache - then this flag would be set to disable this machinery to avoid false positives. * Better defaults when using -fno-LLVM * Fix compiler_rt and libc static libraries were getting a .zig extension instead of .a extension. * when using the stage1 backend, put the object file next to the stage1.id file in the cache directory. this prevents an object file from polluting the cwd when using zig from the CLI.

5 files changed, 149 insertions(+), 120 deletions(-)

BRANCH_TODO+5-6
......@@ -1,7 +1,6 @@
1 * make sure that `zig cc -o hello hello.c -target native-native-musl` and `zig build-exe hello.zig -lc -target native-native-musl` will share the same libc build.
2 * zig cc as a preprocessor (-E)
13 * tests passing with -Dskip-non-native
2 * make sure zig cc works
3 - using it as a preprocessor (-E)
4 - try building some software
54 * `-ftime-report`
65 * -fstack-report print stack size diagnostics\n"
76 * -fdump-analysis write analysis.json file with type information\n"
......@@ -15,14 +14,12 @@
1514 * MachO LLD linking
1615 * COFF LLD linking
1716 * WASM LLD linking
18 * skip LLD caching when bin directory is not in the cache (so we don't put `id.txt` into the cwd)
19 (maybe make it an explicit option and have main.zig disable it)
20 - make sure that `zig cc -o hello hello.c -target native-native-musl` and `zig build-exe hello.zig -lc -target native-native-musl` will share the same libc build.
2117 * audit the CLI options for stage2
2218 * audit the base cache hash
2319 * On operating systems that support it, do an execve for `zig test` and `zig run` rather than child process.
2420 * restore error messages for stage2_add_link_lib
2521 * windows CUSTOMBUILD : error : unable to build compiler_rt: FileNotFound [D:\a\1\s\build\zig_install_lib_files.vcxproj]
22 * try building some software with zig cc
2623
2724 * implement proper parsing of clang stderr/stdout and exposing compile errors with the Compilation API
2825 * implement proper parsing of LLD stderr/stdout and exposing compile errors with the Compilation API
......@@ -59,3 +56,5 @@
5956 * close the --pkg-begin --pkg-end Package directory handles
6057 * make std.Progress support multithreaded
6158 * update musl.zig static data to use native path separator in static data rather than replacing '/' at runtime
59 * linking hello world with LLD, lld is silently calling exit(1) instead of reporting ok=false. when run standalone the error message is: ld.lld: error: section [index 3] has a sh_offset (0x57000) + sh_size (0x68) that is greater than the file size (0x57060)
60
src/Compilation.zig+51-33
......@@ -3,10 +3,11 @@ const Compilation = @This();
33const std = @import("std");
44const mem = std.mem;
55const Allocator = std.mem.Allocator;
6const Value = @import("value.zig").Value;
76const assert = std.debug.assert;
87const log = std.log.scoped(.compilation);
98const Target = std.Target;
9
10const Value = @import("value.zig").Value;
1011const target_util = @import("target.zig");
1112const Package = @import("Package.zig");
1213const link = @import("link.zig");
......@@ -286,6 +287,13 @@ pub const InitOptions = struct {
286287 emit_h: ?EmitLoc = null,
287288 link_mode: ?std.builtin.LinkMode = null,
288289 dll_export_fns: ?bool = false,
290 /// Normally when using LLD to link, Zig uses a file named "lld.id" in the
291 /// same directory as the output binary which contains the hash of the link
292 /// operation, allowing Zig to skip linking when the hash would be unchanged.
293 /// In the case that the output binary is being emitted into a directory which
294 /// is externally modified - essentially anything other than zig-cache - then
295 /// this flag would be set to disable this machinery to avoid false positives.
296 disable_lld_caching: bool = false,
289297 object_format: ?std.builtin.ObjectFormat = null,
290298 optimize_mode: std.builtin.Mode = .Debug,
291299 keep_source_files_loaded: bool = false,
......@@ -371,6 +379,26 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {
371379
372380 const ofmt = options.object_format orelse options.target.getObjectFormat();
373381
382 // Make a decision on whether to use LLVM or our own backend.
383 const use_llvm = if (options.use_llvm) |explicit| explicit else blk: {
384 // If we have no zig code to compile, no need for LLVM.
385 if (options.root_pkg == null)
386 break :blk false;
387
388 // If we are the stage1 compiler, we depend on the stage1 c++ llvm backend
389 // to compile zig code.
390 if (build_options.is_stage1)
391 break :blk true;
392
393 // We would want to prefer LLVM for release builds when it is available, however
394 // we don't have an LLVM backend yet :)
395 // We would also want to prefer LLVM for architectures that we don't have self-hosted support for too.
396 break :blk false;
397 };
398 if (!use_llvm and options.machine_code_model != .default) {
399 return error.MachineCodeModelNotSupported;
400 }
401
374402 // Make a decision on whether to use LLD or our own linker.
375403 const use_lld = if (options.use_lld) |explicit| explicit else blk: {
376404 if (!build_options.have_llvm)
......@@ -393,7 +421,7 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {
393421 break :blk true;
394422 }
395423
396 if (build_options.is_stage1) {
424 if (use_llvm) {
397425 // If stage1 generates an object file, self-hosted linker is not
398426 // yet sophisticated enough to handle that.
399427 break :blk options.root_pkg != null;
......@@ -402,25 +430,6 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {
402430 break :blk false;
403431 };
404432
405 // Make a decision on whether to use LLVM or our own backend.
406 const use_llvm = if (options.use_llvm) |explicit| explicit else blk: {
407 // If we have no zig code to compile, no need for LLVM.
408 if (options.root_pkg == null)
409 break :blk false;
410
411 // If we are the stage1 compiler, we depend on the stage1 c++ llvm backend
412 // to compile zig code.
413 if (build_options.is_stage1)
414 break :blk true;
415
416 // We would want to prefer LLVM for release builds when it is available, however
417 // we don't have an LLVM backend yet :)
418 // We would also want to prefer LLVM for architectures that we don't have self-hosted support for too.
419 break :blk false;
420 };
421 if (!use_llvm and options.machine_code_model != .default) {
422 return error.MachineCodeModelNotSupported;
423 }
424433
425434 const link_libc = options.link_libc or
426435 (is_exe_or_dyn_lib and target_util.osRequiresLibC(options.target));
......@@ -720,6 +729,7 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {
720729 .llvm_cpu_features = llvm_cpu_features,
721730 .is_compiler_rt_or_libc = options.is_compiler_rt_or_libc,
722731 .each_lib_rpath = options.each_lib_rpath orelse false,
732 .disable_lld_caching = options.disable_lld_caching,
723733 });
724734 errdefer bin_file.destroy();
725735 comp.* = .{
......@@ -2288,7 +2298,7 @@ pub fn updateSubCompilation(sub_compilation: *Compilation) !void {
22882298 }
22892299}
22902300
2291fn buildStaticLibFromZig(comp: *Compilation, basename: []const u8, out: *?CRTFile) !void {
2301fn buildStaticLibFromZig(comp: *Compilation, src_basename: []const u8, out: *?CRTFile) !void {
22922302 const tracy = trace(@src());
22932303 defer tracy.end();
22942304
......@@ -2304,12 +2314,20 @@ fn buildStaticLibFromZig(comp: *Compilation, basename: []const u8, out: *?CRTFil
23042314 .path = special_path,
23052315 .handle = special_dir,
23062316 },
2307 .root_src_path = basename,
2317 .root_src_path = src_basename,
23082318 };
2319 const root_name = mem.split(src_basename, ".").next().?;
2320 const target = comp.getTarget();
2321 const bin_basename = try std.zig.binNameAlloc(comp.gpa, .{
2322 .root_name = root_name,
2323 .target = target,
2324 .output_mode = .Lib,
2325 });
2326 defer comp.gpa.free(bin_basename);
23092327
23102328 const emit_bin = Compilation.EmitLoc{
23112329 .directory = null, // Put it in the cache directory.
2312 .basename = basename,
2330 .basename = bin_basename,
23132331 };
23142332 const optimize_mode: std.builtin.Mode = blk: {
23152333 if (comp.is_test)
......@@ -2323,8 +2341,8 @@ fn buildStaticLibFromZig(comp: *Compilation, basename: []const u8, out: *?CRTFil
23232341 .global_cache_directory = comp.global_cache_directory,
23242342 .local_cache_directory = comp.global_cache_directory,
23252343 .zig_lib_directory = comp.zig_lib_directory,
2326 .target = comp.getTarget(),
2327 .root_name = mem.split(basename, ".").next().?,
2344 .target = target,
2345 .root_name = root_name,
23282346 .root_pkg = &root_pkg,
23292347 .output_mode = .Lib,
23302348 .rand = comp.rand,
......@@ -2358,7 +2376,9 @@ fn buildStaticLibFromZig(comp: *Compilation, basename: []const u8, out: *?CRTFil
23582376
23592377 assert(out.* == null);
23602378 out.* = Compilation.CRTFile{
2361 .full_object_path = try sub_compilation.bin_file.options.directory.join(comp.gpa, &[_][]const u8{basename}),
2379 .full_object_path = try sub_compilation.bin_file.options.directory.join(comp.gpa, &[_][]const u8{
2380 sub_compilation.bin_file.options.sub_path,
2381 }),
23622382 .lock = sub_compilation.bin_file.toOwnedLock(),
23632383 };
23642384}
......@@ -2461,7 +2481,7 @@ fn updateStage1Module(comp: *Compilation) !void {
24612481 ) orelse return error.OutOfMemory;
24622482
24632483 const stage1_pkg = try createStage1Pkg(arena, "root", mod.root_pkg, null);
2464 const output_dir = comp.bin_file.options.directory.path orelse ".";
2484 const output_dir = directory.path orelse ".";
24652485 const test_filter = comp.test_filter orelse ""[0..0];
24662486 const test_name_prefix = comp.test_name_prefix orelse ""[0..0];
24672487 stage1_module.* = .{
......@@ -2617,13 +2637,11 @@ pub fn build_crt_file(
26172637 try sub_compilation.updateSubCompilation();
26182638
26192639 try comp.crt_files.ensureCapacity(comp.gpa, comp.crt_files.count() + 1);
2620 const artifact_path = if (sub_compilation.bin_file.options.directory.path) |p|
2621 try std.fs.path.join(comp.gpa, &[_][]const u8{ p, basename })
2622 else
2623 try comp.gpa.dupe(u8, basename);
26242640
26252641 comp.crt_files.putAssumeCapacityNoClobber(basename, .{
2626 .full_object_path = artifact_path,
2642 .full_object_path = try sub_compilation.bin_file.options.directory.join(comp.gpa, &[_][]const u8{
2643 sub_compilation.bin_file.options.sub_path,
2644 }),
26272645 .lock = sub_compilation.bin_file.toOwnedLock(),
26282646 });
26292647}
src/link.zig+1
......@@ -66,6 +66,7 @@ pub const Options = struct {
6666 error_return_tracing: bool,
6767 is_compiler_rt_or_libc: bool,
6868 each_lib_rpath: bool,
69 disable_lld_caching: bool,
6970 gc_sections: ?bool = null,
7071 allow_shlib_undefined: ?bool = null,
7172 linker_script: ?[]const u8 = null,
src/link/Elf.zig+91-81
......@@ -23,6 +23,7 @@ const File = link.File;
2323const build_options = @import("build_options");
2424const target_util = @import("../target.zig");
2525const glibc = @import("../glibc.zig");
26const Cache = @import("../Cache.zig");
2627
2728const default_entry_addr = 0x8000000;
2829
......@@ -1225,7 +1226,8 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void {
12251226 const use_stage1 = build_options.is_stage1 and self.base.options.use_llvm;
12261227 if (use_stage1) {
12271228 const obj_basename = try std.fmt.allocPrint(arena, "{}.o", .{self.base.options.root_name});
1228 const full_obj_path = try directory.join(arena, &[_][]const u8{obj_basename});
1229 const o_directory = self.base.options.module.?.zig_cache_artifact_directory;
1230 const full_obj_path = try o_directory.join(arena, &[_][]const u8{obj_basename});
12291231 break :blk full_obj_path;
12301232 }
12311233
......@@ -1235,6 +1237,12 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void {
12351237 break :blk full_obj_path;
12361238 } else null;
12371239
1240 const is_lib = self.base.options.output_mode == .Lib;
1241 const is_dyn_lib = self.base.options.link_mode == .Dynamic and is_lib;
1242 const is_exe_or_dyn_lib = is_dyn_lib or self.base.options.output_mode == .Exe;
1243 const have_dynamic_linker = self.base.options.link_libc and
1244 self.base.options.link_mode == .Dynamic and is_exe_or_dyn_lib;
1245
12381246 // Here we want to determine whether we can save time by not invoking LLD when the
12391247 // output is unchanged. None of the linker options or the object files that are being
12401248 // linked are in the hash that namespaces the directory we are outputting to. Therefore,
......@@ -1245,78 +1253,78 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void {
12451253 // our digest. If so, we can skip linking. Otherwise, we proceed with invoking LLD.
12461254 const id_symlink_basename = "lld.id";
12471255
1248 // We are about to obtain this lock, so here we give other processes a chance first.
1249 self.base.releaseLock();
1250
1251 var ch = comp.cache_parent.obtain();
1252 defer ch.deinit();
1256 var man: Cache.Manifest = undefined;
1257 defer if (!self.base.options.disable_lld_caching) man.deinit();
1258
1259 var digest: [Cache.hex_digest_len]u8 = undefined;
1260
1261 if (!self.base.options.disable_lld_caching) {
1262 man = comp.cache_parent.obtain();
1263
1264 // We are about to obtain this lock, so here we give other processes a chance first.
1265 self.base.releaseLock();
1266
1267 try man.addOptionalFile(self.base.options.linker_script);
1268 try man.addOptionalFile(self.base.options.version_script);
1269 try man.addListOfFiles(self.base.options.objects);
1270 for (comp.c_object_table.items()) |entry| {
1271 _ = try man.addFile(entry.key.status.success.object_path, null);
1272 }
1273 try man.addOptionalFile(module_obj_path);
1274 // We can skip hashing libc and libc++ components that we are in charge of building from Zig
1275 // installation sources because they are always a product of the compiler version + target information.
1276 man.hash.addOptional(self.base.options.stack_size_override);
1277 man.hash.addOptional(self.base.options.gc_sections);
1278 man.hash.add(self.base.options.eh_frame_hdr);
1279 man.hash.add(self.base.options.rdynamic);
1280 man.hash.addListOfBytes(self.base.options.extra_lld_args);
1281 man.hash.addListOfBytes(self.base.options.lib_dirs);
1282 man.hash.addListOfBytes(self.base.options.rpath_list);
1283 man.hash.add(self.base.options.each_lib_rpath);
1284 man.hash.add(self.base.options.is_compiler_rt_or_libc);
1285 man.hash.add(self.base.options.z_nodelete);
1286 man.hash.add(self.base.options.z_defs);
1287 if (self.base.options.link_libc) {
1288 man.hash.add(self.base.options.libc_installation != null);
1289 if (self.base.options.libc_installation) |libc_installation| {
1290 man.hash.addBytes(libc_installation.crt_dir.?);
1291 }
1292 if (have_dynamic_linker) {
1293 man.hash.addOptionalBytes(self.base.options.dynamic_linker);
1294 }
1295 }
1296 if (is_dyn_lib) {
1297 man.hash.addOptionalBytes(self.base.options.override_soname);
1298 man.hash.addOptional(self.base.options.version);
1299 }
1300 man.hash.addListOfBytes(self.base.options.system_libs);
1301 man.hash.addOptional(self.base.options.allow_shlib_undefined);
1302 man.hash.add(self.base.options.bind_global_refs_locally);
12531303
1254 const is_lib = self.base.options.output_mode == .Lib;
1255 const is_dyn_lib = self.base.options.link_mode == .Dynamic and is_lib;
1256 const is_exe_or_dyn_lib = is_dyn_lib or self.base.options.output_mode == .Exe;
1257 const have_dynamic_linker = self.base.options.link_libc and
1258 self.base.options.link_mode == .Dynamic and is_exe_or_dyn_lib;
1304 // We don't actually care whether it's a cache hit or miss; we just need the digest and the lock.
1305 _ = try man.hit();
1306 digest = man.final();
12591307
1260 try ch.addOptionalFile(self.base.options.linker_script);
1261 try ch.addOptionalFile(self.base.options.version_script);
1262 try ch.addListOfFiles(self.base.options.objects);
1263 for (comp.c_object_table.items()) |entry| {
1264 _ = try ch.addFile(entry.key.status.success.object_path, null);
1265 }
1266 try ch.addOptionalFile(module_obj_path);
1267 // We can skip hashing libc and libc++ components that we are in charge of building from Zig
1268 // installation sources because they are always a product of the compiler version + target information.
1269 ch.hash.addOptional(self.base.options.stack_size_override);
1270 ch.hash.addOptional(self.base.options.gc_sections);
1271 ch.hash.add(self.base.options.eh_frame_hdr);
1272 ch.hash.add(self.base.options.rdynamic);
1273 ch.hash.addListOfBytes(self.base.options.extra_lld_args);
1274 ch.hash.addListOfBytes(self.base.options.lib_dirs);
1275 ch.hash.addListOfBytes(self.base.options.rpath_list);
1276 ch.hash.add(self.base.options.each_lib_rpath);
1277 ch.hash.add(self.base.options.is_compiler_rt_or_libc);
1278 ch.hash.add(self.base.options.z_nodelete);
1279 ch.hash.add(self.base.options.z_defs);
1280 if (self.base.options.link_libc) {
1281 ch.hash.add(self.base.options.libc_installation != null);
1282 if (self.base.options.libc_installation) |libc_installation| {
1283 ch.hash.addBytes(libc_installation.crt_dir.?);
1284 }
1285 if (have_dynamic_linker) {
1286 ch.hash.addOptionalBytes(self.base.options.dynamic_linker);
1308 var prev_digest_buf: [digest.len]u8 = undefined;
1309 const prev_digest: []u8 = directory.handle.readLink(id_symlink_basename, &prev_digest_buf) catch |err| blk: {
1310 log.debug("ELF LLD new_digest={} readlink error: {}", .{digest, @errorName(err)});
1311 // Handle this as a cache miss.
1312 break :blk prev_digest_buf[0..0];
1313 };
1314 if (mem.eql(u8, prev_digest, &digest)) {
1315 log.debug("ELF LLD digest={} match - skipping invocation", .{digest});
1316 // Hot diggity dog! The output binary is already there.
1317 self.base.lock = man.toOwnedLock();
1318 return;
12871319 }
1320 log.debug("ELF LLD prev_digest={} new_digest={}", .{prev_digest, digest});
1321
1322 // We are about to change the output file to be different, so we invalidate the build hash now.
1323 directory.handle.deleteFile(id_symlink_basename) catch |err| switch (err) {
1324 error.FileNotFound => {},
1325 else => |e| return e,
1326 };
12881327 }
1289 if (is_dyn_lib) {
1290 ch.hash.addOptionalBytes(self.base.options.override_soname);
1291 ch.hash.addOptional(self.base.options.version);
1292 }
1293 ch.hash.addListOfBytes(self.base.options.system_libs);
1294 ch.hash.addOptional(self.base.options.allow_shlib_undefined);
1295 ch.hash.add(self.base.options.bind_global_refs_locally);
1296
1297 // We don't actually care whether it's a cache hit or miss; we just need the digest and the lock.
1298 _ = try ch.hit();
1299 const digest = ch.final();
1300
1301 var prev_digest_buf: [digest.len]u8 = undefined;
1302 const prev_digest: []u8 = directory.handle.readLink(id_symlink_basename, &prev_digest_buf) catch |err| blk: {
1303 log.debug("ELF LLD new_digest={} readlink error: {}", .{digest, @errorName(err)});
1304 // Handle this as a cache miss.
1305 break :blk prev_digest_buf[0..0];
1306 };
1307 if (mem.eql(u8, prev_digest, &digest)) {
1308 log.debug("ELF LLD digest={} match - skipping invocation", .{digest});
1309 // Hot diggity dog! The output binary is already there.
1310 self.base.lock = ch.toOwnedLock();
1311 return;
1312 }
1313 log.debug("ELF LLD prev_digest={} new_digest={}", .{prev_digest, digest});
1314
1315 // We are about to change the output file to be different, so we invalidate the build hash now.
1316 directory.handle.deleteFile(id_symlink_basename) catch |err| switch (err) {
1317 error.FileNotFound => {},
1318 else => |e| return e,
1319 };
13201328
13211329 const target = self.base.options.target;
13221330 const is_obj = self.base.options.output_mode == .Obj;
......@@ -1620,18 +1628,20 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void {
16201628 std.log.warn("unexpected LLD stderr: {}", .{stderr_context.data.items});
16211629 }
16221630
1623 // Update the dangling symlink with the digest. If it fails we can continue; it only
1624 // means that the next invocation will have an unnecessary cache miss.
1625 directory.handle.symLink(&digest, id_symlink_basename, .{}) catch |err| {
1626 std.log.warn("failed to save linking hash digest symlink: {}", .{@errorName(err)});
1627 };
1628 // Again failure here only means an unnecessary cache miss.
1629 ch.writeManifest() catch |err| {
1630 std.log.warn("failed to write cache manifest when linking: {}", .{ @errorName(err) });
1631 };
1632 // We hang on to this lock so that the output file path can be used without
1633 // other processes clobbering it.
1634 self.base.lock = ch.toOwnedLock();
1631 if (!self.base.options.disable_lld_caching) {
1632 // Update the dangling symlink with the digest. If it fails we can continue; it only
1633 // means that the next invocation will have an unnecessary cache miss.
1634 directory.handle.symLink(&digest, id_symlink_basename, .{}) catch |err| {
1635 std.log.warn("failed to save linking hash digest symlink: {}", .{@errorName(err)});
1636 };
1637 // Again failure here only means an unnecessary cache miss.
1638 man.writeManifest() catch |err| {
1639 std.log.warn("failed to write cache manifest when linking: {}", .{ @errorName(err) });
1640 };
1641 // We hang on to this lock so that the output file path can be used without
1642 // other processes clobbering it.
1643 self.base.lock = man.toOwnedLock();
1644 }
16351645}
16361646
16371647const LLDContext = struct {
src/main.zig+1
......@@ -1425,6 +1425,7 @@ pub fn buildOutputType(
14251425 .test_evented_io = test_evented_io,
14261426 .test_filter = test_filter,
14271427 .test_name_prefix = test_name_prefix,
1428 .disable_lld_caching = !have_enable_cache,
14281429 }) catch |err| {
14291430 fatal("unable to create compilation: {}", .{@errorName(err)});
14301431 };