The problem is that variant.cpp really does define an interrupt handler for the uart. In C/C++ you can't override bare functions, only methods of a class. That last statement is what most people will say and normally is a safe bet. It isn't strictly true. Think of this for a moment: the core will always call serialEvent() when the first serial port gets data. You may or may not actually define such a function. So, why doesn't it fault with a link error if you don't define the function? Because the core has an empty function called serialEvent. But, I said you can't override bare functions. Well, I lied. You can but only with a linker trick. serialEvent shows you this trick. You can define a function as weak and override it with a strong function. (All functions are strong by default - you have to set them as weak)
So, it might be possible to override the core interrupt handlers for serial if you define them as weak:
void UART_Handler(void) __attribute__((weak));
I have no idea if you can define interrupt handlers as weak or not. But, you are free to try. If it works then you will have edited your copy of the Arduino core and no one else will be able to compile your sketch unless they too edit their core files. In this case perhaps it would be good to bring up this issue on the Arduino developers list and define all interrupt handlers as weak in case a sketch wants to override.
EDIT: I set the versions of the serial interrupt handlers in variant.cpp to weak and made my own. The resulting sketch compiled and the map file suggests that it properly is using the new version. I don't have enough hardware with me right now to actually test but I'm about 95% sure that this method will work perfectly fine. So, have fun.