我一直在try 嵌入式C编程...非常裸露的金属,唯一的工具链,而不是IDE.为此,我也一直在编写自己的链接器脚本.问题是,当我的C代码中除了main之外还有任何其他函数时,PCSP(程序计数器和堆栈指针寄存器)就会混乱.

我会提供代码:

左:

ENTRY(start)

MEMORY {
  FLASH (rw)  : ORIGIN = 0x0000000000000000, LENGTH = 0x000000000000BFFF
  RAM (rwx)   : ORIGIN = 0x0000000040000000, LENGTH = 0x000000001FFFFFFF
}

_estack = ORIGIN(RAM) + LENGTH(RAM);

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

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

  .bss : {
    _sbss = .;
    *(.bss)
    *(.bss*)
    . = ALIGN(4);
    _ebss = .;
  } > RAM
}

ASM文件(稍后我将添加更多内容):

.syntax unified
.cpu cortex-a8

.global start

.word _estack

.word _sbss
.word _ebss

.word _sdata
.word _edata

.section .text
start:
  ldr sp, =_estack

  b main

编译标志还包括:

CFLAGS := -c \
    -g
    -march=armv7-a \
    -nostdlib

QEMU_FLAGS := -m $(MEMORY) \
    -S \
    -machine cubieboard \
    -cpu cortex-a8  \
    -gdb tcp::$(PORT)

Problem demonstration:

C代码:

int main(void) {

    volatile int a = 0, b = 1;
    for(int i = 2; i < 4; i++) {
        int k = a + b;
        a = b;
        b = k;
    }

    return 0;
}

Gdb(将架构设置为"arm")输出:

start () at start.s:16
16        ldr sp, =_estack
(gdb) load
Loading section .text, size 0x94 lma 0x0
Start address 0x00000088, load size 148
Transfer rate: 1184 bits in <1 sec, 148 bytes/write.
(gdb) info reg
...
sp             0x0                 0x0 <main>
lr             0x0                 0
pc             0x88                0x88 <start>
cpsr           0x400001d3          1073742291
fpscr          0x0                 0
fpsid          0x410330c0          1090728128
...
Quit
(gdb) step
start () at start.s:18
18        b main
(gdb) step
main () at main.c:1
1       int main(void) {
(gdb) step
3               volatile int a = 0, b = 1;
(gdb) info reg
...
r11            0x5ffffffb          1610612731
r12            0x0                 0
sp             0x5fffffe7          0x5fffffe7
lr             0x0                 0
pc             0xc                 0xc <main+12>
cpsr           0x400001d3          1073742291
fpscr          0x0                 0
fpsid          0x410330c0          1090728128
...

也就是说.都很好.

当我有职能时:

C代码:

void my_func(void) {
    int volatile c = 1000;
}

int main(void) {

    volatile int a = 0, b = 1;
    for(int i = 2; i < 4; i++) {
        int k = a + b;
        a = b;
        b = k;
    }

    my_func();

    return 0;
}

Gdb输出:

start () at start.s:16
16        ldr sp, =_estack
(gdb) load
Loading section .text, size 0xbc lma 0x0
Start address 0x000000b0, load size 188
Transfer rate: 1504 bits in <1 sec, 188 bytes/write.
(gdb) step
start () at start.s:18
18        b main
(gdb) step
main () at main.c:5
5       int main(void) {
(gdb) step
my_func () at main.c:3
3       }
(gdb) print a
No symbol "a" in current context.
(gdb) info regs
Undefined info command: "regs".  Try "help info".
(gdb) info reg
...
sp             0x0                 0x0 <my_func>
lr             0x2c                44
pc             0x14                0x14 <my_func+20>
cpsr           0x400001d7          1073742295
...
(gdb) disas my_func
Dump of assembler code for function my_func:
   0x00000000 <+0>:     push    {r11}           @ (str r11, [sp, #-4]!)
   0x00000004 <+4>:     add     r11, sp, #0
   0x00000008 <+8>:     sub     sp, sp, #12
   0x0000000c <+12>:    mov     r3, #1000       @ 0x3e8
   0x00000010 <+16>:    str     r3, [r11, #-8]
=> 0x00000014 <+20>:    nop                     @ (mov r0, r0)
   0x00000018 <+24>:    add     sp, r11, #0
   0x0000001c <+28>:    pop     {r11}           @ (ldr r11, [sp], #4)
   0x00000020 <+32>:    bx      lr
End of assembler dump.
(gdb) disas main
Dump of assembler code for function main:
   0x00000024 <+0>:     push    {r11, lr}
   0x00000028 <+4>:     add     r11, sp, #4
   0x0000002c <+8>:     sub     sp, sp, #16
   0x00000030 <+12>:    mov     r3, #0
   0x00000034 <+16>:    str     r3, [r11, #-16]
   0x00000038 <+20>:    mov     r3, #1
   0x0000003c <+24>:    str     r3, [r11, #-20] @ 0xffffffec
   0x00000040 <+28>:    mov     r3, #2
   0x00000044 <+32>:    str     r3, [r11, #-8]
   0x00000048 <+36>:    b       0x78 <main+84>
   0x0000004c <+40>:    ldr     r2, [r11, #-16]
   0x00000050 <+44>:    ldr     r3, [r11, #-20] @ 0xffffffec
   0x00000054 <+48>:    add     r3, r2, r3
   0x00000058 <+52>:    str     r3, [r11, #-12]
   0x0000005c <+56>:    ldr     r3, [r11, #-20] @ 0xffffffec
   0x00000060 <+60>:    str     r3, [r11, #-16]
   0x00000064 <+64>:    ldr     r3, [r11, #-12]
   0x00000068 <+68>:    str     r3, [r11, #-20] @ 0xffffffec
   0x0000006c <+72>:    ldr     r3, [r11, #-8]
   0x00000070 <+76>:    add     r3, r3, #1
   0x00000074 <+80>:    str     r3, [r11, #-8]
   0x00000078 <+84>:    ldr     r3, [r11, #-8]
   0x0000007c <+88>:    cmp     r3, #3
   0x00000080 <+92>:    ble     0x4c <main+40>
   0x00000084 <+96>:    bl      0x0 <my_func>
   0x00000088 <+100>:   mov     r3, #0
   0x0000008c <+104>:   mov     r0, r3
   0x00000090 <+108>:   sub     sp, r11, #4
   0x00000094 <+112>:   pop     {r11, lr}
   0x00000098 <+116>:   bx      lr

(gdb) print &c
$1 = (volatile int *) 0xfffffff8

0xfffffff8扩展到链接器Skript上写入的RAM之外.

有人能解释一下什么是错误的,我如何防止它?

推荐答案

您的链接器脚本中的RAM和闪存长度的值错误.它们应该是一些东西的整数倍--硬件中的内存大小从来不是这样的奇数.

此错误导致您将初始SP值设置为0x5fffffff.调用标准的ARM过程不允许这样做,它说:

SP模块4=0.堆栈必须始终与单词边界对齐.

在公共接口上(即,当您调用由C编译器编译的函数时):

SP mod 8 = 0.堆栈必须双字对齐.

(https://github.com/ARM-software/abi-aa/blob/main/aapcs32/aapcs32.rst)

如果你违反了这些规则,那么行为就没有定义,也就是说,理论上任何事情都可能发生.具体会发生什么情况将取决于C编译器发出的代码.

在本例中,我们可以看到编译器已经发出了LDR和STR指令来访问堆栈中的内存.在CPU开始执行的模式中,如果您try 在未对齐的地址上使用它们,它们将出现数据中止异常.ARM上的异常由CPU在低内存中的适当入口点开始执行来处理.您的程序没有在异常向量上设置任何代码,它只是将启动 routine 放在那里,因此如果在任何点发生异常,执行都会跳到启动 routine 的中途.

我建议您在程序的开头放置一组适当的异常向量,即使每个入口点的代码只是"分支到相同的地址",即一个紧密循环.这样,如果您的代码中有错误,那么您将看到您接受了一个异常(因为CPU将在其中一个异常向量地址开始循环),而不是具有奇怪的行为.

您可能还应该查看一下过程调用标准,因为它指定了代码调用C函数所需的所有设置.

C++相关问答推荐

rSP堆栈指针在返回函数调用的值时有任何用途吗?

从内联程序集调用Rust函数和调用约定

核心转储文件中出现奇怪的大小变化

在没有动态内存分配的情况下,用C语言最快地将各种数组复制到单个较大的数组中

ARM64 ASIMD固有的加载uint8_t* 到uint16x8(x3)?

防止规范模式在C++中 echo 特殊字符

I2C外设在单次交易后出现故障

OpenSSL:如何将吊销列表与SSL_CTX_LOAD_VERIFY_LOCATIONS一起使用?

在C中创建任意类型的只读指针参数

如何使用空元素块声明指针数组

如何组合两个宏来初始化C语言中的字符串数组?

STM32 FATFS用户手册(Um1721)中的代码正确吗?

基于蝶数恰好有8个除数的事实的代码

计算SIZE_MAX元素的长数组的大小

gdb - 你能找到持有内部 glibc 锁的线程吗?

C 程序不显示任何输出,但它接受 CS50 Lab1 的输入问题

无法理解 fgets 输出

函数的typedef是标准 C 语法吗?它与函数指针的typedef有何不同?

通过修改c中的合并排序对数组的偶数索引进行排序

使用共享变量同步多线程 C 中的函数