authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2019-07-07 17:06:09-04:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2019-07-07 17:56:08-04:00
log7ccf7807b3f12428adc5d9ca0ede4e3f4ec6dbbc
treeaf8db742f220123b24c3959b6f33173d8b1e619e
parent3b97940fb384285a8f88088c9e73e582f8bdbf96
signaturelock-open Commit is signed but in an unrecognized format.

ability to target any glibc version


14 files changed, 691 insertions(+), 53 deletions(-)

CMakeLists.txt+4
......@@ -421,6 +421,7 @@ set(ZIG_MAIN_SRC "${CMAKE_SOURCE_DIR}/src/main.cpp")
421421set(ZIG0_SHIM_SRC "${CMAKE_SOURCE_DIR}/src/userland.cpp")
422422
423423set(ZIG_SOURCES
424 "${CMAKE_SOURCE_DIR}/src/glibc.cpp"
424425 "${CMAKE_SOURCE_DIR}/src/analyze.cpp"
425426 "${CMAKE_SOURCE_DIR}/src/ast_render.cpp"
426427 "${CMAKE_SOURCE_DIR}/src/bigfloat.cpp"
......@@ -2667,6 +2668,9 @@ set(ZIG_MUSL_SRC_FILES
26672668)
26682669
26692670set(ZIG_LIBC_FILES
2671 "glibc/abi.txt"
2672 "glibc/fns.txt"
2673 "glibc/vers.txt"
26702674 "glibc/bits/byteswap.h"
26712675 "glibc/bits/endian.h"
26722676 "glibc/bits/floatn-common.h"
src/all_types.hpp+4
......@@ -1921,6 +1921,7 @@ struct CodeGen {
19211921 Buf *zig_lib_dir;
19221922 Buf *zig_std_dir;
19231923 Buf *dynamic_linker_path;
1924 Buf *version_script_path;
19241925
19251926 const char **llvm_argv;
19261927 size_t llvm_argv_len;
......@@ -3788,6 +3789,9 @@ static const size_t stack_trace_ptr_count = 32;
37883789#define NAMESPACE_SEP_CHAR '.'
37893790#define NAMESPACE_SEP_STR "."
37903791
3792#define CACHE_OUT_SUBDIR "o"
3793#define CACHE_HASH_SUBDIR "h"
3794
37913795enum FloatMode {
37923796 FloatModeStrict,
37933797 FloatModeOptimized,
src/codegen.cpp+33-3
......@@ -24,9 +24,6 @@
2424#include <stdio.h>
2525#include <errno.h>
2626
27#define CACHE_OUT_SUBDIR "o"
28#define CACHE_HASH_SUBDIR "h"
29
3027static void init_darwin_native(CodeGen *g) {
3128 char *osx_target = getenv("MACOSX_DEPLOYMENT_TARGET");
3229 char *ios_target = getenv("IPHONEOS_DEPLOYMENT_TARGET");
......@@ -9502,6 +9499,7 @@ static Error check_cache(CodeGen *g, Buf *manifest_dir, Buf *digest) {
95029499 cache_buf(ch, &g->libc->kernel32_lib_dir);
95039500 }
95049501 cache_buf_opt(ch, g->dynamic_linker_path);
9502 cache_buf_opt(ch, g->version_script_path);
95059503
95069504 // gen_c_objects appends objects to g->link_objects which we want to include in the hash
95079505 gen_c_objects(g);
......@@ -9695,3 +9693,35 @@ ZigPackage *codegen_create_package(CodeGen *g, const char *root_src_dir, const c
96959693 }
96969694 return pkg;
96979695}
9696
9697CodeGen *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
2020 OutType out_type, BuildMode build_mode, Buf *zig_lib_dir, Buf *override_std_dir,
2121 ZigLibCInstallation *libc, Buf *cache_dir);
2222
23CodeGen *create_child_codegen(CodeGen *parent_gen, Buf *root_src_path, OutType out_type,
24 ZigLibCInstallation *libc);
25
2326void codegen_set_clang_argv(CodeGen *codegen, const char **args, size_t len);
2427void codegen_set_llvm_argv(CodeGen *codegen, const char **args, size_t len);
2528void codegen_set_is_test(CodeGen *codegen, bool is_test);
src/compiler.cpp+26
......@@ -9,9 +9,13 @@ static Buf saved_stage1_path = BUF_INIT;
99static Buf saved_lib_dir = BUF_INIT;
1010static Buf saved_special_dir = BUF_INIT;
1111static Buf saved_std_dir = BUF_INIT;
12
1213static Buf saved_dynamic_linker_path = BUF_INIT;
1314static bool searched_for_dyn_linker = false;
1415
16static Buf saved_libc_path = BUF_INIT;
17static bool searched_for_libc = false;
18
1519Buf *get_stage1_cache_path(void) {
1620 if (saved_stage1_path.list.length != 0) {
1721 return &saved_stage1_path;
......@@ -36,6 +40,28 @@ static void detect_dynamic_linker(Buf *lib_path) {
3640#endif
3741}
3842
43const 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
3965Buf *get_self_dynamic_linker_path(void) {
4066 for (;;) {
4167 if (saved_dynamic_linker_path.list.length != 0) {
src/compiler.hpp+1
......@@ -14,6 +14,7 @@
1414Buf *get_stage1_cache_path(void);
1515Error get_compiler_id(Buf **result);
1616Buf *get_self_dynamic_linker_path(void);
17Buf *get_self_libc_path(void);
1718
1819Buf *get_zig_lib_dir(void);
1920Buf *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
13static const ZigGLibCLib glibc_libs[] = {
14 {"c", 6},
15 {"m", 6},
16 {"pthread", 0},
17 {"dl", 2},
18 {"rt", 1},
19};
20
21Error 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
156Error 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
344uint32_t hash_glibc_target(const ZigTarget *x) {
345 return x->arch * 3250106448 +
346 x->os * 542534372 +
347 x->abi * 59162639;
348}
349
350bool 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>
359Error 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
389Error glibc_detect_native_version(ZigGLibCVersion *glibc_ver) {
390 return ErrorUnknownABI;
391}
392#endif
393
394size_t glibc_lib_count(void) {
395 return array_length(glibc_libs);
396}
397
398const ZigGLibCLib *glibc_lib_enum(size_t index) {
399 assert(index < array_length(glibc_libs));
400 return &glibc_libs[index];
401}
402
403const 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
13struct ZigGLibCLib {
14 const char *name;
15 uint8_t sover;
16};
17
18struct ZigGLibCFn {
19 Buf *name;
20 const ZigGLibCLib *lib;
21};
22
23struct 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
28uint32_t hash_glibc_target(const ZigTarget *x);
29bool eql_glibc_target(const ZigTarget *a, const ZigTarget *b);
30
31struct 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
42Error glibc_load_metadata(ZigGLibCAbi **out_result, Buf *zig_lib_dir, bool verbose);
43Error 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
47Error glibc_detect_native_version(ZigGLibCVersion *glibc_ver);
48
49size_t glibc_lib_count(void);
50const ZigGLibCLib *glibc_lib_enum(size_t index);
51const ZigGLibCLib *glibc_lib_find(const char *name);
52
53#endif
src/link.cpp+31-48
......@@ -11,6 +11,7 @@
1111#include "analyze.hpp"
1212#include "compiler.hpp"
1313#include "install_files.h"
14#include "glibc.hpp"
1415
1516struct LinkJob {
1617 CodeGen *codegen;
......@@ -19,37 +20,6 @@ struct LinkJob {
1920 HashMap<Buf *, bool, buf_hash, buf_eql_buf> rpath_table;
2021};
2122
22static 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
5323static const char *build_libc_object(CodeGen *parent_gen, const char *name, CFile *c_file) {
5424 CodeGen *child_gen = create_child_codegen(parent_gen, nullptr, OutTypeObj, nullptr);
5525 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) {
7646 return path_from_zig_lib(g, "libunwind", subpath);
7747}
7848
79static 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
9149static const char *build_libunwind(CodeGen *parent) {
9250 CodeGen *child_gen = create_child_codegen(parent, nullptr, OutTypeLib, nullptr);
9351 codegen_set_out_name(child_gen, buf_create_from_str("unwind"));
......@@ -892,6 +850,30 @@ static void add_rpath(LinkJob *lj, Buf *rpath) {
892850 lj->rpath_table.put(rpath, true);
893851}
894852
853static 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
895877static void construct_linker_job_elf(LinkJob *lj) {
896878 CodeGen *g = lj->codegen;
897879
......@@ -990,6 +972,11 @@ static void construct_linker_job_elf(LinkJob *lj) {
990972 if (is_dyn_lib) {
991973 lj->args.append("-soname");
992974 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 }
993980 }
994981
995982 // .o files
......@@ -1053,11 +1040,7 @@ static void construct_linker_job_elf(LinkJob *lj) {
10531040 }
10541041 } else if (target_is_glibc(g->zig_target)) {
10551042 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);
10611044 lj->args.append(get_libc_crt_file(g, "libc_nonshared.a"));
10621045 } else if (target_is_musl(g->zig_target)) {
10631046 lj->args.append(build_libunwind(g));
src/main.cpp+54-2
......@@ -15,6 +15,7 @@
1515#include "target.hpp"
1616#include "libc_installation.hpp"
1717#include "userland.h"
18#include "glibc.hpp"
1819
1920#include <stdio.h>
2021
......@@ -96,6 +97,7 @@ static int print_full_usage(const char *arg0, FILE *file, int return_code) {
9697 " --forbid-library [lib] make it an error to link against lib\n"
9798 " --library-path [dir] add a directory to the library search path\n"
9899 " --linker-script [path] use a custom linker script\n"
100 " --version-script [path] provide a version .map file\n"
99101 " --object [obj] add object file to build\n"
100102 " -L[dir] alias for --library-path\n"
101103 " -rdynamic add all symbols to the dynamic symbol table\n"
......@@ -189,10 +191,33 @@ static int print_target_list(FILE *f) {
189191 for (size_t i = 0; i < libc_count; i += 1) {
190192 ZigTarget libc_target;
191193 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);
194200 }
195201
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 }
196221 return EXIT_SUCCESS;
197222}
198223
......@@ -437,6 +462,8 @@ int main(int argc, char **argv) {
437462 const char *mmacosx_version_min = nullptr;
438463 const char *mios_version_min = nullptr;
439464 const char *linker_script = nullptr;
465 Buf *version_script = nullptr;
466 const char *target_glibc = nullptr;
440467 ZigList<const char *> rpath_list = {0};
441468 bool each_lib_rpath = false;
442469 ZigList<const char *> objects = {0};
......@@ -783,6 +810,10 @@ int main(int argc, char **argv) {
783810 frameworks.append(argv[i]);
784811 } else if (strcmp(arg, "--linker-script") == 0) {
785812 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];
786817 } else if (strcmp(arg, "-rpath") == 0) {
787818 rpath_list.append(argv[i]);
788819 } else if (strcmp(arg, "--test-filter") == 0) {
......@@ -904,6 +935,10 @@ int main(int argc, char **argv) {
904935 ZigTarget target;
905936 if (target_string == nullptr) {
906937 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 }
907942 } else {
908943 if ((err = target_parse_triple(&target, target_string))) {
909944 if (err == ErrorUnknownArchitecture && target.arch != ZigLLVM_UnknownArch) {
......@@ -921,6 +956,22 @@ int main(int argc, char **argv) {
921956 return print_error_usage(arg0);
922957 }
923958 }
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 }
924975 }
925976
926977 if (output_dir != nullptr && enable_cache == CacheOptOn) {
......@@ -1074,6 +1125,7 @@ int main(int argc, char **argv) {
10741125 codegen_set_is_test(g, cmd == CmdTest);
10751126 g->want_single_threaded = want_single_threaded;
10761127 codegen_set_linker_script(g, linker_script);
1128 g->version_script_path = version_script;
10771129 if (each_lib_rpath)
10781130 codegen_set_each_lib_rpath(g, each_lib_rpath);
10791131
src/target.cpp+36
......@@ -10,6 +10,8 @@
1010#include "target.hpp"
1111#include "util.hpp"
1212#include "os.hpp"
13#include "compiler.hpp"
14#include "glibc.hpp"
1315
1416#include <stdio.h>
1517
......@@ -466,6 +468,29 @@ const char *target_abi_name(ZigLLVM_EnvironmentType abi) {
466468 return ZigLLVMGetEnvironmentTypeName(abi);
467469}
468470
471Error 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
469494void get_native_target(ZigTarget *target) {
470495 ZigLLVM_OSType os_type;
471496 ZigLLVM_ObjectFormatType oformat; // ignored; based on arch/os
......@@ -481,6 +506,17 @@ void get_native_target(ZigTarget *target) {
481506 if (target->abi == ZigLLVM_UnknownEnvironment) {
482507 target->abi = target_default_abi(target->arch, target->os);
483508 }
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
484520}
485521
486522Error target_parse_archsub(ZigLLVM_ArchType *out_arch, ZigLLVM_SubArchType *out_sub,
src/target.hpp+9
......@@ -77,12 +77,19 @@ enum TargetSubsystem {
7777 TargetSubsystemAuto
7878};
7979
80struct ZigGLibCVersion {
81 uint32_t major; // always 2
82 uint32_t minor;
83 uint32_t patch;
84};
85
8086struct ZigTarget {
8187 ZigLLVM_ArchType arch;
8288 ZigLLVM_SubArchType sub_arch;
8389 ZigLLVM_VendorType vendor;
8490 Os os;
8591 ZigLLVM_EnvironmentType abi;
92 ZigGLibCVersion *glibc_version; // null means default
8693 bool is_native;
8794};
8895
......@@ -105,6 +112,8 @@ Error target_parse_archsub(ZigLLVM_ArchType *arch, ZigLLVM_SubArchType *sub,
105112Error target_parse_os(Os *os, const char *os_ptr, size_t os_len);
106113Error target_parse_abi(ZigLLVM_EnvironmentType *abi, const char *abi_ptr, size_t abi_len);
107114
115Error target_parse_glibc_version(ZigGLibCVersion *out, const char *text);
116
108117size_t target_arch_count(void);
109118ZigLLVM_ArchType target_arch_enum(size_t index);
110119const char *target_arch_name(ZigLLVM_ArchType arch);
src/util.cpp+26
......@@ -86,6 +86,32 @@ Optional<Slice<uint8_t>> SplitIterator_next(SplitIterator *self) {
8686 return Optional<Slice<uint8_t>>::some(self->buffer.slice(start, end));
8787}
8888
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.
92Optional<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
89115// Ported from std/mem.zig
90116Slice<uint8_t> SplitIterator_rest(SplitIterator *self) {
91117 // move to beginning of token
src/util.hpp+1
......@@ -314,6 +314,7 @@ struct SplitIterator {
314314
315315bool SplitIterator_isSplitByte(SplitIterator *self, uint8_t byte);
316316Optional< Slice<uint8_t> > SplitIterator_next(SplitIterator *self);
317Optional< Slice<uint8_t> > SplitIterator_next_separate(SplitIterator *self);
317318Slice<uint8_t> SplitIterator_rest(SplitIterator *self);
318319SplitIterator memSplit(Slice<uint8_t> buffer, Slice<uint8_t> split_bytes);
319320