Ben*_*oît 6 assembly arm gnu-assembler att
我刚刚开始学习ARM程序集,我不明白为什么GNU as语法与x86*不同.
由于指令是相同的,我会期望一切都像x86*,除了指令本身,但相反,我正在努力加载字符串的地址,等等.我从头开始通过在线阅读一些PDF ,man 2 syscall并反编译基本的例子,因为我不确定我可以在网上找到的各种Hello World的价值.
我的问题:
%印记#或一个$sigil.事实上,如果我编译mov r0, $0,objdump -D给我一个回复mov r0, #1.一切都汇集到了同样的东西mov r0, #1:
mov %r0, $1
10080: e3a00001 mov r0, #1
mov r0, $1
10084: e3a00001 mov r0, #1
mov %r0, #1
10088: e3a00001 mov r0, #1
mov r0, #1
1008c: e3a00001 mov r0, #1
Run Code Online (Sandbox Code Playgroud)
我无法直接使用标签的地址来加载字符串地址,所以我需要使用一个变量.mov r1, $hello或者ldr r1, $hello不工作.在x86_64中,我会写的mov $hello, %rsi.所以我正在做gcc所做的事情,我正在用另一个标签的地址创建一个单词.
我无法放置我的常量.rodata或者我得到了一个Error: internal_relocation (type: OFFSET_IMM) not fixed up,但是将所有内容都放在了.text工作中(这部分与语法无关)
.section .text
hello:
.asciz "Hello World\n"
.set hello_len, .-hello
hello_addr:
.word hello
.align 4
.global _start
_start:
mov r0, $1
ldr r1, hello_addr
mov r2, $hello_len
mov r7, $4
swi $0
mov r0, $0
mov r7, $1
swi $0
Run Code Online (Sandbox Code Playgroud)
GNU汇编程序(GAS)使用AT&T语法进行x86汇编的原因是为了与AT&T的x86汇编程序兼容.AT&T没有使用基于英特尔官方x86汇编语法的语法,而是选择基于早期的68000和PDP-11汇编器创建新语法.当x86支持被添加到GNU编译器(GCC)时,它生成了AT&T语法汇编,因为那是他们使用的汇编程序.在此之后的某个时间创建GAS时,GNU汇编程序必须使用该语法.
但是,没有适用于ARM CPU的AT&T汇编程序版本.当GNU项目开始将GCC和GAS移植到ARM目标时,没有理由为ARM程序集创建自己的新的和不兼容的语法.相反,它们基于ARM官方语法使用的语法.这意味着您可以在ARM的官方文档中查找ARM指令,并使用您在GNU汇编程序中看到的语法和操作数顺序.在AT&T语法中编写x86程序集时,您只需要知道规则和异常,这些规则和异常在任何地方都没有正式记录.
您无法将地址直接加载到ARM程序集中的寄存器中的原因不是语法问题.ARM CPU根本没有可以执行此操作的指令.所有ARM指令的大小相同,为32位,没有空间将32位地址编码为立即数.但是ARM汇编确实提供了LDR伪指令的形式,可以处理自动加载一个32位的地址和常数:ldr r1, =hello.这将导致汇编程序将32位常量存储在文字表中,并使用PC相对LDR指令将其加载到内存中.如果加载的常量恰好足够小,可以使用MOV或MVN直接加载,而是生成指令.
你不能把常量放入的原因.rodata要么是因为使用PC相对LDR指令来解决它太远了(它需要使用+/- 4KB,因为这个最大位移可以适合单个32位ARM指令)或您正在使用的对象格式不支持PC相对寻址到不同的部分.(您的ldr r1, hello_addr指令使用PC相对寻址,因为无法在ARM指令中对32位地址进行编码.)
汇编语言由汇编程序定义,汇编程序是解析它的程序.创建或创建汇编程序符合处理器供应商(IP或芯片)的最佳利益.记录机器语言也符合他们的最佳利益,因此它们将机器语言与他们创建或签约的汇编语言相匹配,以便这些项目一起工作.汇编语言绝不是适用于所有平台的通用事物,没有理由认为对于同一目标,不同的汇编程序将使用相同的汇编语言,最着名的是AT&T与intel x86的悲惨结果.英特尔本可以更好地确定,但它是CISC并且当时有意义(mov指令如此过载,但汇编语言仍然可以更清洁一点,记住我们现在已经有几十年的经验了很多经验).
到目前为止,GNU总是在添加目标时破坏了目标存在的汇编语言,因此它们为该目标创建了一种新的汇编语言.也许是故意不相容,有时关闭,但仍然足以与之不相容.同样,有一些指令适用于gnu汇编程序汇编语言,但后来存在差异.现实情况是,它不是"GNU",而是选择为该目标创建该端口的个人或团队,并且他们做任何他们认为哪种是汇编语言的本质.
如果你在ARM之前学会了x86我真的觉得你,我真的希望x86不是你的第一个汇编语言.百分号注册事物在历史上并不是一个x86的东西,实际上有人觉得他们需要添加它时有点悲伤,因为当时许多汇编程序已经被编写,证明缺乏对这种东西的需求.ARM汇编语言无论是GNU还是ARM汇编程序的众多版本之一,都是最干净的汇编语言之一,最有意义,最不模糊.
重要的是机器代码,机器代码是您必须符合该目标的标准,而不是汇编语言.您可以制作机器代码,汇编语言可以并且确实有所不同,这是汇编语言的本质.与AT&T以及已经完成各个GNU目标端口的人一样,如果您使用通用文件格式进行对象输出(在ARM的情况下是elf),您肯定会编写自己的汇编语言和汇编语言,那么您可以使用汇编程序编写汇编语言,然后使用GNU工具将其与C或其他工具链接.没有人阻止你这样做,这是学习指令集的一种非常好的方法,我更喜欢编写反汇编程序或指令集模拟器但编写汇编程序(大致是周末任务,可能还有几个星期的时间用于微调)也会做得很好.
人们可能很容易抱怨x86 GNU汇编语言看起来不像arm或mips,填补空白.不太相关,有很明显的原因.Ssemi-portable可以使用gnu端口之前的文档或工具.这本身就是为什么gnu汇编程序甚至根本就被使用...如果在一些其他处理器通常找到语法之后设计了arm后端,那么有人会创建一个备用端口.还要注意在gnu世界中发生令人不安的武器装配,也许你应该跳上那个潮流......
要回答您的实际问题,因为您确实有实际问题.这些是完全不同的指令集x86和arm.CISC与RISC,你不能有一个固定大小的指令,适合你想要的任何大小的立即.immediates有规则(请阅读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
Run Code Online (Sandbox Code Playgroud)
给
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
Run Code Online (Sandbox Code Playgroud)
如果它们不适合它或它是一个标签,它们会为你创造价值(在.text中,因为你不能认为你可以到达任何其他部分).如果他们可以为你创建一个mov(至少GAS会).
或者你可以像mylabeladd一样自己制作电脑相对负载
如果您想要访问任何其他部分,那么您必须正确地执行此操作:
.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
Run Code Online (Sandbox Code Playgroud)
联系时给予
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
Run Code Online (Sandbox Code Playgroud)
您必须对外部标签执行相同的操作,但对于分支等,在同一.text部分中,链接器将尝试帮助您.
.globl _start
_start:
b fun
Run Code Online (Sandbox Code Playgroud)
在另一个文件中
.globl fun
fun:
b .
Run Code Online (Sandbox Code Playgroud)
并不奇怪......
00000000 <_start>:0:eaffffff b 4
00000004:4:eafffffe b 4
但是如果
.thumb
.thumb_func
.globl fun
fun:
b .
Run Code Online (Sandbox Code Playgroud)
谢谢gnu!
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
Run Code Online (Sandbox Code Playgroud)
或模拟一个非常大的程序
.globl _start
_start:
b fun
.space 0x10000000
Run Code Online (Sandbox Code Playgroud)
叹:
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
Run Code Online (Sandbox Code Playgroud)
那么就像跨越各个部分一样
.globl _start
_start:
ldr r0,=fun
bx fun
.ltorg
.space 0x10000000
Run Code Online (Sandbox Code Playgroud)
这工作......
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>
Run Code Online (Sandbox Code Playgroud)
但你必须确保链接器正在帮助你,因为它可能没有,从手臂到拇指的蹦床总是在那里......
.globl _start
_start:
b fun
.globl more_fun
more_fun:
b .
Run Code Online (Sandbox Code Playgroud)
其他档案
.thumb
.thumb_func
.globl fun
fun:
b more_fun
Run Code Online (Sandbox Code Playgroud)
产生完美破碎的代码.
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
Run Code Online (Sandbox Code Playgroud)
现在我使用了更多可能有效的gnu特定语法...
.globl _start
_start:
b fun
void more_fun ( void )
{
return;
}
Run Code Online (Sandbox Code Playgroud)
不,不要猜
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
Run Code Online (Sandbox Code Playgroud)
虽然乐趣的所有部分......显然你正在处理不同的指令集x86,arm,mips,avr,msp430,pdp11,xtensa,risc-v和其他gnu支持的目标.一旦你学会了一种汇编语言,或者两三种汇编语言,其余的都比不同语言更相似,语法就是语法,很容易超越,真正的问题是你能做什么或不做什么.答案通常在于该供应商的文档(不仅仅是你用Google搜索的一些指令集参考)