A hands-on tutorial for utilizing the GNU Venture Debugger

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

Shortcuts

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.

Command-line parameters

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:

  • Proceed debugging the meeting (see “Debug with out symbols” beneath)
  • Compile with debug info utilizing the data within the subsequent part

Compile with debug info

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:

  • 101 strains
  • Language: C++
  • Compiler (model, tuning, structure, debug flag, language customary)
  • Debugging format: DWARF 2
  • No preprocessor macro info accessible (when compiled with GCC, macros can be found solely when compiled with the -g3 flag).

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.

Debug this system

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.

Watchpoints

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.

Altering breakpoints and watchpoints

Kind data watchpoints to print an inventory of beforehand set watchpoints:

Delete breakpoints and 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.

Disable or allow breakpoints and watchpoints

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.

Conditional breakpoints

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

Make breakpoints conditional

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.

Set breakpoints in different supply information

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.

Catchpoints

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.

Consider and manipulate symbols

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.

Connect to a working course of

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.

Transfer by the stack

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.

Specify the supply information

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.

Consider dump information

Learn Creating and debugging Linux dump information for details about this subject.

TL;DR:

  1. I assume you are working with a latest model of Fedora
  2. Invoke coredump with the c1 change: coredump -c1
  3. Load the most recent dumpfile with GDB: coredumpctl debug
  4. Open TUI mode and enter format src

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.

Debug with out symbols

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 *.

Select the disassembler taste

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:

Save configuration information

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.

Learn directions

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.

Learn reminiscence

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:

  • n: Repeat rely (default: 1) refers back to the unit dimension
  • f: Format specifier, like in printf
  • u: Unit dimension
    • b: bytes
    • h: half phrases (2 bytes)
    • w: phrase (Four bytes)(default)
    • g: large phrase (eight bytes)

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.

Manipulate the meeting

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:

  1. The bottom pointer (rbp) of the calling operate is saved on the stack
  2. The worth of the stack pointer (rsp) is loaded to the bottom pointer (rbp)

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.

Conclusion

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.

Supply

Germany Devoted Server

Leave a Reply