Page 1 of 1

GP Excpetion when doing retf in Long Mode

Posted: Tue May 28, 2019 5:02 am
by Freggar
Emulator: QEMU
Compiler: x86_64-w64-mingw32-gcc (and as)

I'm trying to simulate a far jump via retf. For testing purposes I try to jump to the same segment I'm already in. The following code is executed after ExitBootServices():

Code: Select all

.intel_syntax noprefix
.global test_retf

test_retf:
	push rbp 
	mov rbp, rsp

	mov ax, cs
	push ax # push cs
	push jump_target # push target
	retf

jump_target: 
	nop # do something after far jump
	pop rbp # Return
	ret

.att_syntax noprefix
Executing this code produces a GP Exception:

Code: Select all

!!!! X64 Exception Type - 0D(#GP - General Protection)  CPU Apic ID - 00000000 !!!!
ExceptionData - 0000000000009090
RIP  - 00000000066FB010, CS  - 0000000000000038, RFLAGS - 0000000000000046
RAX  - 0000000000000038, RCX - 0000000000000000, RDX - 0000000000000000
RBX  - 0000000000000000, RSP - 0000000007F1F986, RBP - 0000000007F1F990
RSI  - 0000000000000009, RDI - 0000000006E92118
R8   - 0000000007F1F93C, R9  - 0000000000000078, R10 - 0000000007BC4840
R11  - 0000000036E11C9A, R12 - 0000000000000000, R13 - 0000000006BDA018
R14  - 0000000000000000, R15 - 0000000007650000
DS   - 0000000000000030, ES  - 0000000000000030, FS  - 0000000000000030
GS   - 0000000000000030, SS  - 0000000000000030
CR0  - 0000000080010033, CR2 - 0000000000000000, CR3 - 0000000007C01000
CR4  - 0000000000000668, CR8 - 0000000000000000
DR0  - 0000000000000000, DR1 - 0000000000000000, DR2 - 0000000000000000
DR3  - 0000000000000000, DR6 - 00000000FFFF0FF0, DR7 - 0000000000000400
GDTR - 0000000007BEEA98 0000000000000047, LDTR - 0000000000000000
IDTR - 000000000765A018 0000000000000FFF,   TR - 0000000000000000
FXSAVE_STATE - 0000000007F1F5E0
!!!! Find image based on IP(0x66FB010) (No PDB)  (ImageBase=00000000066FA000, EntryPoint=00000000066FB020) !!!!
Can someone point me to what I'm doing wrong? I've read the RETF section from the AMD Programmer’s Manual Volume 3 and cannot find out what I'm doing wrong.

Re: GP Excpetion when doing retf in Long Mode

Posted: Tue May 28, 2019 5:52 am
by Octocontrabass
In long mode, "push ax" still only pushes two bytes onto the stack. You need an instruction that will push eight bytes, such as "push rax".

(This might not be the only problem, but it's the only one I can spot right now.)

Re: GP Excpetion when doing retf in Long Mode

Posted: Tue May 28, 2019 7:03 am
by Freggar
Thanks for the pointer, doing

Code: Select all

mov rax, cs
push rax 
instead, will yield the same error. Are you sure that I need to push 8 bytes?
All stack pops are determined by the operand size. If necessary, the target rIP is zero-extended to 64
bits before assuming program control.

AMD Programmer’s Manual Volume 3 -- RET (far)
This leads me to believe that I need to push 2 bytes since the CS register is 2 bytes.

Re: GP Excpetion when doing retf in Long Mode

Posted: Tue May 28, 2019 7:44 am
by Octocontrabass
Freggar wrote:Are you sure that I need to push 8 bytes?
Yes, assuming you're using a far return with a 64-bit operand size. I'm not familiar with GAS for x86 assembly, but I know NASM and YASM disagree on whether "retf" should be encoded with a 32-bit operand size or a 64-bit operand size. You might need to write something else to get the right operand size.

It's not made clear in the section of the manual you quote, but CS is popped using the operand size, discarding the unused high-order bits if the operand size is 32-bit or 64-bit.

Re: GP Excpetion when doing retf in Long Mode

Posted: Wed May 29, 2019 3:10 am
by Freggar
Yes, seems like it was an issue with operand size. I got it working by using the code from this stackoverflow post

Code: Select all

sub $16, %rsp
movq $8, 8(%rsp)
movabsq $fun, %rax
mov %rax, (%rsp)
lretq
The "lretq" translates to "rex.W retf" in objdump, so you probably need to specify 64-bit operand size.