Building common app+bootloader application, I don't know how to reliably separate bootloader part of the code (nonpaged address range, paged address range, vectors, strings, ISRs etc..) and app part of the code. So why not simplify the matter and build two separate applications: bootloader and the app with vectors in the secondary vector table? And you make your bootloader redirecting normal vectors to secondary vector table.
For debugging purposes you just build your app with interrupt vectors in their normal locations and debug as there was no bootloader. To build loadable image you use conditional compiling and recompile the app with all interrupt vectors moved to secondary vector table. If you are using CW and their interrupt N syntax, then it could look like
void interrupt (N+(bootloadersize/2)) ISR(void)
{
}
, of course if secondary vector table is in nonbanked mem just below the bootloader. The best is to write protect bootloader memory and set bootloadersize to the size of write protected region...