As kotest shows we can achieve reduction in the size of non-discardable sections of LKMs (and thus reduce total size of memory occupied by kernel) with moving constants used in functions from .init.text to discardable sections like .init.rodata. As usually we can do it manually
or invent some dirty hacks to automate this boring process. And the first thought that comes to mind is to employ
gcc
Lets assume that we have some string literal referred from set of functions F1 ... FN. We can safely move it to discardable section only if all this functions itself located in discardable sections. This leads to two consequences
- in case of gcc plugin we must operate on RTL bcs some functions can be inlined (or even fully eliminated) - you just cannot do it in GIMPLE
- we need some tracking of cross-references between functions and literals
Unfortunately gcc does not have such tracking - all string literals stored in string pool. To make live even worse sometimes string pool belongs to function. From this comment
Only a few targets need per-function constant pools. Most can use one per-file pool
we can conclude that actually nobody ever understand when per-function constant pools are used. Probably I could patch my gcc plugin to add tracking of string literals, finding candidates for eviction to discardable section and marking them with __section__
attribute. However debugging of RTL gcc-plugins is real nightmare
Patching obj files
The next logical step is try to move string literals directly in object files (for example with LIEF). kotest already able to find candidates so the rest is simply to move them, right? Personally I believe that this is possible but there are several problems with this approach:
- currently LIEF (or binutils) cannot do it
- we need to patch not only relocs but also DWARF debug info and symbol table - bcs after removing of some string from .rodata addresses of all entries below must be changed
In general patching of binary is not perfect idea, especially if there is good alternative
Patching asm files
gcc can generate them with -S option and they are just plain text. Everybody is able to parse text files, is not it?
So I write perl script to parse .S file, collect functions, literals, finding candidates and move them. Obvious drawback of this method - it is very processor specific. Under different processor not only instructions for loading of string literals differs but even asm directives. So currently only limited processors are supported:
- x86_64
- aarch64
- mips
command line options
- -F - file with names of functions considered as discardable
- -f - dump found functions
- -g - try all symbols not marked as global
- -l - dump found string literals
- -r - name of output discardable section, usually this should be .init.rodata
- -s - name of discardable section where functions lye - usually .init.text
- -v - verbose mode
- -w - for debugging only, don't rewrite original .S file
to select processor:
- -i for x86_64
- -M for mips
by default processor is aarch64