mirror of
https://github.com/torvalds/linux.git
synced 2025-11-03 01:59:51 +02:00
libbpf: Extend linker API to support in-memory ELF files
The new_fd and add_fd functions correspond to the original new and add_file functions, but accept an FD instead of a file name. This gives API consumers the option of using anonymous files/memfds to avoid writing ELFs to disk. This new API will be useful for performing linking as part of bpftrace's JIT compilation. The add_buf function is a convenience wrapper that does the work of creating a memfd for the caller. Signed-off-by: Alastair Robertson <ajor@meta.com> Signed-off-by: Andrii Nakryiko <andrii@kernel.org> Link: https://lore.kernel.org/bpf/20241211164030.573042-3-ajor@meta.com
This commit is contained in:
parent
b641712925
commit
6d5e5e5d7c
3 changed files with 150 additions and 21 deletions
|
|
@ -1796,9 +1796,14 @@ struct bpf_linker_file_opts {
|
||||||
struct bpf_linker;
|
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(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,
|
LIBBPF_API int bpf_linker__add_file(struct bpf_linker *linker,
|
||||||
const char *filename,
|
const char *filename,
|
||||||
const struct bpf_linker_file_opts *opts);
|
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 int bpf_linker__finalize(struct bpf_linker *linker);
|
||||||
LIBBPF_API void bpf_linker__free(struct bpf_linker *linker);
|
LIBBPF_API void bpf_linker__free(struct bpf_linker *linker);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -432,4 +432,8 @@ LIBBPF_1.5.0 {
|
||||||
} LIBBPF_1.4.0;
|
} LIBBPF_1.4.0;
|
||||||
|
|
||||||
LIBBPF_1.6.0 {
|
LIBBPF_1.6.0 {
|
||||||
|
global:
|
||||||
|
bpf_linker__add_buf;
|
||||||
|
bpf_linker__add_fd;
|
||||||
|
bpf_linker__new_fd;
|
||||||
} LIBBPF_1.5.0;
|
} LIBBPF_1.5.0;
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,10 @@
|
||||||
*
|
*
|
||||||
* Copyright (c) 2021 Facebook
|
* Copyright (c) 2021 Facebook
|
||||||
*/
|
*/
|
||||||
|
#ifndef _GNU_SOURCE
|
||||||
|
#define _GNU_SOURCE
|
||||||
|
#endif
|
||||||
|
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
|
@ -16,6 +20,7 @@
|
||||||
#include <elf.h>
|
#include <elf.h>
|
||||||
#include <libelf.h>
|
#include <libelf.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
|
#include <sys/mman.h>
|
||||||
#include "libbpf.h"
|
#include "libbpf.h"
|
||||||
#include "btf.h"
|
#include "btf.h"
|
||||||
#include "libbpf_internal.h"
|
#include "libbpf_internal.h"
|
||||||
|
|
@ -152,6 +157,8 @@ struct bpf_linker {
|
||||||
/* global (including extern) ELF symbols */
|
/* global (including extern) ELF symbols */
|
||||||
int glob_sym_cnt;
|
int glob_sym_cnt;
|
||||||
struct glob_sym *glob_syms;
|
struct glob_sym *glob_syms;
|
||||||
|
|
||||||
|
bool fd_is_owned;
|
||||||
};
|
};
|
||||||
|
|
||||||
#define pr_warn_elf(fmt, ...) \
|
#define pr_warn_elf(fmt, ...) \
|
||||||
|
|
@ -159,6 +166,9 @@ struct bpf_linker {
|
||||||
|
|
||||||
static int init_output_elf(struct bpf_linker *linker);
|
static int init_output_elf(struct bpf_linker *linker);
|
||||||
|
|
||||||
|
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,
|
static int linker_load_obj_file(struct bpf_linker *linker,
|
||||||
struct src_obj *obj);
|
struct src_obj *obj);
|
||||||
static int linker_sanity_check_elf(struct src_obj *obj);
|
static int linker_sanity_check_elf(struct src_obj *obj);
|
||||||
|
|
@ -190,7 +200,7 @@ void bpf_linker__free(struct bpf_linker *linker)
|
||||||
if (linker->elf)
|
if (linker->elf)
|
||||||
elf_end(linker->elf);
|
elf_end(linker->elf);
|
||||||
|
|
||||||
if (linker->fd >= 0)
|
if (linker->fd >= 0 && linker->fd_is_owned)
|
||||||
close(linker->fd);
|
close(linker->fd);
|
||||||
|
|
||||||
strset__free(linker->strtab_strs);
|
strset__free(linker->strtab_strs);
|
||||||
|
|
@ -244,6 +254,49 @@ struct bpf_linker *bpf_linker__new(const char *filename, struct bpf_linker_opts
|
||||||
pr_warn("failed to create '%s': %d\n", filename, err);
|
pr_warn("failed to create '%s': %d\n", filename, err);
|
||||||
goto err_out;
|
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);
|
err = init_output_elf(linker);
|
||||||
if (err)
|
if (err)
|
||||||
|
|
@ -435,24 +488,11 @@ static int init_output_elf(struct bpf_linker *linker)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int bpf_linker__add_file(struct bpf_linker *linker, const char *filename,
|
static int bpf_linker_add_file(struct bpf_linker *linker, int fd,
|
||||||
const struct bpf_linker_file_opts *opts)
|
const char *filename)
|
||||||
{
|
{
|
||||||
struct src_obj obj = {};
|
struct src_obj obj = {};
|
||||||
int err = 0, fd;
|
int err = 0;
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
obj.filename = filename;
|
obj.filename = filename;
|
||||||
obj.fd = fd;
|
obj.fd = fd;
|
||||||
|
|
@ -472,12 +512,91 @@ int bpf_linker__add_file(struct bpf_linker *linker, const char *filename,
|
||||||
free(obj.sym_map);
|
free(obj.sym_map);
|
||||||
if (obj.elf)
|
if (obj.elf)
|
||||||
elf_end(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);
|
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)
|
static bool is_dwarf_sec_name(const char *name)
|
||||||
{
|
{
|
||||||
/* approximation, but the actual list is too long */
|
/* approximation, but the actual list is too long */
|
||||||
|
|
@ -2687,9 +2806,10 @@ int bpf_linker__finalize(struct bpf_linker *linker)
|
||||||
}
|
}
|
||||||
|
|
||||||
elf_end(linker->elf);
|
elf_end(linker->elf);
|
||||||
close(linker->fd);
|
|
||||||
|
|
||||||
linker->elf = NULL;
|
linker->elf = NULL;
|
||||||
|
|
||||||
|
if (linker->fd_is_owned)
|
||||||
|
close(linker->fd);
|
||||||
linker->fd = -1;
|
linker->fd = -1;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue