4444set -o errexit
4545set -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-
5847usage () {
5948 echo " usage: faddr2line [--list] <object file> <func+offset> <func+offset>..." >&2
6049 exit 1
@@ -69,14 +58,22 @@ die() {
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
7471# init/main.c! This only works for vmlinux. Otherwise it falls back to
7572# printing the absolute path.
7673find_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 | ${AWK} ' $8 == "start_kernel" {printf "0x%s", $2}' )
8077 [[ -z $start_kernel_addr ]] && return
8178
8279 local file_line=$( ${ADDR2LINE} -e $objfile $start_kernel_addr )
@@ -97,99 +94,146 @@ __faddr2line() {
9794 local dir_prefix=$3
9895 local print_warnings=$4
9996
100- local func =${func_addr% +* }
97+ local sym_name =${func_addr% +* }
10198 local offset=${func_addr#* +}
10299 offset=${offset%/* }
103- local size =
104- [[ $func_addr =~ " /" ]] && size =${func_addr#*/ }
100+ local user_size =
101+ [[ $func_addr =~ " /" ]] && user_size =${func_addr#*/ }
105102
106- if [[ -z $func ]] || [[ -z $offset ]] || [[ $func = $func_addr ]]; then
103+ if [[ -z $sym_name ]] || [[ -z $offset ]] || [[ $sym_name = $func_addr ]]; then
107104 warn " bad func+offset $func_addr "
108105 DONE=1
109106 return
110107 fi
111108
112109 # 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+
123169 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 "
125171 DONE=1
126172 return
127173 fi
174+
128175 sym_size=0x$( printf %x $sym_size )
129176
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 ))
132179 if [[ -z $addr ]] || [[ $addr = 0 ]]; then
133- warn " bad address: $sym_base + $offset "
180+ warn " bad address: $sym_addr + $offset "
134181 DONE=1
135182 return
136183 fi
137184 addr=0x$( printf %x $addr )
138185
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
148188 [[ $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 )"
150190 continue ;
151191 fi
152192
153- # make sure the provided offset is within the symbol's range
193+ # Make sure the provided offset is within the symbol's range:
154194 if [[ $offset -gt $sym_size ]]; then
155195 [[ $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 )"
157197 continue
158198 fi
159199
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:
161202 [[ $FIRST = 0 ]] && echo
162203 FIRST=0
163204
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 :"
168206
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):
169213 if [[ $LIST = 0 ]]; then
170- echo " $file_lines " | while read -r line
214+ echo " $output " | while read -r line
171215 do
172216 echo $line
173217 done
174218 DONE=1;
175- return
219+ continue
176220 fi
177221
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
180224 do
181225 echo
182226 echo $line
183227 n=$( echo $line | sed ' s/.*:\([0-9]\+\).*/\1/g' )
184228 n1=$[$n -5]
185229 n2=$[$n +5]
186230 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
188232 done
189233
190234 DONE=1
191235
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 ' )
193237}
194238
195239[[ $# -lt 2 ]] && usage
0 commit comments