Search code examples
cassemblyx86-64valgrindinline-assembly

What does this inline-assembly code snippet in Valgrind mean?


What does this mean in Valgrind's VALGRIND_DO_CLIENT_REQUEST_EXPR?

__asm__ volatile (
  __SPECIAL_INSTRUCTION_PREAMBLE
  /* %RDX = client_request ( %RAX ) */        
  "xchgq %rbx, %rbx"
  : "=d" (_zzq_result)
  : "a" (&_zzq_args[0]), "0" (_zzq_default)
  : "cc", "memory"
)

Where __SPECIAL_INSTRUCTION_PREAMBLE is defined as:

#define __SPECIAL_INSTRUCTION_PREAMBLE    \
  "rolq $3,  %%rdi ; rolq $13, %%rdi\n\t" \
  "rolq $61, %%rdi ; rolq $51, %%rdi\n\t"

Seems like some black magic.


Solution

  • There is no way for a 'client' executable to interact with the Valgrind host directly. The client and host have separate memory spaces. Unlike tools such as Purify and the Sanitizers Valgrind can't provide any C functions for clients to be able to introspect any information from the Valgrind host.

    The client request mechanism uses a short asm preamble as a marker for the Valgrind host. This is a sequence of instructions that has no side effects and is also a sequence that no compiler will ever emit. It would be a big problem if ever compilers generated code that Valgrind incorrrectly saw as a client request. The amd64 example that you gave uses 4 left rotates of RDI with a total of 128 bits, leaving it unchanged. That's followed by an exchange of RBX with itself. Other CPU architectures use similar 'no-op' sequences to signal client requests.

    Since these instructions do nothing they can be compiled into release builds with only a very small size and runtime overhead.

    Here is the comment in the Valgrind source that explains these special instructions (in VEX/priv/guest_amd64_toIR.c - in Valgrind we use 'guest' and 'client' interchangeably).

    
    /* "Special" instructions.
    
       This instruction decoder can decode three special instructions
       which mean nothing natively (are no-ops as far as regs/mem are
       concerned) but have meaning for supporting Valgrind.  A special
       instruction is flagged by the 16-byte preamble 48C1C703 48C1C70D
       48C1C73D 48C1C733 (in the standard interpretation, that means: rolq
       $3, %rdi; rolq $13, %rdi; rolq $61, %rdi; rolq $51, %rdi).
       Following that, one of the following 3 are allowed (standard
       interpretation in parentheses):
    
          4887DB (xchgq %rbx,%rbx)   %RDX = client_request ( %RAX )
          4887C9 (xchgq %rcx,%rcx)   %RAX = guest_NRADDR
          4887D2 (xchgq %rdx,%rdx)   call-noredir *%RAX
          4887F6 (xchgq %rdi,%rdi)   IR injection
    
       Any other bytes following the 16-byte preamble are illegal and
       constitute a failure in instruction decoding.  This all assumes
       that the preamble will never occur except in specific code
       fragments designed for Valgrind to catch.
    
       No prefixes may precede a "Special" instruction.
    */
    

    and the corrrespongding code

       /* Spot "Special" instructions (see comment at top of file). */
       {
          const UChar* code = guest_code + delta;
          /* Spot the 16-byte preamble:
             48C1C703   rolq $3,  %rdi
             48C1C70D   rolq $13, %rdi
             48C1C73D   rolq $61, %rdi
             48C1C733   rolq $51, %rdi
          */
          if (code[ 0] == 0x48 && code[ 1] == 0xC1 && code[ 2] == 0xC7 
                                                   && code[ 3] == 0x03 &&
              code[ 4] == 0x48 && code[ 5] == 0xC1 && code[ 6] == 0xC7 
                                                   && code[ 7] == 0x0D &&
              code[ 8] == 0x48 && code[ 9] == 0xC1 && code[10] == 0xC7 
                                                   && code[11] == 0x3D &&
              code[12] == 0x48 && code[13] == 0xC1 && code[14] == 0xC7 
                                                   && code[15] == 0x33) {
             /* Got a "Special" instruction preamble.  Which one is it? */
             if (code[16] == 0x48 && code[17] == 0x87 
                                  && code[18] == 0xDB /* xchgq %rbx,%rbx */) {
                /* %RDX = client_request ( %RAX ) */
                DIP("%%rdx = client_request ( %%rax )\n");
                delta += 19;
                jmp_lit(&dres, Ijk_ClientReq, guest_RIP_bbstart+delta);
                vassert(dres.whatNext == Dis_StopHere);
                goto decode_success;
             }
             else