Beispiel #1
0
static void give_control_back_to_vgdb(void)
{
#if !defined(VGO_solaris)
   /* cause a SIGSTOP to be sent to ourself, so that vgdb takes control.
      vgdb will then restore the stack so as to resume the activity
      before the ptrace (typically do_syscall_WRK). */
   if (VG_(kill)(VG_(getpid)(), VKI_SIGSTOP) != 0)
      vg_assert2(0, "SIGSTOP for vgdb could not be generated\n");

   /* If we arrive here, it means a call was pushed on the stack
      by vgdb, but during this call, vgdb and/or connection
      died. Alternatively, it is a bug in the vgdb<=>Valgrind gdbserver
      ptrace handling. */
   vg_assert2(0, 
              "vgdb did not took control. Did you kill vgdb ?\n"
              "busy %d vgdb_interrupted_tid %u\n",
              busy, vgdb_interrupted_tid);
#else /* defined(VGO_solaris) */
   /* On Solaris, this code is run within the context of an agent thread
      (see vgdb-invoker-solaris.c and "PCAGENT" control message in
      proc(4)). Exit the agent thread now.
    */
   SysRes sres = VG_(do_syscall0)(SYS_lwp_exit);
   if (sr_isError(sres))
      vg_assert2(0, "The agent thread could not be exited\n");
#endif /* !defined(VGO_solaris) */
}
Beispiel #2
0
static 
void handle_maybe_load_notifier( const UChar* soname, 
                                       HChar* symbol, Addr addr )
{
#  if defined(VGP_x86_linux)
   /* x86-linux only: if we see _dl_sysinfo_int80, note its address.
      See comment on declaration of VG_(client__dl_sysinfo_int80) for
      the reason.  As far as I can tell, the relevant symbol is always
      in object with soname "ld-linux.so.2". */
   if (symbol && symbol[0] == '_' 
              && 0 == VG_(strcmp)(symbol, "_dl_sysinfo_int80")
              && 0 == VG_(strcmp)(soname, "ld-linux.so.2")) {
      if (VG_(client__dl_sysinfo_int80) == 0)
         VG_(client__dl_sysinfo_int80) = addr;
   }
#  endif

   /* Normal load-notifier handling after here.  First, ignore all
      symbols lacking the right prefix. */
   if (0 != VG_(strncmp)(symbol, VG_NOTIFY_ON_LOAD_PREFIX, 
                                 VG_NOTIFY_ON_LOAD_PREFIX_LEN))
      /* Doesn't have the right prefix */
      return;

   if (VG_(strcmp)(symbol, VG_STRINGIFY(VG_NOTIFY_ON_LOAD(freeres))) == 0)
      VG_(client___libc_freeres_wrapper) = addr;
   else
      vg_assert2(0, "unrecognised load notification function: %s", symbol);
}
Beispiel #3
0
/* Allocate a stack for the main thread, and run it all the way to the
   end.  Although we already have a working VgStack
   (VG_(interim_stack)) it's better to allocate a new one, so that
   overflow detection works uniformly for all threads.
*/
void VG_(main_thread_wrapper_NORETURN)(ThreadId tid)
{
   Addr sp;
   VG_(debugLog)(1, "syswr-l4", 
                    "entering VG_(main_thread_wrapper_NORETURN)\n");

   sp = ML_(allocstack)(tid);

   /* If we can't even allocate the first thread's stack, we're hosed.
      Give up. */
   vg_assert2(sp != 0, "Cannot allocate main thread's stack.");

   /* shouldn't be any other threads around yet */
   vg_assert( VG_(count_living_threads)() == 1 );

   ML_(call_on_new_stack_0_1)( 
      (Addr)sp,               /* stack */
      0,                      /* bogus return address */
      run_a_thread_NORETURN,  /* fn to call */
      (Word)tid               /* arg to give it */
   );

   /*NOTREACHED*/
   vg_assert(0);
}
Beispiel #4
0
static void give_control_back_to_vgdb(void)
{
   /* cause a SIGSTOP to be sent to ourself, so that vgdb takes control.
      vgdb will then restore the stack so as to resume the activity
      before the ptrace (typically do_syscall_WRK). */
   if (VG_(kill)(VG_(getpid)(), VKI_SIGSTOP) != 0)
      vg_assert2(0, "SIGSTOP for vgdb could not be generated\n");

   /* If we arrive here, it means a call was pushed on the stack
      by vgdb, but during this call, vgdb and/or connection
      died. Alternatively, it is a bug in the vgdb<=>Valgrind gdbserver
      ptrace handling. */
   vg_assert2(0, 
              "vgdb did not took control. Did you kill vgdb ?\n"
              "busy %d vgdb_interrupted_tid %d\n",
              busy, vgdb_interrupted_tid);
}
Beispiel #5
0
static void handle_load_notifier( Char* symbol, Addr addr )
{
   if (VG_(strcmp)(symbol, STR(VG_NOTIFY_ON_LOAD(freeres))) == 0)
      __libc_freeres_wrapper = addr;
//   else if (VG_(strcmp)(symbol, STR(VG_WRAPPER(pthread_startfunc_wrapper))) == 0)
//      VG_(pthread_startfunc_wrapper)((Addr)(si->offset + sym->st_value));
   else
      vg_assert2(0, "unrecognised load notification function: %s", symbol);
}
Beispiel #6
0
// Given an AvlNode, return the pointer to the element.
static inline
void* elem_of_node(const AvlNode *n)
{
   vg_assert2(n->magic == OSET_MAGIC,
              "bad magic on node %p = %x (expected %x)\n"
              "possible causes:\n"
              " - node metadata corrupted by overwriting end of element?\n",
              n, n->magic, OSET_MAGIC);
   return (void*)((Addr)n + sizeof(AvlNode));
}
Beispiel #7
0
// Given a pointer to the node's element, return the pointer to the AvlNode
// structure.  If the node has a bad magic number, it will die with an
// assertion failure.
static inline
AvlNode* node_of_elem(const void *elem)
{
   AvlNode* n = (AvlNode*)((Addr)elem - sizeof(AvlNode));
   vg_assert2(n->magic == OSET_MAGIC,
              "bad magic on node %p = %x (expected %x)\n"
              "possible causes:\n"
              " - node not allocated with VG_(OSet_AllocNode)()?\n"
              " - node metadata corrupted by underwriting start of element?\n",
              n, n->magic, OSET_MAGIC);
   return n;
}
Beispiel #8
0
// returns: 0 = success, non-0 is failure
//
// We can execute only binaries (ELF, etc) or scripts that begin with "#!".
// (Not, for example, scripts that don't begin with "#!";  see the
// VG_(do_exec)() invocation from m_main.c for how that's handled.)
Int VG_(do_exec_inner)(const HChar* exe, ExeInfo* info)
{
   SysRes res;
   Int fd;
   Int ret;

   res = VG_(pre_exec_check)(exe, &fd, False/*allow_setuid*/);
   if (sr_isError(res))
      return sr_Err(res);

   vg_assert2(sr_Res(res) >= 0 && sr_Res(res) < EXE_HANDLER_COUNT, 
              "invalid VG_(pre_exec_check) result");

   ret = (*exe_handlers[sr_Res(res)].load_fn)(fd, exe, info);

   VG_(close)(fd);

   return ret;
}
Beispiel #9
0
Bool VG_(machine_get_hwcaps)( void )
{
   vg_assert(hwcaps_done == False);
   hwcaps_done = True;

   // Whack default settings into vai, so that we only need to fill in
   // any interesting bits.
   LibVEX_default_VexArchInfo(&vai);

#if defined(VGA_x86)
   { Bool have_sse1, have_sse2;
     UInt eax, ebx, ecx, edx;

     if (!VG_(has_cpuid)())
        /* we can't do cpuid at all.  Give up. */
        return False;

     VG_(cpuid)(0, &eax, &ebx, &ecx, &edx);
     if (eax < 1)
        /* we can't ask for cpuid(x) for x > 0.  Give up. */
        return False;

     /* get capabilities bits into edx */
     VG_(cpuid)(1, &eax, &ebx, &ecx, &edx);

     have_sse1 = (edx & (1<<25)) != 0; /* True => have sse insns */
     have_sse2 = (edx & (1<<26)) != 0; /* True => have sse2 insns */

     if (have_sse2 && have_sse1) {
        va          = VexArchX86;
        vai.subarch = VexSubArchX86_sse2;
        VG_(machine_x86_have_mxcsr) = 1;
        return True;
     }

     if (have_sse1) {
        va          = VexArchX86;
        vai.subarch = VexSubArchX86_sse1;
        VG_(machine_x86_have_mxcsr) = 1;
        return True;
     }

     va          = VexArchX86;
     vai.subarch = VexSubArchX86_sse0;
     VG_(machine_x86_have_mxcsr) = 0;
     return True;
   }

#elif defined(VGA_amd64)
   vg_assert(VG_(has_cpuid)());
   va          = VexArchAMD64;
   vai.subarch = VexSubArch_NONE;
   return True;

#elif defined(VGA_ppc32)
   { /* ppc32 doesn't seem to have a sane way to find out what insn
        sets the CPU supports.  So we have to arse around with
        SIGILLs.  Yuck. */
     vki_sigset_t         saved_set, tmp_set;
     struct vki_sigaction saved_act, tmp_act;

     volatile Bool have_fp, have_vmx;

     VG_(sigemptyset)(&tmp_set);
     VG_(sigaddset)(&tmp_set, VKI_SIGILL);

     VG_(sigprocmask)(VKI_SIG_UNBLOCK, &tmp_set, &saved_set);

     VG_(sigaction)(VKI_SIGILL, NULL, &saved_act);
     tmp_act = saved_act;

     tmp_act.sa_flags &= ~VKI_SA_RESETHAND;
     tmp_act.sa_flags &= ~VKI_SA_SIGINFO;

     tmp_act.ksa_handler = handler_sigill;
     VG_(sigaction)(VKI_SIGILL, &tmp_act, NULL);

     have_fp = True;
     if (__builtin_setjmp(env_sigill)) {
        have_fp = False;
     } else {
        __asm__ __volatile__("fmr 0,0");
     }

     tmp_act.ksa_handler = handler_sigill;
     VG_(sigaction)(VKI_SIGILL, &tmp_act, NULL);

     have_vmx = True;
     if (__builtin_setjmp(env_sigill)) {
        have_vmx = False;
     } else {
        __asm__ __volatile__("vor 0,0,0");
     }

     VG_(sigaction)(VKI_SIGILL, &saved_act, NULL);
     VG_(sigprocmask)(VKI_SIG_SETMASK, &saved_set, NULL);

     /* VG_(printf)("FP %d VMX %d\n", (Int)have_fp, (Int)have_vmx); */

     /* We can only support 3 cases, not 4 (vmx but no fp).  So make
	fp a prerequisite for vmx. */
     if (have_vmx && !have_fp)
        have_vmx = False;

     VG_(machine_ppc32_has_FP)  = have_fp  ? 1 : 0;
     VG_(machine_ppc32_has_VMX) = have_vmx ? 1 : 0;

     va = VexArchPPC32;

     if (have_fp == False && have_vmx == False) {
        vai.subarch = VexSubArchPPC32_I;
     }
     else if (have_fp == True && have_vmx == False) {
        vai.subarch = VexSubArchPPC32_FI;
     }
     else if (have_fp == True && have_vmx == True) {
        vai.subarch = VexSubArchPPC32_VFI;
     } else {
        /* this can't happen. */
        vg_assert2(0, "VG_(machine_get_hwcaps)(ppc32)");
     }

     /* But we're not done yet: VG_(machine_ppc32_set_clszB) must be
        called before we're ready to go. */
     return True;
   }

#elif defined(VGA_ppc64)
   { vki_sigset_t         saved_set, tmp_set;
     struct vki_sigaction saved_act, tmp_act;

     volatile Bool have_vmx;

     VG_(sigemptyset)(&tmp_set);
     VG_(sigaddset)(&tmp_set, VKI_SIGILL);

     VG_(sigprocmask)(VKI_SIG_UNBLOCK, &tmp_set, &saved_set);

     VG_(sigaction)(VKI_SIGILL, NULL, &saved_act);
     tmp_act = saved_act;

     tmp_act.sa_flags &= ~VKI_SA_RESETHAND;
     tmp_act.sa_flags &= ~VKI_SA_SIGINFO;

     tmp_act.ksa_handler = handler_sigill;
     VG_(sigaction)(VKI_SIGILL, &tmp_act, NULL);

     have_vmx = True;
     if (__builtin_setjmp(env_sigill)) {
        have_vmx = False;
     } else {
        __asm__ __volatile__("vor 0,0,0");
     }

     VG_(sigaction)(VKI_SIGILL, &saved_act, NULL);
     VG_(sigprocmask)(VKI_SIG_SETMASK, &saved_set, NULL);

     /* VG_(printf)("VMX %d\n", (Int)have_vmx); */

     VG_(machine_ppc64_has_VMX) = have_vmx ? 1 : 0;

     va = VexArchPPC64;
     vai.subarch = have_vmx ? VexSubArchPPC64_VFI : VexSubArchPPC64_FI;

     /* But we're not done yet: VG_(machine_ppc64_set_clszB) must be
        called before we're ready to go. */
     return True;
   }

#else
#  error "Unknown arch"
#endif
}
Beispiel #10
0
/* Using ptrace calls, vgdb will force an invocation of gdbserver.
   VG_(invoke_gdbserver) is the entry point called through the
   vgdb ptrace technique. */
void VG_(invoke_gdbserver) ( int check )
{
   /* ******* Avoid non-reentrant function call from here ..... 
      till the ".... till here" below. */

   /* We need to determine the state of the various threads to decide
      if we directly invoke gdbserver or if we rather indicate to the
      scheduler to invoke the gdbserver.  To decide that, it is
      critical to avoid any "coregrind" function call as the ptrace
      might have stopped the process in the middle of this (possibly)
      non-rentrant function.  So, it is only when all threads are in
      an "interruptible" state that we can safely invoke
      gdbserver. Otherwise, we let the valgrind scheduler invoke
      gdbserver at the next poll.  This poll will be made very soon
      thanks to a call to VG_(force_vgdb_poll). */
   int n_tid;

   vg_assert (check == 0x8BADF00D);

   if (busy) {
      interrupts_while_busy++;
      give_control_back_to_vgdb();
   }
   interrupts_non_busy++;

   /* check if all threads are in an "interruptible" state.  If yes,
      we invoke gdbserver. Otherwise, we tell the scheduler to wake up
      asap. */
   for (n_tid = 1; n_tid < VG_N_THREADS; n_tid++) {
      switch (VG_(threads)[n_tid].status) {
      /* interruptible states. */
      case VgTs_WaitSys:
      case VgTs_Yielding:
         if (vgdb_interrupted_tid == 0) vgdb_interrupted_tid = n_tid;
         break;

      case VgTs_Empty:     
      case VgTs_Zombie:
         break;

      /* non interruptible states. */
      case VgTs_Init:
      case VgTs_Runnable:
         interrupts_non_interruptible++;
         VG_(force_vgdb_poll) ();
         give_control_back_to_vgdb();

      default:             vg_assert(0);
      }
   }

   /* .... till here.
      From here onwards, function calls are ok: it is
      safe to call valgrind core functions: all threads are blocked in
      a system call or are yielding or ... */
   dlog(1, "invoke_gdbserver running_tid %d vgdb_interrupted_tid %d\n",
        VG_(running_tid), vgdb_interrupted_tid);
   call_gdbserver (vgdb_interrupted_tid, vgdb_reason);
   vgdb_interrupted_tid = 0;
   dlog(1,
        "exit invoke_gdbserver running_tid %d\n", VG_(running_tid));
   give_control_back_to_vgdb();

   vg_assert2(0, "end of invoke_gdbserver reached");

}
Bool VG_(maybe_Z_demangle) ( const HChar* sym, 
                             /*OUT*/const HChar** so,
                             /*OUT*/const HChar** fn,
                             /*OUT*/Bool* isWrap,
                             /*OUT*/Int*  eclassTag,
                             /*OUT*/Int*  eclassPrio )
{
   static HChar *sobuf;
   static HChar *fnbuf;
   static SizeT  buf_len = 0;

   /* The length of the name after undoing Z-encoding is always smaller
      than the mangled name. Making the soname and fnname buffers as large
      as the demangled name is therefore always safe and overflow can never
      occur. */
   SizeT len = VG_(strlen)(sym) + 1;

   if (buf_len < len) {
      sobuf = VG_(arena_realloc)(VG_AR_DEMANGLE, "Z-demangle", sobuf, len);
      fnbuf = VG_(arena_realloc)(VG_AR_DEMANGLE, "Z-demangle", fnbuf, len);
      buf_len = len;
   }
   sobuf[0] = fnbuf[0] = '\0';

   if (so) 
     *so = sobuf;
   *fn = fnbuf;

#  define EMITSO(ch)                           \
      do {                                     \
         if (so) {                             \
            sobuf[soi++] = ch; sobuf[soi] = 0; \
         }                                     \
      } while (0)
#  define EMITFN(ch)                           \
      do {                                     \
         fnbuf[fni++] = ch; fnbuf[fni] = 0;    \
      } while (0)

   Bool error, valid, fn_is_encoded, is_VG_Z_prefixed;
   Int  soi, fni, i;

   error = False;
   soi = 0;
   fni = 0;

   valid =     sym[0] == '_'
           &&  sym[1] == 'v'
           &&  sym[2] == 'g'
           && (sym[3] == 'r' || sym[3] == 'w')
           &&  VG_(isdigit)(sym[4])
           &&  VG_(isdigit)(sym[5])
           &&  VG_(isdigit)(sym[6])
           &&  VG_(isdigit)(sym[7])
           &&  VG_(isdigit)(sym[8])
           &&  sym[9] == 'Z'
           && (sym[10] == 'Z' || sym[10] == 'U')
           &&  sym[11] == '_';

   if (valid
       && sym[4] == '0' && sym[5] == '0' && sym[6] == '0' && sym[7] == '0'
       && sym[8] != '0') {
      /* If the eclass tag is 0000 (meaning "no eclass"), the priority
         must be 0 too. */
      valid = False;
   }

   if (!valid)
      return False;

   fn_is_encoded = sym[10] == 'Z';

   if (isWrap)
      *isWrap = sym[3] == 'w';

   if (eclassTag) {
      *eclassTag =    1000 * ((Int)sym[4] - '0')
                   +  100 * ((Int)sym[5] - '0')
                   +  10 * ((Int)sym[6] - '0')
                   +  1 * ((Int)sym[7] - '0');
      vg_assert(*eclassTag >= 0 && *eclassTag <= 9999);
   }

   if (eclassPrio) {
      *eclassPrio = ((Int)sym[8]) - '0';
      vg_assert(*eclassPrio >= 0 && *eclassPrio <= 9);
   }

   /* Now check the soname prefix isn't "VG_Z_", as described in
      pub_tool_redir.h. */
   is_VG_Z_prefixed =
      sym[12] == 'V' &&
      sym[13] == 'G' &&
      sym[14] == '_' &&
      sym[15] == 'Z' &&
      sym[16] == '_';
   if (is_VG_Z_prefixed) {
      vg_assert2(0, "symbol with a 'VG_Z_' prefix: %s.\n"
                    "see pub_tool_redir.h for an explanation.", sym);
   }

   /* Now scan the Z-encoded soname. */
   i = 12;
   while (True) {

      if (sym[i] == '_')
      /* Found the delimiter.  Move on to the fnname loop. */
         break;

      if (sym[i] == 0) {
         error = True;
         goto out;
      }

      if (sym[i] != 'Z') {
         EMITSO(sym[i]);
         i++;
         continue;
      }

      /* We've got a Z-escape. */
      i++;
      switch (sym[i]) {
         case 'a': EMITSO('*'); break;
         case 'c': EMITSO(':'); break;
         case 'd': EMITSO('.'); break;
         case 'h': EMITSO('-'); break;
         case 'p': EMITSO('+'); break;
         case 's': EMITSO(' '); break;
         case 'u': EMITSO('_'); break;
         case 'A': EMITSO('@'); break;
         case 'D': EMITSO('$'); break;
         case 'L': EMITSO('('); break;
         case 'R': EMITSO(')'); break;
         case 'S': EMITSO('/'); break;
         case 'Z': EMITSO('Z'); break;
         default: error = True; goto out;
      }
      i++;
   }

   vg_assert(sym[i] == '_');
   i++;

   /* Now deal with the function name part. */
   if (!fn_is_encoded) {

      /* simple; just copy. */
      while (True) {
         if (sym[i] == 0)
            break;
         EMITFN(sym[i]);
         i++;
      }
      goto out;

   }

   /* else use a Z-decoding loop like with soname */
   while (True) {

      if (sym[i] == 0)
         break;

      if (sym[i] != 'Z') {
         EMITFN(sym[i]);
         i++;
         continue;
      }

      /* We've got a Z-escape. */
      i++;
      switch (sym[i]) {
         case 'a': EMITFN('*'); break;
         case 'c': EMITFN(':'); break;
         case 'd': EMITFN('.'); break;
         case 'h': EMITFN('-'); break;
         case 'p': EMITFN('+'); break;
         case 's': EMITFN(' '); break;
         case 'u': EMITFN('_'); break;
         case 'A': EMITFN('@'); break;
         case 'D': EMITFN('$'); break;
         case 'L': EMITFN('('); break;
         case 'R': EMITFN(')'); break;
         case 'Z': EMITFN('Z'); break;
         default: error = True; goto out;
      }
      i++;
   }

  out:
   EMITSO(0);
   EMITFN(0);

   if (error) {
      /* Something's wrong.  Give up. */
      VG_(message)(Vg_UserMsg,
                   "m_demangle: error Z-demangling: %s\n", sym);
      return False;
   }

   return True;
}
Beispiel #12
0
// Insert element e into the non-empty AVL tree t.
// Returns True if the depth of the tree has grown.
static Bool avl_insert(AvlTree* t, AvlNode* n)
{
   Word cmpres = cmp_key_root(t, n);

   if (cmpres < 0) {
      // Insert into the left subtree.
      if (t->root->left) {
         // Only need to set the used fields in the subtree.
         AvlTree left_subtree;
         left_subtree.root   = t->root->left;
         left_subtree.cmp    = t->cmp;
         left_subtree.keyOff = t->keyOff;
         if (avl_insert(&left_subtree, n)) {
             switch (t->root->balance--) {
             case 1: return False;
             case 0: return True;
             }
             if (t->root->left->balance < 0) {
                avl_swr(&(t->root));
                t->root->balance = 0;
                t->root->right->balance = 0;
             } else {
                avl_swl(&(t->root->left));
                avl_swr(&(t->root));
                avl_nasty(t->root);
             }
         } else {
            t->root->left=left_subtree.root;
         }
         return False;
      } else {
         t->root->left = n;
         if (t->root->balance--) return False;
         return True;
      }

   } else if (cmpres > 0) {
      // Insert into the right subtree
      if (t->root->right) {
         // Only need to set the used fields in the subtree.
         AvlTree right_subtree;
         right_subtree.root   = t->root->right;
         right_subtree.cmp    = t->cmp;
         right_subtree.keyOff = t->keyOff;
         if (avl_insert(&right_subtree, n)) {
            switch (t->root->balance++) {
            case -1: return False;
            case  0: return True;
            }
            if (t->root->right->balance > 0) {
               avl_swl(&(t->root));
               t->root->balance = 0;
               t->root->left->balance = 0;
            } else {
               avl_swr(&(t->root->right));
               avl_swl(&(t->root));
               avl_nasty(t->root);
            }
         } else {
            t->root->right=right_subtree.root;
         }
         return False;
      } else {
         t->root->right = n;
         if (t->root->balance++) return False;
         return True;
      }

   } else {
      vg_assert2(0, "OSet_Insert: duplicate element added");
   }
}
Beispiel #13
0
Bool VG_(maybe_Z_demangle) ( const HChar* sym,
                             /*OUT*/HChar* so, Int soLen,
                             /*OUT*/HChar* fn, Int fnLen,
                             /*OUT*/Bool* isWrap )
{
#  define EMITSO(ch)                           \
      do {                                     \
         if (so) {                             \
            if (soi >= soLen) {                \
               so[soLen-1] = 0; oflow = True;  \
            } else {                           \
               so[soi++] = ch; so[soi] = 0;    \
            }                                  \
         }                                     \
      } while (0)
#  define EMITFN(ch)                           \
      do {                                     \
         if (fni >= fnLen) {                   \
            fn[fnLen-1] = 0; oflow = True;     \
         } else {                              \
            fn[fni++] = ch; fn[fni] = 0;       \
         }                                     \
      } while (0)

    Bool error, oflow, valid, fn_is_encoded, is_VG_Z_prefixed;
    Int  soi, fni, i;

    vg_assert(soLen > 0 || (soLen == 0 && so == NULL));
    vg_assert(fnLen > 0);
    error = False;
    oflow = False;
    soi = 0;
    fni = 0;

    valid =     sym[0] == '_'
                &&  sym[1] == 'v'
                &&  sym[2] == 'g'
                && (sym[3] == 'r' || sym[3] == 'w' || sym[3] == 'n')
                &&  sym[4] == 'Z'
                && (sym[5] == 'Z' || sym[5] == 'U')
                &&  sym[6] == '_';
    if (!valid)
        return False;

    fn_is_encoded = sym[5] == 'Z';

    if (isWrap)
        *isWrap = sym[3] == 'w';

    /* Now check the soname prefix isn't "VG_Z_", as described in
       pub_tool_redir.h. */
    is_VG_Z_prefixed =
        sym[ 7] == 'V' &&
        sym[ 8] == 'G' &&
        sym[ 9] == '_' &&
        sym[10] == 'Z' &&
        sym[11] == '_';
    if (is_VG_Z_prefixed) {
        vg_assert2(0, "symbol with a 'VG_Z_' prefix: %s.\n"
                   "see pub_tool_redir.h for an explanation.", sym);
    }

    /* Now scan the Z-encoded soname. */
    i = 7;
    while (True) {

        if (sym[i] == '_')
            /* Found the delimiter.  Move on to the fnname loop. */
            break;

        if (sym[i] == 0) {
            error = True;
            goto out;
        }

        if (sym[i] != 'Z') {
            EMITSO(sym[i]);
            i++;
            continue;
        }

        /* We've got a Z-escape. */
        i++;
        switch (sym[i]) {
        case 'a':
            EMITSO('*');
            break;
        case 'c':
            EMITSO(':');
            break;
        case 'd':
            EMITSO('.');
            break;
        case 'h':
            EMITSO('-');
            break;
        case 'p':
            EMITSO('+');
            break;
        case 's':
            EMITSO(' ');
            break;
        case 'u':
            EMITSO('_');
            break;
        case 'A':
            EMITSO('@');
            break;
        case 'D':
            EMITSO('$');
            break;
        case 'L':
            EMITSO('(');
            break;
        case 'R':
            EMITSO(')');
            break;
        case 'Z':
            EMITSO('Z');
            break;
        default:
            error = True;
            goto out;
        }
        i++;
    }

    vg_assert(sym[i] == '_');
    i++;

    /* Now deal with the function name part. */
    if (!fn_is_encoded) {

        /* simple; just copy. */
        while (True) {
            if (sym[i] == 0)
                break;
            EMITFN(sym[i]);
            i++;
        }
        goto out;

    }

    /* else use a Z-decoding loop like with soname */
    while (True) {

        if (sym[i] == 0)
            break;

        if (sym[i] != 'Z') {
            EMITFN(sym[i]);
            i++;
            continue;
        }

        /* We've got a Z-escape. */
        i++;
        switch (sym[i]) {
        case 'a':
            EMITFN('*');
            break;
        case 'c':
            EMITFN(':');
            break;
        case 'd':
            EMITFN('.');
            break;
        case 'h':
            EMITFN('-');
            break;
        case 'p':
            EMITFN('+');
            break;
        case 's':
            EMITFN(' ');
            break;
        case 'u':
            EMITFN('_');
            break;
        case 'A':
            EMITFN('@');
            break;
        case 'D':
            EMITFN('$');
            break;
        case 'L':
            EMITFN('(');
            break;
        case 'R':
            EMITFN(')');
            break;
        case 'Z':
            EMITFN('Z');
            break;
        default:
            error = True;
            goto out;
        }
        i++;
    }

out:
    EMITSO(0);
    EMITFN(0);

    if (error) {
        /* Something's wrong.  Give up. */
        VG_(message)(Vg_UserMsg,
                     "m_demangle: error Z-demangling: %s\n", sym);
        return False;
    }
    if (oflow) {
        /* It didn't fit.  Give up. */
        VG_(message)(Vg_UserMsg,
                     "m_demangle: oflow Z-demangling: %s\n", sym);
        return False;
    }

    return True;
}