In that case bootloader is irrelevant. You need to debug your code. Alternatively you can replace your hard fault handler with the code below, and the value of s_pc will tell you the address of the faulting instruction.
//-----------------------------------------------------------------------------
void irq_handler_hard_fault_c(uint32_t lr, uint32_t msp, uint32_t psp)
{
uint32_t s_r0, s_r1, s_r2, s_r3, s_r12, s_lr, s_pc, s_psr;
uint32_t r_CFSR, r_HFSR, r_DFSR, r_AFSR, r_BFAR, r_MMAR;
uint32_t *sp = (uint32_t *)((lr & 4) ? psp : msp);
s_r0 = sp[0];
s_r1 = sp[1];
s_r2 = sp[2];
s_r3 = sp[3];
s_r12 = sp[4];
s_lr = sp[5];
s_pc = sp[6];
s_psr = sp[7];
r_CFSR = SCB->CFSR; // Configurable Fault Status Register (MMSR, BFSR and UFSR)
r_HFSR = SCB->HFSR; // Hard Fault Status Register
r_DFSR = SCB->DFSR; // Debug Fault Status Register
r_MMAR = SCB->MMFAR; // MemManage Fault Address Register
r_BFAR = SCB->BFAR; // Bus Fault Address Register
r_AFSR = SCB->AFSR; // Auxiliary Fault Status Register
asm("nop"); // Setup breakpoint here
while (1);
}
//-----------------------------------------------------------------------------
__attribute__((naked)) void irq_handler_hard_fault(void) // !!! Rename to match your vector table entry
{
asm volatile (R"asm(
mov r0, lr
mrs r1, msp
mrs r2, psp
b irq_handler_hard_fault_c
)asm"
);
}