網頁

2006年3月27日 星期一

C的組語

GAS與x86(IA32)的AT&T syntax組語寫法
基本介紹
Unix下寫組合語言, 這個需要組合語言的基礎也要有系統的觀念。 使用的工具有nasm或GNU as, nasm用Intel的語法就可以, GNU as跟其它unix上的工具as一樣要按照AT&T的語法規定, 所以如果你是先學Intel的有些語法上需要注意。
AT&T assembly與Intel/Microsoft syntax的不同
order(來源與目的暫存器順序不同)
source在前面destination在後面
Intex Syntax AT&T Syntax
instr dest,source instr source,dest
mov eax,[ecx] movl (%ecx),%eax
register naming(暫存器命名)
AT&T前面要加個% Intel Syntax AT&T Syntax
mov eax,1 movl $1,%eax
mov ebx,0ffh movl $0xff,%ebx
int 80h int $0x80
imme operand(立即定址命名)
AT&T前面要加個$
Intel Syntax AT&T Syntax
mov eax,1 movl $1,%eax
mov ebx,0ffh movl $0xff,%ebx
int 80h int $0x80
memory reference(間接定址)
AT&T用大括號()
Intel Syntax AT&T Syntax
instr foo,segreg:[base+index*scale+disp] instr %segreg:disp(base,index,scale),foo
mov eax,[ebx+20h] movl 0x20(%ebx),%eax
add eax,[ebx+ecx*2h] addl (%ebx,%ecx,0x2),%eax
lea eax,[ebx+ecx] leal (%ebx,%ecx),%eax
sub eax,[ebx+ecx*4h-20h] subl -0x20(%ebx,%ecx,0x4),%eax
opcode naming(指令命名)
必需指定長度 根據系統而不一樣 word有的是32 bits有的是16 bits b : byte
w : word
l : long
Intel Syntax AT&T Syntax
mov al,bl movb %bl,%al
mov ax,bx movw %bx,%ax
mov eax,ebx movl %ebx,%eax
mov eax, dword ptr [ebx] movl (%ebx),%eax

type casting(型別轉換) s (signed)
z (zero)
bl (from byte to long)
bw (from byte to word)
wl (from word to long)
movsbl %al, %edx
long jump, call與ret
Intel Syntax AT&T Syntax
jmp far seg:offsetljmp seg, offset
jmp far INITSEG:GO ljmp $INITSEG, $GO
call far INITSEG:GO lcall $INITSEG, $GO
ret far STACK_ADJUS lret $STACK_ADJUST
C下的inline組語
在C語言中勘進組語的程式碼加個
__asm__("asm code");
__asm__("movl $1,%eax\n\t" // SYS_exit
"xor %ebx,%ebx\n\t"
"int $0x80"
);

注意quote 與\n\t的位置 \n\t是因為gcc其實根據這些與其它c code產生一個.s檔 \n\t只是在.s檔產生newline與tab這在每一行都要除了最後一行不用 另外inline assembly的 通式是 __asm__(asm statements : outputs : inputs : clobber);

通式有兩個重要的概念, 一個是可以和C程式傳變數來溝通,另外 如果我們指定了eax ebx...等register,則這下可完了,如果其他的C code 也正在用eax ebx,則compiler必須先把這些值推進stack才能跑你的asm code, 所以我們可以不特別指定register,讓gcc自動作register運用的最佳化。 其實這是其他mips和其他CPU的作法,別種CPU的register命名沒 eax, ebx.....這麼死與囉唆, $1 $2 $3...就搞定,還可以換來換去很有彈性。 這個通式就是在做這樣的事,請看一個例子 int main (void) {
int operand1, operand2, sum, accumulator;
operand1 = rand (); operand2 = rand ();
__asm__ ("movl %1, %0\n\t"
"addl %2, %0"
: "=r" (sum) /* output operands */
: "r" (operand1), "r" (operand2) /* input operands */
: "0"); /* clobbered operands */
accumulator = sum;
__asm__ ("addl %1, %0\n\t"
"addl %2, %0"
: "=r" (accumulator)
: "0" (accumulator), "g" (operand1), "r" (operand2)
: "0");
return accumulator;
}

第一個__asm__是說input的東西把operand1(C的變數)放到r,也就是%1, operand2(C的變數)放到r,也就是%2,然後執行assembly code的 movl與addl, 然後結果放到sum(C的變數)=r 也就是%0, 在這邊我們沒有指定eax ebx,只是很單純的%0 %1 %2 r。 %0 %1 %2 分別對應了output r, input r, input r, %0 %1 %2.....會先對應output裡的register,再對應input裡的register, 就是它們出現的順序。 gcc會幫我們最佳化register的使用。 clobbered operands是一堆registers, 我們告訴gcc說這些registers的值已經被暴力的摧毀(被改變了), 你要重新考慮它們的合法性才行, 在這邊0就是%0,gcc會特別照顧一下它所選的%0的值。 有一些規則要說明
r, g, 0這些東西叫constraints(限制的意思),每一個符號有特殊意義 代表這個暫存器必需是什麼型式的(暫存器有通用暫存器,符點運算暫存器, cpu指令有的允許直接對memory做運算,有的不準等等條件的暫存器, r代表通用型暫存器)。
output operand前一定要有個"="表示這個constraint是read-ony, "="叫constraint modifier。
input output operands後一定要跟著相對應的C 變數的參數, 這是給asm的參數。
如果statement裡真要指定register要多加個%變成%%eax
通用constraints I, J, K .... P 是根據不同cpu可以做不同的解釋來表示一個範圍的立即定址整數
Q, R, S .... U
0, 1, 2 .... 相對於assembly statement裡的%0 %1 %2 ....
a, b, c .... f 可以根據不同cpu可以做不同定義的registers
m 表示這是一個memory的operand 例如mov eax, data中的data
p 合法的記憶體位址
r 一般通用型register
g 任何的通用型register, memory operand, 立即定址的整數

constraints在386上有
a eax
b ebx
c ecx
d edx
S esi
D edi
I 表示只有constant value (0 to 31)才行
r 可以是eax ebx ecx edx esi edi
q 可以是eax ebx ecx edx
g eax, ebx, ecx, edx or variable in memory
A eax and edx combined into a 64-bit integer (use long longs)

沒有留言:

張貼留言