| // Copyright 2017 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "asm.h" |
| #include "setjmp_impl.h" |
| #include <zircon/tls.h> |
| |
| #define DW_CFA_expression 0x10 |
| #define DW_CFA_val_expression 0x16 |
| #define DW_OP_breg0 0x70 |
| #define DW_OP_breg2 (DW_OP_breg0 + 2) |
| #define DW_OP_breg5 (DW_OP_breg0 + 5) |
| #define DW_OP_breg7 (DW_OP_breg0 + 7) |
| #define DW_OP_deref 0x06 |
| |
| // CFI to find regno at 8*index(%rdi). |
| .macro jb_cfi regno, index |
| .if 8 * \index < 0x7f |
| .cfi_escape DW_CFA_expression, \regno, 2, DW_OP_breg5, 8 * \index |
| .else |
| .error "offset too large for one-byte SLEB128" |
| .endif |
| .endm |
| |
| .macro restore reg, index |
| mov 8*\index(%rdi), \reg |
| .cfi_same_value \reg |
| .endm |
| |
| ENTRY(longjmp) |
| |
| // The sanitizer runtime wants to be informed of non-local exits. |
| // Call __asan_handle_no_return() before doing the actual longjmp. |
| #if __has_feature(address_sanitizer) |
| // Save our incoming argument registers on the stack around calling |
| // __asan_handle_no_return. The incoming stack is misaligned by one |
| // word, so we need to move the stack by an odd number of words, |
| // hence the unnecessary save/restore of %rax. |
| push_reg %rax |
| push_reg %rsi |
| push_reg %rdi |
| call __asan_handle_no_return@PLT |
| pop_reg %rdi |
| pop_reg %rsi |
| pop_reg %rax |
| #endif |
| |
| // Calculate return value: %r11d = %esi ?: 1 |
| mov $1, %r11d |
| test %esi, %esi |
| cmovnz %esi, %r11d |
| |
| // Load the mangled values into temporaries. |
| mov 8*JB_PC(%rdi), %rax |
| .cfi_undefined %rax |
| mov 8*JB_SP(%rdi), %rcx |
| mov 8*JB_FP(%rdi), %rdx |
| mov 8*JB_USP(%rdi), %rsi |
| .cfi_undefined %rsi |
| |
| // Demangle each temporary. |
| xor __setjmp_manglers+8*JB_PC(%rip), %rax |
| xor __setjmp_manglers+8*JB_SP(%rip), %rcx |
| xor __setjmp_manglers+8*JB_FP(%rip), %rdx |
| xor __setjmp_manglers+8*JB_USP(%rip), %rsi |
| |
| // The next instruction clobbers the state of longjmp's caller. |
| // So from here on, we'll use CFI that unwinds to setjmp's caller instead. |
| // Both callers have the same %rdi value, which we're still using. |
| .cfi_register %rip, %rax |
| .cfi_register %rsp, %rcx |
| .cfi_register %rbp, %rdx |
| //.cfi_register %unsafe_sp, %rsi -- No DWARF register number for it! |
| jb_cfi 3, JB_RBX |
| jb_cfi 12, JB_R12 |
| jb_cfi 13, JB_R13 |
| jb_cfi 14, JB_R14 |
| jb_cfi 15, JB_R15 |
| |
| // Restore all the vanilla callee-saves registers. |
| restore %rbx, JB_RBX |
| restore %r12, JB_R12 |
| restore %r13, JB_R13 |
| restore %r14, JB_R14 |
| restore %r15, JB_R15 |
| |
| // Restore the demangled values. |
| mov %rdx, %rbp |
| .cfi_same_value %rbp |
| mov %rsi, %fs:ZX_TLS_UNSAFE_SP_OFFSET |
| //.cfi_same_value %unsafe_sp -- No DWARF register number for it! |
| mov %rax, (%rcx) |
| .cfi_escape DW_CFA_expression, 16, 3, DW_OP_breg2, 0, DW_OP_deref |
| |
| // Restore SP last. |
| // After this, our CFA is setjmp's CFA rather than longjmp's CFA. |
| mov %rcx, %rsp |
| .cfi_same_value %rsp |
| .cfi_escape DW_CFA_expression, 16, 3, DW_OP_breg7, 0, DW_OP_deref |
| |
| // Don't leak the demangled values. |
| xor %ecx, %ecx |
| xor %edx, %edx |
| xor %esi, %esi |
| |
| mov %r11d, %eax |
| ret |
| |
| END(longjmp) |
| |
| ALIAS(longjmp, _longjmp) |
| WEAK_ALIAS(longjmp, siglongjmp) |