Know Your Tools – 一个编译神秘的问题,和工具的易用性

/ 0评 / 0

程序员啊,你要认识你自己(的工具)!

神秘的编译问题

我目前从事嵌入式裸机开发,所以免不了需要用 Keil. 今天(指本文发表的一个月内)遇到了一个非常诡异的问题:头文件更改后不生效,链接阶段由于宏定义不正确会出错。二次确认更改的是正确的项目下的正确的头文件;重启无效;甚至 Keil 重装都无效。

具体来说,链接脚本中是包含一些宏处理的。它差不多长这个样子:

#! armcc -E -I.
#include "./mem_config.h"

---8<---<snip>---8<---

#if USE_ADVANCED_MEMORY_CONFIG
ADVANCED_MEMORY_REGION ADVANCED_MEMORY_ADDRESS ADVANCED_MEMORY_SIZE
{
  (.amm.*)
}
#endif

---8<---<snip>---8<---

在编译时,会先调用 armcc 对这个文件中的宏进行展开,和 C 语言的预处理器行为一致。这就意味着如果 memory_config.h 中没有定义 USE_ADVANCED_MEMORY_CONFIG 或者定义为 0 的话,对应的区域就不会生成。

自然,代码中也有对应的宏处理:

#include "mem_config.h"

#if USE_ADVANCED_MEMORY_CONFIG
void advanced_memory_config_init() {
  extern uint32_t Image$$ADVANCED_MEMORY_REGION$$Base;
  extern uint32_t Image$$ADVANCED_MEMORY_REGION$$Length;

  // do funny things with symbols defined above
}
#endif

其中,Image$$ADVANCED_MEMORY_REGION$$BaseImage$$ADVANCED_MEMORY_REGION$$Length 是链接器定义的符号。当这个区域存在时有定义,分别对应该区域的加载地址和长度。

十分有趣的是,在某台计算机上修改 mem_config.h 的并不会产生任何效果,USE_ADVANCED_MEMORY_CONFIG 的值始终为 0. 而换一台电脑,同样的工程、同样的代码就可以正常编译。

诊断

一般遇到这种问题,很容易先想到是不是改错了文件或者头文件包含目录设置得有问题。之前已经确认了没有改错头文件,那么就只能考虑是包含目录设置有问题,引入了不属于这个项目的文件。

可是翻遍了项目的属性设置,检查了每一个可能出问题的地方,都没有找到任何问题。更可气的是其他计算机编译同样的工程就没有问题。

这个时候已经不能再依赖 Keil 本身尝试找到问题了。掏出 ProcMon 看看吧。

ProcMon 很快就抓到了这样一个条目:

armcc.exe 正在尝试从 %TEMP% 中取头文件,而 %TEMP% 中却又莫名地有一份 mem_config.h 存在。打开确认这个文件并不是编译中介文件后,删除该文件。编译终于能正常通过了。

查阅工具的手册,并没有说明 armcc 会选择 %TEMP% 作为默认包含目录[1]——毕竟,这份文件本身并不是一个编译中间文件,也不应该出现在 %TEMP% 下;而且一个编译器会选择将 %TEMP% 作为默认包含路径这种事情也让我觉得十分神奇——你能想象 gcc 或者 clang/tmp 作为优先级极高的默认包含目录(而且不告诉你)会导致多少问题么?

工具的易用性

又到了喜闻乐见的喷 Keil 时间。上一次喷 Keil 还是因为这玩意差点挂了我的实验

如果之前文本配置和 GUI 配置之间的差异只是个人喜好上的不同,这次的事件就反映了一个更严重的问题:你的工具会包含一些你可能完全无法预期的行为,而且这种行为并不会被告知:它会耍一些小聪明。当这个小聪明炸掉了你的工程的时候,就需要你有更多的调试经验才能完成诊断。

「工具应该符合直觉。」这里的「符合直觉」并不一定表示学习成本低,而是指它的行为和手册上描述的行为是一致的,最重要的是它不会做你没有告知它去做的事情。

许多人认为命令行是「不符合直觉」的,这个我想还是跟现在的计算环境有关:现在的孩子们已经不再需要和命令行打交道;哦,抱歉,是不再需要和 IBM 兼容机打交道[2]。图形界面是直观的,这是因为图形界面是给人类设计的;无数的界面设计师和程序员教会了机器以人类的方式说话。但是作为系统工程师的你,并不能依赖机器说人类的语言:你总有一天需要学会机器的语言,然后以机器的方式直接和机器对话。

参考资料

参考资料
1 ARM. ARM Compiler armcc User Guide[F/OL]. ARM Developer. [2022-12-05]. https://developer.arm.com/documentation/dui0472/k/Getting-Started-with-the-Compiler/Compiler-command-line-options-and-search-paths.
2 八私今天进步了没 等. 当代年轻一代信息技术现状[V/OL]. Bilibili. (2022-01-29)[2022-03-05]. https://www.bilibili.com/video/BV1pL4y1x7i7

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

Your comments will be submitted to a human moderator and will only be shown publicly after approval. The moderator reserves the full right to not approve any comment without reason. Please be civil.