最经典的搬运代码分析

来源:互联网 发布:加人软件 编辑:程序博客网 时间:2024/05/16 05:43

由ADS生成的代码的标号是以设置的RO为基准的,也就是说CopyProcBeg是RAM中的一个地址 。(这解释无敌了)boot烧在0位置 整个代码绝对的起始位置(绝对地址)都是以RO开始的 即entry位置开始,所有偏移都是相对于它的,而开始boot烧在0X0处却可运行是因为,搬运代码函数之前代码与实际用不到太多有实际作用的地址,可以用普通偏移解决,而JLINK仿真开始为止和设定位置相等这个设置对代码烧入FLASH以后的分布没有关系。只是把code和data分段了一下.

3楼:>>参与讨论sklar
呵呵,进来的都发表下看法吧
   知道的说下,不清楚的也把自己的看法说说吧,大家交流啊,楼上的用什么ARM板?我用的是立YU泰的44B0

4楼:>>参与讨论luhuaren
re
RO Base设置为0xc000000 RW Base设置为0xc5f0000,可是BIOS不是要放0X0000000的吗

注意:如果是用44B0,那么44B0上电复位后PC的值是0,
    AREA    Init,CODE,READONLY

    ENTRY
ResetEntry
    b ResetHandler                ;for debug
    b HandlerUndef                ;handlerUndef
    b HandlerSWI                ;SWI interrupt handler
    b HandlerPabort                ;handlerPAbort
    b HandlerDabort                ;handlerDAbort
    b .                            ;handlerReserved
    b HandlerIRQ
    b HandlerFIQ

系统上电后第一条指令执行的是b ResetHandler    (在0地址处)这是一条相对跳转指令,也就是说,这条指令和RO的设置无关
跳转到ResetHandler处,进行一些初始化动作后,就要进行代码拷贝,就是把FLASH里的代码拷贝到RO指定的地址处,代码拷贝是这样实现的



    ;****************************************************
    ;拷贝并粘贴 RW data/zero initialized data            *
    ;****************************************************
    adr    r0, ResetEntry        
    ldr    r1,BaseOfROM        
    cmp    r0,r1                
                                
    ldreq    r0, TopOfROM        
    beq    InitRamData            
    
    ;****************************************************
    ;计算拷贝程序在FLASH中的实际位置                       *
    ;****************************************************
    ldr    r2,=CopyProcBeg    
    sub    r1, r2, r1;r2-r1->r1
    add    r0, r0, r1    
    ldr    r3,=CopyProcEnd
    
    ;****************************************************
    ;将拷贝程序复制到ram中                                *
    ;****************************************************
0    
    ldmia    r0!, {r4-r7}        
    stmia    r2!, {r4-r7}        
    cmp    r2, r3
    bcc    %B0    
    
    ;********************************************************
    ;开始用ram中的拷贝程序复本将所有剩下的代码复制到ram中    *
    ;********************************************************
    ldr    r3, TopOfROM        
    ;ldr    pc, =CopyProcBeg    ;装入绝对地址(指到RAM中去的)
    b    CopyProcBeg
    ;********************************************************
    ;本段将代码由实际烧入的地址拷贝到ro-base所指定的位置    *
    ;只拷贝CopyProcEnd以后的代码  
              ;********************************************************
CopyProcBeg    
0    
    ldmia    r0!, {r4-r11}
    stmia    r2!, {r4-r11}
    cmp    r2, r3
    bcc    %B0    
CopyProcEnd
    
    sub    r1, r2, r3
    sub    r0, r0, r1        
    
InitRamData    
    ldr    r2, BaseOfBSS
    ldr    r3, BaseOfZero    
0
    cmp    r2, r3
    ldrcc    r1, [r0], #4
    strcc    r1, [r2], #4
    bcc    %B0    

    mov    r0,    #0
    ldr    r3,    EndOfBSS
1    
    cmp    r2,    r3
    strcc    r0, [r2], #4
    bcc    %B1            
;===================================================================
    adr    r0, ResetEntry        
    ldr    r1,BaseOfROM        
    cmp    r0,r1    
请注意这几条语句,尤其是adr    r0, ResetEntry,这条语句当你反汇编后发现是一条这样的指令
;[0xe24f0f5d]   sub      r0,pc,#0x174   ; #0xc008000
     也是一条与位置无关的指令,ResetEntry的值同PC的值相关联,
当然,PC一上电的值为0,所以这里ResetEntry的值也为0,所以
    ldr    r1,BaseOfROM        
    cmp    r0,r1
比较的结果就是R0 != R1于是进行代码拷贝,将复制程序拷贝完后,就可以跳转到RAM中去,执
5楼:>>参与讨论luhuaren
RE
可以下在U-BOOT的源码看一下,感觉很多开发板上流行的BOOT代码都是从U-BOOT上改过来的

6楼:>>参与讨论pigjiang
有个疑问
IMPORT    |Image$$RO$$Base|    ; ROM code start    
IMPORT    |Image$$RO$$Limit|    ; RAM data starts after ROM program
IMPORT    |Image$$RW$$Base|    ; Pre-initialised variables
IMPORT    |Image$$ZI$$Base|    ; uninitialised variables
IMPORT    |Image$$ZI$$Limit|    ; End of variable RAM space
===============================================
这几行的||里面的标号是从哪里获得的?
他们是不是就是设置linker的OUTPUT选项里面填入的RO和RW的值?

7楼:>>参与讨论wowow
RO/RW
RO/RW地址在debug/release设置不同的值,一个用于仿真器加载到ram运行,一个用于FLASH中运行。如果你用了中断,并且ARM不支持REMAP,调ram程序时FLASH里必须也烧进中断向量表。


|Image$$RO$$Base| 之类是linker生成的,但有些情况下不生成。最好自己读一读关于linker的pdf

8楼:>>参与讨论sklar
to  luhuaren
   你的这些代码我在44Binit.S文件里没有,不过有点懂你说的意思了,ADS里设置的这些地址就是把代码考到SDRAM里的地址?
    adr    r0, ResetEntry        
    ldr    r1,BaseOfROM        
    cmp    r0,r1      //??这里没怎么没说跳到哪里?

9楼:>>参与讨论luhuaren
re
当然,PC一上电的值为0,所以这里ResetEntry的值也为0;
不好意思,这句话写的不对..........ResetEntry的值得应该是|Image$$RO$$Base|

如果RO的值和你程序烧录起始地址的值不同,(如:你设置的RO=0xc000000,注意这个地址是在指向RAM,而你烧录的代码是在0地址处),这样就需要你烧录在0地址开始处的一小部分启动代码把程序从烧录地址搬运到RO设置的地址中去,但要注意,连接器为你连接的地址是以RO为基准的,这样你可能会有个疑问,为什么程序的运行域是以RO(0xc000000)为基准,但处于FLASH(0地址开始)里的一小部分启动代码还能运行呢,这就是因为程序中用了与地址无关的指令,使得一上电,能够顺利的执行0地址处的启动代码
注意
ENTRY
ResetEntry
    b ResetHandler                ;for debug
这条指令并不是跳转到RO处,它还是跳到了FLASH里,
再注意以下几句
adr    r0, ResetEntry        这里并不是把真正的ResetEntry的值装入了R0,其实装入R0的值是0,可以看看反汇编,你自然明白        
    ldr    r1,BaseOfROM        
    cmp    r0,r1    
想必应该可以理解了,如果再不理解,那建议多看看书.

10楼:>>参与讨论sklar
收了
    恩,还得多看了!

11楼:>>参与讨论时宗
参考一下CPU datasheet
以2410/2440为例:SDRAM 一般用的是CPU的BANK6和BANK7(各64M),从BANK0到BANK6正好是0X30000000也就是所谓的0地址ResetEntry。

12楼:>>参与讨论luhuaren
RE
 没搞懂楼上的说的意思。

13楼:>>参与讨论sklar
11楼可以详细点吗
   呵呵,11楼说的我也不明白!
期待详细点!

14楼:>>参与讨论pigjiang
to luhuaren还有个疑问
    ;****************************************************
    ;计算拷贝程序在FLASH中的实际位置                       *
    ;****************************************************
-->ldr    r2,=CopyProcBeg    
    sub    r1, r2, r1;r2-r1->r1
    add    r0, r0, r1    
    ldr    r3,=CopyProcEnd

如你所说,前面比较r0和r1结果自然是不等,所以直接运行到这里来了。
请问:CopyProcBeg  是44binit.s里面的一个标号,它所代表的地址到底应该是在FLASH里还是在ram里?如果在FLASH里,那么r2应该小于r1,因为r1=BaseOfROM,那么sub    r1, r2, r1这个减法是不是有问题?

主要是觉得这个时候标号CopyProcBeg  应该是在FLASH里的某一个地址。
今天看了一天这段代码也没明白,谢谢指点!

15楼:>>参与讨论luhuaren
RE
ldr    r2,=CopyProcBeg    
注意用的是LDR指令

由ADS生成的代码的标号是以设置的RO为基准的,也就是说CopyProcBeg是RAM中的一个地址 。(这解释无敌了)


但是如果你用ADR    r2,=CopyProcBeg    
这条指令就不一样了,具体怎么不一样,看看指令的解释,但很多地方只是一笔带过,没有很详细的解释,我是看了汇编后代码才明白的
它会生成一条类似下面的指令
sub      r0,pc,#0x174   ; #0xc008000

以上我理解的应该没有错误,因为我已经在板子上验证过。

在我不理解这些的时候,到网上搜索过很多资料,但是没有谁确切的解释过这些东东,后来等我弄明白了以后,在LINUX论坛上发现有讨论U-BOOT的帖子对这些做了很明确的解释。
为什么程序的运行域和加载域不同,但启动代码还能在加载域运行,并将加载域的代码拷贝到运行域中去,然后将PC指到运行域中去,在运行域中执行代码,可能是每一个初学者看启动代码时最难理解的地方。理解了这些,其他的,都相对好弄一些/

16楼:>>参与讨论pigjiang
谢谢luhuaren的解释。
还需进一步参透:)

17楼:>>参与讨论瑶瑶

可是为什么我把RObase的地址设置为0x0000时,运行程序时总是发生重启的现象的,都没有运行到我的应用程序,就连开发板中提供的ucos276版本的软件都会有这个问题, 不知道上面的大侠有没有遇到过这个问题,该如何解决?(改成0x0c008000就没有问题了)

 

18楼:>>参与讨论saga

to yaoyao

前面要留出0x8000的空间用来建立堆栈的(我觉得他说的不对,目前觉得不对,要是跑裸机RObase=0x0是没问题的(但是要注意把数据段搬到RAM中),要是操作系统定义在RObase=0x0c000000恐怕是不行)

原创粉丝点击