阅读一下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 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段指定位置。
阅读(2810) | 评论(0) | 转发(2) |