none
Adjustor Thunk

    Question

  • Hi
    Can anyone tell me what is the adjustor thunk?

    class A
    {
    public:
    	   A()
    	   {
    		   a = 0;
    		   a1 = 1;
    	   }
    	   virtual void Print()
    	   {
    		   cout<<" a = "<<a<<endl;
    	   }
    
    protected:
    	      int a;
    		  int a1;	
    		  int a2;
    };
    
    class B
    {
    public:
    	B()
    	{
    		b = 0;
    	}
    	virtual void Print()
    	{
    		cout<<" b = "<<b<<endl;
    	}
    
    	virtual void Check()
    	{
              
    	}
    
    protected:
    	      int b;
    			 	   
    };
    
    class C : public A,public B
    {
    public:
    	   C()
    	   {
    		   c = 0;
    	   }
    	   virtual void Print()
    	   {
    			cout<<" c = "<<c<<endl;
    	   }
    	   virtual void Check()
    		{
              
    		}
    private:
    	    int c;
    };
    
    void main()
    {
    	C obj;
    
    	A* aPtr = &obj;
    	aPtr->Print();
    	B* bPtr = &obj;
    	
    }
    In the above example, When i explode the obj, I could find the adjustor thunk in the first entry of the vtable of class B.

    I want to know under which circumstances adjustor thunk is being inserted in the memory layout of the object?
    Already i have started a thread on this. But i was not able to be get a convincing answer
    Monday, January 11, 2010 10:24 AM

Answers

  • Syed Babu wrote:
    > So in my original case the print function was expecting the A*.
     
    Yes, that's one way to look at it. Again, in that example, A* and C* actually refer to the same address, so you may say the function expects an A* or a C* - makes no difference at the assembly level.

    > So when this is being fixed. I mean during the compile time is it fixed that the print function is expecting A*
     
    Yes.

    > If i change the derivation order like
    >
    > class
    >  C : public
    >  B,public
    >  A
    >
    > The print function will be expecting the B*. Am i right?
     
    Correct. And the A subobject's vtable would contain an adjustor thunk.
    --
    Igor Tandetnik
    • Marked as answer by Syed Babu Tuesday, January 12, 2010 2:52 PM
    Tuesday, January 12, 2010 1:10 PM

All replies

  • Googled .
    Microsoft MVP - Visual C++
    Blog: http://nibuthomas.com
    Posts are provided as is without warranties or guaranties.
    Monday, January 11, 2010 11:14 AM
    Moderator
  • There was an old MSDN article "C++ Under the Hood" by Jan Gray. I found a copy at following link: http://www.openrce.org/articles/files/jangrayhood.pdf
    Monday, January 11, 2010 11:48 AM
  • Syed Babu wrote:
    > Can anyone tell me what is the adjustor thunk?
    >
    > In the above example, When i explode the obj, I could find the adjustor thunk in the first entry of the vtable of class B.
     
    B is located at some non-zero offset inside C. You can print &obj and bPtr and see for yourself - they refer to different addresses.
     
    When you call bPtr->Print(), the compiler passes bPtr pointer - a B* pointer to a B subobject inside C - as a hidden 'this' parameter.
    But C::Print implementation needs C* pointer. To make this work, there's a special vtable for B, where B::Print entry points to a small fragment of code that adjusts 'this' pointer to point a few bytes back (to switch from B* to C* pointing to an enclosing C object), then jumps to C::Print. This fragment of code is the adjustor thunk. C's constructor makes the vtable pointer in its B subobject to point to this special vtable.
    --
    Igor Tandetnik
    Monday, January 11, 2010 12:55 PM
  • Hi Igor
    Thanks for your reply. Your explanation raised another question in me.... What you say is if the print function is called using pointer to B , the B pointer has to be converted to C* [ ie., hidden pointer which is nothing but this ]. Then how about the following case...

    class A
    {
    public:
    	   A()
    	   {
    		   a = 0;
    		   a1 = 1;
    	   }
    	   virtual void Print()
    	   {
    		   cout<<" a = "<<a<<endl;
    	   }
    
    protected:
    	      int a;
    		  int a1;	
    		  int a2;
    };
    
    class B
    {
    public:
    	B()
    	{
    		b = 0;
    	}
    	virtual void Check()
    	{
               cout<<" Base Check function is called"<<endl;
    	}
    
    protected:
    	      int b;
    			 	   
    };
    
    class C : public A,public B
    {
    public:
    	   C()
    	   {
    		   c = 0;
    	   }
    	   virtual void Print()
    	   {
    		cout<<" c = "<<c<<endl;
    	   }
    	  virtual void Check()
    	  {
                   cout<<" Derived Check function is called"<<endl;
    	  }
    
    private:
    	    int c;
    };
    
    void main()
    {
    	C obj;
    
    	B* bPtr = &obj;
    	bPtr->Check();
    
    }
    

    Here also Check function is called using pointer to B where as the check function expects const C* as a this pointer. So B* pointer has to be converted into C*. Why in this case adjuster thunk is missing. Without adjuster thunk how the B* is converted to C*????



    Monday, January 11, 2010 1:46 PM
  • Syed Babu wrote:
    > Thanks for your reply. Your explanation raised another question in me.... What you say is if the print function is called using
    > pointer to B , the B pointer has to be converted to C* [ ie., hidden pointer which is nothing but this ]. Then how about the
    > following case... 
    >
    [snip]
    >
    > Here also Check function is called using pointer to B where as the check function expects const C* as a this pointer. So B*
    > pointer has to be converted into C*. Why in this case adjuster thunk is missing. Without adjuster thunk how the B* is converted
    > to C*???? 
     
    Looking at the disassembly, here's what happens. The 'this' pointer adjustment is actually inside C::Check implementation - basically, C::Check expects a B* pointer and automatically fixes it to a C*. So when you call bPtr->Check(), no adjustment needs to be made. On the other hand, if you write obj.Check(), the adjustment *the other way* is made at the call site, so that C::Check still gets its B* pointer and adjusts it back.
     
    In your original example, the compiler couldn't do the same trick with Print and had to resort to adjuster thunk because C::Print implemented both A::Print and B::Print and couldn't know whether the incoming 'this' pointer was A* or B*. However, since A is located at offset 0 in C, A* and C* are actually the same address, so only B* needs to be adjusted.
    --
    Igor Tandetnik
    Monday, January 11, 2010 2:10 PM
  • Thanks igor
    Give me sometime to reply back I'l go through the code and you explanation once again
    Monday, January 11, 2010 2:39 PM
  • Hi Igor
    You have mentioned that "C::Check expects a B* pointer". Is it true?
    Does it not required a pointer to C*?
    So you mean to say if there is a virtual function and if it is overridden in derived class, the derived class function will always expect base pointer [ as it is derived from base ] and internally the derived class function will adjust the base class pointer to the derived class pointer..
    Correct me if i'm wrong...
    Tuesday, January 12, 2010 5:26 AM
  • Syed Babu wrote:
    > You have mentioned that "C::Check expects a B* pointer". Is it true?
     
    Yes.

    > Does it not required a pointer to C*?
     
    No.
     
    I don't mean that it expects B* at the high, syntactic level. Of course when you write code, you have to have a C* pointer on which to call the method. But at the assembly level, it assumes that the address passed in ECX is not the address of the first byte of C object, but the address of a B subobject inside C. This is just an implementation detail you are not supposed to care about.

    > So you mean to say if there is a virtual function and if it is overridden in derived class, the derived class function will
    > always expect base pointer
     
    Apparently, yes. Note that in the most common case - that of single inheritance - the base class pointer and the derived class pointer are one and the same as the base class subobject is positioned at offset 0 in the derived class object.
    --
    Igor Tandetnik
    Tuesday, January 12, 2010 6:00 AM
  • So in my original case the print function was expecting the A*.
    So when this is being fixed. I mean during the compile time is it fixed that the print function is expecting A*

    If i change the derivation order like
    class
     C : public
     B,public
     A
    The print function will be expecting the B*. Am i right?

    Tuesday, January 12, 2010 6:15 AM
  • Syed Babu wrote:
    > So in my original case the print function was expecting the A*.
     
    Yes, that's one way to look at it. Again, in that example, A* and C* actually refer to the same address, so you may say the function expects an A* or a C* - makes no difference at the assembly level.

    > So when this is being fixed. I mean during the compile time is it fixed that the print function is expecting A*
     
    Yes.

    > If i change the derivation order like
    >
    > class
    >  C : public
    >  B,public
    >  A
    >
    > The print function will be expecting the B*. Am i right?
     
    Correct. And the A subobject's vtable would contain an adjustor thunk.
    --
    Igor Tandetnik
    • Marked as answer by Syed Babu Tuesday, January 12, 2010 2:52 PM
    Tuesday, January 12, 2010 1:10 PM
  • Thanks igor
    The answer looks convincing.
    Thanks once again if you dont mind can i know your email id so than in case of doubt i can ping you directly
    Tuesday, January 12, 2010 2:34 PM
  • Syed Babu wrote:
    > Thanks once again if you dont mind can i know your email id so than in case of doubt i can ping you directly
     
    I'd rather you post here. This way, others would also benefit from any discussion that ensues.
    --
    Igor Tandetnik
    Tuesday, January 12, 2010 2:56 PM
  • Ok Thanks igor.
    Can you tell me something about you like where do you work n what kind of work you do etc.,
    Tuesday, January 12, 2010 2:58 PM
  • Syed Babu wrote:
    > Can you tell me something about you like where do you work n what kind of work you do etc.,
     
    No offense, but why do you care? If I thought it were relevant, I'd put this information into my profile. I didn't.
    --
    Igor Tandetnik
    Tuesday, January 12, 2010 3:05 PM
  • No problem
    Wednesday, January 13, 2010 4:16 AM