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