防止编译器不断折叠表达式的技巧
A trick to prevent the compiler from constant folding out an expression
我的程序中有一个字符串文字,我正在尝试创建一个业余校验和以确保该字符串文字在可移植可执行文件中没有被替换。
为此,我创建了字符串文字的散列,并将其作为整数文字存储在程序中。现在我有两个文字,一个用于字符串,一个用于散列。
在我的代码中,我通过使用以相同方式散列字符串文字的函数来实现校验和,我创建了一个新的运行时散列并根据散列文字检查该散列。
问题当然是,通过编译器优化,它可能会预先计算运行时哈希,然后我会根据哈希文字检查哈希文字,并且校验和将始终 return 为真。
所以我正在寻找一种技巧,让编译器认为字符串文字是一个动态字符串,可以是任何东西,这样它就不会对运行时哈希进行常量折叠优化,我的代码将正常工作.
您或许可以将字符串文字声明为 const volatile
,例如
const volatile char myliteral[] = "some literal string";
您还可以在构建时计算哈希值,例如在你的构建过程中有一些东西来提取适当的字符串并单独计算散列。
最后,如果字符串及其散列在两个不同的翻译单元中(例如在 file1.c
和 file2.c
中),您需要 link 时间优化来实现内联在构建时。使用当前 GCC(即 GCC 4.9 或 5),您需要 显式 在编译时传递 -flto
并在 link 时获得 link-时间优化,所以如果你不明确地那样做(例如在你的Makefile
中使用CC=gcc -flto -O2
),它就不会发生。
顺便说一句,您可能会对整个可执行文件、整个共享库或某个给定的目标文件进行校验和。详细信息是 OS 具体的。在 Linux 上见 dlopen(3), dlsym(3), dladdr(3), dl_iterate_phdr(5), elf(5), proc(5)
此外,您可以对初始文字的一些 随机后缀子串 进行散列(例如,在运行时散列 myliteral+random()%strlen(myliteral)
)并保留并与此类部分的常量数组进行比较散列。编译器不太可能内联所有这些!
我实际上相信这在实践中不是一个真正的问题:没有人会关心你的可执行文件,也没有人会花时间反编译它。
顺便说一句,您可以生成一个单独的 __timestamp.c
文件,其中包含时间戳和校验和信息(我在 2018 年夏季的 bismon 项目中正在这样做),并且 link 它带有你的可执行文件。
我的程序中有一个字符串文字,我正在尝试创建一个业余校验和以确保该字符串文字在可移植可执行文件中没有被替换。
为此,我创建了字符串文字的散列,并将其作为整数文字存储在程序中。现在我有两个文字,一个用于字符串,一个用于散列。
在我的代码中,我通过使用以相同方式散列字符串文字的函数来实现校验和,我创建了一个新的运行时散列并根据散列文字检查该散列。
问题当然是,通过编译器优化,它可能会预先计算运行时哈希,然后我会根据哈希文字检查哈希文字,并且校验和将始终 return 为真。
所以我正在寻找一种技巧,让编译器认为字符串文字是一个动态字符串,可以是任何东西,这样它就不会对运行时哈希进行常量折叠优化,我的代码将正常工作.
您或许可以将字符串文字声明为 const volatile
,例如
const volatile char myliteral[] = "some literal string";
您还可以在构建时计算哈希值,例如在你的构建过程中有一些东西来提取适当的字符串并单独计算散列。
最后,如果字符串及其散列在两个不同的翻译单元中(例如在 file1.c
和 file2.c
中),您需要 link 时间优化来实现内联在构建时。使用当前 GCC(即 GCC 4.9 或 5),您需要 显式 在编译时传递 -flto
并在 link 时获得 link-时间优化,所以如果你不明确地那样做(例如在你的Makefile
中使用CC=gcc -flto -O2
),它就不会发生。
顺便说一句,您可能会对整个可执行文件、整个共享库或某个给定的目标文件进行校验和。详细信息是 OS 具体的。在 Linux 上见 dlopen(3), dlsym(3), dladdr(3), dl_iterate_phdr(5), elf(5), proc(5)
此外,您可以对初始文字的一些 随机后缀子串 进行散列(例如,在运行时散列 myliteral+random()%strlen(myliteral)
)并保留并与此类部分的常量数组进行比较散列。编译器不太可能内联所有这些!
我实际上相信这在实践中不是一个真正的问题:没有人会关心你的可执行文件,也没有人会花时间反编译它。
顺便说一句,您可以生成一个单独的 __timestamp.c
文件,其中包含时间戳和校验和信息(我在 2018 年夏季的 bismon 项目中正在这样做),并且 link 它带有你的可执行文件。