I. Statement
Although most of Linux's core code is written in C language, but the inevitable part of it is written in assembly language. Some assembly language code is written directly in the assembly source program, especially the Linux boot code section, and some of the use of GCC embedded assembly language embedded in the C language program. This article briefly introduces the embedded assembly language in GCC, primarily to help those friends who just started reading the Linux core code to start faster.
The main source of information for this article is the GNU two info file: As.info and Gcc.info, and if you think the introduction in this article is not detailed enough, you can check out the two files. Of course, a more authoritative message is available for direct access to these two documents. If you don't want to be confused by a lot of the information in these two documents, I suggest you read this article first and then consult more authoritative information if necessary.
Two. Introduction
In the Linux core code, there is still a considerable part of the assembly language code. If you want to read the Linux code smoothly, you can't bypass this part of the code. In the Linux assembly language code, there are two main formats: one is directly written in assembly language source program form, this part is mainly some Linux startup code, and the other part of the use of GCC Embedded assembly language statement ASM embedded in the Linux C language code. This article is mainly about the second form of assembly language code.
First, I introduce the syntax format of assembly language as supported by as. You know, we now learn the format of the assembly language is mainly Intel style, and in the core code of Linux is used in at&t format assembly language code, it should be said that most people of this format is not very familiar with the assembly language, so I think it is necessary to introduce.
Next, I'll introduce GCC's inline assembly language format. GCC's Embedded assembly language provides a good way to embed the assembler instruction directly in C language source program, which can directly control the sequence of instruction and have good interface with C language, so it is used in many parts of Linux code.
Three. GCC Embedded assembly language statement ASM
Using the ASM Statement of GCC, you can embed assembly language instructions directly in C code, and you can use the C expression to specify the number of operands to use for assembly instructions. This feature provides a lot of convenience.
To use this feature, you first write a template for the assembly instruction (which is somewhat similar to the instruction template in the machine description file), and then you specify a qualification string for each operand. For example:
extern __inline__ void change_bit (int nr,volatile void *addr)
{
__asm__ __volatile__ (Lock_prefix
"Btcl%1,%0 "
: "=m" (ADDR)
: "IR" (NR));
}
In the above function:
Lock_prefix: This is a macro that, if defined __smp__, expands to "lock;" To specify the bus lock prefix, otherwise the extension is "".
ADDR: This is also a macro, defined as (* (volatile struct __dummy *) ADDR)
"Btcl%1,%0 ": This is the embedded assembly language instruction, BTCL for the instruction opcode,%1,%0 is the placeholder for this instruction two operands." The following two qualifying strings are used to describe the two operands.
: "=m" (ADDR): The qualifying string after the first colon is used to describe the "output" operand in the directive. The addr in the scraper link the operands to the variables in the C language. This qualifying string represents "%0" in the instruction as the number of memory operands that the addr pointer points to. This is a "output" type of memory operand.
: "IR" (NR): The qualifying string after the second colon is used to describe the "input" operand in the directive. This qualifying string indicates that "%1" in the instruction is a variable nr, which can be either an immediate operand or a register operand.
* Note: The correspondence between the qualifying string and the operand placeholder is as follows: in all qualifying strings (including all qualifying strings after the first colon and after the second colon), the first occurrence of the string is used to describe the operand '%0 ', the second occurrence of the string describing the operand '%1 ', and so on.
① Assembly Instruction Template
The assembly instruction template in the ASM statement consists mainly of assembly instruction sequence and qualified string. Multiple assembler directives can be included in an ASM statement. Use the operand placeholder in the assembly instruction sequence to refer to variables in the C language. An ASM statement can contain up to 10 operand placeholders:%0,%1,...,%9. The assembly instruction sequence is followed by the operand qualification string, which qualifies the placeholder in the instruction sequence. The qualifying content includes: which C-language variable The placeholder corresponds to, what type of operand it can be, and so on. The qualifying string can be divided into three parts: the output operand qualification string (the qualified string after the first colon after the instruction sequence), the input operand qualification string (between the first colon and the second colon), and the third type of qualifying string after the second colon. A comma interval is used between qualified strings of the same type. The first qualifying string that appears in the ASM statement is used to describe the placeholder "%0", the second to describe the placeholder "%1", and so on, regardless of the type of the qualifying string. If there is no output operand in the instruction sequence, the first qualifying string that appears in the statement (the string used to describe the input operand) should have two colons (so that the compiler knows that there are no output operands in the instruction).
The C language variable corresponding to the output operand in the instruction should have a left value type, and of course there is no such left value limit for the output operand. The output operand must be write-only, that is, the ASM is not directly to support the type of assembly instruction that takes out an operand, performs a certain calculation, and then saves the result back to that operand, and must pass the description in a particular format. If the assembly instruction contains an operand of an input-output type, you must refer to the different functions of the operand in the template with two placeholders: one for the input and the other for the output. For example:
ASM ("Addl%2,%0 ":" =r "(foo):" 0 "(foo)," G "(bar);
In the above instruction,
'%0 ' is an input-output type operand,
"=r" (foo) is used to qualify its output function, and the output of the instruction is stored in the C-language variable foo;
The "%1" operand is not explicitly present in the directive. But for it has a qualifying string of "0" (foo), in fact the implicit "%1" operand in the instruction is used to describe the input function of the "%0" operand, and "0" in its qualification string qualifies the "%1" operand and "%0"
Have the same address. You can understand the template in the instructions above: the directive adds the values in "%1" and "%2", the results are stored back in '%0 ', and the '%1 ' in the directive has the same address as '%0 '. Note that the "0" qualifying character used to describe "%1" is sufficient to ensure that "%1" has the same address as "%0".
However, this input-output operation does not work correctly if done with the following instructions:
ASM ("Addl%2,%0 ":" =r "(foo):" R "(foo)," G "(bar));
Although "%0" and "%1" in this directive also refer to the C language variable foo, GCC does not guarantee that they have the same address in the resulting assembler.
There are also some assembly instructions that may change the value of some registers, which must be notified to the compiler in the appropriate assembly instruction template. So there's a third type of qualifying string in the template that follows the input operand qualification string, separated by a colon. These strings are the names of some registers that will change the contents of those registers on behalf of the instructions.
Some hardware registers may be referenced directly in an inline assembly instruction, we already know that in the assembly language of the AT&T format, the register name is prefixed with "%", in order to retain the "%" in the generated assembler, the reference to the hardware register in the ASM statement must be "%" Prefix as a register name. If the assembly instruction changes the contents of the hardware register, do not forget to notify the compiler (add the corresponding string in the third type of qualified string). There are also some instructions that may change the contents of the CPU flag register Eflag, then you need to add "CC" to the third type of qualifying string.
To prevent GCC from changing the assembly instructions in ASM during the optimization process, you can add the "volatile" modifier after the "ASM" keyword.
Multiple assembly language instructions can be described in an ASM statement, and the ";" is used between the assembly instructions. or "n" separated.
② Operand qualifying Character
In the operand qualification string, the specified qualifier character is used to describe the corresponding operand, and some common qualifying characters are: (there are some qualifying characters not covered, see GCC.INFO)
1. "M": operands are memory variables.
2. "O": the operand is a memory variable, but it must be addressed in an "offset" type, which is either a base address or a base address plus address addressing.
3. "V": the operand is a memory variable, and its addressing mode is not "offset" type.
4. "": the operand is a memory variable, and its address is automatically incremented.
6. "R": the operand is a universal register.
7. "I": the operand is an immediate operand. (its value can be determined at compile time)
8. "N": operand is an immediate operand. Some systems do not support immediate operands other than word (double-byte), which are described with "n" instead of "I".
9. "G": operands can be immediate number, memory variable or register, as long as the register belongs to the General register.
"X": operands are allowed to be of any type.
One. "0", "1",..., "9": operands match a specified number of operands. That is, the operand is the number of the specified operand. For example, if "0" is used to describe the "%1" operand, then "%1" refers to the fact that "%0 "Operand.
The "P": the operand is a valid memory address (pointer).
13. "=": The operand is written in the instruction (output operand).
14. "+": operands are read-write type (input-output operands) in the instruction.
"F": floating-point number registers.
"T": the first floating-point number register.
"U": Second floating-point number register.
"I": the immediate number between 0-31. (for 32-bit shift instructions)
"J": Immediate number between 0-63. (for 64-bit shift instructions)
"N": the immediate number between 0-255. (for "out" directives)
"G": Standard 80387 floating-point constants.
Note: There are some unusual qualifying characters that are not described here, and some qualifying characters, such as "%", "&", etc. because I lack some knowledge of the compiler, so I do not understand their meaning, if a master willing to add, inadvertently grateful. But the qualifying characters that appear in the core code are almost the same.
A supplementary note to the article "Embedded assembly language in GCC"
First contact with the at&t format of the assembly code, looking at that a bunch of inexplicable strange symbols, really a bit of a pain in the feeling, had to slowly to chew GCC documents, in the state of indefinitely after a period of time. Later found on the Internet Lingxi wrote the "Embedded assembly language in GCC," a text, read after the benefit. A few months down, contact the source code more later, slowly have some experience. In order to make the first contact with the at&t format of the assembly code of the comrades do not suffer from my pain, I sorted out the article to share with you. If there is a mistake, you are welcome to correct and improve together.
In this paper, the embedded assembly language in GCC is explained in the way of example.
First, GCC processing of embedded assembly language
GCC when compiling embedded assembly language, the following steps are taken:
Variable input: Puts the input operand into the appropriate registers based on the contents of the qualifier, if the qualifier is specified as an immediate number ("I") or a Memory variable ("M"), the step is omitted if the qualifier does not specify the type of the input operand (such as the commonly used "G"), GCC will decide whether to enter the operand into a register as needed. So that each placeholder is associated with a register, the memory variable, or the immediate number, forms a one by one corresponding relationship. This is the explanation of the content after the second colon. For example:: "A" (foo), "I" (+), "M" (bar) means%0 Corresponds to the EAX register,%1 corresponds to the 100,%2 corresponding memory variable bar.
Generate code: And then based on this one-to-one relationship (which should also include the output operator), using these registers, memory variables, or immediate numbers to replace placeholders in assembly code (a bit like a macro operation), note that a step does not check whether the assembly code generated by this substitution operation is legitimate, for example, If there is such an instruction ASM ("Movl%0,%1":: "M" (foo), "M" (bar); If you compile the source file with the Gcc-c-s option, then in the resulting assembly file you will see a directive that generates MOVL Foo,bar, which is clearly wrong. This error will be found later in the compile check.
Variable output: Outputs the contents of a register to a memory variable, as specified by the output qualifier. If the qualifier for the output operand is specified as a memory variable ("M"), the step is omitted. This is the interpretation of the content after the first colon, such as: ASM ("mov%0,%1": "=m" (foo), "= A "(bar):), compiled as:
#APP
MOVL Foo,eax
#NO_APP
MOVL Eax,bar
The statement is a bit weird, but it's a good representation of how GCC works.
Take a piece of code in ARCH/I386/KERNEL/APM.C as an example, let's compare what happens before and after they compile:
Ii. explanation of the content after the third colon
The third colon is optimized for GCC, which tells GCC how to use registers and memory in this assembly code, so that GCC does not cause errors when optimizing processing. It can be a register name such as "EAX", "ebx", "ecx", which means that the assembly code explicitly operates on the register, such as ASM ("MOV%%eax,%0 ",:" =r "(foo)::" EAX ") so that GCC avoids using eax as a temporary variable when optimizing, or avoids the cache to EAX memory variables encoded through that segment.
The following code is optimized with the-o2 level of GCC, which shows the role of "EAX" after the third colon in the embedded assembly
Compiled Code of source program
int main ()
{
int bar=1;
Bar=fun ();
bar++;
return bar;
}
PUSHL%EBP
MOVL%ESP,%EBP
Call Fun
Incl%eax #显然, bar defaults to using EAX registers
Leave
Ret
Add the PostScript:
Compiled Code of source program
int main ()
{
int bar=1;
Bar=fun ();
ASM volatile (""::: "eax");
bar++;
return bar;
}
PUSHL%EBP
MOVL%ESP,%EBP
Call Fun
MOVL%eax,%edx #bar改为使用edx寄存器
Incl%edx
The return value of the MOVL%edx,%eax #放入main ()
Ret
"Merory" is a commonly used qualification, it means that the assembly code changes memory in unpredictable ways, so that GCC does not allow the cache to register memory variables to use the register to assemble the code when it is optimized, or a synchronization error may occur. With the example above, the problem is well understood.
Iii. interpretation of the "&" qualifier
This is a more common qualifier for output, which tells GCC that the registers used by the output operand are not allowed to be used by input operands.
For qualifiers such as "G", "R", GCC typically uses the same register for both the input operand and the output operand in order to efficiently utilize a few common registers. But if the code is not well prepared, it can cause some unexpected errors, such as:
ASM ("Call Fun;mov ebx,%1": "=a" (foo): "R" (bar);
The result of GCC compilation is that Foo and bar use the EAX registers at the same time:
MOVL Bar,eax
#APP
Call Fun
MOVL Ebx,eax
#NO_APP
MOVL Eax,foo
The intention of this code is to put the return value of the fun () function into the Foo variable, but Schen, the value of EBX to flush out the return value, so this is a bad code, the solution is to add a number of output operands with a "&" qualifier:
ASM ("Call Fun;mov ebx,%1": "=&a" (foo): "R" (bar);
This will allow GCC to find a different number of input operands and no longer use the EAX register.
Supplementary Note:
In fact & to read code does not make sense, only GCC or write a compilation of care.
If you have to know the role of &, explain it here.
1. When GCC processes embedded assemblies, if two input operations have the same values, they may be assigned to the same register to reduce the use of registers.
2. GCC regards the embedded assembly as a whole, and it does not know whether it is an instruction or multiple instructions, usually it thinks that the input operand has not changed when the assembly instruction outputs the result, and the assumption is mostly established when a single instruction is set, but it may not be tenable when multiple instructions are used. For example, the definition is as follows:
#define ADD1 (A,B) ASM ("Incl%0/n/taddl%2,%0 ":" =r "(res):" 0 "(a)," R "(b))
Calculate a+b+1. Let's look at the instructions generated by ADD1 (A,A), assuming that%eax contains a value:
#%0=eax,%2=eax
#APP
Incl%eax
Addl%eax,%eax
#NO_APP
The results are incorrect, and it's easy for people to read or write compilations, but GCC doesn't know, add an ampersand, and define it as:
#define ADD1 (A, b) ASM ("Addl%2,%0 ":" =&r "(res):" 0 "(a)," R "(b))
Tells GCC parameter count%0 is Earlyclobber and does not assign to the same register with%2, so add1 (a,a) generates instructions:
MOVL%eax,%edx
#%0=edx,%2=eax
#APP
Incl%edx
Addl%eax,%edx # edx = output
#NO_APP
Iv. interpretation of the%quot;& "qualifier" (Old Iron Supplement)
%: the operand that can be exchanged with the next operand in the instructions this means that the compilation can exchange these two operands so that the operand constraints can be met in a less costly way, which is often used in an instruction template with an addition instruction that is really only two operands, and the result of such an addition instruction must be stored in one of the two operands.
The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion;
products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the
content of the page makes you feel confusing, please write us an email, we will handle the problem
within 5 days after receiving your email.
If you find any instances of plagiarism from the community, please send an email to:
info-contact@alibabacloud.com
and provide relevant evidence. A staff member will contact you within 5 working days.