Windows XP 的功能局部静电发生器故障代码
Function local statics generate faulty code for Windows XP
我最初在 ReverseEngineering StackExchange 上 post 编辑了这个,但不知道它到底属于哪里。我还是决定 post 在这里。
最近,Microsoft Visual Studio 2015 编译器终于符合 C++ 标准要求,为函数局部静态生成线程安全代码。在大多数情况下,这工作得很好,但我 运行 在 Windows XP 上遇到了以下 3 条指令导致崩溃的情况:
mov eax,dword ptr fs:[0000002Ch]
mov ecx,dword ptr [MyModule!_tls_index (102eea44)]
mov ecx,dword ptr [eax+ecx*4]
显然,编译器似乎通过首先插入当前线程的 TLS 插槽来实现线程安全。 fs:2Ch
应该根据文档导致 TLS 数组。但是在 Windows XP 上,似乎没有设置 fs:2Ch
。这为我返回了 0,下一条指令也是如此(_tls_index
也是 0。)这导致第三条指令在访问无效内存时崩溃。
有人知道为什么 fs:2Ch
可能无法在 Windows XP 上设置吗?我们的代码中都使用了函数局部静态,我无法想象没有其他人 运行 参与其中。
更新
我已经仔细考虑了我在这个问题上应用的每个标签。请不要添加或删除任何内容。
逆向工程 SE 的 Peter Ferrie 巧妙地回答了这个问题。
在 C++11 标准中,具有静态或线程存储持续时间的块作用域变量必须在任何其他初始化发生之前进行零初始化。当控制首先通过变量的声明时发生初始化。如果在初始化期间抛出异常,则认为该变量未初始化,并在下一次控制通过声明时重新尝试初始化。如果控制在初始化的同时进入声明,则并发执行将在初始化完成时阻塞。如果控件在初始化期间以递归方式重新输入声明,则行为未定义。默认情况下,Visual Studio 从 Visual Studio 2015 年开始实施此标准行为。可以通过设置 /Zc:threadSafeInit 编译器选项来显式指定此行为。 +
静态局部变量的线程安全初始化依赖于通用 C 运行-time 库 (UCRT) 中实现的代码。为避免依赖 UCRT,或保留 Visual Studio 2015 之前的 Visual Studio 版本的非线程安全初始化行为,请使用 /Zc:threadSafeInit- 选项。如果您知道不需要线程安全,请使用此选项围绕静态局部声明生成更小、更快的代码。
线程安全静态局部变量在内部使用线程局部存储 (TLS) 以在静态已初始化时提供高效执行。此功能的实现依赖于 Windows 操作系统在 Windows Vista 和更高版本操作系统中的支持功能。 Windows XP、Windows Server 2003 和较旧的操作系统不支持此功能,因此无法获得效率优势。这些操作系统对可以加载的 TLS 部分的数量也有下限。超过 TLS 部分限制可能会导致崩溃。如果这是您的代码中的问题,尤其是在旧操作系统上必须 运行 的代码中,请使用 /Zc:threadSafeInit- 禁用线程安全初始化代码。
我最初在 ReverseEngineering StackExchange 上 post 编辑了这个,但不知道它到底属于哪里。我还是决定 post 在这里。
最近,Microsoft Visual Studio 2015 编译器终于符合 C++ 标准要求,为函数局部静态生成线程安全代码。在大多数情况下,这工作得很好,但我 运行 在 Windows XP 上遇到了以下 3 条指令导致崩溃的情况:
mov eax,dword ptr fs:[0000002Ch]
mov ecx,dword ptr [MyModule!_tls_index (102eea44)]
mov ecx,dword ptr [eax+ecx*4]
显然,编译器似乎通过首先插入当前线程的 TLS 插槽来实现线程安全。 fs:2Ch
应该根据文档导致 TLS 数组。但是在 Windows XP 上,似乎没有设置 fs:2Ch
。这为我返回了 0,下一条指令也是如此(_tls_index
也是 0。)这导致第三条指令在访问无效内存时崩溃。
有人知道为什么 fs:2Ch
可能无法在 Windows XP 上设置吗?我们的代码中都使用了函数局部静态,我无法想象没有其他人 运行 参与其中。
更新
我已经仔细考虑了我在这个问题上应用的每个标签。请不要添加或删除任何内容。
逆向工程 SE 的 Peter Ferrie 巧妙地回答了这个问题。
在 C++11 标准中,具有静态或线程存储持续时间的块作用域变量必须在任何其他初始化发生之前进行零初始化。当控制首先通过变量的声明时发生初始化。如果在初始化期间抛出异常,则认为该变量未初始化,并在下一次控制通过声明时重新尝试初始化。如果控制在初始化的同时进入声明,则并发执行将在初始化完成时阻塞。如果控件在初始化期间以递归方式重新输入声明,则行为未定义。默认情况下,Visual Studio 从 Visual Studio 2015 年开始实施此标准行为。可以通过设置 /Zc:threadSafeInit 编译器选项来显式指定此行为。 + 静态局部变量的线程安全初始化依赖于通用 C 运行-time 库 (UCRT) 中实现的代码。为避免依赖 UCRT,或保留 Visual Studio 2015 之前的 Visual Studio 版本的非线程安全初始化行为,请使用 /Zc:threadSafeInit- 选项。如果您知道不需要线程安全,请使用此选项围绕静态局部声明生成更小、更快的代码。 线程安全静态局部变量在内部使用线程局部存储 (TLS) 以在静态已初始化时提供高效执行。此功能的实现依赖于 Windows 操作系统在 Windows Vista 和更高版本操作系统中的支持功能。 Windows XP、Windows Server 2003 和较旧的操作系统不支持此功能,因此无法获得效率优势。这些操作系统对可以加载的 TLS 部分的数量也有下限。超过 TLS 部分限制可能会导致崩溃。如果这是您的代码中的问题,尤其是在旧操作系统上必须 运行 的代码中,请使用 /Zc:threadSafeInit- 禁用线程安全初始化代码。