QEMU用户模式测试AARCH64程序

2024-09-07 18:44

本文主要是介绍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 mainmain:
// Call puts and return stp fp, lr, [sp, #0x10]!mov fp, spadr x0, msgbl putsmov w0, wzrldp fp, lr, [sp], #0x10retmsg:.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, msgmov x2, lenmov w8, #64 /*Syscall number for write for ARM64*/svc #0// syscall exit(int status)mov x0, #0mov w8, #93 /*Syscall number for exit for ARM64*/svc #0msg:.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,直接运行程序也是可以的。 

参考文献

  • Assembler, QEMU, GDB
  • Running Arm Binaries on x86 with QEMU-User | Azeria Labs

这篇关于QEMU用户模式测试AARCH64程序的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1145849

相关文章

SpringBoot实现微信小程序支付功能

《SpringBoot实现微信小程序支付功能》小程序支付功能已成为众多应用的核心需求之一,本文主要介绍了SpringBoot实现微信小程序支付功能,文中通过示例代码介绍的非常详细,对大家的学习或者工作... 目录一、引言二、准备工作(一)微信支付商户平台配置(二)Spring Boot项目搭建(三)配置文件

Linux系统配置NAT网络模式的详细步骤(附图文)

《Linux系统配置NAT网络模式的详细步骤(附图文)》本文详细指导如何在VMware环境下配置NAT网络模式,包括设置主机和虚拟机的IP地址、网关,以及针对Linux和Windows系统的具体步骤,... 目录一、配置NAT网络模式二、设置虚拟机交换机网关2.1 打开虚拟机2.2 管理员授权2.3 设置子

SpringBoot如何通过Map实现策略模式

《SpringBoot如何通过Map实现策略模式》策略模式是一种行为设计模式,它允许在运行时选择算法的行为,在Spring框架中,我们可以利用@Resource注解和Map集合来优雅地实现策略模式,这... 目录前言底层机制解析Spring的集合类型自动装配@Resource注解的行为实现原理使用直接使用M

mysql删除无用用户的方法实现

《mysql删除无用用户的方法实现》本文主要介绍了mysql删除无用用户的方法实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧... 1、删除不用的账户(1) 查看当前已存在账户mysql> select user,host,pa

C#原型模式之如何通过克隆对象来优化创建过程

《C#原型模式之如何通过克隆对象来优化创建过程》原型模式是一种创建型设计模式,通过克隆现有对象来创建新对象,避免重复的创建成本和复杂的初始化过程,它适用于对象创建过程复杂、需要大量相似对象或避免重复初... 目录什么是原型模式?原型模式的工作原理C#中如何实现原型模式?1. 定义原型接口2. 实现原型接口3

如何用java对接微信小程序下单后的发货接口

《如何用java对接微信小程序下单后的发货接口》:本文主要介绍在微信小程序后台实现发货通知的步骤,包括获取Access_token、使用RestTemplate调用发货接口、处理AccessTok... 目录配置参数 调用代码获取Access_token调用发货的接口类注意点总结配置参数 首先需要获取Ac

大数据spark3.5安装部署之local模式详解

《大数据spark3.5安装部署之local模式详解》本文介绍了如何在本地模式下安装和配置Spark,并展示了如何使用SparkShell进行基本的数据处理操作,同时,还介绍了如何通过Spark-su... 目录下载上传解压配置jdk解压配置环境变量启动查看交互操作命令行提交应用spark,一个数据处理框架

基于Python开发PDF转Doc格式小程序

《基于Python开发PDF转Doc格式小程序》这篇文章主要为大家详细介绍了如何基于Python开发PDF转Doc格式小程序,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 用python实现PDF转Doc格式小程序以下是一个使用Python实现PDF转DOC格式的GUI程序,采用T

将java程序打包成可执行文件的实现方式

《将java程序打包成可执行文件的实现方式》本文介绍了将Java程序打包成可执行文件的三种方法:手动打包(将编译后的代码及JRE运行环境一起打包),使用第三方打包工具(如Launch4j)和JDK自带... 目录1.问题提出2.如何将Java程序打包成可执行文件2.1将编译后的代码及jre运行环境一起打包2

SpringBoot中整合RabbitMQ(测试+部署上线最新完整)的过程

《SpringBoot中整合RabbitMQ(测试+部署上线最新完整)的过程》本文详细介绍了如何在虚拟机和宝塔面板中安装RabbitMQ,并使用Java代码实现消息的发送和接收,通过异步通讯,可以优化... 目录一、RabbitMQ安装二、启动RabbitMQ三、javascript编写Java代码1、引入