From 5a3c02137e6a15d3b8c1fb3595d55003ea44139a Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 8 Oct 2018 13:24:39 -0400 Subject: support building static libraries closes #1493 closes #54 --- src/link.cpp | 43 +++++++++++++++++++++++-------------------- 1 file changed, 23 insertions(+), 20 deletions(-) (limited to 'src/link.cpp') diff --git a/src/link.cpp b/src/link.cpp index 8b75f0e783..c961042324 100644 --- a/src/link.cpp +++ b/src/link.cpp @@ -29,9 +29,9 @@ static const char *get_libc_static_file(CodeGen *g, const char *file) { return buf_ptr(out_buf); } -static Buf *build_o_raw(CodeGen *parent_gen, const char *oname, Buf *full_path) { +static Buf *build_a_raw(CodeGen *parent_gen, const char *aname, 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, + CodeGen *child_gen = codegen_create(full_path, child_target, OutTypeLib, parent_gen->build_mode, parent_gen->zig_lib_dir); child_gen->out_h_path = nullptr; @@ -43,32 +43,26 @@ static Buf *build_o_raw(CodeGen *parent_gen, const char *oname, Buf *full_path) child_gen->verbose_cimport = parent_gen->verbose_cimport; codegen_set_strip(child_gen, parent_gen->strip_debug_symbols); - codegen_set_is_static(child_gen, parent_gen->is_static); + codegen_set_is_static(child_gen, true); - codegen_set_out_name(child_gen, buf_create_from_str(oname)); + codegen_set_out_name(child_gen, buf_create_from_str(aname)); 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; - } - child_gen->enable_cache = true; codegen_build_and_link(child_gen); return &child_gen->output_file_path; } -static Buf *build_o(CodeGen *parent_gen, const char *oname) { - Buf *source_basename = buf_sprintf("%s.zig", oname); +static Buf *build_a(CodeGen *parent_gen, const char *aname) { + Buf *source_basename = buf_sprintf("%s.zig", aname); 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); + return build_a_raw(parent_gen, aname, full_path); } static Buf *build_compiler_rt(CodeGen *parent_gen) { @@ -77,7 +71,7 @@ static Buf *build_compiler_rt(CodeGen *parent_gen) { 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); + return build_a_raw(parent_gen, "compiler_rt", full_path); } static const char *get_darwin_arch_string(const ZigTarget *t) { @@ -317,8 +311,8 @@ static void construct_linker_job_elf(LinkJob *lj) { 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)); + Buf *builtin_a_path = build_a(g, "builtin"); + lj->args.append(buf_ptr(builtin_a_path)); } // sometimes libgcc is missing stuff, so we still build compiler_rt and rely on weak linkage @@ -548,8 +542,8 @@ static void construct_linker_job_coff(LinkJob *lj) { 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)); + Buf *builtin_a_path = build_a(g, "builtin"); + lj->args.append(buf_ptr(builtin_a_path)); } // msvc compiler_rt is missing some stuff, so we still build it and rely on weak linkage @@ -960,8 +954,17 @@ void codegen_link(CodeGen *g) { } if (g->out_type == OutTypeLib && g->is_static) { - fprintf(stderr, "Zig does not yet support creating static libraries\nSee https://github.com/ziglang/zig/issues/1493\n"); - exit(1); + ZigList file_names = {}; + for (size_t i = 0; i < g->link_objects.length; i += 1) { + file_names.append((const char *)buf_ptr(g->link_objects.at(i))); + } + ZigLLVM_OSType os_type = get_llvm_os_type(g->zig_target.os); + codegen_add_time_event(g, "LLVM Link"); + if (ZigLLVMWriteArchive(buf_ptr(&g->output_file_path), file_names.items, file_names.length, os_type)) { + fprintf(stderr, "Unable to write archive '%s'\n", buf_ptr(&g->output_file_path)); + exit(1); + } + return; } lj.link_in_crt = (g->libc_link_lib != nullptr && g->out_type == OutTypeExe); -- cgit v1.2.3 From 6b93495792678bd00a6205967bb2704a9a35e9c7 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 9 Oct 2018 12:18:09 -0400 Subject: more efficient builtin library code generation * introduce --disable-pic option which can generally be allowed to be the default. compiler_rt.a and builtin.a get this option when you build a static executable. * compiler_rt and builtin libraries are not built for build-lib --static * posix_spawn instead of fork/execv * disable the error limit on LLD. Fixes the blank lines printed --- src/all_types.hpp | 1 + src/codegen.cpp | 6 +++++- src/link.cpp | 19 ++++++++++--------- src/main.cpp | 12 ++++++++++++ src/os.cpp | 34 ++++++++++++++++------------------ 5 files changed, 44 insertions(+), 28 deletions(-) (limited to 'src/link.cpp') diff --git a/src/all_types.hpp b/src/all_types.hpp index d7071590d8..a144013947 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -1763,6 +1763,7 @@ struct CodeGen { bool linker_rdynamic; bool no_rosegment_workaround; bool each_lib_rpath; + bool disable_pic; Buf *mmacosx_version_min; Buf *mios_version_min; diff --git a/src/codegen.cpp b/src/codegen.cpp index fd7a386078..02105b4d2a 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -7228,7 +7228,10 @@ static void init(CodeGen *g) { bool is_optimized = g->build_mode != BuildModeDebug; LLVMCodeGenOptLevel opt_level = is_optimized ? LLVMCodeGenLevelAggressive : LLVMCodeGenLevelNone; - LLVMRelocMode reloc_mode = g->is_static ? LLVMRelocStatic : LLVMRelocPIC; + if (g->out_type == OutTypeExe && g->is_static) { + g->disable_pic = true; + } + LLVMRelocMode reloc_mode = g->disable_pic ? LLVMRelocStatic : LLVMRelocPIC; const char *target_specific_cpu_args; const char *target_specific_features; @@ -8047,6 +8050,7 @@ static Error check_cache(CodeGen *g, Buf *manifest_dir, Buf *digest) { cache_bool(ch, g->linker_rdynamic); cache_bool(ch, g->no_rosegment_workaround); cache_bool(ch, g->each_lib_rpath); + cache_bool(ch, g->disable_pic); cache_buf_opt(ch, g->mmacosx_version_min); cache_buf_opt(ch, g->mios_version_min); cache_usize(ch, g->version_major); diff --git a/src/link.cpp b/src/link.cpp index c961042324..095d3d79d7 100644 --- a/src/link.cpp +++ b/src/link.cpp @@ -44,6 +44,7 @@ static Buf *build_a_raw(CodeGen *parent_gen, const char *aname, Buf *full_path) codegen_set_strip(child_gen, parent_gen->strip_debug_symbols); codegen_set_is_static(child_gen, true); + child_gen->disable_pic = parent_gen->disable_pic; codegen_set_out_name(child_gen, buf_create_from_str(aname)); @@ -209,10 +210,9 @@ static void construct_linker_job_elf(LinkJob *lj) { lj->args.append(getLDMOption(&g->zig_target)); bool is_lib = g->out_type == OutTypeLib; - bool is_static = g->is_static || (!is_lib && g->link_libs_list.length == 0); - bool shared = !is_static && is_lib; + bool shared = !g->is_static && is_lib; Buf *soname = nullptr; - if (is_static) { + 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) { @@ -236,7 +236,7 @@ static void construct_linker_job_elf(LinkJob *lj) { if (lj->link_in_crt) { const char *crt1o; const char *crtbegino; - if (is_static) { + if (g->is_static) { crt1o = "crt1.o"; crtbegino = "crtbeginT.o"; } else { @@ -287,7 +287,7 @@ static void construct_linker_job_elf(LinkJob *lj) { lj->args.append(buf_ptr(g->libc_static_lib_dir)); } - if (!is_static) { + if (!g->is_static) { if (g->dynamic_linker != nullptr) { assert(buf_len(g->dynamic_linker) != 0); lj->args.append("-dynamic-linker"); @@ -309,7 +309,7 @@ static void construct_linker_job_elf(LinkJob *lj) { lj->args.append((const char *)buf_ptr(g->link_objects.at(i))); } - if (g->out_type == OutTypeExe || g->out_type == OutTypeLib) { + if (g->out_type == OutTypeExe || (g->out_type == OutTypeLib && !g->is_static)) { if (g->libc_link_lib == nullptr) { Buf *builtin_a_path = build_a(g, "builtin"); lj->args.append(buf_ptr(builtin_a_path)); @@ -339,7 +339,7 @@ static void construct_linker_job_elf(LinkJob *lj) { // libc dep if (g->libc_link_lib != nullptr) { - if (is_static) { + if (g->is_static) { lj->args.append("--start-group"); lj->args.append("-lgcc"); lj->args.append("-lgcc_eh"); @@ -540,7 +540,7 @@ static void construct_linker_job_coff(LinkJob *lj) { lj->args.append((const char *)buf_ptr(g->link_objects.at(i))); } - if (g->out_type == OutTypeExe || g->out_type == OutTypeLib) { + if (g->out_type == OutTypeExe || (g->out_type == OutTypeLib && !g->is_static)) { if (g->libc_link_lib == nullptr) { Buf *builtin_a_path = build_a(g, "builtin"); lj->args.append(buf_ptr(builtin_a_path)); @@ -872,7 +872,7 @@ static void construct_linker_job_macho(LinkJob *lj) { } // 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) { + if (g->out_type == OutTypeExe || (g->out_type == OutTypeLib && !g->is_static)) { Buf *compiler_rt_o_path = build_compiler_rt(g); lj->args.append(buf_ptr(compiler_rt_o_path)); } @@ -969,6 +969,7 @@ void codegen_link(CodeGen *g) { lj.link_in_crt = (g->libc_link_lib != nullptr && g->out_type == OutTypeExe); + lj.args.append("-error-limit=0"); construct_linker_job(&lj); diff --git a/src/main.cpp b/src/main.cpp index f9df802cb3..5e827b5ba6 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -47,6 +47,7 @@ static int print_full_usage(const char *arg0) { " --cache-dir [path] override the cache directory\n" " --cache [auto|off|on] build in global cache, print out paths to stdout\n" " --color [auto|off|on] enable or disable colored error messages\n" + " --disable-pic disable Position Independent Code for libraries\n" " --emit [asm|bin|llvm-ir] emit a specific file format as compilation output\n" " -ftime-report print timing diagnostics\n" " --libc-include-dir [path] directory where libc stdlib.h resides\n" @@ -386,6 +387,7 @@ int main(int argc, char **argv) { size_t ver_minor = 0; size_t ver_patch = 0; bool timing_info = false; + bool disable_pic = false; const char *cache_dir = nullptr; CliPkg *cur_pkg = allocate(1); BuildMode build_mode = BuildModeDebug; @@ -556,6 +558,8 @@ int main(int argc, char **argv) { each_lib_rpath = true; } else if (strcmp(arg, "-ftime-report") == 0) { timing_info = true; + } else if (strcmp(arg, "--disable-pic") == 0) { + disable_pic = true; } else if (strcmp(arg, "--test-cmd-bin") == 0) { test_exec_args.append(nullptr); } else if (arg[1] == 'L' && arg[2] != 0) { @@ -849,6 +853,14 @@ int main(int argc, char **argv) { buf_out_name = buf_create_from_str("run"); } CodeGen *g = codegen_create(zig_root_source_file, target, out_type, build_mode, get_zig_lib_dir()); + if (disable_pic) { + if (out_type != OutTypeLib || !is_static) { + fprintf(stderr, "--disable-pic only applies to static libraries"); + return EXIT_FAILURE; + } + g->disable_pic = true; + } + g->enable_time_report = timing_info; buf_init_from_str(&g->cache_dir, cache_dir ? cache_dir : default_zig_cache_name); codegen_set_out_name(g, buf_out_name); diff --git a/src/os.cpp b/src/os.cpp index ed069b2ff8..e1bd6bd479 100644 --- a/src/os.cpp +++ b/src/os.cpp @@ -46,6 +46,7 @@ typedef SSIZE_T ssize_t; #include #include #include +#include #endif @@ -88,25 +89,22 @@ static void populate_termination(Termination *term, int status) { } static void os_spawn_process_posix(const char *exe, ZigList &args, Termination *term) { - pid_t pid = fork(); - if (pid == -1) - zig_panic("fork failed: %s", strerror(errno)); - if (pid == 0) { - // child - const char **argv = allocate(args.length + 2); - argv[0] = exe; - argv[args.length + 1] = nullptr; - for (size_t i = 0; i < args.length; i += 1) { - argv[i + 1] = args.at(i); - } - execvp(exe, const_cast(argv)); - zig_panic("execvp failed: %s", strerror(errno)); - } else { - // parent - int status; - waitpid(pid, &status, 0); - populate_termination(term, status); + const char **argv = allocate(args.length + 2); + argv[0] = exe; + argv[args.length + 1] = nullptr; + for (size_t i = 0; i < args.length; i += 1) { + argv[i + 1] = args.at(i); } + + pid_t pid; + int rc = posix_spawn(&pid, exe, nullptr, nullptr, const_cast(argv), environ); + if (rc != 0) { + zig_panic("posix_spawn failed: %s", strerror(rc)); + } + + int status; + waitpid(pid, &status, 0); + populate_termination(term, status); } #endif -- cgit v1.2.3 From e0a94db65e24c756e5e91ac1dfafa5c85035eaee Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 9 Oct 2018 12:46:22 -0400 Subject: fix error limit linker arg on windows --- src/link.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'src/link.cpp') diff --git a/src/link.cpp b/src/link.cpp index 095d3d79d7..6166584353 100644 --- a/src/link.cpp +++ b/src/link.cpp @@ -192,6 +192,8 @@ static Buf *get_dynamic_linker_path(CodeGen *g) { static void construct_linker_job_elf(LinkJob *lj) { CodeGen *g = lj->codegen; + lj->args.append("-error-limit=0"); + if (g->libc_link_lib != nullptr) { find_libc_lib_path(g); } @@ -381,6 +383,7 @@ static void construct_linker_job_elf(LinkJob *lj) { static void construct_linker_job_wasm(LinkJob *lj) { CodeGen *g = lj->codegen; + lj->args.append("-error-limit=0"); lj->args.append("--no-entry"); // So lld doesn't look for _start. lj->args.append("-o"); lj->args.append(buf_ptr(&g->output_file_path)); @@ -419,6 +422,8 @@ static bool zig_lld_link(ZigLLVM_ObjectFormatType oformat, const char **args, si static void construct_linker_job_coff(LinkJob *lj) { CodeGen *g = lj->codegen; + lj->args.append("/ERRORLIMIT:0"); + if (g->libc_link_lib != nullptr) { find_libc_lib_path(g); } @@ -755,6 +760,7 @@ static bool darwin_version_lt(DarwinPlatform *platform, int major, int minor) { static void construct_linker_job_macho(LinkJob *lj) { CodeGen *g = lj->codegen; + lj->args.append("-error-limit=0"); lj->args.append("-demangle"); if (g->linker_rdynamic) { @@ -969,7 +975,6 @@ void codegen_link(CodeGen *g) { lj.link_in_crt = (g->libc_link_lib != nullptr && g->out_type == OutTypeExe); - lj.args.append("-error-limit=0"); construct_linker_job(&lj); -- cgit v1.2.3 From e29a3b1d2a74732c6e6e4a1f10a02b45797fe4e2 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 9 Oct 2018 13:02:01 -0400 Subject: stage1 link: compiler_rt and builtin libs know ... ...whether they will be linked with libc --- src/link.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'src/link.cpp') diff --git a/src/link.cpp b/src/link.cpp index 6166584353..ad3e0ef8d0 100644 --- a/src/link.cpp +++ b/src/link.cpp @@ -53,6 +53,14 @@ static Buf *build_a_raw(CodeGen *parent_gen, const char *aname, Buf *full_path) codegen_set_mmacosx_version_min(child_gen, parent_gen->mmacosx_version_min); codegen_set_mios_version_min(child_gen, parent_gen->mios_version_min); + // This is so that compiler_rt and builtin libraries know whether they + // will eventually be linked with libc. They make different decisions + // about what to export depending on whether libc is linked. + if (parent_gen->libc_link_lib != nullptr) { + LinkLib *new_link_lib = codegen_add_link_lib(child_gen, parent_gen->libc_link_lib->name); + new_link_lib->provided_explicitly = parent_gen->libc_link_lib->provided_explicitly; + } + child_gen->enable_cache = true; codegen_build_and_link(child_gen); return &child_gen->output_file_path; -- cgit v1.2.3 From 81c6f087241b6b7a1c12de72a2061cb02df0f93f Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 9 Oct 2018 13:12:50 -0400 Subject: add workaround for bad LLD macos code --- src/link.cpp | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) (limited to 'src/link.cpp') diff --git a/src/link.cpp b/src/link.cpp index ad3e0ef8d0..424b06169e 100644 --- a/src/link.cpp +++ b/src/link.cpp @@ -31,8 +31,18 @@ static const char *get_libc_static_file(CodeGen *g, const char *file) { static Buf *build_a_raw(CodeGen *parent_gen, const char *aname, 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, OutTypeLib, parent_gen->build_mode, - parent_gen->zig_lib_dir); + + // The Mach-O LLD code is not well maintained, and trips an assertion + // when we link compiler_rt and builtin as libraries rather than objects. + // Here we workaround this by having compiler_rt and builtin be objects. + // TODO write our own linker. https://github.com/ziglang/zig/issues/1535 + OutType child_out_type = OutTypeLib; + if (parent_gen->zig_target.os == OsMacOSX) { + child_out_type = OutTypeObj; + } + + CodeGen *child_gen = codegen_create(full_path, child_target, child_out_type, + parent_gen->build_mode, parent_gen->zig_lib_dir); child_gen->out_h_path = nullptr; child_gen->verbose_tokenize = parent_gen->verbose_tokenize; -- cgit v1.2.3