Newer
Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
(****************************************************************************)
(* *)
(* Copyright (C) 2001-2003 *)
(* George C. Necula <necula@cs.berkeley.edu> *)
(* Scott McPeak <smcpeak@cs.berkeley.edu> *)
(* Wes Weimer <weimer@cs.berkeley.edu> *)
(* Ben Liblit <liblit@cs.berkeley.edu> *)
(* All rights reserved. *)
(* *)
(* Redistribution and use in source and binary forms, with or without *)
(* modification, are permitted provided that the following conditions *)
(* are met: *)
(* *)
(* 1. Redistributions of source code must retain the above copyright *)
(* notice, this list of conditions and the following disclaimer. *)
(* *)
(* 2. Redistributions in binary form must reproduce the above copyright *)
(* notice, this list of conditions and the following disclaimer in the *)
(* documentation and/or other materials provided with the distribution. *)
(* *)
(* 3. The names of the contributors may not be used to endorse or *)
(* promote products derived from this software without specific prior *)
(* written permission. *)
(* *)
(* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS *)
(* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT *)
(* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS *)
(* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE *)
(* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, *)
(* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, *)
(* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; *)
(* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER *)
(* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT *)
(* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN *)
(* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE *)
(* POSSIBILITY OF SUCH DAMAGE. *)
(* *)
(* File modified by CEA (Commissariat à l'énergie atomique et aux *)
(* énergies alternatives) *)
(* and INRIA (Institut National de Recherche en Informatique *)
(* et Automatique). *)
(****************************************************************************)
(* Modified by TrustInSoft *)
(* Type check and elaborate ABS to CIL *)
(* The references to ISO means ANSI/ISO 9899-1999 *)
module H = Hashtbl
module IH = Datatype.Int.Hashtbl
open Pretty_utils
open Cabs
open Cabshelper
open Cil
let valid_sid = false
(* All statements generated here must have an invalid sid. Use this variable
for the valid_sid label of Cil.mkStmt*. *)
open Cil_types
open Cil_datatype
(* Maps the start and end positions of a function declaration or definition
(including its possible contract) to its name. *)
module FuncLocs = struct
include
State_builder.List_ref
(Datatype.Triple
(Cil_datatype.Position)(Cil_datatype.Position)(Datatype.String)
)
(struct
let name = "FuncLocs"
let dependencies = [ Kernel.Files.self ]
end)
let add_loc ?spec loc1 loc2 funcname =
let startpos =
match spec with
| None -> fst loc1
| Some (_, spec_loc) -> fst spec_loc
in
let endpos = snd loc2 in
add (startpos, endpos, funcname)
end
let func_locs () = FuncLocs.get ()
(* Attributes which are entirely unsupported by Frama-C and must cause a
parsing error, since their behavior requires non-standard parsing *)
let unsupported_attributes = ["vector_size"]
(* Attributes which must be erased to avoid issues (e.g., GCC 'malloc'
attributes can refer to erased functions and make the code un-reparsable *)
let erased_attributes = ["malloc"]
(fun a -> Ast_attributes.register_attribute a AttrIgnored)
let stripUnderscore s =
if String.length s = 1 then begin
if s = "_" then
Kernel.error ~once:true ~current:true "Invalid attribute name %s" s;
s
end else begin
let res = Extlib.strip_underscore s in
if res = "" then
Kernel.error ~once:true ~current:true "Invalid attribute name %s" s;
if List.mem res unsupported_attributes then
Kernel.error ~current:true "unsupported attribute: %s" s
else begin
if not (Ast_attributes.is_known_attribute res) then
Kernel.warning
~once:true ~current:true ~wkey:Kernel.wkey_unknown_attribute
"Unknown attribute: %s" s
let frama_c_keep_block = "FRAMA_C_KEEP_BLOCK"
let () = Cil_printer.register_shallow_attribute frama_c_keep_block
let () = Ast_attributes.register_attribute frama_c_keep_block AttrStmt
let fc_stdlib = "fc_stdlib"
let fc_stdlib_generated = "fc_stdlib_generated"
let () = Cil_printer.register_shallow_attribute fc_stdlib
let () = Cil_printer.register_shallow_attribute fc_stdlib_generated
(* fc_stdlib attribute already registered in cil.ml *)
let () = Ast_attributes.register_attribute fc_stdlib_generated (AttrName false)
let fc_local_static = "fc_local_static"
let () = Cil_printer.register_shallow_attribute fc_local_static
let () = Ast_attributes.register_attribute fc_local_static (AttrName false)
let frama_c_destructor = "fc_destructor"
let () = Cil_printer.register_shallow_attribute frama_c_destructor
let () = Ast_attributes.register_attribute frama_c_destructor (AttrName false)
(* packed and aligned are treated separately, we ignore them
during standard processing.
*)
Ast_attributes.register_attribute "packed" AttrIgnored;
Ast_attributes.register_attribute "aligned" AttrIgnored;
Ast_attributes.register_attribute "warn_unused_result" (AttrFunType false);
Ast_attributes.register_attribute "FC_OLDSTYLEPROTO" (AttrName false);
Ast_attributes.register_attribute "static" (AttrName false);
Ast_attributes.register_attribute "missingproto" (AttrName false);
Ast_attributes.register_attribute "dummy" AttrIgnored;
Ast_attributes.register_attribute "signal" AttrIgnored; (* AVR-specific attribute *)
Ast_attributes.register_attribute "leaf" AttrIgnored;
Ast_attributes.register_attribute "nonnull" AttrIgnored;
Ast_attributes.register_attribute "deprecated" AttrIgnored;
Ast_attributes.register_attribute "access" AttrIgnored;
Ast_attributes.register_attribute "returns_twice" AttrIgnored;
Ast_attributes.register_attribute "pure" AttrIgnored;
Ast_attributes.register_attribute "cleanup" AttrIgnored;
Ast_attributes.register_attribute "warning" AttrIgnored;
(** A hook into the code that creates temporary local vars. By default this
is the identity function, but you can overwrite it if you need to change the
types of cabs2cil-introduced temp variables. *)
let typeForInsertedVar: (Cil_types.typ -> Cil_types.typ) ref = ref (fun t -> t)
let cabs_exp loc node = { expr_loc = loc; expr_node = node }
let abort_context ?loc msg =
let loc = match loc with
| None -> Current_loc.get()
let append fmt =
Format.pp_print_newline fmt ();
Virgile Prevosto
committed
Errorloc.pp_context_from_file fmt loc
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
in
Kernel.abort ~current:true ~append msg
module IgnorePureExpHook =
Hook.Build (struct type t = string * Cil_types.exp end)
let register_ignore_pure_exp_hook f =
IgnorePureExpHook.extend (fun (x,z) -> f x z)
module ImplicitPrototypeHook =
Hook.Build (struct type t = varinfo end)
let register_implicit_prototype_hook f = ImplicitPrototypeHook.extend f
module DifferentDeclHook =
Hook.Build(struct type t = varinfo * varinfo end)
let register_different_decl_hook f =
DifferentDeclHook.extend (fun (x,y) -> f x y)
module NewGlobalHook = Hook.Build(struct type t = varinfo * bool end)
let register_new_global_hook f = NewGlobalHook.extend (fun (x, y) -> f x y)
module LocalFuncHook = Hook.Build(struct type t = varinfo end)
let register_local_func_hook = LocalFuncHook.extend
module IgnoreSideEffectHook =
Hook.Build(struct type t = Cabs.expression * Cil_types.exp end)
let register_ignore_side_effect_hook f =
IgnoreSideEffectHook.extend (fun (y,z) -> f y z)
module ConditionalSideEffectHook =
Hook.Build(struct type t = Cabs.expression * Cabs.expression end)
module ForLoopHook =
Hook.Build(struct
type t =
Cabs.for_clause * Cabs.expression * Cabs.expression * Cabs.statement
end)
let register_for_loop_all_hook f =
ForLoopHook.extend (fun (x,y,z,t) -> f x y z t)
let register_for_loop_init_hook f =
ForLoopHook.extend (fun (x,_,_,_) -> f x)
let register_for_loop_test_hook f =
ForLoopHook.extend (fun (_,x,_,_) -> f x)
let register_for_loop_incr_hook f =
ForLoopHook.extend (fun (_,_,x,_) -> f x)
let register_for_loop_body_hook f =
ForLoopHook.extend (fun (_,_,_,x) -> f x)
let register_conditional_side_effect_hook f =
ConditionalSideEffectHook.extend (fun (y,z) -> f y z)
(* These symbols are supposed to be macros. It is not possible to
take their address or to redeclare them outside of the proper header
in stdlib. See CERT MSC38-C rule.
*)
let no_suppress_function_macro =
[ "assert"; "setjmp"; "va_arg"; "va_copy"; "va_end"; "va_start" ]
let no_redefine_macro =
"errno" :: "math_errhandling" :: no_suppress_function_macro
let is_stdlib_function_macro n = List.mem n no_suppress_function_macro
let is_stdlib_macro n = List.mem n no_redefine_macro
let is_bitwise_bop = function
| Cabs.BAND | Cabs.BOR | Cabs.XOR -> true
| _ -> false
let is_relational_bop = function
| EQ | NE | LT | GT | LE | GE -> true
| _ -> false
let rec stripParen e =
match e with
| { expr_node = Cabs.PAREN e } -> stripParen e
| e -> e
let is_for_builtin builtin info =
match info with
| SINGLE_INIT { expr_node = VARIABLE name } ->
String.equal ("__fc_" ^ builtin) name
| _ -> false
let rec is_dangerous_offset = function
NoOffset -> false
| Field (fi, o) ->
Cil.typeHasAttribute "volatile" (Cil.unrollType fi.ftype) ||
is_dangerous_offset o
| Index _ -> true
let rec is_dangerous e = match e.enode with
| Lval lv | AddrOf lv | StartOf lv -> is_dangerous_lval lv
| UnOp (_,e,_) | CastE(_,e) -> is_dangerous e
| BinOp(_,e1,e2,_) -> is_dangerous e1 || is_dangerous e2
| Const _ | SizeOf _ | SizeOfE _ | SizeOfStr _ | AlignOf _ | AlignOfE _ ->
false
and is_dangerous_lval = function
| Var v,_ when
(not v.vglob && not v.vformal && not v.vtemp)
|| Ast_attributes.has_attribute "volatile" v.vattr
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
|| Cil.typeHasAttribute "volatile" (Cil.unrollType v.vtype)
-> true
(* Local might be uninitialized, which will trigger UB,
but we assume that the variables we generate are correctly initialized.
*)
| Var _, o -> is_dangerous_offset o
| Mem _,_ -> true
class check_no_locals = object
inherit nopCilVisitor
method! vlval (h,_) =
(match h with
| Var v ->
if not v.vglob then
Kernel.error ~once:true ~current:true
"Forbidden access to local variable %a in static initializer"
Cil_printer.pp_varinfo v
| _ -> ());
DoChildren
end
let rec check_no_locals_in_initializer i =
match i with
| SingleInit e ->
ignore (visitCilExpr (new check_no_locals) e)
| CompoundInit (ct, initl) ->
foldLeftCompound ~implicit:false
~doinit:(fun _off' i' _ () ->
check_no_locals_in_initializer i')
~ct:ct
~initl:initl
~acc:()
(* ---------- source error message handling ------------- *)
let cabslu s =
{Cil_datatype.Position.unknown with
Filepath.pos_path = Datatype.Filepath.of_string ("Cabs2cil_start" ^ s)},
{Cil_datatype.Position.unknown with
Filepath.pos_path = Datatype.Filepath.of_string ("Cabs2cil_end" ^ s)}
(** Keep a list of the variable ID for the variables that were created to
* hold the result of function calls *)
let callTempVars: unit IH.t = IH.create 13
(* Check that s starts with the prefix p *)
let prefix p s =
let lp = String.length p in
let ls = String.length s in
lp <= ls && String.sub s 0 lp = p
(***** PROCESS PRAGMAS **********)
(* fc_stdlib pragma. Delimits blocks of globals that are declared in
a given std lib header. By default, they will not be pretty-printed by
frama-c -print, which will emit #include "header.h" instead
*)
let current_stdheader = ref []
let pop_stdheader () =
match !current_stdheader with
| s::l ->
Kernel.debug ~dkey:Kernel.dkey_typing_pragma "Popping %s %s" fc_stdlib s;
current_stdheader := l
| [] ->
Kernel.warning ~current:true
"#pragma %s pop does not match a push" fc_stdlib
let push_stdheader s =
Kernel.debug ~dkey:Kernel.dkey_typing_pragma "Pushing %s %s@." fc_stdlib s;
current_stdheader := s::!current_stdheader
(* Returns the topmost (latest) header that is not internal to Frama-C,
unless it is the only one.
This prevents the pretty-printing function from including Frama-C
internal files, unless they were directly specified by the user. *)
let get_current_stdheader () =
let rec aux = function
| [] -> ""
| [ s ] -> s
| s :: l when String.starts_with ~prefix:"__fc_" s -> aux l
| s :: _ -> s
in
aux !current_stdheader
(* there are several pragmas that we process directly here and remove
from the globals list, by returning None. We bind their respective
processing functions with the operator below.
*)
let (>>?) opt f =
match opt with
| Some (name, args) -> f name args
| _ -> opt
let process_stdlib_pragma name args =
if name = fc_stdlib then begin
match args with
| [ ACons ("pop",_) ] -> pop_stdheader (); None
| [ ACons ("push",_); AStr s ] ->
let base_name = (System_config.Share.libc:>string) in
let relative_name = Filepath.relativize ~base_name s in
push_stdheader relative_name;
None
| _ -> Some (name, args)
end else Some (name, args)
let fc_stdlib_attribute attrs =
let open Ast_attributes in
let s = get_current_stdheader () in
if s = "" then attrs
Virgile Prevosto
committed
else begin
let payload, attrs =
if has_attribute fc_stdlib attrs then begin
AStr s :: find_attribute fc_stdlib attrs,
drop_attribute fc_stdlib attrs
Virgile Prevosto
committed
end else [AStr s], attrs
in
add_attribute (fc_stdlib, payload) attrs
Virgile Prevosto
committed
end
(* ICC align/noalign pragmas (not supported by GCC/MSVC with this syntax).
Implemented by translating them to 'aligned' attributes. Currently,
only default and noalign are supported, not explicit alignment values.
Cf. www.slac.stanford.edu/grp/cd/soft/rmx/manuals/IC_386.PDF *)
let current_pragma_align = ref (None : bool option)
let pragma_align_by_struct = H.create 17
let process_align_pragma name args =
let aux pname v =
(if Machine.(msvcMode () || gccMode ())
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
then Kernel.warning ?wkey:None else Kernel.debug ~level:1 ?dkey:None)
~current:true "Parsing ICC '%s' pragma." pname;
match args with
| [] -> current_pragma_align := Some v
| l ->
List.iter
(function
| AStr s | ACons (s, _) -> H.replace pragma_align_by_struct s v
| _ -> Kernel.warning ~current:true
"Unsupported '%s' pragma not honored by Frama-C." pname
) l
in
match name with
| "align" -> aux "align" true
| "noalign" -> aux "noalign" false
| _ -> ()
let align_pragma_for_struct sname =
try Some (H.find pragma_align_by_struct sname)
with Not_found -> !current_pragma_align
(* The syntax and semantics for the pack pragmas are GCC's, which emulates most
of MSVC's behaviors. Some of it has been tested using MSVC 2010.
Note that #pragma pack directives are emulated by translating them into
GCC-style attributes, which in turn are not supported by MSVC.
Therefore some combinations of attributes may be impossible to produce in
MSVC, which means that Frama-C on an MSVC machdep may accept more programs
that MSVC would. *)
(* The pack pragma stack *)
let packing_pragma_stack = Stack.create ()
(* The current pack pragma *)
let current_packing_pragma = ref None
let pretty_current_packing_pragma fmt =
let align =
Option.value ~default:(Integer.of_int (Machine.alignof_aligned ()))
!current_packing_pragma
in
Integer.pretty fmt align
(* Checks if [n] is a valid alignment for #pragma pack, and emits a warning
if it is not the case. Returns the value to be set as current packing pragma.
From the MSDN reference
(msdn.microsoft.com/en-us/library/2e70t5y1(v=vs.100).aspx):
Valid values are 1, 2, 4, 8, and 16.
NOTE: GCC seems to consider '#pragma pack(0)' as equivalent to '#pragma pack()',
but this is not specified in their documentation. To avoid rejecting programs
with such pragmas, we emulate GCC's current behavior but emit a warning.
This is the only case when this function returns [None]. *)
let get_valid_pragma_pack_alignment n =
if Integer.is_zero n && Machine.gccMode () then begin
Kernel.warning ~current:true "GCC accepts pack(0) but does not specify its \
behavior; considering it equivalent to pack()";
true, None
end
else begin
let valid = Integer.(equal n one || equal n two || equal n four ||
equal n eight || equal n sixteen)
in
if not valid then
Kernel.warning ~current:true "ignoring invalid packing alignment (%a)"
Integer.pretty n;
valid, Some n
end
let process_pack_pragma name args =
begin match name with
| "pack" -> begin
match args with
| [ACons ("",[])] (* #pragma pack() *) ->
Kernel.feedback ~dkey:Kernel.dkey_typing_pragma ~current:true
"packing pragma: restoring alignment to default (%d)"
(Machine.alignof_aligned ());
current_packing_pragma := None; None
| [AInt n] (* #pragma pack(n) *) ->
let is_valid, new_pragma = get_valid_pragma_pack_alignment n in
if is_valid then begin
Kernel.feedback ~dkey:Kernel.dkey_typing_pragma ~current:true
"packing pragma: setting alignment to %a" Integer.pretty n;
current_packing_pragma := new_pragma; None
end else
| [ACons ("push",[])] (* #pragma pack(push) *) ->
Kernel.feedback ~dkey:Kernel.dkey_typing_pragma ~current:true
"packing pragma: pushing alignment %t" pretty_current_packing_pragma;
Stack.push !current_packing_pragma packing_pragma_stack; None
| [ACons ("push",[]); AInt n] (* #pragma pack(push,n) *) ->
let is_valid, new_pragma = get_valid_pragma_pack_alignment n in
if is_valid then begin
Kernel.feedback ~dkey:Kernel.dkey_typing_pragma ~current:true
"packing pragma: pushing alignment %t, setting alignment to %a"
pretty_current_packing_pragma Integer.pretty n;
Stack.push !current_packing_pragma packing_pragma_stack;
current_packing_pragma:= new_pragma; None
end else
| ACons ("push",[]) :: args (* unknown push directive *) ->
Kernel.warning ~current:true
"Unsupported argument for pragma pack push directive: `%a'."
Format.(
pp_print_list
~pp_sep:(fun fmt ()->pp_print_string fmt ", ")
Cil_printer.pp_attrparam)
args;
(* We don't change the current packing directive, but
nevertheless push it on the stack, to avoid a mismatched
pop somewhere later. *)
Stack.push !current_packing_pragma packing_pragma_stack;
None
| [ACons ("pop",[])] (* #pragma pack(pop) *) ->
begin try
current_packing_pragma := Stack.pop packing_pragma_stack;
Kernel.feedback ~dkey:Kernel.dkey_typing_pragma ~current:true
"packing pragma: popped alignment %t" pretty_current_packing_pragma;
None
with Stack.Empty ->
(* GCC/Clang/MSVC seem to ignore the directive when a pop() is
called with an empty stack, so we emulate their behavior. *)
Kernel.warning ~current:true
"ignoring #pragma pack(pop) with empty stack";
None
end
| [ACons ("show",[])] (* #pragma pack(show) *) ->
| _ ->
Kernel.warning ~current:true
"Unsupported packing pragma not honored by Frama-C: #pragma pack(%a)"
(Pretty_utils.pp_list ~sep:", " ~empty:"<empty>"
Cil_printer.pp_attrparam) args;
| _ -> Some (name, args)
end
let force_packed_attribute a =
if Ast_attributes.has_attribute "packed" a then a
else Ast_attributes.add_attribute ("packed",[]) a
let is_power_of_two i = i > 0 && i land (i-1) = 0
(* Computes the numeric value corresponding to an 'aligned' attribute:
– if 'aligned' (without integer), then use the maximum machine alignment;
– else, try to const-fold the expression to an integer value.
Returns [Some n] in case of success, [None] otherwise.
Note that numeric values that are not powers of two are invalid and
also return [None]. *)
let eval_aligned_attrparams aps =
match aps with
| [] -> Some (Integer.of_int (Machine.alignof_aligned ()))
| [ap] ->
begin
match Cil.intOfAttrparam ap with
| None -> None
| Some n -> if is_power_of_two n then Some (Integer.of_int n) else None
end
| _ -> (* 'aligned(m,n,...)' is not a valid syntax *) None
let warn_invalid_align_attribute aps =
Kernel.warning ~current:true ~once:true
"ignoring invalid aligned attribute: %a"
Cil_printer.pp_attribute ("aligned", aps)
(* If there is more than one 'aligned' attribute, GCC's behavior is to
consider the maximum among them. This function computes this value
and also emits warnings for invalid attributes. *)
let combine_aligned_attributes attrs =
match Ast_attributes.filter_attributes "aligned" attrs with
| [] -> None
| aligned_attrs ->
List.fold_left (fun acc attr ->
match attr with
| ("aligned", aps) ->
begin
let align = eval_aligned_attrparams aps in
if align = None then begin
warn_invalid_align_attribute aps;
acc
end else
match acc, align with
| None, a | a, None -> a
| Some old_n, Some new_n -> Some (Integer.max old_n new_n)
end
| _ -> assert false (* attributes were previously filtered by name *)
) None aligned_attrs
let warn_incompatible_pragmas_attributes apragma has_attrs =
if apragma <> None then
Kernel.warning ~current:true ~once:true
"ignoring 'align' pragma due to presence of 'pack' pragma.@ \
No compiler was supposed to accept both syntaxes.";
if Machine.msvcMode () && has_attrs then
(* MSVC does not allow attributes *)
Kernel.warning ~current:true ~once:true
"field attributes should not be present in MSVC-compatible sources"
(* checks [attrs] for invalid aligned() attributes *)
let check_aligned attrs =
List.fold_right (fun attr acc ->
match attr with
| ("aligned", aps) ->
if eval_aligned_attrparams aps = None then
(warn_invalid_align_attribute aps; acc)
else attr :: acc
| _ -> attr :: acc
) attrs []
(* Takes into account the possible effect of '#pragma pack' directives on
component [ci], and checks the alignment of aligned() attributes.
This function is complemented by
[process_pragmas_pack_align_field_attributes]. *)
let process_pragmas_pack_align_comp_attributes loc ci cattrs =
let open Ast_attributes in
match !current_packing_pragma, align_pragma_for_struct ci.corig_name with
| None, None -> check_aligned cattrs
| Some n, apragma ->
warn_incompatible_pragmas_attributes apragma (cattrs <> []);
let with_aligned_attributes =
match combine_aligned_attributes cattrs with
| None ->
(* No valid aligned attributes in this field.
– if the composite type has a packed attribute, then add the
alignment given by the pack pragma;
– otherwise, no alignment attribute is necessary.
Drop existing "aligned" attributes, if there are invalid ones. *)
if has_attribute "packed" cattrs then (drop_attribute "aligned" cattrs)
Kernel.feedback ~source ~dkey:Kernel.dkey_typing_pragma
"adding aligned(%a) attribute to comp '%s' due to packing pragma"
Integer.pretty n ci.cname;
add_attribute ("aligned",[AInt n]) (drop_attribute "aligned" cattrs)
end
| Some local ->
(* The largest aligned wins with GCC. Don't know
with other compilers. *)
let align = Integer.max n local in
Kernel.feedback ~source ~dkey:Kernel.dkey_typing_pragma
"setting aligned(%a) attribute to comp '%s' due to packing pragma"
Integer.pretty align ci.cname;
add_attribute ("aligned",[AInt align])
(drop_attribute "aligned" cattrs)
in
force_packed_attribute with_aligned_attributes
| None, Some true ->
drop_attribute "aligned" cattrs
| None, Some false ->
force_packed_attribute
(add_attribute
(("aligned",[AInt Integer.one]))
(drop_attribute "aligned" cattrs))
(* Takes into account the possible effect of '#pragma pack' directives on
field [fi], and checks the alignment of aligned() attributes.
Because we emulate them using GCC attributes, this transformation
is complex and depends on several factors:
- if the struct inside the pragma is packed, then ignore alignment constraints
given by the pragma;
- otherwise, each struct field should have the alignment given by the pack
directive, unless that field already has an align attribute, in which case
the minimum of both is taken into account (note that, in GCC, if a field
has 2 alignment directives, it is the maximum of those that is taken). *)
let process_pragmas_pack_align_field_attributes fi fattrs cattr =
let open Current_loc.Operators in
let open Ast_attributes in
let<> UpdatedCurrentLoc = fi.floc in
match !current_packing_pragma, align_pragma_for_struct fi.forig_name with
| None, None -> check_aligned fattrs
| Some n, apragma ->
warn_incompatible_pragmas_attributes apragma (fattrs <> []);
let field_align = combine_aligned_attributes fattrs in
(* If this field has no valid aligned attributes and the composite type
has a packed attribute, nothing needs to be done: the composite will
have the "packed" attribute anyway. *)
if field_align = None && has_attribute "packed" cattr then
drop_attribute "aligned" fattrs
else
(* Otherwise, align on min(n, max(field alignment, type alignment)):
the field alignment attribute (if there is one) may be smaller than
its type alignment, so we get the maximum of both. Then, we apply
the pragma pack: the final alignment will be the minimum between what
we had and [n]. *)
let type_align = Integer.of_int (Cil.bytesAlignOf fi.ftype) in
let existing_align =
Option.fold field_align ~none:type_align ~some:(Integer.max type_align)
in
let new_align = Integer.min n existing_align in
Kernel.feedback ~dkey:Kernel.dkey_typing_pragma ~current:true
"%s aligned(%a) attribute to field '%s.%s' due to packing pragma"
(if Option.is_none field_align then "adding" else "setting")
Integer.pretty new_align fi.fcomp.cname fi.fname;
let new_attr = ("aligned", [AInt new_align]) in
add_attribute new_attr (drop_attribute "aligned" fattrs)
| None, Some true ->
drop_attribute "aligned" fattrs
| None, Some false ->
(add_attribute
("aligned",[AInt Integer.one])
(drop_attribute "aligned" fattrs))
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
(***** COMPUTED GOTO ************)
(* The address of labels are small integers (starting from 0). A computed
* goto is replaced with a switch on the address of the label. We generate
* only one such switch and we'll jump to it from all computed gotos. To
* accomplish this we'll add a local variable to store the target of the
* goto. *)
(* The local variable in which to put the detonation of the goto and the
* statement where to jump *)
let gotoTargetData: (varinfo * stmt) option ref = ref None
(* The "addresses" of labels *)
let gotoTargetHash: (string, int) H.t = H.create 13
let gotoTargetNextAddr: int ref = ref 0
(* When we process an argument list, remember the argument index which has a
* transparent union type, along with the original type. We need this to
* process function definitions *)
let transparentUnionArgs : (int * typ) list ref = ref []
let debugLoc = false
let convLoc (l : cabsloc) =
if debugLoc then
Kernel.debug "convLoc at %a: line %d, btye %d\n"
Datatype.Filepath.pretty (fst l).Filepath.pos_path
(fst l).Filepath.pos_lnum (fst l).Filepath.pos_bol;
l
let isOldStyleVarArgName n =
if Machine.msvcMode () then n = "va_alist"
else n = "__builtin_va_alist"
let isOldStyleVarArgTypeName n =
if Machine.msvcMode () then n = "va_list" || n = "__ccured_va_list"
else n = "__builtin_va_alist_t"
(* CERT EXP 46 rule: operands of bitwise operators should not be of type _Bool
or the result of a comparison.
*)
let check_logical_operand e t =
let (source,_) = e.expr_loc in
match Cil.unrollTypeNode t with
| TInt IBool ->
Kernel.warning ~wkey:Kernel.wkey_cert_exp_46 ~source
"operand of bitwise operator has boolean type"
| _ ->
let rec aux = function
| { expr_node = Cabs.PAREN e} -> aux e
| { expr_node = Cabs.BINARY (bop,_,_); expr_loc = (source, _) }
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
when is_relational_bop bop ->
Kernel.warning ~wkey:Kernel.wkey_cert_exp_46 ~source
"operand of bitwise operator is a logical relation"
| _ -> (* EXP 46 does not forbid something like
(x && y) & z, even though the logical and returns 0 or 1 as
a relational operator. Maybe this should be clarified. *)
()
in
aux e
(*** EXPRESSIONS *************)
(* We collect here the program *)
let theFile : global list ref = ref []
let theFileTypes : global list ref = ref []
(* This hashtbl contains the varinfo-indexed globals of theFile.
They are duplicated here for faster lookup *)
let theFileVars : global Cil_datatype.Varinfo.Hashtbl.t =
Cil_datatype.Varinfo.Hashtbl.create 13
let findVarInTheFile vi =
try List.rev (Cil_datatype.Varinfo.Hashtbl.find_all theFileVars vi)
with Not_found -> []
let update_fundec_in_theFile vi (f:global -> unit) =
let rec aux = function
| [] -> assert false
| (GFunDecl _ as g) :: _ -> f g
| _ :: tl -> aux tl
in
aux (findVarInTheFile vi)
let update_funspec_in_theFile vi spec =
let rec aux = function
| [] -> assert false
| GFun (f,oldloc) :: _ ->
Logic_utils.merge_funspec ~oldloc f.sspec spec
| _ :: tl -> aux tl
in
aux (findVarInTheFile vi)
let find_existing_behaviors vi =
let behaviors spec = List.map (fun x -> x.b_name) spec.spec_behavior in
let aux acc = function
| GFun(f,_) -> (behaviors f.sspec) @ acc
| GFunDecl (spec,_,_) -> behaviors spec @ acc
| _ -> acc
in List.fold_left aux [] (findVarInTheFile vi)
let get_formals vi =
let rec aux = function
| [] -> assert false
| GFun(f,_)::_ -> f.sformals
| _ :: tl -> aux tl
in aux (findVarInTheFile vi)
let initGlobals () =
theFile := [];
theFileTypes := [];
Cil_datatype.Varinfo.Hashtbl.clear theFileVars
(* Keep track of some variable ids that must be turned into definitions. We
* do this when we encounter what appears a definition of a global but
* without initializer. We leave it a declaration because maybe down the road
* we see another definition with an initializer. But if we don't see any
* then we turn the last such declaration into a definition without
* initializer *)
let mustTurnIntoDef: bool IH.t = IH.create 117
(* Globals that have already been defined. Indexed by the variable name. *)
let alreadyDefined: (string, location) H.t = H.create 117
let isDefined vi = H.mem alreadyDefined vi.vorig_name
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
(* Globals that were created due to static local variables. We chose their
* names to be distinct from any global encountered at the time. But we might
* see a global with conflicting name later in the file. *)
let staticLocals: (string, varinfo) H.t = H.create 13
(* Typedefs. We chose their names to be distinct from any global encountered
* at the time. But we might see a global with conflicting name later in the
* file *)
let typedefs: (string, typeinfo) H.t = H.create 13
let fileGlobals () =
let rec revonto (tail: global list) = function
[] -> tail
| GVarDecl (vi, _) :: rest when IH.mem mustTurnIntoDef vi.vid ->
IH.remove mustTurnIntoDef vi.vid;
(* Use the location of vi instead of the one carried by GVarDecl.
Maybe we found in the same file a declaration and then a tentative
definition. In this case, both are GVarDecl, but the location carried
by [vi] is the location of the tentative definition, which is more
useful. *)
if vi.vstorage = Extern then vi.vstorage <- NoStorage;
vi.vdefined <- true;
revonto (GVar (vi, {init = None}, vi.vdecl) :: tail) rest
| x :: rest -> revonto (x :: tail) rest
in
revonto (revonto [] !theFile) !theFileTypes
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
class checkGlobal = object
inherit nopCilVisitor
method! vglob = function
| GVar _ -> DoChildren
| _ -> SkipChildren
method! vexpr exp =
begin
match exp.enode with
| SizeOfE _ ->
(* sizeOf doesn't depend on the definitions *)
()
| _ ->
let problematic_var : string option ref = ref None in
let is_varinfo_cst vi =
let res = Cil.isConstType vi.vtype && isDefined vi in
if not res then problematic_var := Some vi.vorig_name;
res
in
if not(isConstant ~is_varinfo_cst exp)
then
match !problematic_var with
| Some name ->
Kernel.error ~once:true ~current:true
("%s is not a compile-time constant") name
| None ->
Kernel.error ~once:true ~current:true
"Initializer element is not a compile-time constant";
end;
SkipChildren
end
let cabsPushGlobal (g: global) =
ignore (visitCilGlobal (new checkGlobal) g);
pushGlobal g ~types:theFileTypes ~variables:theFile;
(match g with
| GVar (vi, _, _) | GVarDecl (vi, _)
| GFun ({svar = vi}, _) | GFunDecl (_, vi, _) ->
(* Do 'add' and not 'replace' here, as we may store both
declarations and definitions for the same varinfo *)
Cil_datatype.Varinfo.Hashtbl.add theFileVars vi g
| _ -> ()
)
(********* ENVIRONMENTS ***************)
(* The environment is kept in two distinct data structures. A hash table maps
* each original variable name into a varinfo (for variables, or an
* enumeration tag, or a type). (Note that the varinfo might contain an
* alpha-converted name different from that of the lookup name.) The Ocaml
* hash tables can keep multiple mappings for a single key. Each time the
* last mapping is returned and upon deletion the old mapping is restored. To
* keep track of local scopes we also maintain a list of scopes (represented
* as lists). *)
type envdata =
EnvVar of varinfo (* The name refers to a variable
* (which could also be a function) *)
| EnvEnum of enumitem (* the name refers to an enum item *)
| EnvTyp of typ (* The name is of the form "struct
* foo", or "union foo" or "enum foo"
* and refers to a type. Note that
* the name of the actual type might
* be different from foo due to alpha
* conversion *)
| EnvLabel of string (* The name refers to a label. This
* is useful for GCC's locally
* declared labels. The lookup name
* for this category is "label foo" *)
let env = Datatype.String.Hashtbl.create 307
(* ghost environment: keep track of all symbols, in the order
in which they have been introduced. Superset of env *)
let ghost_env = Datatype.String.Hashtbl.create 307
(* We also keep a global environment. This is always a subset of the env *)
let global_env = Datatype.String.Hashtbl.create 307
(* ghost global environment: superset of global and subset of ghost *)
let ghost_global_env = Datatype.String.Hashtbl.create 307
(* maps a C label to the ghost environment of variables in scope
at this program point. Used for typing \at() terms and predicates. *)
let label_env = Datatype.String.Hashtbl.create 307
let add_label_env lab =
let add_if_absent v (d,_) map =
match d with
| EnvVar vi when not (Datatype.String.Map.mem v map) ->
Datatype.String.Map.add v vi map
| _ -> map
in
let open Datatype.String.Hashtbl in
let lab_env = fold add_if_absent ghost_env Datatype.String.Map.empty in
add label_env lab lab_env
let remove_label_env lab =
Datatype.String.Hashtbl.remove label_env lab
(* In the scope we keep the original name, so we can remove them from the
* hash table easily *)
type undoScope =
UndoRemoveFromEnv of bool * string (* boolean indicates whether we should
remove from ghost env only, or both.*)
| UndoAlphaEnv of location Alpha.undoAlphaElement list
let scopes : undoScope list ref list ref = ref []
(* tries to estimate if the name 's' was declared in the current scope;
note that this may not work in all cases *)
let declared_in_current_scope ~ghost s =
match !scopes with
| [] -> (* global scope *)
let env = if ghost then ghost_global_env else global_env in
Datatype.String.Hashtbl.mem env s
| cur_scope :: _ ->
let names_declared_in_current_scope =
| UndoRemoveFromEnv (ghost',s) when ghost || not ghost' -> Some s
| _ -> None)
!cur_scope