重写C中的函数调用

重写C中的函数调用,c,function,linker,overriding,C,Function,Linker,Overriding,为了记录调用,我想覆盖对各种API的某些函数调用,但我也可能想在数据发送到实际函数之前对其进行操作 例如,假设我在源代码中多次使用名为getObjectName的函数。有时我想临时重写此函数,因为我想更改此函数的行为以查看不同的结果 我创建一个新的源文件,如下所示: #include <apiheader.h> const char *getObjectName (object *anObject) { if (anObject == NULL) r

为了记录调用,我想覆盖对各种API的某些函数调用,但我也可能想在数据发送到实际函数之前对其进行操作

例如,假设我在源代码中多次使用名为
getObjectName
的函数。有时我想临时重写此函数,因为我想更改此函数的行为以查看不同的结果

我创建一个新的源文件,如下所示:

#include <apiheader.h>    

const char *getObjectName (object *anObject)
{
    if (anObject == NULL)
        return "(null)";
    else
        return "name should be here";
}
gcc program.c -Wl,-wrap,getObjectName -o program
const char *getObjectName (object *anObject) {
  static char * (*func)();

  if(!func)
    func = (char *(*)()) dlsym(RTLD_NEXT, "getObjectName");
  printf("Overridden!\n");     
  return(func(anObject));    // call original function
}
#包括
常量字符*getObjectName(对象*anObject)
{
if(anObject==NULL)
返回“(空)”;
其他的
return“名称应在此处”;
}
我像往常一样编译所有其他源代码,但在与API库链接之前,我首先将其与此函数链接。这很好,只是我显然不能在重写函数中调用真正的函数


有没有一种更简单的方法可以“重写”一个函数而不获取链接/编译错误/警告?理想情况下,我希望能够通过编译和链接一个或两个额外的文件来覆盖函数,而不是摆弄链接选项或更改我的程序的实际源代码。

如果您只想捕获/修改调用的源代码,最简单的解决方案是将头文件放在一起(
intercept.h
)带有:

#ifdef截获
#定义getObjectName(x)myGetObjectName(x)
#恩迪夫
然后按如下方式实现函数(在
intercept.c
中,不包括
intercept.h
):

const char*myGetObjectName(object*anObject){
如果(anObject==NULL)返回“(NULL)”;
返回getObjectName(一个对象);
然后确保要拦截调用的每个源文件的顶部都有以下内容:

#包括“intercept.h”
当您使用“
-DINTERCEPT
”编译时,所有文件都将调用您的函数,而不是真正的函数,而您的函数仍将调用真正的函数

不使用“
-DINTERCEPT
”进行编译将防止发生拦截


如果您想拦截所有调用(而不仅仅是来自源代码的调用),这就有点麻烦了——通常可以通过动态加载和解析实际函数(使用
dlload-
dlsym-
类型调用)来完成但我认为在您的情况下没有必要这样做。

在涉及两个存根库的链接器中,也有一种棘手的方法

库#1与宿主库链接,并以另一个名称显示正在重新定义的符号

库#2与库#1链接,在库#1中插入调用并调用重新定义的版本


请务必小心此处的链接顺序,否则它将不起作用。

您可以将函数指针定义为全局变量。调用者语法不会更改。当程序启动时,它可以检查是否将某个命令行标志或环境变量设置为启用日志记录,然后保存函数指针的原始值,并将其替换为l日志记录功能。您不需要特殊的“启用日志记录”构建。用户可以“在字段中”启用日志记录

您需要能够修改调用者的源代码,但不能修改被调用者的源代码(因此,在调用第三方库时,这将起作用)

foo.h:

typedef const char* (*GetObjectNameFuncPtr)(object *anObject);
extern GetObjectNameFuncPtr GetObjectName;
foo.cpp:

const char* GetObjectName_real(object *anObject)
{
    return "object name";
}

const char* GetObjectName_logging(object *anObject)
{
    if (anObject == null)
        return "(null)";
    else
        return GetObjectName_real(anObject);
}

GetObjectNameFuncPtr GetObjectName = GetObjectName_real;

void main()
{
    GetObjectName(NULL); // calls GetObjectName_real();

    if (isLoggingEnabled)
        GetObjectName = GetObjectName_logging;

    GetObjectName(NULL); // calls GetObjectName_logging();
}

如果使用GCC,则可以使函数
。非弱函数的函数:

测试.c

#include <stdio.h>

__attribute__((weak)) void test(void) { 
    printf("not overridden!\n"); 
}

int main() {
    test();
}
#include <stdio.h>

void test(void) {
    printf("overridden!\n");
}
__attribute__((weak)) void test(void);
... other weak function declarations ...
/* for GCC, these will become weak definitions */
#ifdef __GNUC__
#include "weakdecls.h"
#endif

void test(void) { 
    ...
}

... other functions ...
test1.c

#include <stdio.h>

__attribute__((weak)) void test(void) { 
    printf("not overridden!\n"); 
}

int main() {
    test();
}
#include <stdio.h>

void test(void) {
    printf("overridden!\n");
}
__attribute__((weak)) void test(void);
... other weak function declarations ...
/* for GCC, these will become weak definitions */
#ifdef __GNUC__
#include "weakdecls.h"
#endif

void test(void) { 
    ...
}

... other functions ...
遗憾的是,这对其他编译器不起作用。但是,如果使用GCC编译,您可以在自己的文件中包含包含可重写函数的弱声明,只在API实现文件中放置一个include:

weakdecls.h

#include <stdio.h>

__attribute__((weak)) void test(void) { 
    printf("not overridden!\n"); 
}

int main() {
    test();
}
#include <stdio.h>

void test(void) {
    printf("overridden!\n");
}
__attribute__((weak)) void test(void);
... other weak function declarations ...
/* for GCC, these will become weak definitions */
#ifdef __GNUC__
#include "weakdecls.h"
#endif

void test(void) { 
    ...
}

... other functions ...
函数.c

#include <stdio.h>

__attribute__((weak)) void test(void) { 
    printf("not overridden!\n"); 
}

int main() {
    test();
}
#include <stdio.h>

void test(void) {
    printf("overridden!\n");
}
__attribute__((weak)) void test(void);
... other weak function declarations ...
/* for GCC, these will become weak definitions */
#ifdef __GNUC__
#include "weakdecls.h"
#endif

void test(void) { 
    ...
}

... other functions ...

这样做的缺点是,如果不对api文件做些什么(需要这三行代码和weakdecls),它就不能完全工作。但一旦您进行了更改,就可以通过在一个文件中编写全局定义并将其链接到中来轻松覆盖函数。

对于gcc,在Linux下您可以使用
--wrap
链接器标志,如下所示:

#include <apiheader.h>    

const char *getObjectName (object *anObject)
{
    if (anObject == NULL)
        return "(null)";
    else
        return "name should be here";
}
gcc program.c -Wl,-wrap,getObjectName -o program
const char *getObjectName (object *anObject) {
  static char * (*func)();

  if(!func)
    func = (char *(*)()) dlsym(RTLD_NEXT, "getObjectName");
  printf("Overridden!\n");     
  return(func(anObject));    // call original function
}
并将您的功能定义为:

const char *__wrap_getObjectName (object *anObject)
{
    if (anObject == NULL)
        return "(null)";
    else
        return __real_getObjectName( anObject ); // call the real function
}
这将确保对
getObjectName()
的所有调用(在链接时)都被重新路由到您的包装函数。但是,在Mac OS X下的gcc中没有这个非常有用的标志


如果您使用g++进行编译,请记住使用
extern“C”
声明包装函数。

您也可以使用共享库(Unix)或DLL(Windows)来完成此操作(可能会有点性能损失)。然后您可以更改DLL/以便加载(一个版本用于调试,一个版本用于非调试)

我过去也做过类似的事情(不是为了达到你想要达到的目标,但基本前提是一样的),结果很好

[根据OP评论进行编辑]

事实上,我想 重写函数是因为 怀疑他们在工作上表现不同 不同的操作系统

有两种常见的方法(据我所知)来处理这个问题,共享lib/dll方法或编写链接所针对的不同实现

对于这两种解决方案(共享libs或不同的链接),您将使用foo_linux.c、foo_osx.c、foo_win32.c(或者更好的方法是linux/foo.c、osx/foo.c和win32/foo.c),然后编译并链接到相应的解决方案


如果您正在为不同的平台寻找不同的代码和debug-vs-release,我可能倾向于使用共享lib/DLL解决方案,因为它是最灵活的解决方案。

您可以使用
LD\u PRELOAD
技巧覆盖函数-请参见
man-LD。因此
。使用您的函数编译共享lib并启动二进制文件ry(您甚至不需要修改二进制文件!),比如
LD\u PRELOAD=mylib.so myprog

在你的身体里