1#!/bin/bash 2# SPDX-License-Identifier: GPL-2.0 3# Disassemble the Code: line in Linux oopses 4# usage: decodecode < oops.file 5# 6# options: set env. variable AFLAGS=options to pass options to "as"; 7# e.g., to decode an i386 oops on an x86_64 system, use: 8# AFLAGS=--32 decodecode < 386.oops 9# PC=hex - the PC (program counter) the oops points to 10 11faultlinenum=1 12 13cleanup() { 14 rm -f $T $T.s $T.o $T.oo $T.aa $T.dis 15 exit 1 16} 17 18die() { 19 echo "$@" 20 exit 1 21} 22 23trap cleanup EXIT 24 25T=`mktemp` || die "cannot create temp file" 26code= 27cont= 28 29while read i ; do 30 31case "$i" in 32*Code:*) 33 code=$i 34 cont=yes 35 ;; 36*) 37 [ -n "$cont" ] && { 38 xdump="$(echo $i | grep '^[[:xdigit:]<>[:space:]]\+$')" 39 if [ -n "$xdump" ]; then 40 code="$code $xdump" 41 else 42 cont= 43 fi 44 } 45 ;; 46esac 47 48done 49 50if [ -z "$code" ]; then 51 rm $T 52 exit 53fi 54 55echo $code 56code=`echo $code | sed -e 's/.*Code: //'` 57 58width=`expr index "$code" ' '` 59width=$((($width-1)/2)) 60case $width in 611) type=byte ;; 622) type=2byte ;; 634) type=4byte ;; 64esac 65 66if [ -z "$ARCH" ]; then 67 case `uname -m` in 68 aarch64*) ARCH=arm64 ;; 69 arm*) ARCH=arm ;; 70 esac 71fi 72 73# Params: (tmp_file, pc_sub) 74disas() { 75 t=$1 76 pc_sub=$2 77 78 ${CROSS_COMPILE}as $AFLAGS -o $t.o $t.s > /dev/null 2>&1 79 80 if [ "$ARCH" = "arm" ]; then 81 if [ $width -eq 2 ]; then 82 OBJDUMPFLAGS="-M force-thumb" 83 fi 84 85 ${CROSS_COMPILE}strip $t.o 86 fi 87 88 if [ "$ARCH" = "arm64" ]; then 89 if [ $width -eq 4 ]; then 90 type=inst 91 fi 92 93 ${CROSS_COMPILE}strip $t.o 94 fi 95 96 if [ "$ARCH" = "riscv" ]; then 97 OBJDUMPFLAGS="-M no-aliases --section=.text -D" 98 ${CROSS_COMPILE}strip $t.o 99 fi 100 101 if [ $pc_sub -ne 0 ]; then 102 if [ $PC ]; then 103 adj_vma=$(( $PC - $pc_sub )) 104 OBJDUMPFLAGS="$OBJDUMPFLAGS --adjust-vma=$adj_vma" 105 fi 106 fi 107 108 ${CROSS_COMPILE}objdump $OBJDUMPFLAGS -S $t.o | \ 109 grep -v "/tmp\|Disassembly\|\.text\|^$" > $t.dis 2>&1 110} 111 112# Match the maximum number of opcode bytes from @op_bytes contained within 113# @opline 114# 115# Params: 116# @op_bytes: The string of bytes from the Code: line 117# @opline: The disassembled line coming from objdump 118# 119# Returns: 120# The max number of opcode bytes from the beginning of @op_bytes which match 121# the opcode bytes in the objdump line. 122get_substr_opcode_bytes_num() 123{ 124 local op_bytes=$1 125 local opline=$2 126 127 local retval=0 128 substr="" 129 130 for opc in $op_bytes; 131 do 132 substr+="$opc" 133 134 opcode="$substr" 135 if [ "$ARCH" = "riscv" ]; then 136 opcode=$(echo $opcode | tr ' ' '\n' | tac | tr -d '\n') 137 fi 138 139 # return if opcode bytes do not match @opline anymore 140 if ! echo $opline | grep -q "$opcode"; 141 then 142 break 143 fi 144 145 # add trailing space 146 substr+=" " 147 retval=$((retval+1)) 148 done 149 150 return $retval 151} 152 153# Return the line number in objdump output to where the IP marker in the Code: 154# line points to 155# 156# Params: 157# @all_code: code in bytes without the marker 158# @dis_file: disassembled file 159# @ip_byte: The byte to which the IP points to 160get_faultlinenum() 161{ 162 local all_code="$1" 163 local dis_file="$2" 164 165 # num bytes including IP byte 166 local num_bytes_ip=$(( $3 + 1 * $width )) 167 168 # Add the two header lines (we're counting from 1). 169 local retval=3 170 171 # remove marker 172 all_code=$(echo $all_code | sed -e 's/[<>()]//g') 173 174 while read line 175 do 176 get_substr_opcode_bytes_num "$all_code" "$line" 177 ate_opcodes=$? 178 179 if ! (( $ate_opcodes )); then 180 continue 181 fi 182 183 num_bytes_ip=$((num_bytes_ip - ($ate_opcodes * $width) )) 184 if (( $num_bytes_ip <= 0 )); then 185 break 186 fi 187 188 # Delete matched opcode bytes from all_code. For that, compute 189 # how many chars those opcodes are represented by and include 190 # trailing space. 191 # 192 # a byte is 2 chars, ate_opcodes is also the number of trailing 193 # spaces 194 del_chars=$(( ($ate_opcodes * $width * 2) + $ate_opcodes )) 195 196 all_code=$(echo $all_code | sed -e "s!^.\{$del_chars\}!!") 197 198 let "retval+=1" 199 200 done < $dis_file 201 202 return $retval 203} 204 205marker=`expr index "$code" "\<"` 206if [ $marker -eq 0 ]; then 207 marker=`expr index "$code" "\("` 208fi 209 210touch $T.oo 211if [ $marker -ne 0 ]; then 212 # How many bytes to subtract from the program counter 213 # in order to get to the beginning virtual address of the 214 # Code: 215 pc_sub=$(( (($marker - 1) / (2 * $width + 1)) * $width )) 216 echo All code >> $T.oo 217 echo ======== >> $T.oo 218 beforemark=`echo "$code"` 219 echo -n " .$type 0x" > $T.s 220 221 echo $beforemark | sed -e 's/ /,0x/g; s/[<>()]//g' >> $T.s 222 223 disas $T $pc_sub 224 225 cat $T.dis >> $T.oo 226 227 get_faultlinenum "$code" "$T.dis" $pc_sub 228 faultlinenum=$? 229 230 # and fix code at-and-after marker 231 code=`echo "$code" | cut -c$((${marker} + 1))-` 232 233 rm -f $T.o $T.s $T.dis 234fi 235 236echo Code starting with the faulting instruction > $T.aa 237echo =========================================== >> $T.aa 238code=`echo $code | sed -e 's/\r//;s/ [<(]/ /;s/[>)] / /;s/ /,0x/g; s/[>)]$//'` 239echo -n " .$type 0x" > $T.s 240echo $code >> $T.s 241disas $T 0 242cat $T.dis >> $T.aa 243 244cat $T.oo | sed -e "${faultlinenum}s/^\([^:]*:\)\(.*\)/\1\*\2\t\t<-- trapping instruction/" 245echo 246cat $T.aa 247cleanup 248