使用arm-poky-linux-gnueabi-gcc跨编译不同目录生成二进制差异原因咨询
这种情况在交叉编译场景里其实挺常见的,核心原因大多和编译器嵌入的非代码相关元数据或者构建环境的隐含差异有关,具体来说有这几个主要因素:
源文件绝对路径被嵌入二进制
GCC(包括你用的arm-poky-linux-gnueabi-gcc)默认会把源文件的绝对路径写入调试符号、DWARF调试信息,甚至在某些优化等级下,会把路径作为字符串常量嵌入到二进制中(比如用于错误提示、断言信息)。当你在不同目录编译时,源文件的绝对路径不同,这部分内容自然会不一样。哪怕你没加-g开启调试信息,某些编译模式下还是会保留少量路径相关的元数据。编译时间戳与构建ID
ELF格式的二进制文件头部会记录编译的时间戳,另外GCC默认会生成一个build-id(通过链接器选项--build-id),这个ID会基于编译环境、文件路径等信息生成,不同目录编译时这个ID会变化,导致二进制内容差异。即使是同一代码同一时间在不同目录编译,build-id也可能不同。构建系统的隐含路径依赖
如果你用Makefile或者CMake这类构建工具,它们可能会自动生成依赖文件(比如.d文件),这些文件里会包含源文件的绝对路径。当在不同目录构建时,依赖文件的内容不同,可能会导致编译过程中传递给编译器的隐含参数有细微差别,最终影响输出的二进制。另外,某些构建脚本可能会把当前目录(PWD环境变量)作为编译参数的一部分,这也会带来差异。环境变量的细微差异
虽然你执行了source命令配置工具链,但不同目录下执行该命令时,某些环境变量(比如PWD、CMAKE_BINARY_DIR如果用CMake的话)本身就是动态变化的。如果你的编译脚本或Makefile中不小心引用了这些变量,就会导致编译参数不一致,进而生成不同的二进制。
怎么解决这个问题?
要生成确定性的二进制(即相同代码在不同目录编译得到完全一致的文件),你可以给GCC和链接器加上这些选项:
-ffile-prefix-map=$(PWD)=.:把源文件的绝对路径替换成相对路径.,消除路径差异-Wl,--build-id=none:禁用链接器生成的build-id-fno-toplevel-reorder:禁止编译器对全局符号的顺序进行随机化(某些GCC版本默认会做这个优化)-D__DATE__="\"fixed-date\"" -D__TIME__="\"fixed-time\"":固定编译时间戳,避免时间带来的差异
另外,确保你的构建脚本(比如Makefile)不要引用当前目录的绝对路径,尽量使用相对路径来指定源文件和输出文件。
内容的提问来源于stack exchange,提问作者Anil Antony

