汇编语言是由汇编程序定义的,即解析它的程序。创建或创建汇编程序符合处理器供应商(IP 或芯片)的最大利益。记录机器语言也符合他们的最大利益,因此他们将机器语言与他们创建或承包的汇编语言相匹配,以便这些项目一起工作。汇编语言绝不是适用于所有平台的通用事物,没有理由假设对于相同的目标,不同的汇编程序会使用相同的汇编语言,最著名的是 AT&T 与 intel x86 的悲惨结果。英特尔本可以做得更好,但它是 CISC 并且在当时是有意义的(mov 指令如此重载,但汇编语言仍然可以更简洁一些,请记住,我们现在已经有几十年的经验了)。
据我所知,当添加目标时,GNU 总是会破坏目标存在的汇编语言,因此它们会为该目标创建一种新的汇编语言。也许是故意不相容,有时接近,但仍然足以不相容。同样,有一些指令可以跨 gnu 汇编器汇编语言工作,但也存在差异。现实情况是,不是“GNU”,而是选择为该目标创建该端口的个人或团队,他们做任何他们想做的事,这就是汇编语言的本质。
如果您在 ARM 之前学习 x86,我真的很同情您,我真的希望 x86 不是您的第一个汇编语言。百分号寄存器的东西在历史上不是 x86 的东西,当许多汇编程序被编写证明不需要这样的东西时,有人觉得他们需要添加它真的有点令人遗憾。 ARM 汇编语言,无论是 GNU 还是多种 ARM 汇编语言中的一种,都是最干净的汇编语言之一,最有意义,最不模糊。
重要的是机器代码,机器代码是您必须为该目标遵守的标准,而不是汇编语言。你能不能把机器码弄出来,汇编语言可以而且确实会有所不同,这就是汇编语言的本质。与 AT&T 和完成单个 GNU 目标端口的人一样,当然欢迎您编写自己的汇编程序和汇编语言,如果您使用通用文件格式作为对象输出(在 ARM 的情况下为 elf),那么您可以使用您的汇编器编写您的汇编语言,然后将其与 C 或其他使用 GNU 工具的链接。没有人阻止你这样做,这是学习指令集的好方法,我更喜欢编写反汇编程序或指令集模拟器,但编写汇编程序(大约是周末任务,可能还要几个工作日晚上进行微调)也会做得很好。
人们可以很容易地抱怨 x86 GNU 汇编语言看起来不像 arm 或 mips,填补了空白。不是很相关,有非常明显的原因。在 gnu 端口之前具有文档或工具的半便携式。这本身就是为什么甚至使用 gnu 汇编程序的原因......如果 arm 后端是按照其他一些处理器常见的语法设计的,那么有人会制作一个备用端口。另请注意,gnu 世界中正在发生令人不安的武器组装问题,也许您应该加入这个潮流......
回答您的实际问题,因为您确实有实际问题。这些是完全不同的指令集 x86 和 arm。 CISC vs RISC,你不能有一个固定大小的指令,也不能适应任何你想要的大小。立即数有规则(请阅读 ARM 文档以了解您尝试使用的说明),否则您必须执行 pc 相对负载,并且 pc 相对负载可以走的距离是有限的,您可能从一些 x86 指令中理解范围有限。到目前为止,各种汇编程序都给了我们一个伪代码解决方案:
ldr r0,=0x00110000
ldr r0,=0x12345678
ldr r0,=mylabel
ldr r0,mylabeladd
ldr r0,myvalue
b .
mylabeladd: .word mylabel
mylabel: .word 1,2,3,4
myvalue: .word 0x11223344
给予
00000000 <mylabeladd-0x18>:
0: e3a00811 mov r0, #1114112 ; 0x110000
4: e59f0024 ldr r0, [pc, #36] ; 30 <myvalue+0x4>
8: e59f0024 ldr r0, [pc, #36] ; 34 <myvalue+0x8>
c: e59f0004 ldr r0, [pc, #4] ; 18 <mylabeladd>
10: e59f0014 ldr r0, [pc, #20] ; 2c <myvalue>
14: eafffffe b 14 <mylabeladd-0x4>
00000018 <mylabeladd>:
18: 0000001c andeq r0, r0, r12, lsl r0
0000001c <mylabel>:
1c: 00000001 andeq r0, r0, r1
20: 00000002 andeq r0, r0, r2
24: 00000003 andeq r0, r0, r3
28: 00000004 andeq r0, r0, r4
0000002c <myvalue>:
2c: 11223344 ; <UNDEFINED> instruction: 0x11223344
30: 12345678 eorsne r5, r4, #120, 12 ; 0x7800000
34: 0000001c andeq r0, r0, r12, lsl r0
如果他们不适合它或者如果它是一个标签,他们会为您创造价值(在 .text 中,因为您不能假设您可以到达任何其他部分)。如果他们可以为您创建一个 mov(至少 GAS 可以)。
或者您可以像在 mylabeladd 中那样自己制作 pc 相对负载
如果你想到达任何其他部分,那么你必须正确地做:
.globl _start
_start:
mov r3,#1
ldr r0,=mydata
str r3,[r0]
ldr r1,mydataadd
str r3,[r1]
b .
mydataadd: .word mydata
.data
mydata: .word 0
链接时给予
00001000 <_start>:
1000: e3a03001 mov r3, #1
1004: e59f0010 ldr r0, [pc, #16] ; 101c <mydataadd+0x4>
1008: e5803000 str r3, [r0]
100c: e59f1004 ldr r1, [pc, #4] ; 1018 <mydataadd>
1010: e5813000 str r3, [r1]
1014: eafffffe b 1014 <_start+0x14>
00001018 <mydataadd>:
1018: 80000000 andhi r0, r0, r0
101c: 80000000 andhi r0, r0, r0
Disassembly of section .data:
80000000 <__data_start>:
80000000: 00000000 andeq r0, r0, r0
对于外部标签,您必须做同样的事情,但对于在同一个 .text 部分中的分支等,链接器会尝试帮助您。
.globl _start
_start:
b fun
在另一个文件中
.globl fun
fun:
b .
并不奇怪......
00000000 <_>:
0: eaffffff b 4
00000004:
4: eaffffe b 4
如果
.thumb
.thumb_func
.globl fun
fun:
b .
谢谢你!
00000000 <_start>:
0: ea000000 b 8 <__fun_from_arm>
00000004 <fun>:
4: e7fe b.n 4 <fun>
...
00000008 <__fun_from_arm>:
8: e59fc000 ldr r12, [pc] ; 10 <__fun_from_arm+0x8>
c: e12fff1c bx r12
10: 00000005 andeq r0, r0, r5
14: 00000000 andeq r0, r0, r0
或模拟一个非常大的程序
.globl _start
_start:
b fun
.space 0x10000000
叹息:
arm-none-eabi-ld -Ttext=0 so.o x.o -o so.elf
so.o: In function `_start':
(.text+0x0): relocation truncated to fit: R_ARM_JUMP24 against symbol `fun' defined in .text section in x.o
那么就像跨越部分一样
.globl _start
_start:
ldr r0,=fun
bx fun
.ltorg
.space 0x10000000
这行得通……
00000000 <_start>:
0: e51f0000 ldr r0, [pc, #-0] ; 8 <_start+0x8>
4: e12fff10 bx r0
8: 1000000d andne r0, r0, sp
...
1000000c <fun>:
1000000c: e7fe b.n 1000000c <fun>
但你必须确保链接器能帮助你,因为它可能不会帮助你,而且从手臂到拇指的蹦床也不总是在那里......
.globl _start
_start:
b fun
.globl more_fun
more_fun:
b .
其他文件
.thumb
.thumb_func
.globl fun
fun:
b more_fun
产生完全损坏的代码。
00000000 <_start>:
0: ea000002 b 10 <__fun_from_arm>
00000004 <more_fun>:
4: eafffffe b 4 <more_fun>
00000008 <fun>:
8: e7fc b.n 4 <more_fun>
a: 0000 movs r0, r0
c: 0000 movs r0, r0
...
00000010 <__fun_from_arm>:
10: e59fc000 ldr r12, [pc] ; 18 <__fun_from_arm+0x8>
14: e12fff1c bx r12
18: 00000009 andeq r0, r0, r9
1c: 00000000 andeq r0, r0, r0
现在我是否使用了更多可能有效的 gnu 特定语法...
.globl _start
_start:
b fun
void more_fun ( void )
{
return;
}
不,猜不到
00000000 <_start>:
0: ea000002 b 10 <__fun_from_arm>
00000004 <more_fun>:
4: e12fff1e bx lr
00000008 <fun>:
8: e7fc b.n 4 <more_fun>
a: 0000 movs r0, r0
c: 0000 movs r0, r0
...
00000010 <__fun_from_arm>:
10: e59fc000 ldr r12, [pc] ; 18 <__fun_from_arm+0x8>
14: e12fff1c bx r12
18: 00000009 andeq r0, r0, r9
1c: 00000000 andeq r0, r0, r0
虽然所有的乐趣...显然你正在处理不同的指令集 x86、arm、mips、avr、msp430、pdp11、xtensa、risc-v 和其他 gnu 支持的目标。一旦您学习了一种或两种或三种汇编语言,其余的则相似多于不同,语法就是语法,易于超越,真正的问题是您可以使用该指令集做什么或不做什么。答案通常在该供应商的文档中(不仅仅是您搜索的一些指令集参考)