Error Running jextract on d3d12.h

Maurizio Cimadamore maurizio.cimadamore at oracle.com
Mon Sep 14 10:52:51 UTC 2020


I think there's something off in here:

> MemorySegment segment =
> MemorySegment.allocateNative(dxgi_h.IDXGIFactory1Vtbl.$LAYOUT().byteSize());
> MemoryAddress address = segment.address().addOffset(64);
I see a couple of issues:

1) how do you determine that the offset of the function address you want 
to obtain is 64 byte from the start? Didn't jextract give you a getter 
for that?

2) Even assuming 64 byte is the correct offset, note that the segment 
will be initialized to all zeros - I presume this vtable must be filled 
in somehow, or perhaps obtained from a global variable?

 From your earlier email I see this:

> interface IDXGIFactory
>       {
>           CONST_VTBL struct IDXGIFactoryVtbl *lpVtbl;
>       };
So... perhaps once yo create a IDXGIFactory, you can just grab the 
pointer to the vtable by accessing its `lpVtbl` field - no need to 
allocate a new segment (the library has done the allocation for you).

If I'm correct, this can be achieved with something like (not sure if 
the names are going to be 100% correct since I don't have a jextracted 
version of the API with me - so beware, I have NOT tested this :-) ):

var dxgiFactoryPtr = MemoryAccess.getAddress(MemorySegment.ofNativeRestricted(), dxgiFactory);
var lpVtbl = MemoryAccess.getAddress(MemorySegment.ofNativeRestricted(), dxgiFactoryPtr);
var vtable = RuntimeHelper.ofArrayRestricted(lpVtbl, IDXGIFactory1Vtbl.$LAYOUT(), 1); //1
var address = IDXGIFactory1Vtbl$EnumAdapters$get(vtable);


And this should give the address pointer you are looking for. We need to 
smooth the edges especially around (1) - e.g. to turn a random native 
pointer into a struct with given layout (jextract should generate a 
static helper for that, see [1]) - but with the code above you should be 
able to get there, I hope.

P.S.
In these situations, my suggestion is, especially when attacking a 
native library for the first time, to always try the library directly 
using C and see what happens, to understand exactly what kind of 
pattern/idiom the library requires. I did that many times and realized 
my assumptions on how library worked were wrong; other users have posted 
similar instances in this mailing list. Once you get things working in 
C, porting the code in Java using the Panama API is typically a trivial 
matter.

Maurizio

[1] - https://bugs.openjdk.java.net/browse/JDK-8253102


On 13/09/2020 02:37, Michael Ennen wrote:
> Well, progress of a kind? This crashes the VM instead of not compiling or
> throwing an exception:
>
> // IDXGIFactory1** dxgiFactory;
> var dxgiFactory = scope.allocate(C_POINTER);
> // HRESULT = CreateDXGIFactory1(_uuid(dxgiFactory), &dxgiFactory))
> int hresult = dxgi_h.CreateDXGIFactory1(GUID(IID.IID_IDXGIFactory1),
> dxgiFactory);
> System.out.println("hresult: " + hresult);
> var dxgiAdapter = scope.allocate(C_POINTER);
> System.out.println("IDXGIFactory1Vtbl byte size: " +
> dxgi_h.IDXGIFactory1Vtbl.$LAYOUT().byteSize()); //112
> MemorySegment segment =
> MemorySegment.allocateNative(dxgi_h.IDXGIFactory1Vtbl.$LAYOUT().byteSize());
> MemoryAddress address = segment.address().addOffset(64);
> FunctionDescriptor functionDescriptor = FunctionDescriptor.of(C_INT, C_INT,
> C_POINTER);
> MethodType methodType = MethodType.methodType(int.class, int.class,
> MemoryAddress.class);
> MethodHandle methodHandle = getSystemLinker().downcallHandle(address,
> methodType, functionDescriptor);
> try {
> methodHandle.invokeWithArguments(0, dxgiAdapter.address());
> } catch (Throwable throwable) {
> throwable.printStackTrace();
> }
>
> On Sat, Sep 12, 2020 at 6:34 PM Michael Ennen <mike.ennen at gmail.com> wrote:
>
>> I think I see why. It's because I am passing in a MemorySegment, which is
>> the dxgiAdapter which I initialize thusly:
>>
>> var dxgiAdapter = scope.allocate(C_POINTER);
>>
>> I guess I need to make it a MemoryAddress instead.
>>
>> On Sat, Sep 12, 2020 at 2:25 PM Michael Ennen <mike.ennen at gmail.com>
>> wrote:
>>
>>> Thanks so much Ty. That's super helpful. I think I was able to make quite
>>> a bit of progress:
>>>
>>> var dxgiAdapter = scope.allocate(C_POINTER);
>>> System.out.println("IDXGIFactory1Vtbl byte size: " +
>>> dxgi_h.IDXGIFactory1Vtbl.$LAYOUT().byteSize());
>>> MemorySegment segment =
>>> MemorySegment.allocateNative(dxgi_h.IDXGIFactory1Vtbl.$LAYOUT().byteSize());
>>> MemoryAddress address = segment.address().addOffset(64);
>>> FunctionDescriptor functionDescriptor = FunctionDescriptor.of(C_INT,
>>> C_INT, C_POINTER);
>>> MethodType methodType = MethodType.methodType(int.class, int.class,
>>> MemoryAddress.class);
>>> MethodHandle methodHandle = getSystemLinker().downcallHandle(address,
>>> methodType, functionDescriptor);
>>> try {
>>> methodHandle.invokeWithArguments(0, dxgiAdapter);
>>> } catch (Throwable throwable) {
>>> throwable.printStackTrace();
>>> }
>>>
>>> Note that the EnumAdapters1 function signature is actually:
>>>
>>> HRESULT EnumAdapters1(
>>>    UINT          Adapter,
>>>    IDXGIAdapter1 **ppAdapter
>>> );
>>>
>>>
>>> So that's why I went with:
>>>
>>> FunctionDescriptor functionDescriptor = FunctionDescriptor.of(C_INT,
>>> C_INT, C_POINTER);
>>>
>>> and then
>>>
>>> methodHandle.invokeWithArguments(0, dxgiAdapter);
>>>
>>> I did some fancy stuff as per your suggestion which I feel should be
>>> getting the address of the EumAdapters1 function
>>> pointer (basically start at the base address of the struct and then move
>>> 64 bytes past, which should be the 8th function
>>> pointer which in this case is EnumAdapters1).
>>>
>>> I do get the following exception on invoking the method handle, though:
>>>
>>> hresult: 0
>>> factory: MemorySegment{ id=0x3898fd8a limit: 8 }
>>> IDXGIFactory1Vtbl byte size: 112
>>> java.lang.ClassCastException: Cannot cast
>>> jdk.internal.foreign.NativeMemorySegmentImpl to
>>> jdk.incubator.foreign.MemoryAddress
>>>          at java.base/java.lang.Class.cast(Class.java:3816)
>>>          at
>>> java.base/java.lang.invoke.MethodHandle.invokeWithArguments(MethodHandle.java:733)
>>>          at com.dx12.DX12.main(DX12.java:91)
>>>          at
>>> java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native
>>> Method)
>>>          at
>>> java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:64)
>>>          at
>>> java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
>>>          at java.base/java.lang.reflect.Method.invoke(Method.java:564)
>>>          at
>>> jdk.compiler/com.sun.tools.javac.launcher.Main.execute(Main.java:415)
>>>          at
>>> jdk.compiler/com.sun.tools.javac.launcher.Main.run(Main.java:192)
>>>          at
>>> jdk.compiler/com.sun.tools.javac.launcher.Main.main(Main.java:132)
>>> vh: VarHandle[varType=jdk.incubator.foreign.MemoryAddress,
>>> coord=[interface jdk.incubator.foreign.MemorySegment]]
>>>
>>> Any more assistance would be greatly appreciate but undeserved :).
>>>
>>>
>>> On Sat, Sep 12, 2020 at 1:41 AM Ty Young <youngty1997 at gmail.com> wrote:
>>>
>>>> Not a JDK developer, but if you want to do this by hand(aka no
>>>> jextract), I had to do this for NVAPI which obfuscates function pointers
>>>> using keys, e.g.:
>>>>
>>>>
>>>> nvapi_h.nvapi_QueryInterface(0x0150e828);
>>>>
>>>>
>>>> where "0x0150e828" is the "NvAPI_Initialize" function key as per the
>>>> Open Source headers Nvidia released sometime ago.
>>>>
>>>>
>>>> The only difference is that, since the function pointer is in a struct,
>>>> you need to get the segment in the struct containing the function
>>>> pointer(see "slice" MemorySegment method) and actually read from it
>>>> using a VarHandle. You then take that MemoryAddress, with a
>>>> FunctionDescriptor and MethodType, and create a MethodHandle. For
>>>> reference, this my working code("NativeFunction" and "NativeTypes" do
>>>> not exist in Panama, those are mine but they dont matter) for the
>>>> NvAPI_Initalize API:
>>>>
>>>>
>>>> public class NvAPI_Initialize implements NativeFunction
>>>> {
>>>>       private final MemoryAddress address;
>>>>       private final FunctionDescriptor descriptor;
>>>>       private final MethodType type;
>>>>
>>>>       private final MethodHandle handle;
>>>>
>>>>       public NvAPI_Initialize() throws NoSuchMethodException, Throwable
>>>>       {
>>>>           this.address = nvapi_h.nvapi_QueryInterface(0x0150e828);
>>>>
>>>>           this.descriptor = FunctionDescriptor.of(NativeTypes.INT);
>>>>
>>>>           this.type = MethodType.methodType(int.class);
>>>>
>>>>           this.handle =
>>>> CSupport.getSystemLinker().downcallHandle(this.address, this.type,
>>>> this.descriptor);
>>>>       }
>>>>
>>>>       // etc
>>>>
>>>> }
>>>>
>>>>
>>>> It looks like in your case the FunctionDescriptor would be:
>>>>
>>>>
>>>> this.descriptor = FunctionDescriptor.of(CSupport.C_POINTER,
>>>> CSupport.C_INT, CSupport.C_POINTER);
>>>>
>>>>
>>>> for MethodType:
>>>>
>>>>
>>>> MethodType.methodType(MemoryAddress.class, int.class,
>>>> MemoryAddress.class);
>>>>
>>>>
>>>> where the first argument takes in a IDXGIFactory, the second an unsigned
>>>> int, and the third a pointer to what looks like an opaque device that
>>>> you pass to other functions.
>>>>
>>>>
>>>> If you haven't already, it might help to make bindings with a smaller,
>>>> less complicated library just to get a feel with how things work under
>>>> the hood.
>>>>
>>>>
>>>> Hope this helps.
>>>>
>>>>
>>>> On 9/12/20 2:23 AM, Michael Ennen wrote:
>>>>> Looking into this a bit further I think I chose a really bad library to
>>>>> start with as the function I need to call, EnumAdapters, is inside
>>>>> some type of "VTbl" struct that is trying to emulate a C++ class with
>>>>> virtual functions. What jextract is returning is most likely *correct*
>>>>> but in this case so complicated because of how it is laid out in C. It
>>>> is
>>>>> not clear at all how to grab the EnumAdapters function from
>>>>> this mess, and it is probably above my head.
>>>>>
>>>>> EXTERN_C const IID IID_IDXGIFactory;
>>>>>
>>>>> #if defined(__cplusplus) && !defined(CINTERFACE)
>>>>>
>>>>>       MIDL_INTERFACE("7b7166ec-21c7-44ae-b21a-c9ae321ae369")
>>>>>       IDXGIFactory : public IDXGIObject
>>>>>       {
>>>>>       public:
>>>>>           virtual HRESULT STDMETHODCALLTYPE EnumAdapters(
>>>>>               /* [in] */ UINT Adapter,
>>>>>               /* [annotation][out] */
>>>>>               _COM_Outptr_  IDXGIAdapter **ppAdapter) = 0;
>>>>>
>>>>>           virtual HRESULT STDMETHODCALLTYPE MakeWindowAssociation(
>>>>>               HWND WindowHandle,
>>>>>               UINT Flags) = 0;
>>>>>
>>>>>           virtual HRESULT STDMETHODCALLTYPE GetWindowAssociation(
>>>>>               /* [annotation][out] */
>>>>>               _Out_  HWND *pWindowHandle) = 0;
>>>>>
>>>>>           virtual HRESULT STDMETHODCALLTYPE CreateSwapChain(
>>>>>               /* [annotation][in] */
>>>>>               _In_  IUnknown *pDevice,
>>>>>               /* [annotation][in] */
>>>>>               _In_  DXGI_SWAP_CHAIN_DESC *pDesc,
>>>>>               /* [annotation][out] */
>>>>>               _COM_Outptr_  IDXGISwapChain **ppSwapChain) = 0;
>>>>>
>>>>>           virtual HRESULT STDMETHODCALLTYPE CreateSoftwareAdapter(
>>>>>               /* [in] */ HMODULE Module,
>>>>>               /* [annotation][out] */
>>>>>               _COM_Outptr_  IDXGIAdapter **ppAdapter) = 0;
>>>>>
>>>>>       };
>>>>>
>>>>>
>>>>> #else /* C style interface */
>>>>>
>>>>>       typedef struct IDXGIFactoryVtbl
>>>>>       {
>>>>>           BEGIN_INTERFACE
>>>>>
>>>>>           HRESULT ( STDMETHODCALLTYPE *QueryInterface )(
>>>>>               IDXGIFactory * This,
>>>>>               /* [in] */ REFIID riid,
>>>>>               /* [annotation][iid_is][out] */
>>>>>               _COM_Outptr_  void **ppvObject);
>>>>>
>>>>>           ULONG ( STDMETHODCALLTYPE *AddRef )(
>>>>>               IDXGIFactory * This);
>>>>>
>>>>>           ULONG ( STDMETHODCALLTYPE *Release )(
>>>>>               IDXGIFactory * This);
>>>>>
>>>>>           HRESULT ( STDMETHODCALLTYPE *SetPrivateData )(
>>>>>               IDXGIFactory * This,
>>>>>               /* [annotation][in] */
>>>>>               _In_  REFGUID Name,
>>>>>               /* [in] */ UINT DataSize,
>>>>>               /* [annotation][in] */
>>>>>               _In_reads_bytes_(DataSize)  const void *pData);
>>>>>
>>>>>           HRESULT ( STDMETHODCALLTYPE *SetPrivateDataInterface )(
>>>>>               IDXGIFactory * This,
>>>>>               /* [annotation][in] */
>>>>>               _In_  REFGUID Name,
>>>>>               /* [annotation][in] */
>>>>>               _In_opt_  const IUnknown *pUnknown);
>>>>>
>>>>>           HRESULT ( STDMETHODCALLTYPE *GetPrivateData )(
>>>>>               IDXGIFactory * This,
>>>>>               /* [annotation][in] */
>>>>>               _In_  REFGUID Name,
>>>>>               /* [annotation][out][in] */
>>>>>               _Inout_  UINT *pDataSize,
>>>>>               /* [annotation][out] */
>>>>>               _Out_writes_bytes_(*pDataSize)  void *pData);
>>>>>
>>>>>           HRESULT ( STDMETHODCALLTYPE *GetParent )(
>>>>>               IDXGIFactory * This,
>>>>>               /* [annotation][in] */
>>>>>               _In_  REFIID riid,
>>>>>               /* [annotation][retval][out] */
>>>>>               _COM_Outptr_  void **ppParent);
>>>>>
>>>>>           HRESULT ( STDMETHODCALLTYPE *EnumAdapters )(
>>>>>               IDXGIFactory * This,
>>>>>               /* [in] */ UINT Adapter,
>>>>>               /* [annotation][out] */
>>>>>               _COM_Outptr_  IDXGIAdapter **ppAdapter);
>>>>>
>>>>>           HRESULT ( STDMETHODCALLTYPE *MakeWindowAssociation )(
>>>>>               IDXGIFactory * This,
>>>>>               HWND WindowHandle,
>>>>>               UINT Flags);
>>>>>
>>>>>           HRESULT ( STDMETHODCALLTYPE *GetWindowAssociation )(
>>>>>               IDXGIFactory * This,
>>>>>               /* [annotation][out] */
>>>>>               _Out_  HWND *pWindowHandle);
>>>>>
>>>>>           HRESULT ( STDMETHODCALLTYPE *CreateSwapChain )(
>>>>>               IDXGIFactory * This,
>>>>>               /* [annotation][in] */
>>>>>               _In_  IUnknown *pDevice,
>>>>>               /* [annotation][in] */
>>>>>               _In_  DXGI_SWAP_CHAIN_DESC *pDesc,
>>>>>               /* [annotation][out] */
>>>>>               _COM_Outptr_  IDXGISwapChain **ppSwapChain);
>>>>>
>>>>>           HRESULT ( STDMETHODCALLTYPE *CreateSoftwareAdapter )(
>>>>>               IDXGIFactory * This,
>>>>>               /* [in] */ HMODULE Module,
>>>>>               /* [annotation][out] */
>>>>>               _COM_Outptr_  IDXGIAdapter **ppAdapter);
>>>>>
>>>>>           END_INTERFACE
>>>>>       } IDXGIFactoryVtbl;
>>>>>
>>>>>       interface IDXGIFactory
>>>>>       {
>>>>>           CONST_VTBL struct IDXGIFactoryVtbl *lpVtbl;
>>>>>       };
>>>>>
>>>>>
>>>
>>> --
>>> Michael Ennen
>>>
>>
>> --
>> Michael Ennen
>>
>


More information about the panama-dev mailing list