编译 ARM 二进制文件,运行 它们在 ARMulator 中

Compile ARM binaries, run them in ARMulator

这是我的问题:

简介

我目前正在尝试在 ARM 处理器上执行一些基本代码。由于我目前(可能很长一段时间)身边没有任何 ARM 硬件,所以我已经使用 QEMU(一种 ARM 模拟器)好几天了,我不得不说,它的效果很好.但是用QEMU,有种拔刀杀苍蝇的感觉。所以我找了一些更轻的模拟器,发现了 ARMulator。

“ARMulator 是一系列程序,可模拟各种 ARM 处理器及其支持架构的指令集。 ARMulator 透明地连接到兼容的 ARM 调试器,以提供独立于硬件的 ARM 软件开发环境。通信通过远程调试接口 (RDI) 进行。”
(来源:http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dai0032f/index.html
“准确度很好,虽然它被归类为周期计数准确而不是周期准确,这是因为 ARM 流水线没有完全建模(尽管寄存器互锁是)。解决方案是一条指令,因此当单步执行时,寄存器互锁被忽略,并且返回的循环计数与程序简单 运行 不同,这是不可避免的。”
(来源:https://en.wikipedia.org/wiki/ARMulator

环境

从那里开始,我尝试了几个 ARMulator 版本。不得不说周围没有很多,事实上我只试过 3 个版本(名字不是官方的,那只是我给他们起的名字来识别它们):
- XYZ Armulator : https://github.com/x-y-z/armulator 2016-02-20
- SourceForge Armulator:https://sourceforge.net/projects/armulator/ 2013-04-26
- Phanrahan Armulator : https://github.com/phanrahan/armulator 2014-11-11

我不确定这些版本是官方的,我想我在不同的网站上看到过好几次,有些版本确实是官方的,可能是专有的,并且包含的​​工具比所需的更多。这是我正在谈论的一些例子:
- 在 ARM Connected Community 上,他们谈论了一个 RealViewDevelopmentSuite,它似乎包含 ARMulator:https://community.arm.com/message/12272#12272
- ......当我再次找到其中一个时会添加其他人 但这些解决方案不是免费的。

现在介绍工具链。我找到的资源中说明了两个不同的工具链:
- arm-elf-abi : 在 XYZ ARMulator GitHub 上声明,建议使用此命令 ($ arm-elf-gcc -mthumb -Bstatic -o ) 编译二进制 executable。
我找到的唯一版本是 Windows…遗憾的是,我找不到任何 Unix 版本。
- Arm-none-eabi:在本教程中声明:http://www.bravegnu.org/gnu-eprog/hello-arm.html,这是我一直在使用 QEMU 的那个。我在某处读到,编译 ARM 程序集时不需要 Arm-elf 工具链,而 Arm-none 就足够了。

我测试的两个程序是最简单的:

装配中的一个:helloarm.s

  .global _start
 .text  
entry:  b _start  
  .align  
_start: 
  mov   r4, #5         @ Load register r4 with the value 5
  mov   r3, #4         @ Load register r3 with the value 4
  add   r0, r4, r3     @ Add r3 and r4 and store in r0
  b stop
stop:  b stop @  Infinite loop

C 中的一个:test.c

int c_entry() {
  return 0;
}

编译过程

起初,我使用了本教程中解释的在 QEMU 上工作的相同方法:http://www.bravegnu.org/gnu-eprog/hello-arm.html。在 QEMU 上,一切正常,但仿真过程略有不同。在执行 QEMU 之前,我必须先将二进制文件加载到 RAM 中。 启动 ARMulator ($ armulator ) 的方式不同,我想二进制自动加载是 RAM。

我尝试了三种不同的编译方式,不确定哪种最合适one.Here :

程序集:

 $ arm-none-eabi-as –s -g helloarm.s -o helloarm.o    
 $ arm-none-eabi-ld -Ttext=0x0 -o helloarm.elf helloarm.o    
 $ arm-none-eabi-objcopy -O binary helloarm.elf helloarm.bin      

我们现在应该有两个“二进制文件”,一个 .bin 和一个 .elf。
我仍然不知道有什么区别。需要多读一些。

C :

 $ arm-none-eabi-gcc -mthumb -Bstatic --specs=nosys.specs srcs/main.c –o a.out  

一个额外的:
我还尝试了本教程中介绍的以下方法,这让我觉得 Armulator 是用来执行 .elf 二进制文件的。用这种方法制作的文件称为 c_entry.
https://balau82.wordpress.com/2010/02/14/simplest-bare-metal-program-for-arm/ 问题是一样的。

从那时起,我们有 6 个二进制文件:

问题

将 SourceForge 和 Phanrahan Armulator 与任何二进制文件(elf 或 bin)一起使用时:

$ ./armulator asm-helloarm.bin
Error opening file 00000000.bin
$ ./armulator a.out
Error opening file 00000000.bin
$ ./armulator helloarm.elf
Error opening file 00000000.bin

使用 XYZ Armulator 时:

$ armulator helloarm.bin
Segmentation fault (core dumped)

$ armulator a.out
Unexpect Instr:

可能原因
- ARMulator 不知道如何解码某些指令。情况可能是这样,但我的程序似乎太基础了……它什么都不做,returns 0…
- 我使用了错误的工具链,或者错误地使用了正确的工具链。
- Armulator 不应以这种方式使用。

注释
运行使用 arm-none-eabi-gdb 编译二进制文件时,我无法 运行 或启动程序。 只有此命令有效:target,但它只会将目标文件重置为已选择的二进制文件。
当我输入开始时,它说“没有加载符号 table。使用 "file" 命令。”

提前感谢您的帮助,或者至少感谢您的阅读,
希望我不是唯一一个在 Armulator 上遇到困难的人。

此致,
约翰

我认为重点在于您关于没有 ARM 的评论。很大一部分事物具有某种 on/off 开关。有一个手臂。如果你有一台 x86 计算机来阅读这个网页,那台计算机可能有几个 ARMS 以及一些其他处理器。

无论如何。感谢您指出那些 links,非常棒。查看最后一个 Phanrahan Armulator 源码,我们可以在 PutWord 函数中看到一些特殊地址。我没有读完你的整个问题 TL;DR,所以直接跳到一个简单的工作示例中。

hello.s:

.globl _start
_start:
    b reset
    b hang
    b hang
    b hang
    b hang
    b hang
    b hang
    b hang

hang: b hang

reset:
    mov r0,#0x16000000
    mov r1,#0x55
    str r1,[r0]
    add r1,r1,#1
    str r1,[r0]
    mov r0,#0xF0000000
    str r1,[r0]
    b hang

内存映射:

MEMORY
{
    ram : ORIGIN = 0x00000000, LENGTH = 0x1000
}
SECTIONS
{
    .text : { *(.text*) } > ram
}

生成文件:

#ARMGNU ?= arm-none-linux-gnueabi
ARMGNU ?= arm-none-eabi
#ARMGNU ?= arm-linux-gnueabi
COPS = -Wall -O2 -nostdlib -nostartfiles -ffreestanding 


all : hello.bin

clean :
    rm -f *.o
    rm -f *.bin
    rm -f *.elf
    rm -f *.list
    rm -f *.srec
    rm -f *.hex

hello.o : hello.s
    $(ARMGNU)-as hello.s -o hello.o


hello.bin : hello.o memmap
    $(ARMGNU)-ld -T memmap hello.o -o hello.elf
    $(ARMGNU)-objdump -D hello.elf > hello.list
    $(ARMGNU)-objcopy hello.elf -O ihex hello.hex
    $(ARMGNU)-objcopy hello.elf -O srec hello.srec 
    $(ARMGNU)-objcopy hello.elf -O binary hello.bin 

构建副本 hello.bin 到 00000000.bin 后,正如错误消息所暗示的那样。

然后

./armulator

r0 = 16000000
r1 = 00000055
Ur1 = 00000056
Vr0 = f0000000

ERROR PutWord(0xF0000000,0x56)
NumScycles 8
NumNcycles 7
NumIcycles 0
NumCcycles 0
NumFcycles 0
NumInstrs  8
TotlCycles 15

我们看到 U 和 V 字符出现(0x55 和 0x56)加上其他特殊地址反应。

hello.list

00000000 <_start>:
   0:   ea000007    b   24 <reset>
   4:   ea000005    b   20 <hang>
   8:   ea000004    b   20 <hang>
   c:   ea000003    b   20 <hang>
  10:   ea000002    b   20 <hang>
  14:   ea000001    b   20 <hang>
  18:   ea000000    b   20 <hang>
  1c:   eaffffff    b   20 <hang>

00000020 <hang>:
  20:   eafffffe    b   20 <hang>

00000024 <reset>:
  24:   e3a00416    mov r0, #369098752  ; 0x16000000
  28:   e3a01055    mov r1, #85 ; 0x55
  2c:   e5801000    str r1, [r0]
  30:   e2811001    add r1, r1, #1
  34:   e5801000    str r1, [r0]
  38:   e3a0020f    mov r0, #-268435456 ; 0xf0000000
  3c:   e5801000    str r1, [r0]
  40:   eafffff6    b   20 <hang>

正如您目前可能已经阅读的全尺寸复位处理程序必须是地址为零的第一条指令。理想情况下,它是一个分支或一个加载 pc,因为你只有一个指令来获得 out/over 异常 table.

bin、hex、elf 等。想想 gif、jpg、png 等。各种不同的文件格式既有像素数据,也有其他信息,例如图像的像素宽和高。也许是一些其他的编码或压缩或其他什么。您可以使用文本编辑器检查 .hex 和 .srec,它们是流行的 ascii 文件格式。这些中的每一个都只是为我们的程序存储指令和数据的不同方式。由于各种原因,有各种各样的格式,就像图像文件一样,有他们的原因,为什么有人决定制作一个新的。在这种情况下,“.bin”格式并不是所有具有该扩展名的文件的意思,但是当您将 -O binary 与 objcopy 一起使用时,您将获得原始内存映像

hexdump -C hello.bin 
00000000  07 00 00 ea 05 00 00 ea  04 00 00 ea 03 00 00 ea  |................|
00000010  02 00 00 ea 01 00 00 ea  00 00 00 ea ff ff ff ea  |................|
00000020  fe ff ff ea 16 04 a0 e3  55 10 a0 e3 00 10 80 e5  |........U.......|
00000030  01 10 81 e2 00 10 80 e5  0f 02 a0 e3 00 10 80 e5  |................|
00000040  f6 ff ff ea                                       |....|
00000044

并将其与上面的列表进行比较,您会发现这只是原始 instruction/program 数据。这显然是这个模拟器想要的。

如果你想将它扩展到 C 程序中,你至少需要设置一个堆栈(例如 mov sp,#0x4000)然后分支 link 到入口点的名称,不必须是 main() 有时你不想要 main() 因为一些编译器会添加额外的垃圾。然后在我的情况下,在 bl 到 notmain 之后我 b 挂起处理一个 return 如果有的话。

您想在我的 makefile 中使用所有这些 COPS 标志。

它们是我编写裸机的方式,我可以使用 gcc 编译器 arm-none-eabi、arm-none-linux-gnueabi 等的任何变体.差异应该与包含或支持的 C 库有关。在这些情况下,普通的 glibc 与 newlib。我不调用 C 库函数,所以我在那里没有问题,我只需要一个原始编译器来从 C 到对象。 linker 脚本可以像我展示的一样简单,您可以根据需要添加 .data 和 .bss。如果您觉得有必要,它们可能会变得更加复杂。如果您不在 linker 脚本中调出对象,那么 linker 会按命令行顺序使用这些对象,因此您的输入对象(开头为 table 的对象),必须是对象列表中的第一个。