linux 源碼中__asm__ __volatile__作用

[Linux內核系列—C語言中內嵌彙編 asm volatile ]https://cn.aliyun.com/jiaocheng/68768.html
[ linux源碼閱讀筆記 asm函數]https://www.cnblogs.com/elnino/p/4313340.html

static inline char * strcpy(char * dest, const char *src)
{
	char *xdest = dest;
	__asm__ __volatile__
	("1: \tmoeb %1@+, %0@+\n\t"   "jne 1b"  //這個冒號不是分隔符
	: "=a" (dest) , "=a" (stc)
	: "0"(dest), "1" (src)
	 : "memory");
	return xdest;
}

asm volatile(“xxx”);
asm” 表示後面的代碼爲內嵌彙編,
"asm"是"asm"的別名。
volatile” 表示編譯器不要優化代碼,後面的指令 保留原樣,“volatile"是它的別名。括號裏面是彙編指令。
內嵌彙編語法如下:
__asm__(彙編語句模板: 輸出部分: 輸入部分: 破壞描述部分)
共四個部分:彙編語句模板,輸出部分,輸入部分,破壞描述部分,各部分使用”:“格開,彙編語句模板必不可少,其他三部分可選,如果使用了後面的部分,而前面部分爲空,也需要用”:"格開,相應部分內容爲空。例如:
__asm__ __volatile__("cli": : :"memory")

1、彙編語句模板
彙編語句模板由彙編語句序列組成,語句之間使用";"、"/n"或"/n/t"分開。指令中的操作數可以使用佔位符引用C語言變量,操作數佔位符最多10個,名稱如下:%0,%1,…,%9。指令中使用佔位符表示的操作數,總被視爲long型(4個字節),但對其施加的操作根據指令可以是字或者字節,當把操作數當作字或者字節使用時,默認爲低字或者低字節。對字節操作可以顯式的指明是低字節還是次字節。方法是在%和序號之間插入一個字母,"b"代表低字節,"h"代表高字節,例如:%h1。

2、輸出部分
輸出部分描述輸出操作數,不同的操作數描述符之間用逗號格開,每個操作數描述符由限定字符串和C 語言變量組成。每個輸出操作數的限定字符串必須包含"="表示他是一個輸出操作數。
例:
__asm__ __volatile__("pushfl ; popl %0 ; cli":"=g" (x) )
描述符字符串表示對該變量的限制條件,這樣GCC 就可以根據這些條件決定如何分配寄存器,如何產生必要的代碼處理指令操作數與C表達式或C變量之間的聯繫。

3、輸入部分
輸入部分描述輸入操作數,不同的操作數描述符之間使用逗號格開,每個操作數描述符由限定字符串和C語言表達式或者C語言變量組成。
例1 :
__asm__ __volatile__ ("lidt %0" : : "m" (real_mode_idt));
例二(bitops.h):

Static __inline__ void __set_bit(int nr, volatile void * addr)
{
         __asm__(
                         "btsl %1,%0"
                         :"=m" (ADDR)
                         :"Ir" (nr));
}

後 例功能是將(*addr)的第nr位設爲1。第一個佔位符%0與C 語言變量ADDR對應,第二個佔位符%1與C語言變量nr對應。因此上面的彙編語句代碼與下面的僞代碼等價:btsl nr, ADDR,該指令的兩個操作數不能全是內存變量,因此將nr的限定字符串指定爲"Ir",將nr 與立即數或者寄存器相關聯,這樣兩個操作數中只有ADDR爲內存變量。

4、限制字符
4.1、限制字符列表
限制字符有很多種,有些是與特定體系結構相關,此處僅列出常用的限定字符和i386中可能用到的一些常用的限定符。它們的作用是指示編譯器如何處理其後的C語言變量與指令操作數之間的關係。

分類 限定符 描述
通用寄存器 “a” 將輸入變量放入eax 這裏有一個問題:假設eax已經被使用,那怎麼辦?
其實很簡單:因爲GCC 知道eax 已經被使用,它在這段彙編代碼的起始處
插入一條語句pushl %eax,將eax 內容保存到堆棧,
然 後在這段代碼結束處再增加一條語句popl %eax,恢復eax的內容
“b” 將輸入變量放入ebx
“c” 將輸入變量放入ecx
“d” 將輸入變量放入edx
“s” 將輸入變量放入esi
“d” 將輸入變量放入edi
“q” 將輸入變量放入eax,ebx,ecx,edx中的一個
“r” 將輸入變量放入通用寄存器,也就是eax,ebx,ecx,edx,esi,edi中的一個
"A"把eax和edx合成一個64 位的寄存器(use long longs)
內存 “m” 內存變量
“o” 操作數爲內存變量,但是其尋址方式是偏移量類型, 也即是基址尋址,或者是基址加變址尋址
“V” 操作數爲內存變量,但尋址方式不是偏移量類型
" " 操作數爲內存變量,但尋址方式爲自動增量
“p” 操作數是一個合法的內存地址(指針)
寄存器或內存 “g” 將輸入變量放入eax,ebx,ecx,edx中的一個 或者作爲內存變量
“X” 操作數可以是任何類型
立即數 “I” 0-31之間的立即數(用於32位移位指令)
“J” 0-63之間的立即數(用於64位移位指令)
“N” 0-255之間的立即數(用於out指令)
“i” 立即數
“n” 立即數,有些系統不支持除字以外的立即數, 這些系統應該使用"n"而不是"i"
匹配 " 0 ", 表示用它限制的操作數與某個指定的操作數匹配,
“1” … 也即該操作數就是指定的那個操作數,例如"0"
“9” 去描述"%1"操作數,那麼"%1"引用的其實就是"%0"操作數,
注意作爲限定符字母的0-9 與 指令中的"%0"-"%9"的區別,前者描述操作數,後者代表操作數。
&; 該輸出操作數不能使用過和輸入操作數相同的寄存器
操作數類型 “=” 操作數在指令中是隻寫的(輸出操作數)
“+” 操作數在指令中是讀寫類型的(輸入輸出操作數)
浮點數 “f” 浮點寄存器
“t” 第一個浮點寄存器
“u” 第二個浮點寄存器
“G” 標準的80387浮點常數
% 該操作數可以和下一個操作數交換位置
例如addl的兩個操作數可以交換順序 (當然兩個操作數都不能是立即數)
# 部分註釋,從該字符到其後的逗號之間所有字母被忽略
* 表示如果選用寄存器,則其後的字母被忽略
修飾符 輸入/輸出 意義
= O 表示此Output操作表達式是Write-Only的。
+ O 表示此Output操作表達式是Read-Write的。
& O 表示此Output操作表達式獨佔爲其指定的寄存器。
% I 表示此Input 操作表達式中的C/C++表達式可以和下一 個Input操作表達式中的C/C++表達式互換

5、破壞描述部分
破壞描述符用於通知編譯器我們使用了哪些寄存器或內存,由逗號格開的字符串組成,每個字符串描述一種情況,一般是寄存器名;除寄存器外還有"memory"。例如:"%eax","%ebx","memory"等。

"memory"比較特殊,可能是內嵌彙編中最難懂部分。爲解釋清楚它,先介紹一下編譯器的優化知識,再看C關鍵字volatile。最後去看該描述符。
彙編指令速查

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章