diff options
| author | Andrew Kelley <andrew@ziglang.org> | 2020-09-30 01:00:06 -0700 |
|---|---|---|
| committer | Andrew Kelley <andrew@ziglang.org> | 2020-09-30 01:04:30 -0700 |
| commit | 3249e5d952cfcecca999391ffc02cce92ff8fcc4 (patch) | |
| tree | 62f8f37e804d2fb2a817e090ce882100b06cc728 /src/link | |
| parent | 2a893efae12b2f80d0ff5fd883fdaeb00d9425e4 (diff) | |
| download | zig-3249e5d952cfcecca999391ffc02cce92ff8fcc4.tar.gz zig-3249e5d952cfcecca999391ffc02cce92ff8fcc4.zip | |
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.
Diffstat (limited to 'src/link')
| -rw-r--r-- | src/link/MachO.zig | 378 |
1 files changed, 200 insertions, 178 deletions
diff --git a/src/link/MachO.zig b/src/link/MachO.zig index 4c0be32922..961b64a840 100644 --- a/src/link/MachO.zig +++ b/src/link/MachO.zig @@ -319,7 +319,6 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void { break :blk full_obj_path; } else null; - const is_obj = self.base.options.output_mode == .Obj; const is_lib = self.base.options.output_mode == .Lib; const is_dyn_lib = self.base.options.link_mode == .Dynamic and is_lib; 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 { }; } - // Create an LLD command line and invoke it. - var argv = std.ArrayList([]const u8).init(self.base.allocator); - defer argv.deinit(); - // Even though we're calling LLD as a library it thinks the first argument is its own exe name. - try argv.append("lld"); - if (is_obj) { - try argv.append("-r"); - } + const full_out_path = try directory.join(arena, &[_][]const u8{self.base.options.emit.?.sub_path}); - try argv.append("-error-limit"); - try argv.append("0"); + if (self.base.options.output_mode == .Obj) { + // LLD's MachO driver does not support the equvialent of `-r` so we do a simple file copy + // here. TODO: think carefully about how we can avoid this redundant operation when doing + // build-obj. See also the corresponding TODO in linkAsArchive. + const the_object_path = blk: { + if (self.base.options.objects.len != 0) + break :blk self.base.options.objects[0]; - try argv.append("-demangle"); + if (comp.c_object_table.count() != 0) + break :blk comp.c_object_table.items()[0].key.status.success.object_path; - if (self.base.options.rdynamic) { - try argv.append("--export-dynamic"); - } + if (module_obj_path) |p| + break :blk p; - try argv.appendSlice(self.base.options.extra_lld_args); + // TODO I think this is unreachable. Audit this situation when solving the above TODO + // regarding eliding redundant object -> object transformations. + return error.NoObjectsToLink; + }; + // This can happen when using --enable-cache and using the stage1 backend. In this case + // we can skip the file copy. + if (!mem.eql(u8, the_object_path, full_out_path)) { + try fs.cwd().copyFile(the_object_path, fs.cwd(), full_out_path, .{}); + } + } else { + // Create an LLD command line and invoke it. + var argv = std.ArrayList([]const u8).init(self.base.allocator); + defer argv.deinit(); + // Even though we're calling LLD as a library it thinks the first argument is its own exe name. + try argv.append("lld"); - if (self.base.options.z_nodelete) { - try argv.append("-z"); - try argv.append("nodelete"); - } - if (self.base.options.z_defs) { - try argv.append("-z"); - try argv.append("defs"); - } + try argv.append("-error-limit"); + try argv.append("0"); - if (is_dyn_lib) { - try argv.append("-static"); - } else { - try argv.append("-dynamic"); - } + try argv.append("-demangle"); - if (is_dyn_lib) { - try argv.append("-dylib"); + if (self.base.options.rdynamic) { + try argv.append("--export-dynamic"); + } - if (self.base.options.version) |ver| { - const compat_vers = try std.fmt.allocPrint(arena, "{d}.0.0", .{ver.major}); - try argv.append("-compatibility_version"); - try argv.append(compat_vers); + try argv.appendSlice(self.base.options.extra_lld_args); - const cur_vers = try std.fmt.allocPrint(arena, "{d}.{d}.{d}", .{ ver.major, ver.minor, ver.patch }); - try argv.append("-current_version"); - try argv.append(cur_vers); + if (self.base.options.z_nodelete) { + try argv.append("-z"); + try argv.append("nodelete"); + } + if (self.base.options.z_defs) { + try argv.append("-z"); + try argv.append("defs"); } - // TODO getting an error when running an executable when doing this rpath thing - //Buf *dylib_install_name = buf_sprintf("@rpath/lib%s.%" ZIG_PRI_usize ".dylib", - // buf_ptr(g->root_out_name), g->version_major); - //try argv.append("-install_name"); - //try argv.append(buf_ptr(dylib_install_name)); - } + if (is_dyn_lib) { + try argv.append("-static"); + } else { + try argv.append("-dynamic"); + } - try argv.append("-arch"); - try argv.append(darwinArchString(target.cpu.arch)); + if (is_dyn_lib) { + try argv.append("-dylib"); - switch (target.os.tag) { - .macosx => { - try argv.append("-macosx_version_min"); - }, - .ios, .tvos, .watchos => switch (target.cpu.arch) { - .i386, .x86_64 => { - try argv.append("-ios_simulator_version_min"); + if (self.base.options.version) |ver| { + const compat_vers = try std.fmt.allocPrint(arena, "{d}.0.0", .{ver.major}); + try argv.append("-compatibility_version"); + try argv.append(compat_vers); + + const cur_vers = try std.fmt.allocPrint(arena, "{d}.{d}.{d}", .{ ver.major, ver.minor, ver.patch }); + try argv.append("-current_version"); + try argv.append(cur_vers); + } + + // TODO getting an error when running an executable when doing this rpath thing + //Buf *dylib_install_name = buf_sprintf("@rpath/lib%s.%" ZIG_PRI_usize ".dylib", + // buf_ptr(g->root_out_name), g->version_major); + //try argv.append("-install_name"); + //try argv.append(buf_ptr(dylib_install_name)); + } + + try argv.append("-arch"); + try argv.append(darwinArchString(target.cpu.arch)); + + switch (target.os.tag) { + .macosx => { + try argv.append("-macosx_version_min"); }, - else => { - try argv.append("-iphoneos_version_min"); + .ios, .tvos, .watchos => switch (target.cpu.arch) { + .i386, .x86_64 => { + try argv.append("-ios_simulator_version_min"); + }, + else => { + try argv.append("-iphoneos_version_min"); + }, }, - }, - else => unreachable, - } - const ver = target.os.version_range.semver.min; - const version_string = try std.fmt.allocPrint(arena, "{d}.{d}.{d}", .{ ver.major, ver.minor, ver.patch }); - try argv.append(version_string); + else => unreachable, + } + const ver = target.os.version_range.semver.min; + const version_string = try std.fmt.allocPrint(arena, "{d}.{d}.{d}", .{ ver.major, ver.minor, ver.patch }); + try argv.append(version_string); - try argv.append("-sdk_version"); - try argv.append(version_string); + try argv.append("-sdk_version"); + try argv.append(version_string); - if (target_util.requiresPIE(target) and self.base.options.output_mode == .Exe) { - try argv.append("-pie"); - } + if (target_util.requiresPIE(target) and self.base.options.output_mode == .Exe) { + try argv.append("-pie"); + } - const full_out_path = try directory.join(arena, &[_][]const u8{self.base.options.emit.?.sub_path}); - try argv.append("-o"); - try argv.append(full_out_path); - - // rpaths - var rpath_table = std.StringHashMap(void).init(self.base.allocator); - defer rpath_table.deinit(); - for (self.base.options.rpath_list) |rpath| { - if ((try rpath_table.fetchPut(rpath, {})) == null) { - try argv.append("-rpath"); - try argv.append(rpath); + try argv.append("-o"); + try argv.append(full_out_path); + + // rpaths + var rpath_table = std.StringHashMap(void).init(self.base.allocator); + defer rpath_table.deinit(); + for (self.base.options.rpath_list) |rpath| { + if ((try rpath_table.fetchPut(rpath, {})) == null) { + try argv.append("-rpath"); + try argv.append(rpath); + } } - } - if (is_dyn_lib) { - if ((try rpath_table.fetchPut(full_out_path, {})) == null) { - try argv.append("-rpath"); - try argv.append(full_out_path); + if (is_dyn_lib) { + if ((try rpath_table.fetchPut(full_out_path, {})) == null) { + try argv.append("-rpath"); + try argv.append(full_out_path); + } } - } - for (self.base.options.lib_dirs) |lib_dir| { - try argv.append("-L"); - try argv.append(lib_dir); - } + for (self.base.options.lib_dirs) |lib_dir| { + try argv.append("-L"); + try argv.append(lib_dir); + } - // Positional arguments to the linker such as object files. - try argv.appendSlice(self.base.options.objects); + // Positional arguments to the linker such as object files. + try argv.appendSlice(self.base.options.objects); - for (comp.c_object_table.items()) |entry| { - try argv.append(entry.key.status.success.object_path); - } - if (module_obj_path) |p| { - try argv.append(p); - } + for (comp.c_object_table.items()) |entry| { + try argv.append(entry.key.status.success.object_path); + } + if (module_obj_path) |p| { + try argv.append(p); + } - // compiler_rt on darwin is missing some stuff, so we still build it and rely on LinkOnce - if (is_exe_or_dyn_lib and !self.base.options.is_compiler_rt_or_libc) { - try argv.append(comp.compiler_rt_static_lib.?.full_object_path); - } + // compiler_rt on darwin is missing some stuff, so we still build it and rely on LinkOnce + if (is_exe_or_dyn_lib and !self.base.options.is_compiler_rt_or_libc) { + try argv.append(comp.compiler_rt_static_lib.?.full_object_path); + } - // Shared libraries. - const system_libs = self.base.options.system_libs.items(); - try argv.ensureCapacity(argv.items.len + system_libs.len); - for (system_libs) |entry| { - const link_lib = entry.key; - // By this time, we depend on these libs being dynamically linked libraries and not static libraries - // (the check for that needs to be earlier), but they could be full paths to .dylib files, in which - // case we want to avoid prepending "-l". - const ext = Compilation.classifyFileExt(link_lib); - const arg = if (ext == .shared_library) link_lib else try std.fmt.allocPrint(arena, "-l{}", .{link_lib}); - argv.appendAssumeCapacity(arg); - } + // Shared libraries. + const system_libs = self.base.options.system_libs.items(); + try argv.ensureCapacity(argv.items.len + system_libs.len); + for (system_libs) |entry| { + const link_lib = entry.key; + // By this time, we depend on these libs being dynamically linked libraries and not static libraries + // (the check for that needs to be earlier), but they could be full paths to .dylib files, in which + // case we want to avoid prepending "-l". + const ext = Compilation.classifyFileExt(link_lib); + const arg = if (ext == .shared_library) link_lib else try std.fmt.allocPrint(arena, "-l{}", .{link_lib}); + argv.appendAssumeCapacity(arg); + } - // libc++ dep - if (!is_obj and self.base.options.link_libcpp) { - try argv.append(comp.libcxxabi_static_lib.?.full_object_path); - try argv.append(comp.libcxx_static_lib.?.full_object_path); - } + // libc++ dep + if (self.base.options.link_libcpp) { + try argv.append(comp.libcxxabi_static_lib.?.full_object_path); + try argv.append(comp.libcxx_static_lib.?.full_object_path); + } - // On Darwin, libSystem has libc in it, but also you have to use it - // to make syscalls because the syscall numbers are not documented - // and change between versions. So we always link against libSystem. - // LLD craps out if you do -lSystem cross compiling, so until that - // codebase gets some love from the new maintainers we're left with - // this dirty hack. - if (self.base.options.is_native_os) { - try argv.append("-lSystem"); - } + // On Darwin, libSystem has libc in it, but also you have to use it + // to make syscalls because the syscall numbers are not documented + // and change between versions. So we always link against libSystem. + // LLD craps out if you do -lSystem cross compiling, so until that + // codebase gets some love from the new maintainers we're left with + // this dirty hack. + if (self.base.options.is_native_os) { + try argv.append("-lSystem"); + } - for (self.base.options.framework_dirs) |framework_dir| { - try argv.append("-F"); - try argv.append(framework_dir); - } - for (self.base.options.frameworks) |framework| { - try argv.append("-framework"); - try argv.append(framework); - } + for (self.base.options.framework_dirs) |framework_dir| { + try argv.append("-F"); + try argv.append(framework_dir); + } + for (self.base.options.frameworks) |framework| { + try argv.append("-framework"); + try argv.append(framework); + } - if (allow_shlib_undefined) { - try argv.append("-undefined"); - try argv.append("dynamic_lookup"); - } - if (self.base.options.bind_global_refs_locally) { - try argv.append("-Bsymbolic"); - } + if (allow_shlib_undefined) { + try argv.append("-undefined"); + try argv.append("dynamic_lookup"); + } + if (self.base.options.bind_global_refs_locally) { + try argv.append("-Bsymbolic"); + } - if (self.base.options.verbose_link) { - Compilation.dump_argv(argv.items); - } + if (self.base.options.verbose_link) { + Compilation.dump_argv(argv.items); + } - const new_argv = try arena.allocSentinel(?[*:0]const u8, argv.items.len, null); - for (argv.items) |arg, i| { - new_argv[i] = try arena.dupeZ(u8, arg); - } + const new_argv = try arena.allocSentinel(?[*:0]const u8, argv.items.len, null); + for (argv.items) |arg, i| { + new_argv[i] = try arena.dupeZ(u8, arg); + } - var stderr_context: LLDContext = .{ - .macho = self, - .data = std.ArrayList(u8).init(self.base.allocator), - }; - defer stderr_context.data.deinit(); - var stdout_context: LLDContext = .{ - .macho = self, - .data = std.ArrayList(u8).init(self.base.allocator), - }; - defer stdout_context.data.deinit(); - const llvm = @import("../llvm.zig"); - const ok = llvm.Link( - .MachO, - new_argv.ptr, - new_argv.len, - append_diagnostic, - @ptrToInt(&stdout_context), - @ptrToInt(&stderr_context), - ); - if (stderr_context.oom or stdout_context.oom) return error.OutOfMemory; - if (stdout_context.data.items.len != 0) { - std.log.warn("unexpected LLD stdout: {}", .{stdout_context.data.items}); - } - if (!ok) { - // TODO parse this output and surface with the Compilation API rather than - // directly outputting to stderr here. - std.debug.print("{}", .{stderr_context.data.items}); - return error.LLDReportedFailure; - } - if (stderr_context.data.items.len != 0) { - std.log.warn("unexpected LLD stderr: {}", .{stderr_context.data.items}); + var stderr_context: LLDContext = .{ + .macho = self, + .data = std.ArrayList(u8).init(self.base.allocator), + }; + defer stderr_context.data.deinit(); + var stdout_context: LLDContext = .{ + .macho = self, + .data = std.ArrayList(u8).init(self.base.allocator), + }; + defer stdout_context.data.deinit(); + const llvm = @import("../llvm.zig"); + const ok = llvm.Link( + .MachO, + new_argv.ptr, + new_argv.len, + append_diagnostic, + @ptrToInt(&stdout_context), + @ptrToInt(&stderr_context), + ); + if (stderr_context.oom or stdout_context.oom) return error.OutOfMemory; + if (stdout_context.data.items.len != 0) { + std.log.warn("unexpected LLD stdout: {}", .{stdout_context.data.items}); + } + if (!ok) { + // TODO parse this output and surface with the Compilation API rather than + // directly outputting to stderr here. + std.debug.print("{}", .{stderr_context.data.items}); + return error.LLDReportedFailure; + } + if (stderr_context.data.items.len != 0) { + std.log.warn("unexpected LLD stderr: {}", .{stderr_context.data.items}); + } } if (!self.base.options.disable_lld_caching) { |
