summaryrefslogtreecommitdiff
path: root/scripts/gen-btf.sh
blob: 8ca96eb10a69e025119c1db8f42cd97f7c16b929 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
#!/bin/sh
# SPDX-License-Identifier: GPL-2.0
# Copyright (c) 2025 Meta Platforms, Inc. and affiliates.
#
# This script generates BTF data for the provided ELF file.
#
# Kernel BTF generation involves these conceptual steps:
#   1. pahole generates BTF from DWARF data
#   2. resolve_btfids applies kernel-specific btf2btf
#      transformations and computes data for .BTF_ids section
#   3. the result gets linked/objcopied into the target binary
#
# How step (3) should be done differs between vmlinux, and
# kernel modules, which is the primary reason for the existence
# of this script.
#
# For modules the script expects vmlinux passed in as --btf_base.
# Generated .BTF, .BTF.base and .BTF_ids sections become embedded
# into the input ELF file with objcopy.
#
# For vmlinux the input file remains unchanged and two files are produced:
#   - ${1}.btf.o ready for linking into vmlinux
#   - ${1}.BTF_ids with .BTF_ids data blob
# This output is consumed by scripts/link-vmlinux.sh

set -e

usage()
{
	echo "Usage: $0 [--btf_base <file>] <target ELF file>"
	exit 1
}

BTF_BASE=""

while [ $# -gt 0 ]; do
	case "$1" in
	--btf_base)
		BTF_BASE="$2"
		shift 2
		;;
	-*)
		echo "Unknown option: $1" >&2
		usage
		;;
	*)
		break
		;;
	esac
done

if [ $# -ne 1 ]; then
	usage
fi

ELF_FILE="$1"
shift

is_enabled() {
	grep -q "^$1=y" ${objtree}/include/config/auto.conf
}

case "${KBUILD_VERBOSE}" in
*1*)
	set -x
	;;
esac

gen_btf_data()
{
	btf1="${ELF_FILE}.BTF.1"
	${PAHOLE} -J ${PAHOLE_FLAGS}			\
		${BTF_BASE:+--btf_base ${BTF_BASE}}	\
		--btf_encode_detached=${btf1}		\
		"${ELF_FILE}"

	${RESOLVE_BTFIDS} ${RESOLVE_BTFIDS_FLAGS}	\
		${BTF_BASE:+--btf_base ${BTF_BASE}}	\
		--btf ${btf1} "${ELF_FILE}"
}

gen_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
	# deletes all symbols including __start_BTF and __stop_BTF, which will
	# be redefined in the linker script.
	echo "" | ${CC} ${CLANG_FLAGS} ${KBUILD_CPPFLAGS} ${KBUILD_CFLAGS} -fno-lto -c -x c -o ${btf_data} -
	${OBJCOPY} --add-section .BTF=${ELF_FILE}.BTF \
		--set-section-flags .BTF=alloc,readonly ${btf_data}
	${OBJCOPY} --only-section=.BTF --strip-all ${btf_data}

	# 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
}

embed_btf_data()
{
	${OBJCOPY} --add-section .BTF=${ELF_FILE}.BTF ${ELF_FILE}

	# a module might not have a .BTF_ids or .BTF.base section
	btf_base="${ELF_FILE}.BTF.base"
	if [ -f "${btf_base}" ]; then
		${OBJCOPY} --add-section .BTF.base=${btf_base} ${ELF_FILE}
	fi
	btf_ids="${ELF_FILE}.BTF_ids"
	if [ -f "${btf_ids}" ]; then
		${RESOLVE_BTFIDS} --patch_btfids ${btf_ids} ${ELF_FILE}
	fi
}

cleanup()
{
	rm -f "${ELF_FILE}.BTF.1"
	rm -f "${ELF_FILE}.BTF"
	if [ "${BTFGEN_MODE}" = "module" ]; then
		rm -f "${ELF_FILE}.BTF.base"
		rm -f "${ELF_FILE}.BTF_ids"
	fi
}
trap cleanup EXIT

BTFGEN_MODE="vmlinux"
if [ -n "${BTF_BASE}" ]; then
	BTFGEN_MODE="module"
fi

gen_btf_data

case "${BTFGEN_MODE}" in
vmlinux)
	gen_btf_o
	;;
module)
	embed_btf_data
	;;
esac

exit 0