Commit da927466 authored by Alexei Starovoitov's avatar Alexei Starovoitov

Merge branch 'libbpf-field-existence'

Andrii Nakryiko says:

====================
This patch set generalizes libbpf's CO-RE relocation support. In addition to
existing field's byte offset relocation, libbpf now supports field existence
relocations, which are emitted by Clang when using
__builtin_preserve_field_info(<field>, BPF_FIELD_EXISTS). A convenience
bpf_core_field_exists() macro is added to bpf_core_read.h BPF-side header,
along the bpf_field_info_kind enum containing currently supported types of
field information libbpf supports. This list will grow as libbpf gains support
for other relo kinds.

This patch set upgrades the format of .BTF.ext's relocation record to match
latest Clang's format (12 -> 16 bytes). This is not a breaking change, as the
previous format hasn't been released yet as part of official Clang version
release.

v1->v2:
- unify bpf_field_info_kind enum and naming changes (Alexei);
- added bpf_core_field_exists() to bpf_core_read.h.
====================
Signed-off-by: default avatarAlexei Starovoitov <ast@kernel.org>
parents 14f2cf60 c7566a69
......@@ -2,6 +2,28 @@
#ifndef __BPF_CORE_READ_H__
#define __BPF_CORE_READ_H__
/*
* enum bpf_field_info_kind is passed as a second argument into
* __builtin_preserve_field_info() built-in to get a specific aspect of
* a field, captured as a first argument. __builtin_preserve_field_info(field,
* info_kind) returns __u32 integer and produces BTF field relocation, which
* is understood and processed by libbpf during BPF object loading. See
* selftests/bpf for examples.
*/
enum bpf_field_info_kind {
BPF_FIELD_BYTE_OFFSET = 0, /* field byte offset */
BPF_FIELD_EXISTS = 2, /* field existence in target kernel */
};
/*
* Convenience macro to check that field actually exists in target kernel's.
* Returns:
* 1, if matching field is present in target kernel;
* 0, if no matching field found.
*/
#define bpf_core_field_exists(field) \
__builtin_preserve_field_info(field, BPF_FIELD_EXISTS)
/*
* bpf_core_read() abstracts away bpf_probe_read() call and captures offset
* relocation for source address using __builtin_preserve_access_index()
......@@ -12,7 +34,7 @@
* a relocation, which records BTF type ID describing root struct/union and an
* accessor string which describes exact embedded field that was used to take
* an address. See detailed description of this relocation format and
* semantics in comments to struct bpf_offset_reloc in libbpf_internal.h.
* semantics in comments to struct bpf_field_reloc in libbpf_internal.h.
*
* This relocation allows libbpf to adjust BPF instruction to use correct
* actual field offset, based on target kernel BTF type that matches original
......
......@@ -888,14 +888,14 @@ static int btf_ext_setup_line_info(struct btf_ext *btf_ext)
return btf_ext_setup_info(btf_ext, &param);
}
static int btf_ext_setup_offset_reloc(struct btf_ext *btf_ext)
static int btf_ext_setup_field_reloc(struct btf_ext *btf_ext)
{
struct btf_ext_sec_setup_param param = {
.off = btf_ext->hdr->offset_reloc_off,
.len = btf_ext->hdr->offset_reloc_len,
.min_rec_size = sizeof(struct bpf_offset_reloc),
.ext_info = &btf_ext->offset_reloc_info,
.desc = "offset_reloc",
.off = btf_ext->hdr->field_reloc_off,
.len = btf_ext->hdr->field_reloc_len,
.min_rec_size = sizeof(struct bpf_field_reloc),
.ext_info = &btf_ext->field_reloc_info,
.desc = "field_reloc",
};
return btf_ext_setup_info(btf_ext, &param);
......@@ -975,9 +975,9 @@ struct btf_ext *btf_ext__new(__u8 *data, __u32 size)
goto done;
if (btf_ext->hdr->hdr_len <
offsetofend(struct btf_ext_header, offset_reloc_len))
offsetofend(struct btf_ext_header, field_reloc_len))
goto done;
err = btf_ext_setup_offset_reloc(btf_ext);
err = btf_ext_setup_field_reloc(btf_ext);
if (err)
goto done;
......
......@@ -60,8 +60,8 @@ struct btf_ext_header {
__u32 line_info_len;
/* optional part of .BTF.ext header */
__u32 offset_reloc_off;
__u32 offset_reloc_len;
__u32 field_reloc_off;
__u32 field_reloc_len;
};
LIBBPF_API void btf__free(struct btf *btf);
......
This diff is collapsed.
......@@ -96,8 +96,10 @@ struct bpf_object_open_opts {
const char *object_name;
/* parse map definitions non-strictly, allowing extra attributes/data */
bool relaxed_maps;
/* process CO-RE relocations non-strictly, allowing them to fail */
bool relaxed_core_relocs;
};
#define bpf_object_open_opts__last_field relaxed_maps
#define bpf_object_open_opts__last_field relaxed_core_relocs
LIBBPF_API struct bpf_object *bpf_object__open(const char *path);
LIBBPF_API struct bpf_object *
......
......@@ -110,7 +110,7 @@ struct btf_ext {
};
struct btf_ext_info func_info;
struct btf_ext_info line_info;
struct btf_ext_info offset_reloc_info;
struct btf_ext_info field_reloc_info;
__u32 data_size;
};
......@@ -135,13 +135,23 @@ struct bpf_line_info_min {
__u32 line_col;
};
/* The minimum bpf_offset_reloc checked by the loader
/* bpf_field_info_kind encodes which aspect of captured field has to be
* adjusted by relocations. Currently supported values are:
* - BPF_FIELD_BYTE_OFFSET: field offset (in bytes);
* - BPF_FIELD_EXISTS: field existence (1, if field exists; 0, otherwise);
*/
enum bpf_field_info_kind {
BPF_FIELD_BYTE_OFFSET = 0, /* field byte offset */
BPF_FIELD_EXISTS = 2, /* field existence in target kernel */
};
/* The minimum bpf_field_reloc checked by the loader
*
* Offset relocation captures the following data:
* Field relocation captures the following data:
* - insn_off - instruction offset (in bytes) within a BPF program that needs
* its insn->imm field to be relocated with actual offset;
* its insn->imm field to be relocated with actual field info;
* - type_id - BTF type ID of the "root" (containing) entity of a relocatable
* offset;
* field;
* - access_str_off - offset into corresponding .BTF string section. String
* itself encodes an accessed field using a sequence of field and array
* indicies, separated by colon (:). It's conceptually very close to LLVM's
......@@ -172,15 +182,16 @@ struct bpf_line_info_min {
* bpf_probe_read(&dst, sizeof(dst),
* __builtin_preserve_access_index(&src->a.b.c));
*
* In this case Clang will emit offset relocation recording necessary data to
* In this case Clang will emit field relocation recording necessary data to
* be able to find offset of embedded `a.b.c` field within `src` struct.
*
* [0] https://llvm.org/docs/LangRef.html#getelementptr-instruction
*/
struct bpf_offset_reloc {
struct bpf_field_reloc {
__u32 insn_off;
__u32 type_id;
__u32 access_str_off;
enum bpf_field_info_kind kind;
};
#endif /* __LIBBPF_LIBBPF_INTERNAL_H */
......@@ -174,6 +174,21 @@
.fails = true, \
}
#define EXISTENCE_DATA(struct_name) STRUCT_TO_CHAR_PTR(struct_name) { \
.a = 42, \
}
#define EXISTENCE_CASE_COMMON(name) \
.case_name = #name, \
.bpf_obj_file = "test_core_reloc_existence.o", \
.btf_src_file = "btf__core_reloc_" #name ".o", \
.relaxed_core_relocs = true \
#define EXISTENCE_ERR_CASE(name) { \
EXISTENCE_CASE_COMMON(name), \
.fails = true, \
}
struct core_reloc_test_case {
const char *case_name;
const char *bpf_obj_file;
......@@ -183,6 +198,7 @@ struct core_reloc_test_case {
const char *output;
int output_len;
bool fails;
bool relaxed_core_relocs;
};
static struct core_reloc_test_case test_cases[] = {
......@@ -283,6 +299,59 @@ static struct core_reloc_test_case test_cases[] = {
},
.output_len = sizeof(struct core_reloc_misc_output),
},
/* validate field existence checks */
{
EXISTENCE_CASE_COMMON(existence),
.input = STRUCT_TO_CHAR_PTR(core_reloc_existence) {
.a = 1,
.b = 2,
.c = 3,
.arr = { 4 },
.s = { .x = 5 },
},
.input_len = sizeof(struct core_reloc_existence),
.output = STRUCT_TO_CHAR_PTR(core_reloc_existence_output) {
.a_exists = 1,
.b_exists = 1,
.c_exists = 1,
.arr_exists = 1,
.s_exists = 1,
.a_value = 1,
.b_value = 2,
.c_value = 3,
.arr_value = 4,
.s_value = 5,
},
.output_len = sizeof(struct core_reloc_existence_output),
},
{
EXISTENCE_CASE_COMMON(existence___minimal),
.input = STRUCT_TO_CHAR_PTR(core_reloc_existence___minimal) {
.a = 42,
},
.input_len = sizeof(struct core_reloc_existence),
.output = STRUCT_TO_CHAR_PTR(core_reloc_existence_output) {
.a_exists = 1,
.b_exists = 0,
.c_exists = 0,
.arr_exists = 0,
.s_exists = 0,
.a_value = 42,
.b_value = 0xff000002u,
.c_value = 0xff000003u,
.arr_value = 0xff000004u,
.s_value = 0xff000005u,
},
.output_len = sizeof(struct core_reloc_existence_output),
},
EXISTENCE_ERR_CASE(existence__err_int_sz),
EXISTENCE_ERR_CASE(existence__err_int_type),
EXISTENCE_ERR_CASE(existence__err_int_kind),
EXISTENCE_ERR_CASE(existence__err_arr_kind),
EXISTENCE_ERR_CASE(existence__err_arr_value_type),
EXISTENCE_ERR_CASE(existence__err_struct_type),
};
struct data {
......@@ -305,11 +374,14 @@ void test_core_reloc(void)
for (i = 0; i < ARRAY_SIZE(test_cases); i++) {
test_case = &test_cases[i];
if (!test__start_subtest(test_case->case_name))
continue;
obj = bpf_object__open(test_case->bpf_obj_file);
LIBBPF_OPTS(bpf_object_open_opts, opts,
.relaxed_core_relocs = test_case->relaxed_core_relocs,
);
obj = bpf_object__open_file(test_case->bpf_obj_file, &opts);
if (CHECK(IS_ERR_OR_NULL(obj), "obj_open",
"failed to open '%s': %ld\n",
test_case->bpf_obj_file, PTR_ERR(obj)))
......
#include "core_reloc_types.h"
void f(struct core_reloc_existence x) {}
#include "core_reloc_types.h"
void f(struct core_reloc_existence___err_wrong_arr_kind x) {}
#include "core_reloc_types.h"
void f(struct core_reloc_existence___err_wrong_arr_value_type x) {}
#include "core_reloc_types.h"
void f(struct core_reloc_existence___err_wrong_int_kind x) {}
#include "core_reloc_types.h"
void f(struct core_reloc_existence___err_wrong_int_sz x) {}
#include "core_reloc_types.h"
void f(struct core_reloc_existence___err_wrong_int_type x) {}
#include "core_reloc_types.h"
void f(struct core_reloc_existence___err_wrong_struct_type x) {}
#include "core_reloc_types.h"
void f(struct core_reloc_existence___minimal x) {}
......@@ -674,3 +674,59 @@ struct core_reloc_misc_extensible {
int c;
int d;
};
/*
* EXISTENCE
*/
struct core_reloc_existence_output {
int a_exists;
int a_value;
int b_exists;
int b_value;
int c_exists;
int c_value;
int arr_exists;
int arr_value;
int s_exists;
int s_value;
};
struct core_reloc_existence {
int a;
struct {
int b;
};
int c;
int arr[1];
struct {
int x;
} s;
};
struct core_reloc_existence___minimal {
int a;
};
struct core_reloc_existence___err_wrong_int_sz {
short a;
};
struct core_reloc_existence___err_wrong_int_type {
int b[1];
};
struct core_reloc_existence___err_wrong_int_kind {
struct{ int x; } c;
};
struct core_reloc_existence___err_wrong_arr_kind {
int arr;
};
struct core_reloc_existence___err_wrong_arr_value_type {
short arr[1];
};
struct core_reloc_existence___err_wrong_struct_type {
int s;
};
// SPDX-License-Identifier: GPL-2.0
// Copyright (c) 2019 Facebook
#include <linux/bpf.h>
#include <stdint.h>
#include "bpf_helpers.h"
#include "bpf_core_read.h"
char _license[] SEC("license") = "GPL";
static volatile struct data {
char in[256];
char out[256];
} data;
struct core_reloc_existence_output {
int a_exists;
int a_value;
int b_exists;
int b_value;
int c_exists;
int c_value;
int arr_exists;
int arr_value;
int s_exists;
int s_value;
};
struct core_reloc_existence {
struct {
int x;
} s;
int arr[1];
int a;
struct {
int b;
};
int c;
};
SEC("raw_tracepoint/sys_enter")
int test_core_existence(void *ctx)
{
struct core_reloc_existence *in = (void *)&data.in;
struct core_reloc_existence_output *out = (void *)&data.out;
out->a_exists = bpf_core_field_exists(in->a);
if (bpf_core_field_exists(in->a))
out->a_value = BPF_CORE_READ(in, a);
else
out->a_value = 0xff000001u;
out->b_exists = bpf_core_field_exists(in->b);
if (bpf_core_field_exists(in->b))
out->b_value = BPF_CORE_READ(in, b);
else
out->b_value = 0xff000002u;
out->c_exists = bpf_core_field_exists(in->c);
if (bpf_core_field_exists(in->c))
out->c_value = BPF_CORE_READ(in, c);
else
out->c_value = 0xff000003u;
out->arr_exists = bpf_core_field_exists(in->arr);
if (bpf_core_field_exists(in->arr))
out->arr_value = BPF_CORE_READ(in, arr[0]);
else
out->arr_value = 0xff000004u;
out->s_exists = bpf_core_field_exists(in->s);
if (bpf_core_field_exists(in->s))
out->s_value = BPF_CORE_READ(in, s.x);
else
out->s_value = 0xff000005u;
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