来自 main 的 C 函数没有插入 arm 中的堆栈

标签 c gcc assembly arm cortex-m3

我正在为 stm32l152C-discovery 板执行 arm cortex-m3 的 C 代码,但我观察到来自 main 的函数调用没有被压入堆栈。我已经分析了这个源代码的 asm 代码,但我发现它没问题。为了更好地理解,请在此处查看为 C 代码生成的 asm 代码:​​

main.elf:     file format elf32-littlearm

*SYMBOL TABLE:
00000010 l    d  .text  00000000 .text
00000000 l    d  .debug_info    00000000 .debug_info
00000000 l    d  .debug_abbrev  00000000 .debug_abbrev
00000000 l    d  .debug_aranges 00000000 .debug_aranges
00000000 l    d  .debug_line    00000000 .debug_line
00000000 l    d  .debug_str 00000000 .debug_str
00000000 l    d  .comment   00000000 .comment
00000000 l    d  .ARM.attributes    00000000 .ARM.attributes
00000000 l    d  .debug_frame   00000000 .debug_frame
00000000 l    df *ABS*  00000000 main.c
00000000 l    df *ABS*  00000000 clock.c
20004ffc g       .text  00000000 _STACKTOP
**00000028 g     F .text    000000e0 SystemClock_Config**
20000000 g       .text  00000000 _DATA_BEGIN
20000000 g       .text  00000000 _HEAP
**00000010 g     F .text    00000016 main**
20000000 g       .text  00000000 _BSS_END
00000108 g       .text  00000000 _DATAI_BEGIN
20000000 g       .text  00000000 _BSS_BEGIN
00000108 g       .text  00000000 _DATAI_END
20000000 g       .text  00000000 _DATA_END
Disassembly of section .text:
00000010 <main>:

#define LL_GPIO_MODE_OUTPUT    1

void SystemInit() ;
int main()
{
  10:   b580        push    {r7, lr}
  12:   b082        sub sp, #8
  14:   af00        add r7, sp, #0
    int i = 0;
  16:   2300        movs    r3, #0
  18:   607b        str r3, [r7, #4]
  SystemClock_Config(); 
  **1a: f000 f805   bl  28 <SystemClock_Config>
    for(;;)
        i++;
  1e:   687b        ldr r3, [r7, #4]
  20:   3301        adds    r3, #1**
  22:   607b        str r3, [r7, #4]
  24:   e7fb        b.n 1e <main+0xe>

}
00000028 <SystemClock_Config>:
  *            PLLDIV                         = 3
  *            Flash Latency(WS)              = 1
  * @retval None
  */
void SystemClock_Config(void)
{
  28:   b480        push    {r7}
  2a:   af00        add r7, sp, #0
  SET_BIT(FLASH->ACR, FLASH_ACR_ACC64);
  2c:   4a33        ldr r2, [pc, #204]  ; (fc <SystemClock_Config+0xd4>)
  2e:   4b33        ldr r3, [pc, #204]  ; (fc <SystemClock_Config+0xd4>)
  30:   681b        ldr r3, [r3, #0]
  32:   f043 0304   orr.w   r3, r3, #4
  36:   6013        str r3, [r2, #0]
  MODIFY_REG(FLASH->ACR, FLASH_ACR_LATENCY, LL_FLASH_LATENCY_1);
  38:   4a30        ldr r2, [pc, #192]  ; (fc <SystemClock_Config+0xd4>)
  3a:   4b30        ldr r3, [pc, #192]  ; (fc <SystemClock_Config+0xd4>)
  3c:   681b        ldr r3, [r3, #0]
  3e:   f043 0301   orr.w   r3, r3, #1
  42:   6013        str r3, [r2, #0]*
}

执行在 PC 寄存器中的 0x1a、0x1c、0x1e、0x20 附近循环。

halted: PC: 0x0000001a
halted: PC: 0x0000001c
halted: PC: 0x0000001e
halted: PC: 0x00000020
halted: PC: 0x0000001a
halted: PC: 0x0000001c
halted: PC: 0x0000001e
halted: PC: 0x00000020
halted: PC: 0x0000001a
halted: PC: 0x0000001c
halted: PC: 0x0000001e
halted: PC: 0x00000020

它应该跳转到 0x1a 处的 0x28 (SystemClock_Config)。

最佳答案

一个非常简单的完整示例:

vector .s

.thumb

.globl _start
_start:

.word 0x20001000
.word reset

.thumb_func
reset:
    bl centry
done: b done

所以.c

unsigned int fun ( unsigned int );
unsigned int centry ( void )
{
    return(fun(5)+1);
}

函数.c

unsigned int fun ( unsigned int x )
{
    return(x+1);
}

flash.ld

MEMORY
{
    rom : ORIGIN = 0x00000000, LENGTH = 0x1000
}

SECTIONS
{
    .text : { *(.text*) } > rom
    .rodata : { *(.rodata*) } > rom
}

build

arm-none-eabi-as --warn --fatal-warnings -mcpu=cortex-m0 vectors.s -o vectors.o
arm-none-eabi-gcc -Wall -Werror -O2 -nostdlib -nostartfiles -ffreestanding  -mcpu=cortex-m0 -mthumb -c so.c -o so.o
arm-none-eabi-gcc -Wall -Werror -O2 -nostdlib -nostartfiles -ffreestanding  -mcpu=cortex-m0 -mthumb -c fun.c -o fun.o
arm-none-eabi-ld -o so.elf -T flash.ld vectors.o so.o fun.o
arm-none-eabi-objdump -D so.elf > so.list
arm-none-eabi-objcopy so.elf so.bin -O binary

整个程序

00000000 <_start>:
   0:   20001000    andcs   r1, r0, r0
   4:   00000009    andeq   r0, r0, r9

00000008 <reset>:
   8:   f000 f802   bl  10 <centry>

0000000c <done>:
   c:   e7fe        b.n c <done>
    ...

00000010 <centry>:
  10:   b510        push    {r4, lr}
  12:   2005        movs    r0, #5
  14:   f000 f802   bl  1c <fun>
  18:   3001        adds    r0, #1
  1a:   bd10        pop {r4, pc}

0000001c <fun>:
  1c:   3001        adds    r0, #1
  1e:   4770        bx  lr

程序模拟:

read32(0x00000000)=0x20001000
read32(0x00000004)=0x00000009
--- 0x00000008: 0xF000 
--- 0x0000000A: 0xF802 bl 0x0000000F
--- 0x00000010: 0xB510 push {r4,lr}
write32(0x20000FF8,0x00000000)
write32(0x20000FFC,0x0000000D)
--- 0x00000012: 0x2005 movs r0,#0x05
--- 0x00000014: 0xF000 
--- 0x00000016: 0xF802 bl 0x0000001B
--- 0x0000001C: 0x3001 adds r0,#0x01
--- 0x0000001E: 0x4770 bx r14
--- 0x00000018: 0x3001 adds r0,#0x01
--- 0x0000001A: 0xBD10 pop {r4,pc}
read32(0x20000FF8)=0x00000000
read32(0x20000FFC)=0x0000000D
--- 0x0000000C: 0xE7FE b 0x0000000B
--- 0x0000000C: 0xE7FE b 0x0000000B
--- 0x0000000C: 0xE7FE b 0x0000000B
--- 0x0000000C: 0xE7FE b 0x0000000B
--- 0x0000000C: 0xE7FE b 0x0000000B
--- 0x0000000C: 0xE7FE b 0x0000000B
--- 0x0000000C: 0xE7FE b 0x0000000B
--- 0x0000000C: 0xE7FE b 0x0000000B
--- 0x0000000C: 0xE7FE b 0x0000000B
--- 0x0000000C: 0xE7FE b 0x0000000B

确定这是一个有点无用的程序,但它演示了启动和调用函数(函数地址不显示在堆栈上,当你调用 (bl) 时,r14 获取返回地址,r15 获取分支地址到。如果你有像 centry 这样的嵌套函数(C 入口点 main() 不是一个重要的函数名称,你可以随意调用你的入口点,只要你的 Bootstrap 匹配)调用 fun,那么你需要保留返回地址无论您如何选择,通常都将其保存在堆栈中。压入 r4 只是为了使堆栈在每个 abi 的 64 位边界上对齐。

对于您的系统,您通常会为 0x08000000 (stm32) 设置链接描述文件。

您缺少的是二进制文件的开头,您能否对内存镜像/二进制文件进行 hexdump,以显示 main 之前的少量字节,包括 main 的前几条指令?

如果裸机程序没有正确执行最简单的引导步骤,您要做的第一件事就是根据架构检查入口点或 vector 表所在的二进制文件,看看您是否正确构建了它。

在我的示例中,这是一个 cortex-m,因此堆栈指针初始化值(如果您选择使用它)位于 0x00000000,您可以将任何东西放在那里,然后如果需要,只需覆盖 sp,您的选择...然后地址 0x00000004 是复位 vector ,它是处理复位的代码地址,其中 lsbit 设置为指示拇指模式。

所以 0x00000008|1 = 0x00000009。

如果你没有

0x2000xxxx 0x00000011

那么您的处理器将无法正常启动。我非常习惯使用 0x08000000,以至于我不记得 0x00000000 是否适用于 stm,理论上应该……但这取决于您加载闪存的方式以及当时芯片所处的模式/状态。

你可能需要为 0x08000000 链接,如果没有其他改变,至少要链接

0x2000xxxx 0x08000011

作为二进制/内存镜像中的前两个单词。

编辑

请注意,您可以制作一个二进制文件,该二进制文件既可以通过 vector 也可以通过引导加载程序输入

.thumb

.thumb_func
.global _start
_start:
bl reset
.word _start
reset:
    ldr r0,stacktop
    mov sp,r0
    bl notmain
    b hang
.thumb_func
hang:   b .
.align 
stacktop: .word 0x20001000

在堆栈地址点放置一个分支(用 bl 填充空间),然后稍后加载堆栈指针。

或者使用分支

.thumb

.thumb_func
.global _start
_start:
b reset
nop
.word _start
reset:
    ldr r0,stacktop
    mov sp,r0
    bl notmain
    b hang
.thumb_func
hang:   b .
.align
stacktop: .word 0x20001000

关于来自 main 的 C 函数没有插入 arm 中的堆栈,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46186563/

相关文章:

c - 为什么 scanf_s 在 vi​​sual studio 以外的地方不起作用?

gcc - 了解汇编列表输出中的 GCC 浮点常量

c++ - 为什么内存地址以 0x 开头?

c++ - 为什么重载运算符&&错误?

Python 3.4 Winrandom 编译报错

c++ - x86 汇编中的 "lock"指令是什么意思?

gcc - 在 NASM 和 GCC 中提供 incbin 路径作为预定义宏

c - K&R c中练习5.1的措辞

c - C 中的数组 - 最后一个元素分配索引值

c - 解析 iCal 文件的提示