aboutsummaryrefslogtreecommitdiffstats
path: root/scripts
diff options
context:
space:
mode:
authorKonstantin Aladyshev <aladyshev22@gmail.com>2021-07-18 19:59:47 +0300
committerKonstantin Aladyshev <aladyshev22@gmail.com>2021-07-18 19:59:47 +0300
commit88429e84dac6a5fb590c6e8e39d60536edda8485 (patch)
treec1e8d28eedd9db185cce0115b549eef249888895 /scripts
parent2b80073ef8d7887bd7b03036370a6f1ea54ed992 (diff)
downloadUEFI-Lessons-88429e84dac6a5fb590c6e8e39d60536edda8485.tar.gz
UEFI-Lessons-88429e84dac6a5fb590c6e8e39d60536edda8485.tar.bz2
UEFI-Lessons-88429e84dac6a5fb590c6e8e39d60536edda8485.zip
Add scripts for GDB launch on UEFI shell applications and drivers
Signed-off-by: Konstantin Aladyshev <aladyshev22@gmail.com>
Diffstat (limited to 'scripts')
-rw-r--r--scripts/efi.py202
-rwxr-xr-xscripts/run_gdb.sh144
2 files changed, 346 insertions, 0 deletions
diff --git a/scripts/efi.py b/scripts/efi.py
new file mode 100644
index 0000000..c904bae
--- /dev/null
+++ b/scripts/efi.py
@@ -0,0 +1,202 @@
+#!/usr/bin/env python3
+# Load OVMF debug symbols into gdb
+# Author: Artem Nefedov
+
+import gdb
+import re
+import os
+from collections import OrderedDict
+
+
+def clear_symbols():
+ gdb.execute('file')
+ gdb.execute('symbol-file')
+
+
+def remote_connect():
+ gdb.execute('target remote :1234')
+
+
+def update_addresses(base_addr, text_addr, data_addr):
+ try:
+ print(' Base address ' + base_addr)
+ i_base_addr = int(base_addr, 16)
+ except:
+ print('Failed to locate base address')
+ return None
+
+ try:
+ print('.text address ' + text_addr)
+ print('.data address ' + data_addr)
+ i_text_addr = int(text_addr, 16)
+ i_data_addr = int(data_addr, 16)
+ except:
+ print("Failed to locate sections' addresses")
+ return None
+
+ return ((i_base_addr + i_text_addr), (i_base_addr + i_data_addr))
+
+
+def add_symbol_file(file_name, i_text_addr, i_data_addr):
+ gdb.execute('add-symbol-file ' + file_name +
+ ' ' + hex(i_text_addr) +
+ ' -s .data ' + hex(i_data_addr))
+
+
+def get_pagination():
+ out = gdb.execute('show pagination', to_string=True)
+ return out.split()[-1].rstrip('.')
+
+
+class Command_efi(gdb.Command):
+ """
+ Load all debug symbols for UEFI OVMF image. Image must be build with EDK2.
+ Requires debug.log to be located in working directory.
+ By default, it will try to load all drivers listed in debug.log.
+ Alternatively, you can explicitly specify a list of drives to load.
+
+ Usage: efi [options] [driver_1 driver_2 ...]
+
+ Options:
+ -r - connect to remote target after execution
+ -64 - use X64 architecture (default is IA32)
+ """
+
+ LOG_FILE = 'debug.log'
+ A_PATTERN = r'Loading [^ ]+ at (0x[0-9A-F]{8,}) [^ ]+ ([^ ]+)\.efi'
+
+ def __init__(self):
+ super(Command_efi, self).__init__("efi", gdb.COMMAND_OBSCURE)
+ self.arch = 'IA32'
+
+ def find_file(self, name):
+ # look in working directory first (without subdirectories)
+ for f in os.listdir('.'):
+ if f == name and os.path.isfile(f):
+ return f
+ # if nothing is found, look in "Build" directory and subdirectories
+ if not os.path.isdir('Build'):
+ return None
+ for root, dirs, files in os.walk('Build'):
+ if name in files and self.arch in root.split(os.sep):
+ return os.path.join(root, name)
+
+ def get_addresses(self, file_name):
+ gdb.execute('file ' + file_name)
+ ok_arch = False
+ file_arch = None
+
+ for line in gdb.execute('info files', to_string=True).split('\n'):
+ if ' is .text' in line:
+ text_addr = line.split()[0]
+ elif ' is .data' in line:
+ data_addr = line.split()[0]
+ elif ' file type ' in line:
+ file_arch = line.split()[-1].rstrip('.')
+ if file_arch == 'pei-i386' and self.arch == 'IA32':
+ ok_arch = True
+ elif file_arch == 'pei-x86-64' and self.arch == 'X64':
+ ok_arch = True
+
+ gdb.execute('file')
+
+ if ok_arch:
+ return (text_addr, data_addr)
+ else:
+ print('Bad file architecture ' + str(file_arch))
+ return (None, None)
+
+ def get_drivers(self, drivers):
+ print('Looking for addresses in ' + self.LOG_FILE)
+ with open(self.LOG_FILE, 'r', errors='ignore') as f:
+ for match in re.finditer(self.A_PATTERN, f.read()):
+ name = match.group(2)
+ if not drivers or name in drivers or name + '.efi' in drivers:
+ yield (match.group(1), name + '.efi')
+
+ def invoke(self, arg, from_tty):
+ self.dont_repeat()
+ clear_symbols()
+
+ pagination = get_pagination()
+
+ if pagination == 'on':
+ print('Turning pagination off')
+ gdb.execute('set pagination off')
+
+ if arg:
+ drivers = [d for d in arg.split() if not d.startswith('-')]
+ if drivers:
+ print('Using pre-defined driver list: ' + str(drivers))
+ if '-64' in arg.split():
+ self.arch = 'X64'
+ gdb.execute('set architecture i386:x86-64:intel')
+ else:
+ drivers = None
+
+ if not os.path.isdir('Build'):
+ print('Directory "Build" is missing')
+
+ print('With architecture ' + self.arch)
+
+ files_in_log = OrderedDict()
+ load_addresses = {}
+ used_addresses = {}
+
+ # if same file is loaded multiple times in log, use last occurence
+ for base_addr, file_name in self.get_drivers(drivers):
+ files_in_log[file_name] = base_addr
+
+ for file_name in files_in_log:
+ efi_file = self.find_file(file_name)
+
+ if not efi_file:
+ print('File ' + file_name + ' not found')
+ continue
+
+ debug_file = efi_file[:-3] + 'debug'
+
+ if not os.path.isfile(debug_file):
+ print('No debug file for ' + efi_file)
+ continue
+
+ print('EFI file ' + efi_file)
+
+ if efi_file and debug_file:
+ text_addr, data_addr = self.get_addresses(efi_file)
+
+ if not text_addr or not data_addr:
+ continue
+
+ base_addr = files_in_log[file_name]
+
+ prev_used = used_addresses.get(base_addr)
+
+ if prev_used:
+ print('WARNING: duplicate base address ' + base_addr)
+ print('(was previously provided for ' + prev_used + ')')
+ print('Only new file will be loaded')
+ del load_addresses[prev_used]
+ else:
+ used_addresses[base_addr] = debug_file
+
+ load_addresses[debug_file] = (
+ update_addresses(base_addr,
+ text_addr,
+ data_addr))
+
+ if load_addresses:
+ for debug_file in load_addresses:
+ add_symbol_file(debug_file, *load_addresses[debug_file])
+ else:
+ print('No symbols loaded')
+
+ if pagination == 'on':
+ print('Restoring pagination')
+ gdb.execute('set pagination on')
+
+ if arg and '-r' in arg.split():
+ remote_connect()
+
+
+Command_efi()
diff --git a/scripts/run_gdb.sh b/scripts/run_gdb.sh
new file mode 100755
index 0000000..a5ba6af
--- /dev/null
+++ b/scripts/run_gdb.sh
@@ -0,0 +1,144 @@
+#!/bin/bash
+
+##### Controllable parameters #####
+QEMU_SHARED_FOLDER=~/UEFI_disk
+PACKAGE=UefiLessonsPkg
+###################################
+
+function show_help {
+ echo "Description:"
+ echo " run_gdb.sh is a script that helps to debug UEFI shell applications and drivers"
+ echo ""
+ echo "Usage: run_gdb.sh [-1|-f] -m <module>"
+ echo " -1 This is a first run of this configuration"
+ echo " (in this case before main gdb launch there would be another QEMU start that will create 'debug.log' file)"
+ echo " -f Load all debug symbols"
+ echo " (this will load all OVMF debug symbols - with this you could step inside OVMF functions)"
+ echo " -m <module> UEFI module to debug"
+ echo " -p <package> UEFI package to debug"
+ echo " (by default it is equal to PACKAGE variable in the head of the script)"
+ echo " -q <dir> QEMU shared directory"
+ echo " (by default it is equal to QEMU_SHARED_FOLDER variable in the head of the script)"
+ echo ""
+ echo "Examples:"
+ echo " run_gdb.sh -1 -m MyApp - create 'debug.log' file with the necessary address information for the 'MyApp'"
+ echo " and debug it with gdb"
+ echo " run_gdb.sh -1 -m MyApp -f - create 'debug.log' file with the necessary address information for the 'MyApp'"
+ echo " and debug it with gdb (all debug symbols are included, i.e. you can step into OVMF functions)"
+ echo " run_gdb.sh -m MyApp - debug 'MyApp' with gdb ('debug.log' was created in the last run, no need to remake it again)"
+}
+
+
+# A POSIX variable
+OPTIND=1 # Reset in case getopts has been used previously in the shell.
+
+while getopts "h?1fm:q:p:" opt; do
+ case "$opt" in
+ h|\?)
+ show_help
+ exit 0
+ ;;
+ 1) FIRST_RUN=1
+ ;;
+ f) FULL=1
+ ;;
+ m) TARGET=$OPTARG
+ ;;
+ q) QEMU_SHARED_FOLDER=$OPTARG
+ ;;
+ p) PACKAGE=$OPTARG
+ ;;
+ esac
+done
+
+shift $((OPTIND-1))
+
+[ "${1:-}" = "--" ] && shift
+
+
+
+if [[ ! -z "${FULL}" ]]; then
+ DRIVERS=''
+else
+ DRIVERS=${TARGET}
+fi
+
+
+function test_file {
+ FILE_NAME=$1
+ if [[ ! -f ${FILE_NAME} ]]; then
+ echo "Error! There is no file ${FILE_NAME}"
+ exit 1;
+ fi
+}
+
+TARGET_INF="${PACKAGE}/${TARGET}/${TARGET}.inf"
+TARGET_C="${PACKAGE}/${TARGET}/${TARGET}.c"
+
+OVMF="Build/OvmfX64/DEBUG_GCC5/FV/OVMF.fd"
+
+test_file "${TARGET_INF}"
+test_file "${TARGET_C}"
+test_file "${OVMF}"
+
+ENTRY_POINT_NAME=$(grep ENTRY_POINT ${TARGET_INF} | cut -f 2 -d "=")
+if [ ${ENTRY_POINT_NAME} == "ShellCEntryLib" ]; then
+ ENTRY_POINT_NAME="ShellAppMain"
+fi
+ENTRY_POINT_LINE=$(grep -n ${ENTRY_POINT_NAME} ${TARGET_C} | cut -f 1 -d ":")
+
+MODULE_TYPE=$(grep MODULE_TYPE ${TARGET_INF} | cut -f 2 -d "=")
+if [ ${MODULE_TYPE} == "UEFI_DRIVER" ]; then
+ LAUNCH_COMMAND="load fs0:${TARGET}.efi"
+else
+ LAUNCH_COMMAND="fs0:${TARGET}.efi"
+fi
+
+if [[ ! -z "${FIRST_RUN}" || ! -f debug.log ]]; then
+ touch debug.log
+ # If it is a first run, we need to create 'debug.log' file for addresses
+ TARGET_EFI="Build/${PACKAGE}/DEBUG_GCC5/X64/${TARGET}.efi"
+ test_file "${TARGET_EFI}"
+ cp ${TARGET_EFI} ${QEMU_SHARED_FOLDER}
+ tmux new-session \; \
+ send-keys "tail -f debug.log" Enter \; \
+ split-window -v \; \
+ send-keys "qemu-system-x86_64 \
+ -drive if=pflash,format=raw,readonly,file=${OVMF} \
+ -drive format=raw,file=fat:rw:${QEMU_SHARED_FOLDER} \
+ -net none \
+ -nographic \
+ -global isa-debugcon.iobase=0x402 \
+ -debugcon file:debug.log \
+ -s" C-m Enter \; \
+ send-keys C-m Enter \; \
+ send-keys "${LAUNCH_COMMAND}" Enter \;
+fi
+
+
+test_file "${QEMU_SHARED_FOLDER}/${TARGET}.efi"
+touch debug_temp.log
+tmux new-session \; \
+ send-keys "gdb -ex 'source efi.py' -tui" Enter \; \
+ split-window -h \; \
+ send-keys "tail -f debug_temp.log" Enter \; \
+ split-window -v \; \
+ send-keys "qemu-system-x86_64 \
+ -drive if=pflash,format=raw,readonly,file=${OVMF} \
+ -drive format=raw,file=fat:rw:${QEMU_SHARED_FOLDER} \
+ -net none \
+ -nographic \
+ -global isa-debugcon.iobase=0x402 \
+ -debugcon file:debug_temp.log \
+ -s" C-m Enter \; \
+ select-pane -t 0 \; \
+ send-keys "efi -64 ${DRIVERS}" Enter \; \
+ send-keys "b ${TARGET_C}:${ENTRY_POINT_LINE}" Enter \; \
+ send-keys Enter \; \
+ send-keys "target remote :1234" Enter \; \
+ send-keys "c" Enter \; \
+ select-pane -t 2 \; \
+ send-keys C-m Enter \; \
+ send-keys "${LAUNCH_COMMAND}" Enter \;
+
+