[PATCH v10 07/13] kbuild: generate an address ranges map at vmlinux link time

From: Nick Alcock
Date: Mon Dec 05 2022 - 11:33:26 EST


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
[...]

This will be used by kallmodsyms to let us associate symbols with
built-in modules at address-range granularity (i.e., space-efficiently),
and to let us disambiguate symbols with identical name and module by
annotating them with the translation unit they come from.

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.

CONFIG_LTO_CLANG by default optimizes by working from the LTOed
vmlinux.o file: but this doesn't work for kallmodsyms, as the resulting
mapfile lists vmlinux.o as the source object file in all cases, ruining
its attempt to disambiguate symbols using object file names. So we
suppress that optimization in this case.

We also suppress it when IBT is enabled, but IBT *requires* use of the
intermediate vmlinux.o, since that's the .o that objtool has run over.
We lift this restriction in the next commit.

(There are similar problems with everything else that uses ld -r: since
this amounts, in total, to a few parts of KVM on aarch64, I haven't
implemented a general ld -r fix: the fix in the next commit is specific
to vmlinux.o.)

Signed-off-by: Nick Alcock <nick.alcock@xxxxxxxxxx>
Reviewed-by: Kris Van Hees <kris.van.hees@xxxxxxxxxx>
---

Notes:
v6: use ${wl} where appropriate to avoid failure on UML
v10: mention ultimate use in kallmodsyms, conflicts with IBT and ld -r

scripts/link-vmlinux.sh | 24 +++++++++++++++++++++---
1 file changed, 21 insertions(+), 3 deletions(-)

diff --git a/scripts/link-vmlinux.sh b/scripts/link-vmlinux.sh
index 32e573943cf0..a40d372b1289 100755
--- a/scripts/link-vmlinux.sh
+++ b/scripts/link-vmlinux.sh
@@ -60,7 +60,10 @@ vmlinux_link()
# skip output file argument
shift

- if is_enabled CONFIG_LTO_CLANG || is_enabled CONFIG_X86_KERNEL_IBT; then
+ # kallmodsyms needs a linker mapfile that contains original object
+ # file names, so cannot use this optimization.
+ if { is_enabled CONFIG_LTO_CLANG || is_enabled CONFIG_X86_KERNEL_IBT; } && \
+ ! is_enabled CONFIG_KALLMODSYMS; then
# Use vmlinux.o instead of performing the slow LTO link again.
objs=vmlinux.o
libs=
@@ -94,7 +97,7 @@ vmlinux_link()
ldflags="${ldflags} ${wl}--strip-debug"
fi

- if is_enabled CONFIG_VMLINUX_MAP; then
+ if is_enabled CONFIG_VMLINUX_MAP || is_enabled CONFIG_KALLMODSYMS; then
ldflags="${ldflags} ${wl}-Map=${output}.map"
fi

@@ -144,6 +147,21 @@ kallsyms()
{
local kallsymopt;

+ # 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"
+ if is_enabled CONFIG_KALLMODSYMS; then
+ ${AWK} '
+ /\.o$/ && start==1 { print $(NF-2), $(NF-1), $NF }
+ /^Linker script and memory map/ { start = 1 }
+ /^\.brk/ { exit(0) }
+ ' ${3} | sort > .tmp_vmlinux.ranges
+ fi
+
+ # get kallsyms options
if is_enabled CONFIG_KALLSYMS_ALL; then
kallsymopt="${kallsymopt} --all-symbols"
fi
@@ -175,7 +193,7 @@ kallsyms_step()

vmlinux_link ${kallsyms_vmlinux} "${kallsymso_prev}" ${btf_vmlinux_bin_o}
mksysmap ${kallsyms_vmlinux} ${kallsyms_vmlinux}.syms
- kallsyms ${kallsyms_vmlinux}.syms ${kallsyms_S}
+ kallsyms ${kallsyms_vmlinux}.syms ${kallsyms_S} ${kallsyms_vmlinux}.map

info AS ${kallsyms_S}
${CC} ${NOSTDINC_FLAGS} ${LINUXINCLUDE} ${KBUILD_CPPFLAGS} \
--
2.38.0.266.g481848f278