一区二区三区日韩精品-日韩经典一区二区三区-五月激情综合丁香婷婷-欧美精品中文字幕专区

分享

Gcc嵌入式匯編

 lifei_szdz 2012-04-25

Gcc嵌入式匯編

   Linux的源代碼中,有很多C語言的函數(shù)中嵌入一段匯編語言程序段,這就是gcc提供的“asm”功能,例如在include/asm-i386/system.h中定義的,讀控制寄存器CR0的一個(gè)宏read_cr0()

 

#define read_cr0() ({ \

         unsigned int __dummy; \

         __asm__( \

                 "movl %%cr0,%0\n\t" \

                 :"=r" (__dummy)); \

         __dummy; \

 })

 

這種形式看起來比較陌生,這是因?yàn)檫@不是標(biāo)準(zhǔn)C所定義的形式,而是gcc 對(duì)C語言的擴(kuò)充。其中__dummyC函數(shù)所定義的變量;關(guān)鍵詞__asm__表示匯編代碼的開始。括弧中第一個(gè)引號(hào)中為匯編指令movl,緊接著有一個(gè)冒號(hào),這種形式閱讀起來比較復(fù)雜。

一般而言,嵌入式匯編語言片段比單純的匯編語言代碼要復(fù)雜得多,因?yàn)檫@里存在怎樣分配和使用寄存器,以及把C代碼中的變量應(yīng)該存放在哪個(gè)寄存器中。為了達(dá)到這個(gè)目的,就必須對(duì)一般的C語言進(jìn)行擴(kuò)充,增加對(duì)編譯器的指導(dǎo)作用,因此,嵌入式匯編看起來晦澀而難以讀懂。

1. 嵌入式匯編的一般形式:

 

__asm__ __volatile__ ("<asm routine>" : output : input : modify);

 

   其中,__asm__表示匯編代碼的開始,其后可以跟__volatile__(這是可選項(xiàng)),其含義是避免“asm”指令被刪除、移動(dòng)或組合;然后就是小括弧,括弧中的內(nèi)容是我們介紹的重點(diǎn):

·   "<asm routine>"為匯編指令部分,例如,"movl %%cr0,%0\n\t"。數(shù)字前加前綴“%“,如%1,%2等表示使用寄存器的樣板操作數(shù)??梢允褂玫牟僮鲾?shù)總數(shù)取決于具體CPU中通用寄存器的數(shù)量,如Intel可以有8個(gè)。指令中有幾個(gè)操作數(shù),就說明有幾個(gè)變量需要與寄存器結(jié)合,由gcc在編譯時(shí)根據(jù)后面輸出部分和輸入部分的約束條件進(jìn)行相應(yīng)的處理。由于這些樣板操作數(shù)的前綴使用了”%“,因此,在用到具體的寄存器時(shí)就在前面加兩個(gè)“%”,如%%cr0。

·   輸出部分(output),用以規(guī)定對(duì)輸出變量(目標(biāo)操作數(shù))如何與寄存器結(jié)合的約束(constraint,輸出部分可以有多個(gè)約束,互相以逗號(hào)分開。每個(gè)約束以“=”開頭,接著用一個(gè)字母來表示操作數(shù)的類型,然后是關(guān)于變量結(jié)合的約束。例如,上例中:

:"=r" (__dummy)

“=r”表示相應(yīng)的目標(biāo)操作數(shù)(指令部分的%0)可以使用任何一個(gè)通用寄存器,并且變量__dummy 存放在這個(gè)寄存器中,但如果是:

:“=m”__dummy

“=m”就表示相應(yīng)的目標(biāo)操作數(shù)是存放在內(nèi)存單元__dummy中。

表示約束條件的字母很多,表 25 給出幾個(gè)主要的約束字母及其含義:

   2.5  主要的約束字母及其含義

      字母

含義

   m, v,o

表示內(nèi)存單元

   R

表示任何通用寄存器

   Q

表示寄存器eax, ebx, ecx,edx之一

   I, h

表示直接操作數(shù)

   E, F

表示浮點(diǎn)數(shù)

   G

表示“任意”

   a, b.c d

表示要求使用寄存器eax/ax/al, ebx/bx/bl, ecx/cx/cledx/dx/dl

   S, D

表示要求使用寄存器esiedi

   I

表示常數(shù)(031

·   輸入部分(Input):輸入部分與輸出部分相似,但沒有“=”。如果輸入部分一個(gè)操作數(shù)所要求使用的寄存器,與前面輸出部分某個(gè)約束所要求的是同一個(gè)寄存器,那就把對(duì)應(yīng)操作數(shù)的編號(hào)(如“1”,“2”等)放在約束條件中,在后面的例子中,我們會(huì)看到這種情況。

·   修改部分(modify:這部分常常以“memory”為約束條件,以表示操作完成后內(nèi)存中的內(nèi)容已有改變,如果原來某個(gè)寄存器的內(nèi)容來自內(nèi)存,那么現(xiàn)在內(nèi)存中這個(gè)單元的內(nèi)容已經(jīng)改變。

 

注意,指令部分為必選項(xiàng),而輸入部分、輸出部分及修改部分為可選項(xiàng),當(dāng)輸入部分存在,而輸出部分不存在時(shí),分號(hào)“:“要保留,當(dāng)“memory”存在時(shí),三個(gè)分號(hào)都要保留,例如system.h中的宏定義__cli()

   #define __cli()                 __asm__ __volatile__("cli": : :"memory")

2.  Linux源代碼中嵌入式匯編舉例

   Linux源代碼中,在arch目錄下的.h.c文件中,很多文件都涉及嵌入式匯編,下面以system.h中的C函數(shù)為例,說明嵌入式匯編的應(yīng)用。

1)簡(jiǎn)單應(yīng)用

#define __save_flags(x)         __asm__ __volatile__("pushfl ; popl %0":"=g" (x): /* no input */)

#define __restore_flags(x)      __asm__ __volatile__("pushl %0 ; popfl": /* no output */

 :"g" (x):"memory", "cc")

第一個(gè)宏是保存標(biāo)志寄存器的值,第二個(gè)宏是恢復(fù)標(biāo)志寄存器的值。第一個(gè)宏中的pushfl指令是把標(biāo)志寄存器的值壓棧。而popl是把棧頂?shù)闹担▌倝喝霔5?/span>flags)彈出到x變量中,這個(gè)變量可以存放在一個(gè)寄存器或內(nèi)存中。這樣,你可以很容易地讀懂第二個(gè)宏。

(2) 較復(fù)雜應(yīng)用

static inline unsigned long get_limit(unsigned long segment)

{

         unsigned long __limit;

         __asm__("lsll %1,%0"

                 :"=r" (__limit):"r" (segment));

        return __limit+1;

}

這是一個(gè)設(shè)置段界限的函數(shù),匯編代碼段中的輸出參數(shù)為__limit(即%0),輸入?yún)?shù)為segment(即%1)。Lsll是加載段界限的指令,即把segment段描述符中的段界限字段裝入某個(gè)寄存器(這個(gè)寄存器與__limit結(jié)合),函數(shù)返回__limit1,即段長。

3)復(fù)雜應(yīng)用

    Linux內(nèi)核代碼中,有關(guān)字符串操作的函數(shù)都是通過嵌入式匯編完成的,因?yàn)閮?nèi)核及用戶程序?qū)ψ址瘮?shù)的調(diào)用非常頻繁,因此,用匯編代碼實(shí)現(xiàn)主要是為了提高效率(當(dāng)然是以犧牲可讀性和可維護(hù)性為代價(jià)的)。在此,我們僅列舉一個(gè)字符串比較函數(shù)strcmp,其代碼在arch/i386string.h中。

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符,在每條命令的結(jié)束加這兩個(gè)符號(hào),是為了讓gcc把嵌入式匯編代碼翻譯成一般的匯編代碼時(shí)能夠保證換行和留有一定的空格。例如,上面的嵌入式匯編會(huì)被翻譯成:

1   lodsb        //裝入串操作數(shù),即從[esi]傳送到al寄存器,然后esi指向串中下一個(gè)元素

      scasb          //掃描串操作數(shù),即從al中減去es:[edi],不保留結(jié)果,只改變標(biāo)志

      jne2f          //如果兩個(gè)字符不相等,則轉(zhuǎn)到標(biāo)號(hào)2   

      testb %al  %al  

      jne 1b

      xorl %eax %eax

      jmp 3f

2:    sbbl %eax %eax

      orb $1 %al

3:

 這段代碼看起來非常熟悉,讀起來也不困難。其中1f 表示往前(forword)找到第一個(gè)標(biāo)號(hào)為1的那一行,相應(yīng)地,1b表示往后找。其中嵌入式匯編代碼中輸出和輸入部分的結(jié)合情況為:

·   返回值__res,放在al寄存器中,與%0相結(jié)合;

·   局部變量d0,與%1相結(jié)合,也與輸入部分的cs參數(shù)相對(duì)應(yīng),也存放在寄存器ESI中,即ESI中存放源字符串的起始地址。

·   局部變量d1, 與%2相結(jié)合,也與輸入部分的ct參數(shù)相對(duì)應(yīng),也存放在寄存器EDI中,即EDI中存放目的字符串的起始地址。

通過對(duì)這段代碼的分析我們應(yīng)當(dāng)體會(huì)到,萬變不利其本,嵌入式匯編與一般匯編的區(qū)別僅僅是形式,本質(zhì)依然不變。因此,全面掌握Intel 386 匯編指令乃突破閱讀低層代碼之根本。

 

 

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類似文章 更多

    国产精品一区二区传媒蜜臀| 精品一区二区三区人妻视频| 欧美日韩综合在线第一页| 久久99国产精品果冻传媒| 欧美国产日本高清在线| 欧洲一区二区三区自拍天堂| 五月综合激情婷婷丁香| 日韩欧美中文字幕人妻| 免费一区二区三区少妇| 国产综合香蕉五月婷在线| 国产亚洲欧美自拍中文自拍| 夫妻性生活动态图视频| 国产性情片一区二区三区 | 不卡视频在线一区二区三区| 国产超薄黑色肉色丝袜| 欧美一级黄片免费视频| 午夜传媒视频免费在线观看| 沐浴偷拍一区二区视频| 伊人久久青草地综合婷婷| 日韩国产亚洲欧美激情| 99久热只有精品视频最新| 太香蕉久久国产精品视频| 日本高清不卡在线一区| 高潮少妇高潮久久精品99| 日本丁香婷婷欧美激情| 免费午夜福利不卡片在线 视频| 亚洲午夜福利不卡片在线| 欧美日韩国产亚洲三级理论片| 在线欧洲免费无线码二区免费| 青青免费操手机在线视频| 欧美日韩国产自拍亚洲| 国产又粗又爽又猛又黄的 | 日本人妻的诱惑在线观看| 97精品人妻一区二区三区麻豆| 日本精品啪啪一区二区三区| 中文字幕精品一区二区年下载| 亚洲最大福利在线观看| 日韩中文字幕狠狠人妻| 精品国产亚洲免费91| 国产又大又猛又粗又长又爽| 日本精品最新字幕视频播放|