0

I have a simple print string program as part of a UEFI application. I am trying to print three strings in a row onto the screen, using the output console function OUTPUT_STRING

The part I'm struggling with is below:

;--some code above this--
;Print information string
mov rcx, [CONOUT]
lea rdx, [info]
call [CONOUT_PRINT_STRING]

lea rdx, [info2]
call printString

mov rcx, [CONOUT]
lea rdx, [info3]
call [CONOUT_PRINT_STRING]

;--some code between this--

;prints a string
;rdx = the address of the string
printString:
    mov rcx, [CONOUT]
    call [CONOUT_PRINT_STRING]
    ret     
;--some code below this--

In Qemu, this will print all three strings (located at info, info2, and info3) correctly. However, on my desktop's BIOS, it will print the first two strings and not print the third.

Modifying the code to the following makes all three strings print on both computers:

;--some code above this--
;Print information string
mov rcx, [CONOUT]
lea rdx, [info]
call [CONOUT_PRINT_STRING]

lea rdx, [info2]
call printString
next:

mov rcx, [CONOUT]
lea rdx, [info3]
call [CONOUT_PRINT_STRING]

;--some code between this--

;prints a string
;rdx = the address of the string
printString:
    mov rcx, [CONOUT]
    call [CONOUT_PRINT_STRING]
    push next
    ret     
;--some code below this--

However, this defeats the purpose of this being a function, since I want to be able to use it multiple times within my application.

My question is, why does ret not do what I expect it to do on my desktop's UEFI implementation? Is it due to my program, or the UEFI itself? (for reference, my motherboard is an AsRock Steel Legend B450M, BIOS version 4.10)

And if it is my program that's the issue, how can I fix it?

Thanks!

JD9999
  • 394
  • 1
  • 7
  • 18
  • 2
    `ret` always just pops a qword into RIP. Doesn't UEFI use the Windows x64 calling convention, including shadow space? The callee owns 32 bytes of space above their return address, so when your `printString` makes a call, that function may step on `printString`'s return address because it didn't reserve space. – Peter Cordes Sep 11 '21 at 10:08
  • 1
    And BTW, if you wanted to jump back, `jmp next` would be a relative jump, position independent, like `call`. `push next` uses a 32-bit (sign-extended to 64) absolute address as an immediate. – Peter Cordes Sep 11 '21 at 10:18
  • 3
    There are two aspects of the UEFI calling convention that are neglected here. Peter mentioned one. The other is that the stack must be 16-byte aligned. To fix both, in printString, subtract 0x28 from rsp before the call, and add it back before the ret. – prl Sep 11 '21 at 11:23
  • Thanks prl and Peter Cordes, I understand about shadow space now, and my updated code works correctly. More out of interest then, why is this not a requirement for the code to run correctly on QEMU? – JD9999 Sep 12 '21 at 03:20

0 Answers0