Boehm GC を Windows Mobile 6 用にコンパイル

C++ 用のガベージコレクタ Boehm GC を WM6 用にコンパイルします。
(WM6 エミュレータにコンソールをインストールする必要があります: 昨日の記事参照)

(修正: CE_MAKEFILE で、gc.lib とヘッダファイルのインストール先、それにプロジェクトの追加のインストール先を修正。Arm4バイナリを VC のライブラリに突っ込んじゃいかんぞな)

(あとやっぱり Win32 でのテスト結果と比較すると、WinCE 版のテストでスタックオーバーフローが起こるのはおかしい)

ソースの入手

http://www.hpl.hp.com/personal/Hans_Boehm/gc/gc_source/ から、gc-7.1.tar.gz を落とします。

下準備

「続きを読む」を開くとファイルが見られます。
ソースを展開したディレクトリに、CE_MAKEFILEwince.mak をコピーします。
さらに、CE_MAKEFILEMAKEFILE にコピーします。

次に、ソースにパッチを当てます。
(patch が必要です。うちでは MSYS に含まれる patch を使いました)

ソースを展開したディレクトリに、gc.patch, include.patch, tests.patch の3ファイル(続きを読む参照)をコピーします。

以下のコマンドを順に実行します。

> patch < gc.patch
> cd include
> patch < ..\include.patch
> cd ../tests
> patch < ..\tests.patch
gc.lib のコンパイルとインストール

nmake を実行します。
オプションをつけないとデバッグ版になります。

リリース版をコンパイルする場合は

> nmake NODEBUG=1

としてください。

コンパイルに成功したらインストールです。

> nmake install

$(VCINSTALLDIR)/lib に gc.lib が、$(VCINSTALLDIR)/include/gc に include 以下のヘッダファイルがコピーされます。

テストプログラムをエミュレータで動かす

VS2005 を起動して、新しいプロジェクトを作成します。
プロジェクトの種類は Visual C++ - スマート デバイス で、テンプレートは Win32 スマート デバイス プロジェクト、ソリューション名は BoehmGCTest、プロジェクト名は test とします。

プラットフォームは Windows Mobile 6 Professional、アプリケーションの種類は Windows アプリケーション、追加のオプションは 空のプロジェクト です。

出来上がったディレクトリ(通常 マイドキュメント\Visual Studio 2005\Projects\BoehmGCTest)に、gc/tests の内容をすべてコピーします。

test_cc

test のソースファイルに既存の項目として ../test_cpp.cc を追加します。

testプロジェクトのプロパティを開き、

  • 構成プロパティ - C/C++ - 全般 の「追加のインクルードディレクトリ」として C:\Program Files\Windows Mobile 6 SDK\PocketPC\Include\gc
  • 構成プロパティ - リンカ - 入力 の「追加の依存ファイル」として gc.lib

ビルドして、実行

エミュレータでプログラムが実行され、最後に Visual Studio の出力画面に「プログラム '[b797c492] test_cpp.exe' はコード 0 (0x0) で終了しました。」と表示されれば(とりあえず)OK。

test

ファイル - 追加 - 新しいプロジェクト で、プロジェクト名 test を作成します。
(プラットフォームとアプリケーションの設定については test_cpp と同様)
ソース ファイルに test.c を追加します。

test_cpp と同じように構成プロパティを設定して、デバッグモードで実行すると、2回失敗が起きて(デバッガで続行すると)正常終了します。

失敗の原因が、テストプログラムのバグかライブラリのコンパイルミスかは不明。

終了時にコンソールが消えるので、1415行の return 0; にブレークポイントを仕掛けておくのが吉です。

他のテストプログラムについて

まだなんにもしていません(^^;)>

走らせる場合は、プログラムの適切な場所に

#ifdef WINCE
#	define GC_printf printf
#endif

以上の3行を挿入し、WinMain の前の #ifdef 文

#ifdef MSWIN32
  int APIENTRY WinMain(
      HINSTANCE instance, HINSTANCE prev, LPSTR cmd, int cmdShow ) 

を以下のように直せば、動くようです。

#if defined(MSWIN32) || defined(MSWINCE)

ただし、コマンドライン用のテストプログラムをそのまま流用しているので、テスト実行時には Visual Studioデバッグモードを使い、WinMain の最後の return 0; にブレークポイントを仕掛けておく必要があります。
でないと、せっかくの printf 出力がプログラム終了と同時に蒸発します。

せっかく VC2005 なんだから、可変長引数マクロで

#	define GC_printf(fmt, ...) NKDbgPrintfW( TEXT(fmt), __VA_ARGS__ );

と決めたかったけれど、エラー連発であきらめた。
UNICODE関係で文字列の型がむちゃくちゃになるのです。

メイクファイル

CE_MAKEFILE
# Makefile for Windows NT.  Assumes Microsoft compiler, and a single thread.
# DLLs are included in the root set under NT, but not under win32S.
# Use "nmake nodebug=1 all" for optimized versions of library, gctest and editor.

MY_CPU=ARM
CPU=$(MY_CPU)
!include <wince.mak>

.SUFFIXES :
.SUFFIXES : .obj .asm .c .cpp .cxx .f .f90 .for .rc

WM6SDK_DIR = C:\Program Files\Windows Mobile 6 SDK\PocketPC

OBJS= alloc.obj reclaim.obj allchblk.obj misc.obj mach_dep.obj os_dep.obj mark_rts.obj headers.obj mark.obj obj_map.obj blacklst.obj finalize.obj new_hblk.obj dbg_mlc.obj malloc.obj stubborn.obj dyn_load.obj typd_mlc.obj ptr_chck.obj gc_cpp.obj mallocx.obj

all: gc.lib

.c.obj:
	$(cc) $(cdebug) $(cflags) $(cvars) -Iinclude -DALL_INTERIOR_POINTERS -D__STDC__ -DGC_NOT_DLL -DGC_BUILD $*.c /Fo$*.obj

.cpp.obj:
	$(cc) $(cdebug) $(cflags) $(cvars) -Iinclude -DALL_INTERIOR_POINTERS -DGC_NOT_DLL -DGC_BUILD $*.CPP /Fo$*.obj

gc.lib: $(OBJS)
	$(lib) /MACHINE:$(MY_CPU) /out:gc.lib $(OBJS)

gc_cpp.obj: include\gc_cpp.h include\gc.h gc_cpp.cpp

gc_cpp.cpp: gc_cpp.cc
	copy gc_cpp.cc gc_cpp.cpp

clean:
	-del *.exe *.obj *.lib *.pdb gc_cpp.cpp

install: gc.lib
	xcopy /D /Y gc.lib "$(WM6SDK_DIR)\Lib"
	xcopy /D /Y include "$(WM6SDK_DIR)\Include\gc"
wince.mak
bin = "C:\Program Files\Microsoft Visual Studio 8\VC\ce\bin\x86_arm"
cc     = $(bin)\cl
link   = $(bin)\link
lib = $(bin)\lib

cwinflags = /D "_WIN32_WCE=0x502" /D "WINVER=0x502" /D "UNDER_CE" /D "WIN32_PLATFORM_PSPC" /D "WINCE"  /D "ARM" /D "_ARM_" /D "_UNICODE"  /D "UNICODE" /D "POCKETPC2003_UI_MODEL" /D "OLD_WIN32_LOG_FILE" /I"C:\Program Files\Windows Mobile 6 SDK\PocketPC\Include\Armv4i"
cmtflags = /D_MC 
cflags = -c $(cwinflags) -nologo -GS $(cmtflags)# /showIncludes 

!IFDEF NODEBUG
cdebug = -Ox -DNDEBUG
!ELSE
cdebug = -Zi -Od -DDEBUG
!ENDIF


lwinflags =/subsystem:windowsce,5.02 \
/LIBPATH:"C:\Program Files\Windows Mobile 6 SDK\PocketPC\Lib\Armv4i" \
/LIBPATH:"C:\Program Files\Microsoft Visual Studio 8\VC\ce\lib\armv4i" \
/LIBPATH:"C:\Program Files\Microsoft Visual Studio 8\VC\ce\atlmfc\lib\armv4i"

libs = /nodefaultlib:coredll.lib /nodefaultlib:corelibc.lib 

guiflags = -debugtype:cv  /INCREMENTAL:NO /NOLOGO $(lwinflags) -stack:16384 -out:test_cpp.exe gc.lib $(libs)

パッチファイル

gc.patch
*** gc-7.1/dbg_mlc.c	Tue Aug 14 06:20:28 2007
--- TestGC2/dbg_mlc.c	Fri Oct  2 17:12:40 2009
***************
*** 641,647 ****
--- 641,649 ----
      if (str == NULL) return NULL;
      copy = GC_debug_malloc_atomic(strlen(str) + 1, OPT_RA s, i);
      if (copy == NULL) {
+ #ifndef MSWINCE
        errno = ENOMEM;
+ #endif
        return NULL;
      }
      strcpy(copy, str);
*** gc-7.1/gc_cpp.cc	Wed Dec 19 08:25:48 2007
--- TestGC2/gc_cpp.cc	Tue Oct  6 17:38:05 2009
***************
*** 23,29 ****
  
  **************************************************************************/
  
! #include <gc_cpp.h>
  
  void* operator new( size_t size ) {
      return GC_MALLOC_UNCOLLECTABLE( size );}
--- 23,29 ----
  
  **************************************************************************/
  
! #include "gc_cpp.h"
  
  void* operator new( size_t size ) {
      return GC_MALLOC_UNCOLLECTABLE( size );}
*** gc-7.1/malloc.c	Mon Mar 10 14:33:41 2008
--- TestGC2/malloc.c	Fri Oct  2 17:13:23 2009
***************
*** 244,250 ****
--- 244,252 ----
  
    if (s == NULL) return NULL;
    if ((copy = GC_malloc_atomic(strlen(s) + 1)) == NULL) {
+ #ifndef MSWINCE
      errno = ENOMEM;
+ #endif
      return NULL;
    }
    strcpy(copy, s);
*** gc-7.1/os_dep.c	Sat Mar  1 04:01:28 2008
--- TestGC2/os_dep.c	Tue Oct  6 17:28:03 2009
***************
*** 1456,1461 ****
--- 1456,1464 ----
      return p;
    }
  # endif
+ # ifdef MSWINCE
+   GC_bool GC_wnt = FALSE;
+ # endif
  
  # ifndef REDIRECT_MALLOC
    /* We maintain a linked list of AllocationBase values that we know	*/
include.patch
Common subdirectories: gc-7.1/include/extra and TestGC2/include/extra
diff -c gc-7.1/include/gc_config_macros.h TestGC2/include/gc_config_macros.h
*** gc-7.1/include/gc_config_macros.h	Tue Jul  3 03:21:33 2007
--- TestGC2/include/gc_config_macros.h	Fri Oct  2 18:09:06 2009
***************
*** 142,148 ****
  # else /* ! _WIN32_WCE */
  /* Yet more kluges for WinCE */
  #   include <stdlib.h>		/* size_t is defined here */
!     typedef long ptrdiff_t;	/* ptrdiff_t is not defined */
  # endif
  
  #if defined(_DLL) && !defined(GC_NOT_DLL) && !defined(GC_DLL)
--- 142,151 ----
  # else /* ! _WIN32_WCE */
  /* Yet more kluges for WinCE */
  #   include <stdlib.h>		/* size_t is defined here */
! #   ifndef _PTRDIFF_T_DEFINED
!       typedef long ptrdiff_t;	/* ptrdiff_t is not defined */
! #     define _PTRDIFF_T_DEFINED
! #   endif
  # endif
  
  #if defined(_DLL) && !defined(GC_NOT_DLL) && !defined(GC_DLL)
Common subdirectories: gc-7.1/include/private and TestGC2/include/private
tests.patch
diff -c gc-7.1/tests/test.c TestGC2/tests/test.c
*** gc-7.1/tests/test.c	Mon Feb 25 05:15:28 2008
--- TestGC2/tests/test.c	Tue Oct  6 18:38:18 2009
***************
*** 75,80 ****
--- 75,85 ----
  #  define GC_COND_INIT()
  #endif
  
+ #ifdef WINCE
+ #	define GC_printf printf
+ #endif
+ 
+ 
  /* Allocation Statistics */
  int stubborn_count = 0;
  int uncollectable_count = 0;
***************
*** 1344,1350 ****
  #if !defined(PCR) \
      && !defined(GC_WIN32_THREADS) && !defined(GC_PTHREADS) \
      || defined(LINT)
! #if defined(MSWIN32) && !defined(__MINGW32__)
    int APIENTRY WinMain(HINSTANCE instance, HINSTANCE prev, LPTSTR cmd, int n)
  #else
    int main()
--- 1349,1355 ----
  #if !defined(PCR) \
      && !defined(GC_WIN32_THREADS) && !defined(GC_PTHREADS) \
      || defined(LINT)
! #if defined(MSWINCE) || defined(MSWIN32) && !defined(__MINGW32__)
    int APIENTRY WinMain(HINSTANCE instance, HINSTANCE prev, LPTSTR cmd, int n)
  #else
    int main()
Only in TestGC2/tests: test.obj
diff -c gc-7.1/tests/test_cpp.cc TestGC2/tests/test_cpp.cc
*** gc-7.1/tests/test_cpp.cc	Thu Jun  7 08:32:19 2007
--- TestGC2/tests/test_cpp.cc	Tue Oct  6 17:57:57 2009
***************
*** 53,58 ****
--- 53,62 ----
  #   define USE_GC GC
  #endif
  
+ #ifdef WINCE
+ #	define GC_printf printf
+ #endif
+ 
  
  #define my_assert( e ) \
      if (! (e)) { \
***************
*** 177,183 ****
      return (void*) ~ i;}
  
  
! #ifdef MSWIN32
  int APIENTRY WinMain(
      HINSTANCE instance, HINSTANCE prev, LPSTR cmd, int cmdShow ) 
  {
--- 181,187 ----
      return (void*) ~ i;}
  
  
! #if defined(MSWIN32) || defined(MSWINCE)
  int APIENTRY WinMain(
      HINSTANCE instance, HINSTANCE prev, LPSTR cmd, int cmdShow ) 
  {
***************
*** 187,193 ****
      for (argc = 1; argc < sizeof( argv ) / sizeof( argv[ 0 ] ); argc++) {
          argv[ argc ] = strtok( argc == 1 ? cmd : 0, " \t" );
          if (0 == argv[ argc ]) break;}
- 
  #else
  # ifdef MACOS
      int main() {
--- 191,196 ----