问题
I know how to overwrite C-lib functions in just simple sample. But what I need is IN A REAL PROJECT which has hundreds of files that call fprintf.
In each file, there's a "#include < stdio.h >", and tens or hundreds of calls of fprintf. I wanna make all these fprintf to do my own job. I cannot remove the "stdio.h" and put a "#include < myprint.h >", where myprint.h defines the real function or macro of fprintf that do my own job. "stdio.h" has many other calls in the project. I want a simple enough solution.
Thanks!
Do I make my question clear enough?...
Update on 2014.03.08: Salute to all women at first...
Please see my 2nd post below.
回答1:
If you are using gcc for example, you can invoke it in the following way:
gcc -include myheader.h -Dfprintf=myfprintf file.c
And that will include myheader.h before every other include header in file.c and will redefine fprintf as myfprintf.
You can find more details here
回答2:
Question update overview: I added GCC option to CMAKE, but it seems not working.
====== SOLUTION TO MY FIRST POST ======
Hi all,
As for the question in my first post, I found a hook solution as follows:
#define _GNU_SOURCE
// for time() and localtime()
#include <time.h>
// for fprintf/fwrite
#include <stdio.h>
// for va_* functions
#include <stdarg.h>
// for dlsym
#include <dlfcn.h>
// compile command line: gcc -shared -Wl,--no-as-needed -ldl -fPIC -Wall hooktest.c -o hooktest.so
// how to use: LD_PRELOAD=hooktest.so ./hooktestprog, where hooktestprog includes stdio.h and calls fprintf
// a helper function
void print_time(FILE *fp, const char *format, ...)
{
va_list args;
va_start(args, format);
vfprintf(fp, format, args);
va_end(args);
}
// GCC optimizes fprintf to fwrite
// to make original program use fprintf exactly, compile the program (not this library) with -fno-builtin-fprintf
int fprintf(FILE *stream, const char *format, ...)
{
static int (*my_fprintf)(FILE *stream, const char *format, ...) = NULL;
if (NULL == my_fprintf)
{
my_fprintf = (int (*)(FILE *stream, const char *format, ...))dlsym(RTLD_NEXT, "fprintf");
}
va_list args;
va_start(args, format);
time_t tNow = time(NULL);
struct tm *tmNow = localtime(&tNow);
print_time(stream, "%04d/%02d/%02d %02d:%02d:%02d === ", tmNow->tm_year+1900, tmNow->tm_mon+1, tmNow->tm_mday, tmNow->tm_hour, tmNow->tm_min, tmNow->tm_sec);
my_fprintf(stream, format, args);
va_end(args);
return 0;
}
// GCC optimizes fprintf to fwrite
// to make original program use fprintf exactly, compile the program (not this library) with -fno-builtin-fprintf
/* size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream)
{
static size_t (*my_fwrite)(const void *, size_t, size_t, FILE *) = NULL;
if (NULL == my_fwrite)
{
my_fwrite = (size_t (*)(const void *, size_t, size_t, FILE *))dlsym(RTLD_NEXT, "fwrite");
}
time_t tNow = time(NULL);
struct tm *tmNow = localtime(&tNow);
print_time(stream, "%04d/%02d/%02d %02d:%02d:%02d === ", tmNow->tm_year+1900, tmNow->tm_mon+1, tmNow->tm_mday, tmNow->tm_hour, tmNow->tm_min, tmNow->tm_sec);
my_fwrite(ptr, size, nmemb, stream);
FILE *fp = fopen("/home/idesclient/log_freerdp.log", "a");
if (NULL != fp)
{
print_time(fp, "%04d/%02d/%02d %02d:%02d:%02d === ", tmNow->tm_year+1900, tmNow->tm_mon+1, tmNow->tm_mday, tmNow->tm_hour, tmNow->tm_min, tmNow->tm_sec);
my_fwrite(ptr, size, nmemb, fp);
fclose(fp);
}
return nmemb;
} */
I wanna override fprintf, but GCC optimizes fprintf to fwrite. You can check this by writing a sample that calls fprintf, compile it, and use the command "nm yoursample | grep @GLIBC_2.0". you can see something like "fwrite@GLIBC_2.0", and there is NO "fprintf@GLIBC_2.0".
But I cannot override fwrite because there are other fwrite calls in my project. As you can see, I commented out fwrite().
I found another solution: add "-fno-builtin-fprintf" while compiling a sample program, and now the sample works. NOTE: only the sample works, not my project works... The command line is as follows:
>gcc -shared -Wl,--no-as-needed -ldl -fPIC -Wall hooktest.c -o hooktest.so
>gcc -fno-builtin-fprintf sample.c -o sample # where sample.c calls fprintf
>LD_PRELOAD=./hooktest.so ./sample
Now I can see fprintf adds time information - fprintf is hooked.
====== NEW QUESTION STARTS HERE ======
By now, it seems I can add "-fno-builtin-fprintf" to my toplevel CMakeLists.txt (the project has many subdirectories, it's quite a complex project), and everythind SHOULD work. But in real practice, it does not.
I have the following lines in my toplevel CMakeLists.txt:
if(CMAKE_COMPILER_IS_GNUCC)
xxxxxx (this is the original definitions in my project)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fno-builtin-fprintf")
endif()
But after I compile the project, use "LD_PRELOAD=/xxx/hooktest.so /xxx/project/bin/somebinary", fprintf is not hooked.
I do another test. In my C code above, I comment fprintf and uncomment fwrite (GCC optimizes fprintf to fwrite, see expaination above), and remove the "-fno-builtin-fprintf" in the toplevel CMakeLists.txt. I can see fprintf hooked to fwrite in my code, but only fprintf(stderr, "xxx") hooked, but fprintf(stderr, "xxx %s %d xxx", somestring, someinteger) not hooked. It seems that fprintf with % and more than 2 arguments cannot be hooked.
Please help to debug this. Thanks!
来源:https://stackoverflow.com/questions/22241757/how-to-override-fprintf-in-the-c-library-how-to-add-gcc-option-to-toplevel-cmak