GetCPUDescriptorHandleForHeapStart stack corruption
Asked Answered
F

2

13

I've stumbled upon a rather unusual problem while programming with DirectX 12.0. No research so far has been insightful.

I am programming using C (not C++). It would appear the official DirectX 12 headers support bindings for both C and C++, however writing C equivalent code to do a said task causes a crash whereas C++ does not. I don't believe the error is mine.

Here's the details: I have here in my D3D12 device-initialisation procedure the following block of code:

/* 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);

Where hRTV stands for 'Handle to Render Target View' (D3D12_CPU_DESCRIPTOR_HANDLE) and pRTVHeap stands for 'Pointer to Render Target View Heap' (ID3D12DescriptorHeap).

Here is the C++ equivalent - this works fine:

/* 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();

There are no compile-time errors present but at runtime, calling this method (GetCPUDescriptorHandleForHeapStart) in C triggers a stack corruption (%ESP is displaced by 4-bytes).

I examined the disassembly for the the method and made note of the RET (return) instruction:

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

For those familiar with assembly and (hopefully) the __stdcall calling convention of COM (Component Object Model) objects, the this (or equivalent) pointer, passed on the stack, is the first parameter (and, in this case, should be the only parameter) of the method, a practice that enables COM objects to access their own data.

The following code snippet (also shown above) is what elicits my confusion, and rightfully so when the runtime throws a 'misaligned stack pointer / stack corruption' (%ESP) error:

ret        8

Only one parameter should be passed in this case (this pointer). The size of a pointer (on a 32-bit system - my target architecture is x86) is 4 bytes (32 bits), so why does the callee clean up 8 bytes on the stack?

Am I right to call this a bug? Does Microsoft need to be informed of this issue? Am I wrong?

Thank you for your time and I hope anyone with more knowledge than I do can enlighten me. Please do not suggest the age-old argument of preferring C++ over C.

Feather answered 6/12, 2015 at 15:11 Comment(2)
Does the same thing happen for e.g. GetResourceAllocationInfo and other struct-returning methods? In any case, you should make your edit an answer and accept it (maybe even get the self-learner badge as well) to help increase visibility for people from the future.Mainmast
I shall investigate. I wasn't expecting a reply - excuse my late response. Having reported the issue to Microsoft, I've but received an acknowledgement. I have no idea whether anything is being done, or whether my issue is being considered. It should be easy to fix because, after all, it is only the header file that needs modification and not the API itself. But I shall investigate and return with my discoveries. I cannot guarantee anything, I am still a DirectX-12 newbie.Feather
P
3

This was finally fixed in Windows SDK 10.0.20348.0. At least the various GetDesc functions were also affected, judging from the header changes.

Poncho answered 1/10, 2021 at 21:36 Comment(1)
It's been six long years.Feather
F
13

SOLUTION

Debug symbols for D3D12.DLL reveal just enough. Naming conventions (e.g. ID3D12DescriptionHeap::GetCPUDescriptorHandleForHeapStart) are a strong indication that the DLL is written in C++. A (hidden) second parameter is indeed passed to the method - a pointer to the output structure D3D12_CPU_DESCRIPTOR_HANDLE (comprising nothing more than just an integer, aliased as a structure. I don't know why they do this). I forgot that C++ differs from C in that C++ can return structures as return values, and that structures cannot be passed as a return via the accumulator (%EAX) register, so it must be passed as a pointer on the stack to the callee.

The problem is bad C bindings (a Microsoft header bug). The following fix is proposed:

Old code:

D3D12_CPU_DESCRIPTOR_HANDLE (
    STDMETHODCALLTYPE *GetCPUDescriptorHandleForHeapStart )( 
    ID3D12DescriptorHeap * This);

Replace with:

void ( STDMETHODCALLTYPE *GetCPUDescriptorHandleForHeapStart )(
    ID3D12DescriptorHeap *This, D3D12_CPU_DESCRIPTOR_HANDLE *pOut);

Thank you.

EDIT

This header bug has been fixed with the release of the 10.0.22000.0 Windows SDK.

Feather answered 16/12, 2015 at 21:58 Comment(1)
More discussion of THISCALL vs STDCALL and DirectX's use of non-standard nano-COM vs COM at github.com/dotnet/coreclr/pull/23974Somatist
P
3

This was finally fixed in Windows SDK 10.0.20348.0. At least the various GetDesc functions were also affected, judging from the header changes.

Poncho answered 1/10, 2021 at 21:36 Comment(1)
It's been six long years.Feather

© 2022 - 2024 — McMap. All rights reserved.