diff options
Diffstat (limited to '')
-rw-r--r-- | toolkit/crashreporter/test/win64UnwindInfoTests.asm | 378 |
1 files changed, 378 insertions, 0 deletions
diff --git a/toolkit/crashreporter/test/win64UnwindInfoTests.asm b/toolkit/crashreporter/test/win64UnwindInfoTests.asm new file mode 100644 index 0000000000..4dd5ce7646 --- /dev/null +++ b/toolkit/crashreporter/test/win64UnwindInfoTests.asm @@ -0,0 +1,378 @@ +; This Source Code Form is subject to the terms of the Mozilla Public +; License, v. 2.0. If a copy of the MPL was not distributed with this +; file, You can obtain one at http://mozilla.org/MPL/2.0/. + +; Comments indicate stack memory layout during execution. +; For example at the top of a function, where RIP just points to the return +; address, the stack looks like +; rip = [ra] +; And after pushing rax to the stack, +; rip = [rax][ra] +; And then, after allocating 20h bytes on the stack, +; rip = [..20..][rax][ra] +; And then, after pushing a function pointer, +; rip = [pfn][..20..][rax][ra] + +include ksamd64.inc + +.code + +; It helps to add padding between functions so they're not right up against +; each other. Adds clarity to debugging, and gives a bit of leeway when +; searching for symbols (e.g. a function whose last instruction is CALL +; would push a return address that's in the next function.) +PaddingBetweenFunctions macro + repeat 10h + int 3 + endm +endm + +DoCrash macro + mov rax, 7 + mov byte ptr [rax], 9 +endm + +PaddingBetweenFunctions + +; There is no rip addressing mode in x64. The only way to get the value +; of rip is to call a function, and pop it from the stack. +WhoCalledMe proc + pop rax ; rax is now ra + push rax ; Restore ra so this function can return. + sub rax, 5 ; Correct for the size of the call instruction + ret +WhoCalledMe endp + +PaddingBetweenFunctions + +; Any function that we expect to test against on the stack, we'll need its +; real address. If we use function pointers in C, we'll get the address to jump +; table entries. This bit of code at the beginning of each function will +; return the real address we'd expect to see in stack traces. +; +; rcx (1st arg) = mode +; rax (return) = address of either NO_MANS_LAND or this function. +; +; When mode is 0, we place the address of NO_MANS_LAND in RAX, for the function +; to use as it wants. This is just for convenience because almost all functions +; here need this address at some point. +; +; When mode is 1, the address of this function is returned. +TestHeader macro + call WhoCalledMe + test rcx, rcx + je continue_test + ret +continue_test: + inc rcx + call x64CrashCFITest_NO_MANS_LAND + xor rcx, rcx +endm + +; The point of this is to add a stack frame to test against. +; void* x64CrashCFITest_Launcher(int getAddress, void* pTestFn) +x64CrashCFITest_Launcher proc frame + TestHeader + + .endprolog + call rdx + ret +x64CrashCFITest_Launcher endp + +PaddingBetweenFunctions + +; void* x64CrashCFITest_NO_MANS_LAND(uint64_t mode); +; Not meant to be called. Only when mode = 1 in order to return its address. +; Place this function's address on the stack so the stack scanning algorithm +; thinks this is a return address, and places it on the stack trace. +x64CrashCFITest_NO_MANS_LAND proc frame + TestHeader + .endprolog + ret +x64CrashCFITest_NO_MANS_LAND endp + +PaddingBetweenFunctions + +; Test that we: +; - handle unknown opcodes gracefully +; - fall back to other stack unwind strategies if CFI doesn't work +; +; In order to properly unwind this frame, we'd need to fully support +; SET_FPREG with offsets, plus restoring registers via PUSH_NONVOL. +; To do this, sprinkle the stack with bad return addresses +; and stack pointers. +x64CrashCFITest_UnknownOpcode proc frame + TestHeader + + push rax + .allocstack 8 + + push rbp + .pushreg rbp + + push rax + push rsp + push rax + push rsp + .allocstack 20h + ; rsp = [rsp][pfn][rsp][pfn][rbp][pfn][ra] + + lea rbp, [rsp+10h] + .setframe rbp, 10h + ; rsp = [rsp][pfn] [rsp][pfn][rbp][pfn][ra] + ; rbp = ^ + + .endprolog + + ; Now modify RSP so measuring stack size from unwind ops will not help + ; finding the return address. + push rax + push rsp + ; rsp = [rsp][pfn][rsp][pfn] [rsp][pfn][rbp][pfn][ra] + + DoCrash + +x64CrashCFITest_UnknownOpcode endp + +PaddingBetweenFunctions + +; void* x64CrashCFITest_PUSH_NONVOL(uint64_t mode); +; +; Test correct handling of PUSH_NONVOL unwind code. +; +x64CrashCFITest_PUSH_NONVOL proc frame + TestHeader + + push r10 + .pushreg r10 + push r15 + .pushreg r15 + push rbx + .pushreg rbx + push rsi + .pushreg rsi + push rbp + .pushreg rbp + ; rsp = [rbp][rsi][rbx][r15][r10][ra] + + push rax + .allocstack 8 + ; rsp = [pfn][rbp][rsi][rbx][r15][r10][ra] + + .endprolog + + DoCrash + +x64CrashCFITest_PUSH_NONVOL endp + +PaddingBetweenFunctions + +; void* x64CrashCFITest_ALLOC_SMALL(uint64_t mode); +; +; Small allocations are between 8bytes and 512kb-8bytes +; +x64CrashCFITest_ALLOC_SMALL proc frame + TestHeader + + push rax + push rax + push rax + push rax + .allocstack 20h + ; rsp = [pfn][pfn][pfn][pfn][ra] + + .endprolog + + DoCrash + +x64CrashCFITest_ALLOC_SMALL endp + +PaddingBetweenFunctions + +; void* x64CrashCFITest_ALLOC_LARGE(uint64_t mode); +; +; Allocations between 512kb and 4gb +; Note: ReserveStackSpace() in nsTestCrasher.cpp pre-allocates stack +; space for this. +x64CrashCFITest_ALLOC_LARGE proc frame + TestHeader + + sub rsp, 0a000h + .allocstack 0a000h + ; rsp = [..640kb..][ra] + + mov qword ptr [rsp], rax + ; rsp = [pfn][..640kb-8..][ra] + + .endprolog + + DoCrash + +x64CrashCFITest_ALLOC_LARGE endp + +PaddingBetweenFunctions + +; void* x64CrashCFITest_SAVE_NONVOL(uint64_t mode); +; +; Test correct handling of SAVE_NONVOL unwind code. +; +x64CrashCFITest_SAVE_NONVOL proc frame + TestHeader + + sub rsp, 30h + .allocstack 30h + ; rsp = [..30..][ra] + + mov qword ptr [rsp+28h], r10 + .savereg r10, 28h + mov qword ptr [rsp+20h], rbp + .savereg rbp, 20h + mov qword ptr [rsp+18h], rsi + .savereg rsi, 18h + mov qword ptr [rsp+10h], rbx + .savereg rbx, 10h + mov qword ptr [rsp+8], r15 + .savereg r15, 8 + ; rsp = [r15][rbx][rsi][rbp][r10][ra] + + mov qword ptr [rsp], rax + + ; rsp = [pfn][r15][rbx][rsi][rbp][r10][ra] + + .endprolog + + DoCrash + +x64CrashCFITest_SAVE_NONVOL endp + +PaddingBetweenFunctions + +; void* x64CrashCFITest_SAVE_NONVOL_FAR(uint64_t mode); +; +; Similar to the test above but adding 640kb to most offsets. +; Note: ReserveStackSpace() in nsTestCrasher.cpp pre-allocates stack +; space for this. +x64CrashCFITest_SAVE_NONVOL_FAR proc frame + TestHeader + + sub rsp, 0a0030h + .allocstack 0a0030h + ; rsp = [..640k..][..30..][ra] + + mov qword ptr [rsp+28h+0a0000h], r10 + .savereg r10, 28h+0a0000h + mov qword ptr [rsp+20h+0a0000h], rbp + .savereg rbp, 20h+0a0000h + mov qword ptr [rsp+18h+0a0000h], rsi + .savereg rsi, 18h+0a0000h + mov qword ptr [rsp+10h+0a0000h], rbx + .savereg rbx, 10h+0a0000h + mov qword ptr [rsp+8+0a0000h], r15 + .savereg r15, 8+0a0000h + ; rsp = [..640k..][..8..][r15][rbx][rsi][rbp][r10][ra] + + mov qword ptr [rsp], rax + + ; rsp = [pfn][..640k..][r15][rbx][rsi][rbp][r10][ra] + + .endprolog + + DoCrash + +x64CrashCFITest_SAVE_NONVOL_FAR endp + +PaddingBetweenFunctions + +; void* x64CrashCFITest_SAVE_XMM128(uint64_t mode); +; +; Test correct handling of SAVE_XMM128 unwind code. +x64CrashCFITest_SAVE_XMM128 proc frame + TestHeader + + sub rsp, 30h + .allocstack 30h + ; rsp = [..30..][ra] + + movdqu [rsp+20h], xmm6 + .savexmm128 xmm6, 20h + ; rsp = [..20..][xmm6][ra] + + movdqu [rsp+10h], xmm15 + .savexmm128 xmm15, 10h + ; rsp = [..10..][xmm15][xmm6][ra] + + mov qword ptr [rsp], rax + ; rsp = [pfn][..8..][xmm15][xmm6][ra] + + .endprolog + + DoCrash + +x64CrashCFITest_SAVE_XMM128 endp + +PaddingBetweenFunctions + +; void* x64CrashCFITest_SAVE_XMM128(uint64_t mode); +; +; Similar to the test above but adding 640kb to most offsets. +; Note: ReserveStackSpace() in nsTestCrasher.cpp pre-allocates stack +; space for this. +x64CrashCFITest_SAVE_XMM128_FAR proc frame + TestHeader + + sub rsp, 0a0030h + .allocstack 0a0030h + ; rsp = [..640kb..][..30..][ra] + + movdqu [rsp+20h+0a0000h], xmm6 + .savexmm128 xmm6, 20h+0a0000h + ; rsp = [..640kb..][..20..][xmm6][ra] + + movdqu [rsp+10h+0a0000h], xmm6 + .savexmm128 xmm15, 10h+0a0000h + ; rsp = [..640kb..][..10..][xmm15][xmm6][ra] + + mov qword ptr [rsp], rax + ; rsp = [pfn][..640kb..][..8..][xmm15][xmm6][ra] + + .endprolog + + DoCrash + +x64CrashCFITest_SAVE_XMM128_FAR endp + +PaddingBetweenFunctions + +; void* x64CrashCFITest_EPILOG(uint64_t mode); +; +; The epilog unwind op will also set the unwind version to 2. +; Test that we don't choke on UWOP_EPILOG or version 2 unwind info. +x64CrashCFITest_EPILOG proc frame + TestHeader + + push rax + .allocstack 8 + ; rsp = [pfn][ra] + + .endprolog + + DoCrash + + .beginepilog + + ret + +x64CrashCFITest_EPILOG endp + +PaddingBetweenFunctions + +; Having an EOF symbol at the end of this file contains symbolication to this +; file. So addresses beyond this file don't get mistakenly symbolicated as a +; meaningful function name. +x64CrashCFITest_EOF proc frame + TestHeader + .endprolog + ret +x64CrashCFITest_EOF endp + +end |