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

5
我们正在尝试将Linux镜像加载到特定位置的DRAM中,DRAM结束地址为0x80000000,我们从引导日志中得知。我们正在地址0x5000000处加载我们的镜像,在此之前,图像中的各个部分在某些地址上进行加载,这些地址大于0x80000000,例如再次来自引导日志。
 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

这是一个相当长的列表,因此没有完整发布。但我们可以看到一件事情。文本部分大于DRAM结束地址,因此尽管在加载第一节后没有出现任何错误,但图像应该无法正确加载,而在加载其他部分后,在此消息之后它会停滞。

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

我的问题是如何将这些不同部分的地址与我们的DRAM地址对齐,是否可以使用objcopy工具来更改这些不同部分的地址。
在编译前是否有任何设置这些部分地址的方法?另外一个问题是,程序加载完成后出现挂起的原因是什么?

赏金是好的,但并不一定会给你带来解决方案,因为问题不够详细,并且我们看不到你的代码。你基本上是在要求人们盲目尝试。 - Alexey Frunze
是的,Alexey,你已经非常好地回答了我的问题,我认为获得更多关于这个问题的观点是一个好主意。我不想让别人替我解决问题,因为那样我就学不到东西了,只是想得到一些好的想法。 - Amit Singh Tomar
好的观点,实际上。 - Alexey Frunze
@AmitSinghTomar:看一下我的回答,Linux内核在不同的位置(查看您自己的Linux' vmlinux文件)具有入口点和节(程序头)是完全正常的。关键是物理地址与虚拟地址。 - konrad.kruczynski
3个回答

6
“从文件位置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结尾地址

不,它并不是大于(至少不是指更大),编译时期望在内存中的地址为0xc5000000

因此,尽管我们在加载第一部分后没有收到任何错误,但可能无法正确加载映像的其他部分。

这个映像可以在任何地方加载,它只是用于加载的数据。
另一方面,如果将部分加载到地址0xc5000000是真实的含义,那么该文件就被加载到了不存在的位置,因为您的RAM在0x7fffffff结束。

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

这是预料之中的。机器代码很少是位置无关的,因此,如果您将其加载到与其应该加载的位置不同的位置,则它将无法工作。或者如果它根本没有被加载,那么您要执行什么?垃圾。

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

根据系统,您可能有以下两个选项或两者都有:
  • 设置页面翻译,使得从0xc5000000开始的虚拟地址对应于整个程序中从0x5000000开始的物理地址。
  • 找到编译器正在使用的链接脚本,并将初始部分地址从0xc5000000更改为0x5000000,请搜索此内容,查看编译器/链接器文档。
另外,0x5000000作为入口点有些奇怪。这不是说一定是错的,只是很少见。我会确保start标签(或_start或其他任何标签)确实接收与.text节的开头相同的地址。如果由于某种原因它不是这种情况,则链接器脚本、编译器/链接器命令行选项或加载器存在问题。

感谢@Alexey的友好回复。有几件事情想和您分享,这些是我在问题中遗漏的。入口点0x5000000是我们通过更改内核的.config文件而更改的,因为无法将图像加载到默认地址0x1000000。第二件事是我们之前已经加载并执行了一个基于QNX的内核映像到同一DRAM中,现在我们是否可以在Linux内核映像中使用与QNX映像相同的部分和入口点地址?还有一个您提出的观点是入口点是.text部分的一部分。 - Amit Singh Tomar
这是一个问题吗?我不太明白你的观点。在这里用于QNX映像的部分和入口点地址,现在可以用于Linux内核映像吗? - Alexey Frunze
嗨Alesxey,正如你建议的那样,在更改页面偏移值后,我们在2 GB范围内获得了部分地址,并且在程序加载完成后,我们的映像没有挂起,但我们遇到了其他问题,很快它会显示程序加载完成,CPU正在重置而不是将控制权交给Linux内核映像,希望能听到你对此事的看法。 - Amit Singh Tomar
我不知道。但我建议你先让一个小程序跑起来,然后再回到整个操作系统,或许做增量“检查点”(例如,在内核初始化代码的每个主要步骤之前和之后在屏幕或串口上打印一些内容)。如果可能的话,也可以在带有调试器的模拟器上尝试运行。 - Alexey Frunze

2
你使用什么加载器?“image”的形式是什么?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/(您的架构)/kernel/vmlinux.lds.S 中找到它们。但问题可能不在您身上,而是在加载程序或其选项上。

1

请检查您的 arch/arm/mach-xxx/Makefile.boot 文件(您使用的是 ARM 板,对吗?)。

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

这是来自 Ti omap3 芯片。

我认为您需要相同的内容。


感谢@liyaoshi的回复,但我们使用x86板。 - Amit Singh Tomar
抱歉这个回答,我犯了一个错误。我以为0x80000000是你的板子的起始地址。 - liyaoshi

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接