|
4 | 4 | #include <stdio.h> |
5 | 5 | #include <stdlib.h> |
6 | 6 | #include <string.h> |
| 7 | +#include <fcntl.h> |
7 | 8 | #include <unistd.h> |
8 | 9 | #include <errno.h> |
9 | 10 | #include <linux/err.h> |
10 | 11 | #include <linux/btf.h> |
| 12 | +#include <gelf.h> |
11 | 13 | #include "btf.h" |
12 | 14 | #include "bpf.h" |
13 | 15 | #include "libbpf.h" |
@@ -417,6 +419,132 @@ struct btf *btf__new(__u8 *data, __u32 size) |
417 | 419 | return btf; |
418 | 420 | } |
419 | 421 |
|
| 422 | +static bool btf_check_endianness(const GElf_Ehdr *ehdr) |
| 423 | +{ |
| 424 | +#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ |
| 425 | + return ehdr->e_ident[EI_DATA] == ELFDATA2LSB; |
| 426 | +#elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ |
| 427 | + return ehdr->e_ident[EI_DATA] == ELFDATA2MSB; |
| 428 | +#else |
| 429 | +# error "Unrecognized __BYTE_ORDER__" |
| 430 | +#endif |
| 431 | +} |
| 432 | + |
| 433 | +struct btf *btf__parse_elf(const char *path, struct btf_ext **btf_ext) |
| 434 | +{ |
| 435 | + Elf_Data *btf_data = NULL, *btf_ext_data = NULL; |
| 436 | + int err = 0, fd = -1, idx = 0; |
| 437 | + struct btf *btf = NULL; |
| 438 | + Elf_Scn *scn = NULL; |
| 439 | + Elf *elf = NULL; |
| 440 | + GElf_Ehdr ehdr; |
| 441 | + |
| 442 | + if (elf_version(EV_CURRENT) == EV_NONE) { |
| 443 | + pr_warning("failed to init libelf for %s\n", path); |
| 444 | + return ERR_PTR(-LIBBPF_ERRNO__LIBELF); |
| 445 | + } |
| 446 | + |
| 447 | + fd = open(path, O_RDONLY); |
| 448 | + if (fd < 0) { |
| 449 | + err = -errno; |
| 450 | + pr_warning("failed to open %s: %s\n", path, strerror(errno)); |
| 451 | + return ERR_PTR(err); |
| 452 | + } |
| 453 | + |
| 454 | + err = -LIBBPF_ERRNO__FORMAT; |
| 455 | + |
| 456 | + elf = elf_begin(fd, ELF_C_READ, NULL); |
| 457 | + if (!elf) { |
| 458 | + pr_warning("failed to open %s as ELF file\n", path); |
| 459 | + goto done; |
| 460 | + } |
| 461 | + if (!gelf_getehdr(elf, &ehdr)) { |
| 462 | + pr_warning("failed to get EHDR from %s\n", path); |
| 463 | + goto done; |
| 464 | + } |
| 465 | + if (!btf_check_endianness(&ehdr)) { |
| 466 | + pr_warning("non-native ELF endianness is not supported\n"); |
| 467 | + goto done; |
| 468 | + } |
| 469 | + if (!elf_rawdata(elf_getscn(elf, ehdr.e_shstrndx), NULL)) { |
| 470 | + pr_warning("failed to get e_shstrndx from %s\n", path); |
| 471 | + goto done; |
| 472 | + } |
| 473 | + |
| 474 | + while ((scn = elf_nextscn(elf, scn)) != NULL) { |
| 475 | + GElf_Shdr sh; |
| 476 | + char *name; |
| 477 | + |
| 478 | + idx++; |
| 479 | + if (gelf_getshdr(scn, &sh) != &sh) { |
| 480 | + pr_warning("failed to get section(%d) header from %s\n", |
| 481 | + idx, path); |
| 482 | + goto done; |
| 483 | + } |
| 484 | + name = elf_strptr(elf, ehdr.e_shstrndx, sh.sh_name); |
| 485 | + if (!name) { |
| 486 | + pr_warning("failed to get section(%d) name from %s\n", |
| 487 | + idx, path); |
| 488 | + goto done; |
| 489 | + } |
| 490 | + if (strcmp(name, BTF_ELF_SEC) == 0) { |
| 491 | + btf_data = elf_getdata(scn, 0); |
| 492 | + if (!btf_data) { |
| 493 | + pr_warning("failed to get section(%d, %s) data from %s\n", |
| 494 | + idx, name, path); |
| 495 | + goto done; |
| 496 | + } |
| 497 | + continue; |
| 498 | + } else if (btf_ext && strcmp(name, BTF_EXT_ELF_SEC) == 0) { |
| 499 | + btf_ext_data = elf_getdata(scn, 0); |
| 500 | + if (!btf_ext_data) { |
| 501 | + pr_warning("failed to get section(%d, %s) data from %s\n", |
| 502 | + idx, name, path); |
| 503 | + goto done; |
| 504 | + } |
| 505 | + continue; |
| 506 | + } |
| 507 | + } |
| 508 | + |
| 509 | + err = 0; |
| 510 | + |
| 511 | + if (!btf_data) { |
| 512 | + err = -ENOENT; |
| 513 | + goto done; |
| 514 | + } |
| 515 | + btf = btf__new(btf_data->d_buf, btf_data->d_size); |
| 516 | + if (IS_ERR(btf)) |
| 517 | + goto done; |
| 518 | + |
| 519 | + if (btf_ext && btf_ext_data) { |
| 520 | + *btf_ext = btf_ext__new(btf_ext_data->d_buf, |
| 521 | + btf_ext_data->d_size); |
| 522 | + if (IS_ERR(*btf_ext)) |
| 523 | + goto done; |
| 524 | + } else if (btf_ext) { |
| 525 | + *btf_ext = NULL; |
| 526 | + } |
| 527 | +done: |
| 528 | + if (elf) |
| 529 | + elf_end(elf); |
| 530 | + close(fd); |
| 531 | + |
| 532 | + if (err) |
| 533 | + return ERR_PTR(err); |
| 534 | + /* |
| 535 | + * btf is always parsed before btf_ext, so no need to clean up |
| 536 | + * btf_ext, if btf loading failed |
| 537 | + */ |
| 538 | + if (IS_ERR(btf)) |
| 539 | + return btf; |
| 540 | + if (btf_ext && IS_ERR(*btf_ext)) { |
| 541 | + btf__free(btf); |
| 542 | + err = PTR_ERR(*btf_ext); |
| 543 | + return ERR_PTR(err); |
| 544 | + } |
| 545 | + return btf; |
| 546 | +} |
| 547 | + |
420 | 548 | static int compare_vsi_off(const void *_a, const void *_b) |
421 | 549 | { |
422 | 550 | const struct btf_var_secinfo *a = _a; |
|
0 commit comments