ARM汇编程序指令

时间:2017-05-15 12:03:59

标签: assembly arm

我正在研究ARM汇编程序,当我开始解释如何从文件读取/写入时,我不明白它是如何分支的,代码是这样的:

@ fopen input argv[1]
PUSH {R1}
LDR R0, [R1,#0x04]
LDR R1, =r
BL fopen
LDR R1, =fin
STR R0, [R1]

BL fopen 它在哪里分支?对 fopen 的唯一提及是:

.global .fopen

后来在该计划中。 我想也许我不明白以点开头的指令是如何起作用的,但我在网上找到的唯一一个就是它们被称为指令。 任何人都可以澄清这个吗?

1 个答案:

答案 0 :(得分:2)

这是一个涵盖你所要求的内容的例子,也许更多......

one.s

.globl _start
_start:
    bl notmain
    bl hello
    b .

two.s

.extern notmain

.globl hello
hello:
    bl notmain
    bl there
    bx lr

there:
    bx lr

three.c

unsigned int x;
void notmain ( void )
{
    x=5;
}

so.ld

MEMORY
{
    bob : ORIGIN = 0x08000000, LENGTH = 0x1000
    ted : ORIGIN = 0x20000000, LENGTH = 0x1000
}

SECTIONS
{
    .text : { *(.text*) } > bob
    .rodata : { *(.rodata*) } > bob
    .bss : { *(.bss*) } > ted
}

arm-none-eabi-as --warn --fatal-warnings one.s -o one.o
arm-none-eabi-as --warn --fatal-warnings two.s -o two.o
arm-none-eabi-gcc -Wall -Werror -O2 -nostdlib -nostartfiles -ffreestanding -c three.c -o three.o
arm-none-eabi-ld -T so.ld one.o two.o three.o -o so.elf
arm-none-eabi-objdump -D so.elf > so.list

因此,对于这些看起来很简单,这里有很多内容,首先,汇编语言由汇编程序定义,解析它的程序,有些将尝试与其他汇编程序兼容(nasm和以masm为例)。但有些情况并非如此,尤其是涉及非汇编指令类型的指令时。 所以上面是基于gnu的,用于汇编器和链接器的binutils,用于编译器的gcc。使用通用的全尺寸ARM目标。

Gnu链接器需要一个名为_start的标签,用作入口点,注意它不关心main,main通常是由" bootstrap"通常为您准备工具链的代码。在这种情况下,我正在制作自己的,并使用工具链作为一组工具......

所以你可以使用.globl或.global它们是相同的,还有其他方法可以做到这一点,在其他汇编语言中你可能有一个FUNCTION或PROCEDURE或其他指令用于声明这个标签不仅仅是一个标签。 Gnu汇编程序(gas)认为标签(在这种情况下是_start:)就像在C文件中的函数上放置静态一样,gas假定标签是本地的,C假设标签是全局的(除了函数内部的自然例外等) ...

就像当你从C程序中的一个文件调用另一个函数时,链接器稍后会解析(即使你执行gcc hello.c -o hello gcc正在调用整个链,多个程序只处理编译到程序集语言,然后汇编程序,然后链接器然后清除临时文件,除非你告诉它不)。与gcc不同,当我们使用未在任何地方定义或声明为外部的标签时,气体不会抱怨。

所以看一下one.o

的输出

注意没有任何处理器等只是一个工具链,你可以自己做这些实验。我的代码至少你可以使用arm-none-linux-gnueabi,arm-linux-gnueabi也不一定需要arm-none-eabi

Disassembly of section .text:

00000000 <_start>:
   0:   ebfffffe    bl  0 <notmain>
   4:   ebfffffe    bl  0 <hello>
   8:   eafffffe    b   8 <_start+0x8>

好的,这是一个意外,我发誓。首先,寻址从零开始,因为这是一个尚未链接的对象。因为notmain和hello在这一点上是未解析的外部组件,所以汇编程序可以执行bl指令,但没有使用偏移量,因此gas选择将分支编码为self。接下来,最后一行上的点表示自我b。意味着分支到自我,我可以在前面放一个标签,然后分支到那个标签

here: b here

最终使用更清晰易读的代码。 gnu汇编程序还有其他有趣的事情可以做

1:
 b 1f
 b 1b
1:

1来自标签1,f表示向前分支到向前看的编号为1的标签。分支向标签1向后看(1b),因此第一条指令向前分支两条,第二条向后向后分支。

c:  ea000000    b   14 <_start+0x14>
  10:   eafffffd    b   c <_start+0xc>

你可以对汇编程序特有的细微差别感到疯狂,这些细微差别可以使代码更易于输入,但更难以阅读,并且更不便于使用。

arm-none-eabi-objdump -D two.o

00000000 <hello>:
   0:   ebfffffe    bl  0 <notmain>
   4:   eb000000    bl  c <there>
   8:   e12fff1e    bx  lr

0000000c <there>:
   c:   e12fff1e    bx  lr

在two.s中我确实声明了一个外部标签,没有受到伤害,更清洁的汇编程序做的更少。我也声明并使用了一个本地标签,那里,汇编器可以找到这个标签并且可以为这个标签生成一个带有偏移量的正确bl,所以链接器不需要,对于notmain标签虽然它仍然需要填充一些内容留待链接器稍后修复。

00000000 <notmain>:
   0:   e3a02005    mov r2, #5
   4:   e59f3004    ldr r3, [pc, #4]    ; 10 <notmain+0x10>
   8:   e5832000    str r2, [r3]
   c:   e12fff1e    bx  lr
  10:   00000000    andeq   r0, r0, r0

three.o来自C程序,标签/函数notmain是自动全局的,因为我没有在它前面添加静态同样x是一个全局变量。

但是x位于.bss部分,它与.text部分是分开的,其中代码是这样的,此时编译器不知道该标签有多远,所以它生成的代码是半特定的指令集,其他像x86可能只使用far mov而不是pc relative,这是在这里使用的。地址/偏移量0x10处的0x00000000是链接器将用x的地址填充的位置,代码可以生成该地址的读取,然后读取该地址的数据并进行分配。

MEMORY
{
    bob : ORIGIN = 0x08000000, LENGTH = 0x1000
    ted : ORIGIN = 0x20000000, LENGTH = 0x1000
}

SECTIONS
{
    .text : { *(.text*) } > bob
    .rodata : { *(.rodata*) } > bob
    .bss : { *(.bss*) } > ted
}

链接器脚本和/或命令行非常特定于供应商工具链中的链接器,如编译器编译指示或其他指令,汇编程序指令,链接描述文件是特定于工具的,并且不希望与其他工具或其他版本兼容。相同的工具。

对于gnu ld(链接器)它没有比这更简单我避免像rom和ram这样的词来表明它们没有影响它们只是连接内存空间描述和哪些部分之间的点的名称我想要在那些记忆空间。

将所有这些与所有这些工作链接在一起的链接器放在一起。

Disassembly of section .text:

08000000 <_start>:
 8000000:   eb000005    bl  800001c <notmain>
 8000004:   eb000000    bl  800000c <hello>
 8000008:   eafffffe    b   8000008 <_start+0x8>

0800000c <hello>:
 800000c:   eb000002    bl  800001c <notmain>
 8000010:   eb000000    bl  8000018 <there>
 8000014:   e12fff1e    bx  lr

08000018 <there>:
 8000018:   e12fff1e    bx  lr

0800001c <notmain>:
 800001c:   e3a02005    mov r2, #5
 8000020:   e59f3004    ldr r3, [pc, #4]    ; 800002c <notmain+0x10>
 8000024:   e5832000    str r2, [r3]
 8000028:   e12fff1e    bx  lr
 800002c:   20000000    andcs   r0, r0, r0

Disassembly of section .bss:

20000000 <x>:
20000000:   00000000    andeq   r0, r0, r0

我说我希望.text位于0x08000000,就在那里,我首先在命令行上放置one.o,所以它的代码首先出现,然后是两个然后是三个。您可以使用链接描述文件来更改此内容,但在我使用gnu链接器的过程中,它会按命令行顺序执行。

00000000 <_start>:
   0:   ebfffffe    bl  0 <notmain>
   4:   ebfffffe    bl  0 <hello>
   8:   eafffffe    b   8 <_start+0x8>

08000000 <_start>:
 8000000:   eb000005    bl  800001c <notmain>
 8000004:   eb000000    bl  800000c <hello>
 8000008:   eafffffe    b   8000008 <_start+0x8>

比较链接之前和之后,链接器在命令行上找到了一个包含外部标签(notmain和hello)的对象,并修改了指令以正确访问它们。

同样来自two.s的hello函数也调用了notmain resolved。由于汇编程序已经解决了对那里的调用没有改变。

最后,notmain的地址为x填充,以便它可以根据需要修改它。

因此,就像表面上出现的代码一样简单,有很多通用的工具链以及工具链特定的东西。

在您的情况下,链接器成功使用fopen标签(函数)的库或其他对象将被链接并且对该函数的调用已解决。

每当有人询问如何在汇编中执行X时,答案通常与您在其他语言中使用的方式相同,在这种情况下,如何以汇编语言打开文件,首先必须进行操作系统和/或许多代码来处理文件系统和硬件,在这种情况下答案是简单地调用fopen库函数来处理操作系统特定的东西,操作系统处理文件和硬件的东西......