C 表達(dá)式中的匯編指令
asm 為 gcc 中的關(guān)鍵字,asm 表達(dá)式為在 C代碼中嵌套匯編指令,該表達(dá)式只是單純的替換出匯編代碼,并不對匯編代碼的含義進(jìn)行解析。
asm 表達(dá)式有兩種形式,第二種 asm-qualifiers 包含了 goto 語句。
第一種形式為常見的用法,AssemblerTemplate 和 OutputOperands 必須存在, 其中 Clobbers 存在需要 InputOperands 也出現(xiàn)。
asm asm-qualifiers ( AssemblerTemplate
: OutputOperands
[ : InputOperands
[ : Clobbers ] ])
asm asm-qualifiers ( AssemblerTemplate
:
: InputOperands
: Clobbers
: GotoLabels)
Qualifiers 的類型
volatile, 避免編譯器的優(yōu)化inline, 內(nèi)斂限定符,最小的體積goto, 包含跳轉(zhuǎn)指令
參數(shù)
AssemblerTemplate
- 匯編指令模板是包含匯編器指令的文字字符串,編輯器替換引用輸入,編譯器不會解析該指令的含義。OutputOperands
- 由 AssemblerTemplate 中的指令修改的C變量的逗號分隔列表,允許使用空列表。InputOperands
- 由 AssemblerTemplate 中的指令讀取的C變量的逗號分隔列表,允許使用空列表。Clobbers
- 用逗號分隔的寄存器列表或由 AssemblerTemplate 修改的值,不能出現(xiàn)在 OutputOperands 和 InputOperands 中被提及,允許使用空列表。GotoLabels
- 當(dāng)使用asm的goto形式時,此部分包含 AssemblerTemplate 中的代碼可能跳轉(zhuǎn)到的所有C標(biāo)簽的列表。
AssemblerTemplate
匯編指令由一個字符串給出,多條匯編指令結(jié)合在一起使用的時候,中間以 \r\t 隔開,如
asm("inc %0\n\tinc %0" : "=r"(res) : "0"(res));
/APP
# 11 "asm.c" 1
inc %rax
inc %rax
# 0 "" 2
/NO_APPs
需要轉(zhuǎn)義的字符:%, =, {, }, |
故在ATT匯編中,對寄存器進(jìn)行操作的需要雙 %%, 如 inc %%rax.
OutputOperands
操作數(shù)之間用逗號分隔。 每個操作數(shù)具有以下格式:
[ [asmSymbolicName] ] constraint (cvariablename)
asmSymbolicName
- 為操作數(shù)指定名稱,格式為 %[name]
c // res = num asm("movq %[num], %[res]" : [res] "=r"(res) : [num] "m"(num));
- 如果未指定名稱使用數(shù)字, 從 output 域開始,第一個參數(shù)為 %0, 一次類推, 這里的 res 為 %0, num 為 %1
c // res = num asm("movq %1, %0" : "=r"(res) : "m"(num));constraint
- 一個字符串常量,用于指定對操作數(shù)的存儲的 約束, 需要以 "=" 或 "+" 開頭cvariablename
- 指定一個C左值表達(dá)式來保存輸出,通常是一個變量名。 括號是語法的必需部分
第一個參數(shù)為增加可讀性使用的,現(xiàn)在我們有代碼如下
int64_t res;
int64_t num = 1;
asm("movq %[num], %[res]" : [res] "=r"(res) : [num] "m"(num));
asm("movq %1, %0" : "=r"(res) : "m"(num));
asm("movq %1, %0" : "=m"(res) : "m"(num));
asm("movq %1, %0" : "=r"(res) : "r"(num));
// 對應(yīng)的匯編代碼, 只保留asm表達(dá)式中的代碼
# 13 "asm.c" 1
movq -16(%rbp), %rax // asm-1
# 0 "" 2
/NO_APP
/APP
# 15 "asm.c" 1
movq -16(%rbp), %rax // asm-2
# 0 "" 2
/NO_APP
/APP
# 17 "asm.c" 1
movq -16(%rbp), -8(%rbp) // asm-3
# 0 "" 2
/NO_APP
/APP
# 19 "asm.c" 1
movq %rax, %rax // asm-4
# 0 "" 2
/NO_APP
- 使用名稱替換和數(shù)字替換效果一樣,見 asm-1 和 asm-2約束的用法,這里使用比較簡單通用的的兩種情況,
r為通過寄存器尋址操作,m通過內(nèi)存尋址操作,所以看到當(dāng) - 約束了
r就對應(yīng)寄存器的操作。 - 結(jié)果保存在 res 也就是 cvariablename 中
InputOperands
輸入操作數(shù)使C變量和表達(dá)式中的值可用于匯編代碼。
[ [asmSymbolicName] ] constraint (cexpression)
asmSymbolicName 和輸出列表的用法完全一致
constraint 約束不能使用 = 和 +. 可以使用 "0", 這表明在輸出約束列表中(從零開始)的條目,指定的輸入必須與輸出約束位于同一位置。
int64_t res = 3;
int64_t num = 1;
asm("addq %1, %0" : "=g"(res) : "0"(num));
// 輸入輸出位置相同
movq $3, -8(%rbp)
movq $1, -16(%rbp)
movq -16(%rbp), %rax
/APP
# 32 "asm.c" 1
addq %rax, %rax
# 0 "" 2
/NO_APP
- cexpression 可以不為左值,作為匯編表達(dá)式的輸入值即可
- Clobbers
破壞列表,主要用于指示編譯器生成的匯編指令。
從asm表達(dá)式中看到輸出操作數(shù)中列出條目的更改編譯器是可以確定的,但內(nèi)聯(lián)匯編代碼可能不僅對輸出進(jìn)行了修改。 例如,計算可能需要其他寄存器,或者處理器可能會由于特定匯編程序指令而破壞寄存器的值。 為了將這些更改通知編譯器,在Clobber列表中列出這些會產(chǎn)生副作用的條目。 破壞列表條目可以是寄存器名稱,也可以是特殊的破壞列表項(在下面列出)。 每個內(nèi)容列表條目都是一個字符串常量,用雙引號引起來并用逗號分隔。
寄存器
```c
asm volatile("movc3 %0, %1, %2"
: /* No outputs. */
: "r"(from), "r"(to), "g"(count)
: "%rbx", "%rcx", "%rdx", "memory");
/APP
# 25 "asm.c" 1
movc3 %rax, %r8, -72(%rbp)
# 0 "" 2
/NO_APP
```
可以看到使用到了 rax 寄存器,然后修改程序在 Clobbers 增加 %rax, 結(jié)果如下
```c
asm volatile("movc3 %0, %1, %2"
: /* No outputs. */
: "r"(from), "r"(to), "g"(count)
: "%rax", "%rbx", "%rcx", "%rdx", "memory");
/APP
# 25 "asm.c" 1
movc3 %r8, %r9, -72(%rbp)
# 0 "" 2
/NO_APP
```
特殊破壞列表項
- "cc", 表示匯編代碼修改了標(biāo)志寄存器
- "memory", 為了確保內(nèi)存中包含正確的值,編譯器可能需要在執(zhí)行asm之前將特定的寄存器值刷新到內(nèi)存中
編譯器為了破壞列表項的值受到破壞,當(dāng)這些條目是寄存器時,不對其進(jìn)行使用;為特殊參數(shù)時,重新刷新得到最新的值。
約束
一些基礎(chǔ)的約束
| 約束名 | 說明 |
|---|---|
| whitespace | 空白字符被忽略 |
| m | 允許使用內(nèi)存操作數(shù),以及機(jī)器通常支持的任何類型的地址 |
| o | 允許使用內(nèi)存操作數(shù),但前提是地址是可偏移的 |
| V | 允許使用內(nèi)存操作數(shù),不可偏移的內(nèi)存地址,與 "o'互斥 |
| r | 允許在通用寄存器中使用的寄存器操作數(shù),其中可以指定寄存器,如 a(%rax), b(%rbx) |
| i | 允許使用立即整數(shù)操作數(shù) |
| n | 允許使用具有已知數(shù)值的立即整數(shù)操作數(shù), ‘I', ‘J', ‘K', … ‘P' 更應(yīng)該使用 n |
| F | 允許使用浮點立即數(shù) |
| g | 允許使用任何寄存器,內(nèi)存或立即數(shù)整數(shù)操作數(shù),但非通用寄存器除外 |
| X | 允許任何操作數(shù), ‘0', ‘1', ‘2', … ‘9' |
| p | 允許使用有效內(nèi)存地址的操作數(shù) |
標(biāo)識符約束
| 標(biāo)識符 | 說明 |
|---|---|
| = | 表示此操作數(shù)是由該指令寫入的:先前的值將被丟棄并由新數(shù)據(jù)替換 |
| + | 表示該操作數(shù)由指令讀取和寫入 |
| & | 表示(在特定替代方法中)此操作數(shù)是早期指令操作數(shù),它是在使用輸入操作數(shù)完成指令之前寫入的,故輸入操作數(shù)部分不能分配與輸出操作數(shù)相同的寄存器 |
| % | 表示該操作數(shù)與后續(xù)操作數(shù)的可交換指令 |
內(nèi)核示例
x86 的內(nèi)存屏障指令。
// 避免編譯器的優(yōu)化,聲明此處內(nèi)存可能發(fā)生破壞
#define barrier() asm volatile("" ::: "memory")
// 在32位的CPU下,lock 指令為鎖總線,加上一條內(nèi)存操作指令就達(dá)到了內(nèi)存屏障的作用,64位的cpu已經(jīng)有新增的 *fence 指令可以使用
// mb() 執(zhí)行一個內(nèi)存屏障作用的指令,為指定CPU操作;破壞列表聲明 cc memory 指示避免編譯器進(jìn)行優(yōu)化
#ifdef CONFIG_X86_32
#define mb() asm volatile(ALTERNATIVE("lock; addl $0,-4(%%esp)", "mfence", \
X86_FEATURE_XMM2) ::: "memory", "cc")
#define rmb() asm volatile(ALTERNATIVE("lock; addl $0,-4(%%esp)", "lfence", \
X86_FEATURE_XMM2) ::: "memory", "cc")
#define wmb() asm volatile(ALTERNATIVE("lock; addl $0,-4(%%esp)", "sfence", \
X86_FEATURE_XMM2) ::: "memory", "cc")
#else
#define mb() asm volatile("mfence":::"memory")
#define rmb() asm volatile("lfence":::"memory")
#define wmb() asm volatile("sfence" ::: "memory")
#endif
x86 下獲取 current 的值
DECLARE_PER_CPU(struct task_struct *, current_task);
#define this_cpu_read_stable(var) percpu_stable_op("mov", var)
static __always_inline struct task_struct *get_current(void)
{
return this_cpu_read_stable(current_task);
}
#define percpu_stable_op(op, var) \
({ \
typeof(var) pfo_ret__; \
switch (sizeof(var)) { \
case 8: \
asm(op "q "__percpu_arg(P1)",%0" \
: "=r" (pfo_ret__) \
: "p" (&(var))); \
break; \
} \
pfo_ret__; \
})
current_task 為一個 struct task_struct 類型的指針,追蹤宏調(diào)用,在x86-64 下命中了 case 8: 的匯編代碼, 展開的代碼為
asm("mov" "q ""%%""gs" ":" "%" "P1"",%0" : "=r" (pfo_ret__) : "p" (&(current_task)));
// 變換一下為
asm("movq %%gs:%P1, %0" : "=r"(pfo_ret__) : "p"(&(current_task)));
這行代碼的含義為將 約束輸入部分必須為有效的地址(p約束), 將CPU id(通過段寄存器gs和偏移通過GDT得到,這里后文分析了)通過寄存器(r約束)賦值給 pfo_ret__.
參考
總結(jié)
以上所述是小編給大家介紹的C 表達(dá)式中的匯編指令,希望對大家有所幫助,如果大家有任何疑問請給我留言,小編會及時回復(fù)大家的。在此也非常感謝大家對腳本之家網(wǎng)站的支持!
如果你覺得本文對你有幫助,歡迎轉(zhuǎn)載,煩請注明出處,謝謝!
相關(guān)文章
匯編語言DOSBox及debug.exe在Windows64下環(huán)境搭建
這篇文章主要為大家介紹了匯編語言環(huán)境的搭建DOSBox及debug.exe在Windows64下安裝配置過程,有需要的朋友可以借鑒參考下,希望能夠有所幫助2021-11-11
Windows10下利用DOSBOX和MASM32搭建匯編語言開發(fā)環(huán)境
這篇文章主要介紹了Windows10下利用DOSBOX和MASM32搭建匯編語言開發(fā)環(huán)境,本文給大家介紹的非常詳細(xì),具有一定的參考借鑒價值,需要的朋友可以參考下2020-01-01
匯編語言進(jìn)制轉(zhuǎn)換之16進(jìn)制轉(zhuǎn)10進(jìn)制
這篇文章主要介紹了匯編語言進(jìn)制轉(zhuǎn)換之16進(jìn)制轉(zhuǎn)10進(jìn)制,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-07-07
匯編語言程序設(shè)計之根據(jù)輸入改變屏幕顏色的代碼
這篇文章主要介紹了匯編語言中的根據(jù)輸入改變屏幕顏色的代碼,本文通過實例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-03-03

