Gentoolto: Trim unused exported kernel symbols (CONFIG_TRIM_UNUSED_KSYMS)

Created on 11 Oct 2019  Â·  19Comments  Â·  Source: InBetweenNames/gentooLTO

I'm tweaking my kernel config, when I ran across this option CONFIG_TRIM_UNUSED_KSYMS.

The title is Trim unused exported kernel symbols with a snippit of the description being:

This option allows for unused exported symbols to be dropped from the build.
In turn, this provides the compiler more opportunities (especially when using LTO) for optimizing the code and reducing binary size.
This might have some security advantages as well.

Anyone have any knowledge about the implications of this option?

Thanks!

Most helpful comment

I'm staring at initial changes for CONFIG_TRIM_UNUSED_KSYMS support, and from what I understand we just need to add required symbols into include/generated/autoksyms.h, which adjusting by the scripts/adjust_autoksyms.sh script during build. So I collected main work into separate script for external modules, and patched autoksyms.h generation logic a bit.

support-trim-unused-ksyms-for-external-modules.patch

--- a/scripts/adjust_autoksyms.sh
+++ b/scripts/adjust_autoksyms.sh
@@ -19,6 +19,7 @@

 cur_ksyms_file="include/generated/autoksyms.h"
 new_ksyms_file="include/generated/autoksyms.h.tmpnew"
+ext_ksyms_file="include/generated/autoksyms.h.extern"

 info() {
        if [ "$quiet" != "silent_" ]; then
@@ -49,7 +50,11 @@
 sed 's/ko$/mod/' modules.order |
 xargs -n1 sed -n -e '2{s/ /\n/g;/^$/!p;}' -- |
 sort -u |
-sed -e 's/\(.*\)/#define __KSYM_\1 1/' >> "$new_ksyms_file"
+sed -e 's/\(.*\)/#define __KSYM_\1 1/' >> "$new_ksyms_file".tmp
+
+# Merge external and new ksym files
+sort -u "$new_ksyms_file".tmp "$ext_ksyms_file" >> "$new_ksyms_file"
+rm -f "$new_ksyms_file".tmp

 # Special case for modversions (see modpost.c)
 if [ -n "$CONFIG_MODVERSIONS" ]; then
@@ -82,7 +87,8 @@
        # Replace the old list with tne new one
        old=$(grep -c "^#define __KSYM_" "$cur_ksyms_file" || true)
        new=$(grep -c "^#define __KSYM_" "$new_ksyms_file" || true)
-       info "KSYMS" "symbols: before=$old, after=$new, changed=$changed"
+       ext=$(grep -c "^#define __KSYM_" "$ext_ksyms_file" || true)
+       info "KSYMS" "symbols: before=$old, after=$new, external=$ext, changed=$changed"
        info "UPD" "$cur_ksyms_file"
        mv -f "$new_ksyms_file" "$cur_ksyms_file"
        # Then trigger a rebuild of affected source files
extern-ksym.sh
#!/bin/sh

#CUR_KERNEL_VER=5.4.8-gentoo
CUR_KERNEL_VER=$(uname -r)

. /usr/src/linux-${CUR_KERNEL_VER}/include/config/auto.conf

list_required_ksyms() {
    nm $1 |
        sed -n 's/^ \+U //p' |
        sed -ns -e '{s/ /\n/g;/^$/!p;}' |
        sort -u |
        while read sym; do
            if [ -n "$CONFIG_HAVE_UNDERSCORE_SYMBOL_PREFIX" ]; then
                sym="${sym#_}"
            fi
            echo "#define __KSYM_${sym} 1"
        done
}


# Find all external modules
mod_dir_list=$(find /lib/modules/${CUR_KERNEL_VER} -mindepth 1 -maxdepth 1 -type d  \! -name 'kernel' | xargs)

for mod_dir in $mod_dir_list; do
    for ko in ${mod_dir}/*.ko; do
        list_required_ksyms ${ko}
    done
done | sort -u

_Here should be a clean-up, because some symbols are defined in neighbour *.ko modules, but no big deal for now. This will propose kernel to include unknown symbols, like KSYM_nvUvmInterfaceRetainChannelResources, which I guess it don't care about._

Test

cd /usr/src/linux
sh /path/to/extern-ksym.sh > include/generated/autoksyms.h.extern
patch -p1 < /path/to/support-trim-unused-ksyms-for-external-modules.patch
make nconfig # enable CONFIG_TRIM_UNUSED_KSYMS
make
# check output:
#  CHK     include/generated/autoksyms.h
#  KSYMS   symbols: before=0, after=2965, external=563, changed=2965
#  UPD     include/generated/autoksyms.h

Result

file | 5.4.8-gentoo | 5.4.8-gentoo.old
-- | -- | --
System.map | 2344152 | 2932044
vmlinuz | 7062064 | 7164464

Note

  • External modules should be built/updated BEFORE kernel update, this mean if you going to update both module and kernel, then you should

    1. update module for current kernel

    2. generate autoksyms.h.extern file

    3. switch to new kernel, build it and rebuild external modules again

  • I guess autoksyms.h.extern should be regenerated by every external module update, and put in an known place.
  • There is a guard currently for external kernel modules, which says "CONFIG_TRIM_UNUSED_KSYMS: should not be set. But it is."
    https://bugs.gentoo.org/591832
    linux-mod.eclass:584

    Status

  • _I'm currently looking for solution to easily override the guard w/o modification of eclass and ebuilds_

  • _Then I'm going to rebuild all external modules and pray reboot._
  • UPD: Hello from CONFIG_TRIM_UNUSED_KSYMS + (NVIDIA + Virtualbox + etc.) modules installed. :wink:

All 19 comments

the risk is that out-of-tree kernel modules, like wireguard and (formerly?)
zfs won't be able to stop stripping of the symbols they need.

also any kernel modules you install as precompiled binaries are at risk of
missing symbols, unless you have other modules that need the same symbols.

for most users I betthe main risk would be nvidia drivers. it would be
interesting to see if they still work, and if not whether there are other
drivers that could be built to preserve the right dependencies (nouveau?).

alternatively there may be a list used for stripping we could add needed
symbols to.

On Fri, Oct 11, 2019, 05:43 Ellison Patterson notifications@github.com
wrote:

I'm tweaking my kernel config, when I ran across this option
CONFIG_TRIM_UNUSED_KSYMS.

The title is Trim unused exported kernel symbols with a snippit of the
description being:

This option allows for unused exported symbols to be dropped from the build. In turn, this provides the compiler more opportunities (especially when using LTO) for optimizing the code and reducing binary size. This might have some security advantages as well.

Anyone have any knowledge about the implications of this option?

Thanks!

—
You are receiving this because you are subscribed to this thread.
Reply to this email directly, view it on GitHub
https://github.com/InBetweenNames/gentooLTO/issues/423?email_source=notifications&email_token=AAAHXYSO4W547YLURRLNRVLQOBYGVA5CNFSM4I7ZJIP2YY3PNVWWK3TUL52HS4DFUVEXG43VMWVGG33NNVSW45C7NFSM4HRGLILA,
or unsubscribe
https://github.com/notifications/unsubscribe-auth/AAAHXYQBYZ4UKPBNXVI7XQTQOBYGVANCNFSM4I7ZJIPQ
.

That was sudden closure. Why close it all of a sudden @ellisonpatterson?

Also, I wonder if the option would affect VirtualBox's modules...

That was sudden closure. Why close it all of a sudden @ellisonpatterson?

Also, I wonder if the option would affect VirtualBox's modules...

It seems to affect various modules (ZFS for my build) and I'm not sure if it's worth pursuing since the feature itself could lead to issues.

CONFIG_TRIM_UNUSED_KSYMS will break all external modules.

If unsure, or if you need to build out-of-tree modules, say N.

You can patch in the module like this for example. The reason ZFS isn't an in-kernel option is due to GPL licensing. For something as small as ZFS it should be simple, I'm not sure not sure how difficult it would be for NVIDIA. Ofc you shouldn't redistribute it if you do decide to do it for yourself.

Hmm... I wonder if VirtualBox modules are affected. Those are the only modules I have, period. Yeah, I build everything in (instead of "M", I select "Y" in menuconfig).

Edit

I just realized that I already said that (regarding VB modules).

The mainline kernel (5.4-rc7) currently has CONFIG_MODULE_ALLOW_MISSING_NAMESPACE_IMPORTS

If MODULE_ALLOW_MISSING_NAMESPACE_IMPORTS is enabled (default=n), the
requirement for modules to import all namespaces that are used by
the module is relaxed.

Enabling this option effectively allows (invalid) modules to be loaded
while only a warning is emitted.

Disabling this option keeps the enforcement at module loading time and
loading is denied if the module's imports are not satisfactory.

It seems as if this option would you allow you to load external modules that are invalid due to their signatures/required symbols being stripped.

Edit: didn't work..for now

@ellisonpatterson Given that the discussion has sparked yet again, maybe you consider reopening the ticket?

@ellisonpatterson Given that the discussion has sparked yet again, maybe you consider reopening the ticket?

Sorry about that, I have reopened it for further discussion (thank you!)

The mainline kernel (5.4-rc7) currently has CONFIG_MODULE_ALLOW_MISSING_NAMESPACE_IMPORTS

If MODULE_ALLOW_MISSING_NAMESPACE_IMPORTS is enabled (default=n), the
requirement for modules to import all namespaces that are used by
the module is relaxed.

Enabling this option effectively allows (invalid) modules to be loaded
while only a warning is emitted.

Disabling this option keeps the enforcement at module loading time and
loading is denied if the module's imports are not satisfactory.

It seems as if this option would you allow you to load external modules that are invalid due to their signatures/required symbols being stripped.

Edit: didn't work..for now

That's an unrelated option. The kernel is cleaning up the module source interface and this option will break ones that haven't been updated. It won't help with external modules that depend on some symbol that got dropped from the kernel though.

Note that the LTO advantage isn't relevant unless you've got a patched kernel that you're compiling with LTO turned on.

I'm staring at initial changes for CONFIG_TRIM_UNUSED_KSYMS support, and from what I understand we just need to add required symbols into include/generated/autoksyms.h, which adjusting by the scripts/adjust_autoksyms.sh script during build. So I collected main work into separate script for external modules, and patched autoksyms.h generation logic a bit.

support-trim-unused-ksyms-for-external-modules.patch

--- a/scripts/adjust_autoksyms.sh
+++ b/scripts/adjust_autoksyms.sh
@@ -19,6 +19,7 @@

 cur_ksyms_file="include/generated/autoksyms.h"
 new_ksyms_file="include/generated/autoksyms.h.tmpnew"
+ext_ksyms_file="include/generated/autoksyms.h.extern"

 info() {
        if [ "$quiet" != "silent_" ]; then
@@ -49,7 +50,11 @@
 sed 's/ko$/mod/' modules.order |
 xargs -n1 sed -n -e '2{s/ /\n/g;/^$/!p;}' -- |
 sort -u |
-sed -e 's/\(.*\)/#define __KSYM_\1 1/' >> "$new_ksyms_file"
+sed -e 's/\(.*\)/#define __KSYM_\1 1/' >> "$new_ksyms_file".tmp
+
+# Merge external and new ksym files
+sort -u "$new_ksyms_file".tmp "$ext_ksyms_file" >> "$new_ksyms_file"
+rm -f "$new_ksyms_file".tmp

 # Special case for modversions (see modpost.c)
 if [ -n "$CONFIG_MODVERSIONS" ]; then
@@ -82,7 +87,8 @@
        # Replace the old list with tne new one
        old=$(grep -c "^#define __KSYM_" "$cur_ksyms_file" || true)
        new=$(grep -c "^#define __KSYM_" "$new_ksyms_file" || true)
-       info "KSYMS" "symbols: before=$old, after=$new, changed=$changed"
+       ext=$(grep -c "^#define __KSYM_" "$ext_ksyms_file" || true)
+       info "KSYMS" "symbols: before=$old, after=$new, external=$ext, changed=$changed"
        info "UPD" "$cur_ksyms_file"
        mv -f "$new_ksyms_file" "$cur_ksyms_file"
        # Then trigger a rebuild of affected source files
extern-ksym.sh
#!/bin/sh

#CUR_KERNEL_VER=5.4.8-gentoo
CUR_KERNEL_VER=$(uname -r)

. /usr/src/linux-${CUR_KERNEL_VER}/include/config/auto.conf

list_required_ksyms() {
    nm $1 |
        sed -n 's/^ \+U //p' |
        sed -ns -e '{s/ /\n/g;/^$/!p;}' |
        sort -u |
        while read sym; do
            if [ -n "$CONFIG_HAVE_UNDERSCORE_SYMBOL_PREFIX" ]; then
                sym="${sym#_}"
            fi
            echo "#define __KSYM_${sym} 1"
        done
}


# Find all external modules
mod_dir_list=$(find /lib/modules/${CUR_KERNEL_VER} -mindepth 1 -maxdepth 1 -type d  \! -name 'kernel' | xargs)

for mod_dir in $mod_dir_list; do
    for ko in ${mod_dir}/*.ko; do
        list_required_ksyms ${ko}
    done
done | sort -u

_Here should be a clean-up, because some symbols are defined in neighbour *.ko modules, but no big deal for now. This will propose kernel to include unknown symbols, like KSYM_nvUvmInterfaceRetainChannelResources, which I guess it don't care about._

Test

cd /usr/src/linux
sh /path/to/extern-ksym.sh > include/generated/autoksyms.h.extern
patch -p1 < /path/to/support-trim-unused-ksyms-for-external-modules.patch
make nconfig # enable CONFIG_TRIM_UNUSED_KSYMS
make
# check output:
#  CHK     include/generated/autoksyms.h
#  KSYMS   symbols: before=0, after=2965, external=563, changed=2965
#  UPD     include/generated/autoksyms.h

Result

file | 5.4.8-gentoo | 5.4.8-gentoo.old
-- | -- | --
System.map | 2344152 | 2932044
vmlinuz | 7062064 | 7164464

Note

  • External modules should be built/updated BEFORE kernel update, this mean if you going to update both module and kernel, then you should

    1. update module for current kernel

    2. generate autoksyms.h.extern file

    3. switch to new kernel, build it and rebuild external modules again

  • I guess autoksyms.h.extern should be regenerated by every external module update, and put in an known place.
  • There is a guard currently for external kernel modules, which says "CONFIG_TRIM_UNUSED_KSYMS: should not be set. But it is."
    https://bugs.gentoo.org/591832
    linux-mod.eclass:584

    Status

  • _I'm currently looking for solution to easily override the guard w/o modification of eclass and ebuilds_

  • _Then I'm going to rebuild all external modules and pray reboot._
  • UPD: Hello from CONFIG_TRIM_UNUSED_KSYMS + (NVIDIA + Virtualbox + etc.) modules installed. :wink:

So, it's hard to fully automate process, but I already built several kernel versions with this, and in general I just copy generated autoksyms.h.extern list into installed kernel's include/generated/ directory, before build.

Some stats:

Size | Version | Notes
-- | -- | --
6959664 | vmlinuz-5.3.10-gentoo |
6963760 | vmlinuz-5.3.11-gentoo |
6992432 | vmlinuz-5.4.0-gentoo |
6984240 | vmlinuz-5.4.1-gentoo |
6984240 | vmlinuz-5.4.2-gentoo |
6988336 | vmlinuz-5.4.3-gentoo |
6988336 | vmlinuz-5.4.5-gentoo |
6988336 | vmlinuz-5.4.6-gentoo |
7160368 | vmlinuz-5.4.7-gentoo | BPF forced by in-kernel Gentoo systemd option (likely)
7164464 | vmlinuz-5.4.8-gentoo.old | Normal build
7062064 | vmlinuz-5.4.8-gentoo.old | Trimmed ksyms
5898800 | vmlinuz-5.4.8-gentoo | Minor modules cleanup (unused VBox, etc.)
5624368 | vmlinuz-5.4.10-gentoo | More cleanups (unused BPF, Perf, Debugfs,etc. are dropped)
5624368 | vmlinuz-5.4.11-gentoo |
5624368 | vmlinuz-5.4.12-gentoo |
5628464 | vmlinuz-5.4.13-gentoo |
5628464 | vmlinuz-5.4.14-gentoo |

Probably placebo effect: very fast content (re)draw in Firefox.

In what unit is the size?

hi i compiled the new kernel 5.5 today and i was not able to compile VirtualBox Modules and get here and i dont know why ????

is this

zcat /proc/config.gz | grep CONFIG_MODULE_ALLOW_MISSING_NAMESPACE_IMPORTS
CONFIG_MODULE_ALLOW_MISSING_NAMESPACE_IMPORTS=y

enabled by default ..... im recompiling now Thanks for the info.

nope i still cannot compile the modules even with CONFIG_MODULE_ALLOW_MISSING_NAMESPACE_IMPORTS disabled

Thks,
Little gentoo forum post on the subject:
https://forums.gentoo.org/viewtopic-p-8437576.html#8437576
Thks 4 ur attention.

Calling script updated, cf above link.
Thks 4 ur attention.

Calling script updated, cf above link.
Hopefully near beta.
Thks 4 ur attention.

Single script version, cf above link.
Thks 4 ur attention.

@pchome

. /usr/src/linux-${CUR_KERNEL_VER}/include/config/auto.conf
Sorry to disturb for a likely to be noob question:
I'm wondering about the purpose of the above command.
All I found it about it is that the file get displayed if set +x in bash script.

Is there any reason I'm missing?

Big up for your work, by the way ;)
Thks 4 ur attention, interest & support.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

mid-kid picture mid-kid  Â·  5Comments

prototype99 picture prototype99  Â·  3Comments

Muffindrake picture Muffindrake  Â·  8Comments

Hello71 picture Hello71  Â·  5Comments

javashin picture javashin  Â·  4Comments