Transferred from: jjavaa
For. LDS files, the storage location and entry address of each segment of an executable program are determined, which is also the role of link locating. The following uses the U-boot lDs as an example to describe the uboot link process.
First, let's take a look at the complete description of the. LDS file format on the GNU Official Website:
Sections {
...
Secname start block (align) (noload): At (ldadr)
{Contents}> region: phdr = fill
...
}
Secname and contents are mandatory. The former is used to name this segment, and the latter is used to determine what part of the code is placed in this segment. The following is an explanation of some keywords in this description.
1. secname: segment name
2. Contents: determines which content is placed in this segment. It can be the entire target file or a segment in the target file (code segment, data segment, etc)
3. Start: the Relocation Address of the segment. The Link (run) address of this segment. If there is a location-independent instruction in the code, this segment must be placed on this address when the program is running. Start can be described by any symbol describing the address.
4. At (ldadr): defines the storage (loading) address of this segment. If this option is not used, the loading address is equal to the running address, you can use this option to control the locations where each segment is stored in the output file.
Example:
/* NAND. LDS */
Sections {
Firtst 0x00000000: {head. O init. O}
Second 0x30000000: At (4096) {main. O}
}
Above, head. O is placed at the beginning of 0x00000000 address, init. o put in head. o, their running address is also 0x00000000, that is, the connection and storage address are the same (not specified at); main. O is placed at the beginning of 4096 (0x1000, which is the storage address specified by at), but its running address is 0x30000000, before running the program, you need to copy from 0x1000 (loading address) to 0x30000000 (running address). In this process, you also need to read the flash and copy the program to the corresponding position to run the program. This is the difference between the storage address and the operating address. It is called the loading time domain and the running time domain. It can be specified separately in the. LDS connection script file.
The compiled. LDS file is called and executed with-tfilename when you use the arm-Linux-LD connection command, as shown in figure
Arm-Linux-LD-tnand. lds x. o y. O-o xy. O. You can also use the-ttext parameter to directly specify the connection address, as shown in figure
Arm-Linux-LD-ttext 0x30000000 X. o y. O-o xy. O.
Since the program has two types of addresses, it involves some jump commands.
In arm assembly, there are two jump Methods: B jump command and LDR command assign value to PC.
Pay special attention to the meanings of the two commands:
(1) B step: the B jump command is relative to the current Pc value, and the offset is calculated by the bit [] of the command, this makes the program using the B command not dependent on the position of the Code to be jumped, only the instruction itself.
(2) ldr pc, = step: this command is a pseudo command that will generate the following code after compilation:
Ldr pc, 0x30008000
<0x30008000>
Step
It refers to reading data from a location (STEP) in the memory and assigning it to the PC. It also depends on the value of the current PC, but the offset is the connection address (runtime address) of the step ), so you can use it to redirect from flash to ram.
(3) In addition, it is necessary to review the pseudocommands of ADR. The relocate code in U-boot is to use ADR to realize whether the current program is in Ram or flash:
Relocate:/* relocate U-boot to Ram */
ADR r0, _ start/* R0 is the current position of the Code */
/* ADR pseudocommand. The assembler automatically calculates the value of "_ start" in this command based on the value of the current PC. The value of PC is placed in R0 when _ start is executed:
When this segment is executed in flash, R0 = _ start = 0; when this segment is executed in Ram, _ start = _ text_base (in board/smdk2410/config. the value specified in MK is 0x33f80000, that is, U-boot copies the code to the beginning of the code snippet executed in Ram )*/
LDR R1, _ text_base/* test whether it is started from flash or Ram */
/* The result of executing this sentence R1 is always 0x33ff80000, because this value is specified by the link */
CMP r0, R1/* compare R0 and R1. do not perform relocation during debugging */
Talk about the connection script in combination with the u-boot.lds.
Output_format ("elf32 & shy; littlearm", "elf32 & shy; littlearm", "elf32 & shy; littlearm ")
; Specifies that the output executable file is in the ELF format, 32-bit arm command, small-end
Output_arch (ARM)
; Specify the platform for outputting executable files as arm
Entry (_ start)
; Specifies the starting code segment of the output executable file as _ start.
Sections
{
. = 0x00000000; locate the current address as 0
. = Align (4); the Code is 4 bytes aligned
. Text:; specifies the code segment
{
CPU/ARM920T/start. O (. Text); the first part of the code
* (. Text); Other code
}
. = Align (4)
. Rodata: {* (. rodata)}; specifies the read-only data segment.
. = Align (4 );
. Data: {* (. Data)}; specifies the read/write data segment.
. = Align (4 );
. Got: {* (. Got)}; specifies the got segment. The got segment is a custom segment of uboot and is not a standard segment.
_ U_boot_cmd_start =.; assign _ u_boot_cmd_start to the current position, that is, the start position.
. U_boot_cmd: {* (. u_boot_cmd)}; specify the u_boot_cmd segment. uboot puts all uboot commands in this segment.
_ U_boot_cmd_end =.; assign _ u_boot_end _end to the current position, that is, the end position.
. = Align (4 );
_ Bss_start =.; assign _ bss_start to the current position, that is, the start position of the BSS segment.
. BSS: {* (. BSS)}; specifies the BSS segment
_ End =.; assign the value of _ end to the current position, that is, the end position of the BSS segment.
} Most of the analysis of u-boot.lds files on the Internet are all thousands of times, for example, the following is my u-boot.lds found on the Internet information.
Output_format ("elf32-littlearm", "elf32-littlearm", "elf32-littlearm ")
/* Specify that the output executable file is in ELF format, 32-bit arm command, and a small end */
Output_arch (ARM)
/* Specify the platform for outputting executable files as arm */
Entry (_ start)
/* Specify the starting code segment of the output executable file as _ start */
Sections
{
/* Specify the global entry point of the executable image file. Generally, this address is placed at 0x0 in the ROM (flash. This address must be known to the compiler. It is usually modified here */
. = 0x00000000;/*; Starting from 0x0 */
. = Align (4);/* the code is 4 bytes aligned */
. Text:
{
CPU/ARM920T/start. O (. Text)
/* The first part of the code */
* (. Text)
/* Function of each text segment in sequence */
}
. = Align (4 );
/* The code is 4-byte aligned */
. Rodata: {* (sort_by_alignment (sort_by_name (. rodata *)))}
/* Specify the read-only data segment */
. = Align (4 );
/* The code is 4-byte aligned */
. Data: {* (. Data )}
. = Align (4 );
/* The code is 4-byte aligned */
. Got: {* (. Got )}
/* Specify the got segment. The got segment is a custom uboot segment and is not a standard segment */
. = .;
_ U_boot_1__start = .;
/* Assign _ u_boot_cmd_start to the current position, that is, the start position */
. U_boot_cmd: {* (. u_boot_cmd )}
/* Specify the u_boot_cmd segment. uboot places all uboot commands in this segment .*/
_ U_boot_1__end = .;
/* Assign _ u_boot_end _end to the current position, that is, the end position */
. = Align (4 );
/* The code is 4-byte aligned */
_ Bss_start = .;
/* Assign _ bss_start to the current position, that is, the start position of the BSS segment */
. BSS (noload): {* (. BSS). = align (4 );}
/* Specify the BSS segment to tell the loader not to load this segment */
_ Bss_end = .;
/* Assign the _ end value to the current position, that is, the end position of the BSS segment */
}
After reading the above parsing ideas, it should have been very clear, so compile U-boot and check system. Map,
30100000 T _ start
30100020 T _ undefined_instruction
30100024 T _ software_interrupt
30100028 T _ prefetch_abort
3010002c T _ data_abort
30100030 T _ not_used
30100034 T _ IRQ
30100038 T _ FIQ
Found that the link address of _ start is not the current address of. Text in the u-boot.lds 0x00000000, but 0x30100000, which produces a lot of questions:
(1) Why does the first address of. Text specified by the u-boot.lds not work?
(2) What is 0x30100000? Who specifies that the first address of. Text is 0x30100000?
(3) If there are other actions changed the first address of. Text, then the action and the priority of the u-boot.lds is how to decide?
In fact, these three questions are found in the makefile ldflags variable and the u-boot.lds answer. Let's try to modify the u-boot.lds to the following (the red font part is the modified part ):
Output_format ("elf32-littlearm", "elf32-littlearm", "elf32-littlearm ")
/* Specify that the output executable file is in ELF format, 32-bit arm command, and a small end */
Output_arch (ARM)
/* Specify the platform for outputting executable files as arm */
Entry (_ start)
/* Specify the starting code segment of the output executable file as _ start */
Sections
{
/* Specify the global entry point of the executable image file. Generally, this address is placed at 0x0 in the ROM (flash. This address must be known to the compiler. It is usually modified here */
. = 0x30000000;/*; Starting from 0x0 */
. = Align (4);/* the code is 4 bytes aligned */
. Rodata: {* (sort_by_alignment (sort_by_name (. rodata *)))}
. = Align (4 );
/* The code is 4-byte aligned */
. Text:
{
CPU/ARM920T/start. O (. Text)
/* The first part of the code */
* (. Text)
/* Function of each text segment in sequence */
}
/* Specify the read-only data segment */
. = Align (4 );
/* The code is 4-byte aligned */
. Data: {* (. Data )}
. = Align (4 );
/* The code is 4-byte aligned */
. Got: {* (. Got )}
/* Specify the got segment. The got segment is a custom uboot segment and is not a standard segment */
. = .;
_ U_boot_1__start = .;
/* Assign _ u_boot_cmd_start to the current position, that is, the start position */
. U_boot_cmd: {* (. u_boot_cmd )}
/* Specify the u_boot_cmd segment. uboot places all uboot commands in this segment .*/
_ U_boot_1__end = .;
/* Assign _ u_boot_end _end to the current position, that is, the end position */
. = Align (4 );
/* The code is 4-byte aligned */
_ Bss_start = .;
/* Assign _ bss_start to the current position, that is, the start position of the BSS segment */
. BSS (noload): {* (. BSS). = align (4 );}
/* Specify the BSS segment to tell the loader not to load this segment */
_ Bss_end = .;
/* Assign the _ end value to the current position, that is, the end position of the BSS segment */
}
On the face of u-boot.lds mainly made two changes
(1) Change 0x00000000 to 0x30000000.
(2) Change the address of. Text and. rodata.
Re-compile U-boot and view system. Map
30000000 R version_string
30000028 R c.27.2365
30100000 T _ start
30100020 T _ undefined_instruction
From the content of system. map, we can see that:
(1) The address set for the u-boot.lds (0x00000000 or 0x30000000) is valid.
(2) The text address is still 30100000
Check the ldflags variable in makefile and find an instruction.
Ldflags + =-ttext $ (text_base) Where text_base is defined in the config. mk file under the subdirectory of the development board name corresponding to the Board folder in the U-boot root directory
Text_base = 0x30100000
Here we should understand why _ start, that is. the first address of text is always 0x30100000. During connection, the LD command will assign the address specified by the parameter-ttext. text, so. text does not work for the default address (current address) in the u-boot.lds