Skip to content

Commit fa01322

Browse files
authored
makes all C data type sizes a multitude of their alignment (#1501)
Though it is abi-specific in general, apparently most if not all ABI require the structure size to be a multitude of its alignment, so I decided to make it the default. In addition, adds comprehensive documentation to each field. Also publishes `padding` and `next_multitude_of` functions to leverage custom alignment/padding implementations.
1 parent 435bf85 commit fa01322

File tree

2 files changed

+122
-43
lines changed

2 files changed

+122
-43
lines changed

lib/bap_c/bap_c_size.ml

Lines changed: 24 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,14 @@ type 'a unqualified = (no_qualifier, 'a) spec
88

99
type bits = Int.t
1010

11+
let next_multitude_of ~n x = (x + (n-1)) land (lnot (n-1))
12+
13+
14+
let padding alignment offset =
15+
let align = Size.in_bits alignment in
16+
(align - offset mod align) mod align
17+
18+
1119

1220
class base (m : model) = object(self)
1321
method integer (t : integer) : size =
@@ -56,12 +64,9 @@ class base (m : model) = object(self)
5664
| `Pointer _ -> (self#pointer :> size)
5765

5866
method padding t offset : size option =
59-
let align = Size.in_bits (self#alignment t) in
60-
match (align - offset mod align) mod align with
61-
| 0 -> None
62-
| n -> match Size.of_int n with
63-
| Error _ -> None
64-
| Ok s -> Some s
67+
match Size.of_int @@ padding (self#alignment t) offset with
68+
| Error _ -> None
69+
| Ok s -> Some s
6570

6671
method alignment (t : Bap_c_type.t) : size =
6772
let byte = `r8 in
@@ -75,14 +80,17 @@ class base (m : model) = object(self)
7580
| `Function _ -> (self#pointer :> size)
7681
| #scalar as t -> self#scalar t
7782

78-
79-
method bits : t -> Int.t option = fun t -> match t with
80-
| `Void -> None
81-
| #scalar as t -> Some (Size.in_bits (self#scalar t))
82-
| `Function _ -> None
83-
| `Union s -> self#union s
84-
| `Array s -> self#array s
85-
| `Structure s -> self#structure s
83+
method bits : t -> Int.t option = fun t ->
84+
let size = match t with
85+
| `Void -> None
86+
| #scalar as t -> Some (Size.in_bits (self#scalar t))
87+
| `Function _ -> None
88+
| `Union s -> self#union s
89+
| `Array s -> self#array s
90+
| `Structure s -> self#structure s in
91+
Option.map size ~f:(fun size ->
92+
let alignment = self#alignment t in
93+
next_multitude_of ~n:(Size.in_bits alignment) size)
8694

8795
method array : _ -> Int.t option =
8896
fun {Spec.t={Array.element=t; size}} -> match size with
@@ -101,12 +109,10 @@ class base (m : model) = object(self)
101109

102110
method structure : compound unqualified -> Int.t option =
103111
fun {Spec.t={Compound.fields}} ->
104-
let padding t offset =
105-
let align = Size.in_bits (self#alignment t) in
106-
(align - offset mod align) mod align in
107112
List.fold fields ~init:(Some 0) ~f:(fun sz (_,field) -> match sz with
108113
| None -> None
109114
| Some sz -> match self#bits field with
110115
| None -> None
111-
| Some sz' -> Some (sz + sz' + padding field sz))
116+
| Some sz' ->
117+
Some (sz + sz' + padding (self#alignment field) sz))
112118
end

lib/bap_c/bap_c_size.mli

Lines changed: 98 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -7,60 +7,133 @@ open Bap_c_type
77

88
type bits = Int.t
99

10-
(** Base class for computing size of C data types.
10+
11+
(** [next_multitude_of ~n x] returns [y >= x] so that [y]
12+
is a multitude of [n], i.e., [y = n * k].
13+
14+
@since 2.5.0 *)
15+
val next_multitude_of : n:int -> int -> int
16+
17+
18+
(** [padding alignment offset] computes the required padding at
19+
[offset] to ensure the [alignment].
20+
21+
@since 2.5.0 *)
22+
val padding : Size.t -> int -> int
23+
24+
(** The base class for computing sizes and aligments of C data types.
25+
1126
The algorithm is implemented as a class to allow
1227
a particular implementation to fine tune the calculation.
13-
We need here an open recursion, since type is inherently
14-
recursive.
1528
16-
The entry method is the [bits] method.
29+
The [model] argument defines the default sizes for integral data
30+
types. If no suitable model is available for your architecture
31+
then use the closest model and override the specific methods to
32+
fine-tune the data model of your target.
33+
34+
The entry methods are [bits] and [aligment].
35+
36+
{3 Example}
37+
38+
For example, let's compute the size of the
39+
40+
{v
41+
struct foo {
42+
char v1;
43+
int v2;
44+
char v3;
45+
};
46+
v}
47+
48+
Using the LP64 data model, in which integers are 32 bit long and
49+
char is 8 bit. The size of the structure is 12 bytes, due to
50+
the 3 bytes of padding before [v2] and six bytes of trailing
51+
padding.
52+
53+
{[
54+
# let size = new C.Size.base `LP64;;
55+
# size#bits C.Type.(structure "foo" [
56+
"v1", basic `char;
57+
"v2", basic `uint;
58+
"v3", basic `char
59+
]);;
60+
- : C.Size.bits option = Some 96
61+
]}
1762
*)
1863
class base : model -> object
19-
(** returns a size of the data type representation if type
20-
definition is complete. Otherwise [None] is returned.
21-
The size is computed with respect to padding and alignment
22-
restructions.
64+
65+
66+
(** returns a size of the data type representation in bits.
67+
68+
For incomplete types returns [None]. The size is always a
69+
multitude of the data type alignment and includes the
70+
paddings necessary for preserving the alignment restrictions.
71+
72+
@since 2.5.0 the size is a multitude of the alignment.
2373
*)
2474
method bits : t -> bits option
2575

26-
(** [alignment t] calculates an alignment restriction for data
27-
type [t]. The default alignment rules are the following:
28-
- if type is scalar then the alignment is [sizeof(t)];
29-
- if type is [elt\[\]] then the alignment is [sizeof(elt)];
30-
- if type is structure or union, the the alignment of is
31-
the maximum alignment of a field;
32-
- if type is function, then alignment is equal to sizeof
33-
pointer
34-
- if type is void then alignment is 8 bits.*)
76+
(** [alignment t] the alignment of data type [t].
77+
78+
The alignment of
79+
- void or an incomplete type is 8;
80+
- a scalar is [sizeof(t)];
81+
- an array is the alignment its element;
82+
- a function pointer is [sizeof] the pointer;
83+
- a structure or a union is the largest of the element's alignments.
84+
85+
*)
3586
method alignment : t -> size
3687

88+
(** DEPRECATED. Use the [padding] function if you need to compute
89+
padding. *)
90+
method padding : t -> bits -> size option
91+
[@@deprecated "since [2021-05] this method is ignored"]
3792
(* this method was deprecated as
3893
1) it has an incorrect type (padding can have any number of bits)
3994
2) padding is fully defined by the alignemnt and there is no
4095
need to parameterize it. *)
41-
method padding : t -> bits -> size option
42-
[@@deprecated "since [2021-05] this method is ignored"]
4396

4497

45-
(** [array spec] if array [spec] is complete, then returns a
46-
product of the bitwidth of array size and array's element
47-
type, otherwise returns [None] *)
98+
(** [array spec] if array [spec] is complete, i.e., the number of
99+
elements is known, then returns a product of the
100+
array size and the array's element type in bits,
101+
otherwise returns [None]
102+
*)
48103
method array : (cvr qualifier, array) spec -> bits option
49104

50-
(** if spec is complete then returns a size of the biggest
51-
element, including padding *)
105+
(** if spec is complete then returns a size in bits of the biggest
106+
element, including the padding between fields, but excludeing
107+
the trailing padding. *)
52108
method union : (no_qualifier, compound) spec -> bits option
53109

54110
(** if spec is complete then returns a total size of the
55-
structure, including padding. *)
111+
structure, including the padding between fields, but excluding
112+
the trailing padding. *)
56113
method structure : (no_qualifier, compound) spec -> bits option
57114

115+
116+
(** the size of intergral types. *)
58117
method integer : integer -> size
118+
119+
(** the size of a pointer. *)
59120
method pointer : addr_size
121+
122+
(** the size of the enumeration. *)
60123
method enum : (string * int64 option) list -> size
124+
125+
(** the size of a real floating-point data type. *)
61126
method real : real -> [`r32 | `r64 | `r128]
127+
128+
(** the size of a complex floating-point data type. *)
62129
method complex : complex -> size
130+
131+
(** the size of a floating-point data type. *)
63132
method floating : floating -> size
133+
134+
(** the size of a basic data type. *)
64135
method basic : basic -> size
136+
137+
(** the size of a scalar data type. *)
65138
method scalar : scalar -> size
66139
end

0 commit comments

Comments
 (0)