link もっと前
   2020年 2月 3日 -
      2020年 1月 25日  
link もっと後

link 未来から過去へ表示(*)
link 過去から未来へ表示

link permalink

link 編集する

Zephyr OS で遊ぼう その 2 - ボードを追加してビルド

目次: Zephyr を調べる - まとめリンク

前回 ninja を実行したときに嫌というほどエラーが出ました。エラーを追う前に、コンフィグがあっているかチェックしたいと思います。

Zephyr は Linux Kernel と似たコンフィグのシステム(Kconfig)を持っています。使い方も Linux と似ており、ninja menuconfig とすると、ケバケバしい色(私の環境だと真っ白+黄色になる……)の画面が表示されます。


menuconfig の画面

なぜか Architecture が x86 となっていたり、SoC が LiteX になっていたり、色々おかしいです。なぜこうなってしまったかは後にして、手当たり次第でそれらしい値に直すと、Board Selection に今回追加した Hoge target が出現します。

menuconfig で手修正する箇所
$ ninja menuconfig

Architecture (x86 architecture)  --->
  ( ) RISCV architecture を選択

SoC/CPU/Configuration Selection (LiteX VexRiscv system implementation)  --->
  ( ) SiFive Freedom SOC implementation を選択

コンフィグがそれらしくなったら ninja でビルドします。

menuconfig で手修正後のビルド
$ ninja

zephyr/samples/hello_world/src/main.c:12:30: error: 'CONFIG_BOARD' undeclared (first use in this function); did you mean 'CONFIG_ARCH'?
  printk("Hello World! %s\n", CONFIG_BOARD);
                              ^~~~~~~~~~~~
                              CONFIG_ARCH

...

In file included from ../include/devicetree.h:12,
                 from ../soc/riscv/riscv-privilege/sifive-freedom/soc.h:15,
                 from ../include/arch/riscv/arch.h:25, 
                 from ../include/arch/cpu.h:23,
                 from ../include/kernel_includes.h:34, 
                 from ../include/kernel.h:17,
                 from zephyr/drivers/interrupt_controller/intc_plic.c:13:
zephyr/drivers/interrupt_controller/intc_plic.c: In function 'riscv_plic_irq_enable':
zephyr/include/generated/devicetree_fixups.h:8:25: error: 'DT_INST_0_SIFIVE_PLIC_1_0_0_IRQ_EN_BASE_ADDRESS' undeclared (first use in this function)
 #define DT_PLIC_IRQ_EN  DT_INST_0_SIFIVE_PLIC_1_0_0_IRQ_EN_BASE_ADDRESS
                         ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

...

先程よりはマシになりましたが、まだエラーが山のように出ます。エラーの種類を大別すると CONFIG_BOARD の定義がない、DT_INST_ なんちゃらの定義がない、に分けられます。

最初の CONFIG_BOARD については、いかにもボード側で定義しなければならなさそうな名前です。おなじみ他のボードを参照(boards/riscv/qemu_riscv32/Kconfig.defconfig など)すると、Kconfig.board で定義した config BOARD_HOGE が定義されているときに限り、CONFIG_BOARD にボード名のデフォルト値を設定していました。

つまり、このように書けば良いみたいです。

Kconfig.defconfig

# SPDX-License-Identifier: Apache-2.0

if BOARD_HOGE

config BOARD
	default "hoge"

endif

それ以外の DT_INST_ なんちゃらの定義については、少し複雑です。

Zephyr のデバイスツリーの扱い

Linux ではデバイスツリーファイル(*.dts)をデバイスツリーコンパイラ dtc でコンパイルして、Flattened Device Tree(fdt)という形式のバイナリにします。fdt はカーネル内部に組み込んだり、ブートローダが起動時にカーネルに渡すなどして利用されます。

しかし Zephyr はそもそも dtc を使わず、Python で処理します。デバイスツリーを書き間違えると Python スクリプトが怒ってくるのですが、これが理由みたいです。なんだと?と思う方は Zephyr のドキュメント(Devicetree - Zephyr Project Documentation)をご参照ください。下記に引用します。

Note: In addition to the Python code above, the standard dtc DTS compiler is also run on the devicetree. This is just to catch any errors or warnings it generates. The output is unused.

ドキュメントの Generated macros の章を見ると、デバイスツリーから DT_INST_ なんちゃら、というマクロを生成する、という説明があり、ビルドエラーの原因となったマクロの仲間のようです。

デバイスツリーを足そう

推測するにデバイスツリーを何も定義していないことが、DT_INST_ なんちゃらマクロが存在しないことの原因ではないでしょうか?他のボード(boards/riscv/qemu_riscv32/qemu_riscv32.dts など)がデバイスツリーをどうしているか、見ながら真似してみます。

hoge.dts

/* SPDX-License-Identifier: Apache-2.0 */

/dts-v1/;

#include <riscv32-fe310.dtsi>

/ {
	model = "Hoge Board";
	compatible = "hoge,hoge";

	chosen {
		zephyr,console = &uart0;
		zephyr,shell-uart = &uart0;
		zephyr,sram = &dtim;
		zephyr,flash = &flash0;
	};
};

&gpio0 {
	status = "okay";
};

&uart0 {
	status = "okay";
	current-speed = <115200>;
	clock-frequency = <16000000>;
};

&spi0 {
	status = "okay";

	#address-cells = <1>;
	#size-cells = <0>;
	reg = <0x10014000 0x1000 0x20400000 0xc00000>;
	flash0: flash@0 {
		compatible = "issi,is25lp128", "jedec,spi-nor";
		size = <134217728>;
		label = "FLASH0";
		jedec-id = [96 60 18];
		reg = <0>;
		// Dummy entry
		spi-max-frequency = <0>;
	};
};

ほぼ全てコピーしただけです。ビルドしてみると、最後の方まで行きますが、いくつかシンボルがないと言われます。ログはフルパスが出ていて鬱陶しいので意味が残る程度に削っています。

デバイスツリー定義後のビルドエラー
$ ninja

...

riscv64-zephyr-elf/bin/ld: zephyr/arch/arch/riscv/core/libarch__riscv__core.a(isr.S.obj): in function `.L0 ':
zephyr/arch/riscv/core/isr.S:244: undefined reference to `_sw_isr_table'
riscv64-zephyr-elf/bin/ld: zephyr/kernel/libkernel.a(sched.c.obj): in function `z_reset_time_slice':
zephyr/kernel/sched.c:276: undefined reference to `z_clock_elapsed'
riscv64-zephyr-elf/bin/ld: zephyr/kernel/libkernel.a(timeout.c.obj): in function `elapsed':
zephyr/kernel/timeout.c:69: undefined reference to `z_clock_elapsed'
collect2: error: ld returned 1 exit status
%
ninja: build stopped: subcommand failed.

これらのシンボルはドライバが提供するもののようですので、コンフィグでそれらしきものを ON にしていきます。

コンフィグ追加
General Architecture Options  --->
  Interrupt Configuration  --->
    [ ] Use generated IRQ tables を選択

General Kernel Options  --->
  [ ] Execute in place

Device Drivers  --->
  [ ] Serial Drivers  ----
    [ ] Enable UART Interrupt support を選択
    [ ] SiFive Freedom serial driver (NEW)  ----
      [ ] Enable SIFIVE Port 0 (NEW)  ---- を選択

  [ ] Console drivers  ---
    [ ] Use UART for console (NEW) を選択

  Timer Drivers  --->
    [ ] RISCV Machine Timer を選択

  [ ] GPIO Drivers  ----
    [ ] SiFive Freedom Processor GPIO driver (NEW)  ---- を選択

  [ ] Enable board pinmux driver  ----
    [ ] SiFive Freedom SOC pinmux driver (NEW)  ----

このように全て手動で設定する必要はない(後々不要となる)ので、あまり詳しくなる必要はないですが、

SiFive 関連
SiFive の SoC なのでコンフィグを片っ端から ON にしています
Use generated IRQ tables
_sw_isr_table シンボルの定義に関わっています
Execute in Place(XIP)
ON にしないと、ビルドしたときに RAM サイズから溢れているというエラーで怒られます
RISCV Machine Timer
z_clock_elapsed シンボルの定義に関わっています

コンフィグがそれらしくなったら ninja でビルドして、実行します。

コンフィグ追加後、ビルド&実行
$ ninja

...

Memory region         Used Size  Region Size  %age Used
             ROM:       13165 B        12 MB      0.10%
             RAM:        3808 B        16 KB     23.24%
        IDT_LIST:         569 B         2 KB     27.78%
[98/98] Linking C executable zephyr/zephyr.elf

$ /usr/bin/qemu-system-riscv32 -nographic -machine sifive_e -net none -chardev stdio,id=con,mux=on -serial chardev:con -mon chardev=con,mode=readline -kernel /home/katsuhiro/share/projects/oss/zephyr/build/zephyr/zephyr.elf

*** Booting Zephyr OS build zephyr-v2.1.0-1471-g7e7a4426d835  ***
Hello World! hoge

実行できました。Hello World の後ろがボード名(CONFIG_BOARD)です。無事、アプリが hoge ボード上で動いたということですね。次回は面倒くさかった手動コンフィグの撲滅に挑みます。

[編集者: すずき]
[更新: 2020年 9月 2日 16:56]

コメント一覧

  • コメントはありません。
open/close この記事にコメントする



link permalink

link 編集する

Zephyr OS で遊ぼう その 1 - ボードを追加して cmake

目次: Zephyr を調べる - まとめリンク

先日(2020年 1月 31日の日記参照)RISC-V 32bit 版の Zephyr OS が動作しました。気分を変えて、別のボードへの移植に挑戦してみたいと思います。

名前は何でも良いのですが、とりあえず hoge ボードということにします。-DBOARD=hoge を指定して cmake を実行すると、そんなものはないと怒られ、猛烈な勢いでヘルプメッセージが出るとともに、サポートされているボードの一覧が出てきます。

存在しないボードを指定したときの、ボード一覧
$ cmake -G Ninja -DBOARD=hoge ../samples/hello_world/

-- Zephyr version: 2.1.99
-- Found PythonInterp: /usr/bin/python3 (found suitable version "3.7.6", minimum required is "3.6")
-- Selected BOARD hoge
No board named 'hoge' found
see usage:

...

Supported Boards:

...

  riscv:
    hifive1
    hifive1_revb
    litex_vexriscv
    m2gl025_miv
    qemu_riscv32
    qemu_riscv64
    rv32m1_vega_ri5cy
    rv32m1_vega_zero_riscy
  x86:
    acrn
    gpmrb
    minnowboard
    qemu_x86_64
    qemu_x86_coverage
    qemu_x86
    qemu_x86_nommu
    up_squared

...

まず、この一覧に hoge ボードを載せるのが第一段階です。ボードの一覧を調べている cmake のスクリプトを見ます。

ボード一覧を調べている箇所

# zephyr/cmake/app/boilerplate.cmake

...

foreach(root ${BOARD_ROOT})
  # NB: find_path will return immediately if the output variable is
  # already set
  find_path(BOARD_DIR
    NAMES ${BOARD}_defconfig
    PATHS ${root}/boards/*/*
    NO_DEFAULT_PATH
    )
  if(BOARD_DIR AND NOT (${root} STREQUAL ${ZEPHYR_BASE}))
    set(USING_OUT_OF_TREE_BOARD 1)
  endif()

...

これで探しているように見えます。boards/*/* ディレクトリに「ボード名_defconfig」という名前のファイルがあれば良さそうです。足してから cmake を再度、存在しないボード名(hoge2 とかで良いです)で実行しましょう。

defconfig を足した後のボード一覧
$ mkdir boards/riscv/hoge
$ touch boards/riscv/hoge/hoge_defconfig

$ cmake -G Ninja -DBOARD=hoge2 ../samples/hello_world/

...

  riscv:
    hifive1
    hifive1_revb
    hoge        ★hoge が出てきた★
    litex_vexriscv
    m2gl025_miv
    qemu_riscv32
    qemu_riscv64
    rv32m1_vega_ri5cy
    rv32m1_vega_zero_riscy

...

やりました。ボードの一覧に hoge が出ました。BOARD=hoge にして cmake を実行しましょう。

defconfig を足した後の cmake
$ cmake -G Ninja -DBOARD=hoge ../samples/hello_world/

-- Zephyr version: 2.1.99
-- Selected BOARD hoge
Parsing zephyr/Kconfig
zephyr/scripts/kconfig/kconfig.py: Kconfig.zephyr:26: 'zephyr/boards/riscv/hoge/Kconfig.defconfig' not found (in 'source "$(BOARD_DIR)/Kconfig.defconfig"'). Check that environment variables are set correctly (e.g. $srctree, which is set to 'zephyr'). Also note that unset environment variables expand to the empty string.
CMake Error at zephyr/cmake/kconfig.cmake:214 (message):
  command failed with return code: 1
Call Stack (most recent call first):
  zephyr/cmake/app/boilerplate.cmake:461 (include)
  CMakeLists.txt:5 (include)

-- Configuring incomplete, errors occurred!

まだ色々とエラーが出ています。エラーメッセージは Kconfig.defconfig がないと言っています。このファイルを足すと、今度は Kconfig.board がないと言われますので、両方とも足します。

Kconfig.defconfig, Kconfig.board を足した後の cmake
$ touch boards/riscv/hoge/Kconfig.defconfig
$ touch boards/riscv/hoge/Kconfig.board

$ cmake -G Ninja -DBOARD=hoge ../samples/hello_world/

-- Zephyr version: 2.1.99
-- Found PythonInterp: /usr/bin/python3 (found suitable version "3.7.6", minimum required is "3.6")
-- Selected BOARD hoge
Parsing zephyr/Kconfig
Loaded configuration 'zephyr/boards/riscv/hoge/hoge_defconfig'
Merged configuration 'zephyr/samples/hello_world/prj.conf'

warning: <choice> (defined at boards/Kconfig:19) defined with type unknown

error: Aborting due to non-whitelisted Kconfig warning 'warning: <choice> (defined at boards/Kconfig:19)
defined with type unknown'. If this warning doesn't point to an actual problem, you can add it to
the whitelist at the top of zephyr/scripts/kconfig/kconfig.py.

CMake Error at zephyr/cmake/kconfig.cmake:214 (message):
  command failed with return code: 1
Call Stack (most recent call first):
  zephyr/cmake/app/boilerplate.cmake:461 (include)
  CMakeLists.txt:5 (include)


-- Configuring incomplete, errors occurred!

今度は choice が無いと言っています。エラーを出しているのは下記の cmake ファイルです。

エラーを出している kconfig.cmake
# zephyr/cmake/kconfig.cmake

execute_process(
  COMMAND
  ${PYTHON_EXECUTABLE}
  ${ZEPHYR_BASE}/scripts/kconfig/kconfig.py
  ${input_configs_are_handwritten}
  ${KCONFIG_ROOT}
  ${DOTCONFIG}
  ${AUTOCONF_H}
  ${PARSED_KCONFIG_SOURCES_TXT}
  ${input_configs}
  WORKING_DIRECTORY ${APPLICATION_SOURCE_DIR}
  # The working directory is set to the app dir such that the user
  # can use relative paths in CONF_FILE, e.g. CONF_FILE=nrf5.conf
  RESULT_VARIABLE ret
  )
if(NOT "${ret}" STREQUAL "0")
  message(FATAL_ERROR "command failed with return code: ${ret}")
endif()

このまま kconfig.py を見ても良いですが、おそらく時間のムダです。親切なエラーメッセージが出ているからです。メッセージの言うとおり boards/Kconfig を見ます。

choice のエラーの原因
# boards/Kconfig

...

# Note: $BOARD_DIR might be a glob pattern

choice
	prompt "Board Selection"

source "$(BOARD_DIR)/Kconfig.board"    ★このファイルが空★

endchoice

先ほど作成した Kconfig.board が空っぽだったため、choice の選択肢が存在せず怒られているようです。書く内容については、別のボード(例えば zephyr/boards/riscv/qemu_riscv32/Kconfig.board)を参照すると良いと思います。今回はこんな内容にしました。

Kconfig.board を書き換え

# SPDX-License-Identifier: Apache-2.0
 
config BOARD_HOGE
	bool "Hoge target"
	depends on SOC_RISCV_SIFIVE_FREEDOM

この定義だと SiFive の Freedom が搭載されていることになりますが、depends on の先に手を出すには、SoC の定義を加えなければなりません。一度に紹介しても訳がわからないので、今回はボードに焦点を絞ります。

書き換えた後にもう一度 cmake を実行すると、成功します。

Kconfig.board を書き換えた後に cmake 実行
$ cmake -G Ninja -DBOARD=hoge ../samples/hello_world/

-- Zephyr version: 2.1.99
-- Found PythonInterp: /usr/bin/python3 (found suitable version "3.7.6", minimum required is "3.6")
-- Selected BOARD hoge
Parsing zephyr/Kconfig
Loaded configuration 'zephyr/boards/riscv/hoge/hoge_defconfig'
Merged configuration 'zephyr/samples/hello_world/prj.conf'
Configuration saved to 'zephyr/build/zephyr/.config'
-- The C compiler identification is GNU 8.3.0
-- The CXX compiler identification is GNU 8.3.0
-- The ASM compiler identification is GNU
-- Found assembler: /home/katsuhiro/x-tools/riscv64-zephyr-elf/bin/riscv64-zephyr-elf-gcc
-- Cache files will be written to: /home/katsuhiro/.cache/zephyr
-- Configuring done
-- Generating done
-- Build files have been written to: zephyr/build


$ ninja

[0/1] Re-running CMake...
-- Zephyr version: 2.1.99
-- Selected BOARD hoge
Parsing zephyr/Kconfig
Loaded configuration 'zephyr/build/zephyr/.config'
No change to 'zephyr/build/zephyr/.config'
-- Cache files will be written to: /home/katsuhiro/.cache/zephyr
-- Configuring done
-- Generating done
-- Build files have been written to: zephyr/build
[5/83] Building C object zephyr/CMakeFiles/offsets.dir/arch/riscv/core/offsets/offsets.c.obj
FAILED: zephyr/CMakeFiles/offsets.dir/arch/riscv/core/offsets/offsets.c.obj
ccache /home/katsuhiro/x-tools/riscv64-zephyr-elf/bin/riscv64-zephyr-elf-gcc -DBUILD_VERSION=zephyr-v2.1.0-1471-g7e7a4426d835 -DKERNEL -D_FORTIFY_SOURCE=2 -D__ZEPHYR__=1 -I../kernel/include -I../arch/riscv/include -I../include -Izephyr/include/generated -I../soc/riscv/litex-vexriscv -isystem ../lib/libc/minimal/include -isystem /home/katsuhiro/x-tools/riscv64-zephyr-elf/lib/gcc/riscv64-zephyr-elf/8.3.0/include -isystem /home/katsuhiro/x-tools/riscv64-zephyr-elf/lib/gcc/riscv64-zephyr-elf/8.3.0/include-fixed -Os -imacroszephyr/build/zephyr/include/generated/autoconf.h -ffreestanding -fno-common -g -mabi=ilp32 -march=rv32ima -imacroszephyr/include/toolchain/zephyr_stdint.h -Wall -Wformat -Wformat-security -Wno-format-zero-length -Wno-main -Wno-pointer-sign -Wpointer-arith -Wno-unused-but-set-variable -Werror=implicit-int -fno-asynchronous-unwind-tables -fno-pie -fno-pic -fno-strict-overflow -fno-reorder-functions -fno-defer-pop -fmacro-prefix-map=zephyr/samples/hello_world=CMAKE_SOURCE_DIR -fmacro-prefix-map=zephyr=ZEPHYR_BASE -ffunction-sections -fdata-sections -std=c99 -nostdinc -MD -MT zephyr/CMakeFiles/offsets.dir/arch/riscv/core/offsets/offsets.c.obj -MF zephyr/CMakeFiles/offsets.dir/arch/riscv/core/offsets/offsets.c.obj.d -o zephyr/CMakeFiles/offsets.dir/arch/riscv/core/offsets/offsets.c.obj   -c zephyr/arch/riscv/core/offsets/offsets.c
In file included from ../include/sys/atomic.h:468,
                 from ../include/kernel_includes.h:21, 
                 from ../include/kernel.h:17,
                 from zephyr/arch/riscv/core/offsets/offsets.c:16:
zephyr/include/generated/syscalls/atomic.h:25:19: error: conflicting types for 'atomic_cas'
 static inline int atomic_cas(atomic_t * target, atomic_val_t old_value, atomic_val_t new_value)
                   ^~~~~~~~~~
In file included from ../include/kernel_includes.h:21, 
                 from ../include/kernel.h:17,
                 from zephyr/arch/riscv/core/offsets/offsets.c:16:
../include/sys/atomic.h:44:20: note: previous definition of 'atomic_cas' was here
 static inline bool atomic_cas(atomic_t *target, atomic_val_t old_value,

せっかく cmake がうまくいった、と思ったのも束の間で、ninja は見るのが嫌になるくらい大量のエラーを表示して失敗します。続きはまた今度。

[編集者: すずき]
[更新: 2020年 9月 2日 16:55]

コメント一覧

  • コメントはありません。
open/close この記事にコメントする



link permalink

link 編集する

Zephyr のエントリアドレス

目次: Zephyr を調べる - まとめリンク

Zephyr を qemu_riscv32 ボード向けにコンフィグし、ビルドディレクトリ下で ninja run を実行すると、最終的に下記のコマンドが実行され、QMEU が起動します。

QEMU の起動コマンド
$ /usr/bin/qemu-system-riscv32 -nographic -machine sifive_e -net none -chardev stdio,id=con,mux=on -serial chardev:con -mon chardev=con,mode=readline -kernel zephyr/build/zephyr/zephyr.elf -s -S

GDB で追うとわかりますが、QEMU の実行開始アドレスは 0x1000 です。0x1000 には 0x20400000 にジャンプするコードだけが置かれています。

0x1000 にあるコード

   0x1000:      lui     t0,0x20400
=> 0x1004:      jr      t0
   0x1008:      unimp
   0x100a:      unimp

HiFive1 実機であれば 0x20400000 は QSPI Flash がマッピングされているアドレスです(SiFive FE310-G000 Manual v2p3 を参照)。QEMU の場合は ELF ファイル build/zephyr/zephyr.elf のセクション情報通りに配置されます。

0x1000 にあるコード
$ riscv64-zephyr-elf-readelf -a build/zephyr/zephyr.elf  | less

...

Section Headers:
  [Nr] Name              Type            Addr     Off    Size   ES Flg Lk Inf Al
  [ 0]                   NULL            00000000 000000 000000 00      0   0  0
  [ 1] vector            PROGBITS        20400000 000094 000010 00  AX  0   0  4
  [ 2] exceptions        PROGBITS        20400010 0000a4 000258 00  AX  0   0  4
  [ 3] text              PROGBITS        20400268 0002fc 002ad4 00  AX  0   0  4
  [ 4] sw_isr_table      PROGBITS        20402d3c 002dd0 000200 00  WA  0   0  4
  [ 5] devconfig         PROGBITS        20402f3c 002fd0 000060 00   A  0   0  4
  [ 6] rodata            PROGBITS        20402f9c 003030 000215 00   A  0   0  4
  [ 7] datas             PROGBITS        80000000 003248 000018 00  WA  0   0  4
  [ 8] initlevel         PROGBITS        80000018 003260 000060 00  WA  0   0  4
  [ 9] _k_mutex_area     PROGBITS        80000078 0032c0 000014 00  WA  0   0  4
  [10] bss               NOBITS          80000090 0032e0 00013c 00  WA  0   0  8
  [11] noinit            NOBITS          800001d0 0032e0 000e00 00  WA  0   0 16

...

ジャンプ先の 0x20400000 には vector というセクションが対応していることがわかります。ELF ファイルを逆アセンブルしてみると、

0x20400000 にあるコード

zephyr/zephyr.elf:     file format elf32-littleriscv


Disassembly of section vector:

20400000 <__start>:

        /*
         * Set mtvec (Machine Trap-Vector Base-Address Register)
         * to __irq_wrapper.
         */
        la t0, __irq_wrapper
20400000:       00000297                auipc   t0,0x0
20400004:       01028293                addi    t0,t0,16 # 20400010 <__irq_wrapper>
        csrw mtvec, t0
20400008:       30529073                csrw    mtvec,t0

        /* Jump to __initialize */
        tail __initialize
2040000c:       4b40106f                j       204014c0 <__initialize>

以上のように __start という関数が居るようです。実装は zephyr/soc/riscv/riscv-privilege/common/vector.S にあります。

Zephyr とデバイスツリー

このエントリポイントアドレスはどのように決まるのでしょう?Zephyr では、ボード用のデバイスツリー zephyr/boards/riscv/qemu_riscv32/qemu_riscv32.dts にある Flash の先頭アドレスを変えると ELF のエントリポイント(0x20400000)も追従します。

ボード用のデバイスツリーの chosen ノード

        chosen {
                zephyr,console = &uart0;
                zephyr,shell-uart = &uart0;
                zephyr,sram = &dtim;
                zephyr,flash = &flash0;
        };

この chosen 以下を検知しているようです。スクリプト scripts/dts/gen_defines.py を見ると、SPI のときだけ特殊処理になっていて、それ以外は reg の値を先頭アドレスとみなしています。

Flash のサイズを見ている箇所

# zephyr/scripts/dts/gen_defines.py

...

def write_flash_node(edt):
    # Writes output for the top-level flash node pointed at by
    # zephyr,flash in /chosen

    node = edt.chosen_node("zephyr,flash")

    out_comment(f"/chosen/zephyr,flash ({node.path if node else 'missing'})")

    if not node:
        # No flash node. Write dummy values.
        out("FLASH_BASE_ADDRESS", 0)
        out("FLASH_SIZE", 0)
        return

    if len(node.regs) != 1:
        err("expected zephyr,flash to have a single register, has "
            f"{len(node.regs)}")

    if node.on_bus == "spi" and len(node.bus_node.regs) == 2:
        reg = node.bus_node.regs[1]  # QSPI flash
    else:
        reg = node.regs[0]

    out("FLASH_BASE_ADDRESS", hex(reg.addr))
    if reg.size:
        out("FLASH_SIZE", reg.size//1024)

...

Flash のアドレスは DT_FLASH_BASE_ADDRESS というマクロで参照できます。エントリーポイントを最終的に決めるのはリンカースクリプトです。SoC で独自のリンカースクリプトを実装することもできるようになっていますが、RISC-V の多くの SoC は、下記のスクリプトを使っています。

エントリーポイントを決めている箇所

// zephyr/include/arch/riscv/common/linker.ld

MEMORY
{
#ifdef CONFIG_XIP
    ROM (rx)  : ORIGIN = DT_FLASH_BASE_ADDRESS, LENGTH = KB(DT_FLASH_SIZE)
#endif
    RAM (rwx) : ORIGIN = CONFIG_SRAM_BASE_ADDRESS, LENGTH = KB(CONFIG_SRAM_SIZE)
    /* Used by and documented in include/linker/intlist.ld */
    IDT_LIST  (wx)      : ORIGIN = 0xFFFFF7FF, LENGTH = 2K
}

Zephyr のエントリポイントがどうやって決まるのか、少しわかった気がします。

[編集者: すずき]
[更新: 2020年 9月 2日 16:55]

コメント一覧

  • コメントはありません。
open/close この記事にコメントする



link permalink

link 編集する

Hello! Zephyr OS!! (RISC-V 32bit 版)

目次: Zephyr を調べる - まとめリンク

以前 Zephyr OS を実行しました(2019年 1月 12日の日記参照)が、今回は RISC-V 32bit 版で試してみようと思います。

この例では build をビルドディレクトリとします。どこに置いても動くはずですが、私はとりあえず zephyr の下に置いています。

クロスコンパイラの準備

前回同様 Zephyr SDK もしくは Crosstool-NG が使えます。参考までに Crosstool-NG のコンフィグを載せておきます。

crosstool-NG RISC-V 版のビルド
$ ./ct-ng menuconfig

Target options  --->
  Target Architecture (riscv)  --->
  [*] Build a multilib toolchain (READ HELP!!!)
  Bitness: (64-bit)  --->
  (rv32ima) Architecture level
  (ilp32) Generate code for the specific ABI

Toolchain options  --->
  (zephyr) Tuple's vendor string

Debug facilities  --->
  [*] gdb  --->
    [*]     Build a static cross gdb

一見すると 32bit CPU のコードをビルドする予定なのに、Bitness: 64bit にしており、不思議なコンフィグに見えるかもしれませんが、Zephyr OS のビルドはクセがあって、この設定が必要です。

Architecture level, Generate code for the specific ABI は GCC が生成するバイナリの命令セットを指定しており、それぞれ -march=rv32ima, -mabi=ilp32 に対応します。特に何も指定しないと rv32gc, ilp32f になるようです。

Zephyr の CMake スクリプト
zephyr/cmake/toolchain/xtools/target.cmake

set(CROSS_COMPILE_TARGET_riscv   riscv64-zephyr-elf)

set(CROSS_COMPILE_TARGET ${CROSS_COMPILE_TARGET_${ARCH}})

Zephyr の CMakefile を見るとわかるんですが、riscv64 も riscv32 も区別せず同じコンパイラでビルドし、しかもコンパイラ名は常に riscv64-zephyr-elf-gcc だと思っています。したがって 64bit 版をビルドする必要があり、multilib を有効にしています。

(余談)riscv 向けの -mabi, -march オプションの値を決めているのは zephyr/compiler/gcc/target.cmake で、rv32ima 固定になっています


-    list(APPEND TOOLCHAIN_C_FLAGS -mabi=ilp32 -march=rv32ima)
+    list(APPEND TOOLCHAIN_C_FLAGS -mabi=ilp32f -march=rv32gc)

上記のように変えると、Crosstool-NG で Architecture level, Generate code for the specific ABI の設定をしなくても良くなりますが、動くかどうかは試していません。

ビルドの方法

実ボードを持っていないので QEMU で試します。SiFive HiFive1 をエミュレートしているそうです。

環境変数のセットアップと cmake の実行
$ cat ~/.zephyrrc 

export ZEPHYR_TOOLCHAIN_VARIANT=xtools
export XTOOLS_TOOLCHAIN_PATH=/home/katsuhiro/x-tools

$ cd zephyr
$ source zephyr-env.sh

$ mkdir build
$ cd build

$ cmake -G Ninja -DBOARD=qemu_riscv32 ../samples/hello_world/

-- Zephyr version: 2.1.99
-- Found PythonInterp: /usr/bin/python3 (found suitable version "3.7.6", minimum required is "3.6")
-- Selected BOARD qemu_riscv32
-- Loading /home/katsuhiro/share/projects/oss/zephyr/boards/riscv/qemu_riscv32/qemu_riscv32.dts as base
Devicetree configuration written to /home/katsuhiro/share/projects/oss/zephyr/build/zephyr/include/generated/devicetree.conf
Parsing /home/katsuhiro/share/projects/oss/zephyr/Kconfig
Loaded configuration '/home/katsuhiro/share/projects/oss/zephyr/boards/riscv/qemu_riscv32/qemu_riscv32_defconfig'
Merged configuration '/home/katsuhiro/share/projects/oss/zephyr/samples/hello_world/prj.conf'
Configuration saved to '/home/katsuhiro/share/projects/oss/zephyr/build/zephyr/.config'
-- The C compiler identification is GNU 8.3.0
-- The CXX compiler identification is GNU 8.3.0
-- The ASM compiler identification is GNU
-- Found assembler: /home/katsuhiro/x-tools/riscv64-zephyr-elf/bin/riscv64-zephyr-elf-gcc
-- Cache files will be written to: /home/katsuhiro/.cache/zephyr
-- Configuring done
-- Generating done
-- Build files have been written to: /home/katsuhiro/share/projects/oss/zephyr/build

実行するときは ninja run で良いんですが、前回同様に ninja が何を起動しているか調べてみます。

Zephyr OS 実行
$ /usr/bin/qemu-system-riscv32 -nographic -machine sifive_e -net none -chardev stdio,id=con,mux=on -serial chardev:con -mon chardev=con,mode=readline -kernel /home/katsuhiro/share/projects/oss/zephyr/build/zephyr/zephyr.elf -s -S

(gdb から continue をすると、下記が出力される)

*** Booting Zephyr OS build zephyr-v2.1.0-1471-g7e7a4426d835  ***
Hello World! qemu_riscv32

オプション -s は GDB の接続を localhost:1234 で受け付けます、という意味です。オプション -S はエミュレータを Halted 状態で起動します。もし GDB をつなぐ必要がなければ -s や -S を削って起動してください。

GDB 接続
$ riscv64-zephyr-elf-gdb

GNU gdb (crosstool-NG 1.24.0.60-a152d61) 8.3.1
Copyright (C) 2019 Free Software Foundation, Inc.

...

(gdb) set arch riscv:rv32
The target architecture is assumed to be riscv:rv32

(gdb) target remote localhost:1234

Remote debugging using localhost:1234
warning: No executable has been specified and target does not support
determining executable automatically.  Try using the "file" command.
0x00001000 in ?? ()

(gdb) continue

Continuing.

実行できました。おなじみの Hello World! です。

[編集者: すずき]
[更新: 2020年 9月 2日 16:55]

コメント一覧

  • コメントはありません。
open/close この記事にコメントする



link permalink

link 編集する

クロスビルド用ツールチェーン - GCC 10.0 にしたらハマった

新し目の AArch64 のクロスコンパイル用ツールチェーンを作ろうとして、かなりハマったのでメモしておきます。

基本的には前回(2019年 4月 29日の日記参照)ご紹介した手順でビルドします。GCC と glibc のコードを変えなくて良い組み合わせは下記の通りです。特に新しいバージョンを使う理由がなければ、この組み合わせが無難です。

  • gcc: 8.3.0 (tag: releases/gcc-8.3.0)
  • glibc: 2.28 (tag: glibc-2.28)

私は新しい GCC が使いたかったので、HEAD にしました(バージョン的には 10.0 相当)。どうやら GCC のエラーチェックが厳しくなるらしく、glibc のビルドが通らなくなります。たくさんエラーが出ますが、一例を挙げると、下記のようなエラーです。

GCC HEAD (GCC 10.0 相当) で glibc 2.28 をビルドするとコンパイルエラー
./../include/libc-symbols.h:534:26: error: '__EI___errno_location' specifies less restrictive attributes than its target '__errno_location': 'const', 'nothrow' [-Werror=missing-attributes]
  534 |   extern __typeof (name) __EI_##name \
      |                          ^~~~~

エラーは glibc を新しくすると解決されるかと思いきや、よりおかしなことになります。例えば GCC 8.3 のまま glibc 2.30(おそらく 2.29 でも同じ症状が出る)にすると、下記のような変なエラーが出ます。

GCC 8.3 で glibc 2.30 をビルドするとリンクエラー
crosstool-builder-new-aarch64/buildroot/lib/gcc/aarch64-unknown-linux-gnu/8.3.0/../../../../aarch64-unknown-linux-gnu/bin/ld: crosstool-builder-new-aarch64/build/glibc/support/links-dso-program.o: Relocations in generic ELF (EM: 62)
crosstool-builder-new-aarch64/buildroot/lib/gcc/aarch64-unknown-linux-gnu/8.3.0/../../../../aarch64-unknown-linux-gnu/bin/ld: crosstool-builder-new-aarch64/build/glibc/support/links-dso-program.o: Relocations in generic ELF (EM: 62)
...

エラーの原因となっているオブジェクト links-dso-program.o を調べると、AArch64 向けにビルドしているにも関わらず、なぜか x86_64 用のオブジェクトが生成されています。

glibc/support 下に生成されたオブジェクト
build/glibc/support$ file *.o

echo-container.o:    ELF 64-bit LSB relocatable, ARM aarch64, version 1 (SYSV), with debug_info, not stripped
links-dso-program.o: ELF 64-bit LSB relocatable, x86-64, version 1 (SYSV), with debug_info, not stripped    ★★★★これ★★★★
shell-container.o:   ELF 64-bit LSB relocatable, ARM aarch64, version 1 (SYSV), with debug_info, not stripped
stamp.o:             empty
test-container.o:    ELF 64-bit LSB relocatable, ARM aarch64, version 1 (SYSV), with debug_info, not stripped
true-container.o:    ELF 64-bit LSB relocatable, ARM aarch64, version 1 (SYSV), with debug_info, not stripped

いったい何ですかね、これ。バグなのか、仕様なのかわかりません……。

GCC 10.0 と glibc 2.30 の組み合わせでビルドする方法

GCC と glibc をお互い最新にした組み合わせ、すなわち下記の組み合わせにしたとき、

  • gcc: 10.0 (HEAD)
  • glibc: 2.30 (tag: glibc-2.30)

先ほど説明した、両方のエラーに遭遇してビルドできませんので、glibc にパッチを当ててビルドエラーを回避します。

まずはコンパイルエラーを無視するパッチです。本来はエラーを無視するのではなく、エラーが指摘している事項を直すべきですけど、今回の主眼ではないのと、いずれ glibc 本家が直るだろうことを期待しておきます。

glibc のビルドエラーをあえて無視するパッチ

diff --git a/Makeconfig b/Makeconfig
index fd36c58c04..106688e210 100644
--- a/Makeconfig
+++ b/Makeconfig
@@ -916,7 +916,8 @@ ifeq	"$(strip $(+cflags))" ""
 endif	# $(+cflags) == ""
 
 +cflags += $(cflags-cpu) $(+gccwarn) $(+merge-constants) $(+math-flags) \
-	   $(+stack-protector)
+	   $(+stack-protector) \
+	   -Wno-zero-length-bounds -Wno-array-bounds -Wno-maybe-uninitialized
 +gcc-nowarn := -w
 
 # Each sysdeps directory can contain header files that both will be

次の links-dso-program はコミットログを見る限り、テストのサポート用ライブラリなので、とりあえず無くても動くはずです。ビルド自体をやめるパッチをあてます。

glibc の support/links-dso-program をビルドさせないパッチ

diff --git a/support/Makefile b/support/Makefile
index ab66913a02..19c3de2043 100644
--- a/support/Makefile
+++ b/support/Makefile
@@ -184,12 +184,12 @@ CFLAGS-support_paths.c = \
 		-DSBINDIR_PATH=\"$(sbindir)\" \
 		-DROOTSBINDIR_PATH=\"$(rootsbindir)\"
 
-ifeq (,$(CXX))
-LINKS_DSO_PROGRAM = links-dso-program-c
-else
-LINKS_DSO_PROGRAM = links-dso-program
-LDLIBS-links-dso-program = -lstdc++ -lgcc -lgcc_s $(libunwind)
-endif
+#ifeq (,$(CXX))
+#LINKS_DSO_PROGRAM = links-dso-program-c
+#else
+#LINKS_DSO_PROGRAM = links-dso-program
+#LDLIBS-links-dso-program = -lstdc++ -lgcc -lgcc_s $(libunwind)
+#endif
 
 ifeq (yes,$(have-selinux))
 LDLIBS-$(LINKS_DSO_PROGRAM) += -lselinux

クロスコンパイル環境は、各モジュールのバージョンアップですぐ壊れてしまって辛いです。ARM がこれだけ覇権を握っているにも関わらず、gcc も glibc もあまりチェックしてないんですかね……??

[編集者: すずき]
[更新: 2020年 1月 28日 00:12]

コメント一覧

  • superzeros 
    ARM64 target (cortexa53)と x86_64 host (Ryzen7)のクロスコンパイラのビルドで、当方にもご指摘のエラーがありました。で、そのパッチを拝借したところ、見事GLIBCのコンパイルが通りました。

    ありがとうございました!

    なんと申しましょうか、とても柔軟かつ大胆に繊細な対応をなされますね。

    補足:当方の各パッケージのバージョンは latest(2020-04-15)
    linux-5.6.4 (headers)
    gcc-9.3.0
    glibc-2.31
    binutils-2.34
    configure やBUILD手法は archlinux PKGBUILD と Cross LFS BOOK 等のWEB情報を参考にして適当にやっております(笑  
    (2020年04月15日 19:59:41)
  • すずき 
    コメントありがとうございます。お役に立ったようで良かったです。

    動作に支障無さそうなところだったので、かなり適当に変えてしまいました。

    大胆というか適当というか……。 
    (2020年04月15日 23:13:54)
  • superzeros 
    少し気になったのでglibcの履歴を調べてみたら・・・

    glibc-2.31/support/links-dso-program-c.c
    glibc-2.31/support/links-dso-program.cc

    このファイルは、2.29で最初に追加され

    \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\

    #include <iostream>
    using namespace std;
    int
    main (int argc, char **argv)
    /* Complexity to keep gcc from optimizing this away. */
    cout << (argc > 1 ? argv[1] : "null");
    return 0;


    \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\

    で、続いて2.30では

    \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\

    #include <iostream>
    /* makedb needs selinux dso's. */
    #ifdef HAVE_SELINUX
    # include <selinux/selinux.h>
    #endif
    using namespace std;
    /* The purpose of this file is to indicate to the build system which
    shared objects need to be copied into the testroot, such as gcc or
    selinux support libraries. This program is never executed, only
    scanned for dependencies on shared objects, so the code below may
    seem weird - it's written to survive gcc optimization and force
    such dependencies.
    */
    int
    main (int argc, char **argv)
    /* Complexity to keep gcc from optimizing this away. */
    cout << (argc > 1 ? argv[1] : "null");
    #ifdef HAVE_SELINUX
    /* This exists to force libselinux.so to be required. */
    cout << "selinux " << is_selinux_enabled ();
    #endif
    return 0;


    \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\

    2.29では、どういう経緯で誰によって導入されたのか、知りませんけど
    2.30では、SElinux(国家安全保障局)がどうたらこうたらの追加です
    誰のためになるんでしょうか ・・・

    自分の所有するハードウエアを自分で使いこなしたいだけのわたしからすれば
    バージョンアップに紛れ込んだオープンソースへの破壊活動みたいなもんですよ
    貴重な時間を奪われるところでしたが、おかげさまですぐに解決できてよかったです
    貴方に感謝いたします

      \(^o^)/
     
    (2020年04月16日 21:07:51)
  • すずき 
    詳細は調べていないので、コード中のコメントからの想像ですが……破壊活動というほどではないかなと思います。

    SELinux が有効なときは、SELinux のライブラリがないと動かないのでしょう。このヘルパープログラムは SELinux のライブラリを使っているから、ビルドシステムはテスト用の rootfs に SELinux のライブラリをコピーしてくれ、ということを示すための変更だと思います。 
    (2020年04月18日 23:05:39)
open/close この記事にコメントする



link permalink

link 編集する

C 言語の未定義動作と最適化

くそ長いですが、C 言語の未定義動作怖いね、printf でタイミング以外も動き変えられるよ、という話です。

環境ですが x86_64 向け Debian GNU/Linux 9.2 で実行しています。また GCC のバージョンは gcc (Debian 9.2.1-22) 9.2.1 20200104 です。

未定義動作のため、コンパイラの種類や、GCC のバージョンにより結果が変わると思われます。お家のマシンで試すならご留意ください。

1番目の実験

この日記の最後に貼ったプログラム(このプログラムをコンパイルすると、激しい警告が出ます)を gcc -Wall -O2 a.c && ./a.out のように実行すると、

1番目の実験: あれ?バッファオーバーランは…?
0: 0 0 0
1: 0 1 0
2: 0 2 0
3: 0 3 0
4: 0 4 0
...
47: 0 47 0
48: 0 48 0
49: 0 49 0
1770: 1770

こうなります。0〜59 の和は 1770 です。あってます。良かったですね。

なに?そういう問題じゃない?「なぜ array 終端を超えて guard2 にバッファオーバーランしない?」と考えた方、するどいです。しかし世の中そう単純ではありません。

2番目の実験

10行目の printf のコメントを外してローカル変数のアドレスを表示させると、

2番目の実験: 10行目の printf を有効、突然のバッファオーバーラン
0x7ffd9b348a10 0x7ffd9b348ae0 0x7ffd9b348bb0
0: 0 0 52
1: 0 1 53
2: 0 2 54
3: 0 3 55
4: 0 4 56
...
45: 0 45 0
46: 0 46 0
47: 0 47 0
48: 0 48 0
49: 0 49 0
1770: 1770

こうなります。突然オーバーランするようになりました。printf が何かしたんでしょうか、不思議ですね?

3番目の実験

どうして for ループを無意味に 2分割したのか?くっつけてみたらわかります。Segmentation Fault します。

3番目の実験: ループを 1つにするとクラッシュ
0: 0 0 0
1: 0 1 0
2: 0 2 0
3: 0 3 0
4: 0 4 0
...
45: 0 45 0
46: 0 46 0
47: 0 47 0
48: 0 48 0
49: 0 49 0
Segmentation fault

もう意味不明ですよね。何が起こっているんでしょう?

タネ明かし

この 60回の for ループは「配列の終端を超えたアクセス」が C 言語仕様上の未定義動作なので、何が起きても正しい、つまりどの結果も正しいです。

これだけだと、何言ってんのか意味不明だと思うので「printf 有効/無効」「for ループ 1つ/2つ」に着目して説明します。

1番目の実験(printf 無効、for ループ 2つ)
プログラムを見ると guard1, guard2 に対して memset 0 した後、参照のみで代入しません。コンパイラは guard1, guard2 をスタックに配置せず、配列への参照(guard1[i], guard2[i])は全て「定数の 0」に置換します。
(GIMPLE を見たら 033t.fre1 で 0 に置換されるようです)
このとき array のバッファオーバーランはスタックに退避されているレジスタ値などを書きつぶしますが、ギリギリ続行できています。
2番目の実験(printf 有効、for ループ 2つ)
1番目と変わりないと思いきや、printf が guard1, guard2 のアドレス参照をするため、定数の 0 に置換すると返せるアドレスがなくなり結果が変わってしまいます。このため、guard1, guard2 はスタックに配置されます。
このとき array のバッファーオーバーランは隣に配置された guard2 を書きつぶします。
3番目の実験(printf 無効、for ループ 1つ
いわゆる偶然の結果です。1番目と同様に guard1, guard2 はスタックに配置されず、array のバッファオーバーランによりスタックに退避したレジスタ値などが壊れます。for ループが 1つ減ったことでスタックに退避されるレジスタが 1つ減って(8バイト分余裕がなくなる)、1番目の実験でギリギリリターンアドレスを壊されずに耐えていたものが、耐えられなくなります。

3番目の実験の裏打ちとして、試しにループ回数を 80回くらいにすると for ループが 1つだろうが 2つだろうが、リターンアドレスがぶっ壊れて Segmentation Fault します。10行目の printf を有効にすると guard1, guard2 がスタックに配置されて、受け止めてくれるので、80回でも耐えます。

難解な C 言語仕様、曖昧な利用者の理解、過激なコンパイラの最適化、が招く結末

バッファオーバーランを期待していた向きには残念(?)かもしれませんが、guard1, guard2 はメモリ上に置いても置かなくても、C 言語仕様に矛盾しないなら、どっちでも良いです。もっというと C 言語仕様に矛盾しないなら、コンパイラの最適化は何をやっても OK です。

この「C 言語仕様に矛盾しないなら」はおそらくコンパイラ開発者には常識なのでしょうけども、C 言語の仕様は人間に優しくないのと、大多数の C 言語プログラマは言語仕様(特に未定義動作)を理解しておらず、何となく使っています。

難解な仕様、曖昧な理解、過激な最適化の相乗効果により、今日も世界のどこかで
「最適化で動きが変になっちゃったよ……。どうして…どうして……?」
とコンパイラとすれ違ったプログラマが泣いているでしょう。。。

参考

大したものではありませんが、ソースコードを載せておきます。

実験用ソースコード

#include <stdio.h>
#include <string.h>

int undefined()
{
	int guard1[50];
	int array[50];
	int guard2[50];
	int sum = 0, i;

	memset(guard1, 0, sizeof(guard1));
	memset(guard2, 0, sizeof(guard2));
	//printf("%p %p %p\n", &guard1[0], &array[0], &guard2[0]);

	for (i = 0; i < 60; i++) {
		array[i] = i;
	}

	for (i = 0; i < 60; i++) {
		sum += array[i];
	}

	for (i = 0; i < 50; i++) {
		printf("%2d: %d %d %d\n", i, guard1[i], array[i], guard2[i]);
	}

	return sum;
}

int main(int argc, char *argv[])
{
	int sum1 = 0, sum2 = 0, i;

	sum1 = undefined();

	for (i = 0; i < 60; i++) {
		sum2 += i;
	}

	printf("%d: %d\n", sum1, sum2);

	return 0;
}
[編集者: すずき]
[更新: 2020年 1月 26日 19:04]

コメント一覧

  • コメントはありません。
open/close この記事にコメントする



link もっと前
   2020年 2月 3日 -
      2020年 1月 25日  
link もっと後

管理用メニュー

link 記事を新規作成

合計:  counter total
本日:  counter today

link About www.katsuster.net
RDF ファイル RSS 1.0
QR コード QR コード

最終更新: 9/24 21:43

カレンダー

<2020>
<<<02>>>
------1
2345678
9101112131415
16171819202122
23242526272829

最近のコメント 5件

  • link 20年09月20日
    hdk 「最近は音楽聞く時やビデオ視聴時はミニコン...」
    (更新:09/24 21:43)
  • link 20年09月20日
    すずき 「ありゃー、同じ壊れ方ですね。\n新たなヘ...」
    (更新:09/24 00:23)
  • link 20年09月20日
    hdk 「うちのATH-AD300もやはり頭にプラ...」
    (更新:09/23 12:26)
  • link 20年07月10日
    すずき 「鳥のゲームは知りませんでした。色々やって...」
    (更新:08/11 18:59)
  • link 20年07月12日
    すずき 「小学生でサイトに投稿はスゴイです。そして...」
    (更新:08/11 18:59)

最近の記事 3件

link もっとみる
  • link 20年09月19日
    すずき 「[SHARP のマスク] 今となっては、高級マスクになってしまった...」
    (更新:09/23 04:48)
  • link 20年09月20日
    すずき 「[ヘッドフォンが壊れた] 以前(2012年 11月 8日の日記参照...」
    (更新:09/23 03:06)
  • link 20年09月22日
    すずき 「[3DMark] Steam のセールで 3DMark が意味不明...」
    (更新:09/23 02:55)

こんてんつ

open/close wiki
open/close Java API

過去の日記

open/close 2002年
open/close 2003年
open/close 2004年
open/close 2005年
open/close 2006年
open/close 2007年
open/close 2008年
open/close 2009年
open/close 2010年
open/close 2011年
open/close 2012年
open/close 2013年
open/close 2014年
open/close 2015年
open/close 2016年
open/close 2017年
open/close 2018年
open/close 2019年
open/close 2020年
open/close 過去日記について

その他の情報

open/close アクセス統計
open/close サーバ一覧
open/close サイトの情報