Skip to content

Commit a246670

Browse files
theihorKernel Patches Daemon
authored andcommitted
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. This patch changes 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}.distilled_base.btf with .BTF.base section data (for modules) * ${1}.btf_ids with .BTF_ids section data, if it exists in ${1} The execution of resolve_btfids and consumption of its output is orchestrated by scripts/gen-btf.sh introduced in this patch. The rationale for this approach is that updating ELF in-place with libelf API is complicated and bug-prone, especially in the context of the kernel build. On the other hand applying objcopy to manipulate ELF sections is simpler and more reliable. 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 (details below). The patch doesn't change this approach, only the implementation is a little different. Before this patch it worked like follows: * pahole consumed .tmp_vmlinux1 [1] and added .BTF section with llvm-objcopy [2] to it * then everything except the .BTF section was stripped from .tmp_vmlinux1 into a .tmp_vmlinux1.bpf.o object [1], later linked into vmlinux * resolve_btfids was executed later on vmlinux.unstripped [3], 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 the kernel modules creating special .bpf.o file is not necessary, and so embedding of sections data produced by resolve_btfids is straightforward with the objcopy. [1] https://git.kernel.org/pub/scm/linux/kernel/git/bpf/bpf.git/tree/scripts/link-vmlinux.sh#n115 [2] https://git.kernel.org/pub/scm/devel/pahole/pahole.git/tree/btf_encoder.c#n1835 [3] https://git.kernel.org/pub/scm/linux/kernel/git/bpf/bpf.git/tree/scripts/link-vmlinux.sh#n285 Signed-off-by: Ihor Solodrai <[email protected]>
1 parent 919af32 commit a246670

File tree

5 files changed

+272
-66
lines changed

5 files changed

+272
-66
lines changed

MAINTAINERS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4672,6 +4672,7 @@ F: net/sched/act_bpf.c
46724672
F: net/sched/cls_bpf.c
46734673
F: samples/bpf/
46744674
F: scripts/bpf_doc.py
4675+
F: scripts/gen-btf.sh
46754676
F: scripts/Makefile.btf
46764677
F: scripts/pahole-version.sh
46774678
F: tools/bpf/

scripts/Makefile.modfinal

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,9 +38,8 @@ quiet_cmd_btf_ko = BTF [M] $@
3838
cmd_btf_ko = \
3939
if [ ! -f $(objtree)/vmlinux ]; then \
4040
printf "Skipping BTF generation for %s due to unavailability of vmlinux\n" $@ 1>&2; \
41-
else \
42-
LLVM_OBJCOPY="$(OBJCOPY)" $(PAHOLE) -J $(PAHOLE_FLAGS) $(MODULE_PAHOLE_FLAGS) --btf_base $(objtree)/vmlinux $@; \
43-
$(RESOLVE_BTFIDS) -b $(objtree)/vmlinux $@; \
41+
else \
42+
$(objtree)/scripts/gen-btf.sh --btf_base $(objtree)/vmlinux $@; \
4443
fi;
4544

4645
# Same as newer-prereqs, but allows to exclude specified extra dependencies

scripts/gen-btf.sh

Lines changed: 166 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,166 @@
1+
#!/bin/sh
2+
# SPDX-License-Identifier: GPL-2.0
3+
# Copyright (c) 2025 Meta Platforms, Inc. and affiliates.
4+
#
5+
# This script generates BTF data for the provided ELF file.
6+
#
7+
# Kernel BTF generation involves these conceptual steps:
8+
# 1. pahole generates BTF from DWARF data
9+
# 2. resolve_btfids applies kernel-specific btf2btf
10+
# transformations and computes data for .BTF_ids section
11+
# 3. the result gets linked/objcopied into the target binary
12+
#
13+
# How step (3) should be done differs between vmlinux, and
14+
# kernel modules, which is the primary reason for the existence
15+
# of this script.
16+
#
17+
# For modules the script expects vmlinux passed in as --btf_base.
18+
# Generated .BTF, .BTF.base and .BTF_ids sections become embedded
19+
# into the input ELF file with objcopy.
20+
#
21+
# For vmlinux the input file remains unchanged and two files are produced:
22+
# - ${1}.btf.o ready for linking into vmlinux
23+
# - ${1}.btf_ids with .BTF_ids data blob
24+
# This output is consumed by scripts/link-vmlinux.sh
25+
26+
set -e
27+
28+
usage()
29+
{
30+
echo "Usage: $0 [--btf_base <file>] <target ELF file>"
31+
exit 1
32+
}
33+
34+
BTF_BASE=""
35+
36+
while [ $# -gt 0 ]; do
37+
case "$1" in
38+
--btf_base)
39+
BTF_BASE="$2"
40+
shift 2
41+
;;
42+
-*)
43+
echo "Unknown option: $1" >&2
44+
usage
45+
;;
46+
*)
47+
break
48+
;;
49+
esac
50+
done
51+
52+
if [ $# -ne 1 ]; then
53+
usage
54+
fi
55+
56+
ELF_FILE="$1"
57+
shift
58+
59+
is_enabled() {
60+
grep -q "^$1=y" ${objtree}/include/config/auto.conf
61+
}
62+
63+
info()
64+
{
65+
printf " %-7s %s\n" "${1}" "${2}"
66+
}
67+
68+
case "${KBUILD_VERBOSE}" in
69+
*1*)
70+
set -x
71+
;;
72+
esac
73+
74+
if ! is_enabled CONFIG_DEBUG_INFO_BTF; then
75+
exit 0
76+
fi
77+
78+
gen_btf_data()
79+
{
80+
info BTF "${ELF_FILE}"
81+
btf1="${ELF_FILE}.btf.1"
82+
${PAHOLE} -J ${PAHOLE_FLAGS} \
83+
${BTF_BASE:+--btf_base ${BTF_BASE}} \
84+
--btf_encode_detached=${btf1} \
85+
"${ELF_FILE}"
86+
87+
info BTFIDS "${ELF_FILE}"
88+
RESOLVE_BTFIDS_OPTS=""
89+
if is_enabled CONFIG_WERROR; then
90+
RESOLVE_BTFIDS_OPTS+=" --fatal_warnings "
91+
fi
92+
if [ -n "${KBUILD_VERBOSE}" ]; then
93+
RESOLVE_BTFIDS_OPTS+=" -v "
94+
fi
95+
${RESOLVE_BTFIDS} ${RESOLVE_BTFIDS_OPTS} \
96+
${BTF_BASE:+--btf_base ${BTF_BASE}} \
97+
--btf ${btf1} "${ELF_FILE}"
98+
}
99+
100+
gen_btf_o()
101+
{
102+
local btf_data=${ELF_FILE}.btf.o
103+
104+
# Create ${btf_data} which contains just .BTF section but no symbols. Add
105+
# SHF_ALLOC because .BTF will be part of the vmlinux image. --strip-all
106+
# deletes all symbols including __start_BTF and __stop_BTF, which will
107+
# be redefined in the linker script.
108+
info OBJCOPY "${btf_data}"
109+
echo "" | ${CC} -c -x c -o ${btf_data} -
110+
${OBJCOPY} --add-section .BTF=${ELF_FILE}.btf \
111+
--set-section-flags .BTF=alloc,readonly ${btf_data}
112+
${OBJCOPY} --only-section=.BTF --strip-all ${btf_data}
113+
114+
# Change e_type to ET_REL so that it can be used to link final vmlinux.
115+
# GNU ld 2.35+ and lld do not allow an ET_EXEC input.
116+
if is_enabled CONFIG_CPU_BIG_ENDIAN; then
117+
et_rel='\0\1'
118+
else
119+
et_rel='\1\0'
120+
fi
121+
printf "${et_rel}" | dd of="${btf_data}" conv=notrunc bs=1 seek=16 status=none
122+
}
123+
124+
embed_btf_data()
125+
{
126+
info OBJCOPY "${ELF_FILE}"
127+
128+
${OBJCOPY} \
129+
--add-section .BTF=${ELF_FILE}.btf \
130+
--add-section .BTF.base=${ELF_FILE}.distilled_base.btf \
131+
${ELF_FILE}
132+
133+
# a module might not have a .BTF_ids section
134+
if [ -f "${ELF_FILE}.btf_ids" ]; then
135+
${OBJCOPY} --update-section .BTF_ids=${ELF_FILE}.btf_ids ${ELF_FILE}
136+
fi
137+
}
138+
139+
cleanup()
140+
{
141+
rm -f "${ELF_FILE}.btf.1"
142+
rm -f "${ELF_FILE}.btf"
143+
if [ "${BTFGEN_MODE}" == "module" ]; then
144+
rm -f "${ELF_FILE}.distilled_base.btf"
145+
rm -f "${ELF_FILE}.btf_ids"
146+
fi
147+
}
148+
trap cleanup EXIT
149+
150+
BTFGEN_MODE="vmlinux"
151+
if [ -n "${BTF_BASE}" ]; then
152+
BTFGEN_MODE="module"
153+
fi
154+
155+
gen_btf_data
156+
157+
case "${BTFGEN_MODE}" in
158+
vmlinux)
159+
gen_btf_o
160+
;;
161+
module)
162+
embed_btf_data
163+
;;
164+
esac
165+
166+
exit 0

scripts/link-vmlinux.sh

Lines changed: 6 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -105,34 +105,6 @@ vmlinux_link()
105105
${kallsymso} ${btf_vmlinux_bin_o} ${arch_vmlinux_o} ${ldlibs}
106106
}
107107

108-
# generate .BTF typeinfo from DWARF debuginfo
109-
# ${1} - vmlinux image
110-
gen_btf()
111-
{
112-
local btf_data=${1}.btf.o
113-
114-
info BTF "${btf_data}"
115-
LLVM_OBJCOPY="${OBJCOPY}" ${PAHOLE} -J ${PAHOLE_FLAGS} ${1}
116-
117-
# Create ${btf_data} which contains just .BTF section but no symbols. Add
118-
# SHF_ALLOC because .BTF will be part of the vmlinux image. --strip-all
119-
# deletes all symbols including __start_BTF and __stop_BTF, which will
120-
# be redefined in the linker script. Add 2>/dev/null to suppress GNU
121-
# objcopy warnings: "empty loadable segment detected at ..."
122-
${OBJCOPY} --only-section=.BTF --set-section-flags .BTF=alloc,readonly \
123-
--strip-all ${1} "${btf_data}" 2>/dev/null
124-
# Change e_type to ET_REL so that it can be used to link final vmlinux.
125-
# GNU ld 2.35+ and lld do not allow an ET_EXEC input.
126-
if is_enabled CONFIG_CPU_BIG_ENDIAN; then
127-
et_rel='\0\1'
128-
else
129-
et_rel='\1\0'
130-
fi
131-
printf "${et_rel}" | dd of="${btf_data}" conv=notrunc bs=1 seek=16 status=none
132-
133-
btf_vmlinux_bin_o=${btf_data}
134-
}
135-
136108
# Create ${2}.o file with all symbols from the ${1} object file
137109
kallsyms()
138110
{
@@ -204,6 +176,7 @@ if is_enabled CONFIG_ARCH_WANTS_PRE_LINK_VMLINUX; then
204176
fi
205177

206178
btf_vmlinux_bin_o=
179+
btfids_vmlinux=
207180
kallsymso=
208181
strip_debug=
209182
generate_map=
@@ -224,11 +197,13 @@ if is_enabled CONFIG_KALLSYMS || is_enabled CONFIG_DEBUG_INFO_BTF; then
224197
fi
225198

226199
if is_enabled CONFIG_DEBUG_INFO_BTF; then
227-
if ! gen_btf .tmp_vmlinux1; then
200+
if ! scripts/gen-btf.sh .tmp_vmlinux1; then
228201
echo >&2 "Failed to generate BTF for vmlinux"
229202
echo >&2 "Try to disable CONFIG_DEBUG_INFO_BTF"
230203
exit 1
231204
fi
205+
btf_vmlinux_bin_o=.tmp_vmlinux1.btf.o
206+
btfids_vmlinux=.tmp_vmlinux1.btf_ids
232207
fi
233208

234209
if is_enabled CONFIG_KALLSYMS; then
@@ -281,14 +256,9 @@ fi
281256

282257
vmlinux_link "${VMLINUX}"
283258

284-
# fill in BTF IDs
285259
if is_enabled CONFIG_DEBUG_INFO_BTF; then
286-
info BTFIDS "${VMLINUX}"
287-
RESOLVE_BTFIDS_ARGS=""
288-
if is_enabled CONFIG_WERROR; then
289-
RESOLVE_BTFIDS_ARGS=" --fatal_warnings "
290-
fi
291-
${RESOLVE_BTFIDS} ${RESOLVE_BTFIDS_ARGS} "${VMLINUX}"
260+
info OBJCOPY ${btfids_vmlinux}
261+
${OBJCOPY} --update-section .BTF_ids=${btfids_vmlinux} ${VMLINUX}
292262
fi
293263

294264
mksysmap "${VMLINUX}" System.map

0 commit comments

Comments
 (0)