日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

C语言内联汇编

發(fā)布時(shí)間:2024/8/1 编程问答 53 豆豆
生活随笔 收集整理的這篇文章主要介紹了 C语言内联汇编 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

在閱讀linux 源碼的時(shí)候,我們會(huì)看到很多C語(yǔ)言?xún)?nèi)聯(lián)匯編的代碼。下面我們集中看看C語(yǔ)言?xún)?nèi)聯(lián)匯編是怎么樣的。

首先,我們得想想為什么會(huì)有在C語(yǔ)言里面內(nèi)聯(lián)匯編的需求。
主要有兩個(gè),一個(gè)是我們覺(jué)得在被頻繁調(diào)用的函數(shù),如果使用C寫(xiě)出來(lái)的代碼,可能執(zhí)行效率達(dá)不到我們的預(yù)期,于是我們就使用匯編語(yǔ)言來(lái)把這個(gè)函數(shù)的邏輯實(shí)現(xiàn)出來(lái),例如qsort函數(shù);
另一個(gè)是我們需要使用某些只能通過(guò)匯編指令才能實(shí)現(xiàn)的功能。可能有人會(huì)問(wèn),還有C語(yǔ)言無(wú)法實(shí)現(xiàn)的功能?這還真的有,例如開(kāi)中斷和關(guān)中斷

#define sti() __asm__ ("sti"::) #define cli() __asm__ ("cli"::)

顯然就只能使用匯編指令來(lái)開(kāi)中斷和關(guān)中斷了。

現(xiàn)在我們來(lái)看看內(nèi)聯(lián)匯編的一些規(guī)則。
內(nèi)聯(lián)匯編的一般格式如下:

asm [volatile] ( AssemblerTemplate : [部分1OutputOperands] [ : 部分2InputOperands[ : 部分3Clobbers ] ]);

首先! 內(nèi)聯(lián)匯編是一個(gè)statement ,也就是一條語(yǔ)句! 因此,一條內(nèi)聯(lián)匯編代碼后邊,需要跟著一個(gè)分號(hào)。

asm是關(guān)鍵字,告訴編譯器之后緊挨著的第一個(gè)小括號(hào)內(nèi)部的就是內(nèi)聯(lián)的匯編代碼,一般也可以把a(bǔ)sm寫(xiě)作__asm__。
volatile 也是關(guān)鍵字,如果寫(xiě)上volatile表示關(guān)閉編譯器對(duì)這段匯編代碼的優(yōu)化。我們看看gcc官網(wǎng)對(duì) volatile 的解釋:

GCC’s optimizers sometimes discard asm statements if they determine there is no need for the output variables. Also, the optimizers may move code out of loops if they believe that the code will always return the same result (i.e. none of its input values change between calls). Using the volatile qualifier disables these optimizations. asm statements that have no output operands, including asm goto statements, are implicitly volatile.

也就是說(shuō),如何gcc編譯器發(fā)現(xiàn)一段內(nèi)聯(lián)匯編代碼的輸出不被使用到,或者它發(fā)現(xiàn)在一個(gè)循環(huán)里面這段代碼一直返回同一個(gè)值,那么它就會(huì)把這段內(nèi)聯(lián)匯編代碼直接discard. 顯然,對(duì)于用一個(gè)問(wèn)題,有千千萬(wàn)萬(wàn)種寫(xiě)法,編譯器只能做一些淺層的優(yōu)化。當(dāng)我們的代碼寫(xiě)的比較復(fù)雜時(shí),它將對(duì)我們的代碼進(jìn)行錯(cuò)誤的優(yōu)化,這是我們不想看到的,因此一般我們會(huì)加上這個(gè)volatile參數(shù)。

接下來(lái)是一個(gè) AssemblerTemplate ,這個(gè)匯編模板要求是一個(gè)包含匯編指令的字符串,里面可以含有一些指向輸入、輸出操作數(shù)的占位符。gcc編譯器通過(guò)一定的規(guī)則,將模板里面所有的占位符替換掉,并將替換后的結(jié)果輸入到匯編器中。

因?yàn)閰R編模板要求是一個(gè)字符串,那么如果我們有多條匯編指令,那怎么辦呢?一個(gè)方法是將所有的匯編指令寫(xiě)在一行,這種方式當(dāng)然可以,但是代碼不美觀。另外一種方法是利用C語(yǔ)言里面相鄰字符串可以直接拼接成一個(gè)長(zhǎng)的字符串這條規(guī)則,我們可以每一行寫(xiě)一條匯編指令,然后使用\ 將不同行的匯編指令字符串合并起來(lái)就可以了。例如一個(gè)合法的匯編模板可以是這樣子的:

"mov %0,%1\n\t " \ "mov %1,%2\n\t"

它其實(shí)就是等價(jià)于:mov %0,%1\n\t mov %1 ,%2
其中以%開(kāi)始的%1,%2···加做占位符,它由下面的輸入輸出操作數(shù)來(lái)決定。既然% 開(kāi)始的都是占位符,那么我想輸出%怎么辦呢?比如我寫(xiě)了這么一條匯編模板:

"mov %eax,%ebx\n\t"

這是會(huì)報(bào)錯(cuò)的,因?yàn)間cc把%eax中的eax當(dāng)做一個(gè)占位符了。此時(shí),我需要使用%%來(lái)轉(zhuǎn)義出%符號(hào)
ok,到目前為止,我們可能會(huì)對(duì)%0,%1,%2···產(chǎn)生疑問(wèn),這些占位符是如何與具體的某個(gè)數(shù)據(jù)產(chǎn)生關(guān)聯(lián)的?
此時(shí)就需要介紹輸出操作數(shù)和輸入操作數(shù)了。
輸出操作數(shù)緊跟著匯編模板,之間隔著一個(gè):號(hào)。
輸出操作數(shù)的格式為:

[asmSymbolicName1] constraint (cvariablename1),[asmSymbolicName1] constraint (cvariablename1)···

第一部分[asmSymbolicName1]叫做asm符號(hào)別名,就是相當(dāng)于給后面的C語(yǔ)言變量cvariablename1設(shè)置一個(gè)匯編里面使用的別名。在匯編指令里面使用%[別名]來(lái)訪問(wèn)這個(gè)變量。
這部分可以省略。另外編譯器默認(rèn)為內(nèi)聯(lián)匯編的每個(gè)輸出、輸入操作數(shù)設(shè)置一個(gè)0,1,2,3,4···的數(shù)字別名。按各個(gè)操作數(shù)出現(xiàn)的次序,依次給這些操作數(shù)設(shè)置對(duì)應(yīng)序號(hào)的數(shù)字別名。這些數(shù)字別名,在匯編模板里面使用%數(shù)字來(lái)訪問(wèn)。
舉個(gè)例子:

int sum(int a,int b) {int rst = 0; __asm__ volatile("addl %1,%2\n\t"\"addl %3,%2\n\t"\"mov %2,%[rst]\n\t"\"mov %%eax,%2\n\t":[rst]"+r"(rst):"a" (a),"b"(b),"c"(123456):);return rst; }

輸出操作數(shù)有:[rst]"+r"(rst),因此,在匯編模板中%[rst]和%0都是與輸出變量rst綁定。
第二部分是一個(gè)約束字符串。約束字符串給出了程序員對(duì)編譯器在轉(zhuǎn)換匯編模板時(shí)候的一些建議。注意,只是建議。常見(jiàn)約束有https://gcc.gnu.org/onlinedocs/gcc/Simple-Constraints.html#Simple-Constraints
最后一個(gè)(cvariablename1) 通過(guò)一個(gè)括號(hào)將所指向的C語(yǔ)言變量指示出來(lái)。
整個(gè)輸出操作數(shù)的描述可以沒(méi)有。
下一部分的輸入操作數(shù)的原理同輸出部分。
最后一個(gè)所謂的破壞域聲明,這個(gè)一般可不填,也只是給編譯器提供的建議而已。

實(shí)例分析

#define _set_gate(gate_addr,type,dpl,addr) \ __asm__ ("movw %%dx,%%ax\n\t" \"movw %0,%%dx\n\t" \"movl %%eax,%1\n\t" \"movl %%edx,%2" \: \: "i" ((short) (0x8000+(dpl<<13)+(type<<8))), \"o" (*((char *) (gate_addr))), \"o" (*(4+(char *) (gate_addr))), \"d" ((char *) (addr)),"a" (0x00080000))

輸入操作數(shù)描述部分:

:"i" ((short) (0x8000+(dpl<<13)+(type<<8))), \"o" (*((char *) (gate_addr))), \"o" (*(4+(char *) (gate_addr))), \"d" ((char *) (addr)),"a" (0x00080000))

說(shuō)明:%0 指向表達(dá)式((short) (0x8000+(dpl<<13)+(type<<8))),其約束i表明它是一個(gè)立即數(shù);
%1指向表達(dá)式*((char *) (gate_addr)),其約束o表示這個(gè)表達(dá)式的值是一個(gè)內(nèi)存地址===>就是會(huì)翻譯成一個(gè)地址而不是立即數(shù)$xxxxxx。
%2指向表達(dá)式*(4+(char *) (gate_addr)),其約束o表示這個(gè)表達(dá)式的值也是一個(gè)內(nèi)存地址。
%3指向表達(dá)式((char *) (addr)),其約束d表示匯編指令的在執(zhí)行前先將(char*)(addr)的值給edx;
%4指向表達(dá)式(0x00080000)), 其約束a表示匯編指令在執(zhí)行前會(huì)先將(4+(char*)(addr))的值給eax;

注意:在AT&T匯編中,立即數(shù)和直接內(nèi)存訪問(wèn)時(shí)不同的。
例如:
head.S中

movl $0x10,%eax # reload all the segment registersmov %ax,%ds # after changing gdt. CS was alreadymov %ax,%es # reloaded in 'setup_gdt'mov %ax,%fsmov %ax,%gslss stack_start,%espxorl %eax,%eax 1: incl %eax # check that A20 really IS enabledmovl %eax,0x000000 # loop forever if it isn't

movl $0x10,%eax中的0x10是立即數(shù),因?yàn)榍懊嬗袀€(gè)$
而movl %eax,0x000000中的0x0000000卻是內(nèi)存偏移量,表示 ds指示的段基址+0x0000000 所指向的內(nèi)存區(qū)域。

總結(jié)

以上是生活随笔為你收集整理的C语言内联汇编的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。