Why this function does point to itself with a offset of 1?

I'm trying to write a bare metal blink program for a Nucleo-64 Stm32F401re board using C. However while starting debugging for errors (it didn't blink yet) I found an odd adress for which I found no explanation. This is the output of the relevant part of the disassembly:

    blink.elf:     file format elf32-littlearm


Disassembly of section .text:

08000000 <isr_vector_table>:
 8000000:       20018000        andcs   r8, r1, r0
 8000004:       08000009        stmdaeq r0, {r0, r3}

08000008 <Reset_Handler>:
 8000008:       b480            push    {r7}
 800000a:       af00            add     r7, sp, #0
 800000c:       bf00            nop
 800000e:       46bd            mov     sp, r7
 8000010:       bc80            pop     {r7}
 8000012:       4770            bx      lr

Disassembly of section .ARM.attributes:

00000000 <.ARM.attributes>:
   0:   00002d41        andeq   r2, r0, r1, asr #26
   4:   61656100        cmnvs   r5, r0, lsl #2
   8:   01006962        tsteq   r0, r2, ror #18
   c:   00000023        andeq   r0, r0, r3, lsr #32
  10:   2d453705        stclcs  7, cr3, [r5, #-20]      ; 0xffffffec
  14:   0d06004d        stceq   0, cr0, [r6, #-308]     ; 0xfffffecc
  18:   02094d07        andeq   r4, r9, #448    ; 0x1c0
  1c:   01140412        tsteq   r4, r2, lsl r4
  20:   03170115        tsteq   r7, #1073741829 ; 0x40000005
  24:   01190118        tsteq   r9, r8, lsl r1
  28:   061e011a                        ; <UNDEFINED> instruction: 0x061e011a
  2c:   Address 0x0000002c is out of bounds.

The Reset_Handler function itself is on the right adress but by using its name as pointer in the code it points one adress further! Here is the corresponding code:

extern int _stack_top; // bigger Memory Adress
    
    void Reset_Handler (void);

    __attribute__((section(".isr_vector"))) int* isr_vector_table[] = {

        (int*)&_stack_top,
        (int*)Reset_Handler
    };

    void Reset_Handler (void) {
        
    } 

And the Linker script I used which is basically the same used in most tutorials.

OUTPUT_ARCH(arm)
OUTPUT_FORMAT("elf32-littlearm", "elf32-bigarm", "elf32-littlearm")
ENTRY(Reset_Handler)

MEMORY
{
    FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 512K
    SRAM (rwx) : ORIGIN = 0x20000000, LENGTH = 96K
}

    _stack_top = ORIGIN(SRAM)+LENGTH(SRAM);

SECTIONS
{

    
    
    
    


    .text : 
    {
        . = ALIGN(4);   
        *(.isr_vector)
        *(.text*)
        *(.glue_7)
        *(.glue_7t)
        *(.eh_frame)
        KEEP(*(.init))
        KEEP(*(.fini))
        . = ALIGN(4);
        _etext = .;
    } > FLASH

    .rodata :
    {
        . = ALIGN(4);   
        *(.rodata*)
        . = ALIGN(4);
    } > FLASH

    .ARM.extab :
        {
            *(.ARM.extab* .gnu.linkonce.armextab.*)
    } >FLASH

    .ARM :
     {
            __exidx_start = .;
            *(.ARM.exidx*)
            __exidx_end = .;
     } >FLASH



    .preinit_array :
    {
    PROVIDE_HIDDEN (__preinit_array_start = .);
    KEEP (*(.preinit_array*))
    PROVIDE_HIDDEN (__preinit_array_end = .);
    } >FLASH

    .init_array :
    {
    PROVIDE_HIDDEN (__init_array_start = .);
    KEEP (*(SORT(.init_array.*)))
    KEEP (*(.init_array*))
    PROVIDE_HIDDEN (__init_array_end = .);
    } >FLASH

    .fini_array :
    {   
    PROVIDE_HIDDEN (__fini_array_start = .);
    KEEP (*(.fini_array*))
    KEEP (*(SORT(.fini_array.*)))
    PROVIDE_HIDDEN (__fini_array_end = .);
    } >FLASH



    . = ALIGN(4);
    _sidata = LOADADDR(.data);

    .data :
    {
        . = ALIGN(4);
        _sdata = .;
        *(.data*)
        . = ALIGN(4);
        _edata = .;
    } > SRAM AT > FLASH

    .bss :
    {
        . = ALIGN(4);
        _sbss = .;
        __bss_start__ = _sbss;
        *(.bss*)
        *(COMMON)
        . = ALIGN(4);
        _ebss = .;
        __bss_end__ = _ebss;
    } > SRAM

    /DISCARD/ :
    {
         libc.a ( * )
         libm.a ( * )
         libgcc.a ( * )
     }

     .ARM.attributes 0 : { *(.ARM.attributes) }
        
    
    
}

So why the adress stored in the isr_vector_table is 08000009 and not 08000008? The only way I so far could change it to the right value was through hardcoding the value or defining a extra section for the Reset_Handler so I could use the adress as another extern value like the _stack_top.

Here are the commands I used for compilation as I don't know if they are necessary to find an answer:

cd C:/bare_metal
arm-none-eabi-gcc.exe -g main.c -o blink.elf -Wall -T STM32F4.ld -mcpu=cortex-m4 -mthumb --specs=nosys.specs -nostdlib -O0
arm-none-eabi-objdump.exe -D blink.elf

1 answer

  • answered 2021-01-25 12:06 Tagli

    From the Programming Manual PM0214 of STM32F4:

    Vector table
    The vector table contains the reset value of the stack pointer, and the start addresses, also called exception vectors, for all exception handlers. Figure 11 on page 39 shows the order of the exception vectors in the vector table. The least-significant bit of each vector must be 1, indicating that the exception handler is Thumb code.

    So, the LSb = 1 indicates that the instruction pointed by that vector is a Thumb instruction. Cortex-M cores support only Thumb instruction set. The compiler knows that, and makes LSb = 1 automatically. If you somehow manage to make it 0, it won't work.