/* * Copyright (c) 2015 Andrew Kelley * * This file is part of zig, which is MIT licensed. * See http://opensource.org/licenses/MIT */ #include "link.hpp" #include "os.hpp" #include "config.h" #include "codegen.hpp" #include "analyze.hpp" struct LinkJob { CodeGen *codegen; Buf out_file; ZigList args; bool link_in_crt; HashMap rpath_table; }; static const char *get_libc_file(CodeGen *g, const char *file) { Buf *out_buf = buf_alloc(); os_path_join(g->libc_lib_dir, buf_create_from_str(file), out_buf); return buf_ptr(out_buf); } static const char *get_libc_static_file(CodeGen *g, const char *file) { Buf *out_buf = buf_alloc(); os_path_join(g->libc_static_lib_dir, buf_create_from_str(file), out_buf); return buf_ptr(out_buf); } static Buf *build_o_raw(CodeGen *parent_gen, const char *oname, Buf *full_path) { ZigTarget *child_target = parent_gen->is_native_target ? nullptr : &parent_gen->zig_target; CodeGen *child_gen = codegen_create(full_path, child_target, OutTypeObj, parent_gen->build_mode, parent_gen->zig_lib_dir); child_gen->want_h_file = false; child_gen->verbose_tokenize = parent_gen->verbose_tokenize; child_gen->verbose_ast = parent_gen->verbose_ast; child_gen->verbose_link = parent_gen->verbose_link; child_gen->verbose_ir = parent_gen->verbose_ir; child_gen->verbose_llvm_ir = parent_gen->verbose_llvm_ir; child_gen->verbose_cimport = parent_gen->verbose_cimport; codegen_set_cache_dir(child_gen, parent_gen->cache_dir); codegen_set_strip(child_gen, parent_gen->strip_debug_symbols); codegen_set_is_static(child_gen, parent_gen->is_static); codegen_set_out_name(child_gen, buf_create_from_str(oname)); codegen_set_errmsg_color(child_gen, parent_gen->err_color); codegen_set_mmacosx_version_min(child_gen, parent_gen->mmacosx_version_min); codegen_set_mios_version_min(child_gen, parent_gen->mios_version_min); for (size_t i = 0; i < parent_gen->link_libs_list.length; i += 1) { LinkLib *link_lib = parent_gen->link_libs_list.at(i); LinkLib *new_link_lib = codegen_add_link_lib(child_gen, link_lib->name); new_link_lib->provided_explicitly = link_lib->provided_explicitly; } codegen_build(child_gen); const char *o_ext = target_o_file_ext(&child_gen->zig_target); Buf *o_out_name = buf_sprintf("%s%s", oname, o_ext); Buf *output_path = buf_alloc(); os_path_join(parent_gen->cache_dir, o_out_name, output_path); codegen_link(child_gen, buf_ptr(output_path)); codegen_destroy(child_gen); return output_path; } static Buf *build_o(CodeGen *parent_gen, const char *oname) { Buf *source_basename = buf_sprintf("%s.zig", oname); Buf *full_path = buf_alloc(); os_path_join(parent_gen->zig_std_special_dir, source_basename, full_path); return build_o_raw(parent_gen, oname, full_path); } static Buf *build_compiler_rt(CodeGen *parent_gen) { Buf *dir_path = buf_alloc(); os_path_join(parent_gen->zig_std_special_dir, buf_create_from_str("compiler_rt"), dir_path); Buf *full_path = buf_alloc(); os_path_join(dir_path, buf_create_from_str("index.zig"), full_path); return build_o_raw(parent_gen, "compiler_rt", full_path); } static const char *get_darwin_arch_string(const ZigTarget *t) { switch (t->arch.arch) { case ZigLLVM_aarch64: return "arm64"; case ZigLLVM_thumb: case ZigLLVM_arm: return "arm"; case ZigLLVM_ppc: return "ppc"; case ZigLLVM_ppc64: return "ppc64"; case ZigLLVM_ppc64le: return "ppc64le"; default: return ZigLLVMGetArchTypeName(t->arch.arch); } } static const char *getLDMOption(const ZigTarget *t) { switch (t->arch.arch) { case ZigLLVM_x86: return "elf_i386"; case ZigLLVM_aarch64: return "aarch64linux"; case ZigLLVM_aarch64_be: return "aarch64_be_linux"; case ZigLLVM_arm: case ZigLLVM_thumb: return "armelf_linux_eabi"; case ZigLLVM_armeb: case ZigLLVM_thumbeb: return "armebelf_linux_eabi"; case ZigLLVM_ppc: return "elf32ppclinux"; case ZigLLVM_ppc64: return "elf64ppc"; case ZigLLVM_ppc64le: return "elf64lppc"; case ZigLLVM_sparc: case ZigLLVM_sparcel: return "elf32_sparc"; case ZigLLVM_sparcv9: return "elf64_sparc"; case ZigLLVM_mips: return "elf32btsmip"; case ZigLLVM_mipsel: return "elf32ltsmip"; return "elf64btsmip"; case ZigLLVM_mips64el: return "elf64ltsmip"; case ZigLLVM_systemz: return "elf64_s390"; case ZigLLVM_x86_64: if (t->env_type == ZigLLVM_GNUX32) { return "elf32_x86_64"; } return "elf_x86_64"; default: zig_unreachable(); } } static void add_rpath(LinkJob *lj, Buf *rpath) { if (lj->rpath_table.maybe_get(rpath) != nullptr) return; lj->args.append("-rpath"); lj->args.append(buf_ptr(rpath)); lj->rpath_table.put(rpath, true); } static Buf *try_dynamic_linker_path(const char *ld_name) { const char *cc_exe = getenv("CC"); cc_exe = (cc_exe == nullptr) ? "cc" : cc_exe; ZigList args = {}; args.append(buf_ptr(buf_sprintf("-print-file-name=%s", ld_name))); Termination term; Buf *out_stderr = buf_alloc(); Buf *out_stdout = buf_alloc(); int err; if ((err = os_exec_process(cc_exe, args, &term, out_stderr, out_stdout))) { return nullptr; } if (term.how != TerminationIdClean || term.code != 0) { return nullptr; } if (buf_ends_with_str(out_stdout, "\n")) { buf_resize(out_stdout, buf_len(out_stdout) - 1); } if (buf_len(out_stdout) == 0 || buf_eql_str(out_stdout, ld_name)) { return nullptr; } return out_stdout; } static Buf *get_dynamic_linker_path(CodeGen *g) { if (g->is_native_target && g->zig_target.arch.arch == ZigLLVM_x86_64) { static const char *ld_names[] = { "ld-linux-x86-64.so.2", "ld-musl-x86_64.so.1", }; for (size_t i = 0; i < array_length(ld_names); i += 1) { const char *ld_name = ld_names[i]; Buf *result = try_dynamic_linker_path(ld_name); if (result != nullptr) { return result; } } } return target_dynamic_linker(&g->zig_target); } static void construct_linker_job_elf(LinkJob *lj) { CodeGen *g = lj->codegen; if (g->libc_link_lib != nullptr) { find_libc_lib_path(g); } if (g->linker_script) { lj->args.append("-T"); lj->args.append(g->linker_script); } if (g->no_rosegment_workaround) { lj->args.append("--no-rosegment"); } lj->args.append("--gc-sections"); lj->args.append("-m"); lj->args.append(getLDMOption(&g->zig_target)); bool is_lib = g->out_type == OutTypeLib; bool shared = !g->is_static && is_lib; Buf *soname = nullptr; if (g->is_static) { if (g->zig_target.arch.arch == ZigLLVM_arm || g->zig_target.arch.arch == ZigLLVM_armeb || g->zig_target.arch.arch == ZigLLVM_thumb || g->zig_target.arch.arch == ZigLLVM_thumbeb) { lj->args.append("-Bstatic"); } else { lj->args.append("-static"); } } else if (shared) { lj->args.append("-shared"); if (buf_len(&lj->out_file) == 0) { buf_appendf(&lj->out_file, "lib%s.so.%" ZIG_PRI_usize ".%" ZIG_PRI_usize ".%" ZIG_PRI_usize "", buf_ptr(g->root_out_name), g->version_major, g->version_minor, g->version_patch); } soname = buf_sprintf("lib%s.so.%" ZIG_PRI_usize "", buf_ptr(g->root_out_name), g->version_major); } lj->args.append("-o"); lj->args.append(buf_ptr(&lj->out_file)); if (lj->link_in_crt) { const char *crt1o; const char *crtbegino; if (g->is_static) { crt1o = "crt1.o"; crtbegino = "crtbeginT.o"; } else { crt1o = "Scrt1.o"; crtbegino = "crtbegin.o"; } lj->args.append(get_libc_file(g, crt1o)); lj->args.append(get_libc_file(g, "crti.o")); 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); add_rpath(lj, rpath); } if (g->each_lib_rpath) { for (size_t i = 0; i < g->lib_dirs.length; i += 1) { const char *lib_dir = g->lib_dirs.at(i); 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")) { continue; } bool does_exist; Buf *test_path = buf_sprintf("%s/lib%s.so", lib_dir, buf_ptr(link_lib->name)); if (os_file_exists(test_path, &does_exist) != ErrorNone) { zig_panic("link: unable to check if file exists: %s", buf_ptr(test_path)); } if (does_exist) { add_rpath(lj, buf_create_from_str(lib_dir)); break; } } } } 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"); lj->args.append(lib_dir); } if (g->libc_link_lib != nullptr) { lj->args.append("-L"); lj->args.append(buf_ptr(g->libc_lib_dir)); lj->args.append("-L"); lj->args.append(buf_ptr(g->libc_static_lib_dir)); } if (!g->is_static) { if (g->dynamic_linker != nullptr) { assert(buf_len(g->dynamic_linker) != 0); lj->args.append("-dynamic-linker"); lj->args.append(buf_ptr(g->dynamic_linker)); } else { Buf *resolved_dynamic_linker = get_dynamic_linker_path(g); lj->args.append("-dynamic-linker"); lj->args.append(buf_ptr(resolved_dynamic_linker)); } } if (shared) { lj->args.append("-soname"); lj->args.append(buf_ptr(soname)); } // .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))); } if (g->out_type == OutTypeExe || g->out_type == OutTypeLib) { if (g->libc_link_lib == nullptr) { Buf *builtin_o_path = build_o(g, "builtin"); lj->args.append(buf_ptr(builtin_o_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)); } 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")) { 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, ".so")) { 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->libc_link_lib != nullptr) { if (g->is_static) { lj->args.append("--start-group"); lj->args.append("-lgcc"); lj->args.append("-lgcc_eh"); lj->args.append("-lc"); lj->args.append("-lm"); lj->args.append("--end-group"); } else { lj->args.append("-lgcc"); lj->args.append("--as-needed"); lj->args.append("-lgcc_s"); lj->args.append("--no-as-needed"); lj->args.append("-lc"); lj->args.append("-lm"); lj->args.append("-lgcc"); lj->args.append("--as-needed"); lj->args.append("-lgcc_s"); lj->args.append("--no-as-needed"); } } // crt end if (lj->link_in_crt) { lj->args.append(get_libc_static_file(g, "crtend.o")); lj->args.append(get_libc_file(g, "crtn.o")); } if (!g->is_native_target) { lj->args.append("--allow-shlib-undefined"); } if (g->zig_target.os == OsZen) { lj->args.append("-e"); lj->args.append("_start"); lj->args.append("--image-base=0x10000000"); } } static void construct_linker_job_wasm(LinkJob *lj) { CodeGen *g = lj->codegen; lj->args.append("--relocatable"); // So lld doesn't look for _start. lj->args.append("-o"); lj->args.append(buf_ptr(&lj->out_file)); // .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))); } } //static bool is_target_cyg_mingw(const ZigTarget *target) { // return (target->os == ZigLLVM_Win32 && target->env_type == ZigLLVM_Cygnus) || // (target->os == ZigLLVM_Win32 && target->env_type == ZigLLVM_GNU); //} static void coff_append_machine_arg(CodeGen *g, ZigList *list) { if (g->zig_target.arch.arch == ZigLLVM_x86) { list->append("-MACHINE:X86"); } else if (g->zig_target.arch.arch == ZigLLVM_x86_64) { list->append("-MACHINE:X64"); } else if (g->zig_target.arch.arch == ZigLLVM_arm) { list->append("-MACHINE:ARM"); } } static void link_diag_callback(void *context, const char *ptr, size_t len) { Buf *diag = reinterpret_cast(context); buf_append_mem(diag, ptr, len); } static bool zig_lld_link(ZigLLVM_ObjectFormatType oformat, const char **args, size_t arg_count, Buf *diag) { buf_resize(diag, 0); return ZigLLDLink(oformat, args, arg_count, link_diag_callback, diag); } static void construct_linker_job_coff(LinkJob *lj) { CodeGen *g = lj->codegen; if (g->libc_link_lib != nullptr) { find_libc_lib_path(g); } lj->args.append("-NOLOGO"); if (!g->strip_debug_symbols) { lj->args.append("-DEBUG"); } coff_append_machine_arg(g, &lj->args); if (g->windows_subsystem_windows) { lj->args.append("/SUBSYSTEM:windows"); } else if (g->windows_subsystem_console) { lj->args.append("/SUBSYSTEM:console"); } // The commented out stuff is from when we linked with MinGW // Now that we're linking with LLD it remains to be determined // how to handle --target-environ gnu // These comments are a clue bool is_library = g->out_type == OutTypeLib; //bool dll = g->out_type == OutTypeLib; //bool shared = !g->is_static && dll; //if (g->is_static) { // lj->args.append("-Bstatic"); //} else { // if (dll) { // lj->args.append("--dll"); // } else if (shared) { // lj->args.append("--shared"); // } // lj->args.append("-Bdynamic"); // if (dll || shared) { // lj->args.append("-e"); // if (g->zig_target.arch.arch == ZigLLVM_x86) { // lj->args.append("_DllMainCRTStartup@12"); // } else { // lj->args.append("DllMainCRTStartup"); // } // lj->args.append("--enable-auto-image-base"); // } //} lj->args.append(buf_ptr(buf_sprintf("-OUT:%s", buf_ptr(&lj->out_file)))); if (g->libc_link_lib != nullptr) { lj->args.append(buf_ptr(buf_sprintf("-LIBPATH:%s", buf_ptr(g->msvc_lib_dir)))); lj->args.append(buf_ptr(buf_sprintf("-LIBPATH:%s", buf_ptr(g->kernel32_lib_dir)))); lj->args.append(buf_ptr(buf_sprintf("-LIBPATH:%s", buf_ptr(g->libc_lib_dir)))); if (g->libc_static_lib_dir != nullptr) { lj->args.append(buf_ptr(buf_sprintf("-LIBPATH:%s", buf_ptr(g->libc_static_lib_dir)))); } } if (lj->link_in_crt) { const char *lib_str = g->is_static ? "lib" : ""; const char *d_str = (g->build_mode == BuildModeDebug) ? "d" : ""; if (g->is_static) { Buf *cmt_lib_name = buf_sprintf("libcmt%s.lib", d_str); lj->args.append(buf_ptr(cmt_lib_name)); } else { Buf *msvcrt_lib_name = buf_sprintf("msvcrt%s.lib", d_str); lj->args.append(buf_ptr(msvcrt_lib_name)); } Buf *vcruntime_lib_name = buf_sprintf("%svcruntime%s.lib", lib_str, d_str); lj->args.append(buf_ptr(vcruntime_lib_name)); Buf *crt_lib_name = buf_sprintf("%sucrt%s.lib", lib_str, d_str); lj->args.append(buf_ptr(crt_lib_name)); //Visual C++ 2015 Conformance Changes //https://msdn.microsoft.com/en-us/library/bb531344.aspx lj->args.append("legacy_stdio_definitions.lib"); //if (shared || dll) { // lj->args.append(get_libc_file(g, "dllcrt2.o")); //} else { // if (g->windows_linker_unicode) { // lj->args.append(get_libc_file(g, "crt2u.o")); // } else { // lj->args.append(get_libc_file(g, "crt2.o")); // } //} //lj->args.append(get_libc_static_file(g, "crtbegin.o")); // msvcrt depends on kernel32 lj->args.append("kernel32.lib"); } else { lj->args.append("-NODEFAULTLIB"); if (!is_library) { if (g->have_winmain) { lj->args.append("-ENTRY:WinMain"); } else { lj->args.append("-ENTRY:WinMainCRTStartup"); } } } if (is_library && !g->is_static) { lj->args.append("-DLL"); } for (size_t i = 0; i < g->lib_dirs.length; i += 1) { const char *lib_dir = g->lib_dirs.at(i); lj->args.append(buf_ptr(buf_sprintf("-LIBPATH:%s", lib_dir))); } for (size_t i = 0; i < g->link_objects.length; i += 1) { lj->args.append((const char *)buf_ptr(g->link_objects.at(i))); } if (g->out_type == OutTypeExe || g->out_type == OutTypeLib) { if (g->libc_link_lib == nullptr) { Buf *builtin_o_path = build_o(g, "builtin"); lj->args.append(buf_ptr(builtin_o_path)); } // msvc compiler_rt is missing some stuff, so we still build it and rely on weak linkage Buf *compiler_rt_o_path = build_compiler_rt(g); lj->args.append(buf_ptr(compiler_rt_o_path)); } Buf *def_contents = buf_alloc(); ZigList gen_lib_args = {0}; 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")) { continue; } if (link_lib->provided_explicitly) { if (lj->codegen->zig_target.env_type == ZigLLVM_GNU) { 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)); } } else { buf_resize(def_contents, 0); buf_appendf(def_contents, "LIBRARY %s\nEXPORTS\n", buf_ptr(link_lib->name)); for (size_t exp_i = 0; exp_i < link_lib->symbols.length; exp_i += 1) { Buf *symbol_name = link_lib->symbols.at(exp_i); buf_appendf(def_contents, "%s\n", buf_ptr(symbol_name)); } buf_appendf(def_contents, "\n"); Buf *def_path = buf_alloc(); os_path_join(g->cache_dir, buf_sprintf("%s.def", buf_ptr(link_lib->name)), def_path); os_write_file(def_path, def_contents); Buf *generated_lib_path = buf_alloc(); os_path_join(g->cache_dir, buf_sprintf("%s.lib", buf_ptr(link_lib->name)), generated_lib_path); gen_lib_args.resize(0); gen_lib_args.append("link"); coff_append_machine_arg(g, &gen_lib_args); gen_lib_args.append(buf_ptr(buf_sprintf("-DEF:%s", buf_ptr(def_path)))); gen_lib_args.append(buf_ptr(buf_sprintf("-OUT:%s", buf_ptr(generated_lib_path)))); Buf diag = BUF_INIT; if (!zig_lld_link(g->zig_target.oformat, gen_lib_args.items, gen_lib_args.length, &diag)) { fprintf(stderr, "%s\n", buf_ptr(&diag)); exit(1); } lj->args.append(buf_ptr(generated_lib_path)); } } //if (g->libc_link_lib != nullptr) { //if (g->is_static) { // lj->args.append("--start-group"); //} //lj->args.append("-lmingw32"); //lj->args.append("-lgcc"); //bool is_android = (g->zig_target.env_type == ZigLLVM_Android); //bool is_cyg_ming = is_target_cyg_mingw(&g->zig_target); //if (!g->is_static && !is_android) { // if (!is_cyg_ming) { // lj->args.append("--as-needed"); // } // //lj->args.append("-lgcc_s"); // if (!is_cyg_ming) { // lj->args.append("--no-as-needed"); // } //} //if (g->is_static && !is_android) { // //lj->args.append("-lgcc_eh"); //} //if (is_android && !g->is_static) { // lj->args.append("-ldl"); //} //lj->args.append("-lmoldname"); //lj->args.append("-lmingwex"); //lj->args.append("-lmsvcrt"); //if (g->windows_subsystem_windows) { // lj->args.append("-lgdi32"); // lj->args.append("-lcomdlg32"); //} //lj->args.append("-ladvapi32"); //lj->args.append("-lshell32"); //lj->args.append("-luser32"); //lj->args.append("-lkernel32"); //if (g->is_static) { // lj->args.append("--end-group"); //} //if (lj->link_in_crt) { // lj->args.append(get_libc_static_file(g, "crtend.o")); //} //} } // Parse (([0-9]+)(.([0-9]+)(.([0-9]+)?))?)? and return the // grouped values as integers. Numbers which are not provided are set to 0. // return true if the entire string was parsed (9.2), or all groups were // parsed (10.3.5extrastuff). static bool darwin_get_release_version(const char *str, int *major, int *minor, int *micro, bool *had_extra) { *had_extra = false; *major = 0; *minor = 0; *micro = 0; if (*str == '\0') return false; char *end; *major = (int)strtol(str, &end, 10); if (*str != '\0' && *end == '\0') return true; if (*end != '.') return false; str = end + 1; *minor = (int)strtol(str, &end, 10); if (*str != '\0' && *end == '\0') return true; if (*end != '.') return false; str = end + 1; *micro = (int)strtol(str, &end, 10); if (*str != '\0' && *end == '\0') return true; if (str == end) return false; *had_extra = true; return true; } enum DarwinPlatformKind { MacOS, IPhoneOS, IPhoneOSSimulator, }; struct DarwinPlatform { DarwinPlatformKind kind; int major; int minor; int micro; }; static void get_darwin_platform(LinkJob *lj, DarwinPlatform *platform) { CodeGen *g = lj->codegen; if (g->mmacosx_version_min) { platform->kind = MacOS; } else if (g->mios_version_min) { platform->kind = IPhoneOS; } else if (g->zig_target.os == OsMacOSX) { platform->kind = MacOS; g->mmacosx_version_min = buf_create_from_str("10.10"); } else { zig_panic("unable to infer -mmacosx-version-min or -mios-version-min"); } bool had_extra; if (platform->kind == MacOS) { if (!darwin_get_release_version(buf_ptr(g->mmacosx_version_min), &platform->major, &platform->minor, &platform->micro, &had_extra) || had_extra || platform->major != 10 || platform->minor >= 100 || platform->micro >= 100) { zig_panic("invalid -mmacosx-version-min"); } } else if (platform->kind == IPhoneOS) { if (!darwin_get_release_version(buf_ptr(g->mios_version_min), &platform->major, &platform->minor, &platform->micro, &had_extra) || had_extra || platform->major >= 10 || platform->minor >= 100 || platform->micro >= 100) { zig_panic("invalid -mios-version-min"); } } else { zig_unreachable(); } if (platform->kind == IPhoneOS && (g->zig_target.arch.arch == ZigLLVM_x86 || g->zig_target.arch.arch == ZigLLVM_x86_64)) { platform->kind = IPhoneOSSimulator; } } static bool darwin_version_lt(DarwinPlatform *platform, int major, int minor) { if (platform->major < major) { return true; } else if (platform->major > major) { return false; } if (platform->minor < minor) { return true; } return false; } static void construct_linker_job_macho(LinkJob *lj) { CodeGen *g = lj->codegen; lj->args.append("-demangle"); if (g->linker_rdynamic) { lj->args.append("-export_dynamic"); } bool is_lib = g->out_type == OutTypeLib; bool shared = !g->is_static && is_lib; if (g->is_static) { lj->args.append("-static"); } else { lj->args.append("-dynamic"); } if (is_lib) { if (!g->is_static) { lj->args.append("-dylib"); Buf *compat_vers = buf_sprintf("%" ZIG_PRI_usize ".0.0", g->version_major); lj->args.append("-compatibility_version"); lj->args.append(buf_ptr(compat_vers)); Buf *cur_vers = buf_sprintf("%" ZIG_PRI_usize ".%" ZIG_PRI_usize ".%" ZIG_PRI_usize, g->version_major, g->version_minor, g->version_patch); lj->args.append("-current_version"); lj->args.append(buf_ptr(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); //lj->args.append("-install_name"); //lj->args.append(buf_ptr(dylib_install_name)); if (buf_len(&lj->out_file) == 0) { buf_appendf(&lj->out_file, "lib%s.%" ZIG_PRI_usize ".%" ZIG_PRI_usize ".%" ZIG_PRI_usize ".dylib", buf_ptr(g->root_out_name), g->version_major, g->version_minor, g->version_patch); } } } lj->args.append("-arch"); lj->args.append(get_darwin_arch_string(&g->zig_target)); DarwinPlatform platform; get_darwin_platform(lj, &platform); switch (platform.kind) { case MacOS: lj->args.append("-macosx_version_min"); break; case IPhoneOS: lj->args.append("-iphoneos_version_min"); break; case IPhoneOSSimulator: lj->args.append("-ios_simulator_version_min"); break; } lj->args.append(buf_ptr(buf_sprintf("%d.%d.%d", platform.major, platform.minor, platform.micro))); if (g->out_type == OutTypeExe) { if (g->is_static) { lj->args.append("-no_pie"); } else { lj->args.append("-pie"); } } lj->args.append("-o"); lj->args.append(buf_ptr(&lj->out_file)); for (size_t i = 0; i < g->rpath_list.length; i += 1) { Buf *rpath = g->rpath_list.at(i); add_rpath(lj, rpath); } add_rpath(lj, &lj->out_file); if (shared) { lj->args.append("-headerpad_max_install_names"); } else if (g->is_static) { lj->args.append("-lcrt0.o"); } else { switch (platform.kind) { case MacOS: if (darwin_version_lt(&platform, 10, 5)) { lj->args.append("-lcrt1.o"); } else if (darwin_version_lt(&platform, 10, 6)) { lj->args.append("-lcrt1.10.5.o"); } else if (darwin_version_lt(&platform, 10, 8)) { lj->args.append("-lcrt1.10.6.o"); } break; case IPhoneOS: if (g->zig_target.arch.arch == ZigLLVM_aarch64) { // iOS does not need any crt1 files for arm64 } else if (darwin_version_lt(&platform, 3, 1)) { lj->args.append("-lcrt1.o"); } else if (darwin_version_lt(&platform, 6, 0)) { lj->args.append("-lcrt1.3.1.o"); } break; case IPhoneOSSimulator: // no crt1.o needed break; } } 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"); lj->args.append(lib_dir); } for (size_t i = 0; i < g->link_objects.length; i += 1) { lj->args.append((const char *)buf_ptr(g->link_objects.at(i))); } // compiler_rt on darwin is missing some stuff, so we still build it and rely on LinkOnce if (g->out_type == OutTypeExe || g->out_type == OutTypeLib) { Buf *compiler_rt_o_path = build_compiler_rt(g); lj->args.append(buf_ptr(compiler_rt_o_path)); } if (g->is_native_target) { 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")) { // 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"); } else { 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)); } } } } else { lj->args.append("-undefined"); lj->args.append("dynamic_lookup"); } if (platform.kind == MacOS) { if (darwin_version_lt(&platform, 10, 5)) { lj->args.append("-lgcc_s.10.4"); } else if (darwin_version_lt(&platform, 10, 6)) { lj->args.append("-lgcc_s.10.5"); } } else { zig_panic("TODO"); } for (size_t i = 0; i < g->darwin_frameworks.length; i += 1) { lj->args.append("-framework"); lj->args.append(buf_ptr(g->darwin_frameworks.at(i))); } } static void construct_linker_job(LinkJob *lj) { switch (lj->codegen->zig_target.oformat) { case ZigLLVM_UnknownObjectFormat: zig_unreachable(); 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); case ZigLLVM_Wasm: return construct_linker_job_wasm(lj); } } void codegen_link(CodeGen *g, const char *out_file) { codegen_add_time_event(g, "Build Dependencies"); LinkJob lj = {0}; // even though we're calling LLD as a library it thinks the first // argument is its own exe name lj.args.append("lld"); lj.rpath_table.init(4); lj.codegen = g; if (out_file) { buf_init_from_str(&lj.out_file, out_file); } else { buf_resize(&lj.out_file, 0); } if (g->verbose_llvm_ir) { fprintf(stderr, "\nOptimization:\n"); fprintf(stderr, "---------------\n"); fflush(stderr); LLVMDumpModule(g->module); } bool override_out_file = (buf_len(&lj.out_file) != 0); if (!override_out_file) { assert(g->root_out_name); buf_init_from_buf(&lj.out_file, g->root_out_name); if (g->out_type == OutTypeExe) { buf_append_str(&lj.out_file, target_exe_file_ext(&g->zig_target)); } } if (g->out_type == OutTypeObj) { if (override_out_file) { assert(g->link_objects.length == 1); Buf *o_file_path = g->link_objects.at(0); int err; if ((err = os_rename(o_file_path, &lj.out_file))) { zig_panic("unable to rename object file %s into final output %s: %s", buf_ptr(o_file_path), buf_ptr(&lj.out_file), err_str(err)); } } return; } if (g->out_type == OutTypeLib && g->is_static) { // invoke `ar` // example: // # static link into libfoo.a // ar rcs libfoo.a foo1.o foo2.o zig_panic("TODO invoke ar"); return; } lj.link_in_crt = (g->libc_link_lib != nullptr && g->out_type == OutTypeExe); construct_linker_job(&lj); if (g->verbose_link) { for (size_t i = 0; i < lj.args.length; i += 1) { const char *space = (i != 0) ? " " : ""; fprintf(stderr, "%s%s", space, lj.args.at(i)); } fprintf(stderr, "\n"); } Buf diag = BUF_INIT; codegen_add_time_event(g, "LLVM Link"); if (!zig_lld_link(g->zig_target.oformat, lj.args.items, lj.args.length, &diag)) { fprintf(stderr, "%s\n", buf_ptr(&diag)); exit(1); } codegen_add_time_event(g, "Done"); }