Author Topic: Argument passing  (Read 862 times)

JK

  • Member
  • **
  • Posts: 158
Argument passing
« on: November 29, 2021, 05:06:43 AM »
I have a problem with a quite lengthy (test)program with a quite complicated dependencies. Therefore i hope posting the relevant part of the disassembly will do:

Code: [Select]
000000013FF12193 | CC                       | int3                                |
000000013FF12194 | 48:81EC C0000000         | sub rsp,C0                          |
000000013FF1219B | 48:8D85 48FCFFFF         | lea rax,qword ptr ss:[rbp-3B8]      |
000000013FF121A2 | 48:898424 20010000       | mov qword ptr ss:[rsp+120],rax      |
000000013FF121AA | 48:8B85 38FCFFFF         | mov rax,qword ptr ss:[rbp-3C8]      |
000000013FF121B1 | 48:898424 10010000       | mov qword ptr ss:[rsp+110],rax      |
000000013FF121B9 | 8B85 28FCFFFF            | mov eax,dword ptr ss:[rbp-3D8]      |
000000013FF121BF | 898424 00010000          | mov dword ptr ss:[rsp+100],eax      |
000000013FF121C6 | 48:C78424 F0000000 A1860 | mov qword ptr ss:[rsp+F0],186A1     |
000000013FF121D2 | 48:C78424 E0000000 81969 | mov qword ptr ss:[rsp+E0],989681    |
000000013FF121DE | 48:8B85 20FCFFFF         | mov rax,qword ptr ss:[rbp-3E0]      | [rbp-3E0]:L"wstringz"
000000013FF121E5 | 48:898424 D0000000       | mov qword ptr ss:[rsp+D0],rax       |
000000013FF121ED | 48:8B85 18FCFFFF         | mov rax,qword ptr ss:[rbp-3E8]      | [rbp-3E8]:"asciiz"
000000013FF121F4 | 48:898424 C0000000       | mov qword ptr ss:[rsp+C0],rax       |
000000013FF121FC | 48:8D85 10FCFFFF         | lea rax,qword ptr ss:[rbp-3F0]      | [rbp-3F0]:L"Wstring"
000000013FF12203 | 48:898424 B0000000       | mov qword ptr ss:[rsp+B0],rax       |
000000013FF1220B | 48:8D85 08FCFFFF         | lea rax,qword ptr ss:[rbp-3F8]      | [rbp-3F8]:"String"
000000013FF12212 | 48:898424 A0000000       | mov qword ptr ss:[rsp+A0],rax       |
000000013FF1221A | 48:C78424 90000000 9CFFF | mov qword ptr ss:[rsp+90],FFFFFFFFF |
000000013FF12226 | 48:C78424 80000000 64000 | mov qword ptr ss:[rsp+80],64        | 64:'d'
000000013FF12232 | 48:C74424 70 C0FFFFFF    | mov qword ptr ss:[rsp+70],FFFFFFFFF |
000000013FF1223B | 48:C74424 60 40000000    | mov qword ptr ss:[rsp+60],40        | 40:'@'
000000013FF12244 | C74424 50 E0FFFFFF       | mov dword ptr ss:[rsp+50],FFFFFFE0  |
000000013FF1224C | C74424 40 20000000       | mov dword ptr ss:[rsp+40],20        | 20:' '
000000013FF12254 | 49:C7C1 F0FFFFFF         | mov r9,FFFFFFFFFFFFFFF0             |
000000013FF1225B | 41:B8 10000000           | mov r8d,10                          |
000000013FF12261 | 48:C7C2 F8FFFFFF         | mov rdx,FFFFFFFFFFFFFFF8            |
000000013FF12268 | B9 08000000              | mov ecx,8                           |
000000013FF1226D | E8 6DF4FFFF              | call proc_arg_literal.13FF116DF     |
000000013FF12272 | 48:81C4 C0000000         | add rsp,C0                          |

my options are:
Code: [Select]
  OPTION STACKBASE : RBP
  option win64:5
  option cstack:on         
  OPTION FRAME:AUTO           ;SEH

The call takes 19 arguments of different types. But the reserved stack space (sub rsp, C0) is not sufficient and therefore by pushing arguments onto the stack some locals are overwritten before they are actually passed. So the passed data is corrupt.

Adding "sub rsp, 080h" before the call and "add rsp, 080h" after the call solves the problem. So the calculation of the required stack space seems to have a problem, because only C0 bytes are reserved (sub rsp, C0h) but obviously more (mov qword ptr ss:[rsp+120h],rax) are needed. 


JK

nidud

  • Member
  • *****
  • Posts: 2341
    • https://github.com/nidud/asmc
Re: Argument passing
« Reply #1 on: November 29, 2021, 06:09:49 AM »
my options are:
Code: [Select]
  OPTION STACKBASE : RBP
  option win64:5
  option cstack:on         
  OPTION FRAME:AUTO           ;SEH

Try using names for the options and see how that goes.

  option cstack:on         
  option win64:frame align save

JK

  • Member
  • **
  • Posts: 158
Re: Argument passing
« Reply #2 on: November 29, 2021, 07:10:06 AM »
Doesn´t help either, it is still "sub rsp, C0"

This is the corresponding invoke line:
Code: [Select]
INVOKE ARGTEST, 08h, -08h, 010h, -010h, 020h, -020h, 040h, -040h, 064h, -064h, ADDR  _S.S1, ADDR  _S.S2, QWORD PTR _SS.S3, QWORD PTR _SS.S4, 0989681h, 0186A1h, REAL4 PTR _N.N1, REAL8 PTR _N.N2, REAL10 PTR _N.N3

Could passing structure members (_S.S1, etc.) be a problem?


JK

nidud

  • Member
  • *****
  • Posts: 2341
    • https://github.com/nidud/asmc
Re: Argument passing
« Reply #3 on: November 29, 2021, 07:50:07 AM »
Doesn´t help either, it is still "sub rsp, C0"

That is correct. 19 args is odd so this becomes 20, and 0xC0 is 24 args so there is enough space added for the call. The problem is option win64:5 which shouldn't be allowed as stack alignment requires stack calculation.

Quote
This is the corresponding invoke line:
Code: [Select]
INVOKE ARGTEST, 08h, -08h, 010h, -010h, 020h, -020h, 040h, -040h, 064h, -064h, ADDR  _S.S1, ADDR  _S.S2, QWORD PTR _SS.S3, QWORD PTR _SS.S4, 0989681h, 0186A1h, REAL4 PTR _N.N1, REAL8 PTR _N.N2, REAL10 PTR _N.N3

Could passing structure members (_S.S1, etc.) be a problem?

I will assume the proto for ARGTEST has at least one argument > 64-bit as the arguments on the stack is 128-bit in size.

JK

  • Member
  • **
  • Posts: 158
Re: Argument passing
« Reply #4 on: November 29, 2021, 08:23:39 AM »
Quote
The problem is option win64:5 which shouldn't be allowed as stack alignment requires stack calculation.
No - because i do stack alignment myself after defining locals
Code: [Select]
and rsp,-16the epilogue at procedure end fixes rsp again (mov rsp, rbp). This works like a charm so far.


Quote
I will assume the proto for ARGTEST has at least one argument > 64-bit as the arguments on the stack is 128-bit in size.
Yes - indeed the last argument is a REAL10 and removing it makes it work. But does that mean i cannot pass a REAL10 - this cannot be true.




nidud

  • Member
  • *****
  • Posts: 2341
    • https://github.com/nidud/asmc
Re: Argument passing
« Reply #5 on: November 29, 2021, 08:32:29 AM »
Yes - indeed the last argument is a REAL10 and removing it makes it work. But does that mean i cannot pass a REAL10 - this cannot be true.

No, you can't pass real10 without extending the stack as it's larger than 64-bit.

JK

  • Member
  • **
  • Posts: 158
Re: Argument passing
« Reply #6 on: November 29, 2021, 08:39:58 AM »
Quote
No, you can't pass real10 without extending the stack as it's larger than 64-bit.

"extending the stack", this i new to me, could you please explain a bit more. I suppose you need more stack space, but where is the problem calculating the required (extra) space, when you can calculate the stack offsets for the arguments in such a case.

Just asking - i´m a bit confused. Thanks for your help

nidud

  • Member
  • *****
  • Posts: 2341
    • https://github.com/nidud/asmc
Re: Argument passing
« Reply #7 on: November 29, 2021, 09:35:21 AM »
It's not really a problem but you have to tell the assembler to do so as it is beyond any standard. The stack may be sized up to available registers (vectors: xmm0, ymm0, and zmm0).

No modern compilers really use long double anymore but it is handled as 128-bit (in 64-bit code) here in the same way as real16.

Example:
Code: [Select]
option win64:3

foo proto :real4, :real8, :real10, :real16

    .code

bar proc a:real4, b:real8, c:real10, d:real16

    foo(a,b,c,d)
    ret
bar endp

Here the arguments needs to be saved and later passed to the function foo(), so the stack needs to be extended.

bar:
        movaps  xmmword ptr [rsp+38H], xmm3
        movaps  xmmword ptr [rsp+28H], xmm2
        movq    qword ptr [rsp+18H], xmm1
        movd    dword ptr [rsp+8H], xmm0
        push    rbp                     
        mov     rbp, rsp               
        sub     rsp, 64                 
        movaps  xmm3, xmmword ptr [rbp+40H]
        movaps  xmm2, xmmword ptr [rbp+30H]
        movq    xmm1, qword ptr [rbp+20H]
        movd    xmm0, dword ptr [rbp+10H]
        call    foo
        leave     
        ret

JK

  • Member
  • **
  • Posts: 158
Re: Argument passing
« Reply #8 on: November 30, 2021, 05:30:43 AM »
Quote
It's not really a problem but you have to tell the assembler to do so
How would i do that? Parsing the PROTO and/or the procedure header the assembler can know (from REAL10/REAL16), that it must do that, but it doesn´t do that (at least not properly as it seems).

I boiled down my code to an example demonstrating the problem:
Code: [Select]
OPTION STACKBASE:RBP
option win64:5
option cstack:on
OPTION FRAME:AUTO   


;*************************************************************************************
; 1.) this works
;*************************************************************************************
;ARGTEST PROTO :BYTE,:SBYTE,:WORD,:SWORD,:DWORD,:SDWORD,:QWORD,:SQWORD,:PTR BYTE,:PTR WORD,:REAL4,:REAL8

;*************************************************************************************
; 2.) this doesn´t
;*************************************************************************************
;ARGTEST PROTO :BYTE,:SBYTE,:WORD,:SWORD,:DWORD,:SDWORD,:QWORD,:SQWORD,:PTR BYTE,:PTR WORD,:REAL4,:REAL8,:REAL10,:REAL16

;*************************************************************************************
; 3.) this doesn´t either
;*************************************************************************************
;ARGTEST PROTO :BYTE,:SBYTE,:WORD,:SWORD,:DWORD,:SDWORD,:QWORD,:SQWORD,:PTR BYTE,:PTR WORD,:REAL4,:REAL8,:REAL16

;*************************************************************************************
; 4.) this doesn´t either (requires option win64:7, crashes on option win64:5)
;     but in this case g_r10 is passed properly, in case 2 and 3 it isn´t
;*************************************************************************************
ARGTEST PROTO :REAL10,:BYTE,:SBYTE,:WORD,:SWORD,:DWORD,:SDWORD,:QWORD,:SQWORD,:PTR BYTE,:PTR WORD,:REAL4,:REAL8


.data

g_r4  real4  17.6
g_r8  real8  17.6
align 16
g_r10 real10 17.6
g_r16 real16 17.6


.code


;*************************************************************************************
; 1.) this works
;*************************************************************************************
;ARGTEST PROC USES RBX RSI RDI R12 R13 R14 R15 X_B: BYTE, X_SB: SBYTE, X_W: WORD, X_I: SWORD, X_D: DWORD, X_L: SDWORD,
;             X_Q: QWORD, X_SQ: SQWORD, X_P1: PTR BYTE, X_P2: PTR WORD, X_R4: REAL4, X_R8: REAL8

;*************************************************************************************
; 2.) this doesn´t
;*************************************************************************************
;ARGTEST PROC USES RBX RSI RDI R12 R13 R14 R15 X_B: BYTE, X_SB: SBYTE, X_W: WORD, X_I: SWORD, X_D: DWORD, X_L: SDWORD,
;             X_Q: QWORD, X_SQ: SQWORD, X_P1: PTR BYTE, X_P2: PTR WORD, X_R4: REAL4, X_R8: REAL8, X_R10: REAL10, X_R16: REAL16

;*************************************************************************************
; 3.) this doesn´t either
;*************************************************************************************
;ARGTEST PROC USES RBX RSI RDI R12 R13 R14 R15 X_B: BYTE, X_SB: SBYTE, X_W: WORD, X_I: SWORD, X_D: DWORD, X_L: SDWORD,
;             X_Q: QWORD, X_SQ: SQWORD, X_P1: PTR BYTE, X_P2: PTR WORD, X_R4: REAL4, X_R8: REAL8, X_R16: REAL16

;*************************************************************************************
; 4.) this doesn´t either (requires option win64:7, crashes on option win64:5)
;     but in this case g_r10 is passed properly, in case 2 and 3 it isn´t
;*************************************************************************************
ARGTEST PROC USES RBX RSI RDI R12 R13 R14 R15 X_R10: REAL10, X_B: BYTE, X_SB: SBYTE, X_W: WORD, X_I: SWORD, X_D: DWORD, X_L: SDWORD,
             X_Q: QWORD, X_SQ: SQWORD, X_P1: PTR BYTE, X_P2: PTR WORD, X_R4: REAL4, X_R8: REAL8


;*************************************************************************************

;*************************************************************************************
LOCAL l1: BYTE
LOCAL l2: SBYTE
LOCAL l3: WORD
LOCAL l4: SWORD
LOCAL l5: DWORD
LOCAL l6: SDWORD
LOCAL l7: QWORD
LOCAL l8: SQWORD
LOCAL p1: PTR BYTE
LOCAL p2: PTR WORD
sub rsp, -16                                          ;align stack


int 3
  movzx RAX, X_B
  movzx RAX, X_SB
  movzx RAX, X_W
  movzx RAX, X_I
  mov   EAX, X_D
  mov   EAX, X_L
  mov   RAX, X_Q
  mov   RAX, X_SQ

  fld X_R4
  fld X_R8
  fld X_R10


ret


ARGTEST endp


;*************************************************************************************


start proc uses xbx xsi xdi ;r15
;***************************************************************************
;
;***************************************************************************
LOCAL l1: BYTE
LOCAL l2: SBYTE
LOCAL l3: WORD
LOCAL l4: SWORD
LOCAL l5: DWORD
LOCAL l6: SDWORD
LOCAL l7: QWORD
LOCAL l8: SQWORD
LOCAL p1: PTR BYTE
LOCAL p2: PTR WORD
sub rsp, -16                                          ;align stack


  mov l1, 1
  mov l2, 2
  mov l3, 3
  mov l4, 4
  mov l5, 5
  mov l6, 6
  mov l7, 7
  mov l8, 8


  movzx RAX, l1
  movzx RAX, l2
  movzx RAX, l3
  movzx RAX, l4
  mov   EAX, l5
  mov   EAX, l6
  mov   RAX, l7
  mov   RAX, l8

  fld g_r4
  fld g_r8
  fld g_r10



;*************************************************************************************
; 1.) this works
;*************************************************************************************
;  INVOKE ARGTEST, l1, l2, l3, l4, l5, l6, l7, l8 , p1, p2, REAL4 PTR g_r4, REAL8 PTR g_r8

;*************************************************************************************
; 2.) this doesn´t
;*************************************************************************************
;  INVOKE ARGTEST, l1, l2, l3, l4, l5, l6, l7, l8 , p1, p2, REAL4 PTR g_r4, REAL8 PTR g_r8, REAL10 PTR g_r10, g_r16

;*************************************************************************************
; 3.) this doesn´t either
;*************************************************************************************
;  INVOKE ARGTEST, l1, l2, l3, l4, l5, l6, l7, l8 , p1, p2, g_r4, g_r8, g_r16

;*************************************************************************************
; 4.) this doesn´t either (requires option win64:7, crashes on option win64:5)
;     but in this case g_r10 is passed properly, in case 2 and 3 it isn´t
;;*************************************************************************************
;  INVOKE ARGTEST, g_r10, l1, l2, l3, l4, l5, l6, l7, l8 , p1, p2, g_r4, g_r8

;*************************************************************************************
; 5.) this works, BUT ONLY WITH EXTRA STACK SPACE (requires option win64:7, crashes on option win64:5)
;     g_r10 is passed properly
;;*************************************************************************************
  sub rsp, 080H
  INVOKE ARGTEST, g_r10, l1, l2, l3, l4, l5, l6, l7, l8 , p1, p2, g_r4, g_r8
  add rsp, 080h


ret


start endp

 
end start

As soon as a REAL10/REAL16 is in the argument list, some of the preceding arguments don´t get the expected value, because these locals are overwritten by filling the stack with arguments for the call.

Why do 4.) and 5.) crash with OPTION WIN64:5 ?

OPTION WIN64:7 changes the order in which locals are arranged on the stack (the order in memory differs from the order in code), which makes it hard to initialize all locals in a consistent manner, when some should be initialized to certain values and all the rest should be set to zero.

But even with OPTION WIN64:7 the problem is still there, extra stack space must be manually added to make it work properly. Is this intended? As already mentioned above the assembler could (and should) account for this by itself - or am i wrong here, and it is this not possible?

I hope i could explain the problem now clear enough - Thanks.


JK

nidud

  • Member
  • *****
  • Posts: 2341
    • https://github.com/nidud/asmc
Re: Argument passing
« Reply #9 on: November 30, 2021, 08:00:47 AM »
Quote
It's not really a problem but you have to tell the assembler to do so
How would i do that?
option win64:[auto | 2]

Quote
Parsing the PROTO and/or the procedure header the assembler can know (from REAL10/REAL16), that it must do that, but it doesn´t do that (at least not properly as it seems).

That's correct. When you deal with vectors you exclusively pass arguments in registers, so the stack calculations do not extend beyond the max number of register arguments. Normally you will use VECTORCALL for this (6 registers) but also FASTCALL may be used.

As the stack arguments beyond registers don't load vectors correctly (and miss calculate the size) this combination can't be used.

option win64:3

foo proto vectorcall :real16, :real16, :real16, :real16, :real16, :real16

.code

bar proc vectorcall a:real16, b:real16, c:real16, d:real16, e:real16, f:real16

    foo(xmm0,b,c,d,e,f)
    ret
bar endp

        movaps  xmmword ptr [rsp+58H], xmm5
        movaps  xmmword ptr [rsp+48H], xmm4
        movaps  xmmword ptr [rsp+38H], xmm3
        movaps  xmmword ptr [rsp+28H], xmm2
        movaps  xmmword ptr [rsp+18H], xmm1
        push    rbp
        mov     rbp, rsp
        sub     rsp, 96
        movaps  xmm5, xmmword ptr [rbp+60H]
        movaps  xmm4, xmmword ptr [rbp+50H]
        movaps  xmm3, xmmword ptr [rbp+40H]
        movaps  xmm2, xmmword ptr [rbp+30H]
        movaps  xmm1, xmmword ptr [rbp+20H]
        call    foo@@96
        leave
        ret


Quote
OPTION WIN64:7 changes the order in which locals are arranged on the stack (the order in memory differs from the order in code), which makes it hard to initialize all locals in a consistent manner, when some should be initialized to certain values and all the rest should be set to zero.

option win64:[align | 4] align locals in the same way a struct is aligned. Intended use:

  local a:byte,
        b:real16,
        c:byte,
        d:real16

    movaps a,xmm0
    movaps d,xmm1

JK

  • Member
  • **
  • Posts: 158
Re: Argument passing
« Reply #10 on: November 30, 2021, 09:45:14 AM »
Quote
option win64:[align | 4] align locals in the same way a struct is aligned.
Sorry, my fault i mixed up alignment and order - the order is kept (which is sometimes important for me)

OK, as a summary (64 bit code) you could say: as soon as an argument is passed, whose size is larger than pointer size, you cannot rely on the assembler to calculate the required stack space correctly, you must take care of it by adding extra stack space yourself as needed.

Having e.g. a REAL16 in the argument list doubles the required stack space, so adding 8 bytes for each argument and rounding up to the next multiple of 16 should be safe.

Is this correct?

nidud

  • Member
  • *****
  • Posts: 2341
    • https://github.com/nidud/asmc
Re: Argument passing
« Reply #11 on: November 30, 2021, 10:33:25 AM »
OK, as a summary (64 bit code) you could say: as soon as an argument is passed, whose size is larger than pointer size, you cannot rely on the assembler to calculate the required stack space correctly, you must take care of it by adding extra stack space yourself as needed.

It should be fixed at some point but it is part of the vector calling convention used in math functions, like DirectXMath. A 128-bit vector is a float (real16) and as real10 is handled as real16 it then becomes a vector.

Quote
Having e.g. a REAL16 in the argument list doubles the required stack space, so adding 8 bytes for each argument and rounding up to the next multiple of 16 should be safe.

Is this correct?

In general arguments larger than 64-bit should be passed as pointers unless used as a vector.

nidud

  • Member
  • *****
  • Posts: 2341
    • https://github.com/nidud/asmc
Re: Argument passing
« Reply #12 on: November 30, 2021, 09:16:47 PM »
I fixed the extended stack calculation in version 2.33.17.

Code: [Select]
option win64:3
option cstack:on

.data
g_r4  real4  17.6
g_r8  real8  17.6
align 16
g_r10 real10 17.6
align 16
g_r16 real16 17.6

.code

ARGTEST PROC USES RBX RSI RDI R12 R13 R14 R15 X_R10: REAL10, X_B: BYTE, X_SB: SBYTE,
        X_W: WORD, X_I: SWORD, X_D: DWORD, X_L: SDWORD, X_Q: QWORD, X_SQ: SQWORD, X_P1: PTR BYTE,  X_P2: PTR WORD,
        X_R4: REAL4, X_R8: REAL8

LOCAL l1: BYTE
LOCAL l2: SBYTE
LOCAL l3: WORD
LOCAL l4: SWORD
LOCAL l5: DWORD
LOCAL l6: SDWORD
LOCAL l7: QWORD
LOCAL l8: SQWORD
LOCAL p1: PTR BYTE
LOCAL p2: PTR WORD

  movzx RAX, X_B
  movzx RAX, X_SB
  movzx RAX, X_W
  movzx RAX, X_I
  mov   EAX, X_D
  mov   EAX, X_L
  mov   RAX, X_Q
  mov   RAX, X_SQ

  fld X_R4
  fld X_R8
  fld X_R10
  ret

ARGTEST endp

start proc uses rbx rsi rdi ;r15

LOCAL l1: BYTE
LOCAL l2: SBYTE
LOCAL l3: WORD
LOCAL l4: SWORD
LOCAL l5: DWORD
LOCAL l6: SDWORD
LOCAL l7: QWORD
LOCAL l8: SQWORD
LOCAL p1: PTR BYTE
LOCAL p2: PTR WORD
;and rsp, -16 ;align stack

  mov l1, 1
  mov l2, 2
  mov l3, 3
  mov l4, 4
  mov l5, 5
  mov l6, 6
  mov l7, 7
  mov l8, 8


  movzx RAX, l1
  movzx RAX, l2
  movzx RAX, l3
  movzx RAX, l4
  mov   EAX, l5
  mov   EAX, l6
  mov   RAX, l7
  mov   RAX, l8

  fld g_r4
  fld g_r8
  fld g_r10
  INVOKE ARGTEST, g_r10, l1, l2, l3, l4, l5, l6, l7, l8 , p1, p2, g_r4, g_r8
  ret

start endp

end start

However, arguments larger than 64-bit doesn't work correctly in INVOKE.
Code: [Select]
option win64:3

large struct
x db 17 dup(?)
large ends

foo proto :large, :large

.code

bar proc a:large
  local b:large
    foo(a, b)
    ret
bar endp

        mov     qword ptr [rsp+8H], rcx
        push    rbp
        mov     rbp, rsp
        sub     rsp, 64
        lea     rdx, [rbp-18H]
        lea     rcx, [rbp+10H]
        call    foo
        leave
        ret

Vectors are handled correctly as register arguments but fails as stack arguments.

proto fastcall   :real16, :real16, :real16, :real16, :real16, :real16, :real16
proto vectorcall :real16, :real16, :real16, :real16, :real16, :real16, :real16

JK

  • Member
  • **
  • Posts: 158
Re: Argument passing
« Reply #13 on: December 01, 2021, 04:01:54 AM »
Quote
In general arguments larger than 64-bit should be passed as pointers unless used as a vector.
Quote
Vectors are handled correctly as register arguments but fails as stack arguments.
OK, this is something i can deal with, Thanks for all your work, your explanation and help!

JK

JK

  • Member
  • **
  • Posts: 158
Re: Argument passing
« Reply #14 on: December 02, 2021, 03:26:12 AM »
Sorry, it´s me again :bgrin:

I found another problem this time in 32 bit code:
Code: [Select]
.686P
.model flat, stdcall         
OPTION STACKBASE : EBP
option cstack:on 
.xmm


_NUM2 STRUCT
  N1 OWORD ?
  N2 OWORD ?
_NUM2 ENDS


.DATA

G_FLS REAL4 -0.4
G_FLD REAL8  8.8


;ARGTEST PROTO :BYTE,:SBYTE,:WORD,:SWORD,:DWORD,:SDWORD,:SQWORD,:QWORD,:DWORD,:SDWORD,:DWORD,:DWORD,:PTR BYTE,:PTR WORD,:QWORD,:QWORD,:REAL4,:REAL8


ARGTEST PROTO :REAL4,:REAL8                         
;ARGTEST PROTO :REAL8,:REAL4                         


.CODE

;ARGTEST PROC USES EBX ESI EDI X_B: BYTE, X_SB: SBYTE, X_W: WORD, X_I: SWORD, X_D: DWORD, X_L: SDWORD, X_Q: SQWORD, X_SQ: QWORD, X_X: DWORD, X_SX: SDWORD, X_S$: DWORD, X_WS$$: DWORD, X_AZ:PTR BYTE, X_WZ:PTR WORD, X_CCR: QWORD, X_CCX: QWORD, X_FLS: REAL4, X_FLD: REAL8


ARGTEST PROC USES EBX ESI EDI X_FLS: REAL4, X_FLD: REAL8
;ARGTEST PROC USES EBX ESI EDI X_FLD: REAL8, X_FLS: REAL4


  fld G_FLS
  fld G_FLD
  fld X_FLS
  fld X_FLD
int 3                                                 ;you would expect to see "8.8xxxxx" in ST(0) here


RET
ARGTEST ENDP

MAIN PROC USES EBX ESI EDI
local _N:_NUM2


.data
_fpl17 REAL8 8.8
.code
FLD _fpl17 
FSTP REAL8 PTR _N.N2                                  ;error
;FSTP REAL8 PTR _N.N1                                  ;works


.data
_fpl16 REAL4 -0.4
.code
FLD _fpl16 
FSTP REAL4 PTR _N.N1                                  ;works
;FSTP REAL4 PTR _N.N2                                  ;works


int 3
;INVOKE ARGTEST, 08h, -08h, 010h, -010h, 020h, -020h, 040h, -040h, 064h, -064h,ADDR  _S.S1,ADDR  _S.S2,DWORD PTR _S.S3,DWORD PTR _S.S4, 0989681h, 0186A1h,REAL4 PTR _N.N1,REAL8 PTR _N.N2

LEA eax, _N.N2
INVOKE ARGTEST, REAL4 PTR _N.N1,REAL8 PTR _N.N2       ;error (REAL4 -> _N.N1, REAL8 -> _N.N2)
;INVOKE ARGTEST, REAL8 PTR _N.N2,REAL4 PTR _N.N1       ;error

;INVOKE ARGTEST, REAL4 PTR _N.N2,REAL8 PTR _N.N1       ;works (REAL4 -> _N.N2, REAL8 -> _N.N1)
;INVOKE ARGTEST, REAL8 PTR _N.N1,REAL4 PTR _N.N2       ;works


RET
MAIN ENDP


END MAIN

;*************************************************************************************
;both (argument order) work, if REAL8 is assigned to _N.N1
;both (argument order) fail, if REAL8 is assigned to _N.N2
;for REAL4 it doesn´t matter, if it is assigned to _N.N1 or _N.N2
;it doesn´t depend on the number of arguments
;*************************************************************************************

passing a REAL8 (or REAL10), which has been assigned to a member of a structure, fails, if it isn´t assigned to the first member (see test code above)

A disassembly

0090103B | CC                       | int3                                   
0090103C | 8D45 F0                  | lea eax,dword ptr ss:[ebp-10]         
0090103F | FF75 E4                  | push dword ptr ss:[ebp-1C]             
00901042 | FF75 E0                  | push dword ptr ss:[ebp-20]             
00901045 | FF75 E0                  | push dword ptr ss:[ebp-20]             

shows, that _N.N1 (epb-20) is passed instead of _N.N2 (ebp-10). According to my tests this happens only with structure members (a regular local variable will work) and only for the second (and following) members (the first member works as expected). 

Could you please re-test yourself and have a look at it - thanks.

JK