裸机 ARM Cortex-A7 newlib crt0 未初始化 .bss 和 .data 区域

问题描述

我正在学习编写裸机 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 似乎有一个 bug,它会导致 newlib 对 HEAPINFO 的半主机调用崩溃,所以要在 QEMU 上运行我的代码,你必须编译 QEMU master,并使用 make run 目标来运行在 tmux 会话中使用 QEMU 编写代码

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

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

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

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

所以我想了解一下我的做法是否正确?我是否遗漏了一些可以让 newlib 为我初始化这些区域的东西?还是习惯自己做初始化?

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

解决方法

我似乎遇到了 newlib 本身的错误,而且由于一些偶然的运气,我当前的代码运行良好。

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

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

事实证明,this line of code 正在将标签地址加载到 r1,但它应该加载该标签地址中的内容,即 ldr r1,.LC0 而不是 adr r1,.LC0

这个错字的后果是 heapinfo semihosting 调用返回的数据覆盖了该标签之后的其他数据,其中包含有关内存区域的信息。它反过来影响了后面 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 提交问题。

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...