authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-09-29 00:26:18-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-09-29 00:26:18-07:00
logfa6d150441d1d8679a77c5e9a6071fa952851376
treeaddec8faed4b228955e4e0a9f095d582f40625e2
parent41f6627521f7ff03962ff73944f5be3722346385

stage2: MachO LLD Linking


4 files changed, 385 insertions(+), 17 deletions(-)

BRANCH_TODO-1
......@@ -1,4 +1,3 @@
1 * MachO LLD linking
21 * audit the CLI options for stage2
32 * audit the base cache hash
43 * On operating systems that support it, do an execve for `zig test` and `zig run` rather than child process.
src/Compilation.zig+8-5
......@@ -558,6 +558,8 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {
558558 break :blk buf.items[0 .. buf.items.len - 1 :0].ptr;
559559 } else null;
560560
561 const strip = options.strip or !target_util.hasDebugInfo(options.target);
562
561563 // We put everything into the cache hash that *cannot be modified during an incremental update*.
562564 // For example, one cannot change the target between updates, but one can change source files,
563565 // so the target goes into the cache hash, but source files do not. This is so that we can
......@@ -586,7 +588,7 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {
586588 cache.hash.add(stack_check);
587589 cache.hash.add(link_mode);
588590 cache.hash.add(function_sections);
589 cache.hash.add(options.strip);
591 cache.hash.add(strip);
590592 cache.hash.add(link_libc);
591593 cache.hash.add(options.link_libcpp);
592594 cache.hash.add(options.output_mode);
......@@ -671,7 +673,7 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {
671673 } else null;
672674 errdefer if (module) |zm| zm.deinit();
673675
674 const error_return_tracing = !options.strip and switch (options.optimize_mode) {
676 const error_return_tracing = !strip and switch (options.optimize_mode) {
675677 .Debug, .ReleaseSafe => true,
676678 .ReleaseFast, .ReleaseSmall => false,
677679 };
......@@ -751,7 +753,7 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {
751753 .system_libs = system_libs,
752754 .lib_dirs = options.lib_dirs,
753755 .rpath_list = options.rpath_list,
754 .strip = options.strip,
756 .strip = strip,
755757 .is_native_os = options.is_native_os,
756758 .function_sections = options.function_sections orelse false,
757759 .allow_shlib_undefined = options.linker_allow_shlib_undefined,
......@@ -2415,10 +2417,11 @@ fn buildStaticLibFromZig(comp: *Compilation, src_basename: []const u8, out: *?CR
24152417 };
24162418 const root_name = mem.split(src_basename, ".").next().?;
24172419 const target = comp.getTarget();
2420 const output_mode: std.builtin.OutputMode = if (target.cpu.arch.isWasm()) .Obj else .Lib;
24182421 const bin_basename = try std.zig.binNameAlloc(comp.gpa, .{
24192422 .root_name = root_name,
24202423 .target = target,
2421 .output_mode = .Obj,
2424 .output_mode = output_mode,
24222425 });
24232426 defer comp.gpa.free(bin_basename);
24242427
......@@ -2441,7 +2444,7 @@ fn buildStaticLibFromZig(comp: *Compilation, src_basename: []const u8, out: *?CR
24412444 .target = target,
24422445 .root_name = root_name,
24432446 .root_pkg = &root_pkg,
2444 .output_mode = .Obj,
2447 .output_mode = output_mode,
24452448 .rand = comp.rand,
24462449 .libc_installation = comp.bin_file.options.libc_installation,
24472450 .emit_bin = emit_bin,
src/link/Elf.zig+8-10
......@@ -1241,6 +1241,7 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void {
12411241 break :blk full_obj_path;
12421242 } else null;
12431243
1244 const is_obj = self.base.options.output_mode == .Obj;
12441245 const is_lib = self.base.options.output_mode == .Lib;
12451246 const is_dyn_lib = self.base.options.link_mode == .Dynamic and is_lib;
12461247 const is_exe_or_dyn_lib = is_dyn_lib or self.base.options.output_mode == .Exe;
......@@ -1248,6 +1249,9 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void {
12481249 self.base.options.link_mode == .Dynamic and is_exe_or_dyn_lib;
12491250 const link_in_crt = self.base.options.link_libc and self.base.options.output_mode == .Exe;
12501251 const target = self.base.options.target;
1252 const gc_sections = self.base.options.gc_sections orelse !is_obj;
1253 const stack_size = self.base.options.stack_size_override orelse 16777216;
1254 const allow_shlib_undefined = self.base.options.allow_shlib_undefined orelse !self.base.options.is_native_os;
12511255
12521256 // Here we want to determine whether we can save time by not invoking LLD when the
12531257 // output is unchanged. None of the linker options or the object files that are being
......@@ -1279,8 +1283,8 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void {
12791283 try man.addOptionalFile(module_obj_path);
12801284 // We can skip hashing libc and libc++ components that we are in charge of building from Zig
12811285 // installation sources because they are always a product of the compiler version + target information.
1282 man.hash.addOptional(self.base.options.stack_size_override);
1283 man.hash.addOptional(self.base.options.gc_sections);
1286 man.hash.add(stack_size);
1287 man.hash.add(gc_sections);
12841288 man.hash.add(self.base.options.eh_frame_hdr);
12851289 man.hash.add(self.base.options.rdynamic);
12861290 man.hash.addListOfBytes(self.base.options.extra_lld_args);
......@@ -1304,7 +1308,7 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void {
13041308 man.hash.addOptional(self.base.options.version);
13051309 }
13061310 man.hash.addStringSet(self.base.options.system_libs);
1307 man.hash.addOptional(self.base.options.allow_shlib_undefined);
1311 man.hash.add(allow_shlib_undefined);
13081312 man.hash.add(self.base.options.bind_global_refs_locally);
13091313
13101314 // We don't actually care whether it's a cache hit or miss; we just need the digest and the lock.
......@@ -1332,8 +1336,6 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void {
13321336 };
13331337 }
13341338
1335 const is_obj = self.base.options.output_mode == .Obj;
1336
13371339 // Create an LLD command line and invoke it.
13381340 var argv = std.ArrayList([]const u8).init(self.base.allocator);
13391341 defer argv.deinit();
......@@ -1347,9 +1349,7 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void {
13471349
13481350 if (self.base.options.output_mode == .Exe) {
13491351 try argv.append("-z");
1350 const stack_size = self.base.options.stack_size_override orelse 16777216;
1351 const arg = try std.fmt.allocPrint(arena, "stack-size={}", .{stack_size});
1352 try argv.append(arg);
1352 try argv.append(try std.fmt.allocPrint(arena, "stack-size={}", .{stack_size}));
13531353 }
13541354
13551355 if (self.base.options.linker_script) |linker_script| {
......@@ -1357,7 +1357,6 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void {
13571357 try argv.append(linker_script);
13581358 }
13591359
1360 const gc_sections = self.base.options.gc_sections orelse !is_obj;
13611360 if (gc_sections) {
13621361 try argv.append("--gc-sections");
13631362 }
......@@ -1577,7 +1576,6 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void {
15771576 }
15781577 }
15791578
1580 const allow_shlib_undefined = self.base.options.allow_shlib_undefined orelse !self.base.options.is_native_os;
15811579 if (allow_shlib_undefined) {
15821580 try argv.append("--allow-shlib-undefined");
15831581 }
src/link/MachO.zig+369-1
......@@ -17,6 +17,8 @@ const Module = @import("../Module.zig");
1717const Compilation = @import("../Compilation.zig");
1818const link = @import("../link.zig");
1919const File = link.File;
20const Cache = @import("../Cache.zig");
21const target_util = @import("../target.zig");
2022
2123pub const base_tag: File.Tag = File.Tag.macho;
2224
......@@ -180,8 +182,12 @@ pub fn createEmpty(gpa: *Allocator, options: link.Options) !*MachO {
180182
181183pub fn flush(self: *MachO, comp: *Compilation) !void {
182184 if (build_options.have_llvm and self.base.options.use_lld) {
183 return error.MachOLLDLinkingUnimplemented;
185 return self.linkWithLLD(comp);
184186 } else {
187 switch (self.base.options.effectiveOutputMode()) {
188 .Exe, .Obj => {},
189 .Lib => return error.TODOImplementWritingLibFiles,
190 }
185191 return self.flushModule(comp);
186192 }
187193}
......@@ -282,6 +288,368 @@ pub fn flushModule(self: *MachO, comp: *Compilation) !void {
282288 }
283289}
284290
291fn linkWithLLD(self: *MachO, comp: *Compilation) !void {
292 const tracy = trace(@src());
293 defer tracy.end();
294
295 var arena_allocator = std.heap.ArenaAllocator.init(self.base.allocator);
296 defer arena_allocator.deinit();
297 const arena = &arena_allocator.allocator;
298
299 const directory = self.base.options.emit.?.directory; // Just an alias to make it shorter to type.
300
301 // If there is no Zig code to compile, then we should skip flushing the output file because it
302 // will not be part of the linker line anyway.
303 const module_obj_path: ?[]const u8 = if (self.base.options.module) |module| blk: {
304 const use_stage1 = build_options.is_stage1 and self.base.options.use_llvm;
305 if (use_stage1) {
306 const obj_basename = try std.zig.binNameAlloc(arena, .{
307 .root_name = self.base.options.root_name,
308 .target = self.base.options.target,
309 .output_mode = .Obj,
310 });
311 const o_directory = self.base.options.module.?.zig_cache_artifact_directory;
312 const full_obj_path = try o_directory.join(arena, &[_][]const u8{obj_basename});
313 break :blk full_obj_path;
314 }
315
316 try self.flushModule(comp);
317 const obj_basename = self.base.intermediary_basename.?;
318 const full_obj_path = try directory.join(arena, &[_][]const u8{obj_basename});
319 break :blk full_obj_path;
320 } else null;
321
322 const is_obj = self.base.options.output_mode == .Obj;
323 const is_lib = self.base.options.output_mode == .Lib;
324 const is_dyn_lib = self.base.options.link_mode == .Dynamic and is_lib;
325 const is_exe_or_dyn_lib = is_dyn_lib or self.base.options.output_mode == .Exe;
326 const have_dynamic_linker = self.base.options.link_libc and
327 self.base.options.link_mode == .Dynamic and is_exe_or_dyn_lib;
328 const link_in_crt = self.base.options.link_libc and self.base.options.output_mode == .Exe;
329 const target = self.base.options.target;
330 const stack_size = self.base.options.stack_size_override orelse 16777216;
331 const allow_shlib_undefined = self.base.options.allow_shlib_undefined orelse !self.base.options.is_native_os;
332
333 const id_symlink_basename = "lld.id";
334
335 var man: Cache.Manifest = undefined;
336 defer if (!self.base.options.disable_lld_caching) man.deinit();
337
338 var digest: [Cache.hex_digest_len]u8 = undefined;
339
340 if (!self.base.options.disable_lld_caching) {
341 man = comp.cache_parent.obtain();
342
343 // We are about to obtain this lock, so here we give other processes a chance first.
344 self.base.releaseLock();
345
346 try man.addOptionalFile(self.base.options.linker_script);
347 try man.addOptionalFile(self.base.options.version_script);
348 try man.addListOfFiles(self.base.options.objects);
349 for (comp.c_object_table.items()) |entry| {
350 _ = try man.addFile(entry.key.status.success.object_path, null);
351 }
352 try man.addOptionalFile(module_obj_path);
353 // We can skip hashing libc and libc++ components that we are in charge of building from Zig
354 // installation sources because they are always a product of the compiler version + target information.
355 man.hash.add(stack_size);
356 man.hash.add(self.base.options.rdynamic);
357 man.hash.addListOfBytes(self.base.options.extra_lld_args);
358 man.hash.addListOfBytes(self.base.options.lib_dirs);
359 man.hash.addListOfBytes(self.base.options.framework_dirs);
360 man.hash.addListOfBytes(self.base.options.frameworks);
361 man.hash.addListOfBytes(self.base.options.rpath_list);
362 man.hash.add(self.base.options.is_compiler_rt_or_libc);
363 man.hash.add(self.base.options.z_nodelete);
364 man.hash.add(self.base.options.z_defs);
365 if (is_dyn_lib) {
366 man.hash.addOptional(self.base.options.version);
367 }
368 man.hash.addStringSet(self.base.options.system_libs);
369 man.hash.add(allow_shlib_undefined);
370 man.hash.add(self.base.options.bind_global_refs_locally);
371
372 // We don't actually care whether it's a cache hit or miss; we just need the digest and the lock.
373 _ = try man.hit();
374 digest = man.final();
375
376 var prev_digest_buf: [digest.len]u8 = undefined;
377 const prev_digest: []u8 = directory.handle.readLink(id_symlink_basename, &prev_digest_buf) catch |err| blk: {
378 log.debug("MachO LLD new_digest={} readlink error: {}", .{ digest, @errorName(err) });
379 // Handle this as a cache miss.
380 break :blk prev_digest_buf[0..0];
381 };
382 if (mem.eql(u8, prev_digest, &digest)) {
383 log.debug("MachO LLD digest={} match - skipping invocation", .{digest});
384 // Hot diggity dog! The output binary is already there.
385 self.base.lock = man.toOwnedLock();
386 return;
387 }
388 log.debug("MachO LLD prev_digest={} new_digest={}", .{ prev_digest, digest });
389
390 // We are about to change the output file to be different, so we invalidate the build hash now.
391 directory.handle.deleteFile(id_symlink_basename) catch |err| switch (err) {
392 error.FileNotFound => {},
393 else => |e| return e,
394 };
395 }
396
397 // Create an LLD command line and invoke it.
398 var argv = std.ArrayList([]const u8).init(self.base.allocator);
399 defer argv.deinit();
400 // Even though we're calling LLD as a library it thinks the first argument is its own exe name.
401 try argv.append("lld");
402 if (is_obj) {
403 try argv.append("-r");
404 }
405
406 try argv.append("-error-limit");
407 try argv.append("0");
408
409 try argv.append("-demangle");
410
411 if (self.base.options.rdynamic) {
412 try argv.append("--export-dynamic");
413 }
414
415 try argv.appendSlice(self.base.options.extra_lld_args);
416
417 if (self.base.options.z_nodelete) {
418 try argv.append("-z");
419 try argv.append("nodelete");
420 }
421 if (self.base.options.z_defs) {
422 try argv.append("-z");
423 try argv.append("defs");
424 }
425
426 if (is_dyn_lib) {
427 try argv.append("-static");
428 } else {
429 try argv.append("-dynamic");
430 }
431
432 if (is_dyn_lib) {
433 try argv.append("-dylib");
434
435 if (self.base.options.version) |ver| {
436 const compat_vers = try std.fmt.allocPrint(arena, "{d}.0.0", .{ver.major});
437 try argv.append("-compatibility_version");
438 try argv.append(compat_vers);
439
440 const cur_vers = try std.fmt.allocPrint(arena, "{d}.{d}.{d}", .{ ver.major, ver.minor, ver.patch });
441 try argv.append("-current_version");
442 try argv.append(cur_vers);
443 }
444
445 // TODO getting an error when running an executable when doing this rpath thing
446 //Buf *dylib_install_name = buf_sprintf("@rpath/lib%s.%" ZIG_PRI_usize ".dylib",
447 // buf_ptr(g->root_out_name), g->version_major);
448 //try argv.append("-install_name");
449 //try argv.append(buf_ptr(dylib_install_name));
450 }
451
452 try argv.append("-arch");
453 try argv.append(darwinArchString(target.cpu.arch));
454
455 switch (target.os.tag) {
456 .macosx => {
457 try argv.append("-macosx_version_min");
458 },
459 .ios, .tvos, .watchos => switch (target.cpu.arch) {
460 .i386, .x86_64 => {
461 try argv.append("-ios_simulator_version_min");
462 },
463 else => {
464 try argv.append("-iphoneos_version_min");
465 },
466 },
467 else => unreachable,
468 }
469 const ver = target.os.version_range.semver.min;
470 const version_string = try std.fmt.allocPrint(arena, "{d}.{d}.{d}", .{ ver.major, ver.minor, ver.patch });
471 try argv.append(version_string);
472
473 try argv.append("-sdk_version");
474 try argv.append(version_string);
475
476 if (target_util.requiresPIE(target) and self.base.options.output_mode == .Exe) {
477 try argv.append("-pie");
478 }
479
480 const full_out_path = try directory.join(arena, &[_][]const u8{self.base.options.emit.?.sub_path});
481 try argv.append("-o");
482 try argv.append(full_out_path);
483
484 // rpaths
485 var rpath_table = std.StringHashMap(void).init(self.base.allocator);
486 defer rpath_table.deinit();
487 for (self.base.options.rpath_list) |rpath| {
488 if ((try rpath_table.fetchPut(rpath, {})) == null) {
489 try argv.append("-rpath");
490 try argv.append(rpath);
491 }
492 }
493 if (is_dyn_lib) {
494 if ((try rpath_table.fetchPut(full_out_path, {})) == null) {
495 try argv.append("-rpath");
496 try argv.append(full_out_path);
497 }
498 }
499
500 for (self.base.options.lib_dirs) |lib_dir| {
501 try argv.append("-L");
502 try argv.append(lib_dir);
503 }
504
505 // Positional arguments to the linker such as object files.
506 try argv.appendSlice(self.base.options.objects);
507
508 for (comp.c_object_table.items()) |entry| {
509 try argv.append(entry.key.status.success.object_path);
510 }
511 if (module_obj_path) |p| {
512 try argv.append(p);
513 }
514
515 // compiler_rt on darwin is missing some stuff, so we still build it and rely on LinkOnce
516 if (is_exe_or_dyn_lib and !self.base.options.is_compiler_rt_or_libc) {
517 try argv.append(comp.compiler_rt_static_lib.?.full_object_path);
518 }
519
520 // Shared libraries.
521 const system_libs = self.base.options.system_libs.items();
522 try argv.ensureCapacity(argv.items.len + system_libs.len);
523 for (system_libs) |entry| {
524 const link_lib = entry.key;
525 // By this time, we depend on these libs being dynamically linked libraries and not static libraries
526 // (the check for that needs to be earlier), but they could be full paths to .dylib files, in which
527 // case we want to avoid prepending "-l".
528 const ext = Compilation.classifyFileExt(link_lib);
529 const arg = if (ext == .shared_library) link_lib else try std.fmt.allocPrint(arena, "-l{}", .{link_lib});
530 argv.appendAssumeCapacity(arg);
531 }
532
533 // libc++ dep
534 if (!is_obj and self.base.options.link_libcpp) {
535 try argv.append(comp.libcxxabi_static_lib.?.full_object_path);
536 try argv.append(comp.libcxx_static_lib.?.full_object_path);
537 }
538
539 // On Darwin, libSystem has libc in it, but also you have to use it
540 // to make syscalls because the syscall numbers are not documented
541 // and change between versions. So we always link against libSystem.
542 // LLD craps out if you do -lSystem cross compiling, so until that
543 // codebase gets some love from the new maintainers we're left with
544 // this dirty hack.
545 if (self.base.options.is_native_os) {
546 try argv.append("-lSystem");
547 }
548
549 for (self.base.options.framework_dirs) |framework_dir| {
550 try argv.append("-F");
551 try argv.append(framework_dir);
552 }
553 for (self.base.options.frameworks) |framework| {
554 try argv.append("-framework");
555 try argv.append(framework);
556 }
557
558 if (allow_shlib_undefined) {
559 try argv.append("-undefined");
560 try argv.append("dynamic_lookup");
561 }
562 if (self.base.options.bind_global_refs_locally) {
563 try argv.append("-Bsymbolic");
564 }
565
566 if (self.base.options.verbose_link) {
567 Compilation.dump_argv(argv.items);
568 }
569
570 // TODO allocSentinel crashed stage1 so this is working around it.
571 const new_argv_with_sentinel = try arena.alloc(?[*:0]const u8, argv.items.len + 1);
572 new_argv_with_sentinel[argv.items.len] = null;
573 const new_argv = new_argv_with_sentinel[0..argv.items.len :null];
574 for (argv.items) |arg, i| {
575 new_argv[i] = try arena.dupeZ(u8, arg);
576 }
577
578 var stderr_context: LLDContext = .{
579 .macho = self,
580 .data = std.ArrayList(u8).init(self.base.allocator),
581 };
582 defer stderr_context.data.deinit();
583 var stdout_context: LLDContext = .{
584 .macho = self,
585 .data = std.ArrayList(u8).init(self.base.allocator),
586 };
587 defer stdout_context.data.deinit();
588 const llvm = @import("../llvm.zig");
589 const ok = llvm.Link(
590 .MachO,
591 new_argv.ptr,
592 new_argv.len,
593 append_diagnostic,
594 @ptrToInt(&stdout_context),
595 @ptrToInt(&stderr_context),
596 );
597 if (stderr_context.oom or stdout_context.oom) return error.OutOfMemory;
598 if (stdout_context.data.items.len != 0) {
599 std.log.warn("unexpected LLD stdout: {}", .{stdout_context.data.items});
600 }
601 if (!ok) {
602 // TODO parse this output and surface with the Compilation API rather than
603 // directly outputting to stderr here.
604 std.debug.print("{}", .{stderr_context.data.items});
605 return error.LLDReportedFailure;
606 }
607 if (stderr_context.data.items.len != 0) {
608 std.log.warn("unexpected LLD stderr: {}", .{stderr_context.data.items});
609 }
610
611 if (!self.base.options.disable_lld_caching) {
612 // Update the dangling symlink with the digest. If it fails we can continue; it only
613 // means that the next invocation will have an unnecessary cache miss.
614 directory.handle.symLink(&digest, id_symlink_basename, .{}) catch |err| {
615 std.log.warn("failed to save linking hash digest symlink: {}", .{@errorName(err)});
616 };
617 // Again failure here only means an unnecessary cache miss.
618 man.writeManifest() catch |err| {
619 std.log.warn("failed to write cache manifest when linking: {}", .{@errorName(err)});
620 };
621 // We hang on to this lock so that the output file path can be used without
622 // other processes clobbering it.
623 self.base.lock = man.toOwnedLock();
624 }
625}
626
627const LLDContext = struct {
628 data: std.ArrayList(u8),
629 macho: *MachO,
630 oom: bool = false,
631};
632
633fn append_diagnostic(context: usize, ptr: [*]const u8, len: usize) callconv(.C) void {
634 const lld_context = @intToPtr(*LLDContext, context);
635 const msg = ptr[0..len];
636 lld_context.data.appendSlice(msg) catch |err| switch (err) {
637 error.OutOfMemory => lld_context.oom = true,
638 };
639}
640
641fn darwinArchString(arch: std.Target.Cpu.Arch) []const u8 {
642 return switch (arch) {
643 .aarch64, .aarch64_be, .aarch64_32 => "arm64",
644 .thumb, .arm => "arm",
645 .thumbeb, .armeb => "armeb",
646 .powerpc => "ppc",
647 .powerpc64 => "ppc64",
648 .powerpc64le => "ppc64le",
649 else => @tagName(arch),
650 };
651}
652
285653pub fn deinit(self: *MachO) void {
286654 self.offset_table.deinit(self.base.allocator);
287655 self.string_table.deinit(self.base.allocator);