静态初始化访问冲突
Access violation on static initialization
我正在 Visual Studio 2015 Windows 7 开发一个应用程序。该应用程序有一个 C# 前端、一个 C++ CLR 包装器和 C++ 本机代码。
我的应用程序在使用 C++ 本机代码在函数范围内初始化静态变量时因访问冲突而崩溃。但仅限于 Windows Server 2003 Enterprise SP2 而不是 Windows 7 或 Windows Server 2012。我知道 Windows Server 2003 不受支持,但我必须针对它平台再持续几个月,Visual Studio 2015 年提供了一个平台工具集来定位它。
我创建了一个可重现的小示例,您可以在最后找到它。
崩溃只发生在涉及的所有三个部分(C#、C++ CLR、C++)中。如果我删除任何,崩溃就消失了。
只有定义了自定义构造函数才会发生崩溃。如果我删除构造函数,崩溃就消失了。
我不是汇编专家,但对我来说,崩溃似乎是由检查是否需要静态初始化的代码引起的。甚至没有调用构造函数。
我的问题是:为什么它会在 Windows Server 2003 上崩溃?我是否遗漏了一些重要的项目设置?
错误信息
Unhandled exception at 0x1000167E (Native.dll) in Managed.exe.dmp: 0xC0000005: Access violation reading location 0x00000000.
Visual C# 控制台应用程序"Managed.exe"
Program.cs
// Target framework: .NET Framework 4
// Platform target: x86
using System;
namespace Managed
{
class Program
{
static void Main(string[] args)
{
Console.Write("Press enter to start test...");
Console.ReadLine();
Native.Wrapper wrapper = new Native.Wrapper();
Console.WriteLine("Test was successful");
Console.Write("Press enter to exit...");
Console.ReadLine();
}
}
}
Visual C++ CLR Class 库 "Native.dll"
Wrapper.hpp
#pragma once
namespace Native
{
public ref class Wrapper
{
public:
Wrapper();
}; // public ref class Wrapper
} // namespace Native
Wrapper.cpp
// Platform Toolset: Visual Studio 2015 - Windows XP (v140_xp)
// Common Language Runtime Support: Common Language Runtime Support (/clr)
// .NET Target Framework Version: v4.0
// Warning Level: Level4
// Treat Warnings As Errors: Yes (/WX)
// Precompiled Header: Not Using Precompiled Headers
// SubSystem: Console (/SUBSYSTEM:CONSOLE)
// Optimization: Disabled (/Od)
#pragma once
#include "Wrapper.hpp"
#include "Caller.hpp"
namespace Native
{
Wrapper::Wrapper()
{
Caller* caller = new Caller();
delete caller;
}
} // namespace Native
Caller.hpp
#pragma once
namespace Native
{
class Caller
{
public:
Caller();
}; // class Caller
} // namespace Native
Caller.cpp
// Platform Toolset: Visual Studio 2015 - Windows XP (v140_xp)
// Common Language Runtime Support: No Common Language RunTime Support
// Warning Level: Level4
// Treat Warnings As Errors: Yes (/WX)
// Precompiled Header: Not Using Precompiled Headers
// SubSystem: Console (/SUBSYSTEM:CONSOLE)
// Optimization: Disabled (/Od)
#include "Caller.hpp"
#include "Singleton.hpp"
namespace Native
{
Caller::Caller()
{
Singleton::GetInstance()->DoSomething();
}
} // namespace Native
Singleton.hpp
#pragma once
#include <iostream>
namespace Native
{
class Singleton
{
public:
Singleton() // !!! remove constructor to prevent crash !!!
{ }
static Singleton* GetInstance()
{
static Singleton Instance; // !!! crashes here !!!
return &Instance;
}
void DoSomething()
{
std::wcout << L"Doing something...\n";
}
}; // class Singleton
} // namespace Native
反汇编
static Singleton* GetInstance()
{
10001650 push ebp
10001651 mov ebp,esp
10001653 push 0FFFFFFFFh
10001655 push 10006A8Ch
1000165A mov eax,dword ptr fs:[00000000h]
10001660 push eax
10001661 mov eax,dword ptr ds:[1001B334h]
10001666 xor eax,ebp
10001668 push eax
10001669 lea eax,[ebp-0Ch]
1000166C mov dword ptr fs:[00000000h],eax
static Singleton Instance;
10001672 mov eax,dword ptr ds:[1001B5D0h]
10001677 mov ecx,dword ptr fs:[2Ch]
1000167E mov edx,dword ptr [ecx+eax*4] // !!! access violation here !!!
10001681 mov eax,dword ptr ds:[1001B5A4h]
10001686 cmp eax,dword ptr [edx+4]
1000168C jle Native::Singleton::GetInstance+79h (100016C9h)
寄存器
EAX = 00000000 EBX = 00000000 ECX = 00000000 EDX = 006A0003 ESI = 001647C8
EDI = 0012F3BC EIP = 1000167E ESP = 0012F394 EBP = 0012F3A4 EFL = 00010282
编辑 1
在没有发生崩溃的地方进行本地调试时,程序集可以看到更多符号:
static Singleton* GetInstance()
{
0FBD1650 push ebp
0FBD1651 mov ebp,esp
0FBD1653 push 0FFFFFFFFh
0FBD1655 push offset __ehhandler$?GetInstance@Singleton@Native@@SAPAV12@XZ (0FBD86BCh)
0FBD165A mov eax,dword ptr fs:[00000000h]
0FBD1660 push eax
0FBD1661 mov eax,dword ptr [___security_cookie (0FBF03CCh)]
0FBD1666 xor eax,ebp
0FBD1668 push eax
0FBD1669 lea eax,[ebp-0Ch]
0FBD166C mov dword ptr fs:[00000000h],eax
static Singleton Instance;
0FBD1672 mov eax,dword ptr [__tls_index (0FBF0668h)]
0FBD1677 mov ecx,dword ptr fs:[2Ch]
0FBD167E mov edx,dword ptr [ecx+eax*4]
0FBD1681 mov eax,dword ptr [TSS0<`template-parameter-2',Native::Singleton::tInstance,Native::Singleton * * const volatile,void,int, ?? &> (0FBF063Ch)]
0FBD1686 cmp eax,dword ptr [edx+4]
0FBD168C jle Native::Singleton::GetInstance+79h (0FBD16C9h)
符号 __tls_index
似乎属于某个线程本地存储(从名称中猜测)。这与 Magic statics 匹配,它使用线程本地存储作为参考实现中的性能优化。崩溃时,线程本地存储 returns 0
.
这可能是 Windows Server 2003 上管理和初始化线程本地存储的运行时环境的错误吗?
编辑 2
通过 Microsoft Connect 报告为错误:Bug report
您的构造函数自 public 以来未被删除。
将其移至您的 class 声明的私有部分。
这是 Microsoft 在 Microsoft Connect 上发布到我的 bug report 的答案:
Windows Server 2003 and Windows XP have problems with dynamically loading a DLL (via LoadLibrary) that uses thread-local storage, which is what thread-safe statics use internally to provide efficient execution when the static local has already been initialized. As these systems are out of support, it is extremely unlikely for a patch to be created for those systems to add this support as is present in Vista and newer OSes, and we are reluctant to penalize the performance on in-support OSes to provide this functionality to the old out-of-support ones.
To work around the issue you can use /Zc:threadSafeInit- to disable the thread-safe initialization code and this will avoid the thread-local variable. Of course by doing so the initialization code reverts back to the VS2013 mode and is not thread-safe, so this option is only viable if you don't rely on the thread-safety of local statics.
我正在 Visual Studio 2015 Windows 7 开发一个应用程序。该应用程序有一个 C# 前端、一个 C++ CLR 包装器和 C++ 本机代码。
我的应用程序在使用 C++ 本机代码在函数范围内初始化静态变量时因访问冲突而崩溃。但仅限于 Windows Server 2003 Enterprise SP2 而不是 Windows 7 或 Windows Server 2012。我知道 Windows Server 2003 不受支持,但我必须针对它平台再持续几个月,Visual Studio 2015 年提供了一个平台工具集来定位它。
我创建了一个可重现的小示例,您可以在最后找到它。
崩溃只发生在涉及的所有三个部分(C#、C++ CLR、C++)中。如果我删除任何,崩溃就消失了。
只有定义了自定义构造函数才会发生崩溃。如果我删除构造函数,崩溃就消失了。
我不是汇编专家,但对我来说,崩溃似乎是由检查是否需要静态初始化的代码引起的。甚至没有调用构造函数。
我的问题是:为什么它会在 Windows Server 2003 上崩溃?我是否遗漏了一些重要的项目设置?
错误信息
Unhandled exception at 0x1000167E (Native.dll) in Managed.exe.dmp: 0xC0000005: Access violation reading location 0x00000000.
Visual C# 控制台应用程序"Managed.exe"
Program.cs
// Target framework: .NET Framework 4
// Platform target: x86
using System;
namespace Managed
{
class Program
{
static void Main(string[] args)
{
Console.Write("Press enter to start test...");
Console.ReadLine();
Native.Wrapper wrapper = new Native.Wrapper();
Console.WriteLine("Test was successful");
Console.Write("Press enter to exit...");
Console.ReadLine();
}
}
}
Visual C++ CLR Class 库 "Native.dll"
Wrapper.hpp
#pragma once
namespace Native
{
public ref class Wrapper
{
public:
Wrapper();
}; // public ref class Wrapper
} // namespace Native
Wrapper.cpp
// Platform Toolset: Visual Studio 2015 - Windows XP (v140_xp)
// Common Language Runtime Support: Common Language Runtime Support (/clr)
// .NET Target Framework Version: v4.0
// Warning Level: Level4
// Treat Warnings As Errors: Yes (/WX)
// Precompiled Header: Not Using Precompiled Headers
// SubSystem: Console (/SUBSYSTEM:CONSOLE)
// Optimization: Disabled (/Od)
#pragma once
#include "Wrapper.hpp"
#include "Caller.hpp"
namespace Native
{
Wrapper::Wrapper()
{
Caller* caller = new Caller();
delete caller;
}
} // namespace Native
Caller.hpp
#pragma once
namespace Native
{
class Caller
{
public:
Caller();
}; // class Caller
} // namespace Native
Caller.cpp
// Platform Toolset: Visual Studio 2015 - Windows XP (v140_xp)
// Common Language Runtime Support: No Common Language RunTime Support
// Warning Level: Level4
// Treat Warnings As Errors: Yes (/WX)
// Precompiled Header: Not Using Precompiled Headers
// SubSystem: Console (/SUBSYSTEM:CONSOLE)
// Optimization: Disabled (/Od)
#include "Caller.hpp"
#include "Singleton.hpp"
namespace Native
{
Caller::Caller()
{
Singleton::GetInstance()->DoSomething();
}
} // namespace Native
Singleton.hpp
#pragma once
#include <iostream>
namespace Native
{
class Singleton
{
public:
Singleton() // !!! remove constructor to prevent crash !!!
{ }
static Singleton* GetInstance()
{
static Singleton Instance; // !!! crashes here !!!
return &Instance;
}
void DoSomething()
{
std::wcout << L"Doing something...\n";
}
}; // class Singleton
} // namespace Native
反汇编
static Singleton* GetInstance()
{
10001650 push ebp
10001651 mov ebp,esp
10001653 push 0FFFFFFFFh
10001655 push 10006A8Ch
1000165A mov eax,dword ptr fs:[00000000h]
10001660 push eax
10001661 mov eax,dword ptr ds:[1001B334h]
10001666 xor eax,ebp
10001668 push eax
10001669 lea eax,[ebp-0Ch]
1000166C mov dword ptr fs:[00000000h],eax
static Singleton Instance;
10001672 mov eax,dword ptr ds:[1001B5D0h]
10001677 mov ecx,dword ptr fs:[2Ch]
1000167E mov edx,dword ptr [ecx+eax*4] // !!! access violation here !!!
10001681 mov eax,dword ptr ds:[1001B5A4h]
10001686 cmp eax,dword ptr [edx+4]
1000168C jle Native::Singleton::GetInstance+79h (100016C9h)
寄存器
EAX = 00000000 EBX = 00000000 ECX = 00000000 EDX = 006A0003 ESI = 001647C8
EDI = 0012F3BC EIP = 1000167E ESP = 0012F394 EBP = 0012F3A4 EFL = 00010282
编辑 1
在没有发生崩溃的地方进行本地调试时,程序集可以看到更多符号:
static Singleton* GetInstance()
{
0FBD1650 push ebp
0FBD1651 mov ebp,esp
0FBD1653 push 0FFFFFFFFh
0FBD1655 push offset __ehhandler$?GetInstance@Singleton@Native@@SAPAV12@XZ (0FBD86BCh)
0FBD165A mov eax,dword ptr fs:[00000000h]
0FBD1660 push eax
0FBD1661 mov eax,dword ptr [___security_cookie (0FBF03CCh)]
0FBD1666 xor eax,ebp
0FBD1668 push eax
0FBD1669 lea eax,[ebp-0Ch]
0FBD166C mov dword ptr fs:[00000000h],eax
static Singleton Instance;
0FBD1672 mov eax,dword ptr [__tls_index (0FBF0668h)]
0FBD1677 mov ecx,dword ptr fs:[2Ch]
0FBD167E mov edx,dword ptr [ecx+eax*4]
0FBD1681 mov eax,dword ptr [TSS0<`template-parameter-2',Native::Singleton::tInstance,Native::Singleton * * const volatile,void,int, ?? &> (0FBF063Ch)]
0FBD1686 cmp eax,dword ptr [edx+4]
0FBD168C jle Native::Singleton::GetInstance+79h (0FBD16C9h)
符号 __tls_index
似乎属于某个线程本地存储(从名称中猜测)。这与 Magic statics 匹配,它使用线程本地存储作为参考实现中的性能优化。崩溃时,线程本地存储 returns 0
.
这可能是 Windows Server 2003 上管理和初始化线程本地存储的运行时环境的错误吗?
编辑 2
通过 Microsoft Connect 报告为错误:Bug report
您的构造函数自 public 以来未被删除。 将其移至您的 class 声明的私有部分。
这是 Microsoft 在 Microsoft Connect 上发布到我的 bug report 的答案:
Windows Server 2003 and Windows XP have problems with dynamically loading a DLL (via LoadLibrary) that uses thread-local storage, which is what thread-safe statics use internally to provide efficient execution when the static local has already been initialized. As these systems are out of support, it is extremely unlikely for a patch to be created for those systems to add this support as is present in Vista and newer OSes, and we are reluctant to penalize the performance on in-support OSes to provide this functionality to the old out-of-support ones.
To work around the issue you can use /Zc:threadSafeInit- to disable the thread-safe initialization code and this will avoid the thread-local variable. Of course by doing so the initialization code reverts back to the VS2013 mode and is not thread-safe, so this option is only viable if you don't rely on the thread-safety of local statics.