【问题标题】:LD_PRELOAD-ed shared object traps read(), but not open().. Why?LD_PRELOAD-ed 共享对象陷阱 read(),但不是 open().. 为什么?
【发布时间】:2015-03-18 18:03:22
【问题描述】:

我在 Linux(在我的情况下是 Debian)上拦截 open() 时遇到问题。这是一个内置到共享对象中的简约 C 源代码:

/* Defines are needed for dlfcn.h (RTLD_NEXT) */
#define __USE_GNU
#define _GNU_SOURCE

#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <dlfcn.h>
#include <unistd.h>

int open(char const *path, int oflag, ...) {
    int (*real_open)(char const *, int, ...);
    char *msg;
    va_list args;
    int mflag;

    fprintf(stderr, ">>>>> in open <<<<<<\n");
    real_open = dlsym(RTLD_NEXT, "open");
    if ( (msg = dlerror()) ) {
        fprintf(stderr, "dlsym error: %s\n", msg);
        exit(1);
    }
    va_start(args, oflag);
    mflag = va_arg(args, int);
    return real_open(path, oflag, mflag);
}

ssize_t read(int fd, void *buf, size_t count) {
    ssize_t (*real_read)(int, void*, size_t);
    char *msg;

    fprintf(stderr, ">>>>> in read <<<<<\n");
    real_read = dlsym(RTLD_NEXT, "read");
    if ( (msg = dlerror()) ) {
        fprintf(stderr, "dlsym error: %s\n", msg);
        exit(1);
    }
    return real_read(fd, buf, count);
}

共享对象的构建使用:

cc -c -fPIC -Wall funcs.c
cc -shared -o libfuncs.so funcs.o -ldl -lc

现在当我尝试

export LD_PRELOAD=/path/to/libfuncs.so
cat somefile

然后我只在输出中看到read() 的跟踪,即&gt;&gt;&gt;&gt;&gt; in read &lt;&lt;&lt;&lt;&lt;。我从来没有看到open() 的踪迹。我检查了 cat Makefile 使用 strace 做了什么,果然 - open()read() 都被调用了:

open("Makefile", O_RDONLY|O_LARGEFILE)  = 3
fstat64(3, {st_mode=S_IFREG|0644, st_size=213, ...}) = 0
read(3, "testrun: libfuncs.so\n\tLD_PRELOAD"..., 32768) = 213

顺便说一句,我还检查了其他程序,例如 odbash,从未拦截过 open()。这里发生了什么?提前谢谢...

`

【问题讨论】:

  • 两种可能性。 1) 未正确使用库,请参阅手册页以获取示例。 2)open函数其实是OS或者SHELL中内置的宏。

标签: c linux shared-libraries ld-preload


【解决方案1】:

为了补充 shooper 的答案,glibc 在编译调用 open() 的应用程序时,似乎执行了从 open()open64() 的程序集级重命名,例如

$ gcc -D_FILE_OFFSET_BITS=64 open.c -o open

在上述命令中添加-S 并检查open.s 中的程序集源代码表明open64() 确实被调用了。 $ gcc -E ... 表明它不仅仅是基于预处理器的重命名。

预处理后的源代码有这个位,它执行重命名:

extern int open (const char *__file, int __oflag, ...) __asm__ ("" "open64")
     __attribute__ ((__nonnull__ (1)));

负责生成该声明的代码位于 /usr/include/fcntl.h

#ifndef __USE_FILE_OFFSET64
extern int open (const char *__file, int __oflag, ...) __nonnull ((1));
#else
# ifdef __REDIRECT
extern int __REDIRECT (open, (const char *__file, int __oflag, ...), open64)
     __nonnull ((1));
# else
#  define open open64
# endif
#endif
#ifdef __USE_LARGEFILE64
extern int open64 (const char *__file, int __oflag, ...) __nonnull ((1));
#endif

__REDIRECT 的声明是这里使用的。

如果这是真正的问题,我的猜测是 glibc 反过来将open64() 调用转换为您在strace 中看到的open("Makefile", O_RDONLY|O_LARGEFILE) 系统调用。由于您只能覆盖函数而不是系统调用本身,因此您应该尝试open64(),而不是像 shooper 建议的那样。

(顺便说一句,我不确定 glibc 为什么会这样做,如果是出于其他原因,而不仅仅是为了避免使用宏。)

【讨论】:

    【解决方案2】:

    也许尝试“open64”而不是“open”?

    【讨论】:

      猜你喜欢
      • 2011-12-13
      • 1970-01-01
      • 2020-01-01
      • 2011-02-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多