如何 detect/find 64 位结构对齐问题
How to detect/find 64 bit struct alignment issues
我们有一个相当旧的代码库,我们正在迁移到 64 位以使用一些新的第 3 方 64 位工具。我们 运行 Windows 7,使用 VS2010,代码用 C++ 编写。
过去两天,我们试图找出成员变量未设置为预期值的问题。使用 VS2010 内存调试器,我们能够看到成员变量内存位置偏移了四个字节。一些研究引导我们进行结构对齐和 64 位。
原来class中定义的第一个变量是一个32位整数,第二个变量是一个指针。如果我们将指针移动到 class 的顶部,所有成员变量都会正确对齐。这解决了问题。
我们还注意到
- 将四字节缓冲区添加到 class "fixed" 问题的顶部
- 将包对齐设置为四个字节也是 "fixed" 问题。
问题是,如何检测这些错误?我们将警告级别设置为 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 位 C/C++ 应用程序进行了大量搜索。正如 Andrey Cpp 指出并链接到的那样,存在基于成员变量的顺序和大小的结构对齐问题。不幸的是 PVS-Studio 没有指出这个问题。我不认为这是 PVS-Studio 上的错误。进一步的研究和反复试验使我们尝试在 VS2013 中构建项目。这个问题在 VS2013 中消失了。我能找到的最接近解释为什么它在 VS2013 中工作的是 here.
On x64, the object layout of a class may change from previous releases. If it has a virtual function but it doesn’t have a base class that has a virtual function, the object model of the compiler inserts a pointer to a virtual function table after the data member layout. This means the layout may not be optimal in all cases. In previous releases, an optimization for x64 would try to improve the layout for you, but because it failed to work correctly in complex code situations, it was removed in Visual C++ in Visual Studio 2013.
它看起来是 VS2010 中的一个错误,已在 VS2013 中修复。 VS2013 中的内存调试器显示了我所期望的对齐方式。
我们有一个相当旧的代码库,我们正在迁移到 64 位以使用一些新的第 3 方 64 位工具。我们 运行 Windows 7,使用 VS2010,代码用 C++ 编写。
过去两天,我们试图找出成员变量未设置为预期值的问题。使用 VS2010 内存调试器,我们能够看到成员变量内存位置偏移了四个字节。一些研究引导我们进行结构对齐和 64 位。
原来class中定义的第一个变量是一个32位整数,第二个变量是一个指针。如果我们将指针移动到 class 的顶部,所有成员变量都会正确对齐。这解决了问题。
我们还注意到
- 将四字节缓冲区添加到 class "fixed" 问题的顶部
- 将包对齐设置为四个字节也是 "fixed" 问题。
问题是,如何检测这些错误?我们将警告级别设置为 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 位 C/C++ 应用程序进行了大量搜索。正如 Andrey Cpp 指出并链接到的那样,存在基于成员变量的顺序和大小的结构对齐问题。不幸的是 PVS-Studio 没有指出这个问题。我不认为这是 PVS-Studio 上的错误。进一步的研究和反复试验使我们尝试在 VS2013 中构建项目。这个问题在 VS2013 中消失了。我能找到的最接近解释为什么它在 VS2013 中工作的是 here.
On x64, the object layout of a class may change from previous releases. If it has a virtual function but it doesn’t have a base class that has a virtual function, the object model of the compiler inserts a pointer to a virtual function table after the data member layout. This means the layout may not be optimal in all cases. In previous releases, an optimization for x64 would try to improve the layout for you, but because it failed to work correctly in complex code situations, it was removed in Visual C++ in Visual Studio 2013.
它看起来是 VS2010 中的一个错误,已在 VS2013 中修复。 VS2013 中的内存调试器显示了我所期望的对齐方式。