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