2009年07月14日

Ubuntuのmingw32のsnprintfのバグ

Ubuntuのmingw32のsnprintfのバグを見つけて修正しました。

どうやら既知の問題だったようですが、もしかしたら役に立つかもしれないのでここにメモを残します。

mingw32とwineについて

Ubuntuのmingw32パッケージはWindows用のクロスコンパイルツールです。このコンパイラを使うとUbuntu上でWindowsアプリをビルドすることができます。

WineはWindowsアプリを動かすための互換レイヤです。全てのWindowsアプリを動かすことができるわけではありませんが、クロスコンパイルしたものをちょっと確認するには重宝します。



$ cat hello.c
#include <stdio.h>

main()
{
  printf("Hello, world\n");
}
$ 
$ gcc -o hello hello.c
$ file hello
hello: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), for 
GNU/Linux 2.6.8, dynamically linked (uses shared libs), not stripped $ ./hello Hello, world $

Ubuntuではmingw32とwineのインストールは以下のコマンドでできます。

$ sudo apt-get install mingw32 wine

さきほどと同じソースファイルをクロスコンパイルしてWindowsの実行ファイルを作ってみます。

$ i586-mingw32msvc-gcc -o hello.exe hello.c
$ file hello.exe
hello.exe: MS-DOS executable PE  for MS Windows (console) Intel 80386 32-bit
$ ./hello.exe
Hello, world
$ 

hello.exe はMS-DOSの実行ファイルですが、WineのおかげでUbuntu上で実行することができました。

mingw32でのsnprintfのバグ

さて、このクロスコンパイラを便利に使っていたのですが、あるプログラムだけどうも動作がおかしくなることに気が付きました。そのプログラムが生成するファイルの内容が壊れているのです。デバッガで丸一日追いかけた結果、どうもsnprintfの動作がおかしいということが判明しました。

問題切り分けのために、単純なsnprintfのテストプログラムを試してみました。

$ cat snprintf0.c
#include <stdio.h>

main()
{
  char message[20];

  snprintf(message, sizeof(message), "%ld", (long)0);
  puts(message);
}

$

このプログラムをコンパイルして動かしてみます。

まずは、普通にgccで。

$ gcc -o snprintf0 snprintf0.c
$ file snprintf0
snprintf0: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), for GNU/Linux
 2.6.8, dynamically linked (uses shared libs), not stripped
$ ./snprintf0
0 
$

正しく0が表示されました。

今度はmingw32でやってみます。

$ i586-mingw32msvc-gcc -o snprintf0.exe snprintf0.c
$ file snprintf0.exe
snprintf0.exe: MS-DOS executable PE  for MS Windows (console) Intel 80386 32-bit
$ ./snprintf0.exe
4831803848261632
$ 

0ではなくて変な数字が表示されてしまいました。

snprintf0.exe を実際にWindowsにコピーして実行してみましたが正しくは実行できませんでした。なのでWineではなくmingwクロスコンパイラの問題だと思われます。

クロスコンパイラで使われるライブラリのソースを取り出してきて、snprintfのところを見てみると、いきなり明らかなバグを見つけてしまいました。

                        case 'l':
                                if (fmt[0] == 'l')
                                  {
                                    fmt++;
                                    len = LEN_LL;
                                  }
                                else
                                  len = LEN_LL;
                                goto fmtloop;

このif文のelseのところはlen = LEN_LL でなくて LEN_L の誤りです。

パッチにすると以下のような感じ。

--- mingw_snprintf.c.org	2009-07-08 17:05:01.000000000 +0900
+++ mingw_snprintf.c	2009-07-08 17:05:24.000000000 +0900
@@ -465,7 +465,7 @@
 				    len = LEN_LL;
 				  }
   				else
-				  len = LEN_LL;
+				  len = LEN_L;
 				goto fmtloop;
 			case 'L':
 				flag_ld++;

修正方法

Ubuntuのソースパッケージを持ってきて、ソースを修正してビルドし、必要なライブラリだけを入れ替えてみたところ問題を解決することができました。

以下に私のやった手順をあげておきます。

まずビルドに必要になるツールをインストールしておきます。

$ sudo apt-get build-dep mingw32-runtime
$ sudo apt-get install fakeroot

テンポラリなディレクトリにソースを展開します。

$ mkdir tmp
$ cd tmp
$ apt-get source mingw32-runtime

ソースパッケージの中のアーカイブを展開します。

$ cd mingw32-runtime-3.13/
$ debian/rules unpack-stamp

ここでソースの修正をします。

$ pushd build_dir/src/mingw-runtime-3.13-20070825-1/mingwex/gdtoa/

  ** エディタでソースを修正する **
  ** 修正点の確認 **
$ diff -u mingw_snprintf.c.org mingw_snprintf.c
--- mingw_snprintf.c.org	2009-07-13 14:16:46.000000000 +0900
+++ mingw_snprintf.c	2009-07-13 14:17:08.000000000 +0900
@@ -465,7 +465,7 @@
 				    len = LEN_LL;
 				  }
   				else
-				  len = LEN_LL;
+				  len = LEN_L;
 				goto fmtloop;
 			case 'L':
 				flag_ld++;
$ 
$ popd

tmp/mingw32-runtime-3.13のディレクトリでビルドします。

$ debian/rules build
$ fakeroot debian/rules install

修正したsnprintfが含まれているライブラリを確認します。

$ cd debian/mingw32-runtime/usr/i586-mingw32msvc/lib
$ i586-mingw32msvc-nm libmingwex.a |grep snprintf
mingw_snprintf.o:
00001d00 T ___mingw_snprintf
00000170 T ___mingw_vsnprintf
00001d00 T _snprintf
00000170 T _vsnprintf

snprintfが含まれているライブラリだけをシステムにコピーします。

$ sudo mv /usr/i586-mingw32msvc/lib/libmingwex.a /usr/i586-mingw32msvc/lib/libmingwex.a.old
$ sudo cp libmingwex.a /usr/i586-mingw32msvc/lib/

さきほどのテストプログラムで確認してみます。

$ cd ~/work
$ i586-mingw32msvc-gcc -o snprintf0.exe snprintf0.c
$ ./snprintf0.exe 
0 
$ 

治りました。

せっかくなのでこの修正のパッチを投げようと思ったのですが、すでに報告されていました。

https://bugs.launchpad.net/ubuntu/+source/mingw32-runtime/+bug/242089

しかし、現在(2009/7/13)のところ、まだこの修正は反映されていません。Ubuntu 9.04でもバグ有のままです。このバグで困っている方の参考になれば幸いです。



トラックバックURL

コメントする

名前
 
  絵文字
 
 
記事検索
最新コメント
アクセスカウンター
  • 今日:
  • 昨日:
  • 累計:

QRコード
QRコード