64位彙編移植小結

http://zzhhui.blog.sohu.com/31474896.html

 

 

64位彙編移植小結

這裏的移植工作主要針對從32位x86cpu向intel及amd的64位cpu以及 xinxp64平臺。編譯環境爲vs 2005.net,並且主要涉及彙編語言的移植。所用彙編語言編譯器爲yasm。

因爲對於C語言,在vs2005.net環境下可直接進行64位平臺的編譯,其中注意事項已有許多文章涉及,這裏不再複述。

A、而對於彙編語言,首先要注意,必須爲純彙編格式(*.asm文件)或intrinsic指令格式。其次,在64位平臺上,不建議使用nasm編譯器(我沒找到其對於64位編譯的相關支持),而建議採用yasm,這個彙編編譯器是在nasm的基礎上產生的,可以說對nasm的功能兼容,並且支持64位編譯,詳細地介紹及相關下載見:http://www.tortall.net/projects/yasm/

B、x86-64較x86-32多了8個通用寄存器,而且,每個通用寄存器都是64位寬,它們是:
rax,rbx,rcx,rdx,rsi,rdi,rsp,rbp
r8,r9,r10,r11,r12,r13,r14,r15
同時,x86-64全面支持x86-32和x86-16的通用寄存器:
eax,ax,al,ah,
ebx,bx,bl,bh,

但是,在對寄存器進行入/出棧操作時只能對相應的64位寄存器入/出棧,即:
( Instructions that modify the stack (push, pop, call, ret, enter, and leave) are implicitly 64-bit. Their 32-bit counterparts are not available, but their 16-bit counterparts are. Examples in NASM syntax: 
    push eax  ; illegal instruction
    push rbx  ; 1-byte instruction
    push r11  ; 2-byte instruction with REX prefix)

C、關於x64的調用約定:

在設計調用約定時,x64 體系結構利用機會清除了現有 Win32 調用約定(如 __stdcall、__cdecl、__fastcall、_thiscall 等)的混亂。在 Win64 中,只有一個本機調用約定和 __cdecl 之類的修飾符被編譯器忽略。除此之外,減少調用約定行爲還爲可調試性帶來了好處。

您需要了解的有關 x64 調用約定的主要內容是:它與 x86 fastcall 約定的相似之處。使用 x64 約定,會將前 4 個整數參數(從左至右)傳入指定的 64 位寄存器:

RCX: 1st integer argumentRDX: 2nd integer argumentR8: 3rd integer argumentR9: 4th integer argument

前 4 個以外的整數參數將傳遞到堆棧。該指針被視爲整數參數,因此始終位於 RCX 寄存器內。對於浮點參數,前 4 個參數將傳入 XMM0 到 XMM3 的寄存器,後續的浮點參數將放置到線程堆棧上。

更進一步探究調用約定,即使參數可以傳入寄存器,編譯器仍然可以通過消耗 RSP 寄存器在堆棧上爲其預留空間。至少,每個函數必須在堆棧上預留 32 個字節(4 個 64 位值)。該空間允許將傳入函數的寄存器輕鬆地複製到已知的堆棧位置。不要求被調用函數將輸入寄存器參數溢出至堆棧,但需要時,堆棧空間預留確保它可以這樣做。當然,如果要傳遞 4 個以上的整數參數,則必須預留相應的額外堆棧空間。

讓我們看一個示例。請考慮一個將兩個整數參數傳遞給子函數的函數。編譯器不僅會將值賦給 RCX 和 RDX,還會從 RSP 堆棧指針寄存器中減去 32 個字節。在被調用函數中,可以在寄存器(RCX 和 RDX)中訪問參數。如果被調用代碼因其他目的而需要寄存器,可將寄存器複製到預留的 32 字節堆棧區域中。圖 6 顯示在傳遞 6 個整數參數之後的寄存器和堆棧。


對這裏的參數壓棧,需要說明的是(對於整形參數):
1、第四個參數後的參數按順序反向壓棧(8字節)
2、爲前四個參數預留的32字節佔空間是固定的、預分配的,不用被調用函數的編寫者負責,也就是說,不管你是否用到,這部分棧空間都佔用了,rsp所指向的位置如上圖。
所以對於上面的例子,如果在被調用函數重要訪問p5,p6正確的方法是:
mov eax,[esp+32+8];p5
mov ebx,[esp+32+16];p6

 D、一個很特別的寄存器 rip,相當於x86-32的eip. 在x86-32是不可直接訪問的,如mov eax,eip是錯的,但在x86-64位下卻可以,如 mov,rax,qword ptr [rip+100]是對的。而且,它除了是個程序計數器外,也是個“數據基地址”,有此可見,它現在是身兼兩職!爲什麼在x86-64位下要用rip做訪問數據的基地址呢?因爲,在x86-64下,DS,ES,CS,SS都沒有實際意義了,也就是說,它們不再參與地址計算,只是爲了兼容x86-32。FS,GS還是參與地址計算,它們兩個和x86-32的意義相同。由於DS,ES,CS,SS沒有意義了,所以在編譯動態庫時,符號變量是不允許直接出現在彙編代碼中的,而必須與rip一起使用,即
mov rax,[symb wrt rip]或者lea rbx,[symb wrt rip]

參考網頁:
AMD64 Architecture ---http://www.tortall.net/projects/yasm/wiki/AMD64
開始進行 64 位 Windows 系統編程之前需要了解的所有信息---http://www.microsoft.com/china/MSDN/library/Windev/64bit/issuesx64.mspx?mfr=true
The history of calling conventions, part 5: amd64---http://blogs.msdn.com/oldnewthing/archive/2004/01/14/58579.aspx

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章