QEMU用户模式测试AARCH64程序

QEMU的两种模式

QEMU(快速模拟器)是一个开源的机器模拟器和虚拟化器,它能够模拟多种处理器架构,并且可以在不同平台上运行。QEMU 支持两种模式:用户模式和系统模式。

  • 用户模式(User Mode):
    • 用户模式下的 QEMU 仅模拟用户空间的二进制执行环境,不模拟底层硬件。
    • 这种模式下,QEMU 可以运行不同架构编译的二进制程序,而不需要这些程序与宿主机的架构相匹配。
    • 用户模式通常用于测试和运行不同架构的应用程序,或者在不支持特定硬件架构的系统上运行程序。
    • 用户模式不需要硬件虚拟化支持,因为它不模拟硬件。
  • 系统模式(System Mode):
    • 系统模式下的 QEMU 模拟整个计算机系统,包括 CPU、内存、硬盘、网络接口等硬件设备。
    • 这种模式下,QEMU 可以运行完整的操作系统镜像,允许用户安装和运行完整的操作系统。
    • 系统模式需要更多的资源,并且可能需要硬件虚拟化支持,以提高性能和安全性。
    • 系统模式允许用户进行更全面的测试,包括操作系统级别的测试和硬件兼容性测试。
    • 在系统模式下,QEMU 还可以使用不同的后端来提高性能,例如使用 KVM(Kernel-based Virtual Machine)在 Linux 系统上进行硬件辅助虚拟化。KVM 可以将 QEMU 转换为一个全功能的虚拟机监控器,提供接近原生硬件的性能。

要启动 QEMU 并使用用户模式或系统模式,你需要使用不同的命令行参数。例如:

用户模式:qemu-<arch> -cpu <cpu-type> -L <path-to-libraries> <program-to-run>
系统模式:qemu-system-<arch> -m <memory-size> -hda <disk-image>
其中 <arch> 是你想要模拟的架构,比如aarch64,<cpu-type> 是特定的 CPU 类型,<path-to-libraries> 是库文件的路径,<program-to-run> 是你想要运行的程序,<memory-size> 是分配给虚拟机的内存大小,<disk-image> 是虚拟硬盘镜像的路径。

QEMU 是一个非常灵活的工具,可以根据需要进行配置和扩展,以满足不同的模拟和虚拟化需求。

用户模式的安装

简单的执行如下命令就可以安装用户模式:

sudo apt-get update
sudo apt-get install qemu-user qemu-user-static gdb-multiarch build-essential  gcc-aarch64-linux-gnu

用户模式C程序的测试

编写一个简单的C程序:

#include <stdio.h>

int main(void) { 
    return printf("Hello AARCH64!\n"); 
}

使用aarch64的编译器进行静态的交叉编译:

aarch64-linux-gnu-gcc -static -o hello64 hello.c

编译后使用file命令确认一下编译产生的事aarch64架构文件。 

developer@ecs-cloud-host-ubuntu-img-make:~$ file hello64 
hello64: ELF 64-bit LSB executable, ARM aarch64, version 1 (GNU/Linux), statically linked, BuildID[sha1]=c6209c6d4f0f2ec56fc1e9fe2443c34009ad60d5, for GNU/Linux 3.7.0, not stripped

如果没有安装QEMU用户模式程序,直接运行该程序会报告错误:

bash: ./hello64: cannot execute binary file: Exec format error

 安装了QEMU用户模式之后,直接运行程序就可以得到正确的输出了。这是因为系统内有qemu-user-static,它支持了其他架构程序的直接运行。

developer@ecs-cloud-host-ubuntu-img-make:~$ ./hello64 
Hello AARCH64!

也可以不使用static编译,比如:

aarch64-linux-gnu-gcc -o hello64dyn hello.c

不过不能直接运行hello64dyn这个程序,会报告错误:

developer@ecs-cloud-host-ubuntu-img-make:~$ ./hello64dyn 
aarch64-binfmt-P: Could not open '/lib/ld-linux-aarch64.so.1': No such file or directory

这是需要使用qemu-user了:

developer@ecs-cloud-host-ubuntu-img-make:~$ qemu-aarch64 -L /usr/aarch64-linux-gnu ./hello64dyn 
Hello AARCH64!

用户模式汇编程序的测试(调用C库)

准备一个简单的汇编程序hello.S

.section .text
    .global main

main:
// Call puts and return 
    stp fp, lr, [sp, #0x10]!
    mov fp, sp
    adr x0, msg
    bl puts
    mov w0, wzr
    ldp fp, lr, [sp], #0x10
    ret

msg:
    .asciz "Hello Again!"
    .align 2

 这个程序里面调用了C语言的puts函数。

前两条指令是所谓的序言,SP 寄存器被调整,以便在可能的中断情况下保存数据,以便使用 puts 函数。

adr 指令将 x0 设置为字符串所在位置的内存地址。

'bl puts' 调用 puts 函数。它是一个 C 库函数,它将文本输出到控制台/终端。或者,可以使用 printf 代替。之所以使用 puts,是因为它对 CPU 来说更快,因为它不能接受所谓的字符串中的格式描述符。此外,puts 函数在控制台上自动加入换行。注意我们没有在 Hello Again 字符串中包含 "\n"。

'mov w0, wzr' 指令是给 CPU 一个零值,它将在退出程序时使用。稍后,CPU 将调用 C/C++ 退出函数来退出程序。每当你编写任何 .S(main:)类型的源文件时,最终的 ret 指令将使 CPU 结束程序并调用 C/C++ 退出函数。在 ret 指令之前,w0 应该总是设置为 0。

最后两条指令是所谓的尾声。基本上是序言的相反。

直接使用下面的语句去编译,C语言的编译器会自动处理汇编代码并进行库链接。

aarch64-linux-gnu-gcc  -o hello hello.S

运行程序的方法和前面类似:

developer@ecs-cloud-host-ubuntu-img-make:~$ qemu-aarch64 -L /usr/aarch64-linux-gnu ./hello
Hello Again!

用户模式汇编程序的测试(调用系统调用)

我们也可以不使用C语言函数,直接使用QEMU模拟的系统调用。这和系统模式所模拟的裸机编程有所不同,系统模式是不提供任何系统调用的,你必须自己写串口输出代码或者使用编译好的BIOS文件。

下面是汇编代码:

.section .text
    .global _start

_start:
// syscall write(int fd, const void *buf, size_t count)
    mov x0, #1    
    adr x1, msg
    mov x2, len
    mov w8, #64 /*Syscall number for write for ARM64*/
    svc #0

// syscall exit(int status)
    mov x0, #0
    mov w8, #93 /*Syscall number for exit for ARM64*/
    svc #0

msg:
    .asciz "Hello World!\n"
    len = . - msg

QEMU 模拟所谓的系统调用。这些系统调用旨在模拟具有内置基本任务功能的计算机 BIOS。可以在程序中使用 svc 指令调用。在真正的硬件中,svc 指令将进入一个异常处理程序。在这个程序中,有代码(已经由程序早期编写)来处理 svc 的使用。

在 QEMU 中,这是完全不同的。你的程序在异常处理程序中写了什么并不重要。QEMU 将执行 svc 来模拟一个已经编写了某些指令的计算机 BIOS。简而言之,那里的例程在计算机 BIOS 中是通用的。因此,该例程接受某些参数以产生结果。你可以看出这是情况,因为在上述文件中,我们没有编写任何指令到任何异常处理程序。模拟系统调用的不好之处在于我们不能实际看到和调试 svc 异常。好的是,我们可以使用这些模拟器系统调用来执行基本任务。比如打印文本到控制台(终端),修改文件等。

系统调用 Write 会在控制台(终端)上打印消息。系统调用 Exit 退出程序。

有许多系统调用,每个系统调用都需要参数。在系统调用被“调用”(通过 svc 指令)之前,必须将正确的值放在正确的寄存器中。这种将正确的参数放在正确的寄存器中的概念被称为 ARM64 的调用约定。

代码中一些变量的解释:

  • adr 设置 msg 标签所在位置的内存地址。
  • msg: 是源文件中 Hello World 字符串所在的标签。字符串中的 "\n" 部分是你在控制台/终端输入新行时需要输入的。
  • len = . - msg 这是用于计算字符串大小的汇编器指令。点(.)在这里代表这里。所以 '这里' 减去 msg 将设置字符串的字节量。

要运行程序,需要将其汇编成一个目标文件。然后使用链接器制作一个可执行文件。这可以通过以下两个终端命令完成..

aarch64-linux-gnu-as  hello_world.s -o hello_world.o 
aarch64-linux-gnu-ld hello_world.o -o hello_world

所以我们现在有一个可执行文件。我们可以用 QEMU 启动它:

qemu-aarch64 ./hello_world

得到如下输出:

developer@ecs-cloud-host-ubuntu-img-make:~$ qemu-aarch64 ./hello_world
Hello World!

因为安装了qemu-user-static,直接运行程序也是可以的。 

参考文献

  • 30
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
qemu-system-aarch64是一个运行ARM64架构的虚拟机的命令行工具。当我们在运行这个命令时,如果没有指定虚拟机类型,也没有默认设置,就会出现 "qemu-system-aarch64: no machine specified, and there is no default" 的报错信息。 这个报错的原因是因为在运行qemu-system-aarch64命令时,我们需要指定要运行的虚拟机的类型。虚拟机类型是根据具体的应用或需求来选择的,例如rpi3表示树莓派3型号的虚拟机,virt表示通用的虚拟机类型等等。如果没有指定虚拟机类型,qemu无法确定具体要运行哪个虚拟机,因此会出现这个错误。 解决这个问题的方法是在运行qemu-system-aarch64命令时,使用"-machine"参数来指定要运行的虚拟机类型。例如,如果我们想运行rpi3类型的虚拟机,命令可以修改为: "qemu-system-aarch64 -machine rpi3"。同时,也可以通过"-M"参数来指定虚拟机类型,例如: "qemu-system-aarch64 -M rpi3"。 此外,这个报错信息也提示我们当前没有默认设置的虚拟机类型。要设置默认虚拟机类型,可以使用"-machine"或"-M"参数,加上要设置的虚拟机类型,例如: "qemu-system-aarch64 -machine virt"。这样,当我们运行qemu-system-aarch64命令时,就默认使用指定的虚拟机类型。 综上所述,qemu-system-aarch64: no machine specified, and there is no default报错信息是因为运行qemu-system-aarch64命令时没有指定虚拟机类型,并且也没有默认设置。需要使用"-machine"或"-M"参数来指定当前要运行的虚拟机类型。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

神一样的老师

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值