2010-03-25 289 views
2

我有一段C++代碼(在GNU/Linux環境下用g ++編譯)加載函數指針(它的作用如何並不重要),並將一些參數推入堆棧一些內聯彙編,然後調用該函數,代碼如下:32位到64位內聯彙編移植

unsigned long stack[] = { 1, 23, 33, 43 }; 

/* save all the registers and the stack pointer */ 
unsigned long esp; 
asm __volatile__ ("pusha"); 
asm __volatile__ ("mov %%esp, %0" :"=m" (esp)); 

for(i = 0; i < sizeof(stack); i++){ 
    unsigned long val = stack[i]; 
    asm __volatile__ ("push %0" :: "m"(val)); 
} 

unsigned long ret = function_pointer(); 

/* restore registers and stack pointer */ 
asm __volatile__ ("mov %0, %%esp" :: "m" (esp)); 
asm __volatile__ ("popa"); 

我想補充某種

#ifdef _LP64 
    // 64bit inline assembly 
#else 
    // 32bit version as above example 
#endif 

但我不知道64位的機器,任何人都內嵌彙編可以幫助我嗎?

感謝

+0

Simone,我相信你正在尋找應用程序二進制接口ABI。 – 2010-03-25 22:27:39

+0

您是否可以首先了解爲什麼使用內聯彙編調用函數的原因? 當您移動堆棧指針時,用C代碼混合程序塊會很危險。我很高興ret的價值能夠倖免於難。 – nategoose 2010-03-26 18:47:18

回答

4

儘管在內聯程序集中調用帶有適當參數的函數指針應該不是什麼大問題,但我不認爲在x64中重新編碼會幫助你,因爲要使用的調用約定非常有用可能不同(默認情況下32位和64位Linux是完全不同的)。詳情請看here。所以我猜,如果在這種情況下沒有內聯彙編可以離開(參見其他答案),那麼它將更容易移植。

編輯:好的,我看你可能不得不使用程序集。這裏有一些指針。根據Agner Fog的文檔,linux x64使用RDI,RSI,RDX,RCX,R8,R9和XMM0-XMM7進行參數傳輸。這意味着爲了達到您想要的效果(不考慮浮點使用),您的功能將不得不:

(1)保存所有需要保存的寄存器(RBX,RBP,R12-R15):預留堆棧中的空間並將這些寄存器移到那裏。這將是沿(Intel語法)的財產以後行:

sub rsp, 0xSomeNumber1 
mov [rsp+i*8], r# ; insert appropriate i for each register r# to be moved 

(2)計算,你將不得不通過堆棧傳遞給目標函數參數的個數。使用這個來預留堆棧上的所需空間(sub rsp, 0xSomeNumber2),考慮到0xSomeNumber1,這樣堆棧將在最後16字節對齊,即rsp必須是16的倍數。此後不要修改rsp直到你的被調用函數已經返回。 (3)將你的函數參數加載到堆棧上(如果需要的話)和用於參數傳輸的寄存器中。在我看來,最簡單的方法是最後從堆棧參數和加載寄存器參數開始。

;loop over stack parameters - something like this 
mov rax, qword ptr [AddrOfFirstStackParam + 8*NumberOfStackParam] 
mov [rsp + OffsetToFirstStackParam + 8*NumberOfStackParam], rax 

根據你如何設置你的例程,第一個堆棧參數等的偏移可能是不必要的。然後設置的寄存器傳遞的參數數目(跳過那些你不需要):

mov r9, Param6 
mov r8, Param5 
mov rcx, Param4 
mov rdx, Param3 
mov rsi, Param2 
mov rdi, Param1 

(4)使用不同的寄存器從上面的調用目標函數:

call qword ptr [r#] ; assuming register r# contains the address of the target function 

( 5)恢復保存的寄存器,並將其恢復爲其在進入你的功能時的值。如果需要,可將被調用函數的返回值複製到任何你想要的位置。就這樣。

注意:上述草圖沒有考慮要在XMM寄存器中傳遞的浮點值,但同樣的原則適用。 免責聲明:我在Win64上做了類似的事情,但從來沒有在Linux上做過,因此可能會有一些細節我忽略。仔細閱讀,仔細編寫代碼並進行測試。

+0

如果我只能找到一些這些功能的實現的例子來實現我目前用內聯彙編做的很好的XD – 2010-03-25 19:40:29

+0

@Simone:做到這一點,要回答的第一個問題就是被調用函數的x64版本是如何期望將參數傳遞給它們的。 – PhiS 2010-03-25 19:47:55

+0

這應該是'天真',或'本地'?要麼在這方面工作...;) – 2010-03-25 19:51:59

2

沒有真正回答你的問題,但我想你也許可以通過使用setcontext(或makecontext)獨立於平臺的方式實現這一目標。

+0

是的,我認爲你的可能是正確的,我會學習這個功能集,謝謝 – 2010-03-25 18:53:34

1

主要問題:

  • PUSHAD/POPAD是在x64丟失,你必須把你要保存
  • 您需要保存您的RSP(64位堆棧指針),在合適的各個寄存器64位寄存器(RAX ?, R8等?)
  • 調用約定已經從86到64的變化的32位到64位

摘要幾乎肯定改變:

  • 以E開頭的寄存器現在具有以R開頭的64位等效項.RXX,RBX,RCX,RDX,RDI,RSI,RIP,RSP,RBP。
  • 新的寄存器:R8 ...... R15
  • 沒有PUSHAD,無POPAD

我移植一些內嵌x86代碼到x64的Windows。您應該花一些時間閱讀x64指令集,並閱讀操作系統的調用約定。 Windows上的改變是激進的,新的調用約定更簡單。我懷疑GNU/Linux的變化也會有所不同,我絕對不會認爲它是一樣的。

我同意以前的答案,如果您可以使用替代方法而不是本地編碼,請這樣做。就我而言,我無法避免它。

+0

是的,但是使用(set | make)上下文將參數壓入堆棧然後調用通用函數指針的方式是什麼? – 2010-03-25 23:32:22