(* Import a C header file. * Copyright (C) 2008 Red Hat Inc., Richard W.M. Jones * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * $Id$ *) open Printf open ExtList open ExtString open Cil module P = Bitmatch_persistent let (//) = Filename.concat let () = (* Parse command line arguments. *) let debug = ref false in let save_temps = ref false in let version () = printf "bitmatch-import-c %s" Bitmatch.version; exit 1 in let cpp_args = ref [] in let cpp_arg2 name value = cpp_args := (name ^ value) :: !cpp_args in let argspec = Arg.align [ "--debug", Arg.Set debug, " Debug messages"; "--version", Arg.Unit version, " Display version and exit"; "-save-temps", Arg.Set save_temps, " Save temporary files"; "-I", Arg.String (cpp_arg2 "-I"), "dir Specify extra include directory for cpp"; "-D", Arg.String (cpp_arg2 "-D"), "name=value Define value in cpp"; "-U", Arg.String (cpp_arg2 "-U"), "name Undefine value in cpp"; ] in let input_file = ref None in let anon_fun str = match !input_file with | None -> input_file := Some str | Some _ -> eprintf "bitmatch-import-c: only give a single input file\n"; exit 1 in let usage_msg = "\ bitmatch-import-c: Import C structures and constants and generate bitmatching functions from them. Please see the manual page bitmatch-import-c(1) for more information. OPTIONS" in Arg.parse argspec anon_fun usage_msg; let debug = !debug in let save_temps = !save_temps in let input_file = match !input_file with | Some f -> f | None -> eprintf "bitmatch-import-c: no input file specified\n"; exit 1 in let cpp_args = List.rev !cpp_args in (* Grab the file and pass it to the preprocessor, and then read the * C code into memory using CIL. *) msvcMode := false; Cil.initCIL (); (* XXX Unavoidable tmp exploit here. Fix? *) let tmp, delete_tmp = if not save_temps then ( let tmp = Filename.temp_file (Filename.temp_dir_name) ".i" in tmp, fun () -> try Unix.unlink tmp with Unix.Unix_error _ -> () ) else ( let tmp = Filename.chop_extension input_file ^ ".i" in tmp, fun () -> (* -save-temps, so do nothing *) () ) in let cmd = sprintf "cpp %s -I %s -include bitmatch-import-prefix.h %s > %s" (String.concat " " (List.map Filename.quote cpp_args)) (Filename.quote (Bitmatch_config.ocamllibdir // "bitmatch")) (Filename.quote input_file) (Filename.quote tmp) in if debug then prerr_endline cmd; if Sys.command cmd <> 0 then ( eprintf "%s: command failed\n" cmd; delete_tmp (); exit 1 ); (* Why does Frontc.parse return a continuation ...? *) let file = (Frontc.parse tmp) () in delete_tmp (); (* Find out which structures, #defines, etc. are to be imported. * (cf. the macros in bitmatch-import-prefix.h) *) let constants = List.filter_map ( function | GVar ({vname = vname; vtype = vtype}, { init = Some (SingleInit vinit) }, loc) when String.starts_with vname "__bitmatch_constant_" -> let vname = String.sub vname 20 (String.length vname - 20) in (* Do constant folding on the initializer and then calculate * its compile-time value. *) let vinit = match isInteger (constFold true vinit) with | Some i -> i | None -> Errormsg.error "%a: non-constant initializer: %a" d_loc loc d_exp vinit; -1L in Some (vname, vinit, loc) | _ -> None ) file.globals in let structs = List.filter_map ( function | GType ({tname = tname; ttype = ttype}, loc) when String.starts_with tname "__bitmatch_import_" -> let tname = String.sub tname 18 (String.length tname - 18) in Some (tname, ttype, loc) | _ -> None ) file.globals in if !Errormsg.hadErrors then exit 1; (* If debugging, print out the imports. *) if debug then ( List.iter ( fun (vname, vinit, loc) -> Errormsg.log "%a: import %s as constant 0x%LX\n" d_loc loc vname vinit; ) constants; List.iter ( fun (tname, ttype, loc) -> Errormsg.log "%a: import %s as %a\n" d_loc loc tname d_type ttype; ) structs; ); (* (* Output constants. * XXX Disabled at the moment until we work out where to put them XXX *) List.iter ( fun (vname, vinit, loc) -> printf "let %s = 0x%LX\n" vname vinit ) constants; *) (* Output structures. *) List.iter ( fun (tname, ttype, loc) -> (* Uncomment the next line if you want to really print the * complete CIL structure of the type (for debugging etc.). * The ASTs printed here are usually quite large. *) (*Errormsg.log "%a: %s %a\n" d_loc loc tname d_plaintype ttype;*) (* Recursive function to generate a persistent pattern from a * C struct or union. Quite a few limitations at the moment: * (1) Structure elements must be in order. * (2) Doesn't really work with unions [XXX]. * * Parameters: * ?names List of names of parent structs. Used in the * recursive case for nested structs. * ?offset Offset of struct within parent, usually NoOffset. Used * in the recursive case for nested structs. * ?endian Inherited endianness, usually None. Used for C * __attribute__((bitwise)). * ttype CIL type of struct. * Returns: * pattern A bitmatch persistent pattern. *) let rec pattern_of_struct ?(names=[]) ?(offset=NoOffset) ?(endian=None) ttype = match ttype with (* Some types contain attributes to indicate their * endianness. See many examples from . *) | (TNamed ({ tname = tname; ttype = TNamed (_, attrs) }, _) as t) when hasAttribute "bitwise" attrs -> let endian = if String.starts_with tname "__le" then Some Bitmatch.LittleEndian else if String.starts_with tname "__be" then Some Bitmatch.BigEndian else ( Errormsg.warn "%a: unknown bitwise attribute typename: %s\n" d_loc loc tname; endian ) in pattern_of_struct ~names ~offset ~endian (unrollType t) (* See into named types. *) | (TNamed _ as t) -> pattern_of_struct ~names ~offset ~endian (unrollType t) (* struct or union *) | TComp ({ cdefined = true; cfields = cfields }, _) -> let cfields = List.map ( fun ({ fname = fname; ftype = ftype } as finfo) -> let offset = Field (finfo, offset) in let names = fname :: names in pattern_of_struct ~names ~offset ~endian ftype ) cfields in List.flatten cfields (* int array with constant length *) | TArray (basetype, (Some _ as len), _) when isIntegralType basetype -> let nr_elems = lenOfArray len in let bitsoffset, totalwidth = bitsOffset ttype offset in let bitswidth = totalwidth / nr_elems (* of the element *) in let basetype = unrollType basetype in let ikind = match basetype with | TInt (ikind, _) -> ikind | t -> Errormsg.unimp "%a: unhandled type: %a" d_loc loc d_type t; IInt in let fname = String.concat "_" (List.rev names) in (* If the base type is 8 bits then we always translate this to * a string (whether the C type is signed or unsigned). There * is no endianness in bytes so ignore that. *) if bitswidth = 8 then [pattern_field_of_string fname bitsoffset nr_elems] else ( (* XXX Realistically we don't handle arrays well at * the moment. Perhaps we should give up and match * this to a bitstring? *) let signed = isSigned ikind in if debug then eprintf "--> array %s: nr_elems=%d signed=%b\n" fname nr_elems signed; [] (* XXX *) ) (* basic integer type *) | TInt (ikind, _) -> let bitsoffset, bitswidth = bitsOffset ttype offset in (*if debug then ( let name = String.concat "." (List.rev names) in Errormsg.log "%s: int: %d, %d\n" name bitsoffset bitswidth );*) let fname = String.concat "_" (List.rev names) in let field = pattern_field_of_int fname bitsoffset bitswidth ikind endian in [field] (* a pointer - in this mapping we assume this is an address * (endianness and wordsize come from function parameters), * in other words we DON'T try to follow pointers, we just * note that they are there. *) | TPtr _ -> let bitsoffset, bitswidth = bitsOffset ttype offset in let fname = String.concat "_" (List.rev names) in if debug then eprintf "--> pointer %s: bitsoffset=%d bitswidth=%d\n" fname bitsoffset bitswidth; [] (* XXX *) | t -> Errormsg.warn "pattern_of_struct: %a: unhandled type: %a" d_loc loc d_type t; [] (* Convert a single int field into a pattern field. * Could be a bitfield, byte, short, etc. *) and pattern_field_of_int fname bitsoffset bitswidth ikind endian = let signed = isSigned ikind in let _loc = camlp4_loc_of_cil_loc loc in let field = P.create_pattern_field _loc in let field = P.set_lident_patt field fname in let field = P.set_type_int field in let field = P.set_length_int field bitswidth in let field = P.set_offset_int field bitsoffset in let field = P.set_signed field signed in let field = match endian with | Some endian -> P.set_endian field endian | None -> P.set_endian field Bitmatch.NativeEndian in field and pattern_field_of_string fname bitsoffset nr_elems = let _loc = camlp4_loc_of_cil_loc loc in let field = P.create_pattern_field _loc in let field = P.set_lident_patt field fname in let field = P.set_type_string field in let field = P.set_length_int field (nr_elems*8) in let field = P.set_offset_int field bitsoffset in field (* Convert a CIL location into a camlp4 location. Grrr these * should be compatible! *) and camlp4_loc_of_cil_loc loc = let _loc = Camlp4.PreCast.Syntax.Ast.Loc.mk loc.file in Camlp4.PreCast.Syntax.Ast.Loc.move_line loc.line _loc in (* Match on the type of this structure, and from it generate * a single parsing function. *) match ttype with (* struct or union *) | TComp ({ cdefined = true; cname = cname }, _) -> let pattern = pattern_of_struct ttype in let named_pattern = cname, P.Pattern pattern in P.named_to_channel stdout named_pattern (* An undefined struct or union -- means one which was only ever * defined with 'struct foo;'. This is an error. *) | TComp ({ cdefined = false; cname = cname }, _) -> Errormsg.error "%a: struct or union has no definition: %s" d_loc loc cname (* Types which are not allowed, eg. void, int, arrays. *) | TVoid _ | TInt _ | TFloat _ | TPtr _ | TArray _ | TFun _ | TNamed _ | TBuiltin_va_list _ -> Errormsg.error "%a: not a struct or union: %a" d_loc loc d_type ttype (* Types which we might implement in the future. * For enum we should probably split out enums separately * from structs above, since enums are more like constants. *) | TEnum ({ ename = ename }, _) -> Errormsg.unimp "%a: %a" d_loc loc d_type ttype ) structs; if !Errormsg.hadErrors then exit 1; exit 0