开发者

How does the Visual C++ compiler pass the this ptr to the called function?

开发者 https://www.devze.com 2023-03-22 16:45 出处:网络
I\'m learning C++ using Eckel\'s \"Thinking in C++\". It states the following: If a class contains virtual methods, a virtual function table is created for that class etc. The workings of the functi

I'm learning C++ using Eckel's "Thinking in C++". It states the following:

  • If a class contains virtual methods, a virtual function table is created for that class etc. The workings of the function table are explained roughly. (I know a vtable is not mandatory, but Visual C++ creates one.)
  • The calling object is passed to the called function as an argument. (This might not be true for Visual C++ (or any compiler).) I'm trying to find out how VC++ passes the calling object to the function.

To test both points in Visual C++, I've created the following class (using Visual Studio 2010, WinXP Home 32bit):

ByteExaminer.h:

#pragma once
class ByteExaminer
{
public:
short b[2];
    ByteExaminer(void);
    virtual void f() const;
    virtual void g() const;
    void bruteFG();
};

ByteExaminer.cpp:

#include "StdAfx.h"
#include "ByteExaminer.h"

using namespace std;

ByteExaminer::ByteExaminer(void)
{
    b[0] = 25;
    b[1] = 26;
}

void ByteExaminer::f(void) const
{
    cout << "virtual f(); b[0]: " << hex << b[0] << endl;
}

void ByteExaminer::g(void) const
{
    cout << "virtual g(); b[1]: " << hex << b[1] <开发者_运维知识库< endl;
}

void ByteExaminer::bruteFG(void)
{
    int *mem = reinterpret_cast<int*>(this);
    void (*fg[])(ByteExaminer*) = { (void (*)(ByteExaminer*))(*((int *)*mem)), (void (*)(ByteExaminer*))(*((int *)(*mem + 4))) };
    fg[0](this);
    fg[1](this);
}

The navigation through the vtable in bruteFG() works - when I call fg[0](this), f() is called. What does NOT work, however, is the passing of this to the function - meaning that this->b[0] is not printed correctly (garbage comes out instead. I'm actually lucky this doesn't produce a segfault).

So the actual output for

ByteExaminer be;
be.bruteFG();

is:

virtual f(); b[0]: 1307
virtual g(); b[1]: 0

So how should I proceed to get the correct result? How are the this pointers passed to functions in VC++?

(Nota bene: I'm NOT going to program this way seriously, ever. This is "for the lulz"; or for the learning experience. So don't try to convert me to proper C++ianity :))


Member functions in Visual Studio have a special calling convention, __thiscall, where this is passed in a special register. Which one, I don't recall, but MSDN will say. You will have to go down to assembler if you want to call a function pointer which is in a vtable.

Of course, your code exhibits massively undefined behaviour- it's only OK to alias an object using a char or unsigned char pointer, and definitely not an int pointer- even ignoring the whole vtable assumptions thing.


OK using DeadMG's hint I've found a way without using assembler:

1) Remove the ByteExaminer* arg from the functions in the fg[] array 2) Add a function void callfunc(void (*)()); to ByteExaminer:

void ByteExaminer::callfunc(void (*func)())
{
    func();
}

... this apparently works because func() is the first thing to be used in callfunc, so ecx is apparently not changed before. But this is a dirty trick (as you can see in the code above, I'm always on the hunt for clean code). I'm still looking for better ways.

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号