Difference between revisions of "GDB"

From ReactOS Wiki
Jump to: navigation, search
(Experimental Method for Loading Symbols Automatically in GDB)
m (Finally not outdated)
 
(3 intermediate revisions by 2 users not shown)
Line 14: Line 14:
 
  target remote localhost:567
 
  target remote localhost:567
  
== Installing CygWin ==
+
== Installing CygWin for Windows users ==
 
 
 
Download http://www.cygwin.com/setup.exe and start it. You can use default options throughout, only make sure you select "gdb" from the "Devel" category in the package selection dialog.
 
Download http://www.cygwin.com/setup.exe and start it. You can use default options throughout, only make sure you select "gdb" from the "Devel" category in the package selection dialog.
 
+
{{Warning|The patched version described below is probably outdated, the regular one should be fine}}
 
Although the standard CygWin gdb/insight should work, there is a slightly modified version available at http://svn.reactos.org/downloads/gdb/gdb_reactos.zip
 
Although the standard CygWin gdb/insight should work, there is a slightly modified version available at http://svn.reactos.org/downloads/gdb/gdb_reactos.zip
  
Line 28: Line 27:
 
For instructions on how to build the modified version from source see the end of this document.
 
For instructions on how to build the modified version from source see the end of this document.
  
== Preparing ReactOS ==
+
== GDB for Linux users ==
  
At the moment, the GDB remote stub in ReactOS is hardcoded to use COM2. If you need to make it use COM1, edit reactos\ntoskrnl\kd\wrappers\gdbstub.c, search for GdbPortInfo and make the appropriate change (2 -> 1) there.
+
By default, GDB is compiled to handle ELF executables. In order to get the best debugging experience with GDB, getting a version able to support Windows executable is highly recommended. The easiest way is to install the mingw-w64 version of GDB provided by your Linux distribution. If your distribution doesn't have one, you can compile it from source. Run those commands:
Normally ReactOS is built with -Os optimization, this means the compiler can re-arrange some code and keep variables in registers. We recommend building without optimization. Set GDB property in your config.rbuild to "1".
+
<nowiki>
 +
wget http://ftp.gnu.org/gnu/gdb/gdb-9.1.tar.xz
 +
tar xf gdb-9.1.tar.xz
 +
cd gdb-9.1
 +
mkdir build
 +
cd build
 +
./configure --prefix=/opt/pe-gdb --target=i686-w64-mingw32 --enable-lto --with-python=/usr/bin/python --with-system-readline --with-expat
 +
make
 +
sudo make install
 +
</nowiki>
  
GDB needs the "stabs" debug info which is normally stripped from the executables during build. To prevent this from happening, set an environment variable "ROS_BUILDNOSTRIP" to the value "yes". Then do a "make clean" and a "make".
+
Then you can launch GDB with
 +
/opt/pe-gdb/bin/i686-w64-mingw32-gdb
  
If you check e.g. output-i386\ntoskrnl, you'll see a "ntoskrnl.exe" and a "ntoskrnl.nostrip.exe". The "ntoskrnl.exe" version is the one that is installed in the ReactOS image, the ntoskrnl.nostrip.exe is there just for the benefit of GDB. "make install" knows it should use "ntoskrnl.exe".
+
== Preparing ReactOS ==
  
Change your freeldr.ini file to activate GDB debugging. The magic word is "Options=/DEBUGPORT=GDB", personally I also like to capture the debug log even when I'm using GDB, so I have "Options=/DEBUGPORT=COM1 /DEBUGPORT=GDB". The virtual COM1 port from my VMware machine is then redirected to a file, the virtual COM2 port to a physical port.
+
ReactOS has a new GDB stub in a form of a KDCOM module. But, since KDCOM is disabled by default in GCC builds, it must be enabled.
 +
The cleanest way to achieve this is to create a new build directory, and run inside a RosBE session:
 +
cd output-i686-mingw-newbuild
 +
/path/to/reactos/configure.sh -D_WINKD_:BOOL=TRUE -DGDB:BOOL=TRUE -DSEPARATE_DBG:BOOL=TRUE -DKDBG:BOOL=FALSE
 +
ninja bootcd
  
== Starting a debug session ==
+
A new bootcd with GDB debugging enabled is ready to be installed. Note that you must attach gdb to the installer too.
  
You're all set to start a debug session now. Boot ReactOS and in freeldr select the entry with the "/DEBUGPORT=GDB" option. Very early on in the boot process, the screen should display "Waiting for GDB to attach".
+
If you want to keep your current build directory, you can just set the variables in the CMakeCache.txt of the build folder
  
This is your cue to start Insight (insight.exe from c:\cygwin\bin). Open its console window (Console from
+
== Starting a debug session ==
the View menu, Ctrl+N shortcut or the "C:\" button in the button bar). You now need to tell GDB where your source is located. Since Insight is a CygWin app, you have to do so in "CygWin notation", e.g. if the root of your source tree is "H:\ros\reactos", you will specify "/cygdrive/h/ros/reactos". Give the following commands in the command window (adjusted for your own situation):
+
Before debugging ReactOS, make sure it can find debug symbols. They are located in /path/to/build/symbols. Enter:
 
+
  set solib-search-path /path/to/build/symbols
  directory /cygdrive/h/ros/reactos
+
Then connect to the server with:
symbol-file /cygdrive/h/ros/reactos/output-i386/ntoskrnl/ntoskrnl.nostrip.exe
+
target remote localhost:567
 
 
The first tells GDB the location of the source tree, the second tells it to load symbols from the ntoskrnl.nostrip.exe executable. Now, tell GDB that we're debugging remotely:
 
 
 
  set remotebaud 115200
 
  target remote /dev/ttyS0
 
 
 
(The 115200 baudrate is again hardcoded, you can change it in reactos/ntoskrnl/kd/gdbstub.c)
 
 
 
/dev/ttyS0 is the CygWin equivalent of COM1, if your setup is such that GDB should be using COM2 substitute /dev/ttyS1.
 
 
 
These commands should produce a message telling you that you're currently in DbgBreakPointWithStatus@4(). At this point GDB is active and you can use e.g. the "where" command to get a stack backtrace although I personally use the Insight Stack view more than the "where" command. Note that the status of the program is listed as "Program not running. Click on run icon to start.", but that's a lie. You need to use the "continue" (or just "c") command to continue execution. When you give the continue command, booting ReactOS should proceed normally. GDB will
 
stay out of the way as long as there's no breakpoint or exception occurring. You can trigger activation of GDB by typing a "K" while holding down the "Tab" key in ReactOS.
 
 
 
The initial four commands given above are commands you will need to give on every invocation, so it's easier to store them in a .gdbinit file. I have my .gdbinit file stored in C:\cygwin\bin and use a shortcut with C:\cygwin\bin\insight.exe as target and C:\cygwin\bin as start directory.
 
 
 
== Making GDB useful ==
 
 
 
When ReactOS is waiting for GDB to attach, only ntoskrnl and hal are loaded. This means that at that point you can only set breakpoints in ntoskrnl and hal (and as it turns out it is not easy to set a breakpoint in hal). The easiest way around this is to just put a breakpoint in the component you want to debug by inserting a line
 
  
  __debugbreak();
+
GDB should break at the debug trap function, type "c" to continue execution. Then you can enter the debugger by typing "Tab+K" into ReactOS console, or "Ctrl+C" into GDB. GDB show a lot of started threads, because the stub started to list all the threads running in the system.
  
in the source of the component and then recompiling/installing it. When this breakpoint is hit, GDB is activated. But there's a slight problem, we only told GDB about the symbols in ntoskrnl, not about symbols in other components. This can be fixed by giving the command (example):
+
Type <code>bt</code> to view the stack trace, which should look like something like this:
 +
<nowiki>
 +
(gdb) bt
 +
#0  RtlpBreakWithStatusInstruction@0 () at /home/sdever/Documents/ReactOS/reactos/sdk/lib/rtl/i386/debug_asm.S:56
 +
#1  0x80895ee9 in @KeUpdateSystemTime@12 (TrapFrame=0x809e6ca4 <P0BootStackData+11428>, Increment=149952, Irql=2 '\002') at /home/sdever/Documents/ReactOS/reactos/ntoskrnl/ke/time.c:101
 +
#2  0x802eb7b4 in @HalpProfileInterruptHandler@4 (TrapFrame=0x809e6ca4 <P0BootStackData+11428>) at /home/sdever/Documents/ReactOS/reactos/hal/halx86/generic/timer.c:199
 +
#3  0x802ec9f7 in _stricmp (s1=0x249c0 '?' <repeats 200 times>..., s2=0x20ac <error: Cannot access memory at address 0x20ac>) at /home/sdever/Documents/ReactOS/reactos/sdk/lib/crt/string/stricmp.c:10
 +
#4  0x802eb4f2 in HalSetProfileInterval@4 (Interval=10382640) at /home/sdever/Documents/ReactOS/reactos/hal/halx86/generic/profil.c:102
 +
#5  0x8090cf47 in @PopIdle0@4 (PowerState=<optimized out>) at /home/sdever/Documents/ReactOS/reactos/ntoskrnl/po/power.c:484
 +
#6  0x80937852 in @KiIdleLoop@0 () at /home/sdever/Documents/ReactOS/reactos/ntoskrnl/ke/i386/thrdini.c:320
 +
#7  0x8098b8b7 in KiSystemStartupBootStack@0 () at /home/sdever/Documents/ReactOS/reactos/ntoskrnl/ke/i386/kiinit.c:686
 +
#8  0x0000000e in ?? ()
 +
#9  0x00000000 in ?? ()</nowiki>
 +
At this point, only ntoskrnl.exe and hal.dll symbols are loaded. If you want to put breakpoints into other drivers, you must load symbols with <code>sharedlibrary</code> command.
  
  add-symbol-file /cygdrive/h/ros/reactos/output-i386/dll/win32/kernel32/kernel32.nostrip.dll
+
You can also examine kernel structures, for instance:
 +
<nowiki>(gdb) print ((KPCR*)0xffdff000)->Prcb->CurrentThread->Process
 +
$2 = (_KPROCESS *) 0x809ec080 <KiInitialProcess>
 +
(gdb) print (_EPROCESS *)$2->ImageFileName
 +
There is no member named ImageFileName.
 +
(gdb) print ((_EPROCESS *)$2)->ImageFileName
 +
$3 = "Idle", '\000' <repeats 11 times></nowiki>
 +
== Debugging the early boot process ==
 +
It is possible to debug the boot process using GDB with QEMU in the following way;
  
This tells GDB to additionally load the symbols for kernel32.dll, which is useful if you're debugging kernel32.dll. Incidentally, you can load these symbols during GDB startup, when the module itself hasn't been loaded by ReactOS yet. In other words, you can add these add-symbol-file commands to your .gdbinit file.
+
Start QEMU like this
  
This (usually) works fine for user mode components. It kind of breaks when the .DLL needs to be relocated by ReactOS because its memory space overlaps with the memory space of another component. Unless you tell it otherwise, GDB will assume a component is loaded at the address specified in the PE header of the file. If the component is actually loaded at a different address, you'll have to inform GDB of this.
+
qemu-system-i386 -s -S -m 512 -hda "$HOME/VirtualBox VMs/reactos/reactos.qcow"
  
This is especially true for kernel mode components. Only ntoskrnl has the correct load address (0x80000000) in its header, all other components are loaded in the first free available kernel memory slot. Even worse, this
+
* <code>-s</code> will open a gdbserver at tcp port 1234
can mean that kernel components might be loaded at a different addresses on a subsequent boot. To get around this, I use the following patch to ntoskrnl/ldr/loader.c:
+
* <code>-S</code> will not start the CPU at startup of qemu
 +
* <code>-m 512</code> sets the ram size to 512 MB
 +
* <code><nowiki>-hda <path></nowiki></code> specified the hard disk image
  
Index: ntoskrnl/ldr/loader.c
+
Create a <code>~/.gdbinit</code> which looks like this. (you can also enter these commands at the gdb prompt instead):
===================================================================
 
--- ntoskrnl/ldr/loader.c      (revision 20628)
 
+++ ntoskrnl/ldr/loader.c      (working copy)
 
@@ -747,13 +747,26 @@
 
 
      /*  Allocate a virtual section for the module  */
 
      DriverBase = NULL;
 
+    if (0 == wcscmp(L"\\??\\C:\\ReactOS\\system32\\win32k.sys", FileName->Buffer))
 
+    {
 
+      DriverBase = (PVOID) 0xe8000000;
 
+    }
 
+    if (0 == wcscmp(L"\\SystemRoot\\system32\\drivers\\afd.sys", FileName->Buffer))
 
+    {
 
+      DriverBase = (PVOID) 0xe8100000;
 
+    }
 
+    if (0 == wcscmp(L"\\SystemRoot\\system32\\drivers\\tcpip.sys", FileName->Buffer))
 
+    {
 
+      DriverBase = (PVOID) 0xe8300000;
 
+    }
 
+
 
      DriverBase = MmAllocateSection(DriverSize, DriverBase);
 
      if (DriverBase == 0)
 
      {
 
          CPRINT("Failed to allocate a virtual section for driver\n");
 
          return STATUS_UNSUCCESSFUL;
 
      }
 
-    DPRINT("DriverBase for %wZ: %x\n", FileName, DriverBase);
 
+    DPRINT1("DriverBase for %wZ: %x\n", FileName, DriverBase);
 
 
   
 
   
      /* Copy headers over */
+
  set disassembly-flavor intel
      memcpy(DriverBase, ModuleLoadBase, PENtHeaders->OptionalHeader.SizeOfHeaders);
+
layout asm
 
+
layout regs
This causes win32k.sys, afd.sys and tcpip.sys to be loaded by preference at 0xe8000000, 0xe8100000 and 0xe8300000 respectively. I then add the following lines to my .gdbinit:
+
target remote localhost:1234
 
+
set architecture i8086
  add-symbol-file /cygdrive/h/ros/reactos/output-i386/subsystems/win32/win32k/win32k.nostrip.sys 0xe8001000
+
break *0x7c00
  add-symbol-file /cygdrive/h/ros/reactos/output-i386/drivers/network/afd/afd.nostrip.sys 0xe8101000
+
break *0x7e00
  add-symbol-file /cygdrive/h/ros/reactos/output-i386/drivers/network/tcpip/tcpip.nostrip.sys 0xe8301000
 
 
 
Note the correspondence between the addresses in the loader patch and the addresses in the .gdbinit file: those in .gdbinit are 0x1000 higher than the ones in the loader patch. This is because the in the loader we specify
 
the start of the module, while in the .gdbinit file we need to specify the start of the code section. Since there's a 4096 (0x1000) byte header at the start of the module, the code section starts 4096 bytes after the
 
start of the module.
 
 
 
== Known problems ==
 
 
 
Although debugging with GDB generally works quite nice, there are still a few problems. The first is that you can't inspect the value of a static variable in kernel mode components. This is also caused by the relocation
 
of the module. The "add-symbol-file &lt;module&gt; &lt;code-address&gt;" tells GDB that the code has been relocated to a new address, but it doesn't tell GDB that the data has been relocated too. It's possible to tell GDB to relocate other sections too, but usually I'm too lazy to bother with that and just make a small change in the source code, setting a local (stack-based) pointer variable to the static data and then inspecting the value pointed to by that local variable.
 
 
 
Another problem is that GDB is triggered by "first-chance" exceptions. This means that GDB will get control, even if the code itself is prepared to handle the exception. The GDB stub should probably be changed to default
 
to second-chance exception handling only (where it would only get invoked if the code didn't handle the exception).
 
 
 
When a thread raises an exception, other threads are not blocked from running. This means that the other threads can cause changes in the machine state, basically making them able to change stuff behind your back. It also means that another thread can raise an exception, and then we have two threads competing for GDBs attention. To guard against this, a fast mutex is used to prevent a second thread from entering the GDB stub. Since this raises IRQL to APC_LEVEL this has its own set of problems.
 
 
 
GDB "knows" about setjmp/longjmp. This is a problem, because it will try to set a breakpoint by itself on the longjmp() call in MSVCRT. That's fine, when MSVCRT is actually loaded. If you're debugging a process which doesn't load MSVCRT, but you have "add-symbol-file msvcrt" in your .gdbinit, you'll run into trouble. GDB will try to set the breakpoint but it will fail and as a result your "continue" command will fail. Workaround: just don't put msvcrt symbol loading file in your .gdbinit unless you really need it.
 
  
== Building the modified GDB version from source ==
+
Now start <code>gdb</code>. It will automatically execute the commands from the <code>~/.gdbinit</code> file.
  
To build from source, you need the following CygWin packages:
+
As the bios will load the MBR at memory location 0x0000:0x7c00, and you have set a breakpoint, you can simply enter <code>c</code> or <code>cont</code> to jump to the start of the bootstrap code.
  
Category Devel
+
The reactos bootstrap code in the MBR will relocate itself to 0x1FE0:0x7c00 and load the bootrecord of the active partition at 0x0000:0x0600 and jumps to that location. The same breakpoint will hit, so we can enter <code>c</code> again. Note that gdb did not update the disassembly. You can enter <code>layout asm</code> and <code>layout regs</code> to fix this, but the correct code will be executed, even if you don't do that.
* bison
 
* flex
 
* gcc
 
* gdb (with source)
 
* make
 
* patchutils
 
Libs
 
* libncurses-devel
 
* tcltk (with source)
 
  
After downloading and installing these, download http://svn.reactos.org/downloads/gdb/gdb_reactos_patch.zip
+
The bootstrap code in the bootrecord, will load sector 14 of a FAT32 volume to 0x0000:0x7e00 and continue executing there. Entering <code>c</code> will hit that breakpoint.
and unpack in /usr/src (C:\cygwin\usr\src if you unpack from Windows).
 
  
Start a Cygwin bash shell and check that /usr/src/tcltk.patch and /usr/src/gdb.patch are present. Then build the modified GDB using the following commands (from your bash shell):
+
freeldr.sys will be loaded at 0x0000:0xF800, so you can set a breakpoint there too, to continue there.  
  
cd /usr/src/tcltk-20030901-1; patch -p 0 < ../tcltk.patch
 
cd /usr/src/gdb-20041228-3; patch -p 0 < ../gdb.patch
 
ln -s ../tcltk-20030901-1/tk tk
 
ln -s ../tcltk-20030901-1/tcl tcl
 
ln -s ../tcltk-20030901-1/itcl itcl
 
cd /usr/src; mkdir gdb-build; cd gdb-build
 
../gdb-20041228-3/configure --with-prefix=/usr
 
make
 
cd gdb
 
strip gdb.exe; strip gdbtui.exe; strip insight.exe
 
 
[[Category:Tutorial]]
 
[[Category:Tutorial]]

Latest revision as of 15:33, 16 April 2020

This HOWTO describes the installation and setup of GDB versions as kernel debuggers for ReactOS. It focuses on the CygWin GDB since it comes with a GUI in the form of the "insight" executable, however most of the information should be applicable to other GDB versions too. Note that it is also possible to debug user mode code using this setup, you can jump between kernel mode and user mode.

Hardware setup

Since we're using the "remote" capabilities of GDB, we have to make a serial connection from the host (where GDB is running) to the target (where ReactOS is running), using a null modem cable. A cable with the following layout works (DB9 connectors):

  • On each connector, tie 1, 4 and 6 (DTR, CD, DSR) together
  • Cross 2 and 3 from one connector to 3 and 2 on the other one (Rx, Tx)
  • Connect 5 on both connectors (GND)
  • Cross 7 and 8 from one connector to 8 and 7 on the other one (RTS, CTS)

The target machine can be another computer, or it can be a VMware virtual machine. To use a VMware virtual machine install VmwareGateway from http://l4ka.org/tools/vmwaregateway.php and attach GDB to tcp\ip port:

target remote localhost:567

Installing CygWin for Windows users

Download http://www.cygwin.com/setup.exe and start it. You can use default options throughout, only make sure you select "gdb" from the "Devel" category in the package selection dialog.

Icon speedy deletion.png Warning: The patched version described below is probably outdated, the regular one should be fine


Although the standard CygWin gdb/insight should work, there is a slightly modified version available at http://svn.reactos.org/downloads/gdb/gdb_reactos.zip

Changes in the modified version:

  • Fix to allow hardware watchpoints for remote i386 targets
  • Removed a check to allow stack back tracing from kernel mode back into user mode
  • Mark insight.exe as a Windows subsystem executable (as opposed to a console subsys executable)

You can download the .zip file and replace the CygWin gdb.exe, gdbtui.exe and insight.exe (if you used defaults they will be located in C:\cygwin\bin) with the versions in the .zip file. If you use the modified version, you still need to install the CygWin gdb package, since it contains some support files that are not included in the .zip

For instructions on how to build the modified version from source see the end of this document.

GDB for Linux users

By default, GDB is compiled to handle ELF executables. In order to get the best debugging experience with GDB, getting a version able to support Windows executable is highly recommended. The easiest way is to install the mingw-w64 version of GDB provided by your Linux distribution. If your distribution doesn't have one, you can compile it from source. Run those commands:

wget http://ftp.gnu.org/gnu/gdb/gdb-9.1.tar.xz
tar xf gdb-9.1.tar.xz
cd gdb-9.1
mkdir build
cd build
./configure --prefix=/opt/pe-gdb --target=i686-w64-mingw32 --enable-lto --with-python=/usr/bin/python --with-system-readline --with-expat
make
sudo make install

Then you can launch GDB with

/opt/pe-gdb/bin/i686-w64-mingw32-gdb

Preparing ReactOS

ReactOS has a new GDB stub in a form of a KDCOM module. But, since KDCOM is disabled by default in GCC builds, it must be enabled. The cleanest way to achieve this is to create a new build directory, and run inside a RosBE session:

cd output-i686-mingw-newbuild
/path/to/reactos/configure.sh -D_WINKD_:BOOL=TRUE -DGDB:BOOL=TRUE -DSEPARATE_DBG:BOOL=TRUE -DKDBG:BOOL=FALSE
ninja bootcd

A new bootcd with GDB debugging enabled is ready to be installed. Note that you must attach gdb to the installer too.

If you want to keep your current build directory, you can just set the variables in the CMakeCache.txt of the build folder

Starting a debug session

Before debugging ReactOS, make sure it can find debug symbols. They are located in /path/to/build/symbols. Enter:

set solib-search-path /path/to/build/symbols

Then connect to the server with:

target remote localhost:567

GDB should break at the debug trap function, type "c" to continue execution. Then you can enter the debugger by typing "Tab+K" into ReactOS console, or "Ctrl+C" into GDB. GDB show a lot of started threads, because the stub started to list all the threads running in the system.

Type bt to view the stack trace, which should look like something like this:

(gdb) bt
#0  RtlpBreakWithStatusInstruction@0 () at /home/sdever/Documents/ReactOS/reactos/sdk/lib/rtl/i386/debug_asm.S:56
#1  0x80895ee9 in @KeUpdateSystemTime@12 (TrapFrame=0x809e6ca4 <P0BootStackData+11428>, Increment=149952, Irql=2 '\002') at /home/sdever/Documents/ReactOS/reactos/ntoskrnl/ke/time.c:101
#2  0x802eb7b4 in @HalpProfileInterruptHandler@4 (TrapFrame=0x809e6ca4 <P0BootStackData+11428>) at /home/sdever/Documents/ReactOS/reactos/hal/halx86/generic/timer.c:199
#3  0x802ec9f7 in _stricmp (s1=0x249c0 '?' <repeats 200 times>..., s2=0x20ac <error: Cannot access memory at address 0x20ac>) at /home/sdever/Documents/ReactOS/reactos/sdk/lib/crt/string/stricmp.c:10
#4  0x802eb4f2 in HalSetProfileInterval@4 (Interval=10382640) at /home/sdever/Documents/ReactOS/reactos/hal/halx86/generic/profil.c:102
#5  0x8090cf47 in @PopIdle0@4 (PowerState=<optimized out>) at /home/sdever/Documents/ReactOS/reactos/ntoskrnl/po/power.c:484
#6  0x80937852 in @KiIdleLoop@0 () at /home/sdever/Documents/ReactOS/reactos/ntoskrnl/ke/i386/thrdini.c:320
#7  0x8098b8b7 in KiSystemStartupBootStack@0 () at /home/sdever/Documents/ReactOS/reactos/ntoskrnl/ke/i386/kiinit.c:686
#8  0x0000000e in ?? ()
#9  0x00000000 in ?? ()

At this point, only ntoskrnl.exe and hal.dll symbols are loaded. If you want to put breakpoints into other drivers, you must load symbols with sharedlibrary command.

You can also examine kernel structures, for instance:

(gdb) print ((KPCR*)0xffdff000)->Prcb->CurrentThread->Process
$2 = (_KPROCESS *) 0x809ec080 <KiInitialProcess>
(gdb) print (_EPROCESS *)$2->ImageFileName
There is no member named ImageFileName.
(gdb) print ((_EPROCESS *)$2)->ImageFileName
$3 = "Idle", '\000' <repeats 11 times>

Debugging the early boot process

It is possible to debug the boot process using GDB with QEMU in the following way;

Start QEMU like this

qemu-system-i386 -s -S -m 512 -hda "$HOME/VirtualBox VMs/reactos/reactos.qcow"
  • -s will open a gdbserver at tcp port 1234
  • -S will not start the CPU at startup of qemu
  • -m 512 sets the ram size to 512 MB
  • -hda <path> specified the hard disk image

Create a ~/.gdbinit which looks like this. (you can also enter these commands at the gdb prompt instead):

set disassembly-flavor intel
layout asm
layout regs
target remote localhost:1234
set architecture i8086
break *0x7c00
break *0x7e00

Now start gdb. It will automatically execute the commands from the ~/.gdbinit file.

As the bios will load the MBR at memory location 0x0000:0x7c00, and you have set a breakpoint, you can simply enter c or cont to jump to the start of the bootstrap code.

The reactos bootstrap code in the MBR will relocate itself to 0x1FE0:0x7c00 and load the bootrecord of the active partition at 0x0000:0x0600 and jumps to that location. The same breakpoint will hit, so we can enter c again. Note that gdb did not update the disassembly. You can enter layout asm and layout regs to fix this, but the correct code will be executed, even if you don't do that.

The bootstrap code in the bootrecord, will load sector 14 of a FAT32 volume to 0x0000:0x7e00 and continue executing there. Entering c will hit that breakpoint.

freeldr.sys will be loaded at 0x0000:0xF800, so you can set a breakpoint there too, to continue there.