目次: 自宅サーバー
この日記システム、Wikiの話。
その他。
目次: Arduino
一覧が欲しくなったので作りました。
目次: 自宅サーバー
Debianの更新はとても簡単でありがたいですが、セキュリティ強化やらなんやらで毎回やり方が変わりますね。11.7 (bullseye) から12.0 (bookworm) にバージョンアップする方法をメモしておきます。
まずaptのリポジトリ設定はstable指定とします。コードネーム指定は使ったことないので知らないです、たぶん書き換える必要があるはず。単にapt-get updateとdist-upgradeを実行すると下記のように怒られます。
# apt-get update && apt-get dist-upgrade 取得:1 http://ftp.jp.debian.org/debian stable InRelease [147 kB] 取得:2 http://security.debian.org stable-security InRelease [48.0 kB] 取得:3 http://security.debian.org stable-security/main Sources [9,184 B] 取得:4 http://security.debian.org stable-security/main amd64 Packages [17.6 kB] 取得:5 http://security.debian.org stable-security/main Translation-en [7,460 B] パッケージリストを読み込んでいます... 完了 N: Repository 'http://ftp.jp.debian.org/debian stable InRelease' changed its 'Version' value from '11.7' to '12.0' E: Repository 'http://ftp.jp.debian.org/debian stable InRelease' changed its 'Codename' value from 'bullseye' to 'bookworm' N: This must be accepted explicitly before updates for this repository can be applied. See apt-secure(8) manpage for details.
エラーメッセージにあるapt-secureのヘルプを眺めても特に何も書いてなくて悲しいですが、apt-getの説明を見ると --allow-releaseinfo-changeというオプションがあるようです。悲しいことにまだ和訳がなくて英語のままでした……。
--allow-releaseinfo-change Allow the update command to continue downloading data from a repository which changed its information of the release contained in the repository indicating e.g a new major release. APT will fail at the update command for such repositories until the change is confirmed to ensure the user is prepared for the change. See also apt-secure(8) for details on the concept and configuration. Specialist options (--allow-releaseinfo-change-field) exist to allow changes only for certain fields like origin, label, codename, suite, version and defaultpin. See also apt_preferences(5). Configuration Item: Acquire::AllowReleaseInfoChange. (適当訳) updateコマンドにリリースの情報が変更されたリポジトリ(新たなメジャーリリースなどを示す)からのデータのダウンロード継続を許可します。 ユーザーが確実に変更に対して準備ができるように、変更が確認されるまでは、APTはそのようなリポジトリに対するupdateコマンドを失敗させます。 概念や設定の詳細についてはapt-secure(8) も参照してください。 いくつかのorigin, label, codename, suite, version, defaultpinのようなフィールドに限った変更を許可するための、専門家向けオプション(--allow-releaseinfo-change-field)があります。 apt_preferences(5) も参照してください。 設定項目: Acquire::AllowReleaseInfoChange
私の和訳は若干怪しいですけど、apt-get update --allow-releaseinfo-changeを実行すれば良さそうです。security.debian.orgのInReleaseは、この日記を書く前にallow-releaseinfo-changeをしたせい?なのか、取得にならずヒット(既に更新済み)になっています。気にしないでください。
# apt-get update --allow-releaseinfo-change 取得:1 http://ftp.jp.debian.org/debian stable InRelease [116 kB] ヒット:2 http://security.debian.org stable-security InRelease 116 kBを0秒 で取得しました (252 kB/s) パッケージリストを読み込んでいます... 完了 # apt-get update && apt-get dist-upgrade ヒット:1 http://security.debian.org stable-security InRelease 取得:2 http://ftp.jp.debian.org/debian stable InRelease [147 kB] パッケージリストを読み込んでいます... 完了 N: Repository 'http://ftp.jp.debian.org/debian stable InRelease' changed its 'Version' value from '11.7' to '12.0' E: Repository 'http://ftp.jp.debian.org/debian stable InRelease' changed its 'Codename' value from 'bullseye' to 'bookworm' N: This must be accepted explicitly before updates for this repository can be applied. See apt-secure(8) manpage for details.
あれ?ダメですね、まだ怒っています。もう一回やってみましょうか。
# apt-get update --allow-releaseinfo-change 取得:1 http://ftp.jp.debian.org/debian stable InRelease [147 kB] 取得:2 http://ftp.jp.debian.org/debian stable/non-free Sources [78.2 kB] 取得:3 http://ftp.jp.debian.org/debian stable/main Sources [9,628 kB] ヒット:4 http://security.debian.org stable-security InRelease 取得:5 http://ftp.jp.debian.org/debian stable/contrib Sources [51.2 kB] 取得:6 http://ftp.jp.debian.org/debian stable/main amd64 Packages [8,904 kB] 取得:7 http://ftp.jp.debian.org/debian stable/main Translation-ja [754 kB] 取得:8 http://ftp.jp.debian.org/debian stable/main Translation-en [6,076 kB] 取得:9 http://ftp.jp.debian.org/debian stable/main all Contents (deb) [32.8 MB] 取得:10 http://ftp.jp.debian.org/debian stable/main amd64 Contents (deb) [11.3 MB] 取得:11 http://ftp.jp.debian.org/debian stable/contrib amd64 Packages [54.3 kB] 取得:12 http://ftp.jp.debian.org/debian stable/contrib Translation-en [48.7 kB] 取得:13 http://ftp.jp.debian.org/debian stable/contrib amd64 Contents (deb) [61.2 kB] 取得:14 http://ftp.jp.debian.org/debian stable/contrib all Contents (deb) [98.6 kB] 取得:15 http://ftp.jp.debian.org/debian stable/non-free amd64 Packages [98.5 kB] 取得:16 http://ftp.jp.debian.org/debian stable/non-free Translation-en [67.2 kB] 取得:17 http://ftp.jp.debian.org/debian stable/non-free all Contents (deb) [839 kB] 取得:18 http://ftp.jp.debian.org/debian stable/non-free amd64 Contents (deb) [76.4 kB] 71.1 MBを23秒 で取得しました (3,091 kB/s) パッケージリストを読み込んでいます... 完了 N: Repository 'http://ftp.jp.debian.org/debian stable InRelease' changed its 'Version' value from '11.7' to '12.0' N: Repository 'http://ftp.jp.debian.org/debian stable InRelease' changed its 'Codename' value from 'bullseye' to 'bookworm' # apt-get update && apt-get dist-upgrade ヒット:1 http://security.debian.org stable-security InRelease ヒット:2 http://ftp.jp.debian.org/debian stable InRelease パッケージリストを読み込んでいます... 完了 パッケージリストを読み込んでいます... 完了 依存関係ツリーを作成しています... 完了 状態情報を読み取っています... 完了 アップグレードパッケージを検出しています... 完了 (以下略)
今度はうまくいきました。先ほどとの違いは良くわかりませんが、めげずに何回かやった方が良いんでしょうか。
目次: C言語とlibc
組み込みソフトなどでバイナリデータをC言語の配列として記述したいときがあります。毎回手で変換するのは面倒ですし、スクリプトや実行バイナリだと移植が面倒(特にWindows)で、意外と悩ましいです。
もしcmakeだけで実装できれば移植の心配はなくなる(cmakeが対応しているプラットフォームに限る)のでは?と思い、試しに作ってみました。変換の本体は下記の関数です。
function(convert_bin2c input_file output_file c_var_name)
file(READ ${input_file} BIN_HEX HEX)
file(SIZE ${input_file} BIN_LEN)
# Wrap lines per 16bytes
string(REPEAT ".." 16 REGEX_PAT)
string(REGEX REPLACE "(${REGEX_PAT})" "\\1\n" BIN_HEX_WRAP ${BIN_HEX})
string(REGEX REPLACE "([0-9a-f][0-9a-f])" "0x\\1, " BIN_ARRAY ${BIN_HEX_WRAP})
# C format
set(C_H_INC "#include <stdint.h>")
set(C_H_LEN "const size_t ${c_var_name}_len = ${BIN_LEN};")
set(C_H_DAT "const uint8_t ${c_var_name}_dat[] = {\n${BIN_ARRAY}\n};")
# Generate header
file(WRITE ${output_file} "${C_H_INC}\n\n${C_H_LEN}\n${C_H_DAT}\n")
endfunction()
処理の概要は下記のとおりです。cmakeは文法にクセがありすぎて見づらいし書きづらいし最悪ですが、やっていることは難しくありません。
なおfile(SIZE) はcmake 3.14、string(REPEAT) はcmake 3.15で追加された機能なので、古すぎるcmakeだと動かないと思います。詳細はcmakeのドキュメントをご確認ください。
例えば下記のように使います。
convert_bin2c(${CMAKE_SOURCE_DIR}/test.bin ${CMAKE_BINARY_DIR}/include/bin.h array_binary)
ソースコードディレクトリ直下のtest.binをビルドディレクトリのinclude/bin.hに変換し、array_binary_dat(配列本体)という名前にします。
これだけだと合っているか間違っているか確認しづらいので、動作確認用に簡単なソースコード一式を作りましょう。
cmake_minimum_required(VERSION 3.16)
project(test_bin2c)
enable_language(C)
add_executable(test_bin2c)
target_sources(test_bin2c PRIVATE main.c)
target_include_directories(test_bin2c PRIVATE
${CMAKE_BINARY_DIR}/include
)
function(convert_bin2c input_file output_file c_var_name)
file(READ ${input_file} BIN_HEX HEX)
file(SIZE ${input_file} BIN_LEN)
# Wrap lines per 16bytes
string(REPEAT ".." 16 REGEX_PAT)
string(REGEX REPLACE "(${REGEX_PAT})" "\\1\n" BIN_HEX_WRAP ${BIN_HEX})
string(REGEX REPLACE "([0-9a-f][0-9a-f])" "0x\\1, " BIN_ARRAY ${BIN_HEX_WRAP})
# C format
set(C_H_INC "#include <stdint.h>")
set(C_H_LEN "const size_t ${c_var_name}_len = ${BIN_LEN};")
set(C_H_DAT "const uint8_t ${c_var_name}_dat[] = {\n${BIN_ARRAY}\n};")
# Generate header
file(WRITE ${output_file} "${C_H_INC}\n\n${C_H_LEN}\n${C_H_DAT}\n")
endfunction()
convert_bin2c(${CMAKE_SOURCE_DIR}/test.bin ${CMAKE_BINARY_DIR}/include/bin.h array_binary)
実行ファイル名はtest_bin2cでソースコードはmain.cです。変換対象のバイナリはtest.binとしました。変換後のC言語ソースコードはビルドディレクトリの下のinclude/bin.hとしました。
動作確認用のmain.cとtest.binは下記の通りです。
#include <stdio.h>
#include <stdint.h>
#include <unistd.h>
#include <bin.h>
static void dump(const uint8_t *dat, size_t len)
{
printf("00000000: ");
for (size_t i = 0; i < len; i++) {
printf("%02x ", dat[i]);
if (i % 16 == 7) {
printf(" ");
}
if (i % 16 == 15) {
printf("\n%08x: ", (int)(i + 1));
}
}
printf("\n");
}
int main(int argc, char *argv[])
{
dump(array_binary_dat, array_binary_len);
return 0;
}
$ cat test.bin aaaa bbbb cccc DDDDD EEEEE FFFFF
あえて説明するほどでもないですが、main.cは配列を16バイトずつダンプするだけのプログラムです。test.binはこのデータのままでも、お好きなデータに置き換えて試しても良いです。
これでビルドできるはずですので、確認します。ビルドツールをNinjaにしたのは私の単なる好みなので、何を使っても良いです。動くはず。
$ cmake -B build -G Ninja ./ -- The C compiler identification is GNU 12.2.0 -- The CXX compiler identification is GNU 12.2.0 -- Detecting C compiler ABI info -- Detecting C compiler ABI info - done -- Check for working C compiler: /usr/lib/ccache/cc - skipped -- Detecting C compile features -- Detecting C compile features - done -- Detecting CXX compiler ABI info -- Detecting CXX compiler ABI info - done -- Check for working CXX compiler: /usr/lib/ccache/c++ - skipped -- Detecting CXX compile features -- Detecting CXX compile features - done -- Configuring done -- Generating done -- Build files have been written to: /home/katsuhiro/share/projects/c/test-cmake-bin2c/build $ ninja -C build ninja: Entering directory `build' [2/2] Linking C executable test_bin2c
生成されたヘッダファイルを確認します。コンパイルは通っているので文法的には間違っていないと思いますが、一応ご紹介ということで。
$ cat build/include/bin.h #include <stdint.h> const size_t array_binary_len = 33; const uint8_t array_binary_dat[] = { 0x61, 0x61, 0x61, 0x61, 0x0a, 0x62, 0x62, 0x62, 0x62, 0x0a, 0x63, 0x63, 0x63, 0x63, 0x0a, 0x44, 0x44, 0x44, 0x44, 0x44, 0x0a, 0x45, 0x45, 0x45, 0x45, 0x45, 0x0a, 0x46, 0x46, 0x46, 0x46, 0x46, 0x0a, };
変換結果は特に問題なさそうなので、動作確認します。正しく変換できているかどうか、hexdumpの出力と比較します。
$ ./build/test_bin2c 00000000: 61 61 61 61 0a 62 62 62 62 0a 63 63 63 63 0a 44 00000010: 44 44 44 44 0a 45 45 45 45 45 0a 46 46 46 46 46 00000020: 0a $ hexdump -C test.bin 00000000 61 61 61 61 0a 62 62 62 62 0a 63 63 63 63 0a 44 |aaaa.bbbb.cccc.D| 00000010 44 44 44 44 0a 45 45 45 45 45 0a 46 46 46 46 46 |DDDD.EEEEE.FFFFF| 00000020 0a |.| 00000021
動きました。結果も間違ってなさそうです。
思いつく欠点としては、巨大なファイルを扱うとcmakeが遅かったりメモリ食いすぎて死んじゃう気がします。が、cmakeが扱いきれないレベルの巨大配列をソースコードに書くこと自体、設計が間違っていると思うので、処理速度や巨大ファイルの扱いはあまり気にしないことにします。
目次: OpenOCD
今まで散々OpenOCDを使っておきながら今更感がありますが、OpenOCDをソースコードからビルドする方法のメモです。
環境依存の部分を減らすためUbuntu 20.04 LTSのDockerイメージを起点にします。初めに依存ライブラリの開発用パッケージをインストールします。ドキュメントを生成するならdoxygenなども必要ですが、今回は省略しています。
# apt-get install -y git gcc g++ autoconf automake libtool pkg-config make \ libusb-1.0-0-dev libhidapi-dev libgpiod-dev libftdi1-dev
ソースコードを取得したらbootstrapを実行して(最初の1回だけで良いです)、configureを実行します。configureの最後にどんな設定が有効になったか一覧が出ます。親切で良いですね。
$ git clone https://git.code.sf.net/p/openocd/code openocd-code $ cd openocd-code $ ./bootstrap (略) $ ./configure --enable-internal-libjaylink (略) OpenOCD configuration summary -------------------------------------------------- MPSSE mode of FTDI based devices yes (auto) ST-Link Programmer yes (auto) TI ICDI JTAG Programmer yes (auto) Keil ULINK JTAG Programmer yes (auto) Altera USB-Blaster II Compatible yes (auto) Bitbang mode of FT232R based devices yes (auto) Versaloon-Link JTAG Programmer yes (auto) TI XDS110 Debug Probe yes (auto) CMSIS-DAP v2 Compliant Debugger yes (auto) OSBDM (JTAG only) Programmer yes (auto) eStick/opendous JTAG Programmer yes (auto) Olimex ARM-JTAG-EW Programmer yes (auto) Raisonance RLink JTAG Programmer yes (auto) USBProg JTAG Programmer yes (auto) Espressif JTAG Programmer yes (auto) CMSIS-DAP Compliant Debugger yes (auto) Nu-Link Programmer yes (auto) Cypress KitProg Programmer yes (auto) Altera USB-Blaster Compatible yes (auto) ASIX Presto Adapter yes (auto) OpenJTAG Adapter yes (auto) SEGGER J-Link Programmer yes (auto) Bus Pirate yes (auto) Use Capstone disassembly framework no
基本的にOpenOCDのconfigureは依存ライブラリを発見したら、関連する機能を自動的に有効にしてくれますので、特に何も指定する必要がありません。が、今回は --enable-internal-libjaylinkを指定してOpenOCDが内蔵しているlibjaylinkを使用しています。
なぜかというとOpenOCDはlibjaylink 0.2以降を必要としますが、Ubuntu 20.04が提供するlibjaylink(パッケージ名libjaylink-dev)はバージョンが0.1.0と古く、インストールしてもSEGGER J-Link Programmerの機能がyesにならないためです。
ログを見ていると--enable-internal-libjaylinkはdeprecatedで将来的に使えなくなるという警告が出ており、使えなくなると困ってしまうのですが……、とりあえず使える限りは使いましょう。
(略) libjaylink configuration summary: - Package version ................ 0.3.1 - Library version ................ 2:0:2 - Installation prefix ............ /usr/local - Building on .................... x86_64-pc-linux-gnu - Building for ................... x86_64-pc-linux-gnu Enabled transports: - USB ............................ yes - TCP ............................ yes configure: WARNING: Using the internal libjaylink is deprecated and will not be possible in the future. (略)
無事configureが成功したらmakeします。バイナリはsrc/ ディレクトリの下に生成されます。
$ make $ ./src/openocd --version Open On-Chip Debugger 0.12.0+dev-00248-g56fd04832 (2023-06-28-00:00) Licensed under GNU GPL v2 For bug reports, read http://openocd.org/doc/doxygen/bugs.html
うまくいったようです。良かった良かった。
目次: OpenOCD
忘れてしまうので。RISC-Vの独自(もしくは標準に準拠しているものの新しすぎるなど)のCSR(Control and Status Register)を読み出す方法をメモしておきます。GDBでOpenOCDに接続しinfo reg (レジスタ名) とすると内容が読み出せます。
(gdb) info reg mscratch mepc mcause mtval mip mscratch 0x0 0 mepc 0x6000c580 1610663296 mcause 0xb 11 mtval 0x0 0 mip 0x880 2176
もう一つの方法としてCSRの番号指定でも読み出せます。csr(番号) という名前になります。番号は10進数で指定するようで、例えばmscratch (0x340) なら832になります。
(gdb) info reg csr832 csr833 csr834 csr835 csr836 csr832 0x0 0 csr833 0x6000c580 1610663296 csr834 0xb 11 csr835 0x0 0 csr836 0x880 2176
これだけだと名前がわかりにくいだけで特に嬉しくありませんが、これから紹介するOpenOCDの設定と組み合わせると任意のCSRが読み出せるようになって非常に便利です。
例としてNSITEXE NS31のRNMI CSR(mnscratch, mnepc, mncause, mnstatus(※))を読み出してみましょう。
書き起こしておくと0x740 mnscratch, 0x741 mnepc, 0x742 mncause, 0x744 mnstatus です。10進数ですと1856, 1857, 1858, 1860ですね。これらのCSRを読みだそうとしても、OpenOCD側が存在を認識していないためエラーになります。
(gdb) info reg csr1856 Invalid register `csr1856'
CSRを読むためにOpenOCDを書き換えて再ビルドして……では大変すぎます。そんなお困りごとをOpenOCDもわかっていて救済策が用意されています。
riscv expose_csrs 1856-1860
この一行を追加してもう一度試すと、
(gdb) info reg csr1856 csr1857 csr1858 csr1860 csr1856 0x0 0 csr1857 0x180188e 25172110 csr1858 0x80000000 -2147483648 csr1860 0x8 8
無事読み出すことができました。
(※)このレジスタは独自CSRではなくResumable NMIという規格で提案中のレジスタです。が、OpenOCDが未対応という意味では独自レジスタと同じなので、この例で取り上げました。