我对提供如此棒的特性的 GNU 人们表示真诚的感谢。感谢 Mr.Pramode C E 所做的所有帮助。感谢在 Govt Engineering College 和 Trichur 的朋友们的精神支持和合作,尤其是 Nisha Kurur 和 Sakeeb S 。 感谢在 Gvot Engineering College 和 Trichur 的老师们的合作。
汇编程序模板由汇编指令组成.每一个操作数由一个操作数约束字符串所描述,其后紧接一个括弧括起的 C 表达式。冒号用于将汇编程序模板和第一个输出操作数分开,另一个(冒号)用于将最后一个输出操作数和第一个输入操作数分开,如果存在的话。逗号用于分离每一个组内的操作数。总操作数的数目限制在10个,或者机器描述中的任何指令格式中的最大操作数数目,以较大者为准。
汇编程序模板包含了被插入到 C 程序的汇编指令集。其格式为:每条指令用双引号圈起,或者整个指令组用双引号圈起。同时每条指令应以分界符结尾。有效的分界符有换行符(\n)和逗号(;)。’\n’ 可以紧随一个制表符(\t)。我们应该都明白使用换行符或制表符的原因了吧?和 C 表达式对应的操作数使用 %0、%1 ... 等等表示。
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 inline asm functions as MACRO’s. We can see many asm functions in the kernel code. (/usr/src/linux/include/asm/*.h).
1. First we start with a simple example. We’ll write a program to add two numbers.
> `
>
> * * *
>
> <pre>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;
> }
> </pre>
>
> * * *
>
> `
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.
> `
>
> * * *
>
> <pre> __asm__ __volatile__(
> " lock ;\n"
> " addl %1,%0 ;\n"
> : "=m" (my_var)
> : "ir" (my_int), "m" (my_var)
> : /* no clobber-list */
> );
> </pre>
>
> * * *
>
> `
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.
2. Now we’ll perform some action on some registers/variables and compare the value.
> `
>
> * * *
>
> <pre> __asm__ __volatile__( "decl %0; sete %1"
> : "=m" (my_var), "=q" (cond)
> : "m" (my_var)
> : "memory"
> );
> </pre>
>
> * * *
>
> `
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.
3. How to set/clear a bit in a register? As next recipe, we are going to see it.
> `
>
> * * *
>
> <pre>__asm__ __volatile__( "btsl %1,%0"
> : "=m" (ADDR)
> : "Ir" (pos)
> : "cc"
> );
> </pre>
>
> * * *
>
> `
Here, the bit at the position ’pos’ of variable at ADDR ( a memory variable ) is set to`1`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.
4. Now we look at some more complicated but useful function. String copy.
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 will change 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.
> `
>
> * * *
>
> <pre>#define mov_blk(src, dest, numwords) \
> __asm__ __volatile__ ( \
> "cld\n\t" \
> "rep\n\t" \
> "movsl" \
> : \
> : "S" (src), "D" (dest), "c" (numwords) \
> : "%ecx", "%esi", "%edi" \
> )
> </pre>
>
> * * *
>
> `
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.
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.
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.
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.
> `
>
> * * *
>
> <pre>{
> asm("movl $1,%%eax; /* SYS_exit is 1 */
> xorl %%ebx,%%ebx; /* Argument is in ebx, it is 0 */
> int $0x80" /* Enter kernel mode */
> );
> }
> </pre>
>
> * * *
>
> `
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 by`int $0x80`, the`exit(0)`is executed. This is how exit works.
* * *
## 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 the constraints 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 this document, please let us know.
* * *
## 9.References.
1. [Brennan’s Guide to Inline Assembly](http://www.delorie.com/djgpp/doc/brennan/brennan_att_inline_djgpp.html)
2. [Using Assembly Language in Linux](http://linuxassembly.org/articles/linasm.html)
3. [Using as, The GNU Assembler](http://www.gnu.org/manual/gas-2.9.1/html_mono/as.html)
4. [Using and Porting the GNU Compiler Collection (GCC)](http://gcc.gnu.org/onlinedocs/gcc_toc.html)