Commit 29da17c4 authored by Alexei Starovoitov's avatar Alexei Starovoitov

Merge branch 'libbpf: support custom .rodata.*/.data.* sections'

Andrii Nakryiko says:

====================

This patch set refactors internals of libbpf to enable support for multiple
custom .rodata.* and .data.* sections. Each such section is backed by its own
BPF_MAP_TYPE_ARRAY, memory-mappable just like .rodata/.data. This is not
extended to .bss because .bss is not a great name, it is generated by compiler
with name that reflects completely irrelevant historical implementation
details. Given that users have to annotate their variables with
SEC(".data.my_sec") explicitly, standardizing on .rodata. and .data. prefixes
makes more sense and keeps things simpler.

Additionally, this patch set makes it simpler to work with those special
internal maps by allowing to look them up by their full ELF section name.

Patch #1 is a preparatory patch that deprecates one libbpf API and moves
custom logic into libbpf.c, where it's used. This code is later refactored
with the rest of libbpf.c logic to support multiple data section maps.

See individual patches for all the details.

For new custom "dot maps", their full ELF section names are used as the names
that are sent into the kernel. Object name isn't prepended like for
.data/.rodata/.bss. The reason is that with longer custom names, there isn't
much space left for object name anyways. Also, if BTF is supported,
btf_value_type_id points to DATASEC BTF type, which contains full original ELF
name of the section, so tools like bpftool could use that to recover full
name. This patch set doesn't add this logic yet, this is left for follow up
patches.

One interesting possibility that is now open by these changes is that it's
possible to do:

    bpf_trace_printk("My fmt %s", sizeof("My fmt %s"), "blah");

and it will work as expected. I haven't updated libbpf-provided helpers in
bpf_helpers.h for snprintf, seq_printf, and printk, because using
`static const char ___fmt[] = fmt;` trick is still efficient and doesn't fill
out the buffer at runtime (no copying). But we might consider updating them in
the future, especially with the array check that Kumar proposed (see [0]).

  [0] https://lore.kernel.org/bpf/20211012041524.udytbr2xs5wid6x2@apollo.localdomain/

v1->v2:
  - don't prepend object name for new dot maps;
  - add __read_mostly example in selftests (Daniel).
====================
Signed-off-by: default avatarAlexei Starovoitov <ast@kernel.org>
parents b0c7663d 4f2511e1
......@@ -33,6 +33,11 @@ static void sanitize_identifier(char *name)
name[i] = '_';
}
static bool str_has_prefix(const char *str, const char *prefix)
{
return strncmp(str, prefix, strlen(prefix)) == 0;
}
static bool str_has_suffix(const char *str, const char *suffix)
{
size_t i, n1 = strlen(str), n2 = strlen(suffix);
......@@ -67,23 +72,47 @@ static void get_header_guard(char *guard, const char *obj_name)
guard[i] = toupper(guard[i]);
}
static const char *get_map_ident(const struct bpf_map *map)
static bool get_map_ident(const struct bpf_map *map, char *buf, size_t buf_sz)
{
static const char *sfxs[] = { ".data", ".rodata", ".bss", ".kconfig" };
const char *name = bpf_map__name(map);
int i, n;
if (!bpf_map__is_internal(map))
return name;
if (str_has_suffix(name, ".data"))
return "data";
else if (str_has_suffix(name, ".rodata"))
return "rodata";
else if (str_has_suffix(name, ".bss"))
return "bss";
else if (str_has_suffix(name, ".kconfig"))
return "kconfig";
else
return NULL;
if (!bpf_map__is_internal(map)) {
snprintf(buf, buf_sz, "%s", name);
return true;
}
for (i = 0, n = ARRAY_SIZE(sfxs); i < n; i++) {
const char *sfx = sfxs[i], *p;
p = strstr(name, sfx);
if (p) {
snprintf(buf, buf_sz, "%s", p + 1);
sanitize_identifier(buf);
return true;
}
}
return false;
}
static bool get_datasec_ident(const char *sec_name, char *buf, size_t buf_sz)
{
static const char *pfxs[] = { ".data", ".rodata", ".bss", ".kconfig" };
int i, n;
for (i = 0, n = ARRAY_SIZE(pfxs); i < n; i++) {
const char *pfx = pfxs[i];
if (str_has_prefix(sec_name, pfx)) {
snprintf(buf, buf_sz, "%s", sec_name + 1);
sanitize_identifier(buf);
return true;
}
}
return false;
}
static void codegen_btf_dump_printf(void *ctx, const char *fmt, va_list args)
......@@ -100,24 +129,14 @@ static int codegen_datasec_def(struct bpf_object *obj,
const char *sec_name = btf__name_by_offset(btf, sec->name_off);
const struct btf_var_secinfo *sec_var = btf_var_secinfos(sec);
int i, err, off = 0, pad_cnt = 0, vlen = btf_vlen(sec);
const char *sec_ident;
char var_ident[256];
char var_ident[256], sec_ident[256];
bool strip_mods = false;
if (strcmp(sec_name, ".data") == 0) {
sec_ident = "data";
strip_mods = true;
} else if (strcmp(sec_name, ".bss") == 0) {
sec_ident = "bss";
strip_mods = true;
} else if (strcmp(sec_name, ".rodata") == 0) {
sec_ident = "rodata";
strip_mods = true;
} else if (strcmp(sec_name, ".kconfig") == 0) {
sec_ident = "kconfig";
} else {
if (!get_datasec_ident(sec_name, sec_ident, sizeof(sec_ident)))
return 0;
}
if (strcmp(sec_name, ".kconfig") != 0)
strip_mods = true;
printf(" struct %s__%s {\n", obj_name, sec_ident);
for (i = 0; i < vlen; i++, sec_var++) {
......@@ -194,22 +213,61 @@ static int codegen_datasecs(struct bpf_object *obj, const char *obj_name)
struct btf *btf = bpf_object__btf(obj);
int n = btf__get_nr_types(btf);
struct btf_dump *d;
struct bpf_map *map;
const struct btf_type *sec;
char sec_ident[256], map_ident[256];
int i, err = 0;
d = btf_dump__new(btf, NULL, NULL, codegen_btf_dump_printf);
if (IS_ERR(d))
return PTR_ERR(d);
bpf_object__for_each_map(map, obj) {
/* only generate definitions for memory-mapped internal maps */
if (!bpf_map__is_internal(map))
continue;
if (!(bpf_map__def(map)->map_flags & BPF_F_MMAPABLE))
continue;
if (!get_map_ident(map, map_ident, sizeof(map_ident)))
continue;
sec = NULL;
for (i = 1; i <= n; i++) {
const struct btf_type *t = btf__type_by_id(btf, i);
const char *name;
if (!btf_is_datasec(t))
continue;
err = codegen_datasec_def(obj, btf, d, t, obj_name);
name = btf__str_by_offset(btf, t->name_off);
if (!get_datasec_ident(name, sec_ident, sizeof(sec_ident)))
continue;
if (strcmp(sec_ident, map_ident) == 0) {
sec = t;
break;
}
}
/* In some cases (e.g., sections like .rodata.cst16 containing
* compiler allocated string constants only) there will be
* special internal maps with no corresponding DATASEC BTF
* type. In such case, generate empty structs for each such
* map. It will still be memory-mapped and its contents
* accessible from user-space through BPF skeleton.
*/
if (!sec) {
printf(" struct %s__%s {\n", obj_name, map_ident);
printf(" } *%s;\n", map_ident);
} else {
err = codegen_datasec_def(obj, btf, d, sec, obj_name);
if (err)
goto out;
}
}
out:
btf_dump__free(d);
return err;
......@@ -385,6 +443,7 @@ static void codegen_destroy(struct bpf_object *obj, const char *obj_name)
{
struct bpf_program *prog;
struct bpf_map *map;
char ident[256];
codegen("\
\n\
......@@ -405,10 +464,7 @@ static void codegen_destroy(struct bpf_object *obj, const char *obj_name)
}
bpf_object__for_each_map(map, obj) {
const char *ident;
ident = get_map_ident(map);
if (!ident)
if (!get_map_ident(map, ident, sizeof(ident)))
continue;
if (bpf_map__is_internal(map) &&
(bpf_map__def(map)->map_flags & BPF_F_MMAPABLE))
......@@ -432,6 +488,7 @@ static int gen_trace(struct bpf_object *obj, const char *obj_name, const char *h
struct bpf_object_load_attr load_attr = {};
DECLARE_LIBBPF_OPTS(gen_loader_opts, opts);
struct bpf_map *map;
char ident[256];
int err = 0;
err = bpf_object__gen_loader(obj, &opts);
......@@ -477,12 +534,10 @@ static int gen_trace(struct bpf_object *obj, const char *obj_name, const char *h
",
obj_name, opts.data_sz);
bpf_object__for_each_map(map, obj) {
const char *ident;
const void *mmap_data = NULL;
size_t mmap_size = 0;
ident = get_map_ident(map);
if (!ident)
if (!get_map_ident(map, ident, sizeof(ident)))
continue;
if (!bpf_map__is_internal(map) ||
......@@ -544,15 +599,15 @@ static int gen_trace(struct bpf_object *obj, const char *obj_name, const char *h
return err; \n\
", obj_name);
bpf_object__for_each_map(map, obj) {
const char *ident, *mmap_flags;
const char *mmap_flags;
ident = get_map_ident(map);
if (!ident)
if (!get_map_ident(map, ident, sizeof(ident)))
continue;
if (!bpf_map__is_internal(map) ||
!(bpf_map__def(map)->map_flags & BPF_F_MMAPABLE))
continue;
if (bpf_map__def(map)->map_flags & BPF_F_RDONLY_PROG)
mmap_flags = "PROT_READ";
else
......@@ -602,7 +657,8 @@ static int do_skeleton(int argc, char **argv)
DECLARE_LIBBPF_OPTS(bpf_object_open_opts, opts);
char obj_name[MAX_OBJ_NAME_LEN] = "", *obj_data;
struct bpf_object *obj = NULL;
const char *file, *ident;
const char *file;
char ident[256];
struct bpf_program *prog;
int fd, err = -1;
struct bpf_map *map;
......@@ -673,8 +729,7 @@ static int do_skeleton(int argc, char **argv)
}
bpf_object__for_each_map(map, obj) {
ident = get_map_ident(map);
if (!ident) {
if (!get_map_ident(map, ident, sizeof(ident))) {
p_err("ignoring unrecognized internal map '%s'...",
bpf_map__name(map));
continue;
......@@ -727,8 +782,7 @@ static int do_skeleton(int argc, char **argv)
if (map_cnt) {
printf("\tstruct {\n");
bpf_object__for_each_map(map, obj) {
ident = get_map_ident(map);
if (!ident)
if (!get_map_ident(map, ident, sizeof(ident)))
continue;
if (use_loader)
printf("\t\tstruct bpf_map_desc %s;\n", ident);
......@@ -897,9 +951,7 @@ static int do_skeleton(int argc, char **argv)
);
i = 0;
bpf_object__for_each_map(map, obj) {
ident = get_map_ident(map);
if (!ident)
if (!get_map_ident(map, ident, sizeof(ident)))
continue;
codegen("\
......
......@@ -1107,99 +1107,6 @@ struct btf *btf__parse_split(const char *path, struct btf *base_btf)
return libbpf_ptr(btf_parse(path, base_btf, NULL));
}
static int compare_vsi_off(const void *_a, const void *_b)
{
const struct btf_var_secinfo *a = _a;
const struct btf_var_secinfo *b = _b;
return a->offset - b->offset;
}
static int btf_fixup_datasec(struct bpf_object *obj, struct btf *btf,
struct btf_type *t)
{
__u32 size = 0, off = 0, i, vars = btf_vlen(t);
const char *name = btf__name_by_offset(btf, t->name_off);
const struct btf_type *t_var;
struct btf_var_secinfo *vsi;
const struct btf_var *var;
int ret;
if (!name) {
pr_debug("No name found in string section for DATASEC kind.\n");
return -ENOENT;
}
/* .extern datasec size and var offsets were set correctly during
* extern collection step, so just skip straight to sorting variables
*/
if (t->size)
goto sort_vars;
ret = bpf_object__section_size(obj, name, &size);
if (ret || !size || (t->size && t->size != size)) {
pr_debug("Invalid size for section %s: %u bytes\n", name, size);
return -ENOENT;
}
t->size = size;
for (i = 0, vsi = btf_var_secinfos(t); i < vars; i++, vsi++) {
t_var = btf__type_by_id(btf, vsi->type);
var = btf_var(t_var);
if (!btf_is_var(t_var)) {
pr_debug("Non-VAR type seen in section %s\n", name);
return -EINVAL;
}
if (var->linkage == BTF_VAR_STATIC)
continue;
name = btf__name_by_offset(btf, t_var->name_off);
if (!name) {
pr_debug("No name found in string section for VAR kind\n");
return -ENOENT;
}
ret = bpf_object__variable_offset(obj, name, &off);
if (ret) {
pr_debug("No offset found in symbol table for VAR %s\n",
name);
return -ENOENT;
}
vsi->offset = off;
}
sort_vars:
qsort(btf_var_secinfos(t), vars, sizeof(*vsi), compare_vsi_off);
return 0;
}
int btf__finalize_data(struct bpf_object *obj, struct btf *btf)
{
int err = 0;
__u32 i;
for (i = 1; i <= btf->nr_types; i++) {
struct btf_type *t = btf_type_by_id(btf, i);
/* Loader needs to fix up some of the things compiler
* couldn't get its hands on while emitting BTF. This
* is section size and global variable offset. We use
* the info from the ELF itself for this purpose.
*/
if (btf_is_datasec(t)) {
err = btf_fixup_datasec(obj, btf, t);
if (err)
break;
}
}
return libbpf_err(err);
}
static void *btf_get_raw_data(const struct btf *btf, __u32 *size, bool swap_endian);
int btf__load_into_kernel(struct btf *btf)
......
......@@ -123,6 +123,7 @@ LIBBPF_API struct btf *btf__load_from_kernel_by_id_split(__u32 id, struct btf *b
LIBBPF_DEPRECATED_SINCE(0, 6, "use btf__load_from_kernel_by_id instead")
LIBBPF_API int btf__get_from_id(__u32 id, struct btf **btf);
LIBBPF_DEPRECATED_SINCE(0, 6, "intended for internal libbpf use only")
LIBBPF_API int btf__finalize_data(struct bpf_object *obj, struct btf *btf);
LIBBPF_DEPRECATED_SINCE(0, 6, "use btf__load_into_kernel instead")
LIBBPF_API int btf__load(struct btf *btf);
......
......@@ -370,15 +370,14 @@ enum libbpf_map_type {
LIBBPF_MAP_KCONFIG,
};
static const char * const libbpf_type_to_btf_name[] = {
[LIBBPF_MAP_DATA] = DATA_SEC,
[LIBBPF_MAP_BSS] = BSS_SEC,
[LIBBPF_MAP_RODATA] = RODATA_SEC,
[LIBBPF_MAP_KCONFIG] = KCONFIG_SEC,
};
struct bpf_map {
char *name;
/* real_name is defined for special internal maps (.rodata*,
* .data*, .bss, .kconfig) and preserves their original ELF section
* name. This is important to be be able to find corresponding BTF
* DATASEC information.
*/
char *real_name;
int fd;
int sec_idx;
size_t sec_offset;
......@@ -462,6 +461,40 @@ struct module_btf {
int fd_array_idx;
};
enum sec_type {
SEC_UNUSED = 0,
SEC_RELO,
SEC_BSS,
SEC_DATA,
SEC_RODATA,
};
struct elf_sec_desc {
enum sec_type sec_type;
Elf64_Shdr *shdr;
Elf_Data *data;
};
struct elf_state {
int fd;
const void *obj_buf;
size_t obj_buf_sz;
Elf *elf;
Elf64_Ehdr *ehdr;
Elf_Data *symbols;
Elf_Data *st_ops_data;
size_t shstrndx; /* section index for section name strings */
size_t strtabidx;
struct elf_sec_desc *secs;
int sec_cnt;
int maps_shndx;
int btf_maps_shndx;
__u32 btf_maps_sec_btf_id;
int text_shndx;
int symbols_shndx;
int st_ops_shndx;
};
struct bpf_object {
char name[BPF_OBJ_NAME_LEN];
char license[64];
......@@ -477,47 +510,17 @@ struct bpf_object {
struct extern_desc *externs;
int nr_extern;
int kconfig_map_idx;
int rodata_map_idx;
bool loaded;
bool has_subcalls;
bool has_rodata;
struct bpf_gen *gen_loader;
/* Information when doing ELF related work. Only valid if efile.elf is not NULL */
struct elf_state efile;
/*
* Information when doing elf related work. Only valid if fd
* is valid.
*/
struct {
int fd;
const void *obj_buf;
size_t obj_buf_sz;
Elf *elf;
GElf_Ehdr ehdr;
Elf_Data *symbols;
Elf_Data *data;
Elf_Data *rodata;
Elf_Data *bss;
Elf_Data *st_ops_data;
size_t shstrndx; /* section index for section name strings */
size_t strtabidx;
struct {
GElf_Shdr shdr;
Elf_Data *data;
} *reloc_sects;
int nr_reloc_sects;
int maps_shndx;
int btf_maps_shndx;
__u32 btf_maps_sec_btf_id;
int text_shndx;
int symbols_shndx;
int data_shndx;
int rodata_shndx;
int bss_shndx;
int st_ops_shndx;
} efile;
/*
* All loaded bpf_object is linked in a list, which is
* All loaded bpf_object are linked in a list, which is
* hidden to caller. bpf_objects__<func> handlers deal with
* all objects.
*/
......@@ -551,15 +554,16 @@ struct bpf_object {
char path[];
};
#define obj_elf_valid(o) ((o)->efile.elf)
static const char *elf_sym_str(const struct bpf_object *obj, size_t off);
static const char *elf_sec_str(const struct bpf_object *obj, size_t off);
static Elf_Scn *elf_sec_by_idx(const struct bpf_object *obj, size_t idx);
static Elf_Scn *elf_sec_by_name(const struct bpf_object *obj, const char *name);
static int elf_sec_hdr(const struct bpf_object *obj, Elf_Scn *scn, GElf_Shdr *hdr);
static Elf64_Shdr *elf_sec_hdr(const struct bpf_object *obj, Elf_Scn *scn);
static const char *elf_sec_name(const struct bpf_object *obj, Elf_Scn *scn);
static Elf_Data *elf_sec_data(const struct bpf_object *obj, Elf_Scn *scn);
static Elf64_Sym *elf_sym_by_idx(const struct bpf_object *obj, size_t idx);
static Elf64_Rel *elf_rel_by_idx(Elf_Data *data, size_t idx);
void bpf_program__unload(struct bpf_program *prog)
{
......@@ -701,25 +705,25 @@ bpf_object__add_programs(struct bpf_object *obj, Elf_Data *sec_data,
size_t sec_sz = sec_data->d_size, sec_off, prog_sz, nr_syms;
int nr_progs, err, i;
const char *name;
GElf_Sym sym;
Elf64_Sym *sym;
progs = obj->programs;
nr_progs = obj->nr_programs;
nr_syms = symbols->d_size / sizeof(GElf_Sym);
nr_syms = symbols->d_size / sizeof(Elf64_Sym);
sec_off = 0;
for (i = 0; i < nr_syms; i++) {
if (!gelf_getsym(symbols, i, &sym))
continue;
if (sym.st_shndx != sec_idx)
sym = elf_sym_by_idx(obj, i);
if (sym->st_shndx != sec_idx)
continue;
if (GELF_ST_TYPE(sym.st_info) != STT_FUNC)
if (ELF64_ST_TYPE(sym->st_info) != STT_FUNC)
continue;
prog_sz = sym.st_size;
sec_off = sym.st_value;
prog_sz = sym->st_size;
sec_off = sym->st_value;
name = elf_sym_str(obj, sym.st_name);
name = elf_sym_str(obj, sym->st_name);
if (!name) {
pr_warn("sec '%s': failed to get symbol name for offset %zu\n",
sec_name, sec_off);
......@@ -732,7 +736,7 @@ bpf_object__add_programs(struct bpf_object *obj, Elf_Data *sec_data,
return -LIBBPF_ERRNO__FORMAT;
}
if (sec_idx != obj->efile.text_shndx && GELF_ST_BIND(sym.st_info) == STB_LOCAL) {
if (sec_idx != obj->efile.text_shndx && ELF64_ST_BIND(sym->st_info) == STB_LOCAL) {
pr_warn("sec '%s': program '%s' is static and not supported\n", sec_name, name);
return -ENOTSUP;
}
......@@ -765,9 +769,9 @@ bpf_object__add_programs(struct bpf_object *obj, Elf_Data *sec_data,
* as static to enable more permissive BPF verification mode
* with more outside context available to BPF verifier
*/
if (GELF_ST_BIND(sym.st_info) != STB_LOCAL
&& (GELF_ST_VISIBILITY(sym.st_other) == STV_HIDDEN
|| GELF_ST_VISIBILITY(sym.st_other) == STV_INTERNAL))
if (ELF64_ST_BIND(sym->st_info) != STB_LOCAL
&& (ELF64_ST_VISIBILITY(sym->st_other) == STV_HIDDEN
|| ELF64_ST_VISIBILITY(sym->st_other) == STV_INTERNAL))
prog->mark_btf_static = true;
nr_progs++;
......@@ -1168,12 +1172,8 @@ static struct bpf_object *bpf_object__new(const char *path,
obj->efile.obj_buf_sz = obj_buf_sz;
obj->efile.maps_shndx = -1;
obj->efile.btf_maps_shndx = -1;
obj->efile.data_shndx = -1;
obj->efile.rodata_shndx = -1;
obj->efile.bss_shndx = -1;
obj->efile.st_ops_shndx = -1;
obj->kconfig_map_idx = -1;
obj->rodata_map_idx = -1;
obj->kern_version = get_kernel_version();
obj->loaded = false;
......@@ -1185,7 +1185,7 @@ static struct bpf_object *bpf_object__new(const char *path,
static void bpf_object__elf_finish(struct bpf_object *obj)
{
if (!obj_elf_valid(obj))
if (!obj->efile.elf)
return;
if (obj->efile.elf) {
......@@ -1193,13 +1193,10 @@ static void bpf_object__elf_finish(struct bpf_object *obj)
obj->efile.elf = NULL;
}
obj->efile.symbols = NULL;
obj->efile.data = NULL;
obj->efile.rodata = NULL;
obj->efile.bss = NULL;
obj->efile.st_ops_data = NULL;
zfree(&obj->efile.reloc_sects);
obj->efile.nr_reloc_sects = 0;
zfree(&obj->efile.secs);
obj->efile.sec_cnt = 0;
zclose(obj->efile.fd);
obj->efile.obj_buf = NULL;
obj->efile.obj_buf_sz = 0;
......@@ -1207,10 +1204,11 @@ static void bpf_object__elf_finish(struct bpf_object *obj)
static int bpf_object__elf_init(struct bpf_object *obj)
{
Elf64_Ehdr *ehdr;
int err = 0;
GElf_Ehdr *ep;
Elf *elf;
if (obj_elf_valid(obj)) {
if (obj->efile.elf) {
pr_warn("elf: init internal error\n");
return -LIBBPF_ERRNO__LIBELF;
}
......@@ -1220,8 +1218,7 @@ static int bpf_object__elf_init(struct bpf_object *obj)
* obj_buf should have been validated by
* bpf_object__open_buffer().
*/
obj->efile.elf = elf_memory((char *)obj->efile.obj_buf,
obj->efile.obj_buf_sz);
elf = elf_memory((char *)obj->efile.obj_buf, obj->efile.obj_buf_sz);
} else {
obj->efile.fd = open(obj->path, O_RDONLY);
if (obj->efile.fd < 0) {
......@@ -1233,23 +1230,37 @@ static int bpf_object__elf_init(struct bpf_object *obj)
return err;
}
obj->efile.elf = elf_begin(obj->efile.fd, ELF_C_READ_MMAP, NULL);
elf = elf_begin(obj->efile.fd, ELF_C_READ_MMAP, NULL);
}
if (!obj->efile.elf) {
if (!elf) {
pr_warn("elf: failed to open %s as ELF file: %s\n", obj->path, elf_errmsg(-1));
err = -LIBBPF_ERRNO__LIBELF;
goto errout;
}
if (!gelf_getehdr(obj->efile.elf, &obj->efile.ehdr)) {
obj->efile.elf = elf;
if (elf_kind(elf) != ELF_K_ELF) {
err = -LIBBPF_ERRNO__FORMAT;
pr_warn("elf: '%s' is not a proper ELF object\n", obj->path);
goto errout;
}
if (gelf_getclass(elf) != ELFCLASS64) {
err = -LIBBPF_ERRNO__FORMAT;
pr_warn("elf: '%s' is not a 64-bit ELF object\n", obj->path);
goto errout;
}
obj->efile.ehdr = ehdr = elf64_getehdr(elf);
if (!obj->efile.ehdr) {
pr_warn("elf: failed to get ELF header from %s: %s\n", obj->path, elf_errmsg(-1));
err = -LIBBPF_ERRNO__FORMAT;
goto errout;
}
ep = &obj->efile.ehdr;
if (elf_getshdrstrndx(obj->efile.elf, &obj->efile.shstrndx)) {
if (elf_getshdrstrndx(elf, &obj->efile.shstrndx)) {
pr_warn("elf: failed to get section names section index for %s: %s\n",
obj->path, elf_errmsg(-1));
err = -LIBBPF_ERRNO__FORMAT;
......@@ -1257,7 +1268,7 @@ static int bpf_object__elf_init(struct bpf_object *obj)
}
/* Elf is corrupted/truncated, avoid calling elf_strptr. */
if (!elf_rawdata(elf_getscn(obj->efile.elf, obj->efile.shstrndx), NULL)) {
if (!elf_rawdata(elf_getscn(elf, obj->efile.shstrndx), NULL)) {
pr_warn("elf: failed to get section names strings from %s: %s\n",
obj->path, elf_errmsg(-1));
err = -LIBBPF_ERRNO__FORMAT;
......@@ -1265,8 +1276,7 @@ static int bpf_object__elf_init(struct bpf_object *obj)
}
/* Old LLVM set e_machine to EM_NONE */
if (ep->e_type != ET_REL ||
(ep->e_machine && ep->e_machine != EM_BPF)) {
if (ehdr->e_type != ET_REL || (ehdr->e_machine && ehdr->e_machine != EM_BPF)) {
pr_warn("elf: %s is not a valid eBPF object file\n", obj->path);
err = -LIBBPF_ERRNO__FORMAT;
goto errout;
......@@ -1281,10 +1291,10 @@ static int bpf_object__elf_init(struct bpf_object *obj)
static int bpf_object__check_endianness(struct bpf_object *obj)
{
#if __BYTE_ORDER == __LITTLE_ENDIAN
if (obj->efile.ehdr.e_ident[EI_DATA] == ELFDATA2LSB)
if (obj->efile.ehdr->e_ident[EI_DATA] == ELFDATA2LSB)
return 0;
#elif __BYTE_ORDER == __BIG_ENDIAN
if (obj->efile.ehdr.e_ident[EI_DATA] == ELFDATA2MSB)
if (obj->efile.ehdr->e_ident[EI_DATA] == ELFDATA2MSB)
return 0;
#else
# error "Unrecognized __BYTE_ORDER__"
......@@ -1324,41 +1334,27 @@ static bool bpf_map_type__is_map_in_map(enum bpf_map_type type)
return false;
}
int bpf_object__section_size(const struct bpf_object *obj, const char *name,
__u32 *size)
static int find_elf_sec_sz(const struct bpf_object *obj, const char *name, __u32 *size)
{
int ret = -ENOENT;
Elf_Data *data;
Elf_Scn *scn;
*size = 0;
if (!name) {
if (!name)
return -EINVAL;
} else if (!strcmp(name, DATA_SEC)) {
if (obj->efile.data)
*size = obj->efile.data->d_size;
} else if (!strcmp(name, BSS_SEC)) {
if (obj->efile.bss)
*size = obj->efile.bss->d_size;
} else if (!strcmp(name, RODATA_SEC)) {
if (obj->efile.rodata)
*size = obj->efile.rodata->d_size;
} else if (!strcmp(name, STRUCT_OPS_SEC)) {
if (obj->efile.st_ops_data)
*size = obj->efile.st_ops_data->d_size;
} else {
Elf_Scn *scn = elf_sec_by_name(obj, name);
Elf_Data *data = elf_sec_data(obj, scn);
scn = elf_sec_by_name(obj, name);
data = elf_sec_data(obj, scn);
if (data) {
ret = 0; /* found it */
*size = data->d_size;
}
}
return *size ? 0 : ret;
}
int bpf_object__variable_offset(const struct bpf_object *obj, const char *name,
__u32 *off)
static int find_elf_var_offset(const struct bpf_object *obj, const char *name, __u32 *off)
{
Elf_Data *symbols = obj->efile.symbols;
const char *sname;
......@@ -1367,23 +1363,20 @@ int bpf_object__variable_offset(const struct bpf_object *obj, const char *name,
if (!name || !off)
return -EINVAL;
for (si = 0; si < symbols->d_size / sizeof(GElf_Sym); si++) {
GElf_Sym sym;
for (si = 0; si < symbols->d_size / sizeof(Elf64_Sym); si++) {
Elf64_Sym *sym = elf_sym_by_idx(obj, si);
if (!gelf_getsym(symbols, si, &sym))
continue;
if (GELF_ST_BIND(sym.st_info) != STB_GLOBAL ||
GELF_ST_TYPE(sym.st_info) != STT_OBJECT)
if (ELF64_ST_BIND(sym->st_info) != STB_GLOBAL ||
ELF64_ST_TYPE(sym->st_info) != STT_OBJECT)
continue;
sname = elf_sym_str(obj, sym.st_name);
sname = elf_sym_str(obj, sym->st_name);
if (!sname) {
pr_warn("failed to get sym name string for var %s\n",
name);
pr_warn("failed to get sym name string for var %s\n", name);
return -EIO;
}
if (strcmp(name, sname) == 0) {
*off = sym.st_value;
*off = sym->st_value;
return 0;
}
}
......@@ -1435,17 +1428,55 @@ static size_t bpf_map_mmap_sz(const struct bpf_map *map)
return map_sz;
}
static char *internal_map_name(struct bpf_object *obj,
enum libbpf_map_type type)
static char *internal_map_name(struct bpf_object *obj, const char *real_name)
{
char map_name[BPF_OBJ_NAME_LEN], *p;
const char *sfx = libbpf_type_to_btf_name[type];
int sfx_len = max((size_t)7, strlen(sfx));
int pfx_len = min((size_t)BPF_OBJ_NAME_LEN - sfx_len - 1,
strlen(obj->name));
int pfx_len, sfx_len = max((size_t)7, strlen(real_name));
/* This is one of the more confusing parts of libbpf for various
* reasons, some of which are historical. The original idea for naming
* internal names was to include as much of BPF object name prefix as
* possible, so that it can be distinguished from similar internal
* maps of a different BPF object.
* As an example, let's say we have bpf_object named 'my_object_name'
* and internal map corresponding to '.rodata' ELF section. The final
* map name advertised to user and to the kernel will be
* 'my_objec.rodata', taking first 8 characters of object name and
* entire 7 characters of '.rodata'.
* Somewhat confusingly, if internal map ELF section name is shorter
* than 7 characters, e.g., '.bss', we still reserve 7 characters
* for the suffix, even though we only have 4 actual characters, and
* resulting map will be called 'my_objec.bss', not even using all 15
* characters allowed by the kernel. Oh well, at least the truncated
* object name is somewhat consistent in this case. But if the map
* name is '.kconfig', we'll still have entirety of '.kconfig' added
* (8 chars) and thus will be left with only first 7 characters of the
* object name ('my_obje'). Happy guessing, user, that the final map
* name will be "my_obje.kconfig".
* Now, with libbpf starting to support arbitrarily named .rodata.*
* and .data.* data sections, it's possible that ELF section name is
* longer than allowed 15 chars, so we now need to be careful to take
* only up to 15 first characters of ELF name, taking no BPF object
* name characters at all. So '.rodata.abracadabra' will result in
* '.rodata.abracad' kernel and user-visible name.
* We need to keep this convoluted logic intact for .data, .bss and
* .rodata maps, but for new custom .data.custom and .rodata.custom
* maps we use their ELF names as is, not prepending bpf_object name
* in front. We still need to truncate them to 15 characters for the
* kernel. Full name can be recovered for such maps by using DATASEC
* BTF type associated with such map's value type, though.
*/
if (sfx_len >= BPF_OBJ_NAME_LEN)
sfx_len = BPF_OBJ_NAME_LEN - 1;
/* if there are two or more dots in map name, it's a custom dot map */
if (strchr(real_name + 1, '.') != NULL)
pfx_len = 0;
else
pfx_len = min((size_t)BPF_OBJ_NAME_LEN - sfx_len - 1, strlen(obj->name));
snprintf(map_name, sizeof(map_name), "%.*s%.*s", pfx_len, obj->name,
sfx_len, libbpf_type_to_btf_name[type]);
sfx_len, real_name);
/* sanitise map name to characters allowed by kernel */
for (p = map_name; *p && p < map_name + sizeof(map_name); p++)
......@@ -1457,7 +1488,7 @@ static char *internal_map_name(struct bpf_object *obj,
static int
bpf_object__init_internal_map(struct bpf_object *obj, enum libbpf_map_type type,
int sec_idx, void *data, size_t data_sz)
const char *real_name, int sec_idx, void *data, size_t data_sz)
{
struct bpf_map_def *def;
struct bpf_map *map;
......@@ -1470,9 +1501,11 @@ bpf_object__init_internal_map(struct bpf_object *obj, enum libbpf_map_type type,
map->libbpf_type = type;
map->sec_idx = sec_idx;
map->sec_offset = 0;
map->name = internal_map_name(obj, type);
if (!map->name) {
pr_warn("failed to alloc map name\n");
map->real_name = strdup(real_name);
map->name = internal_map_name(obj, real_name);
if (!map->real_name || !map->name) {
zfree(&map->real_name);
zfree(&map->name);
return -ENOMEM;
}
......@@ -1495,6 +1528,7 @@ bpf_object__init_internal_map(struct bpf_object *obj, enum libbpf_map_type type,
map->mmaped = NULL;
pr_warn("failed to alloc map '%s' content buffer: %d\n",
map->name, err);
zfree(&map->real_name);
zfree(&map->name);
return err;
}
......@@ -1508,34 +1542,43 @@ bpf_object__init_internal_map(struct bpf_object *obj, enum libbpf_map_type type,
static int bpf_object__init_global_data_maps(struct bpf_object *obj)
{
int err;
struct elf_sec_desc *sec_desc;
const char *sec_name;
int err = 0, sec_idx;
/*
* Populate obj->maps with libbpf internal maps.
*/
if (obj->efile.data_shndx >= 0) {
for (sec_idx = 1; sec_idx < obj->efile.sec_cnt; sec_idx++) {
sec_desc = &obj->efile.secs[sec_idx];
switch (sec_desc->sec_type) {
case SEC_DATA:
sec_name = elf_sec_name(obj, elf_sec_by_idx(obj, sec_idx));
err = bpf_object__init_internal_map(obj, LIBBPF_MAP_DATA,
obj->efile.data_shndx,
obj->efile.data->d_buf,
obj->efile.data->d_size);
if (err)
return err;
}
if (obj->efile.rodata_shndx >= 0) {
sec_name, sec_idx,
sec_desc->data->d_buf,
sec_desc->data->d_size);
break;
case SEC_RODATA:
obj->has_rodata = true;
sec_name = elf_sec_name(obj, elf_sec_by_idx(obj, sec_idx));
err = bpf_object__init_internal_map(obj, LIBBPF_MAP_RODATA,
obj->efile.rodata_shndx,
obj->efile.rodata->d_buf,
obj->efile.rodata->d_size);
if (err)
return err;
obj->rodata_map_idx = obj->nr_maps - 1;
}
if (obj->efile.bss_shndx >= 0) {
sec_name, sec_idx,
sec_desc->data->d_buf,
sec_desc->data->d_size);
break;
case SEC_BSS:
sec_name = elf_sec_name(obj, elf_sec_by_idx(obj, sec_idx));
err = bpf_object__init_internal_map(obj, LIBBPF_MAP_BSS,
obj->efile.bss_shndx,
sec_name, sec_idx,
NULL,
obj->efile.bss->d_size);
sec_desc->data->d_size);
break;
default:
/* skip */
break;
}
if (err)
return err;
}
......@@ -1832,7 +1875,7 @@ static int bpf_object__init_kconfig_map(struct bpf_object *obj)
map_sz = last_ext->kcfg.data_off + last_ext->kcfg.sz;
err = bpf_object__init_internal_map(obj, LIBBPF_MAP_KCONFIG,
obj->efile.symbols_shndx,
".kconfig", obj->efile.symbols_shndx,
NULL, map_sz);
if (err)
return err;
......@@ -1870,15 +1913,13 @@ static int bpf_object__init_user_maps(struct bpf_object *obj, bool strict)
*
* TODO: Detect array of map and report error.
*/
nr_syms = symbols->d_size / sizeof(GElf_Sym);
nr_syms = symbols->d_size / sizeof(Elf64_Sym);
for (i = 0; i < nr_syms; i++) {
GElf_Sym sym;
Elf64_Sym *sym = elf_sym_by_idx(obj, i);
if (!gelf_getsym(symbols, i, &sym))
continue;
if (sym.st_shndx != obj->efile.maps_shndx)
if (sym->st_shndx != obj->efile.maps_shndx)
continue;
if (GELF_ST_TYPE(sym.st_info) == STT_SECTION)
if (ELF64_ST_TYPE(sym->st_info) == STT_SECTION)
continue;
nr_maps++;
}
......@@ -1895,40 +1936,38 @@ static int bpf_object__init_user_maps(struct bpf_object *obj, bool strict)
/* Fill obj->maps using data in "maps" section. */
for (i = 0; i < nr_syms; i++) {
GElf_Sym sym;
Elf64_Sym *sym = elf_sym_by_idx(obj, i);
const char *map_name;
struct bpf_map_def *def;
struct bpf_map *map;
if (!gelf_getsym(symbols, i, &sym))
if (sym->st_shndx != obj->efile.maps_shndx)
continue;
if (sym.st_shndx != obj->efile.maps_shndx)
continue;
if (GELF_ST_TYPE(sym.st_info) == STT_SECTION)
if (ELF64_ST_TYPE(sym->st_info) == STT_SECTION)
continue;
map = bpf_object__add_map(obj);
if (IS_ERR(map))
return PTR_ERR(map);
map_name = elf_sym_str(obj, sym.st_name);
map_name = elf_sym_str(obj, sym->st_name);
if (!map_name) {
pr_warn("failed to get map #%d name sym string for obj %s\n",
i, obj->path);
return -LIBBPF_ERRNO__FORMAT;
}
if (GELF_ST_BIND(sym.st_info) == STB_LOCAL) {
if (ELF64_ST_BIND(sym->st_info) == STB_LOCAL) {
pr_warn("map '%s' (legacy): static maps are not supported\n", map_name);
return -ENOTSUP;
}
map->libbpf_type = LIBBPF_MAP_UNSPEC;
map->sec_idx = sym.st_shndx;
map->sec_offset = sym.st_value;
map->sec_idx = sym->st_shndx;
map->sec_offset = sym->st_value;
pr_debug("map '%s' (legacy): at sec_idx %d, offset %zu.\n",
map_name, map->sec_idx, map->sec_offset);
if (sym.st_value + map_def_sz > data->d_size) {
if (sym->st_value + map_def_sz > data->d_size) {
pr_warn("corrupted maps section in %s: last map \"%s\" too small\n",
obj->path, map_name);
return -EINVAL;
......@@ -1936,11 +1975,11 @@ static int bpf_object__init_user_maps(struct bpf_object *obj, bool strict)
map->name = strdup(map_name);
if (!map->name) {
pr_warn("failed to alloc map name\n");
pr_warn("map '%s': failed to alloc map name\n", map_name);
return -ENOMEM;
}
pr_debug("map %d is \"%s\"\n", i, map->name);
def = (struct bpf_map_def *)(data->d_buf + sym.st_value);
def = (struct bpf_map_def *)(data->d_buf + sym->st_value);
/*
* If the definition of the map in the object file fits in
* bpf_map_def, copy it. Any extra fields in our version
......@@ -2510,12 +2549,13 @@ static int bpf_object__init_maps(struct bpf_object *obj,
static bool section_have_execinstr(struct bpf_object *obj, int idx)
{
GElf_Shdr sh;
Elf64_Shdr *sh;
if (elf_sec_hdr(obj, elf_sec_by_idx(obj, idx), &sh))
sh = elf_sec_hdr(obj, elf_sec_by_idx(obj, idx));
if (!sh)
return false;
return sh.sh_flags & SHF_EXECINSTR;
return sh->sh_flags & SHF_EXECINSTR;
}
static bool btf_needs_sanitization(struct bpf_object *obj)
......@@ -2650,6 +2690,104 @@ static int bpf_object__init_btf(struct bpf_object *obj,
return 0;
}
static int compare_vsi_off(const void *_a, const void *_b)
{
const struct btf_var_secinfo *a = _a;
const struct btf_var_secinfo *b = _b;
return a->offset - b->offset;
}
static int btf_fixup_datasec(struct bpf_object *obj, struct btf *btf,
struct btf_type *t)
{
__u32 size = 0, off = 0, i, vars = btf_vlen(t);
const char *name = btf__name_by_offset(btf, t->name_off);
const struct btf_type *t_var;
struct btf_var_secinfo *vsi;
const struct btf_var *var;
int ret;
if (!name) {
pr_debug("No name found in string section for DATASEC kind.\n");
return -ENOENT;
}
/* .extern datasec size and var offsets were set correctly during
* extern collection step, so just skip straight to sorting variables
*/
if (t->size)
goto sort_vars;
ret = find_elf_sec_sz(obj, name, &size);
if (ret || !size || (t->size && t->size != size)) {
pr_debug("Invalid size for section %s: %u bytes\n", name, size);
return -ENOENT;
}
t->size = size;
for (i = 0, vsi = btf_var_secinfos(t); i < vars; i++, vsi++) {
t_var = btf__type_by_id(btf, vsi->type);
var = btf_var(t_var);
if (!btf_is_var(t_var)) {
pr_debug("Non-VAR type seen in section %s\n", name);
return -EINVAL;
}
if (var->linkage == BTF_VAR_STATIC)
continue;
name = btf__name_by_offset(btf, t_var->name_off);
if (!name) {
pr_debug("No name found in string section for VAR kind\n");
return -ENOENT;
}
ret = find_elf_var_offset(obj, name, &off);
if (ret) {
pr_debug("No offset found in symbol table for VAR %s\n",
name);
return -ENOENT;
}
vsi->offset = off;
}
sort_vars:
qsort(btf_var_secinfos(t), vars, sizeof(*vsi), compare_vsi_off);
return 0;
}
static int btf_finalize_data(struct bpf_object *obj, struct btf *btf)
{
int err = 0;
__u32 i, n = btf__get_nr_types(btf);
for (i = 1; i <= n; i++) {
struct btf_type *t = btf_type_by_id(btf, i);
/* Loader needs to fix up some of the things compiler
* couldn't get its hands on while emitting BTF. This
* is section size and global variable offset. We use
* the info from the ELF itself for this purpose.
*/
if (btf_is_datasec(t)) {
err = btf_fixup_datasec(obj, btf, t);
if (err)
break;
}
}
return libbpf_err(err);
}
int btf__finalize_data(struct bpf_object *obj, struct btf *btf)
{
return btf_finalize_data(obj, btf);
}
static int bpf_object__finalize_btf(struct bpf_object *obj)
{
int err;
......@@ -2657,7 +2795,7 @@ static int bpf_object__finalize_btf(struct bpf_object *obj)
if (!obj->btf)
return 0;
err = btf__finalize_data(obj, obj->btf);
err = btf_finalize_data(obj, obj->btf);
if (err) {
pr_warn("Error finalizing %s: %d.\n", BTF_ELF_SEC, err);
return err;
......@@ -2893,32 +3031,36 @@ static Elf_Scn *elf_sec_by_name(const struct bpf_object *obj, const char *name)
return NULL;
}
static int elf_sec_hdr(const struct bpf_object *obj, Elf_Scn *scn, GElf_Shdr *hdr)
static Elf64_Shdr *elf_sec_hdr(const struct bpf_object *obj, Elf_Scn *scn)
{
Elf64_Shdr *shdr;
if (!scn)
return -EINVAL;
return NULL;
if (gelf_getshdr(scn, hdr) != hdr) {
shdr = elf64_getshdr(scn);
if (!shdr) {
pr_warn("elf: failed to get section(%zu) header from %s: %s\n",
elf_ndxscn(scn), obj->path, elf_errmsg(-1));
return -EINVAL;
return NULL;
}
return 0;
return shdr;
}
static const char *elf_sec_name(const struct bpf_object *obj, Elf_Scn *scn)
{
const char *name;
GElf_Shdr sh;
Elf64_Shdr *sh;
if (!scn)
return NULL;
if (elf_sec_hdr(obj, scn, &sh))
sh = elf_sec_hdr(obj, scn);
if (!sh)
return NULL;
name = elf_sec_str(obj, sh.sh_name);
name = elf_sec_str(obj, sh->sh_name);
if (!name) {
pr_warn("elf: failed to get section(%zu) name from %s: %s\n",
elf_ndxscn(scn), obj->path, elf_errmsg(-1));
......@@ -2946,13 +3088,29 @@ static Elf_Data *elf_sec_data(const struct bpf_object *obj, Elf_Scn *scn)
return data;
}
static Elf64_Sym *elf_sym_by_idx(const struct bpf_object *obj, size_t idx)
{
if (idx >= obj->efile.symbols->d_size / sizeof(Elf64_Sym))
return NULL;
return (Elf64_Sym *)obj->efile.symbols->d_buf + idx;
}
static Elf64_Rel *elf_rel_by_idx(Elf_Data *data, size_t idx)
{
if (idx >= data->d_size / sizeof(Elf64_Rel))
return NULL;
return (Elf64_Rel *)data->d_buf + idx;
}
static bool is_sec_name_dwarf(const char *name)
{
/* approximation, but the actual list is too long */
return str_has_pfx(name, ".debug_");
}
static bool ignore_elf_section(GElf_Shdr *hdr, const char *name)
static bool ignore_elf_section(Elf64_Shdr *hdr, const char *name)
{
/* no special handling of .strtab */
if (hdr->sh_type == SHT_STRTAB)
......@@ -3000,6 +3158,7 @@ static int cmp_progs(const void *_a, const void *_b)
static int bpf_object__elf_collect(struct bpf_object *obj)
{
struct elf_sec_desc *sec_desc;
Elf *elf = obj->efile.elf;
Elf_Data *btf_ext_data = NULL;
Elf_Data *btf_data = NULL;
......@@ -3007,17 +3166,27 @@ static int bpf_object__elf_collect(struct bpf_object *obj)
const char *name;
Elf_Data *data;
Elf_Scn *scn;
GElf_Shdr sh;
Elf64_Shdr *sh;
/* ELF section indices are 1-based, so allocate +1 element to keep
* indexing simple. Also include 0th invalid section into sec_cnt for
* simpler and more traditional iteration logic.
*/
obj->efile.sec_cnt = 1 + obj->efile.ehdr->e_shnum;
obj->efile.secs = calloc(obj->efile.sec_cnt, sizeof(*obj->efile.secs));
if (!obj->efile.secs)
return -ENOMEM;
/* a bunch of ELF parsing functionality depends on processing symbols,
* so do the first pass and find the symbol table
*/
scn = NULL;
while ((scn = elf_nextscn(elf, scn)) != NULL) {
if (elf_sec_hdr(obj, scn, &sh))
sh = elf_sec_hdr(obj, scn);
if (!sh)
return -LIBBPF_ERRNO__FORMAT;
if (sh.sh_type == SHT_SYMTAB) {
if (sh->sh_type == SHT_SYMTAB) {
if (obj->efile.symbols) {
pr_warn("elf: multiple symbol tables in %s\n", obj->path);
return -LIBBPF_ERRNO__FORMAT;
......@@ -3027,9 +3196,11 @@ static int bpf_object__elf_collect(struct bpf_object *obj)
if (!data)
return -LIBBPF_ERRNO__FORMAT;
idx = elf_ndxscn(scn);
obj->efile.symbols = data;
obj->efile.symbols_shndx = elf_ndxscn(scn);
obj->efile.strtabidx = sh.sh_link;
obj->efile.symbols_shndx = idx;
obj->efile.strtabidx = sh->sh_link;
}
}
......@@ -3041,16 +3212,18 @@ static int bpf_object__elf_collect(struct bpf_object *obj)
scn = NULL;
while ((scn = elf_nextscn(elf, scn)) != NULL) {
idx++;
idx = elf_ndxscn(scn);
sec_desc = &obj->efile.secs[idx];
if (elf_sec_hdr(obj, scn, &sh))
sh = elf_sec_hdr(obj, scn);
if (!sh)
return -LIBBPF_ERRNO__FORMAT;
name = elf_sec_str(obj, sh.sh_name);
name = elf_sec_str(obj, sh->sh_name);
if (!name)
return -LIBBPF_ERRNO__FORMAT;
if (ignore_elf_section(&sh, name))
if (ignore_elf_section(sh, name))
continue;
data = elf_sec_data(obj, scn);
......@@ -3059,8 +3232,8 @@ static int bpf_object__elf_collect(struct bpf_object *obj)
pr_debug("elf: section(%d) %s, size %ld, link %d, flags %lx, type=%d\n",
idx, name, (unsigned long)data->d_size,
(int)sh.sh_link, (unsigned long)sh.sh_flags,
(int)sh.sh_type);
(int)sh->sh_link, (unsigned long)sh->sh_flags,
(int)sh->sh_type);
if (strcmp(name, "license") == 0) {
err = bpf_object__init_license(obj, data->d_buf, data->d_size);
......@@ -3078,21 +3251,25 @@ static int bpf_object__elf_collect(struct bpf_object *obj)
btf_data = data;
} else if (strcmp(name, BTF_EXT_ELF_SEC) == 0) {
btf_ext_data = data;
} else if (sh.sh_type == SHT_SYMTAB) {
} else if (sh->sh_type == SHT_SYMTAB) {
/* already processed during the first pass above */
} else if (sh.sh_type == SHT_PROGBITS && data->d_size > 0) {
if (sh.sh_flags & SHF_EXECINSTR) {
} else if (sh->sh_type == SHT_PROGBITS && data->d_size > 0) {
if (sh->sh_flags & SHF_EXECINSTR) {
if (strcmp(name, ".text") == 0)
obj->efile.text_shndx = idx;
err = bpf_object__add_programs(obj, data, name, idx);
if (err)
return err;
} else if (strcmp(name, DATA_SEC) == 0) {
obj->efile.data = data;
obj->efile.data_shndx = idx;
} else if (strcmp(name, RODATA_SEC) == 0) {
obj->efile.rodata = data;
obj->efile.rodata_shndx = idx;
} else if (strcmp(name, DATA_SEC) == 0 ||
str_has_pfx(name, DATA_SEC ".")) {
sec_desc->sec_type = SEC_DATA;
sec_desc->shdr = sh;
sec_desc->data = data;
} else if (strcmp(name, RODATA_SEC) == 0 ||
str_has_pfx(name, RODATA_SEC ".")) {
sec_desc->sec_type = SEC_RODATA;
sec_desc->shdr = sh;
sec_desc->data = data;
} else if (strcmp(name, STRUCT_OPS_SEC) == 0) {
obj->efile.st_ops_data = data;
obj->efile.st_ops_shndx = idx;
......@@ -3100,37 +3277,29 @@ static int bpf_object__elf_collect(struct bpf_object *obj)
pr_info("elf: skipping unrecognized data section(%d) %s\n",
idx, name);
}
} else if (sh.sh_type == SHT_REL) {
int nr_sects = obj->efile.nr_reloc_sects;
void *sects = obj->efile.reloc_sects;
int sec = sh.sh_info; /* points to other section */
} else if (sh->sh_type == SHT_REL) {
int targ_sec_idx = sh->sh_info; /* points to other section */
/* Only do relo for section with exec instructions */
if (!section_have_execinstr(obj, sec) &&
if (!section_have_execinstr(obj, targ_sec_idx) &&
strcmp(name, ".rel" STRUCT_OPS_SEC) &&
strcmp(name, ".rel" MAPS_ELF_SEC)) {
pr_info("elf: skipping relo section(%d) %s for section(%d) %s\n",
idx, name, sec,
elf_sec_name(obj, elf_sec_by_idx(obj, sec)) ?: "<?>");
idx, name, targ_sec_idx,
elf_sec_name(obj, elf_sec_by_idx(obj, targ_sec_idx)) ?: "<?>");
continue;
}
sects = libbpf_reallocarray(sects, nr_sects + 1,
sizeof(*obj->efile.reloc_sects));
if (!sects)
return -ENOMEM;
obj->efile.reloc_sects = sects;
obj->efile.nr_reloc_sects++;
obj->efile.reloc_sects[nr_sects].shdr = sh;
obj->efile.reloc_sects[nr_sects].data = data;
} else if (sh.sh_type == SHT_NOBITS && strcmp(name, BSS_SEC) == 0) {
obj->efile.bss = data;
obj->efile.bss_shndx = idx;
sec_desc->sec_type = SEC_RELO;
sec_desc->shdr = sh;
sec_desc->data = data;
} else if (sh->sh_type == SHT_NOBITS && strcmp(name, BSS_SEC) == 0) {
sec_desc->sec_type = SEC_BSS;
sec_desc->shdr = sh;
sec_desc->data = data;
} else {
pr_info("elf: skipping section(%d) %s (size %zu)\n", idx, name,
(size_t)sh.sh_size);
(size_t)sh->sh_size);
}
}
......@@ -3146,19 +3315,19 @@ static int bpf_object__elf_collect(struct bpf_object *obj)
return bpf_object__init_btf(obj, btf_data, btf_ext_data);
}
static bool sym_is_extern(const GElf_Sym *sym)
static bool sym_is_extern(const Elf64_Sym *sym)
{
int bind = GELF_ST_BIND(sym->st_info);
int bind = ELF64_ST_BIND(sym->st_info);
/* externs are symbols w/ type=NOTYPE, bind=GLOBAL|WEAK, section=UND */
return sym->st_shndx == SHN_UNDEF &&
(bind == STB_GLOBAL || bind == STB_WEAK) &&
GELF_ST_TYPE(sym->st_info) == STT_NOTYPE;
ELF64_ST_TYPE(sym->st_info) == STT_NOTYPE;
}
static bool sym_is_subprog(const GElf_Sym *sym, int text_shndx)
static bool sym_is_subprog(const Elf64_Sym *sym, int text_shndx)
{
int bind = GELF_ST_BIND(sym->st_info);
int type = GELF_ST_TYPE(sym->st_info);
int bind = ELF64_ST_BIND(sym->st_info);
int type = ELF64_ST_TYPE(sym->st_info);
/* in .text section */
if (sym->st_shndx != text_shndx)
......@@ -3356,30 +3525,31 @@ static int bpf_object__collect_externs(struct bpf_object *obj)
int i, n, off, dummy_var_btf_id;
const char *ext_name, *sec_name;
Elf_Scn *scn;
GElf_Shdr sh;
Elf64_Shdr *sh;
if (!obj->efile.symbols)
return 0;
scn = elf_sec_by_idx(obj, obj->efile.symbols_shndx);
if (elf_sec_hdr(obj, scn, &sh))
sh = elf_sec_hdr(obj, scn);
if (!sh)
return -LIBBPF_ERRNO__FORMAT;
dummy_var_btf_id = add_dummy_ksym_var(obj->btf);
if (dummy_var_btf_id < 0)
return dummy_var_btf_id;
n = sh.sh_size / sh.sh_entsize;
n = sh->sh_size / sh->sh_entsize;
pr_debug("looking for externs among %d symbols...\n", n);
for (i = 0; i < n; i++) {
GElf_Sym sym;
Elf64_Sym *sym = elf_sym_by_idx(obj, i);
if (!gelf_getsym(obj->efile.symbols, i, &sym))
if (!sym)
return -LIBBPF_ERRNO__FORMAT;
if (!sym_is_extern(&sym))
if (!sym_is_extern(sym))
continue;
ext_name = elf_sym_str(obj, sym.st_name);
ext_name = elf_sym_str(obj, sym->st_name);
if (!ext_name || !ext_name[0])
continue;
......@@ -3401,7 +3571,7 @@ static int bpf_object__collect_externs(struct bpf_object *obj)
t = btf__type_by_id(obj->btf, ext->btf_id);
ext->name = btf__name_by_offset(obj->btf, t->name_off);
ext->sym_idx = i;
ext->is_weak = GELF_ST_BIND(sym.st_info) == STB_WEAK;
ext->is_weak = ELF64_ST_BIND(sym->st_info) == STB_WEAK;
ext->sec_btf_id = find_extern_sec_btf_id(obj->btf, ext->btf_id);
if (ext->sec_btf_id <= 0) {
......@@ -3606,9 +3776,14 @@ bpf_object__find_program_by_name(const struct bpf_object *obj,
static bool bpf_object__shndx_is_data(const struct bpf_object *obj,
int shndx)
{
return shndx == obj->efile.data_shndx ||
shndx == obj->efile.bss_shndx ||
shndx == obj->efile.rodata_shndx;
switch (obj->efile.secs[shndx].sec_type) {
case SEC_BSS:
case SEC_DATA:
case SEC_RODATA:
return true;
default:
return false;
}
}
static bool bpf_object__shndx_is_maps(const struct bpf_object *obj,
......@@ -3621,22 +3796,25 @@ static bool bpf_object__shndx_is_maps(const struct bpf_object *obj,
static enum libbpf_map_type
bpf_object__section_to_libbpf_map_type(const struct bpf_object *obj, int shndx)
{
if (shndx == obj->efile.data_shndx)
return LIBBPF_MAP_DATA;
else if (shndx == obj->efile.bss_shndx)
if (shndx == obj->efile.symbols_shndx)
return LIBBPF_MAP_KCONFIG;
switch (obj->efile.secs[shndx].sec_type) {
case SEC_BSS:
return LIBBPF_MAP_BSS;
else if (shndx == obj->efile.rodata_shndx)
case SEC_DATA:
return LIBBPF_MAP_DATA;
case SEC_RODATA:
return LIBBPF_MAP_RODATA;
else if (shndx == obj->efile.symbols_shndx)
return LIBBPF_MAP_KCONFIG;
else
default:
return LIBBPF_MAP_UNSPEC;
}
}
static int bpf_program__record_reloc(struct bpf_program *prog,
struct reloc_desc *reloc_desc,
__u32 insn_idx, const char *sym_name,
const GElf_Sym *sym, const GElf_Rel *rel)
const Elf64_Sym *sym, const Elf64_Rel *rel)
{
struct bpf_insn *insn = &prog->insns[insn_idx];
size_t map_idx, nr_maps = prog->obj->nr_maps;
......@@ -3653,7 +3831,7 @@ static int bpf_program__record_reloc(struct bpf_program *prog,
}
if (sym_is_extern(sym)) {
int sym_idx = GELF_R_SYM(rel->r_info);
int sym_idx = ELF64_R_SYM(rel->r_info);
int i, n = obj->nr_extern;
struct extern_desc *ext;
......@@ -3766,7 +3944,7 @@ static int bpf_program__record_reloc(struct bpf_program *prog,
}
for (map_idx = 0; map_idx < nr_maps; map_idx++) {
map = &obj->maps[map_idx];
if (map->libbpf_type != type)
if (map->libbpf_type != type || map->sec_idx != sym->st_shndx)
continue;
pr_debug("prog '%s': found data map %zd (%s, sec %d, off %zu) for insn %u\n",
prog->name, map_idx, map->name, map->sec_idx,
......@@ -3818,9 +3996,8 @@ static struct bpf_program *find_prog_by_sec_insn(const struct bpf_object *obj,
}
static int
bpf_object__collect_prog_relos(struct bpf_object *obj, GElf_Shdr *shdr, Elf_Data *data)
bpf_object__collect_prog_relos(struct bpf_object *obj, Elf64_Shdr *shdr, Elf_Data *data)
{
Elf_Data *symbols = obj->efile.symbols;
const char *relo_sec_name, *sec_name;
size_t sec_idx = shdr->sh_info;
struct bpf_program *prog;
......@@ -3830,8 +4007,8 @@ bpf_object__collect_prog_relos(struct bpf_object *obj, GElf_Shdr *shdr, Elf_Data
__u32 insn_idx;
Elf_Scn *scn;
Elf_Data *scn_data;
GElf_Sym sym;
GElf_Rel rel;
Elf64_Sym *sym;
Elf64_Rel *rel;
scn = elf_sec_by_idx(obj, sec_idx);
scn_data = elf_sec_data(obj, scn);
......@@ -3846,33 +4023,36 @@ bpf_object__collect_prog_relos(struct bpf_object *obj, GElf_Shdr *shdr, Elf_Data
nrels = shdr->sh_size / shdr->sh_entsize;
for (i = 0; i < nrels; i++) {
if (!gelf_getrel(data, i, &rel)) {
rel = elf_rel_by_idx(data, i);
if (!rel) {
pr_warn("sec '%s': failed to get relo #%d\n", relo_sec_name, i);
return -LIBBPF_ERRNO__FORMAT;
}
if (!gelf_getsym(symbols, GELF_R_SYM(rel.r_info), &sym)) {
sym = elf_sym_by_idx(obj, ELF64_R_SYM(rel->r_info));
if (!sym) {
pr_warn("sec '%s': symbol 0x%zx not found for relo #%d\n",
relo_sec_name, (size_t)GELF_R_SYM(rel.r_info), i);
relo_sec_name, (size_t)ELF64_R_SYM(rel->r_info), i);
return -LIBBPF_ERRNO__FORMAT;
}
if (rel.r_offset % BPF_INSN_SZ || rel.r_offset >= scn_data->d_size) {
if (rel->r_offset % BPF_INSN_SZ || rel->r_offset >= scn_data->d_size) {
pr_warn("sec '%s': invalid offset 0x%zx for relo #%d\n",
relo_sec_name, (size_t)GELF_R_SYM(rel.r_info), i);
relo_sec_name, (size_t)ELF64_R_SYM(rel->r_info), i);
return -LIBBPF_ERRNO__FORMAT;
}
insn_idx = rel.r_offset / BPF_INSN_SZ;
insn_idx = rel->r_offset / BPF_INSN_SZ;
/* relocations against static functions are recorded as
* relocations against the section that contains a function;
* in such case, symbol will be STT_SECTION and sym.st_name
* will point to empty string (0), so fetch section name
* instead
*/
if (GELF_ST_TYPE(sym.st_info) == STT_SECTION && sym.st_name == 0)
sym_name = elf_sec_name(obj, elf_sec_by_idx(obj, sym.st_shndx));
if (ELF64_ST_TYPE(sym->st_info) == STT_SECTION && sym->st_name == 0)
sym_name = elf_sec_name(obj, elf_sec_by_idx(obj, sym->st_shndx));
else
sym_name = elf_sym_str(obj, sym.st_name);
sym_name = elf_sym_str(obj, sym->st_name);
sym_name = sym_name ?: "<?";
pr_debug("sec '%s': relo #%d: insn #%u against '%s'\n",
......@@ -3894,7 +4074,7 @@ bpf_object__collect_prog_relos(struct bpf_object *obj, GElf_Shdr *shdr, Elf_Data
/* adjust insn_idx to local BPF program frame of reference */
insn_idx -= prog->sec_insn_off;
err = bpf_program__record_reloc(prog, &relos[prog->nr_reloc],
insn_idx, sym_name, &sym, &rel);
insn_idx, sym_name, sym, rel);
if (err)
return err;
......@@ -3926,8 +4106,7 @@ static int bpf_map_find_btf_info(struct bpf_object *obj, struct bpf_map *map)
* LLVM annotates global data differently in BTF, that is,
* only as '.data', '.bss' or '.rodata'.
*/
ret = btf__find_by_name(obj->btf,
libbpf_type_to_btf_name[map->libbpf_type]);
ret = btf__find_by_name(obj->btf, map->real_name);
}
if (ret < 0)
return ret;
......@@ -5942,10 +6121,10 @@ bpf_object__relocate(struct bpf_object *obj, const char *targ_btf_path)
}
static int bpf_object__collect_st_ops_relos(struct bpf_object *obj,
GElf_Shdr *shdr, Elf_Data *data);
Elf64_Shdr *shdr, Elf_Data *data);
static int bpf_object__collect_map_relos(struct bpf_object *obj,
GElf_Shdr *shdr, Elf_Data *data)
Elf64_Shdr *shdr, Elf_Data *data)
{
const int bpf_ptr_sz = 8, host_ptr_sz = sizeof(void *);
int i, j, nrels, new_sz;
......@@ -5954,10 +6133,9 @@ static int bpf_object__collect_map_relos(struct bpf_object *obj,
struct bpf_map *map = NULL, *targ_map;
const struct btf_member *member;
const char *name, *mname;
Elf_Data *symbols;
unsigned int moff;
GElf_Sym sym;
GElf_Rel rel;
Elf64_Sym *sym;
Elf64_Rel *rel;
void *tmp;
if (!obj->efile.btf_maps_sec_btf_id || !obj->btf)
......@@ -5966,28 +6144,30 @@ static int bpf_object__collect_map_relos(struct bpf_object *obj,
if (!sec)
return -EINVAL;
symbols = obj->efile.symbols;
nrels = shdr->sh_size / shdr->sh_entsize;
for (i = 0; i < nrels; i++) {
if (!gelf_getrel(data, i, &rel)) {
rel = elf_rel_by_idx(data, i);
if (!rel) {
pr_warn(".maps relo #%d: failed to get ELF relo\n", i);
return -LIBBPF_ERRNO__FORMAT;
}
if (!gelf_getsym(symbols, GELF_R_SYM(rel.r_info), &sym)) {
sym = elf_sym_by_idx(obj, ELF64_R_SYM(rel->r_info));
if (!sym) {
pr_warn(".maps relo #%d: symbol %zx not found\n",
i, (size_t)GELF_R_SYM(rel.r_info));
i, (size_t)ELF64_R_SYM(rel->r_info));
return -LIBBPF_ERRNO__FORMAT;
}
name = elf_sym_str(obj, sym.st_name) ?: "<?>";
if (sym.st_shndx != obj->efile.btf_maps_shndx) {
name = elf_sym_str(obj, sym->st_name) ?: "<?>";
if (sym->st_shndx != obj->efile.btf_maps_shndx) {
pr_warn(".maps relo #%d: '%s' isn't a BTF-defined map\n",
i, name);
return -LIBBPF_ERRNO__RELOC;
}
pr_debug(".maps relo #%d: for %zd value %zd rel.r_offset %zu name %d ('%s')\n",
i, (ssize_t)(rel.r_info >> 32), (size_t)sym.st_value,
(size_t)rel.r_offset, sym.st_name, name);
pr_debug(".maps relo #%d: for %zd value %zd rel->r_offset %zu name %d ('%s')\n",
i, (ssize_t)(rel->r_info >> 32), (size_t)sym->st_value,
(size_t)rel->r_offset, sym->st_name, name);
for (j = 0; j < obj->nr_maps; j++) {
map = &obj->maps[j];
......@@ -5995,13 +6175,13 @@ static int bpf_object__collect_map_relos(struct bpf_object *obj,
continue;
vi = btf_var_secinfos(sec) + map->btf_var_idx;
if (vi->offset <= rel.r_offset &&
rel.r_offset + bpf_ptr_sz <= vi->offset + vi->size)
if (vi->offset <= rel->r_offset &&
rel->r_offset + bpf_ptr_sz <= vi->offset + vi->size)
break;
}
if (j == obj->nr_maps) {
pr_warn(".maps relo #%d: cannot find map '%s' at rel.r_offset %zu\n",
i, name, (size_t)rel.r_offset);
pr_warn(".maps relo #%d: cannot find map '%s' at rel->r_offset %zu\n",
i, name, (size_t)rel->r_offset);
return -EINVAL;
}
......@@ -6028,10 +6208,10 @@ static int bpf_object__collect_map_relos(struct bpf_object *obj,
return -EINVAL;
moff = btf_member_bit_offset(def, btf_vlen(def) - 1) / 8;
if (rel.r_offset - vi->offset < moff)
if (rel->r_offset - vi->offset < moff)
return -EINVAL;
moff = rel.r_offset - vi->offset - moff;
moff = rel->r_offset - vi->offset - moff;
/* here we use BPF pointer size, which is always 64 bit, as we
* are parsing ELF that was built for BPF target
*/
......@@ -6076,10 +6256,18 @@ static int bpf_object__collect_relos(struct bpf_object *obj)
{
int i, err;
for (i = 0; i < obj->efile.nr_reloc_sects; i++) {
GElf_Shdr *shdr = &obj->efile.reloc_sects[i].shdr;
Elf_Data *data = obj->efile.reloc_sects[i].data;
int idx = shdr->sh_info;
for (i = 0; i < obj->efile.sec_cnt; i++) {
struct elf_sec_desc *sec_desc = &obj->efile.secs[i];
Elf64_Shdr *shdr;
Elf_Data *data;
int idx;
if (sec_desc->sec_type != SEC_RELO)
continue;
shdr = sec_desc->shdr;
data = sec_desc->data;
idx = shdr->sh_info;
if (shdr->sh_type != SHT_REL) {
pr_warn("internal error at %d\n", __LINE__);
......@@ -6202,6 +6390,7 @@ load_program(struct bpf_program *prog, struct bpf_insn *insns, int insns_cnt,
char *license, __u32 kern_version, int *pfd)
{
struct bpf_prog_load_params load_attr = {};
struct bpf_object *obj = prog->obj;
char *cp, errmsg[STRERR_BUFSIZE];
size_t log_buf_size = 0;
char *log_buf = NULL;
......@@ -6222,7 +6411,7 @@ load_program(struct bpf_program *prog, struct bpf_insn *insns, int insns_cnt,
load_attr.prog_type = prog->type;
load_attr.expected_attach_type = prog->expected_attach_type;
if (kernel_supports(prog->obj, FEAT_PROG_NAME))
if (kernel_supports(obj, FEAT_PROG_NAME))
load_attr.name = prog->name;
load_attr.insns = insns;
load_attr.insn_cnt = insns_cnt;
......@@ -6235,8 +6424,8 @@ load_program(struct bpf_program *prog, struct bpf_insn *insns, int insns_cnt,
load_attr.prog_ifindex = prog->prog_ifindex;
/* specify func_info/line_info only if kernel supports them */
btf_fd = bpf_object__btf_fd(prog->obj);
if (btf_fd >= 0 && kernel_supports(prog->obj, FEAT_BTF_FUNC)) {
btf_fd = bpf_object__btf_fd(obj);
if (btf_fd >= 0 && kernel_supports(obj, FEAT_BTF_FUNC)) {
load_attr.prog_btf_fd = btf_fd;
load_attr.func_info = prog->func_info;
load_attr.func_info_rec_size = prog->func_info_rec_size;
......@@ -6247,7 +6436,7 @@ load_program(struct bpf_program *prog, struct bpf_insn *insns, int insns_cnt,
}
load_attr.log_level = prog->log_level;
load_attr.prog_flags = prog->prog_flags;
load_attr.fd_array = prog->obj->fd_array;
load_attr.fd_array = obj->fd_array;
/* adjust load_attr if sec_def provides custom preload callback */
if (prog->sec_def && prog->sec_def->preload_fn) {
......@@ -6259,9 +6448,9 @@ load_program(struct bpf_program *prog, struct bpf_insn *insns, int insns_cnt,
}
}
if (prog->obj->gen_loader) {
bpf_gen__prog_load(prog->obj->gen_loader, &load_attr,
prog - prog->obj->programs);
if (obj->gen_loader) {
bpf_gen__prog_load(obj->gen_loader, &load_attr,
prog - obj->programs);
*pfd = -1;
return 0;
}
......@@ -6282,18 +6471,23 @@ load_program(struct bpf_program *prog, struct bpf_insn *insns, int insns_cnt,
if (log_buf && load_attr.log_level)
pr_debug("verifier log:\n%s", log_buf);
if (prog->obj->rodata_map_idx >= 0 &&
kernel_supports(prog->obj, FEAT_PROG_BIND_MAP)) {
struct bpf_map *rodata_map =
&prog->obj->maps[prog->obj->rodata_map_idx];
if (obj->has_rodata && kernel_supports(obj, FEAT_PROG_BIND_MAP)) {
struct bpf_map *map;
int i;
if (bpf_prog_bind_map(ret, bpf_map__fd(rodata_map), NULL)) {
for (i = 0; i < obj->nr_maps; i++) {
map = &prog->obj->maps[i];
if (map->libbpf_type != LIBBPF_MAP_RODATA)
continue;
if (bpf_prog_bind_map(ret, bpf_map__fd(map), NULL)) {
cp = libbpf_strerror_r(errno, errmsg, sizeof(errmsg));
pr_warn("prog '%s': failed to bind .rodata map: %s\n",
prog->name, cp);
/* Don't fail hard if can't bind rodata. */
}
}
}
*pfd = ret;
ret = 0;
......@@ -7682,6 +7876,7 @@ static void bpf_map__destroy(struct bpf_map *map)
}
zfree(&map->name);
zfree(&map->real_name);
zfree(&map->pin_path);
if (map->fd >= 0)
......@@ -8268,7 +8463,7 @@ static struct bpf_map *find_struct_ops_map_by_offset(struct bpf_object *obj,
/* Collect the reloc from ELF and populate the st_ops->progs[] */
static int bpf_object__collect_st_ops_relos(struct bpf_object *obj,
GElf_Shdr *shdr, Elf_Data *data)
Elf64_Shdr *shdr, Elf_Data *data)
{
const struct btf_member *member;
struct bpf_struct_ops *st_ops;
......@@ -8276,58 +8471,58 @@ static int bpf_object__collect_st_ops_relos(struct bpf_object *obj,
unsigned int shdr_idx;
const struct btf *btf;
struct bpf_map *map;
Elf_Data *symbols;
unsigned int moff, insn_idx;
const char *name;
__u32 member_idx;
GElf_Sym sym;
GElf_Rel rel;
Elf64_Sym *sym;
Elf64_Rel *rel;
int i, nrels;
symbols = obj->efile.symbols;
btf = obj->btf;
nrels = shdr->sh_size / shdr->sh_entsize;
for (i = 0; i < nrels; i++) {
if (!gelf_getrel(data, i, &rel)) {
rel = elf_rel_by_idx(data, i);
if (!rel) {
pr_warn("struct_ops reloc: failed to get %d reloc\n", i);
return -LIBBPF_ERRNO__FORMAT;
}
if (!gelf_getsym(symbols, GELF_R_SYM(rel.r_info), &sym)) {
sym = elf_sym_by_idx(obj, ELF64_R_SYM(rel->r_info));
if (!sym) {
pr_warn("struct_ops reloc: symbol %zx not found\n",
(size_t)GELF_R_SYM(rel.r_info));
(size_t)ELF64_R_SYM(rel->r_info));
return -LIBBPF_ERRNO__FORMAT;
}
name = elf_sym_str(obj, sym.st_name) ?: "<?>";
map = find_struct_ops_map_by_offset(obj, rel.r_offset);
name = elf_sym_str(obj, sym->st_name) ?: "<?>";
map = find_struct_ops_map_by_offset(obj, rel->r_offset);
if (!map) {
pr_warn("struct_ops reloc: cannot find map at rel.r_offset %zu\n",
(size_t)rel.r_offset);
pr_warn("struct_ops reloc: cannot find map at rel->r_offset %zu\n",
(size_t)rel->r_offset);
return -EINVAL;
}
moff = rel.r_offset - map->sec_offset;
shdr_idx = sym.st_shndx;
moff = rel->r_offset - map->sec_offset;
shdr_idx = sym->st_shndx;
st_ops = map->st_ops;
pr_debug("struct_ops reloc %s: for %lld value %lld shdr_idx %u rel.r_offset %zu map->sec_offset %zu name %d (\'%s\')\n",
pr_debug("struct_ops reloc %s: for %lld value %lld shdr_idx %u rel->r_offset %zu map->sec_offset %zu name %d (\'%s\')\n",
map->name,
(long long)(rel.r_info >> 32),
(long long)sym.st_value,
shdr_idx, (size_t)rel.r_offset,
map->sec_offset, sym.st_name, name);
(long long)(rel->r_info >> 32),
(long long)sym->st_value,
shdr_idx, (size_t)rel->r_offset,
map->sec_offset, sym->st_name, name);
if (shdr_idx >= SHN_LORESERVE) {
pr_warn("struct_ops reloc %s: rel.r_offset %zu shdr_idx %u unsupported non-static function\n",
map->name, (size_t)rel.r_offset, shdr_idx);
pr_warn("struct_ops reloc %s: rel->r_offset %zu shdr_idx %u unsupported non-static function\n",
map->name, (size_t)rel->r_offset, shdr_idx);
return -LIBBPF_ERRNO__RELOC;
}
if (sym.st_value % BPF_INSN_SZ) {
if (sym->st_value % BPF_INSN_SZ) {
pr_warn("struct_ops reloc %s: invalid target program offset %llu\n",
map->name, (unsigned long long)sym.st_value);
map->name, (unsigned long long)sym->st_value);
return -LIBBPF_ERRNO__FORMAT;
}
insn_idx = sym.st_value / BPF_INSN_SZ;
insn_idx = sym->st_value / BPF_INSN_SZ;
member = find_member_by_offset(st_ops->type, moff * 8);
if (!member) {
......@@ -8606,9 +8801,30 @@ const struct bpf_map_def *bpf_map__def(const struct bpf_map *map)
return map ? &map->def : libbpf_err_ptr(-EINVAL);
}
static bool map_uses_real_name(const struct bpf_map *map)
{
/* Since libbpf started to support custom .data.* and .rodata.* maps,
* their user-visible name differs from kernel-visible name. Users see
* such map's corresponding ELF section name as a map name.
* This check distinguishes .data/.rodata from .data.* and .rodata.*
* maps to know which name has to be returned to the user.
*/
if (map->libbpf_type == LIBBPF_MAP_DATA && strcmp(map->real_name, DATA_SEC) != 0)
return true;
if (map->libbpf_type == LIBBPF_MAP_RODATA && strcmp(map->real_name, RODATA_SEC) != 0)
return true;
return false;
}
const char *bpf_map__name(const struct bpf_map *map)
{
return map ? map->name : NULL;
if (!map)
return NULL;
if (map_uses_real_name(map))
return map->real_name;
return map->name;
}
enum bpf_map_type bpf_map__type(const struct bpf_map *map)
......@@ -8827,7 +9043,22 @@ bpf_object__find_map_by_name(const struct bpf_object *obj, const char *name)
struct bpf_map *pos;
bpf_object__for_each_map(pos, obj) {
if (pos->name && !strcmp(pos->name, name))
/* if it's a special internal map name (which always starts
* with dot) then check if that special name matches the
* real map name (ELF section name)
*/
if (name[0] == '.') {
if (pos->real_name && strcmp(pos->real_name, name) == 0)
return pos;
continue;
}
/* otherwise map name has to be an exact match */
if (map_uses_real_name(pos)) {
if (strcmp(pos->real_name, name) == 0)
return pos;
continue;
}
if (strcmp(pos->name, name) == 0)
return pos;
}
return errno = ENOENT, NULL;
......
......@@ -52,8 +52,8 @@
#endif
/* Older libelf all end up in this expression, for both 32 and 64 bit */
#ifndef GELF_ST_VISIBILITY
#define GELF_ST_VISIBILITY(o) ((o) & 0x03)
#ifndef ELF64_ST_VISIBILITY
#define ELF64_ST_VISIBILITY(o) ((o) & 0x03)
#endif
#define BTF_INFO_ENC(kind, kind_flag, vlen) \
......@@ -303,10 +303,6 @@ struct bpf_prog_load_params {
int libbpf__bpf_prog_load(const struct bpf_prog_load_params *load_attr);
int bpf_object__section_size(const struct bpf_object *obj, const char *name,
__u32 *size);
int bpf_object__variable_offset(const struct bpf_object *obj, const char *name,
__u32 *off);
struct btf *btf_get_from_fd(int btf_fd, struct btf *base_btf);
void btf_get_kernel_prefix_kind(enum bpf_attach_type attach_type,
const char **prefix, int *kind);
......
......@@ -15,7 +15,6 @@
#include <linux/btf.h>
#include <elf.h>
#include <libelf.h>
#include <gelf.h>
#include <fcntl.h>
#include "libbpf.h"
#include "btf.h"
......
......@@ -163,7 +163,7 @@ void test_core_autosize(void)
usleep(1);
bss_map = bpf_object__find_map_by_name(skel->obj, "test_cor.bss");
bss_map = bpf_object__find_map_by_name(skel->obj, ".bss");
if (!ASSERT_OK_PTR(bss_map, "bss_map_find"))
goto cleanup;
......
......@@ -867,7 +867,7 @@ void test_core_reloc(void)
goto cleanup;
}
data_map = bpf_object__find_map_by_name(obj, "test_cor.bss");
data_map = bpf_object__find_map_by_name(obj, ".bss");
if (CHECK(!data_map, "find_data_map", "data map not found\n"))
goto cleanup;
......
......@@ -103,11 +103,18 @@ static void test_global_data_struct(struct bpf_object *obj, __u32 duration)
static void test_global_data_rdonly(struct bpf_object *obj, __u32 duration)
{
int err = -ENOMEM, map_fd, zero = 0;
struct bpf_map *map;
struct bpf_map *map, *map2;
__u8 *buff;
map = bpf_object__find_map_by_name(obj, "test_glo.rodata");
if (CHECK_FAIL(!map || !bpf_map__is_internal(map)))
if (!ASSERT_OK_PTR(map, "map"))
return;
if (!ASSERT_TRUE(bpf_map__is_internal(map), "is_internal"))
return;
/* ensure we can lookup internal maps by their ELF names */
map2 = bpf_object__find_map_by_name(obj, ".rodata");
if (!ASSERT_EQ(map, map2, "same_maps"))
return;
map_fd = bpf_map__fd(map);
......
......@@ -16,7 +16,7 @@ void test_global_data_init(void)
if (CHECK_FAIL(err))
return;
map = bpf_object__find_map_by_name(obj, "test_glo.rodata");
map = bpf_object__find_map_by_name(obj, ".rodata");
if (CHECK_FAIL(!map || !bpf_map__is_internal(map)))
goto out;
......
......@@ -93,7 +93,7 @@ void serial_test_kfree_skb(void)
if (CHECK(!fexit, "find_prog", "prog eth_type_trans not found\n"))
goto close_prog;
global_data = bpf_object__find_map_by_name(obj2, "kfree_sk.bss");
global_data = bpf_object__find_map_by_name(obj2, ".bss");
if (CHECK(!global_data, "find global data", "not found\n"))
goto close_prog;
......
......@@ -37,7 +37,7 @@ void test_rdonly_maps(void)
if (CHECK(err, "obj_load", "err %d errno %d\n", err, errno))
goto cleanup;
bss_map = bpf_object__find_map_by_name(obj, "test_rdo.bss");
bss_map = bpf_object__find_map_by_name(obj, ".bss");
if (CHECK(!bss_map, "find_bss_map", "failed\n"))
goto cleanup;
......
......@@ -16,10 +16,13 @@ void test_skeleton(void)
struct test_skeleton* skel;
struct test_skeleton__bss *bss;
struct test_skeleton__data *data;
struct test_skeleton__data_dyn *data_dyn;
struct test_skeleton__rodata *rodata;
struct test_skeleton__rodata_dyn *rodata_dyn;
struct test_skeleton__kconfig *kcfg;
const void *elf_bytes;
size_t elf_bytes_sz = 0;
int i;
skel = test_skeleton__open();
if (CHECK(!skel, "skel_open", "failed to open skeleton\n"))
......@@ -30,7 +33,12 @@ void test_skeleton(void)
bss = skel->bss;
data = skel->data;
data_dyn = skel->data_dyn;
rodata = skel->rodata;
rodata_dyn = skel->rodata_dyn;
ASSERT_STREQ(bpf_map__name(skel->maps.rodata_dyn), ".rodata.dyn", "rodata_dyn_name");
ASSERT_STREQ(bpf_map__name(skel->maps.data_dyn), ".data.dyn", "data_dyn_name");
/* validate values are pre-initialized correctly */
CHECK(data->in1 != -1, "in1", "got %d != exp %d\n", data->in1, -1);
......@@ -46,6 +54,12 @@ void test_skeleton(void)
CHECK(rodata->in.in6 != 0, "in6", "got %d != exp %d\n", rodata->in.in6, 0);
CHECK(bss->out6 != 0, "out6", "got %d != exp %d\n", bss->out6, 0);
ASSERT_EQ(rodata_dyn->in_dynarr_sz, 0, "in_dynarr_sz");
for (i = 0; i < 4; i++)
ASSERT_EQ(rodata_dyn->in_dynarr[i], -(i + 1), "in_dynarr");
for (i = 0; i < 4; i++)
ASSERT_EQ(data_dyn->out_dynarr[i], i + 1, "out_dynarr");
/* validate we can pre-setup global variables, even in .bss */
data->in1 = 10;
data->in2 = 11;
......@@ -53,6 +67,10 @@ void test_skeleton(void)
bss->in4 = 13;
rodata->in.in6 = 14;
rodata_dyn->in_dynarr_sz = 4;
for (i = 0; i < 4; i++)
rodata_dyn->in_dynarr[i] = i + 10;
err = test_skeleton__load(skel);
if (CHECK(err, "skel_load", "failed to load skeleton: %d\n", err))
goto cleanup;
......@@ -64,6 +82,10 @@ void test_skeleton(void)
CHECK(bss->in4 != 13, "in4", "got %lld != exp %lld\n", bss->in4, 13LL);
CHECK(rodata->in.in6 != 14, "in6", "got %d != exp %d\n", rodata->in.in6, 14);
ASSERT_EQ(rodata_dyn->in_dynarr_sz, 4, "in_dynarr_sz");
for (i = 0; i < 4; i++)
ASSERT_EQ(rodata_dyn->in_dynarr[i], i + 10, "in_dynarr");
/* now set new values and attach to get them into outX variables */
data->in1 = 1;
data->in2 = 2;
......@@ -73,6 +95,8 @@ void test_skeleton(void)
bss->in5.b = 6;
kcfg = skel->kconfig;
skel->data_read_mostly->read_mostly_var = 123;
err = test_skeleton__attach(skel);
if (CHECK(err, "skel_attach", "skeleton attach failed: %d\n", err))
goto cleanup;
......@@ -93,6 +117,11 @@ void test_skeleton(void)
CHECK(bss->kern_ver != kcfg->LINUX_KERNEL_VERSION, "ext2",
"got %d != exp %d\n", bss->kern_ver, kcfg->LINUX_KERNEL_VERSION);
for (i = 0; i < 4; i++)
ASSERT_EQ(data_dyn->out_dynarr[i], i + 10, "out_dynarr");
ASSERT_EQ(skel->bss->out_mostly_var, 123, "out_mostly_var");
elf_bytes = test_skeleton__elf_bytes(&elf_bytes_sz);
ASSERT_OK_PTR(elf_bytes, "elf_bytes");
ASSERT_GE(elf_bytes_sz, 0, "elf_bytes_sz");
......
......@@ -5,6 +5,8 @@
#include <linux/bpf.h>
#include <bpf/bpf_helpers.h>
#define __read_mostly SEC(".data.read_mostly")
struct s {
int a;
long long b;
......@@ -40,9 +42,20 @@ int kern_ver = 0;
struct s out5 = {};
const volatile int in_dynarr_sz SEC(".rodata.dyn");
const volatile int in_dynarr[4] SEC(".rodata.dyn") = { -1, -2, -3, -4 };
int out_dynarr[4] SEC(".data.dyn") = { 1, 2, 3, 4 };
int read_mostly_var __read_mostly;
int out_mostly_var;
SEC("raw_tp/sys_enter")
int handler(const void *ctx)
{
int i;
out1 = in1;
out2 = in2;
out3 = in3;
......@@ -53,6 +66,11 @@ int handler(const void *ctx)
bpf_syscall = CONFIG_BPF_SYSCALL;
kern_ver = LINUX_KERNEL_VERSION;
for (i = 0; i < in_dynarr_sz; i++)
out_dynarr[i] = in_dynarr[i];
out_mostly_var = read_mostly_var;
return 0;
}
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment