hc
2024-10-16 50a212ec906f7524620675f0c57357691c26c81f
kernel/scripts/faddr2line
....@@ -44,17 +44,6 @@
4444 set -o errexit
4545 set -o nounset
4646
47
-READELF="${CROSS_COMPILE:-}readelf"
48
-ADDR2LINE="${CROSS_COMPILE:-}addr2line"
49
-SIZE="${CROSS_COMPILE:-}size"
50
-NM="${CROSS_COMPILE:-}nm"
51
-
52
-command -v awk >/dev/null 2>&1 || die "awk isn't installed"
53
-command -v ${READELF} >/dev/null 2>&1 || die "readelf isn't installed"
54
-command -v ${ADDR2LINE} >/dev/null 2>&1 || die "addr2line isn't installed"
55
-command -v ${SIZE} >/dev/null 2>&1 || die "size isn't installed"
56
-command -v ${NM} >/dev/null 2>&1 || die "nm isn't installed"
57
-
5847 usage() {
5948 echo "usage: faddr2line [--list] <object file> <func+offset> <func+offset>..." >&2
6049 exit 1
....@@ -69,14 +58,23 @@
6958 exit 1
7059 }
7160
61
+READELF="${CROSS_COMPILE:-}readelf"
62
+ADDR2LINE="${CROSS_COMPILE:-}addr2line"
63
+AWK="awk"
64
+
65
+command -v ${AWK} >/dev/null 2>&1 || die "${AWK} isn't installed"
66
+command -v ${READELF} >/dev/null 2>&1 || die "${READELF} isn't installed"
67
+command -v ${ADDR2LINE} >/dev/null 2>&1 || die "${ADDR2LINE} isn't installed"
68
+
7269 # Try to figure out the source directory prefix so we can remove it from the
7370 # addr2line output. HACK ALERT: This assumes that start_kernel() is in
74
-# kernel/init.c! This only works for vmlinux. Otherwise it falls back to
71
+# init/main.c! This only works for vmlinux. Otherwise it falls back to
7572 # printing the absolute path.
7673 find_dir_prefix() {
7774 local objfile=$1
7875
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 | sed 's/\[.*\]//' |
77
+ ${AWK} '$8 == "start_kernel" {printf "0x%s", $2}')
8078 [[ -z $start_kernel_addr ]] && return
8179
8280 local file_line=$(${ADDR2LINE} -e $objfile $start_kernel_addr)
....@@ -97,86 +95,158 @@
9795 local dir_prefix=$3
9896 local print_warnings=$4
9997
100
- local func=${func_addr%+*}
101
- local offset=${func_addr#*+}
102
- offset=${offset%/*}
103
- local size=
104
- [[ $func_addr =~ "/" ]] && size=${func_addr#*/}
98
+ local sym_name=${func_addr%+*}
99
+ local func_offset=${func_addr#*+}
100
+ func_offset=${func_offset%/*}
101
+ local user_size=
102
+ local file_type
103
+ local is_vmlinux=0
104
+ [[ $func_addr =~ "/" ]] && user_size=${func_addr#*/}
105105
106
- if [[ -z $func ]] || [[ -z $offset ]] || [[ $func = $func_addr ]]; then
106
+ if [[ -z $sym_name ]] || [[ -z $func_offset ]] || [[ $sym_name = $func_addr ]]; then
107107 warn "bad func+offset $func_addr"
108108 DONE=1
109109 return
110110 fi
111111
112
- # Go through each of the object's symbols which match the func name.
113
- # In rare cases there might be duplicates.
114
- file_end=$(${SIZE} -Ax $objfile | awk '$1 == ".text" {print $2}')
115
- while read symbol; do
116
- local fields=($symbol)
117
- local sym_base=0x${fields[0]}
118
- local sym_type=${fields[1]}
119
- local sym_end=${fields[3]}
112
+ # vmlinux uses absolute addresses in the section table rather than
113
+ # section offsets.
114
+ local file_type=$(${READELF} --file-header $objfile |
115
+ ${AWK} '$1 == "Type:" { print $2; exit }')
116
+ if [[ $file_type = "EXEC" ]] || [[ $file_type == "DYN" ]]; then
117
+ is_vmlinux=1
118
+ fi
120119
121
- # calculate the size
122
- local sym_size=$(($sym_end - $sym_base))
123
- if [[ -z $sym_size ]] || [[ $sym_size -le 0 ]]; then
124
- warn "bad symbol size: base: $sym_base end: $sym_end"
120
+ # Go through each of the object's symbols which match the func name.
121
+ # In rare cases there might be duplicates, in which case we print all
122
+ # matches.
123
+ while read line; do
124
+ local fields=($line)
125
+ local sym_addr=0x${fields[1]}
126
+ local sym_elf_size=${fields[2]}
127
+ local sym_sec=${fields[6]}
128
+ local sec_size
129
+ local sec_name
130
+
131
+ # Get the section size:
132
+ sec_size=$(${READELF} --section-headers --wide $objfile |
133
+ sed 's/\[ /\[/' |
134
+ ${AWK} -v sec=$sym_sec '$1 == "[" sec "]" { print "0x" $6; exit }')
135
+
136
+ if [[ -z $sec_size ]]; then
137
+ warn "bad section size: section: $sym_sec"
125138 DONE=1
126139 return
127140 fi
141
+
142
+ # Get the section name:
143
+ sec_name=$(${READELF} --section-headers --wide $objfile |
144
+ sed 's/\[ /\[/' |
145
+ ${AWK} -v sec=$sym_sec '$1 == "[" sec "]" { print $2; exit }')
146
+
147
+ if [[ -z $sec_name ]]; then
148
+ warn "bad section name: section: $sym_sec"
149
+ DONE=1
150
+ return
151
+ fi
152
+
153
+ # Calculate the symbol size.
154
+ #
155
+ # Unfortunately we can't use the ELF size, because kallsyms
156
+ # also includes the padding bytes in its size calculation. For
157
+ # kallsyms, the size calculation is the distance between the
158
+ # symbol and the next symbol in a sorted list.
159
+ local sym_size
160
+ local cur_sym_addr
161
+ local found=0
162
+ while read line; do
163
+ local fields=($line)
164
+ cur_sym_addr=0x${fields[1]}
165
+ local cur_sym_elf_size=${fields[2]}
166
+ local cur_sym_name=${fields[7]:-}
167
+
168
+ if [[ $cur_sym_addr = $sym_addr ]] &&
169
+ [[ $cur_sym_elf_size = $sym_elf_size ]] &&
170
+ [[ $cur_sym_name = $sym_name ]]; then
171
+ found=1
172
+ continue
173
+ fi
174
+
175
+ if [[ $found = 1 ]]; then
176
+ sym_size=$(($cur_sym_addr - $sym_addr))
177
+ [[ $sym_size -lt $sym_elf_size ]] && continue;
178
+ found=2
179
+ break
180
+ fi
181
+ done < <(${READELF} --symbols --wide $objfile | sed 's/\[.*\]//' | ${AWK} -v sec=$sym_sec '$7 == sec' | sort --key=2)
182
+
183
+ if [[ $found = 0 ]]; then
184
+ warn "can't find symbol: sym_name: $sym_name sym_sec: $sym_sec sym_addr: $sym_addr sym_elf_size: $sym_elf_size"
185
+ DONE=1
186
+ return
187
+ fi
188
+
189
+ # If nothing was found after the symbol, assume it's the last
190
+ # symbol in the section.
191
+ [[ $found = 1 ]] && sym_size=$(($sec_size - $sym_addr))
192
+
193
+ if [[ -z $sym_size ]] || [[ $sym_size -le 0 ]]; then
194
+ warn "bad symbol size: sym_addr: $sym_addr cur_sym_addr: $cur_sym_addr"
195
+ DONE=1
196
+ return
197
+ fi
198
+
128199 sym_size=0x$(printf %x $sym_size)
129200
130
- # calculate the address
131
- local addr=$(($sym_base + $offset))
201
+ # Calculate the address from user-supplied offset:
202
+ local addr=$(($sym_addr + $func_offset))
132203 if [[ -z $addr ]] || [[ $addr = 0 ]]; then
133
- warn "bad address: $sym_base + $offset"
204
+ warn "bad address: $sym_addr + $func_offset"
134205 DONE=1
135206 return
136207 fi
137208 addr=0x$(printf %x $addr)
138209
139
- # weed out non-function symbols
140
- if [[ $sym_type != t ]] && [[ $sym_type != T ]]; then
210
+ # If the user provided a size, make sure it matches the symbol's size:
211
+ if [[ -n $user_size ]] && [[ $user_size -ne $sym_size ]]; then
141212 [[ $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 ]] &&
149
- echo "skipping $func address at $addr due to size mismatch ($size != $sym_size)"
213
+ echo "skipping $sym_name address at $addr due to size mismatch ($user_size != $sym_size)"
150214 continue;
151215 fi
152216
153
- # make sure the provided offset is within the symbol's range
154
- if [[ $offset -gt $sym_size ]]; then
217
+ # Make sure the provided offset is within the symbol's range:
218
+ if [[ $func_offset -gt $sym_size ]]; then
155219 [[ $print_warnings = 1 ]] &&
156
- echo "skipping $func address at $addr due to size mismatch ($offset > $sym_size)"
220
+ echo "skipping $sym_name address at $addr due to size mismatch ($func_offset > $sym_size)"
157221 continue
158222 fi
159223
160
- # separate multiple entries with a blank line
224
+ # In case of duplicates or multiple addresses specified on the
225
+ # cmdline, separate multiple entries with a blank line:
161226 [[ $FIRST = 0 ]] && echo
162227 FIRST=0
163228
164
- # pass real address to addr2line
165
- echo "$func+$offset/$sym_size:"
166
- local file_lines=$(${ADDR2LINE} -fpie $objfile $addr | sed "s; $dir_prefix\(\./\)*; ;")
167
- [[ -z $file_lines ]] && return
229
+ echo "$sym_name+$func_offset/$sym_size:"
168230
231
+ # Pass section address to addr2line and strip absolute paths
232
+ # from the output:
233
+ local args="--functions --pretty-print --inlines --exe=$objfile"
234
+ [[ $is_vmlinux = 0 ]] && args="$args --section=$sec_name"
235
+ local output=$(${ADDR2LINE} $args $addr | sed "s; $dir_prefix\(\./\)*; ;")
236
+ [[ -z $output ]] && continue
237
+
238
+ # Default output (non --list):
169239 if [[ $LIST = 0 ]]; then
170
- echo "$file_lines" | while read -r line
240
+ echo "$output" | while read -r line
171241 do
172242 echo $line
173243 done
174244 DONE=1;
175
- return
245
+ continue
176246 fi
177247
178
- # show each line with context
179
- echo "$file_lines" | while read -r line
248
+ # For --list, show each line with its corresponding source code:
249
+ echo "$output" | while read -r line
180250 do
181251 echo
182252 echo $line
....@@ -184,12 +254,12 @@
184254 n1=$[$n-5]
185255 n2=$[$n+5]
186256 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)}' $f
257
+ ${AWK} 'NR>=strtonum("'$n1'") && NR<=strtonum("'$n2'") { if (NR=='$n') printf(">%d<", NR); else printf(" %d ", NR); printf("\t%s\n", $0)}' $f
188258 done
189259
190260 DONE=1
191261
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; }')
262
+ done < <(${READELF} --symbols --wide $objfile | sed 's/\[.*\]//' | ${AWK} -v fn=$sym_name '$4 == "FUNC" && $8 == fn')
193263 }
194264
195265 [[ $# -lt 2 ]] && usage