Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/pkg
Path: blob/main/libpkg/pkg_abi.c
2065 views
1
/*-
2
* Copyright (c) 2011-2025 Baptiste Daroussin <[email protected]>
3
* Copyright (c) 2012-2013 Matthew Seaman <[email protected]>
4
* Copyright (c) 2024 The FreeBSD Foundation
5
*
6
* This software was developed in part by Isaac Freund <[email protected]>
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 "private/utils.h"
26
#include "xmalloc.h"
27
28
#define _PATH_UNAME "/usr/bin/uname"
29
30
/* All possibilities on FreeBSD as of 5/26/2014 */
31
struct arch_trans {
32
const char *elftype;
33
const char *archid;
34
};
35
36
static struct arch_trans machine_arch_translation[] = { { "x86:32", "i386" },
37
{ "x86:64", "amd64" }, { "powerpc:32:eb", "powerpc" },
38
{ "powerpc:64:eb", "powerpc64" }, { "powerpc:64:el", "powerpc64le" },
39
{ "sparc64:64", "sparc64" }, { "ia64:64", "ia64" },
40
/* All the ARM stuff */
41
{ "armv6:32:el:eabi:hardfp", "armv6" },
42
{ "armv7:32:el:eabi:hardfp", "armv7" }, { "aarch64:64", "aarch64" },
43
/* And now MIPS */
44
{ "mips:32:el:o32", "mipsel" }, { "mips:32:el:n32", "mipsn32el" },
45
{ "mips:32:eb:o32", "mips" }, { "mips:32:eb:n32", "mipsn32" },
46
{ "mips:64:el:n64", "mips64el" }, { "mips:64:eb:n64", "mips64" },
47
/* And RISC-V */
48
{ "riscv:32:hf", "riscv32" }, { "riscv:32:sf", "riscv32sf" },
49
{ "riscv:64:hf", "riscv64" }, { "riscv:64:sf", "riscv64sf" },
50
51
{ NULL, NULL } };
52
53
static struct {
54
enum pkg_os os;
55
const char *string;
56
} os_string_table[] = {
57
{ PKG_OS_UNKNOWN, "Unknown" },
58
{ PKG_OS_FREEBSD, "FreeBSD" },
59
{ PKG_OS_NETBSD, "NetBSD" },
60
{ PKG_OS_DRAGONFLY, "dragonfly" },
61
{ PKG_OS_LINUX, "Linux" },
62
{ PKG_OS_DARWIN, "Darwin" },
63
{ -1, NULL },
64
};
65
66
/* This table does not include PKG_ARCH_AMD64 as the string translation of
67
that arch is os-dependent. */
68
static struct {
69
enum pkg_arch arch;
70
const char *string;
71
} arch_string_table[] = {
72
{ PKG_ARCH_UNKNOWN, "unknown"},
73
{ PKG_ARCH_I386, "i386"},
74
{ PKG_ARCH_ARMV6, "armv6"},
75
{ PKG_ARCH_ARMV7, "armv7"},
76
{ PKG_ARCH_AARCH64, "aarch64"},
77
{ PKG_ARCH_POWERPC, "powerpc"},
78
{ PKG_ARCH_POWERPC64, "powerpc64"},
79
{ PKG_ARCH_POWERPC64LE, "powerpc64le"},
80
{ PKG_ARCH_RISCV32, "riscv32"},
81
{ PKG_ARCH_RISCV64, "riscv64"},
82
{ -1, NULL },
83
};
84
85
const char *
86
pkg_os_to_string(enum pkg_os os)
87
{
88
for (size_t i = 0; os_string_table[i].string != NULL; i++) {
89
if (os == os_string_table[i].os) {
90
return os_string_table[i].string;
91
}
92
}
93
assert(0);
94
}
95
96
enum pkg_os
97
pkg_os_from_string(const char *string)
98
{
99
for (size_t i = 0; os_string_table[i].string != NULL; i++) {
100
if (STREQ(string, os_string_table[i].string)) {
101
return os_string_table[i].os;
102
}
103
}
104
return (PKG_OS_UNKNOWN);
105
}
106
107
/* Returns true if the OS uses "amd64" rather than "x86_64" */
108
static bool
109
pkg_os_uses_amd64_name(enum pkg_os os)
110
{
111
switch (os) {
112
case PKG_OS_FREEBSD:
113
return (true);
114
case PKG_OS_DARWIN:
115
case PKG_OS_NETBSD:
116
case PKG_OS_LINUX:
117
return (false);
118
case PKG_OS_DRAGONFLY:
119
case PKG_OS_UNKNOWN:
120
default:
121
assert(0);
122
}
123
}
124
125
const char *
126
pkg_arch_to_string(enum pkg_os os, enum pkg_arch arch)
127
{
128
if (arch == PKG_ARCH_AMD64) {
129
if (os == PKG_OS_DRAGONFLY) {
130
return ("x86:64");
131
} else if (pkg_os_uses_amd64_name(os)) {
132
return ("amd64");
133
} else {
134
return ("x86_64");
135
}
136
}
137
138
for (size_t i = 0; arch_string_table[i].string != NULL; i++) {
139
if (arch == arch_string_table[i].arch) {
140
return arch_string_table[i].string;
141
}
142
}
143
144
assert(0);
145
}
146
147
enum pkg_arch
148
pkg_arch_from_string(enum pkg_os os, const char *string)
149
{
150
if (os == PKG_OS_DRAGONFLY) {
151
if (STREQ(string, "x86:64")) {
152
return (PKG_ARCH_AMD64);
153
}
154
} else if (pkg_os_uses_amd64_name(os)) {
155
if (STREQ(string, "amd64")) {
156
return (PKG_ARCH_AMD64);
157
}
158
} else {
159
if (STREQ(string, "x86_64")) {
160
return (PKG_ARCH_AMD64);
161
}
162
}
163
164
for (size_t i = 0; arch_string_table[i].string != NULL; i++) {
165
if (STREQ(string, arch_string_table[i].string)) {
166
return arch_string_table[i].arch;
167
}
168
}
169
170
return (PKG_ARCH_UNKNOWN);
171
}
172
173
bool
174
pkg_abi_string_only_major_version(enum pkg_os os)
175
{
176
switch (os) {
177
case PKG_OS_FREEBSD:
178
case PKG_OS_NETBSD:
179
case PKG_OS_DARWIN:
180
return (true);
181
case PKG_OS_DRAGONFLY:
182
case PKG_OS_LINUX:
183
return (false);
184
case PKG_OS_UNKNOWN:
185
default:
186
assert (0);
187
}
188
}
189
190
char *
191
pkg_abi_to_string(const struct pkg_abi *abi)
192
{
193
char *ret;
194
if (pkg_abi_string_only_major_version(abi->os)) {
195
xasprintf(&ret, "%s:%d:%s", pkg_os_to_string(abi->os),
196
abi->major, pkg_arch_to_string(abi->os, abi->arch));
197
} else {
198
xasprintf(&ret, "%s:%d.%d:%s", pkg_os_to_string(abi->os),
199
abi->major, abi->minor,
200
pkg_arch_to_string(abi->os, abi->arch));
201
}
202
return (ret);
203
}
204
205
bool
206
pkg_abi_from_string(struct pkg_abi *abi, const char *string)
207
{
208
*abi = (struct pkg_abi){0};
209
210
bool ret = false;
211
212
char *copy = xstrdup(string);
213
214
char *iter = copy;
215
char *os = strsep(&iter, ":");
216
assert(os != NULL);
217
abi->os = pkg_os_from_string(os);
218
if (abi->os == PKG_OS_UNKNOWN) {
219
pkg_emit_error("Unknown OS '%s' in ABI string", os);
220
goto out;
221
}
222
223
char *version = strsep(&iter, ":");
224
if (version == NULL) {
225
pkg_emit_error("Invalid ABI string '%s', "
226
"missing version and architecture", string);
227
goto out;
228
}
229
const char *errstr = NULL;
230
if (pkg_abi_string_only_major_version(abi->os)) {
231
abi->major = strtonum(version, 1, INT_MAX, &errstr);
232
} else {
233
/* XXX add tests for this */
234
char *major = strsep(&version, ".");
235
char *minor = strsep(&version, ".");
236
237
assert(major != NULL);
238
if (minor == NULL) {
239
pkg_emit_error("Invalid ABI string %s, "
240
"missing minor OS version", string);
241
goto out;
242
}
243
244
abi->major = strtonum(major, 1, INT_MAX, &errstr);
245
if (errstr != NULL) {
246
abi->minor = strtonum(minor, 1, INT_MAX, &errstr);
247
}
248
}
249
if (errstr != NULL) {
250
pkg_emit_error("Invalid version in ABI string '%s'", string);
251
goto out;
252
}
253
254
/* DragonFlyBSD continues to use the legacy/altabi format.
255
For example: dragonfly:5.10:x86:64
256
This means we can't use strsep again since that would split the arch
257
string for dragonfly. */
258
char *arch = iter;
259
if (arch == NULL) {
260
pkg_emit_error("Invalid ABI string '%s', "
261
"missing architecture", string);
262
goto out;
263
}
264
265
abi->arch = pkg_arch_from_string(abi->os, arch);
266
if (abi->arch == PKG_ARCH_UNKNOWN) {
267
pkg_emit_error("Unknown architecture '%s' in ABI string", arch);
268
goto out;
269
}
270
271
if (abi->os == PKG_OS_DRAGONFLY && abi->arch != PKG_ARCH_AMD64) {
272
pkg_emit_error("Invalid ABI string '%s', "
273
"only x86:64 is supported on dragonfly.", string);
274
goto out;
275
}
276
277
ret = true;
278
out:
279
free(copy);
280
return (ret);
281
}
282
283
void
284
pkg_abi_set_freebsd_osversion(struct pkg_abi *abi, int osversion)
285
{
286
assert(abi->os == PKG_OS_FREEBSD);
287
288
abi->major = osversion / 100000;
289
abi->minor = (osversion / 1000) % 100;
290
abi->patch = osversion % 1000;
291
}
292
293
int
294
pkg_abi_get_freebsd_osversion(struct pkg_abi *abi)
295
{
296
assert(abi->os == PKG_OS_FREEBSD);
297
298
return (abi->major * 100000) + (abi->minor * 1000) + abi->patch;
299
}
300
301
int
302
pkg_abi_from_file(struct pkg_abi *abi)
303
{
304
char rooted_abi_file[PATH_MAX];
305
const char *abi_files[] = {
306
getenv("ABI_FILE"),
307
_PATH_UNAME,
308
_PATH_BSHELL,
309
};
310
char work_abi_file[PATH_MAX];
311
char work_arch_hint[PATH_MAX];
312
313
int i, fd;
314
315
/*
316
* Perhaps not yet needed, but it may be in the future that there's no
317
* need to check root under some conditions where there is a rootdir.
318
* This also helps alleviate some excessive wrapping later.
319
*/
320
bool checkroot = ctx.pkg_rootdir != NULL;
321
for (fd = -1, i = 0; i < NELEM(abi_files); i++) {
322
if (abi_files[i] == NULL)
323
continue;
324
325
const char *sep = strrchr(abi_files[i], '#');
326
if (sep) {
327
strlcpy(work_abi_file, abi_files[i],
328
MIN(sep - abi_files[i] + 1, sizeof(work_abi_file)));
329
strlcpy(work_arch_hint, sep + 1,
330
sizeof(work_arch_hint));
331
} else {
332
strlcpy(work_abi_file, abi_files[i],
333
sizeof(work_abi_file));
334
work_arch_hint[0] = '\0';
335
}
336
337
/*
338
* Try prepending rootdir and using that if it exists. If
339
* ABI_FILE is specified, assume that the consumer didn't want
340
* it mangled by rootdir.
341
*/
342
if (i > 0 && checkroot &&
343
snprintf(rooted_abi_file, PATH_MAX, "%s/%s",
344
ctx.pkg_rootdir, work_abi_file) < PATH_MAX) {
345
if ((fd = open(rooted_abi_file, O_RDONLY)) >= 0) {
346
strlcpy(work_abi_file, rooted_abi_file,
347
sizeof(work_abi_file));
348
break;
349
}
350
}
351
if ((fd = open(work_abi_file, O_RDONLY)) >= 0) {
352
break;
353
}
354
/* if the ABI_FILE was provided we only care about it */
355
if (i == 0)
356
break;
357
}
358
if (fd == -1) {
359
pkg_emit_error(
360
"Unable to determine the ABI, none of the ABI_FILEs can be read.");
361
return EPKG_FATAL;
362
}
363
364
365
int ret = pkg_elf_abi_from_fd(fd, abi);
366
if (EPKG_OK != ret) {
367
if (-1 == lseek(fd, 0, SEEK_SET)) {
368
pkg_emit_errno("Error seeking file", work_abi_file);
369
ret = EPKG_FATAL;
370
goto close_out;
371
}
372
373
enum pkg_arch arch_hint = PKG_ARCH_UNKNOWN;
374
if (work_arch_hint[0]) {
375
arch_hint = pkg_arch_from_string(PKG_OS_DARWIN, work_arch_hint);
376
if (arch_hint == PKG_ARCH_UNKNOWN) {
377
pkg_emit_error("Invalid ABI_FILE architecture hint %s",
378
work_arch_hint);
379
ret = EPKG_FATAL;
380
goto close_out;
381
}
382
}
383
384
ret = pkg_macho_abi_from_fd(fd, abi, arch_hint);
385
if (EPKG_OK != ret) {
386
pkg_emit_error(
387
"Unable to determine ABI, %s cannot be parsed.",
388
work_abi_file);
389
ret = EPKG_FATAL;
390
goto close_out;
391
}
392
}
393
394
close_out:
395
if (close(fd)) {
396
pkg_emit_errno("Error closing file", work_abi_file);
397
ret = EPKG_FATAL;
398
}
399
return ret;
400
}
401
402
int
403
pkg_arch_to_legacy(const char *arch, char *dest, size_t sz)
404
{
405
int i = 0;
406
struct arch_trans *arch_trans;
407
408
memset(dest, '\0', sz);
409
/* Lower case the OS */
410
while (arch[i] != ':' && arch[i] != '\0') {
411
dest[i] = tolower(arch[i]);
412
i++;
413
}
414
if (arch[i] == '\0')
415
return (0);
416
417
dest[i++] = ':';
418
419
/* Copy the version */
420
while (arch[i] != ':' && arch[i] != '\0') {
421
dest[i] = arch[i];
422
i++;
423
}
424
if (arch[i] == '\0')
425
return (0);
426
427
dest[i++] = ':';
428
429
for (arch_trans = machine_arch_translation; arch_trans->elftype != NULL;
430
arch_trans++) {
431
if (STREQ(arch + i, arch_trans->archid)) {
432
strlcpy(dest + i, arch_trans->elftype,
433
sz - (arch + i - dest));
434
return (0);
435
}
436
}
437
strlcpy(dest + i, arch + i, sz - (arch + i - dest));
438
439
return (0);
440
}
441
442
void
443
pkg_cleanup_shlibs_required(struct pkg *pkg, charv_t *internal_provided)
444
{
445
struct pkg_file *file = NULL;
446
const char *lib;
447
448
vec_foreach(pkg->shlibs_required, i) {
449
const char *s = pkg->shlibs_required.d[i];
450
if (charv_search(&pkg->shlibs_provided, s) != NULL ||
451
charv_search(internal_provided, s) != NULL) {
452
pkg_debug(2,
453
"remove %s from required shlibs as the "
454
"package %s provides this library itself",
455
s, pkg->name);
456
vec_remove_and_free(&pkg->shlibs_required, i, free);
457
i--;
458
continue;
459
}
460
if (match_ucl_lists(s,
461
pkg_config_get("SHLIB_REQUIRE_IGNORE_GLOB"),
462
pkg_config_get("SHLIB_REQUIRE_IGNORE_REGEX"))) {
463
pkg_debug(2,
464
"remove %s from required shlibs for package %s as it "
465
"is matched by SHLIB_REQUIRE_IGNORE_GLOB/REGEX.",
466
s, pkg->name);
467
vec_remove_and_free(&pkg->shlibs_required, i, free);
468
i--;
469
continue;
470
}
471
file = NULL;
472
while (pkg_files(pkg, &file) == EPKG_OK) {
473
if ((lib = strstr(file->path, s)) != NULL &&
474
strlen(lib) == strlen(s) && lib[-1] == '/') {
475
pkg_debug(2,
476
"remove %s from required shlibs as "
477
"the package %s provides this file itself",
478
s, pkg->name);
479
480
vec_remove_and_free(&pkg->shlibs_required, i,
481
free);
482
i--;
483
break;
484
}
485
}
486
}
487
}
488
489
int
490
pkg_analyse_files(struct pkgdb *db __unused, struct pkg *pkg, const char *stage)
491
{
492
struct pkg_file *file = NULL;
493
int ret = EPKG_OK;
494
char fpath[MAXPATHLEN + 1];
495
bool failures = false;
496
497
int (*pkg_analyse_init)(const char *stage);
498
int (*pkg_analyse)(const bool developer_mode, struct pkg *pkg,
499
const char *fpath, char **provided, enum pkg_shlib_flags *flags);
500
int (*pkg_analyse_close)();
501
502
if (0 == strncmp(pkg->abi, "Darwin", 6)) {
503
pkg_analyse_init=pkg_analyse_init_macho;
504
pkg_analyse=pkg_analyse_macho;
505
pkg_analyse_close=pkg_analyse_close_macho;
506
} else {
507
pkg_analyse_init=pkg_analyse_init_elf;
508
pkg_analyse=pkg_analyse_elf;
509
pkg_analyse_close=pkg_analyse_close_elf;
510
}
511
512
if (vec_len(&pkg->shlibs_required) != 0) {
513
vec_free_and_free(&pkg->shlibs_required, free);
514
}
515
516
if (vec_len(&pkg->shlibs_provided) != 0) {
517
vec_free_and_free(&pkg->shlibs_provided, free);
518
}
519
520
ret = pkg_analyse_init(stage);
521
if (ret != EPKG_OK) {
522
goto cleanup;
523
}
524
525
/* Assume no architecture dependence, for contradiction */
526
if (ctx.developer_mode)
527
pkg->flags &= ~(PKG_CONTAINS_ELF_OBJECTS |
528
PKG_CONTAINS_STATIC_LIBS | PKG_CONTAINS_LA);
529
530
/* shlibs that are provided by files in the package but not matched by
531
SHLIB_PROVIDE_PATHS_* are still used to filter the shlibs
532
required by the package */
533
charv_t internal_provided = vec_init();
534
/* list of shlibs that are in the path to be evaluated for provided but are symlinks */
535
charv_t maybe_provided = vec_init();
536
537
while (pkg_files(pkg, &file) == EPKG_OK) {
538
struct stat st;
539
if (stage != NULL)
540
snprintf(fpath, sizeof(fpath), "%s/%s", stage,
541
file->path);
542
else
543
strlcpy(fpath, file->path, sizeof(fpath));
544
545
char *provided = NULL;
546
enum pkg_shlib_flags provided_flags = PKG_SHLIB_FLAGS_NONE;
547
548
ret = pkg_analyse(ctx.developer_mode, pkg, fpath, &provided, &provided_flags);
549
if (EPKG_WARN == ret) {
550
failures = true;
551
}
552
553
if (provided != NULL) {
554
const ucl_object_t *paths = NULL;
555
556
switch (provided_flags) {
557
case PKG_SHLIB_FLAGS_NONE:
558
paths = pkg_config_get("SHLIB_PROVIDE_PATHS_NATIVE");
559
break;
560
case PKG_SHLIB_FLAGS_COMPAT_32:
561
paths = pkg_config_get("SHLIB_PROVIDE_PATHS_COMPAT_32");
562
break;
563
case PKG_SHLIB_FLAGS_COMPAT_LINUX:
564
paths = pkg_config_get("SHLIB_PROVIDE_PATHS_COMPAT_LINUX");
565
break;
566
case (PKG_SHLIB_FLAGS_COMPAT_32 | PKG_SHLIB_FLAGS_COMPAT_LINUX):
567
paths = pkg_config_get("SHLIB_PROVIDE_PATHS_COMPAT_LINUX_32");
568
break;
569
default:
570
assert(0);
571
}
572
assert(paths != NULL);
573
574
if (lstat(fpath, &st) != 0) {
575
pkg_emit_errno("lstat() failed for", fpath);
576
continue;
577
}
578
/* If the corresponding PATHS option isn't set (i.e. an empty ucl array)
579
don't do any filtering for backwards compatibility. */
580
if (ucl_array_size(paths) == 0 || pkg_match_paths_list(paths, file->path)) {
581
lstat(fpath, &st);
582
if (S_ISREG(st.st_mode)) {
583
pkg_addshlib_provided(pkg, provided, provided_flags);
584
} else {
585
vec_push(&maybe_provided, pkg_shlib_name_with_flags(provided, provided_flags));
586
}
587
} else {
588
vec_push(&internal_provided, pkg_shlib_name_with_flags(provided, provided_flags));
589
}
590
free(provided);
591
}
592
}
593
594
vec_foreach(maybe_provided, i) {
595
vec_foreach(internal_provided, j) {
596
if (STREQ(maybe_provided.d[i], internal_provided.d[j])) {
597
pkg_addshlib_provided(pkg, maybe_provided.d[i], PKG_SHLIB_FLAGS_NONE);
598
vec_remove_and_free(&internal_provided, j, free);
599
}
600
}
601
vec_remove_and_free(&maybe_provided, i, free);
602
}
603
vec_free(&maybe_provided);
604
/*
605
* Do not depend on libraries that a package provides itself
606
*/
607
pkg_cleanup_shlibs_required(pkg, &internal_provided);
608
vec_free_and_free(&internal_provided, free);
609
610
vec_foreach(pkg->shlibs_provided, i) {
611
if (match_ucl_lists(pkg->shlibs_provided.d[i],
612
pkg_config_get("SHLIB_PROVIDE_IGNORE_GLOB"),
613
pkg_config_get("SHLIB_PROVIDE_IGNORE_REGEX"))) {
614
pkg_debug(2,
615
"remove %s from provided shlibs for package %s as it "
616
"is matched by SHLIB_PROVIDE_IGNORE_GLOB/REGEX.",
617
pkg->shlibs_provided.d[i], pkg->name);
618
vec_remove_and_free(&pkg->shlibs_provided, i, free);
619
continue;
620
}
621
}
622
623
/*
624
* if the package is not supposed to provide share libraries then
625
* drop the provided one
626
*/
627
if (pkg_kv_get(&pkg->annotations, "no_provide_shlib") != NULL) {
628
vec_free_and_free(&pkg->shlibs_provided, free);
629
}
630
631
if (failures)
632
goto cleanup;
633
634
ret = EPKG_OK;
635
636
cleanup:
637
ret = pkg_analyse_close();
638
639
return (ret);
640
}
641
642