Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

Merge branch 'libbpf-extend-linker-api-to-support-in-memory-elf-files'

Alastair Robertson says:

====================
libbpf: Extend linker API to support in-memory ELF files

This gives API consumers the option of using anonymous files/memfds to
avoid writing temporary ELFs to disk, which will be useful for performing
linking as part of bpftrace's JIT compilation.

v3:
- Removed "filename" option. Now always generate our own filename for
passed-in FDs and buffers.
- Use a common function (bpf_linker_add_file) for shared
implementation of bpf_linker__add_file, bpf_linker__add_fd and
bpf_linker__add_buf.
====================

Link: https://patch.msgid.link/20241211164030.573042-1-ajor@meta.com
Signed-off-by: Andrii Nakryiko <andrii@kernel.org>

+182 -53
+5
tools/lib/bpf/libbpf.h
··· 1796 1796 struct bpf_linker; 1797 1797 1798 1798 LIBBPF_API struct bpf_linker *bpf_linker__new(const char *filename, struct bpf_linker_opts *opts); 1799 + LIBBPF_API struct bpf_linker *bpf_linker__new_fd(int fd, struct bpf_linker_opts *opts); 1799 1800 LIBBPF_API int bpf_linker__add_file(struct bpf_linker *linker, 1800 1801 const char *filename, 1801 1802 const struct bpf_linker_file_opts *opts); 1803 + LIBBPF_API int bpf_linker__add_fd(struct bpf_linker *linker, int fd, 1804 + const struct bpf_linker_file_opts *opts); 1805 + LIBBPF_API int bpf_linker__add_buf(struct bpf_linker *linker, void *buf, size_t buf_sz, 1806 + const struct bpf_linker_file_opts *opts); 1802 1807 LIBBPF_API int bpf_linker__finalize(struct bpf_linker *linker); 1803 1808 LIBBPF_API void bpf_linker__free(struct bpf_linker *linker); 1804 1809
+4
tools/lib/bpf/libbpf.map
··· 432 432 } LIBBPF_1.4.0; 433 433 434 434 LIBBPF_1.6.0 { 435 + global: 436 + bpf_linker__add_buf; 437 + bpf_linker__add_fd; 438 + bpf_linker__new_fd; 435 439 } LIBBPF_1.5.0;
+173 -53
tools/lib/bpf/linker.c
··· 4 4 * 5 5 * Copyright (c) 2021 Facebook 6 6 */ 7 + #ifndef _GNU_SOURCE 8 + #define _GNU_SOURCE 9 + #endif 10 + 7 11 #include <stdbool.h> 8 12 #include <stddef.h> 9 13 #include <stdio.h> ··· 20 16 #include <elf.h> 21 17 #include <libelf.h> 22 18 #include <fcntl.h> 19 + #include <sys/mman.h> 23 20 #include "libbpf.h" 24 21 #include "btf.h" 25 22 #include "libbpf_internal.h" ··· 157 152 /* global (including extern) ELF symbols */ 158 153 int glob_sym_cnt; 159 154 struct glob_sym *glob_syms; 155 + 156 + bool fd_is_owned; 160 157 }; 161 158 162 159 #define pr_warn_elf(fmt, ...) \ 163 160 libbpf_print(LIBBPF_WARN, "libbpf: " fmt ": %s\n", ##__VA_ARGS__, elf_errmsg(-1)) 164 161 165 - static int init_output_elf(struct bpf_linker *linker, const char *file); 162 + static int init_output_elf(struct bpf_linker *linker); 166 163 167 - static int linker_load_obj_file(struct bpf_linker *linker, const char *filename, 168 - const struct bpf_linker_file_opts *opts, 164 + static int bpf_linker_add_file(struct bpf_linker *linker, int fd, 165 + const char *filename); 166 + 167 + static int linker_load_obj_file(struct bpf_linker *linker, 169 168 struct src_obj *obj); 170 169 static int linker_sanity_check_elf(struct src_obj *obj); 171 170 static int linker_sanity_check_elf_symtab(struct src_obj *obj, struct src_sec *sec); ··· 200 191 if (linker->elf) 201 192 elf_end(linker->elf); 202 193 203 - if (linker->fd >= 0) 194 + if (linker->fd >= 0 && linker->fd_is_owned) 204 195 close(linker->fd); 205 196 206 197 strset__free(linker->strtab_strs); ··· 242 233 if (!linker) 243 234 return errno = ENOMEM, NULL; 244 235 245 - linker->fd = -1; 236 + linker->filename = strdup(filename); 237 + if (!linker->filename) { 238 + err = -ENOMEM; 239 + goto err_out; 240 + } 246 241 247 - err = init_output_elf(linker, filename); 242 + linker->fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, 0644); 243 + if (linker->fd < 0) { 244 + err = -errno; 245 + pr_warn("failed to create '%s': %d\n", filename, err); 246 + goto err_out; 247 + } 248 + linker->fd_is_owned = true; 249 + 250 + err = init_output_elf(linker); 251 + if (err) 252 + goto err_out; 253 + 254 + return linker; 255 + 256 + err_out: 257 + bpf_linker__free(linker); 258 + return errno = -err, NULL; 259 + } 260 + 261 + struct bpf_linker *bpf_linker__new_fd(int fd, struct bpf_linker_opts *opts) 262 + { 263 + struct bpf_linker *linker; 264 + char filename[32]; 265 + int err; 266 + 267 + if (fd < 0) 268 + return errno = EINVAL, NULL; 269 + 270 + if (!OPTS_VALID(opts, bpf_linker_opts)) 271 + return errno = EINVAL, NULL; 272 + 273 + if (elf_version(EV_CURRENT) == EV_NONE) { 274 + pr_warn_elf("libelf initialization failed"); 275 + return errno = EINVAL, NULL; 276 + } 277 + 278 + linker = calloc(1, sizeof(*linker)); 279 + if (!linker) 280 + return errno = ENOMEM, NULL; 281 + 282 + snprintf(filename, sizeof(filename), "fd:%d", fd); 283 + linker->filename = strdup(filename); 284 + if (!linker->filename) { 285 + err = -ENOMEM; 286 + goto err_out; 287 + } 288 + 289 + linker->fd = fd; 290 + linker->fd_is_owned = false; 291 + 292 + err = init_output_elf(linker); 248 293 if (err) 249 294 goto err_out; 250 295 ··· 357 294 return sym; 358 295 } 359 296 360 - static int init_output_elf(struct bpf_linker *linker, const char *file) 297 + static int init_output_elf(struct bpf_linker *linker) 361 298 { 362 299 int err, str_off; 363 300 Elf64_Sym *init_sym; 364 301 struct dst_sec *sec; 365 - 366 - linker->filename = strdup(file); 367 - if (!linker->filename) 368 - return -ENOMEM; 369 - 370 - linker->fd = open(file, O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, 0644); 371 - if (linker->fd < 0) { 372 - err = -errno; 373 - pr_warn("failed to create '%s': %s\n", file, errstr(err)); 374 - return err; 375 - } 376 302 377 303 linker->elf = elf_begin(linker->fd, ELF_C_WRITE, NULL); 378 304 if (!linker->elf) { ··· 488 436 return 0; 489 437 } 490 438 491 - int bpf_linker__add_file(struct bpf_linker *linker, const char *filename, 492 - const struct bpf_linker_file_opts *opts) 439 + static int bpf_linker_add_file(struct bpf_linker *linker, int fd, 440 + const char *filename) 493 441 { 494 442 struct src_obj obj = {}; 495 443 int err = 0; 496 444 497 - if (!OPTS_VALID(opts, bpf_linker_file_opts)) 498 - return libbpf_err(-EINVAL); 445 + obj.filename = filename; 446 + obj.fd = fd; 499 447 500 - if (!linker->elf) 501 - return libbpf_err(-EINVAL); 502 - 503 - err = err ?: linker_load_obj_file(linker, filename, opts, &obj); 448 + err = err ?: linker_load_obj_file(linker, &obj); 504 449 err = err ?: linker_append_sec_data(linker, &obj); 505 450 err = err ?: linker_append_elf_syms(linker, &obj); 506 451 err = err ?: linker_append_elf_relos(linker, &obj); ··· 512 463 free(obj.sym_map); 513 464 if (obj.elf) 514 465 elf_end(obj.elf); 515 - if (obj.fd >= 0) 516 - close(obj.fd); 517 466 467 + return err; 468 + } 469 + 470 + int bpf_linker__add_file(struct bpf_linker *linker, const char *filename, 471 + const struct bpf_linker_file_opts *opts) 472 + { 473 + int fd, err; 474 + 475 + if (!OPTS_VALID(opts, bpf_linker_file_opts)) 476 + return libbpf_err(-EINVAL); 477 + 478 + if (!linker->elf) 479 + return libbpf_err(-EINVAL); 480 + 481 + fd = open(filename, O_RDONLY | O_CLOEXEC); 482 + if (fd < 0) { 483 + err = -errno; 484 + pr_warn("failed to open file '%s': %s\n", filename, errstr(err)); 485 + return libbpf_err(err); 486 + } 487 + 488 + err = bpf_linker_add_file(linker, fd, filename); 489 + close(fd); 518 490 return libbpf_err(err); 491 + } 492 + 493 + int bpf_linker__add_fd(struct bpf_linker *linker, int fd, 494 + const struct bpf_linker_file_opts *opts) 495 + { 496 + char filename[32]; 497 + int err; 498 + 499 + if (!OPTS_VALID(opts, bpf_linker_file_opts)) 500 + return libbpf_err(-EINVAL); 501 + 502 + if (!linker->elf) 503 + return libbpf_err(-EINVAL); 504 + 505 + if (fd < 0) 506 + return libbpf_err(-EINVAL); 507 + 508 + snprintf(filename, sizeof(filename), "fd:%d", fd); 509 + err = bpf_linker_add_file(linker, fd, filename); 510 + return libbpf_err(err); 511 + } 512 + 513 + int bpf_linker__add_buf(struct bpf_linker *linker, void *buf, size_t buf_sz, 514 + const struct bpf_linker_file_opts *opts) 515 + { 516 + char filename[32]; 517 + int fd, written, ret; 518 + 519 + if (!OPTS_VALID(opts, bpf_linker_file_opts)) 520 + return libbpf_err(-EINVAL); 521 + 522 + if (!linker->elf) 523 + return libbpf_err(-EINVAL); 524 + 525 + snprintf(filename, sizeof(filename), "mem:%p+%zu", buf, buf_sz); 526 + 527 + fd = memfd_create(filename, 0); 528 + if (fd < 0) { 529 + ret = -errno; 530 + pr_warn("failed to create memfd '%s': %s\n", filename, errstr(ret)); 531 + return libbpf_err(ret); 532 + } 533 + 534 + written = 0; 535 + while (written < buf_sz) { 536 + ret = write(fd, buf, buf_sz); 537 + if (ret < 0) { 538 + ret = -errno; 539 + pr_warn("failed to write '%s': %s\n", filename, errstr(ret)); 540 + goto err_out; 541 + } 542 + written += ret; 543 + } 544 + 545 + ret = bpf_linker_add_file(linker, fd, filename); 546 + err_out: 547 + close(fd); 548 + return libbpf_err(ret); 519 549 } 520 550 521 551 static bool is_dwarf_sec_name(const char *name) ··· 662 534 return sec; 663 535 } 664 536 665 - static int linker_load_obj_file(struct bpf_linker *linker, const char *filename, 666 - const struct bpf_linker_file_opts *opts, 537 + static int linker_load_obj_file(struct bpf_linker *linker, 667 538 struct src_obj *obj) 668 539 { 669 540 int err = 0; ··· 681 554 #error "Unknown __BYTE_ORDER__" 682 555 #endif 683 556 684 - pr_debug("linker: adding object file '%s'...\n", filename); 557 + pr_debug("linker: adding object file '%s'...\n", obj->filename); 685 558 686 - obj->filename = filename; 687 - 688 - obj->fd = open(filename, O_RDONLY | O_CLOEXEC); 689 - if (obj->fd < 0) { 690 - err = -errno; 691 - pr_warn("failed to open file '%s': %s\n", filename, errstr(err)); 692 - return err; 693 - } 694 559 obj->elf = elf_begin(obj->fd, ELF_C_READ_MMAP, NULL); 695 560 if (!obj->elf) { 696 - pr_warn_elf("failed to parse ELF file '%s'", filename); 561 + pr_warn_elf("failed to parse ELF file '%s'", obj->filename); 697 562 return -EINVAL; 698 563 } 699 564 700 565 /* Sanity check ELF file high-level properties */ 701 566 ehdr = elf64_getehdr(obj->elf); 702 567 if (!ehdr) { 703 - pr_warn_elf("failed to get ELF header for %s", filename); 568 + pr_warn_elf("failed to get ELF header for %s", obj->filename); 704 569 return -EINVAL; 705 570 } 706 571 ··· 700 581 obj_byteorder = ehdr->e_ident[EI_DATA]; 701 582 if (obj_byteorder != ELFDATA2LSB && obj_byteorder != ELFDATA2MSB) { 702 583 err = -EOPNOTSUPP; 703 - pr_warn("unknown byte order of ELF file %s\n", filename); 584 + pr_warn("unknown byte order of ELF file %s\n", obj->filename); 704 585 return err; 705 586 } 706 587 if (link_byteorder == ELFDATANONE) { ··· 710 591 obj_byteorder == ELFDATA2MSB ? "big" : "little"); 711 592 } else if (link_byteorder != obj_byteorder) { 712 593 err = -EOPNOTSUPP; 713 - pr_warn("byte order mismatch with ELF file %s\n", filename); 594 + pr_warn("byte order mismatch with ELF file %s\n", obj->filename); 714 595 return err; 715 596 } 716 597 ··· 718 599 || ehdr->e_machine != EM_BPF 719 600 || ehdr->e_ident[EI_CLASS] != ELFCLASS64) { 720 601 err = -EOPNOTSUPP; 721 - pr_warn_elf("unsupported kind of ELF file %s", filename); 602 + pr_warn_elf("unsupported kind of ELF file %s", obj->filename); 722 603 return err; 723 604 } 724 605 725 606 if (elf_getshdrstrndx(obj->elf, &obj->shstrs_sec_idx)) { 726 - pr_warn_elf("failed to get SHSTRTAB section index for %s", filename); 607 + pr_warn_elf("failed to get SHSTRTAB section index for %s", obj->filename); 727 608 return -EINVAL; 728 609 } 729 610 ··· 735 616 shdr = elf64_getshdr(scn); 736 617 if (!shdr) { 737 618 pr_warn_elf("failed to get section #%zu header for %s", 738 - sec_idx, filename); 619 + sec_idx, obj->filename); 739 620 return -EINVAL; 740 621 } 741 622 742 623 sec_name = elf_strptr(obj->elf, obj->shstrs_sec_idx, shdr->sh_name); 743 624 if (!sec_name) { 744 625 pr_warn_elf("failed to get section #%zu name for %s", 745 - sec_idx, filename); 626 + sec_idx, obj->filename); 746 627 return -EINVAL; 747 628 } 748 629 749 630 data = elf_getdata(scn, 0); 750 631 if (!data) { 751 632 pr_warn_elf("failed to get section #%zu (%s) data from %s", 752 - sec_idx, sec_name, filename); 633 + sec_idx, sec_name, obj->filename); 753 634 return -EINVAL; 754 635 } 755 636 ··· 785 666 err = libbpf_get_error(obj->btf); 786 667 if (err) { 787 668 pr_warn("failed to parse .BTF from %s: %s\n", 788 - filename, errstr(err)); 669 + obj->filename, errstr(err)); 789 670 return err; 790 671 } 791 672 sec->skipped = true; ··· 796 677 err = libbpf_get_error(obj->btf_ext); 797 678 if (err) { 798 679 pr_warn("failed to parse .BTF.ext from '%s': %s\n", 799 - filename, errstr(err)); 680 + obj->filename, errstr(err)); 800 681 return err; 801 682 } 802 683 sec->skipped = true; ··· 813 694 break; 814 695 default: 815 696 pr_warn("unrecognized section #%zu (%s) in %s\n", 816 - sec_idx, sec_name, filename); 697 + sec_idx, sec_name, obj->filename); 817 698 err = -EINVAL; 818 699 return err; 819 700 } ··· 2806 2687 } 2807 2688 2808 2689 elf_end(linker->elf); 2809 - close(linker->fd); 2810 - 2811 2690 linker->elf = NULL; 2691 + 2692 + if (linker->fd_is_owned) 2693 + close(linker->fd); 2812 2694 linker->fd = -1; 2813 2695 2814 2696 return 0;