Recent

Author Topic: Functions that return structures  (Read 3461 times)

440bx

  • Hero Member
  • *****
  • Posts: 5471
Functions that return structures
« on: May 23, 2025, 09:52:56 am »
There are some (rare) functions in the Windows API that return structures, one such function is (note: 32bit only):
Code: Pascal  [Select][+][-]
  1.  
  2.   function RtlConvertLongToLargeInteger
  3.             (
  4.              { _in_ } In32bitSigned : longint
  5.             )
  6.          : TLARGE_INTEGER; stdcall; external ntdll;
  7.  
In C, when this function is called, the 64 bit TLARGE_INTEGER (LARGE_INTEGER in C) is returned in the register pair eax:edx.

The problem is, FPC expects the function result to be returned on the stack instead of that register pair. 

My question is: is there a way to tell FPC that the return value is in that register pair instead of on the stack ?

In this particular case, a solution is to declare the function's result as "int64" instead of TLARGE_INTEGER but, that's not accurate (it takes typecasting the int64 to TLARGE_INTEGER to access the low and high parts, which shouldn't be necessary.)

Thank you for your help.

For anyone interested, here is a little piece of code to "play with":
Code: Pascal  [Select][+][-]
  1. {$APPTYPE       CONSOLE}
  2.  
  3. {$TYPEDADDRESS  ON}
  4.  
  5. {$LONGSTRINGS   OFF}
  6.  
  7. {$ifdef WIN64}
  8.   {$FATAL This function is not available in 32 bit.  Forced compilation termination.}
  9. {$endif}
  10.  
  11. { --------------------------------------------------------------------------- }
  12.  
  13.  
  14. program _AccessViolation;
  15.   { NOTE: can cause an access violation due to stack corruption               }
  16. uses
  17.   Windows
  18.   ;
  19.  
  20. type
  21.   TLARGE_INTEGER = LARGE_INTEGER;
  22.  
  23. const
  24.   ntdll = 'ntdll';
  25.  
  26.  
  27.  
  28. {$ifdef WIN32}
  29.   { as defined in C                                                           }
  30.  
  31.   function RtlConvertLongToLargeInteger
  32.             (
  33.              { _in_ } In32bitSigned : longint
  34.             )
  35.          : TLARGE_INTEGER; stdcall; external ntdll;
  36.  
  37.  
  38.   { as CAN BE defined                                                         }
  39.  
  40.   function RtlConvertLongToLargeIntegerB
  41.               (
  42.                { _in_ } In32bitSigned : longint
  43.               )
  44.            : int64; stdcall; external ntdll name 'RtlConvertLongToLargeInteger';
  45. {$endif}
  46.  
  47. const
  48.   LONG_VALUE   = high(longint);
  49.  
  50. var
  51.   LargeInteger : TLARGE_INTEGER;
  52.  
  53.  
  54. begin
  55.   if IsDebuggerPresent() then asm int3 end;
  56.  
  57.   writeln;
  58.   writeln;
  59.  
  60.   writeln('  Long to convert : ', LONG_VALUE);
  61.  
  62.   writeln;
  63.   writeln('  converted with RtlConvertLongToLargeIntegerB: ',
  64.           RtlConvertLongToLargeIntegerB(LONG_VALUE));
  65.  
  66.   writeln;
  67.   writeln('  converted with RtlConvertLongToLargeInteger : ');
  68.   LargeInteger := RtlConvertLongToLargeInteger(LONG_VALUE);
  69.   with LargeInteger do
  70.   begin
  71.     writeln('  Low  part: ', LowPart);
  72.     writeln('  High part: ', HighPart);
  73.   end;
  74.  
  75.   writeln;
  76.   writeln;
  77.   writeln('press ENTER/RETURN to end this program');
  78.   readln;
  79. end.
  80.  
(FPC v3.0.4 and Lazarus 1.8.2) or (FPC v3.2.2 and Lazarus v4.0rc3) on Windows 7 SP1 64bit.

Fibonacci

  • Hero Member
  • *****
  • Posts: 754
  • Internal Error Hunter
Re: Functions that return structures
« Reply #1 on: May 23, 2025, 10:02:47 am »
But isnt LARGE_INTEGER literally Int64?

Do you have any other examples of functions that return structures?

Where is the AV?
« Last Edit: May 23, 2025, 10:26:29 am by Fibonacci »

gues1

  • Jr. Member
  • **
  • Posts: 54
Re: Functions that return structures
« Reply #2 on: May 23, 2025, 10:46:46 am »
As far I known, this function is abbandoned and was in use only in Windows 2000. This is the note about Microsoft that suggest (uhmmm .... more than a suggestion) the use of "INT64" instead: https://learn.microsoft.com/en-us/windows-hardware/drivers/ddi/wdm/nf-wdm-rtlconvertlongtolargeinteger

I really never see this function ... may be was in use for windows kernel driver only ?

440bx

  • Hero Member
  • *****
  • Posts: 5471
Re: Functions that return structures
« Reply #3 on: May 23, 2025, 10:50:26 am »
But isnt LARGE_INTEGER literally Int64?
No, it is a "unioned" structure.  One union consists of a HighPart and a LowPart and another, the QuadPart is an int64 but, the really important thing is that it is a _structure_.  An int64 is _never_ a structure.

Do you have any other examples of functions that return structures?
of the top of my mind, RtlConvertULongToLargeInteger is another.  Instead of a longint, it takes a DWORD as parameter.

Where is the AV?
the AV eventually occurs due to stack corruption, it does not necessarily occur after you run the program just once.  It may take multiple runs before it occurs.




I really never see this function ... may be was in use for windows kernel driver only ?
It was used when MS C did not support 64 bit types (which was a good while back) and yes, it isn't a function seen much, particularly these days but, it is still present in ntdll.

(FPC v3.0.4 and Lazarus 1.8.2) or (FPC v3.2.2 and Lazarus v4.0rc3) on Windows 7 SP1 64bit.

Fibonacci

  • Hero Member
  • *****
  • Posts: 754
  • Internal Error Hunter
Re: Functions that return structures
« Reply #4 on: May 23, 2025, 10:57:41 am »
No, it is a "unioned" structure.  One union consists of a HighPart and a LowPart and another, the QuadPart is an int64 but, the really important thing is that it is a _structure_.  An int64 is _never_ a structure.

No, its literally Int64. The structure is optional.

of the top of my mind, RtlConvertULongToLargeInteger is another.  Instead of a longint, it takes a DWORD as parameter.

Returns Int64. Any other "structures"?

the AV eventually occurs due to stack corruption, it does not necessarily occur after you run the program just once.  It may take multiple runs before it occurs.

How many? 1000 is enough? No AV here.

440bx

  • Hero Member
  • *****
  • Posts: 5471
Re: Functions that return structures
« Reply #5 on: May 23, 2025, 10:59:29 am »
No, its literally Int64. The structure is optional.
But of course it is, and the red paint you've been smoking is vitamin C.
(FPC v3.0.4 and Lazarus 1.8.2) or (FPC v3.2.2 and Lazarus v4.0rc3) on Windows 7 SP1 64bit.

Fibonacci

  • Hero Member
  • *****
  • Posts: 754
  • Internal Error Hunter
Re: Functions that return structures
« Reply #6 on: May 23, 2025, 11:01:07 am »
But of course it is, and the red paint you've been smoking is vitamin C.

 :o

Youre wrong

silvercoder70

  • Full Member
  • ***
  • Posts: 190
    • Tim Coates
Re: Functions that return structures
« Reply #7 on: May 23, 2025, 11:05:42 am »
And deprecated (and likely undocumented) ...

See: https://www.google.com/search?q=rtlconvertlongtolargeinteger+deprecated
🔥 Pascal Isn’t Dead -> See What It Can Do: @silvercoder70 on YouTube

440bx

  • Hero Member
  • *****
  • Posts: 5471
Re: Functions that return structures
« Reply #8 on: May 23, 2025, 12:33:43 pm »
And deprecated (and likely undocumented) ...

See: https://www.google.com/search?q=rtlconvertlongtolargeinteger+deprecated
Yes, it's deprecated.  It is documented and, it is still available for programs to use.

However, the question is:
Quote
My question is: is there a way to tell FPC that the return value is in that register pair instead of on the stack ?

(FPC v3.0.4 and Lazarus 1.8.2) or (FPC v3.2.2 and Lazarus v4.0rc3) on Windows 7 SP1 64bit.

Fibonacci

  • Hero Member
  • *****
  • Posts: 754
  • Internal Error Hunter
Re: Functions that return structures
« Reply #9 on: May 23, 2025, 12:41:33 pm »
Use cdecl

marcov

  • Administrator
  • Hero Member
  • *
  • Posts: 12266
  • FPC developer.
Re: Functions that return structures
« Reply #10 on: May 23, 2025, 01:13:55 pm »
However, the question is:
Quote
My question is: is there a way to tell FPC that the return value is in that register pair instead of on the stack ?

No. There are only calling convention modifiers, there is no way to customise calling conventions (in this case that would be registrable small structs)

440bx

  • Hero Member
  • *****
  • Posts: 5471
Re: Functions that return structures
« Reply #11 on: May 23, 2025, 01:28:24 pm »
No. There are only calling convention modifiers, there is no way to customise calling conventions (in this case that would be registrable small structs)
I suspected that was the case but, figured I'd ask, just in case. 

Thank you @marcov
(FPC v3.0.4 and Lazarus 1.8.2) or (FPC v3.2.2 and Lazarus v4.0rc3) on Windows 7 SP1 64bit.

PascalDragon

  • Hero Member
  • *****
  • Posts: 6004
  • Compiler Developer
Re: Functions that return structures
« Reply #12 on: May 25, 2025, 09:54:29 pm »
No. There are only calling convention modifiers, there is no way to customise calling conventions (in this case that would be registrable small structs)
I suspected that was the case but, figured I'd ask, just in case. 

Thank you @marcov

The question is how GCC and clang handle such a construct. Could very well be that we still have a bug in the stdcall calling convention 🤷‍♀️ (and best also compare cdecl in case it behaves differently)

gues1

  • Jr. Member
  • **
  • Posts: 54
Re: Functions that return structures
« Reply #13 on: May 26, 2025, 01:24:35 am »
This is from Wikipedia: https://en.wikipedia.org/wiki/X86_calling_conventions

Also, functions with stdcall modifier (default x Win32 API) return values the same way as functions declared with cdecl.

So, if the record size is 64 bit or less the value should be returned in EAX:EDX.

The function returns in the correct way. FPC should act like that.
« Last Edit: May 26, 2025, 01:29:46 am by gues1 »

440bx

  • Hero Member
  • *****
  • Posts: 5471
Re: Functions that return structures
« Reply #14 on: May 26, 2025, 01:35:12 am »
The question is how GCC and clang handle such a construct. Could very well be that we still have a bug in the stdcall calling convention 🤷‍♀️ (and best also compare cdecl in case it behaves differently)
I don't know what GCC and clang do in that case but, I do know that RtlConvertLongToLargeInteger is a stdcall function.  It is documented as such and dis-assemblies show it is.

The fact is, for stdcall, MSVC puts the return value in the pair eax:edx while FPC puts the return value on the stack.  This causes the stack to be corrupted and an AV to eventually follow.

The following disassembly shows it clearly.  It's stdcall and the result is in eax:edx

Code: ASM  [Select][+][-]
  1. .text:77052D32 ; Exported entry 699. RtlConvertLongToLargeInteger
  2. .text:77052D32
  3. .text:77052D32 ; =============== S U B R O U T I N E =======================================
  4. .text:77052D32
  5. .text:77052D32
  6. .text:77052D32 ; __stdcall __RtlConvertLongToLargeInteger(x)
  7. .text:77052D32                 public ___RtlConvertLongToLargeInteger@4
  8. .text:77052D32 ___RtlConvertLongToLargeInteger@4 proc near
  9. .text:77052D32                                         ; DATA XREF: .text:off_77020210↑o
  10. .text:77052D32
  11. .text:77052D32 arg_0           = dword ptr  4
  12. .text:77052D32
  13. .text:77052D32                 mov     eax, [esp+arg_0]
  14. .text:77052D36                 cdq
  15. .text:77052D37                 retn    4
  16. .text:77052D37 ___RtlConvertLongToLargeInteger@4 endp
  17.  
(FPC v3.0.4 and Lazarus 1.8.2) or (FPC v3.2.2 and Lazarus v4.0rc3) on Windows 7 SP1 64bit.

 

TinyPortal © 2005-2018