The format of basic inline assembly is very much straight forward. Its basic form ishtml
基本彙編嵌入格式以下:linux
asm("assembly code");
git
Example.express
- asm("movl %ecx %eax"); /* moves the contents of ecx to eax */
- __asm__("movb %bh (%eax)"); /*moves the byte from bh to the memory pointed by eax */
You might have noticed that here I’ve used asm
and __asm__
. Both are valid. We can use__asm__
if the keywordasm
conflicts with something in our program. If we have more than one instructions, we write one per line in double quotes, and also suffix a ’\n’ and ’\t’ to the instruction. This is because gcc sends each instruction as a string toas(GAS) and by using the newline/tab we send correctly formatted lines to the assembler.編程
可能注意到了這裏使用了 asm 和 __asm__. 都是有效的.若是關鍵字asm在程序中有衝突, 則可使用__asm__. 若是咱們須要使用一條以上的彙編指令, 咱們應該每條佔用一行, 用雙引號括起,並加上'\n'和'\t'後綴. 這是由於gcc把用字符串的格式把彙編指令傳給as(GAS), 而後利用換行符, 把它們轉換成正確的彙編格式.小程序
Example.app
- __asm__ ("movl %eax, %ebx\n\t"
- "movl $56, %esi\n\t"
- "movl %ecx, $label(%edx,%ebx,$4)\n\t"
- "movb %ah, (%ebx)");
If in our code we touch (ie, change the contents) some registers and return from asm without fixing those changes, something bad is going to happen. This is because GCC have no idea about the changes in the register contents and this leads us to trouble, especially when compiler makes some optimizations. It will suppose that some register contains the value of some variable that we might have changed without informing GCC, and it continues like nothing happened. What we can do is either use those instructions having no side effects or fix things when we quit or wait for something to crash. This is where we want some extended functionality. Extended asm provides us with that functionality.less
若是咱們的代碼裏使用了寄存器, 而且在返回的時候沒有還原它, 這將有壞的狀況發生. 由於GCC並不知道寄存器的值改變了, 特別是編譯器對代碼進行優化的時候. 編譯器會認爲,那些存放變量的寄存器,咱們並無改變它,而後繼續本身的優化. 爲了不這種狀況, 要麼, 咱們不改變寄存器的值, 要麼, 彙編函數返回以前, 還原寄存器使用前的值, 或者 等着代碼崩潰(wait for something to crash). 正是因爲存在這樣的問題,咱們須要使用"Extended Asm". 它將提供給咱們擴展功能, 解決上邊的問題.ide
In basic inline assembly, we had only instructions. In extended assembly, we can also specify the operands. It allows us to specify the input registers,output registers and a list of clobbered registers. It is not mandatory to specify the registers to use, we can leave that head ache to GCC and that probably fit into GCC’s optimization scheme better. Anyway the basic format is:函數
在基本嵌入彙編格式中,咱們只使用了指令. 在擴展彙編中, 咱們還能夠指定更多操做. 它容許咱們指定輸入寄存器, 輸出寄存器和變化表(clobber list). 咱們並不必定要指定使用哪些寄存器. 咱們能夠把這件事情交給GCC去作. 擴展彙編的格式以下:
- asm ( assembler template
- : output operands /* optional */
- : input operands /* optional */
- : list of clobbered registers /* optional */
- );
The assembler template consists of assembly instructions. Each operand is described by an operand-constraint string followed by the C expression in parentheses. A colon separates the assembler template from the first output operand and another separates the last output operand from the first input, if any. Commas separate the operands within each group. The total number of operands is limited to ten or to the maximum number of operands in any instruction pattern in the machine description, whichever is greater.
這個模板由若干條彙編指令組成, 每一個操做數(括號裏C語言的變量)都有一個限制符(「」中的內容)加以描述. 冒號用來分割輸入的操做和輸出的操做. 若是每組內有多個操做數,用逗號分割它們. 操做數最多爲10個, 或者依照具體機器而異 .
If there are no output operands but there are input operands, you must place two consecutive colons surrounding the place where the output operands would go.
若是沒有輸出操做, 可是又有輸入, 你必須使用連續兩個冒號, 兩個連續冒號中無內容, 表示沒有輸出結果的數據操做 .
Example:
- asm ("cld\n\t"
- "rep\n\t"
- "stosl"
- : /* no output registers */
- : "c" (count), "a" (fill_value), "D" (dest)
- : "%ecx", "%edi"
- );
Now, what does this code do? The above inline fills the fill_value
count
times to the location pointed to by the registeredi
. It also says to gcc that, the contents of registerseax
andedi
are no longer valid. Let us see one more example to make things more clearer.
上面這段代碼作了什麼? 這段內嵌彙編把 fill_value, count裝入寄存器,同時告知GCC,clobber list目錄中的寄存器eax,edi,已經改變. 咱們來看下一個例子:
- int a=10, b;
- asm ("movl %1, %%eax;
- movl %%eax, %0;"
- :"=r"(b) /* output */
- :"r"(a) /* input */
- :"%eax" /* clobbered register */
- );
Here what we did is we made the value of ’b’ equal to that of ’a’ using assembly instructions. Some points of interest are:
代碼目的是讓'b'的值與'a'的值相等.
When the execution of "asm" is complete, "b" will reflect the updated value, as it is specified as an output operand. In other words, the change made to "b" inside "asm" is supposed to be reflected outside the "asm".
當這段彙編代碼執行完畢,'b'變量將會存儲這個結果,,正如例子裏聲明這個變量爲輸出。 換句話說, 'b'用來反映彙編程序裏值的變化。
Now we may look each field in detail.
如今,深刻的理解每一塊,看看細節。
The assembler template contains the set of assembly instructions that gets inserted inside the C program. The format is like: either each instruction should be enclosed within double quotes, or the entire group of instructions should be within double quotes. Each instruction should also end with a delimiter. The valid delimiters are newline(\n) and semicolon(;). ’\n’ may be followed by a tab(\t). We know the reason of newline/tab, right?. Operands corresponding to the C expressions are represented by %0, %1 ... etc.
這個彙編模板包含一套完整的彙編指令,幫助在c語言內嵌入彙編語言。具體格式以下:每條指令應該加上雙括號,或者給整套匯編指令加上雙括號(如,最後一個例子)。每條指令結尾都應加上結束符,合法的結束符有(\n)和(;),或許還應該在 \n後邊加上一個 \t,咱們應該瞭解緣由吧? 括號裏的若干操做數,依次對應%0,%1。。。等。
C expressions serve as operands for the assembly instructions inside "asm". Each operand is written as first an operand constraint in double quotes. For output operands, there’ll be a constraint modifier also within the quotes and then follows the C expression which stands for the operand. ie,
"constraint" (C expression) is the general form. For output operands an additional modifier will be there. Constraints are primarily used to decide the addressing modes for operands. They are also used in specifying the registers to be used.
內嵌的彙編指令須要C變量爲其提供一個操做數,這個操做數應加上括號。以輸出操做爲例,首先會有一個限制符,而後跟上C變量,運算結果將存入這個變量。
雙引號內的「限制符」是一個規定的格式。在輸出操做中,這個限制符會額外多一個符號(=)。限制符主要用來決定操做數的尋址方式。同時還可指定使用某一個寄存器。
If we use more than one operand, they are separated by comma.
In the assembler template, each operand is referenced by numbers. Numbering is done as follows. If there are a total of n operands (both input and output inclusive), then the first output operand is numbered 0, continuing in increasing order, and the last input operand is numbered n-1. The maximum number of operands is as we saw in the previous section.
Output operand expressions must be lvalues. The input operands are not restricted like this. They may be expressions. The extended asm feature is most often used for machine instructions the compiler itself does not know as existing ;-). If the output expression cannot be directly addressed (for example, it is a bit-field), our constraint must allow a register. In that case, GCC will use the register as the output of the asm, and then store that register contents into the output.
As stated above, ordinary output operands must be write-only; GCC will assume that the values in these operands before the instruction are dead and need not be generated. Extended asm also supports input-output or read-write operands.
若是咱們不止一個操做(有輸入,有輸出),就必須使用冒號將他們分開。
在標準彙編模板中,每一個操做數會有一個Number與之對應。若是咱們一共使用了n個操做數,那麼輸出操做裏的第一個操做數就排0號,以後遞增,因此最後一個輸出操做的操做數編號爲n-1。操做數的最多個數,前邊已經提到過了。(通常最多10個或者某些機器指令支持更多)
輸出操做的表達式必須是數值,輸入操做沒有這個限制,他多是表達式。擴展彙編經常用於實現機器平臺自身特殊的指令,編譯器可能並不能識別他們:-)。若是輸出表達式不能直接被尋址(好比,他是一個位字段),咱們就應該使用「限制符」指定一個寄存器。這樣,GCC會使用此寄存器存儲輸出結果,而後再將寄存器的值存入輸出操做數。
So now we concentrate on some examples. We want to multiply a number by 5.For that we use the instructionlea
.
咱們如今分析幾個例子。咱們想給一個數乘以5。所以,咱們使用lea指令: (彙編語句leal(r1,r2,4),r3語句表示r1+r2*4→r3。這個例子能夠很是快地將x乘5。)
- asm ("leal (%1,%1,4), %0"
- : "=r" (five_times_x)
- : "r" (x)
- );
Here our input is in ’x’. We didn’t specify the register to be used. GCC will choose some register for input, one for output and does what we desired.If we want the input and output to reside in the same register, we can instruct GCC to do so. Here we use those types of read-write operands. By specifying proper constraints, here we do it.
這裏,輸入一個變量x,咱們並沒指定特定的寄存器來存儲它,GCC會選擇一個(「r」表示gcc選擇)。如咱們所要求的,gcc會自動選擇兩個寄存器,一個給input,一個給output。若是咱們想給input和output指定同一個寄存器,咱們能夠要求GCC這樣作(經過更改「限制符」內容)。
- asm ("leal (%0,%0,4), %0"
- : "=r" (five_times_x)
- : "0" (x)
- );
Now the input and output operands are in the same register. But we don’t know which register. Now if we want to specify that also, there is a way.
上例,咱們就讓input和output使用同一個寄存器,可是不知道具體哪個。(若是輸入操做的限制符爲0或爲空,則說明使用與相應輸出同樣的寄存器。)若是,咱們想指定使用具體一個寄存器,能夠看看以下代碼:「c」表示使用寄存器ecx。
- asm ("leal (%%ecx,%%ecx,4), %%ecx"
- : "=c" (x)
- : "c" (x)
- );
In all the three examples above, we didn’t put any register to the clobber list. why? In the first two examples, GCC decides the registers and it knows what changes happen. In the last one, we don’t have to putecx
on the clobberlist, gcc knows it goes into x. Therefore, since it can know the value ofecx
, it isn’t considered clobbered.
上面三個例子,咱們沒有把任何寄存器放入clobber list,爲何?前兩個例子,由GCC選擇寄存器,因此它知道那些寄存器值改變了。最後一個例子,咱們沒有把ecx寄存器放入clobber list,GCC知道它的值變成x了。所以,既然GCC知道ecx寄存器的值,就不必加入到clobber list。
Some instructions clobber some hardware registers. We have to list those registers in the clobber-list, ie the field after the third ’:’ in the asm function. This is to inform gcc that we will use and modify them ourselves. So gcc will not assume that the values it loads into these registers will be valid. We shoudn’t list the input and output registers in this list. Because, gcc knows that "asm" uses them (because they are specified explicitly as constraints). +If the instructions use any other registers, implicitly or explicitly (and the registers are not present either in input or in the output constraint list), then those registers have to be specified in theclobbered list.
一些指令改變了硬件寄存器的值。這時須要在clobber list中列舉出這些寄存器,位置所在彙編代碼的最後一個「:」以後。這是爲了告知GCC,咱們將使用和更改列舉出的寄存器。那麼,GCC就知道以前裝載到寄存器裏的值已經無效了,不會使用寄存器的舊值進行錯誤操做。咱們沒必要把input,output所使用的寄存器列入clobber list,由於GCC知道彙編代碼已經使用和改變了那些寄存器。
If our instruction can alter the condition code register, we have to add "cc" to the list of clobbered registers.
若是彙編代碼將改變條件碼寄存器,咱們須要在clobber list中加入「cc」。
If our instruction modifies memory in an unpredictable fashion, add "memory" to the list of clobbered registers. This will cause GCC to not keep memory values cached in registers across the assembler instruction. We also have to add thevolatile keyword if the memory affected is not listed in the inputs or outputs of the asm.
若是彙編指令更改了內存值,需在clobber list中加入「memory」。這樣,在 彙編語句執行過程當中,GCC再也不使用寄存器內的值。咱們還須要加入volatile關鍵字,若是彙編的輸出輸入操做影響到了內存值,並且並無將這種變化加入到clobber list。
We can read and write the clobbered registers as many times as we like. Consider the example of multiple instructions in a template; it assumes the subroutine _foo accepts arguments in registerseax
andecx
.
clobber list中的寄存器能夠反覆讀寫。參考下面這個例子,代碼子程序__foo用eax,ecx寄存器傳遞參數。則這倆寄存器的值再也不可靠,因此加入到clobber list中。
- asm ("movl %0,%%eax;
- movl %1,%%ecx;
- call _foo"
- : /* no outputs */
- : "g" (from), "g" (to)
- : "eax", "ecx"
- );
If you are familiar with kernel sources or some beautiful code like that, you must have seen many functions declared asvolatile
or__volatile__
which follows anasm
or__asm__
. I mentioned earlier about the keywordsasm
and__asm__
. So what is thisvolatile
?
If our assembly statement must execute where we put it, (i.e. must not be moved out of a loop as an optimization), put the keywordvolatile
after asm and before the ()’s. So to keep it from moving, deleting and all, we declare it as
asm volatile ( ... : ... : ... : ...);
若是你熟悉內核代碼或者像她同樣漂亮的代碼,你必定見到過許多函數被volatile或__volatile__修飾,一般緊跟在 asm或__asm__後邊。我先前提到過asm和__asm__的區別。那volatile呢?
Use __volatile__
when we have to be verymuch careful.
If our assembly is just for doing some calculations and doesn’t have any side effects, it’s better not to use the keywordvolatile
. Avoiding it helps gcc in optimizing the code and making it more beautiful.
In the section Some Useful Recipes
, I have provided many examples for inline asm functions. There we can see the clobber-list in detail.
若是咱們的彙編代碼僅僅作一些計算而且沒有什麼反作用,那麼最好不用volatile。不使用它,能夠幫助GCC優化代碼,讓代碼更漂亮。
下邊Some Useful Recipes部分,我提供了許多內嵌彙編代碼的例子,咱們能夠看到更多細節。
By this time, you might have understood that constraints have got a lot to do with inline assembly. But we’ve said little about constraints. Constraints can say whether an operand may be in a register, and which kinds of register;whether the operand can be a memory reference, and which kinds of address; whether the operand may be an immediate constant, and which possible values (ie range of values)it may have.... etc.
看到這裏,你應該知道彙編裏的限制符(constraint)作了不少的事。但,咱們只花了不多的篇幅敘述限制符。好比,限制符能夠指定一個寄存器,限制符能夠指向一塊內存空間,限制符能夠是一個當即數。。。等。
There are a number of constraints of which only a few are used frequently. We’ll have a look at those constraints.
有大量的限制符,咱們經常使用使用其中不多一部份,如今來看看:
When operands are specified using this constraint, they get stored in General Purpose Registers(GPR). Take the following example:
當操做指定了「r」限制符,那麼操做數將會被存儲在通用寄存器內。看下例:
asm ("movl %%eax, %0\n" :"=r"(myval));
Here the variable myval is kept in a register, the value in register eax
is copied onto that register, and the value ofmyval
is updated into the memory from this register. When the "r" constraint is specified, gcc may keep the variable in any of the available GPRs. To specify the register, you must directly specify the register names by using specific register constraints. They are:
這裏的變量myval被存儲在一個寄存器內,代碼將eax寄存器的值拷貝到myval佔用的寄存器內,而後myval寄存器的值將更新myval的內存值。當「r」限制符被指定,GCC可能分配任意一個通用寄存器來存儲操做數。若是要確切使用某個寄存器,你應該指定這個寄存器名稱,經過下表的格式:
- +---+--------------------+
- | r | Register(s) |
- +---+--------------------+
- | a | %eax, %ax, %al |
- | b | %ebx, %bx, %bl |
- | c | %ecx, %cx, %cl |
- | d | %edx, %dx, %dl |
- | S | %esi, %si |
- | D | %edi, %di |
- +---+--------------------+
When the operands are in the memory, any operations performed on them will occur directly in the memory location, as opposed to register constraints, which first store the value in a register to be modified and then write it back to the memory location. But register constraints are usually used only when they are absolutely necessary for an instruction or they significantly speed up the process. Memory constraints can be used most efficiently in cases where a C variable needs to be updated inside "asm" and you really don’t want to use a register to hold its value. For example, the value of idtr is stored in the memory location loc:
若是限制符「m」後的操做數在內存中,任何對它們的操做都會直接更改內存值。與「r」限制符不一樣,「r」首先將操做數保存在寄存器內,而後在寄存器裏進行數據操做,接着把數據寫回內存區域。使用「r」限制符,一般是因爲某些指令必須使用,或者爲了加快程序運行,因此佔用寄存器。「m」限制符運用更頻繁,當咱們但願在彙編執行過程當中就更新內存,或者不但願額外佔用一個寶貴的寄存器來裝載變量值,就使用「m」限制符。以下:idtr的值就被保存在loc那塊內存。
asm("sidt %0\n" : :"m"(loc));
In some cases, a single variable may serve as both the input and theoutput operand. Such cases may be specified in "asm" by using matching constraints.
在某些狀況下,輸出輸入操做多是同一個操做數。這種狀況咱們須要指定匹配限制符「數字」。
asm ("incl %0" :"=a"(var):"0"(var));
We saw similar examples in operands subsection also. In this example for matching constraints, the register %eax is used as both the input and the output variable. var input is read to %eax and updated %eax is stored in var again after increment. "0" here specifies the same constraint as the 0th output variable. That is, it specifies that the output instance of var should be stored in %eax only. This constraint can be used:
上邊見到過相似的例子,此例「0」使用了匹配限制符,寄存器eax同時供input,output使用。輸入變量var被讀入到eax,運算結束後,再被存儲到eax。「0」這個限制符表示:與第0個操做數使用相同的寄存器。這樣,就指明瞭輸出輸入使用同一個寄存器。這個限制符在以下地方可能用到:
The most important effect of using matching restraints is that they lead to the efficient use of available registers.
使用匹配限制符最重要的做用是:使得對有限寄存器資源使用更高效。
Some other constraints used are:
其餘一些限制符:
Following constraints are x86 specific.
下面的限制符是x86特定的:
While using constraints, for more precise control over the effects of constraints, GCC provides us with constraint modifiers. Mostly used constraint modifiers are
在使用限制符的時候,爲了更準確的利用限制符的功能,GCC提供給咱們一些限制語句修飾符。最經常使用的修飾符有:「=」,「&」。
The list and explanation of constraints is by no means complete. Examples can give a better understanding of the use and usage of inline asm. In the next section we’ll see some examples, there we’ll find more about clobber-lists and constraints.
上邊對限制符的解釋毫不徹底,下邊的例子可讓咱們更好的理解嵌入彙編的用法。下邊一節咱們將看一些例子,那裏咱們將遇到更多的colbber list和限制符。
Now we have covered the basic theory about GCC inline assembly, now we shall concentrate on some simple examples. It is always handy to write inlineasm functions as MACRO’s. We can see many asm functions in the kernel code.(/usr/src/linux/include/asm/*.h).
咱們已經接觸過內嵌彙編的基本理論。如今咱們專一幾個例子。使用內嵌彙編來定義宏是很是精妙的,咱們常常能夠在內核代碼中看到。
First we start with a simple example. We’ll write a program to add two numbers.
首先,咱們寫個小程序,使兩個數作加法。
- int main(void)
- {
- int foo = 10, bar = 15;
- __asm__ __volatile__("addl %%ebx,%%eax"
- :"=a"(foo)
- :"a"(foo), "b"(bar)
- );
- printf("foo+bar=%d\n", foo);
- return 0;
- }
Here we insist GCC to store foo in %eax, bar in %ebx and we also want the result in %eax. The ’=’ sign shows that it is an output register. Now we can add an integer to a variable in some other way.
這裏咱們指定GCC使用eax寄存器來存儲foo,bar存放在ebx寄存器,而且結果存放到eax中。這個「=」表示這是輸出結果的寄存器。接下來,咱們用其餘方法把一個整數存放進一個變量中。
- __asm__ __volatile__(
- " lock ;\n"
- " addl %1,%0 ;\n"
- : "=m" (my_var)
- : "ir" (my_int), "m" (my_var)
- : /* no clobber-list */
- );
This is an atomic addition. We can remove the instruction ’lock’ to remove the atomicity. In the output field, "=m" says that my_var is an output and it is in memory. Similarly, "ir" says that, my_int is an integer and should reside in some register (recall the table we saw above). No registers are in the clobber list.
這是一個(atomic addition)。咱們能夠去掉‘lock’來去除這個(atomicity)。在輸出區域,「=m」是說my_var是輸出操做數,而且它在內存裏。「ir」是說my_int是一個整數,而且會選擇一個通用寄存器存放它。沒有寄存器在clobber list。
Now we’ll perform some action on some registers/variables and compare the value.
如今咱們實現一些操做,使用寄存器/變量,並比較他們的值。
- __asm__ __volatile__( "decl %0; sete %1"
- : "=m" (my_var), "=q" (cond)
- : "m" (my_var)
- : "memory"
- );
Here, the value of my_var is decremented by one and if the resulting value is 0
then, the variable cond is set. We can add atomicity by adding an instruction "lock;\n\t"as the first instruction in assembler template.
In a similar way we can use "incl %0" instead of "decl %0", so as to increment my_var.
Points to note here are that (i) my_var is a variable residing in memory. (ii) cond is in any of the registers eax, ebx, ecx and edx. The constraint "=q" guarantees it. (iii) And we can see that memory is there in the clobber list. ie, the code is changing the contents of memory.
這裏,my_var的值遞減到1,若是結果值爲0。那麼,cond就肯定了。
要點:1.my_var存在內存中。2.cond被「=q」限制,因此它佔用 eax,ebx,ecx,edx中的一個。3.memory在clobber list中,即,代碼改變了內存。
How to set/clear a bit in a register? As next recipe, we are going to see it.
怎麼設置/清除寄存器的一位?下個例子:
- __asm__ __volatile__( "btsl %1,%0"
- : "=m" (ADDR)
- : "Ir" (pos)
- : "cc"
- );
Here, the bit at the position ’pos’ of variable at ADDR ( a memory variable ) is set to1
We can use ’btrl’ for ’btsl’ to clear the bit. The constraint "Ir" of pos says that, pos is in a register, and it’s value ranges from 0-31 (x86 dependant constraint). ie, we can set/clear any bit from 0th to 31st of the variable at ADDR. As the condition codes will be changed, we are adding "cc" to clobberlist.
內存變量ADDR的'pos'字位被置1。咱們可使用‘btrl’指令來清除這位。限制符「Ir」表示pos存放到寄存器內,而且pos的範圍是0~31(x86專用限制符,上邊有說起)。隨着條件代碼的改變,咱們把「cc」加入到clobber list。
Now we look at some more complicated but useful function. String copy.
如今咱們看實現String copy函數:
- static inline char * strcpy(char * dest,const char *src)
- {
- int d0, d1, d2;
- __asm__ __volatile__( "1:\tlodsb\n\t"
- "stosb\n\t"
- "testb %%al,%%al\n\t"
- "jne 1b"
- : "=&S" (d0), "=&D" (d1), "=&a" (d2)
- : "0" (src),"1" (dest)
- : "memory");
- return dest;
- }
The source address is stored in esi, destination in edi, and then starts the copy, when we reach at 0, copying is complete. Constraints "&S", "&D", "&a"say that the registers esi, edi and eax are early clobber registers, ie, their contents willchange before the completion of the function. Here also it’s clear that why memory is in clobberlist.
We can see a similar function which moves a block of double words. Notice that the function is declared as a macro.
源地址存放在esi中,目的地址在edi,接下來開始拷貝。當到達0時,程序完成。限制符「&S」,「&D」,「&a」分別指定esi,edi,eax,而且他們是早期改變的寄存器,即,他們的內容在程序結束前,就會有變化。一樣memroy出如今clobber list。下例,相似的功能,移動兩個字節的內存塊,注意,它是以宏的方式實現的。
- #define mov_blk(src, dest, numwords) \
- __asm__ __volatile__ ( \
- "cld\n\t" \
- "rep\n\t" \
- "movsl" \
- : \
- : "S" (src), "D" (dest), "c" (numwords) \
- : "%ecx", "%esi", "%edi" \
- )
Here we have no outputs, so the changes that happen to the contents of the registers ecx, esi and edi are side effects of the block movement. So we have to add them to the clobber list.
這裏沒有輸出,使用兩個連續的::。ecx,esi,edi的變化實現內存塊搬移。因此,把他們也加到clobber list。
In Linux, system calls are implemented using GCC inline assembly. Let us look how a system call is implemented.All the system calls are written as macros (linux/unistd.h). For example, a system call with three arguments is defined as a macro as shown below.
Linux中,系統調用用內嵌彙編實現。咱們一塊兒看看系統調用怎樣實現的。全部的系統調用都用匯編書寫。例如,系統調用3個參數,被定義成以下宏:
- #define _syscall3(type,name,type1,arg1,type2,arg2,type3,arg3) \
- type name(type1 arg1,type2 arg2,type3 arg3) \
- { \
- long __res; \
- __asm__ volatile ( "int $0x80" \
- : "=a" (__res) \
- : "0" (__NR_##name),"b" ((long)(arg1)),"c" ((long)(arg2)), \
- "d" ((long)(arg3))); \
- __syscall_return(type,__res); \
- }
Whenever a system call with three arguments is made, the macro shown above is used to make the call.The syscall number is placed in eax, then each parameters in ebx, ecx, edx. And finally "int 0x80" is the instruction which makes the system call work. The return value can be collected from eax.
每當系統調用一個3參數的函數時,就使用上邊這個宏來調用。系統調用的名稱name被放置在eax,其後的參數分別佔用ebx,ecx,edx。最後「int 0x80」這條指令實現了調用,並且返回值存入eax內。
Every system calls are implemented in a similar way. Exit is a single parameter syscall and let’s see how it’s code will look like. It is as shown below.
每一個系統調用的實現都類似,Exit()系統函數只有一個參數,咱們來看看它的代碼長什麼樣子。以下:
- {
- asm("movl $1,%%eax; /* SYS_exit is 1 */
- xorl %%ebx,%%ebx; /* Argument is in ebx, it is 0 */
- int $0x80" /* Enter kernel mode */
- );
- }
The number of exit is "1" and here, it’s parameter is 0. So we arrange eax to contain 1 and ebx to contain 0 and byint $0x80
, theexit(0)
is executed. This is how exit works.
exit的編號是1,它的參數是0。因此咱們安排eax存放1,ebx存放2,而後指令「int $0x80」,exit(0)就執行了。這就是它的工做方式。
This document has gone through the basics of GCC Inline Assembly. Once you have understood the basic concept it is not difficult to take steps by your own. We saw some examples which are helpful in understanding the frequently used features of GCC Inline Assembly.
GCC Inlining is a vast subject and this article is by no means complete. More details about the syntax’s we discussed about is available in the official documentation for GNU Assembler. Similarly, for a complete list of theconstraints refer to the official documentation of GCC.
And of-course, the Linux kernel use GCC Inline in a large scale. So we can find many examples of various kinds in the kernel sources. They can help us a lot.
If you have found any glaring typos, or outdated info in thisdocument, please let us know.
這個文檔縱覽了GCC內嵌彙編的基本內容。一旦你理解了這些基本概念,那麼修行靠我的。
GCC內嵌彙編是一個巨大的工程,而且這樣的藝術之做毫不會完成。更多細節請參見GNU Assembler官方文檔。
固然,Linux大規模使用了GCC內嵌彙編,因此咱們從中能夠找到大量的例子。她們會給咱們不少幫助。