Guy*_*ham 3 c assembly stack gcc x86-64
我试图完全理解函数调用的堆栈增长机制,我感到有点困惑.为了更好地理解我写了以下简单程序:
#include <stdio.h>
#include <stdint.h>
void callee(uint32_t* p)
{
uint32_t tmp = 9;
printf("callee - tmp is located at address location:%p and p is:%p \n", &tmp, p);
}
void caller()
{
uint32_t tmp1 = 12;
printf("caller - address of tmp1:%p \n", &tmp1);
calle(&tmp1);
}
int main(int argc, char** argv)
{
caller();
return 0;
}
Run Code Online (Sandbox Code Playgroud)
使用在线汇编程序转换器,我得到以下程序集输出(我只留下callee函数的代码):
.LC0:
.string "callee - tmp is located at address location:%p and p is:%p \n"
calle:
push rbp
mov rbp, rsp
sub rsp, 32 // command 1
mov QWORD PTR [rbp-24], rdi
mov DWORD PTR [rbp-4], 9 // command 2
mov rdx, QWORD PTR [rbp-24]
lea rax, [rbp-4]
mov rsi, rax
mov edi, OFFSET FLAT:.LC0
mov eax, 0
call printf
nop
leave
ret
Run Code Online (Sandbox Code Playgroud)
据我所知,考虑到命令1和2(如上所述),当我使用命令编译它时,堆栈确实向低位地址增长,并且编译代码的(样本)输出gcc myProg.c -o prog如下:
调用者 - tmp1的地址:0x7ffe423e8ed4
callee - tmp位于地址:0x7ffe423e8eb4,p为:0x7ffe423e8ed4
在可以看到的情况下,实际上,在callee函数内分配的局部变量位于比caller函数内的局部变量更低的内存地址.太好了.
然而,当我使用-O2选项(即:)编译程序时,gcc -O2 myProg.c -o prog编译代码的(样本)输出如下:
调用者 - tmp1的地址:0x7fff0d5bfa90
callee - tmp位于地址:0x7fff0d5bfa94,p为:0x7fff0d5bfa90
这次,描述了在callee堆栈帧内分配的局部变量位于比caller函数内的局部变量更高的存储器地址中.
所以我的问题是 - -O2优化选项优化"达到"堆栈增长机制实际改变的情况,或者我在这里错过了什么......?
gcc 版本:7.3
架构:x86_64
操作系统:Ubuntu 18.04.
感谢您的澄清.
盖伊.
-O2 内联函数,此时编译器可以随意进行堆栈分配.
单独对象(如tmp和tmp1)之间的地址比较在C中是技术上未定义的行为,因此基于函数嵌套的地址之间的任何类型>或<关系都不是优化在遵循as-if规则时需要保留的可观察副作用.在内联函数时,编译器甚至不会尝试这样做.
ISO C11草案n1548,§6.5.8关系运算符
5)当比较两个指针时,结果取决于指向的对象的地址空间中的相对位置.如果两个指向对象类型的指针都指向同一个对象,或者两个指针都指向同一个数组对象的最后一个元素,则它们相等.如果指向的对象是同一聚合对象的成员,则指向稍后声明的结构成员的指针比指向结构中先前声明的成员的指针大,指向具有较大下标值的数组元素的指针比指向同一数组的元素的指针大.具有较低的下标值.指向同一个union对象的成员的所有指针都比较相等.如果表达式P指向数组对象的元素并且表达式Q指向同一数组对象的最后一个元素,则指针表达式Q + 1比较大于P. 在所有其他情况下,行为未定义
将地址转换为整数uintptr_t,或者将它们打印出来并在脑中进行比较,不是UB,但结果仍然不能保证基于任何东西.