We compile each C or assembly file separately, but do not connect, generate a lot of. o Files, These. o files are first scattered, the first thing we need to consider is how to combine them, and secondly, these. o files have a mutual invocation relationship; Moreover, the last bin file we generate is to be run in hardware, and each part of the address must be carefully explained. I think in writing makefile, the most important thing is LD's understanding, the following talk about my experience:
First of all, to determine that our program does not use the standard C library, or some of the system's library files, these are generally developed on the operating system to pay attention to the problem, there is not much to say, familiar with the Linux programming people, basically will use the LD command; here, we start from the beginning, directly to the assembly language connection.
We write a assembler program that controls the GPIO, thus controlling the external led, the code is as follows;
. text
. Global _start
_start:
LDR r0,=0x56000010 @GPBCON Registers
MOV r1,# 0x00000400
STR R1,[R0]
LDR r0,=0x56000014
MOV R1, #0x00000000
STR R1,[r0]
Main_loop:
B Main_loop
The code is simple, which is to set the IO port and write the data. We see how it compiles, note that we are not using ARM-LINUX-GCC here, but ARM-ELF-GCC, there is nothing bigger than the difference between the two, ARM-LINUX-GCC may contain more library files, on the command line compiled above is no difference. Let's see how it's compiled:
Arm-elf-gcc-g-c-o LED_ON.O led_on.s First pure compilation not connected
Arm-elf-ld-ttext 0x00000000-g Led_on.o-o led_on_elf
Using Ttext to indicate where our program is stored, the elf file is generated, not our actual bin, but some tools can be used to debug it. And then:
Arm-elf-objcopy-o binary-s led_on_elf Led_on.bin
Generate the bin file.
The-t option is an important option in the LD command, which can be used to directly indicate code snippets, data segments, PhD students,
section, for complex connections, you can write a script specifically to tell the compiler how to connect.
-ttext Addr
-tdata Addr
-TBSS Addr
Arm-elf-ld-ttext 0x00000000-g Led_on.o-o Led_on_elf, the operating address is 0x00000000, because there is no specified data segment and BSS, they will be placed in the back of the default. The same code is different ttext, you can compare the difference between them, LD will automatically adjust the address of the jump.
The second concept: Section,section can be understood as a piece, such as a sub-function in C, is a section, the linker ld each section in the object file as a whole, assigning it a running address (memory layout) , this process is relocation (relocation), and finally merging all the target files into one target file.
The link is controlled by a linker script that describes the mapping of the input file's sections to the output file and the memory layout of the output file.
Therefore, linker always uses a linker script, and if not specifically specified, uses the default script; You can use the '-t ' command-line option to specify a linker script.
* Input and output segments of the image file
Linker merge multiple input files into one output file. Both the output file and the input file are target files (object file), and the output file is often called an executable (executable) file.
Each target file has a series of sections, the section of the input file is called the input section, and the section of the output file is called an output section.
A section can be loadable, that is, the output file will need to be loaded into the memory (similar to the RO&RW segment), or it can be allocatable, so the section does not have any content, At some point, the corresponding memory area is initialized with 0 (similar to the Zi segment), and if a section is neither loadable nor allocatable, it usually contains debug information.
The output section for each loadable or allocatable has two addresses, one for VMA (virtual memory address), the run-time domain address for the sections, and the LMA (load memory Address), which is the load time domain of the section.
You can view the sections in the target file by appending the '-h ' option to the Objdump tool.
* Simple linker script
(1) Sections command:
The SECTIONS command tells the linker how to map input SECTIONS into output SECTIONS, and what to place the output SECTIONS In memory.
The command format is as follows:
SECTIONS
{
Sections-command
Sections-command
......
}
Where Sections-command can be a entry command, a symbolic assignment, an output segment description, or a overlay description.
(2) Address counter '. ' (Location counter):
The symbol can be used only inside the sections command, with an initial value of ' 0 ', which can be assigned, or it can be evaluated or assigned to other symbols. It automatically calculates the current address based on the size of the output segment described internally by the sections command.
(3) Output section description (description):
Mentioned earlier in the sections command can be described in the output section, the format described is as follows:
section [Address] [(type)]: [at (LMA)]
{
Output-section-command
Output-section-command
...
} [>region] [at>lma_region] [:p HDR:p HDR ...] [=fillexp]
Many additional options are not available. The Output-section-command can be a symbolic assignment, an input segment description, a data value to be directly included, or a specific output segment keyword.
*linker Script Instance
==============================
Output_arch (ARM)
ENTRY (_start)
SECTIONS {
. = 0xa3f00000;
__boot_start =.;
. Start ALIGN (4): {
* (. Text.start)
}
. Setup ALIGN (4): {
Setup_block =.;
* (. Setup)
Setup_block_end =.;
}
. Text ALIGN (4): {
* (. Text)
}
. Rodata ALIGN (4): {
* (. Rodata)
}
. Data ALIGN (4): {
* (. Data)
}
. Got ALIGN (4): {
* (. Got)
}
__boot_end =.;
. BSS ALIGN (16): {
Bss_start =.;
* (. BSS)
* (COMMON)
Bss_end =.;
}
. Comment ALIGN (16): {
* (. Comment)
}
Stack_point = __boot_start + 0x00100000;
Loader_size = __boot_end-__boot_start;
Setup_size = Setup_block_end-setup_block;
}
=============================
The description structure similar to the following in the sections command is the output segment description:
. Start ALIGN (4): {
* (. Text.start)
}
. Start returns a location-based counter (.) For Output section name,align (4) The 4-byte aligned address value. * (. Text.start) is the input segment description, * is a wildcard, meaning that all the. Text.start segments in the linked object file are linked into the output segment named. Start.
The section and its attributes identified in the source file are actually descriptions of the input segments, such as. Text.start the code for the input segment in the source file Start.s is as follows:
. section. Text.start
. Global _start
_start:
b Start
Arm-elf-ld-ttimer.lds-o timer_elf header. O
A timer.lds file must exist here.
For an. lds file, it defines the connection process after the entire program compiles, determining where each segment of an executable program is stored. Although I still do not use it, but it is very important, it is necessary to understand.
Take a look at the full description of the. lds file in the GNU official website:
SECTIONS {
...
Secname start BLOCK (align) (NOLOAD): at ( ldadr )
{ contents } > region :phdr =Fill
...
}
Secname and contents are required, and others are optional. Here are a few common look:
1,secname: paragraph name
2,contents: Decide which content in this paragraph, can be the entire target file, can also be a paragraph in the target file (code snippet, data segment, etc.)
3,start: The address of the connection (operation) of this paragraph, if not using at (LDADR), the address stored in this paragraph is also start. the GNU website says start can be described by any one of the symbols describing the address.
4,at (Ldadr): Defines the address of this section to store (load).
/* Nand.lds */
SECTIONS {
FIRTST 0x00000000: {head.o INIT.O}
Second 0x30000000:at (4096) {MAIN.O}
}
Above,HEAD.O placed at the beginning of the 0x00000000 address,INIT.O placed behind HEAD.O, their operating address is 0x00000000, that is, the connection and storage address is the same (no at specified); The MAIN.O is placed at the beginning of the 4096 (0x1000, which is the storage address specified by the at), but its run address is 0x30000000 and needs to be copied from the 0x1000 (load place) to the 0x30000000 (runtime), this process is also used to read Nand Flash.
This is the difference between the storage address and the connection (run) address, called the load time domain and the runtime domain, which can be specified separately in the. LDS connection script file.
A well-written . lds file, with -tfilename to invoke execution when using the ARM-LINUX-LD Connection command, as
Arm-linux-ld–tnand.lds x.o y.o–o xy.o. The connection address is also specified directly with the-ttext parameter, such as
Arm-linux-ld–ttext 0x30000000 x.o y.o–o xy.o.
Since the program has two kinds of addresses, it involves some of the difference between the jump instructions, here just write down, in case you forget to also see, before a lot of things have not been written down now forget almost.
In arm assembly, there are often two methods of jumping:b Jump instruction,LDR instruction to the PC assignment value.
I myself have been summed up as follows:
b Step1:b Jump instruction is relative jump, depends on the current PC value, offset is calculated by the instruction itself bit[23:0], which makes the program using the b instruction does not depend on the location of the code to jump to, just look at the instruction itself.
Ldr pc, =STEP1: This instruction reads the data from a location in memory (Step1) and assigns it to the pc, and also relies on the value of the current pc, but the offset is the connection address (theruntime address) of that location (STEP1), so it can be implemented from Flash -to-RAM program jumps.
In addition, it is necessary to recall the ADR pseudo-directive,U-boot in the relocate code is the ADR implementation of the current program is in RAM or Flash. Still using my comments at the time
ADR R0, _start/* R0 is the current location of the code */
/* ADR pseudo-instructions, the assembler automatically calculates the value of the pc when executing to _start by the value of the current PC , and puts it into r0:
When this segment is executed in flash r0 = _start = 0; When this segment is executed in RAM _start = _text_base (the value specified in Board/smdk2410/config.mk is 0x33f80000, which is the beginning of the code snippet that u-boot copies the code into RAM to execute ) * /
LDR R1, _text_base/* Test to determine whether to start from Flash, or RAM */
/* The result of this sentence execution R1 is always 0x33ff80000, because this value is also specified by the compiler (ads in Settings, or- d set compiler parameters ) */
CMP r0, r1/* Compare R0 and R1, do not perform relocation while debugging * /
Below, combine u-boot.lds to see a formal connection script file. The basic function of this document can also be seen, although the above analysis of a lot of, but the GNU style of the symbols still really let me feel puzzled.
Output_format ("Elf32­littlearm", "Elf32­littlearm", "Elf32­littlearm")
; Specifies that the output executable file isElf format, 32-bitARM instructions, Small end
Output_arch (ARM)
; Specifies the platform for the output executable file to beArm
ENTRY (_start)
; Specify the starting Code field for the output executable file_start.
SECTIONS
{
. = 0x00000000; From0x0 Position Start
. = ALIGN (4); Code to4-byte alignment
. text:; Specify code snippet
{
CPU/ARM920T/START.O (. Text); The first code part of the Code
* (. text); Other Code sections
}
. = ALIGN (4)
. Rodata: {* (. rodata)}; Specify read-only data segments
. = ALIGN (4);
. Data: {* (. data)}; Specify read/Write Data segment
. = ALIGN (4);
. Got: {* (. got)};Got segment, the Got segment type isUboot a custom segment, non-standard segment
__u_boot_cmd_start =. ; put__u_boot_cmd_start assignment to the current position, which is the starting position
.u_boot_cmd: {* (. u_boot_cmd)}; U_boot_cmd , Uboot put all the uboot commands in the paragraph .
__u_boot_cmd_end =.; assigns the __u_boot_cmd_end to the current position , which is the end position
. = ALIGN (4);
__bss_start =.; assigns the __bss_start to the current position , that is, the start position of the BSS segment
. BSS: {* (. BSS)}; Specifying BSS Segments
_end =.; assigns the _end to the current position , which is the end position of the BSS segment
}
Reproduced
ARM-LINUX-LD command