forked from mirrors/linux
		
	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: 1d1a0e7c51 ("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
			
			
This commit is contained in:
		
							parent
							
								
									c2f75a43f5
								
							
						
					
					
						commit
						dcea997bee
					
				
					 1 changed files with 34 additions and 11 deletions
				
			
		|  | @ -95,17 +95,25 @@ __faddr2line() { | ||||||
| 	local print_warnings=$4 | 	local print_warnings=$4 | ||||||
| 
 | 
 | ||||||
| 	local sym_name=${func_addr%+*} | 	local sym_name=${func_addr%+*} | ||||||
| 	local offset=${func_addr#*+} | 	local func_offset=${func_addr#*+} | ||||||
| 	offset=${offset%/*} | 	func_offset=${func_offset%/*} | ||||||
| 	local user_size= | 	local user_size= | ||||||
|  | 	local file_type | ||||||
|  | 	local is_vmlinux=0 | ||||||
| 	[[ $func_addr =~ "/" ]] && user_size=${func_addr#*/} | 	[[ $func_addr =~ "/" ]] && user_size=${func_addr#*/} | ||||||
| 
 | 
 | ||||||
| 	if [[ -z $sym_name ]] || [[ -z $offset ]] || [[ $sym_name = $func_addr ]]; then | 	if [[ -z $sym_name ]] || [[ -z $func_offset ]] || [[ $sym_name = $func_addr ]]; then | ||||||
| 		warn "bad func+offset $func_addr" | 		warn "bad func+offset $func_addr" | ||||||
| 		DONE=1 | 		DONE=1 | ||||||
| 		return | 		return | ||||||
| 	fi | 	fi | ||||||
| 
 | 
 | ||||||
|  | 	# vmlinux uses absolute addresses in the section table rather than | ||||||
|  | 	# section offsets. | ||||||
|  | 	local file_type=$(${READELF} --file-header $objfile | | ||||||
|  | 		${AWK} '$1 == "Type:" { print $2; exit }') | ||||||
|  | 	[[ $file_type = "EXEC" ]] && is_vmlinux=1 | ||||||
|  | 
 | ||||||
| 	# Go through each of the object's symbols which match the func name. | 	# Go through each of the object's symbols which match the func name. | ||||||
| 	# In rare cases there might be duplicates, in which case we print all | 	# In rare cases there might be duplicates, in which case we print all | ||||||
| 	# matches. | 	# matches. | ||||||
|  | @ -114,9 +122,11 @@ __faddr2line() { | ||||||
| 		local sym_addr=0x${fields[1]} | 		local sym_addr=0x${fields[1]} | ||||||
| 		local sym_elf_size=${fields[2]} | 		local sym_elf_size=${fields[2]} | ||||||
| 		local sym_sec=${fields[6]} | 		local sym_sec=${fields[6]} | ||||||
|  | 		local sec_size | ||||||
|  | 		local sec_name | ||||||
| 
 | 
 | ||||||
| 		# Get the section size: | 		# Get the section size: | ||||||
| 		local sec_size=$(${READELF} --section-headers --wide $objfile | | 		sec_size=$(${READELF} --section-headers --wide $objfile | | ||||||
| 			sed 's/\[ /\[/' | | 			sed 's/\[ /\[/' | | ||||||
| 			${AWK} -v sec=$sym_sec '$1 == "[" sec "]" { print "0x" $6; exit }') | 			${AWK} -v sec=$sym_sec '$1 == "[" sec "]" { print "0x" $6; exit }') | ||||||
| 
 | 
 | ||||||
|  | @ -126,6 +136,17 @@ __faddr2line() { | ||||||
| 			return | 			return | ||||||
| 		fi | 		fi | ||||||
| 
 | 
 | ||||||
|  | 		# Get the section name: | ||||||
|  | 		sec_name=$(${READELF} --section-headers --wide $objfile | | ||||||
|  | 			sed 's/\[ /\[/' | | ||||||
|  | 			${AWK} -v sec=$sym_sec '$1 == "[" sec "]" { print $2; exit }') | ||||||
|  | 
 | ||||||
|  | 		if [[ -z $sec_name ]]; then | ||||||
|  | 			warn "bad section name: section: $sym_sec" | ||||||
|  | 			DONE=1 | ||||||
|  | 			return | ||||||
|  | 		fi | ||||||
|  | 
 | ||||||
| 		# Calculate the symbol size. | 		# Calculate the symbol size. | ||||||
| 		# | 		# | ||||||
| 		# Unfortunately we can't use the ELF size, because kallsyms | 		# Unfortunately we can't use the ELF size, because kallsyms | ||||||
|  | @ -174,10 +195,10 @@ __faddr2line() { | ||||||
| 
 | 
 | ||||||
| 		sym_size=0x$(printf %x $sym_size) | 		sym_size=0x$(printf %x $sym_size) | ||||||
| 
 | 
 | ||||||
| 		# Calculate the section address from user-supplied offset: | 		# Calculate the address from user-supplied offset: | ||||||
| 		local addr=$(($sym_addr + $offset)) | 		local addr=$(($sym_addr + $func_offset)) | ||||||
| 		if [[ -z $addr ]] || [[ $addr = 0 ]]; then | 		if [[ -z $addr ]] || [[ $addr = 0 ]]; then | ||||||
| 			warn "bad address: $sym_addr + $offset" | 			warn "bad address: $sym_addr + $func_offset" | ||||||
| 			DONE=1 | 			DONE=1 | ||||||
| 			return | 			return | ||||||
| 		fi | 		fi | ||||||
|  | @ -191,9 +212,9 @@ __faddr2line() { | ||||||
| 		fi | 		fi | ||||||
| 
 | 
 | ||||||
| 		# Make sure the provided offset is within the symbol's range: | 		# Make sure the provided offset is within the symbol's range: | ||||||
| 		if [[ $offset -gt $sym_size ]]; then | 		if [[ $func_offset -gt $sym_size ]]; then | ||||||
| 			[[ $print_warnings = 1 ]] && | 			[[ $print_warnings = 1 ]] && | ||||||
| 				echo "skipping $sym_name address at $addr due to size mismatch ($offset > $sym_size)" | 				echo "skipping $sym_name address at $addr due to size mismatch ($func_offset > $sym_size)" | ||||||
| 			continue | 			continue | ||||||
| 		fi | 		fi | ||||||
| 
 | 
 | ||||||
|  | @ -202,11 +223,13 @@ __faddr2line() { | ||||||
| 		[[ $FIRST = 0 ]] && echo | 		[[ $FIRST = 0 ]] && echo | ||||||
| 		FIRST=0 | 		FIRST=0 | ||||||
| 
 | 
 | ||||||
| 		echo "$sym_name+$offset/$sym_size:" | 		echo "$sym_name+$func_offset/$sym_size:" | ||||||
| 
 | 
 | ||||||
| 		# Pass section address to addr2line and strip absolute paths | 		# Pass section address to addr2line and strip absolute paths | ||||||
| 		# from the output: | 		# from the output: | ||||||
| 		local output=$(${ADDR2LINE} -fpie $objfile $addr | sed "s; $dir_prefix\(\./\)*; ;") | 		local args="--functions --pretty-print --inlines --exe=$objfile" | ||||||
|  | 		[[ $is_vmlinux = 0 ]] && args="$args --section=$sec_name" | ||||||
|  | 		local output=$(${ADDR2LINE} $args $addr | sed "s; $dir_prefix\(\./\)*; ;") | ||||||
| 		[[ -z $output ]] && continue | 		[[ -z $output ]] && continue | ||||||
| 
 | 
 | ||||||
| 		# Default output (non --list): | 		# Default output (non --list): | ||||||
|  |  | ||||||
		Loading…
	
		Reference in a new issue
	
	 Josh Poimboeuf
						Josh Poimboeuf