u-boot之ldr pc _start_armboot解析以及relocation(重定位)问题

8 篇文章 0 订阅
1 篇文章 0 订阅
在cpu/arm920t/start.S中,将text relocate 到Ram后,其代码段的最后1行有条语句:
ldr pc _start_armboot
_start_armboot: .word start_armboot
    start_armboot是一个函数指针,这个symbol对应了符号表里的函数地址,这个函数是一个C语言的函数,他就是u-boot的stage2的入口点,这个stage2应该是在RAM里面执行的。问题就来了,既然我们只是手动将text relocate到了RAM里面,此时FLASH和RAM里面都有start_armboot的代码,为什么程序就要跳转到RAM里,而不是依然在FLASH里?
    我最初感觉除非是ld在连接的时候,修改了符号表里的内容,指定start_armboot符号的地址是RAM里的地址,这样只要我们执行时取其地址,取到的肯定是RAM中的地址。
    经查阅资料,有如下一段解释:
转自: http://www.mail-archive.com/u-boot-users@lists.sourceforge.net/msg04018.html
----- Original Message ----
> > > > From: Vishal Oliyil Kunnil <[EMAIL PROTECTED]>
> > > > To: Tiju <[EMAIL PROTECTED]>
> > > >
> > > > Sent: Monday, 31 March, 2008 2:58:12 PM
> > > > Subject: Re: [U-Boot-Users] s3c2440 -- serial_init
> > > >
> > > > TEXT_BASE is the address for which u-boot is linked for.If you take an
> > > > objdump of u-boot
> > > > elf, you will see that it links for address beginning with that
> > > > specified by TEXT_BASE.
> > > > Meaning, you link for the address thus specified.
> > > > Typically the binary will be run from the reset vector of the
> > > > processor, which is not necessarily
> > > > TEXT_BASE : say 0x0 flash address. U-boot starts executing from the
> > > > reset vector,
> > > > relocates to RAM and since it is linked for TEXT_BASE, the ldr pc,
> > > > _start_armboot
> > > > will branch to the start_armboot which is in RAM.
> > > > -------snip - start.S --------------
> > > > ldr pc, _start_armboot
> > > > _start_armboot: .word start_armboot
> > > > -------snip - start.S --------------
> > > > Regards,
> > > > Vishal    上面的意思是说,我们在u-boot的C代码的编译环境里制定了TEXT_BASE的值,然后所有生成的C函数的可执行代码都是以TEXT_BASE作为连接基地址,而不是我们的0x00000000(u-boot在FLASH开始执行的起始地址),也就是说这是我们u-boot的代码中在FLASH里,除了最开始的一部分汇编码是在FLASH里执行的,其他由C语言实现的部分都只能在RAM里执行,因为我们给他们定好的基地址就是RAM的地址。
    这其实是个重定位问题,见另一篇文章:
http://blog.chinaunix.net/u2/72383/showart.php?id=1072523
关于u-boot的relocation(重定位)问题
阅读一下ld相关的资料就可以知道,通过培植lds连接脚本或者在连接时提供相应的参数,可以控制连接生成的代码的起始地址,这里的起始地址有2个:
    1. VMA
    2. LMA
    其中,VMA可以认为是程序运行时,认为自己的起始地址,就是他的所有的symbol的地址的基址,即如果访问一个symbol,那么这个symbol的地址应该是VMA+symbol在代码内的偏移地址。
    而LMA可以认为是将程序装载的地址,一般情况下,2者是相同的,但是在u-boot这种需要relocatin的情况下,2这就是不同的,貌似在u-boot中,LMA压根没什么意义。
    为什么这么说,因为在连接的时候,LDFLAGS中有-Ttext $(TEXT_BASE),其含义是将text section定位基址设置成TEXT_BASE,这样生成的结果里的诸如函数地址,等symbol对应的地址都是以TEXT_BASE作为基地址的。这个据说可以通过objdump查看生成的u-boot的各条汇编代码的地址看出来。但是为什么我们还可以将u-boot烧至到flash去执行呢?这是因为u-boot的stage1的汇编代码里虽然用到了一些label,但是他们在真正使用的时候其实是采用的偏移地址跳转,因此无论烧到哪里都能执行。但是还有个问题,那么stage1跳转到stage2时有条指令:
    ldr pc _start_armboot
    _start_armboot: .word start_armboot
    这个跳转为什么可以呢,首先,start_armboot是一个函数指针,他的地址是定义在由相应的c文件生成的目标文件中的,在连接的时候,他的对应的地址受到我们指定的TEXT_BASE的影响,它的地址其实是在RAM中,因此,如果我们执行这条语句,CPU会到RAM里去寻找start_armboot的代码,因此,可以这样说,如果我们不把代码拷贝到RAM里的话,当执行到这里的时候是会出错的,CPU不会去执行位于flash中的start_armboot函数的代码,start_armboot并不指向哪里。

    如果细心的话,还会看到在 /common/hush.c 文件里,有段代码:

    static struct reserved_combo reserved_list[] = {
    { "if",    RES_IF,    FLAG_THEN | FLAG_START },
    { "then",  RES_THEN,  FLAG_ELIF | FLAG_ELSE | FLAG_FI },
    { "elif",  RES_ELIF,  FLAG_THEN },
    { "else",  RES_ELSE,  FLAG_FI   },
    { "fi",    RES_FI,    FLAG_END  },
    { "for",   RES_FOR,   FLAG_IN   | FLAG_START },
    { "while", RES_WHILE, FLAG_DO   | FLAG_START },
    { "until", RES_UNTIL, FLAG_DO   | FLAG_START },
    { "in",    RES_IN,    FLAG_DO   },
    { "do",    RES_DO,    FLAG_DONE },
    { "done",  RES_DONE,  FLAG_END  }
};

static void u_boot_hush_reloc(void)
{
    unsigned long addr;
    struct reserved_combo *r;

    for (r=reserved_list; r<reserved_list+NRES; r++) {
        addr = (ulong) (r->literal) + gd->reloc_off;
        r->literal = (char *)addr;
    }
}

    可见,reserved_list中的数据应该是属于data段的,我们已经把代码relocation到RAM里面了,况且连接的时候指定了VMA,为什么这里还要修改r->literal呢?首先,我们的确已经把DATA段relocation到了RAM里面,查看board/smdk2410/u-boot.lds可以知道,data段位于text段和bss段之间,而拷贝的时候是将text段到bss段之间(包括text段)的内容全都拷贝到了RAM,可有一点,在连接的时候,我们用-Ttext $(TEXT_BASE)只是指定了连接得到的代码,指令段的起始地址设置为RAM里地址TEXT_BASE,而data段我们并没有管她,造成的结果就是数据段的地址还是flash里的地址。所以现在我们要修改我们这些自身已经位于RAM中的指针变量,让它们的值指向RAM里而不再是flash里。
    gd->reloc_off, gd是一个乘放全局变量的一个好地方。gd->reloc_off应该是在relocation时记录的relocation偏移。

    可见,代码,symbol的地址我们不用操心,因为ld自动给我们改了,而数据段没有改,我们就的自己改。可是为什么不连接期间也手动指定数据段的起始地址?因为我们肯定知道代码段的地址是TEXT_BASE,但是我们压根就不知道代码段的长度,以及一些其他的段的长度,因此就不能确定data段在代码段后面的位置,因此也就不能在连接的时候给data段指定位置。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值