Understand the internal mechanism of ARM running C program in three minutes

Publisher:wmghyuLatest update time:2021-10-14 Source: eefocusKeywords:ARM Reading articles on mobile phones Scan QR code
Read articles on your mobile phone anytime, anywhere

1. Code

Previously, I learned how to use C language to light up the LED on an ARM bare metal machine. I learned that in an ARM program, the main function needs an assembly instruction to boot itself. The function of the assembly instruction is to set the stack address, that is, to indicate the storage address of the program; and to boot the main function.


Here we use this program to analyze the internal mechanism of C program execution in ARM and the storage location of the program in the stack.


Below is the source code of the C program, the assembly instructions for the boot, and the disassembly file produced by cross-compilation:


C:


int main()

{

unsigned int *pGPFCON = (unsigned int *)0x56000050;

unsigned int *pGPFDAT = (unsigned int *)0x56000054;


/* Configure GPF4 as output pin */

*pGPFCON = 0x100;

/* Set GPF4 output to 0 */

*pGPFDAT = 0;


return 0;

}


Assembly instructions:


.text

.global _start


_start:


/* Set up memory: sp stack */

ldr sp, =4096 /* nand start */

// ldr sp, =0x40000000+4096 /* nor start*/


/* Call main */

bl main


halt:

b halt


Disassembler:


Disassembly of section .text:



/*Address*/ /*Machine code*/ /*Assembly instruction*/

00000000 <_start>:

   0: e3a0da01 mov sp, #4096 ; 0x1000

   4: eb000000 bl c


00000008 :

   8: eafffffe b 8


0000000c

:

   c: e1a0c00d mov ip, sp

  10: e92dd800 stmdb sp!, {fp, ip, lr, pc}

  14: e24cb004 sub fp, ip, #4 ; 0x4

  18: e24dd008 sub sp, sp, #8 ; 0x8

  1c: e3a03456 mov r3, #1442840576 ; 0x56000000

  20: e2833050 add r3, r3, #80 ; 0x50

  24: e50b3010 str r3, [fp, #-16]

  28: e3a03456 mov r3, #1442840576 ; 0x56000000

  2c: e2833054 add r3, r3, #84 ; 0x54

  30: e50b3014 str r3, [fp, #-20]

  34: e51b2010 ldr r2, [fp, #-16]

  38: e3a03c01 mov r3, #256 ; 0x100

  3c: e5823000 str r3, [r2]

  40: e51b2014 ldr r2, [fp, #-20]

  44: e3a03000 mov r3, #0 ; 0x0

  48: e5823000 str r3, [r2]

  4c: e3a03000 mov r3, #0 ; 0x0

  50: e1a00003 mov r0, r3

  54: e24bd00c sub sp, fp, #12 ; 0xc

  58: e89da800 ldmia sp, {fp, sp, pc}

Disassembly of section .comment:



/* Comments */

00000000 <.comment>:

   0: 43434700 cmpmi r3, #0 ; 0x0

   4: 4728203a undefined

   8: 2029554e eorcs r5, r9, lr, asr #10

   c: 2e342e33 mrccs 14, 1, r2, cr4, cr3, {1}

  10: Address 0x10 is out of bounds.


2. Knowledge Reserve

1.ARM assembly instructions

For a detailed introduction to ARM assembly instructions, see another blog:


Link


First, let's make a brief introduction to assembly instructions:


This starts with the CPU. The recognizable language of the computer is machine code, which is binary. The CPU can recognize and execute programs written in machine code. This development method is too complicated and difficult to master, so there are assembly instructions. Assembly instructions are actually a package of machine code. Assembly instructions can be converted into machine code after compilation, but compared to machine code, assembly instructions can be directly read and understood by people. An assembly instruction in ARM can be converted into 32-bit machine code. The machine code executed by ARM's CPU at a time is 32 bits, which means that ARM can process one assembly instruction at a time.


Under the control of the controller, the CPU can read and write data in the memory to the registers inside the CPU (such as r0, r1, sp, pc, lr...), and then the arithmetic unit will perform operations on the values ​​in the registers, including addition, subtraction, multiplication, division and logical operations, and the results can be written to the specified registers. The CPU can also jump to execute according to instructions, that is, jump to a specified memory address to fetch instructions for execution. The operation of the computer is supported by such high-speed and repeated simple operations of the CPU.


So the general process of assembly instructions is as follows: the programmer writes assembly instructions, which are compiled into machine code, the machine code is stored in memory, the CPU reads and writes memory, and the CPU executes the machine code (assembly instruction).


As shown in the figure, the CPU registers:

insert image description here

2. Register knowledge

Parameters are passed between subroutines through registers r0~r3

lr originally holds the return address of the subroutine. When the value of lr is stored in the data stack, lr can have other uses.

sp is the data stack pointer. It must be the same when entering and exiting a subroutine. sp always points to the top of the stack.

PC is used as a program counter and cannot be used for other purposes

ip is the scratch register called inside the subroutine


3. Code Analysis

The assembly instructions of the boot code have two functions: setting up the stack and booting the function


Setting up the stack is to use the sp (Stack Pointer) stack pointer, which is the stack top pointer. sp always points to the top of the stack, and the memory space where the program runs is in the allocated stack space.


The boot function is to guide ARM to the memory space where the function written in C language is stored to execute the function written in C language (in the form of machine code in memory). The boot uses the jump command bl, which can make ARM jump to the specified memory address and copy the address of the next instruction to the lr register so that after calling the function, it can return to the calling point and continue to execute the next instruction. You can use the mov pc, lr instruction to return to the next instruction before the call and continue execution.


1. Instruction analysis

The following is a detailed analysis of each assembly instruction:


First instruction:

mov ip, sp


It saves the current stack top pointer sp to ip.


Second instruction:

stmdb sp!, {fp, ip, lr, pc}


First, sp is shifted down 4Byte from the top of the stack of 4096 (db: shift first, then store), and then the current value of the pc register is stored in the memory address of 4092. Note that the address of the current instruction is 0X10, so the current value of pc is: 0X18 (ARM pipeline execution instruction);


Then, sp moves down 4 bytes from the memory address of 4092, and stores the value of the lr register at the memory address of 4088. The lr register stores the scene when the assembly instruction calls the main function, that is, the instruction at memory address 8.


Then, sp moves down 4 bytes from the memory address of 4088, and stores the value of the ip register at the memory address of 4084. The ip register stores the original stack top pointer address, which is 4096.


Then, sp moves down 4 bytes from the memory address of 4084, and stores the value of the fp register at the memory address of 4080.


Finally, the memory address pointed to by sp is the address that was modified last, which is 4080.


The stack situation at this time is as shown below:

insert image description here

The third instruction:

sub fp, ip, #4


Put the result of ip-4Byte into fp, which is the memory address of 4096-4=4092 (ip stores the original stack top pointer pointing to 4096), and fp points to the memory address of 4092.


Fourth directive:

sub sp, sp, #8


Here, the memory address pointed to by sp is reduced by 8 bytes, that is, 8 bytes of space are freed up after the memory address storing the four register information, which is exactly the space for two instructions, leaving space for storing two local variables later.


The fifth and sixth instructions:

mov r3, #1442840576 ; 0x56000000

add r3, r3, #80 ; 0x50

str r3, [fp, #-16]


Store 0X56000050 into the r3 register.


The seventh directive:

str r3, [fp, #-16]


Write the content of r3, that is, the local variable 0X56000050, to the address of fp-16Byte. The memory address of fp-16Byte is exactly the memory address immediately following the first four registers. It can be seen that the local variables of the calling function are stored after these four basic information registers.


The eighth, ninth and tenth instructions:

mov r3, #1442840576 ; 0x56000000

add r3, r3, #84 ; 0x54

str r3, [fp, #-20]


These three instructions have the same meaning as the three instructions above, which are to store local variables in memory, and store 0X56000054 right below 0X56000050. Note that the sp stack pointer is next to the local variable.


At this time, the stack situation is as shown below:

insert image description here

Eleventh Directive:

ldr r2, [fp, #-16]


Read 0X56000050 into the r2 register.


Article 12:

mov r3, #256 ;0X100


Write 0X100 to the r3 register.


Article 13:

str r3, [r2]


Write 0X100 to the memory space at address 0X56000050, that is, write it to the GPFCON register, and configure the GPF4 pin mode to output.


Instructions 14, 15 and 16:

ldr r2, [fp, #-20]

mov r3, #0 ; 0x0

str r3, [r2]


Same as above, write 0 into the GPFDACT register, light up the LED corresponding to GPF4, and the GPF4 pin outputs a low level.


Articles 17 and 18:

mov r3, #0 ; 0x0

mov r0, r3


Clearing the r0 register is equivalent to return 0 in the main function. The r0, r1, r2, and r3 registers are used to pass parameters between subroutines.


Article 19:

sub sp, fp, #12


Re-point sp to the address of fp-12Byte, which is 4080.


Article 20:

ldmia sp, {fp, sp, pc}


Restore registers from the stack


First, sp reads 4Bytes from the current address 4080, which is the address where fp is first saved, and writes it into fp.


Then, sp moves up 4Byte from the memory address of 4080, which is 4084, reads 4Bytes, and writes to sp, which means writing the previous ip value (4096) to sp


Then, sp moves up 4Byte from the memory address of 4084, which is 4092, reads 4Bytes, and writes to pc, which is to write the previous lr value (the address of the assembly boot main) to pc, which is 8, and the program jumps back to the address of 0X8, which is the return of main.


2. Overall analysis

It can be seen that the 4K space includes: register initial values, local variables, and code segments. The 4K memory is sufficient for the program to run.


Let's make a brief summary:


The code segment will be saved behind the stack


The beginning of the stack saves the original registers: pc, lr, ip, fp, to ensure that the function can be returned to the original position after calling


The value of sp must be the same when entering and exiting the subroutine.

[1] [2]
Keywords:ARM Reference address:Understand the internal mechanism of ARM running C program in three minutes

Previous article:S3C2440—4. Clock system
Next article:Detailed explanation of embedded ARM assembly

Latest Microcontroller Articles
Change More Related Popular Components

EEWorld
subscription
account

EEWorld
service
account

Automotive
development
circle

About Us Customer Service Contact Information Datasheet Sitemap LatestNews


Room 1530, 15th Floor, Building B, No.18 Zhongguancun Street, Haidian District, Beijing, Postal Code: 100190 China Telephone: 008610 8235 0740

Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved 京ICP证060456号 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号