Commit 55a0016221

Andrew Kelley <andrew@ziglang.org>
2019-03-05 21:54:08
dynamic linker path is independent from libc installation
1 parent 0d10ab0
src/all_types.hpp
@@ -1851,6 +1851,7 @@ struct CodeGen {
     ZigPackage *root_package;
     Buf *zig_lib_dir;
     Buf *zig_std_dir;
+    Buf *dynamic_linker_path;
 
     const char **llvm_argv;
     size_t llvm_argv_len;
src/codegen.cpp
@@ -257,6 +257,10 @@ void codegen_set_out_name(CodeGen *g, Buf *out_name) {
     g->root_out_name = out_name;
 }
 
+void codegen_set_dynamic_linker(CodeGen *g, Buf *dynamic_linker_path) {
+    g->dynamic_linker_path = dynamic_linker_path;
+}
+
 void codegen_add_lib_dir(CodeGen *g, const char *dir) {
     g->lib_dirs.append(dir);
 }
@@ -7908,6 +7912,51 @@ static void init(CodeGen *g) {
     }
 }
 
+static void detect_dynamic_linker(CodeGen *g) {
+    if (g->dynamic_linker_path != nullptr)
+        return;
+
+    if (g->zig_target->is_native) {
+        // target_dynamic_linker is usually correct. However on some systems, such as NixOS
+        // it will be incorrect. See if we can do better by looking at what zig's own
+        // dynamic linker path is.
+        g->dynamic_linker_path = get_self_dynamic_linker_path();
+        if (g->dynamic_linker_path != nullptr)
+            return;
+
+        // If Zig is statically linked, such as via distributed binary static builds, the above
+        // trick won't work. What are we left with? Try to run the system C compiler and get
+        // it to tell us the dynamic linker path
+#if defined(ZIG_OS_LINUX)
+        {
+            Error err;
+            static const char *dyn_tests[] = {
+#if defined(ZIG_ARCH_X86_64)
+                "ld-linux-x86-64.so.2",
+                "ld-musl-x86_64.so.1",
+#endif
+            };
+            Buf *result = buf_alloc();
+            for (size_t i = 0; i < array_length(dyn_tests); i += 1) {
+                const char *lib_name = dyn_tests[i];
+                if ((err = zig_libc_cc_print_file_name(lib_name, result, false, true))) {
+                    if (err != ErrorCCompilerCannotFindFile) {
+                        fprintf(stderr, "Unable to detect native dynamic linker: %s\n", err_str(err));
+                        exit(1);
+                    }
+                    continue;
+                }
+                g->dynamic_linker_path = result;
+                return;
+            }
+        }
+#endif
+    }
+
+    // Otherwise go with the standard linker path.
+    g->dynamic_linker_path = buf_create_from_str(target_dynamic_linker(g->zig_target));
+}
+
 static void detect_libc(CodeGen *g) {
     Error err;
 
@@ -9029,8 +9078,8 @@ static Error check_cache(CodeGen *g, Buf *manifest_dir, Buf *digest) {
         cache_buf(ch, &g->libc->crt_dir);
         cache_buf(ch, &g->libc->msvc_lib_dir);
         cache_buf(ch, &g->libc->kernel32_lib_dir);
-        cache_buf(ch, &g->libc->dynamic_linker_path);
     }
+    cache_buf(ch, g->dynamic_linker_path);
 
     gen_c_objects(g);
 
@@ -9132,6 +9181,7 @@ void codegen_build_and_link(CodeGen *g) {
     assert(g->out_type != OutTypeUnknown);
 
     detect_libc(g);
+    detect_dynamic_linker(g);
 
     Buf *artifact_dir = buf_alloc();
     Buf digest = BUF_INIT;
src/codegen.hpp
@@ -29,6 +29,7 @@ void codegen_set_is_static(CodeGen *codegen, bool is_static);
 void codegen_set_strip(CodeGen *codegen, bool strip);
 void codegen_set_errmsg_color(CodeGen *codegen, ErrColor err_color);
 void codegen_set_out_name(CodeGen *codegen, Buf *out_name);
+void codegen_set_dynamic_linker(CodeGen *g, Buf *dynamic_linker);
 void codegen_add_lib_dir(CodeGen *codegen, const char *dir);
 void codegen_add_forbidden_lib(CodeGen *codegen, Buf *lib);
 LinkLib *codegen_add_link_lib(CodeGen *codegen, Buf *lib);
src/compiler.cpp
@@ -1,4 +1,5 @@
 #include "cache_hash.hpp"
+#include "os.hpp"
 
 #include <stdio.h>
 
@@ -8,8 +9,9 @@ static Buf saved_stage1_path = BUF_INIT;
 static Buf saved_lib_dir = BUF_INIT;
 static Buf saved_special_dir = BUF_INIT;
 static Buf saved_std_dir = BUF_INIT;
+static Buf saved_dynamic_linker_path = BUF_INIT;
 
-Buf *get_stage1_cache_path() {
+Buf *get_stage1_cache_path(void) {
     if (saved_stage1_path.list.length != 0) {
         return &saved_stage1_path;
     }
@@ -57,6 +59,13 @@ Error get_compiler_id(Buf **result) {
         Buf *lib_path = lib_paths.at(i);
         if ((err = cache_add_file(ch, lib_path)))
             return err;
+#if defined(ZIG_OS_LINUX) && defined(ZIG_ARCH_X86_64)
+        if (buf_ends_with_str(lib_path, "ld-linux-x86-64.so.2")) {
+            buf_init_from_buf(&saved_dynamic_linker_path, lib_path);
+        } else if (buf_ends_with_str(lib_path, "ld-musl-x86-64.so.1")) {
+            buf_init_from_buf(&saved_dynamic_linker_path, lib_path);
+        }
+#endif
     }
     if ((err = cache_final(ch, &saved_compiler_id)))
         return err;
@@ -67,6 +76,11 @@ Error get_compiler_id(Buf **result) {
     return ErrorNone;
 }
 
+Buf *get_self_dynamic_linker_path(void) {
+    Buf *dontcare;
+    (void)get_compiler_id(&dontcare); // for the side-effects of caching the dynamic linker path
+    return (saved_dynamic_linker_path.list.length == 0) ? nullptr : &saved_dynamic_linker_path;
+}
 
 static bool test_zig_install_prefix(Buf *test_path, Buf *out_zig_lib_dir) {
     Buf lib_buf = BUF_INIT;
src/compiler.hpp
@@ -11,11 +11,12 @@
 #include "buffer.hpp"
 #include "error.hpp"
 
-Buf *get_stage1_cache_path();
+Buf *get_stage1_cache_path(void);
 Error get_compiler_id(Buf **result);
+Buf *get_self_dynamic_linker_path(void);
 
-Buf *get_zig_lib_dir();
-Buf *get_zig_special_dir();
-Buf *get_zig_std_dir();
+Buf *get_zig_lib_dir(void);
+Buf *get_zig_special_dir(void);
+Buf *get_zig_std_dir(void);
 
 #endif
src/libc_installation.cpp
@@ -16,7 +16,6 @@ static const char *zig_libc_keys[] = {
     "crt_dir",
     "msvc_lib_dir",
     "kernel32_lib_dir",
-    "dynamic_linker_path",
 };
 
 static const size_t zig_libc_keys_len = array_length(zig_libc_keys);
@@ -37,7 +36,6 @@ static void zig_libc_init_empty(ZigLibCInstallation *libc) {
     buf_init_from_str(&libc->crt_dir, "");
     buf_init_from_str(&libc->msvc_lib_dir, "");
     buf_init_from_str(&libc->kernel32_lib_dir, "");
-    buf_init_from_str(&libc->dynamic_linker_path, "");
 }
 
 Error zig_libc_parse(ZigLibCInstallation *libc, Buf *libc_file, const ZigTarget *target, bool verbose) {
@@ -78,7 +76,6 @@ Error zig_libc_parse(ZigLibCInstallation *libc, Buf *libc_file, const ZigTarget
         match = match || zig_libc_match_key(name, value, found_keys, 2, &libc->crt_dir);
         match = match || zig_libc_match_key(name, value, found_keys, 3, &libc->msvc_lib_dir);
         match = match || zig_libc_match_key(name, value, found_keys, 4, &libc->kernel32_lib_dir);
-        match = match || zig_libc_match_key(name, value, found_keys, 5, &libc->dynamic_linker_path);
     }
 
     for (size_t i = 0; i < zig_libc_keys_len; i += 1) {
@@ -131,15 +128,6 @@ Error zig_libc_parse(ZigLibCInstallation *libc, Buf *libc_file, const ZigTarget
         }
     }
 
-    if (buf_len(&libc->dynamic_linker_path) == 0) {
-        if (!target_is_darwin(target) && target->os != OsWindows) {
-            if (verbose) {
-                fprintf(stderr, "dynamic_linker_path may not be empty for %s\n", target_os_name(target->os));
-            }
-            return ErrorSemanticAnalyzeFail;
-        }
-    }
-
     return ErrorNone;
 }
 
@@ -301,8 +289,8 @@ static Error zig_libc_find_native_include_dir_posix(ZigLibCInstallation *self, b
     }
     return ErrorFileNotFound;
 }
-#if !defined(ZIG_OS_DARWIN) && !defined(ZIG_OS_FREEBSD) && !defined(ZIG_OS_NETBSD)
-static Error zig_libc_cc_print_file_name(const char *o_file, Buf *out, bool want_dirname, bool verbose) {
+#if defined(ZIG_OS_LINUX)
+Error zig_libc_cc_print_file_name(const char *o_file, Buf *out, bool want_dirname, bool verbose) {
     const char *cc_exe = getenv("CC");
     cc_exe = (cc_exe == nullptr) ? "cc" : cc_exe;
     ZigList<const char *> args = {};
@@ -340,32 +328,6 @@ static Error zig_libc_find_native_crt_dir_posix(ZigLibCInstallation *self, bool
     return zig_libc_cc_print_file_name("crt1.o", &self->crt_dir, true, verbose);
 }
 #endif
-
-static Error zig_libc_find_native_dynamic_linker_posix(ZigLibCInstallation *self, bool verbose) {
-#if defined(ZIG_OS_LINUX)
-    Error err;
-    static const char *dyn_tests[] = {
-        "ld-linux-x86-64.so.2",
-        "ld-musl-x86_64.so.1",
-    };
-    for (size_t i = 0; i < array_length(dyn_tests); i += 1) {
-        const char *lib_name = dyn_tests[i];
-        if ((err = zig_libc_cc_print_file_name(lib_name, &self->dynamic_linker_path, false, true))) {
-            if (err != ErrorCCompilerCannotFindFile)
-                return err;
-            continue;
-        }
-        return ErrorNone;
-    }
-#endif
-    ZigTarget native_target;
-    get_native_target(&native_target);
-    const char *dynamic_linker_path = target_dynamic_linker(&native_target);
-    if (dynamic_linker_path != nullptr) {
-        buf_init_from_str(&self->dynamic_linker_path, dynamic_linker_path);
-    }
-    return ErrorNone;
-}
 #endif
 
 void zig_libc_render(ZigLibCInstallation *self, FILE *file) {
@@ -391,17 +353,12 @@ void zig_libc_render(ZigLibCInstallation *self, FILE *file) {
         "# Only needed when targeting Windows.\n"
         "kernel32_lib_dir=%s\n"
         "\n"
-        "# The full path to the dynamic linker, on the target system.\n"
-        "# Not needed when targeting MacOS or Windows.\n"
-        "dynamic_linker_path=%s\n"
-        "\n"
     ,
         buf_ptr(&self->include_dir),
         buf_ptr(&self->sys_include_dir),
         buf_ptr(&self->crt_dir),
         buf_ptr(&self->msvc_lib_dir),
-        buf_ptr(&self->kernel32_lib_dir),
-        buf_ptr(&self->dynamic_linker_path)
+        buf_ptr(&self->kernel32_lib_dir)
     );
 }
 
@@ -438,12 +395,10 @@ Error zig_libc_find_native(ZigLibCInstallation *self, bool verbose) {
         return err;
 #if defined(ZIG_OS_FREEBSD) || defined(ZIG_OS_NETBSD)
     buf_init_from_str(&self->crt_dir, "/usr/lib");
-#elif !defined(ZIG_OS_DARWIN)
+#elif defined(ZIG_OS_LINUX)
     if ((err = zig_libc_find_native_crt_dir_posix(self, verbose)))
         return err;
 #endif
-    if ((err = zig_libc_find_native_dynamic_linker_posix(self, verbose)))
-        return err;
     return ErrorNone;
 #endif
 }
src/libc_installation.hpp
@@ -21,7 +21,6 @@ struct ZigLibCInstallation {
     Buf crt_dir;
     Buf msvc_lib_dir;
     Buf kernel32_lib_dir;
-    Buf dynamic_linker_path;
 };
 
 Error ATTRIBUTE_MUST_USE zig_libc_parse(ZigLibCInstallation *libc, Buf *libc_file,
@@ -30,4 +29,8 @@ void zig_libc_render(ZigLibCInstallation *self, FILE *file);
 
 Error ATTRIBUTE_MUST_USE zig_libc_find_native(ZigLibCInstallation *self, bool verbose);
 
+#if defined(ZIG_OS_LINUX)
+Error zig_libc_cc_print_file_name(const char *o_file, Buf *out, bool want_dirname, bool verbose);
+#endif
+
 #endif
src/link.cpp
@@ -517,14 +517,9 @@ static void construct_linker_job_elf(LinkJob *lj) {
         }
 
         if (!g->is_static) {
-            if (g->libc != nullptr) {
-                assert(buf_len(&g->libc->dynamic_linker_path) != 0);
-                lj->args.append("-dynamic-linker");
-                lj->args.append(buf_ptr(&g->libc->dynamic_linker_path));
-            } else {
-                lj->args.append("-dynamic-linker");
-                lj->args.append(target_dynamic_linker(g->zig_target));
-            }
+            assert(g->dynamic_linker_path != nullptr);
+            lj->args.append("-dynamic-linker");
+            lj->args.append(buf_ptr(g->dynamic_linker_path));
         }
 
     }
@@ -545,7 +540,6 @@ static void construct_linker_job_elf(LinkJob *lj) {
             lj->args.append(buf_ptr(builtin_a_path));
         }
 
-        // sometimes libgcc is missing stuff, so we still build compiler_rt and rely on weak linkage
         Buf *compiler_rt_o_path = build_compiler_rt(g);
         lj->args.append(buf_ptr(compiler_rt_o_path));
     }
src/main.cpp
@@ -166,7 +166,7 @@ static int print_target_list(FILE *f) {
         SubArchList sub_arch_list = target_subarch_list(arch);
         size_t sub_count = target_subarch_count(sub_arch_list);
         const char *arch_native_str = (native.arch == arch) ? " (native)" : "";
-        fprintf(stderr, "  %s%s\n", arch_name, arch_native_str);
+        fprintf(f, "  %s%s\n", arch_name, arch_native_str);
         for (size_t sub_i = 0; sub_i < sub_count; sub_i += 1) {
             ZigLLVM_SubArchType sub = target_subarch_enum(sub_arch_list, sub_i);
             const char *sub_name = target_subarch_name(sub);
@@ -406,6 +406,7 @@ int main(int argc, char **argv) {
     bool verbose_cc = false;
     ErrColor color = ErrColorAuto;
     CacheOpt enable_cache = CacheOptAuto;
+    const char *dynamic_linker = nullptr;
     const char *libc_txt = nullptr;
     ZigList<const char *> clang_argv = {0};
     ZigList<const char *> llvm_argv = {0};
@@ -715,6 +716,8 @@ int main(int argc, char **argv) {
                     }
                 } else if (strcmp(arg, "--name") == 0) {
                     out_name = argv[i];
+                } else if (strcmp(arg, "--dynamic-linker") == 0) {
+                    dynamic_linker = argv[i];
                 } else if (strcmp(arg, "--libc") == 0) {
                     libc_txt = argv[i];
                 } else if (strcmp(arg, "-isystem") == 0) {
@@ -1027,6 +1030,8 @@ int main(int argc, char **argv) {
             codegen_set_llvm_argv(g, llvm_argv.items, llvm_argv.length);
             codegen_set_strip(g, strip);
             codegen_set_is_static(g, is_static);
+            if (dynamic_linker != nullptr)
+                codegen_set_dynamic_linker(g, buf_create_from_str(dynamic_linker));
             g->verbose_tokenize = verbose_tokenize;
             g->verbose_ast = verbose_ast;
             g->verbose_link = verbose_link;