2019年2月15日金曜日

aarch64 向けベアメタルプログラムを QEMU と gdb でデバッグする

aarch64 のベアメタルプログラムの挙動がわからん過ぎたのでデバッグ環境を構築し、使い方を勉強した。

前提

aarch64 のビルド環境とエミュレーター、gdb は導入済み。

この記事内で言及はないが、 mikoto2000/qemu-aarch64:3.1.0 を使用した。

デバッグ対象のビルド

デバッグオプション(-g3)をつけて、プログラムをビルドする。

# make
aarch64-linux-gnu-as -g3 -o boot.o boot.S
aarch64-linux-gnu-gcc -g3 -O0 -I ../util/include -c -o main.o main.c
aarch64-linux-gnu-gcc -g3 -O0 -I ../util/include -c -o uart.o ../util/src/uart.c
aarch64-linux-gnu-gcc -g3 -O0 -I ../util/include -c -o string.o ../util/src/string.c
aarch64-linux-gnu-gcc -g3 -O0 -I ../util/include -c -o system_register.o ../util/src/system_register.S
aarch64-linux-gnu-ld -o kernel8.elf boot.o main.o uart.o string.o system_register.o -T linker.ld
aarch64-linux-gnu-objcopy -O binary kernel8.elf kernel8.img

QEMU でデバッグ実行

デバッグ対象バイナリを指定して、 QEMU を起動する。

# qemu-system-aarch64 -kernel ./kernel8.img -nographic -machine raspi3 -s -S
  • -s : localhost:1234 で gdb を待ち受ける
  • -S : 開始と同時にプログラムを一時停止する

gdb で QEMU に接続する

別のターミナルを開いて、 gdb で QEMU に接続する。

# gdb-multiarch -q ./kernel8.elf
Reading symbols from ./kernel8.elf...done.
(gdb) target remote localhost:1234
Remote debugging using localhost:1234
0x0000000000000000 in ?? ()
(gdb) b start
Breakpoint 1 at 0x80000: file boot.S, line 7.
(gdb) b go_to_main
Breakpoint 2 at 0x8002c: file boot.S, line 22.
(gdb) c
Continuing.

Thread 1 hit Breakpoint 1, start () at boot.S:7
7           mov sp, #0x80000
...(略)

みたいな感じで作ったプログラムをデバッグできる。

以上。

2019年2月12日火曜日

vim-lsp と eclipse.jdt.ls で Java プログラミング環境を構築する

下記ツイートを見たので、試してみた。

前提

  • OS: Windows 10 Pro
  • Vim: 8.1 patch 573
  • vim のパッケージ管理は vim8 のパッケージ機能 & git submodule を使っている

eclipse.jdt.ls のダウンロードと配置

次のサイトから jdt-language-server-0.32.0-201901231649.tar.gz をダウンロードする。

今回は、 ~/eclipse.jdt.ls/ に配置するので、ディレクトリ ~/eclipse.jdt.ls/ を作って、 tar.gz を展開してできたものを格納する。 格納するファイル(ディレクトリ)は次の 5 つ。

  • config_linux
  • config_mac
  • config_win
  • features
  • plugins

必要プラグインの導入

git submodule で管理しているので、必要な vim プラグインを git リポジトリに登録する。 登録するプラグインは次の 2 つ。

cd /PATH/TO/dotvim
git submodule add https://github.com/prabirshrestha/async.vim ./.vim/pack/lsp/opt/async.vim
git submodule add https://github.com/prabirshrestha/vim-lsp ./.vim/pack/lsp/opt/vim-lsp

async.vimvim-lsp 用でしか使わない予定なので pack/lsp 内に入れてしまう。

vimrc へ vim-lsp の設定を追加

vim-lsp の READMEeclipse.jdt.ls の README を見ながら .vimrc へ追記。

""" for java development
command! Javad call StartJavaDevelopment()
function! StartJavaDevelopment()
    packadd async.vim
    packadd vim-lsp

    " ログ表示の設定
    let g:lsp_log_verbose = 0
    "let g:lsp_log_file = expand('~/vim-lsp.log')
    "let g:asyncomplete_log_file = expand('~/asyncomplete.log')

    autocmd User lsp_setup call lsp#register_server({
        \ 'name': 'eclipse.jdt.ls',
        \ 'cmd': {server_info->[
        \     'c:\java\jdk1.8.0_201\bin\java',
        \     '-Declipse.application=org.eclipse.jdt.ls.core.id1',
        \     '-Dosgi.bundles.defaultStartLevel=4',
        \     '-Declipse.product=org.eclipse.jdt.ls.core.product',
        \     '-Dlog.level=ALL',
        \     '-noverify',
        \     '-Dfile.encoding=UTF-8',
        \     '-Xmx1G',
        \     '-jar',
        \     fnamemodify("~", ":p") . '/eclipse.jdt.ls/plugins/org.eclipse.equinox.launcher_1.5.200.v20180922-1751.jar',
        \     '-configuration',
        \     fnamemodify("~", ":p") . '/eclipse.jdt.ls/config_win',
        \     '-data',
        \     fnamemodify("~", ":p") . '/eclipse.jdt.ls/workspace'
        \ ]},
        \ 'root_uri':{server_info->lsp#utils#path_to_uri(lsp#utils#find_nearest_parent_file_directory(lsp#utils#get_buffer_path(), 'build.gradle'))},
        \ 'whitelist': ['java'],
        \ })

    call lsp#enable()
    autocmd FileType java setlocal omnifunc=lsp#complete
endfunction

「いつでも有効」にはしたくないので、 Javad コマンドを実行したときに packadd して必要な定義を行うようにした。

lsp#register_server の値

  • name: サーバ名
    • サーバが判別できる任意の名前
  • cmd: LSP 起動コマンド
    • bat ファイル作ってそれを呼び出すようにしたほうがスマートっぽい
  • root_uri: プロジェクトルートの場所
    • ここでは vim-lsp のユーティリティを使って、 build.gradle 探すようにしている
  • whitelist: この LSP サーバを使う FileType

これで、 eclipse.jdt.ls の補完結果が、 vim のオムニ補完に表示されるようになる。

補完系プラグインを使っているならば、そのヘルプに補完結果指定方法が書かれているはず。

以上。

参考資料

2019年2月9日土曜日

Renesas V850 64bit Linux 版ツールチェインの Docker イメージを作る

Dockerfile の作り方

基本的に前回の記事を Dockerfile に落とし込むだけ。

記事からの変更点は以下の通り。

  • ダウンロードする場所を最初から / にした

cd は、 RUN ではなくて Dockerfile の WORKDIR コマンドで行う。

実行環境の構築

追加の作業として、ビルドした成果物のバイナリとライブラリのみを実行用環境にコピーする。

こうすることで、実行環境のコンテナイメージのサイズ削減ができる

使用ライブラリの特定

ldd を使って、各バイナリがどのライブラリを使用しているか確認する。

# ldd /usr/local/bin/* | grep ".so" | cut -d '(' -f -1 | sort -u
        /lib64/ld-linux-x86-64.so.2
        libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6
        libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2
        libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1
        libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6
        libstdc++.so.6 => /lib/x86_64-linux-gnu/libstdc++.so.6
        linux-vdso.so.1
# ldd /lib64/ld-linux-x86-64.so.2 /lib/x86_64-linux-gnu/libc.so.6 /lib/x86_64-linux-gnu/libdl.so.2 /lib/x86_64-linux-gnu/libgcc_s.so.1 /lib/x86_64-linux-gnu/libm.so.6 /lib/x86_64-linux-gnu/libstdc++.so.6 | grep ".so" | cut -d '(' -f -1 | sort -u
        /lib64/ld-linux-x86-64.so.2
        libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6
        libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1
        libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6
        linux-vdso.so.1

ということで、この辺があれば良さそう。

  • /lib64/ld-linux-x86-64.so.2
  • /lib/x86_64-linux-gnu/libc.so.6
  • /lib/x86_64-linux-gnu/libdl.so.2
  • /lib/x86_64-linux-gnu/libgcc_s.so.1
  • /lib/x86_64-linux-gnu/libm.so.6
  • /lib/x86_64-linux-gnu/libstdc++.so.6

buster-slim には全部入っている。

完成した Dockerfile

# ビルド環境を構築してビルド
FROM debian:buster-slim as build-env

LABEL maintainer "mikoto2000 <mikoto2000@gmail.com>"
LABEL version="v14.01"
LABEL description "Runesas V850 Toolchain v14.01(fromhttps://gcc-renesas.com/ja/v850/v850-download-toolchains/)"

RUN apt-get update \
    && apt-get -y install \
        build-essential \
        libgmp-dev \
        libmpfr-dev \
        libmpc-dev \
        texinfo \
        wget \
    && apt-get clean \
    && rm -rf /var/lib/apt/lists/*

# ツールチェインのソース取得
WORKDIR /
RUN wget https://gcc-renesas.com/downloads/d.php?f=v850/binutils/14.01/binutils-2.24_v850_v14.01.tar.bz2 -O binutils-2.24_v850_v14.01.tar.bz2
RUN tar xf binutils-2.24_v850_v14.01.tar.bz2
RUN wget https://gcc-renesas.com/downloads/d.php?f=v850/gcc/14.01/gcc-4.9.2_v850_v14.01.tar.bz2 -O gcc-4.9.2_v850_v14.01.tar.bz2
RUN tar xf gcc-4.9.2_v850_v14.01.tar.bz2
RUN wget https://gcc-renesas.com/downloads/d.php?f=v850/newlib/14.01/newlib-2.1.0_v850_v14.01.tar.bz2 -O newlib-2.1.0_v850_v14.01.tar.bz2
RUN tar xf newlib-2.1.0_v850_v14.01.tar.bz2

# binutils のビルド
RUN mkdir -p /build/binutils
WORKDIR /build/binutils
RUN ../../binutils-2.24/configure --target=v850-elf --prefix=/usr/local --enable-soft-float
RUN make CFLAGS="-Wno-cast-function-type -Wno-implicit-fallthrough -Wno-shift-negative-value -Wno-unused-value -Wno-pointer-compare"
RUN make install


# gcc のビルド
RUN mkdir -p /build/gcc
WORKDIR /build/gcc
RUN ../../gcc-4.9.2/configure --target=v850-elf --prefix=/usr/local --enable-languages=c,c++ --disable-nls --disable-multilib --disable-libssp --with-newlib --with-headers=/newlib-2.1.0/newlib/libc/include
RUN make CXXFLAGS="-std=c++03"
RUN make install


# newlib のビルド
RUN mkdir -p /build/newlib
WORKDIR /build/newlib
RUN ../../newlib-2.1.0/configure --target=v850-elf --prefix=/usr/local
RUN make
RUN make install


# 実行環境を構築してビルド環境からバイナリをコピー
FROM debian:buster-slim

LABEL maintainer "mikoto2000 <mikoto2000@gmail.com>"
LABEL version="v14.01"
LABEL description "Runesas V850 Toolchain v14.01(fromhttps://gcc-renesas.com/ja/v850/v850-download-toolchains/)"

# build-env から必要なファイルをコピー
COPY --from=build-env \
    /usr/local \
    /usr/local

# make くらい必要でしょ?
RUN apt-get update \
    && apt-get -y install \
        make \
    && apt-get clean \
    && rm -rf /var/lib/apt/lists/*

イメージのビルド

cd /PATH/TO/Dockerfile
docker build -t mikoto2000/v850-toolchain:latest .

動作確認

# cd /athrill/sample/barmetal/step2/
# make clean all
rm -f start.o vector.o training.o main.o test_suite.o test_instruction.o test_main.elf *.map
v850-elf-gcc -c -I. -I../common -I../../../trunk/src/config/target/v850esfk3 -O0 -mdisable -callt -mno-app-regs -mtda=0 -gdwarf-2 -Wall -Wno-unused-label -Wpointer-arith  -mv850e2 -Wa,-mno-bcond17 -Wa,-mwarn-signed-overflow -Wa,-mwarn-unsigned-overflow start.S
/usr/local/libexec/gcc/v850-elf/4.9-GNUV850_v14.01/cc1: error while loading shared libraries: libmpc.so.3: cannot open shared object file: No such file or directory
make: *** [../build/v850esfk3/Makefile.common:35: start.o] Error 1

おや?ここにも実行ファイルがあったんですね。依存ライブラリ確認。

# cd /usr/local/libexec/gcc/v850-elf/4.9-GNUV850_v14.01/
# ldd cc1 cc1plus coll
ect2 liblto_plugin.so.0.0.0
cc1:
        linux-vdso.so.1 (0x00007ffdcfdd8000)
        libmpc.so.3 => not found
        libmpfr.so.6 => not found
        libgmp.so.10 => /lib/x86_64-linux-gnu/libgmp.so.10 (0x00007ff060af8000)
        libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007ff060af3000)
        libstdc++.so.6 => /lib/x86_64-linux-gnu/libstdc++.so.6 (0x00007ff060970000)
        libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007ff0607ed000)
        libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1 (0x00007ff0607d1000)
        libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007ff060610000)
        /lib64/ld-linux-x86-64.so.2 (0x00007ff061877000)
cc1plus:
        linux-vdso.so.1 (0x00007ffeb59b3000)
        libmpc.so.3 => not found
        libmpfr.so.6 => not found
        libgmp.so.10 => /lib/x86_64-linux-gnu/libgmp.so.10 (0x00007f5c57a0d000)
        libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007f5c57a08000)
        libstdc++.so.6 => /lib/x86_64-linux-gnu/libstdc++.so.6 (0x00007f5c57885000)
        libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007f5c57702000)
        libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1 (0x00007f5c576e6000)
        libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f5c57525000)
        /lib64/ld-linux-x86-64.so.2 (0x00007f5c588f6000)
collect2:
        linux-vdso.so.1 (0x00007fff6cf2a000)
        libstdc++.so.6 => /lib/x86_64-linux-gnu/libstdc++.so.6 (0x00007f6c99fde000)
        libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007f6c99e5b000)
        libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1 (0x00007f6c99e41000)
        libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f6c99c80000)
        /lib64/ld-linux-x86-64.so.2 (0x00007f6c9a1de000)
liblto_plugin.so.0.0.0:
        linux-vdso.so.1 (0x00007ffc407a1000)
        libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f92edef9000)
        /lib64/ld-linux-x86-64.so.2 (0x00007f92ee0d1000)

これが無い。

  • libmpc.so.3 => not found
  • libmpfr.so.6 => not found

さっき作ったビルド用コンテナイメージでライブラリの場所を確認して Dockerfile に追加。

COPY --from=build-env \
    /usr/lib/x86_64-linux-gnu/libmpc.so.3 \
    /usr/lib/x86_64-linux-gnu/libmpc.so.3
COPY --from=build-env \
    /usr/lib/x86_64-linux-gnu/libmpfr.so.6 \
    /usr/lib/x86_64-linux-gnu/libmpfr.so.6

...gmp はどこいった? 対処療法っぽいけどまぁその辺は後々問題が出たら対応で。

docker イメージを再ビルドする。

動作確認再挑戦

# cd /athrill/sample/barmetal/step2/
# make clean all

OK.

Docker Hub に公開した

以上。

更新履歴

日付 更新内容
2019/2/9 新規作成
2019/3/11 gcc のビルド時、『s/--with-header/--with-headers/ で target-libstdc++-v3 のビルドエラーは解消できる』という指摘を反映