获取正确的参数指针

时间:2012-10-04 18:00:46

标签: c gcc clang callstack

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

我们有这个例子:(为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)&amp; arg_b - (int)&amp; 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

(这个例子很不稳定,例如把一个printf放在foo里面,gcc总是打印出“我们得到了一个beaf”.Clang .. not ..)

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

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

Thanx你的时间!

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

3 个答案:

答案 0 :(得分:4)

简而言之,不要这样做。

您正在做的是未定义的行为。如果您想要arg_b的地址,请写下&arg_b。由于C的别名规则,试图通过使用arg_a的指针欺骗来实现它。编译器假定某些指针不会相互别名,因此优化程序会在您执行此操作时进行不再适用的假设。结果,代码功能不正常。

答案 1 :(得分:0)

我不认为这对你有用。

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

我正在看的stdarg.h的版本(在Windows上附带gcc 4.4.0)基本上是一堆调用编译器内置的宏 - 如果你有正确的编译器,那么应​​该很容易,即使你没有使用标准库的其余部分(我可以理解不愿意把整个混乱局面拉下来)。

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

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

答案 2 :(得分:0)

您正在做的事情导致未定义的行为。

访问可选参数(来自...)的正确方法是使用{{1}中的va_listva_start()va_arg()va_end()宏}}

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

这里不允许猜测。