1#!/usr/bin/env bash
2# SPDX-License-Identifier: LGPL-2.1-or-later
3
4set -e
5
6if [[ $# -lt 2 ]]; then
7    echo "Usage: ${0} TARGET INPUT [GDBSCRIPT]"
8    echo "Debug systemd-boot/stub in QEMU."
9    echo
10    echo "TARGET should point to the EFI binary to be examined inside the"
11    echo "build directory (systemd-boot\$ARCH.efi or linux\$arch.efi.stub)."
12    echo
13    echo "INPUT should point to the QEMU serial output pipe. This is used to"
14    echo "extract the location of the symbols. For this to work, QEMU must"
15    echo "be run with '-s -serial pipe:PATH'. Note that QEMU will append"
16    echo ".in/.out to the path, while this script expects the out pipe directly."
17    echo
18    echo "If GDBSCRIPT is empty, gdb is run directly attached to the boot"
19    echo "loader, otherwise a script is generated in the given path that allows"
20    echo "attaching manually like this:"
21    echo "    (gdb) source GDBSCRIPT"
22    echo "    (gdb) target remote :1234"
23    echo
24    echo "Example usage:"
25    echo "    mkfifo /tmp/sdboot.{in,out}"
26    echo "    qemu-system-x86_64 [...] -s -serial pipe:/tmp/sdboot"
27    echo "    ./tools/debug-sd-boot.sh ./build/src/boot/efi/systemd-bootx64.efi \\"
28    echo "        /tmp/sdboot.out"
29    exit 1
30fi
31
32binary=$(realpath "${1}")
33if [[ "${1}" =~ systemd-boot([[:alnum:]]+).efi ]]; then
34    target="systemd-boot"
35    symbols=$(realpath "${1%efi}elf")
36elif [[ "${1}" =~ linux([[:alnum:]]+).efi.stub ]]; then
37    target="systemd-stub"
38    symbols=$(realpath "${1%efi.stub}elf.stub")
39else
40    echo "Cannot detect EFI binary '${1}'."
41    exit 1
42fi
43
44case "${BASH_REMATCH[1]}" in
45    ia32) arch="i386";;
46    x64)  arch="i386:x86-64";;
47    aa64) arch="aarch64";;
48    arm|riscv64) arch="${BASH_REMATCH[1]}";;
49    *)
50        echo "Unknown EFI arch '${BASH_REMATCH[1]}'."
51        exit 1
52esac
53
54# system-boot will print out a line like this to inform us where gdb is supposed to
55# look for .text and .data section:
56#        systemd-boot@0x0,0x0
57while read -r line; do
58    if [[ "${line}" =~ ${target}@(0x[[:xdigit:]]+),(0x[[:xdigit:]]+) ]]; then
59        text="${BASH_REMATCH[1]}"
60        data="${BASH_REMATCH[2]}"
61        break
62    fi
63done < "${2}"
64
65if [[ -z "${text}" || -z "${data}" ]]; then
66    echo "Could not determine text and data location."
67    exit 1
68fi
69
70if [[ -z "${3}" ]]; then
71    gdb_script=$(mktemp /tmp/debug-sd-boot.XXXXXX.gdb)
72    trap 'rm -f "${gdb_script}"' EXIT
73else
74    gdb_script="${3}"
75fi
76
77echo "file ${binary}
78add-symbol-file ${symbols} ${text} -s .data ${data}
79set architecture ${arch}" > "${gdb_script}"
80
81if [[ -z "${3}" ]]; then
82    gdb -x "${gdb_script}" -ex "target remote :1234"
83else
84    echo "GDB script written to '${gdb_script}'."
85fi
86