You forgot to mention for which architecture and derivative the question is, runtime routines are compiler specific.
For some cases there might be ways for the compiler to avoid runtime calls, but in general the compiler wont be able to compile arbitrary C core without using runtime calls.
The general solution for the problem is to split up the application into 2 separate applications, one containing the boot loader (and it's copies of the needed runtime routines) and a separate one for the main application code.
In a safe field flash upgrade scenario I would think that the bootloader itself does not get reflashed to avoid a time window where the device is not recoverable. If so, using a separate elf file also helps to isolate any dependencies between the changing and the non changing code. For example if the bootloader uses a function in the app code, this immediately fails when the app code is erased and therefore easy to detect (as you did :smileyhappy: ). However if the app uses some code of the bootloader, it will work just fine, until one day the bootloader changes just a little, addresses shift a bit and then when the app in the field gets updated and an old bootloader is present, the call into the bootloader misses it target. (While it just worked fine with the new bootloader when testing the app...).
If you decide to stick with the "bootloader is part of the app" approach, then for the 8/16 bit compilers (S12/S08), there is a message which can be mapped to an error to fail when the compiler uses a runtime call. By default the message is off. Optimizing for speed usually uses fewer runtime calls.
Daniel