libbpf: Extend linker API to support in-memory ELF files
authorAlastair Robertson <ajor@meta.com>
Wed, 11 Dec 2024 16:40:30 +0000 (08:40 -0800)
committerAndrii Nakryiko <andrii@kernel.org>
Thu, 12 Dec 2024 23:16:53 +0000 (15:16 -0800)
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
tools/lib/bpf/libbpf.h
tools/lib/bpf/libbpf.map
tools/lib/bpf/linker.c

index b2ce3a72b11d6a6f304c95333dd839d1b098f74b..d45807103565e670085603dc5c7d9135a7e9031f 100644 (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);
 
index 54b6f312cfa809c09865f72afe3599236171cc6b..a8b2936a164668ca3c662d7251233e3763b97b6d 100644 (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;
index c49e94506d9c66491df04dceecf883231974b190..b52f71c59616fd359dce5af2cb277e2b9da04943 100644 (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,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;