From 3b26e508638e96997aff3d5ef2c42a917bf86cc8 Mon Sep 17 00:00:00 2001 From: Michael Dusan Date: Thu, 4 Jun 2020 16:20:55 -0400 Subject: [PATCH] macOS: macho ld64.lld fixes * bring `construct_linker_job_macho` to parity with `construct_linker_job_elf` * macho now sets `-error-limit` * macho on macOS now sets `-macosx_version_min` and `-sdk_version` to `10.13` when running `zig0` * macho now detects when `-l` prefix is not needed * macho on macOS detects system libraries in a case-insensitive manner * macho now ads user-specified libraries to linker command-line args when condition `is_native_os != true` * re-ordered some macho args positions to match elf positions closes #5059 closes #5067 --- src-self-hosted/stage2.zig | 2 + src/link.cpp | 112 ++++++++++++++++++++++--------------- src/stage2.cpp | 2 + src/stage2.h | 3 + src/target.cpp | 29 ++++++---- src/util.hpp | 8 +++ 6 files changed, 101 insertions(+), 55 deletions(-) diff --git a/src-self-hosted/stage2.zig b/src-self-hosted/stage2.zig index 91524d7396991766a283fef8797daf364d17a538..d96f27a84b55e6e68af4dc9bdbb17e4c3a2c61c5 100644 --- a/src-self-hosted/stage2.zig +++ b/src-self-hosted/stage2.zig @@ -1271,3 +1271,5 @@ export fn stage2_clang_arg_next(it: *ClangArgIterator) Error { }; return .None; } + +export const stage2_is_zig0 = false; diff --git a/src/link.cpp b/src/link.cpp index 0f27cee9ab7897eda756eadaac40f54024f9c8c5..80baffe51c5bf5c1c7ff95f06bd247bd55ea9e4f 100644 --- a/src/link.cpp +++ b/src/link.cpp @@ -2002,6 +2002,7 @@ static void construct_linker_job_elf(LinkJob *lj) { lj->args.append(buf_ptr(compiler_rt_o_path)); } + // libraries for (size_t i = 0; i < g->link_libs_list.length; i += 1) { LinkLib *link_lib = g->link_libs_list.at(i); if (buf_eql_str(link_lib->name, "c")) { @@ -2662,8 +2663,8 @@ static void construct_linker_job_coff(LinkJob *lj) { static void construct_linker_job_macho(LinkJob *lj) { CodeGen *g = lj->codegen; - // LLD MACH-O has no error limit option. - //lj->args.append("-error-limit=0"); + lj->args.append("-error-limit"); + lj->args.append("0"); lj->args.append("-demangle"); switch (g->linker_gc_sections) { @@ -2736,6 +2737,7 @@ static void construct_linker_job_macho(LinkJob *lj) { lj->args.append("-iphoneos_version_min"); } } + Buf *version_string = buf_sprintf("%d.%d.%d", g->zig_target->glibc_or_darwin_version->major, g->zig_target->glibc_or_darwin_version->minor, @@ -2744,6 +2746,12 @@ static void construct_linker_job_macho(LinkJob *lj) { lj->args.append("-sdk_version"); lj->args.append(buf_ptr(version_string)); + } else if (stage2_is_zig0 && g->zig_target->os == OsMacOSX) { + // running `zig0`; `-pie` requires versions >= 10.5; select 10.13 + lj->args.append("-macosx_version_min"); + lj->args.append("10.13"); + lj->args.append("-sdk_version"); + lj->args.append("10.13"); } if (g->out_type == OutTypeExe) { @@ -2773,64 +2781,58 @@ static void construct_linker_job_macho(LinkJob *lj) { lj->args.append(lib_dir); } + // .o files for (size_t i = 0; i < g->link_objects.length; i += 1) { lj->args.append((const char *)buf_ptr(g->link_objects.at(i))); } - // libc++ dep - if (g->libcpp_link_lib != nullptr && g->out_type != OutTypeObj) { - lj->args.append(build_libcxxabi(g, lj->build_dep_prog_node)); - lj->args.append(build_libcxx(g, lj->build_dep_prog_node)); - } - // compiler_rt on darwin is missing some stuff, so we still build it and rely on LinkOnce if (g->out_type == OutTypeExe || is_dyn_lib) { Buf *compiler_rt_o_path = build_compiler_rt(g, OutTypeLib, lj->build_dep_prog_node); lj->args.append(buf_ptr(compiler_rt_o_path)); } - if (g->zig_target->is_native_os) { - for (size_t lib_i = 0; lib_i < g->link_libs_list.length; lib_i += 1) { - LinkLib *link_lib = g->link_libs_list.at(lib_i); - if (target_is_libc_lib_name(g->zig_target, buf_ptr(link_lib->name))) { - // handled by libSystem - continue; - } - if (strchr(buf_ptr(link_lib->name), '/') == nullptr) { - Buf *arg = buf_sprintf("-l%s", buf_ptr(link_lib->name)); - lj->args.append(buf_ptr(arg)); - } else { - lj->args.append(buf_ptr(link_lib->name)); - } + // libraries + for (size_t lib_i = 0; lib_i < g->link_libs_list.length; lib_i += 1) { + LinkLib *link_lib = g->link_libs_list.at(lib_i); + if (buf_eql_str(link_lib->name, "c")) { + // libc is linked specially + continue; } + if (target_is_libcpp_lib_name(g->zig_target, buf_ptr(link_lib->name))) { + // libc++ is linked specially + continue; + } + if (g->zig_target->is_native_os && target_is_libc_lib_name(g->zig_target, buf_ptr(link_lib->name))) { + // libSystem is linked specially + continue; + } + + Buf *arg; + if (buf_starts_with_str(link_lib->name, "/") || buf_ends_with_str(link_lib->name, ".a") || + buf_ends_with_str(link_lib->name, ".dylib")) + { + arg = link_lib->name; + } else { + arg = buf_sprintf("-l%s", buf_ptr(link_lib->name)); + } + lj->args.append(buf_ptr(arg)); + } + + // libc++ dep + if (g->libcpp_link_lib != nullptr && g->out_type != OutTypeObj) { + lj->args.append(build_libcxxabi(g, lj->build_dep_prog_node)); + lj->args.append(build_libcxx(g, lj->build_dep_prog_node)); + } + + // libc dep + if (g->zig_target->is_native_os || stage2_is_zig0) { // 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 lj->args.append("-lSystem"); } - switch (g->linker_allow_shlib_undefined) { - case OptionalBoolNull: - if (!g->zig_target->is_native_os) { - lj->args.append("-undefined"); - lj->args.append("dynamic_lookup"); - } - break; - case OptionalBoolFalse: - break; - case OptionalBoolTrue: - lj->args.append("-undefined"); - lj->args.append("dynamic_lookup"); - break; - } - switch (g->linker_bind_global_refs_locally) { - case OptionalBoolNull: - case OptionalBoolFalse: - break; - case OptionalBoolTrue: - lj->args.append("-Bsymbolic"); - break; - } for (size_t i = 0; i < g->framework_dirs.length; i += 1) { const char *framework_dir = g->framework_dirs.at(i); @@ -2843,6 +2845,29 @@ static void construct_linker_job_macho(LinkJob *lj) { lj->args.append(buf_ptr(g->darwin_frameworks.at(i))); } + switch (g->linker_allow_shlib_undefined) { + case OptionalBoolNull: + if (!g->zig_target->is_native_os && !stage2_is_zig0) { + // TODO https://github.com/ziglang/zig/issues/5059 + lj->args.append("-undefined"); + lj->args.append("dynamic_lookup"); + } + break; + case OptionalBoolFalse: + break; + case OptionalBoolTrue: + lj->args.append("-undefined"); + lj->args.append("dynamic_lookup"); + break; + } + switch (g->linker_bind_global_refs_locally) { + case OptionalBoolNull: + case OptionalBoolFalse: + break; + case OptionalBoolTrue: + lj->args.append("-Bsymbolic"); + break; + } } static void construct_linker_job(LinkJob *lj) { @@ -2920,7 +2945,6 @@ void codegen_link(CodeGen *g) { construct_linker_job(&lj); - if (g->verbose_link) { for (size_t i = 0; i < lj.args.length; i += 1) { const char *space = (i != 0) ? " " : ""; diff --git a/src/stage2.cpp b/src/stage2.cpp index 44e788b01c0f84e4052bf68a39282a708424f2f8..887ad461bc4ac0cb745b6d9193d1d84a24236605 100644 --- a/src/stage2.cpp +++ b/src/stage2.cpp @@ -322,3 +322,5 @@ enum Error stage2_clang_arg_next(struct Stage2ClangArgIterator *it) { const char *msg = "stage0 called stage2_clang_arg_next"; stage2_panic(msg, strlen(msg)); } + +const bool stage2_is_zig0 = true; diff --git a/src/stage2.h b/src/stage2.h index 0acef166d709c1b76e661249596dde598c9d1809..7875ef26c39ace621f5b9d3ccdd065b60dcc6b8a 100644 --- a/src/stage2.h +++ b/src/stage2.h @@ -379,4 +379,7 @@ ZIG_EXTERN_C void stage2_clang_arg_iterator(struct Stage2ClangArgIterator *it, // ABI warning ZIG_EXTERN_C enum Error stage2_clang_arg_next(struct Stage2ClangArgIterator *it); +// ABI warning +ZIG_EXTERN_C const bool stage2_is_zig0; + #endif diff --git a/src/target.cpp b/src/target.cpp index 73c0924ab8a9364fac7785e6be95bf77a4223e5b..84080ba1fafa990870ce277c95e9124798393962 100644 --- a/src/target.cpp +++ b/src/target.cpp @@ -1209,39 +1209,46 @@ const char *target_libc_generic_name(const ZigTarget *target) { } bool target_is_libc_lib_name(const ZigTarget *target, const char *name) { - if (strcmp(name, "c") == 0) + auto equal = str_eql_str; + if (target->os == OsMacOSX) + equal = str_eql_str_ignore_case; + + if (equal(name, "c")) return true; if (target_abi_is_gnu(target->abi) && target->os == OsWindows) { // mingw-w64 - if (strcmp(name, "m") == 0) + if (equal(name, "m")) return true; return false; } if (target_abi_is_gnu(target->abi) || target_abi_is_musl(target->abi) || target_os_is_darwin(target->os)) { - if (strcmp(name, "m") == 0) + if (equal(name, "m")) return true; - if (strcmp(name, "rt") == 0) + if (equal(name, "rt")) return true; - if (strcmp(name, "pthread") == 0) + if (equal(name, "pthread")) return true; - if (strcmp(name, "crypt") == 0) + if (equal(name, "crypt")) return true; - if (strcmp(name, "util") == 0) + if (equal(name, "util")) return true; - if (strcmp(name, "xnet") == 0) + if (equal(name, "xnet")) return true; - if (strcmp(name, "resolv") == 0) + if (equal(name, "resolv")) return true; - if (strcmp(name, "dl") == 0) + if (equal(name, "dl")) return true; - if (strcmp(name, "util") == 0) + if (equal(name, "util")) return true; } + if (target_os_is_darwin(target->os) && equal(name, "System")) + return true; + return false; } diff --git a/src/util.hpp b/src/util.hpp index ab8f4c8e70507bcc1c99298dc77d752e149ea37a..66efe6dfd1736cfdb7d22023ed14b8d7b3a97c00 100644 --- a/src/util.hpp +++ b/src/util.hpp @@ -96,6 +96,14 @@ static inline bool mem_eql_str(const char *mem, size_t mem_len, const char *str) return mem_eql_mem(mem, mem_len, str, strlen(str)); } +static inline bool str_eql_str(const char *a, const char* b) { + return mem_eql_mem(a, strlen(a), b, strlen(b)); +} + +static inline bool str_eql_str_ignore_case(const char *a, const char* b) { + return mem_eql_mem_ignore_case(a, strlen(a), b, strlen(b)); +} + static inline bool is_power_of_2(uint64_t x) { return x != 0 && ((x & (~x + 1)) == x); } -- 2.54.0