我们如何将Linux映像加载到内存中的适当位置

时间:2013-03-20 09:34:34

标签: linux linux-kernel bootloader bootstrapping

我们正在尝试将linux映像加载到DRAM的特定位置,DRAM结束地址是 我们从启动日志中得知的“mem device结束地址是0x80000000”的0x80000000。我们正在地址“0x5000000”加载我们的图像,并且在图像的variuos部分加载到大于“0x80000000”的某个地址之前,从引导日志中再次获得eaxmple

 loading section to address 0xc5000000 from file position 0x1000, size is 0x5ac13e

上述行中“从文件位置0x1000”的含义。

加载的第一部分是.text部分,下面是部分标题的vmlinux图像转储

[Nr]      Name           Type            Addr     Off    Size   ES Flg Lk Inf Al

[ 0]                     NULL            00000000 000000 000000 00      0   0  0

 [ 1]   .text            PROGBITS        c5000000 001000 5ac13e 00  AX  0   0 4096

 [ 2]      .notes           NOTE            c55ac140 5ad140 000168 00  AX  0   0  4

 [ 3]    __ex_table        PROGBITS        c55ac2b0 5ad2b0 000fe0 00   A  0   0  4

 [ 4]   .rodata           PROGBITS        c55ae000 5af000 20a930 00   A  0   0 64

 [ 5]   __bug_table       PROGBITS        c57b8930 7b9930 0075fc 00   A  0   0  1

 [ 6]   .pci_fixup        PROGBITS        c57bff2c 7c0f2c 001a90 00   A  0   0  4

 [ 7]    .builtin_fw       PROGBITS        c57c19bc 7c29bc 0000cc 00   A  0   0  4

它是一个非常大的列表,所以没有发布完整。但是我们可以在这里看到一件事.text部分大于DRAM结束地址,所以尽管我们在加载后没有收到任何错误,但是图像不应该被正确编码它继续加载其他部分但在此消息之后它会挂起。

    program load complete, entry point: 0x5000000, size: 0x92e7fc

我的问题是如何将这些不同的部分地址与我们的DRAM地址对齐, 这里可以使用objcopy实用程序来更改这些不同部分的地址。

有没有办法在编译之前设置这些节地址? 第二件事可能是这个Hang afer程序加载完成的原因。

3 个答案:

答案 0 :(得分:6)

from file position 0x1000表示它的含义。你在转储中有它:

[Nr]      Name           Type            Addr     Off    Size   ES Flg Lk Inf Al
...
[ 1]   .text            PROGBITS        c5000000 001000 5ac13e 00  AX  0   0 4096

这是.text部分在文件中开始的位置,偏移量为0x1000

  

但是我们可以在这里看到一件事.text部分大于DRAM结束地址

不,它不是更大的(至少不是更大的意义上的意义),它的编译期望它将被加载到地址{{1在记忆中。

  

所以图片不应该被正确编码,尽管我们在加载第一部分后没有收到任何错误它继续加载其他部分

图像可以在任何地方加载,它只是用于加载的数据。

OTOH,如果0xc5000000表示它所说的内容,那么文件就会被加载到任何地方,因为你的RAM以loading section to address 0xc5000000结束。

  

但在此消息之后它会挂起。

这是预期的。机器代码很少与位置无关,因此如果将它加载到与应该加载​​的位置不同的位置,它将无法工作。或者如果它甚至没有加载,那么你要执行什么?垃圾。

  

有没有办法在编译之前设置这些节地址?

根据系统的不同,您可以选择以下两个选项之一,或两者兼而有之:

  • 以这样的方式设置页面翻译:{{1​​}}的虚拟地址向上映射到0x7fffffff的物理地址以及整个程序的物理地址
  • 找到编译器正在使用的链接描述文件,并将初始部分地址从0xc5000000更改为0x5000000,google up,请参阅编译器/链接器文档

此外,入口点为0xc5000000有点奇怪。并不是说这一定是错的,只是很少这样。我确保0x5000000标签(或0x5000000或其他任何标签)确实收到与start部分开头相同的地址。如果出于某种原因,情况并非如此,那么链接器脚本或编译器/链接器命令行选项或加载器都会出现问题。

答案 1 :(得分:3)

你使用什么装载机? “形象”的形式是什么? U-boot图像,原始图像,vmlinux ELF文件?我想最后一个判断是否存在部分等。从ELF文件中你应该加载不是部分而是所谓的程序头。例如,这是OpenRISC linux内核程序头列表(使用readelf -l获得):

Elf file type is EXEC (Executable file)
Entry point 0xc0000000
There are 2 program headers, starting at offset 52

Program Headers:
  Type           Offset   VirtAddr   PhysAddr   FileSiz MemSiz  Flg Align
  LOAD           0x002000 0xc0000000 0x00000000 0x231728 0x232000 RWE 0x2000
  LOAD           0x234000 0xc0232000 0x00232000 0x17c78c 0x18bfcc RWE 0x2000

 Section to Segment mapping:
  Segment Sections...
   00     .text .rodata __ksymtab __ksymtab_gpl __ksymtab_strings __param __modver 
   01     .data __ex_table .head.text .init.text .init.data .bss 

查看VirtAddrPhysAddr之间的差异(由于通常的Linux内核映射而丢弃c)。当然,物理地址应该用于加载。

内核为符号,部分等使用虚拟地址的原因是,在启动过程中,您可以快速输入MMU初始化的时刻,虚拟地址是唯一有效的地址。

最后,关于更改这些地址。实际上,正如Alexey所指出的,链接器脚本是关键。您可以在arch/(your arch)/kernel/vmlinux.lds.S中找到这些内容。但关键是这是IMO不是你的问题,问题可能在于装载机或它的选择。

答案 2 :(得分:1)

检查你的arch / arm / mach-xxx / Makefile.boot(你用右臂板吗?)

  zreladdr-y        += 0x80008000
params_phys-y       := 0x80000100
initrd_phys-y       := 0x80800000

这是来自Ti omap3芯片。

我认为你需要相同的