【问题标题】:How to detect/find 64 bit struct alignment issues如何检测/查找 64 位结构对齐问题
【发布时间】:2015-04-03 02:55:25
【问题描述】:

我们有一个相当旧的代码库,我们正在迁移到 64 位以利用一些新的 3rd 方 64 位工具。我们在 Windows 7 上运行,使用 VS2010,代码用 C++ 编写。

过去两天,我们试图找出一个成员变量未设置为预期值的问题。使用 VS2010 内存调试器,我们能够看到成员变量内存位置偏移了四个字节。一些研究引导我们使用 struct 对齐和 64 位。

原来类中定义的第一个变量是一个 32 位整数,第二个变量是一个指针。如果我们将指针移到类的顶部,则所有成员变量都正确对齐。这解决了问题。

我们还注意到

  1. 在类的顶部添加一个四字节缓冲区“修复”了问题
  2. 将包对齐设置为四个字节也“修复”了该问题。

问题是,您如何检测这些错误?我们将警告级别设置为 EnabelAllWarnings 并尝试了几个静态 c++ 分析器,但都没有成功。

发生这种情况的一个例子。由于保密协议,我无法显示另一个。

class CTaskBar
{
public:

    bool CreateTaskbarIcon( HWND hWnd,
                        HINSTANCE hInstance,
                        UINT unNotifyMessage,
                        UINT unIcon,
                        LPCTSTR szCaption)
    {
        if (hWnd == NULL)
            return false;
        if (hInstance == NULL)
            return false;

        m_hWnd = hWnd;
        m_hInstance = hInstance;

        m_NIDTaskbarIconData.cbSize = sizeof(NOTIFYICONDATA);
        m_NIDTaskbarIconData.hWnd   = m_hWnd;
        m_NIDTaskbarIconData.uID    = IDI_TASKBARICON;

        if ( unIcon ) {

        if ( m_hTaskbarIcon )
            DestroyIcon( m_hTaskbarIcon );

        m_hTaskbarIcon = ::LoadIcon( m_hInstance, MAKEINTRESOURCE( unIcon ) );

        m_NIDTaskbarIconData.hIcon  = m_hTaskbarIcon;
        m_NIDTaskbarIconData.uFlags |= NIF_ICON;
    }

    if ( unNotifyMessage ) {
        m_NIDTaskbarIconData.uCallbackMessage = unNotifyMessage;
        m_NIDTaskbarIconData.uFlags |= NIF_MESSAGE;
    }

    if ( szCaption ) {
        strcpy(m_NIDTaskbarIconData.szTip, szCaption);
        m_NIDTaskbarIconData.uFlags |= NIF_TIP;
    }

    // The Shell_NotifyIcon call will add the icon to the task bar.
    if ( Shell_NotifyIcon(NIM_ADD, &m_NIDTaskbarIconData) )
        return true;

    return false;
}

private:
    NOTIFYICONDATA  m_NIDTaskbarIconData;
    HINSTANCE       m_hInstance;
    HWND            m_hWnd;
    HMENU           m_hTaskbarMenu;
    HICON           m_hTaskbarIcon;
};

基本上,应用程序在 Windows API 调用深处的 Shell_NotifyIcon 处崩溃。如果我们查看内存调试器,我们可以看到内存被设置,但偏移了四个字节。如果我将 m_NIDTaskbarIconData 移动到 m_hTaskbarIcon 之后。在内存调试器中一切看起来都是正确的,没有崩溃。

【问题讨论】:

  • 当你编译为 64 位时,所有带有指针的结构都应该至少对齐到 8 个字节。是否正在进行一些编译指示包装? (编辑:哦,我刚看到你的第二颗子弹。正在打包。)
  • 这对我来说闻起来像是未定义的行为(甚至可能违反了一个定义规则),如果是这种情况,静态检查器通常很难检测到。您能否提供有关该问题的更多具体细节?
  • @Mysticial 从某种意义上说,我们知道我们可以设置它并且事情似乎会自行解决,因此正在打包。这两个应用程序都进行了一些打包,因为它们通过 TCP 发送和接收二进制打包消息。当我们意识到设置打包让事情消失时,我们确实回顾了使用打包的单个头文件以确保在底部重置打包,并使用 #pragma pack(show) 重新检查以进行验证。
  • 我没有看到好的信号。也许没有完全初始化 NOTIFYICONDATA 并且没有使用 NIM_SETVERSION。如果它实际上在 shell 内崩溃,那么你有一个很好的提示,它正在访问已释放的内存,或者你现在正在目睹 32 位中已经存在的潜在堆损坏问题的副作用版本。

标签: c++ visual-studio-2010 64-bit


【解决方案1】:

所以我们确实对 64 位 C/C++ 应用程序进行了大量搜索。正如 Andrey Cpp 指出并链接到的,存在基于成员变量的顺序和大小的结构对齐问题。不幸的是,PVS-Studio 没有指出这个问题。我认为这不是 PVS-Studio 的错误。进一步的研究和反复试验,使我们尝试在 VS2013 中构建项目。这个问题在 VS2013 中消失了。我能找到的最接近解释为什么它在 VS2013 中工作的东西是here

在 x64 上,类的对象布局可能与以前的版本有所不同。如果它有一个虚函数但它没有一个具有虚函数的基类,编译器的对象模型会在数据成员布局之后插入一个指向虚函数表的指针。这意味着布局可能并非在所有情况下都是最佳的。在以前的版本中,针对 x64 的优化会尝试为您改进布局,但由于它无法在复杂代码情况下正常工作,在 Visual Studio 2013 的 Visual C++ 中已将其删除

它看起来是 VS2010 中的一个错误,已在 VS2013 中修复。 VS2013 中的内存调试器显示了我所期望的对齐方式。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多