Script to debug memtest86plus (efi-version) with GDB in QEMU (#177)

* Add HOW_TO_DEBUG_WITH_GDB file
* Add debug_memtest.sh
* Add debug target to Makefile
* Add binaries and generated files for debugging to gitignore
* Add DEBUG code for memset and memcpy
* Add debug additions to Makefile in build32
* Add debug_memtest.sh to build32

[Lionel Debroux: post-merge fixups: removed a couple whitespace changes and a backup file; undone Github squash merge authorship damage.]
This commit is contained in:
Regina König 2023-07-30 21:53:18 +02:00 committed by Lionel Debroux
parent df803bf294
commit bcf8171593
8 changed files with 659 additions and 24 deletions

8
.gitignore vendored
View File

@ -7,8 +7,10 @@
# Object files
*.o
# Generated file
# Generated files
build_version.h
gdbscript
memtest_shared_debug.lds
# Binaries
memtest_shared
@ -18,10 +20,14 @@ memtest_shared.bin
*.img
*.iso
*.mbr
*.debug
# Directories
grub-iso
html
latex
# OVMF
OVMF*

97
HOW_TO_DEBUG_WITH_GDB.md Normal file
View File

@ -0,0 +1,97 @@
# How to debug memtest.efi in QEMU with GDB
`debug_memtest.sh` is a script that allows memtest86plus developers to set up a debugging environment with GDB in QEMU.
It calls the `make debug` target of a modified Makefile to create an additional debug-symbol file called `memtest.debug`.
The symbols of this file are loaded with an offset (in accordance with the loading location of the efi-image) into GDB to match the exact addresses of the symbols in memory.
## Prerequisites
* this approach was tested on Ubuntu 18.04 - 22.04
* the debug script is created only for the efi 64-bit version
* qemu-system-x86_64 and ovmf must be installed
## How to run
* navigate to build64 directory
* run `./debug_memtest.sh`
* or type `./debug_memtest.sh -h` for help
### Remarks - create own gdbscript
It is possible to provide an own gdb-script. Name it 'gdbscript' and place it in the build64 directory.
This script will automatically be loaded when gdb starts.
!! But be careful when cleaning the directory by './debug_memtest.sh -c'. It also removes 'gdbscript'.
!! Make sure that you have made a copy of 'gdbscript' when running this command.
## Navigate inside Qemu/UEFI
* wait until UEFI-Shell is loaded
* type "fs0:" - Enter
* type "memtest.efi" - Enter
### Inside GDB
When GDB is running, it stops at the first breakpoint at main(). Feel free to add further breakpoints or continue with `c`.
### Remarks - auto-boot memtest86+
In step **Navigate inside QEMU/UEFI**, you have to navigate to the directory which contains memtest.efi and manually launch it.
If you want to automatically boot from memtest.efi, there is an additional step required to add memtest to the first place at the bootorder:
When the UEFI Shell is running, type
`bcfg boot add 0 FS0:\EFI\boot\BOOT_X64.efi "memtest"`
and confirm with Enter.
The directory "\EFI\boot" and the file "BOOT_X64.efi" are automatically
created by the debug-script.
When you run the script the next time, memtest.efi should run without
previous user interaction.
!! But be careful when cleaning the directory by './debug_memtest.sh -c'. It also removes this setting.
!! Make sure that you have made a copy of 'OVMF*'-files when running this command.
## Clean directory
'debug_memtest.sh' has an own clean procedure which cleans additional files not mentioned in Makefile's
'make clean' target. When you run this command, make sure that you have saved 'gdbscript' and/or OVMF* files if there are custom changes.
To clean the directory, type `./debug_memtest.sh -c`
## Possible features/alternatives and further considerations
### Detection of Image Base
To assign the correct address for all debugging symbols, it is neccessary to add an offset to the values in memtest.debug (the file containing the debug symbols). This offset consists of the IMAGE_BASE and the BASE_OF_CODE.
Both values are defined in `memtest86plus/boot/header.S`
* IMMUTABILITY OF ALL CONDITIONS
if you assume, that these values will never change during the development phase of memtest86plus AND memtest.efi is always loaded at this preferred address in qemu-system-x86_64 (which seems to be the case) then it is possible to hardcode the offset in the script (for the implementation see debug_memtest_simple.sh)
* ADAPTABILITY TO DEVELOPMENT CHANGES
if there is a chance, that these values WILL change during the development phase but memtest.efi is always loaded at this preferred address then the value can be read from header.S by the debug script just right before starting the debugging (for an example, see debug_memtest_full.sh)
* EXPECTED ERRATIC BEHAVIOUR OF QEMU
If it is expected that memtest.efi is NOT always loaded at the same address, it is inevitable to determine the actual loading address first. This approach comprises a DEBUG-build of OVMF.fd (which requires the cloning of the whole edk2-repository and manually build OVMF.fd). With this DEBUG-version of OVMF.fd it is possible to write the loading addresses of all modules into a debug.log.
This proceeding has been tested successfully but is actually not implemented in one of the srcipts.
### Handle relocation of memtest
memtest86plus relocates itself during the test procedures. As the script loads the symbol table with a given offset, debugging is only possible when the code is located at the original position. There are several ways to deal with relocation:
* IGNORE RELOCATION
Just ignore the fact that at a part of the time the symbols are not recognized by gdb as gdb has no information about the symbols, when memtest86plus has been relocated. It is still possible to debug the code since memtest86plus jumps sooner or later back to the original position and all precedures which are executed at one location are also executed at the other position.
BUT: If a bug is position-dependent (i.e. it occurs only at the relocated position), you are not able to debug it.
* DISABLE RELOCATION
TODO: Is it possible to deactivate relocation? E.g. by outcommenting some code or setting a flag? Does it have benefits over the first approach?
* FOLLOW RELOCATION
If the position after relocation is expected to be always the same, then you can just load the symbol table twice. This is done in debug_memtest_simple.sh (the offsets are 0x201000 and 0x400000). If the locations can vary then the offsets must be determined dynamically ... todo: how?

View File

@ -13,6 +13,17 @@ endif
CFLAGS = -std=c11 -Wall -Wextra -Wshadow -m32 -march=i586 -fpic -fno-builtin \
-ffreestanding -fomit-frame-pointer -fno-stack-protector
ifeq ($(DEBUG), 1)
CFLAGS+=-ggdb3 -DDEBUG_GDB
OPT_SMALL=-Og
OPT_FAST=-Og
MS_LDS=memtest_shared_debug.lds
else
OPT_SMALL=-Os
OPT_FAST=-O3
MS_LDS=ldscripts/memtest_shared.lds
endif
INC_DIRS = -I../boot -I../system -I../lib -I../tests -I../app -Iapp
SYS_OBJS = system/acpi.o \
@ -74,6 +85,14 @@ OBJS = boot/startup.o boot/efisetup.o $(SYS_OBJS) $(IMC_OBJS) $(LIB_OBJS) $(TST_
all: memtest.bin memtest.efi
check:
@if [ -z ${DEBUG} ]; then\
echo "Macro DEBUG is not defined. Run debug_memtest.sh to invoke debug target";\
exit 1;\
fi
debug: check memtest.debug memtest.efi
-include boot/efisetup.d
-include $(subst .o,.d,$(SYS_OBJS))
-include $(subst .o,.d,$(IMC_OBJS))
@ -93,31 +112,31 @@ boot/%.o: ../boot/%.S ../boot/boot.h app/build_version.h
boot/efisetup.o: ../boot/efisetup.c
@mkdir -p boot
$(CC) -c $(CFLAGS) -Os $(INC_DIRS) -o $@ $< -MMD -MP -MT $@ -MF $(@:.o=.d)
$(CC) -c $(CFLAGS) $(OPT_SMALL) $(INC_DIRS) -o $@ $< -MMD -MP -MT $@ -MF $(@:.o=.d)
system/reloc.o: ../system/reloc32.c
@mkdir -p system
$(CC) -c $(CFLAGS) -fno-strict-aliasing -Os $(INC_DIRS) -o $@ $< -MMD -MP -MT $@ -MF $(@:.o=.d)
$(CC) -c $(CFLAGS) -fno-strict-aliasing $(OPT_SMALL) $(INC_DIRS) -o $@ $< -MMD -MP -MT $@ -MF $(@:.o=.d)
system/%.o: ../system/%.c
@mkdir -p system
$(CC) -c $(CFLAGS) -Os $(INC_DIRS) -o $@ $< -MMD -MP -MT $@ -MF $(@:.o=.d)
$(CC) -c $(CFLAGS) $(OPT_SMALL) $(INC_DIRS) -o $@ $< -MMD -MP -MT $@ -MF $(@:.o=.d)
system/imc/%.o: ../system/imc/%.c
@mkdir -p system/imc
$(CC) -c $(CFLAGS) -Os $(INC_DIRS) -o $@ $< -MMD -MP -MT $@ -MF $(@:.o=.d)
$(CC) -c $(CFLAGS) $(OPT_SMALL) $(INC_DIRS) -o $@ $< -MMD -MP -MT $@ -MF $(@:.o=.d)
lib/%.o: ../lib/%.c
@mkdir -p lib
$(CC) -c $(CFLAGS) -Os $(INC_DIRS) -o $@ $< -MMD -MP -MT $@ -MF $(@:.o=.d)
$(CC) -c $(CFLAGS) $(OPT_SMALL) $(INC_DIRS) -o $@ $< -MMD -MP -MT $@ -MF $(@:.o=.d)
tests/%.o: ../tests/%.c
@mkdir -p tests
$(CC) -c $(CFLAGS) -O3 $(INC_DIRS) -o $@ $< -MMD -MP -MT $@ -MF $(@:.o=.d)
$(CC) -c $(CFLAGS) $(OPT_FAST) $(INC_DIRS) -o $@ $< -MMD -MP -MT $@ -MF $(@:.o=.d)
app/%.o: ../app/%.c app/build_version.h
@mkdir -p app
$(CC) -c $(CFLAGS) -Os $(INC_DIRS) -o $@ $< -MMD -MP -MT $@ -MF $(@:.o=.d)
$(CC) -c $(CFLAGS) $(OPT_SMALL) $(INC_DIRS) -o $@ $< -MMD -MP -MT $@ -MF $(@:.o=.d)
app/build_version.h: FORCE
@mkdir -p app
@ -138,13 +157,18 @@ FORCE:
# Link it statically once so I know I don't have undefined symbols and
# then link it dynamically so I have full relocation information.
memtest_shared: $(OBJS) ldscripts/memtest_shared.lds Makefile
$(LD) --warn-constructors --warn-common -static -T ldscripts/memtest_shared.lds -o $@ $(OBJS) && \
$(LD) -shared -Bsymbolic -T ldscripts/memtest_shared.lds -o $@ $(OBJS)
memtest_shared: $(OBJS) $(MS_LDS) Makefile
$(LD) --warn-constructors --warn-common -static -T $(MS_LDS) -o $@ $(OBJS) && \
$(LD) -shared -Bsymbolic -T $(MS_LDS) -o $@ $(OBJS)
memtest_shared.bin: memtest_shared
$(OBJCOPY) -O binary $< memtest_shared.bin
memtest.debug: memtest_shared
objcopy --only-keep-debug memtest_shared memtest.debug
strip -R .eh_frame memtest_shared
strip -R .comment memtest_shared
memtest.bin: memtest_shared.bin boot/bootsect.o boot/setup.o ldscripts/memtest_bin.lds
$(eval SIZES=$(shell size -B -d memtest_shared | grep memtest_shared))
$(LD) --defsym=_bss_size=$(word 3,$(SIZES)) -T ldscripts/memtest_bin.lds boot/bootsect.o boot/setup.o -b binary memtest_shared.bin -o memtest.bin

226
build32/debug_memtest.sh Executable file
View File

@ -0,0 +1,226 @@
#! /bin/bash
###############################################################################
#
# Description : `debug_memtest.sh` is a script that allows memtest86plus
# developers to set up a debugging environment with GDB in QEMU. It calls
# the `make debug` target of a modified Makefile to create an additional
# debug-symbol file called `memtest.debug`. The symbols of this file are
# loaded with an offset (in accordance with the loading location of the
# efi-image) into GDB to match the exact addresses of the symbols in memory.
#
# For more detailed information, please read 'HOW_TO_DEBUG_WITH_GDB'
#
# Author : Regina König
# Year : 2022
# Email : koenig_regina@arcor.de
#
##############################################################################
Help() {
echo "Syntax: $0 [-h|-c|-t <terminal><execution_command>]"
echo "options:"
echo " -h Print this help"
echo " -c Delete all debugging related files"
echo " -t Define an alternative terminal"
echo " You can define your own terminal inclusive its execution command via:"
echo " ./debug_script.sh -t \"<terminal> <execution_command> \""
echo " See following examples:"
echo " ./debug_script.sh -t \"x-terminal-emulator -e \""
echo " ./debug_script.sh -t \"gnome-terminal -- \""
echo " ./debug_script.sh -t \"xterm -e \""
}
Clear() {
echo "Deleting files..."
rm -rf hda-contents
rm -f debug.log
rm -f gdbscript
rm -f QemuKill
make clean
rm -f OVMF32_VARS.fd
rm -f OVMF32_CODE.fd
rm -f memtest_shared_debug.lds
}
while getopts ":hct:" option; do
case $option in
h) # display Help
Help
exit;;
c) # clear directory
Clear
exit;;
t) # define own terminal
TERMINAL="$OPTARG"
if ! $TERMINAL ls; then
echo "Your entered command is not valid. Please check it again"
echo "Or type \"./debug_memtest.sh -h\" for help"
exit 1
fi
exit;;
\?) # invalid option
echo "Error: Invalid option"
echo "Type $0 -h for more information"
exit;;
esac
done
Check() {
# Check if QEMU and OVMF are installed
if ! command -v qemu-system-i386 > /dev/null 2>&1; then
echo "Qemu not installed"
exit 1
fi
# Check for presence of OVMF32_VARS.fd and OVMF32_CODE.fd
if [ ! -f OVMF32_CODE.fd ] && [ ! -f /usr/share/OVMF/OVMF32_CODE.fd ]; then
echo "Package ovmf-ia32 not installed. Type 'sudo apt install ovmf-ia32' and create some symlinks."
echo "Or copy your own versions of OVMF32_VARS.fd and OVMF32_CODE.fd into this directory"
exit 1
fi
# Check if gdb is installed
if ! command -v gdb > /dev/null 2>&1; then
echo "GDB not installed"
exit 1
fi
# Check for various terminals. Do not define TERMINAL if already defined by commandline
if [ -z $TERMINAL ]; then
if command -v x-terminal-emulator &> /dev/null; then
echo "x-terminal-emulator found"
TERMINAL="x-terminal-emulator -e "
elif command -v gnome-terminal &> /dev/null; then
echo "gnome-terminal found"
TERMINAL="gnome-terminal -- "
elif command -v xterm &> /dev/null; then
echo "xterm found"
TERMINAL="xterm -e "
else
echo "No terminal recognized. Please install x-terminal-emulator or gnome-terminal or xterm."
echo "Or define your own terminal alternatively."
echo "Type ./debug_memtest.sh -h for more information"
exit 1
fi
fi
}
Make() {
make debug DEBUG=1
ret_val=$?
if [[ $ret_val -ne 0 ]] ; then
echo "Make failed with return value: $ret_val"
exit 1
fi
}
# Retrieve addresses from code (not used in this version)
# Get_Offsets() {
# IMAGEBASE=$(grep -P '#define\tIMAGE_BASE' header.S | cut -f3)
# BASEOFCODE=$(grep -P '#define\tBASE_OF_CODE' header.S | cut -f3)
# TODO: get RELOCADDR and DATA
# }
Init() {
QEMU="qemu-system-i386"
QEMU_FLAGS+=" -hda fat:rw:hda-contents -net none"
QEMU_FLAGS+=" -drive if=pflash,format=raw,readonly=on,file=OVMF32_CODE.fd"
QEMU_FLAGS+=" -drive if=pflash,format=raw,file=OVMF32_VARS.fd"
# Define offsets for loading of symbol-table
IMAGEBASE=0x200000
BASEOFCODE=0x1000
DATA=0x23000
RELOCADDR=0x400000
printf -v OFFSET "0x%X" $(($IMAGEBASE + $BASEOFCODE))
printf -v DATAOFFSET "0x%X" $(($IMAGEBASE + $BASEOFCODE + $DATA))
printf -v RELOCDATA "0x%X" $(($RELOCADDR + $DATA))
GDB_FILE="gdbscript"
# Check if gdbscript exists. If not, create one.
if [ ! -f $GDB_FILE ]
then
echo "Creating gdbscript.."
echo "set pagination off" > $GDB_FILE
if [ -z "$OFFSET" ] || [ -z "$RELOCADDR" ]; then
echo "OFFSET or RELOCADDR is not set."
exit 1
fi
echo "add-symbol-file memtest.debug $OFFSET -s .data $DATAOFFSET" >> $GDB_FILE
echo "add-symbol-file memtest.debug $RELOCADDR -s .data $RELOCDATA" >> $GDB_FILE
echo "b main" >> $GDB_FILE
echo "commands" >> $GDB_FILE
echo "layout src" >> $GDB_FILE
echo "delete 1" >> $GDB_FILE
echo "end" >> $GDB_FILE
echo "b run_at" >> $GDB_FILE
echo "shell sleep 0.5" >> $GDB_FILE
echo "target remote localhost:1234" >> $GDB_FILE
echo "info b" >> $GDB_FILE
echo "c" >> $GDB_FILE
fi
if [ ! -f ldscripts/memtest_shared.lds ]; then
echo "'memtest_shared.lds' does not exist."
exit 1
fi
sed '/DISCARD/d' ldscripts/memtest_shared.lds > memtest_shared_debug.lds
if [ ! -f memtest_shared_debug.lds ]; then
echo "Creation of 'memtest_shared_debug.lds' failed."
exit 1
fi
}
Prepare_Directory() {
# Create dir hda-contents and a boot directory
mkdir -p hda-contents/EFI/boot
if [ ! -d hda-contents ]; then
echo "Creation of directory hda-contents failed."
exit 1
fi
# Copy memtest.efi to hda-contents
cp memtest.efi hda-contents/
cp memtest.efi hda-contents/EFI/boot/BOOT_IA32.efi
# Copy OVMF* files from /usr/share
if [ ! -f OVMF32_VARS.fd ] || [ ! -f OVMF32_CODE.fd ]; then
cp /usr/share/OVMF/OVMF32_CODE.fd .
cp /usr/share/OVMF/OVMF32_VARS.fd .
fi
}
# Global checks
Check
# Initialize
Init
# Build
Make
# Create needed directories and move efi binary to appropriate location
Prepare_Directory
# Run QEMU and launch second terminal,
# wait for connection via gdb
$TERMINAL gdb -x $GDB_FILE &
$QEMU $QEMU_FLAGS -s -S

View File

@ -13,6 +13,17 @@ endif
CFLAGS = -std=c11 -Wall -Wextra -Wshadow -m64 -march=x86-64 -mno-mmx -mno-sse -mno-sse2 \
-fpic -fno-builtin -ffreestanding -fomit-frame-pointer -fno-stack-protector
ifeq ($(DEBUG), 1)
CFLAGS+=-ggdb3 -DDEBUG_GDB
OPT_SMALL=-Og
OPT_FAST=-Og
MS_LDS=memtest_shared_debug.lds
else
OPT_SMALL=-Os
OPT_FAST=-O3
MS_LDS=ldscripts/memtest_shared.lds
endif
INC_DIRS = -I../boot -I../system -I../lib -I../tests -I../app -Iapp
SYS_OBJS = system/acpi.o \
@ -73,6 +84,14 @@ OBJS = boot/startup.o boot/efisetup.o $(SYS_OBJS) $(IMC_OBJS) $(LIB_OBJS) $(TST_
all: memtest.bin memtest.efi
check:
@if [ -z ${DEBUG} ]; then\
echo "Macro DEBUG is not defined. Run debug_memtest.sh to invoke debug target";\
exit 1;\
fi
debug: check memtest.debug memtest.efi
-include boot/efisetup.d
-include $(subst .o,.d,$(SYS_OBJS))
-include $(subst .o,.d,$(IMC_OBJS))
@ -92,31 +111,31 @@ boot/%.o: ../boot/%.S ../boot/boot.h app/build_version.h
boot/efisetup.o: ../boot/efisetup.c
@mkdir -p boot
$(CC) -c $(CFLAGS) -Os $(INC_DIRS) -o $@ $< -MMD -MP -MT $@ -MF $(@:.o=.d)
$(CC) -c $(CFLAGS) $(OPT_SMALL) $(INC_DIRS) -o $@ $< -MMD -MP -MT $@ -MF $(@:.o=.d)
system/reloc.o: ../system/reloc64.c
@mkdir -p system
$(CC) -c $(CFLAGS) -fno-strict-aliasing -Os $(INC_DIRS) -o $@ $< -MMD -MP -MT $@ -MF $(@:.o=.d)
$(CC) -c $(CFLAGS) -fno-strict-aliasing $(OPT_SMALL) $(INC_DIRS) -o $@ $< -MMD -MP -MT $@ -MF $(@:.o=.d)
system/%.o: ../system/%.c
@mkdir -p system
$(CC) -c $(CFLAGS) -Os $(INC_DIRS) -o $@ $< -MMD -MP -MT $@ -MF $(@:.o=.d)
$(CC) -c $(CFLAGS) $(OPT_SMALL) $(INC_DIRS) -o $@ $< -MMD -MP -MT $@ -MF $(@:.o=.d)
system/imc/%.o: ../system/imc/%.c
@mkdir -p system/imc
$(CC) -c $(CFLAGS) -Os $(INC_DIRS) -o $@ $< -MMD -MP -MT $@ -MF $(@:.o=.d)
$(CC) -c $(CFLAGS) $(OPT_SMALL) $(INC_DIRS) -o $@ $< -MMD -MP -MT $@ -MF $(@:.o=.d)
lib/%.o: ../lib/%.c
@mkdir -p lib
$(CC) -c $(CFLAGS) -Os $(INC_DIRS) -o $@ $< -MMD -MP -MT $@ -MF $(@:.o=.d)
$(CC) -c $(CFLAGS) $(OPT_SMALL) $(INC_DIRS) -o $@ $< -MMD -MP -MT $@ -MF $(@:.o=.d)
tests/%.o: ../tests/%.c
@mkdir -p tests
$(CC) -c $(CFLAGS) -O3 $(INC_DIRS) -o $@ $< -MMD -MP -MT $@ -MF $(@:.o=.d)
$(CC) -c $(CFLAGS) $(OPT_FAST) $(INC_DIRS) -o $@ $< -MMD -MP -MT $@ -MF $(@:.o=.d)
app/%.o: ../app/%.c app/build_version.h
@mkdir -p app
$(CC) -c $(CFLAGS) -Os $(INC_DIRS) -o $@ $< -MMD -MP -MT $@ -MF $(@:.o=.d)
$(CC) -c $(CFLAGS) $(OPT_SMALL) $(INC_DIRS) -o $@ $< -MMD -MP -MT $@ -MF $(@:.o=.d)
app/build_version.h: FORCE
@mkdir -p app
@ -137,13 +156,18 @@ FORCE:
# Link it statically once so I know I don't have undefined symbols and
# then link it dynamically so I have full relocation information.
memtest_shared: $(OBJS) ldscripts/memtest_shared.lds Makefile
$(LD) --warn-constructors --warn-common -static -T ldscripts/memtest_shared.lds -o $@ $(OBJS) && \
$(LD) -shared -Bsymbolic -T ldscripts/memtest_shared.lds -o $@ $(OBJS)
memtest_shared: $(OBJS) $(MS_LDS) Makefile
$(LD) --warn-constructors --warn-common -static -T $(MS_LDS) -o $@ $(OBJS) && \
$(LD) -shared -Bsymbolic -T $(MS_LDS) -o $@ $(OBJS)
memtest_shared.bin: memtest_shared
$(OBJCOPY) -O binary $< memtest_shared.bin
memtest.debug: memtest_shared
objcopy --only-keep-debug memtest_shared memtest.debug
strip -R .eh_frame memtest_shared
strip -R .comment memtest_shared
memtest.bin: memtest_shared.bin boot/bootsect.o boot/setup.o ldscripts/memtest_bin.lds
$(eval SIZES=$(shell size -B -d memtest_shared | grep memtest_shared))
$(LD) --defsym=_bss_size=$(word 3,$(SIZES)) -T ldscripts/memtest_bin.lds boot/bootsect.o boot/setup.o -b binary memtest_shared.bin -o memtest.bin

229
build64/debug_memtest.sh Executable file
View File

@ -0,0 +1,229 @@
#! /bin/bash
###############################################################################
#
# Description : `debug_memtest.sh` is a script that allows memtest86plus
# developers to set up a debugging environment with GDB in QEMU. It calls
# the `make debug` target of a modified Makefile to create an additional
# debug-symbol file called `memtest.debug`. The symbols of this file are
# loaded with an offset (in accordance with the loading location of the
# efi-image) into GDB to match the exact addresses of the symbols in memory.
#
# For more detailed information, please read 'HOW_TO_DEBUG_WITH_GDB'
#
# Author : Regina König
# Year : 2022
# Email : koenig_regina@arcor.de
#
##############################################################################
Help() {
echo "Syntax: $0 [-h|-c|-t <terminal><execution_command>]"
echo "options:"
echo " -h Print this help"
echo " -c Delete all debugging related files"
echo " -t Define an alternative terminal"
echo " You can define your own terminal inclusive its execution command via:"
echo " ./debug_script.sh -t \"<terminal> <execution_command> \""
echo " See following examples:"
echo " ./debug_script.sh -t \"x-terminal-emulator -e \""
echo " ./debug_script.sh -t \"gnome-terminal -- \""
echo " ./debug_script.sh -t \"xterm -e \""
}
Clear() {
echo "Deleting files..."
rm -rf hda-contents
rm -f debug.log
rm -f gdbscript
rm -f QemuKill
make clean
rm -f OVMF.fd
rm -f OVMF_VARS.fd
rm -f OVMF_CODE.fd
rm -f memtest_shared_debug.lds
}
while getopts ":hct:" option; do
case $option in
h) # display Help
Help
exit;;
c) # clear directory
Clear
exit;;
t) # define own terminal
TERMINAL="$OPTARG"
if ! $TERMINAL ls; then
echo "Your entered command is not valid. Please check it again"
echo "Or type \"./debug_memtest.sh -h\" for help"
exit 1
fi
exit;;
\?) # invalid option
echo "Error: Invalid option"
echo "Type $0 -h for more information"
exit;;
esac
done
Check() {
# Check if QEMU and OVMF are installed
if ! command -v qemu-system-x86_64 > /dev/null 2>&1; then
echo "Qemu not installed"
exit 1
fi
# Check for presence of OVMF.fd, OVMF_VARS.fd and OVMF_CODE.fd
if [ ! -f OVMF.fd ] && [ ! -f /usr/share/ovmf/OVMF.fd ]; then
echo "Package ovmf not installed. Type 'sudo apt install ovmf'."
echo "Or copy your own versions of OVMF.fd, OVMF_VARS.fd and OVMF_CODE.fd into this directory"
exit 1
fi
# Check if gdb is installed
if ! command -v gdb > /dev/null 2>&1; then
echo "GDB not installed"
exit 1
fi
# Check for various terminals. Do not define TERMINAL if already defined by commandline
if [ -z $TERMINAL ]; then
if command -v x-terminal-emulator &> /dev/null; then
echo "x-terminal-emulator found"
TERMINAL="x-terminal-emulator -e "
elif command -v gnome-terminal &> /dev/null; then
echo "gnome-terminal found"
TERMINAL="gnome-terminal -- "
elif command -v xterm &> /dev/null; then
echo "xterm found"
TERMINAL="xterm -e "
else
echo "No terminal recognized. Please install x-terminal-emulator or gnome-terminal or xterm."
echo "Or define your own terminal alternatively."
echo "Type ./debug_memtest.sh -h for more information"
exit 1
fi
fi
}
Make() {
make debug DEBUG=1
ret_val=$?
if [[ $ret_val -ne 0 ]] ; then
echo "Make failed with return value: $ret_val"
exit 1
fi
}
# Retrieve addresses from code (not used in this version)
# Get_Offsets() {
# IMAGEBASE=$(grep -P '#define\tIMAGE_BASE' header.S | cut -f3)
# BASEOFCODE=$(grep -P '#define\tBASE_OF_CODE' header.S | cut -f3)
# TODO: get RELOCADDR and DATA
# }
Init() {
QEMU="qemu-system-x86_64"
QEMU_FLAGS=" -bios OVMF.fd"
QEMU_FLAGS+=" -hda fat:rw:hda-contents -net none"
QEMU_FLAGS+=" -drive if=pflash,format=raw,readonly=on,file=OVMF_CODE.fd"
QEMU_FLAGS+=" -drive if=pflash,format=raw,file=OVMF_VARS.fd"
# Define offsets for loading of symbol-table
IMAGEBASE=0x200000
BASEOFCODE=0x1000
DATA=0x23000
RELOCADDR=0x400000
printf -v OFFSET "0x%X" $(($IMAGEBASE + $BASEOFCODE))
printf -v DATAOFFSET "0x%X" $(($IMAGEBASE + $BASEOFCODE + $DATA))
printf -v RELOCDATA "0x%X" $(($RELOCADDR + $DATA))
GDB_FILE="gdbscript"
# Check if gdbscript exists. If not, create one.
if [ ! -f $GDB_FILE ]
then
echo "Creating gdbscript.."
echo "set pagination off" > $GDB_FILE
if [ -z "$OFFSET" ] || [ -z "$RELOCADDR" ]; then
echo "OFFSET or RELOCADDR is not set."
exit 1
fi
echo "add-symbol-file memtest.debug $OFFSET -s .data $DATAOFFSET" >> $GDB_FILE
echo "add-symbol-file memtest.debug $RELOCADDR -s .data $RELOCDATA" >> $GDB_FILE
echo "b main" >> $GDB_FILE
echo "commands" >> $GDB_FILE
echo "layout src" >> $GDB_FILE
echo "delete 1" >> $GDB_FILE
echo "end" >> $GDB_FILE
echo "b run_at" >> $GDB_FILE
echo "shell sleep 0.5" >> $GDB_FILE
echo "target remote localhost:1234" >> $GDB_FILE
echo "info b" >> $GDB_FILE
echo "c" >> $GDB_FILE
fi
if [ ! -f ldscripts/memtest_shared.lds ]; then
echo "'memtest_shared.lds' does not exist."
exit 1
fi
sed '/DISCARD/d' ldscripts/memtest_shared.lds > memtest_shared_debug.lds
if [ ! -f memtest_shared_debug.lds ]; then
echo "Creation of 'memtest_shared_debug.lds' failed."
exit 1
fi
}
Prepare_Directory() {
# Create dir hda-contents and a boot directory
mkdir -p hda-contents/EFI/boot
if [ ! -d hda-contents ]; then
echo "Creation of directory hda-contents failed."
exit 1
fi
# Copy memtest.efi to hda-contents
cp memtest.efi hda-contents/
cp memtest.efi hda-contents/EFI/boot/BOOT_X64.efi
# Copy OVMF* files from /usr/share
if [ ! -f OVMF.fd ] || [ ! -f OVMF_VARS.fd ] || [ ! -f OVMF_CODE.fd ]; then
cp /usr/share/ovmf/OVMF.fd .
cp /usr/share/OVMF/OVMF_CODE.fd .
cp /usr/share/OVMF/OVMF_VARS.fd .
fi
}
# Global checks
Check
# Initialize
Init
# Build
Make
# Create needed directories and move efi binary to appropriate location
Prepare_Directory
# Run QEMU and launch second terminal,
# wait for connection via gdb
$TERMINAL gdb -x $GDB_FILE &
$QEMU $QEMU_FLAGS -s -S

View File

@ -53,6 +53,27 @@ void *memmove(void *dest, const void *src, size_t n)
return dest;
}
#ifdef DEBUG_GDB
void *memcpy (void *dest, const void *src, size_t len)
{
char *d = dest;
const char *s = src;
while (len--)
*d++ = *s++;
return dest;
}
void *memset (void *dest, int val, size_t len)
{
unsigned char *ptr = dest;
while (len-- > 0)
*ptr++ = val;
return dest;
}
#endif
char *strstr(const char *haystack, const char *needle)
{
size_t haystack_len = strlen(haystack);

View File

@ -36,7 +36,11 @@ static inline int memcmp(const void *s1, const void *s2, size_t n)
* not overlap.
* void *memcpy(void *dst, const void *src, size_t n);
*/
#ifndef DEBUG_GDB
#define memcpy(d, s, n) __builtin_memcpy((d), (s), (n))
#else
void *memcpy (void *dest, const void *src, size_t len);
#endif
/**
* Copies n bytes from the memory area pointed to by src to the memory area
@ -50,7 +54,11 @@ void *memmove(void *dest, const void *src, size_t n);
* value c.
* void *memset(void *s, int c, size_t n);
*/
#ifndef DEBUG_GDB
#define memset(s, c, n) __builtin_memset((s), (c), (n))
#else
void *memset (void *dest, int val, size_t len);
#endif
/**
* Returns the string length, excluding the terminating null character.