[v2] 128bit DllCall Topic is solved

Put simple Tips and Tricks that are not entire Tutorials in this forum
swagfag
Posts: 6222
Joined: 11 Jan 2017, 17:59

[v2] 128bit DllCall

28 Aug 2021, 12:48

apparently there are functions that return 128bit structs BY VALUE
https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/div?view=msvc-160

irrelevant post below, see viewtopic.php?p=417951#p417951
DllCall supports at most 64bits, so trying to call this function will fail. to call this function:
  • retrieve its function pointer
  • create a wrapper that calls it
  • chunk the returned struct and return a small enough chunk normally and the rest via outparams
  • or Buffer()-alloc enough memory and copy the struct there

Code: Select all

#Requires AutoHotkey v2.0-beta.1

if !ucrtbase := DllCall('GetModuleHandle', 'Str', 'ucrtbase', 'Ptr')
	throw Error('ucrtbase.dll not loaded or missing, use windows 10')

if !lldiv := DllCall('GetProcAddress', 'Ptr', ucrtbase, 'AStr', 'lldiv', 'Ptr')
	throw Error('lldiv not found')

if (A_PtrSize = 4)
	NumPut('Int64', 0xEC83F8E483EC8B55, 'Int64', 0xFF1475FF24048D10, 'Int64', 0x0875FF0C75FF1075,
		   'Int64', 0x0F14C4831855FF50, 'Int64', 0xC1280F1C458B0810, 'Int64', 0x7E0F6608D8730F66,
		   'Int64', 0xD8730F66C1280FC2, 'Int64', 0x108904407E0F660C, 'Int64', 0xD9730F66C87E0F66,
		   'Int64', 0x5DE58BCA7E0F6604, 'Int64', 0x00000000000018C2, wrapper := Buffer(88))
else
	NumPut('Int64', 0x8B4930EC83485340, 'Int64', 0x48C28B4CD98B49C0, 'Int64', 0xFF20244C8D48D18B,
		   'Int64', 0xC16F0F6608100FD0, 'Int64', 0x730F66C87E0F4866, 'Int64', 0x834803D60F6608D8,
		   'Int64', 0x00000000C35B30C4, wrapper := Buffer(56))

DllCall('VirtualProtect', 'Ptr', wrapper, 'Ptr', wrapper.Size, 'UInt', 0x40, 'UInt*', 0) ; PAGE_EXECUTE_READWRITE

quot := DllCall(wrapper, 'Int64', 0x7FFFFFFFFFFFFFFF, 'Int64', 2, 'Ptr', lldiv, 'Int64*', &rem := 0, 'Int64')

MsgBox quot '`n' rem

Code: Select all

// #include <stdlib.h>
typedef struct _lldiv_t
{
	long long quot;
	long long rem;
} lldiv_t;

using plldiv = lldiv_t(*)(__int64, __int64);

extern "C" __declspec(dllexport) __int64 wrapper(__int64 numer, __int64 denom, plldiv lldiv, __int64* rem)
{
	const auto results = lldiv(numer, denom);
	*rem = results.rem;
	return results.quot;
}
old similar threads, probably
https://autohotkey.com/board/topic/21038-unsolved-not-supported-numget-128-bits/
Last edited by swagfag on 29 Aug 2021, 12:17, edited 1 time in total.
guest3456
Posts: 3465
Joined: 09 Oct 2013, 10:31

Re: [v2] 128bit DllCall

28 Aug 2021, 14:16

what are all those magic hex numbers

swagfag
Posts: 6222
Joined: 11 Jan 2017, 17:59

Re: [v2] 128bit DllCall

28 Aug 2021, 14:28

the compiled 32/64bit mcode of the c function source below
lexikos
Posts: 9684
Joined: 30 Sep 2013, 04:07
Contact:

Re: [v2] 128bit DllCall  Topic is solved

28 Aug 2021, 21:25

Or... you could just call the function.

Code: Select all

retval := Buffer(16, 0)
DllCall("ucrtbase\lldiv", "ptr", retval, "int64", 0x7FFFFFFFFFFFFFFF, "int64", 2, "cdecl")
MsgBox NumGet(retval, 0, "int64") "`n" NumGet(retval, 8, "int64")
User-defined types can be returned by value from global functions and static member functions. To return a user-defined type by value in RAX, it must have a length of 1, 2, 4, 8, 16, 32, or 64 bits [simplified for brevity: and must be a POD type]. Otherwise, the caller must allocate memory for the return value and pass a pointer to it as the first argument. The remaining arguments are then shifted one argument to the right. The same pointer must be returned by the callee in RAX.
Source: x64 calling convention | Microsoft Docs
On x86 platforms, all arguments are widened to 32 bits when they are passed. Return values are also widened to 32 bits and returned in the EAX register, except for 8-byte structures, which are returned in the EDX:EAX register pair. Larger structures are returned in the EAX register as pointers to hidden return structures.
Source: Argument Passing and Naming Conventions | Microsoft Docs
I couldn't find clearer documentation for x86 cdecl/stdcall return values. The address of the "hidden return structure" must be passed as a parameter somehow, because the callee can't be expected to allocate that memory itself. Compiling and disassembling your code for x86 shows that the return structure's address is pushed last, meaning it is the first parameter.

A return type of "ptr" will give you the address of the return structure, but there's no need, since its whatever you passed as the first parameter.

Functions declared using the __stdcall modifier return values the same way as functions declared using __cdecl.
Source: __stdcall | Microsoft Docs
That's great... "__cdecl" is a link to the corresponding page. On that page, there's no mention of how values are returned. :facepalm:


This doesn't look like a tutorial. Wouldn't Tips & Tricks v2 be a better fit?
swagfag
Posts: 6222
Joined: 11 Jan 2017, 17:59

Re: [v2] 128bit DllCall

29 Aug 2021, 02:07

Otherwise, the caller must allocate memory for the return value and pass a pointer to it as the first argument. The remaining arguments are then shifted one argument to the right.
huh, so this is why the DllCall kept errorring out when i tried calling the function with the "normal" number of arguments in the "normal" order. cool, that will save some time. thanks for the info

v2 tips & tricks
well, it applies to both versions. its just written in a particular one, but u can move it
Helgef
Posts: 4709
Joined: 17 Jul 2016, 01:02
Contact:

Re: [v2] 128bit DllCall

30 Aug 2021, 12:00

Thanks all for sharing.

I wrote a dllcall wrapper function for passing and fetching structs, not limited to 128 bit size, :arrow: dllcall_stuct(). It is for alpha.

Cheers.

Return to “Tips and Tricks”

Who is online

Users browsing this forum: No registered users and 5 guests