Branch data Line data Source code
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 : 2 : Error glibc_load_metadata(ZigGLibCAbi **out_result, Buf *zig_lib_dir, bool verbose) {
22 : : Error err;
23 : :
24 : 2 : ZigGLibCAbi *glibc_abi = allocate<ZigGLibCAbi>(1);
25 : 2 : glibc_abi->vers_txt_path = buf_sprintf("%s" OS_SEP "libc" OS_SEP "glibc" OS_SEP "vers.txt", buf_ptr(zig_lib_dir));
26 : 2 : glibc_abi->fns_txt_path = buf_sprintf("%s" OS_SEP "libc" OS_SEP "glibc" OS_SEP "fns.txt", buf_ptr(zig_lib_dir));
27 : 2 : glibc_abi->abi_txt_path = buf_sprintf("%s" OS_SEP "libc" OS_SEP "glibc" OS_SEP "abi.txt", buf_ptr(zig_lib_dir));
28 : 2 : glibc_abi->version_table.init(16);
29 : :
30 : 2 : Buf *vers_txt_contents = buf_alloc();
31 [ - + ]: 2 : if ((err = os_fetch_file_path(glibc_abi->vers_txt_path, vers_txt_contents))) {
32 [ # # ]: 0 : if (verbose) {
33 : 0 : fprintf(stderr, "Unable to read %s: %s\n", buf_ptr(glibc_abi->vers_txt_path), err_str(err));
34 : : }
35 : 0 : return err;
36 : : }
37 : 2 : Buf *fns_txt_contents = buf_alloc();
38 [ - + ]: 2 : if ((err = os_fetch_file_path(glibc_abi->fns_txt_path, fns_txt_contents))) {
39 [ # # ]: 0 : if (verbose) {
40 : 0 : fprintf(stderr, "Unable to read %s: %s\n", buf_ptr(glibc_abi->fns_txt_path), err_str(err));
41 : : }
42 : 0 : return err;
43 : : }
44 : 2 : Buf *abi_txt_contents = buf_alloc();
45 [ - + ]: 2 : if ((err = os_fetch_file_path(glibc_abi->abi_txt_path, abi_txt_contents))) {
46 [ # # ]: 0 : if (verbose) {
47 : 0 : fprintf(stderr, "Unable to read %s: %s\n", buf_ptr(glibc_abi->abi_txt_path), err_str(err));
48 : : }
49 : 0 : return err;
50 : : }
51 : :
52 : : {
53 : 2 : SplitIterator it = memSplit(buf_to_slice(vers_txt_contents), str("\n"));
54 : : for (;;) {
55 : 82 : Optional<Slice<uint8_t>> opt_component = SplitIterator_next(&it);
56 [ + + ]: 82 : if (!opt_component.is_some) break;
57 : 80 : Buf *ver_buf = buf_create_from_slice(opt_component.value);
58 : 80 : ZigGLibCVersion *this_ver = glibc_abi->all_versions.add_one();
59 [ - + ]: 80 : if ((err = target_parse_glibc_version(this_ver, buf_ptr(ver_buf)))) {
60 [ # # ]: 0 : if (verbose) {
61 : 0 : fprintf(stderr, "Unable to parse glibc version '%s': %s\n", buf_ptr(ver_buf), err_str(err));
62 : : }
63 : 0 : return err;
64 : : }
65 : 80 : }
66 : : }
67 : : {
68 : 2 : SplitIterator it = memSplit(buf_to_slice(fns_txt_contents), str("\n"));
69 : : for (;;) {
70 : 7408 : Optional<Slice<uint8_t>> opt_component = SplitIterator_next(&it);
71 [ + + ]: 7408 : if (!opt_component.is_some) break;
72 : 7406 : SplitIterator line_it = memSplit(opt_component.value, str(" "));
73 : 7406 : Optional<Slice<uint8_t>> opt_fn_name = SplitIterator_next(&line_it);
74 [ - + ]: 7406 : if (!opt_fn_name.is_some) {
75 [ # # ]: 0 : if (verbose) {
76 : 0 : fprintf(stderr, "%s: Expected function name\n", buf_ptr(glibc_abi->fns_txt_path));
77 : : }
78 : 0 : return ErrorInvalidFormat;
79 : : }
80 : 7406 : Optional<Slice<uint8_t>> opt_lib_name = SplitIterator_next(&line_it);
81 [ - + ]: 7406 : if (!opt_lib_name.is_some) {
82 [ # # ]: 0 : if (verbose) {
83 : 0 : fprintf(stderr, "%s: Expected lib name\n", buf_ptr(glibc_abi->fns_txt_path));
84 : : }
85 : 0 : return ErrorInvalidFormat;
86 : : }
87 : :
88 : 7406 : Buf *this_fn_name = buf_create_from_slice(opt_fn_name.value);
89 : 7406 : Buf *this_lib_name = buf_create_from_slice(opt_lib_name.value);
90 : 7406 : glibc_abi->all_functions.append({ this_fn_name, glibc_lib_find(buf_ptr(this_lib_name)) });
91 : 7406 : }
92 : : }
93 : : {
94 : 2 : SplitIterator it = memSplit(buf_to_slice(abi_txt_contents), str("\n"));
95 : 2 : ZigGLibCVerList *ver_list_base = nullptr;
96 : : for (;;) {
97 [ + + ]: 62 : if (ver_list_base == nullptr) {
98 : 32 : Optional<Slice<uint8_t>> opt_line = SplitIterator_next_separate(&it);
99 [ + + ]: 32 : if (!opt_line.is_some) break;
100 : :
101 : 30 : ver_list_base = allocate<ZigGLibCVerList>(glibc_abi->all_functions.length);
102 : 30 : ZigTarget *target = allocate<ZigTarget>(1);
103 : 30 : SplitIterator line_it = memSplit(opt_line.value, str(" "));
104 : : for (;;) {
105 : 80 : Optional<Slice<uint8_t>> opt_target = SplitIterator_next(&line_it);
106 [ + + ]: 80 : if (!opt_target.is_some) break;
107 : :
108 : 50 : SplitIterator component_it = memSplit(opt_target.value, str("-"));
109 : 50 : Optional<Slice<uint8_t>> opt_arch = SplitIterator_next(&component_it);
110 : 50 : assert(opt_arch.is_some);
111 : 50 : Optional<Slice<uint8_t>> opt_os = SplitIterator_next(&component_it);
112 : 50 : assert(opt_os.is_some); // it's always "linux" so we ignore it
113 : 50 : Optional<Slice<uint8_t>> opt_abi = SplitIterator_next(&component_it);
114 : 50 : assert(opt_abi.is_some);
115 : :
116 : :
117 : 50 : err = target_parse_archsub(&target->arch, &target->sub_arch,
118 : 50 : (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 [ + - ][ + + ]: 50 : assert(err == ErrorNone || err == ErrorUnknownArchitecture);
121 : :
122 : 50 : target->os = OsLinux;
123 : :
124 : 50 : err = target_parse_abi(&target->abi, (char*)opt_abi.value.ptr, opt_abi.value.len);
125 : 50 : assert(err == ErrorNone);
126 : :
127 : 50 : glibc_abi->version_table.put(target, ver_list_base);
128 : 50 : }
129 : 30 : continue;
130 : : }
131 [ + + ]: 111120 : for (size_t fn_i = 0; fn_i < glibc_abi->all_functions.length; fn_i += 1) {
132 : 111090 : ZigGLibCVerList *ver_list = &ver_list_base[fn_i];
133 : 111090 : Optional<Slice<uint8_t>> opt_line = SplitIterator_next_separate(&it);
134 : 111090 : assert(opt_line.is_some);
135 : :
136 : 111090 : SplitIterator line_it = memSplit(opt_line.value, str(" "));
137 : : for (;;) {
138 : 214920 : Optional<Slice<uint8_t>> opt_ver = SplitIterator_next(&line_it);
139 [ + + ]: 214920 : if (!opt_ver.is_some) break;
140 : 103830 : assert(ver_list->len < 8); // increase the array len in the type
141 : :
142 : 103830 : unsigned long ver_index = strtoul(buf_ptr(buf_create_from_slice(opt_ver.value)), nullptr, 10);
143 : 103830 : assert(ver_index < 255); // use a bigger integer in the type
144 : 103830 : ver_list->versions[ver_list->len] = ver_index;
145 : 103830 : ver_list->len += 1;
146 : 103830 : }
147 : : }
148 : 30 : ver_list_base = nullptr;
149 : 60 : }
150 : : }
151 : :
152 : 2 : *out_result = glibc_abi;
153 : 2 : return ErrorNone;
154 : : }
155 : :
156 : 2 : 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 : 2 : Buf *cache_dir = get_stage1_cache_path();
162 : 2 : CacheHash *cache_hash = allocate<CacheHash>(1);
163 : 2 : Buf *manifest_dir = buf_sprintf("%s" OS_SEP CACHE_HASH_SUBDIR, buf_ptr(cache_dir));
164 : 2 : cache_init(cache_hash, manifest_dir);
165 : :
166 : : Buf *compiler_id;
167 [ - + ]: 2 : if ((err = get_compiler_id(&compiler_id))) {
168 [ # # ]: 0 : if (verbose) {
169 : 0 : fprintf(stderr, "unable to get compiler id: %s\n", err_str(err));
170 : : }
171 : 0 : return err;
172 : : }
173 : 2 : cache_buf(cache_hash, compiler_id);
174 : 2 : cache_int(cache_hash, target->arch);
175 : 2 : cache_int(cache_hash, target->abi);
176 : 2 : cache_int(cache_hash, target->glibc_version->major);
177 : 2 : cache_int(cache_hash, target->glibc_version->minor);
178 : 2 : cache_int(cache_hash, target->glibc_version->patch);
179 : :
180 : 2 : Buf digest = BUF_INIT;
181 : 2 : buf_resize(&digest, 0);
182 [ - + ]: 2 : if ((err = cache_hit(cache_hash, &digest))) {
183 : : // Treat an invalid format error as a cache miss.
184 [ # # ]: 0 : if (err != ErrorInvalidFormat)
185 : 0 : return err;
186 : : }
187 : : // We should always get a cache hit because there are no
188 : : // files in the input hash.
189 : 2 : assert(buf_len(&digest) != 0);
190 : :
191 : 2 : Buf *dummy_dir = buf_alloc();
192 : 2 : os_path_join(manifest_dir, &digest, dummy_dir);
193 : :
194 [ - + ]: 2 : if ((err = os_make_path(dummy_dir)))
195 : 0 : return err;
196 : :
197 : 2 : Buf *test_if_exists_path = buf_alloc();
198 : 2 : os_path_join(dummy_dir, buf_create_from_str("ok"), test_if_exists_path);
199 : :
200 : : bool hit;
201 [ - + ]: 2 : if ((err = os_file_exists(test_if_exists_path, &hit)))
202 : 0 : return err;
203 : :
204 [ + + ]: 2 : if (hit) {
205 : 1 : *out_dir = dummy_dir;
206 : 1 : return ErrorNone;
207 : : }
208 : :
209 : :
210 : 1 : ZigGLibCVerList *ver_list_base = glibc_abi->version_table.get(target);
211 : :
212 : 1 : uint8_t target_ver_index = 0;
213 [ + - ]: 38 : for (;target_ver_index < glibc_abi->all_versions.length; target_ver_index += 1) {
214 : 38 : const ZigGLibCVersion *this_ver = &glibc_abi->all_versions.at(target_ver_index);
215 [ + + ][ + - ]: 38 : if (this_ver->major == target->glibc_version->major &&
216 [ + - ]: 1 : this_ver->minor == target->glibc_version->minor &&
217 : 1 : this_ver->patch == target->glibc_version->patch)
218 : : {
219 : 1 : break;
220 : : }
221 : : }
222 [ - + ]: 1 : if (target_ver_index == glibc_abi->all_versions.length) {
223 [ # # ]: 0 : if (verbose) {
224 : 0 : fprintf(stderr, "Unrecognized glibc version: %d.%d.%d\n",
225 : 0 : target->glibc_version->major,
226 : 0 : target->glibc_version->minor,
227 : 0 : target->glibc_version->patch);
228 : : }
229 : 0 : return ErrorUnknownABI;
230 : : }
231 : :
232 : 1 : Buf *map_file_path = buf_sprintf("%s" OS_SEP "all.map", buf_ptr(dummy_dir));
233 : 1 : Buf *map_contents = buf_alloc();
234 : :
235 [ + + ]: 41 : for (uint8_t ver_i = 0; ver_i < glibc_abi->all_versions.length; ver_i += 1) {
236 : 40 : const ZigGLibCVersion *ver = &glibc_abi->all_versions.at(ver_i);
237 [ + + ]: 40 : if (ver->patch == 0) {
238 : 28 : buf_appendf(map_contents, "GLIBC_%d.%d { };\n", ver->major, ver->minor);
239 : : } else {
240 : 12 : buf_appendf(map_contents, "GLIBC_%d.%d.%d { };\n", ver->major, ver->minor, ver->patch);
241 : : }
242 : : }
243 : :
244 [ - + ]: 1 : if ((err = os_write_file(map_file_path, map_contents))) {
245 [ # # ]: 0 : if (verbose) {
246 : 0 : fprintf(stderr, "unable to write %s: %s", buf_ptr(map_file_path), err_str(err));
247 : : }
248 : 0 : return err;
249 : : }
250 : :
251 : :
252 [ + + ]: 6 : for (size_t lib_i = 0; lib_i < array_length(glibc_libs); lib_i += 1) {
253 : 5 : const ZigGLibCLib *lib = &glibc_libs[lib_i];
254 : 5 : Buf *zig_file_path = buf_sprintf("%s" OS_SEP "%s.zig", buf_ptr(dummy_dir), lib->name);
255 : 5 : Buf *zig_body = buf_alloc();
256 : 5 : Buf *zig_footer = buf_alloc();
257 : :
258 : 5 : buf_appendf(zig_body, "comptime {\n");
259 : 5 : buf_appendf(zig_body, " asm (\n");
260 : :
261 [ + + ]: 18520 : for (size_t fn_i = 0; fn_i < glibc_abi->all_functions.length; fn_i += 1) {
262 : 18515 : const ZigGLibCFn *libc_fn = &glibc_abi->all_functions.at(fn_i);
263 [ + + ]: 18515 : if (libc_fn->lib != lib) continue;
264 : 3703 : ZigGLibCVerList *ver_list = &ver_list_base[fn_i];
265 : : // Pick the default symbol version:
266 : : // - If there are no versions, don't emit it
267 : : // - Take the greatest one <= than the target one
268 : : // - If none of them is <= than the
269 : : // specified one don't pick any default version
270 [ + + ]: 3703 : if (ver_list->len == 0) continue;
271 : 3390 : uint8_t chosen_def_ver_index = 255;
272 [ + + ]: 6843 : for (uint8_t ver_i = 0; ver_i < ver_list->len; ver_i += 1) {
273 : 3453 : uint8_t ver_index = ver_list->versions[ver_i];
274 [ + + ][ + + ]: 3453 : if ((chosen_def_ver_index == 255 || ver_index > chosen_def_ver_index) &&
[ + + ]
275 : : target_ver_index >= ver_index)
276 : : {
277 : 3345 : chosen_def_ver_index = ver_index;
278 : : }
279 : : }
280 [ + + ]: 6843 : for (uint8_t ver_i = 0; ver_i < ver_list->len; ver_i += 1) {
281 : 3453 : uint8_t ver_index = ver_list->versions[ver_i];
282 : :
283 : : Buf *stub_name;
284 : 3453 : const ZigGLibCVersion *ver = &glibc_abi->all_versions.at(ver_index);
285 : 3453 : const char *sym_name = buf_ptr(libc_fn->name);
286 [ + + ]: 3453 : if (ver->patch == 0) {
287 : 1172 : stub_name = buf_sprintf("%s_%d_%d", sym_name, ver->major, ver->minor);
288 : : } else {
289 : 2281 : stub_name = buf_sprintf("%s_%d_%d_%d", sym_name, ver->major, ver->minor, ver->patch);
290 : : }
291 : :
292 : 3453 : buf_appendf(zig_footer, "export fn %s() void {}\n", buf_ptr(stub_name));
293 : :
294 : : // Default symbol version definition vs normal symbol version definition
295 [ + + ]: 3370 : const char *at_sign_str = (chosen_def_ver_index != 255 &&
296 [ + + ]: 6823 : ver_index == chosen_def_ver_index) ? "@@" : "@";
297 [ + + ]: 3453 : if (ver->patch == 0) {
298 : 1172 : buf_appendf(zig_body, " \\\\ .symver %s, %s%sGLIBC_%d.%d\n",
299 : 1172 : buf_ptr(stub_name), sym_name, at_sign_str, ver->major, ver->minor);
300 : : } else {
301 : 2281 : buf_appendf(zig_body, " \\\\ .symver %s, %s%sGLIBC_%d.%d.%d\n",
302 : 2281 : buf_ptr(stub_name), sym_name, at_sign_str, ver->major, ver->minor, ver->patch);
303 : : }
304 : : // Hide the stub to keep the symbol table clean
305 : 3453 : buf_appendf(zig_body, " \\\\ .hidden %s\n", buf_ptr(stub_name));
306 : : }
307 : : }
308 : :
309 : 5 : buf_appendf(zig_body, " );\n");
310 : 5 : buf_appendf(zig_body, "}\n");
311 : 5 : buf_append_buf(zig_body, zig_footer);
312 : :
313 [ - + ]: 5 : if ((err = os_write_file(zig_file_path, zig_body))) {
314 [ # # ]: 0 : if (verbose) {
315 : 0 : fprintf(stderr, "unable to write %s: %s", buf_ptr(zig_file_path), err_str(err));
316 : : }
317 : 0 : return err;
318 : : }
319 : :
320 : 5 : CodeGen *child_gen = create_child_codegen(g, zig_file_path, OutTypeLib, nullptr);
321 : 5 : codegen_set_out_name(child_gen, buf_create_from_str(lib->name));
322 : 5 : codegen_set_lib_version(child_gen, lib->sover, 0, 0);
323 : 5 : child_gen->is_dynamic = true;
324 : 5 : child_gen->is_dummy_so = true;
325 : 5 : child_gen->version_script_path = map_file_path;
326 : 5 : child_gen->enable_cache = false;
327 : 5 : child_gen->output_dir = dummy_dir;
328 : 5 : codegen_build_and_link(child_gen);
329 : : }
330 : :
331 [ - + ]: 1 : if ((err = os_write_file(test_if_exists_path, buf_alloc()))) {
332 [ # # ]: 0 : if (verbose) {
333 : 0 : fprintf(stderr, "unable to write %s: %s", buf_ptr(test_if_exists_path), err_str(err));
334 : : }
335 : 0 : return err;
336 : : }
337 : 1 : *out_dir = dummy_dir;
338 : 2 : return ErrorNone;
339 : : }
340 : :
341 : 71 : uint32_t hash_glibc_target(const ZigTarget *x) {
342 : 71 : return x->arch * 3250106448 +
343 : 142 : x->os * 542534372 +
344 : 71 : x->abi * 59162639;
345 : : }
346 : :
347 : 113 : bool eql_glibc_target(const ZigTarget *a, const ZigTarget *b) {
348 [ + - ]: 21 : return a->arch == b->arch &&
349 [ + + ][ + + ]: 134 : a->os == b->os &&
350 : 113 : a->abi == b->abi;
351 : : }
352 : :
353 : : #ifdef ZIG_OS_LINUX
354 : : #include <unistd.h>
355 : 26 : Error glibc_detect_native_version(ZigGLibCVersion *glibc_ver) {
356 : 26 : Buf *self_libc_path = get_self_libc_path();
357 [ - + ]: 26 : if (self_libc_path == nullptr) {
358 : : // TODO There is still more we could do to detect the native glibc version. For example,
359 : : // we could look at the ELF file of `/usr/bin/env`, find `libc.so.6`, and then `readlink`
360 : : // to find out the glibc version. This is relevant for the static zig builds distributed
361 : : // on the download page, since the above detection based on zig's own dynamic linking
362 : : // will not work.
363 : :
364 : 0 : return ErrorUnknownABI;
365 : : }
366 : 26 : Buf *link_name = buf_alloc();
367 : 26 : buf_resize(link_name, 4096);
368 : 26 : ssize_t amt = readlink(buf_ptr(self_libc_path), buf_ptr(link_name), buf_len(link_name));
369 [ - + ]: 26 : if (amt == -1) {
370 : 0 : return ErrorUnknownABI;
371 : : }
372 : 26 : buf_resize(link_name, amt);
373 [ - + ][ - + ]: 26 : if (!buf_starts_with_str(link_name, "libc-") || !buf_ends_with_str(link_name, ".so")) {
[ + - ]
374 : 0 : return ErrorUnknownABI;
375 : : }
376 : : // example: "libc-2.3.4.so"
377 : : // example: "libc-2.27.so"
378 : 26 : buf_resize(link_name, buf_len(link_name) - 3); // chop off ".so"
379 : 26 : glibc_ver->major = 2;
380 : 26 : glibc_ver->minor = 0;
381 : 26 : glibc_ver->patch = 0;
382 : 26 : return target_parse_glibc_version(glibc_ver, buf_ptr(link_name) + 5);
383 : : }
384 : : #else
385 : : Error glibc_detect_native_version(ZigGLibCVersion *glibc_ver) {
386 : : return ErrorUnknownABI;
387 : : }
388 : : #endif
389 : :
390 : 2 : size_t glibc_lib_count(void) {
391 : 2 : return array_length(glibc_libs);
392 : : }
393 : :
394 : 10 : const ZigGLibCLib *glibc_lib_enum(size_t index) {
395 : 10 : assert(index < array_length(glibc_libs));
396 : 10 : return &glibc_libs[index];
397 : : }
398 : :
399 : 7406 : const ZigGLibCLib *glibc_lib_find(const char *name) {
400 [ + - ]: 10438 : for (size_t i = 0; i < array_length(glibc_libs); i += 1) {
401 [ + + ]: 10438 : if (strcmp(glibc_libs[i].name, name) == 0) {
402 : 7406 : return &glibc_libs[i];
403 : : }
404 : : }
405 : 0 : return nullptr;
406 : : }
|