← Back to list

Debugging with GDB

GNU Debugger (gdb) is the standard debugging tool for Linux systems and is widely used in the Linux community. gdb is often available by…

Oscar404 · 2024-09-15 13:26 · 2 claps · 7.0 min read
#gdb #debugging #reverse-engineering #code-debugging
Open on Medium ↗
Wiki topics: 💻 · Programming 🔓 · Open Source 🎮 · Gaming

Debugging with GDB

GNU Debugger (gdb) is the standard debugging tool for Linux systems and is widely used in the Linux community. gdb is often available by default on many Linux distributions and can be installed on any of them.

Main features of gdb:

  1. Command-line Debugger: gdb is a CLI-based debugger, meaning it operates via command-line inputs without a graphical interface (GUI). This allows for greater control and flexibility by relying on commands entered into the terminal.
  2. Scriptable: gdb can be scripted to perform specific tasks or automate debugging processes, giving users a powerful tool for automated corrections and advanced debugging capabilities.
  3. Remote Debugging Support: gdb supports remote debugging, allowing you to debug a program running on another machine through a network connection. This is particularly useful for debugging applications on embedded systems or systems where you don’t have direct access.

Installing gdb:

You can install gdb on Linux using the following commands:

For Debian-based distributions like Ubuntu:

sudo apt-get install gdb

For RPM-based distributions like CentOS:

sudo yum install gdb

Consider the following C program:

#include <stdio.h>
int addNumbers(int a, int b);

int main() {
    int n1, n2, sum;

    printf("Enter the first number: ");
    scanf("%d", &n1);
    printf("Enter the second number: ");
    scanf("%d", &n2);

    sum = addNumbers(n1, n2);        
    printf("sum = %d", sum);

    return 0;
}

int addNumbers(int a, int b) {
    int result;
    result = a + b;
    return result;          
}

This simple code contains two functions: the main function, which is the entry point of the program, and the addNumbers function, which takes two integers, adds them together, and returns the result.

Compiling for Linux or Windows:

You can compile this code into an ELF file for Linux or an EXE file for Windows:

For Linux ELF format:

gcc -m32 function.c -o function.elf

For Windows EXE format:

gcc -m32 function.c -o function.exe

then we are ready to run GDB on this code and compilation:

Running GDB:

To run gdb, simply use:

gdb
  1. You can load an executable file into gdb after launching the tool with the following command:
(gdb) file a.out
  1. Alternatively, you can attach gdb to an already running process using its PID:
(gdb) attach 126004
  1. or you can load the file with GDB directly:
gdb program.elf

After converting the code to a final executable file, we can now debug it:

GDB info Commands:

Although GDB does not provide a direct command to disassemble the entire file, it provides many amazing options and the Info command can extract a lot of data about the program:

  1. First, we open the file using GDB:
$ gdb function.elf
  1. Secondly, to display all the program functions Available, we use the following info command:
(gdb) info functions

GDB info functions

GDB info functions

As you can see in the previous image, all the program functions have been reached, even the two functions that we defined in the code main, addNumbers. We will also use the info command extensively throughout our interaction with GDB, and these are the most important commands:

info files: to display information about the files and libraries associated with the program.

info registers: displays the contents of the current registers.

info variables: displays a list of all global variables in the program.

info locals: displays all local variables in the current frame.

info breakpoints: displays all breakpoints that have been set in the program.

info frame: displays information about the current frame in calls.

info threads: displays a list of active threads in the program.

Disassembly with GDB:

As we mentioned earlier, the Disassembler is the one that converts Binary code to Assembly code, and we can perform this task using GDB via the following command:

First, we specify the function that we want to target by specifying its name, and we can do this via the command:

(gdb) info functions

Secondly, we convert the assembly system because GDB by default uses the AT&T system, so we need to change it to Intel:

(gdb) set disassembly-flavor intel
(gdb) disassemble main

Third, we write the Disassemble command and the function we want to start from the specified point (the main function):

(gdb) disassemble main

Disassembly in GDB

Disassembly in GDB

As you can see in the previous image, the assembly code for the main function was successfully retrieved.

Note that targeting the main function allows you to see the main sequence of events and calls in the program as a whole, and since main is the starting point for execution, its code often contains calls to most of the important functions in the program, or at least the functions that are used initially to set up the program.

The best practice is: Start by analyzing main to understand the basic structure, then use stepping and backtrace to discover other functions more deeply.

If you want a comprehensive analysis, you can later disassemble specific functions by name, or even disassemble the entire program using tools like objdump with a careful analysis as mentioned earlier.

Thirdly, we can disassemble memory address and not just target functions or classes by their names.

For example, by targeting the main function, we were able to access the addNumbers function by its address in memory:

Analyzing the code

Analyzing the code

Then we disassemble it at its memory address:

(gdb) disassemble 0x1244
(gdb) break addNumbers

Note that from the analysis of the previous code, the value of the first and second numbers is transferred to eax, edx, then they are combined using the add instruction.

Thus, we can microscopically identify parts of the code even if they are inside executable files.

After learning about Disassembly in GDB, let’s learn how to actually run the program while analyzing it:

Debugging with GDB:

Actually, the previous steps are also part of Debugging but the main part of it is running the code or part of it and the flow function:

  1. We put a breakpoint on the function we want, in our case we will target the addNumbers function. We can target it by name or memory address:
(gdb) break addNumbers
(gdb) run
  1. After setting a Breakpoint, we run the code or start Debugging using the following commands:
  • The run command is used to start the program from the beginning (i.e. we do not need to set any breakpoints to execute it).

Also note: If you execute the run command, any state or data collected before that will be lost, such as any information about current Breakpoints, Registers, etc. will be restored to its original state as if you had started a new GDB tool, which is a good thing that makes us restart the program over and over again simply to test it.

  • The continue command is used to resume the program execution after it has been paused. Also note: continue does not start the program from the beginning, but resumes the process execution from where it stopped.
  • The quit command is used to terminate GDB completely.
  • The kill command is used to terminate the process that is currently being debugged. So, when you enter kill, the program you ran in GDB will stop, which is useful if you want to terminate the program without exiting GDB.
  1. Now let’s run the addNumbers function:
(gdb) run
(gdb) info registers

Debugging addNubmers function

Debugging addNubmers function

As you can see in the previous image, after executing the function we entered the two numbers until the execution stopped again.

  1. Now let’s display the Registers to see the values ​​stored in them:
(gdb) info registers

info registers

info registers

As you can see in the previous image, the first column on the left contains the names of the Registers, the middle column contains the value stored in it in Hexadecimal and the last column contains the value stored in it as well but in Decimal if possible. Also, the eflags that we talked about earlier appear and are set to PF, SF, IF. Also note that the numbers 20 and 30 have been saved in eax, edx as we mentioned earlier! Here we can display any value stored in a specific Register using the print command:

(gdb) print $eax

  1. Finally, let’s complete the execution of the code (addNumbers function):
(gdb) continue

Exited normally

Exited normally

As you can see in the previous image, the sum of the two numbers has been printed and the function exits normally. In the same way, we can analyze any part of the program microscopically, which helps us understand it even in the largest projects.

Breakpoints in GDB:

As mentioned earlier you can set multiple software breakpoints which work at the software level.

You can do this in GDB by the same command, like setting Breakpoints:

(gdb) break main
(gdb) break addNumbers

Then you specify all Breakpoints using the info command:

Breakpoints in GDB

Breakpoints in GDB

As you can see in the previous image, each breakpoint has its own number and even its own address in memory.

We can navigate between them using the number and the enable command:

(gdb) enable 1

Or disable a specific breakpoint using the disable command:

(gdb) disable 2

Enabling/Disabling breakpoints in GDB

Enabling/Disabling breakpoints in GDB

Finally, you can delete any of them using the delete command, or even delete them all at once:

(gdb) delete 1
(gdb) delete breakpoints

This way we can effectively handle Breakpoints within GDB to control the execution of the code and focus on any part of it.


메타데이터
post_id
3d8ec5756ae2
slug
debugging-with-gdb-3d8ec5756ae2
url
https://medium.com/@Oscar404/debugging-with-gdb-3d8ec5756ae2
canonical_url
https://medium.com/@Oscar404/debugging-with-gdb-3d8ec5756ae2
author_url
https://medium.com/@Oscar404
status
ok
fetched_at
2026-06-27 10:07:59