STM32 Incorrect vector table entry generated with gcc

I’m building some of the ST example projects using gcc, which work pretty well, except that after a hard reset, I almost immediately hit a hard fault exception. When the part (STM32F103) jumps to the hardfault handler, I can manually (with GDB) go place it at the location of the reset handler, and the examples run fine, but I’d like to get the cause of the hard fault resolved.

Doing some reading, and chatting with some of the folks on this forum, I’ve realized that in order for vector table entries to work properly in the cortex micros, they have to have their least significant bit set to 1, to signify that the ISR is thumb code. (All code for the cortex-m3s is thumb-2 code, so all vectors should have this bit set)

Ok. So when I actually compile my code using gcc, the startup code provided by ST appears not to compile correctly. The vector table is generated, but the entry for the Reset_Handler does not have that bit set correctly. Here’s some of the startup code:

 .syntax unified
  .cpu cortex-m3
  .fpu softvfp
  .thumb

.global  g_pfnVectors
.global  SystemInit_ExtMemCtl_Dummy
.global  Default_Handler

/* start address for the initialization values of the .data section. 
defined in linker script */
.word  _sidata
/* start address for the .data section. defined in linker script */  
.word  _sdata
/* end address for the .data section. defined in linker script */
.word  _edata
/* start address for the .bss section. defined in linker script */
.word  _sbss
/* end address for the .bss section. defined in linker script */
.word  _ebss

.equ  BootRAM,        0xF1E0F85F
/**
 * @brief  This is the code that gets called when the processor first
 *          starts execution following a reset event. Only the absolutely
 *          necessary set is performed, after which the application
 *          supplied main() routine is called. 
 * @param  None
 * @retval : None
*/

    .section  .text.Reset_Handler
  .weak  Reset_Handler
  .type  Reset_Handler, %function
Reset_Handler:  

/* Copy the data segment initializers from flash to SRAM */  
  movs  r1, #0
  b  LoopCopyDataInit

CopyDataInit:
  ldr  r3, =_sidata
  ldr  r3, [r3, r1]
  str  r3, [r0, r1]
  adds  r1, r1, #4
...

etc… and then the vector table:

 .section  .isr_vector,"a",%progbits
  .type  g_pfnVectors, %object
  .size  g_pfnVectors, .-g_pfnVectors
    
    
g_pfnVectors:
  .word  _estack           /* defined in linker script */
  .word  Reset_Handler
  .word  NMI_Handler
  .word  HardFault_Handler
  .word  MemManage_Handler
  .word  BusFault_Handler
  .word  UsageFault_Handler
...

So that looks fine to me, but the vector table output, that I read from my listing file is thus:

Disassembly of section .isr_vector:

08000000 <g_pfnVectors>:
 8000000:	20010000 	andcs	r0, r1, r0
 8000004:	0800b6f0 	stmdaeq	r0, {r4, r5, r6, r7, r9, sl, ip, sp, pc}
 8000008:	08000a29 	stmdaeq	r0, {r0, r3, r5, r9, fp}
 800000c:	08000a35 	stmdaeq	r0, {r0, r2, r4, r5, r9, fp}
 8000010:	08000a3d 	stmdaeq	r0, {r0, r2, r3, r4, r5, r9, fp}
 8000014:	08000a45 	stmdaeq	r0, {r0, r2, r6, r9, fp}
 8000018:	08000a4d 	stmdaeq	r0, {r0, r2, r3, r6, r9, fp}

The thing is, the address for the Reset_Handler (which should be stored at 0x8000004) is correct, it’s just that that lowest bit isn’t set. When I define other ISRs (in my C code) and their addresses are placed in the table, the LSB in their vector table entry is set correctly.

I guess the big question is… what gives? followed by … how do I fix it? I’ve tried this with several versions of gcc, all with essentially the same behavior.

Any thoughts?

This seems to be a problem that only I have, so I’m not sure that anyone cares what the answer is, but I got it figured out.

The .thumb_set directive appears to work the magic on the LSB. All the normal ISRs have a .weakref and a .thumb_set to configure them, but not the Reset_Handler (in the ST startup code) because it is always defined, and defined in the startup code file.

To ensure that that LSB is set, all you have to do is this:

  .thumb_set Reset_Handler,Reset_Handler

Which seems to do the trick.

If this is the wrong way about things, or someone else knows better, I’d like to hear about it!

-R

[edit: you posted the solution while I was entering this: Glad you figured it out! (Puzzling that I don’t need that directive though).]

That’s really strange. Since it stays the same when you change compilers, it (duh) must be caused by something in one of your source or config files. But what? The flags I’ve been using for gcc are:

-mcpu=cortex-m3 -march=armv7-m -mfloat-abi=softfp -mthumb

The only time I’ve seen anything like this was when I tried to hand-code my own crt0.s and didn’t know that the

.type Reset_Handler, %function

directive was required before the Reset_Handler. I don’t really beleive this has anything to do with your problem, but since I can’t think of anything else… If you have crt0.o available, you might do objdump -t on it and confirm that Reset_Handler has the “F” (function) flag set. The top of the objdump output from my project looks like

crt0.o:     file format elf32-little

SYMBOL TABLE:
00000000 l    d  .text    00000000 .text
00000000 l    d  .data    00000000 .data
00000000 l    d  .bss    00000000 .bss
f1e0f85f l       *ABS*    00000000 BootRAM
00000000 l    d  .text.Reset_Handler    00000000 .text.Reset_Handler
00000000 l       .text.Reset_Handler    00000000 $t
0000000c l       .text.Reset_Handler    00000000 LoopCopyDataInit
00000004 l       .text.Reset_Handler    00000000 CopyDataInit
00000020 l       .text.Reset_Handler    00000000 LoopFillZerobss
0000001a l       .text.Reset_Handler    00000000 FillZerobss
00000000 l    d  .text.Default_Handler    00000000 .text.Default_Handler
00000000 l       .text.Default_Handler    00000000 Infinite_Loop
00000000 l       .text.Default_Handler    00000000 $t
00000000 l    d  .isr_vector    00000000 .isr_vector
0000002c l       .text.Reset_Handler    00000000 $d
00000000 l    d  .ARM.attributes    00000000 .ARM.attributes
00000000 g     O .isr_vector    00000000 g_pfnVectors
00000000         *UND*    00000000 SystemInit_ExtMemCtl_Dummy
00000000 g       .text.Default_Handler    00000002 Default_Handler
00000000         *UND*    00000000 _sidata
00000000         *UND*    00000000 _sdata
00000000         *UND*    00000000 _edata
00000000         *UND*    00000000 _sbss
00000000         *UND*    00000000 _ebss
00000001  w    F .text.Reset_Handler    0000002c Reset_Handler
00000000         *UND*    00000000 main
00000000         *UND*    00000000 _estack
00000001  w    F .text.Default_Handler    00000002 NMI_Handler
00000001  w    F .text.Default_Handler    00000002 HardFault_Handler
00000001  w    F .text.Default_Handler    00000002 MemManage_Handler
00000001  w    F .text.Default_Handler    00000002 BusFault_Handler
00000001  w    F .text.Default_Handler    00000002 UsageFault_Handler
.........

I can’t interpret most of that, but presumably the Reset_Handler entry of interest is the last one.

You may have to provide more excruciating detail: IDE (if any), Makefile (if any), linker file(s), etc