size_t nr_maps;
size_t maps_cap;
- char *kconfig_path;
+ char *kconfig;
struct extern_desc *externs;
int nr_extern;
int kconfig_map_idx;
return 0;
}
-static int bpf_object__read_kernel_config(struct bpf_object *obj,
- const char *config_path,
- void *data)
+static int bpf_object__process_kconfig_line(struct bpf_object *obj,
+ char *buf, void *data)
{
- char buf[PATH_MAX], *sep, *value;
struct extern_desc *ext;
+ char *sep, *value;
int len, err = 0;
void *ext_val;
__u64 num;
- gzFile file;
- if (config_path) {
- file = gzopen(config_path, "r");
- } else {
- struct utsname uts;
+ if (strncmp(buf, "CONFIG_", 7))
+ return 0;
- uname(&uts);
- len = snprintf(buf, PATH_MAX, "/boot/config-%s", uts.release);
- if (len < 0)
- return -EINVAL;
- else if (len >= PATH_MAX)
- return -ENAMETOOLONG;
- /* gzopen also accepts uncompressed files. */
- file = gzopen(buf, "r");
- if (!file)
- file = gzopen("/proc/config.gz", "r");
+ sep = strchr(buf, '=');
+ if (!sep) {
+ pr_warn("failed to parse '%s': no separator\n", buf);
+ return -EINVAL;
+ }
+
+ /* Trim ending '\n' */
+ len = strlen(buf);
+ if (buf[len - 1] == '\n')
+ buf[len - 1] = '\0';
+ /* Split on '=' and ensure that a value is present. */
+ *sep = '\0';
+ if (!sep[1]) {
+ *sep = '=';
+ pr_warn("failed to parse '%s': no value\n", buf);
+ return -EINVAL;
+ }
+
+ ext = find_extern_by_name(obj, buf);
+ if (!ext || ext->is_set)
+ return 0;
+
+ ext_val = data + ext->data_off;
+ value = sep + 1;
+
+ switch (*value) {
+ case 'y': case 'n': case 'm':
+ err = set_ext_value_tri(ext, ext_val, *value);
+ break;
+ case '"':
+ err = set_ext_value_str(ext, ext_val, value);
+ break;
+ default:
+ /* assume integer */
+ err = parse_u64(value, &num);
+ if (err) {
+ pr_warn("extern %s=%s should be integer\n",
+ ext->name, value);
+ return err;
+ }
+ err = set_ext_value_num(ext, ext_val, num);
+ break;
}
+ if (err)
+ return err;
+ pr_debug("extern %s=%s\n", ext->name, value);
+ return 0;
+}
+
+static int bpf_object__read_kconfig_file(struct bpf_object *obj, void *data)
+{
+ char buf[PATH_MAX];
+ struct utsname uts;
+ int len, err = 0;
+ gzFile file;
+
+ uname(&uts);
+ len = snprintf(buf, PATH_MAX, "/boot/config-%s", uts.release);
+ if (len < 0)
+ return -EINVAL;
+ else if (len >= PATH_MAX)
+ return -ENAMETOOLONG;
+
+ /* gzopen also accepts uncompressed files. */
+ file = gzopen(buf, "r");
+ if (!file)
+ file = gzopen("/proc/config.gz", "r");
+
if (!file) {
- pr_warn("failed to read kernel config at '%s'\n", config_path);
+ pr_warn("failed to open system Kconfig\n");
return -ENOENT;
}
while (gzgets(file, buf, sizeof(buf))) {
- if (strncmp(buf, "CONFIG_", 7))
- continue;
-
- sep = strchr(buf, '=');
- if (!sep) {
- err = -EINVAL;
- pr_warn("failed to parse '%s': no separator\n", buf);
- goto out;
- }
- /* Trim ending '\n' */
- len = strlen(buf);
- if (buf[len - 1] == '\n')
- buf[len - 1] = '\0';
- /* Split on '=' and ensure that a value is present. */
- *sep = '\0';
- if (!sep[1]) {
- err = -EINVAL;
- *sep = '=';
- pr_warn("failed to parse '%s': no value\n", buf);
+ err = bpf_object__process_kconfig_line(obj, buf, data);
+ if (err) {
+ pr_warn("error parsing system Kconfig line '%s': %d\n",
+ buf, err);
goto out;
}
+ }
- ext = find_extern_by_name(obj, buf);
- if (!ext)
- continue;
- if (ext->is_set) {
- err = -EINVAL;
- pr_warn("re-defining extern '%s' not allowed\n", buf);
- goto out;
- }
+out:
+ gzclose(file);
+ return err;
+}
- ext_val = data + ext->data_off;
- value = sep + 1;
+static int bpf_object__read_kconfig_mem(struct bpf_object *obj,
+ const char *config, void *data)
+{
+ char buf[PATH_MAX];
+ int err = 0;
+ FILE *file;
- switch (*value) {
- case 'y': case 'n': case 'm':
- err = set_ext_value_tri(ext, ext_val, *value);
- break;
- case '"':
- err = set_ext_value_str(ext, ext_val, value);
- break;
- default:
- /* assume integer */
- err = parse_u64(value, &num);
- if (err) {
- pr_warn("extern %s=%s should be integer\n",
- ext->name, value);
- goto out;
- }
- err = set_ext_value_num(ext, ext_val, num);
+ file = fmemopen((void *)config, strlen(config), "r");
+ if (!file) {
+ err = -errno;
+ pr_warn("failed to open in-memory Kconfig: %d\n", err);
+ return err;
+ }
+
+ while (fgets(buf, sizeof(buf), file)) {
+ err = bpf_object__process_kconfig_line(obj, buf, data);
+ if (err) {
+ pr_warn("error parsing in-memory Kconfig line '%s': %d\n",
+ buf, err);
break;
}
- if (err)
- goto out;
- pr_debug("extern %s=%s\n", ext->name, value);
}
-out:
- gzclose(file);
+ fclose(file);
return err;
}
__bpf_object__open(const char *path, const void *obj_buf, size_t obj_buf_sz,
const struct bpf_object_open_opts *opts)
{
- const char *obj_name, *kconfig_path;
+ const char *obj_name, *kconfig;
struct bpf_program *prog;
struct bpf_object *obj;
char tmp_name[64];
return obj;
obj->relaxed_core_relocs = OPTS_GET(opts, relaxed_core_relocs, false);
- kconfig_path = OPTS_GET(opts, kconfig_path, NULL);
- if (kconfig_path) {
- obj->kconfig_path = strdup(kconfig_path);
- if (!obj->kconfig_path)
+ kconfig = OPTS_GET(opts, kconfig, NULL);
+ if (kconfig) {
+ obj->kconfig = strdup(kconfig);
+ if (!obj->kconfig)
return ERR_PTR(-ENOMEM);
}
}
static int bpf_object__resolve_externs(struct bpf_object *obj,
- const char *config_path)
+ const char *extra_kconfig)
{
bool need_config = false;
struct extern_desc *ext;
return -EINVAL;
}
}
+ if (need_config && extra_kconfig) {
+ err = bpf_object__read_kconfig_mem(obj, extra_kconfig, data);
+ if (err)
+ return -EINVAL;
+ need_config = false;
+ for (i = 0; i < obj->nr_extern; i++) {
+ ext = &obj->externs[i];
+ if (!ext->is_set) {
+ need_config = true;
+ break;
+ }
+ }
+ }
if (need_config) {
- err = bpf_object__read_kernel_config(obj, config_path, data);
+ err = bpf_object__read_kconfig_file(obj, data);
if (err)
return -EINVAL;
}
obj->loaded = true;
err = bpf_object__probe_caps(obj);
- err = err ? : bpf_object__resolve_externs(obj, obj->kconfig_path);
+ err = err ? : bpf_object__resolve_externs(obj, obj->kconfig);
err = err ? : bpf_object__sanitize_and_load_btf(obj);
err = err ? : bpf_object__sanitize_maps(obj);
err = err ? : bpf_object__create_maps(obj);
zfree(&map->pin_path);
}
- zfree(&obj->kconfig_path);
+ zfree(&obj->kconfig);
zfree(&obj->externs);
obj->nr_extern = 0;
static struct test_case {
const char *name;
const char *cfg;
- const char *cfg_path;
bool fails;
struct test_core_extern__data data;
} test_cases[] = {
- { .name = "default search path", .cfg_path = NULL,
- .data = { .bpf_syscall = true } },
- { .name = "/proc/config.gz", .cfg_path = "/proc/config.gz",
- .data = { .bpf_syscall = true } },
- { .name = "missing config", .fails = true,
- .cfg_path = "/proc/invalid-config.gz" },
+ { .name = "default search path", .data = { .bpf_syscall = true } },
{
.name = "custom values",
- .cfg = "CONFIG_BPF_SYSCALL=y\n"
+ .cfg = "CONFIG_BPF_SYSCALL=n\n"
"CONFIG_TRISTATE=m\n"
"CONFIG_BOOL=y\n"
"CONFIG_CHAR=100\n"
"CONFIG_STR=\"abracad\"\n"
"CONFIG_MISSING=0",
.data = {
- .bpf_syscall = true,
+ .bpf_syscall = false,
.tristate_val = TRI_MODULE,
.bool_val = true,
.char_val = 100,
int n = sizeof(*skel->data) / sizeof(uint64_t);
for (i = 0; i < ARRAY_SIZE(test_cases); i++) {
- char tmp_cfg_path[] = "/tmp/test_core_extern_cfg.XXXXXX";
struct test_case *t = &test_cases[i];
DECLARE_LIBBPF_OPTS(bpf_object_open_opts, opts,
- .kconfig_path = t->cfg_path,
+ .kconfig = t->cfg,
);
if (!test__start_subtest(t->name))
continue;
- if (t->cfg) {
- size_t n = strlen(t->cfg) + 1;
- int fd = mkstemp(tmp_cfg_path);
- int written;
-
- if (CHECK(fd < 0, "mkstemp", "errno: %d\n", errno))
- continue;
- printf("using '%s' as config file\n", tmp_cfg_path);
- written = write(fd, t->cfg, n);
- close(fd);
- if (CHECK_FAIL(written != n))
- goto cleanup;
- opts.kconfig_path = tmp_cfg_path;
- }
-
skel = test_core_extern__open_opts(&opts);
if (CHECK(!skel, "skel_open", "skeleton open failed\n"))
goto cleanup;
j, exp[j], got[j]);
}
cleanup:
- if (t->cfg)
- unlink(tmp_cfg_path);
test_core_extern__destroy(skel);
skel = NULL;
}