0

我正在学习编写裸机 ARM Cortex-A7 固件以在带有半主机的 QEMU 上运行。我知道 ARM GCC 有一个名为 newlib 的 libc 实现,它支持常用 libc 函数的半主机。所以我试图让 newlib 也能正常工作。

在解决了很多问题之后,代码终于在 QEMU 上正常运行了:https ://github.com/iNvEr7/qemu-learn/tree/master/semihosting-newlib

(注意:QEMU 5.2.0 似乎有一个错误会导致 newlib 对 HEAPINFO 的半主机调用崩溃,因此要在 QEMU 上运行我的代码,您必须编译 QEMU 主服务器,并使用make runtarget 在 tmux 会话中使用 QEMU 运行代码)

但是,我想为我在与 newlib 集成时遇到的一些问题找到一些答案。

据我了解,newlib 作为一个 libc 实现,提供了一个 crt0 例程来初始化应用程序的内存区域,包括 .bss、.data、堆和堆栈。

但是,根据我的测试,GCC 链接的 crt0 不会初始化 .bss 和 .data 区域,因此会导致后面的 crt0 例程崩溃。

所以我必须为 .bss 和 .data 编写自己的初始化代码,以使其正确运行。

所以我想了解我是否以正确的方式做事?我是否遗漏了一些可以让 newlib 为我初始化这些区域的东西?还是自己进行初始化是常规的?

注意:我正在使用 arm-none-eabi-gcc stable 9-2019-q4-major

4

1 回答 1

0

似乎我在 newlib 本身中遇到了一个错误,并且由于一些随机的运气,我当前的代码运行良好。

所以我将我的工具链更新为 gcc-arm-none-eabi-10-2020-q4-major 并尝试编译相同的代码。这次又崩溃了。

所以我附加了 GDB 并逐步通过 ctr0 汇编代码试图找出原因。

事实证明,这行代码正在将标签的地址加载到 r1,但它应该加载该标签地址中的内容,ldr r1, .LC0 而不是adr r1, .LC0 .

这种错字的后果是 heapinfo 半主机调用返回的数据覆盖了该标签之后的其他数据,其中包含有关内存区域的信息。它反过来又影响了 crt0 例程中的 .bss 初始化代码。在我之前使用旧工具链的测试中,幸运的是它运行时没有崩溃,但使用最新的工具链,这样的错误会导致致命的崩溃。

我也意识到 5.2.0 QEMU 崩溃也可能是由这个 newlib 错误引起的,而不是 QEMU 问题。不知何故,主 QEMU 版本的行为有所不同,使崩溃消失了。

我已经向 newlib 提交了一个补丁。令我惊讶的是,这样一个致命的错误可以在不知不觉中溜走这么多年,而它却可以通过一个简单的 hello world 程序来揭示。

无论如何,我的挖掘似乎也回答了我的问题。如果 newlib 工作正常,它应该已经初始化了 .bss 部分。但是 newlib 中没有代码来初始化 .data 部分,我们必须手动为裸机执行此操作。


情节转折:从 newlib 邮件列表中返回。事实证明,newlib 的实现确实正确地符合 ARM 规范:

https://developer.arm.com/documentation/100863/0300/Semihosting-operations/SYS-HEAPINFO--0x16-?lang=en

其中“参数寄存器包含指向四字段数据块的指针的地址”。

相反,QEMU 进行了误解并写入了错误的地址。将改为向 QEMU 提出问题。

于 2021-02-16T23:18:31.187 回答