For those who’re a programmer and also you wish to put a sure performance in your software program, you begin by pondering of how to implement it—similar to writing a way, defining a category, or creating new information varieties. Then you definitely write the implementation in a language that the compiler or interpreter can perceive. However what if the compiler or interpreter doesn’t perceive the directions as you had them in thoughts, though you are certain you probably did every little thing proper? What if the software program works tremendous more often than not however causes bugs in sure circumstances? In these circumstances, you need to know use a debugger appropriately to search out the supply of your troubles.
The GNU Venture Debugger (GDB) is a strong device for locating bugs in packages. It helps you uncover the explanation for an error or crash by monitoring what’s going on inside this system throughout execution.
This text is a hands-on tutorial on primary GDB utilization. To comply with together with the examples, open the command line and clone this repository:
git clone https://github.com/hANSIc99/core_dump_example.git
Each command in GDB may be shortened. For instance,
data break
, which exhibits the set breakpoints, may be shortened to
i break
. You may see these abbreviations elsewhere, however on this article, I’ll write out your entire command in order that it’s clear which operate is used.
You possibly can connect GDB to each executable. Navigate to the repository you cloned, and compile it by working make. It is best to now have an executable referred to as coredump. (See my article on Creating and debugging Linux dump information for extra info..
To connect GDB to the executable, sort: gdb coredump.
Your output ought to seem like this:
It says no debugging symbols had been discovered.
Debugging info is a part of the thing file (the executable) and consists of information varieties, operate signatures, and the connection between the supply code and the opcode. At this level, you’ve got two choices:
To incorporate debug info within the binary file, you need to recompile it. Open the Makefile and take away the hashtag (#) from line 9:
CFLAGS =-Wall -Werror -std=c++11 -g
The g choice tells the compiler to incorporate the debug info. Run make clear adopted by make and invoke GDB once more. It is best to get this output and may begin debugging the code:
The extra debugging info will enhance the scale of the executable. On this case, it will increase the executable by 2.5 occasions (from 26,088 byte to 65,480 byte).
Begin this system with the -c1 change by typing run -c1. This system will begin and crash when it reaches State_4:
You possibly can retrieve further details about this system. The command data supply supplies details about the present file:
The command data shared prints an inventory of dynamic libraries with their addresses within the digital deal with house that was loaded on startup in order that this system will execute:
If you wish to study library dealing with in Linux, see my article The way to deal with dynamic and static libraries in Linux.
You could have observed you can begin this system inside GDB with the run command. The run command accepts command-line arguments such as you would use to start out this system from the console. The -c1 change will trigger this system to crash on stage 4. To run this system from the start, you do not have to give up GDB; merely use the run command once more. With out the -c1 change, this system executes an infinite loop. You would need to cease it with Ctrl+C.
You may also execute a program step-by-step. In C/C++, the entry level is the principle operate. Use the command listing fundamental to open the a part of the supply code that exhibits the principle operate:
The principle operate is on line 33, so add a breakpoint there by typing break 33:
Run this system by typing run. As anticipated, this system stops on the fundamental operate. Kind format src to indicate the supply code in parallel:
You are actually in GDB’s textual content consumer interface (TUI) mode. Use the Up and Down arrow keys to scroll by the supply code.
GDB highlights the road to be executed. By typing subsequent (n), you’ll be able to execute the instructions line by line. GBD executes the final command in the event you do not specify a brand new one. To step by the code, simply hit the Enter key.
Infrequently, you’ll discover that TUI’s output will get a bit corrupted:
If this occurs, press Ctrl+L to reset the display screen.
Use Ctrl+X+A to enter and go away TUI mode at will. You will discover different key bindings within the guide.
To give up GDB, merely sort give up.
The center of this instance program consists of a state machine working in an infinite loop. The variable n_state is a straightforward enum that determines the present state:
whereas(true){
change(n_state){
case State_1:
std::cout << “State_1 reached” << std::flush;
n_state = State_2;
break;
case State_2:
std::cout << “State_2 reached” << std::flush;
n_state = State_3;
break;
(…..)
}
}
You wish to cease this system when n_state is about to the worth State_5. To take action, cease this system on the fundamental operate and set a watchpoint for n_state:
watch n_state == State_5
Setting watchpoints with the variable title works provided that the specified variable is obtainable within the present context.
Once you proceed this system’s execution by typing proceed, it’s best to get output like:
For those who proceed the execution, GDB will cease when the watchpoint expression evaluates to false:
You possibly can specify watchpoints for normal worth modifications, particular values, and skim or write entry.
Kind data watchpoints to print an inventory of beforehand set watchpoints:
As you’ll be able to see, watchpoints are numbers. To delete a selected watchpoint, sort delete adopted by the variety of the watchpoint. For instance, my watchpoint has the quantity 2; to take away this watchpoint, enter delete 2.
Warning: For those who use delete with out specifying a quantity, all watchpoints and breakpoints can be deleted.
The identical applies to breakpoints. Within the screenshot beneath, I added a number of breakpoints and printed an inventory of them by typing data breakpoint:
To take away a single breakpoint, sort delete adopted by its quantity. Alternatively, you’ll be able to take away a breakpoint by specifying its line quantity. For instance, the command clear 78 will take away breakpoint quantity 7, which is about on line 78.
As a substitute of eradicating a breakpoint or watchpoint, you’ll be able to disable it by typing disable adopted by its quantity. Within the following, breakpoints Three and Four are disabled and are marked with a minus signal within the code window:
It’s also doable to switch a variety of breakpoints or watchpoints by typing one thing like disable 2 – 4. If you wish to reactivate the factors, sort allow adopted by their numbers.
First, take away all breakpoints and watchpoints by typing delete. You continue to need this system to cease on the fundamental operate, however as a substitute of specifying a line quantity, add a breakpoint by naming the operate instantly. Kind break fundamental so as to add a breakpoint on the fundamental operate.
Kind run to start out the execution from the start, and this system will cease on the fundamental operate.
The principle operate consists of the variable n_state_3_count, which is incremented when the state machine hits state 3.
So as to add a conditional breakpoint primarily based on the worth of n_state_3_count sort:
break 54 if n_state_3_count == 3
Proceed the execution. This system will execute the state machine 3 times earlier than it stops at line 54. To examine the worth of n_state_3_count, sort:
print n_state_3_count
It’s also doable to make an current breakpoint conditional. Take away the not too long ago added breakpoint with clear 54, and add a easy breakpoint by typing break 54. You can also make this breakpoint conditional by typing:
situation Three n_state_3_count == 9
The three refers back to the breakpoint quantity.
In case you have a program that consists of a number of supply information, you’ll be able to set breakpoints by specifying the file title earlier than the road quantity, e.g., break fundamental.cpp:54.
Along with breakpoints and watchpoints, you may also set catchpoints. Catchpoints apply to program occasions like performing syscalls, loading shared libraries, or elevating exceptions.
To catch the write syscall, which is used to write down to STDOUT, enter:
catch syscall write
Every time this system writes to the console output, GDB will interrupt execution.
Within the guide, yow will discover a complete chapter masking break-, watch-, and catchpoints.
Printing the values of variables is finished with the print command. The overall syntax is print <expression> <worth>. The worth of a variable may be modified by typing:
set variable <variable-name> <new-value>.
Within the screenshot beneath, I gave the variable n_state_3_count the worth 123.
The /x expression prints the worth in hexadecimal; with the & operator, you’ll be able to print the deal with inside the digital deal with house.
If you’re undecided of a sure image’s information sort, yow will discover it with whatis:
If you wish to listing all variables which can be accessible within the scope of the principle operate, sort data scope fundamental:
The DW_OP_fbreg values seek advice from the stack offset primarily based on the present subroutine.
Alternatively, in case you are already inside a operate and wish to listing all variables on the present stack body, you should utilize data locals:
Examine the guide to be taught extra about inspecting symbols.
The command gdb connect <process-id> lets you connect to an already working course of by specifying the method ID (PID). Fortunately, the coredump program prints its present PID to the display screen, so you do not have to manually discover it with ps or high.
Begin an occasion of the coredump utility:
./coredump
The working system provides the PID 2849. Open a separate console window, transfer to the coredump utility’s supply listing, and fasten GDB:
gdb connect 2849
GDB instantly stops the execution once you connect it. Kind format src and backtrace to look at the decision stack:
The output exhibits the method interrupted whereas executing the std::this_thread::sleep_for<…>(…) operate that was referred to as in line 92 of fundamental.cpp.
As quickly as you give up GDB, the method will proceed working.
You will discover extra details about attaching to a working course of within the GDB guide.
Return to this system by utilizing up two occasions to maneuver up within the stack to fundamental.cpp:
Often, the compiler will create a subroutine for every operate or technique. Every subroutine has its personal stack body, so transferring upwards within the stackframe means transferring upwards within the callstack.
You will discover out extra about stack analysis within the guide.
When attaching to an already working course of, GDB will search for the supply information within the present working listing. Alternatively, you’ll be able to specify the supply directories manually with the listing command.
Learn Creating and debugging Linux dump information for details about this subject.
TL;DR:
The output of backtrace exhibits that the crash occurred 5 stack frames away from fundamental.cpp. Enter to leap on to the defective line of code in fundamental.cpp:
A have a look at the supply code exhibits that this system tried to free a pointer that was not returned by a reminiscence administration operate. This ends in undefined conduct and induced the SIGABRT.
If there aren’t any sources accessible, issues get very onerous. I had my first expertise with this when attempting to unravel reverse-engineering challenges. It’s also helpful to have some information of meeting language.
Try the way it works with this instance.
Go to the supply listing, open the Makefile, and edit line 9 like this:
CFLAGS =-Wall -Werror -std=c++11 #-g
To recompile this system, run make clear adopted by make and begin GDB. This system not has any debugging symbols to paved the way by the supply code.
The command data file reveals the reminiscence areas and entry level of the binary:
The entry level corresponds with the start of the .textual content space, which incorporates the precise opcode. So as to add a breakpoint on the entry level, sort break *0x401110 then begin execution by typing run:
To arrange a breakpoint at a sure deal with, specify it with the dereferencing operator *.
Earlier than digging deeper into meeting, you’ll be able to select which meeting taste to make use of. GDB’s default is AT&T, however I want the Intel syntax. Change it with:
set disassembly-flavor intel
Now open the meeting and register the window by typing format asm and format reg. It is best to now see output like this:
Though you’ve got already entered many instructions, you have not truly began debugging. If you’re closely debugging an utility or attempting to unravel a reverse-engineering problem, it may be helpful to save lots of your GDB-specific settings in a file.
The config file gdbinit on this challenge’s GitHub repository incorporates the not too long ago used instructions:
set disassembly-flavor intel
set write on
break *0x401110
run -c2
format asm
format reg
The set write on command lets you modify the binary throughout execution.
Stop GDB and reopen it with the configuration file: gdb -x gdbinit coredump.
With the c2 change utilized, this system will crash. This system stops on the entry operate, so you need to write proceed to proceed with execution:
The idiv instruction performs an integer division with the dividend within the RAX register and the divisor specified as an argument. The quotient is loaded into the RAX register, and the rest is loaded into RDX.
From the register overview, you’ll be able to see the RAX incorporates 5, so you need to discover out which worth is saved on the stack at place RBP-0x4.
To learn uncooked reminiscence content material, it’s essential to specify just a few extra parameters than for studying symbols. Once you scroll up a bit within the meeting output, you’ll be able to see the division of the stack:
You are most within the worth of rbp-0x4 as a result of that is the place the place the argument for idiv is saved. From the screenshot, you’ll be able to see that the subsequent variable is situated at rbp-0x8, so the variable at rbp-0x4 is Four bytes huge.
In GDB, you should utilize the x command to look at any reminiscence content material:
x/ < non-obligatory parameter n f u > < reminiscence deal with addr >
Non-compulsory parameters:
To print out the worth at rbp-0x4, sort x/u $rbp-4:
For those who preserve this sample in thoughts, it is easy to look at the reminiscence. Examine the inspecting reminiscence part within the guide.
The arithmetic exception occurred within the subroutine zeroDivide(). Once you scroll a bit upward with the Up arrow key, yow will discover this sample:
0x401211 <_Z10zeroDividev> push rbp
0x401212 <_Z10zeroDividev+1> mov rbp,rsp
That is referred to as the operate prologue:
Skip this subroutine utterly. You possibly can examine the decision stack with backtrace. You might be just one stack body forward of your fundamental operate, so you’ll be able to return to fundamental with a single up:
In your fundamental operate, yow will discover this sample:
0x401431 <fundamental+497> cmp BYTE PTR [rbp-0x12],0x0
0x401435 <fundamental+501> je 0x40145f <fundamental+543>
0x401437 <fundamental+503> name 0x401211<_Z10zeroDividev>
The subroutine zeroDivide() is entered solely when bounce equal (je) evaluates to true. You possibly can simply substitute this with a jump-not-equal (jne) instruction, which has the opcode 0x75 (supplied you might be on an x86/64 structure; the opcodes are completely different on different architectures). Restart this system by typing run. When this system stops on the entry operate, manipulate the opcode by typing:
set *(unsigned char*)0x401435 = 0x75
Lastly, sort proceed. This system will skip the subroutine zeroDivide() and will not crash anymore.
You will discover GDB working within the background in lots of built-in improvement environments (IDEs), together with Qt Creator and the Native Debug extension for VSCodium.
It is helpful to know leverage GDB’s performance. Often, not all of GDB’s features can be utilized from the IDE, so that you profit from having expertise utilizing GDB from the command line.
Leave a Reply
You must be logged in to post a comment.