Commit d10bbd28e9

Andrew Kelley <superjoe30@gmail.com>
2017-03-13 16:54:56
use lld instead of system linker
1 parent 7bc0145
cmake/Findclang.cmake
@@ -11,7 +11,7 @@ find_path(CLANG_INCLUDE_DIRS NAMES clang/Frontend/ASTUnit.h
         /usr/lib/llvm-4/include
         /mingw64/include)
 
-macro(FIND_AND_ADD_CLANG_LIB _libname_)
+    macro(FIND_AND_ADD_CLANG_LIB _libname_)
     string(TOUPPER ${_libname_} _prettylibname_)
     find_library(CLANG_${_prettylibname_}_LIB NAMES ${_libname_}
         PATHS
cmake/Findlld.cmake
@@ -0,0 +1,38 @@
+# Copyright (c) 2017 Andrew Kelley
+# This file is MIT licensed.
+# See http://opensource.org/licenses/MIT
+
+# LLD_FOUND
+# LLD_INCLUDE_DIRS
+# LLD_LIBRARIES
+
+find_path(LLD_INCLUDE_DIRS NAMES lld/Driver/Driver.h
+    PATHS
+        /usr/lib/llvm-4/include
+        /mingw64/include)
+
+    macro(FIND_AND_ADD_LLD_LIB _libname_)
+    string(TOUPPER ${_libname_} _prettylibname_)
+    find_library(LLD_${_prettylibname_}_LIB NAMES ${_libname_}
+        PATHS
+            /usr/lib/llvm-4/lib
+            /mingw64/lib)
+        if(LLD_${_prettylibname_}_LIB)
+            set(LLD_LIBRARIES ${LLD_LIBRARIES} ${LLD_${_prettylibname_}_LIB})
+    endif()
+endmacro(FIND_AND_ADD_LLD_LIB)
+
+FIND_AND_ADD_LLD_LIB(lldDriver)
+FIND_AND_ADD_LLD_LIB(lldELF)
+FIND_AND_ADD_LLD_LIB(lldCOFF)
+FIND_AND_ADD_LLD_LIB(lldMachO)
+FIND_AND_ADD_LLD_LIB(lldReaderWriter)
+FIND_AND_ADD_LLD_LIB(lldCore)
+FIND_AND_ADD_LLD_LIB(lldYAML)
+FIND_AND_ADD_LLD_LIB(lldConfig)
+
+include(FindPackageHandleStandardArgs)
+find_package_handle_standard_args(LLD DEFAULT_MSG LLD_LIBRARIES LLD_INCLUDE_DIRS)
+
+mark_as_advanced(LLD_INCLUDE_DIRS LLD_LIBRARIES)
+
example/hello_world/hello_libc.zig
@@ -1,6 +1,6 @@
 const c = @cImport(@cInclude("stdio.h"));
 
 export fn main(argc: c_int, argv: &&u8) -> c_int {
-    c.printf(c"Hello, world!\n");
+    _ = c.printf(c"Hello, world!\n");
     return 0;
 }
src/all_types.hpp
@@ -1268,6 +1268,9 @@ struct CodeGen {
     ZigList<Buf *> link_libs; // non-libc link libs
     // add -framework [name] args to linker
     ZigList<Buf *> darwin_frameworks;
+    // add -rpath [name] args to linker
+    ZigList<Buf *> rpath_list;
+
 
     // reminder: hash tables must be initialized before use
     HashMap<Buf *, ImportTableEntry *, buf_hash, buf_eql_buf> import_table;
@@ -1342,7 +1345,6 @@ struct CodeGen {
     Buf *libc_include_dir;
     Buf *zig_std_dir;
     Buf *dynamic_linker;
-    Buf *linker_path;
     Buf *ar_path;
     Buf triple_str;
     bool is_release_build;
src/codegen.cpp
@@ -89,8 +89,6 @@ CodeGen *codegen_create(Buf *root_source_dir, const ZigTarget *target) {
         g->libc_lib_dir = buf_create_from_str("");
         g->libc_static_lib_dir = buf_create_from_str("");
         g->libc_include_dir = buf_create_from_str("");
-        g->linker_path = buf_create_from_str("");
-        g->ar_path = buf_create_from_str("");
         g->darwin_linker_version = buf_create_from_str("");
     } else {
         // native compilation, we can rely on the configuration stuff
@@ -101,8 +99,6 @@ CodeGen *codegen_create(Buf *root_source_dir, const ZigTarget *target) {
         g->libc_lib_dir = buf_create_from_str(ZIG_LIBC_LIB_DIR);
         g->libc_static_lib_dir = buf_create_from_str(ZIG_LIBC_STATIC_LIB_DIR);
         g->libc_include_dir = buf_create_from_str(ZIG_LIBC_INCLUDE_DIR);
-        g->linker_path = buf_create_from_str(ZIG_LD_PATH);
-        g->ar_path = buf_create_from_str(ZIG_AR_PATH);
         g->darwin_linker_version = buf_create_from_str(ZIG_HOST_LINK_VERSION);
 
         if (g->zig_target.os == ZigLLVM_Darwin ||
@@ -180,18 +176,14 @@ void codegen_set_dynamic_linker(CodeGen *g, Buf *dynamic_linker) {
     g->dynamic_linker = dynamic_linker;
 }
 
-void codegen_set_linker_path(CodeGen *g, Buf *linker_path) {
-    g->linker_path = linker_path;
-}
-
-void codegen_set_ar_path(CodeGen *g, Buf *ar_path) {
-    g->ar_path = ar_path;
-}
-
 void codegen_add_lib_dir(CodeGen *g, const char *dir) {
     g->lib_dirs.append(dir);
 }
 
+void codegen_add_rpath(CodeGen *g, const char *name) {
+    g->rpath_list.append(buf_create_from_str(name));
+}
+
 void codegen_add_link_lib(CodeGen *g, const char *lib) {
     if (strcmp(lib, "c") == 0) {
         g->link_libc = true;
src/codegen.hpp
@@ -32,13 +32,12 @@ void codegen_set_libc_static_lib_dir(CodeGen *g, Buf *libc_static_lib_dir);
 void codegen_set_libc_include_dir(CodeGen *codegen, Buf *libc_include_dir);
 void codegen_set_zig_std_dir(CodeGen *codegen, Buf *zig_std_dir);
 void codegen_set_dynamic_linker(CodeGen *g, Buf *dynamic_linker);
-void codegen_set_linker_path(CodeGen *g, Buf *linker_path);
-void codegen_set_ar_path(CodeGen *g, Buf *ar_path);
 void codegen_set_windows_subsystem(CodeGen *g, bool mwindows, bool mconsole);
 void codegen_set_windows_unicode(CodeGen *g, bool municode);
 void codegen_add_lib_dir(CodeGen *codegen, const char *dir);
 void codegen_add_link_lib(CodeGen *codegen, const char *lib);
 void codegen_add_framework(CodeGen *codegen, const char *name);
+void codegen_add_rpath(CodeGen *codegen, const char *name);
 void codegen_set_mlinker_version(CodeGen *g, Buf *darwin_linker_version);
 void codegen_set_rdynamic(CodeGen *g, bool rdynamic);
 void codegen_set_mmacosx_version_min(CodeGen *g, Buf *mmacosx_version_min);
src/config.h.in
@@ -18,8 +18,6 @@
 #define ZIG_LIBC_INCLUDE_DIR "@ZIG_LIBC_INCLUDE_DIR@"
 #define ZIG_LIBC_LIB_DIR "@ZIG_LIBC_LIB_DIR@"
 #define ZIG_LIBC_STATIC_LIB_DIR "@ZIG_LIBC_STATIC_LIB_DIR@"
-#define ZIG_LD_PATH "@ZIG_LD_PATH@"
-#define ZIG_AR_PATH "@ZIG_AR_PATH@"
 #define ZIG_DYNAMIC_LINKER "@ZIG_DYNAMIC_LINKER@"
 #define ZIG_HOST_LINK_VERSION "@ZIG_HOST_LINK_VERSION@"
 
src/link.cpp
@@ -103,6 +103,7 @@ static const char *get_darwin_arch_string(const ZigTarget *t) {
     }
 }
 
+
 static const char *getLDMOption(const ZigTarget *t) {
     switch (t->arch.arch) {
         case ZigLLVM_x86:
@@ -147,7 +148,7 @@ static const char *getLDMOption(const ZigTarget *t) {
     }
 }
 
-static void construct_linker_job_linux(LinkJob *lj) {
+static void construct_linker_job_elf(LinkJob *lj) {
     CodeGen *g = lj->codegen;
 
     if (lj->link_in_crt) {
@@ -202,6 +203,12 @@ static void construct_linker_job_linux(LinkJob *lj) {
         lj->args.append(get_libc_static_file(g, crtbegino));
     }
 
+    for (size_t i = 0; i < g->rpath_list.length; i += 1) {
+        Buf *rpath = g->rpath_list.at(i);
+        lj->args.append("-rpath");
+        lj->args.append(buf_ptr(rpath));
+    }
+
     for (size_t i = 0; i < g->lib_dirs.length; i += 1) {
         const char *lib_dir = g->lib_dirs.at(i);
         lj->args.append("-L");
@@ -293,7 +300,7 @@ static bool is_target_cyg_mingw(const ZigTarget *target) {
         (target->os == ZigLLVM_Win32 && target->env_type == ZigLLVM_GNU);
 }
 
-static void construct_linker_job_mingw(LinkJob *lj) {
+static void construct_linker_job_coff(LinkJob *lj) {
     CodeGen *g = lj->codegen;
 
     if (lj->link_in_crt) {
@@ -546,7 +553,7 @@ static bool darwin_version_lt(DarwinPlatform *platform, int major, int minor) {
     return false;
 }
 
-static void construct_linker_job_darwin(LinkJob *lj) {
+static void construct_linker_job_macho(LinkJob *lj) {
     CodeGen *g = lj->codegen;
 
     int ver_major;
@@ -686,85 +693,16 @@ static void construct_linker_job_darwin(LinkJob *lj) {
 }
 
 static void construct_linker_job(LinkJob *lj) {
-    switch (lj->codegen->zig_target.os) {
-        case ZigLLVM_UnknownOS: // freestanding
-            // TODO we want to solve this problem with LLD, but for now let's
-            // assume gnu binutils
-            // http://lists.llvm.org/pipermail/llvm-dev/2017-February/109835.html
-            return construct_linker_job_linux(lj);
-        case ZigLLVM_Linux:
-            if (lj->codegen->zig_target.arch.arch == ZigLLVM_hexagon) {
-                zig_panic("TODO construct hexagon_TC linker job");
-            } else {
-                return construct_linker_job_linux(lj);
-            }
-        case ZigLLVM_CloudABI:
-            zig_panic("TODO construct CloudABI linker job");
-        case ZigLLVM_Darwin:
-        case ZigLLVM_MacOSX:
-        case ZigLLVM_IOS:
-            return construct_linker_job_darwin(lj);
-        case ZigLLVM_DragonFly:
-            zig_panic("TODO construct DragonFly linker job");
-        case ZigLLVM_OpenBSD:
-            zig_panic("TODO construct OpenBSD linker job");
-        case ZigLLVM_Bitrig:
-            zig_panic("TODO construct Bitrig linker job");
-        case ZigLLVM_NetBSD:
-            zig_panic("TODO construct NetBSD linker job");
-        case ZigLLVM_FreeBSD:
-            zig_panic("TODO construct FreeBSD linker job");
-        case ZigLLVM_Minix:
-            zig_panic("TODO construct Minix linker job");
-        case ZigLLVM_NaCl:
-            zig_panic("TODO construct NaCl_TC linker job");
-        case ZigLLVM_Solaris:
-            zig_panic("TODO construct Solaris linker job");
-        case ZigLLVM_Win32:
-            switch (lj->codegen->zig_target.env_type) {
-                default:
-                    if (lj->codegen->zig_target.oformat == ZigLLVM_ELF) {
-                        zig_panic("TODO construct Generic_ELF linker job");
-                    } else if (lj->codegen->zig_target.oformat == ZigLLVM_MachO) {
-                        zig_panic("TODO construct MachO linker job");
-                    } else {
-                        zig_panic("TODO construct Generic_GCC linker job");
-                    }
-                case ZigLLVM_GNU:
-                    return construct_linker_job_mingw(lj);
-                case ZigLLVM_Itanium:
-                    zig_panic("TODO construct CrossWindowsToolChain linker job");
-                case ZigLLVM_MSVC:
-                case ZigLLVM_UnknownEnvironment:
-                    zig_panic("TODO construct MSVC linker job");
-            }
-        case ZigLLVM_CUDA:
-            zig_panic("TODO construct Cuda linker job");
-        default:
-            // Of these targets, Hexagon is the only one that might have
-            // an OS of Linux, in which case it got handled above already.
-            if (lj->codegen->zig_target.arch.arch == ZigLLVM_tce) {
-                zig_panic("TODO construct TCE linker job");
-            } else if (lj->codegen->zig_target.arch.arch == ZigLLVM_hexagon) {
-                zig_panic("TODO construct Hexagon_TC linker job");
-            } else if (lj->codegen->zig_target.arch.arch == ZigLLVM_xcore) {
-                zig_panic("TODO construct XCore linker job");
-            } else if (lj->codegen->zig_target.arch.arch == ZigLLVM_shave) {
-                zig_panic("TODO construct SHAVE linker job");
-            } else if (lj->codegen->zig_target.oformat == ZigLLVM_ELF) {
-                zig_panic("TODO construct Generic_ELF linker job");
-            } else if (lj->codegen->zig_target.oformat == ZigLLVM_MachO) {
-                zig_panic("TODO construct MachO linker job");
-            } else {
-                zig_panic("TODO construct Generic_GCC linker job");
-            }
-
-    }
-}
+    switch (lj->codegen->zig_target.oformat) {
+        case ZigLLVM_UnknownObjectFormat:
+            zig_unreachable();
 
-static void ensure_we_have_linker_path(CodeGen *g) {
-    if (!g->linker_path || buf_len(g->linker_path) == 0) {
-        zig_panic("zig does not know the path to the linker");
+        case ZigLLVM_COFF:
+            return construct_linker_job_coff(lj);
+        case ZigLLVM_ELF:
+            return construct_linker_job_elf(lj);
+        case ZigLLVM_MachO:
+            return construct_linker_job_macho(lj);
     }
 }
 
@@ -838,44 +776,23 @@ void codegen_link(CodeGen *g, const char *out_file) {
     }
 
     lj.link_in_crt = (g->link_libc && g->out_type == OutTypeExe);
-    ensure_we_have_linker_path(g);
 
     construct_linker_job(&lj);
 
 
     if (g->verbose) {
-        fprintf(stderr, "%s", buf_ptr(g->linker_path));
+        fprintf(stderr, "link");
         for (size_t i = 0; i < lj.args.length; i += 1) {
             fprintf(stderr, " %s", lj.args.at(i));
         }
         fprintf(stderr, "\n");
     }
 
-    Buf ld_stderr = BUF_INIT;
-    Buf ld_stdout = BUF_INIT;
-    Termination term;
-    int err = os_exec_process(buf_ptr(g->linker_path), lj.args, &term, &ld_stderr, &ld_stdout);
-    if (err) {
-        fprintf(stderr, "linker not found: '%s'\n", buf_ptr(g->linker_path));
-        exit(1);
-    }
+    Buf diag = BUF_INIT;
 
-    if (term.how != TerminationIdClean || term.code != 0) {
-        if (term.how == TerminationIdClean) {
-            fprintf(stderr, "linker failed with return code %d\n", term.code);
-        } else if (term.how == TerminationIdSignaled) {
-            fprintf(stderr, "linker failed with signal %d\n", term.code);
-        } else {
-            fprintf(stderr, "linker failed\n");
-        }
-        fprintf(stderr, "%s ", buf_ptr(g->linker_path));
-        for (size_t i = 0; i < lj.args.length; i += 1) {
-            fprintf(stderr, "%s ", lj.args.at(i));
-        }
-        fprintf(stderr, "\n%s\n", buf_ptr(&ld_stderr));
+    if (!ZigLLDLink(g->zig_target.oformat, lj.args.items, lj.args.length, &diag)) {
+        fprintf(stderr, "%s\n", buf_ptr(&diag));
         exit(1);
-    } else if (buf_len(&ld_stderr)) {
-        fprintf(stderr, "%s\n", buf_ptr(&ld_stderr));
     }
 
     if (g->out_type == OutTypeLib ||
src/main.cpp
@@ -43,6 +43,7 @@ static int usage(const char *arg0) {
         "  -isystem [dir]               add additional search path for other .h files\n"
         "  -dirafter [dir]              same as -isystem but do it last\n"
         "  --library-path [dir]         add a directory to the library search path\n"
+        "  -L[dir]                      alias for --library-path\n"
         "  --library [lib]              link against lib\n"
         "  --target-arch [name]         specify target architecture\n"
         "  --target-os [name]           specify target operating system\n"
@@ -57,6 +58,7 @@ static int usage(const char *arg0) {
         "  -framework [name]            (darwin only) link against framework\n"
         "  --check-unused               perform semantic analysis on unused declarations\n"
         "  --linker-script [path]       use a custom linker script\n"
+        "  -rpath [path]                add a directory to the runtime library search path\n"
     , arg0);
     return EXIT_FAILURE;
 }
@@ -123,8 +125,6 @@ int main(int argc, char **argv) {
     const char *libc_include_dir = nullptr;
     const char *zig_std_dir = nullptr;
     const char *dynamic_linker = nullptr;
-    const char *linker_path = nullptr;
-    const char *ar_path = nullptr;
     ZigList<const char *> clang_argv = {0};
     ZigList<const char *> lib_dirs = {0};
     ZigList<const char *> link_libs = {0};
@@ -142,6 +142,7 @@ int main(int argc, char **argv) {
     const char *mios_version_min = nullptr;
     bool check_unused = false;
     const char *linker_script = nullptr;
+    ZigList<const char *> rpath_list = {0};
 
     for (int i = 1; i < argc; i += 1) {
         char *arg = argv[i];
@@ -165,6 +166,9 @@ int main(int argc, char **argv) {
                 rdynamic = true;
             } else if (strcmp(arg, "--check-unused") == 0) {
                 check_unused = true;
+            } else if (arg[1] == 'L' && arg[2] != 0) {
+                // alias for --library-path
+                lib_dirs.append(&arg[2]);
             } else if (i + 1 >= argc) {
                 return usage(arg0);
             } else {
@@ -205,17 +209,13 @@ int main(int argc, char **argv) {
                     zig_std_dir = argv[i];
                 } else if (strcmp(arg, "--dynamic-linker") == 0) {
                     dynamic_linker = argv[i];
-                } else if (strcmp(arg, "--ld-path") == 0) {
-                    linker_path = argv[i];
-                } else if (strcmp(arg, "--ar-path") == 0) {
-                    ar_path = argv[i];
                 } else if (strcmp(arg, "-isystem") == 0) {
                     clang_argv.append("-isystem");
                     clang_argv.append(argv[i]);
                 } else if (strcmp(arg, "-dirafter") == 0) {
                     clang_argv.append("-dirafter");
                     clang_argv.append(argv[i]);
-                } else if (strcmp(arg, "--library-path") == 0) {
+                } else if (strcmp(arg, "--library-path") == 0 || strcmp(arg, "-L") == 0) {
                     lib_dirs.append(argv[i]);
                 } else if (strcmp(arg, "--library") == 0) {
                     link_libs.append(argv[i]);
@@ -235,6 +235,8 @@ int main(int argc, char **argv) {
                     frameworks.append(argv[i]);
                 } else if (strcmp(arg, "--linker-script") == 0) {
                     linker_script = argv[i];
+                } else if (strcmp(arg, "-rpath") == 0) {
+                    rpath_list.append(argv[i]);
                 } else {
                     fprintf(stderr, "Invalid argument: %s\n", arg);
                     return usage(arg0);
@@ -373,10 +375,6 @@ int main(int argc, char **argv) {
                 codegen_set_zig_std_dir(g, buf_create_from_str(zig_std_dir));
             if (dynamic_linker)
                 codegen_set_dynamic_linker(g, buf_create_from_str(dynamic_linker));
-            if (linker_path)
-                codegen_set_linker_path(g, buf_create_from_str(linker_path));
-            if (ar_path)
-                codegen_set_ar_path(g, buf_create_from_str(ar_path));
             codegen_set_verbose(g, verbose);
             codegen_set_errmsg_color(g, color);
 
@@ -389,6 +387,9 @@ int main(int argc, char **argv) {
             for (size_t i = 0; i < frameworks.length; i += 1) {
                 codegen_add_framework(g, frameworks.at(i));
             }
+            for (size_t i = 0; i < rpath_list.length; i += 1) {
+                codegen_add_rpath(g, rpath_list.at(i));
+            }
 
             codegen_set_windows_subsystem(g, mwindows, mconsole);
             codegen_set_windows_unicode(g, municode);
src/zig_llvm.cpp
@@ -42,6 +42,8 @@
 #include <llvm/Transforms/IPO/PassManagerBuilder.h>
 #include <llvm/Transforms/Scalar.h>
 
+#include <lld/Driver/Driver.h>
+
 using namespace llvm;
 
 void ZigLLVMInitializeLoopStrengthReducePass(LLVMPassRegistryRef R) {
@@ -790,3 +792,36 @@ Buf *get_dynamic_linker(LLVMTargetMachineRef target_machine_ref) {
     }
 }
 
+bool ZigLLDLink(ZigLLVM_ObjectFormatType oformat, const char **args, size_t arg_count, Buf *diag_buf) {
+    ArrayRef<const char *> array_ref_args(args, arg_count);
+
+    buf_resize(diag_buf, 0);
+    class MyOStream: public raw_ostream {
+        public:
+            MyOStream(Buf *_diag_buf) : raw_ostream(true), diag_buf(_diag_buf) {
+
+            }
+            void write_impl(const char *ptr, size_t len) override {
+                buf_append_mem(diag_buf, ptr, len);
+            }
+            uint64_t current_pos() const override {
+                return buf_len(diag_buf);
+            }
+            Buf *diag_buf;
+    } diag(diag_buf);
+
+    switch (oformat) {
+        case ZigLLVM_UnknownObjectFormat:
+            zig_unreachable();
+
+        case ZigLLVM_COFF:
+            return lld::coff::link(array_ref_args);
+
+        case ZigLLVM_ELF:
+            return lld::elf::link(array_ref_args, false, diag);
+
+        case ZigLLVM_MachO:
+            return lld::mach_o::link(array_ref_args, diag);
+    }
+    zig_unreachable();
+}
src/zig_llvm.hpp
@@ -351,6 +351,9 @@ const char *ZigLLVMGetEnvironmentTypeName(ZigLLVM_EnvironmentType env_type);
  * This stuff is not LLVM API but it depends on the LLVM C++ API so we put it here.
  */
 struct Buf;
+
+bool ZigLLDLink(ZigLLVM_ObjectFormatType oformat, const char **args, size_t arg_count, Buf *diag);
+
 void ZigLLVMGetNativeTarget(ZigLLVM_ArchType *arch_type, ZigLLVM_SubArchType *sub_arch_type,
         ZigLLVM_VendorType *vendor_type, ZigLLVM_OSType *os_type, ZigLLVM_EnvironmentType *environ_type,
         ZigLLVM_ObjectFormatType *oformat);
CMakeLists.txt
@@ -17,8 +17,6 @@ message("Configuring zig version ${ZIG_VERSION}")
 set(ZIG_LIBC_LIB_DIR "" CACHE STRING "Default native target libc directory where crt1.o can be found")
 set(ZIG_LIBC_STATIC_LIB_DIR "" CACHE STRING "Default native target libc directory where crtbeginT.o can be found")
 set(ZIG_LIBC_INCLUDE_DIR "/usr/include" CACHE STRING "Default native target libc include directory")
-set(ZIG_LD_PATH "ld" CACHE STRING "Path to ld for the native target")
-set(ZIG_AR_PATH "ar" CACHE STRING "Path to ar for the native target")
 set(ZIG_DYNAMIC_LINKER "" CACHE STRING "Override dynamic linker for native target")
 
 option(ZIG_TEST_COVERAGE "Build Zig with test coverage instrumentation" OFF)
@@ -32,6 +30,9 @@ link_directories(${LLVM_LIBDIRS})
 find_package(clang)
 include_directories(${CLANG_INCLUDE_DIRS})
 
+find_package(lld)
+include_directories(${LLD_INCLUDE_DIRS})
+
 include_directories(
     ${CMAKE_SOURCE_DIR}
     ${CMAKE_BINARY_DIR}
@@ -192,6 +193,7 @@ set_target_properties(zig PROPERTIES
 )
 target_link_libraries(zig LINK_PUBLIC
     ${CLANG_LIBRARIES}
+    ${LLD_LIBRARIES}
     ${LLVM_LIBRARIES}
 )
 install(TARGETS zig DESTINATION bin)