Re: [PATCH bpf-next v2 1/6] kallsyms: Add KALLSYMS_QUERY ioctl to /proc/kallsyms
From: Andrii Nakryiko
Date: Thu Oct 29 2026 - 18:17:07 EST
On Thu, Oct 29, 2026 at 3:06 PM Samuel Wu <wusamuel@xxxxxxxxxx> wrote:
>
> On Wed, Oct 28, 2026 at 6:40 AM David Laight
> <david.laight.linux@xxxxxxxxx> wrote:
> >
> > On Thu, 22 Oct 2026 17:16:02 -0700
> > Samuel Wu <wusamuel@xxxxxxxxxx> wrote:
> >
> > > Add an extensible KALLSYMS_QUERY ioctl() interface to /proc/kallsyms
> > > to support fast point queries without scanning the entire text file.
> > > Exactly one query mode must be set in query_flags.
> > >
> > > When KALLSYMS_QUERY_BY_SYMBOL is set in query_flags, KALLSYMS_QUERY
> > > resolves a NUL-terminated symbol name in vmlinux or loaded modules and
> > > then populates sym_addr:
> > > - Searches vmlinux first via kallsyms_lookup_names() (binary search),
> > > which takes precedence over loaded modules, and falls back to
> > > module_kallsyms_on_each_symbol() under module_mutex for module
> > > symbols
> > > - Matches names exactly, like kallsyms_lookup_name(), disregarding the
> > > .llvm.<hash> matching, following precedent set by commit fb6a421fb615
> > > ("kallsyms: Match symbols exactly with CONFIG_LTO_CLANG")
> >
> > Hmmmm....
> > What happens when you have functions foo and foo.constprop0
> > In some sense they are duplicates.
>
> BY_NAME matches names exactly, like kallsyms_lookup_name(), so they
> would be different symbols at different addresses. Please correct me
> if I'm missing something here!
>
foo and foo.constprop0 are different functions with different
arguments, so I think we should distinguish them. The only special
case is .llvm.<hash> because that suffix is added by compiler for LTO
and I don't think the function itself is any different from the
original
> > > - Resolves 0x0 symbol addresses in both vmlinux and modules
> > > - Rejects ambiguous symbols (multiple entries with the same name at
> > > different addresses within vmlinux, or across loaded modules) with
> > > -EADDRNOTAVAIL, while allowing same-address aliases
> > >
> > > Userspace symbolizers resolve kernel addresses to symbol names with
> > > /proc/kallsyms. This is typically done by reading the entire
> > > /proc/kallsyms text file, and keeping a compressed sorted copy of it in
> > > order to resolve addresses at runtime.
> >
> > You could mmap the tables into userspace and have a ioctl that reported
> > the offsets of the tables (etc).
> > Then user lookup would faster then kernel lookup!
> > Especially since you could throw a bit more memory at it to remove the
> > linear scans.
>
> I think that would increase the UAPI surface because it would set the
> table format, so I'd prefer not to. It would only cover vmlinux and
> not modules, and then we'd need to apply kptr_restrict checks to the
> mapping.
>
> > >
> > > When KALLSYMS_QUERY_BY_ADDR is set in query_flags, KALLSYMS_QUERY
> > > resolves one address to the symbol that contains it:
> > > - sym_addr holds the address to resolve on input.
> > > - sym_name_addr must point to a user buffer of sym_name_size bytes
> > > that receives the NUL-terminated symbol name. On output,
> > > sym_name_size holds the number of bytes written (including the
> > > terminating NUL). -ENAMETOOLONG is returned if the buffer is too
> > > small; a buffer of KSYM_NAME_LEN (512 at the time of writing) bytes
> > > is always large enough.
> > > - Symbols are resolved via kallsyms_lookup(), the same lookup as %pS,
> > > covering vmlinux, loaded modules, BPF JIT images, and freed module
> > > init functions still tracked by ftrace.
> > >
> > > On an 8 CPU Cuttlefish (x86-64 VM), using this patchseries to load a BPF
> > > program with 11 typeless ksyms has ~220x speedup (157ms -> 0.7ms).
> >
> > Have you tried measuring it with my patches that speed up the kernel
> > 'search by name' code?
> > That speeds the lookup by a factor of ~18 (testing the code in userspace).
>
> Not yet. Can you point me to the latest version? Happy to try it out.
>
> > You also should allow for 'find first' and 'find next'.
> > As well as letting the code iterate though functions with the same name
> > it is absolutely trivial to enhance the 'lookup by name' code to match
> > a prefix.
>
> Agreed, that would be useful. In v3, the idea will be to have BY_NAME
> return the first match by default, and an ALL_MATCHES flag to report
> how many matches exist. Find next and prefix matching can be added
> later with breaking UAPI, and best to not include in v3 since there
> isn't yet a user.
I do think it would be good to spend a bit of thought on how to
support at least one-by-one pagination from the get go. The same
mechanism can then be used by batch version. I.e., think what can we
use to provide data back to user which they can use to continue search
after the symbol we just returned (at least for fixed set of symbols
in vmlinux and modules; we also have dynamic symbols added for [bpf]
"module", etc, we should support those as much as possible with the
least possible deviation, IMO)
>
> > (Basically it needs to 'return -index' instead of 'return -1' when the
> > binary scan fails.)
> >
> >
> > >
> > > On an 8 CPU x86-64 VM with a 175k line kallsyms, there are broad
> > > performance and memory savings for the symbolization and callstack
> > > sampling tool, Perfetto:
> > > - Init time: ~350x speedup (155.7ms -> 0.44ms)
> > > - 50k symbolizations: ~12x speedup (25.2ms -> 2.2ms)
> > > - VmRSS: -68% (15.1MB -> 4.8MB)
> > >
> > > Suggested-by: Andrii Nakryiko <andrii@xxxxxxxxxx>
> > > Signed-off-by: Samuel Wu <wusamuel@xxxxxxxxxx>
> > > ---
> > > include/uapi/linux/fs.h | 41 +++++++++++++
> > > kernel/kallsyms.c | 132 ++++++++++++++++++++++++++++++++++++++++
> > > 2 files changed, 173 insertions(+)
> > >
> > > diff --git a/include/uapi/linux/fs.h b/include/uapi/linux/fs.h
> > > index 34c6f219462a..b43f227dad0e 100644
> > > --- a/include/uapi/linux/fs.h
> > > +++ b/include/uapi/linux/fs.h
> > > @@ -674,6 +674,47 @@ struct procmap_query {
> > > __u64 build_id_addr; /* in */
> > > };
> > >
> > > +/* /proc/kallsyms ioctl */
> > > +#define KALLSYMS_QUERY _IOWR(PROCFS_IOCTL_MAGIC, 18, struct kallsyms_query)
> > > +
> > > +/* Exactly one query mode must be set in query_flags. */
> > > +enum kallsyms_query_flags {
> > > + /*
> > > + * Look up a symbol in vmlinux or loaded modules (vmlinux first; BPF
> > > + * and ftrace symbols are not searched) by exact NUL-terminated name
> > > + * at sym_name_addr and return sym_addr. sym_addr and sym_name_size
> > > + * must be 0 on input.
> > > + */
> > > + KALLSYMS_QUERY_BY_SYMBOL = 0x01,
> > > + /*
> > > + * Look up the symbol covering sym_addr and write its NUL-terminated
> > > + * name to the sym_name_addr buffer of sym_name_size bytes. The same
> > > + * symbols as %pS are searched: vmlinux, loaded modules, BPF JIT
> > > + * images, and freed module init functions still tracked by ftrace.
> > > + * A buffer of KSYM_NAME_LEN (512 at the time of writing) bytes is
> > > + * always enough. On success, sym_name_size is set to the number of
> > > + * bytes written (including the terminating NUL).
> > > + */
> > > + KALLSYMS_QUERY_BY_ADDR = 0x02,
> > > +};
> > > +
> > > +/*
> > > + * Returns -ENOENT if no symbol matches, -EADDRNOTAVAIL if the name maps to
> > > + * more than one address, -ENAMETOOLONG if the name does not fit in
> > > + * KSYM_NAME_LEN (512 at the time of writing) or sym_name_size bytes, and
> > > + * -EPERM if kallsyms addresses are hidden from the opener of /proc/kallsyms
> > > + * (e.g. kptr_restrict).
> > > + */
> > > +struct kallsyms_query {
> > > + __u64 size; /* in - struct size for compatibility */
> >
> > I'm not sure you need a size here as well as the one in the ioctl command.
> > It only complicates things when they differ.
> >
> > If you need a new version they you add an extra case to the switch statement.
> > Simple and easy,
>
> Good find thanks! Will update.
think about user space application compiled against UAPI headers from
older kernel version. For that application size will be smaller and
that's ok, newer kernel should honor this and only fill out smaller
part of v2 struct. Versioning is done by appending fields. This is a
well established pattern that provides a pretty good forward/backward
compat story, let's keep it.
>
> > > + __u64 query_flags; /* in - query mode (enum kallsyms_query_flags) */
> > > + __u64 sym_name_addr; /* in - address of symbol name buffer */
> > > + __u64 sym_addr; /* out (BY_SYMBOL) / in (BY_ADDR) */
> > > + __u64 sym_name_size; /* in/out - name buffer size / bytes written */
> >
> > Maybe separate the buffer size from the length of the symbol written?
> > The in/out parameter will get annoying in code doing multiple lookups.
> > Or just have the buffer size and require it to be '\0' terminated.
> > (There is a strncpy_from_user() can that be used.)
>
> That is simpler- in v3, we can have buffer sizes as input only, and
> names will always be NUL-terminated.
>
user code can be easily written by always setting buffer size, even if
kallsyms_query is reused. This is the pattern with PROCMAP_QUERY,
let's keep it consistent.
also, returning actual size even if buffer is too short is a good way
for user space to "probe" and increase buffer if necessary. unless it
adds tons of complexity, this is a good and flexible UAPI
> > The '\0' terminated string handling is likely to be noise against
> > all the rest of the lookup code.
> >
> > David
> >
[...]