【发布时间】:2015-11-22 19:45:33
【问题描述】:
好的,关于什么和为什么的背景?
我想在桌面 linux 上编译和运行微控制器固件(裸机,无操作系统)。我不想写字节码解释器或二进制翻译器;我想编译原始源代码。将固件作为标准 GUI 应用程序运行具有许多优势,例如快速开发迭代、高级调试、自动化测试、压力测试等。我之前在一些项目中使用 AVR 微控制器完成了此操作,通常采取以下步骤:
- 提供桌面上不存在的硬件相关标头(主要是 MMIO 寄存器定义 -> 全局变量)
- 实现外设仿真代码(lcd、eeprom)
- 做一些反映原始设备用户界面(液晶显示器、按钮)的 GUI
- 把所有东西粘在一起
前 3 个步骤很简单(AVR 的代码不多),最后一个步骤很棘手。 FW 中的一些构造在桌面版本中最终成为无限循环(例如,忙循环等待外围寄存器更改或中断处理程序更改内存),其他构造最终成为无操作(写入 MMIO,在真实系统上触发某些事情) ,并且将固件的主循环与 GUI 库的主循环融合起来也需要一些创造力。如果 FW 分层很好,那么低级代码可以用胶水函数代替,而无需过多修改。
虽然这些更改会影响整体行为,但我发现最终结果在许多情况下都非常有用。不幸的是,这种方法具有侵入性(修改固件),并且粘合逻辑高度依赖于固件的架构(每次都需要重新发明)。
越来越接近问题...
从 C/C++ 的角度来看,固件与在适当操作系统上运行的代码之间最重要的区别是 MMIO。 MMIO访问有副作用,读写的副作用不同。在桌面应用程序中,这个概念不存在(除非你从用户空间戳硬件)。如果可以在读取或写入内存位置时定义一个钩子,这将启用适当的外围设备仿真,并且固件可以大部分完整地编译。当然,这不能在 C++ 中完成,母语的全部目的就是反对这一点。但是内存调试器在仪器的帮助下使用相同的概念(跟踪内存访问运行时)。
我对实现有一些想法,所以我的问题是您认为它们有多可行,或者有没有其他方法可以达到相同的结果?
-
根本没有仪器。如果内存位置被访问,x86 可以发出信号,并且调试器使用它来实现观察点(内存访问中断)。作为概念证明,我创建了这个测试程序:
#include <stdio.h> volatile int UDR; void read() { printf("UDR read\n"); } void write() { printf("UDR write\n"); } int main() { UDR=1; printf("%i\n", UDR); return 0; }UDR 是我要跟踪的 MMIO 寄存器,如果我使用以下脚本在 GDB 下运行编译程序:
watch UDR commands call write() cont end rwatch UDR commands call read() cont end结果正是我想要的:
UDR write UDR read 1问题是我根本不知道这是否可扩展。据我所知,观察点是有限的硬件资源,但无法找到 x86 的限制。我可能需要不到 100 个。GDB 也支持软件观察点,但仅用于编写,因此它实际上不能用于此目的。代码只能在 GDB 会话下运行的另一个缺点。
运行时检测。如果我是正确的,Valgrind/libvex 会这样做:读取编译的二进制文件并在内存访问位置(以及许多其他位置)插入检测代码。我可以编写配置了地址和回调的新 Valgrind 工具作为上述 GDB 脚本,并执行应用程序 Valgrind 会话。你认为这可行吗?我找到了一些关于创建新工具的文档,但这似乎并不容易。
编译时检测。 clang 和 gcc 中的内存和地址清理程序就是这样工作的。这是一个由两部分组成的游戏,编译器发出检测代码,并且一个消毒库(实现实际检查)链接到应用程序。我的想法是用执行上述回调的自己的实现替换 sanitizer 库,而不进行任何编译器修改(这可能超出了我的能力)。不幸的是,我没有找到太多关于检测代码和 sanitizer 库如何交互的文档,我只找到了描述检查器算法的论文。
这就是我的问题,对任何主题的任何评论都表示赞赏。 :)
【问题讨论】:
-
选择一种语言。 C 不是 C++。特别是,C++ 类允许您重载运算符。
UDR=1可以拨打MMIO_t::operator=(int)。 -
我从未使用过这样的模拟器,而是在实际硬件上使用 JTAG 调试。 “在模拟器上工作但不在设备上工作”的帖子数量令人印象深刻......
-
对于一个设备我有 C 固件,对于另一个我有 C++11。 MMIO 寄存器是简单的 uint8_t、uint16_t 易失性全局变量(在固件中具有固定地址),使用插桩处理这些变量将与语言无关。为 MMIO 定义自定义类型并为这种类型定义 op=() 是个好主意,我没有想到。但它只允许捕获写入,对于繁忙的等待循环,我也需要捕获读取。无论如何,我会试一试。谢谢!
-
@MartinJames 是的,我有 JTAG,我在正常开发中使用它。用一堆电缆将我的笔记本电脑、JTAG、目标板和工作台电源相互连接起来有点笨拙,但这对于固定工作台来说是可以的。我想在旅途中拥有一个开发选项。正如我所说,桌面调试和测试功能比 JTAG 提供的要好很多。我也知道仿真/模拟不是真实的,这也不是这个实验的目标。
-
好吧,我不得不承认我的办公室有几根电缆……好吧,太多了,以至于我不会走到长凳下面,因为有东西掉出来了,我可能再也无法让它工作了, (还有一些我不想见的东西住在下面:)。 JTAG 为我提供了源代码级断点和步进、堆栈、寄存器、变量和内存检查 - 我可以接受。
标签: c++ c instrumentation emulation