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>
This commit is contained in:
Andrii Nakryiko
2024-12-12 14:58:49 -08:00
3 changed files with 182 additions and 53 deletions

View File

@@ -1796,9 +1796,14 @@ struct bpf_linker_file_opts {
struct bpf_linker;
LIBBPF_API struct bpf_linker *bpf_linker__new(const char *filename, struct bpf_linker_opts *opts);
LIBBPF_API struct bpf_linker *bpf_linker__new_fd(int fd, struct bpf_linker_opts *opts);
LIBBPF_API int bpf_linker__add_file(struct bpf_linker *linker,
const char *filename,
const struct bpf_linker_file_opts *opts);
LIBBPF_API int bpf_linker__add_fd(struct bpf_linker *linker, int fd,
const struct bpf_linker_file_opts *opts);
LIBBPF_API int bpf_linker__add_buf(struct bpf_linker *linker, void *buf, size_t buf_sz,
const struct bpf_linker_file_opts *opts);
LIBBPF_API int bpf_linker__finalize(struct bpf_linker *linker);
LIBBPF_API void bpf_linker__free(struct bpf_linker *linker);

View File

@@ -432,4 +432,8 @@ LIBBPF_1.5.0 {
} LIBBPF_1.4.0;
LIBBPF_1.6.0 {
global:
bpf_linker__add_buf;
bpf_linker__add_fd;
bpf_linker__new_fd;
} LIBBPF_1.5.0;

View File

@@ -4,6 +4,10 @@
*
* Copyright (c) 2021 Facebook
*/
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#include <stdbool.h>
#include <stddef.h>
#include <stdio.h>
@@ -16,6 +20,7 @@
#include <elf.h>
#include <libelf.h>
#include <fcntl.h>
#include <sys/mman.h>
#include "libbpf.h"
#include "btf.h"
#include "libbpf_internal.h"
@@ -152,15 +157,19 @@ struct bpf_linker {
/* global (including extern) ELF symbols */
int glob_sym_cnt;
struct glob_sym *glob_syms;
bool fd_is_owned;
};
#define pr_warn_elf(fmt, ...) \
libbpf_print(LIBBPF_WARN, "libbpf: " fmt ": %s\n", ##__VA_ARGS__, elf_errmsg(-1))
static int init_output_elf(struct bpf_linker *linker, const char *file);
static int init_output_elf(struct bpf_linker *linker);
static int linker_load_obj_file(struct bpf_linker *linker, const char *filename,
const struct bpf_linker_file_opts *opts,
static int bpf_linker_add_file(struct bpf_linker *linker, int fd,
const char *filename);
static int linker_load_obj_file(struct bpf_linker *linker,
struct src_obj *obj);
static int linker_sanity_check_elf(struct src_obj *obj);
static int linker_sanity_check_elf_symtab(struct src_obj *obj, struct src_sec *sec);
@@ -191,7 +200,7 @@ void bpf_linker__free(struct bpf_linker *linker)
if (linker->elf)
elf_end(linker->elf);
if (linker->fd >= 0)
if (linker->fd >= 0 && linker->fd_is_owned)
close(linker->fd);
strset__free(linker->strtab_strs);
@@ -233,9 +242,63 @@ struct bpf_linker *bpf_linker__new(const char *filename, struct bpf_linker_opts
if (!linker)
return errno = ENOMEM, NULL;
linker->fd = -1;
linker->filename = strdup(filename);
if (!linker->filename) {
err = -ENOMEM;
goto err_out;
}
err = init_output_elf(linker, filename);
linker->fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, 0644);
if (linker->fd < 0) {
err = -errno;
pr_warn("failed to create '%s': %d\n", filename, err);
goto err_out;
}
linker->fd_is_owned = true;
err = init_output_elf(linker);
if (err)
goto err_out;
return linker;
err_out:
bpf_linker__free(linker);
return errno = -err, NULL;
}
struct bpf_linker *bpf_linker__new_fd(int fd, struct bpf_linker_opts *opts)
{
struct bpf_linker *linker;
char filename[32];
int err;
if (fd < 0)
return errno = EINVAL, NULL;
if (!OPTS_VALID(opts, bpf_linker_opts))
return errno = EINVAL, NULL;
if (elf_version(EV_CURRENT) == EV_NONE) {
pr_warn_elf("libelf initialization failed");
return errno = EINVAL, NULL;
}
linker = calloc(1, sizeof(*linker));
if (!linker)
return errno = ENOMEM, NULL;
snprintf(filename, sizeof(filename), "fd:%d", fd);
linker->filename = strdup(filename);
if (!linker->filename) {
err = -ENOMEM;
goto err_out;
}
linker->fd = fd;
linker->fd_is_owned = false;
err = init_output_elf(linker);
if (err)
goto err_out;
@@ -294,23 +357,12 @@ static Elf64_Sym *add_new_sym(struct bpf_linker *linker, size_t *sym_idx)
return sym;
}
static int init_output_elf(struct bpf_linker *linker, const char *file)
static int init_output_elf(struct bpf_linker *linker)
{
int err, str_off;
Elf64_Sym *init_sym;
struct dst_sec *sec;
linker->filename = strdup(file);
if (!linker->filename)
return -ENOMEM;
linker->fd = open(file, O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, 0644);
if (linker->fd < 0) {
err = -errno;
pr_warn("failed to create '%s': %s\n", file, errstr(err));
return err;
}
linker->elf = elf_begin(linker->fd, ELF_C_WRITE, NULL);
if (!linker->elf) {
pr_warn_elf("failed to create ELF object");
@@ -436,19 +488,16 @@ static int init_output_elf(struct bpf_linker *linker, const char *file)
return 0;
}
int bpf_linker__add_file(struct bpf_linker *linker, const char *filename,
const struct bpf_linker_file_opts *opts)
static int bpf_linker_add_file(struct bpf_linker *linker, int fd,
const char *filename)
{
struct src_obj obj = {};
int err = 0;
if (!OPTS_VALID(opts, bpf_linker_file_opts))
return libbpf_err(-EINVAL);
obj.filename = filename;
obj.fd = fd;
if (!linker->elf)
return libbpf_err(-EINVAL);
err = err ?: linker_load_obj_file(linker, filename, opts, &obj);
err = err ?: linker_load_obj_file(linker, &obj);
err = err ?: linker_append_sec_data(linker, &obj);
err = err ?: linker_append_elf_syms(linker, &obj);
err = err ?: linker_append_elf_relos(linker, &obj);
@@ -463,12 +512,91 @@ int bpf_linker__add_file(struct bpf_linker *linker, const char *filename,
free(obj.sym_map);
if (obj.elf)
elf_end(obj.elf);
if (obj.fd >= 0)
close(obj.fd);
return err;
}
int bpf_linker__add_file(struct bpf_linker *linker, const char *filename,
const struct bpf_linker_file_opts *opts)
{
int fd, err;
if (!OPTS_VALID(opts, bpf_linker_file_opts))
return libbpf_err(-EINVAL);
if (!linker->elf)
return libbpf_err(-EINVAL);
fd = open(filename, O_RDONLY | O_CLOEXEC);
if (fd < 0) {
err = -errno;
pr_warn("failed to open file '%s': %s\n", filename, errstr(err));
return libbpf_err(err);
}
err = bpf_linker_add_file(linker, fd, filename);
close(fd);
return libbpf_err(err);
}
int bpf_linker__add_fd(struct bpf_linker *linker, int fd,
const struct bpf_linker_file_opts *opts)
{
char filename[32];
int err;
if (!OPTS_VALID(opts, bpf_linker_file_opts))
return libbpf_err(-EINVAL);
if (!linker->elf)
return libbpf_err(-EINVAL);
if (fd < 0)
return libbpf_err(-EINVAL);
snprintf(filename, sizeof(filename), "fd:%d", fd);
err = bpf_linker_add_file(linker, fd, filename);
return libbpf_err(err);
}
int bpf_linker__add_buf(struct bpf_linker *linker, void *buf, size_t buf_sz,
const struct bpf_linker_file_opts *opts)
{
char filename[32];
int fd, written, ret;
if (!OPTS_VALID(opts, bpf_linker_file_opts))
return libbpf_err(-EINVAL);
if (!linker->elf)
return libbpf_err(-EINVAL);
snprintf(filename, sizeof(filename), "mem:%p+%zu", buf, buf_sz);
fd = memfd_create(filename, 0);
if (fd < 0) {
ret = -errno;
pr_warn("failed to create memfd '%s': %s\n", filename, errstr(ret));
return libbpf_err(ret);
}
written = 0;
while (written < buf_sz) {
ret = write(fd, buf, buf_sz);
if (ret < 0) {
ret = -errno;
pr_warn("failed to write '%s': %s\n", filename, errstr(ret));
goto err_out;
}
written += ret;
}
ret = bpf_linker_add_file(linker, fd, filename);
err_out:
close(fd);
return libbpf_err(ret);
}
static bool is_dwarf_sec_name(const char *name)
{
/* approximation, but the actual list is too long */
@@ -534,8 +662,7 @@ static struct src_sec *add_src_sec(struct src_obj *obj, const char *sec_name)
return sec;
}
static int linker_load_obj_file(struct bpf_linker *linker, const char *filename,
const struct bpf_linker_file_opts *opts,
static int linker_load_obj_file(struct bpf_linker *linker,
struct src_obj *obj)
{
int err = 0;
@@ -554,26 +681,18 @@ static int linker_load_obj_file(struct bpf_linker *linker, const char *filename,
#error "Unknown __BYTE_ORDER__"
#endif
pr_debug("linker: adding object file '%s'...\n", filename);
pr_debug("linker: adding object file '%s'...\n", obj->filename);
obj->filename = filename;
obj->fd = open(filename, O_RDONLY | O_CLOEXEC);
if (obj->fd < 0) {
err = -errno;
pr_warn("failed to open file '%s': %s\n", filename, errstr(err));
return err;
}
obj->elf = elf_begin(obj->fd, ELF_C_READ_MMAP, NULL);
if (!obj->elf) {
pr_warn_elf("failed to parse ELF file '%s'", filename);
pr_warn_elf("failed to parse ELF file '%s'", obj->filename);
return -EINVAL;
}
/* Sanity check ELF file high-level properties */
ehdr = elf64_getehdr(obj->elf);
if (!ehdr) {
pr_warn_elf("failed to get ELF header for %s", filename);
pr_warn_elf("failed to get ELF header for %s", obj->filename);
return -EINVAL;
}
@@ -581,7 +700,7 @@ static int linker_load_obj_file(struct bpf_linker *linker, const char *filename,
obj_byteorder = ehdr->e_ident[EI_DATA];
if (obj_byteorder != ELFDATA2LSB && obj_byteorder != ELFDATA2MSB) {
err = -EOPNOTSUPP;
pr_warn("unknown byte order of ELF file %s\n", filename);
pr_warn("unknown byte order of ELF file %s\n", obj->filename);
return err;
}
if (link_byteorder == ELFDATANONE) {
@@ -591,7 +710,7 @@ static int linker_load_obj_file(struct bpf_linker *linker, const char *filename,
obj_byteorder == ELFDATA2MSB ? "big" : "little");
} else if (link_byteorder != obj_byteorder) {
err = -EOPNOTSUPP;
pr_warn("byte order mismatch with ELF file %s\n", filename);
pr_warn("byte order mismatch with ELF file %s\n", obj->filename);
return err;
}
@@ -599,12 +718,12 @@ static int linker_load_obj_file(struct bpf_linker *linker, const char *filename,
|| ehdr->e_machine != EM_BPF
|| ehdr->e_ident[EI_CLASS] != ELFCLASS64) {
err = -EOPNOTSUPP;
pr_warn_elf("unsupported kind of ELF file %s", filename);
pr_warn_elf("unsupported kind of ELF file %s", obj->filename);
return err;
}
if (elf_getshdrstrndx(obj->elf, &obj->shstrs_sec_idx)) {
pr_warn_elf("failed to get SHSTRTAB section index for %s", filename);
pr_warn_elf("failed to get SHSTRTAB section index for %s", obj->filename);
return -EINVAL;
}
@@ -616,21 +735,21 @@ static int linker_load_obj_file(struct bpf_linker *linker, const char *filename,
shdr = elf64_getshdr(scn);
if (!shdr) {
pr_warn_elf("failed to get section #%zu header for %s",
sec_idx, filename);
sec_idx, obj->filename);
return -EINVAL;
}
sec_name = elf_strptr(obj->elf, obj->shstrs_sec_idx, shdr->sh_name);
if (!sec_name) {
pr_warn_elf("failed to get section #%zu name for %s",
sec_idx, filename);
sec_idx, obj->filename);
return -EINVAL;
}
data = elf_getdata(scn, 0);
if (!data) {
pr_warn_elf("failed to get section #%zu (%s) data from %s",
sec_idx, sec_name, filename);
sec_idx, sec_name, obj->filename);
return -EINVAL;
}
@@ -666,7 +785,7 @@ static int linker_load_obj_file(struct bpf_linker *linker, const char *filename,
err = libbpf_get_error(obj->btf);
if (err) {
pr_warn("failed to parse .BTF from %s: %s\n",
filename, errstr(err));
obj->filename, errstr(err));
return err;
}
sec->skipped = true;
@@ -677,7 +796,7 @@ static int linker_load_obj_file(struct bpf_linker *linker, const char *filename,
err = libbpf_get_error(obj->btf_ext);
if (err) {
pr_warn("failed to parse .BTF.ext from '%s': %s\n",
filename, errstr(err));
obj->filename, errstr(err));
return err;
}
sec->skipped = true;
@@ -694,7 +813,7 @@ static int linker_load_obj_file(struct bpf_linker *linker, const char *filename,
break;
default:
pr_warn("unrecognized section #%zu (%s) in %s\n",
sec_idx, sec_name, filename);
sec_idx, sec_name, obj->filename);
err = -EINVAL;
return err;
}
@@ -2687,9 +2806,10 @@ int bpf_linker__finalize(struct bpf_linker *linker)
}
elf_end(linker->elf);
close(linker->fd);
linker->elf = NULL;
if (linker->fd_is_owned)
close(linker->fd);
linker->fd = -1;
return 0;