ラベル C言語 の投稿を表示しています。 すべての投稿を表示
ラベル C言語 の投稿を表示しています。 すべての投稿を表示

2024年3月29日金曜日

ttyd の main 関数を Rust プログラムから呼び出す(Rust から C の関数を呼び出す)

開発環境の準備

開発用コンテナの起動

docker run -it --rm --workdir /work -p 0.0.0.0:7681:7681 rust:1.77.0-slim-bookworm bash
WORKDIR=/work

ttyd のソースコード取得

git インストール。

apt update
apt install -y git

ttyd のリポジトリを clone.

cd $WORKDIR
git clone --depth 1 -b 1.7.5 https://github.com/tsl0922/ttyd

プロジェクトディレクトリへ移動。

cd $WORKDIR/ttyd

ttyd をライブラリとしてビルド

ttyd の README を参照しながら、ビルドに必要なパッケージをインストールする。

apt install -y build-essential cmake git libjson-c-dev libwebsockets-dev

前回の apt install と重複があるが、気にせず ttyd の README からそのままコピペ。

CMakeLists.txt を修正

実行バイナリではなく、 .a を生成するように CMakeLists.txt を修正する。

sed -i -e 's/add_executable(${PROJECT_NAME} ${SOURCE_FILES})/add_library(${PROJECT_NAME} STATIC ${SOURCE_FILES})/' ./CMakeLists.txt
  • add_executable: 実行バイナリを生成する
  • add_library: .a を生成する

関数 main の名前変更

別プログラムに組み込みたいので、エントリーポイントである main が存在すると困る。

なので、 mainttyd_main へリネームする。

sed -i -e 's/main(/ttyd_main(/' ./src/server.c

別プログラムから、 ttyd_main(2, ["ttyd", "-W", "bash"]) のような感じで呼び出すイメージ。

ttyd のビルド

mkdir build && cd build
cmake ..
make

これで、 libttyd.a が生成される。

ttyd を組み込む Rust プログラムを作る

ttydwrapper というプロジェクトを作って、 固定値で ttyd -W -t enableSixel=true bash コマンドを実行するのと同じように ttyd を呼び出すプログラムを作る。

bindgen に必要なパッケージのインストール

C 言語との FFI をするにあたり、 bindgen というコマンドを利用するので、それに必要なパッケージをインストールする。

apt update
apt install -y libclang-16-dev

Cargo プロジェクトの作成

今回は、 ttydwrapper という名前で作成。

cd $WORKDIR
cargo new ttydwrapper
cd ttydwrapper
rustup component add rustfmt

FFI 用のヘッダーファイルを作成

Rust から C の ttyd_main を呼び出すので、 ttyd_main の定義が記載されているヘッダーファイルを作成する。

Rust では、一般的に、 binding.h に必要なヘッダーファイルを列挙するらしい。

なので今回は、 ttyd.httyd_main を定義し、そのヘッダーファイルを binding.h へ記載する構成でヘッダーを作成した。

mkdir include
cat << EOF > ./include/ttyd.h
void ttyd_main(int argc, char **argv);
EOF

cat << EOF > ./include/binding.h
#include "ttyd.h"
EOF

バインディングを生成

bindgen コマンドで、ヘッダーから Rust のコードを生成する。

cargo install bindgen-cli
bindgen ./include/binding.h > ./src/bindings.rs

以下のような、 binding.rs が生成される。

/* automatically generated by rust-bindgen 0.69.4 */

extern "C" {
    pub fn ttyd_main(argc: ::std::os::raw::c_int, argv: *mut *mut ::std::os::raw::c_char);
}

生成された binding.rslib.rs からインクルードさせるのが一般的らしいので libs.rs を作る。

cat << EOF > ./src/lib.rs
include!("bindings.rs");
EOF

ライブラリをコピー

ライブラリは lib 以下に無いとダメなので .a を移動する。

mkdir $WORKDIR/ttydwrapper/lib
cp $WORKDIR/ttyd/build/libttyd.a $WORKDIR/ttydwrapper/lib/

ライブラリの情報を作成

build.rs にビルド時に必要なライブラリ情報の列挙をする。

cat << EOF > ./build.rs
fn main() {
    println!("cargo:rustc-link-search=native=$WORKDIR/ttydwrapper/lib");
    println!("cargo:rustc-link-search=native=/usr/lib/x86_64-linux-gnu");
    println!("cargo:rustc-link-lib=static=ttyd");
    println!("cargo:rustc-link-lib=dylib=uv");
    println!("cargo:rustc-link-lib=dylib=websockets");
    println!("cargo:rustc-link-lib=static=ssl");
    println!("cargo:rustc-link-lib=static=crypto");
    println!("cargo:rustc-link-lib=static=z");
    println!("cargo:rustc-link-lib=static=json-c");
}
EOF

ビルド時に build.rs の設定を使用するように修正

Cargo.toml[package] の末尾に build = "build.rs" を追加する。

(sed で実現できなかったので vim で編集した)

main 関数の作成

Rust の main 関数を作成し、 ttyd_main を呼び出すプログラムを作る。

cat << EOF > ./src/main.rs
use std::ffi::CString;
use std::os::raw::c_char;
use ttydwrapper::ttyd_main;

fn main() {

    let args = vec!["tty", "-W", "-t", "enableSixel=true", "bash"];

    let c_args: Vec<CString> = args.into_iter()
        .map(|arg| CString::new(arg).expect("CString::new failed"))
        .collect();

    let mut argv: Vec<*mut c_char> = c_args.iter()
        .map(|arg| arg.as_ptr() as *mut c_char)
        .collect();

    let argv_ptr: *mut *mut c_char = argv.as_mut_ptr();

    unsafe {
        ttyd_main(5, argv_ptr);
    }
}
EOF

ビルド

cargo build

これで ./target/debug/ttydwrapper が生成される。

動作確認

コンテナ起動時に 7681 をポートフォワーディングしているので、 ttydwrapper を実行した後 http://localhost:7681 へアクセスすると ttyd のターミナルが開く。

以上。

ここまで書いたところで、本当にやりたいのは Windows でだった事に気付いた…。

参考資料

2022年11月6日日曜日

C プログラムを WASI 向けの Wasm としてビルドし、 WasmEdge で実行する

そのうち Docker で Wasm コンテナが実行できるようになる(Docker DesktopがWebAssemblyランタイムを統合。コンテナと同様にWebAssemblyイメージを実行可能に - Publickey)らしいので、Wasm のビルドと実行を試してみる。

前提

  • OS: Windows 11 Pro 22H2 ビルド: 22621.755
  • Docker: Docker Desktop 4.13.1 (90346)
  • 使用イメージ: debian:bullseye-slim

イメージ起動

docker run -it --rm debian:bullseye-slim

環境構築

前提ツール類インストール

apt-get update
apt-get install -y curl git libxml2 file
  • curl : WasmEdge のインストールに必要
  • git : WasmEdge のインストールに必要
  • libxml2 : Wasm バイナリのビルドに必要なライブラリ
  • file : ビルドしたバイナリが Wasm バイナリになっているかを確認するために使用

wasi-sdk のインストール

Wasm のコンパイルに必要な SDK をインストールする。

curl -LO https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-16/wasi-sdk_16.0_amd64.deb
apt-get install -f ./wasi-sdk_16.0_amd64.deb

/opt/wasi-sdk/ にインストールされる。

WasmEdge のインストール

Wasm 実行に必要なランタイムをインストールする。

curl -sSf https://raw.githubusercontent.com/WasmEdge/WasmEdge/master/utils/install.sh | bash

~/.wasmedge にインストールされる。

ビルド

C のソースコードを作成

cat << EOF >> main.c
#include <stdio.h>

int main(int argc, char **argv) {
    printf("Hello, World!\n");
    return 0;
}
EOF

clang によるビルド

/opt/wasi-sdk/bin 内に clang があるので、それを使ってビルド。

/opt/wasi-sdk/bin/clang main.c --sysroot=/opt/wasi-sdk/share/wasi-sysroot/ -o ./main.wasm

バイナリの確認

file コマンドで確認すると、 WebAssembly であることが確認できる。

# file main.wasm
main.wasm: WebAssembly (wasm) binary module version 0x1 (MVP)

実行

wasmedge に、ビルドしたバイナリを渡す。

# ~/.wasmedge/bin/wasmedge ./main.wasm
Hello, World!

実行できた。以上。

参考資料

2020年8月30日日曜日

gcc の std オプションのデフォルト値を調べる

調べてみました。

gcc

$ echo | gcc -dM -E - | grep -F __STDC_V
#define __STDC_VERSION__ 201112L
  • -E : コンパイル・リンクを行わない(プリプロセッサ処理だけ行う)
  • -dM : 有効なマクロ定義を出力

g++

$ echo | g++ -dM -E -x c++ - | grep -F __cplusplus
#define __cplusplus 201402L
  • -x : 言語設定
    • 標準入力だと .c or .cpp で判定できないので、明示的に .cpp であることを知らせる

ちなみに、サポートしている std オプションの一覧は以下で取得できる。

gcc -v --help 2>/dev/null | grep -E "^\s+\-std=.*$"
g++ -v --help 2>/dev/null | grep -E "^\s+\-std=.*$"

参考資料

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年1月31日木曜日

Renesas V850 GCC を 64bit Linux でビルドする

Renesas V850 64bit Linux版gccコンパイラ - Qiitaの追試をしようと思ったのだけど、前回の差分を見るに、特にコード修正しなくても対応できそうなので試した。

結果、コード修正なしでも athrill のサンプルがビルドできるものが出来上がった。

環境

  • OS: Windows 10 Pro
  • docker: Docker version 18.09.1, build 4c52b90
  • 使ったコンテナ: debian:buster-slim

ubuntu:bionic, ubuntu:xenial でも同じ手順でビルドできるはず。

コンテナ起動

docker run -it --rm -v "$(pwd)\athrill:/athrill" debian:buster-slim

動作確認用に athrill のディレクトリをマウントしておく。

V850 ツールチェインビルド環境をととのえる

apt-get update
apt-get -y upgrade
apt-get install -y build-essential libgmp-dev libmpfr-dev libmpc-dev texinfo wget

必要なソースコードのダウンロードと展開

mkdir /toolchain
cd /toolchain
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
tar xfv binutils-2.24_v850_v14.01.tar.bz2
mv binutils-2.24 /binutils
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
tar xfv gcc-4.9.2_v850_v14.01.tar.bz2
mv gcc-4.9.2 /gcc
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
tar xfv newlib-2.1.0_v850_v14.01.tar.bz2
mv newlib-2.1.0 /newlib

最初は展開した場所を指定して configure したが、 make に失敗するのでルートに配置しなおすことにした。

binutils のビルド

mkdir -p /build/binutils
cd /build/binutils
../../binutils/configure --target=v850-elf --prefix=/usr/local --enable-soft-float
make CFLAGS="-Wno-cast-function-type -Wno-implicit-fallthrough -Wno-shift-negative-value -Wno-unused-value -Wno-pointer-compare"
make install

gcc のビルド

mkdir -p /build/gcc
cd /build/gcc
../../gcc/configure --target=v850-elf --prefix=/usr/local --enable-languages=c,c++ --disable-nls --disable-multilib --disable-libssp --with-newlib --with-headers=/newlib/newlib/libc/include
# xenial なら make のみで OK.
make CXXFLAGS="-std=c++03"
make install

c++11 から invalid になった構文(?)が使われているらしく、デフォルト設定だとビルドできないため、 -std=c++03 を指定。

newlib のビルド

mkdir -p /build/newlib
cd /build/newlib
../../newlib/configure --target=v850-elf --prefix=/usr/local
make
make install

動作確認

cd /athrill/sample/barmetal/step2
make clean all
../../../bin/linux/athrill -c1 -m memory.txt -d device_config.txt -t 500 test_main.elf

OK.

あとは docker のマルチステージビルドでバイナリだけコピーすればいい感じになるのではないでしょうか? これは後でやる。

参考資料

更新履歴

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

2019年1月24日木曜日

Renesas V850 64bit Linux版gccコンパイラの、本家との差分をとった

Renesas V850 64bit Linux版gccコンパイラ - Qiitaに、以下の記述があったので、公開されている変更後ソースコード本家のソースコードの差分をとってみた。

というわけで,ルネサスさんのサイトの binutilsを使用する方が良いと判断しました.
最初からそうすればよかったのですが,ルネサスさんのサイトのbinutilsはビルドエラーが発生するので,GNUサイトから取得していたという経緯があります.

いずれにせよ,発生したコンパイルエラーは,それほど難しいものではないので,強引に修正しビルドを通しました.本 binutilsを使用することで,bcond 問題も -msoft-float 問題も無事解決できました!

差分は以下の通り。

diff --git a/binutils/objcopy.c b/binutils/objcopy.c
index 14f6b96..e4ee7e6 100644
--- a/binutils/objcopy.c
+++ b/binutils/objcopy.c
@@ -1890,7 +1890,8 @@ copy_object (bfd *ibfd, bfd *obfd, const bfd_arch_info_type *input_arch)
        /* Umm, not sure what to do in this case.  */
        debuglink_vma = 0x1000;
 
-         bfd_set_section_vma (obfd, gnu_debuglink_section, debuglink_vma);
+         int tmp = bfd_set_section_vma (obfd, gnu_debuglink_section, debuglink_vma);
+              if (tmp == FALSE) return 0;
        }
    }
     }
diff --git a/gas/config/obj-elf.c b/gas/config/obj-elf.c
index 3377261..6d74846 100644
--- a/gas/config/obj-elf.c
+++ b/gas/config/obj-elf.c
@@ -1937,7 +1937,8 @@ obj_elf_init_stab_section (segT seg)
 
   /* Force the section to align to a longword boundary.  Without this,
      UnixWare ar crashes.  */
-  bfd_set_section_alignment (stdoutput, seg, 2);
+  int tmp = bfd_set_section_alignment (stdoutput, seg, 2);
+  if (tmp == FALSE) return;
 
   /* Make space for this first symbol.  */
   p = frag_more (12);
diff --git a/gas/config/tc-v850.c b/gas/config/tc-v850.c
index 8ac805b..5c4a029 100644
--- a/gas/config/tc-v850.c
+++ b/gas/config/tc-v850.c
@@ -238,7 +238,8 @@ do_v850_seg (int i, subsegT sub)
       bfd_set_section_flags (stdoutput, seg->s, seg->flags);
       if ((seg->flags & SEC_LOAD) == 0)
    seg_info (seg->s)->bss = 1;
-      bfd_set_section_alignment (stdoutput, seg->s, 2); 
+      int tmp = bfd_set_section_alignment (stdoutput, seg->s, 2); 
+      if (tmp == TRUE) return;
     }
 }
 
@@ -3747,7 +3748,8 @@ v850_md_end (void)
 
   note_sec = subseg_new (V850_NOTE_SECNAME, 0);
   bfd_set_section_flags (stdoutput, note_sec, SEC_HAS_CONTENTS | SEC_READONLY | SEC_MERGE);
-  bfd_set_section_alignment (stdoutput, note_sec, 2);
+  int tmp = bfd_set_section_alignment (stdoutput, note_sec, 2);
+  if (tmp == FALSE) return;
 
   /* Provide default values for all of the notes.  */
   for (id = V850_NOTE_ALIGNMENT; id <= NUM_V850_NOTES; id++)
diff --git a/gas/subsegs.c b/gas/subsegs.c
index 69f837b..2b90858 100644
--- a/gas/subsegs.c
+++ b/gas/subsegs.c
@@ -67,7 +67,8 @@ subseg_change (register segT seg, register int subseg)
     {
       seginfo = (segment_info_type *) xcalloc (1, sizeof (*seginfo));
       seginfo->bfd_section = seg;
-      bfd_set_section_userdata (stdoutput, seg, seginfo);
+      int tmp = bfd_set_section_userdata (stdoutput, seg, seginfo);
+      if (tmp == TRUE) return;
     }
 }
  
@@ -169,7 +170,8 @@ subseg_get (const char *segname, int force_new)
       secptr->output_section = secptr;
       seginfo = (segment_info_type *) xcalloc (1, sizeof (*seginfo));
       seginfo->bfd_section = secptr;
-      bfd_set_section_userdata (stdoutput, secptr, seginfo);
+      int tmp = bfd_set_section_userdata (stdoutput, secptr, seginfo);
+      if (tmp == TRUE) return secptr;
     }
   return secptr;
 }
diff --git a/gas/write.c b/gas/write.c
index 745abe6..f2887c3 100644
--- a/gas/write.c
+++ b/gas/write.c
@@ -362,8 +362,10 @@ record_alignment (/* Segment to which alignment pertains.  */
   if (seg == absolute_section)
     return;
 
-  if ((unsigned int) align > bfd_get_section_alignment (stdoutput, seg))
-    bfd_set_section_alignment (stdoutput, seg, align);
+  if ((unsigned int) align > bfd_get_section_alignment (stdoutput, seg)) {
+    int tmp = bfd_set_section_alignment (stdoutput, seg, align);
+    if (tmp == TRUE) return;
+  }
 }
 
 int
diff --git a/ld/ldlang.c b/ld/ldlang.c
index ba7f493..f12f258 100644
--- a/ld/ldlang.c
+++ b/ld/ldlang.c
@@ -4831,10 +4831,12 @@ lang_size_sections_1
               " section %s\n"), os->name);
 
        input = os->children.head->input_section.section;
-       bfd_set_section_vma (os->bfd_section->owner,
+       int tmp = bfd_set_section_vma (os->bfd_section->owner,
                     os->bfd_section,
                     bfd_section_vma (input->owner, input));
-       os->bfd_section->size = input->size;
+       if (tmp == TRUE) {
+           os->bfd_section->size = input->size;
+       }
        break;
          }
 
@@ -4916,9 +4918,10 @@ lang_size_sections_1
                 os->name, (unsigned long) (newdot - savedot));
          }
 
-       bfd_set_section_vma (0, os->bfd_section, newdot);
-
-       os->bfd_section->output_offset = 0;
+       int tmp = bfd_set_section_vma (0, os->bfd_section, newdot);
+       if (tmp == TRUE) {
+           os->bfd_section->output_offset = 0;
+       }
          }
 
        lang_size_sections_1 (&os->children.head, os,
diff --git a/opcodes/v850-dis.c b/opcodes/v850-dis.c
index 9433274..91cf998 100644
--- a/opcodes/v850-dis.c
+++ b/opcodes/v850-dis.c
@@ -434,7 +434,20 @@ disassemble (bfd_vma memaddr,
          info->fprintf_func (info->stream, "ep");
          break;
        case V850_OPERAND_SRG:
+#if 1
+{
+         if (value <= 31) {
+           info->fprintf_func (info->stream, "%s", v850_sreg_names[value]);
+         }
+         else {
+           char buf[128];
+           sprintf(buf, "unknown(%ld)", value);
+           info->fprintf_func (info->stream, "%s", buf);
+         }
+}
+#else
          info->fprintf_func (info->stream, "%s", v850_sreg_names[value]);
+#endif
          break;
        case V850E_OPERAND_REG_LIST:
          {

この修正が何を意味するのかは全く確認していないが、 その辺はおいおい見ていきましょう。

以上。

2018年9月12日水曜日

QEMU でエミュレートした Raspberry Pi 3 のシステムレジスタを確認した

前回からだいぶたってしまったが、 QEMU が Raspberry Pi 3 のエミュレートに対応したので復活。

システムレジスタの値を確認するコードを作成したので、仕様書と突き合わせていく。

実行結果

core id 以外はすべて 2 進数表記なので 0x でなく 0b 表記でないといけなかったですね…。

qemu-system-aarch64 -kernel ./kernel8.elf -serial null -serial mon:stdio -nographic -machine raspi3
core id: 0.
currentel: 0x1100.
nzcv: 0x1100000000000000000000000000000.
daif: 0x1111000000.
spsr_el1: 0x0.
spsr_el2: 0x0.
spsr_el3: 0x0.
midr_el1: 0x1000001000011111101000000110100.
revidr_el1: 0x0.
id_aa64pfr0_el1: 0x10001000100010.
id_aa64isar0_el1: 0x10001000100100000.
isr_el1: 0x0.

仕様書との突合せ

CurrentEL

記載箇所: Architecture Reference Manual - C5.2.1 CurrentEL, Current Exception Level

EL
0b00 EL0
0b01 EL1
0b10 EL2
0b11 EL3

ということで、 EL3 で動いているということがわかった。

NZCV

記載箇所: Architecture Reference Manual - C5.2.10 NZCV, Condition Flags

ラベル 意味
V Overflow condition flag. (1: 直前の計算がオーバーフローした, 0: 直前の計算がオーバーフローしていない)
C Carry condition flag. (1: 直前の計算で桁上がりした, 0: 直前の計算で桁上がりしていない)
Z Zero condition flag. (1: 直前の計算結果がゼロだった, 0: 直前の計算結果がゼロでなかった)
N Negative condition flag. (1: 直前の計算結果を符号付として解釈した時に、それが負数だった, 0:直前の計算結果を符号付として解釈した時に、それが負数でなかった)

29, 30 が 1 なので、この場合、Carry Condition flag と Zero condition flag が立っているのがわかる。

DAIF

記載箇所: Architecture Reference Manual - C5.2.2 DAIF, Interrupt Mask Bits

ラベル 意味
D Debug exceptions mask.
A SError interrupt Process state mask.
I IRQ mask.
F FIQ mask.

DAIF が全部マスクされていますね。

spsr_el1 - 3

EL1 から EL3 まで全部同じで全部 0。 例外一度も飛ばしていませんからね。

MIDR_EL1

ブロック 意味
Implementer 0x41 ARM Limited
Variant 0x0 製品のバリエーション判定に使えるが、 QEMU の場合は 0x0 固定らしい?
Architecture 0b1111 ID_* レジスタで個別に識別されるという意味
PartNum 0x0D03 製品番号?
Revision 0b0100 デバイスのリビジョン番号

revidr_el1

実装定義。 QEMU の場合は全部ゼロ。

id_aa64pfr0_el1

ブロック 意味
EL0 0b0010 Aarch64 または Aarch32 で実行ができる
EL1 0b0010 Aarch64 または Aarch32 で実行ができる
EL2 0b0010 Aarch64 または Aarch32 で実行ができる
EL3 0b0010 Aarch64 または Aarch32 で実行ができる
FP 0b0000 浮動小数点サポート(詳細は仕様書にて)
AdvSIMD 0b0000 SIMD サポート(詳細は仕様書にて)
GIC 0b0000 GIC Interface 未サポート
RAS 0b0000 RAS 未サポート
SVE 0b0000 SVE 未実装

oh…, GIC 未サポートなのか…。

id_aa64isar0_el1

ブロック 意味
AES 0b0010 AESE, AESD, AESMC, AESIMC, PMULL/PMULL2
SHA1 0b0001 SHA1C, SHA1P, SHA1M, SHA1H, SHA1SU0, SHA1SU1
SHA2 0b0001 SHA256H, SHA256H2, SHA256SU0, SHA256SU1, SHA512, SHA512H, SHA512H2, SHA512U0, SHA512SU1
CRC32 0b0001 CRC32B, CRC32H, CRC32W, CRC32X, CRC32CB, CRC32CH, CRC32CW, CRC32CX
Atomic 0b0000 Atomic 未サポート
RDM 0b0000 RDM 未サポート
SHA3 0b0000 SHA3 未サポート
SM3 0b0000 SM3 未サポート
SM4 0b0000 SM4 未サポート
DP 0b0000 DP 未サポート
FHM 0b0000 FHM 未サポート

とのこと。

isr_el1

ブロック 意味
F 0b0 FIQ pending bit.(0: No pending, 1: pending)
I 0b0 IRQ pending bit.(0: No pending, 1: pending)
A 0b0 SError pending bit.(0: No pending, 1: pending)

ペンディングとは?

まとめ

以上。 GIC 未サポートというのは予想外だったけれどまぁこんなものでしょうか?

参考資料

2018年8月14日火曜日

debian で Google Test 環境を整える

とりあえず動作確認。

以下のプログラム test_example.cpp を実行できるところまで。

test_example.cpp

#include "gtest/gtest.h"

namespace {

TEST(Practice, First) {
    EXPECT_EQ(1, 1);
}

}

環境

  • OS: Windows 10 Pro
  • Docker: 18.06.0-ce, build 0ffa825
  • Image: debian:stretch-slim

debian アップデート

apt-get update

必要パッケージのインストール

C/C++ ビルドのために build-essential を、 googletest のソース取得とビルドのために、 googletest, cmake をインストール。

apt-get install build-essential cmake googletest

googletest のビルド

debian の googletest パッケージは、ソースしか入っていないので、ビルドする必要がある。

cd ${PATH_TO_WORK}
mkdir googletest
cd googletest
cmake /usr/src/googletest/googletest
make

これで、 libgtest.a, libgtest_main.a が生成される。 共有ライブラリとしてビルドしたい場合には、 cmake -DBUILD_SHARED_LIBS=ON /usr/src/googletest/googletest とすると .so を生成してくれる。

動作確認

あとは、ビルド時に .a-lpthread を使うようにすれば OK.

# テストビルド
cd ${PATH_TO_WORK}
g++ ./test_example.cpp ./googletest/libgtest.a ./googletest/libgtest_main.a -lpthread -o ./test_example

# テスト実行
./test_example
Running main() from gtest_main.cc
[==========] Running 1 test from 1 test case.
[----------] Global test environment set-up.
[----------] 1 test from Practice
[ RUN      ] Practice.First
[       OK ] Practice.First (0 ms)
[----------] 1 test from Practice (0 ms total)

[----------] Global test environment tear-down
[==========] 1 test from 1 test case ran. (0 ms total)
[  PASSED  ] 1 test.

はい。

MSYS2 で Google Test 環境を整える

とりあえず動作確認。

以下のプログラム test_example.cpp を実行できるところまで。

test_example.cpp

#include "gtest/gtest.h"

namespace {

TEST(Practice, First) {
    EXPECT_EQ(1, 1);
}

}

環境

  • OS: Windows 10 Pro
  • MSYS2

MSYS2 インストール直後の状態から始めます。

MSYS2 アップデート

# 1 回目は mintty とか pacman のアップデート
# 「×」でウィンドウを消して MSYS 再起動。
pacman -Syu
pacman -Syu

必要パッケージのインストール

主に g++, make, gtest のためのパッケージをインストールする。

pacman -Ss mingw-w64-x86_64-toolchain mingw-w64-x86_64-gtest

動作確認

基本これだけなので、最初のアレをもうビルドできるはず。

# テストビルド
g++ ./test_example.cpp -lgtest -lgtest_main -o test_example.exe

# テスト実行
./test_example.exe
Running main() from gtest_main.cc
[==========] Running 1 test from 1 test case.
[----------] Global test environment set-up.
[----------] 1 test from Practice
[ RUN      ] Practice.First
[       OK ] Practice.First (0 ms)
[----------] 1 test from Practice (0 ms total)

[----------] Global test environment tear-down
[==========] 1 test from 1 test case ran. (0 ms total)
[  PASSED  ] 1 test.

はい。

2018年4月10日火曜日

C 言語の extern の挙動確認

『extern で外部関数定義をすると、「ポインタ」的なものが生成される』という話が出てきて、 んん?と思ったので gcc で挙動を確認する。

環境

下記環境で動作確認を行った。

  • OS: Windows10 Pro
  • MSYS2
  • gcc.exe (Rev1, Built by MSYS2 project) 7.2.0

多分 pacman -S base-devel で環境一式そろうはず...。

extern を使ったコードの作成

■ main.c

#include <stdio.h>

extern int impl_func();

void echo(char* str) {
    printf(str);
}

int main(int argc, char* argv[]) {

    echo("Hello, World!");

    impl_func();

    return 0;
}

■ impl.c

int impl_func() {
    return 1;
}

ビルド

コンパイル

$ gcc -c main.c -o main.o
$ gcc -c impl.c -o impl.o

リンク

$ ld -plugin C:/msys64/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/7.2.0/liblto_plugin-0.dll -plugin-opt=C:/msys64/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/7.2.0/lto-wrapper.exe -plugin-opt=-fresolution=C:\msys64\tmp\ccLHLsgf.res -plugin-opt=-pass-through=-lmingw32 -plugin-opt=-pass-through=-lgcc -plugin-opt=-pass-through=-lgcc_eh -plugin-opt=-pass-through=-lmoldname -plugin-opt=-pass-through=-lmingwex -plugin-opt=-pass-through=-lmsvcrt -plugin-opt=-pass-through=-lpthread -plugin-opt=-pass-through=-ladvapi32 -plugin-opt=-pass-through=-lshell32 -plugin-opt=-pass-through=-luser32 -plugin-opt=-pass-through=-lkernel32 -plugin-opt=-pass-through=-lmingw32 -plugin-opt=-pass-through=-lgcc -plugin-opt=-pass-through=-lgcc_eh -plugin-opt=-pass-through=-lmoldname -plugin-opt=-pass-through=-lmingwex -plugin-opt=-pass-through=-lmsvcrt -m i386pep -Bdynamic -o main.exe C:/msys64/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/7.2.0/../../../../x86_64-w64-mingw32/lib/../lib/crt2.o C:/msys64/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/7.2.0/crtbegin.o -LC:/msys64/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/7.2.0 -LC:/msys64/mingw64/bin/../lib/gcc -LC:/msys64/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/7.2.0/../../../../x86_64-w64-mingw32/lib/../lib -LC:/msys64/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/7.2.0/../../../../lib -LC:/msys64/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/7.2.0/../../../../x86_64-w64-mingw32/lib -LC:/msys64/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/7.2.0/../../.. main.o impl.o -lmingw32 -lgcc -lgcc_eh -lmoldname -lmingwex -lmsvcrt -lpthread -ladvapi32 -lshell32 -luser32 -lkernel32 -lmingw32 -lgcc -lgcc_eh -lmoldname -lmingwex -lmsvcrt C:/msys64/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/7.2.0/../../../../x86_64-w64-mingw32/lib/../lib/default-manifest.o C:/msys64/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/7.2.0/crtend.o

比較

シンボルテーブル

特別なシンボルは増えていないようだ。

externimpl_func 関数を外部定義参照した main.o は、 シンボル impl_func が未定義となる。

リンク時点で未定義シンボルのアドレス解決が行われ、 main.o の時点では未定義だったアドレスが設定されるということらしい。

$ nm main.o
0000000000000000 b .bss
0000000000000000 d .data
0000000000000000 p .pdata
0000000000000000 r .rdata
0000000000000000 r .rdata$zzz
0000000000000000 t .text
0000000000000000 r .xdata
                 U __main
0000000000000000 T echo
                 U impl_func
000000000000001c T main
                 U printf

$ nm impl.o
0000000000000000 b .bss
0000000000000000 d .data
0000000000000000 p .pdata
0000000000000000 r .rdata$zzz
0000000000000000 t .text
0000000000000000 r .xdata
0000000000000000 T impl_func

$ nm main.exe | grep impl_func
00000000004015c0 T impl_func

うーん、これで extern の挙動分かったつもりになったけど、 把握するうえでこれ以外に情報は必要だろうか?

とりあえず調べた内容は残しておく。