免费视频淫片aa毛片_日韩高清在线亚洲专区vr_日韩大片免费观看视频播放_亚洲欧美国产精品完整版

打開APP
userphoto
未登錄

開通VIP,暢享免費(fèi)電子書等14項(xiàng)超值服

開通VIP
GCC-Inline-Assembly-HOWTO(C語言嵌入?yún)R編)

GCC-Inline-Assembly-HOWTO(C語言嵌入?yún)R編)

分類: arm 155人閱讀 評(píng)論(0) 收藏 舉報(bào)

怎樣在C語言嵌入?yún)R編代碼.

抱歉,不知什么緣故,所有例子里的代碼都排成了一行,我在編輯模式里換行正常,很漂亮的代碼。請(qǐng)知道如何解決的朋友告訴我下。

源網(wǎng)頁:    >>WIKI<<   

4. Basic Inline.

The format of basic inline assembly is very much straight forward. Its basic form is

基本匯編嵌入格式如下:

asm("assembly code");

Example.


  1. asm("movl %ecx %eax"); /* moves the contents of ecx to eax */  
  2. __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__. 都是有效的.如果關(guān)鍵字asm在程序中有沖突, 則可以使用__asm__. 如果我們需要使用一條以上的匯編指令, 我們應(yīng)該每條占用一行, 用雙引號(hào)括起,并加上'\n'和'\t'后綴. 這是因?yàn)間cc把用字符串的格式把匯編指令傳給as(GAS), 然后利用換行符, 把它們轉(zhuǎn)換成正確的匯編格式.


Example.


  1. __asm__ ("movl %eax, %ebx\n\t"  
  2.          "movl $56, %esi\n\t"  
  3.          "movl %ecx, $label(%edx,%ebx,$4)\n\t"  
  4.          "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.

如果我們的代碼里使用了寄存器, 并且在返回的時(shí)候沒有還原它, 這將有壞的情況發(fā)生. 因?yàn)镚CC并不知道寄存器的值改變了, 特別是編譯器對(duì)代碼進(jìn)行優(yōu)化的時(shí)候. 編譯器會(huì)認(rèn)為,那些存放變量的寄存器,我們并沒有改變它,然后繼續(xù)自己的優(yōu)化. 為了避免這種情況, 要么, 我們不改變寄存器的值, 要么, 匯編函數(shù)返回之前, 還原寄存器使用前的值, 或者 等著代碼崩潰(wait for something to crash). 正是由于存在這樣的問題,我們需要使用"Extended Asm". 它將提供給我們擴(kuò)展功能, 解決上邊的問題.


5. Extended Asm.

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:

在基本嵌入?yún)R編格式中,我們只使用了指令. 在擴(kuò)展匯編中, 我們還可以指定更多操作. 它允許我們指定輸入寄存器, 輸出寄存器和變化表(clobber list).  我們并不一定要指定使用哪些寄存器. 我們可以把這件事情交給GCC去做.  擴(kuò)展匯編的格式如下:


  1. asm ( assembler template   
  2.     : output operands                  /* optional */  
  3.     : input operands                   /* optional */  
  4.     : list of clobbered registers      /* optional */  
  5.     );  

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.

這個(gè)模板由若干條匯編指令組成, 每個(gè)操作數(shù)(括號(hào)里C語言的變量)都有一個(gè)限制符(“”中的內(nèi)容)加以描述. 冒號(hào)用來分割輸入的操作和輸出的操作. 如果每組內(nèi)有多個(gè)操作數(shù),用逗號(hào)分割它們.  操作數(shù)最多為10個(gè), 或者依照具體機(jī)器而異 .

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.

如果沒有輸出操作, 但是又有輸入, 你必須使用連續(xù)兩個(gè)冒號(hào), 兩個(gè)連續(xù)冒號(hào)中無內(nèi)容, 表示沒有輸出結(jié)果的數(shù)據(jù)操作 .

Example:


  1. asm ("cld\n\t"  
  2.      "rep\n\t"  
  3.      "stosl"  
  4.      : /* no output registers */  
  5.      : "c" (count), "a" (fill_value), "D" (dest)  
  6.      : "%ecx""%edi"   
  7.      );  

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.

上面這段代碼做了什么? 這段內(nèi)嵌匯編把 fill_value, count裝入寄存器,同時(shí)告知GCC,clobber list目錄中的寄存器eax,edi,已經(jīng)改變.  我們來看下一個(gè)例子:


  1. int a=10, b;  
  2. asm ("movl %1, %%eax;   
  3.       movl %%eax, %0;"  
  4.      :"=r"(b)        /* output */  
  5.      :"r"(a)         /* input */  
  6.      :"%eax"         /* clobbered register */  
  7.      );        

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'的值相等.

  • "b" is the output operand, referred to by %0 and "a" is the input operand, referred to by %1.
  • "r" is a constraint on the operands. We’ll see constraints in detail later. For the time being, "r" says to GCC to use any register for storing the operands. output operand constraint should have a constraint modifier "=".And this modifier says that it is the output operand and is write-only.
  • There are two %’s prefixed to the register name. This helps GCC to distinguish between the operands and registers. operands have a single % as prefix.
  • The clobbered register %eax after the third colon tells GCC that the value of %eax is to be modified inside "asm", so GCC won’t use this register to store any other value.
  • 'b'是要輸出的數(shù)據(jù),%0也指它。 'a'是輸入的數(shù)據(jù),%1也指它。
  • 'r' 是對(duì)操作數(shù)的約束。呆會(huì)在詳細(xì)了解。 暫時(shí)這樣理解,‘r’告訴GCC選擇一個(gè)可用的寄存器來保存這個(gè)操作數(shù)。 輸出操作數(shù),應(yīng)該使用‘=’, 表示這個(gè)數(shù)據(jù)只寫。
  • 雙%%前綴,指明這是一個(gè)寄存器名。 單%指明操作數(shù)。 這幫組GCC辨別 操作數(shù)和寄存器。
  • 第三個(gè)冒號(hào)后邊, 這個(gè)變化表(clobber list)里的寄存器%eax,告訴gcc聲明的寄存器值已經(jīng)改變,這樣,GCC不會(huì)在其他地方使用這個(gè)寄存器了。

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".

當(dāng)這段匯編代碼執(zhí)行完畢,'b'變量將會(huì)存儲(chǔ)這個(gè)結(jié)果,,正如例子里聲明這個(gè)變量為輸出。 換句話說, 'b'用來反映匯編程序里值的變化。

Now we may look each field in detail.

現(xiàn)在,深入的理解每一塊,看看細(xì)節(jié)。


5.1 Assembler Template.

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.

這個(gè)匯編模板包含一套完整的匯編指令,幫助在c語言內(nèi)嵌入?yún)R編語言。具體格式如下:每條指令應(yīng)該加上雙括號(hào),或者給整套匯編指令加上雙括號(hào)(如,最后一個(gè)例子)。每條指令結(jié)尾都應(yīng)加上結(jié)束符,合法的結(jié)束符有(\n)和(;),或許還應(yīng)該在 \n后邊加上一個(gè) \t,我們應(yīng)該了解原因吧? 括號(hào)里的若干操作數(shù),依次對(duì)應(yīng)%0,%1。。。等。

5.2 Operands.

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.

內(nèi)嵌的匯編指令需要C變量為其提供一個(gè)操作數(shù),這個(gè)操作數(shù)應(yīng)加上括號(hào)。以輸出操作為例,首先會(huì)有一個(gè)限制符,然后跟上C變量,運(yùn)算結(jié)果將存入這個(gè)變量。

雙引號(hào)內(nèi)的“限制符”是一個(gè)規(guī)定的格式。在輸出操作中,這個(gè)限制符會(huì)額外多一個(gè)符號(hào)(=)。限制符主要用來決定操作數(shù)的尋址方式。同時(shí)還可指定使用某一個(gè)寄存器。


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.

如果我們不止一個(gè)操作(有輸入,有輸出),就必須使用冒號(hào)將他們分開。

在標(biāo)準(zhǔn)匯編模板中,每個(gè)操作數(shù)會(huì)有一個(gè)Number與之對(duì)應(yīng)。如果我們一共使用了n個(gè)操作數(shù),那么輸出操作里的第一個(gè)操作數(shù)就排0號(hào),之后遞增,所以最后一個(gè)輸出操作的操作數(shù)編號(hào)為n-1。操作數(shù)的最多個(gè)數(shù),前邊已經(jīng)提到過了。(一般最多10個(gè)或者某些機(jī)器指令支持更多)

輸出操作的表達(dá)式必須是數(shù)值,輸入操作沒有這個(gè)限制,他可能是表達(dá)式。擴(kuò)展匯編常常用于實(shí)現(xiàn)機(jī)器平臺(tái)自身特殊的指令,編譯器可能并不能識(shí)別他們:-)。如果輸出表達(dá)式不能直接被尋址(比如,他是一個(gè)位字段),我們就應(yīng)該使用“限制符”指定一個(gè)寄存器。這樣,GCC會(huì)使用此寄存器存儲(chǔ)輸出結(jié)果,然后再將寄存器的值存入輸出操作數(shù)。

So now we concentrate on some examples. We want to multiply a number by 5.For that we use the instructionlea.

我們現(xiàn)在分析幾個(gè)例子。我們想給一個(gè)數(shù)乘以5。因此,我們使用lea指令:   (匯編語句leal(r1,r2,4),r3語句表示r1+r2*4→r3。這個(gè)例子可以非??斓貙乘5。


  1. asm ("leal (%1,%1,4), %0"  
  2.      : "=r" (five_times_x)  
  3.      : "r" (x)   
  4.      );  

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.

這里輸入一個(gè)變量x,我們并沒指定特定的寄存器來存儲(chǔ)它,GCC會(huì)選擇一個(gè)(“r”表示gcc選擇)。如我們所要求的,gcc會(huì)自動(dòng)選擇兩個(gè)寄存器,一個(gè)給input一個(gè)給output。如果我們想給input和output指定同一個(gè)寄存器,我們可以要求GCC這樣做(通過更改“限制符”內(nèi)容)。


  1. asm ("leal (%0,%0,4), %0"  
  2.      : "=r" (five_times_x)  
  3.      : "0" (x)   
  4.      );  

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使用同一個(gè)寄存器,但是不知道具體哪一個(gè)。(如果輸入操作的限制符為0或?yàn)榭眨瑒t說明使用與相應(yīng)輸出一樣的寄存器。)如果,我們想指定使用具體一個(gè)寄存器,可以看看如下代碼:“c”表示使用寄存器ecx。


  1. asm ("leal (%%ecx,%%ecx,4), %%ecx"  
  2.      : "=c" (x)  
  3.      : "c" (x)   
  4.      );  

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.

上面三個(gè)例子,我們沒有把任何寄存器放入clobber list,為什么?前兩個(gè)例子,由GCC選擇寄存器,所以它知道那些寄存器值改變了。最后一個(gè)例子,我們沒有把ecx寄存器放入clobber list,GCC知道它的值變成x了。因此,既然GCC知道ecx寄存器的值,就沒必要加入到clobber list

5.3 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.

一些指令改變了硬件寄存器的值。這時(shí)需要在clobber list中列舉出這些寄存器,位置所在匯編代碼的最后一個(gè)“:”之后。這是為了告知GCC,我們將使用和更改列舉出的寄存器。那么,GCC就知道之前裝載到寄存器里的值已經(jīng)無效了,不會(huì)使用寄存器的舊值進(jìn)行錯(cuò)誤操作。我們不必把input,output所使用的寄存器列入clobber list,因?yàn)镚CC知道匯編代碼已經(jīng)使用和改變了那些寄存器。

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.

如果匯編指令更改了內(nèi)存值,需在clobber list中加入“memory”。這樣,在 匯編語句執(zhí)行過程中,GCC不再使用寄存器內(nèi)的值。我們還需要加入volatile關(guān)鍵字,如果匯編的輸出輸入操作影響到了內(nèi)存值,而且并沒有將這種變化加入到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中的寄存器可以反復(fù)讀寫。參考下面這個(gè)例子,代碼子程序__foo用eax,ecx寄存器傳遞參數(shù)。則這倆寄存器的值不再可靠,所以加入到clobber list中。


  1. asm ("movl %0,%%eax;  
  2.       movl %1,%%ecx;  
  3.       call _foo"  
  4.      : /* no outputs */  
  5.      : "g" (from), "g" (to)  
  6.      : "eax""ecx"  
  7.      );  

5.4 Volatile ...?

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 ( ... : ... : ... : ...);

如果你熟悉內(nèi)核代碼或者像她一樣漂亮的代碼,你一定見到過許多函數(shù)被volatile或__volatile__修飾,通常緊跟在 asm或__asm__后邊。我先前提到過asm和__asm__的區(qū)別。那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.

如果我們的匯編代碼僅僅做一些計(jì)算并且沒有什么副作用,那么最好不用volatile。不使用它,可以幫助GCC優(yōu)化代碼,讓代碼更漂亮。

下邊Some Useful Recipes部分,我提供了許多內(nèi)嵌匯編代碼的例子,我們可以看到更多細(xì)節(jié)。



6. More about constraints.

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.

看到這里,你應(yīng)該知道匯編里的限制符(constraint)做了很多的事。但,我們只花了很少的篇幅敘述限制符。比如,限制符可以指定一個(gè)寄存器,限制符可以指向一塊內(nèi)存空間,限制符可以是一個(gè)立即數(shù)。。。等。

6.1 Commonly used constraints.

There are a number of constraints of which only a few are used frequently. We’ll have a look at those constraints.

有大量的限制符,我們常用使用其中很少一部份,現(xiàn)在來看看:

  • Register operand constraint(r)

    When operands are specified using this constraint, they get stored in General Purpose Registers(GPR). Take the following example:

    當(dāng)操作指定了“r”限制符,那么操作數(shù)將會(huì)被存儲(chǔ)在通用寄存器內(nèi)。看下例:

    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被存儲(chǔ)在一個(gè)寄存器內(nèi),代碼將eax寄存器的值拷貝到myval占用的寄存器內(nèi),然后myval寄存器的值將更新myval的內(nèi)存值。當(dāng)“r”限制符被指定,GCC可能分配任意一個(gè)通用寄存器來存儲(chǔ)操作數(shù)。如果要確切使用某個(gè)寄存器,你應(yīng)該指定這個(gè)寄存器名稱,通過下表的格式:

    1. +---+--------------------+  
    2. | r |    Register(s)     |  
    3. +---+--------------------+  
    4. | a |   %eax, %ax, %al   |  
    5. | b |   %ebx, %bx, %bl   |  
    6. | c |   %ecx, %cx, %cl   |  
    7. | d |   %edx, %dx, %dl   |  
    8. | S |   %esi, %si        |  
    9. | D |   %edi, %di        |  
    10. +---+--------------------+  

  • Memory operand constraint(m)

    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”后的操作數(shù)在內(nèi)存中,任何對(duì)它們的操作都會(huì)直接更改內(nèi)存值。與“r”限制符不同,“r”首先將操作數(shù)保存在寄存器內(nèi),然后在寄存器里進(jìn)行數(shù)據(jù)操作,接著把數(shù)據(jù)寫回內(nèi)存區(qū)域。使用“r”限制符,通常是由于某些指令必須使用,或者為了加快程序運(yùn)行,所以占用寄存器?!癿”限制符運(yùn)用更頻繁,當(dāng)我們希望在匯編執(zhí)行過程中就更新內(nèi)存,或者不希望額外占用一個(gè)寶貴的寄存器來裝載變量值,就使用“m”限制符。如下:idtr的值就被保存在loc那塊內(nèi)存。

    asm("sidt %0\n" : :"m"(loc));


  • Matching(Digit) constraints

    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.

    在某些情況下,輸出輸入操作可能是同一個(gè)操作數(shù)。這種情況我們需要指定匹配限制符“數(shù)字”。 

    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同時(shí)供input,output使用。輸入變量var被讀入到eax,運(yùn)算結(jié)束后,再被存儲(chǔ)到eax?!?”這個(gè)限制符表示:與第0個(gè)操作數(shù)使用相同的寄存器。這樣,就指明了輸出輸入使用同一個(gè)寄存器。這個(gè)限制符在如下地方可能用到:

    • In cases where input is read from a variable or the variable is modified and modification is written back to the same variable.
    • In cases where separate instances of input and output operands are not necessary.
    • 輸出輸入為同一變量時(shí)。
    • 沒有必要使用更多的寄存器時(shí)。

    The most important effect of using matching restraints is that they lead to the efficient use of available registers.

    使用匹配限制符最重要的作用是:使得對(duì)有限寄存器資源使用更高效。

    Some other constraints used are:

    其他一些限制符:

    1. "m" : A memory operand is allowed, with any kind of address that the machine supports in general.
    2. "o" : A memory operand is allowed, but only if the address is offsettable. ie, adding a small offset to the address gives a valid address.
    3. "V" : A memory operand that is not offsettable. In other words, anything that would fit the `m’ constraint but not the `o’constraint.
    4. "i" : An immediate integer operand (one with constant value) is allowed. This includes symbolic constants whose values will be known only at assembly time.
    5. "n" : An immediate integer operand with a known numeric value is allowed. Many systems cannot support assembly-time constants for operands less than a word wide. Constraints for these operands should use ’n’ rather than ’i’.
    6. "g" : Any register, memory or immediate integer operand is allowed, except for registers that are not general registers.
    7. “m”:對(duì)內(nèi)存的操作被允許,用一個(gè)合法內(nèi)存空間來做操作數(shù)。
    8. “o”:對(duì)內(nèi)存的操作被允許,但是必須支持地址偏移值,即,對(duì)于給出的地址,加上一個(gè)偏移量,此時(shí)也是一個(gè)合法的地址。
    9. “V”:對(duì)內(nèi)存的操作被允許,但是不支持偏移量。也就是說,支持“m”限制符,但不支持“o”的那些地址。
    10. “i”:對(duì)立即整數(shù)(const,常值)的操作被允許,這個(gè)常值可以是運(yùn)行到匯編內(nèi)才被賦值。
    11. “n”:對(duì)立即整數(shù)的操作被允許。許多系統(tǒng)不支持匯編中的操作數(shù)小于一個(gè)字寬,對(duì)于這些操作數(shù),應(yīng)該使用“n”而非“i”。
    12. “g”:任意寄存器,內(nèi)存,立即數(shù)都被允許。除了非通用寄存器。

    Following constraints are x86 specific.

    下面的限制符是x86特定的:

    1. "r" : Register operand constraint, look table given above.
    2. "q" : Registers a, b, c or d.
    3. "I" : Constant in range 0 to 31 (for 32-bit shifts).
    4. "J" : Constant in range 0 to 63 (for 64-bit shifts).
    5. "K" : 0xff.
    6. "L" : 0xffff.
    7. "M" : 0, 1, 2, or 3 (shifts for lea instruction).
    8. "N" : Constant in range 0 to 255 (for out instruction).
    9. "f" : Floating point register
    10. "t" : First (top of stack) floating point register
    11. "u" : Second floating point register
    12. "A" : Specifies the `a’ or `d’ registers. This is primarily useful for 64-bit integer values intended to be returned with the `d’ register holding the most significant bits and the `a’ register holding the least significant bits.

    6.2 Constraint Modifiers.

    While using constraints, for more precise control over the effects of constraints, GCC provides us with constraint modifiers. Mostly used constraint modifiers are

    在使用限制符的時(shí)候,為了更準(zhǔn)確的利用限制符的功能,GCC提供給我們一些限制語句修飾符。最常用的修飾符有:“=”,“&”。

    1. "=" : Means that this operand is write-only for this instruction; the previous value is discarded and replaced by output data.
    2. "&" : Means that this operand is an earlyclobber operand, which is modified before the instruction is finished using the input operands. Therefore, this operand may not lie in a register that is used as an input operand or as part of any memory address. An input operand can be tied to an earlyclobber operand if its only use as an input occurs before the early result is written.
    3. “=”表示此操作數(shù)類型是只寫。之前的值會(huì)被輸出數(shù)據(jù)值替代。
    4. “&”表示此操作數(shù)是一個(gè)很早更變的(earlyclobber)操作數(shù)。在指令執(zhí)行過程中,輸出操作數(shù)產(chǎn)生之前,輸入操作數(shù)還未使用完成,所以輸出操作數(shù)不能與該指令的任何輸入操作數(shù)公用同一寄存器。這個(gè)聲明就為防止這種合并寄存器的優(yōu)化。因此,這個(gè)輸入操作數(shù)可能沒有被保存到寄存器。

      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.

      上邊對(duì)限制符的解釋絕不完全,下邊的例子可以讓我們更好的理解嵌入?yún)R編的用法。下邊一節(jié)我們將看一些例子,那里我們將遇到更多的colbber list和限制符。



    7. Some Useful Recipes.

    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).

    我們已經(jīng)接觸過內(nèi)嵌匯編的基本理論。現(xiàn)在我們專注幾個(gè)例子。使用內(nèi)嵌匯編來定義宏是非常精妙的,我們經(jīng)??梢栽趦?nèi)核代碼中看到。

    1. First we start with a simple example. We’ll write a program to add two numbers.

      首先,我們寫個(gè)小程序,使兩個(gè)數(shù)做加法。

      1. int main(void)  
      2. {  
      3.         int foo = 10, bar = 15;  
      4.         __asm__ __volatile__("addl  %%ebx,%%eax"  
      5.                              :"=a"(foo)  
      6.                              :"a"(foo), "b"(bar)  
      7.                              );  
      8.         printf("foo+bar=%d\n", foo);  
      9.         return 0;  
      10. }  

      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寄存器來存儲(chǔ)foo,bar存放在ebx寄存器,并且結(jié)果存放到eax中。這個(gè)“=”表示這是輸出結(jié)果的寄存器。接下來,我們用其他方法把一個(gè)整數(shù)存放進(jìn)一個(gè)變量中。


      1. __asm__ __volatile__(  
      2.                      "   lock       ;\n"  
      3.                      "   addl %1,%0 ;\n"  
      4.                      : "=m"  (my_var)  
      5.                      : "ir"  (my_int), "m" (my_var)  
      6.                      :                                 /* no clobber-list */  
      7.                      );  

      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.

      這是一個(gè)(atomic addition)。我們可以去掉‘lock’來去除這個(gè)(atomicity)。在輸出區(qū)域,“=m”是說my_var是輸出操作數(shù),并且它在內(nèi)存里?!癷r”是說my_int是一個(gè)整數(shù),并且會(huì)選擇一個(gè)通用寄存器存放它。沒有寄存器在clobber list。

    2. Now we’ll perform some action on some registers/variables and compare the value.

      現(xiàn)在我們實(shí)現(xiàn)一些操作,使用寄存器/變量,并比較他們的值。


      1. __asm__ __volatile__(  "decl %0; sete %1"  
      2.                      : "=m" (my_var), "=q" (cond)  
      3.                      : "m" (my_var)   
      4.                      : "memory"  
      5.                      );  

      Here, the value of my_var is decremented by one and if the resulting value is 0then, 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,如果結(jié)果值為0。那么,cond就確定了。

      要點(diǎn):1.my_var存在內(nèi)存中。2.cond被“=q”限制,所以它占用 eax,ebx,ecx,edx中的一個(gè)。3.memory在clobber list中,即,代碼改變了內(nèi)存。

    3. How to set/clear a bit in a register? As next recipe, we are going to see it.

      怎么設(shè)置/清除寄存器的一位?下個(gè)例子:


      1. __asm__ __volatile__(   "btsl %1,%0"  
      2.                       : "=m" (ADDR)  
      3.                       : "Ir" (pos)  
      4.                       : "cc"  
      5.                       );  

      Here, the bit at the position ’pos’ of variable at ADDR ( a memory variable ) is set to1We 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.

      內(nèi)存變量ADDR的'pos'字位被置1。我們可以使用‘btrl’指令來清除這位。限制符“Ir”表示pos存放到寄存器內(nèi),并且pos的范圍是0~31(x86專用限制符,上邊有提及)。隨著條件代碼的改變,我們把“cc”加入到clobber list。

    4. Now we look at some more complicated but useful function. String copy.

      現(xiàn)在我們看實(shí)現(xiàn)String copy函數(shù):

      1. static inline char * strcpy(char * dest,const char *src)  
      2. {  
      3. int d0, d1, d2;  
      4. __asm__ __volatile__(  "1:\tlodsb\n\t"  
      5.                        "stosb\n\t"  
      6.                        "testb %%al,%%al\n\t"  
      7.                        "jne 1b"  
      8.                      : "=&S" (d0), "=&D" (d1), "=&a" (d2)  
      9.                      : "0" (src),"1" (dest)   
      10.                      : "memory");  
      11. return dest;  
      12. }  

      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,接下來開始拷貝。當(dāng)?shù)竭_(dá)0時(shí),程序完成。限制符“&S”,“&D”,“&a”分別指定esi,edi,eax,并且他們是早期改變的寄存器,即,他們的內(nèi)容在程序結(jié)束前,就會(huì)有變化。同樣memroy出現(xiàn)在clobber list。下例,類似的功能,移動(dòng)兩個(gè)字節(jié)的內(nèi)存塊,注意,它是以宏的方式實(shí)現(xiàn)的。



      1. #define mov_blk(src, dest, numwords) \  
      2. __asm__ __volatile__ (                                          \  
      3.                        "cld\n\t"                                \  
      4.                        "rep\n\t"                                \  
      5.                        "movsl"                                  \  
      6.                        :                                        \  
      7.                        : "S" (src), "D" (dest), "c" (numwords)  \  
      8.                        : "%ecx""%esi""%edi"                 \  
      9.                        )  

      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.

      這里沒有輸出,使用兩個(gè)連續(xù)的::。ecx,esi,edi的變化實(shí)現(xiàn)內(nèi)存塊搬移。所以,把他們也加到clobber list。

    5. 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中,系統(tǒng)調(diào)用用內(nèi)嵌匯編實(shí)現(xiàn)。我們一起看看系統(tǒng)調(diào)用怎樣實(shí)現(xiàn)的。所有的系統(tǒng)調(diào)用都用匯編書寫。例如,系統(tǒng)調(diào)用3個(gè)參數(shù),被定義成如下宏:

      1. #define _syscall3(type,name,type1,arg1,type2,arg2,type3,arg3) \  
      2. type name(type1 arg1,type2 arg2,type3 arg3) \  
      3. { \  
      4. long __res; \  
      5. __asm__ volatile (  "int $0x80" \  
      6.                   : "=a" (__res) \  
      7.                   : "0" (__NR_##name),"b" ((long)(arg1)),"c" ((long)(arg2)), \  
      8.                     "d" ((long)(arg3))); \  
      9. __syscall_return(type,__res); \  
      10. }  


      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.

      每當(dāng)系統(tǒng)調(diào)用一個(gè)3參數(shù)的函數(shù)時(shí),就使用上邊這個(gè)宏來調(diào)用。系統(tǒng)調(diào)用的名稱name被放置在eax,其后的參數(shù)分別占用ebx,ecx,edx。最后“int 0x80”這條指令實(shí)現(xiàn)了調(diào)用,而且返回值存入eax內(nèi)。

      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.

      每個(gè)系統(tǒng)調(diào)用的實(shí)現(xiàn)都相似,Exit()系統(tǒng)函數(shù)只有一個(gè)參數(shù),我們來看看它的代碼長什么樣子。如下:


      1. {  
      2.         asm("movl $1,%%eax;         /* SYS_exit is 1 */  
      3.              xorl %%ebx,%%ebx;      /* Argument is in ebx, it is 0 */  
      4.              int  $0x80"            /* Enter kernel mode */  
      5.              );  
      6. }  

      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的編號(hào)是1,它的參數(shù)是0。所以我們安排eax存放1,ebx存放2,然后指令“int $0x80”,exit(0)就執(zhí)行了。這就是它的工作方式。


    8. Concluding Remarks.

    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.

    這個(gè)文檔縱覽了GCC內(nèi)嵌匯編的基本內(nèi)容。一旦你理解了這些基本概念,那麼修行靠個(gè)人。

    GCC內(nèi)嵌匯編是一個(gè)巨大的工程,并且這樣的藝術(shù)之作絕不會(huì)完成。更多細(xì)節(jié)請(qǐng)參見GNU Assembler官方文檔。

    當(dāng)然,Linux大規(guī)模使用了GCC內(nèi)嵌匯編,所以我們從中可以找到大量的例子。她們會(huì)給我們很多幫助。



    請(qǐng)留言,我將立刻改正翻譯不妥之處,以免誤導(dǎo)讀者,為了鍛煉自己閱讀能力,我才干了這事兒。thanks a lot~

    :-)

  • 本站僅提供存儲(chǔ)服務(wù),所有內(nèi)容均由用戶發(fā)布,如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊舉報(bào)。
    打開APP,閱讀全文并永久保存 查看更多類似文章
    猜你喜歡
    類似文章
    GUN C內(nèi)聯(lián)匯編
    Using Inline Assembly With gcc
    c語言內(nèi)嵌匯編代碼之constraint modifier中 = 和 的區(qū)別
    SSE指令集優(yōu)化心得(二)
    Linux 中 x86 的內(nèi)聯(lián)匯編
    AT&T匯編學(xué)習(xí)筆記
    更多類似文章 >>
    生活服務(wù)
    分享 收藏 導(dǎo)長圖 關(guān)注 下載文章
    綁定賬號(hào)成功
    后續(xù)可登錄賬號(hào)暢享VIP特權(quán)!
    如果VIP功能使用有故障,
    可點(diǎn)擊這里聯(lián)系客服!

    聯(lián)系客服