Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 4695bafa authored by Josh Poimboeuf's avatar Josh Poimboeuf Committed by Greg Kroah-Hartman
Browse files

faddr2line: Fix overlapping text section failures, the sequel



[ Upstream commit dcea997beed694cbd8705100ca1a6eb0d886de69 ]

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: default avatarPeter Zijlstra <peterz@infradead.org>
Signed-off-by: default avatarJosh Poimboeuf <jpoimboe@kernel.org>
Link: https://lore.kernel.org/r/7d25bc1408bd3a750ac26e60d2f2815a5f4a8363.1654130536.git.jpoimboe@kernel.org


Signed-off-by: default avatarSasha Levin <sashal@kernel.org>
parent 1b34d6a9
Loading
Loading
Loading
Loading
+34 −11
Original line number Diff line number Diff line
@@ -95,17 +95,25 @@ __faddr2line() {
	local print_warnings=$4

	local sym_name=${func_addr%+*}
	local offset=${func_addr#*+}
	offset=${offset%/*}
	local func_offset=${func_addr#*+}
	func_offset=${func_offset%/*}
	local user_size=
	local file_type
	local is_vmlinux=0
	[[ $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"
		DONE=1
		return
	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.
	# In rare cases there might be duplicates, in which case we print all
	# matches.
@@ -114,9 +122,11 @@ __faddr2line() {
		local sym_addr=0x${fields[1]}
		local sym_elf_size=${fields[2]}
		local sym_sec=${fields[6]}
		local sec_size
		local sec_name

		# Get the section size:
		local sec_size=$(${READELF} --section-headers --wide $objfile |
		sec_size=$(${READELF} --section-headers --wide $objfile |
			sed 's/\[ /\[/' |
			${AWK} -v sec=$sym_sec '$1 == "[" sec "]" { print "0x" $6; exit }')

@@ -126,6 +136,17 @@ __faddr2line() {
			return
		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.
		#
		# Unfortunately we can't use the ELF size, because kallsyms
@@ -174,10 +195,10 @@ __faddr2line() {

		sym_size=0x$(printf %x $sym_size)

		# Calculate the section address from user-supplied offset:
		local addr=$(($sym_addr + $offset))
		# Calculate the address from user-supplied offset:
		local addr=$(($sym_addr + $func_offset))
		if [[ -z $addr ]] || [[ $addr = 0 ]]; then
			warn "bad address: $sym_addr + $offset"
			warn "bad address: $sym_addr + $func_offset"
			DONE=1
			return
		fi
@@ -191,9 +212,9 @@ __faddr2line() {
		fi

		# 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 ]] &&
				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
		fi

@@ -202,11 +223,13 @@ __faddr2line() {
		[[ $FIRST = 0 ]] && echo
		FIRST=0

		echo "$sym_name+$offset/$sym_size:"
		echo "$sym_name+$func_offset/$sym_size:"

		# Pass section address to addr2line and strip absolute paths
		# 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

		# Default output (non --list):