GDB (GNU debugger): foundation

zhaozj2021-02-16  60

In terms of calling this machine executable (ie, not java * or perl, etc.), use GDB. GDB can be used for source code-level debugging, as well as tracking programs without source code or checking a core file left by a termination program. Unfortunately, when you have never used it, or if you have not used it for some time, it may be difficult to use it. Figure 1 shows each command required for debugging using GDB.

Command Description

FILE Load Program

B set Breakpoint

r run

c Continue

S Step (Line)

Si Step (Machine Instruction)

n next (Step over function call)

Finish Run Until Function Returns

I r show all registers

I R show Specific Register

l List Source

P display value

SET ARGS SET COMMAND LINE ARGUMENTS

figure 1

To use GDB as a source code-level debugger, make sure the program is compiled with the debug symbol; this is the GCC -g option. For start-up GDB, you can achieve the purpose by entering GDB ProgramName (which is GDB SIMPLE) or by running the GDB itself by separately running the GDB itself.

To set basic breakpoints, you can interrupt on a function name or line number. For example, B 27 will set a breakpoint on the 27th line of the current file. There are two ways to use the function name: b main in the first line of the function main executable code, B * Main Sets a breakpoint on the port address of Main (if each of the instructions are intended for single step debugging function, This is useful).

Once the first breakpoint is set, Run or R can be used to start the program and run to the first breakpoint. You can also run the program without any breakpoint, if you don't know where the program crashes, this will be helpful. When you hit a breakpoint C or Continue, the program will resume execute until the next breakpoint is.

STEP "Single Step" debugging source line. Step Instruction (SI) Single-step debug machine code row (when you are subtocate to optimize the optimized code, the Si instruction may be particularly useful, which will be described later). Next works like Step, but it does not track the entry function call (if tracking into function calls if it is wrong, you can use Finish to complete the function, and then interrupt it in its return).

Separate INFO REGISTER (I R) itself displays the value of all registers (except for floating point value), but you can specify a register name. On the 31-bit system, the general-purpose register is named GPR0, GPR1, GPR2, etc .; on the 64-bit system, they are named R0, R1, R2, and the like. Floating point registers follow the same naming convention: FPR0, FPR1, FPR2, etc. on the 31-bit system; F0, F1, F2, and the like are all in the 64-bit system.

"L" lists the source code around the program currently stopped. You can also specify the line number that starts listing the code or the name to be listed. Print allows you to print any variables in the program. One of the best advantage of Print is that it will take out all the values ​​in a structure or allow you to directly reference part of the structure:

Breakpoint 1, Main () at Simple.c: 3030 Boink.boik = & R1;

(GDB) Print boink

$ 3 = {boik = 0x0}

(GDB) Print Boink.boik

$ 4 = (INT *) 0x0

Finally, SET ARGS sets the command line parameters for the program. You can also specify the command line parameters when running RUN, but SET ARGS will make the parameters in the multiple execution of Run.

GDB POST MORTEM

When the program unexpectedly terminated, the kernel will try to generate a core file to determine what error has occurred. However, the core file is usually not generated under the default settings. This can use the ulimit command to change. Ulimit -c unlimited helps ensure that you get the full core file for your application.

Although the core document currently only provides limited values ​​in multi-threaded applications, the 2.5 version of the development kernel has begun to process this problem. It is expected that there is some ideal thread improvement in the core of version 2.6.

Command Description

FILE Load Program

Core Load Core File

Bt Back TRACE

WHERE SAME AS Back Trace

I f Frame Information

Up Move Up Stack

Down Move Down Stack

Frame Jump To Frame

Disassem Display Function's Machine Code

I Locals Display Local Variable Values

figure 2

Figure 2 highlights a series of convenient post mortem commands.

(GDB) FILE SIMPLE

Reading Symbols from Simple ... DONE.

(GDB) Core Core

Core was generated by `/simple '.

Program Terminated with Signal 11, Segmentation Fault.

Reading symbols from /lib/libc.so.6...done.

Loaded Symbols for /Lib/Libc.so.6

Reading symbols from /lib/ld.so.1...done.

Loaded Symbols for /Lib/ld.so.1

# 0 0x400ab738 in memcpy () from /lib/libc.so.6

(GDB) WHERE

# 0 0x400ab738 in memcpy () from /lib/libc.so.6

# 1 0x40066e in main () at Simple.c: 34

# 2 0x40041eb8 in __libc_start_main () from /lib/libc.so.6

# 3 0x4004ac in _start ()

(GDB) I f

Stack Level 0, Frame AT 0x7fffffff7a0:

PSWA = 0x400AB738 in Memcpy; Saved PSWA 0x0

(Frameless), Called by Frame AT 0x7ffff7a0

Arglist At 0x7fffff7a0, Args:

Locals AT 0x7fffff7a0, Previous Frame's SP is 0x0

(GDB) UP

# 1 0x40066e in main () at Simple.c: 34

34 Memcpy (Doink.boik, Boink.boik, Sizeof (Boink.boik);

(GDB) I locals

Doink = {boik = 0x4019a0}

Boink = {boik = 0x0} (gdb) Ptype boink.boik

TYPE = INT *

(GDB) Print * Boink.boik

Cannot Access Memory At Address 0x0

(GDB) Print * DOINK.BOIK

$ 1 = 4

image 3

Figure 3 briefly shows the complete operation of a core program. Similarly, we used the Simple program. But not manual loading programs and core files, but transfer from command line:

GDB SIMPLE CORE

After loading the symbol, GDB will indicate where the program terminates. Note Current Frame # 0 contains the address calculated in the previous section. GDB will cut high in the 31-bit system and only the instruction address is displayed. Also note that frame # 1 contains the return address in GPR14.

Next, I f provides information about the current stack frame. Move it up in the stack frame to main, which is where we leave the frame (ie, the place to call Memcpy). Simple i locals provides a value of a variable passing to Memcpy, one of which is 0x0 of BOINK.BOIK. Use ptype to check the variable type, which will confirm that it is a integer pointer, and if the purpose is to copy the content, it should not be 0x0. The last option is to use Print, to release the pointer reference through an asterisk (*) to receive the value.

Handling optimized code

Previously, I mentioned that when you have an optimized code in the source code level, GDB may become a bit tricky. The compiler optimizes some of the execution order to maximize performance. Figure 4 shows such an example. You can see how the line number switches from 32 to 30 and then switch back to 32.

(GDB) Break main

BreakPoint 1 AT 0x800007A8: File Simple.c, Line 32.

(GDB) R

Starting Program: / Home / Grundym / Foo / Simple

BreakPoint 1, Main () at Simple.c: 32

32 do_one_thing (& doink);

(GDB) S

30 DOINK.BOIK = & r1;

(GDB)

32 do_one_thing (& doink);

(GDB)

Do_one_thing (pnum_times = 0x1ffffffffff / 90) at Simple.c: 47

47 for (i = 0; i <4; i ) {

Figure 4

How to deal with this situation? Use Si and Ni (Next INSTRUCTION; it similar to Si, it will skip subroutines) will be very helpful. At this level, it is very good to understand Zarchitecture is helpful.

(GDB) BREAK * Main

BreakPoint 1 at 0x80000794: File Simple.c, Line 27.

(GDB) Display / i $ PSWA

(GDB) R

Starting Program: / Home / Grundym / Foo / Simple

Breakpoint 1, Main () at Simple.c: 27

27 {

1: X / I $ PSWA 0x80000794: EB AF F0 50 00 24 STMG% R10,% R15, 80 (% R15)

(GDB) Si

0x8000079A 27 {

1: X / I $ PSWA 0x8000079A: B9 04 00 1F LGR% R1,% R15

(GDB)

0x8000079E 27 {

1: X / I $ PSWA 0x8000079E: A7 FB FF 58 AGHI% R15, -168 (GDB)

0x800007a2 in main () at Simple.c: 27

27 {

1: X / I $ PSWA 0x800007A2: E3 10 F0 00 00 24 STG% R1, 0 (% R15)

(GDB)

32 do_one_thing (& doink);

1: X / I $ PSWA 0x800007A8: 41 C0 f0 A0 la% R12, 160 (% R15)

(GDB)

30 DOINK.BOIK = & r1;

1: X / I $ PSWA 0x800007ac: C0 10 00 00 08 C2 LARL% R1, 0X80001930

(GDB)

0x800007B2 30 doink.boik = & r1;

1: X / I $ PSWA 0x800007B2: E3 10 F0 A0 00 24 STG% R1, 160 (% R15)

(GDB)

32 do_one_thing (& doink);

1: X / I $ PSWA 0x800007B8: B9 04 00 2C LGR% R2,% R12

(GDB)

0x800007BC 32 DO_ONE_THING (& DOINK);

1: X / I $ pswa 0x800007bc: c0 E5 FF FF 68 BRASL% R14, 0X8000068C

(GDB)

Do_one_thing (pnum_times = 0x1ffffffff7f8) at Simple.c: 44

44 {

1: X / I $ PSWA 0x8000068C: EB BF F0 58 00 24 STMG% R11,% R15, 88 (% R15)

(GDB)

Figure 5

Figure 5 shows the settings for the program for debugging. First set up a breakpoint at the address of the main (), then set up a Display. Display is an expression that prints related information when the code is stopped. In this example, Display is set to display instructions at the current command address. / i is a format printing as an anti-assembly code, and the current instruction pointer is in value / register (Value / Register) $ PSWA.

Single-step commissioning code, it will be apparent that each machine instruction is associated with a row C code. The front four lines are associated with the 27th line (ie the beginning of the function main). The first four lines are typical function introduction operations, they save registers, stack pointers and adjust the stack. When the associated line number becomes 32, we set a function call to do_one_thing ().

When Display is working, it displays the command of X / I as the actual data display. x is a command to check the memory. / i is formatted in an instruction format; / x will format in a 16-en-format; / a will format it in 16. However, you should regard this value as the address as much as possible, and parses the symbol name.

Display / i $ PSWA

Display / x $ PSWM

Display / a $ r0

Display / a $ R1

Display / a $ R2

Display / a $ R3

Display / a $ R4

Display / a $ R5

Display / a $ R6

Display / a $ R7

Display / a $ R8

Display / a $ r9

Display / a $ r10

Display / a $ R11

Display / a $ R12

Display / a $ R13

Display / a $ R12

Display / a $ R14

Display / a $ R15

Display / 10i $ PSWA

Image 6

When operating in the command level, set some display may be helpful. You can place all the display commands in a file and use the -x option on the command line to specify it. Figure 6 contains the display command that works in the assembler stage. Breakpoint 1, Main () at Simple.c: 27

27 {

20: X / 10i $ PSWA

0x80000794: EB AF F0 50 00 24 STMG% R10,% R15, 80 (% R15)

0x8000079A: B9 04 00 1F LGR% R1,% R15

0x8000079E: A7 FB FF 58 AGHI% R15, -168

0x800007A2: E3 10 F0 00 00 24 STG% R1, 0 (% R15)

0x800007A8: 41 C0 F0 A0 la% R12, 160 (% R15)

0x800007ac: C0 10 00 00 08 C2 LARL% R1, 0X80001930

0x800007B2: E3 10 F0 A0 00 24 STG% R1, 160 (% R15)

0x800007B8: B9 04 00 2C LGR% R2,% R12

0x800007BC: C0 E5 FF FF 68 BRASL% R14, 0X8000068C

0x800007C2: E3 10 F0 A0 00 04 LG% R1, 160 (% R15)

19: / a $ r15 = 0x1fffffffffff698

18: / a $ r14 = 0x10000057B04 <__ libc_start_main 260>

17: / a $ r12 = 0x10000019108 <__ curbrk 280>

16: / a $ r13 = 0x8006c9be

15: / a $ r12 = 0x10000019108 <__ curbrk 280>

14: / a $ r11 = 0x1ffffffff7f8

13: / a $ r10 = 0x80000418 <_init>

12: / a $ r9 = 0x100000198f8 <_dl_debug_mask>

11: / a $ r8 = 0x1000017BEE0

10: / a $ r7 = 0x1

9: / a $ r6 = 0x2

8: / a $ r5 = 0x100001803d8

7: / a $ r4 = 0x1fffffff808

6: / a $ r3 = 0x1ffffffff7f8

5: / a $ r2 = 0x1

4: / a $ r1 = 0x80000794

3: / a $ r0 = 0x1ff00000000

2: / x $ pswm = 0x705c00180000000

1: X / I $ PSWA 0x80000794: EB AF F0 50 00 24 STMG% R10,% R15, 80 (% R15)

(GDB)

Figure 7

This command prints all PSW values, all universal registers and lower 10-row machine code starting from the current command address. Figure 7 shows the results when we interrupt in main (). It can be seen that in some registers, the / A format parsing makes it easier to understand what is happening.

Conclude

For some basic tools that can be used for Linux application debugging, the information in this article should provide you with useful entry information.

转载请注明原文地址:https://www.9cbs.com/read-19653.html

New Post(0)