Close

AVRs, stdio, printf, etc...

A project log for limited-code hacks/ideas

When you're running out of code-space... sometimes yah's gots to hack. Here are some ideas.

eric-hertzEric Hertz 11/25/2016 at 11:560 Comments

Look here... There's no way you're going to fit printf, or any of its incarnations, in 1kB...

Whether you knew that already, or not, the problem is, the default options may include it, anyhow... Even if you don't explicitly "#include <stdio.h>" anywhere in your code.

So, you've gotta get rid of it!

Unfortunately, this ain't for the faint-of-heart...

----------

First things, first... You have to Make Certain you're not using stdio.h *anywhere* in your code...

This may not be easy to do... it's entirely plausible some of the libraries you use *might* make use of it... Or, it's equally possible that by #including something *else* that stdio.h will be inherently-included.

I mean, basically what it boils down to is this: if you use formatted-text, anywhere, then most-likely you're using printf, or one of its variants (sprintf, etc), plausibly indirectly...

Can't help yah, much, here... but here's what I do:

Try adding this to your makefile:

CFLAGS += -E -dM

(put it at the top, unless "CFLAGS =" is somewhere below... which'd overwrite it...)

Then, when you compile (e.g. run 'make'), you won't get an executable, but instead you'll get the Preprocessor's output. This'll be in your *.o files...

Most Likely the "build" will "fail"... because, it's trying to create an executable from .o files which don't contain executable code. Just ignore that, this is exactly what we want to happen.

Now, search for all the *.o files, and look for _STDIO_H_...

Here's one way to do that... In the project-folder execute the following (in BASH):

find ./ -name \*.o -exec grep -H STDIO {} \;
That'll search for all the files ending in .o, and for each one found, search within those files for "STDIO"

Here's a result indicating that my main.o file somehow makes use of stdio...

$ find ./ -name \*.o -exec grep -H STDIO {} \;
./_BUILD/main.o:#define _STDIO_H_ 1

But, note, this does NOT indicate that main.c necessarily contains "#include <stdio.h>" Just that one of the files included within main.c (or one of the files included by one of those included files) contains "#include <stdio.h>". So you might have to dig through all those #includes... In my case, "#include <stdio.h>" is in main.h, and main.c contains '#include "main.h"'.

(I think there's an easier way to do this, I vaguely recall a preprocessor-option (like the '-E -dM' added earlier) that shows a hierarchy of #includes, but I can't recall it right now).

----------

So... I lucked out, I don't actually make use of the functions provided by stdio.h (e.g. printf), AND the only location of "#include <stdio.h>" is within my own code which compiles into main.o, I just need to locate it (again, turns out it's in main.h), and remove that line...

But, again, this may occur in various other libraries, etc... And those libraries might require it... and therein I can't help you.

------

But here's the kicker:

Even though removing that line from main.h results in the above search resulting in *zero* matches... stdio *may very well* still be linked into your project, using up a LOT of program-space for something that's never even used.

This inclusion of the stdio-library occurs, most-likely, somewhere in your makefiles...

Here's a snippet I wrote up in my own custom makefiles... This is long-since forgotten-knowledge, so I hope it's pretty explanatory, or at least gives some idea of where to look for more info.

This is from my file avrCommon.mk:

# From the avr-libc user-manual:
# (http://www.nongnu.org/avr-libc/user-manual/group__avr__stdio.html)

# " Since the full implementation of all the [features of vfprintf] becomes
# " fairly large, three different flavours of vfprintf() can be selected
# " using linker options. The default vfprintf() implements all the 
# " mentioned functionality except floating point conversions. A minimized
# " version of vfprintf() is available that only implements the very basic
# " integer and string conversion facilities, but only the '#' additional 
# " option can be specified using conversion flags (these flags are parsed
# " correctly from the format specification, but then simply ignored). 
# " This version can be requested using the following compiler options:
# "   -Wl,-u,vfprintf -lprintf_min
# " If the full functionality including the floating point conversions is 
# " required, the following options should be used:
# "   -Wl,-u,vfprintf -lprintf_flt -lm

# So, if NONE of the following are defined: 
#  AVR_MIN_PRINTF, AVR_FLOAT_PRINTF, nor AVR_NO_STDIO
#   Then we will get the default version of stdio/vfprintf, etc.
#   The Default is slightly larger than printf_min, 
#   but also slightly smaller than printf_flt
#
# But, this can be problematic if you're not using *any* references to 
#  functionality provided by stdio
#
# If Anywhere within your source-code "#include <stdio.h>" is mentioned,
# EVEN IF YOU DON'T USE ITS FUNCTIONS
# the default (and somewhat large) stdio-library WILL BE LINKED-IN.
#
# So, you might think choosing printf_min is the right way to go...
#  but, even then, if stdio's functionality is *unused*
#  you'll be linking-in a bunch of (albiet smaller) *unused* functions

# So far, these options are only to be set in the project's makefile
# And setting AVR_NO_STDIO is risky if stdio.h is actually included
# Since it compiles the default version, which is larger than min...
# Which also doesn't have floating-point support.
ifneq ($(AVR_NO_STDIO), TRUE)
ifeq ($(AVR_MIN_PRINTF), TRUE)
LDFLAGS += -Wl,-u,vfprintf -lprintf_min
else
ifeq ($(AVR_FLOAT_PRINTF), TRUE)
# Floating point printf version (requires -lm below)
LDFLAGS +=  -Wl,-u,vfprintf -lprintf_flt
endif
endif
endif

# So put this in your makefile (assuming it references this one):
# and uncomment one...

#Only one is paid-attention-to, in decreasing priority...
#AVR_NO_STDIO = TRUE
#AVR_MIN_PRINTF = TRUE
#AVR_FLOAT_PRINTF = TRUE

# NOTE that if AVR_NO_STDIO is true AND you make no reference to stdio.h
# anywhere in your code, or its dependencies...
# Then code-size should be smaller than choosing MIN_PRINTF, because
# vfprintf will not be linked in at all

# HOWEVER: If AVR_NO_STDIO is TRUE AND stdio.h is referenced (maybe by
# mistake?) then code-size will be *larger* than having chosen MIN_PRINTF
# Because the Default vfprintf will be used.

# It's confusing and hokey.

# Example a/o LCDrevisited2012-27:
# stdio.h is not referenced anywhere
# with AVR_MIN_PRINTF=TRUE, codesize is 8020 Bytes
# with AVR_NO_STDIO=TRUE, codesize is 7048 Bytes
# That's nearly 1/8th of the codeSpace, or 1KB, taken up by functions 
# that're never used! (e.g. vfprintf was linking-in, but not used)
# (Previously, LDFLAGS was set as in AVR_MIN_PRINTF as a default, in
#  this file, avrCommon.mk)
# with AVR_FLOAT_PRINTF=TRUE, codesize overflows by 1664 Bytes.
# so that's... 9856 Bytes

(Note that AVR_NO_STDIO, AVR_MIN_PRINTF, and AVR_FLOAT_PRINTF are my own options, and MOST LIKELY WILL NOT exist in your makefile)

So, if I'm parsing that long-forgotten-knowledge correctly, the key is to FIRST: Make CERTAIN you're not using stdio.h AT ALL (even in dependencies), as described, earlier... Then Make CERTAIN you *don't* have reference in your makefiles to anything like:

LDFLAGS += -Wl,-u,vfprintf -lprintf_min
NOR
LDFLAGS +=  -Wl,-u,vfprintf -lprintf_flt

AGAIN, it's a bit counter-intuitive, because if you're *NOT* using stdio, then the "printf_min" option will *increase* your code-size.

--------

And, of course, after you've done all this, make sure to remove (or comment-out) that line added to your makefile, earlier:

CFLAGS += -E -dM

Discussions