mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 02:30:34 +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);
 | 
							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)
 | 
					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