вторник, 23 июля 2024 г.

moving string literals to discardable sections

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
  1. 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
  2. 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:
  1. currently LIEF (or binutils) cannot do it
  2. 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
Also for aarch64 it try to track distance between adrp/add pair and current size of function - if it is lesser than 2Kb script will move literal inside function (and so emulate functions constant pool like producing by VC++)

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

Комментариев нет:

Отправить комментарий