cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

decode_stacktrace.sh (7026B)


      1#!/bin/bash
      2# SPDX-License-Identifier: GPL-2.0
      3# (c) 2014, Sasha Levin <sasha.levin@oracle.com>
      4#set -x
      5
      6usage() {
      7	echo "Usage:"
      8	echo "	$0 -r <release> | <vmlinux> [<base path>|auto] [<modules path>]"
      9}
     10
     11if [[ $1 == "-r" ]] ; then
     12	vmlinux=""
     13	basepath="auto"
     14	modpath=""
     15	release=$2
     16
     17	for fn in {,/usr/lib/debug}/boot/vmlinux-$release{,.debug} /lib/modules/$release{,/build}/vmlinux ; do
     18		if [ -e "$fn" ] ; then
     19			vmlinux=$fn
     20			break
     21		fi
     22	done
     23
     24	if [[ $vmlinux == "" ]] ; then
     25		echo "ERROR! vmlinux image for release $release is not found" >&2
     26		usage
     27		exit 2
     28	fi
     29else
     30	vmlinux=$1
     31	basepath=${2-auto}
     32	modpath=$3
     33	release=""
     34	debuginfod=
     35
     36	# Can we use debuginfod-find?
     37	if type debuginfod-find >/dev/null 2>&1 ; then
     38		debuginfod=${1-only}
     39	fi
     40
     41	if [[ $vmlinux == "" && -z $debuginfod ]] ; then
     42		echo "ERROR! vmlinux image must be specified" >&2
     43		usage
     44		exit 1
     45	fi
     46fi
     47
     48declare aarray_support=true
     49declare -A cache 2>/dev/null
     50if [[ $? != 0 ]]; then
     51	aarray_support=false
     52else
     53	declare -A modcache
     54fi
     55
     56find_module() {
     57	if [[ -n $debuginfod ]] ; then
     58		if [[ -n $modbuildid ]] ; then
     59			debuginfod-find debuginfo $modbuildid && return
     60		fi
     61
     62		# Only using debuginfod so don't try to find vmlinux module path
     63		if [[ $debuginfod == "only" ]] ; then
     64			return
     65		fi
     66	fi
     67
     68	if [[ "$modpath" != "" ]] ; then
     69		for fn in $(find "$modpath" -name "${module//_/[-_]}.ko*") ; do
     70			if readelf -WS "$fn" | grep -qwF .debug_line ; then
     71				echo $fn
     72				return
     73			fi
     74		done
     75		return 1
     76	fi
     77
     78	modpath=$(dirname "$vmlinux")
     79	find_module && return
     80
     81	if [[ $release == "" ]] ; then
     82		release=$(gdb -ex 'print init_uts_ns.name.release' -ex 'quit' -quiet -batch "$vmlinux" 2>/dev/null | sed -n 's/\$1 = "\(.*\)".*/\1/p')
     83	fi
     84
     85	for dn in {/usr/lib/debug,}/lib/modules/$release ; do
     86		if [ -e "$dn" ] ; then
     87			modpath="$dn"
     88			find_module && return
     89		fi
     90	done
     91
     92	modpath=""
     93	return 1
     94}
     95
     96parse_symbol() {
     97	# The structure of symbol at this point is:
     98	#   ([name]+[offset]/[total length])
     99	#
    100	# For example:
    101	#   do_basic_setup+0x9c/0xbf
    102
    103	if [[ $module == "" ]] ; then
    104		local objfile=$vmlinux
    105	elif [[ $aarray_support == true && "${modcache[$module]+isset}" == "isset" ]]; then
    106		local objfile=${modcache[$module]}
    107	else
    108		local objfile=$(find_module)
    109		if [[ $objfile == "" ]] ; then
    110			echo "WARNING! Modules path isn't set, but is needed to parse this symbol" >&2
    111			return
    112		fi
    113		if [[ $aarray_support == true ]]; then
    114			modcache[$module]=$objfile
    115		fi
    116	fi
    117
    118	# Remove the englobing parenthesis
    119	symbol=${symbol#\(}
    120	symbol=${symbol%\)}
    121
    122	# Strip segment
    123	local segment
    124	if [[ $symbol == *:* ]] ; then
    125		segment=${symbol%%:*}:
    126		symbol=${symbol#*:}
    127	fi
    128
    129	# Strip the symbol name so that we could look it up
    130	local name=${symbol%+*}
    131
    132	# Use 'nm vmlinux' to figure out the base address of said symbol.
    133	# It's actually faster to call it every time than to load it
    134	# all into bash.
    135	if [[ $aarray_support == true && "${cache[$module,$name]+isset}" == "isset" ]]; then
    136		local base_addr=${cache[$module,$name]}
    137	else
    138		local base_addr=$(nm "$objfile" 2>/dev/null | awk '$3 == "'$name'" && ($2 == "t" || $2 == "T") {print $1; exit}')
    139		if [[ $base_addr == "" ]] ; then
    140			# address not found
    141			return
    142		fi
    143		if [[ $aarray_support == true ]]; then
    144			cache[$module,$name]="$base_addr"
    145		fi
    146	fi
    147	# Let's start doing the math to get the exact address into the
    148	# symbol. First, strip out the symbol total length.
    149	local expr=${symbol%/*}
    150
    151	# Now, replace the symbol name with the base address we found
    152	# before.
    153	expr=${expr/$name/0x$base_addr}
    154
    155	# Evaluate it to find the actual address
    156	expr=$((expr))
    157	local address=$(printf "%x\n" "$expr")
    158
    159	# Pass it to addr2line to get filename and line number
    160	# Could get more than one result
    161	if [[ $aarray_support == true && "${cache[$module,$address]+isset}" == "isset" ]]; then
    162		local code=${cache[$module,$address]}
    163	else
    164		local code=$(${CROSS_COMPILE}addr2line -i -e "$objfile" "$address" 2>/dev/null)
    165		if [[ $aarray_support == true ]]; then
    166			cache[$module,$address]=$code
    167		fi
    168	fi
    169
    170	# addr2line doesn't return a proper error code if it fails, so
    171	# we detect it using the value it prints so that we could preserve
    172	# the offset/size into the function and bail out
    173	if [[ $code == "??:0" ]]; then
    174		return
    175	fi
    176
    177	# Strip out the base of the path on each line
    178	code=$(while read -r line; do echo "${line#$basepath/}"; done <<< "$code")
    179
    180	# In the case of inlines, move everything to same line
    181	code=${code//$'\n'/' '}
    182
    183	# Replace old address with pretty line numbers
    184	symbol="$segment$name ($code)"
    185}
    186
    187debuginfod_get_vmlinux() {
    188	local vmlinux_buildid=${1##* }
    189
    190	if [[ $vmlinux != "" ]]; then
    191		return
    192	fi
    193
    194	if [[ $vmlinux_buildid =~ ^[0-9a-f]+ ]]; then
    195		vmlinux=$(debuginfod-find debuginfo $vmlinux_buildid)
    196		if [[ $? -ne 0 ]] ; then
    197			echo "ERROR! vmlinux image not found via debuginfod-find" >&2
    198			usage
    199			exit 2
    200		fi
    201		return
    202	fi
    203	echo "ERROR! Build ID for vmlinux not found. Try passing -r or specifying vmlinux" >&2
    204	usage
    205	exit 2
    206}
    207
    208decode_code() {
    209	local scripts=`dirname "${BASH_SOURCE[0]}"`
    210
    211	echo "$1" | $scripts/decodecode
    212}
    213
    214handle_line() {
    215	if [[ $basepath == "auto" && $vmlinux != "" ]] ; then
    216		module=""
    217		symbol="kernel_init+0x0/0x0"
    218		parse_symbol
    219		basepath=${symbol#kernel_init (}
    220		basepath=${basepath%/init/main.c:*)}
    221	fi
    222
    223	local words
    224
    225	# Tokenize
    226	read -a words <<<"$1"
    227
    228	# Remove hex numbers. Do it ourselves until it happens in the
    229	# kernel
    230
    231	# We need to know the index of the last element before we
    232	# remove elements because arrays are sparse
    233	local last=$(( ${#words[@]} - 1 ))
    234
    235	for i in "${!words[@]}"; do
    236		# Remove the address
    237		if [[ ${words[$i]} =~ \[\<([^]]+)\>\] ]]; then
    238			unset words[$i]
    239		fi
    240
    241		# Format timestamps with tabs
    242		if [[ ${words[$i]} == \[ && ${words[$i+1]} == *\] ]]; then
    243			unset words[$i]
    244			words[$i+1]=$(printf "[%13s\n" "${words[$i+1]}")
    245		fi
    246	done
    247
    248	if [[ ${words[$last]} =~ ^[0-9a-f]+\] ]]; then
    249		words[$last-1]="${words[$last-1]} ${words[$last]}"
    250		unset words[$last]
    251		last=$(( $last - 1 ))
    252	fi
    253
    254	if [[ ${words[$last]} =~ \[([^]]+)\] ]]; then
    255		module=${words[$last]}
    256		module=${module#\[}
    257		module=${module%\]}
    258		modbuildid=${module#* }
    259		module=${module% *}
    260		if [[ $modbuildid == $module ]]; then
    261			modbuildid=
    262		fi
    263		symbol=${words[$last-1]}
    264		unset words[$last-1]
    265	else
    266		# The symbol is the last element, process it
    267		symbol=${words[$last]}
    268		module=
    269		modbuildid=
    270	fi
    271
    272	unset words[$last]
    273	parse_symbol # modifies $symbol
    274
    275	# Add up the line number to the symbol
    276	echo "${words[@]}" "$symbol $module"
    277}
    278
    279while read line; do
    280	# Let's see if we have an address in the line
    281	if [[ $line =~ \[\<([^]]+)\>\] ]] ||
    282	   [[ $line =~ [^+\ ]+\+0x[0-9a-f]+/0x[0-9a-f]+ ]]; then
    283		# Translate address to line numbers
    284		handle_line "$line"
    285	# Is it a code line?
    286	elif [[ $line == *Code:* ]]; then
    287		decode_code "$line"
    288	# Is it a version line?
    289	elif [[ -n $debuginfod && $line =~ PID:\ [0-9]+\ Comm: ]]; then
    290		debuginfod_get_vmlinux "$line"
    291	else
    292		# Nothing special in this line, show it as is
    293		echo "$line"
    294	fi
    295done