由于我想挂钩strcmp,Code如下:
#include <stdio.h>
#include <string.h>
int strcmp(const char *s1, const char *s2)
{
printf("hooked strcmp\n");
return 0;
}
// gcc test.c -shared -fPIC -o libtest.so
#include <stdio.h>
#include <string.h>
#include "test.h"
int main(int argc, char *argv[])
{
strcmp(argv[1], "aba"); // didn't call strcmp in libtest
int i = strcmp(argv[1], "aba"); // call strcmp in libtest
}
// gcc main.c
// LD_PRELOAD=./libtest.so ./a.out 12123
我的问题是:为什么strcmp会在这两个条件下出现差异?
答案 0 :(得分:0)
如果您不使用strcmp
返回的值,则该调用不执行任何操作。因此编译器可以自由删除调用。在某些情况下,编译器可能还会内联对strcmp
的调用,因为它准确地知道strcmp
的作用。
标准对此进行了宽容,该标准保留了标准库标头中声明的所有标识符(无论标头是否实际包含在内):
§7.1.3/ 1:“以下任何子条款中的所有带有外部链接的标识符[即标准库标题] ...始终保留用作具有外部链接的标识符。
§7.1.3/ 2:“如果程序声明或定义了一个标识符 保留它的上下文(7.1.4允许的情况除外)......行为未定义。
例外,根据§7.1.4/ 2:“如果可以在不引用标题中定义的任何类型的情况下声明库函数,则允许声明该函数并使用它而不包括其关联的标题。“
由于定义自己的strcmp
显然是未定义的行为(除非您使用独立的编译器),因此应该简单地避免它。但是,在实践中,可以使用一些常见的编译器以不可移植的方式进行操作。
首先,您必须避免包含string.h
标准库标头。随gcc分发的标准C库标头声明strcmp
__attribute__((pure))
:
除了返回值之外,许多函数都没有效果,它们的返回值仅取决于参数和/或全局变量。这样的函数可以像算术运算符那样经受公共子表达式消除和循环优化。应使用属性
pure
声明这些函数。
这个声明(它是C标准的扩展)允许编译器消除对strcmp
的第一次调用,因为它认为该函数没有副作用(包括写入stdout)。即使没有优化,gcc和clang也会消除第一个调用。
你可以说服clang编译两次调用strcmp
,你可以避免删除,只是不包括string.h
标题(并且在没有strcmp
的情况下自己声明pure
属性)。但是这对gcc来说是不够的,因为默认情况下,gcc会自动包含许多标准库函数的内联版本的声明(当前列表可以找到here in the GCC manual。)正如最后一个链接所提到的,你可以禁止自动声明将-fno-builtin-strcmp
添加到命令行(以及不包括标准标题)。