Chinaunix首页 | 论坛 | 博客
  • 博客访问: 988374
  • 博文数量: 200
  • 博客积分: 5011
  • 博客等级: 大校
  • 技术积分: 2479
  • 用 户 组: 普通用户
  • 注册时间: 2008-06-27 15:07
文章分类

全部博文(200)

文章存档

2009年(12)

2008年(190)

我的朋友

分类: LINUX

2008-07-04 10:43:10

    阅读一下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段指定位置。



阅读(2806) | 评论(0) | 转发(2) |
给主人留下些什么吧!~~