GetCPUDescriptorHandleForHeapStart 堆栈损坏
GetCPUDescriptorHandleForHeapStart stack corruption
我在使用 DirectX 12.0 编程时偶然发现了一个相当不寻常的问题。迄今为止没有任何研究具有洞察力。
我正在使用 C(不是 C++)编程。看起来官方的 DirectX 12 headers 支持 C 和 C++ 的绑定,但是编写 C 等效代码来执行上述任务会导致崩溃,而 C++ 不会。我不相信错误是我的。
详情如下:我的 D3D12 device-initialisation 程序中有以下代码块:
/* Get a handle to the memory location in the render target
view heap to identify where the render target views will be
located for the two back buffers */
hRTV = pThis->pRTVHeap->lpVtbl->GetCPUDescriptorHandleForHeapStart(pThis->pRTVHeap);
其中 hRTV 代表'Handle to Render Target View' (D3D12_CPU_DESCRIPTOR_HANDLE) 和 pRTVHeap 代表'Pointer to Render Target View Heap' (ID3D12DescriptorHeap).
这是 C++ 等效项 - 这工作正常:
/* Get a handle to the memory location in the render target
view heap to identify where the render target views will be
located for the two back buffers */
hRTV = this->pRTVHeap->GetCPUDescriptorHandleForHeapStart();
不存在 compile-time 错误,但在运行时,在 C 中调用此方法 (GetCPUDescriptorHandleForHeapStart) 会触发堆栈损坏(%ESP 被替换为 4 个字节)。
我检查了方法的反汇编并记下了 RET (return) 指令:
mov edi,edi
push ebp
mov ebp,esp
mov ecx,dword ptr [ebp+8]
mov eax,dword ptr [ecx+2Ch]
cmp dword ptr [eax],2
jne 5029004A
mov eax,dword ptr [ebp+0Ch]
mov ecx,dword ptr [ecx+28h]
mov dword ptr [eax],ecx
jmp 50290055
push dword ptr [ebp+0Ch]
call 5029005E
mov eax,dword ptr [ebp+0Ch]
pop ebp
ret 8
对于那些熟悉汇编和(希望如此)COM(组件 Object 模型)objects 的 __stdcall
调用约定的人,this
(或等效的)指针,在堆栈上传递,是该方法的第一个参数(在本例中,应该是唯一的参数),这种做法使 COM objects 能够访问自己的数据。
下面的代码片段(也在上面显示)引起了我的困惑,当运行时抛出 'misaligned stack pointer / stack corruption' (%ESP) 错误时,这是理所当然的:
ret 8
在这种情况下只应传递一个参数(this
指针)。一个指针的大小(在32位系统上-我的目标架构是x86)是4个字节(32位),那么为什么被调用者清理堆栈上的8个字节?
我称这是一个错误是否正确?是否需要将此问题告知 Microsoft?我错了吗?
感谢您的宝贵时间,希望比我知识渊博的人能赐教。请不要提出 age-old 比 C 更喜欢 C++ 的论点。
解决方案
D3D12.DLL 的调试符号足以显示。命名约定(例如 ID3D12DescriptionHeap::GetCPUDescriptorHandleForHeapStart
)强烈表明 DLL 是用 C++ 编写的。 (隐藏的)第二个参数确实传递给了该方法 - 指向输出结构 D3D12_CPU_DESCRIPTOR_HANDLE
的指针(仅包含一个整数,别名为结构。我不知道他们为什么这样做)。我忘记了 C++ 与 C 的不同之处在于 C++ 可以 return 结构作为 return 值,并且结构不能作为 return 通过累加器 (%EAX) 寄存器传递,因此它必须作为堆栈上的指针传递给被调用者。
问题是错误的 C 绑定(Microsoft header 错误)。建议进行以下修复:
旧代码:
D3D12_CPU_DESCRIPTOR_HANDLE (
STDMETHODCALLTYPE *GetCPUDescriptorHandleForHeapStart )(
ID3D12DescriptorHeap * This);
替换为:
void ( STDMETHODCALLTYPE *GetCPUDescriptorHandleForHeapStart )(
ID3D12DescriptorHeap *This, D3D12_CPU_DESCRIPTOR_HANDLE *pOut);
谢谢。
编辑
此 header 错误已随着 10.0.22000.0 Windows SDK 的发布得到修复。
这最终在 Windows SDK 10.0.20348.0 中得到修复。从 header 的变化来看,至少各种 GetDesc 函数也受到了影响。
我在使用 DirectX 12.0 编程时偶然发现了一个相当不寻常的问题。迄今为止没有任何研究具有洞察力。
我正在使用 C(不是 C++)编程。看起来官方的 DirectX 12 headers 支持 C 和 C++ 的绑定,但是编写 C 等效代码来执行上述任务会导致崩溃,而 C++ 不会。我不相信错误是我的。
详情如下:我的 D3D12 device-initialisation 程序中有以下代码块:
/* Get a handle to the memory location in the render target
view heap to identify where the render target views will be
located for the two back buffers */
hRTV = pThis->pRTVHeap->lpVtbl->GetCPUDescriptorHandleForHeapStart(pThis->pRTVHeap);
其中 hRTV 代表'Handle to Render Target View' (D3D12_CPU_DESCRIPTOR_HANDLE) 和 pRTVHeap 代表'Pointer to Render Target View Heap' (ID3D12DescriptorHeap).
这是 C++ 等效项 - 这工作正常:
/* Get a handle to the memory location in the render target
view heap to identify where the render target views will be
located for the two back buffers */
hRTV = this->pRTVHeap->GetCPUDescriptorHandleForHeapStart();
不存在 compile-time 错误,但在运行时,在 C 中调用此方法 (GetCPUDescriptorHandleForHeapStart) 会触发堆栈损坏(%ESP 被替换为 4 个字节)。
我检查了方法的反汇编并记下了 RET (return) 指令:
mov edi,edi
push ebp
mov ebp,esp
mov ecx,dword ptr [ebp+8]
mov eax,dword ptr [ecx+2Ch]
cmp dword ptr [eax],2
jne 5029004A
mov eax,dword ptr [ebp+0Ch]
mov ecx,dword ptr [ecx+28h]
mov dword ptr [eax],ecx
jmp 50290055
push dword ptr [ebp+0Ch]
call 5029005E
mov eax,dword ptr [ebp+0Ch]
pop ebp
ret 8
对于那些熟悉汇编和(希望如此)COM(组件 Object 模型)objects 的 __stdcall
调用约定的人,this
(或等效的)指针,在堆栈上传递,是该方法的第一个参数(在本例中,应该是唯一的参数),这种做法使 COM objects 能够访问自己的数据。
下面的代码片段(也在上面显示)引起了我的困惑,当运行时抛出 'misaligned stack pointer / stack corruption' (%ESP) 错误时,这是理所当然的:
ret 8
在这种情况下只应传递一个参数(this
指针)。一个指针的大小(在32位系统上-我的目标架构是x86)是4个字节(32位),那么为什么被调用者清理堆栈上的8个字节?
我称这是一个错误是否正确?是否需要将此问题告知 Microsoft?我错了吗?
感谢您的宝贵时间,希望比我知识渊博的人能赐教。请不要提出 age-old 比 C 更喜欢 C++ 的论点。
解决方案
D3D12.DLL 的调试符号足以显示。命名约定(例如 ID3D12DescriptionHeap::GetCPUDescriptorHandleForHeapStart
)强烈表明 DLL 是用 C++ 编写的。 (隐藏的)第二个参数确实传递给了该方法 - 指向输出结构 D3D12_CPU_DESCRIPTOR_HANDLE
的指针(仅包含一个整数,别名为结构。我不知道他们为什么这样做)。我忘记了 C++ 与 C 的不同之处在于 C++ 可以 return 结构作为 return 值,并且结构不能作为 return 通过累加器 (%EAX) 寄存器传递,因此它必须作为堆栈上的指针传递给被调用者。
问题是错误的 C 绑定(Microsoft header 错误)。建议进行以下修复:
旧代码:
D3D12_CPU_DESCRIPTOR_HANDLE (
STDMETHODCALLTYPE *GetCPUDescriptorHandleForHeapStart )(
ID3D12DescriptorHeap * This);
替换为:
void ( STDMETHODCALLTYPE *GetCPUDescriptorHandleForHeapStart )(
ID3D12DescriptorHeap *This, D3D12_CPU_DESCRIPTOR_HANDLE *pOut);
谢谢。
编辑
此 header 错误已随着 10.0.22000.0 Windows SDK 的发布得到修复。
这最终在 Windows SDK 10.0.20348.0 中得到修复。从 header 的变化来看,至少各种 GetDesc 函数也受到了影响。