通過PDB檔案實現非嵌入式的c 反射

2021-09-25 08:29:19 字數 2721 閱讀 9743

首先,為了在c++中實現反射系統,我認為需要解決以下兩個問題:

(1)根據乙個給定符號,獲取符號對應的位址資訊。

(2)根據位址資訊,能夠對其進行相應操作。

對(2)需要再說明的是:為了能夠對位址指向的物件進行操作,需要一些用於描述這個物件的最基本資訊(比如物件的型別),而這些資訊就是物件的meta資訊。有了meta資訊,我們才能正確的操作物件。

不同型別的物件的meta資訊應該是不同的:

(1)對於簡單的資料成員(int、double、struct等),meta資訊應該包括資料所佔記憶體的布局。通過記憶體布局和記憶體位址,我們可以對其進行get/set操作。

(2)對於函式,meta資訊應該有呼叫慣例(calling conventions)、引數、返回值等資訊。通過這些資訊我們能對函式進行呼叫操作。

(3)對於類,meta資訊可以認為是前兩種的組合。應該包含方法表(函式集合)和資料成員集合。

暫時不考慮剩餘的其他型別,如模板、巨集等。

我以此實現了乙個反射系統的demo。基本做法是,通過windows的dia(debug inte***ce access)api讀取程式的pdb檔案。以此來獲得乙個符號的位址資訊,以及這個符號的基本meta資訊。這樣可以做到根據乙個位址,對資料進行讀寫的操作。但是對函式來說似乎還不夠。為了成功呼叫函式,我們還需要根據函式的呼叫慣例,正確的把引數入棧、並從指定位置獲取返回值等。libffi就是專門幹這事的,但是可惜的是它並不支援msvc:-(

不過好在我熟悉python,知道python也有ctype這個模組來處理ffi(foreign function inte***ce)。把它的原始碼稍作剪裁應該就可以拿來用了。這部分工作挺順利的,瀏覽了一下ctype的實現,發現只需要改非常少的幾處即可。cpython的libffi_msvc在此處:d

目前demo功能比較簡陋,只實現了對資料物件的get/set,以及呼叫全域性的函式。不過對於其他複雜的物件的反射,基本上參考這兩個功能就可以輕鬆的實現了。

具體的使用如下**所示:

1、引導程式對應的pdb檔案

creflmgr refl_mgr;

//從pdb檔案載入反射資訊

refl_mgr.loaddatafromfile(g_wszpdbfilename);

2、對基本資料的get/set

//

get instance data member

creflobject *refl_obj_bar_c;

refl_mgr.getreflobjectfromparent(refl_obj_bar, l"c

", &refl_obj_bar_c);

creflvalue refl_val_bar_c;

refl_mgr.getclassinstdatamember(&bar, refl_obj_bar_c, &refl_val_bar_c);

printf(

"%d\n

", refl_val_bar_c.m_data.m_i8);

//set instance data member

creflobject *refl_obj_bar_i;

refl_mgr.getreflobjectfromparent(refl_obj_bar, l"i

", &refl_obj_bar_i);

creflvalue refl_val_bar_i;

refl_val_bar_i.m_data.m_i32 = 1024

;refl_mgr.setclassinstdatamember(&bar, refl_obj_bar_i, &refl_val_bar_i);

printf(

"%d\n

", bar.i);

3、呼叫函式

//

函式creflobject *refl_obj_func1;

refl_mgr.getreflobjectfromglobal(l

"testcallfunc1

", &refl_obj_func1);

refl_mgr.printreflobject(refl_obj_func1);

std::vector

vargs;

ffi_obj arg1;

arg1.type =ffi_type_sint32;

arg1.value.i32 = 2014

;vargs.push_back(arg1);

ffi_obj arg2;

arg2.type =ffi_type_pointer;

arg2.value.p = (void*)"

hello world";

vargs.push_back(arg2);

ffi_obj ret;

ret.type =ffi_type_void;

refl_mgr.callreflobject(refl_obj_func1, vargs, ret);

當然demo是不完整的,目前我認為還有就幾個未解決的比較重要的問題是:

(1)處理對函式的不正確的呼叫。比如傳入了錯誤的引數、返回值型別指定錯了等。這部分cpython的libffi_msvc也沒有做好,它做的僅僅是在windows下檢測函式呼叫前後堆疊暫存器的位置是否一致,詳見此處。但是僅有這個檢測是不夠的,它並不能有效的防止崩潰問題。我認為最好能做到能對引數、返回值進行型別檢查,如果發現錯誤能列印錯誤,並且不進行函式的呼叫操作。

(2)如何在linux gcc環境下實現。

非嵌入式與嵌入式的區別

非嵌入式是通過軟體控制硬體,軟硬體之間直接聯絡來實現要求。但是一旦硬體發生改變軟體也要改變,為了降低這種偶合度過高的問題,出現了嵌入式。嵌入式在軟體和硬體之間新增了作業系統,軟體通過控制作業系統進而控制硬體,硬體發生改變並不會導致軟體也發生改變,這為軟體開發人員節約了很多時間,並且嵌入式能在已有的硬...

嵌入式 檔案IO的學習

用 實現linux中cp複製的功能 include stdio.h include unistd.h include fcntl.h include string.h intmain int argc,char ar int argc是記錄你在命令列敲入的字串個數,char ar 是存放你寫入是字串...

Tinyxml的嵌入式Linux實現

這兩天專案需要在嵌入式linux系統上實現tinyxml解析簡單的xml檔案,於是在網上查詢並收集了一點資料,現在補充完善!版本 tinyxml 2 6 2.zip 首先修改 makefile檔案 1 將 cxx g 改為 cxx arm linux g cc arm linux gcc ld ar...