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