2012-10-04 63 views
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 

(這個例子是很不穩定的,例如通過將一個printf中富,GCC始終打印「我們得到了一個BEAF 「。Clang ..不是..)

上面的例子只是我嘗試和明確的方法。

我真正的問題(和我的目標)如下: 如何提取arg_a的原始地址,即調用者(主)通過使用CLANG從foo函數中將0xbe00推入棧中的地址? (海灣合作委員會是不是一個選項,雖然它仍然很有趣)

感謝您的時間!

編輯:爲了使問題更有意義,提出富可變參數...

+0

聽起來就像你試圖粉碎堆棧。 (Tsk tsk。)你已經做出了一些假設,不會像一般情況那樣起作用。就在我頭頂的一些不好的假設:堆棧的順序,甚至有一個堆棧[編譯器可以註冊通過值,如果他們想要],並存在已知值在該內存中用於調試/溢出檢測。 –

+0

你是正確的!由於代碼將使用MIPS工具鏈進行編譯(通常會通過寄存器$ 4- $ 7中的前4個參數),事情看起來更難...... 請參閱我編輯的編輯.. – yanyan

回答

0

我不認爲這會爲你工作。

首先,如果gcc是你的編譯器,爲什麼不直接使用標準C庫?如果你不想那樣做,那麼從標準實現中只需要stdarg.h呢?

我正在看的stdarg.h的版本(在Windows上帶有gcc 4.4.0)基本上是一堆調用編譯器內建的宏 - 如果你有正確的編譯器,那麼它應該很容易,即使你不使用標準庫的其餘部分(我可以理解不願意把這一切搞亂)。

如果這對您不適用,請嘗試將您正在編寫的函數標記爲可變參數,並查看編譯器文檔以瞭解它如何構建堆棧。您可能需要根據平臺編寫一些彙編代碼片段,但通過將函數聲明爲可變參數,您將強制編譯器以可預測的方式構建堆棧。

如果您的編譯器不支持函數的可變參數,那麼您可能需要重新考慮其他選項。

+0

邁克爾你是非常有幫助的!事實上,stdarg.h是__builtin_函數的一組宏,對於gcc和llvm/clang(我使用第二個函數)差不多。所以我最終實現了可變參數函數的「正確」方式,(儘管「錯誤」的方式更加有趣!) – yanyan

4

總之,不這樣做。

你在做什麼是未定義的行爲。如果你想要arg_b的地址,那麼寫&arg_b。試圖通過使用arg_a使用指針欺騙來獲取它將不會工作,因爲C的別名規則。編譯器假定某些指針不會相互混淆,所以當你這樣做時,優化器會做出不再是假的假設。結果,代碼功能不正確。

+0

謝謝你回答亞當! 我想這樣做的原因是因爲我想爲沒有的嵌入式設備構建可變參數函數(printf實際)。所以寫&arg_b不是一個選項... – yanyan

0

你在做什麼會導致未定義的行爲。

訪問可選參數(從...)的正確方法是從<stdarg.h>使用va_listva_start()va_arg()va_end()宏。

使用這些宏的例子很多,你可以很容易在網上找到它們。

猜測是不允許在這裏。