来自open()的钩子的分段错误

时间:2015-02-16 13:28:12

标签: c linux gcc

我正在尝试在系统函数open()上创建一个钩子。我已经按照以下几点做了这个。

我使用以下内容创建了一个包装器库:

extern int mocked_open(const char* fn, int flags, va_list args);

int open(const char* fn, int flags, ...)
{
    int r = -1;
    va_list args;

    va_start(args, flags);
    r = mocked_open(fn, flags, args);
    va_end(args);

    return r;
}

我把它编译成libwrapper.so,我使用LD_PRELOAD加载。

mocked_open()的实现如下(我使用CPPUtest框架):

int mocked_open(const char* fn, int flags, va_list args)
{
    if (strncmp(fn, test_device_id, 11) == 0)
    {
        return mock().actualCall("open").returnValue().getIntValue();
    }
    else
    {
        int r = -1;
        int (*my_open)(const char*, int, ...);
        void* fptr = dlsym(RTLD_NEXT, "open");
        memcpy(&my_open, &fptr, sizeof(my_open));

        if (flags & O_CREAT)
        {
            r = my_open(fn, flags, va_arg(args, mode_t));
        }
        else
        {
            r = my_open(fn, flags);
        }

        return r;
    }
}

test_device_id是一个简单的字符串(" test_device"),我希望不会在其他地方使用。

在运行测试期间,可执行文件因分段错误而崩溃。我已将此跟踪到GCC配置文件功能,该功能要打开/创建一堆.gcda文件,并为此调用open()

在使用strace进行一些调试后(根据下面的建议),我发现行r = my_open(fn, flags, va_arg(args, mode_t));确实是罪魁祸首。它被递归地调用,或者似乎是这样:我看到很多对这一行的调用,没有函数返回。然后是段错误。正在打开的文件是相应的.gcda文件(用于分析)。实际上,只有启用了性能分析才会出现段错误。

3 个答案:

答案 0 :(得分:5)

试试这个

typedef int (*OpenFunction)(const char* fn, int flags, ...);

然后

OpenFunction function;
void      **pointer;

pointer  = (void **)&function;
*pointer = dlsym(RTLD_NEXT, "open");

这是一个完整的工作示例

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <dlfcn.h>
#include <unistd.h>
#include <fcntl.h>

#include <errno.h>

typedef int (*OpenFunction)(const char* fn, int flags, ...);

int main(int argc, char **argv)
{
    OpenFunction function;
    void       *dl;
    int         fd;
    void      **pointer;

    if (argc < 2)
        return -1;
    pointer  = (void **)&function;
    *pointer = dlsym(RTLD_NEXT, "open");

    fd = function(argv[1], O_RDONLY);
    if (fd != -1)
    {
        printf("file opened succesfully\n");
        close(fd);
    }
    else
    {
        printf("%s: cannot open the file\n", strerror(errno));
    }
    return 0;
}

答案 1 :(得分:4)

当您使用gcov profiling进行编译时,编译器会在您的函数中插入额外的代码,以跟踪已执行的代码。在粗糙的伪代码中,插入的代码将执行(除其他外):

if (!output_file_has_been_opened) {
    fd = open(output_filename, ...);
    check_ok(fd);
    output_file_has_been_opened = TRUE;
    track_coverage();
}

...所以如果输出文件尚未成功打开(如程序开头),它将尝试打开它。不幸的是,在这种情况下,将调用您的模拟open()函数 - 它具有相同的插入代码;由于文件仍未成功打开,并且由于gcov代码不知道发生了异常情况,因此它将再次尝试open()调用 - 这就是导致递归(以及最终的段错误)的原因,一旦堆栈耗尽)。

答案 2 :(得分:0)

您正在错误地传递va_list。尝试以下希望帮助

        r = my_open(fn, flags, args);

了解更多信息 http://c-faq.com/varargs/handoff.html