Content

Not quite a Yegge long.

Mock Objects for C++, Part 2

Tuesday 4 November 2008 - Filed under Code

Last time, we looked at how vtables work, and how we can fake them to implement interfaces at runtime. Now we need to figure out how to wire up a fake vtable that actually does what we want, based on expectations the user declares.

Here’s an example of where we’re going. It should be somewhat familiar if you’ve done mock objects in other languages:

ISomeInterface * foo =
    CreateMock<ISomeInterface>();

expect( foo, &ISomeInterface::SomeMethod )
    .once().withArgs( 1, 2, 3 ).returns(42);

// todo: do some stuff that should
// call foo->SomeMethod()

verify( foo );

 

Now the first obvious problem here is that we need to find the vtable index for SomeMethod() on ISomeInterface. Note, this is extremely nonportable – we’re going to exploit knowledge of how Visual C++ implements pointer-to-member and virtual dispatch.

When we take a pointer-to-member on a single-inheritance class (like pretty much any interface), we end up with a 4-byte "plain" function pointer, albeit wrapped in a type that’s required by the standard to be not useful except to call. For your safety, of course.

We’re going to subvert the type system here, and get the raw pointer. I use a blatantly unsafe union-based cast for this purpose. You might want to assert that sizeof(T) == sizeof(void *) to make sure you’re not screwing up – at least then you’ll get a predictable, early error if you somehow manage to use this on a multiple-, virtual-, or unknown-inheritance pointer-to-member.

Next, we’ll look at the code pointed to by this member function pointer. This has a pretty standard form, and there’s one version of it emitted for each vtable index (these thunks are shared across classes, since only the offset changes!)

First, there’s zero or more straightforward jumps. These exist in debug mode, and have symbols associated with them. I can only imagine that they exist to improve debug support, enable edit & continue, easier incremental linking, etc. Very simple to walk over:

e9 o32                jmp o32

Eventually, we get to a virtual dispatcher, which just looks up a constant offset into the current object’s vtable, and jumps to the function pointed to:

8b 44 24 04          mov eax, [esp+4]
8b 00                mov eax, [eax]  ; vtable
ff 60 o8             jmp [eax + o8]

There’s a slight variation for offset 0, since a more efficient encoding exists, but that’s pretty straightforward to deal with.

So, let’s fetch the vtable index, based on a pointer to a member function of the interface:

inline size_t get_function_index( 
    char const * x )
{
    // first, follow linker thunks
    while( *x == (char) 0xe9 )
        x += (*(size_t *)(x+1)) + 5;

    // index, taking into account 0 case
    // (slightly different encoding)
    return (x[7] == 0×60)
        ? (x[8] >> 2) : 0;
}

Now we can get the vtable index of a function, we can start wiring up expectations. Next time.

2008-11-04  »  admin

Talkback

  1. Chris on Software » Blog Archive » Mock Objects for C++, Part 3
    4 November 2008 @ 8:29 pm

    [...] Chris on Software Not quite a Yegge long. « Mock Objects for C++, Part 2 [...]

Share your thoughts

Re: Mock Objects for C++, Part 2







Tags you can use (optional):
<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>