コグノスケ


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

link もっと前
2023年6月18日 >>> 2023年6月9日
link もっと後

2023年6月16日

cmakeでバイナリをC言語の配列に変換する

目次: C言語とlibc

組み込みソフトなどでバイナリデータをC言語の配列として記述したいときがあります。毎回手で変換するのは面倒ですし、スクリプトや実行バイナリだと移植が面倒(特にWindows)で、意外と悩ましいです。

もしcmakeだけで実装できれば移植の心配はなくなる(cmakeが対応しているプラットフォームに限る)のでは?と思い、試しに作ってみました。変換の本体は下記の関数です。

バイナリからC言語の配列へ変換する関数

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(READ ... HEX): バイナリを16進数変換して読み出し
  • string() 辺り: 16バイトごとに改行を入れ、C言語の記述方式に変換
  • set() 辺り: includeや変数名を追加してファイルに書き出し

なお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(配列本体)という名前にします。

動作確認用のコードを作ろう

これだけだと合っているか間違っているか確認しづらいので、動作確認用に簡単なソースコード一式を作りましょう。

動作確認用のCMakeLists.txt

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は下記の通りです。

動作確認用のmain.c

#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;
}
動作確認用のtest.bin
$ 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の出力と比較します。

実行結果とhexdump -Cとの比較
$ ./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が扱いきれないレベルの巨大配列をソースコードに書くこと自体、設計が間違っていると思うので、処理速度や巨大ファイルの扱いはあまり気にしないことにします。

編集者:すずき(2023/06/20 14:23)

コメント一覧

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



2023年6月13日

DebianのUpgrade

目次: 自宅サーバー

Debianの更新はとても簡単でありがたいですが、セキュリティ強化やらなんやらで毎回やり方が変わりますね。11.7 (bullseye) から12.0 (bookworm) にバージョンアップする方法をメモしておきます。

まずaptのリポジトリ設定はstable指定とします。コードネーム指定は使ったことないので知らないです、たぶん書き換える必要があるはず。単にapt-get updateとdist-upgradeを実行すると下記のように怒られます。

InReleaseが変わっていると怒られる
# 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というオプションがあるようです。悲しいことにまだ和訳がなくて英語のままでした……。

apt-getのヘルプ
       --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をしたせい?なのか、取得にならずヒット(既に更新済み)になっています。気にしないでください。

InReleaseの更新
# 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.

あれ?ダメですね、まだ怒っています。もう一回やってみましょうか。

InReleaseの更新、再び
# 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
パッケージリストを読み込んでいます... 完了
パッケージリストを読み込んでいます... 完了
依存関係ツリーを作成しています... 完了
状態情報を読み取っています... 完了
アップグレードパッケージを検出しています... 完了
(以下略)

今度はうまくいきました。先ほどとの違いは良くわかりませんが、めげずに何回かやった方が良いんでしょうか。

編集者:すずき(2024/01/13 14:29)

コメント一覧

  • hdkさん(2023/06/13 22:57)
    リリースノートのアップグレード手順を見ると時々いろいろとこわいことが書かれているので(特にsystemdが導入された時など)、間違えてアップグレードしないために、stableじゃなくてコードネーム指定で使っています。
  • すずきさん(2023/06/14 14:23)
    私はあまりカスタマイズしていない(SSH, SambaとWebサーバーくらいしか使っていない)のもあって、ぶっ飛んだら直すのさ〜と割と気楽にアップグレードしてしまいます。
open/close この記事にコメントする



link もっと前
2023年6月18日 >>> 2023年6月9日
link もっと後

管理用メニュー

link 記事を新規作成

<2023>
<<<06>>>
----123
45678910
11121314151617
18192021222324
252627282930-

最近のコメント5件

  • link 21年3月13日
    すずきさん (03/05 15:13)
    「あー、このプログラムがまずいんですね。ご...」
  • link 21年3月13日
    emkさん (03/05 12:44)
    「キャストでvolatileを外してアクセ...」
  • link 24年1月24日
    すずきさん (02/19 18:37)
    「簡単にできる方法はPowerShellの...」
  • link 24年1月24日
    KKKさん (02/19 02:30)
    「追伸です。\nネットで調べたらマイクロソ...」
  • link 24年1月24日
    KKKさん (02/19 02:25)
    「私もエラーで困ってます\n手動での回復パ...」

最近の記事3件

  • link 24年3月25日
    すずき (03/26 03:20)
    「[Might and Magic Book One TASのその後] 目次: Might and Magicファミコン版以前(...」
  • link 21年10月4日
    すずき (03/26 03:14)
    「[Might and Magicファミコン版 - まとめリンク] 目次: Might and Magicファミコン版TASに挑...」
  • link 24年3月19日
    すずき (03/20 02:52)
    「[モジュラージャックの規格] 古くは電話線で、今だとEthernetで良く見かけるモジュラージャックというコネクタとレセプタク...」
link もっとみる

こんてんつ

open/close wiki
open/close Linux JM
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 2021年
open/close 2022年
open/close 2023年
open/close 2024年
open/close 過去日記について

その他の情報

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

合計:  counter total
本日:  counter today

link About www.katsuster.net
RDFファイル RSS 1.0

最終更新: 03/26 03:20