mirror of
https://github.com/torvalds/linux.git
synced 2026-11-07 06:17:02 +01:00
btf: Relocate and expose module inline BTF
Module .BTF.inline is split relative to its module BTF. For external modules, module BTF can instead be split relative to a distilled .BTF.base and is relocated against vmlinux when the module loads. Parse ordinary module inline BTF relative to the pre-relocation module BTF. Retain the module relocation ID and string maps, then rebase inline type IDs and string offsets onto the final vmlinux/module BTF space before exposing the result through sysfs. Malformed or unrelocatable optional inline BTF is warned about and omitted without preventing normal module BTF registration. The btf_vmlinux_inline carrier remains on its existing hash-checked, vmalloc-backed publishing path, preserving mmap support for vmlinux.inline when delivered by a module. Signed-off-by: Alan Maguire <alan.maguire@oracle.com> Signed-off-by: Alexei Starovoitov <ast@kernel.org> Link: https://patch.msgid.link/20261009112324.1442319-6-alan.maguire@oracle.com
This commit is contained in:
parent
5876080366
commit
42d7153fcb
247
kernel/bpf/btf.c
247
kernel/bpf/btf.c
|
|
@ -6925,51 +6925,22 @@ __u32 btf_relocate_id(const struct btf *btf, __u32 id)
|
|||
|
||||
#ifdef CONFIG_DEBUG_INFO_BTF_MODULES
|
||||
|
||||
static struct btf *btf_parse_module(const char *module_name, const void *data,
|
||||
unsigned int data_size, void *base_data,
|
||||
unsigned int base_data_size)
|
||||
static struct btf *btf_parse_split(struct btf_verifier_env *env,
|
||||
const char *name, const void *data,
|
||||
unsigned int data_size, struct btf *base_btf)
|
||||
{
|
||||
struct btf *btf = NULL, *vmlinux_btf, *base_btf = NULL;
|
||||
struct btf_verifier_env *env = NULL;
|
||||
struct bpf_verifier_log *log;
|
||||
int err = 0;
|
||||
|
||||
vmlinux_btf = bpf_get_btf_vmlinux();
|
||||
if (IS_ERR(vmlinux_btf))
|
||||
return vmlinux_btf;
|
||||
if (!vmlinux_btf)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
env = kzalloc_obj(*env, GFP_KERNEL | __GFP_NOWARN);
|
||||
if (!env)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
log = &env->log;
|
||||
log->level = BPF_LOG_KERNEL;
|
||||
|
||||
if (base_data) {
|
||||
base_btf = btf_parse_base(env, ".BTF.base", base_data, base_data_size);
|
||||
if (IS_ERR(base_btf)) {
|
||||
err = PTR_ERR(base_btf);
|
||||
goto errout;
|
||||
}
|
||||
} else {
|
||||
base_btf = vmlinux_btf;
|
||||
}
|
||||
struct btf *btf;
|
||||
int err;
|
||||
|
||||
btf = kzalloc_obj(*btf, GFP_KERNEL | __GFP_NOWARN);
|
||||
if (!btf) {
|
||||
err = -ENOMEM;
|
||||
goto errout;
|
||||
}
|
||||
if (!btf)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
env->btf = btf;
|
||||
|
||||
btf->base_btf = base_btf;
|
||||
btf->start_id = base_btf->nr_types;
|
||||
btf->start_str_off = base_btf->hdr.str_len;
|
||||
btf_set_base_btf(btf, base_btf);
|
||||
btf->kernel_btf = true;
|
||||
btf->named_start_id = 0;
|
||||
strscpy(btf->name, module_name);
|
||||
strscpy(btf->name, name);
|
||||
|
||||
btf->data = kvmemdup(data, data_size, GFP_KERNEL | __GFP_NOWARN);
|
||||
if (!btf->data) {
|
||||
|
|
@ -6996,28 +6967,180 @@ static struct btf *btf_parse_module(const char *module_name, const void *data,
|
|||
if (err)
|
||||
goto errout;
|
||||
|
||||
return btf;
|
||||
|
||||
errout:
|
||||
btf_free(btf);
|
||||
return ERR_PTR(err);
|
||||
}
|
||||
|
||||
static int btf_rebase_inline(struct btf *inline_btf,
|
||||
const struct btf *module_btf,
|
||||
const u32 *module_id_map,
|
||||
const u32 *module_str_map,
|
||||
u32 old_module_type_cnt)
|
||||
{
|
||||
u32 old_start_id = inline_btf->start_id;
|
||||
u32 old_start_str_off = inline_btf->start_str_off;
|
||||
u32 old_module_start_str_off = old_start_str_off - module_btf->hdr.str_len;
|
||||
u32 new_start_id = btf_nr_types(module_btf);
|
||||
u32 new_start_str_off = module_btf->start_str_off + module_btf->hdr.str_len;
|
||||
s64 id_delta = (s64)new_start_id - old_start_id;
|
||||
s64 str_delta = (s64)new_start_str_off - old_start_str_off;
|
||||
u32 i;
|
||||
|
||||
/*
|
||||
* The inline BTF was parsed relative to the original module BTF. Its
|
||||
* base IDs must therefore use the map generated when that BTF was
|
||||
* relocated, while IDs for inline-local types only move by the change
|
||||
* in the module BTF's starting ID.
|
||||
*/
|
||||
for (i = 0; i < inline_btf->nr_types; i++) {
|
||||
struct btf_field_iter it;
|
||||
struct btf_type *t = inline_btf->types[i];
|
||||
u32 *id, *str_off;
|
||||
int err;
|
||||
|
||||
err = btf_field_iter_init(&it, t, BTF_FIELD_ITER_IDS);
|
||||
if (err)
|
||||
return err;
|
||||
while ((id = btf_field_iter_next(&it))) {
|
||||
if (!*id)
|
||||
continue;
|
||||
if (*id < old_module_type_cnt) {
|
||||
if (module_id_map)
|
||||
*id = module_id_map[*id];
|
||||
} else if (*id >= old_start_id) {
|
||||
*id += id_delta;
|
||||
} else {
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
err = btf_field_iter_init(&it, t, BTF_FIELD_ITER_STRS);
|
||||
if (err)
|
||||
return err;
|
||||
while ((str_off = btf_field_iter_next(&it))) {
|
||||
if (!*str_off)
|
||||
continue;
|
||||
if (*str_off < old_module_start_str_off) {
|
||||
/* Vmlinux strings retain their offsets for in-tree modules. */
|
||||
if (!module_id_map)
|
||||
continue;
|
||||
if (!module_str_map || !module_str_map[*str_off])
|
||||
return -EINVAL;
|
||||
*str_off = module_str_map[*str_off];
|
||||
continue;
|
||||
}
|
||||
*str_off += str_delta;
|
||||
}
|
||||
}
|
||||
|
||||
btf_set_base_btf(inline_btf, module_btf);
|
||||
btf_check_sorted(inline_btf);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct btf *btf_parse_module(const char *module_name, const void *data,
|
||||
unsigned int data_size, void *base_data,
|
||||
unsigned int base_data_size,
|
||||
const void *inline_data,
|
||||
unsigned int inline_data_size,
|
||||
void **relocated_inline_data)
|
||||
{
|
||||
struct btf *btf = NULL, *inline_btf = NULL, *vmlinux_btf, *base_btf = NULL;
|
||||
struct btf_verifier_env *env = NULL;
|
||||
struct bpf_verifier_log *log;
|
||||
u32 *module_str_map = NULL;
|
||||
u32 old_module_type_cnt;
|
||||
int err = 0;
|
||||
|
||||
*relocated_inline_data = NULL;
|
||||
vmlinux_btf = bpf_get_btf_vmlinux();
|
||||
if (IS_ERR(vmlinux_btf))
|
||||
return vmlinux_btf;
|
||||
if (!vmlinux_btf)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
env = kzalloc_obj(*env, GFP_KERNEL | __GFP_NOWARN);
|
||||
if (!env)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
log = &env->log;
|
||||
log->level = BPF_LOG_KERNEL;
|
||||
|
||||
if (base_data) {
|
||||
base_btf = btf_parse_base(env, ".BTF.base", base_data, base_data_size);
|
||||
if (IS_ERR(base_btf)) {
|
||||
err = PTR_ERR(base_btf);
|
||||
goto errout;
|
||||
}
|
||||
} else {
|
||||
base_btf = vmlinux_btf;
|
||||
}
|
||||
|
||||
btf = btf_parse_split(env, module_name, data, data_size, base_btf);
|
||||
if (IS_ERR(btf)) {
|
||||
err = PTR_ERR(btf);
|
||||
btf = NULL;
|
||||
goto errout;
|
||||
}
|
||||
old_module_type_cnt = btf_nr_types(btf);
|
||||
|
||||
if (inline_data_size) {
|
||||
inline_btf = btf_parse_split(env, module_name, inline_data,
|
||||
inline_data_size, btf);
|
||||
if (IS_ERR(inline_btf)) {
|
||||
pr_warn("failed to validate module [%s] inline BTF: %ld\n",
|
||||
module_name, PTR_ERR(inline_btf));
|
||||
inline_btf = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (base_btf != vmlinux_btf) {
|
||||
err = btf_relocate(btf, vmlinux_btf, &btf->base_id_map, NULL);
|
||||
err = btf_relocate(btf, vmlinux_btf, &btf->base_id_map,
|
||||
&module_str_map);
|
||||
if (err)
|
||||
goto errout;
|
||||
btf_free(base_btf);
|
||||
base_btf = vmlinux_btf;
|
||||
}
|
||||
|
||||
btf_verifier_env_free(env);
|
||||
if (inline_btf) {
|
||||
err = btf_rebase_inline(inline_btf, btf, btf->base_id_map,
|
||||
module_str_map, old_module_type_cnt);
|
||||
if (err) {
|
||||
pr_warn("failed to relocate module [%s] inline BTF: %d\n",
|
||||
module_name, err);
|
||||
btf_free(inline_btf);
|
||||
} else {
|
||||
*relocated_inline_data = inline_btf->data;
|
||||
inline_btf->data = NULL;
|
||||
btf_free(inline_btf);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* With a distilled base, btf_relocate() replaces the base BTF and
|
||||
* rewrites string offsets. Check ordering only after that final BTF
|
||||
* view has been established, so named_start_id describes the BTF used
|
||||
* by name lookups.
|
||||
*/
|
||||
btf_check_sorted(btf);
|
||||
btf_verifier_env_free(env);
|
||||
kvfree(module_str_map);
|
||||
refcount_set(&btf->refcnt, 1);
|
||||
return btf;
|
||||
|
||||
errout:
|
||||
kvfree(module_str_map);
|
||||
btf_verifier_env_free(env);
|
||||
if (inline_btf)
|
||||
btf_free(inline_btf);
|
||||
if (!IS_ERR(base_btf) && base_btf != vmlinux_btf)
|
||||
btf_free(base_btf);
|
||||
if (btf) {
|
||||
kvfree(btf->data);
|
||||
kvfree(btf->types);
|
||||
kfree(btf);
|
||||
}
|
||||
if (btf)
|
||||
btf_free(btf);
|
||||
return ERR_PTR(err);
|
||||
}
|
||||
|
||||
|
|
@ -9119,6 +9242,8 @@ struct btf_module {
|
|||
struct module *module;
|
||||
struct btf *btf;
|
||||
struct bin_attribute *sysfs_attr;
|
||||
struct bin_attribute *sysfs_inline_attr;
|
||||
void *btf_inline_data;
|
||||
int flags;
|
||||
};
|
||||
|
||||
|
|
@ -9238,6 +9363,8 @@ static int btf_module_notify(struct notifier_block *nb, unsigned long op,
|
|||
struct module *mod = module;
|
||||
struct bin_attribute *attr;
|
||||
struct btf *btf;
|
||||
void *inline_data = NULL, *relocated_inline_data = NULL;
|
||||
unsigned int inline_data_size = 0;
|
||||
int err = 0;
|
||||
|
||||
if (op != MODULE_STATE_COMING && op != MODULE_STATE_LIVE &&
|
||||
|
|
@ -9251,6 +9378,12 @@ static int btf_module_notify(struct notifier_block *nb, unsigned long op,
|
|||
break;
|
||||
}
|
||||
|
||||
#if IS_ENABLED(CONFIG_DEBUG_INFO_BTF_INLINE)
|
||||
if (mod->btf_inline_data_size) {
|
||||
inline_data = mod->btf_inline_data;
|
||||
inline_data_size = mod->btf_inline_data_size;
|
||||
}
|
||||
#endif
|
||||
if (!mod->btf_data_size)
|
||||
break;
|
||||
btf_mod = kzalloc_obj(*btf_mod);
|
||||
|
|
@ -9259,7 +9392,9 @@ static int btf_module_notify(struct notifier_block *nb, unsigned long op,
|
|||
goto out;
|
||||
}
|
||||
btf = btf_parse_module(mod->name, mod->btf_data, mod->btf_data_size,
|
||||
mod->btf_base_data, mod->btf_base_data_size);
|
||||
mod->btf_base_data, mod->btf_base_data_size,
|
||||
inline_data, inline_data_size,
|
||||
&relocated_inline_data);
|
||||
if (IS_ERR(btf)) {
|
||||
kfree(btf_mod);
|
||||
if (!IS_ENABLED(CONFIG_MODULE_ALLOW_BTF_MISMATCH)) {
|
||||
|
|
@ -9274,6 +9409,7 @@ static int btf_module_notify(struct notifier_block *nb, unsigned long op,
|
|||
err = btf_alloc_id(btf);
|
||||
if (err) {
|
||||
btf_free(btf);
|
||||
kvfree(relocated_inline_data);
|
||||
kfree(btf_mod);
|
||||
goto out;
|
||||
}
|
||||
|
|
@ -9282,6 +9418,7 @@ static int btf_module_notify(struct notifier_block *nb, unsigned long op,
|
|||
mutex_lock(&btf_module_mutex);
|
||||
btf_mod->module = module;
|
||||
btf_mod->btf = btf;
|
||||
btf_mod->btf_inline_data = relocated_inline_data;
|
||||
list_add(&btf_mod->list, &btf_modules);
|
||||
mutex_unlock(&btf_module_mutex);
|
||||
|
||||
|
|
@ -9291,6 +9428,21 @@ static int btf_module_notify(struct notifier_block *nb, unsigned long op,
|
|||
goto out;
|
||||
}
|
||||
btf_mod->sysfs_attr = attr;
|
||||
|
||||
if (relocated_inline_data) {
|
||||
char name[MODULE_NAME_LEN + sizeof(".inline")];
|
||||
|
||||
snprintf(name, sizeof(name), "%s.inline", mod->name);
|
||||
attr = sysfs_btf_add(name, relocated_inline_data,
|
||||
inline_data_size, false, NULL);
|
||||
if (IS_ERR(attr)) {
|
||||
kvfree(relocated_inline_data);
|
||||
btf_mod->btf_inline_data = NULL;
|
||||
err = 0;
|
||||
break;
|
||||
}
|
||||
btf_mod->sysfs_inline_attr = attr;
|
||||
}
|
||||
break;
|
||||
case MODULE_STATE_LIVE:
|
||||
if (!mod->btf_data_size)
|
||||
|
|
@ -9323,6 +9475,9 @@ static int btf_module_notify(struct notifier_block *nb, unsigned long op,
|
|||
list_del(&btf_mod->list);
|
||||
if (btf_mod->sysfs_attr)
|
||||
sysfs_btf_remove(btf_mod->sysfs_attr);
|
||||
if (btf_mod->sysfs_inline_attr)
|
||||
sysfs_btf_remove(btf_mod->sysfs_inline_attr);
|
||||
kvfree(btf_mod->btf_inline_data);
|
||||
purge_cand_cache(btf_mod->btf);
|
||||
btf_put(btf_mod->btf);
|
||||
kfree(btf_mod);
|
||||
|
|
|
|||
|
|
@ -68,10 +68,7 @@ esac
|
|||
|
||||
gen_btf_data()
|
||||
{
|
||||
inline=""
|
||||
if [ -n "${BTF_INLINE}" ] && [ -z "${BTF_BASE}" ]; then
|
||||
inline="--inline"
|
||||
fi
|
||||
inline=${BTF_INLINE:+--inline}
|
||||
btf1="${ELF_FILE}.BTF.1"
|
||||
${PAHOLE} -J ${PAHOLE_FLAGS} \
|
||||
${BTF_BASE:+--btf_base ${BTF_BASE}} \
|
||||
|
|
@ -130,7 +127,7 @@ embed_btf_data()
|
|||
if [ -f "${btf_base}" ]; then
|
||||
${OBJCOPY} --add-section .BTF.base=${btf_base} ${ELF_FILE}
|
||||
fi
|
||||
btf_inline=""
|
||||
btf_inline=${ELF_FILE}.BTF.inline
|
||||
case "${ELF_FILE}" in
|
||||
*/btf_vmlinux_inline.ko)
|
||||
btf_inline=${BTF_BASE}.BTF.inline
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user