Using GDB to Debug Executables Without Debug Information
This article provides a comprehensive guide on how to employ GDB for debugging programs that lack symbol information, covering basic commands, breakpoint techniques, core‑dump analysis, reverse debugging, and a practical example of integrating VSCode, GDB, and QEMU to debug an ARM64 Linux kernel.
Introduction
MiHoYo, a Chinese game development company, shares a second‑round interview question: how to use GDB to debug an executable without debug information.
Problem Statement
Question: How to use GDB to debug an executable that does not contain debugging symbols?
Approach: Although symbol names are unavailable, basic debugging is still possible. Install GDB, start it with the target program, and use commands such as break (by address or offset), run , next , step , and print to inspect memory and registers.
Steps and Examples
Compile with -g to generate debug information (optional for comparison): gcc -g -o program program.c
Start GDB: gdb program
Set breakpoints, run, and step through the program: (gdb) break 10 (gdb) run (gdb) step (gdb) print variable_name
What is GDB?
GDB (GNU Debugger) supports languages such as C, C++, Ada, Objective‑C, and Pascal, and works for both local and remote debugging on Linux and Windows.
Installation and Startup
Check installation with gdb -v and install if missing.
Start GDB with gdb program or gdb then file program .
Pass arguments using gdb --args program arg1 arg2 or set them inside GDB with set args .
Core File Debugging
When a program crashes, a core file may be generated (requires ulimit -c unlimited ). Use gdb program core and the bt command to view the stack trace.
Common GDB Commands
list – show source lines.
break – set a breakpoint (by line, function, or address).
run – start the program.
next – step over a line.
step – step into a function.
continue – resume execution.
print – display variable or expression.
watch – stop when a variable changes.
catch – stop on events such as signals or library loads.
handle – configure signal handling.
info registers , info frame , backtrace – inspect registers, stack frames, and call stack.
Reverse Debugging (GDB 7.0+)
Record execution with record , then use reverse‑continue ( rc ) or reverse‑step ( rstep ) to move backward through the program flow, useful for tracing the origin of bugs.
Practical Example: VSCode + GDB + QEMU for ARM64 Kernel Debugging
This section describes building a minimal root filesystem with BusyBox, configuring an ARM64 Linux kernel (enabling CONFIG_DEBUG_INFO and CONFIG_INITRAMFS_SOURCE ), and launching QEMU with GDB remote debugging.
Install cross‑compilation tools (gcc‑aarch64‑linux‑gnu, etc.).
Build BusyBox statically and create an initramfs with required /etc files ( profile , inittab , fstab , and startup scripts).
Copy necessary libraries into the rootfs.
Configure the kernel (enable debug info, set initramfs source, etc.) and compile with make ARCH=arm64 Image .
Compile QEMU 4.2.1 from source and launch the kernel: /usr/local/bin/qemu-system-aarch64 -m 512M -smp 4 -cpu cortex-a57 -machine virt \ -kernel path/to/Image -append "rdinit=/linuxrc nokaslr console=ttyAMA0" \ -nographic -s The -s option opens GDB on port 1234.
Create a VSCode .vscode/launch.json configuration using gdb-multiarch and miDebuggerServerAddress: "localhost:1234" to attach to the running kernel.
Set breakpoints in the kernel source (e.g., init/main.c ) and perform step‑by‑step debugging directly from VSCode.
These steps demonstrate a complete workflow for low‑level debugging without symbol information, from simple user‑space programs to full kernel development.
Deepin Linux
Research areas: Windows & Linux platforms, C/C++ backend development, embedded systems and Linux kernel, etc.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.