0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013
0014
0015
0016
0017
0018
0019
0020
0021
0022
0023
0024
0025
0026
0027
0028
0029
0030
0031
0032
0033
0034
0035
0036
0037
0038
0039
0040
0041
0042
0043
0044 set -o errexit
0045 set -o nounset
0046
0047 usage() {
0048 echo "usage: faddr2line [--list] <object file> <func+offset> <func+offset>..." >&2
0049 exit 1
0050 }
0051
0052 warn() {
0053 echo "$1" >&2
0054 }
0055
0056 die() {
0057 echo "ERROR: $1" >&2
0058 exit 1
0059 }
0060
0061 READELF="${CROSS_COMPILE:-}readelf"
0062 ADDR2LINE="${CROSS_COMPILE:-}addr2line"
0063 AWK="awk"
0064 GREP="grep"
0065
0066 command -v ${AWK} >/dev/null 2>&1 || die "${AWK} isn't installed"
0067 command -v ${READELF} >/dev/null 2>&1 || die "${READELF} isn't installed"
0068 command -v ${ADDR2LINE} >/dev/null 2>&1 || die "${ADDR2LINE} isn't installed"
0069
0070
0071
0072
0073
0074 find_dir_prefix() {
0075 local objfile=$1
0076
0077 local start_kernel_addr=$(${READELF} --symbols --wide $objfile | ${AWK} '$8 == "start_kernel" {printf "0x%s", $2}')
0078 [[ -z $start_kernel_addr ]] && return
0079
0080 local file_line=$(${ADDR2LINE} -e $objfile $start_kernel_addr)
0081 [[ -z $file_line ]] && return
0082
0083 local prefix=${file_line%init/main.c:*}
0084 if [[ -z $prefix ]] || [[ $prefix = $file_line ]]; then
0085 return
0086 fi
0087
0088 DIR_PREFIX=$prefix
0089 return 0
0090 }
0091
0092 __faddr2line() {
0093 local objfile=$1
0094 local func_addr=$2
0095 local dir_prefix=$3
0096 local print_warnings=$4
0097
0098 local sym_name=${func_addr%+*}
0099 local func_offset=${func_addr
0100 func_offset=${func_offset%/*}
0101 local user_size=
0102 local file_type
0103 local is_vmlinux=0
0104 [[ $func_addr =~ "/" ]] && user_size=${func_addr
0105
0106 if [[ -z $sym_name ]] || [[ -z $func_offset ]] || [[ $sym_name = $func_addr ]]; then
0107 warn "bad func+offset $func_addr"
0108 DONE=1
0109 return
0110 fi
0111
0112
0113
0114 local file_type=$(${READELF} --file-header $objfile |
0115 ${AWK} '$1 == "Type:" { print $2; exit }')
0116 if [[ $file_type = "EXEC" ]] || [[ $file_type == "DYN" ]]; then
0117 is_vmlinux=1
0118 fi
0119
0120
0121
0122
0123 while read line; do
0124 local fields=($line)
0125 local sym_addr=0x${fields[1]}
0126 local sym_elf_size=${fields[2]}
0127 local sym_sec=${fields[6]}
0128 local sec_size
0129 local sec_name
0130
0131
0132 sec_size=$(${READELF} --section-headers --wide $objfile |
0133 sed 's/\[ /\[/' |
0134 ${AWK} -v sec=$sym_sec '$1 == "[" sec "]" { print "0x" $6; exit }')
0135
0136 if [[ -z $sec_size ]]; then
0137 warn "bad section size: section: $sym_sec"
0138 DONE=1
0139 return
0140 fi
0141
0142
0143 sec_name=$(${READELF} --section-headers --wide $objfile |
0144 sed 's/\[ /\[/' |
0145 ${AWK} -v sec=$sym_sec '$1 == "[" sec "]" { print $2; exit }')
0146
0147 if [[ -z $sec_name ]]; then
0148 warn "bad section name: section: $sym_sec"
0149 DONE=1
0150 return
0151 fi
0152
0153
0154
0155
0156
0157
0158
0159 local sym_size
0160 local cur_sym_addr
0161 local found=0
0162 while read line; do
0163 local fields=($line)
0164 cur_sym_addr=0x${fields[1]}
0165 local cur_sym_elf_size=${fields[2]}
0166 local cur_sym_name=${fields[7]:-}
0167
0168 if [[ $cur_sym_addr = $sym_addr ]] &&
0169 [[ $cur_sym_elf_size = $sym_elf_size ]] &&
0170 [[ $cur_sym_name = $sym_name ]]; then
0171 found=1
0172 continue
0173 fi
0174
0175 if [[ $found = 1 ]]; then
0176 sym_size=$(($cur_sym_addr - $sym_addr))
0177 [[ $sym_size -lt $sym_elf_size ]] && continue;
0178 found=2
0179 break
0180 fi
0181 done < <(${READELF} --symbols --wide $objfile | ${AWK} -v sec=$sym_sec '$7 == sec' | sort --key=2)
0182
0183 if [[ $found = 0 ]]; then
0184 warn "can't find symbol: sym_name: $sym_name sym_sec: $sym_sec sym_addr: $sym_addr sym_elf_size: $sym_elf_size"
0185 DONE=1
0186 return
0187 fi
0188
0189
0190
0191 [[ $found = 1 ]] && sym_size=$(($sec_size - $sym_addr))
0192
0193 if [[ -z $sym_size ]] || [[ $sym_size -le 0 ]]; then
0194 warn "bad symbol size: sym_addr: $sym_addr cur_sym_addr: $cur_sym_addr"
0195 DONE=1
0196 return
0197 fi
0198
0199 sym_size=0x$(printf %x $sym_size)
0200
0201
0202 local addr=$(($sym_addr + $func_offset))
0203 if [[ -z $addr ]] || [[ $addr = 0 ]]; then
0204 warn "bad address: $sym_addr + $func_offset"
0205 DONE=1
0206 return
0207 fi
0208 addr=0x$(printf %x $addr)
0209
0210
0211 if [[ -n $user_size ]] && [[ $user_size -ne $sym_size ]]; then
0212 [[ $print_warnings = 1 ]] &&
0213 echo "skipping $sym_name address at $addr due to size mismatch ($user_size != $sym_size)"
0214 continue;
0215 fi
0216
0217
0218 if [[ $func_offset -gt $sym_size ]]; then
0219 [[ $print_warnings = 1 ]] &&
0220 echo "skipping $sym_name address at $addr due to size mismatch ($func_offset > $sym_size)"
0221 continue
0222 fi
0223
0224
0225
0226 [[ $FIRST = 0 ]] && echo
0227 FIRST=0
0228
0229 echo "$sym_name+$func_offset/$sym_size:"
0230
0231
0232
0233 local args="--functions --pretty-print --inlines --exe=$objfile"
0234 [[ $is_vmlinux = 0 ]] && args="$args --section=$sec_name"
0235 local output=$(${ADDR2LINE} $args $addr | sed "s; $dir_prefix\(\./\)*; ;")
0236 [[ -z $output ]] && continue
0237
0238
0239 if [[ $LIST = 0 ]]; then
0240 echo "$output" | while read -r line
0241 do
0242 echo $line
0243 done
0244 DONE=1;
0245 continue
0246 fi
0247
0248
0249 echo "$output" | while read -r line
0250 do
0251 echo
0252 echo $line
0253 n=$(echo $line | sed 's/.*:\([0-9]\+\).*/\1/g')
0254 n1=$[$n-5]
0255 n2=$[$n+5]
0256 f=$(echo $line | sed 's/.*at \(.\+\):.*/\1/g')
0257 ${AWK} 'NR>=strtonum("'$n1'") && NR<=strtonum("'$n2'") { if (NR=='$n') printf(">%d<", NR); else printf(" %d ", NR); printf("\t%s\n", $0)}' $f
0258 done
0259
0260 DONE=1
0261
0262 done < <(${READELF} --symbols --wide $objfile | ${AWK} -v fn=$sym_name '$4 == "FUNC" && $8 == fn')
0263 }
0264
0265 [[ $
0266
0267 objfile=$1
0268
0269 LIST=0
0270 [[ "$objfile" == "--list" ]] && LIST=1 && shift && objfile=$1
0271
0272 [[ ! -f $objfile ]] && die "can't find objfile $objfile"
0273 shift
0274
0275 ${READELF} --section-headers --wide $objfile | ${GREP} -q '\.debug_info' || die "CONFIG_DEBUG_INFO not enabled"
0276
0277 DIR_PREFIX=supercalifragilisticexpialidocious
0278 find_dir_prefix $objfile
0279
0280 FIRST=1
0281 while [[ $
0282 func_addr=$1
0283 shift
0284
0285
0286 DONE=0
0287 __faddr2line $objfile $func_addr $DIR_PREFIX 0
0288
0289
0290 if [[ $DONE = 0 ]]; then
0291 __faddr2line $objfile $func_addr $DIR_PREFIX 1
0292 warn "no match for $func_addr"
0293 fi
0294 done