It's very basic, but I didn't know it so far, so make a note.
When I set breakpoints on a function in GDB, I didn't know where to stop execution at the machine language level. Immediately before the caller's call? Immediately after the call? After pushing the base pointer? After securing the area for local variables?
From the conclusion, it was after allocating the area of the local variable
.
(That is, the minimum required stack frame processing is executed when called)
The language (standard) is C99.
environment
gcc version 5.4.0 20160609 (Ubuntu 5.4.0-6ubuntu1~16.04.11)
Linux Vega 4.4.0-131-generic #157-Ubuntu SMP Thu Jul 12 15:51:36 UTC 2018 x86_64 x86_64 x86_64 GNU/Linux
Confirmation code
#include <stdio.h>
int func(int a, int b){
int c;
c = a + b;
return(2 * c);
}
int main(void){
int n;
n = func(1, 2);
printf("%d\n", n);
return 0;
}
x86
compile
gcc -m32 -g -O0 -std=c99 -fno-stack-protector test.c
Check with gdb
salacia@Vega:~/tmp/gdb$ gdb a.out
GNU gdb (Ubuntu 7.11.1-0ubuntu1~16.5) 7.11.1
Copyright (C) 2016 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law. Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from a.out...done.
(gdb) b func
Breakpoint 1 at 0x8048411: file test.c, line 5.
(gdb) r
Starting program: /home/salacia/tmp/gdb/a.out
Breakpoint 1, func (a=1, b=2) at test.c:5
5 c = a + b;
(gdb) p $eip
$1 = (void (*)()) 0x8048411 <func+6>
The instruction that stopped was placed at the address 0x8048411
. (Up to the previous instruction has been executed)
Let's look at the instructions before and after.
salacia@Vega:~/tmp/gdb$ objdump -d -M intel a.out
a.out: file format elf32-i386
=============================abridgement=============================
0804840b <func>:
804840b: 55 push ebp
804840c: 89 e5 mov ebp,esp
804840e: 83 ec 10 sub esp,0x10
8048411: 8b 55 08 mov edx,DWORD PTR [ebp+0x8]
8048414: 8b 45 0c mov eax,DWORD PTR [ebp+0xc]
8048417: 01 d0 add eax,edx
8048419: 89 45 fc mov DWORD PTR [ebp-0x4],eax
804841c: 8b 45 fc mov eax,DWORD PTR [ebp-0x4]
804841f: 01 c0 add eax,eax
8048421: c9 leave
8048422: c3 ret
804840b: Save base pointer 804840c: Set the stack pointer to the new base pointer. 804840e: Subtract esp to reserve space for local variables 8048411: Copy the argument to the register for the operation (stopped here), the processing in the function starts here
So, on x86, when I set a breakpoint on a function, I found that it runs up to the stack frame processing
.
I haven't touched the x64 assembler, so I don't know what the result will be, but I did my own research. If you make a mistake, I would appreciate it if you could point it out.
compile
gcc -g -O0 -std=c99 -fno-stack-protector test.c
Check with gdb
salacia@Vega:~/tmp/gdb$ gdb a.out
GNU gdb (Ubuntu 7.11.1-0ubuntu1~16.5) 7.11.1
Copyright (C) 2016 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law. Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from a.out...done.
(gdb) b func
Breakpoint 1 at 0x400530: file test.c, line 5.
(gdb) r
Starting program: /home/salacia/tmp/gdb/a.out
Breakpoint 1, func (a=1, b=2) at test.c:5
5 c = a + b;
(gdb) p $rip
$1 = (void (*)()) 0x400530 <func+10>
It stopped at the instruction at the address 0x400530
.
Let's look at the instructions before and after this address.
salacia@Vega:~/tmp/gdb$ objdump -d -M intel a.out
a.out: file format elf64-x86-64
=============================abridgement=============================
0000000000400526 <func>:
400526: 55 push rbp
400527: 48 89 e5 mov rbp,rsp
40052a: 89 7d ec mov DWORD PTR [rbp-0x14],edi
40052d: 89 75 e8 mov DWORD PTR [rbp-0x18],esi
400530: 8b 55 ec mov edx,DWORD PTR [rbp-0x14]
400533: 8b 45 e8 mov eax,DWORD PTR [rbp-0x18]
400536: 01 d0 add eax,edx
400538: 89 45 fc mov DWORD PTR [rbp-0x4],eax
40053b: 8b 45 fc mov eax,DWORD PTR [rbp-0x4]
40053e: 01 c0 add eax,eax
400540: 5d pop rbp
400541: c3 ret
First, the base pointer is saved and a new base pointer is set.
Next is probably the argument processing part.
The Linux x64 calling convention (System V ABI) attempts to pass arguments in registers before the stack.
Since there are two 4-byte arguments this time, the caller stores the values in ʻedi and ʻesi
.
We have copied these values into the stack frame (instructions 40052a
and 40052d
).
So far, we've reached a breakpoint. Unlike x86, there is no stack pointer subtraction etc. In the case of x64, is this the treatment of stack frame processing? Is it okay to think that?
It's unpleasant to end here, so I'll try a little more.
I looked at a function that does nothing in particular to find out how far the stack frame is processed.
salacia@Vega:~/tmp/gdb$ cat func.c
int func(int a, int b){
int c;
return 0;
}
salacia@Vega:~/tmp/gdb$ objdump -d -M intel func.o
func.o: file format elf64-x86-64
Disassembly of section .text:
0000000000000000 <func>:
0: 55 push rbp
1: 48 89 e5 mov rbp,rsp
4: 89 7d fc mov DWORD PTR [rbp-0x4],edi
7: 89 75 f8 mov DWORD PTR [rbp-0x8],esi
a: b8 00 00 00 00 mov eax,0x0
f: 5d pop rbp
10: c3 ret
Unlike x86, it doesn't seem to subtract the stack pointer. And, after all, the argument is copied to the stack frame. After that, since the return location is put in ʻeax`, it is already in the processing in the function.
I feel that push cannot be done unless the stack pointer is subtracted, but since there are few local variables, does it mean that stack operations are unnecessary?
So, this time I tried using the stack. In x64, if the number of arguments is large and it cannot be passed in the register, the stack will be used, so I called a function with many arguments.
int func2(int a, int b, int c, int d, int e, int f, int g){
return 0;
}
int func(int a, int b){
int c = 0x00;
int d = 0x11;
int e = 0x22;
int f = 0x33;
int g = 0x44;
int h = 0x55;
int i = 0x66;
func2(a, b, c, d, e, f, g);
return 0;
}
salacia@Vega:~/tmp/gdb$ objdump -d -M intel func.o
func.o: file format elf64-x86-64
Disassembly of section .text:
0000000000000000 <func2>:
0: 55 push rbp
1: 48 89 e5 mov rbp,rsp
4: 89 7d fc mov DWORD PTR [rbp-0x4],edi
7: 89 75 f8 mov DWORD PTR [rbp-0x8],esi
a: 89 55 f4 mov DWORD PTR [rbp-0xc],edx
d: 89 4d f0 mov DWORD PTR [rbp-0x10],ecx
10: 44 89 45 ec mov DWORD PTR [rbp-0x14],r8d
14: 44 89 4d e8 mov DWORD PTR [rbp-0x18],r9d
18: b8 00 00 00 00 mov eax,0x0
1d: 5d pop rbp
1e: c3 ret
000000000000001f <func>:
1f: 55 push rbp
20: 48 89 e5 mov rbp,rsp
23: 48 83 ec 28 sub rsp,0x28
27: 89 7d dc mov DWORD PTR [rbp-0x24],edi
2a: 89 75 d8 mov DWORD PTR [rbp-0x28],esi
2d: c7 45 fc 00 00 00 00 mov DWORD PTR [rbp-0x4],0x0
34: c7 45 f8 11 00 00 00 mov DWORD PTR [rbp-0x8],0x11
3b: c7 45 f4 22 00 00 00 mov DWORD PTR [rbp-0xc],0x22
42: c7 45 f0 33 00 00 00 mov DWORD PTR [rbp-0x10],0x33
49: c7 45 ec 44 00 00 00 mov DWORD PTR [rbp-0x14],0x44
50: c7 45 e8 55 00 00 00 mov DWORD PTR [rbp-0x18],0x55
57: c7 45 e4 66 00 00 00 mov DWORD PTR [rbp-0x1c],0x66
5e: 44 8b 4d f0 mov r9d,DWORD PTR [rbp-0x10]
62: 44 8b 45 f4 mov r8d,DWORD PTR [rbp-0xc]
66: 8b 4d f8 mov ecx,DWORD PTR [rbp-0x8]
69: 8b 55 fc mov edx,DWORD PTR [rbp-0x4]
6c: 8b 75 d8 mov esi,DWORD PTR [rbp-0x28]
6f: 8b 45 dc mov eax,DWORD PTR [rbp-0x24]
72: 8b 7d ec mov edi,DWORD PTR [rbp-0x14]
75: 57 push rdi
76: 89 c7 mov edi,eax
78: e8 00 00 00 00 call 7d <func+0x5e>
7d: 48 83 c4 08 add rsp,0x8
81: b8 00 00 00 00 mov eax,0x0
86: c9 leave
87: c3 ret
When performing a stack operation in a function, it seems that the stack pointer is subtracted (0x23
instruction).
Some functions have fewer instructions, so it can be said that they are more optimized than x86.
So where does it stop if you set a function that started using this stack as a breakpoint?
#include <stdio.h>
int func2(int a, int b, int c, int d, int e, int f, int g){
return 0;
}
int func(int a, int b){
int c = 0x00;
int d = 0x11;
int e = 0x22;
int f = 0x33;
int g = 0x44;
int h = 0x55;
int i = 0x66;
func2(a, b, c, d, e, f, g);
return 0;
}
int main(void){
int n;
n = func(1, 2);
printf("%d\n", n);
return 0;
}
salacia@Vega:~/tmp/gdb$ gdb a.out
GNU gdb (Ubuntu 7.11.1-0ubuntu1~16.5) 7.11.1
Copyright (C) 2016 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law. Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from a.out...done.
(gdb) b func
Breakpoint 1 at 0x400553: file test.c, line 8.
(gdb) r
Starting program: /home/salacia/tmp/gdb/a.out
Breakpoint 1, func (a=1, b=2) at test.c:8
8 int c = 0x00;
(gdb) p $rip
$1 = (void (*)()) 0x400553 <func+14>
Look before and after 0x400553
.
0000000000400545 <func>:
400545: 55 push rbp
400546: 48 89 e5 mov rbp,rsp
400549: 48 83 ec 28 sub rsp,0x28
40054d: 89 7d dc mov DWORD PTR [rbp-0x24],edi
400550: 89 75 d8 mov DWORD PTR [rbp-0x28],esi
400553: c7 45 fc 00 00 00 00 mov DWORD PTR [rbp-0x4],0x0
40055a: c7 45 f8 11 00 00 00 mov DWORD PTR [rbp-0x8],0x11
400561: c7 45 f4 22 00 00 00 mov DWORD PTR [rbp-0xc],0x22
400568: c7 45 f0 33 00 00 00 mov DWORD PTR [rbp-0x10],0x33
40056f: c7 45 ec 44 00 00 00 mov DWORD PTR [rbp-0x14],0x44
400576: c7 45 e8 55 00 00 00 mov DWORD PTR [rbp-0x18],0x55
40057d: c7 45 e4 66 00 00 00 mov DWORD PTR [rbp-0x1c],0x66
400584: 44 8b 4d f0 mov r9d,DWORD PTR [rbp-0x10]
400588: 44 8b 45 f4 mov r8d,DWORD PTR [rbp-0xc]
40058c: 8b 4d f8 mov ecx,DWORD PTR [rbp-0x8]
40058f: 8b 55 fc mov edx,DWORD PTR [rbp-0x4]
400592: 8b 75 d8 mov esi,DWORD PTR [rbp-0x28]
400595: 8b 45 dc mov eax,DWORD PTR [rbp-0x24]
400598: 8b 7d ec mov edi,DWORD PTR [rbp-0x14]
40059b: 57 push rdi
40059c: 89 c7 mov edi,eax
40059e: e8 83 ff ff ff call 400526 <func2>
4005a3: 48 83 c4 08 add rsp,0x8
4005a7: b8 00 00 00 00 mov eax,0x0
4005ac: c9 leave
4005ad: c3 ret
It seems that it stopped executing until it copied the argument into the stack frame. From here, the process of assigning the initial value to the local variable has started.
In x64, the following is the stack frame processing, and if you set a breakpoint in the function with gdb, execute it so far
--Save the base pointer and set a new base pointer --Allocate space for local variables (may not be done) --Copy arguments into stack frame
I learned a lot. ~~ (The bonus was the main story) ~~
Recommended Posts