静态初始化访问冲突

时间:2015-09-11 06:48:21

标签: c++ visual-c++ c++-cli

我正在使用Windows 7上的Visual Studio 2015创建一个应用程序。该应用程序有一个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类库" 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匹配,后者使用线程本地存储作为参考实现中的性能优化。崩溃时,线程本地存储返回0

这可能是Windows Server 2003上管理和初始化线程本地存储的运行时环境的错误吗?

编辑2

通过Microsoft Connect报告为错误:Bug report

1 个答案:

答案 0 :(得分:27)

这是Microsoft在Microsoft Connect上发布到bug report的答案:

  

Windows Server 2003和Windows XP在动态加载使用线程本地存储的DLL(通过LoadLibrary)方面存在问题,这是在静态本地已初始化时内部用于提供高效执行的线程安全静态。由于这些系统不受支持,因此为这些系统创建补丁极不可能像Vista和更新的操作系统中那样添加此支持,我们不愿意惩罚支持操作系统的性能以提供此功能。旧的不支持的功能。

     

要解决此问题,您可以使用/ Zc:threadSafeInit-来禁用线程安全的初始化代码,这将避免线程局部变量。当然,通过这样做,初始化代码将恢复为VS2013模式并且不是线程安全的,因此只有在不依赖于本地静态的线程安全性时,此选项才可行。