flat assembler
Message board for the users of flat assembler.
Index
> OS Construction > Use UEFI Graphics Output Protocol Goto page Previous 1, 2, 3, 4, 5, 6, 7 Next |
Author |
|
bitRAKE 17 Apr 2020, 15:08
Shadow space is always needed that is why I put ENTER instruction at the beginning: it aligns the stack MOD16, and also reserves local space for shadow and additional parameters -- all in one instruction. If one is not playing with RSP (remains constant) then nothing further needs to be done at this call level.
https://board.flatassembler.net/topic.php?t=7119 |
|||
17 Apr 2020, 15:08 |
|
Fulgurance 17 Apr 2020, 16:32
But i don't understand. Why some UEFI fonction work without this stack ? For example, when i call UEFI function to ClearScreen, no problem without any stack.
And why do you change stack value into this address (for example)? Code: mov qword[rsp+8*5],EFI_OPEN_PROTOCOL_BY_HANDLE_PROTOCOL In this link provided by you : https://en.wikipedia.org/wiki/X86_calling_conventions#Microsoft_x64_calling_convention, it's written about shadow space: Quote: it is the caller's responsibility to allocate 32 bytes of "shadow space" on the stack right before calling the function But if i calculate good, 32 bytes is for 64bits x4 ? But when you pass rsp+8*5, what is the connection ??? I'm lost. And if you need to initialize stack, why i don't seen in your code any SS value asignment ? And in last, why there many UEFI calling convention, for example Sys V, Microsoft ... etc, UEFI isn't unified firmware .... ? If i remember good, UEFI boot from PE executable, no other file type ... how Unix can call differently UEFI fonction ? Some points are very obscure |
|||
17 Apr 2020, 16:32 |
|
bitRAKE 17 Apr 2020, 17:15
It's true that UEFI uses PE file format, but there is no OS. So, much of the OS-specific PE requirements don't apply. For example, there is no sub-system or exception handling. This is why we need to fall back to the UEFI spec in all cases.
I know where RSP is at all times in my code. I do reserve shadow space at the start of each level of code and know it is there throughout: Code: Level0: enter 1024,0 ; shadow space mov [rsp+8*5],$CAFE call Level1 call Level2 call Level3 call Level4 ;... leave retn Level1: enter 32,0 ; local shadow call Level2 leave retn Level2: enter 32,0 ; local shadow call Level1 leave retn ; etc... Quote: on the stack right before calling the function It is completely possible that some UEFI functions do not use the shadow space, but we cannot rely on that being the case on all firmware. Unix needs to support different calling conventions in their compilers. I don't care about any of those politics that get in the way of my programming. On Linux, I use the red zone and different registers - whatever the environment specifies. _________________ ¯\(°_o)/¯ “languages are not safe - uses can be” Bjarne Stroustrup |
|||
17 Apr 2020, 17:15 |
|
Fulgurance 17 Apr 2020, 17:35
Okay, now i understand for ENTER call just one time. But i don't understand your calculation. why rsp + 8*5 ? Dont you store qword into the stack ?
|
|||
17 Apr 2020, 17:35 |
|
Fulgurance 17 Apr 2020, 18:10
Okay... And about my previous question ?
Quote: Okay, now i understand for ENTER call just one time. But i don't understand your calculation. why rsp + 8*5 ? Dont you store qword into the stack ? And why when i try to call BootServices LocateHandle function, i have return of unknown error code ? I haven't 0,1 or 2 ... |
|||
17 Apr 2020, 18:10 |
|
revolution 17 Apr 2020, 18:16
All functions are entered with the stack at 16n+8 because the call will push the return address of 8 bytes. So you need to push one more 8 byte value to align it to 16n+0. And from there you always adjust even multiples of 8 bytes to keep the stack at 16n+0.
This is why your EFIBootServicesStatusCode code is incorrect. It doesn't align the stack to 16n+0 and it doesn't allocate any shadow space when it calls external functions. When your code doesn't comply with the spec then you can't guarantee it will work. Slow down. Test each piece one-by-one. You have many wheels turning all at once, and when one or more fail you don't know which are working and which are failing. |
|||
17 Apr 2020, 18:16 |
|
bitRAKE 17 Apr 2020, 18:40
Fulgurance wrote: But i don't understand your calculation. why rsp + 8*5 ? Dont you store qword into the stack ? [rsp+8*0] ; shadow space for parm one (volitile) [rsp+8*1] ; shadow space for parm two (volitile) [rsp+8*2] ; shadow space for parm three (volitile) [rsp+8*3] ; shadow space for parm four (volitile) [rsp+8*4] ; param five must be stored here [rsp+8*5] ; param six must be stored here _________________ ¯\(°_o)/¯ “languages are not safe - uses can be” Bjarne Stroustrup |
|||
17 Apr 2020, 18:40 |
|
Fulgurance 17 Apr 2020, 18:56
Yes,it's an address, it's true. I have forgotten...
But i don't understand: if you have parameter on the stack at RSP+ 8*5 address, the next parameter piushed into stack as 64 bits size, the next offset is 64 bits further on ? For example, when i make that simple code, the second label have this address 64 bits further on. It's not the same thing into stack ? Code: label1: dq ? label2: dq ? EDIT: I think i have forgotten address are calculate in bytes omg... not bits |
|||
17 Apr 2020, 18:56 |
|
Fulgurance 17 Apr 2020, 20:22
For example, just that, is it correct ?
Code: enter 24,0 and spl,0xF0 mov rcx,[rdx+EFISystemTable.BootServices] mov rax,[rcx+EFIBootServices.LocateProtocol] mov rdx,GUID.EFIGraphicsOutputProtocol xor r8,r8 mov r9,Interface.EFIGraphicsOutputProtocol call rax leave I prepare shadow stack for 3 parameters, 3 x 8 bytes = 24, with enter 24,0, is it okay ? I ensure stack alignment with and spl,0xF0, is it right ? Next, i pass parameters and call function. I release stack after with leave instruction. Is it okay ? |
|||
17 Apr 2020, 20:22 |
|
revolution 17 Apr 2020, 20:29
Fulgurance wrote: For example, just that, is it correct ? Follow the spec. When a function starts you need to align the stack first, usually with a "push rbp", but can also be "sub rsp,8". Do this once only at function entry, and reverse it upon function exit. Using "and rsp,-0x10" can be a problem if you ever want to do a "ret" later, I suggest not to use "and rsp,-0x10", it is a bad habit. Allocating 24 bytes is wrong. Always allocate in multiples of 16. Always allocate after stack alignment is done at function entry. Always allocate at least 32 bytes of shadow space for the callee to use, and allocate more when you have more than four parameters. I suggest not to use enter or leave, they have hidden usage of registers and make the code more opaque IMO. Instead use explicit registers and instructions that clearly show what is happening. Follow the spec. You are getting yourself in trouble by trying to do different things. |
|||
17 Apr 2020, 20:29 |
|
Fulgurance 17 Apr 2020, 20:57
Thanks, your explanation is simple. Some of yours examples code are difficult for me, many instructions used is for me unknow. I know just base instruction, but not SSE or other ...
I must stay simple. I have trying to correct my previous code, is this okay ? Code: mov rbp,rsp add rsp,0x20 sub rsp,0x8 mov rcx,[rdx+EFISystemTable.BootServices] mov rax,[rcx+EFIBootServices.LocateProtocol] mov rdx,GUID.EFIGraphicsOutputProtocol xor r8,r8 mov r9,Interface.EFIGraphicsOutputProtocol call rax mov rsp,rbp (i save rsp pointer, i think it's good idea) |
|||
17 Apr 2020, 20:57 |
|
revolution 17 Apr 2020, 21:08
You didn't give enough context. Is that an entire function?
Assuming it is then I suggest something like this: Code: my_function: push rbp ;we must save this for the caller, and this also aligns the stack ;mov rbp,rsp ;we don't need this here, but if you have entry parameters then this can be useful sub rsp,0x20 ;minimum requirement, use more if you have more than 4 parameters mov rcx,[rdx+EFISystemTable.BootServices] ;rcx = first parameter to callee mov rax,[rcx+EFIBootServices.LocateProtocol] mov rdx,GUID.EFIGraphicsOutputProtocol ;rdx = second parameter to callee xor r8,r8 ;r8 = third parameter to callee mov r9,Interface.EFIGraphicsOutputProtocol ;r9 = fourth parameter to callee call rax add rsp,0x20 ;reverse of what we allocated above pop rbp ;now the top of stack is the return address ret |
|||
17 Apr 2020, 21:08 |
|
Fulgurance 17 Apr 2020, 21:32
Why you push RBP, i don't understand that... Is it mandatory for calling convention into UEFI ? What do you call the "return address" ?
If programmer use RBP, is it not just to save RSP ? Don't you have initialized any value at RBP, no ? Just look this if you need my code context (i have applied your example): Code: format pe64 efi entry Main section '.text' code readable executable Main: mov [SystemTable],rdx push rbp sub rsp,0x20 mov rcx,[rdx+EFISystemTable.BootServices] mov rax,[rcx+EFIBootServices.LocateProtocol] mov rdx,GUID.EFIGraphicsOutputProtocol xor r8,r8 mov r9,Interface.EFIGraphicsOutputProtocol call rax add rsp,0x20 pop rbp mov rdx,[SystemTable] jmp $ include "EFIBase/GUID.fasm" include "EFIBase/Interface.fasm" include "EFIBase/EFIDataTypes.fasm" include "EFIStatusCode/EFIBootServicesStatusCode.fasm" include "EFITableHeader/EFITableHeader.fasm" include "EFISystemTable/EFISystemTable.fasm" include "EFIBootServices/EFIBootServices.fasm" include "EFIBootServices/IndexTables/EFILocateSearchType.fasm" include "EFISimpleTextOutputProtocol/EFISimpleTextOutputProtocol.fasm" include "EFIGraphicsOutputProtocol/EFIGraphicsOutputProtocol.fasm" include "EFIGraphicsOutputProtocol/IndexTables/EFIGraphicsOutputBltOperation.fasm" section '.data' data readable writable executable SystemTable: dq ? |
|||
17 Apr 2020, 21:32 |
|
revolution 17 Apr 2020, 21:45
I used push rbp for three purposes.
The first is to align the stack because the caller always gives us a stack align to 16n+8, so we have to fix it to 16n+0. The second is that now we can use rbp for our own purposes. We have to return a valid value in rbp to the caller, so saving it means we can now use it and restore it later when we do a ret. The third is to give access to any caller parameters on the stack. It isn't always necessary to save rbp if you never use it, but if you don't save it then you need an alternative instruction to align the stack. "sub rsp,8" will also do that. It encodes to more bytes if that is an issue to you. But you need something to align the stack. You can't avoid it. |
|||
17 Apr 2020, 21:45 |
|
revolution 17 Apr 2020, 21:48
BTW: Are you sure the SystemTable parameter is in RDX?
RDX is the second parameter. What is in RCX? |
|||
17 Apr 2020, 21:48 |
|
Fulgurance 17 Apr 2020, 22:16
How can you sure stack is aligned when you push RBP, it's that i understand ...
And you say 16n+8, what is n ? What do you mean ? Quote:
When UEFI start, isn't RDX pointer to SystemTable ? RCX must contain first parameter ? Is it that ??? :O I'm totally lost o_o |
|||
17 Apr 2020, 22:16 |
|
revolution 17 Apr 2020, 23:01
The calling standard guarantees that the stack is at 16n+8 upon entry to your code. "n" is any number so multiplying by 16 means the lowest four bits are zero. So a single push will put the stack at 16n+0 and your stack is now aligned.
|
|||
17 Apr 2020, 23:01 |
|
bitRAKE 18 Apr 2020, 06:25
RCX is the ImageHandle
RDX is the SystemTable Signed images can use their handle to access protocols if SecureBoot is enabled. _________________ ¯\(°_o)/¯ “languages are not safe - uses can be” Bjarne Stroustrup |
|||
18 Apr 2020, 06:25 |
|
revolution 18 Apr 2020, 06:59
So have we verified that setting rcx = [rdx+EFISystemTable.BootServices] as the first parameter for the call is correct? It seems to be redundant if it is.
|
|||
18 Apr 2020, 06:59 |
|
Goto page Previous 1, 2, 3, 4, 5, 6, 7 Next < Last Thread | Next Thread > |
Forum Rules:
|
Copyright © 1999-2024, Tomasz Grysztar. Also on GitHub, YouTube.
Website powered by rwasa.