MSYS2 による gcc 開発環境の構築（2）

[前回](http://qiita.com/spiegel-im-spiegel/items/ba4e8d2418bdfe0c8049)は [MSYS2] で [MinGW-w64] の環境を入れるところで終わってしまい，（その後仕事が忙しくなったこともあり）そのまま投げっぱなしの状態だったが，とりあえず gcc の動作確認まで終わらせておく。そうそう， `pacman -Syu` で最新版に更新するのを忘れないように。

今回のターゲットは [pgpdump]。

[pgpdump] は OpenPGP パケットを可視化してくれる（ただし復号は行わない）便利ツールだ。ソースコードのみの提供でバイナリは提供されてない（ただし Linux ディストリビュータによってはバイナリを提供している場合がある）。ビルドすれば Windows 環境でも動作可能。

ソースコードは GitHub で提供されているので，あらかじめ `git clone` で取得しておく。

- [kazu-yamamoto/pgpdump](https://github.com/kazu-yamamoto/pgpdump)

## Win32 版のビルド

まずは `mingw32_shell.bat` を起動して Win32 の環境を立ち上げる（`mingw32_shell.bat` って何？ ってかたは[前回の記事](http://qiita.com/spiegel-im-spiegel/items/ba4e8d2418bdfe0c8049)を参照あれ）。

まずは `configure` を走らせて環境を確認する。

```shell
$ ./configure
checking for gcc... gcc
checking whether the C compiler works... yes
checking for C compiler default output file name... a.exe
checking for suffix of executables... .exe
checking whether we are cross compiling... no
checking for suffix of object files... o
checking whether we are using the GNU C compiler... yes
checking whether gcc accepts -g... yes
checking for gcc option to accept ISO C89... none needed
checking for inflate in -lz... yes
checking for BZ2_bzBuffToBuffDecompress in -lbz2... yes
checking how to run the C preprocessor... gcc -E
checking for grep that handles long lines and -e... /usr/bin/grep
checking for egrep... /usr/bin/grep -E
checking for ANSI C header files... yes
checking for sys/types.h... yes
checking for sys/stat.h... yes
checking for stdlib.h... yes
checking for string.h... yes
checking for memory.h... yes
checking for strings.h... yes
checking for inttypes.h... yes
checking for stdint.h... yes
checking for unistd.h... yes
checking for unistd.h... (cached) yes
checking sys/time.h usability... yes
checking sys/time.h presence... yes
checking for sys/time.h... yes
checking unixlib/local.h usability... no
checking unixlib/local.h presence... no
checking for unixlib/local.h... no
checking whether time.h and sys/time.h may both be included... yes
checking whether struct tm is in sys/time.h or time.h... time.h
checking for struct tm.tm_zone... no
checking whether tzname is declared... yes
checking for tzname... yes
configure: creating ./config.status
config.status: creating Makefile
config.status: WARNING:  'Makefile.in' seems to ignore the --datarootdir setting
config.status: creating config.h
```

特にエラーもなく終了。

[pgpdump] では，パケット内の圧縮データを扱うため， `libz` および `libbz2` が必要となる。昔の MinGW32 ではこれらのライブラリを個別にインストールする必要があったが，最近のはあらかじめ開発環境に入ってるので助かる。

これで生成される `Makefile` は以下のとおり。

```makefile:Makefile
prefix = /usr/local
exec_prefix = ${prefix}
bindir = ${exec_prefix}/bin
mandir = ${prefix}/share/man
LIBS = -lbz2 -lz
CFLAGS  = -g -O2 -O -Wall
LDFLAGS =
VERSION = `git tag | tail -1 | sed -e 's/v//'`

RM = rm -f
INSTALL  = install

INCS = pgpdump.h
SRCS = pgpdump.c types.c tagfuncs.c packet.c subfunc.c signature.c keys.c \
       buffer.c uatfunc.c
OBJS = pgpdump.o types.o tagfuncs.o packet.o subfunc.o signature.o keys.o \
       buffer.o uatfunc.o
PROG = pgpdump

MAN  = pgpdump.1

CNF = config.h config.status config.cache config.log
MKF = Makefile

.c.o:
	$(CC) -c $(CFLAGS) $<

all: $(PROG)

$(PROG): $(OBJS)
	$(CC) $(CFLAGS) -o $(PROG) $(OBJS) $(LIBS) $(LDFLAGS)

clean:
	$(RM) $(OBJS) $(PROG)

distclean:
	$(RM) $(OBJS) $(PROG) $(CNF) $(MKF)

install: all
	$(INSTALL) -d $(DESTDIR)$(bindir)
	$(INSTALL) -cp -pm755 $(PROG) $(DESTDIR)$(bindir)
	$(INSTALL) -d $(DESTDIR)$(mandir)/man1
	$(INSTALL) -cp -pm644 $(MAN) $(DESTDIR)$(mandir)/man1

archive:
	git archive master -o ~/pgpdump-$(VERSION).tar --prefix=pgpdump-$(VERSION)/
	gzip ~/pgpdump-$(VERSION).tar
```

この時点での問題は3つ。

1. `prefix` が `/usr/local` になっている。このままでもエラーにはならないが，今回は Win32 版と Win64 版を分けたいので `/mingw32` としたい
1. `CC` が明示されないのでこのまま `mingw32-make` を走らせると `cc` コマンドを呼びだそうとする
1. リンク時のオプション（`LDFLAGS`）に `-static` がないため，このままビルドすると DLL に依存する構成になってしまう

このうち2番目の問題は少し厄介だが

1. Malefile.in に `CC = @CC@` の記述を追加して `configure` しなおす
1. `mingw32-make` 起動時の引数に `CC=gcc` を追加する
1. 環境変数に `CC=gcc` を追加する

のいずれかで対処できる。

1番目と3番目は `configure` コマンドに情報を渡せばよい。

```shell
$ ./configure --prefix=/mingw32 LDFLAGS=-static
```

では，作りなおした `Makefile` でビルドする。

```shell
$ mingw32-make
gcc -c -g -O2 -O -Wall pgpdump.c
gcc -c -g -O2 -O -Wall types.c
gcc -c -g -O2 -O -Wall tagfuncs.c
gcc -c -g -O2 -O -Wall packet.c
gcc -c -g -O2 -O -Wall subfunc.c
gcc -c -g -O2 -O -Wall signature.c
gcc -c -g -O2 -O -Wall keys.c
gcc -c -g -O2 -O -Wall buffer.c
gcc -c -g -O2 -O -Wall uatfunc.c
gcc -g -O2 -O -Wall -o pgpdump pgpdump.o types.o tagfuncs.o packet.o subfunc.o signature.o keys.o buffer.o uatfunc.o -lbz2 -lz  -static

$ strip pgpdump.exe
```

## Win64 版のビルド

次は Win64 版。 `mingw64_shell.bat` を起動して Win64 の環境を立ち上げる。 Win32 版で要領は分かったので一気に終わらせてしまおう。

```shell
$ ./configure --prefix=/mingw64 LDFLAGS=-static
checking for gcc... gcc
checking whether the C compiler works... yes
checking for C compiler default output file name... a.exe
checking for suffix of executables... .exe
checking whether we are cross compiling... no
checking for suffix of object files... o
checking whether we are using the GNU C compiler... yes
checking whether gcc accepts -g... yes
checking for gcc option to accept ISO C89... none needed
checking for inflate in -lz... yes
checking for BZ2_bzBuffToBuffDecompress in -lbz2... yes
checking how to run the C preprocessor... gcc -E
checking for grep that handles long lines and -e... /usr/bin/grep
checking for egrep... /usr/bin/grep -E
checking for ANSI C header files... yes
checking for sys/types.h... yes
checking for sys/stat.h... yes
checking for stdlib.h... yes
checking for string.h... yes
checking for memory.h... yes
checking for strings.h... yes
checking for inttypes.h... yes
checking for stdint.h... yes
checking for unistd.h... yes
checking for unistd.h... (cached) yes
checking sys/time.h usability... yes
checking sys/time.h presence... yes
checking for sys/time.h... yes
checking unixlib/local.h usability... no
checking unixlib/local.h presence... no
checking for unixlib/local.h... no
checking whether time.h and sys/time.h may both be included... yes
checking whether struct tm is in sys/time.h or time.h... time.h
checking for struct tm.tm_zone... no
checking whether tzname is declared... yes
checking for tzname... yes
configure: creating ./config.status
config.status: creating Makefile
config.status: WARNING:  'Makefile.in' seems to ignore the --datarootdir setting
config.status: creating config.h

$ mingw32-make.exe
gcc -c -g -O2 -O -Wall pgpdump.c
gcc -c -g -O2 -O -Wall types.c
gcc -c -g -O2 -O -Wall tagfuncs.c
gcc -c -g -O2 -O -Wall packet.c
gcc -c -g -O2 -O -Wall subfunc.c
gcc -c -g -O2 -O -Wall signature.c
gcc -c -g -O2 -O -Wall keys.c
gcc -c -g -O2 -O -Wall buffer.c
gcc -c -g -O2 -O -Wall uatfunc.c
gcc -g -O2 -O -Wall -o pgpdump pgpdump.o types.o tagfuncs.o packet.o subfunc.o signature.o keys.o buffer.o uatfunc.o -lbz2 -lz  -static

$ strip pgpdump.exe
```

## 動作確認

[pgpdump] の動作確認のために [JPCERT/CC の公開鍵](https://www.jpcert.or.jp/jpcert-pgp.html)をダンプしてみる。 Windows 環境で問題なく動くことを確かめるためにコマンドプロンプトから起動する。

```shell
C:>pgpdump info-0x69ECE048.asc
Old: Public Key Packet(tag 6)(269 bytes)
        Ver 4 - new
        Public key creation time - Tue Jun 02 14:43:57 東京 (標準時) 2009
        Pub alg - RSA Encrypt or Sign(pub 1)
        RSA n(2048 bits) - ...
        RSA e(17 bits) - ...
Old: User ID Packet(tag 13)(29 bytes)
        User ID - JPCERT/CC <info@jpcert.or.jp>
Old: Signature Packet(tag 2)(316 bytes)
        Ver 4 - new
        Sig type - Generic certification of a User ID and Public Key packet(0x10).
        Pub alg - RSA Encrypt or Sign(pub 1)
        Hash alg - SHA1(hash 2)
        Hashed Sub: preferred symmetric algorithms(sub 11)(3 bytes)
                Sym alg - AES with 256-bit key(sym 9)
                Sym alg - CAST5(sym 3)
                Sym alg - Triple-DES(sym 2)
        Hashed Sub: key server preferences(sub 23)(4 bytes)
                Flag - No-modify
        Hashed Sub: key flags(sub 27)(4 bytes)
                Flag - This key may be used to certify other keys
                Flag - This key may be used to sign data
                Flag - This key may be used to encrypt communications
                Flag - This key may be used to encrypt storage
                Flag - The private component of this key may be in the possession of more than one person
        Hashed Sub: preferred compression algorithms(sub 22)(2 bytes)
                Comp alg - ZLIB <RFC1950>(comp 2)
                Comp alg - ZIP <RFC1951>(comp 1)
        Hashed Sub: features(sub 30)(4 bytes)
                Flag - Modification detection (packets 18 and 19)
        Hashed Sub: preferred hash algorithms(sub 21)(3 bytes)
                Hash alg - SHA256(hash 8)
                Hash alg - SHA384(hash 9)
                Hash alg - SHA512(hash 10)
        Hashed Sub: signature creation time(sub 2)(4 bytes)
                Time - Tue Jun 16 12:51:22 東京 (標準時) 2009
        Sub: issuer key ID(sub 16)(8 bytes)
                Key ID - 0x317D97A469ECE048
        Hash left 2 bytes - cd 79
        RSA m^d mod n(2047 bits) - ...
                -> PKCS-1
Old: Signature Packet(tag 2)(277 bytes)
        Ver 3 - old
        Hash material(5 bytes):
                Sig type - Generic certification of a User ID and Public Key packet(0x10).
                Creation time - Tue Jun 02 14:43:57 東京 (標準時) 2009
        Key ID - 0xE7734FA60C7BDE12
        Pub alg - RSA Encrypt or Sign(pub 1)
        Hash alg - SHA1(hash 2)
        Hash left 2 bytes - e9 53
        RSA m^d mod n(2047 bits) - ...
                -> PKCS-1
Old: Signature Packet(tag 2)(156 bytes)
        Ver 4 - new
        Sig type - Generic certification of a User ID and Public Key packet(0x10).
        Pub alg - RSA Encrypt or Sign(pub 1)
        Hash alg - SHA1(hash 2)
        Hashed Sub: signature creation time(sub 2)(4 bytes)
                Time - Mon Jun 15 14:51:27 東京 (標準時) 2009
        Sub: issuer key ID(sub 16)(8 bytes)
                Key ID - 0x8C756B2E2C94D4ED
        Hash left 2 bytes - 35 fd
        RSA m^d mod n(1022 bits) - ...
                -> PKCS-1
Old: Public Subkey Packet(tag 14)(269 bytes)
        Ver 4 - new
        Public key creation time - Tue Jun 02 14:43:57 東京 (標準時) 2009
        Pub alg - RSA Encrypt or Sign(pub 1)
        RSA n(2048 bits) - ...
        RSA e(17 bits) - ...
Old: Signature Packet(tag 2)(577 bytes)
        Ver 4 - new
        Sig type - Subkey Binding Signature(0x18).
        Pub alg - RSA Encrypt or Sign(pub 1)
        Hash alg - SHA1(hash 2)
        Hashed Sub: signature creation time(sub 2)(4 bytes)
                Time - Tue Jun 02 14:43:58 東京 (標準時) 2009
        Hashed Sub: key flags(sub 27)(4 bytes)
                Flag - This key may be used to encrypt communications
                Flag - This key may be used to encrypt storage
        Hashed Sub: embedded signature(sub 32)(284 bytes)
        Ver 4 - new
        Sig type - Primary Key Binding Signature(0x19).
        Pub alg - RSA Encrypt or Sign(pub 1)
        Hash alg - SHA256(hash 8)
        Hashed Sub: signature creation time(sub 2)(4 bytes)
                Time - Tue Jun 02 14:43:57 東京 (標準時) 2009
        Sub: issuer key ID(sub 16)(8 bytes)
                Key ID - 0x09D704B753BA1622
        Hash left 2 bytes - 71 2d
        RSA m^d mod n(2048 bits) - ...
                -> PKCS-1
        Sub: issuer key ID(sub 16)(8 bytes)
                Key ID - 0x317D97A469ECE048
        Hash left 2 bytes - 1d e2
        RSA m^d mod n(2046 bits) - ...
                -> PKCS-1
```

うむ，問題ないようだな。ちなみに Windows バイナリは[うちのサイトでも公開](http://www.baldanders.info/spiegel/archive/pgpdump/)しているのでご自由にどうぞ。

[MSYS2]: https://github.com/msys2 "msys2"
[MinGW-w64]: http://mingw-w64.org/ "Mingw-w64 - GCC for Windows 64 & 32 bits [mingw-w64]"
[pgpdump]: http://www.mew.org/~kazu/proj/pgpdump/en/ "pgpdump"