History

Merge tag 'objtool-core-2022-05-23' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip

Pull objtool updates from Ingo Molnar: - Comprehensive interface overhaul: ================================= Objtool's interface has some issues: - Several features are done unconditionally, without any way to turn them off. Some of them might be surprising. This makes objtool tricky to use, and prevents porting individual features to other arches. - The config dependencies are too coarse-grained. Objtool enablement is tied to CONFIG_STACK_VALIDATION, but it has several other features independent of that. - The objtool subcmds ("check" and "orc") are clumsy: "check" is really a subset of "orc", so it has all the same options. The subcmd model has never really worked for objtool, as it only has a single purpose: "do some combination of things on an object file". - The '--lto' and '--vmlinux' options are nonsensical and have surprising behavior. Overhaul the interface: - get rid of subcmds - make all features individually selectable - remove and/or clarify confusing/obsolete options - update the documentation - fix some bugs found along the way - Fix x32 regression - Fix Kbuild cleanup bugs - Add scripts/objdump-func helper script to disassemble a single function from an object file. - Rewrite scripts/faddr2line to be section-aware, by basing it on 'readelf', moving it away from 'nm', which doesn't handle multiple sections well, which can result in decoding failure. - Rewrite & fix symbol handling - which had a number of bugs wrt. object files that don't have global symbols - which is rare but possible. Also fix a bunch of symbol handling bugs found along the way. * tag 'objtool-core-2022-05-23' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip: (23 commits) objtool: Fix objtool regression on x32 systems objtool: Fix symbol creation scripts/faddr2line: Fix overlapping text section failures scripts: Create objdump-func helper script objtool: Remove libsubcmd.a when make clean objtool: Remove inat-tables.c when make clean objtool: Update documentation objtool: Remove --lto and --vmlinux in favor of --link objtool: Add HAVE_NOINSTR_VALIDATION objtool: Rename "VMLINUX_VALIDATION" -> "NOINSTR_VALIDATION" objtool: Make noinstr hacks optional objtool: Make jump label hack optional objtool: Make static call annotation optional objtool: Make stack validation frame-pointer-specific objtool: Add CONFIG_OBJTOOL objtool: Extricate sls from stack validation objtool: Rework ibt and extricate from stack validation objtool: Make stack validation optional objtool: Add option to print section addresses objtool: Don't print parentheses in function addresses ...

Linus Torvalds committed 4 years ago

Commit 22922de2 parents 2319be1 22682a034 files · +955 −683Browse files

Showing the changes to scripts (5 of 34 files). Show all

Unified · Split

5 files
  1. scripts/Makefile.build +14 −9
  2. scripts/faddr2line +97 −53
  3. scripts/link-vmlinux.sh +39 −23
  4. scripts/objdump-func +29 −0
  5. scripts/package/builddeb +1 −1
scripts/Makefile.build+14−9
@@ -222,25 +222,29 @@ cmd_record_mcount = $(if $(findstring $(strip $(CC_FLAGS_FTRACE)),$(_c_flags)), · 221 unmodified lines
222 $(sub_cmd_record_mcount))222 $(sub_cmd_record_mcount))
223endif # CONFIG_FTRACE_MCOUNT_USE_RECORDMCOUNT223endif # CONFIG_FTRACE_MCOUNT_USE_RECORDMCOUNT
224 224
225ifdef CONFIG_STACK_VALIDATION225ifdef CONFIG_OBJTOOL
226 226
227objtool := $(objtree)/tools/objtool/objtool227objtool := $(objtree)/tools/objtool/objtool
228 228
229objtool_args = \229objtool_args = \
230 $(if $(CONFIG_UNWINDER_ORC),orc generate,check) \230 $(if $(CONFIG_HAVE_JUMP_LABEL_HACK), --hacks=jump_label) \
231 $(if $(part-of-module), --module) \231 $(if $(CONFIG_HAVE_NOINSTR_HACK), --hacks=noinstr) \
232 $(if $(CONFIG_X86_KERNEL_IBT), --lto --ibt) \232 $(if $(CONFIG_X86_KERNEL_IBT), --ibt) \
233 $(if $(CONFIG_FRAME_POINTER),, --no-fp) \233 $(if $(CONFIG_FTRACE_MCOUNT_USE_OBJTOOL), --mcount) \
234 $(if $(CONFIG_GCOV_KERNEL), --no-unreachable) \234 $(if $(CONFIG_UNWINDER_ORC), --orc) \
235 $(if $(CONFIG_RETPOLINE), --retpoline) \235 $(if $(CONFIG_RETPOLINE), --retpoline) \
236 $(if $(CONFIG_SLS), --sls) \
237 $(if $(CONFIG_STACK_VALIDATION), --stackval) \
238 $(if $(CONFIG_HAVE_STATIC_CALL_INLINE), --static-call) \
236 --uaccess \239 --uaccess \
237 $(if $(CONFIG_FTRACE_MCOUNT_USE_OBJTOOL), --mcount) \240 $(if $(linked-object), --link) \
238 $(if $(CONFIG_SLS), --sls)241 $(if $(part-of-module), --module) \
242 $(if $(CONFIG_GCOV_KERNEL), --no-unreachable)
239 243
240cmd_objtool = $(if $(objtool-enabled), ; $(objtool) $(objtool_args) $@)244cmd_objtool = $(if $(objtool-enabled), ; $(objtool) $(objtool_args) $@)
241cmd_gen_objtooldep = $(if $(objtool-enabled), { echo ; echo '$@: $$(wildcard $(objtool))' ; } >> $(dot-target).cmd)245cmd_gen_objtooldep = $(if $(objtool-enabled), { echo ; echo '$@: $$(wildcard $(objtool))' ; } >> $(dot-target).cmd)
242 246
243endif # CONFIG_STACK_VALIDATION247endif # CONFIG_OBJTOOL
244 248
245ifneq ($(CONFIG_LTO_CLANG)$(CONFIG_X86_KERNEL_IBT),)249ifneq ($(CONFIG_LTO_CLANG)$(CONFIG_X86_KERNEL_IBT),)
246 250
@@ -303,6 +307,7 @@ quiet_cmd_cc_prelink_modules = LD [M] $@ · 56 unmodified lines
303# modules into native code307# modules into native code
304$(obj)/%.prelink.o: objtool-enabled = y308$(obj)/%.prelink.o: objtool-enabled = y
305$(obj)/%.prelink.o: part-of-module := y309$(obj)/%.prelink.o: part-of-module := y
310$(obj)/%.prelink.o: linked-object := y
306 311
307$(obj)/%.prelink.o: $(obj)/%.o FORCE312$(obj)/%.prelink.o: $(obj)/%.o FORCE
308 $(call if_changed,cc_prelink_modules)313 $(call if_changed,cc_prelink_modules)
scripts/faddr2line+97−53
@@ -44,17 +44,6 @@ · 43 unmodified lines
44set -o errexit44set -o errexit
45set -o nounset45set -o nounset
46 46
47READELF="${CROSS_COMPILE:-}readelf"
48ADDR2LINE="${CROSS_COMPILE:-}addr2line"
49SIZE="${CROSS_COMPILE:-}size"
50NM="${CROSS_COMPILE:-}nm"
51
52command -v awk >/dev/null 2>&1 || die "awk isn't installed"
53command -v ${READELF} >/dev/null 2>&1 || die "readelf isn't installed"
54command -v ${ADDR2LINE} >/dev/null 2>&1 || die "addr2line isn't installed"
55command -v ${SIZE} >/dev/null 2>&1 || die "size isn't installed"
56command -v ${NM} >/dev/null 2>&1 || die "nm isn't installed"
57
58usage() {47usage() {
59 echo "usage: faddr2line [--list] <object file> <func+offset> <func+offset>..." >&248 echo "usage: faddr2line [--list] <object file> <func+offset> <func+offset>..." >&2
60 exit 149 exit 1
@@ -69,6 +58,14 @@ die() { · 8 unmodified lines
69 exit 158 exit 1
70}59}
71 60
61READELF="${CROSS_COMPILE:-}readelf"
62ADDR2LINE="${CROSS_COMPILE:-}addr2line"
63AWK="awk"
64
65command -v ${AWK} >/dev/null 2>&1 || die "${AWK} isn't installed"
66command -v ${READELF} >/dev/null 2>&1 || die "${READELF} isn't installed"
67command -v ${ADDR2LINE} >/dev/null 2>&1 || die "${ADDR2LINE} isn't installed"
68
72# Try to figure out the source directory prefix so we can remove it from the69# Try to figure out the source directory prefix so we can remove it from the
73# addr2line output. HACK ALERT: This assumes that start_kernel() is in70# addr2line output. HACK ALERT: This assumes that start_kernel() is in
74# init/main.c! This only works for vmlinux. Otherwise it falls back to71# init/main.c! This only works for vmlinux. Otherwise it falls back to
@@ -76,7 +73,7 @@ die() { · 1 unmodified line
76find_dir_prefix() {73find_dir_prefix() {
77 local objfile=$174 local objfile=$1
78 75
79 local start_kernel_addr=$(${READELF} -sW $objfile | awk '$8 == "start_kernel" {printf "0x%s", $2}')76 local start_kernel_addr=$(${READELF} --symbols --wide $objfile | ${AWK} '$8 == "start_kernel" {printf "0x%s", $2}')
80 [[ -z $start_kernel_addr ]] && return77 [[ -z $start_kernel_addr ]] && return
81 78
82 local file_line=$(${ADDR2LINE} -e $objfile $start_kernel_addr)79 local file_line=$(${ADDR2LINE} -e $objfile $start_kernel_addr)
@@ -97,86 +94,133 @@ __faddr2line() { · 14 unmodified lines
97 local dir_prefix=$394 local dir_prefix=$3
98 local print_warnings=$495 local print_warnings=$4
99 96
100 local func=${func_addr%+*}97 local sym_name=${func_addr%+*}
101 local offset=${func_addr#*+}98 local offset=${func_addr#*+}
102 offset=${offset%/*}99 offset=${offset%/*}
103 local size=100 local user_size=
104 [[ $func_addr =~ "/" ]] && size=${func_addr#*/}101 [[ $func_addr =~ "/" ]] && user_size=${func_addr#*/}
105 102
106 if [[ -z $func ]] || [[ -z $offset ]] || [[ $func = $func_addr ]]; then103 if [[ -z $sym_name ]] || [[ -z $offset ]] || [[ $sym_name = $func_addr ]]; then
107 warn "bad func+offset $func_addr"104 warn "bad func+offset $func_addr"
108 DONE=1105 DONE=1
109 return106 return
110 fi107 fi
111 108
112 # Go through each of the object's symbols which match the func name.109 # Go through each of the object's symbols which match the func name.
113 # In rare cases there might be duplicates.110 # In rare cases there might be duplicates, in which case we print all
114 file_end=$(${SIZE} -Ax $objfile | awk '$1 == ".text" {print $2}')111 # matches.
115 while read symbol; do112 while read line; do
116 local fields=($symbol)113 local fields=($line)
117 local sym_base=0x${fields[0]}114 local sym_addr=0x${fields[1]}
118 local sym_type=${fields[1]}115 local sym_elf_size=${fields[2]}
119 local sym_end=${fields[3]}116 local sym_sec=${fields[6]}
120 117
121 # calculate the size118 # Get the section size:
122 local sym_size=$(($sym_end - $sym_base))119 local sec_size=$(${READELF} --section-headers --wide $objfile |
120 sed 's/\[ /\[/' |
121 ${AWK} -v sec=$sym_sec '$1 == "[" sec "]" { print "0x" $6; exit }')
122
123 if [[ -z $sec_size ]]; then
124 warn "bad section size: section: $sym_sec"
125 DONE=1
126 return
127 fi
128
129 # Calculate the symbol size.
130 #
131 # Unfortunately we can't use the ELF size, because kallsyms
132 # also includes the padding bytes in its size calculation. For
133 # kallsyms, the size calculation is the distance between the
134 # symbol and the next symbol in a sorted list.
135 local sym_size
136 local cur_sym_addr
137 local found=0
138 while read line; do
139 local fields=($line)
140 cur_sym_addr=0x${fields[1]}
141 local cur_sym_elf_size=${fields[2]}
142 local cur_sym_name=${fields[7]:-}
143
144 if [[ $cur_sym_addr = $sym_addr ]] &&
145 [[ $cur_sym_elf_size = $sym_elf_size ]] &&
146 [[ $cur_sym_name = $sym_name ]]; then
147 found=1
148 continue
149 fi
150
151 if [[ $found = 1 ]]; then
152 sym_size=$(($cur_sym_addr - $sym_addr))
153 [[ $sym_size -lt $sym_elf_size ]] && continue;
154 found=2
155 break
156 fi
157 done < <(${READELF} --symbols --wide $objfile | ${AWK} -v sec=$sym_sec '$7 == sec' | sort --key=2)
158
159 if [[ $found = 0 ]]; then
160 warn "can't find symbol: sym_name: $sym_name sym_sec: $sym_sec sym_addr: $sym_addr sym_elf_size: $sym_elf_size"
161 DONE=1
162 return
163 fi
164
165 # If nothing was found after the symbol, assume it's the last
166 # symbol in the section.
167 [[ $found = 1 ]] && sym_size=$(($sec_size - $sym_addr))
168
123 if [[ -z $sym_size ]] || [[ $sym_size -le 0 ]]; then169 if [[ -z $sym_size ]] || [[ $sym_size -le 0 ]]; then
124 warn "bad symbol size: base: $sym_base end: $sym_end"170 warn "bad symbol size: sym_addr: $sym_addr cur_sym_addr: $cur_sym_addr"
125 DONE=1171 DONE=1
126 return172 return
127 fi173 fi
174
128 sym_size=0x$(printf %x $sym_size)175 sym_size=0x$(printf %x $sym_size)
129 176
130 # calculate the address177 # Calculate the section address from user-supplied offset:
131 local addr=$(($sym_base + $offset))178 local addr=$(($sym_addr + $offset))
132 if [[ -z $addr ]] || [[ $addr = 0 ]]; then179 if [[ -z $addr ]] || [[ $addr = 0 ]]; then
133 warn "bad address: $sym_base + $offset"180 warn "bad address: $sym_addr + $offset"
134 DONE=1181 DONE=1
135 return182 return
136 fi183 fi
137 addr=0x$(printf %x $addr)184 addr=0x$(printf %x $addr)
138 185
139 # weed out non-function symbols186 # If the user provided a size, make sure it matches the symbol's size:
140 if [[ $sym_type != t ]] && [[ $sym_type != T ]]; then187 if [[ -n $user_size ]] && [[ $user_size -ne $sym_size ]]; then
141 [[ $print_warnings = 1 ]] &&
142 echo "skipping $func address at $addr due to non-function symbol of type '$sym_type'"
143 continue
144 fi
145
146 # if the user provided a size, make sure it matches the symbol's size
147 if [[ -n $size ]] && [[ $size -ne $sym_size ]]; then
148 [[ $print_warnings = 1 ]] &&188 [[ $print_warnings = 1 ]] &&
149 echo "skipping $func address at $addr due to size mismatch ($size != $sym_size)"189 echo "skipping $sym_name address at $addr due to size mismatch ($user_size != $sym_size)"
150 continue;190 continue;
151 fi191 fi
152 192
153 # make sure the provided offset is within the symbol's range193 # Make sure the provided offset is within the symbol's range:
154 if [[ $offset -gt $sym_size ]]; then194 if [[ $offset -gt $sym_size ]]; then
155 [[ $print_warnings = 1 ]] &&195 [[ $print_warnings = 1 ]] &&
156 echo "skipping $func address at $addr due to size mismatch ($offset > $sym_size)"196 echo "skipping $sym_name address at $addr due to size mismatch ($offset > $sym_size)"
157 continue197 continue
158 fi198 fi
159 199
160 # separate multiple entries with a blank line200 # In case of duplicates or multiple addresses specified on the
201 # cmdline, separate multiple entries with a blank line:
161 [[ $FIRST = 0 ]] && echo202 [[ $FIRST = 0 ]] && echo
162 FIRST=0203 FIRST=0
163 204
164 # pass real address to addr2line205 echo "$sym_name+$offset/$sym_size:"
165 echo "$func+$offset/$sym_size:"
166 local file_lines=$(${ADDR2LINE} -fpie $objfile $addr | sed "s; $dir_prefix\(\./\)*; ;")
167 [[ -z $file_lines ]] && return
168 206
207 # Pass section address to addr2line and strip absolute paths
208 # from the output:
209 local output=$(${ADDR2LINE} -fpie $objfile $addr | sed "s; $dir_prefix\(\./\)*; ;")
210 [[ -z $output ]] && continue
211
212 # Default output (non --list):
169 if [[ $LIST = 0 ]]; then213 if [[ $LIST = 0 ]]; then
170 echo "$file_lines" | while read -r line214 echo "$output" | while read -r line
171 do215 do
172 echo $line216 echo $line
173 done217 done
174 DONE=1;218 DONE=1;
175 return219 continue
176 fi220 fi
177 221
178 # show each line with context222 # For --list, show each line with its corresponding source code:
179 echo "$file_lines" | while read -r line223 echo "$output" | while read -r line
180 do224 do
181 echo225 echo
182 echo $line226 echo $line
@@ -184,12 +228,12 @@ __faddr2line() { · 1 unmodified line
184 n1=$[$n-5]228 n1=$[$n-5]
185 n2=$[$n+5]229 n2=$[$n+5]
186 f=$(echo $line | sed 's/.*at \(.\+\):.*/\1/g')230 f=$(echo $line | sed 's/.*at \(.\+\):.*/\1/g')
187 awk 'NR>=strtonum("'$n1'") && NR<=strtonum("'$n2'") { if (NR=='$n') printf(">%d<", NR); else printf(" %d ", NR); printf("\t%s\n", $0)}' $f231 ${AWK} 'NR>=strtonum("'$n1'") && NR<=strtonum("'$n2'") { if (NR=='$n') printf(">%d<", NR); else printf(" %d ", NR); printf("\t%s\n", $0)}' $f
188 done232 done
189 233
190 DONE=1234 DONE=1
191 235
192 done < <(${NM} -n $objfile | awk -v fn=$func -v end=$file_end '$3 == fn { found=1; line=$0; start=$1; next } found == 1 { found=0; print line, "0x"$1 } END {if (found == 1) print line, end; }')236 done < <(${READELF} --symbols --wide $objfile | ${AWK} -v fn=$sym_name '$4 == "FUNC" && $8 == fn')
193}237}
194 238
195[[ $# -lt 2 ]] && usage239[[ $# -lt 2 ]] && usage
scripts/link-vmlinux.sh+39−23
@@ -108,16 +108,22 @@ objtool_link() · 107 unmodified lines
108 local objtoolcmd;108 local objtoolcmd;
109 local objtoolopt;109 local objtoolopt;
110 110
111 if is_enabled CONFIG_STACK_VALIDATION && \111 if ! is_enabled CONFIG_OBJTOOL; then
112 ( is_enabled CONFIG_LTO_CLANG || is_enabled CONFIG_X86_KERNEL_IBT ); then112 return;
113 fi
113 114
114 # Don't perform vmlinux validation unless explicitly requested,115 if is_enabled CONFIG_LTO_CLANG || is_enabled CONFIG_X86_KERNEL_IBT; then
115 # but run objtool on vmlinux.o now that we have an object file.116
116 if is_enabled CONFIG_UNWINDER_ORC; then117 # For LTO and IBT, objtool doesn't run on individual
117 objtoolcmd="orc generate"118 # translation units. Run everything on vmlinux instead.
119
120 if is_enabled CONFIG_HAVE_JUMP_LABEL_HACK; then
121 objtoolopt="${objtoolopt} --hacks=jump_label"
118 fi122 fi
119 123
120 objtoolopt="${objtoolopt} --lto"124 if is_enabled CONFIG_HAVE_NOINSTR_HACK; then
125 objtoolopt="${objtoolopt} --hacks=noinstr"
126 fi
121 127
122 if is_enabled CONFIG_X86_KERNEL_IBT; then128 if is_enabled CONFIG_X86_KERNEL_IBT; then
123 objtoolopt="${objtoolopt} --ibt"129 objtoolopt="${objtoolopt} --ibt"
@@ -126,34 +132,44 @@ objtool_link() · 2 unmodified lines
126 if is_enabled CONFIG_FTRACE_MCOUNT_USE_OBJTOOL; then132 if is_enabled CONFIG_FTRACE_MCOUNT_USE_OBJTOOL; then
127 objtoolopt="${objtoolopt} --mcount"133 objtoolopt="${objtoolopt} --mcount"
128 fi134 fi
135
136 if is_enabled CONFIG_UNWINDER_ORC; then
137 objtoolopt="${objtoolopt} --orc"
138 fi
139
140 if is_enabled CONFIG_RETPOLINE; then
141 objtoolopt="${objtoolopt} --retpoline"
142 fi
143
144 if is_enabled CONFIG_SLS; then
145 objtoolopt="${objtoolopt} --sls"
146 fi
147
148 if is_enabled CONFIG_STACK_VALIDATION; then
149 objtoolopt="${objtoolopt} --stackval"
150 fi
151
152 if is_enabled CONFIG_HAVE_STATIC_CALL_INLINE; then
153 objtoolopt="${objtoolopt} --static-call"
154 fi
155
156 objtoolopt="${objtoolopt} --uaccess"
129 fi157 fi
130 158
131 if is_enabled CONFIG_VMLINUX_VALIDATION; then159 if is_enabled CONFIG_NOINSTR_VALIDATION; then
132 objtoolopt="${objtoolopt} --noinstr"160 objtoolopt="${objtoolopt} --noinstr"
133 fi161 fi
134 162
135 if [ -n "${objtoolopt}" ]; then163 if [ -n "${objtoolopt}" ]; then
136 if [ -z "${objtoolcmd}" ]; then164
137 objtoolcmd="check"
138 fi
139 objtoolopt="${objtoolopt} --vmlinux"
140 if ! is_enabled CONFIG_FRAME_POINTER; then
141 objtoolopt="${objtoolopt} --no-fp"
142 fi
143 if is_enabled CONFIG_GCOV_KERNEL; then165 if is_enabled CONFIG_GCOV_KERNEL; then
144 objtoolopt="${objtoolopt} --no-unreachable"166 objtoolopt="${objtoolopt} --no-unreachable"
145 fi167 fi
146 if is_enabled CONFIG_RETPOLINE; then
147 objtoolopt="${objtoolopt} --retpoline"
148 fi
149 168
150 objtoolopt="${objtoolopt} --uaccess"169 objtoolopt="${objtoolopt} --link"
151 170
152 if is_enabled CONFIG_SLS; then
153 objtoolopt="${objtoolopt} --sls"
154 fi
155 info OBJTOOL ${1}171 info OBJTOOL ${1}
156 tools/objtool/objtool ${objtoolcmd} ${objtoolopt} ${1}172 tools/objtool/objtool ${objtoolopt} ${1}
157 fi173 fi
158}174}
159 175
scripts/objdump-func+29−0
@@ -0,0 +1,29 @@
1#!/bin/bash
2# SPDX-License-Identifier: GPL-2.0
3#
4# Disassemble a single function.
5#
6# usage: objdump-func <file> <func>
7
8set -o errexit
9set -o nounset
10
11OBJDUMP="${CROSS_COMPILE:-}objdump"
12
13command -v gawk >/dev/null 2>&1 || die "gawk isn't installed"
14
15usage() {
16 echo "usage: objdump-func <file> <func>" >&2
17 exit 1
18}
19
20[[ $# -lt 2 ]] && usage
21
22OBJ=$1; shift
23FUNC=$1; shift
24
25# Secret feature to allow adding extra objdump args at the end
26EXTRA_ARGS=$@
27
28# Note this also matches compiler-added suffixes like ".cold", etc
29${OBJDUMP} -wdr $EXTRA_ARGS $OBJ | gawk -M -v f=$FUNC '/^$/ { P=0; } $0 ~ "<" f "(\\..*)?>:" { P=1; O=strtonum("0x" $1); } { if (P) { o=strtonum("0x" $1); printf("%04x ", o-O); print $0; } }'
scripts/package/builddeb+1−1
@@ -67,7 +67,7 @@ deploy_kernel_headers () { · 66 unmodified lines
67 ) > debian/hdrsrcfiles67 ) > debian/hdrsrcfiles
68 68
69 {69 {
70 if is_enabled CONFIG_STACK_VALIDATION; then70 if is_enabled CONFIG_OBJTOOL; then
71 echo tools/objtool/objtool71 echo tools/objtool/objtool
72 fi72 fi
73 73