S3C2440 lds链接脚本解析

1.  SECTIONS到底意味着什么

    在一个裸版程序里面含有*.lds文件,而lds文件意味着如果你的程序烧录在nandflash,那在nandflash的内存将根据lds文件指定偏移来分布,下面从不同场景来解释SECTIONS的内容。


2.  小于4K程序

    若程序小于4K,那程序的整个重定位地址设置为0,可以没有拷贝到sdram的处理。

    uart.lds

SECTIONS {
    . = 0x00000000;
    .text          :   { *(.text) }
    .rodata ALIGN(4) : {*(.rodata)} 
    .data ALIGN(4) : { *(.data) }
    .bss ALIGN(4)  : { *(.bss)  *(COMMON) }
}

    head.S

.text
.global _start
_start:
            ldr     r0, =0x53000000     @ WATCHDOG寄存器地址
            mov     r1, #0x0                     
            str   r1, [r0]              @ 写入0,禁止WATCHDOG,否则CPU会不断重启
            
            ldr     sp, =1024*4         @ 设置堆栈,注意:不能大于4k, 因为现在可用的内存只有4K
                                        @ nand flash中的代码在复位后会移到内部ram中,此ram只有4K
            bl      main                @ 调用C程序中的main函数
halt_loop:
            b       halt_loop

    Nandflash其内存结构如下:


    一开始运行之前,默认会从NandFlash拷贝4K大小的程序代码到芯片片内内存上面并且从0开始执行,由于片内内存只有4K,所以链接脚本只适用于程序运行空间小于4K的情况。


3. 大于4K程序

    若要支持程序大于4K,就需要考虑将程序拷贝到sdram中运行,修改head.s文件,添加从片内内存拷贝到Sdram的操作,同时链接脚本设置重定位地址为0x30000000。

   uart.lds

SECTIONS {
    . = 0x30000000;
    .text          :   { *(.text) }
    .rodata ALIGN(4) : {*(.rodata)} 
    .data ALIGN(4) : { *(.data) }
    .bss ALIGN(4)  : { *(.bss)  *(COMMON) }
}

    head.S

.extern     main
.text 
.global _start 
_start:
Reset:                  
    ldr sp, =4096          
    bl  disable_watch_dog  
    bl  clock_init         
    bl  memsetup           
    bl  nand_init

    ldr r0, =0x30000000    
    mov r1, #0         
    mov r2, #(1024*10)     
    bl  nand_read          
    ldr pc, =on_sdram
onsdram:
    ldr sp, =0x34000000    
    ldr lr, =halt_loop     
    ldr pc, =main          
halt_loop:
    b   halt_loop

    此时NanFlash和Sdram内存分布如下:


    当重定位0x30000000后,其内存的指令码后会发生什么变化呢?以main函数里面,获取全局变量值为例,

    重定位0x00的时候:

 690:	e59f2224 	ldr	r2, [pc, #548]	; 8bc <main+0x234>
 8bc:	000009d4 	ldrdeq	r0, [r0], -r4
 9d4:	00000000 	andeq	r0, r0, r0

    重定位0x30000000的时候:

30000690:	e59f2224 	ldr	r2, [pc, #548]	; 300008bc <main+0x234>
300008bc:	300009d4 	ldrdcc	r0, [r0], -r4
300009d4:	00000000 	andeq	r0, r0, r0

    三句汇编代码其作用是获取全局变量的值,将其放入寄存器r2中。可以看到第一条汇编指令码和第三条汇编指令码没有变化,重点分析第二条汇编指令码,第二条汇编指令码实际上是第一条汇编寄存器寻址偏移, [pc, #548]的值需要找8bc或300008bc偏移的汇编指令,而其汇编指令又指向0x9d4或0x300009d4偏移,最终获取在其偏移的内容。由此推想若重定位到0x00,当执行到main函数时,会导致执行地址还在片内范围,假设9d4大于4K,则寻址大于片内内存4K,程序就会溢出。所以链接脚本需要设置重定位地址为0x30000000。


5. 大于4K程序并支持中断

    当需要支持中断时,head.S需要加上中断向量表,如下:

@******************************************************************************
@ File锟斤拷head.S
@ 锟斤拷锟杰o拷锟斤拷锟斤拷SDRAM锟斤拷锟斤拷锟斤拷锟斤拷锟狡碉拷SDRAM锟斤拷然锟斤拷锟斤拷锟斤拷SDRAM锟斤拷锟斤拷执锟斤拷
@******************************************************************************       
   
.extern     main
.text 
.global _start 
_start:
@******************************************************************************
@ 中断向量,本程序中,除Reset和HandleIRQ外,其它异常都没有使用
@******************************************************************************
 b   Reset

@ 0x04: 未定义指令中止模式的向量地?
HandleUndef:
	b   HandleUndef

@ 0x08: 管理模式的向量地址,通过SWI指令进入此模式
HandleSWI:
	b   HandleSWI

@ 0x0c: 指令预取终止导致的异常的向量地址
HandlePrefetchAbort:
	b   HandlePrefetchAbort

@ 0x10: 数据访问终止导致的异常的向量地址
HandleDataAbort:
	b   HandleDataAbort

@ 0x14: 保留
HandleNotUsed:
	b   HandleNotUsed

@ 0x18: 中断模式的向量地址
	b   HandleIRQ

@ 0x1c: 快中断模式的向量地址
HandleFIQ:
	b   HandleFIQ

Reset:                  
    ldr sp, =4096           @ 锟斤拷锟斤拷栈指锟诫,锟斤拷锟铰讹拷锟斤拷C锟斤拷锟斤拷锟斤拷锟斤拷锟斤拷前锟斤拷要锟斤拷锟秸�
    bl  disable_watch_dog   @ 锟截憋拷WATCHDOG锟斤拷锟斤拷锟斤拷CPU锟结不锟斤拷锟斤拷锟斤拷
    bl  clock_init          @ 锟斤拷锟斤拷MPLL锟斤拷锟侥憋拷FCLK锟斤拷HCLK锟斤拷PCLK
    bl  memsetup            @ 锟斤拷锟矫存储锟斤拷锟斤拷锟斤拷锟斤拷使锟斤拷SDRAM
    bl  nand_init

	ldr r0, =0x30000000     @1. 鐩爣鍦板潃=0x30000000锛岃繖鏄疭DRAM鐨勮捣濮嬪湴鍧�
    mov r1, #4096           	@2. 婧愬湴鍧�   = 4096锛岃繛鎺ョ殑鏃跺�欙紝main.c涓殑浠g爜閮藉瓨鍦∟AND Flash鍦板潃4096寮�濮嬪
    mov r2, #(1024*20)      @3. 澶嶅埗闀垮害= 1024(bytes)锛屽浜庢湰瀹為獙鐨刴ain.c锛岃繖鏄冻澶熶簡
    bl  nand_read           @璋冪敤C鍑芥暟nand_read_ll
    ldr pc, =on_sdram                   @ 跳到SDRAM中继续执行
on_sdram:
    msr cpsr_c, #0xd2    	@ 进入中断模式
    ldr sp, =4096           @ 设置中断模式栈指针
    msr cpsr_c, #0xdf       @ 进入系统模式
    ldr sp, =0x34000000     @ 锟斤拷锟斤拷栈指锟斤拷
    msr cpsr_c, #0x5f       @ 设置I-bit=0,开IRQ中断

    ldr lr, =halt_loop      @ 锟斤拷锟矫凤拷锟截碉拷址
    ldr pc, =main           @ 锟斤拷锟斤拷main锟斤拷锟斤拷
halt_loop:
    b   halt_loop

HandleIRQ:
 	 sub lr, lr, #4                  		@ 计算返回地址
	 stmdb   sp!,    { r0-r12,lr }   	@ 保存使用到的寄存器
	 							@ 注意,此时的sp是中断模式的sp
	 							@ 初始值是上面设置的4096
	 ldr lr, =int_return             		@ 设置调用ISR即EINT_Handle函数后的返回地址
	 ldr pc, =Uart0_Handle 			@ 调用中断服务函数,在interrupt.c中

 int_return:
 	 ldmia   sp!,    { r0-r12,pc }^  	@ 中断返回, ^表示将spsr的值复制到cpsr

  此时需要修改链接脚本,若延续上述写法,如下:

SECTIONS {
    . = 0x30000000;
    .text          :   { *(.text) }
    .rodata ALIGN(4) : {*(.rodata)} 
    .data ALIGN(4) : { *(.data) }
    .bss ALIGN(4)  : { *(.bss)  *(COMMON) }
}

   则会有严重的问题,这时看head.S生成的汇编指令

Disassembly of section .text:

30000000 <_start>:
30000000:	ea000006 	b	30000020 <Reset>

    第一条指令码,直接程序跑飞,因为这条指令码ea000006表示跳转到30000020执行,而30000020位于Sdram,还并没有初始化,所以链接脚本应该这样写:

SECTIONS {
    . = 0x00000000;
    .init : AT(0){ head.o init.o nand.o} 
    . = 0x30000000;
    .text ALIGN(4) : AT(4096) { *(.text) } 
    .rodata ALIGN(4) :  {*(.rodata)}
    .data ALIGN(4)   :  { *(.data) }
    __bss_start = .;
    .bss ALIGN(4)  : { *(.bss)  *(COMMON) }
    __bss_end = .;
}

    可以看到main函数的重定位也以0x30000000为基准。当执行到main函数时,此时能保证在sdram里执行,并且也能保证取数据段的时候,能得到偏移哪怕大于4K也能正确的重定位到,看如下main汇编指令码:

300025a4 <main>:
300025a4:	e92d4080 	push	{r7, lr}
300025a8:	e24dd080 	sub	sp, sp, #128	; 0x80
300025ac:	e3a0207d 	mov	r2, #125	; 0x7d

    此时看nandflash和sdram的内存分布如下:

    

    

    


  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值