CRT0.S代码分析
<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" />jupiter 2006.08.20
.global main // int main(void)
.global _etext // -> .data initial values in ROM
.global _data // -> .data area in RAM
.global _edata // end of .data area
.global __bss_start // -> .bss area in RAM
.global __bss_end__ // end of .bss area
.global _stack // top of stack
.global symbol
在GNU AS中.global使ld能够看见这个符号。如果你在程序的某个部分中定义了symbol,那么在与它一起链接的其它程序中,这个符号也是可见的。
在这部分中定义了几个符号,其中包括main,这是我们所写的C程序的入口;_etext,即end of text,text段的末尾也就是data段的开始,从此处开始是.data段,.data段存放的是需要被初始化的数据,例如那些带有初始化值的全局变量;_data和_edata指明了数据段的起始和结束位置;同理__bss_start和__bss_end__指明了bss段的起始和结束位置;_stack指明了栈顶,对于ARM处理器来说,栈一般是满递减的,即full descending。
/************
在这里我简单的介绍一下程序的装载与执行过程。程序一般可以分成text、data和bss段,我们必须把程序放在非易失性存储器(NVM)中,试着想一下,如果你把程序放在RAM中,那么系统掉电之后,你还能运行你的程序吗?而每个段又有自己的装载地址和运行地址,装载地址和运行地址可能相同,也可能不同。但是对于data段来说,这两个地址肯定是不相同的,因为我们一般不能在程序运行的过程中修改NVM的内容,我们需要在程序的初始化代码中将data段的内容从它的装载地址拷贝到它的运行地址处(我们稍后就能看到这是如何做到的)。回到上面的问题,_etext就是data段在NVM中的起始地址,而_data就是data段运行时的起始地址。这里有一个常识,在可执行文件中,一般首先存放text段,紧接着是data段,最后才是bss段,当然对于bss段来说还有一个技巧以后再谈。
************/
//定义各个异常模式的堆栈大小
.set UND_STACK_SIZE, 0x00000004
.set ABT_STACK_SIZE, 0x00000004
.set FIQ_STACK_SIZE, 0x00000004
.set IRQ_STACK_SIZE, 0X00000080
.set SVC_STACK_SIZE, 0x00000004
//定义标准模式位和PSR中的中断(I & F)标志
.set MODE_USR, 0x10 // User Mode
.set MODE_FIQ, 0x11 // FIQ Mode
.set MODE_IRQ, 0x12 // IRQ Mode
.set MODE_SVC, 0x13 // Supervisor Mode
.set MODE_ABT, 0x17 // Abort Mode
.set MODE_UND, 0x1B // Undefined Mode
.set MODE_SYS, 0x<?xml:namespace prefix = st1 ns = "urn:schemas-microsoft-com:office:smarttags" />1F // System Mode
.set symbol, expression
设置symbol的值为expression。这会改变symbol的值和类型以与expression保持一致。如果symbol具有external标志,那么此标志保持不变。可以在汇编语句中多次.set一个符号。如果你.set一个全局符号,那么保存在目标文件中的将是最后一次设置的值。
.equ I_BIT, 0x80 // when I bit is set, IRQ is disabled
.equ F_BIT, 0x40 // when F bit is set, FIQ is disabled
.equ symbol, expression
.equ的用法和作用与.set相同。
.text
.code 32
.align 2
.global _boot
.func _boot
_boot:
// Runtime Interrupt Vectors
// -------------------------
Vectors:
b _start // reset - _start
ldr pc,_undf // undefined - _undf
ldr pc,_swi // SWI - _swi
ldr pc,_pabt // program abort - _pabt
ldr pc,_dabt // data abort - _dabt
nop // reserved
ldr pc,[pc,#-0xFF0] // IRQ - read the VIC
ldr pc,_fiq // FIQ - _fiq
#if 0
// Use this group for production
_undf: .word _reset // undefined - _reset
_swi: .word _reset // SWI - _reset
_pabt: .word _reset // program abort - _reset
_dabt: .word _reset // data abort - _reset
_irq: .word _reset // IRQ - _reset
_fiq: .word _reset // FIQ - _reset
#else
// Use this group for development
_undf: .word __undf // undefined
_swi: .word __swi // SWI
_pabt: .word __pabt // program abort
_dabt: .word __dabt // data abort
_irq: .word __irq // IRQ
_fiq: .word __fiq // FIQ
__undf: b . // undefined
__swi: b . // SWI
__pabt: b . // program abort
__dabt: b . // data abort
__irq: b . // IRQ
__fiq: b . // FIQ
#endif
.size _boot, . - _boot
.endfunc
这个部分定义了异常向量表。
.func name [,label]
.func为函数name添加调试信息,但是除非在汇编的时候使能调试信息否则这些信息被忽略。目前仅支持—gstabs选项。Label是函数的入口点,如果省略,那么必须给name添加一个前缀字符,这个字符通常是一个下划线(_)或者为空,这依赖于目标平台。目前所有的函数的返回类型都被定义为void。函数必须以.endfunc结束。
编译器生成.size这个命令符是为了在符号表中包含辅助调试信息。它仅在.def/.endef对中才是允许的。.size仅仅在生成COFF格式时才有意义;当as生成b.out时,虽然它可以接收这个命令符,但只是忽略它。(这里我们可以认为它不存在)
// Setup the operating mode & stack.
// ---------------------------------
.global _start, start, _mainCRTStartup
.func _start
_start:
start:
_mainCRTStartup:
// Initialize Interrupt System
// - Set stack location for each mode
// - Leave in System Mode with Interrupts Disabled
// -----------------------------------------------
ldr r0,=_stack
msr CPSR_c,#MODE_UND|I_BIT|F_BIT // Undefined Instruction Mode
mov sp,r0
sub r0,r0,#UND_STACK_SIZE
msr CPSR_c,#MODE_ABT|I_BIT|F_BIT // Abort Mode
mov sp,r0
sub r0,r0,#ABT_STACK_SIZE
msr CPSR_c,#MODE_FIQ|I_BIT|F_BIT // FIQ Mode
mov sp,r0
sub r0,r0,#FIQ_STACK_SIZE
msr CPSR_c,#MODE_IRQ|I_BIT|F_BIT // IRQ Mode
mov sp,r0
sub r0,r0,#IRQ_STACK_SIZE
msr CPSR_c,#MODE_SVC|I_BIT|F_BIT // Supervisor Mode
mov sp,r0
sub r0,r0,#SVC_STACK_SIZE
msr CPSR_c,#MODE_SYS|I_BIT|F_BIT // System Mode
mov sp,r0
这一步主要是设置各个异常模式的堆栈,注意在设置的时候需要禁止IRQ和FIQ。这段代码也是系统复位后执行的第一段代码。执行完这段代码后系统处于系统模式,并且IRQ和FIQ都是禁止的。
// Copy initialized data to its execution address in RAM
// -----------------------------------------------------
#ifdef ROM_RUN
ldr r1,=_etext // -> ROM data start
ldr r2,=_data // -> data start
ldr r3,=_edata // -> end of data
1: cmp r2,r3 // check if data to move
ldrlo r0,[r1],#4 // copy it
strlo r0,[r2],#4
blo 1b // loop until done
#endif
// Clear .bss
// ----------
mov r0,#0 // get a zero
ldr r1,=__bss_start // -> bss start
ldr r2,=__bss_end__ // -> bss end
2: cmp r1,r2 // check if data to clear
strlo r0,[r1],#4 // clear 4 bytes
blo 2b // loop until done
这两段代码主要完成data段的搬运和bss段的清零。
// Call main program: main(0)
// --------------------------
mov r0,#0 // no arguments (argc = 0)
mov r1,r0
mov r2,r0
mov fp,r0 // null frame pointer
mov r7,r0 // null frame pointer for thumb
ldr r10,=main
mov lr,pc
bx r10 // enter main()
.size _start, . - _start
.endfunc
这段代码完成到C语言main函数的跳转。
.global _reset, reset, exit, abort
.func _reset
_reset:
reset:
exit:
abort:
#if 0
// Disable interrupts, then force a hardware reset by driving P23 low
// -------------------------------------------------------------------
mrs r0,cpsr // get PSR
orr r0,r0,#I_BIT|F_BIT // disable IRQ and FIQ
msr cpsr,r0 // set up status register
ldr r1,=(PS_BASE) // PS Base Address
ldr r0,=(PS_PIO) // PIO Module
str r0,[r1,#PS_PCER_OFF] // enable its clock
ldr r1,=(PIO_BASE) // PIO Base Address
ldr r0,=(1<<23) // P23
str r0,[r1,#PIO_PER_OFF] // make sure pin is contolled by PIO
str r0,[r1,#PIO_CODR_OFF] // set the pin low
str r0,[r1,#PIO_OER_OFF] // make it an output
#endif
b . // loop until reset
.size _reset, . - _reset
.endfunc
.end
文章评论(0条评论)
登录后参与讨论