1

以下简短的c程序:

void foo(int a, int b) {
        printf("a = %p b = %p\n", &a, &b);
}

main() {
        foo(1, 2);
}

好的,现在我用gdb来查看这个程序。我得到了输出:

a = 0x7fff5fbff9ac b = 0x7fff5fbff9a8

并在输出后停止执行(在 foo() 中)。现在我检查了 0x7fff5fbff9ac ,内容是:

1....正确

然后是 0x7fff5fbff9a8 和内容:

2...正确

现在我想查看函数的返回地址并检查(a + 4 个字节):

x/g 0x7fff5fbff9b1(8 个字节!!地址,因此是“g”(巨字))

其内容是:

(gdb) x/g 0x7fff5fbff9b1
0x7fff5fbff9b1: 0xd700007fff5fbff9

但是:这不是 MAIN 的退货广告!我的错在哪里?

4

3 回答 3

3

你的问题中有一大堆错误的假设。

您假设整数参数在返回地址正上方的堆栈上传递(因为它们在默认调用约定下的许多--not all --x86 ABI 中)。如果是这种情况,那么在调用之后,您的堆栈将如下所示:

// stack frame of main( )
// ...
value of b
value of a
return address <--- stack pointer

但是,您的假设是不正确的。您已将代码编译为 64 位可执行文件(如您正在打印的指针的大小所证明的那样)。根据 OS X ABI,在 64 位 Intel 可执行文件中,前几个整数参数在寄存器中传递,而不是在堆栈中。因此,在调用之后,堆栈实际上看起来像这样:

// stack frame of main( )
// ...
return address <--- stack pointer

由于您获取 and 的地址ab它们将在调用之前的某个时间写入堆栈printf( )(除非编译器非常聪明,并且意识到它实际上不需要传递printf( )有效指针,因为它不会使用指向的值,但是随着优化的进行,那将是非常邪恶的),但是您真的不知道它们相对于返回地址的位置;事实上,因为 64 位 ABI 提供了一个红色区域,你甚至不知道它们是在堆栈指针之上还是之下。因此,在您打印出 a 和 b 的地址时,您的堆栈如下所示:

// stack frame of main( )
// ...
return address                     |
// ...                             |
// a and b are somewhere down here | <-- stack pointer points somewhere in here
// ...                             |

一般来说,C 语言标准没有说明堆栈布局,甚至根本不需要堆栈。您无法以任何可移植方式从 C 代码中获取此类信息。

于 2010-12-09T00:34:57.057 回答
1

您做错的事情是对给定平台上堆栈框架的布局做出一大堆不正确和随机的假设。你从哪里得到关于“a + 4字节”位置应该保存返回地址的奇怪想法?

如果您真的想这样做,请获取您平台的文档(或进行一些逆向工程)以找出返回地址的存储位置和准确方式。进行随机猜测,然后询问其他人为什么您的随机猜测没有产生您出于某种原因期望的结果,这并不是一种有效的方法。

于 2010-12-09T00:39:44.600 回答
1

首先,&a + 4is 0x7FFF5FBFF9B0,因此您正在寻找与您认为的位置相距一个字节的偏移量。

其次,保存的帧指针位于a和返回地址之间,这就是您看到的值。

于 2010-12-09T00:19:37.463 回答