When a linker is looking for symbols used, it will first look in the
list of object files it is given, then move on to searching the static
libraries. So an override of a library function should have priority.
However, when it pulls in a symbol, it will pull in the whole object
file that it is in. (Section garbage collection might let it later
remove unneeded sections, but that is /after/ this stage.) A static
library file is a collection of object files, so when the link requires
pulling in a symbol from the library, it gets all the symbols from the
object file containing it. Usually, libraries are build from lots of
small files with a single function or a few highly related functions in
order to minimise this issue. (It reduces scope for inter-procedural
optimisations in the library, however.)
In your case, it is not unlikely that the object library object file
that contains "sprintf" also contains "snprintf", "vsnprintf", and other
related functions. So although you have overriden "sprintf", perhaps
"ctime" uses "snprintf" and its object file also contains "sprintf" -
thus causing a conflict.
You can find out more by looking at your map file (even from a failed
link), especially with cross-references enabled in the map file.
Solutions then involve overriding the other library functions used by
"ctime", using a different library (perhaps "newlib-nano" does not have
the malloc issue in the first place), using something other than
"ctime", accepting the library's "sprintf", etc.
(As an aside - you should be wary about using "sprintf". "snprintf" is
normally a better choice.)