为什么这个函数确实以 1 的偏移量指向自身?

问题描述

我正在尝试使用 C 为 Nucleo-64 Stm32F401re 板编写裸机闪烁程序。 然而,在开始调试错误时(它还没有闪烁),我发现了一个奇怪的地址,我没有找到任何解释。这是反汇编相关部分的输出

    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,asr #26
   4:   61656100        cmnvs   r5,lsl #2
   8:   01006962        tsteq   r0,r2,ror #18
   c:   00000023        andeq   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,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.

Reset_Handler 函数本身在正确的地址上,但是通过在代码中使用它的名称作为指针,它进一步指向一个地址!对应的代码如下:

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) {
        
    } 

我使用的链接器脚本与大多数教程中使用的基本相同。

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) }
        
    
    
}

那么为什么 isr_vector_table 中存储的地址是 08000009 而不是 08000008? 到目前为止,我可以将其更改为正确值的唯一方法是通过对该值进行硬编码或为 Reset_Handler 定义一个额外的部分,以便我可以将该地址用作另一个外部值,例如 _stack_top。

以下是我用于编译的命令,因为我不知道它们是否需要找到答案:

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

解决方法

来自STM32F4的编程手册PM0214:

矢量表
向量表包含堆栈的重置值 指针和起始地址,也称为异常向量,用于 所有异常处理程序。第 39 页的图 11 显示了 向量表中的异常向量。最不重要的位 每个向量必须为 1,表示异常处理程序为 Thumb 代码。

因此,LSb = 1 表示该向量指向的指令是 Thumb 指令。 Cortex-M 内核仅支持 Thumb 指令集。编译器知道这一点,并自动使 LSb = 1。如果您以某种方式设法将其设为 0,它将无法工作。