TL;博士;
您可能无法将值强制进入编译单元,但您可以通过在标头中定义全局变量来强制使用符号。IE:long using_my_library_version_1_2_3;
该符号将可以在最终的二进制文件中从外部访问,并且可以对其进行测试(尽管与任何解决方案一样,它可以被规避,更不用说可以更改标头本身)。
编辑:为了澄清(由于评论),不要使用static
变量。
通过使用全局变量,它将默认为extern
并且不会被优化(以防加载二进制文件的其他对象使用标识符)。
注意事项和示例:
正如评论中提到的,全局变量的标识符(名称)是这种方法中的字符串。
但是,在编译可执行文件(和内核)时,在使用 ( ) 编译时,标识符可能会从最终二进制文件中删除-s
。这通常由嵌入式系统开发人员和喜欢让调试成为活生生的地狱的人执行(甚至更多)。
一个简单的例子:
// main.c
int this_is_example_version_0_0_1; /* variable name will show in the file */
int main(void) {
/* placed anywhere to avoid the "not used" warning: */
(void)this_is_example_version_0_0_1;
return 0;
}
// extra.c
int this_is_example_version_0_0_1; /* repeat line to your heart's content */
int this_is_example_version_0_0_1; /* (i.e., if header has no include guard) */
编译:
$ cc -xc -o a -Wall -O2 main.c extra.c
列出所有标识符/名称(将显示全局):
nm ./a | grep "this_is_example_version"
使用以下方法测试二进制文件中的字符串:
$ grep -F "this_is_example_version" ./a
细节:
关于 C 的有趣事实使这个解决方案成为可能......:
C 定义extern
为全局范围内函数和变量声明的默认值(6.2.2,第 5 小节)。
根据第 6.2.2 节(“标识符的链接”),“具有外部链接的特定标识符的每个声明都表示相同的对象或函数。”
这意味着全局范围内的重复声明将被整理为单个声明。
当变量被放置在全局范围内并且它的所有位都设置为零时,变量声明和变量定义看起来相同。
这是因为全局变量默认初始化为零。因此,编译器无法判断int foo;
是定义 ( int foo = 0;
) 还是声明 ( extern int foo;
)。
由于这种“身份”和这些规则,编译器将模糊的全局变量声明/定义转换为“弱”声明,由链接器解析。
这意味着如果你定义一个没有extern
关键字和值的全局变量,模棱两可的声明/定义将迫使编译器发出一个弱符号,该符号将在最终的二进制文件中公开。
该符号可用于标识在程序中某处使用了标头这一事实。