我假设您的意思是互通而不是互联互通?LPC1769 是一个 cortex-m3,它只是 thumb/thumb2,因此它不支持 arm 指令,因此该平台没有可用的交互操作。尽管如此,使用编译器看看发生了什么:
先让编译器为你做这件事,然后在 asm 中自己尝试...
开始.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);
}
二.c
unsigned int two ( unsigned int t )
{
return(t+5);
}
生成文件
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 <hang+0x2>)
1002: 46fe mov lr, pc
1004: 4700 bx r0
00001006 <hang>:
1006: e7fe b.n 1006 <hang>
1008: 0000100c andeq r1, r0, ip
0000100c <hello>:
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 <two>:
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 <hello>:
0: e92d4008 push {r3, lr}
4: ebfffffe bl 0 <two>
8: e8bd4008 pop {r3, lr}
c: e2800007 add r0, r0, #7
10: e12fff1e bx lr
编译器使用 bl 假设/希望它将从 arm 调用 arm。但它没有,所以他们所做的就是在里面放了一个蹦床。
0000100c <hello>:
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 切换到 thumb 模式,被加载到一次性寄存器 ip(r12?)然后 bx ip 发生切换模式。分支链接在 lr 中设置了返回地址,这无疑是一个 arm 模式地址(lsbit 零)。
00001020 <two>:
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} ,除非您确实需要保存该指令并且不互通)。现在在一个只有 thumb+thumb2 的 cortex-m3 上,你不能互通,所以你可以使用 mov pc,lr 和 pop {pc},但是代码不可移植,这不是一个好习惯,因为那个习惯会当您切换回手臂编程时会咬您。
因此,由于 hello 在使用设置链接寄存器的 bl 时处于 arm 模式,因此 two_from_arm 中的 bx 不会触及链接寄存器,因此当 two() 返回 bx lr 时,它会在 bl __two_from_arm 之后返回 arm 模式hello() 函数中的行。
还要注意拇指功能后额外的 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 <two>:
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 <three>
104c: 00000000 andeq r0, r0, r0
现在这是一个非常酷的蹦床。bl 到three_from_thumb 处于thumb 模式,链接寄存器设置为返回two() 函数,lsbit 设置无疑表示返回thumb 模式。
蹦床以 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
那实际上行不通吗?它会让你从一个手臂到另一个拇指,而不是从一个拇指到另一个手臂。我将把它作为练习留给读者。
如果您将 start.s 更改为此
.thumb
.globl _start
_start:
bl hello
hang : b hang
链接器会照顾我们:
00001000 <_start>:
1000: f000 f820 bl 1044 <__hello_from_thumb>
00001004 <hang>:
1004: e7fe b.n 1004 <hang>
...
00001044 <__hello_from_thumb>:
1044: 4778 bx pc
1046: 46c0 nop ; (mov r8, r8)
1048: eaffffee b 1008 <hello>
我会并且总是反汇编这样的程序,以确保编译器和链接器解决了这些问题。还要注意,例如 __hello_from_thumb 可以从任何 thumb 函数中使用,如果我从几个地方调用 hello,一些 arm,一些 thumb,并且 hello 是为 arm 编译的,那么 arm 调用将直接调用 hello(如果他们可以到达)并且所有拇指调用将共享相同的 hello_from_thumb (如果它们可以到达)。
这些示例中的编译器假设代码保持在相同模式(简单分支链接),并且链接器添加了互通代码......
如果您真的指的是互联网络而不是互联互通,那么请描述那是什么,我将删除此答案。
编辑:
您在调用 Double 期间使用寄存器来保留 lr,这将不起作用,没有寄存器适用于您需要使用内存的情况,最简单的是堆栈。看看编译器是怎么做的:
00001008 <hello>:
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 位边界上对齐堆栈(使其更快)。需要注意的是链接寄存器保留在堆栈中,但弹出不会弹出到 pc,因为这不是 ARMv4 构建,因此需要一个 bx 从函数返回。因为这是 arm 模式,我们可以弹出到 lr 并简单地 bx lr。
对于拇指,您只能直接按下 r0-r7 和 lr,然后直接弹出 r0-r7 和 pc,您不想弹出到 pc,因为只有在您处于相同模式(拇指或手臂)时才有效。这对 cortex-m 来说很好,或者如果你知道你所有的呼叫者是什么也很好,但总的来说很糟糕。所以
00001024 <two>:
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 并保存一条指令),所以有两个弹出,一个是为了摆脱堆栈上的虚拟值,另一个是其他将返回值放在寄存器中,以便他们可以bx到它返回。
您的 Start 函数不符合 ABI,因此当您将它与 printf 调用等大型库混合时,毫无疑问您会崩溃。如果你没有,那就是愚蠢的运气。您的 main 程序集列表显示既没有使用 r4 也没有使用 r10,并且假设除了引导程序之外没有调用 main(),那么这就是您逃脱 r4 或 r10 的原因。
如果这真的是 LPC1769,那么整个讨论是无关紧要的,因为它不支持 ARM 也不支持互通(互通 = ARM 模式代码和拇指模式代码的混合)。您的问题与互通无关,您不是互通(注意函数末尾的 pop {pc})。您的问题可能与您的汇编代码有关。
编辑2:
更改 makefile 以指定 cortex-m
00001008 <hello>:
1008: b508 push {r3, lr}
100a: f000 f805 bl 1018 <two>
100e: 3007 adds r0, #7
1010: bd08 pop {r3, pc}
1012: 46c0 nop ; (mov r8, r8)
00001014 <three>:
1014: 3003 adds r0, #3
1016: 4770 bx lr
00001018 <two>:
1018: b508 push {r3, lr}
101a: f7ff fffb bl 1014 <three>
101e: 3005 adds r0, #5
1020: bd08 pop {r3, pc}
1022: 46c0 nop ; (mov r8, r8)
首先,它全是拇指,因为 cortex-m 上没有手臂模式,其次,函数返回不需要 bx(因为没有手臂/拇指模式更改)。所以 pop {pc} 会起作用。
奇怪的是,虚拟寄存器仍然在推送中使用,我尝试了 arm7tdmi/armv4t 构建,它仍然这样做,所以还有一些其他标志可以用来摆脱这种行为。
如果您的愿望是学习如何制作可以从 C 中调用的汇编函数,那么您应该已经完成了。创建一个有点类似于您要在 asm 中创建的函数的框架的 C 函数:
extern unsigned int Double ( unsigned int );
unsigned int Start ( void )
{
return(Double(42));
}
组装然后拆卸
00000000 <Start>:
0: b508 push {r3, lr}
2: 202a movs r0, #42 ; 0x2a
4: f7ff fffe bl 0 <Double>
8: bd08 pop {r3, pc}
a: 46c0 nop ; (mov r8, r8)
并从组装功能开始。
.globl Start
.thumb_func
Start:
push {lr}
mov r0, #42
bl Double
pop {pc}
那,或者阅读 gcc 的 arm abi 并了解哪些寄存器可以使用和不能使用而不将它们保存在堆栈中,哪些寄存器用于传递和返回参数。