162306a36Sopenharmony_ci#!/bin/bash
262306a36Sopenharmony_ci# SPDX-License-Identifier: GPL-2.0
362306a36Sopenharmony_ci# Disassemble the Code: line in Linux oopses
462306a36Sopenharmony_ci# usage: decodecode < oops.file
562306a36Sopenharmony_ci#
662306a36Sopenharmony_ci# options: set env. variable AFLAGS=options to pass options to "as";
762306a36Sopenharmony_ci# e.g., to decode an i386 oops on an x86_64 system, use:
862306a36Sopenharmony_ci# AFLAGS=--32 decodecode < 386.oops
962306a36Sopenharmony_ci# PC=hex - the PC (program counter) the oops points to
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_cifaultlinenum=1
1262306a36Sopenharmony_ci
1362306a36Sopenharmony_cicleanup() {
1462306a36Sopenharmony_ci	rm -f $T $T.s $T.o $T.oo $T.aa $T.dis
1562306a36Sopenharmony_ci	exit 1
1662306a36Sopenharmony_ci}
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_cidie() {
1962306a36Sopenharmony_ci	echo "$@"
2062306a36Sopenharmony_ci	exit 1
2162306a36Sopenharmony_ci}
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_citrap cleanup EXIT
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_ciT=`mktemp` || die "cannot create temp file"
2662306a36Sopenharmony_cicode=
2762306a36Sopenharmony_cicont=
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_ciwhile read i ; do
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_cicase "$i" in
3262306a36Sopenharmony_ci*Code:*)
3362306a36Sopenharmony_ci	code=$i
3462306a36Sopenharmony_ci	cont=yes
3562306a36Sopenharmony_ci	;;
3662306a36Sopenharmony_ci*)
3762306a36Sopenharmony_ci	[ -n "$cont" ] && {
3862306a36Sopenharmony_ci		xdump="$(echo $i | grep '^[[:xdigit:]<>[:space:]]\+$')"
3962306a36Sopenharmony_ci		if [ -n "$xdump" ]; then
4062306a36Sopenharmony_ci			code="$code $xdump"
4162306a36Sopenharmony_ci		else
4262306a36Sopenharmony_ci			cont=
4362306a36Sopenharmony_ci		fi
4462306a36Sopenharmony_ci	}
4562306a36Sopenharmony_ci	;;
4662306a36Sopenharmony_ciesac
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_cidone
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_ciif [ -z "$code" ]; then
5162306a36Sopenharmony_ci	rm $T
5262306a36Sopenharmony_ci	exit
5362306a36Sopenharmony_cifi
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ciecho $code
5662306a36Sopenharmony_cicode=`echo $code | sed -e 's/.*Code: //'`
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ciwidth=`expr index "$code" ' '`
5962306a36Sopenharmony_ciwidth=$((($width-1)/2))
6062306a36Sopenharmony_cicase $width in
6162306a36Sopenharmony_ci1) type=byte ;;
6262306a36Sopenharmony_ci2) type=2byte ;;
6362306a36Sopenharmony_ci4) type=4byte ;;
6462306a36Sopenharmony_ciesac
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_ciif [ -z "$ARCH" ]; then
6762306a36Sopenharmony_ci    case `uname -m` in
6862306a36Sopenharmony_ci	aarch64*) ARCH=arm64 ;;
6962306a36Sopenharmony_ci	arm*) ARCH=arm ;;
7062306a36Sopenharmony_ci    esac
7162306a36Sopenharmony_cifi
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci# Params: (tmp_file, pc_sub)
7462306a36Sopenharmony_cidisas() {
7562306a36Sopenharmony_ci	t=$1
7662306a36Sopenharmony_ci	pc_sub=$2
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci	${CROSS_COMPILE}as $AFLAGS -o $t.o $t.s > /dev/null 2>&1
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci	if [ "$ARCH" = "arm" ]; then
8162306a36Sopenharmony_ci		if [ $width -eq 2 ]; then
8262306a36Sopenharmony_ci			OBJDUMPFLAGS="-M force-thumb"
8362306a36Sopenharmony_ci		fi
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ci		${CROSS_COMPILE}strip $t.o
8662306a36Sopenharmony_ci	fi
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_ci	if [ "$ARCH" = "arm64" ]; then
8962306a36Sopenharmony_ci		if [ $width -eq 4 ]; then
9062306a36Sopenharmony_ci			type=inst
9162306a36Sopenharmony_ci		fi
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci		${CROSS_COMPILE}strip $t.o
9462306a36Sopenharmony_ci	fi
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ci	if [ "$ARCH" = "riscv" ]; then
9762306a36Sopenharmony_ci		OBJDUMPFLAGS="-M no-aliases --section=.text -D"
9862306a36Sopenharmony_ci		${CROSS_COMPILE}strip $t.o
9962306a36Sopenharmony_ci	fi
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_ci	if [ $pc_sub -ne 0 ]; then
10262306a36Sopenharmony_ci		if [ $PC ]; then
10362306a36Sopenharmony_ci			adj_vma=$(( $PC - $pc_sub ))
10462306a36Sopenharmony_ci			OBJDUMPFLAGS="$OBJDUMPFLAGS --adjust-vma=$adj_vma"
10562306a36Sopenharmony_ci		fi
10662306a36Sopenharmony_ci	fi
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ci	${CROSS_COMPILE}objdump $OBJDUMPFLAGS -S $t.o | \
10962306a36Sopenharmony_ci		grep -v "/tmp\|Disassembly\|\.text\|^$" > $t.dis 2>&1
11062306a36Sopenharmony_ci}
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_ci# Match the maximum number of opcode bytes from @op_bytes contained within
11362306a36Sopenharmony_ci# @opline
11462306a36Sopenharmony_ci#
11562306a36Sopenharmony_ci# Params:
11662306a36Sopenharmony_ci# @op_bytes: The string of bytes from the Code: line
11762306a36Sopenharmony_ci# @opline: The disassembled line coming from objdump
11862306a36Sopenharmony_ci#
11962306a36Sopenharmony_ci# Returns:
12062306a36Sopenharmony_ci# The max number of opcode bytes from the beginning of @op_bytes which match
12162306a36Sopenharmony_ci# the opcode bytes in the objdump line.
12262306a36Sopenharmony_ciget_substr_opcode_bytes_num()
12362306a36Sopenharmony_ci{
12462306a36Sopenharmony_ci	local op_bytes=$1
12562306a36Sopenharmony_ci	local opline=$2
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_ci	local retval=0
12862306a36Sopenharmony_ci	substr=""
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_ci	for opc in $op_bytes;
13162306a36Sopenharmony_ci	do
13262306a36Sopenharmony_ci		substr+="$opc"
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_ci		opcode="$substr"
13562306a36Sopenharmony_ci		if [ "$ARCH" = "riscv" ]; then
13662306a36Sopenharmony_ci			opcode=$(echo $opcode | tr ' ' '\n' | tac | tr -d '\n')
13762306a36Sopenharmony_ci		fi
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ci		# return if opcode bytes do not match @opline anymore
14062306a36Sopenharmony_ci		if ! echo $opline | grep -q "$opcode";
14162306a36Sopenharmony_ci		then
14262306a36Sopenharmony_ci			break
14362306a36Sopenharmony_ci		fi
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_ci		# add trailing space
14662306a36Sopenharmony_ci		substr+=" "
14762306a36Sopenharmony_ci		retval=$((retval+1))
14862306a36Sopenharmony_ci	done
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_ci	return $retval
15162306a36Sopenharmony_ci}
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_ci# Return the line number in objdump output to where the IP marker in the Code:
15462306a36Sopenharmony_ci# line points to
15562306a36Sopenharmony_ci#
15662306a36Sopenharmony_ci# Params:
15762306a36Sopenharmony_ci# @all_code: code in bytes without the marker
15862306a36Sopenharmony_ci# @dis_file: disassembled file
15962306a36Sopenharmony_ci# @ip_byte: The byte to which the IP points to
16062306a36Sopenharmony_ciget_faultlinenum()
16162306a36Sopenharmony_ci{
16262306a36Sopenharmony_ci	local all_code="$1"
16362306a36Sopenharmony_ci	local dis_file="$2"
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_ci	# num bytes including IP byte
16662306a36Sopenharmony_ci	local num_bytes_ip=$(( $3 + 1 * $width ))
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_ci	# Add the two header lines (we're counting from 1).
16962306a36Sopenharmony_ci	local retval=3
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_ci	# remove marker
17262306a36Sopenharmony_ci	all_code=$(echo $all_code | sed -e 's/[<>()]//g')
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_ci	while read line
17562306a36Sopenharmony_ci	do
17662306a36Sopenharmony_ci		get_substr_opcode_bytes_num "$all_code" "$line"
17762306a36Sopenharmony_ci		ate_opcodes=$?
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_ci		if ! (( $ate_opcodes )); then
18062306a36Sopenharmony_ci			continue
18162306a36Sopenharmony_ci		fi
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_ci		num_bytes_ip=$((num_bytes_ip - ($ate_opcodes * $width) ))
18462306a36Sopenharmony_ci		if (( $num_bytes_ip <= 0 )); then
18562306a36Sopenharmony_ci			break
18662306a36Sopenharmony_ci		fi
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_ci		# Delete matched opcode bytes from all_code. For that, compute
18962306a36Sopenharmony_ci		# how many chars those opcodes are represented by and include
19062306a36Sopenharmony_ci		# trailing space.
19162306a36Sopenharmony_ci		#
19262306a36Sopenharmony_ci		# a byte is 2 chars, ate_opcodes is also the number of trailing
19362306a36Sopenharmony_ci		# spaces
19462306a36Sopenharmony_ci		del_chars=$(( ($ate_opcodes * $width * 2) + $ate_opcodes ))
19562306a36Sopenharmony_ci
19662306a36Sopenharmony_ci		all_code=$(echo $all_code | sed -e "s!^.\{$del_chars\}!!")
19762306a36Sopenharmony_ci
19862306a36Sopenharmony_ci		let "retval+=1"
19962306a36Sopenharmony_ci
20062306a36Sopenharmony_ci	done < $dis_file
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_ci	return $retval
20362306a36Sopenharmony_ci}
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_cimarker=`expr index "$code" "\<"`
20662306a36Sopenharmony_ciif [ $marker -eq 0 ]; then
20762306a36Sopenharmony_ci	marker=`expr index "$code" "\("`
20862306a36Sopenharmony_cifi
20962306a36Sopenharmony_ci
21062306a36Sopenharmony_citouch $T.oo
21162306a36Sopenharmony_ciif [ $marker -ne 0 ]; then
21262306a36Sopenharmony_ci	# How many bytes to subtract from the program counter
21362306a36Sopenharmony_ci	# in order to get to the beginning virtual address of the
21462306a36Sopenharmony_ci	# Code:
21562306a36Sopenharmony_ci	pc_sub=$(( (($marker - 1) / (2 * $width + 1)) * $width ))
21662306a36Sopenharmony_ci	echo All code >> $T.oo
21762306a36Sopenharmony_ci	echo ======== >> $T.oo
21862306a36Sopenharmony_ci	beforemark=`echo "$code"`
21962306a36Sopenharmony_ci	echo -n "	.$type 0x" > $T.s
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_ci	echo $beforemark | sed -e 's/ /,0x/g; s/[<>()]//g' >> $T.s
22262306a36Sopenharmony_ci
22362306a36Sopenharmony_ci	disas $T $pc_sub
22462306a36Sopenharmony_ci
22562306a36Sopenharmony_ci	cat $T.dis >> $T.oo
22662306a36Sopenharmony_ci
22762306a36Sopenharmony_ci	get_faultlinenum "$code" "$T.dis" $pc_sub
22862306a36Sopenharmony_ci	faultlinenum=$?
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_ci	# and fix code at-and-after marker
23162306a36Sopenharmony_ci	code=`echo "$code" | cut -c$((${marker} + 1))-`
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_ci	rm -f $T.o $T.s $T.dis
23462306a36Sopenharmony_cifi
23562306a36Sopenharmony_ci
23662306a36Sopenharmony_ciecho Code starting with the faulting instruction  > $T.aa
23762306a36Sopenharmony_ciecho =========================================== >> $T.aa
23862306a36Sopenharmony_cicode=`echo $code | sed -e 's/\r//;s/ [<(]/ /;s/[>)] / /;s/ /,0x/g; s/[>)]$//'`
23962306a36Sopenharmony_ciecho -n "	.$type 0x" > $T.s
24062306a36Sopenharmony_ciecho $code >> $T.s
24162306a36Sopenharmony_cidisas $T 0
24262306a36Sopenharmony_cicat $T.dis >> $T.aa
24362306a36Sopenharmony_ci
24462306a36Sopenharmony_cicat $T.oo | sed -e "${faultlinenum}s/^\([^:]*:\)\(.*\)/\1\*\2\t\t<-- trapping instruction/"
24562306a36Sopenharmony_ciecho
24662306a36Sopenharmony_cicat $T.aa
24762306a36Sopenharmony_cicleanup
248