なんとな~くしあわせ?の日記

「そしてそれゆえ、知識そのものが力である」 (Nam et ipsa scientia potestas est.) 〜 フランシス・ベーコン

winedbgを使う

このページの翻訳です
WineHQ - Using the Wine Debugger

1.3. Wineのデバッガを使う

この章ではWineのデバッグをどこから始めればよいか説明します。もし詰まった場所があったり、助けがほしい時は、どのようにすれば役に立つバグレポートを書けるかという情報を得るために"Wine Users Guide"の"How to Report A Bug"の章を読んでください。

1.3.1. Wineのクラッシュ

Wineはたいてい以下のような出力を表示する:

Unhandled exception: page fault on write access to 0x00000000 in 32-bit code (0x0043369e).
Register dump:
 CS:0023 SS:002b DS:002b ES:002b FS:0063 GS:006b
 EIP:0043369e ESP:0b3ee90c EBP:0b3ee938 EFLAGS:00010246(  R- --  I  Z- -P- )
 EAX:00000072 EBX:7b8acff4 ECX:00000000 EDX:6f727265
 ESI:7ba3b37c EDI:7ffa0000
Stack dump:
0x0b3ee90c:  7b82ced8 00000000 7ba3b348 7b884401
0x0b3ee91c:  7b883cdc 00000008 00000000 7bc36e7b
0x0b3ee92c:  7b8acff4 7b82ceb9 7b8acff4 0b3eea18
0x0b3ee93c:  7b82ce82 00000000 00000000 00000000
0x0b3ee94c:  00000000 0b3ee968 70d7ed7b 70c50000
0x0b3ee95c:  00000000 0b3eea40 7b87fd40 7b82d0d0
Backtrace:
=>0 0x0043369e in elementclient (+0x3369e) (0x0b3ee938)
  1 0x7b82ce82 CONSOLE_SendEventThread+0xe1(pmt=0x0(nil)) [/usr/src/debug/wine-1.5.14/dlls/kernel32/console.c:1989] in kernel32 (0x0b3eea18)
  2 0x7bc76320 call_thread_func_wrapper+0xb() in ntdll (0x0b3eea28)
  3 0x7bc7916e call_thread_func+0x7d(entry=0x7b82cda0, arg=0x0(nil), frame=0xb3eeb18) [/usr/src/debug/wine-1.5.14/dlls/ntdll/signal_i386.c:2522] in ntdll (0x0b3eeaf8)
  4 0x7bc762fe RtlRaiseException+0x21() in ntdll (0x0b3eeb18)
  5 0x7bc7f3da start_thread+0xe9(info=0x7ffa0fb8) [/usr/src/debug/wine-1.5.14/dlls/ntdll/thread.c:408] in ntdll (0x0b3ef368)
  6 0xf7597adf start_thread+0xce() in libpthread.so.0 (0x0b3ef468)
0x0043369e: movl	%edx,0x0(%ecx)
Modules:
Module	Address			Debug info	Name (143 modules)
PE	  340000-  3af000	Deferred        speedtreert
PE	  3b0000-  3d6000	Deferred        ftdriver
PE	  3e0000-  3e6000	Deferred        immwrapper
PE	  400000-  b87000	Export          elementclient
PE	  b90000-  e04000	Deferred        elementskill
PE	  e10000-  e42000	Deferred        ifc22
PE	10000000-10016000	Deferred        zlibwapi
ELF	41f75000-41f7e000	Deferred        librt.so.1
ELF	41ff9000-42012000	Deferred        libresolv.so.2
PE	48080000-480a8000	Deferred        msls31
PE	65340000-653d2000	Deferred        oleaut32
PE	70200000-70294000	Deferred        wininet
PE	702b0000-70328000	Deferred        urlmon
PE	70440000-704cf000	Deferred        mlang
PE	70bd0000-70c34000	Deferred        shlwapi
PE	70c50000-70ef3000	Deferred        mshtml
PE	71930000-719b8000	Deferred        shdoclc
PE	78130000-781cb000	Deferred        msvcr80
ELF	79afb000-7b800000	Deferred        libnvidia-glcore.so.304.51
ELF	7b800000-7ba3d000	Dwarf           kernel32<elf>
  \-PE	7b810000-7ba3d000	\               kernel32
ELF	7bc00000-7bcd5000	Dwarf           ntdll<elf>
  \-PE	7bc10000-7bcd5000	\               ntdll
ELF	7bf00000-7bf04000	Deferred        <wine-loader>
ELF	7c288000-7c400000	Deferred        libvorbisenc.so.2
PE	7c420000-7c4a7000	Deferred        msvcp80
ELF	7c56d000-7c5b6000	Deferred        dinput<elf>
  \-PE	7c570000-7c5b6000	\               dinput
ELF	7c5b6000-7c600000	Deferred        libdbus-1.so.3
ELF	7c70e000-7c715000	Deferred        libasyncns.so.0
ELF	7c715000-7c77e000	Deferred        libsndfile.so.1
ELF	7c77e000-7c7e5000	Deferred        libpulsecommon-1.1.so
ELF	7c7e5000-7c890000	Deferred        krnl386.exe16.so
PE	7c7f0000-7c890000	Deferred        krnl386.exe16
ELF	7c890000-7c900000	Deferred        ieframe<elf>
  \-PE	7c8a0000-7c900000	\               ieframe
ELF	7ca00000-7ca1a000	Deferred        rasapi32<elf>
  \-PE	7ca10000-7ca1a000	\               rasapi32
ELF	7ca1a000-7ca21000	Deferred        libnss_dns.so.2
ELF	7ca21000-7ca25000	Deferred        libnss_mdns4_minimal.so.2
ELF	7ca25000-7ca2d000	Deferred        libogg.so.0
ELF	7ca2d000-7ca5a000	Deferred        libvorbis.so.0
ELF	7cd5d000-7cd9c000	Deferred        libflac.so.8
ELF	7cd9c000-7cdea000	Deferred        libpulse.so.0
ELF	7cdfe000-7ce23000	Deferred        iphlpapi<elf>
  \-PE	7ce00000-7ce23000	\               iphlpapi
ELF	7cff1000-7cffd000	Deferred        libnss_nis.so.2
ELF	7d60d000-7d629000	Deferred        wsock32<elf>
  \-PE	7d610000-7d629000	\               wsock32
ELF	7d80d000-7d828000	Deferred        libnsl.so.1
ELF	7d8cf000-7d8db000	Deferred        libgsm.so.1
ELF	7d8db000-7d903000	Deferred        winepulse<elf>
  \-PE	7d8e0000-7d903000	\               winepulse
ELF	7d95c000-7d966000	Deferred        libwrap.so.0
ELF	7d966000-7d96d000	Deferred        libxtst.so.6
ELF	7d96d000-7d992000	Deferred        mmdevapi<elf>
  \-PE	7d970000-7d992000	\               mmdevapi
ELF	7d9b3000-7d9d0000	Deferred        msimtf<elf>
  \-PE	7d9c0000-7d9d0000	\               msimtf
ELF	7d9d0000-7d9e5000	Deferred        comm.drv16.so
PE	7d9e0000-7d9e5000	Deferred        comm.drv16
ELF	7da83000-7db5f000	Deferred        libgl.so.1
ELF	7db60000-7db63000	Deferred        libx11-xcb.so.1
ELF	7db63000-7db78000	Deferred        system.drv16.so
PE	7db70000-7db78000	Deferred        system.drv16
ELF	7db98000-7dca1000	Deferred        opengl32<elf>
  \-PE	7dbb0000-7dca1000	\               opengl32
ELF	7dca1000-7dcb6000	Deferred        vdmdbg<elf>
  \-PE	7dcb0000-7dcb6000	\               vdmdbg
ELF	7dcce000-7dd04000	Deferred        uxtheme<elf>
  \-PE	7dcd0000-7dd04000	\               uxtheme
ELF	7dd04000-7dd0a000	Deferred        libxfixes.so.3
ELF	7dd0a000-7dd15000	Deferred        libxcursor.so.1
ELF	7dd16000-7dd1f000	Deferred        libjson.so.0
ELF	7dd24000-7dd38000	Deferred        psapi<elf>
  \-PE	7dd30000-7dd38000	\               psapi
ELF	7dd78000-7dda1000	Deferred        libexpat.so.1
ELF	7dda1000-7ddd6000	Deferred        libfontconfig.so.1
ELF	7ddd6000-7dde6000	Deferred        libxi.so.6
ELF	7dde6000-7ddef000	Deferred        libxrandr.so.2
ELF	7ddef000-7de11000	Deferred        libxcb.so.1
ELF	7de11000-7df49000	Deferred        libx11.so.6
ELF	7df49000-7df5b000	Deferred        libxext.so.6
ELF	7df5b000-7df75000	Deferred        libice.so.6
ELF	7df75000-7e005000	Deferred        winex11<elf>
  \-PE	7df80000-7e005000	\               winex11
ELF	7e005000-7e0a5000	Deferred        libfreetype.so.6
ELF	7e0a5000-7e0c5000	Deferred        libtinfo.so.5
ELF	7e0c5000-7e0ea000	Deferred        libncurses.so.5
ELF	7e123000-7e1eb000	Deferred        crypt32<elf>
  \-PE	7e130000-7e1eb000	\               crypt32
ELF	7e1eb000-7e235000	Deferred        dsound<elf>
  \-PE	7e1f0000-7e235000	\               dsound
ELF	7e235000-7e2a7000	Deferred        ddraw<elf>
  \-PE	7e240000-7e2a7000	\               ddraw
ELF	7e2a7000-7e3e3000	Deferred        wined3d<elf>
  \-PE	7e2b0000-7e3e3000	\               wined3d
ELF	7e3e3000-7e417000	Deferred        d3d8<elf>
  \-PE	7e3f0000-7e417000	\               d3d8
ELF	7e417000-7e43b000	Deferred        imm32<elf>
  \-PE	7e420000-7e43b000	\               imm32
ELF	7e43b000-7e46f000	Deferred        ws2_32<elf>
  \-PE	7e440000-7e46f000	\               ws2_32
ELF	7e46f000-7e49a000	Deferred        msacm32<elf>
  \-PE	7e470000-7e49a000	\               msacm32
ELF	7e49a000-7e519000	Deferred        rpcrt4<elf>
  \-PE	7e4b0000-7e519000	\               rpcrt4
ELF	7e519000-7e644000	Deferred        ole32<elf>
  \-PE	7e530000-7e644000	\               ole32
ELF	7e644000-7e6f7000	Deferred        winmm<elf>
  \-PE	7e650000-7e6f7000	\               winmm
ELF	7e6f7000-7e7fa000	Deferred        comctl32<elf>
  \-PE	7e700000-7e7fa000	\               comctl32
ELF	7e7fa000-7ea23000	Deferred        shell32<elf>
  \-PE	7e810000-7ea23000	\               shell32
ELF	7ea23000-7eaf9000	Deferred        gdi32<elf>
  \-PE	7ea30000-7eaf9000	\               gdi32
ELF	7eafb000-7eaff000	Deferred        libnvidia-tls.so.304.51
ELF	7eaff000-7eb09000	Deferred        libxrender.so.1
ELF	7eb09000-7eb0f000	Deferred        libxxf86vm.so.1
ELF	7eb0f000-7eb18000	Deferred        libsm.so.6
ELF	7eb18000-7eb32000	Deferred        version<elf>
  \-PE	7eb20000-7eb32000	\               version
ELF	7eb32000-7ec87000	Deferred        user32<elf>
  \-PE	7eb40000-7ec87000	\               user32
ELF	7ec87000-7ecf1000	Deferred        advapi32<elf>
  \-PE	7ec90000-7ecf1000	\               advapi32
ELF	7ecf1000-7ed8f000	Deferred        msvcrt<elf>
  \-PE	7ed00000-7ed8f000	\               msvcrt
ELF	7ef8f000-7ef9c000	Deferred        libnss_files.so.2
ELF	7ef9c000-7efc7000	Deferred        libm.so.6
ELF	7efc8000-7efe5000	Deferred        libgcc_s.so.1
ELF	7efe5000-7f000000	Deferred        crtdll<elf>
  \-PE	7eff0000-7f000000	\               crtdll
ELF	f73d0000-f73d4000	Deferred        libxinerama.so.1
ELF	f73d4000-f73d8000	Deferred        libxau.so.6
ELF	f73da000-f73df000	Deferred        libdl.so.2
ELF	f73df000-f7591000	Dwarf           libc.so.6
ELF	f7591000-f75ab000	Dwarf           libpthread.so.0
ELF	f75ab000-f76ef000	Dwarf           libwine.so.1
ELF	f7722000-f7728000	Deferred        libuuid.so.1
ELF	f7729000-f774a000	Deferred        ld-linux.so.2
ELF	f774a000-f774b000	Deferred        [vdso].so
Threads:
process  tid      prio (all id:s are in hex)
00000008 (D) C:\Perfect World Entertainment\Perfect World International\element\elementclient.exe
	00000031    0 <==
	00000035   15
	00000012    0
	00000021    0
	00000045    0
	00000044    0
	00000043    0
	00000038   15
	00000037    0
	00000036   15
	00000034    0
	00000033    0
	00000032    0
	00000027    0
	00000009    0
0000000e services.exe
	0000000b    0
	00000020    0
	00000017    0
	00000010    0
	0000000f    0
00000014 winedevice.exe
	0000001e    0
	0000001b    0
	00000016    0
	00000015    0
0000001c plugplay.exe
	00000022    0
	0000001f    0
	0000001d    0
00000023 explorer.exe
	00000024    0
  1. 1. クラッシュの内容を段階的にデバッグします。あなたはそのどこかで止まるかもしれません、しかしそのバグレポートに集められた情報を利用可能な限り報告してください。クラッシュの原因を取得します。これはたいていページフォルト、Wineで未実装の機能、もしくはそのようなものです。クラッシュの報告時はその情報があなたにとって意味を成さないものであってもすべてのクラッシュダンプを報告します。(これがページフォルトや0x00への書き込みアクセスだった場合、大抵の場合WineはアプリケーションにNULLを渡します。)
  1. 2. クラッシュの原因を特定します。これは、大抵の場合エラーへの直接もしくは副次的な処理もしくはWineの誤った振る舞いのためなので、Wineを WINEDEBUG=+relay という環境変数を設定して再度起動してください。これは大量の出力を表示するでしょうが、大抵その原因は最後の呼び出しに位置しています。これらの行は大抵以下のような感じです:
000d:Call advapi32.RegOpenKeyExW(00000090,7eb94da0 L"Patterns",00000000,00020019,0033f968) ret=7eb39af8
^^^^      ^^^^^^^^ ^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^  ^^^^^^^^^^^^
|         |        |             |                 |           |                           |返り値のアドレス
|         |        |             |                 |           |追加の引数
|         |        |             |                 |印字可能なパラメータ
|         |        |             |引数
|         |        |呼び出された関数
|         |呼び出された関数のモジュール
|呼び出されたスレッド

000d:Ret  advapi32.RegOpenKeyExW() retval=00000000 ret=7eb39af8
                                   ^^^^^^^^^^^^^^^
                                   |返り値が32bitならばvalueは0
  1. 3. もしあなたがWineの関数内で誤った動きを見つけたなら、なぜそのような動きをするか見つけようとしてください。その関数をソースコード中で見つけます。渡された引数の意味を捉えてください。たいていソースコードの始めには WINE_DEFAULT_DEBUG_CHANNEL(channel)が存在します。WineをWINEDEBUG=+xyz,+relayという環境変数を設定して再度起動しましょう。そうすると突然そこにはWINE_DECLARE_DEBUG_CHANNEL(channel)の形式でソースコードの始めのほうで定義されたデバッグ用のチャンネルが現れます。さすれば、そのムカつく関数はこれらのチャンネルの一つを使用しているかもしれません。それではTRACE_(channel)(" ... /n")という関数を使用して、中身を見てみましょう。これは、コマンドラインに追加のチャンネルを追加してやります。
  1. 4. 内部用デバッガを使ってどのようにデバッグするかについての追加情報はprograms/winedbg/READMEで見つけられるでしょう。
  1. 5. もしこの情報が不明瞭、もしくはその関数自体で何が起こっているか知りたいのであれば、WineをWINEDEBUG=+allで起動させてください、これはWine内のすべてのデバッグ情報を吐かせます。その際は生成されるデバッグ出力を制限する必要があります。それはgrepを使ったりして出力をパイプすることで成し遂げられます、もしくはレジストリキーを使用するという代替手段もあります。それについては1.5.3章を見よ。
  1. 6. もしそれさえも十分でなければ、あなたが関連ありそうだと思う関数に自分でデバッグ出力を追加します。デバッグロギングについての章の中でより多くの情報を見られます。もしくは、Wineデバッガの代わりにgdbを使用してプログラムを走らせたほうがいいかもしれない。もしあなたがそうするならば、gdb内でのセグメンテーション違反の処理を無効化するためにhandle SIGSEGV nostop noprintを利用してください(これはWin16に必要)。
  1. 7. もしくはその関数にブレークポイントを張ることも可能だ。wineの代わりにwinedbgを起動させる。デバッガは走りだすとブレークポイントを張ったRegOpenKeyExW(あなたのデバッグしたい関数名に変えてね)でブレークする。そして、普通のプログラムの実行を続けさせる。Wineはブレークポイントに到達したならば止まるでしょう。もしプログラムがその関数の呼び出しでクラッシュしないならば、その関数に入る所までそれを続ける。あなたはもしかするとクラッシュのその時までその関数へのシングルステップを続けるかもしれない*1。他のデバッガのコマンドを使えばレジスタ等が表示できる。

1.3.2. プログラムがハングする、何も起きない場合

wineの代わりにwinedbgを使用してプログラムを起動します。プログラムがwinedbgの端末内につなぎとめられたら、Ctrl-Cを押します。これはプログラムを停止して、プログラムがどうしてクラッシュしたかデバッグすることを許します。

1.3.3. メッセージボックスでエラーが報告される場合

ときどきプログラムは漠然としたメッセージボックスを使ってエラーを報告してきます。私たちはそれらをクラッシュとして同じ方法でデバッグできますが、そこには問題があります…メッセージボックスを設定するためにそのプログラムはWineが出力する膨大なデバッグコードを同様に呼び出すのです。そのエラーは大抵メッセージボックスを設定する前に起こるので、あなたはwinedgbを起動させることができ、MessageBoxAに(これはwin16とwin32のプログラムに呼ばれる)ブレークポイントを張れます、そしてデバッグを継続できます。WINEDEBUG=+allを使えばWineは設定しているメッセージボックスの前に止まるでしょう。上記で説明しているように続けます。あなたはWINEDEBUG=+relay wine program.exe 2>&1 | less -iのようなコマンドを使って"MessageBox"の文字列を検索できるでしょう。

1.3.4. プログラムを逆アセンブルする

あなたは文書化されてない機能を確認したり使用したりするためにムカつくプログラムを逆アセンブルしようとしているかもしれない。最良の方法、それは無料で利用可能なWin16向け逆アセンブラツールWindows Codebackです。ファイル名はwcbxxx.zipです(または wcb105a.zip)。Win32のプログラムを逆アセンブルすることは、例えばAnsgar Trimborn氏のGoVestなどを利用することで可能です。それはここにあります。もしくはもっと新しくてより良いDataRescueの対話型逆アセンブラー(IDA)を使用できるかもしれない。AppDBからたくさんのバージョンのIDAが見つけられるので見てみてください。他の有名な逆アセンブラーとしてはURSoftのWindows Disassembler 32が挙げられます。w32dsm87.zip(もしくはそれに類似する奴)をwinsite.comかsoftpedia.comから探してください。Windows Disassembler 32は現在Wineで使用するには問題がありそうなので、IDAかGoVestを使うのが良さそうです。考えられる逆アセンブラーとしては他にNuMegaのSoftIceが挙げられます。そのソフトウェアはCompuWareとして権利を取得されたためWindowsドライバーの開発元になってしまいました。新しいバージョンのSoftIceはWindowsのサービスを走らせることを要求し、それ故にWineでは動作しようとしないのです。もしどれも動かなければ、Googleで他の逆アセンブラーを探したほうがいいかもです。

アセンブルされたコードを理解することが質問に挙がるだろう。多くのコードは標準的なC言語のエントリーにあたる(それは大抵C言語で書かれているからだ)。Win16の関数のエントリーは大抵以下のようなものだ:

push bp
mov bp, sp
... function code ..
retf XXXX 	<--------- XXXX は引数のBYTE数

これはデータの保存を伴わないFAR関数*2です。その引数はオフセット値のインクリメントを伴う[bp+6]で始まる。あと、その[bp+6]は右辺の引数に属する、それはPASCALの関数の慣習を使用するwin16の関数群を外に公開するためなのですが。なので、私たちがstrcmp(a,b)を使用する場合、a[bp+6]にあたり、そしてb[bp+10]にあたるということになる。

ほとんどの関数はスタックフレーム*3内にデータを保存する:

enter 0086, 00
... function code ...
leave
retf XXXX

This does mostly the same as above, but also adds 0x86 bytes of stackstorage, which is accessed using [bp-xx]. Before calling a function, arguments are pushed on the stack using something like this:

push word ptr [bp-02]   <- will be at [bp+8]
push di                 <- will be at [bp+6]
call KERNEL.LSTRLEN

Here first the selector and then the offset to the passed string are pushed.

1.3.5. デバッグの手順例

Let's debug the infamous Word SHARE.EXE message box:

|marcus@jet $ wine winword.exe
|            +---------------------------------------------+
|            | !  You must leave Windows and load SHARE.EXE|
|            |    before starting Word.                    |
|            +---------------------------------------------+
|marcus@jet $ WINEDEBUG=+relay,-debug wine winword.exe
|CallTo32(wndproc=0x40065bc0,hwnd=000001ac,msg=00000081,wp=00000000,lp=00000000)
|Win16 task 'winword': Breakpoint 1 at 0x01d7:0x001a
|CallTo16(func=0127:0070,ds=0927)
|Call WPROCS.24: TASK_RESCHEDULE() ret=00b7:1456 ds=0927
|Ret  WPROCS.24: TASK_RESCHEDULE() retval=0x8672 ret=00b7:1456 ds=0927
|CallTo16(func=01d7:001a,ds=0927)
|     AX=0000 BX=3cb4 CX=1f40 DX=0000 SI=0000 DI=0927 BP=0000 ES=11f7
|Loading symbols: /home/marcus/wine/wine...
|Stopped on breakpoint 1 at 0x01d7:0x001a
|In 16 bit mode.
|Wine-dbg>break MessageBoxA                          <---- Set Breakpoint
|Breakpoint 2 at 0x40189100 (MessageBoxA [msgbox.c:190])
|Wine-dbg>c                                            <---- Continue
|Call KERNEL.91: INITTASK() ret=0157:0022 ds=08a7
|     AX=0000 BX=3cb4 CX=1f40 DX=0000 SI=0000 DI=08a7 ES=11d7 EFL=00000286
|CallTo16(func=090f:085c,ds=0dcf,0x0000,0x0000,0x0000,0x0000,0x0800,0x0000,0x0000,0x0dcf)
|...                                                   <----- Much debug output
|Call KERNEL.136: GETDRIVETYPE(0x0000) ret=060f:097b ds=0927
                               ^^^^^^ Drive 0 (A:)
|Ret  KERNEL.136: GETDRIVETYPE() retval=0x0002 ret=060f:097b ds=0927
                                        ^^^^^^  DRIVE_REMOVEABLE
						(It is a floppy diskdrive.)

|Call KERNEL.136: GETDRIVETYPE(0x0001) ret=060f:097b ds=0927
                               ^^^^^^ Drive 1 (B:)
|Ret  KERNEL.136: GETDRIVETYPE() retval=0x0000 ret=060f:097b ds=0927
                                        ^^^^^^  DRIVE_CANNOTDETERMINE
						(I don't have drive B: assigned)

|Call KERNEL.136: GETDRIVETYPE(0x0002) ret=060f:097b ds=0927
                               ^^^^^^^ Drive 2 (C:)
|Ret  KERNEL.136: GETDRIVETYPE() retval=0x0003 ret=060f:097b ds=0927
                                        ^^^^^^ DRIVE_FIXED
                                               (specified as a hard disk)

|Call KERNEL.97: GETTEMPFILENAME(0x00c3,0x09278364"doc",0x0000,0927:8248) ret=060f:09b1 ds=0927
                                 ^^^^^^           ^^^^^        ^^^^^^^^^
                                 |                |            |buffer for fname
                                 |                |temporary name ~docXXXX.tmp
                                 |Force use of Drive C:.

|Warning: GetTempFileName returns 'C:~doc9281.tmp', which doesn't seem to be writable.
|Please check your configuration file if this generates a failure.
Whoops, it even detects that something is wrong!

|Ret  KERNEL.97: GETTEMPFILENAME() retval=0x9281 ret=060f:09b1 ds=0927
                                          ^^^^^^ Temporary storage ID

|Call KERNEL.74: OPENFILE(0x09278248"C:~doc9281.tmp",0927:82da,0x1012) ret=060f:09d8 ds=0927
                                    ^^^^^^^^^^^^^^^^ ^^^^^^^^^ ^^^^^^^
                                    |filename        |OFSTRUCT |open mode:

                                       OF_CREATE|OF_SHARE_EXCLUSIVE|OF_READWRITE

This fails, since my C: drive is in this case mounted readonly.

|Ret  KERNEL.74: OPENFILE() retval=0xffff ret=060f:09d8 ds=0927
                                   ^^^^^^ HFILE_ERROR16, yes, it failed.

|Call USER.1: MESSAGEBOX(0x0000,0x09278376"You must close Windows and load SHARE.EXE before you start Word.",0x00000000,0x1030) ret=060f:084f ds=0927

And MessageBox'ed.

|Stopped on breakpoint 2 at 0x40189100 (MessageBoxA [msgbox.c:190])
|190     {		<- the sourceline
In 32 bit mode.
Wine-dbg>

The code seems to find a writable harddisk and tries to create a file there. To work around this bug, you can define C: as a network drive, which is ignored by the code above.

1.3.6. デバッグのチップス

Here are some additional debugging tips:

If you have a program crashing at such an early loader phase that you can't use the Wine debugger normally, but Wine already executes the program's start code, then you may use a special trick. You should do a

WINEDEBUG=+relay wine program
to get a listing of the functions the program calls in its start function. Now you do a
winedbg winfile.exe
This way, you get into winedbg. Now you can set a breakpoint on any function the program calls in the start function and just type c to bypass the eventual calls of Winfile to this function until you are finally at the place where this function gets called by the crashing start function. Now you can proceed with your debugging as usual.

If you try to run a program and it quits after showing an error message box, the problem can usually be identified in the return value of one of the functions executed before MessageBox(). That's why you should re-run the program with e.g.

WINEDEBUG=+relay wine program_name &>relmsg
Then do a more relmsg and search for the last occurrence of a call to the string "MESSAGEBOX". This is a line like
Call USER.1: MESSAGEBOX(0x0000,0x01ff1246 "Runtime error 219 at 0004:1056.",0x00000000,0x1010) ret=01f7:2160 ds=01ff
In my example the lines before the call to MessageBox() look like that:
Call KERNEL.96: FREELIBRARY(0x0347) ret=01cf:1033 ds=01ff
CallTo16(func=033f:0072,ds=01ff,0x0000)
Ret  KERNEL.96: FREELIBRARY() retval=0x0001 ret=01cf:1033 ds=01ff
Call KERNEL.96: FREELIBRARY(0x036f) ret=01cf:1043 ds=01ff
CallTo16(func=0367:0072,ds=01ff,0x0000)
Ret  KERNEL.96: FREELIBRARY() retval=0x0001 ret=01cf:1043 ds=01ff
Call KERNEL.96: FREELIBRARY(0x031f) ret=01cf:105c ds=01ff
CallTo16(func=0317:0072,ds=01ff,0x0000)
Ret  KERNEL.96: FREELIBRARY() retval=0x0001 ret=01cf:105c ds=01ff
Call USER.171: WINHELP(0x02ac,0x01ff05b4 "COMET.HLP",0x0002,0x00000000) ret=01cf:1070 ds=01ff
CallTo16(func=0117:0080,ds=01ff)
Call WPROCS.24: TASK_RESCHEDULE() ret=00a7:0a2d ds=002b
Ret  WPROCS.24: TASK_RESCHEDULE() retval=0x0000 ret=00a7:0a2d ds=002b
Ret  USER.171: WINHELP() retval=0x0001 ret=01cf:1070 ds=01ff
Call KERNEL.96: FREELIBRARY(0x01be) ret=01df:3e29 ds=01ff
Ret  KERNEL.96: FREELIBRARY() retval=0x0000 ret=01df:3e29 ds=01ff
Call KERNEL.52: FREEPROCINSTANCE(0x02cf00ba) ret=01f7:1460 ds=01ff
Ret  KERNEL.52: FREEPROCINSTANCE() retval=0x0001 ret=01f7:1460 ds=01ff
Call USER.1: MESSAGEBOX(0x0000,0x01ff1246 "Runtime error 219 at 0004:1056.",0x00000000,0x1010) ret=01f7:2160 ds=01ff

I think that the call to MessageBox() in this example is not caused by a wrong result value of some previously executed function (it's happening quite often like that), but instead the message box complains about a runtime error at 0x0004:0x1056.

As the segment value of the address is only 4, I think that that is only an internal program value. But the offset address reveals something quite interesting: offset 1056 is very close to the return address of FREELIBRARY():

Call KERNEL.96: FREELIBRARY(0x031f) ret=01cf:105c ds=01ff
^^^^
Provided that segment 0x0004 is indeed segment 0x1cf, we now we can use IDA to disassemble the part that caused the error. We just have to find the address of the call to FreeLibrary(). Some lines before that the runtime error occurred. But be careful! In some cases you don't have to disassemble the main program, but instead some DLL called by it in order to find the correct place where the runtime error occurred. That can be determined by finding the origin of the segment value (in this case 0x1cf).
If you have created a relay file of some crashing program and want to set a breakpoint at a certain location which is not yet available as the program loads the breakpoint segment during execution, you may set a breakpoint to GetVersion16/32 as those functions are called very often.

Then do a c until you are able to set this breakpoint without error message.

1.3.7. Some basic debugger usages

After starting your program with

winedbg myprog.exe
the program loads and you get a prompt at the program starting point. Then you can set breakpoints:

b RoutineName      (by routine name) OR
b *0x812575        (by address)
Then you hit c (continue) to run the program. It stops at the breakpoint. You can type

step               (to step one line) OR
stepi              (to step one machine instruction at a time;
                    here, it helps to know the basic 386
                    instruction set)
info reg           (to see registers)
info stack         (to see hex values in the stack)
info local         (to see local variables)
list line number   (to list source code)
x variable name    (to examine a variable; only works if code
                    is not compiled with optimization)
x 0x4269978        (to examine a memory location)
?                  (help)
q                  (quit)

By hitting Enter, you repeat the last command.

1.3.8. Useful programs

Some useful programs:

GoVest: govest.zip is available from http://www.oocities.com/govest/.
Simple win32 disassembler that works well with Wine.

IDA:
IDA Pro is highly recommended, but is not free. DataRescue does however make trial versions available.

Take a look in the AppDB for links to various versions of IDA.

XRAY: http://garbo.uwasa.fi/pub/pc/sysinfo/xray15.zip
Traces DOS calls (Int 21h, DPMI, ...). Use it with Windows to correct file management problems etc.

pedump: ftp://ftp.simtel.net/pub/simtelnet/win95/prog/pedump.zip
Dumps the imports and exports of a PE (Portable Executable) DLL.

winedump: (included in wine tree)
Dumps the imports and exports of a PE (Portable Executable) DLL.

*1:延々と…

*2:アセンブラ用語、「「far」はアドレスを32ビットで扱い、「near」は16ビットで扱うということです。」らしいぞ。
最近プログラムの勉強をしています「far」[near]の意味がよくわから... - Yahoo!知恵袋

*3:サブルーチン毎にコールスタックに格納する情報
コールスタック