QEMU AArch64(ARM64) で遊ぼう

目次

1 はじめに

QEMUはオープンソースのCPUエミュレータです。

ARMは 組み込み機器、スマートフォン、携帯ゲーム機 などに広く用いられている CPUです。

以前、QEMUにLinuxのユーザーランドをエミュレートするユーザーエミュレーション と呼ばれる環境があるのを知り 32bit ARMで遊んでみました (QEMU ARM で遊ぼう)。 その後、しばらくして ARM64(AArch64)もいけるらしいという事を知り遊んでみたという次第です。

ただの暇つぶしなので、役に立たないかも知れませんが、ログをだらだらと記してみたいと 思います。

因みに、ARM64は64bit版のARMアーキテクチャを指す俗称のようです。 公式(?)にはAArch64と呼ぶようです。本ページではインストールディレクトリには 俗称のarm64を使用しています。他は各ツールのビルドパラメータに従い aarch64とarm64を使い分けています。

2 AAarch64クロスコンパイル環境を作る

本へっぽこページのコンテンツでは日常的な事なのですが、クロスコンパイラはソースからビルドします。 今回は VMware上の Fedora(Linuxのディストリビューションの一つ)でビルドをしました。 ビルドに必要なライブラリなどは予めインストールされているものと仮定しています。 クロスgccのビルドには、GMP,MPFR,MPC を始め、texinfoやncurses などの開発ライブラリ (これらのライブラリはいずれもx86ネイティブなものです)なども必要になるかと思いますが、 それらはパッケージインストールしてもらえれば良いと思います。

2.1 AAarch64クロスコンパイラのビルドに必要なファイル

以下のアーカイブを使用しました。

  • binutils-2.25.tar.bz2 : アセンブラ/リンカ
  • gcc-4.9.2.tar.bz2 : コンパイラ
  • linux-3.18.1.tar.xz : Linuxカーネルアーカイブ(実際に使用するのはその一部)
  • glibc-2.20.tar.xz : Cライブラリ
  • gdb-7.8.1.tar.xz : デバッガ

他のバージョンでも大丈夫かも知れませんが、古すぎたり新しすぎたりすると 指定したコンパイルオプションではうまくビルドできなかったりするかも知れません。 その場合はうまく解決してください(^^;

2.2 ビルド手順

例では /usr/local/arm64_linux にインストールするものとしてビルドします。 アーカイブ群は ~/Downloads の下に、ビルドはカレントディレクトリで行う ものとしています。

また、作法的には良くないのですが、/usr/local および /usr/local/arm64_linuxは ファイルパーミッションを一般ユーザに空けたものとして実行しています。 「そういうの嫌なんですけど?」という方はインストール先ディレクトリを 任意のディレクトリに読み替えるか、「sudo make install」していると読み替えて ください。

今回はLinux(x86)の QEMUを使う都合でコンパイラのビルドもLinux(x86)上で 行いましたが、Cygwinなんかでもビルドできるのではないかと思います。

2.2.1 事前の設定

予めビルドされるであろう実行ファイル群が置かれるパスにPATHを通しておきます。

  1. export PATH="/usr/local/arm64_linux/bin:$PATH"

2.2.2 binutilsのビルド

  1. bzip2 -dc ~/Downloads/binutils-2.25.tar.bz2 | tar xf -
  2. mkdir -p binutils-2.25/build_linux
  3. pushd binutils-2.25/build_linux
  4. ../configure --target=aarch64-linux-gnu --prefix=/usr/local/arm64_linux
  5. make
  6. make install
  7. popd

2.2.3 gccのビルド(1回目)

  1. bzip2 -dc ~/Downloads/gcc-4.9.2.tar.bz2 | tar xf -
  2. mkdir -p gcc-4.9.2/build1_linux
  3. pushd gcc-4.9.2/build1_linux
  4. ../configure --target=aarch64-linux-gnu --prefix=/usr/local/arm64_linux --with-newlib --without-headers --disable-nls --disable-shared --disable-multilib --disable-decimal-float --disable-threads --disable-libatomic --disable-libgomp --disable-libitm --disable-libquadmath --disable-libsanitizer --disable-libssp --disable-libvtv --disable-libcilkrts --disable-libstdc++-v3 --enable-languages=c,c++
  5. make
  6. make install
  7. popd

まだAArch64用のCライブラリが存在していませんので、configureにはgcc内に含まれるライブラリ群を ビルドしないように指定しています。

ソースはもう一度利用する為、そのままにしておきます。

2.2.4 Linuxヘッダのインストール

  1. xz -dc ~/Downloads/linux-3.18.1.tar.xz | tar xf -
  2. pushd linux-3.18.1
  3. make mrproper
  4. make headers_install ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- INSTALL_HDR_PATH=/usr/local/arm64_linux/aarch64-linux-gnu
  5. popd

システムコールを行う方法はOSに依存する為、Linuxのヘッダだけをインストールします。

2.2.5 glibcのビルド

  1. xz -dc ~/Downloads/glibc-2.20.tar.xz | tar xf -
  2. mkdir -p glibc-2.20/build_linux
  3. pushd glibc-2.20/build_linux
  4. ../configure --prefix=/usr/local/arm64_linux/aarch64-linux-gnu --host=aarch64-linux-gnu --build=$(../scripts/config.guess) --disable-profile --enable-kernel=2.6.32 --with-headers=/usr/local/arm64_linux/aarch64-linux-gnu/include libc_cv_forced_unwind=yes libc_cv_ctors_header=yes libc_cv_c_cleanup=yes
  5. make
  6. make install
  7. popd

2.2.6 libstdc++のビルド

  1. mkdir -p gcc-4.9.2/build_linux_libstdc++
  2. pushd gcc-4.9.2/build_linux_libstdc++
  3. ../libstdc++-v3/configure --target=aarch64-linux-gnu --prefix=/usr/local/arm64_linux/aarch64-linux-gnu --disable-multilib --disable-shared --disable-nls --disable-libstdcxx-threads --disable-libstdcxx-pch --with-gxx-include-dir=/usr/local/arm64_linux/aarch64-linux-gnu/include/c++/4.9.2
  4. make
  5. make install
  6. popd

1回目のgccビルドで使用したソースを利用しています。

2.2.7 gccのビルド(2回目)

  1. mkdir -p gcc-4.9.2/build2_linux
  2. pushd gcc-4.9.2/build2_linux
  3. ../configure --target=aarch64-linux-gnu --prefix=/usr/local/arm64_linux --disable-nls --disable-shared --disable-multilib --disable-libgomp --disable-threads --enable-languages=c,c++ --disable-libstdcxx-pch --disable-bootstrap
  4. make
  5. make install
  6. popd

ビルドしたglibcを参照するgccをビルドします。1回目のgccビルドで使用したソースを利用しています。

2.2.8 gdbのビルド

  1. xz -dc ~/Downloads/gdb-7.8.1.tar.xz | tar xf -
  2. mkdir -p gdb-7.8.1/build_linux
  3. pushd gdb-7.8.1/build_linux
  4. ../configure --target=aarch64-linux-gnu --prefix=/usr/local/arm64_linux
  5. make
  6. make install
  7. popd

2.3 コンパイルテスト

ビルドしたクロスコンパイラを使って以下のソースをコンパイルしてみます。

#include <stdlib.h>
#include <stdio.h>

int main()
{
    printf("Hello AArch64 world.\n") ;
    return(0) ;
}

hello.cなどにセーブし、以下のような感じでコンパイルします。

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

$ aarch64-linux-gnu-readelf -h hello
ELF Header:
  Magic:   7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00
  Class:                             ELF64
  Data:                              2's complement, little endian
  Version:                           1 (current)
  OS/ABI:                            UNIX - System V
  ABI Version:                       0
  Type:                              EXEC (Executable file)
  Machine:                           AArch64
  Version:                           0x1
  Entry point address:               0x400410
  Start of program headers:          64 (bytes into file)
  Start of section headers:          9304 (bytes into file)
  Flags:                             0x0
  Size of this header:               64 (bytes)
  Size of program headers:           56 (bytes)
  Number of program headers:         7
  Size of section headers:           64 (bytes)
  Number of section headers:         36
  Section header string table index: 33

この例では 共有ライブラリを使用する実行ファイルが生成されます。

3 QEMUでAArch64バイナリを動かしてみる

QEMUを使って、AArch64のバイナリを動かしてみます。

3.1 QEMUのビルド

AArch64のバイナリが無かったのでソースからビルドします。ビルドにはautoconf,automake, libtoolパッケージをインストールしておく必要があります。 アーカイブはQEMU公式ページ より、バージョン2.4.0.1 をダウンロードし、~/Downloads/qemu-2.4.0.1.tar.bz2においてあるものとします。

  1. bzip2 -dc ~/Downloads/qemu-2.4.0.1.tar.bz2 | tar xf -
  2. pushd qemu-2.4.0.1
  3. ./configure --target-list=aarch64-linux-user
  4. make
  5. popd

実行バイナリは qemu-2.4.0.1/aarch64-linux-user/qemu-aarch64 に出来上がります。

$ ./qemu-2.4.0.1/aarch64-linux-user/qemu-aarch64
usage: qemu-aarch64 [options] program [arguments...]
Linux CPU emulator (compiled for aarch64 emulation)

Options and associated environment variables:

Argument      Env-variable      Description
-h                              print this help
-g port       QEMU_GDB          wait gdb connection to 'port'
-L path       QEMU_LD_PREFIX    set the elf interpreter prefix to 'path'
-s size       QEMU_STACK_SIZE   set the stack size to 'size' bytes
-cpu model    QEMU_CPU          select CPU (-cpu help for list)
-E var=value  QEMU_SET_ENV      sets targets environment variable (see below)
-U var        QEMU_UNSET_ENV    unsets targets environment variable (see below)
-0 argv0      QEMU_ARGV0        forces target process argv[0] to be 'argv0'
-r uname      QEMU_UNAME        set qemu uname release string to 'uname'
-B address    QEMU_GUEST_BASE   set guest_base address to 'address'
-R size       QEMU_RESERVED_VA  reserve 'size' bytes for guest virtual address space
-d item[,...] QEMU_LOG          enable logging of specified items (use '-d help' for a list of items)
-D logfile    QEMU_LOG_FILENAME write logs to 'logfile' (default stderr)
-p pagesize   QEMU_PAGESIZE     set the host page size to 'pagesize'
-singlestep   QEMU_SINGLESTEP   run in singlestep mode
-strace       QEMU_STRACE       log system calls
-seed         QEMU_RAND_SEED    Seed for pseudo-random number generator
-version      QEMU_VERSION      display version information and exit

Defaults:
QEMU_LD_PREFIX  = /usr/gnemul/qemu-aarch64
QEMU_STACK_SIZE = 8388608 byte

You can use -E and -U options or the QEMU_SET_ENV and
QEMU_UNSET_ENV environment variables to set and unset
environment variables for the target process.
It is possible to provide several variables by separating them
by commas in getsubopt(3) style. Additionally it is possible to
provide the -E and -U options multiple times.
The following lines are equivalent:
    -E var1=val2 -E var2=val2 -U LD_PRELOAD -U LD_DEBUG
    -E var1=val2,var2=val2 -U LD_PRELOAD,LD_DEBUG
    QEMU_SET_ENV=var1=val2,var2=val2 QEMU_UNSET_ENV=LD_PRELOAD,LD_DEBUG
Note that if you provide several changes to a single variable
the last change will stay in effect.

3.2 動かしてみる

「2.3 コンパイルテスト」のバイナリを動かしてみます。

$ ./qemu-2.4.0.1/aarch64-linux-user/qemu-aarch64 -L /usr/local/arm64_linux/aarch64-linux-gnu ./hello
Hello AArch64 world.

-Lオプションで共有ライブラリパスを指定しています。

3.3 デバッガを使ってみる

qemu-armでは、-gオプションを使ってgdbから接続する事ができます。 argを表示するプログラムを例にして接続手順を示します。

注意: スタティックリンクしないと、ステップ実行の度にアクセスエラーするようです。この為、コンパイル時に-staticオプションを指定します。

以下のようにコンパイルした実行ファイルをqemuで起動します。 このとき、プロンプトは戻らず、ずっと待った状態になります。

$ cat gdbtest.c
#include <stdlib.h>
#include <stdio.h>

int main(int argc, char* argv[])
{
    int i ;
    printf("argc=%d\n",argc) ;
    for( i=0 ; i<argc ; i++ ){
        printf("%s\n",argv[i]) ;
    }
    return(0) ;
}

$ aarch64-linux-gnu-gcc -static -g gdbtest.c -o gdbtest

$ ./qemu-2.4.0.1/aarch64-linux-user/qemu-aarch64 -g 1111 -L /usr/local/arm64_linux/aarch64-linux-gnu ./gdbtest aaa bbb ccc ddd eee

別のターミナルなどでgdbを起動します。

$ aarch64-linux-gnu-gdb -q
(gdb) file gdbtest
Reading symbols from gdbtest...done.
(gdb) target remote localhost:1111
Remote debugging using localhost:1111
warning: Can not parse XML target description; XML support was disabled at compile time
_start () at ../sysdeps/aarch64/start.S:48
48              mov     x29, #0
(gdb) b main
Breakpoint 1 at 0x400ab0: file gdbtest.c, line 7.
(gdb) c
Continuing.

Breakpoint 1, main (argc=6, argv=0x40007fffb8) at gdbtest.c:7
7           printf("argc=%d\n",argc) ;
(gdb) l
2       #include <stdio.h>
3
4       int main(int argc, char* argv[])
5       {
6           int i ;
7           printf("argc=%d\n",argc) ;
8           for( i=0 ; i<argc ; i++ ){
9               printf("%s\n",argv[i]) ;
10          }
11          return(0) ;
(gdb)

ブレークポイントを指定したり変数の内容を表示できます。 Cプログラムの真の開始位置である_start()で止まっている状態なので、 「c」(continue)コマンドで 実行再開します。

4 終わりに

唐突に思いついて遊んだ結果なので役に立つ事はあまり無いと思いますが、 もし役に立つ事があれば幸いです。

5 履歴

  2015/10/06 : 初版。
  2016/02/05 : 誤記修正。

TOP PREV

著者: TANE

Emacs 24.5.1 (Org mode 8.2.10)