Every embedded system developer should know by now, that using printf() is not a good thing for smaller systems. Printf() and the like are not only problematic from a code and data size perspective, they are infamous for vulnerability attacks too.

In this article I’ll show you multiple ways how to ban printf() or anything similar you want to avoid.
Search
The first obvious way is to search for ‘printf’ in your code base:

Obviously, this might get a lot of hits. The above search is not case sensitive, so it shows all kind of similar usage of printf() you might look into.
Linker Map File
There might be many calls to printf() in the application, but not all of them actually are used.
For that case, check the linker .map file: if the address/size of the object is non-zero, it is definitely used:

Image Info
Another way to find out usage of a symbol is using the ‘Image Info’, e.g. in the NXP MCUXpresso IDE:

Retarget (none) library
For embedded applications, the standard I/O which is used by printf() can be re-target, e.g. to a UART or using semihosting, which is usually implemented with a library variant. Use such a library and check if you still can link:

As described in Which Embedded GCC Standard Library? newlib, newlib-nano, …, using a ‘none’ library will not map the low level functions. As long as your application does not re-target them to something like a UART, then this is an easy way to catch printf()
usage indirectly.
Assert and printf()
Using asserts in the code is a good way to catch runtime errors. Some SDKs are using printf(
) in asserts which is questionable in my view. To test it if the asserts are the source of remaining printf()
‘s, you can set the NDEBUG macro:

Redefining linker symbols
To catch hidden usage of printf()
or any other undesired function, a working approach is to redefined the symbols in the linking phase.

With the GNU linker –defsym command I can redirect usage of a symbol to a custom one, for example
--defsym=printf="you_shall_not_use_printf"
That way, the linker will throw an error, because that symbol cannot be found. And if you define an empty function with that name, you can set a breakpoint and find out who might call it anyway.
Summary
Using printf()
and the likes can be very problematic for embedded applications: it can use lots of code, data and stack space, plus can cause a security threat.
I do not have and use printf()
in production code, and if I have to use variable argument list printing,then I use the better alternatives like XFormat. But it still might be used or linked in with libraries, including the C/C++ standard library one.
I have presented a few ways how to prevent using it, or finding out who is using it. I’m using the --defsym
way in different ways successfully to catch, isolate and remove usage of undesired standard library functions.
Happy linking 🙂
Links
- Format string vulnerability: https://www.invicti.com/blog/web-security/format-string-vulnerabilities/
- printf() format string attacks: https://owasp.org/www-community/attacks/Format_string_attack
- XFormat, a Lightweight printf() and sprintf() Alternative
- GNU gcc printf() and BuiltIn Optimizations
- Tutorial: Using a Terminal Input and Output; *without* printf() and scanf()
- Adding the Picolib C/C++ Standard Library to an existing GNU ARM Embedded Toolchain
- ARM SWO ITM Console Bidirectional Standard I/O Retargeting
- Which Embedded GCC Standard Library? newlib, newlib-nano, …
The double dash in –defsym seems to have been converted to a single dash or emdash or similar in some of the text. E.g.:
“GNU linker –defsym command
With the GNU linker –defsym command I can redirect usage of a symbol to a custom one, for example”
LikeLiked by 1 person
Maybe it’s the platform used here? Mine also got converted in the previous comment! :-)
LikeLiked by 1 person
Yes, I’m constantly struggling with the WordPress platform about such things, including having source code in the articles. It constantly tries to transform it into HTML format and syntax, which is very annoying. 😦
LikeLike
Hi Tommy,
maybe I have found a way to tell WordPress not to change the double dashes into a single long one. At least now it looks OK for me. Is it fixed for you too?
Thanks!
LikeLike
Testing double dash –defsym
LikeLike
Still doesn’t work for me it seems.
LikeLike
I had to mark it as ‘code’ in the article. As for commenting, if I mark it as code:
--double-dash-test
should work too.
LikeLike
It’s best to avoid printf() altogether when developing embedded or small hardware systems. Replace the urge to throw down “hello world” breadcrumbs instead with the toggling of lights or other signals using unoccupied GPIO lines. You can even blink once for good, twice for bad, thrice for X, etc.
If one really needs formatted output, separate the “Swiss Army” knife style of printf() into lots of little single-mode functions — aptly named — and let the logic of program design dictate which format and function to use when, rather than relying upon a function’s runtime decision. A bit of redundancy saves a world of grief.
LikeLiked by 1 person
Hi Paul,
100% agree! I do use the McuLib for string manipulation (strcat, strcpy, printing, …) not only to avoid printf() and the likes, but evenly important to have routines which always terminate the string with a zero byte plus with the provided size parameter they do not overflow the buffer. It might be a few more coding lines to build the string and put it out, but at the end it uses less code, less memory and is safe too.
LikeLike
Add to this list : printf() is awfully slow, which means it is processor power hungry.
LikeLike
I also agree with the “don’t use *printf in embedded” rule of thumb. But … there are several tiny standalone *printf implementations that are not slow, bloated or use heap that are a good alternative if such functionality is needed. E.g.
https://embeddedartistry.com/blog/2019/11/06/an-embedded-friendly-printf-implementation/
https://github.com/eyalroz/printf
https://github.com/mpaland/printf
https://github.com/cjlano/tinyprintf
https://www.menie.org/georges/embedded/small_printf_source_code.html
LikeLike
Yes, agreed, it all depends about the amount of features and formatting strings it supports. I’m using XFormat (https://mcuoneclipse.com/2014/08/17/xformat-a-lightweight-printf-and-sprintf-alternative/) in most of my projects. And the implementation in Picolib (https://mcuoneclipse.com/2023/02/09/adding-the-picolib-c-c-standard-library-to-an-existing-gnu-arm-embedded-toolchain/) is much smaller than the one in Newlib too.
LikeLike
Yes, indeed. And because more code and RAM needed, it might need a bigger CPU/MCU which likely will consume more power too.
LikeLike
Erich,
I would add that the primary reason printf is so resource wasteful is because it is one of the few functions in C with a variable-length argument list, which is not that critical to embedded systems. The reason people use it (and scanf) is to convert numbers to/from binary representation from/to a human-readable form. To that end I developed my own set of conversion functions years ago, totally obviating the need for printf.
LikeLike
Hi Gary,
the variable length thing is not much contributing to the overhead. What contributes is all the different formatting possibilities (floating point, alignment, hex, size, lengths, …) which need to be there even if not actually used. The other part is that it uses down the layers hooks to write out the data, possibly including dealing with file I/O.
And yes, we have built up such conversion functions and utility routines to cover the needs in our applications, and they are available in a ‘McuUtility’ module.
LikeLike
If you are using GCC then you can use
#pragma GCC poison printf sprintf …
Note this only poisons identifiers used after the pragma, see the GCC docs.
LikeLike
Hi Graham,
I was not aware of that pragma, thanks for sharing!
To make it ‘global’, one can simply use the -include option: https://mcuoneclipse.com/2019/02/23/different-ways-of-software-configuration/
LikeLike
You can also (re)define printf in a header with __attribute__((error(“do not use printf”)))
The linker will error out if anything links to this symbol then.
LikeLike
That’s actually a good idea too, thanks for sharing.
LikeLike
One typo: “ can cause a security thread”
LikeLike
thanks, fixed 🙂
LikeLike
what about sprintf?
LikeLike
sprintf shares the same overhead as printf for the formatting string. It is just better in the sense that it stores things into a buffer than writing things to a standard I/O channel which might cause reentrancy issues.
and most important: use snprintf, not sprintf.
LikeLike
can you review nanoprintf https://github.com/charlesnicholson/nanoprintf
LikeLike
I think there is good information on the wiki already about it and how to use it?
LikeLike