目次: 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が扱いきれないレベルの巨大配列をソースコードに書くこと自体、設計が間違っていると思うので、処理速度や巨大ファイルの扱いはあまり気にしないことにします。
 この記事にコメントする
 この記事にコメントする
目次: 自宅サーバー
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 パッケージリストを読み込んでいます... 完了 パッケージリストを読み込んでいます... 完了 依存関係ツリーを作成しています... 完了 状態情報を読み取っています... 完了 アップグレードパッケージを検出しています... 完了 (以下略)
今度はうまくいきました。先ほどとの違いは良くわかりませんが、めげずに何回かやった方が良いんでしょうか。
 この記事にコメントする
 この記事にコメントする
| < | 2023 | > | ||||
| << | < | 06 | > | >> | ||
| 日 | 月 | 火 | 水 | 木 | 金 | 土 | 
| - | - | - | - | 1 | 2 | 3 | 
| 4 | 5 | 6 | 7 | 8 | 9 | 10 | 
| 11 | 12 | 13 | 14 | 15 | 16 | 17 | 
| 18 | 19 | 20 | 21 | 22 | 23 | 24 | 
| 25 | 26 | 27 | 28 | 29 | 30 | - | 
 wiki
 wiki Linux JM
 Linux JM Java API
 Java API 2002年
 2002年 2003年
 2003年 2004年
 2004年 2005年
 2005年 2006年
 2006年 2007年
 2007年 2008年
 2008年 2009年
 2009年 2010年
 2010年 2011年
 2011年 2012年
 2012年 2013年
 2013年 2014年
 2014年 2015年
 2015年 2016年
 2016年 2017年
 2017年 2018年
 2018年 2019年
 2019年 2020年
 2020年 2021年
 2021年 2022年
 2022年 2023年
 2023年 2024年
 2024年 2025年
 2025年 過去日記について
 過去日記について アクセス統計
 アクセス統計 サーバ一覧
 サーバ一覧 サイトの情報
 サイトの情報合計: 
本日: