static inline int strcmp(const char * cs,const char * ct) { int d0, d1; register int __res; __asm__ __volatile__( "1:\tlodsb\n\t" "scasb\n\t" "jne 2f\n\t" "testb %%al,%%al\n\t" "jne 1b\n\t" "xorl %%eax,%%eax\n\t" "jmp 3f\n" "2:\tsbbl %%eax,%%eax\n\t" "orb $1,%%al\n" "3:" :"=a" (__res), "=&S" (d0), "=&D" (d1) :"1" (cs),"2" (ct)); return __res; }
其中的“\n”是換行符,“\t”是 tab 符,在每條命令的結束加這兩個符號,是為了讓
gcc 把嵌入式匯編代碼翻譯成一般的匯編代碼時能夠保證換行和留有一定的空格。
例如,上 面的嵌入式匯編會被翻譯成:
1: lodsb |
//裝入串操作數,即從DS段中esi位置的字符傳送到 al 寄存器,然后 esi 根據DF指向串中下一個元素 ,DF=0,增加;DF=1,減少 |
Scasb |
//掃描串操作數,即從 al 中減去ES段中edi位置的字符,不保留結果,只改變標志 CF,AF,PF,SF,OF,ZF,若字符相等,ZF=1,否則ZF=0。若DS段中字符小於ES段中串,則CF=1,后面sbb運算會出現-1 |
Jne2f |
//如果兩個字符不相等,則轉到標號 |
testb %al %al |
//如果al中全是0,則ZF=1(邏輯與結果為0),如果字符串結束遇到null零 |
jne 1b |
//如果ZF=0(邏輯與結果不為0)即字符串未結束,則繼續比較 |
xorl %eax %eax |
//自身異或,結果為0,CF=0,eax清空 |
jmp 3f |
//向前跳至3:,退出,返回值為0 |
2: sbbl %eax %eax |
//32位sbb src,dest;dest-src-CF,存入dest,如果CF=1,則結果為-1(全是1);否則為0。(結果為-1時標志位:CF=1,SF=1,OF=1,ZF=0,PF=1) |
orb $1 %al |
//對0位或1,保證結果為-1或者1。如果結果為0,或1之后為1,即字符大,返回1,字符小,返回-1 |
3: |
|
參考《深入分析linux內核》
這段代碼看起來非常熟悉,讀起來也不困難。其中 3f 表示往前(forword)找到第一個
標號為 3 的那一行,相應地,1b 表示往后找。其中嵌入式匯編代碼中輸出和輸入部分的結合 情況為:
• 返回值__res,放在 al 寄存器中,與%0相結合;
• 局部變量 d0,與%1 相結合,也與輸入部分的 cs 參數相對應,也存放在寄存器 ESI
中,即 ESI 中存放源字符串的起始地址。
• 局部變量 d1,與%2 相結合,也與輸入部分的 ct 參數相對應,也存放在寄存器 EDI
中,即 EDI 中存放目的字符串的起始地址。
通過對這段代碼的分析我們應當體會到,萬變不利其本,嵌入式匯編與一般匯編的區別 僅僅是形式,本質依然不變。因此,全面掌握 Intel 386 匯編指令乃突破閱讀低層代碼之根 本
大部分指令對於AT&T使用以下后綴:
b 字節(8位),對應於Intel的byte ptr
w 字(16位),對應於Intel的word ptr
l 雙字(實際是表示long,32位),對應於Intel的dword ptr
q 四字(64位),對應於Intel的qword ptr