zigコードから生成したdynamic libraryおよびstatic libraryをCでコンパイルして使う
TL;DR
zig ccを使うと色々と簡単、特にstatic linkをする場合はハマりにくく楽なので、使える場合は使うと良さそうです。
zig cc自体、gccやclangのdrop-in replacementを目的として出来たCコンパイラなので多くの場合はそのままポンと乗せ替えができそうですが、環境によっては色々な事情もあるでしょうし本記事ではgccを使う方法についても記します。
前提
zig init-lib
で吐き出されるコード (src/main.zig
) を使います。
const std = @import("std"); const testing = std.testing; export fn add(a: i32, b: i32) i32 { return a + b; } test "basic add functionality" { try testing.expect(add(3, 7) == 10); }
なお、export
modifierが付いていないとライブラリを経由してCから参照することができません。
One of the primary use cases for Zig is exporting a library with the C ABI for other programming languages to call into. The export keyword in front of functions, variables, and types causes them to be part of the library API:
https://ziglang.org/documentation/0.9.1/#Exporting-a-C-Library
Cのコード (main.c
) は以下の通り。zigで実装しているadd
関数をCコード中で利用します。
#include <stdio.h> #include <stdint.h> int32_t add(int32_t a, int32_t b); int main() { printf("Added: %d\n", add(123, 321)); return 0; }
zigとCの型の対応については以下のドキュメントを参照すると良いでしょう。
Documentation - The Zig Programming Language
なお試した環境の各種バージョンは以下の通りです。
$ uname -srvmo Linux 5.15.0-47-generic #51-Ubuntu SMP Thu Aug 11 07:51:15 UTC 2022 x86_64 GNU/Linux $ zig version 0.9.1 $ gcc --version gcc (Ubuntu 11.2.0-19ubuntu1) 11.2.0 Copyright (C) 2021 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
Dynamic Link
これは至って普通です。
zigのコードをsoファイルのdynamic libraryにするには、以下のようにzig build-lib
を実行すると良いです。-target
に何を指定できるのかについてはzig targets
コマンドの結果を参照すると良いでしょう。色々とクロスビルドできて便利ですね。
$ zig build-lib -dynamic -target x86_64-linux-gnu src/main.zig
すると、libmain.so
というファイルが出力されるので
$ gcc -L . main.c -lmain
このようにgccでdynamic linkすると、
$ LD_LIBRARY_PATH=. ./a.out Added: 444
このように実行することができます。
なおzig ccを使う場合は"gcc"の部分を"zig cc"に書き替えるだけで動きます:
$ zig cc -L . main.c -lmain
ちなみにzig ccを使ってコンパイルしたバイナリを実行する場合はLD_LIBRARY_PATH
を指定する必要が無くなるのですが、gccでも同じことをしたい場合は-Wl,-rpath,.
みたいな感じでオプションを指定してRPATH
にライブラリの検索パスを追加してあげると良いでしょう *1。
Static Link
static linkする場合はいろいろとzig側にオプションが必要になってきます。
$ zig build-lib -static -fPIC -fcompiler-rt -target x86_64-linux-musl src/main.zig
このようにするとlibmain.a
というstatic libraryが生成されます。
今回はstatic linkをするので、glibcの代わりにmuslを使います *2。
-fcompiler-rt
はcompiler-rtのシンボル情報を出力に含めるためのオプションです。これが無いと、static linkをする際にzigランタイムの持つシンボルを解決できず、Cコンパイラがコンパイルできなくなります。
そしてmuslを使う場合は-fPIC
オプションにより位置独立コードとしてライブラリを出力する必要があります *3。
そしてgcc側では
$ gcc -L . -static main.c -lmain
としてコンパイルするとstatic linkされた実行形式が得られることとなります *4。
なお、gcc (あるいは別のCコンパイラ) の代わりにzig ccを使う場合はbuild-lib
のオプションは簡略化することができます。
$ zig build-lib -static -target x86_64-linux-musl src/main.zig $ zig cc -L . -static main.c -lmain
まとめ
- zigのコードからCのdynamic libraryおよびstatic libraryを作成してCに組み込むことができた
- Cで書くにはしんどいコードをzigで書いて組み込む、みたいなadaptiveな使い方が比較的簡単にできそう
- zig ccは便利