Changeset 219885 in webkit
- Timestamp:
- Jul 25, 2017 1:56:04 PM (7 years ago)
- Location:
- trunk/Source/JavaScriptCore
- Files:
-
- 5 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/Source/JavaScriptCore/ChangeLog
r219874 r219885 1 2017-07-25 Mark Lam <mark.lam@apple.com> 2 3 Fix bugs in probe code to change sp on x86, x86_64 and 32-bit ARM. 4 https://bugs.webkit.org/show_bug.cgi?id=174809 5 <rdar://problem/33504759> 6 7 Reviewed by Filip Pizlo. 8 9 1. When the probe handler function changes the sp register to point to the 10 region of stack in the middle of the ProbeContext on the stack, there is a 11 bug where the ProbeContext's register values to be restored can be over-written 12 before they can be restored. This is now fixed. 13 14 2. Added more robust probe tests for changing the sp register. 15 16 3. Made existing probe tests to ensure that probe handlers were actually called. 17 18 4. Added some verification to testProbePreservesGPRS(). 19 20 5. Change all the probe tests to fail early on discovering an error instead of 21 batching till the end of the test. This helps point a finger to the failing 22 issue earlier. 23 24 This patch was tested on x86, x86_64, and ARMv7. ARM64 probe code will be fixed 25 next in https://bugs.webkit.org/show_bug.cgi?id=174697. 26 27 * assembler/MacroAssemblerARM.cpp: 28 * assembler/MacroAssemblerARMv7.cpp: 29 * assembler/MacroAssemblerX86Common.cpp: 30 * assembler/testmasm.cpp: 31 (JSC::testProbeReadsArgumentRegisters): 32 (JSC::testProbeWritesArgumentRegisters): 33 (JSC::testProbePreservesGPRS): 34 (JSC::testProbeModifiesStackPointer): 35 (JSC::testProbeModifiesStackPointerToInsideProbeContextOnStack): 36 (JSC::testProbeModifiesStackPointerToNBytesBelowSP): 37 (JSC::testProbeModifiesProgramCounter): 38 (JSC::run): 39 1 40 2017-07-25 Brian Burg <bburg@apple.com> 2 41 -
trunk/Source/JavaScriptCore/assembler/MacroAssemblerARM.cpp
r219740 r219885 216 216 // MacroAssemblerARM::probe() has already generated code to store some values. 217 217 // The top of stack now looks like this: 218 // esp[0 * ptrSize]: probe Function219 // esp[1 * ptrSize]: arg218 // esp[0 * ptrSize]: probe handler function 219 // esp[1 * ptrSize]: probe arg 220 220 // esp[2 * ptrSize]: saved r3 / S0 221 221 // esp[3 * ptrSize]: saved ip … … 289 289 // 290 290 // 2. Issue 1 means we will need to write to the stack location at 291 // ProbeContext.cpu.sp - 4. But if the user probe function had modified 292 // the value of ProbeContext.cpu.sp to point in the range between 293 // &ProbeContext.cpu.ip thru &ProbeContext.cpu.aspr, then the action for 294 // Issue 1 may trash the values to be restored before we can restore 295 // them. 291 // ProbeContext.cpu.gprs[sp] - PTR_SIZE. But if the user probe function had 292 // modified the value of ProbeContext.cpu.gprs[sp] to point in the range between 293 // &ProbeContext.cpu.gprs[ip] thru &ProbeContext.cpu.sprs[aspr], then the action 294 // for Issue 1 may trash the values to be restored before we can restore them. 296 295 // 297 // The solution is to check if ProbeContext.cpu. spcontains a value in296 // The solution is to check if ProbeContext.cpu.gprs[sp] contains a value in 298 297 // the undesirable range. If so, we copy the remaining ProbeContext 299 // register data to a safe range (at memory lower than where 300 // ProbeContext.cpu.sp points) first, and restore the remaining register 301 // from this new range. 302 303 "add ip, sp, #" STRINGIZE_VALUE_OF(PROBE_CPU_APSR_OFFSET) "\n" 298 // register data to a safe area first, and restore the remaining register 299 // from this new safe area. 300 301 // The restore area for the pc will be located at 1 word below the resultant sp. 302 // All restore values are located at offset <= PROBE_CPU_APSR_OFFSET. Hence, 303 // we need to make sure that resultant sp > offset of apsr + 1. 304 "add ip, sp, #" STRINGIZE_VALUE_OF(PROBE_CPU_APSR_OFFSET + PTR_SIZE) "\n" 304 305 "ldr lr, [sp, #" STRINGIZE_VALUE_OF(PROBE_CPU_SP_OFFSET) "]" "\n" 305 306 "cmp lr, ip" "\n" 306 307 "bgt " SYMBOL_STRING(ctiMasmProbeTrampolineEnd) "\n" 307 308 308 // We get here because the new expected stack pointer location is lower 309 // than where it's supposed to be. This means the safe range of stack 310 // memory where we'll be copying the remaining register restore values to 311 // might be in a region of memory below the sp i.e. unallocated stack 312 // memory. This in turn makes it vulnerable to interrupts potentially 313 // trashing the copied values. To prevent that, we must first allocate the 314 // needed stack memory by adjusting the sp before the copying. 315 316 "sub lr, lr, #(6 * " STRINGIZE_VALUE_OF(PTR_SIZE) 317 " + " STRINGIZE_VALUE_OF(PROBE_CPU_IP_OFFSET) ")" "\n" 318 319 "mov ip, sp" "\n" 320 "mov sp, lr" "\n" 321 "mov lr, ip" "\n" 322 309 // Getting here means that the restore area will overlap the ProbeContext data 310 // that we will need to get the restoration values from. So, let's move that 311 // data to a safe place before we start writing into the restore area. 312 // Let's locate the "safe area" at 2x sizeof(ProbeContext) below where the 313 // restore area. This ensures that: 314 // 1. The safe area does not overlap the restore area. 315 // 2. The safe area does not overlap the ProbeContext. 316 // This makes it so that we can use memcpy (does not require memmove) semantics 317 // to copy the restore values to the safe area. 318 319 // lr already contains [sp, #STRINGIZE_VALUE_OF(PROBE_CPU_SP_OFFSET)]. 320 "sub lr, lr, #(2 * " STRINGIZE_VALUE_OF(PROBE_ALIGNED_SIZE) ")" "\n" 321 322 "mov ip, sp" "\n" // Save the original ProbeContext*. 323 324 // Make sure the stack pointer points to the safe area. This ensures that the 325 // safe area is protected from interrupt handlers overwriting it. 326 "mov sp, lr" "\n" // sp now points to the new ProbeContext in the safe area. 327 328 "mov lr, ip" "\n" // Use lr as the old ProbeContext*. 329 330 // Copy the restore data to the new ProbeContext*. 323 331 "ldr ip, [lr, #" STRINGIZE_VALUE_OF(PROBE_CPU_IP_OFFSET) "]" "\n" 324 332 "str ip, [sp, #" STRINGIZE_VALUE_OF(PROBE_CPU_IP_OFFSET) "]" "\n" … … 332 340 "str ip, [sp, #" STRINGIZE_VALUE_OF(PROBE_CPU_APSR_OFFSET) "]" "\n" 333 341 342 // ctiMasmProbeTrampolineEnd expects lr to contain the sp value to be restored. 343 // Since we used it as scratch above, let's restore it. 344 "ldr lr, [sp, #" STRINGIZE_VALUE_OF(PROBE_CPU_SP_OFFSET) "]" "\n" 345 334 346 SYMBOL_STRING(ctiMasmProbeTrampolineEnd) ":" "\n" 347 348 // Set up the restore area for sp and pc. 349 // lr already contains [sp, #STRINGIZE_VALUE_OF(PROBE_CPU_SP_OFFSET)]. 350 351 // Push the pc on to the restore area. 335 352 "ldr ip, [sp, #" STRINGIZE_VALUE_OF(PROBE_CPU_PC_OFFSET) "]" "\n" 336 "ldr lr, [sp, #" STRINGIZE_VALUE_OF(PROBE_CPU_SP_OFFSET) "]" "\n"337 353 "sub lr, lr, #" STRINGIZE_VALUE_OF(PTR_SIZE) "\n" 338 354 "str ip, [lr]" "\n" 355 // Point sp to the restore area. 339 356 "str lr, [sp, #" STRINGIZE_VALUE_OF(PROBE_CPU_SP_OFFSET) "]" "\n" 340 357 358 // All done with math i.e. won't trash the status register (apsr) and don't need 359 // scratch registers (lr and ip) anymore. Restore apsr, lr, and ip. 341 360 "ldr ip, [sp, #" STRINGIZE_VALUE_OF(PROBE_CPU_APSR_OFFSET) "]" "\n" 342 361 "msr APSR, ip" "\n" 343 "ldr ip, [sp, #" STRINGIZE_VALUE_OF(PROBE_CPU_LR_OFFSET) "]" "\n" 344 "mov lr, ip" "\n" 362 "ldr lr, [sp, #" STRINGIZE_VALUE_OF(PROBE_CPU_LR_OFFSET) "]" "\n" 345 363 "ldr ip, [sp, #" STRINGIZE_VALUE_OF(PROBE_CPU_IP_OFFSET) "]" "\n" 364 365 // Restore the sp and pc. 346 366 "ldr sp, [sp, #" STRINGIZE_VALUE_OF(PROBE_CPU_SP_OFFSET) "]" "\n" 347 348 367 "pop { pc }" "\n" 349 368 ); -
trunk/Source/JavaScriptCore/assembler/MacroAssemblerARMv7.cpp
r219740 r219885 187 187 // MacroAssemblerARMv7::probe() has already generated code to store some values. 188 188 // The top of stack now looks like this: 189 // esp[0 * ptrSize]: probe Function190 // esp[1 * ptrSize]: arg189 // esp[0 * ptrSize]: probe handler function 190 // esp[1 * ptrSize]: probe arg 191 191 // esp[2 * ptrSize]: saved r0 192 192 // esp[3 * ptrSize]: saved ip … … 255 255 // 1. Normal ARM calling convention relies on moving lr to pc to return to 256 256 // the caller. In our case, the address to return to is specified by 257 // ProbeContext.cpu. pc. And at that moment, we won't have any available257 // ProbeContext.cpu.gprs[pc]. And at that moment, we won't have any available 258 258 // scratch registers to hold the return address (lr needs to hold 259 // ProbeContext.cpu. lr, not the return address).259 // ProbeContext.cpu.gprs[lr], not the return address). 260 260 // 261 261 // The solution is to store the return address on the stack and load the … … 263 263 // 264 264 // 2. Issue 1 means we will need to write to the stack location at 265 // ProbeContext.cpu.sp - 4. But if the user probe function had modified 266 // the value of ProbeContext.cpu.sp to point in the range between 267 // &ProbeContext.cpu.ip thru &ProbeContext.cpu.aspr, then the action for 268 // Issue 1 may trash the values to be restored before we can restore 269 // them. 265 // ProbeContext.cpu.gprs[sp] - PTR_SIZE. But if the user probe function had 266 // modified the value of ProbeContext.cpu.gprs[sp] to point in the range between 267 // &ProbeContext.cpu.gprs[ip] thru &ProbeContext.cpu.sprs[aspr], then the action 268 // for Issue 1 may trash the values to be restored before we can restore them. 270 269 // 271 // The solution is to check if ProbeContext.cpu. spcontains a value in270 // The solution is to check if ProbeContext.cpu.gprs[sp] contains a value in 272 271 // the undesirable range. If so, we copy the remaining ProbeContext 273 // register data to a safe range (at memory lower than where 274 // ProbeContext.cpu.sp points) first, and restore the remaining register 275 // from this new range. 276 277 "add ip, sp, #" STRINGIZE_VALUE_OF(PROBE_CPU_APSR_OFFSET) "\n" 272 // register data to a safe area first, and restore the remaining register 273 // from this new safe area. 274 275 // The restore area for the pc will be located at 1 word below the resultant sp. 276 // All restore values are located at offset <= PROBE_CPU_APSR_OFFSET. Hence, 277 // we need to make sure that resultant sp > offset of apsr + 1. 278 "add ip, sp, #" STRINGIZE_VALUE_OF(PROBE_CPU_APSR_OFFSET + PTR_SIZE) "\n" 278 279 "ldr lr, [sp, #" STRINGIZE_VALUE_OF(PROBE_CPU_SP_OFFSET) "]" "\n" 279 280 "cmp lr, ip" "\n" … … 281 282 "bgt " SYMBOL_STRING(ctiMasmProbeTrampolineEnd) "\n" 282 283 283 // We get here because the new expected stack pointer location is lower 284 // than where it's supposed to be. This means the safe range of stack 285 // memory where we'll be copying the remaining register restore values to 286 // might be in a region of memory below the sp i.e. unallocated stack 287 // memory. This, in turn, makes it vulnerable to interrupts potentially 288 // trashing the copied values. To prevent that, we must first allocate the 289 // needed stack memory by adjusting the sp before the copying. 290 291 "sub lr, lr, #(6 * " STRINGIZE_VALUE_OF(PTR_SIZE) 292 " + " STRINGIZE_VALUE_OF(PROBE_CPU_IP_OFFSET) ")" "\n" 293 294 "mov ip, sp" "\n" 295 "mov sp, lr" "\n" 296 "mov lr, ip" "\n" 297 284 // Getting here means that the restore area will overlap the ProbeContext data 285 // that we will need to get the restoration values from. So, let's move that 286 // data to a safe place before we start writing into the restore area. 287 // Let's locate the "safe area" at 2x sizeof(ProbeContext) below where the 288 // restore area. This ensures that: 289 // 1. The safe area does not overlap the restore area. 290 // 2. The safe area does not overlap the ProbeContext. 291 // This makes it so that we can use memcpy (does not require memmove) semantics 292 // to copy the restore values to the safe area. 293 294 // lr already contains [sp, #STRINGIZE_VALUE_OF(PROBE_CPU_SP_OFFSET)]. 295 "sub lr, lr, #(2 * " STRINGIZE_VALUE_OF(PROBE_ALIGNED_SIZE) ")" "\n" 296 297 "mov ip, sp" "\n" // Save the original ProbeContext*. 298 299 // Make sure the stack pointer points to the safe area. This ensures that the 300 // safe area is protected from interrupt handlers overwriting it. 301 "mov sp, lr" "\n" // sp now points to the new ProbeContext in the safe area. 302 303 "mov lr, ip" "\n" // Use lr as the old ProbeContext*. 304 305 // Copy the restore data to the new ProbeContext*. 298 306 "ldr ip, [lr, #" STRINGIZE_VALUE_OF(PROBE_CPU_IP_OFFSET) "]" "\n" 299 307 "str ip, [sp, #" STRINGIZE_VALUE_OF(PROBE_CPU_IP_OFFSET) "]" "\n" … … 307 315 "str ip, [sp, #" STRINGIZE_VALUE_OF(PROBE_CPU_APSR_OFFSET) "]" "\n" 308 316 317 // ctiMasmProbeTrampolineEnd expects lr to contain the sp value to be restored. 318 // Since we used it as scratch above, let's restore it. 319 "ldr lr, [sp, #" STRINGIZE_VALUE_OF(PROBE_CPU_SP_OFFSET) "]" "\n" 320 309 321 ".thumb_func " THUMB_FUNC_PARAM(ctiMasmProbeTrampolineEnd) "\n" 310 322 SYMBOL_STRING(ctiMasmProbeTrampolineEnd) ":" "\n" 323 324 // Set up the restore area for sp and pc. 325 // lr already contains [sp, #STRINGIZE_VALUE_OF(PROBE_CPU_SP_OFFSET)]. 326 327 // Push the pc on to the restore area. 311 328 "ldr ip, [sp, #" STRINGIZE_VALUE_OF(PROBE_CPU_PC_OFFSET) "]" "\n" 312 "ldr lr, [sp, #" STRINGIZE_VALUE_OF(PROBE_CPU_SP_OFFSET) "]" "\n"313 329 "sub lr, lr, #" STRINGIZE_VALUE_OF(PTR_SIZE) "\n" 314 330 "str ip, [lr]" "\n" 331 // Point sp to the restore area. 315 332 "str lr, [sp, #" STRINGIZE_VALUE_OF(PROBE_CPU_SP_OFFSET) "]" "\n" 316 333 334 // All done with math i.e. won't trash the status register (apsr) and don't need 335 // scratch registers (lr and ip) anymore. Restore apsr, lr, and ip. 317 336 "ldr ip, [sp, #" STRINGIZE_VALUE_OF(PROBE_CPU_APSR_OFFSET) "]" "\n" 318 337 "msr APSR, ip" "\n" 319 "ldr ip, [sp, #" STRINGIZE_VALUE_OF(PROBE_CPU_LR_OFFSET) "]" "\n" 320 "mov lr, ip" "\n" 338 "ldr lr, [sp, #" STRINGIZE_VALUE_OF(PROBE_CPU_LR_OFFSET) "]" "\n" 321 339 "ldr ip, [sp, #" STRINGIZE_VALUE_OF(PROBE_CPU_IP_OFFSET) "]" "\n" 340 341 // Restore the sp and pc. 322 342 "ldr sp, [sp, #" STRINGIZE_VALUE_OF(PROBE_CPU_SP_OFFSET) "]" "\n" 323 324 343 "pop { pc }" "\n" 325 344 ); -
trunk/Source/JavaScriptCore/assembler/MacroAssemblerX86Common.cpp
r219740 r219885 172 172 // esp[0 * ptrSize]: eflags 173 173 // esp[1 * ptrSize]: return address / saved eip 174 // esp[2 * ptrSize]: probe Function175 // esp[3 * ptrSize]: arg1174 // esp[2 * ptrSize]: probe handler function 175 // esp[3 * ptrSize]: probe arg 176 176 // esp[4 * ptrSize]: saved eax 177 177 // esp[5 * ptrSize]: saved esp … … 241 241 // There are 6 more registers left to restore: 242 242 // eax, ecx, ebp, esp, eip, and eflags. 243 // We need to handle these last few restores carefully because: 244 // 245 // 1. We need to push the return address on the stack for ret to use. 246 // That means we need to write to the stack. 247 // 2. The user probe function may have altered the restore value of esp to 248 // point to the vicinity of one of the restore values for the remaining 249 // registers left to be restored. 250 // That means, for requirement 1, we may end up writing over some of the 251 // restore values. We can check for this, and first copy the restore 252 // values to a "safe area" on the stack before commencing with the action 253 // for requirement 1. 254 // 3. For requirement 2, we need to ensure that the "safe area" is 255 // protected from interrupt handlers overwriting it. Hence, the esp needs 256 // to be adjusted to include the "safe area" before we start copying the 257 // the restore values. 258 243 244 // The restoration process at ctiMasmProbeTrampolineEnd below works by popping 245 // 5 words off the stack into eflags, eax, ecx, ebp, and eip. These 5 words need 246 // to be pushed on top of the final esp value so that just by popping the 5 words, 247 // we'll get the esp that the probe wants to set. Let's call this area (for storing 248 // these 5 words) the restore area. 249 "movl " STRINGIZE_VALUE_OF(PROBE_CPU_ESP_OFFSET) "(%ebp), %ecx" "\n" 250 "subl $5 * " STRINGIZE_VALUE_OF(PTR_SIZE) ", %ecx" "\n" 251 252 // ecx now points to the restore area. 253 254 // Before we copy values from the ProbeContext to the restore area, we need to 255 // make sure that the restore area does not overlap any of the values that we'll 256 // be copying from in the ProbeContext. All the restore values to be copied from 257 // comes from offset <= PROBE_CPU_EFLAGS_OFFSET in the ProbeContext. 259 258 "movl %ebp, %eax" "\n" 260 259 "addl $" STRINGIZE_VALUE_OF(PROBE_CPU_EFLAGS_OFFSET) ", %eax" "\n" 261 "cmpl %eax, " STRINGIZE_VALUE_OF(PROBE_CPU_ESP_OFFSET) "(%ebp)" "\n"260 "cmpl %eax, %ecx" "\n" 262 261 "jg " SYMBOL_STRING(ctiMasmProbeTrampolineEnd) "\n" 263 262 264 // Locate the "safe area" at 2x sizeof(ProbeContext) below where the new 265 // rsp will be. This time we don't have to 32-byte align it because we're 266 // not using this area to store any xmm regs. 267 "movl " STRINGIZE_VALUE_OF(PROBE_CPU_ESP_OFFSET) "(%ebp), %eax" "\n" 263 // Getting here means that the restore area will overlap the ProbeContext data 264 // that we will need to get the restoration values from. So, let's move that 265 // data to a safe place before we start writing into the restore area. 266 // Let's locate the "safe area" at 2x sizeof(ProbeContext) below where the 267 // restore area. This ensures that: 268 // 1. The safe area does not overlap the restore area. 269 // 2. The safe area does not overlap the ProbeContext. 270 // This makes it so that we can use memcpy (does not require memmove) semantics 271 // to copy the restore values to the safe area. 272 // Note: the safe area does not have to 32-byte align it because we're not using 273 // it to store any xmm regs. 274 "movl %ecx, %eax" "\n" 268 275 "subl $2 * " STRINGIZE_VALUE_OF(PROBE_ALIGNED_SIZE) ", %eax" "\n" 276 277 // eax now points to the safe area. 278 279 // Make sure the stack pointer points to the safe area. This ensures that the 280 // safe area is protected from interrupt handlers overwriting it. 269 281 "movl %eax, %esp" "\n" 270 282 271 "subl $" STRINGIZE_VALUE_OF(PROBE_CPU_EAX_OFFSET) ", %eax" "\n"272 283 "movl " STRINGIZE_VALUE_OF(PROBE_CPU_EAX_OFFSET) "(%ebp), %ecx" "\n" 273 284 "movl %ecx, " STRINGIZE_VALUE_OF(PROBE_CPU_EAX_OFFSET) "(%eax)" "\n" … … 284 295 "movl %eax, %ebp" "\n" 285 296 297 // We used ecx above as scratch register. Let's restore it to points to the 298 // restore area. 299 "movl " STRINGIZE_VALUE_OF(PROBE_CPU_ESP_OFFSET) "(%ebp), %ecx" "\n" 300 "subl $5 * " STRINGIZE_VALUE_OF(PTR_SIZE) ", %ecx" "\n" 301 302 // ecx now points to the restore area. 303 286 304 SYMBOL_STRING(ctiMasmProbeTrampolineEnd) ":" "\n" 287 "movl " STRINGIZE_VALUE_OF(PROBE_CPU_ESP_OFFSET) "(%ebp), %eax" "\n" 288 "subl $5 * " STRINGIZE_VALUE_OF(PTR_SIZE) ", %eax" "\n" 289 // At this point, %esp should be < %eax. 290 291 "movl " STRINGIZE_VALUE_OF(PROBE_CPU_EFLAGS_OFFSET) "(%ebp), %ecx" "\n" 292 "movl %ecx, 0 * " STRINGIZE_VALUE_OF(PTR_SIZE) "(%eax)" "\n" 293 "movl " STRINGIZE_VALUE_OF(PROBE_CPU_EAX_OFFSET) "(%ebp), %ecx" "\n" 294 "movl %ecx, 1 * " STRINGIZE_VALUE_OF(PTR_SIZE) "(%eax)" "\n" 295 "movl " STRINGIZE_VALUE_OF(PROBE_CPU_ECX_OFFSET) "(%ebp), %ecx" "\n" 296 "movl %ecx, 2 * " STRINGIZE_VALUE_OF(PTR_SIZE) "(%eax)" "\n" 297 "movl " STRINGIZE_VALUE_OF(PROBE_CPU_EBP_OFFSET) "(%ebp), %ecx" "\n" 298 "movl %ecx, 3 * " STRINGIZE_VALUE_OF(PTR_SIZE) "(%eax)" "\n" 299 "movl " STRINGIZE_VALUE_OF(PROBE_CPU_EIP_OFFSET) "(%ebp), %ecx" "\n" 300 "movl %ecx, 4 * " STRINGIZE_VALUE_OF(PTR_SIZE) "(%eax)" "\n" 301 "movl %eax, %esp" "\n" 302 305 306 // Copy remaining restore values from the ProbeContext to the restore area. 307 "movl " STRINGIZE_VALUE_OF(PROBE_CPU_EFLAGS_OFFSET) "(%ebp), %eax" "\n" 308 "movl %eax, 0 * " STRINGIZE_VALUE_OF(PTR_SIZE) "(%ecx)" "\n" 309 "movl " STRINGIZE_VALUE_OF(PROBE_CPU_EAX_OFFSET) "(%ebp), %eax" "\n" 310 "movl %eax, 1 * " STRINGIZE_VALUE_OF(PTR_SIZE) "(%ecx)" "\n" 311 "movl " STRINGIZE_VALUE_OF(PROBE_CPU_ECX_OFFSET) "(%ebp), %eax" "\n" 312 "movl %eax, 2 * " STRINGIZE_VALUE_OF(PTR_SIZE) "(%ecx)" "\n" 313 "movl " STRINGIZE_VALUE_OF(PROBE_CPU_EBP_OFFSET) "(%ebp), %eax" "\n" 314 "movl %eax, 3 * " STRINGIZE_VALUE_OF(PTR_SIZE) "(%ecx)" "\n" 315 "movl " STRINGIZE_VALUE_OF(PROBE_CPU_EIP_OFFSET) "(%ebp), %eax" "\n" 316 "movl %eax, 4 * " STRINGIZE_VALUE_OF(PTR_SIZE) "(%ecx)" "\n" 317 "movl %ecx, %esp" "\n" 318 319 // Do the remaining restoration by popping off the restore area. 303 320 "popfd" "\n" 304 321 "popl %eax" "\n" … … 322 339 // esp[0 * ptrSize]: rflags 323 340 // esp[1 * ptrSize]: return address / saved rip 324 // esp[2 * ptrSize]: probe Function325 // esp[3 * ptrSize]: arg1341 // esp[2 * ptrSize]: probe handler function 342 // esp[3 * ptrSize]: probe arg 326 343 // esp[4 * ptrSize]: saved rax 327 344 // esp[5 * ptrSize]: saved rsp … … 421 438 // There are 6 more registers left to restore: 422 439 // rax, rcx, rbp, rsp, rip, and rflags. 423 // We need to handle these last few restores carefully because: 424 // 425 // 1. We need to push the return address on the stack for ret to use 426 // That means we need to write to the stack. 427 // 2. The user probe function may have altered the restore value of esp to 428 // point to the vicinity of one of the restore values for the remaining 429 // registers left to be restored. 430 // That means, for requirement 1, we may end up writing over some of the 431 // restore values. We can check for this, and first copy the restore 432 // values to a "safe area" on the stack before commencing with the action 433 // for requirement 1. 434 // 3. For both requirement 2, we need to ensure that the "safe area" is 435 // protected from interrupt handlers overwriting it. Hence, the esp needs 436 // to be adjusted to include the "safe area" before we start copying the 437 // the restore values. 438 440 441 // The restoration process at ctiMasmProbeTrampolineEnd below works by popping 442 // 5 words off the stack into rflags, rax, rcx, rbp, and rip. These 5 words need 443 // to be pushed on top of the final esp value so that just by popping the 5 words, 444 // we'll get the esp that the probe wants to set. Let's call this area (for storing 445 // these 5 words) the restore area. 446 "movq " STRINGIZE_VALUE_OF(PROBE_CPU_ESP_OFFSET) "(%rbp), %rcx" "\n" 447 "subq $5 * " STRINGIZE_VALUE_OF(PTR_SIZE) ", %rcx" "\n" 448 449 // rcx now points to the restore area. 450 451 // Before we copy values from the ProbeContext to the restore area, we need to 452 // make sure that the restore area does not overlap any of the values that we'll 453 // be copying from in the ProbeContext. All the restore values to be copied from 454 // comes from offset <= PROBE_CPU_EFLAGS_OFFSET in the ProbeContext. 439 455 "movq %rbp, %rax" "\n" 440 456 "addq $" STRINGIZE_VALUE_OF(PROBE_CPU_EFLAGS_OFFSET) ", %rax" "\n" 441 "cmpq %rax, " STRINGIZE_VALUE_OF(PROBE_CPU_ESP_OFFSET) "(%rbp)" "\n"457 "cmpq %rax, %rcx" "\n" 442 458 "jg " SYMBOL_STRING(ctiMasmProbeTrampolineEnd) "\n" 443 459 444 // Locate the "safe area" at 2x sizeof(ProbeContext) below where the new 445 // rsp will be. This time we don't have to 32-byte align it because we're 446 // not using to store any xmm regs. 447 "movq " STRINGIZE_VALUE_OF(PROBE_CPU_ESP_OFFSET) "(%rbp), %rax" "\n" 460 // Getting here means that the restore area will overlap the ProbeContext data 461 // that we will need to get the restoration values from. So, let's move that 462 // data to a safe place before we start writing into the restore area. 463 // Let's locate the "safe area" at 2x sizeof(ProbeContext) below where the 464 // restore area. This ensures that: 465 // 1. The safe area does not overlap the restore area. 466 // 2. The safe area does not overlap the ProbeContext. 467 // This makes it so that we can use memcpy (does not require memmove) semantics 468 // to copy the restore values to the safe area. 469 // Note: the safe area does not have to 32-byte align it because we're not using 470 // it to store any xmm regs. 471 "movq %rcx, %rax" "\n" 448 472 "subq $2 * " STRINGIZE_VALUE_OF(PROBE_ALIGNED_SIZE) ", %rax" "\n" 473 474 // rax now points to the safe area. 475 476 // Make sure the stack pointer points to the safe area. This ensures that the 477 // safe area is protected from interrupt handlers overwriting it. 449 478 "movq %rax, %rsp" "\n" 450 479 … … 463 492 "movq %rax, %rbp" "\n" 464 493 494 // We used rcx above as scratch register. Let's restore it to points to the 495 // restore area. 496 "movq " STRINGIZE_VALUE_OF(PROBE_CPU_ESP_OFFSET) "(%rbp), %rcx" "\n" 497 "subq $5 * " STRINGIZE_VALUE_OF(PTR_SIZE) ", %rcx" "\n" 498 499 // rcx now points to the restore area. 500 465 501 SYMBOL_STRING(ctiMasmProbeTrampolineEnd) ":" "\n" 466 "movq " STRINGIZE_VALUE_OF(PROBE_CPU_ESP_OFFSET) "(%rbp), %rax" "\n" 467 "subq $5 * " STRINGIZE_VALUE_OF(PTR_SIZE) ", %rax" "\n" 468 // At this point, %rsp should be < %rax. 469 470 "movq " STRINGIZE_VALUE_OF(PROBE_CPU_EFLAGS_OFFSET) "(%rbp), %rcx" "\n" 471 "movq %rcx, 0 * " STRINGIZE_VALUE_OF(PTR_SIZE) "(%rax)" "\n" 472 "movq " STRINGIZE_VALUE_OF(PROBE_CPU_EAX_OFFSET) "(%rbp), %rcx" "\n" 473 "movq %rcx, 1 * " STRINGIZE_VALUE_OF(PTR_SIZE) "(%rax)" "\n" 474 "movq " STRINGIZE_VALUE_OF(PROBE_CPU_ECX_OFFSET) "(%rbp), %rcx" "\n" 475 "movq %rcx, 2 * " STRINGIZE_VALUE_OF(PTR_SIZE) "(%rax)" "\n" 476 "movq " STRINGIZE_VALUE_OF(PROBE_CPU_EBP_OFFSET) "(%rbp), %rcx" "\n" 477 "movq %rcx, 3 * " STRINGIZE_VALUE_OF(PTR_SIZE) "(%rax)" "\n" 478 "movq " STRINGIZE_VALUE_OF(PROBE_CPU_EIP_OFFSET) "(%rbp), %rcx" "\n" 479 "movq %rcx, 4 * " STRINGIZE_VALUE_OF(PTR_SIZE) "(%rax)" "\n" 480 "movq %rax, %rsp" "\n" 481 502 503 // Copy remaining restore values from the ProbeContext to the restore area. 504 "movq " STRINGIZE_VALUE_OF(PROBE_CPU_EFLAGS_OFFSET) "(%rbp), %rax" "\n" 505 "movq %rax, 0 * " STRINGIZE_VALUE_OF(PTR_SIZE) "(%rcx)" "\n" 506 "movq " STRINGIZE_VALUE_OF(PROBE_CPU_EAX_OFFSET) "(%rbp), %rax" "\n" 507 "movq %rax, 1 * " STRINGIZE_VALUE_OF(PTR_SIZE) "(%rcx)" "\n" 508 "movq " STRINGIZE_VALUE_OF(PROBE_CPU_ECX_OFFSET) "(%rbp), %rax" "\n" 509 "movq %rax, 2 * " STRINGIZE_VALUE_OF(PTR_SIZE) "(%rcx)" "\n" 510 "movq " STRINGIZE_VALUE_OF(PROBE_CPU_EBP_OFFSET) "(%rbp), %rax" "\n" 511 "movq %rax, 3 * " STRINGIZE_VALUE_OF(PTR_SIZE) "(%rcx)" "\n" 512 "movq " STRINGIZE_VALUE_OF(PROBE_CPU_EIP_OFFSET) "(%rbp), %rax" "\n" 513 "movq %rax, 4 * " STRINGIZE_VALUE_OF(PTR_SIZE) "(%rcx)" "\n" 514 "movq %rcx, %rsp" "\n" 515 516 // Do the remaining restoration by popping off the restore area. 482 517 "popfq" "\n" 483 518 "popq %rax" "\n" -
trunk/Source/JavaScriptCore/assembler/testmasm.cpp
r219821 r219885 34 34 #include <wtf/Compiler.h> 35 35 #include <wtf/DataLog.h> 36 #include <wtf/Function.h> 36 37 #include <wtf/Lock.h> 37 38 #include <wtf/NumberOfCores.h> … … 56 57 StaticLock crashLock; 57 58 58 typedef std::function<void(CCallHelpers&)> Generator;59 typedef WTF::Function<void(CCallHelpers&)> Generator; 59 60 60 61 template<typename T> T nextID(T id) { return static_cast<T>(id + 1); } … … 103 104 #endif // ENABLE(MASM_PROBE) 104 105 105 MacroAssemblerCodeRef compile(Generator generate)106 MacroAssemblerCodeRef compile(Generator&& generate) 106 107 { 107 108 CCallHelpers jit; … … 119 120 120 121 template<typename T, typename... Arguments> 121 T compileAndRun(Generator generator, Arguments... arguments)122 { 123 return invoke<T>(compile( generator), arguments...);122 T compileAndRun(Generator&& generator, Arguments... arguments) 123 { 124 return invoke<T>(compile(WTFMove(generator)), arguments...); 124 125 } 125 126 … … 137 138 void testProbeReadsArgumentRegisters() 138 139 { 139 bool success = true;140 bool probeWasCalled = false; 140 141 compileAndRun<void>([&] (CCallHelpers& jit) { 141 142 jit.emitFunctionPrologue(); … … 158 159 159 160 jit.probe([&] (ProbeContext* context) { 160 success = success && context->gpr(GPRInfo::argumentGPR0) == testWord(0); 161 success = success && context->gpr(GPRInfo::argumentGPR1) == testWord(1); 162 success = success && context->gpr(GPRInfo::argumentGPR2) == testWord(2); 163 success = success && context->gpr(GPRInfo::argumentGPR3) == testWord(3); 164 165 success = success && context->fpr(FPRInfo::fpRegT0) == testWord32(0); 166 success = success && context->fpr(FPRInfo::fpRegT1) == testWord32(1); 161 probeWasCalled = true; 162 CHECK(context->gpr(GPRInfo::argumentGPR0) == testWord(0)); 163 CHECK(context->gpr(GPRInfo::argumentGPR1) == testWord(1)); 164 CHECK(context->gpr(GPRInfo::argumentGPR2) == testWord(2)); 165 CHECK(context->gpr(GPRInfo::argumentGPR3) == testWord(3)); 166 167 CHECK(context->fpr(FPRInfo::fpRegT0) == testWord32(0)); 168 CHECK(context->fpr(FPRInfo::fpRegT1) == testWord32(1)); 167 169 }); 168 170 jit.emitFunctionEpilogue(); 169 171 jit.ret(); 170 172 }); 171 CHECK( success);173 CHECK(probeWasCalled); 172 174 } 173 175 … … 177 179 // that we can read from argument registers. We'll use that ability to validate 178 180 // that our writes did take effect. 179 bool success = true;181 unsigned probeCallCount = 0; 180 182 compileAndRun<void>([&] (CCallHelpers& jit) { 181 183 jit.emitFunctionPrologue(); … … 197 199 198 200 // Write expected values. 199 jit.probe([] (ProbeContext* context) { 201 jit.probe([&] (ProbeContext* context) { 202 probeCallCount++; 200 203 context->gpr(GPRInfo::argumentGPR0) = testWord(0); 201 204 context->gpr(GPRInfo::argumentGPR1) = testWord(1); … … 209 212 // Validate that expected values were written. 210 213 jit.probe([&] (ProbeContext* context) { 211 success = success && context->gpr(GPRInfo::argumentGPR0) == testWord(0); 212 success = success && context->gpr(GPRInfo::argumentGPR1) == testWord(1); 213 success = success && context->gpr(GPRInfo::argumentGPR2) == testWord(2); 214 success = success && context->gpr(GPRInfo::argumentGPR3) == testWord(3); 215 216 success = success && context->fpr(FPRInfo::fpRegT0) == testWord32(0); 217 success = success && context->fpr(FPRInfo::fpRegT1) == testWord32(1); 214 probeCallCount++; 215 CHECK(context->gpr(GPRInfo::argumentGPR0) == testWord(0)); 216 CHECK(context->gpr(GPRInfo::argumentGPR1) == testWord(1)); 217 CHECK(context->gpr(GPRInfo::argumentGPR2) == testWord(2)); 218 CHECK(context->gpr(GPRInfo::argumentGPR3) == testWord(3)); 219 220 CHECK(context->fpr(FPRInfo::fpRegT0) == testWord32(0)); 221 CHECK(context->fpr(FPRInfo::fpRegT1) == testWord32(1)); 218 222 }); 219 223 … … 221 225 jit.ret(); 222 226 }); 223 CHECK( success);227 CHECK(probeCallCount == 2); 224 228 } 225 229 … … 242 246 // having already validated that we can read and write from registers. We'll use these abilities 243 247 // to validate that the probe preserves register values. 244 bool success = true;248 unsigned probeCallCount = 0; 245 249 MacroAssembler::CPUState originalState; 246 250 … … 250 254 // Write expected values into the registers (except for sp, fp, and pc). 251 255 jit.probe([&] (ProbeContext* context) { 256 probeCallCount++; 252 257 for (auto id = CCallHelpers::firstRegister(); id <= CCallHelpers::lastRegister(); id = nextID(id)) { 253 258 originalState.gpr(id) = context->gpr(id); … … 264 269 // Invoke the probe to call a lot of functions and trash register values. 265 270 jit.probe([&] (ProbeContext*) { 266 success = success && (testFunctionToTrashGPRs(0, 1, 2, 3, 4, 5, 6, 7, 8, 9) == 10); 267 success = success && (testFunctionToTrashFPRs(0, 1, 2, 3, 4, 5, 6, 7, 8, 9) == 10); 271 probeCallCount++; 272 CHECK(testFunctionToTrashGPRs(0, 1, 2, 3, 4, 5, 6, 7, 8, 9) == 10); 273 CHECK(testFunctionToTrashFPRs(0, 1, 2, 3, 4, 5, 6, 7, 8, 9) == 10); 268 274 }); 269 275 270 276 // Validate that the registers have the expected values. 271 277 jit.probe([&] (ProbeContext* context) { 278 probeCallCount++; 272 279 for (auto id = CCallHelpers::firstRegister(); id <= CCallHelpers::lastRegister(); id = nextID(id)) { 273 280 if (isPC(id)) 274 281 continue; 275 282 if (isSP(id) || isFP(id)) { 276 success = success && context->gpr(id) == originalState.gpr(id);283 CHECK(context->gpr(id) == originalState.gpr(id)); 277 284 continue; 278 285 } 279 success = success && context->gpr(id) == testWord(id);286 CHECK(context->gpr(id) == testWord(id)); 280 287 } 281 288 for (auto id = CCallHelpers::firstFPRegister(); id <= CCallHelpers::lastFPRegister(); id = nextID(id)) 282 success = success && context->fpr(id) == testWord(id);289 CHECK(context->fpr(id) == testWord(id)); 283 290 }); 284 291 285 292 // Restore the original state. 286 293 jit.probe([&] (ProbeContext* context) { 294 probeCallCount++; 287 295 for (auto id = CCallHelpers::firstRegister(); id <= CCallHelpers::lastRegister(); id = nextID(id)) { 288 296 if (isPC(id) || isSP(id) || isFP(id)) … … 292 300 for (auto id = CCallHelpers::firstFPRegister(); id <= CCallHelpers::lastFPRegister(); id = nextID(id)) 293 301 context->fpr(id) = originalState.fpr(id); 302 }); 303 304 // Validate that the original state was restored. 305 jit.probe([&] (ProbeContext* context) { 306 probeCallCount++; 307 for (auto id = CCallHelpers::firstRegister(); id <= CCallHelpers::lastRegister(); id = nextID(id)) { 308 if (isPC(id) || isSP(id) || isFP(id)) 309 continue; 310 CHECK(context->gpr(id) == originalState.gpr(id)); 311 } 312 for (auto id = CCallHelpers::firstFPRegister(); id <= CCallHelpers::lastFPRegister(); id = nextID(id)) 313 CHECK(context->fpr(id) == originalState.fpr(id)); 294 314 }); 295 315 … … 297 317 jit.ret(); 298 318 }); 299 CHECK(success); 300 } 301 302 void testProbeModifiesStackPointer() 303 { 304 bool success = true; 305 uint8_t* originalSP; 319 CHECK(probeCallCount == 5); 320 } 321 322 void testProbeModifiesStackPointer(WTF::Function<void*(ProbeContext*)> computeModifiedStack) 323 { 324 unsigned probeCallCount = 0; 325 MacroAssembler::CPUState originalState; 326 uint8_t* originalSP { nullptr }; 327 void* modifiedSP { nullptr }; 328 #if CPU(X86) || CPU(X86_64) || CPU(ARM_THUMB2) || CPU(ARM_TRADITIONAL) 329 uintptr_t modifiedFlags { 0 }; 330 #endif 306 331 307 332 compileAndRun<void>([&] (CCallHelpers& jit) { 308 333 jit.emitFunctionPrologue(); 309 334 310 // Preserve original stack pointer and modify the sp. 311 jit.probe([&] (ProbeContext* context) { 335 // Preserve original stack pointer and modify the sp, and 336 // write expected values into other registers (except for fp, and pc). 337 jit.probe([&] (ProbeContext* context) { 338 probeCallCount++; 339 for (auto id = CCallHelpers::firstRegister(); id <= CCallHelpers::lastRegister(); id = nextID(id)) { 340 originalState.gpr(id) = context->gpr(id); 341 if (isPC(id) || isSP(id) || isFP(id)) 342 continue; 343 context->gpr(id) = testWord(static_cast<int>(id)); 344 } 345 for (auto id = CCallHelpers::firstFPRegister(); id <= CCallHelpers::lastFPRegister(); id = nextID(id)) { 346 originalState.fpr(id) = context->fpr(id); 347 context->fpr(id) = testWord(id); 348 } 349 #if CPU(X86) || CPU(X86_64) 350 originalState.spr(X86Registers::eflags) = context->spr(X86Registers::eflags); 351 modifiedFlags = originalState.spr(X86Registers::eflags) ^ 0xc5; 352 context->spr(X86Registers::eflags) = modifiedFlags; 353 #elif CPU(ARM_THUMB2) || CPU(ARM_TRADITIONAL) 354 originalState.spr(ARMRegisters::apsr) = context->spr(ARMRegisters::apsr); 355 modifiedFlags = originalState.spr(ARMRegisters::apsr) ^ 0xf0000000; 356 context->spr(ARMRegisters::apsr) = modifiedFlags; 357 #endif 312 358 originalSP = reinterpret_cast<uint8_t*>(context->sp()); 313 context->sp() = originalSP - 1 * KB; 314 }); 315 316 // Validate that the stack pointer has the expected value, and restore the original. 317 jit.probe([&] (ProbeContext* context) { 318 success = (reinterpret_cast<uint8_t*>(context->sp()) == (originalSP - 1 * KB)); 359 modifiedSP = computeModifiedStack(context); 360 context->sp() = modifiedSP; 361 }); 362 363 // Validate that the registers have the expected values. 364 jit.probe([&] (ProbeContext* context) { 365 probeCallCount++; 366 for (auto id = CCallHelpers::firstRegister(); id <= CCallHelpers::lastRegister(); id = nextID(id)) { 367 if (isPC(id) || isSP(id)) 368 continue; 369 if (isFP(id)) { 370 CHECK(context->gpr(id) == originalState.gpr(id)); 371 continue; 372 } 373 CHECK(context->gpr(id) == testWord(id)); 374 } 375 for (auto id = CCallHelpers::firstFPRegister(); id <= CCallHelpers::lastFPRegister(); id = nextID(id)) 376 CHECK(context->fpr(id) == testWord(id)); 377 #if CPU(X86) || CPU(X86_64) 378 CHECK(context->spr(X86Registers::eflags) == modifiedFlags); 379 #elif CPU(ARM_THUMB2) || CPU(ARM_TRADITIONAL) 380 CHECK(context->spr(ARMRegisters::apsr) == modifiedFlags); 381 #endif 382 CHECK(context->sp() == modifiedSP); 383 }); 384 385 // Restore the original state. 386 jit.probe([&] (ProbeContext* context) { 387 probeCallCount++; 388 for (auto id = CCallHelpers::firstRegister(); id <= CCallHelpers::lastRegister(); id = nextID(id)) { 389 if (isPC(id) || isSP(id) || isFP(id)) 390 continue; 391 context->gpr(id) = originalState.gpr(id); 392 } 393 for (auto id = CCallHelpers::firstFPRegister(); id <= CCallHelpers::lastFPRegister(); id = nextID(id)) 394 context->fpr(id) = originalState.fpr(id); 395 #if CPU(X86) || CPU(X86_64) 396 context->spr(X86Registers::eflags) = originalState.spr(X86Registers::eflags); 397 #elif CPU(ARM_THUMB2) || CPU(ARM_TRADITIONAL) 398 context->spr(ARMRegisters::apsr) = originalState.spr(ARMRegisters::apsr); 399 #endif 319 400 context->sp() = originalSP; 320 401 }); 321 402 322 // Validate that the original stack pointer was restored. 323 jit.probe([&] (ProbeContext* context) { 324 success = (context->sp() == originalSP); 403 // Validate that the original state was restored. 404 jit.probe([&] (ProbeContext* context) { 405 probeCallCount++; 406 for (auto id = CCallHelpers::firstRegister(); id <= CCallHelpers::lastRegister(); id = nextID(id)) { 407 if (isPC(id) || isSP(id) || isFP(id)) 408 continue; 409 CHECK(context->gpr(id) == originalState.gpr(id)); 410 } 411 for (auto id = CCallHelpers::firstFPRegister(); id <= CCallHelpers::lastFPRegister(); id = nextID(id)) 412 CHECK(context->fpr(id) == originalState.fpr(id)); 413 #if CPU(X86) || CPU(X86_64) 414 CHECK(context->spr(X86Registers::eflags) == originalState.spr(X86Registers::eflags)); 415 #elif CPU(ARM_THUMB2) || CPU(ARM_TRADITIONAL) 416 CHECK(context->spr(ARMRegisters::apsr) == originalState.spr(ARMRegisters::apsr)); 417 #endif 418 CHECK(context->sp() == originalSP); 325 419 }); 326 420 … … 328 422 jit.ret(); 329 423 }); 330 CHECK(success); 424 CHECK(probeCallCount == 4); 425 } 426 427 void testProbeModifiesStackPointerToInsideProbeContextOnStack() 428 { 429 for (size_t offset = 0; offset < sizeof(ProbeContext); offset += sizeof(uintptr_t)) { 430 testProbeModifiesStackPointer([=] (ProbeContext* context) -> void* { 431 return reinterpret_cast<uint8_t*>(context) + offset; 432 }); 433 } 434 } 435 436 void testProbeModifiesStackPointerToNBytesBelowSP() 437 { 438 for (size_t offset = 0; offset < 1 * KB; offset += sizeof(uintptr_t)) { 439 testProbeModifiesStackPointer([=] (ProbeContext* context) -> void* { 440 return reinterpret_cast<uint8_t*>(context->cpu.sp()) - offset; 441 }); 442 } 331 443 } 332 444 … … 336 448 // having already validated that we can read and write from registers. We'll use these abilities 337 449 // to validate that the probe preserves register values. 338 bool success = false; 450 unsigned probeCallCount = 0; 451 bool continuationWasReached = false; 339 452 340 453 MacroAssemblerCodeRef continuation = compile([&] (CCallHelpers& jit) { 341 454 // Validate that we reached the continuation. 342 455 jit.probe([&] (ProbeContext*) { 343 success = true; 456 probeCallCount++; 457 continuationWasReached = true; 344 458 }); 345 459 … … 353 467 // Write expected values into the registers. 354 468 jit.probe([&] (ProbeContext* context) { 469 probeCallCount++; 355 470 context->pc() = continuation.code().executableAddress(); 356 471 }); … … 358 473 jit.breakpoint(); // We should never get here. 359 474 }); 360 CHECK(success); 475 CHECK(probeCallCount == 2); 476 CHECK(continuationWasReached); 361 477 } 362 478 #endif // ENABLE(MASM_PROBE) … … 390 506 RUN(testProbeWritesArgumentRegisters()); 391 507 RUN(testProbePreservesGPRS()); 392 RUN(testProbeModifiesStackPointer()); 508 RUN(testProbeModifiesStackPointerToInsideProbeContextOnStack()); 509 RUN(testProbeModifiesStackPointerToNBytesBelowSP()); 393 510 RUN(testProbeModifiesProgramCounter()); 394 511 #endif
Note: See TracChangeset
for help on using the changeset viewer.