ARM彙編 從內嵌彙編開始

2021-08-31 07:17:54 字數 3623 閱讀 6032

對於基於arm的risc處理器,gnu c編譯器提供了在c**中內嵌彙編的功能。這種特性提供了c**沒有的功能,比如手動優化軟體關鍵部分的**、使用相關的處理器指令。

__asm__ __volatile__("hlt"); "__asm__"表示後面的**為內嵌彙編,"asm"是"__asm__"的別名。"__volatile__"表示編譯器不要優化**,後面的指令保留原樣,"volatile"是它的別名。括號裡面是彙編指令。

彙編指令模板

使用內嵌彙編,要先編寫彙編指令模板,然後將c語言表示式與指令的運算元相關聯,並告訴gcc對這些操作有哪些限制條件。

讓我們以乙個簡單的例子開始:

asm(

"mov r0, r0\n\t"

"mov r0, r0"

); /* nop 例子 */

該語句的作用是將r0移動到r0中。換句話講他並不幹任何事。典型的就是nop指令,作用就是短時的延時。可以在乙個asm宣告中寫多個彙編指令。但是為了增加程式的可讀性,最好將每乙個彙編指令單獨放一行。換行符和製表符的使用可以使得指令列表看起來變得美觀(

不加\n編譯會報錯)。

1.」nop"指令即空指令,

2. 執行該指令時微控制器什麼都不做,但是會占用乙個指令的時間。

3. 當指令間需要有延時(給外部裝置足夠的響應時間;或是軟體的延時等),可以插入「nop」指令。

通用的內嵌彙編模版是這樣的:

asm(code:output operand list:input operand list:clobber list); 

彙編和

c語句之間的聯絡通過上面

asm宣告中可選的

output operand list

和input operand list。clobber list

後面再說。

下面是將

c語言的乙個整型變數傳遞給彙編,傳遞給

c語言的另外乙個整型變數。

int x = 5, y = 0;

__asm__("mov %[output], %[input]\n" : [output] "=r"(y) : [input]  "r" (x));

彙編指令每乙個

asm語句被冒號(

:)分成了四個部分。彙編指令放在第一部分中的「」中間。

"mov %[output], %[input]\n"

運算元列表接下來是冒號後的可選擇的output operand list:input operand list,每乙個條目是由一對

(方括號)和被他包括的符號名組成,它後面跟著限制性字串,再後面是圓括號和它括著的

c變數。 "output"前面的限制字串是"=r",其中"="表示"output"是輸出運算元,"r" 表示需要將"result"與某個通用暫存器相關聯,先將運算元的值讀入暫存器,然後在指令中使用相應暫存器,而不是"output"本身,當然指令執行完後需要將暫存器中的值存入變數"output",從表面上看好像是指令直接對"result"進行操作,實際上gcc做了隱式處理,這樣我們可以少寫一 些指令。"input"前面的"r"表示該表示式需要先放入某個暫存器,然後在指令中使用該暫存器參加運算。

c表示式或者變數與暫存器的關係由gcc自動處理,我們只需使用限制字串指導gcc如何處理即可。限制字元必須與指令對運算元的要求相匹配,否則產生的 彙編**將會有錯,讀者可以將上例中的兩個"r",都改為"m"(m表示運算元放在記憶體,而不是暫存器中),編譯後得到的結果是:

movl input, result

很明顯這是一條非法指令,因此限制字串必須與指令對運算元的要求匹配。例如指令movl允許暫存器到暫存器,立即數到暫存器等,但是不允許記憶體到記憶體的操作,因此兩個運算元不能同時使用"m"作為限定字元。

上面nop例子的輸出如下:

破壞符列表

有時候,當你想通知gcc當前內聯彙編語句可能對某些暫存器和記憶體進行修改,希望gcc將這一點考慮進去,此時就可以在clobber/modify域中進行宣告這些暫存器和記憶體。

這種情況一般發生在乙個暫存器出現在instructionlist,但不是有output/input操作表示式所指 定的,也不是在一些output/input操作表示式使用「r」約束時有gcc為其選擇的,同時此暫存器被instructionlist修改,而這個暫存器只是供當前內聯彙編使用的情況。

例如:__asm__ (「mov r0, #0x34」 ::: 「r0」)

暫存器r0出現在instructionlist中,且被mov指令修改,但卻未被任何output/input操作表示式指定,所以需要在clobber/modify域中指定「r0」,讓gcc知道這一點。

因為你在output/input操作表示式所指定的暫存器,或當你為一些output/input表示式使用「r」約束,gcc為你選擇乙個暫存器,編譯器對這些暫存器是非常清楚的, 它知道這些暫存器是被修改的,因此不需要在clobber/modify域中在宣告它們。除此之外,gcc對剩下的暫存器中哪些會被當前內聯彙編修改一無所知。所以,如果當前內聯彙編修改了這些暫存器,就最好在clobber/modify域中宣告,讓gcc針對這些暫存器做相應的處理,否則可能會造成暫存器 的不一致,造成程式執行錯誤。

注意事項

就像上面的nop例子,asm宣告的4個部分中,只要最尾部沒有使用的部分都可以省略。但是有有一點要注意的是,上面的4個部分中只要後面的還要使用,前面的部分沒有使用也不能省略,必須空但是保留冒號。為了增加**的可讀性,你可以使用換行,空格,還有

c風格的注釋。

在早期的

c**中還有如下寫法:

asm("mov %0,%1" : "=r" (output) : "m" (input));

在彙編**中運算元的引用使用的是%後面跟乙個數字,%1代表第乙個運算元,%2**第二個運算元,稱為佔位符,往後的類推。運算元至多有10 個,用"%0","%1"...."%9"表示。這個方法目前最新的編譯器還是支援的。但是它不便於維護**。試想一下,你寫了大量的彙編指令的**,要是你想插入乙個運算元,那麼你就不得不從新修改運算元編號。

為什麼需要彙編

c限制了你更加貼近底層操作硬體,比如,c中沒有直接修改程式狀態暫存器(psr)的宣告。

寫出更加優化的**。

如果我們想做逆向工程,或者理解相關二進位制程式的執行流程,構建arm架構的shellcode,rop鏈,以及除錯arm應用,這些都要求先懂得arm彙編。

參考**

擴充套件閱讀

【系列分享】arm 彙編基礎速成1:arm彙編以及組合語言基礎介紹

ARM彙編和內嵌彙編

彙編指令 跳轉指令 b bl blxbx 資料處理指令 mov mvnadd adcsub rsbrsc andor eorbic cmncmp tstteq 狀態暫存器指令 mrs msrldr strldm stmmcr arm和協處理器之間資料交換 mrccond 可選的條件 rd 存放返回狀...

VB內嵌彙編

vb簡單易用,但功能有時候受限制。vc,delphi可以直接在程式中寫彙編 可惱的是,vb不行。我看過網上也有關於vb嵌入彙編的,不過有些方法,過於複雜,而且也沒相應 的介紹。我這裡提供一種方法,也許大家以後可能有用!基本思路 彙編 可以存在乙個byte型別的陣列中,然後,通過某種手段,把系統控制權...

關於內嵌彙編

最近才發現原來noi不給用內嵌彙編 那麼這篇文章除了平常做做oj以外就沒什麼意義了 參考自 嗯 搞oi久了應該都會碰到這麼一種問題 爆棧 當然,手寫棧是最普遍的一種解決方法 比如noi2011 day2 t1,ms是臨時換的一道水題,只要會手寫棧就可以水過 pascal黨是很舒服的,因為可以在 中手...