如何通過asm關鍵字嵌入?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 修飾,相當于是把它們導出為全局的,所以可以在匯編代碼中使用。
那么問題來了:如果是一個局部變量,在匯編代代碼中就不會用 .globl 導出,此時在內(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 的導出符號,a 和 b 沒有其他地方使用,因此直接把他們的數(shù)值復制到棧空間中了:
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ù)列表:匯編代碼如何把處理結果傳遞到 C 代碼中;
輸入操作數(shù)列表:C 代碼如何把數(shù)據(jù)傳遞給內(nèi)聯(lián)匯編代碼;
改動的寄存器:告訴編譯器,在內(nèi)聯(lián)匯編代碼中,我們使用了哪些寄存器;
“改動的寄存器”可以省略,此時最后一個冒號可以不要,但是前面的冒號必須保留,即使輸出/輸入操作數(shù)列表為空。
關于“改動的寄存器”再解釋一下: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)存地址輸出結果;
從哪些寄存器或內(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 復制到寄存器 %ebx,變量 data2 復制到寄存器 %ecx。這樣,內(nèi)聯(lián)匯編代碼中,就可以通過這兩個寄存器來操作這兩個數(shù)了;
"=a"(data3) ==> 把處理結果放在寄存器 %eax 中,然后復制給變量data3。前面的修飾符等號意思是:會寫入往 %eax 中寫入數(shù)據(jù),不會從中讀取數(shù)據(jù);
通過上面的這種格式,內(nèi)聯(lián)匯編代碼中,就可以使用指定的寄存器來操作局部變量了,稍后將會看到局部變量是如何從經(jīng)過?臻g,復制到寄存器中的。
生成匯編代碼指令:
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)),復制到寄存器 %eax,再復制到寄存器 %ebx;
把數(shù)字 2 通過?臻g(-16(%ebp)),復制到寄存器 %edx,再復制到寄存器 %ecx;
這 2 個操作正是對應了內(nèi)聯(lián)匯編代碼中的“輸入操作數(shù)列表”部分:"b"(data1),"c"(data2)。
在內(nèi)聯(lián)匯編代碼之后(#NO_APP 之后),把 %eax 寄存器中的值復制到棧中的 -12(%ebp) 位置,這個位置正是局部變量 data3 所在的位置,這樣就完成了輸出操作。

請輸入評論內(nèi)容...
請輸入評論/評論長度6~500個字
最新活動更多
推薦專題
- 1 UALink規(guī)范發(fā)布:挑戰(zhàn)英偉達AI統(tǒng)治的開始
- 2 北電數(shù)智主辦酒仙橋論壇,探索AI產(chǎn)業(yè)發(fā)展新路徑
- 3 “AI寒武紀”爆發(fā)至今,五類新物種登上歷史舞臺
- 4 降薪、加班、裁員三重暴擊,“AI四小龍”已折戟兩家
- 5 國產(chǎn)智駕迎戰(zhàn)特斯拉FSD,AI含量差幾何?
- 6 光計算迎來商業(yè)化突破,但落地仍需時間
- 7 東陽光:2024年扭虧、一季度凈利大增,液冷疊加具身智能打開成長空間
- 8 地平線自動駕駛方案解讀
- 9 封殺AI“照騙”,“淘寶們”終于不忍了?
- 10 優(yōu)必選:營收大增主靠小件,虧損繼續(xù)又逢關稅,能否乘機器人東風翻身?