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 [ $pc_sub -ne 0 ]; then 97 if [ $PC ]; then 98 adj_vma=$(( $PC - $pc_sub )) 99 OBJDUMPFLAGS="$OBJDUMPFLAGS --adjust-vma=$adj_vma" 100 fi 101 fi 102 103 ${CROSS_COMPILE}objdump $OBJDUMPFLAGS -S $t.o | \ 104 grep -v "/tmp\|Disassembly\|\.text\|^$" > $t.dis 2>&1 105} 106 107# Match the maximum number of opcode bytes from @op_bytes contained within 108# @opline 109# 110# Params: 111# @op_bytes: The string of bytes from the Code: line 112# @opline: The disassembled line coming from objdump 113# 114# Returns: 115# The max number of opcode bytes from the beginning of @op_bytes which match 116# the opcode bytes in the objdump line. 117get_substr_opcode_bytes_num() 118{ 119 local op_bytes=$1 120 local opline=$2 121 122 local retval=0 123 substr="" 124 125 for opc in $op_bytes; 126 do 127 substr+="$opc" 128 129 # return if opcode bytes do not match @opline anymore 130 if ! echo $opline | grep -q "$substr"; 131 then 132 break 133 fi 134 135 # add trailing space 136 substr+=" " 137 retval=$((retval+1)) 138 done 139 140 return $retval 141} 142 143# Return the line number in objdump output to where the IP marker in the Code: 144# line points to 145# 146# Params: 147# @all_code: code in bytes without the marker 148# @dis_file: disassembled file 149# @ip_byte: The byte to which the IP points to 150get_faultlinenum() 151{ 152 local all_code="$1" 153 local dis_file="$2" 154 155 # num bytes including IP byte 156 local num_bytes_ip=$(( $3 + 1 * $width )) 157 158 # Add the two header lines (we're counting from 1). 159 local retval=3 160 161 # remove marker 162 all_code=$(echo $all_code | sed -e 's/[<>()]//g') 163 164 while read line 165 do 166 get_substr_opcode_bytes_num "$all_code" "$line" 167 ate_opcodes=$? 168 169 if ! (( $ate_opcodes )); then 170 continue 171 fi 172 173 num_bytes_ip=$((num_bytes_ip - ($ate_opcodes * $width) )) 174 if (( $num_bytes_ip <= 0 )); then 175 break 176 fi 177 178 # Delete matched opcode bytes from all_code. For that, compute 179 # how many chars those opcodes are represented by and include 180 # trailing space. 181 # 182 # a byte is 2 chars, ate_opcodes is also the number of trailing 183 # spaces 184 del_chars=$(( ($ate_opcodes * $width * 2) + $ate_opcodes )) 185 186 all_code=$(echo $all_code | sed -e "s!^.\{$del_chars\}!!") 187 188 let "retval+=1" 189 190 done < $dis_file 191 192 return $retval 193} 194 195marker=`expr index "$code" "\<"` 196if [ $marker -eq 0 ]; then 197 marker=`expr index "$code" "\("` 198fi 199 200touch $T.oo 201if [ $marker -ne 0 ]; then 202 # How many bytes to subtract from the program counter 203 # in order to get to the beginning virtual address of the 204 # Code: 205 pc_sub=$(( (($marker - 1) / (2 * $width + 1)) * $width )) 206 echo All code >> $T.oo 207 echo ======== >> $T.oo 208 beforemark=`echo "$code"` 209 echo -n " .$type 0x" > $T.s 210 211 echo $beforemark | sed -e 's/ /,0x/g; s/[<>()]//g' >> $T.s 212 213 disas $T $pc_sub 214 215 cat $T.dis >> $T.oo 216 217 get_faultlinenum "$code" "$T.dis" $pc_sub 218 faultlinenum=$? 219 220 # and fix code at-and-after marker 221 code=`echo "$code" | cut -c$((${marker} + 1))-` 222 223 rm -f $T.o $T.s $T.dis 224fi 225 226echo Code starting with the faulting instruction > $T.aa 227echo =========================================== >> $T.aa 228code=`echo $code | sed -e 's/\r//;s/ [<(]/ /;s/[>)] / /;s/ /,0x/g; s/[>)]$//'` 229echo -n " .$type 0x" > $T.s 230echo $code >> $T.s 231disas $T 0 232cat $T.dis >> $T.aa 233 234cat $T.oo | sed -e "${faultlinenum}s/^\([^:]*:\)\(.*\)/\1\*\2\t\t<-- trapping instruction/" 235echo 236cat $T.aa 237cleanup 238