Branch data Line data Source code
1 : : /*-
2 : : * Copyright (c) 2011-2012 Baptiste Daroussin <bapt@FreeBSD.org>
3 : : * Copyright (c) 2012-2013 Matthew Seaman <matthew@FreeBSD.org>
4 : : * Copyright (c) 2024 The FreeBSD Foundation
5 : : *
6 : : * This software was developed in part by Isaac Freund <ifreund@freebsdfoundation.org>
7 : : * under sponsorship from the FreeBSD Foundation.
8 : : *
9 : : * SPDX-License-Identifier: BSD-2-Clause
10 : : */
11 : : #ifdef HAVE_CONFIG_H
12 : : #include "pkg_config.h"
13 : : #endif
14 : :
15 : : #include <ctype.h>
16 : : #include <paths.h>
17 : : #include <string.h>
18 : : #include <unistd.h>
19 : :
20 : : #include "pkg.h"
21 : : #include "private/pkg_abi.h"
22 : : #include "private/binfmt.h"
23 : : #include "private/event.h"
24 : : #include "private/pkg.h"
25 : : #include "xmalloc.h"
26 : :
27 : : #define _PATH_UNAME "/usr/bin/uname"
28 : :
29 : : /* All possibilities on FreeBSD as of 5/26/2014 */
30 : : struct arch_trans {
31 : : const char *elftype;
32 : : const char *archid;
33 : : };
34 : :
35 : : static struct arch_trans machine_arch_translation[] = { { "x86:32", "i386" },
36 : : { "x86:64", "amd64" }, { "powerpc:32:eb", "powerpc" },
37 : : { "powerpc:64:eb", "powerpc64" }, { "powerpc:64:el", "powerpc64le" },
38 : : { "sparc64:64", "sparc64" }, { "ia64:64", "ia64" },
39 : : /* All the ARM stuff */
40 : : { "armv6:32:el:eabi:hardfp", "armv6" },
41 : : { "armv7:32:el:eabi:hardfp", "armv7" }, { "aarch64:64", "aarch64" },
42 : : /* And now MIPS */
43 : : { "mips:32:el:o32", "mipsel" }, { "mips:32:el:n32", "mipsn32el" },
44 : : { "mips:32:eb:o32", "mips" }, { "mips:32:eb:n32", "mipsn32" },
45 : : { "mips:64:el:n64", "mips64el" }, { "mips:64:eb:n64", "mips64" },
46 : : /* And RISC-V */
47 : : { "riscv:32:hf", "riscv32" }, { "riscv:32:sf", "riscv32sf" },
48 : : { "riscv:64:hf", "riscv64" }, { "riscv:64:sf", "riscv64sf" },
49 : :
50 : : { NULL, NULL } };
51 : :
52 : : static struct {
53 : : enum pkg_os os;
54 : : const char *string;
55 : : } os_string_table[] = {
56 : : { PKG_OS_UNKNOWN, "Unknown" },
57 : : { PKG_OS_FREEBSD, "FreeBSD" },
58 : : { PKG_OS_NETBSD, "NetBSD" },
59 : : { PKG_OS_DRAGONFLY, "dragonfly" },
60 : : { PKG_OS_LINUX, "Linux" },
61 : : { PKG_OS_DARWIN, "Darwin" },
62 : : { -1, NULL },
63 : : };
64 : :
65 : : /* This table does not include PKG_ARCH_AMD64 as the string translation of
66 : : that arch is os-dependent. */
67 : : static struct {
68 : : enum pkg_arch arch;
69 : : const char *string;
70 : : } arch_string_table[] = {
71 : : { PKG_ARCH_UNKNOWN, "unknown"},
72 : : { PKG_ARCH_I386, "i386"},
73 : : { PKG_ARCH_ARMV6, "armv6"},
74 : : { PKG_ARCH_ARMV7, "armv7"},
75 : : { PKG_ARCH_AARCH64, "aarch64"},
76 : : { PKG_ARCH_POWERPC, "powerpc"},
77 : : { PKG_ARCH_POWERPC64, "powerpc64"},
78 : : { PKG_ARCH_POWERPC64LE, "powerpc64le"},
79 : : { PKG_ARCH_RISCV32, "riscv32"},
80 : : { PKG_ARCH_RISCV64, "riscv64"},
81 : : { -1, NULL },
82 : : };
83 : :
84 : : const char *
85 : 7389 : pkg_os_to_string(enum pkg_os os)
86 : : {
87 [ + - ]: 16070 : for (size_t i = 0; os_string_table[i].string != NULL; i++) {
88 [ + + ]: 16070 : if (os == os_string_table[i].os) {
89 : 7389 : return os_string_table[i].string;
90 : : }
91 : 8681 : }
92 : 0 : assert(0);
93 : : }
94 : :
95 : : enum pkg_os
96 : 2 : pkg_os_from_string(const char *string)
97 : : {
98 [ + - ]: 4 : for (size_t i = 0; os_string_table[i].string != NULL; i++) {
99 [ + + ]: 4 : if (STREQ(string, os_string_table[i].string)) {
100 : 2 : return os_string_table[i].os;
101 : : }
102 : 2 : }
103 : 0 : return (PKG_OS_UNKNOWN);
104 : 2 : }
105 : :
106 : : /* Returns true if the OS uses "amd64" rather than "x86_64" */
107 : : static bool
108 : 7047 : pkg_os_uses_amd64_name(enum pkg_os os)
109 : : {
110 [ + + + - : 7047 : switch (os) {
- - - ]
111 : : case PKG_OS_FREEBSD:
112 : 7047 : return (true);
113 : : case PKG_OS_DARWIN:
114 : : case PKG_OS_NETBSD:
115 : : case PKG_OS_LINUX:
116 : 242 : return (false);
117 : : case PKG_OS_DRAGONFLY:
118 : 0 : case PKG_OS_UNKNOWN:
119 : : default:
120 : 0 : assert(0);
121 : : }
122 : 7047 : }
123 : :
124 : : const char *
125 : 7421 : pkg_arch_to_string(enum pkg_os os, enum pkg_arch arch)
126 : : {
127 [ + + ]: 7421 : if (arch == PKG_ARCH_AMD64) {
128 [ + + ]: 7055 : if (os == PKG_OS_DRAGONFLY) {
129 : 28 : return ("x86:64");
130 [ + + ]: 7027 : } else if (pkg_os_uses_amd64_name(os)) {
131 : 6803 : return ("amd64");
132 : : } else {
133 : 224 : return ("x86_64");
134 : : }
135 : : }
136 : :
137 [ + - ]: 1958 : for (size_t i = 0; arch_string_table[i].string != NULL; i++) {
138 [ + + ]: 1958 : if (arch == arch_string_table[i].arch) {
139 : 366 : return arch_string_table[i].string;
140 : : }
141 : 1592 : }
142 : :
143 : 0 : assert(0);
144 : 7421 : }
145 : :
146 : : enum pkg_arch
147 : 20 : pkg_arch_from_string(enum pkg_os os, const char *string)
148 : : {
149 [ - + ]: 20 : if (os == PKG_OS_DRAGONFLY) {
150 [ # # ]: 0 : if (STREQ(string, "x86:64")) {
151 : 0 : return (PKG_ARCH_AMD64);
152 : : }
153 [ + + ]: 20 : } else if (pkg_os_uses_amd64_name(os)) {
154 [ + - ]: 2 : if (STREQ(string, "amd64")) {
155 : 0 : return (PKG_ARCH_AMD64);
156 : : }
157 : 2 : } else {
158 [ + + ]: 18 : if (STREQ(string, "x86_64")) {
159 : 6 : return (PKG_ARCH_AMD64);
160 : : }
161 : : }
162 : :
163 [ + + ]: 72 : for (size_t i = 0; arch_string_table[i].string != NULL; i++) {
164 [ + + ]: 70 : if (STREQ(string, arch_string_table[i].string)) {
165 : 12 : return arch_string_table[i].arch;
166 : : }
167 : 58 : }
168 : :
169 : 2 : return (PKG_ARCH_UNKNOWN);
170 : 20 : }
171 : :
172 : : bool
173 : 7391 : pkg_abi_string_only_major_version(enum pkg_os os)
174 : : {
175 [ + - + - : 7391 : switch (os) {
+ + - ]
176 : : case PKG_OS_FREEBSD:
177 : : case PKG_OS_NETBSD:
178 : : case PKG_OS_DARWIN:
179 : 7391 : return (true);
180 : : case PKG_OS_DRAGONFLY:
181 : : case PKG_OS_LINUX:
182 : 56 : return (false);
183 : 0 : case PKG_OS_UNKNOWN:
184 : : default:
185 : 0 : assert (0);
186 : : }
187 : 7391 : }
188 : :
189 : : char *
190 : 4266 : pkg_abi_to_string(const struct pkg_abi *abi)
191 : : {
192 : : char *ret;
193 [ + + ]: 4266 : if (pkg_abi_string_only_major_version(abi->os)) {
194 : 8468 : xasprintf(&ret, "%s:%d:%s", pkg_os_to_string(abi->os),
195 : 4234 : abi->major, pkg_arch_to_string(abi->os, abi->arch));
196 : 4234 : } else {
197 : 64 : xasprintf(&ret, "%s:%d.%d:%s", pkg_os_to_string(abi->os),
198 : 32 : abi->major, abi->minor,
199 : 32 : pkg_arch_to_string(abi->os, abi->arch));
200 : : }
201 : 4266 : return (ret);
202 : : }
203 : :
204 : : bool
205 : 2 : pkg_abi_from_string(struct pkg_abi *abi, const char *string)
206 : : {
207 : 2 : *abi = (struct pkg_abi){0};
208 : :
209 : 2 : bool ret = false;
210 : :
211 : 2 : char *copy = xstrdup(string);
212 : :
213 : 2 : char *iter = copy;
214 : 2 : char *os = strsep(&iter, ":");
215 [ + - ]: 2 : assert(os != NULL);
216 : 2 : abi->os = pkg_os_from_string(os);
217 [ + - ]: 2 : if (abi->os == PKG_OS_UNKNOWN) {
218 : 0 : pkg_emit_error("Unknown OS '%s' in ABI string", os);
219 : 0 : goto out;
220 : : }
221 : :
222 : 2 : char *version = strsep(&iter, ":");
223 [ + - ]: 2 : if (version == NULL) {
224 : 0 : pkg_emit_error("Invalid ABI string '%s', "
225 : 0 : "missing version and architecture", string);
226 : 0 : goto out;
227 : : }
228 : 2 : const char *errstr = NULL;
229 [ + - ]: 2 : if (pkg_abi_string_only_major_version(abi->os)) {
230 : 2 : abi->major = strtonum(version, 1, INT_MAX, &errstr);
231 : 2 : } else {
232 : : /* XXX add tests for this */
233 : 0 : char *major = strsep(&version, ".");
234 : 0 : char *minor = strsep(&version, ".");
235 : :
236 [ # # ]: 0 : assert(major != NULL);
237 [ # # ]: 0 : if (minor == NULL) {
238 : 0 : pkg_emit_error("Invalid ABI string %s, "
239 : 0 : "missing minor OS version", string);
240 : 0 : goto out;
241 : : }
242 : :
243 : 0 : abi->major = strtonum(major, 1, INT_MAX, &errstr);
244 [ # # ]: 0 : if (errstr != NULL) {
245 : 0 : abi->minor = strtonum(minor, 1, INT_MAX, &errstr);
246 : 0 : }
247 : : }
248 [ - + ]: 2 : if (errstr != NULL) {
249 : 0 : pkg_emit_error("Invalid version in ABI string '%s'", string);
250 : 0 : goto out;
251 : : }
252 : :
253 : : /* DragonFlyBSD continues to use the legacy/altabi format.
254 : : For example: dragonfly:5.10:x86:64
255 : : This means we can't use strsep again since that would split the arch
256 : : string for dragonfly. */
257 : 2 : char *arch = iter;
258 [ + - ]: 2 : if (arch == NULL) {
259 : 0 : pkg_emit_error("Invalid ABI string '%s', "
260 : 0 : "missing architecture", string);
261 : 0 : goto out;
262 : : }
263 : :
264 : 2 : abi->arch = pkg_arch_from_string(abi->os, arch);
265 [ + - ]: 2 : if (abi->arch == PKG_ARCH_UNKNOWN) {
266 : 0 : pkg_emit_error("Unknown architecture '%s' in ABI string", arch);
267 : 0 : goto out;
268 : : }
269 : :
270 [ - + # # ]: 2 : if (abi->os == PKG_OS_DRAGONFLY && abi->arch != PKG_ARCH_AMD64) {
271 : 0 : pkg_emit_error("Invalid ABI string '%s', "
272 : 0 : "only x86:64 is supported on dragonfly.", string);
273 : 0 : goto out;
274 : : }
275 : :
276 : 2 : ret = true;
277 : : out:
278 : 2 : free(copy);
279 : 2 : return (ret);
280 : : }
281 : :
282 : : void
283 : 907 : pkg_abi_set_freebsd_osversion(struct pkg_abi *abi, int osversion)
284 : : {
285 [ + - ]: 907 : assert(abi->os == PKG_OS_FREEBSD);
286 : :
287 : 907 : abi->major = osversion / 100000;
288 : 907 : abi->minor = (osversion / 1000) % 100;
289 : 907 : abi->patch = osversion % 1000;
290 : 907 : }
291 : :
292 : : int
293 : 4192 : pkg_abi_get_freebsd_osversion(struct pkg_abi *abi)
294 : : {
295 [ + - ]: 4192 : assert(abi->os == PKG_OS_FREEBSD);
296 : :
297 : 4192 : return (abi->major * 100000) + (abi->minor * 1000) + abi->patch;
298 : : }
299 : :
300 : : int
301 : 925 : pkg_abi_from_file(struct pkg_abi *abi)
302 : : {
303 : : char rooted_abi_file[PATH_MAX];
304 : 925 : const char *abi_files[] = {
305 : 925 : getenv("ABI_FILE"),
306 : : _PATH_UNAME,
307 : : _PATH_BSHELL,
308 : : };
309 : : char work_abi_file[PATH_MAX];
310 : : char work_arch_hint[PATH_MAX];
311 : :
312 : : int i, fd;
313 : :
314 : : /*
315 : : * Perhaps not yet needed, but it may be in the future that there's no
316 : : * need to check root under some conditions where there is a rootdir.
317 : : * This also helps alleviate some excessive wrapping later.
318 : : */
319 : 925 : bool checkroot = ctx.pkg_rootdir != NULL;
320 [ - + ]: 1780 : for (fd = -1, i = 0; i < NELEM(abi_files); i++) {
321 [ + + ]: 1780 : if (abi_files[i] == NULL)
322 : 855 : continue;
323 : :
324 : 925 : const char *sep = strrchr(abi_files[i], '#');
325 [ + + ]: 925 : if (sep) {
326 : 36 : strlcpy(work_abi_file, abi_files[i],
327 [ + - ]: 18 : MIN(sep - abi_files[i] + 1, sizeof(work_abi_file)));
328 : 18 : strlcpy(work_arch_hint, sep + 1,
329 : : sizeof(work_arch_hint));
330 : 18 : } else {
331 : 907 : strlcpy(work_abi_file, abi_files[i],
332 : : sizeof(work_abi_file));
333 : 907 : work_arch_hint[0] = '\0';
334 : : }
335 : :
336 : : /*
337 : : * Try prepending rootdir and using that if it exists. If
338 : : * ABI_FILE is specified, assume that the consumer didn't want
339 : : * it mangled by rootdir.
340 : : */
341 [ + + + + : 925 : if (i > 0 && checkroot &&
- + ]
342 : 190 : snprintf(rooted_abi_file, PATH_MAX, "%s/%s",
343 : 190 : ctx.pkg_rootdir, work_abi_file) < PATH_MAX) {
344 [ + - ]: 95 : if ((fd = open(rooted_abi_file, O_RDONLY)) >= 0) {
345 : 0 : strlcpy(work_abi_file, rooted_abi_file,
346 : : sizeof(work_abi_file));
347 : 0 : break;
348 : : }
349 : 95 : }
350 [ + - ]: 925 : if ((fd = open(work_abi_file, O_RDONLY)) >= 0) {
351 : 925 : break;
352 : : }
353 : : /* if the ABI_FILE was provided we only care about it */
354 [ # # ]: 0 : if (i == 0)
355 : 0 : break;
356 : 0 : }
357 [ + - ]: 925 : if (fd == -1) {
358 : 0 : pkg_emit_error(
359 : : "Unable to determine the ABI, none of the ABI_FILEs can be read.");
360 : 0 : return EPKG_FATAL;
361 : : }
362 : :
363 : :
364 : 925 : int ret = pkg_elf_abi_from_fd(fd, abi);
365 [ + + ]: 956 : if (EPKG_OK != ret) {
366 [ - + ]: 37 : if (-1 == lseek(fd, 0, SEEK_SET)) {
367 : 0 : pkg_emit_errno("Error seeking file", work_abi_file);
368 : 0 : ret = EPKG_FATAL;
369 : 0 : goto close_out;
370 : : }
371 : :
372 : 37 : enum pkg_arch arch_hint = PKG_ARCH_UNKNOWN;
373 [ + + ]: 37 : if (work_arch_hint[0]) {
374 : 18 : arch_hint = pkg_arch_from_string(PKG_OS_DARWIN, work_arch_hint);
375 [ + + ]: 18 : if (arch_hint == PKG_ARCH_UNKNOWN) {
376 : 2 : pkg_emit_error("Invalid ABI_FILE architecture hint %s",
377 : 2 : work_arch_hint);
378 : 2 : ret = EPKG_FATAL;
379 : 2 : goto close_out;
380 : : }
381 : 16 : }
382 : :
383 : 35 : ret = pkg_macho_abi_from_fd(fd, abi, arch_hint);
384 [ + + ]: 35 : if (EPKG_OK != ret) {
385 : 4 : pkg_emit_error(
386 : : "Unable to determine ABI, %s cannot be parsed.",
387 : 4 : work_abi_file);
388 : 4 : ret = EPKG_FATAL;
389 : 4 : goto close_out;
390 : : }
391 : 31 : }
392 : :
393 : : close_out:
394 [ + - ]: 925 : if (close(fd)) {
395 : 0 : pkg_emit_errno("Error closing file", work_abi_file);
396 : 0 : ret = EPKG_FATAL;
397 : 0 : }
398 : 925 : return ret;
399 : 925 : }
400 : :
401 : : int
402 : 5757 : pkg_arch_to_legacy(const char *arch, char *dest, size_t sz)
403 : : {
404 : 5757 : int i = 0;
405 : : struct arch_trans *arch_trans;
406 : :
407 : 5757 : memset(dest, '\0', sz);
408 : : /* Lower case the OS */
409 [ + + + + : 42814 : while (arch[i] != ':' && arch[i] != '\0') {
+ + ]
410 : 37057 : dest[i] = tolower(arch[i]);
411 : 37057 : i++;
412 : : }
413 [ + + ]: 5757 : if (arch[i] == '\0')
414 : 510 : return (0);
415 : :
416 : 5247 : dest[i++] = ':';
417 : :
418 : : /* Copy the version */
419 [ + + - + : 15795 : while (arch[i] != ':' && arch[i] != '\0') {
+ + ]
420 : 10548 : dest[i] = arch[i];
421 : 10548 : i++;
422 : : }
423 [ + - ]: 5247 : if (arch[i] == '\0')
424 : 0 : return (0);
425 : :
426 : 5247 : dest[i++] = ':';
427 : :
428 [ + + ]: 14716 : for (arch_trans = machine_arch_translation; arch_trans->elftype != NULL;
429 : 9469 : arch_trans++) {
430 [ + + ]: 14562 : if (STREQ(arch + i, arch_trans->archid)) {
431 : 10186 : strlcpy(dest + i, arch_trans->elftype,
432 : 5093 : sz - (arch + i - dest));
433 : 5093 : return (0);
434 : : }
435 : 9469 : }
436 : 154 : strlcpy(dest + i, arch + i, sz - (arch + i - dest));
437 : :
438 : 154 : return (0);
439 : 5757 : }
440 : :
441 : : int
442 : 332 : pkg_analyse_files(struct pkgdb *db __unused, struct pkg *pkg, const char *stage)
443 : : {
444 : 332 : struct pkg_file *file = NULL;
445 : 332 : int ret = EPKG_OK;
446 : : char fpath[MAXPATHLEN + 1];
447 : : const char *lib;
448 : 332 : bool failures = false;
449 : :
450 : : int (*pkg_analyse_init)(const char *stage);
451 : : int (*pkg_analyse)(const bool developer_mode, struct pkg *pkg, const char *fpath);
452 : : int (*pkg_analyse_close)();
453 : :
454 [ + + ]: 332 : if (0 == strncmp(pkg->abi, "Darwin", 6)) {
455 : 9 : pkg_analyse_init=pkg_analyse_init_macho;
456 : 9 : pkg_analyse=pkg_analyse_macho;
457 : 9 : pkg_analyse_close=pkg_analyse_close_macho;
458 : 9 : } else {
459 : 323 : pkg_analyse_init=pkg_analyse_init_elf;
460 : 323 : pkg_analyse=pkg_analyse_elf;
461 : 323 : pkg_analyse_close=pkg_analyse_close_elf;
462 : : }
463 : :
464 [ + + ]: 332 : if (tll_length(pkg->shlibs_required) != 0) {
465 [ + - + + : 4 : tll_free_and_free(pkg->shlibs_required, free);
- + ]
466 : 2 : }
467 : :
468 [ + + ]: 332 : if (tll_length(pkg->shlibs_provided) != 0) {
469 [ + - + + : 10 : tll_free_and_free(pkg->shlibs_provided, free);
- + ]
470 : 5 : }
471 : :
472 : 332 : ret = pkg_analyse_init(stage);
473 [ - + ]: 332 : if (ret != EPKG_OK) {
474 : 0 : goto cleanup;
475 : : }
476 : :
477 : : /* Assume no architecture dependence, for contradiction */
478 [ + - ]: 332 : if (ctx.developer_mode)
479 : 0 : pkg->flags &= ~(PKG_CONTAINS_ELF_OBJECTS |
480 : : PKG_CONTAINS_STATIC_LIBS | PKG_CONTAINS_LA);
481 : :
482 [ + + ]: 588 : while (pkg_files(pkg, &file) == EPKG_OK) {
483 [ + + ]: 256 : if (stage != NULL)
484 : 136 : snprintf(fpath, sizeof(fpath), "%s/%s", stage,
485 : 68 : file->path);
486 : : else
487 : 188 : strlcpy(fpath, file->path, sizeof(fpath));
488 : :
489 : 256 : ret = pkg_analyse(ctx.developer_mode, pkg, fpath);
490 [ + - ]: 256 : if (EPKG_WARN == ret) {
491 : 0 : failures = true;
492 : 0 : }
493 : : }
494 : :
495 : : /*
496 : : * Do not depend on libraries that a package provides itself
497 : : */
498 [ + + + + : 372 : tll_foreach(pkg->shlibs_required, s)
+ + ]
499 : : {
500 [ - + ]: 40 : if (stringlist_contains(&pkg->shlibs_provided, s->item)) {
501 : 0 : pkg_debug(2,
502 : : "remove %s from required shlibs as the "
503 : : "package %s provides this library itself",
504 : 0 : s->item, pkg->name);
505 [ # # # # : 0 : tll_remove_and_free(pkg->shlibs_required, s, free);
# # ]
506 : 0 : continue;
507 : : }
508 : 40 : file = NULL;
509 [ + + ]: 84 : while (pkg_files(pkg, &file) == EPKG_OK) {
510 [ - + # # ]: 44 : if ((lib = strstr(file->path, s->item)) != NULL &&
511 [ # # ]: 0 : strlen(lib) == strlen(s->item) && lib[-1] == '/') {
512 : 0 : pkg_debug(2,
513 : : "remove %s from required shlibs as "
514 : : "the package %s provides this file itself",
515 : 0 : s->item, pkg->name);
516 : :
517 [ # # # # : 0 : tll_remove_and_free(pkg->shlibs_required, s,
# # ]
518 : : free);
519 : 0 : break;
520 : : }
521 : : }
522 : 40 : }
523 : :
524 : : /*
525 : : * if the package is not supposed to provide share libraries then
526 : : * drop the provided one
527 : : */
528 [ + - ]: 332 : if (pkg_kv_get(&pkg->annotations, "no_provide_shlib") != NULL) {
529 [ # # # # : 0 : tll_free_and_free(pkg->shlibs_provided, free);
# # ]
530 : 0 : }
531 : :
532 [ - + ]: 332 : if (failures)
533 : 0 : goto cleanup;
534 : :
535 : 332 : ret = EPKG_OK;
536 : :
537 : : cleanup:
538 : 332 : ret = pkg_analyse_close();
539 : :
540 : 332 : return (ret);
541 : : }
|