avr-gcc might replace a call to printf by a call to puts provided the arguments of printf allows to do so. You can avoid this optimization by means of -fno-builtin-printf which takes away any knowledge (except prototype information) about printf from the compiler. This will result in a call to printf (which you are using anyway), saving the call to fputs.

If that's the case, why provide an optimized version of printf() for constant strings at all? I guess maybe it happens inevitably due to compiler machinations.

Anyway, at least one person (me) hit this issue and it took me some time to find the culprit. I set LDFLAGS for the printf_min long ago with the idea of generally keeping program size low, and this turns out to be a very poor general policy. The current docs don't give any indication of this, while they do describe smaller savings related to vfprintf choice in the presence of printf(). That's inconsistent and somewhat misleading.

Well, the fact that the -u vfprintf needs to be forced is
an unfortunate side effect of libprintf_min.a only containing
a different definition of vfprintf(), but not e.g. for printf()
or other members of the family.

Thus, if you only add -lprintf_min, this library will be
processed by a time when the linker has not seen any obvious
need to drag the vfprintf symbol in from it, so in effect, it
will be ignored. Later on, when it comes to resolve the
symbols needed by (e.g.) printf, it will suddenly also need
vfprintf, but libprintf_min.a is already done by that time,
so the standard version of vfprintf is used (from libc.a).

We could probably reorganize these libraries to also contain
the other printf family members, but 1) that needs to be done
by someone (who?), and 2) it won't fix the history, so the
documentation has to remain the way it is for at least a
while (a couple of years or more).

While the complaint this bug is focussing on is not
completely invalid, I think it's practically a non-issue:
except for the degraded testcase, nobody would want to drag
in the full power (and bloat) of printf() & co. if all he
wants to do is printing some constant strings without using
any of the formatting functionality. As soon as a real
printf functionality is needed, as seen, there is indeed a
minimal saving of space by libprintf_min.a.

"Note that if your program doesn't end up calling vfprintf(), using the printf_min linker flags will increase code size because it forces the vfprintf() function to be included even though it isn't used."

It's only when both options are used together that you get that behaviour.

I think using -Wl,-u,vfprintf is incorrect and there is no need to use that option unless you want to keep unused code in your image.

Personally, I'd find the behaviour of keeping unused code in the image to be the confusing behaviour, not the fact that my debug builds are considerably larger than my release builds.

You're correct, there's no reason when printf isn't being used to use those linker options. However:

realistically at least some linker options are at least somewhat black magic to many users.

printf() is particularly likely to go from unused to used during debugging, during which large swings in code size may be extra confusing

For these reasons, I still think adding something to the documentation would make sense. It could be as simple as one sentence explaining what you just explained:

"Note that if your program doesn't end up calling vfprintf(), using the printf_min linker flags will increase code size because it forces the vfprintf() function to be included even though it isn't used."

Ok, with printf("foo: %d\n, 42), the program is indeed smaller with by 264 bytes when -Wl,-u,vfprintf -lprintf_min is used than when none of the printf linker flags are used.

I guess this is a wishlist item really: it would be nice if using printf_min didn't end up adding 1000 or so bytes to the program size when variadic vfprintf() invocations don't happen at all.
I suppose given the realities of how the linker works that's unlikely. At least the issue deserves a mention in the discussion of these options in the vfprintf() documentation (that they can end up making your program a lot bigger, rather than smaller).

The documentation seems to be saying that using those flags is supposed to save flash, but without them, a main() that does a single printf("foo\n"); gives me a 330 byte flash, while with them I get a 1330 byte flash.

If I remove the printf() call, I get flash sizes of 1228 or 228 with or without the above mentioned printf-min flags, respectively.

It looks like somehow the printf-min flag ends up dragging in a bunch extra code somehow?