如何通過asm關(guān)鍵字嵌入?yún)R編語言代碼?
查看 test2.s 中內(nèi)聯(lián)匯編代碼之前的部分,可以看到:
.file "test2.c"
.globl a
.data
.a(chǎn)lign 4
.type a, @object
.size a, 4
a:
.long 1
.globl b
.a(chǎn)lign 4
.type b, @object
.size b, 4
b:
.long 2
.comm c,4,4
變量 a, b 被 .globl 修飾,c 被 .comm 修飾,相當(dāng)于是把它們導(dǎo)出為全局的,所以可以在匯編代碼中使用。
那么問題來了:如果是一個局部變量,在匯編代代碼中就不會用 .globl 導(dǎo)出,此時在內(nèi)聯(lián)匯編指令中,還可以直接使用嗎?
眼見為實,我們把這 3 個變量放到 main 函數(shù)的內(nèi)部,作為局部變量來試一下。
4. test3.c 嘗試操作局部變量#include <stdio.h>
int main()
{
int a = 1;
int b = 2;
int c;
asm("movl a, %eax "
"addl b, %eax "
"movl %eax, c");
printf("c = %d ", c);
return 0;
}
生成匯編代碼指令:
gcc -m32 -S -o test3.s test3.c
在 test3.s 中可以看到?jīng)]有 a, b, c 的導(dǎo)出符號,a 和 b 沒有其他地方使用,因此直接把他們的數(shù)值復(fù)制到?臻g中了:
movl $1, -20(%ebp)
movl $2, -16(%ebp)
我們來嘗試編譯成可執(zhí)行程序:
$ gcc -m32 -o test3 test3.c
/tmp/ccuY0TOB.o: In function `main':
test3.c:(.text+0x20): undefined reference to `a'
test3.c:(.text+0x26): undefined reference to `b'
test3.c:(.text+0x2b): undefined reference to `c'
collect2: error: ld returned 1 exit status
編譯報錯:找不到對 a,b,c 的引用!那該怎么辦,才能使用局部變量呢?擴展 asm 格式!
二、擴展 asm 格式
1. 指令格式
asm [volatile] ("匯編指令" : "輸出操作數(shù)列表" : "輸入操作數(shù)列表" : "改動的寄存器")
格式說明
匯編指令:與基本asm格式相同;
輸出操作數(shù)列表:匯編代碼如何把處理結(jié)果傳遞到 C 代碼中;
輸入操作數(shù)列表:C 代碼如何把數(shù)據(jù)傳遞給內(nèi)聯(lián)匯編代碼;
改動的寄存器:告訴編譯器,在內(nèi)聯(lián)匯編代碼中,我們使用了哪些寄存器;
“改動的寄存器”可以省略,此時最后一個冒號可以不要,但是前面的冒號必須保留,即使輸出/輸入操作數(shù)列表為空。
關(guān)于“改動的寄存器”再解釋一下:gcc 在編譯 C 代碼的時候,需要使用一系列寄存器;我們手寫的內(nèi)聯(lián)匯編代碼中,也使用了一些寄存器。
為了通知編譯器,讓它知道: 在內(nèi)聯(lián)匯編代碼中有哪些寄存器被我們用戶使用了,可以在這里列舉出來,這樣的話,gcc 就會避免使用這些列舉出的寄存器
2. 輸出和輸入操作數(shù)列表的格式
在系統(tǒng)中,存儲變量的地方就2個:寄存器和內(nèi)存。因此,告訴內(nèi)聯(lián)匯編代碼輸出和輸入操作數(shù),其實就是告訴它:
向哪些寄存器或內(nèi)存地址輸出結(jié)果;
從哪些寄存器或內(nèi)存地址讀取輸入數(shù)據(jù);
這個過程也要滿足一定的格式:
"[輸出修飾符]約束"(寄存器或內(nèi)存地址)
(1)約束
就是通過不同的字符,來告訴編譯器使用哪些寄存器,或者內(nèi)存地址。包括下面這些字符:
a: 使用 eax/ax/al 寄存器;
b: 使用 ebx/bx/bl 寄存器;
c: 使用 ecx/cx/cl 寄存器;
d: 使用 edx/dx/dl 寄存器;
r: 使用任何可用的通用寄存器;
m: 使用變量的內(nèi)存位置;
先記住這幾個就夠用了,其他的約束選項還有:D, S, q, A, f, t, u等等,需要的時候再查看文檔。
(2)輸出修飾符
顧名思義,它使用來修飾輸出的,對輸出寄存器或內(nèi)存地址提供額外的說明,包括下面4個修飾符:
+:被修飾的操作數(shù)可以讀取,可以寫入;
=:被修飾的操作數(shù)只能寫入;
%:被修飾的操作數(shù)可以和下一個操作數(shù)互換;
&:在內(nèi)聯(lián)函數(shù)完成之前,可以刪除或者重新使用被修飾的操作數(shù);
語言描述比較抽象,直接看例子!
3. test4.c 通過寄存器操作局部變量#include <stdio.h>
int main()
{
int data1 = 1;
int data2 = 2;
int data3;
asm("movl %%ebx, %%eax "
"addl %%ecx, %%eax"
: "=a"(data3)
: "b"(data1),"c"(data2));
printf("data3 = %d ", data3);
return 0;
}
有 2 個地方需要注意一下。
在內(nèi)聯(lián)匯編代碼中,沒有聲明“改動的寄存器”列表,也就是說可以省略掉(前面的冒號也不需要);
擴展asm格式中,寄存器前面必須寫 2 個%;
代碼解釋:
"b"(data1),"c"(data2) ==> 把變量 data1 復(fù)制到寄存器 %ebx,變量 data2 復(fù)制到寄存器 %ecx。這樣,內(nèi)聯(lián)匯編代碼中,就可以通過這兩個寄存器來操作這兩個數(shù)了;
"=a"(data3) ==> 把處理結(jié)果放在寄存器 %eax 中,然后復(fù)制給變量data3。前面的修飾符等號意思是:會寫入往 %eax 中寫入數(shù)據(jù),不會從中讀取數(shù)據(jù);
通過上面的這種格式,內(nèi)聯(lián)匯編代碼中,就可以使用指定的寄存器來操作局部變量了,稍后將會看到局部變量是如何從經(jīng)過?臻g,復(fù)制到寄存器中的。
生成匯編代碼指令:
gcc -m32 -S -o test4.s test4.c
匯編代碼 test4.s 如下:
movl $1, -20(%ebp)
movl $2, -16(%ebp)
movl -20(%ebp), %eax
movl -16(%ebp), %edx
movl %eax, %ebx
movl %edx, %ecx
#APP
# 10 "test4.c" 1
movl %ebx, %eax
addl %ecx, %eax
# 0 "" 2
#NO_APP
movl %eax, -12(%ebp)
可以看到,在進入手寫的內(nèi)聯(lián)匯編代碼之前:
把數(shù)字 1 通過?臻g(-20(%ebp)),復(fù)制到寄存器 %eax,再復(fù)制到寄存器 %ebx;
把數(shù)字 2 通過棧空間(-16(%ebp)),復(fù)制到寄存器 %edx,再復(fù)制到寄存器 %ecx;
這 2 個操作正是對應(yīng)了內(nèi)聯(lián)匯編代碼中的“輸入操作數(shù)列表”部分:"b"(data1),"c"(data2)。
在內(nèi)聯(lián)匯編代碼之后(#NO_APP 之后),把 %eax 寄存器中的值復(fù)制到棧中的 -12(%ebp) 位置,這個位置正是局部變量 data3 所在的位置,這樣就完成了輸出操作。

請輸入評論內(nèi)容...
請輸入評論/評論長度6~500個字
最新活動更多
-
7月8日立即報名>> 【在線會議】英飛凌新一代智能照明方案賦能綠色建筑與工業(yè)互聯(lián)
-
7月22-29日立即報名>> 【線下論壇】第三屆安富利汽車生態(tài)圈峰會
-
7.30-8.1火熱報名中>> 全數(shù)會2025(第六屆)機器人及智能工廠展
-
7月31日免費預(yù)約>> OFweek 2025具身智能機器人產(chǎn)業(yè)技術(shù)創(chuàng)新應(yīng)用論壇
-
免費參會立即報名>> 7月30日- 8月1日 2025全數(shù)會工業(yè)芯片與傳感儀表展
-
即日-2025.8.1立即下載>> 《2024智能制造產(chǎn)業(yè)高端化、智能化、綠色化發(fā)展藍皮書》
推薦專題