DLLの闇 (2)

id:NyaRuRu:20060715 の続き.
分かったこと.

Dependency Walker は素晴らしい.

さあみなさん,目の前のバービー人形にでも DLL Hell を説明してみてください.
「system32 ディレクトリの mfc42.dll などが,古いバージョンや新しいバージョンのもので置き換えられてしまうことで,今まで動いていたアプリケーションが実行時エラーを起こすようになる現象」というのではどうも生ぬるい気がします.



Windows の DLL の仕組み,特に PE ヘッダに記述するいわゆる「暗黙のリンク」は,ベースファイル名しか記述しないため,名前空間の競合やロード順序によっては簡単に破綻してしまうことがあります.例えば "Security.dll" という名前の DLL が引き起こした混乱について,The Old New Thing で紹介されています.

Don't name your DLL "Security.dll"
http://blogs.msdn.com/oldnewthing/archive/2004/07/02/171769.aspx

Dependency Walker は,この名前解決をシミュレートしてくれます.単に PE ファイルのヘッダをダンプして,どの DLL に暗黙にリンクしているか調べるだけなら dumpbin で十分です.Dependency Walker は,それだけではなく,その環境で exe ファイルを実行したらどの DLL がロードされるかについて,ある程度の情報を与えてくれるところが優れています.
前回作った SxS による ws2_32.dll の置き換えサンプル (id:NyaRuRu:20060711#p1) を,次のように配置し,Dependency Walker 2.1.3623 に test.exe を読み込ませてみます.環境は Windows Vista build 5456 日本語版です.

  • d:\test\test.exe
  • d:\test\ws2_32.dll

Dependency Walker 起動直後の状態では,モジュールのパスが表示されていません.

起動直後の状態

そこで,F9 を押してパスを表示させます.

パスを表示させた状態

exe ファイルと同じディレクトリにある ws2_32.dll がロードされることが分かります.
参考までに Windows 2000 SP4 での実行画面.(ただし,c:\test ではなく d:\test に配置しています)

パスを表示させた状態 (Windows 2000 SP4)

Windows 2000 は manifest を使用した SxS コンポーネントに対応していません.よく見れば msvcr80.dll のパスが Vista 環境と異なっています.プライベートな ws2_32.dll が呼び出されるのは純粋に DLL の検索順序によるものと予想されます.
これを確かめるために Dependency Walker のメニューから,Options → Configure Module Search Orders を選んでみましょう.まずは Vista 環境のものから.

Configure Module Search Orders (Windows Vista build 5456)

test.exe の manifest がきちんと反映されていて,SxS コンポーネントが検索順序の上位に来ていることが分かります.また,ws2_32.dll が KnownDLLs として扱われていること*1にも注意が必要です.manifest ファイルで ws2_32.dll を指定しなかった場合,アプリケーションディレクトリよりも KnownDLL の方が検索順序で上位にくるため,システムディレクトリの ws2_32.dll がロードされることになります.
次に Windows 2000 SP4 の場合.

Configure Module Search Orders (Windows 2000 SP4)

Windows 2000 は manifest を使用した SxS コンポーネントに対応していないのでこれらが検索順序から消えています.一方で,ws2_32.dll は KnownDLLs に含まれません.結果として,アプリケーションディレクトリの ws2_32.dll が読み込まれることになります*2



Dependency Walker を活用したデバッグ方法が,Mozilla の開発者向け資料に紹介されています.
Dependency Walker (depends.exe) の使用方法
この資料を読むまで知りませんでしたが,Dependency Walker にはプロファイル機能もあって,プロセス実行中の LoadLibrary や GetProcAddres の呼出しをフックし,そのログから依存グラフを再構成することができるようです.また,一連の作業は CUI からバッチ処理することもできるとのことでした.
というわけで,Mozilla の開発者向けページにあるように,DLL のロードで困ったことがあれば,Dependency Walker のプロファイルログを作成し,しかるべきところに投げてみるという方法は,かなり有効かもしれません.

*1:ただし,レジストリの KnownDLLs の項目に ws2_32.dll は含まれていない.この挙動は,http://support.microsoft.com/kb/164501/en-us に記載されている「KnownDLL から暗黙にリンクされる DLL」についての記述を反映したものと考えられる.ただし,KnownDLLs の挙動は OS によって変化することにも注意.詳しくは『[http://www.microsoft.com/technet/technetmag/issues/2007/09/WindowsConfidential/default.aspx?loc=jp/:title=]』参照のこと.

*2:Windows 2000 当時は,アプリケーションディレクトリ→カレントディレクトリ→システムディレクトリの順位だったことに注意.see SafeDllSearchMode.id:NyaRuRu:20040931#p2