为什么GNU在x86和ARM之间的语法不同?

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)

Ros*_*dge 9

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位地址进行编码.)


old*_*mer 7

汇编语言由汇编程序定义,汇编程序是解析它的程序.创建或创建汇编程序符合处理器供应商(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搜索的一些指令集参考)