diff --git a/.gitignore b/.gitignore index 765ac696..fdc3cedf 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,4 @@ _build/* **/.merlin test_asm/build .rdcache +.vscode diff --git a/doc/html/index.html b/doc/html/index.html index 6c320a2b..5a8f3d34 100644 --- a/doc/html/index.html +++ b/doc/html/index.html @@ -2,7 +2,7 @@
Analyse.Base
Analyse.Collected
collect the various test analysis data
val mk_analysis : ElfTypes.test -> string -> string option -> Analyse__.CollectedType.analysis
Analyse.Collected
collect the various test analysis data
val mk_analysis :
+ ElfTypes.test ->
+ string ->
+ string option ->
+ Analyse__.CollectedType.analysis
Analyse.DwarfLineInfo
post-process DWARF source line info
type evaluated_line_info_sequence
=
{
elis_first : Utils.addr; |
elis_last : Utils.addr; |
elis_lnh : Dwarf.line_number_header; |
elis_lines : Dwarf.line_number_registers list; |
}
type evaluated_line_info_entry
=
{
elie_first : Utils.addr; |
elie_last : Utils.addr; |
elie_lnh : Dwarf.line_number_header; |
elie_lnr : Dwarf.line_number_registers; |
}
type evaluated_line_info_for_instruction
=
{
elifi_start : bool; |
elifi_entry : evaluated_line_info_entry; |
}
val pp_line_number_header_concise : Dwarf.line_number_header -> string
val pp_sequence_concise : evaluated_line_info_sequence -> string
val pp_elie_concise : evaluated_line_info_entry -> string
val split_into_sequences : (Dwarf.line_number_header * Dwarf.line_number_registers list) -> evaluated_line_info_sequence list
val split_into_entries : evaluated_line_info_sequence -> evaluated_line_info_entry list
val mk_line_info : Dwarf.evaluated_line_info -> Analyse__.ControlFlowTypes.instruction array -> evaluated_line_info_for_instruction list array
val source_file_cache : ((string option * string option * string) * string array option) list Stdlib.ref
val actual_directories : string option -> (string option * string option * string) -> string * string
val source_line : (string option * string option * string) -> int -> string option
val pp_source_line : string option -> int -> string
val mk_subprogram_name : Dwarf.dwarf_static -> evaluated_line_info_for_instruction -> string
val pp_dwarf_source_file_lines' : Types.ppmode -> Dwarf.dwarf_static -> bool -> bool -> evaluated_line_info_for_instruction -> string
val dwarf_source_file_line_numbers_by_index : ElfTypes.test -> evaluated_line_info_for_instruction list array -> int -> (string * int) list
Analyse.DwarfLineInfo
post-process DWARF source line info
post-processed DWARF source line info
type evaluated_line_info_sequence = {
elis_first : Utils.addr;
elis_last : Utils.addr;
elis_lnh : Dwarf.line_number_header;
elis_lines : Dwarf.line_number_registers list;
}
type evaluated_line_info_entry = {
elie_first : Utils.addr;
elie_last : Utils.addr;
elie_lnh : Dwarf.line_number_header;
elie_lnr : Dwarf.line_number_registers;
}
type evaluated_line_info_for_instruction = {
elifi_start : bool;
elifi_entry : evaluated_line_info_entry;
}
val pp_sequence_concise : evaluated_line_info_sequence -> string
val pp_elie_concise : evaluated_line_info_entry -> string
val split_into_sequences :
+ (Dwarf.line_number_header * Dwarf.line_number_registers list) ->
+ evaluated_line_info_sequence list
val split_into_entries :
+ evaluated_line_info_sequence ->
+ evaluated_line_info_entry list
val mk_line_info :
+ Dwarf.evaluated_line_info ->
+ Analyse__.ControlFlowTypes.instruction array ->
+ evaluated_line_info_for_instruction list array
find and pretty-print source lines for addresses
val mk_subprogram_name :
+ Dwarf.dwarf_static ->
+ evaluated_line_info_for_instruction ->
+ string
val pp_dwarf_source_file_lines' :
+ Types.ppmode ->
+ Dwarf.dwarf_static ->
+ bool ->
+ bool ->
+ evaluated_line_info_for_instruction ->
+ string
val dwarf_source_file_line_numbers_by_index :
+ ElfTypes.test ->
+ evaluated_line_info_for_instruction list array ->
+ int ->
+ (string * int) list
Analyse.Elf
val parse_elf_file : string -> ElfTypes.test
val marshal_to_file : string -> 'a -> unit
val marshal_from_file : string -> ElfTypes.test option
Analyse.Elf
pp symbol map
use linksem to parse ELF file and extract DWARF info
val parse_elf_file : string -> ElfTypes.test
marshal and unmarshal test
val marshal_from_file : string -> ElfTypes.test option
Analyse.ElfTypes
type of collected ELF-file data from linksem
type test
=
{
elf_file : Elf_file.elf_file; |
arch : architecture; |
symbol_map : Elf_file.global_symbol_init_info; |
segments : Elf_interpreted_segment.elf64_interpreted_segment list; |
e_entry : Utils.natural; |
e_machine : Utils.natural; |
dwarf_static : Dwarf.dwarf_static; |
dwarf_semi_pp_frame_info : (Utils.natural * string * (string * string) list) list; |
}
Analyse.ElfTypes
type of collected ELF-file data from linksem
AMD x86-64 architecture, elf_ma_x86_64 = 62
type test = {
elf_file : Elf_file.elf_file;
arch : architecture;
symbol_map : (string
+ * (Z.t
+ * Z.t
+ * Utils.Sym.t
+ * (Byte_sequence_wrapper.byte_sequence
+ * Analyse__.Symbols.rels)
+ * Z.t))
+ list;
e_entry : Utils.natural;
e_machine : Utils.natural;
dwarf_static : Dwarf.dwarf_static;
dwarf_semi_pp_frame_info : (Utils.natural * string * (string * string) list)
+ list;
}
Analyse.Globals
val elf : string option Stdlib.ref
val branch_table_data_file : string option Stdlib.ref
val objdump_d : string option Stdlib.ref
val elf2 : string option Stdlib.ref
val branch_table_data_file2 : string option Stdlib.ref
val objdump_d2 : string option Stdlib.ref
val qemu_log : string option Stdlib.ref
val comp_dir : string option Stdlib.ref
val cfg_dot_file : string option Stdlib.ref
val cfg_source_nodes : string option Stdlib.ref
val cfg_source_nodes2 : string option Stdlib.ref
val out_file : string option Stdlib.ref
val out_dir : string option Stdlib.ref
val clip_binary : bool Stdlib.ref
val show_vars : bool Stdlib.ref
val show_cfa : bool Stdlib.ref
val show_source : bool Stdlib.ref
val ppmode : Types.ppmode Stdlib.ref
val src_target_dir : string option Stdlib.ref
val copy_sources_dry_run : bool Stdlib.ref
val skylight : bool Stdlib.ref
Analyse.Globals
val ppmode : Types.ppmode Stdlib.ref
Analyse.Pp
type render_kind
=
| Render_symbol_star |
| Render_symbol_nostar |
| Render_source |
| Render_frame |
| Render_instruction |
| Render_vars |
| Render_vars_new |
| Render_vars_old |
| Render_inlining |
| Render_ctrlflow |
val render_colour : render_kind -> string
val render_class_name : render_kind -> string
val html_idiom : html_idiom
val css : Types.ppmode -> render_kind -> string -> string
val last_frame_info : string Stdlib.ref
val last_var_info : string list Stdlib.ref
val last_source_info : string Stdlib.ref
val pp_instruction_init : unit -> unit
val pp_instruction : Types.ppmode -> ElfTypes.test -> Analyse__.CollectedType.analysis -> int -> int -> Analyse__.ControlFlowTypes.instruction -> string
val skylight : unit -> string
val chunk_filename_whole : Types.ppmode -> 'a -> string -> string * string
val chunk_filename_per_cu : Types.ppmode -> 'a -> string -> Dwarf.sdt_compilation_unit -> string * string
val wrap_chunks : Types.ppmode -> ('a * 'b * string) list -> ('a * 'b * string) list
val whole_file_chunks : Types.ppmode -> ElfTypes.test -> Analyse__.CollectedType.analysis -> 'a -> ('b * string * string * 'c) list list -> (string * string * string) list
val pp_instructions_ranged : Types.ppmode -> ElfTypes.test -> Analyse__.CollectedType.analysis -> (Utils.addr * Utils.addr) -> string
val chunks_of_ranged_cu : Types.ppmode -> ElfTypes.test -> Analyse__.CollectedType.analysis -> 'a -> ((Utils.natural * Utils.natural) * Dwarf.sdt_compilation_unit) -> string * (string * string * string) list
val wrap_body : Types.ppmode -> (string * string * string) -> string
val output_file : (string * string * string) -> unit
val output_whole_file_files : Types.ppmode -> ElfTypes.test -> Analyse__.CollectedType.analysis -> 'a -> ('b * string * string * 'c) list list -> unit
val output_per_cu_files : Types.ppmode -> ElfTypes.test -> Analyse__.CollectedType.analysis -> 'a -> ((Utils.natural * Utils.natural) * Dwarf.sdt_compilation_unit) list -> (string * string * string * string) list list
val pp_test_analysis : Types.ppmode -> ElfTypes.test -> Analyse__.CollectedType.analysis -> string
Analyse.Pp
render collected analysis data to text or css
val render_colour : render_kind -> string
val render_class_name : render_kind -> string
val html_idiom : html_idiom
val css : Types.ppmode -> render_kind -> string -> string
pretty-print one instruction
val pp_instruction :
+ Types.ppmode ->
+ ElfTypes.test ->
+ Analyse__.CollectedType.analysis ->
+ int ->
+ int ->
+ Analyse__.ControlFlowTypes.instruction ->
+ string
pretty-print test analysis
val chunk_filename_whole : Types.ppmode -> 'a -> string -> string * string
val chunk_filename_per_cu :
+ Types.ppmode ->
+ 'a ->
+ string ->
+ Dwarf.sdt_compilation_unit ->
+ string * string
val wrap_chunks :
+ Types.ppmode ->
+ ('a * 'b * string) list ->
+ ('a * 'b * string) list
val whole_file_chunks :
+ Types.ppmode ->
+ ElfTypes.test ->
+ Analyse__.CollectedType.analysis ->
+ 'a ->
+ ('b * string * string * 'c) list list ->
+ (string * string * string) list
val pp_instructions_ranged :
+ Types.ppmode ->
+ ElfTypes.test ->
+ Analyse__.CollectedType.analysis ->
+ (Utils.addr * Sym_ocaml.Num.t) ->
+ string
val chunks_of_ranged_cu :
+ Types.ppmode ->
+ ElfTypes.test ->
+ Analyse__.CollectedType.analysis ->
+ 'a ->
+ ((Utils.natural * Utils.natural) * Dwarf.sdt_compilation_unit) ->
+ string * (string * string * string) list
val wrap_body : Types.ppmode -> (string * string * string) -> string
val output_whole_file_files :
+ Types.ppmode ->
+ ElfTypes.test ->
+ Analyse__.CollectedType.analysis ->
+ 'a ->
+ ('b * string * string * 'c) list list ->
+ unit
val output_per_cu_files :
+ Types.ppmode ->
+ ElfTypes.test ->
+ Analyse__.CollectedType.analysis ->
+ 'a ->
+ ((Utils.natural * Utils.natural) * Dwarf.sdt_compilation_unit) list ->
+ (string * string * string * string) list list
val pp_test_analysis :
+ Types.ppmode ->
+ ElfTypes.test ->
+ Analyse__.CollectedType.analysis ->
+ string
Analyse.Types
types shared between analyse* and the read-dwarf top-level
Analyse.Types
types shared between analyse* and the read-dwarf top-level
Analyse.Utils
Miscellaneous types and utility functions used throughout the analyse code
type addr
= natural
machine address
val pp_addr : natural -> string
val measure_time : bool
val time : string -> ('a -> 'b) -> 'a -> 'b
Print the time this function call took. The string is just for the printed message
val esc : Types.ppmode -> string -> string
val sys_command : string -> unit
Analyse.Utils
Miscellaneous types and utility functions used throughout the analyse code
type natural = Utils.Sym.t
TODO: Maybe just use Z.t everywhere (it's shorter)
type addr = natural
machine address
val pp_addr : natural -> string
Print the time this function call took. The string is just for the printed message
val esc : Types.ppmode -> string -> string
Analyse
include Base
module Base : sig ... end
module Collected : sig ... end
collect the various test analysis data
module DwarfLineInfo : sig ... end
post-process DWARF source line info
module Elf : sig ... end
module ElfTypes : sig ... end
type of collected ELF-file data from linksem
module Globals : sig ... end
module Pp : sig ... end
module Types : sig ... end
types shared between analyse* and the read-dwarf top-level
module Utils : sig ... end
Miscellaneous types and utility functions used throughout the analyse code
Analyse
include module type of struct include Base end
module Base : sig ... end
module CallGraph : sig ... end
compute call-graph
module Collected : sig ... end
collect the various test analysis data
module DwarfLineInfo : sig ... end
post-process DWARF source line info
module Elf : sig ... end
pp symbol map
module ElfTypes : sig ... end
type of collected ELF-file data from linksem
module Globals : sig ... end
module Pp : sig ... end
render collected analysis data to text or css
module Types : sig ... end
types shared between analyse* and the read-dwarf top-level
module Utils : sig ... end
Miscellaneous types and utility functions used throughout the analyse code
Arch
This module adds some code that is related to the Architecture specific modules but is in itself architecture independent.
include Sig
type func_abi
=
{
init : State.t -> State.t; | Gives the initial state for verifying the function, from a given global register state. Only global registers are kept. |
}
Describe the ABI of a function
This is a record because I expect to add many other fields later.
type dwarf_reg_map
= State.Reg.t array
The map of dwarf register: Which register number map to which ISA register
val supports : Config.Arch.t -> bool
Tells if this Arch module supports this architecture
val init : Config.Arch.t -> unit
If this arch module supports
the architecture, then initialize read-dwarf state using this architecture
val initialized : unit -> Config.Arch.t option
Return Some(arch)
is the loaded arch is arch
and None
if nothing is loaded yet.
val module_name : string
The name of the arch module. Must be the name of the module i.e. Config.arch_module
val loaded_name : string
For dynamic arch module, the name of the dynamically loaded module. Otherwise module_name
val dwarf_reg_map : unit -> dwarf_reg_map
Get the register map of the architecture
val is_local : State.Reg.t -> bool
Tell if a register is local for the ABI
val nop : unit -> Utils.BytesSeq.t
Give the opcode of the nop instruction (For Sail/Isla initialisation
val pc : unit -> State.Reg.t
Give the register index for the program counter
val sp : unit -> State.Reg.t
Give the register index for the stack pointer
val assemble_to_elf : string -> string
Take an instruction string and give the name of an temporary ELF file created that contains the instruction at symbol instr.
val split_into_instrs : Utils.BytesSeq.t -> Utils.BytesSeq.t list
Split a byte-sequence into a list of instructions.
val is_ret : Utils.BytesSeq.t -> bool
Tell if an instruction is a return instruction.
val is_cmp : Utils.BytesSeq.t -> (State.Reg.t * Utils.BitVec.t) option
Tell if an instruction is a compare instruction. Returns Some (reg,bv)
where the contents of reg
are compared against the value bv
if it is and None
if not.
val is_bl : Utils.BytesSeq.t -> Utils.BitVec.t option
Tell if an instruction is an (unconditional) branch on immediate with link instructions. Returns Some bv
where bv
is the offset (from the address of this instruction, in the range +/-128MB) that is branched to if it is and None
if not.
val ensure_loaded : Config.Arch.t -> unit
Ensure that the right architecture type is loaded
val pp_api : func_api -> Utils.Pp.document
val get : unit -> Config.Arch.t
Get the initialized architecture type. Fails (Failure
) if not architecture was loaded
val get_config : unit -> Config.File.ArchConf.t
Get the configuration for the initialized architecture
val get_isla_config : unit -> Config.File.ArchConf.Isla.t
Get the Isla configuration for the initialized architecture
val load_elf_arch : Elf.File.t -> unit
Load the architecture of this File
Arch
This module adds some code that is related to the Architecture specific modules but is in itself architecture independent.
include module type of struct include Sig end
Describe the C API of a function
type func_abi = Sig.func_abi = {
init : State.t -> State.t;
Gives the initial state for verifying the function, from a given global register state. Only global registers are kept.
*)}
Describe the ABI of a function
This is a record because I expect to add many other fields later.
type dwarf_reg_map = State.Reg.t array
The map of dwarf register: Which register number map to which ISA register
val supports : Config.Arch.t -> bool
Tells if this Arch module supports this architecture
val init : Config.Arch.t -> unit
If this arch module supports
the architecture, then initialize read-dwarf state using this architecture
val initialized : unit -> Config.Arch.t option
Return Some(arch)
is the loaded arch is arch
and None
if nothing is loaded yet.
The name of the arch module. Must be the name of the module i.e. Config.arch_module
For dynamic arch module, the name of the dynamically loaded module. Otherwise module_name
val dwarf_reg_map : unit -> dwarf_reg_map
Get the register map of the architecture
val is_local : State.Reg.t -> bool
Tell if a register is local for the ABI
val nop : unit -> Utils.BytesSeq.t
Give the opcode of the nop instruction (For Sail/Isla initialisation
val pc : unit -> State.Reg.t
Give the register index for the program counter
val sp : unit -> State.Reg.t
Give the register index for the stack pointer
Take an instruction string and give the name of an temporary ELF file created that contains the instruction at symbol instr.
val split_into_instrs : Elf.Symbol.data -> Elf.Symbol.data list
Split a byte-sequence into a list of instructions.
val is_ret : Utils.BytesSeq.t -> bool
Tell if an instruction is a return instruction.
val is_cmp : Utils.BytesSeq.t -> (State.Reg.t * Utils.BitVec.t) option
Tell if an instruction is a compare instruction. Returns Some (reg,bv)
where the contents of reg
are compared against the value bv
if it is and None
if not.
val is_bl : Utils.BytesSeq.t -> Utils.BitVec.t option
Tell if an instruction is an (unconditional) branch on immediate with link instructions. Returns Some bv
where bv
is the offset (from the address of this instruction, in the range +/-128MB) that is branched to if it is and None
if not.
val ensure_loaded : Config.Arch.t -> unit
Ensure that the right architecture type is loaded
val pp_api : func_api -> Utils.Pp.document
val get : unit -> Config.Arch.t
Get the initialized architecture type. Fails (Failure
) if not architecture was loaded
val get_config : unit -> Config.File.ArchConf.t
Get the configuration for the initialized architecture
val get_isla_config : unit -> Config.File.ArchConf.Isla.t
Get the Isla configuration for the initialized architecture
val load_elf_arch : Elf.File.t -> unit
Load the architecture of this Elf.File
Here I will present how to describe a symbolic state of a specific architecture in read-dwarf.
The architectures are listed by the Config.Arch.t
enumeration. Not all architectures in the enumeration have the full support, but an architecture must be in the enumeration to be supported. Feel free to add others. This enumeration is used everywhere to refer to various architectures.
read-dwarf uses Dune's virtual modules to parameterise on the architecture, though currently there is only one supported: aarch64
. Any executable (including inline test executables) must therefore be given valid implementation of the virtual module.
The signature for the architecture is in Sig
, the only module in a Dune library called sig
; an implementation is in src/arch/aarch64/sig.ml
, as part of a library called sig_aarch64
. Because Arch
relies on Sig
, it too becomes virtual, and so does every module that uses Arch
. See src/isla/dune
to see how to integrate this for running inline tests, otherwise simply add sig_aarch64
(or any other implementation module) to the libraries
Dune stanza when defining an executable (see src/bin/dune
and src/tests/dune
for examples).
A lot of architecture specific things, like the function call ABI, the physical memory size and some other things are in the Arch
module. However some things are not. For example the list of registers and their SMT types (Ast.ty
) are managed by the State.Reg
module. This is because the register list is not statically hard-coded. It is deduced at run-time from isla, which means that we do not need to maintain a list of system register anywhere. Obviously the Arch
module need to know about some register to manage the ABI, so those registers will be introduced by Arch
, but all the other register are only introduced if an instruction touching them is used. This means that if a system register is never used, it's exactly the same as if doesn't exist for read-dwarf.
Full architectural state are represented by value of type State.t
, those value are symbolic and actually represent set of concrete state. This is explained in State
it self.
All state are derived from other state using State.copy
, apart from the first one which should be Run.Init.state
. When calling that function, Isla will be called on a dummy instruction to get the isla initialization sequence. Currently the Run.Init
module just considers the Isla start state as the initial state. To get the initial state at function entry, only must get the ABI of the function (of type Arch.func_abi
) and then call Arch.func_abi.init
Here I will present how to describe a symbolic state of a specific architecture in read-dwarf.
The architectures are listed by the Config.Arch.t
enumeration. Not all architectures in the enumeration have the full support, but an architecture must be in the enumeration to be supported. Feel free to add others. This enumeration is used everywhere to refer to various architectures.
read-dwarf uses Dune's virtual modules to parameterise on the architecture, though currently there is only one supported: aarch64
. Any executable (including inline test executables) must therefore be given valid implementation of the virtual module.
The signature for the architecture is in Sig
, the only module in a Dune library called sig
; an implementation is in src/arch/aarch64/sig.ml
, as part of a library called sig_aarch64
. Because Arch
relies on Sig
, it too becomes virtual, and so does every module that uses Arch
. See src/isla/dune
to see how to integrate this for running inline tests, otherwise simply add sig_aarch64
(or any other implementation module) to the libraries
Dune stanza when defining an executable (see src/bin/dune
and src/tests/dune
for examples).
A lot of architecture specific things, like the function call ABI, the physical memory size and some other things are in the Arch
module. However some things are not. For example the list of registers and their SMT types (Ast.ty
) are managed by the State.Reg
module. This is because the register list is not statically hard-coded. It is deduced at run-time from isla, which means that we do not need to maintain a list of system register anywhere. Obviously the Arch
module need to know about some register to manage the ABI, so those registers will be introduced by Arch
, but all the other register are only introduced if an instruction touching them is used. This means that if a system register is never used, it's exactly the same as if doesn't exist for read-dwarf.
Full architectural state are represented by value of type State.t
, those value are symbolic and actually represent set of concrete state. This is explained in State
it self.
All state are derived from other state using State.copy
, apart from the first one which should be Run.Init.state
. When calling that function, Isla will be called on a dummy instruction to get the isla initialization sequence. Currently the Run.Init
module just considers the Isla start state as the initial state. To get the initial state at function entry, only must get the ABI of the function (of type Arch.func_abi
) and then call Arch.func_abi.init
Base.Size
include AstGen.Def.Size
type t
=
| B8 |
| B16 |
| B32 |
| B64 |
The possible sizes for memory accesses. It may be necessary to add B128
at some point
val of_bytes : int -> t
Create a size value from a valid size in byte
val of_bits : int -> t
Create a size value from a valid size in bits
val to_bytes : t -> int
Get the byte size corresponding to that value
val to_bits : t -> int
Get the bits size corresponding to that value
val equal : 'a -> 'a -> bool
val pp_bytes : t -> Utils.Pp.document
Pretty-print a size as just the byte number
val pp_bits : t -> PPrintEngine.document
Pretty print a size at "16bits" for example
Base.Size
include module type of struct include AstGen.Def.Size end
The possible sizes for memory accesses.
val of_bytes : int -> t
Create a size value from a valid size in byte
val of_bits : int -> t
Create a size value from a valid size in bits
val to_bytes : t -> int
Get the byte size corresponding to that value
val to_bits : t -> int
Get the bits size corresponding to that value
val pp_bytes : t -> Utils.Pp.document
Pretty-print a size as just the byte number
val pp_bits : t -> Utils.Pp.document
Pretty print a size at "16bits" for example
Ast.Base
The main module to use the AST of expression and SMT operation for a more generic overview of the AST, see SymbolicExpressions
.
include AstGen.Def
include AstGen.Ott
type flag
= string
type enum
= int * int
type 'm binmem
=
| Select of 'm |
| Store of 'm |
type bvarith
=
| Bvuremi |
| Bvsremi |
| Bvsmodi |
| Bvnand |
| Bvnor |
| Bvxnor |
| Bvsub |
| Bvudiv |
| Bvudivi |
| Bvsdiv |
| Bvsdivi |
| Bvurem |
| Bvsrem |
| Bvsmod |
| Bvshl |
| Bvlshr |
| Bvashr |
type bvcomp
=
| Bvult |
| Bvslt |
| Bvule |
| Bvsle |
| Bvuge |
| Bvsge |
| Bvugt |
| Bvsgt |
type bvmanyarith
=
| Bvand |
| Bvor |
| Bvxor |
| Bvadd |
| Bvmul |
type unop
=
| Not |
| Bvnot |
| Bvredand |
| Bvredor |
| Bvneg |
| Extract of int * int |
| ZeroExtend of int |
| SignExtend of int |
type 'm binop
=
| Binmem of 'm binmem |
| Eq |
| Bvarith of bvarith |
| Bvcomp of bvcomp |
type manyop
=
| And |
| Or |
| Bvmanyarith of bvmanyarith |
| Concat |
type ('a, 'v, 'b, 'm) exp
=
| Var of 'v * 'a |
| Bound of 'b * 'a |
| Bits of Utils.BitVec.t * 'a |
| Bool of bool * 'a |
| Enum of enum * 'a |
| Unop of unop * ('a, 'v, 'b, 'm) exp * 'a |
| Binop of 'm binop * ('a, 'v, 'b, 'm) exp * ('a, 'v, 'b, 'm) exp * 'a |
| Manyop of manyop * ('a, 'v, 'b, 'm) exp list * 'a |
| Vec of ('a, 'v, 'b, 'm) exp list * 'a |
| Ite of ('a, 'v, 'b, 'm) exp * ('a, 'v, 'b, 'm) exp * ('a, 'v, 'b, 'm) exp * 'a |
| Let of 'b * ('a, 'v, 'b, 'm) exp * ('b * ('a, 'v, 'b, 'm) exp) list * ('a, 'v, 'b, 'm) exp * 'a |
type 'm ty
=
| Ty_Mem of 'm |
| Ty_Bool |
| Ty_BitVec of int |
| Ty_Enum of int |
| Ty_Array of 'm ty * 'm ty |
type ('a, 'v, 'b, 'm) smt
=
| DeclareConst of 'v * 'm ty |
| DefineConst of 'v * ('a, 'v, 'b, 'm) exp |
| Assert of ('a, 'v, 'b, 'm) exp |
| Simplify of ('a, 'v, 'b, 'm) exp * (string * bool) list |
| Push |
| Pop |
| GetVersion |
| CheckSat |
| Exit |
type ('a, 'v, 'b, 'm) smt_ans
=
| Error of string |
| Version of string |
| Sat |
| Unsat |
| Unknown |
| Unsupported |
| Exp of ('a, 'v, 'b, 'm) exp |
type no
=
Generic empty type. This kind of type is explained here. In particular, when this type appear in a match case, the match case can contain a refutation pattern .
to indicate that this impossible.
A refutation pattern also work with higher-level constructors, for example in this case:
type a = A of int | B of no * int
+Base (read-dwarf.Ast.Base) Module Ast.Base
The main module to use the AST of expression and SMT operation for a more generic overview of the AST, see SymbolicExpressions
.
include module type of struct include AstGen.Def end
include module type of struct include AstGen.Ott end
type bvarith = AstGen.Ott.bvarith =
type 'm binop = 'm AstGen.Ott.binop =
type unop = AstGen.Ott.unop =
type ('a, 'v, 'b, 'm) exp = ('a, 'v, 'b, 'm) AstGen.Ott.exp =
| Var of 'v * 'a
| Bound of 'b * 'a
| Bits of Utils.BitVec.t * 'a
| Bool of bool * 'a
| Enum of enum * 'a
| Unop of unop * ('a, 'v, 'b, 'm) exp * 'a
| Binop of 'm binop * ('a, 'v, 'b, 'm) exp * ('a, 'v, 'b, 'm) exp * 'a
| Manyop of manyop * ('a, 'v, 'b, 'm) exp list * 'a
| Vec of ('a, 'v, 'b, 'm) exp list * 'a
| Ite of ('a, 'v, 'b, 'm) exp * ('a, 'v, 'b, 'm) exp * ('a, 'v, 'b, 'm) exp * 'a
| Let of 'b * ('a, 'v, 'b, 'm) exp
+ * ('b * ('a, 'v, 'b, 'm) exp) list
+ * ('a, 'v, 'b, 'm) exp
+ * 'a
type 'm ty = 'm AstGen.Ott.ty =
type ('a, 'v, 'b, 'm) smt = ('a, 'v, 'b, 'm) AstGen.Ott.smt =
type ('a, 'v, 'b, 'm) smt_ans = ('a, 'v, 'b, 'm) AstGen.Ott.smt_ans =
| Error of string
| Version of string
| Sat
| Unsat
| Unknown
| Unsupported
| Exp of ('a, 'v, 'b, 'm) exp
auxiliary functions on the new list types
library functions
subrules
auxiliary functions
free variables
substitutions
context application
definitions
type no = AstGen.Def.no = |
Generic empty type. This kind of type is explained here. In particular, when this type appear in a match case, the match case can contain a refutation pattern .
to indicate that this impossible.
A refutation pattern also work with higher-level constructors, for example in this case:
type a = A of int | B of no * int
let f : a -> int = function
| A i -> i
- | B _ -> .
It also work in product types:
let f : no * int -> unit = function _ -> .
However it doesn't work for sum types: the no
type need to appear directly in the pattern:
type complex_empty = A of no | B of no
+ | B _ -> .
It also work in product types:
let f : no * int -> unit = function _ -> .
However it doesn't work for sum types: the no
type need to appear directly in the pattern:
type complex_empty = A of no | B of no
let f : complex_empty = function _ -> . (* does not compile *)
-let f : complex_empty = function A _ | B _ -> . (* compiles *)
In case this behavior is needed, there Destructors in Ast
. See this section to see how they are used.
type lrng
= Isla_lang.AST.lrng
The type of an expression range. This imported from Isla to avoid having two incompatible types.
This represent a range in a source file, so generally this is a pair of loc
, but it may also be Unknown
.
module Size = AstGen.Def.Size
Size
module Size : sig ... end
Parsing
module Parser = AstGen.Parser
module Lexer = AstGen.Lexer
exception
ParseError of loc * string
Exception that represent an Isla parsing error
exception
LexError of loc * string
Exception that represent an Isla lexing error
type lexbuf
= Stdlib.Lexing.lexbuf
type lexer
= lexbuf -> Parser.token
type 'a parser
= lexer -> lexbuf -> 'a
val parse : 'a parser -> ('b -> lexbuf) -> ?filename:string -> 'b -> 'a
Parse a single Isla instruction output from a Lexing.lexbuf
val from_string : string -> Stdlib.Lexing.lexbuf
val from_channel : Stdlib.in_channel -> Stdlib.Lexing.lexbuf
val parse_exp_string : ?filename:string -> string -> (AstGen.Def.lrng, string, string, AstGen.Def.Size.t) AstGen.Ott.exp
Parse a single Isla expression from a string
val parse_exp_channel : ?filename:string -> Stdlib.in_channel -> (AstGen.Def.lrng, string, string, AstGen.Def.Size.t) AstGen.Ott.exp
Parse a single Isla expression from a channel
val parse_smt_ans_string : ?filename:string -> string -> AstGen.Def.rsmt_ans
Parse a single Isla expression from a string
val parse_smt_ans_channel : ?filename:string -> Stdlib.in_channel -> AstGen.Def.rsmt_ans
Parse a single Isla expression from a channel
include AstGen.Parser_pp
val pp_raw_bvar : string -> PPrintEngine.document
val pp_raw_bvf : Utils.BitVec.t -> PPrintEngine.document
val pp_raw_flag : string -> PPrintEngine.document
val pp_raw_vvar : int -> PPrintEngine.document
val pp_raw_name : string -> PPrintEngine.document
val pp_raw_enum_ty : int -> PPrintEngine.document
val pp_raw_enum : AstGen.Ott.enum -> PPrintEngine.document
val pp_raw_int : int -> PPrintEngine.document
val pp_raw_bvi : int -> PPrintEngine.document
val pp_raw_bv : string -> PPrintEngine.document
val pp_raw_str : string -> PPrintEngine.document
val pp_raw_mem_size : AstGen.Def.Size.t -> Utils.Pp.document
val pp_raw_binmem : AstGen.Def.Size.t AstGen.Ott.binmem -> PPrintEngine.document
val pp_raw_var : ('a -> PPrintEngine.document) -> 'a -> PPrintEngine.document
val pp_raw_bbvar : string -> PPrintEngine.document
val pp_raw_bind : ('a -> PPrintEngine.document) -> (string * ('b, 'a, string, AstGen.Def.Size.t) AstGen.Ott.exp) -> PPrintEngine.document
val pp_raw_exp : ('a -> PPrintEngine.document) -> ('b, 'a, string, AstGen.Def.Size.t) AstGen.Ott.exp -> PPrintEngine.document
val pp_raw_fbool : (string * bool) -> PPrintEngine.document
val pp_raw_smt : ('a -> PPrintEngine.document) -> ('b, 'a, string, AstGen.Def.Size.t) AstGen.Ott.smt -> PPrintEngine.document
val pp_raw_smts : ('a -> PPrintEngine.document) -> ('b, 'a, string, AstGen.Def.Size.t) AstGen.Ott.smt list -> PPrintEngine.document
val pp_raw_smt_ans : ('a -> PPrintEngine.document) -> ('b, 'a, string, AstGen.Def.Size.t) AstGen.Ott.smt_ans -> PPrintEngine.document
val pp_raw_ty : AstGen.Def.Size.t AstGen.Ott.ty -> PPrintEngine.document
val pp_raw_bool : bool -> PPrintEngine.document
val pp_raw_unop : AstGen.Ott.unop -> PPrintEngine.document
val pp_raw_bvarith : AstGen.Ott.bvarith -> PPrintEngine.document
val pp_raw_bvcomp : AstGen.Ott.bvcomp -> PPrintEngine.document
val pp_raw_binop : AstGen.Def.Size.t AstGen.Ott.binop -> PPrintEngine.document
val pp_raw_bvmanyarith : AstGen.Ott.bvmanyarith -> PPrintEngine.document
val pp_raw_manyop : AstGen.Ott.manyop -> PPrintEngine.document
val pp_bvar : string -> PPrintEngine.document
val pp_bvf : Utils.BitVec.t -> PPrintEngine.document
val pp_flag : string -> PPrintEngine.document
val pp_vvar : int -> PPrintEngine.document
val pp_name : string -> PPrintEngine.document
val pp_enum_ty : int -> PPrintEngine.document
val pp_enum : AstGen.Ott.enum -> PPrintEngine.document
val pp_int : int -> PPrintEngine.document
val pp_bvi : int -> PPrintEngine.document
val pp_bv : string -> PPrintEngine.document
val pp_str : string -> PPrintEngine.document
val pp_j : int -> string
val pp_mem_size : AstGen.Def.Size.t -> Utils.Pp.document
val pp_binmem : AstGen.Def.Size.t AstGen.Ott.binmem -> PPrintEngine.document
val pp_var : ('a -> PPrintEngine.document) -> 'a -> PPrintEngine.document
val pp_bbvar : string -> PPrintEngine.document
val pp_bind : ('a -> PPrintEngine.document) -> (string * ('b, 'a, string, AstGen.Def.Size.t) AstGen.Ott.exp) -> PPrintEngine.document
val pp_exp : ('a -> PPrintEngine.document) -> ('b, 'a, string, AstGen.Def.Size.t) AstGen.Ott.exp -> PPrintEngine.document
val pp_fbool : (string * bool) -> PPrintEngine.document
val pp_smt : ('a -> PPrintEngine.document) -> ('b, 'a, string, AstGen.Def.Size.t) AstGen.Ott.smt -> PPrintEngine.document
val pp_smts : ('a -> PPrintEngine.document) -> ('b, 'a, string, AstGen.Def.Size.t) AstGen.Ott.smt list -> PPrintEngine.document
val pp_smt_ans : ('a -> PPrintEngine.document) -> ('b, 'a, string, AstGen.Def.Size.t) AstGen.Ott.smt_ans -> PPrintEngine.document
val pp_ty : AstGen.Def.Size.t AstGen.Ott.ty -> PPrintEngine.document
val pp_bool : bool -> PPrintEngine.document
val pp_unop : AstGen.Ott.unop -> PPrintEngine.document
val pp_bvarith : AstGen.Ott.bvarith -> PPrintEngine.document
val pp_bvcomp : AstGen.Ott.bvcomp -> PPrintEngine.document
val pp_binop : AstGen.Def.Size.t AstGen.Ott.binop -> PPrintEngine.document
val pp_bvmanyarith : AstGen.Ott.bvmanyarith -> PPrintEngine.document
val pp_manyop : AstGen.Ott.manyop -> PPrintEngine.document
Analysers
All function that start with is_
val is_atomic : ('a, 'b, 'c, 'd) exp -> bool
Destructors
Aparently I overestimated ocaml type-system in it's handling of empty types.
Here are some function to destroy empty types.
Expectors
Functions to assert that a specific constructor is used and get the value
val expect_bits : ('a, 'b, 'c, 'd) exp -> Utils.BitVec.t
val ty_expect_bv : 'a ty -> int
Construtors
Comparisons
\ No newline at end of file
+let f : complex_empty = function A _ | B _ -> . (* compiles *)
In case this behavior is needed, there Destructors in Ast
. See this section to see how they are used.
The type of an expression range. This imported from Isla to avoid having two incompatible types.
This represent a range in a source file, so generally this is a pair of loc
, but it may also be Unknown
.
type rexp = (lrng, string, string, AstGen.Def.Size.t) exp
Raw expression coming out of the parser
type rty = AstGen.Def.Size.t ty
Raw type coming out of the parser
type rsmt = (lrng, string, string, AstGen.Def.Size.t) smt
Raw SMT command coming out of the parser
type rsmt_ans = (lrng, string, string, AstGen.Def.Size.t) smt_ans
Raw SMT answer coming out of the parser
module Size : sig ... end
module Parser = AstGen.Parser
module Lexer = AstGen.Lexer
exception ParseError of loc * string
Exception that represent an Isla parsing error
exception LexError of loc * string
Exception that represent an Isla lexing error
type lexer = lexbuf -> Parser.token
Parse a single Isla instruction output from a Lexing.lexbuf
val parse_exp_string :
+ ?filename:string ->
+ string ->
+ (AstGen.Def.lrng, string, string, AstGen.Def.Size.t) AstGen.Ott.exp
Parse a single Isla expression from a string
val parse_exp_channel :
+ ?filename:string ->
+ Stdlib.in_channel ->
+ (AstGen.Def.lrng, string, string, AstGen.Def.Size.t) AstGen.Ott.exp
Parse a single Isla expression from a channel
val parse_smt_ans_string : ?filename:string -> string -> AstGen.Def.rsmt_ans
Parse a single Isla expression from a string
val parse_smt_ans_channel :
+ ?filename:string ->
+ Stdlib.in_channel ->
+ AstGen.Def.rsmt_ans
Parse a single Isla expression from a channel
include module type of struct include AstGen.Parser_pp end
val pp_raw_bvf : Utils.BitVec.t -> PPrint.document
val pp_raw_enum : AstGen.Ott.enum -> PPrint.document
val pp_raw_mem_size : AstGen.Def.Size.t -> Utils.Pp.document
val pp_raw_binmem : AstGen.Def.Size.t AstGen.Ott.binmem -> PPrint.document
val pp_raw_bind :
+ ('a -> PPrint.document) ->
+ (string * ('b, 'a, string, AstGen.Def.Size.t) AstGen.Ott.exp) ->
+ PPrint.document
val pp_raw_exp :
+ ('a -> PPrint.document) ->
+ ('b, 'a, string, AstGen.Def.Size.t) AstGen.Ott.exp ->
+ PPrint.document
val pp_raw_smt :
+ ('a -> PPrint.document) ->
+ ('b, 'a, string, AstGen.Def.Size.t) AstGen.Ott.smt ->
+ PPrint.document
val pp_raw_smts :
+ ('a -> PPrint.document) ->
+ ('b, 'a, string, AstGen.Def.Size.t) AstGen.Ott.smt list ->
+ PPrint.document
val pp_raw_smt_ans :
+ ('a -> PPrint.document) ->
+ ('b, 'a, string, AstGen.Def.Size.t) AstGen.Ott.smt_ans ->
+ PPrint.document
val pp_raw_ty : AstGen.Def.Size.t AstGen.Ott.ty -> PPrint.document
val pp_raw_unop : AstGen.Ott.unop -> PPrint.document
val pp_raw_bvarith : AstGen.Ott.bvarith -> PPrint.document
val pp_raw_bvcomp : AstGen.Ott.bvcomp -> PPrint.document
val pp_raw_binop : AstGen.Def.Size.t AstGen.Ott.binop -> PPrint.document
val pp_raw_bvmanyarith : AstGen.Ott.bvmanyarith -> PPrint.document
val pp_raw_manyop : AstGen.Ott.manyop -> PPrint.document
val pp_bvf : Utils.BitVec.t -> PPrint.document
val pp_enum : AstGen.Ott.enum -> PPrint.document
val pp_mem_size : AstGen.Def.Size.t -> Utils.Pp.document
val pp_binmem : AstGen.Def.Size.t AstGen.Ott.binmem -> PPrint.document
val pp_bind :
+ ('a -> PPrint.document) ->
+ (string * ('b, 'a, string, AstGen.Def.Size.t) AstGen.Ott.exp) ->
+ PPrint.document
val pp_exp :
+ ('a -> PPrint.document) ->
+ ('b, 'a, string, AstGen.Def.Size.t) AstGen.Ott.exp ->
+ PPrint.document
val pp_smt :
+ ('a -> PPrint.document) ->
+ ('b, 'a, string, AstGen.Def.Size.t) AstGen.Ott.smt ->
+ PPrint.document
val pp_smts :
+ ('a -> PPrint.document) ->
+ ('b, 'a, string, AstGen.Def.Size.t) AstGen.Ott.smt list ->
+ PPrint.document
val pp_smt_ans :
+ ('a -> PPrint.document) ->
+ ('b, 'a, string, AstGen.Def.Size.t) AstGen.Ott.smt_ans ->
+ PPrint.document
val pp_ty : AstGen.Def.Size.t AstGen.Ott.ty -> PPrint.document
val pp_unop : AstGen.Ott.unop -> PPrint.document
val pp_bvarith : AstGen.Ott.bvarith -> PPrint.document
val pp_bvcomp : AstGen.Ott.bvcomp -> PPrint.document
val pp_binop : AstGen.Def.Size.t AstGen.Ott.binop -> PPrint.document
val pp_bvmanyarith : AstGen.Ott.bvmanyarith -> PPrint.document
val pp_manyop : AstGen.Ott.manyop -> PPrint.document
All function that start with is_
val is_atomic : ('a, 'b, 'c, 'd) exp -> bool
Aparently I overestimated ocaml type-system in it's handling of empty types.
Here are some function to destroy empty types.
Functions to assert that a specific constructor is used and get the value
val expect_bits : ('a, 'b, 'c, 'd) exp -> Utils.BitVec.t
val ty_expect_bv : 'a ty -> int
Ast.Manip
This module provide generic facilities of expression and SMT statements provided by Ast
. It is intended to only provide syntactic facilities over Ast
types, in particular Ast.exp
.
In particular it provides generic mapping and iteration function over expressions as well a function allowing to convert between the various Expression type parameter and options.
Warning: due to OCaml type system limitations, mainly issue #9456
, this module is sometimes required to use Obj
.magic in some specific cases. No other module should ever do that. If you need to use Obj.magic
to bypass OCaml type system limitation about Ast
type, add a function here instead.
val annot_exp : ('a, 'v, 'b, 'm) Base.exp -> 'a
Get the annotation of an expression
TODO: This would be much more efficient if the annotation was always the first member of the constructor and not the last (in that case the offset to fetch the annotation do not depend on the constructor index). This may require to modify ott.
This section is filled on demand.
direct_a_map_b
take a function b -> b
and applies it to all b
in a
, non-recursively. Then a new a with the same structure is formed.
direct_a_iter_b
take a function b -> unit
and applies it to all b
in a
, non-recursively.
val direct_exp_map_exp : (('a, 'v, 'b, 'm) Base.exp -> ('a, 'v, 'b, 'm) Base.exp) -> ('a, 'v, 'b, 'm) Base.exp -> ('a, 'v, 'b, 'm) Base.exp
val direct_exp_iter_exp : (('a, 'v, 'b, 'm) Base.exp -> unit) -> ('a, 'v, 'b, 'm) Base.exp -> unit
val direct_exp_fold_left_exp : ('a -> ('b, 'c, 'd, 'e) Base.exp -> 'a) -> 'a -> ('b, 'c, 'd, 'e) Base.exp -> 'a
val direct_exp_for_all_exp : (('a, 'b, 'c, 'd) Base.exp -> bool) -> ('a, 'b, 'c, 'd) Base.exp -> bool
val direct_exp_exists_exp : (('a, 'b, 'c, 'd) Base.exp -> bool) -> ('a, 'b, 'c, 'd) Base.exp -> bool
This section is filled on demand.
a_map_b
take a function b -> b
and applies it to all the b
in a
, and do that recursively on all b that appear directly or indirectly in a
a_iter_b
take a function b -> unit
and applies it to all the b
in a
, and do that recursively
Doing this when a = b
is not well defined, and can be easily done using the direct version from previous section.
val exp_iter_var : ('v -> unit) -> ('a, 'v, 'b, 'm) Base.exp -> unit
Iterate a function on all the variable of an expression
All of those function convert the underlying variable type through the AST. They cannot be the usual map function because they change the type
This section allow to go from expression without let-bindings to expression with them and vice-versa.
val allow_lets : ('a, 'v, Base.no, 'm) Base.exp -> ('a, 'v, 'b, 'm) Base.exp
Allow bound variables and lets in an expression. This operation is a no-op and has no runtime cost, it's just a type change.
val smt_allow_lets : ('a, 'v, Base.no, 'm) Base.smt -> ('a, 'v, 'b, 'm) Base.smt
Same as allow_lets
but for the smt
type
val unfold_lets : b1 a v b2 m. ?context:('b1, ('a, 'v, 'b2, 'm) Base.exp) Stdlib.Hashtbl.t -> ('a, 'v, 'b1, 'm) Base.exp -> ('a, 'v, 'b2, 'm) Base.exp
Unfold all lets. There are no remaining lets in the output, Therefore the output type of let binding can be anything including Ast.no
. In particular doing allow_lets
after this function is useless
This section allow to go from expression without memory operations to expression with them and vice-versa.
val allow_mem : ('a, 'v, 'b, Base.no) Base.exp -> ('a, 'v, 'b, 'm) Base.exp
Allow memory operations in an expression.
val check_no_mem : ('a, 'v, 'b, 'm) Base.exp -> bool
Check that not memory operation take place in that expression. Return true
if that's the case and false
otherwise.
This is not resilient to change of type: If a new memory constructor is added, then this function will be wrong
val expect_no_mem : ?handler:(unit -> ('a, 'v, 'b, 'm2) Base.exp) -> ('a, 'v, 'b, 'm1) Base.exp -> ('a, 'v, 'b, 'm2) Base.exp
Expect that an exp
has no memory constructor, and then return it with memory removed from the type. Throws Failure
if the value had memory constructors.
This is not resilient to change of type, If a new memory constructor is added, then this function will be unsound.
TODO: Find a way to make it resilient
Ast.Manip
This module provide generic facilities of expression and SMT statements provided by Ast
. It is intended to only provide syntactic facilities over Ast
types, in particular Ast.exp
.
In particular it provides generic mapping and iteration function over expressions as well a function allowing to convert between the various Expression type parameter and options.
Warning: due to OCaml type system limitations, mainly issue #9456
, this module is sometimes required to use Obj.magic
in some specific cases. No other module should ever do that. If you need to use Obj.magic
to bypass OCaml type system limitation about Ast
type, add a function here instead.
val annot_exp : ('a, 'v, 'b, 'm) Base.exp -> 'a
Get the annotation of an expression
TODO: This would be much more efficient if the annotation was always the first member of the constructor and not the last (in that case the offset to fetch the annotation do not depend on the constructor index). This may require to modify ott.
This section is filled on demand.
direct_a_map_b
take a function b -> b
and applies it to all b
in a
, non-recursively. Then a new a with the same structure is formed.
direct_a_iter_b
take a function b -> unit
and applies it to all b
in a
, non-recursively.
This section is filled on demand.
a_map_b
take a function b -> b
and applies it to all the b
in a
, and do that recursively on all b that appear directly or indirectly in a
a_iter_b
take a function b -> unit
and applies it to all the b
in a
, and do that recursively
Doing this when a = b
is not well defined, and can be easily done using the direct version from previous section.
val exp_iter_var : ('v -> unit) -> ('a, 'v, 'b, 'm) Base.exp -> unit
Iterate a function on all the variable of an expression
val exp_iter_annot : ('a -> unit) -> ('a, 'v, 'b, 'm) Base.exp -> unit
Iterate a function on all the annotations of an expression
All of those function convert the underlying variable type through the AST. They cannot be the usual map function because they change the type
Old alias to make conversion explicit
val exp_var_subst :
+ 'va 'a 'vb 'b 'm. ('va -> 'a -> ('a, 'vb, 'b, 'm) Base.exp) ->
+ ('a, 'va, 'b, 'm) Base.exp ->
+ ('a, 'vb, 'b, 'm) Base.exp
Substitute variable with expression according to substitution function
This section allow to go from expression without let-bindings to expression with them and vice-versa.
Allow bound variables and lets in an expression. This operation is a no-op and has no runtime cost, it's just a type change.
Same as allow_lets
but for the smt
type
val unfold_lets :
+ 'b1 'a 'v 'b2 'm. ?context:('b1, ('a, 'v, 'b2, 'm) Base.exp) Stdlib.Hashtbl.t ->
+ ('a, 'v, 'b1, 'm) Base.exp ->
+ ('a, 'v, 'b2, 'm) Base.exp
Unfold all lets. There are no remaining lets in the output, Therefore the output type of let binding can be anything including Ast.no
. In particular doing allow_lets
after this function is useless
This section allow to go from expression without memory operations to expression with them and vice-versa.
Allow memory operations in an expression.
val check_no_mem : ('a, 'v, 'b, 'm) Base.exp -> bool
Check that not memory operation take place in that expression. Return true
if that's the case and false
otherwise.
This is not resilient to change of type: If a new memory constructor is added, then this function will be wrong
val expect_no_mem :
+ ?handler:(unit -> ('a, 'v, 'b, 'm2) Base.exp) ->
+ ('a, 'v, 'b, 'm1) Base.exp ->
+ ('a, 'v, 'b, 'm2) Base.exp
Expect that an exp
has no memory constructor, and then return it with memory removed from the type. Throws Failure
if the value had memory constructors.
This is not resilient to change of type, If a new memory constructor is added, then this function will be unsound.
TODO: Find a way to make it resilient
Ast
include Base
include AstGen.Def
include AstGen.Ott
type flag
= string
type enum
= int * int
type 'm binmem
=
| Select of 'm |
| Store of 'm |
type bvarith
=
| Bvuremi |
| Bvsremi |
| Bvsmodi |
| Bvnand |
| Bvnor |
| Bvxnor |
| Bvsub |
| Bvudiv |
| Bvudivi |
| Bvsdiv |
| Bvsdivi |
| Bvurem |
| Bvsrem |
| Bvsmod |
| Bvshl |
| Bvlshr |
| Bvashr |
type bvcomp
=
| Bvult |
| Bvslt |
| Bvule |
| Bvsle |
| Bvuge |
| Bvsge |
| Bvugt |
| Bvsgt |
type bvmanyarith
=
| Bvand |
| Bvor |
| Bvxor |
| Bvadd |
| Bvmul |
type unop
=
| Not |
| Bvnot |
| Bvredand |
| Bvredor |
| Bvneg |
| Extract of int * int |
| ZeroExtend of int |
| SignExtend of int |
type 'm binop
=
| Binmem of 'm binmem |
| Eq |
| Bvarith of bvarith |
| Bvcomp of bvcomp |
type manyop
=
| And |
| Or |
| Bvmanyarith of bvmanyarith |
| Concat |
type ('a, 'v, 'b, 'm) exp
=
| Var of 'v * 'a |
| Bound of 'b * 'a |
| Bits of Utils.BitVec.t * 'a |
| Bool of bool * 'a |
| Enum of enum * 'a |
| Unop of unop * ('a, 'v, 'b, 'm) exp * 'a |
| Binop of 'm binop * ('a, 'v, 'b, 'm) exp * ('a, 'v, 'b, 'm) exp * 'a |
| Manyop of manyop * ('a, 'v, 'b, 'm) exp list * 'a |
| Vec of ('a, 'v, 'b, 'm) exp list * 'a |
| Ite of ('a, 'v, 'b, 'm) exp * ('a, 'v, 'b, 'm) exp * ('a, 'v, 'b, 'm) exp * 'a |
| Let of 'b * ('a, 'v, 'b, 'm) exp * ('b * ('a, 'v, 'b, 'm) exp) list * ('a, 'v, 'b, 'm) exp * 'a |
type 'm ty
=
| Ty_Mem of 'm |
| Ty_Bool |
| Ty_BitVec of int |
| Ty_Enum of int |
| Ty_Array of 'm ty * 'm ty |
type ('a, 'v, 'b, 'm) smt
=
| DeclareConst of 'v * 'm ty |
| DefineConst of 'v * ('a, 'v, 'b, 'm) exp |
| Assert of ('a, 'v, 'b, 'm) exp |
| Simplify of ('a, 'v, 'b, 'm) exp * (string * bool) list |
| Push |
| Pop |
| GetVersion |
| CheckSat |
| Exit |
type ('a, 'v, 'b, 'm) smt_ans
=
| Error of string |
| Version of string |
| Sat |
| Unsat |
| Unknown |
| Unsupported |
| Exp of ('a, 'v, 'b, 'm) exp |
type no
=
Generic empty type. This kind of type is explained here. In particular, when this type appear in a match case, the match case can contain a refutation pattern .
to indicate that this impossible.
A refutation pattern also work with higher-level constructors, for example in this case:
type a = A of int | B of no * int
+Ast (read-dwarf.Ast) Module Ast
include module type of struct include Base end
include module type of struct include AstGen.Def end
include module type of struct include AstGen.Ott end
type bvarith = AstGen.Ott.bvarith =
type 'm binop = 'm AstGen.Ott.binop =
type unop = AstGen.Ott.unop =
type ('a, 'v, 'b, 'm) exp = ('a, 'v, 'b, 'm) AstGen.Ott.exp =
| Var of 'v * 'a
| Bound of 'b * 'a
| Bits of Utils.BitVec.t * 'a
| Bool of bool * 'a
| Enum of enum * 'a
| Unop of unop * ('a, 'v, 'b, 'm) exp * 'a
| Binop of 'm binop * ('a, 'v, 'b, 'm) exp * ('a, 'v, 'b, 'm) exp * 'a
| Manyop of manyop * ('a, 'v, 'b, 'm) exp list * 'a
| Vec of ('a, 'v, 'b, 'm) exp list * 'a
| Ite of ('a, 'v, 'b, 'm) exp * ('a, 'v, 'b, 'm) exp * ('a, 'v, 'b, 'm) exp * 'a
| Let of 'b * ('a, 'v, 'b, 'm) exp
+ * ('b * ('a, 'v, 'b, 'm) exp) list
+ * ('a, 'v, 'b, 'm) exp
+ * 'a
type 'm ty = 'm AstGen.Ott.ty =
type ('a, 'v, 'b, 'm) smt = ('a, 'v, 'b, 'm) AstGen.Ott.smt =
type ('a, 'v, 'b, 'm) smt_ans = ('a, 'v, 'b, 'm) AstGen.Ott.smt_ans =
| Error of string
| Version of string
| Sat
| Unsat
| Unknown
| Unsupported
| Exp of ('a, 'v, 'b, 'm) exp
auxiliary functions on the new list types
library functions
subrules
auxiliary functions
free variables
substitutions
context application
definitions
type no = AstGen.Def.no = |
Generic empty type. This kind of type is explained here. In particular, when this type appear in a match case, the match case can contain a refutation pattern .
to indicate that this impossible.
A refutation pattern also work with higher-level constructors, for example in this case:
type a = A of int | B of no * int
let f : a -> int = function
| A i -> i
- | B _ -> .
It also work in product types:
let f : no * int -> unit = function _ -> .
However it doesn't work for sum types: the no
type need to appear directly in the pattern:
type complex_empty = A of no | B of no
+ | B _ -> .
It also work in product types:
let f : no * int -> unit = function _ -> .
However it doesn't work for sum types: the no
type need to appear directly in the pattern:
type complex_empty = A of no | B of no
let f : complex_empty = function _ -> . (* does not compile *)
-let f : complex_empty = function A _ | B _ -> . (* compiles *)
In case this behavior is needed, there Destructors in Ast
. See this section to see how they are used.
type lrng
= Isla_lang.AST.lrng
The type of an expression range. This imported from Isla to avoid having two incompatible types.
This represent a range in a source file, so generally this is a pair of loc
, but it may also be Unknown
.
module Size = AstGen.Def.Size
Size
module Size = Base.Size
Parsing
module Parser = AstGen.Parser
module Lexer = AstGen.Lexer
exception
ParseError of loc * string
Exception that represent an Isla parsing error
exception
LexError of loc * string
Exception that represent an Isla lexing error
type lexbuf
= Stdlib.Lexing.lexbuf
type lexer
= lexbuf -> Parser.token
type 'a parser
= lexer -> lexbuf -> 'a
val parse : 'a parser -> ('b -> lexbuf) -> ?filename:string -> 'b -> 'a
Parse a single Isla instruction output from a Lexing.lexbuf
val from_string : string -> Stdlib.Lexing.lexbuf
val from_channel : Stdlib.in_channel -> Stdlib.Lexing.lexbuf
val parse_exp_string : ?filename:string -> string -> (AstGen.Def.lrng, string, string, AstGen.Def.Size.t) AstGen.Ott.exp
Parse a single Isla expression from a string
val parse_exp_channel : ?filename:string -> Stdlib.in_channel -> (AstGen.Def.lrng, string, string, AstGen.Def.Size.t) AstGen.Ott.exp
Parse a single Isla expression from a channel
val parse_smt_ans_string : ?filename:string -> string -> AstGen.Def.rsmt_ans
Parse a single Isla expression from a string
val parse_smt_ans_channel : ?filename:string -> Stdlib.in_channel -> AstGen.Def.rsmt_ans
Parse a single Isla expression from a channel
include AstGen.Parser_pp
val pp_raw_bvar : string -> PPrintEngine.document
val pp_raw_bvf : Utils.BitVec.t -> PPrintEngine.document
val pp_raw_flag : string -> PPrintEngine.document
val pp_raw_vvar : int -> PPrintEngine.document
val pp_raw_name : string -> PPrintEngine.document
val pp_raw_enum_ty : int -> PPrintEngine.document
val pp_raw_enum : AstGen.Ott.enum -> PPrintEngine.document
val pp_raw_int : int -> PPrintEngine.document
val pp_raw_bvi : int -> PPrintEngine.document
val pp_raw_bv : string -> PPrintEngine.document
val pp_raw_str : string -> PPrintEngine.document
val pp_raw_mem_size : AstGen.Def.Size.t -> Utils.Pp.document
val pp_raw_binmem : AstGen.Def.Size.t AstGen.Ott.binmem -> PPrintEngine.document
val pp_raw_var : ('a -> PPrintEngine.document) -> 'a -> PPrintEngine.document
val pp_raw_bbvar : string -> PPrintEngine.document
val pp_raw_bind : ('a -> PPrintEngine.document) -> (string * ('b, 'a, string, AstGen.Def.Size.t) AstGen.Ott.exp) -> PPrintEngine.document
val pp_raw_exp : ('a -> PPrintEngine.document) -> ('b, 'a, string, AstGen.Def.Size.t) AstGen.Ott.exp -> PPrintEngine.document
val pp_raw_fbool : (string * bool) -> PPrintEngine.document
val pp_raw_smt : ('a -> PPrintEngine.document) -> ('b, 'a, string, AstGen.Def.Size.t) AstGen.Ott.smt -> PPrintEngine.document
val pp_raw_smts : ('a -> PPrintEngine.document) -> ('b, 'a, string, AstGen.Def.Size.t) AstGen.Ott.smt list -> PPrintEngine.document
val pp_raw_smt_ans : ('a -> PPrintEngine.document) -> ('b, 'a, string, AstGen.Def.Size.t) AstGen.Ott.smt_ans -> PPrintEngine.document
val pp_raw_ty : AstGen.Def.Size.t AstGen.Ott.ty -> PPrintEngine.document
val pp_raw_bool : bool -> PPrintEngine.document
val pp_raw_unop : AstGen.Ott.unop -> PPrintEngine.document
val pp_raw_bvarith : AstGen.Ott.bvarith -> PPrintEngine.document
val pp_raw_bvcomp : AstGen.Ott.bvcomp -> PPrintEngine.document
val pp_raw_binop : AstGen.Def.Size.t AstGen.Ott.binop -> PPrintEngine.document
val pp_raw_bvmanyarith : AstGen.Ott.bvmanyarith -> PPrintEngine.document
val pp_raw_manyop : AstGen.Ott.manyop -> PPrintEngine.document
val pp_bvar : string -> PPrintEngine.document
val pp_bvf : Utils.BitVec.t -> PPrintEngine.document
val pp_flag : string -> PPrintEngine.document
val pp_vvar : int -> PPrintEngine.document
val pp_name : string -> PPrintEngine.document
val pp_enum_ty : int -> PPrintEngine.document
val pp_enum : AstGen.Ott.enum -> PPrintEngine.document
val pp_int : int -> PPrintEngine.document
val pp_bvi : int -> PPrintEngine.document
val pp_bv : string -> PPrintEngine.document
val pp_str : string -> PPrintEngine.document
val pp_j : int -> string
val pp_mem_size : AstGen.Def.Size.t -> Utils.Pp.document
val pp_binmem : AstGen.Def.Size.t AstGen.Ott.binmem -> PPrintEngine.document
val pp_var : ('a -> PPrintEngine.document) -> 'a -> PPrintEngine.document
val pp_bbvar : string -> PPrintEngine.document
val pp_bind : ('a -> PPrintEngine.document) -> (string * ('b, 'a, string, AstGen.Def.Size.t) AstGen.Ott.exp) -> PPrintEngine.document
val pp_exp : ('a -> PPrintEngine.document) -> ('b, 'a, string, AstGen.Def.Size.t) AstGen.Ott.exp -> PPrintEngine.document
val pp_fbool : (string * bool) -> PPrintEngine.document
val pp_smt : ('a -> PPrintEngine.document) -> ('b, 'a, string, AstGen.Def.Size.t) AstGen.Ott.smt -> PPrintEngine.document
val pp_smts : ('a -> PPrintEngine.document) -> ('b, 'a, string, AstGen.Def.Size.t) AstGen.Ott.smt list -> PPrintEngine.document
val pp_smt_ans : ('a -> PPrintEngine.document) -> ('b, 'a, string, AstGen.Def.Size.t) AstGen.Ott.smt_ans -> PPrintEngine.document
val pp_ty : AstGen.Def.Size.t AstGen.Ott.ty -> PPrintEngine.document
val pp_bool : bool -> PPrintEngine.document
val pp_unop : AstGen.Ott.unop -> PPrintEngine.document
val pp_bvarith : AstGen.Ott.bvarith -> PPrintEngine.document
val pp_bvcomp : AstGen.Ott.bvcomp -> PPrintEngine.document
val pp_binop : AstGen.Def.Size.t AstGen.Ott.binop -> PPrintEngine.document
val pp_bvmanyarith : AstGen.Ott.bvmanyarith -> PPrintEngine.document
val pp_manyop : AstGen.Ott.manyop -> PPrintEngine.document
Analysers
All function that start with is_
val is_atomic : ('a, 'b, 'c, 'd) exp -> bool
Destructors
Aparently I overestimated ocaml type-system in it's handling of empty types.
Here are some function to destroy empty types.
Expectors
Functions to assert that a specific constructor is used and get the value
val expect_bits : ('a, 'b, 'c, 'd) exp -> Utils.BitVec.t
val ty_expect_bv : 'a ty -> int
Construtors
Comparisons
module Base : sig ... end
The main module to use the AST of expression and SMT operation for a more generic overview of the AST, see SymbolicExpressions
.
In case this behavior is needed, there Destructors in Ast
. See this section to see how they are used.
The type of an expression range. This imported from Isla to avoid having two incompatible types.
This represent a range in a source file, so generally this is a pair of loc
, but it may also be Unknown
.
type rexp = (lrng, string, string, Base.Size.t) exp
Raw expression coming out of the parser
type rty = Base.Size.t ty
Raw type coming out of the parser
type rsmt = (lrng, string, string, Base.Size.t) smt
Raw SMT command coming out of the parser
type rsmt_ans = (lrng, string, string, Base.Size.t) smt_ans
Raw SMT answer coming out of the parser
module Size = Base.Size
module Parser = AstGen.Parser
module Lexer = AstGen.Lexer
exception ParseError of loc * string
Exception that represent an Isla parsing error
exception LexError of loc * string
Exception that represent an Isla lexing error
type lexer = lexbuf -> Parser.token
Parse a single Isla instruction output from a Lexing.lexbuf
val parse_exp_string :
+ ?filename:string ->
+ string ->
+ (AstGen.Def.lrng, string, string, AstGen.Def.Size.t) AstGen.Ott.exp
Parse a single Isla expression from a string
val parse_exp_channel :
+ ?filename:string ->
+ Stdlib.in_channel ->
+ (AstGen.Def.lrng, string, string, AstGen.Def.Size.t) AstGen.Ott.exp
Parse a single Isla expression from a channel
val parse_smt_ans_string : ?filename:string -> string -> AstGen.Def.rsmt_ans
Parse a single Isla expression from a string
val parse_smt_ans_channel :
+ ?filename:string ->
+ Stdlib.in_channel ->
+ AstGen.Def.rsmt_ans
Parse a single Isla expression from a channel
include module type of struct include AstGen.Parser_pp end
val pp_raw_bvf : Utils.BitVec.t -> PPrint.document
val pp_raw_enum : AstGen.Ott.enum -> PPrint.document
val pp_raw_mem_size : AstGen.Def.Size.t -> Utils.Pp.document
val pp_raw_binmem : AstGen.Def.Size.t AstGen.Ott.binmem -> PPrint.document
val pp_raw_bind :
+ ('a -> PPrint.document) ->
+ (string * ('b, 'a, string, AstGen.Def.Size.t) AstGen.Ott.exp) ->
+ PPrint.document
val pp_raw_exp :
+ ('a -> PPrint.document) ->
+ ('b, 'a, string, AstGen.Def.Size.t) AstGen.Ott.exp ->
+ PPrint.document
val pp_raw_smt :
+ ('a -> PPrint.document) ->
+ ('b, 'a, string, AstGen.Def.Size.t) AstGen.Ott.smt ->
+ PPrint.document
val pp_raw_smts :
+ ('a -> PPrint.document) ->
+ ('b, 'a, string, AstGen.Def.Size.t) AstGen.Ott.smt list ->
+ PPrint.document
val pp_raw_smt_ans :
+ ('a -> PPrint.document) ->
+ ('b, 'a, string, AstGen.Def.Size.t) AstGen.Ott.smt_ans ->
+ PPrint.document
val pp_raw_ty : AstGen.Def.Size.t AstGen.Ott.ty -> PPrint.document
val pp_raw_unop : AstGen.Ott.unop -> PPrint.document
val pp_raw_bvarith : AstGen.Ott.bvarith -> PPrint.document
val pp_raw_bvcomp : AstGen.Ott.bvcomp -> PPrint.document
val pp_raw_binop : AstGen.Def.Size.t AstGen.Ott.binop -> PPrint.document
val pp_raw_bvmanyarith : AstGen.Ott.bvmanyarith -> PPrint.document
val pp_raw_manyop : AstGen.Ott.manyop -> PPrint.document
val pp_bvf : Utils.BitVec.t -> PPrint.document
val pp_enum : AstGen.Ott.enum -> PPrint.document
val pp_mem_size : AstGen.Def.Size.t -> Utils.Pp.document
val pp_binmem : AstGen.Def.Size.t AstGen.Ott.binmem -> PPrint.document
val pp_bind :
+ ('a -> PPrint.document) ->
+ (string * ('b, 'a, string, AstGen.Def.Size.t) AstGen.Ott.exp) ->
+ PPrint.document
val pp_exp :
+ ('a -> PPrint.document) ->
+ ('b, 'a, string, AstGen.Def.Size.t) AstGen.Ott.exp ->
+ PPrint.document
val pp_smt :
+ ('a -> PPrint.document) ->
+ ('b, 'a, string, AstGen.Def.Size.t) AstGen.Ott.smt ->
+ PPrint.document
val pp_smts :
+ ('a -> PPrint.document) ->
+ ('b, 'a, string, AstGen.Def.Size.t) AstGen.Ott.smt list ->
+ PPrint.document
val pp_smt_ans :
+ ('a -> PPrint.document) ->
+ ('b, 'a, string, AstGen.Def.Size.t) AstGen.Ott.smt_ans ->
+ PPrint.document
val pp_ty : AstGen.Def.Size.t AstGen.Ott.ty -> PPrint.document
val pp_unop : AstGen.Ott.unop -> PPrint.document
val pp_bvarith : AstGen.Ott.bvarith -> PPrint.document
val pp_bvcomp : AstGen.Ott.bvcomp -> PPrint.document
val pp_binop : AstGen.Def.Size.t AstGen.Ott.binop -> PPrint.document
val pp_bvmanyarith : AstGen.Ott.bvmanyarith -> PPrint.document
val pp_manyop : AstGen.Ott.manyop -> PPrint.document
All function that start with is_
val is_atomic : ('a, 'b, 'c, 'd) exp -> bool
Aparently I overestimated ocaml type-system in it's handling of empty types.
Here are some function to destroy empty types.
Functions to assert that a specific constructor is used and get the value
val expect_bits : ('a, 'b, 'c, 'd) exp -> Utils.BitVec.t
val ty_expect_bv : 'a ty -> int
module Base : sig ... end
The main module to use the AST of expression and SMT operation for a more generic overview of the AST, see SymbolicExpressions
.
Def.Size
type t
=
| B8 |
| B16 |
| B32 |
| B64 |
The possible sizes for memory accesses. It may be necessary to add B128
at some point
val of_bytes : int -> t
Create a size value from a valid size in byte
val of_bits : int -> t
Create a size value from a valid size in bits
val to_bytes : t -> int
Get the byte size corresponding to that value
val to_bits : t -> int
Get the bits size corresponding to that value
val equal : 'a -> 'a -> bool
val pp_bytes : t -> Utils.Pp.document
Pretty-print a size as just the byte number
val pp_bits : t -> PPrintEngine.document
Pretty print a size at "16bits" for example
Def.Size
val of_bytes : int -> t
Create a size value from a valid size in byte
val of_bits : int -> t
Create a size value from a valid size in bits
val to_bytes : t -> int
Get the byte size corresponding to that value
val to_bits : t -> int
Get the bits size corresponding to that value
val pp_bytes : t -> Utils.Pp.document
Pretty-print a size as just the byte number
val pp_bits : t -> Utils.Pp.document
Pretty print a size at "16bits" for example
AstGen.Def
include Ott
type flag
= string
type enum
= int * int
type 'm binmem
=
| Select of 'm |
| Store of 'm |
type bvarith
=
| Bvuremi |
| Bvsremi |
| Bvsmodi |
| Bvnand |
| Bvnor |
| Bvxnor |
| Bvsub |
| Bvudiv |
| Bvudivi |
| Bvsdiv |
| Bvsdivi |
| Bvurem |
| Bvsrem |
| Bvsmod |
| Bvshl |
| Bvlshr |
| Bvashr |
type bvcomp
=
| Bvult |
| Bvslt |
| Bvule |
| Bvsle |
| Bvuge |
| Bvsge |
| Bvugt |
| Bvsgt |
type bvmanyarith
=
| Bvand |
| Bvor |
| Bvxor |
| Bvadd |
| Bvmul |
type unop
=
| Not |
| Bvnot |
| Bvredand |
| Bvredor |
| Bvneg |
| Extract of int * int |
| ZeroExtend of int |
| SignExtend of int |
type 'm binop
=
| Binmem of 'm binmem |
| Eq |
| Bvarith of bvarith |
| Bvcomp of bvcomp |
type manyop
=
| And |
| Or |
| Bvmanyarith of bvmanyarith |
| Concat |
type ('a, 'v, 'b, 'm) exp
=
| Var of 'v * 'a |
| Bound of 'b * 'a |
| Bits of Utils.BitVec.t * 'a |
| Bool of bool * 'a |
| Enum of enum * 'a |
| Unop of unop * ('a, 'v, 'b, 'm) exp * 'a |
| Binop of 'm binop * ('a, 'v, 'b, 'm) exp * ('a, 'v, 'b, 'm) exp * 'a |
| Manyop of manyop * ('a, 'v, 'b, 'm) exp list * 'a |
| Vec of ('a, 'v, 'b, 'm) exp list * 'a |
| Ite of ('a, 'v, 'b, 'm) exp * ('a, 'v, 'b, 'm) exp * ('a, 'v, 'b, 'm) exp * 'a |
| Let of 'b * ('a, 'v, 'b, 'm) exp * ('b * ('a, 'v, 'b, 'm) exp) list * ('a, 'v, 'b, 'm) exp * 'a |
type 'm ty
=
| Ty_Mem of 'm |
| Ty_Bool |
| Ty_BitVec of int |
| Ty_Enum of int |
| Ty_Array of 'm ty * 'm ty |
type ('a, 'v, 'b, 'm) smt
=
| DeclareConst of 'v * 'm ty |
| DefineConst of 'v * ('a, 'v, 'b, 'm) exp |
| Assert of ('a, 'v, 'b, 'm) exp |
| Simplify of ('a, 'v, 'b, 'm) exp * (string * bool) list |
| Push |
| Pop |
| GetVersion |
| CheckSat |
| Exit |
type ('a, 'v, 'b, 'm) smt_ans
=
| Error of string |
| Version of string |
| Sat |
| Unsat |
| Unknown |
| Unsupported |
| Exp of ('a, 'v, 'b, 'm) exp |
type no
=
Generic empty type. This kind of type is explained here. In particular, when this type appear in a match case, the match case can contain a refutation pattern .
to indicate that this impossible.
A refutation pattern also work with higher-level constructors, for example in this case:
type a = A of int | B of no * int
+Def (read-dwarf.AstGen.Def) Module AstGen.Def
include module type of struct include Ott end
type bvarith = Ott.bvarith =
type unop = Ott.unop =
type ('a, 'v, 'b, 'm) exp = ('a, 'v, 'b, 'm) Ott.exp =
| Var of 'v * 'a
| Bound of 'b * 'a
| Bits of Utils.BitVec.t * 'a
| Bool of bool * 'a
| Enum of enum * 'a
| Unop of unop * ('a, 'v, 'b, 'm) exp * 'a
| Binop of 'm binop * ('a, 'v, 'b, 'm) exp * ('a, 'v, 'b, 'm) exp * 'a
| Manyop of manyop * ('a, 'v, 'b, 'm) exp list * 'a
| Vec of ('a, 'v, 'b, 'm) exp list * 'a
| Ite of ('a, 'v, 'b, 'm) exp * ('a, 'v, 'b, 'm) exp * ('a, 'v, 'b, 'm) exp * 'a
| Let of 'b * ('a, 'v, 'b, 'm) exp
+ * ('b * ('a, 'v, 'b, 'm) exp) list
+ * ('a, 'v, 'b, 'm) exp
+ * 'a
type ('a, 'v, 'b, 'm) smt_ans = ('a, 'v, 'b, 'm) Ott.smt_ans =
| Error of string
| Version of string
| Sat
| Unsat
| Unknown
| Unsupported
| Exp of ('a, 'v, 'b, 'm) exp
auxiliary functions on the new list types
library functions
subrules
auxiliary functions
free variables
substitutions
context application
definitions
Generic empty type. This kind of type is explained here. In particular, when this type appear in a match case, the match case can contain a refutation pattern .
to indicate that this impossible.
A refutation pattern also work with higher-level constructors, for example in this case:
type a = A of int | B of no * int
let f : a -> int = function
| A i -> i
- | B _ -> .
It also work in product types:
let f : no * int -> unit = function _ -> .
However it doesn't work for sum types: the no
type need to appear directly in the pattern:
type complex_empty = A of no | B of no
+ | B _ -> .
It also work in product types:
let f : no * int -> unit = function _ -> .
However it doesn't work for sum types: the no
type need to appear directly in the pattern:
type complex_empty = A of no | B of no
let f : complex_empty = function _ -> . (* does not compile *)
-let f : complex_empty = function A _ | B _ -> . (* compiles *)
In case this behavior is needed, there Destructors in Ast
. See this section to see how they are used.
type lrng
= Isla_lang.AST.lrng
The type of an expression range. This imported from Isla to avoid having two incompatible types.
This represent a range in a source file, so generally this is a pair of loc
, but it may also be Unknown
.
module Size : sig ... end
\ No newline at end of file
+let f : complex_empty = function A _ | B _ -> . (* compiles *)In case this behavior is needed, there Destructors in Ast
. See this section to see how they are used.
The type of an expression range. This imported from Isla to avoid having two incompatible types.
This represent a range in a source file, so generally this is a pair of loc
, but it may also be Unknown
.
module Size : sig ... end
AstGen.Lexer
val __ocaml_lex_tables : Stdlib.Lexing.lex_tables
val token : Stdlib.Lexing.lexbuf -> Parser.token
val __ocaml_lex_token_rec : Stdlib.Lexing.lexbuf -> int -> Parser.token
AstGen.Lexer
val token : Stdlib.Lexing.lexbuf -> Parser.token
val __ocaml_lex_token_rec : Stdlib.Lexing.lexbuf -> int -> Parser.token
AstGen.Ott
type flag
= string
type enum
= int * int
type 'm binmem
=
| Select of 'm |
| Store of 'm |
type bvarith
=
| Bvuremi |
| Bvsremi |
| Bvsmodi |
| Bvnand |
| Bvnor |
| Bvxnor |
| Bvsub |
| Bvudiv |
| Bvudivi |
| Bvsdiv |
| Bvsdivi |
| Bvurem |
| Bvsrem |
| Bvsmod |
| Bvshl |
| Bvlshr |
| Bvashr |
type bvcomp
=
| Bvult |
| Bvslt |
| Bvule |
| Bvsle |
| Bvuge |
| Bvsge |
| Bvugt |
| Bvsgt |
type bvmanyarith
=
| Bvand |
| Bvor |
| Bvxor |
| Bvadd |
| Bvmul |
type unop
=
| Not |
| Bvnot |
| Bvredand |
| Bvredor |
| Bvneg |
| Extract of int * int |
| ZeroExtend of int |
| SignExtend of int |
type 'm binop
=
| Binmem of 'm binmem |
| Eq |
| Bvarith of bvarith |
| Bvcomp of bvcomp |
type manyop
=
| And |
| Or |
| Bvmanyarith of bvmanyarith |
| Concat |
type ('a, 'v, 'b, 'm) exp
=
| Var of 'v * 'a |
| Bound of 'b * 'a |
| Bits of Utils.BitVec.t * 'a |
| Bool of bool * 'a |
| Enum of enum * 'a |
| Unop of unop * ('a, 'v, 'b, 'm) exp * 'a |
| Binop of 'm binop * ('a, 'v, 'b, 'm) exp * ('a, 'v, 'b, 'm) exp * 'a |
| Manyop of manyop * ('a, 'v, 'b, 'm) exp list * 'a |
| Vec of ('a, 'v, 'b, 'm) exp list * 'a |
| Ite of ('a, 'v, 'b, 'm) exp * ('a, 'v, 'b, 'm) exp * ('a, 'v, 'b, 'm) exp * 'a |
| Let of 'b * ('a, 'v, 'b, 'm) exp * ('b * ('a, 'v, 'b, 'm) exp) list * ('a, 'v, 'b, 'm) exp * 'a |
type 'm ty
=
| Ty_Mem of 'm |
| Ty_Bool |
| Ty_BitVec of int |
| Ty_Enum of int |
| Ty_Array of 'm ty * 'm ty |
type ('a, 'v, 'b, 'm) smt
=
| DeclareConst of 'v * 'm ty |
| DefineConst of 'v * ('a, 'v, 'b, 'm) exp |
| Assert of ('a, 'v, 'b, 'm) exp |
| Simplify of ('a, 'v, 'b, 'm) exp * (string * bool) list |
| Push |
| Pop |
| GetVersion |
| CheckSat |
| Exit |
type ('a, 'v, 'b, 'm) smt_ans
=
| Error of string |
| Version of string |
| Sat |
| Unsat |
| Unknown |
| Unsupported |
| Exp of ('a, 'v, 'b, 'm) exp |
AstGen.Ott
warning: the backend selected ignores the file structure informations
type ('a, 'v, 'b, 'm) exp =
| Var of 'v * 'a
| Bound of 'b * 'a
| Bits of Utils.BitVec.t * 'a
| Bool of bool * 'a
| Enum of enum * 'a
| Unop of unop * ('a, 'v, 'b, 'm) exp * 'a
| Binop of 'm binop * ('a, 'v, 'b, 'm) exp * ('a, 'v, 'b, 'm) exp * 'a
| Manyop of manyop * ('a, 'v, 'b, 'm) exp list * 'a
| Vec of ('a, 'v, 'b, 'm) exp list * 'a
| Ite of ('a, 'v, 'b, 'm) exp * ('a, 'v, 'b, 'm) exp * ('a, 'v, 'b, 'm) exp * 'a
| Let of 'b * ('a, 'v, 'b, 'm) exp
+ * ('b * ('a, 'v, 'b, 'm) exp) list
+ * ('a, 'v, 'b, 'm) exp
+ * 'a
type ('a, 'v, 'b, 'm) smt_ans =
| Error of string
| Version of string
| Sat
| Unsat
| Unknown
| Unsupported
| Exp of ('a, 'v, 'b, 'm) exp
auxiliary functions on the new list types
library functions
subrules
auxiliary functions
free variables
substitutions
context application
definitions
AstGen.Parser
type token
=
| ZERO_UNDERSCORE_EXTEND |
| VVAR of int |
| UNSUPPORTED |
| UNSAT |
| UNKNOWN |
| TRUE |
| STR of string |
| STORE |
| SIMPLIFY |
| SIGN_UNDERSCORE_EXTEND |
| SELECT |
| SAT |
| RPAREN |
| PUSH |
| POP |
| OR |
| NOT |
| NAME of string |
| MEM |
| LPAREN_UNDERSCORE |
| LPAREN |
| LET |
| ITE |
| INT of int |
| GET_MINUS_INFO |
| FLAG of string |
| FALSE |
| EXTRACT |
| EXIT |
| ERROR |
| EQ |
| EOF |
| ENUM_UNDERSCORE_TY of int |
| ENUM of int * int |
| DEFINE_MINUS_CONST |
| DECLARE_MINUS_CONST |
| CONCAT |
| COLON_VERSION |
| CHECK_MINUS_SAT |
| BVXOR |
| BVXNOR |
| BVUREM_UNDERSCORE_I |
| BVUREM |
| BVULT |
| BVULE |
| BVUGT |
| BVUGE |
| BVUDIV_UNDERSCORE_I |
| BVUDIV |
| BVSUB |
| BVSREM_UNDERSCORE_I |
| BVSREM |
| BVSMOD_UNDERSCORE_I |
| BVSMOD |
| BVSLT |
| BVSLE |
| BVSHL |
| BVSGT |
| BVSGE |
| BVSDIV_UNDERSCORE_I |
| BVSDIV |
| BVREDOR |
| BVREDAND |
| BVOR |
| BVNOT |
| BVNOR |
| BVNEG |
| BVNAND |
| BVMUL |
| BVLSHR |
| BVI of int |
| BVF of Utils.BitVec.t |
| BVASHR |
| BVAR of string |
| BVAND |
| BVADD |
| BV of string |
| BOOL |
| BITVEC |
| ASSERT |
| ARRAY |
| AND |
val smts_start : (Stdlib.Lexing.lexbuf -> token) -> Stdlib.Lexing.lexbuf -> Def.rsmt list
val smt_start : (Stdlib.Lexing.lexbuf -> token) -> Stdlib.Lexing.lexbuf -> Def.rsmt
val smt_ans_start : (Stdlib.Lexing.lexbuf -> token) -> Stdlib.Lexing.lexbuf -> Def.rsmt_ans
val exp_start : (Stdlib.Lexing.lexbuf -> token) -> Stdlib.Lexing.lexbuf -> (Def.lrng, string, string, Def.Size.t) Ott.exp
AstGen.Parser
type token =
| ZERO_UNDERSCORE_EXTEND
| VVAR of int
| UNSUPPORTED
| UNSAT
| UNKNOWN
| TRUE
| STR of string
| STORE
| SIMPLIFY
| SIGN_UNDERSCORE_EXTEND
| SELECT
| SAT
| RPAREN
| PUSH
| POP
| OR
| NOT
| NAT of int
| NAME of string
| MINUS
| MEM
| LPAREN_UNDERSCORE
| LPAREN
| LET
| ITE
| GET_MINUS_INFO
| FLAG of string
| FALSE
| EXTRACT
| EXIT
| ERROR
| EQ
| EOF
| ENUM_UNDERSCORE_TY of int
| ENUM of int * int
| DEFINE_MINUS_CONST
| DECLARE_MINUS_CONST
| CONCAT
| COLON_VERSION
| CHECK_MINUS_SAT
| BVXOR
| BVXNOR
| BVUREM_UNDERSCORE_I
| BVUREM
| BVULT
| BVULE
| BVUGT
| BVUGE
| BVUDIV_UNDERSCORE_I
| BVUDIV
| BVSUB
| BVSREM_UNDERSCORE_I
| BVSREM
| BVSMOD_UNDERSCORE_I
| BVSMOD
| BVSLT
| BVSLE
| BVSHL
| BVSGT
| BVSGE
| BVSDIV_UNDERSCORE_I
| BVSDIV
| BVREDOR
| BVREDAND
| BVOR
| BVNOT
| BVNOR
| BVNEG
| BVNAND
| BVMUL
| BVLSHR
| BVI of int
| BVF of Utils.BitVec.t
| BVASHR
| BVAR of string
| BVAND
| BVADD
| BV of string
| BOOL
| BITVEC
| ASSERT
| ARRAY
| AND
val smt_ans_start :
+ (Stdlib.Lexing.lexbuf -> token) ->
+ Stdlib.Lexing.lexbuf ->
+ Def.rsmt_ans
val exp_start :
+ (Stdlib.Lexing.lexbuf -> token) ->
+ Stdlib.Lexing.lexbuf ->
+ (Def.lrng, string, string, Def.Size.t) Ott.exp
AstGen.Parser_pp
val pp_raw_bvar : string -> PPrintEngine.document
val pp_raw_bvf : Utils.BitVec.t -> PPrintEngine.document
val pp_raw_flag : string -> PPrintEngine.document
val pp_raw_vvar : int -> PPrintEngine.document
val pp_raw_name : string -> PPrintEngine.document
val pp_raw_enum_ty : int -> PPrintEngine.document
val pp_raw_enum : Ott.enum -> PPrintEngine.document
val pp_raw_int : int -> PPrintEngine.document
val pp_raw_bvi : int -> PPrintEngine.document
val pp_raw_bv : string -> PPrintEngine.document
val pp_raw_str : string -> PPrintEngine.document
val pp_raw_mem_size : Def.Size.t -> Utils.Pp.document
val pp_raw_binmem : Def.Size.t Ott.binmem -> PPrintEngine.document
val pp_raw_var : ('a -> PPrintEngine.document) -> 'a -> PPrintEngine.document
val pp_raw_bbvar : string -> PPrintEngine.document
val pp_raw_bind : ('a -> PPrintEngine.document) -> (string * ('b, 'a, string, Def.Size.t) Ott.exp) -> PPrintEngine.document
val pp_raw_exp : ('a -> PPrintEngine.document) -> ('b, 'a, string, Def.Size.t) Ott.exp -> PPrintEngine.document
val pp_raw_fbool : (string * bool) -> PPrintEngine.document
val pp_raw_smt : ('a -> PPrintEngine.document) -> ('b, 'a, string, Def.Size.t) Ott.smt -> PPrintEngine.document
val pp_raw_smts : ('a -> PPrintEngine.document) -> ('b, 'a, string, Def.Size.t) Ott.smt list -> PPrintEngine.document
val pp_raw_smt_ans : ('a -> PPrintEngine.document) -> ('b, 'a, string, Def.Size.t) Ott.smt_ans -> PPrintEngine.document
val pp_raw_ty : Def.Size.t Ott.ty -> PPrintEngine.document
val pp_raw_bool : bool -> PPrintEngine.document
val pp_raw_unop : Ott.unop -> PPrintEngine.document
val pp_raw_bvarith : Ott.bvarith -> PPrintEngine.document
val pp_raw_bvcomp : Ott.bvcomp -> PPrintEngine.document
val pp_raw_binop : Def.Size.t Ott.binop -> PPrintEngine.document
val pp_raw_bvmanyarith : Ott.bvmanyarith -> PPrintEngine.document
val pp_raw_manyop : Ott.manyop -> PPrintEngine.document
val pp_bvar : string -> PPrintEngine.document
val pp_bvf : Utils.BitVec.t -> PPrintEngine.document
val pp_flag : string -> PPrintEngine.document
val pp_vvar : int -> PPrintEngine.document
val pp_name : string -> PPrintEngine.document
val pp_enum_ty : int -> PPrintEngine.document
val pp_enum : Ott.enum -> PPrintEngine.document
val pp_int : int -> PPrintEngine.document
val pp_bvi : int -> PPrintEngine.document
val pp_bv : string -> PPrintEngine.document
val pp_str : string -> PPrintEngine.document
val pp_j : int -> string
val pp_mem_size : Def.Size.t -> Utils.Pp.document
val pp_binmem : Def.Size.t Ott.binmem -> PPrintEngine.document
val pp_var : ('a -> PPrintEngine.document) -> 'a -> PPrintEngine.document
val pp_bbvar : string -> PPrintEngine.document
val pp_bind : ('a -> PPrintEngine.document) -> (string * ('b, 'a, string, Def.Size.t) Ott.exp) -> PPrintEngine.document
val pp_exp : ('a -> PPrintEngine.document) -> ('b, 'a, string, Def.Size.t) Ott.exp -> PPrintEngine.document
val pp_fbool : (string * bool) -> PPrintEngine.document
val pp_smt : ('a -> PPrintEngine.document) -> ('b, 'a, string, Def.Size.t) Ott.smt -> PPrintEngine.document
val pp_smts : ('a -> PPrintEngine.document) -> ('b, 'a, string, Def.Size.t) Ott.smt list -> PPrintEngine.document
val pp_smt_ans : ('a -> PPrintEngine.document) -> ('b, 'a, string, Def.Size.t) Ott.smt_ans -> PPrintEngine.document
val pp_ty : Def.Size.t Ott.ty -> PPrintEngine.document
val pp_bool : bool -> PPrintEngine.document
val pp_unop : Ott.unop -> PPrintEngine.document
val pp_bvarith : Ott.bvarith -> PPrintEngine.document
val pp_bvcomp : Ott.bvcomp -> PPrintEngine.document
val pp_binop : Def.Size.t Ott.binop -> PPrintEngine.document
val pp_bvmanyarith : Ott.bvmanyarith -> PPrintEngine.document
val pp_manyop : Ott.manyop -> PPrintEngine.document
AstGen.Parser_pp
val pp_raw_bvf : Utils.BitVec.t -> PPrint.document
val pp_raw_enum : Ott.enum -> PPrint.document
val pp_raw_mem_size : Def.Size.t -> Utils.Pp.document
val pp_raw_binmem : Def.Size.t Ott.binmem -> PPrint.document
val pp_raw_bind :
+ ('a -> PPrint.document) ->
+ (string * ('b, 'a, string, Def.Size.t) Ott.exp) ->
+ PPrint.document
val pp_raw_exp :
+ ('a -> PPrint.document) ->
+ ('b, 'a, string, Def.Size.t) Ott.exp ->
+ PPrint.document
val pp_raw_smt :
+ ('a -> PPrint.document) ->
+ ('b, 'a, string, Def.Size.t) Ott.smt ->
+ PPrint.document
val pp_raw_smts :
+ ('a -> PPrint.document) ->
+ ('b, 'a, string, Def.Size.t) Ott.smt list ->
+ PPrint.document
val pp_raw_smt_ans :
+ ('a -> PPrint.document) ->
+ ('b, 'a, string, Def.Size.t) Ott.smt_ans ->
+ PPrint.document
val pp_raw_ty : Def.Size.t Ott.ty -> PPrint.document
val pp_raw_unop : Ott.unop -> PPrint.document
val pp_raw_bvarith : Ott.bvarith -> PPrint.document
val pp_raw_bvcomp : Ott.bvcomp -> PPrint.document
val pp_raw_binop : Def.Size.t Ott.binop -> PPrint.document
val pp_raw_bvmanyarith : Ott.bvmanyarith -> PPrint.document
val pp_raw_manyop : Ott.manyop -> PPrint.document
val pp_bvf : Utils.BitVec.t -> PPrint.document
val pp_enum : Ott.enum -> PPrint.document
val pp_mem_size : Def.Size.t -> Utils.Pp.document
val pp_binmem : Def.Size.t Ott.binmem -> PPrint.document
val pp_bind :
+ ('a -> PPrint.document) ->
+ (string * ('b, 'a, string, Def.Size.t) Ott.exp) ->
+ PPrint.document
val pp_exp :
+ ('a -> PPrint.document) ->
+ ('b, 'a, string, Def.Size.t) Ott.exp ->
+ PPrint.document
val pp_smt :
+ ('a -> PPrint.document) ->
+ ('b, 'a, string, Def.Size.t) Ott.smt ->
+ PPrint.document
val pp_smts :
+ ('a -> PPrint.document) ->
+ ('b, 'a, string, Def.Size.t) Ott.smt list ->
+ PPrint.document
val pp_smt_ans :
+ ('a -> PPrint.document) ->
+ ('b, 'a, string, Def.Size.t) Ott.smt_ans ->
+ PPrint.document
val pp_ty : Def.Size.t Ott.ty -> PPrint.document
val pp_unop : Ott.unop -> PPrint.document
val pp_bvarith : Ott.bvarith -> PPrint.document
val pp_bvcomp : Ott.bvcomp -> PPrint.document
val pp_binop : Def.Size.t Ott.binop -> PPrint.document
val pp_bvmanyarith : Ott.bvmanyarith -> PPrint.document
val pp_manyop : Ott.manyop -> PPrint.document
AstGen
module Def : sig ... end
module Lexer : sig ... end
module Ott : sig ... end
module Parser : sig ... end
module Parser_pp : sig ... end
AstGen
module Def : sig ... end
module Lexer : sig ... end
module Ott : sig ... end
warning: the backend selected ignores the file structure informations
module Parser : sig ... end
module Parser_pp : sig ... end
This page is about how we analyse the ELF and DWARF information in binary files All the module in this page provide a wrapping interface around linksem
. An important convention is that if there is an internal type with a name, the corresponding type in linksem representing the same concept is named linksem_name
.
TODO: There is a lot a binary analysis code in Analyse*
modules that should be documented.
The Elf
group of modules provide the main interface to ELF information. It will parse the ELF file and extract the symbol table. In particular they provide direct access to ELF symbols like function and global variables.
DWARF information is processed after linksem
by the Dw
modules, for functions and variables. This step also does C type linking in Ctype
and inverting DWARF location in Dw.Loc
.
This page is about how we analyse the ELF and DWARF information in binary files All the module in this page provide a wrapping interface around linksem
. An important convention is that if there is an internal type with a name, the corresponding type in linksem representing the same concept is named linksem_name
.
TODO: There is a lot a binary analysis code in Analyse*
modules that should be documented.
The Elf
group of modules provide the main interface to ELF information. It will parse the ELF file and extract the symbol table. In particular they provide direct access to ELF symbols like function and global variables.
DWARF information is processed after linksem
by the Dw
modules, for functions and variables. This step also does C type linking in Ctype
and inverting DWARF location in Dw.Loc
.
read-dwarf
as a various set of subcommand for testing various aspect of the code. They are call like read-dwarf subcommand --options ..
. The convention is that for each subcommand there is a corresponding module. For example the run-func
subcommand is implemented in Run.Func
. All the command line interface of read-dwarf use the cmdliner
library. Those module export a command
value like Run.Func.command
. This value is then used by Main
to build and call the main command line.
To add a new subcommand, you have to make a new module, with the logic of that subcommand, export a command
value, and add that value in Main
.
There are a certain number of common options for setting binary paths or config file locations from the command line. They are all defined in Config.CommonOpt
. This module also contain common cmdliner
infrastructure.
This section is about command to test the isla interaction on single instructions. All those subcommand start with isla-*
.
isla-server
(in Isla.Server.Cmd
) allow to do manual call to the isla server. The input is un-parsed and transmitted as raw text to isla, however the result is parsed and printed again as the protocol is partially a binary protocol.isla-test
(in Isla.Test
) allow to test all the elements of the pipeline individually. In particular it allows to parse an isla trace text from disk and other similar low-level testing operation. It completely ignores the Isla.Cache
.This section is about sub-commands that dump ELF and DWARF information without doing any symbolic execution.
rd
(in Run.ReadDwarf
) : Dumps an ELF file in the read-dwarf format. DWARF information is interleaved with the result of the objdump, so one can see how the dwarf information is positioned compared to the assembly. It will also try to read the source file to interleave the original source code.dump-sym
(in Other_cmds.DumpSym
) : Dumps the ELF symbols as parsed by the Elf
modules.dump-dwarf
(in Other_cmds.DumpDwarf
) : Dumps the ELF information as parsed by the Dw
modules.This section is about sub-commands that test the symbolic execution engine. They all start with run-*
.
run-instr
(in Run.Instr
): Run a single instruction. Can also dump its Trace
.run-bb
(in Run.BB
): Run a basic block. This will run instructions in order, without updating the PC: Any jump will be ignored.run-block
(in Run.Block
): Run a complex block of instruction. One need to specify a start point and an end condition.run-func
(in Run.Func
): Same as run-block
but start at function start and does proper function entry initialization according to the ABI.run-func-rd
(in Run.FuncRD
: Same as run-func
but also interleave the rd
output.The read-dwarf cache
sub-command (in Utils.Cache
.cmd) provides some cache maintenance operations.
read-dwarf
as a various set of subcommand for testing various aspect of the code. They are call like read-dwarf subcommand --options ..
. The convention is that for each subcommand there is a corresponding module. For example the run-func
subcommand is implemented in Run.Func
. All the command line interface of read-dwarf use the cmdliner
library. Those module export a command
value like Run.Func.command
. This value is then used by Main
to build and call the main command line.
To add a new subcommand, you have to make a new module, with the logic of that subcommand, export a command
value, and add that value in Main
.
There are a certain number of common options for setting binary paths or config file locations from the command line. They are all defined in Config.CommonOpt
. This module also contain common cmdliner
infrastructure.
This section is about command to test the isla interaction on single instructions. All those subcommand start with isla-*
.
isla-server
(in Isla.Server.Cmd
) allow to do manual call to the isla server. The input is un-parsed and transmitted as raw text to isla, however the result is parsed and printed again as the protocol is partially a binary protocol.isla-test
(in Isla.Test
) allow to test all the elements of the pipeline individually. In particular it allows to parse an isla trace text from disk and other similar low-level testing operation. It completely ignores the Isla.Cache
.This section is about sub-commands that dump ELF and DWARF information without doing any symbolic execution.
rd
(in Run.ReadDwarf
) : Dumps an ELF file in the read-dwarf format. DWARF information is interleaved with the result of the objdump, so one can see how the dwarf information is positioned compared to the assembly. It will also try to read the source file to interleave the original source code.dump-sym
(in Other_cmds.DumpSym
) : Dumps the ELF symbols as parsed by the Elf
modules.dump-dwarf
(in Other_cmds.DumpDwarf
) : Dumps the ELF information as parsed by the Dw
modules.This section is about sub-commands that test the symbolic execution engine. They all start with run-*
.
run-instr
(in Run.Instr
): Run a single instruction. Can also dump its Trace
.run-bb
(in Run.BB
): Run a basic block. This will run instructions in order, without updating the PC: Any jump will be ignored.run-block
(in Run.Block
): Run a complex block of instruction. One need to specify a start point and an end condition.run-func
(in Run.Func
): Same as run-block
but start at function start and does proper function entry initialization according to the ABI.run-func-rd
(in Run.FuncRD
: Same as run-func
but also interleave the rd
output.The read-dwarf cache
sub-command (in Utils.Cache.cmd
) provides some cache maintenance operations.
Config.Arch
This module provides an enumeration of architecture for internal identification
Config.Arch
This module provides an enumeration of architecture for internal identification
val to_string : t -> string
val of_string : string -> t
val pp : t -> Utils.Pp.document
val size : t -> int
val fmt : Stdlib.Format.formatter -> t -> unit
val conv : t Cmdliner.Arg.conv
Config.CommonOpt
This module provide support for common command line option to be used across multiple subcomands.
It also provide some utilities on the command line.
This section is to manage the configuration file. See Config.File
to see how the configuration file works
Config.CommonOpt
This module provide support for common command line option to be used across multiple subcomands.
It also provide some utilities on the command line.
This section is to manage the configuration file. See Config.File
to see how the configuration file works
Passing --config=FILE
on the CLI will setup the Config.File
module to load that file as the configuration file.
For test executables, you will have to call File.ensure_loaded
directly.
val arch_opt : Arch.t option Cmdliner.Term.t
val arch : Arch.t Cmdliner.Term.t
The list of common options. Almost all sub-commands should use this.
ArchConf.Isla
This module provides the current isla configuration.
type t
=
{
ignored_regs : string list; | The list of register to be ignored |
arch_file : string; | The name of the architecture.ir file to use. This is the file that was compiled with |
arch_toml : string; | The name of the architecture.toml file to use with Isla. |
arch_file_digest : string; | The digest of the file in |
linearize : string list; | List of sail function to be linearized. That means that if there is a control-flow branching in that function, instead of generating multiple traces, isla will generate a single trace with it-then-else expression |
other_opts : string list; | List of other option to pass to isla. Ideally if an option is to be easily used, a new field of |
}
Isla configuration option. Everything in here is salient for cache coherency which means that if any option if changed here, the whole Isla
.Cache is invalidated. This is checked with digest
.
ArchConf.Isla
This module provides the current isla configuration.
type t = {
ignored_regs : string list;
The list of register to be ignored
*)arch_file : string;
The name of the architecture.ir file to use. This is the file that was compiled with isla-sail
from the sail source. Alternatively this file can be found in the isla-snapshots
respository.
arch_toml : string;
The name of the architecture.toml file to use with Isla.
*)arch_file_digest : string;
linearize : string list;
List of sail function to be linearized. That means that if there is a control-flow branching in that function, instead of generating multiple traces, isla will generate a single trace with it-then-else expression
*)other_opts : string list;
List of other option to pass to isla. Ideally if an option is to be easily used, a new field of t
should be created, but for quick and dirty testing, this field can be used.
}
Isla configuration option. Everything in here is salient for cache coherency which means that if any option if changed here, the whole Isla.Cache
is invalidated. This is checked with digest
.
val digest : t -> string
Produces a digest of the Isla configuration for invalidating the Isla.Cache
when some configuration parameter changes
File.ArchConf
module Isla : sig ... end
This module provides the current isla configuration.
File.ArchConf
module Isla : sig ... end
This module provides the current isla configuration.
type t = {
arch : Arch.t;
The architecture targeted by this record
*)toolchain : string;
The toolchain prefix for using this architecture with GNU binutils like objdump
and as
.
isla : Isla.t;
The isla configuration for this architecture
*)}
The list of all architecture specific configuration options
File.Z3
File.Z3
Config.File
This module is to handle the configuration file of the program.
For now I expect a TOML format and use the toml
library from opam. The data structure themselves are format agnostic so if we change the TOML library of change of format, only this file must be modified. For example to JSON or YAML.
The top level usage is to first set the file
reference which will be done by CommonOpt.config
using ensure_loaded
. Then the config can be accessed with the various Accessors
All field of records in this section have a matching line in the checked-in config.toml
. If something is optional here, and should be disabled in the default config.toml
, put a commented line there, so that the correspondence can be immediately seen.
module ArchConf : sig ... end
module Z3 : sig ... end
type archs_type
= (Arch.t, ArchConf.t) Stdlib.Hashtbl.t
Each architecture specific configuration is specified by a TOML section represented by a Arch.t
record. They are stored in this type
type t
=
{
arch : Arch.t option; | The default architecture to be choosen when no ELF file is specified on the CLI. This is optional, but if supplied, it will override |
archs : archs_type; | All the architecture specific configurations |
z3 : Z3.t; | The Z3 configuration |
}
This section is about loading the configuration from a TOML file
The section provide various accessor to access directly part of the configuration
The config must be loaded otherwise UnloadedConfig
will be thrown
exception
UnloadedConfig
This exception will be raised if any of the accessor is called while the config is not loaded.
val get_config : unit -> t
Get all the configuration information
val get_arch_name : unit -> Arch.t
Get the default architecture name to be used if no architecture is implicitly or explicitly specified on the CLI
val get_arch_config : Arch.t -> ArchConf.t
Get the architecture configuration for a specific architecture. To get the architecture configuration of the currently enabled architecture, Call Arch
.get_config
val get_isla_config : Arch.t -> ArchConf.Isla.t
Get the isla configuration for a specific architecture. To get the isla configuration of the currently enabled architecture, Call Arch
.get_isla_config
val get_z3_config : unit -> Z3.t
Get the Z3 configuration
Config.File
This module is to handle the configuration file of the program.
For now I expect a TOML format and use the toml
library from opam. The data structure themselves are format agnostic so if we change the TOML library of change of format, only this file must be modified. For example to JSON or YAML.
The top level usage is to first set the file
reference which will be done by CommonOpt.config
using ensure_loaded
. Then the config can be accessed with the various Accessors
All field of records in this section have a matching line in the checked-in config.toml
. If something is optional here, and should be disabled in the default config.toml
, put a commented line there, so that the correspondence can be immediately seen.
module ArchConf : sig ... end
module Z3 : sig ... end
type archs_type = (Arch.t, ArchConf.t) Stdlib.Hashtbl.t
Each architecture specific configuration is specified by a TOML section represented by a Arch.t
record. They are stored in this type
type t = {
arch : Arch.t option;
The default architecture to be choosen when no ELF file is specified on the CLI. This is optional, but if supplied, it will override Config.arch
archs : archs_type;
All the architecture specific configurations
*)z3 : Z3.t;
The Z3 configuration
*)}
This section is about loading the configuration from a TOML file
If the configuration file is not already loaded, load it from the specified file.
The section provide various accessor to access directly part of the configuration
The config must be loaded otherwise UnloadedConfig
will be thrown
This exception will be raised if any of the accessor is called while the config is not loaded.
val get_config : unit -> t
Get all the configuration information
val get_arch_name : unit -> Arch.t
Get the default architecture name to be used if no architecture is implicitly or explicitly specified on the CLI
val get_arch_config : Arch.t -> ArchConf.t
Get the architecture configuration for a specific architecture. To get the architecture configuration of the currently enabled architecture, Call Arch.get_config
val get_isla_config : Arch.t -> ArchConf.Isla.t
Get the isla configuration for a specific architecture. To get the isla configuration of the currently enabled architecture, Call Arch.get_isla_config
val get_z3_config : unit -> Z3.t
Get the Z3 configuration
Config
module type S = sig ... end
include S
module Arch : sig ... end
This module provides an enumeration of architecture for internal identification
module File : sig ... end
This module is to handle the configuration file of the program.
module CommonOpt : sig ... end
This module provide support for common command line option to be used across multiple subcomands.
Config
module type S = sig ... end
This is the compile-time configuration module. It is pulled from the root config.ml
file or will fallback to the default_config.ml
file
include S
module Arch : sig ... end
This module provides an enumeration of architecture for internal identification
module File : sig ... end
This module is to handle the configuration file of the program.
module CommonOpt : sig ... end
This module provide support for common command line option to be used across multiple subcomands.
Config.S
Config.S
This is the compile-time configuration module. It is pulled from the root config.ml
file or will fallback to the default_config.ml
file
All compile-time configuration options are specified here. For runtime configuration, look at Config.File
.
Currently, read-dwarf only supports compile-time (not run-time) selection of architecture, through Dune's virtual modules. See Architecture
for more details.
Other compile-time configuration options can be edited in the file src/confing/default.ml
, and accessed in the rest of the code through the Config
module.
Runtime configuration is loaded from toml file at runtime and handled by the Config.File
module. The location of the file (and the side effect of loading) is determined by the Config.CommonOpt.config
option. The various fields can then be accessed by Accessors.
Currently, read-dwarf only supports compile-time (not run-time) selection of architecture, through Dune's virtual modules. See Architecture
for more details.
Other compile-time configuration options can be edited in the file src/confing/default.ml
, and accessed in the rest of the code through the Config
module.
Runtime configuration is loaded from toml file at runtime and handled by the Config.File
module. The location of the file (and the side effect of loading) is determined by the Config.CommonOpt.config
option. The various fields can then be accessed by Accessors.
Ctype.FieldMap
A range map over field to represent a structure layout
type obj
= field
The type of the contained object
type obj_off
= obj * int
The type of an object with an offset
type t
The type of the map from address ranges to obj
val empty : t
An empty RngMap
val is_in : objaddr:int -> obj -> int -> bool
Test if an address is inside the object at address objaddr
val at : t -> int -> obj
Get the object containing the address. Throw Not_found
if no object contains the address
val at_opt : t -> int -> obj option
Get the object containing the address. None
if no object contains the address
val at_off : t -> int -> obj_off
Get the object containing the address and the offset of the address inside the object
at_off map addr = (obj, off)
means:
| | | | - map 0 obj start point obj end - |<--------------addr-------------->| - |<---off--->| - |<------len obj------>|
In other words, at_off
allow a change of coordinate from the map frame to the object frame.
Throw Not_found
if no object contains the address
val at_off_opt : t -> int -> obj_off option
Get the object containing the address and the offset of the address inside the object. See at_off
for more explanation.
None
if no object contains the address
val update : (obj -> obj) -> t -> int -> t
Update the binding containing the provided address. If no binding contained the address, this is a no-op
val iteri : (int -> obj -> unit) -> t -> unit
Iter a function over all the objects with their address
val clear : t -> pos:int -> len:int -> t
Clear an area of the RngMap.
If an object is partially in the specified block. It will be removed entirely.
See clear_crop
for a different behavior. See clear_bounds
to allow some bounds to be infinity.
val clear_crop : t -> pos:int -> len:int -> crop:(pos:int -> len:int -> obj -> obj) -> t
Clear an area of the RngMap.
If a block is partially in the specified block, It will be cropped by using the provided crop function.
crop ~pos ~len obj
is supposed to crop the object obj
and keep only the segment [pos:pos +len)
of it (in the object coordinate frame).
val clear_bounds : ?start:int -> ?endp:int -> t -> t
Same as clear
but if a bound is missing, then we erase until infinity in that direction. The target interval is [start:endp)
.
In particular clear_bounds map =
empty
.
val add : t -> int -> obj -> t
Add an object at a specific address. The whole range of addresses covered by the object must be free
val addp : t -> obj_off -> t
Add an object at a specific address. The whole range of addresses covered by the object must be free
val to_seq : ?start:int -> ?endp:int -> t -> (int * obj) Utils.Seq.t
Return a sequence of all the object overlapping the range [start:endp)
. The first and last element may not be entierly contained in the ranged. If any bound is unspecified, it goes to infinity in that direction.
In particular to_seq map
will iterate the entiere RngMap
Ctype.FieldMap
A range map over field to represent a structure layout
type obj = field
The type of the contained object
type obj_off = obj * int
The type of an object with an offset
The type of the map from address ranges to obj
val empty : t
An empty RngMap
val is_in : objaddr:int -> obj -> int -> bool
Test if an address is inside the object at address objaddr
Get the object containing the address. Throw Not_found
if no object contains the address
Get the object containing the address. None
if no object contains the address
Get the object containing the address and the offset of the address inside the object
at_off map addr = (obj, off)
means:
| | | | + map 0 obj start point obj end + |<--------------addr-------------->| + |<---off--->| + |<------len obj------>|
In other words, at_off
allow a change of coordinate from the map frame to the object frame.
Throw Not_found
if no object contains the address
Get the object containing the address and the offset of the address inside the object. See at_off
for more explanation.
None
if no object contains the address
Update the binding containing the provided address. If no binding contained the address, this is a no-op
Iter a function over all the objects with their address
Clear an area of the RngMap.
If an object is partially in the specified block. It will be removed entirely.
See clear_crop
for a different behavior. See clear_bounds
to allow some bounds to be infinity.
Clear an area of the RngMap.
If a block is partially in the specified block, It will be cropped by using the provided crop function.
crop ~pos ~len obj
is supposed to crop the object obj
and keep only the segment [pos:pos +len)
of it (in the object coordinate frame).
Add an object at a specific address. The whole range of addresses covered by the object must be free
Add an object at a specific address. The whole range of addresses covered by the object must be free
val to_seq : ?start:int -> ?endp:int -> t -> (int * obj) Utils.Seq.t
Return a sequence of all the object overlapping the range [start:endp)
. The first and last element may not be entierly contained in the ranged. If any bound is unspecified, it goes to infinity in that direction.
In particular to_seq map
will iterate the entiere RngMap
Ctype
This module provides the internal C-like type system. This type system is slightly different than the normal C type system. This module only provides the Ocaml datastructure to represent those types. The typing rules are implemented in Trace
.Typer, where they are applied dire
These types follow the normal C type structure except for pointers that are more complex. To handle the fact that the C compiler knows perfectly the ABI and is "allowed" to used it, we have to make pointer types resist manual adjusting of pointers to point to the field of a struct. However the new pointer cannot just have type field_type*
because one could want to get back a pointer to the whole structure by subtracting an offset from the field pointer. Thus a pointer must never forget information about its surroundings. Those surroundings are called a fragment
and represent all the type information of the fragment of memory in which a pointer lies. The pointer is thus represented as a fragment of memory and an offset. Since the pointer type is more complex and packs more information, the surface syntax has changed. A pointer type is written between braces, so A*
becomes {A}
, but in more complex cases, all informations fit between the braces.
Furthermore, the fragment part of the pointer does not record any information about aliasing: two different type fragments are perfectly allowed to alias. To handle non-aliasing properties, like the stack not aliasing the heap or restrict pointers, pointers also have a provenance
field. See State.Mem
.
There is another problem: The C language does not define a C type system for the whole program, contrary to C++. It defines only a type system per compilation unit. This limitation is too annoying to work with so the module implements some kind of linking of types, similar to C++ rules. See C type linking: From Linksem.
type provenance
=
| Restricted of int |
| Main |
This is the provenance of the pointer. This tells to which symbolic memory block a pointer points to. To get the full explanation, go to State.Mem
val pp_provenance : provenance -> Utils.Pp.document
type unqualified
=
| Machine of int | Size in bytes for now | |||
| Cint of {
} | ||||
| Cbool | ||||
| Ptr of {
} |
| |||
| Struct of {
} | See | |||
| Array of {
} | ||||
| Enum of {
} | See | |||
| FuncPtr | Hack to accommodate PKVM | |||
| Missing | Hack to accommodate PKVM |
The unqualified part of the C type without const volatile, ...
and t
=
{
unqualified : unqualified; |
const : bool; |
volatile : bool; |
restrict : bool; |
constexpr : bool; |
}
The internal representation of generalized C types
and fragment
=
| Unknown | Unknown type, But without possibility of learning |
| Single of t | Single object: Only when accessing of a global variable |
| DynArray of t | Generic C pointer, may point to multiple element of that type |
| DynFragment of int | Writable fragment for memory whose type is changing dynamically |
| Global | The Global fragment that contains all the fixed ELF section .text, .data, .rodata, ... |
The type of a memory fragment
type field
=
{
fname : string option; |
offset : int; |
typ : t; |
size : int; |
}
A field in a structure
type linksem_field
= linksem_t Dwarf.struct_union_member
module FieldMap : Utils.RngMap.S with type obj = field
A range map over field to represent a structure layout
type struc
=
{
layout : FieldMap.t; |
name : string; |
size : int; |
complete : bool; |
}
The type of a C structure.
A structure can be complete or incomplete but due to some internal hackery this is a need for a subtle difference with C: Even incomplete structure have a size, they just don't have any field.
A incomplete struct can and will often be complete later as the interpretation of DWARF information advances.
The name field is the linking name of the struct.
type enum
=
{
name : string; |
labels : (int, string option) Stdlib.Hashtbl.t; |
}
The representation type of a C enumeration
type cupdie_id
= int * int
The identifier for a linksem_cupdie. See ids_of_cupdie
type linksem_env
= linksem_t list
The type of environement linksem gives us.
Only structs, enums and unions can appear. A type can appear multiple times and also be forward-declared with missing data like size. env_of_linksem
hopefully deals with all those problems
type linksem_indexed_env
= (cupdie_id, linksem_t) Stdlib.Hashtbl.t
An version of the linksem environement indexed by cupdie_id
type env
=
{
structs : (string, struc) Utils.IdMap.t; |
enums : (string, enum) Utils.IdMap.t; |
lenv : linksem_indexed_env; |
}
The type environment that contain mapping from linking name and a generated id
to the actual content of structs and enumerations.
Linking names can be:
typedef.name
for an unnamed struct declared in a typedefouter.member
for unnamed struct used as the type of a member
of a struct with linking name outer
.As an unnamed struct can be declared in the linksem environement but used with the typedef only after the initial environement setup of env_of_linksem
, the original linksem type must be kept alive in lenv
val is_struct : t -> bool
val is_array : t -> bool
val is_ptr : t -> bool
val is_scalar : t -> bool
val is_composite : t -> bool
val is_constexpr : t -> bool
val ptr : t -> unqualified
Make a simple pointer from a type
val voidstar : unqualified
A void* pointer
val qual : ?const:bool -> ?volatile:bool -> ?restrict:bool -> ?constexpr:bool -> unqualified -> t
Create a qualified type from an unqualified type with the specified qualifiers
val add_qual : ?const:bool -> ?volatile:bool -> ?restrict:bool -> ?constexpr:bool -> t -> t
update specified qualifiers. Other qualifier are kept
val machine : ?constexpr:bool -> int -> t
Create a machine type of that size without qualifiers
val of_frag : ?provenance:provenance -> ?offset:int -> ?constexpr:bool -> ?restrict:bool -> fragment -> t
Create a pointer to fragment with specified offset (0 by default)
val of_frag_somewhere : ?provenance:provenance -> ?constexpr:bool -> ?restrict:bool -> fragment -> t
Create a pointer to fragment Somewhere
val incomplete_struct : string -> int -> struc
Build an incomplete struct with a linking name and a size
val make_env : linksem_indexed_env -> env
Makes an empty environement from and indexed linksem environement
This section give implementation of sizeof function.
Dynamic array have size 0 until we are able to deal with C99 last member dynamic arrays. This will mess up with type_at
. TODO fix it.
val sizeof_unqualified : unqualified -> int
Give the size of an unqualified
type. Need the environement.
val sizeof : t -> int
Give the size of an type. Need the environement.
val len : t -> int
For being used in RngMap
.LenObject
This section contain the whole hierarchy of function used to convert type from DWARF representation to the internal type system.
During this conversion, C type linking happens. This means that structure with the same name from different compilation unit are identified as being the same. If they do not have the same layout, an error is raised. This would mean that either the C program does very weird thing with it's type that we don't want to verify or more likely that it is ill-formed. As we have to deal with anonymous struct, name are a bit more complex than plain struct
tags. See env
for a description.
The top-level interface for types is of_linksem
and the top-level interface for environement is env_of_linksem
. Those will be called by Dw
at DWARF loading time to generate a coherent type system.
type conversion_context
=
{
env : env; |
potential_link_name : string option; |
}
This type is a conversion context.
Its role is to contain all the things that all the functions in this section will need to convert types.
val ids_of_cupdie : Dwarf.cupdie -> cupdie_id
Get the id of a linksem cupdie
val pp_decl : Dwarf.decl -> PPrintEngine.document
Pretty print the dwarf decl type
TODO: Move that in the appropriate place
exception
LinkError
This exception is raised when the type we are trying to reach must came from another translation unit or later in the current one.
The information is incomplete at this moment to create is.
Normally this exception should only happen during the initial env_of_linksem
. If it happens elsewhere, either the code used an anonymous struct that do not have C++-like linkage or the compiler did not do its job.
val expect_some_link : 'a option -> 'a
val base_type_of_linksem : ?size:Z.t -> encoding:Z.t -> string -> unqualified
Convert a base type with a name and encoding and maybe a size to its inner representation
Only integers, chars and bools supported. No floating points
val field_of_linksem : cc:conversion_context -> linksem_field -> FieldMap.obj
val field_map_of_linksem : string -> cc:conversion_context -> linksem_field list -> FieldMap.t
val struc_of_linksem : cc:conversion_context -> string -> int -> linksem_field list -> struc
val struct_type_of_linksem : ?force_complete:bool -> cc:conversion_context -> cupdie:Dwarf.cupdie -> mname:string option -> decl:Dwarf.decl -> unqualified
Build a struct from it's cupdie and name. If force_complete
is true and the struct is incomplete. It will try to complete is using cupdie
and throw LinkError
if it fails.
val enum_of_linksem : cc:conversion_context -> string -> Dwarf.enumeration_member list -> enum
val enum_type_of_linksem : cc:conversion_context -> cupdie:Dwarf.cupdie -> mname:string option -> decl:Dwarf.decl -> unqualified
val unqualified_of_linksem : ?force_complete:bool -> cc:conversion_context -> linksem_t -> unqualified
Convert an unqualified type. Union are just Machine n
where n is their size
val of_linksem_none : unit -> t
Placeholder for whatever the right thing to do is when linksem found no type - TODO: fix
val of_linksem_cc : ?force_complete:bool -> cc:conversion_context -> ?const:bool -> ?volatile:bool -> ?restrict:bool -> linksem_t -> t
The main of_linksem
that take a full conversion_context and qualifiers.
The user friendly version is of_linksem
See struct_type_of_linksem
for the explanation of force_complete
.
All the qualifier passed as parameter are added to the resulting type.
val of_linksem : env:env -> linksem_t -> t
The user friendly interface that convert a type using the environment. This useful when there is no additional linking information to pass on
val env_of_linksem : linksem_env -> env
The main environment conversion function.
This the function that deal with all the type linking process, forward declaration an all that stuff.
First it create the indexed linksem environment (member lenv of env
),
Then it registers all named structs as incomplete in the environment (to deal with self recursion, only named structs can self-recurse).
Finally it run of_linksem_cc
on all the types with force_complete
on. During this phase it ignore all LinkError
that arise. It assumes that if some thing was incomplete at that point, it will become complete later.
Then it can return the freshly built environment.
If some structs are still incomplete, but are actually used, the LinkError
will be raised at the time of use.
val pp_signed : bool -> PPrintEngine.document
val pp : t -> Utils.Pp.document
Pretty print a type. If an environement is provided, structs and enums will be printed with a name, otherwise they will just have a number
val pp_unqualified : unqualified -> Utils.Pp.document
val pp_fragment : fragment -> Utils.Pp.document
val pp_offset : offset -> Utils.Pp.document
val pp_arr : t -> int option list -> Utils.Pp.document
val pp_arr_dim : int option -> Utils.Pp.document
val pp_field : field -> PPrintEngine.document
val pp_struct : struc -> Utils.Pp.document
val pp_enums : enum -> Utils.Pp.document
val pp_env : env -> Utils.Pp.document
Print the whole environement (not the linksem indexed environment)
This section is about getting the type at a specific offset in things.
val struct_at : env:env -> size:int -> struc -> int -> t Utils.Option.t
Same as type_at
but for structs
val type_at : env:env -> size:int -> t -> int -> t Utils.Option.t
Get the type of size size
at the provided offset in another type
Ctype
This module provides the internal C-like type system. This type system is slightly different than the normal C type system. This module only provides the Ocaml datastructure to represent those types. The typing rules are implemented in Trace.Typer
, where they are applied dire
These types follow the normal C type structure except for pointers that are more complex. To handle the fact that the C compiler knows perfectly the ABI and is "allowed" to used it, we have to make pointer types resist manual adjusting of pointers to point to the field of a struct. However the new pointer cannot just have type field_type*
because one could want to get back a pointer to the whole structure by subtracting an offset from the field pointer. Thus a pointer must never forget information about its surroundings. Those surroundings are called a fragment
and represent all the type information of the fragment of memory in which a pointer lies. The pointer is thus represented as a fragment of memory and an offset. Since the pointer type is more complex and packs more information, the surface syntax has changed. A pointer type is written between braces, so A*
becomes {A}
, but in more complex cases, all informations fit between the braces.
Furthermore, the fragment part of the pointer does not record any information about aliasing: two different type fragments are perfectly allowed to alias. To handle non-aliasing properties, like the stack not aliasing the heap or restrict pointers, pointers also have a provenance
field. See State.Mem
.
There is another problem: The C language does not define a C type system for the whole program, contrary to C++. It defines only a type system per compilation unit. This limitation is too annoying to work with so the module implements some kind of linking of types, similar to C++ rules. See C type linking: From Linksem.
This is the provenance of the pointer. This tells to which symbolic memory block a pointer points to. To get the full explanation, go to State.Mem
val pp_provenance : provenance -> Utils.Pp.document
type unqualified =
| Machine of int
Size in bytes for now
*)| Cint of {
}
| Cbool
| Ptr of {
fragment : fragment;
offset : offset;
provenance : provenance;
}
fragment
is about a type fragment. offset
is the position in that type fragment. provenance
is about a runtime symbolic block (see State.Mem
).
| Struct of {
}
| Array of {
elem : t;
dims : int option list;
}
| Enum of {
}
| FuncPtr
Hack to accommodate PKVM
*)| Missing
Hack to accommodate PKVM
*)| Bits
Hack to prevent losing type information when processing bitvectors with non-whole-byte sizes
*)The unqualified part of the C type without const volatile, ...
The internal representation of generalized C types
and fragment =
| Unknown
Unknown type, But without possibility of learning
*)| Single of t
Single object: Only when accessing of a global variable
*)| DynArray of t
Generic C pointer, may point to multiple element of that type
*)| DynFragment of int
Writable fragment for memory whose type is changing dynamically
*)| Global of string
The Global fragment that contains all the fixed ELF section .text, .data, .rodata, ...
*)The type of a memory fragment
A field in a structure
type linksem_field = linksem_t Dwarf.struct_union_member
module FieldMap : Utils.RngMap.S with type obj = field
A range map over field to represent a structure layout
The type of a C structure.
A structure can be complete or incomplete but due to some internal hackery this is a need for a subtle difference with C: Even incomplete structure have a size, they just don't have any field.
A incomplete struct can and will often be complete later as the interpretation of DWARF information advances.
The name field is the linking name of the struct.
The representation type of a C enumeration
The identifier for a linksem_cupdie. See ids_of_cupdie
type linksem_env = linksem_t list
The type of environement linksem gives us.
Only structs, enums and unions can appear. A type can appear multiple times and also be forward-declared with missing data like size. env_of_linksem
hopefully deals with all those problems
An version of the linksem environement indexed by cupdie_id
type env = {
structs : (string, struc) Utils.IdMap.t;
enums : (string, enum) Utils.IdMap.t;
lenv : linksem_indexed_env;
}
The type environment that contain mapping from linking name and a generated id
to the actual content of structs and enumerations.
Linking names can be:
typedef.name
for an unnamed struct declared in a typedefouter.member
for unnamed struct used as the type of a member
of a struct with linking name outer
.As an unnamed struct can be declared in the linksem environement but used with the typedef only after the initial environement setup of env_of_linksem
, the original linksem type must be kept alive in lenv
val is_struct : t -> bool
val is_array : t -> bool
val is_ptr : t -> bool
val is_scalar : t -> bool
val is_composite : t -> bool
val is_constexpr : t -> bool
val ptr : t -> unqualified
Make a simple pointer from a type
val voidstar : unqualified
A void* pointer
val qual :
+ ?const:bool ->
+ ?volatile:bool ->
+ ?restrict:bool ->
+ ?constexpr:bool ->
+ unqualified ->
+ t
Create a qualified type from an unqualified type with the specified qualifiers
val add_qual :
+ ?const:bool ->
+ ?volatile:bool ->
+ ?restrict:bool ->
+ ?constexpr:bool ->
+ t ->
+ t
update specified qualifiers. Other qualifier are kept
val machine : ?constexpr:bool -> int -> t
Create a machine type of that size without qualifiers
val of_frag :
+ ?provenance:provenance ->
+ ?offset:int ->
+ ?constexpr:bool ->
+ ?restrict:bool ->
+ fragment ->
+ t
Create a pointer to fragment with specified offset (0 by default)
val of_frag_somewhere :
+ ?provenance:provenance ->
+ ?constexpr:bool ->
+ ?restrict:bool ->
+ fragment ->
+ t
Create a pointer to fragment Somewhere
val incomplete_struct : string -> int -> struc
Build an incomplete struct with a linking name and a size
val make_env : linksem_indexed_env -> env
Makes an empty environement from and indexed linksem environement
This section give implementation of sizeof function.
Dynamic array have size 0 until we are able to deal with C99 last member dynamic arrays. This will mess up with type_at
. TODO fix it.
val sizeof_unqualified : unqualified -> int
Give the size of an unqualified
type. Need the environement.
val sizeof : t -> int
Give the size of an type. Need the environement.
val len : t -> int
For being used in RngMap.LenObject
This section contain the whole hierarchy of function used to convert type from DWARF representation to the internal type system.
During this conversion, C type linking happens. This means that structure with the same name from different compilation unit are identified as being the same. If they do not have the same layout, an error is raised. This would mean that either the C program does very weird thing with it's type that we don't want to verify or more likely that it is ill-formed. As we have to deal with anonymous struct, name are a bit more complex than plain struct
tags. See env
for a description.
The top-level interface for types is of_linksem
and the top-level interface for environement is env_of_linksem
. Those will be called by Dw
at DWARF loading time to generate a coherent type system.
This type is a conversion context.
Its role is to contain all the things that all the functions in this section will need to convert types.
val ids_of_cupdie : Dwarf.cupdie -> cupdie_id
Get the id of a linksem cupdie
val pp_decl : Dwarf.decl -> Utils.Pp.document
Pretty print the dwarf decl type
TODO: Move that in the appropriate place
This exception is raised when the type we are trying to reach must came from another translation unit or later in the current one.
The information is incomplete at this moment to create is.
Normally this exception should only happen during the initial env_of_linksem
. If it happens elsewhere, either the code used an anonymous struct that do not have C++-like linkage or the compiler did not do its job.
val base_type_of_linksem :
+ ?size:Sym_ocaml.Num.t ->
+ encoding:Sym_ocaml.Num.t ->
+ string ->
+ unqualified
Convert a base type with a name and encoding and maybe a size to its inner representation
Only integers, chars and bools supported. No floating points
val field_of_linksem : cc:conversion_context -> linksem_field -> FieldMap.obj
val field_map_of_linksem :
+ string ->
+ cc:conversion_context ->
+ linksem_field list ->
+ FieldMap.t
val struc_of_linksem :
+ cc:conversion_context ->
+ string ->
+ int ->
+ linksem_field list ->
+ struc
val struct_type_of_linksem :
+ ?force_complete:bool ->
+ cc:conversion_context ->
+ cupdie:Dwarf.cupdie ->
+ mname:string option ->
+ decl:Dwarf.decl ->
+ unqualified
Build a struct from it's cupdie and name. If force_complete
is true and the struct is incomplete. It will try to complete is using cupdie
and throw LinkError
if it fails.
val enum_of_linksem :
+ cc:conversion_context ->
+ string ->
+ Dwarf.enumeration_member list ->
+ enum
val enum_type_of_linksem :
+ cc:conversion_context ->
+ cupdie:Dwarf.cupdie ->
+ mname:string option ->
+ decl:Dwarf.decl ->
+ unqualified
val unqualified_of_linksem :
+ ?force_complete:bool ->
+ cc:conversion_context ->
+ linksem_t ->
+ unqualified
Convert an unqualified type. Union are just Machine n
where n is their size
val of_linksem_none : unit -> t
Placeholder for whatever the right thing to do is when linksem found no type - TODO: fix
val of_linksem_cc :
+ ?force_complete:bool ->
+ cc:conversion_context ->
+ ?const:bool ->
+ ?volatile:bool ->
+ ?restrict:bool ->
+ linksem_t ->
+ t
The main of_linksem
that take a full conversion_context and qualifiers.
The user friendly version is of_linksem
See struct_type_of_linksem
for the explanation of force_complete
.
All the qualifier passed as parameter are added to the resulting type.
The user friendly interface that convert a type using the environment. This useful when there is no additional linking information to pass on
val env_of_linksem : linksem_env -> env
The main environment conversion function.
This the function that deal with all the type linking process, forward declaration an all that stuff.
First it create the indexed linksem environment (member lenv of env
),
Then it registers all named structs as incomplete in the environment (to deal with self recursion, only named structs can self-recurse).
Finally it run of_linksem_cc
on all the types with force_complete
on. During this phase it ignore all LinkError
that arise. It assumes that if some thing was incomplete at that point, it will become complete later.
Then it can return the freshly built environment.
If some structs are still incomplete, but are actually used, the LinkError
will be raised at the time of use.
val pp_signed : bool -> Utils.Pp.document
val pp : t -> Utils.Pp.document
Pretty print a type. If an environement is provided, structs and enums will be printed with a name, otherwise they will just have a number
val pp_unqualified : unqualified -> Utils.Pp.document
val pp_fragment : fragment -> Utils.Pp.document
val pp_offset : offset -> Utils.Pp.document
val pp_arr : t -> int option list -> Utils.Pp.document
val pp_arr_dim : int option -> Utils.Pp.document
val pp_field : field -> Utils.Pp.document
val pp_struct : struc -> Utils.Pp.document
val pp_enums : enum -> Utils.Pp.document
val pp_env : env -> Utils.Pp.document
Print the whole environement (not the linksem indexed environment)
This section is about getting the type at a specific offset in things.
val struct_at : env:env -> size:int -> struc -> int -> t Utils.Option.t
Same as type_at
but for structs
val type_at : env:env -> size:int -> t -> int -> t Utils.Option.t
Get the type of size size
at the provided offset in another type
Dw.Func
This module contain all the definition to handle functions as defined in the DWARF information of the target file
type func
=
{
name : string; |
scope : scope; |
ret : Ctype.t option; |
}
Type of a dwarf function that may or may not be inlined
If this type stand on its own, then it is inlined. If it is inside a t
then it's a top level function. There is no separate type for inline functions because they do not have any special data that a top level function may not have
and scope
=
{
vars : Var.t list; |
funcs : func list; |
scopes : scope list; |
}
Type of a dwarf scope that may contain recursively other data. The lists here have the semantic meaning of sets: the order is irrelevant.
val func_of_linksem : Elf.File.t -> Ctype.env -> linksem_func -> func
Create a Dwarf function from its linksem counterpart
val scope_of_linksem : Elf.File.t -> Ctype.env -> linksem_scope -> scope
Create and Dwarf scope from its linksem counterpart
val func_get_api : func -> Arch.func_api
Get the API of the function
val pp_raw_func : func -> Utils.Pp.OCaml.representation
val pp_raw_scope : scope -> Utils.Pp.OCaml.representation
type t
=
{
sym : Elf.Symbol.t option; |
func : func; |
}
This the type of a top-level function. It may have an associated elf symbol
This type will contain all necessary indexes for function-wide fast access to relevant dwarf information.
val of_linksem : Elf.File.t -> Ctype.env -> linksem_t -> t
Create a dwarf top level function from its Linksem counterpart. The ELF file is to get a potential matching symbol. For now the matching is made with name and then code address, but perhaps the code using this should be rephrased in terms of addresses instead of symbols, to be more resilient
val get_api : t -> Arch.func_api
Get the API of a top level function
Dw.Func
This module contain all the definition to handle functions as defined in the DWARF information of the target file
Type of a dwarf function that may or may not be inlined
If this type stand on its own, then it is inlined. If it is inside a t
then it's a top level function. There is no separate type for inline functions because they do not have any special data that a top level function may not have
Type of a dwarf scope that may contain recursively other data. The lists here have the semantic meaning of sets: the order is irrelevant.
val func_of_linksem : Elf.File.t -> Ctype.env -> linksem_func -> func
Create a Dwarf function from its linksem counterpart
val scope_of_linksem : Elf.File.t -> Ctype.env -> linksem_scope -> scope
Create and Dwarf scope from its linksem counterpart
val func_get_api : func -> Arch.func_api
Get the API of the function
val pp_raw_func : func -> Utils.Pp.document
val pp_raw_scope : scope -> Utils.Pp.document
This the type of a top-level function. It may have an associated elf symbol
This type will contain all necessary indexes for function-wide fast access to relevant dwarf information.
val of_linksem : Elf.File.t -> Ctype.env -> linksem_t -> t
Create a dwarf top level function from its Linksem counterpart. The ELF file is to get a potential matching symbol. For now the matching is made with name and then code address, but perhaps the code using this should be rephrased in terms of addresses instead of symbols, to be more resilient
val get_api : t -> Arch.func_api
Get the API of a top level function
val pp_raw : t -> Utils.Pp.document
Pretty print a raw top level function
Dw.Loc
This module represent architectural locations. Locations in DWARF information are represented as a little stack language (represented as dwop
list
) which describes how to compute the target value from the current concrete state. This computation can be arbitrarily complex, for example take half of the value from the high bits of a register and then the other half from memory at a specific address coming from another register.
Those expression are not directly usable to guide the type inference, as we would much more like simple direct information like "This value is in that register". To solve this, there is a custom type t
that interpret simple static locations directly and leaves more complex location uninterpreted.
This interpretation is currently very basic. It remains to be seen if it actually need to be improved.
type t
=
| Register of State.Reg.t | In the register |
| RegisterOffset of State.Reg.t * int | At register + offset address |
| StackFrame of int | On the stackFrame with offset |
| Global of Elf.SymTable.sym_offset | Global variable with an offset |
| Dwarf of dwop list | Uninterpreted dwarf location |
The type of a location, as static as possible
type linksem_t
= dwop list
The type of a location in linksem format
val vDW_OP_addr : int
The integer value of the DW_OP_addr constant in DWARF standard
TODO this should come from LinkSem's dwarf
val of_linksem : ?amap:Arch.dwarf_reg_map -> Elf.File.t -> linksem_t -> t
Convert a linksem location description into a Loc.t
Very naive for now : If the list has a single element that we can translate directly, we do. Otherwise, we dump it into the t.Dwarf
constructor
val to_string : t -> string
Convert the location to a string. This is not reversible
Dw.Loc
This module represent architectural locations. Locations in DWARF information are represented as a little stack language (represented as dwop
list
) which describes how to compute the target value from the current concrete state. This computation can be arbitrarily complex, for example take half of the value from the high bits of a register and then the other half from memory at a specific address coming from another register.
Those expression are not directly usable to guide the type inference, as we would much more like simple direct information like "This value is in that register". To solve this, there is a custom type t
that interpret simple static locations directly and leaves more complex location uninterpreted.
This interpretation is currently very basic. It remains to be seen if it actually need to be improved.
type t =
| Register of State.Reg.t
In the register
*)| RegisterOffset of State.Reg.t * int
At register + offset address
*)| StackFrame of int
On the stackFrame with offset
*)| Global of Elf.SymTable.sym_offset
Global variable with an offset
*)| Const of Utils.Sym.t
| Dwarf of dwop list
Uninterpreted dwarf location
*)The type of a location, as static as possible
type linksem_t = dwop list
The type of a location in linksem format
The integer value of the DW_OP_addr constant in DWARF standard
TODO this should come from LinkSem's dwarf
val of_linksem : ?amap:Arch.dwarf_reg_map -> Elf.File.t -> linksem_t -> t
val to_string : t -> string
Convert the location to a string. This is not reversible
val pp : t -> Utils.Pp.document
Pretty-print the location
Dw.Var
This module contain all the definition to handle local and global variables as defined in the DWARF information of the target file
type t
=
{
name : string; |
param : bool; |
ctype : Ctype.t; |
locs : ((int * int) * Loc.t) list; |
}
Type of a DWARF variable
val clamp_z : Z.t -> int
Convert from Z.t to int, if there is an overflow, returns Int.max_int instead of throwing
val of_linksem : Elf.File.t -> Ctype.env -> linksem_t -> t
Create a DWARF variable from its linksem counterpart
Dw.Var
This module contain all the definition to handle local and global variables as defined in the DWARF information of the target file
type range = Elf.Address.t * Elf.Address.t option
type t = {
name : string;
param : bool;
ctype : Ctype.t;
locs : (range * Loc.t) list;
locs_frame_base : (range * Loc.t) list;
}
Type of a DWARF variable
Merge contiguous location lists
val end_addr_of_sym : Utils.Sym.t -> Elf.Address.t option
val of_linksem : Elf.File.t -> Ctype.env -> linksem_t -> t
Create a DWARF variable from its linksem counterpart
val pp_raw : t -> Utils.Pp.document
Pretty print a variable
Dw
This module provides the specifically interpreted DWARF information needed for read-dwarf operations
I would have called this module dwarf but linksem decided that is was a good idea to dump all its modules in the global namespace.
module Var : sig ... end
This module contain all the definition to handle local and global variables as defined in the DWARF information of the target file
module Func : sig ... end
This module contain all the definition to handle functions as defined in the DWARF information of the target file
module Loc : sig ... end
This module represent architectural locations. Locations in DWARF information are represented as a little stack language (represented as dwop
list
) which describes how to compute the target value from the current concrete state. This computation can be arbitrarily complex, for example take half of the value from the high bits of a register and then the other half from memory at a specific address coming from another register.
type t
=
{
elf : Elf.File.t; |
ldwarf : Dwarf.dwarf; |
ldwarf_sdt : Dwarf.sdt_dwarf; |
funcs : (string, Func.t) Stdlib.Hashtbl.t; |
vars : (string, Var.t) Stdlib.Hashtbl.t; |
tenv : Ctype.env; |
}
The type that represent a elf-dwarf binary whose information has been fully interpreted
val dwarferror : ('a, unit, string, 'b) Stdlib.format4 -> 'a
Throw a DwarfError
val of_elf : Elf.File.t -> t
Get Dwarf information from an Elf file.
May raise a DwarfError
if a problem occurs.
val of_file : string -> t
Get Dwarf information from an Elf file by name. Use File.of_file
Dw
This module provides the specifically interpreted DWARF information needed for read-dwarf operations
I would have called this module dwarf but linksem decided that is was a good idea to dump all its modules in the global namespace.
module Var : sig ... end
This module contain all the definition to handle local and global variables as defined in the DWARF information of the target file
module Func : sig ... end
This module contain all the definition to handle functions as defined in the DWARF information of the target file
module Loc : sig ... end
This module represent architectural locations. Locations in DWARF information are represented as a little stack language (represented as dwop
list
) which describes how to compute the target value from the current concrete state. This computation can be arbitrarily complex, for example take half of the value from the high bits of a register and then the other half from memory at a specific address coming from another register.
type t = {
elf : Elf.File.t;
ldwarf : Dwarf.dwarf;
ldwarf_sdt : Dwarf.sdt_dwarf;
funcs : (string, Func.t) Stdlib.Hashtbl.t;
vars : (string, Var.t) Stdlib.Hashtbl.t;
tenv : Ctype.env;
}
The type that represent a elf-dwarf binary whose information has been fully interpreted
Throw a DwarfError
val of_elf : Elf.File.t -> t
Get Dwarf information from an Elf file.
May raise a DwarfError
if a problem occurs.
val of_file : string -> t
Get Dwarf information from an Elf file by name. Use Elf.File.of_file
val pp_raw : t -> Utils.Pp.document
Pretty print dwarf data as an ocaml structure
Elf.File
module SymTbl = SymTable
type machine
=
| Supp of Config.Arch.t |
| Other of int |
The machine type of the ELF file. It can be a known architecture, or one that is not in Arch
type t
=
{
filename : string; | The name on the file system. Useful for error messages |
symbols : SymTbl.t; | The symbol table |
entry : int; | The address of the entry point; only used in |
machine : machine; | The target architecture of the file; only used in |
linksem : Elf_file.elf_file; | The original linksem structure for the file; only used in |
rodata : Segment.t; | The read-only data section |
}
The type containing all the information about an ELF file
val elferror : ('a, unit, string, 'b) Stdlib.format4 -> 'a
Throw an ElfError
val of_file : string -> t
Parse an ELF file to create an Elf.File.t
using Linksem.
May raise an ElfError
Elf.File
This module represent an ELF 64 file. We do not deal with 32 bit ELF files for now The main interesting information is the symbol table.
module SymTbl = SymTable
The machine type of the ELF file. It can be a known architecture, or one that is not in Arch
val pp_machine : machine -> Utils.Pp.document
Pretty prints a machine
module SMap : sig ... end
type t = {
filename : string;
The name on the file system. Useful for error messages
*)symbols : SymTbl.t;
The symbol table
*)entry : int;
The address of the entry point; only used in dumpSym.ml
machine : machine;
The target architecture of the file; only used in arch.ml, dumpSym.ml, dw.ml
linksem : Elf_file.elf_file;
The original linksem structure for the file; only used in dw.ml
rodata : Segment.t SMap.t;
The read-only data sections
*)sections : section list;
}
The type containing all the information about an ELF file
Throw an ElfError
val of_file : string -> t
Parse an ELF file to create an Elf.File.t
using Linksem.
May raise an ElfError
Elf.Segment
type t
=
{
data : Utils.BytesSeq.t; | |
addr : int; | The actual start address of the BytesSeq |
size : int; | redundant with |
read : bool; | |
write : bool; | |
execute : bool; |
}
The type of a segment
val of_linksem : Elf_interpreted_segment.elf64_interpreted_segment -> t
Loads a t
using a linksem interpreted segment.
val is_in : t -> int -> bool
Check if an address is inside a segment
val get_addr : (Utils.BytesSeq.t -> int -> 'a) -> t -> int -> 'a
Get a value at an address which is in this segment using the getter provided
val get_addr_list_opt : (Utils.BytesSeq.t -> int -> 'a) -> t list -> int -> 'a option
Get a value at an address which is one of the segment of this list. It must be entirely in one of the segment
Elf.Segment
The goal of this module is to represent a segment as loaded in memory. In particular, all information about file layout is intentionally lost I use basic ints for speed. It it fails for some reason, I'll move to int64s.
This is basically a Utils.BytesSeq
with metadata.
type t = {
data : Utils.BytesSeq.t * Relocations.t;
addr : int;
The actual start address of the BytesSeq
*)size : int;
read : bool;
write : bool;
execute : bool;
}
The type of a segment
val of_linksem : Elf_interpreted_segment.elf64_interpreted_segment -> t
Loads a t
using a linksem interpreted segment.
val is_in : t -> int -> bool
Check if an address is inside a segment
val get_addr :
+ ((Utils.BytesSeq.t * Relocations.t) -> int -> 'a) ->
+ t ->
+ int ->
+ 'a
Get a value at an address which is in this segment using the getter provided
val get_addr_list_opt :
+ ((Utils.BytesSeq.t * Relocations.t) -> int -> 'a) ->
+ t list ->
+ int ->
+ 'a option
Get a value at an address which is one of the segment of this list. It must be entirely in one of the segment
Elf.SymTable
type sym
= Symbol.t
type linksem_sym
= Symbol.linksem_t
type sym_offset
= sym * int
The type of a symbol with offset
val empty : t
The empty symbol table
val add : t -> sym -> t
Return a new table with the symbol added If there already exists a symbol covering the same area, merge them with Symbol.t.other_names
val of_addr : t -> int -> sym
Get the symbol owning that address. Not_found is raised if no symbol own that address.data See of_addr_opt
val of_addr_opt : t -> int -> sym option
Get the symbol owning that address. None if no symbol own that address. See of_addr
val of_addr_with_offset : t -> int -> sym_offset
Get a symbol with the offset that correspond to that address
val of_addr_with_offset_opt : t -> int -> sym_offset option
Get a symbol with the offset that correspond to that address
val to_addr_offset : sym_offset -> int
Get back the raw address from a symbol+offset value
val string_of_sym_offset : sym_offset -> string
Transform a symbol + offset into the corresponding string
val sym_offset_of_string : t -> string -> sym_offset
Transform a symbol + offset string into the actual symbol and the integer offset
val of_position_string : t -> string -> sym_offset
Convert a position string to a symbol + offset
A position string is a string describing a position in an ELF file. Two format are accepted for now:
Elf.SymTable
The module provide a type to represent a symbol table.
The interesting operations provided are fetching symbol by name and knowing which symbol owns a specific address
of_position_string
provides a convenient way of describing a position in the ELF file from a human text input like the CLI.
type sym = Symbol.t
type linksem_sym = Symbol.linksem_t
type sym_offset = sym * int
The type of a symbol with offset
type linksem_t = LinksemRelocatable.global_symbol_init_info
val empty : t
The empty symbol table
Return a new table with the symbol added If there already exists a symbol covering the same area, merge them with Symbol.t.other_names
Get the symbol owning that address. Not_found is raised if no symbol own that address.data See of_addr_opt
Get the symbol owning that address. None if no symbol own that address. See of_addr
val of_addr_with_offset : t -> Address.t -> sym_offset
Get a symbol with the offset that correspond to that address
val of_addr_with_offset_opt : t -> Address.t -> sym_offset option
Get a symbol with the offset that correspond to that address
val to_addr_offset : sym_offset -> Address.t
Get back the raw address from a symbol+offset value
val string_of_sym_offset : sym_offset -> string
Transform a symbol + offset into the corresponding string
val sym_offset_of_string : t -> string -> sym_offset
Transform a symbol + offset string into the actual symbol and the integer offset
val of_position_string : t -> string -> sym_offset
Convert a position string to a symbol + offset
A position string is a string describing a position in an ELF file. Two format are accepted for now:
Extract the symbol from the linksem symbol representation.
Need the segments for filling the missing symbol data
val pp_raw : t -> Utils.Pp.document
Pretty print the table as a raw ocaml value
Elf.Symbol
type linksem_typ
= Z.t
type t
=
{
name : string; |
other_names : string list; |
typ : typ; |
addr : int; |
size : int; |
writable : bool; |
data : Utils.BytesSeq.t; |
}
The ELF symbol. This type guarantee the data exists contrary to linksem symbols (it may be all zeros though)
type linksem_t
= string * (Z.t * Z.t * Z.t * Utils.BytesSeq.t option * Z.t)
The type of an ELF symbol in linksem. See of_linksem
val is_in : t -> int -> bool
Check if an address is in a symbol
val len : t -> int
For conformance with the Utils.RngMap.LenObject
module type
val typ_of_linksem : linksem_typ -> typ
Convert the integer type into typ
val linksem_typ : linksem_t -> linksem_typ
Get the type from the linksem symbol type
exception
LoadingError of string * int
LoadingError(name,addr)
means that symbol name
at addr
could not be loaded.
val of_linksem : Segment.t list -> linksem_t -> t
Convert a symbol from linksem to read-dwarf representation using the segment data
May raise LoadingError
when the symbol has no data and the data cannot be found in the segments
val is_interesting : typ -> bool
Tell if a symbol type is interesting for readDwarf purposes
val is_interesting_linksem : linksem_t -> bool
Tell if a linksem symbol is interesting for readDwarf purposes
val sub : t -> int -> int -> Utils.BytesSeq.t
Take the BytesSeq.t corresponding to the offset and length
Elf.Symbol
This module represent an Elf symbol. One important difference with linksem symbols is that the symbols of this module always have the corresponding data (code or initial value). That's why function like of_linksem_with_data
exist.
For now addresses are in ints and assume the top bit is sign extended. It may become Int64.t if required
type t = {
name : string;
other_names : string list;
typ : typ;
addr : Address.t;
size : int;
writable : bool;
data : data;
}
The ELF symbol. This type guarantee the data exists contrary to linksem symbols (it may be all zeros though)
type linksem_t = LinksemRelocatable.symbol
The type of an ELF symbol in linksem. See of_linksem
Check if an address is in a symbol
val len : t -> int
For conformance with the Utils.RngMap.LenObject
module type
val typ_of_linksem : linksem_typ -> typ
Convert the integer type into typ
val linksem_typ : linksem_t -> linksem_typ
Get the type from the linksem symbol type
LoadingError(name,addr)
means that symbol name
at addr
could not be loaded.
Convert a symbol from linksem to read-dwarf representation using the segment data
May raise LoadingError
when the symbol has no data and the data cannot be found in the segments
val is_interesting : typ -> bool
Tell if a symbol type is interesting for readDwarf purposes
val is_interesting_linksem : linksem_t -> bool
Tell if a linksem symbol is interesting for readDwarf purposes
val pp_typ : typ -> Utils.Pp.document
Pretty prints a symbol type
val pp_raw : t -> Utils.Pp.document
Raw pretty printing of a symbol
Elf
module File : sig ... end
module Segment : sig ... end
module SymTable : sig ... end
module Symbol : sig ... end
Elf
module Address : sig ... end
module File : sig ... end
This module represent an ELF 64 file. We do not deal with 32 bit ELF files for now The main interesting information is the symbol table.
module LinksemRelocatable : sig ... end
module Relocations : sig ... end
module Segment : sig ... end
The goal of this module is to represent a segment as loaded in memory. In particular, all information about file layout is intentionally lost I use basic ints for speed. It it fails for some reason, I'll move to int64s.
module SymTable : sig ... end
The module provide a type to represent a symbol table.
module Symbol : sig ... end
This module represent an Elf symbol. One important difference with linksem symbols is that the symbols of this module always have the corresponding data (code or initial value). That's why function like of_linksem_with_data
exist.
Exp.ConcreteEval
This module provides a way of making concrete evaluation of an expression. The only required thing is a context
.
type 'v context
= 'v -> Value.t
A map from variables to concrete values. This map should throw Symbolic
when the variable should be treated as symbolic.
val eval : ?ctxt:'v context -> ('a, 'v, Ast.no, Ast.no) Ast.exp -> Value.t
Evaluate concretely an expression and return a Value
.
If the expression is not concrete, it will throw Symbolic
.
The default ctxt
consider all variables to be symbolic.
eval
may succeed even if is_concrete
is false
in presence of conditionals. Indeed only the taken branch of the conditional is evaluated, so the other may be symbolic. For example:
eval ExpTyped.(ite ~cond:(bool true) (bits_smt #x2a) (var ...)) = ExpTyped.(bits_smt #x2a)
val is_concrete : (_, _, _, _) Ast.exp -> bool
Tell if an expression is concrete, which means no variables of any kind
Exp.ConcreteEval
This module provides a way of making concrete evaluation of an expression. The only required thing is a context
.
type 'v context = 'v -> Value.t
A map from variables to concrete values. This map should throw Symbolic
when the variable should be treated as symbolic.
Evaluate concretely an expression and return a Value
.
If the expression is not concrete, it will throw Symbolic
.
The default ctxt
consider all variables to be symbolic.
eval
may succeed even if is_concrete
is false
in presence of conditionals. Indeed only the taken branch of the conditional is evaluated, so the other may be symbolic. For example:
eval ExpTyped.(ite ~cond:(bool true) (bits_smt #x2a) (var ...)) = ExpTyped.(bits_smt #x2a)
val is_concrete : (_, _, _, _) Ast.exp -> bool
Tell if an expression is concrete, which means no variables of any kind
Make.1-Var
Make.Var
val pp : t -> Utils.Pp.document
Pretty printer to be used, both for memory pretty printing and for sending memory to Z3
Exp.Make
type var
= Var.t
The type of variable provided in the functor
Exp.Make
type var = Var.t
The type of variable provided in the functor
Test syntactic equality. a + b
and b + a
would test different under this predicate
val pp : t -> Utils.Pp.document
Pretty print the expression using PpExp
val pp_smt : t -> Utils.Pp.document
Pretty print the expression in SMTLIB language
Exp.PpExp
This module provides a human readable pretty printing for Ast
expressions
If you don't want to bother with details, use pp_exp
and don't read the rest.
The precedence are the ones from C/C++ and Ocaml with some tweaks. In particular the precedence between bitwise operation and arithmetic operation are separated, so parenthesis will always be required between them.
The order is:
Unary operator cannot linebreak (but their content can)
Examples:
-a[1-3].2a:6[z+32]
which is: (bvneg (concat ((_ extract 3 1) a) ((_ zero_extend 32) #b101010)))
type prec
=
| IF |
| OR |
| AND |
| EQ |
| COMP |
| ADD |
| MUL |
| BITS |
| SHIFTS |
| UNARY |
| CONCAT |
| EXTRACT |
| PARENS |
The operators possible precedence
val compat : outer:prec -> inner:prec -> bool
Figure out if an expression of precedence inner
in an expression of precedence outer
needs parentheses
val prec_unop : Ast.unop -> prec
val prec_bvarith : Ast.bvarith -> prec
val prec_binop : Ast.no Ast.binop -> prec
val prec_bvmanyarith : Ast.bvmanyarith -> prec
val prec_manyop : Ast.manyop -> prec
val pp_bits : Utils.BitVec.t -> Utils.Pp.document
val ppnot : Utils.Pp.document
val pp_unop : Ast.unop -> Utils.Pp.document -> Utils.Pp.document
val sym_bvarith : Ast.bvarith -> Utils.Pp.document
val sym_bvcomp : Ast.bvcomp -> Utils.Pp.document
val pp_binop : Ast.no Ast.binop -> Utils.Pp.document -> Utils.Pp.document -> Utils.Pp.document
val sym_bvmanyarith : Ast.bvmanyarith -> Utils.Pp.document
val pp_manyop : Ast.manyop -> PPrintEngine.document list -> Utils.Pp.document
val pp_if : PPrintEngine.document -> PPrintEngine.document -> PPrintEngine.document -> Utils.Pp.document
val pp_exp_prec : ('v -> Utils.Pp.document) -> ('a, 'v, Ast.no, Ast.no) Ast.exp -> Utils.Pp.document * prec
Pretty print an expression and return its precedence
Exp.PpExp
This module provides a human readable pretty printing for Ast
expressions
If you don't want to bother with details, use pp_exp
and don't read the rest.
The precedence are the ones from C/C++ and Ocaml with some tweaks. In particular the precedence between bitwise operation and arithmetic operation are separated, so parenthesis will always be required between them.
The order is:
Unary operator cannot linebreak (but their content can)
Examples:
-a[1-3].2a:6[z+32]
which is: (bvneg (concat ((_ extract 3 1) a) ((_ zero_extend 32) #b101010)))
The operators possible precedence
Figure out if an expression of precedence inner
in an expression of precedence outer
needs parentheses
val prec_bvarith : Ast.bvarith -> prec
val prec_bvmanyarith : Ast.bvmanyarith -> prec
val prec_manyop : Ast.manyop -> prec
val pp_bits : Utils.BitVec.t -> Utils.Pp.document
val ppnot : Utils.Pp.document
val pp_unop : Ast.unop -> Utils.Pp.document -> Utils.Pp.document
val sym_bvarith : Ast.bvarith -> Utils.Pp.document
val sym_bvcomp : Ast.bvcomp -> Utils.Pp.document
val pp_binop :
+ Ast.no Ast.binop ->
+ Utils.Pp.document ->
+ Utils.Pp.document ->
+ Utils.Pp.document
val sym_bvmanyarith : Ast.bvmanyarith -> Utils.Pp.document
val pp_manyop : Ast.manyop -> Utils.Pp.document list -> Utils.Pp.document
val pp_if :
+ Utils.Pp.document ->
+ Utils.Pp.document ->
+ Utils.Pp.document ->
+ Utils.Pp.document
val pp_exp_prec :
+ ('v -> Utils.Pp.document) ->
+ ('a, 'v, Ast.no, Ast.no) Ast.exp ->
+ Utils.Pp.document * prec
Pretty print an expression and return its precedence
val pp_exp :
+ ('a -> Utils.Pp.document) ->
+ ('b, 'a, Ast.no, Ast.no) Ast.exp ->
+ Utils.Pp.document
The main function for pretty printing an expression
Exp.Sums
This module provide sum manipulation functionality on top of typed expression Typed.t
This provide a semantic view of sums as list of terms and conversion
val split : ('v, 'm) Typed.t -> ('v, 'm) Typed.t list
Split an expression as a list of terms. This function sees through +,- and extracts.
Any expression e
should have the same semantic meaning as Typed.sum (split e)
.
TODO I need to sort according to an arbitrary order to be able to compare reliably. This will probably be part of a more general simplifier work.
val merge : size:int -> ('v, 'm) Typed.t list -> ('v, 'm) Typed.t
Merge a list of terms into a sum expression. This is an upgrade of Typed.sum
to allow empty lists. In the case of an empty list, a 0
of size size
will be inserted instead.
val add_term : term:('v, 'm) Typed.t -> ('v, 'm) Typed.t -> ('v, 'm) Typed.t
Add a term
to a sum, This is the same, as using split
, then adding term
to the list, then merging with Typed.sum
val remove_term : equal:(('v, 'm) Typed.t -> ('v, 'm) Typed.t -> bool) -> term:('v, 'm) Typed.t -> ('v, 'm) Typed.t -> ('v, 'm) Typed.t option
Remove a term
from a sum. Return Some res
if successful and None
otherwise.
val smart_substract : equal:(('v, 'm) Typed.t -> ('v, 'm) Typed.t -> bool) -> term:('v, 'm) Typed.t -> ('v, 'm) Typed.t -> ('v, 'm) Typed.t
Same as remove_term
but if the term
is not found, add the opposite (Ast.unop.Bvneg
) to the sum
val split_concrete : ('v, Ast.no) Typed.t -> ('v, Ast.no) Typed.t option * Utils.BitVec.t
Split away the concrete terms of the sum and the symbolic part. The symbolic part can be None
if the expression was fully concrete. If the symbolic part is Some e
, then not
has_concrete_term
e
will hold.
val has_concrete_term : ('v, 'm) Typed.t -> bool
Tells if an expression has a concrete term
Exp.Sums
This module provide sum manipulation functionality on top of typed expression Typed.t
This provide a semantic view of sums as list of terms and conversion
Split an expression as a list of terms. This function sees through +,- and extracts.
Any expression e
should have the same semantic meaning as Typed.sum (split e)
.
TODO I need to sort according to an arbitrary order to be able to compare reliably. This will probably be part of a more general simplifier work.
Merge a list of terms into a sum expression. This is an upgrade of Typed.sum
to allow empty lists. In the case of an empty list, a 0
of size size
will be inserted instead.
val remove_term :
+ equal:(('v, 'm) Typed.t -> ('v, 'm) Typed.t -> bool) ->
+ term:('v, 'm) Typed.t ->
+ ('v, 'm) Typed.t ->
+ ('v, 'm) Typed.t option
Remove a term
from a sum. Return Some res
if successful and None
otherwise.
val smart_substract :
+ equal:(('v, 'm) Typed.t -> ('v, 'm) Typed.t -> bool) ->
+ term:('v, 'm) Typed.t ->
+ ('v, 'm) Typed.t ->
+ ('v, 'm) Typed.t
Same as remove_term
but if the term
is not found, add the opposite (Ast.unop.Bvneg
) to the sum
val split_concrete :
+ ('v, Ast.no) Typed.t ->
+ ('v, Ast.no) Typed.t option * Utils.BitVec.t
Split away the concrete terms of the sum and the symbolic part. The symbolic part can be None
if the expression was fully concrete. If the symbolic part is Some e
, then not
has_concrete_term
e
will hold.
val has_concrete_term : ('v, 'm) Typed.t -> bool
Tells if an expression has a concrete term
Exp.Typed
This module provide operation on typed expressions i.e expressions whose annotations are their SMT type (Ast.ty
).
type ('v, 'm) t
= ('m Ast.ty, 'v, Ast.no, 'm) Ast.exp
The type for a typed expression.
'v
type is the type of expression variable.'m
type should be either Ast.no
if memory operation are disabled or Ast.Size.t
if they are enabled.The let bindings are always disabled.
val get_type : ('v, 'm) t -> 'm Ast.ty
Get the type of a typed expression. Specialized version of Ast.Manip.annot_exp
This constuctors build a new expression as required, and compute the new type. They assume the operation is well typed (They assert it).
val var : typ:'m Ast.ty -> 'v -> ('v, 'm) t
val bits : Utils.BitVec.t -> ('v, 'm) t
val bool : bool -> ('v, 'm) t
val enum : Ast.enum -> ('v, 'm) t
val vec_idx_type : 'a Ast.ty
This is completely arbitrary, because it is not currently used.
val vec : ('a Ast.ty, 'b, Ast.no, 'a) Ast.exp list -> ('a Ast.ty, 'b, Ast.no, 'a) Ast.exp
val unop : Ast.unop -> ('v, 'm) t -> ('v, 'm) t
val binop : 'm Ast.binop -> ('v, 'm) t -> ('v, 'm) t -> ('v, 'm) t
val manyop : AstGen.Ott.manyop -> ('v, 'm) t list -> ('v, 'm) t
In addition to well-typedness requirement, this function will throw Invalid_argument
if the list is empty. If the list has a single element, it will just return that element instead of building the symbolic operation.
val bits_int : size:int -> int -> ('a, 'b) t
val bits_smt : string -> ('a, 'b) t
val zero : size:int -> ('a, 'b) t
val true_ : ('a, 'b) t
val false_ : ('a, 'b) t
val (+) : ('a, 'b) t -> ('a, 'b) t -> ('a, 'b) t
val sum : ('a, 'b) t list -> ('a, 'b) t
val sub : ('a, 'b) t -> ('a, 'b) t -> ('a, 'b) t
val (-) : ('a, 'b) t -> ('a, 'b) t -> ('a, 'b) t
val (*) : ('a, 'b) t -> ('a, 'b) t -> ('a, 'b) t
val prod : ('a, 'b) t list -> ('a, 'b) t
val sdiv : ('a, 'b) t -> ('a, 'b) t -> ('a, 'b) t
val not : ('a, 'b) t -> ('a, 'b) t
val neg : ('a, 'b) t -> ('a, 'b) t
val extract : first:int -> last:int -> ('a, 'b) t -> ('a, 'b) t
val eq : ('a, 'b) t -> ('a, 'b) t -> ('a, 'b) t
val (=) : ('a, 'b) t -> ('a, 'b) t -> ('a, 'b) t
val concat : ('a, 'b) t list -> ('a, 'b) t
val comp : Ast.bvcomp -> ('a, 'b) t -> ('a, 'b) t -> ('a, 'b) t
val add_type : a v m. ty_of_var:('a -> 'v -> 'm Ast.ty) -> ('a, 'v, Ast.no, 'm) Ast.exp -> ('v, 'm) t
Replace the annotation of expression by the SMT types. The expression must be already well typed (you can trust the SMT solver on this one)
It will still assert
that an expression is well typed as a side effect.
val is_well_typed : ('v, 'm) t -> bool
Check if an expression is well typed
Exp.Typed
This module provide operation on typed expressions i.e expressions whose annotations are their SMT type (Ast.ty
).
The type for a typed expression.
'v
type is the type of expression variable.'m
type should be either Ast.no
if memory operation are disabled or Ast.Size.t
if they are enabled.The let bindings are always disabled.
Get the type of a typed expression. Specialized version of Ast.Manip.annot_exp
val is_bool : 'a Ast.ty -> bool
val is_bv : 'a Ast.ty -> bool
val is_enum : 'a Ast.ty -> bool
val expect_bool : 'a Ast.ty -> unit
val expect_bv : 'a Ast.ty -> int
val expect_enum : 'a Ast.ty -> int
This constuctors build a new expression as required, and compute the new type. They assume the operation is well typed (They assert it).
val bits : Utils.BitVec.t -> ('v, 'm) t
val bool : bool -> ('v, 'm) t
val vec_idx_type : 'a Ast.ty
This is completely arbitrary, because it is not currently used.
val manyop : AstGen.Ott.manyop -> ('v, 'm) t list -> ('v, 'm) t
In addition to well-typedness requirement, this function will throw Invalid_argument
if the list is empty. If the list has a single element, it will just return that element instead of building the symbolic operation.
val bits_int : size:int -> int -> ('a, 'b) t
val bits_smt : string -> ('a, 'b) t
val zero : size:int -> ('a, 'b) t
val true_ : ('a, 'b) t
val false_ : ('a, 'b) t
val comp : Ast.bvcomp -> ('a, 'b) t -> ('a, 'b) t -> ('a, 'b) t
val add_type :
+ 'a 'v 'm. ty_of_var:('a -> 'v -> 'm Ast.ty) ->
+ ('a, 'v, Ast.no, 'm) Ast.exp ->
+ ('v, 'm) t
Replace the annotation of expression by the SMT types. The expression must be already well typed (you can trust the SMT solver on this one)
It will still assert
that an expression is well typed as a side effect.
val is_well_typed : ('v, 'm) t -> bool
Check if an expression is well typed
Exp.Value
This module provide a type to represent concrete values.
There is not concrete value for memory yet, but maybe there should be at some point to be able to fully support concrete evaluation without external tool
type t
=
| Bool of bool |
| Enum of Ast.enum |
| Bv of Utils.BitVec.t |
| Vec of t list |
The type of concrete values
val to_string : t -> string
String representation of concrete values
val bv : Utils.BitVec.t -> t
Bv
constructor
val expect_bool : t -> bool
Extract a boolean or fail
val expect_bv : ?size:int -> t -> Utils.BitVec.t
Extract a bit vector or fail. If size
is specified, then it fail if the size don't match
Exp.Value
This module provide a type to represent concrete values.
There is not concrete value for memory yet, but maybe there should be at some point to be able to fully support concrete evaluation without external tool
The type of concrete values
val to_string : t -> string
String representation of concrete values
val pp : t -> Utils.Pp.document
Pretty printer for concrete values
val bv : Utils.BitVec.t -> t
Bv
constructor
val expect_bool : t -> bool
Extract a boolean or fail
val expect_bv : ?size:int -> t -> Utils.BitVec.t
Extract a bit vector or fail. If size
is specified, then it fail if the size don't match
Exp
This module intends to provider a convenience expression module by lifting function like equality or pretty printing from variable to expressions.
It is restricted to typed expression as defined in Typed
For now it also do not support memory expression, but it will soon.
module ConcreteEval : sig ... end
This module provides a way of making concrete evaluation of an expression. The only required thing is a context
.
module Sums : sig ... end
This module provide sum manipulation functionality on top of typed expression Typed.t
module Typed : sig ... end
This module provide operation on typed expressions i.e expressions whose annotations are their SMT type (Ast.ty
).
module Value : sig ... end
This module provide a type to represent concrete values.
module type Var = sig ... end
This is the type signature for variable required by this module
module Pp = PpExp
Exp
This module intends to provider a convenience expression module by lifting function like equality or pretty printing from variable to expressions.
It is restricted to typed expression as defined in Typed
For now it also do not support memory expression, but it will soon.
module ConcreteEval : sig ... end
This module provides a way of making concrete evaluation of an expression. The only required thing is a context
.
module Sums : sig ... end
This module provide sum manipulation functionality on top of typed expression Typed.t
module Typed : sig ... end
This module provide operation on typed expressions i.e expressions whose annotations are their SMT type (Ast.ty
).
module Value : sig ... end
This module provide a type to represent concrete values.
module type Var = sig ... end
This is the type signature for variable required by this module
module Pp = PpExp
Exp.S
The signature of the output module of Make
Exp.S
The signature of the output module of Make
Test syntactic equality. a + b
and b + a
would test different under this predicate
val pp : t -> Utils.Pp.document
Pretty print the expression using PpExp
val pp_smt : t -> Utils.Pp.document
Pretty print the expression in SMTLIB language
Exp.Var
This is the type signature for variable required by this module
Exp.Var
This is the type signature for variable required by this module
val pp : t -> Utils.Pp.document
Pretty printer to be used, both for memory pretty printing and for sending memory to Z3
This page describe the whole instruction pipeline from how isla is called, to how to run them on states.
The main idea to represent instruction semantics, is that of trace. A trace is a list of event that affect the current machine state. Those trace may also contain assertions, which means that the trace only decribes the behavior of a state that satisfy those assertions, and do not define any behavior for a state that do not satisfy those assertion. With a set of such traces you can define the behavior of a whole instruction.
If a state statisfy none of the assertion of any traces of an instruction then running that instruction on that state is undefined behavior. In pratice even if the normal model of sail fully defines an instruction on every state, we will manually make some behavior undefined. In particular all processors exceptions are considered undefined behavior for now.
In the implementation there is two different fromat for traces: The original isla format from the isla tool and a custom simpler format in the Trace
module.
Isla is a tool to symbolically execute a Sail ISA semantic description. Isla will take such semantics and use it to generate symbolic traces for instructions, In this section I describe how I process those traces. The interesting types are defined in Isla
.
First to call isla itself. All of this is done in Isla.Server
. The Isla tool suite expect *.ir
file that is some sort of preprocessed sail source.
This processing is done by isla-sail
, but they can be found in isla-snapshots
. For now we also keep a working snapshot aarch64.ir
in the root directory.
To call isla, Isla.Server
use the isla-client
program. This program will take an hexadecimal opcode and call the isla_client
sail function in the *.ir
file. This function take the opcode, decode it and run it, all of which is done symbolically by isla. isla-client
then send back a symbolic recording of all operations used by the sail code. This code is parsed using the isla-lang
library by the Isla
module that is a wrapper around isla-lang
functionality.
As the raw trace contained by isla are big and contain a lot of useless information, I first preprocess them to remove a maximum of useless part in Isla.Preprocess
Isla trace are cached by Isla.Cache
by opcode. This organisation assumes that there is no configuration to send to isla before symbolically running an instruction and thus each opcode correspond to a single trace representation.
Then Isla.Type
can be used to type the isla traces and at the same time discover all register used by the trace and thus potentially add them to the State.Reg
module.
There are two way of used those isla traces, running them directly on states with Isla.Run
which is kind of deprecated, or going trough the internal Trace
format.
Even after preprocession, Isla traces are still quite complex. They consider meaningful event that in the usual setting are not like reading a register. They also contain a lot a possible event about processor sleeping and concurency that read-dwarf do not support. Therefore a new trace format has been created in Trace
that only contains the part of behavior that read-dwarf actually use and understand. The trace are also cached in Trace.Cache
.
Full instruction are represented by Run.Instr
.t that contain all the traces but also additional metadata that can be useful.
See SymbolicExecution
to see how to run those traces.
This page describe the whole instruction pipeline from how isla is called, to how to run them on states.
The main idea to represent instruction semantics, is that of trace. A trace is a list of event that affect the current machine state. Those trace may also contain assertions, which means that the trace only decribes the behavior of a state that satisfy those assertions, and do not define any behavior for a state that do not satisfy those assertion. With a set of such traces you can define the behavior of a whole instruction.
If a state statisfy none of the assertion of any traces of an instruction then running that instruction on that state is undefined behavior. In pratice even if the normal model of sail fully defines an instruction on every state, we will manually make some behavior undefined. In particular all processors exceptions are considered undefined behavior for now.
In the implementation there is two different fromat for traces: The original isla format from the isla tool and a custom simpler format in the Trace
module.
Isla is a tool to symbolically execute a Sail ISA semantic description. Isla will take such semantics and use it to generate symbolic traces for instructions, In this section I describe how I process those traces. The interesting types are defined in Isla
.
First to call isla itself. All of this is done in Isla.Server
. The Isla tool suite expect *.ir
file that is some sort of preprocessed sail source.
This processing is done by isla-sail
, but they can be found in isla-snapshots
. For now we also keep a working snapshot aarch64.ir
in the root directory.
To call isla, Isla.Server
use the isla-client
program. This program will take an hexadecimal opcode and call the isla_client
sail function in the *.ir
file. This function take the opcode, decode it and run it, all of which is done symbolically by isla. isla-client
then send back a symbolic recording of all operations used by the sail code. This code is parsed using the isla-lang
library by the Isla
module that is a wrapper around isla-lang
functionality.
As the raw trace contained by isla are big and contain a lot of useless information, I first preprocess them to remove a maximum of useless part in Isla.Preprocess
Isla trace are cached by Isla.Cache
by opcode. This organisation assumes that there is no configuration to send to isla before symbolically running an instruction and thus each opcode correspond to a single trace representation.
Then Isla.Type
can be used to type the isla traces and at the same time discover all register used by the trace and thus potentially add them to the State.Reg
module.
There are two way of used those isla traces, running them directly on states with Isla.Run
which is kind of deprecated, or going trough the internal Trace
format.
Even after preprocession, Isla traces are still quite complex. They consider meaningful event that in the usual setting are not like reading a register. They also contain a lot a possible event about processor sleeping and concurency that read-dwarf do not support. Therefore a new trace format has been created in Trace
that only contains the part of behavior that read-dwarf actually use and understand. The trace are also cached in Trace.Cache
.
Full instruction are represented by Run.Instr.t
that contain all the traces but also additional metadata that can be useful.
See SymbolicExecution
to see how to run those traces.
Isla.Base
This module wraps all isla-lang
functionality. No other module should directly touch the Isla_lang
module.
The isla trace syntax is compose of events that a regrouped into traces. There are a lot of possible events, and some of them are unsupported. in particular all the ones that deal with concurrency.
All the variable in isla traces are numbered. They are named v28
in the syntax and are represented as a plain int
in Ocaml. The structure of the traces is a mix of SMT declaration about those variable and actual processor events. SMT declaration use SMT expressions of type exp
which entirely distinct from Ast.exp
(Use Isla.Conv
to convert). On the contrary processor event do not contain direct SMT expressions but Sail values of type valu
. A valu
can be either a single symbolic variable, a concrete bitvector/boolean/enumeration value or a more complex sail structure with fields that contain other valu
s and some other things.
Event of reading and writing complex register like PSTATE
in AArch64
will provide the whole structure as a valu to be read or written. However, Those events may contain a accessor list that implies that only specific field of the struct are written or read. This useful, because of the flat representation of registers in State.Reg
: each field is considered to be a separate register.
The isla types are polymorphic over the annotation because that comes from isla-lang
, but in practice the only annotation I use the source position of type lrng
, that's why there is a set of aliases starting with r
.
include Isla_lang.AST
type enum
= int * int
type lrng
= Isla_lang__Isla_lang_ast.lrng
=
| UnknownRng |
| Generated of lrng |
| Range of Stdlib.Lexing.position * Stdlib.Lexing.position |
exception
Parse_error_locn of lrng * string
val pp_lpos : Stdlib.Lexing.position -> PPrint.document
val pp_lrng : lrng -> PPrint.document
type bvmanyarith
= Isla_lang__Isla_lang_ast.bvmanyarith
=
| Bvand |
| Bvor |
| Bvxor |
| Bvadd |
| Bvmul |
type bvcomp
= Isla_lang__Isla_lang_ast.bvcomp
=
| Bvult |
| Bvslt |
| Bvule |
| Bvsle |
| Bvuge |
| Bvsge |
| Bvugt |
| Bvsgt |
type bvarith
= Isla_lang__Isla_lang_ast.bvarith
=
| Bvnand |
| Bvnor |
| Bvxnor |
| Bvsub |
| Bvudiv |
| Bvudivi |
| Bvsdiv |
| Bvsdivi |
| Bvurem |
| Bvsrem |
| Bvsmod |
| Bvshl |
| Bvlshr |
| Bvashr |
type manyop
= Isla_lang__Isla_lang_ast.manyop
=
| And |
| Or |
| Bvmanyarith of bvmanyarith |
| Concat |
type unop
= Isla_lang__Isla_lang_ast.unop
=
| Not |
| Bvnot |
| Bvredand |
| Bvredor |
| Bvneg |
| Extract of int * int |
| ZeroExtend of int |
| SignExtend of int |
type binop
= Isla_lang__Isla_lang_ast.binop
=
| Eq |
| Bvarith of bvarith |
| Bvcomp of bvcomp |
type accessor
= Isla_lang__Isla_lang_ast.accessor
=
| Field of string |
type ty
= Isla_lang__Isla_lang_ast.ty
=
| Ty_Bool |
| Ty_BitVec of int |
| Ty_Enum of int |
| Ty_Array of ty * ty |
type 'a exp
= 'a Isla_lang__Isla_lang_ast.exp
=
| Var of int * 'a |
| Bits of string * 'a |
| Bool of bool * 'a |
| Enum of enum * 'a |
| Unop of unop * 'a exp * 'a |
| Binop of binop * 'a exp * 'a exp * 'a |
| Manyop of manyop * 'a exp list * 'a |
| Ite of 'a exp * 'a exp * 'a exp * 'a |
type valu
= Isla_lang__Isla_lang_ast.valu
=
| Val_Symbolic of int |
| Val_Bool of bool |
| Val_I of int * int |
| Val_Bits of string |
| Val_Enum of enum |
| Val_String of string |
| Val_Unit |
| Val_NamedUnit of string |
| Val_Vector of valu list |
| Val_List of valu list |
| Val_Struct of (string * valu) list |
| Val_Poison |
type accessor_list
= Isla_lang__Isla_lang_ast.accessor_list
=
| Nil |
| Cons of accessor list |
type 'a smt
= 'a Isla_lang__Isla_lang_ast.smt
=
| DeclareConst of int * ty |
| DefineConst of int * 'a exp |
| Assert of 'a exp |
| DefineEnum of int |
type valu_option
= valu option
type valu_concrete
= Isla_lang__Isla_lang_ast.valu_concrete
=
| CVal_Bool of bool |
| CVal_I of int * int |
| CVal_Bits of string |
| CVal_Enum of enum |
| CVal_String of string |
| CVal_Unit |
| CVal_NamedUnit of string |
| CVal_Vector of valu list |
| CVal_List of valu list |
| CVal_Struct of (string * valu) list |
| CVal_Poison |
type 'a event
= 'a Isla_lang__Isla_lang_ast.event
=
| Smt of 'a smt * 'a |
| Branch of int * string * 'a |
| ReadReg of string * accessor_list * valu * 'a |
| WriteReg of string * accessor_list * valu * 'a |
| ReadMem of valu * valu * valu * int * valu_option * 'a |
| WriteMem of int * valu * valu * valu * int * valu_option * 'a |
| BranchAddress of valu * 'a |
| Barrier of valu * 'a |
| CacheOp of valu * valu * 'a |
| MarkReg of string * string * 'a |
| Cycle of 'a |
| Instr of valu * 'a |
| Sleeping of int * 'a |
| WakeRequest of 'a |
| SleepRequest of 'a |
type 'a trc
= 'a Isla_lang__Isla_lang_ast.trc
=
| Trace of 'a event list |
type 'a exp_val
= 'a Isla_lang__Isla_lang_ast.exp_val
=
| EV_Bits of string * 'a |
| EV_Bool of bool * 'a |
| EV_Enum of enum * 'a |
| EV_Unop of unop * 'a exp_val * 'a |
| EV_Binop of binop * 'a exp_val * 'a exp_val * 'a |
| EV_Manyop of manyop * 'a exp_val list * 'a |
| EV_Ite of 'a exp_val * 'a exp_val * 'a exp_val * 'a |
exception
ParseError of loc * string
Exception that represent an Isla parsing error
exception
LexError of loc * string
Exception that represent an Isla lexing error
type lexer
= Stdlib.Lexing.lexbuf -> Parser.token
type 'a parser
= lexer -> Stdlib.Lexing.lexbuf -> 'a
val parse : 'a parser -> ?filename:string -> Stdlib.Lexing.lexbuf -> 'a
Parse a single Isla instruction output from a Lexing.lexbuf
val parse_exp : ?filename:string -> Stdlib.Lexing.lexbuf -> rexp
Parse a single Isla expression from a Lexing.lexbuf
val parse_exp_string : ?filename:string -> string -> rexp
Parse a single Isla expression from a string
val parse_exp_channel : ?filename:string -> Stdlib.in_channel -> rexp
Parse a single Isla expression from a channel
val parse_trc : ?filename:string -> Stdlib.Lexing.lexbuf -> rtrc
Parse an Isla trace from a Lexing.lexbuf
val parse_trc_string : ?filename:string -> string -> rtrc
Parse an Isla trace from a string
val parse_trc_channel : ?filename:string -> Stdlib.in_channel -> rtrc
Parse an Isla trace from a channel
include Isla_lang.PP
val pp_raw_vvar : int -> PPrintEngine.document
val pp_raw_name : string -> PPrintEngine.document
val pp_raw_enum_ty : int -> PPrintEngine.document
val pp_raw_enum : Isla_lang__.Isla_lang_ast.enum -> PPrintEngine.document
val pp_raw_int : int -> PPrintEngine.document
val pp_raw_bvi : int -> PPrintEngine.document
val pp_raw_bv : string -> PPrintEngine.document
val pp_raw_str : string -> PPrintEngine.document
val pp_raw_ty : Isla_lang__.Isla_lang_ast.ty -> PPrintEngine.document
val pp_raw_bool : bool -> PPrintEngine.document
val pp_raw_unop : Isla_lang__.Isla_lang_ast.unop -> PPrintEngine.document
val pp_raw_bvarith : Isla_lang__.Isla_lang_ast.bvarith -> PPrintEngine.document
val pp_raw_bvcomp : Isla_lang__.Isla_lang_ast.bvcomp -> PPrintEngine.document
val pp_raw_binop : Isla_lang__.Isla_lang_ast.binop -> PPrintEngine.document
val pp_raw_bvmanyarith : Isla_lang__.Isla_lang_ast.bvmanyarith -> PPrintEngine.document
val pp_raw_manyop : Isla_lang__.Isla_lang_ast.manyop -> PPrintEngine.document
val pp_raw_exp : 'a Isla_lang__.Isla_lang_ast.exp -> PPrintEngine.document
val pp_raw_exp_val : 'a Isla_lang__.Isla_lang_ast.exp_val -> PPrintEngine.document
val pp_raw_smt : 'a Isla_lang__.Isla_lang_ast.smt -> PPrintEngine.document
val pp_raw_valu : Isla_lang__.Isla_lang_ast.valu -> PPrintEngine.document
val pp_raw_selem : (string * Isla_lang__.Isla_lang_ast.valu) -> PPrintEngine.document
val pp_raw_valu_concrete : Isla_lang__.Isla_lang_ast.valu_concrete -> PPrintEngine.document
val pp_raw_selem_concrete : (string * Isla_lang__.Isla_lang_ast.valu_concrete) -> PPrintEngine.document
val pp_raw_accessor : Isla_lang__.Isla_lang_ast.accessor -> PPrintEngine.document
val pp_raw_accessor_list : Isla_lang__.Isla_lang_ast.accessor_list -> PPrintEngine.document
val pp_raw_valu_option : Isla_lang__.Isla_lang_ast.valu_option -> PPrintEngine.document
val pp_raw_event : 'a Isla_lang__.Isla_lang_ast.event -> PPrintEngine.document
val pp_raw_trc : 'a Isla_lang__.Isla_lang_ast.trc -> PPrintEngine.document
val pp_vvar : int -> PPrintEngine.document
val pp_name : string -> PPrintEngine.document
val pp_enum_ty : int -> PPrintEngine.document
val pp_enum : Isla_lang__.Isla_lang_ast.enum -> PPrintEngine.document
val pp_int : int -> PPrintEngine.document
val pp_bvi : int -> PPrintEngine.document
val pp_bv : string -> PPrintEngine.document
val pp_str : string -> PPrintEngine.document
val pp_j : int -> string
val pp_ty : Isla_lang__.Isla_lang_ast.ty -> PPrintEngine.document
val pp_bool : bool -> PPrintEngine.document
val pp_unop : Isla_lang__.Isla_lang_ast.unop -> PPrintEngine.document
val pp_bvarith : Isla_lang__.Isla_lang_ast.bvarith -> PPrintEngine.document
val pp_bvcomp : Isla_lang__.Isla_lang_ast.bvcomp -> PPrintEngine.document
val pp_binop : Isla_lang__.Isla_lang_ast.binop -> PPrintEngine.document
val pp_bvmanyarith : Isla_lang__.Isla_lang_ast.bvmanyarith -> PPrintEngine.document
val pp_manyop : Isla_lang__.Isla_lang_ast.manyop -> PPrintEngine.document
val pp_exp : 'a Isla_lang__.Isla_lang_ast.exp -> PPrintEngine.document
val pp_exp_val : 'a Isla_lang__.Isla_lang_ast.exp_val -> PPrintEngine.document
val pp_smt : 'a Isla_lang__.Isla_lang_ast.smt -> PPrintEngine.document
val pp_valu : Isla_lang__.Isla_lang_ast.valu -> PPrintEngine.document
val pp_selem : (string * Isla_lang__.Isla_lang_ast.valu) -> PPrintEngine.document
val pp_valu_concrete : Isla_lang__.Isla_lang_ast.valu_concrete -> PPrintEngine.document
val pp_selem_concrete : (string * Isla_lang__.Isla_lang_ast.valu_concrete) -> PPrintEngine.document
val pp_accessor : Isla_lang__.Isla_lang_ast.accessor -> PPrintEngine.document
val pp_accessor_list : Isla_lang__.Isla_lang_ast.accessor_list -> PPrintEngine.document
val pp_valu_option : Isla_lang__.Isla_lang_ast.valu_option -> PPrintEngine.document
val pp_event : 'a Isla_lang__.Isla_lang_ast.event -> PPrintEngine.document
val pp_trc : 'a Isla_lang__.Isla_lang_ast.trc -> PPrintEngine.document
Isla.Base
This module wraps all isla-lang
functionality. No other module should directly touch the Isla_lang
module.
The isla trace syntax is compose of events that a regrouped into traces. There are a lot of possible events, and some of them are unsupported. in particular all the ones that deal with concurrency.
All the variable in isla traces are numbered. They are named v28
in the syntax and are represented as a plain int
in Ocaml. The structure of the traces is a mix of SMT declaration about those variable and actual processor events. SMT declaration use SMT expressions of type exp
which entirely distinct from Ast.exp
(Use Isla.Conv
to convert). On the contrary processor event do not contain direct SMT expressions but Sail values of type valu
. A valu
can be either a single symbolic variable, a concrete bitvector/boolean/enumeration value or a more complex sail structure with fields that contain other valu
s and some other things.
Event of reading and writing complex register like PSTATE
in AArch64
will provide the whole structure as a valu to be read or written. However, Those events may contain a accessor list that implies that only specific field of the struct are written or read. This useful, because of the flat representation of registers in State.Reg
: each field is considered to be a separate register.
The isla types are polymorphic over the annotation because that comes from isla-lang
, but in practice the only annotation I use the source position of type lrng
, that's why there is a set of aliases starting with r
.
include module type of struct include Isla_lang.AST end
type lrng = Isla_lang__Isla_lang_ast.lrng =
| UnknownRng
| Generated of lrng
| Range of Stdlib.Lexing.position * Stdlib.Lexing.position
exception Parse_error_locn of lrng * string
val pp_lrng : lrng -> PPrint.document
type base_val = Isla_lang__Isla_lang_ast.base_val =
| Val_Symbolic of int
| Val_Bool of bool
| Val_Bits of string
| Val_Enum of enum
type assume_val = Isla_lang__Isla_lang_ast.assume_val =
| AVal_Var of string * accessor_list
| AVal_Bool of bool
| AVal_Bits of string
| AVal_Enum of enum
type tag_value = valu option
type !'a event = 'a Isla_lang__Isla_lang_ast.event =
| Smt of 'a smt * 'a
| Branch of int * string * 'a
| ReadReg of string * accessor_list * valu * 'a
| WriteReg of string * accessor_list * valu * 'a
| ReadMem of valu * valu * valu * int * tag_value * 'a
| WriteMem of valu * valu * valu * valu * int * tag_value * 'a
| BranchAddress of valu * 'a
| Barrier of valu * 'a
| CacheOp of valu * valu * 'a
| MarkReg of string * string * 'a
| Cycle of 'a
| Instr of valu * 'a
| Sleeping of int * 'a
| WakeRequest of 'a
| SleepRequest of 'a
| AssumeReg of string * accessor_list * valu * 'a
| Assume of 'a a_exp * 'a
| FunAssume of string * valu * arg_list * 'a
| UseFunAssume of string * valu * arg_list * 'a
| AbstractCall of string * valu * arg_list * 'a
| AbstractPrimop of string * valu * arg_list * 'a
type instruction_segments = Isla_lang__Isla_lang_ast.instruction_segments =
| Segments of segment list
type !'a maybe_fork = 'a Isla_lang__Isla_lang_ast.maybe_fork =
| Cases of string * 'a tree_trc list
| End
and !'a tree_trc = 'a Isla_lang__Isla_lang_ast.tree_trc =
| TreeTrace of 'a event list * 'a maybe_fork
type !'a trcs = 'a Isla_lang__Isla_lang_ast.trcs =
| Traces of 'a trc list
| TracesWithSegments of instruction_segments * 'a trc list
type !'a whole_tree = 'a Isla_lang__Isla_lang_ast.whole_tree =
| BareTree of 'a tree_trc
| TreeWithSegments of instruction_segments * 'a tree_trc
val subst_val_maybe_fork : base_val -> int -> 'a maybe_fork -> 'a maybe_fork
val subst_val_whole_tree : base_val -> int -> 'a whole_tree -> 'a whole_tree
exception ParseError of loc * string
Exception that represent an Isla parsing error
exception LexError of loc * string
Exception that represent an Isla lexing error
type 'a parser = lexer -> Stdlib.Lexing.lexbuf -> 'a
val parse : 'a parser -> ?filename:string -> Stdlib.Lexing.lexbuf -> 'a
Parse a single Isla instruction output from a Lexing.lexbuf
val parse_exp : ?filename:string -> Stdlib.Lexing.lexbuf -> rexp
Parse a single Isla expression from a Lexing.lexbuf
val parse_exp_string : ?filename:string -> string -> rexp
Parse a single Isla expression from a string
val parse_exp_channel : ?filename:string -> Stdlib.in_channel -> rexp
Parse a single Isla expression from a channel
val parse_trc : ?filename:string -> Stdlib.Lexing.lexbuf -> rtrc
Parse an Isla trace from a Lexing.lexbuf
val parse_trc_string : ?filename:string -> string -> rtrc
Parse an Isla trace from a string
val parse_trc_channel : ?filename:string -> Stdlib.in_channel -> rtrc
Parse an Isla trace from a channel
val parse_trcs_string : ?filename:string -> string -> rtrcs
val parse_trcs_channel : ?filename:string -> Stdlib.in_channel -> rtrcs
val parse_segments :
+ ?filename:string ->
+ Stdlib.Lexing.lexbuf ->
+ instruction_segments
val parse_segments_string : ?filename:string -> string -> instruction_segments
val parse_segments_channel :
+ ?filename:string ->
+ Stdlib.in_channel ->
+ instruction_segments
include module type of struct include Isla_lang.PP end
Cache.Epoch
val to_file : string -> (string * int * string) -> unit
val of_file : string -> string * int * string
val of_config : Server.Config.t -> string * int * string
Build the epoch from the config. This function does the config Digest
Cache.Epoch
val of_config : Config.File.ArchConf.Isla.t -> string * int * string
Build the epoch from the config. This function does the config Digest
Cache.IC
The isla cache module
type key
= Opcode.t
type value
= TraceList.t
type epoch
= Epoch.t
type t
= Utils__Cache.Make(Opcode)(TraceList)(Epoch).t
Cache.IC
The isla cache module
Cache.Opcode
Implementation of Cache
.Key for opcodes.
It is a special encoding of BytesSeq. If it is short enough to fit in the hash, then we do it. Otherwise we store in a file.
The exact encoding is here (back mean the last/top bit of the integer, i.e. IntBits
.back):
Short encoding: bit 0 to back -3 : The data bit back -3 to back: The size of the data bit back : cleared
Long encoding: bit 0 to back -1 : The start of the data bit back -1: set
type t
= Utils.BytesSeq.t option
val equal : Utils.BytesSeq.t option -> Utils.BytesSeq.t option -> bool
val hash : Utils.BytesSeq.t option -> Utils.IntBits.t
val to_file : string -> Utils.BytesSeq.t option -> unit
val of_file : Utils.IntBits.t -> string -> Utils.BytesSeq.t option
Cache.Opcode
Implementation of Cache.Key
for opcodes.
It is a special encoding of BytesSeq. If it is short enough to fit in the hash, then we do it. Otherwise we store in a file.
The exact encoding is here (back mean the last/top bit of the integer, i.e. IntBits.back
):
Short encoding: bit 0 to back -3 : The data bit back -3 to back: The size of the data bit back : cleared
Long encoding: bit 0 to back -1 : The start of the data bit back -1: set
type t = Server.opcode option
val reloc_id : Relocation.t option -> int
val reloc_of_id : int -> Relocation.t option
val equal :
+ (Utils.BytesSeq.t * 'a) option ->
+ (Utils.BytesSeq.t * 'a) option ->
+ bool
val small_enough : Utils.BytesSeq.t -> int -> bool
val hash : (Utils.BytesSeq.t * Relocation.t option) option -> Utils.IntBits.t
val to_file : 'a -> (Utils.BytesSeq.t * Relocation.t option) option -> unit
val of_file :
+ Utils.IntBits.t ->
+ 'a ->
+ (Utils.BytesSeq.t * Relocation.t option) option
Cache.TraceList
Representation of trace lists on disk.
It is just a list of traces separated by new lines
type t
= Base.rtrc list
Cache.TraceList
Representation of trace lists on disk.
It is just a list of traces separated by new lines
Isla.Cache
This module provide a caching system for isla trace on top of Server
.
It uses the cache named "isla" of Cache
Call start
to start and stop
. Do not interact directly with the Server
if you use the cache.
You can use ensure_started
to force the Server
to start but you probably shouldn't do that. By the default the Server
is only started if the traces of an instruction are required and not in the cache.
type config
= Server.config
The type of Isla configuration
module TraceList : sig ... end
Representation of trace lists on disk.
val epoch : int
An epoch independant of the isla version, bump if you change the representation of the traces on disk.
Reset (or not) when bumping isla version (Server.required_version
)
The Epoch also include the digest of the Isla configuration. Any change of configuration will wipe out the cache.
module Epoch : sig ... end
module IC : sig ... end
The isla cache module
val configr : config option Stdlib.ref
If this is set and cache is also set, then the server should be started with the architecture inside this variable when required
val start : config -> unit
Start the caching system. Do not yet start the server
val get_traces : Utils.BytesSeq.t -> Base.rtrc list
Get the traces of the opcode given. Use Server
if the value is not in the cache
Isla.Cache
This module provide a caching system for isla trace on top of Server
.
It uses the cache named "isla" of Cache
Call start
to start and stop
. Do not interact directly with the Server
if you use the cache.
You can use ensure_started
to force the Server
to start but you probably shouldn't do that. By the default the Server
is only started if the traces of an instruction are required and not in the cache.
type config = Server.config
The type of Isla configuration
module Opcode : sig ... end
Implementation of Cache.Key
for opcodes.
module TraceList : sig ... end
Representation of trace lists on disk.
An epoch independant of the isla version, bump if you change the representation of the traces on disk.
Reset (or not) when bumping isla version (Server.required_version
)
The Epoch also include the digest of the Isla configuration. Any change of configuration will wipe out the cache.
module Epoch : sig ... end
module IC : sig ... end
The isla cache module
val configr : config option Stdlib.ref
If this is set and cache is also set, then the server should be started with the architecture inside this variable when required
val start : config -> unit
Start the caching system. Do not yet start the server
val get_traces : Server.opcode -> Base.rtrcs
Get the traces of the opcode given. Use Server
if the value is not in the cache
Isla.Conv
val ty : Isla__.Base.ty -> 'a Ast.ty
val unop : Isla__.Base.unop -> Ast.unop
val bvarith : Isla__.Base.bvarith -> Ast.bvarith
val bvcomp : Isla__.Base.bvcomp -> Ast.bvcomp
val bvmanyarith : Isla__.Base.bvmanyarith -> Ast.bvmanyarith
val binop : Isla__.Base.binop -> 'm Ast.binop
val manyop : Isla__.Base.manyop -> Ast.manyop
val direct_exp_no_var : ('a Isla__.Base.exp -> ('a, 'v, 'b, 'm) Ast.exp) -> 'a Isla__.Base.exp -> ('a, 'v, 'b, 'm) Ast.exp
val exp_var_conv : (int -> 'v) -> 'a Isla__.Base.exp -> ('a, 'v, 'b, 'm) Ast.exp
val exp : 'a Isla__.Base.exp -> ('a, int, 'b, 'c) Ast.exp
val exp_var_subst : (int -> 'a -> ('a, 'v, 'b, 'm) Ast.exp) -> 'a Isla__.Base.exp -> ('a, 'v, 'b, 'm) Ast.exp
Convert an expression from isla to Ast but using a var-to-exp conversion function
val exp_add_type_var_subst : (int -> 'a -> ('v, 'm) Exp.Typed.t) -> 'a Isla__.Base.exp -> ('v, 'm) Exp.Typed.t
Convert directly from an untyped isla expression to an Exp.Typed
by substituing isla variables with already typed expressions
val smt_var_conv : (int -> 'v) -> 'a Isla__.Base.smt -> ('a, 'v, 'b, 'm) Ast.smt
val smt : 'a Isla__.Base.smt -> ('a, int, 'b, 'c) Ast.smt
Isla.Conv
val bvarith : Base.bvarith -> Ast.bvarith
val bvcomp : Base.bvcomp -> Ast.bvcomp
val bvmanyarith : Base.bvmanyarith -> Ast.bvmanyarith
val binop : Base.binop -> 'm Ast.binop
val manyop : Base.manyop -> Ast.manyop
val exp_var_subst :
+ (int -> 'a -> ('a, 'v, 'b, 'm) Ast.exp) ->
+ 'a Base.exp ->
+ ('a, 'v, 'b, 'm) Ast.exp
Convert an expression from isla to Ast but using a var-to-exp conversion function
val exp_add_type_var_subst :
+ (int -> 'a -> ('v, 'm) Exp.Typed.t) ->
+ 'a Base.exp ->
+ ('v, 'm) Exp.Typed.t
Convert directly from an untyped isla expression to an Exp.Typed
by substituing isla variables with already typed expressions
Isla.Manip
This module provide generic manipulation function of isla ast
val is_linear : 'a Isla__.Base.trc -> bool
Check if a trace is linear (has no branches)
val annot_exp : 'a Isla__.Base.exp -> 'a
Get the annotation of an expression
val annot_event : 'a Isla__.Base.event -> 'a
Get the annotation of an event
This section is filled on demand.
direct_a_map_b
take a function b -> b
and applies it to all b
in a
, non-recursively. Then a new a with the same structure is formed.
direct_a_iter_b
take a function b -> unit
and applies it to all b
in a
, non-recursively.
val direct_exp_map_exp : ('a Isla__.Base.exp -> 'a Isla__.Base.exp) -> 'a Isla__.Base.exp -> 'a Isla__.Base.exp
val direct_exp_iter_exp : ('a Isla__.Base.exp -> unit) -> 'a Isla__.Base.exp -> unit
val direct_smt_map_exp : ('a Isla__.Base.exp -> 'a Isla__.Base.exp) -> 'a Isla__.Base.smt -> 'a Isla__.Base.smt
val direct_event_map_exp : ('a Isla__.Base.exp -> 'a Isla__.Base.exp) -> 'a Isla__.Base.event -> 'a Isla__.Base.event
val direct_event_iter_valu : (Isla__.Base.valu -> unit) -> 'a Isla__.Base.event -> unit
val direct_event_map_valu : (Isla__.Base.valu -> Isla__.Base.valu) -> 'a Isla__.Base.event -> 'a Isla__.Base.event
val direct_valu_iter_valu : (Isla__.Base.valu -> unit) -> Isla__.Base.valu -> unit
val direct_valu_map_valu : (Isla__.Base.valu -> Isla__.Base.valu) -> Isla__.Base.valu -> Isla__.Base.valu
This section is filled on demand.
a_map_b
take a function b -> b
and applies it to all the b
in a
, and do that recursively on all b that appear directly or indirectly in a
a_iter_b
take a function b -> unit
and applies it to all the b
in a
, and do that recursively
Doing this when a = b is not well defined, and can be easily done using the direct version from previous section.
In case where a type is not recusive like event
, both direct and recursive versions are the same.
val exp_iter_var : (int -> unit) -> 'a Isla__.Base.exp -> unit
iterate a function on all the variable of an expression
val event_iter_valu : (Isla__.Base.valu -> unit) -> 'a Isla__.Base.event -> unit
val var_subst : (int -> 'a -> 'a Isla__.Base.exp) -> 'a Isla__.Base.exp -> 'a Isla__.Base.exp
Substitute variable with expression according to subst function
val accessor_of_string : string -> Isla__.Base.accessor
val string_of_accessor : Isla__.Base.accessor -> string
val accessor_of_string_list : string list -> Isla__.Base.accessor_list
val string_of_accessor_list : Isla__.Base.accessor_list -> string list
val valu_get : Isla__.Base.valu -> string list -> Isla__.Base.valu
Follow the path in a value like A.B.C in (struct (|B| (struct (|C| ...))))
This is some basic trace filtering that remove unwanted item from the trace in various situations
val split_cycle : 'a Isla__.Base.trc -> 'a Isla__.Base.trc * 'a Isla__.Base.trc
Split the trace between before and after the "cycle" event
val remove_init : 'a Isla__.Base.trc -> 'a Isla__.Base.trc
Remove all events before the "cycle" event, keep the SMT statements
val remove_ignored : Utils.String.t Utils.List.t -> 'a Isla__.Base.trc -> 'a Isla__.Base.trc
Remove all the events related to ignored registers
Isla.Manip
This module provide generic manipulation function of isla ast
val is_linear : 'a Base.trc -> bool
Check if a trace is linear (has no branches)
val annot_exp : 'a Base.exp -> 'a
Get the annotation of an expression
val annot_event : 'a Base.event -> 'a
Get the annotation of an event
This section is filled on demand.
direct_a_map_b
take a function b -> b
and applies it to all b
in a
, non-recursively. Then a new a with the same structure is formed.
direct_a_iter_b
take a function b -> unit
and applies it to all b
in a
, non-recursively.
val direct_event_map_exp :
+ ('a Base.exp -> 'a Base.exp) ->
+ 'a Base.event ->
+ 'a Base.event
val direct_event_iter_valu : (Base.valu -> unit) -> 'a Base.event -> unit
val direct_event_map_valu :
+ (Base.valu -> Base.valu) ->
+ 'a Base.event ->
+ 'a Base.event
This section is filled on demand.
a_map_b
take a function b -> b
and applies it to all the b
in a
, and do that recursively on all b that appear directly or indirectly in a
a_iter_b
take a function b -> unit
and applies it to all the b
in a
, and do that recursively
Doing this when a = b is not well defined, and can be easily done using the direct version from previous section.
In case where a type is not recusive like event
, both direct and recursive versions are the same.
val exp_iter_var : (int -> unit) -> 'a Base.exp -> unit
iterate a function on all the variable of an expression
val event_iter_valu : (Base.valu -> unit) -> 'a Base.event -> unit
Iterate a function over a valu
broken up into fields, extending the given path.
Map a function over a valu
broken up into fields, extending the given path.
Substitute variable with expression according to subst function
val accessor_of_string : string -> Base.accessor
val string_of_accessor : Base.accessor -> string
val accessor_of_string_list : string list -> Base.accessor_list
val string_of_accessor_list : Base.accessor_list -> string list
Follow the path in a value like A.B.C in (struct (|B| (struct (|C| ...))))
This is some basic trace filtering that remove unwanted item from the trace in various situations
Split the trace between before and after the "cycle" event
Remove all events before the "cycle" event, keep the SMT statements
val remove_ignored : Utils.String.t Utils.List.t -> 'a Base.trc -> 'a Base.trc
Remove all the events related to ignored registers
Isla.Preprocess
This module is about preprocessing isla traces. This includes:
This is important because isla print everything the sail code does, but, as the sail code is not written for performance, it will often do a lot of computation and then later decide it wasn't useful and discard the result. This quickly make isla traces bloated.
Once preprocessed, there is only a simple list of traces.
For example at time of writing, a basic load like ldr x0, [x1]
has about 1300 variable before preprocessing and 27 after.
TODO: Remove useless register reads (Need a model where reading register has no side effect).
val preprocess : Server.config -> Server.trcs -> Base.rtrc list
Preprocess a group of traces, by removing useless registers (according to the config), removing initialisation code and simplifying with simplify_trc
Isla.Preprocess
This module is about preprocessing isla traces. This includes:
This is important because isla print everything the sail code does, but, as the sail code is not written for performance, it will often do a lot of computation and then later decide it wasn't useful and discard the result. This quickly make isla traces bloated.
Once preprocessed, there is only a simple list of traces.
For example at time of writing, a basic load like ldr x0, [x1]
has about 1300 variable before preprocessing and 27 after.
TODO: Remove useless register reads (Need a model where reading register has no side effect).
Simplify a simple trace by removing all useless variables
val preprocess : Server.config -> Server.trcs -> Base.rtrcs
Preprocess a group of traces, by removing useless registers (according to the config), removing initialisation code and simplifying with simplify_trc
Isla.Run
This module provide facility to run Isla
trace over states
The main functions are trc
for pure interface and trc_mut
for imperative interface.
RunError
will be thrown when something goes wrong.
It is for testing purpose only, otherwise use Trace.Run
. Typing does not work, and some other expected behavior may not work either.
This module can be considered deprecated/legacy.
exception
RunError of Isla__.Base.lrng * string
Exception that represent an Isla runtime error which should not happen
val run_error : Isla__.Base.lrng -> ('a, unit, string, 'b) Stdlib.format4 -> 'a
Throw a run error with the string part as formated by the format string
type value_context
= State.exp Utils.HashVector.t
The contex of value that associate isla variable numbers to state expression
val get_var : Isla__.Base.lrng -> State.Exp.t Utils.HashVector.t -> int -> State.Exp.t
Get the expression associated to the free variable. Throw RunError
, if the variable is no bound. The lrng
is for error reporting.
val exp_conv_subst : value_context -> Base.rexp -> State.exp
Convert an Isla
expression to an Ast
by substituing all free variable with the bound expression in the value_context
.
If a variable is not bound, throw RunError
val exp_of_valu : Isla__.Base.lrng -> State.Exp.t Utils.HashVector.t -> Isla__.Base.valu -> State.exp
Give the State.exp
that represents the input valu
.
A symbolic variable i is represented by the expression bound to it in the provided value_context
.
Newly created expression are located with the provided lrng
.
If the value is not convertible to a state expression, throw a RunError
val write_to_var : 'a -> 'b Utils.HashVector.t -> int -> 'b -> unit
This function write an expression to symbolic variable. The write is ignored if the variable was already set because isla guarantee that it would be the same value (Trusting Isla here)
val write_to_valu : 'a -> 'b Utils.HashVector.t -> Isla__.Base.valu -> 'b -> unit
This function write an expression to an valu
.
If the valu is a variable, it is added to the context, otherwise nothing happens.
val event_mut : value_context -> State.t -> Base.revent -> unit
Run an event on State.t
and a value_context
by mutating both
val trc_mut : ?vc:State.exp Utils.HashVector.t -> State.t -> Base.rtrc -> unit
This function run an isla trace on a state by mutation. If a vc
is provided, then it is used and mutated according to the trace.
Any encountered branch are ignored and their assertion are added to the state
Isla.Run
This module provide facility to run Isla
trace over states
The main functions are trc
for pure interface and trc_mut
for imperative interface.
RunError
will be thrown when something goes wrong.
It is for testing purpose only, otherwise use Trace.Run
. Typing does not work, and some other expected behavior may not work either.
This module can be considered deprecated/legacy.
exception RunError of Base.lrng * string
Exception that represent an Isla runtime error which should not happen
val run_error : Base.lrng -> ('a, unit, string, 'b) Stdlib.format4 -> 'a
Throw a run error with the string part as formated by the format string
type value_context = State.exp Utils.HashVector.t
The contex of value that associate isla variable numbers to state expression
val get_var : Base.lrng -> State.Exp.t Utils.HashVector.t -> int -> State.Exp.t
val exp_conv_subst : value_context -> Base.rexp -> State.exp
Convert an Isla
expression to an Ast
by substituing all free variable with the bound expression in the value_context
.
If a variable is not bound, throw RunError
val exp_of_valu :
+ Base.lrng ->
+ State.Exp.t Utils.HashVector.t ->
+ Base.valu ->
+ State.exp
Give the State.exp
that represents the input valu
.
A symbolic variable i is represented by the expression bound to it in the provided value_context
.
Newly created expression are located with the provided lrng
.
If the value is not convertible to a state expression, throw a RunError
val write_to_var : 'a -> 'b Utils.HashVector.t -> int -> 'b -> unit
This function write an expression to symbolic variable. The write is ignored if the variable was already set because isla guarantee that it would be the same value (Trusting Isla here)
val write_to_valu : 'a -> 'b Utils.HashVector.t -> Base.valu -> 'b -> unit
This function write an expression to an valu
.
If the valu is a variable, it is added to the context, otherwise nothing happens.
val event_mut : value_context -> State.t -> Base.revent -> unit
Run an event on State.t
and a value_context
by mutating both
val trc_mut : ?vc:State.exp Utils.HashVector.t -> State.t -> Base.rtrc -> unit
This function run an isla trace on a state by mutation. If a vc
is provided, then it is used and mutated according to the trace.
Any encountered branch are ignored and their assertion are added to the state
Server.Cmd
This module provide a CLI subcommand to test isla directly. All isla output is reported as raw text
val server_test : Config.Arch.t -> unit
val term : unit Cmdliner.Term.t
val info : Cmdliner.Term.info
val command : unit Cmdliner.Term.t * Cmdliner.Term.info
Server.Cmd
This module provide a CLI subcommand to test isla directly. All isla output is reported as raw text
val server_test : Config.Arch.t -> unit
Isla.Server
This module is about launching isla as a background server and using it
Important remark: even if I call isla a server because in a logical sense read-dwarf is making request to isla and isla is serving them, in Unix sense read-dwarf is the server because it listen to the socket and isla connect to it.
module ConfigFile = Config.File
module CommonOpt = Config.CommonOpt
module Config = Config.File.ArchConf.Isla
module Server = Utils.Cmd.SocketServer
type config
= Config.t
The configuration record type
type trcs
= (bool * Base.rtrc) list
The raw output of the server for an instruction.
It is a list of traces, each with a flag telling if they are normal traces (no processor exception/fault) or not
val required_version : string
Bump when updating isla. TODO: move the version checking to allow a range of version. Also, right now the cache invalidation is based on this and not on the actual isla version, which may be dangerous.
val req_num : int Stdlib.ref
val server : Server.t option Stdlib.ref
This instance of socket server for isla
val get_server : unit -> Server.t
Assume the server is started and get it out of the reference
val cmd_of_config : config -> string -> string array
Compute the isla-client command line from the isla configuration
val raw_start : config -> unit
Start the server with the specified architecture, do not attempt any checks
type basic_answer
=
| Error |
| Version of string |
| StartTraces |
| Trace of bool * string |
| EndTraces |
This should match exactly with the Answer type in isla-client code
val read_basic_answer : unit -> basic_answer
Read an answer from isla-client. This must match exactly write_answer
in client.rs
in isla
type answer
=
| Version of string |
| Traces of (bool * string) list |
The interpreted answer. If the protocol is followed, then one request lead to exactly one answer of that type
val expect_version : answer -> string
Expect a version answer and fails if it is not the case
val expect_traces : answer -> (bool * string) list
Expect isla traces and fails if it is not the case
val expect_parsed_traces : answer -> trcs
Expect isla traces and fails if it is not the case, additionally parse them
exception
IslaError
When isla encounter a non fatal error with that specific request. This error is recoverable and the sever can accept other requests
val read_answer : unit -> answer
Read the answer from isla, block until full answer
val pp_answer : answer -> PPrintEngine.document
Answer pretty printer
type request
=
| TEXT_ASM of string |
| ASM of Utils.BytesSeq.t |
| VERSION |
| STOP |
The type of a request to isla
val string_of_request : request -> string
Convert a request into the string message expected by isla-client This should match the protocol
val send_string_request : string -> unit
Send a string request to the server, and do not wait for any answer
val request_bin_parsed : Utils.BytesSeq.t -> trcs
Request the traces of a binary instruction and parse the result.
This is the main entry point of this module.
val send_request : request -> unit
Send a request without expecting any answer
val start : config -> unit
Start isla and check version
module Cmd : sig ... end
This module provide a CLI subcommand to test isla directly. All isla output is reported as raw text
Isla.Server
This module is about launching isla as a background server and using it
Important remark: even if I call isla a server because in a logical sense read-dwarf is making request to isla and isla is serving them, in Unix sense read-dwarf is the server because it listen to the socket and isla connect to it.
module ConfigFile = Config.File
module CommonOpt = Config.CommonOpt
module Config = Config.File.ArchConf.Isla
module Server = Utils.Cmd.SocketServer
type config = Config.t
The configuration record type
type trcs = Base.instruction_segments option * (bool * Base.rtrc) list
The raw output of the server for an instruction.
It is a list of traces, each with a flag telling if they are normal traces (no processor exception/fault) or not
type opcode = Utils.BytesSeq.t * Relocation.t option
Bump when updating isla. TODO: move the version checking to allow a range of version. Also, right now the cache invalidation is based on this and not on the actual isla version, which may be dangerous.
val server : Server.t option Stdlib.ref
This instance of socket server for isla
val get_server : unit -> Server.t
Assume the server is started and get it out of the reference
val cmd_of_config : config -> string -> string array
Compute the isla-client command line from the isla configuration
val raw_start : config -> unit
Start the server with the specified architecture, do not attempt any checks
val read_basic_answer : unit -> basic_answer
Read an answer from isla-client. This must match exactly write_answer
in client.rs
in isla
The interpreted answer. If the protocol is followed, then one request lead to exactly one answer of that type
val expect_version : answer -> string
Expect a version answer and fails if it is not the case
val expect_traces : answer -> string option * (bool * string) list
Expect isla traces and fails if it is not the case
Expect isla traces and fails if it is not the case, additionally parse them
When isla encounter a non fatal error with that specific request. This error is recoverable and the sever can accept other requests
val read_answer : unit -> answer
Read the answer from isla, block until full answer
val pp_answer : answer -> Utils.Pp.document
Answer pretty printer
The type of a request to isla
val string_of_request : request -> string
Convert a request into the string message expected by isla-client This should match the protocol
Send a string request to the server, and do not wait for any answer
Request the traces of a binary instruction and parse the result.
This is the main entry point of this module.
val send_request : request -> unit
Send a request without expecting any answer
val start : config -> unit
Start isla and check version
Test that isla can start and keep a valid version
module Cmd : sig ... end
This module provide a CLI subcommand to test isla directly. All isla output is reported as raw text
Isla.Test
This file about testing interaction with Isla for single instructions
module SMT = Z3
type pmode
=
The type of processing requested
type isla_mode
=
The input syntax
type imode
=
| CMD | Read the input as the main command line argument |
| FILE | Read the input in a file |
| ELF of string | Read the input from an elf symbol + offset. implies BIN for isla_mode |
The way input is taken
val arg : string Cmdliner.Term.t
val direct : bool Cmdliner.Term.t
val bin : bool Cmdliner.Term.t
val hex : bool Cmdliner.Term.t
val noparse : bool Cmdliner.Term.t
val preprocess : bool Cmdliner.Term.t
val typer : bool Cmdliner.Term.t
val run : bool Cmdliner.Term.t
val simp : bool Cmdliner.Term.t
val file : bool Cmdliner.Term.t
val sym : string option Cmdliner.Term.t
val input_f2m : bool -> string option -> imode Cmdliner.Term.ret
Input flag to mode conversion
val imode_term : imode Cmdliner.Term.t
val input : imode -> string -> (string * string) Cmdliner.Term.ret
Input takes the imode and the main argument and returns the filename and input string
val input_term : (string * string) Cmdliner.Term.t
val isla_f2m : bool -> bool -> bool -> 'a option -> isla_mode Cmdliner.Term.ret
Convert various flag describe the mode of operation into the mode of operation If sym is activated, then the default mode is BIN and not ASM
val isla_mode_term : isla_mode Cmdliner.Term.t
val isla_mode_to_request : isla_mode -> string -> Server.request
val isla_run : isla_mode -> Config.Arch.t -> (string * string) -> string * string * Server.config
Run isla and return a text trace with a filename (if mode is RAW than just return the trace and filename without isla)
If isla return multiple traces, just silently pick the first non-exceptional one
val isla_term : (string * string * Server.config) Cmdliner.Term.t
val processing_f2m : bool -> bool -> bool -> bool -> pmode
How far into the processing pipeline we go. We just pick the deepest option chosen
val pmode_term : pmode Cmdliner.Term.t
val processing : bool -> pmode -> (string * string * Server.config) -> unit
Does the actual processing of the trace
Isla.Test
This file about testing interaction with Isla for single instructions
module SMT = Z3
type pmode =
| DUMP
Dump isla output on standard output
*)| PARSE
parse isla output and prettyprint it
*)| TYPE
Type isla output and dump var types. Also dump the deduced register file
*)| RUN
Run the the isla output on a test state and print all branches and states
*)| SIMP
Run the the isla output on a test state and also print a simplified version
*)The type of processing requested
val input_f2m : bool -> string option -> imode Cmdliner.Term.ret
Input flag to mode conversion
val imode_term : imode Cmdliner.Term.t
val input : imode -> string -> (string * string) Cmdliner.Term.ret
Input takes the imode and the main argument and returns the filename and input string
val isla_f2m : bool -> bool -> bool -> 'a option -> isla_mode Cmdliner.Term.ret
Convert various flag describe the mode of operation into the mode of operation If sym is activated, then the default mode is BIN and not ASM
val isla_mode_term : isla_mode Cmdliner.Term.t
val isla_mode_to_request : isla_mode -> string -> Server.request
val isla_run :
+ isla_mode ->
+ Config.Arch.t ->
+ (string * string) ->
+ string * string * Server.config
Run isla and return a text trace with a filename (if mode is RAW than just return the trace and filename without isla)
If isla return multiple traces, just silently pick the first non-exceptional one
val isla_term : (string * string * Server.config) Cmdliner.Term.t
val processing_f2m : bool -> bool -> bool -> bool -> pmode
How far into the processing pipeline we go. We just pick the deepest option chosen
val pmode_term : pmode Cmdliner.Term.t
val processing : bool -> pmode -> (string * string * Server.config) -> unit
Does the actual processing of the trace
Isla.Type
This module is about type isla trace and register discovery.
The actual goal is dicovering the existence and type of registers from Isla traces, but this require type the trace with ty
and thus full type checking of traces. We expect isla to be correct, so a type error would be very surprising.
type type_context
= Isla__.Base.ty Utils.HashVector.t
A context that associate Isla types to Isla variables
type lty
= Isla__.Base.lrng * Isla__.Base.ty
A group of the source range and type for an expression
exception
TypeError of Isla__.Base.lrng * string
Exception that represent an Isla typing error
val tassert : Isla__.Base.lrng -> string -> bool -> unit
Assert some properties for type correctness. Requires a lrng and a string error message
val expect_bool : string -> (Isla__.Base.lrng * Isla__.Base.ty) -> unit
val expect_bv : string -> (Isla__.Base.lrng * Isla__.Base.ty) -> int
val expect_enum : string -> (Isla__.Base.lrng * Isla__.Base.ty) -> int
val type_unop : Isla__.Base.lrng -> Isla__.Base.unop -> lty -> Isla__.Base.ty
val type_binop : Isla__.Base.lrng -> Isla__.Base.binop -> (Isla__.Base.lrng * Isla__.Base.ty) -> (Isla__.Base.lrng * Isla__.Base.ty) -> Isla__.Base.ty
val type_manyop : Isla__.Base.lrng -> Isla__.Base.manyop -> lty list -> Isla__.Base.ty
val type_valu : Isla__.Base.lrng -> type_context -> Isla__.Base.valu -> (State.Reg.Path.t * State.Reg.ty) list
Take an Isla value and a context and give the list of field that correspond to that value. Those fields would need to be prefixed with the top register name before being added in State.Reg
val ltype_expr : type_context -> Isla__.Base.lrng Isla__.Base.exp -> lty
val type_expr : type_context -> Isla__.Base.lrng Isla__.Base.exp -> Isla__.Base.ty
val type_trc : ?tc:Isla__.Base.ty Utils.HashVector.t -> Base.rtrc -> Isla__.Base.ty Utils.HashVector.t
Add the new register found in the trace and returns the type context for free variables
val pp_tcontext : Isla_lang__.Isla_lang_ast.ty Utils.HashVector.t -> Utils.Pp.document
Print a type context for debugging
Isla.Type
This module is about type isla trace and register discovery.
The actual goal is dicovering the existence and type of registers from Isla traces, but this require type the trace with ty
and thus full type checking of traces. We expect isla to be correct, so a type error would be very surprising.
type type_context = Base.ty Utils.HashVector.t
A context that associate Isla types to Isla variables
exception TypeError of Base.lrng * string
Exception that represent an Isla typing error
val tassert : Base.lrng -> string -> bool -> unit
Assert some properties for type correctness. Requires a lrng and a string error message
val type_manyop : Base.lrng -> Base.manyop -> lty list -> Base.ty
val type_valu :
+ Base.lrng ->
+ type_context ->
+ Base.valu ->
+ (State.Reg.Path.t * State.Reg.ty) list
Take an Isla value and a context and give the list of field that correspond to that value. Those fields would need to be prefixed with the top register name before being added in State.Reg
val ltype_expr : type_context -> Base.lrng Base.exp -> lty
val type_expr : type_context -> Base.lrng Base.exp -> Base.ty
val type_trc :
+ ?tc:Base.ty Utils.HashVector.t ->
+ Base.rtrc ->
+ Base.ty Utils.HashVector.t
Add the new register found in the trace and returns the type context for free variables
val pp_tcontext :
+ Isla_lang__.Isla_lang_ast.ty Utils.HashVector.t ->
+ Utils.Pp.document
Print a type context for debugging
Isla
include Base
include Isla_lang.AST
type enum
= int * int
type lrng
= Isla_lang__Isla_lang_ast.lrng
=
| UnknownRng |
| Generated of lrng |
| Range of Stdlib.Lexing.position * Stdlib.Lexing.position |
exception
Parse_error_locn of lrng * string
val pp_lpos : Stdlib.Lexing.position -> PPrint.document
val pp_lrng : lrng -> PPrint.document
type bvmanyarith
= Isla_lang__Isla_lang_ast.bvmanyarith
=
| Bvand |
| Bvor |
| Bvxor |
| Bvadd |
| Bvmul |
type bvcomp
= Isla_lang__Isla_lang_ast.bvcomp
=
| Bvult |
| Bvslt |
| Bvule |
| Bvsle |
| Bvuge |
| Bvsge |
| Bvugt |
| Bvsgt |
type bvarith
= Isla_lang__Isla_lang_ast.bvarith
=
| Bvnand |
| Bvnor |
| Bvxnor |
| Bvsub |
| Bvudiv |
| Bvudivi |
| Bvsdiv |
| Bvsdivi |
| Bvurem |
| Bvsrem |
| Bvsmod |
| Bvshl |
| Bvlshr |
| Bvashr |
type manyop
= Isla_lang__Isla_lang_ast.manyop
=
| And |
| Or |
| Bvmanyarith of bvmanyarith |
| Concat |
type unop
= Isla_lang__Isla_lang_ast.unop
=
| Not |
| Bvnot |
| Bvredand |
| Bvredor |
| Bvneg |
| Extract of int * int |
| ZeroExtend of int |
| SignExtend of int |
type binop
= Isla_lang__Isla_lang_ast.binop
=
| Eq |
| Bvarith of bvarith |
| Bvcomp of bvcomp |
type accessor
= Isla_lang__Isla_lang_ast.accessor
=
| Field of string |
type ty
= Isla_lang__Isla_lang_ast.ty
=
| Ty_Bool |
| Ty_BitVec of int |
| Ty_Enum of int |
| Ty_Array of ty * ty |
type 'a exp
= 'a Isla_lang__Isla_lang_ast.exp
=
| Var of int * 'a |
| Bits of string * 'a |
| Bool of bool * 'a |
| Enum of enum * 'a |
| Unop of unop * 'a exp * 'a |
| Binop of binop * 'a exp * 'a exp * 'a |
| Manyop of manyop * 'a exp list * 'a |
| Ite of 'a exp * 'a exp * 'a exp * 'a |
type valu
= Isla_lang__Isla_lang_ast.valu
=
| Val_Symbolic of int |
| Val_Bool of bool |
| Val_I of int * int |
| Val_Bits of string |
| Val_Enum of enum |
| Val_String of string |
| Val_Unit |
| Val_NamedUnit of string |
| Val_Vector of valu list |
| Val_List of valu list |
| Val_Struct of (string * valu) list |
| Val_Poison |
type accessor_list
= Isla_lang__Isla_lang_ast.accessor_list
=
| Nil |
| Cons of accessor list |
type 'a smt
= 'a Isla_lang__Isla_lang_ast.smt
=
| DeclareConst of int * ty |
| DefineConst of int * 'a exp |
| Assert of 'a exp |
| DefineEnum of int |
type valu_option
= valu option
type valu_concrete
= Isla_lang__Isla_lang_ast.valu_concrete
=
| CVal_Bool of bool |
| CVal_I of int * int |
| CVal_Bits of string |
| CVal_Enum of enum |
| CVal_String of string |
| CVal_Unit |
| CVal_NamedUnit of string |
| CVal_Vector of valu list |
| CVal_List of valu list |
| CVal_Struct of (string * valu) list |
| CVal_Poison |
type 'a event
= 'a Isla_lang__Isla_lang_ast.event
=
| Smt of 'a smt * 'a |
| Branch of int * string * 'a |
| ReadReg of string * accessor_list * valu * 'a |
| WriteReg of string * accessor_list * valu * 'a |
| ReadMem of valu * valu * valu * int * valu_option * 'a |
| WriteMem of int * valu * valu * valu * int * valu_option * 'a |
| BranchAddress of valu * 'a |
| Barrier of valu * 'a |
| CacheOp of valu * valu * 'a |
| MarkReg of string * string * 'a |
| Cycle of 'a |
| Instr of valu * 'a |
| Sleeping of int * 'a |
| WakeRequest of 'a |
| SleepRequest of 'a |
type 'a trc
= 'a Isla_lang__Isla_lang_ast.trc
=
| Trace of 'a event list |
type 'a exp_val
= 'a Isla_lang__Isla_lang_ast.exp_val
=
| EV_Bits of string * 'a |
| EV_Bool of bool * 'a |
| EV_Enum of enum * 'a |
| EV_Unop of unop * 'a exp_val * 'a |
| EV_Binop of binop * 'a exp_val * 'a exp_val * 'a |
| EV_Manyop of manyop * 'a exp_val list * 'a |
| EV_Ite of 'a exp_val * 'a exp_val * 'a exp_val * 'a |
exception
ParseError of loc * string
Exception that represent an Isla parsing error
exception
LexError of loc * string
Exception that represent an Isla lexing error
type lexer
= Stdlib.Lexing.lexbuf -> Parser.token
type 'a parser
= lexer -> Stdlib.Lexing.lexbuf -> 'a
val parse : 'a parser -> ?filename:string -> Stdlib.Lexing.lexbuf -> 'a
Parse a single Isla instruction output from a Lexing.lexbuf
val parse_exp : ?filename:string -> Stdlib.Lexing.lexbuf -> rexp
Parse a single Isla expression from a Lexing.lexbuf
val parse_exp_string : ?filename:string -> string -> rexp
Parse a single Isla expression from a string
val parse_exp_channel : ?filename:string -> Stdlib.in_channel -> rexp
Parse a single Isla expression from a channel
val parse_trc : ?filename:string -> Stdlib.Lexing.lexbuf -> rtrc
Parse an Isla trace from a Lexing.lexbuf
val parse_trc_string : ?filename:string -> string -> rtrc
Parse an Isla trace from a string
val parse_trc_channel : ?filename:string -> Stdlib.in_channel -> rtrc
Parse an Isla trace from a channel
include Isla_lang.PP
val pp_raw_vvar : int -> PPrintEngine.document
val pp_raw_name : string -> PPrintEngine.document
val pp_raw_enum_ty : int -> PPrintEngine.document
val pp_raw_enum : Isla_lang__.Isla_lang_ast.enum -> PPrintEngine.document
val pp_raw_int : int -> PPrintEngine.document
val pp_raw_bvi : int -> PPrintEngine.document
val pp_raw_bv : string -> PPrintEngine.document
val pp_raw_str : string -> PPrintEngine.document
val pp_raw_ty : Isla_lang__.Isla_lang_ast.ty -> PPrintEngine.document
val pp_raw_bool : bool -> PPrintEngine.document
val pp_raw_unop : Isla_lang__.Isla_lang_ast.unop -> PPrintEngine.document
val pp_raw_bvarith : Isla_lang__.Isla_lang_ast.bvarith -> PPrintEngine.document
val pp_raw_bvcomp : Isla_lang__.Isla_lang_ast.bvcomp -> PPrintEngine.document
val pp_raw_binop : Isla_lang__.Isla_lang_ast.binop -> PPrintEngine.document
val pp_raw_bvmanyarith : Isla_lang__.Isla_lang_ast.bvmanyarith -> PPrintEngine.document
val pp_raw_manyop : Isla_lang__.Isla_lang_ast.manyop -> PPrintEngine.document
val pp_raw_exp : 'a Isla_lang__.Isla_lang_ast.exp -> PPrintEngine.document
val pp_raw_exp_val : 'a Isla_lang__.Isla_lang_ast.exp_val -> PPrintEngine.document
val pp_raw_smt : 'a Isla_lang__.Isla_lang_ast.smt -> PPrintEngine.document
val pp_raw_valu : Isla_lang__.Isla_lang_ast.valu -> PPrintEngine.document
val pp_raw_selem : (string * Isla_lang__.Isla_lang_ast.valu) -> PPrintEngine.document
val pp_raw_valu_concrete : Isla_lang__.Isla_lang_ast.valu_concrete -> PPrintEngine.document
val pp_raw_selem_concrete : (string * Isla_lang__.Isla_lang_ast.valu_concrete) -> PPrintEngine.document
val pp_raw_accessor : Isla_lang__.Isla_lang_ast.accessor -> PPrintEngine.document
val pp_raw_accessor_list : Isla_lang__.Isla_lang_ast.accessor_list -> PPrintEngine.document
val pp_raw_valu_option : Isla_lang__.Isla_lang_ast.valu_option -> PPrintEngine.document
val pp_raw_event : 'a Isla_lang__.Isla_lang_ast.event -> PPrintEngine.document
val pp_raw_trc : 'a Isla_lang__.Isla_lang_ast.trc -> PPrintEngine.document
val pp_vvar : int -> PPrintEngine.document
val pp_name : string -> PPrintEngine.document
val pp_enum_ty : int -> PPrintEngine.document
val pp_enum : Isla_lang__.Isla_lang_ast.enum -> PPrintEngine.document
val pp_int : int -> PPrintEngine.document
val pp_bvi : int -> PPrintEngine.document
val pp_bv : string -> PPrintEngine.document
val pp_str : string -> PPrintEngine.document
val pp_j : int -> string
val pp_ty : Isla_lang__.Isla_lang_ast.ty -> PPrintEngine.document
val pp_bool : bool -> PPrintEngine.document
val pp_unop : Isla_lang__.Isla_lang_ast.unop -> PPrintEngine.document
val pp_bvarith : Isla_lang__.Isla_lang_ast.bvarith -> PPrintEngine.document
val pp_bvcomp : Isla_lang__.Isla_lang_ast.bvcomp -> PPrintEngine.document
val pp_binop : Isla_lang__.Isla_lang_ast.binop -> PPrintEngine.document
val pp_bvmanyarith : Isla_lang__.Isla_lang_ast.bvmanyarith -> PPrintEngine.document
val pp_manyop : Isla_lang__.Isla_lang_ast.manyop -> PPrintEngine.document
val pp_exp : 'a Isla_lang__.Isla_lang_ast.exp -> PPrintEngine.document
val pp_exp_val : 'a Isla_lang__.Isla_lang_ast.exp_val -> PPrintEngine.document
val pp_smt : 'a Isla_lang__.Isla_lang_ast.smt -> PPrintEngine.document
val pp_valu : Isla_lang__.Isla_lang_ast.valu -> PPrintEngine.document
val pp_selem : (string * Isla_lang__.Isla_lang_ast.valu) -> PPrintEngine.document
val pp_valu_concrete : Isla_lang__.Isla_lang_ast.valu_concrete -> PPrintEngine.document
val pp_selem_concrete : (string * Isla_lang__.Isla_lang_ast.valu_concrete) -> PPrintEngine.document
val pp_accessor : Isla_lang__.Isla_lang_ast.accessor -> PPrintEngine.document
val pp_accessor_list : Isla_lang__.Isla_lang_ast.accessor_list -> PPrintEngine.document
val pp_valu_option : Isla_lang__.Isla_lang_ast.valu_option -> PPrintEngine.document
val pp_event : 'a Isla_lang__.Isla_lang_ast.event -> PPrintEngine.document
val pp_trc : 'a Isla_lang__.Isla_lang_ast.trc -> PPrintEngine.document
module Base : sig ... end
This module wraps all isla-lang
functionality. No other module should directly touch the Isla_lang
module.
module Conv : sig ... end
module Manip : sig ... end
This module provide generic manipulation function of isla ast
module Preprocess : sig ... end
This module is about preprocessing isla traces. This includes:
module Server : sig ... end
This module is about launching isla as a background server and using it
module Test : sig ... end
This file about testing interaction with Isla for single instructions
module Type : sig ... end
This module is about type isla trace and register discovery.
Isla
include module type of struct include Base end
include module type of struct include Isla_lang.AST end
type lrng = Isla_lang__Isla_lang_ast.lrng =
| UnknownRng
| Generated of lrng
| Range of Stdlib.Lexing.position * Stdlib.Lexing.position
exception Parse_error_locn of lrng * string
val pp_lrng : lrng -> PPrint.document
type base_val = Isla_lang__Isla_lang_ast.base_val =
| Val_Symbolic of int
| Val_Bool of bool
| Val_Bits of string
| Val_Enum of enum
type assume_val = Isla_lang__Isla_lang_ast.assume_val =
| AVal_Var of string * accessor_list
| AVal_Bool of bool
| AVal_Bits of string
| AVal_Enum of enum
type tag_value = valu option
type !'a event = 'a Isla_lang__Isla_lang_ast.event =
| Smt of 'a smt * 'a
| Branch of int * string * 'a
| ReadReg of string * accessor_list * valu * 'a
| WriteReg of string * accessor_list * valu * 'a
| ReadMem of valu * valu * valu * int * tag_value * 'a
| WriteMem of valu * valu * valu * valu * int * tag_value * 'a
| BranchAddress of valu * 'a
| Barrier of valu * 'a
| CacheOp of valu * valu * 'a
| MarkReg of string * string * 'a
| Cycle of 'a
| Instr of valu * 'a
| Sleeping of int * 'a
| WakeRequest of 'a
| SleepRequest of 'a
| AssumeReg of string * accessor_list * valu * 'a
| Assume of 'a a_exp * 'a
| FunAssume of string * valu * arg_list * 'a
| UseFunAssume of string * valu * arg_list * 'a
| AbstractCall of string * valu * arg_list * 'a
| AbstractPrimop of string * valu * arg_list * 'a
type instruction_segments = Isla_lang__Isla_lang_ast.instruction_segments =
| Segments of segment list
type !'a maybe_fork = 'a Isla_lang__Isla_lang_ast.maybe_fork =
| Cases of string * 'a tree_trc list
| End
and !'a tree_trc = 'a Isla_lang__Isla_lang_ast.tree_trc =
| TreeTrace of 'a event list * 'a maybe_fork
type !'a trcs = 'a Isla_lang__Isla_lang_ast.trcs =
| Traces of 'a trc list
| TracesWithSegments of instruction_segments * 'a trc list
type !'a whole_tree = 'a Isla_lang__Isla_lang_ast.whole_tree =
| BareTree of 'a tree_trc
| TreeWithSegments of instruction_segments * 'a tree_trc
val subst_val_maybe_fork : base_val -> int -> 'a maybe_fork -> 'a maybe_fork
val subst_val_whole_tree : base_val -> int -> 'a whole_tree -> 'a whole_tree
module Lexer = Base.Lexer
module Parser = Base.Parser
exception ParseError of loc * string
Exception that represent an Isla parsing error
exception LexError of loc * string
Exception that represent an Isla lexing error
type 'a parser = lexer -> Stdlib.Lexing.lexbuf -> 'a
val parse : 'a parser -> ?filename:string -> Stdlib.Lexing.lexbuf -> 'a
Parse a single Isla instruction output from a Lexing.lexbuf
val parse_exp : ?filename:string -> Stdlib.Lexing.lexbuf -> rexp
Parse a single Isla expression from a Lexing.lexbuf
val parse_exp_string : ?filename:string -> string -> rexp
Parse a single Isla expression from a string
val parse_exp_channel : ?filename:string -> Stdlib.in_channel -> rexp
Parse a single Isla expression from a channel
val parse_trc : ?filename:string -> Stdlib.Lexing.lexbuf -> rtrc
Parse an Isla trace from a Lexing.lexbuf
val parse_trc_string : ?filename:string -> string -> rtrc
Parse an Isla trace from a string
val parse_trc_channel : ?filename:string -> Stdlib.in_channel -> rtrc
Parse an Isla trace from a channel
val parse_trcs_string : ?filename:string -> string -> rtrcs
val parse_trcs_channel : ?filename:string -> Stdlib.in_channel -> rtrcs
val parse_segments :
+ ?filename:string ->
+ Stdlib.Lexing.lexbuf ->
+ instruction_segments
val parse_segments_string : ?filename:string -> string -> instruction_segments
val parse_segments_channel :
+ ?filename:string ->
+ Stdlib.in_channel ->
+ instruction_segments
include module type of struct include Isla_lang.PP end
module Base : sig ... end
This module wraps all isla-lang
functionality. No other module should directly touch the Isla_lang
module.
module Conv : sig ... end
module Manip : sig ... end
This module provide generic manipulation function of isla ast
module Preprocess : sig ... end
This module is about preprocessing isla traces. This includes:
module Relocation : sig ... end
module Server : sig ... end
This module is about launching isla as a background server and using it
module Test : sig ... end
This file about testing interaction with Isla for single instructions
module Type : sig ... end
This module is about type isla trace and register discovery.
Other_cmds.CopySources
This module is the body implementation for the copy-sources
subcommand.
Other_cmds.CopySourcesCmd
Other_cmds.CopySourcesCmd
This module is the command-line processing for the copy-sources
subcommand.
Other_cmds.DumpDwarf
Other_cmds.DumpDwarf
This module adds a command to dump the interpreted DWARF information of an ELF file. The DWARF information is dumped as interpreted by read-dwarf,
See ReadDwarf
for a different dump/interpretation.
Other_cmds.DumpSym
Other_cmds.DumpSym
This module adds a command to dump the symbol of an ELF file with their content
ReadDwarf.Default
ReadDwarf.Default
Other_cmds.ReadDwarf
module Default : sig ... end
Other_cmds.ReadDwarf
module Default : sig ... end
Other_cmds
module CopySources : sig ... end
module CopySourcesCmd : sig ... end
module DumpDwarf : sig ... end
module DumpSym : sig ... end
module ReadDwarf : sig ... end
Other_cmds
module CopySources : sig ... end
This module is the body implementation for the copy-sources
subcommand.
module CopySourcesCmd : sig ... end
This module is the command-line processing for the copy-sources
subcommand.
module DumpDwarf : sig ... end
This module adds a command to dump the interpreted DWARF information of an ELF file. The DWARF information is dumped as interpreted by read-dwarf,
module DumpSym : sig ... end
This module adds a command to dump the symbol of an ELF file with their content
module ReadDwarf : sig ... end
Pretty printing is mostly done with the pprint
library. This library is exposed by the Utils.Pp
module with extra combinators. It should always be used by this Utils.Pp
module.
The logging system is defined in the module Utils.Logs
and is the main way of signaling information like warnings to the user.
Pretty printing is mostly done with the pprint
library. This library is exposed by the Utils.Pp
module with extra combinators. It should always be used by this Utils.Pp
module.
The logging system is defined in the module Utils.Logs
and is the main way of signaling information like warnings to the user.
Qtest_isla
Qtest_isla
Qtest_run
Qtest_run
Qtest_sig_aarch64
Qtest_sig_aarch64
Qtest_utils
Qtest_utils
Run.BB
module SMT = Z3
val dump : bool Cmdliner.Term.t
val reg_types : bool Cmdliner.Term.t
val no_run : bool Cmdliner.Term.t
val simp_trace : bool Cmdliner.Term.t
val simp_state : bool Cmdliner.Term.t
val simp : bool Cmdliner.Term.t
val elf : string Cmdliner.Term.t
val sym : string Cmdliner.Term.t
val len : int option Cmdliner.Term.t
val get_code : string -> string -> int option -> Utils.BytesSeq.t
val code_term : Utils.BytesSeq.t Cmdliner.Term.t
val simp_trace_term : bool Cmdliner.Term.t
val simp_state_term : bool Cmdliner.Term.t
val get_bb : bool -> bool -> bool -> Utils.BytesSeq.t -> Bb_lib.t
val bb_term : Bb_lib.t Cmdliner.Term.t
val run_bb : bool -> bool -> Bb_lib.t -> unit
val term : unit Cmdliner.Term.t
val info : Cmdliner.Term.info
val command : unit Cmdliner.Term.t * Cmdliner.Term.info
Run.BB
This module allow to do a test run of all the machinery for a single basic block
module SMT = Z3
val get_code : string -> string -> int option -> Utils.BytesSeq.t
val code_term : Utils.BytesSeq.t Cmdliner.Term.t
val get_bb : bool -> bool -> bool -> Utils.BytesSeq.t -> Bb_lib.t
val bb_term : Bb_lib.t Cmdliner.Term.t
val run_bb : bool -> bool -> Bb_lib.t -> unit
Run.Bb_lib
type trc
= Trace.t
type state
= State.t
type t
=
{
main : trc array; |
}
Type of a basic block.
The main part is the traces of all the non-branching instruction
val from_binary : Utils.BytesSeq.t -> t
Take a binary block and call isla on all the instruction to get traces Also does the typing of traces for register discovery. TODO Support variable length instructions
val simplify_mut : t -> unit
Simplifies the traces in the basic block
val run_mut : ?dwarf:Dw.t -> State.t -> t -> unit
Run a linear basic block on a state by mutation.
If dwarf
is provided, the run is typed.
val run : ?dwarf:Dw.t -> State.t -> t -> state
Run a linear basic block on a trace and return a new state
If dwarf
is provided, the run is typed.
val pp : t -> PPrintEngine.document
Pretty print the basic block (The traces)
Run.Bb_lib
This module provide code to manipulate basic block and run them.
This is only for use by BB
and debugging. I don't think this should be used for anything else. Block
should generally be used instead.
type trc = Trace.t
type state = State.t
Type of a basic block.
The main part is the traces of all the non-branching instruction
val from_binary : Utils.BytesSeq.t -> t
Take a binary block and call isla on all the instruction to get traces Also does the typing of traces for register discovery. TODO Support variable length instructions
val simplify_mut : t -> unit
Simplifies the traces in the basic block
Run a linear basic block on a state by mutation.
If dwarf
is provided, the run is typed.
Run a linear basic block on a trace and return a new state
If dwarf
is provided, the run is typed.
val pp : t -> Utils.Pp.document
Pretty print the basic block (The traces)
Run.Block
val no_run : bool Cmdliner.Term.t
val reg_types : bool Cmdliner.Term.t
val len : int option Cmdliner.Term.t
val breakpoints : string list Cmdliner.Term.t
val ensure_linear : bool Cmdliner.Term.t
val elf : string Cmdliner.Term.t
val start : string Cmdliner.Term.t
val get_elf_start : string -> string -> Elf.File.t * Elf.SymTable.sym_offset
val elf_term : (Elf.File.t * Elf.SymTable.sym_offset) Cmdliner.Term.t
val gen_block : (Elf.File.t * Elf.SymTable.sym_offset) -> int option -> string list -> Elf.File.t * Block_lib.t
val elfblock_term : (Elf.File.t * Block_lib.t) Cmdliner.Term.t
val prune_paths : (State.Base.t -> bool) -> 'a State.Tree.t -> 'a State.Tree.t option
val has_assert_false : State.t -> bool
val run_block : (Elf.File.t * Block_lib.t) -> bool -> bool -> bool -> unit
val term : unit Cmdliner.Term.t
val info : Cmdliner.Term.info
val command : unit Cmdliner.Term.t * Cmdliner.Term.info
Run.Block
This module add the run-block
sub command.
This subcommand is about running a complex block of execution It start at a specific offset in a block and can terminate on various condition. If not enough condition are met, it may crash by reaching an invalid instruction
val get_elf_start : string -> string -> Elf.File.t * Elf.SymTable.sym_offset
val elf_term : (Elf.File.t * Elf.SymTable.sym_offset) Cmdliner.Term.t
val gen_block :
+ (Elf.File.t * Elf.SymTable.sym_offset) ->
+ int option ->
+ string list ->
+ Elf.File.t * Block_lib.t
val elfblock_term : (Elf.File.t * Block_lib.t) Cmdliner.Term.t
val prune_paths :
+ (State.Base.t -> bool) ->
+ 'a State.Tree.t ->
+ 'a State.Tree.t option
val has_assert_false : State.t -> bool
val run_block : (Elf.File.t * Block_lib.t) -> bool -> bool -> bool -> unit
Run.Block_lib
type t
=
{
runner : Runner.t; |
start : int; |
endpred : State.exp -> string option; |
}
endpred pc_exp
gives when to stop
val make : runner:Runner.t -> start:int -> endpred:(State.exp -> string option) -> t
Build a complex block starting from start
in sym
and ending when endpred
says so. endpred
is a predicate on the symbolic PC expression
type label
=
The labels on tree node at the output of run
val label_to_string : label -> string
val pp_label : label -> Utils.Pp.document
val run : ?every_instruction:bool -> ?relevant:(int, 'a) Stdlib.Hashtbl.t -> t -> State.t -> label State.Tree.t
Run the block an return a state tree indexed by the addresses of the branches.
When every_instruction
is true, It will make a snapshot of the state i.e a tree node at each instruction. By default it will only make a Tree node on branching points.
The output is a tree because state merging is not implemented so if we are going twice on the same PC, the whole thing will be run twice separately in two separate tree branches.
val gen_endpred : ?min:int -> ?max:int -> ?loop:int -> ?brks:int list -> unit -> State.exp -> string option
Generic end predicate. Will stop if:
min
max
brks
loop
Run.Block_lib
This module provides a representation of a complex block of code.
The end of the block is decided by endpred
, an arbitrary predicate on the pc. In particular if the PC is symbolic the execution is stopped anyway. This means that we either reached the top level function return or an unresolved branch table.
To generate easily end predicates, there is gen_endpred
.
endpred pc_exp
gives when to stop
val make :
+ runner:Runner.t ->
+ start:Elf.Address.t ->
+ endpred:(State.exp -> string option) ->
+ t
Build a complex block starting from start
in sym
and ending when endpred
says so. endpred
is a predicate on the symbolic PC expression
type label =
| Start
Root node of the tree
*)| End of string
Lead node of the tree, the string describe which end condition has be triggered
*)| BranchAt of Elf.Address.t
A Branching node at a given PC
*)| NormalAt of Elf.Address.t
A normal instruction at PC. Exists only if every_instruction
is true
The labels on tree node at the output of run
val label_to_string : label -> string
val pp_label : label -> Utils.Pp.document
val run :
+ ?every_instruction:bool ->
+ ?relevant:(Elf.Address.t, 'a) Stdlib.Hashtbl.t ->
+ t ->
+ State.t ->
+ label State.Tree.t
Run the block an return a state tree indexed by the addresses of the branches.
When every_instruction
is true, It will make a snapshot of the state i.e a tree node at each instruction. By default it will only make a Tree node on branching points.
The output is a tree because state merging is not implemented so if we are going twice on the same PC, the whole thing will be run twice separately in two separate tree branches.
val gen_endpred :
+ ?min:Elf.Address.t ->
+ ?max:Elf.Address.t ->
+ ?loop:int ->
+ ?brks:Elf.Address.t list ->
+ unit ->
+ State.exp ->
+ string option
Generic end predicate. Will stop if:
min
max
brks
loop
Run.Func
val get_state_tree : elf:string -> name:string -> ?dump:bool -> ?entry:bool -> ?len:int -> ?breakpoints:string list -> ?loop:int -> ?tree_to_file:string -> unit -> Block_lib.label State.Tree.t
val command : unit Cmdliner.Term.t * Cmdliner.Term.info
Run.Func
val get_state_tree :
+ elf:string ->
+ name:string ->
+ ?dump:bool ->
+ ?entry:bool ->
+ ?len:int ->
+ ?breakpoints:string list ->
+ ?loop:int ->
+ ?tree_to_file:string ->
+ ?init:(State.t -> State.t) ->
+ ?every_instruction:bool ->
+ unit ->
+ Block_lib.label State.Tree.t
Run.FuncRD
val run_func_rd : string -> string -> string -> string option -> string list -> unit
val elf : string Cmdliner.Term.t
val func : string Cmdliner.Term.t
val objdump_d : string Cmdliner.Term.t
val branch_table : string option Cmdliner.Term.t
val breakpoints : string list Cmdliner.Term.t
val term : unit Cmdliner.Term.t
val info : Cmdliner.Term.info
val command : unit Cmdliner.Term.t * Cmdliner.Term.info
Run.FuncRD
This module is a merge of rd
sub command and run-func --loop=1
. It will run the equivalent of run-func --loop=1
, and print the instruction like rd
but will print the state in a light form (State.pp_partial
) between instructions.
Run.Init
val init_state : State.t option Stdlib.ref
The initial state
val init : unit -> State.t
Intialize this module by calling Isla on Arch.nop
to get initial machine state
val state : unit -> State.t
Return the initial state. Compute it if required.
Run.Init
This module handle architecture initialization. For now it trust default isla initialization but this should change soon (TODO)
val init_state : State.t option Stdlib.ref
The initial state
val init : unit -> State.t
Intialize this module by calling Isla on Arch.nop
to get initial machine state
val state : unit -> State.t
Return the initial state. Compute it if required.
Run.Instr
val instr : string Cmdliner.Term.t
val dump_trace : bool Cmdliner.Term.t
val dump_isla : bool Cmdliner.Term.t
val no_run : bool Cmdliner.Term.t
val isla_run : bool Cmdliner.Term.t
val simp_trace : bool Cmdliner.Term.t
val simp_state : bool Cmdliner.Term.t
val simp : bool Cmdliner.Term.t
val reg_types : bool Cmdliner.Term.t
val init : bool Cmdliner.Term.t
val elf : string option Cmdliner.Term.t
val get_instr : Config.Arch.t -> string -> string option -> Utils.BytesSeq.t
val instr_term : Utils.BytesSeq.t Cmdliner.Term.t
val simp_trace_term : bool Cmdliner.Term.t
val simp_state_term : bool Cmdliner.Term.t
val get_traces : Utils.BytesSeq.t -> bool -> bool -> traces
val pre_traces_term : traces Cmdliner.Term.t
val simp_traces : bool -> traces -> traces
val dump_traces : bool -> traces -> traces
val traces_term : traces Cmdliner.Term.t
val run_instr : bool -> bool -> bool -> traces -> unit
val term : unit Cmdliner.Term.t
val info : Cmdliner.Term.info
val command : unit Cmdliner.Term.t * Cmdliner.Term.info
Run.Instr
This module add the run-instr
sub command.
This subcommand is about testing the various operations that can happen in an instruction processing. This is not isla-test
, the instructions will always be fetched from the cache and use Init
. If this fail at an earlier point, use isla-test
to debug the Isla pipeline
Additionally run-instr
supports branching instructions.
val get_instr : Config.Arch.t -> string -> string option -> Utils.BytesSeq.t
val instr_term : Utils.BytesSeq.t Cmdliner.Term.t
val get_traces : 'a -> 'b -> 'c -> traces
val pre_traces_term : traces Cmdliner.Term.t
val traces_term : traces Cmdliner.Term.t
val run_instr : bool -> bool -> bool -> traces -> unit
Run.ReadDwarf
val dry_run : unit Cmdliner.Term.t
val skylight : unit Cmdliner.Term.t
val comp_dir : unit Cmdliner.Term.t
val clip_binary : unit Cmdliner.Term.t
val no_vars : unit Cmdliner.Term.t
val no_cfa : unit Cmdliner.Term.t
val no_source : unit Cmdliner.Term.t
val objdump_d : unit Cmdliner.Term.t
val branch_tables : unit Cmdliner.Term.t
val elf : unit Cmdliner.Term.t
val objdump_d2 : unit Cmdliner.Term.t
val branch_tables2 : unit Cmdliner.Term.t
val elf2 : unit Cmdliner.Term.t
val qemu_log : unit Cmdliner.Term.t
val out_file : unit Cmdliner.Term.t
val out_dir : unit Cmdliner.Term.t
val cfg_dot_file : unit Cmdliner.Term.t
val cfg_source_nodes : unit Cmdliner.Term.t
val cfg_source_nodes2 : unit Cmdliner.Term.t
val html : unit Cmdliner.Term.t
val options : unit Cmdliner.Term.t list
val full_term : unit Cmdliner.Term.t
val info : Cmdliner.Term.info
val command : unit Cmdliner.Term.t * Cmdliner.Term.info
Run.ReadDwarf
This module implements the rd
subcommand. This was to original meaning of "read-dwarf", and the first command.
Together with run-func-rd
, this is the main user of the Analyse
code.
Run.Runner
module Reg = State.Reg
type slot
=
| Normal of Trace.Instr.t | The traces and the size of the instruction |
| Special of int | Special instructions. Will be used to represent external events |
| Nocode | The is no code at this address. Running it is UB. Also used if an address is in between instructions |
| IslaFail of int | This means Isla pipeline failed on that instruction |
Give the instruction descriptor at a given address
type t
=
{
elf : Elf.File.t; | |
dwarf : Dw.t option; | |
instrs : (int, slot) Stdlib.Hashtbl.t; | Instruction cache |
pc : Reg.t; | |
funcs : int Utils.Vec.t; | Loaded functions by loading order |
}
val of_elf : ?dwarf:Dw.t -> Elf.File.t -> t
val of_dwarf : Dw.t -> t
val load_sym : t -> Elf.Symbol.t -> unit
Load a symbol into the runner. All instruction traces are fetched and cached.
TODO support variable length instructions.
val execute_normal : ?prelock:(State.t -> unit) -> pc:int -> t -> Trace.Instr.t -> State.t -> State.t list
Run the traces on the state.
If the state is unlocked and the instruction is single trace, then the function mutates the state and returns the same state.
Otherwise the state is locked (if not) and new states are returned
In any case the returned states are unlocked.
val skip : t -> State.t -> State.t list
val run : ?prelock:(State.t -> unit) -> t -> State.t -> State.t list
Do the whole fetch and execute cycle. Take the PC from the state, and fetch it's instruction and then run it. It return the list of possible behavior of that instruction. Normally the union of the set of concrete states represented by this list of symbolic state cover all the defined behaviors of the fetched instruction from the initial state.
If the state is unlocked and the instruction is single trace, then the function mutates the state and returns the same state.
Otherwise the state is locked (if not) and new unlocked states are returned.
In any case the returned states are unlocked.
val expect_normal : t -> int -> Trace.Instr.t
Return the Instr
.t data of the instruction at address, and throw Not_found
if the instruction was invalid
val get_normal_opt : t -> int -> Trace.Instr.t option
Return the Instr
.t data of the instruction at address, and None
if the instruction was invalid
val pp_slot : slot -> PPrintEngine.document
Pretty prints a instruction slot
Run.Runner
This module provide the program runner that caches all the information required to make a state transition
Later this module will handle inlining in a transparent way, and may also compress basic block traces (We'll need to check this is okay with type inference)
For now this module load instructions on a per-symbol basis, and do not try to load instructions outside of function symbol. TODO: remove this restriction.
The final goal of this module is to encode all necessary information to perform a state transition. In particular, the run
function should never require more argument: If extra information is required, it should be part of the runner if it is dependent on the whole program or in the state if it is specific to the state.
module Reg = State.Reg
type slot =
| Normal of Trace.Instr.t
The traces and the size of the instruction
*)| Special of int
Special instructions. Will be used to represent external events
*)| Nocode
The is no code at this address. Running it is UB. Also used if an address is in between instructions
*)| IslaFail of int
This means Isla pipeline failed on that instruction
*)Give the instruction descriptor at a given address
type t = {
elf : Elf.File.t;
dwarf : Dw.t option;
instrs : (Elf.Address.t, slot) Stdlib.Hashtbl.t;
Instruction cache
*)pc : Reg.t;
funcs : Elf.Address.t Utils.Vec.t;
Loaded functions by loading order
*)}
val of_elf : ?dwarf:Dw.t -> Elf.File.t -> t
val load_sym : t -> Elf.Symbol.t -> unit
Load a symbol into the runner. All instruction traces are fetched and cached.
TODO support variable length instructions.
val fetch : t -> Elf.Address.t -> slot
Fetch an instruction, and return corresponding slot.
val execute_normal :
+ ?prelock:(State.t -> unit) ->
+ pc:Elf.Address.t ->
+ t ->
+ Trace.Instr.t ->
+ State.t ->
+ State.t list
Run the traces on the state.
If the state is unlocked and the instruction is single trace, then the function mutates the state and returns the same state.
Otherwise the state is locked (if not) and new states are returned
In any case the returned states are unlocked.
Do the whole fetch and execute cycle. Take the PC from the state, and fetch it's instruction and then run it. It return the list of possible behavior of that instruction. Normally the union of the set of concrete states represented by this list of symbolic state cover all the defined behaviors of the fetched instruction from the initial state.
If the state is unlocked and the instruction is single trace, then the function mutates the state and returns the same state.
Otherwise the state is locked (if not) and new unlocked states are returned.
In any case the returned states are unlocked.
val expect_normal : t -> Elf.Address.t -> Trace.Instr.t
Return the Instr.t
data of the instruction at address, and throw Not_found
if the instruction was invalid
val get_normal_opt : t -> Elf.Address.t -> Trace.Instr.t option
Return the Instr.t
data of the instruction at address, and None
if the instruction was invalid
val pp_slot : slot -> Utils.Pp.document
Pretty prints a instruction slot
val pp_instr : t -> Utils.Pp.document
Dump instruction table
Run
module BB : sig ... end
module Bb_lib : sig ... end
module Block : sig ... end
module Block_lib : sig ... end
module Func : sig ... end
module FuncRD : sig ... end
module Init : sig ... end
module Instr : sig ... end
module ReadDwarf : sig ... end
module Runner : sig ... end
Run
module BB : sig ... end
This module allow to do a test run of all the machinery for a single basic block
module Bb_lib : sig ... end
This module provide code to manipulate basic block and run them.
module Block : sig ... end
This module add the run-block
sub command.
module Block_lib : sig ... end
This module provides a representation of a complex block of code.
module Func : sig ... end
module FuncRD : sig ... end
This module is a merge of rd
sub command and run-func --loop=1
. It will run the equivalent of run-func --loop=1
, and print the instruction like rd
but will print the state in a light form (State.pp_partial
) between instructions.
module Init : sig ... end
This module handle architecture initialization. For now it trust default isla initialization but this should change soon (TODO)
module Instr : sig ... end
This module add the run-instr
sub command.
module ReadDwarf : sig ... end
This module implements the rd
subcommand. This was to original meaning of "read-dwarf", and the first command.
module RelProg : sig ... end
module Runner : sig ... end
This module provide the program runner that caches all the information required to make a state transition
module TestRelProg : sig ... end
Sig
This module define all architecture-dependent configuration
It should be used instead of Arch
inside the architecture dependent modules.
Everything inside this module is copied into Arch
, so module that can depend on Arch
may do so.
type func_abi
=
{
init : State.t -> State.t; | Gives the initial state for verifying the function, from a given global register state. Only global registers are kept. |
}
Describe the ABI of a function
This is a record because I expect to add many other fields later.
type dwarf_reg_map
= State.Reg.t array
The map of dwarf register: Which register number map to which ISA register
val supports : Config.Arch.t -> bool
Tells if this Arch module supports this architecture
val init : Config.Arch.t -> unit
If this arch module supports
the architecture, then initialize read-dwarf state using this architecture
val initialized : unit -> Config.Arch.t option
Return Some(arch)
is the loaded arch is arch
and None
if nothing is loaded yet.
val module_name : string
The name of the arch module. Must be the name of the module i.e. Config.arch_module
val loaded_name : string
For dynamic arch module, the name of the dynamically loaded module. Otherwise module_name
val dwarf_reg_map : unit -> dwarf_reg_map
Get the register map of the architecture
val is_local : State.Reg.t -> bool
Tell if a register is local for the ABI
val nop : unit -> Utils.BytesSeq.t
Give the opcode of the nop instruction (For Sail/Isla initialisation
val pc : unit -> State.Reg.t
Give the register index for the program counter
val sp : unit -> State.Reg.t
Give the register index for the stack pointer
val assemble_to_elf : string -> string
Take an instruction string and give the name of an temporary ELF file created that contains the instruction at symbol instr.
val split_into_instrs : Utils.BytesSeq.t -> Utils.BytesSeq.t list
Split a byte-sequence into a list of instructions.
val is_ret : Utils.BytesSeq.t -> bool
Tell if an instruction is a return instruction.
val is_cmp : Utils.BytesSeq.t -> (State.Reg.t * Utils.BitVec.t) option
Tell if an instruction is a compare instruction. Returns Some (reg,bv)
where the contents of reg
are compared against the value bv
if it is and None
if not.
val is_bl : Utils.BytesSeq.t -> Utils.BitVec.t option
Tell if an instruction is an (unconditional) branch on immediate with link instructions. Returns Some bv
where bv
is the offset (from the address of this instruction, in the range +/-128MB) that is branched to if it is and None
if not.
Sig
This module define all architecture-dependent configuration
It should be used instead of Arch
inside the architecture dependent modules.
Everything inside this module is copied into Arch
, so module that can depend on Arch
may do so.
type func_abi = {
init : State.t -> State.t;
Gives the initial state for verifying the function, from a given global register state. Only global registers are kept.
*)}
Describe the ABI of a function
This is a record because I expect to add many other fields later.
type dwarf_reg_map = State.Reg.t array
The map of dwarf register: Which register number map to which ISA register
val supports : Config.Arch.t -> bool
Tells if this Arch module supports this architecture
val init : Config.Arch.t -> unit
If this arch module supports
the architecture, then initialize read-dwarf state using this architecture
val initialized : unit -> Config.Arch.t option
Return Some(arch)
is the loaded arch is arch
and None
if nothing is loaded yet.
The name of the arch module. Must be the name of the module i.e. Config.arch_module
For dynamic arch module, the name of the dynamically loaded module. Otherwise module_name
val dwarf_reg_map : unit -> dwarf_reg_map
Get the register map of the architecture
val is_local : State.Reg.t -> bool
Tell if a register is local for the ABI
val nop : unit -> Utils.BytesSeq.t
Give the opcode of the nop instruction (For Sail/Isla initialisation
val pc : unit -> State.Reg.t
Give the register index for the program counter
val sp : unit -> State.Reg.t
Give the register index for the stack pointer
Take an instruction string and give the name of an temporary ELF file created that contains the instruction at symbol instr.
val split_into_instrs : Elf.Symbol.data -> Elf.Symbol.data list
Split a byte-sequence into a list of instructions.
val is_ret : Utils.BytesSeq.t -> bool
Tell if an instruction is a return instruction.
val is_cmp : Utils.BytesSeq.t -> (State.Reg.t * Utils.BitVec.t) option
Tell if an instruction is a compare instruction. Returns Some (reg,bv)
where the contents of reg
are compared against the value bv
if it is and None
if not.
val is_bl : Utils.BytesSeq.t -> Utils.BitVec.t option
Tell if an instruction is an (unconditional) branch on immediate with link instructions. Returns Some bv
where bv
is the offset (from the address of this instruction, in the range +/-128MB) that is branched to if it is and None
if not.
Base.MemRel
type block
= State.Mem.Fragment.Block.t
val of_int : int -> ('a, 'b) ExTy.t
val eq_block : block -> block -> ('a, 'b) ExTy.t * (State__Base.Mem.Fragment.var, Ast.no) ExTy.t
type event
= State.Mem.Fragment.Event.t
type trace
= event list
val eq : trace -> trace -> (State__Base.Mem.Fragment.var, Ast.no) ExTy.t list
For now, just assume an equality relation for memory
val eq : State.t -> State.t -> (State__Base.Mem.Fragment.var, Ast.no) ExTy.t list
Base.MemRel
type block = State.Mem.Fragment.Block.t
val of_int : int -> ('a, 'b) ExTy.t
type event = State.Mem.Fragment.Event.t
type trace = event list
Base.RegRel
val reg_map_fold : 'a -> ('a -> Reg.Map.reg -> 'b -> 'a) -> 'b Reg.Map.t -> 'a
val present : Reg.t Utils.List.t option -> Reg.t -> bool
val eq : ?except:Reg.t Utils.List.t -> State.t -> State.t -> (State__Base.Exp.var, Ast.no) ExTy.t
This is not symmetric (does it need to be?). NOTE: If there is a reg in st2 that is not in st1, it will not be checked. Example: test3, O2, state 1, __defaultRAM -> |reg:0:__defaultRAM| (not in O0)
Base.RegRel
val reg_map_fold : 'a -> ('a -> Reg.Map.reg -> 'b -> 'a) -> 'b Reg.Map.t -> 'a
val present : Reg.t Utils.List.t option -> Reg.t -> bool
val eq :
+ ?except:Reg.t Utils.List.t ->
+ State.t ->
+ State.t ->
+ (State__Base.Var.t, Ast.no) ExTy.t
This is not symmetric (does it need to be?). NOTE: If there is a reg in st2 that is not in st1, it will not be checked. Example: test3, O2, state 1, __defaultRAM -> |reg:0:__defaultRAM| (not in O0)
Base.Test2
val infer_rels : 'a State.Tree.t pair -> rels
Base.Test2
val infer_rels : 'a State.Tree.t pair -> rels
Base.Test3
val infer_rels : 'a State.Tree.t pair -> rels
Base.Test3
val infer_rels : 'a State.Tree.t pair -> rels
Simrel.Base
module Tree = State.Tree
module ExTy = Exp.Typed
module Tval = State.Tval
module Reg = State.Reg
module Simplify = State.Simplify
module Z3St = State.Simplify.Z3St
module Id = State.Id
type tree
= Run.Block_lib.label Tree.t
type 'a pair
=
{
o0 : 'a; |
o2 : 'a; |
}
val rel_of : (State__Base.Exp.var, Ast.no) ExTy.t pair list -> Tval.t -> Tval.t -> (State__Base.Exp.var, Ast.no) ExTy.t
type field
=
| Reg of Reg.t |
type sep
=
| Eq of field pair list |
type rel
=
| Same |
| Star of sep * rel |
Same should always be at the end.
module Test2 : sig ... end
module Test3 : sig ... end
module MemRel : sig ... end
module RegRel : sig ... end
Simrel.Base
This module encodes the definition and checking of the simulation relation.
module Tree = State.Tree
module ExTy = Exp.Typed
module Tval = State.Tval
module Reg = State.Reg
module Simplify = State.Simplify
module Z3St = State.Simplify.Z3St
module Id = State.Id
type tree = Run.Block_lib.label Tree.t
type rels = matched list
module Test2 : sig ... end
module Test3 : sig ... end
module MemRel : sig ... end
module RegRel : sig ... end
val assn_to_exp : State.t -> (State.Exp.var, Ast.no) ExTy.t
val check_rel : matched list -> bool
Simrel
module Base : sig ... end
Simrel
module Base : sig ... end
This module encodes the definition and checking of the simulation relation.
Base.Exp
Module for state expressions
Base.Exp
Module for state expressions
include Exp.S with type var = var
type var = var
The type of variable provided in the functor
type t = (var, Ast.no) Exp.Typed.t
The type of expression on which this module works
Test syntactic equality. a + b
and b + a
would test different under this predicate
val pp : t -> Utils.Pp.document
Pretty print the expression using PpExp
val pp_smt : t -> Utils.Pp.document
Pretty print the expression in SMTLIB language
val expect_sym_address : t -> Elf.Address.t
val of_section : size:int -> string -> t
val of_address : size:int -> Elf.Address.t -> t
Base.Id
The type of a state ID. for now it's an integer, but it may change later In particular whether a state belong to O0 or O2 may be part of the id
at some point.
Base.Id
The type of a state ID. for now it's an integer, but it may change later In particular whether a state belong to O0 or O2 may be part of the id
at some point.
Fragment.Block
This module provide the concept of memory block, as used by a fragment.
The block represent a memory area that contain a single memory expression.
It may represent a symbolic or concrete address. In the fist case, It may also be concretely bounded.
Fragment.Block
This module provide the concept of memory block, as used by a fragment.
The block represent a memory area that contain a single memory expression.
It may represent a symbolic or concrete address. In the fist case, It may also be concretely bounded.
Fragment.Event
This module provide the trace of reads and writes to a symbolic fragment.
Fragment.Event
This module provide the trace of reads and writes to a symbolic fragment.
val pp : t -> Utils.Pp.document
Mem.Fragment
type var
= Var.t
The type of variables used
module Size = Ast.Size
type exp
= (var, Ast.no) Exp.Typed.t
The type of expressions stored in the fragment
module Block : sig ... end
This module provide the concept of memory block, as used by a fragment.
module Event : sig ... end
This module provide the trace of reads and writes to a symbolic fragment.
val empty : t
The empty memory fragment. Any read will be symbolic
val try_read : t -> Block.t -> exp option
Try to read a expression in a block. If one can provide a symbolic expression representing the content of the block then Some
is returned, otherwise None
is returned.
val try_read_naive : t -> Block.t -> exp option
Same semantic as try_read
but ignores the caches. Is supposed to be slower.
The required property is that if try_read
return Some value
then try_read_naive
must return the same value. It's possible that try_read_naive give a result when try_read
don't
val read_sym : t -> Block.t -> var -> t
Read a symbolic variable from a block. This bound this symbolic variable to the The content of the block in the current memory state.contents
val map_exp : (exp -> exp) -> t -> t
Map a function over all contained expressions. This function must not change the semantic meaning of symbolic expressions in any way. It is intended to be used with simplifying functions and the like
val iter_exp : (exp -> unit) -> t -> unit
Iter a function over all contained expression. Expression may appear more or less than anticipated because of various caching.
val check_cache : t -> unit
Check cache integrity. Throw if something is wrong
val is_empty : t -> bool
Tells if the memory is empty since it's initial base. (No new memory operation were added
Mem.Fragment
type var = Var.t
The type of variables used
module Size = Ast.Size
type exp = (var, Ast.no) Exp.Typed.t
The type of expressions stored in the fragment
module Block : sig ... end
This module provide the concept of memory block, as used by a fragment.
module Event : sig ... end
This module provide the trace of reads and writes to a symbolic fragment.
val empty : t
The empty memory fragment. Any read will be symbolic
Try to read a expression in a block. If one can provide a symbolic expression representing the content of the block then Some
is returned, otherwise None
is returned.
Read a symbolic variable from a block. This bound this symbolic variable to the The content of the block in the current memory state.contents
Map a function over all contained expressions. This function must not change the semantic meaning of symbolic expressions in any way. It is intended to be used with simplifying functions and the like
Iter a function over all contained expression. Expression may appear more or less than anticipated because of various caching.
val check_cache : t -> unit
Check cache integrity. Throw if something is wrong
val is_empty : t -> bool
Tells if the memory is empty since it's initial base. (No new memory operation were added
val pp_raw : t -> Utils.Pp.document
Pretty prints the raw internals fragment. TODO: A nice pretty printer
Base.Mem
This module manages the memory part of the state.
The symbolic memory bounds certain symbolic address to symbolic values, but most addresses are not bound. This basically correspond to a concrete instantiation of a SymbolicFragment
.
However in most case, we have some information that some symbolic value do not alias other symbolic values.For example in no address involving the stack pointer may alias any address no involving the stack pointer except in specific case of escaping which are explicitly not supported (yet). It is intended to support escaping later. There can be other kind of non-aliasing information in case of explicit restrict
annotation or when implicitly passing or returning value by pointer (which some ABI do when such value are too big).
In the absence of escaping, such problem can be solved by representing the memory with multiple SymbolicFragment
. One for the main memory, and one for each "restricted block". The module encapsulate all those different fragment.
To manage such a system, we use the C type system to carry around provenance information in the type Ctype.provenance
. When doing a read
or a write
, this provenance is used to route the read or write to the right symbolic block.
This means that the provenance-tracking part of the C type system must be part of the TCB of read-dwarf as the soundness of the symbolic memory model depend on it.
Finally, the current implementation is only suitable for sequential execution, a new theoretical model and implementation must be developed for concurrent shared memory.
Implementation detail: This type has an imperative interface even if the underlying SymbolicFragment
has a pure interface.
val new_frag : t -> exp -> Ctype.provenance
Add a new fragment with the specified base
module Fragment : State__.SymbolicFragment.S with type var = Var.t
val get_main : t -> Fragment.t
Get the main fragment of memory
Base.Mem
This module manages the memory part of the state.
The symbolic memory bounds certain symbolic address to symbolic values, but most addresses are not bound. This basically correspond to a concrete instantiation of a SymbolicFragment
.
However in most case, we have some information that some symbolic value do not alias other symbolic values.For example in no address involving the stack pointer may alias any address no involving the stack pointer except in specific case of escaping which are explicitly not supported (yet). It is intended to support escaping later. There can be other kind of non-aliasing information in case of explicit restrict
annotation or when implicitly passing or returning value by pointer (which some ABI do when such value are too big).
In the absence of escaping, such problem can be solved by representing the memory with multiple SymbolicFragment
. One for the main memory, and one for each "restricted block". The module encapsulate all those different fragment.
To manage such a system, we use the C type system to carry around provenance information in the type Ctype.provenance
. When doing a read
or a write
, this provenance is used to route the read or write to the right symbolic block.
This means that the provenance-tracking part of the C type system must be part of the TCB of read-dwarf as the soundness of the symbolic memory model depend on it.
Finally, the current implementation is only suitable for sequential execution, a new theoretical model and implementation must be developed for concurrent shared memory.
Implementation detail: This type has an imperative interface even if the underlying SymbolicFragment
has a pure interface.
val new_frag : t -> exp -> Ctype.provenance
Add a new fragment with the specified base
module Fragment : sig ... end
val get_main : t -> Fragment.t
Get the main fragment of memory
val get_frag : t -> int -> Exp.t * Fragment.t
Get fragment
Base.Tval
Module for optionally typed state expressions. Those are symbolic values which may or may not have a C type.
val of_exp : ?ctyp:Ctype.t -> exp -> t
val of_var : ?ctyp:Ctype.t -> var -> t
val of_reg : ?ctyp:Ctype.t -> id -> Reg.t -> t
val map_exp : (exp -> exp) -> t -> t
val iter_exp : (exp -> 'a) -> t -> 'a
val exp : t -> exp
val ctyp : t -> Ctype.t option
val equal : t -> t -> bool
val pp : t -> PPrintEngine.document
Base.Tval
Module for optionally typed state expressions. Those are symbolic values which may or may not have a C type.
val pp : t -> Utils.Pp.document
Base.Var
This module provide state variables. Those are all symbolic variables that may appear in a state. If the same (in the Var.equal
sense) variable appear in two state, that mean that when considered together, there is an implicit relation between them.
This means that the set of pair of concrete states represented by a pair of symbolic state could be a strict subset of the Cartesian product of the sets of concrete state represented by each symbolic state individually.
type t
=
| Register of id * Reg.t | The value of this register in this state |
| ReadVar of id * int * Ast.Size.t | The result of a certain read in a certain state. The size part is not semantically important: Two |
| Arg of int | A function argument |
| RetArg | The address to which the return value should be written. This is used only in certain calling conventions |
| RetAddr | The return address: The address to which a "return" instruction would jump. |
| NonDet of int * Ast.Size.t | Variable representing non-determinism in the spec. Can only be a bit-vector for now. |
The type of a variable in the state
val to_string : t -> string
Convert the variable to the string encoding. For parsing infrastructure reason, the encoding must always contain at least one :
.
val expect_register : t -> Reg.t
Expect a register variable and return the corresponding register. Throw otherwise.
val expect_readvar : t -> int
Expect a read variable and return the corresponding index. Throw otherwise.
val of_string : string -> t
The opposite of to_string
. Will raise Invalid_argument
when the string don't match
val equal : t -> t -> bool
val hash : t -> int
Hashing function for variable. For now it's polymorphic but this may stop at any time
val pp : t -> PPrintEngine.document
Basically to_string
in pp mode
val pp_bar : t -> PPrintEngine.document
Pretty prints but with bars around
Base.Var
This module provide state variables. Those are all symbolic variables that may appear in a state. If the same (in the Var.equal
sense) variable appear in two state, that mean that when considered together, there is an implicit relation between them.
This means that the set of pair of concrete states represented by a pair of symbolic state could be a strict subset of the Cartesian product of the sets of concrete state represented by each symbolic state individually.
type t =
| Register of id * Reg.t
The value of this register in this state
*)| ReadVar of id * int * Ast.Size.t
The result of a certain read in a certain state. The size part is not semantically important: Two ReadVar
with same id
and same number may no have different sizes
| Arg of int
A function argument
*)| RetArg
The address to which the return value should be written. This is used only in certain calling conventions
*)| RetAddr
The return address: The address to which a "return" instruction would jump.
*)| NonDet of int * Ast.Size.t
Variable representing non-determinism in the spec. Can only be a bit-vector for now.
*)| Section of string
Symbolic base address of ELF section. Assume 64bit for now.
*)The type of a variable in the state
val to_string : t -> string
Convert the variable to the string encoding. For parsing infrastructure reason, the encoding must always contain at least one :
.
Expect a register variable and return the corresponding register. Throw otherwise.
val expect_readvar : t -> int
Expect a read variable and return the corresponding index. Throw otherwise.
val of_string : string -> t
The opposite of to_string
. Will raise Invalid_argument
when the string don't match
val hash : t -> int
Hashing function for variable. For now it's polymorphic but this may stop at any time
val pp : t -> Utils.Pp.document
Basically to_string
in pp mode
val pp_bar : t -> Utils.Pp.document
Pretty prints but with bars around
val new_nondet : Ast.Size.t -> t
Get a fresh NonDet variable
State.Base
This module introduce a type to represent the state of the machine.
The symbolic state in this module do not mathematically represent a single state but a set of concrete state with all the symbolic variable over the whole range of their types. State also contain assertions, and so only represent the subset of concrete states, that satisfy all assertions.
Currently the state type only represent the register (including system register) and sequential memory part of an actual machine state. Any other architectural state is not represented.
Additionally, state contain C type information for the Ctype
inference system. Those fields are not semantically part of the state and do not influence in any way which concrete states are represented by the symbolic state. Expect for provenance information: Pointers can be tagged with provenance information which mean that they are part of a specific restricted block of memory or the main block. See Mem
for more information. Thus all the implicit non-aliasing assertion implied by those provenance field are to be considered as part of the group of assertion restricting the set of concrete state represented by a symbolic state.
The presence of C types is optional, which means that this state type can be used in a completely untyped context.
Concrete detail about how symbolic state are represented in Ocaml is in the documentation of t
.
module Id : sig ... end
The type of a state ID. for now it's an integer, but it may change later In particular whether a state belong to O0 or O2 may be part of the id
at some point.
type id
= Id.t
module Var : sig ... end
This module provide state variables. Those are all symbolic variables that may appear in a state. If the same (in the Var.equal
sense) variable appear in two state, that mean that when considered together, there is an implicit relation between them.
type var
= Var.t
The type of variables
module Exp : sig ... end
Module for state expressions
type exp
= Exp.t
module Tval : sig ... end
Module for optionally typed state expressions. Those are symbolic values which may or may not have a C type.
type tval
= Tval.t
module Mem : sig ... end
This module manages the memory part of the state.
type t
= private
{
id : id; | |
base_state : t option; | The immediate dominator state in the control flow graph |
mutable locked : bool; | Tells if the state is locked |
mutable regs : Tval.t Reg.Map.t; | The values and types of registers |
read_vars : Tval.t Utils.Vec.t; | The results of reads made since base state |
mutable asserts : exp list; | Only asserts since base_state |
mem : Mem.t; | |
elf : Elf.File.t option; | Optionally an ELF file, this may be used when running instructions on the state to provide more concrete values in certain case (like when reading from |
fenv : Fragment.env; | The memory type environment. See |
mutable last_pc : int; | The PC of the instruction that lead into this state. The state should be right after that instruction. This has no semantic meaning as part of the state. It's just for helping knowing what comes from where |
}
Represent the state of the machine.
State are represented by their id and may identified to their id, they may not be two different state (and I mean physical equality here) with same id. See State to id management.
A first remark must be made about mutability: The type itself has an imperative interface, a lot of implicitly or explicitly mutable fields. However, Sometime immutable version of the state are required, so the state has a "locking" mechanism. When the locked
field, the state becomes immutable. This is unfortunately not enforced by the type system as that would require to have two different types. However all mutating functions assert that the state is unlocked before doing the mutation. The normal workflow with state is thus to create them unlocked, generaly by copy
ing another state, then mutate is to make a new interesting state, and then lock it so that it can be passed around for it's mathematical pure meaning. To lock a state, use the lock
function.
A second subtlety is that state are not represented in a standalone manner, They are represented as diff from a previous state, the base_state
. In the idea, this state should be the immediate dominator of the current state in the control flow graph.However a state may not represent a full node of the control flow graph but only the part of that node that represent control-flow coming from specific paths. In that case the dominator notion is only about those paths.
Assertions (asserts
) and memory (mem
) are represented as diffs from the base state. In particular all assertion constraining the base state are still constraining the child state.
This also implies a restriction on state dependent variables like Var.t.Register
. The id of such variables can only be the id of the current state or one of it's ancestor. Further more if a variable of type Var.t.ReadVar
exists with and id and a number, then the read_vars
array of the state with that id
must contain that number and the sizes must match. Those restriction are not only about semantic meaning but also more practical Ocaml Gc consideration, see State to id management.
Each state has an id and the state can be refereed physically by id. This mean that there cannot be two different physical Ocaml state in the Gc memory that have the same id. Furthermore the id2state
map is weak and so do not own the state. This means that possession of an id is the same as having a weak pointer to the state, except for two things:
This is in particular useful for variable that contain and id instead of a pointer to the state:
Var.t
type can be defined before the t
type.That means that in theory a state could be Garbage collected while a variable still point to it. However a variable is only allowed to exists in a state if the id it points to is among the ancestors via the base_state
relationship of the containing state. Since base_state
is an GC-owning pointer, this ensure that while the containing state is alive, the variable target state is also alive.
val id2state : (id, t) Utils.WeakMap.t
Global map of states to associate them with identifiers
val next_id : id Stdlib.ref
Next unused id
val lock : t -> unit
Lock the state. Once a state is locked it should not be mutated anymore
val unsafe_unlock : t -> unit
Unlock the state. This is dangerous, do not use if you do not know how to use it, The only realistic use case, is for calling a simplifier and thus not changing the semantic meaning of the state in any way while mutating it.
This is deprecated and should disappear at some point.
val is_locked : t -> bool
Tell if the state is locked, in which case it shouldn't be mutated
val is_possible : t -> bool
Tell is state is possible.
A state is impossible if it has a single assert that is false
. This means that this symbolic state represent the empty set of concrete states.
StateSimplify
.ctxfull will call the SMT solver and set the assertions to that if required so you should call that function before is_possible
val make : ?elf:Elf.File.t -> unit -> t
Makes a fresh state with all variable fresh and new. This fresh state is unlocked.
This should only be used by Init
, all other state should be derived from Init
.state.
val copy : ?elf:Elf.File.t -> t -> t
Do a deep copy of all the mutable part of the state, so it can be mutated without retro-action.
If the source state is locked, then new state is based on it (in the sense of t.base_state
), otherwise it is a literal copy of each field.
The returned state is always unlocked
val copy_if_locked : ?elf:Elf.File.t -> t -> t
Copy the state with copy
if and only if it is locked. The returned state is always unlocked
val set_impossible : t -> unit
Set a state to be impossible (single false
assert).
val make_read : t -> ?ctyp:Ctype.t -> Ast.Size.t -> var
Create a new Var.t.ReadVar
by mutating the state
val set_read : t -> int -> exp -> unit
Set a Var.t.ReadVar
to a specific value in t.read_vars
val read_from_rodata : t -> addr:exp -> size:Ast.Size.t -> exp option
Read memory from rodata
val read : provenance:Ctype.provenance -> ?ctyp:Ctype.t -> t -> addr:exp -> size:Ast.Size.t -> exp
Read the block designated by addr
and size
from the state and return an expression read. This will mutate the state to bind the read result to the newly created read variable.
The ctyp
parameter may give a type to the read variable. This type is fully trusted and not checked in any way.
The expression could be either:
This function is for case with provenance
information is known.
val read_noprov : ?ctyp:Ctype.t -> t -> addr:exp -> size:Ast.Size.t -> exp
A wrapper around read
for use when there is no provenance information. It may able to still perform the read under certain condition and otherwise will fail.
val write : provenance:Ctype.provenance -> t -> addr:exp -> size:Ast.Size.t -> exp -> unit
Write the provided value in the block. Mutate the state.
val write_noprov : t -> addr:exp -> size:Ast.Size.t -> exp -> unit
A wrapper around write
for use when there is no provenance information. It may able to still perform the write under certain condition and otherwise will fail.
val reset_reg : t -> ?ctyp:Ctype.t -> Reg.t -> unit
Reset the register to a symbolic value, and resets the type to the provided type (or no type if not provided)
val set_pc : pc:Reg.t -> t -> int -> unit
Set the PC to a concrete value and keep its type appropriate
val bump_pc : pc:Reg.t -> t -> int -> unit
Bump a concrete PC by a concrete bump (generally the size of a non-branching instruction
val set_last_pc : t -> int -> unit
Set the last_pc
of the state
State.Base
This module introduce a type to represent the state of the machine.
The symbolic state in this module do not mathematically represent a single state but a set of concrete state with all the symbolic variable over the whole range of their types. State also contain assertions, and so only represent the subset of concrete states, that satisfy all assertions.
Currently the state type only represent the register (including system register) and sequential memory part of an actual machine state. Any other architectural state is not represented.
Additionally, state contain C type information for the Ctype
inference system. Those fields are not semantically part of the state and do not influence in any way which concrete states are represented by the symbolic state. Expect for provenance information: Pointers can be tagged with provenance information which mean that they are part of a specific restricted block of memory or the main block. See Mem
for more information. Thus all the implicit non-aliasing assertion implied by those provenance field are to be considered as part of the group of assertion restricting the set of concrete state represented by a symbolic state.
The presence of C types is optional, which means that this state type can be used in a completely untyped context.
Concrete detail about how symbolic state are represented in Ocaml is in the documentation of t
.
module Id : sig ... end
The type of a state ID. for now it's an integer, but it may change later In particular whether a state belong to O0 or O2 may be part of the id
at some point.
type id = Id.t
module Var : sig ... end
This module provide state variables. Those are all symbolic variables that may appear in a state. If the same (in the Var.equal
sense) variable appear in two state, that mean that when considered together, there is an implicit relation between them.
type var = Var.t
The type of variables
module Exp : sig ... end
Module for state expressions
type exp = Exp.t
module Tval : sig ... end
Module for optionally typed state expressions. Those are symbolic values which may or may not have a C type.
type tval = Tval.t
module Relocation : sig ... end
module Mem : sig ... end
This module manages the memory part of the state.
type t = private {
id : id;
base_state : t option;
The immediate dominator state in the control flow graph
*)mutable locked : bool;
Tells if the state is locked
*)mutable regs : Tval.t Reg.Map.t;
The values and types of registers
*)read_vars : Tval.t Utils.Vec.t;
The results of reads made since base state
*)mutable asserts : exp list;
Only asserts since base_state
*)mutable relocation_asserts : exp list;
Only asserts since base_state
*)mem : Mem.t;
elf : Elf.File.t option;
Optionally an ELF file, this may be used when running instructions on the state to provide more concrete values in certain case (like when reading from .rodata
). It will affect the execution behavior. However the symbolic execution should always be more concrete with it than without it
fenv : Fragment.env;
mutable last_pc : Elf.Address.t;
The PC of the instruction that lead into this state. The state should be right after that instruction. This has no semantic meaning as part of the state. It's just for helping knowing what comes from where
*)}
Represent the state of the machine.
State are represented by their id and may identified to their id, they may not be two different state (and I mean physical equality here) with same id. See State to id management.
A first remark must be made about mutability: The type itself has an imperative interface, a lot of implicitly or explicitly mutable fields. However, Sometime immutable version of the state are required, so the state has a "locking" mechanism. When the locked
field, the state becomes immutable. This is unfortunately not enforced by the type system as that would require to have two different types. However all mutating functions assert that the state is unlocked before doing the mutation. The normal workflow with state is thus to create them unlocked, generaly by copy
ing another state, then mutate is to make a new interesting state, and then lock it so that it can be passed around for it's mathematical pure meaning. To lock a state, use the lock
function.
A second subtlety is that state are not represented in a standalone manner, They are represented as diff from a previous state, the base_state
. In the idea, this state should be the immediate dominator of the current state in the control flow graph.However a state may not represent a full node of the control flow graph but only the part of that node that represent control-flow coming from specific paths. In that case the dominator notion is only about those paths.
Assertions (asserts
) and memory (mem
) are represented as diffs from the base state. In particular all assertion constraining the base state are still constraining the child state.
This also implies a restriction on state dependent variables like Var.t.Register
. The id of such variables can only be the id of the current state or one of it's ancestor. Further more if a variable of type Var.t.ReadVar
exists with and id and a number, then the read_vars
array of the state with that id
must contain that number and the sizes must match. Those restriction are not only about semantic meaning but also more practical Ocaml Gc consideration, see State to id management.
Each state has an id and the state can be refereed physically by id. This mean that there cannot be two different physical Ocaml state in the Gc memory that have the same id. Furthermore the id2state
map is weak and so do not own the state. This means that possession of an id is the same as having a weak pointer to the state, except for two things:
This is in particular useful for variable that contain and id instead of a pointer to the state:
Var.t
type can be defined before the t
type.That means that in theory a state could be Garbage collected while a variable still point to it. However a variable is only allowed to exists in a state if the id it points to is among the ancestors via the base_state
relationship of the containing state. Since base_state
is an GC-owning pointer, this ensure that while the containing state is alive, the variable target state is also alive.
val id2state : (id, t) Utils.WeakMap.t
Global map of states to associate them with identifiers
val next_id : id Stdlib.ref
Next unused id
val lock : t -> unit
Lock the state. Once a state is locked it should not be mutated anymore
val unsafe_unlock : t -> unit
Unlock the state. This is dangerous, do not use if you do not know how to use it, The only realistic use case, is for calling a simplifier and thus not changing the semantic meaning of the state in any way while mutating it.
This is deprecated and should disappear at some point.
val is_locked : t -> bool
Tell if the state is locked, in which case it shouldn't be mutated
val is_possible : t -> bool
Tell is state is possible.
A state is impossible if it has a single assert that is false
. This means that this symbolic state represent the empty set of concrete states.
StateSimplify.ctxfull
will call the SMT solver and set the assertions to that if required so you should call that function before is_possible
val make : ?elf:Elf.File.t -> unit -> t
Makes a fresh state with all variable fresh and new. This fresh state is unlocked.
This should only be used by Init
, all other state should be derived from Init.state
.
val copy : ?elf:Elf.File.t -> t -> t
Do a deep copy of all the mutable part of the state, so it can be mutated without retro-action.
If the source state is locked, then new state is based on it (in the sense of t.base_state
), otherwise it is a literal copy of each field.
The returned state is always unlocked
val copy_if_locked : ?elf:Elf.File.t -> t -> t
Copy the state with copy
if and only if it is locked. The returned state is always unlocked
Assigns all sections with global objects to Main fragment
val set_impossible : t -> unit
Set a state to be impossible (single false
assert).
Map a function on all the expressions of a state by mutating. This function, must preserve the semantic meaning of expression (like a simplification function) otherwise state invariants may be broken.
val make_read : t -> ?ctyp:Ctype.t -> Ast.Size.t -> var
Create a new Var.t.ReadVar
by mutating the state
Set a Var.t.ReadVar
to a specific value in t.read_vars
val read_from_rodata : t -> addr:exp -> size:Ast.Size.t -> exp option
Read memory from rodata
val read :
+ provenance:Ctype.provenance ->
+ ?ctyp:Ctype.t ->
+ t ->
+ addr:exp ->
+ size:Ast.Size.t ->
+ exp
Read the block designated by addr
and size
from the state and return an expression read. This will mutate the state to bind the read result to the newly created read variable.
The ctyp
parameter may give a type to the read variable. This type is fully trusted and not checked in any way.
The expression could be either:
This function is for case with provenance
information is known.
val read_noprov : ?ctyp:Ctype.t -> t -> addr:exp -> size:Ast.Size.t -> exp
A wrapper around read
for use when there is no provenance information. It may able to still perform the read under certain condition and otherwise will fail.
val write :
+ provenance:Ctype.provenance ->
+ t ->
+ addr:exp ->
+ size:Ast.Size.t ->
+ exp ->
+ unit
Write the provided value in the block. Mutate the state.
val write_noprov : t -> addr:exp -> size:Ast.Size.t -> exp -> unit
A wrapper around write
for use when there is no provenance information. It may able to still perform the write under certain condition and otherwise will fail.
Reset the register to a symbolic value, and resets the type to the provided type (or no type if not provided)
Sets the type of the register, leaves the value unchanged
Apply a function to a register. Leave the type intact
Set the PC to a concrete value and keep its type appropriate
val set_pc_sym : pc:Reg.t -> t -> Elf.Address.t -> unit
Bump a concrete PC by a concrete bump (generally the size of a non-branching instruction
val set_last_pc : t -> Elf.Address.t -> unit
Set the last_pc
of the state
val pp : t -> Utils.Pp.document
val pp_partial : regs:Reg.t list -> t -> Utils.Pp.document
Print only the mentioned regs and the memory and asserts since the base_state. Until a better solution is found, the fenv will be printed entirely all the time
Fragment.Env
type frag
= t
type t
=
{
frags : frag Utils.Vec.t; |
}
val make : unit -> t
val copy : t -> t
val add_typ : addr:int -> Ctype.t -> t -> id:int -> unit
Add the provided type at addr
into a fragment indexed by id
in the environment
Fragment.Env
type frag = t
val make : unit -> t
Add the provided type at addr
into a fragment indexed by id
in the environment
Add a new fragment to the environment, returns the id of the new fragment. frag
is empty
by default.
val pp : t -> Utils.Pp.document
State.Fragment
This module define a memory fragment to be used by C types
obj
is just Ctype.t
, but a bug in odoc hides that information. Fragment
.obj actually do not exists.
The odoc PR is #349 on github, we just have to wait for it to be merged.
include Utils.RngMap.S with type obj := Ctype.t
type obj_off
= obj * int
The type of an object with an offset
type t
The type of the map from address ranges to obj
val empty : t
An empty RngMap
val is_in : objaddr:int -> obj -> int -> bool
Test if an address is inside the object at address objaddr
val at : t -> int -> obj
Get the object containing the address. Throw Not_found
if no object contains the address
val at_opt : t -> int -> obj option
Get the object containing the address. None
if no object contains the address
val at_off : t -> int -> obj_off
Get the object containing the address and the offset of the address inside the object
at_off map addr = (obj, off)
means:
| | | | - map 0 obj start point obj end - |<--------------addr-------------->| - |<---off--->| - |<------len obj------>|
In other words, at_off
allow a change of coordinate from the map frame to the object frame.
Throw Not_found
if no object contains the address
val at_off_opt : t -> int -> obj_off option
Get the object containing the address and the offset of the address inside the object. See at_off
for more explanation.
None
if no object contains the address
val update : (obj -> obj) -> t -> int -> t
Update the binding containing the provided address. If no binding contained the address, this is a no-op
val iteri : (int -> obj -> unit) -> t -> unit
Iter a function over all the objects with their address
val clear : t -> pos:int -> len:int -> t
Clear an area of the RngMap.
If an object is partially in the specified block. It will be removed entirely.
See clear_crop
for a different behavior. See clear_bounds
to allow some bounds to be infinity.
val clear_crop : t -> pos:int -> len:int -> crop:(pos:int -> len:int -> obj -> obj) -> t
Clear an area of the RngMap.
If a block is partially in the specified block, It will be cropped by using the provided crop function.
crop ~pos ~len obj
is supposed to crop the object obj
and keep only the segment [pos:pos +len)
of it (in the object coordinate frame).
val clear_bounds : ?start:int -> ?endp:int -> t -> t
Same as clear
but if a bound is missing, then we erase until infinity in that direction. The target interval is [start:endp)
.
In particular clear_bounds map =
empty
.
val add : t -> int -> obj -> t
Add an object at a specific address. The whole range of addresses covered by the object must be free
val addp : t -> obj_off -> t
Add an object at a specific address. The whole range of addresses covered by the object must be free
val to_seq : ?start:int -> ?endp:int -> t -> (int * obj) Utils.Seq.t
Return a sequence of all the object overlapping the range [start:endp)
. The first and last element may not be entierly contained in the ranged. If any bound is unspecified, it goes to infinity in that direction.
In particular to_seq map
will iterate the entiere RngMap
module Env : sig ... end
type env
= Env.t
State.Fragment
This module define a memory fragment to be used by C types
obj
is just Ctype.t
, but a bug in odoc hides that information. Fragment.obj
actually do not exists.
The odoc PR is #349 on github, we just have to wait for it to be merged.
include Utils.RngMap.S with type obj := Ctype.t
type obj_off = Ctype.t * int
The type of an object with an offset
val empty : t
An empty RngMap
val is_in : objaddr:int -> Ctype.t -> int -> bool
Test if an address is inside the object at address objaddr
Get the object containing the address. Throw Not_found
if no object contains the address
Get the object containing the address. None
if no object contains the address
Get the object containing the address and the offset of the address inside the object
at_off map addr = (obj, off)
means:
| | | | + map 0 obj start point obj end + |<--------------addr-------------->| + |<---off--->| + |<------len obj------>|
In other words, at_off
allow a change of coordinate from the map frame to the object frame.
Throw Not_found
if no object contains the address
Get the object containing the address and the offset of the address inside the object. See at_off
for more explanation.
None
if no object contains the address
Update the binding containing the provided address. If no binding contained the address, this is a no-op
Map a function over all the objects with their address
Iter a function over all the objects with their address
Clear an area of the RngMap.
If an object is partially in the specified block. It will be removed entirely.
See clear_crop
for a different behavior. See clear_bounds
to allow some bounds to be infinity.
val clear_crop :
+ t ->
+ pos:int ->
+ len:int ->
+ crop:(pos:int -> len:int -> Ctype.t -> Ctype.t) ->
+ t
Clear an area of the RngMap.
If a block is partially in the specified block, It will be cropped by using the provided crop function.
crop ~pos ~len obj
is supposed to crop the object obj
and keep only the segment [pos:pos +len)
of it (in the object coordinate frame).
Add an object at a specific address. The whole range of addresses covered by the object must be free
Add an object at a specific address. The whole range of addresses covered by the object must be free
val to_seq : ?start:int -> ?endp:int -> t -> (int * Ctype.t) Utils.Seq.t
Return a sequence of all the object overlapping the range [start:endp)
. The first and last element may not be entierly contained in the ranged. If any bound is unspecified, it goes to infinity in that direction.
In particular to_seq map
will iterate the entiere RngMap
val pp : t -> Utils.Pp.document
module Env : sig ... end
type env = Env.t
Reg.Map
This module provide a full map over register in the same way than FullVec
provide a map of integers. It still need a generator to generate the value bound to not-yet-added registers.
Because the domain of registers is finite, some extra function are available like iter
and iteri
that are not possible in FullVec
.
If a register is added with add
, it is automatically and implicitly added to the Utils
.Map and the generator must accept this new value. The generator will never be called on invalid register values (i.e. when the generator is called on a register, the former can get the latter's type and name with reg_type
and to_string
)
type reg
= t
type 'a t
The type of the complete map
val map : ('a -> 'b) -> 'a t -> 'b t
Map the function all the registers (including future, not yet added ones)
val map_mut : ('a -> 'a) -> 'a t -> unit
Map the function on all the register by mutation (including future ones)
val map_mut_current : ('a -> 'a) -> 'a t -> unit
Map the function on all current register. Future registers are unchanged
val iter : ('a -> unit) -> 'a t -> unit
Iterate over all the value of all currently present registers
Reg.Map
This module provide a full map over register in the same way than Utils.FullVec
provide a map of integers. It still need a generator to generate the value bound to not-yet-added registers.
Because the domain of registers is finite, some extra function are available like iter
and iteri
that are not possible in Utils.FullVec
.
If a register is added with add
, it is automatically and implicitly added to the Utils.Map
and the generator must accept this new value. The generator will never be called on invalid register values (i.e. when the generator is called on a register, the former can get the latter's type and name with reg_type
and to_string
)
type reg = t
Map the function all the registers (including future, not yet added ones)
val map_mut : ('a -> 'a) -> 'a t -> unit
Map the function on all the register by mutation (including future ones)
val map_mut_current : ('a -> 'a) -> 'a t -> unit
Map the function on all current register. Future registers are unchanged
val iter : ('a -> unit) -> 'a t -> unit
Iterate over all the value of all currently present registers
val pp : ('a -> Utils.Pp.document) -> 'a t -> Utils.Pp.document
Contrary to Utils.FullVector.pp
, this one will print the binding of all registers, and may call the generator to do that
Reg.Path
val to_string : t -> string
Print the path as dotted list of identifier: ["A";"B";"C"] -> "A.B.C"
val of_string : string -> t
Parse the path as dotted list of identifier: "A.B.C" -> ["A";"B";"C"]
Reg.Path
val to_string : t -> string
Print the path as dotted list of identifier: ["A";"B";"C"] -> "A.B.C"
val of_string : string -> t
Parse the path as dotted list of identifier: "A.B.C" -> ["A";"B";"C"]
val pp : t -> Utils.Pp.document
Pretty print the path
State.Reg
This module handle the register abstraction.
A register is defined by a Path
and a type ty
. The path is a representation of dot separated list of identifiers.
Registers are not part of the Arch
module because they are discovered dynamically. This module keeps a global index of all register of the current architecture (in a IdMap
). This map also fix the types of registers.
This allow to represent registers as integer everywhere.
The module also provides Map
and PMap
a respectively full and partial maps over registers. They need special support (especially the full map) because new registers may be added at any time after the creation of the map.
TODO: Support sail vectors (A path will be of type (string + int) list)
Representation of register path. The string reprensentation is with dots. A name may not contain dots, but this is not checked.
module Path : sig ... end
Global register properties and accessors
type t
= private int
The type representing a register. The module invariant is that this type always contains a value bound in the global index and so this is always a valid register id.
type ty
= Ast.no Ast.ty
The type of a register. This is isomorphic to Isla
.ty. Use IslaConv
.ty to convert
val of_int : int -> t option
Convert an integer into the corresponding register.
val mem_path : Path.t -> bool
Check if register is declared with that path
val mem_string : string -> bool
Check if a register is declared with that name. Same as Path.of_string |> mem_path
val of_string : string -> t
Give the register corresponding to a register name
val to_string : t -> string
Give the name of a register
val path_type : Path.t -> ty
Give the type of register path. Throw Not_found
, it that path is not a declared register
val ensure_add : Path.t -> ty -> t
Ensure that a register with that path exists with that type, by adding it or checking it already exists with that type. Return the corresponding register
val ensure_adds : Path.t -> ty -> unit
Ensure that a register with that path exists with that type, by adding it or checking it already exists with that type.
val pp_index : unit -> Utils.Pp.document
Prints the register index (mainly for debugging I suppose)
To achieve a partial map on register, one may just used a plain Hashtbl
. However as register is a finite type one may want to have a map where all the register are bound and thus access to a bound value cannot fail. This is complicated by the fact that new registers can be added after the creation of the map. To handle all those subtleties, there is the Utils
.Map module.
State.Reg
This module handle the register abstraction.
A register is defined by a Path
and a type ty
. The path is a representation of dot separated list of identifiers.
Registers are not part of the Arch
module because they are discovered dynamically. This module keeps a global index of all register of the current architecture (in a Utils.IdMap
). This map also fix the types of registers.
This allow to represent registers as integer everywhere.
The module also provides Map
and PMap
a respectively full and partial maps over registers. They need special support (especially the full map) because new registers may be added at any time after the creation of the map.
TODO: Support sail vectors (A path will be of type (string + int) list)
Representation of register path. The string reprensentation is with dots. A name may not contain dots, but this is not checked.
module Path : sig ... end
Global register properties and accessors
The type representing a register. The module invariant is that this type always contains a value bound in the global index and so this is always a valid register id.
The type of a register. This is isomorphic to Isla.ty
. Use IslaConv.ty
to convert
val of_int : int -> t option
Convert an integer into the corresponding register.
val mem_path : Path.t -> bool
Check if register is declared with that path
Check if a register is declared with that name. Same as Path.of_string |> mem_path
val of_string : string -> t
Give the register corresponding to a register name
val to_string : t -> string
Give the name of a register
Give the type of register path. Throw Not_found
, it that path is not a declared register
Ensure that a register with that path exists with that type, by adding it or checking it already exists with that type. Return the corresponding register
Ensure that a register with that path exists with that type, by adding it or checking it already exists with that type.
val seq_all : unit -> t Utils.Seq.t
Returns a sequence of all registers
val pp : t -> Utils.Pp.document
Pretty prints a register (Just use to_string
)
val pp_ty : ty -> Utils.Pp.document
Pretty prints a register type
val pp_index : unit -> Utils.Pp.document
Prints the register index (mainly for debugging I suppose)
To achieve a partial map on register, one may just used a plain Hashtbl
. However as register is a finite type one may want to have a map where all the register are bound and thus access to a bound value cannot fail. This is complicated by the fact that new registers can be added after the creation of the map. To handle all those subtleties, there is the Utils.Map
module.
module Map : sig ... end
This module provide a full map over register in the same way than Utils.FullVec
provide a map of integers. It still need a generator to generate the value bound to not-yet-added registers.
Simplify.ContextFull
Do a context aware simplify, for now, it just does a context free simplify and then remove assertion that proven true or false by Z3
val counter : Utils.Counter.t
val openc : unit -> unit
val num : unit -> int
val closec : unit -> unit
Simplify.ContextFull
Do a context aware simplify, for now, it just does a context free simplify and then remove assertion that proven true or false by Z3
Z3St.Htbl
type key
= var
type 'a t
= 'a Z3.Make(State__.Base.Var).Htbl.t
val create : int -> 'a t
val clear : 'a t -> unit
val reset : 'a t -> unit
val copy : 'a t -> 'a t
val add : 'a t -> key -> 'a -> unit
val remove : 'a t -> key -> unit
val find : 'a t -> key -> 'a
val find_opt : 'a t -> key -> 'a option
val find_all : 'a t -> key -> 'a list
val replace : 'a t -> key -> 'a -> unit
val mem : 'a t -> key -> bool
val iter : (key -> 'a -> unit) -> 'a t -> unit
val filter_map_inplace : (key -> 'a -> 'a option) -> 'a t -> unit
val fold : (key -> 'a -> 'b -> 'b) -> 'a t -> 'b -> 'b
val length : 'a t -> int
val stats : 'a t -> Stdlib.Hashtbl.statistics
val to_seq : 'a t -> (key * 'a) Stdlib.Seq.t
val to_seq_keys : 'a t -> key Stdlib.Seq.t
val to_seq_values : 'a t -> 'a Stdlib.Seq.t
val add_seq : 'a t -> (key * 'a) Stdlib.Seq.t -> unit
val replace_seq : 'a t -> (key * 'a) Stdlib.Seq.t -> unit
val of_seq : (key * 'a) Stdlib.Seq.t -> 'a t
Z3St.Htbl
type key = var
val create : int -> 'a t
val clear : 'a t -> unit
val reset : 'a t -> unit
val length : 'a t -> int
val stats : 'a t -> Stdlib__Hashtbl.statistics
val to_seq_values : 'a t -> 'a Stdlib.Seq.t
Simplify.Z3St
type var
= Base.Var.t
type exp
= (var, Ast.no) Exp.Typed.t
module Htbl : sig ... end
val declare_var_always : Z3.server -> var -> unit
val declare_var : Z3.server -> declared:unit Htbl.t -> var -> unit
val declare_vars : Z3.server -> declared:unit Htbl.t -> exp -> unit
val simplify : Z3.server -> exp -> exp
val simplify_decl : Z3.server -> declared:unit Htbl.t -> exp -> exp
val send_assert : Z3.server -> exp -> unit
val send_assert_decl : Z3.server -> declared:unit Htbl.t -> exp -> unit
val check : Z3.server -> exp -> bool option
val check_sat : Z3.server -> exp -> bool option
val check_both : Z3.server -> exp -> bool option
val simplify_full : exp -> exp
val check_full : ?hyps:exp list -> exp -> bool option
val check_sat_full : exp list -> bool option
Simplify.Z3St
type var = Base.Var.t
type exp = (var, Ast.no) Exp.Typed.t
module Htbl : sig ... end
val check_sat_full : exp list -> bool option
State.Simplify
This module provide utility to simplify states
module Z3St : sig ... end
val ctxfree : Base.t -> unit
Context free simplify, just expression by expression. Do it mutably
module ContextFull : sig ... end
Do a context aware simplify, for now, it just does a context free simplify and then remove assertion that proven true or false by Z3
val ctxfull : Base.t -> unit
Do a context aware simplify, for now, it just does a context free simplify and then remove assertion that proven true or false by Z3.
If a state has a false assertion anywhere then all assertions will collapse in a single false.
State.Simplify
This module provide utility to simplify states
module Z3St : sig ... end
val ctxfree : Base.t -> unit
Context free simplify, just expression by expression. Do it mutably
module ContextFull : sig ... end
Do a context aware simplify, for now, it just does a context free simplify and then remove assertion that proven true or false by Z3
val ctxfull : Base.t -> unit
Do a context aware simplify, for now, it just does a context free simplify and then remove assertion that proven true or false by Z3.
If a state has a false assertion anywhere then all assertions will collapse in a single false.
Make.1-Var
Make.Var
val pp : t -> Utils.Pp.document
Pretty printer to be used, both for memory pretty printing and for sending memory to Z3
SymbolicBytes.Make
type var
= Var.t
The type of variables used
type exp
= (var, Ast.no) Exp.Typed.t
type t
The type of symbolic bytes.
val empty : t
The empty symbolic byte sequence with no bytes defined
val sub : pos:int -> len:int -> t -> exp option
Extract an expression in [pos:pos+len)
. If any bytes in the range is undefined, Then None
is returned, otherwise a expression is returned
val blit_exp : exp -> pos:int -> len:int -> t -> t
Write the expresion on the interval [pos:pos +len)
of the bytes. The expression must be a bitvector of size exactly 8 * len
.
TODO check it when values are type annotated
SymbolicBytes.Make
type var = Var.t
The type of variables used
type exp = (var, Ast.no) Exp.Typed.t
val empty : t
The empty symbolic byte sequence with no bytes defined
Extract an expression in [pos:pos+len)
. If any bytes in the range is undefined, Then None
is returned, otherwise a expression is returned
Write the expresion on the interval [pos:pos +len)
of the bytes. The expression must be a bitvector of size exactly 8 * len
.
TODO check it when values are type annotated
Clear a range of the symbolic bytes, making all those bytes undefined again. If a bound is missing, it means up to infinity in that direction
val pp : t -> Utils.Pp.document
Pretty prints the symbolic bytes
State.SymbolicBytes
This module provide high-level support for a symbolic array of bytes.
The main differences between this and a big concat
expression is that:
This data structure deliberately do not have any infrastructure to read or write bytes at symbolic positions. See SymbolicFragment
for that.
This data structure do not have a concept of beginning or an end (But it has the concept of first defined byte and last defined byte). In particular addresses can be negative.
It is functorized of the type of variables (Var
) to get variable equality and pretty-printing
Currently this has a pure interface.
module type S = sig ... end
State.SymbolicBytes
This module provide high-level support for a symbolic array of bytes.
The main differences between this and a big concat
expression is that:
This data structure deliberately do not have any infrastructure to read or write bytes at symbolic positions. See SymbolicFragment
for that.
This data structure do not have a concept of beginning or an end (But it has the concept of first defined byte and last defined byte). In particular addresses can be negative.
It is functorized of the type of variables (Var
) to get variable equality and pretty-printing
Currently this has a pure interface.
SymbolicBytes.S
type exp
= (var, Ast.no) Exp.Typed.t
type t
The type of symbolic bytes.
val empty : t
The empty symbolic byte sequence with no bytes defined
val sub : pos:int -> len:int -> t -> exp option
Extract an expression in [pos:pos+len)
. If any bytes in the range is undefined, Then None
is returned, otherwise a expression is returned
val blit_exp : exp -> pos:int -> len:int -> t -> t
Write the expresion on the interval [pos:pos +len)
of the bytes. The expression must be a bitvector of size exactly 8 * len
.
TODO check it when values are type annotated
SymbolicBytes.S
type exp = (var, Ast.no) Exp.Typed.t
val empty : t
The empty symbolic byte sequence with no bytes defined
Extract an expression in [pos:pos+len)
. If any bytes in the range is undefined, Then None
is returned, otherwise a expression is returned
Write the expresion on the interval [pos:pos +len)
of the bytes. The expression must be a bitvector of size exactly 8 * len
.
TODO check it when values are type annotated
Clear a range of the symbolic bytes, making all those bytes undefined again. If a bound is missing, it means up to infinity in that direction
val pp : t -> Utils.Pp.document
Pretty prints the symbolic bytes
State.Tree
This module provides a tree of state to represent an unmerged execution
val bars : string
val bars_length : int
val startbar : int -> Utils.Pp.document
val prefix_iter : ('a -> Base.t -> unit) -> 'a t -> unit
val postfix_iter : ('a -> Base.t -> unit) -> 'a t -> unit
val iter : ('a -> Base.t -> unit) -> 'a t -> unit
Default iter when you don't care about order
State.Tree
This module provides a tree of state to represent an unmerged execution
val startbar : int -> Utils.Pp.document
This is prefix, do a List.rev to get a postfix version
val pp : ('a -> Utils.Pp.document) -> 'a t -> Utils.Pp.document
val pp_all : ('a -> Utils.Pp.document) -> 'a t -> Utils.Pp.document
State
include Base
module Id = Base.Id
The type of a state ID. for now it's an integer, but it may change later In particular whether a state belong to O0 or O2 may be part of the id
at some point.
type id
= Id.t
module Var = Base.Var
This module provide state variables. Those are all symbolic variables that may appear in a state. If the same (in the Var.equal
sense) variable appear in two state, that mean that when considered together, there is an implicit relation between them.
type var
= Var.t
The type of variables
module Exp = Base.Exp
Module for state expressions
type exp
= Exp.t
module Tval = Base.Tval
Module for optionally typed state expressions. Those are symbolic values which may or may not have a C type.
type tval
= Tval.t
module Mem = Base.Mem
This module manages the memory part of the state.
type t
= private
{
id : id; | |
base_state : t option; | The immediate dominator state in the control flow graph |
mutable locked : bool; | Tells if the state is locked |
mutable regs : Tval.t Reg.Map.t; | The values and types of registers |
read_vars : Tval.t Utils.Vec.t; | The results of reads made since base state |
mutable asserts : exp list; | Only asserts since base_state |
mem : Mem.t; | |
elf : Elf.File.t option; | Optionally an ELF file, this may be used when running instructions on the state to provide more concrete values in certain case (like when reading from |
fenv : Fragment.env; | The memory type environment. See |
mutable last_pc : int; | The PC of the instruction that lead into this state. The state should be right after that instruction. This has no semantic meaning as part of the state. It's just for helping knowing what comes from where |
}
Represent the state of the machine.
State are represented by their id and may identified to their id, they may not be two different state (and I mean physical equality here) with same id. See State to id management.
A first remark must be made about mutability: The type itself has an imperative interface, a lot of implicitly or explicitly mutable fields. However, Sometime immutable version of the state are required, so the state has a "locking" mechanism. When the locked
field, the state becomes immutable. This is unfortunately not enforced by the type system as that would require to have two different types. However all mutating functions assert that the state is unlocked before doing the mutation. The normal workflow with state is thus to create them unlocked, generaly by copy
ing another state, then mutate is to make a new interesting state, and then lock it so that it can be passed around for it's mathematical pure meaning. To lock a state, use the lock
function.
A second subtlety is that state are not represented in a standalone manner, They are represented as diff from a previous state, the base_state
. In the idea, this state should be the immediate dominator of the current state in the control flow graph.However a state may not represent a full node of the control flow graph but only the part of that node that represent control-flow coming from specific paths. In that case the dominator notion is only about those paths.
Assertions (asserts
) and memory (mem
) are represented as diffs from the base state. In particular all assertion constraining the base state are still constraining the child state.
This also implies a restriction on state dependent variables like Var.t.Register
. The id of such variables can only be the id of the current state or one of it's ancestor. Further more if a variable of type Var.t.ReadVar
exists with and id and a number, then the read_vars
array of the state with that id
must contain that number and the sizes must match. Those restriction are not only about semantic meaning but also more practical Ocaml Gc consideration, see State to id management.
Each state has an id and the state can be refereed physically by id. This mean that there cannot be two different physical Ocaml state in the Gc memory that have the same id. Furthermore the id2state
map is weak and so do not own the state. This means that possession of an id is the same as having a weak pointer to the state, except for two things:
This is in particular useful for variable that contain and id instead of a pointer to the state:
Var.t
type can be defined before the t
type.That means that in theory a state could be Garbage collected while a variable still point to it. However a variable is only allowed to exists in a state if the id it points to is among the ancestors via the base_state
relationship of the containing state. Since base_state
is an GC-owning pointer, this ensure that while the containing state is alive, the variable target state is also alive.
val id2state : (id, t) Utils.WeakMap.t
Global map of states to associate them with identifiers
val next_id : id Stdlib.ref
Next unused id
val lock : t -> unit
Lock the state. Once a state is locked it should not be mutated anymore
val unsafe_unlock : t -> unit
Unlock the state. This is dangerous, do not use if you do not know how to use it, The only realistic use case, is for calling a simplifier and thus not changing the semantic meaning of the state in any way while mutating it.
This is deprecated and should disappear at some point.
val is_locked : t -> bool
Tell if the state is locked, in which case it shouldn't be mutated
val is_possible : t -> bool
Tell is state is possible.
A state is impossible if it has a single assert that is false
. This means that this symbolic state represent the empty set of concrete states.
StateSimplify
.ctxfull will call the SMT solver and set the assertions to that if required so you should call that function before is_possible
val make : ?elf:Elf.File.t -> unit -> t
Makes a fresh state with all variable fresh and new. This fresh state is unlocked.
This should only be used by Init
, all other state should be derived from Init
.state.
val copy : ?elf:Elf.File.t -> t -> t
Do a deep copy of all the mutable part of the state, so it can be mutated without retro-action.
If the source state is locked, then new state is based on it (in the sense of t.base_state
), otherwise it is a literal copy of each field.
The returned state is always unlocked
val copy_if_locked : ?elf:Elf.File.t -> t -> t
Copy the state with copy
if and only if it is locked. The returned state is always unlocked
val set_impossible : t -> unit
Set a state to be impossible (single false
assert).
val make_read : t -> ?ctyp:Ctype.t -> Ast.Size.t -> var
Create a new Var.t.ReadVar
by mutating the state
val set_read : t -> int -> exp -> unit
Set a Var.t.ReadVar
to a specific value in t.read_vars
val read_from_rodata : t -> addr:exp -> size:Ast.Size.t -> exp option
Read memory from rodata
val read : provenance:Ctype.provenance -> ?ctyp:Ctype.t -> t -> addr:exp -> size:Ast.Size.t -> exp
Read the block designated by addr
and size
from the state and return an expression read. This will mutate the state to bind the read result to the newly created read variable.
The ctyp
parameter may give a type to the read variable. This type is fully trusted and not checked in any way.
The expression could be either:
This function is for case with provenance
information is known.
val read_noprov : ?ctyp:Ctype.t -> t -> addr:exp -> size:Ast.Size.t -> exp
A wrapper around read
for use when there is no provenance information. It may able to still perform the read under certain condition and otherwise will fail.
val write : provenance:Ctype.provenance -> t -> addr:exp -> size:Ast.Size.t -> exp -> unit
Write the provided value in the block. Mutate the state.
val write_noprov : t -> addr:exp -> size:Ast.Size.t -> exp -> unit
A wrapper around write
for use when there is no provenance information. It may able to still perform the write under certain condition and otherwise will fail.
val reset_reg : t -> ?ctyp:Ctype.t -> Reg.t -> unit
Reset the register to a symbolic value, and resets the type to the provided type (or no type if not provided)
val set_pc : pc:Reg.t -> t -> int -> unit
Set the PC to a concrete value and keep its type appropriate
val bump_pc : pc:Reg.t -> t -> int -> unit
Bump a concrete PC by a concrete bump (generally the size of a non-branching instruction
val set_last_pc : t -> int -> unit
Set the last_pc
of the state
module Base : sig ... end
This module introduce a type to represent the state of the machine.
module Fragment : sig ... end
This module define a memory fragment to be used by C types
module Reg : sig ... end
This module handle the register abstraction.
module Simplify : sig ... end
This module provide utility to simplify states
module SymbolicBytes : sig ... end
This module provide high-level support for a symbolic array of bytes.
module SymbolicFragement = State__.SymbolicFragment
module Tree : sig ... end
This module provides a tree of state to represent an unmerged execution
State
include module type of struct include Base end
module Id = Base.Id
The type of a state ID. for now it's an integer, but it may change later In particular whether a state belong to O0 or O2 may be part of the id
at some point.
type id = Id.t
module Var = Base.Var
This module provide state variables. Those are all symbolic variables that may appear in a state. If the same (in the Var.equal
sense) variable appear in two state, that mean that when considered together, there is an implicit relation between them.
type var = Var.t
The type of variables
module Exp = Base.Exp
Module for state expressions
type exp = Exp.t
module Tval = Base.Tval
Module for optionally typed state expressions. Those are symbolic values which may or may not have a C type.
type tval = Tval.t
module Relocation = Base.Relocation
module Mem = Base.Mem
This module manages the memory part of the state.
type t = private Base.t = {
id : id;
base_state : t option;
The immediate dominator state in the control flow graph
*)mutable locked : bool;
Tells if the state is locked
*)mutable regs : Tval.t Reg.Map.t;
The values and types of registers
*)read_vars : Tval.t Utils.Vec.t;
The results of reads made since base state
*)mutable asserts : exp list;
Only asserts since base_state
*)mutable relocation_asserts : exp list;
Only asserts since base_state
*)mem : Mem.t;
elf : Elf.File.t option;
Optionally an ELF file, this may be used when running instructions on the state to provide more concrete values in certain case (like when reading from .rodata
). It will affect the execution behavior. However the symbolic execution should always be more concrete with it than without it
fenv : Fragment.env;
mutable last_pc : Elf.Address.t;
The PC of the instruction that lead into this state. The state should be right after that instruction. This has no semantic meaning as part of the state. It's just for helping knowing what comes from where
*)}
Represent the state of the machine.
State are represented by their id and may identified to their id, they may not be two different state (and I mean physical equality here) with same id. See State to id management.
A first remark must be made about mutability: The type itself has an imperative interface, a lot of implicitly or explicitly mutable fields. However, Sometime immutable version of the state are required, so the state has a "locking" mechanism. When the locked
field, the state becomes immutable. This is unfortunately not enforced by the type system as that would require to have two different types. However all mutating functions assert that the state is unlocked before doing the mutation. The normal workflow with state is thus to create them unlocked, generaly by copy
ing another state, then mutate is to make a new interesting state, and then lock it so that it can be passed around for it's mathematical pure meaning. To lock a state, use the lock
function.
A second subtlety is that state are not represented in a standalone manner, They are represented as diff from a previous state, the base_state
. In the idea, this state should be the immediate dominator of the current state in the control flow graph.However a state may not represent a full node of the control flow graph but only the part of that node that represent control-flow coming from specific paths. In that case the dominator notion is only about those paths.
Assertions (asserts
) and memory (mem
) are represented as diffs from the base state. In particular all assertion constraining the base state are still constraining the child state.
This also implies a restriction on state dependent variables like Var.t.Register
. The id of such variables can only be the id of the current state or one of it's ancestor. Further more if a variable of type Var.t.ReadVar
exists with and id and a number, then the read_vars
array of the state with that id
must contain that number and the sizes must match. Those restriction are not only about semantic meaning but also more practical Ocaml Gc consideration, see State to id management.
Each state has an id and the state can be refereed physically by id. This mean that there cannot be two different physical Ocaml state in the Gc memory that have the same id. Furthermore the id2state
map is weak and so do not own the state. This means that possession of an id is the same as having a weak pointer to the state, except for two things:
This is in particular useful for variable that contain and id instead of a pointer to the state:
Var.t
type can be defined before the t
type.That means that in theory a state could be Garbage collected while a variable still point to it. However a variable is only allowed to exists in a state if the id it points to is among the ancestors via the base_state
relationship of the containing state. Since base_state
is an GC-owning pointer, this ensure that while the containing state is alive, the variable target state is also alive.
val id2state : (id, t) Utils.WeakMap.t
Global map of states to associate them with identifiers
val next_id : id Stdlib.ref
Next unused id
val lock : t -> unit
Lock the state. Once a state is locked it should not be mutated anymore
val unsafe_unlock : t -> unit
Unlock the state. This is dangerous, do not use if you do not know how to use it, The only realistic use case, is for calling a simplifier and thus not changing the semantic meaning of the state in any way while mutating it.
This is deprecated and should disappear at some point.
val is_locked : t -> bool
Tell if the state is locked, in which case it shouldn't be mutated
val is_possible : t -> bool
Tell is state is possible.
A state is impossible if it has a single assert that is false
. This means that this symbolic state represent the empty set of concrete states.
StateSimplify.ctxfull
will call the SMT solver and set the assertions to that if required so you should call that function before is_possible
val make : ?elf:Elf.File.t -> unit -> t
Makes a fresh state with all variable fresh and new. This fresh state is unlocked.
This should only be used by Init
, all other state should be derived from Init.state
.
val copy : ?elf:Elf.File.t -> t -> t
Do a deep copy of all the mutable part of the state, so it can be mutated without retro-action.
If the source state is locked, then new state is based on it (in the sense of t.base_state
), otherwise it is a literal copy of each field.
The returned state is always unlocked
val copy_if_locked : ?elf:Elf.File.t -> t -> t
Copy the state with copy
if and only if it is locked. The returned state is always unlocked
Assigns all sections with global objects to Main fragment
val set_impossible : t -> unit
Set a state to be impossible (single false
assert).
Map a function on all the expressions of a state by mutating. This function, must preserve the semantic meaning of expression (like a simplification function) otherwise state invariants may be broken.
val make_read : t -> ?ctyp:Ctype.t -> Ast.Size.t -> var
Create a new Var.t.ReadVar
by mutating the state
Set a Var.t.ReadVar
to a specific value in t.read_vars
val read_from_rodata : t -> addr:exp -> size:Ast.Size.t -> exp option
Read memory from rodata
val read :
+ provenance:Ctype.provenance ->
+ ?ctyp:Ctype.t ->
+ t ->
+ addr:exp ->
+ size:Ast.Size.t ->
+ exp
Read the block designated by addr
and size
from the state and return an expression read. This will mutate the state to bind the read result to the newly created read variable.
The ctyp
parameter may give a type to the read variable. This type is fully trusted and not checked in any way.
The expression could be either:
This function is for case with provenance
information is known.
val read_noprov : ?ctyp:Ctype.t -> t -> addr:exp -> size:Ast.Size.t -> exp
A wrapper around read
for use when there is no provenance information. It may able to still perform the read under certain condition and otherwise will fail.
val write :
+ provenance:Ctype.provenance ->
+ t ->
+ addr:exp ->
+ size:Ast.Size.t ->
+ exp ->
+ unit
Write the provided value in the block. Mutate the state.
val write_noprov : t -> addr:exp -> size:Ast.Size.t -> exp -> unit
A wrapper around write
for use when there is no provenance information. It may able to still perform the write under certain condition and otherwise will fail.
Reset the register to a symbolic value, and resets the type to the provided type (or no type if not provided)
Sets the type of the register, leaves the value unchanged
Apply a function to a register. Leave the type intact
Set the PC to a concrete value and keep its type appropriate
val set_pc_sym : pc:Reg.t -> t -> Elf.Address.t -> unit
Bump a concrete PC by a concrete bump (generally the size of a non-branching instruction
val set_last_pc : t -> Elf.Address.t -> unit
Set the last_pc
of the state
val pp : t -> Utils.Pp.document
val pp_partial : regs:Reg.t list -> t -> Utils.Pp.document
Print only the mentioned regs and the memory and asserts since the base_state. Until a better solution is found, the fenv will be printed entirely all the time
module Base : sig ... end
This module introduce a type to represent the state of the machine.
module Fragment : sig ... end
This module define a memory fragment to be used by C types
module Reg : sig ... end
This module handle the register abstraction.
module Simplify : sig ... end
This module provide utility to simplify states
module SymbolicBytes : sig ... end
This module provide high-level support for a symbolic array of bytes.
module SymbolicFragement : sig ... end
This module provides a representation of symbolic memory as both a trace and a caching mechanism That is able to fetch some actual read value when there is no risk of aliasing.
module Tree : sig ... end
This module provides a tree of state to represent an unmerged execution
This page decribe the general method for symbolically running code in read-dwarf
.
To run instructions on a state, you must first get the instruction semantics trough the Isla pipeline. Then you can run trace individually using Trace.Run
module or do it all automatically using the Run.Runner
.
To run entire block of instruction there is the legacy Run.BB
to run a branchless and jumpless basic block and Run.Block
to run a complete block of code with control-flow. Run.Block
will output a tree of the possibilities, but is still quite basic. There is no need for fancier generic way of running block of instruction as the actual order of running thing will be choosen by the simulation finding code that is not yet written.
This page decribe the general method for symbolically running code in read-dwarf
.
To run instructions on a state, you must first get the instruction semantics trough the Isla pipeline. Then you can run trace individually using Trace.Run
module or do it all automatically using the Run.Runner
.
To run entire block of instruction there is the legacy Run.BB
to run a branchless and jumpless basic block and Run.Block
to run a complete block of code with control-flow. Run.Block
will output a tree of the possibilities, but is still quite basic. There is no need for fancier generic way of running block of instruction as the actual order of running thing will be choosen by the simulation finding code that is not yet written.
The internal expression syntax is derived from SMT-LIB with the bitvector, boolean, and enumeration theory. The enumeration theory is about types that contain a specific number of numbered item. All the symbolic execution system is based of those expressions.
Optionally, an expression can support the whole memory through the SMT array theory from addresses to bytes or words. This support is optional, see Expression type parameter and options
If you only wish to use those expression, I recommend you directly use the Ast
module. This module export Ast.exp
as well as parsing and pretty printing capabilities.
The Ast
module also define the syntax for SMT-LIB commands (Ast.smt
), and answers (Ast.smt_ans
).
The expressions types are build by ott
. Currently, to avoid repetition, part of the ott
AST comes from isla_lang.ott
and only the difference with isla-lang
are coming from the main ast.ott
file. Only specific parts of isla_lang.ott
are extracted with extract_section.awk
before merging with ast.ott
. The result of the call to ott
are:
AstGen.Ott
that defined the ast typesAstGen.Parser
The menhir parser moduleAstGen.Parser_pp
The pprint pretty-printing moduleAstGen.Lexer
The ocamllex lexer moduleOne thing to be aware of is the dependency chain: first AstGen.Ott
is defined, then AstGen.Def
(which is not generated) defines dome extra type definition to be used, then all the parsing and pretty printing module depend on AstGen.Def
and then Ast
encapsulates all of that for the rest of the codebase.
Unless you are tweaking things inside the AST, you should only use the Ast
module.
The type of expression (Ast.exp
) is parametric and has currently 4 parameters that are propagated to other Ast
types. Some Ast
types only have a subset of those parameters when it make sense. It would be good to respect the variable letter names throughout the codebase to keep it consistent and understandable.
'a
: Annotation type: The annotation is present on every expression constructor, and can be extracted with Ast.Manip
.annot'v
: Variable type: The type of symbolic variables.'b
: Bound variable type: The expression can optionally contain let bindings and bound variables with the usual semantics. This feature of expression can be disabled by putting Ast.no
in that parameter slot, in which case all let-bindings constructors and bound variable (Let
and Bound
) are disabled.'m
: Memory operation: This is intended to be a boolean option: only Ast.Size.t
or Ast.no
. In the first case, expression are allowed to contain memory array type and contain constructors like memory select or store. In the second case, all those operations are disabled and it is known that an expression can only be a bitvector, a boolean or an enumeration. In particular the content of register should generally not contain memory-enabled expressions.Expression coming out of the parser have their type parameter fixed to 'a=
Ast.lrng
, 'v=string
, 'b=string
, 'm=
Ast.Size.t
. Corresponding aliases of the various instanciation with those type are types prefixed by r
like Ast.rexp
, Ast.rsmt
, Ast.rty
, and Ast.rsmt_ans
.
The pretty-printer functions are a bit more tolerant in which type are allowed, but there still are some restrictions. Thus one may need to use some of the conversion function in next section before pretty-printing or after parsing.
Basic operation on expression like mapping/iterating over sub-expresions or variables is provided in Ast.Manip
. This module also provide conversion of type parameters (like changing the type of variables).
Internally, typed expression are used, which mean that the 'a
parameter of the expressions is actually their type of ocaml type Ast.ty
. The Exp.Typed
module provide smart constructors that allow to construct directly typed expressions. It also provide function to convert untyped expression to typed expressions. On top of that the Exp
module provides a functor to apply over a variable functor that allow to lift variable behavior like equality and pretty printing at the expression level.
Ast.Manip
only provide syntactic operation on expression, other modules provide semantic operations on expressions:
Exp.Sums
provide sum manipulation: allow to split and rebuild sum expression to/from list of terms.Exp.ConcreteEval
provides concrete evaluation of expressions. It returns values of type Exp.Value.t
which represent the possible concrete values that can result of an expression evaluation.Exp.PpExp
provide a more human readable expression printing than SMT-LIB syntax. Most operator are infix and have "usual" precedence. This module try to stay injective which means that a given pretty printed text represent a single possible syntactic expression.Z3
module allow interacting with Z3 for simplifying expression and checking SMT properties. If you use the high-level API of this module, you will not have to care about types like Ast.smt
or Ast.smt_ans
.In symbolic execution, one may need to represent large regions of memory in a symbolic manner. It would be possible to do that with a single expression of bitvector type with a very large size but this quickly become unwieldy. Furthermore in some case the machine code will perform writes at symbolic address which the position at which something is written in something else is symbolic. To represent this we use a two stage abstraction.
First, there is State.SymbolicBytes
which represent a block of memory which can contain arbitrary symbolic expressions at arbitrary but concrete addresses. Then State
.SymbolicFragment takes it one step further and provide a way to store arbitrary symbolic expressions at arbitrary symbolic addresses. When reading from a State
.SymbolicFragment, the read may not be resolved because of unknown aliasing of symbolic addresses. Those abstractions are only suitable to represent a sequential view of the memory without any concept of concurrent memory accesses.
The internal expression syntax is derived from SMT-LIB with the bitvector, boolean, and enumeration theory. The enumeration theory is about types that contain a specific number of numbered item. All the symbolic execution system is based of those expressions.
Optionally, an expression can support the whole memory through the SMT array theory from addresses to bytes or words. This support is optional, see Expression type parameter and options
If you only wish to use those expression, I recommend you directly use the Ast
module. This module export Ast.exp
as well as parsing and pretty printing capabilities.
The Ast
module also define the syntax for SMT-LIB commands (Ast.smt
), and answers (Ast.smt_ans
).
The expressions types are build by ott
. Currently, to avoid repetition, part of the ott
AST comes from isla_lang.ott
and only the difference with isla-lang
are coming from the main ast.ott
file. Only specific parts of isla_lang.ott
are extracted with extract_section.awk
before merging with ast.ott
. The result of the call to ott
are:
AstGen.Ott
that defined the ast typesAstGen.Parser
The menhir parser moduleAstGen.Parser_pp
The pprint pretty-printing moduleAstGen.Lexer
The ocamllex lexer moduleOne thing to be aware of is the dependency chain: first AstGen.Ott
is defined, then AstGen.Def
(which is not generated) defines dome extra type definition to be used, then all the parsing and pretty printing module depend on AstGen.Def
and then Ast
encapsulates all of that for the rest of the codebase.
Unless you are tweaking things inside the AST, you should only use the Ast
module.
The type of expression (Ast.exp
) is parametric and has currently 4 parameters that are propagated to other Ast
types. Some Ast
types only have a subset of those parameters when it make sense. It would be good to respect the variable letter names throughout the codebase to keep it consistent and understandable.
'a
: Annotation type: The annotation is present on every expression constructor, and can be extracted with Ast.Manip.annot
'v
: Variable type: The type of symbolic variables.'b
: Bound variable type: The expression can optionally contain let bindings and bound variables with the usual semantics. This feature of expression can be disabled by putting Ast.no
in that parameter slot, in which case all let-bindings constructors and bound variable (Let
and Bound
) are disabled.'m
: Memory operation: This is intended to be a boolean option: only Ast.Size.t
or Ast.no
. In the first case, expression are allowed to contain memory array type and contain constructors like memory select or store. In the second case, all those operations are disabled and it is known that an expression can only be a bitvector, a boolean or an enumeration. In particular the content of register should generally not contain memory-enabled expressions.Expression coming out of the parser have their type parameter fixed to 'a=
Ast.lrng
, 'v=string
, 'b=string
, 'm=
Ast.Size.t
. Corresponding aliases of the various instanciation with those type are types prefixed by r
like Ast.rexp
, Ast.rsmt
, Ast.rty
, and Ast.rsmt_ans
.
The pretty-printer functions are a bit more tolerant in which type are allowed, but there still are some restrictions. Thus one may need to use some of the conversion function in next section before pretty-printing or after parsing.
Basic operation on expression like mapping/iterating over sub-expresions or variables is provided in Ast.Manip
. This module also provide conversion of type parameters (like changing the type of variables).
Internally, typed expression are used, which mean that the 'a
parameter of the expressions is actually their type of ocaml type Ast.ty
. The Exp.Typed
module provide smart constructors that allow to construct directly typed expressions. It also provide function to convert untyped expression to typed expressions. On top of that the Exp
module provides a functor to apply over a variable functor that allow to lift variable behavior like equality and pretty printing at the expression level.
Ast.Manip
only provide syntactic operation on expression, other modules provide semantic operations on expressions:
Exp.Sums
provide sum manipulation: allow to split and rebuild sum expression to/from list of terms.Exp.ConcreteEval
provides concrete evaluation of expressions. It returns values of type Exp.Value.t
which represent the possible concrete values that can result of an expression evaluation.Exp.PpExp
provide a more human readable expression printing than SMT-LIB syntax. Most operator are infix and have "usual" precedence. This module try to stay injective which means that a given pretty printed text represent a single possible syntactic expression.Z3
module allow interacting with Z3 for simplifying expression and checking SMT properties. If you use the high-level API of this module, you will not have to care about types like Ast.smt
or Ast.smt_ans
.In symbolic execution, one may need to represent large regions of memory in a symbolic manner. It would be possible to do that with a single expression of bitvector type with a very large size but this quickly become unwieldy. Furthermore in some case the machine code will perform writes at symbolic address which the position at which something is written in something else is symbolic. To represent this we use a two stage abstraction.
First, there is State.SymbolicBytes
which represent a block of memory which can contain arbitrary symbolic expressions at arbitrary but concrete addresses. Then State.SymbolicFragment
takes it one step further and provide a way to store arbitrary symbolic expressions at arbitrary symbolic addresses. When reading from a State.SymbolicFragment
, the read may not be resolved because of unknown aliasing of symbolic addresses. Those abstractions are only suitable to represent a sequential view of the memory without any concept of concurrent memory accesses.
Tests.BytesSeqT
val has_even_len : string -> bool
val hex_digit : char Tests.Common.Q.arbitrary
val hex_string : string Tests.Common.Q.arbitrary
val is_hex : char -> bool
val well_formed : Tests.Common.QCT.t
val odd_len : Tests.Common.QCT.t
val not_hex : Tests.Common.QCT.t
val tests : Tests.Common.QCT.t list
Tests.BytesSeqT
Common.Gen
include Q.Gen
val return : 'a -> 'a t
val pure : 'a -> 'a t
val (>>=) : 'a t -> ('a -> 'b t) -> 'b t
val (<*>) : ('a -> 'b) t -> 'a t -> 'b t
val map : ('a -> 'b) -> 'a t -> 'b t
val map2 : ('a -> 'b -> 'c) -> 'a t -> 'b t -> 'c t
val map3 : ('a -> 'b -> 'c -> 'd) -> 'a t -> 'b t -> 'c t -> 'd t
val map_keep_input : ('a -> 'b) -> 'a t -> ('a * 'b) t
val (>|=) : 'a t -> ('a -> 'b) -> 'b t
val (<$>) : ('a -> 'b) -> 'a t -> 'b t
val oneof : 'a t list -> 'a t
val oneofl : 'a list -> 'a t
val oneofa : 'a array -> 'a t
val frequency : (int * 'a t) list -> 'a t
val frequencyl : (int * 'a) list -> 'a t
val frequencya : (int * 'a) array -> 'a t
val shuffle_a : 'a array -> unit t
val shuffle_l : 'a list -> 'a list t
val shuffle_w_l : (int * 'a) list -> 'a list t
val unit : unit t
val bool : bool t
val float : float t
val pfloat : float t
val nfloat : float t
val float_bound_inclusive : float -> float t
val float_bound_exclusive : float -> float t
val float_range : float -> float -> float t
val (--.) : float -> float -> float t
val nat : int t
val big_nat : int t
val neg_int : int t
val pint : int t
val int : int t
val small_nat : int t
val small_int : int t
val small_signed_int : int t
val int_bound : int -> int t
val int_range : int -> int -> int t
val graft_corners : 'a t -> 'a list -> unit -> 'a t
val int_pos_corners : int list
val int_corners : int list
val (--) : int -> int -> int t
val ui32 : int32 t
val ui64 : int64 t
val list : 'a t -> 'a list t
val list_size : int t -> 'a t -> 'a list t
val list_repeat : int -> 'a t -> 'a list t
val array : 'a t -> 'a array t
val array_size : int t -> 'a t -> 'a array t
val array_repeat : int -> 'a t -> 'a array t
val opt : 'a t -> 'a option t
val pair : 'a t -> 'b t -> ('a * 'b) t
val triple : 'a t -> 'b t -> 'c t -> ('a * 'b * 'c) t
val quad : 'a t -> 'b t -> 'c t -> 'd t -> ('a * 'b * 'c * 'd) t
val char : char t
val printable : char t
val numeral : char t
val char_range : char -> char -> char t
val string_size : ?gen:char t -> int t -> string t
val string : ?gen:char t -> string t
val string_of : char t -> string t
val string_readable : string t
val small_string : ?gen:char t -> string t
val small_list : 'a t -> 'a list t
val flatten_l : 'a t list -> 'a list t
val flatten_a : 'a t array -> 'a array t
val flatten_opt : 'a t option -> 'a option t
val flatten_res : ('a t, 'e) Stdlib.result -> ('a, 'e) Stdlib.result t
val small_array : 'a t -> 'a array t
val join : 'a t t -> 'a t
val sized : 'a sized -> 'a t
val sized_size : int t -> 'a sized -> 'a t
val fix : (('a -> 'b t) -> 'a -> 'b t) -> 'a -> 'b t
val generate : ?rand:Stdlib.Random.State.t -> n:int -> 'a t -> 'a list
val generate1 : ?rand:Stdlib.Random.State.t -> 'a t -> 'a
Common.Gen
include module type of struct include Q.Gen end
val return : 'a -> 'a t
val pure : 'a -> 'a t
val oneofl : 'a list -> 'a t
val oneofa : 'a array -> 'a t
val frequencyl : (int * 'a) list -> 'a t
val frequencya : (int * 'a) array -> 'a t
val shuffle_a : 'a array -> unit t
val shuffle_l : 'a list -> 'a list t
val shuffle_w_l : (int * 'a) list -> 'a list t
val range_subset : size:int -> int -> int -> int array t
val array_subset : int -> 'a array -> 'a array t
val unit : unit t
val bool : bool t
val float : float t
val pfloat : float t
val nfloat : float t
val float_bound_inclusive : float -> float t
val float_bound_exclusive : float -> float t
val float_range : float -> float -> float t
val (--.) : float -> float -> float t
val exponential : float -> float t
val nat : int t
val big_nat : int t
val neg_int : int t
val pint : int t
val int : int t
val small_nat : int t
val small_int : int t
val small_signed_int : int t
val int_bound : int -> int t
val int_range : int -> int -> int t
val (--) : int -> int -> int t
val int32 : int32 t
val int64 : int64 t
val ui32 : int32 t
val ui64 : int64 t
val char : char t
val printable : char t
val numeral : char t
val char_range : char -> char -> char t
val bytes_printable : bytes t
val bytes_small : bytes t
val string_readable : string t
val string_printable : string t
val string_small : string t
val nat_split2 : int -> (int * int) t
val pos_split2 : int -> (int * int) t
val nat_split : size:int -> int -> int array t
val pos_split : size:int -> int -> int array t
val generate : ?rand:Stdlib.Random.State.t -> n:int -> 'a t -> 'a list
val generate1 : ?rand:Stdlib.Random.State.t -> 'a t -> 'a
Tests.Common
module Q = QCheck
This module provide all the common testing infrastructure. It is intended to be opened in all testing modules
module Gen : sig ... end
module QCT = Q.Test
module Print = Q.Print
Tests.Common
This module provide all the common testing infrastructure. It is intended to be opened in all testing modules
module Gen : sig ... end
Tests.ConcreteEvalT
module ConcreteEval = Exp.ConcreteEval
module Value = Exp.Value
module Typed = Exp.Typed
val const_exp_gen_ty : int -> Ast.no Ast.ty -> ('a, Ast.no) ExpGen.Gen.exp Tests.Common.Gen.t
val const_exp_gen : int -> ('a, Ast.no) ExpGen.Gen.exp Tests.Common.Gen.t
val const_exp_shrinker_top : ('a, 'b, 'c, 'd) Ast.Base.exp -> (('a, 'b, 'c, 'd) Ast.Base.exp -> unit) -> unit
Shrink by trying all sub expressions
val const_exp_shrinker_bot : ('a, 'b, Ast.no, Ast.no) Ast.exp -> (('c, 'd) Exp.Typed.t -> unit) -> unit
Shrink by replacing a non-atomic constant expression by it's constEval evaluation
val const_exp_shrinker : (Ast.no Ast.ty, 'a, Ast.no, Ast.no) Ast.Base.exp -> ((Ast.no Ast.ty, 'a, Ast.no, Ast.no) Ast.exp -> unit) -> unit
Shrink by using both const_exp_shrinker_top
and const_exp_shrinker_bot
val const_exp : ExpGen.ExpT.t Tests.Common.Q.arbitrary
val concrete_eval : Tests.Common.QCT.t
val tests : Tests.Common.QCT.t list
Tests.ConcreteEvalT
This module is for testing ConcreteEval
module ConcreteEval = Exp.ConcreteEval
module Value = Exp.Value
module Typed = Exp.Typed
val const_exp_gen_ty :
+ int ->
+ Ast.no Ast.ty ->
+ ('a, Ast.no) ExpGen.Gen.exp Common.Gen.t
val const_exp_gen : int -> ('a, Ast.no) ExpGen.Gen.exp Common.Gen.t
val const_exp_shrinker_top :
+ ('a, 'b, 'c, 'd) Ast.Base.exp ->
+ (('a, 'b, 'c, 'd) Ast.Base.exp -> unit) ->
+ unit
Shrink by trying all sub expressions
val const_exp_shrinker_bot :
+ ('a, 'b, Ast.no, Ast.no) Ast.exp ->
+ (('c, 'd) Exp.Typed.t -> unit) ->
+ unit
Shrink by replacing a non-atomic constant expression by it's constEval evaluation
val const_exp_shrinker :
+ (Ast.no Ast.ty, 'a, Ast.no, Ast.no) Ast.Base.exp ->
+ (('a, Ast.no) Exp.Typed.t -> unit) ->
+ unit
Shrink by using both const_exp_shrinker_top
and const_exp_shrinker_bot
val const_exp : ExpGen.ExpT.t Tests.Common.Q.arbitrary
ExpGen.ExpT
type var
= Var.t
type t
= (var, Ast.no) Exp.Typed.t
ExpGen.ExpT
type var = Var.t
type t = (var, Ast.no) Exp.Typed.t
val pp : t -> Utils.Pp.document
val pp_smt : t -> Utils.Pp.document
ExpGen.Gen
type ('v, 'm) exp
= ('v, 'm) Exp.Typed.t
val typ : 'a Ast.ty Tests.Common.Gen.t
val var_from_ty : 'a -> (int * 'a) Tests.Common.Gen.t
val bv_var : int -> (int * 'a Ast.ty) Tests.Common.Gen.t
val bool_var : (int * 'a Ast.ty) Tests.Common.Gen.t
val bitvec_size : Utils.BitVec.t Tests.Common.Gen.sized
val bitvec : Utils.BitVec.t Tests.Common.Gen.t
type ('v, 'm) gen_param
=
{
typ : Ast.no Ast.ty; | Cannot contain an enum |
size : int; | |
bv_atom_gen : ('v, 'm) exp Tests.Common.Gen.sized; | |
bool_atom_gen : ('v, 'm) exp Tests.Common.Gen.t; |
}
val atom : params:('a, 'b) gen_param -> ('a, 'b) exp Tests.Common.Gen.t
Generate an atom according to the params
val unop : 'a Ast.ty -> (Ast.unop * 'b Ast.ty) Tests.Common.Gen.t
val binop : 'a Ast.ty -> ('b Ast.binop * 'c Ast.ty * 'c Ast.ty) Tests.Common.Gen.t
val manyop : 'a Ast.ty -> (Ast.manyop * 'b Ast.ty Utils.List.t) Tests.Common.Gen.t
val exp_from_params : ('a, Ast.no) gen_param -> ('a, Ast.no) exp Tests.Common.Gen.t
val bv_consts : int -> ('a, 'b) Exp.Typed.t Tests.Common.Gen.t
val bv_atom_from_var : 'v Tests.Common.Gen.sized -> int -> ('v, 'a) Exp.Typed.t Tests.Common.Gen.t
val bv_atom_with_var : int -> (int * 'a Ast.ty, 'b) Exp.Typed.t Tests.Common.Gen.t
val bool_consts : ('a, 'b) Exp.Typed.t Tests.Common.Gen.t
val bool_atom_from_var : 'v Tests.Common.Gen.t -> ('v, 'a) Exp.Typed.t Tests.Common.Gen.t
val bool_atom_with_var : (int * 'a Ast.ty, 'b) Exp.Typed.t Tests.Common.Gen.t
ExpGen.Gen
type ('v, 'm) exp = ('v, 'm) Exp.Typed.t
val typ : 'a Ast.ty Common.Gen.t
val var_from_ty : 'a -> (int * 'a) Common.Gen.t
val bv_var : int -> (int * 'a Ast.ty) Common.Gen.t
val bool_var : (int * 'a Ast.ty) Common.Gen.t
val bitvec_size : Utils.BitVec.t Common.Gen.sized
val bitvec : Utils.BitVec.t Common.Gen.t
type ('v, 'm) gen_param = {
typ : Ast.no Ast.ty;
Cannot contain an enum
*)size : int;
bv_atom_gen : ('v, 'm) exp Common.Gen.sized;
bool_atom_gen : ('v, 'm) exp Common.Gen.t;
}
val atom : params:('a, 'b) gen_param -> ('a, 'b) exp Common.Gen.t
Generate an atom according to the params
val unop : 'a Ast.ty -> (Ast.unop * 'b Ast.ty) Common.Gen.t
val binop : 'a Ast.ty -> ('b Ast.binop * 'c Ast.ty * 'c Ast.ty) Common.Gen.t
val manyop : 'a Ast.ty -> (Ast.manyop * 'b Ast.ty Utils.List.t) Common.Gen.t
val exp_from_params : ('a, Ast.no) gen_param -> ('a, Ast.no) exp Common.Gen.t
val bv_consts : int -> ('a, 'b) Exp.Typed.t Common.Gen.t
val bv_atom_from_var :
+ 'v Common.Gen.sized ->
+ int ->
+ ('v, 'a) Exp.Typed.t Common.Gen.t
val bv_atom_with_var : int -> (int * 'a Ast.ty, 'b) Exp.Typed.t Common.Gen.t
val bool_consts : ('a, 'b) Exp.Typed.t Common.Gen.t
val bool_atom_from_var : 'v Common.Gen.t -> ('v, 'a) Exp.Typed.t Common.Gen.t
val bool_atom_with_var : (int * 'a Ast.ty, 'b) Exp.Typed.t Common.Gen.t
ExpGen.Var
Test variables to instantiate variable dependent functors for testing.
ExpGen.Var
Test variables to instantiate variable dependent functors for testing.
val pp : t -> Utils.Pp.document
val of_string : string -> t
Z3.Htbl
type key
= var
type 'a t
= 'a Z3.Make(Var).Htbl.t
val create : int -> 'a t
val clear : 'a t -> unit
val reset : 'a t -> unit
val copy : 'a t -> 'a t
val add : 'a t -> key -> 'a -> unit
val remove : 'a t -> key -> unit
val find : 'a t -> key -> 'a
val find_opt : 'a t -> key -> 'a option
val find_all : 'a t -> key -> 'a list
val replace : 'a t -> key -> 'a -> unit
val mem : 'a t -> key -> bool
val iter : (key -> 'a -> unit) -> 'a t -> unit
val filter_map_inplace : (key -> 'a -> 'a option) -> 'a t -> unit
val fold : (key -> 'a -> 'b -> 'b) -> 'a t -> 'b -> 'b
val length : 'a t -> int
val stats : 'a t -> Stdlib.Hashtbl.statistics
val to_seq : 'a t -> (key * 'a) Stdlib.Seq.t
val to_seq_keys : 'a t -> key Stdlib.Seq.t
val to_seq_values : 'a t -> 'a Stdlib.Seq.t
val add_seq : 'a t -> (key * 'a) Stdlib.Seq.t -> unit
val replace_seq : 'a t -> (key * 'a) Stdlib.Seq.t -> unit
val of_seq : (key * 'a) Stdlib.Seq.t -> 'a t
Z3.Htbl
type key = var
val create : int -> 'a t
val clear : 'a t -> unit
val reset : 'a t -> unit
val length : 'a t -> int
val stats : 'a t -> Stdlib__Hashtbl.statistics
val to_seq_values : 'a t -> 'a Stdlib.Seq.t
ExpGen.Z3
type var
= Var.t
type exp
= (var, Ast.no) Exp.Typed.t
module Htbl : sig ... end
val declare_var_always : Z3.server -> var -> unit
val declare_var : Z3.server -> declared:unit Htbl.t -> var -> unit
val declare_vars : Z3.server -> declared:unit Htbl.t -> exp -> unit
val simplify : Z3.server -> exp -> exp
val simplify_decl : Z3.server -> declared:unit Htbl.t -> exp -> exp
val send_assert : Z3.server -> exp -> unit
val send_assert_decl : Z3.server -> declared:unit Htbl.t -> exp -> unit
val check : Z3.server -> exp -> bool option
val check_sat : Z3.server -> exp -> bool option
val check_both : Z3.server -> exp -> bool option
val simplify_full : exp -> exp
val check_full : ?hyps:exp list -> exp -> bool option
val check_sat_full : exp list -> bool option
ExpGen.Z3
type var = Var.t
type exp = (var, Ast.no) Exp.Typed.t
module Htbl : sig ... end
val check_sat_full : exp list -> bool option
Tests.ExpGen
type ('v, 'm) exp
= ('v, 'm) Exp.Typed.t
module Var : sig ... end
Test variables to instantiate variable dependent functors for testing.
module ExpT : sig ... end
module Z3 : sig ... end
module Gen : sig ... end
val shrink_propagate : ('v, 'm) Exp.Typed.t Tests.Common.Q.Shrink.t -> ('v, 'm) Exp.Typed.t -> (('m Ast.ty, 'v, Ast.no, 'm) Ast.exp -> unit) -> unit
Propagate an existing shrinker, by try it on all descendants of this. The The provided shrinker must preserve the type.
val from_gen : ?shrink:ExpT.t Tests.Common.Q.Shrink.t -> (ExpT.var, Ast.no) exp Tests.Common.Q.Gen.t -> ExpT.t Tests.Common.Q.arbitrary
Generate an expression arbitrary from a generator (and optionally a shrink
er)
Tests.ExpGen
This module try to provide generic generators and arbitrary to work with expressions.
For now expression do not use enumeration since the Z3 back-end don't support enumeration yet.
type ('v, 'm) exp = ('v, 'm) Exp.Typed.t
module Var : sig ... end
Test variables to instantiate variable dependent functors for testing.
module ExpT : sig ... end
module Z3 : sig ... end
module Gen : sig ... end
val shrink_propagate :
+ ('v, 'm) Exp.Typed.t Tests.Common.Q.Shrink.t ->
+ ('v, 'm) Exp.Typed.t ->
+ (('v, 'm) Exp.Typed.t -> unit) ->
+ unit
Propagate an existing shrinker, by try it on all descendants of this. The The provided shrinker must preserve the type.
Tests.SimplifyCheck
val var_exp_gen_ty : int -> Ast.no Ast.ty -> (int * 'a Ast.ty, Ast.no) ExpGen.Gen.exp Tests.Common.Gen.t
val var_exp_gen : int -> (int * 'a Ast.ty, Ast.no) ExpGen.Gen.exp Tests.Common.Gen.t
val const_exp_shrinker_top : ('a, 'b, 'c, 'd) Ast.Base.exp -> (('a, 'b, 'c, 'd) Ast.Base.exp -> unit) -> unit
Shrink by trying all sub expressions
val const_exp_shrinker_bot : (Ast.no Ast.ty, ExpGen.Z3.var, Ast.no, Ast.no) Ast.exp -> (ExpGen.Z3.exp -> unit) -> unit
Shrink by replacing a non-atomic expression by it's simplified version
val const_exp_shrinker : (Ast.no Ast.ty, ExpGen.Z3.var, Ast.no, Ast.no) Ast.Base.exp -> ((Ast.no Ast.ty, ExpGen.Z3.var, Ast.no, Ast.no) Ast.exp -> unit) -> unit
Shrink by using both const_exp_shrinker_top
and const_exp_shrinker_bot
val var_exp : ExpGen.ExpT.t Tests.Common.Q.arbitrary
val simplify_check : Tests.Common.QCT.t
val tests : Tests.Common.QCT.t list
Tests.SimplifyCheck
This module is about testing Z3 simplify against Z3 check. It's not really about testing Z3 itself but about testing our parsing of Z3 expression output (in particular let bindings unfolding)
val var_exp_gen_ty :
+ int ->
+ Ast.no Ast.ty ->
+ (int * 'a Ast.ty, Ast.no) ExpGen.Gen.exp Common.Gen.t
val var_exp_gen : int -> (int * 'a Ast.ty, Ast.no) ExpGen.Gen.exp Common.Gen.t
val const_exp_shrinker_top :
+ ('a, 'b, 'c, 'd) Ast.Base.exp ->
+ (('a, 'b, 'c, 'd) Ast.Base.exp -> unit) ->
+ unit
Shrink by trying all sub expressions
val const_exp_shrinker_bot :
+ (Ast.no Ast.ty, ExpGen.Z3.var, Ast.no, Ast.no) Ast.exp ->
+ (ExpGen.Z3.exp -> unit) ->
+ unit
Shrink by replacing a non-atomic expression by it's simplified version
val const_exp_shrinker :
+ (Ast.no Ast.ty, ExpGen.Z3.var, Ast.no, Ast.no) Ast.Base.exp ->
+ ((ExpGen.Z3.var, Ast.no) Exp.Typed.t -> unit) ->
+ unit
Shrink by using both const_exp_shrinker_top
and const_exp_shrinker_bot
val var_exp : ExpGen.ExpT.t Tests.Common.Q.arbitrary
Tests
module BytesSeqT : sig ... end
module Common : sig ... end
module ConcreteEvalT : sig ... end
module ExpGen : sig ... end
module SimplifyCheck : sig ... end
Tests
module BytesSeqT : sig ... end
module Common : sig ... end
module ConcreteEvalT : sig ... end
This module is for testing ConcreteEval
module ExpGen : sig ... end
This module try to provide generic generators and arbitrary to work with expressions.
module SimplifyCheck : sig ... end
This module is about testing Z3 simplify against Z3 check. It's not really about testing Z3 itself but about testing our parsing of Z3 expression output (in particular let bindings unfolding)
Base.Exp
Base.Exp
Base.SimpContext
A instance of Z3.ContextCounter
.
val counter : Utils.Counter.t
val openc : unit -> unit
val num : unit -> int
val closec : unit -> unit
Base.SimpContext
A instance of Z3.ContextCounter
.
Base.Var
This module contains variable used in traces
module Reg = State.Reg
type t
=
| Register of Reg.t | The value of the register at the beginning of the trace |
| Read of int * Ast.Size.t | The result of that memory reading operation |
| NonDet of int * Ast.Size.t | Variable representing non-determinism in the spec |
A trace variable
val to_string : t -> string
Convert the variable to the string encoding. For parsing infractructure reason, the encoding must always contain at least one :
.
Base.Var
This module contains variable used in traces
module Reg = State.Reg
type t =
| Register of Reg.t
The value of the register at the beginning of the trace
*)| Read of int * Ast.Size.t
The result of that memory reading operation
*)| NonDet of int * Ast.Size.t
Variable representing non-determinism in the spec
*)| Segment of string * int
Variable representing symbolic segment in the opcode
*)A trace variable
val to_string : t -> string
Convert the variable to the string encoding. For parsing infractructure reason, the encoding must always contain at least one :
.
val pp : t -> Utils.Pp.document
Pretty prints the variable
Base.VarTbl
val create : int -> 'a t
val clear : 'a t -> unit
val reset : 'a t -> unit
val copy : 'a t -> 'a t
val add : 'a t -> key -> 'a -> unit
val remove : 'a t -> key -> unit
val find : 'a t -> key -> 'a
val find_opt : 'a t -> key -> 'a option
val find_all : 'a t -> key -> 'a list
val replace : 'a t -> key -> 'a -> unit
val mem : 'a t -> key -> bool
val iter : (key -> 'a -> unit) -> 'a t -> unit
val filter_map_inplace : (key -> 'a -> 'a option) -> 'a t -> unit
val fold : (key -> 'a -> 'b -> 'b) -> 'a t -> 'b -> 'b
val length : 'a t -> int
val stats : 'a t -> Stdlib__hashtbl.statistics
val to_seq : 'a t -> (key * 'a) Stdlib.Seq.t
val to_seq_keys : 'a t -> key Stdlib.Seq.t
val to_seq_values : 'a t -> 'a Stdlib.Seq.t
val add_seq : 'a t -> (key * 'a) Stdlib.Seq.t -> unit
val replace_seq : 'a t -> (key * 'a) Stdlib.Seq.t -> unit
val of_seq : (key * 'a) Stdlib.Seq.t -> 'a t
Base.VarTbl
type key = Var.t
val create : int -> 'a t
val clear : 'a t -> unit
val reset : 'a t -> unit
val length : 'a t -> int
val stats : 'a t -> Stdlib__Hashtbl.statistics
val to_seq_values : 'a t -> 'a Stdlib.Seq.t
Z3Tr.Htbl
type key
= var
type 'a t
= 'a Z3.Make(Var).Htbl.t
val create : int -> 'a t
val clear : 'a t -> unit
val reset : 'a t -> unit
val copy : 'a t -> 'a t
val add : 'a t -> key -> 'a -> unit
val remove : 'a t -> key -> unit
val find : 'a t -> key -> 'a
val find_opt : 'a t -> key -> 'a option
val find_all : 'a t -> key -> 'a list
val replace : 'a t -> key -> 'a -> unit
val mem : 'a t -> key -> bool
val iter : (key -> 'a -> unit) -> 'a t -> unit
val filter_map_inplace : (key -> 'a -> 'a option) -> 'a t -> unit
val fold : (key -> 'a -> 'b -> 'b) -> 'a t -> 'b -> 'b
val length : 'a t -> int
val stats : 'a t -> Stdlib.Hashtbl.statistics
val to_seq : 'a t -> (key * 'a) Stdlib.Seq.t
val to_seq_keys : 'a t -> key Stdlib.Seq.t
val to_seq_values : 'a t -> 'a Stdlib.Seq.t
val add_seq : 'a t -> (key * 'a) Stdlib.Seq.t -> unit
val replace_seq : 'a t -> (key * 'a) Stdlib.Seq.t -> unit
val of_seq : (key * 'a) Stdlib.Seq.t -> 'a t
Z3Tr.Htbl
type key = var
val create : int -> 'a t
val clear : 'a t -> unit
val reset : 'a t -> unit
val length : 'a t -> int
val stats : 'a t -> Stdlib__Hashtbl.statistics
val to_seq_values : 'a t -> 'a Stdlib.Seq.t
Base.Z3Tr
type var
= Var.t
type exp
= (var, Ast.no) Exp.Typed.t
module Htbl : sig ... end
val declare_var_always : Z3.server -> var -> unit
val declare_var : Z3.server -> declared:unit Htbl.t -> var -> unit
val declare_vars : Z3.server -> declared:unit Htbl.t -> exp -> unit
val simplify : Z3.server -> exp -> exp
val simplify_decl : Z3.server -> declared:unit Htbl.t -> exp -> exp
val send_assert : Z3.server -> exp -> unit
val send_assert_decl : Z3.server -> declared:unit Htbl.t -> exp -> unit
val check : Z3.server -> exp -> bool option
val check_sat : Z3.server -> exp -> bool option
val check_both : Z3.server -> exp -> bool option
val simplify_full : exp -> exp
val check_full : ?hyps:exp list -> exp -> bool option
val check_sat_full : exp list -> bool option
Base.Z3Tr
type var = Var.t
type exp = (var, Ast.no) Exp.Typed.t
module Htbl : sig ... end
val check_sat_full : exp list -> bool option
Trace.Base
This module defines a new simplified kind of trace to replace Isla traces in the later stages of the instruction semantics processing.
The traces are even simpler and more easily typable. The possible events are in the type event
and traces (t
) are just list of them.
Compared to Isla, the concept of reading a register do not exist anymore. Nor the concept of pure symbolic variable or Sail structured values. Instead expression can contain only registers and results of previous memory read as decribe in the type Var.t
. All writing event directly write an entire expression. There are no intermediary variable definitions.
This raise a problem that if an isla trace reads a register after having written to it, then this is ambiguous to represent.
Thus a partially monadic representation has been chosen:
In the end, both assertion and register write to different registers can be reordered at will.
TODO: To make it more clean, getting register writes and assertions out of the trace would make sense like:
type mem_event = Read of ... | Write of ...
-type t = { asserts : exp list; reg_writes : (Reg.t * exp) list; mem: mem_event list }
For all those reason, concatenating two trace semantically is very different that concatenating list of event, and is not implemented yet.
The important functions are of_isla
to convert and Isla traces and simplify
for simplify traces.
module Var : sig ... end
This module contains variable used in traces
module ExpPp = Exp.Pp
A trace expression. No let bindings, no memory operations
module Typed = Exp.Typed
module Exp : sig ... end
type exp
= Exp.t
type event
=
| WriteReg of {
} | |||
| ReadMem of {
} | |||
| WriteMem of {
} | |||
| Assert of exp |
The event type. See the module description for more details
type t
= event list
val pp : event list -> PPrintEngine.document
Pretty print a trace
This section perform the conversion from Isla trace to the traces of this module.
The conversion is generrally obvious, however there is subtlety: If the Isla trace reads a register after having written it, then the read produce the written expression instead of just the symbolic value of that register. That why there is a written_registers
parameter to some function of this section.
exception
OfIslaError
Throw an error in case of local conversion error. Normally a type-checked Isla trace should not fail in this section
type value_context
= exp Utils.HashVector.t
The context mapping Isla variable numbers to trace expression
val get_var : 'a Utils.HashVector.t -> int -> 'a
Get the exression of the variable at the index. Throw OfIslaError
if the variable is not bound
val exp_conv_subst : value_context -> Isla.rexp -> exp
Convert an Isla expression to a Trace
expression by replacing all Isla variable by their value in the context. Throw OfIslaError
if the substitution fails
val exp_of_valu : Isla_lang.AST.lrng -> exp Utils.HashVector.t -> Isla.valu -> exp
Convert an Isla
.valu in a expression
val write_to_valu : 'a Utils.HashVector.t -> Isla.valu -> 'a -> unit
Write an expression to an Isla
.valu
val event_of_isla : written_registers:(State.Reg.t, exp) Stdlib.Hashtbl.t -> read_counter:Utils.Counter.t -> vc:value_context -> Isla.revent -> event option
Convert an isla event to optionally a Trace event, most events are deleted
module SimpContext : sig ... end
A instance of Z3.ContextCounter
.
module Z3Tr : sig ... end
module VarTbl : sig ... end
Trace.Base
This module defines a new simplified kind of trace to replace Isla traces in the later stages of the instruction semantics processing.
The traces are even simpler and more easily typable. The possible events are in the type event
and traces (t
) are just list of them.
Compared to Isla, the concept of reading a register do not exist anymore. Nor the concept of pure symbolic variable or Sail structured values. Instead expression can contain only registers and results of previous memory read as decribe in the type Var.t
. All writing event directly write an entire expression. There are no intermediary variable definitions.
This raise a problem that if an isla trace reads a register after having written to it, then this is ambiguous to represent.
Thus a partially monadic representation has been chosen:
In the end, both assertion and register write to different registers can be reordered at will.
TODO: To make it more clean, getting register writes and assertions out of the trace would make sense like:
type mem_event = Read of ... | Write of ...
+type t = { asserts : exp list; reg_writes : (Reg.t * exp) list; mem: mem_event list }
For all those reason, concatenating two trace semantically is very different that concatenating list of event, and is not implemented yet.
The important functions are of_isla
to convert and Isla traces and simplify
for simplify traces.
module Var : sig ... end
This module contains variable used in traces
module ExpPp = Exp.Pp
A trace expression. No let bindings, no memory operations
module Typed = Exp.Typed
module Exp : sig ... end
type exp = Exp.t
type event =
| WriteReg of {
reg : State.Reg.t;
value : exp;
}
| ReadMem of {
addr : exp;
value : int;
size : Ast.Size.t;
}
| WriteMem of {
addr : exp;
value : exp;
size : Ast.Size.t;
}
| Assert of exp
The event type. See the module description for more details
type t = event list
val pp_exp : ('a, Var.t, Ast.no, Ast.no) Ast.exp -> Utils.Pp.document
Pretty print an expression
val pp_event : event -> Utils.Pp.document
Pretty print an event
val pp : event list -> Utils.Pp.document
Pretty print a trace
This section perform the conversion from Isla trace to the traces of this module.
The conversion is generrally obvious, however there is subtlety: If the Isla trace reads a register after having written it, then the read produce the written expression instead of just the symbolic value of that register. That why there is a written_registers
parameter to some function of this section.
Throw an error in case of local conversion error. Normally a type-checked Isla trace should not fail in this section
type value_context = exp Utils.HashVector.t
The context mapping Isla variable numbers to trace expression
val get_var : 'a Utils.HashVector.t -> int -> 'a
Get the exression of the variable at the index. Throw OfIslaError
if the variable is not bound
val exp_conv_subst : value_context -> Isla.rexp -> exp
Convert an Isla expression to a Trace
expression by replacing all Isla variable by their value in the context. Throw OfIslaError
if the substitution fails
val exp_of_valu :
+ Isla_lang.AST.lrng ->
+ exp Utils.HashVector.t ->
+ Isla.valu ->
+ exp
Convert an Isla.valu
in a expression
val write_to_valu : 'a Utils.HashVector.t -> Isla.valu -> 'a -> unit
Write an expression to an Isla.valu
val events_of_isla :
+ segments_map:(string * int) Utils.HashVector.t ->
+ written_registers:(State.Reg.t, exp) Stdlib.Hashtbl.t ->
+ read_counter:Utils.Counter.t ->
+ vc:value_context ->
+ Isla.revent ->
+ event list
Convert an isla event to Trace events, most events are deleted
val of_isla : Isla.segment list -> Isla.rtrc -> t
Top level function to convert an isla trace to one of this module
module SimpContext : sig ... end
A instance of Z3.ContextCounter
.
module Z3Tr : sig ... end
module VarTbl : sig ... end
Cache.TC
type key
= Opcode.t
type value
= Traces.t
type epoch
= Epoch.t
type t
= Utils__Cache.Make(Opcode)(Traces)(Epoch).t
Cache.TC
Cache.Traces
Store element of type Base.t
list
on disk.
First a hashmap from register numbers to path and type is stored. Then the actual tracelist is marshaled.
When loading the register numbering may be different, and some may not exist. So we create all missing register, then we replace the old number with the new ones.
type t
= Base.t list
module Reg = State.Reg
type regs
= (Reg.t, Reg.Path.t * Reg.ty) Stdlib.Hashtbl.t
Cache.Traces
Store element of type Base.t
list
on disk.
First a hashmap from register numbers to path and type is stored. Then the actual tracelist is marshaled.
When loading the register numbering may be different, and some may not exist. So we create all missing register, then we replace the old number with the new ones.
type t = Base.t list
module Reg = State.Reg
type regs = (Reg.t, Reg.Path.t * Reg.ty) Stdlib.Hashtbl.t
val to_file : string -> t -> unit
val of_file : string -> t
Trace.Cache
This module provides a caching system for fully processed traces
The top level function to get traces from an opcode is get_traces
. This is the function called by the Run
.Runner.
module Opcode = Isla.Cache.Opcode
module Epoch = Isla.Cache.Epoch
module TC : sig ... end
val cache : TC.t option Stdlib.ref
type config
= Isla.Cache.config
val start : Isla.Cache.config -> unit
Start the caching system. Start Isla.Cache
too
val get_cache : unit -> TC.t
Get the cache and fails if the cache wasn't started
val get_traces : Utils.BytesSeq.t -> Base.t list
Get the traces of the opcode given. Use Isla.Server
if the value is not in the cache
val get_instr : Utils.BytesSeq.t -> Instr.t
Get a full blown Instr
from the opcode, going through the whole Isla pipeline if necessary.
Trace.Cache
This module provides a caching system for fully processed traces
The top level function to get traces from an opcode is get_traces
. This is the function called by the Run.Runner
.
module Opcode = Isla.Cache.Opcode
module Epoch = Isla.Cache.Epoch
module TC : sig ... end
val cache : TC.t option Stdlib.ref
type config = Isla.Cache.config
val start : Isla.Cache.config -> unit
Start the caching system. Start Isla.Cache
too
val get_cache : unit -> TC.t
Get the cache and fails if the cache wasn't started
val get_traces : Isla.Server.opcode -> Base.t list
Get the traces of the opcode given. Use Isla.Server
if the value is not in the cache
val get_instr : (Utils.BytesSeq.t * Elf.Relocations.rel option) -> Instr.t
Get a full blown Instr
from the opcode, going through the whole Isla pipeline if necessary.
Trace.Context
This module provide the type for a context to run a trace
Any information that should be required to run a trace but is not part of the state itself should be added here
type t
=
{
reg_writes : (State.Reg.t * State.tval) Utils.Vec.t; | Stores the delayed register writes |
mem_reads : State.tval Utils.HashVector.t; | Stores the result of memory reads |
state : State.t; | |
dwarf : Dw.t option; | Optionally DWARF information. If present, typing is enabled |
}
The context to run a trace
val expand_var : ctxt:t -> Base.Var.t -> Ast.no Ast.ty -> State.exp
Expand a Trace variable to a State expression, using the context
Trace.Context
This module provide the type for a context to run a trace
Any information that should be required to run a trace but is not part of the state itself should be added here
module SMap : sig ... end
type t = {
reg_writes : (State.Reg.t * State.tval) Utils.Vec.t;
Stores the delayed register writes
*)mem_reads : State.tval Utils.HashVector.t;
Stores the result of memory reads
*)nondets : State.var Utils.HashVector.t;
Stores the mapping of nondet variables
*)state : State.t;
segments : State.exp SMap.t;
asserts : State.exp list;
dwarf : Dw.t option;
Optionally DWARF information. If present, typing is enabled
*)}
The context to run a trace
val make_context :
+ ?dwarf:Dw.t ->
+ ?relocation:Elf.Relocations.rel ->
+ State.t ->
+ t
Build a context
from a state
val expand_var : ctxt:t -> Base.Var.t -> Ast.no Ast.ty -> State.exp
Expand a Trace variable to a State expression, using the context
val typing_enabled : ctxt:t -> bool
Tell if typing should enabled with this context
module Z3St = State.Simplify.Z3St
Trace.Instr
This module provide the representation of an instruction. It only contains generic information about the opcode and not specific information about a place in the code.
module Reg = State.Reg
type trace_meta
=
{
trace : Base.t; |
jump_target : Base.exp option; |
read : Reg.t list; |
written : Reg.t list; |
}
A simple trace with its metadata
If there is a WriteReg {reg;value}
event in trace
where reg = Arch.pc ()
, jump = Some value
, otherwise it is None
. If there are multiple such events, it will store the value of the last one.
type t
=
{
traces : trace_meta list; | |
length : int; | Bytes length |
read : Reg.t list; | |
written : Reg.t list; | |
opcode : Utils.BytesSeq.t; |
}
A full instruction representation
val dedup_regs : State.Reg.t list -> State.Reg.t list
val footprint : t -> State.Reg.t list
val trace_meta_of_trace : Base.t -> trace_meta
Compute the metadata of trace
val of_traces : Utils.BytesSeq.t -> Base.t list -> t
Generate full instruction data from a list of traces
Trace.Instr
This module provide the representation of an instruction. It only contains generic information about the opcode and not specific information about a place in the code.
module Reg = State.Reg
A simple trace with its metadata
If there is a WriteReg {reg;value}
event in trace
where reg = Arch.pc ()
, jump = Some value
, otherwise it is None
. If there are multiple such events, it will store the value of the last one.
module SMap : sig ... end
type t = {
traces : trace_meta list;
length : int;
Bytes length
*)read : Reg.t list;
written : Reg.t list;
opcode : Utils.BytesSeq.t;
relocation : Elf.Relocations.rel option;
}
A full instruction representation
val dedup_regs : State.Reg.t list -> State.Reg.t list
val footprint : t -> State.Reg.t list
val trace_meta_of_trace : Base.t -> trace_meta
Compute the metadata of trace
val of_traces :
+ (Utils.BytesSeq.t * Elf.Relocations.rel option) ->
+ Base.t list ->
+ t
Generate full instruction data from a list of traces
val pp : t -> Utils.Pp.document
Pretty print the representation of an instruction
Trace.Run
This module is for running trace from Trace
like Isla.Run
runs Isla traces.
Due to the semantic of a register access being the register at the beginning of the trace, all register writes are not done immediately but delayed and stored in the context
.
Typing is enabled if Context.typing_enabled
returns true for functions that take a context. For other functions, typing is enabled if the dwarf
optional argument is passed
module Ctxt = Context
type ctxt
= Ctxt.t
val expand : ctxt:ctxt -> Base.exp -> State.exp
Expand a Trace
expression to a State
expression, using the context
val expand_tval : ctxt:ctxt -> Base.exp -> State.tval
Expand a Trace expression to a typed State expression, using the context.
If the context enables typing, the expression will actually be typed, otherwise the type will be None
val event_mut : ctxt:ctxt -> Base.event -> unit
Run the event. The modified state is the one inside ctxt
.
val trace_mut : ?dwarf:Dw.t -> State.t -> Base.t -> unit
Run a trace on the provided state by mutation. Enable typing if dwarf
is provided
Trace.Run
This module is for running trace from Trace
like Isla.Run
runs Isla traces.
Due to the semantic of a register access being the register at the beginning of the trace, all register writes are not done immediately but delayed and stored in the context
.
Typing is enabled if Context.typing_enabled
returns true for functions that take a context. For other functions, typing is enabled if the dwarf
optional argument is passed
module Ctxt = Context
type ctxt = Ctxt.t
val expand_tval : ctxt:ctxt -> Base.exp -> State.tval
Expand a Trace expression to a typed State expression, using the context.
If the context enables typing, the expression will actually be typed, otherwise the type will be None
val event_mut : ctxt:ctxt -> Base.event -> unit
Run the event. The modified state is the one inside ctxt
.
val trace_mut :
+ ?dwarf:Dw.t ->
+ ?relocation:Elf.Relocations.rel ->
+ State.t ->
+ Base.t ->
+ unit
Run a trace on the provided state by mutation. Enable typing if dwarf
is provided
val trace :
+ ?dwarf:Dw.t ->
+ ?relocation:Elf.Relocations.rel ->
+ State.t ->
+ Base.t ->
+ State.t
Run a trace on the provided state by returning an updated copy.
val trace_pc_mut :
+ ?dwarf:Dw.t ->
+ ?relocation:Elf.Relocations.rel ->
+ next:int ->
+ State.t ->
+ Base.t ->
+ unit
Run a trace by mutating the provided state including it's PC. If the trace modified the PC then nothing is done otherwise next
is added to it.
Thus this function automatically handle moving the PC for fall-through instruction
Trace
include Base
module Var = Base.Var
This module contains variable used in traces
module ExpPp = Exp.Pp
A trace expression. No let bindings, no memory operations
module Typed = Exp.Typed
module Exp = Base.Exp
type exp
= Exp.t
type event
=
| WriteReg of {
} | |||
| ReadMem of {
} | |||
| WriteMem of {
} | |||
| Assert of exp |
The event type. See the module description for more details
type t
= event list
val pp : event list -> PPrintEngine.document
Pretty print a trace
This section perform the conversion from Isla trace to the traces of this module.
The conversion is generrally obvious, however there is subtlety: If the Isla trace reads a register after having written it, then the read produce the written expression instead of just the symbolic value of that register. That why there is a written_registers
parameter to some function of this section.
exception
OfIslaError
Throw an error in case of local conversion error. Normally a type-checked Isla trace should not fail in this section
type value_context
= exp Utils.HashVector.t
The context mapping Isla variable numbers to trace expression
val get_var : 'a Utils.HashVector.t -> int -> 'a
Get the exression of the variable at the index. Throw OfIslaError
if the variable is not bound
val exp_conv_subst : value_context -> Isla.rexp -> exp
Convert an Isla expression to a Trace
expression by replacing all Isla variable by their value in the context. Throw OfIslaError
if the substitution fails
val exp_of_valu : Isla_lang.AST.lrng -> exp Utils.HashVector.t -> Isla.valu -> exp
Convert an Isla
.valu in a expression
val write_to_valu : 'a Utils.HashVector.t -> Isla.valu -> 'a -> unit
Write an expression to an Isla
.valu
val event_of_isla : written_registers:(State.Reg.t, exp) Stdlib.Hashtbl.t -> read_counter:Utils.Counter.t -> vc:value_context -> Isla.revent -> event option
Convert an isla event to optionally a Trace event, most events are deleted
module SimpContext = Base.SimpContext
A instance of Z3.ContextCounter
.
module Z3Tr = Base.Z3Tr
module VarTbl = Base.VarTbl
module Base : sig ... end
This module defines a new simplified kind of trace to replace Isla traces in the later stages of the instruction semantics processing.
module Cache : sig ... end
This module provides a caching system for fully processed traces
module Context : sig ... end
This module provide the type for a context to run a trace
module Ctype = Ctype
module Instr : sig ... end
This module provide the representation of an instruction. It only contains generic information about the opcode and not specific information about a place in the code.
Trace
include module type of struct include Base end
module Var = Base.Var
This module contains variable used in traces
module ExpPp = Exp.Pp
A trace expression. No let bindings, no memory operations
module Typed = Exp.Typed
module Exp = Base.Exp
type exp = Exp.t
type event = Base.event =
| WriteReg of {
reg : State.Reg.t;
value : exp;
}
| ReadMem of {
addr : exp;
value : int;
size : Ast.Size.t;
}
| WriteMem of {
addr : exp;
value : exp;
size : Ast.Size.t;
}
| Assert of exp
The event type. See the module description for more details
type t = event list
val pp_exp : ('a, Var.t, Ast.no, Ast.no) Ast.exp -> Utils.Pp.document
Pretty print an expression
val pp_event : event -> Utils.Pp.document
Pretty print an event
val pp : event list -> Utils.Pp.document
Pretty print a trace
This section perform the conversion from Isla trace to the traces of this module.
The conversion is generrally obvious, however there is subtlety: If the Isla trace reads a register after having written it, then the read produce the written expression instead of just the symbolic value of that register. That why there is a written_registers
parameter to some function of this section.
Throw an error in case of local conversion error. Normally a type-checked Isla trace should not fail in this section
type value_context = exp Utils.HashVector.t
The context mapping Isla variable numbers to trace expression
val get_var : 'a Utils.HashVector.t -> int -> 'a
Get the exression of the variable at the index. Throw OfIslaError
if the variable is not bound
val exp_conv_subst : value_context -> Isla.rexp -> exp
Convert an Isla expression to a Trace
expression by replacing all Isla variable by their value in the context. Throw OfIslaError
if the substitution fails
val exp_of_valu :
+ Isla_lang.AST.lrng ->
+ exp Utils.HashVector.t ->
+ Isla.valu ->
+ exp
Convert an Isla.valu
in a expression
val write_to_valu : 'a Utils.HashVector.t -> Isla.valu -> 'a -> unit
Write an expression to an Isla.valu
val events_of_isla :
+ segments_map:(string * int) Utils.HashVector.t ->
+ written_registers:(State.Reg.t, exp) Stdlib.Hashtbl.t ->
+ read_counter:Utils.Counter.t ->
+ vc:value_context ->
+ Isla.revent ->
+ event list
Convert an isla event to Trace events, most events are deleted
val of_isla : Isla.segment list -> Isla.rtrc -> t
Top level function to convert an isla trace to one of this module
module SimpContext = Base.SimpContext
A instance of Z3.ContextCounter
.
module Z3Tr = Base.Z3Tr
module VarTbl = Base.VarTbl
module Base : sig ... end
This module defines a new simplified kind of trace to replace Isla traces in the later stages of the instruction semantics processing.
module Cache : sig ... end
This module provides a caching system for fully processed traces
module Context : sig ... end
This module provide the type for a context to run a trace
module Ctype = Ctype
module Instr : sig ... end
This module provide the representation of an instruction. It only contains generic information about the opcode and not specific information about a place in the code.
The type inference engine is a somewhat basic system right now. The types themselves are defined in Ctype
, but the type inference engine is in Trace
.Typer. This because the type inference is done instruction by instruction over trace expressions, and not directly on state expression.
The type inference engine is a somewhat basic system right now. The types themselves are defined in Ctype
, but the type inference engine is in Trace.Typer
. This because the type inference is done instruction by instruction over trace expressions, and not directly on state expression.
Option
: extension of Stdlib.Option
.Array
: extension of Stdlib.Array
List
: extension of Stdlib.List
Fun
: extension of Stdlib.Fun
Pair
: A module to lift operation over pairsSeq
: extension of Stdlib.Seq
String
: extension of Stdlib.String
Bits
: Intends to provide the same interface as Stdlib.Bytes
but at the individual bit level. The external type is explicitely bytes
.IntBits
: Same interface as Bits
but on an integer. This provides understandable ways of moving set of bits around directly in integers without having to think about shifts.BitVec
: Concrete bitvector library based on zarith
.BytesSeq
: Implement a view over a bytes
. This view can be restricted in a pure interface without requiring to copy the bytes
.RngMap
: Map structure that allow to index value by range of addresses. You can bind a whole interval of addresses to a value.Vec
: Main resizable array module. Layer on top of the res
library.FullVec
: Conceptually a array/vector that binds all the integers to values. It's implemented by a vector and a generator.HashVector
: A Data structure to use a vector as small int to something hashtable. Implemented as a 'a option Vec.t
IdMap
: A hashmap that numbers the bindings such that each binding can be identified by either the key of the integer identifier. Useful for doing symbol numbering for example.Counter
: Just an integer counter to index something. It has a Counter
.get function that give the next integer each time it's called.This module is about addition to the Stdlib.Weak
module of the standard library. Those are data structures that do not retain GC ownership of their values, which mean the GC can delete them at any moment.
WeakPtr
: A single weak pointer whose pointee can be garbage collected if nothing else points to it.WeakMap
: A Hash map which own the keys but not the values. When a value is cleared by the GC, the binding dissapears entirely from the map.Protect
: An improvement over protect
.Raise
: Convenience function to raise and manage exception in an easier way.Files
: Various IO facilities around IO channel and file managementCmd
: Library for easily calling external programs and also for keeping them running as background servers.Cache
: Generic library to implement a caching system in the form of a persistent hash table structure on disk.Option
: extension of Stdlib.Option
.Array
: extension of Stdlib.Array
List
: extension of Stdlib.List
Fun
: extension of Stdlib.Fun
Pair
: A module to lift operation over pairsSeq
: extension of Stdlib.Seq
String
: extension of Stdlib.String
Bits
: Intends to provide the same interface as Stdlib.Bytes
but at the individual bit level. The external type is explicitely bytes
.IntBits
: Same interface as Bits
but on an integer. This provides understandable ways of moving set of bits around directly in integers without having to think about shifts.BitVec
: Concrete bitvector library based on zarith
.BytesSeq
: Implement a view over a bytes
. This view can be restricted in a pure interface without requiring to copy the bytes
.RngMap
: Map structure that allow to index value by range of addresses. You can bind a whole interval of addresses to a value.Vec
: Main resizable array module. Layer on top of the res
library.FullVec
: Conceptually a array/vector that binds all the integers to values. It's implemented by a vector and a generator.HashVector
: A Data structure to use a vector as small int to something hashtable. Implemented as a 'a option Vec.t
IdMap
: A hashmap that numbers the bindings such that each binding can be identified by either the key of the integer identifier. Useful for doing symbol numbering for example.Counter
: Just an integer counter to index something. It has a Counter.get
function that give the next integer each time it's called.This module is about addition to the Stdlib.Weak
module of the standard library. Those are data structures that do not retain GC ownership of their values, which mean the GC can delete them at any moment.
WeakPtr
: A single weak pointer whose pointee can be garbage collected if nothing else points to it.WeakMap
: A Hash map which own the keys but not the values. When a value is cleared by the GC, the binding dissapears entirely from the map.Protect
: An improvement over protect
.Raise
: Convenience function to raise and manage exception in an easier way.Files
: Various IO facilities around IO channel and file managementCmd
: Library for easily calling external programs and also for keeping them running as background servers.Cache
: Generic library to implement a caching system in the form of a persistent hash table structure on disk.Utils.Array
include Stdlib.Array
val length : 'a array -> int
val get : 'a array -> int -> 'a
val set : 'a array -> int -> 'a -> unit
val make : int -> 'a -> 'a array
val create : int -> 'a -> 'a array
val create_float : int -> float array
val make_float : int -> float array
val init : int -> (int -> 'a) -> 'a array
val make_matrix : int -> int -> 'a -> 'a array array
val create_matrix : int -> int -> 'a -> 'a array array
val append : 'a array -> 'a array -> 'a array
val concat : 'a array list -> 'a array
val sub : 'a array -> int -> int -> 'a array
val copy : 'a array -> 'a array
val fill : 'a array -> int -> int -> 'a -> unit
val blit : 'a array -> int -> 'a array -> int -> int -> unit
val to_list : 'a array -> 'a list
val of_list : 'a list -> 'a array
val iter : ('a -> unit) -> 'a array -> unit
val iteri : (int -> 'a -> unit) -> 'a array -> unit
val map : ('a -> 'b) -> 'a array -> 'b array
val mapi : (int -> 'a -> 'b) -> 'a array -> 'b array
val fold_left : ('a -> 'b -> 'a) -> 'a -> 'b array -> 'a
val fold_right : ('b -> 'a -> 'a) -> 'b array -> 'a -> 'a
val iter2 : ('a -> 'b -> unit) -> 'a array -> 'b array -> unit
val map2 : ('a -> 'b -> 'c) -> 'a array -> 'b array -> 'c array
val for_all : ('a -> bool) -> 'a array -> bool
val exists : ('a -> bool) -> 'a array -> bool
val mem : 'a -> 'a array -> bool
val memq : 'a -> 'a array -> bool
val sort : ('a -> 'a -> int) -> 'a array -> unit
val stable_sort : ('a -> 'a -> int) -> 'a array -> unit
val fast_sort : ('a -> 'a -> int) -> 'a array -> unit
val to_seq : 'a array -> 'a Stdlib.Seq.t
val to_seqi : 'a array -> (int * 'a) Stdlib.Seq.t
val of_seq : 'a Stdlib.Seq.t -> 'a array
val of_list_mapi : (int -> 'a -> 'b) -> 'a list -> 'b array
of_list_mapi f l = of_list (List.mapi f i l) = mapi f (of_list l)
val of_list_map : ('a -> 'b) -> 'a list -> 'b array
of_list_map f l = of_list (List.map f l) = map f (of_list l)
val find_pair : ('a -> bool) -> 'a array -> int * 'a
Find the first value satisfying the predicate and return it with its index. Throw Not_found
if no value satisfies the predicate
val find : ('a -> bool) -> 'a array -> 'a
Find the first value satisfying the predicate. Throw Not_found
if no value satisfies the predicate
val find_index : ('a -> bool) -> 'a array -> int
Find the first index whose value satisfies the predicate. Throw Not_found
if no value satisfies the predicate
val find_all_pairs : ('a -> bool) -> 'a t -> (int * 'a) list
Find all the values satisfying the predicate and return them with their index.
val find_all : ('a -> bool) -> 'a t -> 'a list
Find all the values satisfying the predicate
val find_all_indices : ('a -> bool) -> 'a t -> int list
Find all the indices whose value satisfies the predicate
Utils.Array
This module is for extending the Array
module of the standard library
include module type of struct include Stdlib.Array end
of_list_mapi f l = of_list (List.mapi f i l) = mapi f (of_list l)
of_list_map f l = of_list (List.map f l) = map f (of_list l)
Find the first value satisfying the predicate and return it with its index. Throw Not_found
if no value satisfies the predicate
Find the first value satisfying the predicate. Throw Not_found
if no value satisfies the predicate
Find the first index whose value satisfies the predicate. Throw Not_found
if no value satisfies the predicate
val find_all_pairs : ('a -> bool) -> 'a t -> (int * 'a) list
Find all the values satisfying the predicate and return them with their index.
val find_all : ('a -> bool) -> 'a t -> 'a list
Find all the values satisfying the predicate
val find_all_indices : ('a -> bool) -> 'a t -> int list
Find all the indices whose value satisfies the predicate
Utils.BitVec
exception
SizeMismatch of int * int
Raise when the runtime size do not match on operation that require so (like add
)
val size : t -> int
The size of the bitvector
val zero : size:int -> t
The bitvector representing 0 of specified size
val one : size:int -> t
The bitvector representing 1 of specified size
val minus_one : size:int -> t
The bitvector representing -1 of specified size
val to_z : t -> Z.t
To a signed big integer
val to_uz : t -> Z.t
To an unsigned big integer
val of_z : size:int -> Z.t -> t
Of bit integer. Wrapped modulo 2^size
.
val to_int : t -> int
To a signed integer. Fail if it doesn't fit
val to_uint : t -> int
To an unsigned integer. Fail if it doesn't fit without wrapping i.e the result is still positive
val of_int : size:int -> int -> t
Of integer. Wrapped modulo size
.
val to_bool : t -> bool
Convert a one size bitvector to bool. Throw SizeMismatch
if the bitvector is not one-sized
val of_bool : bool -> t
Create a one-sized bitvector representing the boolean
val to_bytes : t -> bytes
Return the shortest bytes that represent the bitvector in little-endian. There may be extra bits (if size is not a multiple of 8) which are zeros.
This bytes may be shorter that the bitvector size, for example the bitvector 1 of size 64bits, will still be returned by this function as a single byte 1. For another behavior, see to_bytes_exact
.
val to_bytes_exact : t -> bytes
Return a bytes representation of mininal length to encompass the whole bitvector size. Extra bits (if size is not a multiple of 8) are zeros.
val bytes_store : bytes -> int -> t -> unit
Store the bitvector in the bytes at the specified offset in little endian. The bitvector size must be a multiple of 8 or Invalid_argument
is thrown
val of_bytes : size:int -> bytes -> t
Read a bitvector from a bytes data (little endian)
val bytes_load : size:int -> bytes -> int -> t
Load a bitvector of size
bits from the bytes at the specified offset (little endian). size
must be a multiple of 8 or Invalid_argument
is thrown
val of_string : ?base:int -> size:int -> string -> t
Parse a string with specified base
(10 if unspecified) and return a bitvector of size size
. If the string is too big, the integer is still parsed and then wrapped modulo 2^size
val of_substring : ?base:int -> size:int -> pos:int -> len:int -> string -> t
Same as of_string
but on the substring starting at pos
of length len
.
val to_string : ?base:int -> ?unsigned:bool -> ?force_width:bool -> ?prefix:bool -> t -> string
Convert the value to a string representation in the specified base
.
base
can only be 2, 8, 10 or 16, otherwise the function fails.
Set unsigned
to true to have unsigned values (signed by default).
Set prefix
to true to have the 0x/0o/0b
prefix (no prefix by default)
Set force_width
to false to not have a digit length matching the bitvector length, otherwise leading zeros will be inserted to match the length.
val to_smt : t -> string
Convert a bitvector to the SMTLib format
val add : t -> t -> t
Add the values. Result is of the same size as the inputs.
Throw SizeMismatch
if sizes differ
val sub : t -> t -> t
Subtract the values. Result is of the same size as the inputs.
Throw SizeMismatch
if sizes differ
val neg : t -> t
Negate the value. Wrap if the value is the smaller integer (It will stay the smallest integer)
val mul : t -> t -> t
Multiply the values. Result is of the same size as the inputs.
Throw SizeMismatch
if sizes differ
val sdiv : t -> t -> t
Divide the values as signed integers. Result is of the same size as the inputs.
It rounds the result toward zero.
Throw SizeMismatch
if sizes differ.
Throw Division_by_zero
if there is a division by zero.
val srem : t -> t -> t
Take the remainder of the signed division. Result is of the same size as the inputs.
a = sdiv a b * b + srem a b
Throw SizeMismatch
if sizes differ.
Throw Division_by_zero
if there is a division by zero.
val smod : t -> t -> t
Take the signed modulo. The result has the sign of the divisor. Result is of the same size as the inputs.
Throw SizeMismatch
if sizes differ.
Throw Division_by_zero
if there is a division by zero.
val udiv : t -> t -> t
Divide the values as unsigned integers. Result is of the same size as the inputs.
Throw SizeMismatch
if sizes differ
Throw Division_by_zero
if there is a division by zero.
val urem : t -> t -> t
Get the remainder of the unsigned division. Result if of the same size as the inputs.
a = udiv a b * b + urem a b
Throw SizeMismatch
if sizes differ
Throw Division_by_zero
if there is a division by zero.
val logand : t -> t -> t
Bitwise and of the values. Result is of the same size as the inputs.
Throw SizeMismatch
if sizes differ
val logor : t -> t -> t
Bitwise or of the values. Result is of the same size as the inputs.
Throw SizeMismatch
if sizes differ
val logxor : t -> t -> t
Bitwise xor of the values. Result is of the same size as the inputs.
Throw SizeMismatch
if sizes differ
val redor : t -> bool
Do an or of all the bits in the bitvector
val redand : t -> bool
Do an and of all the bits in the bitvector
val shift_left_bv : t -> t -> t
Same as shift_left
but the second argument is also a bitvector of any size interpreted as unsigned
val shift_right_arith : t -> int -> t
Do an arithmetic right shift (copy the sign bit). The second argument must be non-negative
val shift_right_arith_bv : t -> t -> t
Same as shift_right_arith
but the second argument is also a bitvector of any size interpreted as unsigned
val shift_right_logic : t -> int -> t
Do an logical right shift (insert zeroes). The second argument must be non-negative
val shift_right_logic_bv : t -> t -> t
Same as shift_right_logic
but the second argument is also a bitvector of any size interpreted as unsigned
Divisions do not have any operators because signed and unsigned division have different semantics
Utils.BitVec
This module provides an interface for a bit vector of dynamic size.
For now this is entirely based on zarith
.
TODO: It could be nice to export this as a separate library on opam at some point
The value of type t is semantically pure and can be compare with polymorphic operators. It will compare the size first, then the value.
The size of bit vectors must always be strictly positive.
Raise when the runtime size do not match on operation that require so (like add
)
val size : t -> int
The size of the bitvector
val zero : size:int -> t
The bitvector representing 0 of specified size
val one : size:int -> t
The bitvector representing 1 of specified size
val minus_one : size:int -> t
The bitvector representing -1 of specified size
val to_z : t -> Z.t
To a signed big integer
val to_uz : t -> Z.t
To an unsigned big integer
val of_z : size:int -> Z.t -> t
Of bit integer. Wrapped modulo 2^size
.
val to_int : t -> int
To a signed integer. Fail if it doesn't fit
val to_uint : t -> int
To an unsigned integer. Fail if it doesn't fit without wrapping i.e the result is still positive
val of_int : size:int -> int -> t
Of integer. Wrapped modulo size
.
val to_bool : t -> bool
Convert a one size bitvector to bool. Throw SizeMismatch
if the bitvector is not one-sized
val of_bool : bool -> t
Create a one-sized bitvector representing the boolean
val to_bytes : t -> bytes
Return the shortest bytes that represent the bitvector in little-endian. There may be extra bits (if size is not a multiple of 8) which are zeros.
This bytes may be shorter that the bitvector size, for example the bitvector 1 of size 64bits, will still be returned by this function as a single byte 1. For another behavior, see to_bytes_exact
.
val to_bytes_exact : t -> bytes
Return a bytes representation of mininal length to encompass the whole bitvector size. Extra bits (if size is not a multiple of 8) are zeros.
val bytes_store : bytes -> int -> t -> unit
Store the bitvector in the bytes at the specified offset in little endian. The bitvector size must be a multiple of 8 or Invalid_argument
is thrown
val of_bytes : size:int -> bytes -> t
Read a bitvector from a bytes data (little endian)
val bytes_load : size:int -> bytes -> int -> t
Load a bitvector of size
bits from the bytes at the specified offset (little endian). size
must be a multiple of 8 or Invalid_argument
is thrown
val of_string : ?base:int -> size:int -> string -> t
Parse a string with specified base
(10 if unspecified) and return a bitvector of size size
. If the string is too big, the integer is still parsed and then wrapped modulo 2^size
val of_substring : ?base:int -> size:int -> pos:int -> len:int -> string -> t
Same as of_string
but on the substring starting at pos
of length len
.
val to_string :
+ ?base:int ->
+ ?unsigned:bool ->
+ ?force_width:bool ->
+ ?prefix:bool ->
+ t ->
+ string
Convert the value to a string representation in the specified base
.
base
can only be 2, 8, 10 or 16, otherwise the function fails.
Set unsigned
to true to have unsigned values (signed by default).
Set prefix
to true to have the 0x/0o/0b
prefix (no prefix by default)
Set force_width
to false to not have a digit length matching the bitvector length, otherwise leading zeros will be inserted to match the length.
val to_smt : t -> string
Convert a bitvector to the SMTLib format
val pp_smt : t -> Pp.document
Print a bitvector with the SMTLib format
Add the values. Result is of the same size as the inputs.
Throw SizeMismatch
if sizes differ
Subtract the values. Result is of the same size as the inputs.
Throw SizeMismatch
if sizes differ
Negate the value. Wrap if the value is the smaller integer (It will stay the smallest integer)
Multiply the values. Result is of the same size as the inputs.
Throw SizeMismatch
if sizes differ
Divide the values as signed integers. Result is of the same size as the inputs.
It rounds the result toward zero.
Throw SizeMismatch
if sizes differ.
Throw Division_by_zero
if there is a division by zero.
Take the remainder of the signed division. Result is of the same size as the inputs.
a = sdiv a b * b + srem a b
Throw SizeMismatch
if sizes differ.
Throw Division_by_zero
if there is a division by zero.
Take the signed modulo. The result has the sign of the divisor. Result is of the same size as the inputs.
Throw SizeMismatch
if sizes differ.
Throw Division_by_zero
if there is a division by zero.
Divide the values as unsigned integers. Result is of the same size as the inputs.
Throw SizeMismatch
if sizes differ
Throw Division_by_zero
if there is a division by zero.
Get the remainder of the unsigned division. Result if of the same size as the inputs.
a = udiv a b * b + urem a b
Throw SizeMismatch
if sizes differ
Throw Division_by_zero
if there is a division by zero.
Bitwise and of the values. Result is of the same size as the inputs.
Throw SizeMismatch
if sizes differ
Bitwise or of the values. Result is of the same size as the inputs.
Throw SizeMismatch
if sizes differ
Bitwise xor of the values. Result is of the same size as the inputs.
Throw SizeMismatch
if sizes differ
val redor : t -> bool
Do an or of all the bits in the bitvector
val redand : t -> bool
Do an and of all the bits in the bitvector
Same as shift_left
but the second argument is also a bitvector of any size interpreted as unsigned
Do an arithmetic right shift (copy the sign bit). The second argument must be non-negative
Same as shift_right_arith
but the second argument is also a bitvector of any size interpreted as unsigned
Do an logical right shift (insert zeroes). The second argument must be non-negative
Same as shift_right_logic
but the second argument is also a bitvector of any size interpreted as unsigned
extract bv a b
extract bits a
to b
included from bv
. Indices start at 0
Divisions do not have any operators because signed and unsigned division have different semantics
Utils.Bits
module Int = IntBits
val check_index : int -> int -> unit
check_index length i
Check that the index i
is valid to index an bytes of length length
. Throw Invalid_argument
if not
val check_range : int -> int -> int -> unit
check_range length i l
Check that the range [i;i+l)
is inside a bytes of length length
. Throw Invalid_argument
if not
val make : int -> bool -> bytes
Create a bytes
large enough to store the specified amount of bits and initialize them as specified by the boolean
val unsafe_get : bytes -> int -> bool
Unsafe version of get
val get : bytes -> int -> bool
Get a bit at a specific index. See unsafe_get
val unsafe_set : bytes -> int -> unit
Unsafe version of set
val set : bytes -> int -> unit
Set a bit at a specific index. See unsafe_set
val unsafe_clear : bytes -> int -> unit
Unsafe version of clear
val clear : bytes -> int -> unit
Clear a bit at a specific index. See unsafe_clear
val unsafe_setb : bytes -> int -> bool -> unit
Unsafe version of setb
val setb : bytes -> int -> bool -> unit
Set a bit at a specific index according to a boolean. See unsafe_setb
val unsafe_blit_to_int : bytes -> int -> Int.t -> int -> int -> Int.t
Unsafe version of blit_to_int
val blit_to_int : bytes -> int -> Int.t -> int -> int -> Int.t
blit_to_int src isrc dest idest len
blits the bits in range [isrc;isrc+len)
of src
to the range [idest;idest + len)
of dest
and returns the result. See unsafe_blit_to_int
.
val unsafe_blit_of_int : Int.t -> int -> bytes -> int -> int -> unit
Unsafe version of blit_of_int
val blit_of_int : Int.t -> int -> bytes -> int -> int -> unit
blit_of_int src isrc dest idest len
blits the bits in range [isrc;isrc+len)
or src to the range [idest;idest + len)
of dest by mutation. See unsafe_blit_of_int
.
Utils.Bits
Like bytes, but for bit level manipulation. The underlying type is still bytes and thus the size has to be a multiple of 8.
The indexing is little endian: bit 9 is least significant bit of byte 1
module Int = IntBits
check_index length i
Check that the index i
is valid to index an bytes of length length
. Throw Invalid_argument
if not
check_range length i l
Check that the range [i;i+l)
is inside a bytes of length length
. Throw Invalid_argument
if not
Create a bytes
large enough to store the specified amount of bits and initialize them as specified by the boolean
Unsafe version of get
Get a bit at a specific index. See unsafe_get
Unsafe version of set
Set a bit at a specific index. See unsafe_set
Unsafe version of clear
Clear a bit at a specific index. See unsafe_clear
Unsafe version of setb
Set a bit at a specific index according to a boolean. See unsafe_setb
Unsafe version of blit_to_int
blit_to_int src isrc dest idest len
blits the bits in range [isrc;isrc+len)
of src
to the range [idest;idest + len)
of dest
and returns the result. See unsafe_blit_to_int
.
val unsafe_blit_of_int : Int.t -> int -> bytes -> int -> int -> unit
Unsafe version of blit_of_int
val blit_of_int : Int.t -> int -> bytes -> int -> int -> unit
blit_of_int src isrc dest idest len
blits the bits in range [isrc;isrc+len)
or src to the range [idest;idest + len)
of dest by mutation. See unsafe_blit_of_int
.
Utils.BytesSeq
val length : t -> int
Get the length of the byteseq in bytes
val to_hex : t -> string
Convert the byte sequence to an hexadecimal string
val to_hex_rev : t -> string
Convert the byte sequence to an reversed hexadecimal string. This will print it like a big-endian integer.
val of_hex : string -> t
Parse the string as hexadecimal like A4B767DF and create a bytes of this a binary data and then a bytesSeq view of it
val sub : t -> int -> int -> t
sub bs start len
Extract a sub range [start:start+len)
of a byte sequence. This is O(1)
bytes
and raw stringval blit : t -> int -> bytes -> int -> int -> unit
blit src srcoff dst dstoff len
copies len
bytes from bytes sequence src
, starting at index srcoff
, to bytes dst
, starting at index dstoff
.
See Bytes.blit
.
val of_bytes : bytes -> t
Create a view of the whole bytes
val of_string : string -> t
Create a view of the whole string as raw bytes
val to_string : t -> string
Create a copy of the view in a string
val to_array : t -> char array
Convert to a char array
val of_array : char array -> t
Convert from a char array
val get : t -> int -> char
Get the bytes at the offset in the byte sequence
val get16le : t -> int -> int
Get a 16 bit integer at the offset in the byte sequence as little endian
val get16be : t -> int -> int
Get a 16 bit integer at the offset in the byte sequence as big endian
val get32le : t -> int -> int32
Get a 32 bit integer at the offset in the byte sequence as little endian
val get32be : t -> int -> int32
Get a 32 bit integer at the offset in the byte sequence as big endian
val get64le : t -> int -> int64
Get a 64 bit integer at the offset in the byte sequence as little endian
val get64be : t -> int -> int64
Get a 64 bit integer at the offset in the byte sequence as big endian
val getintle : t -> int -> int
Get an Ocaml int at the offset in the byte sequence as little endian. The number of bytes read is 4 if Sys.int_size
is 31 and 8 if Sys.int_size
is 63
val getbs : len:int -> t -> int -> t
Get a byte sequence of length len
at the offset in another byte sequence
val getbvle : size:int -> t -> int -> BitVec.t
Get a BitVec
of size size
at the offset in the byte sequence as little endian
val getintle_ze : t -> int -> int
Get an Ocaml int at the offset in the byte sequence as little endian. The number of bytes read is 4 if Sys.int_size
is 31 and 8 if Sys.int_size
is 63. If the read goes beyond the end of the sequence, instead of failing, zeros are read.
Iterators over a byte sequence. If the length of the byte sequence is not a multiple of the step of the iteration then the trailing odd bytes are not iterated over.
val iter : (char -> unit) -> t -> unit
val iter16le : (int -> unit) -> t -> unit
val iter16be : (int -> unit) -> t -> unit
val iter32le : (int32 -> unit) -> t -> unit
val iter32be : (int32 -> unit) -> t -> unit
val iter64le : (int64 -> unit) -> t -> unit
val iter64be : (int64 -> unit) -> t -> unit
val iterbs : len:int -> (t -> unit) -> t -> unit
Iterate over the byte sequence by bytesequence of length len
. If the total byte sequence is not of length a multiple of len
then that iterated value will be shorter
val fold_left : ('a -> char -> 'a) -> 'a -> t -> 'a
val fold_left16le : ('a -> int -> 'a) -> 'a -> t -> 'a
val fold_left16be : ('a -> int -> 'a) -> 'a -> t -> 'a
val fold_left32le : ('a -> int32 -> 'a) -> 'a -> t -> 'a
val fold_left32be : ('a -> int32 -> 'a) -> 'a -> t -> 'a
val fold_left64le : ('a -> int64 -> 'a) -> 'a -> t -> 'a
val fold_left64be : ('a -> int64 -> 'a) -> 'a -> t -> 'a
val fold_leftbs : len:int -> ('a -> t -> 'a) -> 'a -> t -> 'a
val to_list : t -> char list
val to_list16le : t -> int list
val to_list16be : t -> int list
val to_list32le : t -> int32 list
val to_list32be : t -> int32 list
val to_list64le : t -> int64 list
val to_list64be : t -> int64 list
val to_listbs : len:int -> t -> t list
Cut a byte sequence into a list of byte sequences of length len
, (and a shorter last one if the total len is not a multiple of len)
val output : Stdlib.out_channel -> t -> unit
Output the raw data of the byte sequence on the output channel
val input : Stdlib.in_channel -> t
Output the raw date of the byte sequence of the input channel
val pp : t -> Utils.Pp.document
Pretty print a byte sequence as space separated bytes like ab cd ef
.
Here "ab" is the byte number 0 and "ef" is the byte number 2.
val ppc : t -> Utils.Pp.document
Pretty print a byte sequence as an hexadecimal string like abcdef
Here "ab" is the byte number 0 and "ef" is the byte number 2.
This can also be seen as printing the bytesequence as a single integer encoded in big endian format.
val ppint : t -> Utils.Pp.document
Pretty print a byte sequence as an hexadecimal integer (in little endian). The byte order is reversed compared to ppc
For example the byte sequence ab cd ef
will be printed as efcdab
where "ab" is the byte number 0 and "ef" is the byte number 2.
val ppby : by:int -> t -> Utils.Pp.document
Pretty print a byte sequence by step of by
bytes. Each block is pretty printed as an hex string like ppc
and blocks are separated by spaces.
val ppbyint : by:int -> t -> Utils.Pp.document
Pretty print a byte sequence by step of by
bytes. Each block is pretty printed as a reversed hex string i.e like an integer of length by
. Thus each block will printed like with ppcint
Blocks are separated by spaces.
For example to print a byte sequence as a space separated list of little-endian integers do:
ppbyint ~by:4 bs
Utils.BytesSeq
This module represent a byte sub view on a bytes
object. Contrary to Bytes
it is a non-owning immutable view. It do not prevent the original bytes from being modified, and the changes will be propagated in the view. It is additional sugar on top of Linksem's Byte_sequence_wrapper
About all the suffixed function:
BitVec
the specified size
as read in little endian.val length : t -> int
Get the length of the byteseq in bytes
val to_hex : t -> string
Convert the byte sequence to an hexadecimal string
val to_hex_rev : t -> string
Convert the byte sequence to an reversed hexadecimal string. This will print it like a big-endian integer.
val of_hex : string -> t
Parse the string as hexadecimal like A4B767DF and create a bytes of this a binary data and then a bytesSeq view of it
sub bs start len
Extract a sub range [start:start+len)
of a byte sequence. This is O(1)
front i bs
Take the first i
bytes of bs
and discard the rest. Equivalent to sub bs 0 i
back i bs
Take the last i
bytes of bs
and discard the rest. Equivalent to sub bs i (length bs - i)
bytes
and raw stringval blit : t -> int -> bytes -> int -> int -> unit
blit src srcoff dst dstoff len
copies len
bytes from bytes sequence src
, starting at index srcoff
, to bytes dst
, starting at index dstoff
.
See Bytes.blit
.
val of_bytes : bytes -> t
Create a view of the whole bytes
val of_string : string -> t
Create a view of the whole string as raw bytes
val to_string : t -> string
Create a copy of the view in a string
val bytes_sub : bytes -> int -> int -> t
Create a byte sequence view of a specified range of a bytes. See sub
val to_array : t -> char array
Convert to a char array
val of_array : char array -> t
Convert from a char array
val get : t -> int -> char
Get the bytes at the offset in the byte sequence
val get16le : t -> int -> int
Get a 16 bit integer at the offset in the byte sequence as little endian
val get16be : t -> int -> int
Get a 16 bit integer at the offset in the byte sequence as big endian
val get32le : t -> int -> int32
Get a 32 bit integer at the offset in the byte sequence as little endian
val get32be : t -> int -> int32
Get a 32 bit integer at the offset in the byte sequence as big endian
val get64le : t -> int -> int64
Get a 64 bit integer at the offset in the byte sequence as little endian
val get64be : t -> int -> int64
Get a 64 bit integer at the offset in the byte sequence as big endian
val getintle : t -> int -> int
Get an Ocaml int at the offset in the byte sequence as little endian. The number of bytes read is 4 if Sys.int_size
is 31 and 8 if Sys.int_size
is 63
Get a byte sequence of length len
at the offset in another byte sequence
Get a BitVec
of size size
at the offset in the byte sequence as little endian
val getintle_ze : t -> int -> int
Get an Ocaml int at the offset in the byte sequence as little endian. The number of bytes read is 4 if Sys.int_size
is 31 and 8 if Sys.int_size
is 63. If the read goes beyond the end of the sequence, instead of failing, zeros are read.
Iterators over a byte sequence. If the length of the byte sequence is not a multiple of the step of the iteration then the trailing odd bytes are not iterated over.
val iter : (char -> unit) -> t -> unit
val iter16le : (int -> unit) -> t -> unit
val iter16be : (int -> unit) -> t -> unit
val iter32le : (int32 -> unit) -> t -> unit
val iter32be : (int32 -> unit) -> t -> unit
val iter64le : (int64 -> unit) -> t -> unit
val iter64be : (int64 -> unit) -> t -> unit
Iterate over the byte sequence by bytesequence of length len
. If the total byte sequence is not of length a multiple of len
then that iterated value will be shorter
val fold_left : ('a -> char -> 'a) -> 'a -> t -> 'a
val fold_left16le : ('a -> int -> 'a) -> 'a -> t -> 'a
val fold_left16be : ('a -> int -> 'a) -> 'a -> t -> 'a
val fold_left32le : ('a -> int32 -> 'a) -> 'a -> t -> 'a
val fold_left32be : ('a -> int32 -> 'a) -> 'a -> t -> 'a
val fold_left64le : ('a -> int64 -> 'a) -> 'a -> t -> 'a
val fold_left64be : ('a -> int64 -> 'a) -> 'a -> t -> 'a
val to_list : t -> char list
val to_list16le : t -> int list
val to_list16be : t -> int list
val to_list32le : t -> int32 list
val to_list32be : t -> int32 list
val to_list64le : t -> int64 list
val to_list64be : t -> int64 list
Cut a byte sequence into a list of byte sequences of length len
, (and a shorter last one if the total len is not a multiple of len)
val output : Stdlib.out_channel -> t -> unit
Output the raw data of the byte sequence on the output channel
val input : Stdlib.in_channel -> t
Output the raw date of the byte sequence of the input channel
val pp : t -> Pp.document
Pretty print a byte sequence as space separated bytes like ab cd ef
.
Here "ab" is the byte number 0 and "ef" is the byte number 2.
val ppc : t -> Pp.document
Pretty print a byte sequence as an hexadecimal string like abcdef
Here "ab" is the byte number 0 and "ef" is the byte number 2.
This can also be seen as printing the bytesequence as a single integer encoded in big endian format.
val ppint : t -> Pp.document
Pretty print a byte sequence as an hexadecimal integer (in little endian). The byte order is reversed compared to ppc
For example the byte sequence ab cd ef
will be printed as efcdab
where "ab" is the byte number 0 and "ef" is the byte number 2.
val ppby : by:int -> t -> Pp.document
Pretty print a byte sequence by step of by
bytes. Each block is pretty printed as an hex string like ppc
and blocks are separated by spaces.
val ppbyint : by:int -> t -> Pp.document
Pretty print a byte sequence by step of by
bytes. Each block is pretty printed as a reversed hex string i.e like an integer of length by
. Thus each block will printed like with ppcint
Blocks are separated by spaces.
For example to print a byte sequence as a space separated list of little-endian integers do:
ppbyint ~by:4 bs
Test.Cache
type key
= Key.t
type value
= Value.t
type epoch
= UnitEpoch.t
type t
= Make(Key)(Value)(UnitEpoch).t
The type that represent the cache in RAM.
val make : ?fake:bool -> string -> epoch -> t
Build a new cache management object with a name and an epoch If fake
is set, the cache will not touch the disk and behave as a plain Hashtbl.
Test.Cache
type key = Key.t
type value = Value.t
type epoch = UnitEpoch.t
type t = Make(Key)(Value)(UnitEpoch).t
The type that represent the cache in RAM.
Build a new cache management object with a name and an epoch If fake
is set, the cache will not touch the disk and behave as a plain Hashtbl.
Get a value from the cache or None
if no value is bound to the key
Test.Single
type value
= Value.t
type t
= Single(Value).t
The type that represent the cache in RAM.
val make : ?fake:bool -> string -> t
Build a new cache management object with a name If fake
is set, the cache will not touch the disk and behave as a plain ref.
val clear : t -> unit
Clear the value in the cache
Test.Single
type value = Value.t
type t = Single(Value).t
The type that represent the cache in RAM.
val make : ?fake:bool -> string -> t
Build a new cache management object with a name If fake
is set, the cache will not touch the disk and behave as a plain ref.
val clear : t -> unit
Clear the value in the cache
Test.Value
val to_file : string -> t -> unit
Serialize the value to a file
Test.Value
val to_file : string -> t -> unit
Serialize the value to a file
Cmd.Test
This module provide a int -> string cache for testing pruposes. It can be tested with read-dwarf cache --test
module Cache : sig ... end
module Single : sig ... end
Cmd.Test
This module provide a int -> string cache for testing pruposes. It can be tested with read-dwarf cache --test
Cache.Cmd
The cache command line. TODO documentation of the inside
module Test : sig ... end
This module provide a int -> string cache for testing pruposes. It can be tested with read-dwarf cache --test
val clear : bool Cmdliner.Term.t
val all : bool Cmdliner.Term.t
val test : bool Cmdliner.Term.t
val list : bool Cmdliner.Term.t
val arg : string option Cmdliner.Term.t
val fake : bool Cmdliner.Term.t
val op_f2m : bool -> bool -> bool -> operation Cmdliner.Term.ret
Input flags to mode conversion
val operation_term : operation Cmdliner.Term.t
val test : bool -> unit
The testing mini command line to test the Test
cache
val dostuff : operation -> bool -> string option -> bool -> unit
Do the caching operation op
Cache.Cmd
The cache command line. TODO documentation of the inside
module Test : sig ... end
This module provide a int -> string cache for testing pruposes. It can be tested with read-dwarf cache --test
val op_f2m : bool -> bool -> bool -> operation Cmdliner.Term.ret
Input flags to mode conversion
val operation_term : operation Cmdliner.Term.t
The testing mini command line to test the Test
cache
val dostuff : operation -> bool -> string option -> bool -> unit
Do the caching operation op
Cache.IntEpoch
Cache.IntEpoch
Make.1-Key
val to_file : string -> t -> unit
Write the necessary information to retrieve the key in a file. The filename supplied is without the key extension. The implementer must call to_keyfile
on it
val of_file : int -> string -> t
Build back the key from the hash and a storage file. The filename supplied is without the key extension. The implementer must call to_keyfile
on it.
If you start with a key
and a file
, then doing to_file file key
and then of_file (hash key) file
must return something equal
to key.
Make.Key
val to_file : string -> t -> unit
Write the necessary information to retrieve the key in a file. The filename supplied is without the key extension. The implementer must call to_keyfile
on it
val of_file : int -> string -> t
Build back the key from the hash and a storage file. The filename supplied is without the key extension. The implementer must call to_keyfile
on it.
If you start with a key
and a file
, then doing to_file file key
and then of_file (hash key) file
must return something equal
to key.
Make.2-Value
val to_file : string -> t -> unit
Serialize the value to a file
Make.Value
val to_file : string -> t -> unit
Serialize the value to a file
Make.3-Epoch
Make.Epoch
Cache.Make
The Key must provide the Key
interface, where the values must satisfy the Value
interface.
The map is stored on the disk in a simple way. First the key is hashed and this hashed gives a filename with int_to_file
. If there is no collision, then the value is stored in that file and the key is stored (optionally) in hash.key
file (See Key
for details).
If there is collision then the filename is a directory containing numbered files of all the key-value pairs. Again the value is in file named n
when the key is (optionally) in file named n.key
.
type key
= Key.t
The type of keys
type value
= Value.t
The type of values
type epoch
= Epoch.t
The type of the epoch
val make : ?fake:bool -> string -> epoch -> t
Build a new cache management object with a name and an epoch If fake
is set, the cache will not touch the disk and behave as a plain Hashtbl.
Cache.Make
The Key must provide the Key
interface, where the values must satisfy the Value
interface.
The map is stored on the disk in a simple way. First the key is hashed and this hashed gives a filename with int_to_file
. If there is no collision, then the value is stored in that file and the key is stored (optionally) in hash.key
file (See Key
for details).
If there is collision then the filename is a directory containing numbered files of all the key-value pairs. Again the value is in file named n
when the key is (optionally) in file named n.key
.
type key = Key.t
The type of keys
type value = Value.t
The type of values
type epoch = Epoch.t
The type of the epoch
Build a new cache management object with a name and an epoch If fake
is set, the cache will not touch the disk and behave as a plain Hashtbl.
Get a value from the cache or None
if no value is bound to the key
Single.1-Value
val to_file : string -> t -> unit
Serialize the value to a file
Single.Value
val to_file : string -> t -> unit
Serialize the value to a file
Cache.Single
The functor is to make a single cached value. This do not support epochs (Yet)
TODO: Maybe the code would be simpler if this was a map from unit to the value.
type value
= Value.t
The type of the stored value
val make : ?fake:bool -> string -> t
Build a new cache management object with a name If fake
is set, the cache will not touch the disk and behave as a plain ref.
val clear : t -> unit
Clear the value in the cache
Cache.Single
The functor is to make a single cached value. This do not support epochs (Yet)
TODO: Maybe the code would be simpler if this was a map from unit to the value.
type value = Value.t
The type of the stored value
val make : ?fake:bool -> string -> t
Build a new cache management object with a name If fake
is set, the cache will not touch the disk and behave as a plain ref.
val clear : t -> unit
Clear the value in the cache
Cache.UnitEpoch
A dummy epochs implementation, if not epoch is needed
Cache.UnitEpoch
A dummy epochs implementation, if not epoch is needed
Utils.Cache
This section is not part of the external API, but creating an mli file here seemed needlessly annoying.
val find_dir : unit -> string
Find the current cache folder as described in the README.
When searching for a cache, this function will search if there already is a base_dir
directory either in the current directory or one of its parent and use the closest one it find. If it finds none and need a cache, it will create a new directory named base_dir
in the current directory. Therefore the returned directory always exists.
val removedir : string -> unit
Remove a directory and all it's content.
TODO: Make that Windows friendly
TODO move that in Files
val file_type : string -> file_type
Give the file type of the given type. Do not count the number of entry in ther
module type Key = sig ... end
The interface of keys.
module IntKey : Key with type Key.t = int
An implementation of Key
on ints where hash is the identity and no file is written
module type Value = sig ... end
The interface of values: They just need to be able to be serialized to files. This is isn't plain Marshal because some values may want a human readable format.
module type Epoch = sig ... end
A cache can be indexed by an Epoch. When reading a cache with an incompatible epoch, then the cache is deleted on load. Use UnitEpoch
to not have this functionality. Single
caches currently do not support epochs.
module UnitEpoch : sig ... end
A dummy epochs implementation, if not epoch is needed
module IntEpoch : sig ... end
module Cmd : sig ... end
The cache command line. TODO documentation of the inside
Utils.Cache
This module implement a caching system i.e a persistant structure stored on the disk.
A cache can be either:
A cache must be uniquely named and will be stored in find_dir
()/name
. This will be a directory in case of map and a file in case of a single value
This section is not part of the external API, but creating an mli file here seemed needlessly annoying.
Find the current cache folder as described in the README.
When searching for a cache, this function will search if there already is a base_dir
directory either in the current directory or one of its parent and use the closest one it find. If it finds none and need a cache, it will create a new directory named base_dir
in the current directory. Therefore the returned directory always exists.
val file_type : string -> file_type
Give the file type of the given type. Do not count the number of entry in ther
movekey old new
moves a key correponding to the given filenames, if they exist
module type Key = sig ... end
The interface of keys.
An implementation of Key
on ints where hash is the identity and no file is written
module type Value = sig ... end
The interface of values: They just need to be able to be serialized to files. This is isn't plain Marshal because some values may want a human readable format.
module type Epoch = sig ... end
module UnitEpoch : sig ... end
A dummy epochs implementation, if not epoch is needed
module IntEpoch : sig ... end
The functor is to make a single cached value. This do not support epochs (Yet)
module Cmd : sig ... end
The cache command line. TODO documentation of the inside
Cache.Epoch
A cache can be indexed by an Epoch. When reading a cache with an incompatible epoch, then the cache is deleted on load. Use UnitEpoch
to not have this functionality. Single
caches currently do not support epochs.
Cache.Epoch
A cache can be indexed by an Epoch. When reading a cache with an incompatible epoch, then the cache is deleted on load. Use UnitEpoch
to not have this functionality. Single
caches currently do not support epochs.
Cache.Key
The interface of keys.
Key are use to index values in the cache, however instead of using the key directly as a file name, the hexadecimal hash
of the key is used trough int_to_file
. See Make
for more details.
There are two main way of managing keys:
to_keyfile
file. See to_file
.An implementation can do a mix of both i.e. Putting extra information only for value for which there is hash collision.
val to_file : string -> t -> unit
Write the necessary information to retrieve the key in a file. The filename supplied is without the key extension. The implementer must call to_keyfile
on it
val of_file : int -> string -> t
Build back the key from the hash and a storage file. The filename supplied is without the key extension. The implementer must call to_keyfile
on it.
If you start with a key
and a file
, then doing to_file file key
and then of_file (hash key) file
must return something equal
to key.
Cache.Key
The interface of keys.
Key are use to index values in the cache, however instead of using the key directly as a file name, the hexadecimal hash
of the key is used trough int_to_file
. See Make
for more details.
There are two main way of managing keys:
to_keyfile
file. See to_file
.An implementation can do a mix of both i.e. Putting extra information only for value for which there is hash collision.
val to_file : string -> t -> unit
Write the necessary information to retrieve the key in a file. The filename supplied is without the key extension. The implementer must call to_keyfile
on it
val of_file : int -> string -> t
Build back the key from the hash and a storage file. The filename supplied is without the key extension. The implementer must call to_keyfile
on it.
If you start with a key
and a file
, then doing to_file file key
and then of_file (hash key) file
must return something equal
to key.
Cache.S
The output signature of Make
val make : ?fake:bool -> string -> epoch -> t
Build a new cache management object with a name and an epoch If fake
is set, the cache will not touch the disk and behave as a plain Hashtbl.
Cache.S
The output signature of Make
Build a new cache management object with a name and an epoch If fake
is set, the cache will not touch the disk and behave as a plain Hashtbl.
Get a value from the cache or None
if no value is bound to the key
Cache.SingleS
The signature of the output of Single
val make : ?fake:bool -> string -> t
Build a new cache management object with a name If fake
is set, the cache will not touch the disk and behave as a plain ref.
val clear : t -> unit
Clear the value in the cache
Cache.SingleS
The signature of the output of Single
val make : ?fake:bool -> string -> t
Build a new cache management object with a name If fake
is set, the cache will not touch the disk and behave as a plain ref.
val clear : t -> unit
Clear the value in the cache
Cache.Value
The interface of values: They just need to be able to be serialized to files. This is isn't plain Marshal because some values may want a human readable format.
val to_file : string -> t -> unit
Serialize the value to a file
Cache.Value
The interface of values: They just need to be able to be serialized to files. This is isn't plain Marshal because some values may want a human readable format.
val to_file : string -> t -> unit
Serialize the value to a file
Cmd.IOServer
This module provide functionality to run command in the background and communicate with it via redirection on it's standard input and output.
An example of use is in Z3
type t
=
{
cmd : cmd; | |
input : Stdlib.in_channel; | The output of the command from which answer can be read |
output : Stdlib.out_channel; | The input of the server on which request can be sent |
}
The type of pipe IO server
Cmd.IOServer
This module provide functionality to run command in the background and communicate with it via redirection on it's standard input and output.
An example of use is in Z3
type t = {
cmd : cmd;
input : Stdlib.in_channel;
The output of the command from which answer can be read
*)output : Stdlib.out_channel;
The input of the server on which request can be sent
*)}
The type of pipe IO server
Cmd.SocketServer
This module provide functionality for a socket server with which one can communicate on a sockets.
An example of use is in Isla.Server
val start : name:string -> (string -> cmd) -> t
Start the server with provided name and wait for it to connect to the socket. Then build the Server
.t object. The function argument must take a socket name and give a valid command line to call the server process and make it connect to the socket.
val stop : t -> unit
Stop the server and cut the connection, wait for the subprocess to die and then delete the socket
May throw Crash
on error.
val read_byte : t -> int
Read a single byte from the server
val read_string : t -> string
Read a string with the following format:
| header : 4 bytes | data : header bytes |
In other words, read a 4 bytes number, then read that number of bytes into a string
val write_string : t -> string -> unit
Write a string in the same binary format as read_string
Cmd.SocketServer
This module provide functionality for a socket server with which one can communicate on a sockets.
An example of use is in Isla.Server
Start the server with provided name and wait for it to connect to the socket. Then build the Server.t
object. The function argument must take a socket name and give a valid command line to call the server process and make it connect to the socket.
val stop : t -> unit
Stop the server and cut the connection, wait for the subprocess to die and then delete the socket
May throw Crash
on error.
val read_byte : t -> int
Read a single byte from the server
val read_string : t -> string
Read a string with the following format:
| header : 4 bytes | data : header bytes |
In other words, read a 4 bytes number, then read that number of bytes into a string
val write_string : t -> string -> unit
Write a string in the same binary format as read_string
Utils.Cmd
type cmd
= string array
The type of a command to be sent. The program to call must be the item 0 of the array
exception
Crash of cmd * Unix.process_status
If a program do not return with a 0 exit code, we throw that exception giving the command that failed and the invalid status it returned
val call : cmd -> unit
Call the command without redirecting anything. Wait for completion before returning.
May throw Crash
on error.
val call_send : cmd -> sender:(Stdlib.out_channel -> unit) -> unit
Call the command and then call sender
to send it some data on it's stdin
. Then wait for completion.
May throw Crash
on error.
val call_send_string : cmd -> string -> unit
Call the command, send it the string on it's standard input and wait for completion.
May throw Crash
on error.
val call_read : cmd -> reader:(Stdlib.in_channel -> 'a) -> 'a
Call the command and then call reader
to parse what the command outputs on it's stdout
. Then wait for completion and return the parsed value
May throw Crash
on error.
val call_read_string : cmd -> string
Call the command, wait for completion, and return it's stdout
in a string
val call_send_read : cmd -> sender:(Stdlib.out_channel -> unit) -> reader:(Stdlib.in_channel -> 'a) -> 'a
Call the command and then call sender
to send the input data on it's stdin
. Then call reader
to parse an answer from stdout
Then wait for completion and return the parsed value
May throw Crash
on error.
module IOServer : sig ... end
This module provide functionality to run command in the background and communicate with it via redirection on it's standard input and output.
module SocketServer : sig ... end
This module provide functionality for a socket server with which one can communicate on a sockets.
Utils.Cmd
This module provides high-level interaction with external processes.
This provide a functionality similar to Bos
), but this lib is still unstable.
There are two main mode of communication provided:
Programs can be launched in two modes
Calling programs can be done with call*
function like call
, call_read
, call_send
and call_send_read
and only support pipe interaction
Server like setups can be done in pipe mode with IOServer
and in socket mode with SocketServer
.
The type of a command to be sent. The program to call must be the item 0 of the array
exception Crash of cmd * Unix.process_status
If a program do not return with a 0 exit code, we throw that exception giving the command that failed and the invalid status it returned
val call : cmd -> unit
Call the command without redirecting anything. Wait for completion before returning.
May throw Crash
on error.
val call_send : cmd -> sender:(Stdlib.out_channel -> unit) -> unit
Call the command and then call sender
to send it some data on it's stdin
. Then wait for completion.
May throw Crash
on error.
val call_send_string : cmd -> string -> unit
Call the command, send it the string on it's standard input and wait for completion.
May throw Crash
on error.
val call_read : cmd -> reader:(Stdlib.in_channel -> 'a) -> 'a
Call the command and then call reader
to parse what the command outputs on it's stdout
. Then wait for completion and return the parsed value
May throw Crash
on error.
val call_read_string : cmd -> string
Call the command, wait for completion, and return it's stdout
in a string
val call_send_read :
+ cmd ->
+ sender:(Stdlib.out_channel -> unit) ->
+ reader:(Stdlib.in_channel -> 'a) ->
+ 'a
Call the command and then call sender
to send the input data on it's stdin
. Then call reader
to parse an answer from stdout
Then wait for completion and return the parsed value
May throw Crash
on error.
module IOServer : sig ... end
This module provide functionality to run command in the background and communicate with it via redirection on it's standard input and output.
module SocketServer : sig ... end
This module provide functionality for a socket server with which one can communicate on a sockets.
Utils.CmdlinerHelper
val setter : 'a Stdlib.ref -> 'a Cmdliner.Term.t -> unit Cmdliner.Term.t
Return a unit Term
that evaluates the input term and set the reference to the resulting value.
Due to the nature of Cmdliner, this may be evaluated multiple times, so setting the reference to anything else is dangerous.
val add_option : unit Cmdliner.Term.t -> 'a Cmdliner.Term.t -> 'a Cmdliner.Term.t
Add an unit term that need to be evaluated at the same time at the main term.
The order of evaluation is unspecified, but the option will be evaluated before the resulting term is returned.
val add_options : unit Cmdliner.Term.t list -> 'a Cmdliner.Term.t -> 'a Cmdliner.Term.t
Fold add_option on a list of option
val func_option : unit Cmdliner.Term.t -> 'a -> 'a Cmdliner.Term.t
Replaces Term.const but allow a unit terms (like the one generated by setter
) to be evaluated before the function is called
val func_options : unit Cmdliner.Term.t list -> 'a -> 'a Cmdliner.Term.t
Same as func_option
but with a list of unit terms
Utils.CmdlinerHelper
This module provide some Cmdliner
helper functions.
Return a unit Term
that evaluates the input term and set the reference to the resulting value.
Due to the nature of Cmdliner, this may be evaluated multiple times, so setting the reference to anything else is dangerous.
Add an unit term that need to be evaluated at the same time at the main term.
The order of evaluation is unspecified, but the option will be evaluated before the resulting term is returned.
Fold add_option on a list of option
Replaces Term.const but allow a unit terms (like the one generated by setter
) to be evaluated before the function is called
Same as func_option
but with a list of unit terms
Utils.Counter
val make : int -> t
Make a counter starting from the provided value. This means the first call to get
on that counter will be the input value:
let c = make 42 in
-assert(get c = 42);
val get : t -> int
Get the next value of the counter and increment it
val read : t -> int
Get the current value of the counter
val skip : t -> unit
Skip a value of the counter equivalent to ingore (get ...)
Utils.Counter
This module provide a small counter object which is just a int reference on which get
can be called to get an identifier and increment the reference
val make : int -> t
Make a counter starting from the provided value. This means the first call to get
on that counter will be the input value:
let c = make 42 in
+assert(get c = 42);
val get : t -> int
Get the next value of the counter and increment it
val read : t -> int
Get the current value of the counter
val skip : t -> unit
Skip a value of the counter equivalent to ingore (get ...)
Utils.Files
type 'a reader
= Stdlib.in_channel -> 'a
The type of a reader that read an object from a channel. Function like input_*
in Stdlib
type 'a writer
= Stdlib.out_channel -> 'a -> unit
The type of a writer of 'a. Function named output_*
in Stdlib
val input_sexp : Stdlib.in_channel -> string
Reads a S-expression from the input, line by line. When the sexp finishes, there should be nothing else on the line i.e. the last closing parenthesis should be followed by a new line.
val input_list : 'a reader -> Stdlib.in_channel -> 'a list
Try the reader until it fails with End_of_file
and then build the list of all the successfully read objects in order.
val input_array : 'a reader -> Stdlib.in_channel -> 'a array
Try the reader until it fails with End_of_file
and then build the array of all the successfully read objects in order.
val output_list : 'a writer -> Stdlib.out_channel -> 'a list -> unit
Output all the element of the list in order with the provided writer
val read : 'a reader -> string -> 'a
Take a reader and a file and read an object from the file using the reader. Text mode
val read_bin : 'a reader -> string -> 'a
Take a reader and a file and read an object from the file using the reader. Binary mode
val write : 'a writer -> string -> 'a -> unit
Take a writer a file and object and write the object to the file using writer. Text mode
val write_bin : 'a writer -> string -> 'a -> unit
Take a writer fs a file and object and write the object to the file using writer. Binary mode
Utils.Files
This module provides simplified file management and some channel interaction function.
The functions read
and write
are about dealing with a whole file at once without caring about opening or closing it.
The type of a reader that read an object from a channel. Function like input_*
in Stdlib
The type of a writer of 'a. Function named output_*
in Stdlib
Reads a S-expression from the input, line by line. When the sexp finishes, there should be nothing else on the line i.e. the last closing parenthesis should be followed by a new line.
val input_list : 'a reader -> Stdlib.in_channel -> 'a list
Try the reader until it fails with End_of_file
and then build the list of all the successfully read objects in order.
val input_array : 'a reader -> Stdlib.in_channel -> 'a array
Try the reader until it fails with End_of_file
and then build the array of all the successfully read objects in order.
val output_list : 'a writer -> Stdlib.out_channel -> 'a list -> unit
Output all the element of the list in order with the provided writer
val read : 'a reader -> string -> 'a
Take a reader and a file and read an object from the file using the reader. Text mode
val read_bin : 'a reader -> string -> 'a
Take a reader and a file and read an object from the file using the reader. Binary mode
val write : 'a writer -> string -> 'a -> unit
Take a writer a file and object and write the object to the file using writer. Text mode
val write_bin : 'a writer -> string -> 'a -> unit
Take a writer fs a file and object and write the object to the file using writer. Binary mode
write_string file cont
write cont
in file
which is overwritten if it exists
Utils.FullVec
val make : (int -> 'a) -> 'a t
Create a full vector from a generator
val set : 'a t -> int -> 'a -> unit
Set the binding of that integer
val set_after : 'a t -> int -> (int -> 'a) -> unit
Set the binding of all integer after this value by supplying a new generator. The former generator is discarded. The bindings before the value keep their value (if there were not generated, they will be before discarding the old generator
val get : 'a t -> int -> 'a
Get the binding of that integer
val get_vec_until : 'a t -> int -> 'a Vec.t
Get a vector containing at least the elements until the specified value excluded
val map : ('a -> 'b) -> 'a t -> 'b t
Map the function over the fullvec. Postcompose the map on the generator
val map_mut : ('a -> 'a) -> 'a t -> unit
Map the function over the fullvector by mutation. Postcompose the map on the generator.contents Warning, a lot of map_mut
may make the generator big and slow. Maybe try to use set_after
to reset it when required.
val map_mut_until : limit:int -> ('a -> 'a) -> 'a t -> unit
Map the function over the fullvector until the limit. The rest is unchanged
val iter_until : limit:int -> ('a -> unit) -> 'a t -> unit
Iterate until the specified value (excluded). (The FullVec is infinite so you can't iter on all of it)
val iteri_until : limit:int -> (int -> 'a -> unit) -> 'a t -> unit
Same as iter_until
but with the index
Utils.FullVec
A full vector is a vector in which all non-negative integer are bound.
This consist in a normal vector and a function and all the bindings of value after the vector end are the result of the function call on that integer.
It is guaranted that the function will be called lazily on integer when required in stricly increasing order (and thus never twice on the same integer). However an integer may be skipped (When manually set). That means the function can have side effect if necessary. However if the structure is copied (map
or copy
), The generator may be called multiple time on some integer in the different copies.
Both set and get may generate calls to the generator if required.
Any attempt to use a negative integer will raise Invalid_argument
.
A negative integer will never be passed to the generator.
val make : (int -> 'a) -> 'a t
Create a full vector from a generator
val set : 'a t -> int -> 'a -> unit
Set the binding of that integer
val set_after : 'a t -> int -> (int -> 'a) -> unit
Set the binding of all integer after this value by supplying a new generator. The former generator is discarded. The bindings before the value keep their value (if there were not generated, they will be before discarding the old generator
val get : 'a t -> int -> 'a
Get the binding of that integer
Get a vector containing at least the elements until the specified value excluded
Map the function over the fullvec. Postcompose the map on the generator
val map_mut : ('a -> 'a) -> 'a t -> unit
val map_mut_until : limit:int -> ('a -> 'a) -> 'a t -> unit
Map the function over the fullvector until the limit. The rest is unchanged
val iter_until : limit:int -> ('a -> unit) -> 'a t -> unit
Iterate until the specified value (excluded). (The FullVec is infinite so you can't iter on all of it)
val iteri_until : limit:int -> (int -> 'a -> unit) -> 'a t -> unit
Same as iter_until
but with the index
val pp : ('a -> Pp.document) -> 'a t -> Pp.document
Only prints the non-default values
Utils.Fun
val ($) : ('a -> 'b) -> 'a -> 'b
An other application operator. g @@ f @@ 4
replaces g (f 4)
but h $ g 4 $ f 5
replaces h (g 4) (f 5)
@@
has higher precedence so h $ g @@ f 4 $ t @@ p 5 = h (g (f 4)) (t (p 5))
but in practice, I would advise not to mix them.
val (%>) : ('a -> 'b) -> ('b -> 'c) -> 'a -> 'c
Abstraction of piping above a parameter useful to refactor things like List.map f |> List.map g
into List.map (f %> g)
.
The trivial definition is x |> (f %> g) = x |> f |> g
Utils.Fun
More functional combinator. This module extends the base OCaml API of Fun
.
An other application operator. g @@ f @@ 4
replaces g (f 4)
but h $ g 4 $ f 5
replaces h (g 4) (f 5)
@@
has higher precedence so h $ g @@ f 4 $ t @@ p 5 = h (g (f 4)) (t (p 5))
but in practice, I would advise not to mix them.
Abstraction of piping above a parameter useful to refactor things like List.map f |> List.map g
into List.map (f %> g)
.
The trivial definition is x |> (f %> g) = x |> f |> g
When you want to run some imperative code on a value before continuing the pipeline
Utils.HashVector
val mem : 'a t -> int -> bool
Check if an element is set
val set : 'a t -> int -> 'a -> unit
Set a value. Create a new binding if necessary
val clear : 'a t -> int -> unit
Clear a binding, do nothing is the value is not bound.
val get_opt : 'a t -> int -> 'a option
Return Some v
if v is bound to the integer in the hash vector. Return None
if nothing is bound
val get : 'a t -> int -> 'a
Return the value bound to the integer. Throw Not_found
if the integer is not bound
val empty : unit -> 'a t
Create an empty hashVector.t
val bindings : 'a t -> (int * 'a) list
Returns a list of bindings in the hash vector
Utils.HashVector
An hash vector allow a vector to behave as hash map indexed by small integers. It is hash map with the identity hash function.
If all goes well it has the same API as (int, 'a) Hashtbl.t
for positive integers. Any attempt to use a negative integer will raise Invalid_argument
.
val mem : 'a t -> int -> bool
Check if an element is set
val set : 'a t -> int -> 'a -> unit
Set a value. Create a new binding if necessary
val clear : 'a t -> int -> unit
Clear a binding, do nothing is the value is not bound.
val get_opt : 'a t -> int -> 'a option
Return Some v
if v is bound to the integer in the hash vector. Return None
if nothing is bound
val get : 'a t -> int -> 'a
Return the value bound to the integer. Throw Not_found
if the integer is not bound
val empty : unit -> 'a t
Create an empty hashVector.t
val bindings : 'a t -> (int * 'a) list
Returns a list of bindings in the hash vector
val pp : ('a -> Pp.document) -> 'a t -> Pp.document
Pretty print a hashVector
Utils.IdMap
type ('a, 'b) t
The type of a idmap
'a
is type of keys that index the structure
'b
is the type of value that are indexed.
val length : ('a, 'b) t -> int
Gives the number of bindings in the idmap
val make : unit -> ('a, 'b) t
Create a new idmap from scratch
val to_ident : ('a, 'b) t -> 'a -> int
Convert a key in an identifier. Throws if the key is not bound
val to_ident_opt : ('a, 'b) t -> 'a -> int option
Convert a key in an identifier. None if the key is not bound
val of_ident : ('a, 'b) t -> int -> 'a
Convert an identifier to its corresponding key. Throws if the id is not bound
val mem : ('a, 'b) t -> 'a -> bool
Check if a key is bound
val mem_id : ('a, 'b) t -> int -> bool
Check if an id is bound
val getk : ('a, 'b) t -> 'a -> 'b
Get a value by key. Raise Not_found
if the key is not bound.
val getk_opt : ('a, 'b) t -> 'a -> 'b option
Get a value by key. None if the key is not bound.
val geti : ('a, 'b) t -> int -> 'b
Get a value by id. Raise Invalid_argument
if the index is not bound.
val unsafe_geti : ('a, 'b) t -> int -> 'b
Get a value by id, unsafe.
val setk : ('a, 'b) t -> 'a -> 'b -> unit
Set a value by key. Raise Not_found
if the key is not bound.
val seti : ('a, 'b) t -> int -> 'b -> unit
Set a value by id. Raise Invalid_argument
if the index is not bound.
val unsafe_seti : ('a, 'b) t -> int -> 'b -> unit
Set a value by id, unsafe.
val fill_all : ('a, 'b) t -> 'b -> unit
Bind the value to all the keys with the specified value.
val iter : ('a -> int -> 'b -> unit) -> ('a, 'b) t -> unit
Call the function on all the bindings of the idmap
val map_to_list : ('a -> int -> 'b -> 'c) -> ('a, 'b) t -> 'c list
Call the function on all the bindings of the idmap and return the list of results
Utils.IdMap
An IdMap is a map that associate an id to each key (and thus to each value).
The value can be indexed with the key or with the id.
The key can be retrieved from the id and vice versa.
Values can be retreived from both keys and value (
The id is an int
The type of a idmap
'a
is type of keys that index the structure
'b
is the type of value that are indexed.
val length : ('a, 'b) t -> int
Gives the number of bindings in the idmap
val make : unit -> ('a, 'b) t
Create a new idmap from scratch
val add : ('a, 'b) t -> 'a -> 'b -> int
Add a binding, and throw Exists
if the binding already exists
val to_ident : ('a, 'b) t -> 'a -> int
Convert a key in an identifier. Throws if the key is not bound
val to_ident_opt : ('a, 'b) t -> 'a -> int option
Convert a key in an identifier. None if the key is not bound
val of_ident : ('a, 'b) t -> int -> 'a
Convert an identifier to its corresponding key. Throws if the id is not bound
val mem : ('a, 'b) t -> 'a -> bool
Check if a key is bound
val mem_id : ('a, 'b) t -> int -> bool
Check if an id is bound
val getk : ('a, 'b) t -> 'a -> 'b
Get a value by key. Raise Not_found
if the key is not bound.
val getk_opt : ('a, 'b) t -> 'a -> 'b option
Get a value by key. None if the key is not bound.
val geti : ('a, 'b) t -> int -> 'b
Get a value by id. Raise Invalid_argument
if the index is not bound.
val unsafe_geti : ('a, 'b) t -> int -> 'b
Get a value by id, unsafe.
val setk : ('a, 'b) t -> 'a -> 'b -> unit
Set a value by key. Raise Not_found
if the key is not bound.
val seti : ('a, 'b) t -> int -> 'b -> unit
Set a value by id. Raise Invalid_argument
if the index is not bound.
val unsafe_seti : ('a, 'b) t -> int -> 'b -> unit
Set a value by id, unsafe.
val fill_all : ('a, 'b) t -> 'b -> unit
Bind the value to all the keys with the specified value.
val iter : ('a -> int -> 'b -> unit) -> ('a, 'b) t -> unit
Call the function on all the bindings of the idmap
val map_to_list : ('a -> int -> 'b -> 'c) -> ('a, 'b) t -> 'c list
Call the function on all the bindings of the idmap and return the list of results
val pp :
+ ?name:string ->
+ keys:('a -> Pp.document) ->
+ vals:('b -> Pp.document) ->
+ ('a, 'b) t ->
+ Pp.document
Pretty prints
Utils.IntBits
val check_index : int -> unit
Check that the index is valid to index an integer.
Throw Invalid_argument
if the index is not valid
val check_range : int -> int -> unit
Check that the range is valid to index an integer. See module documentation (IntBits
) for the definition of a valid range.
Throw Invalid_argument
if the range is not valid.
val init : bool -> t
Initialize an int with all zeros or all ones depending on the boolean
val get : t -> int -> bool
Get a bit at a specific index. See unsafe_get
val set : t -> int -> t
Set a bit at a specific index. See unsafe_set
val clear : t -> int -> t
Clear a bit at a specific index. See unsafe_clear
val setb : t -> int -> bool -> t
Set a bit at a specific index according to a boolean. See unsafe_setb
val mask : int -> int -> t
mask i l
creates a mask stating at i of length l. This means that the bits of the output in the range [i; i+l)
are ones and the others are 0.
See unsafe_mask
.
val set_range : t -> int -> int -> t
set_range bf i l
sets the range [i; i+l)
to ones in bf
. See unsafe_set_range
val clear_range : t -> int -> int -> t
clear_range bf i l
sets the range [i; i+l)
to zeroes in bf
. See unsafe_set_range
val unsafe_clear_range : t -> int -> int -> t
Unsafe version of clear_range
val sub : t -> int -> int -> t
sub bf i l
outputs the range [i; i+l)
of bf. The bits above l of the result are zeroes.
See unsafe_sub
val set_sub : t -> int -> int -> t -> t
set_sub bf i l data
sets the [i; i+l)
range of bf
to data
. The bits above l
of data
are ignored.
See unsafe_set_sub
val unsafe_set_sub : t -> int -> int -> t -> t
Unsafe version of set_sub
. However the bits above l of data must be zeroes
val blit : t -> int -> t -> int -> int -> t
unsafe_blit src isrc dest idest len
copies [isrc; isrc+len)
of src
into [idest; idest +l)
of dest
.
See unsafe_blit
Utils.IntBits
Manipulate an int as bitfield of size 31 or 63.
I'm tired of having to think about bit shifts and bitwise operations when I do that stuff
Little endian indexing (0 is the least significant bit)
Ranges are specified with and index and a length. The index must be in [0,length)
and the length must be in (0,length]
. Those conditions will be named "valid range". Any range specified in that way can go after the end. When reading, it will behave as if it was zeroes, and on writes, all bits after the end are discarded.
All unsafe function implicitely assume that all indexes and ranges are valid. All safe functions throw if those conditions are not met.
Check that the index is valid to index an integer.
Throw Invalid_argument
if the index is not valid
Check that the range is valid to index an integer. See module documentation (IntBits
) for the definition of a valid range.
Throw Invalid_argument
if the range is not valid.
val init : bool -> t
Initialize an int with all zeros or all ones depending on the boolean
val get : t -> int -> bool
Get a bit at a specific index. See unsafe_get
Set a bit at a specific index. See unsafe_set
Clear a bit at a specific index. See unsafe_clear
Set a bit at a specific index according to a boolean. See unsafe_setb
val mask : int -> int -> t
mask i l
creates a mask stating at i of length l. This means that the bits of the output in the range [i; i+l)
are ones and the others are 0.
See unsafe_mask
.
set_range bf i l
sets the range [i; i+l)
to ones in bf
. See unsafe_set_range
clear_range bf i l
sets the range [i; i+l)
to zeroes in bf
. See unsafe_set_range
Unsafe version of clear_range
sub bf i l
outputs the range [i; i+l)
of bf. The bits above l of the result are zeroes.
See unsafe_sub
set_sub bf i l data
sets the [i; i+l)
range of bf
to data
. The bits above l
of data
are ignored.
See unsafe_set_sub
Unsafe version of set_sub
. However the bits above l of data must be zeroes
unsafe_blit src isrc dest idest len
copies [isrc; isrc+len)
of src
into [idest; idest +l)
of dest
.
See unsafe_blit
Utils.List
include Stdlib.List
val length : 'a list -> int
val compare_lengths : 'a list -> 'b list -> int
val compare_length_with : 'a list -> int -> int
val cons : 'a -> 'a list -> 'a list
val hd : 'a list -> 'a
val tl : 'a list -> 'a list
val nth : 'a list -> int -> 'a
val nth_opt : 'a list -> int -> 'a option
val rev : 'a list -> 'a list
val init : int -> (int -> 'a) -> 'a list
val append : 'a list -> 'a list -> 'a list
val rev_append : 'a list -> 'a list -> 'a list
val concat : 'a list list -> 'a list
val flatten : 'a list list -> 'a list
val iter : ('a -> unit) -> 'a list -> unit
val iteri : (int -> 'a -> unit) -> 'a list -> unit
val map : ('a -> 'b) -> 'a list -> 'b list
val mapi : (int -> 'a -> 'b) -> 'a list -> 'b list
val rev_map : ('a -> 'b) -> 'a list -> 'b list
val filter_map : ('a -> 'b option) -> 'a list -> 'b list
val fold_left : ('a -> 'b -> 'a) -> 'a -> 'b list -> 'a
val fold_right : ('a -> 'b -> 'b) -> 'a list -> 'b -> 'b
val iter2 : ('a -> 'b -> unit) -> 'a list -> 'b list -> unit
val map2 : ('a -> 'b -> 'c) -> 'a list -> 'b list -> 'c list
val rev_map2 : ('a -> 'b -> 'c) -> 'a list -> 'b list -> 'c list
val fold_left2 : ('a -> 'b -> 'c -> 'a) -> 'a -> 'b list -> 'c list -> 'a
val fold_right2 : ('a -> 'b -> 'c -> 'c) -> 'a list -> 'b list -> 'c -> 'c
val for_all : ('a -> bool) -> 'a list -> bool
val exists : ('a -> bool) -> 'a list -> bool
val for_all2 : ('a -> 'b -> bool) -> 'a list -> 'b list -> bool
val exists2 : ('a -> 'b -> bool) -> 'a list -> 'b list -> bool
val mem : 'a -> 'a list -> bool
val memq : 'a -> 'a list -> bool
val find : ('a -> bool) -> 'a list -> 'a
val find_opt : ('a -> bool) -> 'a list -> 'a option
val filter : ('a -> bool) -> 'a list -> 'a list
val find_all : ('a -> bool) -> 'a list -> 'a list
val partition : ('a -> bool) -> 'a list -> 'a list * 'a list
val assoc : 'a -> ('a * 'b) list -> 'b
val assoc_opt : 'a -> ('a * 'b) list -> 'b option
val assq : 'a -> ('a * 'b) list -> 'b
val assq_opt : 'a -> ('a * 'b) list -> 'b option
val mem_assoc : 'a -> ('a * 'b) list -> bool
val mem_assq : 'a -> ('a * 'b) list -> bool
val remove_assoc : 'a -> ('a * 'b) list -> ('a * 'b) list
val remove_assq : 'a -> ('a * 'b) list -> ('a * 'b) list
val split : ('a * 'b) list -> 'a list * 'b list
val combine : 'a list -> 'b list -> ('a * 'b) list
val sort : ('a -> 'a -> int) -> 'a list -> 'a list
val stable_sort : ('a -> 'a -> int) -> 'a list -> 'a list
val fast_sort : ('a -> 'a -> int) -> 'a list -> 'a list
val sort_uniq : ('a -> 'a -> int) -> 'a list -> 'a list
val merge : ('a -> 'a -> int) -> 'a list -> 'a list -> 'a list
val to_seq : 'a list -> 'a Stdlib.Seq.t
val of_seq : 'a Stdlib.Seq.t -> 'a list
val repeat : int -> 'a -> 'a t
repeat n a
return a list of n
a
s.
val set_nth : 'a t -> int -> 'a -> 'a t
Set the nth value to the new value and return the modified list
val last : 'a t -> 'a
Give the last element of the list. Raise Invalid_argument
if the list is empty.
val last_opt : 'a t -> 'a option
Give the last element of the list. Return None
if the list is empty.
val fold_left_same : ('a -> 'a -> 'a) -> 'a t -> 'a
Same as fold_left, but do not require a start element, instead the function start with the first element of the list: fold_left_same f [b1; ...; bn] = f (... (f (f b1 b2) ...) bn
.
It will fail with Invalid_argument
if the list is empty.
It can also be written as fold_left_same f l = fold_left f (hd l) (tl l)
val of_array_map : ('a -> 'b) -> 'a array -> 'b t
Map a function at the same time as we are creating a list from an array
Warning: The function is mapped from the right to the left (in case it has side-effects).
of_array_map f l = of_array (Array.map f l) = map f (of_array l)
val of_array_mapi : (int -> 'a -> 'b) -> 'a array -> 'c t
Same as of_array_map
but with the index
val concat_map_rev : ('a -> 'b list) -> 'a t -> 'b t
Same as concat_map
then rev
val concat_map : ('a -> 'b list) -> 'a t -> 'b t
Same as map
then concat
.
TODO: find a clean way of doing conditional compilation, otherwise this will shadow the official concat_map
in 4.10
val find_map : ('a -> 'b option) -> 'a t -> 'b option
find_map f l
applies f
to the elements of l
in order, and returns the first result of the form Some v
, or None
if f
always return None
TODO: find a clean way of doing conditional compilation, otherwise this will shadow the official find_map
in 4.10
val filter_opt : 'a option t -> 'a t
Takes a list of option and only keeps the Some
. Equivalent to filter_map Fun.id
val partition_map : ('a -> 'b option) -> 'a list -> 'a t * 'b t
This function behaves as partition
then a map
. First we do a partition
with the function assuming Some
means true
, then for all the extracted elements we map f
on them and return the results.
Formally: partition_map f l = (filter (fun a -> f a = None) l, filter_map f l)
val remove : ('a -> bool) -> 'a t -> 'a list option
Remove the first element matching the predicate. Returns None
if no element matches the predicate
val drop : int -> 'a t -> 'a t
Drop the specified number of item from the list. If n is greater than the size of the list, then return the empty list
val take_rev : int -> 'a t -> 'a t
Take the specified number of items from the list, but reverse If n is greater than the size of the list, then return the list reversed
Tail-recursive
val take : int -> 'a t -> 'a list
Take the specified number of items from the list. If n is greater than the size of the list, then return the list
l = take n l @ drop n l
val sub : pos:int -> len:int -> 'a t -> 'a list
sub l pos len
return the sub-list of l starting at pos of length len
val equal : ('a -> 'b -> bool) -> 'a t -> 'b t -> bool
val mem : ('a -> 'b -> bool) -> 'a -> 'b t -> bool
val compare : ('a -> 'b -> int) -> 'a t -> 'b t -> int
If the list have the same length this a lexicographic compare. If one list is shorter, then the missing value a considered smaller than any actual values.
A mental model could to view list as infinite sequence of options that are None
after the end of the list. Then it would be proper lexicographic ordering with Option.compare
val bind : 'a t -> ('a -> 'b list) -> 'b t
Monadic bind. It's just concat_map
val return : 'a -> 'a t
Monadic return
val short_combine : 'a t -> 'b t -> ('a * 'b) t
Same as combine
but if a list is shorter then the elements of the longest list are discarded
val let+ : 'a list -> ('a -> 'b) -> 'b list
Applicative let binding. let+ x = xl in e = let* x = xl in return e
val and+ : 'a t -> 'b t -> ('a * 'b) t
Not strict applicative merge (short_combine
). If both list have different length, the longer one is cropped
val let+! : 'a list -> ('a -> unit) -> unit
Iterative let binding (The expression in the in must be unit). This replace implicitly a unit member of the monad (that is assumed to be uninteresting) to a true unit. In other words, it's an iter
: let+! x = l in e = List.iter (fun x -> e) l
Utils.List
Extension of the List
module of the standard library.
It port forward function of ocaml 4.10 among others.
include module type of struct include Stdlib.List end
val repeat : int -> 'a -> 'a t
repeat n a
return a list of n
a
s.
Set the nth value to the new value and return the modified list
val last : 'a t -> 'a
Give the last element of the list. Raise Invalid_argument
if the list is empty.
val last_opt : 'a t -> 'a option
Give the last element of the list. Return None
if the list is empty.
val fold_left_same : ('a -> 'a -> 'a) -> 'a t -> 'a
Same as fold_left, but do not require a start element, instead the function start with the first element of the list: fold_left_same f [b1; ...; bn] = f (... (f (f b1 b2) ...) bn
.
It will fail with Invalid_argument
if the list is empty.
It can also be written as fold_left_same f l = fold_left f (hd l) (tl l)
val of_array_map : ('a -> 'b) -> 'a array -> 'b t
Map a function at the same time as we are creating a list from an array
Warning: The function is mapped from the right to the left (in case it has side-effects).
of_array_map f l = of_array (Array.map f l) = map f (of_array l)
val of_array_mapi : (int -> 'a -> 'b) -> 'a array -> 'c t
Same as of_array_map
but with the index
Same as concat_map
then rev
Same as map
then concat
.
TODO: find a clean way of doing conditional compilation, otherwise this will shadow the official concat_map
in 4.10
val find_map : ('a -> 'b option) -> 'a t -> 'b option
find_map f l
applies f
to the elements of l
in order, and returns the first result of the form Some v
, or None
if f
always return None
TODO: find a clean way of doing conditional compilation, otherwise this will shadow the official find_map
in 4.10
Takes a list of option and only keeps the Some
. Equivalent to filter_map Fun.id
This function behaves as partition
then a map
. First we do a partition
with the function assuming Some
means true
, then for all the extracted elements we map f
on them and return the results.
Formally: partition_map f l = (filter (fun a -> f a = None) l, filter_map f l)
val remove : ('a -> bool) -> 'a t -> 'a list option
Remove the first element matching the predicate. Returns None
if no element matches the predicate
Drop the specified number of item from the list. If n is greater than the size of the list, then return the empty list
Take the specified number of items from the list, but reverse If n is greater than the size of the list, then return the list reversed
Tail-recursive
val take : int -> 'a t -> 'a list
Take the specified number of items from the list. If n is greater than the size of the list, then return the list
l = take n l @ drop n l
val sub : pos:int -> len:int -> 'a t -> 'a list
sub l pos len
return the sub-list of l starting at pos of length len
Build a list from a sequence, but in reverse. Same as of_seq
then rev
val mem : ('a -> 'b -> bool) -> 'a -> 'b t -> bool
If the list have the same length this a lexicographic compare. If one list is shorter, then the missing value a considered smaller than any actual values.
A mental model could to view list as infinite sequence of options that are None
after the end of the list. Then it would be proper lexicographic ordering with Option.compare
Monadic bind. It's just concat_map
val return : 'a -> 'a t
Monadic return
Same as combine
but if a list is shorter then the elements of the longest list are discarded
Applicative let binding. let+ x = xl in e = let* x = xl in return e
Not strict applicative merge (short_combine
). If both list have different length, the longer one is cropped
Iterative let binding (The expression in the in must be unit). This replace implicitly a unit member of the monad (that is assumed to be uninteresting) to a true unit. In other words, it's an iter
: let+! x = l in e = List.iter (fun x -> e) l
Strict applicative merge (combine
). Will throw if lists have different length
Monadic merge. let* x = xl and* y = yl in ... = let* x= xl in let* y = yl in ...
val hd_opt : 'a t -> 'a option
Logger.S
Logs.Logger
This declare a logger instance. The string parameter is the logger name. It should be the OCaml module name. The normal way of instantiating this module is:
open Logs.Logger (struct
+Logger (read-dwarf.Utils.Logs.Logger) Module Logs.Logger
This declare a logger instance. The string parameter is the logger name. It should be the OCaml module name. The normal way of instantiating this module is:
open Logs.Logger (struct
let str = __MODULE__
-end)
This module provide a lot of logging function, They only provide a format string interface but with Pp.top
on may print efficiently but lazily arbitrary Pp
.document to the logs.
Some examples:
warn "This weird thing happened, I choose that default behavior for case %s" case
debug "Function ... received value %t" Pp.(top printer object)
Parameters
Signature
val set_level : level -> unit
Override the level of this logger. It may still be overridden by the command line
val get_level : unit -> level
Get the current level
val log_fatal : code:int -> level -> ('a, 'b) printf_fatal
Log a fatal problem with format string then shutdown with code
val fail : ('a, 'b) printf_fatal
Failure due to external circumstances, generally wrong user input, then exit with code 1. Level is Base
val fatal : ('a, 'b) printf_fatal
Declare a fatal internal error then exit with code 2. Level is Err
This module provide a lot of logging function, They only provide a format string interface but with Pp.top
on may print efficiently but lazily arbitrary Pp.document
to the logs.
Some examples:
warn "This weird thing happened, I choose that default behavior for case %s" case
debug "Function ... received value %t" Pp.(top printer object)
val set_level : level -> unit
Override the level of this logger. It may still be overridden by the command line
val get_level : unit -> level
Get the current level
val log_fatal : code:int -> level -> ('a, 'b) printf_fatal
Log a fatal problem with format string then shutdown with code
val fail : ('a, 'b) printf_fatal
Failure due to external circumstances, generally wrong user input, then exit with code 1. Level is Base
val fatal : ('a, 'b) printf_fatal
Declare a fatal internal error then exit with code 2. Level is Err
Utils.Logs
type 'a printf
= ('a, Stdlib.out_channel, unit) Stdlib.format -> 'a
The type of normal logging function
type ('a, 'b) printf_fatal
= ('a, Stdlib.out_channel, unit, 'b) Stdlib.format4 -> 'a
The type of a failing logging function, that will exit after printing it's message
type level
=
| Base | The actual output. The only thing printed in quiet mode. Should only appear in |
| Err | An error message |
| Warn | An warning |
| Info | Details on what happens that should be understandable if the person do not know the corresponding module |
| Debug | Everything happening in detail |
The type of log level
val level_to_string : level -> string
Convert level to string
val level_to_header : level -> string
Convert level to string header like "[Error]"
. Base
is the empty string
val set_default_level : level -> unit
Set a default level to all the modules. Erase local customized levels
val set_level : string -> level -> unit
Set level of a module by name
val level_conv : level Cmdliner.Arg.conv
Parser for log level on command line
val set_stdout_level : level -> unit
Set level below which the output goes to stdout
module type String = sig ... end
module Logger : functor (S : String) -> sig ... end
This declare a logger instance. The string parameter is the logger name. It should be the OCaml module name. The normal way of instantiating this module is:
val process_opts : bool -> bool list -> string list -> string list -> level -> unit
val term : unit Cmdliner.Term.t
You can then use all the function in Logger
to print logging messages in your module.
The rest of this module is about manipulation log level
in the different modules. You can dynamically interact with the logs level from the command line by using CommonOpt.logs
The type of normal logging function
The type of a failing logging function, that will exit after printing it's message
val pp_level : level -> Pp.document
Pretty prints a level
val level_to_string : level -> string
Convert level to string
val level_to_header : level -> string
Convert level to string header like "[Error]"
. Base
is the empty string
val set_default_level : level -> unit
Set a default level to all the modules. Erase local customized levels
val set_level : string -> level -> unit
Set level of a module by name
val level_conv : level Cmdliner.Arg.conv
Parser for log level on command line
val set_stdout_level : level -> unit
Set level below which the output goes to stdout
module type String = sig ... end
This declare a logger instance. The string parameter is the logger name. It should be the OCaml module name. The normal way of instantiating this module is:
val process_opts :
+ bool ->
+ bool list ->
+ string list ->
+ string list ->
+ level ->
+ unit
Logs.String
Utils.Option
include module type of Stdlib.Option
val none : 'a option
val some : 'a -> 'a option
val value : 'a option -> default:'a -> 'a
val get : 'a option -> 'a
val bind : 'a option -> ('a -> 'b option) -> 'b option
val join : 'a option option -> 'a option
val map : ('a -> 'b) -> 'a option -> 'b option
val fold : none:'a -> some:('b -> 'a) -> 'b option -> 'a
val iter : ('a -> unit) -> 'a option -> unit
val is_none : 'a option -> bool
val is_some : 'a option -> bool
val equal : ('a -> 'a -> bool) -> 'a option -> 'a option -> bool
val compare : ('a -> 'a -> int) -> 'a option -> 'a option -> int
val to_result : none:'e -> 'a option -> ('a, 'e) Stdlib.result
val to_list : 'a option -> 'a list
val to_seq : 'a option -> 'a Stdlib.Seq.t
val take_first : 'a option -> 'a option -> 'a option
Take the value in the first argument if there is one, otherwise take the value in the second argument, otherwise None
val (|||) : 'a option -> 'a option -> 'a option
take_first
as an operator:
Behave like boolean or but keep the value of the first option that gave true. This is associative but obviously not commutative.
val take_first_list : 'a option list -> 'a option
Take the value of the first Some
in the list. returns None
if all the option were None
val take_all : 'a option -> 'b option -> ('a * 'b) option
If both option have values, give Some
of the pair, otherwise None
val (&&&) : 'a option -> 'b option -> ('a * 'b) option
Take_all as an operator:
Behave like boolean and but keep all the value of the options This is not associative because at type level (a * b) * c is not a * (b * c). Using monadic bindings is recommended for more that 2 operands.
val value_fail : 'a option -> ('b, unit, string, 'a) Stdlib.format4 -> 'b
Expect the option to contain a value and fails (Failure
) otherwise. The format string specify the content of the failure
val value_fun : 'a option -> default:(unit -> 'a) -> 'a
Like Stdlib.Option.value but the default is a called function, that can throw instead of giving a value
val let+ : 'a option -> ('a -> 'b) -> 'b option
Applicative let.
let+ x = mx in e
is Option.map (fun x -> e) mx
val and+ : 'a option -> 'b option -> ('a * 'b) option
Applicative and.
let+ x = mx and+ y = my in e
give Some e
if both mx
and my
were Some
s.
val let+! : 'a option -> ('a -> unit) -> unit
Iter applicative let.
let+! x = mx in e
runs e
if mx
contained a value i.e Option.iter (fun x -> e) mx
val lift : 'a option list -> 'a list option
Commute the list and the option. If the list contains one None
then the result is None
. If you want to keep all the Some
value, use List
.filter_map.
This can be condidered as a list-wide take_all
. A list-wide take_first
would be List.find_map
val map_lift : ('a -> 'b option) -> 'a list -> 'b list option
The same as a List.map and then a lift
val lift_pair : ('a option * 'b option) -> ('a * 'b) option
Lift a pair of options to an option of pair. It is the same as take_all
.
Utils.Option
This module extends the base OCaml API of Option
.
In particular, it adds:
include module type of Stdlib.Option
Take the value in the first argument if there is one, otherwise take the value in the second argument, otherwise None
take_first
as an operator:
Behave like boolean or but keep the value of the first option that gave true. This is associative but obviously not commutative.
Take the value of the first Some
in the list. returns None
if all the option were None
If both option have values, give Some
of the pair, otherwise None
Take_all as an operator:
Behave like boolean and but keep all the value of the options This is not associative because at type level (a * b) * c is not a * (b * c). Using monadic bindings is recommended for more that 2 operands.
Expect the option to contain a value and fails (Failure
) otherwise. The format string specify the content of the failure
Like Stdlib.Option.value but the default is a called function, that can throw instead of giving a value
Create an option from a bool, with the some value as computed by the some function
Return the second argument if the first is false
, otherwise None
Applicative let.
let+ x = mx in e
is Option.map (fun x -> e) mx
Applicative and.
let+ x = mx and+ y = my in e
give Some e
if both mx
and my
were Some
s.
Iter applicative let.
let+! x = mx in e
runs e
if mx
contained a value i.e Option.iter (fun x -> e) mx
Monadic let: let* x = mx in e
is Option.bind mx (fun x -> e)
Monadic and: let* x = mx and* y = my in e
is let* x = mx in let* y = my in e
Commute the list and the option. If the list contains one None
then the result is None
. If you want to keep all the Some
value, use List.filter_map
.
This can be condidered as a list-wide take_all
. A list-wide take_first
would be List.find_map
The same as a List.map and then a lift
Lift a pair of options to an option of pair. It is the same as take_all
.
Utils.Pair
val iter : ('a -> unit) -> ('b -> unit) -> ('a * 'b) -> unit
Iter each function on one side of the pair
val compare : ?fst:('a -> 'a -> int) -> ?snd:('b -> 'b -> int) -> ('a * 'b) -> ('a * 'b) -> int
Compare a pair using provided comparison function. Both fst
and snd
default to the polymorphic compare
val equal : ?fst:('a -> 'a -> bool) -> ?snd:('b -> 'b -> bool) -> ('a * 'b) -> ('a * 'b) -> bool
Test pair equality using provided equality function. Both fst
and snd
default to the polymorphic equality
val make : 'a -> 'b -> 'a * 'b
Just build the pair, not useful on it's own but List.combine
is just List.map2 Pair.make
, and there are some other cases where it is handy for high-order programming
Utils.Pair
This module contain random utility functions dealing with pairs
Iter each function on one side of the pair
val compare :
+ ?fst:('a -> 'a -> int) ->
+ ?snd:('b -> 'b -> int) ->
+ ('a * 'b) ->
+ ('a * 'b) ->
+ int
Compare a pair using provided comparison function. Both fst
and snd
default to the polymorphic compare
val equal :
+ ?fst:('a -> 'a -> bool) ->
+ ?snd:('b -> 'b -> bool) ->
+ ('a * 'b) ->
+ ('a * 'b) ->
+ bool
Test pair equality using provided equality function. Both fst
and snd
default to the polymorphic equality
Just build the pair, not useful on it's own but List.combine
is just List.map2 Pair.make
, and there are some other cases where it is handy for high-order programming
Check that both individual predicates hold
Pp.custom
method compact : output -> unit
method pretty : output -> state -> int -> bool -> unit
method requirement : requirement
Pp.custom
method pretty : output -> state -> int -> bool -> unit
method requirement : requirement
Pp.output
Utils.Pp
This is a wrapper around the pprint
library
include PPrint
val empty : document
val char : char -> document
val string : string -> document
val substring : string -> int -> int -> document
val fancystring : string -> int -> document
val fancysubstring : string -> int -> int -> int -> document
val utf8string : string -> document
val utf8format : ('a, unit, string, document) Stdlib.format4 -> 'a
val hardline : document
val blank : int -> document
val break : int -> document
val (^^) : document -> document -> document
val nest : int -> document -> document
val group : document -> document
val ifflat : document -> document -> document
val align : document -> document
val infinity : requirement
class type output = object ... end
type state
= PPrintEngine.state
=
{
width : int; |
ribbon : int; |
mutable last_indent : int; |
mutable line : int; |
mutable column : int; |
}
class type custom = object ... end
val custom : custom -> document
val requirement : document -> requirement
val pretty : output -> state -> int -> bool -> document -> unit
val compact : output -> document -> unit
val lparen : PPrintEngine.document
val rparen : PPrintEngine.document
val langle : PPrintEngine.document
val rangle : PPrintEngine.document
val lbrace : PPrintEngine.document
val rbrace : PPrintEngine.document
val lbracket : PPrintEngine.document
val rbracket : PPrintEngine.document
val squote : PPrintEngine.document
val dquote : PPrintEngine.document
val bquote : PPrintEngine.document
val semi : PPrintEngine.document
val colon : PPrintEngine.document
val comma : PPrintEngine.document
val space : PPrintEngine.document
val dot : PPrintEngine.document
val sharp : PPrintEngine.document
val slash : PPrintEngine.document
val backslash : PPrintEngine.document
val equals : PPrintEngine.document
val qmark : PPrintEngine.document
val tilde : PPrintEngine.document
val at : PPrintEngine.document
val percent : PPrintEngine.document
val dollar : PPrintEngine.document
val caret : PPrintEngine.document
val ampersand : PPrintEngine.document
val star : PPrintEngine.document
val plus : PPrintEngine.document
val minus : PPrintEngine.document
val underscore : PPrintEngine.document
val bang : PPrintEngine.document
val bar : PPrintEngine.document
val precede : PPrintEngine.document -> PPrintEngine.document -> PPrintEngine.document
val terminate : PPrintEngine.document -> PPrintEngine.document -> PPrintEngine.document
val enclose : PPrintEngine.document -> PPrintEngine.document -> PPrintEngine.document -> PPrintEngine.document
val squotes : PPrintEngine.document -> PPrintEngine.document
val dquotes : PPrintEngine.document -> PPrintEngine.document
val bquotes : PPrintEngine.document -> PPrintEngine.document
val braces : PPrintEngine.document -> PPrintEngine.document
val parens : PPrintEngine.document -> PPrintEngine.document
val angles : PPrintEngine.document -> PPrintEngine.document
val brackets : PPrintEngine.document -> PPrintEngine.document
val twice : PPrintEngine.document -> PPrintEngine.document
val repeat : int -> PPrintEngine.document -> PPrintEngine.document
val concat : PPrintEngine.document list -> PPrintEngine.document
val separate : PPrintEngine.document -> PPrintEngine.document list -> PPrintEngine.document
val concat_map : ('a -> PPrintEngine.document) -> 'a list -> PPrintEngine.document
val separate_map : PPrintEngine.document -> ('a -> PPrintEngine.document) -> 'a list -> PPrintEngine.document
val separate2 : PPrintEngine.document -> PPrintEngine.document -> PPrintEngine.document list -> PPrintEngine.document
val optional : ('a -> PPrintEngine.document) -> 'a option -> PPrintEngine.document
val lines : string -> PPrintEngine.document list
val arbitrary_string : string -> PPrintEngine.document
val words : string -> PPrintEngine.document list
val split : (char -> bool) -> string -> PPrintEngine.document list
val flow : PPrintEngine.document -> PPrintEngine.document list -> PPrintEngine.document
val flow_map : PPrintEngine.document -> ('a -> PPrintEngine.document) -> 'a list -> PPrintEngine.document
val url : string -> PPrintEngine.document
val hang : int -> PPrintEngine.document -> PPrintEngine.document
val prefix : int -> int -> PPrintEngine.document -> PPrintEngine.document -> PPrintEngine.document
val jump : int -> int -> PPrintEngine.document -> PPrintEngine.document
val infix : int -> int -> PPrintEngine.document -> PPrintEngine.document -> PPrintEngine.document -> PPrintEngine.document
val surround : int -> int -> PPrintEngine.document -> PPrintEngine.document -> PPrintEngine.document -> PPrintEngine.document
val soft_surround : int -> int -> PPrintEngine.document -> PPrintEngine.document -> PPrintEngine.document -> PPrintEngine.document
val surround_separate : int -> int -> PPrintEngine.document -> PPrintEngine.document -> PPrintEngine.document -> PPrintEngine.document -> PPrintEngine.document list -> PPrintEngine.document
val surround_separate_map : int -> int -> PPrintEngine.document -> PPrintEngine.document -> PPrintEngine.document -> PPrintEngine.document -> ('a -> PPrintEngine.document) -> 'a list -> PPrintEngine.document
val (!^) : string -> PPrintEngine.document
val (^/^) : PPrintEngine.document -> PPrintEngine.document -> PPrintEngine.document
val (^//^) : PPrintEngine.document -> PPrintEngine.document -> PPrintEngine.document
This section provide function to render documents in a text output.
However unless you know what you are doing, you may prefer using the Logs
module with the function of "Render documents in format strings", instead of calling those function directly.
val fprint : Stdlib.out_channel -> document -> unit
Render the document in a pretty manner on the provided out_channel
val fprintln : Stdlib.out_channel -> document -> unit
Render the document in a compact manner on the provided out_channel
val print : document -> unit
Print the document on stdout
. The general way of
val println : document -> unit
Print the document on stdout
, then a endline, then flush
val eprintln : document -> unit
Print the document on stderr
, then a endline, then flush
val sprintc : ToBuffer.document -> string
Print the document in a string, in a compact manner
val sprint : ToBuffer.document -> string
Print the document in a string, in a pretty manner
This sub section provide functions to embed a pprint
in a usual printf
-like format string.
The general usage is to use the %t
format and then appropriate mapping function. For example:
Printf.printf "Documents: %t" Pp.(top printer object)
This is lazy which means that if you use this in disabled debug messages, the object will not even be read. The only cost will be to allocate the closure on the minor GC.
Wheter to use top
or tos
depends if the final output is an output stream or a plain string.
val top : ('a -> document) -> 'a -> Stdlib.out_channel -> unit
Convert a document printer to a Printf format string via %t
for function outputing on an out_channel
val topi : ('a -> document) -> 'a -> Stdlib.out_channel -> unit
Same as top
but add 4 space of indentation everywhere on the object. The general use is:
Printf.printf "Object:\n%t\n" Pp.(topi printer object)
and it will print:
Object: - object line 1 - object line 2
val tos : ('a -> ToBuffer.document) -> 'a -> unit -> string
Convert a document printer to a Printf format string via %t
for function outputing in a string
This section provide function to create documents from various object as well as combinators to generate documents from more complex objects.
val dprintf : ('a, unit, string, PPrintEngine.document) Stdlib.format4 -> 'a
Printf like function that returns a document of the formatted string. In the end, it's just a string document, nothing more complex
val space : document
A breakable space
val nbspace : document
A non-breakable space (equivalent to char ' '
)
val int : int -> document
Print an int in decimal
val hex : int -> document
Print an int in hexadecimal as if it was unsigned
val shex : int -> document
Print an int in hexadecimal with a sign: -1
will be printed to -0x1
val ptr : int -> document
Print an int in hexadecimal with the 0x
prefix
val byte : char -> document
Print a byte as 2 hexadecimal digit
val hex16 : int -> document
Print an unsigned 16 bit integer as 4 hexadecimal digit
val hex32 : int32 -> document
Print an unsigned 32 bit integer as 8 hexadecimal digit
val hex64 : int64 -> document
Print an unsigned 64 bit integer as 16 hexadecimal digit
val array : ('a -> OCaml.representation) -> 'a array -> OCaml.representation
Print an array when provided with an element printer
val list : ('a -> OCaml.representation) -> 'a list -> OCaml.representation
Print an list when provided with an element printer
val opt : ('a -> OCaml.representation) -> 'a option -> OCaml.representation
Print an option when provided with an element printer
val pair : ('a -> OCaml.representation) -> ('b -> OCaml.representation) -> ('a * 'b) -> OCaml.representation
Print an pair when provided with both element printers
val tup3 : ('a -> OCaml.representation) -> ('b -> OCaml.representation) -> ('c -> OCaml.representation) -> ('a * 'b * 'c) -> OCaml.representation
Print an 3-sized tuple when provided with all three element printers
val erase : 'a -> document
Ignore the input and print nothing
val mapping : string -> (document * document) list -> document
Prints a mapping with this style:
name{ +Pp (read-dwarf.Utils.Pp) Module
Utils.Pp
This module provide all pretty printing functionality. It's main goal is not directly handle the output but to handle how to layout complex data structure in a text format.
The main idea of this library is to separate the layout description phase from the part where you actually lay out the thing to pretty print. Thus the two stage of pretty printing is first to generate a
document
object describing the layout from the object to print and then render the document to the string using one of the printingThis is a wrapper around the
pprint
library
include module type of struct include PPrint end
val empty : document
val char : char -> document
val string : string -> document
val substring : string -> int -> int -> document
val fancystring : string -> int -> document
val fancysubstring : string -> int -> int -> int -> document
val utf8string : string -> document
val utf8format : ('a, unit, string, document) Stdlib.format4 -> 'a
val hardline : document
val blank : int -> document
val break : int -> document
val is_empty : document -> bool
val infinity : requirement
class type output = object ... end
class type custom = object ... end
val custom : custom -> document
val requirement : document -> requirement
val compact : output -> document -> unit
val lparen : document
val rparen : document
val langle : document
val rangle : document
val lbrace : document
val rbrace : document
val lbracket : document
val rbracket : document
val squote : document
val dquote : document
val bquote : document
val semi : document
val colon : document
val comma : document
val dot : document
val sharp : document
val slash : document
val backslash : document
val equals : document
val qmark : document
val tilde : document
val at : document
val percent : document
val dollar : document
val caret : document
val ampersand : document
val star : document
val plus : document
val minus : document
val underscore : document
val bang : document
val bar : document
val lines : string -> document list
val arbitrary_string : string -> document
val words : string -> document list
val split : (char -> bool) -> string -> document list
val url : string -> document
val (!^) : string -> document
Rendering documents
This section provide function to render documents in a text output.
However unless you know what you are doing, you may prefer using the
Logs
module with the function of "Render documents in format strings", instead of calling those function directly.val fprint : Stdlib.out_channel -> document -> unit
Render the document in a pretty manner on the provided
out_channel
val fprintln : Stdlib.out_channel -> document -> unit
Render the document in a compact manner on the provided
out_channel
val print : document -> unit
Print the document on
stdout
. The general way ofval println : document -> unit
Print the document on
stdout
, then a endline, then flushval eprintln : document -> unit
Print the document on
stderr
, then a endline, then flushRender documents in format strings
This sub section provide functions to embed a
pprint
in a usualprintf
-like format string.The general usage is to use the
%t
format and then appropriate mapping function. For example:Printf.printf "Documents: %t" Pp.(top printer object)
This is lazy which means that if you use this in disabled debug messages, the object will not even be read. The only cost will be to allocate the closure on the minor GC.
Wheter to use
top
ortos
depends if the final output is an output stream or a plain string.val top : ('a -> document) -> 'a -> Stdlib.out_channel -> unit
Convert a document printer to a Printf format string via
%t
for function outputing on anout_channel
val topi : ('a -> document) -> 'a -> Stdlib.out_channel -> unit
Same as
top
but add 4 space of indentation everywhere on the object. The general use is:Printf.printf "Object:\n%t\n" Pp.(topi printer object)
and it will print:
Object: +object line 1 +object line 2Convert a document printer to a Printf format string via
%t
for function outputing in a stringCombinators
This section provide function to create documents from various object as well as combinators to generate documents from more complex objects.
val dprintf : ('a, unit, string, document) Stdlib.format4 -> 'a
Printf like function that returns a document of the formatted string. In the end, it's just a string document, nothing more complex
val space : document
A breakable space
val nbspace : document
A non-breakable space (equivalent to
char ' '
)val bool : bool -> document
Print a boolean as "true" or "false"
val int : int -> document
Print an int in decimal
val hex : int -> document
Print an int in hexadecimal as if it was unsigned
val shex : int -> document
Print an int in hexadecimal with a sign:
-1
will be printed to-0x1
val ptr : int -> document
Print an int in hexadecimal with the
0x
prefixval byte : char -> document
Print a byte as 2 hexadecimal digit
val hex16 : int -> document
Print an unsigned 16 bit integer as 4 hexadecimal digit
val hex32 : int32 -> document
Print an unsigned 32 bit integer as 8 hexadecimal digit
val hex64 : int64 -> document
Print an unsigned 64 bit integer as 16 hexadecimal digit
Print an array when provided with an element printer
Print an list when provided with an element printer
Print an option when provided with an element printer
val pair : + ('a -> PPrint.document) -> + ('b -> PPrint.document) -> + ('a * 'b) -> + PPrint.document
Print an pair when provided with both element printers
val tup3 : + ('a -> PPrint.document) -> + ('b -> PPrint.document) -> + ('c -> PPrint.document) -> + ('a * 'b * 'c) -> + PPrint.document
Print an 3-sized tuple when provided with all three element printers
val qstring : string -> document
Print a quoted string
val erase : 'a -> document
Ignore the input and print nothing
Prints a mapping with this style:
name{ key -> value; key -> value; key -> value; -}
val hashtbl : ?name:string -> ('a -> document) -> ('b -> document) -> ('a, 'b) Stdlib.Hashtbl.t -> document
Print a Hashtbl using
mapping
val hashtbl : + ?name:string -> + ('a -> document) -> + ('b -> document) -> + ('a, 'b) Stdlib.Hashtbl.t -> + document
Print a Hashtbl using
mapping
val hashtbl_sorted : + compare:('a -> 'a -> int) -> + ?name:string -> + ('a -> document) -> + ('b -> document) -> + ('a, 'b) Stdlib.Hashtbl.t -> + document
Print a sorted Hashtbl using
mapping
val record : + OCaml.type_name -> + (OCaml.record_field * PPrint.document) list -> + document
\ No newline at end of file +}Print a record in the format
name{ field = value; field = value; field = value; -}
val separate_mapi : document -> (int -> 'a -> document) -> 'a list -> document
Like
separate_map
but with the index
val concat_array_map : ('a -> document) -> 'a array -> document
Concatenate the documents produced by the function over the array
val concat_array_mapi : (int -> 'a -> document) -> 'a array -> document
Concatenate the document produced by the function on the array. The function also gets the index of the element
Special printer
val status : (int -> document) -> Unix.process_status -> document
Prints a
Unix.process_status
with an integer printer
val statusi : Unix.process_status -> document
Prints a
Unix.process_status
with decimal integers
val statush : Unix.process_status -> document
Prints a
Unix.process_status
with hexadecimal integersLike
separate_map
but with the indexConcatenate the documents produced by the function over the array
Concatenate the document produced by the function on the array. The function also gets the index of the element
Special printer
Prints a
Unix.process_status
with an integer printerval statusi : Unix.process_status -> document
Prints a
Unix.process_status
with decimal integersval statush : Unix.process_status -> document
Prints a
Unix.process_status
with hexadecimal integers
Utils.Protect
exception
Protect_both of exn * exn
This exception it thrown when both function and protector have thrown
val protect : (unit -> 'a) -> (unit -> unit) -> 'a
protect f p
runs f then p even if f throws.
If one of f
or p
throw, then that exception is transmitted as is If both throw, the pair of exceptions is encapsulated in Protect_both
and thrown.
This behavior is slightly different from the standard protect
.
Utils.Protect
This module provide try-with-finally kind of exception handling.
It provides a new kind of protect
function.
TODO: Think if this behavior is really useful compared to the standard protect
This exception it thrown when both function and protector have thrown
protect f p
runs f then p even if f throws.
If one of f
or p
throw, then that exception is transmitted as is If both throw, the pair of exceptions is encapsulated in Protect_both
and thrown.
This behavior is slightly different from the standard protect
.
Utils.Raise
val inv_arg : ('a, unit, string, 'b) Stdlib.format4 -> 'a
Printf like funtion that throws an Invalid_Argument
with the formated string
Utils.Raise
This module provide convenience facilities to raise exception or other exception management
Printf like funtion that throws an Invalid_Argument
with the formated string
Printf like funtion that throws a Failure
with the formated string
RngMap.IMap
An integer map: Map.Make(Int)
val empty : 'a t
val is_empty : 'a t -> bool
val mem : key -> 'a t -> bool
val add : key -> 'a -> 'a t -> 'a t
val update : key -> ('a option -> 'a option) -> 'a t -> 'a t
val singleton : key -> 'a -> 'a t
val remove : key -> 'a t -> 'a t
val merge : (key -> 'a option -> 'b option -> 'c option) -> 'a t -> 'b t -> 'c t
val union : (key -> 'a -> 'a -> 'a option) -> 'a t -> 'a t -> 'a t
val compare : ('a -> 'a -> int) -> 'a t -> 'a t -> int
val equal : ('a -> 'a -> bool) -> 'a t -> 'a t -> bool
val iter : (key -> 'a -> unit) -> 'a t -> unit
val fold : (key -> 'a -> 'b -> 'b) -> 'a t -> 'b -> 'b
val for_all : (key -> 'a -> bool) -> 'a t -> bool
val exists : (key -> 'a -> bool) -> 'a t -> bool
val filter : (key -> 'a -> bool) -> 'a t -> 'a t
val partition : (key -> 'a -> bool) -> 'a t -> 'a t * 'a t
val cardinal : 'a t -> int
val bindings : 'a t -> (key * 'a) list
val min_binding : 'a t -> key * 'a
val min_binding_opt : 'a t -> (key * 'a) option
val max_binding : 'a t -> key * 'a
val max_binding_opt : 'a t -> (key * 'a) option
val choose : 'a t -> key * 'a
val choose_opt : 'a t -> (key * 'a) option
val split : key -> 'a t -> 'a t * 'a option * 'a t
val find : key -> 'a t -> 'a
val find_opt : key -> 'a t -> 'a option
val find_first : (key -> bool) -> 'a t -> key * 'a
val find_first_opt : (key -> bool) -> 'a t -> (key * 'a) option
val find_last : (key -> bool) -> 'a t -> key * 'a
val find_last_opt : (key -> bool) -> 'a t -> (key * 'a) option
val map : ('a -> 'b) -> 'a t -> 'b t
val mapi : (key -> 'a -> 'b) -> 'a t -> 'b t
val to_seq : 'a t -> (key * 'a) Stdlib.Seq.t
val to_seq_from : key -> 'a t -> (key * 'a) Stdlib.Seq.t
val add_seq : (key * 'a) Stdlib.Seq.t -> 'a t -> 'a t
val of_seq : (key * 'a) Stdlib.Seq.t -> 'a t
RngMap.IMap
An integer map: Map.Make(Int)
Make.1-Obj
val len : t -> int
The type of range end
Make.Obj
val len : t -> int
The type of range end
RngMap.Make
How to make a RngMap
from a LenObject
type obj
= Obj.t
The type of the contained object
type obj_off
= obj * int
The type of an object with an offset
type t
The type of the map from address ranges to obj
val empty : t
An empty RngMap
val is_in : objaddr:int -> obj -> int -> bool
Test if an address is inside the object at address objaddr
val at : t -> int -> obj
Get the object containing the address. Throw Not_found
if no object contains the address
val at_opt : t -> int -> obj option
Get the object containing the address. None
if no object contains the address
val at_off : t -> int -> obj_off
Get the object containing the address and the offset of the address inside the object
at_off map addr = (obj, off)
means:
| | | | - map 0 obj start point obj end - |<--------------addr-------------->| - |<---off--->| - |<------len obj------>|
In other words, at_off
allow a change of coordinate from the map frame to the object frame.
Throw Not_found
if no object contains the address
val at_off_opt : t -> int -> obj_off option
Get the object containing the address and the offset of the address inside the object. See at_off
for more explanation.
None
if no object contains the address
val update : (obj -> obj) -> t -> int -> t
Update the binding containing the provided address. If no binding contained the address, this is a no-op
val iteri : (int -> obj -> unit) -> t -> unit
Iter a function over all the objects with their address
val clear : t -> pos:int -> len:int -> t
Clear an area of the RngMap.
If an object is partially in the specified block. It will be removed entirely.
See clear_crop
for a different behavior. See clear_bounds
to allow some bounds to be infinity.
val clear_crop : t -> pos:int -> len:int -> crop:(pos:int -> len:int -> obj -> obj) -> t
Clear an area of the RngMap.
If a block is partially in the specified block, It will be cropped by using the provided crop function.
crop ~pos ~len obj
is supposed to crop the object obj
and keep only the segment [pos:pos +len)
of it (in the object coordinate frame).
val clear_bounds : ?start:int -> ?endp:int -> t -> t
Same as clear
but if a bound is missing, then we erase until infinity in that direction. The target interval is [start:endp)
.
In particular clear_bounds map =
empty
.
val add : t -> int -> obj -> t
Add an object at a specific address. The whole range of addresses covered by the object must be free
val addp : t -> obj_off -> t
Add an object at a specific address. The whole range of addresses covered by the object must be free
val to_seq : ?start:int -> ?endp:int -> t -> (int * obj) Utils.Seq.t
Return a sequence of all the object overlapping the range [start:endp)
. The first and last element may not be entierly contained in the ranged. If any bound is unspecified, it goes to infinity in that direction.
In particular to_seq map
will iterate the entiere RngMap
RngMap.Make
How to make a RngMap
from a LenObject
type obj = Obj.t
The type of the contained object
type obj_off = obj * int
The type of an object with an offset
The type of the map from address ranges to obj
val empty : t
An empty RngMap
val is_in : objaddr:int -> obj -> int -> bool
Test if an address is inside the object at address objaddr
Get the object containing the address. Throw Not_found
if no object contains the address
Get the object containing the address. None
if no object contains the address
Get the object containing the address and the offset of the address inside the object
at_off map addr = (obj, off)
means:
| | | | + map 0 obj start point obj end + |<--------------addr-------------->| + |<---off--->| + |<------len obj------>|
In other words, at_off
allow a change of coordinate from the map frame to the object frame.
Throw Not_found
if no object contains the address
Get the object containing the address and the offset of the address inside the object. See at_off
for more explanation.
None
if no object contains the address
Update the binding containing the provided address. If no binding contained the address, this is a no-op
Iter a function over all the objects with their address
Clear an area of the RngMap.
If an object is partially in the specified block. It will be removed entirely.
See clear_crop
for a different behavior. See clear_bounds
to allow some bounds to be infinity.
Clear an area of the RngMap.
If a block is partially in the specified block, It will be cropped by using the provided crop function.
crop ~pos ~len obj
is supposed to crop the object obj
and keep only the segment [pos:pos +len)
of it (in the object coordinate frame).
Add an object at a specific address. The whole range of addresses covered by the object must be free
Add an object at a specific address. The whole range of addresses covered by the object must be free
Return a sequence of all the object overlapping the range [start:endp)
. The first and last element may not be entierly contained in the ranged. If any bound is unspecified, it goes to infinity in that direction.
In particular to_seq map
will iterate the entiere RngMap
PairLenObject.Obj
RngMap.PairLenObject
For types that do not have an inner length representation, we add it with a pair
type t
= Obj.t * int
The type to be indexed by starting addresses, must have a length
val len : t -> int
The type of range end
RngMap.PairLenObject
For types that do not have an inner length representation, we add it with a pair
type t = Obj.t * int
The type to be indexed by starting addresses, must have a length
val len : t -> int
The type of range end
Utils.RngMap
module IMap : sig ... end
An integer map: Map.Make(Int)
module type Object = sig ... end
A module that represent a simple type with no operation
module type LenObject = sig ... end
A module for type that have a concept of length in the rngMap context
module PairLenObject : functor (Obj : Object) -> LenObject with type t = Obj.t * int
For types that do not have an inner length representation, we add it with a pair
module type S = sig ... end
The signature of the range map
Utils.RngMap
A module giving a map indexing data with address ranges and providing access to quick access to data corresponding to any value in-between.
Each address is bound to at most one object so the ranges are not allowed to overlap.
In practice you dont specify the range. The size of the bound range is provided by the stored object itself that is assumed to have a length. The stored object must thus provide a len
function as specfified by the LenObject
signature. However a length can be added to any Object
using PairLenObject
An important remark is that maps are perfectly allowed to have negative addresses, and will considered negative integer as negative addresses. An object of size 7 starting at -4 will end at 3
For now, this has a pure immutable interface.
module IMap : sig ... end
An integer map: Map.Make(Int)
module type Object = sig ... end
A module that represent a simple type with no operation
module type LenObject = sig ... end
A module for type that have a concept of length in the rngMap context
For types that do not have an inner length representation, we add it with a pair
module type S = sig ... end
The signature of the range map
RngMap.LenObject
A module for type that have a concept of length in the rngMap context
val len : t -> int
The type of range end
RngMap.LenObject
A module for type that have a concept of length in the rngMap context
val len : t -> int
The type of range end
RngMap.Object
A module that represent a simple type with no operation
RngMap.Object
A module that represent a simple type with no operation
RngMap.S
The signature of the range map
type obj_off
= obj * int
The type of an object with an offset
type t
The type of the map from address ranges to obj
val empty : t
An empty RngMap
val is_in : objaddr:int -> obj -> int -> bool
Test if an address is inside the object at address objaddr
val at : t -> int -> obj
Get the object containing the address. Throw Not_found
if no object contains the address
val at_opt : t -> int -> obj option
Get the object containing the address. None
if no object contains the address
val at_off : t -> int -> obj_off
Get the object containing the address and the offset of the address inside the object
at_off map addr = (obj, off)
means:
| | | | - map 0 obj start point obj end - |<--------------addr-------------->| - |<---off--->| - |<------len obj------>|
In other words, at_off
allow a change of coordinate from the map frame to the object frame.
Throw Not_found
if no object contains the address
val at_off_opt : t -> int -> obj_off option
Get the object containing the address and the offset of the address inside the object. See at_off
for more explanation.
None
if no object contains the address
val update : (obj -> obj) -> t -> int -> t
Update the binding containing the provided address. If no binding contained the address, this is a no-op
val iteri : (int -> obj -> unit) -> t -> unit
Iter a function over all the objects with their address
val clear : t -> pos:int -> len:int -> t
Clear an area of the RngMap.
If an object is partially in the specified block. It will be removed entirely.
See clear_crop
for a different behavior. See clear_bounds
to allow some bounds to be infinity.
val clear_crop : t -> pos:int -> len:int -> crop:(pos:int -> len:int -> obj -> obj) -> t
Clear an area of the RngMap.
If a block is partially in the specified block, It will be cropped by using the provided crop function.
crop ~pos ~len obj
is supposed to crop the object obj
and keep only the segment [pos:pos +len)
of it (in the object coordinate frame).
val clear_bounds : ?start:int -> ?endp:int -> t -> t
Same as clear
but if a bound is missing, then we erase until infinity in that direction. The target interval is [start:endp)
.
In particular clear_bounds map =
empty
.
val add : t -> int -> obj -> t
Add an object at a specific address. The whole range of addresses covered by the object must be free
val addp : t -> obj_off -> t
Add an object at a specific address. The whole range of addresses covered by the object must be free
val to_seq : ?start:int -> ?endp:int -> t -> (int * obj) Utils.Seq.t
Return a sequence of all the object overlapping the range [start:endp)
. The first and last element may not be entierly contained in the ranged. If any bound is unspecified, it goes to infinity in that direction.
In particular to_seq map
will iterate the entiere RngMap
RngMap.S
The signature of the range map
type obj_off = obj * int
The type of an object with an offset
The type of the map from address ranges to obj
val empty : t
An empty RngMap
val is_in : objaddr:int -> obj -> int -> bool
Test if an address is inside the object at address objaddr
Get the object containing the address. Throw Not_found
if no object contains the address
Get the object containing the address. None
if no object contains the address
Get the object containing the address and the offset of the address inside the object
at_off map addr = (obj, off)
means:
| | | | + map 0 obj start point obj end + |<--------------addr-------------->| + |<---off--->| + |<------len obj------>|
In other words, at_off
allow a change of coordinate from the map frame to the object frame.
Throw Not_found
if no object contains the address
Get the object containing the address and the offset of the address inside the object. See at_off
for more explanation.
None
if no object contains the address
Update the binding containing the provided address. If no binding contained the address, this is a no-op
Iter a function over all the objects with their address
Clear an area of the RngMap.
If an object is partially in the specified block. It will be removed entirely.
See clear_crop
for a different behavior. See clear_bounds
to allow some bounds to be infinity.
Clear an area of the RngMap.
If a block is partially in the specified block, It will be cropped by using the provided crop function.
crop ~pos ~len obj
is supposed to crop the object obj
and keep only the segment [pos:pos +len)
of it (in the object coordinate frame).
Add an object at a specific address. The whole range of addresses covered by the object must be free
Add an object at a specific address. The whole range of addresses covered by the object must be free
Return a sequence of all the object overlapping the range [start:endp)
. The first and last element may not be entierly contained in the ranged. If any bound is unspecified, it goes to infinity in that direction.
In particular to_seq map
will iterate the entiere RngMap
Utils.Seq
include Stdlib.Seq
val empty : 'a t
val return : 'a -> 'a t
val map : ('a -> 'b) -> 'a t -> 'b t
val filter : ('a -> bool) -> 'a t -> 'a t
val filter_map : ('a -> 'b option) -> 'a t -> 'b t
val flat_map : ('a -> 'b t) -> 'a t -> 'b t
val fold_left : ('a -> 'b -> 'a) -> 'a -> 'b t -> 'a
val iter : ('a -> unit) -> 'a t -> unit
val add_count : ?start:int -> 'a t -> (int * 'a) t
Add a counter starting at start
(default 0) in front of each element of the sequence
val iota : ?start:int -> int -> int t
Generate an integer sequence up to len
. Optionally may start at start
instead of 0
val iota_step_up : ?start:int -> step:int -> endi:int -> int t
Generate an integer sequence up to endi
by stepping step
. Optionally may start at start
instead of 0
val cons : 'a -> 'a t -> unit -> 'a node
Add a new element in front of the sequence. That element will appear first before the rest of the sequence.
Added to Stdlib
in Ocaml 4.11
val find_map : ('a -> 'b option) -> 'a t -> 'b option
Applies the specified function to the elements of the sequence in order, and returns the first result of the form Some v
, or None
if no such result was returned.
See List.find_map
Utils.Seq
This module is for extending the Seq module of the standard library
include module type of struct include Stdlib.Seq end
type !'a t = unit -> 'a node
val is_empty : 'a t -> bool
val length : 'a t -> int
val iter : ('a -> unit) -> 'a t -> unit
val fold_left : ('acc -> 'a -> 'acc) -> 'acc -> 'a t -> 'acc
val iteri : (int -> 'a -> unit) -> 'a t -> unit
val fold_lefti : ('acc -> int -> 'a -> 'acc) -> 'acc -> 'a t -> 'acc
val for_all : ('a -> bool) -> 'a t -> bool
val exists : ('a -> bool) -> 'a t -> bool
val find : ('a -> bool) -> 'a t -> 'a option
val find_index : ('a -> bool) -> 'a t -> int option
val find_mapi : (int -> 'a -> 'b option) -> 'a t -> 'b option
val empty : 'a t
val return : 'a -> 'a t
val init : int -> (int -> 'a) -> 'a t
val unfold : ('b -> ('a * 'b) option) -> 'b -> 'a t
val repeat : 'a -> 'a t
val forever : (unit -> 'a) -> 'a t
val iterate : ('a -> 'a) -> 'a -> 'a t
val of_dispenser : (unit -> 'a option) -> 'a t
val to_dispenser : 'a t -> unit -> 'a option
val ints : int -> int t
Add a counter starting at start
(default 0) in front of each element of the sequence
val iota : ?start:int -> int -> int t
Generate an integer sequence up to len
. Optionally may start at start
instead of 0
val iota_step_up : ?start:int -> step:int -> endi:int -> int t
Generate an integer sequence up to endi
by stepping step
. Optionally may start at start
instead of 0
Add a new element in front of the sequence. That element will appear first before the rest of the sequence.
Added to Stdlib
in Ocaml 4.11
val find_map : ('a -> 'b option) -> 'a t -> 'b option
Applies the specified function to the elements of the sequence in order, and returns the first result of the form Some v
, or None
if no such result was returned.
See List.find_map
Utils.String
include Stdlib.String
val length : string -> int
val get : string -> int -> char
val set : bytes -> int -> char -> unit
val create : int -> bytes
val make : int -> char -> string
val init : int -> (int -> char) -> string
val copy : string -> string
val sub : string -> int -> int -> string
val fill : bytes -> int -> int -> char -> unit
val blit : string -> int -> bytes -> int -> int -> unit
val concat : string -> string list -> string
val iter : (char -> unit) -> string -> unit
val iteri : (int -> char -> unit) -> string -> unit
val map : (char -> char) -> string -> string
val mapi : (int -> char -> char) -> string -> string
val trim : string -> string
val escaped : string -> string
val index : string -> char -> int
val index_opt : string -> char -> int option
val rindex : string -> char -> int
val rindex_opt : string -> char -> int option
val index_from : string -> int -> char -> int
val index_from_opt : string -> int -> char -> int option
val rindex_from : string -> int -> char -> int
val rindex_from_opt : string -> int -> char -> int option
val contains : string -> char -> bool
val contains_from : string -> int -> char -> bool
val rcontains_from : string -> int -> char -> bool
val uppercase : string -> string
val lowercase : string -> string
val capitalize : string -> string
val uncapitalize : string -> string
val uppercase_ascii : string -> string
val lowercase_ascii : string -> string
val capitalize_ascii : string -> string
val uncapitalize_ascii : string -> string
Utils.String
Extension of the String
module of the standard library.
include module type of struct include Stdlib.String end
val to_seq : t -> char Stdlib.Seq.t
val to_seqi : t -> (int * char) Stdlib.Seq.t
val of_seq : char Stdlib.Seq.t -> t
val get_utf_8_uchar : t -> int -> Stdlib.Uchar.utf_decode
val is_valid_utf_8 : t -> bool
val get_utf_16be_uchar : t -> int -> Stdlib.Uchar.utf_decode
val is_valid_utf_16be : t -> bool
val get_utf_16le_uchar : t -> int -> Stdlib.Uchar.utf_decode
val is_valid_utf_16le : t -> bool
val hash : t -> int
val seeded_hash : int -> t -> int
Check if a predicate hold for at least one char
in a string
Utils.Vec
val length : 'a t -> int
val empty : unit -> 'a t
val mem : 'a -> 'a t -> bool
val get : 'a t -> int -> 'a
val unsafe_get : 'a t -> int -> 'a
val set : 'a t -> int -> 'a -> unit
val unsafe_set : 'a t -> int -> 'a -> unit
val update : 'a t -> int -> ('a -> 'a) -> unit
val unsafe_update : 'a t -> int -> ('a -> 'a) -> unit
val copy : 'a t -> 'a t
val for_all : ('a -> bool) -> 'a t -> bool
val exists : ('a -> bool) -> 'a t -> bool
val map : ('a -> 'b) -> 'a t -> 'b t
val mapi : (int -> 'a -> 'b) -> 'a t -> 'b t
val iter : ('a -> unit) -> 'a t -> unit
val iteri : (int -> 'a -> unit) -> 'a t -> unit
val iter_until : limit:int -> ('a -> unit) -> 'a t -> unit
Iterates until the limit
. If the vector is shorter, this is a plain iter
val iteri_until : limit:int -> (int -> 'a -> unit) -> 'a t -> unit
Same as iter_until
but with the index
val to_list : 'a t -> 'a list
val to_listi : 'a t -> (int * 'a) list
val fold_right : ('a -> 'b -> 'b) -> 'a t -> 'b -> 'b
val foldi_right : (int -> 'a -> 'b -> 'b) -> 'a t -> 'b -> 'b
val fold_left : ('a -> 'b -> 'a) -> 'a -> 'b t -> 'a
val foldi_left : (int -> 'b -> 'a -> 'b) -> 'a t -> 'b -> 'b
val add_one : 'a t -> 'a -> unit
Add a new element at the end of the vector
val remove_one : 'a t -> unit
Remove the last element of the vector
val remove_n : 'a t -> int -> unit
Remove the last n elements of the vector
val to_array : 'a t -> 'a array
val of_array : 'a array -> 'a t
val ensure : 'a t -> int -> 'a -> unit
Ensure that the vector has size at least the int by resizing if needed The value provided will be use to set the newly created values.
val keep : 'a t -> int -> unit
Keep only the specified number of element. If the vector was smaller, do nothing.
val resize : 'a t -> int -> 'a -> unit
Resize the vector to the specified size. The value provided will be use to set the newly created values if relevant
val map2 : ('a -> 'b -> 'c) -> 'a t -> 'b t -> 'c t
val map_mut : ('a -> 'a) -> 'a t -> unit
Map the function on the vector mutably by replacing each cell by the result of the function
val map_mut_until : limit:int -> ('a -> 'a) -> 'a t -> unit
Same as map_mut
but stop at limit
. If the vector is shorter than limit
this is a plain map_mut
val fill_all : 'a t -> 'a -> unit
Write the value in all the cells of the vector
val insert : 'a t -> int -> 'a -> unit
Insert an element at the specified position that may be one past the end
val to_seq_sub : 'a t -> pos:int -> len:int -> 'a Utils.Seq.t
val to_seq : 'a t -> 'a Utils.Seq.t
val to_seqi_sub : 'a t -> pos:int -> len:int -> (int * 'a) Utils.Seq.t
val to_seqi : 'a t -> (int * 'a) Utils.Seq.t
val pp : ('a -> Utils.Pp.document) -> 'a t -> Utils.Pp.document
Vector pretty printer
val ppi : ('a -> Utils.Pp.document) -> 'a t -> Utils.Pp.document
Vector pretty printer that also prints the index of each element using Pp.mapping
Utils.Vec
This module provide a resizable array. It is a rename of Res.array with added features
val length : 'a t -> int
val empty : unit -> 'a t
val mem : 'a -> 'a t -> bool
val get : 'a t -> int -> 'a
val unsafe_get : 'a t -> int -> 'a
val set : 'a t -> int -> 'a -> unit
val unsafe_set : 'a t -> int -> 'a -> unit
val update : 'a t -> int -> ('a -> 'a) -> unit
val unsafe_update : 'a t -> int -> ('a -> 'a) -> unit
val for_all : ('a -> bool) -> 'a t -> bool
val exists : ('a -> bool) -> 'a t -> bool
val iter : ('a -> unit) -> 'a t -> unit
val iteri : (int -> 'a -> unit) -> 'a t -> unit
val iter_until : limit:int -> ('a -> unit) -> 'a t -> unit
Iterates until the limit
. If the vector is shorter, this is a plain iter
val iteri_until : limit:int -> (int -> 'a -> unit) -> 'a t -> unit
Same as iter_until
but with the index
val to_list : 'a t -> 'a list
val to_listi : 'a t -> (int * 'a) list
val fold_right : ('a -> 'b -> 'b) -> 'a t -> 'b -> 'b
val foldi_right : (int -> 'a -> 'b -> 'b) -> 'a t -> 'b -> 'b
val fold_left : ('a -> 'b -> 'a) -> 'a -> 'b t -> 'a
val foldi_left : (int -> 'b -> 'a -> 'b) -> 'a t -> 'b -> 'b
val add_one : 'a t -> 'a -> unit
Add a new element at the end of the vector
val remove_one : 'a t -> unit
Remove the last element of the vector
val remove_n : 'a t -> int -> unit
Remove the last n elements of the vector
val to_array : 'a t -> 'a array
val of_array : 'a array -> 'a t
val ensure : 'a t -> int -> 'a -> unit
Ensure that the vector has size at least the int by resizing if needed The value provided will be use to set the newly created values.
val keep : 'a t -> int -> unit
Keep only the specified number of element. If the vector was smaller, do nothing.
val resize : 'a t -> int -> 'a -> unit
Resize the vector to the specified size. The value provided will be use to set the newly created values if relevant
val map_mut : ('a -> 'a) -> 'a t -> unit
Map the function on the vector mutably by replacing each cell by the result of the function
val map_mut_until : limit:int -> ('a -> 'a) -> 'a t -> unit
val fill_all : 'a t -> 'a -> unit
Write the value in all the cells of the vector
val insert : 'a t -> int -> 'a -> unit
Insert an element at the specified position that may be one past the end
val pp : ('a -> Pp.document) -> 'a t -> Pp.document
Vector pretty printer
val ppi : ('a -> Pp.document) -> 'a t -> Pp.document
Vector pretty printer that also prints the index of each element using Pp.mapping
Utils.WeakMap
val create : int -> ('a, 'b) t
The initial map
val add : ('a, 'b) t -> 'a -> 'b -> unit
Add a mapping to the map. raise Exists if a is already mapped
val get : ('a, 'b) t -> 'a -> 'b
Retrieves a value from the table. Throws Hashtbl.Not_found if the binding do not exists
Utils.WeakMap
This module provide a simple way of associated key to value without keeping them alive The key is owned by the map but the value is weakly pointed to. When the value is deleted by the GC, the binding disappears.
The value type must be heap allocated (basically not an integer or another weird type)
val create : int -> ('a, 'b) t
The initial map
val add : ('a, 'b) t -> 'a -> 'b -> unit
Add a mapping to the map. raise Exists if a is already mapped
val get : ('a, 'b) t -> 'a -> 'b
Retrieves a value from the table. Throws Hashtbl.Not_found if the binding do not exists
Utils.WeakPtr
val empty : unit -> 'a t
Make a new empty pointer
val make : 'a -> 'a t
Make a new pointer and set it with the given value
val seto : 'a t -> 'a option -> unit
Set or clear the value inside the pointer depending of the option
val set : 'a t -> 'a -> unit
Set the value inside the pointer
val reset : 'a t -> unit
Clear the value inside the pointer
val geto : 'a t -> 'a option
Retrieves the value and return None if the pointer is now empty
val get : 'a t -> 'a
Retrieves the value. Will raise Deleted if the value was deleted by the GC
val check : 'a t -> bool
Check if the pointer is filled
Utils.WeakPtr
This module provide a simple weak pointer that can be rendered invalid by the GC
val empty : unit -> 'a t
Make a new empty pointer
val make : 'a -> 'a t
Make a new pointer and set it with the given value
val seto : 'a t -> 'a option -> unit
Set or clear the value inside the pointer depending of the option
val set : 'a t -> 'a -> unit
Set the value inside the pointer
val reset : 'a t -> unit
Clear the value inside the pointer
val geto : 'a t -> 'a option
Retrieves the value and return None if the pointer is now empty
val get : 'a t -> 'a
Retrieves the value. Will raise Deleted if the value was deleted by the GC
val check : 'a t -> bool
Check if the pointer is filled
Utils
module Array : sig ... end
module BitVec : sig ... end
module Bits : sig ... end
module BytesSeq : sig ... end
module Cache : sig ... end
module Cmd : sig ... end
module CmdlinerHelper : sig ... end
module Counter : sig ... end
module Files : sig ... end
module FullVec : sig ... end
module Fun : sig ... end
module HashVector : sig ... end
module IdMap : sig ... end
module IntBits : sig ... end
module List : sig ... end
module Logs : sig ... end
module Option : sig ... end
module Pair : sig ... end
module Pp : sig ... end
module Protect : sig ... end
module Raise : sig ... end
module RngMap : sig ... end
module Seq : sig ... end
module String : sig ... end
module Vec : sig ... end
module WeakMap : sig ... end
module WeakPtr : sig ... end
Utils
module BitVec : sig ... end
This module provides an interface for a bit vector of dynamic size.
module Bits : sig ... end
Like bytes, but for bit level manipulation. The underlying type is still bytes and thus the size has to be a multiple of 8.
module BytesSeq : sig ... end
This module represent a byte sub view on a bytes
object. Contrary to Bytes
it is a non-owning immutable view. It do not prevent the original bytes from being modified, and the changes will be propagated in the view. It is additional sugar on top of Linksem's Byte_sequence_wrapper
module Cache : sig ... end
This module implement a caching system i.e a persistant structure stored on the disk.
module Cmd : sig ... end
This module provides high-level interaction with external processes.
module CmdlinerHelper : sig ... end
This module provide some Cmdliner
helper functions.
module Counter : sig ... end
This module provide a small counter object which is just a int reference on which get
can be called to get an identifier and increment the reference
module Files : sig ... end
This module provides simplified file management and some channel interaction function.
module FullVec : sig ... end
A full vector is a vector in which all non-negative integer are bound.
module HashVector : sig ... end
An hash vector allow a vector to behave as hash map indexed by small integers. It is hash map with the identity hash function.
module IdMap : sig ... end
An IdMap is a map that associate an id to each key (and thus to each value).
module IntBits : sig ... end
Manipulate an int as bitfield of size 31 or 63.
module Logs : sig ... end
This module provide a logging system for each module.
module Pair : sig ... end
This module contain random utility functions dealing with pairs
module Pp : sig ... end
This module provide all pretty printing functionality. It's main goal is not directly handle the output but to handle how to layout complex data structure in a text format.
module Protect : sig ... end
This module provide try-with-finally kind of exception handling.
module Raise : sig ... end
This module provide convenience facilities to raise exception or other exception management
module RngMap : sig ... end
A module giving a map indexing data with address ranges and providing access to quick access to data corresponding to any value in-between.
module Sym : sig ... end
module Vec : sig ... end
This module provide a resizable array. It is a rename of Res.array with added features
module WeakMap : sig ... end
This module provide a simple way of associated key to value without keeping them alive The key is owned by the map but the value is weakly pointed to. When the value is deleted by the GC, the binding disappears.
module WeakPtr : sig ... end
This module provide a simple weak pointer that can be rendered invalid by the GC
Z3.CheckContext
val counter : Utils.Counter.t
val openc : unit -> unit
val num : unit -> int
val closec : unit -> unit
Z3.CheckContext
ContextCounter.S
Z3.ContextCounter
Module for handling a context numbering scheme automatically.
To use it do: module Whatever = ContextCounter(struct let str= "name here" end)
Then you can use it with Whatever.openc ()
and Whatever.closec ()
. You can get the current instance number with Whatever.num()
at any time.
val counter : Utils.Counter.t
val openc : unit -> unit
Open a new context of that ContextCounter
. Ensure the server is started
val closec : unit -> unit
Close a context opened with openc
. Assert that the current context was indeed opened by this module
Z3.ContextCounter
Module for handling a context numbering scheme automatically.
To use it do: module Whatever = ContextCounter(struct let str= "name here" end)
Then you can use it with Whatever.openc ()
and Whatever.closec ()
. You can get the current instance number with Whatever.num()
at any time.
module S : Utils.Logs.String
val counter : Utils.Counter.t
Close a context opened with openc
. Assert that the current context was indeed opened by this module
Make.1-Var
Make.Var
Z3.Make
type var
= Var.t
The goal of those operation is to declare variables with their types to the SMT solver. The Htbl
allow to declare every variable only once.
type exp
= (var, Ast.no) Exp.Typed.t
The type of expression on which SMT operation are made
val declare_var_always : server -> var -> unit
Declare the variable regardless of whether it has already been declared
val send_assert_decl : server -> declared:unit Htbl.t -> exp -> unit
Literally just declare_vars
then send_assert
val check : server -> exp -> bool option
Check if this expression is always true in the current context. None
is returned when the SMT solver didn't know
Those operations do not require a context to operate. They require not setup and tear-down. They are standalone and fully automated.
On the other hand doing multiple one of those in sequence if much less efficient than using properly the functions of previous section.
val simplify_full : exp -> exp
Do a standalone simplification in it's own context. Do not need anything to be already declared or any context to be opened.
val check_full : ?hyps:exp list -> exp -> bool option
Do a standalone check of whether the expression is implied by the list of hypothesis.
val check_sat_full : exp list -> bool option
Do a standalone check of whether the set of assertion is sat
Z3.Make
type var = Var.t
The goal of those operation is to declare variables with their types to the SMT solver. The Htbl
allow to declare every variable only once.
type exp = (var, Ast.no) Exp.Typed.t
The type of expression on which SMT operation are made
Declare the variable regardless of whether it has already been declared
Declare the variable if it's not in declared
. In that case, it also add it to declared
Declare all not yet declared the variable in the expression, then add them to declared
Simplify the expression in the current context. All the variable must already have been declared.
Literally just declare_vars
then simplify
Literally just declare_vars
then send_assert
Check if this expression is always true in the current context. None
is returned when the SMT solver didn't know
Check if this expression is true on at least one assignment of the variables.ioserver None
is returned when the SMT solver didn't know
Those operations do not require a context to operate. They require not setup and tear-down. They are standalone and fully automated.
On the other hand doing multiple one of those in sequence if much less efficient than using properly the functions of previous section.
Do a standalone simplification in it's own context. Do not need anything to be already declared or any context to be opened.
Do a standalone check of whether the expression is implied by the list of hypothesis.
val check_sat_full : exp list -> bool option
Do a standalone check of whether the set of assertion is sat
Z3.SimpContext
val counter : Utils.Counter.t
val openc : unit -> unit
val num : unit -> int
val closec : unit -> unit
Z3.SimpContext
Z3
This module handles a Z3 server
For high level usage, call start
or ensure_started
then instantiate the Make
functor and use operation in the section about Context less operation:
For a more medium level usage, you may want to manage your context manually before making requests. The best way of doing that is by using a ContextCounter
.
Once you are in the correct context, you can use your instantiated version of Make
to do operations such as: S.declare_var
or S.send_assert
to build your context, and then S.simplify
, S.check
of S.check_sat
.
For low-level details (All function in the first two section of this module should probably be reserved to those who understand the implementation)
The module keeps Z3 as a child process and communicates through pipes using Cmd.IOServer
.
start
sends the introduction in intro.smt2
to Z3 so that it is available in any context. SMT answer are parsed from the pipe with Files.input_sexp
. If the wrong number of answer is expected, the system will just deadlock.
type context_elem
=
{
name : string; |
mutable num : int; |
}
An element of the context stack. It has a name and a declaration number.
The declaration number is incremented at each declaration in the context.
type context
= context_elem list
The type of a SMT context stack
val start_context : context
The starting context
val context_elem_to_string : context_elem -> string
Give a string representation of a context_elem
type server
=
{
ioserver : Utils.Cmd.IOServer.t; |
config : Config.File.Z3.t; |
mutable context : context; |
}
The type of a Z3 server
val server : server option Stdlib.ref
The global Z3 server
val get_server : unit -> server
Assume the server is started and returns it.
val get_context_string : server -> string
Give a string representation of the current context for error reporting
val incr_context : server -> unit
Increment the declaration number of the top context_elem
.
This section handle raw and less raw direct interaction with the Z3 server.
The send_*
function are for sending information to the server with a server handle. This avoided checking if the server is open at each declaration.
The read_*
functions are the same the other way around.
val send_string : server -> string -> unit
Send a string to the server and increment the declaration number in the context
val send_smt : server -> ?ppv:('a -> Utils.Pp.document) -> ('b, 'a, string, AstGen.Def.Size.t) AstGen.Ott.smt -> unit
Send a smt statement to the server and increment the declaration number in the context
val read_string : server -> string
Read a string from the server (A Z3 answer is always a valid sexp)
val read_smt_ans : server -> Ast.rsmt_ans
Read a smt_ans
from the server
val request : ?ppv:('a -> Utils.Pp.document) -> ('b, 'a, string, AstGen.Def.Size.t) AstGen.Ott.smt -> Ast.rsmt_ans
Make a request to the server and expect an answer. Will hang if there is no answer
val command : ?ppv:('a -> Utils.Pp.document) -> ('b, 'a, string, AstGen.Def.Size.t) AstGen.Ott.smt -> unit
Send a command or a declaration to the server
val expect_version : server -> Ast.rsmt_ans -> string
Expect a version answer and fails if it is not the case
val expect_exp : server -> Ast.rsmt_ans -> Ast.rexp
Expect an expression answer and fails if it is not the case
val is_context_sat : server -> bool option
Check if the current context is sat
val ensure_stopped : unit -> unit
Call stop
if the server wasn't stopped
val ensure_started : unit -> unit
Call start
if the server wasn't started
Z3 contexts are managed in a stack way.
There are implemented using (push)
and (pop)
on the Z3 side.
open_context
can open a named context. However if it is expected to open a given context name multiple times, using ContextCounter
is advised.
val open_context : server -> string -> unit
Open a new context with a name.
val close_context : server -> unit
Closes current context of the server
module ContextCounter : functor (S : Utils.Logs.String) -> sig ... end
Module for handling a context numbering scheme automatically.
This section provide functor that can be instantiated to get easy to use SMT functionality
module type Var = sig ... end
The functors in this section require a bit more support from variable than plain Exp.Var
module type S = sig ... end
module SimpContext : sig ... end
module CheckContext : sig ... end
Z3
This module handles a Z3 server
For high level usage, call start
or ensure_started
then instantiate the Make
functor and use operation in the section about Context less operation:
For a more medium level usage, you may want to manage your context manually before making requests. The best way of doing that is by using a ContextCounter
.
Once you are in the correct context, you can use your instantiated version of Make
to do operations such as: S.declare_var
or S.send_assert
to build your context, and then S.simplify
, S.check
of S.check_sat
.
For low-level details (All function in the first two section of this module should probably be reserved to those who understand the implementation)
The module keeps Z3 as a child process and communicates through pipes using Utils.Cmd.IOServer
.
start
sends the introduction in intro.smt2
to Z3 so that it is available in any context. SMT answer are parsed from the pipe with Utils.Files.input_sexp
. If the wrong number of answer is expected, the system will just deadlock.
An element of the context stack. It has a name and a declaration number.
The declaration number is incremented at each declaration in the context.
type context = context_elem list
The type of a SMT context stack
val start_context : context
The starting context
val context_elem_to_string : context_elem -> string
Give a string representation of a context_elem
type server = {
ioserver : Utils.Cmd.IOServer.t;
config : Config.File.Z3.t;
mutable context : context;
}
The type of a Z3 server
val server : server option Stdlib.ref
The global Z3 server
val get_server : unit -> server
Assume the server is started and returns it.
val get_context_string : server -> string
Give a string representation of the current context for error reporting
val incr_context : server -> unit
Increment the declaration number of the top context_elem
.
This section handle raw and less raw direct interaction with the Z3 server.
The send_*
function are for sending information to the server with a server handle. This avoided checking if the server is open at each declaration.
The read_*
functions are the same the other way around.
The request
and command
are high level versions
val send_string : server -> string -> unit
Send a string to the server and increment the declaration number in the context
val send_smt :
+ server ->
+ ?ppv:('a -> Utils.Pp.document) ->
+ ('b, 'a, string, AstGen.Def.Size.t) AstGen.Ott.smt ->
+ unit
Send a smt statement to the server and increment the declaration number in the context
val read_string : server -> string
Read a string from the server (A Z3 answer is always a valid sexp)
val read_smt_ans : server -> Ast.rsmt_ans
Read a smt_ans
from the server
val request :
+ ?ppv:('a -> Utils.Pp.document) ->
+ ('b, 'a, string, AstGen.Def.Size.t) AstGen.Ott.smt ->
+ Ast.rsmt_ans
Make a request to the server and expect an answer. Will hang if there is no answer
val command :
+ ?ppv:('a -> Utils.Pp.document) ->
+ ('b, 'a, string, AstGen.Def.Size.t) AstGen.Ott.smt ->
+ unit
Send a command or a declaration to the server
val expect_version : server -> Ast.rsmt_ans -> string
Expect a version answer and fails if it is not the case
val expect_exp : server -> Ast.rsmt_ans -> Ast.rexp
Expect an expression answer and fails if it is not the case
val is_context_sat : server -> bool option
Check if the current context is sat
Call stop
if the server wasn't stopped
Call start
if the server wasn't started
val ensure_started_get : unit -> server
Call start
if the server wasn't started, then return the server
Reset the Z3 server, forgetting everything. Useful for resetting in a test failure context, but probably shouldn't be used in normal operations
Z3 contexts are managed in a stack way.
There are implemented using (push)
and (pop)
on the Z3 side.
open_context
can open a named context. However if it is expected to open a given context name multiple times, using ContextCounter
is advised.
val open_context : server -> string -> unit
Open a new context with a name.
val close_context : server -> unit
Closes current context of the server
module ContextCounter (S : Utils.Logs.String) : sig ... end
Module for handling a context numbering scheme automatically.
This section provide functor that can be instantiated to get easy to use SMT functionality
module type Var = sig ... end
The functors in this section require a bit more support from variable than plain Exp.Var
module type S = sig ... end
module SimpContext : sig ... end
module CheckContext : sig ... end
module Test : sig ... end
Z3.S
The goal of those operation is to declare variables with their types to the SMT solver. The Htbl
allow to declare every variable only once.
type exp
= (var, Ast.no) Exp.Typed.t
The type of expression on which SMT operation are made
val declare_var_always : server -> var -> unit
Declare the variable regardless of whether it has already been declared
val send_assert_decl : server -> declared:unit Htbl.t -> exp -> unit
Literally just declare_vars
then send_assert
val check : server -> exp -> bool option
Check if this expression is always true in the current context. None
is returned when the SMT solver didn't know
Those operations do not require a context to operate. They require not setup and tear-down. They are standalone and fully automated.
On the other hand doing multiple one of those in sequence if much less efficient than using properly the functions of previous section.
val simplify_full : exp -> exp
Do a standalone simplification in it's own context. Do not need anything to be already declared or any context to be opened.
val check_full : ?hyps:exp list -> exp -> bool option
Do a standalone check of whether the expression is implied by the list of hypothesis.
val check_sat_full : exp list -> bool option
Do a standalone check of whether the set of assertion is sat
Z3.S
The goal of those operation is to declare variables with their types to the SMT solver. The Htbl
allow to declare every variable only once.
type exp = (var, Ast.no) Exp.Typed.t
The type of expression on which SMT operation are made
Declare the variable regardless of whether it has already been declared
Declare the variable if it's not in declared
. In that case, it also add it to declared
Declare all not yet declared the variable in the expression, then add them to declared
Simplify the expression in the current context. All the variable must already have been declared.
Literally just declare_vars
then simplify
Literally just declare_vars
then send_assert
Check if this expression is always true in the current context. None
is returned when the SMT solver didn't know
Check if this expression is true on at least one assignment of the variables.ioserver None
is returned when the SMT solver didn't know
Those operations do not require a context to operate. They require not setup and tear-down. They are standalone and fully automated.
On the other hand doing multiple one of those in sequence if much less efficient than using properly the functions of previous section.
Do a standalone simplification in it's own context. Do not need anything to be already declared or any context to be opened.
Do a standalone check of whether the expression is implied by the list of hypothesis.
val check_sat_full : exp list -> bool option
Do a standalone check of whether the set of assertion is sat
Z3.Var
The functors in this section require a bit more support from variable than plain Exp.Var
Z3.Var
The functors in this section require a bit more support from variable than plain Exp.Var
read-dwarf is a tool for exploring, symbolically executing and validating ELF binaries generated from C, using DWARF debugging information. It will be used to perform translation validation between O0 and O2 binaries. It is written in OCaml and relies on many other tools. Its current set of features allows a user to explore binaries with the source code inlined, and for simple cases, symbolically evaluate a function, check two versions of the same function (compiled at O0 and O2 optimisation levels) evaluate to the same machine state (given a simulation relation) and compute branch tables for indirect jumps. We intend to build upon this foundation of features to handle all more functions, by incorporating information from higher-levels, inferring types and pointer-provenance, inferring simulation relations automatically, and supporting concurrency models for Arm v8.
This documentation is for the internal code of read-dwarf.
Currently read-dwarf does not yet have the actual infrastructure to find a simulation relation between two binaries, however it already has all the necessary infrastructure to run symbolicaly a binary through any control flow path, and infer the C type along the way. The exact C-like type-system used is a bit more advanced than C.
I'll attempt here to give a pipeline overview of what happens when you want to run a function symbolically. All of this is done by the Run.Func
module that provide a CLI to run a single function symbolically.
Elf
and Dw
modules. In this phase C-Type linking happens as described in C type linking: From LinksemDw.Func
format.Architecture
interface is defined in the virtual module Sig
but the only implementation is in src/arch/aarch64/sig.ml
(dune doesn't build docs for virtual module implementations :().Isla.Server
will be started and used by Run.Init
to fetch the machine initial State
. This will call isla
to get the trace of the nop
instruction. See InstructionPipeline
State
can be computed from the machine initial state and the ABI.Run.Runner
with the DWARF information and initialize it.Run.Block
which is a piece of code that can run a delimited block of code. We give to it the end conditions provided by the command line like potential breakpoints. Then we run it.Run.Block
calls the Run.Runner
on each instruction as needed to move forward and build the tree (State.Tree
) of possible states. For each instruction, the whole InstructionPipeline
is run to generate a set of Trace
s and this set of traces is run on a State
to get the next state. See SymbolicExecution
.Utils.Pp
and Utils.Logs
. See Printing
Here is a list of top-level pages that each explains a subgroup of functionality:
Architecture
: All modules related to achitecture representationBinaryAnalysis
: All modules about reading ELF and DWARF informationCLI
: All modules defining the command line interfaces.Configuration
: Configuration organisationInstructionPipeline
: All modules related to instruction semantics processing.Printing
: Generic information about printing and loggingSymbolicExecution
: All modules related to top-level symbolic execution.SymbolicExpressions
: All modules related to symbolic expression manipulationTypeInference
: All modules about the C type system and type inference.Utilities
: List of modules that provide generic functionalityHere is a list of the dependency libraries that are used, and links to their documentation (for those that have some).
linksem
: ELF and DWARF Parser and analyzerisla-lang
: Isla traces parsercmdliner
: Library to parse the command line.pprint
: Pretty-printing library. Use it via the Utils.Pp
module.zarith
: Big integer library. Used by linksem
and all linksem
interacting modules, and by Utils.BitVec
.res
: Resizable array. Use it via the Vec
moduleocamlgraph
: Graph library. Only used in Analyse
for now; may be used elsewhere later.toml
Toml Parsing library. Only used in Config.File
to parse the config file. It should not be used elsewhere.uutf
: Unicode library. Only used to do UTF-8 character folding in Analyse
. Those utility function should probably move to Utils.String
to be accessible elsewhere.Here is an alphabetical list of all modules, except src/arch/aarch64/sig.ml
.
read-dwarf is a tool for exploring, symbolically executing and validating ELF binaries generated from C, using DWARF debugging information. It will be used to perform translation validation between O0 and O2 binaries. It is written in OCaml and relies on many other tools. Its current set of features allows a user to explore binaries with the source code inlined, and for simple cases, symbolically evaluate a function, check two versions of the same function (compiled at O0 and O2 optimisation levels) evaluate to the same machine state (given a simulation relation) and compute branch tables for indirect jumps. We intend to build upon this foundation of features to handle all more functions, by incorporating information from higher-levels, inferring types and pointer-provenance, inferring simulation relations automatically, and supporting concurrency models for Arm v8.
This documentation is for the internal code of read-dwarf.
Currently read-dwarf does not yet have the actual infrastructure to find a simulation relation between two binaries, however it already has all the necessary infrastructure to run symbolicaly a binary through any control flow path, and infer the C type along the way. The exact C-like type-system used is a bit more advanced than C.
I'll attempt here to give a pipeline overview of what happens when you want to run a function symbolically. All of this is done by the Run.Func
module that provide a CLI to run a single function symbolically.
Elf
and Dw
modules. In this phase C-Type linking happens as described in C type linking: From LinksemDw.Func
format.Architecture
interface is defined in the virtual module Sig
but the only implementation is in src/arch/aarch64/sig.ml
(dune doesn't build docs for virtual module implementations :().Isla.Server
will be started and used by Run.Init
to fetch the machine initial State
. This will call isla
to get the trace of the nop
instruction. See InstructionPipeline
State
can be computed from the machine initial state and the ABI.Run.Runner
with the DWARF information and initialize it.Run.Block
which is a piece of code that can run a delimited block of code. We give to it the end conditions provided by the command line like potential breakpoints. Then we run it.Run.Block
calls the Run.Runner
on each instruction as needed to move forward and build the tree (State.Tree
) of possible states. For each instruction, the whole InstructionPipeline
is run to generate a set of Trace
s and this set of traces is run on a State
to get the next state. See SymbolicExecution
.Utils.Pp
and Utils.Logs
. See Printing
Here is a list of top-level pages that each explains a subgroup of functionality:
Architecture
: All modules related to achitecture representationBinaryAnalysis
: All modules about reading ELF and DWARF informationCLI
: All modules defining the command line interfaces.Configuration
: Configuration organisationInstructionPipeline
: All modules related to instruction semantics processing.Printing
: Generic information about printing and loggingSymbolicExecution
: All modules related to top-level symbolic execution.SymbolicExpressions
: All modules related to symbolic expression manipulationTypeInference
: All modules about the C type system and type inference.Utilities
: List of modules that provide generic functionalityHere is a list of the dependency libraries that are used, and links to their documentation (for those that have some).
linksem
: ELF and DWARF Parser and analyzerisla-lang
: Isla traces parsercmdliner
: Library to parse the command line.pprint
: Pretty-printing library. Use it via the Utils.Pp
module.zarith
: Big integer library. Used by linksem
and all linksem
interacting modules, and by Utils.BitVec
.res
: Resizable array. Use it via the Vec
moduleocamlgraph
: Graph library. Only used in Analyse
for now; may be used elsewhere later.toml
Toml Parsing library. Only used in Config.File
to parse the config file. It should not be used elsewhere.uutf
: Unicode library. Only used to do UTF-8 character folding in Analyse
. Those utility function should probably move to Utils.String
to be accessible elsewhere.Here is an alphabetical list of all modules, except src/arch/aarch64/sig.ml
.
Analyse
Arch
This module adds some code that is related to the Architecture specific modules but is in itself architecture independent.Ast
BranchTable
Config
Ctype
This module provides the internal C-like type system. This type system is slightly different than the normal C type system. This module only provides the Ocaml datastructure to represent those types. The typing rules are implemented in Trace.Typer
, where they are applied direDw
This module provides the specifically interpreted DWARF information needed for read-dwarf operationsElf
Exp
This module intends to provider a convenience expression module by lifting function like equality or pretty printing from variable to expressions.AstGen
Isla
Other_cmds
Run
Simrel
State
Tests
Trace
Utils
Z3
This module handles a Z3 server