精品国产人成在线_亚洲高清无码在线观看_国产在线视频国产永久2021_国产AV综合第一页一个的一区免费影院黑人_最近中文字幕MV高清在线视频

0
  • 聊天消息
  • 系統消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發帖/加入社區
會員中心
創作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

在Rust中使用內聯匯編

jf_wN0SrCdH ? 來源:Rust語言中文社區 ? 2023-05-04 09:54 ? 次閱讀

自 Rust 1.59 以降,在 Rust 代碼中內聯匯編代碼的語言特性已然 stable^1。參考知乎上一篇文章^3,我用 Rust 的內聯匯編實現了有棧協程^4。在此過程中學到了一些知識。

本文假設讀者對 x86 匯編有基礎了解。

局部內聯匯編與自動分配寄存器

Rust 的內聯匯編一開始是對標 GCC 的內聯匯編設計的,長得像這樣^5:


	

asm!("mov $4, %eax cpuid mov %eax, $0 mov %ebx, $1 mov %ecx, $2 mov %edx, $3" : "=r"(a), "=r"(b), "=r"(c), "=r"(d) : "m"(info) : "eax", "ebx", "ecx", "edx" )

后來才變成如今富有 Rust 特色的樣子^6:


	

asm!( "mov edi, ebx", "cpuid", "xchg edi, ebx", in("eax") info, lateout("eax") a, out("edi") b, out("ecx") c, out("edx") d, )

與 GCC 內聯匯編語法一樣,Rust 希望即使需要手寫匯編,程序員也能將一部分工作交給編譯器來高效完成,這部分工作就是寄存器分配,畢竟只有編譯器了解內聯匯編前后的上下文,知道該怎么分配寄存器最合適。

asm宏的in,out,inout,lateout,inlateout參數就是為了讓編譯器幫助分配寄存器的。

in表示將變量的值傳給寄存器,編譯器生成的匯編代碼會使得在內聯匯編代碼中讀取相應的寄存器,就得到了傳入的變量的值;

out表示將寄存器的值寫到變量中,在內聯匯編代碼中寫入相應寄存器,編譯器在內聯匯編之后生成的匯編代碼會使得相應變量具有寫入相應寄存器的值;

late則是代表編譯器可以采取進一步的策略來優化寄存器分配:默認的分配策略給每個參數分配不同的寄存器,使用lateoutinlateout的參數則允許編譯器復用某個in參數的寄存器,只要內聯匯編代碼中先讀完所有的in寄存器,再輸出lateoutinlateout寄存器即可。

具體細節以及此處沒講到的option可參考^1。

全局內聯匯編與名稱修飾(Name Mangling)

除了需要寫在函數體中的asm宏,還有需要寫在函數之外的global_asm宏,其作用與獨立的匯編代碼相差不大,一切全由程序員掌控,沒有上節所述寄存器自動分配之功能,還需要手動管理參數傳遞,棧對齊等等。

global_asm我們可以寫出源代碼完全是匯編代碼的函數,函數名就是匯編代碼中的標簽,函數參數和返回值需要按照 ABI 約定來處理^7:


	

use std::global_asm; extern "C" { fn my_asm_add(a: i32, b: i32) -> i32; } global_asm!{ "my_asm_add:", "mov eax, edi", "add eax, esi", "ret", } fn main() { let a = 114; let b = 514; let x = unsafe { my_asm_add(a, b) }; dbg!(x); }

這段代碼在x86_64-unknown-linux-gnu的目標,也就是 Rust Playground 的運行環境下會通過編譯并輸出正確結果 628,但在 64 位 Windows 下則會得到錯誤的結果,因為 64 位 Windows 所用的 C ABI 和 64 位 Linux 不一樣,雖然都是通過寄存器傳遞參數,但 64 位 Windows 的 C ABI 的第一二參數是用 RCX 和 RDX 傳遞,而非示例中的 RDI 和 RSI。

而在MacOS上編譯,結果是編譯不過——雖然和 64 位 Linux 一樣使用 System V AMD64 ABI,但 MacOS 進行 C 語言函數名名稱修飾時會在函數名前加一個下劃線,所以編譯器會試圖尋找_my_asm_add符號,結果找不到。在匯編代碼中把"my_asm_add:"改成"_my_asm_add:"即可編譯通過。

由此可見匯編語言的不可移植性:即使是同一架構,甚至同一 ABI 約定的匯編代碼也相當不可移植。

在代碼編寫過程中,我發現一個技巧可以規避掉名稱修飾的影響。asmglobal_asm宏可以接受格式為sym SYMBOL的參數來引用符號,其中SYMBOL是函數或者靜態變量,這種參數的目的是在匯編語言中直接引用 Rust 函數或靜態變量的符號,盡管 Rust 的名稱修飾算法尚未 stable,但代碼中可以不寫出來而由編譯器來計算。這個功能也可以用在extern符號上,因此可以這樣寫:


	

global_asm!{ ".extern {0}", "{0}:", "mov eax, edi", "add eax, esi", "ret", sym my_asm_add, }

這樣在編譯 x86_64-unknown-linux-gnu 目標時生成的匯編代碼中的標簽是my_asm_add,而對于x86_64-apple-darwin目標,生成的標簽則是_my_asm_add

這樣的技巧不夠方便,更直觀的寫法是 naked function^9,這種函數從外部看來就是一個 unsafe 函數,而內部只允許有一個asm宏調用,編譯器不生成一般函數中會有的各種上下文代碼,函數本體完全由該 asm 宏調用生成。

程序重定位與位置無關代碼

為了加載動態鏈接庫或者避免被黑客利用固定程序地址攻擊,操作系統加載程序時會將其載入到隨機的內存地址,這個過程就是程序重定位。

對于 32 位 x86 程序,需要在加載時修改程序中所有的絕對地址,包括函數的和數據的。在匯編語言中可以直接將標號作為常量使用,但最好不要寫mov eax, LABEL這樣的語句,因為這樣的語句加載器不會識別和修改。應該寫lea eax, LABEL。

x86_64 支持相對 RIP 尋址,Rust 編譯器默認將代碼編譯為使用這項特性的位置無關可執行程序(PIE),因此在 Rust 的內聯匯編中取符號地址需要寫成lea rax, [rip+SYMBOL]。

示例,x86_64 平臺下給 static 變量X加 1 的函數用匯編語言實現[11]:


	

use std::global_asm; static mut X: usize = 0; extern "C" { fn incr_x(); } global_asm!{ "{0}:", "add dword ptr [rip+{X}], 1", "ret", sym incr_x, X = sym X, } fn main() { unsafe { incr_x(); dbg!(X); incr_x(); dbg!(X); } }

32 位 x86 代碼下則需要把匯編代碼改成:


	

global_asm!{ "{0}:", "lea eax, {X}", "add dword ptr[eax], 1", "ret", sym incr_x, X = sym X, }

審核編輯 :李倩
聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規問題,請聯系本站處理。 舉報投訴
  • 代碼
    +關注

    關注

    30

    文章

    4751

    瀏覽量

    68357
  • 編譯器
    +關注

    關注

    1

    文章

    1618

    瀏覽量

    49052
  • Rust
    +關注

    關注

    1

    文章

    228

    瀏覽量

    6572

原文標題:在 Rust 中使用內聯匯編

文章出處:【微信號:Rust語言中文社區,微信公眾號:Rust語言中文社區】歡迎添加關注!文章轉載請注明出處。

收藏 人收藏

    評論

    相關推薦

    如何在Rust中使用Memcached

    Memcached協議的實現,使得開發者可以Rust中使用Memcached。 基礎用法 創建連接 使用Rust語言Memcached需要先創建一個連接??梢允褂?memcached
    的頭像 發表于 09-19 16:30 ?1199次閱讀

    RealView MDK中使內聯函數時需要注意的問題

    的文件中申明為extern類型。原因是MDK中使用的__inline函數時和標準C++中的inline函數有相同的語義。C++標準中,一個內聯函數在用到它的每個解釋單元需要相同的定義,這樣看來只有內部
    發表于 08-02 10:10

    如何編寫內聯匯編MAC指令?

    我使用的是DSPIC33 FJ。我所有的代碼都是用C編寫的,但是考慮到應用程序的復雜性,我不得不使用內聯匯編指令。我的C代碼中,我有三個變量foo,bar和foo bar,其中foo bar
    發表于 08-01 09:38

    內聯函數和匯編指令疑問

    我想用內聯函數來取兩個int型變量(32位)的較大值,看了一下內聯函數有_max2和_max4,和匯編指令MAX2和 MAXU4 但是他們的用法比較奇特,他們用法是把32位的int型變量拆成4個8位
    發表于 10-22 15:28

    如何使用內聯函數法調用匯編函數?

    從C/C++中調用匯編代碼中的函數使用內聯函數法調用匯編函數從C/C++中調用匯編代碼中的變量或者常量
    發表于 04-02 06:57

    哪幾種情況中必須使用內聯匯編或嵌入型匯編

    。Linux驅動入門可以一起交流。一、gcc 內聯匯編內聯匯編即在C中直接使用匯編語句進行編程,使程序可以C程序中實現C語言不能完成的一些
    發表于 12-20 08:00

    Rust代碼中加載靜態庫時,出現錯誤 ` rust-lld: error: undefined symbol: malloc `怎么解決?

    “ [i]malloc ”、“ [i]exit ”。我驗證了使用 ` [i]nm ` 命令。 問題是我打算使用 ffi rust 中使用這個靜態庫。當我嘗試我的
    發表于 06-09 08:44

    內聯匯編的技巧

      有時我們的程序需要一些很高的執行效率或者執行系統底層的功能模塊,這些關鍵的部分我們可以采用內聯匯編直接插入匯編指令來達到我們的要求,以下是幾個技巧與大家共同
    發表于 08-29 10:20 ?884次閱讀

    內聯匯編和嵌入型匯編的使用

    編譯器中的匯編器。使用它可以C/C++程序中實現C/C++語言不能完成的一些工作。例如,在下面幾種情況中必須使用內聯匯編或嵌入型匯編。 程
    發表于 10-19 09:30 ?0次下載

    Rust相比Go的優劣勢

    Rust可以做內聯匯編,Go不行(Rust的SIMD庫也開發中,這種事情你不會用Go做)。
    發表于 06-29 11:19 ?3947次閱讀

    哪幾種情況中必須使用內聯匯編或嵌入型匯編

    ARM系列文章,請點擊以下匯總鏈接:《從0學arm合集》一、gcc 內聯匯編內聯匯編即在C中直接使用匯編語句進行編程,使程序可以C程序中實
    的頭像 發表于 12-24 12:55 ?959次閱讀

    C和匯編如何互相調用?

    一、gcc 內聯匯編 內聯匯編即在C中直接使用匯編語句進行編程,使程序可以C程序中實現C語言不
    的頭像 發表于 12-25 15:50 ?2980次閱讀

    C中直接使用匯編語句進行編程

    ? 一、gcc 內聯匯編 內聯匯編即在C中直接使用匯編語句進行編程,使程序可以C程序中實現C語
    的頭像 發表于 11-16 09:26 ?8112次閱讀

    移動端arm cpu優化學習筆記第4彈--內聯匯編入門(上)

    本文主要內容是介紹ARMv7和v8內聯匯編的一些基礎知識,并且會結合兩個具體例子去看下如何用內聯匯編來改寫原來的代碼。 作者:梁德澎首...
    發表于 02-07 11:03 ?0次下載
    移動端arm cpu優化學習筆記第4彈--<b class='flag-5'>內聯</b><b class='flag-5'>匯編</b>入門(上)

    Rust的內部工作原理

    Rust匯編:了解 Rust 的內部工作原理 非常好的Rust系列文章,通過生成的匯編代碼,讓你了解很多
    的頭像 發表于 06-14 10:34 ?769次閱讀
    <b class='flag-5'>Rust</b>的內部工作原理