summaryrefslogtreecommitdiff
path: root/tools/testing/selftests/bpf/test_loader.c
diff options
context:
space:
mode:
Diffstat (limited to 'tools/testing/selftests/bpf/test_loader.c')
-rw-r--r--tools/testing/selftests/bpf/test_loader.c187
1 files changed, 174 insertions, 13 deletions
diff --git a/tools/testing/selftests/bpf/test_loader.c b/tools/testing/selftests/bpf/test_loader.c
index c4c34cae6102..abdb9e6e3713 100644
--- a/tools/testing/selftests/bpf/test_loader.c
+++ b/tools/testing/selftests/bpf/test_loader.c
@@ -63,6 +63,7 @@ struct test_spec {
struct test_subspec priv;
struct test_subspec unpriv;
const char *btf_custom_path;
+ const char *btf_custom_func_path;
int log_level;
int prog_flags;
int mode_mask;
@@ -93,7 +94,7 @@ void test_loader_fini(struct test_loader *tester)
free(tester->log_buf);
}
-static void free_msgs(struct expected_msgs *msgs)
+void free_msgs(struct expected_msgs *msgs)
{
int i;
@@ -590,6 +591,8 @@ static int parse_test_spec(struct test_loader *tester,
jit_on_next_line = true;
} else if ((val = str_has_pfx(s, "test_btf_path="))) {
spec->btf_custom_path = val;
+ } else if ((val = str_has_pfx(s, "test_btf_func_path="))) {
+ spec->btf_custom_func_path = val;
} else if ((val = str_has_pfx(s, "test_caps_unpriv="))) {
err = parse_caps(val, &spec->unpriv.caps, "test caps");
if (err)
@@ -789,6 +792,43 @@ static void emit_stderr(const char *stderr, bool force)
fprintf(stdout, "STDERR:\n=============\n%s=============\n", stderr);
}
+static void verify_stderr(int prog_fd, struct expected_msgs *msgs)
+{
+ LIBBPF_OPTS(bpf_prog_stream_read_opts, ropts);
+ char *buf;
+ int ret;
+
+ if (!msgs->cnt)
+ return;
+
+ buf = malloc(TEST_LOADER_LOG_BUF_SZ);
+ if (!ASSERT_OK_PTR(buf, "malloc"))
+ return;
+
+ ret = bpf_prog_stream_read(prog_fd, 2, buf, TEST_LOADER_LOG_BUF_SZ - 1,
+ &ropts);
+ if (ret > 0) {
+ buf[ret] = '\0';
+ emit_stderr(buf, false);
+ validate_msgs(buf, msgs, emit_stderr);
+ } else {
+ ASSERT_GT(ret, 0, "stderr stream read");
+ }
+
+ free(buf);
+}
+
+void verify_test_stderr(struct bpf_object *obj, struct bpf_program *prog)
+{
+ struct test_spec spec = {};
+
+ if (parse_test_spec(NULL, obj, prog, &spec))
+ return;
+
+ verify_stderr(bpf_program__fd(prog), &spec.priv.stderr);
+ free_test_spec(&spec);
+}
+
static void emit_stdout(const char *bpf_stdout, bool force)
{
if (!force && env.verbosity == VERBOSE_NONE)
@@ -1138,6 +1178,123 @@ static int get_stream(int stream_id, int prog_fd, char *text, size_t text_sz)
return ret;
}
+/*
+ * Fix up the program's BTF using BTF from a separate file.
+ *
+ * For __naked subprogs, clang drops parameter names from BTF. Find FUNC
+ * entries with anonymous parameters and replace their FUNC_PROTO with the
+ * properly-named version from the custom file.
+ */
+static int fixup_btf_from_path(struct bpf_object *obj, const char *path)
+{
+ struct btf *prog_btf, *custom_btf;
+ __u32 i, j, cnt, custom_cnt;
+ int err = 0;
+
+ prog_btf = bpf_object__btf(obj);
+ if (!prog_btf)
+ return 0;
+
+ custom_btf = btf__parse(path, NULL);
+ if (!ASSERT_OK_PTR(custom_btf, "parse_custom_btf"))
+ return -EINVAL;
+
+ cnt = btf__type_cnt(prog_btf);
+ custom_cnt = btf__type_cnt(custom_btf);
+
+ /* Fix up FUNC entries with anonymous params.
+ * Save all data from prog_btf BEFORE calling btf__add_*,
+ * since those calls may reallocate the BTF data buffer
+ * and invalidate any pointers obtained from btf__type_by_id.
+ */
+ for (i = 1; i < cnt; i++) {
+ const struct btf_type *t = btf__type_by_id(prog_btf, i);
+ const struct btf_type *fp, *custom_t, *custom_fp;
+ const struct btf_param *params, *custom_params;
+ __u32 ret_type_id, vlen;
+ __u32 *prog_param_types = NULL;
+ const char *name;
+ int new_proto_id;
+
+ if (!btf_is_func(t))
+ continue;
+
+ fp = btf__type_by_id(prog_btf, t->type);
+ if (!fp || !btf_is_func_proto(fp) || btf_vlen(fp) == 0)
+ continue;
+
+ /* Check if any param is anonymous */
+ params = btf_params(fp);
+ if (params[0].name_off != 0)
+ continue;
+
+ /* Find matching FUNC by name in custom BTF */
+ name = btf__name_by_offset(prog_btf, t->name_off);
+ if (!name)
+ continue;
+
+ for (j = 1; j < custom_cnt; j++) {
+ const char *cname;
+
+ custom_t = btf__type_by_id(custom_btf, j);
+ if (!btf_is_func(custom_t))
+ continue;
+ cname = btf__name_by_offset(custom_btf, custom_t->name_off);
+ if (cname && strcmp(name, cname) == 0)
+ break;
+ }
+ if (j >= custom_cnt)
+ continue;
+
+ custom_fp = btf__type_by_id(custom_btf, custom_t->type);
+ if (!custom_fp || !btf_is_func_proto(custom_fp))
+ continue;
+
+ vlen = btf_vlen(fp);
+ if (vlen != btf_vlen(custom_fp))
+ continue;
+
+ /* Save data before btf__add_* calls invalidate pointers */
+ ret_type_id = fp->type;
+ prog_param_types = malloc(vlen * sizeof(*prog_param_types));
+ if (!prog_param_types) {
+ err = -ENOMEM;
+ break;
+ }
+ for (j = 0; j < vlen; j++)
+ prog_param_types[j] = params[j].type;
+
+ /* Add a new FUNC_PROTO: param names from custom, types from prog */
+ new_proto_id = btf__add_func_proto(prog_btf, ret_type_id);
+ if (new_proto_id < 0) {
+ err = new_proto_id;
+ free(prog_param_types);
+ break;
+ }
+
+ custom_params = btf_params(custom_fp);
+ for (j = 0; j < vlen; j++) {
+ const char *pname;
+
+ pname = btf__name_by_offset(custom_btf, custom_params[j].name_off);
+ err = btf__add_func_param(prog_btf, pname ?: "", prog_param_types[j]);
+ if (err)
+ break;
+ }
+ free(prog_param_types);
+ if (err)
+ break;
+
+ /* Update the FUNC to point to the new FUNC_PROTO (re-fetch
+ * since btf__add_* may have reallocated the data buffer).
+ */
+ ((struct btf_type *)btf__type_by_id(prog_btf, i))->type = new_proto_id;
+ }
+
+ btf__free(custom_btf);
+ return err;
+}
+
/* this function is forced noinline and has short generic name to look better
* in test_progs output (in case of a failure)
*/
@@ -1194,13 +1351,27 @@ void run_subtest(struct test_loader *tester,
}
}
- /* Implicitly reset to NULL if next test case doesn't specify */
+ /* Implicitly reset to NULL if next test case doesn't specify.
+ * btf_custom_func_path also serves as btf_custom_path for kfunc resolution.
+ */
open_opts->btf_custom_path = spec->btf_custom_path;
+ if (!open_opts->btf_custom_path)
+ open_opts->btf_custom_path = spec->btf_custom_func_path;
tobj = bpf_object__open_mem(obj_bytes, obj_byte_cnt, open_opts);
if (!ASSERT_OK_PTR(tobj, "obj_open_mem")) /* shouldn't happen */
goto subtest_cleanup;
+ /* Fix up __naked subprog BTF using a separate file with named params */
+ if (spec->btf_custom_func_path) {
+ err = fixup_btf_from_path(tobj, spec->btf_custom_func_path);
+ if (err) {
+ PRINT_FAIL("failed to fixup BTF from %s: %d\n",
+ spec->btf_custom_func_path, err);
+ goto tobj_cleanup;
+ }
+ }
+
i = 0;
bpf_object__for_each_program(tprog_iter, tobj) {
spec_iter = &specs[i++];
@@ -1314,17 +1485,7 @@ void run_subtest(struct test_loader *tester,
goto tobj_cleanup;
}
- if (subspec->stderr.cnt) {
- err = get_stream(2, bpf_program__fd(tprog),
- tester->log_buf, tester->log_buf_sz);
- if (err <= 0) {
- PRINT_FAIL("Unexpected retval from get_stream(): %d, errno = %d\n",
- err, errno);
- goto tobj_cleanup;
- }
- emit_stderr(tester->log_buf, false /*force*/);
- validate_msgs(tester->log_buf, &subspec->stderr, emit_stderr);
- }
+ verify_stderr(bpf_program__fd(tprog), &subspec->stderr);
if (subspec->stdout.cnt) {
err = get_stream(1, bpf_program__fd(tprog),