Commit 6d5e5e5d authored by Alastair Robertson's avatar Alastair Robertson Committed by Andrii Nakryiko
Browse files

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: default avatarAlastair Robertson <ajor@meta.com>
Signed-off-by: default avatarAndrii Nakryiko <andrii@kernel.org>
Link: https://lore.kernel.org/bpf/20241211164030.573042-3-ajor@meta.com
parent b6417129
Loading
Loading
Loading
Loading
+5 −0
Original line number Diff line number Diff line
@@ -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);

+4 −0
Original line number Diff line number Diff line
@@ -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;
+141 −21
Original line number Diff line number Diff line
@@ -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,6 +157,8 @@ 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, ...)									\
@@ -159,6 +166,9 @@ struct bpf_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,
				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)
		elf_end(linker->elf);

	if (linker->fd >= 0)
	if (linker->fd >= 0 && linker->fd_is_owned)
		close(linker->fd);

	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);
		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)
@@ -435,24 +488,11 @@ static int init_output_elf(struct bpf_linker *linker)
	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, fd;

	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);
	}
	int err = 0;

	obj.filename = filename;
	obj.fd = fd;
@@ -472,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 */
@@ -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;