For embedded targets with small memories and static linking, the size of functions like *printf and their dependencies is painful, particularily for targets that need software floating point.
avr-libc has long had a printf / scanf implementation that by default does not include floating point support. There's a library that can be liked to provide the floating-point enabled functions, but the required functions have to be pulled in manually with -Wl,-u if they are otherwise only referenced from libc, lest these symbols got resolved with the integer-only implementations from libc itself. All in all, a rather unsatisfying state of affairs when trying to run the gcc regression test suite. Newlib also has an integer-only printf implementation, but in this case, the default is the other way round - you have to use functions with nonstandard names to use the integer-only implementations. And a half-hearted approach to use this can easily end up with linking in both the integer-only version and the floating-point enabled one, resulting in increased executable size instead of a saving. I think we can do better with integrated compiler/linker support. Trying to do a perfect job i of course impossible because of Rice's theorem, but it doesn't have to be perfect to be useful. Just looking statically at each *printf statement, we can look at the format strings and/or the passed arguments. Floating point arguments are easier to check for by the compiler than parsing the format string. There is already code that parses the format strings for the purpose of warnings, but it would be a somewhat intrusive change to add this functionality there, and the information is not available where a variable format is used anyway. In a standards-conformant application, floating point formats can only be used with floating point arguments, so checking for the latter seems most effective. So my idea is to make the compile emit special calls when there are no floating point arguments. A library that provides the floating point enabled *printf/*scanf precedes libc in link order. Libc contains the integer-only implementations of *scanf/*printf, in two parts: entry points with the special function name, which in the same object file also contain a reference to the ordinary function name, and another object file with the ordinary symbol and the integer-only implementation. Thus, if any application translation unit has pulled in a floating-point enabled implementation, this is the one that'll be used. Otherwise, the integer-only one will be used. Use of special sections and alphasorting of these in the linker script ensures that the integer-only entry points appear in the right place at the start of the chosen implementation. If vfprintf is used I've implemented this for AVR with these commits: https://github.com/embecosm/avr-gcc/commit/3b3bfe33fe29b6d29d8fb96e5d57ee025adf7af0 https://github.com/embecosm/avr-libc/commit/c55eba74838635613c8b80d86a85ed605a79d337 https://github.com/embecosm/avr-binutils-gdb/commit/72b3a1ea3659577198838a7149c6882a079da403 Although it could use some more testing, and thought how to best introduce the change as to avoid getting broken toolchains when components are out-of-sync. Now Joerg Wunsch suggested we might want to facto out more pieces, like the long long support. This quickly leads to a combinatorial explosion. If we want to support a more modular *printf / *scanf, than maybe a different approach is warranted. Say, if we could give a symbol and section attribute and/or pragma to individual case labels of a switch, and put the different pieces into separate object files (maybe with a bit of objcopy massaging). The symbols references to trigger the inclusion of the case objects could be generated by the gcc backend by processing suitably annotated function calls. E.g. we might put something into CALL_FUNCTION_USAGE, or play with TARGET_ENCODE_SECTION_INFO.