Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/pkg
Path: blob/main/libpkg/pkg_elf.c
2065 views
1
/*-
2
* Copyright (c) 2011-2024 Baptiste Daroussin <[email protected]>
3
* Copyright (c) 2012-2013 Matthew Seaman <[email protected]>
4
*
5
* SPDX-License-Identifier: BSD-2-Clause
6
*/
7
8
#ifdef HAVE_CONFIG_H
9
#include "pkg_config.h"
10
#endif
11
12
#ifdef HAVE_SYS_ENDIAN_H
13
#include <sys/endian.h>
14
#elif HAVE_ENDIAN_H
15
#include <endian.h>
16
#elif HAVE_MACHINE_ENDIAN_H
17
#include <machine/endian.h>
18
#endif
19
#include <sys/types.h>
20
#if defined(HAVE_SYS_ELF_COMMON_H) && !defined(__DragonFly__)
21
#include <sys/elf_common.h>
22
#endif
23
#include <sys/stat.h>
24
25
#include <assert.h>
26
#include <dlfcn.h>
27
#include <fcntl.h>
28
#include <gelf.h>
29
#include <libgen.h>
30
#if defined(HAVE_LINK_H) && !defined(__DragonFly__) && defined(HAVE_LIBELF)
31
#include <link.h>
32
#endif
33
#include <paths.h>
34
#include <stdbool.h>
35
#include <string.h>
36
#include <unistd.h>
37
#ifdef HAVE_LIBELF
38
#include <libelf.h>
39
#endif
40
41
#include <bsd_compat.h>
42
43
#include "pkg.h"
44
#include "private/pkg.h"
45
#include "private/pkg_abi.h"
46
#include "private/event.h"
47
#include "private/binfmt.h"
48
49
#ifndef NT_ABI_TAG
50
#define NT_ABI_TAG 1
51
#endif
52
53
#define NT_VERSION 1
54
#define NT_ARCH 2
55
#define NT_GNU_ABI_TAG 1
56
57
#ifndef roundup2
58
#define roundup2(x, y) (((x)+((y)-1))&(~((y)-1))) /* if y is powers of two */
59
#endif
60
61
static void elf_parse_abi(Elf *elf, GElf_Ehdr *ehdr, struct pkg_abi *abi);
62
63
#ifndef HAVE_ELF_NOTE
64
typedef Elf32_Nhdr Elf_Note;
65
#endif
66
67
static int
68
analyse_elf(struct pkg *pkg, const char *fpath, char **provided,
69
enum pkg_shlib_flags *provided_flags)
70
{
71
assert(*provided == NULL);
72
assert(*provided_flags == PKG_SHLIB_FLAGS_NONE);
73
74
int ret = EPKG_OK;
75
76
pkg_debug(1, "analysing elf %s", fpath);
77
78
struct stat sb;
79
if (lstat(fpath, &sb) != 0)
80
pkg_emit_errno("fstat() failed for", fpath);
81
/* ignore empty files */
82
if (sb.st_size == 0)
83
return (EPKG_END); /* Empty file or sym-link: no results */
84
85
int fd = open(fpath, O_RDONLY, 0);
86
if (fd < 0) {
87
return (EPKG_FATAL);
88
}
89
90
if (elf_version(EV_CURRENT) == EV_NONE) {
91
pkg_emit_error("ELF library initialization failed: %s",
92
elf_errmsg(-1));
93
return (EPKG_FATAL);
94
}
95
96
Elf *e = elf_begin(fd, ELF_C_READ, NULL);
97
if (e == NULL) {
98
ret = EPKG_FATAL;
99
pkg_debug(1, "elf_begin() for %s failed: %s", fpath,
100
elf_errmsg(-1));
101
goto cleanup;
102
}
103
104
if (elf_kind(e) != ELF_K_ELF) {
105
/* Not an elf file: no results */
106
ret = EPKG_END;
107
pkg_debug(1, "not an elf");
108
goto cleanup;
109
}
110
111
if (ctx.developer_mode)
112
pkg->flags |= PKG_CONTAINS_ELF_OBJECTS;
113
114
GElf_Ehdr elfhdr;
115
if (gelf_getehdr(e, &elfhdr) == NULL) {
116
ret = EPKG_WARN;
117
pkg_debug(1, "getehdr() failed: %s.", elf_errmsg(-1));
118
goto cleanup;
119
}
120
121
if (elfhdr.e_type != ET_DYN && elfhdr.e_type != ET_EXEC &&
122
elfhdr.e_type != ET_REL) {
123
pkg_debug(1, "not an elf");
124
ret = EPKG_END;
125
goto cleanup;
126
}
127
128
/* Parse the needed information from the dynamic section header */
129
Elf_Scn *scn = NULL;
130
Elf_Scn *dynamic = NULL;
131
size_t numdyn = 0;
132
size_t sh_link = 0;
133
while ((scn = elf_nextscn(e, scn)) != NULL) {
134
GElf_Shdr shdr;
135
if (gelf_getshdr(scn, &shdr) != &shdr) {
136
ret = EPKG_FATAL;
137
pkg_emit_error("getshdr() for %s failed: %s", fpath,
138
elf_errmsg(-1));
139
goto cleanup;
140
}
141
if (shdr.sh_type == SHT_DYNAMIC) {
142
dynamic = scn;
143
sh_link = shdr.sh_link;
144
if (shdr.sh_entsize == 0) {
145
ret = EPKG_END;
146
goto cleanup;
147
}
148
numdyn = shdr.sh_size / shdr.sh_entsize;
149
break;
150
}
151
}
152
153
if (dynamic == NULL) {
154
ret = EPKG_END;
155
goto cleanup; /* not a dynamically linked elf: no results */
156
}
157
158
struct pkg_abi elf_abi;
159
elf_parse_abi(e, &elfhdr, &elf_abi);
160
if (elf_abi.arch == PKG_ARCH_UNKNOWN) {
161
ret = EPKG_END;
162
goto cleanup;
163
}
164
165
if (elf_abi.os == PKG_OS_UNKNOWN) {
166
/* It is necessary to fall back to checking the ELF header if elf_parse_abi()
167
* was not able to determine the OS due to missing ELF notes. However, we
168
* only do this fallback when analyzing for shlibs rather than directly in
169
* elf_parse_abi() because we cannot determine the version without ELF notes.
170
* Since we do not need to check the osversion when analyzing shlibs, the
171
* fallback is fine here.
172
*/
173
if (elfhdr.e_ident[EI_OSABI] == ELFOSABI_FREEBSD) {
174
elf_abi.os = PKG_OS_FREEBSD;
175
} else if (ctx.abi.os == PKG_OS_LINUX || ctx.abi.os == PKG_OS_FREEBSD) {
176
/* There is no reliable way to identify shared libraries targeting Linux.
177
* It would be possible to reliably identify Linux executables by checking
178
* the dynamic linker path in DT_INTERP. Shared libraries however do not
179
* have DT_INTERP set.
180
*
181
* Reading the notes section for NT_GNU_ABI_TAG is not sufficient either
182
* as this is only required for executables, not shared libraries.
183
* See https://refspecs.linuxfoundation.org/LSB_1.2.0/gLSB/noteabitag.html
184
*
185
* Therefore, if pkg is targeting Linux assume that ELF files with unknown
186
* target OS are also targeting Linux.
187
*
188
* Furthermore, if pkg is targeting FreeBSD also assume that ELF
189
* files with unknown target OS are targeting Linux. This is consistent
190
* with the behavior of the FreeBSD kernel, which falls back to Linux by
191
* default if it is unable to determine the target OS of an ELF file.
192
* (This behavior can be overridden with a fallback_brand sysctl.)
193
*
194
* We could add a pkg option to configure the fallback OS
195
* in the future if necessary.
196
*/
197
elf_abi.os = PKG_OS_LINUX;
198
} else {
199
ret = EPKG_END;
200
goto cleanup;
201
}
202
}
203
204
enum pkg_shlib_flags flags = pkg_shlib_flags_from_abi(&elf_abi);
205
if ((flags & PKG_SHLIB_FLAGS_COMPAT_LINUX) == 0 && elf_abi.os != ctx.abi.os) {
206
ret = EPKG_END;
207
goto cleanup; /* Incompatible OS */
208
}
209
if ((flags & PKG_SHLIB_FLAGS_COMPAT_32) == 0 && elf_abi.arch != ctx.abi.arch) {
210
ret = EPKG_END;
211
goto cleanup; /* Incompatible architecture */
212
}
213
214
Elf_Data *data = elf_getdata(dynamic, NULL);
215
if (data == NULL) {
216
ret = EPKG_END; /* Some error occurred, ignore this file */
217
goto cleanup;
218
}
219
220
for (size_t dynidx = 0; dynidx < numdyn; dynidx++) {
221
GElf_Dyn *dyn, dyn_mem;
222
if ((dyn = gelf_getdyn(data, dynidx, &dyn_mem)) == NULL) {
223
ret = EPKG_FATAL;
224
pkg_emit_error("getdyn() failed for %s: %s", fpath,
225
elf_errmsg(-1));
226
goto cleanup;
227
}
228
229
const char *shlib = elf_strptr(e, sh_link, dyn->d_un.d_val);
230
if (shlib == NULL || *shlib == '\0') {
231
continue;
232
}
233
234
if (strncmp(shlib, "lib", 3) != 0) {
235
continue;
236
}
237
238
if (dyn->d_tag == DT_SONAME) {
239
if (*provided != NULL) {
240
pkg_emit_error("malformed ELF file %s has "
241
"multiple DT_SONAME entries", fpath);
242
goto cleanup;
243
}
244
*provided = xstrdup(shlib);
245
*provided_flags = flags;
246
} else if (dyn->d_tag == DT_NEEDED) {
247
/*
248
* some packages record fullpath to a lib
249
* neovim is an example, skip them for now
250
*/
251
if (*shlib == '/')
252
continue;
253
pkg_addshlib_required(pkg, shlib, flags);
254
}
255
}
256
257
cleanup:
258
if (e != NULL)
259
elf_end(e);
260
close(fd);
261
262
return (ret);
263
}
264
265
static int
266
analyse_fpath(struct pkg *pkg, const char *fpath)
267
{
268
const char *dot;
269
270
dot = strrchr(fpath, '.');
271
272
if (dot == NULL) /* No extension */
273
return (EPKG_OK);
274
275
if (dot[1] == 'a' && dot[2] == '\0')
276
pkg->flags |= PKG_CONTAINS_STATIC_LIBS;
277
278
if ((dot[1] == 'l' && dot[2] == 'a' && dot[3] == '\0'))
279
pkg->flags |= PKG_CONTAINS_LA;
280
281
return (EPKG_OK);
282
}
283
284
static enum pkg_arch
285
aeabi_parse_arm_attributes(void *data, size_t length)
286
{
287
uint32_t sect_len;
288
uint8_t *section = data;
289
290
#define MOVE(len) do { \
291
assert(length >= (len)); \
292
section += (len); \
293
length -= (len); \
294
} while (0)
295
296
if (length == 0 || *section != 'A')
297
return (PKG_ARCH_UNKNOWN);
298
MOVE(1);
299
300
/* Read the section length */
301
if (length < sizeof(sect_len))
302
return (PKG_ARCH_UNKNOWN);
303
memcpy(&sect_len, section, sizeof(sect_len));
304
305
/*
306
* The section length should be no longer than the section it is within
307
*/
308
if (sect_len > length)
309
return (PKG_ARCH_UNKNOWN);
310
311
MOVE(sizeof(sect_len));
312
313
/* Skip the vendor name */
314
while (length != 0) {
315
if (*section == '\0')
316
break;
317
MOVE(1);
318
}
319
if (length == 0)
320
return (PKG_ARCH_UNKNOWN);
321
MOVE(1);
322
323
while (length != 0) {
324
uint32_t tag_length;
325
326
switch(*section) {
327
case 1: /* Tag_File */
328
MOVE(1);
329
if (length < sizeof(tag_length))
330
return (PKG_ARCH_UNKNOWN);
331
memcpy(&tag_length, section, sizeof(tag_length));
332
break;
333
case 2: /* Tag_Section */
334
case 3: /* Tag_Symbol */
335
default:
336
return (PKG_ARCH_UNKNOWN);
337
}
338
/* At least space for the tag and size */
339
if (tag_length <= 5)
340
return (PKG_ARCH_UNKNOWN);
341
tag_length--;
342
/* Check the tag fits */
343
if (tag_length > length)
344
return (PKG_ARCH_UNKNOWN);
345
346
#define MOVE_TAG(len) do { \
347
assert(tag_length >= (len)); \
348
MOVE(len); \
349
tag_length -= (len); \
350
} while(0)
351
352
MOVE(sizeof(tag_length));
353
tag_length -= sizeof(tag_length);
354
355
while (tag_length != 0) {
356
uint8_t tag;
357
358
assert(tag_length >= length);
359
360
tag = *section;
361
MOVE_TAG(1);
362
363
/*
364
* These tag values come from:
365
*
366
* Addenda to, and Errata in, the ABI for the
367
* ARM Architecture. Release 2.08, section 2.3.
368
*/
369
if (tag == 6) { /* == Tag_CPU_arch */
370
uint8_t val;
371
372
val = *section;
373
/*
374
* We don't support values that require
375
* more than one byte.
376
*/
377
if (val & (1 << 7))
378
return (PKG_ARCH_UNKNOWN);
379
380
/* We have an ARMv4 or ARMv5 */
381
if (val <= 5)
382
return (PKG_ARCH_UNKNOWN);
383
else if (val == 6) /* We have an ARMv6 */
384
return (PKG_ARCH_ARMV6);
385
else /* We have an ARMv7+ */
386
return (PKG_ARCH_ARMV7);
387
} else if (tag == 4 || tag == 5 || tag == 32 ||
388
tag == 65 || tag == 67) {
389
while (*section != '\0' && length != 0)
390
MOVE_TAG(1);
391
if (tag_length == 0)
392
return (PKG_ARCH_UNKNOWN);
393
/* Skip the last byte */
394
MOVE_TAG(1);
395
} else if ((tag >= 7 && tag <= 31) || tag == 34 ||
396
tag == 36 || tag == 38 || tag == 42 || tag == 44 ||
397
tag == 64 || tag == 66 || tag == 68 || tag == 70) {
398
/* Skip the uleb128 data */
399
while (*section & (1 << 7) && length != 0)
400
MOVE_TAG(1);
401
if (tag_length == 0)
402
return (PKG_ARCH_UNKNOWN);
403
/* Skip the last byte */
404
MOVE_TAG(1);
405
} else
406
return (PKG_ARCH_UNKNOWN);
407
#undef MOVE_TAG
408
}
409
410
break;
411
}
412
return (PKG_ARCH_UNKNOWN);
413
#undef MOVE
414
}
415
416
static enum pkg_arch
417
elf_parse_arch(Elf *elf, GElf_Ehdr *ehdr)
418
{
419
switch (ehdr->e_machine) {
420
case EM_386:
421
return (PKG_ARCH_I386);
422
case EM_X86_64:
423
return (PKG_ARCH_AMD64);
424
case EM_AARCH64:
425
return (PKG_ARCH_AARCH64);
426
case EM_ARM:
427
/* Only support EABI */
428
if ((ehdr->e_flags & EF_ARM_EABIMASK) == 0) {
429
return (PKG_ARCH_UNKNOWN);
430
}
431
432
size_t shstrndx;
433
elf_getshdrstrndx(elf, &shstrndx);
434
435
GElf_Shdr shdr;
436
Elf_Scn *scn = NULL;
437
while ((scn = elf_nextscn(elf, scn)) != NULL) {
438
if (gelf_getshdr(scn, &shdr) != &shdr) {
439
break;
440
}
441
const char *sh_name = elf_strptr(elf, shstrndx, shdr.sh_name);
442
if (sh_name == NULL) {
443
continue;
444
}
445
if (STREQ(".ARM.attributes", sh_name)) {
446
Elf_Data *data = elf_getdata(scn, NULL);
447
return (aeabi_parse_arm_attributes(data->d_buf, data->d_size));
448
}
449
}
450
break;
451
case EM_PPC:
452
return (PKG_ARCH_POWERPC);
453
case EM_PPC64:
454
switch (ehdr->e_ident[EI_DATA]) {
455
case ELFDATA2MSB:
456
return (PKG_ARCH_POWERPC64);
457
case ELFDATA2LSB:
458
return (PKG_ARCH_POWERPC64LE);
459
}
460
break;
461
case EM_RISCV:
462
switch (ehdr->e_ident[EI_CLASS]) {
463
case ELFCLASS32:
464
return (PKG_ARCH_RISCV32);
465
case ELFCLASS64:
466
return (PKG_ARCH_RISCV64);
467
}
468
break;
469
}
470
471
return (PKG_ARCH_UNKNOWN);
472
}
473
474
/* Returns true if the OS and version were successfully parsed */
475
static bool
476
elf_note_analyse(Elf_Data *data, GElf_Ehdr *elfhdr, struct pkg_abi *abi)
477
{
478
Elf_Note note;
479
char *src;
480
uint32_t gnu_abi_tag[4];
481
int note_ost[6] = {
482
PKG_OS_LINUX,
483
PKG_OS_UNKNOWN, /* GNU Hurd */
484
PKG_OS_UNKNOWN, /* Solaris */
485
PKG_OS_FREEBSD,
486
PKG_OS_NETBSD,
487
PKG_OS_UNKNOWN, /* Syllable */
488
};
489
uint32_t version = 0;
490
int version_style = 1;
491
492
src = data->d_buf;
493
494
while ((uintptr_t)src < ((uintptr_t)data->d_buf + data->d_size)) {
495
memcpy(&note, src, sizeof(Elf_Note));
496
src += sizeof(Elf_Note);
497
if ((strncmp ((const char *) src, "FreeBSD", note.n_namesz) == 0) ||
498
(strncmp ((const char *) src, "DragonFly", note.n_namesz) == 0) ||
499
(strncmp ((const char *) src, "NetBSD", note.n_namesz) == 0) ||
500
(note.n_namesz == 0)) {
501
if (note.n_type == NT_VERSION) {
502
version_style = 1;
503
break;
504
}
505
}
506
if (strncmp ((const char *) src, "GNU", note.n_namesz) == 0) {
507
if (note.n_type == NT_GNU_ABI_TAG) {
508
version_style = 2;
509
break;
510
}
511
}
512
src += roundup2(note.n_namesz + note.n_descsz, 4);
513
}
514
if ((uintptr_t)src >= ((uintptr_t)data->d_buf + data->d_size)) {
515
return (false);
516
}
517
if (version_style == 2) {
518
/*
519
* NT_GNU_ABI_TAG
520
* Operating system (OS) ABI information. The
521
* desc field contains 4 words:
522
* word 0: OS descriptor (ELF_NOTE_OS_LINUX, ELF_NOTE_OS_GNU, etc)
523
* word 1: major version of the ABI
524
* word 2: minor version of the ABI
525
* word 3: subminor version of the ABI
526
*/
527
src += roundup2(note.n_namesz, 4);
528
if (elfhdr->e_ident[EI_DATA] == ELFDATA2MSB) {
529
for (int wdndx = 0; wdndx < 4; wdndx++) {
530
gnu_abi_tag[wdndx] = be32dec(src);
531
src += 4;
532
}
533
} else {
534
for (int wdndx = 0; wdndx < 4; wdndx++) {
535
gnu_abi_tag[wdndx] = le32dec(src);
536
src += 4;
537
}
538
}
539
if (gnu_abi_tag[0] < 6) {
540
abi->os= note_ost[gnu_abi_tag[0]];
541
} else {
542
abi->os = PKG_OS_UNKNOWN;
543
}
544
} else {
545
if (note.n_namesz == 0) {
546
abi->os = PKG_OS_UNKNOWN;
547
} else {
548
if (STREQ(src, "FreeBSD"))
549
abi->os = PKG_OS_FREEBSD;
550
else if (STREQ(src, "DragonFly"))
551
abi->os = PKG_OS_DRAGONFLY;
552
else if (STREQ(src, "NetBSD"))
553
abi->os = PKG_OS_NETBSD;
554
}
555
src += roundup2(note.n_namesz, 4);
556
if (elfhdr->e_ident[EI_DATA] == ELFDATA2MSB)
557
version = be32dec(src);
558
else
559
version = le32dec(src);
560
}
561
562
if (version_style == 2) {
563
if (abi->os == PKG_OS_LINUX) {
564
abi->major = gnu_abi_tag[1];
565
abi->minor = gnu_abi_tag[2];
566
} else {
567
abi->major = gnu_abi_tag[1];
568
abi->minor = gnu_abi_tag[2];
569
abi->patch = gnu_abi_tag[3];
570
}
571
} else {
572
switch (abi->os) {
573
case PKG_OS_UNKNOWN:
574
break;
575
case PKG_OS_FREEBSD:
576
pkg_abi_set_freebsd_osversion(abi, version);
577
break;
578
case PKG_OS_DRAGONFLY:
579
abi->major = version / 100000;
580
abi->minor = (((version / 100 % 1000)+1)/2)*2;
581
break;
582
case PKG_OS_NETBSD:
583
abi->major = (version + 1000000) / 100000000;
584
break;
585
default:
586
assert(0);
587
}
588
}
589
590
return (true);
591
}
592
593
static void
594
elf_parse_abi(Elf *elf, GElf_Ehdr *ehdr, struct pkg_abi *abi)
595
{
596
*abi = (struct pkg_abi){0};
597
598
Elf_Scn *scn = NULL;
599
while ((scn = elf_nextscn(elf, scn)) != NULL) {
600
GElf_Shdr shdr;
601
if (gelf_getshdr(scn, &shdr) != &shdr) {
602
pkg_emit_error("getshdr() failed: %s.", elf_errmsg(-1));
603
return;
604
}
605
606
if (shdr.sh_type == SHT_NOTE) {
607
Elf_Data *data = elf_getdata(scn, NULL);
608
/*
609
* loop over all the note section and override what
610
* should be overridden if any
611
*/
612
if (data == NULL)
613
continue;
614
elf_note_analyse(data, ehdr, abi);
615
}
616
}
617
618
abi->arch = elf_parse_arch(elf, ehdr);
619
}
620
621
int
622
pkg_elf_abi_from_fd(int fd, struct pkg_abi *abi)
623
{
624
Elf *elf = NULL;
625
GElf_Ehdr elfhdr;
626
int ret = EPKG_OK;
627
628
if (elf_version(EV_CURRENT) == EV_NONE) {
629
pkg_emit_error("ELF library initialization failed: %s",
630
elf_errmsg(-1));
631
return (EPKG_FATAL);
632
}
633
634
if ((elf = elf_begin(fd, ELF_C_READ, NULL)) == NULL) {
635
ret = EPKG_FATAL;
636
pkg_emit_error("elf_begin() failed: %s.", elf_errmsg(-1));
637
goto cleanup;
638
}
639
640
if (gelf_getehdr(elf, &elfhdr) == NULL) {
641
ret = EPKG_WARN;
642
pkg_debug(1, "getehdr() failed: %s.", elf_errmsg(-1));
643
goto cleanup;
644
}
645
646
elf_parse_abi(elf, &elfhdr, abi);
647
648
if (abi->os == PKG_OS_UNKNOWN) {
649
ret = EPKG_FATAL;
650
pkg_emit_error("failed to determine the operating system");
651
goto cleanup;
652
}
653
654
if (abi->arch == PKG_ARCH_UNKNOWN) {
655
ret = EPKG_FATAL;
656
pkg_emit_error("failed to determine the architecture");
657
goto cleanup;
658
}
659
660
cleanup:
661
if (elf != NULL)
662
elf_end(elf);
663
return (ret);
664
}
665
666
int pkg_analyse_init_elf(__unused const char* stage) {
667
if (elf_version(EV_CURRENT) == EV_NONE)
668
return (EPKG_FATAL);
669
return (EPKG_OK);
670
}
671
672
int pkg_analyse_elf(const bool developer_mode, struct pkg *pkg,
673
const char *fpath, char **provided, enum pkg_shlib_flags *provided_flags)
674
{
675
assert(*provided == NULL);
676
assert(*provided_flags == PKG_SHLIB_FLAGS_NONE);
677
678
int ret = analyse_elf(pkg, fpath, provided, provided_flags);
679
if (developer_mode) {
680
if (ret != EPKG_OK && ret != EPKG_END) {
681
return EPKG_WARN;
682
}
683
analyse_fpath(pkg, fpath);
684
}
685
return ret;
686
}
687
688
int pkg_analyse_close_elf() {
689
return EPKG_OK;
690
}
691
692