;; @file ; IPRT - Unwind hacks for Windows x64. ; ; ; Copyright (C) 2006-2007 Sun Microsystems, Inc. ; ; This file is part of VirtualBox Open Source Edition (OSE), as ; available from http://www.virtualbox.org. This file is free software; ; you can redistribute it and/or modify it under the terms of the GNU ; General Public License (GPL) as published by the Free Software ; Foundation, in version 2 as it comes in the "COPYING" file of the ; VirtualBox OSE distribution. VirtualBox OSE is distributed in the ; hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. ; ; The contents of this file may alternatively be used under the terms ; of the Common Development and Distribution License Version 1.0 ; (CDDL) only, as it comes in the "COPYING.CDDL" file of the ; VirtualBox OSE distribution, in which case the provisions of the ; CDDL are applicable instead of those of the GPL. ; ; You may elect to license modified versions of this file under the ; terms and conditions of either the GPL or the CDDL or both. ; ; Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa ; Clara, CA 95054 USA or visit http://www.sun.com if you need ; additional information or have any questions. ; %ifndef ___iprt_ntwrap_mac %define ___iprt_ntwrap_mac %include "iprt/asmdefs.mac" %ifdef RT_WITH_W64_UNWIND_HACK %ifdef RT_ARCH_AMD64 ;; ; Common prolog, take the proc name as argument. ; This creates a 0x80 byte stack frame. ; %macro NtWrapProlog 1 [proc_frame %1] push rbp [pushreg rbp] mov rbp, rsp [setframe rbp, 0] sub rsp, 0x80 [allocstack 0x80] ; save rdi and load rbp into it mov [rbp - 8h], rdi [savereg rdi, 0x78] mov rdi, rbp [endprolog] %endmacro ;; ; Common epilog, take the proc name as argument. %macro NtWrapEpilog 1 ; restore rbp and rdi then return. mov rbp, rdi mov rdi, [rdi - 8h] leave ret [endproc_frame %1] %endmacro ;; ; Create a stack marker with the rbp. The marker is 32 byte big. ; This is 32-byte aligned and 32 byte in size. ; ; Trashes r10 %macro NtWrapCreateMarker 0 lea r10, [rbp - 30h] and r10, ~1fh ; 32-byte align it. mov dword [r10 ], 0x20080901 mov dword [r10 + 04h], 0x20080902 mov qword [r10 + 08h], rbp mov dword [r10 + 10h], 0x20080903 mov dword [r10 + 14h], 0x20080904 mov qword [r10 + 18h], rbp %endmacro ;; ; Destroys the stack marker. ; ; Trashes r10 %macro NtWrapDestroyMarker 0 lea r10, [rbp - 30h] and r10, ~1fh ; 32-byte align it. mov [r10 ], rbp mov [r10 + 08h], rbp mov [r10 + 10h], rbp mov [r10 + 18h], rbp %endmacro ;; ; Wraps a function with 4 or less argument that will go into registers. %macro NtWrapDrv2DynFunctionWithAllRegParams 2 extern NAME(%2) BEGINPROC %1%2 NtWrapProlog %1%2 NtWrapCreateMarker call NAME(%2) NtWrapDestroyMarker NtWrapEpilog %1%2 ENDPROC %1%2 %endmacro BEGINCODE ; ; The following is not used when we're in the dynamically loaded code ; and we wish to avoid it because it drags in a dependency on IoGetStackLimits. ; ; (Could perhaps fix this differently by instantiating NtWrapLocateMarkerHelper ; in some way, but this works just the same...) ; %ifndef IN_DYNAMICLOAD_CODE ;; ; Find the stack marker with the rbp of the entry frame. ; ; Search the current stack page inline, call a helper function ; which does a safe search of any further stack pages. ; ; Trashes rax, r10 and r11. ; Modifies rbp ; %macro NtWrapLocateMarker 0 mov rax, rbp and rax, ~1fh ; 32-byte align it. ; ; Calc remainig space in the current page. If we're on a ; page boundrary, we'll search the entire previous page. ; mov r10, rax neg r10 and r10, 0fffh inc r10 shr r10, 5 ; /= 32 bytes jz %%not_found ; If zero, take the slow path ; ; The search loop. ; %%again: dec r10 lea rax, [rax + 20h] jz %%not_found cmp dword [rax ], 0x20080901 je %%candidate jmp %%again %%not_found: call NAME(NtWrapLocateMarkerHelper) jmp %%done %%candidate: cmp dword [rax + 04h], 0x20080902 jne %%again cmp dword [rax + 10h], 0x20080903 jne %%again cmp dword [rax + 14h], 0x20080904 jne %%again mov r11, [rax + 08h] cmp r11, [rax + 18h] jne %%again ; found it, change rbp. mov rbp, r11 %%done: %endmacro ;; ; Wraps a function with 4 or less argument that will go into registers. %macro NtWrapDyn2DrvFunctionWithAllRegParams 2 extern NAME(%2) BEGINPROC %1%2 NtWrapProlog %1%2 NtWrapLocateMarker call NAME(%2) NtWrapEpilog %1%2 ENDPROC %1%2 %endmacro ;; ; Wraps a function with 5 argument, where the first 4 goes into registers. %macro NtWrapDyn2DrvFunctionWith5Params 2 extern NAME(%2) BEGINPROC %1%2 NtWrapProlog %1%2 NtWrapLocateMarker mov r11, [rdi + 30h] mov [rsp + 20h], r11 call NAME(%2) NtWrapEpilog %1%2 ENDPROC %1%2 %endmacro ;; ; Wraps a function with 6 argument, where the first 4 goes into registers. %macro NtWrapDyn2DrvFunctionWith6Params 2 extern NAME(%2) BEGINPROC %1%2 NtWrapProlog %1%2 NtWrapLocateMarker mov r11, [rdi + 30h] mov [rsp + 20h], r11 mov r10, [rdi + 38h] mov [rsp + 28h], r10 call NAME(%2) NtWrapEpilog %1%2 ENDPROC %1%2 %endmacro ;; ; Wraps a function with 7 argument, where the first 4 goes into registers. %macro NtWrapDyn2DrvFunctionWith7Params 2 extern NAME(%2) BEGINPROC %1%2 NtWrapProlog %1%2 NtWrapLocateMarker mov r11, [rdi + 30h] mov [rsp + 20h], r11 mov r10, [rdi + 38h] mov [rsp + 28h], r10 mov rax, [rdi + 40h] mov [rsp + 30h], rax call NAME(%2) NtWrapEpilog %1%2 ENDPROC %1%2 %endmacro extern IoGetStackLimits ;; ; Helper that cautiously continues the stack marker search ; NtWrapLocateMarker started. ; ; The stack layout at the time is something like this. ; rbp+08h callers return address. ; rbp-00h saved rbp ; rbp-08h saved rdi ; rbp-09h ; thru unused. ; rbp-80h ; rbp-88h our return address. ; rbp-89h ; thru callee register dump zone. ; rbp-a0h ; ; @param rax Current stack location. ; @param rdi Parent stack frame pointer. (This should equal rbp on entry.) ; ; Trashes: rax, r10, r11. ; Will use the callers stack frame for register saving ASSUMING that ; rbp-80h thru rbp-09h is unused. ; ; Modifies: rbp ; BEGINPROC NtWrapLocateMarkerHelper ; ; Prolog. Save volatile regs and reserve callee space. ; sub rsp, 20h ; For IoGetStackLimits(). mov [rdi - 80h], rax mov [rdi - 78h], rcx mov [rdi - 70h], rdx mov [rdi - 68h], r8 mov [rdi - 60h], r9 ; ; Call VOID IoGetStackLimits(OUT PULONG_PTR LowLimit, OUT PULONG_PTR HighLimit); ; ; Use rdi-40h for the high limit and rdi-50h for the low one, we're only ; interested in the high one. ; lea rcx, [rdi - 40h] ; arg #1 LowLimit lea rdx, [rdi - 50h] ; arg #2 HighLimit mov [rdx], eax ; paranoia - init to end of current search. call IoGetStackLimits ; ; Move the top address into r10, restore rax and continue ; the search. Check that r10 is less than 3 pages from rax. ; mov rax, [rdi - 80h] ; Restore eax (see prolog) mov r10, [rdi - 50h] ; HighLimit and r10, ~1fh ; 32-byte align it (downwards) sub r10, rax jz .not_found ; If already at the top of the stack. cmp r10, 3000h jae .out_of_bounds ; If too far away, something is busted. shr r10, 5 ; /= 32. ; The loop body. .search_loop: cmp dword [rax ], 0x20080901 je .candidate .continue_searching: dec r10 jz .not_found lea rax, [rax + 20h] jmp .search_loop ; Found the first marker, check for the rest. .candidate: cmp dword [rax + 04h], 0x20080902 jne .continue_searching cmp dword [rax + 10h], 0x20080903 jne .continue_searching cmp dword [rax + 14h], 0x20080904 jne .continue_searching mov r11, [rax + 08h] cmp r11, [rax + 18h] jne .continue_searching ; found it, change rbp. mov rbp, r11 ; ; Restore registers and pop the stack frame. ; .epilog: mov r9, [rdi - 60h] mov r8, [rdi - 68h] mov rdx, [rdi - 70h] mov rcx, [rdi - 78h] ; mov rax, [rdi - 80h] add rsp, 20h ret ; ; Needless to say, this isn't supposed to happen. Thus the int3. ; Note down r10 and rax. ; .out_of_bounds: %ifdef DEBUG int3 %endif .not_found: %ifdef DEBUG int3 %endif jmp .epilog ENDPROC NtWrapLocateMarkerHelper %endif ; !IN_DYNAMICLOAD_CODE %endif ; RT_ARCH_AMD64 %endif ; RT_WITH_W64_UNWIND_HACK %endif ; !___iprt_ntwrap_mac