2012-12-30 61 views
2

在米切爾的書(在編程語言概念)的6.2.1章中,提到:類型轉換在C和類型安全

類型轉換。類型轉換允許將一種類型的值用作另一種類型。特別是在C中,可以將整數轉換爲函數,從而允許跳轉到不包含正確形式的指令的位置作爲C函數。

所以我是爲使用這種非安全性,做一些不尋常的 我想是這樣的(僞代碼):

int x = 0; 
print "loop"; 
x(); 

創建一個無限循環。我嘗試了改變和測試,但我無法應付這種情況。 我該如何做這樣的事情或其他一切?

在此先感謝

+0

它編譯?如果答案是「否」,我建議你刻錄這本書。 – wildplasser

+1

沒有將對象指針轉換爲函數指針的標準符合方式。但是,可能存在特定於平臺的填充具有特定值的函數指針的方法。 –

+0

即使您設法將代碼中該點的地址轉換爲變量,然後將其轉換爲函數,然後調用它,它也不會是無限循環,就像無限遞歸一樣。無論如何,這隻會填滿整個堆棧並崩潰。 – JasonD

回答

3

@Soroush,這裏是一個例子,可以幫助你更好的理解這是怎麼回事幕後:

#include <stdio.h> 

int main(void) 
{ 
    printf("begin\n"); 
    printf("loop\n"); 

    // declare a function pointer 
    int (*loopPtr)(); 
    // set the function pointer to the current function 
    loopPtr = main; 
    // skip over the first printf(); 
    loopPtr += 22; 
    // call the new location 
    loopPtr(); 
} 

對我來說,當clang -O0(當然,它的工作原理,直到堆棧是編譯它工作在x86_64耗盡,因爲這是無限遞歸,每個函數調用通過棧空間咀嚼)。

我通過編譯確定了偏移量22,然後分解並從第二個printf()的地址中減去main()的起始地址。

首先,我編譯它:

clang -O0 test.c 

然後拆開它:

otool -tv a.out 

...它生產的輸出:

[...] 
_main: 
0000000100000ee0 pushq %rbp 
0000000100000ee1 movq %rsp,%rbp 
0000000100000ee4 subq $0x20,%rsp 
0000000100000ee8 leaq 0x00000073(%rip),%rdi 
0000000100000eef movb $0x00,%al 
0000000100000ef1 callq 0x100000f40 
0000000100000ef6 leaq 0x0000006c(%rip),%rdi 
0000000100000efd movl %eax,0xf4(%rbp) 
0000000100000f00 movb $0x00,%al 
0000000100000f02 callq 0x100000f40 
[...] 

_main:表示main()的入口點函數,其首地址是0x100000ee0。第一個callq指令對應於第一個printf()調用,我想跳過,所以我選擇了剛纔的地址:0x100000ef6。 0x100000ef6減去0x100000ee0是十進制22。

6

這不是它的工作原理。

在C語言中,你可以施放一個整數值函數指針的值,然後調用這樣一個函數指針

void (*ptr)() = (void (*)())42; 
ptr(); 

但這可能只是導致崩潰,除非你知道自己在做什麼,即你已經以某種方式知道在地址42處開始一個帶有該簽名的函數;在這個例子中有一個固定的地址可能發生在系統編程中,而在應用程序編程中這是非常罕見的。

什麼實際上發生往往比人們希望(尤其是在Windows編程)是通過圍繞鑄造一些整數函數指針(回調在LPARAM/WPARAM人?),然後抹上他們回到函數指針以實際使用它們。

此外,執行此類強制轉換/調用時發生的情況超出了C標準的範圍,這使得實現可以在這方面自由地執行所需操作。

+0

有沒有什麼辦法可以檢索我的代碼的當前地址,通過這種方式它可能會工作... – Soroush

+2

你可以,雖然沒有好的結果。它肯定不會爲'工作'的任何有意義的定義'工作'。 – JasonD

2

那麼,x()不會神奇地調用該地址的函數。我認爲他的意思是這樣的:

typedef void (*functionPtr)(); 

int x; 
//... 
functionPtr foo = (functionPtr)x; 
//or 
functionPtr goo = (functionPtr)&x; 
foo(); 
+0

我*想*他想將'x'('&x')的地址分配給'foo'。可能是錯誤的。 – amit

+0

@amit ok,同樣的東西(雖然加了) –

+0

@amit鑑於他說他期望有一個無限循環,我寧願認爲他想獲得將'x'設置爲0的指令地址,即他基本上都想'goto'到'x'設置的那一行。 – sepp2k

1

你的書是有誤導性的。標準允許的唯一事情就是投射操作,而且只有在該值適合的情況下。在大多數情況下,執行這樣的函數指針是未定義的行爲。你必須非常瞭解你的系統才能做到這一點。所以你引用段落的第二部分:

在C特別整數可以轉換爲一個功能,允許 跳轉到一個位置不包含的 指令的正確形式爲C功能。

只是不正確的形式。特別是所有體面的現代系統都不允許你執行數據,你必須爲頁面設置特殊標誌,以便它可以被認爲包含可執行代碼。