faddr2line: Fix overlapping text section failures, the sequel

If a function lives in a section other than .text, but .text also exists
in the object, faddr2line may wrongly assume .text. This can result in
comically wrong output. For example:

$ scripts/faddr2line vmlinux.o enter_from_user_mode+0x1c
enter_from_user_mode+0x1c/0x30:
find_next_bit at /home/jpoimboe/git/linux/./include/linux/find.h:40
(inlined by) perf_clear_dirty_counters at /home/jpoimboe/git/linux/arch/x86/events/core.c:2504

Fix it by passing the section name to addr2line, unless the object file
is vmlinux, in which case the symbol table uses absolute addresses.

Fixes: 1d1a0e7c5100 ("scripts/faddr2line: Fix overlapping text section failures")
Reported-by: Peter Zijlstra <peterz@infradead.org>
Signed-off-by: Josh Poimboeuf <jpoimboe@kernel.org>
Link: https://lore.kernel.org/r/7d25bc1408bd3a750ac26e60d2f2815a5f4a8363.1654130536.git.jpoimboe@kernel.org

Changed files
+34 -11
scripts
+34 -11
scripts/faddr2line
··· 95 95 local print_warnings=$4 96 96 97 97 local sym_name=${func_addr%+*} 98 - local offset=${func_addr#*+} 99 - offset=${offset%/*} 98 + local func_offset=${func_addr#*+} 99 + func_offset=${func_offset%/*} 100 100 local user_size= 101 + local file_type 102 + local is_vmlinux=0 101 103 [[ $func_addr =~ "/" ]] && user_size=${func_addr#*/} 102 104 103 - if [[ -z $sym_name ]] || [[ -z $offset ]] || [[ $sym_name = $func_addr ]]; then 105 + if [[ -z $sym_name ]] || [[ -z $func_offset ]] || [[ $sym_name = $func_addr ]]; then 104 106 warn "bad func+offset $func_addr" 105 107 DONE=1 106 108 return 107 109 fi 110 + 111 + # vmlinux uses absolute addresses in the section table rather than 112 + # section offsets. 113 + local file_type=$(${READELF} --file-header $objfile | 114 + ${AWK} '$1 == "Type:" { print $2; exit }') 115 + [[ $file_type = "EXEC" ]] && is_vmlinux=1 108 116 109 117 # Go through each of the object's symbols which match the func name. 110 118 # In rare cases there might be duplicates, in which case we print all ··· 122 114 local sym_addr=0x${fields[1]} 123 115 local sym_elf_size=${fields[2]} 124 116 local sym_sec=${fields[6]} 117 + local sec_size 118 + local sec_name 125 119 126 120 # Get the section size: 127 - local sec_size=$(${READELF} --section-headers --wide $objfile | 121 + sec_size=$(${READELF} --section-headers --wide $objfile | 128 122 sed 's/\[ /\[/' | 129 123 ${AWK} -v sec=$sym_sec '$1 == "[" sec "]" { print "0x" $6; exit }') 130 124 131 125 if [[ -z $sec_size ]]; then 132 126 warn "bad section size: section: $sym_sec" 127 + DONE=1 128 + return 129 + fi 130 + 131 + # Get the section name: 132 + sec_name=$(${READELF} --section-headers --wide $objfile | 133 + sed 's/\[ /\[/' | 134 + ${AWK} -v sec=$sym_sec '$1 == "[" sec "]" { print $2; exit }') 135 + 136 + if [[ -z $sec_name ]]; then 137 + warn "bad section name: section: $sym_sec" 133 138 DONE=1 134 139 return 135 140 fi ··· 195 174 196 175 sym_size=0x$(printf %x $sym_size) 197 176 198 - # Calculate the section address from user-supplied offset: 199 - local addr=$(($sym_addr + $offset)) 177 + # Calculate the address from user-supplied offset: 178 + local addr=$(($sym_addr + $func_offset)) 200 179 if [[ -z $addr ]] || [[ $addr = 0 ]]; then 201 - warn "bad address: $sym_addr + $offset" 180 + warn "bad address: $sym_addr + $func_offset" 202 181 DONE=1 203 182 return 204 183 fi ··· 212 191 fi 213 192 214 193 # Make sure the provided offset is within the symbol's range: 215 - if [[ $offset -gt $sym_size ]]; then 194 + if [[ $func_offset -gt $sym_size ]]; then 216 195 [[ $print_warnings = 1 ]] && 217 - echo "skipping $sym_name address at $addr due to size mismatch ($offset > $sym_size)" 196 + echo "skipping $sym_name address at $addr due to size mismatch ($func_offset > $sym_size)" 218 197 continue 219 198 fi 220 199 ··· 223 202 [[ $FIRST = 0 ]] && echo 224 203 FIRST=0 225 204 226 - echo "$sym_name+$offset/$sym_size:" 205 + echo "$sym_name+$func_offset/$sym_size:" 227 206 228 207 # Pass section address to addr2line and strip absolute paths 229 208 # from the output: 230 - local output=$(${ADDR2LINE} -fpie $objfile $addr | sed "s; $dir_prefix\(\./\)*; ;") 209 + local args="--functions --pretty-print --inlines --exe=$objfile" 210 + [[ $is_vmlinux = 0 ]] && args="$args --section=$sec_name" 211 + local output=$(${ADDR2LINE} $args $addr | sed "s; $dir_prefix\(\./\)*; ;") 231 212 [[ -z $output ]] && continue 232 213 233 214 # Default output (non --list):