我最初在ReverseEngineering StackExchange上发布了这个,但不确切知道它属于哪里。无论如何我决定把它发布在这里。
最近,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.)这导致第3条指令在访问无效内存时爆炸。
有人知道为什么在Windows XP上可能没有设置fs:2Ch
吗?函数局部静态在我们的代码中使用,我无法想象没有其他人遇到这个。
更新
我仔细考虑了我已经应用于这个问题的每个标签。请不要添加或删除任何内容。
答案 0 :(得分:0)
彼得·费里在逆向工程SE上很好地回答了这个问题。
答案 1 :(得分:0)
在C ++ 11标准中,具有静态或线程存储持续时间的块作用域变量必须在进行任何其他初始化之前进行零初始化。当控件首次通过变量声明时,就会发生初始化。如果在初始化期间抛出异常,则该变量被视为未初始化,并且在下次控制通过声明时重新尝试初始化。如果控制与初始化同时进入声明,则初始化完成时并发执行块。如果控件在初始化期间以递归方式重新输入声明,则行为未定义。默认情况下,从Visual Studio 2015开始的Visual Studio实现此标准行为。可以通过设置/ Zc:threadSafeInit编译器选项显式指定此行为。 + 静态局部变量的线程安全初始化依赖于在Universal C运行时库(UCRT)中实现的代码。若要避免依赖于UCRT,或者在Visual Studio 2015之前保留Visual Studio版本的非线程安全初始化行为,请使用/ Zc:threadSafeInit-选项。如果您知道不需要线程安全性,请使用此选项生成围绕静态本地声明的稍小,更快的代码。 线程安全的静态局部变量在内部使用线程局部存储(TLS),以便在静态初始化时提供高效的执行。此功能的实现依赖于Windows Vista和更高版本操作系统中的Windows操作系统支持功能。 Windows XP,Windows Server 2003和较旧的操作系统没有此支持,因此它们无法获得效率优势。这些操作系统对可加载的TLS部分的数量也有一个下限。超过TLS部分限制可能导致崩溃。如果这是您的代码中的问题,特别是在必须在较旧的操作系统上运行的代码中,请使用/ Zc:threadSafeInit-来禁用线程安全的初始化代码。