限制某些C結構以駐留在堆的特定區域中。
當使用C實現編程語言的運行時,我們可能希望區分內部運行時數據結構(使用malloc / free或某種內存池分配或從轉儲圖像中分配了mmap )和分配給GC管理堆中分配的用戶對象。
在Mono中,運行時代表託管對象,這些System.Object使用c struct typedef struct _MonoObject { ... } MonoObject衍生自動。單諾使用該類別從T類中屈服的約定,如果其第一個成員是T類:
struct _MonoString {
MonoObject obj ; /* System.String derives from System.Object */
...
};單MonoObject *ptr必須通過手柄間接訪問運行時內置MonoObjectHandle h指針。
該軟件包提供了一個分析C文件的庫,以找到使用原始指針到託管內存的位置。
這是一個人如何使用Centrinel和Centrinel-Report使用BEAR攔截C編譯器調用的Autotools項目:
# assuming you have GHC and cabal installed
$ pushd $CENTRINEL_DIR # go to the centrinel source directory
$ echo "Building Centrinel"
$ cabal sandbox init
$ cabal install happy
$ cabal install alex
$ cabal install --dependencies-only
$ cabal configure
$ cabal build
$ popd
$ echo Building project using "bear"
$ pushd $SRC_DIR
$ ./autoconf ...
$ bear make
# produces a compile_commands.json file
$ popd
$ cd $CENTRINEL_DIR
$ curl -o centrinel-report.tar.gz https://github.com/lambdageek/centrinel-report/releases/download/v0.3.1/centrinel-report.tar.gz
$ tar xvzf centrinel-report.tar.gz
# writes a skeleton HTML report to centrinel-report/
$ cabal run centrinel -- --project $SRC_DIR/compile_commands.json --exclude $SRC_DIR/thirdparty --format json --output centrinel-report/centrinel-report.json
# produces centrinel-report/centrinel-report.json
$ cd centrinel-report && python -m SimpleHTTPServer 8000
# go look at http://localhost:8000/
這是一個早期開發原型,因此設置有些涉及。
您將需要最近的GHC(使用GHC 8.2.x,GHC 8.0.x和7.10.x)和Cabal進行測試。
依賴項:此程序使用language-c庫進行C解析 - 您將需要安裝alex Lexer和happy Parser(無論是系統範圍內還是在此存儲庫中的沙盒中)。下面的步驟設置了一個沙箱,並將所需的程序安裝到其中。 (您不需要alex , happy進行分析,只是為了構建分析儀二進制。)
git clone https://github.com/lambdageek/centrinel.git
cd centrinel
cabal sandbox init
cabal install happy
cabal install alex
cabal install --dependencies-only
cabal configure
cabal build
cabal run centrinel -- --helpinclude/centrinel.h頭Centrinel依靠一些__attribute__((...))屬性,一些預處理器定義了要分析的內容。為了方便起見,當Centrinel調用C預處理器時,它會自動包含用戶代碼之前的文件include/centrinel.h 。如果從沙盒(如上上述)運行, cabal run centrinel命令將確保Centrinel能夠找到此文件。相反,如果您使用cabal install ,則Centrinel標頭將被複製到Cabal Data目錄,即使您隨後移動二進制,構建的centrinel二進制都將指該位置。
該標頭定義__CENTRINEL__至1 ,以表明Centrinel正在運行; __CENTRINEL_MANAGED_ATTR屬性和__CENTRINEL_MANAGED_REGION屬性指定符,您可以用來註釋您的structs,以及宏來定義language-c不了解的某些GCC原始素。
Centrinel會自動包含標頭,您不必明確包含它。
現在,您應該能夠使用cabal run centrinel -- [ARGS] (或cabal repl來玩耍,如果您想分開運行各種步驟)。此外,還有一個腳本centrinelcc ,可以用作CC make調音,作為gcc或clang的倒入替換。
用法: centrinel [--output FILE] -- CFLAGS CFILE
該程序了解cc命令行選項的雜物,例如-D和-I和-U ,並將將其他人傳遞給預處理器。一些對於預處理沒有意義的選擇(例如-c或-MT可以理解並默默丟棄)。默認情況下,分析結果將打印到Stdout;要將它們引導到文件,請添加一個--output FILE選項。
$ cabal run centrinel -- c-examples/attrib.c
Errors:
c-examples/attrib.c:28: (column 2) [WARNING] >>> Region mismatch: Region 1 and Region 2
Additional locations:
c-examples/attrib.c:8: (column 2)
c-examples/attrib.c:13: (column 1)
c-examples/attrib.c:27: (column 1)
c-examples/attrib.c:35: (column 5) [ERROR] >>> Naked pointer(s) to managed object(s) found in declaration
Pointer to managed heap XP
in 0th argument 'x' at c-examples/attrib.c:35: (column 13)
in function declaration 'foo'
c-examples/attrib.c:37: (column 11) [ERROR] >>> Naked pointer(s) to managed object(s) found in declaration
Pointer to managed heap struct Y *
in return type
in function declaration 'bar'
Pointer to managed heap XP
in 1st argument 'x' at c-examples/attrib.c:37: (column 26)
in function declaration 'bar'
c-examples/attrib.c:39: (column 5) [ERROR] >>> Naked pointer(s) to managed object(s) found in declaration
Pointer to managed heap XP
in 0th argument 'x' at c-examples/attrib.c:33: (column 28)
in type defined at c-examples/attrib.c:33: (column 1)
at c-examples/attrib.c:39: (column 10)
in 0th argument 'f' at c-examples/attrib.c:39: (column 19)
in function declaration 'baz'
注意:如果您需要通過儀表板開頭的選項-並且您使用的是cabal run ,則應該寫作--兩次:一次用於Cabal,一次用於Centrinel: cabal run -- -- -DFOO -I../.. c-examples/attrib.c )
用法: centrinel [--output FILE] --project compile_commands.json
clang編譯數據庫是一個JSON文件,該文件指定了一組編譯器調用。 cmake或BEAR等工具可用於生成編譯數據庫(通常稱為compile_commands.json )。 Centrinel可以將數據庫與--project選項:
$ bear make -C some_project_directory # Note: doesn't work on OSX with System Integrity Protection
$ cabal run centrinel -- --project compile_commands.json
CC文件scripts/centrinelcc可以用作make的CC變量的值。它將首先運行真實的編譯器(由REAL_CC環境變量指定,如果不設置為cc ),如果編譯成功,它將調用centrinel (必須在路徑上)
這些步驟尚未自動化
$ cabal install
$ cp dist/build/centrinel/centrinel [somewhere on your PATH]
$ cp scripts/centrinelcc [somewhere on your PATH]
$ export REAL_CC=/path/to/your/cc # if unset, defaults to 'cc'
$ make CC=centrinelcc
默認情況下,Centrinel將使用REAL_CC環境變量的值來運行預處理器,或者cc如果未設置)。指定編譯器的另一種方法是使用--use-cc CC選項,該選項優先於環境變量。
實驗:使用--format json CENTRINEL將分析結果作為JSON BLOB(目前其格式非常迅速)。斑點可能被消耗
使用--exclude DIR (可以多次指定),將不會分析DIR或其子目錄之一的任何輸入文件。 (既可以使用-- CFLAGS CFILE模式,也可以與--project JSON_FILE一起使用)。
對結構定義的區域註釋(由__attribute__((__region(N)))用整數n )將統一結構的第一個成員(前提是它是另一種結構類型),並將報告衝突。
指向區域__CENTRINEL_MANAGED_REGION中的結構(定義為include/centrinel.h )中的__region(1)如果它們在功能原型中發生的任何位置(聲明或定義),則會引起錯誤。
檢查功能體的使用__CENTRINEL_MANAGED_REGION指針。
一種允許使用__CENTRINEL_SUPPRESS_ATTR(1)錯誤地將指針操縱指針的有福函數/結構的註釋方式,可以應用於:
{
l: __CENTRINEL_SUPPRESS_ATTR ( 1 ) ;
/* your code that manipulated managed objecs */
} int __CENTRINEL_SUPPRESS_ATTR ( 1 ) foo (...); /* no warnings about pointers in ... */__CENTRINEL_SUPPRESS_ATTR(0)可用於在被抑制的上下文中轉回檢查。這對於例如宏觀擴展很有用:您希望宏觀的主體被信任,因此抑制了指針檢查,但是參數不受限制,以便對其進行檢查。1 :一個直接的好處是,如果在運行本機運行時代碼時發生垃圾收集,我們不必固定引用的對象,這可能會減少碎片。 ↩