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