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

Time:12-24

I am trying to use sscanf() from preload.so which is generated from preload.c.

To check my sscanf() from preload.so is called, I added extra print statement: printf("test\n");

Is there something I am missing?

Files content mentioned below:

//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;
}

I am doing below steps:

# 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 I am getting:

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

The expected output is:

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

Even ldd output is pointing to preload.so as below, still while execution its giving preference to sscanf() of system and not the one from 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]#

Tweak here is, if I remove -D_GNU_SOURCE=1,it works, gcc -fPIC -shared preload.c -o preload.so -ldl But I can not proceed ahead without defining GNU_SOURCE as its must for future use .

As suggested by @Rachid K, it worked when I redefined my preload.c as below:

#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;
}

CodePudding user response:

Have a look at readelf -s foo.

I think it is likely that there is no call to sscanf in your executable. Assuming you are using GLIBC as libc, I suspect a call to __isoc99_sscanf. This is a redirection which the library makes, apparently because its original sscanf variant uses extensions conflicting with the standard, see this question.

If you look then at readelf -s preload.so as well, it will probably show a definition for sscanf.

The redirection happens via macro in stdio.h which you include in both, but I suspect _GNU_SOURCE=1 disables the redirection, so that even though stdio.h is included in preload.c, it is not replacing sscanf with __isoc99_sscanf there.

In the compilation of foo, you probably do not use -D_GNU_SOURCE=1 and therefore you are getting a mismatch between the symbol names.

Symbol interposition with LD_PRELOAD is always a bit tricky. In addition to issues such as above, there are also many situations in which compilers will optimize out or transform standard library calls. For example printf -> puts if the format string doesn't use any formatting.

CodePudding user response:

sscanf() may be a macro referencing an internal function. Have a look at <stdio.h>. For example, on my system, I have:

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

Hence, sscanf() is actually a macro referencing __isoc99_sscanf(). So, if you redefine the latter, you get what you expect.

#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;
}

After rebuild, you get:

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