2016-06-21 22 views
1

今天我打得四處彙編代碼遞增函數指針創建備用入口點函數:在C中有沒有類似於這個彙編代碼的操作?

.386 
.MODEL FLAT, C 
.DATA 
    INCLUDELIB MSVCRT 
    EXTRN puts:PROC 
    HLO DB "Hello!", 0 
    WLD DB "World!", 0 
.CODE 
    dentry PROC 
     push offset HLO 
     call puts   
     add esp, 4 
     push offset WLD 
     call puts 
     add esp, 4 
     ret 
    dentry ENDP 
    main PROC 
     lea edx, offset dentry 
     call edx 
     lea edx, offset dentry 
     add edx, 13 
     call edx 
     ret 
    main ENDP 
END 

(我知道,在技術上該代碼是無效的,因爲它調用puts沒有CRT被初始化,但它工作時沒有任何彙編或運行時錯誤,至少在MSVC 2010 SP1上)。

請注意,在第二次調用dentry時,我如前所述將edx寄存器中的函數地址與之前一樣,但是這次我增加了它在調用函數之前由13個字節組成。

該程序的輸出因此是:

C:\Temp>dblentry 
Hello! 
World! 
World! 

C:\Temp> 

的「Hello!\nWorld!」第一輸出是從調用該函數的一開始,而第二輸出是從呼叫開始於「 push offset WLD「指令。

我想知道這樣的事情是否存在於從C,Pascal或FORTRAN等彙編語言升級而來的語言中。我知道C不會讓你增加函數指針,但有沒有其他方法來實現這種事情?

+4

看到它一般不會因爲跳繩函數序言會導致各種問題的工作。在更高級的語言中(甚至在這個問題上),你應該把它分成兩個獨立的函數,一個調用另一個函數,讓編譯器選擇最好的實現。 – Jester

+1

我相信較早的FORTRAN支持多個入口點的功能,並且快速的谷歌搜索產生https://gcc.gnu.org/onlinedocs/gcc-3.4.4/g77/Alternate-Entry-Points.html和https:/ /docs.oracle.com/cd/E19957-01/805-4939/6j4m0vn99/index.html – ninjalj

+0

實際的問題是**爲什麼**你想要這樣做?這是一個維護噩夢。如果直到優化:> 99%的程序不需要這個。其餘的東西通常可以通過編譯器進行優化,並且比這種70/80的BASIC黑客更好的結構。 – Olaf

回答

1

可以使用longjmp函數:http://www.cplusplus.com/reference/csetjmp/longjmp/

這是一個相當可怕的功能,但它會做你所追求的。

+0

longjmp只允許您返回到父函數中的一個點。即備份調用樹,而不是成爲當前正在運行的函數的父代的任意函數。這是進行交叉函數跳轉的另一種方式,但實際上並不像OP要求的那樣,因爲使用需求是如此不同。 –

+0

是的,longjmp需要預先執行跳轉條目。但OP示例代碼也是如此。 – Illishar

+1

不,它不! OP的'main()'調用'(dentry + 13)()'。無論初始調用'dentry()'(它在第二次調用之前已經返回),這都會以同樣的方式發生。它甚至不相近。 –

1

AFAIK你只能在asm中寫入多個入口點的函數。

您可以在所有入口點上放置標籤,因此您可以使用普通直接呼叫而不是對第一個函數名稱中的偏移進行硬編碼。

這可以很容易地從C或任何其他語言中調用它們。

如果您擔心混淆不允許函數體重疊的工具(或人類),那麼較早的入口點就像函數落入另一個函數的主體一樣工作。


如果早期的入口點做了一點額外的東西,然後進入主函數,你可能會這樣做。它主要是一種代碼規模節省技術(可能會提高I-cache/uop-cache命中率)。


編譯器傾向於在函數之間重複代碼,而不是在稍微不同的函數之間共享大塊的通用實現。

然而,你可能只用一個額外jmp的東西,如完成它:

int foo(int a) { return bigfunc(a + 1); } 
int bar(int a) { return bigfunc(a + 2); } 

int bigfunc(int x) { /* a lot of code */ } 

見關於Godbolt compiler explorer

foobar尾調用bigfunc,這比其稍微差一個真實的例子bar落入bigfunc。(有foo躍過barbigfunc還是不錯的,尤其,如果bar不是小事。)


跳進一個函數中不一般的安全,因爲不平凡的功能通常需要保存/恢復一些區域。所以序言推動了他們,並且結語將他們彈出。如果跳到中間,那麼序言中的pop會使堆疊不平衡。 (即,將返回地址彈出到寄存器中,並返回到垃圾地址)。

也​​