Changing the "caller" call stack to EFI ABI using HolyC - Slapparoo/TempleOS-EE GitHub Wiki

Changing the "caller" call stack in HolyC

Overview

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.

Implementation Overview

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.

Implementation

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
}
You can see I have made the parameters optional by assigning them a default value so now I can call the method with the function pointer and any of 1, 2, 3 or 4 parameters

Full sample

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.

Sample Output

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]
# full output
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>
⚠️ **GitHub.com Fallback** ⚠️