无法使用通过 LD_PRELOAD 加载的动态库中的函数

Not able to use function from my dynamic library loaded via LD_PRELOAD

我正在尝试使用 preload.so 中的 sscanf(),它是从 preload.c 生成的.

为了检查来自 preload.sosscanf() 是否被调用,我添加了额外的打印语句:printf("test\n");

有什么我遗漏的吗?

文件内容如下:

//preload.c
#include <stdarg.h>
#include <stdio.h>
__attribute__((force_align_arg_pointer)) int sscanf(const char *str, const char *format, ...)
{
  int ret;
  va_list ap;
  va_start(ap, format);
  printf("test\n");
  ret = vsscanf(str, format, ap);
  va_end(ap);
  return ret;
}
//foo.c
#include <stdio.h>

int main(void)
{
        int i;
        sscanf("42", "%d", &i);
        printf("%d\n", i);
        return 0;
}

我正在执行以下步骤:

# gcc -fPIC -shared preload.c -o preload.so -ldl -D_GNU_SOURCE=1
# export LD_PRELOAD=$PWD/preload.so
# gcc foo.c -o foo
test
test
test
test
test
test
test
test
test
test
test

O/p 我得到:

# echo $LD_PRELOAD
/AMIT/sscanf_override/preload.so
# ./foo
42
# LD_PRELOAD=$PWD/preload.so ./foo
42

预期输出为:

$ gcc foo.c -o foo
$ LD_PRELOAD=$PWD/preload.so ./foo
test
42

即使 ldd 输出指向 preload.so,如下所示,仍然在执行时优先考虑系统的 sscanf() 而不是来自 preload.so

root@***sscanf_override]# ldd foo
        linux-vdso.so.1 (0x00007fff4a5e0000)
        /AMIT/sscanf_override/preload.so (0x00007f1cf270a000)
        libc.so.6 => /lib64/libc.so.6 (0x00007f1cf2345000)
        libdl.so.2 => /lib64/libdl.so.2 (0x00007f1cf2141000)
        /lib64/ld-linux-x86-64.so.2 (0x00007f1cf290c000)
[root@***sscanf_override]#

这里的调整是,如果我删除 -D_GNU_SOURCE=1,它会起作用,gcc -fPIC -shared preload.c -o preload.so -ldl 但是如果不将 GNU_SOURCE 定义为将来使用的必需项,我就无法继续。

正如@Rachid K 所建议的,当我如下重新定义我的 preload.c 时它起作用了:

#include <stdarg.h>
#include <stdio.h>
__attribute__((force_align_arg_pointer)) int sscanf(const char *str, const char *format, ...)
{
  int ret;
  va_list ap;
  va_start(ap, format);
  printf("test\n");
  ret = vsscanf(str, format, ap);
  va_end(ap);
  return ret;
}

__attribute__((force_align_arg_pointer)) int __isoc99_sscanf(const char *str, const char *format, ...)
{
  int ret;
  va_list ap;
  va_start(ap, format);
  printf("test\n");
  ret = vsscanf(str, format, ap);
  va_end(ap);
  return ret;
}

看看readelf -s foo

我认为您的可执行文件中很可能没有调用 sscanf。假设您将 GLIBC 用作 libc,我怀疑是对 __isoc99_sscanf 的调用。这是库进行的重定向,显然是因为其原始 sscanf 变体使用了与标准冲突的扩展,请参阅 this question.

如果您再查看 readelf -s preload.so,它可能会显示 sscanf.

的定义

重定向是通过 stdio.h 中的宏进行的,您在两者中都包含了宏,但我怀疑 _GNU_SOURCE=1 禁用了重定向,因此即使 stdio.h 包含在 [=19= 中],它没有用 __isoc99_sscanf 替换 sscanf

foo 的编译中,您可能没有使用 -D_GNU_SOURCE=1,因此符号名称不匹配。

LD_PRELOAD插入符号总是有点棘手。除了上述问题之外,还有很多情况下编译器会优化或转换标准库调用。例如 printf -> puts 如果格式字符串不使用任何格式。

sscanf() 可能是引用内部函数的宏。看看 。例如,在我的系统上,我有:

extern int __isoc99_sscanf (const char *__restrict __s,
                            const char *__restrict __format, ...) __THROW;
#  define fscanf __isoc99_fscanf
#  define scanf __isoc99_scanf
#  define sscanf __isoc99_sscanf

因此,sscanf()实际上是一个引用__isoc99_sscanf()的宏。所以,如果你重新定义后者,你会得到你所期望的。

#include <stdarg.h>
#include <stdio.h>

//__attribute__((force_align_arg_pointer)) int sscanf(const char *str, const char *format, ...)
int __isoc99_sscanf(const char *str, const char *format, ...)
{
  int ret;
  va_list ap;
  va_start(ap, format);
  printf("test\n");
  ret = vsscanf(str, format, ap);
  va_end(ap);
  return ret;
}

重建后,您将获得:

$ gcc -fPIC -shared preload.c -o preload.so -ldl -D_GNU_SOURCE=1
$ LD_PRELOAD=`pwd`/preload.so ./foo
test
42