GNU Linker Wizardry: Wrapping printf() with Timestamps

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:

printf() calls with added timestamps

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 between wrap and printf. 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
GNU ld option to wrap printf

With this, all calls to printf() go through the wrapper:

Wrapped printf() calls

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 🙂

Links

2 thoughts on “GNU Linker Wizardry: Wrapping printf() with Timestamps

  1. 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.

    Like

What do you think?

This site uses Akismet to reduce spam. Learn how your comment data is processed.