1 (* Import a C header file.
2 * Copyright (C) 2008 Red Hat Inc., Richard W.M. Jones
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
28 (* Parse command line arguments. *)
29 let debug = ref false in
30 let save_temps = ref false in
32 printf "bitmatch-import-c %s" Bitmatch.version;
35 let cpp_args = ref [] in
36 let cpp_arg2 name value =
37 cpp_args := (name ^ value) :: !cpp_args
40 let argspec = Arg.align [
41 "--debug", Arg.Set debug,
43 "--version", Arg.Unit version,
44 " Display version and exit";
45 "-save-temps", Arg.Set save_temps,
46 " Save temporary files";
47 "-I", Arg.String (cpp_arg2 "-I"),
48 "dir Specify extra include directory for cpp";
49 "-D", Arg.String (cpp_arg2 "-D"),
50 "name=value Define value in cpp";
51 "-U", Arg.String (cpp_arg2 "-U"),
52 "name Undefine value in cpp";
55 let input_file = ref None in
57 match !input_file with
58 | None -> input_file := Some str
60 eprintf "bitmatch-import-c: only give a single input file\n";
65 bitmatch-import-c: Import C structures and constants and
66 generate bitmatching functions from them. Please see the
67 manual page bitmatch-import-c(1) for more information.
71 Arg.parse argspec anon_fun usage_msg;
74 let save_temps = !save_temps in
76 match !input_file with
79 eprintf "bitmatch-import-c: no input file specified\n";
81 let cpp_args = List.rev !cpp_args in
83 (* Grab the file and pass it to the preprocessor, and then read the
84 * C code into memory using CIL.
89 (* XXX Unavoidable tmp exploit here. Fix? *)
91 if not save_temps then (
92 let tmp = Filename.temp_file (Filename.temp_dir_name) ".i" in
93 tmp, fun () -> try Unix.unlink tmp with Unix.Unix_error _ -> ()
95 let tmp = Filename.chop_extension input_file ^ ".i" in
96 tmp, fun () -> (* -save-temps, so do nothing *) ()
100 sprintf "cpp %s -include bitmatch-import-prefix.h %s > %s"
101 (String.concat " " (List.map Filename.quote cpp_args))
102 (Filename.quote input_file) (Filename.quote tmp) in
103 if debug then prerr_endline cmd;
104 if Sys.command cmd <> 0 then (
105 eprintf "%s: command failed\n" cmd;
110 (* Why does Frontc.parse return a continuation ...? *)
111 let file = (Frontc.parse tmp) () in
114 (* Find out which structures, #defines, etc. are to be imported.
115 * (cf. the macros in bitmatch-import-prefix.h)
120 | GVar ({vname = vname; vtype = vtype},
121 { init = Some (SingleInit vinit) },
123 when String.starts_with vname "__bitmatch_constant_" ->
124 let vname = String.sub vname 20 (String.length vname - 20) in
126 (* Do constant folding on the initializer and then calculate
127 * its compile-time value.
130 match isInteger (constFold true vinit) with
134 "%a: non-constant initializer: %a" d_loc loc d_exp vinit;
137 Some (vname, vinit, loc)
143 | GType ({tname = tname; ttype = ttype}, loc)
144 when String.starts_with tname "__bitmatch_import_" ->
145 let tname = String.sub tname 18 (String.length tname - 18) in
146 Some (tname, ttype, loc)
150 if !Errormsg.hadErrors then exit 1;
152 (* If debugging, print out the imports. *)
155 fun (vname, vinit, loc) ->
156 Errormsg.log "%a: import %s as constant 0x%LX\n" d_loc loc vname vinit;
159 fun (tname, ttype, loc) ->
160 Errormsg.log "%a: import %s as %a\n" d_loc loc tname d_type ttype;
164 (* Output constants. *)
166 fun (vname, vinit, loc) ->
167 printf "let %s = 0x%LX\n" vname vinit
170 (* Output structures. *)
172 fun (tname, ttype, loc) ->
173 (* Uncomment the next line if you want to really print the
174 * complete CIL structure of the type (for debugging etc.).
175 * The ASTs printed here are usually quite large.
177 (*Errormsg.log "%a: %s %a\n" d_loc loc tname d_plaintype ttype;*)
179 (* Match on the type of this structure, and from it generate
180 * a single parsing function.
183 (* struct or union *)
184 | TComp ({ cdefined = true; cname = cname }, _) ->
185 printf "let %s_of_bitstring bits =\n" tname;
186 printf " bitmatch bits with\n";
188 (*output_struct [] NoOffset None ttype;*)
190 printf " Some (...)\n";
191 printf " | { _ } -> None\n\n"
193 (* An undefined struct or union -- means one which was only ever
194 * defined with 'struct foo;'. This is an error.
196 | TComp ({ cdefined = false; cname = cname }, _) ->
198 "%a: struct or union has no definition: %s" d_loc loc cname
200 (* Types which are not allowed, eg. void, int, arrays. *)
201 | TVoid _ | TInt _ | TFloat _ | TPtr _ | TArray _ | TFun _
202 | TNamed _ | TBuiltin_va_list _ ->
204 "%a: not a struct or union: %a" d_loc loc d_type ttype
206 (* Types which we might implement in the future.
207 * For enum we should probably split out enums separately
208 * from structs above, since enums are more like constants.
210 | TEnum ({ ename = ename }, _) ->
211 Errormsg.unimp "%a: %a" d_loc loc d_type ttype
213 let rec to_fields names offset endian = function
214 (* Some types contain attributes to indicate their
215 * endianness. See many examples from <linux/types.h>.
217 | (TNamed ({ tname = tname;
218 ttype = TNamed (_, attrs) },
220 when hasAttribute "bitwise" attrs ->
222 if String.starts_with tname "__le" then
223 Some Bitmatch.LittleEndian
224 else if String.starts_with tname "__be" then
225 Some Bitmatch.BigEndian
227 Errormsg.warn "%a: unknown bitwise attribute typename: %s\n"
231 to_fields names offset endian (unrollType t)
233 (* See into named types. *)
235 to_fields names offset endian (unrollType t)
237 (* struct or union *)
238 | TComp ({ cdefined = true; cfields = cfields }, _) ->
241 fun ({ fname = fname; ftype = ftype } as finfo) ->
242 let offset = Field (finfo, offset) in
243 let names = fname :: names in
244 to_fields names offset endian ftype
248 (* int array with constant length *)
249 | TArray (basetype, (Some _ as len), _)
250 when isIntegralType basetype ->
251 let len = lenOfArray len in
252 let bitsoffset, totalwidth = bitsOffset ttype offset in
253 let bitswidth = totalwidth / len (* of the element *) in
255 let name = String.concat "." (List.rev names) in
256 Errormsg.log "%s: int array: %d, %d, len %d\n"
257 name bitsoffset bitswidth len
259 let basetype = unrollType basetype in
262 | TInt (ikind, _) -> ikind
264 Errormsg.unimp "%a: unhandled type: %a" d_loc loc d_type t;
267 to_int_field "" bitsoffset bitswidth ikind endian in
268 let fname = String.concat "_" (List.rev names) in
269 let byteoffset = bitsoffset lsr 3 in
270 let bytetotalwidth = totalwidth lsr 3 in
271 printf "--> array %s: byteoffset=%d bytetotalwidth=%d len=%d\n"
272 fname byteoffset bytetotalwidth len (* field *);
275 (* basic integer type *)
277 let bitsoffset, bitswidth = bitsOffset ttype offset in
279 let name = String.concat "." (List.rev names) in
280 Errormsg.log "%s: int: %d, %d\n" name bitsoffset bitswidth
282 let fname = String.concat "_" (List.rev names) in
284 to_int_field fname bitsoffset bitswidth ikind endian in
287 (* a pointer - in this mapping we assume this is an address
288 * (endianness and wordsize come from function parameters),
289 * in other words we DON'T try to follow pointers, we just
290 * note that they are there.
293 let bitsoffset, bitswidth = bitsOffset ttype offset in
294 let fname = String.concat "_" (List.rev names) in
295 printf "--> pointer %s: bitsoffset=%d bitswidth=%d\n"
296 fname bitsoffset bitswidth;
300 Errormsg.unimp "to_fields: %a: unhandled type: %a"
304 and to_int_field fname bitsoffset bitswidth ikind endian =
305 let byteoffset = bitsoffset lsr 3 in
306 let bytewidth = bitswidth lsr 3 in
307 let signed = isSigned ikind in
309 if bitsoffset land 7 = 0 && bitswidth land 7 = 0 then (
310 (* Not a bitfield. *)
313 printf "--> byte %s: byteoffset=%d bytewidth=%d signed=%b\n"
314 fname byteoffset bytewidth signed
316 printf "--> short %s: byteoffset=%d bytewidth=%d signed=%b endian=%s\n"
317 fname byteoffset bytewidth signed (Option.map_default Bitmatch.string_of_endian "None" endian)
319 printf "--> int %s: byteoffset=%d bytewidth=%d signed=%b endian=%s\n"
320 fname byteoffset bytewidth signed (Option.map_default Bitmatch.string_of_endian "None" endian)
322 printf "--> long %s: byteoffset=%d bytewidth=%d signed=%b endian=%s\n"
323 fname byteoffset bytewidth signed (Option.map_default Bitmatch.string_of_endian "None" endian)
325 Errormsg.unimp "%s: unhandled integer width: %d bits"
328 (* It's a bitfield if either the offset or width isn't
331 let bitsoffset = bitsoffset land 7 in
332 printf "--> bitfield %s: byteoffset=%d bytewidth=%d signed=%b endian=%s bitsoffset=%d bitswidth=%d\n"
333 fname byteoffset bytewidth
334 signed (Option.map_default Bitmatch.string_of_endian "None" endian) bitsoffset bitswidth
340 if !Errormsg.hadErrors then exit 1;