Windows NT 5.x 系で動かすとどうしてもクラッシュしてしまうプログラムについて

無理やり、Java 9 を Windows 2000で動かす実験をしてるのですが次のコードで引っかかってるので解説するよ。

 SUB_L081B7500:
          push    ebp
          mov    ebp,esp
          mov    edx,[L086A3308]
          mov    eax,fs:[0000002Ch]
          push    ebx
          push    esi
          mov    esi,ecx
          mov    eax,[eax+edx*4] <- eax = 0! Fault

何が行われてるかというと、TLS(Thread Local Storege) にアクセスしようとしてる。

Vista 以降では 0以外がかえってくるのだが…。

disassembly – Thread local storage access on Windows XP – Reverse Engineering Stack Exchange

Obviously the compiler seems to implement thread-safety by first poking into the TLS slot of the current thread. fs:2Ch is supposed to lead to the TLS array per documentation. However on Windows XP, fs:2Ch doesn’t seem to be set. This returned 0 for me and so did the next instruction (_tls_index was also 0.) That led to the 3rd instruction blowing up as it was accessing invalid memory.

“On Windows Vista and later, dynamically-loaded DLLs also support Thread Local Storage. This is in direct contradiction to the existing Portable Executable format documentation, which states that “Statically declared TLS data objects … can be used only in statically loaded image files. This fact makes it unreliable to use static Thread Local Storage data in a DLL unless you know that the DLL, or anything statically linked with it, will never be loaded dynamically with the LoadLibrary API function

XPはLoadLibrary で初期化されないらしい

スレッド ローカル ストレージ (TLS: Thread Local Storage)

Windows Vista より前の Windows オペレーティング システムでは、__declspec( thread ) にいくつか制限があります。 DLL で任意のデータまたはオブジェクトを __declspec( thread ) として宣言した場合、動的に読み込まれたときに保護違反が発生する可能性があります。 DLL が LoadLibrary によって読み込まれると、コードが __declspec( thread ) データを参照するたびにシステム エラーが発生します。 スレッドのグローバル変数領域は実行時に割り当てられるため、この領域のサイズは、アプリケーションの要件および静的にリンクされているすべての DLL の要件に基づいて計算されます。 LoadLibrary を使用すると、__declspec( thread ) を使用して宣言されたスレッド ローカル変数用にこの領域を拡張することはできません。 DLL が LoadLibrary で読み込まれる可能性がある場合は、DLL で TLS API (TlsAlloc など) を使用して TLS を割り当ててください。

・ω・まじかよ!

VC++ 2015で作ったDLLをWindows XPで動かすと最悪死ぬ – イグトランスの頭の中

試しに次のコード書いてみる

DLL_THREAD_ATTACH:
 _asm{
          call DWORD PTR [TlsAlloc]
           mov    eax,fs:[00000018h]
          add    eax,0xe10
          mov fs:[0x2c],eax
 }

ちなみに、これやると、Windows Vista 以降だとクラッシュする

c:\java\jre-9>bin\java -version
Error occurred during initialization of VM
java.lang.UnsatisfiedLinkError: N:\dev\jre-9\bin\nio.dll: 指定されたプロシージャが見つかりません。

    at java.lang.ClassLoader$NativeLibrary.load(java.base/Native Method)
    at java.lang.ClassLoader.loadLibrary0(java.base/Unknown Source)
    at java.lang.ClassLoader.loadLibrary(java.base/Unknown Source)
    at java.lang.Runtime.loadLibrary0(java.base/Unknown Source)
    at java.lang.System.loadLibrary(java.base/Unknown Source)
    at sun.nio.fs.WindowsNativeDispatcher$1.run(java.base/Unknown Source)
    at sun.nio.fs.WindowsNativeDispatcher$1.run(java.base/Unknown Source)
    at java.security.AccessController.doPrivileged(java.base/Native Method)
    at sun.nio.fs.WindowsNativeDispatcher.<clinit>(java.base/Unknown Source)
    at sun.nio.fs.WindowsFileAttributes.get(java.base/Unknown Source)
    at sun.nio.fs.WindowsFileAttributeViews$Basic.readAttributes(java.base/Unknown Source)
    at sun.nio.fs.WindowsFileAttributeViews$Basic.readAttributes(java.base/Unknown Source)
    at sun.nio.fs.WindowsFileSystemProvider.readAttributes(java.base/Unknown Source)
    at sun.nio.fs.AbstractFileSystemProvider.isRegularFile(java.base/Unknown Source)
    at java.nio.file.Files.isRegularFile(java.base/Unknown Source)
    at java.lang.module.ModuleFinder.ofSystem(java.base/Unknown Source)
    at jdk.internal.module.ModuleBootstrap.boot(java.base/Unknown Source)
    at java.lang.System.initPhase2(java.base/Unknown Source)

ちょっとエラーが変わった。 nio.dllで CreateSymbolicLinkW を使っててロードできないらしい

(つづく)

Windows NT 5.x 系で動かすとどうしてもクラッシュしてしまうプログラム その2

おすすめ

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です