Creo que todo depende de que necesitas que realize tu bootloader planeta.
Si el bootloader va a utilizar exactamente los mismos pines que tu aplicacion, lo que creo que es lo mas seguro que ocurra por que normalmente esto esta limitado por el ruteado del PCB y lo conectado. Asi que directamente podrias en tu bootloader setear una "base".. Supongamos SPI para una SD + otra cosa que necesite el bootloader (si es que lo necesita) y lo demas que siga siendo pines digitales comunes y corrientes.
Luego si necesitas otra cosa ahi si lo haces a traves del firmware a grabar, recordando que lo que puedas poner en el firmware puede tranquilamente sobreescribir lo que puso el bootloader antes. Es decir si necesitar activar la UART en X pines en tu firmware, activas eso ( intentando no sobreescribir lo del SPI asi sigue funcionando ) o directamente escribis la UART + SPI de nuevo, y no vas a perder nada. Asi que creo que no deberias hacerte problema por eso.
En temas de rutinas, si que entiendo que tratar de usar desde el firmware rutinas del bootloader puede ser complejo, por ejemplo compartir las librerias para tarjetas SD entre bootloader y firmware (aunque me gustaría aprender a hacerlo para ahorrar mucho espacio en el firmware). Pero lo del remapeo de pines me pilla de nuevo, no se si se mantendrá al saltar del bootloader al firmware o habrá que definirlo otra vez.
A no ser que se cree punteros a funciones y en posiciones de memoria absolutos no vas a poder hacerlo. Aunque espacio en la parte del bootloader o en la parte del firmware va a ocupar por igual.
Libreria SD en el firmware, esto es problematico ya que tengo entendido que te gusta tener los ".hex" en la SD, si por algun motivo se borra ya no hay forma. Asi que queda descartado.
Libreria SD en el bootloader. Esta podrias crear en la flash en una posicion de memoria absoluta, un array con los punteros a funcion. Luego en tu firmware creas un array el cual deberias leer la memoria flash para asi obtener el puntero a esas funciones. De alli es crear un puntero a funcion supongamos
Bootloader
void funcion1(int x,int y);
void funcion1(int z);
const uint32_t __attribute__((address(0x1000))) Vectores = {funcion1,funcion2,funcion3};
Firmware:
leerFlash(arrayConPunteros); //Aca vas a tener que ver como es la memoria en los PIC32, Si es que puede leer de forma "lineal" la flash y directamente hacer un *(0x1000) a 0x100C o si necesitaras algun registro especial para leer la flash
void (*funcion1)(int x,int y) = arrayConPunteros[0];
void (*funcion2)(int z) = arrayConPunteros[1];
Y lo usarias:
(*funcion1)(10,2);
Creo que es correcto hacerlo asi xD.. El por que crear un "array" en el bootloader y no en el programa del firmware apuntar a la direccion de las funciones (por que las funciones se ubicaron de forma dinamica, sino deberias hacerlo de forma absoluta), es por que de esa forma ante cualquier cambio en el bootloader no habria que modificar nada en el firmware, ya que la tabla con los punteros siempre estaria ubicada en el mismo lugar. Lo que cambiaria seria su contenido. Lo cual no te preocuparias vos para nada ya que se generaria dinamicamente y solo te importaria la tabla esa nomas. Si hubieras puesto la direccion de memoria absoluta de las funciones ya ahi habria problemas si es que hay algo agregado al bootloader, lo cual moveria las direcciones a otro lugar, y como dije la tabla seguiria siempre en el mismo lugar
Al menos es lo unico que se me ocurre a realizar xD
EDIT: para que sea mas facil tal ves podes hacer un:
#define Funcion1 (*funcion1)
void Funcion1(int x,int y) = arrayConPunteros[0];
Funcion1(2,10);
Tambien no se si es necesario crear exactamente un array en el firmware para eso. Tal ves algo asi si es que es accesible como una memoria lineal y no hay nada entre medio, como dije depende de como se acceda a la flash
void (*funcion1)(int x,int y) = *(0x1000);