All kernel code basically includes include/Linux/compile. h this file, so it is the foundation, covering some of the column compilation knowledge required by the analysis kernel, this blog will analyze the code in this file:
# Ifndef _ linux_compiler_h
# DEFINE _ linux_compiler_h
# Ifndef _ Assembly __
First, we can see the macro _ Assembly _. This variable is actually added to the compilation code by the compiler using a parameter such as-D, GCC defines this macro as 1. It is used here because the compilation Code does not use attributes similar to _ User (this blog will discuss how the attributes such as _ User return sub-tasks ), this attribute is added when defining function parameters. This avoids unnecessary macro references when compiling assembly code.
# Ifdef _ checker __
Next we will make a judgment on the macro _ checker _. here we need to talk about a lot of things, which is the focus of this blog.
When you compile the kernel code and use make C = 1 or C = 2, a sparse tool is called. How can this tool check the kernel code, checks kernel functions or variables that declare the features recognized by the sparse tool. When the sparse tool is called, # DEFINE _ checker _ 1 is added to the sparse code. In other words, if you use sparse to check the code, the kernel code defines the _ checker _ macro, otherwise it is not defined. Please visit: http://linux.die.net/man/1/sparse
For example:
# DEFINE _ User _ attribute _ (noderef, address_space (1 )))
This macro is important to check whether it belongs to the user space! It can be seen that attributes like _ attribute _ (noderef, address_space (1) are recognized by the sparse tool.
What are the other attributes used for checking? I will introduce them one by one.
_ User, that is, _ attribute _ (noderef, address_space (1), is used to modify a variable, this variable must be non-release reference (_ attribute _ (noderef) -- no dereference), that is, the variable address must be valid, in addition, the address space of the variable must be 1 (_ attribute _ (address_space (1), that is, the user program space. The sparse tool divides the program space into three parts. 0 indicates normal space, that is, normal address space. For kernel code, it is of course the kernel space address. 1 indicates the user address space. There is another 2, indicating the device address ing space. For example, the address space mapped by the hardware device register in the kernel.
Therefore, there is a copy_to_user function in the kernel function (we will introduce it in detail in the System Call blog). This method is used to define the function parameters. Of course, this feature check only works when the sparse tool is installed on the machine and called during compilation.
# DEFINE _ KERNEL/* default address space */
According to the definition, it is to check whether it is in the kernel space. It is the default address space, that is, 0. I want to define it as _ attribute _ (noderef, address_space (0.
# DEFINE _ safe _ attribute _ (SAFE ))
This definition also exists in sparse. The kernel code is added by Linus when 2.6.6-RC1 is changed to 2.6.6-RC2, because:
Some people found that during code compilation, the compiler was a little harsh on variable check, which caused the code to always go wrong during compilation (I didn't check whether the compilation failed or there was warning information here, because the current compiler is no longer the current year's compiler, and the code is not the current year's Code ). For example,
Int test (struct a * a, struct B * B, struct C * c ){
Return a-> A + B-> B + C-> C;
}
This compilation may cause problems because the system does not check whether the parameter is null, so it can be called directly. However, there are many functions in the kernel. When they are called, these parameters must not be empty, so they do not need to be checked for non-empty parameters, therefore, a _ safe attribute is added. If the variable is declared in this way,
Int test (struct a * _ safe a, struct B * _ safe B, struct C * _ safe C ){
Return a-> A + B-> B + C-> C;
}
Compilation is fine.
So far, the definition of _ safe has not been found in the current Code. I don't know if the compiler currently supports this special situation, so you don't need to add such code.
# DEFINE _ force _ attribute _ (Force ))
It indicates that the defined variable type can be forced type conversion. During sparse analysis, no warning information is reported.
# DEFINE _ nocast _ attribute _ (nocast ))
This indicates that the parameter type of this variable must be correct with the actual parameter type, or generate alarm information during sparse.
# DEFINE _ iomem _ attribute _ (noderef, address_space (2 )))
This definition is the same as _ user, but the variable address must be in the device address ing space.
# DEFINE _ acquires (x) _ attribute _ (context (x, 0, 1 )))
# DEFINE _ Releases (x) _ attribute _ (context (x, 1, 0 )))
This is a pair of interrelated function definitions. The first sentence indicates that the reference count of parameter X must be 0 before execution. After execution, the reference count must be 1, and the second sentence is the opposite, this definition is used to modify the variables defined by the function.
# DEFINE _ acquire (x) _ context _ (x, 1)
# DEFINE _ release (x) _ context _ (x,-1)
This is a pair of interrelated function definitions. The first sentence indicates to increase the Count of variable X. The addition is 1, and the second sentence is the opposite. This is used for function execution.
If the above four sentences are unbalanced in the code, an alarm will be reported during sparse detection. Of course, sparse detection is only a means, and it is a means of static code check. Therefore, it has limited help and may trigger an alarm when the correct code is regarded as incorrect. If you still don't know the meaning of the above four sentences, you can find the usage of the symbols in the source code. In essence, the first group and the second group are no different. They are only used in different locations.
# DEFINE _ cond_lock (x, c) (c )? ({__ Acquire (x); 1 ;}): 0)
The condition lock is used. When the value of C is not 0, add 1 to the Count value and return 1. But here I have a question: there is a definition of _ cond_lock, but the corresponding _ cond_unlock is not defined, so there is no way to achieve consistency in the release of variables. In addition, for the definition of the spin_trylock () function, it uses the _ cond_lock function and the _ spin_trylock function. In the _ spin_trylock function, it is called several times, the _ acquire function will be used. In this way, it is equivalent to an operation, and two computations will be performed. This will cause the sparse detection to generate alarm information. After writing code for the experiment, after verifying my judgment, there will indeed be an alarm. If I write the UNLOCK Command twice, there will be no alarm information, but this is inconsistent with the running of the program.
Extern void _ chk_user_ptr (const volatile void _ User *);
Extern void _ chk_io_ptr (const volatile void _ iomem *);
These two statements are interesting. Only the function is defined, but there is no function implementation in the code. The purpose of doing so is to let sparse perform necessary parameter type checks for the code During sparse. In the actual compilation process, the implementation of these two functions is not required.
# Define notrace _ attribute _ (no_instrument_function ))
This statement defines an attribute that can be used to modify a function and specify that the function is not tracked. In the GCC compiler, a very powerful function is implemented. If you open a corresponding option during compilation, you can complete the program execution, some tools are used to display the entire function call process, so that programmers do not need to manually add statements that can display the function call process in all functions. This consumes a lot of time and effort, it is also prone to errors. In terms of applications, graphviz can be used for display. You can check the usage instructions and software implementation principles on the Internet, which is easy to find. It corresponds to the kernel. Because the kernel is always in the running stage, this set of things cannot be used. The kernel implements a ftrace mechanism within itself. when compiling the kernel, if this option is enabled, you can mount a debugfs File System to display the corresponding content. For detailed operation steps, see the documentation in the kernel source code. So much has been said above, and what is the relationship with the notrace attribute? Because two special functions are used during the display of the function call process. Before the function is called and the function is returned after execution, these two special functions are called respectively. If you do not specify the functions of these two functions as the attributes that are not tracked, the entire tracing process will fall into an infinite loop.
# Define likely (x) _ builtin_exact CT (!! (X), 1)
# Define unlikely (x) _ builtin_exact CT (!! (X), 0)
These two sentences are a correspondence relationship. _ Builtin_ct CT (expr, c) is supported by the new version of GCC. It is used for code optimization and is used to tell the compiler that the expr period is very likely C, in this way, when GCC generates the corresponding assembly code, it will put all the code that may be executed together, so that the code can be skipped. Why can this improve the CPU execution efficiency? This is because the CPU has a mechanism for obtaining commands in advance during execution. It extracts some of the commands to be executed and prepares them for execution. The CPU does not know the logic of the program, so they are all pulled from the program. If this time can be skipped, the CPU can use the commands pre-extracted. Otherwise, the commands obtained in advance are useless. Note that the previous versions do not exist in the definition of _ builtin_expect !! The role of this symbol is actually negative. Why? It is to ensure that the value of non-zero X is later 1. If the value of zero is later 0, it is only 0.
# Ifndef barrier
# Define barrier () _ memory_barrier ()
# Endif
If the barrier function is not defined, the barrier () function is defined as _ memory_barrier (). But in the kernel code, it will contain the compiler-gcc.h file, so in this file, the barrier () is defined as _ ASM _ volatile __(""::: "Memory "). Barrier is a barrier. Why is it a barrier? This is because the CPU may execute some commands in the way that it deems optimal to optimize the commands during execution, the execution sequence is not necessarily written by the program in the source code. The compiler may also make some optimizations when generating binary commands. In this way, problems may occur in the execution of multi-CPU, multi-thread, or mutex locks. The memory barrier can be regarded as a line. The memory barrier is used here to ensure that the Operation above the barrier does not affect the operation below the barrier. Then let's see how this barrier works. _ ASM _ indicates that all the subsequent items are Assembly commands. Of course, this is a method to embed assembly in C language, and its syntax has its particularity, I will only talk about this command here. _ Volatile _ indicates that the Assembly command here is not optimized, which ensures the correctness of the code here. "" Indicates that it is an empty command. Since it is an empty command, no input or output is required for the corresponding command. In GCC, it is stipulated that if the assembly is embedded in this way, if the output does not exist, two colons are required to replace the position of the output operand. Therefore, two values must be added ::, the command is ""::. Then add the colon to separate the input and the input over the sky, that is "":::. Memory is a special syntax in GCC. When it is added, the GCC compiler will generate an action that prevents GCC from retaining the memory value in the register, in addition, the corresponding memory is not optimized for storage and loading, and no additional code is generated for this action. This behavior is ensured by the GCC compiler.
# Ifndef reloc_hide
# Define reloc_hide (PTR, Off )/
({Unsigned long _ PTR ;/
_ PTR = (unsigned long) (PTR );/
(Typeof (PTR) (_ PTR + (off ));})
# Endif
Many of the subsequent definitions are not implemented. You can see the annotations, so we will not talk about them here. Alas, but let's just insert another sentence, __the implementation of the deprecated attribute is deprecated.
# Define noinline_for_stack noinline
# Ifndef _ always_inline
# DEFINE _ always_inline inline
# Endif
Here, the noinline and inline attributes are two opposite attributes, which are well understood in terms of meaning.
# Ifndef _ cold
# DEFINE _ cold
# Endif
From the annotations, we can see that if the attribute of a function is _ cold, the compiler will think that this function is almost impossible to be called. During code optimization, this will be taken into account. However, I did not see the description that this attribute is supported in GCC.
# Ifndef _ Section
# DEFINE _ Section (s) _ attribute _ (_ Section _ (# s )))
# Endif
This is easy to understand. It is used to modify the region in which a function is stored without the default compiler method. The name of this region is obtained by the definer. The format is _ Section _ and the user input parameter.
# Define access_once (x) (* (volatile typeof (x) *) & (x ))
The definition of this function is very interesting. It is to access the corresponding things of this X parameter once. It does this: first obtain the address of this X and then convert the address, convert to a pointer pointing to this address type, and then obtain the content pointed to by this pointer. In this way, access is achieved once.
From: http://blog.csdn.net/yunsongice/article/details/5538416