[PATCH bpf-next v2 2/2] libbpf: Extend linker API to support in-memory ELF files

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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@xxxxxxxx>
---
 tools/lib/bpf/libbpf.h   |  12 +++-
 tools/lib/bpf/libbpf.map |   3 +
 tools/lib/bpf/linker.c   | 143 ++++++++++++++++++++++++++++++++++++---
 3 files changed, 145 insertions(+), 13 deletions(-)

diff --git a/tools/lib/bpf/libbpf.h b/tools/lib/bpf/libbpf.h
index b2ce3a72b11d..7a88830a3431 100644
--- a/tools/lib/bpf/libbpf.h
+++ b/tools/lib/bpf/libbpf.h
@@ -1784,21 +1784,29 @@ enum libbpf_tristate {
 struct bpf_linker_opts {
 	/* size of this struct, for forward/backward compatibility */
 	size_t sz;
+	const char *filename;
 };
-#define bpf_linker_opts__last_field sz
+#define bpf_linker_opts__last_field filename
 
 struct bpf_linker_file_opts {
 	/* size of this struct, for forward/backward compatibility */
 	size_t sz;
+	const char *filename;
 };
-#define bpf_linker_file_opts__last_field sz
+#define bpf_linker_file_opts__last_field filename
 
 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, const char *name,
+				   void *buf, int 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);
 
diff --git a/tools/lib/bpf/libbpf.map b/tools/lib/bpf/libbpf.map
index 54b6f312cfa8..23f2a30778f0 100644
--- a/tools/lib/bpf/libbpf.map
+++ b/tools/lib/bpf/libbpf.map
@@ -432,4 +432,7 @@ LIBBPF_1.5.0 {
 } LIBBPF_1.4.0;
 
 LIBBPF_1.6.0 {
+		bpf_linker__add_buf;
+		bpf_linker__add_fd;
+		bpf_linker__new_fd;
 } LIBBPF_1.5.0;
diff --git a/tools/lib/bpf/linker.c b/tools/lib/bpf/linker.c
index 375896a94e6a..fd98469fa20d 100644
--- a/tools/lib/bpf/linker.c
+++ b/tools/lib/bpf/linker.c
@@ -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, ...)									\
@@ -243,6 +250,54 @@ 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;
+}
+
+#define LINKER_MAX_FD_NAME_SIZE 24
+
+struct bpf_linker *bpf_linker__new_fd(int fd, struct bpf_linker_opts *opts)
+{
+	struct bpf_linker *linker;
+	const char *filename;
+	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;
+
+	filename = OPTS_GET(opts, filename, NULL);
+	if (filename) {
+		linker->filename = strdup(filename);
+	} else {
+		linker->filename = malloc(LINKER_MAX_FD_NAME_SIZE);
+		if (!linker->filename)
+			return errno = ENOMEM, NULL;
+		snprintf(linker->filename, LINKER_MAX_FD_NAME_SIZE, "fd:%d", fd);
+	}
+
+	linker->fd = fd;
+	linker->fd_is_owned = false;
 
 	err = init_output_elf(linker);
 	if (err)
@@ -435,16 +490,15 @@ static int init_output_elf(struct bpf_linker *linker)
 }
 
 int bpf_linker__add_file(struct bpf_linker *linker, const char *filename,
-			 const struct bpf_linker_file_opts *opts)
+			 const struct bpf_linker_file_opts *input_opts)
 {
-	struct src_obj obj = {};
-	int err = 0, fd;
+	int fd, ret;
 
-	if (!OPTS_VALID(opts, bpf_linker_file_opts))
-		return libbpf_err(-EINVAL);
+	LIBBPF_OPTS(bpf_linker_file_opts, opts);
 
-	if (!linker->elf)
-		return libbpf_err(-EINVAL);
+	if (input_opts)
+		opts = *input_opts;
+	opts.filename = filename;
 
 	fd = open(filename, O_RDONLY | O_CLOEXEC);
 	if (fd < 0) {
@@ -452,6 +506,37 @@ int bpf_linker__add_file(struct bpf_linker *linker, const char *filename,
 		return -errno;
 	}
 
+	ret = bpf_linker__add_fd(linker, fd, &opts);
+
+	close(fd);
+
+	return ret;
+}
+
+int bpf_linker__add_fd(struct bpf_linker *linker, int fd,
+		       const struct bpf_linker_file_opts *opts)
+{
+	struct src_obj obj = {};
+	const char *filename;
+	char name[LINKER_MAX_FD_NAME_SIZE];
+	int err = 0;
+
+	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);
+
+	filename = OPTS_GET(opts, filename, NULL);
+	if (filename) {
+		obj.filename = filename;
+	} else {
+		snprintf(name, sizeof(name), "fd:%d", fd);
+		obj.filename = name;
+	}
 	obj.fd = fd;
 
 	err = err ?: linker_load_obj_file(linker, opts, &obj);
@@ -469,12 +554,47 @@ 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);
+	/* leave obj.fd for the caller to clean up if appropriate */
 
 	return libbpf_err(err);
 }
 
+int bpf_linker__add_buf(struct bpf_linker *linker, const char *name,
+			void *buf, int buf_sz,
+			const struct bpf_linker_file_opts *input_opts)
+{
+	int fd, written, ret;
+
+	LIBBPF_OPTS(bpf_linker_file_opts, opts);
+
+	if (input_opts)
+		opts = *input_opts;
+	opts.filename = name;
+
+	fd = memfd_create(name, 0);
+	if (fd < 0) {
+		pr_warn("failed to create memfd '%s': %s\n", name, errstr(errno));
+		return -errno;
+	}
+
+	written = 0;
+	while (written < buf_sz) {
+		ret = write(fd, buf, buf_sz);
+		if (ret < 0) {
+			pr_warn("failed to write '%s': %s\n", name, errstr(errno));
+			return -errno;
+		}
+		written += ret;
+	}
+
+	ret = bpf_linker__add_fd(linker, fd, &opts);
+
+	if (fd >= 0)
+		close(fd);
+
+	return ret;
+}
+
 static bool is_dwarf_sec_name(const char *name)
 {
 	/* approximation, but the actual list is too long */
@@ -2691,9 +2811,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;
-- 
2.43.5






[Index of Archives]     [Linux Samsung SoC]     [Linux Rockchip SoC]     [Linux Actions SoC]     [Linux for Synopsys ARC Processors]     [Linux NFS]     [Linux NILFS]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]


  Powered by Linux