Linux Kernel Series 3: Linker script syntax in kernel compilation and link

Source: Internet
Author: User
First, let's talk about how this problem came about. (When analyzing a technology, we should first consider what problems it wants to solve, or when learning new knowledge, what is the purpose of this knowledge ).
  • When compiling the kernel, I found a file named vmlinux. LDS. s under the arch/ARM/kernel directory. At first glance, do you want to compile a file? It seems that it is not. So what does it do? As mentioned earlier, when make v = 1, we found that the file is in the LD command, that is, LD-T vmlinux. lDs. s, as if it was used by the link command, as shown below
For example, arm-Linux-LD-el-p -- no-undefined-X -- Build-ID-O vmlinux-t arch/ARM/kernel/vmlinux. LDS. Man lD, to get-t means: specify a linker script for LD, which means that LD generates the final binary based on the content of this file.
  • Maybe you have never paid attention to the above problem, but when studying the kernel code, there are often some places to say that "_ init Macro will generate a specific section in the final module, then, when the kernel is loaded, find the function in this section. "To put it bluntly, the above sentence means that there is a specific section in the last generated module. What is this?
Well, I hope the above questions will arouse your curiosity. Next, let's talk about literacy. At last, we will give a link address for the readers to go there for further study. What is a section?Well, we need to explain the binary executable programs (such as Elf and exe) generated by compiling links, so, DLL, and kernel (non-compressed, participate in the first section of this series, vmlinux), or how Ko is organized. In fact, we all know more or less what text/BSS/data section (also called Section) is included in these binary codes ). The text section stores code, data stores initialized static variables, and BSS stores uninitialized things... I will not go into detail about the above things. One point is that a binary will eventually contain many sections. So why does section name text/BSS/Data another name? OK. But you have to tell lD, then you can use the-T option to specify a linker script. We will introduce the content in the following example. (I have repeatedly stressed that in theory, we only hope that you will be interested in your own research and pay attention to sharing your achievements with us .) Basic link script knowledgeThe linker script syntax is linker command language (very simple language, so you don't have to worry about it ...). So what is the purpose of LS?
  • Ls describes how the section in the input file (The. o file generated by the GCC-C command is the object file) corresponds to an output file. This is actually easy to understand. For example, an elf consists of three. o file structure, each. O files all have text/data/BSS segments, but the final elf will put three inputs. o file segments are merged.
Now, we will introduce some basic knowledge:
  • LD is used to assemble an input file into an output file. These files have special organizational structures, which are called object file format. Each file is called an object file (this may be the source of the. o file. Haha), the output file is also called an executable file (an executable), but it is also an object file for LD. What are the special features of object files? Well, its internal organization is organized according to Section (segment, or section, which will not be distinguished later. In a word, the object file contains segments ......
  • Each segment has a name and a size. In addition, the section contains some data, which is called Section contents and later called the section content. Each segment has different attributes. For example, the text segment flag is loadable, indicating that the contents in the segment needs to be loaded into the memory during runtime (of course, when the output file is executed. If some segments do not contain contents, these segments are marked as allocatable, that is, some memory needs to be allocated (sometimes the memory will be initialized to 0, and here we should talk about the BSS segment. The BSS segment does not occupy space in the binary file, that is, the size of the binary file on the disk is relatively small, but after being loaded into the memory, the BSS segment needs to be allocated memory space .), There are also some sections that belong to debug, which contain some debug information.
  • What is the address to load to the memory? The loadable and allocable segments have two addresses: VMA: virtual address, that is, the address when the program runs. For example, set the first VMA address of the Text Segment to 0x800000000, this is the first address during running. There is also an LMA, that is, load memory address. This address is the address when the section is loaded. Dizzy? What is the difference between the two? Generally, VMA = LMA. But there are also exceptions. For example, you can set the LMA of a data segment to be In the ROM (that is, copy to the ROM when loading), and copy to ram when running, so that the LMA and VMA are different. --------- It's hard to understand, isn't it? This method is used to initialize some global variables based on the ROM
    Based System. (For a question, how do I set the VMA in the section during run ?? In the future, you may need to study execve implementation in the kernel ). For VMA and LMA, you can view them using the objdump-H option.
Three simple examplesThe following is a simple example, Sections

{
. = 0x10000;
. Text: {* (. Text )}
. = 0x8000000;
. Data: {* (. Data )}
. BSS: {* (. BSS )}
}

  • Sections is the key command in LS syntax. It is used to describe the memory layout of the output file. For example, the preceding example contains three parts: Text, data, and BSS (in fact, text, data, and BSS are segments, but the word sections is a command in LS, hope you can understand ).
  • . = 0x10000; The. parameter indicates location counter (LC ). It indicates that the beginning of the text segment is at 0x10000. This LC should refer to LMA, but in most cases VMA = LMA.
  • . Text: {* (. Text)}, which indicates that the. Text Segment of the output file is composed of. Text segments of all input files. The composition order is the order of input files in the LD command, such as 1.obj, 2. OBJ ......
  • Since then, a. = 0x800000000 ;. If this value is not assigned, the LC should be equal to 0x10000 + sizeof (Text Segment), that is, if the LC is not forcibly specified, by default, it is the length of the last Lc + intermediate section. Fortunately, it is mandatory to specify lc = 0x800000000 here. It indicates that the start of the. Data Segment is located at this address.
  • . Data and later. BSS indicate that the. Data and. BSS segments of the input file are constructed respectively.
What have we learned from this LC file? Well, we can set the LMA value for each segment at will. Of course, in most cases, we do not need to have our own ls to control the memory layout of output files. However, lk (Linux kernel) may be different ...... 4 overlord hard bow-vmlinux. LDS. s analysisOK. With the basic knowledge above, we can directly analyze ARCH/ARM/kernel/vmlinux using overlord. lDs. s. although the final link uses vmlinux. however, the file is created by vmlinux. lDs. S (this is an assembly file) to obtain, arm-Linux-gcc-e-WP,-MD, arch/ARM/kernel /. vmlinux. lDs. d-nostdinc ...... -d__kernel _-mlittle-Endian ...... -dtext_offset = 0x00008000-p-C-uarm-d1_assembly _-o
ARCH/ARM/kernel/vmlinux. LDS
ARCH/ARM/kernel/vmlinux. LDS. S. Therefore, we can analyze vmlinux. LDS directly. /*
A bunch of comments will not be attached here. In addition, add a // number as the comment mark.
* Convert a physical address to a page frame number and back
* /// Output_arch is the command in the LS syntax used to specify the machine arch of the output file. Objdump-F can query all supported machines. // These things involve a type called BFD. You can search for the BFD content by yourself. // The following indicates that the output file is based on the ARM architecture.
Output_arch (ARM) // entry is also a command used to set the entry point. The entry point is stext. According to the LD description, the entry point indicates the first command to run the program. The kernel is a module. You can think of it as a large program running on the hardware. Our program runs on the kernel first. Compare the Java Virtual Machine and the Java program running on it ......
Entry (stext) // set jiffies to jiffies_64
Jiffies = jiffies_64; // defines the output file segment
Sections
{// Set location count to 0xc0008000? The kernel runs at a c0000000 or above address.
. = 0xc0000000 + 0x00008000; // define. text. head segment, which is owned by the input file. text. in the head segment Composition/* ls syntax, the definition of seciton is as follows: Section[ Address] [( Type)]:
[ At (LMA)] [Align ( Section_align)]
[Subalign ( Subsection_align)]
[ Constraint]
{
Output-section-command
Output-section-command
...
} [> Region] [At> Lma_region] [: Phdr: Phdr...] [= Fillexp, AddressFor VMA, and AtThe command is LMA. Generally, the address is not set, so it is equal to the current location counter by default */
. Text. head: {/* this is critical. We can often see some variable declarations in kernel code, such as extern int _ stext, however, the definition cannot be found. In fact, these are all defined in the LDs file. Here we need to talk about the minor knowledge related to the compilation link. Let's know about it. The specific content can be further studied. Suppose that the C Code defines a variable int x = 0. Then, the 1 compiler will first allocate a piece of memory, used to store the value of the variable 2 the compiler creates an item in the program's symbol table to store the address of the variable, for example, the preceding int x = 0, create x items in the symbol table, which points to a piece of memory, sizeof (INT) size, and the storage value is 0. When this X is used, the compiler will generate the corresponding code, first point to the memory of this X, and then read the value in the memory. The above content is the definition of a variable in C. However, variables can also be defined in linker scripts. At this time, only one symbol item is generated, but no memory is allocated .. For example, if _ stext = 0x100, a symbol item is created, pointing to the memory of 0x100, but the value is not stored in the memory. Therefore, if we use the variable defined in LS in C, we can only get its address. The following is an example:

start_of_ROM   = .ROM;end_of_ROM     = .ROM + sizeof (.ROM) - 1;start_of_FLASH = .FLASH;
The above three variables are defined in LS, respectively pointing to the start and end of the. Rom segment and the start of the flash segment. Now I want to copy the content of the RoM segment to the Flash segment in the C code. below is the C code:
extern char start_of_ROM, end_of_ROM, start_of_FLASH;memcpy (& start_of_FLASH, & start_of_ROM, & end_of_ROM - & start_of_ROM);
Pay attention to the address Symbol &. In C code, it is meaningless to use the variable. start_of_rom defined in LS in this way only. Only its address makes sense. Because its value is not initialized. The address points to the beginning of the. Rom segment.To put it bluntly, the variable defined in LS is actually the address, that is, _ stext = 0x100 is an address in the C code int * _ stext = 0x100. Understand?A slot will be allocated in the final lD, and then the address of X will be stored. That is to say, LD knows these actions. Of course, we can also define a variable in LS and use it in C. So the following sentence actually defines a _ stext variable. It can be referenced through extern in C. However, there is a key issue here. The value of X = 0 defined in C is initialized to 0. That is, slot... to be supplemented */
_ Stext = .;.
_ Sinittext = .;
* (. Text. Head)
} // Defines the. init segment, which is composed of all. init. Text/. cpuinit. Text/. meminit. Text. // The LC value starts with. init.
. Init: {/* init code and Data */
*(. Init. text )*(. cpuinit. text )*(. meminit. text) // defines the Variable _ einitext, whose value is the current LC, that is. initial Value of init + *(. init. text )*(. cpuinit. text )*(. meminit. text. That is to say, the variable // _ einitext indicates an end.
_ Einittext =.; // The following Variable
_ Proc_info_begin indicates a start
_ Proc_info_begin = .;
* (.Proc.info. init) // all the content of the .proc.info. init section is in this
_ Proc_info_end =.; // The following Variable
_ Proc_info_end indicates the end. It and the _ proc_info_begin variable have to get stuck with the content of the output file .proc.info. init. // With the introduction of begin and end above, it will be simple later. Most of them are a begin + end to hold a piece of content. According to the previous introduction, begin and end can be referenced in the C program // that is, we can get stuck content through begin + end. For example, we put some initialization function pointers in a begin and end. Then, you can call these functions through a loop. The following is an example.
_ Arch_info_begin = .;
* (.Arch.info. init)
_ Arch_info_end = .;
_ Tagtable_begin = .;
* (. Taglist. init)
_ Tagtable_end = .;
. = Align (16 );
_ Setup_start = .;
* (. Init. Setup)
_ Setup_end = .;
_ Early_begin = .;
* (. Early_param.init)
_ Early_end = .;
_ Initcall_start = .;
* (. Initcallearly. init) _ early_initcall_end = .;
*(. Initcall0.init )*(. initcall0s. init )*(. initcall1.init )*(. initcall1s. init )*(. initcall2.init )*(. initcall2s. init )*(. initcall3.init )*(. initcall3s. init )*(. initcall4.init )*(. initcall4s. init )*(. initcall5.init )*(. initcall5s. init )*(. initcallrootfs. init)
* (. Initcall6.init) * (. initcall6s. init) * (. initcall7.init) * (. initcall7s. init)
_ Initcall_end = .;
_ Con_initcall_start = .;
* (. Con_initcall.init)
_ Con_initcall_end = .;
_ Security_initcall_start = .;
* (. Security_initcall.init)
_ Security_initcall_end = .;
. = Align (32); // align, indicating align. That is, the location counter position must be aligned by 32.
_ Initramfs_start =.; // location of ramfs
Usr/built-in.o (. init. ramfs)
_ Initramfs_end = .;
. = Align (4096); // 4 K alignment
_ Per_cpu_load = .;
_ Per_cpu_start = .;
* (. Data. percpu. page_aligned)
* (. Data. percpu)
* (. Data. percpu. shared_aligned)
_ Per_cpu_end = .;
_ Init_begin = _ stext;
* (. Init. Data) * (. cpuinit. Data) * (. cpuinit. rodata) * (. meminit. Data) * (. meminit. rodata)
. = Align (4096 );
_ Init_end = .;
} // Disacard is a special section, indicating that all input sections meeting this condition are not written to the output section, that is, the output file does not contain the following sections.
/Discard/: {/* exit code and Data */
* (. Exit. Text) * (. cpuexit. Text) * (. memexit. Text)
* (. Exit. Data) * (. cpuexit. Data) * (. cpuexit. rodata) * (. memexit. Data) * (. memexit. rodata)
* (. Exitcall. Exit)
* (. Arm. exidx. Exit. Text)
* (. Arm. extab. Exit. Text)
}
// Omit some content // ADDR is a built-in function used to return VMA/* here is a small example. Let's take a look at what effect VMA and LMA have on sections
{
. Text 0x1000: {* (. Text) _ etext =.;}/. Text Segment VMA is 0x1000, and LMA = VMA
. Mdata 0x2000: // The vma of the. mdata segment is 0x2000, but its LMA is at the end of the. Text Segment.
At (ADDR (. Text) + sizeof (. Text ))
{_ DATA =.; * (. data); _ edata = .;}
. BSS 0x3000:
{_ Bstart =.; * (. BSS) * (common); _ bend = .;}
} Have you seen it ?. The mdata segment is running at 0x2000, but the data load address is after the. Text Segment. Therefore, you need to copy the. mdata segment content during running. Extern char _ etext, _ data, _ edata, _ bstart, _ bend;
Char * src = & _ etext; // _ etext is the VMA address at the end of the. Text end, but it is also the start of the. mdata segment LMA, which is specified by at in the LS type.
Char * DST = & _ data; // _ data is the VMA of the mdata segment. Now you need to copy the content starting from the LMA address to the start of the VMA.

/* Rom has data at end of text; copy it .*/
While (DST <& _ edata)
* DST ++ = * SRC ++; // copy... understand? Think about it.

/* Zero BSS .*/
For (DST = & _ bstart; DST <& _ bend; DST ++)
* DST = 0; // initialize the data area */
. Rodata: At (ADDR (. rodata)-0 ){
_ Start_rodata = .;
* (. Rodata) * (. rodata. *) * (_ vermagic) * (_ markers_strings) * (_ tracepoints_strings)
}
. Rodata1: At (ADDR (. rodata1)-0 ){
* (. Rodata1)
}
... // Omit Part of content _ edata_loc = _ data_loc + sizeof (. data );
. BSS :{
_ Bss_start =.;/* BSS */
* (. BSS)
* (Common)
_ End = .;
}
/* Stabs debugging sections .*/
. Stab 0: {* (. Stab )}
. Stabstr 0: {* (. stabstr )}
. Stab. excl 0: {* (. Stab. excl )}
. Stab. exclstr 0: {* (. Stab. exclstr )}
. Stab. index 0: {* (. Stab. Index )}
. Stab. indexstr 0: {* (. Stab. indexstr )}
. Comment 0: {* (. Comment )}
}
// Assert is a command. If the first parameter is 0, the information of the second parameter (that is, the error message) is printed, and the LD command exits.
Assert (_ proc_info_end-_ proc_info_begin), "missing CPU support ")
Assert (_ arch_info_end-_ arch_info_begin), "no machine record defined ")5. Use the variables defined in ls in the kernel codeLet's take a look at a small example [--> init/Main. C]Extern initcall_t _ initcall_start [], _ initcall_end [], _ early_initcall_end [];
// These values are defined in LS. You can search
Static void _ init do_initcils (void)
{
Initcall_t * call;
// The above has been defined as an array, so the following variables take the pointer directly and use & in the above example. value cannot be used anyway.
For (call = _ early_initcall_end; call <_ initcall_end; call ++)
Do_one_initcall (* Call );

/* Make sure there is no pending stuff from the initcall sequence */
Flush_scheduled_work ();
}SummaryFor more information about Ls, see the following URL:

  1. Lk source code does not find the source of the variable is how --- defined in LS.
  2. What is VMA and LMA.
Related Article

Contact Us

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.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.