launch/attach process#
Note: In gdb, you only need to input the first few letters or even the first letter of the complete command. The premise is that the abbreviation does not conflict with other commands.
gdb ./a.out # Debug an unrun program from the beginning
gdb a <pid> # Attach to a running process
gdb -p <pid> # Same as the previous command
gdb -q a
(gdb) attach <pid> # You can also start first and then attach
(gdb) detach # If debugging via gdb attach, the original process will continue after detach
(gdb) q # Exit gdb
----------------Silent startup options: directly enter gdb, instead of printing a bunch of version copyright information
gdb -silent
gdb -q
# You can set an alias for gdb in ~/.bashrc:
alias gdb="gdb -q"
----------------Silent exit setting: gdb will prompt when exiting: whether to exit the debugging program
#A debugging session is active.
#
# Inferior 1 [process 29686 ] will be killed.
#Quit anyway? (y or n) n
#Silent exit: This command can also be added to the .gdbinit file.
(gdb) set confirm off
----------------When starting gdb, specify the parameters of the program to be debugged
gdb -args ./a.out a b c
----------------You can also set it in gdb via commands
(gdb) set args a b c
(gdb) show args
Argument list to give program being debugged when it is started is "a b c".
----------------You can also specify when running the program
(gdb) r a b # This seems to be saved as the argument list, and the next time r may carry the saved argument list, it is recommended to check (gdb) show args
----------------Build the program after entering gdb
(gdb) shell
(gdb) make CFLAGS="-g -O0"
Start attach debugging with a script that automatically gets the pid#
#!/bin/bash
# Indicate to use the Bash interpreter to execute the script
# Save as agdb.sh and add executable permissions (chmod +x agdb.sh)
# Usage: ./agdb.sh <program_name>
# Get the program name, which is the first argument
prog_bin=\$1
# Check if a program name was passed
if [ -z "$prog_bin" ]; then
echo "Please provide the name or path of the program!"
exit -1
fi
# Get the PID of the program
running_name=$(basename $prog_bin) # Extract the program name
pid=$(pidof $running_name)
# Check if PID is empty
if [ -z "$pid" ]; then
echo "No running process found: $running_name"
exit -1
fi
# Attach to the process
echo "Attaching to process PID: $pid..."
gdb -q -p $pid
Remote Debugging#
(gdb) i target # Display information about the debugging target platform
Remote target # or Local target
Connected to remote target via <connection info> # Port number
Architecture: <arch info> # Target architecture (e.g., x86, ARM, etc.)
(gdb) target remote localhost:25000 # Connect to gdbserver
gdb Temporary Environment Variables#
Set environment variables in GDB that only take effect for debugging functions. Usually temporary, valid only during the debugging session.
(gdb) set env PATH=$PATH:/path/to/some/library
(gdb) set env LD_PRELOAD=$LD_PRELOAD:/path/to/some/library
(gdb) show env LD_PRELOAD # View the temporary environment variables set by gdb
(gdb) unset env LD_PRELOAD
You can also configure gdb to run initialization commands at startup in .gdbinit
:
set env LD_PRELOAD=$LD_PRELOAD:/path/to/some/library
Run Control#
(gdb) r arg1 arg2 ... # Restart running the binary
(gdb) start # Used to start executing the program and pause at the first instruction, e.g., stop at main{
(gdb) stop # Pause execution
(gdb) c # Continue execution
(gdb) n # Step execution, skipping functions
(gdb) s # Step execution, entering function body when encountering a function
(gdb) until line # Run until reaching the specified line
Breakpoints Setting#
-----------------------------------Breakpoints Set
(gdb) b <file:line> # Set a breakpoint at a certain line in the file; the downside of setting breakpoints by line number: if the source program is changed, the previously set breakpoints may not be what you want
(gdb) b <file:function> # Set a breakpoint at a certain function in the file
(gdb) b <namespace::class::function> # Set a breakpoint at a member function of a class
(gdb) b (anonymous namespace)::<func> # Set a breakpoint at a function in an anonymous namespace
(gdb) b <location> <thread-id> # Set a breakpoint at a certain location for a specific thread
(gdb) b <location> if <condition> # Set a conditional breakpoint at a certain location
(gdb) b *0x400522 # When debugging assembly or programs without debug information, you often need to set breakpoints at program addresses
(gdb) tb a.c:15 # tb sets a temporary breakpoint that only takes effect once; after being hit once, it is deleted
-----------------------------------Breakpoints Delete/Enable/Disable
(gdb) d <break-id> # Delete a breakpoint
(gdb) disable <break-id> # Disable a breakpoint
(gdb) enable <break-id> # Enable a breakpoint
-----------------------------------Breakpoints Ignore, can be used for debugging loops
(gdb) ignore 1 5 # Ignore the first five hits of breakpoint 1
(gdb) ignore 1 0 # Cancel ignore
-----------------------------------Breakpoints Set Automatic Execution Commands
(gdb) command <break-id> # Modify existing breakpoint commands with this command
>silent # Execute command silently
>if x > 10
>p <var> # Automatically print variable <var> when hitting the breakpoint
>end
(gdb) i breakpoints # Used to view breakpoint commands
(gdb) delete # Used to delete breakpoint commands
-----------------------------------Breakpoints Environment Save
(gdb) save breakpoints my_breakpoints.txt # Save current breakpoints to path my_breakpoints.txt
(gdb) source my_breakpoints.txt # Load saved breakpoints from my_breakpoints.txt when starting GDB debugging next time
Set Breakpoint at Program Entry#
strip a.out # Remove debug information
-----------------------------Get program entry 1: Use readelf command to view ELF file header, find Entry point address:
readelf -h a.out
#Entry point address: 0x400440
-----------------------------Get program entry 2: (gdb) info files, find Entry point address:
(gdb) info files
#Entry point address: 0x400440
-----------------------------Get program entry 3: objdump -f a.out, find Entry point address:
objdump -f a.out
#Entry point address: 0x400440
-----------------------------Set breakpoint at program entry
(gdb) b *0x400440
Quickly Try Different Inputs#
Without modifying the source code or recompiling, try different inputs for the function using breakpoint commands + reverse debugging
(gdb) b drawing
Breakpoint 1 at 0x40064d: file win.c, line 6.
(gdb) command 1
Type commands for breakpoint(s) 1, one per line.
End with a line saying just "end".
>silent # Avoid output information when stopping at the breakpoint.
>set variable n = 0 # Modify input
>c
>end
# After checking the result, reverse execute and modify again
Watchpoint Setting#
Write watchpoints: the program pauses when the value of a certain variable (watchpoint) changes.
Read watchpoints: the program pauses when a read operation occurs on the variable.
Hardware Watchpoints: When setting a watchpoint, GDB uses hardware-level monitoring functions to track variable value changes. It does not affect program performance. However, there may be a limit on the number of hardware watchpoints, and if exceeded, GDB will use software watchpoints.
Software Watchpoints: If hardware watchpoints are not supported or the limit has been reached, GDB implements them in software. This usually affects program execution speed because it involves constant checking and managing memory.
(gdb) watch a # Set variable a as a write watchpoint
(gdb) wa a thread 2 # Only trigger the watchpoint for variable a in thread 2
(gdb) rw a # Read watchpoint, only effective for hardware watchpoints
(gdb) aw a # Read/write watchpoint
(gdb) delete watchpoint 2
(gdb) disable watchpoint 2 # Disable watchpoint 2
(gdb) enable watchpoint 2
(gdb) set can-use-hw-watchpoints off # Disable hardware watchpoints
(gdb) set can-use-hw-watchpoints on
-------------------------------Set watchpoint using memory address
(gdb) p &a # List the address of a
\$1 = (int *) 0x6009c8 <a>
(gdb) watch *(int*)0x6009c8 # Set watchpoint using dereferenced memory address
Catchpoint Setting#
catchpoint
is used to interrupt program execution when specific events occur (such as signals, function calls, system calls, etc.); catchpoint
is persistent by default and will interrupt the program every time the corresponding event is triggered.
(gdb) tcatch fork # Set as a temporary catchpoint that only captures once
(gdb) catch fork # Capture fork() syscall; GNU/Linux supports this feature. Check the official gdb manual for support on other platforms.
(gdb) catch vfork
(gdb) catch exec
(gdb) catch syscall # Capture all syscalls
(gdb) catch syscall [syscall | number] # Capture syscall with specified name or number
Bypass Anti-Debugging#
Some programs do not want to be debugged by gdb, so they call the ptrace
function in the program. If it fails to return, it indicates that the program is being traced by gdb or similar programs, and it exits directly.
The way to bypass such programs is to set a catchpoint
for the ptrace
call and then modify the return value of ptrace
.
----------------------------eg
#include <sys/ptrace.h>
#include <stdio.h>
int main(){
if (ptrace(PTRACE_TRACEME, 0, 0, 0) < 0 ) {
printf("Gdb is debugging me, exit.\n");
return 1;
}
printf("No debugger, continuing\n");
return 0;
}
----------------------------(gdb) catch syscall ptrace
(gdb) catch syscall ptrace
Catchpoint 2 (syscall 'ptrace' [101])
(gdb) r # Restart the program
Starting program: /data2/home/nanxiao/a
Catchpoint 2 (call to syscall ptrace), 0x00007ffff7b2be9c in ptrace () from /lib64/libc.so.6
(gdb) c
Continuing.
Catchpoint 2 (returned from syscall ptrace), 0x00007ffff7b2be9c in ptrace () from /lib64/libc.so.6
(gdb) set $rax = 0 # Linux64 syscall return value is in rax
(gdb) c
Continuing.
No debugger, continuing
[Inferior 1 (process 11491) exited normally]
Modify Machine States#
(gdb) set $var=XXX # Set gdb variable
----------------Modify Variable
(gdb) set var <varname> = <val> # Modify variable
(gdb) set file::array="Jil" # Modify string array value, string constants are in read-only data segments and cannot be modified
(gdb) p &array # Modify variable by modifying memory, but memory out of bounds is very dangerous
(gdb) set {char[4]} 0x80477a4 = "Jil" # set {type}address=expr
----------------Modify Register
(gdb) set var $eax = 8 # Modify return value register eax
(gdb) set var $pc=0x08050949 # As long as you know the address of a certain instruction, you can modify the PC to jump to the next instruction
----------------Modify
----------------Modify
----------------Modify
Jump Execution#
The conventional method of debugging is to set breakpoints and rerun the program, skipping the intermediate machine states that do not affect the result, and executing to a certain position can save time;
The principle of gdb jump instructions is to modify the program counter PC to make the program jump to the specified line and continue execution. The jump to the specified execution point does not pause; if you want gdb to pause at that execution point, you need to manually set a breakpoint.
(gdb) tb <file>:<num> # tb sets a temporary breakpoint that only takes effect once; after being hit once, it is deleted
(gdb) j <num> # Jump to the specified line number
(gdb) j <func>
(gdb) j <file>:<num>
(gdb) j <file>:<func>
(gdb) j *addr # Jump to the instruction pointed to by a certain address
(gdb) j +<num> # Jump back n lines
(gdb) j -<num> # Jump forward n lines
Time Travel Debugging#
Records the changes in machine states (such as variable, register, memory data changes, etc.) caused by each gdb instruction, stored by default in gdb_record.process_id. When needing to revert to a past state, the debugger will restore these states in reverse order.
Usage scenarios:
1) Hard-to-reproduce bugs, very complex logic issues
2) Multithreading concurrency issues
3) Memory-related issues, such as memory stomping, memory leaks, double frees, etc.
4) Call stack shows all question marks issue
(gdb) record save filename # Save the program execution history state information to a file, the default name is gdb_record.process_id
(gdb) record restore filename # Restore state information from the historical record file
(gdb) show record full insn-number-max # View the maximum number of executable state information that can be recorded, default 200000
(gdb) set record full insn-number-max n # Set the maximum number of instructions n that can be recorded
(gdb) set record full insn-number-max unlimited # Set the maximum number of instructions that can be recorded to unlimited
(gdb) set exec-direction reverse # Set the program to execute in reverse; after executing this command, common commands like next, nexti, step, stepi, continue, finish, etc. will execute in reverse
(gdb) set exec-direction forward # Set the execution direction to forward
(gdb) b n
(gdb) record # Record all state information during program execution
(gdb) c
(gdb) reverse-search # Reverse search previously entered commands
(gdb) record goto start # Jump to the start of the record
(gdb) record goto end # Jump to the end of the record
(gdb) record goto n # Jump to a specific position n in the record
(gdb) reverse-next # Execute one line of code in reverse, without entering function calls
(gdb) reverse-nexti # rni: Execute one instruction in reverse, without entering function calls
(gdb) reverse-step # rs: Execute one line of code in reverse, entering function calls
(gdb) reverse-stepi # rsi: Reverse step execution (machine code level)
(gdb) reverse-continue # rc: Continue execution in reverse
(gdb) reverse-finish # Execute in reverse until the function entry
(gdb) record stop # Stop recording state information
Switch Shell Environment Execution#
(gdb) shell # Enter shell mode, return to the Linux terminal
(gdb) !<shell_cmd> # Equivalent to (gdb) shell <shell_cmd>;
(gdb) pwd # Switch working directory in gdb
(gdb) cd tmp
(gdb) exit # Exit shell mode, return to gdb command line
Print#
Output Settings#
Specify gdb Input/Output Device#
By default, the program's input and output use the same terminal as gdb.
tty # View the current terminal
gdb -tty /dev/pts/2 ./a.out # Set the program's input and output
(gdb) tty /dev/pts/2 # Set the program's input and output
Print String Length Settings#
gdb limits the maximum length of printed strings. When gdb outputs a lot of information, it will pause output and print "---Type to continue, or q to quit---"; the following commands can modify the limit: gdb will output everything without pausing in between.
(gdb) show print elements # Show the maximum print length of strings
(gdb) set print elements <number-of-elements> # Set the maximum print length of strings
(gdb) set print elements 0 # Cancel the maximum print length of strings
(gdb) set print elements unlimited # Same as above
(gdb) set pagination off # Cancel paginated output
(gdb) set height 0 # Cancel the maximum height limit for output information
Machine State#
---------------------------------------Print Variables/Print Stack
(gdb) whatis <var> # Print variable type
(gdb) ptype <var> # Print variable type definition
(gdb) i variables # Print all variables in the current scope with their file and type
(gdb) i variables ^<var>$ # ^ indicates starting with <var>; $ indicates ending with <var>
(gdb) p <var> # Print variable <var>
(gdb) i locals # List all local variables (including static local variables) in the current function stack frame
(gdb) i variables # List all global and static variables
(gdb) i variables var # List all global and static variables starting with the regex var
(gdb) p 'file.c'::<var> # Extern global/static variables belonging to different files may have the same variable name, in which case specify the belonging file
(gdb) p sizeof(wchar_t) # Print the byte size of wchar_t type
(gdb) x/s str # Print ASCII string or character array variable str
(gdb) x/hs str # Print 2-byte string or character array variable str
(gdb) x/ws str # Print 4-byte string or character array variable str
(gdb) p array[index]@num # Print num consecutive elements starting from index
(gdb) set print array-indexes on # Print array indices by default, which do not show index subscript; display index subscript
(gdb) display <var> # Track variable or expression, automatically display the value of specified <var> every time the program pauses; that is, when hitting a breakpoint, stepping, or manually pausing
(gdb) undisplay <var> # Cancel tracking variable
---------------------------------------Print Protobuf Message
(gdb) p <var>.DebugString() # Print the internal structure of the proto object using DebugString();
---------------------------------------Print CPU Register
(gdb) i r # Print the values of all CPU registers, excluding floating-point and vector registers
(gdb) i all-registers # Print all CPU registers
(gdb) i r es # Print the value of CPU register es
(gdb) p $es # Print the value of CPU register es
(gdb) layout regs # In tui mode, display the register window
(gdb) tui reg float # View floating-point registers
(gdb) tui reg general # View general registers
---------------------------------------Print Memory
# n: Positive integer, the number of memory units you want to view
#
# f: Print format:
# - x: Hexadecimal
# - d: Decimal
# - u: Unsigned hexadecimal
# - o: Octal
# - t: Binary
# - a: Hexadecimal
# - c: Character format
# - f: Floating point
#
# u: Size of a single memory unit:
# - b: Single byte
# - h: Double byte
# - w: Four bytes
# - g: Eight bytes
#
# addr: Memory address to print
(gdb) x/<nfu> <addr> # Print memory address, <addr> can be the pointer value of a certain variable; commonly used x/nxb x/nub x/ntb
Using _ and __ for Memory Debugging#
(gdb) x/16xb a # Print memory
0x7fffffffe4a0: 0x00 0x01 0x02 0x03 0x04 0x05 0x06 0x07
0x7fffffffe4a8: 0x08 0x09 0x0a 0x0b 0x0c 0x0d 0x0e 0x0f
(gdb) p $_ # $_ stores the last memory address checked with the x command
\$1 = (int8_t *) 0x7fffffffe4af
(gdb) p $__ # $__ stores the value of the last checked memory data by the x command (usually the last byte)
\$2 = 15
--------------------------------------Some GDB commands (like i breakpoint) may modify the value of $_.
(gdb) i breakpoint # This command generates an anonymous variable to store the breakpoint, address 0x00000000004004a0, value 1
Num Type Disp Enb Address What
1 breakpoint keep y 0x00000000004004a0 in main at a.c:13
breakpoint already hit 1 time
(gdb) p $_
\$6 = (void *) 0x4004a0 <main+44>
Print Process's Virtual Memory#
(gdb) i proc mappings # Display the virtual memory of the current process, including the address range, size, offset, and access permissions of each segment
process 27676 flags:
PR_STOPPED Process (LWP) is stopped
PR_ISTOP Stopped on an event of interest
PR_RLC Run-on-last-close is in effect
PR_MSACCT Microstate accounting enabled
PR_PCOMPAT Micro-state accounting inherited on fork
PR_FAULTED : Incurred a traced hardware fault FLTBPT: Breakpoint trap
Mapped address spaces:
# Start Addr: Segment start address; End Addr: Segment end address; Size: Segment size; Offset: Offset; Flags: Permissions (r readable, w writable, x executable)
Start Addr End Addr Size Offset Flags
0x8046000 0x8047fff 0x2000 0xfffff000 -s--rwx
0x8050000 0x8050fff 0x1000 0 ----r-x
0x8060000 0x8060fff 0x1000 0 ----rwx
0xfee40000 0xfef4efff 0x10f000 0 ----r-x
0xfef50000 0xfef55fff 0x6000 0 ----rwx
0xfef5f000 0xfef66fff 0x8000 0x10f000 ----rwx
0xfef67000 0xfef68fff 0x2000 0 ----rwx
0xfef70000 0xfef70fff 0x1000 0 ----rwx
0xfef80000 0xfef80fff 0x1000 0 ---sr--
0xfef90000 0xfef90fff 0x1000 0 ----rw-
0xfefa0000 0xfefa0fff 0x1000 0 ----rw-
0xfefb0000 0xfefb0fff 0x1000 0 ----rwx
0xfefc0000 0xfefeafff 0x2b000 0 ----r-x
0xfeff0000 0xfeff0fff 0x1000 0 ----rwx
0xfeffb000 0xfeffcfff 0x2000 0x2b000 ----rwx
0xfeffd000 0xfeffdfff 0x1000 0 ----rwx
(gdb) i files # List the locations of each segment in memory, as well as the mapping of the .text, data segment, and uninitialized data segment .bss content of the loaded shared libraries to virtual memory
Symbols from "/data1/nan/a".
Unix /proc child process:
Using the running image of child Thread 1 (LWP 1) via /proc.
While running this, GDB does not access memory from...
Local exec file:
`/data1/nan/a', file type elf32-i386-sol2.
Entry point: 0x8050950
0x080500f4 - 0x08050105 is .interp
0x08050108 - 0x08050114 is .eh_frame_hdr
0x08050114 - 0x08050218 is .hash
0x08050218 - 0x08050418 is .dynsym
0x08050418 - 0x080507e6 is .dynstr
0x080507e8 - 0x08050818 is .SUNW_version
0x08050818 - 0x08050858 is .SUNW_versym
0x08050858 - 0x08050890 is .SUNW_reloc
0x08050890 - 0x080508c8 is .rel.plt
0x080508c8 - 0x08050948 is .plt
......
0xfef5fb58 - 0xfef5fc48 is .dynamic in /usr/lib/libc.so.1
0xfef5fc80 - 0xfef650e2 is .data in /usr/lib/libc.so.1
0xfef650e2 - 0xfef650e2 is .bssf in /usr/lib/libc.so.1
0xfef650e8 - 0xfef65be0 is .picdata in /usr/lib/libc.so.1
0xfef65be0 - 0xfef666a7 is .data1 in /usr/lib/libc.so.1
0xfef666a8 - 0xfef680dc is .bss in /usr/lib/libc.so.1
Print Heap#
Add custom print memory allocation command in .gdbinit
define mallocinfo
set $__f = fopen("/dev/tty", "w") # Open /dev/tty file for output
call malloc_info(0, $__f) # Call malloc_info function to output memory allocation information to the file
call fclose($__f) # Close the file
end
Debug the program and check memory allocation:
(gdb) mallocinfo
<malloc version="1"> # Version of the memory allocation function used
<heap nr="0"> # The first heap, multi-threaded programs will allocate an independent heap for each thread to reduce competition between threads
<sizes> # Allocation status of memory blocks of different sizes; in the example program, no memory blocks are allocated
</sizes>
<total type="fast" count="0" size="0"/> # Fast allocation and other types of memory blocks, count="0" indicates no memory blocks are currently allocated, size="0" indicates occupied memory size is 0.
<total type="rest" count="0" size="0"/> # This part is for the internal implementation of malloc, usually used to optimize memory allocation and distinguish different memory pools.
<system type="current" size="1134592"/> # Memory currently requested and allocated from the OS
<system type="max" size="1134592"/> # Maximum memory that the program can allocate from the OS, indicating that the program has not exceeded the system allocation limit.
<aspace type="total" size="1134592"/> # Size of the address space
<aspace type="mprotect" size="1134592"/> # mprotect is a system call used to change the access permissions of a segment of memory to protect it from illegal modification
</heap>
<total type="fast" count="0" size="0"/>
<total type="rest" count="0" size="0"/>
<system type="current" size="1134592"/>
<system type="max" size="1134592"/>
<aspace type="total" size="1134592"/>
<aspace type="mprotect" size="1134592"/>
</malloc>
Print Parameter Variables by Actual Argument Type in Polymorphic Interfaces#
#include <iostream> // Example code
using namespace std;
class Shape {
public: virtual void draw () {}
};
class Circle : public Shape {
int radius;
public:
Circle () { radius = 1; }
void draw () { cout << "drawing a circle...\n"; }
};
class Square : public Shape {
int height;
public:
Square () { height = 2; }
void draw () { cout << "drawing a square...\n"; }
};
void drawShape (class Shape &p){ // Expose a polymorphic interface, calling different derived class overloaded draw implementations based on the passed object type
p.draw ();
}
int main (void){
Circle a;
Square b;
drawShape (a);
drawShape (b);
return 0;
}
By default, gdb prints the object according to the declared type, so printing the type of the parameter variable p in the above polymorphic interface function stack frame will always be Shape; we want to print the parameter variable by actual argument type in this polymorphic interface function stack frame.
(gdb) set print object on
GDB Pretty Printers#
STL containers use complex memory layouts internally, and printing STL containers directly in GDB will show a bunch of low-level implementation details; GCC provides Python scripts (available since GDB 7.0) to enhance GDB's printing capabilities for C++ containers.
In high versions of gcc (>4.6.0), pretty printers are usually included: /usr/share/gcc/python
gcc.gnu.org Git - gcc.git/tree - libstdc++-v3/python/libstdcxx/
- Add Pretty Printers script to cpp project's .gdbinit
python
import sys
sys.path.insert(0, '/usr/share/gcc/python')
from libstdcxx.v6.printers import register_libstdcxx_printers
register_libstdcxx_printers (None)
end
(gdb) i pretty-printer # Check if STL container Pretty Printers have been successfully enabled
(gdb) set print pretty on
Dependencies#
Shared Library#
(gdb) i sharedlibrary # List all shared libraries loaded by the program
(gdb) i sharedlibrary hiredi # List all shared libraries that match the regex hiredi
Macro#
gcc -g3
compiled programs do not contain preprocessor macro information:
(gdb) p NAME No symbol "NAME" in current context.
If you want to view macro information in gdb, you can compile with gcc -g3:
(gdb) p NAME
$1 = "Joe"
Points#
(gdb) i b # Query all breakpoints
(gdb) i watch # List all watchpoints
Source#
In gdb command line, CTRL + X + A brings up the code window in tui, and pressing it again exits the code window.
(gdb) dir <source_directory> # Add path to find source
(gdb) set substitute-path /old /new # Replace old source search path with new path
(gdb) l # Print code at the pause point
(gdb) l <function> # Print code of function function
(gdb) l <file:function> # Print code of function function in file file
(gdb) l <file:line> # Print code at line line in file file
(gdb) l - # Print backward
(gdb) l +
(gdb) focus # Tui mode
gdb -tui program # Start in tui mode
----------------Adjust tui window size
win <win_name> +7 # win_name can be src, cmd, asm, regs window; + or - shows the number of lines
gdb Info#
(gdb) show version # View gdb version information
(gdb) show copying # View gdb copyright information
(gdb) show warranty # View gdb disclaimer
gdb -help # Help information
(gdb) apropos set # Find all command information that matches the set regex
(gdb) help b # help command to get the usage of a specific command
Function Debugging#
Stack Frame Tracking#
stack frame: The stack frame maintains the function execution environment, the Frame Pointer
points to the bottom of the current stack frame, and the Stack Pointer always points to the top of the stack frame.
Each time a function is called, a stack frame
is maintained on the call stack
. Each independent stack frame generally includes:
Stack Pointer→
) local variables
) register
) Frame Pointer
) return address (the address of the next instruction of the current function process)
) arguments (parameter variables created from passed arguments)
) previous Frame Pointer
Frame Pointer→
(gdb) i functions # List the functions defined in the executable file, as well as the referenced linked functions and system functions
(gdb) i functions thre* # List functions whose names match the regex expression thre, the * after the regex indicates whether debug information is included
(gdb) bt # Print the function call stack at the current breakpoint
(gdb) frame # Display the current call stack level, file, line number, etc.
(gdb) frame 2 # Select the second stack frame to view the variables within it
(gdb) up 1 # Switch to the previous stack frame, which is the previous call stack frame
(gdb) down 2
(gdb) up-silently n # Silent switch version
(gdb) down-silently n
(gdb) frame addr # Select the stack frame with address addr, which is the previous stack frame pointed to by the called by frame's Frame Pointer
(gdb) i frame # Print the contents of the currently selected function stack frame, default prints the top stack frame
Stack level 0, frame at 0x7fffffffe590: # Top stack frame, Stack Pointer
rip = 0x40054e in func (a.c:5); # Return address points to the next instruction to be executed in func
saved rip = 0x400577 # The return address of the previous stack frame, i.e., where to jump after the current function finishes executing
called by frame at 0x7fffffffe5a0 # The Frame Pointer of the previous stack frame
source language c. #
Arglist at 0x7fffffffe580, args: a=1, b=2 # Parameter variables
Locals at 0x7fffffffe580, Previous frame's sp is 0x7fffffffe590 # Local variables, the Stack Pointer of the previous stack frame, here since it is the top stack frame, points to itself
Saved registers: #
rbp at 0x7fffffffe580, rip at 0x7fffffffe588 # Registers
---------------------Print Tail Call Stack Frame
# Enable O1 Tail call optimization option during compilation: When a series of functions' last instruction is to call another function, it is optimized to: the function directly calls the final function, while the intermediate function is optimized away at the assembly level
(gdb) set debug entry-values 1 # Set: In addition to outputting normal function stack frame information, also output tail call related information
---------------------Print Local Variables of Each Layer of Function Call Stack
(gdb) bt # Print the function call stack at the current breakpoint
#0 fun_a () at a.c:6
#1 0x000109b0 in fun_b () at a.c:12
(gdb) bt full # Print local variables of each layer
#0 fun_a () at a.c:6
a = 0
#1 0x000109b0 in fun_b () at a.c:12
b = 1
(gdb) bt full n # Print local variables of the top n layers
(gdb) bt full -n # Print local variables of the bottom n layers
Function Execution Control#
(gdb) finish # Run until exiting the current function
(gdb) return val # Do not execute the remaining statements of the function, directly return val
(gdb) call func() # Execute this statement when stopped at a certain point, directly call the func function defined in the executable file
(gdb) print func() # Execute this statement when stopped at a certain point, directly call the func function defined in the executable file
Enter Functions Without Debug Information#
By default, (gdb) s
will not enter functions without debug information and will skip them like (gdb) n
;
Reasons for not having debug information:
- The
-g
option was not enabled to generate debug information, especially for linked shared libraries, the provider did not enable-g
during compilation -O2
or-O3
optimized it away- Inline functions are embedded by the compiler at the call site, so they do not appear as independent functions in debugging. You can disable inline optimization through compilation options.
(gdb) set step-mode on # Set to not skip functions without debug information, even if there is no debug information, you can enter first and then start assembly debugging
$_exitcode for Printing Program Exit Status Code#
(gdb) p $_exitcode
Process/Thread Debugging#
Process/Thread Execution Control#
By default, gdb prints prompt information when it detects that threads are created and exited.
(gdb) set print thread-events off # Turn off print prompts
# To avoid interference from other threads when debugging one thread
(gdb) set scheduler-locking off # By default, gdb only controls the execution of the debugged single thread, while the scheduling of other threads is independent
(gdb) set scheduler-locking on # Lock scheduling, when debugging one thread, other threads are blocked until explicitly switch threads or finish debugging.
(gdb) set scheduler-locking step # When debugging with step, other threads will be paused; when debugging with next, other threads may continue executing.
(gdb) thread apply <id1><id2> command # Execute gdb command for specified thread set
(gdb) thread apply all command # Execute gdb command for specified thread set
Process Debugging#
When debugging multi-process programs, GDB by default controls the execution of the parent process while the child process runs independently.
---------------------------------Debugging non-locking scheduled fork processes: One process gdb controls execution, one runs independently
(gdb) set follow-fork-mode child # GDB pauses at fork(); if (gdb) n tracks the parent process, you can tell gdb to track the child process; this command may not be supported on non-linux
(gdb) start
(gdb) n
(gdb) set follow-fork-mode parent # Default control of parent process execution (default)
(gdb) i inferiors # Display all processes currently being debugged, including parent and child processes
(gdb) inferior <num> # Switch to debugging the specified process num
(gdb) attach <pid> # Equivalent to switching tracking
---------------------------------Debugging locking scheduled fork processes: Parent and child processes are controlled by gdb for alternating execution and concurrent scheduling
(gdb) set detach-on-fork on # Default
(gdb) set detach-on-fork off # Debug both parent and child processes simultaneously
(gdb) start # The program will stop at the fork() call.
(gdb) i inferiors # Display all processes currently being debugged, including parent and child processes
(gdb) inferior <num> # Switch to debugging the specified process num
(gdb) n
---------------------------------Debugging parallel parent and child processes:
(gdb) set schedule-multiple off # Default
(gdb) set detach-on-fork off # Debug both parent and child processes simultaneously
(gdb) set schedule-multiple on # When executing to fork(), the parent and child processes will run in parallel
(gdb) start
---------------------------------Debugging Multi-Process
gdb a
(gdb) start
(gdb) add-inferior -copies 2 -exec b #-copies number of copied processes, -exec specifies the executable file to load
(gdb) i inferiors
Num Description Executable
3 <null> /home/nanxiao/b
2 <null> /home/nanxiao/b
* 1 process 1586 /home/nanxiao/a
(gdb) inferior 2 # Debug process 2
(gdb) clone-inferior -copies 1 # Clone the currently debugged program
(gdb) i inferiors
Num Description Executable
4 <null> /home/nan/b
3 <null> /home/nan/b
* 2 process 1590 /home/nan/x
1 process 1586 /home/nan/a
Print Process Space#
--------------------------Print all process spaces in the debugging session
(gdb) maint info program-spaces # Print all process spaces in the debugging session, including process space number and bound process id
Id Executable
4 /home/nan/b
Bound inferiors: ID 4 (process 0)
3 /home/nan/b
Bound inferiors: ID 3 (process 0)
* 2 /home/nan/b
Bound inferiors: ID 2 (process 15902)
1 /home/nan/a
Bound inferiors: ID 1 (process 15753)
Thread Debugging#
(gdb) i threads # View all thread numbers in the current program, thread id, current call stack function
(gdb) i threads 1 2 # View specified threads
(gdb) thread 2 # GDB switches to debugging thread 2 and displays the current call stack function of that thread
(gdb) wa a thread 2 # Only trigger variable a's watchpoint in thread 2
pstack <pid> # You can dump all thread ids and backtraces of a certain process in advance for easier gdb debugging
$_thread for Thread Debugging#
# Can help you quickly locate potential issues in concurrent programs, such as race conditions, deadlocks, etc.
(gdb) watch a # Monitor variable a
(gdb) command 2 # Set to automatically execute gdb command when breakpoint number 2 is triggered: print the thread modifying a
> printf "thread id=%d\n", $_thread # $_thread will be replaced with the current thread's thread ID
> end
Solaris-Specific Thread Debugging Commands#
(gdb) maint info sol-threads # View all threads in the current program: type, number, corresponding lwp, current thread state, entry function of the thread
user thread #1, lwp 1, (active)
user thread #2, lwp 2, (active) startfunc: monitor_thread
user thread #3, lwp 3, (asleep) startfunc: mem_db_thread
- Sleep func: 0x000aa32c # If the thread is in a sleeping state, the address of the sleep function will also be displayed
Asynchronous Debugging#
Synchronous tasks are operations executed in a specific order (such as multi-threaded mutually exclusive modifications to memory), common synchronization mechanisms (lock mechanisms) will block other tasks during execution.
Asynchronous tasks are operations that require waiting; asynchronous tasks need to efficiently utilize system resources: they need to yield the CPU during operations that require waiting, and then preempt the currently executing task when the operation is completed through some mechanism (such as signals, event loops, callbacks, thread pools, etc.).
signals
Debugging#
GDB provides powerful signal handling control features.
(gdb) i signals # View the default handling method for each signal in the current debugging process
Signal Stop Print Pass to program Description # Signal name, whether to stop the program when capturing the signal, whether to print information, whether GDB will pass it to the program for custom handler processing, description
SIGHUP Yes Yes Yes Hangup
SIGINT Yes Yes No Interrupt # This signal will not be passed to the program for processing, but is used for process control signals
SIGQUIT Yes Yes Yes Quit
...
SIGALRM No No Yes Alarm clock
(gdb) handle <Signal> nostop # Capture SIGHUP signal without stopping the program
(gdb) handle <Signal> stop
(gdb) handle <Signal> noprint
(gdb) handle <Signal> print
(gdb) handle <Signal> nopass # Whether to pass to the program, allowing the program to use custom handle processing
(gdb) handle <Signal> pass
(gdb) signal <Signal> # Send a signal to the debugging program, which is more convenient than switching to shell and using kill to send
(gdb) signal 0 # After the program is paused, use this to make the program run again
$_siginfo Handling Abnormal Exit#
$_siginfo
stores detailed information about the currently received signal. This is particularly useful when handling signals such as SIGSEGV
(segmentation fault) or SIGFPE
(arithmetic error).
(gdb) ptype $_siginfo
type = struct {
int si_signo; # Signal number, indicating the type of signal received.
int si_errno; # Error information related to the signal (usually 0).
int si_code; # Additional information provided by the kernel, describing the specific reason for the signal.
union {
int _pad[28];
struct {...} _kill;
struct {...} _timer;
struct {...} _rt;
struct {...} _sigchld;
struct {...} _sigfault;
struct {...} _sigpoll;
} _sifields; # Contains different struct information based on the type of signal, providing more signal-specific data.
}
(gdb) ptype $_siginfo._sifields._sigfault
type = struct {
void *si_addr;
}
(gdb) p $_siginfo._sifields._sigfault.si_addr # View the memory address that caused the segmentation fault
\$4 = (void *) 0x850e
Deadlock Debugging#
Core Dump#
ulimit -a
-a Display the current resource limit settings.
-c <core file limit> Set the maximum size of core files, in blocks.
-d <data segment size> Maximum size of the program data segment, in KB.
-f <file size> Maximum size of files that the shell can create, in blocks.
-H Set hard limits on resources, which are limits set by the administrator.
-m <memory size> Specify the upper limit of memory that can be used, in KB.
-n <number of files> Specify the maximum number of files that can be opened at the same time.
-p <buffer size> Specify the size of the pipe buffer, in 512-byte units.
-s <stack size> Specify the upper limit of the stack, in KB.
-S Set elastic limits on resources.
-t <CPU time> Specify the upper limit of CPU usage time, in seconds.
-u <number of programs> Maximum number of programs that can be opened by the user.
-v <virtual memory size> Specify the upper limit of virtual memory that can be used, in KB.
Generally, when ulimit -c unlimited
is set; when the program encounters a serious error (such as segmentation fault, illegal instruction, etc.), the program's machine states are dumped into a core file for troubleshooting.
ulimit -c # Check if the system currently allows generating core files; returning 0 indicates that core file generation is disabled. Output a positive integer indicates the size of core files that the system allows to generate
ulimit -c unlimited # Adjust the size limit for generating core files
echo "/coredump/core.%e.%p" > /proc/sys/kernel/core_pattern # Edit /etc/sysctl.conf or /proc/sys/kernel/core_pattern file to set the save path for core files, file name is core.<program name>.<PID>
-------------Generate core
(gdb) generate-core-file # Generate a core file, recording the current debugging process state. The default generated core file name is core.<PID>
(gdb) generate-core-file my_core_dump # Specify a custom file name for the core file
(gdb) gcore # The gcore tool has the same effect as the generate-core-file command in gdb, the default generated core dump file name is core.<PID>
(gdb) gcore my_core_dump
#gcore tool can perform core dump on a running process (even if the process has not crashed), generating a snapshot of that process's memory:
ps aux | grep <your_program> # Find the process's pid
gcore <pid> # Generate core.<PID> file
-------------------Analyze core
gdb -c core.a.1402638140 ./a.out # Debug the core file generated by ./a.out
(gdb) bt full # View the exception backtrace, check where it crashed
(gdb) i locals
# Or load the coredump like this
gdb -q
(gdb) file ./a.out
(gdb) core /coredump/core.a.1402638140
Assembly Debugging#
Print Assembly#
(gdb) disass # Print current assembly instructions
(gdb) disassemble func # Print assembly instructions of func function
(gdb) disas /m func # Display source code along with assembly instructions
(gdb) disassemble /r func # Print machine code of func function in hexadecimal
(gdb) disassemble /mr func # Assembly + machine code
(gdb) i line 13 # View the start and end addresses of the assembly instructions involved in this line
Line 13 of "foo.c" starts at address 0x4004e9 <main+37> and ends at 0x40050c <main+72>.
(gdb) disassemble 0x4004e9, 0x40050c # View the assembly instructions corresponding to the address range
(gdb) set disassemble-next-line off
(gdb) set disassemble-next-line on # Automatically disassemble the code to be executed next
(gdb) set disassemble-next-line auto # Disassemble the code to be executed next only when there is no source code, generally for functions without debug information
(gdb) set disassembly-flavor intel # Intel format, similarly for att it would be at&t format
(gdb) display /ni $pc # Each time returning to the gdb command line, only print the next n assembly instructions to be executed
(gdb) undisplay
(gdb) layout asm # Display assembly code window in tui mode
(gdb) layout split # Assembly and source dual window in tui mode
Assembly Debugging Execution Control#
(gdb) si # Step execute one assembly instruction
(gdb) ni # Do not step into
Set Breakpoint at the First Assembly Instruction of a Function#
-------eg:
#include <stdio.h>
int global_var;
void change_var(){
global_var=100;
}
int main(void){
change_var();
return 0;
}
The command to set a breakpoint at the function: “b func” will not set the breakpoint at the beginning of the function at the assembly instruction level:
(gdb) b main
Breakpoint 1 at 0x8050c12: file a.c, line 9.
(gdb) r
Starting program: /data1/nan/a
[Thread debugging using libthread_db enabled]
[New Thread 1 (LWP 1)]
[Switching to Thread 1 (LWP 1)]
Breakpoint 1, main () at a.c:9
9 change_var();
(gdb) disassemble
Dump of assembler code for function main:
0x08050c0f <+0>: push %ebp
0x08050c10 <+1>: mov %esp,%ebp
=> 0x08050c12 <+3>: call 0x8050c00 <change_var>
0x08050c17 <+8>: mov $0x0,%eax
0x08050c1c <+13>: pop %ebp
0x08050c1d <+14>: ret
End of assembler dump.
To set a breakpoint at the beginning of the function at the assembly instruction level, you need to add * to the function name: “b *func”:
(gdb) b *main
Breakpoint 1 at 0x8050c0f: file a.c, line 8.
(gdb) r
Starting program: /data1/nan/a
[Thread debugging using libthread_db enabled]
[New Thread 1 (LWP 1)]
[Switching to Thread 1 (LWP 1)]
Breakpoint 1, main () at a.c:8
8 int main(void){
(gdb) disassemble
Dump of assembler code for function main:
=> 0x08050c0f <+0>: push %ebp
0x08050c10 <+1>: mov %esp,%ebp
0x08050c12 <+3>: call 0x8050c00 <change_var>
0x08050c17 <+8>: mov $0x0,%eax
0x08050c1c <+13>: pop %ebp
0x08050c1d <+14>: ret
End of assembler dump.
Modify the Binary File of the Debugged Program#
Modify the Binary File of the Debugged Program | 100 gdb Tips
gdb Logging#
gdb does not enable logging by default; if enabled, gdb.txt will be generated in the current directory to record all output results from the gdb command line, facilitating the review of history.
(gdb) show logging # View logging settings
(gdb) set logging on # Set to enable gdb logging
(gdb) set logging off
(gdb) set logging file log.txt # Change the log file path to log.txt
(gdb) set logging redirect on # The output in the GDB session (including command execution results and debugging information) will be written to the log file instead of displayed in the terminal.
(gdb) set logging redirect off
(gdb) set logging overwrite on # Each time GDB starts and logging is enabled, if the log file already exists, it will be recorded in overwrite mode
(gdb) set logging overwrite off # Append mode recording
.gdb_history#
gdb does not save historical commands by default; if saved, historical commands are saved by default in the .gdb_history file in the current directory.
(gdb) set history save on # Enable saving gdb historical commands
(gdb) set history filename ~/.gdbcmd # Set save path
.gdbinit Template#
When gdb starts, it will execute the commands in the .gdbinit script in the HOME directory and the current directory.
C Project#
C++ Project#
# Print contents in STL containers
python
import sys
sys.path.insert(0, "/home/xmj/project/gcc-trunk/libstdc++-v3/python")
from libstdcxx.v6.printers import register_libstdcxx_printers
register_libstdcxx_printers (None)
end
# Save historical commands
set history filename ~/.gdb_history
set history save on
# Do not display prompt information when exiting
set confirm off
# Print objects according to derived types
set print object on
# Print array indices
set print array-indexes on
# Print one struct member per line
set print pretty on
Memory Debugging#
todo#
gdb Multi-window Management
View Object Types
C++ Cross-platform Multithreading
Multithreading Debugging Management
Find Threads, Thread Breakpoints
Execute Commands for Threads
Thread Log Information Control
Call Internal and External Functions
Skip Functions
Create and Debug Release Versions
Create Patches for Software
Memory Leak Detection
Memory Checking
Remote Debugging
Deadlock Debugging
Core Dump Basics
Stack Overflow Core Dump Analysis
Analyze Coredump Without Debug Symbols
Software "Cracking"