6 Using CIL
There are two predominant ways to use CIL to write a program analysis or
transformation. The first is to phrase your analysis as a module that is
called by our existing driver. The second is to use CIL as a stand-alone
library. We highly recommend that you use cilly, our driver.
6.1 Using cilly, the CIL driver
The most common way to use CIL is to write an Ocaml module containing your
analysis and transformation, which you then link into our boilerplate
driver application called cilly. cilly is a Perl script that
processes and mimics GCC and MSVC command-line arguments and then
calls cilly.byte.exe or cilly.asm.exe (CIL's Ocaml executable).
An example of such module is logwrites.ml, a transformation that is
distributed with CIL and whose purpose is to instrument code to print the
addresses of memory locations being written. (We plan to release a
C-language interface to CIL so that you can write your analyses in C
instead of Ocaml.) See Section 9 for a survey of other example
modules.
Assuming that you have written logwrites.ml, here is how you use it:
Modify logwrites.ml so that it includes a CIL ``feature
descriptor'' like this:
let feature : featureDescr =
{ fd_name = "logwrites";
fd_enabled = Util.logWrites;
fd_description = "generation of code to log memory writes";
fd_extraopt = [];
fd_doit =
(function (f: file) ->
let lwVisitor = new logWriteVisitor in
visitCilFileSameGlobals lwVisitor f)
}
The fd_name field names the feature and its associated command-line
arguments. The fd_enabled field is a bool ref in a module that
both maincil.ml and logwrites.ml can reference. util.ml is a
good enough choice.
When the user passes the --dologwrites
command-line option to cilly, the variable associated with the
fd_enabled flag is set and the fd_doit function is called
on the Cil.file that represents the merger (see Section 14) of
all C files listed as arguments.
- Put logwrites.ml in the src or src/ext directory. This
will make sure that make can find it. If you want to put it in some
other directory modify the Makefile and add to SOURCEDIRS your
directory.
- Modify the Makefile and add your module to the MODULES
variable. The order of the modules matters. Add your modules somewhere after
cil and before maincil.
- Modify maincil.ml so that your new feature descriptor appears in
the global list of CIL features.
let features : C.featureDescr list =
[ Logcalls.feature;
Logwrites.feature; (* add this line to include the logwrites feature! *)
Oneret.feature;
Heapify.feature1;
Heapify.feature2;
makeCFGFeature;
Partial.feature;
Simplemem.feature;
]
@ Feature_config.features
Standard code in cilly takes care of adding command-line arguments,
printing the description, and calling your function automatically.
Note: do not worry about introducing new bugs into CIL by adding a single
line to the feature list.
- Now you can invoke the cilly application on a preprocessed file, or
instead use the cilly driver which provides a convenient compiler-like
interface to cilly. See Section 8 for details using cilly.
Remember to enable your analysis by passing the right argument (e.g.,
--dologwrites).
6.2 Using CIL as a library
CIL can also be built as a library that is called from your stand-alone
application. Add cil/src, cil/src/frontc, cil/obj/x86_LINUX
(or cil/obj/x86_WIN32) to your Ocaml project -I include paths.
Building CIL will also build the library cil/obj/*/cil.cma (or
cil/obj/*/cil.cmxa). You can then link your application against that
library.
You can call the Frontc.parse: string -> unit -> Cil.file function with
the name of a file containing the output of the C preprocessor.
The Mergecil.merge: Cil.file list -> string -> Cil.file function merges
multiple files. You can then invoke your analysis function on the resulting
Cil.file data structure. You might want to call
Rmtmps.removeUnusedTemps first to clean up the prototypes and variables
that are not used. Then you can call the function Cil.dumpFile:
cilPrinter -> out_channel -> Cil.file -> unit to print the file to a
given output channel. A good cilPrinter to use is
defaultCilPrinter.
Check out src/maincil.ml and bin/cilly for other good ideas
about high-level file processing. Again, we highly recommend that you just
our cilly driver so that you can avoid spending time re-inventing the
wheel to provide drop-in support for standard makefiles.
Here is a concrete example of compiling and linking your project against
CIL. Imagine that your program analysis or transformation is contained in
the single file main.ml.
$ ocamlopt -c -I $(CIL)/obj/x86_LINUX/ main.ml
$ ocamlopt -o main unix.cmxa \
$(CIL)/obj/x86_LINUX/cil.cmxa main.cmx
The first line compiles your analysis, the second line links it against CIL
(as a library) and the Ocaml Unix library. For more information about
compiling and linking Ocaml programs, see the Ocaml home page
at http://caml.inria.fr/ocaml/.
In the next section we give an overview of the API that you can use
to write your analysis and transformation.