3116 lines · 81.0 KB · C
RawDownload
// SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)/* * BPF static linker * * Copyright (c) 2021 Facebook */#ifndef _GNU_SOURCE#define _GNU_SOURCE#endif #include <stdbool.h>#include <stddef.h>#include <stdio.h>#include <stdlib.h>#include <string.h>#include <unistd.h>#include <errno.h>#include <linux/err.h>#include <linux/btf.h>#include <elf.h>#include <libelf.h>#include <fcntl.h>#include <sys/mman.h>#include "libbpf.h"#include "btf.h"#include "libbpf_internal.h"#include "strset.h" #define BTF_EXTERN_SEC ".extern" struct src_sec {	const char *sec_name;	/* positional (not necessarily ELF) index in an array of sections */	int id;	/* positional (not necessarily ELF) index of a matching section in a final object file */	int dst_id;	/* section data offset in a matching output section */	int dst_off;	/* whether section is omitted from the final ELF file */	bool skipped;	/* whether section is an ephemeral section, not mapped to an ELF section */	bool ephemeral; 	/* ELF info */	size_t sec_idx;	Elf_Scn *scn;	Elf64_Shdr *shdr;	Elf_Data *data; 	/* corresponding BTF DATASEC type ID */	int sec_type_id;}; struct src_obj {	const char *filename;	int fd;	Elf *elf;	/* Section header strings section index */	size_t shstrs_sec_idx;	/* SYMTAB section index */	size_t symtab_sec_idx; 	struct btf *btf;	struct btf_ext *btf_ext; 	/* List of sections (including ephemeral). Slot zero is unused. */	struct src_sec *secs;	int sec_cnt; 	/* mapping of symbol indices from src to dst ELF */	int *sym_map;	/* mapping from the src BTF type IDs to dst ones */	int *btf_type_map;}; /* single .BTF.ext data section */struct btf_ext_sec_data {	size_t rec_cnt;	__u32 rec_sz;	void *recs;}; struct glob_sym {	/* ELF symbol index */	int sym_idx;	/* associated section id for .ksyms, .kconfig, etc, but not .extern */	int sec_id;	/* extern name offset in STRTAB */	int name_off;	/* optional associated BTF type ID */	int btf_id;	/* BTF type ID to which VAR/FUNC type is pointing to; used for	 * rewriting types when extern VAR/FUNC is resolved to a concrete	 * definition	 */	int underlying_btf_id;	/* sec_var index in the corresponding dst_sec, if exists */	int var_idx; 	/* extern or resolved/global symbol */	bool is_extern;	/* weak or strong symbol, never goes back from strong to weak */	bool is_weak;}; struct dst_sec {	char *sec_name;	/* positional (not necessarily ELF) index in an array of sections */	int id; 	bool ephemeral; 	/* ELF info */	size_t sec_idx;	Elf_Scn *scn;	Elf64_Shdr *shdr;	Elf_Data *data; 	/* final output section size */	int sec_sz;	/* final output contents of the section */	void *raw_data; 	/* corresponding STT_SECTION symbol index in SYMTAB */	int sec_sym_idx; 	/* section's DATASEC variable info, emitted on BTF finalization */	bool has_btf;	int sec_var_cnt;	struct btf_var_secinfo *sec_vars; 	/* section's .BTF.ext data */	struct btf_ext_sec_data func_info;	struct btf_ext_sec_data line_info;	struct btf_ext_sec_data core_relo_info;}; struct bpf_linker {	char *filename;	int fd;	Elf *elf;	Elf64_Ehdr *elf_hdr;	bool swapped_endian; 	/* Output sections metadata */	struct dst_sec *secs;	int sec_cnt; 	struct strset *strtab_strs; /* STRTAB unique strings */	size_t strtab_sec_idx; /* STRTAB section index */	size_t symtab_sec_idx; /* SYMTAB section index */ 	struct btf *btf;	struct btf_ext *btf_ext; 	/* global (including extern) ELF symbols */	int glob_sym_cnt;	struct glob_sym *glob_syms; 	bool fd_is_owned;}; #define pr_warn_elf(fmt, ...)									\	libbpf_print(LIBBPF_WARN, "libbpf: " fmt ": %s\n", ##__VA_ARGS__, elf_errmsg(-1)) 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);static int linker_sanity_check_elf_symtab(struct src_obj *obj, struct src_sec *sec);static int linker_sanity_check_elf_relos(struct src_obj *obj, struct src_sec *sec);static int linker_sanity_check_btf(struct src_obj *obj);static int linker_sanity_check_btf_ext(struct src_obj *obj);static int linker_fixup_btf(struct src_obj *obj);static int linker_append_sec_data(struct bpf_linker *linker, struct src_obj *obj);static int linker_append_elf_syms(struct bpf_linker *linker, struct src_obj *obj);static int linker_append_elf_sym(struct bpf_linker *linker, struct src_obj *obj,				 Elf64_Sym *sym, const char *sym_name, int src_sym_idx);static int linker_append_elf_relos(struct bpf_linker *linker, struct src_obj *obj);static int linker_append_btf(struct bpf_linker *linker, struct src_obj *obj);static int linker_append_btf_ext(struct bpf_linker *linker, struct src_obj *obj); static int finalize_btf(struct bpf_linker *linker);static int finalize_btf_ext(struct bpf_linker *linker); void bpf_linker__free(struct bpf_linker *linker){	int i; 	if (!linker)		return; 	free(linker->filename); 	if (linker->elf)		elf_end(linker->elf); 	if (linker->fd >= 0 && linker->fd_is_owned)		close(linker->fd); 	strset__free(linker->strtab_strs); 	btf__free(linker->btf);	btf_ext__free(linker->btf_ext); 	for (i = 1; i < linker->sec_cnt; i++) {		struct dst_sec *sec = &linker->secs[i]; 		free(sec->sec_name);		free(sec->raw_data);		free(sec->sec_vars); 		free(sec->func_info.recs);		free(sec->line_info.recs);		free(sec->core_relo_info.recs);	}	free(linker->secs); 	free(linker->glob_syms);	free(linker);} struct bpf_linker *bpf_linker__new(const char *filename, struct bpf_linker_opts *opts){	struct bpf_linker *linker;	int err; 	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; 	linker->filename = strdup(filename);	if (!linker->filename) {		err = -ENOMEM;		goto err_out;	} 	linker->fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, 0644);	if (linker->fd < 0) {		err = -errno;		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)		goto err_out; 	return linker; err_out:	bpf_linker__free(linker);	return errno = -err, NULL;} static struct dst_sec *add_dst_sec(struct bpf_linker *linker, const char *sec_name){	struct dst_sec *secs = linker->secs, *sec;	size_t new_cnt = linker->sec_cnt ? linker->sec_cnt + 1 : 2; 	secs = libbpf_reallocarray(secs, new_cnt, sizeof(*secs));	if (!secs)		return NULL; 	/* zero out newly allocated memory */	memset(secs + linker->sec_cnt, 0, (new_cnt - linker->sec_cnt) * sizeof(*secs)); 	linker->secs = secs;	linker->sec_cnt = new_cnt; 	sec = &linker->secs[new_cnt - 1];	sec->id = new_cnt - 1;	sec->sec_name = strdup(sec_name);	if (!sec->sec_name)		return NULL; 	return sec;} static Elf64_Sym *add_new_sym(struct bpf_linker *linker, size_t *sym_idx){	struct dst_sec *symtab = &linker->secs[linker->symtab_sec_idx];	Elf64_Sym *syms, *sym;	size_t sym_cnt = symtab->sec_sz / sizeof(*sym); 	syms = libbpf_reallocarray(symtab->raw_data, sym_cnt + 1, sizeof(*sym));	if (!syms)		return NULL; 	sym = &syms[sym_cnt];	memset(sym, 0, sizeof(*sym)); 	symtab->raw_data = syms;	symtab->sec_sz += sizeof(*sym);	symtab->shdr->sh_size += sizeof(*sym);	symtab->data->d_size += sizeof(*sym); 	if (sym_idx)		*sym_idx = sym_cnt; 	return sym;} static int init_output_elf(struct bpf_linker *linker){	int err, str_off;	Elf64_Sym *init_sym;	struct dst_sec *sec; 	linker->elf = elf_begin(linker->fd, ELF_C_WRITE, NULL);	if (!linker->elf) {		pr_warn_elf("failed to create ELF object");		return -EINVAL;	} 	/* ELF header */	linker->elf_hdr = elf64_newehdr(linker->elf);	if (!linker->elf_hdr) {		pr_warn_elf("failed to create ELF header");		return -EINVAL;	} 	linker->elf_hdr->e_machine = EM_BPF;	linker->elf_hdr->e_type = ET_REL;	/* Set unknown ELF endianness, assign later from input files */	linker->elf_hdr->e_ident[EI_DATA] = ELFDATANONE; 	/* STRTAB */	/* initialize strset with an empty string to conform to ELF */	linker->strtab_strs = strset__new(INT_MAX, "", sizeof(""));	if (libbpf_get_error(linker->strtab_strs))		return libbpf_get_error(linker->strtab_strs); 	sec = add_dst_sec(linker, ".strtab");	if (!sec)		return -ENOMEM; 	sec->scn = elf_newscn(linker->elf);	if (!sec->scn) {		pr_warn_elf("failed to create STRTAB section");		return -EINVAL;	} 	sec->shdr = elf64_getshdr(sec->scn);	if (!sec->shdr)		return -EINVAL; 	sec->data = elf_newdata(sec->scn);	if (!sec->data) {		pr_warn_elf("failed to create STRTAB data");		return -EINVAL;	} 	str_off = strset__add_str(linker->strtab_strs, sec->sec_name);	if (str_off < 0)		return str_off; 	sec->sec_idx = elf_ndxscn(sec->scn);	linker->elf_hdr->e_shstrndx = sec->sec_idx;	linker->strtab_sec_idx = sec->sec_idx; 	sec->shdr->sh_name = str_off;	sec->shdr->sh_type = SHT_STRTAB;	sec->shdr->sh_flags = SHF_STRINGS;	sec->shdr->sh_offset = 0;	sec->shdr->sh_link = 0;	sec->shdr->sh_info = 0;	sec->shdr->sh_addralign = 1;	sec->shdr->sh_size = sec->sec_sz = 0;	sec->shdr->sh_entsize = 0; 	/* SYMTAB */	sec = add_dst_sec(linker, ".symtab");	if (!sec)		return -ENOMEM; 	sec->scn = elf_newscn(linker->elf);	if (!sec->scn) {		pr_warn_elf("failed to create SYMTAB section");		return -EINVAL;	} 	sec->shdr = elf64_getshdr(sec->scn);	if (!sec->shdr)		return -EINVAL; 	sec->data = elf_newdata(sec->scn);	if (!sec->data) {		pr_warn_elf("failed to create SYMTAB data");		return -EINVAL;	}	/* Ensure libelf translates byte-order of symbol records */	sec->data->d_type = ELF_T_SYM; 	str_off = strset__add_str(linker->strtab_strs, sec->sec_name);	if (str_off < 0)		return str_off; 	sec->sec_idx = elf_ndxscn(sec->scn);	linker->symtab_sec_idx = sec->sec_idx; 	sec->shdr->sh_name = str_off;	sec->shdr->sh_type = SHT_SYMTAB;	sec->shdr->sh_flags = 0;	sec->shdr->sh_offset = 0;	sec->shdr->sh_link = linker->strtab_sec_idx;	/* sh_info should be one greater than the index of the last local	 * symbol (i.e., binding is STB_LOCAL). But why and who cares?	 */	sec->shdr->sh_info = 0;	sec->shdr->sh_addralign = 8;	sec->shdr->sh_entsize = sizeof(Elf64_Sym); 	/* .BTF */	linker->btf = btf__new_empty();	err = libbpf_get_error(linker->btf);	if (err)		return err; 	/* add the special all-zero symbol */	init_sym = add_new_sym(linker, NULL);	if (!init_sym)		return -EINVAL; 	init_sym->st_name = 0;	init_sym->st_info = 0;	init_sym->st_other = 0;	init_sym->st_shndx = SHN_UNDEF;	init_sym->st_value = 0;	init_sym->st_size = 0; 	return 0;} static int bpf_linker_add_file(struct bpf_linker *linker, int fd,			       const char *filename){	struct src_obj obj = {};	int err = 0; 	obj.filename = filename;	obj.fd = fd; 	err = err ?: linker_load_obj_file(linker, &obj);	err = err ?: linker_append_sec_data(linker, &obj);

Lines 1–500 of 3116.

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t