Changing the "caller" call stack to EFI ABI using HolyC - Slapparoo/TempleOS-EE GitHub Wiki
TempleOS’s native call stack is method parameters are passed on the stack, so when calling a method written in HolyC parameters are pushed on the stack, prior to call then accessed within the method by the offset into the stack.
I am currently writing in HolyC the ability to call the Windows ABI stack frame, initialy as the "caller" but potentially later as the "callee". This is because I am writing a EFI bootstrapper for templeOS, wholely inside TempleOS, so fully self contained with no external dependancies.
The Windows ABI uses register based parameter passing to methods, then once the registers are full it will pass additional parameters on the stack fortunatly for the most part the return value will be in RAX, as far as I can tell this will only change if its a float. See also (Windows x64 Calling Convention)[https://learn.microsoft.com/en-us/cpp/build/x64-calling-convention?view=msvc-170]
Initially I will focus on up to four parameter calls when the parameters are placed in RCX, RDX, R8, R9, there are additional rules around the preservation of other registers, as far as I can tell the other registers we are interested in preseving are preserved like RBP.
Fortunatly through the foresight of Terry HolyC is a very veristile language especially for "low level" system work. As far as I can tell it only requires one line of assembly.
Creating a generic Wrapper method so inside HolyC we call the Wrapper with a pointer to our destination method and it adjusted the Call stack and calls the destination.
One of the really good features about HolyC is you can nominate the CPU register you want a variable or parameter to be stored in.
declaring a varaibale to use a register <TYPE> [reg <Register>] [variable name];
(also interestingly you don’t require a name for variables).
In our case we want the parameters to be bound to specific CPU registers so we can declare the method as such. so the following code will bind parameter 1 to RCX, param 2 to RDX etc, then we use our single line of asm which is CALL <address> (which pretty much does what it says on the tin and makes a call to another method).
In this case the function pointer has been stored in RBX.
Also note - Assembler can just be used directly in line in HolyC so now the function pointer is in RBX I can simple use CALL RBX
inline.
Also note - in HolyC you can give parameters a default value in the method signature, which means you don’t need to provide them when calling
# Resulting method
U64 EfiWrapper(U8 * reg RBX functionPointer, U64 reg RCX p1 = NULL, U64 reg RDX p2= NULL, U64 reg R8 p3 = NULL, U64 reg R9 = NULL) {
CALL RBX
}
Trace gives you the assembler dump of the compiled output.
Trace;
// Wrapper to change call stack to the Windows EFI ABI
U64 EfiWrapper(U64 reg RBX functionPonter, U64 reg RCX p1 = NULL, U64 reg RDX p2 = NULL, U64 reg R8 p3 = NULL, U64 reg R9 p4 = NULL) {
CALL RBX
}
U64 dummyFunction() {
U64 reg RCX a, reg RDX b, reg R8 c, reg R9 d;
"reg value RCX=0x%x, RDX=0x%x, R8=0x%x, R9=0x%x\n", a, b, c, d;
}
U64 sampleUse() {
// no params
EfiWrapper(&dummyFunction);
// 1 param ....
EfiWrapper(&dummyFunction, 1);
EfiWrapper(&dummyFunction, 1, 2);
EfiWrapper(&dummyFunction, 1, 2, 3);
EfiWrapper(&dummyFunction, 1, 2, 3, 4);
}
sampleUse();
In this case dummyFunction will just print the values of each of the registers.
the the sample file in the editor we can run it with F5
, then output can then be saved to file the <ctrl>-s
to Tmp<#>.DD,Z
The things of interest are - we can see the parameters are getting moved off the stack into our requested registers and down the bottem we can see our print statements printing the values of the registers (they are not being popped from the stack they are being directly addressed in mem so it doesn’t matter that they are accessed out ot order).
2FDBA164 488B4D18 MOV U64 RCX,U64 18[RBP]
2FDBA168 488B5520 MOV U64 RDX,U64 20[RBP]
2FDBA16C 488B5D10 MOV U64 RBX,U64 10[RBP]
2FDBA170 4C8B4528 MOV U64 R8,U64 28[RBP]
2FDBA174 4C8B4D30 MOV U64 R9,U64 30[RBP]
0.000015s
D:/Home>ExeFile2("D:/Home/scratch.HC",CCF_CMD_LINE);
2FDBA500 33C0 XOR U64 RAX,U64 RAX
2FDBA502 C3 RET
2FDBA500 33C0 XOR U64 RAX,U64 RAX
2FDBA502 C3 RET
2FDBA500 33C0 XOR U64 RAX,U64 RAX
2FDBA502 C3 RET
2FDBA500 33C0 XOR U64 RAX,U64 RAX
2FDBA502 C3 RET
&LexWarn PrsFun PrsGlblVarLst PrsStmt WARNING: Function should return val in fun 'EfiWrapper'.
D:/Home/scratch.HC,4
2FDBA160 55 PUSH U64 RBP
2FDBA161 488BEC MOV U64 RBP,U64 RSP
2FDBA164 488B4D18 MOV U64 RCX,U64 18[RBP]
2FDBA168 488B5520 MOV U64 RDX,U64 20[RBP]
2FDBA16C 488B5D10 MOV U64 RBX,U64 10[RBP]
2FDBA170 4C8B4528 MOV U64 R8,U64 28[RBP]
2FDBA174 4C8B4D30 MOV U64 R9,U64 30[RBP]
2FDBA178 FFD3 CALL U64 RBX
2FDBA17A 5D POP U64 RBP
2FDBA17B C22800 RET1 I16 0028
2FDB9DA8 55 PUSH U64 RBP
2FDB9DA9 488BEC MOV U64 RBP,U64 RSP
2FDB9DAC 4883EC20 SUB U64 RSP,I8 20
2FDB9DB0 4951 PUSH U64 R9
2FDB9DB2 4950 PUSH U64 R8
2FDB9DB4 52 PUSH U64 RDX
2FDB9DB5 51 PUSH U64 RCX
2FDB9DB6 6A04 PUSH I8 04
2FDB9DB8 68C89DDB2F PUSH I32 2FDB9DC8
2FDB9DBD E8CCDB25D0 CALL I32 0x1798E
2FDB9DC2 4883C430 ADD U64 RSP,I8 30
2FDB9DC6 C9 LEAVE
2FDB9DC7 C3 RET
2FDB9DC8 7265 JB I8 0x2FDB9E2F
2FDB9DCA 67207661 AND U8 61[SI],U8 DH
2FDB9DCE 6C7565 INS U8 65[RBP],U16 SIDX
2FDB9DD1 205243 AND U8 43[RDX],U8 DL
2FDB9DD4 58 POP U64 RAX
2FDB9DD5 3D30782578 CMP U32 EAX,I32 78257830
2FDB9DDA 2C20 SUB U8 AL,U8 20
2FDB9DDC 52 PUSH U64 RDX
2FDB9DDD 4458 POP U64 RAX
2FDB9DDF 3D30782578 CMP U32 EAX,I32 78257830
2FDB9DE4 2C20 SUB U8 AL,U8 20
2FDB9DE6 52 PUSH U64 RDX
2FDB9DE7 383D30782578 CMP U8 [A801161D],U8 BH
2FDB9DED 2C20 SUB U8 AL,U8 20
2FDB9DEF 52 PUSH U64 RDX
2FDB9DF0 393D30782578 CMP U32 [A8011626],U32 EDI
2FDB9DF6 0A00 OR U8 AL,U8 [RAX]
2FDB99E0 55 PUSH U64 RBP
2FDB99E1 488BEC MOV U64 RBP,U64 RSP
2FDB99E4 6A00 PUSH I8 00
2FDB99E6 6A00 PUSH I8 00
2FDB99E8 6A00 PUSH I8 00
2FDB99EA 6A00 PUSH I8 00
2FDB99EC 68A89DDB2F PUSH I32 2FDB9DA8
2FDB99F1 E86A070000 CALL I32 &EfiWrapper+0x0000
2FDB99F6 6A00 PUSH I8 00
2FDB99F8 6A00 PUSH I8 00
2FDB99FA 6A00 PUSH I8 00
2FDB99FC 6A01 PUSH I8 01
2FDB99FE 68A89DDB2F PUSH I32 2FDB9DA8
2FDB9A03 E858070000 CALL I32 &EfiWrapper+0x0000
2FDB9A08 6A00 PUSH I8 00
2FDB9A0A 6A00 PUSH I8 00
2FDB9A0C 6A02 PUSH I8 02
2FDB9A0E 6A01 PUSH I8 01
2FDB9A10 68A89DDB2F PUSH I32 2FDB9DA8
2FDB9A15 E846070000 CALL I32 &EfiWrapper+0x0000
2FDB9A1A 6A00 PUSH I8 00
2FDB9A1C 6A03 PUSH I8 03
2FDB9A1E 6A02 PUSH I8 02
2FDB9A20 6A01 PUSH I8 01
2FDB9A22 68A89DDB2F PUSH I32 2FDB9DA8
2FDB9A27 E834070000 CALL I32 &EfiWrapper+0x0000
2FDB9A2C 6A04 PUSH I8 04
2FDB9A2E 6A03 PUSH I8 03
2FDB9A30 6A02 PUSH I8 02
2FDB9A32 6A01 PUSH I8 01
2FDB9A34 68A89DDB2F PUSH I32 2FDB9DA8
2FDB9A39 E822070000 CALL I32 &EfiWrapper+0x0000
2FDB9A3E 5D POP U64 RBP
2FDB9A3F C3 RET
E8DBF4FFFF CALL I32 &sampleUse+0x0000
&EfiWrapper+0x03A5 C3 RET
reg value RCX=0x0, RDX=0x0, R8=0x0, R9=0x0
reg value RCX=0x1, RDX=0x0, R8=0x0, R9=0x0
reg value RCX=0x1, RDX=0x2, R8=0x0, R9=0x0
reg value RCX=0x1, RDX=0x2, R8=0x3, R9=0x0
reg value RCX=0x1, RDX=0x2, R8=0x3, R9=0x4
0.073393s ans=0x000000A0=160
D:/Home>