For the. LDS file, it defines the connection process after the program is compiled, and determines the storage location of each segment of the executable program. Although I have not used it yet, it is very important to know about it. Let's take a look at the complete description of the. LDS file format on the GNU Official Website:
Sections { ... SecnameStartBlock (Align) (Noload): (Ldadr) {Contents}>Region:Phdr=Fill ... } |
Secname and contents are mandatory, and others are optional. Below are some common examples:
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 connection (run) Address of the current segment. If at (ldadr) is not used, the address stored in this segment is also start. On the GNU website, start can be described using any type of address description symbol. 4. At (ldadr): defines the address for storing (loading) This section. Let's look at a simple example: (from "2410 full development")
/* 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 specified by at, the storage address), but its running address is 0x30000000. Before running, it must start from 0x1000 (loading location) copy to 0x30000000 (runtime), and read the NAND Flash. This is the difference between the storage address and the connection (run) Address, known as loading the time domain and running the time domain, which 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, there are some differences between jump commands. Here we write them down. If you forget them, you can check them, I have forgotten a lot of things before... In arm assembly, there are two jump Methods: B jump command and LDR command assign value to PC. I have summarized as follows: (1) B Step1: 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, = Step1: This command reads data from a location in the memory (Step1) and assigns it to the PC. It also depends on the value of the current PC, however, the offset is the connection address (runtime address) at that position (step 1), 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. Still use the comments at the time:
Relocate:/* relocate U-boot to Ram */ ADR r0, _ start/* R0 is the current position of the Code */ /* ADR pseudocommand. The assembler automatically calculates the Pc value when executing the command _ start based on the current Pc value and puts it in R0: 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 compiler (set in ads or-d )*/ CMP r0, R1/* compare R0 and R1. do not perform relocation during debugging */ |
Next, take a look at a formal connection script file with the u-boot.lds. The basic functions of this file can still be seen clearly. Although I have analyzed a lot above, the GNU-style symbols still confuse me, despise yourself...
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; Starting from 0x0 . = 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. } |