I tried to use LD_PRELOAD to hook sprintf
function, so I will print to file the result of buffer:
#define _GNU_SOURCE
#include <stdio.h>
#include<dlfcn.h>
int sprintf (char * src , const char * format , char* argp)
{
int (*original_func)(char*,const char * , char*);
original_func = dlsym(RTLD_NEXT,"sprintf");
int ret = (*original_func)(src ,format,argp);
FILE* output = fopen("log.txt","a");
fprintf(output,"%s \n",src);
fclose(output);
return ret;
}
When I compile this code gcc -Wall -fPIC -shared -o my_lib.so test_ld.c -ldl
I got error
test_ld.c:5:5: error: conflicting types for ‘sprintf’
int sprintf (char * src , const char * format , char* argp)
^
In file included from test_ld.c:2:0:
/usr/include/stdio.h:364:12: note: previous declaration of ‘sprintf’ was here
extern int sprintf (char *__restrict __s,
How can I fix that?
Alex's first solution nicely addresses one problem: the conflicting declarations of sprintf
(although there is no reason to not use the same signature as in stdio.h
, see dbush's answer). However, even then there remains one large elephant in the room: sprintf
is a variadic function .
That means that, whenever the wrapped program calls sprintf
with anything other than one char *
third argument, your output may not be correct (and may even depend on your compiler's -O
level)
Calling variadic functions from variadic functions (what is essentially what you're doing here) is a known problem . Any solution will be non-portable. With gcc
, you can use __buitlin_apply
and tap into gcc
s own private way of handling argument lists:
/* sprintf.c, compile with gcc -Wall -fPIC -shared -o sprintf.so sprintf.c -ldl
and use with LD_PRELOAD=./sprintf.so <program> */
#define _GNU_SOURCE
#define sprintf xsprintf
#include <stdio.h>
#include<dlfcn.h>
#undef sprintf
# define ENOUGH 100 /* how many bytes of our call stack
to pass to the original function */
int sprintf (char *src) /* only needs the first argument */
{
void *original_func = dlsym(RTLD_NEXT,"sprintf");
void *arg = __builtin_apply_args();
void *ret = __builtin_apply((void *)original_func, arg, ENOUGH);
FILE* output = fopen("log.txt","a");
fprintf(output,"%s \n",src);
fclose(output);
__builtin_return(ret);
}
A few remarks:
va_list
argument instead of a variable number of arguments. In that case (and sprintf
- vsprintf
is such a case) you can use Alex's (portable) second solution with the va_*
macros. If not, the solution with __builtin_apply()
is the only possible, albeit gcc
-specific, one.main.c
with the -O2
flag, main()
will actually call __sprintf_chk()
instead of sprintf()
(regardless of -fno-builtin
) and the wrapper won't work . To demonstrate the wrapper, compile main.c
with -O0
. Changing the main program to get the wrapper to work is the tail wagging the dog, of course. This shows the fragility of building wrappers: programs often don't call the library functions you expect . A ltrace <program>
beforehand can save a lot of work....The main problem you're having is that your prototype for sprintf
doesn't match the official one. Your function has this signature:
int sprintf (char * src , const char * format , char* argp);
While the official one has:
int sprintf(char *str, const char *format, ...);
You'll need to change your function to have this signature. Once you do that, you'll need to use a va_list
to get the variadic argument. Then you would use that to call vsprintf
which takes an argument of this type instead of using dlsym
to load sprintf
.
#include <stdio.h>
#include <stdarg.h>
int sprintf (char * src , const char * format , ...)
{
va_list args;
va_start(args, format);
int ret = vsprintf(src, format, args);
va_end(args);
FILE* output = fopen("log.txt","a");
fprintf(output,"%s \n",src);
fclose(output);
return ret;
}
You can rename the symbol in stdio, but then there's another problem, compilers like gcc use built-in implementations, unless you pass a flag like -fno-builtin
, the compiler will generate the code inline in the executable, it won't link any library for functions like sprintf.
sprintf.c:
#define _GNU_SOURCE
#define sprintf xsprintf
#include <stdio.h>
#include<dlfcn.h>
#undef sprintf
int sprintf (char * src , const char * format , char* argp)
{
int (*original_func)(char*,const char * , char*);
original_func = dlsym(RTLD_NEXT,"sprintf");
int ret = (*original_func)(src ,format,argp);
FILE* output = fopen("log.txt","a");
fprintf(output,"%s \n",src);
fclose(output);
return ret;
}
main.c:
#include <stdio.h>
int main(int argc, char *argv[]) {
char buffer[80];
sprintf(buffer, "hello world");
puts(buffer);
return 0;
}
Makefile:
all: libsprintf.so main
main: main.c
gcc -Wall -O2 -fno-builtin -o main main.c
libsprintf.so: sprintf.c
gcc -Wall -O2 -shared -fPIC -fno-builtin -o libsprintf.so sprintf.c -ldl
.PHONY: clean
clean:
-rm -f main libsprintf.so
usage:
make
LD_PRELOAD=./libsprintf.so ./main
edit
sprintf.c with variadic implementation (it doesn't call sprintf):
#define _GNU_SOURCE
#define sprintf xsprintf
#include <stdio.h>
#include<dlfcn.h>
#undef sprintf
#include <stdarg.h>
int sprintf (char * src , const char * fmt , ...)
{
va_list args;
va_start(args, fmt);
int ret = vsprintf(src, fmt, args);
va_end(args);
FILE* output = fopen("log.txt","a");
fprintf(output,"%s \n", src);
fclose(output);
return ret;
}
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.