为什么附加的指针参数在汇编中消失了?
Why did additional pointer arguments disappear in assembly?
C代码:
void PtrArg1(int* a,int* b,int* c, int* d, int* e, int* f)
{
return;
}
void PtrArg2(int* a,int* b,int* c, int* d, int* e, int* f, int* g, int* h)
{
return;
}
编译
gcc -c -m64 -o basics basics.c -O0
运行
objdump -d basics -M intel -r
然后导致以下反汇编(Intel 语法):
000000000000000b <PtrArg1>:
b: f3 0f 1e fa endbr64
f: 55 push rbp
10: 48 89 e5 mov rbp,rsp
13: 48 89 7d f8 mov QWORD PTR [rbp-0x8],rdi
17: 48 89 75 f0 mov QWORD PTR [rbp-0x10],rsi
1b: 48 89 55 e8 mov QWORD PTR [rbp-0x18],rdx
1f: 48 89 4d e0 mov QWORD PTR [rbp-0x20],rcx
23: 4c 89 45 d8 mov QWORD PTR [rbp-0x28],r8
27: 4c 89 4d d0 mov QWORD PTR [rbp-0x30],r9
2b: 90 nop
2c: 5d pop rbp
2d: c3 ret
000000000000002e <PtrArg2>:
2e: f3 0f 1e fa endbr64
32: 55 push rbp
33: 48 89 e5 mov rbp,rsp
36: 48 89 7d f8 mov QWORD PTR [rbp-0x8],rdi
3a: 48 89 75 f0 mov QWORD PTR [rbp-0x10],rsi
3e: 48 89 55 e8 mov QWORD PTR [rbp-0x18],rdx
42: 48 89 4d e0 mov QWORD PTR [rbp-0x20],rcx
46: 4c 89 45 d8 mov QWORD PTR [rbp-0x28],r8
4a: 4c 89 4d d0 mov QWORD PTR [rbp-0x30],r9
4e: 90 nop
4f: 5d pop rbp
50: c3 ret
PtrArg1
和 PtrArg2
的参数数量不同,但两者的汇编指令相同。为什么?
这是调用约定造成的(System V AMD64 ABI,current version 1.0)。前六个参数在整数寄存器中传递,所有其他参数被压入堆栈。
执行到位置 PtrArg2+0x4e
后,您将获得以下堆栈布局:
+----------+-----------------+
| offset | content |
+----------+-----------------+
| rbp-0x30 | f |
| rbp-0x28 | e |
| rbp-0x20 | d |
| rbp-0x18 | c |
| rbp-0x10 | b |
| rbp-0x8 | a |
| rbp+0x0 | saved rbp value |
| rbp+0x8 | return address |
| rbp+0x10 | g |
| rbp+0x18 | h |
+----------+-----------------+
由于 g
和 h
是由调用者推送的,因此您可以对这两个函数进行相同的反汇编。对于来电者
void Caller()
{
PtrArg2(1, 2, 3, 4, 5, 6, 7, 8);
}
(为清楚起见,我省略了必要的转换)我们将得到以下反汇编:
Caller():
push rbp
mov rbp, rsp
push 8
push 7
mov r9d, 6
mov r8d, 5
mov ecx, 4
mov edx, 3
mov esi, 2
mov edi, 1
call PtrArg2
add rsp, 16
nop
leave
ret
在调用 PtrArg2
.
之前,参数 h = 8
和 g = 7
被压入堆栈
消失?您期望编译器发出 asm 指令来执行的函数对它们做什么?
您实际上 return;
是 void
函数中的唯一语句,因此除了 ret
之外,该函数不需要做任何事情。如果您使用像 -O2
这样的正常优化级别进行编译,这就是您所得到的。调试模式的代码通常看起来没什么意思,而且充满了冗余/无用的东西。
您看到某些参数的任何说明的唯一原因是您在调试模式下编译,即默认优化级别 -O0
,反优化调试模式。 每个 C 对象(register
本地对象除外)都有一个内存地址,调试模式确保该值确实存在于内存中 before/after 每个 C 语句。这意味着在函数入口处将寄存器参数溢出到堆栈。 Why does clang produce inefficient asm with -O0 (for this simple floating point sum)?
x86-64 System V ABI的调用约定在寄存器中传递前 6 个整数参数,其余在堆栈中。堆栈参数已经有内存地址;编译器不会发出代码将它们复制到 return 地址下方的其他本地变量旁边;那将毫无意义。被调用者 "owns" 它自己的堆栈参数,即它可以将新值存储到调用者写入 args 的堆栈 space 中,因此 space 可以是 args 的真实地址,即使功能是修改它们。
C代码:
void PtrArg1(int* a,int* b,int* c, int* d, int* e, int* f)
{
return;
}
void PtrArg2(int* a,int* b,int* c, int* d, int* e, int* f, int* g, int* h)
{
return;
}
编译
gcc -c -m64 -o basics basics.c -O0
运行
objdump -d basics -M intel -r
然后导致以下反汇编(Intel 语法):
000000000000000b <PtrArg1>:
b: f3 0f 1e fa endbr64
f: 55 push rbp
10: 48 89 e5 mov rbp,rsp
13: 48 89 7d f8 mov QWORD PTR [rbp-0x8],rdi
17: 48 89 75 f0 mov QWORD PTR [rbp-0x10],rsi
1b: 48 89 55 e8 mov QWORD PTR [rbp-0x18],rdx
1f: 48 89 4d e0 mov QWORD PTR [rbp-0x20],rcx
23: 4c 89 45 d8 mov QWORD PTR [rbp-0x28],r8
27: 4c 89 4d d0 mov QWORD PTR [rbp-0x30],r9
2b: 90 nop
2c: 5d pop rbp
2d: c3 ret
000000000000002e <PtrArg2>:
2e: f3 0f 1e fa endbr64
32: 55 push rbp
33: 48 89 e5 mov rbp,rsp
36: 48 89 7d f8 mov QWORD PTR [rbp-0x8],rdi
3a: 48 89 75 f0 mov QWORD PTR [rbp-0x10],rsi
3e: 48 89 55 e8 mov QWORD PTR [rbp-0x18],rdx
42: 48 89 4d e0 mov QWORD PTR [rbp-0x20],rcx
46: 4c 89 45 d8 mov QWORD PTR [rbp-0x28],r8
4a: 4c 89 4d d0 mov QWORD PTR [rbp-0x30],r9
4e: 90 nop
4f: 5d pop rbp
50: c3 ret
PtrArg1
和 PtrArg2
的参数数量不同,但两者的汇编指令相同。为什么?
这是调用约定造成的(System V AMD64 ABI,current version 1.0)。前六个参数在整数寄存器中传递,所有其他参数被压入堆栈。
执行到位置 PtrArg2+0x4e
后,您将获得以下堆栈布局:
+----------+-----------------+
| offset | content |
+----------+-----------------+
| rbp-0x30 | f |
| rbp-0x28 | e |
| rbp-0x20 | d |
| rbp-0x18 | c |
| rbp-0x10 | b |
| rbp-0x8 | a |
| rbp+0x0 | saved rbp value |
| rbp+0x8 | return address |
| rbp+0x10 | g |
| rbp+0x18 | h |
+----------+-----------------+
由于 g
和 h
是由调用者推送的,因此您可以对这两个函数进行相同的反汇编。对于来电者
void Caller()
{
PtrArg2(1, 2, 3, 4, 5, 6, 7, 8);
}
(为清楚起见,我省略了必要的转换)我们将得到以下反汇编:
Caller():
push rbp
mov rbp, rsp
push 8
push 7
mov r9d, 6
mov r8d, 5
mov ecx, 4
mov edx, 3
mov esi, 2
mov edi, 1
call PtrArg2
add rsp, 16
nop
leave
ret
在调用 PtrArg2
.
h = 8
和 g = 7
被压入堆栈
消失?您期望编译器发出 asm 指令来执行的函数对它们做什么?
您实际上 return;
是 void
函数中的唯一语句,因此除了 ret
之外,该函数不需要做任何事情。如果您使用像 -O2
这样的正常优化级别进行编译,这就是您所得到的。调试模式的代码通常看起来没什么意思,而且充满了冗余/无用的东西。
您看到某些参数的任何说明的唯一原因是您在调试模式下编译,即默认优化级别 -O0
,反优化调试模式。 每个 C 对象(register
本地对象除外)都有一个内存地址,调试模式确保该值确实存在于内存中 before/after 每个 C 语句。这意味着在函数入口处将寄存器参数溢出到堆栈。 Why does clang produce inefficient asm with -O0 (for this simple floating point sum)?
x86-64 System V ABI的调用约定在寄存器中传递前 6 个整数参数,其余在堆栈中。堆栈参数已经有内存地址;编译器不会发出代码将它们复制到 return 地址下方的其他本地变量旁边;那将毫无意义。被调用者 "owns" 它自己的堆栈参数,即它可以将新值存储到调用者写入 args 的堆栈 space 中,因此 space 可以是 args 的真实地址,即使功能是修改它们。