From 522397d05e7d4a7c30b91841492360336b24f833 Mon Sep 17 00:00:00 2001 From: Ihor Solodrai Date: Fri, 19 Dec 2025 10:18:25 -0800 Subject: resolve_btfids: Change in-place update with raw binary output Currently resolve_btfids updates .BTF_ids section of an ELF file in-place, based on the contents of provided BTF, usually within the same input file, and optionally a BTF base. Change resolve_btfids behavior to enable BTF transformations as part of its main operation. To achieve this, in-place ELF write in resolve_btfids is replaced with generation of the following binaries: * ${1}.BTF with .BTF section data * ${1}.BTF_ids with .BTF_ids section data if it existed in ${1} * ${1}.BTF.base with .BTF.base section data for out-of-tree modules The execution of resolve_btfids and consumption of its output is orchestrated by scripts/gen-btf.sh introduced in this patch. The motivation for emitting binary data is that it allows simplifying resolve_btfids implementation by delegating ELF update to the $OBJCOPY tool [1], which is already widely used across the codebase. There are two distinct paths for BTF generation and resolve_btfids application in the kernel build: for vmlinux and for kernel modules. For the vmlinux binary a .BTF section is added in a roundabout way to ensure correct linking. The patch doesn't change this approach, only the implementation is a little different. Before this patch it worked as follows: * pahole consumed .tmp_vmlinux1 [2] and added .BTF section with llvm-objcopy [3] to it * then everything except the .BTF section was stripped from .tmp_vmlinux1 into a .tmp_vmlinux1.bpf.o object [2], later linked into vmlinux * resolve_btfids was executed later on vmlinux.unstripped [4], updating it in-place After this patch gen-btf.sh implements the following: * pahole consumes .tmp_vmlinux1 and produces a *detached* file with raw BTF data * resolve_btfids consumes .tmp_vmlinux1 and detached BTF to produce (potentially modified) .BTF, and .BTF_ids sections data * a .tmp_vmlinux1.bpf.o object is then produced with objcopy copying BTF output of resolve_btfids * .BTF_ids data gets embedded into vmlinux.unstripped in link-vmlinux.sh by objcopy --update-section For kernel modules, creating a special .bpf.o file is not necessary, and so embedding of sections data produced by resolve_btfids is straightforward with objcopy. With this patch an ELF file becomes effectively read-only within resolve_btfids, which allows deleting elf_update() call and satellite code (like compressed_section_fix [5]). Endianness handling of .BTF_ids data is also changed. Previously the "flags" part of the section was bswapped in sets_patch() [6], and then Elf_Type was modified before elf_update() to signal to libelf that bswap may be necessary. With this patch we explicitly bswap entire data buffer on load and on dump. [1] https://lore.kernel.org/bpf/131b4190-9c49-4f79-a99d-c00fac97fa44@linux.dev/ [2] https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/scripts/link-vmlinux.sh?h=v6.18#n110 [3] https://git.kernel.org/pub/scm/devel/pahole/pahole.git/tree/btf_encoder.c?h=v1.31#n1803 [4] https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/scripts/link-vmlinux.sh?h=v6.18#n284 [5] https://lore.kernel.org/bpf/20200819092342.259004-1-jolsa@kernel.org/ [6] https://lore.kernel.org/bpf/cover.1707223196.git.vmalik@redhat.com/ Signed-off-by: Ihor Solodrai Signed-off-by: Andrii Nakryiko Tested-by: Alan Maguire Acked-by: Eduard Zingerman Link: https://lore.kernel.org/bpf/20251219181825.1289460-3-ihor.solodrai@linux.dev --- scripts/link-vmlinux.sh | 42 ++++++------------------------------------ 1 file changed, 6 insertions(+), 36 deletions(-) (limited to 'scripts/link-vmlinux.sh') diff --git a/scripts/link-vmlinux.sh b/scripts/link-vmlinux.sh index 4ab44c73da4d..e2207e612ac3 100755 --- a/scripts/link-vmlinux.sh +++ b/scripts/link-vmlinux.sh @@ -106,34 +106,6 @@ vmlinux_link() ${kallsymso} ${btf_vmlinux_bin_o} ${arch_vmlinux_o} ${ldlibs} } -# generate .BTF typeinfo from DWARF debuginfo -# ${1} - vmlinux image -gen_btf() -{ - local btf_data=${1}.btf.o - - info BTF "${btf_data}" - LLVM_OBJCOPY="${OBJCOPY}" ${PAHOLE} -J ${PAHOLE_FLAGS} ${1} - - # Create ${btf_data} which contains just .BTF section but no symbols. Add - # SHF_ALLOC because .BTF will be part of the vmlinux image. --strip-all - # deletes all symbols including __start_BTF and __stop_BTF, which will - # be redefined in the linker script. Add 2>/dev/null to suppress GNU - # objcopy warnings: "empty loadable segment detected at ..." - ${OBJCOPY} --only-section=.BTF --set-section-flags .BTF=alloc,readonly \ - --strip-all ${1} "${btf_data}" 2>/dev/null - # Change e_type to ET_REL so that it can be used to link final vmlinux. - # GNU ld 2.35+ and lld do not allow an ET_EXEC input. - if is_enabled CONFIG_CPU_BIG_ENDIAN; then - et_rel='\0\1' - else - et_rel='\1\0' - fi - printf "${et_rel}" | dd of="${btf_data}" conv=notrunc bs=1 seek=16 status=none - - btf_vmlinux_bin_o=${btf_data} -} - # Create ${2}.o file with all symbols from the ${1} object file kallsyms() { @@ -205,6 +177,7 @@ if is_enabled CONFIG_ARCH_WANTS_PRE_LINK_VMLINUX; then fi btf_vmlinux_bin_o= +btfids_vmlinux= kallsymso= strip_debug= generate_map= @@ -232,11 +205,13 @@ if is_enabled CONFIG_KALLSYMS || is_enabled CONFIG_DEBUG_INFO_BTF; then fi if is_enabled CONFIG_DEBUG_INFO_BTF; then - if ! gen_btf .tmp_vmlinux1; then + if ! ${srctree}/scripts/gen-btf.sh .tmp_vmlinux1; then echo >&2 "Failed to generate BTF for vmlinux" echo >&2 "Try to disable CONFIG_DEBUG_INFO_BTF" exit 1 fi + btf_vmlinux_bin_o=.tmp_vmlinux1.btf.o + btfids_vmlinux=.tmp_vmlinux1.BTF_ids fi if is_enabled CONFIG_KALLSYMS; then @@ -289,14 +264,9 @@ fi vmlinux_link "${VMLINUX}" -# fill in BTF IDs if is_enabled CONFIG_DEBUG_INFO_BTF; then - info BTFIDS "${VMLINUX}" - RESOLVE_BTFIDS_ARGS="" - if is_enabled CONFIG_WERROR; then - RESOLVE_BTFIDS_ARGS=" --fatal_warnings " - fi - ${RESOLVE_BTFIDS} ${RESOLVE_BTFIDS_ARGS} "${VMLINUX}" + info OBJCOPY ${btfids_vmlinux} + ${OBJCOPY} --update-section .BTF_ids=${btfids_vmlinux} ${VMLINUX} fi mksysmap "${VMLINUX}" System.map -- cgit v1.2.3 From 1a8fa7faf4890d201aad4f5d4943f74d840cd0ba Mon Sep 17 00:00:00 2001 From: Ihor Solodrai Date: Tue, 30 Dec 2025 17:25:57 -0800 Subject: resolve_btfids: Implement --patch_btfids Recent changes in BTF generation [1] rely on ${OBJCOPY} command to update .BTF_ids section data in target ELF files. This exposed a bug in llvm-objcopy --update-section code path, that may lead to corruption of a target ELF file. Specifically, because of the bug st_shndx of some symbols may be (incorrectly) set to 0xffff (SHN_XINDEX) [2][3]. While there is a pending fix for LLVM, it'll take some time before it lands (likely in 22.x). And the kernel build must keep working with older LLVM toolchains in the foreseeable future. Using GNU objcopy for .BTF_ids update would work, but it would require changes to LLVM-based build process, likely breaking existing build environments as discussed in [2]. To work around llvm-objcopy bug, implement --patch_btfids code path in resolve_btfids as a drop-in replacement for: ${OBJCOPY} --update-section .BTF_ids=${btf_ids} ${elf} Which works specifically for .BTF_ids section: ${RESOLVE_BTFIDS} --patch_btfids ${btf_ids} ${elf} This feature in resolve_btfids can be removed at some point in the future, when llvm-objcopy with a relevant bugfix becomes common. [1] https://lore.kernel.org/bpf/20251219181321.1283664-1-ihor.solodrai@linux.dev/ [2] https://lore.kernel.org/bpf/20251224005752.201911-1-ihor.solodrai@linux.dev/ [3] https://github.com/llvm/llvm-project/issues/168060#issuecomment-3533552952 Signed-off-by: Ihor Solodrai Link: https://lore.kernel.org/r/20251231012558.1699758-1-ihor.solodrai@linux.dev Signed-off-by: Alexei Starovoitov --- scripts/gen-btf.sh | 2 +- scripts/link-vmlinux.sh | 2 +- tools/bpf/resolve_btfids/main.c | 117 +++++++++++++++++++++++++++++++++++ tools/testing/selftests/bpf/Makefile | 2 +- 4 files changed, 120 insertions(+), 3 deletions(-) (limited to 'scripts/link-vmlinux.sh') diff --git a/scripts/gen-btf.sh b/scripts/gen-btf.sh index 12244dbe097c..0aec86615416 100755 --- a/scripts/gen-btf.sh +++ b/scripts/gen-btf.sh @@ -123,7 +123,7 @@ embed_btf_data() fi local btf_ids="${ELF_FILE}.BTF_ids" if [ -f "${btf_ids}" ]; then - ${OBJCOPY} --update-section .BTF_ids=${btf_ids} ${ELF_FILE} + ${RESOLVE_BTFIDS} --patch_btfids ${btf_ids} ${ELF_FILE} fi } diff --git a/scripts/link-vmlinux.sh b/scripts/link-vmlinux.sh index e2207e612ac3..1915adf3249b 100755 --- a/scripts/link-vmlinux.sh +++ b/scripts/link-vmlinux.sh @@ -266,7 +266,7 @@ vmlinux_link "${VMLINUX}" if is_enabled CONFIG_DEBUG_INFO_BTF; then info OBJCOPY ${btfids_vmlinux} - ${OBJCOPY} --update-section .BTF_ids=${btfids_vmlinux} ${VMLINUX} + ${RESOLVE_BTFIDS} --patch_btfids ${btfids_vmlinux} ${VMLINUX} fi mksysmap "${VMLINUX}" System.map diff --git a/tools/bpf/resolve_btfids/main.c b/tools/bpf/resolve_btfids/main.c index 2cbc252259be..df39982f51df 100644 --- a/tools/bpf/resolve_btfids/main.c +++ b/tools/bpf/resolve_btfids/main.c @@ -862,8 +862,119 @@ static inline int make_out_path(char *buf, u32 buf_sz, const char *in_path, cons return 0; } +/* + * Patch the .BTF_ids section of an ELF file with data from provided file. + * Equivalent to: objcopy --update-section .BTF_ids= + * + * 1. Find .BTF_ids section in the ELF + * 2. Verify that blob file size matches section size + * 3. Update section data buffer with blob data + * 4. Write the ELF file + */ +static int patch_btfids(const char *btfids_path, const char *elf_path) +{ + Elf_Scn *scn = NULL; + FILE *btfids_file; + size_t shdrstrndx; + int fd, err = -1; + Elf_Data *data; + struct stat st; + GElf_Shdr sh; + char *name; + Elf *elf; + + elf_version(EV_CURRENT); + + fd = open(elf_path, O_RDWR, 0666); + if (fd < 0) { + pr_err("FAILED to open %s: %s\n", elf_path, strerror(errno)); + return -1; + } + + elf = elf_begin(fd, ELF_C_RDWR_MMAP, NULL); + if (!elf) { + close(fd); + pr_err("FAILED cannot create ELF descriptor: %s\n", elf_errmsg(-1)); + return -1; + } + + elf_flagelf(elf, ELF_C_SET, ELF_F_LAYOUT); + + if (elf_getshdrstrndx(elf, &shdrstrndx) != 0) { + pr_err("FAILED cannot get shdr str ndx\n"); + goto out; + } + + while ((scn = elf_nextscn(elf, scn)) != NULL) { + + if (gelf_getshdr(scn, &sh) != &sh) { + pr_err("FAILED to get section header\n"); + goto out; + } + + name = elf_strptr(elf, shdrstrndx, sh.sh_name); + if (!name) + continue; + + if (strcmp(name, BTF_IDS_SECTION) == 0) + break; + } + + if (!scn) { + pr_err("FAILED: section %s not found in %s\n", BTF_IDS_SECTION, elf_path); + goto out; + } + + data = elf_getdata(scn, NULL); + if (!data) { + pr_err("FAILED to get %s section data from %s\n", BTF_IDS_SECTION, elf_path); + goto out; + } + + if (stat(btfids_path, &st) < 0) { + pr_err("FAILED to stat %s: %s\n", btfids_path, strerror(errno)); + goto out; + } + + if ((size_t)st.st_size != data->d_size) { + pr_err("FAILED: size mismatch - %s section in %s is %zu bytes, %s is %zu bytes\n", + BTF_IDS_SECTION, elf_path, data->d_size, btfids_path, (size_t)st.st_size); + goto out; + } + + btfids_file = fopen(btfids_path, "rb"); + if (!btfids_file) { + pr_err("FAILED to open %s: %s\n", btfids_path, strerror(errno)); + goto out; + } + + pr_debug("Copying data from %s to %s section of %s (%zu bytes)\n", + btfids_path, BTF_IDS_SECTION, elf_path, data->d_size); + + if (fread(data->d_buf, data->d_size, 1, btfids_file) != 1) { + pr_err("FAILED to read %s\n", btfids_path); + fclose(btfids_file); + goto out; + } + fclose(btfids_file); + + elf_flagdata(data, ELF_C_SET, ELF_F_DIRTY); + if (elf_update(elf, ELF_C_WRITE) < 0) { + pr_err("FAILED to update ELF file %s\n", elf_path); + goto out; + } + + err = 0; +out: + elf_end(elf); + close(fd); + + return err; +} + static const char * const resolve_btfids_usage[] = { "resolve_btfids [] ", + "resolve_btfids --patch_btfids <.BTF_ids file> ", NULL }; @@ -880,6 +991,7 @@ int main(int argc, const char **argv) .funcs = RB_ROOT, .sets = RB_ROOT, }; + const char *btfids_path = NULL; bool fatal_warnings = false; char out_path[PATH_MAX]; @@ -894,6 +1006,8 @@ int main(int argc, const char **argv) "turn warnings into errors"), OPT_BOOLEAN(0, "distill_base", &obj.distill_base, "distill --btf_base and emit .BTF.base section data"), + OPT_STRING(0, "patch_btfids", &btfids_path, "file", + "path to .BTF_ids section data blob to patch into ELF file"), OPT_END() }; int err = -1; @@ -905,6 +1019,9 @@ int main(int argc, const char **argv) obj.path = argv[0]; + if (btfids_path) + return patch_btfids(btfids_path, obj.path); + if (load_btf(&obj)) goto out; diff --git a/tools/testing/selftests/bpf/Makefile b/tools/testing/selftests/bpf/Makefile index f28a32b16ff0..9488d076c740 100644 --- a/tools/testing/selftests/bpf/Makefile +++ b/tools/testing/selftests/bpf/Makefile @@ -657,7 +657,7 @@ $(TRUNNER_TEST_OBJS): $(TRUNNER_OUTPUT)/%.test.o: \ $$(if $$(TEST_NEEDS_BTFIDS), \ $$(call msg,BTFIDS,$(TRUNNER_BINARY),$$@) \ $(RESOLVE_BTFIDS) --btf $(TRUNNER_OUTPUT)/btf_data.bpf.o $$@; \ - $(OBJCOPY) --update-section .BTF_ids=$$@.BTF_ids $$@) + $(RESOLVE_BTFIDS) --patch_btfids $$@.BTF_ids $$@) $(TRUNNER_TEST_OBJS:.o=.d): $(TRUNNER_OUTPUT)/%.test.d: \ $(TRUNNER_TESTS_DIR)/%.c \ -- cgit v1.2.3 From 453dece55bb1cc6812314497a1c5ecc0513457ed Mon Sep 17 00:00:00 2001 From: Ihor Solodrai Date: Wed, 31 Dec 2025 10:39:29 -0800 Subject: scripts/gen-btf.sh: Reduce log verbosity Remove info messages from gen-btf.sh, as they are unnecessarily detailed and sometimes inaccurate [1]. Verbose log can be produced by passing V=1 to make, which will set -x for the shell. [1] https://lore.kernel.org/bpf/CAADnVQ+biTSDaNtoL=ct9XtBJiXYMUqGYLqu604C3D8N+8YH9A@mail.gmail.com/ Suggested-by: Alexei Starovoitov Signed-off-by: Ihor Solodrai Link: https://lore.kernel.org/r/20251231183929.65668-1-ihor.solodrai@linux.dev Signed-off-by: Alexei Starovoitov --- scripts/gen-btf.sh | 10 ---------- scripts/link-vmlinux.sh | 3 ++- 2 files changed, 2 insertions(+), 11 deletions(-) (limited to 'scripts/link-vmlinux.sh') diff --git a/scripts/gen-btf.sh b/scripts/gen-btf.sh index 0aec86615416..d6457661b9b6 100755 --- a/scripts/gen-btf.sh +++ b/scripts/gen-btf.sh @@ -60,28 +60,20 @@ is_enabled() { grep -q "^$1=y" ${objtree}/include/config/auto.conf } -info() -{ - printf " %-7s %s\n" "${1}" "${2}" -} - case "${KBUILD_VERBOSE}" in *1*) set -x ;; esac - gen_btf_data() { - info BTF "${ELF_FILE}" btf1="${ELF_FILE}.BTF.1" ${PAHOLE} -J ${PAHOLE_FLAGS} \ ${BTF_BASE:+--btf_base ${BTF_BASE}} \ --btf_encode_detached=${btf1} \ "${ELF_FILE}" - info BTFIDS "${ELF_FILE}" ${RESOLVE_BTFIDS} ${RESOLVE_BTFIDS_FLAGS} \ ${BTF_BASE:+--btf_base ${BTF_BASE}} \ --btf ${btf1} "${ELF_FILE}" @@ -95,7 +87,6 @@ gen_btf_o() # SHF_ALLOC because .BTF will be part of the vmlinux image. --strip-all # deletes all symbols including __start_BTF and __stop_BTF, which will # be redefined in the linker script. - info OBJCOPY "${btf_data}" echo "" | ${CC} ${CLANG_FLAGS} ${KBUILD_CFLAGS} -c -x c -o ${btf_data} - ${OBJCOPY} --add-section .BTF=${ELF_FILE}.BTF \ --set-section-flags .BTF=alloc,readonly ${btf_data} @@ -113,7 +104,6 @@ gen_btf_o() embed_btf_data() { - info OBJCOPY "${ELF_FILE}.BTF" ${OBJCOPY} --add-section .BTF=${ELF_FILE}.BTF ${ELF_FILE} # a module might not have a .BTF_ids or .BTF.base section diff --git a/scripts/link-vmlinux.sh b/scripts/link-vmlinux.sh index 1915adf3249b..08cd8e25c65c 100755 --- a/scripts/link-vmlinux.sh +++ b/scripts/link-vmlinux.sh @@ -205,6 +205,7 @@ if is_enabled CONFIG_KALLSYMS || is_enabled CONFIG_DEBUG_INFO_BTF; then fi if is_enabled CONFIG_DEBUG_INFO_BTF; then + info BTF .tmp_vmlinux1 if ! ${srctree}/scripts/gen-btf.sh .tmp_vmlinux1; then echo >&2 "Failed to generate BTF for vmlinux" echo >&2 "Try to disable CONFIG_DEBUG_INFO_BTF" @@ -265,7 +266,7 @@ fi vmlinux_link "${VMLINUX}" if is_enabled CONFIG_DEBUG_INFO_BTF; then - info OBJCOPY ${btfids_vmlinux} + info BTFIDS ${VMLINUX} ${RESOLVE_BTFIDS} --patch_btfids ${btfids_vmlinux} ${VMLINUX} fi -- cgit v1.2.3 From 26ad5d6e763070aa146d86b941884b11eb1ac0aa Mon Sep 17 00:00:00 2001 From: Ihor Solodrai Date: Wed, 21 Jan 2026 10:16:17 -0800 Subject: scripts/gen-btf.sh: Use CONFIG_SHELL for execution MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit According to the docs [1], kernel build scripts should be executed via CONFIG_SHELL, which is sh by default. Fixup gen-btf.sh to be runnable with sh, and use CONFIG_SHELL at every invocation site. See relevant discussion for context [2]. [1] https://docs.kernel.org/kbuild/makefiles.html#script-invocation [2] https://lore.kernel.org/bpf/CAADnVQ+dxmSNoJAGb6xV89ffUCKXe5CJXovXZt22nv5iYFV5mw@mail.gmail.com/ Signed-off-by: Ihor Solodrai Tested-by: Gary Guo Reported-by: Gary Guo Suggested-by: Thomas Weißschuh Fixes: 522397d05e7d ("resolve_btfids: Change in-place update with raw binary output") Link: https://lore.kernel.org/r/20260121181617.820300-1-ihor.solodrai@linux.dev Signed-off-by: Alexei Starovoitov --- scripts/Makefile.modfinal | 2 +- scripts/gen-btf.sh | 8 ++++---- scripts/link-vmlinux.sh | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) (limited to 'scripts/link-vmlinux.sh') diff --git a/scripts/Makefile.modfinal b/scripts/Makefile.modfinal index 422c56dc878e..adcbcde16a07 100644 --- a/scripts/Makefile.modfinal +++ b/scripts/Makefile.modfinal @@ -43,7 +43,7 @@ quiet_cmd_btf_ko = BTF [M] $@ if [ ! -f $(objtree)/vmlinux ]; then \ printf "Skipping BTF generation for %s due to unavailability of vmlinux\n" $@ 1>&2; \ else \ - $(srctree)/scripts/gen-btf.sh --btf_base $(objtree)/vmlinux $@; \ + $(CONFIG_SHELL) $(srctree)/scripts/gen-btf.sh --btf_base $(objtree)/vmlinux $@; \ fi; # Same as newer-prereqs, but allows to exclude specified extra dependencies diff --git a/scripts/gen-btf.sh b/scripts/gen-btf.sh index be21ccee3487..8ca96eb10a69 100755 --- a/scripts/gen-btf.sh +++ b/scripts/gen-btf.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/bin/sh # SPDX-License-Identifier: GPL-2.0 # Copyright (c) 2025 Meta Platforms, Inc. and affiliates. # @@ -81,7 +81,7 @@ gen_btf_data() gen_btf_o() { - local btf_data=${ELF_FILE}.btf.o + btf_data=${ELF_FILE}.btf.o # Create ${btf_data} which contains just .BTF section but no symbols. Add # SHF_ALLOC because .BTF will be part of the vmlinux image. --strip-all @@ -107,11 +107,11 @@ embed_btf_data() ${OBJCOPY} --add-section .BTF=${ELF_FILE}.BTF ${ELF_FILE} # a module might not have a .BTF_ids or .BTF.base section - local btf_base="${ELF_FILE}.BTF.base" + btf_base="${ELF_FILE}.BTF.base" if [ -f "${btf_base}" ]; then ${OBJCOPY} --add-section .BTF.base=${btf_base} ${ELF_FILE} fi - local btf_ids="${ELF_FILE}.BTF_ids" + btf_ids="${ELF_FILE}.BTF_ids" if [ -f "${btf_ids}" ]; then ${RESOLVE_BTFIDS} --patch_btfids ${btf_ids} ${ELF_FILE} fi diff --git a/scripts/link-vmlinux.sh b/scripts/link-vmlinux.sh index 08cd8e25c65c..16d6a048e07c 100755 --- a/scripts/link-vmlinux.sh +++ b/scripts/link-vmlinux.sh @@ -206,7 +206,7 @@ fi if is_enabled CONFIG_DEBUG_INFO_BTF; then info BTF .tmp_vmlinux1 - if ! ${srctree}/scripts/gen-btf.sh .tmp_vmlinux1; then + if ! ${CONFIG_SHELL} ${srctree}/scripts/gen-btf.sh .tmp_vmlinux1; then echo >&2 "Failed to generate BTF for vmlinux" echo >&2 "Try to disable CONFIG_DEBUG_INFO_BTF" exit 1 -- cgit v1.2.3 From a081b5789255d27b76cd2cbab85676b2a31dbde1 Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Fri, 16 Jan 2026 10:34:02 +0100 Subject: kallsyms: Get rid of kallsyms relative base When the kallsyms relative base was introduced, per-CPU variable references on x86_64 SMP were implemented as offsets into the respective per-CPU region, rather than offsets relative to the location of the variable's template in the kernel image, which is how other architectures implement it. This required kallsyms to reason about the difference between the two, and the sign of the value in the kallsyms_offsets[] array was used to distinguish them. This meant that negative offsets were not permitted for ordinary variables, and so it was crucial that the relative base was chosen such that all offsets were positive numbers. This is no longer needed: instead, the offsets can simply be encoded as values in the range -/+ 2 GiB, which is precisely what PC32 relocations provide on most architectures. So it is possible to simplify the logic, and just use _text as the anchor directly, and let the linker calculate the final value based on the location of the entry itself. Some architectures (nios2, extensa) do not support place-relative relocations at all, but these are all 32-bit and non-relocatable, and so there is no need for place-relative relocations in the first place, and the actual symbol values can just be stored directly. This makes all entries in the kallsyms_offsets[] array visible as place-relative references in the ELF metadata, which will be important when implementing ELF-based fg-kaslr. Reviewed-by: Kees Cook Signed-off-by: Ard Biesheuvel Link: https://patch.msgid.link/20260116093359.2442297-6-ardb+git@google.com Signed-off-by: Nathan Chancellor --- kernel/kallsyms.c | 6 ++-- kernel/kallsyms_internal.h | 1 - kernel/vmcore_info.c | 1 - scripts/kallsyms.c | 64 ++++++++++--------------------------- scripts/link-vmlinux.sh | 4 +++ tools/perf/tests/vmlinux-kallsyms.c | 1 - 6 files changed, 25 insertions(+), 52 deletions(-) (limited to 'scripts/link-vmlinux.sh') diff --git a/kernel/kallsyms.c b/kernel/kallsyms.c index 049e296f586c..6125724aadb1 100644 --- a/kernel/kallsyms.c +++ b/kernel/kallsyms.c @@ -151,8 +151,10 @@ static unsigned int get_symbol_offset(unsigned long pos) unsigned long kallsyms_sym_address(int idx) { - /* values are unsigned offsets */ - return kallsyms_relative_base + (u32)kallsyms_offsets[idx]; + /* non-relocatable 32-bit kernels just embed the value directly */ + if (!IS_ENABLED(CONFIG_64BIT) && !IS_ENABLED(CONFIG_RELOCATABLE)) + return (u32)kallsyms_offsets[idx]; + return (unsigned long)offset_to_ptr(kallsyms_offsets + idx); } static unsigned int get_symbol_seq(int index) diff --git a/kernel/kallsyms_internal.h b/kernel/kallsyms_internal.h index 9633782f8250..81a867dbe57d 100644 --- a/kernel/kallsyms_internal.h +++ b/kernel/kallsyms_internal.h @@ -8,7 +8,6 @@ extern const int kallsyms_offsets[]; extern const u8 kallsyms_names[]; extern const unsigned int kallsyms_num_syms; -extern const unsigned long kallsyms_relative_base; extern const char kallsyms_token_table[]; extern const u16 kallsyms_token_index[]; diff --git a/kernel/vmcore_info.c b/kernel/vmcore_info.c index fe9bf8db1922..f114719f6cb5 100644 --- a/kernel/vmcore_info.c +++ b/kernel/vmcore_info.c @@ -238,7 +238,6 @@ static int __init crash_save_vmcoreinfo_init(void) VMCOREINFO_SYMBOL(kallsyms_token_table); VMCOREINFO_SYMBOL(kallsyms_token_index); VMCOREINFO_SYMBOL(kallsyms_offsets); - VMCOREINFO_SYMBOL(kallsyms_relative_base); #endif /* CONFIG_KALLSYMS */ arch_crash_save_vmcoreinfo(); diff --git a/scripts/kallsyms.c b/scripts/kallsyms.c index 4b0234e4b12f..37d5c095ad22 100644 --- a/scripts/kallsyms.c +++ b/scripts/kallsyms.c @@ -46,7 +46,6 @@ struct addr_range { }; static unsigned long long _text; -static unsigned long long relative_base; static struct addr_range text_ranges[] = { { "_stext", "_etext" }, { "_sinittext", "_einittext" }, @@ -57,6 +56,7 @@ static struct addr_range text_ranges[] = { static struct sym_entry **table; static unsigned int table_size, table_cnt; static int all_symbols; +static int pc_relative; static int token_profit[0x10000]; @@ -280,7 +280,7 @@ static void read_map(const char *in) static void output_label(const char *label) { printf(".globl %s\n", label); - printf("\tALGN\n"); + printf("\t.balign 4\n"); printf("%s:\n", label); } @@ -343,15 +343,6 @@ static void write_src(void) unsigned int *markers, markers_cnt; char buf[KSYM_NAME_LEN]; - printf("#include \n"); - printf("#if BITS_PER_LONG == 64\n"); - printf("#define PTR .quad\n"); - printf("#define ALGN .balign 8\n"); - printf("#else\n"); - printf("#define PTR .long\n"); - printf("#define ALGN .balign 4\n"); - printf("#endif\n"); - printf("\t.section .rodata, \"a\"\n"); output_label("kallsyms_num_syms"); @@ -434,34 +425,24 @@ static void write_src(void) output_label("kallsyms_offsets"); for (i = 0; i < table_cnt; i++) { - /* - * Use the offset relative to the lowest value - * encountered of all relative symbols, and emit - * non-relocatable fixed offsets that will be fixed - * up at runtime. - */ - - long long offset; - - offset = table[i]->addr - relative_base; - if (offset < 0 || offset > UINT_MAX) { - fprintf(stderr, "kallsyms failure: " - "relative symbol value %#llx out of range\n", - table[i]->addr); - exit(EXIT_FAILURE); + if (pc_relative) { + long long offset = table[i]->addr - _text; + + if (offset < INT_MIN || offset > INT_MAX) { + fprintf(stderr, "kallsyms failure: " + "relative symbol value %#llx out of range\n", + table[i]->addr); + exit(EXIT_FAILURE); + } + printf("\t.long\t_text - . + (%d)\t/* %s */\n", + (int)offset, table[i]->sym); + } else { + printf("\t.long\t%#x\t/* %s */\n", + (unsigned int)table[i]->addr, table[i]->sym); } - printf("\t.long\t%#x\t/* %s */\n", (int)offset, table[i]->sym); } printf("\n"); - output_label("kallsyms_relative_base"); - /* Provide proper symbols relocatability by their '_text' relativeness. */ - if (_text <= relative_base) - printf("\tPTR\t_text + %#llx\n", relative_base - _text); - else - printf("\tPTR\t_text - %#llx\n", _text - relative_base); - printf("\n"); - sort_symbols_by_name(); output_label("kallsyms_seqs_of_names"); for (i = 0; i < table_cnt; i++) @@ -701,22 +682,12 @@ static void sort_symbols(void) qsort(table, table_cnt, sizeof(table[0]), compare_symbols); } -/* find the minimum non-absolute symbol address */ -static void record_relative_base(void) -{ - /* - * The table is sorted by address. - * Take the first symbol value. - */ - if (table_cnt) - relative_base = table[0]->addr; -} - int main(int argc, char **argv) { while (1) { static const struct option long_options[] = { {"all-symbols", no_argument, &all_symbols, 1}, + {"pc-relative", no_argument, &pc_relative, 1}, {}, }; @@ -734,7 +705,6 @@ int main(int argc, char **argv) read_map(argv[optind]); shrink_table(); sort_symbols(); - record_relative_base(); optimize_token_table(); write_src(); diff --git a/scripts/link-vmlinux.sh b/scripts/link-vmlinux.sh index 4ab44c73da4d..73531cb63efc 100755 --- a/scripts/link-vmlinux.sh +++ b/scripts/link-vmlinux.sh @@ -143,6 +143,10 @@ kallsyms() kallsymopt="${kallsymopt} --all-symbols" fi + if is_enabled CONFIG_64BIT || is_enabled CONFIG_RELOCATABLE; then + kallsymopt="${kallsymopt} --pc-relative" + fi + info KSYMS "${2}.S" scripts/kallsyms ${kallsymopt} "${1}" > "${2}.S" diff --git a/tools/perf/tests/vmlinux-kallsyms.c b/tools/perf/tests/vmlinux-kallsyms.c index 74cdbd2ce9d0..524d46478364 100644 --- a/tools/perf/tests/vmlinux-kallsyms.c +++ b/tools/perf/tests/vmlinux-kallsyms.c @@ -27,7 +27,6 @@ static bool is_ignored_symbol(const char *name, char type) * stable symbol list. */ "kallsyms_offsets", - "kallsyms_relative_base", "kallsyms_num_syms", "kallsyms_names", "kallsyms_markers", -- cgit v1.2.3