无法使用通过 LD_PRELOAD 加载的动态库中的函数
Not able to use function from my dynamic library loaded via LD_PRELOAD
我正在尝试使用 preload.so 中的 sscanf()
,它是从 preload.c 生成的.
为了检查来自 preload.so 的 sscanf()
是否被调用,我添加了额外的打印语句: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
我正在尝试使用 preload.so 中的 sscanf()
,它是从 preload.c 生成的.
为了检查来自 preload.so 的 sscanf()
是否被调用,我添加了额外的打印语句: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