disasm-elf.sh (4704B)
1 #!/bin/sh 2 ## disasm-elf.sh — disassemble a hex2pp-emitted ELF with llvm-objdump. 3 ## 4 ## Our seed ELF.hex2 sets ph_memsz to 512 MB (so the BSS region past 5 ## ELF_end is mappable), but ph_filesz is just the on-disk size. 6 ## llvm-objdump trusts memsz when laying out the segment for 7 ## disassembly and runs off the end of the file with 8 ## "The end of the file was unexpectedly encountered". The seed ELF 9 ## also lacks section headers, so --start-address/--stop-address 10 ## doesn't help on its own. 11 ## 12 ## Workaround: copy the ELF, patch ph_memsz down to ph_filesz, then 13 ## disassemble. Output goes to stdout. 14 ## 15 ## We also auto-default --start-address to e_entry so the ELF header + 16 ## program header bytes at the top of PT_LOAD aren't decoded as bogus 17 ## instructions. Pass an explicit --start-address (e.g. 0x600000) to 18 ## override and see the header bytes. 19 ## 20 ## boot-build-p1*.sh writes a one-line sidecar at <elf>.workdir pointing 21 ## at build/$ARCH/.work/<src-without-ext>/. P1pp builds store 22 ## expanded.hex2pp there; legacy raw-P1 seed builds store prog.hex2. 23 ## When that sidecar is present we extract a label map via 24 ## tools/m1-symbols.py and: 25 ## - default --stop-address to :_text_end if that sentinel label is 26 ## present, so trailing rodata doesn't decode as bogus instructions 27 ## - inject "<label>:" headers and rewrite "<PT_LOAD#0+0xNNN>" xrefs 28 ## in the disasm output 29 ## Pass NO_LABELS=1 to disable both behaviors. 30 ## 31 ## Usage: disasm-elf.sh <elf> [llvm-objdump args...] 32 ## defaults to `-d` (text only). For data + text, pass `-D`. 33 34 set -eu 35 36 [ "$#" -ge 1 ] || { echo "usage: $0 <elf> [llvm-objdump args...]" >&2; exit 2; } 37 38 ELF=$1; shift 39 [ -e "$ELF" ] || { echo "missing $ELF" >&2; exit 1; } 40 41 OBJDUMP=${LLVM_OBJDUMP:-llvm-objdump} 42 TRIPLE=${TRIPLE:-aarch64-linux-gnu} 43 44 # ELF fields we read (little-endian 8-byte): 45 # e_entry at file offset 0x18 46 # ph_filesz at file offset 0x60 (e_phoff 0x40 + 0x20) 47 # ph_memsz at file offset 0x68 (e_phoff 0x40 + 0x28) 48 # Single-program-header layout, per our seed ELF. 49 read_le8() { 50 od -An -tu8 -N8 -j"$2" "$1" | tr -d ' \n' 51 } 52 write_le8() { 53 # $1 file, $2 offset, $3 value 54 printf '%016x' "$3" \ 55 | sed 's/\(..\)\(..\)\(..\)\(..\)\(..\)\(..\)\(..\)\(..\)/\8\7\6\5\4\3\2\1/' \ 56 | xxd -r -p \ 57 | dd of="$1" bs=1 seek="$2" count=8 conv=notrunc status=none 58 } 59 60 ENTRY=$(read_le8 "$ELF" 24) 61 FILESZ=$(read_le8 "$ELF" 96) 62 MEMSZ=$(read_le8 "$ELF" 104) 63 64 TMP=$(mktemp -t disasm-elf.XXXXXX) 65 trap 'rm -f "$TMP"' EXIT 66 cp "$ELF" "$TMP" 67 chmod u+w "$TMP" 68 69 if [ "$MEMSZ" != "$FILESZ" ]; then 70 write_le8 "$TMP" 104 "$FILESZ" 71 fi 72 73 # Default to -d if no objdump flags given. 74 [ "$#" -eq 0 ] && set -- -d 75 76 # Auto-skip the ELF header + program header by defaulting 77 # --start-address to e_entry, unless the user supplied their own. 78 have_start=0 79 have_stop=0 80 for arg in "$@"; do 81 case "$arg" in 82 --start-address=*|--start-address) have_start=1;; 83 --stop-address=*|--stop-address) have_stop=1;; 84 esac 85 done 86 if [ "$have_start" -eq 0 ]; then 87 set -- "--start-address=0x$(printf '%x' "$ENTRY")" "$@" 88 fi 89 90 # Locate expanded.hex2pp (new P1pp path) or prog.hex2 (legacy raw-P1 91 # path) via the <elf>.workdir sidecar produced by boot-build-p1*.sh. 92 # The sidecar holds a repo-relative path (build/$ARCH/.work/<src>/), so 93 # resolve it against the repo root inferred from this script's location. 94 HERE=$(dirname "$0") 95 REPO_ROOT=$(cd "$HERE/.." && pwd) 96 HEX2="" 97 if [ -e "$ELF.workdir" ]; then 98 workdir=$(cat "$ELF.workdir") 99 case "$workdir" in 100 /*) ;; # absolute, leave alone 101 *) workdir="$REPO_ROOT/$workdir" ;; 102 esac 103 if [ -e "$workdir/expanded.hex2pp" ]; then 104 HEX2="$workdir/expanded.hex2pp" 105 elif [ -e "$workdir/prog.hex2" ]; then 106 HEX2="$workdir/prog.hex2" 107 else 108 echo "disasm-elf: $ELF.workdir -> $workdir, but no expanded.hex2pp or prog.hex2 there" >&2 109 fi 110 elif [ "${NO_LABELS:-0}" != "1" ]; then 111 echo "disasm-elf: no $ELF.workdir sidecar; rebuild for label annotation" >&2 112 fi 113 MAP="" 114 if [ "${NO_LABELS:-0}" != "1" ] && [ -n "$HEX2" ]; then 115 MAP=$(mktemp -t disasm-elf-map.XXXXXX) 116 trap 'rm -f "$TMP" "$MAP"' EXIT 117 "$HERE/m1-symbols.py" map "$HEX2" > "$MAP" 118 # Default --stop-address to :_text_end if no user value and the 119 # sentinel exists in the map. 120 if [ "$have_stop" -eq 0 ]; then 121 text_end=$(awk '$2 == "_text_end" {print $1; exit}' "$MAP") 122 [ -n "$text_end" ] && set -- "--stop-address=$text_end" "$@" 123 fi 124 fi 125 126 if [ -n "$MAP" ]; then 127 "$OBJDUMP" --triple="$TRIPLE" "$@" "$TMP" \ 128 | "$HERE/m1-symbols.py" annotate "$MAP" 129 else 130 exec "$OBJDUMP" --triple="$TRIPLE" "$@" "$TMP" 131 fi