@@ -85,15 +85,17 @@ command -v ${ADDR2LINE} >/dev/null 2>&1 || die "${ADDR2LINE} isn't installed"
8585# init/main.c! This only works for vmlinux. Otherwise it falls back to
8686# printing the absolute path.
8787find_dir_prefix () {
88- local objfile=$1
89-
90- local start_kernel_addr=$( ${READELF} --symbols --wide $objfile | sed ' s/\[.*\]//' |
88+ local start_kernel_addr=$( echo " ${ELF_SYMS} " | sed ' s/\[.*\]//' |
9189 ${AWK} ' $8 == "start_kernel" {printf "0x%s", $2}' )
9290 [[ -z $start_kernel_addr ]] && return
9391
94- local file_line= $( ${ADDR2LINE} -e $objfile $start_kernel_addr )
95- [[ -z $file_line ]] && return
92+ run_addr2line ${start_kernel_addr} " "
93+ [[ -z $ADDR2LINE_OUT ]] && return
9694
95+ local file_line=${ADDR2LINE_OUT#* at }
96+ if [[ -z $file_line ]] || [[ $file_line = $ADDR2LINE_OUT ]]; then
97+ return
98+ fi
9799 local prefix=${file_line% init/ main.c:* }
98100 if [[ -z $prefix ]] || [[ $prefix = $file_line ]]; then
99101 return
@@ -103,6 +105,71 @@ find_dir_prefix() {
103105 return 0
104106}
105107
108+ run_readelf () {
109+ local objfile=$1
110+ local out=$( ${READELF} --file-header --section-headers --symbols --wide $objfile )
111+
112+ # This assumes that readelf first prints the file header, then the section headers, then the symbols.
113+ # Note: It seems that GNU readelf does not prefix section headers with the "There are X section headers"
114+ # line when multiple options are given, so let's also match with the "Section Headers:" line.
115+ ELF_FILEHEADER=$( echo " ${out} " | sed -n ' /There are [0-9]* section headers, starting at offset\|Section Headers:/q;p' )
116+ ELF_SECHEADERS=$( echo " ${out} " | sed -n ' /There are [0-9]* section headers, starting at offset\|Section Headers:/,$p' | sed -n ' /Symbol table .* contains [0-9]* entries:/q;p' )
117+ ELF_SYMS=$( echo " ${out} " | sed -n ' /Symbol table .* contains [0-9]* entries:/,$p' )
118+ }
119+
120+ check_vmlinux () {
121+ # vmlinux uses absolute addresses in the section table rather than
122+ # section offsets.
123+ IS_VMLINUX=0
124+ local file_type=$( echo " ${ELF_FILEHEADER} " |
125+ ${AWK} ' $1 == "Type:" { print $2; exit }' )
126+ if [[ $file_type = " EXEC" ]] || [[ $file_type == " DYN" ]]; then
127+ IS_VMLINUX=1
128+ fi
129+ }
130+
131+ init_addr2line () {
132+ local objfile=$1
133+
134+ check_vmlinux
135+
136+ ADDR2LINE_ARGS=" --functions --pretty-print --inlines --addresses --exe=$objfile "
137+ if [[ $IS_VMLINUX = 1 ]]; then
138+ # If the executable file is vmlinux, we don't pass section names to
139+ # addr2line, so we can launch it now as a single long-running process.
140+ coproc ADDR2LINE_PROC (${ADDR2LINE} ${ADDR2LINE_ARGS} )
141+ fi
142+ }
143+
144+ run_addr2line () {
145+ local addr=$1
146+ local sec_name=$2
147+
148+ if [[ $IS_VMLINUX = 1 ]]; then
149+ # We send to the addr2line process: (1) the address, then (2) a sentinel
150+ # value, i.e., something that can't be interpreted as a valid address
151+ # (i.e., ","). This causes addr2line to write out: (1) the answer for
152+ # our address, then (2) either "?? ??:0" or "0x0...0: ..." (if
153+ # using binutils' addr2line), or "," (if using LLVM's addr2line).
154+ echo ${addr} >& " ${ADDR2LINE_PROC[1]} "
155+ echo " ," >& " ${ADDR2LINE_PROC[1]} "
156+ local first_line
157+ read -r first_line < & " ${ADDR2LINE_PROC[0]} "
158+ ADDR2LINE_OUT=$( echo " ${first_line} " | sed ' s/^0x[0-9a-fA-F]*: //' )
159+ while read -r line < & " ${ADDR2LINE_PROC[0]} " ; do
160+ if [[ " $line " == " ?? ??:0" ]] || [[ " $line " == " ," ]] || [[ $( echo " $line " | ${GREP} " ^0x00*: " ) ]]; then
161+ break
162+ fi
163+ ADDR2LINE_OUT+=$' \n ' $( echo " $line " | sed ' s/^0x[0-9a-fA-F]*: //' )
164+ done
165+ else
166+ # Run addr2line as a single invocation.
167+ local sec_arg
168+ [[ -z $sec_name ]] && sec_arg=" " || sec_arg=" --section=${sec_name} "
169+ ADDR2LINE_OUT=$( ${ADDR2LINE} ${ADDR2LINE_ARGS} ${sec_arg} ${addr} | sed ' s/^0x[0-9a-fA-F]*: //' )
170+ fi
171+ }
172+
106173__faddr2line () {
107174 local objfile=$1
108175 local func_addr=$2
@@ -113,8 +180,6 @@ __faddr2line() {
113180 local func_offset=${func_addr#* +}
114181 func_offset=${func_offset%/* }
115182 local user_size=
116- local file_type
117- local is_vmlinux=0
118183 [[ $func_addr =~ " /" ]] && user_size=${func_addr#*/ }
119184
120185 if [[ -z $sym_name ]] || [[ -z $func_offset ]] || [[ $sym_name = $func_addr ]]; then
@@ -123,14 +188,6 @@ __faddr2line() {
123188 return
124189 fi
125190
126- # vmlinux uses absolute addresses in the section table rather than
127- # section offsets.
128- local file_type=$( ${READELF} --file-header $objfile |
129- ${AWK} ' $1 == "Type:" { print $2; exit }' )
130- if [[ $file_type = " EXEC" ]] || [[ $file_type == " DYN" ]]; then
131- is_vmlinux=1
132- fi
133-
134191 # Go through each of the object's symbols which match the func name.
135192 # In rare cases there might be duplicates, in which case we print all
136193 # matches.
@@ -143,8 +200,7 @@ __faddr2line() {
143200 local sec_name
144201
145202 # Get the section size:
146- sec_size=$( ${READELF} --section-headers --wide $objfile |
147- sed ' s/\[ /\[/' |
203+ sec_size=$( echo " ${ELF_SECHEADERS} " | sed ' s/\[ /\[/' |
148204 ${AWK} -v sec=$sym_sec ' $1 == "[" sec "]" { print "0x" $6; exit }' )
149205
150206 if [[ -z $sec_size ]]; then
@@ -154,8 +210,7 @@ __faddr2line() {
154210 fi
155211
156212 # Get the section name:
157- sec_name=$( ${READELF} --section-headers --wide $objfile |
158- sed ' s/\[ /\[/' |
213+ sec_name=$( echo " ${ELF_SECHEADERS} " | sed ' s/\[ /\[/' |
159214 ${AWK} -v sec=$sym_sec ' $1 == "[" sec "]" { print $2; exit }' )
160215
161216 if [[ -z $sec_name ]]; then
@@ -197,7 +252,7 @@ __faddr2line() {
197252 found=2
198253 break
199254 fi
200- done < <( ${READELF} --symbols --wide $objfile | sed ' s/\[.*\]//' | ${AWK} -v sec=$sym_sec ' $7 == sec' | sort --key=2)
255+ done < <( echo " ${ELF_SYMS} " | sed ' s/\[.*\]//' | ${AWK} -v sec=$sym_sec ' $7 == sec' | sort --key=2 | ${GREP} -A1 --no-group-separator " ${sym_name} $ " )
201256
202257 if [[ $found = 0 ]]; then
203258 warn " can't find symbol: sym_name: $sym_name sym_sec: $sym_sec sym_addr: $sym_addr sym_elf_size: $sym_elf_size "
@@ -249,9 +304,8 @@ __faddr2line() {
249304
250305 # Pass section address to addr2line and strip absolute paths
251306 # from the output:
252- local args=" --functions --pretty-print --inlines --exe=$objfile "
253- [[ $is_vmlinux = 0 ]] && args=" $args --section=$sec_name "
254- local output=$( ${ADDR2LINE} $args $addr | sed " s; $dir_prefix \(\./\)*; ;" )
307+ run_addr2line $addr $sec_name
308+ local output=$( echo " ${ADDR2LINE_OUT} " | sed " s; $dir_prefix \(\./\)*; ;" )
255309 [[ -z $output ]] && continue
256310
257311 # Default output (non --list):
@@ -278,7 +332,7 @@ __faddr2line() {
278332
279333 DONE=1
280334
281- done < <( ${READELF} --symbols --wide $objfile | sed ' s/\[.*\]//' | ${AWK} -v fn=$sym_name ' $8 == fn' )
335+ done < <( echo " ${ELF_SYMS} " | sed ' s/\[.*\]//' | ${AWK} -v fn=$sym_name ' $8 == fn' )
282336}
283337
284338[[ $# -lt 2 ]] && usage
@@ -291,10 +345,14 @@ LIST=0
291345[[ ! -f $objfile ]] && die " can't find objfile $objfile "
292346shift
293347
294- ${READELF} --section-headers --wide $objfile | ${GREP} -q ' \.debug_info' || die " CONFIG_DEBUG_INFO not enabled"
348+ run_readelf $objfile
349+
350+ echo " ${ELF_SECHEADERS} " | ${GREP} -q ' \.debug_info' || die " CONFIG_DEBUG_INFO not enabled"
351+
352+ init_addr2line $objfile
295353
296354DIR_PREFIX=supercalifragilisticexpialidocious
297- find_dir_prefix $objfile
355+ find_dir_prefix
298356
299357FIRST=1
300358while [[ $# -gt 0 ]]; do
0 commit comments