From: Jim Cromie <jim.cromie@gmail.com>
To: Andrew Morton <akpm@linux-foundation.org>
Cc: Lorenzo Stoakes <ljs@kernel.org>, Kees Cook <kees@kernel.org>,
Masahiro Yamada <masahiroy@kernel.org>,
linux-kernel@vger.kernel.org, linux-kbuild@vger.kernel.org,
bpf@vger.kernel.org, Jim Cromie <jim.cromie@gmail.com>
Subject: [PATCH 2/3] kallsyms: Add 3-byte index into compressed symbols to replace marker scans
Date: Sat, 19 Sep 2026 21:58:56 -0600 [thread overview]
Message-ID: <20260919-ksyms-tune-v1-2-d85c97da1a32@gmail.com> (raw)
In-Reply-To: <20260919-ksyms-tune-v1-0-d85c97da1a32@gmail.com>
The compressed symbol table (kallsyms_names) packs ~130k kernel symbol
names, in address order, into variable-length records with format
[<len>][<tokenized-strings-wo-\0>].
This layout optimizes address-to-name mapping, but name-to-address
lookups require a linear scan. To accelerate lookups, kallsyms_markers
was added to record the offset of every 256th entry, cutting the
worst-case walk from 130k to ~128 hops on average. However, this
still leaves substantial work: during a 17-step binary search in
kallsyms_lookup_names(), the marker walk repeats at every step
(17 * 128), decoding ~2,176 record length headers per lookup.
Address-to-name resolution (sprint_symbol) pays the same 0..255 hop
penalty on every call.
Introduce kallsyms_names_offsets, a 3-byte-per-symbol direct index into
the compressed kallsyms_names table. scripts/kallsyms.c emits this
table at build-time while writing kallsyms_names, capturing the exact
byte offset for each symbol. Using 24 bits covers up to 16 MiB of
compressed symbol names, easily spanning the ~2.3 MiB table while
saving 25% space compared to u32 entries.
With kallsyms_names_offsets:
0. get_symbol_offset() performs an O(1) 3-byte table lookup, eliminating
the ~2,176 header scans per name search.
1. Drop the legacy kallsyms_markers table, saving ~2 KiB of .rodata.
2. Unroll the shift loop in get_symbol_seq() to match
get_symbol_offset() as a direct 3-byte big-endian load.
Signed-off-by: Jim Cromie <jim.cromie@gmail.com>
---
kernel/kallsyms.c | 43 +++++++------------------------------------
kernel/kallsyms_internal.h | 2 +-
scripts/kallsyms.c | 30 ++++++++++++++----------------
3 files changed, 22 insertions(+), 53 deletions(-)
diff --git a/kernel/kallsyms.c b/kernel/kallsyms.c
index b9e573e9a10b..21adc5b74ec5 100644
--- a/kernel/kallsyms.c
+++ b/kernel/kallsyms.c
@@ -113,40 +113,14 @@ static char kallsyms_get_symbol_type(unsigned int off)
/*
- * Find the offset on the compressed stream given and index in the
+ * Find the offset on the compressed table given an index in the
* kallsyms array.
*/
-static unsigned int get_symbol_offset(unsigned long pos)
+static inline unsigned int get_symbol_offset(unsigned long pos)
{
- const u8 *name;
- int i, len;
+ const u8 *p = &kallsyms_names_offsets[3 * pos];
- /*
- * Use the closest marker we have. We have markers every 256 positions,
- * so that should be close enough.
- */
- name = &kallsyms_names[kallsyms_markers[pos >> 8]];
-
- /*
- * Sequentially scan all the symbols up to the point we're searching
- * for. Every symbol is stored in a [<len>][<len> bytes of data] format,
- * so we just need to add the len to the current pointer for every
- * symbol we wish to skip.
- */
- for (i = 0; i < (pos & 0xFF); i++) {
- len = *name;
-
- /*
- * If MSB is 1, it is a "big" symbol, so we need to look into
- * the next byte (and skip it, too).
- */
- if ((len & 0x80) != 0)
- len = ((len & 0x7F) | (name[1] << 7)) + 1;
-
- name = name + len + 1;
- }
-
- return name - kallsyms_names;
+ return (p[0] << 16) | (p[1] << 8) | p[2];
}
unsigned long kallsyms_sym_address(int idx)
@@ -157,14 +131,11 @@ unsigned long kallsyms_sym_address(int idx)
return (unsigned long)offset_to_ptr(kallsyms_offsets + idx);
}
-static unsigned int get_symbol_seq(int index)
+static inline unsigned int get_symbol_seq(int index)
{
- unsigned int i, seq = 0;
-
- for (i = 0; i < 3; i++)
- seq = (seq << 8) | kallsyms_seqs_of_names[3 * index + i];
+ const u8 *p = &kallsyms_seqs_of_names[3 * index];
- return seq;
+ return (p[0] << 16) | (p[1] << 8) | p[2];
}
static int kallsyms_lookup_names(const char *name,
diff --git a/kernel/kallsyms_internal.h b/kernel/kallsyms_internal.h
index 81a867dbe57d..430abccfab63 100644
--- a/kernel/kallsyms_internal.h
+++ b/kernel/kallsyms_internal.h
@@ -12,7 +12,7 @@ extern const unsigned int kallsyms_num_syms;
extern const char kallsyms_token_table[];
extern const u16 kallsyms_token_index[];
-extern const unsigned int kallsyms_markers[];
+extern const u8 kallsyms_names_offsets[];
extern const u8 kallsyms_seqs_of_names[];
#endif // LINUX_KALLSYMS_INTERNAL_H_
diff --git a/scripts/kallsyms.c b/scripts/kallsyms.c
index d996a43c4078..83a8747269ff 100644
--- a/scripts/kallsyms.c
+++ b/scripts/kallsyms.c
@@ -44,6 +44,7 @@ struct sym_entry {
unsigned long long addr;
unsigned int len;
unsigned int seq;
+ unsigned int byte_off;
unsigned char sym[];
};
@@ -393,7 +394,6 @@ static void write_src(FILE *out_bin_file, const char *out_bin_name)
{
unsigned int i, off;
unsigned int best_idx[256];
- unsigned int *markers, markers_cnt;
long bin_start;
char buf[KSYM_NAME_LEN];
@@ -403,18 +403,12 @@ static void write_src(FILE *out_bin_file, const char *out_bin_name)
printf("\t.long\t%u\n", table_cnt);
printf("\n");
- /* table of offset markers, that give the offset in the compressed stream
- * every 256 symbols */
- markers_cnt = (table_cnt + 255) / 256;
- markers = xmalloc(sizeof(*markers) * markers_cnt);
-
output_label("kallsyms_names");
bin_start = bin_pos(out_bin_file);
off = 0;
for (i = 0; i < table_cnt; i++) {
- if ((i & 0xFF) == 0)
- markers[i >> 8] = off;
table[i]->seq = i;
+ table[i]->byte_off = off;
/* There cannot be any symbol of length zero. */
if (table[i]->len == 0) {
@@ -454,14 +448,6 @@ static void write_src(FILE *out_bin_file, const char *out_bin_name)
printf(".size kallsyms_names, . - kallsyms_names\n");
printf("\n");
- output_label("kallsyms_markers");
- for (i = 0; i < markers_cnt; i++)
- printf("\t.long\t%u\n", markers[i]);
- printf(".size kallsyms_markers, . - kallsyms_markers\n");
- printf("\n");
-
- free(markers);
-
output_label("kallsyms_token_table");
bin_start = bin_pos(out_bin_file);
off = 0;
@@ -478,6 +464,7 @@ static void write_src(FILE *out_bin_file, const char *out_bin_name)
output_label("kallsyms_token_index");
for (i = 0; i < 256; i++)
printf("\t.short\t%d\n", best_idx[i]);
+ printf(".size kallsyms_token_index, . - kallsyms_token_index\n");
printf("\n");
output_label("kallsyms_offsets");
@@ -502,6 +489,16 @@ static void write_src(FILE *out_bin_file, const char *out_bin_name)
printf(".size kallsyms_offsets, . - kallsyms_offsets\n");
printf("\n");
+ output_label("kallsyms_names_offsets");
+ for (i = 0; i < table_cnt; i++)
+ printf("\t.byte 0x%02x, 0x%02x, 0x%02x\t/* %s */\n",
+ (unsigned char)(table[i]->byte_off >> 16),
+ (unsigned char)(table[i]->byte_off >> 8),
+ (unsigned char)(table[i]->byte_off >> 0),
+ table[i]->sym);
+ printf(".size kallsyms_names_offsets, . - kallsyms_names_offsets\n");
+ printf("\n");
+
sort_symbols_by_name();
output_label("kallsyms_seqs_of_names");
bin_start = bin_pos(out_bin_file);
@@ -511,6 +508,7 @@ static void write_src(FILE *out_bin_file, const char *out_bin_name)
fputc(table[i]->seq >> 0, out_bin_file);
}
write_incbin(out_bin_name, bin_start, bin_pos(out_bin_file));
+ printf(".size kallsyms_seqs_of_names, . - kallsyms_seqs_of_names\n");
printf("\n");
}
--
2.55.0
next prev parent reply other threads:[~2026-09-20 3:59 UTC|newest]
Thread overview: 9+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-09-20 3:58 [PATCH 0/3] kallsyms: Accelerate symbol name lookups by ~19x Jim Cromie
2026-09-20 3:58 ` [PATCH 1/3] kallsyms: Add test_kallsyms_perf module to benchmark lookup latency Jim Cromie
2026-09-20 3:58 ` Jim Cromie [this message]
2026-09-21 15:25 ` [PATCH 2/3] kallsyms: Add 3-byte index into compressed symbols to replace marker scans David Laight
2026-09-20 3:58 ` [PATCH 3/3] kallsyms: Match compressed tokens on the fly during binary search Jim Cromie
2026-09-21 12:00 ` [PATCH 0/3] kallsyms: Accelerate symbol name lookups by ~19x Jiri Olsa
2026-09-21 14:46 ` Lorenzo Stoakes (ARM)
2026-09-21 23:07 ` Kees Cook
2026-09-22 4:41 ` jim.cromie
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260919-ksyms-tune-v1-2-d85c97da1a32@gmail.com \
--to=jim.cromie@gmail.com \
--cc=akpm@linux-foundation.org \
--cc=bpf@vger.kernel.org \
--cc=kees@kernel.org \
--cc=linux-kbuild@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=ljs@kernel.org \
--cc=masahiroy@kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®