64位彙編移植小結 (學習學習)

來源:互聯網
上載者:User

這裡的移植工作主要針對從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 個整數參數之後的寄存器和堆棧。

圖 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

這裡的移植工作主要針對從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 個整數參數之後的寄存器和堆棧。

圖 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

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.