在 linux 中使用 LD_PRELOAD 挂钩 strcmp?
Hook strcmp using LD_PRELOAD in linux?
因为我要hook strcmp,代码如下:
#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 在这两个条件下不同?
如果您不使用 strcmp
返回的值,则调用不执行任何操作。所以编译器可以随意删除调用。在某些情况下,编译器也可能内联对 strcmp
的调用,因为它确切地知道 strcmp
的作用。
这是标准所容忍的,它保留了标准库 header 中声明的所有标识符(无论是否实际包含 header):
§7.1.3/1:“在以下任何子条款中具有外部 linkage 的所有标识符 [即标准库 headers]... 始终保留用作外部标识符link年龄。
§7.1.3/2:“如果程序在
保留它的上下文(7.1.4 允许的除外)...行为未定义。
例外情况,根据 §7.1.4/2:"Provided that a library function can be declared without reference to any type defined in a header, it is also permissible to declare the function and use it without including its associated header."
由于定义您自己的 strcmp
显然是未定义的行为(除非您使用独立的编译器),所以应该简单地避免它。但是,在实践中,使用一些常见的编译器可能会以 non-portable 的方式进行。
首先,您必须避免包含 string.h
标准库 header。与 gcc 一起分发的标准 C 库 header 声明 strcmp
和 __attribute__((pure))
:
Many functions have no effects except the return value and their return value depends only on the parameters and/or global variables. Such a function can be subject to common subexpression elimination and loop optimization just as an arithmetic operator would be. These functions should be declared with the attribute pure
.
这个声明(它是 C 标准的扩展)让编译器消除对 strcmp
的第一次调用,因为它认为该函数没有 side-effects(这将包括写入 stdout ).即使没有优化,gcc 和 clang 也会消除第一次调用。
您可以说服 clang 编译对 strcmp
的两个调用,您可以简单地通过不包括 string.h
header(并自己声明 strcmp
来避免消除pure
属性)。但这对 gcc 来说还不够,因为 gcc 默认情况下会自动包含许多标准库函数的内联版本的声明(可以找到当前列表 here in the GCC manual。)正如最后 link 所提到的,您可以通过将 -fno-builtin-strcmp
添加到命令行(以及不包括标准 header)来抑制自动声明。
因为我要hook strcmp,代码如下:
#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 在这两个条件下不同?
如果您不使用 strcmp
返回的值,则调用不执行任何操作。所以编译器可以随意删除调用。在某些情况下,编译器也可能内联对 strcmp
的调用,因为它确切地知道 strcmp
的作用。
这是标准所容忍的,它保留了标准库 header 中声明的所有标识符(无论是否实际包含 header):
§7.1.3/1:“在以下任何子条款中具有外部 linkage 的所有标识符 [即标准库 headers]... 始终保留用作外部标识符link年龄。
§7.1.3/2:“如果程序在 保留它的上下文(7.1.4 允许的除外)...行为未定义。
例外情况,根据 §7.1.4/2:"Provided that a library function can be declared without reference to any type defined in a header, it is also permissible to declare the function and use it without including its associated header."
由于定义您自己的 strcmp
显然是未定义的行为(除非您使用独立的编译器),所以应该简单地避免它。但是,在实践中,使用一些常见的编译器可能会以 non-portable 的方式进行。
首先,您必须避免包含 string.h
标准库 header。与 gcc 一起分发的标准 C 库 header 声明 strcmp
和 __attribute__((pure))
:
Many functions have no effects except the return value and their return value depends only on the parameters and/or global variables. Such a function can be subject to common subexpression elimination and loop optimization just as an arithmetic operator would be. These functions should be declared with the attribute
pure
.
这个声明(它是 C 标准的扩展)让编译器消除对 strcmp
的第一次调用,因为它认为该函数没有 side-effects(这将包括写入 stdout ).即使没有优化,gcc 和 clang 也会消除第一次调用。
您可以说服 clang 编译对 strcmp
的两个调用,您可以简单地通过不包括 string.h
header(并自己声明 strcmp
来避免消除pure
属性)。但这对 gcc 来说还不够,因为 gcc 默认情况下会自动包含许多标准库函数的内联版本的声明(可以找到当前列表 here in the GCC manual。)正如最后 link 所提到的,您可以通过将 -fno-builtin-strcmp
添加到命令行(以及不包括标准 header)来抑制自动声明。