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