首页 > 解决方案 > COFF 对象文件中大于 64K 的未初始化数组的意外符号值

问题描述

编辑:我在使用 clang 编译器进行测试时犯了一个错误。这与 clang 一样有效。Visual Studio 2015 产生了意想不到的结果。我的结论是 Visual Studio 2015 有一些可疑之处。

我做了一个观察,我无法解释表示 Visual Studio 2015 生成的 COFF 对象文件中未初始化 C 数组的对象文件符号的值。该值应该以字节为单位表示数组的大小,但这不是我的观察。我希望有人可以解释这里发生了什么。

考虑源文件 testdata_works.c

  char testb[0x10002];

考虑源文件 testdata_fails.c:

  char testa[0x10001];
  char testb[0x10002];

现在编译:

cl -c testdata_works.c
cl -c testdata_fails.c

现在在两个目标文件上运行“dumpbin /symbols”,并注意符号“testb”的值在两个目标文件之间有何不同(!)有关血腥细节,请参见下文。

dumpbin /symbols testdata_works.obj

006 00010002 UNDEF  notype       External     | testb

现在为 testdata_fails.obj

dumpbin /symbols testdata_fails.obj

006 00010001 UNDEF  notype       External     | testa
007 00010041 UNDEF  notype       External     | testb

我们注意到符号 testb 在 testdata_works.obj 中具有预期值 0x10002,但在 testdata_fails.obj 中具有意外值 0x10041。

我把真正奇怪的部分留到最后,这可能会在那些精通 COFF 目标文件格式的人中触发众所周知的“啊哈”:

将 testdata_fails.c 中的数组大小减小到 0x10000 以下,例如:

  char testa[0x0001];
  char testb[0x0002];

并编译

cl -c testdata_fails.c

dumpbin 命令现在为符号 testa 和 testb 生成预期的结果:

006 00000001 UNDEF  notype       External     | testa
007 00000002 UNDEF  notype       External     | testb

关于这里发生了什么以及如何修复它以便它为大于 64K 的符号值产生预期结果的任何建议?

最后的评论:

我已经使用 clang/llvm-nm 7.0.1 而不是 Visual Studio 2015/dumpbin 复制了这个,所以它可能与 COFF 格式有关?

你可能会问我是如何偶然发现这一点的。我正在研究 BSD 内核 genassym.sh 脚本,并决定编写一个小型 C++ STL 程序,该程序可以从 LLVM 库可以读取的任何目标文件格式中实现相同的任务。我已经构建了这个程序,但它不能正常工作,因为目标文件中的数据不符合预期。

标签: c

解决方案


COFF 不保留数组大小。与数组符号关联的值是它的起始地址。要推断数组的大小,dumpbin必须使用一些启发式方法,例如通过查看相邻符号、部分结尾等。显然,启发式方法失败了。只有编译器知道为什么 BSS 比预期的要大。


推荐阅读