英文:
Qemu doesn't load my Image file at my specified address
问题
我的链接器脚本:
SECTIONS{
. = 0xC0008000;
__text_start = .;
.text :
{
boot/start.o
*(.text)
}
__text_end = .;
.rodata ALIGN(4) : {*(.rodata*)}
.data ALIGN(4) : { *(.data) }
__bss_start = .;
.bss ALIGN(4) : { *(.bss) *(COMMON) }
__bss_end = .;
}
0xC0008000 是引导程序的地址,我想在QEMU中指定它。这是我尝试过的:
qemu-system-arm \
-machine virt \
-cpu cortex-a15 \
-nographic \
-S -s \
-m 4096M \
-kernel Image \
# -device loader,file=Image,addr=0xC0008000
它会将PC设置为0x40000000,经过几条指令后,PC会跳转到0x40010000处的引导程序。根据QEMU提供的文档,这是可以接受的,但这不是我期望的。
RAM 从 0x4000_0000 开始 https://qemu.readthedocs.io/en/latest/system/arm/virt.html
但当我将命令更改为:
qemu-system-arm \
-machine virt \
-cpu cortex-a15 \
-nographic \
-S -s \
-m 4096M \
-kernel Image \
-device loader,file=Image,addr=0xC0008000
图像被正确加载,0x40010000 处的指令似乎与 0xC0008000 处的指令重复,但它无法跳转到 0xC0008000,这使得调试变得困难。我想知道为什么PC无法跳转到我期望的位置。谢谢!
英文:
My linker script:
SECTIONS{
. = 0xC0008000;
__text_start = .;
.text :
{
boot/start.o
*(.text)
}
__text_end = .;
.rodata ALIGN(4) : {*(.rodata*)}
.data ALIGN(4) : { *(.data) }
__bss_start = .;
.bss ALIGN(4) : { *(.bss) *(COMMON) }
__bss_end = .;
}
0xC0008000 is the address of bootloader, and I want to specify it in QEMU.
Here is what I've tried. \
qemu-system-arm \
-machine virt \
-cpu cortex-a15 \
-nographic \
-S -s \
-m 4096M \
-kernel Image \
# -device loader,file=Image,addr=0xC0008000
It will set the PC at 0x40000000 and after few instructions, PC will jump to the bootloader at 0x40010000. This is acceptable according to documents offered by QEMU, but it is not what I expect to do.
>RAM starts at 0x4000_0000 https://qemu.readthedocs.io/en/latest/system/arm/virt.html
But when I change the command to
qemu-system-arm \
-machine virt \
-cpu cortex-a15 \
-nographic \
-S -s \
-m 4096M \
-kernel Image \
-device loader,file=Image,addr=0xC0008000
The image is loaded correctly, and it seems to be duplicate instructions in 0x40010000 of the instructions in 0xC0008000, but it can't jump to 0xC0008000 which makes hard to debug.
I wonder why the PC won't be able to jump to where I expect. Thanks!
答案1
得分: 1
你没有说明你的图像文件是什么格式。它是一个ELF文件,还是一个原始二进制文件?从描述的行为来看,我猜它是一个原始二进制文件。
如果你向QEMU传递了-kernel
选项,你是在说“这个文件应该以Linux内核期望的方式启动”。这意味着,除其他事项外,你不关心它在内存映射中的位置,它应该首先通过设置一些寄存器然后跳转到图像的起始位置来启动。(有一个例外:ELF文件根据ELF文件指定的位置加载,并通过跳转到ELF入口点启动。但是现在更好地使用通用加载程序加载ELF文件。)
(“像Linux内核一样启动我”具体意味着什么,请参阅内核文档的这部分。virt板始终会传递一个设备树blob,而不是ATAGs。)
如果你的图像文件不期望像Linux内核一样启动,那么你不应该使用-kernel
选项。
相反,你可以使用通用加载程序(-device loader
)。你的第二个命令行几乎正确,但有两个问题:
-
你没有删除
-kernel
选项,所以你的图像文件将被QEMU加载两次,分别在两个不同的位置。这只会让事情变得混乱,所以删除-kernel
选项。 -
要让通用加载程序设置CPU的起始PC,你需要明确请求。因为你想要的起始PC与图像加载地址相同,所以你可以使用
-device loader,file=Image,addr=0xc0008000,cpu-num=0
来实现。cpu-num部分表示为哪个CPU设置PC。(如果需要将PC设置为与图像加载地址不同的其他地址,则有更复杂的语法可供使用。)
注意1:上述内容适用于原始图像文件。如果你有一个ELF文件,通用加载程序将加载文件到ELF文件指定的地址,你不需要指定addr=
。如果使用cpu-num=
, 它将使用ELF入口点作为初始PC。
注意2:对于32位CPU来说,4GB的RAM有点多。Cortex-A15具有LPAE,因此不像不可能访问超过4GB物理地址标记的RAM部分,但这似乎有点奇怪。3GB(即从0x4000_0000到0xFFFF_FFFF的RAM)可能同样有用,并且可以减少虚拟机占用主机资源的量。
英文:
You don't say what format your Image file is. Is it an ELF file, or a raw binary? I'm guessing from the described behaviour that it's a raw binary.
If you pass QEMU the -kernel
option you are saying "this file should be started the way the Linux kernel expects to be started". That means, among other things, that you don't care where in the memory map it is placed, and that it should be started by first setting up a few registers and then jumping to the start of the image. (There is an exception: ELF files are loaded according to where the ELF file says they should be placed, and started by jumping to the ELF entrypoint. But ELF files are better loaded with the generic loader these days.)
(The specifics of what "boot me like a Linux kernel" means are listed in <a href="https://www.kernel.org/doc/Documentation/arm/Booting">this bit of the kernel documentation</a>. The virt board will always pass a device tree blob, not ATAGs.)
If your Image file is not expecting to be booted like a Linux kernel then you should not use the -kernel
option.
Instead, you can use the generic loader (-device loader
). Your second command line gets this almost right, but it is wrong in two ways:
-
you haven't removed the
-kernel
option, so your image file will be loaded by QEMU twice, in two different places. This is just confusing, so delete the-kernel
option. -
to get the generic loader to set the starting PC for the CPU you need to explicitly ask for that. Since the starting PC you want is the same as the image load address, you can do that with
-device loader,file=Image,addr=0xc0008000,cpu-num=0
. The cpu-num part says which CPU to set the PC for. (There is more complicated syntax for setting the PC to some other address than the image load address if you need it.)
Note 1: the above is for raw image files. If you have an ELF file then the generic loader will load the file to the addresses the ELF file specifies and you don't need to specify addr=
. It will use the ELF entrypoint for the initial PC if you use cpu-num=
.
Note 2: 4GB of RAM for a 32-bit CPU is rather a lot. The Cortex-A15 has LPAE, so it's not like it's impossible to access the part of RAM past the 4GB physical address mark, but it seems odd. 3GB (i.e. ram from 0x4000_0000 to 0xFFFF_FFFF) might be just as useful and save on how much of your host's resources the VM is using.
答案2
得分: 0
使用-kernel
选项时,qemu将执行以下操作:
- 在地址(
0x40010000
)加载内核映像 - 将PC设置为DRAM基地址的起始位置(
0x40000000
) - 开始执行指令(这些指令由QEMU设置):
- 将DTB地址加载到X0寄存器
- 跳转到内核映像的起始位置(
0x40010000
)
如果要在不执行上述操作的情况下传输内核映像并开始执行,则将使用以下命令:
将内核传输到特定位置:-device guest-loader,addr=<addr,你想要复制img的位置>,kernel=<kernel_img_path>
将PC加载到内核的起始位置:-device loader,addr=<addr,你复制img的位置>,cpu-num=0
英文:
When you use -kernel
option qemu will perform the following:
- Load the kernel image at address (
0x40010000
) - Set the PC to starting of the DRAM base (
0x40000000
) - Start executing the instruction (these instructions are set by QEMU):
- Load the DTB address into the X0 register
- Jump to the starting location of your kernel image(
0x40010000
)
Now if you want to transfer your kernel image and start execution without performing the above then the below command will be used:
To transfer kernel at a particular location: -device guest-loader,addr=<addr, where you want to copy img>,kernel=<kernel_img_path>
To load the PC at the start of the kernel: -device loader,addr=<addr, where you copied your img>,cpu-num=0
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论