我對C++中THUNK一種實現技術的分析 (轉)

worldblog發表於2007-12-14
我對C++中THUNK一種實現技術的分析 (轉)[@more@]

我對C++THUNK一種實現技術的分析:namespace prefix = o ns = "urn:schemas--com::office" />

KEY S:C++ THUNK

 

在網際網路上看到這樣一段程式碼,有些網友不知其然,我簡單的把它分析一下。

 

#pragma pack(push,1)

// structure to store the machine code

struct Thunk

{

  char  m_jmp;  // op code of jmp instruction

  unsigned long  m_relproc;  // relative jmp

};

#pragma pack(pop)

 

//This type of structure can contain then thunk code, which can be executed on the fly. Let's take a look at the simple case in which we are going to execute our required function by thunk.

 

//Program 77

#include

#include <.h>

using namespace std;

class C;

C* g_pC = NULL;

typedef void(*pFUN)();

 

class C

{

public:

  Thunk  m_thunk;

  //virtual void g(){};

  void Init(pFUN pFun, void* pThis)

  {

  // op code of jump instruction

  m_thunk.m_jmp = 0xe9;

  // address of the appripriate function

m_thunk.m_relproc = (int)pFun - ((int)this+sizeof(Thunk));

  FlushInstructionCache(GetCurrentProcess(),

  &m_thunk, sizeof(m_thunk));

  }

  // this is cour call back function

  static void CallBackFun()

  {

  C* pC = g_pC;

  // initilize the thunk

  pC->Init(StaticFun, pC);

  // get the address of thunk code

  pFUN pFun = (pFUN)&(pC->m_thunk);

  // start executing thunk code which will call StaticFun

  pFun();

  cout << "C::CallBackFun" << endl;

  }

  static void StaticFun()

  {

  cout << "C::StaticFun" << endl;

  }

};

int main()

{

  C objC;

  g_pC = &objC;

  C::CallBackFun();

  return 0;

}

 

C++在實現多重繼承中,要求有一種機制來實現對this指標的動態修改,比較好的一種實現技術就是採用THUNK技術,通常它是由一小段ASM所完成的實現地址轉變和的程式。上面這段程式碼採用C++語言實現THUNK技術。

 

大家都知道,在調到記憶體中後:

由低到高把記憶體分為:

低------0

佔用區

程式碼區

全程資料區

堆區

棧區

DLL區[有DLL自己的堆疊等]

....

沒有佔用區

高-----4GB

 

當程式執行時,函式

static void StaticFun()

  {

   cout << "C::StaticFun" << endl;

  }

在記憶體的中adress已經確定了,我們假設它為 addr_fun_staticfun

當main函式到

C objC;

 

時,就為objC在資料區->棧內分配一個地址為 addr_data_objC ,長度為sizeof(C)的記憶體塊

至少這段塊記憶體在main函式執行其間是固定不變的。因此我們可知全程指標 g_pC ,以及static void CallBackFun()中的C* pC的值都是addr_data_objC[記得它是棧內],現在我們看pC->Init(StaticFun, pC);函式內部:

void Init(pFUN pFun, void* pThis)

  {

  m_thunk.m_jmp = 0xe9;  //先不管

  m_thunk.m_relproc = (int)pFun - ((int)this+sizeof(Thunk));

  FlushInstructionCache(GetCurrentProcess(),

   &m_thunk, sizeof(m_thunk));

  }

 

由C類宣告可知,C類objC記憶體佈局中只有 Thunk  m_thunk;一個資料成員 。連vptr也沒有。

所以我們得出結果 &objC.m_thunk = addr_data_objC  = (int)this

 

  m_thunk.m_relproc = (int)pFun - ((int)this+sizeof(Thunk));一句

的.m_relproc的值應為addr_fun_staticfun – (addr_data_objC + sizeof(Thunk)) [ 中sizeof(Thunk) = 5]

 

FlushInstructionCache(GetCurrentProcess(), &m_thunk, sizeof(m_thunk));

一句的作用,是填指令緩衝區 ,將記憶體地址為 &objC.m_thunk 處起大小為 sizeof(m_thunk)填充到指令緩衝[我覺得這句函式指令是起到的作用,沒有什麼必要性]。

 

這樣,可以理解為在函式Init內部把在記憶體地址起始為&objC.m_thunk = addr_data_objC 起[在資料棧內] sizeof(thunk)一段資料設定為

m_thunk.m_jmp = 0xe9; 

m_thunk.m_relproc = (int)pFun - ((int)this+sizeof(Thunk));

而這部分資料的二進位制資料從程式碼角度講是起跳轉到addr_fun_staticfun的作用。

 

如果在C類中位於m_thunk之前宣告一個成員或增加類虛擬成員函式,那會讓(int)this <> &m_thunk,所以程式碼不能正常工作[這是建立在類物件記憶體佈局按用一範圍(Private,Public,pro..)成員變數宣告ORDER分配的基礎上],如果想要程式碼正常工作,就要在

m_thunk.m_relproc = (int)pFun - ((int)this+sizeof(Thunk));

處作出相當的修改:

m_thunk.m_relproc = (int)pFun - ((int)this+sizeof(Thunk)+SIZEOF(增加的變數));

 

請大家指正。


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/10752043/viewspace-993130/,如需轉載,請註明出處,否則將追究法律責任。

相關文章