【发布时间】:2023-02-24 08:03:15
【问题描述】:
最近学习了Assembly x86,里面的函数是怎么实现的,栈程序是怎么工作的。但是,我尝试编写这个程序,它通过改变当前被调用函数(f1)的返回地址来调用函数f2,这样指令指针在完成f1时就开始f2,因此不会直接返回到main。它似乎不稳定,有时我会遇到分段错误,而在另一种情况下它可以工作但不返回 0。这是为什么?我的猜测是程序堆栈在运行时没有在内存中分配连续的空间,因此它的行为不是恒定的。有时,如果更改“v[2] = (uintptr_t) f2;”,它会起作用。进入“v[another_index_greater_than_2] = (uintptr_t) f2;”。这很奇怪,因为理论上 v[1] 应该是压入堆栈的旧基指针,而 v[2] 应该是函数的返回地址。
#include <iostream>
using namespace std;
int main();
void f2()
{
int v[1];
cout << "f2\n";
v[2] = (uintptr_t) main;
}
void f1()
{
int v[1];
cout << "f1\n";
v[2] = (uintptr_t) f2;
}
int main()
{
f1();
cout << "Back to main";
return 0;
}
我希望看到按顺序打印的 3 个字符串 (f1、f2、main) 和程序返回 0,但程序的行为似乎是随机的。
【问题讨论】:
-
也许堆栈上的数据比您预期的要多?您使用的编译器是什么?什么是目标系统?使用的 ABI 是什么?堆栈框架是什么样的?
-
另请注意,目前确实没有具有 64 位
int类型的系统,而 64 位系统上的指针是 64 位的。将 64 位存储在 32 位类型中效果不佳。 -
我在 Windows CodeBlocks 中编写代码并使用 GNU GCC 编译
-
作为测试,编译代码
#include <iostream> int main() { std::cout << sizeof(int*); }。如果该值为8,那么您正在为 x64 进行编译,并且指针值不适合 int 给您带符号整数溢出和未定义的行为。 -
这显然是未定义的行为,因此任何期望都是不正确的。例如,编译器可以看到越界访问并完全忽略它。它可能适用于特定 ABI 上特定版本的特定编译器,但通常不能以任何方式移植。