GetCPUDescriptorHandleForHeapStart堆栈损坏
GetCPUDescriptorHandleForHeapStart stack corruption
在使用DirectX 12.0编程时,我偶然发现了一个非常不寻常的问题。到目前为止,还没有任何研究具有深刻见解。
我正在用C(而不是C++)编程。官方的DirectX 12标头似乎同时支持C和C++的绑定,但是编写与C等价的代码来执行所述任务会导致崩溃,而C++则不会。我不相信这个错误是我的。
以下是详细信息:我在D3D12设备初始化程序中有以下代码块:
/* 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代表"句柄到渲染目标视图"(D3D12_CPU_DESCRIPTOR_Handle),pRTVHeap表示"PointertoRender 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();
不存在编译时错误,但在运行时,在C中调用此方法(GetCPUDescriptorHandleForHeapStart)会触发堆栈损坏(%ESP被替换了4个字节)。
我检查了该方法的反汇编,并注意到RET(返回)指令:
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(组件对象模型)对象的__stdcall
调用约定的人来说,在堆栈上传递的this
(或等效的)指针是该方法的第一个参数(在这种情况下,应该是唯一的参数),这种做法使COM对象能够访问自己的数据。
下面的代码片段(也显示在上面)引起了我的困惑,当运行时抛出"未对齐的堆栈指针/堆栈损坏"(%ESP)错误时,这是理所当然的:
ret 8
在这种情况下,只应传递一个参数(this
指针)。指针的大小(在32位系统上,我的目标体系结构是x86)是4个字节(32位),那么被调用者为什么要清理堆栈上的8个字节呢?
我称之为bug是对的吗?是否需要将此问题通知Microsoft?我错了吗?
感谢您抽出时间,我希望任何比我更有知识的人都能启发我。请不要提出喜欢C++而不是C的古老论点。
解决方案
D3D12.DLL的调试符号只显示了足够的内容。命名约定(例如ID3D12DescriptionHeap::GetCPUDescriptorHandleForHeapStart
)有力地表明DLL是用C++编写的。一个(隐藏的)第二个参数确实被传递给了这个方法——一个指向输出结构D3D12_CPU_DESCRIPTOR_HANDLE
的指针(只包括一个整数,别名为一个结构。我不知道他们为什么这么做)。我忘记了C++与C的不同之处在于,C++可以将结构作为返回值返回,并且结构不能通过累加器(%EAX)寄存器作为返回值传递,因此它必须作为堆栈上指向被调用者的指针传递。
问题是C绑定不正确(Microsoft头错误)。提出以下修复方案:
旧代码:
D3D12_CPU_DESCRIPTOR_HANDLE (
STDMETHODCALLTYPE *GetCPUDescriptorHandleForHeapStart )(
ID3D12DescriptorHeap * This);
替换为:
void ( STDMETHODCALLTYPE *GetCPUDescriptorHandleForHeapStart )(
ID3D12DescriptorHeap *This, D3D12_CPU_DESCRIPTOR_HANDLE *pOut);
谢谢。
编辑
此标头错误已随10.0.2220.00 Windows SDK的发布而修复。
这最终在Windows SDK 10.0.20348.0中得到了修复。从标题的变化来看,至少各种GetDesc函数也受到了影响。
- gdb错误:Backtrace已停止:上一帧与此帧相同(堆栈已损坏?)
- 变量周围的堆栈'...'已损坏
- 运行时检查失败 #2 变量"A"周围的堆栈已损坏
- 运行时检查失败 #2 - 变量"e"周围的堆栈已损坏。发生
- 变量周围的堆栈'sortArray'已损坏
- 变量周围的堆栈'folderPath'已损坏
- 运行时检查失败 #2 - 变量周围的堆栈'...'已损坏
- 运行时检查失败 #2 - 变量"l1"周围的堆栈已损坏
- 围绕变量的堆栈是损坏的 c++
- C++:此代码可以编译,但引发运行时检查失败 #2 - 围绕变量周围的堆栈'num'已损坏。发生
- 数组变量周围的堆栈已损坏
- 运行时检查失败 - 变量周围的堆栈已损坏
- 我的 c 字符串复制函数正在损坏其他变量的堆栈
- 调试:运行时检查失败 #2 - 变量"LoggerThread"周围的堆栈已损坏
- 将大双精度转换为无符号 int 期间堆栈损坏
- 运行时检查失败 #2 - 变量周围的堆栈'k'已损坏
- 运行时检查失败 #2 - 变量"数字选择"周围的堆栈已损坏
- 数组问题:变量周围的堆栈'arr'已损坏
- scanf int8_t损坏堆栈
- 如何在不损坏堆栈的情况下将 varargs 与__stdcall一起使用