Background
Anyone who has worked on Linux kernel development has probably come across kallsyms_lookup_name().
However, starting with Linux 5.7, this function was no longer exported, to prevent third-party modules from bypassing EXPORT_SYMBOL restrictions and accessing internal kernel functions.
Meanwhile, eBPF has continued to grow, but it still cannot replace traditional kernel modules in every scenario, such as hardware driver development or certain security features that need to suspend kernel operations while waiting for a decision from user space.
This makes me wonder: if traditional kernel modules have compatibility issues, why can't we improve their compatibility mechanisms while continuing to develop eBPF?
On Misuse
Removing the export of kallsyms_lookup_name() certainly makes it harder for third-party modules to access internal symbols.
But developers who genuinely need those symbols can still find other ways to locate them.
As a result, something that could previously be done through an exported function may now require more complicated workarounds.
For example, this change in Linux 5.7 affected LTTng.
I understand why kernel maintainers don't want third-party drivers to depend on internal implementation details. But removing a lookup interface doesn't make the need for it disappear.
Of course, being able to find a symbol doesn't mean it should be called without restrictions, either.
On Complexity
For out-of-tree driver developers, even if their projects are completely open source, they still have to deal with symbol CRC mismatches, changes to structure layouts, removed interfaces, and differences between Linux distributions.
eBPF has certainly solved some problems, but it cannot cover every use case.
We now have two kernel extension mechanisms with different capabilities, yet we still lack a general-purpose solution for cross-version compatibility of traditional out-of-tree modules.
Another Approach
Borrowing an idea from Occam's razor: since the kernel already has a dynamic module loading mechanism, why not improve it instead of leaving external developers to maintain their own complicated compatibility workarounds?
If kernel maintainers don't want to guarantee long-term stability for every internal symbol, could we instead guarantee just a minimal module loading interface and a more capable symbol lookup API?
For example, when a driver looks up a symbol, the kernel could return additional metadata:
- Symbol address
- Symbol type and ABI version information
- Function prototype or type hash
- The module that owns the symbol and its lifetime information
- Export status, access restrictions, and supported capabilities
For internal symbols that aren't exported, we could also consider more fine-grained access controls instead of simply denying access altogether.
As much type information as possible could be generated automatically by build tools, while behavioral compatibility would require explicit versioning rules. This could reduce the amount of manual maintenance required.
How to use that information would then be up to the driver developers.
This way, the kernel would still not need to guarantee long-term ABI stability for the vast majority of its internal functions.
So Why Not Give It a Try?
Of course, this design would come with costs.
The stable core interfaces would need to be maintained, and a version number alone cannot capture every change in a symbol's behavior. Defining access permissions for unexported internal symbols would also be a challenge.
But I think these problems deserve serious discussion, rather than treating "the kernel doesn't guarantee a stable ABI" as the end of the conversation.
If a core interface eventually needs a breaking change, a new version could be introduced, with a reasonable support period for the old one.
Red Hat and Android GKI have already implemented stable kernel ABIs within certain limits. At the very least, this shows that maintaining ABI stability is possible under the right conditions.
If we can let the kernel evolve freely while allowing external drivers to detect and adapt to changes on their own, why not give it a try?
PS
Note: This article was originally written by me in Chinese. I used AI to help translate it into English, but the ideas and opinions are entirely my own.
Original Chinese version: Linux 内核模块为什么不能自己解决兼容性问题?