-
Notifications
You must be signed in to change notification settings - Fork 15
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Adding offsetof checks as static assertions when refining a C type #144
Changes from all commits
a2a0ac7
001f844
9175422
dbcdf00
be1c01c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -24,15 +24,27 @@ type sizeof_assertion = { | |
size : int | ||
} | ||
|
||
noeq | ||
type offsetof_assertion = { | ||
type_name : ident; | ||
field_name : ident; | ||
offset : int | ||
} | ||
|
||
noeq | ||
type static_assert = | ||
| SizeOfAssertion of sizeof_assertion | ||
| OffsetOfAssertion of offsetof_assertion | ||
|
||
noeq | ||
type static_asserts = { | ||
includes : list string; | ||
sizeof_assertions : list sizeof_assertion | ||
assertions : list static_assert | ||
} | ||
|
||
let empty_static_asserts = { | ||
includes = []; | ||
sizeof_assertions = [] | ||
assertions = [] | ||
} | ||
|
||
let compute_static_asserts (benv:B.global_env) | ||
|
@@ -43,36 +55,62 @@ let compute_static_asserts (benv:B.global_env) | |
match r with | ||
| None -> empty_static_asserts | ||
| Some r -> | ||
let sizeof_assertions = | ||
let static_assertions = | ||
r.type_map | ||
|> List.map | ||
|> List.collect | ||
(fun (i, jopt) -> | ||
let j = | ||
match jopt with | ||
| None -> i | ||
| Some j -> j | ||
in | ||
let offsets = TypeSizes.field_offsets_of_type env j in | ||
let offset_of_assertions = | ||
match offsets with | ||
| Inr msg -> | ||
Ast.warning | ||
(Printf.sprintf | ||
"No offsetof assertions for type %s because %s\ns" | ||
(ident_to_string j) | ||
msg) | ||
i.range; | ||
[] | ||
| Inl offsets -> | ||
offsets |> | ||
List.collect | ||
(fun offset -> | ||
if TypeSizes.is_alignment_field (fst offset) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Exclude assertions on fields that are added for alignment padding; they are implicit in the C type |
||
then [] | ||
else [OffsetOfAssertion { | ||
type_name = i; | ||
field_name = fst offset; | ||
offset = snd offset }]) | ||
in | ||
let t_j = with_dummy_range (Type_app j KindSpec []) in | ||
match TypeSizes.size_of_typ env t_j with | ||
| TypeSizes.Fixed n | ||
| TypeSizes.WithVariableSuffix n -> | ||
{ type_name = i; | ||
size = n } | ||
| _ -> | ||
Ast.error | ||
(Printf.sprintf | ||
"Type %s is variable sized and cannot refine a C type %s" | ||
(ident_to_string j) (ident_to_string i)) | ||
i.range) | ||
let sizeof_assertion = | ||
match TypeSizes.size_of_typ env t_j with | ||
| TypeSizes.Fixed n | ||
| TypeSizes.WithVariableSuffix n -> | ||
SizeOfAssertion { | ||
type_name = i; | ||
size = n } | ||
| _ -> | ||
Ast.error | ||
(Printf.sprintf | ||
"Type %s is variable sized and cannot refine a C type %s" | ||
(ident_to_string j) (ident_to_string i)) | ||
i.range | ||
in | ||
sizeof_assertion::offset_of_assertions) | ||
in | ||
{ | ||
includes = r.Ast.includes; | ||
sizeof_assertions = sizeof_assertions | ||
assertions = static_assertions | ||
} | ||
|
||
let no_static_asserts (sas: static_asserts) : Tot bool = | ||
Nil? sas.includes && | ||
Nil? sas.sizeof_assertions | ||
Nil? sas.assertions | ||
|
||
let has_static_asserts (sas: static_asserts) : Tot bool = | ||
not (no_static_asserts sas) | ||
|
@@ -84,10 +122,17 @@ let print_static_asserts (sas:static_asserts) | |
|> List.map (fun i -> Printf.sprintf "#include \"%s\"" i) | ||
|> String.concat "\n" | ||
in | ||
let print_static_assert (sa:static_assert) = | ||
match sa with | ||
| SizeOfAssertion sa -> | ||
Printf.sprintf "C_ASSERT(sizeof(%s) == %d);" (ident_to_string sa.type_name) sa.size | ||
| OffsetOfAssertion oa -> | ||
Printf.sprintf "C_ASSERT(offsetof(%s, %s) == %d);" (ident_to_string oa.type_name) (ident_to_string oa.field_name) oa.offset | ||
in | ||
let sizeof_assertions = | ||
sas.sizeof_assertions | ||
|> List.map (fun sa -> Printf.sprintf "C_ASSERT(sizeof(%s) == %d);" (ident_to_string sa.type_name) sa.size) | ||
sas.assertions | ||
|> List.map print_static_assert | ||
|> String.concat "\n" | ||
in | ||
Options.make_includes () ^ | ||
includes ^ "\n" ^ sizeof_assertions | ||
includes ^ "\n#include <stddef.h>\n" ^ sizeof_assertions |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -197,14 +197,15 @@ let size_and_alignment_of_atomic_field (env:env_t) (f:atomic_field) | |
size, align | ||
|
||
#push-options "--warn_error -272" | ||
let alignment_prefix = Printf.sprintf "%salignment_padding" Ast.reserved_prefix | ||
let gen_alignment_ident | ||
: unit -> ML ident | ||
= let ctr : ref int = alloc 0 in | ||
fun () -> | ||
let next = !ctr in | ||
ctr := next + 1; | ||
with_range | ||
(to_ident' (Printf.sprintf "%salignment_padding_%d" Ast.reserved_prefix next)) | ||
(to_ident' (Printf.sprintf "%s_%d" alignment_prefix next)) | ||
dummy_range | ||
#pop-options | ||
|
||
|
@@ -399,6 +400,38 @@ let rec size_and_alignment_of_field (env:env_t) | |
let f = { f with v = SwitchCaseField swc field_name } in | ||
f, size, alignment | ||
|
||
let field_offsets_of_type (env:env_t) (typ:ident) | ||
: ML (either (list (ident & int)) string) | ||
= let ge = Binding.global_env_of_env (fst env) in | ||
match GlobalEnv.fields_of_type ge typ with | ||
| None -> | ||
Inr <| Printf.sprintf "No fields for type %s" (ident_to_string typ) | ||
| Some fields -> | ||
let rec field_offsets (current_offset:int) (acc:list (ident & int)) (fields:list field) | ||
: ML (either (list (ident & int)) string) | ||
= match fields with | ||
| [] -> Inl <| List.rev acc | ||
| field :: fields -> | ||
let _, size, _ = size_and_alignment_of_field env false typ field in | ||
let id = | ||
match field.v with | ||
| AtomicField af -> af.v.field_ident | ||
| RecordField _ id | ||
| SwitchCaseField _ id -> id | ||
in | ||
match size with | ||
| Fixed n -> | ||
let next_offset = n + current_offset in | ||
field_offsets next_offset ((id, current_offset) :: acc) fields | ||
|
||
| WithVariableSuffix _ | ||
| Variable -> | ||
Inl <| List.rev ((id, current_offset) :: acc) | ||
in | ||
field_offsets 0 [] fields | ||
|
||
let is_alignment_field (fieldname:ident) = | ||
Utils.string_starts_with (ident_to_string fieldname) alignment_prefix | ||
|
||
let decl_size_with_alignment (env:env_t) (d:decl) | ||
: ML decl | ||
|
@@ -433,12 +466,29 @@ let decl_size_with_alignment (env:env_t) (d:decl) | |
| ExternFn _ _ _ | ||
| ExternProbe _ -> d | ||
|
||
let idents_of_decl (d:decl) = | ||
match d.d_decl.v with | ||
| ModuleAbbrev i _ | ||
| Define i _ _ | ||
| TypeAbbrev _ i | ||
| Enum _ i _ | ||
| ExternFn i _ _ | ||
| ExternProbe i -> [i] | ||
| Record names _ _ _ | ||
| CaseType names _ _ | ||
| OutputType { out_typ_names = names } | ||
| ExternType names -> [names.typedef_name; names.typedef_abbrev] | ||
|
||
let size_of_decls (genv:B.global_env) (senv:size_env) (ds:list decl) = | ||
let env = | ||
B.mk_env genv, senv in | ||
// {senv with sizes = H.create 10} in | ||
let env = B.mk_env genv, senv in | ||
let ds = List.map (decl_size_with_alignment env) ds in | ||
let ge = B.global_env_of_env (fst env) in | ||
ds |> List.iter (fun d -> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Update the global environment with revised type declarations after fields (may) have been added for alignment padding |
||
idents_of_decl d |> List.iter (fun i -> | ||
match H.try_find ge.ge_h i.v with | ||
| None -> () | ||
| Some (_, attrs) -> H.insert ge.ge_h i.v (d, attrs))); | ||
ds | ||
|
||
let finish_module en mname e_and_p = | ||
e_and_p |> snd |> List.iter (H.remove en) | ||
e_and_p |> snd |> List.iter (H.remove en) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
module Utils | ||
inline_for_extraction | ||
let valid_string | ||
(valid: (string -> Tot bool)) | ||
: Tot Type0 | ||
= (s: string { valid s == true }) | ||
|
||
let always_valid (_: string) : Tot bool = true | ||
|
||
let starts_with_capital (s: string) : Tot bool = | ||
String.length s >= 1 && | ||
begin let first = String.sub s 0 1 in | ||
String.compare first "A" >= 0 && String.compare first "Z" <= 0 | ||
end | ||
|
||
let ends_with (s:string) (suffix:string) : bool = | ||
let l = String.length s in | ||
let sl = String.length suffix in | ||
if sl > l || sl = 0 | ||
then false | ||
else let suffix' = String.sub s (l - sl) sl in | ||
suffix = suffix' | ||
|
||
let string_starts_with (big small: string) : Tot bool = | ||
let small_len = String.length small in | ||
if String.length big < small_len | ||
then false | ||
else String.sub big 0 small_len = small | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,8 @@ | ||
#include <stdint.h> | ||
#ifndef C_ASSERT | ||
#define C_ASSERT(e) typedef char __C_ASSERT__[(e)?1:-1] | ||
#endif | ||
typedef char CIRCLE[20]; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Revise the test case, since we now also check offsets, not just sizes |
||
typedef struct _CIRCLE { | ||
uint32_t p[4]; | ||
uint32_t radius; | ||
} CIRCLE; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Moved to Utils