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.BaseAnalyse.Collectedcollect the various test analysis data
val mk_analysis : ElfTypes.test -> string -> string option -> Analyse__.CollectedType.analysisAnalyse.Collectedcollect the various test analysis data
val mk_analysis :
+ ElfTypes.test ->
+ string ->
+ string option ->
+ Analyse__.CollectedType.analysisAnalyse.DwarfLineInfopost-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 -> stringval pp_sequence_concise : evaluated_line_info_sequence -> stringval pp_elie_concise : evaluated_line_info_entry -> stringval split_into_sequences : (Dwarf.line_number_header * Dwarf.line_number_registers list) -> evaluated_line_info_sequence listval split_into_entries : evaluated_line_info_sequence -> evaluated_line_info_entry listval mk_line_info : Dwarf.evaluated_line_info -> Analyse__.ControlFlowTypes.instruction array -> evaluated_line_info_for_instruction list arrayval source_file_cache : ((string option * string option * string) * string array option) list Stdlib.refval actual_directories : string option -> (string option * string option * string) -> string * stringval source_line : (string option * string option * string) -> int -> string optionval pp_source_line : string option -> int -> stringval mk_subprogram_name : Dwarf.dwarf_static -> evaluated_line_info_for_instruction -> stringval pp_dwarf_source_file_lines' : Types.ppmode -> Dwarf.dwarf_static -> bool -> bool -> evaluated_line_info_for_instruction -> stringval dwarf_source_file_line_numbers_by_index : ElfTypes.test -> evaluated_line_info_for_instruction list array -> int -> (string * int) listAnalyse.DwarfLineInfopost-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 -> stringval pp_elie_concise : evaluated_line_info_entry -> stringval split_into_sequences :
+ (Dwarf.line_number_header * Dwarf.line_number_registers list) ->
+ evaluated_line_info_sequence listval split_into_entries :
+ evaluated_line_info_sequence ->
+ evaluated_line_info_entry listval mk_line_info :
+ Dwarf.evaluated_line_info ->
+ Analyse__.ControlFlowTypes.instruction array ->
+ evaluated_line_info_for_instruction list arrayfind and pretty-print source lines for addresses
val mk_subprogram_name :
+ Dwarf.dwarf_static ->
+ evaluated_line_info_for_instruction ->
+ stringval pp_dwarf_source_file_lines' :
+ Types.ppmode ->
+ Dwarf.dwarf_static ->
+ bool ->
+ bool ->
+ evaluated_line_info_for_instruction ->
+ stringval dwarf_source_file_line_numbers_by_index :
+ ElfTypes.test ->
+ evaluated_line_info_for_instruction list array ->
+ int ->
+ (string * int) listAnalyse.Elfval parse_elf_file : string -> ElfTypes.testval marshal_to_file : string -> 'a -> unitval marshal_from_file : string -> ElfTypes.test optionAnalyse.Elfpp symbol map
use linksem to parse ELF file and extract DWARF info
val parse_elf_file : string -> ElfTypes.testmarshal and unmarshal test
val marshal_from_file : string -> ElfTypes.test optionAnalyse.ElfTypestype 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.ElfTypestype 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.Globalsval elf : string option Stdlib.refval branch_table_data_file : string option Stdlib.refval objdump_d : string option Stdlib.refval elf2 : string option Stdlib.refval branch_table_data_file2 : string option Stdlib.refval objdump_d2 : string option Stdlib.refval qemu_log : string option Stdlib.refval comp_dir : string option Stdlib.refval cfg_dot_file : string option Stdlib.refval cfg_source_nodes : string option Stdlib.refval cfg_source_nodes2 : string option Stdlib.refval out_file : string option Stdlib.refval out_dir : string option Stdlib.refval clip_binary : bool Stdlib.refval show_vars : bool Stdlib.refval show_cfa : bool Stdlib.refval show_source : bool Stdlib.refval ppmode : Types.ppmode Stdlib.refval src_target_dir : string option Stdlib.refval copy_sources_dry_run : bool Stdlib.refval skylight : bool Stdlib.refAnalyse.Globalsval ppmode : Types.ppmode Stdlib.refAnalyse.Pptype 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 -> stringval render_class_name : render_kind -> stringval html_idiom : html_idiomval css : Types.ppmode -> render_kind -> string -> stringval last_frame_info : string Stdlib.refval last_var_info : string list Stdlib.refval last_source_info : string Stdlib.refval pp_instruction_init : unit -> unitval pp_instruction : Types.ppmode -> ElfTypes.test -> Analyse__.CollectedType.analysis -> int -> int -> Analyse__.ControlFlowTypes.instruction -> stringval skylight : unit -> stringval chunk_filename_whole : Types.ppmode -> 'a -> string -> string * stringval chunk_filename_per_cu : Types.ppmode -> 'a -> string -> Dwarf.sdt_compilation_unit -> string * stringval wrap_chunks : Types.ppmode -> ('a * 'b * string) list -> ('a * 'b * string) listval whole_file_chunks : Types.ppmode -> ElfTypes.test -> Analyse__.CollectedType.analysis -> 'a -> ('b * string * string * 'c) list list -> (string * string * string) listval pp_instructions_ranged : Types.ppmode -> ElfTypes.test -> Analyse__.CollectedType.analysis -> (Utils.addr * Utils.addr) -> stringval chunks_of_ranged_cu : Types.ppmode -> ElfTypes.test -> Analyse__.CollectedType.analysis -> 'a -> ((Utils.natural * Utils.natural) * Dwarf.sdt_compilation_unit) -> string * (string * string * string) listval wrap_body : Types.ppmode -> (string * string * string) -> stringval output_file : (string * string * string) -> unitval output_whole_file_files : Types.ppmode -> ElfTypes.test -> Analyse__.CollectedType.analysis -> 'a -> ('b * string * string * 'c) list list -> unitval 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 listval pp_test_analysis : Types.ppmode -> ElfTypes.test -> Analyse__.CollectedType.analysis -> stringAnalyse.Pprender collected analysis data to text or css
val render_colour : render_kind -> stringval render_class_name : render_kind -> stringval html_idiom : html_idiomval css : Types.ppmode -> render_kind -> string -> stringpretty-print one instruction
val pp_instruction :
+ Types.ppmode ->
+ ElfTypes.test ->
+ Analyse__.CollectedType.analysis ->
+ int ->
+ int ->
+ Analyse__.ControlFlowTypes.instruction ->
+ stringpretty-print test analysis
val chunk_filename_whole : Types.ppmode -> 'a -> string -> string * stringval chunk_filename_per_cu :
+ Types.ppmode ->
+ 'a ->
+ string ->
+ Dwarf.sdt_compilation_unit ->
+ string * stringval wrap_chunks :
+ Types.ppmode ->
+ ('a * 'b * string) list ->
+ ('a * 'b * string) listval whole_file_chunks :
+ Types.ppmode ->
+ ElfTypes.test ->
+ Analyse__.CollectedType.analysis ->
+ 'a ->
+ ('b * string * string * 'c) list list ->
+ (string * string * string) listval pp_instructions_ranged :
+ Types.ppmode ->
+ ElfTypes.test ->
+ Analyse__.CollectedType.analysis ->
+ (Utils.addr * Sym_ocaml.Num.t) ->
+ stringval chunks_of_ranged_cu :
+ Types.ppmode ->
+ ElfTypes.test ->
+ Analyse__.CollectedType.analysis ->
+ 'a ->
+ ((Utils.natural * Utils.natural) * Dwarf.sdt_compilation_unit) ->
+ string * (string * string * string) listval wrap_body : Types.ppmode -> (string * string * string) -> stringval output_whole_file_files :
+ Types.ppmode ->
+ ElfTypes.test ->
+ Analyse__.CollectedType.analysis ->
+ 'a ->
+ ('b * string * string * 'c) list list ->
+ unitval 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 listval pp_test_analysis :
+ Types.ppmode ->
+ ElfTypes.test ->
+ Analyse__.CollectedType.analysis ->
+ stringAnalyse.Typestypes shared between analyse* and the read-dwarf top-level
Analyse.Typestypes shared between analyse* and the read-dwarf top-level
Analyse.UtilsMiscellaneous types and utility functions used throughout the analyse code
type addr = naturalmachine address
val pp_addr : natural -> stringval measure_time : boolval time : string -> ('a -> 'b) -> 'a -> 'bPrint the time this function call took. The string is just for the printed message
val esc : Types.ppmode -> string -> stringval sys_command : string -> unitAnalyse.UtilsMiscellaneous types and utility functions used throughout the analyse code
type natural = Utils.Sym.tTODO: Maybe just use Z.t everywhere (it's shorter)
type addr = naturalmachine address
val pp_addr : natural -> stringPrint the time this function call took. The string is just for the printed message
val esc : Types.ppmode -> string -> stringAnalyseinclude Basemodule Base : sig ... endmodule Collected : sig ... endcollect the various test analysis data
module DwarfLineInfo : sig ... endpost-process DWARF source line info
module Elf : sig ... endmodule ElfTypes : sig ... endtype of collected ELF-file data from linksem
module Globals : sig ... endmodule Pp : sig ... endmodule Types : sig ... endtypes shared between analyse* and the read-dwarf top-level
module Utils : sig ... endMiscellaneous types and utility functions used throughout the analyse code
Analyseinclude module type of struct include Base endmodule Base : sig ... endmodule CallGraph : sig ... endcompute call-graph
module Collected : sig ... endcollect the various test analysis data
module DwarfLineInfo : sig ... endpost-process DWARF source line info
module Elf : sig ... endpp symbol map
module ElfTypes : sig ... endtype of collected ELF-file data from linksem
module Globals : sig ... endmodule Pp : sig ... endrender collected analysis data to text or css
module Types : sig ... endtypes shared between analyse* and the read-dwarf top-level
module Utils : sig ... endMiscellaneous types and utility functions used throughout the analyse code
ArchThis module adds some code that is related to the Architecture specific modules but is in itself architecture independent.
include Sigtype 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 arrayThe map of dwarf register: Which register number map to which ISA register
val supports : Config.Arch.t -> boolTells if this Arch module supports this architecture
val init : Config.Arch.t -> unitIf this arch module supports the architecture, then initialize read-dwarf state using this architecture
val initialized : unit -> Config.Arch.t optionReturn Some(arch) is the loaded arch is arch and None if nothing is loaded yet.
val module_name : stringThe name of the arch module. Must be the name of the module i.e. Config.arch_module
val loaded_name : stringFor dynamic arch module, the name of the dynamically loaded module. Otherwise module_name
val dwarf_reg_map : unit -> dwarf_reg_mapGet the register map of the architecture
val is_local : State.Reg.t -> boolTell if a register is local for the ABI
val nop : unit -> Utils.BytesSeq.tGive the opcode of the nop instruction (For Sail/Isla initialisation
val pc : unit -> State.Reg.tGive the register index for the program counter
val sp : unit -> State.Reg.tGive the register index for the stack pointer
val assemble_to_elf : string -> stringTake 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 listSplit a byte-sequence into a list of instructions.
val is_ret : Utils.BytesSeq.t -> boolTell if an instruction is a return instruction.
val is_cmp : Utils.BytesSeq.t -> (State.Reg.t * Utils.BitVec.t) optionTell 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 optionTell 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 -> unitEnsure that the right architecture type is loaded
val pp_api : func_api -> Utils.Pp.documentval get : unit -> Config.Arch.tGet the initialized architecture type. Fails (Failure) if not architecture was loaded
val get_config : unit -> Config.File.ArchConf.tGet the configuration for the initialized architecture
val get_isla_config : unit -> Config.File.ArchConf.Isla.tGet the Isla configuration for the initialized architecture
val load_elf_arch : Elf.File.t -> unitLoad the architecture of this File
ArchThis 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 endDescribe 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 arrayThe map of dwarf register: Which register number map to which ISA register
val supports : Config.Arch.t -> boolTells if this Arch module supports this architecture
val init : Config.Arch.t -> unitIf this arch module supports the architecture, then initialize read-dwarf state using this architecture
val initialized : unit -> Config.Arch.t optionReturn 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_mapGet the register map of the architecture
val is_local : State.Reg.t -> boolTell if a register is local for the ABI
val nop : unit -> Utils.BytesSeq.tGive the opcode of the nop instruction (For Sail/Isla initialisation
val pc : unit -> State.Reg.tGive the register index for the program counter
val sp : unit -> State.Reg.tGive 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 listSplit a byte-sequence into a list of instructions.
val is_ret : Utils.BytesSeq.t -> boolTell if an instruction is a return instruction.
val is_cmp : Utils.BytesSeq.t -> (State.Reg.t * Utils.BitVec.t) optionTell 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 optionTell 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 -> unitEnsure that the right architecture type is loaded
val pp_api : func_api -> Utils.Pp.documentval get : unit -> Config.Arch.tGet the initialized architecture type. Fails (Failure) if not architecture was loaded
val get_config : unit -> Config.File.ArchConf.tGet the configuration for the initialized architecture
val get_isla_config : unit -> Config.File.ArchConf.Isla.tGet the Isla configuration for the initialized architecture
val load_elf_arch : Elf.File.t -> unitLoad 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.Sizeinclude AstGen.Def.Sizetype 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 -> tCreate a size value from a valid size in byte
val of_bits : int -> tCreate a size value from a valid size in bits
val to_bytes : t -> intGet the byte size corresponding to that value
val to_bits : t -> intGet the bits size corresponding to that value
val equal : 'a -> 'a -> boolval pp_bytes : t -> Utils.Pp.documentPretty-print a size as just the byte number
val pp_bits : t -> PPrintEngine.documentPretty print a size at "16bits" for example
Base.Sizeinclude module type of struct include AstGen.Def.Size endThe possible sizes for memory accesses.
val of_bytes : int -> tCreate a size value from a valid size in byte
val of_bits : int -> tCreate a size value from a valid size in bits
val to_bytes : t -> intGet the byte size corresponding to that value
val to_bits : t -> intGet the bits size corresponding to that value
val pp_bytes : t -> Utils.Pp.documentPretty-print a size as just the byte number
val pp_bits : t -> Utils.Pp.documentPretty print a size at "16bits" for example
Ast.BaseThe main module to use the AST of expression and SMT operation for a more generic overview of the AST, see SymbolicExpressions.
include AstGen.Definclude AstGen.Otttype flag = stringtype enum = int * inttype '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.lrngThe 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.SizeSize
module Size : sig ... endParsing
module Parser = AstGen.Parsermodule Lexer = AstGen.Lexerexception ParseError of loc * stringException that represent an Isla parsing error
exception LexError of loc * stringException that represent an Isla lexing error
type lexbuf = Stdlib.Lexing.lexbuftype lexer = lexbuf -> Parser.tokentype 'a parser = lexer -> lexbuf -> 'a
val parse : 'a parser -> ('b -> lexbuf) -> ?filename:string -> 'b -> 'aParse a single Isla instruction output from a Lexing.lexbuf
val from_string : string -> Stdlib.Lexing.lexbufval from_channel : Stdlib.in_channel -> Stdlib.Lexing.lexbufval parse_exp_string : ?filename:string -> string -> (AstGen.Def.lrng, string, string, AstGen.Def.Size.t) AstGen.Ott.expParse 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.expParse a single Isla expression from a channel
val parse_smt_ans_string : ?filename:string -> string -> AstGen.Def.rsmt_ansParse a single Isla expression from a string
val parse_smt_ans_channel : ?filename:string -> Stdlib.in_channel -> AstGen.Def.rsmt_ansParse a single Isla expression from a channel
include AstGen.Parser_pp
val pp_raw_bvar : string -> PPrintEngine.documentval pp_raw_bvf : Utils.BitVec.t -> PPrintEngine.documentval pp_raw_flag : string -> PPrintEngine.documentval pp_raw_vvar : int -> PPrintEngine.documentval pp_raw_name : string -> PPrintEngine.documentval pp_raw_enum_ty : int -> PPrintEngine.documentval pp_raw_enum : AstGen.Ott.enum -> PPrintEngine.documentval pp_raw_int : int -> PPrintEngine.documentval pp_raw_bvi : int -> PPrintEngine.documentval pp_raw_bv : string -> PPrintEngine.documentval pp_raw_str : string -> PPrintEngine.documentval pp_raw_mem_size : AstGen.Def.Size.t -> Utils.Pp.documentval pp_raw_binmem : AstGen.Def.Size.t AstGen.Ott.binmem -> PPrintEngine.documentval pp_raw_var : ('a -> PPrintEngine.document) -> 'a -> PPrintEngine.documentval pp_raw_bbvar : string -> PPrintEngine.documentval pp_raw_bind : ('a -> PPrintEngine.document) -> (string * ('b, 'a, string, AstGen.Def.Size.t) AstGen.Ott.exp) -> PPrintEngine.documentval pp_raw_exp : ('a -> PPrintEngine.document) -> ('b, 'a, string, AstGen.Def.Size.t) AstGen.Ott.exp -> PPrintEngine.documentval pp_raw_fbool : (string * bool) -> PPrintEngine.documentval pp_raw_smt : ('a -> PPrintEngine.document) -> ('b, 'a, string, AstGen.Def.Size.t) AstGen.Ott.smt -> PPrintEngine.documentval pp_raw_smts : ('a -> PPrintEngine.document) -> ('b, 'a, string, AstGen.Def.Size.t) AstGen.Ott.smt list -> PPrintEngine.documentval pp_raw_smt_ans : ('a -> PPrintEngine.document) -> ('b, 'a, string, AstGen.Def.Size.t) AstGen.Ott.smt_ans -> PPrintEngine.documentval pp_raw_ty : AstGen.Def.Size.t AstGen.Ott.ty -> PPrintEngine.documentval pp_raw_bool : bool -> PPrintEngine.documentval pp_raw_unop : AstGen.Ott.unop -> PPrintEngine.documentval pp_raw_bvarith : AstGen.Ott.bvarith -> PPrintEngine.documentval pp_raw_bvcomp : AstGen.Ott.bvcomp -> PPrintEngine.documentval pp_raw_binop : AstGen.Def.Size.t AstGen.Ott.binop -> PPrintEngine.documentval pp_raw_bvmanyarith : AstGen.Ott.bvmanyarith -> PPrintEngine.documentval pp_raw_manyop : AstGen.Ott.manyop -> PPrintEngine.documentval pp_bvar : string -> PPrintEngine.documentval pp_bvf : Utils.BitVec.t -> PPrintEngine.documentval pp_flag : string -> PPrintEngine.documentval pp_vvar : int -> PPrintEngine.documentval pp_name : string -> PPrintEngine.documentval pp_enum_ty : int -> PPrintEngine.documentval pp_enum : AstGen.Ott.enum -> PPrintEngine.documentval pp_int : int -> PPrintEngine.documentval pp_bvi : int -> PPrintEngine.documentval pp_bv : string -> PPrintEngine.documentval pp_str : string -> PPrintEngine.documentval pp_j : int -> stringval pp_mem_size : AstGen.Def.Size.t -> Utils.Pp.documentval pp_binmem : AstGen.Def.Size.t AstGen.Ott.binmem -> PPrintEngine.documentval pp_var : ('a -> PPrintEngine.document) -> 'a -> PPrintEngine.documentval pp_bbvar : string -> PPrintEngine.documentval pp_bind : ('a -> PPrintEngine.document) -> (string * ('b, 'a, string, AstGen.Def.Size.t) AstGen.Ott.exp) -> PPrintEngine.documentval pp_exp : ('a -> PPrintEngine.document) -> ('b, 'a, string, AstGen.Def.Size.t) AstGen.Ott.exp -> PPrintEngine.documentval pp_fbool : (string * bool) -> PPrintEngine.documentval pp_smt : ('a -> PPrintEngine.document) -> ('b, 'a, string, AstGen.Def.Size.t) AstGen.Ott.smt -> PPrintEngine.documentval pp_smts : ('a -> PPrintEngine.document) -> ('b, 'a, string, AstGen.Def.Size.t) AstGen.Ott.smt list -> PPrintEngine.documentval pp_smt_ans : ('a -> PPrintEngine.document) -> ('b, 'a, string, AstGen.Def.Size.t) AstGen.Ott.smt_ans -> PPrintEngine.documentval pp_ty : AstGen.Def.Size.t AstGen.Ott.ty -> PPrintEngine.documentval pp_bool : bool -> PPrintEngine.documentval pp_unop : AstGen.Ott.unop -> PPrintEngine.documentval pp_bvarith : AstGen.Ott.bvarith -> PPrintEngine.documentval pp_bvcomp : AstGen.Ott.bvcomp -> PPrintEngine.documentval pp_binop : AstGen.Def.Size.t AstGen.Ott.binop -> PPrintEngine.documentval pp_bvmanyarith : AstGen.Ott.bvmanyarith -> PPrintEngine.documentval 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.tval 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) expRaw expression coming out of the parser
type rty = AstGen.Def.Size.t tyRaw type coming out of the parser
type rsmt = (lrng, string, string, AstGen.Def.Size.t) smtRaw SMT command coming out of the parser
type rsmt_ans = (lrng, string, string, AstGen.Def.Size.t) smt_ansRaw SMT answer coming out of the parser
module Size : sig ... endmodule Parser = AstGen.Parsermodule Lexer = AstGen.Lexerexception ParseError of loc * stringException that represent an Isla parsing error
exception LexError of loc * stringException that represent an Isla lexing error
type lexer = lexbuf -> Parser.tokenParse 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.expParse 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.expParse a single Isla expression from a channel
val parse_smt_ans_string : ?filename:string -> string -> AstGen.Def.rsmt_ansParse a single Isla expression from a string
val parse_smt_ans_channel :
+ ?filename:string ->
+ Stdlib.in_channel ->
+ AstGen.Def.rsmt_ansParse a single Isla expression from a channel
include module type of struct include AstGen.Parser_pp endval pp_raw_bvf : Utils.BitVec.t -> PPrint.documentval pp_raw_enum : AstGen.Ott.enum -> PPrint.documentval pp_raw_mem_size : AstGen.Def.Size.t -> Utils.Pp.documentval pp_raw_binmem : AstGen.Def.Size.t AstGen.Ott.binmem -> PPrint.documentval pp_raw_bind :
+ ('a -> PPrint.document) ->
+ (string * ('b, 'a, string, AstGen.Def.Size.t) AstGen.Ott.exp) ->
+ PPrint.documentval pp_raw_exp :
+ ('a -> PPrint.document) ->
+ ('b, 'a, string, AstGen.Def.Size.t) AstGen.Ott.exp ->
+ PPrint.documentval pp_raw_smt :
+ ('a -> PPrint.document) ->
+ ('b, 'a, string, AstGen.Def.Size.t) AstGen.Ott.smt ->
+ PPrint.documentval pp_raw_smts :
+ ('a -> PPrint.document) ->
+ ('b, 'a, string, AstGen.Def.Size.t) AstGen.Ott.smt list ->
+ PPrint.documentval pp_raw_smt_ans :
+ ('a -> PPrint.document) ->
+ ('b, 'a, string, AstGen.Def.Size.t) AstGen.Ott.smt_ans ->
+ PPrint.documentval pp_raw_ty : AstGen.Def.Size.t AstGen.Ott.ty -> PPrint.documentval pp_raw_unop : AstGen.Ott.unop -> PPrint.documentval pp_raw_bvarith : AstGen.Ott.bvarith -> PPrint.documentval pp_raw_bvcomp : AstGen.Ott.bvcomp -> PPrint.documentval pp_raw_binop : AstGen.Def.Size.t AstGen.Ott.binop -> PPrint.documentval pp_raw_bvmanyarith : AstGen.Ott.bvmanyarith -> PPrint.documentval pp_raw_manyop : AstGen.Ott.manyop -> PPrint.documentval pp_bvf : Utils.BitVec.t -> PPrint.documentval pp_enum : AstGen.Ott.enum -> PPrint.documentval pp_mem_size : AstGen.Def.Size.t -> Utils.Pp.documentval pp_binmem : AstGen.Def.Size.t AstGen.Ott.binmem -> PPrint.documentval pp_bind :
+ ('a -> PPrint.document) ->
+ (string * ('b, 'a, string, AstGen.Def.Size.t) AstGen.Ott.exp) ->
+ PPrint.documentval pp_exp :
+ ('a -> PPrint.document) ->
+ ('b, 'a, string, AstGen.Def.Size.t) AstGen.Ott.exp ->
+ PPrint.documentval pp_smt :
+ ('a -> PPrint.document) ->
+ ('b, 'a, string, AstGen.Def.Size.t) AstGen.Ott.smt ->
+ PPrint.documentval pp_smts :
+ ('a -> PPrint.document) ->
+ ('b, 'a, string, AstGen.Def.Size.t) AstGen.Ott.smt list ->
+ PPrint.documentval pp_smt_ans :
+ ('a -> PPrint.document) ->
+ ('b, 'a, string, AstGen.Def.Size.t) AstGen.Ott.smt_ans ->
+ PPrint.documentval pp_ty : AstGen.Def.Size.t AstGen.Ott.ty -> PPrint.documentval pp_unop : AstGen.Ott.unop -> PPrint.documentval pp_bvarith : AstGen.Ott.bvarith -> PPrint.documentval pp_bvcomp : AstGen.Ott.bvcomp -> PPrint.documentval pp_binop : AstGen.Def.Size.t AstGen.Ott.binop -> PPrint.documentval pp_bvmanyarith : AstGen.Ott.bvmanyarith -> PPrint.documentval pp_manyop : AstGen.Ott.manyop -> PPrint.documentAll function that start with is_
val is_atomic : ('a, 'b, 'c, 'd) exp -> boolAparently 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.tval ty_expect_bv : 'a ty -> intAst.ManipThis 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 -> 'aGet 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.expval direct_exp_iter_exp : (('a, 'v, 'b, 'm) Base.exp -> unit) -> ('a, 'v, 'b, 'm) Base.exp -> unitval direct_exp_fold_left_exp : ('a -> ('b, 'c, 'd, 'e) Base.exp -> 'a) -> 'a -> ('b, 'c, 'd, 'e) Base.exp -> 'aval direct_exp_for_all_exp : (('a, 'b, 'c, 'd) Base.exp -> bool) -> ('a, 'b, 'c, 'd) Base.exp -> boolval direct_exp_exists_exp : (('a, 'b, 'c, 'd) Base.exp -> bool) -> ('a, 'b, 'c, 'd) Base.exp -> boolThis 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 -> unitIterate 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.expAllow 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.smtSame 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.expUnfold 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.expAllow memory operations in an expression.
val check_no_mem : ('a, 'v, 'b, 'm) Base.exp -> boolCheck 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.expExpect 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.ManipThis 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 -> 'aGet 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 -> unitIterate a function on all the variable of an expression
val exp_iter_annot : ('a -> unit) -> ('a, 'v, 'b, 'm) Base.exp -> unitIterate 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.expSubstitute 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.expUnfold 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 -> boolCheck 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.expExpect 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
Astinclude Baseinclude AstGen.Definclude AstGen.Otttype flag = stringtype enum = int * inttype '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.lrngThe 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.SizeSize
module Size = Base.SizeParsing
module Parser = AstGen.Parsermodule Lexer = AstGen.Lexerexception ParseError of loc * stringException that represent an Isla parsing error
exception LexError of loc * stringException that represent an Isla lexing error
type lexbuf = Stdlib.Lexing.lexbuftype lexer = lexbuf -> Parser.tokentype 'a parser = lexer -> lexbuf -> 'a
val parse : 'a parser -> ('b -> lexbuf) -> ?filename:string -> 'b -> 'aParse a single Isla instruction output from a Lexing.lexbuf
val from_string : string -> Stdlib.Lexing.lexbufval from_channel : Stdlib.in_channel -> Stdlib.Lexing.lexbufval parse_exp_string : ?filename:string -> string -> (AstGen.Def.lrng, string, string, AstGen.Def.Size.t) AstGen.Ott.expParse 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.expParse a single Isla expression from a channel
val parse_smt_ans_string : ?filename:string -> string -> AstGen.Def.rsmt_ansParse a single Isla expression from a string
val parse_smt_ans_channel : ?filename:string -> Stdlib.in_channel -> AstGen.Def.rsmt_ansParse a single Isla expression from a channel
include AstGen.Parser_pp
val pp_raw_bvar : string -> PPrintEngine.documentval pp_raw_bvf : Utils.BitVec.t -> PPrintEngine.documentval pp_raw_flag : string -> PPrintEngine.documentval pp_raw_vvar : int -> PPrintEngine.documentval pp_raw_name : string -> PPrintEngine.documentval pp_raw_enum_ty : int -> PPrintEngine.documentval pp_raw_enum : AstGen.Ott.enum -> PPrintEngine.documentval pp_raw_int : int -> PPrintEngine.documentval pp_raw_bvi : int -> PPrintEngine.documentval pp_raw_bv : string -> PPrintEngine.documentval pp_raw_str : string -> PPrintEngine.documentval pp_raw_mem_size : AstGen.Def.Size.t -> Utils.Pp.documentval pp_raw_binmem : AstGen.Def.Size.t AstGen.Ott.binmem -> PPrintEngine.documentval pp_raw_var : ('a -> PPrintEngine.document) -> 'a -> PPrintEngine.documentval pp_raw_bbvar : string -> PPrintEngine.documentval pp_raw_bind : ('a -> PPrintEngine.document) -> (string * ('b, 'a, string, AstGen.Def.Size.t) AstGen.Ott.exp) -> PPrintEngine.documentval pp_raw_exp : ('a -> PPrintEngine.document) -> ('b, 'a, string, AstGen.Def.Size.t) AstGen.Ott.exp -> PPrintEngine.documentval pp_raw_fbool : (string * bool) -> PPrintEngine.documentval pp_raw_smt : ('a -> PPrintEngine.document) -> ('b, 'a, string, AstGen.Def.Size.t) AstGen.Ott.smt -> PPrintEngine.documentval pp_raw_smts : ('a -> PPrintEngine.document) -> ('b, 'a, string, AstGen.Def.Size.t) AstGen.Ott.smt list -> PPrintEngine.documentval pp_raw_smt_ans : ('a -> PPrintEngine.document) -> ('b, 'a, string, AstGen.Def.Size.t) AstGen.Ott.smt_ans -> PPrintEngine.documentval pp_raw_ty : AstGen.Def.Size.t AstGen.Ott.ty -> PPrintEngine.documentval pp_raw_bool : bool -> PPrintEngine.documentval pp_raw_unop : AstGen.Ott.unop -> PPrintEngine.documentval pp_raw_bvarith : AstGen.Ott.bvarith -> PPrintEngine.documentval pp_raw_bvcomp : AstGen.Ott.bvcomp -> PPrintEngine.documentval pp_raw_binop : AstGen.Def.Size.t AstGen.Ott.binop -> PPrintEngine.documentval pp_raw_bvmanyarith : AstGen.Ott.bvmanyarith -> PPrintEngine.documentval pp_raw_manyop : AstGen.Ott.manyop -> PPrintEngine.documentval pp_bvar : string -> PPrintEngine.documentval pp_bvf : Utils.BitVec.t -> PPrintEngine.documentval pp_flag : string -> PPrintEngine.documentval pp_vvar : int -> PPrintEngine.documentval pp_name : string -> PPrintEngine.documentval pp_enum_ty : int -> PPrintEngine.documentval pp_enum : AstGen.Ott.enum -> PPrintEngine.documentval pp_int : int -> PPrintEngine.documentval pp_bvi : int -> PPrintEngine.documentval pp_bv : string -> PPrintEngine.documentval pp_str : string -> PPrintEngine.documentval pp_j : int -> stringval pp_mem_size : AstGen.Def.Size.t -> Utils.Pp.documentval pp_binmem : AstGen.Def.Size.t AstGen.Ott.binmem -> PPrintEngine.documentval pp_var : ('a -> PPrintEngine.document) -> 'a -> PPrintEngine.documentval pp_bbvar : string -> PPrintEngine.documentval pp_bind : ('a -> PPrintEngine.document) -> (string * ('b, 'a, string, AstGen.Def.Size.t) AstGen.Ott.exp) -> PPrintEngine.documentval pp_exp : ('a -> PPrintEngine.document) -> ('b, 'a, string, AstGen.Def.Size.t) AstGen.Ott.exp -> PPrintEngine.documentval pp_fbool : (string * bool) -> PPrintEngine.documentval pp_smt : ('a -> PPrintEngine.document) -> ('b, 'a, string, AstGen.Def.Size.t) AstGen.Ott.smt -> PPrintEngine.documentval pp_smts : ('a -> PPrintEngine.document) -> ('b, 'a, string, AstGen.Def.Size.t) AstGen.Ott.smt list -> PPrintEngine.documentval pp_smt_ans : ('a -> PPrintEngine.document) -> ('b, 'a, string, AstGen.Def.Size.t) AstGen.Ott.smt_ans -> PPrintEngine.documentval pp_ty : AstGen.Def.Size.t AstGen.Ott.ty -> PPrintEngine.documentval pp_bool : bool -> PPrintEngine.documentval pp_unop : AstGen.Ott.unop -> PPrintEngine.documentval pp_bvarith : AstGen.Ott.bvarith -> PPrintEngine.documentval pp_bvcomp : AstGen.Ott.bvcomp -> PPrintEngine.documentval pp_binop : AstGen.Def.Size.t AstGen.Ott.binop -> PPrintEngine.documentval pp_bvmanyarith : AstGen.Ott.bvmanyarith -> PPrintEngine.documentval 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.tval ty_expect_bv : 'a ty -> int
Construtors
Comparisons
module Base : sig ... endThe 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) expRaw expression coming out of the parser
type rty = Base.Size.t tyRaw type coming out of the parser
type rsmt = (lrng, string, string, Base.Size.t) smtRaw SMT command coming out of the parser
type rsmt_ans = (lrng, string, string, Base.Size.t) smt_ansRaw SMT answer coming out of the parser
module Size = Base.Sizemodule Parser = AstGen.Parsermodule Lexer = AstGen.Lexerexception ParseError of loc * stringException that represent an Isla parsing error
exception LexError of loc * stringException that represent an Isla lexing error
type lexer = lexbuf -> Parser.tokenParse 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.expParse 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.expParse a single Isla expression from a channel
val parse_smt_ans_string : ?filename:string -> string -> AstGen.Def.rsmt_ansParse a single Isla expression from a string
val parse_smt_ans_channel :
+ ?filename:string ->
+ Stdlib.in_channel ->
+ AstGen.Def.rsmt_ansParse a single Isla expression from a channel
include module type of struct include AstGen.Parser_pp endval pp_raw_bvf : Utils.BitVec.t -> PPrint.documentval pp_raw_enum : AstGen.Ott.enum -> PPrint.documentval pp_raw_mem_size : AstGen.Def.Size.t -> Utils.Pp.documentval pp_raw_binmem : AstGen.Def.Size.t AstGen.Ott.binmem -> PPrint.documentval pp_raw_bind :
+ ('a -> PPrint.document) ->
+ (string * ('b, 'a, string, AstGen.Def.Size.t) AstGen.Ott.exp) ->
+ PPrint.documentval pp_raw_exp :
+ ('a -> PPrint.document) ->
+ ('b, 'a, string, AstGen.Def.Size.t) AstGen.Ott.exp ->
+ PPrint.documentval pp_raw_smt :
+ ('a -> PPrint.document) ->
+ ('b, 'a, string, AstGen.Def.Size.t) AstGen.Ott.smt ->
+ PPrint.documentval pp_raw_smts :
+ ('a -> PPrint.document) ->
+ ('b, 'a, string, AstGen.Def.Size.t) AstGen.Ott.smt list ->
+ PPrint.documentval pp_raw_smt_ans :
+ ('a -> PPrint.document) ->
+ ('b, 'a, string, AstGen.Def.Size.t) AstGen.Ott.smt_ans ->
+ PPrint.documentval pp_raw_ty : AstGen.Def.Size.t AstGen.Ott.ty -> PPrint.documentval pp_raw_unop : AstGen.Ott.unop -> PPrint.documentval pp_raw_bvarith : AstGen.Ott.bvarith -> PPrint.documentval pp_raw_bvcomp : AstGen.Ott.bvcomp -> PPrint.documentval pp_raw_binop : AstGen.Def.Size.t AstGen.Ott.binop -> PPrint.documentval pp_raw_bvmanyarith : AstGen.Ott.bvmanyarith -> PPrint.documentval pp_raw_manyop : AstGen.Ott.manyop -> PPrint.documentval pp_bvf : Utils.BitVec.t -> PPrint.documentval pp_enum : AstGen.Ott.enum -> PPrint.documentval pp_mem_size : AstGen.Def.Size.t -> Utils.Pp.documentval pp_binmem : AstGen.Def.Size.t AstGen.Ott.binmem -> PPrint.documentval pp_bind :
+ ('a -> PPrint.document) ->
+ (string * ('b, 'a, string, AstGen.Def.Size.t) AstGen.Ott.exp) ->
+ PPrint.documentval pp_exp :
+ ('a -> PPrint.document) ->
+ ('b, 'a, string, AstGen.Def.Size.t) AstGen.Ott.exp ->
+ PPrint.documentval pp_smt :
+ ('a -> PPrint.document) ->
+ ('b, 'a, string, AstGen.Def.Size.t) AstGen.Ott.smt ->
+ PPrint.documentval pp_smts :
+ ('a -> PPrint.document) ->
+ ('b, 'a, string, AstGen.Def.Size.t) AstGen.Ott.smt list ->
+ PPrint.documentval pp_smt_ans :
+ ('a -> PPrint.document) ->
+ ('b, 'a, string, AstGen.Def.Size.t) AstGen.Ott.smt_ans ->
+ PPrint.documentval pp_ty : AstGen.Def.Size.t AstGen.Ott.ty -> PPrint.documentval pp_unop : AstGen.Ott.unop -> PPrint.documentval pp_bvarith : AstGen.Ott.bvarith -> PPrint.documentval pp_bvcomp : AstGen.Ott.bvcomp -> PPrint.documentval pp_binop : AstGen.Def.Size.t AstGen.Ott.binop -> PPrint.documentval pp_bvmanyarith : AstGen.Ott.bvmanyarith -> PPrint.documentval pp_manyop : AstGen.Ott.manyop -> PPrint.documentAll function that start with is_
val is_atomic : ('a, 'b, 'c, 'd) exp -> boolAparently 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.tval ty_expect_bv : 'a ty -> intmodule Base : sig ... endThe main module to use the AST of expression and SMT operation for a more generic overview of the AST, see SymbolicExpressions.
Def.Sizetype 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 -> tCreate a size value from a valid size in byte
val of_bits : int -> tCreate a size value from a valid size in bits
val to_bytes : t -> intGet the byte size corresponding to that value
val to_bits : t -> intGet the bits size corresponding to that value
val equal : 'a -> 'a -> boolval pp_bytes : t -> Utils.Pp.documentPretty-print a size as just the byte number
val pp_bits : t -> PPrintEngine.documentPretty print a size at "16bits" for example
Def.Sizeval of_bytes : int -> tCreate a size value from a valid size in byte
val of_bits : int -> tCreate a size value from a valid size in bits
val to_bytes : t -> intGet the byte size corresponding to that value
val to_bits : t -> intGet the bits size corresponding to that value
val pp_bytes : t -> Utils.Pp.documentPretty-print a size as just the byte number
val pp_bits : t -> Utils.Pp.documentPretty print a size at "16bits" for example
AstGen.Definclude Otttype flag = stringtype enum = int * inttype '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.lrngThe 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 ... endAstGen.Lexerval __ocaml_lex_tables : Stdlib.Lexing.lex_tablesval token : Stdlib.Lexing.lexbuf -> Parser.tokenval __ocaml_lex_token_rec : Stdlib.Lexing.lexbuf -> int -> Parser.tokenAstGen.Lexerval token : Stdlib.Lexing.lexbuf -> Parser.tokenval __ocaml_lex_token_rec : Stdlib.Lexing.lexbuf -> int -> Parser.tokenAstGen.Otttype flag = stringtype enum = int * inttype '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.Ottwarning: 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
+ * 'atype ('a, 'v, 'b, 'm) smt_ans = | Error of string| Version of string| Sat| Unsat| Unknown| Unsupported| Exp of ('a, 'v, 'b, 'm) expauxiliary functions on the new list types
library functions
subrules
auxiliary functions
free variables
substitutions
context application
definitions
AstGen.Parsertype 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 listval smt_start : (Stdlib.Lexing.lexbuf -> token) -> Stdlib.Lexing.lexbuf -> Def.rsmtval smt_ans_start : (Stdlib.Lexing.lexbuf -> token) -> Stdlib.Lexing.lexbuf -> Def.rsmt_ansval exp_start : (Stdlib.Lexing.lexbuf -> token) -> Stdlib.Lexing.lexbuf -> (Def.lrng, string, string, Def.Size.t) Ott.expAstGen.Parsertype 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| ANDval smt_ans_start :
+ (Stdlib.Lexing.lexbuf -> token) ->
+ Stdlib.Lexing.lexbuf ->
+ Def.rsmt_ansval exp_start :
+ (Stdlib.Lexing.lexbuf -> token) ->
+ Stdlib.Lexing.lexbuf ->
+ (Def.lrng, string, string, Def.Size.t) Ott.expAstGen.Parser_ppval pp_raw_bvar : string -> PPrintEngine.documentval pp_raw_bvf : Utils.BitVec.t -> PPrintEngine.documentval pp_raw_flag : string -> PPrintEngine.documentval pp_raw_vvar : int -> PPrintEngine.documentval pp_raw_name : string -> PPrintEngine.documentval pp_raw_enum_ty : int -> PPrintEngine.documentval pp_raw_enum : Ott.enum -> PPrintEngine.documentval pp_raw_int : int -> PPrintEngine.documentval pp_raw_bvi : int -> PPrintEngine.documentval pp_raw_bv : string -> PPrintEngine.documentval pp_raw_str : string -> PPrintEngine.documentval pp_raw_mem_size : Def.Size.t -> Utils.Pp.documentval pp_raw_binmem : Def.Size.t Ott.binmem -> PPrintEngine.documentval pp_raw_var : ('a -> PPrintEngine.document) -> 'a -> PPrintEngine.documentval pp_raw_bbvar : string -> PPrintEngine.documentval pp_raw_bind : ('a -> PPrintEngine.document) -> (string * ('b, 'a, string, Def.Size.t) Ott.exp) -> PPrintEngine.documentval pp_raw_exp : ('a -> PPrintEngine.document) -> ('b, 'a, string, Def.Size.t) Ott.exp -> PPrintEngine.documentval pp_raw_fbool : (string * bool) -> PPrintEngine.documentval pp_raw_smt : ('a -> PPrintEngine.document) -> ('b, 'a, string, Def.Size.t) Ott.smt -> PPrintEngine.documentval pp_raw_smts : ('a -> PPrintEngine.document) -> ('b, 'a, string, Def.Size.t) Ott.smt list -> PPrintEngine.documentval pp_raw_smt_ans : ('a -> PPrintEngine.document) -> ('b, 'a, string, Def.Size.t) Ott.smt_ans -> PPrintEngine.documentval pp_raw_ty : Def.Size.t Ott.ty -> PPrintEngine.documentval pp_raw_bool : bool -> PPrintEngine.documentval pp_raw_unop : Ott.unop -> PPrintEngine.documentval pp_raw_bvarith : Ott.bvarith -> PPrintEngine.documentval pp_raw_bvcomp : Ott.bvcomp -> PPrintEngine.documentval pp_raw_binop : Def.Size.t Ott.binop -> PPrintEngine.documentval pp_raw_bvmanyarith : Ott.bvmanyarith -> PPrintEngine.documentval pp_raw_manyop : Ott.manyop -> PPrintEngine.documentval pp_bvar : string -> PPrintEngine.documentval pp_bvf : Utils.BitVec.t -> PPrintEngine.documentval pp_flag : string -> PPrintEngine.documentval pp_vvar : int -> PPrintEngine.documentval pp_name : string -> PPrintEngine.documentval pp_enum_ty : int -> PPrintEngine.documentval pp_enum : Ott.enum -> PPrintEngine.documentval pp_int : int -> PPrintEngine.documentval pp_bvi : int -> PPrintEngine.documentval pp_bv : string -> PPrintEngine.documentval pp_str : string -> PPrintEngine.documentval pp_j : int -> stringval pp_mem_size : Def.Size.t -> Utils.Pp.documentval pp_binmem : Def.Size.t Ott.binmem -> PPrintEngine.documentval pp_var : ('a -> PPrintEngine.document) -> 'a -> PPrintEngine.documentval pp_bbvar : string -> PPrintEngine.documentval pp_bind : ('a -> PPrintEngine.document) -> (string * ('b, 'a, string, Def.Size.t) Ott.exp) -> PPrintEngine.documentval pp_exp : ('a -> PPrintEngine.document) -> ('b, 'a, string, Def.Size.t) Ott.exp -> PPrintEngine.documentval pp_fbool : (string * bool) -> PPrintEngine.documentval pp_smt : ('a -> PPrintEngine.document) -> ('b, 'a, string, Def.Size.t) Ott.smt -> PPrintEngine.documentval pp_smts : ('a -> PPrintEngine.document) -> ('b, 'a, string, Def.Size.t) Ott.smt list -> PPrintEngine.documentval pp_smt_ans : ('a -> PPrintEngine.document) -> ('b, 'a, string, Def.Size.t) Ott.smt_ans -> PPrintEngine.documentval pp_ty : Def.Size.t Ott.ty -> PPrintEngine.documentval pp_bool : bool -> PPrintEngine.documentval pp_unop : Ott.unop -> PPrintEngine.documentval pp_bvarith : Ott.bvarith -> PPrintEngine.documentval pp_bvcomp : Ott.bvcomp -> PPrintEngine.documentval pp_binop : Def.Size.t Ott.binop -> PPrintEngine.documentval pp_bvmanyarith : Ott.bvmanyarith -> PPrintEngine.documentval pp_manyop : Ott.manyop -> PPrintEngine.documentAstGen.Parser_ppval pp_raw_bvf : Utils.BitVec.t -> PPrint.documentval pp_raw_enum : Ott.enum -> PPrint.documentval pp_raw_mem_size : Def.Size.t -> Utils.Pp.documentval pp_raw_binmem : Def.Size.t Ott.binmem -> PPrint.documentval pp_raw_bind :
+ ('a -> PPrint.document) ->
+ (string * ('b, 'a, string, Def.Size.t) Ott.exp) ->
+ PPrint.documentval pp_raw_exp :
+ ('a -> PPrint.document) ->
+ ('b, 'a, string, Def.Size.t) Ott.exp ->
+ PPrint.documentval pp_raw_smt :
+ ('a -> PPrint.document) ->
+ ('b, 'a, string, Def.Size.t) Ott.smt ->
+ PPrint.documentval pp_raw_smts :
+ ('a -> PPrint.document) ->
+ ('b, 'a, string, Def.Size.t) Ott.smt list ->
+ PPrint.documentval pp_raw_smt_ans :
+ ('a -> PPrint.document) ->
+ ('b, 'a, string, Def.Size.t) Ott.smt_ans ->
+ PPrint.documentval pp_raw_ty : Def.Size.t Ott.ty -> PPrint.documentval pp_raw_unop : Ott.unop -> PPrint.documentval pp_raw_bvarith : Ott.bvarith -> PPrint.documentval pp_raw_bvcomp : Ott.bvcomp -> PPrint.documentval pp_raw_binop : Def.Size.t Ott.binop -> PPrint.documentval pp_raw_bvmanyarith : Ott.bvmanyarith -> PPrint.documentval pp_raw_manyop : Ott.manyop -> PPrint.documentval pp_bvf : Utils.BitVec.t -> PPrint.documentval pp_enum : Ott.enum -> PPrint.documentval pp_mem_size : Def.Size.t -> Utils.Pp.documentval pp_binmem : Def.Size.t Ott.binmem -> PPrint.documentval pp_bind :
+ ('a -> PPrint.document) ->
+ (string * ('b, 'a, string, Def.Size.t) Ott.exp) ->
+ PPrint.documentval pp_exp :
+ ('a -> PPrint.document) ->
+ ('b, 'a, string, Def.Size.t) Ott.exp ->
+ PPrint.documentval pp_smt :
+ ('a -> PPrint.document) ->
+ ('b, 'a, string, Def.Size.t) Ott.smt ->
+ PPrint.documentval pp_smts :
+ ('a -> PPrint.document) ->
+ ('b, 'a, string, Def.Size.t) Ott.smt list ->
+ PPrint.documentval pp_smt_ans :
+ ('a -> PPrint.document) ->
+ ('b, 'a, string, Def.Size.t) Ott.smt_ans ->
+ PPrint.documentval pp_ty : Def.Size.t Ott.ty -> PPrint.documentval pp_unop : Ott.unop -> PPrint.documentval pp_bvarith : Ott.bvarith -> PPrint.documentval pp_bvcomp : Ott.bvcomp -> PPrint.documentval pp_binop : Def.Size.t Ott.binop -> PPrint.documentval pp_bvmanyarith : Ott.bvmanyarith -> PPrint.documentval pp_manyop : Ott.manyop -> PPrint.documentAstGenmodule Def : sig ... endmodule Lexer : sig ... endmodule Ott : sig ... endmodule Parser : sig ... endmodule Parser_pp : sig ... endAstGenmodule Def : sig ... endmodule Lexer : sig ... endmodule Ott : sig ... endwarning: the backend selected ignores the file structure informations
module Parser : sig ... endmodule Parser_pp : sig ... endThis 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.ArchThis module provides an enumeration of architecture for internal identification
Config.ArchThis module provides an enumeration of architecture for internal identification
val to_string : t -> stringval of_string : string -> tval pp : t -> Utils.Pp.documentval size : t -> intval fmt : Stdlib.Format.formatter -> t -> unitval conv : t Cmdliner.Arg.convConfig.CommonOptThis 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.CommonOptThis 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.tval arch : Arch.t Cmdliner.Term.tThe list of common options. Almost all sub-commands should use this.
ArchConf.IslaThis 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.IslaThis 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 -> stringProduces a digest of the Isla configuration for invalidating the Isla.Cache when some configuration parameter changes
File.ArchConfmodule Isla : sig ... endThis module provides the current isla configuration.
File.ArchConfmodule Isla : sig ... endThis 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.Z3File.Z3Config.FileThis 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 ... endmodule Z3 : sig ... endtype archs_type = (Arch.t, ArchConf.t) Stdlib.Hashtbl.tEach 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 UnloadedConfigThis exception will be raised if any of the accessor is called while the config is not loaded.
val get_config : unit -> tGet all the configuration information
val get_arch_name : unit -> Arch.tGet 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.tGet 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.tGet 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.tGet the Z3 configuration
Config.FileThis 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 ... endmodule Z3 : sig ... endtype archs_type = (Arch.t, ArchConf.t) Stdlib.Hashtbl.tEach 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 -> tGet all the configuration information
val get_arch_name : unit -> Arch.tGet 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.tGet 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.tGet 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.tGet the Z3 configuration
Configmodule type S = sig ... endinclude Smodule Arch : sig ... endThis module provides an enumeration of architecture for internal identification
module File : sig ... endThis module is to handle the configuration file of the program.
module CommonOpt : sig ... endThis module provide support for common command line option to be used across multiple subcomands.
Configmodule type S = sig ... endThis 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 Smodule Arch : sig ... endThis module provides an enumeration of architecture for internal identification
module File : sig ... endThis module is to handle the configuration file of the program.
module CommonOpt : sig ... endThis module provide support for common command line option to be used across multiple subcomands.
Config.SConfig.SThis 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.FieldMapA range map over field to represent a structure layout
type obj = fieldThe type of the contained object
type obj_off = obj * intThe type of an object with an offset
type tThe type of the map from address ranges to obj
val empty : tAn empty RngMap
val is_in : objaddr:int -> obj -> int -> boolTest if an address is inside the object at address objaddr
val at : t -> int -> objGet the object containing the address. Throw Not_found if no object contains the address
val at_opt : t -> int -> obj optionGet the object containing the address. None if no object contains the address
val at_off : t -> int -> obj_offGet 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 optionGet 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 -> tUpdate the binding containing the provided address. If no binding contained the address, this is a no-op
val iteri : (int -> obj -> unit) -> t -> unitIter a function over all the objects with their address
val clear : t -> pos:int -> len:int -> tClear 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) -> tClear 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 -> tSame 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 -> tAdd an object at a specific address. The whole range of addresses covered by the object must be free
val addp : t -> obj_off -> tAdd 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.tReturn 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.FieldMapA range map over field to represent a structure layout
type obj = fieldThe type of the contained object
type obj_off = obj * intThe type of an object with an offset
The type of the map from address ranges to obj
val empty : tAn empty RngMap
val is_in : objaddr:int -> obj -> int -> boolTest 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.tReturn 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
CtypeThis 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.documenttype 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_membermodule FieldMap : Utils.RngMap.S with type obj = fieldA 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 * intThe identifier for a linksem_cupdie. See ids_of_cupdie
type linksem_env = linksem_t listThe 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.tAn 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 -> boolval is_array : t -> boolval is_ptr : t -> boolval is_scalar : t -> boolval is_composite : t -> boolval is_constexpr : t -> boolval ptr : t -> unqualifiedMake a simple pointer from a type
val voidstar : unqualifiedA void* pointer
val qual : ?const:bool -> ?volatile:bool -> ?restrict:bool -> ?constexpr:bool -> unqualified -> tCreate a qualified type from an unqualified type with the specified qualifiers
val add_qual : ?const:bool -> ?volatile:bool -> ?restrict:bool -> ?constexpr:bool -> t -> tupdate specified qualifiers. Other qualifier are kept
val machine : ?constexpr:bool -> int -> tCreate a machine type of that size without qualifiers
val of_frag : ?provenance:provenance -> ?offset:int -> ?constexpr:bool -> ?restrict:bool -> fragment -> tCreate a pointer to fragment with specified offset (0 by default)
val of_frag_somewhere : ?provenance:provenance -> ?constexpr:bool -> ?restrict:bool -> fragment -> tCreate a pointer to fragment Somewhere
val incomplete_struct : string -> int -> strucBuild an incomplete struct with a linking name and a size
val make_env : linksem_indexed_env -> envMakes 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 -> intGive the size of an unqualified type. Need the environement.
val sizeof : t -> intGive the size of an type. Need the environement.
val len : t -> intFor 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_idGet the id of a linksem cupdie
val pp_decl : Dwarf.decl -> PPrintEngine.documentPretty print the dwarf decl type
TODO: Move that in the appropriate place
exception LinkErrorThis 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 -> 'aval base_type_of_linksem : ?size:Z.t -> encoding:Z.t -> string -> unqualifiedConvert 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.objval field_map_of_linksem : string -> cc:conversion_context -> linksem_field list -> FieldMap.tval struc_of_linksem : cc:conversion_context -> string -> int -> linksem_field list -> strucval struct_type_of_linksem : ?force_complete:bool -> cc:conversion_context -> cupdie:Dwarf.cupdie -> mname:string option -> decl:Dwarf.decl -> unqualifiedBuild 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 -> enumval enum_type_of_linksem : cc:conversion_context -> cupdie:Dwarf.cupdie -> mname:string option -> decl:Dwarf.decl -> unqualifiedval unqualified_of_linksem : ?force_complete:bool -> cc:conversion_context -> linksem_t -> unqualifiedConvert an unqualified type. Union are just Machine n where n is their size
val of_linksem_none : unit -> tPlaceholder 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 -> tThe 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 -> tThe 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 -> envThe 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.documentval pp : t -> Utils.Pp.documentPretty 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.documentval pp_fragment : fragment -> Utils.Pp.documentval pp_offset : offset -> Utils.Pp.documentval pp_arr : t -> int option list -> Utils.Pp.documentval pp_arr_dim : int option -> Utils.Pp.documentval pp_field : field -> PPrintEngine.documentval pp_struct : struc -> Utils.Pp.documentval pp_enums : enum -> Utils.Pp.documentval pp_env : env -> Utils.Pp.documentPrint 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.tSame as type_at but for structs
val type_at : env:env -> size:int -> t -> int -> t Utils.Option.tGet the type of size size at the provided offset in another type
CtypeThis 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.documenttype unqualified = | Machine of intSize 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 {}| FuncPtrHack to accommodate PKVM
*)| MissingHack to accommodate PKVM
*)| BitsHack 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 = | UnknownUnknown type, But without possibility of learning
*)| Single of tSingle object: Only when accessing of a global variable
*)| DynArray of tGeneric C pointer, may point to multiple element of that type
*)| DynFragment of intWritable fragment for memory whose type is changing dynamically
*)| Global of stringThe 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_membermodule FieldMap : Utils.RngMap.S with type obj = fieldA 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 listThe 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 -> boolval is_array : t -> boolval is_ptr : t -> boolval is_scalar : t -> boolval is_composite : t -> boolval is_constexpr : t -> boolval ptr : t -> unqualifiedMake a simple pointer from a type
val voidstar : unqualifiedA void* pointer
val qual :
+ ?const:bool ->
+ ?volatile:bool ->
+ ?restrict:bool ->
+ ?constexpr:bool ->
+ unqualified ->
+ tCreate a qualified type from an unqualified type with the specified qualifiers
val add_qual :
+ ?const:bool ->
+ ?volatile:bool ->
+ ?restrict:bool ->
+ ?constexpr:bool ->
+ t ->
+ tupdate specified qualifiers. Other qualifier are kept
val machine : ?constexpr:bool -> int -> tCreate a machine type of that size without qualifiers
val of_frag :
+ ?provenance:provenance ->
+ ?offset:int ->
+ ?constexpr:bool ->
+ ?restrict:bool ->
+ fragment ->
+ tCreate a pointer to fragment with specified offset (0 by default)
val of_frag_somewhere :
+ ?provenance:provenance ->
+ ?constexpr:bool ->
+ ?restrict:bool ->
+ fragment ->
+ tCreate a pointer to fragment Somewhere
val incomplete_struct : string -> int -> strucBuild an incomplete struct with a linking name and a size
val make_env : linksem_indexed_env -> envMakes 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 -> intGive the size of an unqualified type. Need the environement.
val sizeof : t -> intGive the size of an type. Need the environement.
val len : t -> intFor 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_idGet the id of a linksem cupdie
val pp_decl : Dwarf.decl -> Utils.Pp.documentPretty 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 ->
+ unqualifiedConvert 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.objval field_map_of_linksem :
+ string ->
+ cc:conversion_context ->
+ linksem_field list ->
+ FieldMap.tval struc_of_linksem :
+ cc:conversion_context ->
+ string ->
+ int ->
+ linksem_field list ->
+ strucval struct_type_of_linksem :
+ ?force_complete:bool ->
+ cc:conversion_context ->
+ cupdie:Dwarf.cupdie ->
+ mname:string option ->
+ decl:Dwarf.decl ->
+ unqualifiedBuild 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 ->
+ enumval enum_type_of_linksem :
+ cc:conversion_context ->
+ cupdie:Dwarf.cupdie ->
+ mname:string option ->
+ decl:Dwarf.decl ->
+ unqualifiedval unqualified_of_linksem :
+ ?force_complete:bool ->
+ cc:conversion_context ->
+ linksem_t ->
+ unqualifiedConvert an unqualified type. Union are just Machine n where n is their size
val of_linksem_none : unit -> tPlaceholder 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 ->
+ tThe 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 -> envThe 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.documentval pp : t -> Utils.Pp.documentPretty 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.documentval pp_fragment : fragment -> Utils.Pp.documentval pp_offset : offset -> Utils.Pp.documentval pp_arr : t -> int option list -> Utils.Pp.documentval pp_arr_dim : int option -> Utils.Pp.documentval pp_field : field -> Utils.Pp.documentval pp_struct : struc -> Utils.Pp.documentval pp_enums : enum -> Utils.Pp.documentval pp_env : env -> Utils.Pp.documentPrint 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.tSame as type_at but for structs
val type_at : env:env -> size:int -> t -> int -> t Utils.Option.tGet the type of size size at the provided offset in another type
Dw.FuncThis 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 -> funcCreate a Dwarf function from its linksem counterpart
val scope_of_linksem : Elf.File.t -> Ctype.env -> linksem_scope -> scopeCreate and Dwarf scope from its linksem counterpart
val func_get_api : func -> Arch.func_apiGet the API of the function
val pp_raw_func : func -> Utils.Pp.OCaml.representationval pp_raw_scope : scope -> Utils.Pp.OCaml.representationtype 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 -> tCreate 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_apiGet the API of a top level function
Dw.FuncThis 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 -> funcCreate a Dwarf function from its linksem counterpart
val scope_of_linksem : Elf.File.t -> Ctype.env -> linksem_scope -> scopeCreate and Dwarf scope from its linksem counterpart
val func_get_api : func -> Arch.func_apiGet the API of the function
val pp_raw_func : func -> Utils.Pp.documentval pp_raw_scope : scope -> Utils.Pp.documentThis 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 -> tCreate 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_apiGet the API of a top level function
val pp_raw : t -> Utils.Pp.documentPretty print a raw top level function
Dw.LocThis 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 listThe type of a location in linksem format
val vDW_OP_addr : intThe 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 -> tConvert 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 -> stringConvert the location to a string. This is not reversible
Dw.LocThis 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.tIn the register
*)| RegisterOffset of State.Reg.t * intAt register + offset address
*)| StackFrame of intOn the stackFrame with offset
*)| Global of Elf.SymTable.sym_offsetGlobal variable with an offset
*)| Const of Utils.Sym.t| Dwarf of dwop listUninterpreted dwarf location
*)The type of a location, as static as possible
type linksem_t = dwop listThe 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 -> tval to_string : t -> stringConvert the location to a string. This is not reversible
val pp : t -> Utils.Pp.documentPretty-print the location
Dw.VarThis 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 -> intConvert 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 -> tCreate a DWARF variable from its linksem counterpart
Dw.VarThis 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 optiontype 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 optionval of_linksem : Elf.File.t -> Ctype.env -> linksem_t -> tCreate a DWARF variable from its linksem counterpart
val pp_raw : t -> Utils.Pp.documentPretty print a variable
DwThis 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 ... endThis module contain all the definition to handle local and global variables as defined in the DWARF information of the target file
module Func : sig ... endThis module contain all the definition to handle functions as defined in the DWARF information of the target file
module Loc : sig ... endThis 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 -> 'aThrow a DwarfError
val of_elf : Elf.File.t -> tGet Dwarf information from an Elf file.
May raise a DwarfError if a problem occurs.
val of_file : string -> tGet Dwarf information from an Elf file by name. Use File.of_file
DwThis 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 ... endThis module contain all the definition to handle local and global variables as defined in the DWARF information of the target file
module Func : sig ... endThis module contain all the definition to handle functions as defined in the DWARF information of the target file
module Loc : sig ... endThis 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 -> tGet Dwarf information from an Elf file.
May raise a DwarfError if a problem occurs.
val of_file : string -> tGet Dwarf information from an Elf file by name. Use Elf.File.of_file
val pp_raw : t -> Utils.Pp.documentPretty print dwarf data as an ocaml structure
Elf.Filemodule SymTbl = SymTabletype 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 -> 'aThrow an ElfError
val of_file : string -> tParse an ELF file to create an Elf.File.t using Linksem.
May raise an ElfError
Elf.FileThis 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 = SymTableThe 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.documentPretty prints a machine
module SMap : sig ... endtype 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 -> tParse an ELF file to create an Elf.File.t using Linksem.
May raise an ElfError
Elf.Segmenttype 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 -> tLoads a t using a linksem interpreted segment.
val is_in : t -> int -> boolCheck if an address is inside a segment
val get_addr : (Utils.BytesSeq.t -> int -> 'a) -> t -> int -> 'aGet 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 optionGet a value at an address which is one of the segment of this list. It must be entirely in one of the segment
Elf.SegmentThe 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 -> tLoads a t using a linksem interpreted segment.
val is_in : t -> int -> boolCheck if an address is inside a segment
val get_addr :
+ ((Utils.BytesSeq.t * Relocations.t) -> int -> 'a) ->
+ t ->
+ int ->
+ 'aGet 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 optionGet a value at an address which is one of the segment of this list. It must be entirely in one of the segment
Elf.SymTabletype sym = Symbol.ttype linksem_sym = Symbol.linksem_ttype sym_offset = sym * intThe type of a symbol with offset
val empty : tThe empty symbol table
val add : t -> sym -> tReturn 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 -> symGet 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 optionGet the symbol owning that address. None if no symbol own that address. See of_addr
val of_addr_with_offset : t -> int -> sym_offsetGet a symbol with the offset that correspond to that address
val of_addr_with_offset_opt : t -> int -> sym_offset optionGet a symbol with the offset that correspond to that address
val to_addr_offset : sym_offset -> intGet back the raw address from a symbol+offset value
val string_of_sym_offset : sym_offset -> stringTransform a symbol + offset into the corresponding string
val sym_offset_of_string : t -> string -> sym_offsetTransform a symbol + offset string into the actual symbol and the integer offset
val of_position_string : t -> string -> sym_offsetConvert 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.SymTableThe 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.ttype linksem_sym = Symbol.linksem_ttype sym_offset = sym * intThe type of a symbol with offset
type linksem_t = LinksemRelocatable.global_symbol_init_infoval empty : tThe 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_offsetGet a symbol with the offset that correspond to that address
val of_addr_with_offset_opt : t -> Address.t -> sym_offset optionGet a symbol with the offset that correspond to that address
val to_addr_offset : sym_offset -> Address.tGet back the raw address from a symbol+offset value
val string_of_sym_offset : sym_offset -> stringTransform a symbol + offset into the corresponding string
val sym_offset_of_string : t -> string -> sym_offsetTransform a symbol + offset string into the actual symbol and the integer offset
val of_position_string : t -> string -> sym_offsetConvert 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.documentPretty print the table as a raw ocaml value
Elf.Symboltype linksem_typ = Z.ttype 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 -> boolCheck if an address is in a symbol
val len : t -> intFor conformance with the Utils.RngMap.LenObject module type
val typ_of_linksem : linksem_typ -> typConvert the integer type into typ
val linksem_typ : linksem_t -> linksem_typGet the type from the linksem symbol type
exception LoadingError of string * intLoadingError(name,addr) means that symbol name at addr could not be loaded.
val of_linksem : Segment.t list -> linksem_t -> tConvert 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 -> boolTell if a symbol type is interesting for readDwarf purposes
val is_interesting_linksem : linksem_t -> boolTell if a linksem symbol is interesting for readDwarf purposes
val sub : t -> int -> int -> Utils.BytesSeq.tTake the BytesSeq.t corresponding to the offset and length
Elf.SymbolThis 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.symbolThe type of an ELF symbol in linksem. See of_linksem
Check if an address is in a symbol
val len : t -> intFor conformance with the Utils.RngMap.LenObject module type
val typ_of_linksem : linksem_typ -> typConvert the integer type into typ
val linksem_typ : linksem_t -> linksem_typGet 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 -> boolTell if a symbol type is interesting for readDwarf purposes
val is_interesting_linksem : linksem_t -> boolTell if a linksem symbol is interesting for readDwarf purposes
val pp_typ : typ -> Utils.Pp.documentPretty prints a symbol type
val pp_raw : t -> Utils.Pp.documentRaw pretty printing of a symbol
Elfmodule File : sig ... endmodule Segment : sig ... endmodule SymTable : sig ... endmodule Symbol : sig ... endElfmodule Address : sig ... endmodule File : sig ... endThis 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 ... endmodule Relocations : sig ... endmodule Segment : sig ... endThe 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 ... endThe module provide a type to represent a symbol table.
module Symbol : sig ... endThis 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.ConcreteEvalThis module provides a way of making concrete evaluation of an expression. The only required thing is a context.
type 'v context = 'v -> Value.tA 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.tEvaluate 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 -> boolTell if an expression is concrete, which means no variables of any kind
Exp.ConcreteEvalThis module provides a way of making concrete evaluation of an expression. The only required thing is a context.
type 'v context = 'v -> Value.tA 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 -> boolTell if an expression is concrete, which means no variables of any kind
Make.1-VarMake.Varval pp : t -> Utils.Pp.documentPretty printer to be used, both for memory pretty printing and for sending memory to Z3
Exp.Maketype var = Var.tThe type of variable provided in the functor
Exp.Maketype var = Var.tThe 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.documentPretty print the expression using PpExp
val pp_smt : t -> Utils.Pp.documentPretty print the expression in SMTLIB language
Exp.PpExpThis 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 -> boolFigure out if an expression of precedence inner in an expression of precedence outer needs parentheses
val prec_unop : Ast.unop -> precval prec_bvarith : Ast.bvarith -> precval prec_binop : Ast.no Ast.binop -> precval prec_bvmanyarith : Ast.bvmanyarith -> precval prec_manyop : Ast.manyop -> precval pp_bits : Utils.BitVec.t -> Utils.Pp.documentval ppnot : Utils.Pp.documentval pp_unop : Ast.unop -> Utils.Pp.document -> Utils.Pp.documentval sym_bvarith : Ast.bvarith -> Utils.Pp.documentval sym_bvcomp : Ast.bvcomp -> Utils.Pp.documentval pp_binop : Ast.no Ast.binop -> Utils.Pp.document -> Utils.Pp.document -> Utils.Pp.documentval sym_bvmanyarith : Ast.bvmanyarith -> Utils.Pp.documentval pp_manyop : Ast.manyop -> PPrintEngine.document list -> Utils.Pp.documentval pp_if : PPrintEngine.document -> PPrintEngine.document -> PPrintEngine.document -> Utils.Pp.documentval pp_exp_prec : ('v -> Utils.Pp.document) -> ('a, 'v, Ast.no, Ast.no) Ast.exp -> Utils.Pp.document * precPretty print an expression and return its precedence
Exp.PpExpThis 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 -> precval prec_bvmanyarith : Ast.bvmanyarith -> precval prec_manyop : Ast.manyop -> precval pp_bits : Utils.BitVec.t -> Utils.Pp.documentval ppnot : Utils.Pp.documentval pp_unop : Ast.unop -> Utils.Pp.document -> Utils.Pp.documentval sym_bvarith : Ast.bvarith -> Utils.Pp.documentval sym_bvcomp : Ast.bvcomp -> Utils.Pp.documentval pp_binop :
+ Ast.no Ast.binop ->
+ Utils.Pp.document ->
+ Utils.Pp.document ->
+ Utils.Pp.documentval sym_bvmanyarith : Ast.bvmanyarith -> Utils.Pp.documentval pp_manyop : Ast.manyop -> Utils.Pp.document list -> Utils.Pp.documentval pp_if :
+ Utils.Pp.document ->
+ Utils.Pp.document ->
+ Utils.Pp.document ->
+ Utils.Pp.documentval pp_exp_prec :
+ ('v -> Utils.Pp.document) ->
+ ('a, 'v, Ast.no, Ast.no) Ast.exp ->
+ Utils.Pp.document * precPretty print an expression and return its precedence
val pp_exp :
+ ('a -> Utils.Pp.document) ->
+ ('b, 'a, Ast.no, Ast.no) Ast.exp ->
+ Utils.Pp.documentThe main function for pretty printing an expression
Exp.SumsThis 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 listSplit 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.tMerge 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.tAdd 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 optionRemove 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.tSame 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.tSplit 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 -> boolTells if an expression has a concrete term
Exp.SumsThis 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 optionRemove 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.tSame 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.tSplit 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 -> boolTells if an expression has a concrete term
Exp.TypedThis 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.expThe 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.tyGet 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) tval bits : Utils.BitVec.t -> ('v, 'm) tval bool : bool -> ('v, 'm) tval enum : Ast.enum -> ('v, 'm) tval vec_idx_type : 'a Ast.tyThis 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.expval unop : Ast.unop -> ('v, 'm) t -> ('v, 'm) tval binop : 'm Ast.binop -> ('v, 'm) t -> ('v, 'm) t -> ('v, 'm) tval manyop : AstGen.Ott.manyop -> ('v, 'm) t list -> ('v, 'm) tIn 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) tval bits_smt : string -> ('a, 'b) tval zero : size:int -> ('a, 'b) tval true_ : ('a, 'b) tval false_ : ('a, 'b) tval (+) : ('a, 'b) t -> ('a, 'b) t -> ('a, 'b) tval sum : ('a, 'b) t list -> ('a, 'b) tval sub : ('a, 'b) t -> ('a, 'b) t -> ('a, 'b) tval (-) : ('a, 'b) t -> ('a, 'b) t -> ('a, 'b) tval (*) : ('a, 'b) t -> ('a, 'b) t -> ('a, 'b) tval prod : ('a, 'b) t list -> ('a, 'b) tval sdiv : ('a, 'b) t -> ('a, 'b) t -> ('a, 'b) tval not : ('a, 'b) t -> ('a, 'b) tval neg : ('a, 'b) t -> ('a, 'b) tval extract : first:int -> last:int -> ('a, 'b) t -> ('a, 'b) tval eq : ('a, 'b) t -> ('a, 'b) t -> ('a, 'b) tval (=) : ('a, 'b) t -> ('a, 'b) t -> ('a, 'b) tval concat : ('a, 'b) t list -> ('a, 'b) tval comp : Ast.bvcomp -> ('a, 'b) t -> ('a, 'b) t -> ('a, 'b) tval add_type : a v m. ty_of_var:('a -> 'v -> 'm Ast.ty) -> ('a, 'v, Ast.no, 'm) Ast.exp -> ('v, 'm) tReplace 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 -> boolCheck if an expression is well typed
Exp.TypedThis 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 -> boolval is_bv : 'a Ast.ty -> boolval is_enum : 'a Ast.ty -> boolval expect_bool : 'a Ast.ty -> unitval expect_bv : 'a Ast.ty -> intval expect_enum : 'a Ast.ty -> intThis 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) tval bool : bool -> ('v, 'm) tval vec_idx_type : 'a Ast.tyThis is completely arbitrary, because it is not currently used.
val manyop : AstGen.Ott.manyop -> ('v, 'm) t list -> ('v, 'm) tIn 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) tval bits_smt : string -> ('a, 'b) tval zero : size:int -> ('a, 'b) tval true_ : ('a, 'b) tval false_ : ('a, 'b) tval comp : Ast.bvcomp -> ('a, 'b) t -> ('a, 'b) t -> ('a, 'b) tval add_type :
+ 'a 'v 'm. ty_of_var:('a -> 'v -> 'm Ast.ty) ->
+ ('a, 'v, Ast.no, 'm) Ast.exp ->
+ ('v, 'm) tReplace 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 -> boolCheck if an expression is well typed
Exp.ValueThis 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 -> stringString representation of concrete values
val bv : Utils.BitVec.t -> tBv constructor
val expect_bool : t -> boolExtract a boolean or fail
val expect_bv : ?size:int -> t -> Utils.BitVec.tExtract a bit vector or fail. If size is specified, then it fail if the size don't match
Exp.ValueThis 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 -> stringString representation of concrete values
val pp : t -> Utils.Pp.documentPretty printer for concrete values
val bv : Utils.BitVec.t -> tBv constructor
val expect_bool : t -> boolExtract a boolean or fail
val expect_bv : ?size:int -> t -> Utils.BitVec.tExtract a bit vector or fail. If size is specified, then it fail if the size don't match
ExpThis 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 ... endThis module provides a way of making concrete evaluation of an expression. The only required thing is a context.
module Sums : sig ... endThis module provide sum manipulation functionality on top of typed expression Typed.t
module Typed : sig ... endThis module provide operation on typed expressions i.e expressions whose annotations are their SMT type (Ast.ty).
module Value : sig ... endThis module provide a type to represent concrete values.
module type Var = sig ... endThis is the type signature for variable required by this module
module Pp = PpExpExpThis 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 ... endThis module provides a way of making concrete evaluation of an expression. The only required thing is a context.
module Sums : sig ... endThis module provide sum manipulation functionality on top of typed expression Typed.t
module Typed : sig ... endThis module provide operation on typed expressions i.e expressions whose annotations are their SMT type (Ast.ty).
module Value : sig ... endThis module provide a type to represent concrete values.
module type Var = sig ... endThis is the type signature for variable required by this module
module Pp = PpExpExp.SThe signature of the output module of Make
Exp.SThe 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.documentPretty print the expression using PpExp
val pp_smt : t -> Utils.Pp.documentPretty print the expression in SMTLIB language
Exp.VarThis is the type signature for variable required by this module
Exp.VarThis is the type signature for variable required by this module
val pp : t -> Utils.Pp.documentPretty 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.BaseThis 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 valus 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.ASTtype enum = int * inttype 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 * stringval pp_lpos : Stdlib.Lexing.position -> PPrint.documentval pp_lrng : lrng -> PPrint.documenttype 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 optiontype 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 * stringException that represent an Isla parsing error
exception LexError of loc * stringException that represent an Isla lexing error
type lexer = Stdlib.Lexing.lexbuf -> Parser.tokentype 'a parser = lexer -> Stdlib.Lexing.lexbuf -> 'aval parse : 'a parser -> ?filename:string -> Stdlib.Lexing.lexbuf -> 'aParse a single Isla instruction output from a Lexing.lexbuf
val parse_exp : ?filename:string -> Stdlib.Lexing.lexbuf -> rexpParse a single Isla expression from a Lexing.lexbuf
val parse_exp_string : ?filename:string -> string -> rexpParse a single Isla expression from a string
val parse_exp_channel : ?filename:string -> Stdlib.in_channel -> rexpParse a single Isla expression from a channel
val parse_trc : ?filename:string -> Stdlib.Lexing.lexbuf -> rtrcParse an Isla trace from a Lexing.lexbuf
val parse_trc_string : ?filename:string -> string -> rtrcParse an Isla trace from a string
val parse_trc_channel : ?filename:string -> Stdlib.in_channel -> rtrcParse an Isla trace from a channel
include Isla_lang.PPval pp_raw_vvar : int -> PPrintEngine.documentval pp_raw_name : string -> PPrintEngine.documentval pp_raw_enum_ty : int -> PPrintEngine.documentval pp_raw_enum : Isla_lang__.Isla_lang_ast.enum -> PPrintEngine.documentval pp_raw_int : int -> PPrintEngine.documentval pp_raw_bvi : int -> PPrintEngine.documentval pp_raw_bv : string -> PPrintEngine.documentval pp_raw_str : string -> PPrintEngine.documentval pp_raw_ty : Isla_lang__.Isla_lang_ast.ty -> PPrintEngine.documentval pp_raw_bool : bool -> PPrintEngine.documentval pp_raw_unop : Isla_lang__.Isla_lang_ast.unop -> PPrintEngine.documentval pp_raw_bvarith : Isla_lang__.Isla_lang_ast.bvarith -> PPrintEngine.documentval pp_raw_bvcomp : Isla_lang__.Isla_lang_ast.bvcomp -> PPrintEngine.documentval pp_raw_binop : Isla_lang__.Isla_lang_ast.binop -> PPrintEngine.documentval pp_raw_bvmanyarith : Isla_lang__.Isla_lang_ast.bvmanyarith -> PPrintEngine.documentval pp_raw_manyop : Isla_lang__.Isla_lang_ast.manyop -> PPrintEngine.documentval pp_raw_exp : 'a Isla_lang__.Isla_lang_ast.exp -> PPrintEngine.documentval pp_raw_exp_val : 'a Isla_lang__.Isla_lang_ast.exp_val -> PPrintEngine.documentval pp_raw_smt : 'a Isla_lang__.Isla_lang_ast.smt -> PPrintEngine.documentval pp_raw_valu : Isla_lang__.Isla_lang_ast.valu -> PPrintEngine.documentval pp_raw_selem : (string * Isla_lang__.Isla_lang_ast.valu) -> PPrintEngine.documentval pp_raw_valu_concrete : Isla_lang__.Isla_lang_ast.valu_concrete -> PPrintEngine.documentval pp_raw_selem_concrete : (string * Isla_lang__.Isla_lang_ast.valu_concrete) -> PPrintEngine.documentval pp_raw_accessor : Isla_lang__.Isla_lang_ast.accessor -> PPrintEngine.documentval pp_raw_accessor_list : Isla_lang__.Isla_lang_ast.accessor_list -> PPrintEngine.documentval pp_raw_valu_option : Isla_lang__.Isla_lang_ast.valu_option -> PPrintEngine.documentval pp_raw_event : 'a Isla_lang__.Isla_lang_ast.event -> PPrintEngine.documentval pp_raw_trc : 'a Isla_lang__.Isla_lang_ast.trc -> PPrintEngine.documentval pp_vvar : int -> PPrintEngine.documentval pp_name : string -> PPrintEngine.documentval pp_enum_ty : int -> PPrintEngine.documentval pp_enum : Isla_lang__.Isla_lang_ast.enum -> PPrintEngine.documentval pp_int : int -> PPrintEngine.documentval pp_bvi : int -> PPrintEngine.documentval pp_bv : string -> PPrintEngine.documentval pp_str : string -> PPrintEngine.documentval pp_j : int -> stringval pp_ty : Isla_lang__.Isla_lang_ast.ty -> PPrintEngine.documentval pp_bool : bool -> PPrintEngine.documentval pp_unop : Isla_lang__.Isla_lang_ast.unop -> PPrintEngine.documentval pp_bvarith : Isla_lang__.Isla_lang_ast.bvarith -> PPrintEngine.documentval pp_bvcomp : Isla_lang__.Isla_lang_ast.bvcomp -> PPrintEngine.documentval pp_binop : Isla_lang__.Isla_lang_ast.binop -> PPrintEngine.documentval pp_bvmanyarith : Isla_lang__.Isla_lang_ast.bvmanyarith -> PPrintEngine.documentval pp_manyop : Isla_lang__.Isla_lang_ast.manyop -> PPrintEngine.documentval pp_exp : 'a Isla_lang__.Isla_lang_ast.exp -> PPrintEngine.documentval pp_exp_val : 'a Isla_lang__.Isla_lang_ast.exp_val -> PPrintEngine.documentval pp_smt : 'a Isla_lang__.Isla_lang_ast.smt -> PPrintEngine.documentval pp_valu : Isla_lang__.Isla_lang_ast.valu -> PPrintEngine.documentval pp_selem : (string * Isla_lang__.Isla_lang_ast.valu) -> PPrintEngine.documentval pp_valu_concrete : Isla_lang__.Isla_lang_ast.valu_concrete -> PPrintEngine.documentval pp_selem_concrete : (string * Isla_lang__.Isla_lang_ast.valu_concrete) -> PPrintEngine.documentval pp_accessor : Isla_lang__.Isla_lang_ast.accessor -> PPrintEngine.documentval pp_accessor_list : Isla_lang__.Isla_lang_ast.accessor_list -> PPrintEngine.documentval pp_valu_option : Isla_lang__.Isla_lang_ast.valu_option -> PPrintEngine.documentval pp_event : 'a Isla_lang__.Isla_lang_ast.event -> PPrintEngine.documentval pp_trc : 'a Isla_lang__.Isla_lang_ast.trc -> PPrintEngine.documentIsla.BaseThis 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 valus 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 endtype lrng = Isla_lang__Isla_lang_ast.lrng = | UnknownRng| Generated of lrng| Range of Stdlib.Lexing.position * Stdlib.Lexing.positionexception Parse_error_locn of lrng * stringval pp_lrng : lrng -> PPrint.documenttype base_val = Isla_lang__Isla_lang_ast.base_val = | Val_Symbolic of int| Val_Bool of bool| Val_Bits of string| Val_Enum of enumtype 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 enumtype tag_value = valu optiontype !'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 * 'atype instruction_segments = Isla_lang__Isla_lang_ast.instruction_segments = | Segments of segment listtype !'a maybe_fork = 'a Isla_lang__Isla_lang_ast.maybe_fork = | Cases of string * 'a tree_trc list| Endand !'a tree_trc = 'a Isla_lang__Isla_lang_ast.tree_trc = | TreeTrace of 'a event list * 'a maybe_forktype !'a trcs = 'a Isla_lang__Isla_lang_ast.trcs = | Traces of 'a trc list| TracesWithSegments of instruction_segments * 'a trc listtype !'a whole_tree = 'a Isla_lang__Isla_lang_ast.whole_tree = | BareTree of 'a tree_trc| TreeWithSegments of instruction_segments * 'a tree_trcval subst_val_maybe_fork : base_val -> int -> 'a maybe_fork -> 'a maybe_forkval subst_val_whole_tree : base_val -> int -> 'a whole_tree -> 'a whole_treeexception ParseError of loc * stringException that represent an Isla parsing error
exception LexError of loc * stringException that represent an Isla lexing error
type 'a parser = lexer -> Stdlib.Lexing.lexbuf -> 'aval parse : 'a parser -> ?filename:string -> Stdlib.Lexing.lexbuf -> 'aParse a single Isla instruction output from a Lexing.lexbuf
val parse_exp : ?filename:string -> Stdlib.Lexing.lexbuf -> rexpParse a single Isla expression from a Lexing.lexbuf
val parse_exp_string : ?filename:string -> string -> rexpParse a single Isla expression from a string
val parse_exp_channel : ?filename:string -> Stdlib.in_channel -> rexpParse a single Isla expression from a channel
val parse_trc : ?filename:string -> Stdlib.Lexing.lexbuf -> rtrcParse an Isla trace from a Lexing.lexbuf
val parse_trc_string : ?filename:string -> string -> rtrcParse an Isla trace from a string
val parse_trc_channel : ?filename:string -> Stdlib.in_channel -> rtrcParse an Isla trace from a channel
val parse_trcs_string : ?filename:string -> string -> rtrcsval parse_trcs_channel : ?filename:string -> Stdlib.in_channel -> rtrcsval parse_segments :
+ ?filename:string ->
+ Stdlib.Lexing.lexbuf ->
+ instruction_segmentsval parse_segments_string : ?filename:string -> string -> instruction_segmentsval parse_segments_channel :
+ ?filename:string ->
+ Stdlib.in_channel ->
+ instruction_segmentsinclude module type of struct include Isla_lang.PP endCache.Epochval to_file : string -> (string * int * string) -> unitval of_file : string -> string * int * stringval of_config : Server.Config.t -> string * int * stringBuild the epoch from the config. This function does the config Digest
Cache.Epochval of_config : Config.File.ArchConf.Isla.t -> string * int * stringBuild the epoch from the config. This function does the config Digest
Cache.ICThe isla cache module
type key = Opcode.ttype value = TraceList.ttype epoch = Epoch.ttype t = Utils__Cache.Make(Opcode)(TraceList)(Epoch).tCache.ICThe isla cache module
Cache.OpcodeImplementation 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 optionval equal : Utils.BytesSeq.t option -> Utils.BytesSeq.t option -> boolval hash : Utils.BytesSeq.t option -> Utils.IntBits.tval to_file : string -> Utils.BytesSeq.t option -> unitval of_file : Utils.IntBits.t -> string -> Utils.BytesSeq.t optionCache.OpcodeImplementation 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 optionval reloc_id : Relocation.t option -> intval reloc_of_id : int -> Relocation.t optionval equal :
+ (Utils.BytesSeq.t * 'a) option ->
+ (Utils.BytesSeq.t * 'a) option ->
+ boolval small_enough : Utils.BytesSeq.t -> int -> boolval hash : (Utils.BytesSeq.t * Relocation.t option) option -> Utils.IntBits.tval to_file : 'a -> (Utils.BytesSeq.t * Relocation.t option) option -> unitval of_file :
+ Utils.IntBits.t ->
+ 'a ->
+ (Utils.BytesSeq.t * Relocation.t option) optionCache.TraceListRepresentation of trace lists on disk.
It is just a list of traces separated by new lines
type t = Base.rtrc listCache.TraceListRepresentation of trace lists on disk.
It is just a list of traces separated by new lines
Isla.CacheThis 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.configThe type of Isla configuration
module TraceList : sig ... endRepresentation of trace lists on disk.
val epoch : intAn 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 ... endmodule IC : sig ... endThe isla cache module
val configr : config option Stdlib.refIf 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 -> unitStart the caching system. Do not yet start the server
val get_traces : Utils.BytesSeq.t -> Base.rtrc listGet the traces of the opcode given. Use Server if the value is not in the cache
Isla.CacheThis 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.configThe type of Isla configuration
module Opcode : sig ... endImplementation of Cache.Key for opcodes.
module TraceList : sig ... endRepresentation 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 ... endmodule IC : sig ... endThe isla cache module
val configr : config option Stdlib.refIf 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 -> unitStart the caching system. Do not yet start the server
val get_traces : Server.opcode -> Base.rtrcsGet the traces of the opcode given. Use Server if the value is not in the cache
Isla.Convval ty : Isla__.Base.ty -> 'a Ast.tyval unop : Isla__.Base.unop -> Ast.unopval bvarith : Isla__.Base.bvarith -> Ast.bvarithval bvcomp : Isla__.Base.bvcomp -> Ast.bvcompval bvmanyarith : Isla__.Base.bvmanyarith -> Ast.bvmanyarithval binop : Isla__.Base.binop -> 'm Ast.binopval manyop : Isla__.Base.manyop -> Ast.manyopval direct_exp_no_var : ('a Isla__.Base.exp -> ('a, 'v, 'b, 'm) Ast.exp) -> 'a Isla__.Base.exp -> ('a, 'v, 'b, 'm) Ast.expval exp_var_conv : (int -> 'v) -> 'a Isla__.Base.exp -> ('a, 'v, 'b, 'm) Ast.expval exp : 'a Isla__.Base.exp -> ('a, int, 'b, 'c) Ast.expval exp_var_subst : (int -> 'a -> ('a, 'v, 'b, 'm) Ast.exp) -> 'a Isla__.Base.exp -> ('a, 'v, 'b, 'm) Ast.expConvert 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.tConvert 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.smtval smt : 'a Isla__.Base.smt -> ('a, int, 'b, 'c) Ast.smtIsla.Convval bvarith : Base.bvarith -> Ast.bvarithval bvcomp : Base.bvcomp -> Ast.bvcompval bvmanyarith : Base.bvmanyarith -> Ast.bvmanyarithval binop : Base.binop -> 'm Ast.binopval manyop : Base.manyop -> Ast.manyopval exp_var_subst :
+ (int -> 'a -> ('a, 'v, 'b, 'm) Ast.exp) ->
+ 'a Base.exp ->
+ ('a, 'v, 'b, 'm) Ast.expConvert 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.tConvert directly from an untyped isla expression to an Exp.Typed by substituing isla variables with already typed expressions
Isla.ManipThis module provide generic manipulation function of isla ast
val is_linear : 'a Isla__.Base.trc -> boolCheck if a trace is linear (has no branches)
val annot_exp : 'a Isla__.Base.exp -> 'aGet the annotation of an expression
val annot_event : 'a Isla__.Base.event -> 'aGet 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.expval direct_exp_iter_exp : ('a Isla__.Base.exp -> unit) -> 'a Isla__.Base.exp -> unitval direct_smt_map_exp : ('a Isla__.Base.exp -> 'a Isla__.Base.exp) -> 'a Isla__.Base.smt -> 'a Isla__.Base.smtval direct_event_map_exp : ('a Isla__.Base.exp -> 'a Isla__.Base.exp) -> 'a Isla__.Base.event -> 'a Isla__.Base.eventval direct_event_iter_valu : (Isla__.Base.valu -> unit) -> 'a Isla__.Base.event -> unitval direct_event_map_valu : (Isla__.Base.valu -> Isla__.Base.valu) -> 'a Isla__.Base.event -> 'a Isla__.Base.eventval direct_valu_iter_valu : (Isla__.Base.valu -> unit) -> Isla__.Base.valu -> unitval direct_valu_map_valu : (Isla__.Base.valu -> Isla__.Base.valu) -> Isla__.Base.valu -> Isla__.Base.valuThis 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 -> unititerate a function on all the variable of an expression
val event_iter_valu : (Isla__.Base.valu -> unit) -> 'a Isla__.Base.event -> unitval var_subst : (int -> 'a -> 'a Isla__.Base.exp) -> 'a Isla__.Base.exp -> 'a Isla__.Base.expSubstitute variable with expression according to subst function
val accessor_of_string : string -> Isla__.Base.accessorval string_of_accessor : Isla__.Base.accessor -> stringval accessor_of_string_list : string list -> Isla__.Base.accessor_listval string_of_accessor_list : Isla__.Base.accessor_list -> string listval valu_get : Isla__.Base.valu -> string list -> Isla__.Base.valuFollow 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.trcSplit the trace between before and after the "cycle" event
val remove_init : 'a Isla__.Base.trc -> 'a Isla__.Base.trcRemove 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.trcRemove all the events related to ignored registers
Isla.ManipThis module provide generic manipulation function of isla ast
val is_linear : 'a Base.trc -> boolCheck if a trace is linear (has no branches)
val annot_exp : 'a Base.exp -> 'aGet the annotation of an expression
val annot_event : 'a Base.event -> 'aGet 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.eventval direct_event_iter_valu : (Base.valu -> unit) -> 'a Base.event -> unitval direct_event_map_valu :
+ (Base.valu -> Base.valu) ->
+ 'a Base.event ->
+ 'a Base.eventThis 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 -> unititerate a function on all the variable of an expression
val event_iter_valu : (Base.valu -> unit) -> 'a Base.event -> unitIterate 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.accessorval string_of_accessor : Base.accessor -> stringval accessor_of_string_list : string list -> Base.accessor_listval string_of_accessor_list : Base.accessor_list -> string listFollow 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.trcRemove all the events related to ignored registers
Isla.PreprocessThis 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 listPreprocess a group of traces, by removing useless registers (according to the config), removing initialisation code and simplifying with simplify_trc
Isla.PreprocessThis 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.rtrcsPreprocess a group of traces, by removing useless registers (according to the config), removing initialisation code and simplifying with simplify_trc
Isla.RunThis 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 * stringException that represent an Isla runtime error which should not happen
val run_error : Isla__.Base.lrng -> ('a, unit, string, 'b) Stdlib.format4 -> 'aThrow a run error with the string part as formated by the format string
type value_context = State.exp Utils.HashVector.tThe 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.tGet 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.expConvert 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.expGive 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 -> unitThis 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 -> unitThis 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 -> unitRun 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 -> unitThis 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.RunThis 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 * stringException that represent an Isla runtime error which should not happen
val run_error : Base.lrng -> ('a, unit, string, 'b) Stdlib.format4 -> 'aThrow a run error with the string part as formated by the format string
type value_context = State.exp Utils.HashVector.tThe 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.tval exp_conv_subst : value_context -> Base.rexp -> State.expConvert 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.expGive 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 -> unitThis 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 -> unitThis 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 -> unitRun 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 -> unitThis 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.CmdThis module provide a CLI subcommand to test isla directly. All isla output is reported as raw text
val server_test : Config.Arch.t -> unitval term : unit Cmdliner.Term.tval info : Cmdliner.Term.infoval command : unit Cmdliner.Term.t * Cmdliner.Term.infoServer.CmdThis module provide a CLI subcommand to test isla directly. All isla output is reported as raw text
val server_test : Config.Arch.t -> unitIsla.ServerThis 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.Filemodule CommonOpt = Config.CommonOptmodule Config = Config.File.ArchConf.Islamodule Server = Utils.Cmd.SocketServertype config = Config.tThe configuration record type
type trcs = (bool * Base.rtrc) listThe 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 : stringBump 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.refval server : Server.t option Stdlib.refThis instance of socket server for isla
val get_server : unit -> Server.tAssume the server is started and get it out of the reference
val cmd_of_config : config -> string -> string arrayCompute the isla-client command line from the isla configuration
val raw_start : config -> unitStart 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_answerRead 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 -> stringExpect a version answer and fails if it is not the case
val expect_traces : answer -> (bool * string) listExpect isla traces and fails if it is not the case
val expect_parsed_traces : answer -> trcsExpect isla traces and fails if it is not the case, additionally parse them
exception IslaErrorWhen 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 -> answerRead the answer from isla, block until full answer
val pp_answer : answer -> PPrintEngine.documentAnswer 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 -> stringConvert a request into the string message expected by isla-client This should match the protocol
val send_string_request : string -> unitSend a string request to the server, and do not wait for any answer
val request_bin_parsed : Utils.BytesSeq.t -> trcsRequest the traces of a binary instruction and parse the result.
This is the main entry point of this module.
val send_request : request -> unitSend a request without expecting any answer
val start : config -> unitStart isla and check version
module Cmd : sig ... endThis module provide a CLI subcommand to test isla directly. All isla output is reported as raw text
Isla.ServerThis 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.Filemodule CommonOpt = Config.CommonOptmodule Config = Config.File.ArchConf.Islamodule Server = Utils.Cmd.SocketServertype config = Config.tThe configuration record type
type trcs = Base.instruction_segments option * (bool * Base.rtrc) listThe 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 optionBump 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.refThis instance of socket server for isla
val get_server : unit -> Server.tAssume the server is started and get it out of the reference
val cmd_of_config : config -> string -> string arrayCompute the isla-client command line from the isla configuration
val raw_start : config -> unitStart the server with the specified architecture, do not attempt any checks
val read_basic_answer : unit -> basic_answerRead 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 -> stringExpect a version answer and fails if it is not the case
val expect_traces : answer -> string option * (bool * string) listExpect 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 -> answerRead the answer from isla, block until full answer
val pp_answer : answer -> Utils.Pp.documentAnswer pretty printer
The type of a request to isla
val string_of_request : request -> stringConvert 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 -> unitSend a request without expecting any answer
val start : config -> unitStart isla and check version
Test that isla can start and keep a valid version
module Cmd : sig ... endThis module provide a CLI subcommand to test isla directly. All isla output is reported as raw text
Isla.TestThis file about testing interaction with Isla for single instructions
module SMT = Z3type 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.tval direct : bool Cmdliner.Term.tval bin : bool Cmdliner.Term.tval hex : bool Cmdliner.Term.tval noparse : bool Cmdliner.Term.tval preprocess : bool Cmdliner.Term.tval typer : bool Cmdliner.Term.tval run : bool Cmdliner.Term.tval simp : bool Cmdliner.Term.tval file : bool Cmdliner.Term.tval sym : string option Cmdliner.Term.tval input_f2m : bool -> string option -> imode Cmdliner.Term.retInput flag to mode conversion
val imode_term : imode Cmdliner.Term.tval input : imode -> string -> (string * string) Cmdliner.Term.retInput takes the imode and the main argument and returns the filename and input string
val input_term : (string * string) Cmdliner.Term.tval isla_f2m : bool -> bool -> bool -> 'a option -> isla_mode Cmdliner.Term.retConvert 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.tval isla_mode_to_request : isla_mode -> string -> Server.requestval isla_run : isla_mode -> Config.Arch.t -> (string * string) -> string * string * Server.configRun 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.tval processing_f2m : bool -> bool -> bool -> bool -> pmodeHow far into the processing pipeline we go. We just pick the deepest option chosen
val pmode_term : pmode Cmdliner.Term.tval processing : bool -> pmode -> (string * string * Server.config) -> unitDoes the actual processing of the trace
Isla.TestThis file about testing interaction with Isla for single instructions
module SMT = Z3type pmode = | DUMPDump isla output on standard output
*)| PARSEparse isla output and prettyprint it
*)| TYPEType isla output and dump var types. Also dump the deduced register file
*)| RUNRun the the isla output on a test state and print all branches and states
*)| SIMPRun 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.retInput flag to mode conversion
val imode_term : imode Cmdliner.Term.tval input : imode -> string -> (string * string) Cmdliner.Term.retInput 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.retConvert 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.tval isla_mode_to_request : isla_mode -> string -> Server.requestval isla_run :
+ isla_mode ->
+ Config.Arch.t ->
+ (string * string) ->
+ string * string * Server.configRun 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.tval processing_f2m : bool -> bool -> bool -> bool -> pmodeHow far into the processing pipeline we go. We just pick the deepest option chosen
val pmode_term : pmode Cmdliner.Term.tval processing : bool -> pmode -> (string * string * Server.config) -> unitDoes the actual processing of the trace
Isla.TypeThis 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.tA context that associate Isla types to Isla variables
type lty = Isla__.Base.lrng * Isla__.Base.tyA group of the source range and type for an expression
exception TypeError of Isla__.Base.lrng * stringException that represent an Isla typing error
val tassert : Isla__.Base.lrng -> string -> bool -> unitAssert some properties for type correctness. Requires a lrng and a string error message
val expect_bool : string -> (Isla__.Base.lrng * Isla__.Base.ty) -> unitval expect_bv : string -> (Isla__.Base.lrng * Isla__.Base.ty) -> intval expect_enum : string -> (Isla__.Base.lrng * Isla__.Base.ty) -> intval type_unop : Isla__.Base.lrng -> Isla__.Base.unop -> lty -> Isla__.Base.tyval type_binop : Isla__.Base.lrng -> Isla__.Base.binop -> (Isla__.Base.lrng * Isla__.Base.ty) -> (Isla__.Base.lrng * Isla__.Base.ty) -> Isla__.Base.tyval type_manyop : Isla__.Base.lrng -> Isla__.Base.manyop -> lty list -> Isla__.Base.tyval type_valu : Isla__.Base.lrng -> type_context -> Isla__.Base.valu -> (State.Reg.Path.t * State.Reg.ty) listTake 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 -> ltyval type_expr : type_context -> Isla__.Base.lrng Isla__.Base.exp -> Isla__.Base.tyval type_trc : ?tc:Isla__.Base.ty Utils.HashVector.t -> Base.rtrc -> Isla__.Base.ty Utils.HashVector.tAdd 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.documentPrint a type context for debugging
Isla.TypeThis 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.tA context that associate Isla types to Isla variables
exception TypeError of Base.lrng * stringException that represent an Isla typing error
val tassert : Base.lrng -> string -> bool -> unitAssert some properties for type correctness. Requires a lrng and a string error message
val type_manyop : Base.lrng -> Base.manyop -> lty list -> Base.tyval type_valu :
+ Base.lrng ->
+ type_context ->
+ Base.valu ->
+ (State.Reg.Path.t * State.Reg.ty) listTake 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 -> ltyval type_expr : type_context -> Base.lrng Base.exp -> Base.tyval type_trc :
+ ?tc:Base.ty Utils.HashVector.t ->
+ Base.rtrc ->
+ Base.ty Utils.HashVector.tAdd 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.documentPrint a type context for debugging
Islainclude Baseinclude Isla_lang.ASTtype enum = int * inttype 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 * stringval pp_lpos : Stdlib.Lexing.position -> PPrint.documentval pp_lrng : lrng -> PPrint.documenttype 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 optiontype 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 * stringException that represent an Isla parsing error
exception LexError of loc * stringException that represent an Isla lexing error
type lexer = Stdlib.Lexing.lexbuf -> Parser.tokentype 'a parser = lexer -> Stdlib.Lexing.lexbuf -> 'aval parse : 'a parser -> ?filename:string -> Stdlib.Lexing.lexbuf -> 'aParse a single Isla instruction output from a Lexing.lexbuf
val parse_exp : ?filename:string -> Stdlib.Lexing.lexbuf -> rexpParse a single Isla expression from a Lexing.lexbuf
val parse_exp_string : ?filename:string -> string -> rexpParse a single Isla expression from a string
val parse_exp_channel : ?filename:string -> Stdlib.in_channel -> rexpParse a single Isla expression from a channel
val parse_trc : ?filename:string -> Stdlib.Lexing.lexbuf -> rtrcParse an Isla trace from a Lexing.lexbuf
val parse_trc_string : ?filename:string -> string -> rtrcParse an Isla trace from a string
val parse_trc_channel : ?filename:string -> Stdlib.in_channel -> rtrcParse an Isla trace from a channel
include Isla_lang.PPval pp_raw_vvar : int -> PPrintEngine.documentval pp_raw_name : string -> PPrintEngine.documentval pp_raw_enum_ty : int -> PPrintEngine.documentval pp_raw_enum : Isla_lang__.Isla_lang_ast.enum -> PPrintEngine.documentval pp_raw_int : int -> PPrintEngine.documentval pp_raw_bvi : int -> PPrintEngine.documentval pp_raw_bv : string -> PPrintEngine.documentval pp_raw_str : string -> PPrintEngine.documentval pp_raw_ty : Isla_lang__.Isla_lang_ast.ty -> PPrintEngine.documentval pp_raw_bool : bool -> PPrintEngine.documentval pp_raw_unop : Isla_lang__.Isla_lang_ast.unop -> PPrintEngine.documentval pp_raw_bvarith : Isla_lang__.Isla_lang_ast.bvarith -> PPrintEngine.documentval pp_raw_bvcomp : Isla_lang__.Isla_lang_ast.bvcomp -> PPrintEngine.documentval pp_raw_binop : Isla_lang__.Isla_lang_ast.binop -> PPrintEngine.documentval pp_raw_bvmanyarith : Isla_lang__.Isla_lang_ast.bvmanyarith -> PPrintEngine.documentval pp_raw_manyop : Isla_lang__.Isla_lang_ast.manyop -> PPrintEngine.documentval pp_raw_exp : 'a Isla_lang__.Isla_lang_ast.exp -> PPrintEngine.documentval pp_raw_exp_val : 'a Isla_lang__.Isla_lang_ast.exp_val -> PPrintEngine.documentval pp_raw_smt : 'a Isla_lang__.Isla_lang_ast.smt -> PPrintEngine.documentval pp_raw_valu : Isla_lang__.Isla_lang_ast.valu -> PPrintEngine.documentval pp_raw_selem : (string * Isla_lang__.Isla_lang_ast.valu) -> PPrintEngine.documentval pp_raw_valu_concrete : Isla_lang__.Isla_lang_ast.valu_concrete -> PPrintEngine.documentval pp_raw_selem_concrete : (string * Isla_lang__.Isla_lang_ast.valu_concrete) -> PPrintEngine.documentval pp_raw_accessor : Isla_lang__.Isla_lang_ast.accessor -> PPrintEngine.documentval pp_raw_accessor_list : Isla_lang__.Isla_lang_ast.accessor_list -> PPrintEngine.documentval pp_raw_valu_option : Isla_lang__.Isla_lang_ast.valu_option -> PPrintEngine.documentval pp_raw_event : 'a Isla_lang__.Isla_lang_ast.event -> PPrintEngine.documentval pp_raw_trc : 'a Isla_lang__.Isla_lang_ast.trc -> PPrintEngine.documentval pp_vvar : int -> PPrintEngine.documentval pp_name : string -> PPrintEngine.documentval pp_enum_ty : int -> PPrintEngine.documentval pp_enum : Isla_lang__.Isla_lang_ast.enum -> PPrintEngine.documentval pp_int : int -> PPrintEngine.documentval pp_bvi : int -> PPrintEngine.documentval pp_bv : string -> PPrintEngine.documentval pp_str : string -> PPrintEngine.documentval pp_j : int -> stringval pp_ty : Isla_lang__.Isla_lang_ast.ty -> PPrintEngine.documentval pp_bool : bool -> PPrintEngine.documentval pp_unop : Isla_lang__.Isla_lang_ast.unop -> PPrintEngine.documentval pp_bvarith : Isla_lang__.Isla_lang_ast.bvarith -> PPrintEngine.documentval pp_bvcomp : Isla_lang__.Isla_lang_ast.bvcomp -> PPrintEngine.documentval pp_binop : Isla_lang__.Isla_lang_ast.binop -> PPrintEngine.documentval pp_bvmanyarith : Isla_lang__.Isla_lang_ast.bvmanyarith -> PPrintEngine.documentval pp_manyop : Isla_lang__.Isla_lang_ast.manyop -> PPrintEngine.documentval pp_exp : 'a Isla_lang__.Isla_lang_ast.exp -> PPrintEngine.documentval pp_exp_val : 'a Isla_lang__.Isla_lang_ast.exp_val -> PPrintEngine.documentval pp_smt : 'a Isla_lang__.Isla_lang_ast.smt -> PPrintEngine.documentval pp_valu : Isla_lang__.Isla_lang_ast.valu -> PPrintEngine.documentval pp_selem : (string * Isla_lang__.Isla_lang_ast.valu) -> PPrintEngine.documentval pp_valu_concrete : Isla_lang__.Isla_lang_ast.valu_concrete -> PPrintEngine.documentval pp_selem_concrete : (string * Isla_lang__.Isla_lang_ast.valu_concrete) -> PPrintEngine.documentval pp_accessor : Isla_lang__.Isla_lang_ast.accessor -> PPrintEngine.documentval pp_accessor_list : Isla_lang__.Isla_lang_ast.accessor_list -> PPrintEngine.documentval pp_valu_option : Isla_lang__.Isla_lang_ast.valu_option -> PPrintEngine.documentval pp_event : 'a Isla_lang__.Isla_lang_ast.event -> PPrintEngine.documentval pp_trc : 'a Isla_lang__.Isla_lang_ast.trc -> PPrintEngine.documentmodule Base : sig ... endThis module wraps all isla-lang functionality. No other module should directly touch the Isla_lang module.
module Conv : sig ... endmodule Manip : sig ... endThis module provide generic manipulation function of isla ast
module Preprocess : sig ... endThis module is about preprocessing isla traces. This includes:
module Server : sig ... endThis module is about launching isla as a background server and using it
module Test : sig ... endThis file about testing interaction with Isla for single instructions
module Type : sig ... endThis module is about type isla trace and register discovery.
Islainclude module type of struct include Base endinclude module type of struct include Isla_lang.AST endtype lrng = Isla_lang__Isla_lang_ast.lrng = | UnknownRng| Generated of lrng| Range of Stdlib.Lexing.position * Stdlib.Lexing.positionexception Parse_error_locn of lrng * stringval pp_lrng : lrng -> PPrint.documenttype base_val = Isla_lang__Isla_lang_ast.base_val = | Val_Symbolic of int| Val_Bool of bool| Val_Bits of string| Val_Enum of enumtype 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 enumtype tag_value = valu optiontype !'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 * 'atype instruction_segments = Isla_lang__Isla_lang_ast.instruction_segments = | Segments of segment listtype !'a maybe_fork = 'a Isla_lang__Isla_lang_ast.maybe_fork = | Cases of string * 'a tree_trc list| Endand !'a tree_trc = 'a Isla_lang__Isla_lang_ast.tree_trc = | TreeTrace of 'a event list * 'a maybe_forktype !'a trcs = 'a Isla_lang__Isla_lang_ast.trcs = | Traces of 'a trc list| TracesWithSegments of instruction_segments * 'a trc listtype !'a whole_tree = 'a Isla_lang__Isla_lang_ast.whole_tree = | BareTree of 'a tree_trc| TreeWithSegments of instruction_segments * 'a tree_trcval subst_val_maybe_fork : base_val -> int -> 'a maybe_fork -> 'a maybe_forkval subst_val_whole_tree : base_val -> int -> 'a whole_tree -> 'a whole_treemodule Lexer = Base.Lexermodule Parser = Base.Parserexception ParseError of loc * stringException that represent an Isla parsing error
exception LexError of loc * stringException that represent an Isla lexing error
type 'a parser = lexer -> Stdlib.Lexing.lexbuf -> 'aval parse : 'a parser -> ?filename:string -> Stdlib.Lexing.lexbuf -> 'aParse a single Isla instruction output from a Lexing.lexbuf
val parse_exp : ?filename:string -> Stdlib.Lexing.lexbuf -> rexpParse a single Isla expression from a Lexing.lexbuf
val parse_exp_string : ?filename:string -> string -> rexpParse a single Isla expression from a string
val parse_exp_channel : ?filename:string -> Stdlib.in_channel -> rexpParse a single Isla expression from a channel
val parse_trc : ?filename:string -> Stdlib.Lexing.lexbuf -> rtrcParse an Isla trace from a Lexing.lexbuf
val parse_trc_string : ?filename:string -> string -> rtrcParse an Isla trace from a string
val parse_trc_channel : ?filename:string -> Stdlib.in_channel -> rtrcParse an Isla trace from a channel
val parse_trcs_string : ?filename:string -> string -> rtrcsval parse_trcs_channel : ?filename:string -> Stdlib.in_channel -> rtrcsval parse_segments :
+ ?filename:string ->
+ Stdlib.Lexing.lexbuf ->
+ instruction_segmentsval parse_segments_string : ?filename:string -> string -> instruction_segmentsval parse_segments_channel :
+ ?filename:string ->
+ Stdlib.in_channel ->
+ instruction_segmentsinclude module type of struct include Isla_lang.PP endmodule Base : sig ... endThis module wraps all isla-lang functionality. No other module should directly touch the Isla_lang module.
module Conv : sig ... endmodule Manip : sig ... endThis module provide generic manipulation function of isla ast
module Preprocess : sig ... endThis module is about preprocessing isla traces. This includes:
module Relocation : sig ... endmodule Server : sig ... endThis module is about launching isla as a background server and using it
module Test : sig ... endThis file about testing interaction with Isla for single instructions
module Type : sig ... endThis module is about type isla trace and register discovery.
Other_cmds.CopySourcesThis module is the body implementation for the copy-sources subcommand.
Other_cmds.CopySourcesCmdOther_cmds.CopySourcesCmdThis module is the command-line processing for the copy-sources subcommand.
Other_cmds.DumpDwarfOther_cmds.DumpDwarfThis 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.DumpSymOther_cmds.DumpSymThis module adds a command to dump the symbol of an ELF file with their content
ReadDwarf.DefaultReadDwarf.DefaultOther_cmds.ReadDwarfmodule Default : sig ... endOther_cmds.ReadDwarfmodule Default : sig ... endOther_cmdsmodule CopySources : sig ... endmodule CopySourcesCmd : sig ... endmodule DumpDwarf : sig ... endmodule DumpSym : sig ... endmodule ReadDwarf : sig ... endOther_cmdsmodule CopySources : sig ... endThis module is the body implementation for the copy-sources subcommand.
module CopySourcesCmd : sig ... endThis module is the command-line processing for the copy-sources subcommand.
module DumpDwarf : sig ... endThis 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 ... endThis module adds a command to dump the symbol of an ELF file with their content
module ReadDwarf : sig ... endPretty 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_islaQtest_islaQtest_runQtest_runQtest_sig_aarch64Qtest_sig_aarch64Qtest_utilsQtest_utilsRun.BBmodule SMT = Z3val dump : bool Cmdliner.Term.tval reg_types : bool Cmdliner.Term.tval no_run : bool Cmdliner.Term.tval simp_trace : bool Cmdliner.Term.tval simp_state : bool Cmdliner.Term.tval simp : bool Cmdliner.Term.tval elf : string Cmdliner.Term.tval sym : string Cmdliner.Term.tval len : int option Cmdliner.Term.tval get_code : string -> string -> int option -> Utils.BytesSeq.tval code_term : Utils.BytesSeq.t Cmdliner.Term.tval simp_trace_term : bool Cmdliner.Term.tval simp_state_term : bool Cmdliner.Term.tval get_bb : bool -> bool -> bool -> Utils.BytesSeq.t -> Bb_lib.tval bb_term : Bb_lib.t Cmdliner.Term.tval run_bb : bool -> bool -> Bb_lib.t -> unitval term : unit Cmdliner.Term.tval info : Cmdliner.Term.infoval command : unit Cmdliner.Term.t * Cmdliner.Term.infoRun.BBThis module allow to do a test run of all the machinery for a single basic block
module SMT = Z3val get_code : string -> string -> int option -> Utils.BytesSeq.tval code_term : Utils.BytesSeq.t Cmdliner.Term.tval get_bb : bool -> bool -> bool -> Utils.BytesSeq.t -> Bb_lib.tval bb_term : Bb_lib.t Cmdliner.Term.tval run_bb : bool -> bool -> Bb_lib.t -> unitRun.Bb_libtype trc = Trace.ttype state = State.ttype 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 -> tTake 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 -> unitSimplifies the traces in the basic block
val run_mut : ?dwarf:Dw.t -> State.t -> t -> unitRun 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 -> stateRun a linear basic block on a trace and return a new state
If dwarf is provided, the run is typed.
val pp : t -> PPrintEngine.documentPretty print the basic block (The traces)
Run.Bb_libThis 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.ttype state = State.tType of a basic block.
The main part is the traces of all the non-branching instruction
val from_binary : Utils.BytesSeq.t -> tTake 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 -> unitSimplifies 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.documentPretty print the basic block (The traces)
Run.Blockval no_run : bool Cmdliner.Term.tval reg_types : bool Cmdliner.Term.tval len : int option Cmdliner.Term.tval breakpoints : string list Cmdliner.Term.tval ensure_linear : bool Cmdliner.Term.tval elf : string Cmdliner.Term.tval start : string Cmdliner.Term.tval get_elf_start : string -> string -> Elf.File.t * Elf.SymTable.sym_offsetval elf_term : (Elf.File.t * Elf.SymTable.sym_offset) Cmdliner.Term.tval gen_block : (Elf.File.t * Elf.SymTable.sym_offset) -> int option -> string list -> Elf.File.t * Block_lib.tval elfblock_term : (Elf.File.t * Block_lib.t) Cmdliner.Term.tval prune_paths : (State.Base.t -> bool) -> 'a State.Tree.t -> 'a State.Tree.t optionval has_assert_false : State.t -> boolval run_block : (Elf.File.t * Block_lib.t) -> bool -> bool -> bool -> unitval term : unit Cmdliner.Term.tval info : Cmdliner.Term.infoval command : unit Cmdliner.Term.t * Cmdliner.Term.infoRun.BlockThis 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_offsetval elf_term : (Elf.File.t * Elf.SymTable.sym_offset) Cmdliner.Term.tval gen_block :
+ (Elf.File.t * Elf.SymTable.sym_offset) ->
+ int option ->
+ string list ->
+ Elf.File.t * Block_lib.tval elfblock_term : (Elf.File.t * Block_lib.t) Cmdliner.Term.tval prune_paths :
+ (State.Base.t -> bool) ->
+ 'a State.Tree.t ->
+ 'a State.Tree.t optionval has_assert_false : State.t -> boolval run_block : (Elf.File.t * Block_lib.t) -> bool -> bool -> bool -> unitRun.Block_libtype 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) -> tBuild 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 -> stringval pp_label : label -> Utils.Pp.documentval run : ?every_instruction:bool -> ?relevant:(int, 'a) Stdlib.Hashtbl.t -> t -> State.t -> label State.Tree.tRun 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 optionGeneric end predicate. Will stop if:
minmaxbrksloopRun.Block_libThis 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) ->
+ tBuild 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 = | StartRoot node of the tree
*)| End of stringLead node of the tree, the string describe which end condition has be triggered
*)| BranchAt of Elf.Address.tA Branching node at a given PC
*)| NormalAt of Elf.Address.tA 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 -> stringval pp_label : label -> Utils.Pp.documentval run :
+ ?every_instruction:bool ->
+ ?relevant:(Elf.Address.t, 'a) Stdlib.Hashtbl.t ->
+ t ->
+ State.t ->
+ label State.Tree.tRun 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 optionGeneric end predicate. Will stop if:
minmaxbrksloopRun.Funcval 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.tval command : unit Cmdliner.Term.t * Cmdliner.Term.infoRun.Funcval 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.tRun.FuncRDval run_func_rd : string -> string -> string -> string option -> string list -> unitval elf : string Cmdliner.Term.tval func : string Cmdliner.Term.tval objdump_d : string Cmdliner.Term.tval branch_table : string option Cmdliner.Term.tval breakpoints : string list Cmdliner.Term.tval term : unit Cmdliner.Term.tval info : Cmdliner.Term.infoval command : unit Cmdliner.Term.t * Cmdliner.Term.infoRun.FuncRDThis 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.Initval init_state : State.t option Stdlib.refThe initial state
val init : unit -> State.tIntialize this module by calling Isla on Arch.nop to get initial machine state
val state : unit -> State.tReturn the initial state. Compute it if required.
Run.InitThis module handle architecture initialization. For now it trust default isla initialization but this should change soon (TODO)
val init_state : State.t option Stdlib.refThe initial state
val init : unit -> State.tIntialize this module by calling Isla on Arch.nop to get initial machine state
val state : unit -> State.tReturn the initial state. Compute it if required.
Run.Instrval instr : string Cmdliner.Term.tval dump_trace : bool Cmdliner.Term.tval dump_isla : bool Cmdliner.Term.tval no_run : bool Cmdliner.Term.tval isla_run : bool Cmdliner.Term.tval simp_trace : bool Cmdliner.Term.tval simp_state : bool Cmdliner.Term.tval simp : bool Cmdliner.Term.tval reg_types : bool Cmdliner.Term.tval init : bool Cmdliner.Term.tval elf : string option Cmdliner.Term.tval get_instr : Config.Arch.t -> string -> string option -> Utils.BytesSeq.tval instr_term : Utils.BytesSeq.t Cmdliner.Term.tval simp_trace_term : bool Cmdliner.Term.tval simp_state_term : bool Cmdliner.Term.tval get_traces : Utils.BytesSeq.t -> bool -> bool -> tracesval pre_traces_term : traces Cmdliner.Term.tval simp_traces : bool -> traces -> tracesval dump_traces : bool -> traces -> tracesval traces_term : traces Cmdliner.Term.tval run_instr : bool -> bool -> bool -> traces -> unitval term : unit Cmdliner.Term.tval info : Cmdliner.Term.infoval command : unit Cmdliner.Term.t * Cmdliner.Term.infoRun.InstrThis 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.tval instr_term : Utils.BytesSeq.t Cmdliner.Term.tval get_traces : 'a -> 'b -> 'c -> tracesval pre_traces_term : traces Cmdliner.Term.tval traces_term : traces Cmdliner.Term.tval run_instr : bool -> bool -> bool -> traces -> unitRun.ReadDwarfval dry_run : unit Cmdliner.Term.tval skylight : unit Cmdliner.Term.tval comp_dir : unit Cmdliner.Term.tval clip_binary : unit Cmdliner.Term.tval no_vars : unit Cmdliner.Term.tval no_cfa : unit Cmdliner.Term.tval no_source : unit Cmdliner.Term.tval objdump_d : unit Cmdliner.Term.tval branch_tables : unit Cmdliner.Term.tval elf : unit Cmdliner.Term.tval objdump_d2 : unit Cmdliner.Term.tval branch_tables2 : unit Cmdliner.Term.tval elf2 : unit Cmdliner.Term.tval qemu_log : unit Cmdliner.Term.tval out_file : unit Cmdliner.Term.tval out_dir : unit Cmdliner.Term.tval cfg_dot_file : unit Cmdliner.Term.tval cfg_source_nodes : unit Cmdliner.Term.tval cfg_source_nodes2 : unit Cmdliner.Term.tval html : unit Cmdliner.Term.tval options : unit Cmdliner.Term.t listval full_term : unit Cmdliner.Term.tval info : Cmdliner.Term.infoval command : unit Cmdliner.Term.t * Cmdliner.Term.infoRun.ReadDwarfThis 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.Runnermodule Reg = State.Regtype 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 -> tval of_dwarf : Dw.t -> tval load_sym : t -> Elf.Symbol.t -> unitLoad 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 listRun 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 listval run : ?prelock:(State.t -> unit) -> t -> State.t -> State.t listDo 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.tReturn 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 optionReturn the Instr.t data of the instruction at address, and None if the instruction was invalid
val pp_slot : slot -> PPrintEngine.documentPretty prints a instruction slot
Run.RunnerThis 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.Regtype slot = | Normal of Trace.Instr.tThe traces and the size of the instruction
*)| Special of intSpecial instructions. Will be used to represent external events
*)| NocodeThe is no code at this address. Running it is UB. Also used if an address is in between instructions
*)| IslaFail of intThis 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 -> tval load_sym : t -> Elf.Symbol.t -> unitLoad a symbol into the runner. All instruction traces are fetched and cached.
TODO support variable length instructions.
val fetch : t -> Elf.Address.t -> slotFetch 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 listRun 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.tReturn 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 optionReturn the Instr.t data of the instruction at address, and None if the instruction was invalid
val pp_slot : slot -> Utils.Pp.documentPretty prints a instruction slot
val pp_instr : t -> Utils.Pp.documentDump instruction table
Runmodule BB : sig ... endmodule Bb_lib : sig ... endmodule Block : sig ... endmodule Block_lib : sig ... endmodule Func : sig ... endmodule FuncRD : sig ... endmodule Init : sig ... endmodule Instr : sig ... endmodule ReadDwarf : sig ... endmodule Runner : sig ... endRunmodule BB : sig ... endThis module allow to do a test run of all the machinery for a single basic block
module Bb_lib : sig ... endThis module provide code to manipulate basic block and run them.
module Block : sig ... endThis module add the run-block sub command.
module Block_lib : sig ... endThis module provides a representation of a complex block of code.
module Func : sig ... endmodule FuncRD : sig ... endThis 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 ... endThis module handle architecture initialization. For now it trust default isla initialization but this should change soon (TODO)
module Instr : sig ... endThis module add the run-instr sub command.
module ReadDwarf : sig ... endThis module implements the rd subcommand. This was to original meaning of "read-dwarf", and the first command.
module RelProg : sig ... endmodule Runner : sig ... endThis module provide the program runner that caches all the information required to make a state transition
module TestRelProg : sig ... endSigThis 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 arrayThe map of dwarf register: Which register number map to which ISA register
val supports : Config.Arch.t -> boolTells if this Arch module supports this architecture
val init : Config.Arch.t -> unitIf this arch module supports the architecture, then initialize read-dwarf state using this architecture
val initialized : unit -> Config.Arch.t optionReturn Some(arch) is the loaded arch is arch and None if nothing is loaded yet.
val module_name : stringThe name of the arch module. Must be the name of the module i.e. Config.arch_module
val loaded_name : stringFor dynamic arch module, the name of the dynamically loaded module. Otherwise module_name
val dwarf_reg_map : unit -> dwarf_reg_mapGet the register map of the architecture
val is_local : State.Reg.t -> boolTell if a register is local for the ABI
val nop : unit -> Utils.BytesSeq.tGive the opcode of the nop instruction (For Sail/Isla initialisation
val pc : unit -> State.Reg.tGive the register index for the program counter
val sp : unit -> State.Reg.tGive the register index for the stack pointer
val assemble_to_elf : string -> stringTake 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 listSplit a byte-sequence into a list of instructions.
val is_ret : Utils.BytesSeq.t -> boolTell if an instruction is a return instruction.
val is_cmp : Utils.BytesSeq.t -> (State.Reg.t * Utils.BitVec.t) optionTell 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 optionTell 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.
SigThis 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 arrayThe map of dwarf register: Which register number map to which ISA register
val supports : Config.Arch.t -> boolTells if this Arch module supports this architecture
val init : Config.Arch.t -> unitIf this arch module supports the architecture, then initialize read-dwarf state using this architecture
val initialized : unit -> Config.Arch.t optionReturn 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_mapGet the register map of the architecture
val is_local : State.Reg.t -> boolTell if a register is local for the ABI
val nop : unit -> Utils.BytesSeq.tGive the opcode of the nop instruction (For Sail/Isla initialisation
val pc : unit -> State.Reg.tGive the register index for the program counter
val sp : unit -> State.Reg.tGive 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 listSplit a byte-sequence into a list of instructions.
val is_ret : Utils.BytesSeq.t -> boolTell if an instruction is a return instruction.
val is_cmp : Utils.BytesSeq.t -> (State.Reg.t * Utils.BitVec.t) optionTell 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 optionTell 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.MemReltype block = State.Mem.Fragment.Block.tval of_int : int -> ('a, 'b) ExTy.tval eq_block : block -> block -> ('a, 'b) ExTy.t * (State__Base.Mem.Fragment.var, Ast.no) ExTy.ttype event = State.Mem.Fragment.Event.ttype trace = event listval eq : trace -> trace -> (State__Base.Mem.Fragment.var, Ast.no) ExTy.t listFor now, just assume an equality relation for memory
val eq : State.t -> State.t -> (State__Base.Mem.Fragment.var, Ast.no) ExTy.t listBase.MemReltype block = State.Mem.Fragment.Block.tval of_int : int -> ('a, 'b) ExTy.ttype event = State.Mem.Fragment.Event.ttype trace = event listBase.RegRelval reg_map_fold : 'a -> ('a -> Reg.Map.reg -> 'b -> 'a) -> 'b Reg.Map.t -> 'aval present : Reg.t Utils.List.t option -> Reg.t -> boolval eq : ?except:Reg.t Utils.List.t -> State.t -> State.t -> (State__Base.Exp.var, Ast.no) ExTy.tThis 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.RegRelval reg_map_fold : 'a -> ('a -> Reg.Map.reg -> 'b -> 'a) -> 'b Reg.Map.t -> 'aval present : Reg.t Utils.List.t option -> Reg.t -> boolval eq :
+ ?except:Reg.t Utils.List.t ->
+ State.t ->
+ State.t ->
+ (State__Base.Var.t, Ast.no) ExTy.tThis 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.Test2val infer_rels : 'a State.Tree.t pair -> relsBase.Test2val infer_rels : 'a State.Tree.t pair -> relsBase.Test3val infer_rels : 'a State.Tree.t pair -> relsBase.Test3val infer_rels : 'a State.Tree.t pair -> relsSimrel.Basemodule Tree = State.Treemodule ExTy = Exp.Typedmodule Tval = State.Tvalmodule Reg = State.Regmodule Simplify = State.Simplifymodule Z3St = State.Simplify.Z3Stmodule Id = State.Idtype tree = Run.Block_lib.label Tree.ttype '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.ttype 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 ... endmodule Test3 : sig ... endmodule MemRel : sig ... endmodule RegRel : sig ... endSimrel.BaseThis module encodes the definition and checking of the simulation relation.
module Tree = State.Treemodule ExTy = Exp.Typedmodule Tval = State.Tvalmodule Reg = State.Regmodule Simplify = State.Simplifymodule Z3St = State.Simplify.Z3Stmodule Id = State.Idtype tree = Run.Block_lib.label Tree.ttype rels = matched listmodule Test2 : sig ... endmodule Test3 : sig ... endmodule MemRel : sig ... endmodule RegRel : sig ... endval assn_to_exp : State.t -> (State.Exp.var, Ast.no) ExTy.tval check_rel : matched list -> boolSimrelmodule Base : sig ... endSimrelmodule Base : sig ... endThis module encodes the definition and checking of the simulation relation.
Base.ExpModule for state expressions
Base.ExpModule for state expressions
include Exp.S with type var = vartype var = varThe type of variable provided in the functor
type t = (var, Ast.no) Exp.Typed.tThe 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.documentPretty print the expression using PpExp
val pp_smt : t -> Utils.Pp.documentPretty print the expression in SMTLIB language
val expect_sym_address : t -> Elf.Address.tval of_section : size:int -> string -> tval of_address : size:int -> Elf.Address.t -> tBase.IdThe 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.IdThe 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.BlockThis 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.BlockThis 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.EventThis module provide the trace of reads and writes to a symbolic fragment.
Fragment.EventThis module provide the trace of reads and writes to a symbolic fragment.
val pp : t -> Utils.Pp.documentMem.Fragmenttype var = Var.tThe type of variables used
module Size = Ast.Sizetype exp = (var, Ast.no) Exp.Typed.tThe type of expressions stored in the fragment
module Block : sig ... endThis module provide the concept of memory block, as used by a fragment.
module Event : sig ... endThis module provide the trace of reads and writes to a symbolic fragment.
val empty : tThe empty memory fragment. Any read will be symbolic
val try_read : t -> Block.t -> exp optionTry 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 optionSame 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 -> tRead 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 -> tMap 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 -> unitIter a function over all contained expression. Expression may appear more or less than anticipated because of various caching.
val check_cache : t -> unitCheck cache integrity. Throw if something is wrong
val is_empty : t -> boolTells if the memory is empty since it's initial base. (No new memory operation were added
Mem.Fragmenttype var = Var.tThe type of variables used
module Size = Ast.Sizetype exp = (var, Ast.no) Exp.Typed.tThe type of expressions stored in the fragment
module Block : sig ... endThis module provide the concept of memory block, as used by a fragment.
module Event : sig ... endThis module provide the trace of reads and writes to a symbolic fragment.
val empty : tThe 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 -> unitCheck cache integrity. Throw if something is wrong
val is_empty : t -> boolTells if the memory is empty since it's initial base. (No new memory operation were added
val pp_raw : t -> Utils.Pp.documentPretty prints the raw internals fragment. TODO: A nice pretty printer
Base.MemThis 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.provenanceAdd a new fragment with the specified base
module Fragment : State__.SymbolicFragment.S with type var = Var.tval get_main : t -> Fragment.tGet the main fragment of memory
Base.MemThis 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.provenanceAdd a new fragment with the specified base
module Fragment : sig ... endval get_main : t -> Fragment.tGet the main fragment of memory
val get_frag : t -> int -> Exp.t * Fragment.tGet fragment
Base.TvalModule 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 -> tval of_var : ?ctyp:Ctype.t -> var -> tval of_reg : ?ctyp:Ctype.t -> id -> Reg.t -> tval map_exp : (exp -> exp) -> t -> tval iter_exp : (exp -> 'a) -> t -> 'aval exp : t -> expval ctyp : t -> Ctype.t optionval equal : t -> t -> boolval pp : t -> PPrintEngine.documentBase.TvalModule for optionally typed state expressions. Those are symbolic values which may or may not have a C type.
val pp : t -> Utils.Pp.documentBase.VarThis 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 -> stringConvert the variable to the string encoding. For parsing infrastructure reason, the encoding must always contain at least one :.
val expect_register : t -> Reg.tExpect a register variable and return the corresponding register. Throw otherwise.
val expect_readvar : t -> intExpect a read variable and return the corresponding index. Throw otherwise.
val of_string : string -> tThe opposite of to_string. Will raise Invalid_argument when the string don't match
val equal : t -> t -> boolval hash : t -> intHashing function for variable. For now it's polymorphic but this may stop at any time
val pp : t -> PPrintEngine.documentBasically to_string in pp mode
val pp_bar : t -> PPrintEngine.documentPretty prints but with bars around
Base.VarThis 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.tThe value of this register in this state
*)| ReadVar of id * int * Ast.Size.tThe 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 intA function argument
*)| RetArgThe address to which the return value should be written. This is used only in certain calling conventions
*)| RetAddrThe return address: The address to which a "return" instruction would jump.
*)| NonDet of int * Ast.Size.tVariable representing non-determinism in the spec. Can only be a bit-vector for now.
*)| Section of stringSymbolic base address of ELF section. Assume 64bit for now.
*)The type of a variable in the state
val to_string : t -> stringConvert 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 -> intExpect a read variable and return the corresponding index. Throw otherwise.
val of_string : string -> tThe opposite of to_string. Will raise Invalid_argument when the string don't match
val hash : t -> intHashing function for variable. For now it's polymorphic but this may stop at any time
val pp : t -> Utils.Pp.documentBasically to_string in pp mode
val pp_bar : t -> Utils.Pp.documentPretty prints but with bars around
val new_nondet : Ast.Size.t -> tGet a fresh NonDet variable
State.BaseThis 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 ... endThe 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.tmodule Var : sig ... endThis 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.tThe type of variables
module Exp : sig ... endModule for state expressions
type exp = Exp.tmodule Tval : sig ... endModule for optionally typed state expressions. Those are symbolic values which may or may not have a C type.
type tval = Tval.tmodule Mem : sig ... endThis 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 copying 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.tGlobal map of states to associate them with identifiers
val next_id : id Stdlib.refNext unused id
val lock : t -> unitLock the state. Once a state is locked it should not be mutated anymore
val unsafe_unlock : t -> unitUnlock 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 -> boolTell if the state is locked, in which case it shouldn't be mutated
val is_possible : t -> boolTell 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 -> tMakes 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 -> tDo 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 -> tCopy the state with copy if and only if it is locked. The returned state is always unlocked
val set_impossible : t -> unitSet a state to be impossible (single false assert).
val make_read : t -> ?ctyp:Ctype.t -> Ast.Size.t -> varCreate a new Var.t.ReadVar by mutating the state
val set_read : t -> int -> exp -> unitSet a Var.t.ReadVar to a specific value in t.read_vars
val read_from_rodata : t -> addr:exp -> size:Ast.Size.t -> exp optionRead memory from rodata
val read : provenance:Ctype.provenance -> ?ctyp:Ctype.t -> t -> addr:exp -> size:Ast.Size.t -> expRead 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 -> expA 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 -> unitWrite the provided value in the block. Mutate the state.
val write_noprov : t -> addr:exp -> size:Ast.Size.t -> exp -> unitA 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 -> unitReset 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 -> unitSet the PC to a concrete value and keep its type appropriate
val bump_pc : pc:Reg.t -> t -> int -> unitBump a concrete PC by a concrete bump (generally the size of a non-branching instruction
val set_last_pc : t -> int -> unitSet the last_pc of the state
State.BaseThis 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 ... endThe 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.tmodule Var : sig ... endThis 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.tThe type of variables
module Exp : sig ... endModule for state expressions
type exp = Exp.tmodule Tval : sig ... endModule for optionally typed state expressions. Those are symbolic values which may or may not have a C type.
type tval = Tval.tmodule Relocation : sig ... endmodule Mem : sig ... endThis 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 copying 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.tGlobal map of states to associate them with identifiers
val next_id : id Stdlib.refNext unused id
val lock : t -> unitLock the state. Once a state is locked it should not be mutated anymore
val unsafe_unlock : t -> unitUnlock 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 -> boolTell if the state is locked, in which case it shouldn't be mutated
val is_possible : t -> boolTell 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 -> tMakes 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 -> tDo 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 -> tCopy 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 -> unitSet 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 -> varCreate 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 optionRead memory from rodata
val read :
+ provenance:Ctype.provenance ->
+ ?ctyp:Ctype.t ->
+ t ->
+ addr:exp ->
+ size:Ast.Size.t ->
+ expRead 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 -> expA 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 ->
+ unitWrite the provided value in the block. Mutate the state.
val write_noprov : t -> addr:exp -> size:Ast.Size.t -> exp -> unitA 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 -> unitBump a concrete PC by a concrete bump (generally the size of a non-branching instruction
val set_last_pc : t -> Elf.Address.t -> unitSet the last_pc of the state
val pp : t -> Utils.Pp.documentval pp_partial : regs:Reg.t list -> t -> Utils.Pp.documentPrint 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.Envtype frag = ttype t = {frags : frag Utils.Vec.t; |
}val make : unit -> tval copy : t -> tval add_typ : addr:int -> Ctype.t -> t -> id:int -> unitAdd the provided type at addr into a fragment indexed by id in the environment
Fragment.Envtype frag = tval make : unit -> tAdd 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.documentState.FragmentThis 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.ttype obj_off = obj * intThe type of an object with an offset
type tThe type of the map from address ranges to obj
val empty : tAn empty RngMap
val is_in : objaddr:int -> obj -> int -> boolTest if an address is inside the object at address objaddr
val at : t -> int -> objGet the object containing the address. Throw Not_found if no object contains the address
val at_opt : t -> int -> obj optionGet the object containing the address. None if no object contains the address
val at_off : t -> int -> obj_offGet 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 optionGet 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 -> tUpdate the binding containing the provided address. If no binding contained the address, this is a no-op
val iteri : (int -> obj -> unit) -> t -> unitIter a function over all the objects with their address
val clear : t -> pos:int -> len:int -> tClear 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) -> tClear 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 -> tSame 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 -> tAdd an object at a specific address. The whole range of addresses covered by the object must be free
val addp : t -> obj_off -> tAdd 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.tReturn 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 ... endtype env = Env.tState.FragmentThis 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.ttype obj_off = Ctype.t * intThe type of an object with an offset
val empty : tAn empty RngMap
val is_in : objaddr:int -> Ctype.t -> int -> boolTest 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) ->
+ tClear 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.tReturn 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.documentmodule Env : sig ... endtype env = Env.tReg.MapThis 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 = ttype 'a tThe type of the complete map
val map : ('a -> 'b) -> 'a t -> 'b tMap the function all the registers (including future, not yet added ones)
val map_mut : ('a -> 'a) -> 'a t -> unitMap the function on all the register by mutation (including future ones)
val map_mut_current : ('a -> 'a) -> 'a t -> unitMap the function on all current register. Future registers are unchanged
val iter : ('a -> unit) -> 'a t -> unitIterate over all the value of all currently present registers
Reg.MapThis 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 = tMap the function all the registers (including future, not yet added ones)
val map_mut : ('a -> 'a) -> 'a t -> unitMap the function on all the register by mutation (including future ones)
val map_mut_current : ('a -> 'a) -> 'a t -> unitMap the function on all current register. Future registers are unchanged
val iter : ('a -> unit) -> 'a t -> unitIterate over all the value of all currently present registers
val pp : ('a -> Utils.Pp.document) -> 'a t -> Utils.Pp.documentContrary to Utils.FullVector.pp, this one will print the binding of all registers, and may call the generator to do that
Reg.Pathval to_string : t -> stringPrint the path as dotted list of identifier: ["A";"B";"C"] -> "A.B.C"
val of_string : string -> tParse the path as dotted list of identifier: "A.B.C" -> ["A";"B";"C"]
Reg.Pathval to_string : t -> stringPrint the path as dotted list of identifier: ["A";"B";"C"] -> "A.B.C"
val of_string : string -> tParse the path as dotted list of identifier: "A.B.C" -> ["A";"B";"C"]
val pp : t -> Utils.Pp.documentPretty print the path
State.RegThis 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 ... endGlobal register properties and accessors
type t = private intThe 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.tyThe type of a register. This is isomorphic to Isla.ty. Use IslaConv.ty to convert
val of_int : int -> t optionConvert an integer into the corresponding register.
val mem_path : Path.t -> boolCheck if register is declared with that path
val mem_string : string -> boolCheck if a register is declared with that name. Same as Path.of_string |> mem_path
val of_string : string -> tGive the register corresponding to a register name
val to_string : t -> stringGive the name of a register
val path_type : Path.t -> tyGive the type of register path. Throw Not_found, it that path is not a declared register
val ensure_add : Path.t -> ty -> tEnsure 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 -> unitEnsure 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.documentPrints 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.RegThis 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 ... endGlobal 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 optionConvert an integer into the corresponding register.
val mem_path : Path.t -> boolCheck 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 -> tGive the register corresponding to a register name
val to_string : t -> stringGive 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.tReturns a sequence of all registers
val pp : t -> Utils.Pp.documentPretty prints a register (Just use to_string)
val pp_ty : ty -> Utils.Pp.documentPretty prints a register type
val pp_index : unit -> Utils.Pp.documentPrints 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 ... endThis 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.ContextFullDo 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.tval openc : unit -> unitval num : unit -> intval closec : unit -> unitSimplify.ContextFullDo 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.Htbltype key = vartype 'a t = 'a Z3.Make(State__.Base.Var).Htbl.tval create : int -> 'a tval clear : 'a t -> unitval reset : 'a t -> unitval copy : 'a t -> 'a tval add : 'a t -> key -> 'a -> unitval remove : 'a t -> key -> unitval find : 'a t -> key -> 'aval find_opt : 'a t -> key -> 'a optionval find_all : 'a t -> key -> 'a listval replace : 'a t -> key -> 'a -> unitval mem : 'a t -> key -> boolval iter : (key -> 'a -> unit) -> 'a t -> unitval filter_map_inplace : (key -> 'a -> 'a option) -> 'a t -> unitval fold : (key -> 'a -> 'b -> 'b) -> 'a t -> 'b -> 'bval length : 'a t -> intval stats : 'a t -> Stdlib.Hashtbl.statisticsval to_seq : 'a t -> (key * 'a) Stdlib.Seq.tval to_seq_keys : 'a t -> key Stdlib.Seq.tval to_seq_values : 'a t -> 'a Stdlib.Seq.tval add_seq : 'a t -> (key * 'a) Stdlib.Seq.t -> unitval replace_seq : 'a t -> (key * 'a) Stdlib.Seq.t -> unitval of_seq : (key * 'a) Stdlib.Seq.t -> 'a tZ3St.Htbltype key = varval create : int -> 'a tval clear : 'a t -> unitval reset : 'a t -> unitval length : 'a t -> intval stats : 'a t -> Stdlib__Hashtbl.statisticsval to_seq_values : 'a t -> 'a Stdlib.Seq.tSimplify.Z3Sttype var = Base.Var.ttype exp = (var, Ast.no) Exp.Typed.tmodule Htbl : sig ... endval declare_var_always : Z3.server -> var -> unitval declare_var : Z3.server -> declared:unit Htbl.t -> var -> unitval declare_vars : Z3.server -> declared:unit Htbl.t -> exp -> unitval simplify : Z3.server -> exp -> expval simplify_decl : Z3.server -> declared:unit Htbl.t -> exp -> expval send_assert : Z3.server -> exp -> unitval send_assert_decl : Z3.server -> declared:unit Htbl.t -> exp -> unitval check : Z3.server -> exp -> bool optionval check_sat : Z3.server -> exp -> bool optionval check_both : Z3.server -> exp -> bool optionval simplify_full : exp -> expval check_full : ?hyps:exp list -> exp -> bool optionval check_sat_full : exp list -> bool optionSimplify.Z3Sttype var = Base.Var.ttype exp = (var, Ast.no) Exp.Typed.tmodule Htbl : sig ... endval check_sat_full : exp list -> bool optionState.SimplifyThis module provide utility to simplify states
module Z3St : sig ... endval ctxfree : Base.t -> unitContext free simplify, just expression by expression. Do it mutably
module ContextFull : sig ... endDo 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 -> unitDo 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.SimplifyThis module provide utility to simplify states
module Z3St : sig ... endval ctxfree : Base.t -> unitContext free simplify, just expression by expression. Do it mutably
module ContextFull : sig ... endDo 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 -> unitDo 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-VarMake.Varval pp : t -> Utils.Pp.documentPretty printer to be used, both for memory pretty printing and for sending memory to Z3
SymbolicBytes.Maketype var = Var.tThe type of variables used
type exp = (var, Ast.no) Exp.Typed.ttype tThe type of symbolic bytes.
val empty : tThe empty symbolic byte sequence with no bytes defined
val sub : pos:int -> len:int -> t -> exp optionExtract 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 -> tWrite 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.Maketype var = Var.tThe type of variables used
type exp = (var, Ast.no) Exp.Typed.tval empty : tThe 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.documentPretty prints the symbolic bytes
State.SymbolicBytesThis 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 ... endState.SymbolicBytesThis 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.Stype exp = (var, Ast.no) Exp.Typed.ttype tThe type of symbolic bytes.
val empty : tThe empty symbolic byte sequence with no bytes defined
val sub : pos:int -> len:int -> t -> exp optionExtract 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 -> tWrite 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.Stype exp = (var, Ast.no) Exp.Typed.tval empty : tThe 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.documentPretty prints the symbolic bytes
State.TreeThis module provides a tree of state to represent an unmerged execution
val bars : stringval bars_length : intval startbar : int -> Utils.Pp.documentval prefix_iter : ('a -> Base.t -> unit) -> 'a t -> unitval postfix_iter : ('a -> Base.t -> unit) -> 'a t -> unitval iter : ('a -> Base.t -> unit) -> 'a t -> unitDefault iter when you don't care about order
State.TreeThis module provides a tree of state to represent an unmerged execution
val startbar : int -> Utils.Pp.documentThis is prefix, do a List.rev to get a postfix version
val pp : ('a -> Utils.Pp.document) -> 'a t -> Utils.Pp.documentval pp_all : ('a -> Utils.Pp.document) -> 'a t -> Utils.Pp.documentStateinclude Basemodule Id = Base.IdThe 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.tmodule Var = Base.VarThis 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.tThe type of variables
module Exp = Base.ExpModule for state expressions
type exp = Exp.tmodule Tval = Base.TvalModule for optionally typed state expressions. Those are symbolic values which may or may not have a C type.
type tval = Tval.tmodule Mem = Base.MemThis 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 copying 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.tGlobal map of states to associate them with identifiers
val next_id : id Stdlib.refNext unused id
val lock : t -> unitLock the state. Once a state is locked it should not be mutated anymore
val unsafe_unlock : t -> unitUnlock 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 -> boolTell if the state is locked, in which case it shouldn't be mutated
val is_possible : t -> boolTell 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 -> tMakes 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 -> tDo 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 -> tCopy the state with copy if and only if it is locked. The returned state is always unlocked
val set_impossible : t -> unitSet a state to be impossible (single false assert).
val make_read : t -> ?ctyp:Ctype.t -> Ast.Size.t -> varCreate a new Var.t.ReadVar by mutating the state
val set_read : t -> int -> exp -> unitSet a Var.t.ReadVar to a specific value in t.read_vars
val read_from_rodata : t -> addr:exp -> size:Ast.Size.t -> exp optionRead memory from rodata
val read : provenance:Ctype.provenance -> ?ctyp:Ctype.t -> t -> addr:exp -> size:Ast.Size.t -> expRead 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 -> expA 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 -> unitWrite the provided value in the block. Mutate the state.
val write_noprov : t -> addr:exp -> size:Ast.Size.t -> exp -> unitA 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 -> unitReset 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 -> unitSet the PC to a concrete value and keep its type appropriate
val bump_pc : pc:Reg.t -> t -> int -> unitBump a concrete PC by a concrete bump (generally the size of a non-branching instruction
val set_last_pc : t -> int -> unitSet the last_pc of the state
module Base : sig ... endThis module introduce a type to represent the state of the machine.
module Fragment : sig ... endThis module define a memory fragment to be used by C types
module Reg : sig ... endThis module handle the register abstraction.
module Simplify : sig ... endThis module provide utility to simplify states
module SymbolicBytes : sig ... endThis module provide high-level support for a symbolic array of bytes.
module SymbolicFragement = State__.SymbolicFragmentmodule Tree : sig ... endThis module provides a tree of state to represent an unmerged execution
Stateinclude module type of struct include Base endmodule Id = Base.IdThe 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.tmodule Var = Base.VarThis 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.tThe type of variables
module Exp = Base.ExpModule for state expressions
type exp = Exp.tmodule Tval = Base.TvalModule for optionally typed state expressions. Those are symbolic values which may or may not have a C type.
type tval = Tval.tmodule Relocation = Base.Relocationmodule Mem = Base.MemThis 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 copying 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.tGlobal map of states to associate them with identifiers
val next_id : id Stdlib.refNext unused id
val lock : t -> unitLock the state. Once a state is locked it should not be mutated anymore
val unsafe_unlock : t -> unitUnlock 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 -> boolTell if the state is locked, in which case it shouldn't be mutated
val is_possible : t -> boolTell 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 -> tMakes 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 -> tDo 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 -> tCopy 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 -> unitSet 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 -> varCreate 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 optionRead memory from rodata
val read :
+ provenance:Ctype.provenance ->
+ ?ctyp:Ctype.t ->
+ t ->
+ addr:exp ->
+ size:Ast.Size.t ->
+ expRead 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 -> expA 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 ->
+ unitWrite the provided value in the block. Mutate the state.
val write_noprov : t -> addr:exp -> size:Ast.Size.t -> exp -> unitA 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 -> unitBump a concrete PC by a concrete bump (generally the size of a non-branching instruction
val set_last_pc : t -> Elf.Address.t -> unitSet the last_pc of the state
val pp : t -> Utils.Pp.documentval pp_partial : regs:Reg.t list -> t -> Utils.Pp.documentPrint 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 ... endThis module introduce a type to represent the state of the machine.
module Fragment : sig ... endThis module define a memory fragment to be used by C types
module Reg : sig ... endThis module handle the register abstraction.
module Simplify : sig ... endThis module provide utility to simplify states
module SymbolicBytes : sig ... endThis module provide high-level support for a symbolic array of bytes.
module SymbolicFragement : sig ... endThis 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 ... endThis 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.BytesSeqTval has_even_len : string -> boolval hex_digit : char Tests.Common.Q.arbitraryval hex_string : string Tests.Common.Q.arbitraryval is_hex : char -> boolval well_formed : Tests.Common.QCT.tval odd_len : Tests.Common.QCT.tval not_hex : Tests.Common.QCT.tval tests : Tests.Common.QCT.t listTests.BytesSeqTCommon.Geninclude Q.Genval return : 'a -> 'a tval pure : 'a -> 'a tval (>>=) : 'a t -> ('a -> 'b t) -> 'b tval (<*>) : ('a -> 'b) t -> 'a t -> 'b tval map : ('a -> 'b) -> 'a t -> 'b tval map2 : ('a -> 'b -> 'c) -> 'a t -> 'b t -> 'c tval map3 : ('a -> 'b -> 'c -> 'd) -> 'a t -> 'b t -> 'c t -> 'd tval map_keep_input : ('a -> 'b) -> 'a t -> ('a * 'b) tval (>|=) : 'a t -> ('a -> 'b) -> 'b tval (<$>) : ('a -> 'b) -> 'a t -> 'b tval oneof : 'a t list -> 'a tval oneofl : 'a list -> 'a tval oneofa : 'a array -> 'a tval frequency : (int * 'a t) list -> 'a tval frequencyl : (int * 'a) list -> 'a tval frequencya : (int * 'a) array -> 'a tval shuffle_a : 'a array -> unit tval shuffle_l : 'a list -> 'a list tval shuffle_w_l : (int * 'a) list -> 'a list tval unit : unit tval bool : bool tval float : float tval pfloat : float tval nfloat : float tval float_bound_inclusive : float -> float tval float_bound_exclusive : float -> float tval float_range : float -> float -> float tval (--.) : float -> float -> float tval nat : int tval big_nat : int tval neg_int : int tval pint : int tval int : int tval small_nat : int tval small_int : int tval small_signed_int : int tval int_bound : int -> int tval int_range : int -> int -> int tval graft_corners : 'a t -> 'a list -> unit -> 'a tval int_pos_corners : int listval int_corners : int listval (--) : int -> int -> int tval ui32 : int32 tval ui64 : int64 tval list : 'a t -> 'a list tval list_size : int t -> 'a t -> 'a list tval list_repeat : int -> 'a t -> 'a list tval array : 'a t -> 'a array tval array_size : int t -> 'a t -> 'a array tval array_repeat : int -> 'a t -> 'a array tval opt : 'a t -> 'a option tval pair : 'a t -> 'b t -> ('a * 'b) tval triple : 'a t -> 'b t -> 'c t -> ('a * 'b * 'c) tval quad : 'a t -> 'b t -> 'c t -> 'd t -> ('a * 'b * 'c * 'd) tval char : char tval printable : char tval numeral : char tval char_range : char -> char -> char tval string_size : ?gen:char t -> int t -> string tval string : ?gen:char t -> string tval string_of : char t -> string tval string_readable : string tval small_string : ?gen:char t -> string tval small_list : 'a t -> 'a list tval flatten_l : 'a t list -> 'a list tval flatten_a : 'a t array -> 'a array tval flatten_opt : 'a t option -> 'a option tval flatten_res : ('a t, 'e) Stdlib.result -> ('a, 'e) Stdlib.result tval small_array : 'a t -> 'a array tval join : 'a t t -> 'a tval sized : 'a sized -> 'a tval sized_size : int t -> 'a sized -> 'a tval fix : (('a -> 'b t) -> 'a -> 'b t) -> 'a -> 'b tval generate : ?rand:Stdlib.Random.State.t -> n:int -> 'a t -> 'a listval generate1 : ?rand:Stdlib.Random.State.t -> 'a t -> 'aCommon.Geninclude module type of struct include Q.Gen endval return : 'a -> 'a tval pure : 'a -> 'a tval oneofl : 'a list -> 'a tval oneofa : 'a array -> 'a tval frequencyl : (int * 'a) list -> 'a tval frequencya : (int * 'a) array -> 'a tval shuffle_a : 'a array -> unit tval shuffle_l : 'a list -> 'a list tval shuffle_w_l : (int * 'a) list -> 'a list tval range_subset : size:int -> int -> int -> int array tval array_subset : int -> 'a array -> 'a array tval unit : unit tval bool : bool tval float : float tval pfloat : float tval nfloat : float tval float_bound_inclusive : float -> float tval float_bound_exclusive : float -> float tval float_range : float -> float -> float tval (--.) : float -> float -> float tval exponential : float -> float tval nat : int tval big_nat : int tval neg_int : int tval pint : int tval int : int tval small_nat : int tval small_int : int tval small_signed_int : int tval int_bound : int -> int tval int_range : int -> int -> int tval (--) : int -> int -> int tval int32 : int32 tval int64 : int64 tval ui32 : int32 tval ui64 : int64 tval char : char tval printable : char tval numeral : char tval char_range : char -> char -> char tval bytes_printable : bytes tval bytes_small : bytes tval string_readable : string tval string_printable : string tval string_small : string tval nat_split2 : int -> (int * int) tval pos_split2 : int -> (int * int) tval nat_split : size:int -> int -> int array tval pos_split : size:int -> int -> int array tval generate : ?rand:Stdlib.Random.State.t -> n:int -> 'a t -> 'a listval generate1 : ?rand:Stdlib.Random.State.t -> 'a t -> 'aTests.Commonmodule Q = QCheckThis module provide all the common testing infrastructure. It is intended to be opened in all testing modules
module Gen : sig ... endmodule QCT = Q.Testmodule Print = Q.PrintTests.CommonThis module provide all the common testing infrastructure. It is intended to be opened in all testing modules
module Gen : sig ... endTests.ConcreteEvalTmodule ConcreteEval = Exp.ConcreteEvalmodule Value = Exp.Valuemodule Typed = Exp.Typedval const_exp_gen_ty : int -> Ast.no Ast.ty -> ('a, Ast.no) ExpGen.Gen.exp Tests.Common.Gen.tval const_exp_gen : int -> ('a, Ast.no) ExpGen.Gen.exp Tests.Common.Gen.tval const_exp_shrinker_top : ('a, 'b, 'c, 'd) Ast.Base.exp -> (('a, 'b, 'c, 'd) Ast.Base.exp -> unit) -> unitShrink by trying all sub expressions
val const_exp_shrinker_bot : ('a, 'b, Ast.no, Ast.no) Ast.exp -> (('c, 'd) Exp.Typed.t -> unit) -> unitShrink 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) -> unitShrink by using both const_exp_shrinker_top and const_exp_shrinker_bot
val const_exp : ExpGen.ExpT.t Tests.Common.Q.arbitraryval concrete_eval : Tests.Common.QCT.tval tests : Tests.Common.QCT.t listTests.ConcreteEvalTThis module is for testing ConcreteEval
module ConcreteEval = Exp.ConcreteEvalmodule Value = Exp.Valuemodule Typed = Exp.Typedval const_exp_gen_ty :
+ int ->
+ Ast.no Ast.ty ->
+ ('a, Ast.no) ExpGen.Gen.exp Common.Gen.tval const_exp_gen : int -> ('a, Ast.no) ExpGen.Gen.exp Common.Gen.tval const_exp_shrinker_top :
+ ('a, 'b, 'c, 'd) Ast.Base.exp ->
+ (('a, 'b, 'c, 'd) Ast.Base.exp -> unit) ->
+ unitShrink by trying all sub expressions
val const_exp_shrinker_bot :
+ ('a, 'b, Ast.no, Ast.no) Ast.exp ->
+ (('c, 'd) Exp.Typed.t -> unit) ->
+ unitShrink 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) ->
+ unitShrink by using both const_exp_shrinker_top and const_exp_shrinker_bot
val const_exp : ExpGen.ExpT.t Tests.Common.Q.arbitraryExpGen.ExpTtype var = Var.ttype t = (var, Ast.no) Exp.Typed.tExpGen.ExpTtype var = Var.ttype t = (var, Ast.no) Exp.Typed.tval pp : t -> Utils.Pp.documentval pp_smt : t -> Utils.Pp.documentExpGen.Gentype ('v, 'm) exp = ('v, 'm) Exp.Typed.tval typ : 'a Ast.ty Tests.Common.Gen.tval var_from_ty : 'a -> (int * 'a) Tests.Common.Gen.tval bv_var : int -> (int * 'a Ast.ty) Tests.Common.Gen.tval bool_var : (int * 'a Ast.ty) Tests.Common.Gen.tval bitvec_size : Utils.BitVec.t Tests.Common.Gen.sizedval bitvec : Utils.BitVec.t Tests.Common.Gen.ttype ('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.tGenerate an atom according to the params
val unop : 'a Ast.ty -> (Ast.unop * 'b Ast.ty) Tests.Common.Gen.tval binop : 'a Ast.ty -> ('b Ast.binop * 'c Ast.ty * 'c Ast.ty) Tests.Common.Gen.tval manyop : 'a Ast.ty -> (Ast.manyop * 'b Ast.ty Utils.List.t) Tests.Common.Gen.tval exp_from_params : ('a, Ast.no) gen_param -> ('a, Ast.no) exp Tests.Common.Gen.tval bv_consts : int -> ('a, 'b) Exp.Typed.t Tests.Common.Gen.tval bv_atom_from_var : 'v Tests.Common.Gen.sized -> int -> ('v, 'a) Exp.Typed.t Tests.Common.Gen.tval bv_atom_with_var : int -> (int * 'a Ast.ty, 'b) Exp.Typed.t Tests.Common.Gen.tval bool_consts : ('a, 'b) Exp.Typed.t Tests.Common.Gen.tval bool_atom_from_var : 'v Tests.Common.Gen.t -> ('v, 'a) Exp.Typed.t Tests.Common.Gen.tval bool_atom_with_var : (int * 'a Ast.ty, 'b) Exp.Typed.t Tests.Common.Gen.tExpGen.Gentype ('v, 'm) exp = ('v, 'm) Exp.Typed.tval typ : 'a Ast.ty Common.Gen.tval var_from_ty : 'a -> (int * 'a) Common.Gen.tval bv_var : int -> (int * 'a Ast.ty) Common.Gen.tval bool_var : (int * 'a Ast.ty) Common.Gen.tval bitvec_size : Utils.BitVec.t Common.Gen.sizedval bitvec : Utils.BitVec.t Common.Gen.ttype ('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.tGenerate an atom according to the params
val unop : 'a Ast.ty -> (Ast.unop * 'b Ast.ty) Common.Gen.tval binop : 'a Ast.ty -> ('b Ast.binop * 'c Ast.ty * 'c Ast.ty) Common.Gen.tval manyop : 'a Ast.ty -> (Ast.manyop * 'b Ast.ty Utils.List.t) Common.Gen.tval exp_from_params : ('a, Ast.no) gen_param -> ('a, Ast.no) exp Common.Gen.tval bv_consts : int -> ('a, 'b) Exp.Typed.t Common.Gen.tval bv_atom_from_var :
+ 'v Common.Gen.sized ->
+ int ->
+ ('v, 'a) Exp.Typed.t Common.Gen.tval bv_atom_with_var : int -> (int * 'a Ast.ty, 'b) Exp.Typed.t Common.Gen.tval bool_consts : ('a, 'b) Exp.Typed.t Common.Gen.tval bool_atom_from_var : 'v Common.Gen.t -> ('v, 'a) Exp.Typed.t Common.Gen.tval bool_atom_with_var : (int * 'a Ast.ty, 'b) Exp.Typed.t Common.Gen.tExpGen.VarTest variables to instantiate variable dependent functors for testing.
ExpGen.VarTest variables to instantiate variable dependent functors for testing.
val pp : t -> Utils.Pp.documentval of_string : string -> tZ3.Htbltype key = vartype 'a t = 'a Z3.Make(Var).Htbl.tval create : int -> 'a tval clear : 'a t -> unitval reset : 'a t -> unitval copy : 'a t -> 'a tval add : 'a t -> key -> 'a -> unitval remove : 'a t -> key -> unitval find : 'a t -> key -> 'aval find_opt : 'a t -> key -> 'a optionval find_all : 'a t -> key -> 'a listval replace : 'a t -> key -> 'a -> unitval mem : 'a t -> key -> boolval iter : (key -> 'a -> unit) -> 'a t -> unitval filter_map_inplace : (key -> 'a -> 'a option) -> 'a t -> unitval fold : (key -> 'a -> 'b -> 'b) -> 'a t -> 'b -> 'bval length : 'a t -> intval stats : 'a t -> Stdlib.Hashtbl.statisticsval to_seq : 'a t -> (key * 'a) Stdlib.Seq.tval to_seq_keys : 'a t -> key Stdlib.Seq.tval to_seq_values : 'a t -> 'a Stdlib.Seq.tval add_seq : 'a t -> (key * 'a) Stdlib.Seq.t -> unitval replace_seq : 'a t -> (key * 'a) Stdlib.Seq.t -> unitval of_seq : (key * 'a) Stdlib.Seq.t -> 'a tZ3.Htbltype key = varval create : int -> 'a tval clear : 'a t -> unitval reset : 'a t -> unitval length : 'a t -> intval stats : 'a t -> Stdlib__Hashtbl.statisticsval to_seq_values : 'a t -> 'a Stdlib.Seq.tExpGen.Z3type var = Var.ttype exp = (var, Ast.no) Exp.Typed.tmodule Htbl : sig ... endval declare_var_always : Z3.server -> var -> unitval declare_var : Z3.server -> declared:unit Htbl.t -> var -> unitval declare_vars : Z3.server -> declared:unit Htbl.t -> exp -> unitval simplify : Z3.server -> exp -> expval simplify_decl : Z3.server -> declared:unit Htbl.t -> exp -> expval send_assert : Z3.server -> exp -> unitval send_assert_decl : Z3.server -> declared:unit Htbl.t -> exp -> unitval check : Z3.server -> exp -> bool optionval check_sat : Z3.server -> exp -> bool optionval check_both : Z3.server -> exp -> bool optionval simplify_full : exp -> expval check_full : ?hyps:exp list -> exp -> bool optionval check_sat_full : exp list -> bool optionExpGen.Z3type var = Var.ttype exp = (var, Ast.no) Exp.Typed.tmodule Htbl : sig ... endval check_sat_full : exp list -> bool optionTests.ExpGentype ('v, 'm) exp = ('v, 'm) Exp.Typed.tmodule Var : sig ... endTest variables to instantiate variable dependent functors for testing.
module ExpT : sig ... endmodule Z3 : sig ... endmodule Gen : sig ... endval 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) -> unitPropagate 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.arbitraryGenerate an expression arbitrary from a generator (and optionally a shrinker)
Tests.ExpGenThis 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.tmodule Var : sig ... endTest variables to instantiate variable dependent functors for testing.
module ExpT : sig ... endmodule Z3 : sig ... endmodule Gen : sig ... endval shrink_propagate :
+ ('v, 'm) Exp.Typed.t Tests.Common.Q.Shrink.t ->
+ ('v, 'm) Exp.Typed.t ->
+ (('v, 'm) Exp.Typed.t -> unit) ->
+ unitPropagate an existing shrinker, by try it on all descendants of this. The The provided shrinker must preserve the type.
Tests.SimplifyCheckval var_exp_gen_ty : int -> Ast.no Ast.ty -> (int * 'a Ast.ty, Ast.no) ExpGen.Gen.exp Tests.Common.Gen.tval var_exp_gen : int -> (int * 'a Ast.ty, Ast.no) ExpGen.Gen.exp Tests.Common.Gen.tval const_exp_shrinker_top : ('a, 'b, 'c, 'd) Ast.Base.exp -> (('a, 'b, 'c, 'd) Ast.Base.exp -> unit) -> unitShrink 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) -> unitShrink 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) -> unitShrink by using both const_exp_shrinker_top and const_exp_shrinker_bot
val var_exp : ExpGen.ExpT.t Tests.Common.Q.arbitraryval simplify_check : Tests.Common.QCT.tval tests : Tests.Common.QCT.t listTests.SimplifyCheckThis 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.tval var_exp_gen : int -> (int * 'a Ast.ty, Ast.no) ExpGen.Gen.exp Common.Gen.tval const_exp_shrinker_top :
+ ('a, 'b, 'c, 'd) Ast.Base.exp ->
+ (('a, 'b, 'c, 'd) Ast.Base.exp -> unit) ->
+ unitShrink 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) ->
+ unitShrink 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) ->
+ unitShrink by using both const_exp_shrinker_top and const_exp_shrinker_bot
val var_exp : ExpGen.ExpT.t Tests.Common.Q.arbitraryTestsmodule BytesSeqT : sig ... endmodule Common : sig ... endmodule ConcreteEvalT : sig ... endmodule ExpGen : sig ... endmodule SimplifyCheck : sig ... endTestsmodule BytesSeqT : sig ... endmodule Common : sig ... endmodule ConcreteEvalT : sig ... endThis module is for testing ConcreteEval
module ExpGen : sig ... endThis module try to provide generic generators and arbitrary to work with expressions.
module SimplifyCheck : sig ... endThis 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.ExpBase.ExpBase.SimpContextA instance of Z3.ContextCounter.
val counter : Utils.Counter.tval openc : unit -> unitval num : unit -> intval closec : unit -> unitBase.SimpContextA instance of Z3.ContextCounter.
Base.VarThis module contains variable used in traces
module Reg = State.Regtype 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 -> stringConvert the variable to the string encoding. For parsing infractructure reason, the encoding must always contain at least one :.
Base.VarThis module contains variable used in traces
module Reg = State.Regtype t = | Register of Reg.tThe value of the register at the beginning of the trace
*)| Read of int * Ast.Size.tThe result of that memory reading operation
*)| NonDet of int * Ast.Size.tVariable representing non-determinism in the spec
*)| Segment of string * intVariable representing symbolic segment in the opcode
*)A trace variable
val to_string : t -> stringConvert the variable to the string encoding. For parsing infractructure reason, the encoding must always contain at least one :.
val pp : t -> Utils.Pp.documentPretty prints the variable
Base.VarTblval create : int -> 'a tval clear : 'a t -> unitval reset : 'a t -> unitval copy : 'a t -> 'a tval add : 'a t -> key -> 'a -> unitval remove : 'a t -> key -> unitval find : 'a t -> key -> 'aval find_opt : 'a t -> key -> 'a optionval find_all : 'a t -> key -> 'a listval replace : 'a t -> key -> 'a -> unitval mem : 'a t -> key -> boolval iter : (key -> 'a -> unit) -> 'a t -> unitval filter_map_inplace : (key -> 'a -> 'a option) -> 'a t -> unitval fold : (key -> 'a -> 'b -> 'b) -> 'a t -> 'b -> 'bval length : 'a t -> intval stats : 'a t -> Stdlib__hashtbl.statisticsval to_seq : 'a t -> (key * 'a) Stdlib.Seq.tval to_seq_keys : 'a t -> key Stdlib.Seq.tval to_seq_values : 'a t -> 'a Stdlib.Seq.tval add_seq : 'a t -> (key * 'a) Stdlib.Seq.t -> unitval replace_seq : 'a t -> (key * 'a) Stdlib.Seq.t -> unitval of_seq : (key * 'a) Stdlib.Seq.t -> 'a tBase.VarTbltype key = Var.tval create : int -> 'a tval clear : 'a t -> unitval reset : 'a t -> unitval length : 'a t -> intval stats : 'a t -> Stdlib__Hashtbl.statisticsval to_seq_values : 'a t -> 'a Stdlib.Seq.tZ3Tr.Htbltype key = vartype 'a t = 'a Z3.Make(Var).Htbl.tval create : int -> 'a tval clear : 'a t -> unitval reset : 'a t -> unitval copy : 'a t -> 'a tval add : 'a t -> key -> 'a -> unitval remove : 'a t -> key -> unitval find : 'a t -> key -> 'aval find_opt : 'a t -> key -> 'a optionval find_all : 'a t -> key -> 'a listval replace : 'a t -> key -> 'a -> unitval mem : 'a t -> key -> boolval iter : (key -> 'a -> unit) -> 'a t -> unitval filter_map_inplace : (key -> 'a -> 'a option) -> 'a t -> unitval fold : (key -> 'a -> 'b -> 'b) -> 'a t -> 'b -> 'bval length : 'a t -> intval stats : 'a t -> Stdlib.Hashtbl.statisticsval to_seq : 'a t -> (key * 'a) Stdlib.Seq.tval to_seq_keys : 'a t -> key Stdlib.Seq.tval to_seq_values : 'a t -> 'a Stdlib.Seq.tval add_seq : 'a t -> (key * 'a) Stdlib.Seq.t -> unitval replace_seq : 'a t -> (key * 'a) Stdlib.Seq.t -> unitval of_seq : (key * 'a) Stdlib.Seq.t -> 'a tZ3Tr.Htbltype key = varval create : int -> 'a tval clear : 'a t -> unitval reset : 'a t -> unitval length : 'a t -> intval stats : 'a t -> Stdlib__Hashtbl.statisticsval to_seq_values : 'a t -> 'a Stdlib.Seq.tBase.Z3Trtype var = Var.ttype exp = (var, Ast.no) Exp.Typed.tmodule Htbl : sig ... endval declare_var_always : Z3.server -> var -> unitval declare_var : Z3.server -> declared:unit Htbl.t -> var -> unitval declare_vars : Z3.server -> declared:unit Htbl.t -> exp -> unitval simplify : Z3.server -> exp -> expval simplify_decl : Z3.server -> declared:unit Htbl.t -> exp -> expval send_assert : Z3.server -> exp -> unitval send_assert_decl : Z3.server -> declared:unit Htbl.t -> exp -> unitval check : Z3.server -> exp -> bool optionval check_sat : Z3.server -> exp -> bool optionval check_both : Z3.server -> exp -> bool optionval simplify_full : exp -> expval check_full : ?hyps:exp list -> exp -> bool optionval check_sat_full : exp list -> bool optionBase.Z3Trtype var = Var.ttype exp = (var, Ast.no) Exp.Typed.tmodule Htbl : sig ... endval check_sat_full : exp list -> bool optionTrace.BaseThis 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 ... endThis module contains variable used in traces
module ExpPp = Exp.PpA trace expression. No let bindings, no memory operations
module Typed = Exp.Typedmodule Exp : sig ... endtype exp = Exp.ttype event = | WriteReg of {
} | |||
| ReadMem of {
} | |||
| WriteMem of {
} | |||
| Assert of exp |
The event type. See the module description for more details
type t = event listval pp : event list -> PPrintEngine.documentPretty 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 OfIslaErrorThrow 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.tThe context mapping Isla variable numbers to trace expression
val get_var : 'a Utils.HashVector.t -> int -> 'aGet the exression of the variable at the index. Throw OfIslaError if the variable is not bound
val exp_conv_subst : value_context -> Isla.rexp -> expConvert 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 -> expConvert an Isla.valu in a expression
val write_to_valu : 'a Utils.HashVector.t -> Isla.valu -> 'a -> unitWrite 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 optionConvert an isla event to optionally a Trace event, most events are deleted
module SimpContext : sig ... endA instance of Z3.ContextCounter.
module Z3Tr : sig ... endmodule VarTbl : sig ... endTrace.BaseThis 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 ... endThis module contains variable used in traces
module ExpPp = Exp.PpA trace expression. No let bindings, no memory operations
module Typed = Exp.Typedmodule Exp : sig ... endtype exp = Exp.ttype 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 expThe event type. See the module description for more details
type t = event listval pp_exp : ('a, Var.t, Ast.no, Ast.no) Ast.exp -> Utils.Pp.documentPretty print an expression
val pp_event : event -> Utils.Pp.documentPretty print an event
val pp : event list -> Utils.Pp.documentPretty 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.tThe context mapping Isla variable numbers to trace expression
val get_var : 'a Utils.HashVector.t -> int -> 'aGet the exression of the variable at the index. Throw OfIslaError if the variable is not bound
val exp_conv_subst : value_context -> Isla.rexp -> expConvert 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 ->
+ expConvert an Isla.valu in a expression
val write_to_valu : 'a Utils.HashVector.t -> Isla.valu -> 'a -> unitWrite 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 listConvert an isla event to Trace events, most events are deleted
val of_isla : Isla.segment list -> Isla.rtrc -> tTop level function to convert an isla trace to one of this module
module SimpContext : sig ... endA instance of Z3.ContextCounter.
module Z3Tr : sig ... endmodule VarTbl : sig ... endCache.TCtype key = Opcode.ttype value = Traces.ttype epoch = Epoch.ttype t = Utils__Cache.Make(Opcode)(Traces)(Epoch).tCache.TCCache.TracesStore element of type Base.tlist 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 listmodule Reg = State.Regtype regs = (Reg.t, Reg.Path.t * Reg.ty) Stdlib.Hashtbl.tCache.TracesStore element of type Base.tlist 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 listmodule Reg = State.Regtype regs = (Reg.t, Reg.Path.t * Reg.ty) Stdlib.Hashtbl.tval to_file : string -> t -> unitval of_file : string -> tTrace.CacheThis 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.Opcodemodule Epoch = Isla.Cache.Epochmodule TC : sig ... endval cache : TC.t option Stdlib.reftype config = Isla.Cache.configval start : Isla.Cache.config -> unitStart the caching system. Start Isla.Cache too
val get_cache : unit -> TC.tGet the cache and fails if the cache wasn't started
val get_traces : Utils.BytesSeq.t -> Base.t listGet the traces of the opcode given. Use Isla.Server if the value is not in the cache
val get_instr : Utils.BytesSeq.t -> Instr.tGet a full blown Instr from the opcode, going through the whole Isla pipeline if necessary.
Trace.CacheThis 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.Opcodemodule Epoch = Isla.Cache.Epochmodule TC : sig ... endval cache : TC.t option Stdlib.reftype config = Isla.Cache.configval start : Isla.Cache.config -> unitStart the caching system. Start Isla.Cache too
val get_cache : unit -> TC.tGet the cache and fails if the cache wasn't started
val get_traces : Isla.Server.opcode -> Base.t listGet 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.tGet a full blown Instr from the opcode, going through the whole Isla pipeline if necessary.
Trace.ContextThis 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.expExpand a Trace variable to a State expression, using the context
Trace.ContextThis 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 ... endtype 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 ->
+ tBuild a context from a state
val expand_var : ctxt:t -> Base.Var.t -> Ast.no Ast.ty -> State.expExpand a Trace variable to a State expression, using the context
val typing_enabled : ctxt:t -> boolTell if typing should enabled with this context
module Z3St = State.Simplify.Z3StTrace.InstrThis 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.Regtype 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 listval footprint : t -> State.Reg.t listval trace_meta_of_trace : Base.t -> trace_metaCompute the metadata of trace
val of_traces : Utils.BytesSeq.t -> Base.t list -> tGenerate full instruction data from a list of traces
Trace.InstrThis 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.RegA 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 ... endtype 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 listval footprint : t -> State.Reg.t listval trace_meta_of_trace : Base.t -> trace_metaCompute the metadata of trace
val of_traces :
+ (Utils.BytesSeq.t * Elf.Relocations.rel option) ->
+ Base.t list ->
+ tGenerate full instruction data from a list of traces
val pp : t -> Utils.Pp.documentPretty print the representation of an instruction
Trace.RunThis 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 = Contexttype ctxt = Ctxt.tval expand : ctxt:ctxt -> Base.exp -> State.expExpand a Trace expression to a State expression, using the context
val expand_tval : ctxt:ctxt -> Base.exp -> State.tvalExpand 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 -> unitRun the event. The modified state is the one inside ctxt.
val trace_mut : ?dwarf:Dw.t -> State.t -> Base.t -> unitRun a trace on the provided state by mutation. Enable typing if dwarf is provided
Trace.RunThis 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 = Contexttype ctxt = Ctxt.tval expand_tval : ctxt:ctxt -> Base.exp -> State.tvalExpand 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 -> unitRun the event. The modified state is the one inside ctxt.
val trace_mut :
+ ?dwarf:Dw.t ->
+ ?relocation:Elf.Relocations.rel ->
+ State.t ->
+ Base.t ->
+ unitRun 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.tRun 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 ->
+ unitRun 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
Traceinclude Basemodule Var = Base.VarThis module contains variable used in traces
module ExpPp = Exp.PpA trace expression. No let bindings, no memory operations
module Typed = Exp.Typedmodule Exp = Base.Exptype exp = Exp.ttype event = | WriteReg of {
} | |||
| ReadMem of {
} | |||
| WriteMem of {
} | |||
| Assert of exp |
The event type. See the module description for more details
type t = event listval pp : event list -> PPrintEngine.documentPretty 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 OfIslaErrorThrow 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.tThe context mapping Isla variable numbers to trace expression
val get_var : 'a Utils.HashVector.t -> int -> 'aGet the exression of the variable at the index. Throw OfIslaError if the variable is not bound
val exp_conv_subst : value_context -> Isla.rexp -> expConvert 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 -> expConvert an Isla.valu in a expression
val write_to_valu : 'a Utils.HashVector.t -> Isla.valu -> 'a -> unitWrite 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 optionConvert an isla event to optionally a Trace event, most events are deleted
module SimpContext = Base.SimpContextA instance of Z3.ContextCounter.
module Z3Tr = Base.Z3Trmodule VarTbl = Base.VarTblmodule Base : sig ... endThis module defines a new simplified kind of trace to replace Isla traces in the later stages of the instruction semantics processing.
module Cache : sig ... endThis module provides a caching system for fully processed traces
module Context : sig ... endThis module provide the type for a context to run a trace
module Ctype = Ctypemodule Instr : sig ... endThis 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.
Traceinclude module type of struct include Base endmodule Var = Base.VarThis module contains variable used in traces
module ExpPp = Exp.PpA trace expression. No let bindings, no memory operations
module Typed = Exp.Typedmodule Exp = Base.Exptype exp = Exp.ttype 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 expThe event type. See the module description for more details
type t = event listval pp_exp : ('a, Var.t, Ast.no, Ast.no) Ast.exp -> Utils.Pp.documentPretty print an expression
val pp_event : event -> Utils.Pp.documentPretty print an event
val pp : event list -> Utils.Pp.documentPretty 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.tThe context mapping Isla variable numbers to trace expression
val get_var : 'a Utils.HashVector.t -> int -> 'aGet the exression of the variable at the index. Throw OfIslaError if the variable is not bound
val exp_conv_subst : value_context -> Isla.rexp -> expConvert 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 ->
+ expConvert an Isla.valu in a expression
val write_to_valu : 'a Utils.HashVector.t -> Isla.valu -> 'a -> unitWrite 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 listConvert an isla event to Trace events, most events are deleted
val of_isla : Isla.segment list -> Isla.rtrc -> tTop level function to convert an isla trace to one of this module
module SimpContext = Base.SimpContextA instance of Z3.ContextCounter.
module Z3Tr = Base.Z3Trmodule VarTbl = Base.VarTblmodule Base : sig ... endThis module defines a new simplified kind of trace to replace Isla traces in the later stages of the instruction semantics processing.
module Cache : sig ... endThis module provides a caching system for fully processed traces
module Context : sig ... endThis module provide the type for a context to run a trace
module Ctype = Ctypemodule Instr : sig ... endThis 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.ArrayList: extension of Stdlib.ListFun: extension of Stdlib.FunPair: A module to lift operation over pairsSeq: extension of Stdlib.SeqString: extension of Stdlib.StringBits: 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.tIdMap: 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.ArrayList: extension of Stdlib.ListFun: extension of Stdlib.FunPair: A module to lift operation over pairsSeq: extension of Stdlib.SeqString: extension of Stdlib.StringBits: 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.tIdMap: 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.Arrayinclude Stdlib.Arrayval length : 'a array -> intval get : 'a array -> int -> 'aval set : 'a array -> int -> 'a -> unitval make : int -> 'a -> 'a arrayval create : int -> 'a -> 'a arrayval create_float : int -> float arrayval make_float : int -> float arrayval init : int -> (int -> 'a) -> 'a arrayval make_matrix : int -> int -> 'a -> 'a array arrayval create_matrix : int -> int -> 'a -> 'a array arrayval append : 'a array -> 'a array -> 'a arrayval concat : 'a array list -> 'a arrayval sub : 'a array -> int -> int -> 'a arrayval copy : 'a array -> 'a arrayval fill : 'a array -> int -> int -> 'a -> unitval blit : 'a array -> int -> 'a array -> int -> int -> unitval to_list : 'a array -> 'a listval of_list : 'a list -> 'a arrayval iter : ('a -> unit) -> 'a array -> unitval iteri : (int -> 'a -> unit) -> 'a array -> unitval map : ('a -> 'b) -> 'a array -> 'b arrayval mapi : (int -> 'a -> 'b) -> 'a array -> 'b arrayval fold_left : ('a -> 'b -> 'a) -> 'a -> 'b array -> 'aval fold_right : ('b -> 'a -> 'a) -> 'b array -> 'a -> 'aval iter2 : ('a -> 'b -> unit) -> 'a array -> 'b array -> unitval map2 : ('a -> 'b -> 'c) -> 'a array -> 'b array -> 'c arrayval for_all : ('a -> bool) -> 'a array -> boolval exists : ('a -> bool) -> 'a array -> boolval mem : 'a -> 'a array -> boolval memq : 'a -> 'a array -> boolval sort : ('a -> 'a -> int) -> 'a array -> unitval stable_sort : ('a -> 'a -> int) -> 'a array -> unitval fast_sort : ('a -> 'a -> int) -> 'a array -> unitval to_seq : 'a array -> 'a Stdlib.Seq.tval to_seqi : 'a array -> (int * 'a) Stdlib.Seq.tval of_seq : 'a Stdlib.Seq.t -> 'a arrayval of_list_mapi : (int -> 'a -> 'b) -> 'a list -> 'b arrayof_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 arrayof_list_map f l = of_list (List.map f l) = map f (of_list l)
val find_pair : ('a -> bool) -> 'a array -> int * 'aFind 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 -> 'aFind the first value satisfying the predicate. Throw Not_found if no value satisfies the predicate
val find_index : ('a -> bool) -> 'a array -> intFind 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) listFind all the values satisfying the predicate and return them with their index.
val find_all : ('a -> bool) -> 'a t -> 'a listFind all the values satisfying the predicate
val find_all_indices : ('a -> bool) -> 'a t -> int listFind all the indices whose value satisfies the predicate
Utils.ArrayThis module is for extending the Array module of the standard library
include module type of struct include Stdlib.Array endof_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) listFind all the values satisfying the predicate and return them with their index.
val find_all : ('a -> bool) -> 'a t -> 'a listFind all the values satisfying the predicate
val find_all_indices : ('a -> bool) -> 'a t -> int listFind all the indices whose value satisfies the predicate
Utils.BitVecexception SizeMismatch of int * intRaise when the runtime size do not match on operation that require so (like add)
val size : t -> intThe size of the bitvector
val zero : size:int -> tThe bitvector representing 0 of specified size
val one : size:int -> tThe bitvector representing 1 of specified size
val minus_one : size:int -> tThe bitvector representing -1 of specified size
val to_z : t -> Z.tTo a signed big integer
val to_uz : t -> Z.tTo an unsigned big integer
val of_z : size:int -> Z.t -> tOf bit integer. Wrapped modulo 2^size.
val to_int : t -> intTo a signed integer. Fail if it doesn't fit
val to_uint : t -> intTo an unsigned integer. Fail if it doesn't fit without wrapping i.e the result is still positive
val of_int : size:int -> int -> tOf integer. Wrapped modulo size.
val to_bool : t -> boolConvert a one size bitvector to bool. Throw SizeMismatch if the bitvector is not one-sized
val of_bool : bool -> tCreate a one-sized bitvector representing the boolean
val to_bytes : t -> bytesReturn 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 -> bytesReturn 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 -> unitStore 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 -> tRead a bitvector from a bytes data (little endian)
val bytes_load : size:int -> bytes -> int -> tLoad 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 -> tParse 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 -> tSame 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 -> stringConvert 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 -> stringConvert a bitvector to the SMTLib format
val add : t -> t -> tAdd the values. Result is of the same size as the inputs.
Throw SizeMismatch if sizes differ
val sub : t -> t -> tSubtract the values. Result is of the same size as the inputs.
Throw SizeMismatch if sizes differ
val neg : t -> tNegate the value. Wrap if the value is the smaller integer (It will stay the smallest integer)
val mul : t -> t -> tMultiply the values. Result is of the same size as the inputs.
Throw SizeMismatch if sizes differ
val sdiv : t -> t -> tDivide 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 -> tTake 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 -> tTake 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 -> tDivide 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 -> tGet 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 -> tBitwise and of the values. Result is of the same size as the inputs.
Throw SizeMismatch if sizes differ
val logor : t -> t -> tBitwise or of the values. Result is of the same size as the inputs.
Throw SizeMismatch if sizes differ
val logxor : t -> t -> tBitwise xor of the values. Result is of the same size as the inputs.
Throw SizeMismatch if sizes differ
val redor : t -> boolDo an or of all the bits in the bitvector
val redand : t -> boolDo an and of all the bits in the bitvector
val shift_left_bv : t -> t -> tSame as shift_left but the second argument is also a bitvector of any size interpreted as unsigned
val shift_right_arith : t -> int -> tDo an arithmetic right shift (copy the sign bit). The second argument must be non-negative
val shift_right_arith_bv : t -> t -> tSame as shift_right_arith but the second argument is also a bitvector of any size interpreted as unsigned
val shift_right_logic : t -> int -> tDo an logical right shift (insert zeroes). The second argument must be non-negative
val shift_right_logic_bv : t -> t -> tSame 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.BitVecThis 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 -> intThe size of the bitvector
val zero : size:int -> tThe bitvector representing 0 of specified size
val one : size:int -> tThe bitvector representing 1 of specified size
val minus_one : size:int -> tThe bitvector representing -1 of specified size
val to_z : t -> Z.tTo a signed big integer
val to_uz : t -> Z.tTo an unsigned big integer
val of_z : size:int -> Z.t -> tOf bit integer. Wrapped modulo 2^size.
val to_int : t -> intTo a signed integer. Fail if it doesn't fit
val to_uint : t -> intTo an unsigned integer. Fail if it doesn't fit without wrapping i.e the result is still positive
val of_int : size:int -> int -> tOf integer. Wrapped modulo size.
val to_bool : t -> boolConvert a one size bitvector to bool. Throw SizeMismatch if the bitvector is not one-sized
val of_bool : bool -> tCreate a one-sized bitvector representing the boolean
val to_bytes : t -> bytesReturn 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 -> bytesReturn 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 -> unitStore 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 -> tRead a bitvector from a bytes data (little endian)
val bytes_load : size:int -> bytes -> int -> tLoad 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 -> tParse 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 -> tSame 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 ->
+ stringConvert 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 -> stringConvert a bitvector to the SMTLib format
val pp_smt : t -> Pp.documentPrint 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 -> boolDo an or of all the bits in the bitvector
val redand : t -> boolDo 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.Bitsmodule Int = IntBitsval check_index : int -> int -> unitcheck_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 -> unitcheck_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 -> bytesCreate a bytes large enough to store the specified amount of bits and initialize them as specified by the boolean
val unsafe_get : bytes -> int -> boolUnsafe version of get
val get : bytes -> int -> boolGet a bit at a specific index. See unsafe_get
val unsafe_set : bytes -> int -> unitUnsafe version of set
val set : bytes -> int -> unitSet a bit at a specific index. See unsafe_set
val unsafe_clear : bytes -> int -> unitUnsafe version of clear
val clear : bytes -> int -> unitClear a bit at a specific index. See unsafe_clear
val unsafe_setb : bytes -> int -> bool -> unitUnsafe version of setb
val setb : bytes -> int -> bool -> unitSet 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.tUnsafe version of blit_to_int
val blit_to_int : bytes -> int -> Int.t -> int -> int -> Int.tblit_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 -> unitUnsafe version of blit_of_int
val blit_of_int : Int.t -> int -> bytes -> int -> int -> unitblit_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.BitsLike 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 = IntBitscheck_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 -> unitUnsafe version of blit_of_int
val blit_of_int : Int.t -> int -> bytes -> int -> int -> unitblit_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.BytesSeqval length : t -> intGet the length of the byteseq in bytes
val to_hex : t -> stringConvert the byte sequence to an hexadecimal string
val to_hex_rev : t -> stringConvert the byte sequence to an reversed hexadecimal string. This will print it like a big-endian integer.
val of_hex : string -> tParse 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 -> tsub 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 -> unitblit 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 -> tCreate a view of the whole bytes
val of_string : string -> tCreate a view of the whole string as raw bytes
val to_string : t -> stringCreate a copy of the view in a string
val to_array : t -> char arrayConvert to a char array
val of_array : char array -> tConvert from a char array
val get : t -> int -> charGet the bytes at the offset in the byte sequence
val get16le : t -> int -> intGet a 16 bit integer at the offset in the byte sequence as little endian
val get16be : t -> int -> intGet a 16 bit integer at the offset in the byte sequence as big endian
val get32le : t -> int -> int32Get a 32 bit integer at the offset in the byte sequence as little endian
val get32be : t -> int -> int32Get a 32 bit integer at the offset in the byte sequence as big endian
val get64le : t -> int -> int64Get a 64 bit integer at the offset in the byte sequence as little endian
val get64be : t -> int -> int64Get a 64 bit integer at the offset in the byte sequence as big endian
val getintle : t -> int -> intGet 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 -> tGet a byte sequence of length len at the offset in another byte sequence
val getbvle : size:int -> t -> int -> BitVec.tGet a BitVec of size size at the offset in the byte sequence as little endian
val getintle_ze : t -> int -> intGet 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 -> unitval iter16le : (int -> unit) -> t -> unitval iter16be : (int -> unit) -> t -> unitval iter32le : (int32 -> unit) -> t -> unitval iter32be : (int32 -> unit) -> t -> unitval iter64le : (int64 -> unit) -> t -> unitval iter64be : (int64 -> unit) -> t -> unitval iterbs : len:int -> (t -> unit) -> t -> unitIterate 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 -> 'aval fold_left16le : ('a -> int -> 'a) -> 'a -> t -> 'aval fold_left16be : ('a -> int -> 'a) -> 'a -> t -> 'aval fold_left32le : ('a -> int32 -> 'a) -> 'a -> t -> 'aval fold_left32be : ('a -> int32 -> 'a) -> 'a -> t -> 'aval fold_left64le : ('a -> int64 -> 'a) -> 'a -> t -> 'aval fold_left64be : ('a -> int64 -> 'a) -> 'a -> t -> 'aval fold_leftbs : len:int -> ('a -> t -> 'a) -> 'a -> t -> 'aval to_list : t -> char listval to_list16le : t -> int listval to_list16be : t -> int listval to_list32le : t -> int32 listval to_list32be : t -> int32 listval to_list64le : t -> int64 listval to_list64be : t -> int64 listval to_listbs : len:int -> t -> t listCut 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 -> unitOutput the raw data of the byte sequence on the output channel
val input : Stdlib.in_channel -> tOutput the raw date of the byte sequence of the input channel
val pp : t -> Utils.Pp.documentPretty 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.documentPretty 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.documentPretty 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.documentPretty 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.documentPretty 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 bsUtils.BytesSeqThis 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 -> intGet the length of the byteseq in bytes
val to_hex : t -> stringConvert the byte sequence to an hexadecimal string
val to_hex_rev : t -> stringConvert the byte sequence to an reversed hexadecimal string. This will print it like a big-endian integer.
val of_hex : string -> tParse 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 -> unitblit 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 -> tCreate a view of the whole bytes
val of_string : string -> tCreate a view of the whole string as raw bytes
val to_string : t -> stringCreate a copy of the view in a string
val bytes_sub : bytes -> int -> int -> tCreate a byte sequence view of a specified range of a bytes. See sub
val to_array : t -> char arrayConvert to a char array
val of_array : char array -> tConvert from a char array
val get : t -> int -> charGet the bytes at the offset in the byte sequence
val get16le : t -> int -> intGet a 16 bit integer at the offset in the byte sequence as little endian
val get16be : t -> int -> intGet a 16 bit integer at the offset in the byte sequence as big endian
val get32le : t -> int -> int32Get a 32 bit integer at the offset in the byte sequence as little endian
val get32be : t -> int -> int32Get a 32 bit integer at the offset in the byte sequence as big endian
val get64le : t -> int -> int64Get a 64 bit integer at the offset in the byte sequence as little endian
val get64be : t -> int -> int64Get a 64 bit integer at the offset in the byte sequence as big endian
val getintle : t -> int -> intGet 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 -> intGet 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 -> unitval iter16le : (int -> unit) -> t -> unitval iter16be : (int -> unit) -> t -> unitval iter32le : (int32 -> unit) -> t -> unitval iter32be : (int32 -> unit) -> t -> unitval iter64le : (int64 -> unit) -> t -> unitval iter64be : (int64 -> unit) -> t -> unitIterate 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 -> 'aval fold_left16le : ('a -> int -> 'a) -> 'a -> t -> 'aval fold_left16be : ('a -> int -> 'a) -> 'a -> t -> 'aval fold_left32le : ('a -> int32 -> 'a) -> 'a -> t -> 'aval fold_left32be : ('a -> int32 -> 'a) -> 'a -> t -> 'aval fold_left64le : ('a -> int64 -> 'a) -> 'a -> t -> 'aval fold_left64be : ('a -> int64 -> 'a) -> 'a -> t -> 'aval to_list : t -> char listval to_list16le : t -> int listval to_list16be : t -> int listval to_list32le : t -> int32 listval to_list32be : t -> int32 listval to_list64le : t -> int64 listval to_list64be : t -> int64 listCut 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 -> unitOutput the raw data of the byte sequence on the output channel
val input : Stdlib.in_channel -> tOutput the raw date of the byte sequence of the input channel
val pp : t -> Pp.documentPretty 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.documentPretty 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.documentPretty 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.documentPretty 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.documentPretty 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 bsTest.Cachetype key = Key.ttype value = Value.ttype epoch = UnitEpoch.ttype t = Make(Key)(Value)(UnitEpoch).tThe type that represent the cache in RAM.
val make : ?fake:bool -> string -> epoch -> tBuild 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.Cachetype key = Key.ttype value = Value.ttype epoch = UnitEpoch.ttype t = Make(Key)(Value)(UnitEpoch).tThe 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.Singletype value = Value.ttype t = Single(Value).tThe type that represent the cache in RAM.
val make : ?fake:bool -> string -> tBuild 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 -> unitClear the value in the cache
Test.Singletype value = Value.ttype t = Single(Value).tThe type that represent the cache in RAM.
val make : ?fake:bool -> string -> tBuild 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 -> unitClear the value in the cache
Test.Valueval to_file : string -> t -> unitSerialize the value to a file
Test.Valueval to_file : string -> t -> unitSerialize the value to a file
Cmd.TestThis module provide a int -> string cache for testing pruposes. It can be tested with read-dwarf cache --test
module Cache : sig ... endmodule Single : sig ... endCmd.TestThis module provide a int -> string cache for testing pruposes. It can be tested with read-dwarf cache --test
Cache.CmdThe cache command line. TODO documentation of the inside
module Test : sig ... endThis module provide a int -> string cache for testing pruposes. It can be tested with read-dwarf cache --test
val clear : bool Cmdliner.Term.tval all : bool Cmdliner.Term.tval test : bool Cmdliner.Term.tval list : bool Cmdliner.Term.tval arg : string option Cmdliner.Term.tval fake : bool Cmdliner.Term.tval op_f2m : bool -> bool -> bool -> operation Cmdliner.Term.retInput flags to mode conversion
val operation_term : operation Cmdliner.Term.tval test : bool -> unitThe testing mini command line to test the Test cache
val dostuff : operation -> bool -> string option -> bool -> unitDo the caching operation op
Cache.CmdThe cache command line. TODO documentation of the inside
module Test : sig ... endThis 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.retInput flags to mode conversion
val operation_term : operation Cmdliner.Term.tThe testing mini command line to test the Test cache
val dostuff : operation -> bool -> string option -> bool -> unitDo the caching operation op
Cache.IntEpochCache.IntEpochMake.1-Keyval to_file : string -> t -> unitWrite 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 -> tBuild 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.Keyval to_file : string -> t -> unitWrite 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 -> tBuild 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-Valueval to_file : string -> t -> unitSerialize the value to a file
Make.Valueval to_file : string -> t -> unitSerialize the value to a file
Make.3-EpochMake.EpochCache.MakeThe 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.tThe type of keys
type value = Value.tThe type of values
type epoch = Epoch.tThe type of the epoch
val make : ?fake:bool -> string -> epoch -> tBuild 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.MakeThe 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.tThe type of keys
type value = Value.tThe type of values
type epoch = Epoch.tThe 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-Valueval to_file : string -> t -> unitSerialize the value to a file
Single.Valueval to_file : string -> t -> unitSerialize the value to a file
Cache.SingleThe 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.tThe type of the stored value
val make : ?fake:bool -> string -> tBuild 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 -> unitClear the value in the cache
Cache.SingleThe 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.tThe type of the stored value
val make : ?fake:bool -> string -> tBuild 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 -> unitClear the value in the cache
Cache.UnitEpochA dummy epochs implementation, if not epoch is needed
Cache.UnitEpochA dummy epochs implementation, if not epoch is needed
Utils.CacheThis section is not part of the external API, but creating an mli file here seemed needlessly annoying.
val find_dir : unit -> stringFind 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 -> unitRemove a directory and all it's content.
TODO: Make that Windows friendly
TODO move that in Files
val file_type : string -> file_typeGive the file type of the given type. Do not count the number of entry in ther
module type Key = sig ... endThe interface of keys.
module IntKey : Key with type Key.t = intAn implementation of Key on ints where hash is the identity and no file is written
module type Value = sig ... endThe 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 ... endA 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 ... endA dummy epochs implementation, if not epoch is needed
module IntEpoch : sig ... endmodule Cmd : sig ... endThe cache command line. TODO documentation of the inside
Utils.CacheThis 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_typeGive 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 ... endThe interface of keys.
An implementation of Key on ints where hash is the identity and no file is written
module type Value = sig ... endThe 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 ... endmodule UnitEpoch : sig ... endA dummy epochs implementation, if not epoch is needed
module IntEpoch : sig ... endThe functor is to make a single cached value. This do not support epochs (Yet)
module Cmd : sig ... endThe cache command line. TODO documentation of the inside
Cache.EpochA 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.EpochA 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.KeyThe 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 -> unitWrite 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 -> tBuild 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.KeyThe 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 -> unitWrite 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 -> tBuild 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.SThe output signature of Make
val make : ?fake:bool -> string -> epoch -> tBuild 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.SThe 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.SingleSThe signature of the output of Single
val make : ?fake:bool -> string -> tBuild 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 -> unitClear the value in the cache
Cache.SingleSThe signature of the output of Single
val make : ?fake:bool -> string -> tBuild 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 -> unitClear the value in the cache
Cache.ValueThe 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 -> unitSerialize the value to a file
Cache.ValueThe 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 -> unitSerialize the value to a file
Cmd.IOServerThis 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.IOServerThis 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.SocketServerThis 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) -> tStart 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 -> unitStop 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 -> intRead a single byte from the server
val read_string : t -> stringRead 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 -> unitWrite a string in the same binary format as read_string
Cmd.SocketServerThis 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 -> unitStop 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 -> intRead a single byte from the server
val read_string : t -> stringRead 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 -> unitWrite a string in the same binary format as read_string
Utils.Cmdtype cmd = string arrayThe 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_statusIf 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 -> unitCall the command without redirecting anything. Wait for completion before returning.
May throw Crash on error.
val call_send : cmd -> sender:(Stdlib.out_channel -> unit) -> unitCall 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 -> unitCall 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) -> 'aCall 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 -> stringCall 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) -> 'aCall 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 ... endThis 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 ... endThis module provide functionality for a socket server with which one can communicate on a sockets.
Utils.CmdThis 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_statusIf 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 -> unitCall the command without redirecting anything. Wait for completion before returning.
May throw Crash on error.
val call_send : cmd -> sender:(Stdlib.out_channel -> unit) -> unitCall 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 -> unitCall 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) -> 'aCall 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 -> stringCall 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) ->
+ 'aCall 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 ... endThis 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 ... endThis module provide functionality for a socket server with which one can communicate on a sockets.
Utils.CmdlinerHelperval setter : 'a Stdlib.ref -> 'a Cmdliner.Term.t -> unit Cmdliner.Term.tReturn 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.tAdd 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.tFold add_option on a list of option
val func_option : unit Cmdliner.Term.t -> 'a -> 'a Cmdliner.Term.tReplaces 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.tSame as func_option but with a list of unit terms
Utils.CmdlinerHelperThis 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.Counterval make : int -> tMake 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 -> intGet the next value of the counter and increment it
val read : t -> intGet the current value of the counter
val skip : t -> unitSkip a value of the counter equivalent to ingore (get ...)
Utils.CounterThis 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 -> tMake 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 -> intGet the next value of the counter and increment it
val read : t -> intGet the current value of the counter
val skip : t -> unitSkip a value of the counter equivalent to ingore (get ...)
Utils.Filestype 'a reader = Stdlib.in_channel -> 'aThe type of a reader that read an object from a channel. Function like input_* in Stdlib
type 'a writer = Stdlib.out_channel -> 'a -> unitThe type of a writer of 'a. Function named output_* in Stdlib
val input_sexp : Stdlib.in_channel -> stringReads 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 listTry 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 arrayTry 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 -> unitOutput all the element of the list in order with the provided writer
val read : 'a reader -> string -> 'aTake a reader and a file and read an object from the file using the reader. Text mode
val read_bin : 'a reader -> string -> 'aTake a reader and a file and read an object from the file using the reader. Binary mode
val write : 'a writer -> string -> 'a -> unitTake a writer a file and object and write the object to the file using writer. Text mode
val write_bin : 'a writer -> string -> 'a -> unitTake a writer fs a file and object and write the object to the file using writer. Binary mode
Utils.FilesThis 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 listTry 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 arrayTry 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 -> unitOutput all the element of the list in order with the provided writer
val read : 'a reader -> string -> 'aTake a reader and a file and read an object from the file using the reader. Text mode
val read_bin : 'a reader -> string -> 'aTake a reader and a file and read an object from the file using the reader. Binary mode
val write : 'a writer -> string -> 'a -> unitTake a writer a file and object and write the object to the file using writer. Text mode
val write_bin : 'a writer -> string -> 'a -> unitTake 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.FullVecval make : (int -> 'a) -> 'a tCreate a full vector from a generator
val set : 'a t -> int -> 'a -> unitSet the binding of that integer
val set_after : 'a t -> int -> (int -> 'a) -> unitSet 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 -> 'aGet the binding of that integer
val get_vec_until : 'a t -> int -> 'a Vec.tGet a vector containing at least the elements until the specified value excluded
val map : ('a -> 'b) -> 'a t -> 'b tMap the function over the fullvec. Postcompose the map on the generator
val map_mut : ('a -> 'a) -> 'a t -> unitMap 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 -> unitMap the function over the fullvector until the limit. The rest is unchanged
val iter_until : limit:int -> ('a -> unit) -> 'a t -> unitIterate 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 -> unitSame as iter_until but with the index
Utils.FullVecA 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 tCreate a full vector from a generator
val set : 'a t -> int -> 'a -> unitSet the binding of that integer
val set_after : 'a t -> int -> (int -> 'a) -> unitSet 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 -> 'aGet 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 -> unitval map_mut_until : limit:int -> ('a -> 'a) -> 'a t -> unitMap the function over the fullvector until the limit. The rest is unchanged
val iter_until : limit:int -> ('a -> unit) -> 'a t -> unitIterate 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 -> unitSame as iter_until but with the index
val pp : ('a -> Pp.document) -> 'a t -> Pp.documentOnly prints the non-default values
Utils.Funval ($) : ('a -> 'b) -> 'a -> 'bAn 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 -> 'cAbstraction 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.FunMore 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.HashVectorval mem : 'a t -> int -> boolCheck if an element is set
val set : 'a t -> int -> 'a -> unitSet a value. Create a new binding if necessary
val clear : 'a t -> int -> unitClear a binding, do nothing is the value is not bound.
val get_opt : 'a t -> int -> 'a optionReturn Some v if v is bound to the integer in the hash vector. Return None if nothing is bound
val get : 'a t -> int -> 'aReturn the value bound to the integer. Throw Not_found if the integer is not bound
val empty : unit -> 'a tCreate an empty hashVector.t
val bindings : 'a t -> (int * 'a) listReturns a list of bindings in the hash vector
Utils.HashVectorAn 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 -> boolCheck if an element is set
val set : 'a t -> int -> 'a -> unitSet a value. Create a new binding if necessary
val clear : 'a t -> int -> unitClear a binding, do nothing is the value is not bound.
val get_opt : 'a t -> int -> 'a optionReturn Some v if v is bound to the integer in the hash vector. Return None if nothing is bound
val get : 'a t -> int -> 'aReturn the value bound to the integer. Throw Not_found if the integer is not bound
val empty : unit -> 'a tCreate an empty hashVector.t
val bindings : 'a t -> (int * 'a) listReturns a list of bindings in the hash vector
val pp : ('a -> Pp.document) -> 'a t -> Pp.documentPretty print a hashVector
Utils.IdMaptype ('a, 'b) tThe 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 -> intGives the number of bindings in the idmap
val make : unit -> ('a, 'b) tCreate a new idmap from scratch
val to_ident : ('a, 'b) t -> 'a -> intConvert a key in an identifier. Throws if the key is not bound
val to_ident_opt : ('a, 'b) t -> 'a -> int optionConvert a key in an identifier. None if the key is not bound
val of_ident : ('a, 'b) t -> int -> 'aConvert an identifier to its corresponding key. Throws if the id is not bound
val mem : ('a, 'b) t -> 'a -> boolCheck if a key is bound
val mem_id : ('a, 'b) t -> int -> boolCheck if an id is bound
val getk : ('a, 'b) t -> 'a -> 'bGet a value by key. Raise Not_found if the key is not bound.
val getk_opt : ('a, 'b) t -> 'a -> 'b optionGet a value by key. None if the key is not bound.
val geti : ('a, 'b) t -> int -> 'bGet a value by id. Raise Invalid_argument if the index is not bound.
val unsafe_geti : ('a, 'b) t -> int -> 'bGet a value by id, unsafe.
val setk : ('a, 'b) t -> 'a -> 'b -> unitSet a value by key. Raise Not_found if the key is not bound.
val seti : ('a, 'b) t -> int -> 'b -> unitSet a value by id. Raise Invalid_argument if the index is not bound.
val unsafe_seti : ('a, 'b) t -> int -> 'b -> unitSet a value by id, unsafe.
val fill_all : ('a, 'b) t -> 'b -> unitBind the value to all the keys with the specified value.
val iter : ('a -> int -> 'b -> unit) -> ('a, 'b) t -> unitCall the function on all the bindings of the idmap
val map_to_list : ('a -> int -> 'b -> 'c) -> ('a, 'b) t -> 'c listCall the function on all the bindings of the idmap and return the list of results
Utils.IdMapAn 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 -> intGives the number of bindings in the idmap
val make : unit -> ('a, 'b) tCreate a new idmap from scratch
val add : ('a, 'b) t -> 'a -> 'b -> intAdd a binding, and throw Exists if the binding already exists
val to_ident : ('a, 'b) t -> 'a -> intConvert a key in an identifier. Throws if the key is not bound
val to_ident_opt : ('a, 'b) t -> 'a -> int optionConvert a key in an identifier. None if the key is not bound
val of_ident : ('a, 'b) t -> int -> 'aConvert an identifier to its corresponding key. Throws if the id is not bound
val mem : ('a, 'b) t -> 'a -> boolCheck if a key is bound
val mem_id : ('a, 'b) t -> int -> boolCheck if an id is bound
val getk : ('a, 'b) t -> 'a -> 'bGet a value by key. Raise Not_found if the key is not bound.
val getk_opt : ('a, 'b) t -> 'a -> 'b optionGet a value by key. None if the key is not bound.
val geti : ('a, 'b) t -> int -> 'bGet a value by id. Raise Invalid_argument if the index is not bound.
val unsafe_geti : ('a, 'b) t -> int -> 'bGet a value by id, unsafe.
val setk : ('a, 'b) t -> 'a -> 'b -> unitSet a value by key. Raise Not_found if the key is not bound.
val seti : ('a, 'b) t -> int -> 'b -> unitSet a value by id. Raise Invalid_argument if the index is not bound.
val unsafe_seti : ('a, 'b) t -> int -> 'b -> unitSet a value by id, unsafe.
val fill_all : ('a, 'b) t -> 'b -> unitBind the value to all the keys with the specified value.
val iter : ('a -> int -> 'b -> unit) -> ('a, 'b) t -> unitCall the function on all the bindings of the idmap
val map_to_list : ('a -> int -> 'b -> 'c) -> ('a, 'b) t -> 'c listCall 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.documentPretty prints
Utils.IntBitsval check_index : int -> unitCheck that the index is valid to index an integer.
Throw Invalid_argument if the index is not valid
val check_range : int -> int -> unitCheck 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 -> tInitialize an int with all zeros or all ones depending on the boolean
val get : t -> int -> boolGet a bit at a specific index. See unsafe_get
val set : t -> int -> tSet a bit at a specific index. See unsafe_set
val clear : t -> int -> tClear a bit at a specific index. See unsafe_clear
val setb : t -> int -> bool -> tSet a bit at a specific index according to a boolean. See unsafe_setb
val mask : int -> int -> tmask 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 -> tset_range bf i l sets the range [i; i+l) to ones in bf. See unsafe_set_range
val clear_range : t -> int -> int -> tclear_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 -> tUnsafe version of clear_range
val sub : t -> int -> int -> tsub 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 -> tset_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 -> tUnsafe version of set_sub. However the bits above l of data must be zeroes
val blit : t -> int -> t -> int -> int -> tunsafe_blit src isrc dest idest len copies [isrc; isrc+len) of src into [idest; idest +l) of dest.
See unsafe_blit
Utils.IntBitsManipulate 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 -> tInitialize an int with all zeros or all ones depending on the boolean
val get : t -> int -> boolGet 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 -> tmask 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.Listinclude Stdlib.Listval length : 'a list -> intval compare_lengths : 'a list -> 'b list -> intval compare_length_with : 'a list -> int -> intval cons : 'a -> 'a list -> 'a listval hd : 'a list -> 'aval tl : 'a list -> 'a listval nth : 'a list -> int -> 'aval nth_opt : 'a list -> int -> 'a optionval rev : 'a list -> 'a listval init : int -> (int -> 'a) -> 'a listval append : 'a list -> 'a list -> 'a listval rev_append : 'a list -> 'a list -> 'a listval concat : 'a list list -> 'a listval flatten : 'a list list -> 'a listval iter : ('a -> unit) -> 'a list -> unitval iteri : (int -> 'a -> unit) -> 'a list -> unitval map : ('a -> 'b) -> 'a list -> 'b listval mapi : (int -> 'a -> 'b) -> 'a list -> 'b listval rev_map : ('a -> 'b) -> 'a list -> 'b listval filter_map : ('a -> 'b option) -> 'a list -> 'b listval fold_left : ('a -> 'b -> 'a) -> 'a -> 'b list -> 'aval fold_right : ('a -> 'b -> 'b) -> 'a list -> 'b -> 'bval iter2 : ('a -> 'b -> unit) -> 'a list -> 'b list -> unitval map2 : ('a -> 'b -> 'c) -> 'a list -> 'b list -> 'c listval rev_map2 : ('a -> 'b -> 'c) -> 'a list -> 'b list -> 'c listval fold_left2 : ('a -> 'b -> 'c -> 'a) -> 'a -> 'b list -> 'c list -> 'aval fold_right2 : ('a -> 'b -> 'c -> 'c) -> 'a list -> 'b list -> 'c -> 'cval for_all : ('a -> bool) -> 'a list -> boolval exists : ('a -> bool) -> 'a list -> boolval for_all2 : ('a -> 'b -> bool) -> 'a list -> 'b list -> boolval exists2 : ('a -> 'b -> bool) -> 'a list -> 'b list -> boolval mem : 'a -> 'a list -> boolval memq : 'a -> 'a list -> boolval find : ('a -> bool) -> 'a list -> 'aval find_opt : ('a -> bool) -> 'a list -> 'a optionval filter : ('a -> bool) -> 'a list -> 'a listval find_all : ('a -> bool) -> 'a list -> 'a listval partition : ('a -> bool) -> 'a list -> 'a list * 'a listval assoc : 'a -> ('a * 'b) list -> 'bval assoc_opt : 'a -> ('a * 'b) list -> 'b optionval assq : 'a -> ('a * 'b) list -> 'bval assq_opt : 'a -> ('a * 'b) list -> 'b optionval mem_assoc : 'a -> ('a * 'b) list -> boolval mem_assq : 'a -> ('a * 'b) list -> boolval remove_assoc : 'a -> ('a * 'b) list -> ('a * 'b) listval remove_assq : 'a -> ('a * 'b) list -> ('a * 'b) listval split : ('a * 'b) list -> 'a list * 'b listval combine : 'a list -> 'b list -> ('a * 'b) listval sort : ('a -> 'a -> int) -> 'a list -> 'a listval stable_sort : ('a -> 'a -> int) -> 'a list -> 'a listval fast_sort : ('a -> 'a -> int) -> 'a list -> 'a listval sort_uniq : ('a -> 'a -> int) -> 'a list -> 'a listval merge : ('a -> 'a -> int) -> 'a list -> 'a list -> 'a listval to_seq : 'a list -> 'a Stdlib.Seq.tval of_seq : 'a Stdlib.Seq.t -> 'a listval repeat : int -> 'a -> 'a trepeat n a return a list of n as.
val set_nth : 'a t -> int -> 'a -> 'a tSet the nth value to the new value and return the modified list
val last : 'a t -> 'aGive the last element of the list. Raise Invalid_argument if the list is empty.
val last_opt : 'a t -> 'a optionGive the last element of the list. Return None if the list is empty.
val fold_left_same : ('a -> 'a -> 'a) -> 'a t -> 'aSame 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 tMap 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 tSame as of_array_map but with the index
val concat_map_rev : ('a -> 'b list) -> 'a t -> 'b tSame as concat_map then rev
val concat_map : ('a -> 'b list) -> 'a t -> 'b tSame 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 optionfind_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 tTakes 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 tThis 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 optionRemove the first element matching the predicate. Returns None if no element matches the predicate
val drop : int -> 'a t -> 'a tDrop 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 tTake 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 listTake 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 listsub l pos len return the sub-list of l starting at pos of length len
val equal : ('a -> 'b -> bool) -> 'a t -> 'b t -> boolval mem : ('a -> 'b -> bool) -> 'a -> 'b t -> boolval compare : ('a -> 'b -> int) -> 'a t -> 'b t -> intIf 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 tMonadic bind. It's just concat_map
val return : 'a -> 'a tMonadic return
val short_combine : 'a t -> 'b t -> ('a * 'b) tSame as combine but if a list is shorter then the elements of the longest list are discarded
val let+ : 'a list -> ('a -> 'b) -> 'b listApplicative let binding. let+ x = xl in e = let* x = xl in return e
val and+ : 'a t -> 'b t -> ('a * 'b) tNot strict applicative merge (short_combine). If both list have different length, the longer one is cropped
val let+! : 'a list -> ('a -> unit) -> unitIterative 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.ListExtension 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 endval repeat : int -> 'a -> 'a trepeat n a return a list of n as.
Set the nth value to the new value and return the modified list
val last : 'a t -> 'aGive the last element of the list. Raise Invalid_argument if the list is empty.
val last_opt : 'a t -> 'a optionGive the last element of the list. Return None if the list is empty.
val fold_left_same : ('a -> 'a -> 'a) -> 'a t -> 'aSame 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 tMap 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 tSame 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 optionfind_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 optionRemove 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 listTake 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 listsub 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 -> boolIf 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 tMonadic 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 optionLogger.SLogs.LoggerThis 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 -> unitOverride the level of this logger. It may still be overridden by the command line
val get_level : unit -> levelGet the current level
val log_fatal : code:int -> level -> ('a, 'b) printf_fatalLog a fatal problem with format string then shutdown with code
val fail : ('a, 'b) printf_fatalFailure due to external circumstances, generally wrong user input, then exit with code 1. Level is Base
val fatal : ('a, 'b) printf_fatalDeclare 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 -> unitOverride the level of this logger. It may still be overridden by the command line
val get_level : unit -> levelGet the current level
val log_fatal : code:int -> level -> ('a, 'b) printf_fatalLog a fatal problem with format string then shutdown with code
val fail : ('a, 'b) printf_fatalFailure due to external circumstances, generally wrong user input, then exit with code 1. Level is Base
val fatal : ('a, 'b) printf_fatalDeclare a fatal internal error then exit with code 2. Level is Err
Utils.Logstype 'a printf = ('a, Stdlib.out_channel, unit) Stdlib.format -> 'aThe type of normal logging function
type ('a, 'b) printf_fatal = ('a, Stdlib.out_channel, unit, 'b) Stdlib.format4 -> 'aThe 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 -> stringConvert level to string
val level_to_header : level -> stringConvert level to string header like "[Error]". Base is the empty string
val set_default_level : level -> unitSet a default level to all the modules. Erase local customized levels
val set_level : string -> level -> unitSet level of a module by name
val level_conv : level Cmdliner.Arg.convParser for log level on command line
val set_stdout_level : level -> unitSet level below which the output goes to stdout
module type String = sig ... endmodule Logger : functor (S : String) -> sig ... endThis 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 -> unitval term : unit Cmdliner.Term.tYou 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.documentPretty prints a level
val level_to_string : level -> stringConvert level to string
val level_to_header : level -> stringConvert level to string header like "[Error]". Base is the empty string
val set_default_level : level -> unitSet a default level to all the modules. Erase local customized levels
val set_level : string -> level -> unitSet level of a module by name
val level_conv : level Cmdliner.Arg.convParser for log level on command line
val set_stdout_level : level -> unitSet level below which the output goes to stdout
module type String = sig ... endThis 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 ->
+ unitLogs.StringUtils.Optioninclude module type of Stdlib.Optionval none : 'a optionval some : 'a -> 'a optionval value : 'a option -> default:'a -> 'aval get : 'a option -> 'aval bind : 'a option -> ('a -> 'b option) -> 'b optionval join : 'a option option -> 'a optionval map : ('a -> 'b) -> 'a option -> 'b optionval fold : none:'a -> some:('b -> 'a) -> 'b option -> 'aval iter : ('a -> unit) -> 'a option -> unitval is_none : 'a option -> boolval is_some : 'a option -> boolval equal : ('a -> 'a -> bool) -> 'a option -> 'a option -> boolval compare : ('a -> 'a -> int) -> 'a option -> 'a option -> intval to_result : none:'e -> 'a option -> ('a, 'e) Stdlib.resultval to_list : 'a option -> 'a listval to_seq : 'a option -> 'a Stdlib.Seq.tval take_first : 'a option -> 'a option -> 'a optionTake 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 optiontake_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 optionTake 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) optionIf both option have values, give Some of the pair, otherwise None
val (&&&) : 'a option -> 'b option -> ('a * 'b) optionTake_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 -> 'bExpect 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) -> 'aLike 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 optionApplicative let.
let+ x = mx in e is Option.map (fun x -> e) mx
val and+ : 'a option -> 'b option -> ('a * 'b) optionApplicative and.
let+ x = mx and+ y = my in e give Some e if both mx and my were Somes.
val let+! : 'a option -> ('a -> unit) -> unitIter 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 optionCommute 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 optionThe same as a List.map and then a lift
val lift_pair : ('a option * 'b option) -> ('a * 'b) optionLift a pair of options to an option of pair. It is the same as take_all.
Utils.OptionThis module extends the base OCaml API of Option.
In particular, it adds:
include module type of Stdlib.OptionTake 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 Somes.
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.Pairval iter : ('a -> unit) -> ('b -> unit) -> ('a * 'b) -> unitIter each function on one side of the pair
val compare : ?fst:('a -> 'a -> int) -> ?snd:('b -> 'b -> int) -> ('a * 'b) -> ('a * 'b) -> intCompare 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) -> boolTest pair equality using provided equality function. Both fst and snd default to the polymorphic equality
val make : 'a -> 'b -> 'a * 'bJust 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.PairThis 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) ->
+ intCompare 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) ->
+ boolTest 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.custommethod compact : output -> unitmethod pretty : output -> state -> int -> bool -> unitmethod requirement : requirementPp.custommethod pretty : output -> state -> int -> bool -> unitmethod requirement : requirementPp.outputUtils.PpThis is a wrapper around the pprint library
include PPrintval empty : documentval char : char -> documentval string : string -> documentval substring : string -> int -> int -> documentval fancystring : string -> int -> documentval fancysubstring : string -> int -> int -> int -> documentval utf8string : string -> documentval utf8format : ('a, unit, string, document) Stdlib.format4 -> 'aval hardline : documentval blank : int -> documentval break : int -> documentval (^^) : document -> document -> documentval nest : int -> document -> documentval group : document -> documentval ifflat : document -> document -> documentval align : document -> documentval infinity : requirementclass type output = object ... endtype state = PPrintEngine.state = {width : int; |
ribbon : int; |
mutable last_indent : int; |
mutable line : int; |
mutable column : int; |
}class type custom = object ... endval custom : custom -> documentval requirement : document -> requirementval pretty : output -> state -> int -> bool -> document -> unitval compact : output -> document -> unitval lparen : PPrintEngine.documentval rparen : PPrintEngine.documentval langle : PPrintEngine.documentval rangle : PPrintEngine.documentval lbrace : PPrintEngine.documentval rbrace : PPrintEngine.documentval lbracket : PPrintEngine.documentval rbracket : PPrintEngine.documentval squote : PPrintEngine.documentval dquote : PPrintEngine.documentval bquote : PPrintEngine.documentval semi : PPrintEngine.documentval colon : PPrintEngine.documentval comma : PPrintEngine.documentval space : PPrintEngine.documentval dot : PPrintEngine.documentval sharp : PPrintEngine.documentval slash : PPrintEngine.documentval backslash : PPrintEngine.documentval equals : PPrintEngine.documentval qmark : PPrintEngine.documentval tilde : PPrintEngine.documentval at : PPrintEngine.documentval percent : PPrintEngine.documentval dollar : PPrintEngine.documentval caret : PPrintEngine.documentval ampersand : PPrintEngine.documentval star : PPrintEngine.documentval plus : PPrintEngine.documentval minus : PPrintEngine.documentval underscore : PPrintEngine.documentval bang : PPrintEngine.documentval bar : PPrintEngine.documentval precede : PPrintEngine.document -> PPrintEngine.document -> PPrintEngine.documentval terminate : PPrintEngine.document -> PPrintEngine.document -> PPrintEngine.documentval enclose : PPrintEngine.document -> PPrintEngine.document -> PPrintEngine.document -> PPrintEngine.documentval squotes : PPrintEngine.document -> PPrintEngine.documentval dquotes : PPrintEngine.document -> PPrintEngine.documentval bquotes : PPrintEngine.document -> PPrintEngine.documentval braces : PPrintEngine.document -> PPrintEngine.documentval parens : PPrintEngine.document -> PPrintEngine.documentval angles : PPrintEngine.document -> PPrintEngine.documentval brackets : PPrintEngine.document -> PPrintEngine.documentval twice : PPrintEngine.document -> PPrintEngine.documentval repeat : int -> PPrintEngine.document -> PPrintEngine.documentval concat : PPrintEngine.document list -> PPrintEngine.documentval separate : PPrintEngine.document -> PPrintEngine.document list -> PPrintEngine.documentval concat_map : ('a -> PPrintEngine.document) -> 'a list -> PPrintEngine.documentval separate_map : PPrintEngine.document -> ('a -> PPrintEngine.document) -> 'a list -> PPrintEngine.documentval separate2 : PPrintEngine.document -> PPrintEngine.document -> PPrintEngine.document list -> PPrintEngine.documentval optional : ('a -> PPrintEngine.document) -> 'a option -> PPrintEngine.documentval lines : string -> PPrintEngine.document listval arbitrary_string : string -> PPrintEngine.documentval words : string -> PPrintEngine.document listval split : (char -> bool) -> string -> PPrintEngine.document listval flow : PPrintEngine.document -> PPrintEngine.document list -> PPrintEngine.documentval flow_map : PPrintEngine.document -> ('a -> PPrintEngine.document) -> 'a list -> PPrintEngine.documentval url : string -> PPrintEngine.documentval hang : int -> PPrintEngine.document -> PPrintEngine.documentval prefix : int -> int -> PPrintEngine.document -> PPrintEngine.document -> PPrintEngine.documentval jump : int -> int -> PPrintEngine.document -> PPrintEngine.documentval infix : int -> int -> PPrintEngine.document -> PPrintEngine.document -> PPrintEngine.document -> PPrintEngine.documentval surround : int -> int -> PPrintEngine.document -> PPrintEngine.document -> PPrintEngine.document -> PPrintEngine.documentval soft_surround : int -> int -> PPrintEngine.document -> PPrintEngine.document -> PPrintEngine.document -> PPrintEngine.documentval surround_separate : int -> int -> PPrintEngine.document -> PPrintEngine.document -> PPrintEngine.document -> PPrintEngine.document -> PPrintEngine.document list -> PPrintEngine.documentval surround_separate_map : int -> int -> PPrintEngine.document -> PPrintEngine.document -> PPrintEngine.document -> PPrintEngine.document -> ('a -> PPrintEngine.document) -> 'a list -> PPrintEngine.documentval (!^) : string -> PPrintEngine.documentval (^/^) : PPrintEngine.document -> PPrintEngine.document -> PPrintEngine.documentval (^//^) : PPrintEngine.document -> PPrintEngine.document -> PPrintEngine.documentThis 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 -> unitRender the document in a pretty manner on the provided out_channel
val fprintln : Stdlib.out_channel -> document -> unitRender the document in a compact manner on the provided out_channel
val print : document -> unitPrint the document on stdout. The general way of
val println : document -> unitPrint the document on stdout, then a endline, then flush
val eprintln : document -> unitPrint the document on stderr, then a endline, then flush
val sprintc : ToBuffer.document -> stringPrint the document in a string, in a compact manner
val sprint : ToBuffer.document -> stringPrint 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 -> unitConvert 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 -> unitSame 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 -> stringConvert 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 -> 'aPrintf like function that returns a document of the formatted string. In the end, it's just a string document, nothing more complex
val space : documentA breakable space
val nbspace : documentA non-breakable space (equivalent to char ' ')
val int : int -> documentPrint an int in decimal
val hex : int -> documentPrint an int in hexadecimal as if it was unsigned
val shex : int -> documentPrint an int in hexadecimal with a sign: -1 will be printed to -0x1
val ptr : int -> documentPrint an int in hexadecimal with the 0x prefix
val byte : char -> documentPrint a byte as 2 hexadecimal digit
val hex16 : int -> documentPrint an unsigned 16 bit integer as 4 hexadecimal digit
val hex32 : int32 -> documentPrint an unsigned 32 bit integer as 8 hexadecimal digit
val hex64 : int64 -> documentPrint an unsigned 64 bit integer as 16 hexadecimal digit
val array : ('a -> OCaml.representation) -> 'a array -> OCaml.representationPrint an array when provided with an element printer
val list : ('a -> OCaml.representation) -> 'a list -> OCaml.representationPrint an list when provided with an element printer
val opt : ('a -> OCaml.representation) -> 'a option -> OCaml.representationPrint an option when provided with an element printer
val pair : ('a -> OCaml.representation) -> ('b -> OCaml.representation) -> ('a * 'b) -> OCaml.representationPrint an pair when provided with both element printers
val tup3 : ('a -> OCaml.representation) -> ('b -> OCaml.representation) -> ('c -> OCaml.representation) -> ('a * 'b * 'c) -> OCaml.representationPrint an 3-sized tuple when provided with all three element printers
val erase : 'a -> documentIgnore the input and print nothing
val mapping : string -> (document * document) list -> documentPrints 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 printing
This is a wrapper around the pprint library
include module type of struct include PPrint end
val empty : documentval char : char -> documentval string : string -> documentval substring : string -> int -> int -> documentval fancystring : string -> int -> documentval fancysubstring : string -> int -> int -> int -> documentval utf8string : string -> documentval utf8format : ('a, unit, string, document) Stdlib.format4 -> 'aval hardline : documentval blank : int -> documentval break : int -> documentval is_empty : document -> boolval infinity : requirementclass type output = object ... endclass type custom = object ... endval custom : custom -> documentval requirement : document -> requirementval compact : output -> document -> unitval lparen : documentval rparen : documentval langle : documentval rangle : documentval lbrace : documentval rbrace : documentval lbracket : documentval rbracket : documentval squote : documentval dquote : documentval bquote : documentval semi : documentval colon : documentval comma : documentval dot : documentval sharp : documentval slash : documentval backslash : documentval equals : documentval qmark : documentval tilde : documentval at : documentval percent : documentval dollar : documentval caret : documentval ampersand : documentval star : documentval plus : documentval minus : documentval underscore : documentval bang : documentval bar : documentval lines : string -> document listval arbitrary_string : string -> documentval words : string -> document listval split : (char -> bool) -> string -> document listval url : string -> documentval (!^) : string -> documentRendering 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 -> unitRender the document in a pretty manner on the provided out_channel
val fprintln : Stdlib.out_channel -> document -> unitRender the document in a compact manner on the provided out_channel
val print : document -> unitPrint the document on stdout. The general way of
val println : document -> unitPrint the document on stdout, then a endline, then flush
val eprintln : document -> unitPrint the document on stderr, then a endline, then flush
Render documents in format strings
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 -> unitConvert 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 -> unitSame 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
Convert a document printer to a Printf format string via %t for function outputing in a string
Combinators
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 -> 'aPrintf like function that returns a document of the formatted string. In the end, it's just a string document, nothing more complex
val space : documentA breakable space
val nbspace : documentA non-breakable space (equivalent to char ' ')
val bool : bool -> documentPrint a boolean as "true" or "false"
val int : int -> documentPrint an int in decimal
val hex : int -> documentPrint an int in hexadecimal as if it was unsigned
val shex : int -> documentPrint an int in hexadecimal with a sign: -1 will be printed to -0x1
val ptr : int -> documentPrint an int in hexadecimal with the 0x prefix
val byte : char -> documentPrint a byte as 2 hexadecimal digit
val hex16 : int -> documentPrint an unsigned 16 bit integer as 4 hexadecimal digit
val hex32 : int32 -> documentPrint an unsigned 32 bit integer as 8 hexadecimal digit
val hex64 : int64 -> documentPrint 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.documentPrint an pair when provided with both element printers
val tup3 :
+ ('a -> PPrint.document) ->
+ ('b -> PPrint.document) ->
+ ('c -> PPrint.document) ->
+ ('a * 'b * 'c) ->
+ PPrint.documentPrint an 3-sized tuple when provided with all three element printers
val qstring : string -> documentPrint a quoted string
val erase : 'a -> documentIgnore 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 -> documentPrint a Hashtbl using mapping
val hashtbl :
+ ?name:string ->
+ ('a -> document) ->
+ ('b -> document) ->
+ ('a, 'b) Stdlib.Hashtbl.t ->
+ documentPrint a Hashtbl using mapping
val hashtbl_sorted :
+ compare:('a -> 'a -> int) ->
+ ?name:string ->
+ ('a -> document) ->
+ ('b -> document) ->
+ ('a, 'b) Stdlib.Hashtbl.t ->
+ documentPrint a sorted Hashtbl using mapping
val record :
+ OCaml.type_name ->
+ (OCaml.record_field * PPrint.document) list ->
+ documentPrint a record in the format
name{
field = value;
field = value;
field = value;
-}val separate_mapi : document -> (int -> 'a -> document) -> 'a list -> documentLike separate_map but with the index
val concat_array_map : ('a -> document) -> 'a array -> documentConcatenate the documents produced by the function over the array
val concat_array_mapi : (int -> 'a -> document) -> 'a array -> documentConcatenate 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 -> documentPrints a Unix.process_status with an integer printer
val statusi : Unix.process_status -> documentPrints a Unix.process_status with decimal integers
val statush : Unix.process_status -> documentPrints a Unix.process_status with hexadecimal integers
\ No newline at end of file
+}Like separate_map but with the index
Concatenate 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 printer
val statusi : Unix.process_status -> documentPrints a Unix.process_status with decimal integers
val statush : Unix.process_status -> documentPrints a Unix.process_status with hexadecimal integers
Utils.Protectexception Protect_both of exn * exnThis exception it thrown when both function and protector have thrown
val protect : (unit -> 'a) -> (unit -> unit) -> 'aprotect 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.ProtectThis 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.Raiseval inv_arg : ('a, unit, string, 'b) Stdlib.format4 -> 'aPrintf like funtion that throws an Invalid_Argument with the formated string
Utils.RaiseThis 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.IMapAn integer map: Map.Make(Int)
val empty : 'a tval is_empty : 'a t -> boolval mem : key -> 'a t -> boolval add : key -> 'a -> 'a t -> 'a tval update : key -> ('a option -> 'a option) -> 'a t -> 'a tval singleton : key -> 'a -> 'a tval remove : key -> 'a t -> 'a tval merge : (key -> 'a option -> 'b option -> 'c option) -> 'a t -> 'b t -> 'c tval union : (key -> 'a -> 'a -> 'a option) -> 'a t -> 'a t -> 'a tval compare : ('a -> 'a -> int) -> 'a t -> 'a t -> intval equal : ('a -> 'a -> bool) -> 'a t -> 'a t -> boolval iter : (key -> 'a -> unit) -> 'a t -> unitval fold : (key -> 'a -> 'b -> 'b) -> 'a t -> 'b -> 'bval for_all : (key -> 'a -> bool) -> 'a t -> boolval exists : (key -> 'a -> bool) -> 'a t -> boolval filter : (key -> 'a -> bool) -> 'a t -> 'a tval partition : (key -> 'a -> bool) -> 'a t -> 'a t * 'a tval cardinal : 'a t -> intval bindings : 'a t -> (key * 'a) listval min_binding : 'a t -> key * 'aval min_binding_opt : 'a t -> (key * 'a) optionval max_binding : 'a t -> key * 'aval max_binding_opt : 'a t -> (key * 'a) optionval choose : 'a t -> key * 'aval choose_opt : 'a t -> (key * 'a) optionval split : key -> 'a t -> 'a t * 'a option * 'a tval find : key -> 'a t -> 'aval find_opt : key -> 'a t -> 'a optionval find_first : (key -> bool) -> 'a t -> key * 'aval find_first_opt : (key -> bool) -> 'a t -> (key * 'a) optionval find_last : (key -> bool) -> 'a t -> key * 'aval find_last_opt : (key -> bool) -> 'a t -> (key * 'a) optionval map : ('a -> 'b) -> 'a t -> 'b tval mapi : (key -> 'a -> 'b) -> 'a t -> 'b tval to_seq : 'a t -> (key * 'a) Stdlib.Seq.tval to_seq_from : key -> 'a t -> (key * 'a) Stdlib.Seq.tval add_seq : (key * 'a) Stdlib.Seq.t -> 'a t -> 'a tval of_seq : (key * 'a) Stdlib.Seq.t -> 'a tRngMap.IMapAn integer map: Map.Make(Int)
Make.1-Objval len : t -> intThe type of range end
Make.Objval len : t -> intThe type of range end
RngMap.MakeHow to make a RngMap from a LenObject
type obj = Obj.tThe type of the contained object
type obj_off = obj * intThe type of an object with an offset
type tThe type of the map from address ranges to obj
val empty : tAn empty RngMap
val is_in : objaddr:int -> obj -> int -> boolTest if an address is inside the object at address objaddr
val at : t -> int -> objGet the object containing the address. Throw Not_found if no object contains the address
val at_opt : t -> int -> obj optionGet the object containing the address. None if no object contains the address
val at_off : t -> int -> obj_offGet 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 optionGet 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 -> tUpdate the binding containing the provided address. If no binding contained the address, this is a no-op
val iteri : (int -> obj -> unit) -> t -> unitIter a function over all the objects with their address
val clear : t -> pos:int -> len:int -> tClear 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) -> tClear 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 -> tSame 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 -> tAdd an object at a specific address. The whole range of addresses covered by the object must be free
val addp : t -> obj_off -> tAdd 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.tReturn 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.MakeHow to make a RngMap from a LenObject
type obj = Obj.tThe type of the contained object
type obj_off = obj * intThe type of an object with an offset
The type of the map from address ranges to obj
val empty : tAn empty RngMap
val is_in : objaddr:int -> obj -> int -> boolTest 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.ObjRngMap.PairLenObjectFor types that do not have an inner length representation, we add it with a pair
type t = Obj.t * intThe type to be indexed by starting addresses, must have a length
val len : t -> intThe type of range end
RngMap.PairLenObjectFor types that do not have an inner length representation, we add it with a pair
type t = Obj.t * intThe type to be indexed by starting addresses, must have a length
val len : t -> intThe type of range end
Utils.RngMapmodule IMap : sig ... endAn integer map: Map.Make(Int)
module type Object = sig ... endA module that represent a simple type with no operation
module type LenObject = sig ... endA module for type that have a concept of length in the rngMap context
module PairLenObject : functor (Obj : Object) -> LenObject with type t = Obj.t * intFor types that do not have an inner length representation, we add it with a pair
module type S = sig ... endThe signature of the range map
Utils.RngMapA 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 ... endAn integer map: Map.Make(Int)
module type Object = sig ... endA module that represent a simple type with no operation
module type LenObject = sig ... endA 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 ... endThe signature of the range map
RngMap.LenObjectA module for type that have a concept of length in the rngMap context
val len : t -> intThe type of range end
RngMap.LenObjectA module for type that have a concept of length in the rngMap context
val len : t -> intThe type of range end
RngMap.ObjectA module that represent a simple type with no operation
RngMap.ObjectA module that represent a simple type with no operation
RngMap.SThe signature of the range map
type obj_off = obj * intThe type of an object with an offset
type tThe type of the map from address ranges to obj
val empty : tAn empty RngMap
val is_in : objaddr:int -> obj -> int -> boolTest if an address is inside the object at address objaddr
val at : t -> int -> objGet the object containing the address. Throw Not_found if no object contains the address
val at_opt : t -> int -> obj optionGet the object containing the address. None if no object contains the address
val at_off : t -> int -> obj_offGet 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 optionGet 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 -> tUpdate the binding containing the provided address. If no binding contained the address, this is a no-op
val iteri : (int -> obj -> unit) -> t -> unitIter a function over all the objects with their address
val clear : t -> pos:int -> len:int -> tClear 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) -> tClear 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 -> tSame 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 -> tAdd an object at a specific address. The whole range of addresses covered by the object must be free
val addp : t -> obj_off -> tAdd 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.tReturn 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.SThe signature of the range map
type obj_off = obj * intThe type of an object with an offset
The type of the map from address ranges to obj
val empty : tAn empty RngMap
val is_in : objaddr:int -> obj -> int -> boolTest 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.Seqinclude Stdlib.Seqval empty : 'a tval return : 'a -> 'a tval map : ('a -> 'b) -> 'a t -> 'b tval filter : ('a -> bool) -> 'a t -> 'a tval filter_map : ('a -> 'b option) -> 'a t -> 'b tval flat_map : ('a -> 'b t) -> 'a t -> 'b tval fold_left : ('a -> 'b -> 'a) -> 'a -> 'b t -> 'aval iter : ('a -> unit) -> 'a t -> unitval add_count : ?start:int -> 'a t -> (int * 'a) tAdd a counter starting at start (default 0) in front of each element of the sequence
val iota : ?start:int -> int -> int tGenerate 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 tGenerate an integer sequence up to endi by stepping step. Optionally may start at start instead of 0
val cons : 'a -> 'a t -> unit -> 'a nodeAdd 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 optionApplies 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.SeqThis module is for extending the Seq module of the standard library
include module type of struct include Stdlib.Seq endtype !'a t = unit -> 'a nodeval is_empty : 'a t -> boolval length : 'a t -> intval iter : ('a -> unit) -> 'a t -> unitval fold_left : ('acc -> 'a -> 'acc) -> 'acc -> 'a t -> 'accval iteri : (int -> 'a -> unit) -> 'a t -> unitval fold_lefti : ('acc -> int -> 'a -> 'acc) -> 'acc -> 'a t -> 'accval for_all : ('a -> bool) -> 'a t -> boolval exists : ('a -> bool) -> 'a t -> boolval find : ('a -> bool) -> 'a t -> 'a optionval find_index : ('a -> bool) -> 'a t -> int optionval find_mapi : (int -> 'a -> 'b option) -> 'a t -> 'b optionval empty : 'a tval return : 'a -> 'a tval init : int -> (int -> 'a) -> 'a tval unfold : ('b -> ('a * 'b) option) -> 'b -> 'a tval repeat : 'a -> 'a tval forever : (unit -> 'a) -> 'a tval iterate : ('a -> 'a) -> 'a -> 'a tval of_dispenser : (unit -> 'a option) -> 'a tval to_dispenser : 'a t -> unit -> 'a optionval ints : int -> int tAdd a counter starting at start (default 0) in front of each element of the sequence
val iota : ?start:int -> int -> int tGenerate 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 tGenerate 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 optionApplies 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.Stringinclude Stdlib.Stringval length : string -> intval get : string -> int -> charval set : bytes -> int -> char -> unitval create : int -> bytesval make : int -> char -> stringval init : int -> (int -> char) -> stringval copy : string -> stringval sub : string -> int -> int -> stringval fill : bytes -> int -> int -> char -> unitval blit : string -> int -> bytes -> int -> int -> unitval concat : string -> string list -> stringval iter : (char -> unit) -> string -> unitval iteri : (int -> char -> unit) -> string -> unitval map : (char -> char) -> string -> stringval mapi : (int -> char -> char) -> string -> stringval trim : string -> stringval escaped : string -> stringval index : string -> char -> intval index_opt : string -> char -> int optionval rindex : string -> char -> intval rindex_opt : string -> char -> int optionval index_from : string -> int -> char -> intval index_from_opt : string -> int -> char -> int optionval rindex_from : string -> int -> char -> intval rindex_from_opt : string -> int -> char -> int optionval contains : string -> char -> boolval contains_from : string -> int -> char -> boolval rcontains_from : string -> int -> char -> boolval uppercase : string -> stringval lowercase : string -> stringval capitalize : string -> stringval uncapitalize : string -> stringval uppercase_ascii : string -> stringval lowercase_ascii : string -> stringval capitalize_ascii : string -> stringval uncapitalize_ascii : string -> stringUtils.StringExtension of the String module of the standard library.
include module type of struct include Stdlib.String endval to_seq : t -> char Stdlib.Seq.tval to_seqi : t -> (int * char) Stdlib.Seq.tval of_seq : char Stdlib.Seq.t -> tval get_utf_8_uchar : t -> int -> Stdlib.Uchar.utf_decodeval is_valid_utf_8 : t -> boolval get_utf_16be_uchar : t -> int -> Stdlib.Uchar.utf_decodeval is_valid_utf_16be : t -> boolval get_utf_16le_uchar : t -> int -> Stdlib.Uchar.utf_decodeval is_valid_utf_16le : t -> boolval hash : t -> intval seeded_hash : int -> t -> intCheck if a predicate hold for at least one char in a string
Utils.Vecval length : 'a t -> intval empty : unit -> 'a tval mem : 'a -> 'a t -> boolval get : 'a t -> int -> 'aval unsafe_get : 'a t -> int -> 'aval set : 'a t -> int -> 'a -> unitval unsafe_set : 'a t -> int -> 'a -> unitval update : 'a t -> int -> ('a -> 'a) -> unitval unsafe_update : 'a t -> int -> ('a -> 'a) -> unitval copy : 'a t -> 'a tval for_all : ('a -> bool) -> 'a t -> boolval exists : ('a -> bool) -> 'a t -> boolval map : ('a -> 'b) -> 'a t -> 'b tval mapi : (int -> 'a -> 'b) -> 'a t -> 'b tval iter : ('a -> unit) -> 'a t -> unitval iteri : (int -> 'a -> unit) -> 'a t -> unitval iter_until : limit:int -> ('a -> unit) -> 'a t -> unitIterates until the limit. If the vector is shorter, this is a plain iter
val iteri_until : limit:int -> (int -> 'a -> unit) -> 'a t -> unitSame as iter_until but with the index
val to_list : 'a t -> 'a listval to_listi : 'a t -> (int * 'a) listval fold_right : ('a -> 'b -> 'b) -> 'a t -> 'b -> 'bval foldi_right : (int -> 'a -> 'b -> 'b) -> 'a t -> 'b -> 'bval fold_left : ('a -> 'b -> 'a) -> 'a -> 'b t -> 'aval foldi_left : (int -> 'b -> 'a -> 'b) -> 'a t -> 'b -> 'bval add_one : 'a t -> 'a -> unitAdd a new element at the end of the vector
val remove_one : 'a t -> unitRemove the last element of the vector
val remove_n : 'a t -> int -> unitRemove the last n elements of the vector
val to_array : 'a t -> 'a arrayval of_array : 'a array -> 'a tval ensure : 'a t -> int -> 'a -> unitEnsure 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 -> unitKeep only the specified number of element. If the vector was smaller, do nothing.
val resize : 'a t -> int -> 'a -> unitResize 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 tval map_mut : ('a -> 'a) -> 'a t -> unitMap 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 -> unitSame 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 -> unitWrite the value in all the cells of the vector
val insert : 'a t -> int -> 'a -> unitInsert 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.tval to_seq : 'a t -> 'a Utils.Seq.tval to_seqi_sub : 'a t -> pos:int -> len:int -> (int * 'a) Utils.Seq.tval to_seqi : 'a t -> (int * 'a) Utils.Seq.tval pp : ('a -> Utils.Pp.document) -> 'a t -> Utils.Pp.documentVector pretty printer
val ppi : ('a -> Utils.Pp.document) -> 'a t -> Utils.Pp.documentVector pretty printer that also prints the index of each element using Pp.mapping
Utils.VecThis module provide a resizable array. It is a rename of Res.array with added features
val length : 'a t -> intval empty : unit -> 'a tval mem : 'a -> 'a t -> boolval get : 'a t -> int -> 'aval unsafe_get : 'a t -> int -> 'aval set : 'a t -> int -> 'a -> unitval unsafe_set : 'a t -> int -> 'a -> unitval update : 'a t -> int -> ('a -> 'a) -> unitval unsafe_update : 'a t -> int -> ('a -> 'a) -> unitval for_all : ('a -> bool) -> 'a t -> boolval exists : ('a -> bool) -> 'a t -> boolval iter : ('a -> unit) -> 'a t -> unitval iteri : (int -> 'a -> unit) -> 'a t -> unitval iter_until : limit:int -> ('a -> unit) -> 'a t -> unitIterates until the limit. If the vector is shorter, this is a plain iter
val iteri_until : limit:int -> (int -> 'a -> unit) -> 'a t -> unitSame as iter_until but with the index
val to_list : 'a t -> 'a listval to_listi : 'a t -> (int * 'a) listval fold_right : ('a -> 'b -> 'b) -> 'a t -> 'b -> 'bval foldi_right : (int -> 'a -> 'b -> 'b) -> 'a t -> 'b -> 'bval fold_left : ('a -> 'b -> 'a) -> 'a -> 'b t -> 'aval foldi_left : (int -> 'b -> 'a -> 'b) -> 'a t -> 'b -> 'bval add_one : 'a t -> 'a -> unitAdd a new element at the end of the vector
val remove_one : 'a t -> unitRemove the last element of the vector
val remove_n : 'a t -> int -> unitRemove the last n elements of the vector
val to_array : 'a t -> 'a arrayval of_array : 'a array -> 'a tval ensure : 'a t -> int -> 'a -> unitEnsure 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 -> unitKeep only the specified number of element. If the vector was smaller, do nothing.
val resize : 'a t -> int -> 'a -> unitResize 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 -> unitMap 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 -> unitval fill_all : 'a t -> 'a -> unitWrite the value in all the cells of the vector
val insert : 'a t -> int -> 'a -> unitInsert an element at the specified position that may be one past the end
val pp : ('a -> Pp.document) -> 'a t -> Pp.documentVector pretty printer
val ppi : ('a -> Pp.document) -> 'a t -> Pp.documentVector pretty printer that also prints the index of each element using Pp.mapping
Utils.WeakMapval create : int -> ('a, 'b) tThe initial map
val add : ('a, 'b) t -> 'a -> 'b -> unitAdd a mapping to the map. raise Exists if a is already mapped
val get : ('a, 'b) t -> 'a -> 'bRetrieves a value from the table. Throws Hashtbl.Not_found if the binding do not exists
Utils.WeakMapThis 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) tThe initial map
val add : ('a, 'b) t -> 'a -> 'b -> unitAdd a mapping to the map. raise Exists if a is already mapped
val get : ('a, 'b) t -> 'a -> 'bRetrieves a value from the table. Throws Hashtbl.Not_found if the binding do not exists
Utils.WeakPtrval empty : unit -> 'a tMake a new empty pointer
val make : 'a -> 'a tMake a new pointer and set it with the given value
val seto : 'a t -> 'a option -> unitSet or clear the value inside the pointer depending of the option
val set : 'a t -> 'a -> unitSet the value inside the pointer
val reset : 'a t -> unitClear the value inside the pointer
val geto : 'a t -> 'a optionRetrieves the value and return None if the pointer is now empty
val get : 'a t -> 'aRetrieves the value. Will raise Deleted if the value was deleted by the GC
val check : 'a t -> boolCheck if the pointer is filled
Utils.WeakPtrThis module provide a simple weak pointer that can be rendered invalid by the GC
val empty : unit -> 'a tMake a new empty pointer
val make : 'a -> 'a tMake a new pointer and set it with the given value
val seto : 'a t -> 'a option -> unitSet or clear the value inside the pointer depending of the option
val set : 'a t -> 'a -> unitSet the value inside the pointer
val reset : 'a t -> unitClear the value inside the pointer
val geto : 'a t -> 'a optionRetrieves the value and return None if the pointer is now empty
val get : 'a t -> 'aRetrieves the value. Will raise Deleted if the value was deleted by the GC
val check : 'a t -> boolCheck if the pointer is filled
Utilsmodule Array : sig ... endmodule BitVec : sig ... endmodule Bits : sig ... endmodule BytesSeq : sig ... endmodule Cache : sig ... endmodule Cmd : sig ... endmodule CmdlinerHelper : sig ... endmodule Counter : sig ... endmodule Files : sig ... endmodule FullVec : sig ... endmodule Fun : sig ... endmodule HashVector : sig ... endmodule IdMap : sig ... endmodule IntBits : sig ... endmodule List : sig ... endmodule Logs : sig ... endmodule Option : sig ... endmodule Pair : sig ... endmodule Pp : sig ... endmodule Protect : sig ... endmodule Raise : sig ... endmodule RngMap : sig ... endmodule Seq : sig ... endmodule String : sig ... endmodule Vec : sig ... endmodule WeakMap : sig ... endmodule WeakPtr : sig ... endUtilsmodule BitVec : sig ... endThis module provides an interface for a bit vector of dynamic size.
module Bits : sig ... endLike 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 ... endThis 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 ... endThis module implement a caching system i.e a persistant structure stored on the disk.
module Cmd : sig ... endThis module provides high-level interaction with external processes.
module CmdlinerHelper : sig ... endThis module provide some Cmdliner helper functions.
module Counter : sig ... endThis 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 ... endThis module provides simplified file management and some channel interaction function.
module FullVec : sig ... endA full vector is a vector in which all non-negative integer are bound.
module HashVector : sig ... endAn 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 ... endAn IdMap is a map that associate an id to each key (and thus to each value).
module IntBits : sig ... endManipulate an int as bitfield of size 31 or 63.
module Logs : sig ... endThis module provide a logging system for each module.
module Pair : sig ... endThis module contain random utility functions dealing with pairs
module Pp : sig ... endThis 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 ... endThis module provide try-with-finally kind of exception handling.
module Raise : sig ... endThis module provide convenience facilities to raise exception or other exception management
module RngMap : sig ... endA 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 ... endmodule Vec : sig ... endThis module provide a resizable array. It is a rename of Res.array with added features
module WeakMap : sig ... endThis 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 ... endThis module provide a simple weak pointer that can be rendered invalid by the GC
Z3.CheckContextval counter : Utils.Counter.tval openc : unit -> unitval num : unit -> intval closec : unit -> unitZ3.CheckContextContextCounter.SZ3.ContextCounterModule 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.tval openc : unit -> unitOpen a new context of that ContextCounter. Ensure the server is started
val closec : unit -> unitClose a context opened with openc. Assert that the current context was indeed opened by this module
Z3.ContextCounterModule 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.Stringval counter : Utils.Counter.tClose a context opened with openc. Assert that the current context was indeed opened by this module
Make.1-VarMake.VarZ3.Maketype var = Var.tThe 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.tThe type of expression on which SMT operation are made
val declare_var_always : server -> var -> unitDeclare the variable regardless of whether it has already been declared
val send_assert_decl : server -> declared:unit Htbl.t -> exp -> unitLiterally just declare_vars then send_assert
val check : server -> exp -> bool optionCheck 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 -> expDo 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 optionDo a standalone check of whether the expression is implied by the list of hypothesis.
val check_sat_full : exp list -> bool optionDo a standalone check of whether the set of assertion is sat
Z3.Maketype var = Var.tThe 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.tThe 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 optionDo a standalone check of whether the set of assertion is sat
Z3.SimpContextval counter : Utils.Counter.tval openc : unit -> unitval num : unit -> intval closec : unit -> unitZ3.SimpContextZ3This 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 listThe type of a SMT context stack
val start_context : contextThe starting context
val context_elem_to_string : context_elem -> stringGive 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.refThe global Z3 server
val get_server : unit -> serverAssume the server is started and returns it.
val get_context_string : server -> stringGive a string representation of the current context for error reporting
val incr_context : server -> unitIncrement 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 -> unitSend 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 -> unitSend a smt statement to the server and increment the declaration number in the context
val read_string : server -> stringRead a string from the server (A Z3 answer is always a valid sexp)
val read_smt_ans : server -> Ast.rsmt_ansRead 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_ansMake 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 -> unitSend a command or a declaration to the server
val expect_version : server -> Ast.rsmt_ans -> stringExpect a version answer and fails if it is not the case
val expect_exp : server -> Ast.rsmt_ans -> Ast.rexpExpect an expression answer and fails if it is not the case
val is_context_sat : server -> bool optionCheck if the current context is sat
val ensure_stopped : unit -> unitCall stop if the server wasn't stopped
val ensure_started : unit -> unitCall 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 -> unitOpen a new context with a name.
val close_context : server -> unitCloses current context of the server
module ContextCounter : functor (S : Utils.Logs.String) -> sig ... endModule 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 ... endThe functors in this section require a bit more support from variable than plain Exp.Var
module type S = sig ... endmodule SimpContext : sig ... endmodule CheckContext : sig ... endZ3This 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 listThe type of a SMT context stack
val start_context : contextThe starting context
val context_elem_to_string : context_elem -> stringGive 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.refThe global Z3 server
val get_server : unit -> serverAssume the server is started and returns it.
val get_context_string : server -> stringGive a string representation of the current context for error reporting
val incr_context : server -> unitIncrement 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 -> unitSend 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 ->
+ unitSend a smt statement to the server and increment the declaration number in the context
val read_string : server -> stringRead a string from the server (A Z3 answer is always a valid sexp)
val read_smt_ans : server -> Ast.rsmt_ansRead 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_ansMake 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 ->
+ unitSend a command or a declaration to the server
val expect_version : server -> Ast.rsmt_ans -> stringExpect a version answer and fails if it is not the case
val expect_exp : server -> Ast.rsmt_ans -> Ast.rexpExpect an expression answer and fails if it is not the case
val is_context_sat : server -> bool optionCheck 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 -> serverCall 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 -> unitOpen a new context with a name.
val close_context : server -> unitCloses current context of the server
module ContextCounter (S : Utils.Logs.String) : sig ... endModule 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 ... endThe functors in this section require a bit more support from variable than plain Exp.Var
module type S = sig ... endmodule SimpContext : sig ... endmodule CheckContext : sig ... endmodule Test : sig ... endZ3.SThe 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.tThe type of expression on which SMT operation are made
val declare_var_always : server -> var -> unitDeclare the variable regardless of whether it has already been declared
val send_assert_decl : server -> declared:unit Htbl.t -> exp -> unitLiterally just declare_vars then send_assert
val check : server -> exp -> bool optionCheck 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 -> expDo 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 optionDo a standalone check of whether the expression is implied by the list of hypothesis.
val check_sat_full : exp list -> bool optionDo a standalone check of whether the set of assertion is sat
Z3.SThe 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.tThe 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 optionDo a standalone check of whether the set of assertion is sat
Z3.VarThe functors in this section require a bit more support from variable than plain Exp.Var
Z3.VarThe 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 InstructionPipelineState 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 Traces and this set of traces is run on a State to get the next state. See SymbolicExecution.Utils.Pp and Utils.Logs. See PrintingHere 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 InstructionPipelineState 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 Traces and this set of traces is run on a State to get the next state. See SymbolicExecution.Utils.Pp and Utils.Logs. See PrintingHere 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