| author | |
| committer | |
| log | 7ccf7807b3f12428adc5d9ca0ede4e3f4ec6dbbc |
| tree | af8db742f220123b24c3959b6f33173d8b1e619e |
| parent | 3b97940fb384285a8f88088c9e73e582f8bdbf96 |
| signature |
14 files changed, 691 insertions(+), 53 deletions(-)
CMakeLists.txt+4| ... | ... | @@ -421,6 +421,7 @@ set(ZIG_MAIN_SRC "${CMAKE_SOURCE_DIR}/src/main.cpp") |
| 421 | 421 | set(ZIG0_SHIM_SRC "${CMAKE_SOURCE_DIR}/src/userland.cpp") |
| 422 | 422 | |
| 423 | 423 | set(ZIG_SOURCES |
| 424 | "${CMAKE_SOURCE_DIR}/src/glibc.cpp" | |
| 424 | 425 | "${CMAKE_SOURCE_DIR}/src/analyze.cpp" |
| 425 | 426 | "${CMAKE_SOURCE_DIR}/src/ast_render.cpp" |
| 426 | 427 | "${CMAKE_SOURCE_DIR}/src/bigfloat.cpp" |
| ... | ... | @@ -2667,6 +2668,9 @@ set(ZIG_MUSL_SRC_FILES |
| 2667 | 2668 | ) |
| 2668 | 2669 | |
| 2669 | 2670 | set(ZIG_LIBC_FILES |
| 2671 | "glibc/abi.txt" | |
| 2672 | "glibc/fns.txt" | |
| 2673 | "glibc/vers.txt" | |
| 2670 | 2674 | "glibc/bits/byteswap.h" |
| 2671 | 2675 | "glibc/bits/endian.h" |
| 2672 | 2676 | "glibc/bits/floatn-common.h" |
src/all_types.hpp+4| ... | ... | @@ -1921,6 +1921,7 @@ struct CodeGen { |
| 1921 | 1921 | Buf *zig_lib_dir; |
| 1922 | 1922 | Buf *zig_std_dir; |
| 1923 | 1923 | Buf *dynamic_linker_path; |
| 1924 | Buf *version_script_path; | |
| 1924 | 1925 | |
| 1925 | 1926 | const char **llvm_argv; |
| 1926 | 1927 | size_t llvm_argv_len; |
| ... | ... | @@ -3788,6 +3789,9 @@ static const size_t stack_trace_ptr_count = 32; |
| 3788 | 3789 | #define NAMESPACE_SEP_CHAR '.' |
| 3789 | 3790 | #define NAMESPACE_SEP_STR "." |
| 3790 | 3791 | |
| 3792 | #define CACHE_OUT_SUBDIR "o" | |
| 3793 | #define CACHE_HASH_SUBDIR "h" | |
| 3794 | ||
| 3791 | 3795 | enum FloatMode { |
| 3792 | 3796 | FloatModeStrict, |
| 3793 | 3797 | FloatModeOptimized, |
src/codegen.cpp+33-3| ... | ... | @@ -24,9 +24,6 @@ |
| 24 | 24 | #include <stdio.h> |
| 25 | 25 | #include <errno.h> |
| 26 | 26 | |
| 27 | #define CACHE_OUT_SUBDIR "o" | |
| 28 | #define CACHE_HASH_SUBDIR "h" | |
| 29 | ||
| 30 | 27 | static void init_darwin_native(CodeGen *g) { |
| 31 | 28 | char *osx_target = getenv("MACOSX_DEPLOYMENT_TARGET"); |
| 32 | 29 | char *ios_target = getenv("IPHONEOS_DEPLOYMENT_TARGET"); |
| ... | ... | @@ -9502,6 +9499,7 @@ static Error check_cache(CodeGen *g, Buf *manifest_dir, Buf *digest) { |
| 9502 | 9499 | cache_buf(ch, &g->libc->kernel32_lib_dir); |
| 9503 | 9500 | } |
| 9504 | 9501 | cache_buf_opt(ch, g->dynamic_linker_path); |
| 9502 | cache_buf_opt(ch, g->version_script_path); | |
| 9505 | 9503 | |
| 9506 | 9504 | // gen_c_objects appends objects to g->link_objects which we want to include in the hash |
| 9507 | 9505 | gen_c_objects(g); |
| ... | ... | @@ -9695,3 +9693,35 @@ ZigPackage *codegen_create_package(CodeGen *g, const char *root_src_dir, const c |
| 9695 | 9693 | } |
| 9696 | 9694 | return pkg; |
| 9697 | 9695 | } |
| 9696 | ||
| 9697 | CodeGen *create_child_codegen(CodeGen *parent_gen, Buf *root_src_path, OutType out_type, | |
| 9698 | ZigLibCInstallation *libc) | |
| 9699 | { | |
| 9700 | CodeGen *child_gen = codegen_create(nullptr, root_src_path, parent_gen->zig_target, out_type, | |
| 9701 | parent_gen->build_mode, parent_gen->zig_lib_dir, parent_gen->zig_std_dir, libc, get_stage1_cache_path()); | |
| 9702 | child_gen->disable_gen_h = true; | |
| 9703 | child_gen->want_stack_check = WantStackCheckDisabled; | |
| 9704 | child_gen->verbose_tokenize = parent_gen->verbose_tokenize; | |
| 9705 | child_gen->verbose_ast = parent_gen->verbose_ast; | |
| 9706 | child_gen->verbose_link = parent_gen->verbose_link; | |
| 9707 | child_gen->verbose_ir = parent_gen->verbose_ir; | |
| 9708 | child_gen->verbose_llvm_ir = parent_gen->verbose_llvm_ir; | |
| 9709 | child_gen->verbose_cimport = parent_gen->verbose_cimport; | |
| 9710 | child_gen->verbose_cc = parent_gen->verbose_cc; | |
| 9711 | child_gen->llvm_argv = parent_gen->llvm_argv; | |
| 9712 | child_gen->dynamic_linker_path = parent_gen->dynamic_linker_path; | |
| 9713 | ||
| 9714 | codegen_set_strip(child_gen, parent_gen->strip_debug_symbols); | |
| 9715 | child_gen->want_pic = parent_gen->have_pic ? WantPICEnabled : WantPICDisabled; | |
| 9716 | child_gen->valgrind_support = ValgrindSupportDisabled; | |
| 9717 | ||
| 9718 | codegen_set_errmsg_color(child_gen, parent_gen->err_color); | |
| 9719 | ||
| 9720 | codegen_set_mmacosx_version_min(child_gen, parent_gen->mmacosx_version_min); | |
| 9721 | codegen_set_mios_version_min(child_gen, parent_gen->mios_version_min); | |
| 9722 | ||
| 9723 | child_gen->enable_cache = true; | |
| 9724 | ||
| 9725 | return child_gen; | |
| 9726 | } | |
| 9727 |
src/codegen.hpp+3| ... | ... | @@ -20,6 +20,9 @@ CodeGen *codegen_create(Buf *main_pkg_path, Buf *root_src_path, const ZigTarget |
| 20 | 20 | OutType out_type, BuildMode build_mode, Buf *zig_lib_dir, Buf *override_std_dir, |
| 21 | 21 | ZigLibCInstallation *libc, Buf *cache_dir); |
| 22 | 22 | |
| 23 | CodeGen *create_child_codegen(CodeGen *parent_gen, Buf *root_src_path, OutType out_type, | |
| 24 | ZigLibCInstallation *libc); | |
| 25 | ||
| 23 | 26 | void codegen_set_clang_argv(CodeGen *codegen, const char **args, size_t len); |
| 24 | 27 | void codegen_set_llvm_argv(CodeGen *codegen, const char **args, size_t len); |
| 25 | 28 | void codegen_set_is_test(CodeGen *codegen, bool is_test); |
src/compiler.cpp+26| ... | ... | @@ -9,9 +9,13 @@ static Buf saved_stage1_path = BUF_INIT; |
| 9 | 9 | static Buf saved_lib_dir = BUF_INIT; |
| 10 | 10 | static Buf saved_special_dir = BUF_INIT; |
| 11 | 11 | static Buf saved_std_dir = BUF_INIT; |
| 12 | ||
| 12 | 13 | static Buf saved_dynamic_linker_path = BUF_INIT; |
| 13 | 14 | static bool searched_for_dyn_linker = false; |
| 14 | 15 | |
| 16 | static Buf saved_libc_path = BUF_INIT; | |
| 17 | static bool searched_for_libc = false; | |
| 18 | ||
| 15 | 19 | Buf *get_stage1_cache_path(void) { |
| 16 | 20 | if (saved_stage1_path.list.length != 0) { |
| 17 | 21 | return &saved_stage1_path; |
| ... | ... | @@ -36,6 +40,28 @@ static void detect_dynamic_linker(Buf *lib_path) { |
| 36 | 40 | #endif |
| 37 | 41 | } |
| 38 | 42 | |
| 43 | const Buf *get_self_libc_path(void) { | |
| 44 | for (;;) { | |
| 45 | if (saved_libc_path.list.length != 0) { | |
| 46 | return &saved_libc_path; | |
| 47 | } | |
| 48 | if (searched_for_libc) | |
| 49 | return nullptr; | |
| 50 | ZigList<Buf *> lib_paths = {}; | |
| 51 | Error err; | |
| 52 | if ((err = os_self_exe_shared_libs(lib_paths))) | |
| 53 | return nullptr; | |
| 54 | for (size_t i = 0; i < lib_paths.length; i += 1) { | |
| 55 | Buf *lib_path = lib_paths.at(i); | |
| 56 | if (buf_ends_with_str(lib_path, "libc.so.6")) { | |
| 57 | buf_init_from_buf(&saved_libc_path, lib_path); | |
| 58 | return &saved_libc_path; | |
| 59 | } | |
| 60 | } | |
| 61 | searched_for_libc = true; | |
| 62 | } | |
| 63 | } | |
| 64 | ||
| 39 | 65 | Buf *get_self_dynamic_linker_path(void) { |
| 40 | 66 | for (;;) { |
| 41 | 67 | if (saved_dynamic_linker_path.list.length != 0) { |
src/compiler.hpp+1| ... | ... | @@ -14,6 +14,7 @@ |
| 14 | 14 | Buf *get_stage1_cache_path(void); |
| 15 | 15 | Error get_compiler_id(Buf **result); |
| 16 | 16 | Buf *get_self_dynamic_linker_path(void); |
| 17 | Buf *get_self_libc_path(void); | |
| 17 | 18 | |
| 18 | 19 | Buf *get_zig_lib_dir(void); |
| 19 | 20 | Buf *get_zig_special_dir(Buf *zig_lib_dir); |
src/glibc.cpp created+410| ... | ... | @@ -0,0 +1,410 @@ |
| 1 | /* | |
| 2 | * Copyright (c) 2019 Andrew Kelley | |
| 3 | * | |
| 4 | * This file is part of zig, which is MIT licensed. | |
| 5 | * See http://opensource.org/licenses/MIT | |
| 6 | */ | |
| 7 | ||
| 8 | #include "glibc.hpp" | |
| 9 | #include "compiler.hpp" | |
| 10 | #include "cache_hash.hpp" | |
| 11 | #include "codegen.hpp" | |
| 12 | ||
| 13 | static const ZigGLibCLib glibc_libs[] = { | |
| 14 | {"c", 6}, | |
| 15 | {"m", 6}, | |
| 16 | {"pthread", 0}, | |
| 17 | {"dl", 2}, | |
| 18 | {"rt", 1}, | |
| 19 | }; | |
| 20 | ||
| 21 | Error glibc_load_metadata(ZigGLibCAbi **out_result, Buf *zig_lib_dir, bool verbose) { | |
| 22 | Error err; | |
| 23 | ||
| 24 | ZigGLibCAbi *glibc_abi = allocate<ZigGLibCAbi>(1); | |
| 25 | glibc_abi->vers_txt_path = buf_sprintf("%s" OS_SEP "libc" OS_SEP "glibc" OS_SEP "vers.txt", buf_ptr(zig_lib_dir)); | |
| 26 | glibc_abi->fns_txt_path = buf_sprintf("%s" OS_SEP "libc" OS_SEP "glibc" OS_SEP "fns.txt", buf_ptr(zig_lib_dir)); | |
| 27 | glibc_abi->abi_txt_path = buf_sprintf("%s" OS_SEP "libc" OS_SEP "glibc" OS_SEP "abi.txt", buf_ptr(zig_lib_dir)); | |
| 28 | glibc_abi->version_table.init(16); | |
| 29 | ||
| 30 | Buf *vers_txt_contents = buf_alloc(); | |
| 31 | if ((err = os_fetch_file_path(glibc_abi->vers_txt_path, vers_txt_contents))) { | |
| 32 | if (verbose) { | |
| 33 | fprintf(stderr, "Unable to read %s: %s\n", buf_ptr(glibc_abi->vers_txt_path), err_str(err)); | |
| 34 | } | |
| 35 | return err; | |
| 36 | } | |
| 37 | Buf *fns_txt_contents = buf_alloc(); | |
| 38 | if ((err = os_fetch_file_path(glibc_abi->fns_txt_path, fns_txt_contents))) { | |
| 39 | if (verbose) { | |
| 40 | fprintf(stderr, "Unable to read %s: %s\n", buf_ptr(glibc_abi->fns_txt_path), err_str(err)); | |
| 41 | } | |
| 42 | return err; | |
| 43 | } | |
| 44 | Buf *abi_txt_contents = buf_alloc(); | |
| 45 | if ((err = os_fetch_file_path(glibc_abi->abi_txt_path, abi_txt_contents))) { | |
| 46 | if (verbose) { | |
| 47 | fprintf(stderr, "Unable to read %s: %s\n", buf_ptr(glibc_abi->abi_txt_path), err_str(err)); | |
| 48 | } | |
| 49 | return err; | |
| 50 | } | |
| 51 | ||
| 52 | { | |
| 53 | SplitIterator it = memSplit(buf_to_slice(vers_txt_contents), str("\n")); | |
| 54 | for (;;) { | |
| 55 | Optional<Slice<uint8_t>> opt_component = SplitIterator_next(&it); | |
| 56 | if (!opt_component.is_some) break; | |
| 57 | Buf *ver_buf = buf_create_from_slice(opt_component.value); | |
| 58 | ZigGLibCVersion *this_ver = glibc_abi->all_versions.add_one(); | |
| 59 | if ((err = target_parse_glibc_version(this_ver, buf_ptr(ver_buf)))) { | |
| 60 | if (verbose) { | |
| 61 | fprintf(stderr, "Unable to parse glibc version '%s': %s\n", buf_ptr(ver_buf), err_str(err)); | |
| 62 | } | |
| 63 | return err; | |
| 64 | } | |
| 65 | } | |
| 66 | } | |
| 67 | { | |
| 68 | SplitIterator it = memSplit(buf_to_slice(fns_txt_contents), str("\n")); | |
| 69 | for (;;) { | |
| 70 | Optional<Slice<uint8_t>> opt_component = SplitIterator_next(&it); | |
| 71 | if (!opt_component.is_some) break; | |
| 72 | SplitIterator line_it = memSplit(opt_component.value, str(" ")); | |
| 73 | Optional<Slice<uint8_t>> opt_fn_name = SplitIterator_next(&line_it); | |
| 74 | if (!opt_fn_name.is_some) { | |
| 75 | if (verbose) { | |
| 76 | fprintf(stderr, "%s: Expected function name\n", buf_ptr(glibc_abi->fns_txt_path)); | |
| 77 | } | |
| 78 | return ErrorInvalidFormat; | |
| 79 | } | |
| 80 | Optional<Slice<uint8_t>> opt_lib_name = SplitIterator_next(&line_it); | |
| 81 | if (!opt_lib_name.is_some) { | |
| 82 | if (verbose) { | |
| 83 | fprintf(stderr, "%s: Expected lib name\n", buf_ptr(glibc_abi->fns_txt_path)); | |
| 84 | } | |
| 85 | return ErrorInvalidFormat; | |
| 86 | } | |
| 87 | ||
| 88 | Buf *this_fn_name = buf_create_from_slice(opt_fn_name.value); | |
| 89 | Buf *this_lib_name = buf_create_from_slice(opt_lib_name.value); | |
| 90 | glibc_abi->all_functions.append({ this_fn_name, glibc_lib_find(buf_ptr(this_lib_name)) }); | |
| 91 | } | |
| 92 | } | |
| 93 | { | |
| 94 | SplitIterator it = memSplit(buf_to_slice(abi_txt_contents), str("\n")); | |
| 95 | ZigGLibCVerList *ver_list_base = nullptr; | |
| 96 | for (;;) { | |
| 97 | if (ver_list_base == nullptr) { | |
| 98 | Optional<Slice<uint8_t>> opt_line = SplitIterator_next_separate(&it); | |
| 99 | if (!opt_line.is_some) break; | |
| 100 | ||
| 101 | ver_list_base = allocate<ZigGLibCVerList>(glibc_abi->all_functions.length); | |
| 102 | ZigTarget *target = allocate<ZigTarget>(1); | |
| 103 | SplitIterator line_it = memSplit(opt_line.value, str(" ")); | |
| 104 | for (;;) { | |
| 105 | Optional<Slice<uint8_t>> opt_target = SplitIterator_next(&line_it); | |
| 106 | if (!opt_target.is_some) break; | |
| 107 | ||
| 108 | SplitIterator component_it = memSplit(opt_target.value, str("-")); | |
| 109 | Optional<Slice<uint8_t>> opt_arch = SplitIterator_next(&component_it); | |
| 110 | assert(opt_arch.is_some); | |
| 111 | Optional<Slice<uint8_t>> opt_os = SplitIterator_next(&component_it); | |
| 112 | assert(opt_os.is_some); // it's always "linux" so we ignore it | |
| 113 | Optional<Slice<uint8_t>> opt_abi = SplitIterator_next(&component_it); | |
| 114 | assert(opt_abi.is_some); | |
| 115 | ||
| 116 | ||
| 117 | err = target_parse_archsub(&target->arch, &target->sub_arch, | |
| 118 | (char*)opt_arch.value.ptr, opt_arch.value.len); | |
| 119 | // there's no sub arch so we might get an error, but the arch is still populated | |
| 120 | assert(err == ErrorNone || err == ErrorUnknownArchitecture); | |
| 121 | ||
| 122 | target->os = OsLinux; | |
| 123 | ||
| 124 | err = target_parse_abi(&target->abi, (char*)opt_abi.value.ptr, opt_abi.value.len); | |
| 125 | assert(err == ErrorNone); | |
| 126 | ||
| 127 | glibc_abi->version_table.put(target, ver_list_base); | |
| 128 | } | |
| 129 | continue; | |
| 130 | } | |
| 131 | for (size_t fn_i = 0; fn_i < glibc_abi->all_functions.length; fn_i += 1) { | |
| 132 | ZigGLibCVerList *ver_list = &ver_list_base[fn_i]; | |
| 133 | Optional<Slice<uint8_t>> opt_line = SplitIterator_next_separate(&it); | |
| 134 | assert(opt_line.is_some); | |
| 135 | ||
| 136 | SplitIterator line_it = memSplit(opt_line.value, str(" ")); | |
| 137 | for (;;) { | |
| 138 | Optional<Slice<uint8_t>> opt_ver = SplitIterator_next(&line_it); | |
| 139 | if (!opt_ver.is_some) break; | |
| 140 | assert(ver_list->len < 8); // increase the array len in the type | |
| 141 | ||
| 142 | unsigned long ver_index = strtoul(buf_ptr(buf_create_from_slice(opt_ver.value)), nullptr, 10); | |
| 143 | assert(ver_index < 255); // use a bigger integer in the type | |
| 144 | ver_list->versions[ver_list->len] = ver_index; | |
| 145 | ver_list->len += 1; | |
| 146 | } | |
| 147 | } | |
| 148 | ver_list_base = nullptr; | |
| 149 | } | |
| 150 | } | |
| 151 | ||
| 152 | *out_result = glibc_abi; | |
| 153 | return ErrorNone; | |
| 154 | } | |
| 155 | ||
| 156 | Error glibc_build_dummies_and_maps(CodeGen *g, const ZigGLibCAbi *glibc_abi, const ZigTarget *target, | |
| 157 | Buf **out_dir, bool verbose) | |
| 158 | { | |
| 159 | Error err; | |
| 160 | ||
| 161 | Buf *cache_dir = get_stage1_cache_path(); | |
| 162 | CacheHash *cache_hash = allocate<CacheHash>(1); | |
| 163 | Buf *manifest_dir = buf_sprintf("%s" OS_SEP CACHE_HASH_SUBDIR, buf_ptr(cache_dir)); | |
| 164 | cache_init(cache_hash, manifest_dir); | |
| 165 | ||
| 166 | Buf *compiler_id; | |
| 167 | if ((err = get_compiler_id(&compiler_id))) { | |
| 168 | if (verbose) { | |
| 169 | fprintf(stderr, "unable to get compiler id: %s\n", err_str(err)); | |
| 170 | } | |
| 171 | return err; | |
| 172 | } | |
| 173 | cache_buf(cache_hash, compiler_id); | |
| 174 | cache_int(cache_hash, target->arch); | |
| 175 | cache_int(cache_hash, target->abi); | |
| 176 | cache_int(cache_hash, target->glibc_version->major); | |
| 177 | cache_int(cache_hash, target->glibc_version->minor); | |
| 178 | cache_int(cache_hash, target->glibc_version->patch); | |
| 179 | ||
| 180 | Buf digest = BUF_INIT; | |
| 181 | buf_resize(&digest, 0); | |
| 182 | if ((err = cache_hit(cache_hash, &digest))) { | |
| 183 | // Treat an invalid format error as a cache miss. | |
| 184 | if (err != ErrorInvalidFormat) | |
| 185 | return err; | |
| 186 | } | |
| 187 | // We should always get a cache hit because there are no | |
| 188 | // files in the input hash. | |
| 189 | assert(buf_len(&digest) != 0); | |
| 190 | ||
| 191 | Buf *dummy_dir = buf_alloc(); | |
| 192 | os_path_join(manifest_dir, &digest, dummy_dir); | |
| 193 | ||
| 194 | if ((err = os_make_path(dummy_dir))) | |
| 195 | return err; | |
| 196 | ||
| 197 | Buf *test_if_exists_path = buf_alloc(); | |
| 198 | os_path_join(dummy_dir, buf_create_from_str("ok"), test_if_exists_path); | |
| 199 | ||
| 200 | bool hit; | |
| 201 | if ((err = os_file_exists(test_if_exists_path, &hit))) | |
| 202 | return err; | |
| 203 | ||
| 204 | // TODO this is for debugging | |
| 205 | fprintf(stderr, "dummy so dir: %s\n", buf_ptr(dummy_dir)); | |
| 206 | ||
| 207 | if (hit) { | |
| 208 | *out_dir = dummy_dir; | |
| 209 | return ErrorNone; | |
| 210 | } | |
| 211 | ||
| 212 | ||
| 213 | ZigGLibCVerList *ver_list_base = glibc_abi->version_table.get(target); | |
| 214 | ||
| 215 | uint8_t target_ver_index = 0; | |
| 216 | for (;target_ver_index < glibc_abi->all_versions.length; target_ver_index += 1) { | |
| 217 | const ZigGLibCVersion *this_ver = &glibc_abi->all_versions.at(target_ver_index); | |
| 218 | if (this_ver->major == target->glibc_version->major && | |
| 219 | this_ver->minor == target->glibc_version->minor && | |
| 220 | this_ver->patch == target->glibc_version->patch) | |
| 221 | { | |
| 222 | break; | |
| 223 | } | |
| 224 | } | |
| 225 | if (target_ver_index == glibc_abi->all_versions.length) { | |
| 226 | if (verbose) { | |
| 227 | fprintf(stderr, "Unrecognized glibc version: %d.%d.%d\n", | |
| 228 | target->glibc_version->major, | |
| 229 | target->glibc_version->minor, | |
| 230 | target->glibc_version->patch); | |
| 231 | } | |
| 232 | return ErrorUnknownABI; | |
| 233 | } | |
| 234 | ||
| 235 | Buf *map_file_path = buf_sprintf("%s" OS_SEP "all.map", buf_ptr(dummy_dir)); | |
| 236 | Buf *map_contents = buf_alloc(); | |
| 237 | ||
| 238 | for (uint8_t ver_i = 0; ver_i < glibc_abi->all_versions.length; ver_i += 1) { | |
| 239 | const ZigGLibCVersion *ver = &glibc_abi->all_versions.at(ver_i); | |
| 240 | if (ver->patch == 0) { | |
| 241 | buf_appendf(map_contents, "GLIBC_%d.%d { };\n", ver->major, ver->minor); | |
| 242 | } else { | |
| 243 | buf_appendf(map_contents, "GLIBC_%d.%d.%d { };\n", ver->major, ver->minor, ver->patch); | |
| 244 | } | |
| 245 | } | |
| 246 | ||
| 247 | if ((err = os_write_file(map_file_path, map_contents))) { | |
| 248 | if (verbose) { | |
| 249 | fprintf(stderr, "unable to write %s: %s", buf_ptr(map_file_path), err_str(err)); | |
| 250 | } | |
| 251 | return err; | |
| 252 | } | |
| 253 | ||
| 254 | ||
| 255 | for (size_t lib_i = 0; lib_i < array_length(glibc_libs); lib_i += 1) { | |
| 256 | const ZigGLibCLib *lib = &glibc_libs[lib_i]; | |
| 257 | Buf *zig_file_path = buf_sprintf("%s" OS_SEP "%s.zig", buf_ptr(dummy_dir), lib->name); | |
| 258 | Buf *zig_body = buf_alloc(); | |
| 259 | Buf *zig_footer = buf_alloc(); | |
| 260 | ||
| 261 | buf_appendf(zig_body, "comptime {\n"); | |
| 262 | buf_appendf(zig_body, " asm (\n"); | |
| 263 | ||
| 264 | for (size_t fn_i = 0; fn_i < glibc_abi->all_functions.length; fn_i += 1) { | |
| 265 | const ZigGLibCFn *libc_fn = &glibc_abi->all_functions.at(fn_i); | |
| 266 | if (libc_fn->lib != lib) continue; | |
| 267 | ZigGLibCVerList *ver_list = &ver_list_base[fn_i]; | |
| 268 | // Pick the default symbol version: | |
| 269 | // - If there are no versions, don't emit it | |
| 270 | // - Take the greatest one <= than the target one | |
| 271 | // - If none of them is <= than the | |
| 272 | // specified one don't pick any default version | |
| 273 | if (ver_list->len == 0) continue; | |
| 274 | uint8_t chosen_def_ver_index = 255; | |
| 275 | for (uint8_t ver_i = 0; ver_i < ver_list->len; ver_i += 1) { | |
| 276 | uint8_t ver_index = ver_list->versions[ver_i]; | |
| 277 | if ((chosen_def_ver_index == 255 || ver_index > chosen_def_ver_index) && | |
| 278 | target_ver_index >= ver_index) | |
| 279 | { | |
| 280 | chosen_def_ver_index = ver_index; | |
| 281 | } | |
| 282 | } | |
| 283 | for (uint8_t ver_i = 0; ver_i < ver_list->len; ver_i += 1) { | |
| 284 | uint8_t ver_index = ver_list->versions[ver_i]; | |
| 285 | ||
| 286 | Buf *stub_name; | |
| 287 | const ZigGLibCVersion *ver = &glibc_abi->all_versions.at(ver_index); | |
| 288 | const char *sym_name = buf_ptr(libc_fn->name); | |
| 289 | if (ver->patch == 0) { | |
| 290 | stub_name = buf_sprintf("%s_%d_%d", sym_name, ver->major, ver->minor); | |
| 291 | } else { | |
| 292 | stub_name = buf_sprintf("%s_%d_%d_%d", sym_name, ver->major, ver->minor, ver->patch); | |
| 293 | } | |
| 294 | ||
| 295 | buf_appendf(zig_footer, "export fn %s() void {}\n", buf_ptr(stub_name)); | |
| 296 | ||
| 297 | // Default symbol version definition vs normal symbol version definition | |
| 298 | const char *at_sign_str = (chosen_def_ver_index != 255 && | |
| 299 | ver_index == chosen_def_ver_index) ? "@@" : "@"; | |
| 300 | if (ver->patch == 0) { | |
| 301 | buf_appendf(zig_body, " \\\\ .symver %s, %s%sGLIBC_%d.%d\n", | |
| 302 | buf_ptr(stub_name), sym_name, at_sign_str, ver->major, ver->minor); | |
| 303 | } else { | |
| 304 | buf_appendf(zig_body, " \\\\ .symver %s, %s%sGLIBC_%d.%d.%d\n", | |
| 305 | buf_ptr(stub_name), sym_name, at_sign_str, ver->major, ver->minor, ver->patch); | |
| 306 | } | |
| 307 | // Hide the stub to keep the symbol table clean | |
| 308 | buf_appendf(zig_body, " \\\\ .hidden %s\n", buf_ptr(stub_name)); | |
| 309 | } | |
| 310 | } | |
| 311 | ||
| 312 | buf_appendf(zig_body, " );\n"); | |
| 313 | buf_appendf(zig_body, "}\n"); | |
| 314 | buf_append_buf(zig_body, zig_footer); | |
| 315 | ||
| 316 | if ((err = os_write_file(zig_file_path, zig_body))) { | |
| 317 | if (verbose) { | |
| 318 | fprintf(stderr, "unable to write %s: %s", buf_ptr(zig_file_path), err_str(err)); | |
| 319 | } | |
| 320 | return err; | |
| 321 | } | |
| 322 | ||
| 323 | CodeGen *child_gen = create_child_codegen(g, zig_file_path, OutTypeLib, nullptr); | |
| 324 | codegen_set_out_name(child_gen, buf_create_from_str(lib->name)); | |
| 325 | codegen_set_lib_version(child_gen, lib->sover, 0, 0); | |
| 326 | child_gen->is_dynamic = true; | |
| 327 | child_gen->is_dummy_so = true; | |
| 328 | child_gen->version_script_path = map_file_path; | |
| 329 | child_gen->enable_cache = false; | |
| 330 | child_gen->output_dir = dummy_dir; | |
| 331 | codegen_build_and_link(child_gen); | |
| 332 | } | |
| 333 | ||
| 334 | if ((err = os_write_file(test_if_exists_path, buf_alloc()))) { | |
| 335 | if (verbose) { | |
| 336 | fprintf(stderr, "unable to write %s: %s", buf_ptr(test_if_exists_path), err_str(err)); | |
| 337 | } | |
| 338 | return err; | |
| 339 | } | |
| 340 | *out_dir = dummy_dir; | |
| 341 | return ErrorNone; | |
| 342 | } | |
| 343 | ||
| 344 | uint32_t hash_glibc_target(const ZigTarget *x) { | |
| 345 | return x->arch * 3250106448 + | |
| 346 | x->os * 542534372 + | |
| 347 | x->abi * 59162639; | |
| 348 | } | |
| 349 | ||
| 350 | bool eql_glibc_target(const ZigTarget *a, const ZigTarget *b) { | |
| 351 | return a->arch == b->arch && | |
| 352 | a->os == b->os && | |
| 353 | a->abi == b->abi; | |
| 354 | } | |
| 355 | ||
| 356 | #ifdef ZIG_OS_LINUX | |
| 357 | #include <unistd.h> | |
| 358 | #include <linux/limits.h> | |
| 359 | Error glibc_detect_native_version(ZigGLibCVersion *glibc_ver) { | |
| 360 | Buf *self_libc_path = get_self_libc_path(); | |
| 361 | if (self_libc_path == nullptr) { | |
| 362 | // TODO There is still more we could do to detect the native glibc version. For example, | |
| 363 | // we could look at the ELF file of `/usr/bin/env`, find `libc.so.6`, and then `readlink` | |
| 364 | // to find out the glibc version. This is relevant for the static zig builds distributed | |
| 365 | // on the download page, since the above detection based on zig's own dynamic linking | |
| 366 | // will not work. | |
| 367 | ||
| 368 | return ErrorUnknownABI; | |
| 369 | } | |
| 370 | Buf *link_name = buf_alloc(); | |
| 371 | buf_resize(link_name, PATH_MAX); | |
| 372 | ssize_t amt = readlink(buf_ptr(self_libc_path), buf_ptr(link_name), buf_len(link_name)); | |
| 373 | if (amt == -1) { | |
| 374 | return ErrorUnknownABI; | |
| 375 | } | |
| 376 | buf_resize(link_name, amt); | |
| 377 | if (!buf_starts_with_str(link_name, "libc-") || !buf_ends_with_str(link_name, ".so")) { | |
| 378 | return ErrorUnknownABI; | |
| 379 | } | |
| 380 | // example: "libc-2.3.4.so" | |
| 381 | // example: "libc-2.27.so" | |
| 382 | buf_resize(link_name, buf_len(link_name) - 3); // chop off ".so" | |
| 383 | glibc_ver->major = 2; | |
| 384 | glibc_ver->minor = 0; | |
| 385 | glibc_ver->patch = 0; | |
| 386 | return target_parse_glibc_version(glibc_ver, buf_ptr(link_name) + 5); | |
| 387 | } | |
| 388 | #else | |
| 389 | Error glibc_detect_native_version(ZigGLibCVersion *glibc_ver) { | |
| 390 | return ErrorUnknownABI; | |
| 391 | } | |
| 392 | #endif | |
| 393 | ||
| 394 | size_t glibc_lib_count(void) { | |
| 395 | return array_length(glibc_libs); | |
| 396 | } | |
| 397 | ||
| 398 | const ZigGLibCLib *glibc_lib_enum(size_t index) { | |
| 399 | assert(index < array_length(glibc_libs)); | |
| 400 | return &glibc_libs[index]; | |
| 401 | } | |
| 402 | ||
| 403 | const ZigGLibCLib *glibc_lib_find(const char *name) { | |
| 404 | for (size_t i = 0; i < array_length(glibc_libs); i += 1) { | |
| 405 | if (strcmp(glibc_libs[i].name, name) == 0) { | |
| 406 | return &glibc_libs[i]; | |
| 407 | } | |
| 408 | } | |
| 409 | return nullptr; | |
| 410 | } |
src/glibc.hpp created+53| ... | ... | @@ -0,0 +1,53 @@ |
| 1 | /* | |
| 2 | * Copyright (c) 2019 Andrew Kelley | |
| 3 | * | |
| 4 | * This file is part of zig, which is MIT licensed. | |
| 5 | * See http://opensource.org/licenses/MIT | |
| 6 | */ | |
| 7 | ||
| 8 | #ifndef ZIG_GLIBC_HPP | |
| 9 | #define ZIG_GLIBC_HPP | |
| 10 | ||
| 11 | #include "all_types.hpp" | |
| 12 | ||
| 13 | struct ZigGLibCLib { | |
| 14 | const char *name; | |
| 15 | uint8_t sover; | |
| 16 | }; | |
| 17 | ||
| 18 | struct ZigGLibCFn { | |
| 19 | Buf *name; | |
| 20 | const ZigGLibCLib *lib; | |
| 21 | }; | |
| 22 | ||
| 23 | struct ZigGLibCVerList { | |
| 24 | uint8_t versions[8]; // 8 is just the max number, we know statically it's big enough | |
| 25 | uint8_t len; | |
| 26 | }; | |
| 27 | ||
| 28 | uint32_t hash_glibc_target(const ZigTarget *x); | |
| 29 | bool eql_glibc_target(const ZigTarget *a, const ZigTarget *b); | |
| 30 | ||
| 31 | struct ZigGLibCAbi { | |
| 32 | Buf *abi_txt_path; | |
| 33 | Buf *vers_txt_path; | |
| 34 | Buf *fns_txt_path; | |
| 35 | ZigList<ZigGLibCVersion> all_versions; | |
| 36 | ZigList<ZigGLibCFn> all_functions; | |
| 37 | // The value is a pointer to all_functions.length items and each item is an index | |
| 38 | // into all_functions. | |
| 39 | HashMap<const ZigTarget *, ZigGLibCVerList *, hash_glibc_target, eql_glibc_target> version_table; | |
| 40 | }; | |
| 41 | ||
| 42 | Error glibc_load_metadata(ZigGLibCAbi **out_result, Buf *zig_lib_dir, bool verbose); | |
| 43 | Error glibc_build_dummies_and_maps(CodeGen *codegen, const ZigGLibCAbi *glibc_abi, const ZigTarget *target, | |
| 44 | Buf **out_dir, bool verbose); | |
| 45 | ||
| 46 | // returns ErrorUnknownABI when glibc is not the native libc | |
| 47 | Error glibc_detect_native_version(ZigGLibCVersion *glibc_ver); | |
| 48 | ||
| 49 | size_t glibc_lib_count(void); | |
| 50 | const ZigGLibCLib *glibc_lib_enum(size_t index); | |
| 51 | const ZigGLibCLib *glibc_lib_find(const char *name); | |
| 52 | ||
| 53 | #endif |
src/link.cpp+31-48| ... | ... | @@ -11,6 +11,7 @@ |
| 11 | 11 | #include "analyze.hpp" |
| 12 | 12 | #include "compiler.hpp" |
| 13 | 13 | #include "install_files.h" |
| 14 | #include "glibc.hpp" | |
| 14 | 15 | |
| 15 | 16 | struct LinkJob { |
| 16 | 17 | CodeGen *codegen; |
| ... | ... | @@ -19,37 +20,6 @@ struct LinkJob { |
| 19 | 20 | HashMap<Buf *, bool, buf_hash, buf_eql_buf> rpath_table; |
| 20 | 21 | }; |
| 21 | 22 | |
| 22 | static CodeGen *create_child_codegen(CodeGen *parent_gen, Buf *root_src_path, OutType out_type, | |
| 23 | ZigLibCInstallation *libc) | |
| 24 | { | |
| 25 | CodeGen *child_gen = codegen_create(nullptr, root_src_path, parent_gen->zig_target, out_type, | |
| 26 | parent_gen->build_mode, parent_gen->zig_lib_dir, parent_gen->zig_std_dir, libc, get_stage1_cache_path()); | |
| 27 | child_gen->disable_gen_h = true; | |
| 28 | child_gen->want_stack_check = WantStackCheckDisabled; | |
| 29 | child_gen->verbose_tokenize = parent_gen->verbose_tokenize; | |
| 30 | child_gen->verbose_ast = parent_gen->verbose_ast; | |
| 31 | child_gen->verbose_link = parent_gen->verbose_link; | |
| 32 | child_gen->verbose_ir = parent_gen->verbose_ir; | |
| 33 | child_gen->verbose_llvm_ir = parent_gen->verbose_llvm_ir; | |
| 34 | child_gen->verbose_cimport = parent_gen->verbose_cimport; | |
| 35 | child_gen->verbose_cc = parent_gen->verbose_cc; | |
| 36 | child_gen->llvm_argv = parent_gen->llvm_argv; | |
| 37 | child_gen->dynamic_linker_path = parent_gen->dynamic_linker_path; | |
| 38 | ||
| 39 | codegen_set_strip(child_gen, parent_gen->strip_debug_symbols); | |
| 40 | child_gen->want_pic = parent_gen->have_pic ? WantPICEnabled : WantPICDisabled; | |
| 41 | child_gen->valgrind_support = ValgrindSupportDisabled; | |
| 42 | ||
| 43 | codegen_set_errmsg_color(child_gen, parent_gen->err_color); | |
| 44 | ||
| 45 | codegen_set_mmacosx_version_min(child_gen, parent_gen->mmacosx_version_min); | |
| 46 | codegen_set_mios_version_min(child_gen, parent_gen->mios_version_min); | |
| 47 | ||
| 48 | child_gen->enable_cache = true; | |
| 49 | ||
| 50 | return child_gen; | |
| 51 | } | |
| 52 | ||
| 53 | 23 | static const char *build_libc_object(CodeGen *parent_gen, const char *name, CFile *c_file) { |
| 54 | 24 | CodeGen *child_gen = create_child_codegen(parent_gen, nullptr, OutTypeObj, nullptr); |
| 55 | 25 | codegen_set_out_name(child_gen, buf_create_from_str(name)); |
| ... | ... | @@ -76,18 +46,6 @@ static const char *path_from_libunwind(CodeGen *g, const char *subpath) { |
| 76 | 46 | return path_from_zig_lib(g, "libunwind", subpath); |
| 77 | 47 | } |
| 78 | 48 | |
| 79 | static const char *build_dummy_so(CodeGen *parent, const char *name, size_t major_version) { | |
| 80 | Buf *glibc_dummy_root_src = buf_sprintf("%s" OS_SEP "libc" OS_SEP "dummy" OS_SEP "%s.zig", | |
| 81 | buf_ptr(parent->zig_lib_dir), name); | |
| 82 | CodeGen *child_gen = create_child_codegen(parent, glibc_dummy_root_src, OutTypeLib, nullptr); | |
| 83 | codegen_set_out_name(child_gen, buf_create_from_str(name)); | |
| 84 | codegen_set_lib_version(child_gen, major_version, 0, 0); | |
| 85 | child_gen->is_dynamic = true; | |
| 86 | child_gen->is_dummy_so = true; | |
| 87 | codegen_build_and_link(child_gen); | |
| 88 | return buf_ptr(&child_gen->output_file_path); | |
| 89 | } | |
| 90 | ||
| 91 | 49 | static const char *build_libunwind(CodeGen *parent) { |
| 92 | 50 | CodeGen *child_gen = create_child_codegen(parent, nullptr, OutTypeLib, nullptr); |
| 93 | 51 | codegen_set_out_name(child_gen, buf_create_from_str("unwind")); |
| ... | ... | @@ -892,6 +850,30 @@ static void add_rpath(LinkJob *lj, Buf *rpath) { |
| 892 | 850 | lj->rpath_table.put(rpath, true); |
| 893 | 851 | } |
| 894 | 852 | |
| 853 | static void add_glibc_libs(LinkJob *lj) { | |
| 854 | Error err; | |
| 855 | ZigGLibCAbi *glibc_abi; | |
| 856 | if ((err = glibc_load_metadata(&glibc_abi, lj->codegen->zig_lib_dir, true))) { | |
| 857 | fprintf(stderr, "%s\n", err_str(err)); | |
| 858 | exit(1); | |
| 859 | } | |
| 860 | ||
| 861 | Buf *artifact_dir; | |
| 862 | if ((err = glibc_build_dummies_and_maps(lj->codegen, glibc_abi, lj->codegen->zig_target, | |
| 863 | &artifact_dir, true))) | |
| 864 | { | |
| 865 | fprintf(stderr, "%s\n", err_str(err)); | |
| 866 | exit(1); | |
| 867 | } | |
| 868 | ||
| 869 | size_t lib_count = glibc_lib_count(); | |
| 870 | for (size_t i = 0; i < lib_count; i += 1) { | |
| 871 | const ZigGLibCLib *lib = glibc_lib_enum(i); | |
| 872 | Buf *so_path = buf_sprintf("%s" OS_SEP "lib%s.so.%d.0.0", buf_ptr(artifact_dir), lib->name, lib->sover); | |
| 873 | lj->args.append(buf_ptr(so_path)); | |
| 874 | } | |
| 875 | } | |
| 876 | ||
| 895 | 877 | static void construct_linker_job_elf(LinkJob *lj) { |
| 896 | 878 | CodeGen *g = lj->codegen; |
| 897 | 879 | |
| ... | ... | @@ -990,6 +972,11 @@ static void construct_linker_job_elf(LinkJob *lj) { |
| 990 | 972 | if (is_dyn_lib) { |
| 991 | 973 | lj->args.append("-soname"); |
| 992 | 974 | lj->args.append(buf_ptr(soname)); |
| 975 | ||
| 976 | if (g->version_script_path != nullptr) { | |
| 977 | lj->args.append("-version-script"); | |
| 978 | lj->args.append(buf_ptr(g->version_script_path)); | |
| 979 | } | |
| 993 | 980 | } |
| 994 | 981 | |
| 995 | 982 | // .o files |
| ... | ... | @@ -1053,11 +1040,7 @@ static void construct_linker_job_elf(LinkJob *lj) { |
| 1053 | 1040 | } |
| 1054 | 1041 | } else if (target_is_glibc(g->zig_target)) { |
| 1055 | 1042 | lj->args.append(build_libunwind(g)); |
| 1056 | lj->args.append(build_dummy_so(g, "c", 6)); | |
| 1057 | lj->args.append(build_dummy_so(g, "m", 6)); | |
| 1058 | lj->args.append(build_dummy_so(g, "pthread", 0)); | |
| 1059 | lj->args.append(build_dummy_so(g, "dl", 2)); | |
| 1060 | lj->args.append(build_dummy_so(g, "rt", 1)); | |
| 1043 | add_glibc_libs(lj); | |
| 1061 | 1044 | lj->args.append(get_libc_crt_file(g, "libc_nonshared.a")); |
| 1062 | 1045 | } else if (target_is_musl(g->zig_target)) { |
| 1063 | 1046 | lj->args.append(build_libunwind(g)); |
src/main.cpp+54-2| ... | ... | @@ -15,6 +15,7 @@ |
| 15 | 15 | #include "target.hpp" |
| 16 | 16 | #include "libc_installation.hpp" |
| 17 | 17 | #include "userland.h" |
| 18 | #include "glibc.hpp" | |
| 18 | 19 | |
| 19 | 20 | #include <stdio.h> |
| 20 | 21 | |
| ... | ... | @@ -96,6 +97,7 @@ static int print_full_usage(const char *arg0, FILE *file, int return_code) { |
| 96 | 97 | " --forbid-library [lib] make it an error to link against lib\n" |
| 97 | 98 | " --library-path [dir] add a directory to the library search path\n" |
| 98 | 99 | " --linker-script [path] use a custom linker script\n" |
| 100 | " --version-script [path] provide a version .map file\n" | |
| 99 | 101 | " --object [obj] add object file to build\n" |
| 100 | 102 | " -L[dir] alias for --library-path\n" |
| 101 | 103 | " -rdynamic add all symbols to the dynamic symbol table\n" |
| ... | ... | @@ -189,10 +191,33 @@ static int print_target_list(FILE *f) { |
| 189 | 191 | for (size_t i = 0; i < libc_count; i += 1) { |
| 190 | 192 | ZigTarget libc_target; |
| 191 | 193 | target_libc_enum(i, &libc_target); |
| 192 | fprintf(f, " %s-%s-%s\n", target_arch_name(libc_target.arch), | |
| 193 | target_os_name(libc_target.os), target_abi_name(libc_target.abi)); | |
| 194 | bool is_native = native.arch == libc_target.arch && | |
| 195 | native.os == libc_target.os && | |
| 196 | native.abi == libc_target.abi; | |
| 197 | const char *native_str = is_native ? " (native)" : ""; | |
| 198 | fprintf(f, " %s-%s-%s%s\n", target_arch_name(libc_target.arch), | |
| 199 | target_os_name(libc_target.os), target_abi_name(libc_target.abi), native_str); | |
| 194 | 200 | } |
| 195 | 201 | |
| 202 | fprintf(f, "\nAvailable glibc versions:\n"); | |
| 203 | ZigGLibCAbi *glibc_abi; | |
| 204 | Error err; | |
| 205 | if ((err = glibc_load_metadata(&glibc_abi, get_zig_lib_dir(), true))) { | |
| 206 | return EXIT_FAILURE; | |
| 207 | } | |
| 208 | for (size_t i = 0; i < glibc_abi->all_versions.length; i += 1) { | |
| 209 | ZigGLibCVersion *this_ver = &glibc_abi->all_versions.at(i); | |
| 210 | bool is_native = native.glibc_version != nullptr && | |
| 211 | native.glibc_version->major == this_ver->major && | |
| 212 | native.glibc_version->minor == this_ver->minor && | |
| 213 | native.glibc_version->patch == this_ver->patch; | |
| 214 | const char *native_str = is_native ? " (native)" : ""; | |
| 215 | if (this_ver->patch == 0) { | |
| 216 | fprintf(f, " %d.%d%s\n", this_ver->major, this_ver->minor, native_str); | |
| 217 | } else { | |
| 218 | fprintf(f, " %d.%d.%d%s\n", this_ver->major, this_ver->minor, this_ver->patch, native_str); | |
| 219 | } | |
| 220 | } | |
| 196 | 221 | return EXIT_SUCCESS; |
| 197 | 222 | } |
| 198 | 223 | |
| ... | ... | @@ -437,6 +462,8 @@ int main(int argc, char **argv) { |
| 437 | 462 | const char *mmacosx_version_min = nullptr; |
| 438 | 463 | const char *mios_version_min = nullptr; |
| 439 | 464 | const char *linker_script = nullptr; |
| 465 | Buf *version_script = nullptr; | |
| 466 | const char *target_glibc = nullptr; | |
| 440 | 467 | ZigList<const char *> rpath_list = {0}; |
| 441 | 468 | bool each_lib_rpath = false; |
| 442 | 469 | ZigList<const char *> objects = {0}; |
| ... | ... | @@ -783,6 +810,10 @@ int main(int argc, char **argv) { |
| 783 | 810 | frameworks.append(argv[i]); |
| 784 | 811 | } else if (strcmp(arg, "--linker-script") == 0) { |
| 785 | 812 | linker_script = argv[i]; |
| 813 | } else if (strcmp(arg, "--version-script") == 0) { | |
| 814 | version_script = buf_create_from_str(argv[i]); | |
| 815 | } else if (strcmp(arg, "-target-glibc") == 0) { | |
| 816 | target_glibc = argv[i]; | |
| 786 | 817 | } else if (strcmp(arg, "-rpath") == 0) { |
| 787 | 818 | rpath_list.append(argv[i]); |
| 788 | 819 | } else if (strcmp(arg, "--test-filter") == 0) { |
| ... | ... | @@ -904,6 +935,10 @@ int main(int argc, char **argv) { |
| 904 | 935 | ZigTarget target; |
| 905 | 936 | if (target_string == nullptr) { |
| 906 | 937 | get_native_target(&target); |
| 938 | if (target_glibc != nullptr) { | |
| 939 | fprintf(stderr, "-target-glibc provided but no -target parameter\n"); | |
| 940 | return print_error_usage(arg0); | |
| 941 | } | |
| 907 | 942 | } else { |
| 908 | 943 | if ((err = target_parse_triple(&target, target_string))) { |
| 909 | 944 | if (err == ErrorUnknownArchitecture && target.arch != ZigLLVM_UnknownArch) { |
| ... | ... | @@ -921,6 +956,22 @@ int main(int argc, char **argv) { |
| 921 | 956 | return print_error_usage(arg0); |
| 922 | 957 | } |
| 923 | 958 | } |
| 959 | if (target_is_glibc(&target)) { | |
| 960 | target.glibc_version = allocate<ZigGLibCVersion>(1); | |
| 961 | ||
| 962 | if (target_glibc != nullptr) { | |
| 963 | if ((err = target_parse_glibc_version(target.glibc_version, target_glibc))) { | |
| 964 | fprintf(stderr, "invalid glibc version '%s': %s\n", target_glibc, err_str(err)); | |
| 965 | return print_error_usage(arg0); | |
| 966 | } | |
| 967 | } else { | |
| 968 | // Default cross-compiling glibc version | |
| 969 | *target.glibc_version = {2, 17, 0}; | |
| 970 | } | |
| 971 | } else if (target_glibc != nullptr) { | |
| 972 | fprintf(stderr, "'%s' is not a glibc-compatible target", target_string); | |
| 973 | return print_error_usage(arg0); | |
| 974 | } | |
| 924 | 975 | } |
| 925 | 976 | |
| 926 | 977 | if (output_dir != nullptr && enable_cache == CacheOptOn) { |
| ... | ... | @@ -1074,6 +1125,7 @@ int main(int argc, char **argv) { |
| 1074 | 1125 | codegen_set_is_test(g, cmd == CmdTest); |
| 1075 | 1126 | g->want_single_threaded = want_single_threaded; |
| 1076 | 1127 | codegen_set_linker_script(g, linker_script); |
| 1128 | g->version_script_path = version_script; | |
| 1077 | 1129 | if (each_lib_rpath) |
| 1078 | 1130 | codegen_set_each_lib_rpath(g, each_lib_rpath); |
| 1079 | 1131 |
src/target.cpp+36| ... | ... | @@ -10,6 +10,8 @@ |
| 10 | 10 | #include "target.hpp" |
| 11 | 11 | #include "util.hpp" |
| 12 | 12 | #include "os.hpp" |
| 13 | #include "compiler.hpp" | |
| 14 | #include "glibc.hpp" | |
| 13 | 15 | |
| 14 | 16 | #include <stdio.h> |
| 15 | 17 | |
| ... | ... | @@ -466,6 +468,29 @@ const char *target_abi_name(ZigLLVM_EnvironmentType abi) { |
| 466 | 468 | return ZigLLVMGetEnvironmentTypeName(abi); |
| 467 | 469 | } |
| 468 | 470 | |
| 471 | Error target_parse_glibc_version(ZigGLibCVersion *glibc_ver, const char *text) { | |
| 472 | glibc_ver->major = 2; | |
| 473 | glibc_ver->minor = 0; | |
| 474 | glibc_ver->patch = 0; | |
| 475 | SplitIterator it = memSplit(str(text), str("GLIBC_.")); | |
| 476 | { | |
| 477 | Optional<Slice<uint8_t>> opt_component = SplitIterator_next(&it); | |
| 478 | if (!opt_component.is_some) return ErrorUnknownABI; | |
| 479 | glibc_ver->major = strtoul(buf_ptr(buf_create_from_slice(opt_component.value)), nullptr, 10); | |
| 480 | } | |
| 481 | { | |
| 482 | Optional<Slice<uint8_t>> opt_component = SplitIterator_next(&it); | |
| 483 | if (!opt_component.is_some) return ErrorNone; | |
| 484 | glibc_ver->minor = strtoul(buf_ptr(buf_create_from_slice(opt_component.value)), nullptr, 10); | |
| 485 | } | |
| 486 | { | |
| 487 | Optional<Slice<uint8_t>> opt_component = SplitIterator_next(&it); | |
| 488 | if (!opt_component.is_some) return ErrorNone; | |
| 489 | glibc_ver->patch = strtoul(buf_ptr(buf_create_from_slice(opt_component.value)), nullptr, 10); | |
| 490 | } | |
| 491 | return ErrorNone; | |
| 492 | } | |
| 493 | ||
| 469 | 494 | void get_native_target(ZigTarget *target) { |
| 470 | 495 | ZigLLVM_OSType os_type; |
| 471 | 496 | ZigLLVM_ObjectFormatType oformat; // ignored; based on arch/os |
| ... | ... | @@ -481,6 +506,17 @@ void get_native_target(ZigTarget *target) { |
| 481 | 506 | if (target->abi == ZigLLVM_UnknownEnvironment) { |
| 482 | 507 | target->abi = target_default_abi(target->arch, target->os); |
| 483 | 508 | } |
| 509 | target->glibc_version = nullptr; | |
| 510 | #ifdef ZIG_OS_LINUX | |
| 511 | if (target_is_glibc(target)) { | |
| 512 | target->glibc_version = allocate<ZigGLibCVersion>(1); | |
| 513 | Error err; | |
| 514 | if ((err = glibc_detect_native_version(target->glibc_version))) { | |
| 515 | // Use a default version. | |
| 516 | *target->glibc_version = {2, 17, 0}; | |
| 517 | } | |
| 518 | } | |
| 519 | #endif | |
| 484 | 520 | } |
| 485 | 521 | |
| 486 | 522 | Error target_parse_archsub(ZigLLVM_ArchType *out_arch, ZigLLVM_SubArchType *out_sub, |
src/target.hpp+9| ... | ... | @@ -77,12 +77,19 @@ enum TargetSubsystem { |
| 77 | 77 | TargetSubsystemAuto |
| 78 | 78 | }; |
| 79 | 79 | |
| 80 | struct ZigGLibCVersion { | |
| 81 | uint32_t major; // always 2 | |
| 82 | uint32_t minor; | |
| 83 | uint32_t patch; | |
| 84 | }; | |
| 85 | ||
| 80 | 86 | struct ZigTarget { |
| 81 | 87 | ZigLLVM_ArchType arch; |
| 82 | 88 | ZigLLVM_SubArchType sub_arch; |
| 83 | 89 | ZigLLVM_VendorType vendor; |
| 84 | 90 | Os os; |
| 85 | 91 | ZigLLVM_EnvironmentType abi; |
| 92 | ZigGLibCVersion *glibc_version; // null means default | |
| 86 | 93 | bool is_native; |
| 87 | 94 | }; |
| 88 | 95 | |
| ... | ... | @@ -105,6 +112,8 @@ Error target_parse_archsub(ZigLLVM_ArchType *arch, ZigLLVM_SubArchType *sub, |
| 105 | 112 | Error target_parse_os(Os *os, const char *os_ptr, size_t os_len); |
| 106 | 113 | Error target_parse_abi(ZigLLVM_EnvironmentType *abi, const char *abi_ptr, size_t abi_len); |
| 107 | 114 | |
| 115 | Error target_parse_glibc_version(ZigGLibCVersion *out, const char *text); | |
| 116 | ||
| 108 | 117 | size_t target_arch_count(void); |
| 109 | 118 | ZigLLVM_ArchType target_arch_enum(size_t index); |
| 110 | 119 | const char *target_arch_name(ZigLLVM_ArchType arch); |
src/util.cpp+26| ... | ... | @@ -86,6 +86,32 @@ Optional<Slice<uint8_t>> SplitIterator_next(SplitIterator *self) { |
| 86 | 86 | return Optional<Slice<uint8_t>>::some(self->buffer.slice(start, end)); |
| 87 | 87 | } |
| 88 | 88 | |
| 89 | // Ported from std/mem.zig. | |
| 90 | // This one won't collapse multiple separators into one, so you could use it, for example, | |
| 91 | // to parse Comma Separated Value format. | |
| 92 | Optional<Slice<uint8_t>> SplitIterator_next_separate(SplitIterator *self) { | |
| 93 | // move to beginning of token | |
| 94 | if (self->index < self->buffer.len && | |
| 95 | SplitIterator_isSplitByte(self, self->buffer.ptr[self->index])) | |
| 96 | { | |
| 97 | self->index += 1; | |
| 98 | } | |
| 99 | size_t start = self->index; | |
| 100 | if (start == self->buffer.len) { | |
| 101 | return {}; | |
| 102 | } | |
| 103 | ||
| 104 | // move to end of token | |
| 105 | while (self->index < self->buffer.len && | |
| 106 | !SplitIterator_isSplitByte(self, self->buffer.ptr[self->index])) | |
| 107 | { | |
| 108 | self->index += 1; | |
| 109 | } | |
| 110 | size_t end = self->index; | |
| 111 | ||
| 112 | return Optional<Slice<uint8_t>>::some(self->buffer.slice(start, end)); | |
| 113 | } | |
| 114 | ||
| 89 | 115 | // Ported from std/mem.zig |
| 90 | 116 | Slice<uint8_t> SplitIterator_rest(SplitIterator *self) { |
| 91 | 117 | // move to beginning of token |
src/util.hpp+1| ... | ... | @@ -314,6 +314,7 @@ struct SplitIterator { |
| 314 | 314 | |
| 315 | 315 | bool SplitIterator_isSplitByte(SplitIterator *self, uint8_t byte); |
| 316 | 316 | Optional< Slice<uint8_t> > SplitIterator_next(SplitIterator *self); |
| 317 | Optional< Slice<uint8_t> > SplitIterator_next_separate(SplitIterator *self); | |
| 317 | 318 | Slice<uint8_t> SplitIterator_rest(SplitIterator *self); |
| 318 | 319 | SplitIterator memSplit(Slice<uint8_t> buffer, Slice<uint8_t> split_bytes); |
| 319 | 320 |