pmeerw's blog

26 Nov 2020

Thu, 26 Nov 2020

QEMU user-mode emulation

qemu can emulate all kind of architectures and processors, including x86 and x86_64, it has presets for a long list of CPUs ([1], 486, pentium, Haswell, etc.)

I've tried this using qemu 4.2.1 on Ubuntu 20.04, latest is 5.1.0.

qemu does full-system emulation AND user-mode emulation. While the former allows to run a wide range of operating systems on any supported architecture [2], the later runs programs for another Linux or BSD target.

       Full-system                     User-mode
+---------------------+         +---------------------+
| Userspace emulation |         | Userspace emulation |
+----------+----------+         +----------+----------+
           |                               |
 +---------+--------+              +-------+-------+
 | Kernel emulation |              | Kernel native |
 +---------+--------+              +-------+-------+
           |                               |
+----------+---------+            +--------+--------+
| Hardware emulation |            | Hardware native |
+--------------------+            +-----------------+

Let's compile the following simple program (hello.c):

#include <stdio.h>
int main() {
  printf("hello world %p\n", main);
  return 0;
And link statically to be self-contained; qemu can handle dynamically linked executables just fine as well.

To compile and link for 32-bit ARM [3]: arm-linux-gnueabihf-gcc -static -o hello-arm hello.c
For 64-bit x86: gcc -static -o hello-x86_x64 hello.c

Let's check:
$ file hello-arm
hello-arm: ELF 32-bit LSB executable, ARM, EABI5 version 1 (GNU/Linux), statically linked, for GNU/Linux 3.2.0, not stripped
$ file hello-x86_x64
hello-x86_x64: ELF 64-bit LSB executable, x86-64, version 1 (GNU/Linux), statically linked, for GNU/Linux 3.2.0, not stripped

On Ubuntu, we need qemu-user [4], and can then execute both binaries:
$ qemu-arm -- ./hello-arm
hello world 0x10425
$ qemu-x86_64 -- ./hello-x86_64
hello world 0x401ce5

qemu translates the input binary to run on the native CPU, also in case the architectures match. It uses internal micro ops (some intermediate representation), these can be observed before and after optimization:
qemu-x86_64 -d op -- ./hello-x86_64
qemu-x86_64 -d op_opt -- ./hello-x86_64

For example:

 mov_i64 tmp0,r13
 mov_i64 tmp1,r13
 and_i64 cc_dst,tmp0,tmp1
 discard cc_src
 discard loc10

Also the input and output assembler code can be seen:
qemu-x86_64 -d in_asm -- ./hello-x86_x64
qemu-x86_64 -d out_asm -- ./hello-x86_x64

[1] qemu -cpu help
[2] arm, m64k, mips, mips64, ppc, sparc, sparc64, etc.
[3] apt install gcc-arm-linux-gnueabihf
[4] apt install qemu-user
[5] To show log items: qemu-x86_64 -d help

posted at: 23:45 | path: /programming | permanent link

Made with PyBlosxom