我可以在 C 程序中拦截正常的 stdio 调用,做一些工作然后调用原始的吗?
Can I intercept normal stdio calls in a C program, do some work and then call the original ones?
我有一堆 C 文件,它们尝试使用 fread()
、fwrite()
、fseek()
等 stdio 函数从磁盘读取和写入 CSV 和其他随机数据。 (如果重要的话,这是一项大学作业,我们将使用不同的块大小和各种结构来试验 IO 性能以跟踪磁盘文件上的数据等。)
我想做的是编译这些源文件(有几十个)
没有来自 <stdio.h>
的 fopen()
、fread()
、fwrite()
的定义。我想提供我自己的 fopen()
、fread()
、fwrite()
,我可以在其中跟踪一些信息,例如哪个进程试图读取哪个文件,以及读取了多少 blocks/pages 等等像那样,然后调用正常的 stdio 函数。
我不想遍历每个文件的每一行并将 fopen()
更改为 my_fopen()
....有没有更好的方法可以在编译时执行此操作?
我正在开发一个 Python 程序,该程序扫描源文件并使用我的函数更改这些调用,但它变得有点混乱,我有点迷路了。我想也许有更好的方法来做到这一点;如果你能给我指出正确的方向,比如搜索什么就太好了。
此外,我不想使用一些 Linux 分析工具来报告哪些系统调用发生在何处;我只想在调用这些函数之前执行一些代码。
不但是是但是不是。据我所知,最好的方法是 LD_PRELOAD 一个提供您自己版本的这些函数的库。您可以通过 dlopen
ing libc.so 获取原件(获取 libc 函数的 dlopen NULL 技巧在这里不适用,因为您的库已经加载)。
LD_PRELOAD 技巧的替代方法(需要您编写一个单独的库并且仅适用于 Linux),您可以使用 GNU [=28] 的 --wrap
选项=]呃。有关此技术的示例,请参阅 here。
与LD_PRELOAD的主要区别:
- 不需要外部库 - 都在可执行文件中;
- 不需要运行时选项;
- 只要您使用 GNU 工具链,就可以在任何平台上工作;
- 仅适用于在 link 时解析的调用 - 动态库仍将使用原始函数
另一种方法:将 -Dfread=my_fread
添加到 Makefile CFLAGS 中,用于任何您希望 "spy" 的 .o 文件。添加 my_fread.o 定义 my_fread
[其中有 no -D
技巧].
对您希望拦截的任何函数重复上述操作。与 LD_PRELOAD
大致相同 [在有效性方面可能更容易实施]。我都做过。
或者创建一个 my_func.h
来定义并在每个文件中插入一个 #include "my_func.h"
。经销商的选择
更新
忘记了另一种方式。正常编译。破坏目标 .o 的 [symbol table] 中的符号名称(通过自定义程序或 ELF/hex 编辑器):将 fread
更改为长度相同且不与任何内容冲突的内容[你可以控制]。目标名称:qread
或 frea_
或其他名称。
使用新名称添加您的拦截 .o。
这看起来 "dirty",但我们在这里 是 一项 "dirty" 工作。这是我用于 .o 的 "old school" [叹气:-)] 方法,我没有源代码并且在 LD_PRELOAD
存在之前。
一种方法是重新定义您需要的所有 stdio 函数。 fopen 变为 my_fopen,fread 变为 my_fread,然后让您的 my_fopen 调用 fopen。这可以在一个头文件中完成,该文件包含在要替换对 fopen 的调用的文件中。请参阅下面的示例。
main.c:
#include <stdio.h>
#include "my_stdio.h"
int main(void)
{
FILE *f;
char buf[256];
f = fopen("test.cvs", "r");
if(f == NULL)
{
printf("Couldn't open file\n");
return 1;
}
fread(buf, sizeof(char), sizeof(buf), f);
fclose(f);
return 0;
}
my_stdio.c:
#include <stdio.h>
FILE *my_fopen(const char *path, const char *mode)
{
FILE *fp;
printf("%s before fopen\n", __FUNCTION__);
fp = fopen(path,mode);
printf("%s after fopen\n", __FUNCTION__);
return fp;
}
int my_fclose(FILE *fp)
{
int rv;
printf("%s before fclose\n", __FUNCTION__);
rv = fclose(fp);
printf("%s after fclose\n", __FUNCTION__);
return rv;
}
size_t my_fread(void *ptr, size_t size, size_t nmemb, FILE *stream)
{
size_t s;
printf("%s before fread\n", __FUNCTION__);
s = fread(ptr,size,nmemb,stream);
printf("%s after fread\n", __FUNCTION__);
return s;
}
size_t my_fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream)
{
size_t s;
printf("%s before fwrite\n", __FUNCTION__);
s = fwrite(ptr,size,nmemb,stream);
printf("%s after fwrite\n", __FUNCTION__);
return s;
}
my_stdio.h:
#ifndef _MY_STDIO_H_
#define _MY_STDIO_H_
#define fopen my_fopen
#define fclose my_fclose
#define fread my_fread
#define fwrite my_fwrite
#endif /* _MY_STDIO_H_ */
生成文件:
main: main.o my_stdio.o
$(CC) -g -o $@ main.o my_stdio.o
main.o: main.c
$(CC) -g -c -o $@ $<
my_stdio.o: my_stdio.c my_stdio.h
$(CC) -g -c -o $@ $<
我有一堆 C 文件,它们尝试使用 fread()
、fwrite()
、fseek()
等 stdio 函数从磁盘读取和写入 CSV 和其他随机数据。 (如果重要的话,这是一项大学作业,我们将使用不同的块大小和各种结构来试验 IO 性能以跟踪磁盘文件上的数据等。)
我想做的是编译这些源文件(有几十个)
没有来自 <stdio.h>
的 fopen()
、fread()
、fwrite()
的定义。我想提供我自己的 fopen()
、fread()
、fwrite()
,我可以在其中跟踪一些信息,例如哪个进程试图读取哪个文件,以及读取了多少 blocks/pages 等等像那样,然后调用正常的 stdio 函数。
我不想遍历每个文件的每一行并将 fopen()
更改为 my_fopen()
....有没有更好的方法可以在编译时执行此操作?
我正在开发一个 Python 程序,该程序扫描源文件并使用我的函数更改这些调用,但它变得有点混乱,我有点迷路了。我想也许有更好的方法来做到这一点;如果你能给我指出正确的方向,比如搜索什么就太好了。
此外,我不想使用一些 Linux 分析工具来报告哪些系统调用发生在何处;我只想在调用这些函数之前执行一些代码。
不但是是但是不是。据我所知,最好的方法是 LD_PRELOAD 一个提供您自己版本的这些函数的库。您可以通过 dlopen
ing libc.so 获取原件(获取 libc 函数的 dlopen NULL 技巧在这里不适用,因为您的库已经加载)。
LD_PRELOAD 技巧的替代方法(需要您编写一个单独的库并且仅适用于 Linux),您可以使用 GNU [=28] 的 --wrap
选项=]呃。有关此技术的示例,请参阅 here。
与LD_PRELOAD的主要区别:
- 不需要外部库 - 都在可执行文件中;
- 不需要运行时选项;
- 只要您使用 GNU 工具链,就可以在任何平台上工作;
- 仅适用于在 link 时解析的调用 - 动态库仍将使用原始函数
另一种方法:将 -Dfread=my_fread
添加到 Makefile CFLAGS 中,用于任何您希望 "spy" 的 .o 文件。添加 my_fread.o 定义 my_fread
[其中有 no -D
技巧].
对您希望拦截的任何函数重复上述操作。与 LD_PRELOAD
大致相同 [在有效性方面可能更容易实施]。我都做过。
或者创建一个 my_func.h
来定义并在每个文件中插入一个 #include "my_func.h"
。经销商的选择
更新
忘记了另一种方式。正常编译。破坏目标 .o 的 [symbol table] 中的符号名称(通过自定义程序或 ELF/hex 编辑器):将 fread
更改为长度相同且不与任何内容冲突的内容[你可以控制]。目标名称:qread
或 frea_
或其他名称。
使用新名称添加您的拦截 .o。
这看起来 "dirty",但我们在这里 是 一项 "dirty" 工作。这是我用于 .o 的 "old school" [叹气:-)] 方法,我没有源代码并且在 LD_PRELOAD
存在之前。
一种方法是重新定义您需要的所有 stdio 函数。 fopen 变为 my_fopen,fread 变为 my_fread,然后让您的 my_fopen 调用 fopen。这可以在一个头文件中完成,该文件包含在要替换对 fopen 的调用的文件中。请参阅下面的示例。
main.c:
#include <stdio.h>
#include "my_stdio.h"
int main(void)
{
FILE *f;
char buf[256];
f = fopen("test.cvs", "r");
if(f == NULL)
{
printf("Couldn't open file\n");
return 1;
}
fread(buf, sizeof(char), sizeof(buf), f);
fclose(f);
return 0;
}
my_stdio.c:
#include <stdio.h>
FILE *my_fopen(const char *path, const char *mode)
{
FILE *fp;
printf("%s before fopen\n", __FUNCTION__);
fp = fopen(path,mode);
printf("%s after fopen\n", __FUNCTION__);
return fp;
}
int my_fclose(FILE *fp)
{
int rv;
printf("%s before fclose\n", __FUNCTION__);
rv = fclose(fp);
printf("%s after fclose\n", __FUNCTION__);
return rv;
}
size_t my_fread(void *ptr, size_t size, size_t nmemb, FILE *stream)
{
size_t s;
printf("%s before fread\n", __FUNCTION__);
s = fread(ptr,size,nmemb,stream);
printf("%s after fread\n", __FUNCTION__);
return s;
}
size_t my_fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream)
{
size_t s;
printf("%s before fwrite\n", __FUNCTION__);
s = fwrite(ptr,size,nmemb,stream);
printf("%s after fwrite\n", __FUNCTION__);
return s;
}
my_stdio.h:
#ifndef _MY_STDIO_H_
#define _MY_STDIO_H_
#define fopen my_fopen
#define fclose my_fclose
#define fread my_fread
#define fwrite my_fwrite
#endif /* _MY_STDIO_H_ */
生成文件:
main: main.o my_stdio.o
$(CC) -g -o $@ main.o my_stdio.o
main.o: main.c
$(CC) -g -c -o $@ $<
my_stdio.o: my_stdio.c my_stdio.h
$(CC) -g -c -o $@ $<