diff --git a/src/kernel_services/plugin_entry_points/db.ml b/src/kernel_services/plugin_entry_points/db.ml index e88014be4f9ba107cf6e08cecf77bbd531802be6..6d7e0a97be49d98241f37be69baf4bc27c6355c0 100644 --- a/src/kernel_services/plugin_entry_points/db.ml +++ b/src/kernel_services/plugin_entry_points/db.ml @@ -618,7 +618,8 @@ module Value = struct (** Type for a Value builtin function *) - type builtin_sig = + type builtin_type = typ * typ list + type builtin = state -> (Cil_types.exp * Cvalue.V.t * Cvalue.V_Offsetmap.t) list -> Value_types.call_result diff --git a/src/kernel_services/plugin_entry_points/db.mli b/src/kernel_services/plugin_entry_points/db.mli index fe390a2c327da09e22208efe9dafb753b104b519..df8e76225528cc8d6f61c8f76e41f1a2012d10d3 100644 --- a/src/kernel_services/plugin_entry_points/db.mli +++ b/src/kernel_services/plugin_entry_points/db.mli @@ -177,26 +177,31 @@ module Value : sig exception Outside_builtin_possibilities - (** Type for a Value builtin function *) - type builtin_sig = - (** Memory state at the beginning of the function *) - state -> - (** Args for the function: the expressions corresponding to the formals - of the functions at the call site, the actual value of those formals, - and a more precise view of those formals using offsetmaps (for eg. - structs) *) - (Cil_types.exp * Cvalue.V.t * Cvalue.V_Offsetmap.t) list -> + (* Type of a C function interpreted by a builtin: + return type and list of argument types. *) + type builtin_type = typ * typ list + + (** Type for an Eva builtin function *) + type builtin = + (** Memory state at the beginning of the function *) + state -> + (** Args for the function: the expressions corresponding to the formals + of the functions at the call site, the actual value of those formals, + and a more precise view of those formals using offsetmaps (for eg. + structs) *) + (Cil_types.exp * Cvalue.V.t * Cvalue.V_Offsetmap.t) list -> Value_types.call_result - val register_builtin: (string -> ?replace:string -> builtin_sig -> unit) ref - (** [!register_builtin name ?replace f] registers an abstract function [f] - to use everytime a C function named [name] is called in the program. - If [replace] is supplied and option [-val-builtins-auto] is active, - calls to [replace] will also be substituted by the builtin. - See also option [-val-builtin] *) + val register_builtin: + (string -> ?replace:string -> ?typ:builtin_type -> builtin -> unit) ref + (** [!register_builtin name ?replace ?typ f] registers an abstract function + [f] to use everytime a C function named [name] of type [typ] is called in + the program. If [replace] is supplied and option [-eva-builtins-auto] is + active, calls to [replace] will also be substituted by the builtin. See + also option [-eva-builtin] *) - val registered_builtins: (unit -> (string * builtin_sig) list) ref - (** Returns a list of the pairs (name, builtin_sig) registered via + val registered_builtins: (unit -> (string * builtin) list) ref + (** Returns a list of the pairs (name, builtin) registered via [register_builtin]. @since Aluminium-20160501 *) diff --git a/src/plugins/value/domains/cvalue/builtins.ml b/src/plugins/value/domains/cvalue/builtins.ml index fb2479c5e3c896d33ad478cb4d07bd459e6354d1..55f7f29e6d3546c8ff5f8b4c5ea1083aef6ebf19 100644 --- a/src/plugins/value/domains/cvalue/builtins.ml +++ b/src/plugins/value/domains/cvalue/builtins.ml @@ -32,11 +32,11 @@ type use_builtin = Always | OnAuto let table = Hashtbl.create 17 -let register_builtin name ?replace f = - Hashtbl.replace table name (f, None, Always); +let register_builtin name ?replace ?typ f = + Hashtbl.replace table name (f, typ, None, Always); match replace with | None -> () - | Some fname -> Hashtbl.replace table fname (f, Some name, OnAuto) + | Some fname -> Hashtbl.replace table fname (f, typ, Some name, OnAuto) let () = Db.Value.register_builtin := register_builtin @@ -45,7 +45,7 @@ let () = Db.Value.register_builtin := register_builtin let registered_builtins () = let l = Hashtbl.fold - (fun name (f, _, u) acc -> if u = Always then (name, f) :: acc else acc) + (fun name (f, _, _, u) acc -> if u = Always then (name, f) :: acc else acc) table [] in List.sort (fun (name1, _) (name2, _) -> String.compare name1 name2) l @@ -54,7 +54,7 @@ let () = Db.Value.registered_builtins := registered_builtins let builtin_names_and_replacements () = let stand_alone, replacements = - Hashtbl.fold (fun name (_, replaced_by, _) (acc1, acc2) -> + Hashtbl.fold (fun name (_, _, replaced_by, _) (acc1, acc2) -> match replaced_by with | None -> name :: acc1, acc2 | Some rep_by -> acc1, (name, rep_by) :: acc2 @@ -94,7 +94,7 @@ let () = let mem_builtin name = try - let _, _, u = Hashtbl.find table name in + let _, _, _, u = Hashtbl.find table name in u = Always with Not_found -> false @@ -109,20 +109,41 @@ let find_builtin_specification kf = TODO: check that the specification is the frama-c libc specification? *) if spec.spec_behavior <> [] then Some spec else None +(* Returns [true] if the function [kf] is incompatible with the expected type + for a given builtin, which therefore cannot be applied. *) +let inconsistent_builtin_typ kf = function + | None -> false (* No expected type provided with the builtin, no check. *) + | Some (expected_result, expected_args) -> + match Kernel_function.get_type kf with + | TFun (result, args, _, _) -> + let args = Cil.argsToList args in + Cil.need_cast result expected_result + || List.length args <> List.length expected_args + || List.exists2 (fun (_, t, _) u -> Cil.need_cast t u) args expected_args + | _ -> assert false + let find_builtin_override kf = let name = try Value_parameters.BuiltinsOverrides.find kf with Not_found -> Kernel_function.get_name kf in try - let f, _, u = Hashtbl.find table name in - if u = Always || Value_parameters.BuiltinsAuto.get () + let f, typ, _, u = Hashtbl.find table name in + if (u = Always || Value_parameters.BuiltinsAuto.get ()) + && not (inconsistent_builtin_typ kf typ) then Extlib.opt_map (fun s -> name, f, s) (find_builtin_specification kf) else None with Not_found -> None -let warn_builtin_override bname kf = +let warn_builtin_override bname kf expected_typ = let source = fst (Kernel_function.get_location kf) in + if inconsistent_builtin_typ kf expected_typ + then + Value_parameters.warning ~source ~once:true + ~wkey:Value_parameters.wkey_builtins_override + "The builtin %s will not be used for function %a of incompatible type." + bname Kernel_function.pretty kf + else if find_builtin_specification kf = None then Value_parameters.warning ~source ~once:true @@ -147,15 +168,18 @@ let warn_builtin_override bname kf = let warn_definitions_overridden_by_builtins () = Value_parameters.BuiltinsOverrides.iter - (fun (kf, name) -> warn_builtin_override (Extlib.the name) kf); + (fun (kf, name) -> + let name = Extlib.the name in + let (_, typ, _ , _) = Hashtbl.find table name in + warn_builtin_override name kf typ); let autobuiltins = Value_parameters.BuiltinsAuto.get () in Hashtbl.iter - (fun name (_, _, u) -> + (fun name (_, typ, _, u) -> if autobuiltins || u = Always then try let kf = Globals.Functions.find_by_name name in - warn_builtin_override name kf + warn_builtin_override name kf typ with Not_found -> ()) table @@ -182,7 +206,7 @@ let clobbered_set_from_ret state ret = type call = (Precise_locs.precise_location, Cvalue.V.t) Eval.call type result = Cvalue.Model.t * Locals_scoping.clobbered_set -type builtin = Db.Value.builtin_sig +type builtin = Db.Value.builtin open Eval diff --git a/src/plugins/value/domains/cvalue/builtins.mli b/src/plugins/value/domains/cvalue/builtins.mli index 351bf9a11ddd32c6df1c74fbba77bbe45f1ecf6e..ebf302fd0d534aa60c398de0623406087182a8e4 100644 --- a/src/plugins/value/domains/cvalue/builtins.mli +++ b/src/plugins/value/domains/cvalue/builtins.mli @@ -25,9 +25,15 @@ exception Invalid_nb_of_args of int -(** Register the given OCaml function as a builtin, that will be used - instead of the Cil C function of the same name *) -val register_builtin: string -> ?replace:string -> Db.Value.builtin_sig -> unit +(** [register_builtin name ?replace ?typ f] registers the ocaml function [f] + as a builtin to be used instead of the C function of name [name]. + If [replace] is provided, the builtin is also used instead of the C function + of name [replace], unless option -eva-builtin-auto is disabled. + If [typ] is provided, consistency between the expected [typ] and the type of + the C function is checked before using the builtin. *) +val register_builtin: + string -> ?replace:string -> + ?typ:Db.Value.builtin_type -> Db.Value.builtin -> unit (** [clobbered_set_from_ret state ret] can be used for functions that return a pointer to where they have written some data. It returns all the bases diff --git a/src/plugins/value/domains/cvalue/builtins_float.ml b/src/plugins/value/domains/cvalue/builtins_float.ml index 458470bfbd5d68f8ad26fab04d9ce4939cdc3369..e636114ac354bbbc3cb8a27ab973fb38ecf73049 100644 --- a/src/plugins/value/domains/cvalue/builtins_float.ml +++ b/src/plugins/value/domains/cvalue/builtins_float.ml @@ -70,8 +70,9 @@ let arity2 fk caml_fun state actuals = let register_arity2 c_name fk f = let name = "Frama_C_" ^ c_name in - Builtins.register_builtin name ~replace:c_name (arity2 fk f); -;; + let t = Cil_types.TFloat (fk, []) in + let typ = t, [t; t] in + Builtins.register_builtin name ~replace:c_name ~typ (arity2 fk f) let () = let open Fval in @@ -80,9 +81,7 @@ let () = register_arity2 "pow" Cil_types.FDouble pow; register_arity2 "powf" Cil_types.FFloat pow; register_arity2 "fmod" Cil_types.FDouble fmod; - register_arity2 "fmodf" Cil_types.FFloat fmod; -;; - + register_arity2 "fmodf" Cil_types.FFloat fmod let arity1 name fk caml_fun state actuals = match actuals with @@ -114,8 +113,9 @@ let arity1 name fk caml_fun state actuals = let register_arity1 c_name fk f = let name = "Frama_C_" ^ c_name in - Builtins.register_builtin name ~replace:c_name (arity1 name fk f); -;; + let t = Cil_types.TFloat (fk, []) in + let typ = t, [t] in + Builtins.register_builtin name ~replace:c_name ~typ (arity1 name fk f) let () = let open Fval in @@ -139,5 +139,4 @@ let () = register_arity1 "floorf" Cil_types.FFloat floor; register_arity1 "ceilf" Cil_types.FFloat ceil; register_arity1 "truncf" Cil_types.FFloat trunc; - register_arity1 "roundf" Cil_types.FFloat fround; -;; + register_arity1 "roundf" Cil_types.FFloat fround diff --git a/src/plugins/value/domains/cvalue/builtins_malloc.ml b/src/plugins/value/domains/cvalue/builtins_malloc.ml index f85c0c466ee462a48d0fb8bbd14878a4890e4a62..7c83a1be556453e8c96e124ec7b3b6c12413ac7e 100644 --- a/src/plugins/value/domains/cvalue/builtins_malloc.ml +++ b/src/plugins/value/domains/cvalue/builtins_malloc.ml @@ -476,7 +476,7 @@ let alloc_by_stack_aux region stack prefix sizev state = (* For each callstack, the first MallocPrecision.get() are precise fresh distinct locations. The following allocations all return the same base, first strong, then weak, and which is extended as needed. *) -let alloc_by_stack ?(prefix="malloc") region ?returns_null : Db.Value.builtin_sig = fun state actuals-> +let alloc_by_stack ?(prefix="malloc") region ?returns_null : Db.Value.builtin = fun state actuals-> let stack = call_stack_no_wrappers () in let sizev = match actuals with | [_,size,_] -> size @@ -501,7 +501,7 @@ let () = Builtins.register_builtin (alloc_by_stack ~prefix:"alloca" Base.Alloca ~returns_null:false) (* Equivalent to [alloc_by_stack], but for [calloc]. *) -let calloc_by_stack : Db.Value.builtin_sig = fun state actuals -> +let calloc_by_stack : Db.Value.builtin = fun state actuals -> calloc_abstract (alloc_by_stack_aux Base.Malloc) state actuals let () = Builtins.register_builtin