ARM-C互通

我正在尝试一个简单的ARM-C互通程序。 这是代码:

#include #include int Double(int a); extern int Start(void); int main(){ int result=0; printf("in C main\n"); result=Start(); printf("result=%d\n",result); return 0; } int Double(int a) { printf("inside double func_argument_value=%d\n",a); return (a*2); } 

汇编文件如下 –

 .syntax unified .cpu cortex-m3 .thumb .align .global Start .global Double .thumb_func Start: mov r10,lr mov r0,#42 bl Double mov lr,r10 mov r2,r0 mov pc,lr 

在LPC1769(嵌入式艺术家电路板)上调试期间,我在“result = Start()”指令上遇到了一个硬错误。我正在尝试在这里进行arm-C网络互连。 在执行上述语句(result = Start())期间的lr值是0x0000029F,其中错误指令是,并且pc值是0x0000029E。 这就是我在r1中获得错误指令的方法

 __asm("mrs r0,MSP\n" "isb\n" "ldr r1,[r0,#24]\n"); 

谁能解释一下我哪里出错了? 任何解决方案表示赞赏 先感谢您。

我是cortex-m3的初学者,正在使用由Code_Red驱动的NXP LPCXpresso IDE。 这是我的代码的反汇编。

  IntDefaultHandler: 00000269: push {r7} 0000026b: add r7, sp, #0 0000026d: bn 0x26c  0000026f: nop 00000271: mov r3, lr 00000273: mov.w r0, #42 ; 0x2a 00000277: bl 0x2c0  0000027b: mov lr, r3 0000027d: mov r2, r0 0000027f: mov pc, lr main: 00000281: push {r7, lr} 00000283: sub sp, #8 00000285: add r7, sp, #0 00000287: mov.w r3, #0 0000028b: str r3, [r7, #4] 0000028d: movw r3, #11212 ; 0x2bcc 00000291: movt r3, #0 00000295: mov r0, r3 00000297: bl 0xd64  0000029b: bl 0x270  0000029f: mov r3, r0 000002a1: str r3, [r7, #4] 000002a3: movw r3, #11224 ; 0x2bd8 000002a7: movt r3, #0 000002ab: mov r0, r3 000002ad: ldr r1, [r7, #4] 000002af: bl 0xd64  000002b3: mov.w r3, #0 000002b7: mov r0, r3 000002b9: add.w r7, r7, #8 000002bd: mov sp, r7 000002bf: pop {r7, pc} Double: 000002c0: push {r7, lr} 000002c2: sub sp, #8 000002c4: add r7, sp, #0 000002c6: str r0, [r7, #4] 000002c8: movw r3, #11236 ; 0x2be4 000002cc: movt r3, #0 000002d0: mov r0, r3 000002d2: ldr r1, [r7, #4] 000002d4: bl 0xd64  000002d8: ldr r3, [r7, #4] 000002da: mov.w r3, r3, lsl #1 000002de: mov r0, r3 000002e0: add.w r7, r7, #8 000002e4: mov sp, r7 000002e6: pop {r7, pc} 

根据您的建议Dwelch,我已将r10更改为r3。

我认为你的意思是互通而不是互联网? LPC1769是一个cortex-m3,它只是拇指/拇指2,因此它不支持arm指令,因此该平台没有可用的互通。 不过,玩编译器看看会发生什么:

让编译器先为你做,然后在asm中自己尝试…

的start.s

 .thumb .globl _start _start: ldr r0,=hello mov lr,pc bx r0 hang : b hang 

你好ç

 extern unsigned int two ( unsigned int ); unsigned int hello ( unsigned int h ) { return(two(h)+7); } 

two.c

 unsigned int two ( unsigned int t ) { return(t+5); } 

Makefile文件

 hello.list : start.s hello.c two.c arm-none-eabi-as -mthumb start.s -o start.o arm-none-eabi-gcc -c -O2 hello.c -o hello.o arm-none-eabi-gcc -c -O2 -mthumb two.c -o two.o arm-none-eabi-ld -Ttext=0x1000 start.o hello.o two.o -o hello.elf arm-none-eabi-objdump -D hello.elf > hello.list clean : rm -f *.o rm -f *.elf rm -f *.list 

生成hello.list

 Disassembly of section .text: 00001000 <_start>: 1000: 4801 ldr r0, [pc, #4] ; (1008 ) 1002: 46fe mov lr, pc 1004: 4700 bx r0 00001006 : 1006: e7fe bn 1006  1008: 0000100c andeq r1, r0, ip 0000100c : 100c: e92d4008 push {r3, lr} 1010: eb000004 bl 1028 <__two_from_arm> 1014: e8bd4008 pop {r3, lr} 1018: e2800007 add r0, r0, #7 101c: e12fff1e bx lr 00001020 : 1020: 3005 adds r0, #5 1022: 4770 bx lr 1024: 0000 movs r0, r0 ... 00001028 <__two_from_arm>: 1028: e59fc000 ldr ip, [pc] ; 1030 <__two_from_arm+0x8> 102c: e12fff1c bx ip 1030: 00001021 andeq r1, r0, r1, lsr #32 1034: 00000000 andeq r0, r0, r0 

hello.o自行拆解:

 00000000 : 0: e92d4008 push {r3, lr} 4: ebfffffe bl 0  8: e8bd4008 pop {r3, lr} c: e2800007 add r0, r0, #7 10: e12fff1e bx lr 

编译器使用bl假设/希望它将从arm调用arm。 但它没有,所以他们所做的是在那里放一个蹦床。

 0000100c : 100c: e92d4008 push {r3, lr} 1010: eb000004 bl 1028 <__two_from_arm> 1014: e8bd4008 pop {r3, lr} 1018: e2800007 add r0, r0, #7 101c: e12fff1e bx lr 00001028 <__two_from_arm>: 1028: e59fc000 ldr ip, [pc] ; 1030 <__two_from_arm+0x8> 102c: e12fff1c bx ip 1030: 00001021 andeq r1, r0, r1, lsr #32 1034: 00000000 andeq r0, r0, r0 

bl到__two_from_arm是一个arm模式到arm模式分支链接。 目标函数的地址(两个)与lsbit设置,告诉bx切换到拇指模式,加载到一次性寄存器ip(r12?)然后bx ip发生切换模式。 分支链接在lr中设置了返回地址,毫无疑问这是一个arm模式地址(lsbit为零)。

 00001020 : 1020: 3005 adds r0, #5 1022: 4770 bx lr 1024: 0000 movs r0, r0 

two()函数做它的东西并返回,注意你必须使用bx lr而不是mov pc,lr在交互时。 基本上如果你没有运行没有T的ARMv4,或者没有运行没有T的ARMv5,那么mov pc,lr是一个好习惯。 但是任何ARMv4T或更新版本(ARMv5T或更新版本)都使用bx lr从函数返回,除非您有特殊原因不这样做。 (避免使用pop {pc}也是出于同样的原因,除非你真的需要保存该指令而不是互通)。 现在只在拇指+拇指2的皮质m3上,你不能互相工作,所以你可以使用mov pc,lr和pop {pc},但代码不可移植,这不习惯,因为这个习惯会当你切换回arm编程时咬你。

因此,当hello处于arm模式时,它使用bl来设置链接寄存器,two_from_arm中的bx不会触及链接寄存器,所以当two()返回bx lr时它会在bl __two_from_arm之后返回arm模式hello()函数中的行。

另请注意拇指function后的额外0x0000,这是为了在一个字边界上对齐程序,以便下面的臂代码对齐…

看看编译器如何做拇指arm改变二,如下所示

 unsigned int three ( unsigned int ); unsigned int two ( unsigned int t ) { return(three(t)+5); } 

并将该函数放在hello.c中

 extern unsigned int two ( unsigned int ); unsigned int hello ( unsigned int h ) { return(two(h)+7); } unsigned int three ( unsigned int t ) { return(t+3); } 

现在我们得到了另一个蹦床

 00001028 : 1028: b508 push {r3, lr} 102a: f000 f80b bl 1044 <__three_from_thumb> 102e: 3005 adds r0, #5 1030: bc08 pop {r3} 1032: bc02 pop {r1} 1034: 4708 bx r1 1036: 46c0 nop ; (mov r8, r8) ... 00001044 <__three_from_thumb>: 1044: 4778 bx pc 1046: 46c0 nop ; (mov r8, r8) 1048: eafffff4 b 1020  104c: 00000000 andeq r0, r0, r0 

现在这是一个非常酷的蹦床。 bl到three_from_thumb处于拇指模式,链接寄存器设置为返回到two()函数,lsbit设置无疑表示返回拇指模式。

蹦床以bx pc开始,pc设置为前面的两个指令,pc内部始终具有lsbit清除,因此如果尚未处于arm模式,则bx pc将始终将您带到arm模式,并且在任一模式下将提前两个指令。 bx pc之前的两条指令是一条arm指令,它将三个函数分支(不是分支链接!),完成蹦床。

注意我是如何编写首先调用hello()的

 _start: ldr r0,=hello mov lr,pc bx r0 hang : b hang 

实际上不会工作吗? 它会让你从arm到拇指但不是从拇指到arm。 我将把它作为读者的练习。

如果您将start.s更改为此

 .thumb .globl _start _start: bl hello hang : b hang 

链接器照顾我们:

 00001000 <_start>: 1000: f000 f820 bl 1044 <__hello_from_thumb> 00001004 : 1004: e7fe bn 1004  ... 00001044 <__hello_from_thumb>: 1044: 4778 bx pc 1046: 46c0 nop ; (mov r8, r8) 1048: eaffffee b 1008  

我会并且总是反汇编这些程序,以确保编译器和链接器解决了这些问题。 另请注意,例如__hello_from_thumb可以在任何拇指函数中使用,如果我从几个地方调用hello,一些arm,一些拇指和hello被编译为arm,那么arm调用将直接调用hello(如果他们可以到达它)并且所有的拇指调用将共享相同的hello_from_thumb(如果他们可以到达它)。

这些示例中的编译器假设代码保持相同模式(简单分支链接)并且链接器添加了交互操作代码…

如果你真的是指互联网而不是互通,那么请描述一下这是什么,我将删除这个答案。

编辑:

你在调用Double时使用了一个寄存器来保存lr,这将无法工作,没有寄存器可以用于你需要使用内存,最简单的是堆栈。 看看编译器是如何做到的:

 00001008 : 1008: e92d4008 push {r3, lr} 100c: eb000009 bl 1038 <__two_from_arm> 1010: e8bd4008 pop {r3, lr} 1014: e2800007 add r0, r0, #7 1018: e12fff1e bx lr 

推动r3可能使堆栈在64位边界上对齐(使其更快)。 需要注意的是链接寄存器保留在堆栈中,但pop不会弹出到pc,因为这不是ARMv4构建,因此需要从函数返回bx。 因为这是arm模式,我们可以弹出lr并简单地bx lr。

对于拇指你只能直接推r0-r7和lr并直接弹出r0-r7和pc你不想弹出到pc,因为这只适用于你在同一模式(拇指或arm)。 这对于皮层来说很好,如果你知道你的所有来电者都是好的,那就很好,但总的来说不好。 所以

 00001024 : 1024: b508 push {r3, lr} 1026: f000 f811 bl 104c <__three_from_thumb> 102a: 3005 adds r0, #5 102c: bc08 pop {r3} 102e: bc02 pop {r1} 1030: 4708 bx r1 

相同的交易r3用作虚拟寄存器以保持堆栈的性能对齐(我使用gcc 4.8.0的默认构建,这可能是具有64位axi总线的平台,指定架构可能删除该额外寄存器)。 因为我们不能弹出pc,我假设因为r1和r3会乱序并选择r3(他们可以选择r2并保存一条指令)有两个pop,一个用来摆脱堆栈上的虚拟值和另外,将返回值放在寄存器中,以便它们可以返回到它。

您的启动function不符合ABI,因此当您将其与诸如printf调用之类的大型库混合时,毫无疑问您将崩溃。 如果你没有这是愚蠢的运气。 主要的汇编列表显示r4和r10都没有被使用,并且假设main()不是除了bootstrap之外被调用的,那么这就是你逃脱r4或r10的原因。

如果这真的是LPC1769,那么这整个讨论是无关紧要的,因为它不支持ARM并且不支持互通(互通=混合ARM模式代码和拇指模式代码)。 你的问题与交互工作无关,你不是在互通(注意函数末尾的pop {pc})。 您的问题可能与汇编代码有关。

EDIT2:

更改makefile以指定cortex-m

 00001008 : 1008: b508 push {r3, lr} 100a: f000 f805 bl 1018  100e: 3007 adds r0, #7 1010: bd08 pop {r3, pc} 1012: 46c0 nop ; (mov r8, r8) 00001014 : 1014: 3003 adds r0, #3 1016: 4770 bx lr 00001018 : 1018: b508 push {r3, lr} 101a: f7ff fffb bl 1014  101e: 3005 adds r0, #5 1020: bd08 pop {r3, pc} 1022: 46c0 nop ; (mov r8, r8) 

首先,它是全拇指,因为在皮质m上没有arm模式,其次,function返回不需要bx(因为没有arm/拇指模式的变化)。 所以pop {pc}会起作用。

奇怪的是,虚拟寄存器仍然在推送时使用,我尝试了arm7tdmi / armv4t构建,它仍然这样做,所以还有一些其他标志用来摆脱这种行为。

如果您希望学习如何制作可以从C调用的汇编函数,那么您应该这样做。 创建一个有点类似于要在asm中创建的函数框架的C函数:

 extern unsigned int Double ( unsigned int ); unsigned int Start ( void ) { return(Double(42)); } 

组装然后拆卸

 00000000 : 0: b508 push {r3, lr} 2: 202a movs r0, #42 ; 0x2a 4: f7ff fffe bl 0  8: bd08 pop {r3, pc} a: 46c0 nop ; (mov r8, r8) 

并从组装function开始。

 .globl Start .thumb_func Start: push {lr} mov r0, #42 bl Double pop {pc} 

那,或者读取gcc的arm abi并理解你可以使用哪些寄存器而不能将它们保存在堆栈中,哪些寄存器用于传递和返回参数。

_start函数是调用main()的C程序的入口点。 为了调试任何C程序的_start函数是在汇编文件中。 实际上,linux上程序的真正入口点不是main(),而是一个名为_start()的函数。 标准库通常提供运行一些初始化代码的版本,然后调用main()。

尝试用gcc -nostdlib编译它: