From: Nick Alcock <hidden> Date: 2021-09-29 21:52:27
/proc/kallsyms is very useful for tracers and other tools that need to
map kernel symbols to addresses.
It would be useful if there were a mapping between kernel symbol and module
name that only changed when the kernel source code is changed. This mapping
should not change simply because a module becomes built into the kernel.
It might also be useful if there were reliable symbol size information to
determine whether an address is within a symbol or outside it, especially
given that there could be huge gaps between symbols.
Fix this by introducing a new config parameter CONFIG_KALLMODSYMS, which
does several things at once (introduced in distinct commits inthis series).
Generate a file "modules_thick.builtin" that maps from the thin archives
that make up built-in modules to their constituent object files. (This
reintroduces the machinery that used to be used to generate
modules.builtin. I am not wedded to this mechanism: if someone can
figure out a mechanism that does not require recursing over the entire
build tree, I'm happy to use it, but I suspect that no such mechanism
exists, since the only place the mapping from object file to module
exists is in the makefiles themselves. Regardless, this is fairly cheap,
adding less than a second to a typical hot-cache build of a large
enterprise kernel. This is true even though it needs to be run
unconditionally whenever the .config changes.)
Generate a linker map ".tmp_vmlinux.map", converting it into
".tmp_vmlinux.ranges", mapping address ranges to object files.
Have scripts/kallsyms read these two new files to map symbol addresses
to built-in-module names and then write a mapping from object file
address to module name to the *.s output file.
The mapping consists of three new symbols:
- kallsyms_module_addresses/kallsyms_module_offsets encodes the
address/offset of each object file (derived from the linker map), in
exactly the same way as kallsyms_addresses/kallsyms_offsets does
for symbols. There is no size: instead, the object files are
assumed to tile the address space. (This is slightly more
space-efficient than using a size). Non-text-section addresses are
skipped: for now, all the users of this interface only need
module/non-module information for instruction pointer addresses, not
absolute-addressed symbols and the like. This restriction can
easily be lifted in future. (For why this isn't called
kallsyms_objfiles, see two entries below.)
- kallsyms_module_names encodes the name of each module in a modified
form of strtab: notably, if an object file appears in *multiple*
modules, all of which are built in, this is encoded via a zero byte,
a one-byte module count, then a series of that many null-terminated
strings. Object files which appear in only one module in such a
multi-module list are redirected to point inside that list, so that
modules which contain some object files shared with other modules
and some object files exclusive to them do not double up the module
name. (There might still be some duplication between multiple
multi-module lists, but this is an extremely marginal size effect,
and resolving it would require an extra layer of lookup tables which
would be even more complex, and incompressible to boot). As a
special case, the table starts with a single zero byte which does
*not* represent the start of a multi-module list.
- kallsyms_modules connects the two, encoding a table associated 1:1
with kallsyms_module_addresses / kallsyms_module_offsets, pointing
at an offset in kallsyms_module_names describing which module (or
modules, for a multi-module list) the code occupying this address
range is part of. If an address range is part of no module (always
built-in) it points at 0 (the null byte at the start of the
kallsyms_module_names list). Entries in this list that would
contain the same value are fused together, along with their
corresponding kallsyms_module_addresses/offsets entries. Due to
this fusion process, and because object files can be split apart into
multiple parts by the linker for hot/cold partitioning and the like,
entries in here do not really correspond to an object file, but more
to some contiguous range of addresses which are guaranteed to belong
to a single built-in module: so it seems best to call the symbols
kallsyms_modules*. (The generator has a data structure that does
correspond more closely to object files, from which kallsyms_modules
is generated, and that does use 'objfiles' terminology.)
Emit a new /proc/kallmodsyms file akin to /proc/kallsyms but with built-in
module names, using a new kallsyms_builtin_module_address() almost identical
to kallsyms_sym_address() to get the address corresponding to a given
.kallsyms_modules index, and a new get_builtin_module_idx quite similar to
get_symbol_pos to determine the index in the .kallsyms_modules array that
relates to a given address. Save a little time by exploiting the fact that
all callers will only ever traverse this list from start to end by allowing
them to pass in the previous index returned from this function as a hint:
thus very few bsearches are actually needed. (In theory this could change
to just walk straight down kallsyms_module_addresses/offsets and not bother
bsearching at all, but doing it this way is hardly any slower and much more
robust.)
The display process is complicated a little by the weird format of the
.kallsyms_module_names table: we have to look for multimodule entries
and print them as space-separated lists of module names.
The resulting /proc/kallmodsyms file looks like this:
ffffffff8b013d20 409 t pt_buffer_setup_aux
ffffffff8b014130 11f T intel_pt_interrupt
ffffffff8b014250 2d T cpu_emergency_stop_pt
ffffffff8b014280 13a t rapl_pmu_event_init [intel_rapl_perf]
ffffffff8b0143c0 bb t rapl_event_update [intel_rapl_perf]
ffffffff8b014480 10 t rapl_pmu_event_read [intel_rapl_perf]
ffffffff8b014490 a3 t rapl_cpu_offline [intel_rapl_perf]
ffffffff8b014540 24 t __rapl_event_show [intel_rapl_perf]
ffffffff8b014570 f2 t rapl_pmu_event_stop [intel_rapl_perf]
This is emitted even if intel_rapl_perf is built into the kernel.
Further down, we see what happens when object files are reused by
multiple modules, all of which are built in to the kernel:
ffffffffa22b3aa0 ab t handle_timestamp [liquidio]
ffffffffa22b3b50 4a t free_netbuf [liquidio]
ffffffffa22b3ba0 8d t liquidio_ptp_settime [liquidio]
ffffffffa22b3c30 b3 t liquidio_ptp_adjfreq [liquidio]
[...]
ffffffffa22b9490 203 t lio_vf_rep_create [liquidio]
ffffffffa22b96a0 16b t lio_vf_rep_destroy [liquidio]
ffffffffa22b9810 1f t lio_vf_rep_modinit [liquidio]
ffffffffa22b9830 1f t lio_vf_rep_modexit [liquidio]
ffffffffa22b9850 d2 t lio_ethtool_get_channels [liquidio] [liquidio_vf]
ffffffffa22b9930 9c t lio_ethtool_get_ringparam [liquidio] [liquidio_vf]
ffffffffa22b99d0 11 t lio_get_msglevel [liquidio] [liquidio_vf]
ffffffffa22b99f0 11 t lio_vf_set_msglevel [liquidio] [liquidio_vf]
ffffffffa22b9a10 2b t lio_get_pauseparam [liquidio] [liquidio_vf]
ffffffffa22b9a40 738 t lio_get_ethtool_stats [liquidio] [liquidio_vf]
ffffffffa22ba180 368 t lio_vf_get_ethtool_stats [liquidio] [liquidio_vf]
ffffffffa22ba4f0 37 t lio_get_regs_len [liquidio] [liquidio_vf]
ffffffffa22ba530 18 t lio_get_priv_flags [liquidio] [liquidio_vf]
ffffffffa22ba550 2e t lio_set_priv_flags [liquidio] [liquidio_vf]
ffffffffa22ba580 69 t lio_set_fecparam [liquidio] [liquidio_vf]
ffffffffa22ba5f0 92 t lio_get_fecparam [liquidio] [liquidio_vf]
[...]
ffffffffa22cbd10 175 t liquidio_set_mac [liquidio_vf]
ffffffffa22cbe90 ab t handle_timestamp [liquidio_vf]
ffffffffa22cbf40 4a t free_netbuf [liquidio_vf]
ffffffffa22cbf90 2b t octnet_link_status_change [liquidio_vf]
ffffffffa22cbfc0 7e t liquidio_vxlan_port_command.constprop.0 [liquidio_vf]
Like /proc/kallsyms, the output is driven by address, so keeps the
curious property of /proc/kallsyms that symbols (like free_netbuf above)
may appear repeatedly with different addresses: but now, unlike in
/proc/kallsyms, we can see that those symbols appear repeatedly because
they are *different symbols* that ultimately belong to different
modules, all of which are built in to the kernel.
Those symbols that come from object files that are genuinely reused and
that appear only once in meory get a /proc/kallmodsyms line with
[multiple] [modules] on it: consumers will have to be ready to handle
such lines.
Also, kernel symbols for built-in modules will probably appear
interspersed with other symbols that are part of different modules and
non-modular always-built-in symbols, which, as usual, have no
square-bracketed module denotation.
As with /proc/kallsyms, non-root usage produces addresses that are
all zero.
I am open to changing the name and/or format of /proc/kallmodsyms, but felt
it best to split it out of /proc/kallsyms to avoid breaking existing
kallsyms parsers. Another possible syntax might be to use {curly brackets}
or something to denote built-in modules: it might be possible to drop
/proc/kallmodsyms and make /proc/kallsyms emit things in this format.
(Equally, now kallmodsyms data uses very little space, the
CONFIG_KALLMODSYMS config option might be something people don't want to
bother with.)
The size impact of all of this is minimal: for the case above, the
kallsyms2.S file went from 14107772 to 14137245 bytes, a gain of 29743
bytes, or 0.16%: vmlinux gained 10824 bytes, a gain of .017%, and the
compressed vmlinux only 7552 bytes, a gain of .08%: though the latter
two values are very configuration-dependent, they seem likely to scale
roughly with the kernel they are part of.
The last patch is an RFC to see if the idea is considered to be worth
spending more time optimizing the representation, which adds a new
kallsyms_sizes section that gives the size of each symbol, and uses this
info to report reliable symbol sizes to in-kernel users, and (via a new
column in /proc/kallmodsyms) to out-of-kernel users too. Having reliable
size info lets us identify inter-symbol gaps and sort symbols so that
start/end-marker and overlapping symbols are consistently ordered with
respect to the symbols they overlap. This certainly uses too much space
right now, 200KiB--1MiB: a better representation is certainly needed. One
that springs to mind is making the table sparse (pairs of symbol
index/size), and recording explicit sizes only for those symbols that
are not immediately followed by a subsequent symbol.
Differences from v4, two months ago:
- Fix building of tristate.conf if missing (usually concealed by the
syncconfig being run for other reasons, but not always: the kernel
test robot spotted it).
- Forward-port atop v5.15-rc3.
Differences from v3, a month earlier:
- Fix a kernel test robot warning in get_ksymbol_core (possible
use of uninitialized variable if kallmodsyms was wanted but
kallsyms_module_offsets was not present, which is most unlikely).
Differences from v2, a couple of months before that:
- Split the series up. In particular, the size impact of the table
optimizer is now quantified, and the symbol-size patch is split out and
turned into an RFC patch, with the /proc/kallmodsyms format before that
patch lacking a size column. Some speculation on how to make the symbol
sizes less space-wasteful is added (but not yet implemented).
- Drop a couple of unnecessary #includes, one unnecessarily exported
symbol, and a needless de-staticing.
Differences from v1, a year or so back:
- Move from a straight symbol->module name mapping to a mapping from
address-range to TU to module name list, bringing major space savings
over the previous approach and support for object files used by many
built-in modules at the same time, at the cost of a slightly more complex
approach (unavoidably so, I think, given that we have to merge three data
sources together: the link map in .tmp_vmlinux.ranges, the nm output on
stdin, and the mapping from TU name to module names in
modules_thick.builtin).
We do opportunistic merging of TUs if they cite the same modules and
reuse module names where doing so is simple: see optimize_obj2mod
below. I considered more extensive searches for mergeable entries and
more intricate encodings of the module name list allowing TUs that are
used by overlapping sets of modules to share their names, but such
modules are rare enough (and such overlapping sharings are vanishingly
rare) that it seemed likely to save only a few bytes at the cost of much
more hard-to-test code. This is doubly true now that the tables needed
are only a few kilobytes in length.
Signed-off-by: Nick Alcock <redacted>
Signed-off-by: Eugene Loh <redacted>
Reviewed-by: Kris Van Hees <redacted>
Nick Alcock (7):
kbuild: bring back tristate.conf
kbuild: add modules_thick.builtin
kbuild: generate an address ranges map at vmlinux link time
kallsyms: introduce sections needed to map symbols to built-in modules
kallsyms: optimize .kallsyms_modules*
kallsyms: add /proc/kallmodsyms
kallsyms: add reliable symbol size info
.gitignore | 1 +
Documentation/dontdiff | 1 +
Makefile | 23 +-
include/linux/module.h | 7 +-
init/Kconfig | 8 +
kernel/kallsyms.c | 304 ++++++++++++++---
kernel/module.c | 4 +-
scripts/Kbuild.include | 6 +
scripts/Makefile | 6 +
scripts/Makefile.modbuiltin | 56 ++++
scripts/kallsyms.c | 642 +++++++++++++++++++++++++++++++++++-
scripts/kconfig/confdata.c | 41 ++-
scripts/link-vmlinux.sh | 22 +-
scripts/modules_thick.c | 200 +++++++++++
scripts/modules_thick.h | 48 +++
15 files changed, 1301 insertions(+), 68 deletions(-)
create mode 100644 scripts/Makefile.modbuiltin
create mode 100644 scripts/modules_thick.c
create mode 100644 scripts/modules_thick.h
--
2.33.0.256.gb827f06fa9
From: Nick Alcock <hidden> Date: 2021-09-29 21:52:25
tristate.conf was dropped because it is not needed to build a
modules.builtin (although dropping it introduces a few false positives
into modules.builtin support), and doing so avoids one round of
recursion through the build tree to build it. But kallmodsyms support
requires building a mapping from object file name to built-in module
name for all builtin modules: this seems to me impossible to accomplish
without parsing all makefiles under the influence of tristate.conf,
since the makefiles are the only place this mapping is recorded.
So bring it back for this purpose.
This partially reverts commit 8b41fc4454e36fbfdbb23f940d023d4dece2de29.
Signed-off-by: Nick Alcock <redacted>
---
Makefile | 2 +-
scripts/kconfig/confdata.c | 41 ++++++++++++++++++++++++++++++++++++--
2 files changed, 40 insertions(+), 3 deletions(-)
@@ -724,7 +724,7 @@ $(KCONFIG_CONFIG):## Do not use $(call cmd,...) here. That would suppress prompts from syncconfig,# so you cannot notice that Kconfig is waiting for the user input.-%/config/auto.conf %/config/auto.conf.cmd %/generated/autoconf.h:$(KCONFIG_CONFIG)+%/config/auto.conf %/config/auto.conf.cmd %/generated/autoconf.h %/tristate.conf:$(KCONFIG_CONFIG)$(Q)$(kecho)" SYNC $@"$(Q)$(MAKE)-f$(srctree)/Makefilesyncconfigelse # !may-sync-config
@@ -1058,7 +1077,7 @@ int conf_write_autoconf(int overwrite)structsymbol*sym;constchar*name;constchar*autoconf_name=conf_get_autoconfig_name();-FILE*out,*out_h;+FILE*out,*tristate,*out_h;inti;if(!overwrite&&is_present(autoconf_name))
@@ -1073,6 +1092,13 @@ int conf_write_autoconf(int overwrite)if(!out)return1;+tristate=fopen(".tmpconfig_tristate","w");+if(!tristate){+fclose(out);+fclose(tristate);+return1;+}+out_h=fopen(".tmpconfig.h","w");if(!out_h){fclose(out);
@@ -1080,6 +1106,7 @@ int conf_write_autoconf(int overwrite)}conf_write_heading(out,&kconfig_printer_cb,NULL);+conf_write_heading(tristate,&tristate_printer_cb,NULL);conf_write_heading(out_h,&header_printer_cb,NULL);for_all_symbols(i,sym){
@@ -1087,11 +1114,13 @@ int conf_write_autoconf(int overwrite)if(!(sym->flags&SYMBOL_WRITE)||!sym->name)continue;-/* write symbols to auto.conf and autoconf.h */+/* write symbols to auto.conf, tristate and autoconf.h */conf_write_symbol(out,sym,&kconfig_printer_cb,(void*)1);+conf_write_symbol(tristate,sym,&tristate_printer_cb,(void*)1);conf_write_symbol(out_h,sym,&header_printer_cb,NULL);}fclose(out);+fclose(tristate);fclose(out_h);name=getenv("KCONFIG_AUTOHEADER");
@@ -1102,6 +1131,14 @@ int conf_write_autoconf(int overwrite)if(rename(".tmpconfig.h",name))return1;+name=getenv("KCONFIG_TRISTATE");+if(!name)+name="include/config/tristate.conf";+if(make_parent_dir(name))+return1;+if(rename(".tmpconfig_tristate",name))+return1;+if(make_parent_dir(autoconf_name))return1;/*
From: Nick Alcock <hidden> Date: 2021-09-29 21:52:38
This is similar to modules.builtin, and constructed in a similar way to
the way that used to be built before commit
8b41fc4454e36fbfdbb23f940d023d4dece2de29, via tristate.conf inclusion
and recursive concatenation up the tree. Unlike modules.builtin,
modules_thick.builtin givs the names of the object files that make up
modules that are comprised of more than one object file, using a syntax
similar to that of makefiles, e.g.:
crypto/crypto.o: crypto/api.o crypto/cipher.o crypto/compress.o crypto/memneq.o
crypto/crypto_algapi.o: crypto/algapi.o crypto/proc.o crypto/scatterwalk.o
crypto/aead.o:
crypto/geniv.o:
(where the latter two are single-file modules).
An upcoming commit will use this mapping to populate /proc/kallmodsyms.
A parser is included that yields a stram of (module, objfile name[])
mappings: it's a bit baroque, but then parsing text files in C is quite
painful, and I'd rather put the complexity in here than in its callers.
The parser is not built in this commit, nor does it have any callers
yet; nor is any rule added that causes modules_thick.builtin to actually
be constructed. (Again, see a later commit for that.)
I am not wedded to the approach used to construct this file, but I don't
see any other way to do it despite spending a week or so trying to tie
it into Kbuild without using a separate Makefile.modbuiltin: unlike the
names of builtin modules (which are also recorded in the source files
themseves via MODULE_*() macros) the mapping from object file name to
built-in module name is not recorded anywhere but in the makefiles
themselves, so we have to at least reparse them with something to
indicate the builtin-ness of each module (i.e., tristate.conf) if we are
to figure out which modules are built-in and which are not.
Signed-off-by: Nick Alcock <redacted>
---
.gitignore | 1 +
Documentation/dontdiff | 1 +
Makefile | 19 +++-
scripts/Kbuild.include | 6 ++
scripts/Makefile.modbuiltin | 56 ++++++++++
scripts/modules_thick.c | 200 ++++++++++++++++++++++++++++++++++++
scripts/modules_thick.h | 48 +++++++++
7 files changed, 330 insertions(+), 1 deletion(-)
create mode 100644 scripts/Makefile.modbuiltin
create mode 100644 scripts/modules_thick.c
create mode 100644 scripts/modules_thick.h
@@ -1507,6 +1507,23 @@ __modinst_pre:endif # CONFIG_MODULES+# modules_thick.builtin maps from kernel modules (or rather the object file+# names they would have had had they not been built in) to their constituent+# object files: we can use this to determine which modules any given object+# file is part of. (We cannot eliminate the slight redundancy here without+# double-expansion.)++modthickbuiltin-files:=$(addsuffix/modules_thick.builtin,$(build-dirs))++modules_thick.builtin:$(modthickbuiltin-files)+$(Q)$(AWK)'!x[$$0]++'$(addsuffix/$@,$(build-dirs))>$@++# tristate.conf is not included from this Makefile. Add it as a prerequisite+# here to make it self-healing in case somebody accidentally removes it.+$(modthickbuiltin-files):include/config/tristate.conf+$(Q)$(MAKE)$(modbuiltin)=$(patsubst%/modules_thick.builtin,%,$@)builtin-file=modules_thick.builtin++#### Cleaning is done on three levels.# make clean Delete most generated files
@@ -0,0 +1,56 @@+# SPDX-License-Identifier: GPL-2.0+# ==========================================================================+# Generating modules_thick.builtin+# ==========================================================================++src:=$(obj)++PHONY:=__modbuiltin+__modbuiltin:++include include/config/auto.conf+# tristate.conf sets tristate variables to uppercase 'Y' or 'M'+# That way, we get the list of built-in modules in obj-Y+include include/config/tristate.conf++include scripts/Kbuild.include++ifdef building_out_of_srctree+# Create output directory if not already present+_dummy:=$(shell[-d$(obj)]||mkdir-p$(obj))+endif++# The filename Kbuild has precedence over Makefile+kbuild-dir:=$(if$(filter/%,$(src)),$(src),$(srctree)/$(src))+kbuild-file:=$(if$(wildcard$(kbuild-dir)/Kbuild),$(kbuild-dir)/Kbuild,$(kbuild-dir)/Makefile)+include $(kbuild-file)++include scripts/Makefile.lib++modthickbuiltin-subdirs:=$(patsubst%,%/modules_thick.builtin,$(subdir-ym))+modthickbuiltin-target:=$(obj)/modules_thick.builtin++__modbuiltin:$(obj)/$(builtin-file)$(subdir-ym)+@:++$(modthickbuiltin-target):$(subdir-ym)FORCE+$(Q)rm-f$@+$(Q)$(foreachmod-o,$(filter%.o,$(obj-Y)),\+printf"%s:"$(addprefix$(obj)/,$(mod-o))>>$@;\+printf" %s"$(sort$(strip$(addprefix$(obj)/,$($(mod-o:.o=-objs))\+$($(mod-o:.o=-y))$($(mod-o:.o=-Y)))))>>$@;\+printf"\n">>$@;)\+cat/dev/null$(modthickbuiltin-subdirs)>>$@;++PHONY+=FORCE++FORCE:++# Descending+# ---------------------------------------------------------------------------++PHONY+=$(subdir-ym)+$(subdir-ym):+$(Q)$(MAKE)$(modbuiltin)=$@builtin-file=$(builtin-file)++.PHONY:$(PHONY)
@@ -0,0 +1,200 @@+/* SPDX-License-Identifier: GPL-2.0 */+/*+*Asimplemodules_thickreader.+*+*(C)2014,2021Oracle,Inc.Allrightsreserved.+*+*Thisprogramisfreesoftware;youcanredistributeitand/ormodify+*itunderthetermsoftheGNUGeneralPublicLicenseaspublishedby+*theFreeSoftwareFoundation;eitherversion2oftheLicense,or+*(atyouroption)anylaterversion.+*/++#include<errno.h>+#include<stdio.h>+#include<stdlib.h>+#include<string.h>++#include"modules_thick.h"++/*+*Readamodules_thick.builtinfileandtranslateitintoastreamof+*name/module-namepairs.+*/++/*+*Constructamodules_thick.builtiniterator.+*/+structmodules_thick_iter*+modules_thick_iter_new(constchar*modules_thick_file)+{+structmodules_thick_iter*i;++i=calloc(1,sizeof(structmodules_thick_iter));+if(i==NULL)+returnNULL;++i->f=fopen(modules_thick_file,"r");++if(i->f==NULL){+fprintf(stderr,"Cannot open builtin module file %s: %s\n",+modules_thick_file,strerror(errno));+returnNULL;+}++returni;+}++/*+*Iterate,returninganewnull-terminatedarrayofobjectfilenames,anda+*newdynamically-allocatedmodulename.(Themodulenamepassedinisfreed.)+*+*Thearrayofobjectfilenamesshouldbefreedbythecaller:thestringsit+*pointstoareownedbytheiterator,andshouldnotbefreed.+*/++char**__attribute__((__nonnull__))+modules_thick_iter_next(structmodules_thick_iter*i,char**module_name)+{+size_tnpaths=1;+char**module_paths;+char*last_slash;+char*last_dot;+char*trailing_linefeed;+char*object_name=i->line;+char*dash;+intcomposite=0;++/*+*Readinallmoduleentries,computingthesuffixless,pathlessname+*ofthemoduleandbuildingthenextarrayfulofobjectfilenamesfor+*return.+*+*Modulescanconsistofmultiplefiles:inthiscase,theportion+*beforethecolonisthepathtothemodule(asbefore):theportion+*afterthecolonisaspace-separatedlistoffilesthatshouldbe+*consideredpartofthismodule.Inthiscase,theportionbeforethe+*nameisan"object file"thatdoesnotactuallyexist:itismerged+*intobuilt-in.awithouteverbeingwrittenout.+*+*Allmodulenameshave-translatedto_,tomatchwhatisdonetothe+*namesofthesamethingswhenbuiltasmodules.+*/++/*+*Reinvocationofexhaustediterator.ReturnNULL,once.+*/+retry:+if(getline(&i->line,&i->line_size,i->f)<0){+if(ferror(i->f)){+fprintf(stderr,"Error reading from modules_thick file:"+" %s\n",strerror(errno));+exit(1);+}+rewind(i->f);+returnNULL;+}++if(i->line[0]=='\0')+gotoretry;++/*+*Slicethelineintwoatthecolon,ifany.Ifthereisanything+*pastthe':',thisisacompositemodule.(Weallowfornocolon+*forrobustness,eventhoughoneshouldalwaysbepresent.)+*/+if(strchr(i->line,':')!=NULL){+char*name_start;++object_name=strchr(i->line,':');+*object_name='\0';+object_name++;+name_start=object_name+strspn(object_name," \n");+if(*name_start!='\0'){+composite=1;+object_name=name_start;+}+}++/*+*Figureoutthemodulename.+*/+last_slash=strrchr(i->line,'/');+last_slash=(!last_slash)?i->line:+last_slash+1;+free(*module_name);+*module_name=strdup(last_slash);+dash=*module_name;++while(dash!=NULL){+dash=strchr(dash,'-');+if(dash!=NULL)+*dash='_';+}++last_dot=strrchr(*module_name,'.');+if(last_dot!=NULL)+*last_dot='\0';++trailing_linefeed=strchr(object_name,'\n');+if(trailing_linefeed!=NULL)+*trailing_linefeed='\0';++/*+*Multifileseparator?Objectfilenamesexplicitlystated:+*slicethemupandshufflethemin.+*+*Thearraysizemaybeanoverestimateifanyobjectfile+*namesstartorendwithspaces(veryunlikely)butcannotbe+*anunderestimate.(Checkforitanyway.)+*/+if(composite){+char*one_object;++for(npaths=0,one_object=object_name;+one_object!=NULL;+npaths++,one_object=strchr(one_object+1,' '));+}++module_paths=malloc((npaths+1)*sizeof(char*));+if(!module_paths){+fprintf(stderr,"%s: out of memory on module %s\n",__func__,+*module_name);+exit(1);+}++if(composite){+char*one_object;+size_ti=0;++while((one_object=strsep(&object_name," "))!=NULL){+if(i>=npaths){+fprintf(stderr,"%s: num_objs overflow on module "+"%s: this is a bug.\n",__func__,+*module_name);+exit(1);+}++module_paths[i++]=one_object;+}+}else+module_paths[0]=i->line;/* untransformed module name */++module_paths[npaths]=NULL;++returnmodule_paths;+}++/*+*Freeaniterator.Canbecalledwhileiterationisunderway,soeven+*statethatisfreedattheendofiterationmustbefreedheretoo.+*/+void+modules_thick_iter_free(structmodules_thick_iter*i)+{+if(i==NULL)+return;+fclose(i->f);+free(i->line);+free(i);+}
From: Nick Alcock <hidden> Date: 2021-09-29 21:52:41
This emits a new file, .tmp_vmlinux.ranges, which maps address
range/size pairs in vmlinux to the object files which make them up,
e.g., in part:
0x0000000000000000 0x30 arch/x86/kernel/cpu/common.o
0x0000000000001000 0x1000 arch/x86/events/intel/ds.o
0x0000000000002000 0x4000 arch/x86/kernel/irq_64.o
0x0000000000006000 0x5000 arch/x86/kernel/process.o
0x000000000000b000 0x1000 arch/x86/kernel/cpu/common.o
0x000000000000c000 0x5000 arch/x86/mm/cpu_entry_area.o
0x0000000000011000 0x10 arch/x86/kernel/espfix_64.o
0x0000000000011010 0x2 arch/x86/kernel/cpu/common.o
[...]
In my simple tests this seems to work with clang too, but if I'm not
sure how stable the format of clang's linker mapfiles is: if it turns
out not to work in some versions, the mapfile-massaging awk script added
here might need some adjustment.
Signed-off-by: Nick Alcock <redacted>
---
scripts/link-vmlinux.sh | 15 ++++++++++++++-
1 file changed, 14 insertions(+), 1 deletion(-)
@@ -248,6 +248,19 @@ kallsyms(){localkallsymopt;+# read the linker map to identify ranges of addresses:+# - for each *.o file, report address, size, pathname+# - most such lines will have four fields+# - but sometimes there is a line break after the first field+# - start reading at "Linker script and memory map"+# - stop reading at ".brk"+${AWK}'+/\.o$/&&start==1{print$(NF-2),$(NF-1),$NF}+/^Linkerscriptandmemorymap/{start=1}+/^\.brk/{exit(0)}+'.tmp_vmlinux.map|sort>.tmp_vmlinux.ranges++# get kallsyms optionsif[-n"${CONFIG_KALLSYMS_ALL}"];thenkallsymopt="${kallsymopt} --all-symbols"fi
From: Nick Alcock <hidden> Date: 2021-09-29 21:52:45
The existing mechanisms in get_symbol_pos to determine the end of a
symbol is an inaccurate heuristic. By passing nm -S output into
scripts/kallsyms.c and writing the symbol sizes to a new .kallsyms_sizes
section, we can get accurate sizes and sort the symbols accordingly,
reliably sorting zero-size symbols first (on the grounds that they are
usually e.g. section markers, and other symbols at the same address are
conceptually contained within them and should be sorted after them),
then larger symbols before smaller ones (so that overlapping symbols
print the containing symbol first, before its containees). We can
also use this to improve aliased symbol detection.
Emit the size info as an extra column in /proc/kallmodsyms (since its
format is not yet set in stone), and export it to iterator consumers.
The notable downside of this is that the new .kallsyms_sizes is pretty
big: a PTR per symbol, so vmlinux.o grows by almost a megabyte, though
it compresses pretty well, so bzImage grows by only a megabyte.
I'm not sure how to reduce this (perhaps using an array with elements
sized to be no larger than needed for the contents, so that almost
always two-byte entries would do? except that in my test kernel two
symbols are bigger than this: sme_workarea, at 400K, and __log_buf, at
100K: the latter seems often likely to be larger than 64K). A simple
scheme to reduce this would be to split the sizes array into several
arrays with differently-sized elements, and run-length-compress away the
zero bytes -- but that's not implemented yet, and might never be if
people think the whole idea of this is pointless.
In the absence of a way to shrink things, this should probably be hidden
behind a new config symbol if exposed at all, and kallmodsyms just shows
zero sizes if it's configured out (but this is enough of an RFC that
that's not yet done: possibly the benefits of this are too marginal to
be worth it, even if they do let kall(mod)syms consumers distinguish
symbols from padding, which was previously impossible).
Signed-off-by: Nick Alcock <redacted>
Signed-off-by: Eugene Loh <redacted>
---
include/linux/module.h | 7 ++--
kernel/kallsyms.c | 74 ++++++++++++++++++++++-------------------
kernel/module.c | 4 ++-
scripts/kallsyms.c | 29 +++++++++++++---
scripts/link-vmlinux.sh | 7 +++-
5 files changed, 77 insertions(+), 44 deletions(-)
@@ -590,7 +590,8 @@ struct module *find_module(const char *name);/* Returns 0 and fills in value, defined and namebuf, or -ERANGE ifsymnumoutofrange.*/intmodule_get_kallsym(unsignedintsymnum,unsignedlong*value,char*type,-char*name,char*module_name,int*exported);+char*name,char*module_name,unsignedlong*size,+int*exported);/* Look for this name: can be of form module:name. */unsignedlongmodule_kallsyms_lookup_name(constchar*name);
@@ -768,8 +769,8 @@ static inline int lookup_module_symbol_attrs(unsigned long addr, unsigned long *}staticinlineintmodule_get_kallsym(unsignedintsymnum,unsignedlong*value,-char*type,char*name,-char*module_name,int*exported)+char*type,char*name,char*module_name,+unsignedlong*size,int*exported){return-ERANGE;}
@@ -257,12 +258,24 @@ int kallsyms_on_each_symbol(int (*fn)(void *, const char *, struct module *,}#endif /* CONFIG_LIVEPATCH */+/*+*Thecallerpassesinanaddress,andwereturnanindextothesymbol--+*potentiallyalsosizeandoffsetinformation.+*Butanaddressmightmaptomultiplesymbolsbecause:+*-somesymbolsmighthavezerosize+*-somesymbolsmightbealiasesofoneanother+*-somesymbolsmightspan(encompass)others+*Thesymbolsshouldalreadybeorderedsothat,foraparticularaddress,+*wefirsthavethezero-sizeones,thenthebiggest,thenthesmallest.+*Sowefindtheindexby:+*-findingthelastsymbolwiththetargetaddress+*-backingtheindexupsolongasboththeaddressandsizeareunchanged+*/staticunsignedlongget_symbol_pos(unsignedlongaddr,unsignedlong*symbolsize,unsignedlong*offset){-unsignedlongsymbol_start=0,symbol_end=0;-unsignedlongi,low,high,mid;+unsignedlonglow,high,mid;/* This kernel should never had been booted. */if(!IS_ENABLED(CONFIG_KALLSYMS_BASE_RELATIVE))
@@ -283,36 +296,17 @@ static unsigned long get_symbol_pos(unsigned long addr,}/*-*Searchforthefirstaliasedsymbol.Aliased-*symbolsaresymbolswiththesameaddress.+*Searchforthefirstaliasedsymbol.*/-while(low&&kallsyms_sym_address(low-1)==kallsyms_sym_address(low))+while(low+&&kallsyms_sym_address(low-1)==kallsyms_sym_address(low)+&&kallsyms_sizes[low-1]==kallsyms_sizes[low])--low;-symbol_start=kallsyms_sym_address(low);--/* Search for next non-aliased symbol. */-for(i=low+1;i<kallsyms_num_syms;i++){-if(kallsyms_sym_address(i)>symbol_start){-symbol_end=kallsyms_sym_address(i);-break;-}-}--/* If we found no next symbol, we use the end of the section. */-if(!symbol_end){-if(is_kernel_inittext(addr))-symbol_end=(unsignedlong)_einittext;-elseif(IS_ENABLED(CONFIG_KALLSYMS_ALL))-symbol_end=(unsignedlong)_end;-else-symbol_end=(unsignedlong)_etext;-}-if(symbolsize)-*symbolsize=symbol_end-symbol_start;+*symbolsize=kallsyms_sizes[low];if(offset)-*offset=addr-symbol_start;+*offset=addr-kallsyms_sym_address(low);returnlow;}
@@ -633,6 +627,7 @@ struct kallsym_iter {loff_tpos_bpf_end;unsignedlongvalue;unsignedintnameoff;/* If iterating in core kernel symbols. */+unsignedlongsize;chartype;charname[KSYM_NAME_LEN];charmodule_name[MODULE_NAME_LEN];
@@ -667,7 +662,7 @@ static int get_ksymbol_mod(struct kallsym_iter *iter)intret=module_get_kallsym(iter->pos-iter->pos_arch_end,&iter->value,&iter->type,iter->name,iter->module_name,-&iter->exported);+&iter->size,&iter->exported);iter->builtin_module_names=NULL;if(ret<0){
@@ -740,6 +735,7 @@ static unsigned long get_ksymbol_core(struct kallsym_iter *iter, int kallmodsymsiter->exported=0;iter->value=kallsyms_sym_address(iter->pos);+iter->size=kallsyms_sizes[iter->pos];iter->type=kallsyms_get_symbol_type(off);iter->module_name[0]='\0';
@@ -858,12 +854,14 @@ static int s_show_internal(struct seq_file *m, void *p, int kallmodsyms){void*value;structkallsym_iter*iter=m->private;+unsignedlongsize;/* Some debugging symbols have no name. Ignore them. */if(!iter->name[0])return0;value=iter->show_value?(void*)iter->value:NULL;+size=iter->show_value?iter->size:0;/**Realmodule,orbuilt-inmoduleand/proc/kallsymsbeingshown.
@@ -4368,7 +4368,8 @@ int lookup_module_symbol_attrs(unsigned long addr, unsigned long *size,}intmodule_get_kallsym(unsignedintsymnum,unsignedlong*value,char*type,-char*name,char*module_name,int*exported)+char*name,char*module_name,unsignedlong*size,+int*exported){structmodule*mod;
@@ -4387,6 +4388,7 @@ int module_get_kallsym(unsigned int symnum, unsigned long *value, char *type,strlcpy(name,kallsyms_symbol_name(kallsyms,symnum),KSYM_NAME_LEN);strlcpy(module_name,mod->name,MODULE_NAME_LEN);*exported=is_exported(name,*value,mod);+*size=kallsyms->symtab[symnum].st_size;preempt_enable();return0;}
@@ -274,7 +274,12 @@ kallsyms()fiinfoKSYMS${2}-${NM}-n${1}|scripts/kallsyms${kallsymopt}>${2}+# "nm -S" does not print symbol size when size is 0+# Therefore use awk to regularize the data:+# - when there are only three fields, add an explicit "0"+# - when there are already four fields, pass through as is+${NM}-n-S${1}|${AWK}'NF==3 {print $1, 0, $2, $3}; NF==4'|\+scripts/kallsyms${kallsymopt}>${2}}# Perform one step in kallsyms generation, including temporary linking of
From: Nick Alcock <hidden> Date: 2021-09-29 21:52:48
Use the tables added in the previous commits to introduce a new
/proc/kallmodsyms, in which [module names] are also given for things
that *could* have been modular had they not been built in to the kernel.
So symbols that are part of, say, ext4 are reported as [ext4] even if
ext4 happens to be buiilt in to the kernel in this configuration.
Symbols that are part of multiple modules at the same time are shown
with [multiple] [module names]: consumers will have to be ready to
handle such lines. Also, kernel symbols for built-in modules will be
sorted by size, as usual for the core kernel, so will probably appear
interspersed with other symbols that are part of different modules and
non-modular always-built-in symbols, which, as usual, have no
square-bracketed module denotation. This differs from /proc/kallsyms,
where all symbols associated with a module will always appear in a group
(and randomly ordered).
The result looks like this:
ffffffff8b013d20 t pt_buffer_setup_aux
ffffffff8b014130 T intel_pt_interrupt
ffffffff8b014250 T cpu_emergency_stop_pt
ffffffff8b014280 t rapl_pmu_event_init [intel_rapl_perf]
ffffffff8b0143c0 t rapl_event_update [intel_rapl_perf]
ffffffff8b014480 t rapl_pmu_event_read [intel_rapl_perf]
ffffffff8b014490 t rapl_cpu_offline [intel_rapl_perf]
ffffffff8b014540 t __rapl_event_show [intel_rapl_perf]
ffffffff8b014570 t rapl_pmu_event_stop [intel_rapl_perf]
This is emitted even if intel_rapl_perf is built into the kernel (but,
obviously, not if it's not in the .config at all, or is in a module that
is not loaded).
Further down, we see what happens when object files are reused by
multiple modules, all of which are built in to the kernel:
ffffffffa22b3aa0 t handle_timestamp [liquidio]
ffffffffa22b3b50 t free_netbuf [liquidio]
ffffffffa22b3ba0 t liquidio_ptp_settime [liquidio]
ffffffffa22b3c30 t liquidio_ptp_adjfreq [liquidio]
[...]
ffffffffa22b9490 t lio_vf_rep_create [liquidio]
ffffffffa22b96a0 t lio_vf_rep_destroy [liquidio]
ffffffffa22b9810 t lio_vf_rep_modinit [liquidio]
ffffffffa22b9830 t lio_vf_rep_modexit [liquidio]
ffffffffa22b9850 t lio_ethtool_get_channels [liquidio] [liquidio_vf]
ffffffffa22b9930 t lio_ethtool_get_ringparam [liquidio] [liquidio_vf]
ffffffffa22b99d0 t lio_get_msglevel [liquidio] [liquidio_vf]
ffffffffa22b99f0 t lio_vf_set_msglevel [liquidio] [liquidio_vf]
ffffffffa22b9a10 t lio_get_pauseparam [liquidio] [liquidio_vf]
ffffffffa22b9a40 t lio_get_ethtool_stats [liquidio] [liquidio_vf]
ffffffffa22ba180 t lio_vf_get_ethtool_stats [liquidio] [liquidio_vf]
ffffffffa22ba4f0 t lio_get_regs_len [liquidio] [liquidio_vf]
ffffffffa22ba530 t lio_get_priv_flags [liquidio] [liquidio_vf]
ffffffffa22ba550 t lio_set_priv_flags [liquidio] [liquidio_vf]
ffffffffa22ba580 t lio_set_fecparam [liquidio] [liquidio_vf]
ffffffffa22ba5f0 t lio_get_fecparam [liquidio] [liquidio_vf]
[...]
ffffffffa22cbd10 t liquidio_set_mac [liquidio_vf]
ffffffffa22cbe90 t handle_timestamp [liquidio_vf]
ffffffffa22cbf40 t free_netbuf [liquidio_vf]
ffffffffa22cbf90 t octnet_link_status_change [liquidio_vf]
ffffffffa22cbfc0 t liquidio_vxlan_port_command.constprop.0 [liquidio_vf]
Like /proc/kallsyms, the output is driven by address, so keeps the
curious property of /proc/kallsyms that symbols (like free_netbuf above)
may appear repeatedly with different addresses: but now, unlike in
/proc/kallsyms, we can see that those symbols appear repeatedly because
they are *different symbols* that ultimately belong to different
modules, all of which are built in to the kernel.
As with /proc/kallsyms, non-root usage produces addresses that are
all zero.
I am not wedded to the name or format of /proc/kallmodsyms, but felt it
best to split it out of /proc/kallsyms to avoid breaking existing
kallsyms parsers. Another possible syntax might be to use {curly
brackets} or something to denote built-in modules: it might be possible
to drop /proc/kallmodsyms and make /proc/kallsyms emit things in this
format. (Equally, now kallmodsyms data uses very little space, the
CONFIG_KALLMODSYMS config option might be something people don't want to
bother with.)
Internally, this uses a new kallsyms_builtin_module_address() almost
identical to kallsyms_sym_address() to get the address corresponding to
a given .kallsyms_modules index, and a new get_builtin_module_idx quite
similar to get_symbol_pos to determine the index in the
.kallsyms_modules array that relates to a given address. Save a little
time by exploiting the fact that all callers will only ever traverse
this list from start to end by allowing them to pass in the previous
index returned from this function as a hint: thus very few bsearches are
actually needed. (In theory this could change to just walk straight
down kallsyms_module_addresses/offsets and not bother bsearching at all,
but doing it this way is hardly any slower and much more robust.)
The display process is complicated a little by the weird format of the
.kallsyms_module_names table: we have to look for multimodule entries
and print them as space-separated lists of module names.
Signed-off-by: Nick Alcock <redacted>
---
kernel/kallsyms.c | 242 +++++++++++++++++++++++++++++++++++++++++++---
1 file changed, 227 insertions(+), 15 deletions(-)
@@ -185,6 +195,25 @@ static inline bool cleanup_symbol_name(char *s)staticinlineboolcleanup_symbol_name(char*s){returnfalse;}#endif+#ifdef CONFIG_KALLMODSYMS+staticunsignedlongkallsyms_builtin_module_address(intidx)+{+if(!IS_ENABLED(CONFIG_KALLSYMS_BASE_RELATIVE))+returnkallsyms_module_addresses[idx];++/* values are unsigned offsets if --absolute-percpu is not in effect */+if(!IS_ENABLED(CONFIG_KALLSYMS_ABSOLUTE_PERCPU))+returnkallsyms_relative_base+(u32)kallsyms_module_offsets[idx];++/* ...otherwise, positive offsets are absolute values */+if(kallsyms_module_offsets[idx]>=0)+returnkallsyms_module_offsets[idx];++/* ...and negative offsets are relative to kallsyms_relative_base - 1 */+returnkallsyms_relative_base-1-kallsyms_module_offsets[idx];+}+#endif+/* Lookup the address for this symbol. Returns 0 if not found. */unsignedlongkallsyms_lookup_name(constchar*name){
@@ -288,6 +317,54 @@ static unsigned long get_symbol_pos(unsigned long addr,returnlow;}+/*+*Thecallerpassesinanaddress,andwereturnanindextothecorresponding+*builtinmoduleindexin.kallsyms_modules,or(unsignedlong)-1ifnone+*match.+*+*Thehint_idx,ifset,isahintastothepossiblereturnvalue,tohandle+*thecommoncaseinwhichconsecutiverunsofaddressesrelatetothesame+*index.+*/+#ifdef CONFIG_KALLMODSYMS+staticunsignedlongget_builtin_module_idx(unsignedlongaddr,unsignedlonghint_idx)+{+unsignedlonglow,high,mid;++if(!IS_ENABLED(CONFIG_KALLSYMS_BASE_RELATIVE))+BUG_ON(!kallsyms_module_addresses);+else+BUG_ON(!kallsyms_module_offsets);++/*+*Doabinarysearchonthesortedkallsyms_modulesarray.Thelast+*entryinthisarrayindicatestheendofthetextsection,notan+*objectfile.+*/+low=0;+high=kallsyms_num_modules-1;++if(hint_idx>low&&hint_idx<(high-1)&&+addr>=kallsyms_builtin_module_address(hint_idx)&&+addr<kallsyms_builtin_module_address(hint_idx+1))+returnhint_idx;++if(addr>=kallsyms_builtin_module_address(low)+&&addr<kallsyms_builtin_module_address(high)){+while(high-low>1){+mid=low+(high-low)/2;+if(kallsyms_builtin_module_address(mid)<=addr)+low=mid;+else+high=mid;+}+returnlow;+}++return(unsignedlong)-1;+}+#endif+/**Lookupanaddressbutdon'tbothertofindanynames.*/
@@ -589,6 +668,8 @@ static int get_ksymbol_mod(struct kallsym_iter *iter)&iter->value,&iter->type,iter->name,iter->module_name,&iter->exported);+iter->builtin_module_names=NULL;+if(ret<0){iter->pos_mod_end=iter->pos;return0;
@@ -608,6 +689,8 @@ static int get_ksymbol_ftrace_mod(struct kallsym_iter *iter)&iter->value,&iter->type,iter->name,iter->module_name,&iter->exported);+iter->builtin_module_names=NULL;+if(ret<0){iter->pos_ftrace_mod_end=iter->pos;return0;
@@ -622,6 +705,7 @@ static int get_ksymbol_bpf(struct kallsym_iter *iter)strlcpy(iter->module_name,"bpf",MODULE_NAME_LEN);iter->exported=0;+iter->builtin_module_names=NULL;ret=bpf_get_kallsym(iter->pos-iter->pos_ftrace_mod_end,&iter->value,&iter->type,iter->name);
@@ -642,23 +726,53 @@ static int get_ksymbol_kprobe(struct kallsym_iter *iter){strlcpy(iter->module_name,"__builtin__kprobes",MODULE_NAME_LEN);iter->exported=0;+iter->builtin_module_names=NULL;returnkprobe_get_kallsym(iter->pos-iter->pos_bpf_end,&iter->value,&iter->type,iter->name)<0?0:1;}/* Returns space to next name. */-staticunsignedlongget_ksymbol_core(structkallsym_iter*iter)+staticunsignedlongget_ksymbol_core(structkallsym_iter*iter,intkallmodsyms){unsignedoff=iter->nameoff;-iter->module_name[0]='\0';+iter->exported=0;iter->value=kallsyms_sym_address(iter->pos);iter->type=kallsyms_get_symbol_type(off);+iter->module_name[0]='\0';+iter->builtin_module_names=NULL;+off=kallsyms_expand_symbol(off,iter->name,ARRAY_SIZE(iter->name));+#ifdef CONFIG_KALLMODSYMS+if(kallmodsyms){+unsignedlongmod_idx=(unsignedlong)-1;++if(kallsyms_module_offsets)+mod_idx=+get_builtin_module_idx(iter->value,+iter->hint_builtin_module_idx);+/*+*Thisisabuilt-inmoduleiffthetablesofbuilt-inmodules+*(address->modulenamemappings)andmodulenamesareknown,+*andiftheaddresswasfoundthere,andifthecorresponding+*moduleindexisnonzero.Allothercasesmeanofftheendof+*thebinaryorinanon-modularrangeinbetweenoneormore+*modules.(Alsoguardagainstacorruptkallsyms_objfiles+*arraypointingofftheendofkallsyms_modules.)+*/+if(kallsyms_modules!=NULL&&kallsyms_module_names!=NULL&&+mod_idx!=(unsignedlong)-1&&+kallsyms_modules[mod_idx]!=0&&+kallsyms_modules[mod_idx]<kallsyms_module_names_len)+iter->builtin_module_names=+&kallsyms_module_names[kallsyms_modules[mod_idx]];+iter->hint_builtin_module_idx=mod_idx;+}+#endifreturnoff-iter->nameoff;}
@@ -704,7 +818,7 @@ static int update_iter_mod(struct kallsym_iter *iter, loff_t pos)}/* Returns false if pos at or past end of file. */-staticintupdate_iter(structkallsym_iter*iter,loff_tpos)+staticintupdate_iter(structkallsym_iter*iter,loff_tpos,intkallmodsyms){/* Module symbols can be accessed randomly. */if(pos>=kallsyms_num_syms)
From: Nick Alcock <hidden> Date: 2021-09-29 21:52:51
These symbols are terribly inefficiently stored at the moment. Add a
simple optimizer which fuses obj2mod_elem entries and uses this to
implement three cheap optimizations:
- duplicate names are eliminated from .kallsyms_module_names.
- entries in .kallsyms_modules which point at single-file modules which
also appear in a multi-module list are redirected to point inside
that list, and the single-file entry is dropped from
.kallsyms_module_names. Thus, modules which contain some object
files shared with other modules and some object files exclusive to
them do not double up the module name. (There might still be some
duplication between multiple multi-module lists, but this is an
extremely marginal size effect, and resolving it would require an
extra layer of lookup tables which would be even more complex, and
incompressible to boot).
- Entries in .kallsyms_modules that would contain the same value after
the above optimizations are fused together, along with their
corresponding .kallsyms_module_addresses/offsets entries. Due to
this fusion process, and because object files can be split apart into
multiple parts by the linker for hot/cold partitioning and the like,
entries in .kallsyms_module_addresses/offsets no longer correspond
1:1 to object files, but more to some contiguous range of addresses
which are guaranteed to belong to a single built-in module, but which
may well stretch over multiple object files.
The optimizer's time complexity is O(log n) in the number of objfiles at
most (and probably much lower), so, given the relatively low number of
objfiles, its runtime overhead is in the noise.
Optimization reduces the overhead of the kallmodsyms tables by about
7500 items, dropping the .tmp_kallsyms2.o object file size by about
33KiB, leaving it 8672 bytes larger than before: a gain of .4%.
The vmlinux size is not yet affected because the variables are not used
and are eliminated by the linker: but if they were used (after the next
commit), the size impact of all of this on the final kernel is minimal:
in my testing, the kallsyms2.S file went from 14107772 to 14137245
bytes, a gain of 29743 bytes, or 0.16%: vmlinux gained 10824 bytes, a
gain of .017%, and the compressed vmlinux only 7552 bytes, a gain of
.08%: though the latter two values are very configuration-dependent,
they seem likely to scale roughly with the kernel they are part of.
Signed-off-by: Nick Alcock <redacted>
---
scripts/kallsyms.c | 267 +++++++++++++++++++++++++++++++++++++++++++--
1 file changed, 258 insertions(+), 9 deletions(-)
@@ -94,14 +105,24 @@ struct obj2mod_elem {size_tnmods;/* number of modules in "mods" */size_tmods_size;/* size of all mods together */intmod_offset;/* offset in .kallsyms_module_names */+/*+*Ifsetatemissiontime,thispointsatanotherobj2modentrythat+*containsthemodulenameweneed(possiblyataslightlylater+*offset,iftheentryisforanobjfilethatappearsinmanymodules).+*/+structobj2mod_elem*xref;structobj2mod_elem*obj2mod_next;+structobj2mod_elem*mod2obj_next;};/*-*Mapfromobjectfilestoobj2modentries(auniquemapping).+*Mapfromobjectfilestoobj2modentries(auniquemapping),andviceversa+*(notunique,butentriesforobjfilesinmorethanonemoduleinthishash+*areignored).*/staticstructobj2mod_elem*obj2mod[OBJ2MOD_N];+staticstructobj2mod_elem*mod2obj[OBJ2MOD_N];staticsize_tnum_objfiles;/*
@@ -177,6 +207,164 @@ static void obj2mod_add(char *obj, char *mod)fprintf(stderr,"kallsyms: out of memory\n");exit(1);}++/*+*Usedinsideoptimize_obj2modtoidentifyduplicatemoduleentries.+*/+structobj2mod_modhash_elem{+structobj2mod_elem*elem;+unsignedintmodhash;/* hash value of this entry */+};++staticintqstrcmp(constvoid*a,constvoid*b)+{+returnstrcmp((constchar*)a,(constchar*)b);+}++staticintqmodhash(constvoid*a,constvoid*b)+{+conststructobj2mod_modhash_elem*el_a=a;+conststructobj2mod_modhash_elem*el_b=b;+if(el_a->modhash<el_b->modhash)+return-1;+elseif(el_a->modhash>el_b->modhash)+return1;+return0;+}++/*+*AssociateallTUsinobj2modwhichrefertothesamemodulewithasingle+*obj2modentryforemission,preferringtopointintothemodulelistina+*multi-moduleobjfile.+*/+staticvoidoptimize_obj2mod(void)+{+size_ti;+size_tn=0;+structobj2mod_elem*elem;+structobj2mod_elem*dedup;+/* An array of all obj2mod_elems, later sorted by hashval. */+structobj2mod_modhash_elem*uniq;+structobj2mod_modhash_elem*last;++/*+*Canonicalizeallmodulelistsbysortingthem,thencomputetheir+*hashvalues.+*/+uniq=malloc(sizeof(structobj2mod_modhash_elem)*num_objfiles);+if(uniq==NULL)+gotooom;++for(i=0;i<OBJ2MOD_N;i++){+for(elem=obj2mod[i];elem;elem=elem->obj2mod_next){+if(elem->nmods>=2){+char**sorter;+char*walk;+char*tmp_mods;+size_tj;++tmp_mods=malloc(elem->mods_size);+sorter=malloc(sizeof(char*)*elem->nmods);+if(sorter==NULL||tmp_mods==NULL)+gotooom;+memcpy(tmp_mods,elem->mods,elem->mods_size);++for(j=0,walk=tmp_mods;j<elem->nmods;+j++){+sorter[j]=walk;+walk+=strlen(walk)+1;+}+qsort(sorter,elem->nmods,sizeof(char*),+qstrcmp);+for(j=0,walk=elem->mods;j<elem->nmods;+j++){+strcpy(walk,sorter[j]);+walk+=strlen(walk)+1;+}+free(tmp_mods);+free(sorter);+}++uniq[n].elem=elem;+uniq[n].modhash=memhash(elem->mods,elem->mods_size);+n++;+}+}++qsort(uniq,num_objfiles,sizeof(structobj2mod_modhash_elem),+qmodhash);++/*+*Workovermultimoduleentries.Thesemustbeemittedinto+*.kallsyms_module_namesasaunit,butwecanstilloptimizeby+*reusingsomeotheridenticalentry.Single-filemodulesareamenable+*tothesameoptimization,butweavoiddoingitfornowsothatwe+*canprefertopointthemdirectlyinsideamultimoduleentry.+*/+for(i=0,last=NULL;i<num_objfiles;i++){+constchar*onemod;+size_tj;++if(uniq[i].elem->nmods<2)+continue;++/* Duplicate multimodule. Reuse the first we saw. */+if(last!=NULL&&last->modhash==uniq[i].modhash){+uniq[i].elem->xref=last->elem;+continue;+}++/*+*Single-moduleentriesrelatingtomodulesalsoemittedas+*partofthismultimoduleentrycanrefertoit:later,we+*willhuntdowntherightspecificmodulenamewithinthis+*multimoduleentryandpointdirectlytoit.+*/+onemod=uniq[i].elem->mods;+for(j=uniq[i].elem->nmods;j>0;j--){+inth=strhash(onemod)&OBJ2MOD_MASK;++for(dedup=mod2obj[h];dedup;+dedup=dedup->mod2obj_next){+if(dedup->nmods>1)+continue;++if(strcmp(dedup->mods,onemod)!=0)+continue;+dedup->xref=uniq[i].elem;+assert(uniq[i].elem->xref==NULL);+}+onemod+=strlen(onemod)+1;+}++last=&uniq[i];+}++/*+*Nowtraverseallsingle-moduleentries,xreffingeveryonethat+*relatestoagivenmoduletothefirstonewesawthatreferstothat+*module.+*/+for(i=0,last=NULL;i<num_objfiles;i++){+if(uniq[i].elem->nmods>1)+continue;++if(uniq[i].elem->xref!=NULL)+continue;++/* Duplicate module name. Reuse the first we saw. */+if(last!=NULL&&last->modhash==uniq[i].modhash){+uniq[i].elem->xref=last->elem;+assert(last->elem->xref==NULL);+continue;+}+last=&uniq[i];+}+return;+oom:+fprintf(stderr,"kallsyms: out of memory optimizing module list\n");+exit(EXIT_FAILURE);+}#endif /* CONFIG_KALLMODSYMS */staticvoidusage(void)
From: Nick Alcock <hidden> Date: 2021-09-29 21:52:54
The mapping consists of three new symbols, computed by integrating the
information in the (just-added) .tmp_vmlinux.ranges and
modules_thick.builtin: taken together, they map address ranges
(corresponding to object files on the input) to the names of zero or
more modules containing those address ranges.
- kallsyms_module_addresses/kallsyms_module_offsets encodes the
address/offset of each object file (derived from the linker map), in
exactly the same way as kallsyms_addresses/kallsyms_offsets does
for symbols. There is no size: instead, the object files are assumed
to tile the address space. (This is slightly more space-efficient
than using a size). Non-text-section addresses are skipped: for now,
all the users of this interface only need module/non-module
information for instruction pointer addresses, not absolute-addressed
symbols and the like. This restriction can easily be lifted in
future. (Regarding the name: right now the entries correspond pretty
closely to object files, so we could call the section
kallsyms_objfiles or something, but the optimizer added in the next
commit will change this.)
- kallsyms_module_names encodes the name of each module in a modified
form of strtab: notably, if an object file appears in *multiple*
modules, all of which are built in, this is encoded via a zero byte,
a one-byte module count, then a series of that many null-terminated
strings. As a special case, the table starts with a single zero byte
which does *not* represent the start of a multi-module list.
- kallsyms_modules connects the two, encoding a table associated 1:1
with kallsyms_module_addresses / kallsyms_module_offsets, pointing
at an offset in kallsyms_module_names describing which module (or
modules, for a multi-module list) the code occupying this address
range is part of. If an address range is part of no module (always
built-in) it points at 0 (the null byte at the start of the
kallsyms_module_names list).
There is no optimization yet: kallsyms_modules and
kallsyms_module_names will almost certainly contain many duplicate
entries, and kallsyms_module_{addresses,offsets} may contain
consecutive entries that point to the same place. The size hit is
fairly substantial as a result, though still much less than a naive
implementation mapping each symbol to a module name would be: 50KiB or
so.
Signed-off-by: Nick Alcock <redacted>
---
Makefile | 2 +-
init/Kconfig | 8 +
scripts/Makefile | 6 +
scripts/kallsyms.c | 366 +++++++++++++++++++++++++++++++++++++++++++--
4 files changed, 371 insertions(+), 11 deletions(-)
@@ -1526,6 +1526,14 @@ config POSIX_TIMERSIfunsuresayy.+configKALLMODSYMS+defaulty+bool"Enable support for /proc/kallmodsyms"ifEXPERT+depends onKALLSYMS+help+Thisoptionenablesthe/proc/kallmodsymsfile,whichmapssymbols+toaddressesandtheirassociatedmodules.+configPRINTKdefaultybool"Enable support for printk"ifEXPERT
@@ -32,6 +32,12 @@ HOSTCFLAGS_sorttable.o += -DUNWINDER_ORC_ENABLEDHOSTLDLIBS_sorttable=-lpthreadendif+kallsyms-objs:=kallsyms.o++ifdef CONFIG_KALLMODSYMS+kallsyms-objs+=modules_thick.o+endif+# The following programs are only built on demandhostprogs+=unifdef
@@ -67,11 +74,116 @@ static int token_profit[0x10000];staticunsignedcharbest_table[256][2];staticunsignedcharbest_table_len[256];+#ifdef CONFIG_KALLMODSYMS+staticunsignedintstrhash(constchar*s)+{+/* fnv32 hash */+unsignedinthash=2166136261U;++for(;*s;s++)+hash=(hash^*s)*0x01000193;+returnhash;+}++#define OBJ2MOD_BITS 10+#define OBJ2MOD_N (1 << OBJ2MOD_BITS)+#define OBJ2MOD_MASK (OBJ2MOD_N - 1)+structobj2mod_elem{+char*obj;+char*mods;/* sorted module name strtab */+size_tnmods;/* number of modules in "mods" */+size_tmods_size;/* size of all mods together */+intmod_offset;/* offset in .kallsyms_module_names */+structobj2mod_elem*obj2mod_next;+};++/*+*Mapfromobjectfilestoobj2modentries(auniquemapping).+*/++staticstructobj2mod_elem*obj2mod[OBJ2MOD_N];+staticsize_tnum_objfiles;++/*+*Anorderedlistofaddressrangesandtheobjfilethatoccupiesthatrange.+*/+structaddrmap_entry{+unsignedlonglongaddr;+structobj2mod_elem*objfile;+};+staticstructaddrmap_entry*addrmap;+staticintaddrmap_num,addrmap_alloced;++staticvoidobj2mod_init(void)+{+memset(obj2mod,0,sizeof(obj2mod));+}++staticstructobj2mod_elem*obj2mod_get(constchar*obj)+{+inti=strhash(obj)&OBJ2MOD_MASK;+structobj2mod_elem*elem;++for(elem=obj2mod[i];elem;elem=elem->obj2mod_next){+if(strcmp(elem->obj,obj)==0)+returnelem;+}+returnNULL;+}++/*+*Notethatagivenobjectfileisfoundinsomemodule,interningitinthe+*obj2modhash.Shouldnotbecalledmorethanonceforanygiven(module,+*object)pair.+*/+staticvoidobj2mod_add(char*obj,char*mod)+{+inti=strhash(obj)&OBJ2MOD_MASK;+structobj2mod_elem*elem;++elem=obj2mod_get(obj);+if(!elem){+elem=malloc(sizeof(structobj2mod_elem));+if(!elem)+gotooom;+memset(elem,0,sizeof(structobj2mod_elem));+elem->obj=strdup(obj);+if(!elem->obj)+gotooom;+elem->mods=strdup(mod);+if(!elem->mods)+gotooom;++elem->obj2mod_next=obj2mod[i];+obj2mod[i]=elem;+num_objfiles++;+}else{+elem->mods=realloc(elem->mods,elem->mods_size++strlen(mod)+1);+if(!elem->mods)+gotooom;+strcpy(elem->mods+elem->mods_size,mod);+}++elem->mods_size+=strlen(mod)+1;+elem->nmods++;+if(elem->nmods>255){+fprintf(stderr,"kallsyms: %s: too many modules associated with this object file\n",+obj);+exit(EXIT_FAILURE);+}+return;+oom:+fprintf(stderr,"kallsyms: out of memory\n");+exit(1);+}+#endif /* CONFIG_KALLMODSYMS */staticvoidusage(void){-fprintf(stderr,"Usage: kallsyms [--all-symbols] "-"[--base-relative] < in.map > out.S\n");+fprintf(stderr,"Usage: kallsyms [--all-symbols] [--absolute-percpu] "+"[--base-relative] [--builtin=modules_thick.builtin] "+"< nm_vmlinux.out > symbols.S\n");exit(1);}
@@ -255,7 +373,7 @@ static int symbol_in_range(const struct sym_entry *s,for(i=0;i<entries;++i){ar=&ranges[i];-if(s->addr>=ar->start&&s->addr<=ar->end)+if(addr>=ar->start&&addr<=ar->end)return1;}
@@ -269,8 +387,8 @@ static int symbol_valid(const struct sym_entry *s)/* if --all-symbols is not specified, then symbols outside the text*andinittextsectionsarediscarded*/if(!all_symbols){-if(symbol_in_range(s,text_ranges,-ARRAY_SIZE(text_ranges))==0)+if(addr_in_range(s->addr,text_ranges,+ARRAY_SIZE(text_ranges))==0)return0;/* Corner case. Discard any symbols with the same value as*_etext_einittext;theycanmovebetweenpass1and2when
@@ -352,6 +470,121 @@ static void output_address(unsigned long long addr)printf("\tPTR\t_text - %#llx\n",_text-addr);}+#ifdef CONFIG_KALLMODSYMS+/* Output the .kallmodsyms_modules symbol content. */+staticvoidoutput_kallmodsyms_modules(void)+{+structobj2mod_elem*elem;+size_toffset=1;+size_ti;++/*+*Traverseandemit,updatingmod_offsetaccordingly.+*Emitasingle\0atthestart,toencodenon-modularobjfiles.+*/+output_label("kallsyms_module_names");+printf("\t.byte\t0\n");+for(i=0;i<OBJ2MOD_N;i++){+for(elem=obj2mod[i];elem;+elem=elem->obj2mod_next){+constchar*onemod;+size_ti;++elem->mod_offset=offset;+onemod=elem->mods;++/*+*Technicallythisisawasteofspace:wecouldjust+*aswellimplementmultimoduleentriesbypointingone+*bytefurtherback,tothetrailing\0oftheprevious+*entry,butdoingitthiswaymakesitmoreobvious+*whenanentryisamultimoduleentry.+*/+if(elem->nmods!=1){+printf("\t.byte\t0\n");+printf("\t.byte\t%zi\n",elem->nmods);+offset+=2;+}++for(i=elem->nmods;i>0;i--){+printf("\t.asciz\t\"%s\"\n",onemod);+offset+=strlen(onemod)+1;+onemod+=strlen(onemod)+1;+}+}+}+printf("\n");+output_label("kallsyms_module_names_len");+printf("\t.long\t%zi\n",offset);+}++staticvoidoutput_kallmodsyms_objfiles(void)+{+size_ti=0;+size_temitted_offsets=0;+size_temitted_objfiles=0;++if(base_relative)+output_label("kallsyms_module_offsets");+else+output_label("kallsyms_module_addresses");++for(i=0;i<addrmap_num;i++){+longlongoffset;+intoverflow;++if(base_relative){+if(!absolute_percpu){+offset=addrmap[i].addr-relative_base;+overflow=(offset<0||offset>UINT_MAX);+}else{+offset=relative_base-addrmap[i].addr-1;+overflow=(offset<INT_MIN||offset>=0);+}+if(overflow){+fprintf(stderr,"kallsyms failure: "+"objfile %s at address %#llx out of range in relative mode\n",+addrmap[i].objfile?addrmap[i].objfile->obj:+"in always-built-in object",table[i]->addr);+exit(EXIT_FAILURE);+}+printf("\t.long\t0x%x\n",(int)offset);+}else+printf("\tPTR\t%#llx\n",addrmap[i].addr);+emitted_offsets++;+}++output_label("kallsyms_modules");++for(i=0;i<addrmap_num;i++){+structobj2mod_elem*elem=addrmap[i].objfile;+/*+*Addressrangecitesnoobjectfile:pointat0,thebuilt-in+*module.+*/+if(addrmap[i].objfile==NULL){+printf("\t.long\t0x0\n");+emitted_objfiles++;+continue;+}++/*+*Zerooffsetistheinitial\0,theretocatchuninitialized+*obj2modentries,andisforbidden.+*/+assert(elem->mod_offset!=0);++printf("\t.long\t0x%x\n",elem->mod_offset);+emitted_objfiles++;+}++assert(emitted_offsets==emitted_objfiles);+output_label("kallsyms_num_modules");+printf("\t.long\t%zi\n",emitted_objfiles);+printf("\n");+}+#endif /* CONFIG_KALLMODSYMS */+/* uncompress a compressed symbol. When this function is called, the best table*mightstillbecompresseditself,sothefunctionneedstoberecursive*/staticintexpand_symbol(constunsignedchar*data,intlen,char*result)
On Wed, Sep 29, 2021 at 10:51:47PM +0100, Nick Alcock wrote:
It would be useful if there were a mapping between kernel symbol and module
name that only changed when the kernel source code is changed. This mapping
should not change simply because a module becomes built into the kernel.
It might also be useful if there were reliable symbol size information to
determine whether an address is within a symbol or outside it, especially
given that there could be huge gaps between symbols.
This is a pretty cool series, but I'm left wondering "for what reason?" :)
Perhaps I missed the specific rationale; there was a lot to run. ;)
It would be useful, sure, but is there something that does, in fact,
need this, or would like this if it were available? Since this provides
a userspace API, what would be consuming that API? For example, when
Syscall User Dispatch was added, it was clear it was for Wine[1].
-Kees
[1] https://lore.kernel.org/lkml/160690190770.3364.5119373826178425644.tip-bot2@tip-bot2/
--
Kees Cook
From: Nick Alcock <hidden> Date: 2021-10-27 17:47:43
/proc/kallsyms is very useful for tracers and other tools that need to
map kernel symbols to addresses.
It would be useful if there were a mapping between kernel symbol and module
name that only changed when the kernel source code is changed. This mapping
should not change simply because a module becomes built into the kernel.
It might also be useful if there were reliable symbol size information to
determine whether an address is within a symbol or outside it, especially
given that there could be huge gaps between symbols.
Fix this by introducing a new config parameter CONFIG_KALLMODSYMS, which
does several things at once (introduced in distinct commits inthis series).
Generate a file "modules_thick.builtin" that maps from the thin archives
that make up built-in modules to their constituent object files. (This
reintroduces the machinery that used to be used to generate
modules.builtin. I am not wedded to this mechanism: if someone can
figure out a mechanism that does not require recursing over the entire
build tree, I'm happy to use it, but I suspect that no such mechanism
exists, since the only place the mapping from object file to module
exists is in the makefiles themselves. Regardless, this is fairly cheap,
adding less than a second to a typical hot-cache build of a large
enterprise kernel. This is true even though it needs to be run
unconditionally whenever the .config changes.)
Generate a linker map ".tmp_vmlinux.map", converting it into
".tmp_vmlinux.ranges", mapping address ranges to object files.
Have scripts/kallsyms read these two new files to map symbol addresses
to built-in-module names and then write a mapping from object file
address to module name to the *.s output file.
The mapping consists of three new symbols:
- kallsyms_module_addresses/kallsyms_module_offsets encodes the
address/offset of each object file (derived from the linker map), in
exactly the same way as kallsyms_addresses/kallsyms_offsets does
for symbols. There is no size: instead, the object files are
assumed to tile the address space. (This is slightly more
space-efficient than using a size). Non-text-section addresses are
skipped: for now, all the users of this interface only need
module/non-module information for instruction pointer addresses, not
absolute-addressed symbols and the like. This restriction can
easily be lifted in future. (For why this isn't called
kallsyms_objfiles, see two entries below.)
- kallsyms_module_names encodes the name of each module in a modified
form of strtab: notably, if an object file appears in *multiple*
modules, all of which are built in, this is encoded via a zero byte,
a one-byte module count, then a series of that many null-terminated
strings. Object files which appear in only one module in such a
multi-module list are redirected to point inside that list, so that
modules which contain some object files shared with other modules
and some object files exclusive to them do not double up the module
name. (There might still be some duplication between multiple
multi-module lists, but this is an extremely marginal size effect,
and resolving it would require an extra layer of lookup tables which
would be even more complex, and incompressible to boot). As a
special case, the table starts with a single zero byte which does
*not* represent the start of a multi-module list.
- kallsyms_modules connects the two, encoding a table associated 1:1
with kallsyms_module_addresses / kallsyms_module_offsets, pointing
at an offset in kallsyms_module_names describing which module (or
modules, for a multi-module list) the code occupying this address
range is part of. If an address range is part of no module (always
built-in) it points at 0 (the null byte at the start of the
kallsyms_module_names list). Entries in this list that would
contain the same value are fused together, along with their
corresponding kallsyms_module_addresses/offsets entries. Due to
this fusion process, and because object files can be split apart into
multiple parts by the linker for hot/cold partitioning and the like,
entries in here do not really correspond to an object file, but more
to some contiguous range of addresses which are guaranteed to belong
to a single built-in module: so it seems best to call the symbols
kallsyms_modules*. (The generator has a data structure that does
correspond more closely to object files, from which kallsyms_modules
is generated, and that does use 'objfiles' terminology.)
Emit a new /proc/kallmodsyms file akin to /proc/kallsyms but with built-in
module names, using a new kallsyms_builtin_module_address() almost identical
to kallsyms_sym_address() to get the address corresponding to a given
.kallsyms_modules index, and a new get_builtin_module_idx quite similar to
get_symbol_pos to determine the index in the .kallsyms_modules array that
relates to a given address. Save a little time by exploiting the fact that
all callers will only ever traverse this list from start to end by allowing
them to pass in the previous index returned from this function as a hint:
thus very few bsearches are actually needed. (In theory this could change
to just walk straight down kallsyms_module_addresses/offsets and not bother
bsearching at all, but doing it this way is hardly any slower and much more
robust.)
The display process is complicated a little by the weird format of the
.kallsyms_module_names table: we have to look for multimodule entries
and print them as space-separated lists of module names.
The resulting /proc/kallmodsyms file looks like this:
ffffffff8b013d20 409 t pt_buffer_setup_aux
ffffffff8b014130 11f T intel_pt_interrupt
ffffffff8b014250 2d T cpu_emergency_stop_pt
ffffffff8b014280 13a t rapl_pmu_event_init [intel_rapl_perf]
ffffffff8b0143c0 bb t rapl_event_update [intel_rapl_perf]
ffffffff8b014480 10 t rapl_pmu_event_read [intel_rapl_perf]
ffffffff8b014490 a3 t rapl_cpu_offline [intel_rapl_perf]
ffffffff8b014540 24 t __rapl_event_show [intel_rapl_perf]
ffffffff8b014570 f2 t rapl_pmu_event_stop [intel_rapl_perf]
This is emitted even if intel_rapl_perf is built into the kernel.
Further down, we see what happens when object files are reused by
multiple modules, all of which are built in to the kernel:
ffffffffa22b3aa0 ab t handle_timestamp [liquidio]
ffffffffa22b3b50 4a t free_netbuf [liquidio]
ffffffffa22b3ba0 8d t liquidio_ptp_settime [liquidio]
ffffffffa22b3c30 b3 t liquidio_ptp_adjfreq [liquidio]
[...]
ffffffffa22b9490 203 t lio_vf_rep_create [liquidio]
ffffffffa22b96a0 16b t lio_vf_rep_destroy [liquidio]
ffffffffa22b9810 1f t lio_vf_rep_modinit [liquidio]
ffffffffa22b9830 1f t lio_vf_rep_modexit [liquidio]
ffffffffa22b9850 d2 t lio_ethtool_get_channels [liquidio] [liquidio_vf]
ffffffffa22b9930 9c t lio_ethtool_get_ringparam [liquidio] [liquidio_vf]
ffffffffa22b99d0 11 t lio_get_msglevel [liquidio] [liquidio_vf]
ffffffffa22b99f0 11 t lio_vf_set_msglevel [liquidio] [liquidio_vf]
ffffffffa22b9a10 2b t lio_get_pauseparam [liquidio] [liquidio_vf]
ffffffffa22b9a40 738 t lio_get_ethtool_stats [liquidio] [liquidio_vf]
ffffffffa22ba180 368 t lio_vf_get_ethtool_stats [liquidio] [liquidio_vf]
ffffffffa22ba4f0 37 t lio_get_regs_len [liquidio] [liquidio_vf]
ffffffffa22ba530 18 t lio_get_priv_flags [liquidio] [liquidio_vf]
ffffffffa22ba550 2e t lio_set_priv_flags [liquidio] [liquidio_vf]
ffffffffa22ba580 69 t lio_set_fecparam [liquidio] [liquidio_vf]
ffffffffa22ba5f0 92 t lio_get_fecparam [liquidio] [liquidio_vf]
[...]
ffffffffa22cbd10 175 t liquidio_set_mac [liquidio_vf]
ffffffffa22cbe90 ab t handle_timestamp [liquidio_vf]
ffffffffa22cbf40 4a t free_netbuf [liquidio_vf]
ffffffffa22cbf90 2b t octnet_link_status_change [liquidio_vf]
ffffffffa22cbfc0 7e t liquidio_vxlan_port_command.constprop.0 [liquidio_vf]
Like /proc/kallsyms, the output is driven by address, so keeps the
curious property of /proc/kallsyms that symbols (like free_netbuf above)
may appear repeatedly with different addresses: but now, unlike in
/proc/kallsyms, we can see that those symbols appear repeatedly because
they are *different symbols* that ultimately belong to different
modules, all of which are built in to the kernel.
Those symbols that come from object files that are genuinely reused and
that appear only once in meory get a /proc/kallmodsyms line with
[multiple] [modules] on it: consumers will have to be ready to handle
such lines.
Also, kernel symbols for built-in modules will probably appear
interspersed with other symbols that are part of different modules and
non-modular always-built-in symbols, which, as usual, have no
square-bracketed module denotation.
As with /proc/kallsyms, non-root usage produces addresses that are
all zero.
I am open to changing the name and/or format of /proc/kallmodsyms, but felt
it best to split it out of /proc/kallsyms to avoid breaking existing
kallsyms parsers. Another possible syntax might be to use {curly brackets}
or something to denote built-in modules: it might be possible to drop
/proc/kallmodsyms and make /proc/kallsyms emit things in this format.
(Equally, now kallmodsyms data uses very little space, the
CONFIG_KALLMODSYMS config option might be something people don't want to
bother with.)
The size impact of all of this is minimal: for the case above, the
kallsyms2.S file went from 14107772 to 14137245 bytes, a gain of 29743
bytes, or 0.16%: vmlinux gained 10824 bytes, a gain of .017%, and the
compressed vmlinux only 7552 bytes, a gain of .08%: though the latter
two values are very configuration-dependent, they seem likely to scale
roughly with the kernel they are part of.
The last patch is an RFC to see if the idea is considered to be worth
spending more time optimizing the representation, which adds a new
kallsyms_sizes section that gives the size of each symbol, and uses this
info to report reliable symbol sizes to in-kernel users, and (via a new
column in /proc/kallmodsyms) to out-of-kernel users too. Having reliable
size info lets us identify inter-symbol gaps and sort symbols so that
start/end-marker and overlapping symbols are consistently ordered with
respect to the symbols they overlap. This certainly uses too much space
right now, 200KiB--1MiB: a better representation is certainly needed. One
that springs to mind is making the table sparse (pairs of symbol
index/size), and recording explicit sizes only for those symbols that
are not immediately followed by a subsequent symbol.
Differences from v4, two months ago:
- Fix building of tristate.conf if missing (usually concealed by the
syncconfig being run for other reasons, but not always: the kernel
test robot spotted it).
- Forward-port atop v5.15-rc3.
Differences from v3, a month earlier:
- Fix a kernel test robot warning in get_ksymbol_core (possible
use of uninitialized variable if kallmodsyms was wanted but
kallsyms_module_offsets was not present, which is most unlikely).
Differences from v2, a couple of months before that:
- Split the series up. In particular, the size impact of the table
optimizer is now quantified, and the symbol-size patch is split out and
turned into an RFC patch, with the /proc/kallmodsyms format before that
patch lacking a size column. Some speculation on how to make the symbol
sizes less space-wasteful is added (but not yet implemented).
- Drop a couple of unnecessary #includes, one unnecessarily exported
symbol, and a needless de-staticing.
Differences from v1, a year or so back:
- Move from a straight symbol->module name mapping to a mapping from
address-range to TU to module name list, bringing major space savings
over the previous approach and support for object files used by many
built-in modules at the same time, at the cost of a slightly more complex
approach (unavoidably so, I think, given that we have to merge three data
sources together: the link map in .tmp_vmlinux.ranges, the nm output on
stdin, and the mapping from TU name to module names in
modules_thick.builtin).
We do opportunistic merging of TUs if they cite the same modules and
reuse module names where doing so is simple: see optimize_obj2mod
below. I considered more extensive searches for mergeable entries and
more intricate encodings of the module name list allowing TUs that are
used by overlapping sets of modules to share their names, but such
modules are rare enough (and such overlapping sharings are vanishingly
rare) that it seemed likely to save only a few bytes at the cost of much
more hard-to-test code. This is doubly true now that the tables needed
are only a few kilobytes in length.
Signed-off-by: Nick Alcock <redacted>
Signed-off-by: Eugene Loh <redacted>
Reviewed-by: Kris Van Hees <redacted>
Nick Alcock (7):
kbuild: bring back tristate.conf
kbuild: add modules_thick.builtin
kbuild: generate an address ranges map at vmlinux link time
kallsyms: introduce sections needed to map symbols to built-in modules
kallsyms: optimize .kallsyms_modules*
kallsyms: add /proc/kallmodsyms
kallsyms: add reliable symbol size info
.gitignore | 1 +
Documentation/dontdiff | 1 +
Makefile | 23 +-
include/linux/module.h | 7 +-
init/Kconfig | 8 +
kernel/kallsyms.c | 304 ++++++++++++++---
kernel/module.c | 4 +-
scripts/Kbuild.include | 6 +
scripts/Makefile | 6 +
scripts/Makefile.modbuiltin | 56 ++++
scripts/kallsyms.c | 642 +++++++++++++++++++++++++++++++++++-
scripts/kconfig/confdata.c | 41 ++-
scripts/link-vmlinux.sh | 22 +-
scripts/modules_thick.c | 200 +++++++++++
scripts/modules_thick.h | 48 +++
15 files changed, 1301 insertions(+), 68 deletions(-)
create mode 100644 scripts/Makefile.modbuiltin
create mode 100644 scripts/modules_thick.c
create mode 100644 scripts/modules_thick.h
--
2.33.0.256.gb827f06fa9
From: Nick Alcock <hidden> Date: 2021-10-27 17:47:47
tristate.conf was dropped because it is not needed to build a
modules.builtin (although dropping it introduces a few false positives
into modules.builtin support), and doing so avoids one round of
recursion through the build tree to build it. But kallmodsyms support
requires building a mapping from object file name to built-in module
name for all builtin modules: this seems to me impossible to accomplish
without parsing all makefiles under the influence of tristate.conf,
since the makefiles are the only place this mapping is recorded.
So bring it back for this purpose.
This partially reverts commit 8b41fc4454e36fbfdbb23f940d023d4dece2de29.
Signed-off-by: Nick Alcock <redacted>
---
Makefile | 2 +-
scripts/kconfig/confdata.c | 41 ++++++++++++++++++++++++++++++++++++--
2 files changed, 40 insertions(+), 3 deletions(-)
@@ -724,7 +724,7 @@ $(KCONFIG_CONFIG):## Do not use $(call cmd,...) here. That would suppress prompts from syncconfig,# so you cannot notice that Kconfig is waiting for the user input.-%/config/auto.conf %/config/auto.conf.cmd %/generated/autoconf.h:$(KCONFIG_CONFIG)+%/config/auto.conf %/config/auto.conf.cmd %/generated/autoconf.h %/tristate.conf:$(KCONFIG_CONFIG)$(Q)$(kecho)" SYNC $@"$(Q)$(MAKE)-f$(srctree)/Makefilesyncconfigelse # !may-sync-config
@@ -1058,7 +1077,7 @@ int conf_write_autoconf(int overwrite)structsymbol*sym;constchar*name;constchar*autoconf_name=conf_get_autoconfig_name();-FILE*out,*out_h;+FILE*out,*tristate,*out_h;inti;if(!overwrite&&is_present(autoconf_name))
@@ -1073,6 +1092,13 @@ int conf_write_autoconf(int overwrite)if(!out)return1;+tristate=fopen(".tmpconfig_tristate","w");+if(!tristate){+fclose(out);+fclose(tristate);+return1;+}+out_h=fopen(".tmpconfig.h","w");if(!out_h){fclose(out);
@@ -1080,6 +1106,7 @@ int conf_write_autoconf(int overwrite)}conf_write_heading(out,&kconfig_printer_cb,NULL);+conf_write_heading(tristate,&tristate_printer_cb,NULL);conf_write_heading(out_h,&header_printer_cb,NULL);for_all_symbols(i,sym){
@@ -1087,11 +1114,13 @@ int conf_write_autoconf(int overwrite)if(!(sym->flags&SYMBOL_WRITE)||!sym->name)continue;-/* write symbols to auto.conf and autoconf.h */+/* write symbols to auto.conf, tristate and autoconf.h */conf_write_symbol(out,sym,&kconfig_printer_cb,(void*)1);+conf_write_symbol(tristate,sym,&tristate_printer_cb,(void*)1);conf_write_symbol(out_h,sym,&header_printer_cb,NULL);}fclose(out);+fclose(tristate);fclose(out_h);name=getenv("KCONFIG_AUTOHEADER");
@@ -1102,6 +1131,14 @@ int conf_write_autoconf(int overwrite)if(rename(".tmpconfig.h",name))return1;+name=getenv("KCONFIG_TRISTATE");+if(!name)+name="include/config/tristate.conf";+if(make_parent_dir(name))+return1;+if(rename(".tmpconfig_tristate",name))+return1;+if(make_parent_dir(autoconf_name))return1;/*
From: Nick Alcock <hidden> Date: 2021-10-27 17:47:48
This is similar to modules.builtin, and constructed in a similar way to
the way that used to be built before commit
8b41fc4454e36fbfdbb23f940d023d4dece2de29, via tristate.conf inclusion
and recursive concatenation up the tree. Unlike modules.builtin,
modules_thick.builtin givs the names of the object files that make up
modules that are comprised of more than one object file, using a syntax
similar to that of makefiles, e.g.:
crypto/crypto.o: crypto/api.o crypto/cipher.o crypto/compress.o crypto/memneq.o
crypto/crypto_algapi.o: crypto/algapi.o crypto/proc.o crypto/scatterwalk.o
crypto/aead.o:
crypto/geniv.o:
(where the latter two are single-file modules).
An upcoming commit will use this mapping to populate /proc/kallmodsyms.
A parser is included that yields a stram of (module, objfile name[])
mappings: it's a bit baroque, but then parsing text files in C is quite
painful, and I'd rather put the complexity in here than in its callers.
The parser is not built in this commit, nor does it have any callers
yet; nor is any rule added that causes modules_thick.builtin to actually
be constructed. (Again, see a later commit for that.)
I am not wedded to the approach used to construct this file, but I don't
see any other way to do it despite spending a week or so trying to tie
it into Kbuild without using a separate Makefile.modbuiltin: unlike the
names of builtin modules (which are also recorded in the source files
themseves via MODULE_*() macros) the mapping from object file name to
built-in module name is not recorded anywhere but in the makefiles
themselves, so we have to at least reparse them with something to
indicate the builtin-ness of each module (i.e., tristate.conf) if we are
to figure out which modules are built-in and which are not.
Signed-off-by: Nick Alcock <redacted>
---
.gitignore | 1 +
Documentation/dontdiff | 1 +
Makefile | 19 +++-
scripts/Kbuild.include | 6 ++
scripts/Makefile.modbuiltin | 56 ++++++++++
scripts/modules_thick.c | 200 ++++++++++++++++++++++++++++++++++++
scripts/modules_thick.h | 48 +++++++++
7 files changed, 330 insertions(+), 1 deletion(-)
create mode 100644 scripts/Makefile.modbuiltin
create mode 100644 scripts/modules_thick.c
create mode 100644 scripts/modules_thick.h
@@ -1507,6 +1507,23 @@ __modinst_pre:endif # CONFIG_MODULES+# modules_thick.builtin maps from kernel modules (or rather the object file+# names they would have had had they not been built in) to their constituent+# object files: we can use this to determine which modules any given object+# file is part of. (We cannot eliminate the slight redundancy here without+# double-expansion.)++modthickbuiltin-files:=$(addsuffix/modules_thick.builtin,$(build-dirs))++modules_thick.builtin:$(modthickbuiltin-files)+$(Q)$(AWK)'!x[$$0]++'$(addsuffix/$@,$(build-dirs))>$@++# tristate.conf is not included from this Makefile. Add it as a prerequisite+# here to make it self-healing in case somebody accidentally removes it.+$(modthickbuiltin-files):include/config/tristate.conf+$(Q)$(MAKE)$(modbuiltin)=$(patsubst%/modules_thick.builtin,%,$@)builtin-file=modules_thick.builtin++#### Cleaning is done on three levels.# make clean Delete most generated files
@@ -0,0 +1,56 @@+# SPDX-License-Identifier: GPL-2.0+# ==========================================================================+# Generating modules_thick.builtin+# ==========================================================================++src:=$(obj)++PHONY:=__modbuiltin+__modbuiltin:++include include/config/auto.conf+# tristate.conf sets tristate variables to uppercase 'Y' or 'M'+# That way, we get the list of built-in modules in obj-Y+include include/config/tristate.conf++include scripts/Kbuild.include++ifdef building_out_of_srctree+# Create output directory if not already present+_dummy:=$(shell[-d$(obj)]||mkdir-p$(obj))+endif++# The filename Kbuild has precedence over Makefile+kbuild-dir:=$(if$(filter/%,$(src)),$(src),$(srctree)/$(src))+kbuild-file:=$(if$(wildcard$(kbuild-dir)/Kbuild),$(kbuild-dir)/Kbuild,$(kbuild-dir)/Makefile)+include $(kbuild-file)++include scripts/Makefile.lib++modthickbuiltin-subdirs:=$(patsubst%,%/modules_thick.builtin,$(subdir-ym))+modthickbuiltin-target:=$(obj)/modules_thick.builtin++__modbuiltin:$(obj)/$(builtin-file)$(subdir-ym)+@:++$(modthickbuiltin-target):$(subdir-ym)FORCE+$(Q)rm-f$@+$(Q)$(foreachmod-o,$(filter%.o,$(obj-Y)),\+printf"%s:"$(addprefix$(obj)/,$(mod-o))>>$@;\+printf" %s"$(sort$(strip$(addprefix$(obj)/,$($(mod-o:.o=-objs))\+$($(mod-o:.o=-y))$($(mod-o:.o=-Y)))))>>$@;\+printf"\n">>$@;)\+cat/dev/null$(modthickbuiltin-subdirs)>>$@;++PHONY+=FORCE++FORCE:++# Descending+# ---------------------------------------------------------------------------++PHONY+=$(subdir-ym)+$(subdir-ym):+$(Q)$(MAKE)$(modbuiltin)=$@builtin-file=$(builtin-file)++.PHONY:$(PHONY)
@@ -0,0 +1,200 @@+/* SPDX-License-Identifier: GPL-2.0 */+/*+*Asimplemodules_thickreader.+*+*(C)2014,2021Oracle,Inc.Allrightsreserved.+*+*Thisprogramisfreesoftware;youcanredistributeitand/ormodify+*itunderthetermsoftheGNUGeneralPublicLicenseaspublishedby+*theFreeSoftwareFoundation;eitherversion2oftheLicense,or+*(atyouroption)anylaterversion.+*/++#include<errno.h>+#include<stdio.h>+#include<stdlib.h>+#include<string.h>++#include"modules_thick.h"++/*+*Readamodules_thick.builtinfileandtranslateitintoastreamof+*name/module-namepairs.+*/++/*+*Constructamodules_thick.builtiniterator.+*/+structmodules_thick_iter*+modules_thick_iter_new(constchar*modules_thick_file)+{+structmodules_thick_iter*i;++i=calloc(1,sizeof(structmodules_thick_iter));+if(i==NULL)+returnNULL;++i->f=fopen(modules_thick_file,"r");++if(i->f==NULL){+fprintf(stderr,"Cannot open builtin module file %s: %s\n",+modules_thick_file,strerror(errno));+returnNULL;+}++returni;+}++/*+*Iterate,returninganewnull-terminatedarrayofobjectfilenames,anda+*newdynamically-allocatedmodulename.(Themodulenamepassedinisfreed.)+*+*Thearrayofobjectfilenamesshouldbefreedbythecaller:thestringsit+*pointstoareownedbytheiterator,andshouldnotbefreed.+*/++char**__attribute__((__nonnull__))+modules_thick_iter_next(structmodules_thick_iter*i,char**module_name)+{+size_tnpaths=1;+char**module_paths;+char*last_slash;+char*last_dot;+char*trailing_linefeed;+char*object_name=i->line;+char*dash;+intcomposite=0;++/*+*Readinallmoduleentries,computingthesuffixless,pathlessname+*ofthemoduleandbuildingthenextarrayfulofobjectfilenamesfor+*return.+*+*Modulescanconsistofmultiplefiles:inthiscase,theportion+*beforethecolonisthepathtothemodule(asbefore):theportion+*afterthecolonisaspace-separatedlistoffilesthatshouldbe+*consideredpartofthismodule.Inthiscase,theportionbeforethe+*nameisan"object file"thatdoesnotactuallyexist:itismerged+*intobuilt-in.awithouteverbeingwrittenout.+*+*Allmodulenameshave-translatedto_,tomatchwhatisdonetothe+*namesofthesamethingswhenbuiltasmodules.+*/++/*+*Reinvocationofexhaustediterator.ReturnNULL,once.+*/+retry:+if(getline(&i->line,&i->line_size,i->f)<0){+if(ferror(i->f)){+fprintf(stderr,"Error reading from modules_thick file:"+" %s\n",strerror(errno));+exit(1);+}+rewind(i->f);+returnNULL;+}++if(i->line[0]=='\0')+gotoretry;++/*+*Slicethelineintwoatthecolon,ifany.Ifthereisanything+*pastthe':',thisisacompositemodule.(Weallowfornocolon+*forrobustness,eventhoughoneshouldalwaysbepresent.)+*/+if(strchr(i->line,':')!=NULL){+char*name_start;++object_name=strchr(i->line,':');+*object_name='\0';+object_name++;+name_start=object_name+strspn(object_name," \n");+if(*name_start!='\0'){+composite=1;+object_name=name_start;+}+}++/*+*Figureoutthemodulename.+*/+last_slash=strrchr(i->line,'/');+last_slash=(!last_slash)?i->line:+last_slash+1;+free(*module_name);+*module_name=strdup(last_slash);+dash=*module_name;++while(dash!=NULL){+dash=strchr(dash,'-');+if(dash!=NULL)+*dash='_';+}++last_dot=strrchr(*module_name,'.');+if(last_dot!=NULL)+*last_dot='\0';++trailing_linefeed=strchr(object_name,'\n');+if(trailing_linefeed!=NULL)+*trailing_linefeed='\0';++/*+*Multifileseparator?Objectfilenamesexplicitlystated:+*slicethemupandshufflethemin.+*+*Thearraysizemaybeanoverestimateifanyobjectfile+*namesstartorendwithspaces(veryunlikely)butcannotbe+*anunderestimate.(Checkforitanyway.)+*/+if(composite){+char*one_object;++for(npaths=0,one_object=object_name;+one_object!=NULL;+npaths++,one_object=strchr(one_object+1,' '));+}++module_paths=malloc((npaths+1)*sizeof(char*));+if(!module_paths){+fprintf(stderr,"%s: out of memory on module %s\n",__func__,+*module_name);+exit(1);+}++if(composite){+char*one_object;+size_ti=0;++while((one_object=strsep(&object_name," "))!=NULL){+if(i>=npaths){+fprintf(stderr,"%s: num_objs overflow on module "+"%s: this is a bug.\n",__func__,+*module_name);+exit(1);+}++module_paths[i++]=one_object;+}+}else+module_paths[0]=i->line;/* untransformed module name */++module_paths[npaths]=NULL;++returnmodule_paths;+}++/*+*Freeaniterator.Canbecalledwhileiterationisunderway,soeven+*statethatisfreedattheendofiterationmustbefreedheretoo.+*/+void+modules_thick_iter_free(structmodules_thick_iter*i)+{+if(i==NULL)+return;+fclose(i->f);+free(i->line);+free(i);+}
From: Nick Alcock <hidden> Date: 2021-10-27 17:47:50
This emits a new file, .tmp_vmlinux.ranges, which maps address
range/size pairs in vmlinux to the object files which make them up,
e.g., in part:
0x0000000000000000 0x30 arch/x86/kernel/cpu/common.o
0x0000000000001000 0x1000 arch/x86/events/intel/ds.o
0x0000000000002000 0x4000 arch/x86/kernel/irq_64.o
0x0000000000006000 0x5000 arch/x86/kernel/process.o
0x000000000000b000 0x1000 arch/x86/kernel/cpu/common.o
0x000000000000c000 0x5000 arch/x86/mm/cpu_entry_area.o
0x0000000000011000 0x10 arch/x86/kernel/espfix_64.o
0x0000000000011010 0x2 arch/x86/kernel/cpu/common.o
[...]
In my simple tests this seems to work with clang too, but if I'm not
sure how stable the format of clang's linker mapfiles is: if it turns
out not to work in some versions, the mapfile-massaging awk script added
here might need some adjustment.
Signed-off-by: Nick Alcock <redacted>
---
scripts/link-vmlinux.sh | 15 ++++++++++++++-
1 file changed, 14 insertions(+), 1 deletion(-)
@@ -248,6 +248,19 @@ kallsyms(){localkallsymopt;+# read the linker map to identify ranges of addresses:+# - for each *.o file, report address, size, pathname+# - most such lines will have four fields+# - but sometimes there is a line break after the first field+# - start reading at "Linker script and memory map"+# - stop reading at ".brk"+${AWK}'+/\.o$/&&start==1{print$(NF-2),$(NF-1),$NF}+/^Linkerscriptandmemorymap/{start=1}+/^\.brk/{exit(0)}+'.tmp_vmlinux.map|sort>.tmp_vmlinux.ranges++# get kallsyms optionsif[-n"${CONFIG_KALLSYMS_ALL}"];thenkallsymopt="${kallsymopt} --all-symbols"fi
From: Nick Alcock <hidden> Date: 2021-10-27 17:47:54
The mapping consists of three new symbols, computed by integrating the
information in the (just-added) .tmp_vmlinux.ranges and
modules_thick.builtin: taken together, they map address ranges
(corresponding to object files on the input) to the names of zero or
more modules containing those address ranges.
- kallsyms_module_addresses/kallsyms_module_offsets encodes the
address/offset of each object file (derived from the linker map), in
exactly the same way as kallsyms_addresses/kallsyms_offsets does
for symbols. There is no size: instead, the object files are assumed
to tile the address space. (This is slightly more space-efficient
than using a size). Non-text-section addresses are skipped: for now,
all the users of this interface only need module/non-module
information for instruction pointer addresses, not absolute-addressed
symbols and the like. This restriction can easily be lifted in
future. (Regarding the name: right now the entries correspond pretty
closely to object files, so we could call the section
kallsyms_objfiles or something, but the optimizer added in the next
commit will change this.)
- kallsyms_module_names encodes the name of each module in a modified
form of strtab: notably, if an object file appears in *multiple*
modules, all of which are built in, this is encoded via a zero byte,
a one-byte module count, then a series of that many null-terminated
strings. As a special case, the table starts with a single zero byte
which does *not* represent the start of a multi-module list.
- kallsyms_modules connects the two, encoding a table associated 1:1
with kallsyms_module_addresses / kallsyms_module_offsets, pointing
at an offset in kallsyms_module_names describing which module (or
modules, for a multi-module list) the code occupying this address
range is part of. If an address range is part of no module (always
built-in) it points at 0 (the null byte at the start of the
kallsyms_module_names list).
There is no optimization yet: kallsyms_modules and
kallsyms_module_names will almost certainly contain many duplicate
entries, and kallsyms_module_{addresses,offsets} may contain
consecutive entries that point to the same place. The size hit is
fairly substantial as a result, though still much less than a naive
implementation mapping each symbol to a module name would be: 50KiB or
so.
Signed-off-by: Nick Alcock <redacted>
---
Makefile | 2 +-
init/Kconfig | 8 +
scripts/Makefile | 6 +
scripts/kallsyms.c | 366 +++++++++++++++++++++++++++++++++++++++++++--
4 files changed, 371 insertions(+), 11 deletions(-)
@@ -1526,6 +1526,14 @@ config POSIX_TIMERSIfunsuresayy.+configKALLMODSYMS+defaulty+bool"Enable support for /proc/kallmodsyms"ifEXPERT+depends onKALLSYMS+help+Thisoptionenablesthe/proc/kallmodsymsfile,whichmapssymbols+toaddressesandtheirassociatedmodules.+configPRINTKdefaultybool"Enable support for printk"ifEXPERT
@@ -32,6 +32,12 @@ HOSTCFLAGS_sorttable.o += -DUNWINDER_ORC_ENABLEDHOSTLDLIBS_sorttable=-lpthreadendif+kallsyms-objs:=kallsyms.o++ifdef CONFIG_KALLMODSYMS+kallsyms-objs+=modules_thick.o+endif+# The following programs are only built on demandhostprogs+=unifdef
@@ -67,11 +74,116 @@ static int token_profit[0x10000];staticunsignedcharbest_table[256][2];staticunsignedcharbest_table_len[256];+#ifdef CONFIG_KALLMODSYMS+staticunsignedintstrhash(constchar*s)+{+/* fnv32 hash */+unsignedinthash=2166136261U;++for(;*s;s++)+hash=(hash^*s)*0x01000193;+returnhash;+}++#define OBJ2MOD_BITS 10+#define OBJ2MOD_N (1 << OBJ2MOD_BITS)+#define OBJ2MOD_MASK (OBJ2MOD_N - 1)+structobj2mod_elem{+char*obj;+char*mods;/* sorted module name strtab */+size_tnmods;/* number of modules in "mods" */+size_tmods_size;/* size of all mods together */+intmod_offset;/* offset in .kallsyms_module_names */+structobj2mod_elem*obj2mod_next;+};++/*+*Mapfromobjectfilestoobj2modentries(auniquemapping).+*/++staticstructobj2mod_elem*obj2mod[OBJ2MOD_N];+staticsize_tnum_objfiles;++/*+*Anorderedlistofaddressrangesandtheobjfilethatoccupiesthatrange.+*/+structaddrmap_entry{+unsignedlonglongaddr;+structobj2mod_elem*objfile;+};+staticstructaddrmap_entry*addrmap;+staticintaddrmap_num,addrmap_alloced;++staticvoidobj2mod_init(void)+{+memset(obj2mod,0,sizeof(obj2mod));+}++staticstructobj2mod_elem*obj2mod_get(constchar*obj)+{+inti=strhash(obj)&OBJ2MOD_MASK;+structobj2mod_elem*elem;++for(elem=obj2mod[i];elem;elem=elem->obj2mod_next){+if(strcmp(elem->obj,obj)==0)+returnelem;+}+returnNULL;+}++/*+*Notethatagivenobjectfileisfoundinsomemodule,interningitinthe+*obj2modhash.Shouldnotbecalledmorethanonceforanygiven(module,+*object)pair.+*/+staticvoidobj2mod_add(char*obj,char*mod)+{+inti=strhash(obj)&OBJ2MOD_MASK;+structobj2mod_elem*elem;++elem=obj2mod_get(obj);+if(!elem){+elem=malloc(sizeof(structobj2mod_elem));+if(!elem)+gotooom;+memset(elem,0,sizeof(structobj2mod_elem));+elem->obj=strdup(obj);+if(!elem->obj)+gotooom;+elem->mods=strdup(mod);+if(!elem->mods)+gotooom;++elem->obj2mod_next=obj2mod[i];+obj2mod[i]=elem;+num_objfiles++;+}else{+elem->mods=realloc(elem->mods,elem->mods_size++strlen(mod)+1);+if(!elem->mods)+gotooom;+strcpy(elem->mods+elem->mods_size,mod);+}++elem->mods_size+=strlen(mod)+1;+elem->nmods++;+if(elem->nmods>255){+fprintf(stderr,"kallsyms: %s: too many modules associated with this object file\n",+obj);+exit(EXIT_FAILURE);+}+return;+oom:+fprintf(stderr,"kallsyms: out of memory\n");+exit(1);+}+#endif /* CONFIG_KALLMODSYMS */staticvoidusage(void){-fprintf(stderr,"Usage: kallsyms [--all-symbols] "-"[--base-relative] < in.map > out.S\n");+fprintf(stderr,"Usage: kallsyms [--all-symbols] [--absolute-percpu] "+"[--base-relative] [--builtin=modules_thick.builtin] "+"< nm_vmlinux.out > symbols.S\n");exit(1);}
@@ -255,7 +373,7 @@ static int symbol_in_range(const struct sym_entry *s,for(i=0;i<entries;++i){ar=&ranges[i];-if(s->addr>=ar->start&&s->addr<=ar->end)+if(addr>=ar->start&&addr<=ar->end)return1;}
@@ -269,8 +387,8 @@ static int symbol_valid(const struct sym_entry *s)/* if --all-symbols is not specified, then symbols outside the text*andinittextsectionsarediscarded*/if(!all_symbols){-if(symbol_in_range(s,text_ranges,-ARRAY_SIZE(text_ranges))==0)+if(addr_in_range(s->addr,text_ranges,+ARRAY_SIZE(text_ranges))==0)return0;/* Corner case. Discard any symbols with the same value as*_etext_einittext;theycanmovebetweenpass1and2when
@@ -352,6 +470,121 @@ static void output_address(unsigned long long addr)printf("\tPTR\t_text - %#llx\n",_text-addr);}+#ifdef CONFIG_KALLMODSYMS+/* Output the .kallmodsyms_modules symbol content. */+staticvoidoutput_kallmodsyms_modules(void)+{+structobj2mod_elem*elem;+size_toffset=1;+size_ti;++/*+*Traverseandemit,updatingmod_offsetaccordingly.+*Emitasingle\0atthestart,toencodenon-modularobjfiles.+*/+output_label("kallsyms_module_names");+printf("\t.byte\t0\n");+for(i=0;i<OBJ2MOD_N;i++){+for(elem=obj2mod[i];elem;+elem=elem->obj2mod_next){+constchar*onemod;+size_ti;++elem->mod_offset=offset;+onemod=elem->mods;++/*+*Technicallythisisawasteofspace:wecouldjust+*aswellimplementmultimoduleentriesbypointingone+*bytefurtherback,tothetrailing\0oftheprevious+*entry,butdoingitthiswaymakesitmoreobvious+*whenanentryisamultimoduleentry.+*/+if(elem->nmods!=1){+printf("\t.byte\t0\n");+printf("\t.byte\t%zi\n",elem->nmods);+offset+=2;+}++for(i=elem->nmods;i>0;i--){+printf("\t.asciz\t\"%s\"\n",onemod);+offset+=strlen(onemod)+1;+onemod+=strlen(onemod)+1;+}+}+}+printf("\n");+output_label("kallsyms_module_names_len");+printf("\t.long\t%zi\n",offset);+}++staticvoidoutput_kallmodsyms_objfiles(void)+{+size_ti=0;+size_temitted_offsets=0;+size_temitted_objfiles=0;++if(base_relative)+output_label("kallsyms_module_offsets");+else+output_label("kallsyms_module_addresses");++for(i=0;i<addrmap_num;i++){+longlongoffset;+intoverflow;++if(base_relative){+if(!absolute_percpu){+offset=addrmap[i].addr-relative_base;+overflow=(offset<0||offset>UINT_MAX);+}else{+offset=relative_base-addrmap[i].addr-1;+overflow=(offset<INT_MIN||offset>=0);+}+if(overflow){+fprintf(stderr,"kallsyms failure: "+"objfile %s at address %#llx out of range in relative mode\n",+addrmap[i].objfile?addrmap[i].objfile->obj:+"in always-built-in object",table[i]->addr);+exit(EXIT_FAILURE);+}+printf("\t.long\t0x%x\n",(int)offset);+}else+printf("\tPTR\t%#llx\n",addrmap[i].addr);+emitted_offsets++;+}++output_label("kallsyms_modules");++for(i=0;i<addrmap_num;i++){+structobj2mod_elem*elem=addrmap[i].objfile;+/*+*Addressrangecitesnoobjectfile:pointat0,thebuilt-in+*module.+*/+if(addrmap[i].objfile==NULL){+printf("\t.long\t0x0\n");+emitted_objfiles++;+continue;+}++/*+*Zerooffsetistheinitial\0,theretocatchuninitialized+*obj2modentries,andisforbidden.+*/+assert(elem->mod_offset!=0);++printf("\t.long\t0x%x\n",elem->mod_offset);+emitted_objfiles++;+}++assert(emitted_offsets==emitted_objfiles);+output_label("kallsyms_num_modules");+printf("\t.long\t%zi\n",emitted_objfiles);+printf("\n");+}+#endif /* CONFIG_KALLMODSYMS */+/* uncompress a compressed symbol. When this function is called, the best table*mightstillbecompresseditself,sothefunctionneedstoberecursive*/staticintexpand_symbol(constunsignedchar*data,intlen,char*result)
From: Nick Alcock <hidden> Date: 2021-10-27 17:47:56
These symbols are terribly inefficiently stored at the moment. Add a
simple optimizer which fuses obj2mod_elem entries and uses this to
implement three cheap optimizations:
- duplicate names are eliminated from .kallsyms_module_names.
- entries in .kallsyms_modules which point at single-file modules which
also appear in a multi-module list are redirected to point inside
that list, and the single-file entry is dropped from
.kallsyms_module_names. Thus, modules which contain some object
files shared with other modules and some object files exclusive to
them do not double up the module name. (There might still be some
duplication between multiple multi-module lists, but this is an
extremely marginal size effect, and resolving it would require an
extra layer of lookup tables which would be even more complex, and
incompressible to boot).
- Entries in .kallsyms_modules that would contain the same value after
the above optimizations are fused together, along with their
corresponding .kallsyms_module_addresses/offsets entries. Due to
this fusion process, and because object files can be split apart into
multiple parts by the linker for hot/cold partitioning and the like,
entries in .kallsyms_module_addresses/offsets no longer correspond
1:1 to object files, but more to some contiguous range of addresses
which are guaranteed to belong to a single built-in module, but which
may well stretch over multiple object files.
The optimizer's time complexity is O(log n) in the number of objfiles at
most (and probably much lower), so, given the relatively low number of
objfiles, its runtime overhead is in the noise.
Optimization reduces the overhead of the kallmodsyms tables by about
7500 items, dropping the .tmp_kallsyms2.o object file size by about
33KiB, leaving it 8672 bytes larger than before: a gain of .4%.
The vmlinux size is not yet affected because the variables are not used
and are eliminated by the linker: but if they were used (after the next
commit), the size impact of all of this on the final kernel is minimal:
in my testing, the kallsyms2.S file went from 14107772 to 14137245
bytes, a gain of 29743 bytes, or 0.16%: vmlinux gained 10824 bytes, a
gain of .017%, and the compressed vmlinux only 7552 bytes, a gain of
.08%: though the latter two values are very configuration-dependent,
they seem likely to scale roughly with the kernel they are part of.
Signed-off-by: Nick Alcock <redacted>
---
scripts/kallsyms.c | 267 +++++++++++++++++++++++++++++++++++++++++++--
1 file changed, 258 insertions(+), 9 deletions(-)
@@ -94,14 +105,24 @@ struct obj2mod_elem {size_tnmods;/* number of modules in "mods" */size_tmods_size;/* size of all mods together */intmod_offset;/* offset in .kallsyms_module_names */+/*+*Ifsetatemissiontime,thispointsatanotherobj2modentrythat+*containsthemodulenameweneed(possiblyataslightlylater+*offset,iftheentryisforanobjfilethatappearsinmanymodules).+*/+structobj2mod_elem*xref;structobj2mod_elem*obj2mod_next;+structobj2mod_elem*mod2obj_next;};/*-*Mapfromobjectfilestoobj2modentries(auniquemapping).+*Mapfromobjectfilestoobj2modentries(auniquemapping),andviceversa+*(notunique,butentriesforobjfilesinmorethanonemoduleinthishash+*areignored).*/staticstructobj2mod_elem*obj2mod[OBJ2MOD_N];+staticstructobj2mod_elem*mod2obj[OBJ2MOD_N];staticsize_tnum_objfiles;/*
@@ -177,6 +207,164 @@ static void obj2mod_add(char *obj, char *mod)fprintf(stderr,"kallsyms: out of memory\n");exit(1);}++/*+*Usedinsideoptimize_obj2modtoidentifyduplicatemoduleentries.+*/+structobj2mod_modhash_elem{+structobj2mod_elem*elem;+unsignedintmodhash;/* hash value of this entry */+};++staticintqstrcmp(constvoid*a,constvoid*b)+{+returnstrcmp((constchar*)a,(constchar*)b);+}++staticintqmodhash(constvoid*a,constvoid*b)+{+conststructobj2mod_modhash_elem*el_a=a;+conststructobj2mod_modhash_elem*el_b=b;+if(el_a->modhash<el_b->modhash)+return-1;+elseif(el_a->modhash>el_b->modhash)+return1;+return0;+}++/*+*AssociateallTUsinobj2modwhichrefertothesamemodulewithasingle+*obj2modentryforemission,preferringtopointintothemodulelistina+*multi-moduleobjfile.+*/+staticvoidoptimize_obj2mod(void)+{+size_ti;+size_tn=0;+structobj2mod_elem*elem;+structobj2mod_elem*dedup;+/* An array of all obj2mod_elems, later sorted by hashval. */+structobj2mod_modhash_elem*uniq;+structobj2mod_modhash_elem*last;++/*+*Canonicalizeallmodulelistsbysortingthem,thencomputetheir+*hashvalues.+*/+uniq=malloc(sizeof(structobj2mod_modhash_elem)*num_objfiles);+if(uniq==NULL)+gotooom;++for(i=0;i<OBJ2MOD_N;i++){+for(elem=obj2mod[i];elem;elem=elem->obj2mod_next){+if(elem->nmods>=2){+char**sorter;+char*walk;+char*tmp_mods;+size_tj;++tmp_mods=malloc(elem->mods_size);+sorter=malloc(sizeof(char*)*elem->nmods);+if(sorter==NULL||tmp_mods==NULL)+gotooom;+memcpy(tmp_mods,elem->mods,elem->mods_size);++for(j=0,walk=tmp_mods;j<elem->nmods;+j++){+sorter[j]=walk;+walk+=strlen(walk)+1;+}+qsort(sorter,elem->nmods,sizeof(char*),+qstrcmp);+for(j=0,walk=elem->mods;j<elem->nmods;+j++){+strcpy(walk,sorter[j]);+walk+=strlen(walk)+1;+}+free(tmp_mods);+free(sorter);+}++uniq[n].elem=elem;+uniq[n].modhash=memhash(elem->mods,elem->mods_size);+n++;+}+}++qsort(uniq,num_objfiles,sizeof(structobj2mod_modhash_elem),+qmodhash);++/*+*Workovermultimoduleentries.Thesemustbeemittedinto+*.kallsyms_module_namesasaunit,butwecanstilloptimizeby+*reusingsomeotheridenticalentry.Single-filemodulesareamenable+*tothesameoptimization,butweavoiddoingitfornowsothatwe+*canprefertopointthemdirectlyinsideamultimoduleentry.+*/+for(i=0,last=NULL;i<num_objfiles;i++){+constchar*onemod;+size_tj;++if(uniq[i].elem->nmods<2)+continue;++/* Duplicate multimodule. Reuse the first we saw. */+if(last!=NULL&&last->modhash==uniq[i].modhash){+uniq[i].elem->xref=last->elem;+continue;+}++/*+*Single-moduleentriesrelatingtomodulesalsoemittedas+*partofthismultimoduleentrycanrefertoit:later,we+*willhuntdowntherightspecificmodulenamewithinthis+*multimoduleentryandpointdirectlytoit.+*/+onemod=uniq[i].elem->mods;+for(j=uniq[i].elem->nmods;j>0;j--){+inth=strhash(onemod)&OBJ2MOD_MASK;++for(dedup=mod2obj[h];dedup;+dedup=dedup->mod2obj_next){+if(dedup->nmods>1)+continue;++if(strcmp(dedup->mods,onemod)!=0)+continue;+dedup->xref=uniq[i].elem;+assert(uniq[i].elem->xref==NULL);+}+onemod+=strlen(onemod)+1;+}++last=&uniq[i];+}++/*+*Nowtraverseallsingle-moduleentries,xreffingeveryonethat+*relatestoagivenmoduletothefirstonewesawthatreferstothat+*module.+*/+for(i=0,last=NULL;i<num_objfiles;i++){+if(uniq[i].elem->nmods>1)+continue;++if(uniq[i].elem->xref!=NULL)+continue;++/* Duplicate module name. Reuse the first we saw. */+if(last!=NULL&&last->modhash==uniq[i].modhash){+uniq[i].elem->xref=last->elem;+assert(last->elem->xref==NULL);+continue;+}+last=&uniq[i];+}+return;+oom:+fprintf(stderr,"kallsyms: out of memory optimizing module list\n");+exit(EXIT_FAILURE);+}#endif /* CONFIG_KALLMODSYMS */staticvoidusage(void)
From: Nick Alcock <hidden> Date: 2021-10-27 17:48:00
The existing mechanisms in get_symbol_pos to determine the end of a
symbol is an inaccurate heuristic. By passing nm -S output into
scripts/kallsyms.c and writing the symbol sizes to a new .kallsyms_sizes
section, we can get accurate sizes and sort the symbols accordingly,
reliably sorting zero-size symbols first (on the grounds that they are
usually e.g. section markers, and other symbols at the same address are
conceptually contained within them and should be sorted after them),
then larger symbols before smaller ones (so that overlapping symbols
print the containing symbol first, before its containees). We can
also use this to improve aliased symbol detection.
Emit the size info as an extra column in /proc/kallmodsyms (since its
format is not yet set in stone), and export it to iterator consumers.
The notable downside of this is that the new .kallsyms_sizes is pretty
big: a PTR per symbol, so vmlinux.o grows by almost a megabyte, though
it compresses pretty well, so bzImage grows by only a megabyte.
I'm not sure how to reduce this (perhaps using an array with elements
sized to be no larger than needed for the contents, so that almost
always two-byte entries would do? except that in my test kernel two
symbols are bigger than this: sme_workarea, at 400K, and __log_buf, at
100K: the latter seems often likely to be larger than 64K). A simple
scheme to reduce this would be to split the sizes array into several
arrays with differently-sized elements, and run-length-compress away the
zero bytes -- but that's not implemented yet, and might never be if
people think the whole idea of this is pointless.
In the absence of a way to shrink things, this should probably be hidden
behind a new config symbol if exposed at all, and kallmodsyms just shows
zero sizes if it's configured out (but this is enough of an RFC that
that's not yet done: possibly the benefits of this are too marginal to
be worth it, even if they do let kall(mod)syms consumers distinguish
symbols from padding, which was previously impossible).
Signed-off-by: Nick Alcock <redacted>
Signed-off-by: Eugene Loh <redacted>
---
include/linux/module.h | 7 ++--
kernel/kallsyms.c | 74 ++++++++++++++++++++++-------------------
kernel/module.c | 4 ++-
scripts/kallsyms.c | 29 +++++++++++++---
scripts/link-vmlinux.sh | 7 +++-
5 files changed, 77 insertions(+), 44 deletions(-)
@@ -590,7 +590,8 @@ struct module *find_module(const char *name);/* Returns 0 and fills in value, defined and namebuf, or -ERANGE ifsymnumoutofrange.*/intmodule_get_kallsym(unsignedintsymnum,unsignedlong*value,char*type,-char*name,char*module_name,int*exported);+char*name,char*module_name,unsignedlong*size,+int*exported);/* Look for this name: can be of form module:name. */unsignedlongmodule_kallsyms_lookup_name(constchar*name);
@@ -768,8 +769,8 @@ static inline int lookup_module_symbol_attrs(unsigned long addr, unsigned long *}staticinlineintmodule_get_kallsym(unsignedintsymnum,unsignedlong*value,-char*type,char*name,-char*module_name,int*exported)+char*type,char*name,char*module_name,+unsignedlong*size,int*exported){return-ERANGE;}
@@ -257,12 +258,24 @@ int kallsyms_on_each_symbol(int (*fn)(void *, const char *, struct module *,}#endif /* CONFIG_LIVEPATCH */+/*+*Thecallerpassesinanaddress,andwereturnanindextothesymbol--+*potentiallyalsosizeandoffsetinformation.+*Butanaddressmightmaptomultiplesymbolsbecause:+*-somesymbolsmighthavezerosize+*-somesymbolsmightbealiasesofoneanother+*-somesymbolsmightspan(encompass)others+*Thesymbolsshouldalreadybeorderedsothat,foraparticularaddress,+*wefirsthavethezero-sizeones,thenthebiggest,thenthesmallest.+*Sowefindtheindexby:+*-findingthelastsymbolwiththetargetaddress+*-backingtheindexupsolongasboththeaddressandsizeareunchanged+*/staticunsignedlongget_symbol_pos(unsignedlongaddr,unsignedlong*symbolsize,unsignedlong*offset){-unsignedlongsymbol_start=0,symbol_end=0;-unsignedlongi,low,high,mid;+unsignedlonglow,high,mid;/* This kernel should never had been booted. */if(!IS_ENABLED(CONFIG_KALLSYMS_BASE_RELATIVE))
@@ -283,36 +296,17 @@ static unsigned long get_symbol_pos(unsigned long addr,}/*-*Searchforthefirstaliasedsymbol.Aliased-*symbolsaresymbolswiththesameaddress.+*Searchforthefirstaliasedsymbol.*/-while(low&&kallsyms_sym_address(low-1)==kallsyms_sym_address(low))+while(low+&&kallsyms_sym_address(low-1)==kallsyms_sym_address(low)+&&kallsyms_sizes[low-1]==kallsyms_sizes[low])--low;-symbol_start=kallsyms_sym_address(low);--/* Search for next non-aliased symbol. */-for(i=low+1;i<kallsyms_num_syms;i++){-if(kallsyms_sym_address(i)>symbol_start){-symbol_end=kallsyms_sym_address(i);-break;-}-}--/* If we found no next symbol, we use the end of the section. */-if(!symbol_end){-if(is_kernel_inittext(addr))-symbol_end=(unsignedlong)_einittext;-elseif(IS_ENABLED(CONFIG_KALLSYMS_ALL))-symbol_end=(unsignedlong)_end;-else-symbol_end=(unsignedlong)_etext;-}-if(symbolsize)-*symbolsize=symbol_end-symbol_start;+*symbolsize=kallsyms_sizes[low];if(offset)-*offset=addr-symbol_start;+*offset=addr-kallsyms_sym_address(low);returnlow;}
@@ -633,6 +627,7 @@ struct kallsym_iter {loff_tpos_bpf_end;unsignedlongvalue;unsignedintnameoff;/* If iterating in core kernel symbols. */+unsignedlongsize;chartype;charname[KSYM_NAME_LEN];charmodule_name[MODULE_NAME_LEN];
@@ -667,7 +662,7 @@ static int get_ksymbol_mod(struct kallsym_iter *iter)intret=module_get_kallsym(iter->pos-iter->pos_arch_end,&iter->value,&iter->type,iter->name,iter->module_name,-&iter->exported);+&iter->size,&iter->exported);iter->builtin_module_names=NULL;if(ret<0){
@@ -740,6 +735,7 @@ static unsigned long get_ksymbol_core(struct kallsym_iter *iter, int kallmodsymsiter->exported=0;iter->value=kallsyms_sym_address(iter->pos);+iter->size=kallsyms_sizes[iter->pos];iter->type=kallsyms_get_symbol_type(off);iter->module_name[0]='\0';
@@ -858,12 +854,14 @@ static int s_show_internal(struct seq_file *m, void *p, int kallmodsyms){void*value;structkallsym_iter*iter=m->private;+unsignedlongsize;/* Some debugging symbols have no name. Ignore them. */if(!iter->name[0])return0;value=iter->show_value?(void*)iter->value:NULL;+size=iter->show_value?iter->size:0;/**Realmodule,orbuilt-inmoduleand/proc/kallsymsbeingshown.
@@ -4368,7 +4368,8 @@ int lookup_module_symbol_attrs(unsigned long addr, unsigned long *size,}intmodule_get_kallsym(unsignedintsymnum,unsignedlong*value,char*type,-char*name,char*module_name,int*exported)+char*name,char*module_name,unsignedlong*size,+int*exported){structmodule*mod;
@@ -4387,6 +4388,7 @@ int module_get_kallsym(unsigned int symnum, unsigned long *value, char *type,strlcpy(name,kallsyms_symbol_name(kallsyms,symnum),KSYM_NAME_LEN);strlcpy(module_name,mod->name,MODULE_NAME_LEN);*exported=is_exported(name,*value,mod);+*size=kallsyms->symtab[symnum].st_size;preempt_enable();return0;}
@@ -274,7 +274,12 @@ kallsyms()fiinfoKSYMS${2}-${NM}-n${1}|scripts/kallsyms${kallsymopt}>${2}+# "nm -S" does not print symbol size when size is 0+# Therefore use awk to regularize the data:+# - when there are only three fields, add an explicit "0"+# - when there are already four fields, pass through as is+${NM}-n-S${1}|${AWK}'NF==3 {print $1, 0, $2, $3}; NF==4'|\+scripts/kallsyms${kallsymopt}>${2}}# Perform one step in kallsyms generation, including temporary linking of
From: Nick Alcock <hidden> Date: 2021-10-27 17:48:05
Use the tables added in the previous commits to introduce a new
/proc/kallmodsyms, in which [module names] are also given for things
that *could* have been modular had they not been built in to the kernel.
So symbols that are part of, say, ext4 are reported as [ext4] even if
ext4 happens to be buiilt in to the kernel in this configuration.
Symbols that are part of multiple modules at the same time are shown
with [multiple] [module names]: consumers will have to be ready to
handle such lines. Also, kernel symbols for built-in modules will be
sorted by size, as usual for the core kernel, so will probably appear
interspersed with other symbols that are part of different modules and
non-modular always-built-in symbols, which, as usual, have no
square-bracketed module denotation. This differs from /proc/kallsyms,
where all symbols associated with a module will always appear in a group
(and randomly ordered).
The result looks like this:
ffffffff8b013d20 t pt_buffer_setup_aux
ffffffff8b014130 T intel_pt_interrupt
ffffffff8b014250 T cpu_emergency_stop_pt
ffffffff8b014280 t rapl_pmu_event_init [intel_rapl_perf]
ffffffff8b0143c0 t rapl_event_update [intel_rapl_perf]
ffffffff8b014480 t rapl_pmu_event_read [intel_rapl_perf]
ffffffff8b014490 t rapl_cpu_offline [intel_rapl_perf]
ffffffff8b014540 t __rapl_event_show [intel_rapl_perf]
ffffffff8b014570 t rapl_pmu_event_stop [intel_rapl_perf]
This is emitted even if intel_rapl_perf is built into the kernel (but,
obviously, not if it's not in the .config at all, or is in a module that
is not loaded).
Further down, we see what happens when object files are reused by
multiple modules, all of which are built in to the kernel:
ffffffffa22b3aa0 t handle_timestamp [liquidio]
ffffffffa22b3b50 t free_netbuf [liquidio]
ffffffffa22b3ba0 t liquidio_ptp_settime [liquidio]
ffffffffa22b3c30 t liquidio_ptp_adjfreq [liquidio]
[...]
ffffffffa22b9490 t lio_vf_rep_create [liquidio]
ffffffffa22b96a0 t lio_vf_rep_destroy [liquidio]
ffffffffa22b9810 t lio_vf_rep_modinit [liquidio]
ffffffffa22b9830 t lio_vf_rep_modexit [liquidio]
ffffffffa22b9850 t lio_ethtool_get_channels [liquidio] [liquidio_vf]
ffffffffa22b9930 t lio_ethtool_get_ringparam [liquidio] [liquidio_vf]
ffffffffa22b99d0 t lio_get_msglevel [liquidio] [liquidio_vf]
ffffffffa22b99f0 t lio_vf_set_msglevel [liquidio] [liquidio_vf]
ffffffffa22b9a10 t lio_get_pauseparam [liquidio] [liquidio_vf]
ffffffffa22b9a40 t lio_get_ethtool_stats [liquidio] [liquidio_vf]
ffffffffa22ba180 t lio_vf_get_ethtool_stats [liquidio] [liquidio_vf]
ffffffffa22ba4f0 t lio_get_regs_len [liquidio] [liquidio_vf]
ffffffffa22ba530 t lio_get_priv_flags [liquidio] [liquidio_vf]
ffffffffa22ba550 t lio_set_priv_flags [liquidio] [liquidio_vf]
ffffffffa22ba580 t lio_set_fecparam [liquidio] [liquidio_vf]
ffffffffa22ba5f0 t lio_get_fecparam [liquidio] [liquidio_vf]
[...]
ffffffffa22cbd10 t liquidio_set_mac [liquidio_vf]
ffffffffa22cbe90 t handle_timestamp [liquidio_vf]
ffffffffa22cbf40 t free_netbuf [liquidio_vf]
ffffffffa22cbf90 t octnet_link_status_change [liquidio_vf]
ffffffffa22cbfc0 t liquidio_vxlan_port_command.constprop.0 [liquidio_vf]
Like /proc/kallsyms, the output is driven by address, so keeps the
curious property of /proc/kallsyms that symbols (like free_netbuf above)
may appear repeatedly with different addresses: but now, unlike in
/proc/kallsyms, we can see that those symbols appear repeatedly because
they are *different symbols* that ultimately belong to different
modules, all of which are built in to the kernel.
As with /proc/kallsyms, non-root usage produces addresses that are
all zero.
I am not wedded to the name or format of /proc/kallmodsyms, but felt it
best to split it out of /proc/kallsyms to avoid breaking existing
kallsyms parsers. Another possible syntax might be to use {curly
brackets} or something to denote built-in modules: it might be possible
to drop /proc/kallmodsyms and make /proc/kallsyms emit things in this
format. (Equally, now kallmodsyms data uses very little space, the
CONFIG_KALLMODSYMS config option might be something people don't want to
bother with.)
Internally, this uses a new kallsyms_builtin_module_address() almost
identical to kallsyms_sym_address() to get the address corresponding to
a given .kallsyms_modules index, and a new get_builtin_module_idx quite
similar to get_symbol_pos to determine the index in the
.kallsyms_modules array that relates to a given address. Save a little
time by exploiting the fact that all callers will only ever traverse
this list from start to end by allowing them to pass in the previous
index returned from this function as a hint: thus very few bsearches are
actually needed. (In theory this could change to just walk straight
down kallsyms_module_addresses/offsets and not bother bsearching at all,
but doing it this way is hardly any slower and much more robust.)
The display process is complicated a little by the weird format of the
.kallsyms_module_names table: we have to look for multimodule entries
and print them as space-separated lists of module names.
Signed-off-by: Nick Alcock <redacted>
---
kernel/kallsyms.c | 242 +++++++++++++++++++++++++++++++++++++++++++---
1 file changed, 227 insertions(+), 15 deletions(-)
@@ -185,6 +195,25 @@ static inline bool cleanup_symbol_name(char *s)staticinlineboolcleanup_symbol_name(char*s){returnfalse;}#endif+#ifdef CONFIG_KALLMODSYMS+staticunsignedlongkallsyms_builtin_module_address(intidx)+{+if(!IS_ENABLED(CONFIG_KALLSYMS_BASE_RELATIVE))+returnkallsyms_module_addresses[idx];++/* values are unsigned offsets if --absolute-percpu is not in effect */+if(!IS_ENABLED(CONFIG_KALLSYMS_ABSOLUTE_PERCPU))+returnkallsyms_relative_base+(u32)kallsyms_module_offsets[idx];++/* ...otherwise, positive offsets are absolute values */+if(kallsyms_module_offsets[idx]>=0)+returnkallsyms_module_offsets[idx];++/* ...and negative offsets are relative to kallsyms_relative_base - 1 */+returnkallsyms_relative_base-1-kallsyms_module_offsets[idx];+}+#endif+/* Lookup the address for this symbol. Returns 0 if not found. */unsignedlongkallsyms_lookup_name(constchar*name){
@@ -288,6 +317,54 @@ static unsigned long get_symbol_pos(unsigned long addr,returnlow;}+/*+*Thecallerpassesinanaddress,andwereturnanindextothecorresponding+*builtinmoduleindexin.kallsyms_modules,or(unsignedlong)-1ifnone+*match.+*+*Thehint_idx,ifset,isahintastothepossiblereturnvalue,tohandle+*thecommoncaseinwhichconsecutiverunsofaddressesrelatetothesame+*index.+*/+#ifdef CONFIG_KALLMODSYMS+staticunsignedlongget_builtin_module_idx(unsignedlongaddr,unsignedlonghint_idx)+{+unsignedlonglow,high,mid;++if(!IS_ENABLED(CONFIG_KALLSYMS_BASE_RELATIVE))+BUG_ON(!kallsyms_module_addresses);+else+BUG_ON(!kallsyms_module_offsets);++/*+*Doabinarysearchonthesortedkallsyms_modulesarray.Thelast+*entryinthisarrayindicatestheendofthetextsection,notan+*objectfile.+*/+low=0;+high=kallsyms_num_modules-1;++if(hint_idx>low&&hint_idx<(high-1)&&+addr>=kallsyms_builtin_module_address(hint_idx)&&+addr<kallsyms_builtin_module_address(hint_idx+1))+returnhint_idx;++if(addr>=kallsyms_builtin_module_address(low)+&&addr<kallsyms_builtin_module_address(high)){+while(high-low>1){+mid=low+(high-low)/2;+if(kallsyms_builtin_module_address(mid)<=addr)+low=mid;+else+high=mid;+}+returnlow;+}++return(unsignedlong)-1;+}+#endif+/**Lookupanaddressbutdon'tbothertofindanynames.*/
@@ -589,6 +668,8 @@ static int get_ksymbol_mod(struct kallsym_iter *iter)&iter->value,&iter->type,iter->name,iter->module_name,&iter->exported);+iter->builtin_module_names=NULL;+if(ret<0){iter->pos_mod_end=iter->pos;return0;
@@ -608,6 +689,8 @@ static int get_ksymbol_ftrace_mod(struct kallsym_iter *iter)&iter->value,&iter->type,iter->name,iter->module_name,&iter->exported);+iter->builtin_module_names=NULL;+if(ret<0){iter->pos_ftrace_mod_end=iter->pos;return0;
@@ -622,6 +705,7 @@ static int get_ksymbol_bpf(struct kallsym_iter *iter)strlcpy(iter->module_name,"bpf",MODULE_NAME_LEN);iter->exported=0;+iter->builtin_module_names=NULL;ret=bpf_get_kallsym(iter->pos-iter->pos_ftrace_mod_end,&iter->value,&iter->type,iter->name);
@@ -642,23 +726,53 @@ static int get_ksymbol_kprobe(struct kallsym_iter *iter){strlcpy(iter->module_name,"__builtin__kprobes",MODULE_NAME_LEN);iter->exported=0;+iter->builtin_module_names=NULL;returnkprobe_get_kallsym(iter->pos-iter->pos_bpf_end,&iter->value,&iter->type,iter->name)<0?0:1;}/* Returns space to next name. */-staticunsignedlongget_ksymbol_core(structkallsym_iter*iter)+staticunsignedlongget_ksymbol_core(structkallsym_iter*iter,intkallmodsyms){unsignedoff=iter->nameoff;-iter->module_name[0]='\0';+iter->exported=0;iter->value=kallsyms_sym_address(iter->pos);iter->type=kallsyms_get_symbol_type(off);+iter->module_name[0]='\0';+iter->builtin_module_names=NULL;+off=kallsyms_expand_symbol(off,iter->name,ARRAY_SIZE(iter->name));+#ifdef CONFIG_KALLMODSYMS+if(kallmodsyms){+unsignedlongmod_idx=(unsignedlong)-1;++if(kallsyms_module_offsets)+mod_idx=+get_builtin_module_idx(iter->value,+iter->hint_builtin_module_idx);+/*+*Thisisabuilt-inmoduleiffthetablesofbuilt-inmodules+*(address->modulenamemappings)andmodulenamesareknown,+*andiftheaddresswasfoundthere,andifthecorresponding+*moduleindexisnonzero.Allothercasesmeanofftheendof+*thebinaryorinanon-modularrangeinbetweenoneormore+*modules.(Alsoguardagainstacorruptkallsyms_objfiles+*arraypointingofftheendofkallsyms_modules.)+*/+if(kallsyms_modules!=NULL&&kallsyms_module_names!=NULL&&+mod_idx!=(unsignedlong)-1&&+kallsyms_modules[mod_idx]!=0&&+kallsyms_modules[mod_idx]<kallsyms_module_names_len)+iter->builtin_module_names=+&kallsyms_module_names[kallsyms_modules[mod_idx]];+iter->hint_builtin_module_idx=mod_idx;+}+#endifreturnoff-iter->nameoff;}
@@ -704,7 +818,7 @@ static int update_iter_mod(struct kallsym_iter *iter, loff_t pos)}/* Returns false if pos at or past end of file. */-staticintupdate_iter(structkallsym_iter*iter,loff_tpos)+staticintupdate_iter(structkallsym_iter*iter,loff_tpos,intkallmodsyms){/* Module symbols can be accessed randomly. */if(pos>=kallsyms_num_syms)
From: kernel test robot <hidden> Date: 2021-10-28 11:37:45
Hi Nick,
Thank you for the patch! Perhaps something to improve:
[auto build test WARNING on linux/master]
[also build test WARNING on linus/master v5.15-rc7]
[cannot apply to jeyu/modules-next masahiroy/kconfig next-20211027]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch]
url: https://github.com/0day-ci/linux/commits/Nick-Alcock/kbuild-bring-back-tristate-conf/20211028-024948
base: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git 2f111a6fd5b5297b4e92f53798ca086f7c7d33a4
config: hexagon-randconfig-r041-20211027 (attached as .config)
compiler: clang version 14.0.0 (https://github.com/llvm/llvm-project 5db7568a6a1fcb408eb8988abdaff2a225a8eb72)
reproduce (this is a W=1 build):
wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
chmod +x ~/bin/make.cross
# https://github.com/0day-ci/linux/commit/3e2ed787d495a82851cad4d99a369524140966ae
git remote add linux-review https://github.com/0day-ci/linux
git fetch --no-tags linux-review Nick-Alcock/kbuild-bring-back-tristate-conf/20211028-024948
git checkout 3e2ed787d495a82851cad4d99a369524140966ae
# save the attached .config to linux build tree
COMPILER_INSTALL_PATH=$HOME/0day COMPILER=clang make.cross W=1 ARCH=hexagon
If you fix the issue, kindly add following tag as appropriate
Reported-by: kernel test robot <redacted>
All warnings (new ones prefixed by >>):
kernel/kallsyms.c:645:12: warning: no previous prototype for function 'arch_get_kallsym' [-Wmissing-prototypes]
int __weak arch_get_kallsym(unsigned int symnum, unsigned long *value,
^
kernel/kallsyms.c:645:1: note: declare 'static' if the function is not intended to be used outside of this translation unit
int __weak arch_get_kallsym(unsigned int symnum, unsigned long *value,
^
static
kernel/kallsyms.c:1054:30: warning: unused variable 'kallsyms_proc_ops' [-Wunused-const-variable]
static const struct proc_ops kallsyms_proc_ops = {
^
From: kernel test robot <hidden> Date: 2021-10-30 16:49:51
Hi Nick,
Thank you for the patch! Yet something to improve:
[auto build test ERROR on linux/master]
[also build test ERROR on linus/master v5.15-rc7]
[cannot apply to jeyu/modules-next masahiroy/kconfig next-20211029]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch]
url: https://github.com/0day-ci/linux/commits/Nick-Alcock/kbuild-bring-back-tristate-conf/20211028-024948
base: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git 2f111a6fd5b5297b4e92f53798ca086f7c7d33a4
config: um-x86_64_defconfig (attached as .config)
compiler: gcc-9 (Debian 9.3.0-22) 9.3.0
reproduce (this is a W=1 build):
# https://github.com/0day-ci/linux/commit/a5f527e3449ec41735da78a430732e96080d49fe
git remote add linux-review https://github.com/0day-ci/linux
git fetch --no-tags linux-review Nick-Alcock/kbuild-bring-back-tristate-conf/20211028-024948
git checkout a5f527e3449ec41735da78a430732e96080d49fe
# save the attached .config to linux build tree
mkdir build_dir
make W=1 O=build_dir ARCH=um SUBARCH=x86_64 SHELL=/bin/bash
If you fix the issue, kindly add following tag as appropriate
Reported-by: kernel test robot <redacted>
All errors (new ones prefixed by >>):
quoted
gcc-9: error: unrecognized command line option '-Map=.tmp_vmlinux.map'