sewardj | 8eb8bab | 2015-07-21 14:44:28 +0000 | [diff] [blame] | 1 | |
| 2 | /*--------------------------------------------------------------------*/ |
| 3 | /*--- Support for doing system calls. syscall-amd64-solaris.S ---*/ |
| 4 | /*--------------------------------------------------------------------*/ |
| 5 | |
| 6 | /* |
| 7 | This file is part of Valgrind, a dynamic binary instrumentation |
| 8 | framework. |
| 9 | |
Elliott Hughes | ed39800 | 2017-06-21 14:41:24 -0700 | [diff] [blame^] | 10 | Copyright (C) 2014-2017 Petr Pavlu |
sewardj | 8eb8bab | 2015-07-21 14:44:28 +0000 | [diff] [blame] | 11 | setup@dagobah.cz |
| 12 | |
| 13 | This program is free software; you can redistribute it and/or |
| 14 | modify it under the terms of the GNU General Public License as |
| 15 | published by the Free Software Foundation; either version 2 of the |
| 16 | License, or (at your option) any later version. |
| 17 | |
| 18 | This program is distributed in the hope that it will be useful, but |
| 19 | WITHOUT ANY WARRANTY; without even the implied warranty of |
| 20 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| 21 | General Public License for more details. |
| 22 | |
| 23 | You should have received a copy of the GNU General Public License |
| 24 | along with this program; if not, write to the Free Software |
| 25 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA |
| 26 | 02111-1307, USA. |
| 27 | |
| 28 | The GNU General Public License is contained in the file COPYING. |
| 29 | */ |
| 30 | |
florian | 3f1d613 | 2015-09-30 20:30:48 +0000 | [diff] [blame] | 31 | #include "pub_core_basics_asm.h" |
| 32 | |
sewardj | 8eb8bab | 2015-07-21 14:44:28 +0000 | [diff] [blame] | 33 | #if defined(VGP_amd64_solaris) |
| 34 | |
sewardj | 8eb8bab | 2015-07-21 14:44:28 +0000 | [diff] [blame] | 35 | #include "pub_core_vkiscnums_asm.h" |
| 36 | #include "libvex_guest_offsets.h" |
| 37 | |
| 38 | /* From vki-solaris.h, checked at startup by m_vki.c. */ |
| 39 | #define VKI_SIG_SETMASK 3 |
| 40 | |
| 41 | /* Prototype: |
| 42 | Int ML_(do_syscall_for_client_WRK)( |
| 43 | Int syscallno, // %rdi = %rbp-48 |
| 44 | void *guest_state, // %rsi = %rbp-40 |
| 45 | const vki_sigset_t *sysmask, // %rdx = %rbp-32 |
| 46 | const vki_sigset_t *postmask, // %rcx = %rbp-24 |
| 47 | UChar *cflag) // %r8 = %rbp-16 |
| 48 | */ |
| 49 | |
| 50 | .macro ESTABLISH_STACKFRAME |
| 51 | /* Establish stack frame. */ |
| 52 | pushq %rbp |
| 53 | movq %rsp, %rbp |
| 54 | pushq %rbx /* save %rbx */ |
| 55 | |
| 56 | /* We'll use %rbx instead of %rbp to address the stack frame after the |
| 57 | door syscall is finished because %rbp is cleared by the syscall. */ |
| 58 | movq %rsp, %rbx /* %rbx = %rbp - 8 */ |
| 59 | |
| 60 | /* Push the parameters on the stack. */ |
| 61 | pushq %r8 /* store %r8 at %rbp-16 */ |
| 62 | pushq %rcx /* store %rcx at %rbp-24 */ |
| 63 | pushq %rdx /* store %rdx at %rbp-32 */ |
| 64 | pushq %rsi /* store %rsi at %rbp-40 */ |
| 65 | pushq %rdi /* store %rdi at %rbp-48 */ |
| 66 | .endm |
| 67 | |
| 68 | .macro UNBLOCK_SIGNALS |
| 69 | /* Set the signal mask which should be current during the syscall. */ |
| 70 | /* Set up for sigprocmask(SIG_SETMASK, sysmask, postmask). */ |
| 71 | movq -24(%rbp), %rdx |
| 72 | movq -32(%rbp), %rsi |
| 73 | movq $VKI_SIG_SETMASK, %rdi |
| 74 | movq $__NR_sigprocmask, %rax |
| 75 | syscall |
| 76 | jc sigprocmask_failed /* sigprocmask failed */ |
| 77 | .endm |
| 78 | |
| 79 | .macro REBLOCK_SIGNALS |
| 80 | /* Set up for sigprocmask(SIG_SETMASK, postmask, NULL). */ |
| 81 | movq $0, %rdx |
| 82 | movq -24(%rbp), %rsi |
| 83 | movq $VKI_SIG_SETMASK, %rdi |
| 84 | movq $__NR_sigprocmask, %rax |
| 85 | syscall |
| 86 | /* The syscall above changes the carry flag. This means that if the |
| 87 | syscall fails and we receive an interrupt after it then we've got |
| 88 | an invalid carry flag value in the fixup code. We don't care about |
| 89 | it because this syscall should never fail and if it does then we're |
| 90 | going to stop Valgrind anyway. */ |
| 91 | jc sigprocmask_failed /* sigprocmask failed */ |
| 92 | .endm |
| 93 | |
| 94 | .macro SIMPLE_RETURN |
| 95 | xorq %rax, %rax /* SUCCESS */ |
| 96 | movq -8(%rbp), %rbx /* restore %rbx */ |
| 97 | movq %rbp, %rsp |
| 98 | popq %rbp |
| 99 | ret |
| 100 | .endm |
| 101 | |
| 102 | sigprocmask_failed: |
| 103 | /* Failure: return 0x8000 | error code. */ |
| 104 | andq $0x7FFF, %rax |
| 105 | orq $0x8000, %rax |
| 106 | movq -8(%rbp), %rbx /* restore %rbx */ |
| 107 | movq %rbp, %rsp |
| 108 | popq %rbp |
| 109 | ret |
| 110 | |
| 111 | .globl ML_(do_syscall_for_client_WRK) |
| 112 | ML_(do_syscall_for_client_WRK): |
| 113 | ESTABLISH_STACKFRAME |
| 114 | |
| 115 | 1: /* Even though we can't take a signal until the sigprocmask completes, |
| 116 | start the range early. If %rip is in the range [1, 2), the syscall |
| 117 | hasn't been started yet. */ |
| 118 | UNBLOCK_SIGNALS |
| 119 | |
| 120 | /* Copy syscall parameters. */ |
| 121 | /* do_syscall8 */ |
| 122 | /* 6 register parameters. */ |
| 123 | movq -40(%rbp), %rax |
| 124 | movq OFFSET_amd64_RDI(%rax), %rdi |
| 125 | movq OFFSET_amd64_RSI(%rax), %rsi |
| 126 | movq OFFSET_amd64_RDX(%rax), %rdx |
| 127 | movq OFFSET_amd64_R10(%rax), %r10 |
| 128 | movq OFFSET_amd64_R8(%rax), %r8 |
| 129 | movq OFFSET_amd64_R9(%rax), %r9 |
| 130 | /* 2 stack parameters. */ |
| 131 | movq OFFSET_amd64_RSP(%rax), %rax |
| 132 | movq 16(%rax), %r11 |
| 133 | pushq %r11 |
| 134 | movq 8(%rax), %r11 |
| 135 | pushq %r11 |
| 136 | /* Return address. */ |
| 137 | movq 0(%rax), %r11 |
| 138 | pushq %r11 |
| 139 | |
| 140 | /* Put syscall number in %rax. */ |
| 141 | movq -48(%rbp), %rax |
| 142 | |
| 143 | /* Do the syscall. Note that the Solaris kernel doesn't directly |
| 144 | restart syscalls! */ |
| 145 | syscall |
| 146 | |
| 147 | 2: /* In the range [2, 3), the syscall result is in %rax and %rdx and C, |
| 148 | but hasn't been committed to the thread state. If we get |
| 149 | interrupted in this section then we'll just use values saved in the |
| 150 | ucontext structure. |
| 151 | |
| 152 | Important note for this and the following section: Don't add here |
| 153 | any code that alters the carry flag or worse, call any function. |
| 154 | That would completely break the fixup after an interrupt. */ |
| 155 | movq -40(%rbp), %rcx |
| 156 | movq %rax, OFFSET_amd64_RAX(%rcx) /* save %rax to VEX */ |
| 157 | movq %rdx, OFFSET_amd64_RDX(%rcx) /* save %rdx to VEX */ |
| 158 | movq -16(%rbp), %rcx |
| 159 | setc 0(%rcx) /* save returned carry flag */ |
| 160 | |
| 161 | 3: /* Re-block signals. If %rip is in [3, 4), then the syscall is |
| 162 | complete and we do not need to worry about it. We have to only |
| 163 | correctly save the carry flag. If we get interrupted in this |
| 164 | section then we just have to propagate the carry flag from the |
| 165 | ucontext structure to the thread state, %rax and %rdx values are |
| 166 | already saved. */ |
| 167 | REBLOCK_SIGNALS |
| 168 | |
| 169 | 4: /* Now safe from signals. */ |
| 170 | SIMPLE_RETURN |
| 171 | |
| 172 | .section .rodata |
| 173 | /* Export the ranges so that |
| 174 | VG_(fixup_guest_state_after_syscall_interrupted) can do the right thing. */ |
| 175 | |
| 176 | .globl ML_(blksys_setup) |
| 177 | .globl ML_(blksys_complete) |
| 178 | .globl ML_(blksys_committed) |
| 179 | .globl ML_(blksys_finished) |
| 180 | ML_(blksys_setup): .quad 1b |
| 181 | ML_(blksys_complete): .quad 2b |
| 182 | ML_(blksys_committed): .quad 3b |
| 183 | ML_(blksys_finished): .quad 4b |
| 184 | .previous |
| 185 | |
| 186 | /* Prototype: |
| 187 | Int ML_(do_syscall_for_client_dret_WRK)( |
| 188 | Int syscallno, // %rdi = %rbp-48 = %rbx-48+8 |
| 189 | void *guest_state, // %rsi = %rbp-40 = %rbx-40+8 |
| 190 | const vki_sigset_t *sysmask, // %rdx = %rbp-32 = %rbx-32+8 |
| 191 | const vki_sigset_t *postmask, // %rcx = %rbp-24 = %rbx-24+8 |
| 192 | UChar *cflag) // %r8 = %rbp-16 = %rbx-16+8 |
| 193 | */ |
| 194 | |
| 195 | /* Door_return is a very special call because the data are stored by the |
| 196 | kernel directly on the stack and the stack pointer is appropriately |
| 197 | modified by the kernel. Therefore we switch to the client stack before |
| 198 | doing the syscall, this is relatively trivial but an extra care has to be |
| 199 | taken when we get interrupted at some point. */ |
| 200 | |
| 201 | .globl ML_(do_syscall_for_client_dret_WRK) |
| 202 | ML_(do_syscall_for_client_dret_WRK): |
| 203 | ESTABLISH_STACKFRAME |
| 204 | |
| 205 | 1: /* Even though we can't take a signal until the sigprocmask completes, |
| 206 | start the range early. If %rip is in the range [1, 2), the syscall |
| 207 | hasn't been started yet. */ |
| 208 | UNBLOCK_SIGNALS |
| 209 | |
| 210 | /* Prepare 6 register parameters. */ |
| 211 | movq -40(%rbp), %rax |
| 212 | movq OFFSET_amd64_RDI(%rax), %rdi |
| 213 | movq OFFSET_amd64_RSI(%rax), %rsi |
| 214 | movq OFFSET_amd64_RDX(%rax), %rdx |
| 215 | movq OFFSET_amd64_R10(%rax), %r10 |
| 216 | movq OFFSET_amd64_R8(%rax), %r8 |
| 217 | movq OFFSET_amd64_R9(%rax), %r9 |
| 218 | |
| 219 | /* Switch to the client stack. */ |
| 220 | movq OFFSET_amd64_RSP(%rax), %rsp /* %rsp = simulated RSP */ |
| 221 | /* Change %rbp to a client value. It will always get committed by |
| 222 | the fixup code for range [2, 3) so it needs to be set to what the |
| 223 | client expects. */ |
| 224 | movq OFFSET_amd64_RBP(%rax), %rbp /* %rbp = simulated RBP */ |
| 225 | |
| 226 | /* Put syscall number in %rax. */ |
| 227 | movq -48+8(%rbx), %rax |
| 228 | |
| 229 | /* Do the syscall. Note that the Solaris kernel doesn't directly |
| 230 | restart syscalls! */ |
| 231 | syscall |
| 232 | |
| 233 | 2: /* In the range [2, 3), the syscall result is in %rax, %rdx, %rsp and |
| 234 | %rbp and C, but hasn't been committed to the thread state. If we |
| 235 | get interrupted in this section then we'll just use values saved in |
| 236 | the ucontext structure. |
| 237 | |
| 238 | Important note for this and the following section: Don't add here |
| 239 | any code that alters the carry flag or worse, call any function. |
| 240 | That would completely break the fixup after an interrupt. */ |
| 241 | movq -40+8(%rbx), %rcx |
| 242 | movq %rax, OFFSET_amd64_RAX(%rcx) /* save %rax to VEX */ |
| 243 | movq %rdx, OFFSET_amd64_RDX(%rcx) /* save %rdx to VEX */ |
| 244 | movq %rsp, OFFSET_amd64_RSP(%rcx) /* save %rsp to VEX */ |
| 245 | movq %rbp, OFFSET_amd64_RBP(%rcx) /* save %rbp to VEX */ |
| 246 | movq -16+8(%rbx), %rcx |
| 247 | setc 0(%rcx) /* save returned carry flag */ |
| 248 | |
| 249 | movq %rbx, %rsp /* switch to V stack */ |
| 250 | |
| 251 | 3: /* Re-block signals. If %rip is in [3, 4), then the syscall is |
| 252 | complete and we do not need worry about it. We have to only |
| 253 | correctly save the carry flag. If we get interrupted in this |
| 254 | section then we just have to propagate the carry flag from the |
| 255 | ucontext structure to the thread state, %rax, %rdx, %rsp and %rbp |
| 256 | values are already saved. */ |
| 257 | movq %rbx, %rbp |
| 258 | addq $8, %rbp |
| 259 | REBLOCK_SIGNALS |
| 260 | |
| 261 | 4: /* Now safe from signals. */ |
| 262 | SIMPLE_RETURN |
| 263 | |
| 264 | .section .rodata |
| 265 | .globl ML_(blksys_setup_DRET) |
| 266 | .globl ML_(blksys_complete_DRET) |
| 267 | .globl ML_(blksys_committed_DRET) |
| 268 | .globl ML_(blksys_finished_DRET) |
| 269 | ML_(blksys_setup_DRET): .quad 1b |
| 270 | ML_(blksys_complete_DRET): .quad 2b |
| 271 | ML_(blksys_committed_DRET): .quad 3b |
| 272 | ML_(blksys_finished_DRET): .quad 4b |
| 273 | .previous |
| 274 | |
| 275 | #endif // defined(VGP_amd64_solaris) |
| 276 | |
florian | 3f1d613 | 2015-09-30 20:30:48 +0000 | [diff] [blame] | 277 | /* Let the linker know we don't need an executable stack */ |
| 278 | MARK_STACK_NO_EXEC |
| 279 | |
sewardj | 8eb8bab | 2015-07-21 14:44:28 +0000 | [diff] [blame] | 280 | /*--------------------------------------------------------------------*/ |
| 281 | /*--- end ---*/ |
| 282 | /*--------------------------------------------------------------------*/ |