* [PATCH v3 0/5] dyndbg: fix i386 alignment and KEEP module sections
From: Jim Cromie @ 2026-05-15 8:58 UTC (permalink / raw)
To: Arnd Bergmann, Jason Baron, Luis Chamberlain, Petr Pavlu,
Daniel Gomez, Sami Tolvanen, Aaron Tomlin
Cc: linux-arch, linux-kernel, linux-modules, Jim Cromie,
Louis Chauvet
This series starts with a fix to a section misalignement on i386, and
follows with cleanups and refactoring for reuse.
When CONFIG_DRM_USE_DYNAMIC_DEBUG=Y (currently marked BROKEN) and
CONFIG_DRM=y, the __dyndbg_classes section gets a single
struct/record. On i386 this is 28-bytes, breaking the required
ALIGN(8) on the following __dyndbg section.
This breaks dynamic_debug_init() with a NULL pointer dereference; the
section traversal logic encounters padding bytes instead of a valid
descriptor.
the series:
1. refactors BOUNDED_SECTION_* macros for later reuse
v2- adds note that macros are for vmlinux.lds.h only, not for modules.
2. drops unused HEADERED_SECTION_* macros
3. fixes ALIGN(8) omission causing NULL ptr on i386
adds,uses BOUNDED_SECTION _ALIGNED variants
4. removes now redundant ALIGN(8)s in vmlinux.lds.h
5. adds dyndbg.lds.S to KEEP sections in modules
follows codetag.lds.h model
v3:
move #includes to top of files,
drop redundant ALIGN(8) in dydnbg.lds.S: DYNDBG_SECTIONS macro
add Reviewed-by tag (thx Petr)
v2:
- avoids BOUNDED_SECTION in modules. Since ELF sections have well
defined bounaries, _start & _end symbols are useless, and empty
sections are unhelpful, possibly confusing to tools.
- sets 0 address to the sections, not just whatever current is.
Signed-off-by: Jim Cromie <jim.cromie@gmail.com>
---
Changes in v3:
- EDITME: describe what is new in this series revision.
- EDITME: use bulletpoints and terse descriptions.
- Link to v2: https://lore.kernel.org/r/20260507-asm-generic-1-v2-0-47c52759d268@gmail.com
Changes in v2:
- EDITME: describe what is new in this series revision.
- EDITME: use bulletpoints and terse descriptions.
- Link to v1: https://lore.kernel.org/r/20260502-asm-generic-1-v1-0-1103ee0152df@gmail.com
---
Jim Cromie (5):
vmlinux.lds.h: refactor BOUNDED_SECTION_* macros into bounded_sections.lds.h
vmlinux.lds.h: drop unused HEADERED_SECTION* macros
vmlinux.lds.h: Fix ALIGN(8) omission causing NULL ptr on i386
vmlinux.lds.h: remove redundant ALIGN(8) directives
dyndbg.lds.S: fix lost dyndbg sections in modules
MAINTAINERS | 1 +
include/asm-generic/bounded_sections.lds.h | 32 ++++++++++++++++
include/asm-generic/dyndbg.lds.h | 18 +++++++++
include/asm-generic/vmlinux.lds.h | 59 ++++--------------------------
scripts/module.lds.S | 2 +
5 files changed, 61 insertions(+), 51 deletions(-)
---
base-commit: 5d6919055dec134de3c40167a490f33c74c12581
change-id: 20260502-asm-generic-1-32c842a35dd0
Best regards,
--
Jim Cromie <jim.cromie@gmail.com>
^ permalink raw reply
* Re: [PATCH v2 5/5] dyndbg.lds.S: fix lost dyndbg sections in modules
From: Petr Pavlu @ 2026-05-14 6:46 UTC (permalink / raw)
To: Jim Cromie
Cc: Arnd Bergmann, Jason Baron, Luis Chamberlain, Daniel Gomez,
Sami Tolvanen, Aaron Tomlin, linux-arch, linux-kernel,
linux-modules
In-Reply-To: <20260507-asm-generic-1-v2-5-47c52759d268@gmail.com>
On 5/7/26 10:40 PM, Jim Cromie wrote:
> With CONFIG_DRM_USE_DYNAMIC_DEBUG=y, several build configs had
> problems with __dyndbg* sections getting lost in drm drivers. Fix
> this by following the model demonstrated in codetag.lds.h.
>
> Introduce include/asm-generic/dyndbg.lds.h, to bundle dynamic-debug's
> multiple sections together, into 2 macros:
>
> vmlinux.lds.h DATA_DATA: move the 2 BOUNDED_SECTION_BY(__dyndbg*)
> calls into dyndbg.lds.h DYNDBG_SECTIONS(). vmlinux.lds.h now includes
> the new file and calls the new macro.
>
> MOD_DYNDBG_SECTIONS declares the 2 BOUNDED_SECTION_BY calls, but wraps
> them with output section syntax to keep them as known and separate ELF
> sections in the module.ko. The KEEP fixes the lost section.
>
> dyndbg.lds.h includes (reuses) bounded-section.lds.h
>
> scripts/module.lds.S: now calls MOD_DYNDBG_SECTIONS right before the
> CODETAG macro (consistent with their placements in vmlinux.lds.h), and
> also includes dyndbg.lds.h
>
> This isolates vmlinux.lds.h from further __dyndbg section additions.
>
> CC: Arnd Bergmann <arnd@arndb.de>
> Signed-off-by: Jim Cromie <jim.cromie@gmail.com>
> ---
> [...]
> diff --git a/include/asm-generic/vmlinux.lds.h b/include/asm-generic/vmlinux.lds.h
> index d9d2eb708355..54897d742c6c 100644
> --- a/include/asm-generic/vmlinux.lds.h
> +++ b/include/asm-generic/vmlinux.lds.h
> @@ -332,6 +332,7 @@
> /*
> * .data section
> */
> +#include <asm-generic/dyndbg.lds.h>
> #define DATA_DATA \
> *(.xiptext) \
> *(DATA_MAIN)
Nit: I suggest placing the dyndbg.lds.h include at the top of the file,
as is usual, together with the codetag.lds.h include.
Otherwise, this looks ok to me. Feel free to add:
Reviewed-by: Petr Pavlu <petr.pavlu@suse.com>
--
Thanks,
Petr
^ permalink raw reply
* Re: [PATCH v4 1/4] kernel: param: initialize module_kset before do_initcalls()
From: Shashank Balaji @ 2026-05-12 13:32 UTC (permalink / raw)
To: Sumit Gupta
Cc: Jon Hunter, Thierry Reding, Gary Guo, Suzuki K Poulose,
James Clark, Alexander Shishkin, Maxime Coquelin,
Alexandre Torgue, Greg Kroah-Hartman, Rafael J. Wysocki,
Danilo Krummrich, Miguel Ojeda, Boqun Feng, Björn Roy Baron,
Benno Lossin, Andreas Hindborg, Alice Ryhl, Trevor Gross,
Richard Cochran, Jonathan Corbet, Shuah Khan, Luis Chamberlain,
Petr Pavlu, Daniel Gomez, Sami Tolvanen, Aaron Tomlin, Mike Leach,
Leo Yan, Rahul Bukte, linux-kernel, coresight, linux-arm-kernel,
driver-core, rust-for-linux, linux-doc, Daniel Palmer, Tim Bird,
linux-modules, linux-tegra
In-Reply-To: <0d9e5a78-948e-42da-9d37-78cc2a700cd6@nvidia.com>
On Tue, May 12, 2026 at 05:44:40PM +0530, Sumit Gupta wrote:
>
> On 12/05/26 14:25, Jon Hunter wrote:
> > Hi Shashank,
> >
> > On 12/05/2026 03:12, Shashank Balaji wrote:
> >
> > ...
> >
> > > > Hi Thierry and Jonathan,
> > > >
> > > > You can find the context for this email in this patch:
> > > > https://lore.kernel.org/all/20260427-acpi_mod_name-v4-1-22b42240c9bf@sony.com/
> > > >
> > > >
> > > > TL;DR: tegra194_cbb_driver and tegra234_cbb_driver are the only drivers
> > > > registering themselves as early as in a pure_initcall. This is a
> > > > problem
> > > > on two fronts:
> > > > 1. Philosophical: As Gary pointed out, pure_initcalls are
> > > > intended to purely
> > > > initialize variables that couldn't be statically initialized. But these
> > > > are doing driver registrations.
> > > > 2. module_kset not initialized at pure_initcall stage: This is
> > > > needed to
> > > > set the module sysfs symlink. Since module_kset is not alive yet during
> > > > pure_initcalls, registering these drivers panics the kernel.
> >
> > Where exactly is this panic seen? Ie. why are we not seeing this?
The panic happens as a result of this patch series. This series aims to
set .mod_name of struct device_driver for platform drivers. So that, for
built-in drivers, their module sysfs symlink can be created on the basis
of .modname. We essentially want to link a platform driver to its
module. This happens naturally if the driver is built as a loadable
module, but for this to happen for built-in modules, .mod_name needs to be set.
To go from .mod_name to the module sysfs symlink, the module_kset kset
needs to be initialized. This currently happens in a subsys_initcall.
These tegra cbb drivers register themselves in a pure_initcall, i.e.
before subsys_initcall, leading to a null dereference of module_kset.
To fix this, we want to move the module_kset creation to pure_initcall,
and tegra cbb driver registration to core_initcall, so after
pure_initcall.
> > > > We would like to do the tegra cbb driver registration in a
> > > > core_initcall
> > > > (or some later initcall works too), and move module_kset initialization
> > > > to a pure_initcall. Like this:
> > > >
> > > > diff --git a/drivers/soc/tegra/cbb/tegra194-cbb.c
> > > > b/drivers/soc/tegra/cbb/tegra194-cbb.c
> > > > index ab75d50cc85c..2f69e104c838 100644
> > > > --- a/drivers/soc/tegra/cbb/tegra194-cbb.c
> > > > +++ b/drivers/soc/tegra/cbb/tegra194-cbb.c
> > > > @@ -2342,7 +2342,7 @@ static int __init tegra194_cbb_init(void)
> > > > {
> > > > return platform_driver_register(&tegra194_cbb_driver);
> > > > }
> > > > -pure_initcall(tegra194_cbb_init);
> > > > +core_initcall(tegra194_cbb_init);
> > > >
> > > > static void __exit tegra194_cbb_exit(void)
> > > > {
> > > > diff --git a/drivers/soc/tegra/cbb/tegra234-cbb.c
> > > > b/drivers/soc/tegra/cbb/tegra234-cbb.c
> > > > index fb26f085f691..785072fa4e85 100644
> > > > --- a/drivers/soc/tegra/cbb/tegra234-cbb.c
> > > > +++ b/drivers/soc/tegra/cbb/tegra234-cbb.c
> > > > @@ -1774,7 +1774,7 @@ static int __init tegra234_cbb_init(void)
> > > > {
> > > > return platform_driver_register(&tegra234_cbb_driver);
> > > > }
> > > > -pure_initcall(tegra234_cbb_init);
> > > > +core_initcall(tegra234_cbb_init);
> > > >
> > > > static void __exit tegra234_cbb_exit(void)
> > > > {
> > > >
> > > > Would this work?
> >
> >
> > I am adding Sumit who has been doing a lot of the Tegra CBB driver work.
> >
> > Sumit, any concerns here? We could run this change through our internal
> > testing to confirm.
> >
> > Jon
> >
>
> CBB driver can be switched to core_initcall.
> pure_initcall was originally added so its IRQ handler is registered
> before other Tegra drivers to catch and print any bad MMIO error
> during their probe.
> Looked at the current state of Tegra drivers:
> - The other early Tegra drivers (PMC, fuse, flowctrl, ARI) all run at
> early_initcall, before either pure_ or core_initcall.
> - The only other Tegra core_initcall is tegra-hsp, and link order keeps
> CBB ahead of it (drivers/soc/ links before drivers/mailbox/).
>
> Acked-by: Sumit Gupta <sumitg@nvidia.com>
Thanks, Sumit and Jon!
^ permalink raw reply
* Re: [PATCH v4 1/4] kernel: param: initialize module_kset before do_initcalls()
From: Sumit Gupta @ 2026-05-12 12:14 UTC (permalink / raw)
To: Jon Hunter, Shashank Balaji, Thierry Reding
Cc: Gary Guo, Suzuki K Poulose, James Clark, Alexander Shishkin,
Maxime Coquelin, Alexandre Torgue, Greg Kroah-Hartman,
Rafael J. Wysocki, Danilo Krummrich, Miguel Ojeda, Boqun Feng,
Björn Roy Baron, Benno Lossin, Andreas Hindborg, Alice Ryhl,
Trevor Gross, Richard Cochran, Jonathan Corbet, Shuah Khan,
Luis Chamberlain, Petr Pavlu, Daniel Gomez, Sami Tolvanen,
Aaron Tomlin, Mike Leach, Leo Yan, Rahul Bukte, linux-kernel,
coresight, linux-arm-kernel, driver-core, rust-for-linux,
linux-doc, Daniel Palmer, Tim Bird, linux-modules, linux-tegra
In-Reply-To: <40c3aab2-b5cf-4297-9b14-3ccfea377c83@nvidia.com>
On 12/05/26 14:25, Jon Hunter wrote:
> Hi Shashank,
>
> On 12/05/2026 03:12, Shashank Balaji wrote:
>
> ...
>
>>> Hi Thierry and Jonathan,
>>>
>>> You can find the context for this email in this patch:
>>> https://lore.kernel.org/all/20260427-acpi_mod_name-v4-1-22b42240c9bf@sony.com/
>>>
>>>
>>> TL;DR: tegra194_cbb_driver and tegra234_cbb_driver are the only drivers
>>> registering themselves as early as in a pure_initcall. This is a
>>> problem
>>> on two fronts:
>>> 1. Philosophical: As Gary pointed out, pure_initcalls are intended
>>> to purely
>>> initialize variables that couldn't be statically initialized. But these
>>> are doing driver registrations.
>>> 2. module_kset not initialized at pure_initcall stage: This is
>>> needed to
>>> set the module sysfs symlink. Since module_kset is not alive yet during
>>> pure_initcalls, registering these drivers panics the kernel.
>
> Where exactly is this panic seen? Ie. why are we not seeing this?
>
>>> We would like to do the tegra cbb driver registration in a
>>> core_initcall
>>> (or some later initcall works too), and move module_kset initialization
>>> to a pure_initcall. Like this:
>>>
>>> diff --git a/drivers/soc/tegra/cbb/tegra194-cbb.c
>>> b/drivers/soc/tegra/cbb/tegra194-cbb.c
>>> index ab75d50cc85c..2f69e104c838 100644
>>> --- a/drivers/soc/tegra/cbb/tegra194-cbb.c
>>> +++ b/drivers/soc/tegra/cbb/tegra194-cbb.c
>>> @@ -2342,7 +2342,7 @@ static int __init tegra194_cbb_init(void)
>>> {
>>> return platform_driver_register(&tegra194_cbb_driver);
>>> }
>>> -pure_initcall(tegra194_cbb_init);
>>> +core_initcall(tegra194_cbb_init);
>>>
>>> static void __exit tegra194_cbb_exit(void)
>>> {
>>> diff --git a/drivers/soc/tegra/cbb/tegra234-cbb.c
>>> b/drivers/soc/tegra/cbb/tegra234-cbb.c
>>> index fb26f085f691..785072fa4e85 100644
>>> --- a/drivers/soc/tegra/cbb/tegra234-cbb.c
>>> +++ b/drivers/soc/tegra/cbb/tegra234-cbb.c
>>> @@ -1774,7 +1774,7 @@ static int __init tegra234_cbb_init(void)
>>> {
>>> return platform_driver_register(&tegra234_cbb_driver);
>>> }
>>> -pure_initcall(tegra234_cbb_init);
>>> +core_initcall(tegra234_cbb_init);
>>>
>>> static void __exit tegra234_cbb_exit(void)
>>> {
>>>
>>> Would this work?
>
>
> I am adding Sumit who has been doing a lot of the Tegra CBB driver work.
>
> Sumit, any concerns here? We could run this change through our
> internal testing to confirm.
>
> Jon
>
CBB driver can be switched to core_initcall.
pure_initcall was originally added so its IRQ handler is registered
before other Tegra drivers to catch and print any bad MMIO error
during their probe.
Looked at the current state of Tegra drivers:
- The other early Tegra drivers (PMC, fuse, flowctrl, ARI) all run at
early_initcall, before either pure_ or core_initcall.
- The only other Tegra core_initcall is tegra-hsp, and link order keeps
CBB ahead of it (drivers/soc/ links before drivers/mailbox/).
Acked-by: Sumit Gupta <sumitg@nvidia.com>
Thank you,
Sumit Gupta
^ permalink raw reply
* Re: [PATCH 00/17] dynamic-debug cleanups refactors maintenance
From: jim.cromie @ 2026-05-12 10:12 UTC (permalink / raw)
To: Andrew Morton, Linux Documentation List
Cc: Jason Baron, Luis Chamberlain, Petr Pavlu, Daniel Gomez,
Sami Tolvanen, Aaron Tomlin, Shuah Khan, Louis Chauvet,
linux-kernel, linux-modules, linux-kselftest,
Łukasz Bartosik
In-Reply-To: <20260508190121.3461706b01f6079bbacdd167@linux-foundation.org>
On Fri, May 8, 2026 at 8:01 PM Andrew Morton <akpm@linux-foundation.org> wrote:
>
> On Mon, 04 May 2026 14:45:06 -0600 Jim Cromie <jim.cromie@gmail.com> wrote:
>
> > This series is nearly all maintenance: it refactors/splits functions,
> > The user visible change to /proc/dynamic_debug/control is s/class
> > unknown/class:_UNKNOWN_/, which is a more visible/greppable indication
> > of incomplete class definitions.
>
> Wait. We can't make userspace-visible changes?
>
- the code has been marked BROKEN for its 1st, intended user: DRM,
so there are no users affected by this change
- UNKNOWN is an error condition, an incomplete/incorrect classmap definition,
and is expected to be caught in implementation or review.
- phase 2 of the patch set has improved compile-time and modprobe-time
validation,
they would catch this coding error.
- I will drop this patch if these reasons are insufficient.
> > Coder visible change is to drop the enum ddebug_class_map_type's
> > unused vals - namely: DD_CLASS_TYPE_DISJOINT_NAMES
> > & DD_CLASS_TYPE_LEVEL_NAMES
> >
> > These allowed more symbolic named inputs:
> > echo +DRM_UT_CORE > /sys/module/drm/parameters/debug
> >
> > But theyre unused 3 years later, and probably not worth keeping.
> > With a removal commit in the log, its easy enough to restore them later.
> >
> > ...
> >
> > MAINTAINERS | 1 +
> > include/linux/dynamic_debug.h | 106 ++---
> > kernel/module/main.c | 12 +-
> > lib/dynamic_debug.c | 504 ++++++++++-----------
> > lib/test_dynamic_debug.c | 28 +-
> > tools/testing/selftests/Makefile | 1 +
> > tools/testing/selftests/dynamic_debug/Makefile | 9 +
> > tools/testing/selftests/dynamic_debug/config | 7 +
> > .../selftests/dynamic_debug/dyndbg_selftest.sh | 257 +++++++++++
> > 9 files changed, 582 insertions(+), 343 deletions(-)
>
> No Documentation/ updates?
I have 2 doc-only updates I peeled off and sent to @Linux Documentation List
Otherwise, there are no behavioral changes here to write about.
Phase 2 has API changes needed to actually fix classmaps for DRM, and
docs to go with it.
I split out phase-1 to lower the barrier to review and apply.
By your response, it seems to have helped.
wrt sashiko review, Ive made several adjustments, Im reviewing,
working the others.
Thanks
Jim
^ permalink raw reply
* Re: [PATCH v4 1/4] kernel: param: initialize module_kset before do_initcalls()
From: Jon Hunter @ 2026-05-12 8:55 UTC (permalink / raw)
To: Shashank Balaji, Thierry Reding, Sumit Gupta
Cc: Gary Guo, Suzuki K Poulose, James Clark, Alexander Shishkin,
Maxime Coquelin, Alexandre Torgue, Greg Kroah-Hartman,
Rafael J. Wysocki, Danilo Krummrich, Miguel Ojeda, Boqun Feng,
Björn Roy Baron, Benno Lossin, Andreas Hindborg, Alice Ryhl,
Trevor Gross, Richard Cochran, Jonathan Corbet, Shuah Khan,
Luis Chamberlain, Petr Pavlu, Daniel Gomez, Sami Tolvanen,
Aaron Tomlin, Mike Leach, Leo Yan, Rahul Bukte, linux-kernel,
coresight, linux-arm-kernel, driver-core, rust-for-linux,
linux-doc, Daniel Palmer, Tim Bird, linux-modules, linux-tegra
In-Reply-To: <agKMcA7a_UqMua5V@JPC00244420>
Hi Shashank,
On 12/05/2026 03:12, Shashank Balaji wrote:
...
>> Hi Thierry and Jonathan,
>>
>> You can find the context for this email in this patch:
>> https://lore.kernel.org/all/20260427-acpi_mod_name-v4-1-22b42240c9bf@sony.com/
>>
>> TL;DR: tegra194_cbb_driver and tegra234_cbb_driver are the only drivers
>> registering themselves as early as in a pure_initcall. This is a problem
>> on two fronts:
>> 1. Philosophical: As Gary pointed out, pure_initcalls are intended to purely
>> initialize variables that couldn't be statically initialized. But these
>> are doing driver registrations.
>> 2. module_kset not initialized at pure_initcall stage: This is needed to
>> set the module sysfs symlink. Since module_kset is not alive yet during
>> pure_initcalls, registering these drivers panics the kernel.
Where exactly is this panic seen? Ie. why are we not seeing this?
>> We would like to do the tegra cbb driver registration in a core_initcall
>> (or some later initcall works too), and move module_kset initialization
>> to a pure_initcall. Like this:
>>
>> diff --git a/drivers/soc/tegra/cbb/tegra194-cbb.c b/drivers/soc/tegra/cbb/tegra194-cbb.c
>> index ab75d50cc85c..2f69e104c838 100644
>> --- a/drivers/soc/tegra/cbb/tegra194-cbb.c
>> +++ b/drivers/soc/tegra/cbb/tegra194-cbb.c
>> @@ -2342,7 +2342,7 @@ static int __init tegra194_cbb_init(void)
>> {
>> return platform_driver_register(&tegra194_cbb_driver);
>> }
>> -pure_initcall(tegra194_cbb_init);
>> +core_initcall(tegra194_cbb_init);
>>
>> static void __exit tegra194_cbb_exit(void)
>> {
>> diff --git a/drivers/soc/tegra/cbb/tegra234-cbb.c b/drivers/soc/tegra/cbb/tegra234-cbb.c
>> index fb26f085f691..785072fa4e85 100644
>> --- a/drivers/soc/tegra/cbb/tegra234-cbb.c
>> +++ b/drivers/soc/tegra/cbb/tegra234-cbb.c
>> @@ -1774,7 +1774,7 @@ static int __init tegra234_cbb_init(void)
>> {
>> return platform_driver_register(&tegra234_cbb_driver);
>> }
>> -pure_initcall(tegra234_cbb_init);
>> +core_initcall(tegra234_cbb_init);
>>
>> static void __exit tegra234_cbb_exit(void)
>> {
>>
>> Would this work?
I am adding Sumit who has been doing a lot of the Tegra CBB driver work.
Sumit, any concerns here? We could run this change through our internal
testing to confirm.
Jon
--
nvpublic
^ permalink raw reply
* Re: [PATCH v4 1/4] kernel: param: initialize module_kset before do_initcalls()
From: Shashank Balaji @ 2026-05-12 2:12 UTC (permalink / raw)
To: Thierry Reding, Jonathan Hunter
Cc: Gary Guo, Suzuki K Poulose, James Clark, Alexander Shishkin,
Maxime Coquelin, Alexandre Torgue, Greg Kroah-Hartman,
Rafael J. Wysocki, Danilo Krummrich, Miguel Ojeda, Boqun Feng,
Björn Roy Baron, Benno Lossin, Andreas Hindborg, Alice Ryhl,
Trevor Gross, Richard Cochran, Jonathan Corbet, Shuah Khan,
Luis Chamberlain, Petr Pavlu, Daniel Gomez, Sami Tolvanen,
Aaron Tomlin, Mike Leach, Leo Yan, Rahul Bukte, linux-kernel,
coresight, linux-arm-kernel, driver-core, rust-for-linux,
linux-doc, Daniel Palmer, Tim Bird, linux-modules, linux-tegra
In-Reply-To: <afCxHUrjr3Z22U6V@JPC00244420>
Hi Thierry, Jonathan,
Just following up on the below, would moving tegra194_cbb_driver and
tegra234_cbb_driver from pure_initcall to core_initcall work for you?
Thanks,
Shashank
On Tue, Apr 28, 2026 at 10:07:41PM +0900, Shashank Balaji wrote:
> Adding Tegra maintainers.
>
> On Tue, Apr 28, 2026 at 12:10:50PM +0100, Gary Guo wrote:
> > On Tue Apr 28, 2026 at 1:37 AM BST, Shashank Balaji wrote:
> > > Hi Gary,
> > >
> > > On Mon, Apr 27, 2026 at 02:29:55PM +0100, Gary Guo wrote:
> > >> On Mon Apr 27, 2026 at 3:41 AM BST, Shashank Balaji wrote:
> > >> > module_kset is initialized in param_sysfs_init(), a subsys_initcall. A number
> > >> > of platform drivers register themselves prior to subsys_initcalls
> > >> > (tegra194_cbb_driver registers in a pure_initcall, for example). With an
> > >> > upcoming patch ("driver core: platform: set mod_name in driver registration")
> > >> > that sets their mod_name in struct device_driver, lookup_or_create_module_kobject()
> > >> > will be called for those drivers, which calls kset_find_obj(module_kset, mod_name).
> > >> > This causes a null deref because module_kset isn't alive yet.
> > >> >
> > >> > Fix this by initializing module_kset in do_basic_setup() before do_initcalls().
> > >> > Modernize the pr_warn while we're at it.
> > >> >
> > >> > Suggested-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
> > >> > Suggested-by: Gary Guo <gary@garyguo.net>
> > >>
> > >> I didn't suggest this change :)
> > >>
> > >> I suggested `pure_initcall`, which is just a one line change.
> > >
> > > Oops, sorry about the misattribution.
> > >
> > >> diff --git a/kernel/params.c b/kernel/params.c
> > >> index 74d620bc2521..ac088d4b09a9 100644
> > >> --- a/kernel/params.c
> > >> +++ b/kernel/params.c
> > >> @@ -957,7 +957,7 @@ static int __init param_sysfs_init(void)
> > >>
> > >> return 0;
> > >> }
> > >> -subsys_initcall(param_sysfs_init);
> > >> +pure_initcall(param_sysfs_init);
> > >>
> > >> /*
> > >> * param_sysfs_builtin_init - add sysfs version and parameter
> > >>
> > >> pure_initcall is level 0 so it happens before all other init calls. Does it not
> > >> work?
> > >
> > > tegra194_cbb_driver registers itself in a pure_initcall too. We wouldn't
> > > want the ordering of its registration and module_kset init to be link order
> > > dependent.
> >
> > It's the only device driver that does this. And I don't think it's supposed to.
> >
> > >From documentation:
> >
> > > A "pure" initcall has no dependencies on anything else, and purely
> > > initializes variables that couldn't be statically initialized.
> >
> > I understand that given large amount of drivers registering themselves during
> > core/arch_initcall that there might be regressions if all of them are moved, but
> > surely we can demote these two specific tegra driver to core/postcore_initcall?
> > This will still be called earlier than init_machine call which happens during
> > arch_initcall.
> >
> > Looks like the tegra CBB driver is just doing error logging anyway.
>
> That's a good point, Gary. Thanks!
>
> Hi Thierry and Jonathan,
>
> You can find the context for this email in this patch:
> https://lore.kernel.org/all/20260427-acpi_mod_name-v4-1-22b42240c9bf@sony.com/
>
> TL;DR: tegra194_cbb_driver and tegra234_cbb_driver are the only drivers
> registering themselves as early as in a pure_initcall. This is a problem
> on two fronts:
> 1. Philosophical: As Gary pointed out, pure_initcalls are intended to purely
> initialize variables that couldn't be statically initialized. But these
> are doing driver registrations.
> 2. module_kset not initialized at pure_initcall stage: This is needed to
> set the module sysfs symlink. Since module_kset is not alive yet during
> pure_initcalls, registering these drivers panics the kernel.
>
> We would like to do the tegra cbb driver registration in a core_initcall
> (or some later initcall works too), and move module_kset initialization
> to a pure_initcall. Like this:
>
> diff --git a/drivers/soc/tegra/cbb/tegra194-cbb.c b/drivers/soc/tegra/cbb/tegra194-cbb.c
> index ab75d50cc85c..2f69e104c838 100644
> --- a/drivers/soc/tegra/cbb/tegra194-cbb.c
> +++ b/drivers/soc/tegra/cbb/tegra194-cbb.c
> @@ -2342,7 +2342,7 @@ static int __init tegra194_cbb_init(void)
> {
> return platform_driver_register(&tegra194_cbb_driver);
> }
> -pure_initcall(tegra194_cbb_init);
> +core_initcall(tegra194_cbb_init);
>
> static void __exit tegra194_cbb_exit(void)
> {
> diff --git a/drivers/soc/tegra/cbb/tegra234-cbb.c b/drivers/soc/tegra/cbb/tegra234-cbb.c
> index fb26f085f691..785072fa4e85 100644
> --- a/drivers/soc/tegra/cbb/tegra234-cbb.c
> +++ b/drivers/soc/tegra/cbb/tegra234-cbb.c
> @@ -1774,7 +1774,7 @@ static int __init tegra234_cbb_init(void)
> {
> return platform_driver_register(&tegra234_cbb_driver);
> }
> -pure_initcall(tegra234_cbb_init);
> +core_initcall(tegra234_cbb_init);
>
> static void __exit tegra234_cbb_exit(void)
> {
>
> Would this work?
>
> Thanks,
> Shashank
>
^ permalink raw reply
* Re: [PATCH] init/main: Expose built-in initcalls and blacklist status via debugfs
From: Aaron Tomlin @ 2026-05-11 20:34 UTC (permalink / raw)
To: arnd, mcgrof, petr.pavlu, da.gomez, samitolvanen, sashal, leitao,
akpm
Cc: neelx, sean, chjohnst, steve, mproche, nick.lane, linux-arch,
linux-modules, linux-kernel
In-Reply-To: <20260510061301.41341-1-atomlin@atomlin.com>
[-- Attachment #1: Type: text/plain, Size: 2091 bytes --]
On Sun, May 10, 2026 at 02:13:01AM -0400, Aaron Tomlin wrote:
> At present, identifying the correct function name to supply to the
> "initcall_blacklist=" kernel command-line parameter requires manual
> inspection of the source code or kernel symbol tables. Furthermore,
> administrators lack a reliable runtime mechanism to verify whether a
> specified built-in module has been successfully blacklisted.
>
> To resolve this, introduce a new debugfs interface at
> /sys/kernel/debug/modules/builtin_initcalls. This file enumerates all
> built-in modules alongside their corresponding initialisation callbacks
> (e.g., those specified by module_init()) in a simple format:
> "module_name init_callback". If a built-in module has been actively
> blacklisted, the entry is explicitly appended with a " [blacklisted]"
> suffix.
>
> To achieve this without incurring runtime overhead, the
> ___define_initcall macro is expanded to statically allocate a
> builtin_initcall_record structure (containing the module and function
> name strings) within a dedicated .rodata_builtin_initcall_records
> section.
>
> This implementation incorporates several critical architectural safety
> measures:
> - The custom section name deliberately begins with ".rodata" to ensure
> KASLR (Kernel Address Space Layout Randomisation) relocation tools
> process and update the string pointers correctly during boot.
> - The structural alignment is explicitly forced to 8 bytes to prevent
> the compiler from inserting silent padding that would misalign the
> linker script boundaries.
> - The blacklisted_initcalls list head is stripped of its
> __initdata_or_module annotation, ensuring the list remains safely
> accessible in memory when queried from user space after early boot.
Hi Sasha, Breno,
I thought this might be of interest to you for review, given your recent
work and interest on the "killswitch" proposal [1].
[1]: https://lore.kernel.org/lkml/20260508195749.1885522-1-sashal@kernel.org/
Kind regards,
--
Aaron Tomlin
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]
^ permalink raw reply
* [PATCH] init/main: Expose built-in initcalls and blacklist status via debugfs
From: Aaron Tomlin @ 2026-05-10 6:13 UTC (permalink / raw)
To: arnd, mcgrof, petr.pavlu, da.gomez, samitolvanen
Cc: atomlin, neelx, sean, chjohnst, steve, mproche, nick.lane,
linux-arch, linux-modules, linux-kernel
At present, identifying the correct function name to supply to the
"initcall_blacklist=" kernel command-line parameter requires manual
inspection of the source code or kernel symbol tables. Furthermore,
administrators lack a reliable runtime mechanism to verify whether a
specified built-in module has been successfully blacklisted.
To resolve this, introduce a new debugfs interface at
/sys/kernel/debug/modules/builtin_initcalls. This file enumerates all
built-in modules alongside their corresponding initialisation callbacks
(e.g., those specified by module_init()) in a simple format:
"module_name init_callback". If a built-in module has been actively
blacklisted, the entry is explicitly appended with a " [blacklisted]"
suffix.
To achieve this without incurring runtime overhead, the
___define_initcall macro is expanded to statically allocate a
builtin_initcall_record structure (containing the module and function
name strings) within a dedicated .rodata_builtin_initcall_records
section.
This implementation incorporates several critical architectural safety
measures:
- The custom section name deliberately begins with ".rodata" to ensure
KASLR (Kernel Address Space Layout Randomisation) relocation tools
process and update the string pointers correctly during boot.
- The structural alignment is explicitly forced to 8 bytes to prevent
the compiler from inserting silent padding that would misalign the
linker script boundaries.
- The blacklisted_initcalls list head is stripped of its
__initdata_or_module annotation, ensuring the list remains safely
accessible in memory when queried from user space after early boot.
The creation of the debugfs file and the associated memory allocations
are strictly guarded by CONFIG_DEBUG_FS and CONFIG_KALLSYMS, as the
underlying initcall blacklisting mechanism is entirely dependent on
kallsyms being present.
Signed-off-by: Aaron Tomlin <atomlin@atomlin.com>
---
include/asm-generic/vmlinux.lds.h | 4 +++
include/linux/init.h | 21 ++++++++++++
init/main.c | 55 ++++++++++++++++++++++++++++++-
3 files changed, 79 insertions(+), 1 deletion(-)
diff --git a/include/asm-generic/vmlinux.lds.h b/include/asm-generic/vmlinux.lds.h
index 60c8c22fd3e4..d0fb971a8d5c 100644
--- a/include/asm-generic/vmlinux.lds.h
+++ b/include/asm-generic/vmlinux.lds.h
@@ -483,6 +483,10 @@
SCHED_DATA \
RO_AFTER_INIT_DATA /* Read only after init */ \
. = ALIGN(8); \
+ __builtin_initcall_records_start = .; \
+ KEEP(*(.rodata_builtin_initcall_records)) \
+ __builtin_initcall_records_end = .; \
+ . = ALIGN(8); \
BOUNDED_SECTION_BY(__tracepoints_ptrs, ___tracepoints_ptrs) \
*(__tracepoints_strings)/* Tracepoints: strings */ \
} \
diff --git a/include/linux/init.h b/include/linux/init.h
index 40331923b9f4..d682fe0d6064 100644
--- a/include/linux/init.h
+++ b/include/linux/init.h
@@ -182,6 +182,16 @@ extern struct module __this_module;
#ifndef __ASSEMBLY__
+#if defined(CONFIG_DEBUG_FS) && defined(CONFIG_KALLSYMS)
+#ifndef KBUILD_MODNAME
+#define KBUILD_MODNAME "unknown"
+#endif
+struct builtin_initcall_record {
+ const char *modname;
+ const char *fnname;
+};
+#endif
+
/*
* initcalls are now grouped by functionality into separate
* subsections. Ordering inside the subsections is determined
@@ -271,8 +281,19 @@ extern struct module __this_module;
__initcall_name(initcall, __iid, id), \
__initcall_section(__sec, __iid))
+#if defined(CONFIG_DEBUG_FS) && defined(CONFIG_KALLSYMS)
+#define ___define_initcall(fn, id, __sec) \
+ __unique_initcall(fn, id, __sec, __initcall_id(fn)); \
+ static const struct builtin_initcall_record \
+ __initcall_name(builtin_initcall_rec, __initcall_id(fn), id) \
+ __used __aligned(8) __section(".rodata_builtin_initcall_records") = { \
+ .modname = KBUILD_MODNAME, \
+ .fnname = #fn, \
+ };
+#else
#define ___define_initcall(fn, id, __sec) \
__unique_initcall(fn, id, __sec, __initcall_id(fn))
+#endif
#define __define_initcall(fn, id) ___define_initcall(fn, id, .initcall##id)
diff --git a/init/main.c b/init/main.c
index 96f93bb06c49..4cdf97306bf6 100644
--- a/init/main.c
+++ b/init/main.c
@@ -104,6 +104,8 @@
#include <linux/randomize_kstack.h>
#include <linux/pidfs.h>
#include <linux/ptdump.h>
+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
#include <linux/time_namespace.h>
#include <linux/unaligned.h>
#include <linux/vdso_datastore.h>
@@ -1251,7 +1253,7 @@ struct blacklist_entry {
char *buf;
};
-static __initdata_or_module LIST_HEAD(blacklisted_initcalls);
+static LIST_HEAD(blacklisted_initcalls);
static int __init initcall_blacklist(char *str)
{
@@ -1316,6 +1318,57 @@ static bool __init_or_module initcall_blacklisted(initcall_t fn)
#endif
__setup("initcall_blacklist=", initcall_blacklist);
+#if defined(CONFIG_DEBUG_FS) && defined(CONFIG_KALLSYMS)
+extern struct builtin_initcall_record __builtin_initcall_records_start[];
+extern struct builtin_initcall_record __builtin_initcall_records_end[];
+
+static int builtin_initcalls_show(struct seq_file *m, void *v)
+{
+ struct builtin_initcall_record *rec;
+ bool blacklisted;
+ struct blacklist_entry *entry;
+
+ for (rec = __builtin_initcall_records_start; rec < __builtin_initcall_records_end; rec++) {
+ blacklisted = false;
+ list_for_each_entry(entry, &blacklisted_initcalls, next) {
+ if (!strcmp(rec->fnname, entry->buf)) {
+ blacklisted = true;
+ break;
+ }
+ }
+ seq_printf(m, "%s %s%s\n",
+ rec->modname,
+ rec->fnname,
+ blacklisted ? " [blacklisted]" : "");
+ }
+
+ return 0;
+}
+DEFINE_SHOW_ATTRIBUTE(builtin_initcalls);
+
+static int __init builtin_initcalls_debugfs_init(void)
+{
+ struct dentry *modules_dir;
+
+ /* Attempt to safely grab a reference to the existing directory */
+ modules_dir = debugfs_lookup("modules", NULL);
+
+ /* * Use modules_dir if found, otherwise dynamically create and use
+ * the fallback directory without.
+ */
+ debugfs_create_file("builtin_initcalls", 0444,
+ modules_dir ?: debugfs_create_dir("modules", NULL),
+ NULL, &builtin_initcalls_fops);
+
+ /* dput() safely ignores NULL if the lookup failed */
+ dput(modules_dir);
+
+ return 0;
+}
+late_initcall(builtin_initcalls_debugfs_init);
+#endif /* CONFIG_DEBUG_FS && CONFIG_KALLSYMS */
+
+
static __init_or_module void
trace_initcall_start_cb(void *data, initcall_t fn)
{
--
2.51.0
^ permalink raw reply related
* Re: [PATCH 00/17] dynamic-debug cleanups refactors maintenance
From: Andrew Morton @ 2026-05-09 2:01 UTC (permalink / raw)
To: Jim Cromie
Cc: Jason Baron, Luis Chamberlain, Petr Pavlu, Daniel Gomez,
Sami Tolvanen, Aaron Tomlin, Shuah Khan, Louis Chauvet,
linux-kernel, linux-modules, linux-kselftest,
Łukasz Bartosik
In-Reply-To: <20260504-dd-cleanups-2-v1-0-6fdd24040642@gmail.com>
On Mon, 04 May 2026 14:45:06 -0600 Jim Cromie <jim.cromie@gmail.com> wrote:
> This series is nearly all maintenance: it refactors/splits functions,
> tightens an internal callchain, drops linked-listing where an
> array,length is already available, reduces verbose=3 logging for
> usability, and reorganizes several structs for better organization.
>
> Getting these into linux-next for integration testing would clear the
> deck for fixing Dynamic-debug classmaps, which is needed to restore
> the un-BROKEN status of DRM_USE_DYNAMIC_DEBUG.
>
> Theyre on master at v7.1-rc2
>
> The "modules" intersection is on the re-composing of struct
> ddebug_info, which is a member in struct module, and maps a module's
> __dyndbg* elf sections.
>
> The user visible change to /proc/dynamic_debug/control is s/class
> unknown/class:_UNKNOWN_/, which is a more visible/greppable indication
> of incomplete class definitions.
Wait. We can't make userspace-visible changes?
> Coder visible change is to drop the enum ddebug_class_map_type's
> unused vals - namely: DD_CLASS_TYPE_DISJOINT_NAMES
> & DD_CLASS_TYPE_LEVEL_NAMES
>
> These allowed more symbolic named inputs:
> echo +DRM_UT_CORE > /sys/module/drm/parameters/debug
>
> But theyre unused 3 years later, and probably not worth keeping.
> With a removal commit in the log, its easy enough to restore them later.
>
> ...
>
> MAINTAINERS | 1 +
> include/linux/dynamic_debug.h | 106 ++---
> kernel/module/main.c | 12 +-
> lib/dynamic_debug.c | 504 ++++++++++-----------
> lib/test_dynamic_debug.c | 28 +-
> tools/testing/selftests/Makefile | 1 +
> tools/testing/selftests/dynamic_debug/Makefile | 9 +
> tools/testing/selftests/dynamic_debug/config | 7 +
> .../selftests/dynamic_debug/dyndbg_selftest.sh | 257 +++++++++++
> 9 files changed, 582 insertions(+), 343 deletions(-)
No Documentation/ updates?
^ permalink raw reply
* Re: [PATCH 00/17] dynamic-debug cleanups refactors maintenance
From: Andrew Morton @ 2026-05-09 1:40 UTC (permalink / raw)
To: Jim Cromie
Cc: Jason Baron, Luis Chamberlain, Petr Pavlu, Daniel Gomez,
Sami Tolvanen, Aaron Tomlin, Shuah Khan, Louis Chauvet,
linux-kernel, linux-modules, linux-kselftest,
Łukasz Bartosik
In-Reply-To: <20260504-dd-cleanups-2-v1-0-6fdd24040642@gmail.com>
On Mon, 04 May 2026 14:45:06 -0600 Jim Cromie <jim.cromie@gmail.com> wrote:
> This series is nearly all maintenance: it refactors/splits functions,
> tightens an internal callchain, drops linked-listing where an
> array,length is already available, reduces verbose=3 logging for
> usability, and reorganizes several structs for better organization.
>
> Getting these into linux-next for integration testing would clear the
> deck for fixing Dynamic-debug classmaps, which is needed to restore
> the un-BROKEN status of DRM_USE_DYNAMIC_DEBUG.
>
> Theyre on master at v7.1-rc2
>
> The "modules" intersection is on the re-composing of struct
> ddebug_info, which is a member in struct module, and maps a module's
> __dyndbg* elf sections.
>
> The user visible change to /proc/dynamic_debug/control is s/class
> unknown/class:_UNKNOWN_/, which is a more visible/greppable indication
> of incomplete class definitions.
>
> Coder visible change is to drop the enum ddebug_class_map_type's
> unused vals - namely: DD_CLASS_TYPE_DISJOINT_NAMES
> & DD_CLASS_TYPE_LEVEL_NAMES
>
> These allowed more symbolic named inputs:
> echo +DRM_UT_CORE > /sys/module/drm/parameters/debug
>
> But theyre unused 3 years later, and probably not worth keeping.
> With a removal commit in the log, its easy enough to restore them later.
Thanks, Jim.
I'll queue this for some runtime testing in linux-next. But AI review
really went to town over it:
https://sashiko.dev/#/patchset/20260504-dd-cleanups-2-v1-0-6fdd24040642@gmail.com
^ permalink raw reply
* [PATCH v2 5/5] dyndbg.lds.S: fix lost dyndbg sections in modules
From: Jim Cromie @ 2026-05-07 20:40 UTC (permalink / raw)
To: Arnd Bergmann, Jason Baron, Luis Chamberlain, Petr Pavlu,
Daniel Gomez, Sami Tolvanen, Aaron Tomlin
Cc: linux-arch, linux-kernel, linux-modules, Jim Cromie
In-Reply-To: <20260507-asm-generic-1-v2-0-47c52759d268@gmail.com>
With CONFIG_DRM_USE_DYNAMIC_DEBUG=y, several build configs had
problems with __dyndbg* sections getting lost in drm drivers. Fix
this by following the model demonstrated in codetag.lds.h.
Introduce include/asm-generic/dyndbg.lds.h, to bundle dynamic-debug's
multiple sections together, into 2 macros:
vmlinux.lds.h DATA_DATA: move the 2 BOUNDED_SECTION_BY(__dyndbg*)
calls into dyndbg.lds.h DYNDBG_SECTIONS(). vmlinux.lds.h now includes
the new file and calls the new macro.
MOD_DYNDBG_SECTIONS declares the 2 BOUNDED_SECTION_BY calls, but wraps
them with output section syntax to keep them as known and separate ELF
sections in the module.ko. The KEEP fixes the lost section.
dyndbg.lds.h includes (reuses) bounded-section.lds.h
scripts/module.lds.S: now calls MOD_DYNDBG_SECTIONS right before the
CODETAG macro (consistent with their placements in vmlinux.lds.h), and
also includes dyndbg.lds.h
This isolates vmlinux.lds.h from further __dyndbg section additions.
CC: Arnd Bergmann <arnd@arndb.de>
Signed-off-by: Jim Cromie <jim.cromie@gmail.com>
---
v2 - Address linker script review feedback for relocatable modules.
v1 of MOD_DYNDBG_SECTIONS() used the BOUNDED_SECTION_BY() macro, which
proved problematic for kernel modules for two reasons:
1. Unwanted Empty Sections:
BOUNDED_SECTION_BY() automatically generates `__start` and `__stop`
symbols. When applied to `MOD_DYNDBG_SECTIONS()`, the linker assumes
the sections are populated due to the symbol definitions, forcing an
empty `__dyndbg` and `__dyndbg_classes` output section in every
compiled module, even those without dynamic debug configuration.
Since the module loader uses `section_objs()` to locate data via
ELF headers instead of relying on `__start`/`__stop` symbols, these
assignments are completely unnecessary.
2. Non-zero Output Addresses:
During relocatable linking (e.g., `ld.bfd -r`), omitting an explicit
base address causes the section to inherit the current location
counter. This results in non-zero sh_addr values in `.ko` files,
which is confusing, degrades compressibility, and can cause issues
with external tools parsing the ELF.
Fix both issues by dropping `BOUNDED_SECTION_BY()` in favor of a simple
`KEEP(*(...))` constraint and explicitly defining the sections with a `0`
base address: `__dyndbg 0 : ALIGN(8) { ... }`.
---
MAINTAINERS | 1 +
include/asm-generic/dyndbg.lds.h | 19 +++++++++++++++++++
include/asm-generic/vmlinux.lds.h | 6 ++----
scripts/module.lds.S | 2 ++
4 files changed, 24 insertions(+), 4 deletions(-)
diff --git a/MAINTAINERS b/MAINTAINERS
index 882214b0e7db..54bb257b60ee 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -9070,6 +9070,7 @@ DYNAMIC DEBUG
M: Jason Baron <jbaron@akamai.com>
M: Jim Cromie <jim.cromie@gmail.com>
S: Maintained
+F: include/asm-generic/dyndbg.lds.h
F: include/linux/dynamic_debug.h
F: lib/dynamic_debug.c
F: lib/test_dynamic_debug.c
diff --git a/include/asm-generic/dyndbg.lds.h b/include/asm-generic/dyndbg.lds.h
new file mode 100644
index 000000000000..844f1cb82020
--- /dev/null
+++ b/include/asm-generic/dyndbg.lds.h
@@ -0,0 +1,19 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+#ifndef __ASM_GENERIC_DYNDBG_LDS_H
+#define __ASM_GENERIC_DYNDBG_LDS_H
+
+#include <asm-generic/bounded_sections.lds.h>
+#define DYNDBG_SECTIONS() \
+ . = ALIGN(8); \
+ BOUNDED_SECTION_BY(__dyndbg, ___dyndbg) \
+ BOUNDED_SECTION_BY(__dyndbg_classes, ___dyndbg_classes)
+
+#define MOD_DYNDBG_SECTIONS() \
+ __dyndbg 0 : ALIGN(8) { \
+ KEEP(*(__dyndbg)) \
+ } \
+ __dyndbg_classes 0 : ALIGN(8) { \
+ KEEP(*(__dyndbg_classes)) \
+ }
+
+#endif /* __ASM_GENERIC_DYNDBG_LDS_H */
diff --git a/include/asm-generic/vmlinux.lds.h b/include/asm-generic/vmlinux.lds.h
index d9d2eb708355..54897d742c6c 100644
--- a/include/asm-generic/vmlinux.lds.h
+++ b/include/asm-generic/vmlinux.lds.h
@@ -332,6 +332,7 @@
/*
* .data section
*/
+#include <asm-generic/dyndbg.lds.h>
#define DATA_DATA \
*(.xiptext) \
*(DATA_MAIN) \
@@ -345,10 +346,7 @@
*(.data..do_once) \
STRUCT_ALIGN(); \
*(__tracepoints) \
- /* implement dynamic printk debug */ \
- . = ALIGN(8); \
- BOUNDED_SECTION_BY(__dyndbg_classes, ___dyndbg_classes) \
- BOUNDED_SECTION_BY(__dyndbg, ___dyndbg) \
+ DYNDBG_SECTIONS() \
CODETAG_SECTIONS() \
LIKELY_PROFILE() \
BRANCH_PROFILE() \
diff --git a/scripts/module.lds.S b/scripts/module.lds.S
index b62683061d79..2e62dc5bd5d4 100644
--- a/scripts/module.lds.S
+++ b/scripts/module.lds.S
@@ -10,6 +10,7 @@
#endif
#include <asm-generic/codetag.lds.h>
+#include <asm-generic/dyndbg.lds.h>
SECTIONS {
/DISCARD/ : {
@@ -61,6 +62,7 @@ SECTIONS {
*(.rodata..L*)
}
+ MOD_DYNDBG_SECTIONS()
MOD_SEPARATE_CODETAG_SECTIONS()
}
--
2.54.0
^ permalink raw reply related
* [PATCH v2 4/5] vmlinux.lds.h: remove redundant ALIGN(8) directives
From: Jim Cromie @ 2026-05-07 20:40 UTC (permalink / raw)
To: Arnd Bergmann, Jason Baron, Luis Chamberlain, Petr Pavlu,
Daniel Gomez, Sami Tolvanen, Aaron Tomlin
Cc: linux-arch, linux-kernel, linux-modules, Jim Cromie
In-Reply-To: <20260507-asm-generic-1-v2-0-47c52759d268@gmail.com>
The BOUNDED_SECTION_PRE_LABEL and BOUNDED_SECTION_POST_LABEL macros
were recently updated to inherently enforce an 8-byte alignment. This
makes the explicit '. = ALIGN(8);' statements preceding 'naked' macro
calls in vmlinux.lds.h redundant.
Remove these redundant alignment directives to clean up the file and
clarify that the macros handle their own alignment padding.
Signed-off-by: Jim Cromie <jim.cromie@gmail.com>
---
include/asm-generic/vmlinux.lds.h | 13 -------------
1 file changed, 13 deletions(-)
diff --git a/include/asm-generic/vmlinux.lds.h b/include/asm-generic/vmlinux.lds.h
index d3f20b411e16..d9d2eb708355 100644
--- a/include/asm-generic/vmlinux.lds.h
+++ b/include/asm-generic/vmlinux.lds.h
@@ -229,7 +229,6 @@
#ifdef CONFIG_KPROBES
#define KPROBE_BLACKLIST() \
- . = ALIGN(8); \
BOUNDED_SECTION(_kprobe_blacklist)
#else
#define KPROBE_BLACKLIST()
@@ -245,7 +244,6 @@
#ifdef CONFIG_EVENT_TRACING
#define FTRACE_EVENTS() \
- . = ALIGN(8); \
BOUNDED_SECTION(_ftrace_events) \
BOUNDED_SECTION_BY(_ftrace_eval_map, _ftrace_eval_maps)
#else
@@ -262,7 +260,6 @@
#ifdef CONFIG_FTRACE_SYSCALLS
#define TRACE_SYSCALLS() \
- . = ALIGN(8); \
BOUNDED_SECTION_BY(__syscalls_metadata, _syscalls_metadata)
#else
#define TRACE_SYSCALLS()
@@ -277,7 +274,6 @@
#ifdef CONFIG_SERIAL_EARLYCON
#define EARLYCON_TABLE() \
- . = ALIGN(8); \
BOUNDED_SECTION_POST_LABEL(__earlycon_table, __earlycon_table, , _end)
#else
#define EARLYCON_TABLE()
@@ -285,11 +281,9 @@
#ifdef CONFIG_SECURITY
#define LSM_TABLE() \
- . = ALIGN(8); \
BOUNDED_SECTION_PRE_LABEL(.lsm_info.init, _lsm_info, __start, __end)
#define EARLY_LSM_TABLE() \
- . = ALIGN(8); \
BOUNDED_SECTION_PRE_LABEL(.early_lsm_info.init, _early_lsm_info, __start, __end)
#else
#define LSM_TABLE()
@@ -315,7 +309,6 @@
#ifdef CONFIG_ACPI
#define ACPI_PROBE_TABLE(name) \
- . = ALIGN(8); \
BOUNDED_SECTION_POST_LABEL(__##name##_acpi_probe_table, \
__##name##_acpi_probe_table,, _end)
#else
@@ -324,7 +317,6 @@
#ifdef CONFIG_THERMAL
#define THERMAL_TABLE(name) \
- . = ALIGN(8); \
BOUNDED_SECTION_POST_LABEL(__##name##_thermal_table, \
__##name##_thermal_table,, _end)
#else
@@ -404,12 +396,10 @@
__end_init_stack = .;
#define JUMP_TABLE_DATA \
- . = ALIGN(8); \
BOUNDED_SECTION_BY(__jump_table, ___jump_table)
#ifdef CONFIG_HAVE_STATIC_CALL_INLINE
#define STATIC_CALL_DATA \
- . = ALIGN(8); \
BOUNDED_SECTION_BY(.static_call_sites, _static_call_sites) \
BOUNDED_SECTION_BY(.static_call_tramp_key, _static_call_tramp_key)
#else
@@ -454,7 +444,6 @@
*(.rodata) *(.rodata.*) *(.data.rel.ro*) \
SCHED_DATA \
RO_AFTER_INIT_DATA /* Read only after init */ \
- . = ALIGN(8); \
BOUNDED_SECTION_BY(__tracepoints_ptrs, ___tracepoints_ptrs) \
*(__tracepoints_strings)/* Tracepoints: strings */ \
} \
@@ -947,12 +936,10 @@
/* Alignment must be consistent with (kunit_suite *) in include/kunit/test.h */
#define KUNIT_TABLE() \
- . = ALIGN(8); \
BOUNDED_SECTION_POST_LABEL(.kunit_test_suites, __kunit_suites, _start, _end)
/* Alignment must be consistent with (kunit_suite *) in include/kunit/test.h */
#define KUNIT_INIT_TABLE() \
- . = ALIGN(8); \
BOUNDED_SECTION_POST_LABEL(.kunit_init_test_suites, \
__kunit_init_suites, _start, _end)
--
2.54.0
^ permalink raw reply related
* [PATCH v2 3/5] vmlinux.lds.h: Fix ALIGN(8) omission causing NULL ptr on i386
From: Jim Cromie @ 2026-05-07 20:40 UTC (permalink / raw)
To: Arnd Bergmann, Jason Baron, Luis Chamberlain, Petr Pavlu,
Daniel Gomez, Sami Tolvanen, Aaron Tomlin
Cc: linux-arch, linux-kernel, linux-modules, Jim Cromie,
Louis Chauvet
In-Reply-To: <20260507-asm-generic-1-v2-0-47c52759d268@gmail.com>
Almost all uses of the BOUNDED_SECTION macros are ALIGN(8), either
explicitly, or by being below an aligned section containing x*8 byte
objects. The noteworthy exception is BOUNDED_SECTION(__dyndbg), which
immediately follows BOUNDED_SECTION(__dyndbg_classes).
On i386, struct _ddebug_classmap is 28 bytes, so without an explicit
ALIGN(8) in the macro, the following __dyndbg section gets misaligned,
causing a NULL ptr deref in dynamic_debug_init().
So fix this with an explicit ALIGN(8) in the existing BOUNDED_SECTION
macros, and introduce _ALIGNED variants to handle the cases with an
explicit . = ALIGN(x)
Explicit alignments: EXCEPTION_TABLE, ORC_UNWIND_TABLE
update BOUNDED_SECTION uses inside . = ALIGN(x) to use _ALIGNED
variants, but keep the outer ALIGNs so the symbols between them are
not "re-aligned".
In particular, scripts/sorttable.c does not tolerate sloppy padding.
At the top of ORC_UNWIND_TABLE, add . = ALIGN(4) to match the struct
orc_header __align() call in the code:
commit b9f174c811e3 ("x86/unwind/orc: Add ELF section with ORC version identifier")
Suggested-by: Louis Chauvet <louis.chauvet@bootlin.com> # _ALIGNED variants.
Link: https://lore.kernel.org/lkml/177402491426.6181.12855763650074831089.b4-review@b4/
Signed-off-by: Jim Cromie <jim.cromie@gmail.com>
---
include/asm-generic/bounded_sections.lds.h | 17 ++++++++++++++---
include/asm-generic/vmlinux.lds.h | 9 +++++----
2 files changed, 19 insertions(+), 7 deletions(-)
diff --git a/include/asm-generic/bounded_sections.lds.h b/include/asm-generic/bounded_sections.lds.h
index 268cdc34389b..8ff3e3420f60 100644
--- a/include/asm-generic/bounded_sections.lds.h
+++ b/include/asm-generic/bounded_sections.lds.h
@@ -3,19 +3,30 @@
#ifndef _ASM_GENERIC_BOUNDED_SECTIONS_H
#define _ASM_GENERIC_BOUNDED_SECTIONS_H
-#define BOUNDED_SECTION_PRE_LABEL(_sec_, _label_, _BEGIN_, _END_) \
+#define BOUNDED_SECTION_PRE_LABEL_ALIGNED(_sec_, _label_, _BEGIN_, _END_, _ALIGNED_) \
+ . = ALIGN(_ALIGNED_); \
_BEGIN_##_label_ = .; \
KEEP(*(_sec_)) \
_END_##_label_ = .;
-#define BOUNDED_SECTION_POST_LABEL(_sec_, _label_, _BEGIN_, _END_) \
+#define BOUNDED_SECTION_PRE_LABEL(_sec_, _label_, _BEGIN_, _END_) \
+ BOUNDED_SECTION_PRE_LABEL_ALIGNED(_sec_, _label_, _BEGIN_, _END_, 8)
+
+#define BOUNDED_SECTION_POST_LABEL_ALIGNED(_sec_, _label_, _BEGIN_, _END_, _ALIGNED_) \
+ . = ALIGN(_ALIGNED_); \
_label_##_BEGIN_ = .; \
KEEP(*(_sec_)) \
_label_##_END_ = .;
+#define BOUNDED_SECTION_POST_LABEL(_sec_, _label_, _BEGIN_, _END_) \
+ BOUNDED_SECTION_POST_LABEL_ALIGNED(_sec_, _label_, _BEGIN_, _END_, 8)
+
#define BOUNDED_SECTION_BY(_sec_, _label_) \
BOUNDED_SECTION_PRE_LABEL(_sec_, _label_, __start, __stop)
-#define BOUNDED_SECTION(_sec) BOUNDED_SECTION_BY(_sec, _sec)
+#define BOUNDED_SECTION_BY_ALIGNED(_sec_, _label_, _ALIGNED_) \
+ BOUNDED_SECTION_PRE_LABEL_ALIGNED(_sec_, _label_, __start, __stop, _ALIGNED_)
+
+#define BOUNDED_SECTION(_sec) BOUNDED_SECTION_BY(_sec, _sec)
#endif /* _ASM_GENERIC_BOUNDED_SECTIONS_H */
diff --git a/include/asm-generic/vmlinux.lds.h b/include/asm-generic/vmlinux.lds.h
index f78300aea8f6..d3f20b411e16 100644
--- a/include/asm-generic/vmlinux.lds.h
+++ b/include/asm-generic/vmlinux.lds.h
@@ -641,7 +641,7 @@
#define EXCEPTION_TABLE(align) \
. = ALIGN(align); \
__ex_table : AT(ADDR(__ex_table) - LOAD_OFFSET) { \
- BOUNDED_SECTION_BY(__ex_table, ___ex_table) \
+ BOUNDED_SECTION_BY_ALIGNED(__ex_table, ___ex_table, align) \
}
/*
@@ -833,16 +833,17 @@
#ifdef CONFIG_UNWINDER_ORC
#define ORC_UNWIND_TABLE \
+ . = ALIGN(4); \
.orc_header : AT(ADDR(.orc_header) - LOAD_OFFSET) { \
- BOUNDED_SECTION_BY(.orc_header, _orc_header) \
+ BOUNDED_SECTION_BY_ALIGNED(.orc_header, _orc_header, 4) \
} \
. = ALIGN(4); \
.orc_unwind_ip : AT(ADDR(.orc_unwind_ip) - LOAD_OFFSET) { \
- BOUNDED_SECTION_BY(.orc_unwind_ip, _orc_unwind_ip) \
+ BOUNDED_SECTION_BY_ALIGNED(.orc_unwind_ip, _orc_unwind_ip, 4)\
} \
. = ALIGN(2); \
.orc_unwind : AT(ADDR(.orc_unwind) - LOAD_OFFSET) { \
- BOUNDED_SECTION_BY(.orc_unwind, _orc_unwind) \
+ BOUNDED_SECTION_BY_ALIGNED(.orc_unwind, _orc_unwind, 2) \
} \
text_size = _etext - _stext; \
. = ALIGN(4); \
--
2.54.0
^ permalink raw reply related
* [PATCH v2 2/5] vmlinux.lds.h: drop unused HEADERED_SECTION* macros
From: Jim Cromie @ 2026-05-07 20:40 UTC (permalink / raw)
To: Arnd Bergmann, Jason Baron, Luis Chamberlain, Petr Pavlu,
Daniel Gomez, Sami Tolvanen, Aaron Tomlin
Cc: linux-arch, linux-kernel, linux-modules, Jim Cromie
In-Reply-To: <20260507-asm-generic-1-v2-0-47c52759d268@gmail.com>
These macros are unused, no point in carrying them any more.
NB: these macros were just moved to bounded_sections.lds.h, from
vmlinux.lds.h, which is the known entity, and therefore more
meaningful in the 1-line summary, so thats what I used as the topic.
Signed-off-by: Jim Cromie <jim.cromie@gmail.com>
---
include/asm-generic/bounded_sections.lds.h | 15 ---------------
1 file changed, 15 deletions(-)
diff --git a/include/asm-generic/bounded_sections.lds.h b/include/asm-generic/bounded_sections.lds.h
index 8c29293ca7fb..268cdc34389b 100644
--- a/include/asm-generic/bounded_sections.lds.h
+++ b/include/asm-generic/bounded_sections.lds.h
@@ -18,19 +18,4 @@
#define BOUNDED_SECTION(_sec) BOUNDED_SECTION_BY(_sec, _sec)
-#define HEADERED_SECTION_PRE_LABEL(_sec_, _label_, _BEGIN_, _END_, _HDR_) \
- _HDR_##_label_ = .; \
- KEEP(*(.gnu.linkonce.##_sec_)) \
- BOUNDED_SECTION_PRE_LABEL(_sec_, _label_, _BEGIN_, _END_)
-
-#define HEADERED_SECTION_POST_LABEL(_sec_, _label_, _BEGIN_, _END_, _HDR_) \
- _label_##_HDR_ = .; \
- KEEP(*(.gnu.linkonce.##_sec_)) \
- BOUNDED_SECTION_POST_LABEL(_sec_, _label_, _BEGIN_, _END_)
-
-#define HEADERED_SECTION_BY(_sec_, _label_) \
- HEADERED_SECTION_PRE_LABEL(_sec_, _label_, __start, __stop)
-
-#define HEADERED_SECTION(_sec) HEADERED_SECTION_BY(_sec, _sec)
-
#endif /* _ASM_GENERIC_BOUNDED_SECTIONS_H */
--
2.54.0
^ permalink raw reply related
* [PATCH v2 1/5] vmlinux.lds.h: refactor BOUNDED_SECTION_* macros into bounded_sections.lds.h
From: Jim Cromie @ 2026-05-07 20:40 UTC (permalink / raw)
To: Arnd Bergmann, Jason Baron, Luis Chamberlain, Petr Pavlu,
Daniel Gomez, Sami Tolvanen, Aaron Tomlin
Cc: linux-arch, linux-kernel, linux-modules, Jim Cromie
In-Reply-To: <20260507-asm-generic-1-v2-0-47c52759d268@gmail.com>
Move BOUNDED_SECTION_* macros to a new helper file:
include/asm-generic/bounded_sections.lds.h and include it back into
vmlinux.lds.h. This allows its reuse later to fix a failure to keep
dyndbg sections in some circumstances.
NOTES:
These macros are only for use in vmlinux.lds.h, where the _start &
_end symbols are needed. Modules keep sections separate in ELF
sections, with their boundaries known, so the _start and _end are not
useful, and may confuse tools not expecting them.
This patch ignores a checkpatch warning, because new file is covered
by "GENERIC INCLUDE/ASM HEADER FILES" in MAINTAINERS
CC: Arnd Bergmann <arnd@arndb.de>
CC: linux-arch@vger.kernel.org
Signed-off-by: Jim Cromie <jim.cromie@gmail.com>
---
include/asm-generic/bounded_sections.lds.h | 36 ++++++++++++++++++++++++++++++
include/asm-generic/vmlinux.lds.h | 30 +------------------------
2 files changed, 37 insertions(+), 29 deletions(-)
diff --git a/include/asm-generic/bounded_sections.lds.h b/include/asm-generic/bounded_sections.lds.h
new file mode 100644
index 000000000000..8c29293ca7fb
--- /dev/null
+++ b/include/asm-generic/bounded_sections.lds.h
@@ -0,0 +1,36 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#ifndef _ASM_GENERIC_BOUNDED_SECTIONS_H
+#define _ASM_GENERIC_BOUNDED_SECTIONS_H
+
+#define BOUNDED_SECTION_PRE_LABEL(_sec_, _label_, _BEGIN_, _END_) \
+ _BEGIN_##_label_ = .; \
+ KEEP(*(_sec_)) \
+ _END_##_label_ = .;
+
+#define BOUNDED_SECTION_POST_LABEL(_sec_, _label_, _BEGIN_, _END_) \
+ _label_##_BEGIN_ = .; \
+ KEEP(*(_sec_)) \
+ _label_##_END_ = .;
+
+#define BOUNDED_SECTION_BY(_sec_, _label_) \
+ BOUNDED_SECTION_PRE_LABEL(_sec_, _label_, __start, __stop)
+
+#define BOUNDED_SECTION(_sec) BOUNDED_SECTION_BY(_sec, _sec)
+
+#define HEADERED_SECTION_PRE_LABEL(_sec_, _label_, _BEGIN_, _END_, _HDR_) \
+ _HDR_##_label_ = .; \
+ KEEP(*(.gnu.linkonce.##_sec_)) \
+ BOUNDED_SECTION_PRE_LABEL(_sec_, _label_, _BEGIN_, _END_)
+
+#define HEADERED_SECTION_POST_LABEL(_sec_, _label_, _BEGIN_, _END_, _HDR_) \
+ _label_##_HDR_ = .; \
+ KEEP(*(.gnu.linkonce.##_sec_)) \
+ BOUNDED_SECTION_POST_LABEL(_sec_, _label_, _BEGIN_, _END_)
+
+#define HEADERED_SECTION_BY(_sec_, _label_) \
+ HEADERED_SECTION_PRE_LABEL(_sec_, _label_, __start, __stop)
+
+#define HEADERED_SECTION(_sec) HEADERED_SECTION_BY(_sec, _sec)
+
+#endif /* _ASM_GENERIC_BOUNDED_SECTIONS_H */
diff --git a/include/asm-generic/vmlinux.lds.h b/include/asm-generic/vmlinux.lds.h
index 60c8c22fd3e4..f78300aea8f6 100644
--- a/include/asm-generic/vmlinux.lds.h
+++ b/include/asm-generic/vmlinux.lds.h
@@ -211,35 +211,7 @@
# endif
#endif
-#define BOUNDED_SECTION_PRE_LABEL(_sec_, _label_, _BEGIN_, _END_) \
- _BEGIN_##_label_ = .; \
- KEEP(*(_sec_)) \
- _END_##_label_ = .;
-
-#define BOUNDED_SECTION_POST_LABEL(_sec_, _label_, _BEGIN_, _END_) \
- _label_##_BEGIN_ = .; \
- KEEP(*(_sec_)) \
- _label_##_END_ = .;
-
-#define BOUNDED_SECTION_BY(_sec_, _label_) \
- BOUNDED_SECTION_PRE_LABEL(_sec_, _label_, __start, __stop)
-
-#define BOUNDED_SECTION(_sec) BOUNDED_SECTION_BY(_sec, _sec)
-
-#define HEADERED_SECTION_PRE_LABEL(_sec_, _label_, _BEGIN_, _END_, _HDR_) \
- _HDR_##_label_ = .; \
- KEEP(*(.gnu.linkonce.##_sec_)) \
- BOUNDED_SECTION_PRE_LABEL(_sec_, _label_, _BEGIN_, _END_)
-
-#define HEADERED_SECTION_POST_LABEL(_sec_, _label_, _BEGIN_, _END_, _HDR_) \
- _label_##_HDR_ = .; \
- KEEP(*(.gnu.linkonce.##_sec_)) \
- BOUNDED_SECTION_POST_LABEL(_sec_, _label_, _BEGIN_, _END_)
-
-#define HEADERED_SECTION_BY(_sec_, _label_) \
- HEADERED_SECTION_PRE_LABEL(_sec_, _label_, __start, __stop)
-
-#define HEADERED_SECTION(_sec) HEADERED_SECTION_BY(_sec, _sec)
+#include <asm-generic/bounded_sections.lds.h>
#ifdef CONFIG_TRACE_BRANCH_PROFILING
#define LIKELY_PROFILE() \
--
2.54.0
^ permalink raw reply related
* [PATCH v2 0/5] dyndbg: fix i386 alignment and KEEP module sections
From: Jim Cromie @ 2026-05-07 20:40 UTC (permalink / raw)
To: Arnd Bergmann, Jason Baron, Luis Chamberlain, Petr Pavlu,
Daniel Gomez, Sami Tolvanen, Aaron Tomlin
Cc: linux-arch, linux-kernel, linux-modules, Jim Cromie,
Louis Chauvet
This series starts with a fix to a section misalignement on i386, and
follows with cleanups and refactoring for reuse.
When CONFIG_DRM_USE_DYNAMIC_DEBUG=Y (currently marked BROKEN) and
CONFIG_DRM=y, the __dyndbg_classes section gets a single
struct/record. On i386 this is 28-bytes, breaking the required
ALIGN(8) on the following __dyndbg section.
This breaks dynamic_debug_init() with a NULL pointer dereference; the
section traversal logic encounters padding bytes instead of a valid
descriptor.
the series:
1. refactors BOUNDED_SECTION_* macros for later reuse
v2- adds note that macros are for vmlinux.lds.h only, not for modules.
2. drops unused HEADERED_SECTION_* macros
3. fixes ALIGN(8) omission causing NULL ptr on i386
adds,uses BOUNDED_SECTION _ALIGNED variants
4. removes now redundant ALIGN(8)s in vmlinux.lds.h
5. adds dyndbg.lds.S to KEEP sections in modules
follows codetag.lds.h model
v2:
- avoids BOUNDED_SECTION in modules. Since ELF sections have well
defined bounaries, _start & _end symbols are useless, and empty
sections are unhelpful, possibly confusing to tools.
- sets 0 address to the sections, not just whatever current is.
Signed-off-by: Jim Cromie <jim.cromie@gmail.com>
---
Changes in v2:
- EDITME: describe what is new in this series revision.
- EDITME: use bulletpoints and terse descriptions.
- Link to v1: https://lore.kernel.org/r/20260502-asm-generic-1-v1-0-1103ee0152df@gmail.com
---
Jim Cromie (5):
vmlinux.lds.h: refactor BOUNDED_SECTION_* macros into bounded_sections.lds.h
vmlinux.lds.h: drop unused HEADERED_SECTION* macros
vmlinux.lds.h: Fix ALIGN(8) omission causing NULL ptr on i386
vmlinux.lds.h: remove redundant ALIGN(8) directives
dyndbg.lds.S: fix lost dyndbg sections in modules
MAINTAINERS | 1 +
include/asm-generic/bounded_sections.lds.h | 32 +++++++++++++++++
include/asm-generic/dyndbg.lds.h | 19 ++++++++++
include/asm-generic/vmlinux.lds.h | 58 +++++-------------------------
scripts/module.lds.S | 2 ++
5 files changed, 62 insertions(+), 50 deletions(-)
---
base-commit: 6d35786de28116ecf78797a62b84e6bf3c45aa5a
change-id: 20260502-asm-generic-1-32c842a35dd0
Best regards,
--
Jim Cromie <jim.cromie@gmail.com>
^ permalink raw reply
* Re: [PATCH v2 2/2] module/kallsyms: sort function symbols and use binary search
From: Petr Pavlu @ 2026-05-06 9:42 UTC (permalink / raw)
To: Petr Mladek
Cc: Stanislaw Gruszka, linux-modules, Sami Tolvanen, Luis Chamberlain,
linux-kernel, linux-trace-kernel, live-patching, Daniel Gomez,
Aaron Tomlin, Steven Rostedt, Masami Hiramatsu, Jordan Rome,
Viktor Malik, Miroslav Benes, Josh Poimboeuf, Joe Lawrence
In-Reply-To: <afsCpoGPasi-kBLb@pathway.suse.cz>
On 5/6/26 10:58 AM, Petr Mladek wrote:
> On Tue 2026-05-05 16:37:56, Petr Pavlu wrote:
>> On 5/5/26 2:24 PM, Petr Mladek wrote:
>>> On Fri 2026-03-27 12:00:05, Stanislaw Gruszka wrote:
>>>> Module symbol lookup via find_kallsyms_symbol() performs a linear scan
>>>> over the entire symtab when resolving an address. The number of symbols
>>>> in module symtabs has grown over the years, largely due to additional
>>>> metadata in non-standard sections, making this lookup very slow.
>>>>
>>>> Improve this by separating function symbols during module load, placing
>>>> them at the beginning of the symtab, sorting them by address, and using
>>>> binary search when resolving addresses in module text.
>>>>
>>>> This also should improve times for linear symbol name lookups, as valid
>>>> function symbols are now located at the beginning of the symtab.
>>>>
>>>> The cost of sorting is small relative to module load time. In repeated
>>>> module load tests [1], depending on .config options, this change
>>>> increases load time between 2% and 4%. With cold caches, the difference
>>>> is not measurable, as memory access latency dominates.
>>>>
>>>> The sorting theoretically could be done in compile time, but much more
>>>> complicated as we would have to simulate kernel addresses resolution
>>>> for symbols, and then correct relocation entries. That would be risky
>>>> if get out of sync.
>>>>
>>>> The improvement can be observed when listing ftrace filter functions.
>>>>
>>>> Before:
>>>>
>>>> root@nano:~# time cat /sys/kernel/tracing/available_filter_functions | wc -l
>>>> 74908
>>>>
>>>> real 0m1.315s
>>>> user 0m0.000s
>>>> sys 0m1.312s
>>>>
>>>> After:
>>>>
>>>> root@nano:~# time cat /sys/kernel/tracing/available_filter_functions | wc -l
>>>> 74911
>>>>
>>>> real 0m0.167s
>>>> user 0m0.004s
>>>> sys 0m0.175s
>>>>
>>>> (there are three more symbols introduced by the patch)
>>>>
>>>> For livepatch modules, the symtab layout is preserved and the existing
>>>> linear search is used. For this case, it should be possible to keep
>>>> the original ELF symtab instead of copying it 1:1, but that is outside
>>>> the scope of this patch.
>>>
>>> What is the exact motivation for the special handling of livepatch modules,
>>> please?
>>>
>>> Honestly, I am always a bit lost in the various symbol tables. It is
>>> possile that I have got something wrong.
>>>
>>> Anyway, my understanding is that there are two aspects which are important
>>> for livepatches:
>>>
>>> 1. Livepatches need to preserve special symbols which are used to
>>> relocate symbols which were local in the original code, see
>>> Documentation/livepatch/module-elf-format.rst
>>>
>>> IMHO, this is why layout_symtab() computes space for all core
>>> symbols in livepatch modules and copies them in add_kallsyms().
>>>
>>> The symtab is normally released when the module is loaded.
>>> But livepatch modules make its own copy of the important
>>> parts, see copy_module_elf().
>>>
>>> IMHO, the sorting of function symbols vs other symbols does
>>> not matter here. I believe that the special relocation
>>> symbols are not affected by this.
>>
>> I'm not sure if I fully follow the conclusion in this point. My
>> understanding is that .klp.rela sections still refer to their special
>> symbols in the symbol table via Elf_Rela::r_info. If the symbol table is
>> filtered or reordered, these references will end up pointing to
>> incorrect symbols.
>
> My understanding is that the relocations point to symbols which
> are found via the name of the entry. Let's take an example
> from Documentation/livepatch/module-elf-format.rst:
>
> 73: 0000000000000000 0 NOTYPE GLOBAL DEFAULT OS [0xff20] .klp.sym.vmlinux.snprintf,0
>
> This symbol points to snprintf() function in vmlinux object.
> The address of this function is found via kallsyms, see
> klp_find_object_symbol().
>
> IMHO, it does not matter if we shuffle this entry in the livepatch
> module because the real address is found via kallsyms().
>
> Even the ordering of the entries in vmlinux is not important in
> _this particular case_ because the "sympos" is zero "0" in this case.
> It means that "snprintf" symbol name is unique in vmlinux.
>
> The ordering of the symbols in "vmlinux" becomes important
> if the livepatch needs to access a symbol and there are more
> symbols of the same name. This is what I tried to describe
> below.
>
> I hope that it makes some sense. As I said, I am not familiar
> with the elf format...
Ah, I misunderstood your original point. I agree that reshuffling the
symbol table in a livepatch module will not cause any issues with
binding .klp.sym.<objname>.<symname>,<sympos> symbol references to their
actual definitions in <objname>.
The problem still exists with the .klp.rela sections. They are regular
RELA sections in the sense that each ELF_R_SYM(Elf_Rela::r_info) value
is an index identifying a symbol in the symbol table. If the module
loader reshuffles or filters the original symbol table in any way, the
indexes in Elf_Rela::r_info would need to be adjusted accordingly.
--
Thanks,
Petr
^ permalink raw reply
* Re: [PATCH v2 2/2] module/kallsyms: sort function symbols and use binary search
From: Petr Mladek @ 2026-05-06 8:58 UTC (permalink / raw)
To: Petr Pavlu
Cc: Stanislaw Gruszka, linux-modules, Sami Tolvanen, Luis Chamberlain,
linux-kernel, linux-trace-kernel, live-patching, Daniel Gomez,
Aaron Tomlin, Steven Rostedt, Masami Hiramatsu, Jordan Rome,
Viktor Malik, Miroslav Benes, Josh Poimboeuf, Joe Lawrence
In-Reply-To: <28bb0f74-8721-4e53-ad89-87b2a78623b2@suse.com>
On Tue 2026-05-05 16:37:56, Petr Pavlu wrote:
> On 5/5/26 2:24 PM, Petr Mladek wrote:
> > On Fri 2026-03-27 12:00:05, Stanislaw Gruszka wrote:
> >> Module symbol lookup via find_kallsyms_symbol() performs a linear scan
> >> over the entire symtab when resolving an address. The number of symbols
> >> in module symtabs has grown over the years, largely due to additional
> >> metadata in non-standard sections, making this lookup very slow.
> >>
> >> Improve this by separating function symbols during module load, placing
> >> them at the beginning of the symtab, sorting them by address, and using
> >> binary search when resolving addresses in module text.
> >>
> >> This also should improve times for linear symbol name lookups, as valid
> >> function symbols are now located at the beginning of the symtab.
> >>
> >> The cost of sorting is small relative to module load time. In repeated
> >> module load tests [1], depending on .config options, this change
> >> increases load time between 2% and 4%. With cold caches, the difference
> >> is not measurable, as memory access latency dominates.
> >>
> >> The sorting theoretically could be done in compile time, but much more
> >> complicated as we would have to simulate kernel addresses resolution
> >> for symbols, and then correct relocation entries. That would be risky
> >> if get out of sync.
> >>
> >> The improvement can be observed when listing ftrace filter functions.
> >>
> >> Before:
> >>
> >> root@nano:~# time cat /sys/kernel/tracing/available_filter_functions | wc -l
> >> 74908
> >>
> >> real 0m1.315s
> >> user 0m0.000s
> >> sys 0m1.312s
> >>
> >> After:
> >>
> >> root@nano:~# time cat /sys/kernel/tracing/available_filter_functions | wc -l
> >> 74911
> >>
> >> real 0m0.167s
> >> user 0m0.004s
> >> sys 0m0.175s
> >>
> >> (there are three more symbols introduced by the patch)
> >>
> >> For livepatch modules, the symtab layout is preserved and the existing
> >> linear search is used. For this case, it should be possible to keep
> >> the original ELF symtab instead of copying it 1:1, but that is outside
> >> the scope of this patch.
> >
> > What is the exact motivation for the special handling of livepatch modules,
> > please?
> >
> > Honestly, I am always a bit lost in the various symbol tables. It is
> > possile that I have got something wrong.
> >
> > Anyway, my understanding is that there are two aspects which are important
> > for livepatches:
> >
> > 1. Livepatches need to preserve special symbols which are used to
> > relocate symbols which were local in the original code, see
> > Documentation/livepatch/module-elf-format.rst
> >
> > IMHO, this is why layout_symtab() computes space for all core
> > symbols in livepatch modules and copies them in add_kallsyms().
> >
> > The symtab is normally released when the module is loaded.
> > But livepatch modules make its own copy of the important
> > parts, see copy_module_elf().
> >
> > IMHO, the sorting of function symbols vs other symbols does
> > not matter here. I believe that the special relocation
> > symbols are not affected by this.
>
> I'm not sure if I fully follow the conclusion in this point. My
> understanding is that .klp.rela sections still refer to their special
> symbols in the symbol table via Elf_Rela::r_info. If the symbol table is
> filtered or reordered, these references will end up pointing to
> incorrect symbols.
My understanding is that the relocations point to symbols which
are found via the name of the entry. Let's take an example
from Documentation/livepatch/module-elf-format.rst:
73: 0000000000000000 0 NOTYPE GLOBAL DEFAULT OS [0xff20] .klp.sym.vmlinux.snprintf,0
This symbol points to snprintf() function in vmlinux object.
The address of this function is found via kallsyms, see
klp_find_object_symbol().
IMHO, it does not matter if we shuffle this entry in the livepatch
module because the real address is found via kallsyms().
Even the ordering of the entries in vmlinux is not important in
_this particular case_ because the "sympos" is zero "0" in this case.
It means that "snprintf" symbol name is unique in vmlinux.
The ordering of the symbols in "vmlinux" becomes important
if the livepatch needs to access a symbol and there are more
symbols of the same name. This is what I tried to describe
below.
I hope that it makes some sense. As I said, I am not familiar
with the elf format...
> This is also described in Documentation/livepatch/module-elf-format.rst,
> section "4.1 A livepatch module's symbol table".
>
> >
> >
> > 2. Livepatches _rely on the sorting_ of symbols in the module.
> > The special relocation symbols might define a symbol position,
> > see
> >
> > .klp.sym.objname.symbol_name,sympos
> >
> > in the documentation. It defines the position of the symbol
> > when there are more symbols of the same name, see
> > klp_match_callback() in kernel/livepatch/core.c.
> >
> > I am afraid that _this patch might break_ this when it moves
> > function symbols before non-funciton ones. I guess that
> > the symbols of the same name will not longer be groupped.
>
> I see. So if the module loader sorts the symbol table in a regular
> module and a livepatch module exists for that module, the livepatch may
> no longer function correctly. This means that the loader cannot even
> reorder the symbol table in regular modules.
Yeah, reordering symbols in regular module might break livepatching.
Namely it might break finding the right symbol when there are
more symbols of the same name.
Best Regards,
Petr
^ permalink raw reply
* Re: [PATCH] dept: update documentation function names to match implementation
From: Byungchul Park @ 2026-05-06 6:27 UTC (permalink / raw)
To: Yunseong Kim
Cc: bagasdotme, 2407018371, Dai.Ngo, Liam.Howlett, a.hindborg,
ada.coupriediaz, adilger.kernel, akpm, alex.gaynor,
alexander.shishkin, aliceryhl, amir73il, andi.shyti, andrii, anna,
arnd, ast, baolin.wang, bigeasy, bjorn3_gh, boqun.feng, bp,
brauner, broonie, bsegall, catalin.marinas, chenhuacai,
chris.p.wilson, christian.koenig, chuck.lever, cl, clrkwllms,
corbet, da.gomez, dakr, damien.lemoal, dan.j.williams,
daniel.vetter, dave.hansen, david, dennis, dietmar.eggemann,
djwong, dri-devel, duyuyang, dwmw, francesco, frederic, gary,
geert+renesas, geert, gregkh, guoweikang.kernel, gustavo,
gwan-gyeong.mun, hamohammed.sa, hannes, harry.yoo, hch,
her0gyugyu, hpa, jack, jglisse, jiangshanlai, jlayton,
joel.granados, joel, joelagnelf, johannes.berg, josef, josh,
jpoimboe, juri.lelli, kees, kernel-team, kernel_team,
kevin.brodsky, kristina.martsenko, lillian, linaro-mm-sig, link,
linux-arch, linux-arm-kernel, linux-block, linux-doc, linux-ext4,
linux-fsdevel, linux-i2c, linux-ide, linux-kernel, linux-media,
linux-mm, linux-modules, linux-nfs, linux-rt-devel, linux,
longman, lorenzo.stoakes, lossin, luto, mark.rutland, masahiroy,
mathieu.desnoyers, matthew.brost, max.byungchul.park, mcgrof,
melissa.srw, mgorman, mhocko, miguel.ojeda.sandonis, minchan,
mingo, mjguzik, neeraj.upadhyay, neil, neilb, netdev, ngupta,
ojeda, okorniev, oleg, paulmck, penberg, peterz, petr.pavlu,
qiang.zhang, rcu, richard.weiyang, rientjes, rodrigosiqueiramelo,
rostedt, rppt, rust-for-linux, samitolvanen, sashal, shakeel.butt,
sj, sumit.semwal, surenb, tglx, thomas.weissschuh, tim.c.chen, tj,
tmgross, tom, torvalds, trondmy, tytso, urezki, usamaarif642,
vbabka, vdavydov.dev, vincent.guittot, vschneid, wangfushuai,
wangkefeng.wang, will, willy, wsa+renesas, x86, yeoreum.yun, ysk,
yunseong.kim, yuzhao, ziy
In-Reply-To: <20260428162614.786365-2-yunseong.kim@est.tech>
On Tue, Apr 28, 2026 at 06:26:15PM +0200, Yunseong Kim wrote:
> Synchronize function names in the documentation with the actual
> implementation to fix naming inconsistencies.
Good catch! Thanks Yunseong. I will apply it on the top.
Byungchul
> Signed-off-by: Yunseong Kim <yunseong.kim@est.tech>
> ---
> Documentation/dev-tools/dept.rst | 2 +-
> Documentation/dev-tools/dept_api.rst | 2 +-
> 2 files changed, 2 insertions(+), 2 deletions(-)
>
> diff --git a/Documentation/dev-tools/dept.rst b/Documentation/dev-tools/dept.rst
> index 333166464543..31b2fe629fab 100644
> --- a/Documentation/dev-tools/dept.rst
> +++ b/Documentation/dev-tools/dept.rst
> @@ -97,7 +97,7 @@ No. What about the following?
>
> mutex_lock A
> mutex_lock A <- DEADLOCK
> - wait_for_complete B <- DEADLOCK
> + wait_for_completion B <- DEADLOCK
> complete B
> mutex_unlock A
> mutex_unlock A
> diff --git a/Documentation/dev-tools/dept_api.rst b/Documentation/dev-tools/dept_api.rst
> index 409116a62849..74e7b1424ad5 100644
> --- a/Documentation/dev-tools/dept_api.rst
> +++ b/Documentation/dev-tools/dept_api.rst
> @@ -113,7 +113,7 @@ Do not use these APIs directly. The raw APIs of dept are:
> dept_stage_wait(map, key, ip, wait_func, time);
> dept_request_event_wait_commit();
> dept_clean_stage();
> - dept_stage_event(task, ip);
> + dept_ttwu_stage_wait(task, ip);
> dept_ecxt_enter(map, evt_flags, ip, ecxt_func, evt_func, sub_local);
> dept_ecxt_holding(map, evt_flags);
> dept_request_event(map, ext_wgen);
> --
> 2.53.0
^ permalink raw reply
* Re: [PATCH v2 2/2] module/kallsyms: sort function symbols and use binary search
From: Petr Pavlu @ 2026-05-05 14:37 UTC (permalink / raw)
To: Petr Mladek
Cc: Stanislaw Gruszka, linux-modules, Sami Tolvanen, Luis Chamberlain,
linux-kernel, linux-trace-kernel, live-patching, Daniel Gomez,
Aaron Tomlin, Steven Rostedt, Masami Hiramatsu, Jordan Rome,
Viktor Malik, Miroslav Benes, Josh Poimboeuf, Joe Lawrence
In-Reply-To: <afnhidn7K7dZ_cPh@pathway.suse.cz>
On 5/5/26 2:24 PM, Petr Mladek wrote:
> On Fri 2026-03-27 12:00:05, Stanislaw Gruszka wrote:
>> Module symbol lookup via find_kallsyms_symbol() performs a linear scan
>> over the entire symtab when resolving an address. The number of symbols
>> in module symtabs has grown over the years, largely due to additional
>> metadata in non-standard sections, making this lookup very slow.
>>
>> Improve this by separating function symbols during module load, placing
>> them at the beginning of the symtab, sorting them by address, and using
>> binary search when resolving addresses in module text.
>>
>> This also should improve times for linear symbol name lookups, as valid
>> function symbols are now located at the beginning of the symtab.
>>
>> The cost of sorting is small relative to module load time. In repeated
>> module load tests [1], depending on .config options, this change
>> increases load time between 2% and 4%. With cold caches, the difference
>> is not measurable, as memory access latency dominates.
>>
>> The sorting theoretically could be done in compile time, but much more
>> complicated as we would have to simulate kernel addresses resolution
>> for symbols, and then correct relocation entries. That would be risky
>> if get out of sync.
>>
>> The improvement can be observed when listing ftrace filter functions.
>>
>> Before:
>>
>> root@nano:~# time cat /sys/kernel/tracing/available_filter_functions | wc -l
>> 74908
>>
>> real 0m1.315s
>> user 0m0.000s
>> sys 0m1.312s
>>
>> After:
>>
>> root@nano:~# time cat /sys/kernel/tracing/available_filter_functions | wc -l
>> 74911
>>
>> real 0m0.167s
>> user 0m0.004s
>> sys 0m0.175s
>>
>> (there are three more symbols introduced by the patch)
>>
>> For livepatch modules, the symtab layout is preserved and the existing
>> linear search is used. For this case, it should be possible to keep
>> the original ELF symtab instead of copying it 1:1, but that is outside
>> the scope of this patch.
>
> What is the exact motivation for the special handling of livepatch modules,
> please?
>
> Honestly, I am always a bit lost in the various symbol tables. It is
> possile that I have got something wrong.
>
> Anyway, my understanding is that there are two aspects which are important
> for livepatches:
>
> 1. Livepatches need to preserve special symbols which are used to
> relocate symbols which were local in the original code, see
> Documentation/livepatch/module-elf-format.rst
>
> IMHO, this is why layout_symtab() computes space for all core
> symbols in livepatch modules and copies them in add_kallsyms().
>
> The symtab is normally released when the module is loaded.
> But livepatch modules make its own copy of the important
> parts, see copy_module_elf().
>
> IMHO, the sorting of function symbols vs other symbols does
> not matter here. I believe that the special relocation
> symbols are not affected by this.
I'm not sure if I fully follow the conclusion in this point. My
understanding is that .klp.rela sections still refer to their special
symbols in the symbol table via Elf_Rela::r_info. If the symbol table is
filtered or reordered, these references will end up pointing to
incorrect symbols.
This is also described in Documentation/livepatch/module-elf-format.rst,
section "4.1 A livepatch module's symbol table".
>
>
> 2. Livepatches _rely on the sorting_ of symbols in the module.
> The special relocation symbols might define a symbol position,
> see
>
> .klp.sym.objname.symbol_name,sympos
>
> in the documentation. It defines the position of the symbol
> when there are more symbols of the same name, see
> klp_match_callback() in kernel/livepatch/core.c.
>
> I am afraid that _this patch might break_ this when it moves
> function symbols before non-funciton ones. I guess that
> the symbols of the same name will not longer be groupped.
I see. So if the module loader sorts the symbol table in a regular
module and a livepatch module exists for that module, the livepatch may
no longer function correctly. This means that the loader cannot even
reorder the symbol table in regular modules.
--
Thanks,
Petr
^ permalink raw reply
* Re: [PATCH v2 2/2] module/kallsyms: sort function symbols and use binary search
From: Petr Mladek @ 2026-05-05 12:24 UTC (permalink / raw)
To: Stanislaw Gruszka
Cc: linux-modules, Sami Tolvanen, Luis Chamberlain, Petr Pavlu,
linux-kernel, linux-trace-kernel, live-patching, Daniel Gomez,
Aaron Tomlin, Steven Rostedt, Masami Hiramatsu, Jordan Rome,
Viktor Malik, Miroslav Benes, Josh Poimboeuf, Joe Lawrence
In-Reply-To: <20260327110005.16499-2-stf_xl@wp.pl>
On Fri 2026-03-27 12:00:05, Stanislaw Gruszka wrote:
> Module symbol lookup via find_kallsyms_symbol() performs a linear scan
> over the entire symtab when resolving an address. The number of symbols
> in module symtabs has grown over the years, largely due to additional
> metadata in non-standard sections, making this lookup very slow.
>
> Improve this by separating function symbols during module load, placing
> them at the beginning of the symtab, sorting them by address, and using
> binary search when resolving addresses in module text.
>
> This also should improve times for linear symbol name lookups, as valid
> function symbols are now located at the beginning of the symtab.
>
> The cost of sorting is small relative to module load time. In repeated
> module load tests [1], depending on .config options, this change
> increases load time between 2% and 4%. With cold caches, the difference
> is not measurable, as memory access latency dominates.
>
> The sorting theoretically could be done in compile time, but much more
> complicated as we would have to simulate kernel addresses resolution
> for symbols, and then correct relocation entries. That would be risky
> if get out of sync.
>
> The improvement can be observed when listing ftrace filter functions.
>
> Before:
>
> root@nano:~# time cat /sys/kernel/tracing/available_filter_functions | wc -l
> 74908
>
> real 0m1.315s
> user 0m0.000s
> sys 0m1.312s
>
> After:
>
> root@nano:~# time cat /sys/kernel/tracing/available_filter_functions | wc -l
> 74911
>
> real 0m0.167s
> user 0m0.004s
> sys 0m0.175s
>
> (there are three more symbols introduced by the patch)
>
> For livepatch modules, the symtab layout is preserved and the existing
> linear search is used. For this case, it should be possible to keep
> the original ELF symtab instead of copying it 1:1, but that is outside
> the scope of this patch.
What is the exact motivation for the special handling of livepatch modules,
please?
Honestly, I am always a bit lost in the various symbol tables. It is
possile that I have got something wrong.
Anyway, my understanding is that there are two aspects which are important
for livepatches:
1. Livepatches need to preserve special symbols which are used to
relocate symbols which were local in the original code, see
Documentation/livepatch/module-elf-format.rst
IMHO, this is why layout_symtab() computes space for all core
symbols in livepatch modules and copies them in add_kallsyms().
The symtab is normally released when the module is loaded.
But livepatch modules make its own copy of the important
parts, see copy_module_elf().
IMHO, the sorting of function symbols vs other symbols does
not matter here. I believe that the special relocation
symbols are not affected by this.
2. Livepatches _rely on the sorting_ of symbols in the module.
The special relocation symbols might define a symbol position,
see
.klp.sym.objname.symbol_name,sympos
in the documentation. It defines the position of the symbol
when there are more symbols of the same name, see
klp_match_callback() in kernel/livepatch/core.c.
I am afraid that _this patch might break_ this when it moves
function symbols before non-funciton ones. I guess that
the symbols of the same name will not longer be groupped.
Idea: Is the shufling really important for the performance, please?
I would expect that binary search would have a good performance
even without the shuffling. It puts aside half of the symbols in
one cycle.
Note that the binary search in find_kallsyms_symbol() is perfectly
fine. The livepatch code explicitly iterates over all symbols using
module_kallsyms_on_each_symbol(), see klp_find_object_symbol().
Best Regards,
Petr
^ permalink raw reply
* [PATCH v5 13/14] kbuild: move handling of module stripping to Makefile.lib
From: Thomas Weißschuh @ 2026-05-05 9:05 UTC (permalink / raw)
To: Alexei Starovoitov, Daniel Borkmann, Andrii Nakryiko,
Eduard Zingerman, Kumar Kartikeya Dwivedi, Nathan Chancellor,
Nicolas Schier, Arnd Bergmann, Luis Chamberlain, Petr Pavlu,
Sami Tolvanen, Daniel Gomez, Paul Moore, James Morris,
Serge E. Hallyn, Jonathan Corbet, Madhavan Srinivasan,
Michael Ellerman, Nicholas Piggin, Naveen N Rao, Mimi Zohar,
Roberto Sassu, Dmitry Kasatkin, Eric Snowberg, Nicolas Schier,
Daniel Gomez, Aaron Tomlin, Christophe Leroy (CS GROUP),
Nicolas Bouchinet, Xiu Jianfeng, Christophe Leroy
Cc: Martin KaFai Lau, Song Liu, Yonghong Song, Jiri Olsa, bpf,
Fabian Grünbichler, Arnout Engelen, Mattia Rizzolo, kpcyrd,
Christian Heusel, Câju Mihai-Drosi, Eric Biggers,
Sebastian Andrzej Siewior, linux-kbuild, linux-kernel, linux-arch,
linux-modules, linux-security-module, linux-doc, linuxppc-dev,
linux-integrity, debian-kernel, Thomas Weißschuh
In-Reply-To: <20260505-module-hashes-v5-0-e174a5a49fce@weissschuh.net>
To allow CONFIG_MODULE_HASHES in combination with INSTALL_MOD_STRIP,
this logc will also be used by Makefile.modfinal.
Move it to a shared location to enable reuse.
Signed-off-by: Thomas Weißschuh <linux@weissschuh.net>
---
scripts/Makefile.lib | 32 ++++++++++++++++++++++++++++++++
scripts/Makefile.modinst | 37 +++++--------------------------------
2 files changed, 37 insertions(+), 32 deletions(-)
diff --git a/scripts/Makefile.lib b/scripts/Makefile.lib
index 0718e39cedda..bb713a1a11be 100644
--- a/scripts/Makefile.lib
+++ b/scripts/Makefile.lib
@@ -484,6 +484,38 @@ define sed-offsets
s:->::; p;}'
endef
+#
+# Module Installation
+#
+quiet_cmd_install_mod = INSTALL $@
+ cmd_install_mod = cp $< $@
+
+# Module Strip
+# ---------------------------------------------------------------------------
+#
+# INSTALL_MOD_STRIP, if defined, will cause modules to be stripped after they
+# are installed. If INSTALL_MOD_STRIP is '1', then the default option
+# --strip-debug will be used. Otherwise, INSTALL_MOD_STRIP value will be used
+# as the options to the strip command.
+ifeq ($(INSTALL_MOD_STRIP),1)
+mod-strip-option := --strip-debug
+else
+mod-strip-option := $(INSTALL_MOD_STRIP)
+endif
+
+# Strip
+ifdef INSTALL_MOD_STRIP
+
+quiet_cmd_strip_mod = STRIP $@
+ cmd_strip_mod = $(STRIP) $(mod-strip-option) $@
+
+else
+
+quiet_cmd_strip_mod =
+ cmd_strip_mod = :
+
+endif
+
# Use filechk to avoid rebuilds when a header changes, but the resulting file
# does not
define filechk_offsets
diff --git a/scripts/Makefile.modinst b/scripts/Makefile.modinst
index 68708a039a62..b95f613e23c8 100644
--- a/scripts/Makefile.modinst
+++ b/scripts/Makefile.modinst
@@ -8,6 +8,7 @@ __modinst:
include $(objtree)/include/config/auto.conf
include $(srctree)/scripts/Kbuild.include
+include $(srctree)/scripts/Makefile.lib
install-y :=
@@ -36,7 +37,7 @@ install-y += $(addprefix $(MODLIB)/, modules.builtin modules.builtin.modinfo)
install-$(CONFIG_BUILTIN_MODULE_RANGES) += $(MODLIB)/modules.builtin.ranges
$(addprefix $(MODLIB)/, modules.builtin modules.builtin.modinfo modules.builtin.ranges): $(MODLIB)/%: % FORCE
- $(call cmd,install)
+ $(call cmd,install_mod)
endif
@@ -65,40 +66,12 @@ install-$(CONFIG_MODULES) += $(modules)
__modinst: $(install-y)
@:
-#
-# Installation
-#
-quiet_cmd_install = INSTALL $@
- cmd_install = cp $< $@
-
-# Strip
-#
-# INSTALL_MOD_STRIP, if defined, will cause modules to be stripped after they
-# are installed. If INSTALL_MOD_STRIP is '1', then the default option
-# --strip-debug will be used. Otherwise, INSTALL_MOD_STRIP value will be used
-# as the options to the strip command.
-ifdef INSTALL_MOD_STRIP
-
ifdef CONFIG_MODULE_HASHES
ifeq ($(KBUILD_EXTMOD),)
+ifdef INSTALL_MOD_STRIP
$(error CONFIG_MODULE_HASHES and INSTALL_MOD_STRIP are mutually exclusive)
endif
endif
-
-ifeq ($(INSTALL_MOD_STRIP),1)
-strip-option := --strip-debug
-else
-strip-option := $(INSTALL_MOD_STRIP)
-endif
-
-quiet_cmd_strip = STRIP $@
- cmd_strip = $(STRIP) $(strip-option) $@
-
-else
-
-quiet_cmd_strip =
- cmd_strip = :
-
endif
#
@@ -131,8 +104,8 @@ endif
$(foreach dir, $(sort $(dir $(install-y))), $(shell mkdir -p $(dir)))
$(dst)/%.ko: %.ko FORCE
- $(call cmd,install)
- $(call cmd,strip)
+ $(call cmd,install_mod)
+ $(call cmd,strip_mod)
$(call cmd,sign)
ifdef CONFIG_MODULES
--
2.54.0
^ permalink raw reply related
* [PATCH v5 12/14] module: Introduce hash-based integrity checking
From: Thomas Weißschuh @ 2026-05-05 9:05 UTC (permalink / raw)
To: Alexei Starovoitov, Daniel Borkmann, Andrii Nakryiko,
Eduard Zingerman, Kumar Kartikeya Dwivedi, Nathan Chancellor,
Nicolas Schier, Arnd Bergmann, Luis Chamberlain, Petr Pavlu,
Sami Tolvanen, Daniel Gomez, Paul Moore, James Morris,
Serge E. Hallyn, Jonathan Corbet, Madhavan Srinivasan,
Michael Ellerman, Nicholas Piggin, Naveen N Rao, Mimi Zohar,
Roberto Sassu, Dmitry Kasatkin, Eric Snowberg, Nicolas Schier,
Daniel Gomez, Aaron Tomlin, Christophe Leroy (CS GROUP),
Nicolas Bouchinet, Xiu Jianfeng, Christophe Leroy
Cc: Martin KaFai Lau, Song Liu, Yonghong Song, Jiri Olsa, bpf,
Fabian Grünbichler, Arnout Engelen, Mattia Rizzolo, kpcyrd,
Christian Heusel, Câju Mihai-Drosi, Eric Biggers,
Sebastian Andrzej Siewior, linux-kbuild, linux-kernel, linux-arch,
linux-modules, linux-security-module, linux-doc, linuxppc-dev,
linux-integrity, debian-kernel, Thomas Weißschuh
In-Reply-To: <20260505-module-hashes-v5-0-e174a5a49fce@weissschuh.net>
The current signature-based module integrity checking has some drawbacks
in combination with reproducible builds. Either the module signing key
is generated at build time, which makes the build unreproducible, or a
static signing key is used, which precludes rebuilds by third parties
and makes the whole build and packaging process much more complicated.
The goal is to reach bit-for-bit reproducibility. Excluding certain
parts of the build output from the reproducibility analysis would be
error-prone and force each downstream consumer to introduce new tooling.
Introduce a new mechanism to ensure only well-known modules are loaded
by embedding a merkle tree root of all modules built as part of the full
kernel build into vmlinux.
Out-of-tree modules can be validated as before through signatures.
Normally the .ko module files depend on a fully built vmlinux to be
available for modpost validation and BTF generation. With
CONFIG_MODULE_HASHES, vmlinux now depends on the modules
to build a merkle tree. This introduces a dependency cycle which is
impossible to satisfy. Work around this by building the modules during
link-vmlinux.sh, after vmlinux is complete enough for modpost and BTF
but before the final module hashes are
The PKCS7 format which is used for regular module signatures can not
represent Merkle proofs, so a new kind of module signature is
introduced. As this signature type is only ever used for builtin
modules, no compatibility issues can arise.
Signed-off-by: Thomas Weißschuh <linux@weissschuh.net>
---
.gitignore | 1 +
Documentation/kbuild/reproducible-builds.rst | 5 +-
Makefile | 7 +-
include/asm-generic/vmlinux.lds.h | 11 +
include/linux/module_hashes.h | 29 ++
include/uapi/linux/module_signature.h | 1 +
kernel/module/Kconfig | 21 +-
kernel/module/Makefile | 1 +
kernel/module/auth.c | 6 +
kernel/module/hashes.c | 95 ++++++
kernel/module/hashes_root.c | 6 +
kernel/module/internal.h | 1 +
scripts/.gitignore | 1 +
scripts/Makefile | 4 +
scripts/Makefile.modinst | 11 +
scripts/Makefile.vmlinux | 32 +++
scripts/include/xalloc.h | 29 ++
scripts/link-vmlinux.sh | 3 +-
scripts/modules-merkle-tree.c | 416 +++++++++++++++++++++++++++
security/lockdown/Kconfig | 2 +-
tools/include/uapi/linux/module_signature.h | 1 +
21 files changed, 677 insertions(+), 6 deletions(-)
diff --git a/.gitignore b/.gitignore
index 3044b9590f05..78cf799401e6 100644
--- a/.gitignore
+++ b/.gitignore
@@ -36,6 +36,7 @@
*.lz4
*.lzma
*.lzo
+*.merkle
*.mod
*.mod.c
*.o
diff --git a/Documentation/kbuild/reproducible-builds.rst b/Documentation/kbuild/reproducible-builds.rst
index bc1eb82211df..b15019678aae 100644
--- a/Documentation/kbuild/reproducible-builds.rst
+++ b/Documentation/kbuild/reproducible-builds.rst
@@ -84,7 +84,10 @@ generate a different temporary key for each build, resulting in the
modules being unreproducible. However, including a signing key with
your source would presumably defeat the purpose of signing modules.
-One approach to this is to divide up the build process so that the
+Instead ``CONFIG_MODULE_HASHES`` can be used to embed a static list
+of valid modules to load.
+
+Another approach to this is to divide up the build process so that the
unreproducible parts can be treated as sources:
1. Generate a persistent signing key. Add the certificate for the key
diff --git a/Makefile b/Makefile
index e27c91ea56fc..def4a2413c43 100644
--- a/Makefile
+++ b/Makefile
@@ -1650,7 +1650,9 @@ ifdef CONFIG_MODULES
# By default, build modules as well
+ifndef CONFIG_MODULE_HASHES
all: modules
+endif
# When we're building modules with modversions, we need to consider
# the built-in objects during the descend as well, in order to
@@ -1666,8 +1668,10 @@ endif
# is an exception.
ifdef CONFIG_DEBUG_INFO_BTF_MODULES
KBUILD_BUILTIN := y
+ifndef CONFIG_MODULE_HASHES
modules: vmlinux
endif
+endif
modules: modules_prepare
@@ -2068,7 +2072,7 @@ modules.order: $(build-dir)
# KBUILD_MODPOST_NOFINAL can be set to skip the final link of modules.
# This is solely useful to speed up test compiles.
modules: modpost
-ifneq ($(KBUILD_MODPOST_NOFINAL),1)
+ifneq ($(CONFIG_MODULE_HASHES)|$(KBUILD_MODPOST_NOFINAL),|1)
$(Q)$(MAKE) -f $(srctree)/scripts/Makefile.modfinal
endif
@@ -2162,6 +2166,7 @@ clean: $(clean-dirs)
-o -name '*.c.[012]*.*' \
-o -name '*.ll' \
-o -name '*.gcno' \
+ -o -name '*.merkle' \
\) -type f -print \
-o -name '.tmp_*' -print \
| xargs rm -rf
diff --git a/include/asm-generic/vmlinux.lds.h b/include/asm-generic/vmlinux.lds.h
index 60c8c22fd3e4..661881e5ef96 100644
--- a/include/asm-generic/vmlinux.lds.h
+++ b/include/asm-generic/vmlinux.lds.h
@@ -508,6 +508,8 @@
\
PRINTK_INDEX \
\
+ MODULE_HASHES \
+ \
/* Kernel symbol table */ \
__ksymtab : AT(ADDR(__ksymtab) - LOAD_OFFSET) { \
__start___ksymtab = .; \
@@ -913,6 +915,15 @@
#define PRINTK_INDEX
#endif
+#ifdef CONFIG_MODULE_HASHES
+#define MODULE_HASHES \
+ .module_hashes : AT(ADDR(.module_hashes) - LOAD_OFFSET) { \
+ KEEP(*(SORT(.module_hashes))) \
+ }
+#else
+#define MODULE_HASHES
+#endif
+
/*
* Discard .note.GNU-stack, which is emitted as PROGBITS by the compiler.
* Otherwise, the type of .notes section would become PROGBITS instead of NOTES.
diff --git a/include/linux/module_hashes.h b/include/linux/module_hashes.h
new file mode 100644
index 000000000000..53b34fa12f2d
--- /dev/null
+++ b/include/linux/module_hashes.h
@@ -0,0 +1,29 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#ifndef _LINUX_MODULE_HASHES_H
+#define _LINUX_MODULE_HASHES_H
+
+#include <linux/compiler_attributes.h>
+#include <linux/types.h>
+#include <crypto/sha2.h>
+
+#define __module_hashes_section __section(".module_hashes")
+#define MODULE_HASHES_HASH_SIZE SHA256_DIGEST_SIZE
+
+struct module_hash {
+ u8 h[MODULE_HASHES_HASH_SIZE];
+};
+
+struct module_hashes_proof {
+ __be32 pos;
+ struct module_hash hash_sigs[];
+} __packed;
+
+struct module_hashes_root {
+ u32 levels;
+ struct module_hash hash;
+};
+
+extern const struct module_hashes_root module_hashes_root;
+
+#endif /* _LINUX_MODULE_HASHES_H */
diff --git a/include/uapi/linux/module_signature.h b/include/uapi/linux/module_signature.h
index 634c9f1c8fc2..78e206996eed 100644
--- a/include/uapi/linux/module_signature.h
+++ b/include/uapi/linux/module_signature.h
@@ -16,6 +16,7 @@
enum module_signature_type {
MODULE_SIGNATURE_TYPE_PKCS7 = 2, /* Signature in PKCS#7 message */
+ MODULE_SIGNATURE_TYPE_MERKLE = 3, /* Merkle proof for modules, opaque structure */
};
/*
diff --git a/kernel/module/Kconfig b/kernel/module/Kconfig
index 84297da666ff..acbbda58e7c8 100644
--- a/kernel/module/Kconfig
+++ b/kernel/module/Kconfig
@@ -272,7 +272,7 @@ config MODULE_SIG
inclusion into an initramfs that wants the module size reduced.
config MODULE_AUTH
- def_bool MODULE_SIG
+ def_bool MODULE_SIG || MODULE_HASHES
config MODULE_SIG_FORCE
bool "Require modules to be validly signed"
@@ -291,7 +291,7 @@ config MODULE_SIG_ALL
modules must be signed manually, using the scripts/sign-file tool.
comment "Do not forget to sign required modules with scripts/sign-file"
- depends on MODULE_SIG_FORCE && !MODULE_SIG_ALL
+ depends on MODULE_SIG_FORCE && !MODULE_SIG_ALL && !MODULE_HASHES
choice
prompt "Hash algorithm to sign modules"
@@ -406,6 +406,23 @@ config MODULE_DECOMPRESS
endif # MODULE_COMPRESS
+config MODULE_HASHES
+ bool "Hash-based module authentication"
+ depends on !MODULE_SIG_ALL
+ depends on !IMA_APPRAISE_MODSIG
+ select MODULE_SIG_FORMAT
+ select CRYPTO_LIB_SHA256
+ help
+ Validate modules by their hashes.
+ Only modules built together with the main kernel image can be
+ validated that way.
+
+ This is a reproducible-build compatible alternative to a build-time
+ generated module keyring, as enabled by
+ CONFIG_MODULE_SIG_KEY=certs/signing_key.pem.
+
+ Also see the warning in MODULE_SIG about stripping modules.
+
config MODULE_ALLOW_MISSING_NAMESPACE_IMPORTS
bool "Allow loading of modules with missing namespace imports"
help
diff --git a/kernel/module/Makefile b/kernel/module/Makefile
index c7200e293d04..da9420f140e9 100644
--- a/kernel/module/Makefile
+++ b/kernel/module/Makefile
@@ -26,3 +26,4 @@ obj-$(CONFIG_KGDB_KDB) += kdb.o
obj-$(CONFIG_MODVERSIONS) += version.o
obj-$(CONFIG_MODULE_UNLOAD_TAINT_TRACKING) += tracking.o
obj-$(CONFIG_MODULE_STATS) += stats.o
+obj-$(CONFIG_MODULE_HASHES) += hashes.o hashes_root.o
diff --git a/kernel/module/auth.c b/kernel/module/auth.c
index 2ee512d26790..cf3fe3f8bd89 100644
--- a/kernel/module/auth.c
+++ b/kernel/module/auth.c
@@ -42,6 +42,9 @@ static __always_inline bool mod_sig_type_valid(enum module_signature_type id_typ
if (id_type == MODULE_SIGNATURE_TYPE_PKCS7 && IS_ENABLED(CONFIG_MODULE_SIG))
return true;
+ if (id_type == MODULE_SIGNATURE_TYPE_MERKLE && IS_ENABLED(CONFIG_MODULE_HASHES))
+ return true;
+
return false;
}
@@ -72,6 +75,9 @@ static int mod_verify_sig(const void *mod, struct load_info *info)
if (ms.id_type == MODULE_SIGNATURE_TYPE_PKCS7 && IS_ENABLED(CONFIG_MODULE_SIG))
return module_sig_check(mod, modlen, mod + modlen, sig_len);
+ if (ms.id_type == MODULE_SIGNATURE_TYPE_MERKLE && IS_ENABLED(CONFIG_MODULE_HASHES))
+ return module_hash_check(mod, modlen, mod + modlen, sig_len);
+
return 0;
}
diff --git a/kernel/module/hashes.c b/kernel/module/hashes.c
new file mode 100644
index 000000000000..3d3cf0366f75
--- /dev/null
+++ b/kernel/module/hashes.c
@@ -0,0 +1,95 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/* Module hash-based integrity checker
+ *
+ * Copyright (C) 2025 Thomas Weißschuh <linux@weissschuh.net>
+ * Copyright (C) 2025 Sebastian Andrzej Siewior <sebastian@breakpoint.cc>
+ *
+ * The structure of the Merkle tree is documented in scripts/modules-merkle-tree.c.
+ */
+
+#define pr_fmt(fmt) "module/hash: " fmt
+
+#include <linux/module_hashes.h>
+#include <linux/module.h>
+#include <linux/unaligned.h>
+
+#include <crypto/sha2.h>
+
+#include "internal.h"
+
+static __init __maybe_unused int module_hashes_init(void)
+{
+ pr_debug("root: levels=%u hash=%*phN\n",
+ module_hashes_root.levels,
+ (int)sizeof(module_hashes_root.hash), &module_hashes_root.hash);
+
+ return 0;
+}
+
+#if IS_ENABLED(CONFIG_MODULE_DEBUG)
+early_initcall(module_hashes_init);
+#endif
+
+static void hash_entry(const struct module_hash *left, const struct module_hash *right,
+ struct module_hash *out)
+{
+ struct sha256_ctx ctx;
+ u8 magic = 0x02;
+
+ sha256_init(&ctx);
+ sha256_update(&ctx, &magic, sizeof(magic));
+ sha256_update(&ctx, left->h, sizeof(left->h));
+ sha256_update(&ctx, right->h, sizeof(right->h));
+ sha256_final(&ctx, out->h);
+}
+
+static void hash_data(const u8 *d, size_t len, unsigned int pos, struct module_hash *out)
+{
+ struct sha256_ctx ctx;
+ u8 magic = 0x01;
+ __be32 pos_be;
+
+ pos_be = cpu_to_be32(pos);
+
+ sha256_init(&ctx);
+ sha256_update(&ctx, &magic, sizeof(magic));
+ sha256_update(&ctx, (const u8 *)&pos_be, sizeof(pos_be));
+ sha256_update(&ctx, d, len);
+ sha256_final(&ctx, out->h);
+}
+
+static bool module_hashes_verify_proof(u32 pos, const struct module_hash *hash_sigs,
+ struct module_hash *cur)
+{
+ for (unsigned int i = 0; i < module_hashes_root.levels; i++, pos >>= 1) {
+ if ((pos & 1) == 0)
+ hash_entry(cur, &hash_sigs[i], cur);
+ else
+ hash_entry(&hash_sigs[i], cur, cur);
+ }
+
+ return !memcmp(cur, &module_hashes_root.hash, sizeof(module_hashes_root.hash));
+}
+
+int module_hash_check(const void *mod, size_t mod_len, const void *sig, size_t sig_len)
+{
+ const struct module_hashes_proof *proof;
+ struct module_hash modhash;
+ size_t proof_size;
+ u32 pos;
+
+ proof_size = struct_size(proof, hash_sigs, module_hashes_root.levels);
+
+ if (sig_len != proof_size)
+ return -ENOPKG;
+
+ proof = (const struct module_hashes_proof *)sig;
+ pos = get_unaligned_be32(&proof->pos);
+
+ hash_data(mod, mod_len, pos, &modhash);
+
+ if (!module_hashes_verify_proof(pos, proof->hash_sigs, &modhash))
+ return -ENOKEY;
+
+ return 0;
+}
diff --git a/kernel/module/hashes_root.c b/kernel/module/hashes_root.c
new file mode 100644
index 000000000000..ffb6adfc2193
--- /dev/null
+++ b/kernel/module/hashes_root.c
@@ -0,0 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include <linux/module_hashes.h>
+
+/* Blank dummy data. Will be replaced by the read data during the build */
+const struct module_hashes_root module_hashes_root __module_hashes_section = {};
diff --git a/kernel/module/internal.h b/kernel/module/internal.h
index aabe7f8e1af4..259e8ca5cb25 100644
--- a/kernel/module/internal.h
+++ b/kernel/module/internal.h
@@ -336,6 +336,7 @@ void module_mark_ro_after_init(const Elf_Ehdr *hdr, Elf_Shdr *sechdrs,
const char *secstrings);
int module_sig_check(const void *mod, size_t mod_len, const void *sig, size_t sig_len);
+int module_hash_check(const void *mod, size_t mod_len, const void *sig, size_t sig_len);
#ifdef CONFIG_MODULE_AUTH
int module_auth_check(struct load_info *info, int flags);
diff --git a/scripts/.gitignore b/scripts/.gitignore
index 4215c2208f7e..8dad9b0d3b2d 100644
--- a/scripts/.gitignore
+++ b/scripts/.gitignore
@@ -5,6 +5,7 @@
/insert-sys-cert
/kallsyms
/module.lds
+/modules-merkle-tree
/recordmcount
/rustdoc_test_builder
/rustdoc_test_gen
diff --git a/scripts/Makefile b/scripts/Makefile
index c983e09be78c..b6291595d9e8 100644
--- a/scripts/Makefile
+++ b/scripts/Makefile
@@ -11,6 +11,7 @@ hostprogs-always-y += sign-file
hostprogs-always-$(CONFIG_SYSTEM_EXTRA_CERTIFICATE) += insert-sys-cert
hostprogs-always-$(CONFIG_RUST_KERNEL_DOCTESTS) += rustdoc_test_builder
hostprogs-always-$(CONFIG_RUST_KERNEL_DOCTESTS) += rustdoc_test_gen
+hostprogs-always-$(CONFIG_MODULE_HASHES) += modules-merkle-tree
hostprogs-always-$(CONFIG_TRACEPOINTS) += tracepoint-update
sorttable-objs := sorttable.o elf-parse.o
@@ -37,6 +38,9 @@ HOSTCFLAGS_asn1_compiler.o = -I$(srctree)/include
HOSTCFLAGS_sign-file.o = $(shell $(HOSTPKG_CONFIG) --cflags libcrypto 2> /dev/null)
HOSTCFLAGS_sign-file.o += -I$(srctree)/tools/include/uapi/
HOSTLDLIBS_sign-file = $(shell $(HOSTPKG_CONFIG) --libs libcrypto 2> /dev/null || echo -lcrypto)
+HOSTCFLAGS_modules-merkle-tree.o = $(shell $(HOSTPKG_CONFIG) --cflags libcrypto 2> /dev/null)
+HOSTCFLAGS_modules-merkle-tree.o += -I$(srctree)/tools/include/uapi/
+HOSTLDLIBS_modules-merkle-tree = $(shell $(HOSTPKG_CONFIG) --libs libcrypto 2> /dev/null || echo -lcrypto)
ifdef CONFIG_UNWINDER_ORC
ifeq ($(ARCH),x86_64)
diff --git a/scripts/Makefile.modinst b/scripts/Makefile.modinst
index 9ba45e5b32b1..68708a039a62 100644
--- a/scripts/Makefile.modinst
+++ b/scripts/Makefile.modinst
@@ -79,6 +79,12 @@ quiet_cmd_install = INSTALL $@
# as the options to the strip command.
ifdef INSTALL_MOD_STRIP
+ifdef CONFIG_MODULE_HASHES
+ifeq ($(KBUILD_EXTMOD),)
+$(error CONFIG_MODULE_HASHES and INSTALL_MOD_STRIP are mutually exclusive)
+endif
+endif
+
ifeq ($(INSTALL_MOD_STRIP),1)
strip-option := --strip-debug
else
@@ -116,6 +122,11 @@ quiet_cmd_sign :=
cmd_sign := :
endif
+ifeq ($(KBUILD_EXTMOD)|$(CONFIG_MODULE_HASHES),|y)
+quiet_cmd_sign = MERKLE [M] $@
+ cmd_sign = cat $(objtree)/$*.merkle >> $@
+endif
+
# Create necessary directories
$(foreach dir, $(sort $(dir $(install-y))), $(shell mkdir -p $(dir)))
diff --git a/scripts/Makefile.vmlinux b/scripts/Makefile.vmlinux
index 6cc661e5292b..a0332c06bde1 100644
--- a/scripts/Makefile.vmlinux
+++ b/scripts/Makefile.vmlinux
@@ -78,6 +78,33 @@ ifdef CONFIG_BUILDTIME_TABLE_SORT
vmlinux.unstripped: scripts/sorttable
endif
+ifdef CONFIG_MODULE_HASHES
+targets += .tmp_module_hashes.c
+
+modules.order: vmlinux.unstripped FORCE
+ $(Q)echo " MAKE modules"
+ $(Q)$(MAKE) -f $(srctree)/Makefile modules
+
+quiet_cmd_modules_merkle_tree = MERKLE $@
+ cmd_modules_merkle_tree = $< $@ .ko
+
+targets += .tmp_module_hashes.c
+.tmp_module_hashes.c: $(objtree)/scripts/modules-merkle-tree modules.order FORCE
+ $(call if_changed,modules_merkle_tree)
+
+targets += .tmp_module_hashes.o
+.tmp_module_hashes.o: .tmp_module_hashes.c FORCE
+
+quiet_cmd_modules_merkle_tree_root = GEN $@
+ cmd_modules_merkle_tree_root = $(OBJCOPY) --dump-section .module_hashes=$@ $<
+
+targets += .tmp_module_hashes.bin
+.tmp_module_hashes.bin: .tmp_module_hashes.o FORCE
+ $(call if_changed,modules_merkle_tree_root)
+
+vmlinux: .tmp_module_hashes.bin
+endif
+
# vmlinux
# ---------------------------------------------------------------------------
@@ -95,6 +122,11 @@ quiet_cmd_objcopy_vmlinux = OBJCOPY $@
cmd_objcopy_vmlinux = $(OBJCOPY) $(patsubst %,--set-section-flags %=noload,$(remove-section-y)) $< $@; \
$(OBJCOPY) $(addprefix --remove-section=,$(remove-section-y)) $(remove-symbols) $@
+ifdef CONFIG_MODULE_HASHES
+# Patch module hashes root into vmlinux after modules have been built.
+ cmd_objcopy_vmlinux += ; $(OBJCOPY) --update-section .module_hashes=.tmp_module_hashes.bin $@
+endif
+
targets += vmlinux
vmlinux: vmlinux.unstripped FORCE
$(call if_changed,objcopy_vmlinux)
diff --git a/scripts/include/xalloc.h b/scripts/include/xalloc.h
index cdadb07d0592..8294bc0b836f 100644
--- a/scripts/include/xalloc.h
+++ b/scripts/include/xalloc.h
@@ -3,6 +3,7 @@
#ifndef XALLOC_H
#define XALLOC_H
+#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
@@ -50,4 +51,32 @@ static inline char *xstrndup(const char *s, size_t n)
return p;
}
+static inline void *xreallocarray(void *oldp, size_t n, size_t size)
+{
+ void *p;
+
+ p = reallocarray(oldp, n, size);
+ if (!p)
+ exit(1);
+
+ return p;
+}
+
+#ifdef _GNU_SOURCE
+static inline char *xasprintf(const char *fmt, ...)
+{
+ va_list ap;
+ char *strp;
+ int ret;
+
+ va_start(ap, fmt);
+ ret = vasprintf(&strp, fmt, ap);
+ va_end(ap);
+ if (ret == -1)
+ exit(1);
+
+ return strp;
+}
+#endif /* _GNU_SOURCE */
+
#endif /* XALLOC_H */
diff --git a/scripts/link-vmlinux.sh b/scripts/link-vmlinux.sh
index f99e196abeea..0edec5ee34dc 100755
--- a/scripts/link-vmlinux.sh
+++ b/scripts/link-vmlinux.sh
@@ -103,7 +103,7 @@ vmlinux_link()
${ld} ${ldflags} -o ${output} \
${wl}--whole-archive ${objs} ${wl}--no-whole-archive \
${wl}--start-group ${libs} ${wl}--end-group \
- ${kallsymso} ${btf_vmlinux_bin_o} ${arch_vmlinux_o} ${ldlibs}
+ ${kallsymso} ${btf_vmlinux_bin_o} ${module_hashes_o} ${arch_vmlinux_o} ${ldlibs}
}
# Create ${2}.o file with all symbols from the ${1} object file
@@ -183,6 +183,7 @@ fi
btf_vmlinux_bin_o=
btfids_vmlinux=
kallsymso=
+module_hashes_o=
strip_debug=
generate_map=
diff --git a/scripts/modules-merkle-tree.c b/scripts/modules-merkle-tree.c
new file mode 100644
index 000000000000..10e3455d5d7a
--- /dev/null
+++ b/scripts/modules-merkle-tree.c
@@ -0,0 +1,416 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Compute hashes for modules files and build a merkle tree.
+ *
+ * Copyright (C) 2025 Sebastian Andrzej Siewior <sebastian@breakpoint.cc>
+ * Copyright (C) 2025 Thomas Weißschuh <linux@weissschuh.net>
+ *
+ * Structure of the Merkle tree:
+ *
+ * The full built modules are leaf nodes. They are hashed pairwise in the order
+ * of modules.order to create internal nodes. These in turn are also hashed
+ * pairwise to create the next higher level of internal nodes. This is repeated
+ * up to a single root node. In case of an uneven amount of node on a level, the
+ * last node is paired with itself.
+ *
+ * The single root node can then be embedded into vmlinux to validate all modules.
+ */
+
+#define _GNU_SOURCE 1
+#include <endian.h>
+#include <err.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdbool.h>
+#include <stdlib.h>
+
+#include <sys/stat.h>
+#include <sys/mman.h>
+
+#include <openssl/evp.h>
+#include <openssl/err.h>
+
+#include "ssl-common.h"
+
+#include <linux/module_signature.h>
+#include <xalloc.h>
+
+static int hash_size;
+static EVP_MD_CTX *ctx;
+
+struct hash {
+ uint8_t h[32]; /* For sha256 */
+};
+
+struct file_entry {
+ char *name;
+ unsigned int pos;
+ struct hash hash;
+};
+
+static struct file_entry *fh_list;
+static size_t num_files;
+
+struct mtree {
+ struct hash **level_hashes;
+ unsigned int *entries;
+ unsigned int num_levels;
+};
+
+static unsigned int log2_roundup(uint32_t val)
+{
+ if (val <= 1)
+ return 1;
+ return 32 - __builtin_clz(val - 1);
+}
+
+static void hash_data(unsigned int pos, unsigned char *data, size_t size, struct hash *ret_hash)
+{
+ uint8_t magic = 0x01; /* domain separation prefix */
+ uint32_t pos_be;
+
+ pos_be = htobe32(pos);
+
+ ERR(EVP_DigestInit_ex(ctx, NULL, NULL) != 1, "EVP_DigestInit_ex()");
+ ERR(EVP_DigestUpdate(ctx, &magic, sizeof(magic)) != 1, "EVP_DigestUpdate(magic)");
+ ERR(EVP_DigestUpdate(ctx, &pos_be, sizeof(pos_be)) != 1, "EVP_DigestUpdate(pos)");
+ ERR(EVP_DigestUpdate(ctx, data, size) != 1, "EVP_DigestUpdate(data)");
+ ERR(EVP_DigestFinal_ex(ctx, ret_hash->h, NULL) != 1, "EVP_DigestFinal_ex()");
+}
+
+static void hash_entry(const struct hash *left, const struct hash *right, struct hash *ret_hash)
+{
+ uint8_t magic = 0x02; /* domain separation prefix */
+
+ ERR(EVP_DigestInit_ex(ctx, NULL, NULL) != 1, "EVP_DigestInit_ex()");
+ ERR(EVP_DigestUpdate(ctx, &magic, sizeof(magic)) != 1, "EVP_DigestUpdate(magic)");
+ ERR(EVP_DigestUpdate(ctx, left, hash_size) != 1, "EVP_DigestUpdate(left)");
+ ERR(EVP_DigestUpdate(ctx, right, hash_size) != 1, "EVP_DigestUpdate(right)");
+ ERR(EVP_DigestFinal_ex(ctx, ret_hash->h, NULL) != 1, "EVP_DigestFinal_ex()");
+}
+
+static void hash_file(struct file_entry *fe)
+{
+ unsigned char *mem;
+ struct stat sb;
+ int fd, ret;
+
+ fd = open(fe->name, O_RDONLY);
+ if (fd < 0)
+ err(1, "Failed to open %s", fe->name);
+
+ ret = fstat(fd, &sb);
+ if (ret)
+ err(1, "Failed to stat %s", fe->name);
+
+ mem = mmap(NULL, sb.st_size, PROT_READ, MAP_SHARED, fd, 0);
+ if (mem == MAP_FAILED)
+ err(1, "Failed to mmap %s", fe->name);
+
+ hash_data(fe->pos, mem, sb.st_size, &fe->hash);
+
+ munmap(mem, sb.st_size);
+ close(fd);
+}
+
+static struct mtree *build_merkle(struct file_entry *files, size_t num_files)
+{
+ unsigned int num_cur_le, num_prev_le;
+ struct mtree *mt;
+
+ if (!num_files)
+ return NULL;
+
+ mt = xmalloc(sizeof(*mt));
+ mt->num_levels = log2_roundup(num_files);
+
+ mt->level_hashes = xcalloc(sizeof(*mt->level_hashes), mt->num_levels);
+
+ mt->entries = xcalloc(sizeof(*mt->entries), mt->num_levels);
+ num_cur_le = (num_files + 1) / 2;
+ mt->entries[0] = num_cur_le;
+ mt->level_hashes[0] = xcalloc(sizeof(**mt->level_hashes), num_cur_le);
+
+ /* First level of pairs */
+ for (size_t i = 0; i < num_files; i += 2) {
+ /* Hash the pair, or the last file with itself if it's odd. */
+ const struct hash *right = i + 1 < num_files ? &files[i + 1].hash : &files[i].hash;
+
+ hash_entry(&files[i].hash, right, &mt->level_hashes[0][i / 2]);
+ }
+
+ for (unsigned int i = 1; i < mt->num_levels; i++) {
+ num_prev_le = num_cur_le;
+
+ num_cur_le = (num_prev_le + 1) / 2;
+ mt->entries[i] = num_cur_le;
+ mt->level_hashes[i] = xcalloc(sizeof(**mt->level_hashes), num_cur_le);
+
+ for (unsigned int n = 0; n < num_prev_le; n += 2) {
+ /* Hash the pair, or the last with itself if it's odd. */
+ const struct hash *right = n + 1 < num_prev_le ?
+ &mt->level_hashes[i - 1][n + 1] :
+ &mt->level_hashes[i - 1][n];
+ hash_entry(&mt->level_hashes[i - 1][n], right,
+ &mt->level_hashes[i][n / 2]);
+ }
+ }
+
+ /* FIXME assert single hash in root */
+
+ return mt;
+}
+
+static void free_mtree(struct mtree *mt)
+{
+ if (!mt)
+ return;
+
+ for (unsigned int i = 0; i < mt->num_levels; i++)
+ free(mt->level_hashes[i]);
+
+ free(mt->level_hashes);
+ free(mt->entries);
+ free(mt);
+}
+
+static void write_be_int(int fd, unsigned int v)
+{
+ unsigned int be_val = htobe32(v);
+
+ if (write(fd, &be_val, sizeof(be_val)) != sizeof(be_val))
+ err(1, "Failed writing to file");
+}
+
+static void write_hash(int fd, const struct hash *hash)
+{
+ if (write(fd, hash->h, hash_size) != hash_size)
+ err(1, "Failed writing to file");
+}
+
+static void build_proof(struct mtree *mt, unsigned int n, int fd)
+{
+ struct file_entry *fe, *fe_sib;
+
+ fe = &fh_list[n];
+
+ if ((n & 1) == 0) {
+ /* No pair, hash with itself */
+ if (n + 1 == num_files)
+ fe_sib = fe;
+ else
+ fe_sib = &fh_list[n + 1];
+ } else {
+ fe_sib = &fh_list[n - 1];
+ }
+ /* First comes the node position into the file */
+ write_be_int(fd, n);
+
+ /* Next is the sibling hash, followed by hashes in the tree */
+ write_hash(fd, &fe_sib->hash);
+
+ for (unsigned int i = 0; i < mt->num_levels - 1; i++) {
+ n >>= 1;
+ if ((n & 1) == 0) {
+ const struct hash *h;
+
+ /* No pair, hash with itself */
+ if (n + 1 == mt->entries[i])
+ h = &mt->level_hashes[i][n];
+ else
+ h = &mt->level_hashes[i][n + 1];
+
+ write_hash(fd, h);
+ } else {
+ write_hash(fd, &mt->level_hashes[i][n - 1]);
+ }
+ }
+}
+
+static void append_module_signature_magic(int fd, unsigned int sig_len)
+{
+ const struct module_signature sig_info = {
+ .id_type = MODULE_SIGNATURE_TYPE_MERKLE,
+ .sig_len = htobe32(sig_len),
+ };
+ const size_t sig_str_len = sizeof(MODULE_SIGNATURE_MARKER) - 1;
+ const char *sig_str = MODULE_SIGNATURE_MARKER;
+
+ if (write(fd, &sig_info, sizeof(sig_info)) != sizeof(sig_info))
+ err(1, "write(sig_info) failed");
+
+ if (write(fd, sig_str, sig_str_len) != sig_str_len)
+ err(1, "write(magic_number) failed");
+}
+
+static void write_merkle_root(struct mtree *mt, const char *filename)
+{
+ unsigned int num_levels;
+ struct hash *h;
+ FILE *f;
+
+ if (mt) {
+ num_levels = mt->num_levels;
+ h = &mt->level_hashes[mt->num_levels - 1][0];
+ } else {
+ num_levels = 0;
+ h = xcalloc(1, hash_size);
+ }
+
+ f = fopen(filename, "w");
+ if (!f)
+ err(1, "Failed to create %s", filename);
+
+ fprintf(f, "#include <linux/module_hashes.h>\n\n");
+ fprintf(f, "const struct\n");
+ fprintf(f, "module_hashes_root module_hashes_root __module_hashes_section = {\n");
+
+ fprintf(f, "\t.levels = %u,\n", num_levels);
+ fprintf(f, "\t.hash = {{");
+ for (unsigned int i = 0; i < hash_size; i++) {
+ char *space = "";
+
+ if (!(i % 8))
+ fprintf(f, "\n\t\t");
+
+ if ((i + 1) % 8)
+ space = " ";
+
+ fprintf(f, "0x%02x,%s", h->h[i], space);
+ }
+ fprintf(f, "\n\t}},");
+
+ fprintf(f, "\n};\n");
+
+ if (fclose(f))
+ err(1, "Failed to write %s", filename);
+
+ if (!mt)
+ free(h);
+}
+
+static char *xstrdup_replace_suffix(const char *str, const char *old_suffix, const char *new_suffix)
+{
+ size_t str_len, old_suffix_len, base_len;
+
+ str_len = strlen(str);
+ old_suffix_len = strlen(old_suffix);
+ base_len = str_len - old_suffix_len;
+
+ if (old_suffix_len > str_len || memcmp(str + base_len, old_suffix, old_suffix_len) != 0)
+ errx(1, "'%s' does not end in '%s'", str, old_suffix);
+
+ return xasprintf("%.*s%s", (int)base_len, str, new_suffix);
+}
+
+static void trim_newline(char *line)
+{
+ size_t len;
+
+ if (!line)
+ return;
+
+ len = strlen(line);
+ if (!len)
+ return;
+
+ if (line[len - 1] == '\n')
+ line[len - 1] = '\0';
+}
+
+static void read_modules_order(const char *fname, const char *suffix)
+{
+ char line[PATH_MAX];
+ FILE *in;
+
+ in = fopen(fname, "r");
+ if (!in)
+ err(1, "Failed to open %s", fname);
+
+ while (fgets(line, PATH_MAX, in)) {
+ struct file_entry *entry;
+
+ trim_newline(line);
+
+ fh_list = xreallocarray(fh_list, num_files + 1, sizeof(*fh_list));
+ entry = &fh_list[num_files];
+
+ entry->pos = num_files;
+ entry->name = xstrdup_replace_suffix(line, ".o", suffix);
+ hash_file(entry);
+
+ num_files++;
+ }
+
+ if (ferror(in))
+ errx(1, "Failed to read %s", fname);
+
+ fclose(in);
+}
+
+static __attribute__((noreturn))
+void usage(void)
+{
+ fprintf(stderr,
+ "Usage: scripts/modules-merkle-tree <kmod suffix> <root definition>\n");
+ exit(2);
+}
+
+int main(int argc, char *argv[])
+{
+ const char *kmod_suffix;
+ const EVP_MD *hash_evp;
+ struct mtree *mt;
+
+ if (argc != 3)
+ usage();
+
+ kmod_suffix = argv[2];
+
+ hash_evp = EVP_sha256();
+ ERR(!hash_evp, "EVP_sha256()");
+
+ ctx = EVP_MD_CTX_new();
+ ERR(!ctx, "EVP_MD_CTX_new()");
+
+ hash_size = EVP_MD_get_size(hash_evp);
+ ERR(hash_size <= 0, "EVP_get_digestbyname");
+
+ if (hash_size != sizeof(struct hash))
+ errx(1, "Invalid hash size");
+
+ if (EVP_DigestInit_ex(ctx, hash_evp, NULL) != 1)
+ ERR(1, "EVP_DigestInit_ex()");
+
+ read_modules_order("modules.order", kmod_suffix);
+
+ mt = build_merkle(fh_list, num_files);
+ write_merkle_root(mt, argv[1]);
+ for (size_t i = 0; i < num_files; i++) {
+ char *signame;
+ int fd;
+
+ signame = xstrdup_replace_suffix(fh_list[i].name, kmod_suffix, ".merkle");
+
+ fd = open(signame, O_WRONLY | O_CREAT | O_TRUNC, 0644);
+ if (fd < 0)
+ err(1, "Can't create %s", signame);
+
+ build_proof(mt, i, fd);
+ append_module_signature_magic(fd, lseek(fd, 0, SEEK_CUR));
+ if (close(fd))
+ err(1, "Can't write %s", signame);
+ }
+
+ free_mtree(mt);
+ for (size_t i = 0; i < num_files; i++)
+ free(fh_list[i].name);
+ free(fh_list);
+
+ EVP_MD_CTX_free(ctx);
+ return 0;
+}
diff --git a/security/lockdown/Kconfig b/security/lockdown/Kconfig
index 155959205b8e..60b240e3ef1f 100644
--- a/security/lockdown/Kconfig
+++ b/security/lockdown/Kconfig
@@ -1,7 +1,7 @@
config SECURITY_LOCKDOWN_LSM
bool "Basic module for enforcing kernel lockdown"
depends on SECURITY
- depends on !MODULES || MODULE_SIG
+ depends on !MODULES || MODULE_SIG || MODULE_HASHES
help
Build support for an LSM that enforces a coarse kernel lockdown
behaviour.
diff --git a/tools/include/uapi/linux/module_signature.h b/tools/include/uapi/linux/module_signature.h
index 634c9f1c8fc2..78e206996eed 100644
--- a/tools/include/uapi/linux/module_signature.h
+++ b/tools/include/uapi/linux/module_signature.h
@@ -16,6 +16,7 @@
enum module_signature_type {
MODULE_SIGNATURE_TYPE_PKCS7 = 2, /* Signature in PKCS#7 message */
+ MODULE_SIGNATURE_TYPE_MERKLE = 3, /* Merkle proof for modules, opaque structure */
};
/*
--
2.54.0
^ permalink raw reply related
* [PATCH v5 14/14] kbuild: make CONFIG_MODULE_HASHES compatible with module stripping
From: Thomas Weißschuh @ 2026-05-05 9:05 UTC (permalink / raw)
To: Alexei Starovoitov, Daniel Borkmann, Andrii Nakryiko,
Eduard Zingerman, Kumar Kartikeya Dwivedi, Nathan Chancellor,
Nicolas Schier, Arnd Bergmann, Luis Chamberlain, Petr Pavlu,
Sami Tolvanen, Daniel Gomez, Paul Moore, James Morris,
Serge E. Hallyn, Jonathan Corbet, Madhavan Srinivasan,
Michael Ellerman, Nicholas Piggin, Naveen N Rao, Mimi Zohar,
Roberto Sassu, Dmitry Kasatkin, Eric Snowberg, Nicolas Schier,
Daniel Gomez, Aaron Tomlin, Christophe Leroy (CS GROUP),
Nicolas Bouchinet, Xiu Jianfeng, Christophe Leroy
Cc: Martin KaFai Lau, Song Liu, Yonghong Song, Jiri Olsa, bpf,
Fabian Grünbichler, Arnout Engelen, Mattia Rizzolo, kpcyrd,
Christian Heusel, Câju Mihai-Drosi, Eric Biggers,
Sebastian Andrzej Siewior, linux-kbuild, linux-kernel, linux-arch,
linux-modules, linux-security-module, linux-doc, linuxppc-dev,
linux-integrity, debian-kernel, Thomas Weißschuh
In-Reply-To: <20260505-module-hashes-v5-0-e174a5a49fce@weissschuh.net>
CONFIG_MODULE_HASHES needs to process the modules at build time in the
exact form they will be loaded at runtime. If the modules are stripped
afterwards they will not be loadable anymore.
Also evaluate INSTALL_MOD_STRIP at build time and build the hashes based
on modules stripped this way.
If users specify inconsistent values of INSTALL_MOD_STRIP between build
and installation time, an error is reported.
Signed-off-by: Thomas Weißschuh <linux@weissschuh.net>
---
.gitignore | 1 +
kernel/module/Kconfig | 5 +++++
scripts/Makefile.modfinal | 9 +++++++++
scripts/Makefile.modinst | 4 ++--
scripts/Makefile.vmlinux | 2 +-
5 files changed, 18 insertions(+), 3 deletions(-)
diff --git a/.gitignore b/.gitignore
index 78cf799401e6..6ce10623c5a3 100644
--- a/.gitignore
+++ b/.gitignore
@@ -30,6 +30,7 @@
*.gz
*.i
*.ko
+*.ko.stripped
*.lex.c
*.ll
*.lst
diff --git a/kernel/module/Kconfig b/kernel/module/Kconfig
index acbbda58e7c8..48be498a4452 100644
--- a/kernel/module/Kconfig
+++ b/kernel/module/Kconfig
@@ -423,6 +423,11 @@ config MODULE_HASHES
Also see the warning in MODULE_SIG about stripping modules.
+# To validate the consistency of INSTALL_MOD_STRIP for MODULE_HASHES
+config MODULE_INSTALL_STRIP
+ string
+ default "$(INSTALL_MOD_STRIP)"
+
config MODULE_ALLOW_MISSING_NAMESPACE_IMPORTS
bool "Allow loading of modules with missing namespace imports"
help
diff --git a/scripts/Makefile.modfinal b/scripts/Makefile.modfinal
index 44a382689a5a..9924a7bb73c5 100644
--- a/scripts/Makefile.modfinal
+++ b/scripts/Makefile.modfinal
@@ -64,7 +64,16 @@ ifdef CONFIG_DEBUG_INFO_BTF_MODULES
endif
+$(call cmd,check_tracepoint)
+%.ko.stripped: %.ko $(wildcard include/config/MODULE_INSTALL_STRIP)
+ $(call cmd,install_mod)
+ $(call cmd,strip_mod)
+
+ifneq ($(CONFIG_MODULE_INSTALL_STRIP),)
+modules.order: $(modules:%.o=%.ko.stripped)
+endif
+
targets += $(modules:%.o=%.ko) $(modules:%.o=%.mod.o) .module-common.o
+targets += $(modules:%.o=%.ko.stripped)
# Update modules.order when a module is (re-)built.
# Allow using it as target dependency.
diff --git a/scripts/Makefile.modinst b/scripts/Makefile.modinst
index b95f613e23c8..fd1fb89bb0bd 100644
--- a/scripts/Makefile.modinst
+++ b/scripts/Makefile.modinst
@@ -68,8 +68,8 @@ __modinst: $(install-y)
ifdef CONFIG_MODULE_HASHES
ifeq ($(KBUILD_EXTMOD),)
-ifdef INSTALL_MOD_STRIP
-$(error CONFIG_MODULE_HASHES and INSTALL_MOD_STRIP are mutually exclusive)
+ifneq ($(INSTALL_MOD_STRIP),$(CONFIG_MODULE_INSTALL_STRIP))
+$(error Inconsistent values for INSTALL_MOD_STRIP between build and installation)
endif
endif
endif
diff --git a/scripts/Makefile.vmlinux b/scripts/Makefile.vmlinux
index a0332c06bde1..a2d170241a2f 100644
--- a/scripts/Makefile.vmlinux
+++ b/scripts/Makefile.vmlinux
@@ -86,7 +86,7 @@ modules.order: vmlinux.unstripped FORCE
$(Q)$(MAKE) -f $(srctree)/Makefile modules
quiet_cmd_modules_merkle_tree = MERKLE $@
- cmd_modules_merkle_tree = $< $@ .ko
+ cmd_modules_merkle_tree = $< $@ $(if $(CONFIG_MODULE_INSTALL_STRIP),.ko.stripped,.ko)
targets += .tmp_module_hashes.c
.tmp_module_hashes.c: $(objtree)/scripts/modules-merkle-tree modules.order FORCE
--
2.54.0
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox