introduction
Programming based on position-independent code PIC (PositionIndependent Code) plays an important role in the development of embedded application systems, especially in the development of Bootloader programs and kernel initialization design in the bare metal state; the position-independent programming method can also be used to build high-efficiency dynamic link libraries in specific applications. Therefore, a deep understanding and proficiency in position-independent programming methods will help developers design simple and clear applications. This article first introduces the basic concepts and implementation principles of position-independent code, then explains the programming method and implementation process of position-independent programming based on ARM assembly, and finally takes Bootloader programming as an example to introduce the role of position-independent programming in Bootloader programming.
1 Position-independent code and programming method
1.1 Basic concepts and implementation principles
An application must be compiled, assembled, and linked before it can become an executable file. During linking, all target files must be relocated, symbol reference rules must be established, and running addresses must be assigned to variables, functions, etc. When the program is executed, the system must load the code into the address space specified during linking to ensure that the program correctly references symbols such as variables and functions during execution so that the program can run normally. In a system with an operating system, the relocation process is automatically completed by the operating system.
When designing the Bootloader program, it must be done in a bare metal environment, and the running address of the Bootloader image file must be set by the programmer. Normally, the Bootloader program is downloaded to the 0x0 address of the ROM for startup, and in most application systems, in order to start quickly, the Bootloader program is first copied to the SDRAM and then run. Generally, the addresses of the two are not the same, and the address relocation process of the program in the SDRAM must be completed by the programmer. In fact, since the Bootloader is the first program to be executed after the system is powered on, the copy of the Bootloader program and all the work before this must be completed by itself, and these instructions are executed in the ROM. In other words, these codes can be executed correctly even if they are not in the runtime address space specified at the time of linking. This is position-independent code, which is a special code that can be executed normally when loaded into any address space.
Position-independent code is often used in the following situations:
◆ The program is dynamically loaded into the memory during operation.
◆ The program is loaded into the memory after being combined with different programs in different occasions (such as shared dynamic link libraries)
. ◆ The mapping between different addresses during operation (such as the Bootloader program).
Although the -fPIC option can be used to generate position-independent code for C language when compiling with GCC, this does not correct the inherent position-dependency defects in programming. In particular, for assembly language code, the programmer must follow certain programming guidelines to ensure the position independence of the program.
1.2 Key points of position-independent programming for ARM processors
The position-independent executable file PIE (PositionIndependent Executable) of the ARM program consists of two parts: position-independent code PIC and position-independent data PID (PositionIndependent Data).
PID is mainly for the readable and writable data segment (.data segment), which stores global variables with initial values. In order to achieve its position independence, register R9 is usually used as a static base address register to point to the first address of the readable and writable segment, and the offset relative to the base address register is used to address the variables of the segment. This method is often used to generate multiple independent data segments for multiple instances of reentrant programs. In program design, it is generally not necessary to consider the position independence of the readable and writable segment, mainly because the readable and writable data is mainly allocated in SDRAM.
PIC includes the code and read-only data (.text segment) in the program. To ensure that the program can run correctly in both ROM and SDRAM space (such as the Bootloader program in the bare metal state), position-independent code programming must be used. The following focuses on the key points of PIC programming.
PIC follows the programming specifications of ATPCS (ARM Thumb Procedure Call Standard) of read-only segment position independence ROPI (Read Only Position Independence):
(1) Program design specifications
When referencing symbols in the same ROPI segment or another ROPI segment with a fixed relative position, it must be a PC-based symbol reference, that is, an offset relative to the current PC is used to implement a jump or a constant access.
① Position-independent program jump. In the ARM assembler, the relative jump instruction B/BL is used to implement program jump. The target address of the jump in the instruction is represented by an offset based on the current PC, which is independent of the absolute address value assigned to the address label during linking. Therefore, the code can jump to any position to achieve position independence.
In addition, you can also use the ADR or ADRL pseudo-instructions to read the address label value into the PC to implement program jumps. This is because pseudo-instructions such as ADR or ADRL will be replaced by the compiler to operate on the address value based on the PC, but the address range that can be read in this way is small and varies depending on whether the address value is word-aligned. However, in ARM programs, using instructions such as LDR to directly read the address label value into the PC to implement program jumps is not position-independent. For example:
LDRPC, =main
The result of compiling the above LDR assembly pseudo-instruction is:
LDRPC, [PC, OFFSET_TO_LPOOL]
LPOOLDCD main
It can be seen that although LDR loads the contents of a PC-based storage unit LPOOL into the PC, the storage unit stores the absolute address of the main function entry determined at link time, so the actual segment where the main function is located is not position-independent.
② Position-independent constant access. In applications, it is often necessary to read and write related registers to complete the necessary hardware initialization. To enhance the readability of the program, the EQU pseudo-instruction is used to assign values to some constants, but position independence must be achieved during the access process. The following introduces the position-independent constant access method using the GPIO initialization of PXA270.
GPIO_BASEEQU0x40e00000;
GPIO base register address GPDR0EQU0x00c; offset relative to GPIO base register
init_GPDR0EQU0xfffbfe00; initial value of register GPDR0
LDRR1, =GPIO_BASE
LDRR0, =init_GPDR0
STRR0, [R1, #GPDR0]
The compiled result of the above assembly code segment is:
LDRR1, [PC, OFFSET_TO_GPIO_BASE]
LDRR0, [PC, OFFSET_TO_init_GPDR0]
STRR0, [R1, #0xc]
GPIO_BASEDCD0x40e00000
GPDR0DCD0x00c
init_GPDR0DCD0xfffbfe00
It can be seen that the LDR pseudo-instruction actually uses the offset based on the PC to reference the symbolic constants GPIO_BASE and init_GPDR0, so it is position-independent. From this, we can draw the following conclusion: using the LDR pseudo-instruction to read a constant into other general registers other than the PC can achieve position-independent constant access; but when an address value is read into the PC for program jump, the jump target is position-dependent.
(2) Programming Specifications 2
Other references by code in the ROPI segment must be absolute addresses, or writable data based on the static base registers of the read-write position-independent (RWPI) segment.
Using absolute addresses can only reference symbols in code segments that have been relocated to specific locations. By introducing absolute addresses in position-independent code, the program can jump to a specified location. For example, assuming that stage 1 of the Bootloader copies its own code to the SDRAM address space specified during linking, when it wants to jump to the C program entry of stage 2, it can use the instruction "LDRPC, =main" to jump to the main function entry address of the program in SDRAM to start execution. This is because the program assigns an absolute address to the main function when compiling and linking, and the system implements program jumps by directly assigning the absolute address of the main function to the PC. If the relative jump instruction "Bmain" is used, it will only jump to the main function entry inside the boot ROM. [page]
2 Application of Position Independent Code in Bootloader Design
When using GNU tools to develop a bootloader, the program will use a linker script to set the memory map of the image file during linking. A simple linker script structure is as follows:
OUTPUT_ARCH(arm)
ENTRY(_start)
SECTIONS {
. = BOOTADDR;/*Bootloader start address*/
__boot_start = .;
.textALIGN(4): {/*Code segment.text*/
*(.text)
}
.dataALIGN(4): {/*Data segment.data*/
*(.data)
}
.gotALIGN(4): {/*Global offset table.got segment*/
*(.got)
}
__boot_end = .;/*Bootloader image file end address*/
.bssALIGN(16): {/*Stack segment.bss*/
__bss_start = .;
*(.bss)
__bss_end = .;
}
}
The syntax of the link script is not introduced here. It should be pointed out that the output segment address described in the link script is the virtual address VMA (Virtual Memory Address). The "virtual address" here only refers to the relocation of each output segment to the corresponding storage address space when the image file is executed, and has nothing to do with memory management. Therefore, the above link script actually specifies that the Bootloader image will be relocated to the storage address space starting from BOOTADDR when it is executed, so as to ensure that the symbols are correctly referenced at the relevant locations so that the program can run normally.
After the ARM processor is reset, it always takes the first instruction from the address 0x0. Therefore, you only need to set BOOTADDR to 0, and then download the compiled executable binary file to the storage space starting from the 0x0 address of the ROM, and the program can be booted normally; however, once the image file is specified to start from the 0x0 address during linking, the Bootloader can only run in the ROM space starting from the 0x0 address, and cannot be copied to the SDRAM space to run for fast booting. Of course, for microprocessors with MMU functions such as PXA270, although the entire Bootloader image can be copied to SDRAM first, and then the MMU function is used to map the SDRAM space to the 0x0 address, and then continue to run in SDRAM; but this will complicate the design and implementation of the Bootloader on the one hand, and on the other hand, in some applications where the MMU function must be shielded (such as booting the armlinux system), the MMU cannot be used for address remapping.
The above problem can be solved by using ARM's position-independent programming. When linking the program, just set BOOTADDR to the address of the SDRAM space (generally, the highest 1 MB storage space in SDRAM is used as the starting address). In this way, after the ARM processor is powered on and reset, the Bootloader can still start execution from address 0 and copy itself to the SDRAM starting from the specified __boot_start to run. The startup code architecture corresponding to the link script that implements the above function is as follows:
.section .text
.globl _start
_start:
Breset/*Reset exception*/
/*Other exception handling code*/
reset:
/*Reset handler*/
copy_boot:/*Copy Bootloader to SDRAM*/
LDRR0, =0x0LDRR1, =__boot_start
LDRR2, =__boot_end
1:LDRMIA R0!, { R3-R10 }
STRMIA R1!, { R3-R10 }
CMPR1, R2
BLT1b
clear_bss:
/*Clear .bss segment*/
BL
init_Stack/*Initialize stack*/
LDRPC, = main/*Jump to C program entry of stage 2*/
.end
The program entry is _start, which is the reset exception. All other exception vectors are implemented using the relative jump instruction B to ensure position independence. After completing the basic hardware initialization, the Bootloader image is copied to the specified SDRAM space using the parameters of __boot_start and __boot_end passed by the link script, and the .bss segment is cleared. After initializing the stack, the program assigns the absolute address of the main function entry to the PC, and then jumps to the SDRAM to continue running. Before the program jumps to the main function, all the code runs in the ROM, so the position independence of the code must be guaranteed. Therefore, when calling subroutines such as initializing GPIO, storage system, and stack, relative jump instructions are used to complete it.
Using position-independent design Bootloader program has the following advantages:
① Simplify the design and facilitate fast booting of the system. Position-independent code can avoid address mapping during booting and easily jump to SDRAM for fast booting.
② Realize intelligent reset processing. Since position-independent code can be loaded into any address space for execution, the current address at runtime is not necessarily the same as the address assigned at link time. Using this feature, you can add the following code to the reset handler after making the processor enter SVC mode and disable interrupts, so that different reset processing can be performed according to the current runtime address:
ADRR0, _start/*Read the instruction address of the _start label near the current PC*/
LDRR1,=__boot_start/*Read the starting address of the Bootloader in SDRAM*/
CMPR0,R1
BEQclear_bss
The _start label address read by the ADR instruction in the above code is determined by the execution address of the instruction. If it is started from the Bootloader in SDRAM, the above comparison results are equal, and the program directly jumps to the clear_bss label address for execution, which can avoid the reinitialization of the storage system and the copy process of the Bootloader; if it is powered on or hardware reset, the program is started from ROM, and the above comparison results are not equal, and the program will perform a comprehensive reset processing operation including system initialization and Bootloader copying.
③ Easy to debug. Bootloader debugging is usually a tedious process. Using position-independent code, the image file can be loaded into SDRAM for debugging, which can truly reflect the situation of the program booting the system from ROM and avoid frequent burning of program memory.
3 Conclusion
The position-independent programming introduced in this article is implemented through symbol reference specifications based on PC or base registers. This method is widely used in actual system development, and can be used for boot program design, general application program development or embedded shared library development. Introducing position-independent code in the design of Bootloader can make the program structure simpler and clearer, avoid address remapping and quickly boot the system from SDRAM; referencing the position-independent design method makes the reset processing function of Bootloader more flexible, and also makes the program debugging in SDRAM and ROM have the same effect.
references
[1] Du Chunlei. ARM Architecture and Programming [M]. Beijing: Tsinghua University Press, 2003.
[2] Chen Wenzhi. Principles and Practices of Embedded System Development [M]. Beijing: Tsinghua University Press, 2005.
[3] ARM Limited. ARM Architecture Reference Manual. 2nd ed,2000.
[4] ARM Limited. ADS Developer Guide. Release 1.2,200111.
[5] ARM Limited. ADS Assembler Guide. Release 1.2,200111.
[6] Red Hat Inc. Using ld, the GNU linker. Version 2.14.
Previous article:Research on 32-bit high-end embedded microprocessor and embedded operating system kernel based on ARM9
Next article:Implementation of S3C2410 interrupt program
Recommended ReadingLatest update time:2024-11-16 21:59
Professor at Beihang University, dedicated to promoting microcontrollers and embedded systems for over 20 years.
- Innolux's intelligent steer-by-wire solution makes cars smarter and safer
- 8051 MCU - Parity Check
- How to efficiently balance the sensitivity of tactile sensing interfaces
- What should I do if the servo motor shakes? What causes the servo motor to shake quickly?
- 【Brushless Motor】Analysis of three-phase BLDC motor and sharing of two popular development boards
- Midea Industrial Technology's subsidiaries Clou Electronics and Hekang New Energy jointly appeared at the Munich Battery Energy Storage Exhibition and Solar Energy Exhibition
- Guoxin Sichen | Application of ferroelectric memory PB85RS2MC in power battery management, with a capacity of 2M
- Analysis of common faults of frequency converter
- In a head-on competition with Qualcomm, what kind of cockpit products has Intel come up with?
- Dalian Rongke's all-vanadium liquid flow battery energy storage equipment industrialization project has entered the sprint stage before production
- Allegro MicroSystems Introduces Advanced Magnetic and Inductive Position Sensing Solutions at Electronica 2024
- Car key in the left hand, liveness detection radar in the right hand, UWB is imperative for cars!
- After a decade of rapid development, domestic CIS has entered the market
- Aegis Dagger Battery + Thor EM-i Super Hybrid, Geely New Energy has thrown out two "king bombs"
- A brief discussion on functional safety - fault, error, and failure
- In the smart car 2.0 cycle, these core industry chains are facing major opportunities!
- The United States and Japan are developing new batteries. CATL faces challenges? How should China's new energy battery industry respond?
- Murata launches high-precision 6-axis inertial sensor for automobiles
- Ford patents pre-charge alarm to help save costs and respond to emergencies
- New real-time microcontroller system from Texas Instruments enables smarter processing in automotive and industrial applications
- [TI recommended course] #SimpleLink?MCU platform SDK code portability#
- To drive a 24V5A motor, what current should the freewheeling diode have?
- Analysis of the six major reasons for Wi-Fi wireless connection failure and solutions
- CC2640R2F supports Alibaba Cloud Link IoT platform
- Zhihuijun's new open source for National Day: Iron Man's robotic arm Dummy, interesting interactions, sewing grape demo, Hongmeng...
- Some understanding of MSP430 usage
- The price keeps going up.
- 【Goodbye 2021, Hello 2022】+ If your heart is facing the sun, there is no need to be sad, smile warmly, the years are still young, goodbye 2021, come on 2022
- A historical alarm function implementation based on Topmicro Lua script
- About the problem of transistor driving buzzer