| ... | ... | @@ -17,6 +17,8 @@ const Module = @import("../Module.zig"); |
| 17 | 17 | const Compilation = @import("../Compilation.zig"); |
| 18 | 18 | const link = @import("../link.zig"); |
| 19 | 19 | const File = link.File; |
| 20 | const Cache = @import("../Cache.zig"); |
| 21 | const target_util = @import("../target.zig"); |
| 20 | 22 | |
| 21 | 23 | pub const base_tag: File.Tag = File.Tag.macho; |
| 22 | 24 | |
| ... | ... | @@ -180,8 +182,12 @@ pub fn createEmpty(gpa: *Allocator, options: link.Options) !*MachO { |
| 180 | 182 | |
| 181 | 183 | pub fn flush(self: *MachO, comp: *Compilation) !void { |
| 182 | 184 | if (build_options.have_llvm and self.base.options.use_lld) { |
| 183 | | return error.MachOLLDLinkingUnimplemented; |
| 185 | return self.linkWithLLD(comp); |
| 184 | 186 | } else { |
| 187 | switch (self.base.options.effectiveOutputMode()) { |
| 188 | .Exe, .Obj => {}, |
| 189 | .Lib => return error.TODOImplementWritingLibFiles, |
| 190 | } |
| 185 | 191 | return self.flushModule(comp); |
| 186 | 192 | } |
| 187 | 193 | } |
| ... | ... | @@ -282,6 +288,368 @@ pub fn flushModule(self: *MachO, comp: *Compilation) !void { |
| 282 | 288 | } |
| 283 | 289 | } |
| 284 | 290 | |
| 291 | fn 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 | |
| 627 | const LLDContext = struct { |
| 628 | data: std.ArrayList(u8), |
| 629 | macho: *MachO, |
| 630 | oom: bool = false, |
| 631 | }; |
| 632 | |
| 633 | fn 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 | |
| 641 | fn 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 | |
| 285 | 653 | pub fn deinit(self: *MachO) void { |
| 286 | 654 | self.offset_table.deinit(self.base.allocator); |
| 287 | 655 | self.string_table.deinit(self.base.allocator); |