bic :AC解释器和API Explorer这个项目允许开发人员使用读取的评估打印循环(也称为REPL)探索和测试C-apis。

BIC的运行时间依赖项如下:
要构建BIC,您需要:
请在建造BIC之前确保已安装这些内容。以下命令应将其安装在Debian/Ubuntu系统上:
apt-get安装build-Ender-exental libreadline-dev autoconf-arconf-arpgmp-dev期望flex bison automake m4 libtool pkg-config
您还可以使用以下命令通过MacOS系统上的Homebrew安装所需的依赖项。
啤酒安装野牛flex GMP读取线AutoConf-Archive
您可以使用以下命令编译并安装BIC:
autoreconf -i ./configure-启用 制作 进行安装
对于MacOS系统上的构建,您需要将配置行更改为:
yacc =“ $(Brew -Prefix Bison)/bin/bish -y” ./configure -enable -debug
您可以使用Docker构建和运行以下命令:
docker build -t bic https://github.com/hexagonal-sun/bic.git#master
一旦构建图像,您就可以使用BIC运行:
Docker Run -I BIC
如果您使用的是Arch Linux,则可以从AUR中安装BIC:
是的-s bic
当没有参数的BIC调用BIC时,用户会带有一个替补提示:
bic>
在这里,您可以键入C语句和#include各种系统标头,以提供对系统上不同API的访问。语句可以直接输入重复;无需定义一个函数才能评估它们。说我们希望执行以下C程序:
#include <stdio.h>
int main ()
{
FILE * f = fopen ( "out.txt" , "w" );
fputs ( "Hello, world!n" , f );
return 0 ;
}我们可以使用以下命令在BIC上使用BIC进行此操作:
bic> #include <stdio.h> bic> file *f; f bic> f = fopen(“ test.txt”,“ w”); bic> fputs(“你好,世界! n”,f); 1 bic>
这将导致BIC呼唤C-Library fopen()和fputs()函数创建文件并将Hello World字符串写入其中。如果您现在退出BIC,则应在当前工作目录中看到一个文件test.txt ,其中包含在字符串Hello, Worldn 。
请注意,在评估表达后,BIC将打印评估的结果。这对于测试简单表达式很有用:
BIC> 2 * 8 + fileno(f); 19
您可以使用BIC获取有关通过将其名称前缀为A的任何变量或类型的信息? 。该特殊语法仅在卧底中起作用,但允许您获得有关类型和变量的各种特征。例如:
bic> #include <stdio.h> bic>?stdout Stdout是指向struct _io_file的指针。 Stdout的值为0x7FF1325BC5C0。 sizeof(stdout)= 8个字节。 Stdout在:/usr/include/stdio.h:138上宣布。
当重元启动时,BIC将查看是否存在~/.bic 。如果这样做会自动评估,并且REPL使用所得的环境。这对于定义常用的函数或变量很有用。例如,说我们的~/.bic文件包含:
#include <stdio.h>
int increment ( int a )
{
return a + 1 ;
}
puts ( "Good morning, Dave." );当我们启动depl时,我们会得到:
$ bic 早上好,戴夫。 BIC>增量(2); 3
如果将bic源文件与-s一起传递,则作为命令行参数,它将通过调用main()函数来对其进行评估。例如,假设我们具有包含以下内容的文件test.c :
#include <stdio.h>
int factorial ( int n )
{
if (! n )
{
return 1 ;
}
return n * factorial ( n - 1 );
}
int main ()
{
printf ( "Factorial of 4 is: %dn" , factorial ( 4 ));
return 0 ;
}然后,我们可以通过-s test.c调用BIC来评估它:
$ bic -s test.c 4的阶乘是:24
如果您希望将论证传递到C文件,请将其附加到BIC的命令行。一旦BIC处理了-s参数,所有其他参数都被视为要将其传递给程序的参数。这些参数是作为argc和argv变量创建的,并传递到main() 。 argv[0]的值是BIC执行的C文件的名称。考虑以下C程序:
#include <stdio.h>
int main ( int argc , char * argv [])
{
for ( int i = 0 ; i < argc ; i ++ )
printf ( "argv[%d] = %sn" , i , argv [ i ]);
return 0 ;
}如果我们没有任何论点:
$ bic -s test.c argv [0] = test.c
鉴于我们用更多的论点调用BIC,则将其传递给该计划:
$ bic -s test.c -a foo -s bar abc argv [0] = test.c argv [1] = -a argv [2] = foo argv [3] = -s argv [4] = bar argv [5] = a argv [6] = b argv [7] = c
您也可以使用特殊表达式: <REPL>;在您的源代码中,使BIC在文件评估中的特定点将您置于REPL:

您可以使用BIC探索LIBC以外的其他库的API。假设我们希望探索Capstone库,我们通过-l选项,在启动时将其库进行biC负载。例如:

请注意,当BIC打印复合数据类型( struct或union )时,它会显示所有成员名称及其相应值。
BIC实现的核心是tree对象。这些是通用对象,可用于表示整个程序以及当前的评估者状态。它在tree.h和tree.c中实现。每种树类型均在c.lang中定义。 c.lang文件是类似于LISP的规范:
T_ADD 。Addition 。tADD 。LHS和RHS 。使用上述属性集创建对象的代码将是:
( deftype T_ADD " Addition " " tADD "
( " LHS " " RHS " ))定义后,我们可以以下面的方式在我们的C代码中使用此对象:
tree make_increment ( tree number )
{
tree add = tree_make ( T_ADD );
tADD_LHS ( add ) = number ;
tADD_RHS ( add ) = tree_make_const_int ( 1 );
return add ;
}请注意,已经生成了一组访问器宏, tADD_LHS()和tADD_RHS() ,以供我们访问其他属性插槽。当在编译过程中设置--enable-debug时,这些宏中的每个宏都会扩展到检查中,以确保在设置对象的tADD_LHS属性时,该对象确实是T_ADD的实例。
c.lang文件由许多生成代码段的源代码编译器读取。这些公用事业包括:
gentype :生成树对象类型的列表。gentree :生成一个包含树对象的所有属性数据的结构。genctypes :生成C型树对象的列表 - 这些代表C中的基本数据类型。genaccess :生成树对象属性的访问宏。gengc :为每个树对象生成标记函数,这允许垃圾收集器穿越对象树。gendump :生成代码以递归倒入树对象。gendot :为给定的tree层次结构生成一个点文件,从而使其可视化。Lexer&Parser的输出是tree对象层次结构,然后将其传递到评估器( evaluator.c )中。然后,评估者将递归评估每个树元素,更新内部评估器状态,从而执行程序。
评估器外部功能的呼叫以平台依赖性方式处理。当前,X86_64和AARCH64是唯一受支持的平台,处理此问题的代码分别在x86_64和aarch64文件夹中。这是通过从评估器中获取函数调用tree对象(由T_FN_CALL代表)的函数调用树对象,并对所有参数进行了评估并将其编码为简单的链接列表。然后将其在汇编中遍历,以根据X86_64或AARCH64调用contentions将值移至正确的寄存器中,然后分支到函数地址。
解析器和Lexer分别在parser.m4和lex.m4中实现。通过M4后,输出是两个野牛解析器和两个弯曲词。
两个解析器的原因是,c repl的语法与c文件的语法大不相同。例如,我们希望用户能够在REPP上评估语句,而无需将其包装在功能中。不幸的是,编写一个函数外部的语句是无效的。因此,我们不希望用户能够在c文件中编写裸语句。为了实现这一目标,我们有两套不同的语法规则,它们产生了两个解析器。大多数语法规则都重叠,因此我们使用单个M4文件来照顾差异。