Before looking at the assembly has been the naked eye to see the results of gCC-S, the disadvantage is that it is not intuitive, can not see the value of the register in real time, so I studied how to use GDB debugging assembly. Of course, a more important purpose of writing this article is that after six months without blogging, the blog will grow. ^_^

My debugging assembly requirements have several points:

  • Ability to step through assembly debugging.
  • Can see the change of register value in real time.
  • You can see the relationship between the source code and the corresponding assembly.

Let’s share the above three requirements with GDB:

Step through assembly debugging

Use Si and Ni. The difference between S and N is that S and N are step debugging at C language level, while Si and Ni are step debugging at assembly level.

Can see the change of register value in real time.

Add the -tui option when using GDB, and run layout regs after opening GDB. Note that it is best to add -tui, otherwise it is very likely that the screen phenomenon.

You can see the relationship between the source code and the corresponding assembly

Run set disassemble-next-line on in GDB to automatically disassemble the code to be executed later.

Int c=sum(x,y); Corresponds to the assembly instruction in the red box below.

If you don’t want to go this far, you can install a plug-in for GDB or use Emacs to do the same. Two articles are recommended:

  • GDB went from running naked to fully dressed
  • GDB utility plug-in (PEda, GEF, GDBinit) full solution

I’ll end with a small example:

int sum(int x,int y){
        return x+y;
}

int main(){
        int x=10;
        int y=20;
        int c=sum(x,y);

        return 0;
}

GCC version 4.4.7, default optimization options.

Let’s step through the assembly of this code:

To set breakpoints

Note that if you want to set the breakpoint at the beginning of an assembly instruction hierarchy function, you should use b *fun instead of b func. Here we set the breakpoint at b *main

Allocate stack frame

0x0000000000400489 <main+0>:	 55	push   %rbp
0x000000000040048a <main+1>:	 48 89 e5	mov    %rsp,%rbp
0x000000000040048d <main+4>:	 48 83 ec 10	sub    $0x10,%rsp
Copy the code

% RBP and % RSP represent the bottom and top of the current stack frame. Where % RBP is the register that the caller needs to save. Sub $0x10,% RSP allocates stack frame space for main. Note that there is 16 bytes of stack space allocated, there will be 4 bytes not used, I personally suspect that the GCC assembly generated cfi_def_cFA_offset 16, this is not to be delve into.

int x=10

0x0000000000400491 <main+8>:	 c7 45 f4 0a 00	00 00	movl   $0xa,-0xc(%rbp)
Copy the code

Put the value of x on the stack

int y=20

0x0000000000400498 <main+15>:         c7 45 f8 14 00	00 00	movl   $0x14,-0x8(%rbp)
Copy the code

Put the value of y on the stack

Sum function call

 0x000000000040049f <main+22>:         8b 55 f8	mov    -0x8(%rbp),%edx
 0x00000000004004a2 <main+25>:         8b 45 f4	mov    -0xc(%rbp),%eax
 0x00000000004004a5 <main+28>:         89 d6	mov    %edx,%esi
 0x00000000004004a7 <main+30>:         89 c7	mov    %eax,%edi
 0x00000000004004a9 <main+32>:         e8 c6 ff ff ff	callq  0x400474	<sum>
Copy the code

Assign x and y to %esi and %edi, respectively, where % EDI and % ESI are specified to pass the first and second arguments of the function. (A question is why can’t mov -0x8(% RBP),%esi directly?) Callq pushes the address of the next instruction onto the stack and jumps to the first instruction of the sum function.

Enter the sum function

0x0000000000400474 <sum+0>:	 55	push   %rbp
0x0000000000400475 <sum+1>:	 48 89 e5	mov    %rsp,%rbp
0x0000000000400478 <sum+4>:	 89 7d fc	mov    %edi,-0x4(%rbp)
0x000000000040047b <sum+7>:	 89 75 f8	mov    %esi,-0x8(%rbp)
Copy the code

As with main, first save % RBP and then extract the function parameters from %edi and %esi.

sum

0x000000000040047e <sum+10>:	 8b 45 f8	mov    -0x8(%rbp),%eax
0x0000000000400481 <sum+13>:	 8b 55 fc	mov    -0x4(%rbp),%edx
0x0000000000400484 <sum+16>:	 8d 04 02	lea    (%rdx,%rax,1),%eax
Copy the code

Add x and y. The lea directive is used here. Refer to the Lea instruction for the introduction of the LEA directive. I won’t repeat it here. Put the return value in %eax. The % RAx register specifies the return value of the function. In GO, if a function can have more than one return value, the return value is put on the stack.

End of the sum function

0x0000000000400487 <sum+19>:	 c9	leaveq
0x0000000000400488 <sum+20>:	 c3	retq
Copy the code

Let’s look at the current stack:



% RSP = % RBP = % RSP = % RBP Simple verification below, should be so).

At the end of the function, we first need to reclaim the stack frame of the current function, restore the saved register, and restore the value of % RIP, that is, the return address.

Leaveq is equivalent to:

mov  %rbp,%rsp     
pop %rbp
Copy the code

Deallocate frees the stack frame of the current function and restores the saved register value. Remember where % RSP should fall back, otherwise % RSP won’t know where to fall back when the function ends.

The REq instruction is equivalent to:

pop %rip
Copy the code

Restore the next instruction address of the above saved Callq to % RIP.

The return value of the receiving function

0x00000000004004ae <main+37>:         89 45 fc	mov    %eax,-0x4(%rbp)
Copy the code

Put the %eax value into the stack frame of main.

return 0

0x00000000004004b1 <main+40>:         b8 00 00 00 00	mov    $0x0,%eax
Copy the code

Same as the sum function above.

End of main

0x00000000004004b6 <main+45>:         c9	leaveq
0x00000000004004b7 <main+46>:         c3	retq
Copy the code

If % RSP and % RBP point to the same memory area as above, take a look at the current stack space at the end of main:

As with the sum function above, I will not repeat it.

The program runs successfully and exits.