在ARM ABI中,如何访问全局变量



我正在为ARM Cortex M3编写一个简单的多任务操作系统。我的线程总是使用进程堆栈指针运行。我有一个继承的应用程序,它使用全局变量。我正试图从线程代码中调用该应用程序中的函数,但它没有正确访问内存。以下陈述正确吗:

  1. 这些全局变量是通过某种相对寻址访问的,并且该相对地址被放置在主堆栈上(使用MSP)?

  2. 我的线程代码,使用PSP,将永远无法访问它们

  3. 我需要在调用这些函数时切换到MSP,然后在使用线程时返回到PSP?

**编辑:澄清这是针对Cortex M 的

全局变量与堆栈无关,甚至与静态局部变量无关。

所以你只需要看看编译器的输出,它就会告诉你一切。

你的问题很模糊——你可能在问许多不同的问题中的一个。我会展示一些基础知识,也许我会幸运的。

请注意,这通常与处理器、模式等无关。arm、thumb、x86等等。与工具链有更多的关系。

如果这太基本了,而你问的是一些非常高级的问题,我不清楚我会删除或重写,没问题。

一次性代码总是解决问题的好主意。

flash的

.thumb
.syntax unified
.word 0x20001000
.word reset
.thumb_func
reset:
bl notmain
b .

notmain.c

unsigned int x;
unsigned int y=5;
void notmain ( void )
{
unsigned int z=7;
x=++y;
z--;
}

flash.ld

MEMORY
{
rom     : ORIGIN = 0x00080000, LENGTH = 0x00001000
ram     : ORIGIN = 0x20000000, LENGTH = 0x00001000
}
SECTIONS
{
.text : { *(.text)   } > rom
.bss : { *(.bss)   } > ram
.data : { *(.data)   } > ram
}

构建

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

检查

Disassembly of section .text:
00080000 <reset-0x8>:
80000:   20001000    andcs   r1, r0, r0
80004:   00080009    andeq   r0, r8, r9
00080008 <reset>:
80008:   f000 f802   bl  80010 <notmain>
8000c:   e7fe        b.n 8000c <reset+0x4>
...
00080010 <notmain>:
80010:   4b04        ldr r3, [pc, #16]   ; (80024 <notmain+0x14>)
80012:   4905        ldr r1, [pc, #20]   ; (80028 <notmain+0x18>)
80014:   681a        ldr r2, [r3, #0]
80016:   3201        adds    r2, #1
80018:   601a        str r2, [r3, #0]
8001a:   600a        str r2, [r1, #0]
8001c:   685a        ldr r2, [r3, #4]
8001e:   3a01        subs    r2, #1
80020:   605a        str r2, [r3, #4]
80022:   4770        bx  lr
80024:   20000004    andcs   r0, r0, r4
80028:   20000000    andcs   r0, r0, r0
Disassembly of section .bss:
20000000 <x>:
20000000:   00000000    andeq   r0, r0, r0
Disassembly of section .data:
20000004 <y>:
20000004:   00000005    andeq   r0, r0, r5
20000008 <z.3645>:
20000008:   00000007    andeq   r0, r0, r7

这是基本的不可重定位等

80010:   4b04        ldr r3, [pc, #16]   ; (80024 <notmain+0x14>)
80014:   681a        ldr r2, [r3, #0]
80016:   3201        adds    r2, #1
80018:   601a        str r2, [r3, #0]
80024:   20000004    andcs   r0, r0, r4

Disassembly of section .data:
20000004 <y>:
20000004:   00000005    andeq   r0, r0, r5

我们可以看到y++。r3获取y的地址,r2获取y的值r2递增,然后保存回内存。

你也可以看到x和z是如何处理的。

现在,由于以下几个原因,这对mcu不起作用。0x20000000地址信息将不存在。只有非易失性存储中的内容当芯片通电并完成重置时,将在那里。以上是相关的,取决于你真正的问题是什么。

MEMORY
{
rom     : ORIGIN = 0x00080000, LENGTH = 0x00001000
ram     : ORIGIN = 0x20000000, LENGTH = 0x00001000
}
SECTIONS
{
.text : { *(.text)   } > rom
.bss : { *(.bss)   } > ram AT > rom
.data : { *(.data)   } > ram AT > rom
}

程序不会更改,但二进制文件会进行

00000000  00 10 00 20 09 00 08 00  00 f0 02 f8 fe e7 00 00  |... ............|
00000010  04 4b 05 49 1a 68 01 32  1a 60 0a 60 5a 68 01 3a  |.K.I.h.2.`.`Zh.:|
00000020  5a 60 70 47 04 00 00 20  00 00 00 20 05 00 00 00  |Z`pG... ... ....|
00000030  07 00 00 00                                       |....|
00000034

在0x2C处,我们看到y的预加载值,在0x30处看到z的预加载数值。

.bss值不在此处。通常情况下,你所做的是添加大量更多的东西到链接器脚本,以获得东西的地址。数据开始和停止,以及bss开始和大小或停止。然后是一个从闪存复制到ram的引导程序,这样初始化的值就在ram中,读/写就可以工作了。

因此,如果你的项目,不管是否称之为操作系统,只是一个编译并链接在一起的大型代码体。然后不做特别的事情,比如很多部分或其他什么。以上就是您所看到的内容,堆栈与全局变量无关。因为它从来都不是正常的。

(msp/psp并没有像arm所暗示的那样工作,我还没有看到第二个堆栈指针的用例,如果处理器有它,他们也没有全部实现)

现在,如果您的线程实际上是单独构建的程序,您将在运行时加载。。。然后他们完全生活在公羊里。所以

MEMORY
{
rom     : ORIGIN = 0x00080000, LENGTH = 0x00001000
ram     : ORIGIN = 0x20000000, LENGTH = 0x00001000
}
SECTIONS
{
.text : { *(.text)   } > ram
.bss : { *(.bss)   } > ram
.data : { *(.data)   } > ram
}

我们添加了-fPIC

arm-none-eabi-gcc -Wall -O2 -ffreestanding -mcpu=cortex-m0 -fPIC -c notmain.c -o notmain.o
Disassembly of section .text:
20000000 <reset-0x8>:
20000000:   20001000    andcs   r1, r0, r0
20000004:   20000009    andcs   r0, r0, r9
20000008 <reset>:
20000008:   f000 f802   bl  20000010 <notmain>
2000000c:   e7fe        b.n 2000000c <reset+0x4>
...
20000010 <notmain>:
20000010:   4a07        ldr r2, [pc, #28]   ; (20000030 <notmain+0x20>)
20000012:   4b08        ldr r3, [pc, #32]   ; (20000034 <notmain+0x24>)
20000014:   447a        add r2, pc
20000016:   58d1        ldr r1, [r2, r3]
20000018:   680b        ldr r3, [r1, #0]
2000001a:   3301        adds    r3, #1
2000001c:   600b        str r3, [r1, #0]
2000001e:   4906        ldr r1, [pc, #24]   ; (20000038 <notmain+0x28>)
20000020:   5852        ldr r2, [r2, r1]
20000022:   6013        str r3, [r2, #0]
20000024:   4a05        ldr r2, [pc, #20]   ; (2000003c <notmain+0x2c>)
20000026:   447a        add r2, pc
20000028:   6813        ldr r3, [r2, #0]
2000002a:   3b01        subs    r3, #1
2000002c:   6013        str r3, [r2, #0]
2000002e:   4770        bx  lr
20000030:   00000034    andeq   r0, r0, r4, lsr r0
20000034:   00000004    andeq   r0, r0, r4
20000038:   00000000    andeq   r0, r0, r0
2000003c:   0000001a    andeq   r0, r0, sl, lsl r0
Disassembly of section .bss:
20000040 <x>:
20000040:   00000000    andeq   r0, r0, r0
Disassembly of section .data:
20000044 <z.3645>:
20000044:   00000007    andeq   r0, r0, r7
20000048 <y>:
20000048:   00000005    andeq   r0, r0, r5
Disassembly of section .got:
2000004c <.got>:
2000004c:   20000040    andcs   r0, r0, r0, asr #32
20000050:   20000048    andcs   r0, r0, r8, asr #32
Disassembly of section .got.plt:
20000054 <_GLOBAL_OFFSET_TABLE_>:
...

因为您可能需要能够在ram中的任何位置加载程序(在规则内)。

代码都是相对的,但由于编译和链接的性质,数据需要一些硬编码。所以他们设置了一个全局偏移表GOT。got的位置是相对于代码的,您不能更改它。

20000010:   4a07        ldr r2, [pc, #28]   ; (20000030 <notmain+0x20>)
20000012:   4b08        ldr r3, [pc, #32]   ; (20000034 <notmain+0x24>)
20000014:   447a        add r2, pc
20000016:   58d1        ldr r1, [r2, r3]
20000018:   680b        ldr r3, [r1, #0]
2000001a:   3301        adds    r3, #1
2000001c:   600b        str r3, [r1, #0]

有你的y++当建立位置独立。

r2得到一个偏移,r3得到另一个偏移。r2是从代码中获取,(你不能将它们分开并移动一个另一个,不是位置独立的意思),所以现在r2指向得到。r3是GOT中到y地址的偏移量。r1获取地址现在就像在r3中获取y之前,加一,将y保存到内存中。

现在,如果你要将其重新定位到一个不是0x20000000的地址引导程序需要转到GOT并修补所有地址,因此您需要链接器魔术来获取get在哪里以及它的比特数等。使用pc弄清楚你在哪里,然后做出调整。如果以0x20002000加载到内存中,则需要将0x2000添加到每个条目中放在桌子上,然后一切都会好起来的。(仍然没有堆栈内容,堆栈不相关)。

如果你有空间的话,一个小技巧。

请注意,我将bss放在数据之前,并且我至少有一个.data项。如果你能保证(例如,在引导程序中强制使用.data)。

00000000  00 10 00 20 09 00 00 20  00 f0 02 f8 fe e7 00 00  |... ... ........|
00000010  07 4a 08 4b 7a 44 d1 58  0b 68 01 33 0b 60 06 49  |.J.KzD.X.h.3.`.I|
00000020  52 58 13 60 05 4a 7a 44  13 68 01 3b 13 60 70 47  |RX.`.JzD.h.;.`pG|
00000030  34 00 00 00 04 00 00 00  00 00 00 00 1a 00 00 00  |4...............|
00000040  00 00 00 00 07 00 00 00  05 00 00 00 40 00 00 20  |............@.. |
00000050  48 00 00 20 00 00 00 00  00 00 00 00 00 00 00 00  |H.. ............|
00000060
20000040 <x>:
20000040:   00000000    andeq   r0, r0, r0

Objdump为-O二进制文件填充二进制文件,为.bss填充零。如果你把它放在最后,那么它就不起作用了。

所以我不知道你的代码是如何使用线程和全局变量的,它是否试图保持每个线程特定的变量?如果是这样的话,它会在前面使用静态局部变量,然后在堆栈上传递地址(即使在那里,你使用的堆栈指针也无关紧要,除非你通常没有正确使用堆栈,否则全局变量不是你的问题。)。

如果您在一个堆栈指针上启动线程或任何代码,并暗示完全独立的堆栈(存储器地址空间)。然后切换,放弃代码进出所需的堆栈信息函数,然后如果在切换堆栈后从函数返回代码不仅会破坏传递的指向静态本地的指针。

因此,一个演示问题的最小示例可以为我们确认到底发生了什么,你的问题到底是什么,问题是什么。如果你想使用cortex-m的两个堆栈指针,你需要仔细阅读,还需要写一些一次性代码示例,看看它是如何工作的,然后将其应用到工具生成的代码中。

同样,如果这太初级了,而我离真正的问题还有几英里远,我肯定会删除这个,没问题。

最新更新