在linux源碼中經常遇到__asm__函數。它其實是函數asm的宏定義
#define __asm__ asm,asm函數讓系統執行匯編語句。
__asm__常常與__volatile__一起出現。__volatile__限制編譯器不能對下面的匯編語句進行優化處理。
分析下面語句
__asm__("movb %3,%%dh\n\t" \
"movb %2,%%dl\n\t" \
"shll $16,%%edx\n\t" \
"movw %1,%%dx" \
:"=d" (__base) \
:"m" (*((addr)+2)), \
"m" (*((addr)+4)), \
"m" (*((addr)+7))); \
__base;})
首先它的基本格式為:
__asm__ ("InstructionList"
:Output
:Input
:Clobber/Modify);
%0,%1,%2,%3分別對應OutPut和Input中出現的操作數,稱為占位符。在此例中,對應關系如下:
%0 __base
%1 (*((addr)+2))
%2 (*((addr)+4))
%3 (*((addr)+7))
這樣的占位符最多有10個。在操作數之前還有一個字符串,該字符串表示將操作數放入對應的位置進行處理。
例如,"=d" (__base),表示將操作數__base放入寄存器%edx。也就是%edx代表了__base,=表示該操作數是WRITE—ONLY的。
而"m"表示內存。這就是所謂的操作約束。
注意,如果操作約束是“m“,也就是內存的話。無論是輸入參數還是輸出參數,對這些參數的改變會反映在內存中。
例如:
int c=0;
__asm__("mov $100,%0\n\t"\
::"m" c)
c的值會被改變為100
具體的寄存器縮寫以及各個符號的含義見下。
每一個Input和Output表達式都必須指定自己的操作約束Operation Constraint,這里將討論在80386平台上所可能使用的操作約束。
當前的輸入或輸出需要借助一個寄存器時,需要為其指定一個寄存器約束,可以直接指定一個寄存器的名字。
常用的寄存器約束的縮寫
約束 意義
r 表示使用一個通用寄存器,由 GCC 在%eax/%ax/%al,%ebx/%bx/%bl,%ecx/%cx/%cl,%edx/%dx/%dl中選取一個GCC認為合適的。
g 表示使用任意一個寄存器,由GCC在所有的可以使用的寄存器中選取一個GCC認為合適的。
q 表示使用一個通用寄存器,和約束r的意義相同。
a 表示使用%eax/%ax/%al
b 表示使用%ebx/%bx/%bl
c 表示使用%ecx/%cx/%cl
d 表示使用%edx/%dx/%dl
D 表示使用%edi/%di
S 表示使用%esi/%si
f 表示使用浮點寄存器
t 表示使用第一個浮點寄存器
u 表示使用第二個浮點寄存器
如果一個Input/Output 操作表達式的C/C++表達式表現為一個內存地址,不想借助於任何寄存器,則可以使用內存約束。比如:
__asm__("lidt%0":"=m"(__idt_addr));
__asm__("lidt%0"::"m"(__idt_addr));
修飾符 輸入/輸出 意義
= O 表示此Output操作表達式是Write-Only的。
+ O 表示此Output操作表達式是Read-Write的。
& O 表示此Output操作表達式獨占為其指定的寄存器。
% I 表示此Input 操作表達式中的C/C++表達式可以和下一 個Input操作表達式中的C/C++表達式互換