2

我有这个......奇怪的问题,我正在拼命寻找解决方案。

我们有这个例子:(为 32 位系统制作)

#include <stdio.h>

//unsigned foo(unsigned arg_a, unsigned arg_b) {
unsigned foo(unsigned arg_a, ...) {
        unsigned *temp = (unsigned *)((unsigned)&arg_a + 4);
        return *temp + arg_a;
}

int main(void) {
        int i = foo(0xbe00, 0x00af);
        printf("We got a %x\n", i);
        return 0;
}

函数 foo 有 2 个参数。目标是根据 arg_a 的地址“猜测”arg_b 的地址。这是基于调用者 (main) 将 arg_a 和 arg_b 推入堆栈的假设(因此 (int)&arg_b - (int)&arg_a == 4 )。

根据编译器和优化级别,输出会有所不同,如下所示:

 gcc -g -Wall -O0 test.c
 ./a.out 
We got a beaf
 gcc -g -Wall -O1 test.c
 ./a.out 
We got a beaf
 gcc -g -Wall -O2 test.c
 ./a.out 
We got a 80542b0
 gcc -g -Wall -O3 test.c
 ./a.out 
We got a 80542b0
 clang -g -Wall -O0 test.c
 ./a.out 
We got a 8054281
 clang -g -Wall -O1 test.c
 ./a.out 
We got a 805423f
 clang -g -Wall -O2 test.c
 ./a.out 
We got a b768d9d6
 clang -g -Wall -O3 test.c
 ./a.out 
We got a b76899d6

(这个例子非常不稳定,例如通过在 foo 中放置一个 printf,gcc 总是打印“We got a beaf”。Clang.. not..)

上面的例子只是我尝试清楚的方式。

我真正的问题(和我的目标)如下:如何提取 arg_a 的原始地址,即调用者(主)通过使用 foo 函数内部的 CLANG 将 0xbe00 推入堆栈的地址?(gcc 不是一个选项,虽然它仍然很有趣)

谢谢你的时间!

编辑:使 foo 可变参数以使问题更有意义...

4

3 回答 3

4

简而言之,不要那样做。

你正在做的是未定义的行为。如果你想要的地址arg_b,然后写&arg_barg_a由于 C 的别名规则,试图通过使用指针技巧来获得它是行不通的。编译器假定某些指针不会相互别名,因此优化器做出的假设在您这样做时不再正确。结果,代码无法正常运行。

于 2012-10-04T18:11:34.393 回答
0

我不认为这对你有用。

首先,如果 gcc 是你的编译器,你为什么不直接引入标准 C 库呢?如果您不想这样做,那么从标准实现中仅引入 stdarg.h 怎么样?

我正在查看的 stdarg.h 版本(在 Windows 上与 gcc 4.4.0 一起提供)基本上是一堆调用编译器内置函数的宏 - 如果你有适合编译器的宏,那么它应该很容易,即使您不使用标准库的其余部分(我可以理解不愿意将整个混乱拉入其中)。

如果这对您不起作用,请尝试将您正在编写的函数标记为可变参数,并查看编译器文档以了解它如何构建堆栈。您可能必须根据平台编写一些汇编程序片段,但是通过将函数声明为可变参数,您将强制编译器以可预测的方式构建堆栈。

如果您的编译器不支持函数的可变参数,那么您可能需要重新考虑其他选项。

于 2012-10-04T18:50:51.123 回答
0

你在做什么会导致未定义的行为。

访问可选参数 (from ...) 的正确方法是使用from va_listva_start()和宏。va_arg()va_end()<stdarg.h>

使用这些宏的示例很多,您可以轻松地在网上找到它们。

这里不允许猜测。

于 2012-10-05T05:03:17.973 回答