I always avoid things like "printf". printf uses fprintf and snprintf. fprintf needs fwrite, and fwrite means including all file I/O, including fopen, fread, fclose, fseek, etc. Usually it means I get a huge library, so instead, I either roll my own low-level routine that just sends a character over the UART for instance, and then another routine, which takes a string and send the string's characters one-by-one using the other routine.
Looking at the above, I see some exit and libshutdown references; that's completely redundant, because you never really exit your program on a microcontroller anyway - you would usually stay in a 'forever' while-loop. (Remember to put a __WFI(); in the loop, to save power, if you can).
The standard C library probably also wants malloc and free, two things that are not appropriate for most Cortex-M0 (unless they're combined with a Cortex-M4 for instance, like the NXP LPC43xx or LPC541xx; these have plenty of space).
So what I'd do in your place, would be to take a look at newlib nano. It's a smaller version of the standard C library, which is much more useful on smaller devices.
But I'd also try to avoid using functions that need the C-library for as long as possible.
-But if you at some point already need the standard library, then using it will not cost much extra. If you already must have printf, just in one place, keep using it, because you might be able to save space, when compared to rolling your own.
There are other library functions, which takes up a lot of space. For instance if you're using math multiplication or division. Mulitplication isn't really that bad, but dividing a 32-bit value by a 32-bit value producing a remainder and a quotient will generate a medium sized subroutine.
If you attempt to use floating point, then you'll get huge and very slow code. Avoid floating point, at almost all cost.
Normally, you'll need only integer math, it's also often much more precise (32-bit floating point only has 24-bit precision for instance).