From 0ead8c29ff979ef8a545c209388d0671497f9422 Mon Sep 17 00:00:00 2001 From: Mohammed Riad <1@mhmrdd.me> Date: Thu, 23 Jul 2026 03:30:19 +0100 Subject: [PATCH 1/5] preset: add btf_offset and btf_size fields --- kernel/base/setup1.S | 8 ++++++++ kernel/base/start.h | 6 +++++- kernel/include/preset.h | 6 +++++- 3 files changed, 18 insertions(+), 2 deletions(-) diff --git a/kernel/base/setup1.S b/kernel/base/setup1.S index c366ac59..10f4641d 100644 --- a/kernel/base/setup1.S +++ b/kernel/base/setup1.S @@ -107,6 +107,14 @@ start_prepare: ldr x13, [x10, #setup_kconfig_size_offset] str x13, [x11, #start_kconfig_size_offset] + // start_preset.btf_offset = setup_preset.btf_offset + ldr x13, [x10, #setup_btf_offset_offset] + str x13, [x11, #start_btf_offset_offset] + + // start_preset.btf_size = setup_preset.btf_size + ldr x13, [x10, #setup_btf_size_offset] + str x13, [x11, #start_btf_size_offset] + // memcpy(&start_preset.setup, &setup_header, KP_HEADER_SIZE); add x0, x11, #start_header_offset add x1, x12, #0 diff --git a/kernel/base/start.h b/kernel/base/start.h index 9fc3f28c..01600bd2 100644 --- a/kernel/base/start.h +++ b/kernel/base/start.h @@ -24,6 +24,8 @@ typedef struct int64_t symbol_lookup_anchor_offset; int64_t kconfig_offset; int64_t kconfig_size; + int64_t btf_offset; + int64_t btf_size; int64_t map_backup_len; uint8_t map_backup[MAP_MAX_SIZE]; uint8_t superkey[SUPER_KEY_LEN]; @@ -43,7 +45,9 @@ typedef struct #define start_symbol_lookup_anchor_offset_offset (start_sprintf_offset_offset + 8) #define start_kconfig_offset_offset (start_symbol_lookup_anchor_offset_offset + 8) #define start_kconfig_size_offset (start_kconfig_offset_offset + 8) -#define start_map_backup_len_offset (start_kconfig_size_offset + 8) +#define start_btf_offset_offset (start_kconfig_size_offset + 8) +#define start_btf_size_offset (start_btf_offset_offset + 8) +#define start_map_backup_len_offset (start_btf_size_offset + 8) #define start_map_backup_offset (start_map_backup_len_offset + 8) #define start_superkey_offset (start_map_backup_offset + MAP_MAX_SIZE) #define start_root_superkey_offset (start_superkey_offset + SUPER_KEY_LEN) diff --git a/kernel/include/preset.h b/kernel/include/preset.h index 22d9721b..740fa7e9 100644 --- a/kernel/include/preset.h +++ b/kernel/include/preset.h @@ -269,7 +269,9 @@ typedef struct _setup_preset_t int64_t symbol_lookup_anchor_offset; int64_t kconfig_offset; int64_t kconfig_size; - uint8_t __[SETUP_PRESERVE_LEN - 32]; + int64_t btf_offset; + int64_t btf_size; + uint8_t __[SETUP_PRESERVE_LEN - 48]; patch_config_t patch_config; char additional[ADDITIONAL_LEN]; } setup_preset_t; @@ -295,6 +297,8 @@ typedef struct _setup_preset_t #define setup_symbol_lookup_anchor_offset_offset (setup_sprintf_offset_offset + 8) #define setup_kconfig_offset_offset (setup_symbol_lookup_anchor_offset_offset + 8) #define setup_kconfig_size_offset (setup_kconfig_offset_offset + 8) +#define setup_btf_offset_offset (setup_kconfig_size_offset + 8) +#define setup_btf_size_offset (setup_btf_offset_offset + 8) #define setup_patch_config_offset (setup_root_superkey_offset + ROOT_SUPER_KEY_HASH_LEN + SETUP_PRESERVE_LEN) #define setup_end (setup_patch_config_offset + PATCH_CONFIG_LEN) #endif From b66dc27376347cccd522db964695417c71168da6 Mon Sep 17 00:00:00 2001 From: Mohammed Riad <1@mhmrdd.me> Date: Thu, 23 Jul 2026 03:30:19 +0100 Subject: [PATCH 2/5] tools: locate the kernel BTF blob and record it in preset --- tools/kallsym.c | 34 ++++++++++++++++++++++++++++++++++ tools/kallsym.h | 1 + tools/patch.c | 17 +++++++++++++++++ 3 files changed, 52 insertions(+) diff --git a/tools/kallsym.c b/tools/kallsym.c index 0863702b..501347b8 100644 --- a/tools/kallsym.c +++ b/tools/kallsym.c @@ -44,6 +44,40 @@ int find_ikconfig_blob(char *img, int32_t imglen, size_t *start, size_t *size) return 0; } +// BTF blob (CONFIG_DEBUG_INFO_BTF) located by header magic, validated to reject +// false positives. Does not rely on __start_BTF/__stop_BTF symbols. +int find_btf_blob(char *img, int32_t imglen, size_t *start, size_t *size) +{ + const uint8_t magic[4] = { 0x9f, 0xeb, 0x01, 0x00 }; // magic 0xeb9f (LE) + version 1 + size_t off = 0; + while (off + 24 <= (size_t)imglen) { + char *p = memmem(img + off, imglen - off, magic, sizeof(magic)); + if (!p) break; + size_t o = p - img; + + const uint8_t *h = (const uint8_t *)p; + uint32_t hdr_len = h[4] | (h[5] << 8) | (h[6] << 16) | ((uint32_t)h[7] << 24); + uint32_t type_off = h[8] | (h[9] << 8) | (h[10] << 16) | ((uint32_t)h[11] << 24); + uint32_t type_len = h[12] | (h[13] << 8) | (h[14] << 16) | ((uint32_t)h[15] << 24); + uint32_t str_off = h[16] | (h[17] << 8) | (h[18] << 16) | ((uint32_t)h[19] << 24); + uint32_t str_len = h[20] | (h[21] << 8) | (h[22] << 16) | ((uint32_t)h[23] << 24); + + // offsets are relative to the end of the header + uint64_t str_end = (uint64_t)hdr_len + str_off + str_len; + int ok = hdr_len >= 24 && type_off == 0 && type_len > 0 && str_len > 0 && + (uint64_t)type_off + type_len <= str_off && o + str_end <= (size_t)imglen && + p[hdr_len + str_off] == '\0'; // BTF string section starts with a NUL + + if (ok) { + *start = o; + *size = (size_t)str_end; + return 0; + } + off = o + sizeof(magic); + } + return 1; +} + int find_linux_banner(kallsym_t *info, char *img, int32_t imglen, void *opt) { /* diff --git a/tools/kallsym.h b/tools/kallsym.h index 179ed9cf..fbaa5c5a 100644 --- a/tools/kallsym.h +++ b/tools/kallsym.h @@ -124,6 +124,7 @@ int dump_all_symbols(kallsym_t *info, char *img); int dump_all_ikconfig(char *img, int32_t imglen); int extract_ikconfig(char *img, int32_t imglen, char **out, int32_t *outlen); int find_ikconfig_blob(char *img, int32_t imglen, size_t *start, size_t *size); +int find_btf_blob(char *img, int32_t imglen, size_t *start, size_t *size); int is_symbol_exists(kallsym_t *info, char *img, const char *symbol); int get_symbol_index_offset(kallsym_t *info, char *img, int32_t index); int get_symbol_offset_and_size(kallsym_t *info, char *img, char *symbol, int32_t *size); diff --git a/tools/patch.c b/tools/patch.c index ffedc599..4b896228 100644 --- a/tools/patch.c +++ b/tools/patch.c @@ -559,6 +559,15 @@ int patch_update_img(const char *kimg_path, const char *kpimg_path, const char * tools_logi("ikconfig gzip blob at 0x%zx, size 0x%zx (runtime puff)\n", kcfg_start, kcfg_bytes); } + // locate the kernel's embedded BTF blob (CONFIG_DEBUG_INFO_BTF) + size_t btf_start = 0, btf_bytes = 0; + int btf_rc = find_btf_blob(kallsym_kimg, pimg.ori_kimg_len, &btf_start, &btf_bytes); + if (btf_rc) { + tools_logw("kernel BTF blob not found (rc=%d), btf unavailable at runtime\n", btf_rc); + } else { + tools_logi("btf blob at 0x%zx, size 0x%zx\n", btf_start, btf_bytes); + } + // kpimg char *kpimg = NULL; int kpimg_len = 0; @@ -833,9 +842,17 @@ int patch_update_img(const char *kimg_path, const char *kpimg_path, const char * setup->kconfig_size = (int64_t)kcfg_bytes; } + // record BTF blob location for runtime parsing + if (!btf_rc) { + setup->btf_offset = (int64_t)btf_start; + setup->btf_size = (int64_t)btf_bytes; + } + if ((is_be() ^ kinfo->is_be)) { setup->kconfig_offset = i64swp(setup->kconfig_offset); setup->kconfig_size = i64swp(setup->kconfig_size); + setup->btf_offset = i64swp(setup->btf_offset); + setup->btf_size = i64swp(setup->btf_size); } // guard extra From 603a886e06eb26a55e0b318a2c02467dba9f2351 Mon Sep 17 00:00:00 2001 From: Mohammed Riad <1@mhmrdd.me> Date: Thu, 23 Jul 2026 03:30:19 +0100 Subject: [PATCH 3/5] predata: parse BTF and expose struct/offset query API --- kernel/base/predata.c | 292 ++++++++++++++++++++++++++++++++++++ kernel/patch/include/kbtf.h | 22 +++ 2 files changed, 314 insertions(+) create mode 100644 kernel/patch/include/kbtf.h diff --git a/kernel/base/predata.c b/kernel/base/predata.c index 2bef0dea..79c2d6e2 100644 --- a/kernel/base/predata.c +++ b/kernel/base/predata.c @@ -9,6 +9,7 @@ #include #include #include +#include #include #include "start.h" @@ -215,6 +216,297 @@ int kp_kconfig_size(void) } KP_EXPORT_SYMBOL(kp_kconfig_size); +#define BTF_MAGIC 0xeb9f +#define BTF_MAX_DEPTH 32 // matches the kernel's MAX_RESOLVE_DEPTH +#define BTF_KIND_INT 1 +#define BTF_KIND_PTR 2 +#define BTF_KIND_ARRAY 3 +#define BTF_KIND_STRUCT 4 +#define BTF_KIND_UNION 5 +#define BTF_KIND_ENUM 6 +#define BTF_KIND_FUNC_PROTO 13 +#define BTF_KIND_VAR 14 +#define BTF_KIND_DATASEC 15 +#define BTF_KIND_DECL_TAG 17 +#define BTF_KIND_ENUM64 19 + +#define BTF_INFO_KIND(info) (((info) >> 24) & 0x1f) +#define BTF_INFO_VLEN(info) ((info) & 0xffff) +#define BTF_INFO_KFLAG(info) ((info) >> 31) +#define BTF_MEMBER_BIT_OFFSET(val) ((val) & 0xffffff) + +struct btf_header +{ + uint16_t magic; + uint8_t version; + uint8_t flags; + uint32_t hdr_len; + uint32_t type_off; + uint32_t type_len; + uint32_t str_off; + uint32_t str_len; +}; + +struct btf_type +{ + uint32_t name_off; + uint32_t info; + union + { + uint32_t size; + uint32_t type; + }; +}; + +struct btf_member +{ + uint32_t name_off; + uint32_t type; + uint32_t offset; +}; + +static const uint8_t *btf_base = 0; +static const struct btf_header *btf_hdr = 0; +static const char *btf_types = 0; +static const char *btf_strs = 0; +static int btf_scan_done = 0; + +static void ensure_btf_loaded(void) +{ + if (btf_base || btf_scan_done) return; + btf_scan_done = 1; + + int64_t off = start_preset.btf_offset; + int64_t size = start_preset.btf_size; + if (!off || !size) return; + if (off < 0 || size < (int64_t)sizeof(struct btf_header) || off > kernel_size || size > kernel_size - off) { + log_boot("btf: bad preset range, off=%lld, size=%lld\n", off, size); + return; + } + + const struct btf_header *h = (const struct btf_header *)(kernel_va + off); + if (h->magic != BTF_MAGIC || h->version != 1 || h->flags != 0 || h->hdr_len < sizeof(struct btf_header)) { + log_boot("btf: bad header magic=%x ver=%d\n", h->magic, h->version); + return; + } + // sections are relative to the end of the header; type then str, no overlap, + // str section must end exactly at the blob end and be NUL-bounded. + uint64_t body = (uint64_t)size - h->hdr_len; + if ((uint64_t)h->type_off + h->type_len > h->str_off || (uint64_t)h->str_off + h->str_len != body || !h->str_len) { + log_boot("btf: bad section layout\n"); + return; + } + const char *strs = (const char *)h + h->hdr_len + h->str_off; + if (strs[0] != '\0' || strs[h->str_len - 1] != '\0') { + log_boot("btf: string section not NUL-bounded\n"); + return; + } + + btf_base = (const uint8_t *)h; + btf_hdr = h; + btf_types = (const char *)btf_base + h->hdr_len + h->type_off; + btf_strs = strs; + log_boot("btf loaded, types: %d, strs: %d\n", h->type_len, h->str_len); +} + +int kp_btf_available(void) +{ + ensure_btf_loaded(); + return btf_base ? 1 : 0; +} +KP_EXPORT_SYMBOL(kp_btf_available); + +static const char *btf_name(uint32_t name_off) +{ + if (!btf_strs || name_off >= btf_hdr->str_len) return ""; + return btf_strs + name_off; +} + +// On-disk size of one btf_type record (fixed header + kind trailer), or 0 if +// the record (or its trailer) would run past the end of the type section. +static unsigned long btf_rec_size(const struct btf_type *t, const char *end) +{ + if ((const char *)(t + 1) > end) return 0; + uint32_t info = t->info; + uint64_t vlen = BTF_INFO_VLEN(info); // <= 0xffff, widen to avoid overflow + uint64_t extra = 0; + switch (BTF_INFO_KIND(info)) { + case BTF_KIND_INT: + case BTF_KIND_DECL_TAG: + case BTF_KIND_VAR: + extra = 4; + break; + case BTF_KIND_ARRAY: + extra = 12; + break; + case BTF_KIND_STRUCT: + case BTF_KIND_UNION: + case BTF_KIND_ENUM64: + case BTF_KIND_DATASEC: + extra = vlen * 12; + break; + case BTF_KIND_ENUM: + case BTF_KIND_FUNC_PROTO: + extra = vlen * 8; + break; + default: + extra = 0; + break; + } + uint64_t total = sizeof(struct btf_type) + extra; + if ((const char *)t + total > end) return 0; + return (unsigned long)total; +} + +static const struct btf_type *btf_type_by_name(const char *name, int want_kind) +{ + ensure_btf_loaded(); + if (!btf_base) return 0; + const char *end = btf_types + btf_hdr->type_len; + const struct btf_type *t = (const struct btf_type *)btf_types; + unsigned long sz; + while ((sz = btf_rec_size(t, end))) { + int kind = BTF_INFO_KIND(t->info); + if ((want_kind < 0 || kind == want_kind || (want_kind == BTF_KIND_STRUCT && kind == BTF_KIND_UNION)) && + t->name_off && !lib_strcmp(btf_name(t->name_off), name)) { + return t; + } + t = (const struct btf_type *)((const char *)t + sz); + } + return 0; +} + +long kp_btf_type_size(const char *name) +{ + const struct btf_type *t = btf_type_by_name(name, -1); + if (!t) return -1; + int kind = BTF_INFO_KIND(t->info); + if (kind == BTF_KIND_STRUCT || kind == BTF_KIND_UNION || kind == BTF_KIND_INT) return t->size; + return -1; +} +KP_EXPORT_SYMBOL(kp_btf_type_size); + +static const struct btf_type *btf_type_by_id(uint32_t id) +{ + if (!btf_base || !id) return 0; + const char *end = btf_types + btf_hdr->type_len; + const struct btf_type *t = (const struct btf_type *)btf_types; + unsigned long sz; + for (uint32_t i = 1; (sz = btf_rec_size(t, end)); i++) { + if (i == id) return t; + t = (const struct btf_type *)((const char *)t + sz); + } + return 0; +} + +// Resolve typedef/const/volatile/restrict wrappers down to the underlying type. +static const struct btf_type *btf_skip_mods(const struct btf_type *t, int depth) +{ + while (t && depth-- > 0) { + int kind = BTF_INFO_KIND(t->info); + if (kind != 8 /*typedef*/ && kind != 10 /*const*/ && kind != 11 /*volatile*/ && kind != 12 /*restrict*/) + break; + t = btf_type_by_id(t->type); + } + return t; +} + +// Recursive member search: descends into anonymous struct/union members, adding +// their byte offset. Returns byte offset within the top struct, or -1. +static long btf_member_offset_rec(const struct btf_type *t, const char *member, int depth) +{ + if (!t || depth <= 0) return -1; + int kind = BTF_INFO_KIND(t->info); + if (kind != BTF_KIND_STRUCT && kind != BTF_KIND_UNION) return -1; + + uint32_t vlen = BTF_INFO_VLEN(t->info); + int kflag = BTF_INFO_KFLAG(t->info); + const struct btf_member *m = (const struct btf_member *)(t + 1); + for (uint32_t i = 0; i < vlen; i++) { + uint32_t bitoff = kflag ? BTF_MEMBER_BIT_OFFSET(m[i].offset) : m[i].offset; + if (m[i].name_off && !lib_strcmp(btf_name(m[i].name_off), member)) { + return bitoff >> 3; + } + if (!m[i].name_off) { // anonymous struct/union -> recurse + const struct btf_type *sub = btf_skip_mods(btf_type_by_id(m[i].type), BTF_MAX_DEPTH); + long r = btf_member_offset_rec(sub, member, depth - 1); + if (r >= 0) return (long)(bitoff >> 3) + r; + } + } + return -1; +} + +long kp_btf_member_offset(const char *struct_name, const char *member) +{ + const struct btf_type *t = btf_type_by_name(struct_name, BTF_KIND_STRUCT); + return btf_member_offset_rec(t, member, BTF_MAX_DEPTH); +} +KP_EXPORT_SYMBOL(kp_btf_member_offset); + +const void *kp_btf_raw(long *size_out) +{ + ensure_btf_loaded(); + if (size_out) *size_out = btf_base ? start_preset.btf_size : 0; + return btf_base; +} +KP_EXPORT_SYMBOL(kp_btf_raw); + +// Low-level primitives so a KPM can walk the type graph itself with its own +// depth/cycle handling, for cases the bounded helpers above do not cover. + +int kp_btf_find_type(const char *name) +{ + ensure_btf_loaded(); + if (!btf_base) return -1; + const char *end = btf_types + btf_hdr->type_len; + const struct btf_type *t = (const struct btf_type *)btf_types; + unsigned long sz; + for (int id = 1; (sz = btf_rec_size(t, end)); id++) { + if (t->name_off && !lib_strcmp(btf_name(t->name_off), name)) return id; + t = (const struct btf_type *)((const char *)t + sz); + } + return -1; +} +KP_EXPORT_SYMBOL(kp_btf_find_type); + +int kp_btf_type_info(int type_id, const char **name_out, int *kind_out, long *size_out) +{ + const struct btf_type *t = btf_type_by_id((uint32_t)type_id); + if (!t) return -1; + if (name_out) *name_out = btf_name(t->name_off); + if (kind_out) *kind_out = BTF_INFO_KIND(t->info); + if (size_out) *size_out = t->size; + return BTF_INFO_VLEN(t->info); // member/param count for struct/union/enum/func_proto +} +KP_EXPORT_SYMBOL(kp_btf_type_info); + +int kp_btf_member(int type_id, int idx, const char **name_out, int *type_id_out, long *bit_offset_out) +{ + const struct btf_type *t = btf_type_by_id((uint32_t)type_id); + if (!t) return -1; + int kind = BTF_INFO_KIND(t->info); + if (kind != BTF_KIND_STRUCT && kind != BTF_KIND_UNION) return -1; + if (idx < 0 || (uint32_t)idx >= BTF_INFO_VLEN(t->info)) return -1; + const struct btf_member *m = (const struct btf_member *)(t + 1) + idx; + uint32_t off = BTF_INFO_KFLAG(t->info) ? BTF_MEMBER_BIT_OFFSET(m->offset) : m->offset; + if (name_out) *name_out = btf_name(m->name_off); + if (type_id_out) *type_id_out = m->type; + if (bit_offset_out) *bit_offset_out = off; + return 0; +} +KP_EXPORT_SYMBOL(kp_btf_member); + +// Skip one typedef/const/volatile/restrict wrapper; returns the underlying id. +int kp_btf_skip_mod(int type_id) +{ + const struct btf_type *t = btf_type_by_id((uint32_t)type_id); + if (!t) return -1; + int kind = BTF_INFO_KIND(t->info); + if (kind == 8 || kind == 9 || kind == 10 || kind == 11 || kind == 18) return (int)t->type; + return type_id; +} +KP_EXPORT_SYMBOL(kp_btf_skip_mod); + static int find_ikconfig_deflate(const uint8_t *gz, int64_t gz_len, const uint8_t **deflate_start, unsigned long *deflate_len, unsigned long *out_len) { diff --git a/kernel/patch/include/kbtf.h b/kernel/patch/include/kbtf.h new file mode 100644 index 00000000..e63b2079 --- /dev/null +++ b/kernel/patch/include/kbtf.h @@ -0,0 +1,22 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2026 bmax121. All Rights Reserved. + */ + +#ifndef _KP_KBTF_H_ +#define _KP_KBTF_H_ + +#include + +int kp_btf_available(void); +long kp_btf_type_size(const char *name); +long kp_btf_member_offset(const char *struct_name, const char *member); +const void *kp_btf_raw(long *size_out); + +// Low-level type-graph walk (arbitrary depth, caller-managed cycle handling). +int kp_btf_find_type(const char *name); +int kp_btf_type_info(int type_id, const char **name_out, int *kind_out, long *size_out); +int kp_btf_member(int type_id, int idx, const char **name_out, int *type_id_out, long *bit_offset_out); +int kp_btf_skip_mod(int type_id); + +#endif From 65693b54f4b55dc4212a37425a2ad2fc6cab27cc Mon Sep 17 00:00:00 2001 From: Mohammed Riad <1@mhmrdd.me> Date: Thu, 23 Jul 2026 03:30:20 +0100 Subject: [PATCH 4/5] task_cred: override struct offsets with BTF when available --- kernel/patch/ksyms/task_cred.c | 39 ++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/kernel/patch/ksyms/task_cred.c b/kernel/patch/ksyms/task_cred.c index d78ad80c..bc0b6fba 100644 --- a/kernel/patch/ksyms/task_cred.c +++ b/kernel/patch/ksyms/task_cred.c @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -609,6 +610,42 @@ int resolve_mm_struct_offset() return 0; } +static void btf_set(int16_t *dst, const char *s, const char *m) +{ + long o = kp_btf_member_offset(s, m); + if (o >= 0 && o <= 0x7fff) *dst = (int16_t)o; +} + +// Override the offset tables with authoritative BTF values when the kernel +// embeds BTF (GKI 5.10+). Runs after the heuristic resolvers, so it is a no-op +// on kernels without BTF and never changes their behavior there. +static void resolve_struct_btf(void) +{ + if (!kp_btf_available()) return; + log_boot("struct offsets: overriding with BTF\n"); + + btf_set(&task_struct_offset.pid_offset, "task_struct", "pid"); + btf_set(&task_struct_offset.tgid_offset, "task_struct", "tgid"); + btf_set(&task_struct_offset.real_cred_offset, "task_struct", "real_cred"); + btf_set(&task_struct_offset.cred_offset, "task_struct", "cred"); + btf_set(&task_struct_offset.comm_offset, "task_struct", "comm"); + btf_set(&task_struct_offset.fs_offset, "task_struct", "fs"); + btf_set(&task_struct_offset.files_offset, "task_struct", "files"); + btf_set(&task_struct_offset.seccomp_offset, "task_struct", "seccomp"); + btf_set(&task_struct_offset.security_offset, "task_struct", "security"); + btf_set(&task_struct_offset.mm_offset, "task_struct", "mm"); + btf_set(&task_struct_offset.active_mm_offset, "task_struct", "active_mm"); + + btf_set(&cred_offset.uid_offset, "cred", "uid"); + btf_set(&cred_offset.gid_offset, "cred", "gid"); + btf_set(&cred_offset.suid_offset, "cred", "suid"); + btf_set(&cred_offset.sgid_offset, "cred", "sgid"); + btf_set(&cred_offset.euid_offset, "cred", "euid"); + btf_set(&cred_offset.egid_offset, "cred", "egid"); + + btf_set(&mm_struct_offset.pgd_offset, "mm_struct", "pgd"); +} + int resolve_struct() { full_cap = CAP_FULL_SET; @@ -623,6 +660,8 @@ int resolve_struct() resolve_mm_struct_offset(); + resolve_struct_btf(); + out: return err; } From 2e13f34630a65f441ca21b1c8d0b801254d31c07 Mon Sep 17 00:00:00 2001 From: Mohammed Riad <1@mhmrdd.me> Date: Thu, 23 Jul 2026 03:30:20 +0100 Subject: [PATCH 5/5] kpms: add BTF query demo --- kpms/demo-btf/Makefile | 27 ++++++++++++++ kpms/demo-btf/btf.c | 82 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 109 insertions(+) create mode 100644 kpms/demo-btf/Makefile create mode 100644 kpms/demo-btf/btf.c diff --git a/kpms/demo-btf/Makefile b/kpms/demo-btf/Makefile new file mode 100644 index 00000000..1add450a --- /dev/null +++ b/kpms/demo-btf/Makefile @@ -0,0 +1,27 @@ +ifndef TARGET_COMPILE + $(error TARGET_COMPILE not set) +endif + +ifndef KP_DIR + KP_DIR = ../.. +endif + +CC = $(TARGET_COMPILE)gcc + +INCLUDE_DIRS := . include patch/include linux/include linux/arch/arm64/include linux/tools/arch/arm64/include +INCLUDE_FLAGS := $(foreach dir,$(INCLUDE_DIRS),-I$(KP_DIR)/kernel/$(dir)) + +objs := btf.o + +all: btf.kpm + +btf.kpm: ${objs} + ${CC} -r -o $@ $^ + +%.o: %.c + ${CC} $(CFLAGS) $(INCLUDE_FLAGS) -c -O2 -o $@ $< + +.PHONY: clean +clean: + rm -rf *.kpm + find . -name "*.o" | xargs rm -f diff --git a/kpms/demo-btf/btf.c b/kpms/demo-btf/btf.c new file mode 100644 index 00000000..02b6603c --- /dev/null +++ b/kpms/demo-btf/btf.c @@ -0,0 +1,82 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2026 bmax121. All Rights Reserved. + */ + +#include +#include +#include +#include +#include + +KPM_NAME("kpm-btf-demo"); +KPM_VERSION("1.0.0"); +KPM_LICENSE("GPL v2"); +KPM_AUTHOR("bmax121"); +KPM_DESCRIPTION("Kernel BTF struct/offset query demo"); + +// Resolve a member by hand using the low-level primitives, descending into +// anonymous struct/union members with the caller's own cycle set. This has no +// fixed depth cap, unlike the convenience helper kp_btf_member_offset. +static long btf_walk_offset(int type_id, const char *member, long byte, const int *seen, int nseen) +{ + const char *name; + int kind; + int vlen = kp_btf_type_info(type_id, &name, &kind, 0); + if (vlen < 0 || (kind != 4 /*struct*/ && kind != 5 /*union*/)) return -1; + + for (int i = 0; i < nseen; i++) + if (seen[i] == type_id) return -1; // cycle + + int seen2[64]; + if (nseen >= 64) return -1; + for (int i = 0; i < nseen; i++) seen2[i] = seen[i]; + seen2[nseen] = type_id; + + for (int i = 0; i < vlen; i++) { + const char *mname; + int mtype; + long bitoff; + if (kp_btf_member(type_id, i, &mname, &mtype, &bitoff)) continue; + if (mname && mname[0] && !strcmp(mname, member)) return byte + (bitoff >> 3); + if (!mname || !mname[0]) { // anonymous -> recurse + long r = btf_walk_offset(kp_btf_skip_mod(mtype), member, byte + (bitoff >> 3), seen2, nseen + 1); + if (r >= 0) return r; + } + } + return -1; +} + +static long btf_demo_init(const char *args, const char *event, void *__user reserved) +{ + (void)reserved; + + pr_info("btf demo init, event: %s, args: %s\n", event, args); + + if (!kp_btf_available()) { + pr_info("kernel BTF is not available\n"); + return 0; + } + + // convenience helper (bounded depth) + pr_info("sizeof(struct task_struct): %ld\n", kp_btf_type_size("task_struct")); + pr_info("task_struct->cred offset: %ld\n", kp_btf_member_offset("task_struct", "cred")); + pr_info("cred->uid offset: %ld\n", kp_btf_member_offset("cred", "uid")); + pr_info("mm_struct->pgd offset: %ld\n", kp_btf_member_offset("mm_struct", "pgd")); + + // low-level primitives (manual walk, no depth cap) + int mm = kp_btf_find_type("mm_struct"); + pr_info("mm_struct type id: %d, pgd offset (manual walk): %ld\n", mm, btf_walk_offset(mm, "pgd", 0, 0, 0)); + + return 0; +} + +static long btf_demo_exit(void *__user reserved) +{ + (void)reserved; + pr_info("btf demo exit\n"); + return 0; +} + +KPM_INIT(btf_demo_init); +KPM_EXIT(btf_demo_exit);