mirror of
https://github.com/memtest86plus/memtest86plus.git
synced 2024-11-23 00:16:24 -06:00
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:
parent
df803bf294
commit
bcf8171593
8
.gitignore
vendored
8
.gitignore
vendored
@ -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
97
HOW_TO_DEBUG_WITH_GDB.md
Normal 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?
|
@ -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
226
build32/debug_memtest.sh
Executable 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
|
@ -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
229
build64/debug_memtest.sh
Executable 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
|
23
lib/string.c
23
lib/string.c
@ -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);
|
||||
@ -107,4 +128,4 @@ uint32_t hexstr2int(const char *hexstr) {
|
||||
ival = (ival << 4) | (b & 0xF);
|
||||
}
|
||||
return ival;
|
||||
}
|
||||
}
|
12
lib/string.h
12
lib/string.h
@ -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);
|
||||
*/
|
||||
#define memcpy(d, s, n) __builtin_memcpy((d), (s), (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);
|
||||
*/
|
||||
#define memset(s, c, n) __builtin_memset((s), (c), (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.
|
||||
|
Loading…
Reference in New Issue
Block a user