1 2 /*--------------------------------------------------------------------*/ 3 /*--- Create/destroy signal delivery frames. ---*/ 4 /*--- sigframe-amd64-linux.c ---*/ 5 /*--------------------------------------------------------------------*/ 6 7 /* 8 This file is part of Valgrind, a dynamic binary instrumentation 9 framework. 10 11 Copyright (C) 2000-2015 Nicholas Nethercote 12 njn (at) valgrind.org 13 14 This program is free software; you can redistribute it and/or 15 modify it under the terms of the GNU General Public License as 16 published by the Free Software Foundation; either version 2 of the 17 License, or (at your option) any later version. 18 19 This program is distributed in the hope that it will be useful, but 20 WITHOUT ANY WARRANTY; without even the implied warranty of 21 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 22 General Public License for more details. 23 24 You should have received a copy of the GNU General Public License 25 along with this program; if not, write to the Free Software 26 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 27 02111-1307, USA. 28 29 The GNU General Public License is contained in the file COPYING. 30 */ 31 32 #if defined(VGP_amd64_linux) 33 34 #include "pub_core_basics.h" 35 #include "pub_core_vki.h" 36 #include "pub_core_threadstate.h" 37 #include "pub_core_aspacemgr.h" 38 #include "pub_core_libcbase.h" 39 #include "pub_core_libcassert.h" 40 #include "pub_core_libcprint.h" 41 #include "pub_core_machine.h" 42 #include "pub_core_options.h" 43 #include "pub_core_signals.h" 44 #include "pub_core_tooliface.h" 45 #include "pub_core_trampoline.h" 46 #include "pub_core_sigframe.h" /* self */ 47 #include "priv_sigframe.h" 48 49 /* This module creates and removes signal frames for signal deliveries 50 on amd64-linux. 51 52 Note, this file contains kernel-specific knowledge in the form of 53 'struct rt_sigframe'. How does that relate to the vki kernel 54 interface stuff? 55 56 A 'struct rtsigframe' is pushed onto the client's stack. This 57 contains a subsidiary vki_ucontext. That holds the vcpu's state 58 across the signal, so that the sighandler can mess with the vcpu 59 state if it really wants. 60 61 FIXME: sigcontexting is basically broken for the moment. When 62 delivering a signal, the integer registers and %rflags are 63 correctly written into the sigcontext, however the FP and SSE state 64 is not. When returning from a signal, only the integer registers 65 are restored from the sigcontext; the rest of the CPU state is 66 restored to what it was before the signal. 67 68 This will be fixed. 69 */ 70 71 72 /*------------------------------------------------------------*/ 73 /*--- Signal frame layouts ---*/ 74 /*------------------------------------------------------------*/ 75 76 // A structure in which to save the application's registers 77 // during the execution of signal handlers. 78 79 // In theory, so long as we get the arguments to the handler function 80 // right, it doesn't matter what the exact layout of the rest of the 81 // frame is. Unfortunately, things like gcc's exception unwinding 82 // make assumptions about the locations of various parts of the frame, 83 // so we need to duplicate it exactly. 84 85 /* Valgrind-specific parts of the signal frame */ 86 struct vg_sigframe 87 { 88 /* Sanity check word. */ 89 UInt magicPI; 90 91 UInt handlerflags; /* flags for signal handler */ 92 93 94 /* Safely-saved version of sigNo, as described above. */ 95 Int sigNo_private; 96 97 /* XXX This is wrong. Surely we should store the shadow values 98 into the shadow memory behind the actual values? */ 99 VexGuestAMD64State vex_shadow1; 100 VexGuestAMD64State vex_shadow2; 101 102 /* HACK ALERT */ 103 VexGuestAMD64State vex; 104 /* end HACK ALERT */ 105 106 /* saved signal mask to be restored when handler returns */ 107 vki_sigset_t mask; 108 109 /* Sanity check word. Is the highest-addressed word; do not 110 move!*/ 111 UInt magicE; 112 }; 113 114 struct rt_sigframe 115 { 116 /* Sig handler's return address */ 117 Addr retaddr; 118 119 /* ucontext */ 120 struct vki_ucontext uContext; 121 122 /* siginfo */ 123 vki_siginfo_t sigInfo; 124 struct _vki_fpstate fpstate; 125 126 struct vg_sigframe vg; 127 }; 128 129 130 //:: /*------------------------------------------------------------*/ 131 //:: /*--- Signal operations ---*/ 132 //:: /*------------------------------------------------------------*/ 133 //:: 134 //:: /* 135 //:: Great gobs of FP state conversion taken wholesale from 136 //:: linux/arch/i386/kernel/i387.c 137 //:: */ 138 //:: 139 //:: /* 140 //:: * FXSR floating point environment conversions. 141 //:: */ 142 //:: #define X86_FXSR_MAGIC 0x0000 143 //:: 144 //:: /* 145 //:: * FPU tag word conversions. 146 //:: */ 147 //:: 148 //:: static inline unsigned short twd_i387_to_fxsr( unsigned short twd ) 149 //:: { 150 //:: unsigned int tmp; /* to avoid 16 bit prefixes in the code */ 151 //:: 152 //:: /* Transform each pair of bits into 01 (valid) or 00 (empty) */ 153 //:: tmp = ~twd; 154 //:: tmp = (tmp | (tmp>>1)) & 0x5555; /* 0V0V0V0V0V0V0V0V */ 155 //:: /* and move the valid bits to the lower byte. */ 156 //:: tmp = (tmp | (tmp >> 1)) & 0x3333; /* 00VV00VV00VV00VV */ 157 //:: tmp = (tmp | (tmp >> 2)) & 0x0f0f; /* 0000VVVV0000VVVV */ 158 //:: tmp = (tmp | (tmp >> 4)) & 0x00ff; /* 00000000VVVVVVVV */ 159 //:: return tmp; 160 //:: } 161 //:: 162 //:: static unsigned long twd_fxsr_to_i387( const struct i387_fxsave_struct *fxsave ) 163 //:: { 164 //:: struct _vki_fpxreg *st = NULL; 165 //:: unsigned long twd = (unsigned long) fxsave->twd; 166 //:: unsigned long tag; 167 //:: unsigned long ret = 0xffff0000u; 168 //:: int i; 169 //:: 170 //:: #define FPREG_ADDR(f, n) ((char *)&(f)->st_space + (n) * 16); 171 //:: 172 //:: for ( i = 0 ; i < 8 ; i++ ) { 173 //:: if ( twd & 0x1 ) { 174 //:: st = (struct _vki_fpxreg *) FPREG_ADDR( fxsave, i ); 175 //:: 176 //:: switch ( st->exponent & 0x7fff ) { 177 //:: case 0x7fff: 178 //:: tag = 2; /* Special */ 179 //:: break; 180 //:: case 0x0000: 181 //:: if ( !st->significand[0] && 182 //:: !st->significand[1] && 183 //:: !st->significand[2] && 184 //:: !st->significand[3] ) { 185 //:: tag = 1; /* Zero */ 186 //:: } else { 187 //:: tag = 2; /* Special */ 188 //:: } 189 //:: break; 190 //:: default: 191 //:: if ( st->significand[3] & 0x8000 ) { 192 //:: tag = 0; /* Valid */ 193 //:: } else { 194 //:: tag = 2; /* Special */ 195 //:: } 196 //:: break; 197 //:: } 198 //:: } else { 199 //:: tag = 3; /* Empty */ 200 //:: } 201 //:: ret |= (tag << (2 * i)); 202 //:: twd = twd >> 1; 203 //:: } 204 //:: return ret; 205 //:: } 206 //:: 207 //:: static void convert_fxsr_to_user( struct _vki_fpstate *buf, 208 //:: const struct i387_fxsave_struct *fxsave ) 209 //:: { 210 //:: unsigned long env[7]; 211 //:: struct _vki_fpreg *to; 212 //:: struct _vki_fpxreg *from; 213 //:: int i; 214 //:: 215 //:: env[0] = (unsigned long)fxsave->cwd | 0xffff0000ul; 216 //:: env[1] = (unsigned long)fxsave->swd | 0xffff0000ul; 217 //:: env[2] = twd_fxsr_to_i387(fxsave); 218 //:: env[3] = fxsave->fip; 219 //:: env[4] = fxsave->fcs | ((unsigned long)fxsave->fop << 16); 220 //:: env[5] = fxsave->foo; 221 //:: env[6] = fxsave->fos; 222 //:: 223 //:: VG_(memcpy)(buf, env, 7 * sizeof(unsigned long)); 224 //:: 225 //:: to = &buf->_st[0]; 226 //:: from = (struct _vki_fpxreg *) &fxsave->st_space[0]; 227 //:: for ( i = 0 ; i < 8 ; i++, to++, from++ ) { 228 //:: unsigned long __user *t = (unsigned long __user *)to; 229 //:: unsigned long *f = (unsigned long *)from; 230 //:: 231 //:: t[0] = f[0]; 232 //:: t[1] = f[1]; 233 //:: to->exponent = from->exponent; 234 //:: } 235 //:: } 236 //:: 237 //:: static void convert_fxsr_from_user( struct i387_fxsave_struct *fxsave, 238 //:: const struct _vki_fpstate *buf ) 239 //:: { 240 //:: unsigned long env[7]; 241 //:: struct _vki_fpxreg *to; 242 //:: const struct _vki_fpreg *from; 243 //:: int i; 244 //:: 245 //:: VG_(memcpy)(env, buf, 7 * sizeof(long)); 246 //:: 247 //:: fxsave->cwd = (unsigned short)(env[0] & 0xffff); 248 //:: fxsave->swd = (unsigned short)(env[1] & 0xffff); 249 //:: fxsave->twd = twd_i387_to_fxsr((unsigned short)(env[2] & 0xffff)); 250 //:: fxsave->fip = env[3]; 251 //:: fxsave->fop = (unsigned short)((env[4] & 0xffff0000ul) >> 16); 252 //:: fxsave->fcs = (env[4] & 0xffff); 253 //:: fxsave->foo = env[5]; 254 //:: fxsave->fos = env[6]; 255 //:: 256 //:: to = (struct _vki_fpxreg *) &fxsave->st_space[0]; 257 //:: from = &buf->_st[0]; 258 //:: for ( i = 0 ; i < 8 ; i++, to++, from++ ) { 259 //:: unsigned long *t = (unsigned long *)to; 260 //:: unsigned long __user *f = (unsigned long __user *)from; 261 //:: 262 //:: t[0] = f[0]; 263 //:: t[1] = f[1]; 264 //:: to->exponent = from->exponent; 265 //:: } 266 //:: } 267 //:: 268 //:: static inline void save_i387_fsave( arch_thread_t *regs, struct _vki_fpstate *buf ) 269 //:: { 270 //:: struct i387_fsave_struct *fs = ®s->m_sse.fsave; 271 //:: 272 //:: fs->status = fs->swd; 273 //:: VG_(memcpy)(buf, fs, sizeof(*fs)); 274 //:: } 275 //:: 276 //:: static void save_i387_fxsave( arch_thread_t *regs, struct _vki_fpstate *buf ) 277 //:: { 278 //:: const struct i387_fxsave_struct *fx = ®s->m_sse.fxsave; 279 //:: convert_fxsr_to_user( buf, fx ); 280 //:: 281 //:: buf->status = fx->swd; 282 //:: buf->magic = X86_FXSR_MAGIC; 283 //:: VG_(memcpy)(buf->_fxsr_env, fx, sizeof(struct i387_fxsave_struct)); 284 //:: } 285 //:: 286 //:: static void save_i387( arch_thread_t *regs, struct _vki_fpstate *buf ) 287 //:: { 288 //:: if ( VG_(have_ssestate) ) 289 //:: save_i387_fxsave( regs, buf ); 290 //:: else 291 //:: save_i387_fsave( regs, buf ); 292 //:: } 293 //:: 294 //:: static inline void restore_i387_fsave( arch_thread_t *regs, const struct _vki_fpstate __user *buf ) 295 //:: { 296 //:: VG_(memcpy)( ®s->m_sse.fsave, buf, sizeof(struct i387_fsave_struct) ); 297 //:: } 298 //:: 299 //:: static void restore_i387_fxsave( arch_thread_t *regs, const struct _vki_fpstate __user *buf ) 300 //:: { 301 //:: VG_(memcpy)(®s->m_sse.fxsave, &buf->_fxsr_env[0], 302 //:: sizeof(struct i387_fxsave_struct) ); 303 //:: /* mxcsr reserved bits must be masked to zero for security reasons */ 304 //:: regs->m_sse.fxsave.mxcsr &= 0xffbf; 305 //:: convert_fxsr_from_user( ®s->m_sse.fxsave, buf ); 306 //:: } 307 //:: 308 //:: static void restore_i387( arch_thread_t *regs, const struct _vki_fpstate __user *buf ) 309 //:: { 310 //:: if ( VG_(have_ssestate) ) { 311 //:: restore_i387_fxsave( regs, buf ); 312 //:: } else { 313 //:: restore_i387_fsave( regs, buf ); 314 //:: } 315 //:: } 316 317 318 /*------------------------------------------------------------*/ 319 /*--- Creating signal frames ---*/ 320 /*------------------------------------------------------------*/ 321 322 /* Create a plausible-looking sigcontext from the thread's 323 Vex guest state. NOTE: does not fill in the FP or SSE 324 bits of sigcontext at the moment. 325 */ 326 static 327 void synth_ucontext(ThreadId tid, const vki_siginfo_t *si, 328 UWord trapno, UWord err, const vki_sigset_t *set, 329 struct vki_ucontext *uc, struct _vki_fpstate *fpstate) 330 { 331 ThreadState *tst = VG_(get_ThreadState)(tid); 332 struct vki_sigcontext *sc = &uc->uc_mcontext; 333 334 VG_(memset)(uc, 0, sizeof(*uc)); 335 336 uc->uc_flags = 0; 337 uc->uc_link = 0; 338 uc->uc_sigmask = *set; 339 uc->uc_stack = tst->altstack; 340 sc->fpstate = fpstate; 341 342 // FIXME: save_i387(&tst->arch, fpstate); 343 344 # define SC2(reg,REG) sc->reg = tst->arch.vex.guest_##REG 345 SC2(r8,R8); 346 SC2(r9,R9); 347 SC2(r10,R10); 348 SC2(r11,R11); 349 SC2(r12,R12); 350 SC2(r13,R13); 351 SC2(r14,R14); 352 SC2(r15,R15); 353 SC2(rdi,RDI); 354 SC2(rsi,RSI); 355 SC2(rbp,RBP); 356 SC2(rbx,RBX); 357 SC2(rdx,RDX); 358 SC2(rax,RAX); 359 SC2(rcx,RCX); 360 SC2(rsp,RSP); 361 362 SC2(rip,RIP); 363 sc->eflags = LibVEX_GuestAMD64_get_rflags(&tst->arch.vex); 364 // FIXME: SC2(cs,CS); 365 // FIXME: SC2(gs,GS); 366 // FIXME: SC2(fs,FS); 367 sc->trapno = trapno; 368 sc->err = err; 369 # undef SC2 370 371 sc->cr2 = (UWord)si->_sifields._sigfault._addr; 372 } 373 374 375 /* Build the Valgrind-specific part of a signal frame. */ 376 377 static void build_vg_sigframe(struct vg_sigframe *frame, 378 ThreadState *tst, 379 const vki_sigset_t *mask, 380 UInt flags, 381 Int sigNo) 382 { 383 frame->sigNo_private = sigNo; 384 frame->magicPI = 0x31415927; 385 frame->vex_shadow1 = tst->arch.vex_shadow1; 386 frame->vex_shadow2 = tst->arch.vex_shadow2; 387 /* HACK ALERT */ 388 frame->vex = tst->arch.vex; 389 /* end HACK ALERT */ 390 frame->mask = tst->sig_mask; 391 frame->handlerflags = flags; 392 frame->magicE = 0x27182818; 393 } 394 395 396 static Addr build_rt_sigframe(ThreadState *tst, 397 Addr rsp_top_of_frame, 398 const vki_siginfo_t *siginfo, 399 const struct vki_ucontext *siguc, 400 void *handler, UInt flags, 401 const vki_sigset_t *mask, 402 void *restorer) 403 { 404 struct rt_sigframe *frame; 405 Addr rsp = rsp_top_of_frame; 406 Int sigNo = siginfo->si_signo; 407 UWord trapno; 408 UWord err; 409 410 rsp -= sizeof(*frame); 411 rsp = VG_ROUNDDN(rsp, 16) - 8; 412 frame = (struct rt_sigframe *)rsp; 413 414 if (! ML_(sf_maybe_extend_stack)(tst, rsp, sizeof(*frame), flags)) 415 return rsp_top_of_frame; 416 417 /* retaddr, siginfo, uContext fields are to be written */ 418 VG_TRACK( pre_mem_write, Vg_CoreSignal, tst->tid, "rt signal handler frame", 419 rsp, offsetof(struct rt_sigframe, vg) ); 420 421 if (flags & VKI_SA_RESTORER) 422 frame->retaddr = (Addr)restorer; 423 else 424 frame->retaddr = (Addr)&VG_(amd64_linux_SUBST_FOR_rt_sigreturn); 425 426 if (siguc) { 427 trapno = siguc->uc_mcontext.trapno; 428 err = siguc->uc_mcontext.err; 429 } else { 430 trapno = 0; 431 err = 0; 432 } 433 434 VG_(memcpy)(&frame->sigInfo, siginfo, sizeof(vki_siginfo_t)); 435 436 /* SIGILL defines addr to be the faulting address */ 437 if (sigNo == VKI_SIGILL && siginfo->si_code > 0) 438 frame->sigInfo._sifields._sigfault._addr 439 = (void*)tst->arch.vex.guest_RIP; 440 441 synth_ucontext(tst->tid, siginfo, trapno, err, mask, 442 &frame->uContext, &frame->fpstate); 443 444 VG_TRACK( post_mem_write, Vg_CoreSignal, tst->tid, 445 rsp, offsetof(struct rt_sigframe, vg) ); 446 447 build_vg_sigframe(&frame->vg, tst, mask, flags, sigNo); 448 449 return rsp; 450 } 451 452 453 void VG_(sigframe_create)( ThreadId tid, 454 Bool on_altstack, 455 Addr rsp_top_of_frame, 456 const vki_siginfo_t *siginfo, 457 const struct vki_ucontext *siguc, 458 void *handler, 459 UInt flags, 460 const vki_sigset_t *mask, 461 void *restorer ) 462 { 463 Addr rsp; 464 struct rt_sigframe *frame; 465 ThreadState* tst = VG_(get_ThreadState)(tid); 466 467 rsp = build_rt_sigframe(tst, rsp_top_of_frame, siginfo, siguc, 468 handler, flags, mask, restorer); 469 frame = (struct rt_sigframe *)rsp; 470 471 /* Set the thread so it will next run the handler. */ 472 /* tst->m_rsp = rsp; also notify the tool we've updated RSP */ 473 VG_(set_SP)(tid, rsp); 474 VG_TRACK( post_reg_write, Vg_CoreSignal, tid, VG_O_STACK_PTR, sizeof(Addr)); 475 476 //VG_(printf)("handler = %p\n", handler); 477 tst->arch.vex.guest_RIP = (Addr) handler; 478 tst->arch.vex.guest_RDI = (ULong) siginfo->si_signo; 479 tst->arch.vex.guest_RSI = (Addr) &frame->sigInfo; 480 tst->arch.vex.guest_RDX = (Addr) &frame->uContext; 481 /* And tell the tool that these registers have been written. */ 482 VG_TRACK( post_reg_write, Vg_CoreSignal, tst->tid, 483 offsetof(VexGuestAMD64State,guest_RIP), sizeof(UWord) ); 484 VG_TRACK( post_reg_write, Vg_CoreSignal, tst->tid, 485 offsetof(VexGuestAMD64State,guest_RDI), sizeof(UWord) ); 486 VG_TRACK( post_reg_write, Vg_CoreSignal, tst->tid, 487 offsetof(VexGuestAMD64State,guest_RSI), sizeof(UWord) ); 488 VG_TRACK( post_reg_write, Vg_CoreSignal, tst->tid, 489 offsetof(VexGuestAMD64State,guest_RDX), sizeof(UWord) ); 490 491 /* This thread needs to be marked runnable, but we leave that the 492 caller to do. */ 493 494 if (0) 495 VG_(printf)("pushed signal frame; %%RSP now = %#lx, " 496 "next %%RIP = %#llx, status=%d\n", 497 rsp, tst->arch.vex.guest_RIP, (Int)tst->status); 498 } 499 500 501 /*------------------------------------------------------------*/ 502 /*--- Destroying signal frames ---*/ 503 /*------------------------------------------------------------*/ 504 505 /* Return False and don't do anything, just set the client to take a 506 segfault, if it looks like the frame is corrupted. */ 507 static 508 Bool restore_vg_sigframe ( ThreadState *tst, 509 struct vg_sigframe *frame, Int *sigNo ) 510 { 511 if (frame->magicPI != 0x31415927 || 512 frame->magicE != 0x27182818) { 513 VG_(message)(Vg_UserMsg, "Thread %u return signal frame " 514 "corrupted. Killing process.\n", 515 tst->tid); 516 VG_(set_default_handler)(VKI_SIGSEGV); 517 VG_(synth_fault)(tst->tid); 518 *sigNo = VKI_SIGSEGV; 519 return False; 520 } 521 tst->sig_mask = frame->mask; 522 tst->tmp_sig_mask = frame->mask; 523 tst->arch.vex_shadow1 = frame->vex_shadow1; 524 tst->arch.vex_shadow2 = frame->vex_shadow2; 525 /* HACK ALERT */ 526 tst->arch.vex = frame->vex; 527 /* end HACK ALERT */ 528 *sigNo = frame->sigNo_private; 529 return True; 530 } 531 532 static 533 void restore_sigcontext( ThreadState *tst, 534 struct vki_sigcontext *sc, 535 struct _vki_fpstate *fpstate ) 536 { 537 tst->arch.vex.guest_RAX = sc->rax; 538 tst->arch.vex.guest_RCX = sc->rcx; 539 tst->arch.vex.guest_RDX = sc->rdx; 540 tst->arch.vex.guest_RBX = sc->rbx; 541 tst->arch.vex.guest_RBP = sc->rbp; 542 tst->arch.vex.guest_RSP = sc->rsp; 543 tst->arch.vex.guest_RSI = sc->rsi; 544 tst->arch.vex.guest_RDI = sc->rdi; 545 tst->arch.vex.guest_R8 = sc->r8; 546 tst->arch.vex.guest_R9 = sc->r9; 547 tst->arch.vex.guest_R10 = sc->r10; 548 tst->arch.vex.guest_R11 = sc->r11; 549 tst->arch.vex.guest_R12 = sc->r12; 550 tst->arch.vex.guest_R13 = sc->r13; 551 tst->arch.vex.guest_R14 = sc->r14; 552 tst->arch.vex.guest_R15 = sc->r15; 553 //:: tst->arch.vex.guest_rflags = sc->rflags; 554 tst->arch.vex.guest_RIP = sc->rip; 555 556 //:: tst->arch.vex.guest_CS = sc->cs; 557 //:: tst->arch.vex.guest_FS = sc->fs; 558 //:: tst->arch.vex.guest_GS = sc->gs; 559 560 //:: restore_i387(&tst->arch, fpstate); 561 } 562 563 564 static 565 SizeT restore_rt_sigframe ( ThreadState *tst, 566 struct rt_sigframe *frame, Int *sigNo ) 567 { 568 if (restore_vg_sigframe(tst, &frame->vg, sigNo)) 569 restore_sigcontext(tst, &frame->uContext.uc_mcontext, &frame->fpstate); 570 571 return sizeof(*frame); 572 } 573 574 575 void VG_(sigframe_destroy)( ThreadId tid, Bool isRT ) 576 { 577 Addr rsp; 578 ThreadState* tst; 579 SizeT size; 580 Int sigNo; 581 582 vg_assert(isRT); 583 584 tst = VG_(get_ThreadState)(tid); 585 586 /* Correctly reestablish the frame base address. */ 587 rsp = tst->arch.vex.guest_RSP; 588 589 size = restore_rt_sigframe(tst, (struct rt_sigframe *)rsp, &sigNo); 590 591 VG_TRACK( die_mem_stack_signal, rsp - VG_STACK_REDZONE_SZB, 592 size + VG_STACK_REDZONE_SZB ); 593 594 if (VG_(clo_trace_signals)) 595 VG_(message)( 596 Vg_DebugMsg, 597 "VG_(signal_return) (thread %u): isRT=%d valid magic; RIP=%#llx\n", 598 tid, isRT, tst->arch.vex.guest_RIP); 599 600 /* tell the tools */ 601 VG_TRACK( post_deliver_signal, tid, sigNo ); 602 } 603 604 #endif // defined(VGP_amd64_linux) 605 606 /*--------------------------------------------------------------------*/ 607 /*--- end ---*/ 608 /*--------------------------------------------------------------------*/ 609