If one is using a dedicated logger module like the McuLog, then you don’t have to worry or care about timestamp support. But if your application is using normal printf()
calls for for logging purpose, you will face issues to adding timestamps to it. You might consider to change all prinft()
calls. This might be a lot of work, or not possible in all cases if you cannot change the source code.
But there is a really cool feature of the GNU linker to solve that problem. It allows to ‘wrap’ around any symbol or function, including the ones in the standard library. That way I can add my mode to the printf(
) code as a wrapper, for example adding a timestamp for every call.
In the example below you can see this in action:
In this article I’ll show how you can wrap any function with custom code.
Outline
If one needs to extend printf() or the like, a simple approach would be to search and replace all calls to it and direct them to our own routine. But this would mean changing many lines of code, and maybe things are inside a library for which we do not have the source code.
A better solution is to use the GNU linker to do the job. That way the source does not need to be changed. All what I have to provide is a wrapper routine plus direct the GNU linker to do the mapping. That way I can map or remap all calls of printf
to a custom implementation. I have used such a related approach in “You_Shall_Not_Use_Printf: How to make sure no printf() is used“.
The solution is to use the GNU linker –wrap option. With this, I can wrap an existing implementation with a new one, and the ‘wrapped’ implementation is still available.
GNU ld –wrap symbol option
From the GNU ld manual pages:
--wrap symbol
Use a wrapper function for symbol. Any undefined reference to symbol will be resolved to __wrap_symbol
. Any undefined reference to __real_symbol
will be resolved to symbol. This can be used to provide a wrapper for a system function. The wrapper function should be called __wrap_symbol
. If it wishes to call the system function, it should call __real_symbol
. Here is a trivial example:
void * __wrap_malloc (int c) { printf ("malloc called with %ld\n", c); return __real_malloc (c); }
Wrapper
With this, I can define a wrapper function for printf()
, and even call the orginal ‘real’ printf()
code from it. Below is an implementation I’m using to add a timestamp in front of every printf()
call. The timestamp value itself is updated by a timer and used as ‘prefix’ for every printf()
output.
/* wrapper.c: wrapper for the standard library printf() implementation */
#include <stdio.h>
#include <stdarg.h>
extern unsigned int timestamp;
int __real__printf(const char *fmt, ...);
int __wrap__printf(const char *fmt, ...) {
unsigned int t = timestamp;
va_list args;
int count0, count1;
count0 = __real__printf(">>%d.%d: ", t/1000, t%1000);
va_start(args, fmt);
count1 = vprintf(fmt, args);
va_end(args);
return count0+count1;
}
💡 Note that in my case (MCUXpresso IDE 11.7.0 with RedLib Semihosting) the call to
printf()
is_printf()
, so that’s why my wrapper has two underscores betweenwrap
andprintf
. Depending on your standard library, you might use the printf symbol instead.
Linker –wrap Option
Finally, I need to tell the linker about the wrapping. For the above case I add the following option to the linker:
-Wl,--wrap=_printf
With this, all calls to printf()
go through the wrapper:
Summary
I really like the GNU linker -wrap option: it allows me to wrap around any function, even the one provided by the standard library. Need a custom malloc()
, strlen()
or anything else: the wrapper will solve it. Using the linker –wrap option is a good solution to replace completely a standard library or any other function or symbol.
Happy wrapping 🙂
one more thing one reads on the manual page without thinking. Thanks for this nice use case.
And I always thought about it to create maximum confusion 😉
BTW: lld has this wrapping as well.
LikeLike
Thanks for the note about lld! I kind of expected that it has a similar thing implemented, but as I’m currently not actively using it, I have not checked lld.
LikeLike