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