限制某些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 :一个直接的好处是,如果在运行本机运行时代码时发生垃圾收集,我们不必固定引用的对象,这可能会减少碎片。 ↩