44
44
set -o errexit
45
45
set -o nounset
46
46
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
-
58
47
usage () {
59
48
echo " usage: faddr2line [--list] <object file> <func+offset> <func+offset>..." >&2
60
49
exit 1
@@ -69,14 +58,22 @@ die() {
69
58
exit 1
70
59
}
71
60
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
+
72
69
# Try to figure out the source directory prefix so we can remove it from the
73
70
# addr2line output. HACK ALERT: This assumes that start_kernel() is in
74
71
# init/main.c! This only works for vmlinux. Otherwise it falls back to
75
72
# printing the absolute path.
76
73
find_dir_prefix () {
77
74
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
77
[[ -z $start_kernel_addr ]] && return
81
78
82
79
local file_line=$( ${ADDR2LINE} -e $objfile $start_kernel_addr )
@@ -97,99 +94,146 @@ __faddr2line() {
97
94
local dir_prefix=$3
98
95
local print_warnings=$4
99
96
100
- local func =${func_addr% +* }
97
+ local sym_name =${func_addr% +* }
101
98
local offset=${func_addr#* +}
102
99
offset=${offset%/* }
103
- local size =
104
- [[ $func_addr =~ " /" ]] && size =${func_addr#*/ }
100
+ local user_size =
101
+ [[ $func_addr =~ " /" ]] && user_size =${func_addr#*/ }
105
102
106
- if [[ -z $func ]] || [[ -z $offset ]] || [[ $func = $func_addr ]]; then
103
+ if [[ -z $sym_name ]] || [[ -z $offset ]] || [[ $sym_name = $func_addr ]]; then
107
104
warn " bad func+offset $func_addr "
108
105
DONE=1
109
106
return
110
107
fi
111
108
112
109
# 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]}
120
-
121
- # calculate the size
122
- local sym_size=$(( $sym_end - $sym_base ))
110
+ # In rare cases there might be duplicates, in which case we print all
111
+ # matches.
112
+ while read line; do
113
+ local fields=($line )
114
+ local sym_addr=0x${fields[1]}
115
+ local sym_elf_size=${fields[2]}
116
+ local sym_sec=${fields[6]}
117
+
118
+ # Get the section size:
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
169
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
171
DONE=1
126
172
return
127
173
fi
174
+
128
175
sym_size=0x$( printf %x $sym_size )
129
176
130
- # calculate the address
131
- local addr=$(( $sym_base + $offset ))
177
+ # Calculate the section address from user-supplied offset:
178
+ local addr=$(( $sym_addr + $offset ))
132
179
if [[ -z $addr ]] || [[ $addr = 0 ]]; then
133
- warn " bad address: $sym_base + $offset "
180
+ warn " bad address: $sym_addr + $offset "
134
181
DONE=1
135
182
return
136
183
fi
137
184
addr=0x$( printf %x $addr )
138
185
139
- # weed out non-function symbols
140
- if [[ $sym_type != t ]] && [[ $sym_type != T ]]; 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
186
+ # If the user provided a size, make sure it matches the symbol's size:
187
+ if [[ -n $user_size ]] && [[ $user_size -ne $sym_size ]]; then
148
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
190
continue ;
151
191
fi
152
192
153
- # make sure the provided offset is within the symbol's range
193
+ # Make sure the provided offset is within the symbol's range:
154
194
if [[ $offset -gt $sym_size ]]; then
155
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
197
continue
158
198
fi
159
199
160
- # separate multiple entries with a blank line
200
+ # In case of duplicates or multiple addresses specified on the
201
+ # cmdline, separate multiple entries with a blank line:
161
202
[[ $FIRST = 0 ]] && echo
162
203
FIRST=0
163
204
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
205
+ echo " $sym_name +$offset /$sym_size :"
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
213
if [[ $LIST = 0 ]]; then
170
- echo " $file_lines " | while read -r line
214
+ echo " $output " | while read -r line
171
215
do
172
216
echo $line
173
217
done
174
218
DONE=1;
175
- return
219
+ continue
176
220
fi
177
221
178
- # show each line with context
179
- echo " $file_lines " | while read -r line
222
+ # For --list, show each line with its corresponding source code:
223
+ echo " $output " | while read -r line
180
224
do
181
225
echo
182
226
echo $line
183
227
n=$( echo $line | sed ' s/.*:\([0-9]\+\).*/\1/g' )
184
228
n1=$[$n -5]
185
229
n2=$[$n +5]
186
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)}' $f
231
+ ${AWK} ' NR>=strtonum("' $n1 ' ") && NR<=strtonum("' $n2 ' ") { if (NR==' $n ' ) printf(">%d<", NR); else printf(" %d ", NR); printf("\t%s\n", $0)}' $f
188
232
done
189
233
190
234
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
239
[[ $# -lt 2 ]] && usage
0 commit comments