Extending the features of a debugger, especially GDB, sounded like a fun and interesting project at first glance. I already have quite a bit of experience working with GDB, and use it almost daily while Reverse Engineering or during CTFs.
For those of you that are not familiar, CTF stands for "Capture the Flag". It is a competition wherein the hosting team creates and puts out challenges/services that are intentionally vulnerable - usually in a way that is specific to a single field of Cyber Security - and the point of the challenge is to figure out what the vulnerability is and use it to get a text string from it called a "flag". Capture more flags, gain more points. I play CTFs with team bi0s.
GDB has an awesome Python API that it utilizes to do things it cannot do straight out the box, and one such thing is pretty-printing.
Imagine you have a weird class/structure in your program, and you want to view it in memory, so you pop it open in GDB. Then you print your variable in GDB and it spits out something so awful that you wish you never started debugging in the first place. This is because GDB obviously cannot know about any and every single structure out there, and sometimes it needs a little help - this is where the awesome Python API I mentioned comes in.
Python essentially hooks onto GDB and interprets something GDB might be unable to, then do some Python magic and voila, you have the same structure neatly formatted! Some nice examples and a deeper explanation of what pretty-printing can be found in the documentation
GCC ships pretty-printing scripts for libstdcxx
, which is essentially all of the C++ STL Structures, and my job is to get the GDB installed on RTEMS to automatically load these pretty-printing scripts at load-time, to enhance the debugging experience on RTEMS 😃. Next, add pretty-printing support for kernel structures present on RTEMS.
Starting off, I already had a couple of things to get ticked off my checklist. I mainly wanted to set up a proper debugging environment, and also study various commonly used kernel structures - before diving deeper into my project. Since my project itself was to extend the functionality of the debugger, what good is it if I don't have a debugging environment setup in place?
Let's jump right into my debugger setup then.
I am working on a Windows 11 (x64), WSL2 (Arch) system. On top of this, I have the RTEMS tooling installed from the instructions in their documentation. On top of that, they provide a variety of options to build various BSPs (think of BSPs as housing for the OS to be embedded upon). I needed a build setup such that:
- I could compile RTEMS C++ programs at will
- GDB could debug those programs remotely through a remote emulator (either built in, or using Qemu)
First, to build a BSP. I had to make 2 important choices:
- Which architecture will I use?
- Which BSP will I use for said architecture? (Note: A single architecture can support multiple BSPs)
I initially thought x86_64
arch, along with any suitable BSP for that arch would have me good to go, but I was sadly mistaken. Not only did I struggle to find BSPs for x86_64
, but I also found that there were no test configurations (yet) for any of the x86_64
BSPs 😦, so I decided not to proceed with that
Next up, I chose the sparc
arch, along with the erc32
BSP, since that was one which seemed to have very good support for emulation and GDB support, and was also the one used in most of the examples mentioned in the docs. So I went ahead and built that.
sparc
has an emulator built in in RTEMS, sparc-rtems6-sis
( sis
: Sparc Instruction Set Simulator), which can emulate the instructions of the RTEMS executable, and GDB can attach to the process through a TCP port.
However, although compiling RTEMS C++ programs and running them worked perfectly on my sparc/erc32
build, I couldn't debug (i.e, step through) code conveniently. I kept hitting some kind of data access violation exception, due to which RTEMS would have a fatal crash and exit. Sad.
So I finalised on the arm/xilinx_zynq_a9_qemu
BSP upon the suggestion of my mentor, which worked perfectly. So, I will be using that for the rest of this blog.
First, a sample C++ program.
1 | // main.cc |
First off, RTEMS uses waf
(an alternative to make
), to build BSPs and applications on the platform. It's pretty versatile and easy to understand. You need 2 main things to build any app like this on RTEMS (apart from the source, of course) - waf
(the script doing all the work) & wscript
(the waf script containing instructions to be executed). Apart from these 2 files, you would also need some dependency files, and an initialisation script.
This page in the documentation gives a pretty good overview on how one can build an app on RTEMS.
Here is my wscript
for all C++ applications like the one above:
1 | # wscript |
The main function we need to focus on is build
. Note the features
(type of program), and cxxflags
parameters passed to the bld
function.
The application can be built with:
1 | # configure the waf for the BSP you are building for |
1 | # build the app |
1 | # run the executable to ensure it works |
Once you confirm that the app works, we can move onto debugging it.
First, for emulation, I went with qemu. I already had a qemu-system-arm
setup so it seemed like the most logical option.
Program can be emulated with:
1 | # machine type: xilinx-zynq-a9 |
On a separate terminal, connect to this emulator (default port: 1234)
1 | arm-rtems6-gdb <app_path.exe> |
More information on remote debugging can be found here.
Voila! Debugging environment set up!
Now, this is a pretty big process, with a lot of commands. This can be made easier, of course. Shell scripting for the win.
1 | # ./setup.sh |
On a different terminal:
1 | qemu-system-arm -M xilinx-zynq-a9 -m \ |
Make a script called init.gdb
1 | target extended-remote localhost:1234 |
On the first terminal, create a new shell script
1 | # ./loadgdb.sh |
I have all of these configured as commands on my terminal to make life easier, you could do that as well.
In next week's blog, I will be diving straight into the main crux of my project - the issue at hand, and how I plan on fixing it, stay tuned!