123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237 |
- #!/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 ;;
- 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 [ $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"
- # return if opcode bytes do not match @opline anymore
- if ! echo $opline | grep -q "$substr";
- 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
|