anyway i have put together a simple program to look at the conversation between caller and callee to see how different number of parameters affects the code on different systems, in particular what happens with stack registers
But first, the calling conventions!!!
Purebasic helpfile:
https://en.wikipedia.org/wiki/X86_calling_conventions- On x86 processors, the available volatile registers are: eax, ecx And edx. All others must be always preserved.
- On x64 processors, the available volatile registers are: rax, rcx, rdx, r8, r9, xmm0, xmm1, xmm2 And xmm3. All others must be always preserved.
So basically all the program is doing is looking at the code for a call to a simple function, and the function itself which simply makes reference to the parameters passed to it:WINDOWS: RCX, RDX, R8, R9
The Microsoft x64 calling convention is followed on Windows and pre-boot UEFI (for long mode on x86-64). It uses registers RCX, RDX, R8, R9 for the first four integer or pointer arguments (in that order), and XMM0, XMM1, XMM2, XMM3 are used for floating point arguments. Additional arguments are pushed onto the stack (right to left). Integer return values (similar to x86) are returned in RAX if 64 bits or less. Floating point return values are returned in XMM0. Parameters less than 64 bits long are not zero extended; the high bits are not zeroed.
When compiling for the x64 architecture in a Windows context (whether using Microsoft or non-Microsoft tools), there is only one calling convention — the one described here, so that stdcall, thiscall, cdecl, fastcall, etc., are now all one and the same.
In the Microsoft x64 calling convention, it's the caller's responsibility to allocate 32 bytes of "shadow space" on the stack right before calling the function (regardless of the actual number of parameters used), and to pop the stack after the call. The shadow space is used to spill RCX, RDX, R8, and R9, but must be made available to all functions, even those with fewer than four parameters.
For example, a function taking 5 integer arguments will take the first to fourth in registers, and the fifth will be pushed on the top of the shadow space. So when the called function is entered, the stack will be composed of (in ascending order) the return address, followed by the shadow space (32 bytes) followed by the fifth parameter.
___
LINUX/OSX: RDI, RSI, RDX, RCX, R8, R9
The calling convention of the System V AMD64 ABI is followed on Solaris, Linux, FreeBSD, OS X, and other UNIX-like or POSIX-compliant operating systems. The first six integer or pointer arguments are passed in registers RDI, RSI, RDX, RCX, R8, and R9, while XMM0, XMM1, XMM2, XMM3, XMM4, XMM5, XMM6 and XMM7 are used for floating point arguments. For system calls, R10 is used instead of RCX. As in the Microsoft x64 calling convention, additional arguments are passed on the stack and the return value is stored in RAX.
Registers RBP, RBX, and R12–R15 are callee-save registers; all others must be saved by the caller if it wishes to preserve their values.
Unlike the Microsoft calling convention, a shadow space is not provided; on function entry, the return address is adjacent to the seventh integer argument on the stack.
Code: Select all
Procedure.i Param2(p1.i, p2.i) ;Callee
mov rdx, p1 : mov rdx, p2
EndProcedure
Param2(1,2) ;Caller
Code: Select all
DisableDebugger : EnableASM
CompilerIf #PB_Compiler_Processor = #PB_Processor_x86
Macro rax : eax : EndMacro
Macro rdx : edx : EndMacro
CompilerEndIf
Procedure.i Param0()
!nop
EndProcedure
Procedure.i Param1(p1.i)
mov rdx, p1 ;*ax is used for return, so we use *dx instead
EndProcedure
Procedure.i Param2(p1.i, p2.i)
mov rdx, p1 : mov rdx, p2
EndProcedure
Procedure.i Param3(p1.i, p2.i, p3.i)
mov rdx, p1 : mov rdx, p2 : mov rdx, p3
EndProcedure
Procedure.i Param4(p1.i, p2.i, p3.i, p4.i)
mov rdx, p1 : mov rdx, p2 : mov rdx, p3 : mov rdx, p4
EndProcedure
Procedure.i Param5(p1.i, p2.i, p3.i, p4.i, p5.i)
mov rdx, p1 : mov rdx, p2 : mov rdx, p3 : mov rdx, p4 : mov rdx, p5
EndProcedure
Procedure.i Param6(p1.i, p2.i, p3.i, p4.i, p5.i, p6.i)
mov rdx, p1 : mov rdx, p2 : mov rdx, p3 : mov rdx, p4 : mov rdx, p5: mov rdx, p6
EndProcedure
Procedure.i Param7(p1.i, p2.i, p3.i, p4.i, p5.i, p6.i, p7.i)
mov rdx, p1 : mov rdx, p2 : mov rdx, p3 : mov rdx, p4 : mov rdx, p5: mov rdx, p6: mov rdx, p7
EndProcedure
Goto ReadAllCodes ;peace to all the anti-Goto'ers!
A0:
Param0(): B0:
A1:
Param1(1): B1:
A2:
Param2(1,2): B2:
A3:
Param3(1,2,3): B3:
A4:
Param4(1,2,3,4): B4:
A5:
Param5(1,2,3,4,5): B5:
A6:
Param6(1,2,3,4,5,6): B6:
A7:
Param7(1,2,3,4,5,6,7)
!nop ;required in x86 otherwise for some weird reason the disasm reports invalid for this one
B7:
Macro ReadCode(numparams, Code_Start, Code_End, CalleeProc)
Text$ + "Caller: (" + Str(numparams) + " parameters)" + #CRLF$
If ExamineAssembly(?Code_Start, ?Code_End)
While NextInstruction()
sInstruct.s = Trim(InstructionString())
If sInstruct = "nop"
ElseIf Left(sInstruct,4) = "call"
Text$ + " call Callee()" + #CRLF$
ElseIf (Left(sInstruct,4) = "mov " Or Left(sInstruct,4) = "push") And Left(Right(sInstruct,4),3) = " 0x"
Text$ + " " + LSet(sInstruct,25," ") + ";#" + Right(sInstruct,1) + #CRLF$
Else
Text$ + " " + sInstruct + #CRLF$
EndIf
Wend
EndIf
pcnt.i = 0
Text$ + "Callee(): " + #CRLF$
If ExamineAssembly(@CalleeProc(), @CalleeProc()+128)
While NextInstruction()
sInstruct.s = Trim(InstructionString())
If Left(sInstruct,10) = "mov rax, [" Or Left(sInstruct,10) = "mov eax, [" Or Left(sInstruct,10) = "mov edx, [" Or Left(sInstruct,10) = "mov rdx, ["
pcnt.i + 1
Text$ + " " + LSet(sInstruct,25," ") + ";#" + Str(pcnt) + #CRLF$
Else
Text$ + " " + sInstruct + #CRLF$
EndIf
If Left(sInstruct,3) = "ret": Break: EndIf
Wend
EndIf
Text$ + #CRLF$
EndMacro
ReadAllCodes:
CompilerIf #PB_Compiler_Processor = #PB_Processor_x64
SysCPU.s="-x64"
CompilerElse
SysCPU.s="-x86"
CompilerEndIf
CompilerIf #PB_Compiler_OS = #PB_OS_Linux
SysOS.s="LNX"
CompilerElseIf #PB_Compiler_OS = #PB_OS_MacOS
SysOS.s="MAC"
CompilerElse
SysOS.s="WIN"
CompilerEndIf
Text$ = SysOS+SysCPU + ": (PB v" + Str(#PB_Compiler_Version) + ")" + #CRLF$ + "========" + #CRLF$
ReadCode(0,A0,B0,Param0)
ReadCode(1,A1,B1,Param1)
ReadCode(2,A2,B2,Param2)
ReadCode(3,A3,B3,Param3)
ReadCode(4,A4,B4,Param4)
ReadCode(5,A5,B5,Param5)
ReadCode(6,A6,B6,Param6)
ReadCode(7,A7,B7,Param7)
SetClipboardText(Text$)
MessageRequester("Saved to clipboard", Text$)
;CreateFile(0, #PB_Compiler_FilePath+"~Stack_"+SysOS+SysCPU+".txt")
; WriteData(0, @Text$, Len(Text$))
;CloseFile(0)