systemd環境でのコアダンプ解析

systemd環境下においては、従来(systemd無しの環境)のコアダンプ解析とは勝手が違ってくる。

コアダンプを有効にする

ulimitコマンドでcore file sizeが0になっていないことを確認する。

$ ulimit -a
-t: cpu time (seconds)              unlimited
-f: file size (blocks)              unlimited
-d: data seg size (kbytes)          unlimited
-s: stack size (kbytes)             8192
-c: core file size (blocks)         unlimited
-m: resident set size (kbytes)      unlimited
-u: processes                       63401
-n: file descriptors                1024
-l: locked-in-memory size (kbytes)  unlimited
-v: address space (kbytes)          unlimited
-x: file locks                      unlimited
-i: pending signals                 63401
-q: bytes in POSIX msg queues       819200
-e: max nice                        0
-r: max rt priority                 99
-N 15:                              unlimited

もしも0になっていたら、次のようにしてコアダンプを有効にする。

$ ulimit -c unlimited

ここまでは従来の方法と同じ。

コアダンプの出力場所

コアダンプの出力場所は /proc/sys/kernel/core_pattern を参照するとわかる。 systemdでは、

$ cat /proc/sys/kernel/core_pattern
|/usr/lib/systemd/systemd-coredump %P %u %g %s %t %c %e

となっており、 /var/lib/systemd/coredump/ 以下にLZ4で圧縮されて保存される。 保存されるファイル名は core.user-app.1000.adc138b171b140d2bb0a20e628857f04.22015.1515249501000000.lz4 といったものになる。

コアダンプの確認方法

従来の方法だと、次のコマンドでコアダンプの解析が行える。

$ gdb ./user-app core

systemd環境下で前述の方法と同様に行うと、コアダンプの認識に失敗する。

$ gdb ./user-app /var/lib/systemd/coredump/core.user-app.1000.adc138b171b140d2bb0a20e628857f04.22015.1515249501000000.lz4
GNU gdb (GDB) 8.0.1
Copyright (C) 2017 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-pc-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from ./user-app...done.
"/var/lib/systemd/coredump/core.user-app.1000.adc138b171b140d2bb0a20e628857f04.22015.1515249501000000.lz4" is not a core dump: File format not recognized

これは /var/lib/systemd/coredump/ 以下のコアダンプファイルがLZ4で圧縮されて保存されているためで、LZ4を展開してやればgdbから認識できるようになる。

$ lz4 -d /var/lib/systemd/coredump/core.user-app.1000.adc138b171b140d2bb0a20e628857f04.22015.1515249501000000.lz4 core
$ gdb ./user-app core

しかし、毎回コアダンプを展開するのは手間だ。 そこでsystemdでは coredumpctl というコマンドが用意されている。

coredumpctl の使い方

保存されているコアダンプ一覧を確認する。

$ coredumpctl list
TIME                            PID   UID   GID SIG COREFILE  EXE
Fri 2017-11-24 11:23:30 JST    1242     0     0  11 missing   /usr/lib/udisks2/udisksd
Sun 2017-11-26 00:12:29 JST    1959  1000  1000   5 missing   /usr/bin/ibus-daemon
Sun 2017-11-26 00:16:42 JST   10271  1000  1000   5 missing   /usr/bin/ibus-daemon
Fri 2017-12-15 18:42:18 JST    5242  1000  1000  11 missing   /usr/share/franz/franz
Tue 2017-12-19 22:49:53 JST    2933  1000  1000  11 missing   /opt/dropbox/dropbox
Thu 2017-12-21 02:14:13 JST    1723  1000  1000  11 missing   /usr/share/franz/franz
Fri 2017-12-22 02:04:45 JST    9251  1000  1000  11 missing   /usr/lib/electron/electron
Sun 2018-01-07 01:25:33 JST    6231  1000  1000   8 present   /home/ryo/code/workspace/csfml/user-app

gdbを使ってコアダンプを解析する。例えば、前述のリストから /home/ryo/code/workspace/csfml/user-app の解析をする場合には、PID 6231を指定する。

$ coredumpctl gdb 6231

参考

OpenOCD + ST-LinkでFirmware書き込み

OpenOCDを使ってSTM32F401 Nucleo-64にST-Link経由でFirmwareを書き込む。

OpenOCDとST-Linkドライバのインストール

Arch Linuxなら以下のコマンドでインストールする。

$ pacman -S openocd stlink

Firmwareを書き込む

OpenOCDで指定できるinterface, target, boardの設定ファイルは/usr/share/openocd/scriptsに配置されている。

OpenOCDで接続する

STM32F401 Nucleo-64 boardのUSB接続の場合

f:id:ryochack:20171103201550j:plain:w480

$ openocd -f board/st_nucleo_f4.cfg

もしくは、

$ openocd -f interface/stlink-v2-1.cfg -f target/stm32f4x.cfg

STM32F401 Chip + 中華ST-Linkの場合

f:id:ryochack:20171103201242j:plain:w480

Nucleoボードを使っているのなら中華ST-Linkを使う必要はないが、試した結果を書き残しておく。

$ openocd -f interface/stlink-v2.cfg -f target/stm32f4x.cfg

なお、Nucleoボード上のSWDコネクタ(CN4)は以下のピンアサインとなっている。

Pin CN4
1 VDD_TARGET
2 SWCLK
3 GND
4 SWDIO
5 NRST
6 SWO

※中華ST-Linkでは、一度ST-LinkのFirmwareをアップデートした後に書き込みに成功するようになった。 (書き込みに成功している中華ST-LinkのFW versionはV2J28S7)

telnetで書き込む

上記手順でOpenOCDで接続後、別のterminalからtelnetを起動する。

$ telnet localhost 4444

telnet上で以下のコマンドを実行することでFirmwareを書き込む。

> reset halt
> flash write_image erase ${elf_file_absolute_path}

telnet無しでFirmwareを書き込む

上記の方法だと、OpenOCDとは別にtelnetを起動する必要があり、若干煩わしい。 そこで、Firmwareを書き込む手順を記載したOpenOCDのconfigファイルを用意し、OpenOCDだけで書き込みを行えるようにする。

任意の名前で以下の内容のconfigファイルを作成する。 (今回はopenocd.cfgという名前にした)

telnet_port 4444
gdb_port 3333

source [find board/st_nucleo_f4.cfg]

init

proc flash_elf {elf_file} {
    reset
    halt
    flash write_image erase $elf_file
    verify_image $elf_file
    echo "flash write_image ($elf_file) complete"
    reset
    exit
}

configファイルを作成したら、以下のコマンドで書き込みを実行する。

$ openocd -f openocd.cfg -c "flash_elf ${elf_file_relative_path}"

※OpenOCDのprocの名前はOpenOCDの既存コマンド名にあるものを使うとエラーになるようだ。
(最初、proc flash {elf_file}としていたところ、以下のエラーが出て小一時間ほどハマってしまった)

Open On-Chip Debugger 0.10.0
Licensed under GNU GPL v2
For bug reports, read
        http://openocd.org/doc/doxygen/bugs.html
Info : The selected transport took over low-level target control. The results might differ compared to plain JTAG/SWD
adapter speed: 2000 kHz
adapter_nsrst_delay: 100
none separate
srst_only separate srst_nogate srst_open_drain connect_deassert_srst
Info : Unable to match requested speed 2000 kHz, using 1800 kHz
Info : Unable to match requested speed 2000 kHz, using 1800 kHz
Info : clock speed 1800 kHz
Info : STLINK v2 JTAG v28 API v2 SWIM v18 VID 0x0483 PID 0x374B
Info : using stlink api v2
Info : Target voltage: 3.256353
Info : stm32f4x.cpu: hardware has 6 breakpoints, 4 watchpoints
flash
Info : Unable to match requested speed 2000 kHz, using 1800 kHz
Info : Unable to match requested speed 2000 kHz, using 1800 kHz
adapter speed: 1800 kHz
target halted due to debug-request, current mode: Thread
xPSR: 0x61000000 pc: 0x1fff1048 msp: 0x20002e40
openocd.cfg:13: Error: wrong # args: should be "flash elf_file"
in procedure 'flash'
at file "openocd.cfg", line 13

ST-LinkのFirmwareアップデート

こちらで公開されているツールを使ってアップデートする。

参考

Rustでクロスコンパイル

x86_64-unknown-linux-gnuのホスト環境上で、CHIP向けのバイナリをRustでクロスコンパイルすることを目的とする。

概要

  1. rustup target add ${target_triple} でターゲットのツールチェインを追加
  2. ターゲットのリンカをインストール
  3. Cargoのconfigファイルにターゲット用のリンカ設定を記述
  4. cargo build --target ${target_triple} でクロスコンパイル実行

※Rustのツールチェイン管理を行ってくれるツールのrustupこちらを参考にインストール済みとする。

環境

ホストのシステム情報。

$ uname -a
Linux hostname 4.13.7-1-ARCH #1 SMP PREEMPT Sat Oct 14 20:13:26 CEST 2017 x86_64 GNU/Linux

$ rustup show
Default host: x86_64-unknown-linux-gnu

1.21.0-x86_64-unknown-linux-gnu (default)
rustc 1.21.0 (3b72af97e 2017-10-09)

ターゲットであるCHIPのシステム情報。

chip@chip:~$ uname -a
Linux chip 4.4.13-ntc-mlc #1 SMP Thu Nov 3 01:28:54 UTC 2016 armv7l GNU/Linux

ターゲットのツールチェインを追加

rustup target listで、Rustがサポートするターゲットのリストを確認する。

$ rustup target list | pr -w 90 --columns=2

aarch64-apple-ios                            mips-unknown-linux-gnu
aarch64-linux-android                        mips-unknown-linux-musl
aarch64-unknown-fuchsia                      mips64-unknown-linux-gnuabi64
aarch64-unknown-linux-gnu                    mips64el-unknown-linux-gnuabi64
arm-linux-androideabi                        mipsel-unknown-linux-gnu
arm-unknown-linux-gnueabi                    mipsel-unknown-linux-musl
arm-unknown-linux-gnueabihf                  powerpc-unknown-linux-gnu
arm-unknown-linux-musleabi                   powerpc64-unknown-linux-gnu
arm-unknown-linux-musleabihf                 powerpc64le-unknown-linux-gnu
armv7-apple-ios                              s390x-unknown-linux-gnu
armv7-linux-androideabi                      sparc64-unknown-linux-gnu
armv7-unknown-linux-gnueabihf                wasm32-unknown-emscripten
armv7-unknown-linux-musleabihf               x86_64-apple-darwin
armv7s-apple-ios                             x86_64-apple-ios
asmjs-unknown-emscripten                     x86_64-linux-android
i386-apple-ios                               x86_64-pc-windows-gnu
i586-pc-windows-msvc                         x86_64-pc-windows-msvc
i586-unknown-linux-gnu                       x86_64-rumprun-netbsd
i686-apple-darwin                            x86_64-unknown-freebsd
i686-linux-android                           x86_64-unknown-fuchsia
i686-pc-windows-gnu                          x86_64-unknown-linux-gnu (default)
i686-pc-windows-msvc                         x86_64-unknown-linux-musl
i686-unknown-freebsd                         x86_64-unknown-netbsd
i686-unknown-linux-gnu                       x86_64-unknown-redox
i686-unknown-linux-musl

armv7-unknown-linux-gnueabihfが見つかり、RustはCHIPのターゲットをサポートしていることを確認できたため、以下のコマンドでCHIP用のツールチェインを追加する。

$ rustup target add armv7-unknown-linux-gnueabihf

rustup target add後にrustup showrustup target listを実行してRustのツールチェインの状態を確認すると、armv7-unknown-linux-gnueabihf(installed)になっているのがわかる。

$ rustup show
Default host: x86_64-unknown-linux-gnu

installed targets for active toolchain
--------------------------------------

armv7-unknown-linux-gnueabihf
x86_64-unknown-linux-gnu

active toolchain
----------------

1.21.0-x86_64-unknown-linux-gnu (default)
rustc 1.21.0 (3b72af97e 2017-10-09)

$ rustup target list | pr -w 90 --columns=2

aarch64-apple-ios                            i686-unknown-linux-musl
aarch64-linux-android                        mips-unknown-linux-gnu
aarch64-unknown-fuchsia                      mips-unknown-linux-musl
aarch64-unknown-linux-gnu                    mips64-unknown-linux-gnuabi64
arm-linux-androideabi                        mips64el-unknown-linux-gnuabi64
arm-unknown-linux-gnueabi                    mipsel-unknown-linux-gnu
arm-unknown-linux-gnueabihf                  mipsel-unknown-linux-musl
arm-unknown-linux-musleabi                   powerpc-unknown-linux-gnu
arm-unknown-linux-musleabihf                 powerpc64-unknown-linux-gnu
armv7-apple-ios                              powerpc64le-unknown-linux-gnu
armv7-linux-androideabi                      s390x-unknown-linux-gnu
armv7-unknown-linux-gnueabihf (installed)    sparc64-unknown-linux-gnu
armv7-unknown-linux-musleabihf               wasm32-unknown-emscripten
armv7s-apple-ios                             x86_64-apple-darwin
asmjs-unknown-emscripten                     x86_64-apple-ios
i386-apple-ios                               x86_64-linux-android
i586-pc-windows-msvc                         x86_64-pc-windows-gnu
i586-unknown-linux-gnu                       x86_64-pc-windows-msvc
i686-apple-darwin                            x86_64-rumprun-netbsd
i686-linux-android                           x86_64-unknown-freebsd
i686-pc-windows-gnu                          x86_64-unknown-fuchsia
i686-pc-windows-msvc                         x86_64-unknown-linux-gnu (default)
i686-unknown-freebsd                         x86_64-unknown-linux-musl
i686-unknown-linux-gnu                       x86_64-unknown-netbsd

ターゲットのリンカをインストール

Rustのツールチェインではターゲットのリンカはサポートしていないため、別途用意する。

$ apt-get install gcc-arm-linux-gnueabihf

Cargoのconfigにターゲットのリンカ設定を記述

ロスコンパイルする対象とするプロジェクトを生成する。

$ cargo new --bin hello

Cargoが生成したスケルトンコードのままで実行すると、Hello, world!が標準出力に出力される。

$ cargo run
   Compiling hello v0.1.0 (file:///host_shared/hello)
    Finished dev [unoptimized + debuginfo] target(s) in 0.44 secs
     Running `target/debug/hello`
Hello, world!

これをそのままターゲット(CHIP)上で動作するようにするために、プロジェクトのconfigファイルを作成し、ターゲットのリンカの設定を記述する。

$ mkdir .cargo
$ cat > .cargo/config << EOF
> [target.armv7-unknown-linux-gnueabihf]
> linker = "arm-linux-gnueabihf-gcc"
> EOF

ロスコンパイル実行

$ cargo build --target armv7-unknown-linux-gnueabihf

ビルドされたバイナリの情報を確認すると、ターゲット(CHIP)向けのバイナリになっていることがわかる。

$ file target/armv7-unknown-linux-gnueabihf/debug/hello
target/armv7-unknown-linux-gnueabihf/debug/hello: ELF 32-bit LSB shared object, ARM, EABI5 version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux-armhf.so.3, for GNU/Linux 3.2.0, BuildID[sha1]=70dfb01aa67d797fc64005ac8cff1dba94a48a34, not stripped

$ readelf -A target/armv7-unknown-linux-gnueabihf/debug/hello
Attribute Section: aeabi
File Attributes
  Tag_CPU_name: "7-A"
  Tag_CPU_arch: v7
  Tag_CPU_arch_profile: Application
  Tag_ARM_ISA_use: Yes
  Tag_THUMB_ISA_use: Thumb-2
  Tag_FP_arch: VFPv3-D16
  Tag_ABI_PCS_GOT_use: GOT-indirect
  Tag_ABI_PCS_wchar_t: 4
  Tag_ABI_FP_rounding: Needed
  Tag_ABI_FP_denormal: Needed
  Tag_ABI_FP_exceptions: Needed
  Tag_ABI_FP_number_model: IEEE 754
  Tag_ABI_align_needed: 8-byte
  Tag_ABI_enum_size: int
  Tag_ABI_VFP_args: VFP registers
  Tag_CPU_unaligned_access: v6
  Tag_ABI_FP_16bit_format: IEEE 754

ロスコンパイルしたバイナリをターゲット上で実行

chip@chip:~$ ./hello
Hello, world!

ターゲット(CHIP)上で実行できることが確認できた。

参考

Rustでのクロスコンパイルに必要な情報が、以下のページに非常にわかりやすく説明されている。
japaric/rust-cross: Everything you need to know about cross compiling Rust programs!

環境構築の検証用にDockerを使う

Rustのクロスコンパイル環境構築の検証にDockerを使い始めた。

開発環境を構築する際、最初は試行錯誤しながら行なうため、さぁ環境が構築ができた、となった時に行なってきた手順のどれが最低限必要なものだったのかがわからなくなることが多い。 必要だと思ってインストールしたものが実は必要なかったり、必要ないと思ったものに実は依存していたりするため、手順に再現性があるのかどうかを検証したくなる。

このようなケースにおいて、いつでもクリーンな環境から手順の確認ができるDockerが非常に便利だったので、以下にRustの開発環境をDockerで立ち上げるまでにやったことをメモとして残しておく。

$ docker --version
Docker version 17.10.0-ce, build f4ffd2511c

Docker HubでRustがインストールされたDockerイメージが公開されているのでpullする。

$ docker pull rust

Rustのイメージが取得できたことを確認する。

$ docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
rust                latest              9578919665db        7 days ago          1.33GB

RustのDockerイメージからコンテナを起動する。

$ docker run -i -t -v ${host_dir_path}:${container_dir_path} --rm rust

docker runのオプションの意味は以下を参照。

  • -i : 標準入力を有効にする
  • -t : 疑似ターミナルを割り当てる
  • -v ${host_dir_path}:${container_dir_path} : ホストとコンテナの共有ディレクトリを指定する
  • --rm : コンテナからExitした時に自動的にコンテナを削除する

共有ディレクトリを指定しているのは、ホストで書いたコードをコンテナ内でビルドし、コンテナでビルドしたファイルをホストから参照するのを容易にするため。

Target Triple

Rustのコンパイラソースコードの中で使用されていた"Triple"という言葉の意味がわからなかったので調べた。

Target Triplet - OSDev Wiki

Target Triplets describe a platform on which code runs and are a core concept in the GNU build system. They contain three fields: the name of the CPU family/model, the vendor, and the operating system name.

サポートするターゲットを表す表現方法を Target Triple と呼んでいる。 ターゲットの指定が以下の3つのフィールドから成っていることが、Tripleと呼ぶ理由らしい。

  • CPU family/model
  • Vendor
  • OS

具体例としては、以下のようなターゲット指定が挙げられる。(よく見るやつ)

なお、clangのドキュメントでは以下のようにフィールド指定が定義されている。
Cross-compilation using Clang

The triple has the general format <arch><sub>-<vendor>-<sys>-<abi>, where: