authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-09-30 01:00:06-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-09-30 01:04:30-07:00
log3249e5d952cfcecca999391ffc02cce92ff8fcc4
tree62f8f37e804d2fb2a817e090ce882100b06cc728
parent2a893efae12b2f80d0ff5fd883fdaeb00d9425e4

MachO: add the same workaround for no -r LLD flag support

This is the MachO equivalent for the code added to COFF for doing the file copy when the input and output are both just one object file.

2 files changed, 202 insertions(+), 180 deletions(-)

src/link/MachO.zig+200-178
......@@ -319,7 +319,6 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void {
319319 break :blk full_obj_path;
320320 } else null;
321321
322 const is_obj = self.base.options.output_mode == .Obj;
323322 const is_lib = self.base.options.output_mode == .Lib;
324323 const is_dyn_lib = self.base.options.link_mode == .Dynamic and is_lib;
325324 const is_exe_or_dyn_lib = is_dyn_lib or self.base.options.output_mode == .Exe;
......@@ -391,215 +390,238 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void {
391390 };
392391 }
393392
394 // Create an LLD command line and invoke it.
395 var argv = std.ArrayList([]const u8).init(self.base.allocator);
396 defer argv.deinit();
397 // Even though we're calling LLD as a library it thinks the first argument is its own exe name.
398 try argv.append("lld");
399 if (is_obj) {
400 try argv.append("-r");
401 }
393 const full_out_path = try directory.join(arena, &[_][]const u8{self.base.options.emit.?.sub_path});
402394
403 try argv.append("-error-limit");
404 try argv.append("0");
395 if (self.base.options.output_mode == .Obj) {
396 // LLD's MachO driver does not support the equvialent of `-r` so we do a simple file copy
397 // here. TODO: think carefully about how we can avoid this redundant operation when doing
398 // build-obj. See also the corresponding TODO in linkAsArchive.
399 const the_object_path = blk: {
400 if (self.base.options.objects.len != 0)
401 break :blk self.base.options.objects[0];
405402
406 try argv.append("-demangle");
403 if (comp.c_object_table.count() != 0)
404 break :blk comp.c_object_table.items()[0].key.status.success.object_path;
407405
408 if (self.base.options.rdynamic) {
409 try argv.append("--export-dynamic");
410 }
406 if (module_obj_path) |p|
407 break :blk p;
411408
412 try argv.appendSlice(self.base.options.extra_lld_args);
409 // TODO I think this is unreachable. Audit this situation when solving the above TODO
410 // regarding eliding redundant object -> object transformations.
411 return error.NoObjectsToLink;
412 };
413 // This can happen when using --enable-cache and using the stage1 backend. In this case
414 // we can skip the file copy.
415 if (!mem.eql(u8, the_object_path, full_out_path)) {
416 try fs.cwd().copyFile(the_object_path, fs.cwd(), full_out_path, .{});
417 }
418 } else {
419 // Create an LLD command line and invoke it.
420 var argv = std.ArrayList([]const u8).init(self.base.allocator);
421 defer argv.deinit();
422 // Even though we're calling LLD as a library it thinks the first argument is its own exe name.
423 try argv.append("lld");
413424
414 if (self.base.options.z_nodelete) {
415 try argv.append("-z");
416 try argv.append("nodelete");
417 }
418 if (self.base.options.z_defs) {
419 try argv.append("-z");
420 try argv.append("defs");
421 }
425 try argv.append("-error-limit");
426 try argv.append("0");
422427
423 if (is_dyn_lib) {
424 try argv.append("-static");
425 } else {
426 try argv.append("-dynamic");
427 }
428 try argv.append("-demangle");
428429
429 if (is_dyn_lib) {
430 try argv.append("-dylib");
430 if (self.base.options.rdynamic) {
431 try argv.append("--export-dynamic");
432 }
431433
432 if (self.base.options.version) |ver| {
433 const compat_vers = try std.fmt.allocPrint(arena, "{d}.0.0", .{ver.major});
434 try argv.append("-compatibility_version");
435 try argv.append(compat_vers);
434 try argv.appendSlice(self.base.options.extra_lld_args);
436435
437 const cur_vers = try std.fmt.allocPrint(arena, "{d}.{d}.{d}", .{ ver.major, ver.minor, ver.patch });
438 try argv.append("-current_version");
439 try argv.append(cur_vers);
436 if (self.base.options.z_nodelete) {
437 try argv.append("-z");
438 try argv.append("nodelete");
439 }
440 if (self.base.options.z_defs) {
441 try argv.append("-z");
442 try argv.append("defs");
440443 }
441444
442 // TODO getting an error when running an executable when doing this rpath thing
443 //Buf *dylib_install_name = buf_sprintf("@rpath/lib%s.%" ZIG_PRI_usize ".dylib",
444 // buf_ptr(g->root_out_name), g->version_major);
445 //try argv.append("-install_name");
446 //try argv.append(buf_ptr(dylib_install_name));
447 }
445 if (is_dyn_lib) {
446 try argv.append("-static");
447 } else {
448 try argv.append("-dynamic");
449 }
448450
449 try argv.append("-arch");
450 try argv.append(darwinArchString(target.cpu.arch));
451 if (is_dyn_lib) {
452 try argv.append("-dylib");
451453
452 switch (target.os.tag) {
453 .macosx => {
454 try argv.append("-macosx_version_min");
455 },
456 .ios, .tvos, .watchos => switch (target.cpu.arch) {
457 .i386, .x86_64 => {
458 try argv.append("-ios_simulator_version_min");
454 if (self.base.options.version) |ver| {
455 const compat_vers = try std.fmt.allocPrint(arena, "{d}.0.0", .{ver.major});
456 try argv.append("-compatibility_version");
457 try argv.append(compat_vers);
458
459 const cur_vers = try std.fmt.allocPrint(arena, "{d}.{d}.{d}", .{ ver.major, ver.minor, ver.patch });
460 try argv.append("-current_version");
461 try argv.append(cur_vers);
462 }
463
464 // TODO getting an error when running an executable when doing this rpath thing
465 //Buf *dylib_install_name = buf_sprintf("@rpath/lib%s.%" ZIG_PRI_usize ".dylib",
466 // buf_ptr(g->root_out_name), g->version_major);
467 //try argv.append("-install_name");
468 //try argv.append(buf_ptr(dylib_install_name));
469 }
470
471 try argv.append("-arch");
472 try argv.append(darwinArchString(target.cpu.arch));
473
474 switch (target.os.tag) {
475 .macosx => {
476 try argv.append("-macosx_version_min");
459477 },
460 else => {
461 try argv.append("-iphoneos_version_min");
478 .ios, .tvos, .watchos => switch (target.cpu.arch) {
479 .i386, .x86_64 => {
480 try argv.append("-ios_simulator_version_min");
481 },
482 else => {
483 try argv.append("-iphoneos_version_min");
484 },
462485 },
463 },
464 else => unreachable,
465 }
466 const ver = target.os.version_range.semver.min;
467 const version_string = try std.fmt.allocPrint(arena, "{d}.{d}.{d}", .{ ver.major, ver.minor, ver.patch });
468 try argv.append(version_string);
486 else => unreachable,
487 }
488 const ver = target.os.version_range.semver.min;
489 const version_string = try std.fmt.allocPrint(arena, "{d}.{d}.{d}", .{ ver.major, ver.minor, ver.patch });
490 try argv.append(version_string);
469491
470 try argv.append("-sdk_version");
471 try argv.append(version_string);
492 try argv.append("-sdk_version");
493 try argv.append(version_string);
472494
473 if (target_util.requiresPIE(target) and self.base.options.output_mode == .Exe) {
474 try argv.append("-pie");
475 }
495 if (target_util.requiresPIE(target) and self.base.options.output_mode == .Exe) {
496 try argv.append("-pie");
497 }
476498
477 const full_out_path = try directory.join(arena, &[_][]const u8{self.base.options.emit.?.sub_path});
478 try argv.append("-o");
479 try argv.append(full_out_path);
480
481 // rpaths
482 var rpath_table = std.StringHashMap(void).init(self.base.allocator);
483 defer rpath_table.deinit();
484 for (self.base.options.rpath_list) |rpath| {
485 if ((try rpath_table.fetchPut(rpath, {})) == null) {
486 try argv.append("-rpath");
487 try argv.append(rpath);
499 try argv.append("-o");
500 try argv.append(full_out_path);
501
502 // rpaths
503 var rpath_table = std.StringHashMap(void).init(self.base.allocator);
504 defer rpath_table.deinit();
505 for (self.base.options.rpath_list) |rpath| {
506 if ((try rpath_table.fetchPut(rpath, {})) == null) {
507 try argv.append("-rpath");
508 try argv.append(rpath);
509 }
488510 }
489 }
490 if (is_dyn_lib) {
491 if ((try rpath_table.fetchPut(full_out_path, {})) == null) {
492 try argv.append("-rpath");
493 try argv.append(full_out_path);
511 if (is_dyn_lib) {
512 if ((try rpath_table.fetchPut(full_out_path, {})) == null) {
513 try argv.append("-rpath");
514 try argv.append(full_out_path);
515 }
494516 }
495 }
496517
497 for (self.base.options.lib_dirs) |lib_dir| {
498 try argv.append("-L");
499 try argv.append(lib_dir);
500 }
518 for (self.base.options.lib_dirs) |lib_dir| {
519 try argv.append("-L");
520 try argv.append(lib_dir);
521 }
501522
502 // Positional arguments to the linker such as object files.
503 try argv.appendSlice(self.base.options.objects);
523 // Positional arguments to the linker such as object files.
524 try argv.appendSlice(self.base.options.objects);
504525
505 for (comp.c_object_table.items()) |entry| {
506 try argv.append(entry.key.status.success.object_path);
507 }
508 if (module_obj_path) |p| {
509 try argv.append(p);
510 }
526 for (comp.c_object_table.items()) |entry| {
527 try argv.append(entry.key.status.success.object_path);
528 }
529 if (module_obj_path) |p| {
530 try argv.append(p);
531 }
511532
512 // compiler_rt on darwin is missing some stuff, so we still build it and rely on LinkOnce
513 if (is_exe_or_dyn_lib and !self.base.options.is_compiler_rt_or_libc) {
514 try argv.append(comp.compiler_rt_static_lib.?.full_object_path);
515 }
533 // compiler_rt on darwin is missing some stuff, so we still build it and rely on LinkOnce
534 if (is_exe_or_dyn_lib and !self.base.options.is_compiler_rt_or_libc) {
535 try argv.append(comp.compiler_rt_static_lib.?.full_object_path);
536 }
516537
517 // Shared libraries.
518 const system_libs = self.base.options.system_libs.items();
519 try argv.ensureCapacity(argv.items.len + system_libs.len);
520 for (system_libs) |entry| {
521 const link_lib = entry.key;
522 // By this time, we depend on these libs being dynamically linked libraries and not static libraries
523 // (the check for that needs to be earlier), but they could be full paths to .dylib files, in which
524 // case we want to avoid prepending "-l".
525 const ext = Compilation.classifyFileExt(link_lib);
526 const arg = if (ext == .shared_library) link_lib else try std.fmt.allocPrint(arena, "-l{}", .{link_lib});
527 argv.appendAssumeCapacity(arg);
528 }
538 // Shared libraries.
539 const system_libs = self.base.options.system_libs.items();
540 try argv.ensureCapacity(argv.items.len + system_libs.len);
541 for (system_libs) |entry| {
542 const link_lib = entry.key;
543 // By this time, we depend on these libs being dynamically linked libraries and not static libraries
544 // (the check for that needs to be earlier), but they could be full paths to .dylib files, in which
545 // case we want to avoid prepending "-l".
546 const ext = Compilation.classifyFileExt(link_lib);
547 const arg = if (ext == .shared_library) link_lib else try std.fmt.allocPrint(arena, "-l{}", .{link_lib});
548 argv.appendAssumeCapacity(arg);
549 }
529550
530 // libc++ dep
531 if (!is_obj and self.base.options.link_libcpp) {
532 try argv.append(comp.libcxxabi_static_lib.?.full_object_path);
533 try argv.append(comp.libcxx_static_lib.?.full_object_path);
534 }
551 // libc++ dep
552 if (self.base.options.link_libcpp) {
553 try argv.append(comp.libcxxabi_static_lib.?.full_object_path);
554 try argv.append(comp.libcxx_static_lib.?.full_object_path);
555 }
535556
536 // On Darwin, libSystem has libc in it, but also you have to use it
537 // to make syscalls because the syscall numbers are not documented
538 // and change between versions. So we always link against libSystem.
539 // LLD craps out if you do -lSystem cross compiling, so until that
540 // codebase gets some love from the new maintainers we're left with
541 // this dirty hack.
542 if (self.base.options.is_native_os) {
543 try argv.append("-lSystem");
544 }
557 // On Darwin, libSystem has libc in it, but also you have to use it
558 // to make syscalls because the syscall numbers are not documented
559 // and change between versions. So we always link against libSystem.
560 // LLD craps out if you do -lSystem cross compiling, so until that
561 // codebase gets some love from the new maintainers we're left with
562 // this dirty hack.
563 if (self.base.options.is_native_os) {
564 try argv.append("-lSystem");
565 }
545566
546 for (self.base.options.framework_dirs) |framework_dir| {
547 try argv.append("-F");
548 try argv.append(framework_dir);
549 }
550 for (self.base.options.frameworks) |framework| {
551 try argv.append("-framework");
552 try argv.append(framework);
553 }
567 for (self.base.options.framework_dirs) |framework_dir| {
568 try argv.append("-F");
569 try argv.append(framework_dir);
570 }
571 for (self.base.options.frameworks) |framework| {
572 try argv.append("-framework");
573 try argv.append(framework);
574 }
554575
555 if (allow_shlib_undefined) {
556 try argv.append("-undefined");
557 try argv.append("dynamic_lookup");
558 }
559 if (self.base.options.bind_global_refs_locally) {
560 try argv.append("-Bsymbolic");
561 }
576 if (allow_shlib_undefined) {
577 try argv.append("-undefined");
578 try argv.append("dynamic_lookup");
579 }
580 if (self.base.options.bind_global_refs_locally) {
581 try argv.append("-Bsymbolic");
582 }
562583
563 if (self.base.options.verbose_link) {
564 Compilation.dump_argv(argv.items);
565 }
584 if (self.base.options.verbose_link) {
585 Compilation.dump_argv(argv.items);
586 }
566587
567 const new_argv = try arena.allocSentinel(?[*:0]const u8, argv.items.len, null);
568 for (argv.items) |arg, i| {
569 new_argv[i] = try arena.dupeZ(u8, arg);
570 }
588 const new_argv = try arena.allocSentinel(?[*:0]const u8, argv.items.len, null);
589 for (argv.items) |arg, i| {
590 new_argv[i] = try arena.dupeZ(u8, arg);
591 }
571592
572 var stderr_context: LLDContext = .{
573 .macho = self,
574 .data = std.ArrayList(u8).init(self.base.allocator),
575 };
576 defer stderr_context.data.deinit();
577 var stdout_context: LLDContext = .{
578 .macho = self,
579 .data = std.ArrayList(u8).init(self.base.allocator),
580 };
581 defer stdout_context.data.deinit();
582 const llvm = @import("../llvm.zig");
583 const ok = llvm.Link(
584 .MachO,
585 new_argv.ptr,
586 new_argv.len,
587 append_diagnostic,
588 @ptrToInt(&stdout_context),
589 @ptrToInt(&stderr_context),
590 );
591 if (stderr_context.oom or stdout_context.oom) return error.OutOfMemory;
592 if (stdout_context.data.items.len != 0) {
593 std.log.warn("unexpected LLD stdout: {}", .{stdout_context.data.items});
594 }
595 if (!ok) {
596 // TODO parse this output and surface with the Compilation API rather than
597 // directly outputting to stderr here.
598 std.debug.print("{}", .{stderr_context.data.items});
599 return error.LLDReportedFailure;
600 }
601 if (stderr_context.data.items.len != 0) {
602 std.log.warn("unexpected LLD stderr: {}", .{stderr_context.data.items});
593 var stderr_context: LLDContext = .{
594 .macho = self,
595 .data = std.ArrayList(u8).init(self.base.allocator),
596 };
597 defer stderr_context.data.deinit();
598 var stdout_context: LLDContext = .{
599 .macho = self,
600 .data = std.ArrayList(u8).init(self.base.allocator),
601 };
602 defer stdout_context.data.deinit();
603 const llvm = @import("../llvm.zig");
604 const ok = llvm.Link(
605 .MachO,
606 new_argv.ptr,
607 new_argv.len,
608 append_diagnostic,
609 @ptrToInt(&stdout_context),
610 @ptrToInt(&stderr_context),
611 );
612 if (stderr_context.oom or stdout_context.oom) return error.OutOfMemory;
613 if (stdout_context.data.items.len != 0) {
614 std.log.warn("unexpected LLD stdout: {}", .{stdout_context.data.items});
615 }
616 if (!ok) {
617 // TODO parse this output and surface with the Compilation API rather than
618 // directly outputting to stderr here.
619 std.debug.print("{}", .{stderr_context.data.items});
620 return error.LLDReportedFailure;
621 }
622 if (stderr_context.data.items.len != 0) {
623 std.log.warn("unexpected LLD stderr: {}", .{stderr_context.data.items});
624 }
603625 }
604626
605627 if (!self.base.options.disable_lld_caching) {
src/main.zig+2-2
......@@ -1337,12 +1337,12 @@ fn buildOutputType(
13371337 }
13381338 };
13391339
1340 if (output_mode == .Obj and object_format == .coff) {
1340 if (output_mode == .Obj and (object_format == .coff or object_format == .macho)) {
13411341 const total_obj_count = c_source_files.items.len +
13421342 @boolToInt(root_src_file != null) +
13431343 link_objects.items.len;
13441344 if (total_obj_count > 1) {
1345 fatal("COFF does not support linking multiple objects into one", .{});
1345 fatal("{s} does not support linking multiple objects into one", .{@tagName(object_format)});
13461346 }
13471347 }
13481348