From 7e5a0dd3399d280b55143bfe272eafe92019cf53 Mon Sep 17 00:00:00 2001 From: Matej Urban Date: Thu, 2 Jan 2025 18:53:12 +0100 Subject: [PATCH 001/116] Parse relocatable file (hacky handling of relocations) --- notes-TODO | 3 +++ src/analyse/ControlFlow.ml | 4 +-- src/analyse/Elf.ml | 2 +- src/bin/copySources.ml | 4 +-- src/dw/dw.ml | 2 +- src/elf/file.ml | 47 ++++++++++++++--------------------- src/elf/linksemRelocatable.ml | 39 +++++++++++++++++++++++++++++ src/elf/symTable.ml | 12 ++++----- src/elf/symTable.mli | 4 +-- src/elf/symbol.ml | 38 +++++++++++++++++----------- src/elf/symbol.mli | 13 +++++++--- 11 files changed, 109 insertions(+), 59 deletions(-) create mode 100644 notes-TODO create mode 100644 src/elf/linksemRelocatable.ml diff --git a/notes-TODO b/notes-TODO new file mode 100644 index 00000000..c8e943ae --- /dev/null +++ b/notes-TODO @@ -0,0 +1,3 @@ +Symbolic symbol table +- value of symbol?? (we don't have segments in relocatable files) +- can probably keep the same api, but addresses are symbolic \ No newline at end of file diff --git a/src/analyse/ControlFlow.ml b/src/analyse/ControlFlow.ml index 166a80c3..9d9628c0 100644 --- a/src/analyse/ControlFlow.ml +++ b/src/analyse/ControlFlow.ml @@ -132,14 +132,14 @@ let branch_table_target_addresses test filename_branch_table_option : (addr * ad (* pull out .rodata section from ELF *) let ((_, rodata_addr, bs) as _rodata : Dwarf.p_context * Nat_big_num.num * BytesSeq.t) = - Dwarf.extract_section_body test.elf_file ".rodata" false + Dwarf.extract_section_body_without_relocations test.elf_file ".rodata" false in (* chop into bytes *) let rodata_bytes : char array = BytesSeq.to_array bs in (* chop into 4-byte words - as needed for branch offset tables, though not for all other things in .rodata *) - let rodata_words : (natural * natural) list = Dwarf.words_of_byte_sequence rodata_addr bs [] in + let rodata_words : (natural * natural) list = Dwarf.words_of_rel_byte_sequence rodata_addr (Dwarf.rbs_no_reloc bs) [] in (*HACK*) let read_rodata_b addr = Elf_types_native_uint.natural_of_byte diff --git a/src/analyse/Elf.ml b/src/analyse/Elf.ml index 0be22494..4b4bfc9b 100644 --- a/src/analyse/Elf.ml +++ b/src/analyse/Elf.ml @@ -143,7 +143,7 @@ let parse_elf_file (filename : string) : test = segments in let ds = - match Dwarf.extract_dwarf_static (Elf_file.ELF_File_64 f1) with + match Dwarf.extract_dwarf_static (Elf_file.ELF_File_64 f1) Abi_aarch64_symbolic_relocation.aarch64_data_relocation_interpreter with | None -> fatal "%s" "extract_dwarf_static failed" | Some ds -> (* Debug.print_string2 (Dwarf.pp_analysed_location_data ds.Dwarf.ds_dwarf diff --git a/src/bin/copySources.ml b/src/bin/copySources.ml index d664698c..094d3257 100644 --- a/src/bin/copySources.ml +++ b/src/bin/copySources.ml @@ -84,8 +84,8 @@ let process_file () : unit = else Some (Byte_sequence.string_of_byte_sequence - (List.nth lnh.lnh_include_directories (dir - 1)))), - Byte_sequence.string_of_byte_sequence lnfe.lnfe_path )) + (rbs_unwrap (List.nth lnh.lnh_include_directories (dir - 1))))), + Byte_sequence.string_of_byte_sequence (rbs_unwrap lnfe.lnfe_path) )) lnh.lnh_file_entries in diff --git a/src/dw/dw.ml b/src/dw/dw.ml index 270c3576..af505b53 100644 --- a/src/dw/dw.ml +++ b/src/dw/dw.ml @@ -85,7 +85,7 @@ let of_elf (elf : Elf.File.t) = Arch.load_elf_arch elf; info "Extracting dwarf of %s" elf.filename; let ldwarf = - match Dwarf.extract_dwarf elf.linksem with + match Dwarf.extract_dwarf elf.linksem Abi_aarch64_symbolic_relocation.aarch64_data_relocation_interpreter with | Some d -> d | None -> dwarferror "Linksem extract_dwarf failed" in diff --git a/src/elf/file.ml b/src/elf/file.ml index 4b49c026..e3d8925d 100644 --- a/src/elf/file.ml +++ b/src/elf/file.ml @@ -105,49 +105,40 @@ let elferror fmt = Printf.ksprintf (fun s -> raise (ElfError s)) fmt let of_file (filename : string) = info "Loading ELF file %s" filename; (* parse the ELF file using linksem *) - let ( (elf_file : Elf_file.elf_file), + let bs = match Byte_sequence.acquire filename with + | Error.Fail s -> elferror "Linksem: Byte_sequence.acquire: %s" s + | Error.Success x -> x + in + let elf64_file = match Elf_file.read_elf64_file bs with + | Error.Fail s -> elferror "Linksem: read_elf64_file: %s" s + | Error.Success x -> x + in + let symbol_map = match LinksemRelocatable.get_elf64_file_global_symbol_init elf64_file with + | Error.Fail s -> elferror "LinksemRelocatable: get_elf64_file_global_symbol_init: %s" s + | Error.Success x -> x + in + (* let ( (elf_file : Elf_file.elf_file), (elf_epi : Sail_interface.executable_process_image), (symbol_map : Elf_file.global_symbol_init_info) ) = match Sail_interface.populate_and_obtain_global_symbol_init_info filename with | Error.Fail s -> elferror "Linksem: populate_and_obtain_global_symbol_init_info: %s" s | Error.Success x -> x - in + in *) (* Check this is a 64 bits ELF file *) - begin - match elf_file with - | Elf_file.ELF_File_32 _ -> elferror "32 bits elf files unsupported" - | _ -> () - end; - let (segments, entry, machine) = - match elf_epi with - | ELF_Class_32 _ -> elferror "32 bits elf file class unsupported" - | ELF_Class_64 (s, e, m) -> (s, e, m) - in - - (* Extract all the segments *) - let segments = - List.filter_map - (fun (seg, prov) -> if prov = Elf_file.FromELF then Some seg else None) - segments - in - let entry = Z.to_int entry in - let machine = machine_of_linksem machine in - debug "Loading ELF segments of %s" filename; - let segments = List.map Segment.of_linksem segments in - debug "Loaded ELF segments %t" - @@ Pp.top (Pp.list Pp.hex) - @@ List.map (fun x -> x.Segment.addr) segments; + let entry = Z.to_int elf64_file.elf64_file_header.elf64_entry in + let machine = machine_of_linksem elf64_file.elf64_file_header.elf64_machine in debug "Loading ELF symbols of %s" filename; - let symbols = SymTbl.of_linksem segments symbol_map in + let symbols = SymTbl.of_linksem symbol_map in debug "Adding .rodata section of %s" filename; (* We add the .rodata section seperately from the symbols because - it can contain non-symbol information such as string literals and constants used in branch-register target calculations - the range of the section is guaranteed to overlap with any symbols within it, and so not suitable to be stored in the [RngMap] *) + let elf_file = Elf_file.ELF_File_64 elf64_file in let rodata = let (_, addr, data) = - Dwarf.extract_section_body elf_file ".rodata" false + Dwarf.extract_section_body_without_relocations elf_file ".rodata" false (* `false' argument is for returning an empty byte-sequence if section is not found, instead of throwing an exception *) in diff --git a/src/elf/linksemRelocatable.ml b/src/elf/linksemRelocatable.ml new file mode 100644 index 00000000..82b4e035 --- /dev/null +++ b/src/elf/linksemRelocatable.ml @@ -0,0 +1,39 @@ +(* TODO header *) + +type sym_addr = string * Z.t + +(* Like in linksem, but address is section+offset, and with a writable flag *) +type symbol = string * (Z.t * Z.t * sym_addr * Byte_sequence_wrapper.byte_sequence * Z.t) * bool + +type global_symbol_init_info = symbol list + +open Elf_symbol_table +open Elf_interpreted_section + +let get_elf64_file_global_symbol_init f : global_symbol_init_info Error.error = + let secs = f.Elf_file.elf64_file_interpreted_sections in + Error.bind (Elf_file.get_elf64_file_symbol_table f) (fun (symtab, strtab) -> + List.filter_map ( + fun entry -> + let name = Uint32_wrapper.to_bigint entry.elf64_st_name in + let addr_offset = Uint64_wrapper.to_bigint entry.elf64_st_value in + let size = Uint64_wrapper.to_bigint entry.elf64_st_size in + let shndx = Uint32_wrapper.to_int entry.elf64_st_shndx in + let typ = Elf_symbol_table.extract_symbol_type entry.elf64_st_info in + let bnd = Elf_symbol_table.extract_symbol_binding entry.elf64_st_info in + Option.map ( + fun section -> + let addr = (section.elf64_section_name_as_string, addr_offset) in + let data = if Byte_sequence.length0 section.elf64_section_body = Z.zero then + Error.return (Byte_sequence.zeros size) + else + Byte_sequence.offset_and_cut addr_offset size section.elf64_section_body + in + Error.bind data (fun data -> + Error.bind (String_table.get_string_at name strtab) (fun str -> + let write = Elf_file.flag_is_set Elf_section_header_table.shf_write section.elf64_section_flags in + Error.return (str, (typ, size, addr, data, bnd), write) + )) + ) (List.nth_opt secs shndx) + ) symtab |> Error.mapM Fun.id + ) \ No newline at end of file diff --git a/src/elf/symTable.ml b/src/elf/symTable.ml index d1b2ecd7..8ef56b4d 100644 --- a/src/elf/symTable.ml +++ b/src/elf/symTable.ml @@ -59,7 +59,7 @@ type sym_offset = sym * int module RMap = RngMap.Make (Symbol) module SMap = Map.Make (String) -type linksem_t = Elf_file.global_symbol_init_info +type linksem_t = LinksemRelocatable.global_symbol_init_info type t = { by_name : sym SMap.t; by_addr : RMap.t } @@ -111,11 +111,11 @@ let of_position_string t s : sym_offset = if s = "" then raise Not_found; if s.[0] = '0' then of_addr_with_offset t (int_of_string s) else sym_offset_of_string t s -let of_linksem segments linksem_map = - let add_linksem_sym_to_map (map : t) (lsym : linksem_sym) = - if is_interesting_linksem lsym then add map (Symbol.of_linksem segments lsym) else map - in - List.fold_left add_linksem_sym_to_map empty linksem_map + let of_linksem linksem_map = + let add_linksem_sym_to_map (map : t) (lsym : linksem_sym) = + if is_interesting_linksem lsym then add map (Symbol.of_linksem lsym) else map + in + List.fold_left add_linksem_sym_to_map empty linksem_map let pp_raw st = RMap.bindings st.by_addr |> List.map (Pair.map Pp.ptr pp_raw) |> Pp.mapping "syms" diff --git a/src/elf/symTable.mli b/src/elf/symTable.mli index bcd57ca5..8cb35435 100644 --- a/src/elf/symTable.mli +++ b/src/elf/symTable.mli @@ -57,7 +57,7 @@ type linksem_sym = Symbol.linksem_t (** The type of a symbol with offset *) type sym_offset = sym * int -type linksem_t = Elf_file.global_symbol_init_info +type linksem_t = LinksemRelocatable.global_symbol_init_info (** The type of a symbol table. *) type t @@ -110,7 +110,7 @@ val of_position_string : t -> string -> sym_offset (** Extract the symbol from the linksem symbol representation. Need the segments for filling the missing symbol data *) -val of_linksem : Segment.t list -> linksem_t -> t +val of_linksem : linksem_t -> t (** Pretty print the table as a raw ocaml value *) val pp_raw : t -> Pp.document diff --git a/src/elf/symbol.ml b/src/elf/symbol.ml index 439dc0ec..d6e0baa3 100644 --- a/src/elf/symbol.ml +++ b/src/elf/symbol.ml @@ -42,27 +42,38 @@ (* *) (*==================================================================================*) +open Logs.Logger (struct + let str = __MODULE__ +end) + (* The documentation is in the mli file *) type typ = NOTYPE | OBJECT | FUNC | SECTION | FILE | UNKNOWN type linksem_typ = Z.t +(* TODO: move somewhere to reuse *) +type addr = { + section : string; + offset: int; +} + type t = { name : string; other_names : string list; typ : typ; + (* addr : addr; *) addr : int; size : int; writable : bool; data : BytesSeq.t; } -type linksem_t = string * (Z.t * Z.t * Z.t * BytesSeq.t option * Z.t) +type linksem_t = LinksemRelocatable.symbol let push_name s t = { t with other_names = s :: t.other_names } -let is_in t addr = t.addr <= addr && addr < t.addr + t.size +(* let is_in t addr = t.addr <= addr && addr < t.addr + t.size *) let len t = t.size @@ -75,7 +86,7 @@ let typ_of_linksem ltyp = | 4 -> FILE | _ -> UNKNOWN -let linksem_typ (_name, (typ, _size, _addr, _data, _)) = typ +let linksem_typ (_name, (typ, _size, _addr, _data, _), _) = typ (** [LoadingError(name,addr)] means that symbol [name] at [addr] could not be loaded *) exception LoadingError of string * int @@ -86,19 +97,17 @@ let _ = Some (Printf.sprintf "Symbol %s at 0x%x could not be loaded" name addr) | _ -> None) -let of_linksem segs (name, (typ, size, addr, data, _)) = +(* for debugging TODO remove *) +module SMap = Map.Make (String) +let locs = SMap.empty |> SMap.add ".text" 0 |> SMap.add ".data" 1000000 |> SMap.add ".eh_frame" 2000000 + +let of_linksem (name, (typ, size, addr, data, _), writable) = let typ = typ_of_linksem typ in let size = Z.to_int size in - let addr = Z.to_int addr in - let segment = - Option.value_fail (Segment.get_containing segs addr) "No segment contains symbol %s" name - in - let writable = segment.write in - let data = - data - |> Option.value_fun ~default:(fun () -> - Segment.get_addr (BytesSeq.getbs ~len:size) segment addr) - in + let section, offset = addr in + (* let addr = { section; offset = Z.to_int offset } in *) + let addr = SMap.find section locs + Z.to_int offset in + debug "Symbol %s at address %s+%d (using %d)" name section (Z.to_int offset) addr; { name; other_names = []; typ; size; addr; data; writable } let is_interesting = function OBJECT | FUNC -> true | _ -> false @@ -128,6 +137,7 @@ let pp_raw sym = ("name", !^(sym.name)); ("other names", separate nbspace (List.map string sym.other_names)); ("typ", pp_typ sym.typ); + (* ("addr", !^(sym.addr.section) ^^ !^"+" ^^ ptr sym.addr.offset); *) ("addr", ptr sym.addr); ("size", ptr sym.size); ("writable", bool sym.writable); diff --git a/src/elf/symbol.mli b/src/elf/symbol.mli index e557439f..51213545 100644 --- a/src/elf/symbol.mli +++ b/src/elf/symbol.mli @@ -55,12 +55,19 @@ type typ = NOTYPE | OBJECT | FUNC | SECTION | FILE | UNKNOWN type linksem_typ = Z.t +(* TODO: move somewhere to reuse *) +type addr = { + section : string; + offset: int; +} + (** The ELF symbol. This type guarantee the data exists contrary to linksem symbols (it may be all zeros though) *) type t = { name : string; other_names : string list; typ : typ; + (* addr : addr; *) addr : int; size : int; writable : bool; @@ -68,13 +75,13 @@ type t = { } (** The type of an ELF symbol in linksem. See {!of_linksem}*) -type linksem_t = string * (Z.t * Z.t * Z.t * BytesSeq.t option * Z.t) +type linksem_t = LinksemRelocatable.symbol (** Add a name to the other names list *) val push_name : string -> t -> t (** Check if an address is in a symbol *) -val is_in : t -> int -> bool +(* val is_in : t -> int -> bool *) (** For conformance with the {!Utils.RngMap.LenObject} module type *) val len : t -> int @@ -93,7 +100,7 @@ exception LoadingError of string * int May raise {!LoadingError} when the symbol has no data and the data cannot be found in the segments *) -val of_linksem : Segment.t list -> linksem_t -> t +val of_linksem : linksem_t -> t (** Tell if a symbol type is interesting for readDwarf purposes *) val is_interesting : typ -> bool From ecb6af3cd66655757460144e55ca559f2435a482 Mon Sep 17 00:00:00 2001 From: Matej Urban Date: Fri, 3 Jan 2025 18:20:24 +0100 Subject: [PATCH 002/116] Update config --- src/config/isla_aarch64.toml | 150 ++++++++--------------------------- 1 file changed, 35 insertions(+), 115 deletions(-) diff --git a/src/config/isla_aarch64.toml b/src/config/isla_aarch64.toml index cf209041..27d6e500 100644 --- a/src/config/isla_aarch64.toml +++ b/src/config/isla_aarch64.toml @@ -1,61 +1,24 @@ -#==================================================================================# -# BSD 2-Clause License # -# # -# Copyright (c) 2020-2021 Thibaut Pérami # -# Copyright (c) 2020-2021 Dhruv Makwana # -# Copyright (c) 2019-2021 Peter Sewell # -# All rights reserved. # -# # -# This software was developed by the University of Cambridge Computer # -# Laboratory as part of the Rigorous Engineering of Mainstream Systems # -# (REMS) project. # -# # -# This project has been partly funded by EPSRC grant EP/K008528/1. # -# This project has received funding from the European Research Council # -# (ERC) under the European Union's Horizon 2020 research and innovation # -# programme (grant agreement No 789108, ERC Advanced Grant ELVER). # -# This project has been partly funded by an EPSRC Doctoral Training studentship. # -# This project has been partly funded by Google. # -# # -# Redistribution and use in source and binary forms, with or without # -# modification, are permitted provided that the following conditions # -# are met: # -# 1. Redistributions of source code must retain the above copyright # -# notice, this list of conditions and the following disclaimer. # -# 2. Redistributions in binary form must reproduce the above copyright # -# notice, this list of conditions and the following disclaimer in # -# the documentation and/or other materials provided with the # -# distribution. # -# # -# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' # -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED # -# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A # -# PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR # -# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, # -# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT # -# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF # -# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND # -# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, # -# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT # -# OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF # -# SUCH DAMAGE. # -# # -#==================================================================================# - # This is a config file for the Sail generated from ARM-ASL -# It is copied from the Isla repo and should be synced periodically. -# It probably contains a lot of stuff that's not needed for read-dwarf. pc = "_PC" ifetch = "Read_ifetch" -read_exclusives = ["Read_exclusive", "Read_exclusive_acquire"] -write_exclusives = ["Write_exclusive", "Write_exclusive_release"] +in_program_order = ["sail_barrier"] # The assembler is used for assembling the code in litmus tests. We # assume it takes arguments like GNU as. -assembler = "aarch64-linux-gnu-as -march=armv8.3-a" # read-dwarf +[[toolchain]] +name = "macos-aarch64" +os = "macos" +arch = "aarch64" +assembler = "as --target=aarch64-unknown-linux-gnu" +objdump = "/opt/homebrew/opt/llvm/bin/llvm-objdump" +linker = "/opt/homebrew/opt/llvm/bin/ld.lld" + +[[toolchain]] +name = "default" +assembler = "aarch64-linux-gnu-as -march=armv8.1-a" objdump = "aarch64-linux-gnu-objdump" linker = "aarch64-linux-gnu-ld" @@ -84,6 +47,8 @@ stride = "0x10" [registers] ignore = [ + "_PC", + "__PC_changed", "SEE", "__unconditional", "__trickbox_enabled", @@ -91,25 +56,15 @@ ignore = [ "__v82_implemented", "__v83_implemented", "__v84_implemented", - "__v85_implemented", - "_GTEExtObsAccess", - "_GTEExtObsActive", - "_GTEExtObsAddress", - "_GTEExtObsCount", - "_GTEExtObsData", - "_GTEExtObsIndex", - "_GTEExtObsResult", - "_GTEExtObsResultIndex", - "_GTEExtObsResultIsAddress", - "_GTE_PPU_Access", - "_GTE_PPU_Address", - "_GTE_PPU_SizeEn" + "__v85_implemented" ] # These registers are set before any symbolic execution occurs [registers.defaults] -"__isla_monomorphize_reads" = false -"__isla_monomorphize_writes" = false +"__isla_vector_gpr" = false +"__isla_continue_on_see" = true +"__monomorphize_reads" = false +"__monomorphize_writes" = false "VBAR_EL1" = "0x0000000000000000" "VBAR_EL2" = "0x0000000000000000" # Causes CNTCV to be incremented every cycle if bit 0 is 1 @@ -117,6 +72,7 @@ ignore = [ # SSAdvance? "MDSCR_EL1" = "0x00000000" "InGuardedPage" = false +"__highest_el_aarch32" = false "__currentInstrLength" = 4 "_PendingPhysicalSE" = false "__CNTControlBase" = "0x0000000000000" @@ -129,19 +85,18 @@ ignore = [ "CFG_RMR_AA64" = "0b1" "CFG_RVBAR" = "0x0000000010300000" "CFG_ID_AA64PFR0_EL1_MPAM" = "0x1" -"CFG_ID_AA64PFR0_EL1_EL3" = "0x2" -"CFG_ID_AA64PFR0_EL1_EL2" = "0x2" -"CFG_ID_AA64PFR0_EL1_EL1" = "0x2" -"CFG_ID_AA64PFR0_EL1_EL0" = "0x2" +"CFG_ID_AA64PFR0_EL1_EL3" = "0x1" +"CFG_ID_AA64PFR0_EL1_EL2" = "0x1" +"CFG_ID_AA64PFR0_EL1_EL1" = "0x1" +"CFG_ID_AA64PFR0_EL1_EL0" = "0x1" # Need to investigate BTI extension. Guard pages cause problems with # memory accesses. "__v81_implemented" = true -"__v82_implemented" = true # read-dwarf -"__v83_implemented" = true # read-dwarf +"__v82_implemented" = false +"__v83_implemented" = false "__v84_implemented" = false "__v85_implemented" = false "__unpred_tsize_aborts" = true -"exclusive_never_fails" = true # read-dwarf # Trickbox has various features for debugging spec and running tests "__trickbox_enabled" = false "__tlb_enabled" = false @@ -173,12 +128,19 @@ ignore = [ "__crypto_sm3_implemented" = false "__crypto_sha512_implemented" = false "__crypto_sha3_implemented" = false +"_GTEExtObsAccess" = "[0x0000; 256]" +"_GTEExtObsAddress" = "[0x0000000000000000; 256]" +"_GTEExtObsData" = "[0x0000000000000000; 256]" +"_GTEExtObsResult" = "[0x0000000000000000; 256]" +"_GTE_PPU_SizeEn" = "[0x00000000; 6]" +"_GTE_PPU_Address" = "[0x0000000000000000; 6]" +"_GTE_PPU_Access" = "[0x00000000; 6]" # These registers are set during symbolic execution by the special builtin "reset_registers" [registers.reset] -# Bit 1 being set causes us to abort on unaligned accesses +# Bit 1 being unset allows unaligned accesses # Bit 26 being set allows cache-maintenance ops in EL0 -"SCTLR_EL1" = "0x0000000004000002" +"SCTLR_EL1" = "0x0000000004000000" # A map from register names that may appear in litmus files to Sail # register names @@ -245,45 +207,3 @@ ignore = [ "W28" = "R28" "W29" = "R29" "W30" = "R30" - -[reads] -Read_acquire = "A" -Read_exclusive_acquire = "A" - -[writes] -Write_release = "L" -Write_exclusive_release = "L" - -[cache_ops] -Cache_op_D_CVAU = "DC" -Cache_op_I_IVAU = "IC" -Cache_op_I_IALLU = "IC" - -# A mapping from Sail barrier_kinds for the spec to the names in cat -# memory models. -[barriers] -Barrier_DMB_SY = "DMB.SY" -Barrier_DMB_ST = "DMB.ST" -Barrier_DMB_LD = "DMB.LD" -Barrier_DMB_ISH = "DMB.ISH" -Barrier_DMB_ISHST = "DMB.ISHST" -Barrier_DMB_ISHLD = "DMB.ISHLD" -Barrier_DMB_NSH = "DMB.NSH" -Barrier_DMB_NSHST = "DMB.NSHST" -Barrier_DMB_NSHLD = "DMB.NSHLD" -Barrier_DMB_OSH = "DMB.OSH" -Barrier_DMB_OSHST = "DMB.OSHST" -Barrier_DMB_OSHLD = "DMB.OSHLD" -Barrier_DSB_SY = "DSB.SY" -Barrier_DSB_ST = "DSB.ST" -Barrier_DSB_LD = "DSB.LD" -Barrier_DSB_ISH = "DSB.ISH" -Barrier_DSB_ISHST = "DSB.ISHST" -Barrier_DSB_ISHLD = "DSB.ISHLD" -Barrier_DSB_NSH = "DSB.NSH" -Barrier_DSB_NSHST = "DSB.NSHST" -Barrier_DSB_NSHLD = "DSB.NSHLD" -Barrier_DSB_OSH = "DSB.OSH" -Barrier_DSB_OSHST = "DSB.OSHST" -Barrier_DSB_OSHLD = "DSB.OSHLD" -Barrier_ISB = "ISB" From 7514a8a69385deba347330a860e338d4ce078b1a Mon Sep 17 00:00:00 2001 From: Matej Urban Date: Sat, 4 Jan 2025 19:31:52 +0100 Subject: [PATCH 003/116] Symbolic symbol table --- src/elf/symTable.ml | 71 ++++++++++++++++++++++++++++++++++---------- src/elf/symTable.mli | 10 +++---- src/elf/symbol.ml | 19 ++++++------ src/elf/symbol.mli | 7 +++-- 4 files changed, 75 insertions(+), 32 deletions(-) diff --git a/src/elf/symTable.ml b/src/elf/symTable.ml index 8ef56b4d..dee03749 100644 --- a/src/elf/symTable.ml +++ b/src/elf/symTable.ml @@ -59,19 +59,57 @@ type sym_offset = sym * int module RMap = RngMap.Make (Symbol) module SMap = Map.Make (String) +module AddrMap = struct + type t = RMap.t SMap.t + + let add t addr sym = + SMap.update addr.section (fun old -> + let old = match old with + | None -> RMap.empty + | Some x -> x + in + Some (RMap.add old addr.offset sym) + ) t + + let update f t addr = + SMap.update addr.section (Option.map (fun x -> RMap.update f x addr.offset)) t + + let empty = SMap.empty + + let at t addr = + SMap.find addr.section t |> Fun.flip RMap.at addr.offset + + let at_opt t addr = + Option.bind (SMap.find_opt addr.section t) @@ Fun.flip RMap.at_opt addr.offset + + let at_off t addr = + SMap.find addr.section t |> Fun.flip RMap.at_off addr.offset + + let at_off_opt t addr = + Option.bind (SMap.find_opt addr.section t) @@ Fun.flip RMap.at_off_opt addr.offset + + let bindings t = + let sections = SMap.bindings t in + List.bind sections @@ fun (section, rmap) -> + let inner_bindings = RMap.bindings rmap in + List.map (fun (offset, sym) -> ({section; offset}, sym)) inner_bindings + + +end + type linksem_t = LinksemRelocatable.global_symbol_init_info -type t = { by_name : sym SMap.t; by_addr : RMap.t } +type t = { by_name : sym SMap.t; by_addr : AddrMap.t } -let empty = { by_name = SMap.empty; by_addr = RMap.empty } +let empty = { by_name = SMap.empty; by_addr = AddrMap.empty } let add t sym = let by_name = SMap.add sym.name sym t.by_name in - try { by_name; by_addr = RMap.add t.by_addr sym.addr sym } + try { by_name; by_addr = AddrMap.add t.by_addr sym.addr sym } with Invalid_argument _ -> let updated = ref false in let by_addr = - RMap.update + AddrMap.update (fun usym -> if usym.addr = sym.addr && usym.size = sym.size then begin updated := true; @@ -88,15 +126,15 @@ let of_name t name = let of_name_opt t name = SMap.find_opt name t.by_name -let of_addr t addr = RMap.at t.by_addr addr +let of_addr t addr = AddrMap.at t.by_addr addr -let of_addr_opt t addr = RMap.at_opt t.by_addr addr +let of_addr_opt t addr = AddrMap.at_opt t.by_addr addr -let of_addr_with_offset t addr = RMap.at_off t.by_addr addr +let of_addr_with_offset t addr = AddrMap.at_off t.by_addr addr -let of_addr_with_offset_opt t addr = RMap.at_off_opt t.by_addr addr +let of_addr_with_offset_opt t addr = AddrMap.at_off_opt t.by_addr addr -let to_addr_offset (sym, offset) = sym.addr + offset +let to_addr_offset (sym, offset) = { section = sym.addr.section; offset = sym.addr.offset + offset } let string_of_sym_offset ((sym, off) : sym_offset) = sym.name ^ "+" ^ string_of_int off @@ -109,15 +147,16 @@ let sym_offset_of_string t s : sym_offset = let of_position_string t s : sym_offset = let s = String.trim s in if s = "" then raise Not_found; - if s.[0] = '0' then of_addr_with_offset t (int_of_string s) else sym_offset_of_string t s + if s.[0] = '0' then raise Not_found (* no absolute addresses *) + else sym_offset_of_string t s - let of_linksem linksem_map = - let add_linksem_sym_to_map (map : t) (lsym : linksem_sym) = - if is_interesting_linksem lsym then add map (Symbol.of_linksem lsym) else map - in - List.fold_left add_linksem_sym_to_map empty linksem_map +let of_linksem linksem_map = + let add_linksem_sym_to_map (map : t) (lsym : linksem_sym) = + if is_interesting_linksem lsym then add map (Symbol.of_linksem lsym) else map + in + List.fold_left add_linksem_sym_to_map empty linksem_map -let pp_raw st = RMap.bindings st.by_addr |> List.map (Pair.map Pp.ptr pp_raw) |> Pp.mapping "syms" +let pp_raw st = AddrMap.bindings st.by_addr |> List.map (Pair.map pp_addr pp_raw) |> Pp.mapping "syms" let iter t f = SMap.iter (fun _ value -> f value) t.by_name diff --git a/src/elf/symTable.mli b/src/elf/symTable.mli index 8cb35435..0b919a8a 100644 --- a/src/elf/symTable.mli +++ b/src/elf/symTable.mli @@ -78,19 +78,19 @@ val of_name_opt : t -> string -> sym option (** Get the symbol owning that address. Not_found is raised if no symbol own that address.data See {!of_addr_opt} *) -val of_addr : t -> int -> sym +val of_addr : t -> Symbol.addr -> sym (** Get the symbol owning that address. None if no symbol own that address. See {!of_addr} *) -val of_addr_opt : t -> int -> sym option +val of_addr_opt : t -> Symbol.addr -> sym option (** Get a symbol with the offset that correspond to that address *) -val of_addr_with_offset : t -> int -> sym_offset +val of_addr_with_offset : t -> Symbol.addr -> sym_offset (** Get a symbol with the offset that correspond to that address *) -val of_addr_with_offset_opt : t -> int -> sym_offset option +val of_addr_with_offset_opt : t -> Symbol.addr -> sym_offset option (** Get back the raw address from a symbol+offset value *) -val to_addr_offset : sym_offset -> int +val to_addr_offset : sym_offset -> Symbol.addr (** Transform a symbol + offset into the corresponding string *) val string_of_sym_offset : sym_offset -> string diff --git a/src/elf/symbol.ml b/src/elf/symbol.ml index d6e0baa3..bcbd00ae 100644 --- a/src/elf/symbol.ml +++ b/src/elf/symbol.ml @@ -62,8 +62,8 @@ type t = { name : string; other_names : string list; typ : typ; - (* addr : addr; *) - addr : int; + addr : addr; + (* addr : int; *) size : int; writable : bool; data : BytesSeq.t; @@ -98,16 +98,15 @@ let _ = | _ -> None) (* for debugging TODO remove *) -module SMap = Map.Make (String) -let locs = SMap.empty |> SMap.add ".text" 0 |> SMap.add ".data" 1000000 |> SMap.add ".eh_frame" 2000000 +(* module SMap = Map.Make (String) +let locs = SMap.empty |> SMap.add ".text" 0 |> SMap.add ".data" 1000000 |> SMap.add ".eh_frame" 2000000 *) let of_linksem (name, (typ, size, addr, data, _), writable) = let typ = typ_of_linksem typ in let size = Z.to_int size in let section, offset = addr in - (* let addr = { section; offset = Z.to_int offset } in *) - let addr = SMap.find section locs + Z.to_int offset in - debug "Symbol %s at address %s+%d (using %d)" name section (Z.to_int offset) addr; + let addr = { section; offset = Z.to_int offset } in + (* let addr = SMap.find section locs + Z.to_int offset in *) { name; other_names = []; typ; size; addr; data; writable } let is_interesting = function OBJECT | FUNC -> true | _ -> false @@ -129,6 +128,8 @@ let pp_typ typ = | FILE -> "FILE" | UNKNOWN -> "UNKNOWN" +let pp_addr addr = Pp.(!^(addr.section) ^^ !^"+" ^^ ptr addr.offset) + let pp_raw sym = Pp.( !^"sym" @@ -137,8 +138,8 @@ let pp_raw sym = ("name", !^(sym.name)); ("other names", separate nbspace (List.map string sym.other_names)); ("typ", pp_typ sym.typ); - (* ("addr", !^(sym.addr.section) ^^ !^"+" ^^ ptr sym.addr.offset); *) - ("addr", ptr sym.addr); + ("addr", pp_addr sym.addr); + (* ("addr", ptr sym.addr); *) ("size", ptr sym.size); ("writable", bool sym.writable); ("data", BytesSeq.ppby ~by:4 sym.data); diff --git a/src/elf/symbol.mli b/src/elf/symbol.mli index 51213545..5b5e9e6f 100644 --- a/src/elf/symbol.mli +++ b/src/elf/symbol.mli @@ -67,8 +67,8 @@ type t = { name : string; other_names : string list; typ : typ; - (* addr : addr; *) - addr : int; + addr : addr; + (* addr : int; *) size : int; writable : bool; data : BytesSeq.t; @@ -117,5 +117,8 @@ val compare : t -> t -> int (** Pretty prints a symbol type *) val pp_typ : typ -> Pp.document +(** Pretty prints symbolic address *) +val pp_addr : addr -> Pp.document + (** Raw pretty printing of a symbol *) val pp_raw : t -> Pp.document From 58091d76021cef4260b7823208af12a57841c6bf Mon Sep 17 00:00:00 2001 From: Matej Urban Date: Sat, 4 Jan 2025 20:14:36 +0100 Subject: [PATCH 004/116] Convert to symbolic addresses to make things compile TODO complete the logic --- src/dw/func.ml | 3 ++- src/dw/loc.ml | 3 ++- src/state/base.ml | 3 ++- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/dw/func.ml b/src/dw/func.ml index 32f64174..e8738d24 100644 --- a/src/dw/func.ml +++ b/src/dw/func.ml @@ -140,7 +140,8 @@ let of_linksem (elf : Elf.File.t) (tenv : Ctype.env) (lfun : linksem_t) = | None -> ( match lfun.ss_entry_address with | Some a -> ( - match Elf.SymTable.of_addr_opt elf.symbols (Nat_big_num.to_int a) with + let addr = Elf.Symbol.{section = ".text"; offset = Nat_big_num.to_int a} in (* TODO this is wrong, need symbolic DWARF *) + match Elf.SymTable.of_addr_opt elf.symbols addr with | Some sym -> Some sym | None -> None ) diff --git a/src/dw/loc.ml b/src/dw/loc.ml index 7b194e3e..bcd4eda0 100644 --- a/src/dw/loc.ml +++ b/src/dw/loc.ml @@ -120,7 +120,8 @@ let of_linksem ?(amap = Arch.dwarf_reg_map ()) (elf : Elf.File.t) : linksem_t -> (* Global *) | [{ op_semantics = OpSem_lit; op_code = code; op_argument_values = [arg]; _ }] as ops when Z.to_int code = vDW_OP_addr -> ( - try Global (Elf.SymTable.of_addr_with_offset elf.symbols @@ int_of_oav arg) + let addr = Elf.Symbol.{ section = ".data"; offset = int_of_oav arg } in (* TODO this is wrong, need symbolic DWARF*) + try Global (Elf.SymTable.of_addr_with_offset elf.symbols @@ addr) with Not_found -> warn "Symbol at 0x%x not found in Loc.of_linksem" (int_of_oav arg); Dwarf ops diff --git a/src/state/base.ml b/src/state/base.ml index 52cad8a8..33d4de14 100644 --- a/src/state/base.ml +++ b/src/state/base.ml @@ -429,9 +429,10 @@ let read_from_rodata (s : t) ~(addr : Exp.t) ~(size : Mem.Size.t) : Exp.t option if not @@ ConcreteEval.is_concrete addr then None else let int_addr = ConcreteEval.eval addr |> Value.expect_bv |> BitVec.to_int in + let sym_addr = Elf.Symbol.{ section = ".rodata"; offset = int_addr } in (* TODO this is wrong *) let size = size |> Ast.Size.to_bits in try - let (sym, offset) = Elf.SymTable.of_addr_with_offset elf.symbols int_addr in + let (sym, offset) = Elf.SymTable.of_addr_with_offset elf.symbols sym_addr in if sym.writable then None else (* Assume little endian here *) From 03ce63b6e1f1a5764d59712afe884c637b4d78fd Mon Sep 17 00:00:00 2001 From: Matej Urban Date: Sat, 4 Jan 2025 20:16:54 +0100 Subject: [PATCH 005/116] DIsable typing --- src/trace/run.ml | 14 ++++++++------ src/trace/typer.ml | 4 ++-- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/src/trace/run.ml b/src/trace/run.ml index bd8eb38d..986d3b53 100644 --- a/src/trace/run.ml +++ b/src/trace/run.ml @@ -70,10 +70,11 @@ let expand ~(ctxt : ctxt) (exp : Base.exp) : State.exp = otherwise the type will be [None] *) let expand_tval ~(ctxt : ctxt) (exp : Base.exp) : State.tval = let sexp = expand ~ctxt exp in - if Ctxt.typing_enabled ~ctxt then + (* if Ctxt.typing_enabled ~ctxt then let ctyp = Typer.expr ~ctxt exp in { ctyp; exp = sexp } - else { ctyp = None; exp = sexp } + else *) + { ctyp = None; exp = sexp } (** Run the event. The modified state is the one inside [ctxt]. *) @@ -85,22 +86,23 @@ let event_mut ~(ctxt : ctxt) (event : Base.event) = | ReadMem { addr; value; size } -> let naddr = expand ~ctxt addr in let tval = - match ctxt.dwarf with + (* match ctxt.dwarf with | Some dwarf -> let ptrtype = Typer.expr ~ctxt addr in Typer.read ~dwarf ctxt.state ?ptrtype ~addr:naddr ~size - | None -> State.read_noprov ctxt.state ~addr:naddr ~size |> State.Tval.of_exp + | None -> *) + State.read_noprov ctxt.state ~addr:naddr ~size |> State.Tval.of_exp in HashVector.set ctxt.mem_reads value tval | WriteMem { addr; value; size } -> ( let naddr = expand ~ctxt addr in - match ctxt.dwarf with + (* match ctxt.dwarf with | Some dwarf -> let ptrtype = Typer.expr ~ctxt addr in debug "Typed write mem with ptr:%t" (Pp.top (Pp.opt Ctype.pp) ptrtype); let value = expand_tval ~ctxt value in Typer.write ~dwarf ctxt.state ?ptrtype ~addr:naddr ~size value - | None -> + | None -> *) let value = expand ~ctxt value in State.write_noprov ctxt.state ~addr:naddr ~size value ) diff --git a/src/trace/typer.ml b/src/trace/typer.ml index e83f3284..f7b0f6de 100644 --- a/src/trace/typer.ml +++ b/src/trace/typer.ml @@ -1,4 +1,4 @@ -(*==================================================================================*) +(* ================================================================================== (* BSD 2-Clause License *) (* *) (* Copyright (c) 2020-2021 Thibaut Pérami *) @@ -314,4 +314,4 @@ let write ~(dwarf : Dw.t) (s : State.t) ?(ptrtype : Ctype.t option) ~addr ~size State.write ~provenance s ~addr ~size value.exp | _ -> warn "Writing without provenance"; - State.write_noprov s ~addr ~size value.exp + State.write_noprov s ~addr ~size value.exp *) From 261484664121657870d1dfc17c0cd2e2cf7c66ce Mon Sep 17 00:00:00 2001 From: Matej Urban Date: Sun, 5 Jan 2025 11:34:06 +0100 Subject: [PATCH 006/116] Refactor (symbolic) address to separate module --- src/dw/func.ml | 2 +- src/dw/loc.ml | 2 +- src/elf/address.ml | 10 ++++++++++ src/elf/symTable.ml | 18 +++++++++--------- src/elf/symTable.mli | 10 +++++----- src/elf/symbol.ml | 15 +++------------ src/elf/symbol.mli | 11 +---------- src/state/base.ml | 2 +- 8 files changed, 31 insertions(+), 39 deletions(-) create mode 100644 src/elf/address.ml diff --git a/src/dw/func.ml b/src/dw/func.ml index e8738d24..18fdd676 100644 --- a/src/dw/func.ml +++ b/src/dw/func.ml @@ -140,7 +140,7 @@ let of_linksem (elf : Elf.File.t) (tenv : Ctype.env) (lfun : linksem_t) = | None -> ( match lfun.ss_entry_address with | Some a -> ( - let addr = Elf.Symbol.{section = ".text"; offset = Nat_big_num.to_int a} in (* TODO this is wrong, need symbolic DWARF *) + let addr = Elf.Address.{section = ".text"; offset = Nat_big_num.to_int a} in (* TODO this is wrong, need symbolic DWARF *) match Elf.SymTable.of_addr_opt elf.symbols addr with | Some sym -> Some sym | None -> None diff --git a/src/dw/loc.ml b/src/dw/loc.ml index bcd4eda0..e9aacff2 100644 --- a/src/dw/loc.ml +++ b/src/dw/loc.ml @@ -120,7 +120,7 @@ let of_linksem ?(amap = Arch.dwarf_reg_map ()) (elf : Elf.File.t) : linksem_t -> (* Global *) | [{ op_semantics = OpSem_lit; op_code = code; op_argument_values = [arg]; _ }] as ops when Z.to_int code = vDW_OP_addr -> ( - let addr = Elf.Symbol.{ section = ".data"; offset = int_of_oav arg } in (* TODO this is wrong, need symbolic DWARF*) + let addr = Elf.Address.{ section = ".data"; offset = int_of_oav arg } in (* TODO this is wrong, need symbolic DWARF*) try Global (Elf.SymTable.of_addr_with_offset elf.symbols @@ addr) with Not_found -> warn "Symbol at 0x%x not found in Loc.of_linksem" (int_of_oav arg); diff --git a/src/elf/address.ml b/src/elf/address.ml new file mode 100644 index 00000000..c9f3b66b --- /dev/null +++ b/src/elf/address.ml @@ -0,0 +1,10 @@ +type t = { + section : string; + offset: int; +} + +let pp addr = Pp.(!^(addr.section) ^^ !^"+" ^^ ptr addr.offset) + +let of_linksem (section, offset) = { section; offset = Z.to_int offset } + +let (+) addr offset = { section = addr.section; offset = addr.offset + offset } \ No newline at end of file diff --git a/src/elf/symTable.ml b/src/elf/symTable.ml index dee03749..f0ac45a1 100644 --- a/src/elf/symTable.ml +++ b/src/elf/symTable.ml @@ -62,7 +62,7 @@ module SMap = Map.Make (String) module AddrMap = struct type t = RMap.t SMap.t - let add t addr sym = + let add t (addr: Address.t) sym = SMap.update addr.section (fun old -> let old = match old with | None -> RMap.empty @@ -71,28 +71,28 @@ module AddrMap = struct Some (RMap.add old addr.offset sym) ) t - let update f t addr = + let update f t (addr: Address.t) = SMap.update addr.section (Option.map (fun x -> RMap.update f x addr.offset)) t let empty = SMap.empty - let at t addr = + let at t (addr: Address.t) = SMap.find addr.section t |> Fun.flip RMap.at addr.offset - let at_opt t addr = + let at_opt t (addr: Address.t) = Option.bind (SMap.find_opt addr.section t) @@ Fun.flip RMap.at_opt addr.offset - let at_off t addr = + let at_off t (addr: Address.t) = SMap.find addr.section t |> Fun.flip RMap.at_off addr.offset - let at_off_opt t addr = + let at_off_opt t (addr: Address.t) = Option.bind (SMap.find_opt addr.section t) @@ Fun.flip RMap.at_off_opt addr.offset let bindings t = let sections = SMap.bindings t in List.bind sections @@ fun (section, rmap) -> let inner_bindings = RMap.bindings rmap in - List.map (fun (offset, sym) -> ({section; offset}, sym)) inner_bindings + List.map (fun (offset, sym) -> (Address.{section; offset}, sym)) inner_bindings end @@ -134,7 +134,7 @@ let of_addr_with_offset t addr = AddrMap.at_off t.by_addr addr let of_addr_with_offset_opt t addr = AddrMap.at_off_opt t.by_addr addr -let to_addr_offset (sym, offset) = { section = sym.addr.section; offset = sym.addr.offset + offset } +let to_addr_offset (sym, offset) = Address.(sym.addr + offset) let string_of_sym_offset ((sym, off) : sym_offset) = sym.name ^ "+" ^ string_of_int off @@ -156,7 +156,7 @@ let of_linksem linksem_map = in List.fold_left add_linksem_sym_to_map empty linksem_map -let pp_raw st = AddrMap.bindings st.by_addr |> List.map (Pair.map pp_addr pp_raw) |> Pp.mapping "syms" +let pp_raw st = AddrMap.bindings st.by_addr |> List.map (Pair.map Address.pp pp_raw) |> Pp.mapping "syms" let iter t f = SMap.iter (fun _ value -> f value) t.by_name diff --git a/src/elf/symTable.mli b/src/elf/symTable.mli index 0b919a8a..18e7de50 100644 --- a/src/elf/symTable.mli +++ b/src/elf/symTable.mli @@ -78,19 +78,19 @@ val of_name_opt : t -> string -> sym option (** Get the symbol owning that address. Not_found is raised if no symbol own that address.data See {!of_addr_opt} *) -val of_addr : t -> Symbol.addr -> sym +val of_addr : t -> Address.t -> sym (** Get the symbol owning that address. None if no symbol own that address. See {!of_addr} *) -val of_addr_opt : t -> Symbol.addr -> sym option +val of_addr_opt : t -> Address.t -> sym option (** Get a symbol with the offset that correspond to that address *) -val of_addr_with_offset : t -> Symbol.addr -> sym_offset +val of_addr_with_offset : t -> Address.t -> sym_offset (** Get a symbol with the offset that correspond to that address *) -val of_addr_with_offset_opt : t -> Symbol.addr -> sym_offset option +val of_addr_with_offset_opt : t -> Address.t -> sym_offset option (** Get back the raw address from a symbol+offset value *) -val to_addr_offset : sym_offset -> Symbol.addr +val to_addr_offset : sym_offset -> Address.t (** Transform a symbol + offset into the corresponding string *) val string_of_sym_offset : sym_offset -> string diff --git a/src/elf/symbol.ml b/src/elf/symbol.ml index bcbd00ae..da1bf9e0 100644 --- a/src/elf/symbol.ml +++ b/src/elf/symbol.ml @@ -52,17 +52,11 @@ type typ = NOTYPE | OBJECT | FUNC | SECTION | FILE | UNKNOWN type linksem_typ = Z.t -(* TODO: move somewhere to reuse *) -type addr = { - section : string; - offset: int; -} - type t = { name : string; other_names : string list; typ : typ; - addr : addr; + addr : Address.t; (* addr : int; *) size : int; writable : bool; @@ -104,8 +98,7 @@ let locs = SMap.empty |> SMap.add ".text" 0 |> SMap.add ".data" 1000000 |> SMap. let of_linksem (name, (typ, size, addr, data, _), writable) = let typ = typ_of_linksem typ in let size = Z.to_int size in - let section, offset = addr in - let addr = { section; offset = Z.to_int offset } in + let addr = Address.of_linksem addr in (* let addr = SMap.find section locs + Z.to_int offset in *) { name; other_names = []; typ; size; addr; data; writable } @@ -128,8 +121,6 @@ let pp_typ typ = | FILE -> "FILE" | UNKNOWN -> "UNKNOWN" -let pp_addr addr = Pp.(!^(addr.section) ^^ !^"+" ^^ ptr addr.offset) - let pp_raw sym = Pp.( !^"sym" @@ -138,7 +129,7 @@ let pp_raw sym = ("name", !^(sym.name)); ("other names", separate nbspace (List.map string sym.other_names)); ("typ", pp_typ sym.typ); - ("addr", pp_addr sym.addr); + ("addr", Address.pp sym.addr); (* ("addr", ptr sym.addr); *) ("size", ptr sym.size); ("writable", bool sym.writable); diff --git a/src/elf/symbol.mli b/src/elf/symbol.mli index 5b5e9e6f..0e91fa11 100644 --- a/src/elf/symbol.mli +++ b/src/elf/symbol.mli @@ -55,19 +55,13 @@ type typ = NOTYPE | OBJECT | FUNC | SECTION | FILE | UNKNOWN type linksem_typ = Z.t -(* TODO: move somewhere to reuse *) -type addr = { - section : string; - offset: int; -} - (** The ELF symbol. This type guarantee the data exists contrary to linksem symbols (it may be all zeros though) *) type t = { name : string; other_names : string list; typ : typ; - addr : addr; + addr : Address.t; (* addr : int; *) size : int; writable : bool; @@ -117,8 +111,5 @@ val compare : t -> t -> int (** Pretty prints a symbol type *) val pp_typ : typ -> Pp.document -(** Pretty prints symbolic address *) -val pp_addr : addr -> Pp.document - (** Raw pretty printing of a symbol *) val pp_raw : t -> Pp.document diff --git a/src/state/base.ml b/src/state/base.ml index 33d4de14..353c6cfd 100644 --- a/src/state/base.ml +++ b/src/state/base.ml @@ -429,7 +429,7 @@ let read_from_rodata (s : t) ~(addr : Exp.t) ~(size : Mem.Size.t) : Exp.t option if not @@ ConcreteEval.is_concrete addr then None else let int_addr = ConcreteEval.eval addr |> Value.expect_bv |> BitVec.to_int in - let sym_addr = Elf.Symbol.{ section = ".rodata"; offset = int_addr } in (* TODO this is wrong *) + let sym_addr = Elf.Address.{ section = ".rodata"; offset = int_addr } in (* TODO this is wrong *) let size = size |> Ast.Size.to_bits in try let (sym, offset) = Elf.SymTable.of_addr_with_offset elf.symbols sym_addr in From fd2c3ff4483f9c59b8e24fd61ce8ac25fe273769 Mon Sep 17 00:00:00 2001 From: Matej Urban Date: Sun, 5 Jan 2025 12:01:00 +0100 Subject: [PATCH 007/116] Symbolic runner (TODO parsing symbolic addresses from SMT) --- src/run/runner.ml | 36 +++++++++++++++++++----------------- 1 file changed, 19 insertions(+), 17 deletions(-) diff --git a/src/run/runner.ml b/src/run/runner.ml index 864dba24..760670f9 100644 --- a/src/run/runner.ml +++ b/src/run/runner.ml @@ -75,9 +75,9 @@ type slot = type t = { elf : Elf.File.t; dwarf : Dw.t option; - instrs : (int, slot) Hashtbl.t; (** Instruction cache *) + instrs : (Elf.Address.t, slot) Hashtbl.t; (** Instruction cache *) pc : Reg.t; - funcs : int Vec.t; (** Loaded functions by loading order *) + funcs : Elf.Address.t Vec.t; (** Loaded functions by loading order *) } let of_elf ?dwarf elf = @@ -100,36 +100,36 @@ let load_sym runner (sym : Elf.Symbol.t) = (fun code -> let (addr, instr_len) = let result = !addr and len = BytesSeq.length code in - addr := !addr + len; + addr := Elf.Address.(!addr + len); (result, len) in try let instr = Trace.Cache.get_instr code in if instr.traces = [] then begin - debug "Instruction at 0x%x in %s is loaded as special" addr sym.name; + debug "Instruction at %t in %s is loaded as special" (Pp.top Elf.Address.pp addr) sym.name; Hashtbl.add runner.instrs addr (Special instr_len) end else begin - debug "Instruction at 0x%x in %s is loaded as normal. Traces are:\n%t" addr sym.name + debug "Instruction at %t in %s is loaded as normal. Traces are:\n%t" (Pp.top Elf.Address.pp addr) sym.name Pp.(topi Trace.Instr.pp instr); Hashtbl.add runner.instrs addr (Normal instr) end with exn -> - warn "Could not convert isla trace of instruction at 0x%x in %s to Trace.t: %s\n%s" addr + warn "Could not convert isla trace of instruction at %t in %s to Trace.t: %s\n%s" (Pp.top Elf.Address.pp addr) runner.elf.filename (Printexc.to_string exn) (Printexc.get_backtrace ()); Hashtbl.add runner.instrs addr (IslaFail instr_len)) opcode_list (** Fetch an instruction, and return corresponding slot. *) -let fetch (runner : t) (pc : int) : slot = - debug "Fetching PC 0x%x" pc; +let fetch (runner : t) (pc : Elf.Address.t) : slot = + debug "Fetching PC %t" (Pp.top Elf.Address.pp pc); match Hashtbl.find_opt runner.instrs pc with | Some v -> v | None -> ( match Elf.SymTable.of_addr_opt runner.elf.symbols pc with | Some sym when sym.typ = Elf.Symbol.FUNC -> if Hashtbl.mem runner.instrs sym.addr then begin - warn "Tried to fetch in middle of instructions in %s at 0x%x" runner.elf.filename pc; + warn "Tried to fetch in middle of instructions in %s at %t" runner.elf.filename (Pp.top Elf.Address.pp pc); Hashtbl.add runner.instrs pc Nocode; Nocode end @@ -138,13 +138,13 @@ let fetch (runner : t) (pc : int) : slot = match Hashtbl.find_opt runner.instrs pc with | Some v -> v | None -> - warn "Tried to fetch in middle of instructions in %s at 0x%x" runner.elf.filename - pc; + warn "Tried to fetch in middle of instructions in %s at %t" runner.elf.filename + (Pp.top Elf.Address.pp pc); Hashtbl.add runner.instrs pc Nocode; Nocode end | _ -> - warn "Tried to fetch outside of normal code in %s at 0x%x" runner.elf.filename pc; + warn "Tried to fetch outside of normal code in %s at %t" runner.elf.filename (Pp.top Elf.Address.pp pc); Hashtbl.add runner.instrs pc Nocode; Nocode ) @@ -188,6 +188,7 @@ let skip runner state : State.t list = let pc_exp = State.get_reg_exp state runner.pc in try let pc = pc_exp |> Ast.expect_bits |> BitVec.to_int in + let pc = Elf.Address.{ section = ".text"; offset = pc } in (* TODO this is wrong, should get symbolic value from pc_exp *) match fetch runner pc with | Normal { traces = _; read = _; written = _; length; opcode = _ } |Special length @@ -195,7 +196,7 @@ let skip runner state : State.t list = let state = State.copy_if_locked state in State.bump_pc ~pc:runner.pc state length; [state] - | Nocode -> Raise.fail "Trying to skip 0x%x in %s: no code there" pc runner.elf.filename + | Nocode -> Raise.fail "Trying to skip %t in %s: no code there" (Pp.tos Elf.Address.pp pc) runner.elf.filename with exn -> err "Trying to skip instruction at %t in %s: Unexpected error" Pp.(top State.Exp.pp pc_exp) @@ -220,13 +221,14 @@ let run ?prelock runner state : State.t list = let pc_exp = State.get_reg_exp state runner.pc in try let pc = pc_exp |> Ast.expect_bits |> BitVec.to_int in + let pc = Elf.Address.{ section = ".text"; offset = pc } in (* TODO this is wrong, should get symbolic value from pc_exp *) match fetch runner pc with | Normal instr -> execute_normal ?prelock ~pc runner instr state | Special _ -> - Raise.fail "Special instruction at 0x%x in %s. unsupported for now" pc runner.elf.filename - | Nocode -> Raise.fail "Trying to run 0x%x in %s: no code there" pc runner.elf.filename + Raise.fail "Special instruction at %t in %s. unsupported for now" (Pp.tos Elf.Address.pp pc) runner.elf.filename + | Nocode -> Raise.fail "Trying to run %t in %s: no code there" (Pp.tos Elf.Address.pp pc) runner.elf.filename | IslaFail _ -> - Raise.fail "Trying to run 0x%x in %s: Isla pipeline failed on that instruction" pc + Raise.fail "Trying to run %t in %s: Isla pipeline failed on that instruction" (Pp.tos Elf.Address.pp pc) runner.elf.filename with exn -> err "Trying to run instruction at %t in %s: Unexpected error" @@ -257,4 +259,4 @@ let pp_slot = (** Dump instruction table *) let pp_instr (runner : t) = let open Pp in - hashtbl_sorted ~name:"Instructions" ~compare ptr pp_slot runner.instrs + hashtbl_sorted ~name:"Instructions" ~compare Elf.Address.pp pp_slot runner.instrs From d42ecf06eb6410fafa9d04ddff56a8e33c3ee9c5 Mon Sep 17 00:00:00 2001 From: Matej Urban Date: Sun, 5 Jan 2025 16:06:53 +0100 Subject: [PATCH 008/116] Run relocatable file TODOS: - translations from/to SMT - relocations - translations from DWARF - provenance or concrete SP - simplify read addresses --- src/bin/dune | 4 ++-- src/bin/main.ml | 4 ++-- src/bin/main_riscv64.ml | 4 ++-- src/bin/readDwarf.ml | 2 +- src/dune | 2 ++ src/run/block.ml | 2 +- src/run/block_lib.ml | 10 ++++++++-- src/run/func.ml | 2 +- src/run/funcRD.ml | 12 ++++++------ src/state/base.ml | 13 +++++++++---- src/state/base.mli | 6 ++++-- 11 files changed, 38 insertions(+), 23 deletions(-) diff --git a/src/bin/dune b/src/bin/dune index f4c90590..fea23d27 100644 --- a/src/bin/dune +++ b/src/bin/dune @@ -4,7 +4,7 @@ (modules main) (flags (:standard -open Utils)) - (libraries config run utils sig_aarch64 other_cmds branchTable)) + (libraries config run utils sig_aarch64 other_cmds)) (executable (name main_riscv64) @@ -12,7 +12,7 @@ (modules main_riscv64) (flags (:standard -open Utils)) - (libraries config run utils sig_riscv64 other_cmds branchTable)) + (libraries config run utils sig_riscv64 other_cmds)) (library (name other_cmds) diff --git a/src/bin/main.ml b/src/bin/main.ml index fc17e98a..714c0fc2 100644 --- a/src/bin/main.ml +++ b/src/bin/main.ml @@ -71,9 +71,9 @@ let commands = Run.Func.command; Run.Instr.command; Run.Block.command; - Run.FuncRD.command; + (* Run.FuncRD.command; *) Other_cmds.CopySourcesCmd.command; - BranchTable.command; + (* BranchTable.command; *) ] let _ = Printexc.record_backtrace Config.enable_backtrace diff --git a/src/bin/main_riscv64.ml b/src/bin/main_riscv64.ml index 92a20d0f..50622cb7 100644 --- a/src/bin/main_riscv64.ml +++ b/src/bin/main_riscv64.ml @@ -71,9 +71,9 @@ let commands = Run.Func.command; Run.Instr.command; Run.Block.command; - Run.FuncRD.command; + (* Run.FuncRD.command; *) Other_cmds.CopySourcesCmd.command; - BranchTable.command; + (* BranchTable.command; *) ] let _ = Printexc.record_backtrace Config.enable_backtrace diff --git a/src/bin/readDwarf.ml b/src/bin/readDwarf.ml index 16d38882..957552f8 100644 --- a/src/bin/readDwarf.ml +++ b/src/bin/readDwarf.ml @@ -71,7 +71,7 @@ let commands = Run.Func.command; Run.Instr.command; Run.Block.command; - Run.FuncRD.command; + (* Run.FuncRD.command; *) CopySourcesCmd.command; ] diff --git a/src/dune b/src/dune index cdaa4d1c..b474cb2a 100644 --- a/src/dune +++ b/src/dune @@ -2,3 +2,5 @@ (release (flags (:standard -short-paths)))) + +(dirs :standard \ branchTable) \ No newline at end of file diff --git a/src/run/block.ml b/src/run/block.ml index 89d0c0e0..ed9f8ddb 100644 --- a/src/run/block.ml +++ b/src/run/block.ml @@ -109,7 +109,7 @@ let gen_block ((elf : Elf.File.t), (symoffset : Elf.SymTable.sym_offset)) len br let open Option in unlift_pair @@ let+ l = len in - (start, start + l) + (start, Elf.Address.(start + l)) in let endpred = Block_lib.gen_endpred ?min ?max ~brks () in Trace.Cache.start @@ Arch.get_isla_config (); diff --git a/src/run/block_lib.ml b/src/run/block_lib.ml index 25e5c528..ddb6c14d 100644 --- a/src/run/block_lib.ml +++ b/src/run/block_lib.ml @@ -56,7 +56,7 @@ open Logs.Logger (struct end) (** [endpred pc_exp] gives when to stop *) -type t = { runner : Runner.t; start : int; endpred : State.exp -> string option } +type t = { runner : Runner.t; start : Elf.Address.t; endpred : State.exp -> string option } (** Build a complex block starting from [start] in [sym] and ending when [endpred] says so. [endpred] is a predicate on the symbolic PC expression *) @@ -137,7 +137,7 @@ let run ?(every_instruction = false) ?relevant (b : t) (start : State.t) : label end in let state = State.copy start in - State.set_pc ~pc:pcreg state b.start; + State.set_pc_sym ~pc:pcreg state b.start; let rest = [run_from state] in State.Tree.{ state = start; data = Start; rest } @@ -148,6 +148,12 @@ let run ?(every_instruction = false) ?relevant (b : t) (start : State.t) : label - pc has be seen more than [loop] *) let gen_endpred ?min ?max ?loop ?(brks = []) () : State.exp -> string option = + (* HACK *) + (* TODO rewrite for symbolic pc *) + let min = Option.map (fun min -> min.Elf.Address.offset) min in + let max = Option.map (fun max -> max.Elf.Address.offset) max in + let brks = List.map (fun brks -> brks.Elf.Address.offset) brks in + (* *) let endnow fmt = Printf.ksprintf Option.some fmt in let pchtbl = Hashtbl.create 10 in let loop_str = diff --git a/src/run/func.ml b/src/run/func.ml index 3afdb1d5..d41d3c03 100644 --- a/src/run/func.ml +++ b/src/run/func.ml @@ -85,7 +85,7 @@ let get_state_tree ~elf:elfname ~name ?(dump = false) ?(entry = false) ?len ?(br let open Option in unlift_pair @@ let+ l = len in - (sym.addr, sym.addr + l) + (sym.addr, Elf.Address.(sym.addr + l)) in let endpred = Block_lib.gen_endpred ?min ?max ?loop ~brks () in let runner = Runner.of_dwarf dwarf in diff --git a/src/run/funcRD.ml b/src/run/funcRD.ml index 9e1dc449..374b7e9d 100644 --- a/src/run/funcRD.ml +++ b/src/run/funcRD.ml @@ -48,14 +48,14 @@ instructions.*) open Cmdliner -open Config.CommonOpt -open Fun +(* open Config.CommonOpt *) +(* open Fun *) open Logs.Logger (struct let str = __MODULE__ end) -let run_func_rd elfname name objdump_d branchtables breakpoints = +(* let run_func_rd elfname name objdump_d branchtables breakpoints = base "Running with rd %s in %s" name elfname; base "Loading %s" elfname; let dwarf = Dw.of_file elfname in @@ -142,7 +142,7 @@ let run_func_rd elfname name objdump_d branchtables breakpoints = |> List.iter (fun (msg, st, regs) -> base "At 0x%x, %s:\n%t" pc msg Pp.(topi (State.pp_partial ~regs) st)); print_string (print_analyse_instruction pc))) - runner.funcs + runner.funcs *) let elf = let doc = "ELF file from which to pull the code" in @@ -168,7 +168,7 @@ let breakpoints = in Arg.(value & opt_all string [] & info ["b"; "break"] ~docv:"POSITION" ~doc) -let term = +(* let term = Term.( CmdlinerHelper.func_options comopts run_func_rd $ elf $ func $ objdump_d $ branch_table $ breakpoints) @@ -181,4 +181,4 @@ let info = in Cmd.(info "run-func-rd" ~doc ~exits) -let command = (term, info) +let command = (term, info) *) diff --git a/src/state/base.ml b/src/state/base.ml index 353c6cfd..abd14f3f 100644 --- a/src/state/base.ml +++ b/src/state/base.ml @@ -317,7 +317,7 @@ type t = { However the symbolic execution should always be more concrete with it than without it *) fenv : Fragment.env; (** The memory type environment. See {!Fragment.env} *) - mutable last_pc : int; + 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 *) @@ -354,7 +354,7 @@ let make ?elf () = mem = Mem.empty (); elf; fenv = Fragment.Env.make (); - last_pc = 0; + last_pc = Elf.Address.{ section = ".text"; offset = 0 }; (* TODO is this right? *) } in next_id := id + 1; @@ -498,6 +498,11 @@ let set_pc ~(pc : Reg.t) (s : t) (pcval : int) = let ctyp = Ctype.of_frag Ctype.Global ~offset:pcval ~constexpr:true in set_reg s pc @@ Tval.make ~ctyp exp +(* TODO *) +let set_pc_sym ~(pc : Reg.t) (s : t) (pcval : Elf.Address.t) = + set_pc ~pc s pcval.offset + + let bump_pc ~(pc : Reg.t) (s : t) (bump : int) = let pc_exp = get_reg_exp s pc in assert (ConcreteEval.is_concrete pc_exp); @@ -520,7 +525,7 @@ let pp s = [ ("id", Id.pp s.id); ("base_state", Option.fold ~none:!^"none" ~some:(fun s -> Id.pp s.id) s.base_state); - ("last_pc", ptr s.last_pc); + ("last_pc", Elf.Address.pp s.last_pc); ("regs", Reg.Map.pp Tval.pp s.regs); ("fenv", Fragment.Env.pp s.fenv); ("read_vars", Vec.ppi Tval.pp s.read_vars); @@ -537,7 +542,7 @@ let pp_partial ~regs s = [ ("id", Id.pp s.id |> some); ("base_state", Option.map (fun s -> Id.pp s.id) s.base_state); - ("last_pc", ptr s.last_pc |> some); + ("last_pc", Elf.Address.pp s.last_pc |> some); ( "regs", List.map (fun reg -> (Reg.pp reg, Reg.Map.get s.regs reg |> Tval.pp)) regs |> Pp.mapping "" |> some ); diff --git a/src/state/base.mli b/src/state/base.mli index 455943b0..05669eb6 100644 --- a/src/state/base.mli +++ b/src/state/base.mli @@ -292,7 +292,7 @@ type t = private { However the symbolic execution should always be more concrete with it than without it *) fenv : Fragment.env; (** The memory type environment. See {!Fragment.env} *) - mutable last_pc : int; + 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 *) @@ -464,6 +464,8 @@ val update_reg_exp : t -> Reg.t -> (exp -> exp) -> unit (** Set the PC to a concrete value and keep its type appropriate *) val set_pc : pc:Reg.t -> t -> int -> unit +val set_pc_sym : pc:Reg.t -> t -> Elf.Address.t -> unit + (** Bump a concrete PC by a concrete bump (generally the size of a non-branching instruction *) val bump_pc : pc:Reg.t -> t -> int -> unit @@ -471,7 +473,7 @@ val bump_pc : pc:Reg.t -> t -> int -> unit val concretize_pc : pc:Reg.t -> t -> unit (** Set the [last_pc] of the state *) -val set_last_pc : t -> int -> unit +val set_last_pc : t -> Elf.Address.t -> unit (** {1 Pretty printing } *) From fbd7c07712dea148d651e81787c333bdf7249dc4 Mon Sep 17 00:00:00 2001 From: Matej Urban Date: Mon, 6 Jan 2025 22:56:36 +0100 Subject: [PATCH 009/116] Enable typer --- src/arch/aarch64/sig.ml | 2 +- src/arch/riscv64/sig.ml | 2 +- src/ctype/ctype.ml | 4 ++-- src/state/base.ml | 3 ++- src/trace/run.ml | 17 ++++++++++------- src/trace/typer.ml | 10 +++++----- 6 files changed, 21 insertions(+), 17 deletions(-) diff --git a/src/arch/aarch64/sig.ml b/src/arch/aarch64/sig.ml index 18b0b18c..af5bd091 100644 --- a/src/arch/aarch64/sig.ml +++ b/src/arch/aarch64/sig.ml @@ -331,7 +331,7 @@ let get_abi api = State.set_reg_type state sp (Ctype.of_frag ~provenance:stack_provenance @@ DynFragment stack_frag_id); State.set_reg state r30 - (State.Tval.of_var ~ctyp:(Ctype.of_frag_somewhere Ctype.Global) RetAddr); + (State.Tval.of_var ~ctyp:(Ctype.of_frag_somewhere (Ctype.Global ".text")) RetAddr); let sp_exp = State.Exp.of_reg state.id sp in (* Assert that Sp is 16 bytes aligned *) State.push_assert state Exp.Typed.(extract ~last:3 ~first:0 sp_exp = bits_int ~size:4 0); diff --git a/src/arch/riscv64/sig.ml b/src/arch/riscv64/sig.ml index 0e14e523..c999179e 100644 --- a/src/arch/riscv64/sig.ml +++ b/src/arch/riscv64/sig.ml @@ -289,7 +289,7 @@ let get_abi api = State.set_reg_type state sp (Ctype.of_frag ~provenance:stack_provenance @@ DynFragment stack_frag_id); State.set_reg state ra - (State.Tval.of_var ~ctyp:(Ctype.of_frag_somewhere Ctype.Global) RetAddr); + (State.Tval.of_var ~ctyp:(Ctype.of_frag_somewhere (Ctype.Global ".text")) RetAddr); let sp_exp = State.Exp.of_reg state.id sp in (* Assert that Sp is 16 bytes aligned *) State.push_assert state Exp.Typed.(extract ~last:3 ~first:0 sp_exp = bits_int ~size:4 0); diff --git a/src/ctype/ctype.ml b/src/ctype/ctype.ml index daa31632..fab17f6b 100644 --- a/src/ctype/ctype.ml +++ b/src/ctype/ctype.ml @@ -136,7 +136,7 @@ and fragment = | 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 + | Global of string (** The Global fragment that contains all the fixed ELF section .text, .data, .rodata, ... *) @@ -669,7 +669,7 @@ and pp_fragment frag = | DynArray t -> pp t ^^ !^"[]" | Unknown -> !^"unknown" | DynFragment i -> dprintf "frag %d" i - | Global -> !^"global" + | Global s -> !^"global " ^^ !^s and pp_offset = function | Const off when off = 0 -> empty diff --git a/src/state/base.ml b/src/state/base.ml index abd14f3f..466a4ca0 100644 --- a/src/state/base.ml +++ b/src/state/base.ml @@ -493,9 +493,10 @@ let get_reg_exp s reg = get_reg s reg |> Tval.exp let update_reg_exp (s : t) (reg : Reg.t) (f : exp -> exp) = Reg.Map.get s.regs reg |> Tval.map_exp f |> Reg.Map.set s.regs reg +(* TODO *) let set_pc ~(pc : Reg.t) (s : t) (pcval : int) = let exp = Typed.bits_int ~size:64 pcval in - let ctyp = Ctype.of_frag Ctype.Global ~offset:pcval ~constexpr:true in + let ctyp = Ctype.of_frag (Ctype.Global ".text") ~offset:pcval ~constexpr:true in set_reg s pc @@ Tval.make ~ctyp exp (* TODO *) diff --git a/src/trace/run.ml b/src/trace/run.ml index 986d3b53..9b4f820a 100644 --- a/src/trace/run.ml +++ b/src/trace/run.ml @@ -70,10 +70,10 @@ let expand ~(ctxt : ctxt) (exp : Base.exp) : State.exp = otherwise the type will be [None] *) let expand_tval ~(ctxt : ctxt) (exp : Base.exp) : State.tval = let sexp = expand ~ctxt exp in - (* if Ctxt.typing_enabled ~ctxt then + if Ctxt.typing_enabled ~ctxt then let ctyp = Typer.expr ~ctxt exp in { ctyp; exp = sexp } - else *) + else { ctyp = None; exp = sexp } (** Run the event. @@ -85,24 +85,27 @@ let event_mut ~(ctxt : ctxt) (event : Base.event) = | WriteReg { reg; value } -> Vec.add_one ctxt.reg_writes (reg, expand_tval ~ctxt value) | ReadMem { addr; value; size } -> let naddr = expand ~ctxt addr in + let ptrtype = Typer.expr ~ctxt addr in + debug "ptrtype: %t" Pp.(top (optional Ctype.pp) ptrtype); let tval = - (* match ctxt.dwarf with + match ctxt.dwarf with | Some dwarf -> - let ptrtype = Typer.expr ~ctxt addr in Typer.read ~dwarf ctxt.state ?ptrtype ~addr:naddr ~size - | None -> *) + | None -> State.read_noprov ctxt.state ~addr:naddr ~size |> State.Tval.of_exp in HashVector.set ctxt.mem_reads value tval | WriteMem { addr; value; size } -> ( let naddr = expand ~ctxt addr in - (* match ctxt.dwarf with + let ptrtype = Typer.expr ~ctxt addr in + debug "ptrtype: %t" Pp.(top (optional Ctype.pp) ptrtype); + match ctxt.dwarf with | Some dwarf -> let ptrtype = Typer.expr ~ctxt addr in debug "Typed write mem with ptr:%t" (Pp.top (Pp.opt Ctype.pp) ptrtype); let value = expand_tval ~ctxt value in Typer.write ~dwarf ctxt.state ?ptrtype ~addr:naddr ~size value - | None -> *) + | None -> let value = expand ~ctxt value in State.write_noprov ctxt.state ~addr:naddr ~size value ) diff --git a/src/trace/typer.ml b/src/trace/typer.ml index f7b0f6de..cf04c5af 100644 --- a/src/trace/typer.ml +++ b/src/trace/typer.ml @@ -1,4 +1,4 @@ -(* ================================================================================== +(* ================================================================================== *) (* BSD 2-Clause License *) (* *) (* Copyright (c) 2020-2021 Thibaut Pérami *) @@ -178,7 +178,7 @@ let manyop ~ctxt (m : Ast.manyop) (tvals : tval list) : Ctype.t option = match List.hd tvals with | { exp = Unop (Extract (_, _), _, _); - ctyp = Some ({ unqualified = Ptr { fragment = Global; offset; _ }; _ } as ctyp); + ctyp = Some ({ unqualified = Ptr { fragment = Global _; offset; _ }; _ } as ctyp); } -> ( match offset with | Somewhere -> Some ctyp @@ -254,8 +254,8 @@ let fragment_at ~(dwarf : Dw.t) ~fenv ~size (frag : Ctype.fragment) at : Ctype.t let frag = Fragment.Env.get fenv i in let* (typ, off) = Fragment.at_off_opt frag at in Ctype.type_at ~env ~size typ off - | Global -> ( - match Elf.SymTable.of_addr_with_offset_opt dwarf.elf.symbols at with + | Global s -> ( + match Elf.SymTable.of_addr_with_offset_opt dwarf.elf.symbols Elf.Address.{ section = s; offset = at } with | Some (sym, offset) -> ( match Hashtbl.find_opt dwarf.vars sym.name with | Some v -> Ctype.type_at ~env ~size v.ctype offset @@ -314,4 +314,4 @@ let write ~(dwarf : Dw.t) (s : State.t) ?(ptrtype : Ctype.t option) ~addr ~size State.write ~provenance s ~addr ~size value.exp | _ -> warn "Writing without provenance"; - State.write_noprov s ~addr ~size value.exp *) + State.write_noprov s ~addr ~size value.exp From 49ffe70846bfb4f421bae33839e86df701aec510 Mon Sep 17 00:00:00 2001 From: Matej Urban Date: Tue, 7 Jan 2025 12:17:45 +0100 Subject: [PATCH 010/116] Some debug prints --- src/trace/run.ml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/trace/run.ml b/src/trace/run.ml index 9b4f820a..f6eaf1c1 100644 --- a/src/trace/run.ml +++ b/src/trace/run.ml @@ -85,6 +85,7 @@ let event_mut ~(ctxt : ctxt) (event : Base.event) = | WriteReg { reg; value } -> Vec.add_one ctxt.reg_writes (reg, expand_tval ~ctxt value) | ReadMem { addr; value; size } -> let naddr = expand ~ctxt addr in + debug "naddr: %t" (Pp.top State.Exp.pp naddr); let ptrtype = Typer.expr ~ctxt addr in debug "ptrtype: %t" Pp.(top (optional Ctype.pp) ptrtype); let tval = @@ -97,6 +98,7 @@ let event_mut ~(ctxt : ctxt) (event : Base.event) = HashVector.set ctxt.mem_reads value tval | WriteMem { addr; value; size } -> ( let naddr = expand ~ctxt addr in + debug "naddr: %t" (Pp.top State.Exp.pp naddr); let ptrtype = Typer.expr ~ctxt addr in debug "ptrtype: %t" Pp.(top (optional Ctype.pp) ptrtype); match ctxt.dwarf with From 3f0498b721f3b52484981f27471678d6908cd543 Mon Sep 17 00:00:00 2001 From: Matej Urban Date: Tue, 7 Jan 2025 12:18:13 +0100 Subject: [PATCH 011/116] Mark potential bug --- src/trace/context.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/trace/context.ml b/src/trace/context.ml index cd8b46be..bfbce294 100644 --- a/src/trace/context.ml +++ b/src/trace/context.ml @@ -67,7 +67,7 @@ let expand_var ~(ctxt : t) (v : Base.Var.t) (a : Ast.no Ast.ty) : State.exp = assert (Base.Var.ty v = a); match v with | Register reg -> State.get_reg_exp ctxt.state reg - | NonDet (i, _) | Read (i, _) -> (HashVector.get ctxt.mem_reads i).exp + | NonDet (i, _) | Read (i, _) -> (HashVector.get ctxt.mem_reads i).exp (* TODO is the NonDet case correct *) let map_var ~(ctxt : t) (v : Base.Var.t) (a : Ast.no Ast.ty) : State.var = assert (Base.Var.ty v = a); From e4485cdf2153781052343b85cac0f7c19a9537df Mon Sep 17 00:00:00 2001 From: Matej Urban Date: Tue, 7 Jan 2025 12:21:48 +0100 Subject: [PATCH 012/116] Add TODO --- src/arch/aarch64/sig.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/arch/aarch64/sig.ml b/src/arch/aarch64/sig.ml index af5bd091..151fce0b 100644 --- a/src/arch/aarch64/sig.ml +++ b/src/arch/aarch64/sig.ml @@ -331,7 +331,7 @@ let get_abi api = State.set_reg_type state sp (Ctype.of_frag ~provenance:stack_provenance @@ DynFragment stack_frag_id); State.set_reg state r30 - (State.Tval.of_var ~ctyp:(Ctype.of_frag_somewhere (Ctype.Global ".text")) RetAddr); + (State.Tval.of_var ~ctyp:(Ctype.of_frag_somewhere (Ctype.Global ".text")) RetAddr); (* TODO doesn't have to be .text *) let sp_exp = State.Exp.of_reg state.id sp in (* Assert that Sp is 16 bytes aligned *) State.push_assert state Exp.Typed.(extract ~last:3 ~first:0 sp_exp = bits_int ~size:4 0); From 4ef12a190f742efde18cadf01fd363cbf8caf8b4 Mon Sep 17 00:00:00 2001 From: Matej Urban Date: Tue, 7 Jan 2025 18:55:41 +0100 Subject: [PATCH 013/116] Implement opcodes with relocations --- src/isla/cache.ml | 35 ++++++++++++++++++++--------------- src/isla/server.ml | 7 +++++++ src/run/bb_lib.ml | 2 +- src/run/instr.ml | 2 +- src/run/runner.ml | 2 +- src/trace/cache.ml | 4 ++-- src/trace/instr.ml | 5 +++-- 7 files changed, 35 insertions(+), 22 deletions(-) diff --git a/src/isla/cache.ml b/src/isla/cache.ml index 038bbdd6..f7d5850f 100644 --- a/src/isla/cache.ml +++ b/src/isla/cache.ml @@ -77,45 +77,49 @@ type config = Server.config bit 0 to back -1 : The start of the data bit back -1: set *) module Opcode (*: Cache.Key *) = struct - type t = BytesSeq.t option + type t = Server.opcode option let equal a b = match (a, b) with | (None, None) -> true - | (Some bs, Some bs2) -> BytesSeq.equal bs bs2 + | (Some (bs, r1), Some (bs2, r2)) -> BytesSeq.equal bs bs2 && r1 = r2 | _ -> false + let small_enough bs rel_id = + BytesSeq.length bs < BytesSeq.int_bytes && rel_id < 16 + let hash = function | None -> 0 - | Some bs -> + | Some (bs, rel) -> let i = BytesSeq.getintle_ze bs 0 in let l = BytesSeq.length bs in - if l < BytesSeq.int_bytes then begin + let rel_id = Server.reloc_id rel in + if small_enough bs rel_id then begin assert (not @@ IntBits.get i IntBits.back); let res = IntBits.blit l 0 i (IntBits.back - 3) 3 in + let res = IntBits.blit rel_id 0 res (IntBits.back - 7) 4 in res end else IntBits.set i IntBits.back - let to_file file = function + let to_file _file = function | None -> () - | Some bs -> - if BytesSeq.length bs < BytesSeq.int_bytes then () + | Some (bs, rel) -> + let rel_id = Server.reloc_id rel in + if small_enough bs rel_id then () else - let keyfile = Utils.Cache.to_keyfile file in - Files.write_bin BytesSeq.output keyfile bs + Raise.todo() - let of_file hash file = + let of_file hash _file = if hash = 0 then None else if IntBits.get hash IntBits.back then - let keyfile = Utils.Cache.to_keyfile file in - Some (Files.read BytesSeq.input keyfile) + Raise.todo() else let data = IntBits.sub hash 0 (IntBits.back - 3) in let size = IntBits.sub hash (IntBits.back - 3) 3 in let b = Bytes.create size in Bits.unsafe_blit_of_int data 0 b 0 (size * 8); - Some (BytesSeq.of_bytes b) + Some (BytesSeq.of_bytes b, None) end (** Representation of trace lists on disk. @@ -216,13 +220,14 @@ let get_cache () = match !cache with Some cache -> cache | None -> failwith "Isla cache was not started" (** Get the traces of the opcode given. Use {!Server} if the value is not in the cache *) -let get_traces (opcode : BytesSeq.t) : Base.rtrc list = +let get_traces (opcode : Server.opcode) : Base.rtrc list = let (cache, config) = get_cache () in match IC.get_opt cache (Some opcode) with | Some trcs -> trcs | None -> ensure_started (); - let trcs = Server.request_bin_parsed opcode in + let raw_opcode, _ = opcode in (*TODO*) + let trcs = Server.request_bin_parsed raw_opcode in let ptrcs = Preprocess.preprocess config trcs in IC.add cache (Some opcode) ptrcs; ptrcs diff --git a/src/isla/server.ml b/src/isla/server.ml index 0fff247d..ec544bb7 100644 --- a/src/isla/server.ml +++ b/src/isla/server.ml @@ -68,6 +68,13 @@ type config = Config.t processor exception/fault) or not *) type trcs = (bool * Base.rtrc) list +type reloc = | + +let reloc_id: reloc option -> int = function +| None -> 0 + +type opcode = BytesSeq.t * reloc option + (** Bump when updating isla. TODO: move the version checking to allow a range of version. Also, right now the cache invalidation is based on diff --git a/src/run/bb_lib.ml b/src/run/bb_lib.ml index 083d07eb..477a9d98 100644 --- a/src/run/bb_lib.ml +++ b/src/run/bb_lib.ml @@ -80,7 +80,7 @@ let from_binary (code : BytesSeq.t) : t = "BB.from_binary: Multiple path instruction.\n\ If this is not a branching instruction, try `run-block --linear'." in - code |> Isla.Cache.get_traces |> get_normal + (code, None) |> Isla.Cache.get_traces |> get_normal (*TODO relocs *) in let main = code |> BytesSeq.to_listbs ~len:4 |> List.map process |> Array.of_list in { main } diff --git a/src/run/instr.ml b/src/run/instr.ml index 07a8dab8..4b8ea439 100644 --- a/src/run/instr.ml +++ b/src/run/instr.ml @@ -152,7 +152,7 @@ let get_traces instr isla_run dump_types : traces = Isla.Cache.start @@ Arch.get_isla_config (); (* I call Init.init manually to print the register types *) Init.init () |> ignore; - let rtraces = Isla.Cache.get_traces instr in + let rtraces = Isla.Cache.get_traces (instr, None) in (* TODO relocs *) List.iter (fun t -> Isla.Type.type_trc t |> ignore) rtraces; if dump_types then base "Register types:\n%t\n" (Pp.topi State.Reg.pp_index ()); if isla_run then IslaTraces rtraces else Traces (List.map Trace.of_isla rtraces) diff --git a/src/run/runner.ml b/src/run/runner.ml index 760670f9..ed65e3e2 100644 --- a/src/run/runner.ml +++ b/src/run/runner.ml @@ -104,7 +104,7 @@ let load_sym runner (sym : Elf.Symbol.t) = (result, len) in try - let instr = Trace.Cache.get_instr code in + let instr = Trace.Cache.get_instr (code, None) in (*TODO relocs*) if instr.traces = [] then begin debug "Instruction at %t in %s is loaded as special" (Pp.top Elf.Address.pp addr) sym.name; Hashtbl.add runner.instrs addr (Special instr_len) diff --git a/src/trace/cache.ml b/src/trace/cache.ml index 366f7bea..0f7d7e33 100644 --- a/src/trace/cache.ml +++ b/src/trace/cache.ml @@ -155,7 +155,7 @@ let get_cache () = match !cache with Some cache -> cache | None -> failwith "Trace cache was not started" (** Get the traces of the opcode given. Use {!Isla.Server} if the value is not in the cache *) -let get_traces (opcode : BytesSeq.t) : Base.t list = +let get_traces (opcode : Isla.Server.opcode) : Base.t list = let cache = get_cache () in match TC.get_opt cache (Some opcode) with | Some trcs -> trcs @@ -168,4 +168,4 @@ let get_traces (opcode : BytesSeq.t) : Base.t list = (** Get a full blown {!Instr} from the opcode, going through the whole Isla pipeline if necessary.*) -let get_instr (opcode : BytesSeq.t) : Instr.t = Instr.of_traces opcode @@ get_traces opcode +let get_instr (opcode : Isla.Server.opcode) : Instr.t = Instr.of_traces opcode @@ get_traces opcode diff --git a/src/trace/instr.ml b/src/trace/instr.ml index 3dd01196..4f0b54b9 100644 --- a/src/trace/instr.ml +++ b/src/trace/instr.ml @@ -66,7 +66,7 @@ type t = { length : int; (** Bytes length *) read : Reg.t list; written : Reg.t list; - opcode : BytesSeq.t; + opcode : Isla.Server.opcode; } let dedup_regs = List.sort_uniq State.Reg.compare @@ -101,7 +101,8 @@ let trace_meta_of_trace trace = (** Generate full instruction data from a list of traces *) let of_traces opcode traces = let traces = List.map trace_meta_of_trace traces in - let length = BytesSeq.length opcode in + let raw_opcode, _ = opcode in + let length = BytesSeq.length raw_opcode in let read = dedup_regs @@ List.concat_map (fun (tr : trace_meta) -> tr.read) traces in let written = dedup_regs @@ List.concat_map (fun (tr : trace_meta) -> tr.written) traces in { traces; length; read; written; opcode } From 8dd5040da133f682fed0f3f72fd7787e856ec82f Mon Sep 17 00:00:00 2001 From: Matej Urban Date: Tue, 7 Jan 2025 23:58:58 +0100 Subject: [PATCH 014/116] [WIP] read relocations from linksem --- notes-TODO | 4 +++- src/elf/linksemRelocatable.ml | 45 +++++++++++++++++++++++++---------- src/elf/relocations.ml | 32 +++++++++++++++++++++++++ src/elf/symbol.ml | 17 +++++++++---- src/elf/symbol.mli | 9 +++++-- 5 files changed, 88 insertions(+), 19 deletions(-) create mode 100644 src/elf/relocations.ml diff --git a/notes-TODO b/notes-TODO index c8e943ae..66ca908d 100644 --- a/notes-TODO +++ b/notes-TODO @@ -1,3 +1,5 @@ Symbolic symbol table - value of symbol?? (we don't have segments in relocatable files) -- can probably keep the same api, but addresses are symbolic \ No newline at end of file +- can probably keep the same api, but addresses are symbolic + +Instruction fetch: is it sound? (rewriting .text) \ No newline at end of file diff --git a/src/elf/linksemRelocatable.ml b/src/elf/linksemRelocatable.ml index 82b4e035..2123d8f3 100644 --- a/src/elf/linksemRelocatable.ml +++ b/src/elf/linksemRelocatable.ml @@ -1,18 +1,40 @@ (* TODO header *) +module SMap = Map.Make (String) + type sym_addr = string * Z.t -(* Like in linksem, but address is section+offset, and with a writable flag *) -type symbol = string * (Z.t * Z.t * sym_addr * Byte_sequence_wrapper.byte_sequence * Z.t) * bool +type rels = + | AArch64 of (Z.t, Abi_aarch64_symbolic_relocation.aarch64_relocation_target Elf_symbolic.abstract_relocation) Pmap.map + +type sym_data = +Byte_sequence_wrapper.byte_sequence * rels + + +(* Like in linksem, but address is section+offset, data has relocations and with a writable flag *) +type symbol = string * (Z.t * Z.t * sym_addr * sym_data * Z.t) * bool type global_symbol_init_info = symbol list open Elf_symbol_table open Elf_interpreted_section -let get_elf64_file_global_symbol_init f : global_symbol_init_info Error.error = - let secs = f.Elf_file.elf64_file_interpreted_sections in - Error.bind (Elf_file.get_elf64_file_symbol_table f) (fun (symtab, strtab) -> +let get_elf64_file_global_symbol_init (f: Elf_file.elf64_file) : global_symbol_init_info Error.error = + let secs = f.elf64_file_interpreted_sections in + let machine = f.elf64_file_header.elf64_machine in + Error.bind (Elf_file.get_elf64_file_symbol_table f) @@ fun (symtab, strtab) -> + let rel_cache = ref SMap.empty in + let get_relocs section = + match SMap.find_opt section !rel_cache with + | Some rels -> rels + | None -> + if machine = Elf_header.elf_ma_aarch64 then + Error.bind + (Elf_symbolic.extract_elf64_relocations_for_section f Abi_aarch64_symbolic_relocation.abi_aarch64_relocation_to_abstract section) + @@ fun relocs -> Error.return (AArch64 relocs) + else + Error.fail @@ "machine not supported " ^ (Elf_header.string_of_elf_machine_architecture machine) + in List.filter_map ( fun entry -> let name = Uint32_wrapper.to_bigint entry.elf64_st_name in @@ -29,11 +51,10 @@ let get_elf64_file_global_symbol_init f : global_symbol_init_info Error.error = else Byte_sequence.offset_and_cut addr_offset size section.elf64_section_body in - Error.bind data (fun data -> - Error.bind (String_table.get_string_at name strtab) (fun str -> - let write = Elf_file.flag_is_set Elf_section_header_table.shf_write section.elf64_section_flags in - Error.return (str, (typ, size, addr, data, bnd), write) - )) + Error.bind (get_relocs section.elf64_section_name_as_string) @@ fun relocs -> + Error.bind data @@ fun data -> + Error.bind (String_table.get_string_at name strtab) @@ fun str -> + let write = Elf_file.flag_is_set Elf_section_header_table.shf_write section.elf64_section_flags in + Error.return (str, (typ, size, addr, (data, relocs), bnd), write) ) (List.nth_opt secs shndx) - ) symtab |> Error.mapM Fun.id - ) \ No newline at end of file + ) symtab |> Error.mapM Fun.id \ No newline at end of file diff --git a/src/elf/relocations.ml b/src/elf/relocations.ml new file mode 100644 index 00000000..e48871b7 --- /dev/null +++ b/src/elf/relocations.ml @@ -0,0 +1,32 @@ +module IMap = Map.Make (Int) + +type target = + AArch64 of Abi_aarch64_symbolic_relocation.aarch64_relocation_target + +type rel = { + target : target; + value : Elf_symbolic.symbolic_expression; +} + +type t = rel IMap.t + +type linksem_t = LinksemRelocatable.rels + +let of_linksem: linksem_t -> t = function +| LinksemRelocatable.AArch64 relocs -> + let add k Elf_symbolic.{ arel_value; arel_target } m = + IMap.add (Z.to_int k) { value = arel_value; target = AArch64 arel_target } m + in + Pmap.fold add relocs IMap.empty + +let sub rels off len = + rels + |> IMap.to_list + |> List.filter_map (fun (pos, rel) -> if off <= pos && pos < off + len then Some (pos-off, rel) else None) + |> IMap.of_list + +let pp rels = + if IMap.is_empty rels then + Pp.empty + else + Pp.string "(has relocations)" \ No newline at end of file diff --git a/src/elf/symbol.ml b/src/elf/symbol.ml index da1bf9e0..8bdce76a 100644 --- a/src/elf/symbol.ml +++ b/src/elf/symbol.ml @@ -52,6 +52,11 @@ type typ = NOTYPE | OBJECT | FUNC | SECTION | FILE | UNKNOWN type linksem_typ = Z.t +type data = { + data: BytesSeq.t; + relocations: Relocations.t +} + type t = { name : string; other_names : string list; @@ -60,7 +65,7 @@ type t = { (* addr : int; *) size : int; writable : bool; - data : BytesSeq.t; + data : data; } type linksem_t = LinksemRelocatable.symbol @@ -95,10 +100,11 @@ let _ = (* module SMap = Map.Make (String) let locs = SMap.empty |> SMap.add ".text" 0 |> SMap.add ".data" 1000000 |> SMap.add ".eh_frame" 2000000 *) -let of_linksem (name, (typ, size, addr, data, _), writable) = +let of_linksem (name, (typ, size, addr, (data, rels), _), writable) = let typ = typ_of_linksem typ in let size = Z.to_int size in let addr = Address.of_linksem addr in + let data = { data; relocations = Relocations.of_linksem rels } in (* let addr = SMap.find section locs + Z.to_int offset in *) { name; other_names = []; typ; size; addr; data; writable } @@ -106,7 +112,10 @@ let is_interesting = function OBJECT | FUNC -> true | _ -> false let is_interesting_linksem lsym = lsym |> linksem_typ |> typ_of_linksem |> is_interesting -let sub sym off len = BytesSeq.sub sym.data off len +let sub sym off len = { + data = BytesSeq.sub sym.data.data off len; + relocations = Relocations.sub sym.data.relocations off len; +} let compare s1 s2 = compare s1.addr s2.addr @@ -133,5 +142,5 @@ let pp_raw sym = (* ("addr", ptr sym.addr); *) ("size", ptr sym.size); ("writable", bool sym.writable); - ("data", BytesSeq.ppby ~by:4 sym.data); + ("data", BytesSeq.ppby ~by:4 sym.data.data ^^ Relocations.pp sym.data.relocations); ]) diff --git a/src/elf/symbol.mli b/src/elf/symbol.mli index 0e91fa11..3bc5803f 100644 --- a/src/elf/symbol.mli +++ b/src/elf/symbol.mli @@ -55,6 +55,11 @@ type typ = NOTYPE | OBJECT | FUNC | SECTION | FILE | UNKNOWN type linksem_typ = Z.t +type data = { + data: BytesSeq.t; + relocations: Relocations.t +} + (** The ELF symbol. This type guarantee the data exists contrary to linksem symbols (it may be all zeros though) *) type t = { @@ -65,7 +70,7 @@ type t = { (* addr : int; *) size : int; writable : bool; - data : BytesSeq.t; + data : data; } (** The type of an ELF symbol in linksem. See {!of_linksem}*) @@ -103,7 +108,7 @@ val is_interesting : typ -> bool val is_interesting_linksem : linksem_t -> bool (** Take the BytesSeq.t corresponding to the offset and length *) -val sub : t -> int -> int -> BytesSeq.t +val sub : t -> int -> int -> data (** Starting address comparison *) val compare : t -> t -> int From d11e65a696879db0234dc1440c39a7f1ceee4f72 Mon Sep 17 00:00:00 2001 From: Matej Urban Date: Wed, 8 Jan 2025 11:55:57 +0100 Subject: [PATCH 015/116] [WIP] relocations from linksem --- src/isla/server.ml | 8 +++++++- src/isla/test.ml | 2 +- src/run/BB.ml | 2 +- src/run/instr.ml | 2 +- src/run/runner.ml | 2 +- src/state/base.ml | 2 +- 6 files changed, 12 insertions(+), 6 deletions(-) diff --git a/src/isla/server.ml b/src/isla/server.ml index ec544bb7..6c093fe3 100644 --- a/src/isla/server.ml +++ b/src/isla/server.ml @@ -68,10 +68,16 @@ type config = Config.t processor exception/fault) or not *) type trcs = (bool * Base.rtrc) list -type reloc = | +type reloc = Elf.Relocations.target let reloc_id: reloc option -> int = function | None -> 0 +| Some (Elf.Relocations.AArch64 Abi_aarch64_symbolic_relocation.Data640) -> 1 +| Some (Elf.Relocations.AArch64 Abi_aarch64_symbolic_relocation.Data320) -> 2 +| Some (Elf.Relocations.AArch64 Abi_aarch64_symbolic_relocation.ADRP) -> 3 +| Some (Elf.Relocations.AArch64 Abi_aarch64_symbolic_relocation.ADD) -> 4 +| Some (Elf.Relocations.AArch64 Abi_aarch64_symbolic_relocation.LDST) -> 5 +| Some (Elf.Relocations.AArch64 Abi_aarch64_symbolic_relocation.CALL) -> 6 type opcode = BytesSeq.t * reloc option diff --git a/src/isla/test.ml b/src/isla/test.ml index 29b011de..c44343d6 100644 --- a/src/isla/test.ml +++ b/src/isla/test.ml @@ -166,7 +166,7 @@ let input imode (arg : string) : (string * string) Term.ret = try Elf.SymTable.of_position_string elf.symbols s with Not_found -> fail "The position %s could not be found in %s" s arg in - `Ok (filename, BytesSeq.to_string (BytesSeq.sub sym.data off 4)) + `Ok (filename, BytesSeq.to_string (BytesSeq.sub sym.data.data off 4)) (* TODO relocations *) let input_term = Term.(ret (const input $ imode_term $ arg)) diff --git a/src/run/BB.ml b/src/run/BB.ml index 0e3cab82..cdddee56 100644 --- a/src/run/BB.ml +++ b/src/run/BB.ml @@ -101,7 +101,7 @@ let get_code elfname symname len : BytesSeq.t = with Not_found -> fail "The symbol %s cannot found in %s" symname elfname in let len = match len with Some i -> i | None -> sym.size - off in - Elf.Symbol.sub sym off len + (Elf.Symbol.sub sym off len).data (*TODO relocations*) let code_term = Term.(CmdlinerHelper.func_options comopts get_code $ elf $ sym $ len) diff --git a/src/run/instr.ml b/src/run/instr.ml index 4b8ea439..efa790bc 100644 --- a/src/run/instr.ml +++ b/src/run/instr.ml @@ -140,7 +140,7 @@ let get_instr arch instr elfopt : BytesSeq.t = in debug "Got symbol:\n%t\n" (Pp.topi Elf.Symbol.pp_raw sym); let len = 4 (* TODO proper Instruction length system *) in - BytesSeq.sub sym.data off len + BytesSeq.sub sym.data.data off len (*TODO relocations*) let instr_term = Term.(CmdlinerHelper.func_options comopts get_instr $ arch $ instr $ elf) diff --git a/src/run/runner.ml b/src/run/runner.ml index ed65e3e2..74eabce5 100644 --- a/src/run/runner.ml +++ b/src/run/runner.ml @@ -94,7 +94,7 @@ let of_dwarf dwarf = of_elf ~dwarf dwarf.elf let load_sym runner (sym : Elf.Symbol.t) = info "Loading symbol %s in %s" sym.name runner.elf.filename; Vec.add_one runner.funcs sym.addr; - let opcode_list = Arch.split_into_instrs sym.data in + let opcode_list = Arch.split_into_instrs sym.data.data in (* TODO relocations *) let addr = ref sym.addr in List.iter (fun code -> diff --git a/src/state/base.ml b/src/state/base.ml index 466a4ca0..4cda529c 100644 --- a/src/state/base.ml +++ b/src/state/base.ml @@ -436,7 +436,7 @@ let read_from_rodata (s : t) ~(addr : Exp.t) ~(size : Mem.Size.t) : Exp.t option if sym.writable then None else (* Assume little endian here *) - let bv = BytesSeq.getbvle ~size sym.data offset in + let bv = BytesSeq.getbvle ~size sym.data.data offset in (* TODO relocations *) Some (Typed.bits bv) with Not_found -> let rodata = elf.rodata in From 8d1db3ee5e3bf77c726e5be0c70b4da24d2c38ce Mon Sep 17 00:00:00 2001 From: Matej Urban Date: Wed, 8 Jan 2025 12:56:34 +0100 Subject: [PATCH 016/116] WIP instructions with relocations --- src/arch/aarch64/sig.ml | 13 ++++++++++++- src/arch/riscv64/sig.ml | 13 ++++++++++++- src/arch/sig.mli | 2 +- src/run/runner.ml | 5 +++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/arch/aarch64/sig.ml b/src/arch/aarch64/sig.ml index 151fce0b..658dab20 100644 --- a/src/arch/aarch64/sig.ml +++ b/src/arch/aarch64/sig.ml @@ -360,7 +360,18 @@ let assemble_to_elf instr = Sys.remove obj_file; elf_file -let split_into_instrs = BytesSeq.to_listbs ~len:4 +let split_into_instrs (data: Elf.Symbol.data) = + let module IMap = Elf.Relocations.IMap in + let rawdata = BytesSeq.to_listbs ~len:4 data.data in + List.mapi (fun pos bytes -> + let (_, rel, rest) = IMap.split pos data.relocations in + if Option.is_some @@ IMap.find_first_opt (fun i -> i < pos + 4) rest then + Raise.fail "Misaligned relocation"; + Elf.Symbol.{ + data = bytes; + relocations = rel |> Option.map (IMap.singleton 0) |> Option.value ~default:IMap.empty; + } + ) rawdata (** https://developer.arm.com/documentation/ddi0596/2020-12/Base-Instructions/RET--Return-from-subroutine- *) let is_ret code = diff --git a/src/arch/riscv64/sig.ml b/src/arch/riscv64/sig.ml index c999179e..4f661598 100644 --- a/src/arch/riscv64/sig.ml +++ b/src/arch/riscv64/sig.ml @@ -317,7 +317,18 @@ let assemble_to_elf instr = Sys.remove obj_file; elf_file -let split_into_instrs = BytesSeq.to_listbs ~len:4 +let split_into_instrs (data: Elf.Symbol.data) = + let module IMap = Elf.Relocations.IMap in + let rawdata = BytesSeq.to_listbs ~len:4 data.data in + List.mapi (fun pos bytes -> + let (_, rel, rest) = IMap.split pos data.relocations in + if Option.is_some @@ IMap.find_first_opt (fun i -> i < pos + 4) rest then + Raise.fail "Misaligned relocation"; + Elf.Symbol.{ + data = bytes; + relocations = rel |> Option.map (IMap.singleton 0) |> Option.value ~default:IMap.empty; + } + ) rawdata let is_ret code = assert (BytesSeq.length code = 4); diff --git a/src/arch/sig.mli b/src/arch/sig.mli index 13268696..a355e3e6 100644 --- a/src/arch/sig.mli +++ b/src/arch/sig.mli @@ -109,7 +109,7 @@ val sp : unit -> State.Reg.t val assemble_to_elf : string -> string (** Split a byte-sequence into a list of instructions. *) -val split_into_instrs : BytesSeq.t -> BytesSeq.t list +val split_into_instrs : Elf.Symbol.data -> Elf.Symbol.data list (** Tell if an instruction is a return instruction. *) val is_ret : BytesSeq.t -> bool diff --git a/src/run/runner.ml b/src/run/runner.ml index 74eabce5..c3a4e031 100644 --- a/src/run/runner.ml +++ b/src/run/runner.ml @@ -94,16 +94,17 @@ let of_dwarf dwarf = of_elf ~dwarf dwarf.elf let load_sym runner (sym : Elf.Symbol.t) = info "Loading symbol %s in %s" sym.name runner.elf.filename; Vec.add_one runner.funcs sym.addr; - let opcode_list = Arch.split_into_instrs sym.data.data in (* TODO relocations *) + let opcode_list = Arch.split_into_instrs sym.data in (* TODO relocations *) let addr = ref sym.addr in List.iter - (fun code -> + (fun Elf.Symbol.{ data = code; relocations } -> let (addr, instr_len) = let result = !addr and len = BytesSeq.length code in addr := Elf.Address.(!addr + len); (result, len) in try + let _reloc = Elf.Relocations.IMap.find_opt 0 relocations in let instr = Trace.Cache.get_instr (code, None) in (*TODO relocs*) if instr.traces = [] then begin debug "Instruction at %t in %s is loaded as special" (Pp.top Elf.Address.pp addr) sym.name; From 8722715f7fe2bf4a345f8688ca23116eb5c201b0 Mon Sep 17 00:00:00 2001 From: Matej Urban Date: Thu, 9 Jan 2025 10:57:15 +0100 Subject: [PATCH 017/116] WIP --- src/isla/base.ml | 20 +++++++++++++++ src/isla/cache.ml | 27 ++++++++----------- src/isla/preprocess.ml | 7 +++-- src/isla/preprocess.mli | 2 +- src/isla/server.ml | 57 +++++++++++++++++++++++++---------------- src/isla/test.ml | 2 +- src/run/bb_lib.ml | 7 ++--- src/run/instr.ml | 7 ++--- src/trace/base.ml | 3 ++- src/trace/cache.ml | 7 +++-- 10 files changed, 87 insertions(+), 52 deletions(-) diff --git a/src/isla/base.ml b/src/isla/base.ml index 4cb88d85..94f4788b 100644 --- a/src/isla/base.ml +++ b/src/isla/base.ml @@ -97,6 +97,8 @@ type rsmt = lrng smt (** The type of raw expressions out of the parser *) type rexp = lrng exp +type rtrcs = lrng trcs + (*****************************************************************************) (*****************************************************************************) (*****************************************************************************) @@ -173,6 +175,24 @@ let parse_trc_string ?(filename = "default") (s : string) : rtrc = let parse_trc_channel ?(filename = "default") (c : in_channel) : rtrc = parse_trc ~filename @@ Lexing.from_channel ~with_positions:true c +let parse_trcs = parse Parser.trcs_start + +let parse_trcs_string ?filename (s : string) : rtrcs = + parse_trcs ?filename @@ Lexing.from_string ~with_positions:true s + +let parse_trcs_channel ?filename (c : in_channel) : rtrcs = + parse_trcs ?filename @@ Lexing.from_channel ~with_positions:true c + +let parse_segments ?filename l = match parse_trcs ?filename l with +| TracesWithSegments (s, []) -> s +| _ -> raise (ParseError (l.lex_start_p, "Data is not SEGMENTS")) + +let parse_segments_string ?filename (s : string) : instruction_segments = + parse_segments ?filename @@ Lexing.from_string ~with_positions:true s + +let parse_segments_channel ?filename (c : in_channel) : instruction_segments = + parse_segments ?filename @@ Lexing.from_channel ~with_positions:true c + (*$R try let exp = parse_exp_string ~filename:"test" "v42" in diff --git a/src/isla/cache.ml b/src/isla/cache.ml index f7d5850f..47845f36 100644 --- a/src/isla/cache.ml +++ b/src/isla/cache.ml @@ -126,23 +126,14 @@ end It is just a list of traces separated by new lines *) module TraceList (*: Cache.Value *) = struct - type t = Base.rtrc list + type t = Base.rtrcs let to_file file (trcs : t) = - let output_trc ochannel trc = Pp.fprintln ochannel @@ Base.pp_trc trc in - let output_trcs = Files.output_list output_trc in - Files.write output_trcs file trcs + Files.write Pp.fprintln file (Base.pp_trcs trcs) let of_file file : t = - let num = ref 0 in - let input_trc ichannel = - let trc = Files.input_sexp ichannel in - let filename = Printf.sprintf "Trace %i of %s" !num file in - incr num; - Base.parse_trc_string ~filename trc - in - let input_trcs = Files.input_list input_trc in - Files.read input_trcs file + let filename = Printf.sprintf "Traces of %s" file in + Files.read Files.input_string file |> Base.parse_trcs_string ~filename end (** An epoch independant of the isla version, bump if you change the representation @@ -220,7 +211,7 @@ let get_cache () = match !cache with Some cache -> cache | None -> failwith "Isla cache was not started" (** Get the traces of the opcode given. Use {!Server} if the value is not in the cache *) -let get_traces (opcode : Server.opcode) : Base.rtrc list = +let get_traces (opcode : Server.opcode) : Base.rtrcs = let (cache, config) = get_cache () in match IC.get_opt cache (Some opcode) with | Some trcs -> trcs @@ -237,11 +228,13 @@ let get_traces (opcode : Server.opcode) : Base.rtrc list = let get_nop () : Base.rtrc = let (cache, _) = get_cache () in match IC.get_opt cache None with - | Some [trc] -> trc + | Some (Traces [trc]) -> trc + | Some (TracesWithSegments _) -> fatal "Corrupted cache, nop has segments" | Some _ -> fatal "Corrupted cache, nop hasn't exactly one trace" | None -> ensure_started (); - let trcs = Server.request_bin_parsed @@ Arch.nop () in + let (segs, trcs) = Server.request_bin_parsed @@ Arch.nop () in + assert (Option.is_none segs); let trc = List.assoc true trcs in - IC.add cache None [trc]; + IC.add cache None (Traces [trc]); trc diff --git a/src/isla/preprocess.ml b/src/isla/preprocess.ml index 0f187384..ec220f35 100644 --- a/src/isla/preprocess.ml +++ b/src/isla/preprocess.ml @@ -157,7 +157,7 @@ let simplify_trc (Trace events : rtrc) : rtrc = (1 + Counter.read new_variables); Trace (List.rev !res) -let preprocess (config : Server.config) (trcs : (bool * rtrc) list) : rtrc list = +let preprocess (config : Server.config) ((segs, trcs) : Server.trcs) : rtrcs = let preprocess_one (b, trc) = if not b then None else @@ -165,4 +165,7 @@ let preprocess (config : Server.config) (trcs : (bool * rtrc) list) : rtrc list let trc = simplify_trc trc in Some trc in - List.filter_map preprocess_one trcs + let trcs = List.filter_map preprocess_one trcs in + match segs with + | None -> Traces (trcs) + | Some segs -> TracesWithSegments (segs, trcs) diff --git a/src/isla/preprocess.mli b/src/isla/preprocess.mli index b78fc41d..2b4ae5be 100644 --- a/src/isla/preprocess.mli +++ b/src/isla/preprocess.mli @@ -72,4 +72,4 @@ val simplify_trc : Base.rtrc -> Base.rtrc (** Preprocess a group of traces, by removing useless registers (according to the config), removing initialisation code and simplifying with {!simplify_trc} *) -val preprocess : Server.config -> Server.trcs -> Base.rtrc list +val preprocess : Server.config -> Server.trcs -> Base.rtrcs diff --git a/src/isla/server.ml b/src/isla/server.ml index 6c093fe3..acec119a 100644 --- a/src/isla/server.ml +++ b/src/isla/server.ml @@ -66,7 +66,7 @@ type config = Config.t It is a list of traces, each with a flag telling if they are normal traces (no processor exception/fault) or not *) -type trcs = (bool * Base.rtrc) list +type trcs = Base.instruction_segments option * (bool * Base.rtrc) list type reloc = Elf.Relocations.target @@ -134,7 +134,7 @@ let raw_stop () = | None -> () (** This should match exactly with the Answer type in isla-client code *) -type basic_answer = Error | Version of string | StartTraces | Trace of bool * string | EndTraces +type basic_answer = Error | Version of string | StartTraces | Trace of bool * string | EndTraces | Segments of string (** Read an answer from isla-client. This must match exactly [write_answer] in [client.rs] in [isla] *) @@ -149,11 +149,12 @@ let read_basic_answer () = let s = Server.read_string serv in Trace (b, s) | 4 -> EndTraces + | 5 -> Segments (Server.read_string serv) | _ -> failwith "Unknown isla anwser" (** The interpreted answer. If the protocol is followed, then one request lead to exactly one answer of that type *) -type answer = Version of string | Traces of (bool * string) list +type answer = Version of string | Traces of (string option * (bool * string) list) (** Expect a version answer and fails if it is not the case *) let expect_version = function Version s -> s | _ -> failwith "expected version number from isla" @@ -163,11 +164,15 @@ let expect_traces = function Traces tl -> tl | _ -> failwith "expected traces fr (** Expect isla traces and fails if it is not the case, additionally parse them *) let expect_parsed_traces a : trcs = - a |> expect_traces - |> List.mapi (fun i (b, t) -> + let rsegs, rtrcs = expect_traces a in + let filename = Printf.sprintf "Isla call %d" !req_num in + let trcs = List.mapi (fun i (b, t) -> ( b, - let filename = Printf.sprintf "Isla call %d, trace %d" !req_num i in - Base.parse_trc_string ~filename t )) + let filename = filename ^ Printf.sprintf ", trace %d" i in + Base.parse_trc_string ~filename t )) rtrcs + in + let segs = Option.map (Base.parse_segments_string ~filename) rsegs in + segs, trcs (** When isla encounter a non fatal error with that specific request. This error is recoverable and the sever can accept other requests *) @@ -175,30 +180,38 @@ exception IslaError (** Read the answer from isla, block until full answer *) let read_answer () : answer = + let rec traces_seq () = + match read_basic_answer () with + | EndTraces -> Seq.Nil + | Trace (bool, s) -> Seq.Cons ((bool, s), traces_seq) + | Error -> raise IslaError + | _ -> failwith "isla protocol error: no EndTraces" + in match read_basic_answer () with | Error -> raise IslaError | Version s -> Version s + | Segments s -> ( + match read_basic_answer () with + | StartTraces -> Traces (Some s, List.of_seq traces_seq) + | _ -> failwith "segments not followed by traces" + ) | StartTraces -> - let rec seq () = - match read_basic_answer () with - | EndTraces -> Seq.Nil - | Trace (bool, s) -> Seq.Cons ((bool, s), seq) - | Error -> raise IslaError - | _ -> failwith "isla protocol error: no EndTraces" - in - Traces (List.of_seq seq) + Traces (None, List.of_seq traces_seq) | _ -> failwith "isla protocol error: Traces element before StartTraces" (** Answer pretty printer *) let pp_answer = function | Version s -> Pp.(prefix 2 1 !^"isla-client version:" !^s) - | Traces l -> - l - |> List.map (fun (b, t) -> - Pp.( - let bdoc = if b then !^"norm:" else !^"ex:" in - prefix 2 1 bdoc (string t))) - |> Pp.(separate (hardline ^^ hardline)) + | Traces (s, l) -> + Pp.( + optional string s + ^^ hardline + ^^ hardline + ^^ (l + |> List.map (fun (b, t) -> + let bdoc = if b then !^"norm:" else !^"ex:" in + prefix 2 1 bdoc (string t)) + |> separate (hardline ^^ hardline))) (** The type of a request to isla *) type request = TEXT_ASM of string | ASM of BytesSeq.t | VERSION | STOP diff --git a/src/isla/test.ml b/src/isla/test.ml index c44343d6..d1e07e4e 100644 --- a/src/isla/test.ml +++ b/src/isla/test.ml @@ -204,7 +204,7 @@ let isla_run isla_mode arch (filename, input) : string * string * Server.config start config; let msg : string = match request (isla_mode_to_request isla_mode input) with - | Traces l -> List.assoc true l + | Traces (_, l) -> List.assoc true l (* TODO segments *) | _ -> failwith "isla did not send back traces" in stop (); diff --git a/src/run/bb_lib.ml b/src/run/bb_lib.ml index 477a9d98..6790fe1a 100644 --- a/src/run/bb_lib.ml +++ b/src/run/bb_lib.ml @@ -64,8 +64,9 @@ type t = { main : trc array } Also does the typing of traces for register discovery. TODO Support variable length instructions *) -let from_binary (code : BytesSeq.t) : t = - let num = BytesSeq.length code / 4 in +let from_binary (_code : BytesSeq.t) : t = + Raise.todo() + (* let num = BytesSeq.length code / 4 in (* TODO fix fixed size instructions *) if BytesSeq.length code != num * 4 then failwith "BB.from_binary: The specified range cuts an instruction"; @@ -83,7 +84,7 @@ let from_binary (code : BytesSeq.t) : t = (code, None) |> Isla.Cache.get_traces |> get_normal (*TODO relocs *) in let main = code |> BytesSeq.to_listbs ~len:4 |> List.map process |> Array.of_list in - { main } + { main } *) (* Sequence of the second test: mpool.c:116.6 (mpool_fini) 40012240: 37000049 tbnz diff --git a/src/run/instr.ml b/src/run/instr.ml index efa790bc..0098d35b 100644 --- a/src/run/instr.ml +++ b/src/run/instr.ml @@ -148,14 +148,15 @@ let simp_trace_term = Term.(const ( || ) $ simp_trace $ simp) let simp_state_term = Term.(const ( || ) $ simp_state $ simp) -let get_traces instr isla_run dump_types : traces = - Isla.Cache.start @@ Arch.get_isla_config (); +let get_traces _instr _isla_run _dump_types : traces = + Raise.todo() + (* Isla.Cache.start @@ Arch.get_isla_config (); (* I call Init.init manually to print the register types *) Init.init () |> ignore; let rtraces = Isla.Cache.get_traces (instr, None) in (* TODO relocs *) List.iter (fun t -> Isla.Type.type_trc t |> ignore) rtraces; if dump_types then base "Register types:\n%t\n" (Pp.topi State.Reg.pp_index ()); - if isla_run then IslaTraces rtraces else Traces (List.map Trace.of_isla rtraces) + if isla_run then IslaTraces rtraces else Traces (List.map Trace.of_isla rtraces) *) let pre_traces_term = Term.(const get_traces $ instr_term $ isla_run $ reg_types) diff --git a/src/trace/base.ml b/src/trace/base.ml index eb0a88b0..891d3133 100644 --- a/src/trace/base.ml +++ b/src/trace/base.ml @@ -320,8 +320,9 @@ let events_of_isla ~written_registers ~read_counter ~(vc : value_context) : | AbstractCall _ -> [] | AbstractPrimop _ -> [] + (* TODO segments *) (** Top level function to convert an isla trace to one of this module *) -let of_isla (Trace events : Isla.rtrc) : t = +let of_isla (_segments: Isla.segment list) (Trace events : Isla.rtrc) : t = let written_registers = Hashtbl.create 10 in let read_counter = Counter.make 0 in let vc = HashVector.empty () in diff --git a/src/trace/cache.ml b/src/trace/cache.ml index 0f7d7e33..cd5e611b 100644 --- a/src/trace/cache.ml +++ b/src/trace/cache.ml @@ -160,8 +160,11 @@ let get_traces (opcode : Isla.Server.opcode) : Base.t list = match TC.get_opt cache (Some opcode) with | Some trcs -> trcs | None -> - let isla_traces = Isla.Cache.get_traces opcode in - let traces = List.map (tee (Isla.Type.type_trc %> ignore) %> Base.of_isla) isla_traces in + let segments, isla_traces = match Isla.Cache.get_traces opcode with + | Traces t -> [], t + | TracesWithSegments (Segments s, t) -> s, t + in + let traces = List.map (tee (Isla.Type.type_trc %> ignore) %> Base.of_isla segments) isla_traces in let straces = List.map Base.simplify traces in TC.add cache (Some opcode) straces; straces From 2d5379fb54038023bc3e9b9e8dd55d3d7aff78f5 Mon Sep 17 00:00:00 2001 From: Matej Urban Date: Thu, 9 Jan 2025 20:35:01 +0100 Subject: [PATCH 018/116] [WIP] symbolic traces --- src/arch/aarch64/sig.ml | 3 ++- src/elf/relocations.ml | 14 ++++++++++++- src/elf/symbol.ml | 2 +- src/isla/cache.ml | 5 ++--- src/isla/preprocess.ml | 20 +++++++++++++----- src/isla/preprocess.mli | 2 +- src/isla/server.ml | 34 +++++++++++++++++++++++++++--- src/isla/test.ml | 4 ++-- src/run/runner.ml | 8 ++++--- src/trace/base.ml | 46 +++++++++++++++++++++++++++++------------ src/trace/context.ml | 8 +------ src/trace/instr.ml | 2 +- src/trace/typer.ml | 1 + 13 files changed, 108 insertions(+), 41 deletions(-) diff --git a/src/arch/aarch64/sig.ml b/src/arch/aarch64/sig.ml index 658dab20..c46bb8b8 100644 --- a/src/arch/aarch64/sig.ml +++ b/src/arch/aarch64/sig.ml @@ -363,7 +363,8 @@ let assemble_to_elf instr = let split_into_instrs (data: Elf.Symbol.data) = let module IMap = Elf.Relocations.IMap in let rawdata = BytesSeq.to_listbs ~len:4 data.data in - List.mapi (fun pos bytes -> + List.mapi (fun i bytes -> + let pos = 4 * i in let (_, rel, rest) = IMap.split pos data.relocations in if Option.is_some @@ IMap.find_first_opt (fun i -> i < pos + 4) rest then Raise.fail "Misaligned relocation"; diff --git a/src/elf/relocations.ml b/src/elf/relocations.ml index e48871b7..b129d48f 100644 --- a/src/elf/relocations.ml +++ b/src/elf/relocations.ml @@ -25,8 +25,20 @@ let sub rels off len = |> List.filter_map (fun (pos, rel) -> if off <= pos && pos < off + len then Some (pos-off, rel) else None) |> IMap.of_list +let pp_rel rel = + let target = match rel.target with + | AArch64 Abi_aarch64_symbolic_relocation.Data640 -> "Data64" + | AArch64 Abi_aarch64_symbolic_relocation.Data320 -> "Data32" + | AArch64 Abi_aarch64_symbolic_relocation.ADD -> "ADD" + | AArch64 Abi_aarch64_symbolic_relocation.ADRP -> "ADRP" + | AArch64 Abi_aarch64_symbolic_relocation.CALL -> "CALL" + | AArch64 Abi_aarch64_symbolic_relocation.LDST -> "LDST" + in + let expr = Elf_symbolic.pp_sym_expr rel.value in + Pp.(!^target ^^ !^": " ^^ !^expr) + let pp rels = if IMap.is_empty rels then Pp.empty else - Pp.string "(has relocations)" \ No newline at end of file + Pp.(mapping "relocations" @@ List.map (fun (i, r) -> (hex i, pp_rel r)) (IMap.to_list rels)) \ No newline at end of file diff --git a/src/elf/symbol.ml b/src/elf/symbol.ml index 8bdce76a..263c184b 100644 --- a/src/elf/symbol.ml +++ b/src/elf/symbol.ml @@ -142,5 +142,5 @@ let pp_raw sym = (* ("addr", ptr sym.addr); *) ("size", ptr sym.size); ("writable", bool sym.writable); - ("data", BytesSeq.ppby ~by:4 sym.data.data ^^ Relocations.pp sym.data.relocations); + ("data", pair (BytesSeq.ppby ~by:4) Relocations.pp (sym.data.data, sym.data.relocations)); ]) diff --git a/src/isla/cache.ml b/src/isla/cache.ml index 47845f36..a44cc8f3 100644 --- a/src/isla/cache.ml +++ b/src/isla/cache.ml @@ -217,8 +217,7 @@ let get_traces (opcode : Server.opcode) : Base.rtrcs = | Some trcs -> trcs | None -> ensure_started (); - let raw_opcode, _ = opcode in (*TODO*) - let trcs = Server.request_bin_parsed raw_opcode in + let trcs = Server.request_bin_parsed opcode in let ptrcs = Preprocess.preprocess config trcs in IC.add cache (Some opcode) ptrcs; ptrcs @@ -233,7 +232,7 @@ let get_nop () : Base.rtrc = | Some _ -> fatal "Corrupted cache, nop hasn't exactly one trace" | None -> ensure_started (); - let (segs, trcs) = Server.request_bin_parsed @@ Arch.nop () in + let (segs, trcs) = Server.request_bin_parsed @@ (Arch.nop (), None) in assert (Option.is_none segs); let trc = List.assoc true trcs in IC.add cache None (Traces [trc]); diff --git a/src/isla/preprocess.ml b/src/isla/preprocess.ml index ec220f35..7aa0abd1 100644 --- a/src/isla/preprocess.ml +++ b/src/isla/preprocess.ml @@ -68,7 +68,7 @@ let expect_processed = function | _ -> Raise.fail "Variables should be processed at this point" (** Preprocess a single trace *) -let simplify_trc (Trace events : rtrc) : rtrc = +let simplify_trc ?(num_segments = 0) (Trace events : rtrc) : rtrc = (* Phase 1: Discover which variable are actually used *) let used = HashVector.empty () in let process_used event = @@ -90,7 +90,8 @@ let simplify_trc (Trace events : rtrc) : rtrc = not yet commited are inlined. Variables are also renumbered at the same time. *) let simplify_context = HashVector.empty () in - let new_variables = Counter.make 0 in + (* Segment variables should not be renamed (we assume that those are v0-v(num_segments-1)) *) + let new_variables = Counter.make num_segments in let res = ref [] in let push_event (d : revent) = res := d :: !res in let push_smt loc (d : rsmt) = push_event (Smt (d, loc)) in @@ -100,13 +101,13 @@ let simplify_trc (Trace events : rtrc) : rtrc = match HashVector.get simplify_context i with | Declared { ty; loc } -> debug "Commiting declared variable %d" i; - let new_val = Counter.get new_variables in + let new_val = if i < num_segments then i else Counter.get new_variables in HashVector.set simplify_context i (Processed new_val); push_smt loc (DeclareConst (new_val, ty)); new_val | Defined { exp; loc } -> debug "Commiting defined variable %d" i; - let new_val = Counter.get new_variables in + let new_val = if i < num_segments then i else Counter.get new_variables in HashVector.set simplify_context i (Processed new_val); debug "New id is %d" new_val; let new_exp = simplify_exp exp in @@ -158,11 +159,20 @@ let simplify_trc (Trace events : rtrc) : rtrc = Trace (List.rev !res) let preprocess (config : Server.config) ((segs, trcs) : Server.trcs) : rtrcs = + let num_segments = + segs + |> Option.map (fun (Segments s) -> + let x = List.length s in + assert (List.for_all (fun (Segment (_,_,v)) -> v < x) s); + x + ) + |> Option.value ~default:0 + in let preprocess_one (b, trc) = if not b then None else let trc = trc |> Manip.remove_init |> Manip.remove_ignored config.ignored_regs in - let trc = simplify_trc trc in + let trc = simplify_trc ~num_segments trc in Some trc in let trcs = List.filter_map preprocess_one trcs in diff --git a/src/isla/preprocess.mli b/src/isla/preprocess.mli index 2b4ae5be..73c8a70f 100644 --- a/src/isla/preprocess.mli +++ b/src/isla/preprocess.mli @@ -67,7 +67,7 @@ *) (** Simplify a simple trace by removing all useless variables *) -val simplify_trc : Base.rtrc -> Base.rtrc +val simplify_trc : ?num_segments:int -> Base.rtrc -> Base.rtrc (** Preprocess a group of traces, by removing useless registers (according to the config), removing initialisation code and simplifying with diff --git a/src/isla/server.ml b/src/isla/server.ml index acec119a..82bd8603 100644 --- a/src/isla/server.ml +++ b/src/isla/server.ml @@ -214,13 +214,41 @@ let pp_answer = function |> separate (hardline ^^ hardline))) (** The type of a request to isla *) -type request = TEXT_ASM of string | ASM of BytesSeq.t | VERSION | STOP +type request = TEXT_ASM of string | ASM of opcode | VERSION | STOP + +let pp_interpreted_opcode (b, r) = + match r with + | None -> Pp.(!^"#x" ^^ BytesSeq.ppint b) + | Some (Elf.Relocations.AArch64 rtype) -> + let bits = BytesSeq.getbvle ~size:32 b 0 in + Pp.( + match rtype with + | Abi_aarch64_symbolic_relocation.Data640 -> Raise.fail "64bit relocation not allowed for instruction" + | Abi_aarch64_symbolic_relocation.Data320 -> !^"x:32" + | Abi_aarch64_symbolic_relocation.ADRP -> + BitVec.pp_smt (BitVec.extract 31 31 bits) + ^^ !^" x0:2 " ^^ + BitVec.pp_smt (BitVec.extract 24 28 bits) + ^^ !^" x1:19 " ^^ + BitVec.pp_smt (BitVec.extract 0 4 bits) + | Abi_aarch64_symbolic_relocation.ADD -> + BitVec.pp_smt (BitVec.extract 22 31 bits) + ^^ !^" x0:12 " ^^ + BitVec.pp_smt (BitVec.extract 0 9 bits) + | Abi_aarch64_symbolic_relocation.LDST -> (* TODO different width loads, alignment *) + BitVec.pp_smt (BitVec.extract 22 31 bits) + ^^ !^" x0:10 " ^^ + BitVec.pp_smt (BitVec.extract 0 11 bits) + | Abi_aarch64_symbolic_relocation.CALL -> + BitVec.pp_smt (BitVec.extract 26 31 bits) + ^^ !^" x0:26 " + ) (** Convert a request into the string message expected by isla-client This should match the protocol *) let string_of_request = function | TEXT_ASM s -> Printf.sprintf "execute_asm %s" s - | ASM b -> Pp.(sprintc @@ !^"execute " ^^ BytesSeq.ppint b) + | ASM b -> Pp.(sprintc @@ !^"execute " ^^ pp_interpreted_opcode b) | VERSION -> "version" | STOP -> "stop" @@ -243,7 +271,7 @@ let request (req : request) : answer = req |> string_of_request |> string_reques This is the main entry point of this module. *) -let request_bin_parsed (bin : BytesSeq.t) : trcs = ASM bin |> request |> expect_parsed_traces +let request_bin_parsed (opcode : opcode) : trcs = ASM opcode |> request |> expect_parsed_traces (** Send a request without expecting any answer *) let send_request req = req |> string_of_request |> send_string_request diff --git a/src/isla/test.ml b/src/isla/test.ml index d1e07e4e..b7a06ad7 100644 --- a/src/isla/test.ml +++ b/src/isla/test.ml @@ -186,8 +186,8 @@ let isla_mode_term = let isla_mode_to_request imode input = match imode with | ASM -> Server.TEXT_ASM input - | HEX -> Server.ASM (BytesSeq.of_hex input) - | BIN -> Server.ASM (BytesSeq.of_string input) + | HEX -> Server.ASM (BytesSeq.of_hex input, None) (* TODO? *) + | BIN -> Server.ASM (BytesSeq.of_string input, None) | _ -> assert false (** Run isla and return a text trace with a filename diff --git a/src/run/runner.ml b/src/run/runner.ml index c3a4e031..dfc4ba73 100644 --- a/src/run/runner.ml +++ b/src/run/runner.ml @@ -94,7 +94,8 @@ let of_dwarf dwarf = of_elf ~dwarf dwarf.elf let load_sym runner (sym : Elf.Symbol.t) = info "Loading symbol %s in %s" sym.name runner.elf.filename; Vec.add_one runner.funcs sym.addr; - let opcode_list = Arch.split_into_instrs sym.data in (* TODO relocations *) + debug "Loding symbol %t" (Pp.top Elf.Symbol.pp_raw sym); + let opcode_list = Arch.split_into_instrs sym.data in let addr = ref sym.addr in List.iter (fun Elf.Symbol.{ data = code; relocations } -> @@ -103,9 +104,10 @@ let load_sym runner (sym : Elf.Symbol.t) = addr := Elf.Address.(!addr + len); (result, len) in + debug "Relocation at address %t: %t" (Pp.top Elf.Address.pp addr) (Pp.top Elf.Relocations.pp relocations); try - let _reloc = Elf.Relocations.IMap.find_opt 0 relocations in - let instr = Trace.Cache.get_instr (code, None) in (*TODO relocs*) + let reloc = Elf.Relocations.IMap.find_opt 0 relocations in + let instr = Trace.Cache.get_instr (code, Option.map (fun (x : Elf.Relocations.rel) -> x.target) reloc) in if instr.traces = [] then begin debug "Instruction at %t in %s is loaded as special" (Pp.top Elf.Address.pp addr) sym.name; Hashtbl.add runner.instrs addr (Special instr_len) diff --git a/src/trace/base.ml b/src/trace/base.ml index 891d3133..2419e7b7 100644 --- a/src/trace/base.ml +++ b/src/trace/base.ml @@ -93,6 +93,7 @@ module Var = struct | Register of Reg.t (** The value of the register at the beginning of the trace *) | Read of int * Ast.Size.t (** The result of that memory reading operation *) | NonDet of int * Ast.Size.t (** Variable representing non-determinism in the spec *) + | Segment of string * int (** Variable representing symbolic segment in the opcode *) (** Convert the variable to the string encoding. For parsing infractructure reason, the encoding must always contain at least one [:]. *) @@ -104,6 +105,8 @@ module Var = struct | NonDet (num, size) -> if size = Ast.Size.B64 then Printf.sprintf "nondet:%i" num else Printf.sprintf "nondet:%i:%dbits" num (Ast.Size.to_bits size) + | Segment (name, bits) -> + Printf.sprintf "segment:%s:%dbits" name bits (** Inverse of {!to_string} *) let of_string s = @@ -117,6 +120,9 @@ module Var = struct | ["nondet"; num; size] -> let size = Scanf.sscanf size "%dbits" Ast.Size.of_bits in NonDet (int_of_string num, size) + | ["segment"; name; bits] -> + let bits = Scanf.sscanf bits "%dbits" Fun.id in + Segment (name, bits) | _ -> Raise.inv_arg "%s is not a Base.Var.t" s (** Pretty prints the variable *) @@ -130,6 +136,7 @@ module Var = struct | Register reg -> Reg.reg_type reg | Read (_, size) -> Ast.Ty_BitVec (Ast.Size.to_bits size) | NonDet (_, size) -> Ast.Ty_BitVec (Ast.Size.to_bits size) + | Segment (_, bits) -> Ast.Ty_BitVec bits let of_reg reg = Register reg end @@ -248,17 +255,27 @@ let write_to_valu vc valu exp = match valu with Isla.(RegVal_Base (Val_Symbolic i)) -> HashVector.set vc i exp | _ -> () (** Convert an isla event to Trace events, most events are deleted *) -let events_of_isla ~written_registers ~read_counter ~(vc : value_context) : +let events_of_isla ~segments_map ~written_registers ~read_counter ~(vc : value_context) : Isla.revent -> event list = function | Smt (DeclareConst (i, ty), _) -> - ( try - match ty with - | Ty_BitVec ((8 | 16 | 32 | 64 | 128) as size) -> - HashVector.set vc i (Exp.of_var (Var.NonDet (i, Ast.Size.of_bits size))) - | Ty_BitVec _ | Ty_Bool | Ty_Enum _ | Ty_Array (_, _) -> - debug "Unimplemented: ignoring non-det variable %i of type %t" i - (Pp.top Isla.pp_ty ty) - with OfIslaError -> warn "not setting nondet:%d" i + ( match HashVector.get_opt segments_map i with + | Some (name, size) -> + let ty_match = match ty with + | Ty_BitVec sz -> size = sz + | _ -> false + in + if not ty_match then fatal "Variable type doesn't match instruction segment %t and %d" (Pp.top Isla.pp_ty ty) size + else + HashVector.set vc i (Exp.of_var (Var.Segment (name, size))) + | None -> + try + match ty with + | Ty_BitVec ((8 | 16 | 32 | 64 | 128) as size) -> + HashVector.set vc i (Exp.of_var (Var.NonDet (i, Ast.Size.of_bits size))) + | Ty_BitVec _ | Ty_Bool | Ty_Enum _ | Ty_Array (_, _) -> + debug "Unimplemented: ignoring non-det variable %i of type %t" i + (Pp.top Isla.pp_ty ty) + with OfIslaError -> warn "not setting nondet:%d" i ); [] | Smt (DefineConst (i, e), _) -> @@ -320,13 +337,16 @@ let events_of_isla ~written_registers ~read_counter ~(vc : value_context) : | AbstractCall _ -> [] | AbstractPrimop _ -> [] - (* TODO segments *) (** Top level function to convert an isla trace to one of this module *) -let of_isla (_segments: Isla.segment list) (Trace events : Isla.rtrc) : t = +let of_isla (segments: Isla.segment list) (Trace events : Isla.rtrc) : t = let written_registers = Hashtbl.create 10 in let read_counter = Counter.make 0 in let vc = HashVector.empty () in - List.concat_map (events_of_isla ~written_registers ~read_counter ~vc) events + let segments_map = HashVector.empty () in + List.iter (fun (Isla.Segment (name, size, var)) -> + HashVector.set segments_map var (name, size) + ) segments; + List.concat_map (events_of_isla ~segments_map ~written_registers ~read_counter ~vc) events (*****************************************************************************) (*****************************************************************************) @@ -346,7 +366,7 @@ let declare_non_det serv events = iter_var (function | Register _ | Read _ -> () - | NonDet _ as var -> + | NonDet _ | Segment _ as var -> if not @@ VarTbl.mem declared @@ var then begin Z3Tr.declare_var_always serv var; VarTbl.add declared var () diff --git a/src/trace/context.ml b/src/trace/context.ml index bfbce294..fb7a54a4 100644 --- a/src/trace/context.ml +++ b/src/trace/context.ml @@ -68,13 +68,7 @@ let expand_var ~(ctxt : t) (v : Base.Var.t) (a : Ast.no Ast.ty) : State.exp = match v with | Register reg -> State.get_reg_exp ctxt.state reg | NonDet (i, _) | Read (i, _) -> (HashVector.get ctxt.mem_reads i).exp (* TODO is the NonDet case correct *) - -let map_var ~(ctxt : t) (v : Base.Var.t) (a : Ast.no Ast.ty) : State.var = - assert (Base.Var.ty v = a); - match v with - | Register reg -> State.Var.Register (ctxt.state.id, reg) - | NonDet (i, size) -> State.Var.NonDet (i, size) - | Read (i, size) -> State.Var.ReadVar (ctxt.state.id, i, size) + | Segment (_name, size) -> Exp.Typed.bits (BitVec.zero ~size) (* TODO put the actual value there *) (** Tell if typing should enabled with this context *) let typing_enabled ~(ctxt : t) = ctxt.dwarf <> None diff --git a/src/trace/instr.ml b/src/trace/instr.ml index 4f0b54b9..25d1a9e5 100644 --- a/src/trace/instr.ml +++ b/src/trace/instr.ml @@ -81,7 +81,7 @@ let trace_meta_of_trace trace = let jump = ref None in let process_var = function | Base.Var.Register reg -> read := reg :: !read - | Base.Var.(Read _ | NonDet _) -> () + | Base.Var.(Read _ | NonDet _ | Segment _) -> () in let process_exp : Base.exp -> unit = Ast.Manip.exp_iter_var process_var in let process_event : Base.event -> unit = function diff --git a/src/trace/typer.ml b/src/trace/typer.ml index cf04c5af..238a17ef 100644 --- a/src/trace/typer.ml +++ b/src/trace/typer.ml @@ -203,6 +203,7 @@ let rec expr ~ctxt (exp : Base.exp) : Ctype.t option = | Var (Register reg, _) -> State.get_reg ctxt.state reg |> State.Tval.ctyp | Var (Read (r, _), _) -> HashVector.get ctxt.mem_reads r |> State.Tval.ctyp | Var (NonDet _, _) -> None + | Var (Segment _, _) -> None (* TODO? *) | Bits (bv, _) -> let size = BitVec.size bv in if size mod 8 = 0 || size = Arch.address_size then From 36f77417fbb3113ed4a056ceee4b2f36d66006d3 Mon Sep 17 00:00:00 2001 From: Matej Urban Date: Fri, 10 Jan 2025 15:20:45 +0100 Subject: [PATCH 019/116] Store segments with instruction --- src/elf/relocations.ml | 39 +++++++++++++++++++++++++++++++++++---- src/isla/server.ml | 15 +++++++++++++-- src/run/runner.ml | 4 ++-- src/trace/cache.ml | 5 ++++- src/trace/instr.ml | 27 ++++++++++++++++++++++----- 5 files changed, 76 insertions(+), 14 deletions(-) diff --git a/src/elf/relocations.ml b/src/elf/relocations.ml index b129d48f..b253f100 100644 --- a/src/elf/relocations.ml +++ b/src/elf/relocations.ml @@ -3,19 +3,39 @@ module IMap = Map.Make (Int) type target = AArch64 of Abi_aarch64_symbolic_relocation.aarch64_relocation_target +type binary_operation = Elf_symbolic.binary_operation + +type unary_operation = Elf_symbolic.unary_operation + +type exp = +| Section of string +| Const of int +| BinOp of (exp * binary_operation * exp) +| UnOp of (unary_operation * exp) +(* | AssertRange of (exp * int * int) *) +| Mask of (exp * int * int) + type rel = { target : target; - value : Elf_symbolic.symbolic_expression; + value : exp; } type t = rel IMap.t type linksem_t = LinksemRelocatable.rels +let rec exp_of_linksem = function +| Elf_symbolic.Section s -> Section s +| Elf_symbolic.Const x -> Const (Z.to_int x) +| Elf_symbolic.BinOp (x, op, y) -> BinOp (exp_of_linksem x, op, exp_of_linksem y) +| Elf_symbolic.UnOp (op, x) -> UnOp (op, exp_of_linksem x) +| Elf_symbolic.AssertRange (x, _, _) -> exp_of_linksem x (* TODO *) +| Elf_symbolic.Mask (x, a, b) -> Mask (exp_of_linksem x, Z.to_int a, Z.to_int b) + let of_linksem: linksem_t -> t = function | LinksemRelocatable.AArch64 relocs -> let add k Elf_symbolic.{ arel_value; arel_target } m = - IMap.add (Z.to_int k) { value = arel_value; target = AArch64 arel_target } m + IMap.add (Z.to_int k) { value = exp_of_linksem arel_value; target = AArch64 arel_target } m in Pmap.fold add relocs IMap.empty @@ -25,6 +45,17 @@ let sub rels off len = |> List.filter_map (fun (pos, rel) -> if off <= pos && pos < off + len then Some (pos-off, rel) else None) |> IMap.of_list +let rec pp_exp = Pp.( + function + | Section s -> !^s + | Const x -> int x + | BinOp (a, Add, b) -> !^"(" ^^ pp_exp a ^^ !^"+" ^^ pp_exp b ^^ !^")" + | BinOp (a, Sub, b) -> !^"(" ^^ pp_exp a ^^ !^"-" ^^ pp_exp b ^^ !^")" + | BinOp (a, And, b) -> !^"(" ^^ pp_exp a ^^ !^"&" ^^ pp_exp b ^^ !^")" + | UnOp (Not, b) -> !^"(" ^^ !^"~" ^^ pp_exp b ^^ !^")" + | Mask (x, a, b) -> pp_exp x ^^ !^"[" ^^ int a ^^ !^":" ^^ int b ^^ !^"]" +) + let pp_rel rel = let target = match rel.target with | AArch64 Abi_aarch64_symbolic_relocation.Data640 -> "Data64" @@ -34,8 +65,8 @@ let pp_rel rel = | AArch64 Abi_aarch64_symbolic_relocation.CALL -> "CALL" | AArch64 Abi_aarch64_symbolic_relocation.LDST -> "LDST" in - let expr = Elf_symbolic.pp_sym_expr rel.value in - Pp.(!^target ^^ !^": " ^^ !^expr) + let expr = pp_exp rel.value in + Pp.(!^target ^^ !^": " ^^ expr) let pp rels = if IMap.is_empty rels then diff --git a/src/isla/server.ml b/src/isla/server.ml index 82bd8603..a4fa0bb4 100644 --- a/src/isla/server.ml +++ b/src/isla/server.ml @@ -81,6 +81,8 @@ let reloc_id: reloc option -> int = function type opcode = BytesSeq.t * reloc option +type reloc_segment = string * (int * int) (* mapping the name of a segment to the range of the relocation value *) + (** Bump when updating isla. TODO: move the version checking to allow a range of version. Also, right now the cache invalidation is based on @@ -223,8 +225,8 @@ let pp_interpreted_opcode (b, r) = let bits = BytesSeq.getbvle ~size:32 b 0 in Pp.( match rtype with - | Abi_aarch64_symbolic_relocation.Data640 -> Raise.fail "64bit relocation not allowed for instruction" - | Abi_aarch64_symbolic_relocation.Data320 -> !^"x:32" + | Abi_aarch64_symbolic_relocation.Data640 -> fatal "Data64 relocation not allowed for instruction" + | Abi_aarch64_symbolic_relocation.Data320 -> fatal "Data32 relocation not allowed for instruction" | Abi_aarch64_symbolic_relocation.ADRP -> BitVec.pp_smt (BitVec.extract 31 31 bits) ^^ !^" x0:2 " ^^ @@ -244,6 +246,15 @@ let pp_interpreted_opcode (b, r) = ^^ !^" x0:26 " ) +(* for interpreting the segments *) +let segments_of_reloc: reloc -> reloc_segment list = function +| Elf.Relocations.AArch64 Abi_aarch64_symbolic_relocation.Data640 -> fatal "invalid relocation for instructions (Data64)" +| Elf.Relocations.AArch64 Abi_aarch64_symbolic_relocation.Data320 -> fatal "invalid relocation for instructions (Data32)" +| Elf.Relocations.AArch64 Abi_aarch64_symbolic_relocation.ADRP -> ["x0", (0, 1); "x1", (2, 20)] (* or absolute? ["x0", (12, 13); "x1", (14, 32)] *) +| Elf.Relocations.AArch64 Abi_aarch64_symbolic_relocation.ADD -> ["x0", (0, 11)] +| Elf.Relocations.AArch64 Abi_aarch64_symbolic_relocation.LDST -> ["x0", (0, 9)] (* TODO depends on load size *) (* or absolute? ["x0", (2, 11)] *) +| Elf.Relocations.AArch64 Abi_aarch64_symbolic_relocation.CALL -> ["x0", (0, 25)] (* or absolute? ["x0", (2, 27)] *) + (** Convert a request into the string message expected by isla-client This should match the protocol *) let string_of_request = function diff --git a/src/run/runner.ml b/src/run/runner.ml index dfc4ba73..d21193d3 100644 --- a/src/run/runner.ml +++ b/src/run/runner.ml @@ -107,7 +107,7 @@ let load_sym runner (sym : Elf.Symbol.t) = debug "Relocation at address %t: %t" (Pp.top Elf.Address.pp addr) (Pp.top Elf.Relocations.pp relocations); try let reloc = Elf.Relocations.IMap.find_opt 0 relocations in - let instr = Trace.Cache.get_instr (code, Option.map (fun (x : Elf.Relocations.rel) -> x.target) reloc) in + let instr = Trace.Cache.get_instr (code, reloc) in if instr.traces = [] then begin debug "Instruction at %t in %s is loaded as special" (Pp.top Elf.Address.pp addr) sym.name; Hashtbl.add runner.instrs addr (Special instr_len) @@ -193,7 +193,7 @@ let skip runner state : State.t list = let pc = pc_exp |> Ast.expect_bits |> BitVec.to_int in let pc = Elf.Address.{ section = ".text"; offset = pc } in (* TODO this is wrong, should get symbolic value from pc_exp *) match fetch runner pc with - | Normal { traces = _; read = _; written = _; length; opcode = _ } + | Normal { traces = _; read = _; written = _; length; opcode = _; segments = _ } |Special length |IslaFail length -> let state = State.copy_if_locked state in diff --git a/src/trace/cache.ml b/src/trace/cache.ml index cd5e611b..7cb5625d 100644 --- a/src/trace/cache.ml +++ b/src/trace/cache.ml @@ -171,4 +171,7 @@ let get_traces (opcode : Isla.Server.opcode) : Base.t list = (** Get a full blown {!Instr} from the opcode, going through the whole Isla pipeline if necessary.*) -let get_instr (opcode : Isla.Server.opcode) : Instr.t = Instr.of_traces opcode @@ get_traces opcode +let get_instr (opcode : BytesSeq.t * Elf.Relocations.rel option) : Instr.t = + let raw_opcode, reloc = opcode in + let reloc_target = Option.map (fun (x : Elf.Relocations.rel) -> x.target) reloc in + Instr.of_traces opcode @@ get_traces (raw_opcode, reloc_target) diff --git a/src/trace/instr.ml b/src/trace/instr.ml index 25d1a9e5..012273a5 100644 --- a/src/trace/instr.ml +++ b/src/trace/instr.ml @@ -60,13 +60,16 @@ type trace_meta = { written : Reg.t list; } +module SMap = Map.Make (String) + (** A full instruction representation *) type t = { traces : trace_meta list; length : int; (** Bytes length *) read : Reg.t list; written : Reg.t list; - opcode : Isla.Server.opcode; + opcode : BytesSeq.t; + segments: Elf.Relocations.exp SMap.t } let dedup_regs = List.sort_uniq State.Reg.compare @@ -99,17 +102,31 @@ let trace_meta_of_trace trace = { trace; jump_target = !jump; read = dedup_regs !read; written = dedup_regs !written } (** Generate full instruction data from a list of traces *) -let of_traces opcode traces = +let of_traces (opcode: BytesSeq.t * Elf.Relocations.rel option) traces = let traces = List.map trace_meta_of_trace traces in - let raw_opcode, _ = opcode in - let length = BytesSeq.length raw_opcode in + let opcode, reloc = opcode in + let length = BytesSeq.length opcode in let read = dedup_regs @@ List.concat_map (fun (tr : trace_meta) -> tr.read) traces in let written = dedup_regs @@ List.concat_map (fun (tr : trace_meta) -> tr.written) traces in - { traces; length; read; written; opcode } + + let segments = match reloc with + | None -> SMap.empty + | Some reloc -> + reloc.target + |> Isla.Server.segments_of_reloc + |> List.map (fun (v, (lo, hi)) -> (v, Elf.Relocations.Mask(reloc.value, hi, lo))) + |> SMap.of_list + in + { traces; length; read; written; opcode; segments } (** Pretty print the representation of an instruction *) let pp instr = let open Pp in + (prefix 4 1 !^"Segments:" @@ + separate_map hardline + (pair string Elf.Relocations.pp_exp) + (SMap.to_list instr.segments)) + ^^ hardline ^^ separate_mapi hardline (fun i trc -> prefix 4 1 (dprintf "Trace %d:" i) (Base.pp trc.trace)) instr.traces From 90b3ac8106d8819cea250fa74bfab1ffc7db052b Mon Sep 17 00:00:00 2001 From: Matej Urban Date: Fri, 10 Jan 2025 16:24:28 +0100 Subject: [PATCH 020/116] WIP eval relocations --- src/run/runner.ml | 5 +++-- src/trace/context.ml | 20 +++++++++++++++++--- src/trace/run.ml | 12 ++++++------ 3 files changed, 26 insertions(+), 11 deletions(-) diff --git a/src/run/runner.ml b/src/run/runner.ml index d21193d3..a557708e 100644 --- a/src/run/runner.ml +++ b/src/run/runner.ml @@ -165,12 +165,13 @@ let fetch (runner : t) (pc : Elf.Address.t) : slot = let execute_normal ?(prelock = ignore) ~pc runner (instr : Trace.Instr.t) state = let dwarf = runner.dwarf in let next = instr.length in + let segments_map = instr.segments in let run_pure () = List.map (fun (trc : Trace.Instr.trace_meta) -> let nstate = State.copy state in State.set_last_pc nstate pc; - Trace.Run.trace_pc_mut ?dwarf ~next nstate trc.trace; + Trace.Run.trace_pc_mut ?dwarf ~segments_map ~next nstate trc.trace; nstate) instr.traces in @@ -178,7 +179,7 @@ let execute_normal ?(prelock = ignore) ~pc runner (instr : Trace.Instr.t) state match instr.traces with | [trc] -> State.set_last_pc state pc; - Trace.Run.trace_pc_mut ?dwarf ~next state trc.trace; + Trace.Run.trace_pc_mut ?dwarf ~segments_map ~next state trc.trace; [state] | _ -> prelock state; diff --git a/src/trace/context.ml b/src/trace/context.ml index fb7a54a4..01da1e50 100644 --- a/src/trace/context.ml +++ b/src/trace/context.ml @@ -48,19 +48,33 @@ should be added here *) +module SMap = Map.Make (String) + (** The context to run a trace *) type t = { reg_writes : (State.Reg.t * State.tval) Vec.t; (** Stores the delayed register writes *) mem_reads : State.tval HashVector.t; (** Stores the result of memory reads *) state : State.t; + segments : State.exp SMap.t; dwarf : Dw.t option; (** Optionally DWARF information. If present, typing is enabled *) } +let rec exp_of_relocation: Elf.Relocations.exp -> State.exp = + let f = exp_of_relocation in function + | Section _ -> Exp.Typed.bits (BitVec.zero ~size:64) (* TODO put the actual value there, size? *) + | Const x -> Exp.Typed.bits (BitVec.of_int x ~size:64) (* TODO size? *) + | BinOp (a, Add, b) -> Exp.Typed.(f a + f b) + | BinOp (a, Sub, b) -> Exp.Typed.(f a - f b) + | BinOp (a, And, b) -> Exp.Typed.manyop (AstGen.Ott.Bvmanyarith AstGen.Ott.Bvand) [f a; f b] + | UnOp (Not, b) -> Exp.Typed.unop AstGen.Ott.Bvnot (f b) + | Mask (x, last, first) -> Exp.Typed.extract ~last ~first (f x) + (** Build a {!context} from a state *) -let make_context ?dwarf state = +let make_context ?dwarf ?segments_map state = let reg_writes = Vec.empty () in let mem_reads = HashVector.empty () in - { state; reg_writes; mem_reads; dwarf } + let segments = segments_map |> Option.value ~default:SMap.empty |> SMap.map exp_of_relocation in + { state; reg_writes; mem_reads; dwarf; segments } (** Expand a Trace variable to a State expression, using the context *) let expand_var ~(ctxt : t) (v : Base.Var.t) (a : Ast.no Ast.ty) : State.exp = @@ -68,7 +82,7 @@ let expand_var ~(ctxt : t) (v : Base.Var.t) (a : Ast.no Ast.ty) : State.exp = match v with | Register reg -> State.get_reg_exp ctxt.state reg | NonDet (i, _) | Read (i, _) -> (HashVector.get ctxt.mem_reads i).exp (* TODO is the NonDet case correct *) - | Segment (_name, size) -> Exp.Typed.bits (BitVec.zero ~size) (* TODO put the actual value there *) + | Segment (name, _) -> SMap.find name ctxt.segments (* TODO put the actual value there *) (** Tell if typing should enabled with this context *) let typing_enabled ~(ctxt : t) = ctxt.dwarf <> None diff --git a/src/trace/run.ml b/src/trace/run.ml index f6eaf1c1..df944ca7 100644 --- a/src/trace/run.ml +++ b/src/trace/run.ml @@ -114,17 +114,17 @@ let event_mut ~(ctxt : ctxt) (event : Base.event) = | Assert exp -> State.push_assert ctxt.state (expand ~ctxt exp) (** Run a trace on the provided state by mutation. Enable typing if [dwarf] is provided *) -let trace_mut ?dwarf (state : State.t) (events : Base.t) : unit = +let trace_mut ?dwarf ?segments_map (state : State.t) (events : Base.t) : unit = assert (not @@ State.is_locked state); info "Running trace with typing %s" (if dwarf <> None then "on" else "off"); - let ctxt = Context.make_context ?dwarf state in + let ctxt = Context.make_context ?dwarf ?segments_map state in List.iter (event_mut ~ctxt) events; Vec.iter (fun (reg, tval) -> State.Reg.Map.set state.regs reg tval) ctxt.reg_writes (** Run a trace on the provided state by returning an updated copy.*) -let trace ?dwarf (start : State.t) (events : Base.t) : State.t = +let trace ?dwarf ?segments_map (start : State.t) (events : Base.t) : State.t = let state = State.copy start in - trace_mut ?dwarf state events; + trace_mut ?dwarf ?segments_map state events; State.lock state; state @@ -133,12 +133,12 @@ let trace ?dwarf (start : State.t) (events : Base.t) : State.t = Thus this function automatically handle moving the PC for fall-through instruction *) -let trace_pc_mut ?dwarf ~(next : int) (state : State.t) (events : Base.t) : unit = +let trace_pc_mut ?dwarf ?segments_map ~(next : int) (state : State.t) (events : Base.t) : unit = let pc = Arch.pc () in let rec is_touching_pc : Base.t -> bool = function | [] -> false | WriteReg { reg; _ } :: _ when reg = pc -> true | _ :: l -> is_touching_pc l in - trace_mut ?dwarf state events; + trace_mut ?dwarf ?segments_map state events; if is_touching_pc events then State.concretize_pc ~pc state else State.bump_pc ~pc state next From 34824b143fe367f9bdeaf3faf30f29f7e597eb64 Mon Sep 17 00:00:00 2001 From: Matej Urban Date: Sun, 12 Jan 2025 12:14:57 +0100 Subject: [PATCH 021/116] Symbolic section addresses --- src/state/base.ml | 6 ++++++ src/state/base.mli | 2 ++ src/trace/context.ml | 2 +- 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/state/base.ml b/src/state/base.ml index 4cda529c..5b054155 100644 --- a/src/state/base.ml +++ b/src/state/base.ml @@ -76,6 +76,8 @@ module Var = struct | NonDet of int * Ast.Size.t (** Variable representing non-determinism in the spec. Can only be bit-vectors of size {8, 16, 32, 64} for now. *) + | Section of string + (** Symbolic base address of ELF section. Assume 64bit for now. *) let to_string = function | Register (state, reg) -> @@ -90,6 +92,7 @@ module Var = struct | NonDet (num, size) -> if size = Ast.Size.B64 then Printf.sprintf "nondet:%i" num else Printf.sprintf "nondet:%i:%dbits" num (Ast.Size.to_bits size) + | Section s -> "section:"^s let expect_register = function | Register (_, reg) -> reg @@ -125,6 +128,7 @@ module Var = struct | ["arg"; num] -> Arg (int_of_string num) | ["retarg"; ""] -> RetArg | ["retaddr"; ""] -> RetAddr + | ["section"; s] -> Section s | _ -> Raise.inv_arg "Invalid state variable: %s" s let of_reg id reg = Register (id, reg) @@ -138,6 +142,7 @@ module Var = struct | (RetArg, RetArg) -> true | (RetAddr, RetAddr) -> true | (NonDet (num, size), NonDet (num', size')) -> num = num' && size = size' + | (Section s, Section s') -> s = s' | _ -> false let hash = Hashtbl.hash @@ -153,6 +158,7 @@ module Var = struct | RetArg -> Ast.Ty_BitVec 64 | RetAddr -> Ast.Ty_BitVec 64 | NonDet (_, size) -> Ast.Ty_BitVec (Ast.Size.to_bits size) + | Section _ -> Ast.Ty_BitVec 64 end type var = Var.t diff --git a/src/state/base.mli b/src/state/base.mli index 05669eb6..b65853c3 100644 --- a/src/state/base.mli +++ b/src/state/base.mli @@ -113,6 +113,8 @@ module Var : sig | RetAddr (** The return address: The address to which a "return" instruction would jump. *) | NonDet of int * Ast.Size.t (** Variable representing non-determinism in the spec. Can only be a bit-vector for now. *) + | Section of string + (** Symbolic base address of ELF section. Assume 64bit for now. *) (** Convert the variable to the string encoding. For parsing infrastructure reason, the encoding must always contain at least one [:]. *) diff --git a/src/trace/context.ml b/src/trace/context.ml index 01da1e50..d3cbd143 100644 --- a/src/trace/context.ml +++ b/src/trace/context.ml @@ -61,7 +61,7 @@ type t = { let rec exp_of_relocation: Elf.Relocations.exp -> State.exp = let f = exp_of_relocation in function - | Section _ -> Exp.Typed.bits (BitVec.zero ~size:64) (* TODO put the actual value there, size? *) + | Section s -> State.Exp.of_var (State.Var.Section s) (* TODO put the actual value there, size? *) | Const x -> Exp.Typed.bits (BitVec.of_int x ~size:64) (* TODO size? *) | BinOp (a, Add, b) -> Exp.Typed.(f a + f b) | BinOp (a, Sub, b) -> Exp.Typed.(f a - f b) From 897e638fb6c98542d8f471dbb3c656983cc6d05c Mon Sep 17 00:00:00 2001 From: Matej Urban Date: Sun, 12 Jan 2025 12:41:23 +0100 Subject: [PATCH 022/116] Fix cache --- src/elf/relocations.ml | 19 +++++++++---------- src/isla/cache.ml | 9 +++++---- src/isla/server.ml | 10 ++++++++++ 3 files changed, 24 insertions(+), 14 deletions(-) diff --git a/src/elf/relocations.ml b/src/elf/relocations.ml index b253f100..8bf736ce 100644 --- a/src/elf/relocations.ml +++ b/src/elf/relocations.ml @@ -56,17 +56,16 @@ let rec pp_exp = Pp.( | Mask (x, a, b) -> pp_exp x ^^ !^"[" ^^ int a ^^ !^":" ^^ int b ^^ !^"]" ) +let pp_target = Pp.(function +| AArch64 Abi_aarch64_symbolic_relocation.Data640 -> !^"Data64" +| AArch64 Abi_aarch64_symbolic_relocation.Data320 -> !^"Data32" +| AArch64 Abi_aarch64_symbolic_relocation.ADD -> !^"ADD" +| AArch64 Abi_aarch64_symbolic_relocation.ADRP -> !^"ADRP" +| AArch64 Abi_aarch64_symbolic_relocation.CALL -> !^"CALL" +| AArch64 Abi_aarch64_symbolic_relocation.LDST -> !^"LDST") + let pp_rel rel = - let target = match rel.target with - | AArch64 Abi_aarch64_symbolic_relocation.Data640 -> "Data64" - | AArch64 Abi_aarch64_symbolic_relocation.Data320 -> "Data32" - | AArch64 Abi_aarch64_symbolic_relocation.ADD -> "ADD" - | AArch64 Abi_aarch64_symbolic_relocation.ADRP -> "ADRP" - | AArch64 Abi_aarch64_symbolic_relocation.CALL -> "CALL" - | AArch64 Abi_aarch64_symbolic_relocation.LDST -> "LDST" - in - let expr = pp_exp rel.value in - Pp.(!^target ^^ !^": " ^^ expr) + Pp.(pp_target rel.target ^^ !^": " ^^ pp_exp rel.value) let pp rels = if IMap.is_empty rels then diff --git a/src/isla/cache.ml b/src/isla/cache.ml index a44cc8f3..98d7dfa2 100644 --- a/src/isla/cache.ml +++ b/src/isla/cache.ml @@ -86,7 +86,7 @@ module Opcode (*: Cache.Key *) = struct | _ -> false let small_enough bs rel_id = - BytesSeq.length bs < BytesSeq.int_bytes && rel_id < 16 + BytesSeq.length bs < BytesSeq.int_bytes && rel_id < 8 let hash = function | None -> 0 @@ -97,7 +97,7 @@ module Opcode (*: Cache.Key *) = struct if small_enough bs rel_id then begin assert (not @@ IntBits.get i IntBits.back); let res = IntBits.blit l 0 i (IntBits.back - 3) 3 in - let res = IntBits.blit rel_id 0 res (IntBits.back - 7) 4 in + let res = IntBits.blit rel_id 0 res (IntBits.back - 6) 3 in res end else IntBits.set i IntBits.back @@ -115,11 +115,12 @@ module Opcode (*: Cache.Key *) = struct else if IntBits.get hash IntBits.back then Raise.todo() else - let data = IntBits.sub hash 0 (IntBits.back - 3) in + let data = IntBits.sub hash 0 (IntBits.back - 6) in + let reloc_id = IntBits.sub hash (IntBits.back - 6) 3 in let size = IntBits.sub hash (IntBits.back - 3) 3 in let b = Bytes.create size in Bits.unsafe_blit_of_int data 0 b 0 (size * 8); - Some (BytesSeq.of_bytes b, None) + Some (BytesSeq.of_bytes b, Server.reloc_of_id reloc_id) end (** Representation of trace lists on disk. diff --git a/src/isla/server.ml b/src/isla/server.ml index a4fa0bb4..b2ba008b 100644 --- a/src/isla/server.ml +++ b/src/isla/server.ml @@ -79,6 +79,16 @@ let reloc_id: reloc option -> int = function | Some (Elf.Relocations.AArch64 Abi_aarch64_symbolic_relocation.LDST) -> 5 | Some (Elf.Relocations.AArch64 Abi_aarch64_symbolic_relocation.CALL) -> 6 +let reloc_of_id: int -> reloc option = function +| 0 -> None +| 1 -> Some (Elf.Relocations.AArch64 Abi_aarch64_symbolic_relocation.Data640) +| 2 -> Some (Elf.Relocations.AArch64 Abi_aarch64_symbolic_relocation.Data320) +| 3 -> Some (Elf.Relocations.AArch64 Abi_aarch64_symbolic_relocation.ADRP) +| 4 -> Some (Elf.Relocations.AArch64 Abi_aarch64_symbolic_relocation.ADD) +| 5 -> Some (Elf.Relocations.AArch64 Abi_aarch64_symbolic_relocation.LDST) +| 6 -> Some (Elf.Relocations.AArch64 Abi_aarch64_symbolic_relocation.CALL) +| _ -> Raise.fail "invalid reloc id" + type opcode = BytesSeq.t * reloc option type reloc_segment = string * (int * int) (* mapping the name of a segment to the range of the relocation value *) From 88cb3402a5b97b7e8f11d472ce67266016a9b4fd Mon Sep 17 00:00:00 2001 From: Matej Urban Date: Tue, 14 Jan 2025 15:32:11 +0100 Subject: [PATCH 023/116] Make PC symbolic in isla --- src/config/config.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/config/config.toml b/src/config/config.toml index 2497ed77..9c968490 100644 --- a/src/config/config.toml +++ b/src/config/config.toml @@ -66,7 +66,7 @@ toolchain = "aarch64-linux-gnu" arch-file = "../../aarch64.ir" # relative to the toml file arch-toml = "isla_aarch64.toml" # relative to the toml file linearize = ["ConditionHolds", "integer_conditional_select", "InterruptPending"] - other-opts = [] + other-opts = ["-R", "_PC=undefined:%bv64"] [archs.riscv64] toolchain = "riscv64-linux-gnu" From ebf0b274049993f5dab4423225507a6182d44cb2 Mon Sep 17 00:00:00 2001 From: Matej Urban Date: Tue, 14 Jan 2025 18:23:24 +0100 Subject: [PATCH 024/116] Symbolic PC --- src/elf/address.ml | 16 +++++++++++++++- src/run/block_lib.ml | 30 +++++++++++++----------------- src/run/runner.ml | 6 ++---- src/state/base.ml | 22 +++++++++++++++++----- src/state/base.mli | 2 ++ 5 files changed, 49 insertions(+), 27 deletions(-) diff --git a/src/elf/address.ml b/src/elf/address.ml index c9f3b66b..701ddd89 100644 --- a/src/elf/address.ml +++ b/src/elf/address.ml @@ -7,4 +7,18 @@ let pp addr = Pp.(!^(addr.section) ^^ !^"+" ^^ ptr addr.offset) let of_linksem (section, offset) = { section; offset = Z.to_int offset } -let (+) addr offset = { section = addr.section; offset = addr.offset + offset } \ No newline at end of file +let (+) addr offset = { section = addr.section; offset = addr.offset + offset } + +let compare f {section=s1; offset=o1} {section=s2; offset=o2} = + if s1 = s2 then + Some (f o1 o2) + else + None + +let (<) = compare (<) + +let (>) = compare (>) + +let (<=) = compare (<=) + +let (>=) = compare (>=) diff --git a/src/run/block_lib.ml b/src/run/block_lib.ml index ddb6c14d..2d548c75 100644 --- a/src/run/block_lib.ml +++ b/src/run/block_lib.ml @@ -107,7 +107,7 @@ let run ?(every_instruction = false) ?relevant (b : t) (start : State.t) : label State.lock state end; let states = - let pc = pc_exp |> Ast.expect_bits |> BitVec.to_int in + let pc = State.Exp.expect_sym_address pc_exp in if Option.fold ~none:true ~some:(Fun.flip Hashtbl.mem pc) relevant then ( info "Running pc %t" (Pp.top State.Exp.pp pc_exp); Runner.run ~prelock b.runner state @@ -148,12 +148,6 @@ let run ?(every_instruction = false) ?relevant (b : t) (start : State.t) : label - pc has be seen more than [loop] *) let gen_endpred ?min ?max ?loop ?(brks = []) () : State.exp -> string option = - (* HACK *) - (* TODO rewrite for symbolic pc *) - let min = Option.map (fun min -> min.Elf.Address.offset) min in - let max = Option.map (fun max -> max.Elf.Address.offset) max in - let brks = List.map (fun brks -> brks.Elf.Address.offset) brks in - (* *) let endnow fmt = Printf.ksprintf Option.some fmt in let pchtbl = Hashtbl.create 10 in let loop_str = @@ -164,21 +158,23 @@ let gen_endpred ?min ?max ?loop ?(brks = []) () : State.exp -> string option = | Some n -> Printf.sprintf "%d times" n | None -> "" in - function - | Ast.Bits (bv, _) -> ( - let pc = BitVec.to_int bv in - debug "enpred: Evaluating PC 0x%x" pc; + fun pc_exp -> + ( try + Some (State.Exp.expect_sym_address pc_exp) + with + _ -> None + ) |> Option.map (fun pc -> + debug "enpred: Evaluating PC %t" (Pp.top Elf.Address.pp pc); match (min, max, loop) with - | (Some min, _, _) when pc < min -> endnow "PC 0x%x was below min 0x%x" pc min - | (_, Some max, _) when pc >= max -> endnow "PC 0x%x was above max 0x%x" pc max - | _ when List.exists (( = ) pc) brks -> endnow "PC 0x%x hit a breakpoint" pc + | (Some min, _, _) when Elf.Address.(pc < min) <> Some false -> endnow "PC %t was below min %t" Pp.(tos Elf.Address.pp pc) Pp.(tos Elf.Address.pp min) + | (_, Some max, _) when Elf.Address.(pc >= max) <> Some false -> endnow "PC %t was above max %t" Pp.(tos Elf.Address.pp pc) Pp.(tos Elf.Address.pp max) + | _ when List.exists (( = ) pc) brks -> endnow "PC %t hit a breakpoint" Pp.(tos Elf.Address.pp pc) | (_, _, Some loop) -> let current_num = Hashtbl.find_opt pchtbl pc |> Option.value ~default:0 in - if current_num >= loop then endnow "PC 0x%x had been seen more than %s" pc loop_str + if current_num >= loop then endnow "PC %t had been seen more than %s" Pp.(tos Elf.Address.pp pc) loop_str else begin Hashtbl.replace pchtbl pc (current_num + 1); None end | _ -> None - ) - | exp -> endnow "PC %t is symbolic" Pp.(tos State.Exp.pp exp) + ) |> Option.value_fun ~default:(fun () -> endnow "PC %t is symbolic" Pp.(tos State.Exp.pp pc_exp)) diff --git a/src/run/runner.ml b/src/run/runner.ml index a557708e..fda103e4 100644 --- a/src/run/runner.ml +++ b/src/run/runner.ml @@ -191,8 +191,7 @@ let execute_normal ?(prelock = ignore) ~pc runner (instr : Trace.Instr.t) state let skip runner state : State.t list = let pc_exp = State.get_reg_exp state runner.pc in try - let pc = pc_exp |> Ast.expect_bits |> BitVec.to_int in - let pc = Elf.Address.{ section = ".text"; offset = pc } in (* TODO this is wrong, should get symbolic value from pc_exp *) + let pc = State.Exp.expect_sym_address pc_exp in match fetch runner pc with | Normal { traces = _; read = _; written = _; length; opcode = _; segments = _ } |Special length @@ -224,8 +223,7 @@ let skip runner state : State.t list = let run ?prelock runner state : State.t list = let pc_exp = State.get_reg_exp state runner.pc in try - let pc = pc_exp |> Ast.expect_bits |> BitVec.to_int in - let pc = Elf.Address.{ section = ".text"; offset = pc } in (* TODO this is wrong, should get symbolic value from pc_exp *) + let pc = State.Exp.expect_sym_address pc_exp in match fetch runner pc with | Normal instr -> execute_normal ?prelock ~pc runner instr state | Special _ -> diff --git a/src/state/base.ml b/src/state/base.ml index 5b054155..489bc5fc 100644 --- a/src/state/base.ml +++ b/src/state/base.ml @@ -172,6 +172,15 @@ module Exp = struct include Exp.Make (Var) let of_reg id reg = Var.of_reg id reg |> of_var + + let expect_sym_address exp = + let sym, conc = Exp.Sums.split_concrete exp in + let section = match sym with + | Some(Ast.Var (Var.Section s, _)) -> s + | _ -> Raise.fail "Expected symbolic Section base" + in + let offset = BitVec.to_int conc in + Elf.Address.{ section; offset } end type exp = Exp.t @@ -507,16 +516,19 @@ let set_pc ~(pc : Reg.t) (s : t) (pcval : int) = (* TODO *) let set_pc_sym ~(pc : Reg.t) (s : t) (pcval : Elf.Address.t) = - set_pc ~pc s pcval.offset + (* set_pc ~pc s pcval.offset *) + let exp = Typed.(var ~typ:(Ty_BitVec 64) (Var.Section pcval.section) + bits_int ~size:64 pcval.offset) in + let ctyp = Ctype.of_frag (Ctype.Global ".text") ~offset:pcval.offset ~constexpr:true in + set_reg s pc @@ Tval.make ~ctyp exp let bump_pc ~(pc : Reg.t) (s : t) (bump : int) = let pc_exp = get_reg_exp s pc in - assert (ConcreteEval.is_concrete pc_exp); - let old_pc = ConcreteEval.eval pc_exp |> Value.expect_bv |> BitVec.to_int in - let new_pc = old_pc + bump in - set_pc ~pc s new_pc + let old_pc = Exp.expect_sym_address pc_exp in + let new_pc = Elf.Address.(old_pc + bump) in + set_pc_sym ~pc s new_pc +(* TODO section + offset *) let concretize_pc ~(pc : Reg.t) (s : t) = let pc_exp = get_reg_exp s pc in try ConcreteEval.eval pc_exp |> Value.expect_bv |> BitVec.to_int |> set_pc ~pc s diff --git a/src/state/base.mli b/src/state/base.mli index b65853c3..bcba6c5f 100644 --- a/src/state/base.mli +++ b/src/state/base.mli @@ -158,6 +158,8 @@ module Exp : sig (** Create an expression from an register and a state id *) val of_reg : id -> Reg.t -> t + + val expect_sym_address : t -> Elf.Address.t end type exp = Exp.t From dde2e988439d0f7e2c5e4eca56c2a4619d67ebf0 Mon Sep 17 00:00:00 2001 From: Matej Urban Date: Wed, 15 Jan 2025 16:47:09 +0100 Subject: [PATCH 025/116] WIP --- src/exp/sums.ml | 20 ++++++++++++++++++++ src/state/base.ml | 5 +++++ src/trace/context.ml | 3 ++- src/utils/list.ml | 14 ++++++++++++++ 4 files changed, 41 insertions(+), 1 deletion(-) diff --git a/src/exp/sums.ml b/src/exp/sums.ml index 4d945032..6d843f5d 100644 --- a/src/exp/sums.ml +++ b/src/exp/sums.ml @@ -42,6 +42,10 @@ (* *) (*==================================================================================*) +open Logs.Logger (struct + let str = __MODULE__ +end) + (* The documentation is in the mli file *) let rec split = @@ -59,6 +63,20 @@ let rec split = let l' = split e' in let rl' = List.rev_map Typed.neg l' in List.rev_append rl' l + | Manyop (Concat, l, _) -> + let all_splits = List.map split l in + let defaults = List.map (fun e -> + let size = e |> Typed.get_type |> Typed.expect_bv in + Typed.bits_int ~size 0 + ) l in + let terms = List.transpose ~defaults all_splits in + List.map Typed.concat terms + | Unop (ZeroExtend m, e, _) -> + let l = split e in + List.map (Typed.unop (ZeroExtend m)) l + | Unop (SignExtend s, e, _) -> + let l = split e in + List.map (Typed.unop (SignExtend s)) l | e -> [e] let merge ~size l = if l = [] then Typed.zero ~size else Typed.sum l @@ -88,6 +106,8 @@ let smart_substract ~equal ~term exp = let split_concrete exp = let size = Typed.expect_bv (Typed.get_type exp) in let terms = split exp in + debug "Split:"; + List.iter (fun t -> debug "\t%t" Pp.(top (PpExp.pp_exp (fun _ -> !^"var")) t)) terms; let (symterms, concvals) = List.partition_map ConcreteEval.eval_if_concrete terms in let concbvs = List.map Value.expect_bv concvals in let concbv = List.fold_left BitVec.( + ) (BitVec.zero ~size) concbvs in diff --git a/src/state/base.ml b/src/state/base.ml index 489bc5fc..55606fc8 100644 --- a/src/state/base.ml +++ b/src/state/base.ml @@ -163,6 +163,8 @@ end type var = Var.t +module Z3St = Z3.Make (Var) + module Sums = Exp.Sums module Typed = Exp.Typed module ConcreteEval = Exp.ConcreteEval @@ -474,6 +476,9 @@ let read ~provenance ?ctyp (s : t) ~(addr : Exp.t) ~(size : Mem.Size.t) : Exp.t Option.value exp ~default:(Exp.of_var var) let read_noprov ?ctyp (s : t) ~(addr : Exp.t) ~(size : Mem.Size.t) : Exp.t = + (* let addr = Z3St.simplify_full addr in *) + let sym, conc = Sums.split_concrete addr in + debug "Address: %t + %t" Pp.(top (optional Exp.pp) sym) Pp.(top BitVec.pp_smt conc); if ConcreteEval.is_concrete addr || Vec.length s.mem.frags = 0 then read ~provenance:Ctype.Main ?ctyp s ~addr ~size else Raise.fail "Trying to access %t in state %d: No provenance info" Pp.(tos Exp.pp addr) s.id diff --git a/src/trace/context.ml b/src/trace/context.ml index d3cbd143..3ed8317d 100644 --- a/src/trace/context.ml +++ b/src/trace/context.ml @@ -82,7 +82,8 @@ let expand_var ~(ctxt : t) (v : Base.Var.t) (a : Ast.no Ast.ty) : State.exp = match v with | Register reg -> State.get_reg_exp ctxt.state reg | NonDet (i, _) | Read (i, _) -> (HashVector.get ctxt.mem_reads i).exp (* TODO is the NonDet case correct *) - | Segment (name, _) -> SMap.find name ctxt.segments (* TODO put the actual value there *) + | Segment (name, _) -> SMap.find name ctxt.segments (*TODO put the actual value there*) + (* | Segment (name, sz) -> Exp.Typed.extract ~first:0 ~last:(sz-1) (State.Exp.of_var (State.Var.Section name)) TODO put the actual value there *) (** Tell if typing should enabled with this context *) let typing_enabled ~(ctxt : t) = ctxt.dwarf <> None diff --git a/src/utils/list.ml b/src/utils/list.ml index 5eae6c30..6eb978ca 100644 --- a/src/utils/list.ml +++ b/src/utils/list.ml @@ -298,3 +298,17 @@ let prod l1 l2 = (** Monadic merge. [let* x = xl and* y = yl in ... = let* x= xl in let* y = yl in ...] *) let ( and* ) = prod + +let hd_opt = function +| [] -> None +| h :: _ -> Some h + +let rec transpose ~defaults l = + let first = map hd_opt l in + let rest = map (drop 1) l in + if for_all Stdlib.Option.is_none first then + [] + else + let t = transpose ~defaults rest in + let h = combine first defaults |> map (fun (value, default) -> Stdlib.Option.value ~default value) in + h :: t From 516f4f1d4a0e95fb7e5e92f0a0322e0585b277a8 Mon Sep 17 00:00:00 2001 From: Matej Urban Date: Wed, 15 Jan 2025 22:27:36 +0100 Subject: [PATCH 026/116] wip --- src/elf/relocations.ml | 34 +++++++++++++++------- src/isla/cache.ml | 25 ++++++++++++++-- src/isla/isla.ml | 1 + src/isla/relocation.ml | 44 ++++++++++++++++++++++++++++ src/isla/server.ml | 65 ++---------------------------------------- src/run/runner.ml | 8 +++--- src/trace/context.ml | 36 +++++++++++++++++++---- src/trace/instr.ml | 21 +++----------- src/trace/run.ml | 12 ++++---- 9 files changed, 138 insertions(+), 108 deletions(-) create mode 100644 src/isla/relocation.ml diff --git a/src/elf/relocations.ml b/src/elf/relocations.ml index 8bf736ce..e9295717 100644 --- a/src/elf/relocations.ml +++ b/src/elf/relocations.ml @@ -13,29 +13,41 @@ type exp = | BinOp of (exp * binary_operation * exp) | UnOp of (unary_operation * exp) (* | AssertRange of (exp * int * int) *) -| Mask of (exp * int * int) +(* | Mask of (exp * int * int) *) type rel = { target : target; value : exp; + range: (int64 * int64) option; + mask : int * int; } type t = rel IMap.t type linksem_t = LinksemRelocatable.rels -let rec exp_of_linksem = function -| Elf_symbolic.Section s -> Section s -| Elf_symbolic.Const x -> Const (Z.to_int x) -| Elf_symbolic.BinOp (x, op, y) -> BinOp (exp_of_linksem x, op, exp_of_linksem y) -| Elf_symbolic.UnOp (op, x) -> UnOp (op, exp_of_linksem x) -| Elf_symbolic.AssertRange (x, _, _) -> exp_of_linksem x (* TODO *) -| Elf_symbolic.Mask (x, a, b) -> Mask (exp_of_linksem x, Z.to_int a, Z.to_int b) +let exp_of_linksem = + let rec value_of_linksem = function + | Elf_symbolic.Section s -> Section s + | Elf_symbolic.Const x -> Const (Z.to_int x) + | Elf_symbolic.BinOp (x, op, y) -> BinOp (value_of_linksem x, op, value_of_linksem y) + | Elf_symbolic.UnOp (op, x) -> UnOp (op, value_of_linksem x) + | Elf_symbolic.AssertRange (_, _, _) -> Raise.fail "AssertRange should not occur in value expression" + | Elf_symbolic.Mask (_, _, _) -> Raise.fail "AssertRange should not occur in value expression" + in function + | Elf_symbolic.Mask (e, hi, lo) -> + let e, range = match e with + | Elf_symbolic.AssertRange (e, min, max) -> e, Some (Z.to_int64 min, Z.to_int64 max) + | e -> e, None + in + fun target -> {target; range; mask = (Z.to_int hi, Z.to_int lo); value = value_of_linksem e} + | _ -> Raise.fail "Expression does not have Mask in top level" + let of_linksem: linksem_t -> t = function | LinksemRelocatable.AArch64 relocs -> let add k Elf_symbolic.{ arel_value; arel_target } m = - IMap.add (Z.to_int k) { value = exp_of_linksem arel_value; target = AArch64 arel_target } m + IMap.add (Z.to_int k) (exp_of_linksem arel_value (AArch64 arel_target)) m in Pmap.fold add relocs IMap.empty @@ -53,7 +65,6 @@ let rec pp_exp = Pp.( | BinOp (a, Sub, b) -> !^"(" ^^ pp_exp a ^^ !^"-" ^^ pp_exp b ^^ !^")" | BinOp (a, And, b) -> !^"(" ^^ pp_exp a ^^ !^"&" ^^ pp_exp b ^^ !^")" | UnOp (Not, b) -> !^"(" ^^ !^"~" ^^ pp_exp b ^^ !^")" - | Mask (x, a, b) -> pp_exp x ^^ !^"[" ^^ int a ^^ !^":" ^^ int b ^^ !^"]" ) let pp_target = Pp.(function @@ -65,7 +76,8 @@ let pp_target = Pp.(function | AArch64 Abi_aarch64_symbolic_relocation.LDST -> !^"LDST") let pp_rel rel = - Pp.(pp_target rel.target ^^ !^": " ^^ pp_exp rel.value) + let hi, lo = rel.mask in + Pp.(pp_target rel.target ^^ !^": " ^^ pp_exp rel.value ^^ !^"[" ^^ int hi ^^ !^":" ^^ int lo ^^ !^"]") let pp rels = if IMap.is_empty rels then diff --git a/src/isla/cache.ml b/src/isla/cache.ml index 98d7dfa2..e5c95d72 100644 --- a/src/isla/cache.ml +++ b/src/isla/cache.ml @@ -79,6 +79,25 @@ type config = Server.config module Opcode (*: Cache.Key *) = struct type t = Server.opcode option + let reloc_id: Relocation.t option -> int = function + | None -> 0 + | Some (Elf.Relocations.AArch64 Abi_aarch64_symbolic_relocation.Data640) -> 1 + | Some (Elf.Relocations.AArch64 Abi_aarch64_symbolic_relocation.Data320) -> 2 + | Some (Elf.Relocations.AArch64 Abi_aarch64_symbolic_relocation.ADRP) -> 3 + | Some (Elf.Relocations.AArch64 Abi_aarch64_symbolic_relocation.ADD) -> 4 + | Some (Elf.Relocations.AArch64 Abi_aarch64_symbolic_relocation.LDST) -> 5 + | Some (Elf.Relocations.AArch64 Abi_aarch64_symbolic_relocation.CALL) -> 6 + + let reloc_of_id: int -> Relocation.t option = function + | 0 -> None + | 1 -> Some (Elf.Relocations.AArch64 Abi_aarch64_symbolic_relocation.Data640) + | 2 -> Some (Elf.Relocations.AArch64 Abi_aarch64_symbolic_relocation.Data320) + | 3 -> Some (Elf.Relocations.AArch64 Abi_aarch64_symbolic_relocation.ADRP) + | 4 -> Some (Elf.Relocations.AArch64 Abi_aarch64_symbolic_relocation.ADD) + | 5 -> Some (Elf.Relocations.AArch64 Abi_aarch64_symbolic_relocation.LDST) + | 6 -> Some (Elf.Relocations.AArch64 Abi_aarch64_symbolic_relocation.CALL) + | _ -> Raise.fail "invalid reloc id" + let equal a b = match (a, b) with | (None, None) -> true @@ -93,7 +112,7 @@ module Opcode (*: Cache.Key *) = struct | Some (bs, rel) -> let i = BytesSeq.getintle_ze bs 0 in let l = BytesSeq.length bs in - let rel_id = Server.reloc_id rel in + let rel_id = reloc_id rel in if small_enough bs rel_id then begin assert (not @@ IntBits.get i IntBits.back); let res = IntBits.blit l 0 i (IntBits.back - 3) 3 in @@ -105,7 +124,7 @@ module Opcode (*: Cache.Key *) = struct let to_file _file = function | None -> () | Some (bs, rel) -> - let rel_id = Server.reloc_id rel in + let rel_id = reloc_id rel in if small_enough bs rel_id then () else Raise.todo() @@ -120,7 +139,7 @@ module Opcode (*: Cache.Key *) = struct let size = IntBits.sub hash (IntBits.back - 3) 3 in let b = Bytes.create size in Bits.unsafe_blit_of_int data 0 b 0 (size * 8); - Some (BytesSeq.of_bytes b, Server.reloc_of_id reloc_id) + Some (BytesSeq.of_bytes b, reloc_of_id reloc_id) end (** Representation of trace lists on disk. diff --git a/src/isla/isla.ml b/src/isla/isla.ml index 5ed4bdf5..e61981e1 100644 --- a/src/isla/isla.ml +++ b/src/isla/isla.ml @@ -50,6 +50,7 @@ module Cache = Cache module Conv = Conv module Manip = Manip module Preprocess = Preprocess +module Relocation = Relocation module Run = Run module Server = Server module Test = Test diff --git a/src/isla/relocation.ml b/src/isla/relocation.ml new file mode 100644 index 00000000..21f7c268 --- /dev/null +++ b/src/isla/relocation.ml @@ -0,0 +1,44 @@ +open Logs.Logger (struct + let str = __MODULE__ +end) + +type t = Elf.Relocations.target + +type segment = string * (int * int) (* mapping the name of a segment to the range of the relocation value *) + +let pp_opcode_with_segments (b, r) = + match r with + | None -> Pp.(!^"#x" ^^ BytesSeq.ppint b) + | Some (Elf.Relocations.AArch64 rtype) -> + let bits = BytesSeq.getbvle ~size:32 b 0 in + Pp.( + match rtype with + | Abi_aarch64_symbolic_relocation.Data640 -> fatal "Data64 relocation not allowed for instruction" + | Abi_aarch64_symbolic_relocation.Data320 -> fatal "Data32 relocation not allowed for instruction" + | Abi_aarch64_symbolic_relocation.ADRP -> + BitVec.pp_smt (BitVec.extract 31 31 bits) + ^^ !^" x0:2 " ^^ + BitVec.pp_smt (BitVec.extract 24 28 bits) + ^^ !^" x1:19 " ^^ + BitVec.pp_smt (BitVec.extract 0 4 bits) + | Abi_aarch64_symbolic_relocation.ADD -> + BitVec.pp_smt (BitVec.extract 22 31 bits) + ^^ !^" x0:12 " ^^ + BitVec.pp_smt (BitVec.extract 0 9 bits) + | Abi_aarch64_symbolic_relocation.LDST -> (* TODO different width loads, alignment *) + BitVec.pp_smt (BitVec.extract 22 31 bits) + ^^ !^" x0:10 " ^^ + BitVec.pp_smt (BitVec.extract 0 11 bits) + | Abi_aarch64_symbolic_relocation.CALL -> + BitVec.pp_smt (BitVec.extract 26 31 bits) + ^^ !^" x0:26 " + ) + +(* for interpreting the segments *) +let segments_of_reloc: t -> segment list = function +| Elf.Relocations.AArch64 Abi_aarch64_symbolic_relocation.Data640 -> fatal "invalid relocation for instructions (Data64)" +| Elf.Relocations.AArch64 Abi_aarch64_symbolic_relocation.Data320 -> fatal "invalid relocation for instructions (Data32)" +| Elf.Relocations.AArch64 Abi_aarch64_symbolic_relocation.ADRP -> ["x0", (0, 1); "x1", (2, 20)] (* or absolute? ["x0", (12, 13); "x1", (14, 32)] *) +| Elf.Relocations.AArch64 Abi_aarch64_symbolic_relocation.ADD -> ["x0", (0, 11)] +| Elf.Relocations.AArch64 Abi_aarch64_symbolic_relocation.LDST -> ["x0", (0, 9)] (* TODO depends on load size *) (* or absolute? ["x0", (2, 11)] *) +| Elf.Relocations.AArch64 Abi_aarch64_symbolic_relocation.CALL -> ["x0", (0, 25)] (* or absolute? ["x0", (2, 27)] *) \ No newline at end of file diff --git a/src/isla/server.ml b/src/isla/server.ml index b2ba008b..e6f247ff 100644 --- a/src/isla/server.ml +++ b/src/isla/server.ml @@ -68,30 +68,8 @@ type config = Config.t processor exception/fault) or not *) type trcs = Base.instruction_segments option * (bool * Base.rtrc) list -type reloc = Elf.Relocations.target - -let reloc_id: reloc option -> int = function -| None -> 0 -| Some (Elf.Relocations.AArch64 Abi_aarch64_symbolic_relocation.Data640) -> 1 -| Some (Elf.Relocations.AArch64 Abi_aarch64_symbolic_relocation.Data320) -> 2 -| Some (Elf.Relocations.AArch64 Abi_aarch64_symbolic_relocation.ADRP) -> 3 -| Some (Elf.Relocations.AArch64 Abi_aarch64_symbolic_relocation.ADD) -> 4 -| Some (Elf.Relocations.AArch64 Abi_aarch64_symbolic_relocation.LDST) -> 5 -| Some (Elf.Relocations.AArch64 Abi_aarch64_symbolic_relocation.CALL) -> 6 - -let reloc_of_id: int -> reloc option = function -| 0 -> None -| 1 -> Some (Elf.Relocations.AArch64 Abi_aarch64_symbolic_relocation.Data640) -| 2 -> Some (Elf.Relocations.AArch64 Abi_aarch64_symbolic_relocation.Data320) -| 3 -> Some (Elf.Relocations.AArch64 Abi_aarch64_symbolic_relocation.ADRP) -| 4 -> Some (Elf.Relocations.AArch64 Abi_aarch64_symbolic_relocation.ADD) -| 5 -> Some (Elf.Relocations.AArch64 Abi_aarch64_symbolic_relocation.LDST) -| 6 -> Some (Elf.Relocations.AArch64 Abi_aarch64_symbolic_relocation.CALL) -| _ -> Raise.fail "invalid reloc id" - -type opcode = BytesSeq.t * reloc option - -type reloc_segment = string * (int * int) (* mapping the name of a segment to the range of the relocation value *) +type opcode = BytesSeq.t * Relocation.t option + (** Bump when updating isla. TODO: move the version checking to allow a range of version. @@ -228,48 +206,11 @@ let pp_answer = function (** The type of a request to isla *) type request = TEXT_ASM of string | ASM of opcode | VERSION | STOP -let pp_interpreted_opcode (b, r) = - match r with - | None -> Pp.(!^"#x" ^^ BytesSeq.ppint b) - | Some (Elf.Relocations.AArch64 rtype) -> - let bits = BytesSeq.getbvle ~size:32 b 0 in - Pp.( - match rtype with - | Abi_aarch64_symbolic_relocation.Data640 -> fatal "Data64 relocation not allowed for instruction" - | Abi_aarch64_symbolic_relocation.Data320 -> fatal "Data32 relocation not allowed for instruction" - | Abi_aarch64_symbolic_relocation.ADRP -> - BitVec.pp_smt (BitVec.extract 31 31 bits) - ^^ !^" x0:2 " ^^ - BitVec.pp_smt (BitVec.extract 24 28 bits) - ^^ !^" x1:19 " ^^ - BitVec.pp_smt (BitVec.extract 0 4 bits) - | Abi_aarch64_symbolic_relocation.ADD -> - BitVec.pp_smt (BitVec.extract 22 31 bits) - ^^ !^" x0:12 " ^^ - BitVec.pp_smt (BitVec.extract 0 9 bits) - | Abi_aarch64_symbolic_relocation.LDST -> (* TODO different width loads, alignment *) - BitVec.pp_smt (BitVec.extract 22 31 bits) - ^^ !^" x0:10 " ^^ - BitVec.pp_smt (BitVec.extract 0 11 bits) - | Abi_aarch64_symbolic_relocation.CALL -> - BitVec.pp_smt (BitVec.extract 26 31 bits) - ^^ !^" x0:26 " - ) - -(* for interpreting the segments *) -let segments_of_reloc: reloc -> reloc_segment list = function -| Elf.Relocations.AArch64 Abi_aarch64_symbolic_relocation.Data640 -> fatal "invalid relocation for instructions (Data64)" -| Elf.Relocations.AArch64 Abi_aarch64_symbolic_relocation.Data320 -> fatal "invalid relocation for instructions (Data32)" -| Elf.Relocations.AArch64 Abi_aarch64_symbolic_relocation.ADRP -> ["x0", (0, 1); "x1", (2, 20)] (* or absolute? ["x0", (12, 13); "x1", (14, 32)] *) -| Elf.Relocations.AArch64 Abi_aarch64_symbolic_relocation.ADD -> ["x0", (0, 11)] -| Elf.Relocations.AArch64 Abi_aarch64_symbolic_relocation.LDST -> ["x0", (0, 9)] (* TODO depends on load size *) (* or absolute? ["x0", (2, 11)] *) -| Elf.Relocations.AArch64 Abi_aarch64_symbolic_relocation.CALL -> ["x0", (0, 25)] (* or absolute? ["x0", (2, 27)] *) - (** Convert a request into the string message expected by isla-client This should match the protocol *) let string_of_request = function | TEXT_ASM s -> Printf.sprintf "execute_asm %s" s - | ASM b -> Pp.(sprintc @@ !^"execute " ^^ pp_interpreted_opcode b) + | ASM b -> Pp.(sprintc @@ !^"execute " ^^ Relocation.pp_opcode_with_segments b) | VERSION -> "version" | STOP -> "stop" diff --git a/src/run/runner.ml b/src/run/runner.ml index fda103e4..7ae5ff59 100644 --- a/src/run/runner.ml +++ b/src/run/runner.ml @@ -165,13 +165,13 @@ let fetch (runner : t) (pc : Elf.Address.t) : slot = let execute_normal ?(prelock = ignore) ~pc runner (instr : Trace.Instr.t) state = let dwarf = runner.dwarf in let next = instr.length in - let segments_map = instr.segments in + let relocation = instr.relocation in let run_pure () = List.map (fun (trc : Trace.Instr.trace_meta) -> let nstate = State.copy state in State.set_last_pc nstate pc; - Trace.Run.trace_pc_mut ?dwarf ~segments_map ~next nstate trc.trace; + Trace.Run.trace_pc_mut ?dwarf ?relocation ~next nstate trc.trace; nstate) instr.traces in @@ -179,7 +179,7 @@ let execute_normal ?(prelock = ignore) ~pc runner (instr : Trace.Instr.t) state match instr.traces with | [trc] -> State.set_last_pc state pc; - Trace.Run.trace_pc_mut ?dwarf ~segments_map ~next state trc.trace; + Trace.Run.trace_pc_mut ?dwarf ?relocation ~next state trc.trace; [state] | _ -> prelock state; @@ -193,7 +193,7 @@ let skip runner state : State.t list = try let pc = State.Exp.expect_sym_address pc_exp in match fetch runner pc with - | Normal { traces = _; read = _; written = _; length; opcode = _; segments = _ } + | Normal { traces = _; read = _; written = _; length; opcode = _; relocation = _ } |Special length |IslaFail length -> let state = State.copy_if_locked state in diff --git a/src/trace/context.ml b/src/trace/context.ml index 3ed8317d..d03b0732 100644 --- a/src/trace/context.ml +++ b/src/trace/context.ml @@ -48,6 +48,10 @@ should be added here *) +open Logs.Logger (struct + let str = __MODULE__ +end) + module SMap = Map.Make (String) (** The context to run a trace *) @@ -59,21 +63,43 @@ type t = { dwarf : Dw.t option; (** Optionally DWARF information. If present, typing is enabled *) } -let rec exp_of_relocation: Elf.Relocations.exp -> State.exp = - let f = exp_of_relocation in function +let rec exp_of_relocation_exp: Elf.Relocations.exp -> State.exp = + let f = exp_of_relocation_exp in function | Section s -> State.Exp.of_var (State.Var.Section s) (* TODO put the actual value there, size? *) | Const x -> Exp.Typed.bits (BitVec.of_int x ~size:64) (* TODO size? *) | BinOp (a, Add, b) -> Exp.Typed.(f a + f b) | BinOp (a, Sub, b) -> Exp.Typed.(f a - f b) | BinOp (a, And, b) -> Exp.Typed.manyop (AstGen.Ott.Bvmanyarith AstGen.Ott.Bvand) [f a; f b] | UnOp (Not, b) -> Exp.Typed.unop AstGen.Ott.Bvnot (f b) - | Mask (x, last, first) -> Exp.Typed.extract ~last ~first (f x) + (* | Mask (x, last, first) -> Exp.Typed.extract ~last ~first (f x) *) (** Build a {!context} from a state *) -let make_context ?dwarf ?segments_map state = +let make_context ?dwarf ?relocation state = let reg_writes = Vec.empty () in let mem_reads = HashVector.empty () in - let segments = segments_map |> Option.value ~default:SMap.empty |> SMap.map exp_of_relocation in + + let segments = relocation + |> Option.map (fun relocation -> + let open Elf.Relocations in + let value = exp_of_relocation_exp relocation.value in + Option.iter (fun (min, max) -> + let min = Exp.Typed.bits @@ BitVec.of_z ~size:64 @@ Z.of_int64 min in + let max = Exp.Typed.bits @@ BitVec.of_z ~size:64 @@ Z.of_int64 max in + let cond1 = Exp.Typed.(binop (Bvcomp Bvsle) min value) in + let cond2 = Exp.Typed.(binop (Bvcomp Bvslt) value max) in + let cond = Exp.Typed.(manyop And [cond1; cond2]) in + State.push_assert state cond; + ) relocation.range; + let (last, first) = relocation.mask in + let masked = Exp.Typed.extract ~first ~last value in + + relocation.target + |> Isla.Relocation.segments_of_reloc + |> SMap.of_list + |> SMap.map (fun (first, last) -> Exp.Typed.extract ~first ~last masked) + ) + |> Option.value ~default:SMap.empty + in { state; reg_writes; mem_reads; dwarf; segments } (** Expand a Trace variable to a State expression, using the context *) diff --git a/src/trace/instr.ml b/src/trace/instr.ml index 012273a5..e53848b1 100644 --- a/src/trace/instr.ml +++ b/src/trace/instr.ml @@ -69,7 +69,7 @@ type t = { read : Reg.t list; written : Reg.t list; opcode : BytesSeq.t; - segments: Elf.Relocations.exp SMap.t + relocation : Elf.Relocations.rel option; } let dedup_regs = List.sort_uniq State.Reg.compare @@ -102,31 +102,18 @@ let trace_meta_of_trace trace = { trace; jump_target = !jump; read = dedup_regs !read; written = dedup_regs !written } (** Generate full instruction data from a list of traces *) -let of_traces (opcode: BytesSeq.t * Elf.Relocations.rel option) traces = +let of_traces ((opcode: BytesSeq.t), (relocation: Elf.Relocations.rel option)) traces = let traces = List.map trace_meta_of_trace traces in - let opcode, reloc = opcode in let length = BytesSeq.length opcode in let read = dedup_regs @@ List.concat_map (fun (tr : trace_meta) -> tr.read) traces in let written = dedup_regs @@ List.concat_map (fun (tr : trace_meta) -> tr.written) traces in - let segments = match reloc with - | None -> SMap.empty - | Some reloc -> - reloc.target - |> Isla.Server.segments_of_reloc - |> List.map (fun (v, (lo, hi)) -> (v, Elf.Relocations.Mask(reloc.value, hi, lo))) - |> SMap.of_list - in - { traces; length; read; written; opcode; segments } + { traces; length; read; written; opcode; relocation } (** Pretty print the representation of an instruction *) let pp instr = let open Pp in - (prefix 4 1 !^"Segments:" @@ - separate_map hardline - (pair string Elf.Relocations.pp_exp) - (SMap.to_list instr.segments)) - ^^ hardline ^^ + !^"Relocation" ^^ optional Elf.Relocations.pp_rel instr.relocation ^^ hardline ^^ separate_mapi hardline (fun i trc -> prefix 4 1 (dprintf "Trace %d:" i) (Base.pp trc.trace)) instr.traces diff --git a/src/trace/run.ml b/src/trace/run.ml index df944ca7..45c9252d 100644 --- a/src/trace/run.ml +++ b/src/trace/run.ml @@ -114,17 +114,17 @@ let event_mut ~(ctxt : ctxt) (event : Base.event) = | Assert exp -> State.push_assert ctxt.state (expand ~ctxt exp) (** Run a trace on the provided state by mutation. Enable typing if [dwarf] is provided *) -let trace_mut ?dwarf ?segments_map (state : State.t) (events : Base.t) : unit = +let trace_mut ?dwarf ?relocation (state : State.t) (events : Base.t) : unit = assert (not @@ State.is_locked state); info "Running trace with typing %s" (if dwarf <> None then "on" else "off"); - let ctxt = Context.make_context ?dwarf ?segments_map state in + let ctxt = Context.make_context ?dwarf ?relocation state in List.iter (event_mut ~ctxt) events; Vec.iter (fun (reg, tval) -> State.Reg.Map.set state.regs reg tval) ctxt.reg_writes (** Run a trace on the provided state by returning an updated copy.*) -let trace ?dwarf ?segments_map (start : State.t) (events : Base.t) : State.t = +let trace ?dwarf ?relocation (start : State.t) (events : Base.t) : State.t = let state = State.copy start in - trace_mut ?dwarf ?segments_map state events; + trace_mut ?dwarf ?relocation state events; State.lock state; state @@ -133,12 +133,12 @@ let trace ?dwarf ?segments_map (start : State.t) (events : Base.t) : State.t = Thus this function automatically handle moving the PC for fall-through instruction *) -let trace_pc_mut ?dwarf ?segments_map ~(next : int) (state : State.t) (events : Base.t) : unit = +let trace_pc_mut ?dwarf ?relocation ~(next : int) (state : State.t) (events : Base.t) : unit = let pc = Arch.pc () in let rec is_touching_pc : Base.t -> bool = function | [] -> false | WriteReg { reg; _ } :: _ when reg = pc -> true | _ :: l -> is_touching_pc l in - trace_mut ?dwarf ?segments_map state events; + trace_mut ?dwarf ?relocation state events; if is_touching_pc events then State.concretize_pc ~pc state else State.bump_pc ~pc state next From 3832eb4c8ff55a6e251c7a29a6239ceeb10328ef Mon Sep 17 00:00:00 2001 From: Matej Urban Date: Wed, 15 Jan 2025 22:28:17 +0100 Subject: [PATCH 027/116] todo --- notes-TODO | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/notes-TODO b/notes-TODO index 66ca908d..b1a58b67 100644 --- a/notes-TODO +++ b/notes-TODO @@ -2,4 +2,8 @@ Symbolic symbol table - value of symbol?? (we don't have segments in relocatable files) - can probably keep the same api, but addresses are symbolic -Instruction fetch: is it sound? (rewriting .text) \ No newline at end of file +Instruction fetch: is it sound? (rewriting .text) + +Z3 finding unique solution +- Get model -> assert not model -> check now it is unsat +- Need to extend the protocol probably \ No newline at end of file From 83414d1a9df9aacb3579cc3e0675ab1fba5326eb Mon Sep 17 00:00:00 2001 From: Matej Urban Date: Thu, 16 Jan 2025 16:55:27 +0100 Subject: [PATCH 028/116] Smarter context full simplifier --- src/ast/manip.ml | 7 ++++ src/bin/main.ml | 1 + src/bin/readDwarf.ml | 1 + src/z3/z3.ml | 76 ++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 85 insertions(+) diff --git a/src/ast/manip.ml b/src/ast/manip.ml index 46a7ce9c..b21dffe7 100644 --- a/src/ast/manip.ml +++ b/src/ast/manip.ml @@ -350,3 +350,10 @@ let check_no_mem (e : ('a, 'v, 'b, 'm) exp) : bool = let expect_no_mem ?(handler = fun () -> failwith "Expected no mem") : ('a, 'v, 'b, 'm1) exp -> ('a, 'v, 'b, 'm2) exp = fun exp -> if check_no_mem exp then Obj.magic exp else handler () + + +let all_subterms e = + let rec recurse acc e = + e :: direct_exp_fold_left_exp recurse acc e + in + direct_exp_fold_left_exp recurse [] e \ No newline at end of file diff --git a/src/bin/main.ml b/src/bin/main.ml index 714c0fc2..132b8f4a 100644 --- a/src/bin/main.ml +++ b/src/bin/main.ml @@ -74,6 +74,7 @@ let commands = (* Run.FuncRD.command; *) Other_cmds.CopySourcesCmd.command; (* BranchTable.command; *) + Z3.Test.command; ] let _ = Printexc.record_backtrace Config.enable_backtrace diff --git a/src/bin/readDwarf.ml b/src/bin/readDwarf.ml index 957552f8..6be5927f 100644 --- a/src/bin/readDwarf.ml +++ b/src/bin/readDwarf.ml @@ -73,6 +73,7 @@ let commands = Run.Block.command; (* Run.FuncRD.command; *) CopySourcesCmd.command; + Z3.Test.command; ] let _ = Printexc.record_backtrace Config.enable_backtrace diff --git a/src/z3/z3.ml b/src/z3/z3.ml index 45321719..73733720 100644 --- a/src/z3/z3.ml +++ b/src/z3/z3.ml @@ -395,6 +395,10 @@ module type S = sig This results in two calls to the SMT solver. one with {!check} and one with {!check_sat} *) val check_both : server -> exp -> bool option + val simplify_subterms : server -> exp -> exp + + val simplify_subterms_decl : server -> declared:unit Htbl.t -> exp -> exp + (*****************************************************************************) (*****************************************************************************) (*****************************************************************************) @@ -415,6 +419,8 @@ module type S = sig (** Do a standalone check of whether the set of assertion is sat *) val check_sat_full : exp list -> bool option + + val simplify_subterms_full : ?hyps:exp list -> exp -> exp end module SimpContext = ContextCounter (struct @@ -516,4 +522,74 @@ module Make (Var : Var) : S with type var = Var.t = struct | _ -> ( match check_sat serv e with Some false as f -> f | _ -> None ) + + + let rec simplify_subterms serv (e : Exp.t) : Exp.t = + e |> Ast.Manip.all_subterms + |> List.find_opt (fun t -> + let et = Typed.get_type e in + let tt = Typed.get_type t in + Printf.printf "Types: %t, %t\n" Pp.(top Ast.pp_ty (Ast.Manip.ty_allow_mem et)) Pp.(top Ast.pp_ty (Ast.Manip.ty_allow_mem tt)); + Typed.get_type e = Typed.get_type t && + let result = check serv Typed.(e = t) in + Printf.printf "%t\n" Pp.(top (optional bool) result); + result = Some true + ) + |> Option.map (simplify_subterms serv) + |> Option.value_fun ~default:(fun () -> + Ast.Manip.direct_exp_map_exp (simplify_subterms serv) e + ) + + let simplify_subterms_decl serv ~declared (e : Exp.t) : Exp.t = + declare_vars serv ~declared e; + simplify_subterms serv e + + let simplify_subterms_full ?(hyps = []) e = + let serv = ensure_started_get () in + SimpContext.openc (); + let declared = Htbl.create 10 in + List.iter (send_assert_decl ~declared serv) hyps; + let res = simplify_subterms_decl ~declared serv e in + SimpContext.closec (); + res +end + +module Test = struct + module Var = struct + include String + + let pp = Pp.string + + let ty _ = Ast.Ty_BitVec 64 + + let of_string = Fun.id + end + + module Typed = Exp.Typed + module Exp = Exp.Make (Var) + + module Z3Test = Make (Var) + + let test () = + let x = Typed.var ~typ:(Ast.Ty_BitVec 64) "x" in + let bvint64 = Typed.bits_int ~size:64 in + let constr = Typed.(binop (Ast.Bvcomp Ast.Bvult) x (bvint64 16)) in + let exp = Typed.(concat [bits_int ~size:60 0; extract x ~first:0 ~last:3]) in + let simplified = Z3Test.simplify_subterms_full ~hyps:[constr] exp in + Printf.printf "original: %t\n" (Pp.top Exp.pp exp); + Printf.printf "simplified: %t\n" (Pp.top Exp.pp simplified); + + + open Cmdliner + open Config.CommonOpt + + + let term = Term.(CmdlinerHelper.func_options (config :: z3 :: comopts) test $ const ()) + + let info = + let doc = "" + in + Cmd.(info "z3-test" ~doc ~exits) + + let command = (term, info) end From 75185eed6e40f7921ed06f66425da84d6d0b05e3 Mon Sep 17 00:00:00 2001 From: Matej Urban Date: Thu, 16 Jan 2025 18:53:56 +0100 Subject: [PATCH 029/116] Simplify relocated addresses --- src/elf/relocations.ml | 20 ++++++++++++++------ src/state/base.ml | 13 ++++++++++++- src/state/base.mli | 4 ++++ src/trace/context.ml | 15 +++++++++------ 4 files changed, 39 insertions(+), 13 deletions(-) diff --git a/src/elf/relocations.ml b/src/elf/relocations.ml index e9295717..1a60a8bd 100644 --- a/src/elf/relocations.ml +++ b/src/elf/relocations.ml @@ -15,10 +15,14 @@ type exp = (* | AssertRange of (exp * int * int) *) (* | Mask of (exp * int * int) *) +type assertion = +| Range of int64 * int64 +| Alignment of int + type rel = { target : target; value : exp; - range: (int64 * int64) option; + assertions: assertion list; mask : int * int; } @@ -33,14 +37,18 @@ let exp_of_linksem = | Elf_symbolic.BinOp (x, op, y) -> BinOp (value_of_linksem x, op, value_of_linksem y) | Elf_symbolic.UnOp (op, x) -> UnOp (op, value_of_linksem x) | Elf_symbolic.AssertRange (_, _, _) -> Raise.fail "AssertRange should not occur in value expression" + | Elf_symbolic.AssertAlignment (_, _) -> Raise.fail "AssertAlignment should not occur in value expression" | Elf_symbolic.Mask (_, _, _) -> Raise.fail "AssertRange should not occur in value expression" in function | Elf_symbolic.Mask (e, hi, lo) -> - let e, range = match e with - | Elf_symbolic.AssertRange (e, min, max) -> e, Some (Z.to_int64 min, Z.to_int64 max) - | e -> e, None - in - fun target -> {target; range; mask = (Z.to_int hi, Z.to_int lo); value = value_of_linksem e} + let rec extract_asserts e = + match e with + | Elf_symbolic.AssertRange (e, min, max) -> let (e, a) = extract_asserts e in e, Range (Z.to_int64 min, Z.to_int64 max) :: a + | Elf_symbolic.AssertAlignment (e, bits) -> let (e, a) = extract_asserts e in e, Alignment (Z.to_int bits) :: a + | e -> e, [] + in + let e, assertions = extract_asserts e in + fun target -> {target; assertions; mask = (Z.to_int hi, Z.to_int lo); value = value_of_linksem e} | _ -> Raise.fail "Expression does not have Mask in top level" diff --git a/src/state/base.ml b/src/state/base.ml index 55606fc8..29f6ab80 100644 --- a/src/state/base.ml +++ b/src/state/base.ml @@ -326,6 +326,7 @@ type t = { mutable regs : Tval.t Reg.Map.t; (** The values and types of registers *) read_vars : Tval.t 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 @@ -368,6 +369,7 @@ let make ?elf () = regs = Reg.Map.init @@ Tval.of_reg id; read_vars = Vec.empty (); asserts = []; + relocation_asserts = []; mem = Mem.empty (); elf; fenv = Fragment.Env.make (); @@ -389,6 +391,7 @@ let copy ?elf state = regs = Reg.Map.copy state.regs; read_vars = Vec.empty (); asserts = (if locked then [] else state.asserts); + relocation_asserts = (if locked then [] else state.relocation_asserts); mem = (if locked then Mem.from state.mem else Mem.copy state.mem); elf = Option.(elf ||| state.elf); fenv = Fragment.Env.copy state.fenv; @@ -405,6 +408,13 @@ let push_assert (s : t) (e : exp) = assert (not @@ is_locked s); s.asserts <- e :: s.asserts +let push_relocation_assert (s : t) (e : exp) = + assert (not @@ is_locked s); + s.relocation_asserts <- e :: s.relocation_asserts + +let rec load_relocation_asserts (s : t) = + s.relocation_asserts @ (s.base_state |> Option.map load_relocation_asserts |> Option.value ~default:[]) + let set_asserts state asserts = assert (not @@ is_locked state); state.asserts <- asserts @@ -476,7 +486,8 @@ let read ~provenance ?ctyp (s : t) ~(addr : Exp.t) ~(size : Mem.Size.t) : Exp.t Option.value exp ~default:(Exp.of_var var) let read_noprov ?ctyp (s : t) ~(addr : Exp.t) ~(size : Mem.Size.t) : Exp.t = - (* let addr = Z3St.simplify_full addr in *) + let hyps = load_relocation_asserts s in + let addr = Z3St.simplify_subterms_full ~hyps addr in let sym, conc = Sums.split_concrete addr in debug "Address: %t + %t" Pp.(top (optional Exp.pp) sym) Pp.(top BitVec.pp_smt conc); if ConcreteEval.is_concrete addr || Vec.length s.mem.frags = 0 then diff --git a/src/state/base.mli b/src/state/base.mli index bcba6c5f..f4945039 100644 --- a/src/state/base.mli +++ b/src/state/base.mli @@ -288,6 +288,7 @@ type t = private { mutable regs : Tval.t Reg.Map.t; (** The values and types of registers *) read_vars : Tval.t 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 @@ -390,6 +391,9 @@ val copy_if_locked : ?elf:Elf.File.t -> t -> t (** Add an assertion to a state *) val push_assert : t -> exp -> unit +(** Add an assertion to a state *) +val push_relocation_assert : t -> exp -> unit + (** Set a state to be impossible (single [false] assert). *) val set_impossible : t -> unit diff --git a/src/trace/context.ml b/src/trace/context.ml index d03b0732..1734f041 100644 --- a/src/trace/context.ml +++ b/src/trace/context.ml @@ -82,22 +82,25 @@ let make_context ?dwarf ?relocation state = |> Option.map (fun relocation -> let open Elf.Relocations in let value = exp_of_relocation_exp relocation.value in - Option.iter (fun (min, max) -> + List.iter (function + | Range (min, max) -> let min = Exp.Typed.bits @@ BitVec.of_z ~size:64 @@ Z.of_int64 min in let max = Exp.Typed.bits @@ BitVec.of_z ~size:64 @@ Z.of_int64 max in let cond1 = Exp.Typed.(binop (Bvcomp Bvsle) min value) in let cond2 = Exp.Typed.(binop (Bvcomp Bvslt) value max) in - let cond = Exp.Typed.(manyop And [cond1; cond2]) in - State.push_assert state cond; - ) relocation.range; + State.push_relocation_assert state Exp.Typed.(manyop And [cond1; cond2]) + | Alignment b -> + let last = b-1 in + State.push_relocation_assert state Exp.Typed.(extract ~first:0 ~last value = bits_int ~size:b 0) + ) relocation.assertions; let (last, first) = relocation.mask in let masked = Exp.Typed.extract ~first ~last value in - + relocation.target |> Isla.Relocation.segments_of_reloc |> SMap.of_list |> SMap.map (fun (first, last) -> Exp.Typed.extract ~first ~last masked) - ) + ) |> Option.value ~default:SMap.empty in { state; reg_writes; mem_reads; dwarf; segments } From ad13b155bf14026cd8d1da92e7b67339833f533e Mon Sep 17 00:00:00 2001 From: Matej Urban Date: Fri, 17 Jan 2025 16:15:45 +0100 Subject: [PATCH 030/116] Fix immediate encoding --- src/isla/relocation.ml | 4 ++-- src/state/base.ml | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/isla/relocation.ml b/src/isla/relocation.ml index 21f7c268..3e3004ad 100644 --- a/src/isla/relocation.ml +++ b/src/isla/relocation.ml @@ -26,9 +26,9 @@ let pp_opcode_with_segments (b, r) = ^^ !^" x0:12 " ^^ BitVec.pp_smt (BitVec.extract 0 9 bits) | Abi_aarch64_symbolic_relocation.LDST -> (* TODO different width loads, alignment *) - BitVec.pp_smt (BitVec.extract 22 31 bits) + BitVec.pp_smt (BitVec.extract 20 31 bits) ^^ !^" x0:10 " ^^ - BitVec.pp_smt (BitVec.extract 0 11 bits) + BitVec.pp_smt (BitVec.extract 0 9 bits) | Abi_aarch64_symbolic_relocation.CALL -> BitVec.pp_smt (BitVec.extract 26 31 bits) ^^ !^" x0:26 " diff --git a/src/state/base.ml b/src/state/base.ml index 29f6ab80..cfcef658 100644 --- a/src/state/base.ml +++ b/src/state/base.ml @@ -488,6 +488,7 @@ let read ~provenance ?ctyp (s : t) ~(addr : Exp.t) ~(size : Mem.Size.t) : Exp.t let read_noprov ?ctyp (s : t) ~(addr : Exp.t) ~(size : Mem.Size.t) : Exp.t = let hyps = load_relocation_asserts s in let addr = Z3St.simplify_subterms_full ~hyps addr in + let addr = Z3St.simplify_full addr in let sym, conc = Sums.split_concrete addr in debug "Address: %t + %t" Pp.(top (optional Exp.pp) sym) Pp.(top BitVec.pp_smt conc); if ConcreteEval.is_concrete addr || Vec.length s.mem.frags = 0 then From 17424037276df13a5aedcc40c23ee9810bc5e1f6 Mon Sep 17 00:00:00 2001 From: Matej Urban Date: Fri, 17 Jan 2025 19:05:06 +0100 Subject: [PATCH 031/116] Write with section offset --- src/state/base.ml | 46 ++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 38 insertions(+), 8 deletions(-) diff --git a/src/state/base.ml b/src/state/base.ml index cfcef658..be7d6474 100644 --- a/src/state/base.ml +++ b/src/state/base.ml @@ -485,15 +485,45 @@ let read ~provenance ?ctyp (s : t) ~(addr : Exp.t) ~(size : Mem.Size.t) : Exp.t Option.iter (set_read s (Var.expect_readvar var)) exp; Option.value exp ~default:(Exp.of_var var) -let read_noprov ?ctyp (s : t) ~(addr : Exp.t) ~(size : Mem.Size.t) : Exp.t = +let address_to_exp ~(size : int) (addr : Elf.Address.t) = + let first = 0 in + let last = size - 1 in + Typed.( + extract ~last ~first + (Exp.of_var @@ Var.Section addr.section) + + + bits_int ~size addr.offset + ) + +let eval_address (s : t) (addr: Exp.t) : Elf.Address.t option = + let ctxt0 = function Var.Section _ -> Value.bv @@ BitVec.of_int ~size:64 0 | _ -> raise ConcreteEval.Symbolic in + let offset = addr |> ConcreteEval.eval ~ctxt:ctxt0 |> Value.expect_bv |> BitVec.to_int in + let sections = Hashtbl.create 10 in + Ast.Manip.exp_iter_var (function Var.Section s -> Hashtbl.add sections s () | _ -> ()) addr; + let hyps = load_relocation_asserts s in - let addr = Z3St.simplify_subterms_full ~hyps addr in - let addr = Z3St.simplify_full addr in - let sym, conc = Sums.split_concrete addr in - debug "Address: %t + %t" Pp.(top (optional Exp.pp) sym) Pp.(top BitVec.pp_smt conc); - if ConcreteEval.is_concrete addr || Vec.length s.mem.frags = 0 then - read ~provenance:Ctype.Main ?ctyp s ~addr ~size - else Raise.fail "Trying to access %t in state %d: No provenance info" Pp.(tos Exp.pp addr) s.id + let size = addr |> Typed.get_type |> Typed.expect_bv in + sections |> Hashtbl.to_seq_keys |> Seq.find_map (fun section -> + let address = Elf.Address.{ section; offset } in + let expression = address_to_exp ~size address in + if Z3St.check_full ~hyps Typed.(expression = addr) = Some true then + Some address + else + None + ) + + +let read_noprov ?ctyp (s : t) ~(addr : Exp.t) ~(size : Mem.Size.t) : Exp.t = + let elf_addr = eval_address s addr in + debug "Address: %t" Pp.(top (optional Elf.Address.pp) elf_addr); + match elf_addr with + | Some elf_addr -> + let addr_size = addr |> Typed.get_type |> Typed.expect_bv in + let addr = address_to_exp ~size:addr_size elf_addr in + read ~provenance:Ctype.Main ?ctyp s ~addr ~size + | None when Vec.length s.mem.frags = 0 -> + read ~provenance:Ctype.Main ?ctyp s ~addr ~size + | None -> Raise.fail "Trying to access %t in state %d: No provenance info" Pp.(tos Exp.pp addr) s.id let write ~provenance (s : t) ~(addr : Exp.t) ~(size : Mem.Size.t) (value : Exp.t) : unit = assert (not @@ is_locked s); From f3468d476bcb286c01421117508073a9ce8974bd Mon Sep 17 00:00:00 2001 From: Matej Urban Date: Fri, 17 Jan 2025 19:19:19 +0100 Subject: [PATCH 032/116] Fix type of pc values in state tree --- src/run/block_lib.ml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/run/block_lib.ml b/src/run/block_lib.ml index 2d548c75..3082c0c1 100644 --- a/src/run/block_lib.ml +++ b/src/run/block_lib.ml @@ -67,14 +67,14 @@ type label = | Start (** Root node of the tree *) | End of string (** Lead node of the tree, the string describe which end condition has be triggered *) - | BranchAt of int (** A Branching node at a given PC *) - | NormalAt of int (** A normal instruction at PC. Exists only if [every_instruction] is true *) + | BranchAt of Elf.Address.t (** A Branching node at a given PC *) + | NormalAt of Elf.Address.t (** A normal instruction at PC. Exists only if [every_instruction] is true *) let label_to_string = function | Start -> "Start" | End s -> Printf.sprintf "End (%s)" s - | BranchAt pc -> Printf.sprintf "Branch at 0x%x" pc - | NormalAt pc -> Printf.sprintf "Normal at 0x%x" pc + | BranchAt pc -> Printf.sprintf "Branch at %t" Pp.(tos Elf.Address.pp pc) + | NormalAt pc -> Printf.sprintf "Normal at %t" Pp.(tos Elf.Address.pp pc) let pp_label label = label |> label_to_string |> Pp.string @@ -123,11 +123,11 @@ let run ?(every_instruction = false) ?relevant (b : t) (start : State.t) : label | [state] when not every_instruction -> run_from state | [nstate] when every_instruction -> let rest = [run_from nstate] in - { state; data = NormalAt (pc_exp |> Ast.expect_bits |> BitVec.to_int); rest } + { state; data = NormalAt (State.Exp.expect_sym_address pc_exp); rest } | states -> let rest = List.map run_from states in State.Tree. - { state; data = BranchAt (pc_exp |> Ast.expect_bits |> BitVec.to_int); rest } + { state; data = BranchAt (State.Exp.expect_sym_address pc_exp); rest } ) else begin info "Reached dead code at %t" (Pp.top State.Exp.pp pc_exp); @@ -162,7 +162,7 @@ let gen_endpred ?min ?max ?loop ?(brks = []) () : State.exp -> string option = ( try Some (State.Exp.expect_sym_address pc_exp) with - _ -> None + _ -> debug "PC is sus"; None ) |> Option.map (fun pc -> debug "enpred: Evaluating PC %t" (Pp.top Elf.Address.pp pc); match (min, max, loop) with From 8d417e6f6e3a2d5bcd7f217fd92ec6948fac8ace Mon Sep 17 00:00:00 2001 From: Matej Urban Date: Fri, 17 Jan 2025 19:26:03 +0100 Subject: [PATCH 033/116] Symbolic write --- src/state/base.ml | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/state/base.ml b/src/state/base.ml index be7d6474..480b06d7 100644 --- a/src/state/base.ml +++ b/src/state/base.ml @@ -530,9 +530,16 @@ let write ~provenance (s : t) ~(addr : Exp.t) ~(size : Mem.Size.t) (value : Exp. Mem.write ~provenance s.mem ~addr ~size ~exp:value let write_noprov (s : t) ~(addr : Exp.t) ~(size : Mem.Size.t) (value : Exp.t) : unit = - if ConcreteEval.is_concrete addr || Vec.length s.mem.frags = 0 then - write ~provenance:Ctype.Main s ~addr ~size value - else Raise.fail "Trying to access %t in state %d: No provenance info" Pp.(tos Exp.pp addr) s.id + let elf_addr = eval_address s addr in + debug "Address: %t" Pp.(top (optional Elf.Address.pp) elf_addr); + match elf_addr with + | Some elf_addr -> + let addr_size = addr |> Typed.get_type |> Typed.expect_bv in + let addr = address_to_exp ~size:addr_size elf_addr in + write ~provenance:Ctype.Main s ~addr ~size value + | None when Vec.length s.mem.frags = 0 -> + write ~provenance:Ctype.Main s ~addr ~size value + | None -> Raise.fail "Trying to access %t in state %d: No provenance info" Pp.(tos Exp.pp addr) s.id let reset_reg (s : t) ?(ctyp : Ctype.t option) (reg : Reg.t) : unit = assert (not @@ is_locked s); From 9cb38f4a94902e472c3004ed2a66d559f7179962 Mon Sep 17 00:00:00 2001 From: Matej Urban Date: Mon, 20 Jan 2025 18:43:15 +0000 Subject: [PATCH 034/116] Separate fragments for sections --- src/run/func.ml | 2 +- src/state/base.ml | 71 +++++++++++++++++++++++++++++++++++----------- src/state/base.mli | 3 ++ 3 files changed, 59 insertions(+), 17 deletions(-) diff --git a/src/run/func.ml b/src/run/func.ml index d41d3c03..91167c8b 100644 --- a/src/run/func.ml +++ b/src/run/func.ml @@ -66,7 +66,7 @@ let no_run_prep ~elf:elfname ~name ~entry = let abi = Arch.get_abi api in Trace.Cache.start @@ Arch.get_isla_config (); base "Computing entry state"; - let start = Init.state () |> State.copy ~elf |> abi.init in + let start = Init.state () |> State.copy ~elf |> State.init_sections ~addr_size:Arch.address_size |> abi.init in if entry then base "Entry state:\n%t" (Pp.topi State.pp start); (dwarf, elf, func, start) diff --git a/src/state/base.ml b/src/state/base.ml index 480b06d7..542a3f61 100644 --- a/src/state/base.ml +++ b/src/state/base.ml @@ -216,6 +216,18 @@ end type tval = Tval.t +let section_to_exp ~(size : int) (section : string) = + Typed.extract ~last:(size-1) ~first:0 + (Exp.of_var @@ Var.Section section) + + +let address_to_exp ~(size : int) (addr : Elf.Address.t) = + Typed.( + section_to_exp ~size addr.section + + + bits_int ~size addr.offset + ) + module Mem = struct module Size = Ast.Size @@ -231,20 +243,28 @@ module Mem = struct In general the stack will be the fragment 0 but this is not guaranteed. Some execution contexts may even not have any stacks.*) - type t = { mutable main : Fragment.t; frags : (Exp.t * Fragment.t) Vec.t } + type t = { + mutable main : Fragment.t; + frags : (Exp.t * Fragment.t) Vec.t; + sections : (string, provenance) Hashtbl.t; (* mapping sections to their fragments *) + } (** Get the main fragment of memory *) - let get_main { main; frags = _ } = main + let get_main { main; frags = _; sections = _ } = main (** Empty memory, every address is unbound *) - let empty () = { main = Fragment.empty; frags = Vec.empty () } + let empty () = { main = Fragment.empty; frags = Vec.empty (); sections = Hashtbl.create 10 } (** Build a new memory from the old one by keeping the old one as a base *) let from mem = - { main = Fragment.from mem.main; frags = Vec.map (Pair.map Fun.id Fragment.from) mem.frags } + { + main = Fragment.from mem.main; + frags = Vec.map (Pair.map Fun.id Fragment.from) mem.frags; + sections = Hashtbl.copy mem.sections; + } (** Copy the memory so that it can be mutated separately *) - let copy mem = { main = mem.main; frags = Vec.copy mem.frags } + let copy mem = { main = mem.main; frags = Vec.copy mem.frags; sections = Hashtbl.copy mem.sections } (** Add a new fragment with the specified base *) let new_frag mem base = @@ -312,11 +332,28 @@ module Mem = struct Vec.ppi (fun (base, frag) -> Pp.infix 2 1 colon (Exp.pp base) (Fragment.pp_raw frag)) mem.frags ); + ("sections", hashtbl string Ctype.pp_provenance mem.sections) ] (** Check is this memory is empty which means all addresses are undefined *) let is_empty mem = Fragment.is_empty mem.main && Vec.for_all (Pair.for_all Fun.ctrue Fragment.is_empty) mem.frags + + + let create_section_frag ~addr_size mem section = + match Hashtbl.find_opt mem.sections section with + | Some prov -> + info "Fragment for section %s already exists" section; + prov + | None -> + let base = section_to_exp ~size:addr_size section in + let prov = new_frag mem base in + Hashtbl.replace mem.sections section prov; + prov + + let get_section_provenance mem section = + Hashtbl.find_opt mem.sections section + |> Option.value ~default:Ctype.Main end type t = { @@ -404,6 +441,15 @@ let copy ?elf state = let copy_if_locked ?elf state = if is_locked state then copy ?elf state else state +let init_sections ~addr_size state = + let state = copy_if_locked state in + let _ = Option.( + let+ elf = state.elf in + Elf.SymTable.iter elf.symbols @@ fun sym -> + let _ = Mem.create_section_frag ~addr_size state.mem sym.addr.section in () + ) in + state + let push_assert (s : t) (e : exp) = assert (not @@ is_locked s); s.asserts <- e :: s.asserts @@ -485,15 +531,6 @@ let read ~provenance ?ctyp (s : t) ~(addr : Exp.t) ~(size : Mem.Size.t) : Exp.t Option.iter (set_read s (Var.expect_readvar var)) exp; Option.value exp ~default:(Exp.of_var var) -let address_to_exp ~(size : int) (addr : Elf.Address.t) = - let first = 0 in - let last = size - 1 in - Typed.( - extract ~last ~first - (Exp.of_var @@ Var.Section addr.section) - + - bits_int ~size addr.offset - ) let eval_address (s : t) (addr: Exp.t) : Elf.Address.t option = let ctxt0 = function Var.Section _ -> Value.bv @@ BitVec.of_int ~size:64 0 | _ -> raise ConcreteEval.Symbolic in @@ -520,7 +557,8 @@ let read_noprov ?ctyp (s : t) ~(addr : Exp.t) ~(size : Mem.Size.t) : Exp.t = | Some elf_addr -> let addr_size = addr |> Typed.get_type |> Typed.expect_bv in let addr = address_to_exp ~size:addr_size elf_addr in - read ~provenance:Ctype.Main ?ctyp s ~addr ~size + let provenance = Mem.get_section_provenance s.mem elf_addr.section in + read ~provenance ?ctyp s ~addr ~size | None when Vec.length s.mem.frags = 0 -> read ~provenance:Ctype.Main ?ctyp s ~addr ~size | None -> Raise.fail "Trying to access %t in state %d: No provenance info" Pp.(tos Exp.pp addr) s.id @@ -536,7 +574,8 @@ let write_noprov (s : t) ~(addr : Exp.t) ~(size : Mem.Size.t) (value : Exp.t) : | Some elf_addr -> let addr_size = addr |> Typed.get_type |> Typed.expect_bv in let addr = address_to_exp ~size:addr_size elf_addr in - write ~provenance:Ctype.Main s ~addr ~size value + let provenance = Mem.get_section_provenance s.mem elf_addr.section in + write ~provenance s ~addr ~size value | None when Vec.length s.mem.frags = 0 -> write ~provenance:Ctype.Main s ~addr ~size value | None -> Raise.fail "Trying to access %t in state %d: No provenance info" Pp.(tos Exp.pp addr) s.id diff --git a/src/state/base.mli b/src/state/base.mli index f4945039..5e281f57 100644 --- a/src/state/base.mli +++ b/src/state/base.mli @@ -386,6 +386,9 @@ val copy : ?elf:Elf.File.t -> t -> t The returned state is always unlocked *) val copy_if_locked : ?elf:Elf.File.t -> t -> t +val init_sections : addr_size:int -> t -> t + + (** {1 State convenience manipulation } *) (** Add an assertion to a state *) From 7e10b1d9466a4f98d2f8d9d09a5e3467c7823fa3 Mon Sep 17 00:00:00 2001 From: Matej Urban Date: Mon, 20 Jan 2025 22:18:51 +0000 Subject: [PATCH 035/116] Initialise symvbols --- src/state/base.ml | 118 ++++++++++++++++++++++++++++++++++--------- src/state/base.mli | 16 ++++++ src/trace/context.ml | 33 ++---------- 3 files changed, 116 insertions(+), 51 deletions(-) diff --git a/src/state/base.ml b/src/state/base.ml index 542a3f61..80a1c2a8 100644 --- a/src/state/base.ml +++ b/src/state/base.ml @@ -183,6 +183,18 @@ module Exp = struct in let offset = BitVec.to_int conc in Elf.Address.{ section; offset } + + let of_section ~(size : int) (section : string) = + Typed.extract ~last:(size-1) ~first:0 + (of_var @@ Var.Section section) + + + let of_address ~(size : int) (addr : Elf.Address.t) = + Typed.( + of_section ~size addr.section + + + bits_int ~size addr.offset + ) end type exp = Exp.t @@ -216,17 +228,71 @@ end type tval = Tval.t -let section_to_exp ~(size : int) (section : string) = - Typed.extract ~last:(size-1) ~first:0 - (Exp.of_var @@ Var.Section section) - +module Relocation = struct + type t = { + value: Exp.t; + asserts: Exp.t list; + target: Elf.Relocations.target; + } -let address_to_exp ~(size : int) (addr : Elf.Address.t) = - Typed.( - section_to_exp ~size addr.section - + - bits_int ~size addr.offset - ) + let rec exp_of_relocation_exp: Elf.Relocations.exp -> exp = + let f = exp_of_relocation_exp in function + | Section s -> Exp.of_var (Var.Section s) (* TODO size? *) + | Const x -> Typed.bits (BitVec.of_int x ~size:64) (* TODO size? *) + | BinOp (a, Add, b) -> Typed.(f a + f b) + | BinOp (a, Sub, b) -> Typed.(f a - f b) + | BinOp (a, And, b) -> Typed.manyop (AstGen.Ott.Bvmanyarith AstGen.Ott.Bvand) [f a; f b] + | UnOp (Not, b) -> Typed.unop AstGen.Ott.Bvnot (f b) + + let of_elf (relocation: Elf.Relocations.rel) = + let open Elf.Relocations in + let value = exp_of_relocation_exp relocation.value in + let asserts = List.map (function + | Range (min, max) -> + let min = Typed.bits @@ BitVec.of_z ~size:64 @@ Z.of_int64 min in + let max = Typed.bits @@ BitVec.of_z ~size:64 @@ Z.of_int64 max in + let cond1 = Typed.(binop (Bvcomp Bvsle) min value) in + let cond2 = Typed.(binop (Bvcomp Bvslt) value max) in + Typed.(manyop And [cond1; cond2]) + | Alignment b -> + let last = b-1 in + Typed.(extract ~first:0 ~last value = bits_int ~size:b 0) + ) relocation.assertions in + let (last, first) = relocation.mask in + let value = Typed.extract ~first ~last value in + { value; asserts; target = relocation.target } + + module IMap = Map.Make (Int) + + let exp_of_data (data : Elf.Symbol.data) = + let size = 8 * (BytesSeq.length data.data) in + (* Assume little endian here *) + let bv = BytesSeq.getbvle ~size data.data 0 in + let exp = Typed.bits bv in + IMap.fold (fun offset rel (exp, asserts) -> + let relocation = of_elf rel in + let pos = 8 * offset in + let width = match relocation.target with + | AArch64 Abi_aarch64_symbolic_relocation.Data640 -> 64 + | AArch64 Abi_aarch64_symbolic_relocation.Data320 -> 32 + | _ -> Raise.fail "Unsopported relocation" + in + let before = if pos > 0 then + [Typed.extract ~first:0 ~last:(pos-1) exp] + else + [] + in + let after = if pos + width < size then + [Typed.extract ~first:(pos+width) ~last:(size-1) exp] + else + [] + in + ( + Typed.concat (before @ relocation.value :: after), + relocation.asserts @ asserts + ) + ) data.relocations (exp, []) +end module Mem = struct module Size = Ast.Size @@ -346,7 +412,7 @@ module Mem = struct info "Fragment for section %s already exists" section; prov | None -> - let base = section_to_exp ~size:addr_size section in + let base = Exp.of_section ~size:addr_size section in let prov = new_frag mem base in Hashtbl.replace mem.sections section prov; prov @@ -441,15 +507,6 @@ let copy ?elf state = let copy_if_locked ?elf state = if is_locked state then copy ?elf state else state -let init_sections ~addr_size state = - let state = copy_if_locked state in - let _ = Option.( - let+ elf = state.elf in - Elf.SymTable.iter elf.symbols @@ fun sym -> - let _ = Mem.create_section_frag ~addr_size state.mem sym.addr.section in () - ) in - state - let push_assert (s : t) (e : exp) = assert (not @@ is_locked s); s.asserts <- e :: s.asserts @@ -469,6 +526,21 @@ let set_impossible state = assert (not @@ is_locked state); state.asserts <- [Typed.false_] +let init_sections ~addr_size state = + let state = copy_if_locked state in + let _ = Option.( + let+ elf = state.elf in + Elf.SymTable.iter elf.symbols @@ fun sym -> + if sym.typ = Elf.Symbol.OBJECT then + let provenance = Mem.create_section_frag ~addr_size state.mem sym.addr.section in + let addr = Exp.of_address ~size:addr_size sym.addr in + let size = Ast.Size.of_bytes sym.size in + let (exp, asserts) = Relocation.exp_of_data sym.data in + Mem.write ~provenance state.mem ~addr ~size ~exp; + List.iter (push_relocation_assert state) asserts; + ) in + state + let map_mut_exp (f : exp -> exp) s : unit = assert (not @@ is_locked s); Reg.Map.map_mut_current (Tval.map_exp f) s.regs; @@ -542,7 +614,7 @@ let eval_address (s : t) (addr: Exp.t) : Elf.Address.t option = let size = addr |> Typed.get_type |> Typed.expect_bv in sections |> Hashtbl.to_seq_keys |> Seq.find_map (fun section -> let address = Elf.Address.{ section; offset } in - let expression = address_to_exp ~size address in + let expression = Exp.of_address ~size address in if Z3St.check_full ~hyps Typed.(expression = addr) = Some true then Some address else @@ -556,7 +628,7 @@ let read_noprov ?ctyp (s : t) ~(addr : Exp.t) ~(size : Mem.Size.t) : Exp.t = match elf_addr with | Some elf_addr -> let addr_size = addr |> Typed.get_type |> Typed.expect_bv in - let addr = address_to_exp ~size:addr_size elf_addr in + let addr = Exp.of_address ~size:addr_size elf_addr in let provenance = Mem.get_section_provenance s.mem elf_addr.section in read ~provenance ?ctyp s ~addr ~size | None when Vec.length s.mem.frags = 0 -> @@ -573,7 +645,7 @@ let write_noprov (s : t) ~(addr : Exp.t) ~(size : Mem.Size.t) (value : Exp.t) : match elf_addr with | Some elf_addr -> let addr_size = addr |> Typed.get_type |> Typed.expect_bv in - let addr = address_to_exp ~size:addr_size elf_addr in + let addr = Exp.of_address ~size:addr_size elf_addr in let provenance = Mem.get_section_provenance s.mem elf_addr.section in write ~provenance s ~addr ~size value | None when Vec.length s.mem.frags = 0 -> diff --git a/src/state/base.mli b/src/state/base.mli index 5e281f57..665b61a9 100644 --- a/src/state/base.mli +++ b/src/state/base.mli @@ -160,6 +160,10 @@ module Exp : sig val of_reg : id -> Reg.t -> t val expect_sym_address : t -> Elf.Address.t + + val of_section : size:int -> string -> t + + val of_address : size:int -> Elf.Address.t -> t end type exp = Exp.t @@ -193,6 +197,18 @@ end type tval = Tval.t +module Relocation : sig + type t = { + value: Exp.t; + asserts: Exp.t list; + target: Elf.Relocations.target; + } + + val of_elf : Elf.Relocations.rel -> t + + val exp_of_data : Elf.Symbol.data -> (exp * exp list) +end + (** {1 State memory management } *) (** This module manages the memory part of the state. diff --git a/src/trace/context.ml b/src/trace/context.ml index 1734f041..66a045ee 100644 --- a/src/trace/context.ml +++ b/src/trace/context.ml @@ -63,16 +63,6 @@ type t = { dwarf : Dw.t option; (** Optionally DWARF information. If present, typing is enabled *) } -let rec exp_of_relocation_exp: Elf.Relocations.exp -> State.exp = - let f = exp_of_relocation_exp in function - | Section s -> State.Exp.of_var (State.Var.Section s) (* TODO put the actual value there, size? *) - | Const x -> Exp.Typed.bits (BitVec.of_int x ~size:64) (* TODO size? *) - | BinOp (a, Add, b) -> Exp.Typed.(f a + f b) - | BinOp (a, Sub, b) -> Exp.Typed.(f a - f b) - | BinOp (a, And, b) -> Exp.Typed.manyop (AstGen.Ott.Bvmanyarith AstGen.Ott.Bvand) [f a; f b] - | UnOp (Not, b) -> Exp.Typed.unop AstGen.Ott.Bvnot (f b) - (* | Mask (x, last, first) -> Exp.Typed.extract ~last ~first (f x) *) - (** Build a {!context} from a state *) let make_context ?dwarf ?relocation state = let reg_writes = Vec.empty () in @@ -80,26 +70,13 @@ let make_context ?dwarf ?relocation state = let segments = relocation |> Option.map (fun relocation -> - let open Elf.Relocations in - let value = exp_of_relocation_exp relocation.value in - List.iter (function - | Range (min, max) -> - let min = Exp.Typed.bits @@ BitVec.of_z ~size:64 @@ Z.of_int64 min in - let max = Exp.Typed.bits @@ BitVec.of_z ~size:64 @@ Z.of_int64 max in - let cond1 = Exp.Typed.(binop (Bvcomp Bvsle) min value) in - let cond2 = Exp.Typed.(binop (Bvcomp Bvslt) value max) in - State.push_relocation_assert state Exp.Typed.(manyop And [cond1; cond2]) - | Alignment b -> - let last = b-1 in - State.push_relocation_assert state Exp.Typed.(extract ~first:0 ~last value = bits_int ~size:b 0) - ) relocation.assertions; - let (last, first) = relocation.mask in - let masked = Exp.Typed.extract ~first ~last value in - - relocation.target + let State.Relocation.{value;asserts;target} = State.Relocation.of_elf relocation in + List.iter (State.push_relocation_assert state) asserts; + + target |> Isla.Relocation.segments_of_reloc |> SMap.of_list - |> SMap.map (fun (first, last) -> Exp.Typed.extract ~first ~last masked) + |> SMap.map (fun (first, last) -> Exp.Typed.extract ~first ~last value) ) |> Option.value ~default:SMap.empty in From 9d5173e6d4a5e6f60ba8ef9ec624d744b26cf50f Mon Sep 17 00:00:00 2001 From: Matej Urban Date: Mon, 20 Jan 2025 22:46:43 +0000 Subject: [PATCH 036/116] Fix loading relocations --- src/elf/linksemRelocatable.ml | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/elf/linksemRelocatable.ml b/src/elf/linksemRelocatable.ml index 2123d8f3..d822c2fa 100644 --- a/src/elf/linksemRelocatable.ml +++ b/src/elf/linksemRelocatable.ml @@ -51,7 +51,20 @@ let get_elf64_file_global_symbol_init (f: Elf_file.elf64_file) : global_symbol_i else Byte_sequence.offset_and_cut addr_offset size section.elf64_section_body in - Error.bind (get_relocs section.elf64_section_name_as_string) @@ fun relocs -> + Error.bind (get_relocs section.elf64_section_name_as_string) @@ fun (AArch64 relocs) -> + let relocs = relocs + |> Pmap.bindings_list + |> List.fold_left (fun m (pos, r) -> + let sz = size in + let open Z in + let open Compare in + if pos >= addr_offset && pos < addr_offset + sz then + Pmap.add (pos - addr_offset) r m + else + m + ) (Pmap.empty Z.compare) + |> fun x -> AArch64 x + in Error.bind data @@ fun data -> Error.bind (String_table.get_string_at name strtab) @@ fun str -> let write = Elf_file.flag_is_set Elf_section_header_table.shf_write section.elf64_section_flags in From 04e3bc6765ca5571778ac2e6b71aff6bca4fc025 Mon Sep 17 00:00:00 2001 From: Matej Urban Date: Tue, 21 Jan 2025 11:35:25 +0000 Subject: [PATCH 037/116] Handle jumps --- src/state/base.ml | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/state/base.ml b/src/state/base.ml index 80a1c2a8..b6918d03 100644 --- a/src/state/base.ml +++ b/src/state/base.ml @@ -606,7 +606,13 @@ let read ~provenance ?ctyp (s : t) ~(addr : Exp.t) ~(size : Mem.Size.t) : Exp.t let eval_address (s : t) (addr: Exp.t) : Elf.Address.t option = let ctxt0 = function Var.Section _ -> Value.bv @@ BitVec.of_int ~size:64 0 | _ -> raise ConcreteEval.Symbolic in - let offset = addr |> ConcreteEval.eval ~ctxt:ctxt0 |> Value.expect_bv |> BitVec.to_int in + let open Option in + let* offset_exp = try + Some (ConcreteEval.eval ~ctxt:ctxt0 addr) + with + ConcreteEval.Symbolic -> None + in + let offset = offset_exp |> Value.expect_bv |> BitVec.to_int in let sections = Hashtbl.create 10 in Ast.Manip.exp_iter_var (function Var.Section s -> Hashtbl.add sections s () | _ -> ()) addr; @@ -679,9 +685,7 @@ let set_pc ~(pc : Reg.t) (s : t) (pcval : int) = let ctyp = Ctype.of_frag (Ctype.Global ".text") ~offset:pcval ~constexpr:true in set_reg s pc @@ Tval.make ~ctyp exp -(* TODO *) let set_pc_sym ~(pc : Reg.t) (s : t) (pcval : Elf.Address.t) = - (* set_pc ~pc s pcval.offset *) let exp = Typed.(var ~typ:(Ty_BitVec 64) (Var.Section pcval.section) + bits_int ~size:64 pcval.offset) in let ctyp = Ctype.of_frag (Ctype.Global ".text") ~offset:pcval.offset ~constexpr:true in set_reg s pc @@ Tval.make ~ctyp exp @@ -693,11 +697,8 @@ let bump_pc ~(pc : Reg.t) (s : t) (bump : int) = let new_pc = Elf.Address.(old_pc + bump) in set_pc_sym ~pc s new_pc -(* TODO section + offset *) let concretize_pc ~(pc : Reg.t) (s : t) = - let pc_exp = get_reg_exp s pc in - try ConcreteEval.eval pc_exp |> Value.expect_bv |> BitVec.to_int |> set_pc ~pc s - with ConcreteEval.Symbolic -> () + pc |> get_reg_exp s |> eval_address s |> Option.iter (set_pc_sym ~pc s) let set_last_pc state pc = assert (not @@ is_locked state); From 646f6a50f5021a4fb2efcf34353debcfb74dc808 Mon Sep 17 00:00:00 2001 From: Matej Urban Date: Sun, 26 Jan 2025 18:24:48 +0000 Subject: [PATCH 038/116] Convert dwarf from linksem --- src/ctype/ctype.ml | 34 +++++++++++++++++----------------- src/dw/addr.ml | 6 ++++++ src/dw/func.ml | 2 +- src/dw/loc.ml | 25 +++++++++++++------------ src/dw/var.ml | 15 +++++++++------ src/utils/sym.ml | 3 +++ 6 files changed, 49 insertions(+), 36 deletions(-) create mode 100644 src/dw/addr.ml create mode 100644 src/utils/sym.ml diff --git a/src/ctype/ctype.ml b/src/ctype/ctype.ml index fab17f6b..1c876f37 100644 --- a/src/ctype/ctype.ml +++ b/src/ctype/ctype.ml @@ -81,17 +81,17 @@ end) (*****************************************************************************) (** {1 DWARF constants } *) -let vDW_ATE_address = "DW_ATE_address" |> Dwarf.base_type_attribute_encode |> Z.to_int +let vDW_ATE_address = "DW_ATE_address" |> Dwarf.base_type_attribute_encode |> Sym.to_int -let vDW_ATE_boolean = "DW_ATE_boolean" |> Dwarf.base_type_attribute_encode |> Z.to_int +let vDW_ATE_boolean = "DW_ATE_boolean" |> Dwarf.base_type_attribute_encode |> Sym.to_int -let vDW_ATE_signed = "DW_ATE_signed" |> Dwarf.base_type_attribute_encode |> Z.to_int +let vDW_ATE_signed = "DW_ATE_signed" |> Dwarf.base_type_attribute_encode |> Sym.to_int -let vDW_ATE_signed_char = "DW_ATE_signed_char" |> Dwarf.base_type_attribute_encode |> Z.to_int +let vDW_ATE_signed_char = "DW_ATE_signed_char" |> Dwarf.base_type_attribute_encode |> Sym.to_int -let vDW_ATE_unsigned = "DW_ATE_unsigned" |> Dwarf.base_type_attribute_encode |> Z.to_int +let vDW_ATE_unsigned = "DW_ATE_unsigned" |> Dwarf.base_type_attribute_encode |> Sym.to_int -let vDW_ATE_unsigned_char = "DW_ATE_unsigned_char" |> Dwarf.base_type_attribute_encode |> Z.to_int +let vDW_ATE_unsigned_char = "DW_ATE_unsigned_char" |> Dwarf.base_type_attribute_encode |> Sym.to_int (*****************************************************************************) (*****************************************************************************) @@ -372,7 +372,7 @@ type conversion_context = { env : env; potential_link_name : string option } (** Get the id of a linksem [cupdie] *) let ids_of_cupdie ((cu, _, die) : Dwarf.cupdie) : cupdie_id = - (Z.to_int cu.cu_header.cuh_offset, Z.to_int die.die_offset) + (Sym.to_int cu.cu_header.cuh_offset, Sym.to_int die.die_offset) (** Pretty print the dwarf decl type @@ -380,7 +380,7 @@ let ids_of_cupdie ((cu, _, die) : Dwarf.cupdie) : cupdie_id = let pp_decl (d : Dwarf.decl) = Pp.dprintf "File %s, line %d" (Option.value d.decl_file ~default:"?") - (d.decl_line |> Option.map Z.to_int |> Option.value ~default:0) + (d.decl_line |> Option.map Sym.to_int |> Option.value ~default:0) (** This exception is raised when the type we are trying to reach must came from another translation unit or later in the current one. @@ -402,14 +402,14 @@ let expect_some_link = Option.value_fun ~default:(fun _ -> raise LinkError) Only integers, chars and bools supported. No floating points *) let base_type_of_linksem ?size ~encoding name = - let encoding = Z.to_int encoding in + let encoding = Sym.to_int encoding in if encoding = vDW_ATE_boolean then Cbool else if encoding = vDW_ATE_signed || encoding = vDW_ATE_unsigned then let size = Option.value_fail size "In Ctype.base_type_of_linksem: integer type %s do not have a size" name in - Cint { name; signed = encoding = vDW_ATE_signed; size = Z.to_int size; ischar = false } + Cint { name; signed = encoding = vDW_ATE_signed; size = Sym.to_int size; ischar = false } else if encoding = vDW_ATE_signed_char || encoding = vDW_ATE_unsigned_char then Cint { name; signed = encoding = vDW_ATE_signed_char; size = 1; ischar = true } else Raise.fail "In Ctype.base_of_linksem: encoding %x unknown" encoding @@ -428,7 +428,7 @@ let rec field_of_linksem ~cc ((_, fname, ltyp, offseto) : linksem_field) : field debug "Processed field %t" Pp.(top (opt string) fname); let offset = match offseto with - | Some offset -> Z.to_int offset + | Some offset -> Sym.to_int offset (* assume missing offsets are zero - perhaps should only occur for union members*) | None -> 0 in @@ -469,7 +469,7 @@ and[@warning "-16"] struct_type_of_linksem ?(force_complete = false) ~cc ~cupdie match Hashtbl.find cc.env.lenv (ids_of_cupdie cupdie) with | CT (CT_struct_union (_, Atk_structure, _, msize, _, Some members)) -> let lsize = expect_some_link msize in - let size = Z.to_int lsize in + let size = Sym.to_int lsize in let cc = { cc with potential_link_name = Some name } in let struc : struc = struc_of_linksem ~cc name size members in IdMap.seti cc.env.structs id struc; @@ -485,7 +485,7 @@ and[@warning "-16"] struct_type_of_linksem ?(force_complete = false) ~cc ~cupdie | CT (CT_struct_union (_, Atk_structure, _, msize, _, Some members)) -> let size = match msize with - | Some x -> Z.to_int x + | Some x -> Sym.to_int x | None -> warn "Struct %s doesn't have size" name; 0 @@ -503,7 +503,7 @@ and[@warning "-16"] struct_type_of_linksem ?(force_complete = false) ~cc ~cupdie and enum_of_linksem ~cc:_ name llabels : enum = let labels = Hashtbl.create 5 in - List.iter (fun (_, name, value) -> Hashtbl.add labels (Z.to_int value) name) llabels; + List.iter (fun (_, name, value) -> Hashtbl.add labels (Sym.to_int value) name) llabels; { name; labels } and enum_type_of_linksem ~cc ~cupdie ~mname ~decl : unqualified = @@ -529,13 +529,13 @@ and unqualified_of_linksem ?(force_complete = false) ~cc : linksem_t -> unqualif | CT (CT_pointer (_, Some t)) -> ptr @@ of_linksem_cc ~cc t | CT (CT_pointer (_, None)) -> voidstar | CT (CT_array (_, elem, l)) -> - Array { elem = of_linksem_cc ~cc elem; dims = List.map Fun.(fst %> Option.map Z.to_int) l } + Array { elem = of_linksem_cc ~cc elem; dims = List.map Fun.(fst %> Option.map Sym.to_int) l } | CT (CT_struct_union (cupdie, Atk_structure, mname, _, decl, _)) -> struct_type_of_linksem ~force_complete ~cc ~cupdie ~mname ~decl | CT (CT_struct_union (_, Atk_union, _, size, decl, _)) -> let size = match size with - | Some s -> Z.to_int s + | Some s -> Sym.to_int s | None -> warn "%t: Sizeless union defaulting to 8 for now" Pp.(top pp_decl decl); 8 @@ -619,7 +619,7 @@ let env_of_linksem (lenv : linksem_env) : env = Option.( let+! name = mname and+ size = msize in if not @@ IdMap.mem env.structs name then - IdMap.add env.structs name @@ incomplete_struct name (Z.to_int size) |> ignore) + IdMap.add env.structs name @@ incomplete_struct name (Sym.to_int size) |> ignore) | _ -> ()) lenv; (* Third phase: Add all the type to the result environement *) diff --git a/src/dw/addr.ml b/src/dw/addr.ml new file mode 100644 index 00000000..0604a6f7 --- /dev/null +++ b/src/dw/addr.ml @@ -0,0 +1,6 @@ +include Elf.Address + +let of_sym : Sym.t -> t = function +| Dwarf.Offset (section, offset) -> { section; offset = Z.to_int offset } +| _ -> Raise.fail "expected section+offset" + diff --git a/src/dw/func.ml b/src/dw/func.ml index 18fdd676..a4d5894f 100644 --- a/src/dw/func.ml +++ b/src/dw/func.ml @@ -140,7 +140,7 @@ let of_linksem (elf : Elf.File.t) (tenv : Ctype.env) (lfun : linksem_t) = | None -> ( match lfun.ss_entry_address with | Some a -> ( - let addr = Elf.Address.{section = ".text"; offset = Nat_big_num.to_int a} in (* TODO this is wrong, need symbolic DWARF *) + let addr = Addr.of_sym a in match Elf.SymTable.of_addr_opt elf.symbols addr with | Some sym -> Some sym | None -> None diff --git a/src/dw/loc.ml b/src/dw/loc.ml index e9aacff2..5d03061a 100644 --- a/src/dw/loc.ml +++ b/src/dw/loc.ml @@ -83,44 +83,45 @@ type linksem_t = dwop list let vDW_OP_addr : int = 0x03 (** The integer value of the DW_OP_reg0 constant in DWARF standard *) -let vDW_OP_reg0 : int = Z.to_int Dwarf.vDW_OP_reg0 +let vDW_OP_reg0 : int = Sym.to_int Dwarf.vDW_OP_reg0 (** The integer value of the DW_OP_breg0 constant in DWARF standard *) -let vDW_OP_breg0 : int = Z.to_int Dwarf.vDW_OP_breg0 +let vDW_OP_breg0 : int = Sym.to_int Dwarf.vDW_OP_breg0 (** Convert a linksem location description into a {!Loc.t} Very naive for now : If the list has a single element that we can translate directly, we do. Otherwise, we dump it into the {!t.Dwarf} constructor *) let of_linksem ?(amap = Arch.dwarf_reg_map ()) (elf : Elf.File.t) : linksem_t -> t = - let int_of_oav : Dwarf.operation_argument_value -> int = function - | OAV_natural n -> Z.to_int n - | OAV_integer i -> Z.to_int i - | _ -> failwith "Expected integer argument" + let sym_of_oav : Dwarf.operation_argument_value -> Sym.t = function + | OAV_natural n -> n + | OAV_integer i -> i + | _ -> failwith "Expected integer argument" in + let int_of_oav oav = oav |> sym_of_oav |> Sym.to_int in function (* Register *) | [({ op_semantics = OpSem_reg; _ } as op)] -> - let reg_num = Z.to_int op.op_code - vDW_OP_reg0 in + let reg_num = Sym.to_int op.op_code - vDW_OP_reg0 in if reg_num >= Array.length amap then failwith (Printf.sprintf "Loc.of_linksem: register number %d unknown, code %x, name %s" reg_num - (Z.to_int op.op_code) op.op_string) + (Sym.to_int op.op_code) op.op_string) else Register amap.(reg_num) (* RegisterOffset *) | [({ op_semantics = OpSem_breg; op_argument_values = [arg]; _ } as op)] -> - let reg_num = Z.to_int op.op_code - vDW_OP_breg0 in + let reg_num = Sym.to_int op.op_code - vDW_OP_breg0 in if reg_num >= Array.length amap then failwith (Printf.sprintf "Loc.of_linksem: register number %d unknown, code %x, name %s" reg_num - (Z.to_int op.op_code) op.op_string) + (Sym.to_int op.op_code) op.op_string) else RegisterOffset (amap.(reg_num), int_of_oav arg) (* StackFrame *) | [{ op_semantics = OpSem_fbreg; op_argument_values = [arg]; _ }] -> StackFrame (int_of_oav arg) (* Global *) | [{ op_semantics = OpSem_lit; op_code = code; op_argument_values = [arg]; _ }] as ops - when Z.to_int code = vDW_OP_addr -> ( - let addr = Elf.Address.{ section = ".data"; offset = int_of_oav arg } in (* TODO this is wrong, need symbolic DWARF*) + when Sym.to_int code = vDW_OP_addr -> ( + let addr = Addr.of_sym @@ sym_of_oav arg in try Global (Elf.SymTable.of_addr_with_offset elf.symbols @@ addr) with Not_found -> warn "Symbol at 0x%x not found in Loc.of_linksem" (int_of_oav arg); diff --git a/src/dw/var.ml b/src/dw/var.ml index 0e6d86d5..e453d61b 100644 --- a/src/dw/var.ml +++ b/src/dw/var.ml @@ -45,21 +45,24 @@ (** This module contain all the definition to handle local and global variables as defined in the DWARF information of the target file *) +type range = Addr.t * Addr.t option + (** Type of a DWARF variable *) -type t = { name : string; param : bool; ctype : Ctype.t; locs : ((int * int) * Loc.t) list } +type t = { name : string; param : bool; ctype : Ctype.t; locs : (range * Loc.t) list } (** Type of a DWARF variable in linksem *) type linksem_t = Dwarf.sdt_variable_or_formal_parameter (** Merge contiguous location lists *) let rec loc_merge = function - | ((a1, b1), d1) :: ((a2, b2), d2) :: l when b1 = a2 && Loc.compare d1 d2 = 0 -> + | ((a1, b1), d1) :: ((a2, b2), d2) :: l when b1 = Some a2 && Loc.compare d1 d2 = 0 -> loc_merge (((a1, b2), d1) :: l) | a :: l -> a :: loc_merge l | [] -> [] -(** Convert from Z.t to int, if there is an overflow, returns Int.max_int instead of throwing *) -let clamp_z z = try Z.to_int z with Z.Overflow when Z.compare z Z.zero > 0 -> Int.max_int +let end_addr_of_sym = function +| Dwarf.Absolute z when Z.compare z (Z.of_int Int.max_int) > 0 -> None +| x -> Some (Addr.of_sym x) (** Create a DWARF variable from its linksem counterpart *) let of_linksem (elf : Elf.File.t) (env : Ctype.env) (lvar : linksem_t) : t = @@ -72,7 +75,7 @@ let of_linksem (elf : Elf.File.t) (env : Ctype.env) (lvar : linksem_t) : t = in let locs = lvar.svfp_locations |> Option.value ~default:[] - |> List.map (fun (a, b, l) -> ((Z.to_int a, clamp_z b), Loc.of_linksem elf l)) + |> List.map (fun (a, b, l) -> ((Addr.of_sym a, end_addr_of_sym b), Loc.of_linksem elf l)) |> loc_merge in { name; param; ctype; locs } @@ -85,5 +88,5 @@ let pp_raw v = [ ("name", string v.name); ("ctype", Ctype.pp v.ctype); - ("locs", list (pair (pair ptr ptr) Loc.pp) v.locs); + ("locs", list (pair (pair Addr.pp (opt Addr.pp)) Loc.pp) v.locs); ]) diff --git a/src/utils/sym.ml b/src/utils/sym.ml new file mode 100644 index 00000000..bb2ecd56 --- /dev/null +++ b/src/utils/sym.ml @@ -0,0 +1,3 @@ +type t = Z.t Dwarf.sym0 + +let to_int x = Z.to_int @@ Dwarf.sym_unwrap x "to_int" \ No newline at end of file From 6f086f2896b91da265891ddd60d36eb3d85c5ef3 Mon Sep 17 00:00:00 2001 From: Matej Urban Date: Sun, 26 Jan 2025 22:39:13 +0000 Subject: [PATCH 039/116] [wip] symbolic analyse --- src/analyse/CallGraph.ml | 8 ++-- src/analyse/ControlFlow.ml | 80 ++++++++++++++++---------------- src/analyse/ControlFlowPpDot.ml | 8 ++-- src/analyse/DwarfFrameInfo.ml | 6 +-- src/analyse/DwarfInliningInfo.ml | 6 +-- src/analyse/DwarfLineInfo.ml | 64 ++++++++++++------------- src/analyse/DwarfVarInfo.ml | 38 +++++++-------- src/analyse/Elf.ml | 4 +- src/analyse/ElfSymbols.ml | 4 +- src/analyse/Pp.ml | 10 ++-- src/analyse/QemuLog.ml | 4 +- src/analyse/Utils.ml | 9 +++- src/utils/sym.ml | 17 ++++++- 13 files changed, 139 insertions(+), 119 deletions(-) diff --git a/src/analyse/CallGraph.ml b/src/analyse/CallGraph.ml index e90602e1..026db808 100644 --- a/src/analyse/CallGraph.ml +++ b/src/analyse/CallGraph.ml @@ -60,7 +60,7 @@ type call_graph_node = addr * index * string list let mk_call_graph test (an : CollectedType.analysis) = let mask_addr x:natural = if !Globals.morello - then Nat_big_num.shift_left (Nat_big_num.shift_right x 1) 1 + then Sym.shift_left (Sym.shift_right x 1) 1 else x in (* take the nodes to be all the elf symbol addresses of stt_func symbol type (each with their list of elf symbol names) together @@ -110,7 +110,7 @@ let mk_call_graph test (an : CollectedType.analysis) = if not (List.exists - (function (a'', _) -> Nat_big_num.equal a' a'') + (function (a'', _) -> Sym.equal a' a'') elf_symbols) then Some (a', ["FROM BL:" ^ s']) else None) @@ -122,7 +122,7 @@ let mk_call_graph test (an : CollectedType.analysis) = match axs with | [] -> acc | (a, x) :: axs' -> - if not (List.exists (function (a', _) -> Nat_big_num.equal a a') acc) then + if not (List.exists (function (a', _) -> Sym.equal a a') acc) then dedup axs' ((a, x) :: acc) else dedup axs' acc in @@ -133,7 +133,7 @@ let mk_call_graph test (an : CollectedType.analysis) = List.sort (function | (a, _) -> ( - function (a', _) -> Nat_big_num.compare a a' + function (a', _) -> Sym.compare a a' )) (elf_symbols @ extra_bl_targets) in diff --git a/src/analyse/ControlFlow.ml b/src/analyse/ControlFlow.ml index 9d9628c0..89bc11f6 100644 --- a/src/analyse/ControlFlow.ml +++ b/src/analyse/ControlFlow.ml @@ -119,11 +119,11 @@ let branch_table_target_addresses test filename_branch_table_option : (addr * ad with | (a_br, a_table, n, shift, a_offset) -> Some - ( Nat_big_num.of_int a_br, - ( Nat_big_num.of_int a_table, - Nat_big_num.of_int n, + ( Sym.of_int a_br, + ( Sym.of_int a_table, + Sym.of_int n, shift, - Nat_big_num.of_int a_offset ) ) + Sym.of_int a_offset ) ) | exception _ -> fatal "couldn't parse branch table data file line: \"%s\"\n" s in List.filter_map parse_line (List.tl (Array.to_list lines)) @@ -131,7 +131,7 @@ let branch_table_target_addresses test filename_branch_table_option : (addr * ad in (* pull out .rodata section from ELF *) - let ((_, rodata_addr, bs) as _rodata : Dwarf.p_context * Nat_big_num.num * BytesSeq.t) = + let ((_, rodata_addr, bs) as _rodata : Dwarf.p_context * Sym.t * BytesSeq.t) = Dwarf.extract_section_body_without_relocations test.elf_file ".rodata" false in (* chop into bytes *) @@ -143,37 +143,37 @@ let branch_table_target_addresses test filename_branch_table_option : (addr * ad let read_rodata_b addr = Elf_types_native_uint.natural_of_byte - rodata_bytes.(Nat_big_num.to_int (Nat_big_num.sub addr rodata_addr)) + rodata_bytes.(Sym.to_int (Sym.sub addr rodata_addr)) in let read_rodata_h addr = - Nat_big_num.add (read_rodata_b addr) - (Nat_big_num.mul (Nat_big_num.of_int 256) - (read_rodata_b (Nat_big_num.add addr (Nat_big_num.of_int 1)))) + Sym.add (read_rodata_b addr) + (Sym.mul (Sym.of_int 256) + (read_rodata_b (Sym.add addr (Sym.of_int 1)))) in let sign_extend_W n = - let half = Nat_big_num.mul (Nat_big_num.of_int 65536) (Nat_big_num.of_int 32768) in - let whole = Nat_big_num.mul half (Nat_big_num.of_int 2) in - if Nat_big_num.greater_equal n half then Nat_big_num.sub n whole else n + let half = Sym.mul (Sym.of_int 65536) (Sym.of_int 32768) in + let whole = Sym.mul half (Sym.of_int 2) in + if Sym.greater_equal n half then Sym.sub n whole else n in let read_rodata_W addr = sign_extend_W - (Nat_big_num.add (read_rodata_b addr) - (Nat_big_num.add - (Nat_big_num.mul (Nat_big_num.of_int 256) - (read_rodata_b (Nat_big_num.add addr (Nat_big_num.of_int 1)))) - (Nat_big_num.add - (Nat_big_num.mul (Nat_big_num.of_int 65536) - (read_rodata_b (Nat_big_num.add addr (Nat_big_num.of_int 2)))) - (Nat_big_num.mul (Nat_big_num.of_int 16777216) - (read_rodata_b (Nat_big_num.add addr (Nat_big_num.of_int 3))))))) + (Sym.add (read_rodata_b addr) + (Sym.add + (Sym.mul (Sym.of_int 256) + (read_rodata_b (Sym.add addr (Sym.of_int 1)))) + (Sym.add + (Sym.mul (Sym.of_int 65536) + (read_rodata_b (Sym.add addr (Sym.of_int 2)))) + (Sym.mul (Sym.of_int 16777216) + (read_rodata_b (Sym.add addr (Sym.of_int 3))))))) in let rec natural_assoc_opt n nys = match nys with | [] -> None - | (n', y) :: nys' -> if Nat_big_num.equal n n' then Some y else natural_assoc_opt n nys' + | (n', y) :: nys' -> if Sym.equal n n' then Some y else natural_assoc_opt n nys' in (* this is the evaluator for a little stack-machine language used in the hafnium.branch-table files to describe the access pattern for each branch table *) @@ -187,8 +187,8 @@ let branch_table_target_addresses test filename_branch_table_option : (addr * ad h read two bytes from the branch table W read four byte from the branch table and sign-extend *) - let rec eval_shift_expression (shift : string) (a_table : Nat_big_num.num) - (a_offset : Nat_big_num.num) (i : Nat_big_num.num) (stack : Nat_big_num.num list) (pc : int) + let rec eval_shift_expression (shift : string) (a_table : Sym.t) + (a_offset : Sym.t) (i : Sym.t) (stack : Sym.t list) (pc : int) = if pc = String.length shift then match stack with @@ -205,8 +205,8 @@ let branch_table_target_addresses test filename_branch_table_option : (addr * ad match stack with | a :: stack' -> let a' = - Nat_big_num.mul a - (Nat_big_num.pow_int_positive 2 (Char.code command - Char.code '0')) + Sym.mul a + (Sym.pow_int_positive 2 (Char.code command - Char.code '0')) in eval_shift_expression shift a_table a_offset i (a' :: stack') (pc + 1) | _ -> fatal "eval_shift_expression shift empty stack" @@ -222,7 +222,7 @@ let branch_table_target_addresses test filename_branch_table_option : (addr * ad (* plus *) match stack with | a1 :: a2 :: stack' -> - let a' = Nat_big_num.add a1 a2 in + let a' = Sym.add a1 a2 in eval_shift_expression shift a_table a_offset i (a' :: stack') (pc + 1) | _ -> fatal "eval_shift_expression plus emptyish stack" else if command = 'b' then @@ -254,24 +254,24 @@ let branch_table_target_addresses test filename_branch_table_option : (addr * ad (function | (a_br, (a_table, size, shift, a_offset)) -> let rec f i = - if i > Nat_big_num.to_int size then [] + if i > Sym.to_int size then [] else let a_target = if shift = "2" then - let table_entry_addr = Nat_big_num.add a_table (Nat_big_num.of_int (4 * i)) in + let table_entry_addr = Sym.add a_table (Sym.of_int (4 * i)) in match natural_assoc_opt table_entry_addr rodata_words with | None -> fatal "no branch table entry for address %s\n" (pp_addr table_entry_addr) | Some table_entry -> let a_target = - Nat_big_num.modulus - (Nat_big_num.add a_table table_entry) - (Nat_big_num.pow_int_positive 2 32) + Sym.modulus + (Sym.add a_table table_entry) + (Sym.pow_int_positive 2 32) in (* that 32 is good for the sign-extended negative 32-bit offsets we see in the old hafnium-playground-src branch tables *) a_target - else eval_shift_expression shift a_table a_offset (Nat_big_num.of_int i) [] 0 + else eval_shift_expression shift a_table a_offset (Sym.of_int i) [] 0 in a_target :: f (i + 1) in @@ -289,10 +289,10 @@ let branch_table_target_addresses test filename_branch_table_option : (addr * ad let parse_addr (s : string) : natural = try - Scanf.sscanf s "0x%Lx" (fun i64 -> Nat_big_num.of_int64 i64) + Scanf.sscanf s "0x%Lx" (fun i64 -> Sym.of_int64 i64) with Scanf.Scan_failure _ -> - Scanf.sscanf s "%Lx" (fun i64 -> Nat_big_num.of_int64 i64) + Scanf.sscanf s "%Lx" (fun i64 -> Sym.of_int64 i64) let parse_target s = match Scanf.sscanf s " %s %s" (fun s1 s2 -> (s1, s2)) with @@ -360,7 +360,7 @@ let parse_control_flow_instruction s mnemonic s' : control_flow_insn = let targets_of_control_flow_insn_without_index branch_table_targets (addr : natural) (opcode_bytes : int list) (c : control_flow_insn) : (target_kind * addr * string) list = - let succ_addr = Nat_big_num.add addr (Nat_big_num.of_int (List.length opcode_bytes)) in + let succ_addr = Sym.add addr (Sym.of_int (List.length opcode_bytes)) in let targets = match c with | C_no_instruction -> [] @@ -465,7 +465,7 @@ let parse_objdump_line (s : string) : objdump_instruction option = if Str.string_match objdump_line_regexp s 0 then begin let addr_int64 = parse_hex_int64 (Str.matched_group 1 s) in - let addr = Nat_big_num.of_int64 addr_int64 in + let addr = Sym.of_int64 addr_int64 in let op = Str.matched_group 2 s in let op = strip_whitespace op in let opcode_byte_strings = @@ -497,7 +497,7 @@ let rec parse_objdump_lines arch lines (next_index : int) (last_address : natura match last_address with | None -> i :: parse_objdump_lines arch lines (next_index + 1) (Some addr) | Some last_address' -> - let last_address'' = Nat_big_num.add last_address' (Nat_big_num.of_int 4) in + let last_address'' = Sym.add last_address' (Sym.of_int 4) in if addr > last_address'' then (* fake up "missing" instructions for any gaps in the address space*) (*warn "gap in objdump instruction address sequence at %s" (pp_addr last_address'');*) @@ -602,11 +602,11 @@ let highlight c = (* highlight branch targets to earlier addresses*) let pp_target_addr_wrt (addr : natural) (c : control_flow_insn) (a : natural) = - (if highlight c && Nat_big_num.less a addr then "^" else "") ^ pp_addr a + (if highlight c && Sym.less a addr then "^" else "") ^ pp_addr a (* highlight branch come-froms from later addresses*) let pp_come_from_addr_wrt (addr : natural) (c : control_flow_insn) (a : natural) = - (if highlight c && Nat_big_num.greater a addr then "v" else "") ^ pp_addr a + (if highlight c && Sym.greater a addr then "v" else "") ^ pp_addr a (* let pp_branch_targets (xs : (addr * control_flow_insn * (target_kind * addr * int * string) list) list) diff --git a/src/analyse/ControlFlowPpDot.ml b/src/analyse/ControlFlowPpDot.ml index fc3f90ac..77375477 100644 --- a/src/analyse/ControlFlowPpDot.ml +++ b/src/analyse/ControlFlowPpDot.ml @@ -204,7 +204,7 @@ let inlining_stack_at_index (an : analysis) k = (function | sss' -> String.concat "\n---\n" - (List.map (Dwarf.pp_sdt_subroutine (Nat_big_num.of_int 0)) sss')) + (List.map (Dwarf.pp_sdt_subroutine (Sym.of_int 0)) sss')) maximal)); [] @@ -793,7 +793,7 @@ let mk_cfg test an visitedo node_name_prefix (recurse_flat : bool) (_inline_all let ((_comp_dir, _dir, _file) as ufe) = Dwarf.unpack_file_entry lnh lnr.lnr_file in - (ufe, Nat_big_num.to_int lnr.lnr_line)) + (ufe, Sym.to_int lnr.lnr_line)) an.line_info.(k)) in @@ -859,12 +859,12 @@ let mk_cfg test an visitedo node_name_prefix (recurse_flat : bool) (_inline_all match new_ss_O2_ambient_option with | None -> fatal "no call site for\n%s" - (Dwarf.pp_sdt_subroutine (Nat_big_num.of_int 0) ss_current) + (Dwarf.pp_sdt_subroutine (Sym.of_int 0) ss_current) | Some new_ss_O2_ambient -> ( match new_ss_O2_ambient.ss_call_site with | None -> fatal "no call site2 for\n%s" - (Dwarf.pp_sdt_subroutine (Nat_big_num.of_int 0) ss_current) + (Dwarf.pp_sdt_subroutine (Sym.of_int 0) ss_current) | Some (_ufe, line, _subprogram_name) -> line ) (*, diff --git a/src/analyse/DwarfFrameInfo.ml b/src/analyse/DwarfFrameInfo.ml index 2b41c478..991b295d 100644 --- a/src/analyse/DwarfFrameInfo.ml +++ b/src/analyse/DwarfFrameInfo.ml @@ -56,11 +56,11 @@ let aof ((a : natural), (_cfa : string), (_regs : (string * string) list)) = a let rec f (aof : 'b -> natural) (a : natural) (last : 'b option) (bs : 'b list) : 'b option = match (last, bs) with | (None, []) -> None - | (Some b', []) -> if Nat_big_num.greater_equal a (aof b') then Some b' else None + | (Some b', []) -> if Sym.greater_equal a (aof b') then Some b' else None | (None, b'' :: bs') -> f aof a (Some b'') bs' | (Some b', b'' :: bs') -> - if Nat_big_num.less a (aof b') then None - else if Nat_big_num.greater_equal a (aof b') && Nat_big_num.less a (aof b'') then Some b' + if Sym.less a (aof b') then None + else if Sym.greater_equal a (aof b') && Sym.less a (aof b'') then Some b' else f aof a (Some b'') bs' let mk_frame_info test instructions : diff --git a/src/analyse/DwarfInliningInfo.ml b/src/analyse/DwarfInliningInfo.ml index bc6a1903..832b4cac 100644 --- a/src/analyse/DwarfInliningInfo.ml +++ b/src/analyse/DwarfInliningInfo.ml @@ -68,7 +68,7 @@ let mk_inlining test sdt instructions = let addr = i.i_addr in let issr_still_current = List.filter - (function (_label, ((_n1, n2), (_m, _n), _is)) -> Nat_big_num.less addr n2) + (function (_label, ((_n1, n2), (_m, _n), _is)) -> Sym.less addr n2) issr_current in @@ -83,8 +83,8 @@ let mk_inlining test sdt instructions = let (issr_starting_here0, issr_rest') = find_first - (function ((_n1, n2), (_m, _n), _is) -> Nat_big_num.less_equal n2 addr) - (function ((n1, _n2), (_m, _n), _is) -> Nat_big_num.equal n1 addr) + (function ((_n1, n2), (_m, _n), _is) -> Sym.less_equal n2 addr) + (function ((n1, _n2), (_m, _n), _is) -> Sym.equal n1 addr) [] issr_rest in diff --git a/src/analyse/DwarfLineInfo.ml b/src/analyse/DwarfLineInfo.ml index cf126a41..b3561313 100644 --- a/src/analyse/DwarfLineInfo.ml +++ b/src/analyse/DwarfLineInfo.ml @@ -86,18 +86,18 @@ type evaluated_line_info_for_instruction = { (* line number sequences can overlap, and we have to walk through instructions (not addresses), so we simplify by splitting all of them into individual entries, sort them by first address, and then walk through them painting a per-instruction array. This is algorithmically a bit terrible, but seems to add only a couple of seconds to read-dwarf rd *) let pp_line_number_header_concise (lnh : Dwarf.line_number_header) : string = - "lnh offset = " ^ Dwarf.pphex lnh.lnh_offset ^ "\n" + "lnh offset = " ^ Dwarf.pphex_sym lnh.lnh_offset ^ "\n" (*^ ("dwarf_format = " ^ (pp_dwarf_format lnh.lnh_dwarf_format ^ ("\n" -^ ("unit_length = " ^ (Nat_big_num.to_string lnh.lnh_unit_length ^ ("\n" -^ ("version = " ^ (Nat_big_num.to_string lnh.lnh_version ^ ("\n" -^ ("header_length = " ^ (Nat_big_num.to_string lnh.lnh_header_length ^ ("\n" -^ ("minimum_instruction_length = " ^ (Nat_big_num.to_string lnh.lnh_minimum_instruction_length ^ ("\n" -^ ("maximum_operations_per_instruction = " ^ (Nat_big_num.to_string lnh.lnh_maximum_operations_per_instruction ^ ("\n" +^ ("unit_length = " ^ (Sym.to_string lnh.lnh_unit_length ^ ("\n" +^ ("version = " ^ (Sym.to_string lnh.lnh_version ^ ("\n" +^ ("header_length = " ^ (Sym.to_string lnh.lnh_header_length ^ ("\n" +^ ("minimum_instruction_length = " ^ (Sym.to_string lnh.lnh_minimum_instruction_length ^ ("\n" +^ ("maximum_operations_per_instruction = " ^ (Sym.to_string lnh.lnh_maximum_operations_per_instruction ^ ("\n" ^ ("default_is_stmt = " ^ (string_of_bool lnh.lnh_default_is_stmt ^ ("\n" -^ ("line_base = " ^ (Nat_big_num.to_string lnh.lnh_line_base ^ ("\n" -^ ("line_range = " ^ (Nat_big_num.to_string lnh.lnh_line_range ^ ("\n" -^ ("opcode_base = " ^ (Nat_big_num.to_string lnh.lnh_opcode_base ^ ("\n" +^ ("line_base = " ^ (Sym.to_string lnh.lnh_line_base ^ ("\n" +^ ("line_range = " ^ (Sym.to_string lnh.lnh_line_range ^ ("\n" +^ ("opcode_base = " ^ (Sym.to_string lnh.lnh_opcode_base ^ ("\n" ^ ("standard_opcode_lengths = " ^ (string_of_list instance_Show_Show_Num_natural_dict lnh.lnh_standard_opcode_lengths ^ ("\n" ^ ("comp_dir = " ^ (string_of_maybe @@ -136,7 +136,7 @@ let split_into_sequences | None -> fatal "split_into_sequences found sequence of length 0" in let last = lnr.lnr_address in - if Nat_big_num.equal first last then fatal "split_into_sequences found first=last" + if Sym.equal first last then fatal "split_into_sequences found first=last" else (); let elis = { @@ -160,8 +160,8 @@ let split_into_entries (s : evaluated_line_info_sequence) : evaluated_line_info_ { elie_first = l1.lnr_address; elie_last = - ( if Nat_big_num.equal l2.lnr_address l1.lnr_address then l1.lnr_address - else Nat_big_num.sub l2.lnr_address (Nat_big_num.of_int 1) + ( if Sym.equal l2.lnr_address l1.lnr_address then l1.lnr_address + else Sym.sub l2.lnr_address (Sym.of_int 1) ); elie_lnh = s.elis_lnh; elie_lnr = l1; @@ -176,9 +176,9 @@ let split_into_entries (s : evaluated_line_info_sequence) : evaluated_line_info_ let mk_line_info (eli: Dwarf.evaluated_line_info) instructions : evaluated_line_info_for_instruction option array = let sequences = List.flatten (List.map split_into_sequences eli) in - let compare_sequence s1 s2 = Nat_big_num.compare s1.elis_first s2.elis_first in + let compare_sequence s1 s2 = Sym.compare s1.elis_first s2.elis_first in let sequences_sorted = List.sort compare_sequence sequences in - (*let overlap_sequence s1 s2 = not( Nat_big_num.greater_equal s2.first s1.last || Nat_big_num.greater_equal s1.first s2.last) in*) + (*let overlap_sequence s1 s2 = not( Sym.greater_equal s2.first s1.last || Sym.greater_equal s1.first s2.last) in*) Printf.printf "mk_line_info\n%s" (String.concat "\n" (List.map pp_sequence_concise sequences_sorted)); @@ -189,12 +189,12 @@ let mk_line_info (eli: Dwarf.evaluated_line_info) instructions : evaluated_line_ let (discardable,remaining') = List.partition (function sequence -> - Nat_big_num.less_equal sequence.elis_last addr) + Sym.less_equal sequence.elis_last addr) remaining_sequences in let (sequences,remaining'') = List.partition (function sequence -> - Nat_big_num.less_equal sequence.elis_first addr) + Sym.less_equal sequence.elis_first addr) remaining' in (sequences,remaining'') in @@ -224,7 +224,7 @@ let mk_line_info (eli: Dwarf.evaluated_line_info) instructions : evaluated_line_ let addr = instructions.(k).i_addr in match remaining_lines with | l1::((l2::remaining_lines') as remaining_lines'') -> - if Nat_big_num.equal addr l1.lnr_address then + if Sym.equal addr l1.lnr_address then (* this instruction address exactly matches the first line of this sequence *) let elifi = { elifi_start = true; @@ -232,7 +232,7 @@ let mk_line_info (eli: Dwarf.evaluated_line_info) instructions : evaluated_line_ elifi_line = l1 } in elifis.(k) <- Some elifi; f active_sequence remaining_lines remaining_sequences (k+1) - else if Nat_big_num.less l1.lnr_address addr && Nat_big_num.less addr l2.lnr_address then + else if Sym.less l1.lnr_address addr && Sym.less addr l2.lnr_address then (* this instruction address is within the range of the first line, but not equal to it*) let elifi = { elifi_start = false; @@ -240,7 +240,7 @@ let mk_line_info (eli: Dwarf.evaluated_line_info) instructions : evaluated_line_ elifi_line = l1 } in elifis.(k) <- Some elifi; f active_sequence remaining_lines remaining_sequences (k+1) - else if Nat_big_num.greater_equal addr l2.lnr_address then + else if Sym.greater_equal addr l2.lnr_address then (* this instruction address is after the range of the first line *) if not(l2.lnr_end_sequence (* invariant: iff remaining'=[]*)) then (* there are more non-end lines left in this sequence: try again with the next *) @@ -268,11 +268,11 @@ let mk_line_info (eli : Dwarf.evaluated_line_info) instructions : let elifis = Array.make size [] in let sequences = List.flatten (List.map split_into_sequences eli) in - let compare_sequence s1 s2 = Nat_big_num.compare s1.elis_first s2.elis_first in + let compare_sequence s1 s2 = Sym.compare s1.elis_first s2.elis_first in let sequences_sorted = List.sort compare_sequence sequences in let entries = List.flatten (List.map split_into_entries sequences_sorted) in - let compare_entry e1 e2 = Nat_big_num.compare e1.elie_first e2.elie_first in + let compare_entry e1 e2 = Sym.compare e1.elie_first e2.elie_first in let entries_sorted = List.sort compare_entry entries in (*List.iter (function elie -> Printf.printf "%s" (pp_elie_concise elie)) entries_sorted;*) @@ -285,7 +285,7 @@ let mk_line_info (eli : Dwarf.evaluated_line_info) instructions : match remaining with | [] -> (acc, remaining) | elie :: remaining' -> - if Nat_big_num.less_equal elie.elie_first addr then + if Sym.less_equal elie.elie_first addr then mk_new_perhaps_relevant (elie :: acc) remaining' else (acc, remaining) in @@ -293,7 +293,7 @@ let mk_line_info (eli : Dwarf.evaluated_line_info) instructions : let (new_perhaps_relevant, remaining') = mk_new_perhaps_relevant [] remaining_entries in let addr_in elie = - Nat_big_num.less_equal elie.elie_first addr && Nat_big_num.less_equal addr elie.elie_last + Sym.less_equal elie.elie_first addr && Sym.less_equal addr elie.elie_last in let still_active_entries = @@ -305,7 +305,7 @@ let mk_line_info (eli : Dwarf.evaluated_line_info) instructions : (function | elie -> let elifi = - { elifi_start = Nat_big_num.equal addr elie.elie_first; elifi_entry = elie } + { elifi_start = Sym.equal addr elie.elie_first; elifi_entry = elie } in elifi) still_active_entries; @@ -443,14 +443,14 @@ let pp_dwarf_source_file_lines' m (ds : Dwarf.dwarf_static) (pp_actual_line : bo | Ascii -> s | Html -> "@" ^ s ^ "@ " in wrap_link m (subprogram_name ^ ":" - ^ Nat_big_num.to_string lnr.lnr_line + ^ Sym.to_string lnr.lnr_line ^ "." - ^ Nat_big_num.to_string lnr.lnr_column + ^ Sym.to_string lnr.lnr_column ^ " (" ^ file ^ ")" ) (* ^ (if elifi.elifi_start then "S" else "s")*) @@ -463,10 +463,10 @@ let pp_dwarf_source_file_lines' m (ds : Dwarf.dwarf_static) (pp_actual_line : bo ^ " " ^ if pp_actual_line then - let line = Nat_big_num.to_int lnr.lnr_line in + let line = Sym.to_int lnr.lnr_line in if line = 0 then "line 0" else - pp_source_line (source_line (comp_dir, dir, file) line) (Nat_big_num.to_int lnr.lnr_column) + pp_source_line (source_line (comp_dir, dir, file) line) (Sym.to_int lnr.lnr_column) else "" (* OLD source line number for O0/2 correlation @@ -479,11 +479,11 @@ let rec dwarf_source_file_line_numbers' test recursion_limit (a : natural) : match sls with | [] -> dwarf_source_file_line_numbers' test (recursion_limit - 1) - (Nat_big_num.sub a (Nat_big_num.of_int 4)) + (Sym.sub a (Sym.of_int 4)) | _ -> List.map (fun ((comp_dir, dir, file), n, lnr, subprogram_name) -> - (subprogram_name, Nat_big_num.to_int n)) + (subprogram_name, Sym.to_int n)) sls let dwarf_source_file_line_numbers test (a : natural) = @@ -507,7 +507,7 @@ let dwarf_source_file_line_numbers_by_index test line_info k : Dwarf.subprogram_at_line test.dwarf_static.ds_subprogram_line_extents ufe lnr.lnr_line ) in - (subprogram_name, Nat_big_num.to_int lnr.lnr_line)) + (subprogram_name, Sym.to_int lnr.lnr_line)) elifis) in match lines with [_] -> lines | [] -> lines | _ -> [] diff --git a/src/analyse/DwarfVarInfo.ml b/src/analyse/DwarfVarInfo.ml index bd0709f0..54e2964f 100644 --- a/src/analyse/DwarfVarInfo.ml +++ b/src/analyse/DwarfVarInfo.ml @@ -76,7 +76,7 @@ let pp_sdt_concise_variable_or_formal_parameter_main (level : int) ^ (match svfp.svfp_type with None -> "none" | Some t -> Dwarf.pp_type_info_deep t) ^ " " (*^ indent ^ "const_value:"*) - ^ (match svfp.svfp_const_value with None -> "" | Some v -> "const:" ^ Nat_big_num.to_string v) + ^ (match svfp.svfp_const_value with None -> "" | Some v -> "const:" ^ Sym.to_string v) ^ " " (*^ indent ^ "external:" ^ show svfp.svfp_external ^ "\n"*) @@ -90,7 +90,7 @@ let pp_sdt_concise_variable_or_formal_parameter (level : int) (is_params : bool) ^ match svfp.svfp_locations with | None -> "no locations\n" - | Some [loc] -> " " ^ Dwarf.pp_parsed_single_location_description (Nat_big_num.of_int 0) loc + | Some [loc] -> " " ^ Dwarf.pp_parsed_single_location_description (Z.of_int 0) loc | Some locs -> "\n" ^ String.concat "" @@ -99,7 +99,7 @@ let pp_sdt_concise_variable_or_formal_parameter (level : int) (is_params : bool) | loc -> "+" ^ Dwarf.pp_parsed_single_location_description - (Nat_big_num.of_int (level + 1)) + (Z.of_int (level + 1)) loc) locs) @@ -150,14 +150,14 @@ let rec locals_subroutine context (ss : Dwarf.sdt_subroutine) = ^ (indent (*^ "name:" ^*) ^ (pp_sdt_maybe ss.ss_name (fun name1 -> name1 ^ "\n") (* ^ indent ^ "cupdie:" ^ pp_cupdie3 ss.ss_cupdie ^ "\n"*) ^ (indent ^ ("kind:" ^ (((match ss.ss_kind with SSK_subprogram -> "subprogram" | SSK_inlined_subroutine -> "inlined subroutine" )) ^ ("\n" - ^ (indent ^ ("call site:" ^ (pp_sdt_maybe ss.ss_call_site (fun ud -> "\n" ^ (indent_level true (Nat_big_num.add level(Nat_big_num.of_int 1)) ^ (pp_ud ud ^ "\n"))) - ^ (indent ^ ("abstract origin:" ^ (pp_sdt_maybe ss.ss_abstract_origin (fun s -> "\n" ^ locals__subroutine (Nat_big_num.add level(Nat_big_num.of_int 1)) s) + ^ (indent ^ ("call site:" ^ (pp_sdt_maybe ss.ss_call_site (fun ud -> "\n" ^ (indent_level true (Sym.add level(Sym.of_int 1)) ^ (pp_ud ud ^ "\n"))) + ^ (indent ^ ("abstract origin:" ^ (pp_sdt_maybe ss.ss_abstract_origin (fun s -> "\n" ^ locals__subroutine (Sym.add level(Sym.of_int 1)) s) (* ^ indent ^ "type:" ^ pp_sdt_maybe ss.ss_type (fun typ -> pp_type_info_deep typ ^"\n" end)*) - ^ (indent ^ ("vars:" ^ (pp_sdt_list ss.ss_vars (pp_sdt_concise_variable_or_formal_parameter (Nat_big_num.add level(Nat_big_num.of_int 1))) - ^ (indent ^ ("unspecified_parameters:" ^ (pp_sdt_list ss.ss_unspecified_parameters (pp_sdt_unspecified_parameter (Nat_big_num.add level(Nat_big_num.of_int 1))) + ^ (indent ^ ("vars:" ^ (pp_sdt_list ss.ss_vars (pp_sdt_concise_variable_or_formal_parameter (Sym.add level(Sym.of_int 1))) + ^ (indent ^ ("unspecified_parameters:" ^ (pp_sdt_list ss.ss_unspecified_parameters (pp_sdt_unspecified_parameter (Sym.add level(Sym.of_int 1))) (* ^ indent ^ "pc ranges:" ^ pp_pc_ranges (level+1) ss.ss_pc_ranges*) - ^ (indent ^ ("subroutines:" ^ (pp_sdt_list ss.ss_subroutines (locals__subroutine (Nat_big_num.add level(Nat_big_num.of_int 1))) - ^ (indent ^ ("lexical_blocks:" ^ (pp_sdt_list ss.ss_lexical_blocks (locals__lexical_block (Nat_big_num.add level(Nat_big_num.of_int 1))) + ^ (indent ^ ("subroutines:" ^ (pp_sdt_list ss.ss_subroutines (locals__subroutine (Sym.add level(Sym.of_int 1))) + ^ (indent ^ ("lexical_blocks:" ^ (pp_sdt_list ss.ss_lexical_blocks (locals__lexical_block (Sym.add level(Sym.of_int 1))) (* ^ indent ^ "decl:" ^ pp_sdt_maybe ss.ss_decl (fun ((ufe,line) as ud) -> "\n" ^ indent_level true (level+1) ^ pp_ufe ufe ^ " " ^ show line ^ "\n" end)*) (* ^ indent ^ "noreturn:" ^ show ss.ss_noreturn ^ "\n"*) (* ^ indent ^ "external:" ^ show ss.ss_external ^"\n"*) @@ -172,10 +172,10 @@ and locals_lexical_block context (lb : Dwarf.sdt_lexical_block) = (* "" (* ^ indent ^ "cupdie:" ^ pp_cupdie3 lb.slb_cupdie ^ "\n"*) - ^ (indent ^ ("vars:" ^ (pp_sdt_list lb.slb_vars (pp_sdt_concise_variable_or_formal_parameter (Nat_big_num.add level(Nat_big_num.of_int 1))) + ^ (indent ^ ("vars:" ^ (pp_sdt_list lb.slb_vars (pp_sdt_concise_variable_or_formal_parameter (Sym.add level(Sym.of_int 1))) (* ^ indent ^ "pc ranges:" ^ pp_pc_ranges (level+1) lb.slb_pc_ranges*) - ^ (indent ^ ("subroutines :" ^ (pp_sdt_list lb.slb_subroutines (locals__subroutine (Nat_big_num.add level(Nat_big_num.of_int 1))) - ^ (indent ^ ("lexical_blocks:" ^ (pp_sdt_list lb.slb_lexical_blocks (locals__lexical_block (Nat_big_num.add level(Nat_big_num.of_int 1))) + ^ (indent ^ ("subroutines :" ^ (pp_sdt_list lb.slb_subroutines (locals__subroutine (Sym.add level(Sym.of_int 1))) + ^ (indent ^ ("lexical_blocks:" ^ (pp_sdt_list lb.slb_lexical_blocks (locals__lexical_block (Sym.add level(Sym.of_int 1))) ^ "\n")))))))))) *) @@ -190,8 +190,8 @@ let locals_compilation_unit context (cu : Dwarf.sdt_compilation_unit) = "" ^ (indent (*^ "name:" *) ^ (cu.scu_name ^ ("\n" (* ^ indent ^ "cupdie:" ^ pp_cupdie3 cu.scu_cupdie ^ "\n"*) - ^ (indent ^ ("vars:" ^ (pp_sdt_list cu.scu_vars (pp_sdt_concise_variable_or_formal_parameter (Nat_big_num.add level(Nat_big_num.of_int 1))) - ^ (indent ^ ("subroutines :" ^ pp_sdt_list cu.scu_subroutines (locals__subroutine (Nat_big_num.add level(Nat_big_num.of_int 1)))))))))))) + ^ (indent ^ ("vars:" ^ (pp_sdt_list cu.scu_vars (pp_sdt_concise_variable_or_formal_parameter (Sym.add level(Sym.of_int 1))) + ^ (indent ^ ("subroutines :" ^ pp_sdt_list cu.scu_subroutines (locals__subroutine (Sym.add level(Sym.of_int 1)))))))))))) *) let locals_dwarf (sdt_d : Dwarf.sdt_dwarf) : (Dwarf.sdt_variable_or_formal_parameter * string list) (*context*) list = @@ -246,7 +246,7 @@ let pp_ranged_var (prefix : string) (var : ranged_var) : string = let ((n1, n2, ops), (svfp, context)) = var in prefix ^ pp_sdt_concise_variable_or_formal_parameter_main 0 svfp - ^ (let s = Dwarf.pp_parsed_single_location_description (Nat_big_num.of_int 0) (n1, n2, ops) in + ^ (let s = Dwarf.pp_parsed_single_location_description (Sym.of_int 0) (n1, n2, ops) in String.sub s 0 (String.length s - 1)) (*hackish stripping of trailing \n from linksem - TODO: fix linksem interface*) ^ " " @@ -299,14 +299,14 @@ let mk_ranged_vars_at_instructions (sdt_d : Dwarf.sdt_dwarf) instructions : if k >= size then () else let addr = instructions.(k).i_addr in - if not (Nat_big_num.less addr_prev addr) then + if not (Sym.less addr_prev addr) then fatal "mk_ranged_vars_at_instructions found non-increasing address %s" (pp_addr addr); let (still_current, old) = - List.partition (function ((_, n2, _), _) -> Nat_big_num.less addr n2) prev + List.partition (function ((_, n2, _), _) -> Sym.less addr n2) prev in let (new', remaining') = partition_first - (function ((n1, _n2, _ops), _var) as _rv -> Nat_big_num.greater_equal addr n1) + (function ((n1, _n2, _ops), _var) as _rv -> Sym.greater_equal addr n1) remaining in (* TODO: do we need to drop any that have been totally skipped over? *) @@ -317,7 +317,7 @@ let mk_ranged_vars_at_instructions (sdt_d : Dwarf.sdt_dwarf) instructions : rvai_remaining.(k) <- remaining'; f addr current remaining' (k + 1) in - f (Nat_big_num.of_int (0 - 1)) [] locals_by_pc_ranges 0; + f (Sym.of_int (0 - 1)) [] locals_by_pc_ranges 0; { rvai_globals = globals_dwarf sdt_d; diff --git a/src/analyse/Elf.ml b/src/analyse/Elf.ml index 4b4bfc9b..0734c459 100644 --- a/src/analyse/Elf.ml +++ b/src/analyse/Elf.ml @@ -58,8 +58,8 @@ let pp_symbol_map (symbol_map : Elf_file.global_symbol_init_info) = String.concat "" (List.map (fun (name, (typ, _size, address, _mb, _binding)) -> - Printf.sprintf "**** name = %s address = %s typ = %d\n" name (pp_addr address) - (Nat_big_num.to_int typ)) + Printf.sprintf "**** name = %s address = %s typ = %d\n" name (pp_addr (Dwarf.Absolute address)) + (Sym.to_int (Dwarf.Absolute typ))) symbol_map) (*****************************************************************************) diff --git a/src/analyse/ElfSymbols.ml b/src/analyse/ElfSymbols.ml index e74f7aeb..83a526ea 100644 --- a/src/analyse/ElfSymbols.ml +++ b/src/analyse/ElfSymbols.ml @@ -54,7 +54,7 @@ open ControlFlowTypes let elf_symbols_of_address (test : test) (addr : natural) : string list = List.filter_map (fun (name, (_typ, _size, address, _mb, _binding)) -> - if address = addr then Some name else None) + if Dwarf.Absolute address = addr then Some name else None) test.symbol_map let mk_elf_symbols test instructions : string list array = @@ -62,5 +62,5 @@ let mk_elf_symbols test instructions : string list array = let address_of_elf_symbol test (s : string) : addr option = List.find_map - (fun (name, (_typ, _size, address, _mb, _binding)) -> if s = name then Some address else None) + (fun (name, (_typ, _size, address, _mb, _binding)) -> if s = name then Some (Dwarf.Absolute address) else None) test.symbol_map diff --git a/src/analyse/Pp.ml b/src/analyse/Pp.ml index d9c749c3..87ce1bec 100644 --- a/src/analyse/Pp.ml +++ b/src/analyse/Pp.ml @@ -574,7 +574,7 @@ let pp_instructions_ranged m test an (low, high) = Printf.printf "pp_instructions_ranged indices: low=%i high=%i \n" (an.index_of_address low) (an.index_of_address high); *) let index_low = an.index_of_address low in - let index_high = (an.index_of_address (Nat_big_num.sub high (Nat_big_num.of_int 4)))+1 in + let index_high = (an.index_of_address (Sym.sub high (Sym.of_int 4)))+1 in let rec subarray_map_to_list f a k k' = if k >= k' then [] else f k a.(k) :: subarray_map_to_list f a (k + 1) k' in @@ -619,7 +619,7 @@ let chunks_of_ranged_cu m test an filename_stem ((low, high), cu) = pp_abbreviations_table cu'.cu_abbreviations_table ); ( "die", ".debug_info die tree", - pp_die c cu'.cu_header d.d_str true (*indent*) (Nat_big_num.of_int 0) true cu'.cu_die ); + pp_die c cu'.cu_header d.d_str true (*indent*) (Sym.of_int 0) true cu'.cu_die ); ( "line", ".debug_line line number info", let lnp = line_number_program_of_compilation_unit d cu' in @@ -628,13 +628,13 @@ let chunks_of_ranged_cu m test an filename_stem ((low, high), cu) = ".debug_line evaluated line info", let lnrs = evaluated_line_info_of_compilation_unit d cu' ds.ds_evaluated_line_info in pp_line_number_registerss lnrs ); - ("sdt", "simple die tree", pp_sdt_compilation_unit (Nat_big_num.of_int 0) cu); + ("sdt", "simple die tree", pp_sdt_compilation_unit (Sym.of_int 0) cu); ( "sdt_globals", "simple die tree globals", - pp_sdt_globals_compilation_unit (Nat_big_num.of_int 0) cu ); + pp_sdt_globals_compilation_unit (Sym.of_int 0) cu ); ( "sdt_locals", "simple die tree locals", - pp_sdt_locals_compilation_unit (Nat_big_num.of_int 0) cu ); + pp_sdt_locals_compilation_unit (Sym.of_int 0) cu ); ("inlined", "inlined subroutine info", pp_inlined_subroutines ds iss); ( "inlined_by_range", "inlined subroutine info by range", diff --git a/src/analyse/QemuLog.ml b/src/analyse/QemuLog.ml index 9591bef5..e25a65c0 100644 --- a/src/analyse/QemuLog.ml +++ b/src/analyse/QemuLog.ml @@ -64,8 +64,8 @@ let read_qemu_log an filename_qemu_log : bool array = (* Printf.printf "%s " s;*) match Scanf.sscanf s "0x%x:%s" (fun addr _ -> addr) with | addr -> - (*Printf.printf "PARSED %s\n" (pp_addr (Nat_big_num.of_int addr));*) - Some (Nat_big_num.of_int addr) + (*Printf.printf "PARSED %s\n" (pp_addr (Sym.of_int addr));*) + Some (Sym.of_int addr) | exception _ -> (*Printf.printf "NOT\n";*) None in List.filter_map parse_line (Array.to_list lines) diff --git a/src/analyse/Utils.ml b/src/analyse/Utils.ml index cf316505..bcd5a717 100644 --- a/src/analyse/Utils.ml +++ b/src/analyse/Utils.ml @@ -49,13 +49,18 @@ open Logs.Logger (struct end) (** TODO: Maybe just use Z.t everywhere (it's shorter) *) -type natural = Nat_big_num.num +type natural = Sym.t (** machine address *) type addr = natural (* hackishly mask out bigint conversion failure *) -let pp_addr (a : natural) = try Ml_bindings.hex_string_of_big_int_pad8 a with Failure s -> let s' = "Failure: int64_of_big_int " ^ Nat_big_num.to_string a in (warn "pp_addr failure: %s" s); s'| e -> raise e +let pp_addr (a : natural) = + try + Dwarf.pp_sym Ml_bindings.hex_string_of_big_int_pad8 a + with + | Failure s -> let s' = "Failure: int64_of_big_int " ^ Sym.to_string a in (warn "pp_addr failure: %s" s); s' + | e -> raise e (** index into instruction-indexed arrays *) type index = int diff --git a/src/utils/sym.ml b/src/utils/sym.ml index bb2ecd56..3f584582 100644 --- a/src/utils/sym.ml +++ b/src/utils/sym.ml @@ -1,3 +1,18 @@ type t = Z.t Dwarf.sym0 -let to_int x = Z.to_int @@ Dwarf.sym_unwrap x "to_int" \ No newline at end of file +let to_int x = Z.to_int @@ Dwarf.sym_unwrap x "to_int" + +let of_int x = Dwarf.Absolute (Z.of_int x) + +let equal = Dwarf.sym_comp Nat_big_num.equal +let less = Dwarf.sym_comp Nat_big_num.less +let less_equal = Dwarf.sym_comp Nat_big_num.less_equal +let greater = Dwarf.sym_comp Nat_big_num.greater +let greater_equal = Dwarf.sym_comp Nat_big_num.greater_equal + +let to_string = Dwarf.pp_sym Z.to_string + +let sub x y = match (x, y) with +| (Dwarf.Offset (s, a), Dwarf.Offset (t, b)) when s = t -> Nat_big_num.sub a b +| (Dwarf.Absolute a, Dwarf.Absolute b) -> Nat_big_num.sub a b +| _ -> failwith "Can't compare" \ No newline at end of file From 14242979a366c234f8dd7fe3f598b12cbb3abe18 Mon Sep 17 00:00:00 2001 From: Matej Urban Date: Mon, 27 Jan 2025 13:46:23 +0000 Subject: [PATCH 040/116] [wip] symbolic analyse --- src/analyse/ControlFlow.ml | 2 +- src/analyse/ControlFlowPpDot.ml | 6 +- src/analyse/DwarfVarInfo.ml | 2 +- src/analyse/Elf.ml | 98 +++++++++++++++------------------ src/analyse/ElfSymbols.ml | 4 +- src/analyse/ElfTypes.ml | 3 +- src/analyse/Pp.ml | 8 +-- src/analyse/Symbols.ml | 70 +++++++++++++++++++++++ src/bin/copySources.ml | 2 +- src/elf/file.ml | 2 +- src/utils/sym.ml | 22 +++++++- 11 files changed, 148 insertions(+), 71 deletions(-) create mode 100644 src/analyse/Symbols.ml diff --git a/src/analyse/ControlFlow.ml b/src/analyse/ControlFlow.ml index 89bc11f6..578279cf 100644 --- a/src/analyse/ControlFlow.ml +++ b/src/analyse/ControlFlow.ml @@ -142,7 +142,7 @@ let branch_table_target_addresses test filename_branch_table_option : (addr * ad let rodata_words : (natural * natural) list = Dwarf.words_of_rel_byte_sequence rodata_addr (Dwarf.rbs_no_reloc bs) [] in (*HACK*) let read_rodata_b addr = - Elf_types_native_uint.natural_of_byte + Dwarf.sym_natural_of_byte rodata_bytes.(Sym.to_int (Sym.sub addr rodata_addr)) in let read_rodata_h addr = diff --git a/src/analyse/ControlFlowPpDot.ml b/src/analyse/ControlFlowPpDot.ml index 77375477..123c0c04 100644 --- a/src/analyse/ControlFlowPpDot.ml +++ b/src/analyse/ControlFlowPpDot.ml @@ -204,7 +204,7 @@ let inlining_stack_at_index (an : analysis) k = (function | sss' -> String.concat "\n---\n" - (List.map (Dwarf.pp_sdt_subroutine (Sym.of_int 0)) sss')) + (List.map (Dwarf.pp_sdt_subroutine (Nat_big_num.of_int 0)) sss')) maximal)); [] @@ -859,12 +859,12 @@ let mk_cfg test an visitedo node_name_prefix (recurse_flat : bool) (_inline_all match new_ss_O2_ambient_option with | None -> fatal "no call site for\n%s" - (Dwarf.pp_sdt_subroutine (Sym.of_int 0) ss_current) + (Dwarf.pp_sdt_subroutine (Nat_big_num.of_int 0) ss_current) | Some new_ss_O2_ambient -> ( match new_ss_O2_ambient.ss_call_site with | None -> fatal "no call site2 for\n%s" - (Dwarf.pp_sdt_subroutine (Sym.of_int 0) ss_current) + (Dwarf.pp_sdt_subroutine (Nat_big_num.of_int 0) ss_current) | Some (_ufe, line, _subprogram_name) -> line ) (*, diff --git a/src/analyse/DwarfVarInfo.ml b/src/analyse/DwarfVarInfo.ml index 54e2964f..144e48b5 100644 --- a/src/analyse/DwarfVarInfo.ml +++ b/src/analyse/DwarfVarInfo.ml @@ -246,7 +246,7 @@ let pp_ranged_var (prefix : string) (var : ranged_var) : string = let ((n1, n2, ops), (svfp, context)) = var in prefix ^ pp_sdt_concise_variable_or_formal_parameter_main 0 svfp - ^ (let s = Dwarf.pp_parsed_single_location_description (Sym.of_int 0) (n1, n2, ops) in + ^ (let s = Dwarf.pp_parsed_single_location_description (Nat_big_num.of_int 0) (n1, n2, ops) in String.sub s 0 (String.length s - 1)) (*hackish stripping of trailing \n from linksem - TODO: fix linksem interface*) ^ " " diff --git a/src/analyse/Elf.ml b/src/analyse/Elf.ml index 0734c459..5b3bb63c 100644 --- a/src/analyse/Elf.ml +++ b/src/analyse/Elf.ml @@ -69,20 +69,23 @@ let pp_symbol_map (symbol_map : Elf_file.global_symbol_init_info) = let parse_elf_file (filename : string) : test = (* call ELF analyser on file *) - let info = Sail_interface.populate_and_obtain_global_symbol_init_info filename in - - let ( (elf_file : Elf_file.elf_file), - (elf_epi : Sail_interface.executable_process_image), - (symbol_map : Elf_file.global_symbol_init_info) ) = - match info with - | Error.Fail s -> fatal "populate_and_obtain_global_symbol_init_info: %s" s + let bs = match Byte_sequence.acquire filename with + | Error.Fail s -> fatal "Linksem: Byte_sequence.acquire: %s" s | Error.Success x -> x in - - let f64 = - match elf_file with Elf_file.ELF_File_64 f -> f | _ -> raise (Failure "not Elf64") + let f64 = match Elf_file.read_elf64_file bs with + | Error.Fail s -> fatal "Linksem: read_elf64_file: %s" s + | Error.Success x -> x in + let symbol_map = match Symbols.get_elf64_file_global_symbol_init f64 with + | Error.Fail s -> fatal "LinksemRelocatable: get_elf64_file_global_symbol_init: %s" s + | Error.Success x -> x + in + + let elf_file = Elf_file.ELF_File_64 f64 in + let entry = f64.elf64_file_header.elf64_entry in + let machine = f64.elf64_file_header.elf64_machine in (* linksem main_elf --symbols looks ok for gcc and clang That uses Elf_file.read_elf64_file bs0 >>= fun f1 -> @@ -125,49 +128,38 @@ let parse_elf_file (filename : string) : test = *) (* Debug.print_string "elf segments etc\n";*) - match (elf_epi, elf_file) with - | (Sail_interface.ELF_Class_32 _, _) -> fatal "%s" "cannot handle ELF_Class_32" - | (_, Elf_file.ELF_File_32 _) -> fatal "%s" "cannot handle ELF_File_32" - | (Sail_interface.ELF_Class_64 (segments, e_entry, e_machine), Elf_file.ELF_File_64 f1) -> - (* architectures from linksem elf_header.lem *) - let arch = - if f64.elf64_file_header.elf64_machine = Elf_header.elf_ma_aarch64 then AArch64 - else if f64.elf64_file_header.elf64_machine = Elf_header.elf_ma_x86_64 then X86 - else fatal "unrecognised ELF file architecture" - in - - (* remove all the auto generated segments (they contain only 0s) *) - let segments = - Lem_list.mapMaybe - (fun (seg, prov) -> if prov = Elf_file.FromELF then Some seg else None) - segments - in - let ds = - match Dwarf.extract_dwarf_static (Elf_file.ELF_File_64 f1) Abi_aarch64_symbolic_relocation.aarch64_data_relocation_interpreter with - | None -> fatal "%s" "extract_dwarf_static failed" - | Some ds -> - (* Debug.print_string2 (Dwarf.pp_analysed_location_data ds.Dwarf.ds_dwarf - ds.Dwarf.ds_analysed_location_data); - Debug.print_string2 (Dwarf.pp_evaluated_frame_info - ds.Dwarf.ds_evaluated_frame_info);*) - ds - in - let dwarf_semi_pp_frame_info = - Dwarf.semi_pp_evaluated_frame_info ds.ds_evaluated_frame_info - in - let test = - { - elf_file; - arch; - symbol_map (*@ (symbols_for_stacks !Globals.elf_threads)*); - segments; - e_entry; - e_machine; - dwarf_static = ds; - dwarf_semi_pp_frame_info; - } - in - test + (* architectures from linksem elf_header.lem *) + let arch = + if f64.elf64_file_header.elf64_machine = Elf_header.elf_ma_aarch64 then AArch64 + else if f64.elf64_file_header.elf64_machine = Elf_header.elf_ma_x86_64 then X86 + else fatal "unrecognised ELF file architecture" + in + + let ds = + match Dwarf.extract_dwarf_static (Elf_file.ELF_File_64 f64) Abi_aarch64_symbolic_relocation.aarch64_data_relocation_interpreter with + | None -> fatal "%s" "extract_dwarf_static failed" + | Some ds -> + (* Debug.print_string2 (Dwarf.pp_analysed_location_data ds.Dwarf.ds_dwarf + ds.Dwarf.ds_analysed_location_data); + Debug.print_string2 (Dwarf.pp_evaluated_frame_info + ds.Dwarf.ds_evaluated_frame_info);*) + ds + in + let dwarf_semi_pp_frame_info = + Dwarf.semi_pp_evaluated_frame_info ds.ds_evaluated_frame_info + in + let test = + { + elf_file; + arch; + symbol_map (*@ (symbols_for_stacks !Globals.elf_threads)*); + e_entry = Dwarf.Absolute (entry); + e_machine = Dwarf.Absolute (machine); + dwarf_static = ds; + dwarf_semi_pp_frame_info; + } + in + test (*****************************************************************************) (** marshal and unmarshal test *) diff --git a/src/analyse/ElfSymbols.ml b/src/analyse/ElfSymbols.ml index 83a526ea..e74f7aeb 100644 --- a/src/analyse/ElfSymbols.ml +++ b/src/analyse/ElfSymbols.ml @@ -54,7 +54,7 @@ open ControlFlowTypes let elf_symbols_of_address (test : test) (addr : natural) : string list = List.filter_map (fun (name, (_typ, _size, address, _mb, _binding)) -> - if Dwarf.Absolute address = addr then Some name else None) + if address = addr then Some name else None) test.symbol_map let mk_elf_symbols test instructions : string list array = @@ -62,5 +62,5 @@ let mk_elf_symbols test instructions : string list array = let address_of_elf_symbol test (s : string) : addr option = List.find_map - (fun (name, (_typ, _size, address, _mb, _binding)) -> if s = name then Some (Dwarf.Absolute address) else None) + (fun (name, (_typ, _size, address, _mb, _binding)) -> if s = name then Some address else None) test.symbol_map diff --git a/src/analyse/ElfTypes.ml b/src/analyse/ElfTypes.ml index a0795eb0..005a7ba6 100644 --- a/src/analyse/ElfTypes.ml +++ b/src/analyse/ElfTypes.ml @@ -62,8 +62,7 @@ type architecture = 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; + symbol_map : Symbols.global_symbol_init_info; e_entry : natural; e_machine : natural; dwarf_static : Dwarf.dwarf_static; diff --git a/src/analyse/Pp.ml b/src/analyse/Pp.ml index 87ce1bec..9dbf2b7a 100644 --- a/src/analyse/Pp.ml +++ b/src/analyse/Pp.ml @@ -619,7 +619,7 @@ let chunks_of_ranged_cu m test an filename_stem ((low, high), cu) = pp_abbreviations_table cu'.cu_abbreviations_table ); ( "die", ".debug_info die tree", - pp_die c cu'.cu_header d.d_str true (*indent*) (Sym.of_int 0) true cu'.cu_die ); + pp_die c cu'.cu_header d.d_str true (*indent*) (Nat_big_num.of_int 0) true cu'.cu_die ); ( "line", ".debug_line line number info", let lnp = line_number_program_of_compilation_unit d cu' in @@ -628,13 +628,13 @@ let chunks_of_ranged_cu m test an filename_stem ((low, high), cu) = ".debug_line evaluated line info", let lnrs = evaluated_line_info_of_compilation_unit d cu' ds.ds_evaluated_line_info in pp_line_number_registerss lnrs ); - ("sdt", "simple die tree", pp_sdt_compilation_unit (Sym.of_int 0) cu); + ("sdt", "simple die tree", pp_sdt_compilation_unit (Nat_big_num.of_int 0) cu); ( "sdt_globals", "simple die tree globals", - pp_sdt_globals_compilation_unit (Sym.of_int 0) cu ); + pp_sdt_globals_compilation_unit (Nat_big_num.of_int 0) cu ); ( "sdt_locals", "simple die tree locals", - pp_sdt_locals_compilation_unit (Sym.of_int 0) cu ); + pp_sdt_locals_compilation_unit (Nat_big_num.of_int 0) cu ); ("inlined", "inlined subroutine info", pp_inlined_subroutines ds iss); ( "inlined_by_range", "inlined subroutine info by range", diff --git a/src/analyse/Symbols.ml b/src/analyse/Symbols.ml new file mode 100644 index 00000000..360ffbf6 --- /dev/null +++ b/src/analyse/Symbols.ml @@ -0,0 +1,70 @@ +(* TODO header *) + +module SMap = Map.Make (String) + +type rels = + | AArch64 of (Z.t, Abi_aarch64_symbolic_relocation.aarch64_relocation_target Elf_symbolic.abstract_relocation) Pmap.map + +type sym_data = +Byte_sequence_wrapper.byte_sequence * rels + + +(* Like in linksem, but address is section+offset, data has relocations and with a writable flag *) +type symbol = string * (Z.t * Z.t * Sym.t * sym_data * Z.t) + +type global_symbol_init_info = symbol list + +open Elf_symbol_table +open Elf_interpreted_section + +let get_elf64_file_global_symbol_init (f: Elf_file.elf64_file) : global_symbol_init_info Error.error = + let secs = f.elf64_file_interpreted_sections in + let machine = f.elf64_file_header.elf64_machine in + Error.bind (Elf_file.get_elf64_file_symbol_table f) @@ fun (symtab, strtab) -> + let rel_cache = ref SMap.empty in + let get_relocs section = + match SMap.find_opt section !rel_cache with + | Some rels -> rels + | None -> + if machine = Elf_header.elf_ma_aarch64 then + Error.bind + (Elf_symbolic.extract_elf64_relocations_for_section f Abi_aarch64_symbolic_relocation.abi_aarch64_relocation_to_abstract section) + @@ fun relocs -> Error.return (AArch64 relocs) + else + Error.fail @@ "machine not supported " ^ (Elf_header.string_of_elf_machine_architecture machine) + in + List.filter_map ( + fun entry -> + let name = Uint32_wrapper.to_bigint entry.elf64_st_name in + let addr_offset = Uint64_wrapper.to_bigint entry.elf64_st_value in + let size = Uint64_wrapper.to_bigint entry.elf64_st_size in + let shndx = Uint32_wrapper.to_int entry.elf64_st_shndx in + let typ = Elf_symbol_table.extract_symbol_type entry.elf64_st_info in + let bnd = Elf_symbol_table.extract_symbol_binding entry.elf64_st_info in + Option.map ( + fun section -> + let addr = Dwarf.Offset (section.elf64_section_name_as_string, addr_offset) in + let data = if Byte_sequence.length0 section.elf64_section_body = Z.zero then + Error.return (Byte_sequence.zeros size) + else + Byte_sequence.offset_and_cut addr_offset size section.elf64_section_body + in + Error.bind (get_relocs section.elf64_section_name_as_string) @@ fun (AArch64 relocs) -> + let relocs = relocs + |> Pmap.bindings_list + |> List.fold_left (fun m (pos, r) -> + let sz = size in + let open Z in + let open Compare in + if pos >= addr_offset && pos < addr_offset + sz then + Pmap.add (pos - addr_offset) r m + else + m + ) (Pmap.empty Z.compare) + |> fun x -> AArch64 x + in + Error.bind data @@ fun data -> + Error.bind (String_table.get_string_at name strtab) @@ fun str -> + Error.return (str, (typ, size, addr, (data, relocs), bnd)) + ) (List.nth_opt secs shndx) + ) symtab |> Error.mapM Fun.id \ No newline at end of file diff --git a/src/bin/copySources.ml b/src/bin/copySources.ml index 094d3257..dc30fd01 100644 --- a/src/bin/copySources.ml +++ b/src/bin/copySources.ml @@ -79,7 +79,7 @@ let process_file () : unit = (function | lnfe -> ( lnh.lnh_comp_dir, - (let dir = Nat_big_num.to_int lnfe.lnfe_directory_index in + (let dir = Sym.to_int lnfe.lnfe_directory_index in if dir = 0 then None else Some diff --git a/src/elf/file.ml b/src/elf/file.ml index e3d8925d..1b2f39fc 100644 --- a/src/elf/file.ml +++ b/src/elf/file.ml @@ -145,7 +145,7 @@ let of_file (filename : string) = Segment. { data; - addr = Nat_big_num.to_int addr; + addr = Sym.to_int addr; (* TODO *) size = BytesSeq.length data; read = true; write = false; diff --git a/src/utils/sym.ml b/src/utils/sym.ml index 3f584582..910c0b1c 100644 --- a/src/utils/sym.ml +++ b/src/utils/sym.ml @@ -3,16 +3,32 @@ type t = Z.t Dwarf.sym0 let to_int x = Z.to_int @@ Dwarf.sym_unwrap x "to_int" let of_int x = Dwarf.Absolute (Z.of_int x) +let of_int64 x = Dwarf.Absolute (Z.of_int64 x) let equal = Dwarf.sym_comp Nat_big_num.equal let less = Dwarf.sym_comp Nat_big_num.less let less_equal = Dwarf.sym_comp Nat_big_num.less_equal let greater = Dwarf.sym_comp Nat_big_num.greater let greater_equal = Dwarf.sym_comp Nat_big_num.greater_equal +let compare = Dwarf.sym_comp Nat_big_num.compare let to_string = Dwarf.pp_sym Z.to_string let sub x y = match (x, y) with -| (Dwarf.Offset (s, a), Dwarf.Offset (t, b)) when s = t -> Nat_big_num.sub a b -| (Dwarf.Absolute a, Dwarf.Absolute b) -> Nat_big_num.sub a b -| _ -> failwith "Can't compare" \ No newline at end of file +| (Dwarf.Offset (s, a), Dwarf.Offset (t, b)) when s = t -> Dwarf.Absolute (Nat_big_num.sub a b) +| (Dwarf.Absolute a, Dwarf.Absolute b) -> Dwarf.Absolute (Nat_big_num.sub a b) +| _ -> Dwarf.Unknown + +let add x y = match (x, y) with +| (Dwarf.Offset (s, a), Dwarf.Absolute b) -> Dwarf.Offset (s, Nat_big_num.add a b) +| (Dwarf.Absolute (a), Dwarf.Offset (s,b)) -> Dwarf.Offset (s, Nat_big_num.add a b) +| (Dwarf.Absolute a, Dwarf.Absolute b) -> Dwarf.Absolute (Nat_big_num.add a b) +| _ -> Dwarf.Unknown + +let mul = Dwarf.sym_map2 Nat_big_num.mul + +let pow_int_positive x y = Dwarf.Absolute (Nat_big_num.pow_int_positive x y) + +let shift_left x s = Dwarf.sym_map (fun x -> Nat_big_num.shift_left x s) x +let shift_right x s = Dwarf.sym_map (fun x -> Nat_big_num.shift_right x s) x +let modulus = Dwarf.sym_map2 Nat_big_num.modulus \ No newline at end of file From 1d1611ff45d7b1b3b76e788ee55a8bec8a5f25a5 Mon Sep 17 00:00:00 2001 From: Matej Urban Date: Mon, 27 Jan 2025 18:16:33 +0000 Subject: [PATCH 041/116] [wip] symbolic analyse --- src/analyse/ControlFlow.ml | 35 ++++++++++++++++++++++++----------- src/analyse/DwarfLineInfo.ml | 3 ++- src/analyse/Pp.ml | 7 +++---- src/utils/sym.ml | 24 ++++++++++++++++++++++-- 4 files changed, 51 insertions(+), 18 deletions(-) diff --git a/src/analyse/ControlFlow.ml b/src/analyse/ControlFlow.ml index 578279cf..2bcc4c4b 100644 --- a/src/analyse/ControlFlow.ml +++ b/src/analyse/ControlFlow.ml @@ -440,12 +440,21 @@ AArch64: let objdump_line_regexp = Str.regexp " *\\([0-9a-fA-F]+\\):[ \t]\\([0-9a-fA-F ]+\\)\t\\([^ \r\t\n]+\\) *\\(.*\\)$" +let section_start_line_regexp = + Str.regexp "Disassembly of section \\(.*\\):$" + type objdump_instruction = natural (*address*) * int list (*opcode bytes*) * string (*mnemonic*) * string (*args etc*) -let parse_objdump_line (s : string) : objdump_instruction option = +let parse_section_start s = + if Str.string_match section_start_line_regexp s 0 then + Some (Str.matched_group 1 s) + else + None + +let parse_objdump_line (s : string) : (int64 * int list * string * string) option = let parse_hex_int64 s' = try Scanf.sscanf s' "%Lx" (fun i64 -> i64) with _ -> fatal "cannot parse address in objdump line %s\n" s @@ -465,7 +474,6 @@ let parse_objdump_line (s : string) : objdump_instruction option = if Str.string_match objdump_line_regexp s 0 then begin let addr_int64 = parse_hex_int64 (Str.matched_group 1 s) in - let addr = Sym.of_int64 addr_int64 in let op = Str.matched_group 2 s in let op = strip_whitespace op in let opcode_byte_strings = @@ -477,7 +485,7 @@ let parse_objdump_line (s : string) : objdump_instruction option = let opcode_bytes = List.map parse_hex_int opcode_byte_strings in let mnemonic = Str.matched_group 3 s in let operands = Str.matched_group 4 s in - Some (addr, opcode_bytes, mnemonic, operands) + Some (addr_int64, opcode_bytes, mnemonic, operands) end else None @@ -486,30 +494,35 @@ let parse_objdump_lines arch lines : objdump_instruction list = List.filter_map (parse_objdump_line arch) (Array.to_list lines) *) -let rec parse_objdump_lines arch lines (next_index : int) (last_address : natural option) : +let with_symbolic_address (section: string) (addr, opcode_bytes, mnemonic, operands) : objdump_instruction = + (Dwarf.Offset (section, Nat_big_num.of_int64 addr), opcode_bytes, mnemonic, operands) + +let rec parse_objdump_lines arch lines (next_index : int) (last_address : int64 option) (section: string option) : objdump_instruction list = if next_index >= Array.length lines then [] else + let section = Option.fold ~none:section ~some:Option.some @@ parse_section_start lines.(next_index) in match parse_objdump_line lines.(next_index) with (* skip over unparseable lines *) - | None -> parse_objdump_lines arch lines (next_index + 1) last_address + | None -> parse_objdump_lines arch lines (next_index + 1) last_address section | Some ((addr, _opcode_bytes, _mnemonic, _operands) as i) -> ( + let mki = with_symbolic_address (Option.get section) in match last_address with - | None -> i :: parse_objdump_lines arch lines (next_index + 1) (Some addr) + | None -> mki i :: parse_objdump_lines arch lines (next_index + 1) (Some addr) section | Some last_address' -> - let last_address'' = Sym.add last_address' (Sym.of_int 4) in + let last_address'' = Int64.add last_address' (Int64.of_int 4) in if addr > last_address'' then (* fake up "missing" instructions for any gaps in the address space*) (*warn "gap in objdump instruction address sequence at %s" (pp_addr last_address'');*) - (last_address'', [], "missing", "") - :: parse_objdump_lines arch lines next_index (Some last_address'') - else i :: parse_objdump_lines arch lines (next_index + 1) (Some addr) + mki (last_address'', [], "missing", "") + :: parse_objdump_lines arch lines next_index (Some last_address'') section + else mki i :: parse_objdump_lines arch lines (next_index + 1) (Some addr) section ) let parse_objdump_file arch filename_objdump_d : objdump_instruction array = match read_file_lines filename_objdump_d with | Error s -> fatal "%s\ncouldn't read objdump-d file: \"%s\"\n" s filename_objdump_d - | Ok lines -> Array.of_list (parse_objdump_lines arch lines 0 None) + | Ok lines -> Array.of_list (parse_objdump_lines arch lines 0 None None) (*****************************************************************************) (** parse control-flow instruction asm from objdump and branch table data *) diff --git a/src/analyse/DwarfLineInfo.ml b/src/analyse/DwarfLineInfo.ml index b3561313..7cb6bf73 100644 --- a/src/analyse/DwarfLineInfo.ml +++ b/src/analyse/DwarfLineInfo.ml @@ -136,6 +136,7 @@ let split_into_sequences | None -> fatal "split_into_sequences found sequence of length 0" in let last = lnr.lnr_address in + (* print_endline (Dwarf.pphex_sym first ^ " " ^ Dwarf.pphex_sym last); *) if Sym.equal first last then fatal "split_into_sequences found first=last" else (); let elis = @@ -293,7 +294,7 @@ let mk_line_info (eli : Dwarf.evaluated_line_info) instructions : let (new_perhaps_relevant, remaining') = mk_new_perhaps_relevant [] remaining_entries in let addr_in elie = - Sym.less_equal elie.elie_first addr && Sym.less_equal addr elie.elie_last + Sym.in_range elie.elie_first elie.elie_last addr in let still_active_entries = diff --git a/src/analyse/Pp.ml b/src/analyse/Pp.ml index 9dbf2b7a..0bf41866 100644 --- a/src/analyse/Pp.ml +++ b/src/analyse/Pp.ml @@ -857,7 +857,6 @@ let pp_test_analysis m test an = call_graph ^ "* ************* transitive call graph **************\n" ^ transitive_call_graph | Html -> - "" - (* "\n* ************* instructions *****************\n" *) - (*pp_instruction_init (); - String.concat "" (Array.to_list (Array.mapi (pp_instruction m test an 0) an.instructions))*) + "\n* ************* instructions *****************\n" + ^ (pp_instruction_init (); + String.concat "" (Array.to_list (Array.mapi (pp_instruction m test an 0) an.instructions))) diff --git a/src/utils/sym.ml b/src/utils/sym.ml index 910c0b1c..839f5c96 100644 --- a/src/utils/sym.ml +++ b/src/utils/sym.ml @@ -1,12 +1,25 @@ type t = Z.t Dwarf.sym0 +let pp x = x |> Dwarf.pphex_sym |> Pp.string + let to_int x = Z.to_int @@ Dwarf.sym_unwrap x "to_int" let of_int x = Dwarf.Absolute (Z.of_int x) let of_int64 x = Dwarf.Absolute (Z.of_int64 x) let equal = Dwarf.sym_comp Nat_big_num.equal -let less = Dwarf.sym_comp Nat_big_num.less + +let max_addr = Z.(shift_left (of_int 1) 64 - (of_int 1)) + +let min_addr = Z.of_int 0 + +(* TODO very hacky *) +let less x y = match (x, y) with +| (Dwarf.Absolute x, Dwarf.Offset (_, y)) when Nat_big_num.less x y -> true +| (Dwarf.Absolute x, Dwarf.Offset (_,_)) when Nat_big_num.greater_equal x max_addr -> false +| (Dwarf.Offset (_,_), Dwarf.Absolute y) when Nat_big_num.less max_addr y -> true +| (Dwarf.Offset (_, x), Dwarf.Absolute y) when Nat_big_num.greater_equal x y -> false +| _ -> Dwarf.sym_comp Nat_big_num.less x y let less_equal = Dwarf.sym_comp Nat_big_num.less_equal let greater = Dwarf.sym_comp Nat_big_num.greater let greater_equal = Dwarf.sym_comp Nat_big_num.greater_equal @@ -16,6 +29,7 @@ let to_string = Dwarf.pp_sym Z.to_string let sub x y = match (x, y) with | (Dwarf.Offset (s, a), Dwarf.Offset (t, b)) when s = t -> Dwarf.Absolute (Nat_big_num.sub a b) +| (Dwarf.Offset (s, a), Dwarf.Absolute b) -> Dwarf.Offset (s, Nat_big_num.sub a b) | (Dwarf.Absolute a, Dwarf.Absolute b) -> Dwarf.Absolute (Nat_big_num.sub a b) | _ -> Dwarf.Unknown @@ -31,4 +45,10 @@ let pow_int_positive x y = Dwarf.Absolute (Nat_big_num.pow_int_positive x y) let shift_left x s = Dwarf.sym_map (fun x -> Nat_big_num.shift_left x s) x let shift_right x s = Dwarf.sym_map (fun x -> Nat_big_num.shift_right x s) x -let modulus = Dwarf.sym_map2 Nat_big_num.modulus \ No newline at end of file +let modulus = Dwarf.sym_map2 Nat_big_num.modulus + +let in_range first last x = match (first, last, x) with +| (Dwarf.Absolute f, Dwarf.Absolute l, Dwarf.Absolute x) -> Nat_big_num.less_equal f x && Nat_big_num.less_equal x l +| (Dwarf.Offset (s1, f), Dwarf.Offset (s2, l), Dwarf.Offset (s, x)) when s1 = s2 -> + s1 = s && Nat_big_num.less_equal f x && Nat_big_num.less_equal x l (* TODO kinda hacky *) +| _ -> Raise.fail "Can't determine if %t is in range [%t,%t]" (Pp.tos pp x) (Pp.tos pp first) (Pp.tos pp last) \ No newline at end of file From 04ee90ef6985ffc26f15d18f29611cd946146832 Mon Sep 17 00:00:00 2001 From: Matej Urban Date: Mon, 27 Jan 2025 23:53:21 +0000 Subject: [PATCH 042/116] Fix parsing objdump --- src/analyse/ControlFlow.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/analyse/ControlFlow.ml b/src/analyse/ControlFlow.ml index 2bcc4c4b..4c305098 100644 --- a/src/analyse/ControlFlow.ml +++ b/src/analyse/ControlFlow.ml @@ -291,7 +291,7 @@ let parse_addr (s : string) : natural = try Scanf.sscanf s "0x%Lx" (fun i64 -> Sym.of_int64 i64) with - Scanf.Scan_failure _ -> + (Scanf.Scan_failure _ | End_of_file) -> Scanf.sscanf s "%Lx" (fun i64 -> Sym.of_int64 i64) let parse_target s = From 5d010309d195e27241d7186e22a46c037e727012 Mon Sep 17 00:00:00 2001 From: Matej Urban Date: Thu, 30 Jan 2025 13:06:17 +0000 Subject: [PATCH 043/116] notes --- notes-TODO | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/notes-TODO b/notes-TODO index b1a58b67..95c382c0 100644 --- a/notes-TODO +++ b/notes-TODO @@ -6,4 +6,17 @@ Instruction fetch: is it sound? (rewriting .text) Z3 finding unique solution - Get model -> assert not model -> check now it is unsat -- Need to extend the protocol probably \ No newline at end of file +- Need to extend the protocol probably + +SIMREL +- state = (pc,M) (and registers TODO) +- (pc,M1) ~ (pc.M2) iff there is MT, such that + MT(A1,A2,sz) = T => M1[A1:A1+sz] =T M2[A2:A2+sz] + and respects dwarf at pc + and maybe some consistency of MT?? e.g. overlaping ranges +- relation (=T) defined using MT + - (=base_type) is equality + - (=struct) fieldwise + - (=*T): + A1 =*T A2 <=> MT(A1,A2,sz(T)) = T +- Hoare logic (or similar) with MT as variable (only read/write commands) From 65ebed5aca0933d92e983ca08b6fcab7e03d5db4 Mon Sep 17 00:00:00 2001 From: Matej Urban Date: Thu, 30 Jan 2025 13:10:22 +0000 Subject: [PATCH 044/116] set SCTLR_EL2 --- src/config/isla_aarch64.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/src/config/isla_aarch64.toml b/src/config/isla_aarch64.toml index 27d6e500..f762a682 100644 --- a/src/config/isla_aarch64.toml +++ b/src/config/isla_aarch64.toml @@ -141,6 +141,7 @@ ignore = [ # Bit 1 being unset allows unaligned accesses # Bit 26 being set allows cache-maintenance ops in EL0 "SCTLR_EL1" = "0x0000000004000000" +"SCTLR_EL2" = "0x0000000004000000" # A map from register names that may appear in litmus files to Sail # register names From d30207dc5a408771da53d1576d342e4cbe71cb21 Mon Sep 17 00:00:00 2001 From: Matej Urban Date: Thu, 30 Jan 2025 13:53:32 +0000 Subject: [PATCH 045/116] Make funcRD work --- src/bin/readDwarf.ml | 2 +- src/elf/address.ml | 2 ++ src/run/funcRD.ml | 38 +++++++++++++++++++++++--------------- 3 files changed, 26 insertions(+), 16 deletions(-) diff --git a/src/bin/readDwarf.ml b/src/bin/readDwarf.ml index 6be5927f..53e15618 100644 --- a/src/bin/readDwarf.ml +++ b/src/bin/readDwarf.ml @@ -71,7 +71,7 @@ let commands = Run.Func.command; Run.Instr.command; Run.Block.command; - (* Run.FuncRD.command; *) + Run.FuncRD.command; CopySourcesCmd.command; Z3.Test.command; ] diff --git a/src/elf/address.ml b/src/elf/address.ml index 701ddd89..f332f31a 100644 --- a/src/elf/address.ml +++ b/src/elf/address.ml @@ -22,3 +22,5 @@ let (>) = compare (>) let (<=) = compare (<=) let (>=) = compare (>=) + +let to_sym {section; offset} = Dwarf.Offset (section, Z.of_int offset) \ No newline at end of file diff --git a/src/run/funcRD.ml b/src/run/funcRD.ml index 374b7e9d..f2d1fffb 100644 --- a/src/run/funcRD.ml +++ b/src/run/funcRD.ml @@ -48,14 +48,18 @@ instructions.*) open Cmdliner -(* open Config.CommonOpt *) -(* open Fun *) +open Config.CommonOpt +open Fun open Logs.Logger (struct let str = __MODULE__ end) -(* let run_func_rd elfname name objdump_d branchtables breakpoints = +let run_func_rd elfname name objdump_d branchtables breakpoints = + match Analyse.Utils.read_file_lines "src/analyse/html-preamble-insts.html" with + | Error _ -> () + | Ok lines -> Array.iter (function s -> Printf.printf "%s\n" s) lines + ; base "Running with rd %s in %s" name elfname; base "Loading %s" elfname; let dwarf = Dw.of_file elfname in @@ -70,13 +74,13 @@ end) let abi = Arch.get_abi api in Trace.Cache.start @@ Arch.get_isla_config (); base "Computing entry state"; - let start = Init.state () |> State.copy ~elf |> abi.init in + let start = Init.state () |> State.copy ~elf |> State.init_sections ~addr_size:Arch.address_size |> abi.init in base "Loading %s for Analyse" elfname; let analyse_test = Analyse.Elf.parse_elf_file elfname in base "Analysing %s for Analyse" elfname; let analyse_analysis = Analyse.Collected.mk_analysis analyse_test objdump_d branchtables in let print_analyse_instruction pc = - let pc = Z.of_int pc in + let pc = Elf.Address.to_sym pc in let index = analyse_analysis.index_of_address pc in let instr = analyse_analysis.instructions.(index) in Analyse.Pp.pp_instruction Analyse.Types.Html (*Ascii*) analyse_test analyse_analysis 0 index @@ -98,7 +102,7 @@ end) let tree = Block_lib.run ~every_instruction:true block start in base "Ended running, start pretty printing"; (* This table will contain the state diff to print at each pc with a message *) - let instr_data : (int, string * State.t * State.Reg.t list) Hashtbl.t = + let instr_data : (Elf.Address.t, string * State.t * State.Reg.t list) Hashtbl.t = Hashtbl.create 100 in let get_footprint pc = @@ -113,7 +117,7 @@ end) let last_pc = st.last_pc in let last_instr_f = get_footprint last_pc in let s = - if last_pc <> pc - 4 then Printf.sprintf "Coming from 0x%x: " last_pc else "" + if Elf.Address.(last_pc + 4 <> pc) then Printf.sprintf "Coming from %t: " Pp.(tos Elf.Address.pp last_pc) else "" in let regs = List.merge_uniq Stdlib.compare cur_instr_f last_instr_f in Hashtbl.add instr_data pc (Printf.sprintf "%sBefore branch" s, st, regs) @@ -122,27 +126,31 @@ end) let last_pc = st.last_pc in let last_instr_f = get_footprint last_pc in let s = - if last_pc <> pc - 4 then Printf.sprintf "Coming from 0x%x: " last_pc else "" + if Elf.Address.(last_pc + 4 <> pc) then Printf.sprintf "Coming from %t: " Pp.(tos Elf.Address.pp last_pc) else "" in let regs = List.merge_uniq Stdlib.compare cur_instr_f last_instr_f in Hashtbl.add instr_data pc (Printf.sprintf "%sNormal instruction" s, st, regs) | Block_lib.End s -> let last_pc = st.last_pc in let last_instr = Runner.expect_normal runner last_pc in - Hashtbl.add instr_data (st.last_pc + 4) + Hashtbl.add instr_data Elf.Address.(st.last_pc + 4) (Printf.sprintf "End because: %s" s, st, Trace.Instr.footprint last_instr)) tree; Vec.iter (fun funcaddr -> let sym = Elf.SymTable.of_addr elf.symbols funcaddr in Analyse.Pp.pp_instruction_init (); - Seq.iota_step_up ~start:funcaddr ~step:4 ~endi:(funcaddr + sym.size) - |> Seq.iter (fun pc -> + Seq.iota_step_up ~start:0 ~step:4 ~endi:sym.size + |> Seq.iter (fun pc_diff -> + let pc = Elf.Address.(funcaddr + pc_diff) in Hashtbl.find_all instr_data pc |> List.iter (fun (msg, st, regs) -> - base "At 0x%x, %s:\n%t" pc msg Pp.(topi (State.pp_partial ~regs) st)); + base "At %t, %s:\n%t" Pp.(top Elf.Address.pp pc) msg Pp.(topi (State.pp_partial ~regs) st)); print_string (print_analyse_instruction pc))) - runner.funcs *) + runner.funcs; + match Analyse.Utils.read_file_lines "src/analyse/html-postamble.html" with + | Error _ -> () + | Ok lines -> Array.iter (function s -> Printf.printf "%s\n" s) lines let elf = let doc = "ELF file from which to pull the code" in @@ -168,7 +176,7 @@ let breakpoints = in Arg.(value & opt_all string [] & info ["b"; "break"] ~docv:"POSITION" ~doc) -(* let term = +let term = Term.( CmdlinerHelper.func_options comopts run_func_rd $ elf $ func $ objdump_d $ branch_table $ breakpoints) @@ -181,4 +189,4 @@ let info = in Cmd.(info "run-func-rd" ~doc ~exits) -let command = (term, info) *) +let command = (term, info) From b0dc540d6795bc61c126918cc6ec204d97728430 Mon Sep 17 00:00:00 2001 From: Matej Urban Date: Fri, 31 Jan 2025 15:20:47 +0000 Subject: [PATCH 046/116] Fix init of large objects --- src/elf/file.ml | 1 + src/state/base.ml | 15 ++++++++++----- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/src/elf/file.ml b/src/elf/file.ml index 1b2f39fc..d628ed8f 100644 --- a/src/elf/file.ml +++ b/src/elf/file.ml @@ -142,6 +142,7 @@ let of_file (filename : string) = (* `false' argument is for returning an empty byte-sequence if section is not found, instead of throwing an exception *) in + Printf.printf "%t" Pp.(top Sym.pp addr); Segment. { data; diff --git a/src/state/base.ml b/src/state/base.ml index b6918d03..96e35997 100644 --- a/src/state/base.ml +++ b/src/state/base.ml @@ -533,11 +533,16 @@ let init_sections ~addr_size state = Elf.SymTable.iter elf.symbols @@ fun sym -> if sym.typ = Elf.Symbol.OBJECT then let provenance = Mem.create_section_frag ~addr_size state.mem sym.addr.section in - let addr = Exp.of_address ~size:addr_size sym.addr in - let size = Ast.Size.of_bytes sym.size in - let (exp, asserts) = Relocation.exp_of_data sym.data in - Mem.write ~provenance state.mem ~addr ~size ~exp; - List.iter (push_relocation_assert state) asserts; + Seq.iota_step_up ~step:16 ~endi:sym.size + |> Seq.iter (fun off -> + let len = min 16 (sym.size - off) in + let data = Elf.Symbol.sub sym off len in + let addr = Exp.of_address ~size:addr_size Elf.Address.(sym.addr + off) in + let size = Ast.Size.of_bytes len in + let (exp, asserts) = Relocation.exp_of_data data in + Mem.write ~provenance state.mem ~addr ~size ~exp; + List.iter (push_relocation_assert state) asserts; + ) ) in state From bda07fb41125359631d1f18e07eb7be184c7cffc Mon Sep 17 00:00:00 2001 From: Matej Urban Date: Fri, 31 Jan 2025 16:05:25 +0000 Subject: [PATCH 047/116] [wip] Run program --- src/bin/readDwarf.ml | 1 + src/run/relProg.ml | 109 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 110 insertions(+) create mode 100644 src/run/relProg.ml diff --git a/src/bin/readDwarf.ml b/src/bin/readDwarf.ml index 53e15618..60a7749f 100644 --- a/src/bin/readDwarf.ml +++ b/src/bin/readDwarf.ml @@ -72,6 +72,7 @@ let commands = Run.Instr.command; Run.Block.command; Run.FuncRD.command; + Run.RelProg.command; CopySourcesCmd.command; Z3.Test.command; ] diff --git a/src/run/relProg.ml b/src/run/relProg.ml new file mode 100644 index 00000000..f2a3f202 --- /dev/null +++ b/src/run/relProg.ml @@ -0,0 +1,109 @@ +open Cmdliner +open Config.CommonOpt + +open Logs.Logger (struct + let str = __MODULE__ +end) + + +let run_prog elfname name objdump_d branchtables = + match Analyse.Utils.read_file_lines "src/analyse/html-preamble-insts.html" with + | Error _ -> () + | Ok lines -> Array.iter (function s -> Printf.printf "%s\n" s) lines + ; + base "Running with rd %s in %s" name elfname; + base "Loading %s" elfname; + let dwarf = Dw.of_file elfname in + let elf = dwarf.elf in + let func = + Dw.get_func_opt ~name dwarf + |> Option.value_fun ~default:(fun () -> fail "Function %s wasn't found in %s" name elfname) + in + let api = Dw.Func.get_api func in + base "API %t" (Pp.top Arch.pp_api api); + base "Loading ABI"; + let abi = Arch.get_abi api in + Trace.Cache.start @@ Arch.get_isla_config (); + base "Computing entry state"; + let start = Init.state () |> State.copy ~elf |> State.init_sections ~addr_size:Arch.address_size |> abi.init in + base "Loading %s for Analyse" elfname; + let analyse_test = Analyse.Elf.parse_elf_file elfname in + base "Analysing %s for Analyse" elfname; + let analyse_analysis = Analyse.Collected.mk_analysis analyse_test objdump_d branchtables in + let print_analyse_instruction pc = + let pc = Elf.Address.to_sym pc in + let index = analyse_analysis.index_of_address pc in + let instr = analyse_analysis.instructions.(index) in + Analyse.Pp.pp_instruction Analyse.Types.Html (*Ascii*) analyse_test analyse_analysis 0 index + instr + in + (* base "Entry state:\n%t" Pp.(topi State.pp start); *) + match func.sym with + | None -> fail "Function %s exists in DWARF data but do not have any code" name + | Some sym -> + let endpred = Block_lib.gen_endpred () in + let runner = Runner.of_dwarf dwarf in + let block = Block_lib.make ~runner ~start:sym.addr ~endpred in + base "Start running"; + let tree = Block_lib.run ~every_instruction:true block start in + base "Ended running, start pretty printing"; + (* This table will contain the state diff to print at each pc with a message *) + (* let instr_data : (Elf.Address.t, string * State.t * State.Reg.t list) Hashtbl.t = + Hashtbl.create 100 + in + let get_footprint pc = + Runner.get_normal_opt runner pc |> Option.fold ~none:[] ~some:Trace.Instr.footprint + in *) + State.Tree.iter + (fun a st -> + match a with + | Block_lib.Start -> () + | Block_lib.BranchAt pc -> + let last_pc = st.last_pc in + if Elf.Address.(last_pc + 4 <> pc) then + Printf.printf "\nJUMP from %t:\n " Pp.(top Elf.Address.pp last_pc); + print_string (print_analyse_instruction pc); + print_endline "BRANCH!"; + | Block_lib.NormalAt pc -> + let last_pc = st.last_pc in + if Elf.Address.(last_pc + 4 <> pc) then + Printf.printf "\nJUMP from %t:\n " Pp.(top Elf.Address.pp last_pc); + print_string (print_analyse_instruction pc); + | Block_lib.End _ -> ()) + tree; + match Analyse.Utils.read_file_lines "src/analyse/html-postamble.html" with + | Error _ -> () + | Ok lines -> Array.iter (function s -> Printf.printf "%s\n" s) lines + + +let elf = + let doc = "ELF file from which to pull the code" in + Arg.(required & pos 0 (some non_dir_file) None & info [] ~docv:"ELF_FILE" ~doc) + +let func = + let doc = "Symbol name of the function to run" in + Arg.(value & pos 1 string "main" & info [] ~docv:"FUNCTION" ~doc) + +let objdump_d = + let doc = "File containing result of objdump -d" in + Arg.(required & opt (some non_dir_file) None & info ["objdump-d"] ~docv:"OBJDUMP_FILE" ~doc) + +let branch_table = + let doc = "File containing branch table base addresses and sizes" in + Arg.( + (* required *) + value & opt (some non_dir_file) None & info ["branch-tables"] ~docv:"BRANCH_TABLES_FILE" ~doc) + +let term = + Term.( + CmdlinerHelper.func_options comopts run_prog + $ elf $ func $ objdump_d $ branch_table) + +let info = + let doc = + "Run main of relocatable file" + in + Cmd.(info "run-rel-prog" ~doc ~exits) + +let command = (term, info) + \ No newline at end of file From 8b30d350b9d940d568eaf518549f11ab45278cc1 Mon Sep 17 00:00:00 2001 From: Matej Urban Date: Sat, 1 Feb 2025 19:38:24 +0000 Subject: [PATCH 048/116] Run and print debug info --- src/dw/loc.ml | 8 +++++++ src/run/relProg.ml | 59 +++++++++++++++++++++++++++++++++++++++++----- src/utils/sym.ml | 4 +++- 3 files changed, 64 insertions(+), 7 deletions(-) diff --git a/src/dw/loc.ml b/src/dw/loc.ml index 5d03061a..3ead2a51 100644 --- a/src/dw/loc.ml +++ b/src/dw/loc.ml @@ -72,6 +72,7 @@ type t = | RegisterOffset of State.Reg.t * int (** At register + offset address *) | StackFrame of int (** On the stackFrame with offset *) | Global of Elf.SymTable.sym_offset (** Global variable with an offset *) + | Const of Z.t | Dwarf of dwop list (** Uninterpreted dwarf location *) (** The type of a location in linksem format *) @@ -128,6 +129,9 @@ let of_linksem ?(amap = Arch.dwarf_reg_map ()) (elf : Elf.File.t) : linksem_t -> Dwarf ops ) (* Other *) + | [{ op_semantics = OpSem_lit; op_argument_values = [arg]; _ }; { op_semantics = OpSem_stack_value; _ }] -> + let value = Sym.to_z @@ sym_of_oav arg in + Const value | ops -> Dwarf ops (** Convert the location to a string. This is not reversible *) @@ -136,6 +140,7 @@ let to_string = function | RegisterOffset (reg, off) -> Printf.sprintf "[%s+%x]" (State.Reg.to_string reg) off | StackFrame off -> Printf.sprintf "[frame+%x]" off | Global symoff -> Elf.SymTable.string_of_sym_offset symoff + | Const x -> Z.to_string x | Dwarf ops -> Dwarf.pp_operations ops (** Compare two location. Loc.t is not compatible with polymorphic compare *) @@ -154,6 +159,9 @@ let compare l1 l2 = Pair.compare ~fst:Elf.Symbol.compare (sym1, off1) (sym2, off2) | (Global (_, _), _) -> -1 | (_, Global (_, _)) -> 1 + | (Const x, Const y) -> Z.compare x y + | (Const _, _) -> -1 + | (_, Const _) -> 1 | (Dwarf ops1, Dwarf ops2) -> compare ops1 ops2 (** Pretty-print the location *) diff --git a/src/run/relProg.ml b/src/run/relProg.ml index f2a3f202..62c3f8da 100644 --- a/src/run/relProg.ml +++ b/src/run/relProg.ml @@ -5,6 +5,51 @@ open Logs.Logger (struct let str = __MODULE__ end) +let pp_eval_loc sz st (loc: Dw.Loc.t) : PPrint.document = + let value = match loc with + | Register reg -> Some (State.get_reg_exp st reg) + | RegisterOffset (reg, off) -> + let r = State.get_reg_exp st reg in + Some (State.read_noprov st ~addr:Exp.Typed.(r + bits_int ~size:Arch.address_size off) ~size:(Ast.Size.of_bytes sz)) + | StackFrame _off -> + None + | Global symoff -> + let addr = Elf.SymTable.to_addr_offset symoff in + let addr = State.Exp.of_address ~size:Arch.address_size addr in + Some (State.read_noprov st ~addr ~size:(Ast.Size.of_bytes sz)) + | Const x -> Some(x |> BitVec.of_z ~size:(8*sz) |> Exp.Typed.bits) + | Dwarf _ops -> None in + Pp.optional State.Exp.pp value + +let printvars ~st ~(dwarf: Dw.t) pc = + let st = State.copy_if_locked st in + let pv vars = + Seq.iter (fun (v: Dw.Var.t) -> + let sz = Ctype.sizeof v.ctype in + match List.find_map (fun ((lo,hi), loc) -> Option.( + let open Elf.Address in + let* hi = hi in + let* over = lo <= pc in + let* under = pc < hi in + if over && under then + Some loc + else + None + )) v.locs with + | None -> () + | Some loc -> Printf.printf "%s = %t\n" v.name Pp.(top (pp_eval_loc sz st) loc); + ) + vars + in + pv (Hashtbl.to_seq_values dwarf.vars); + Hashtbl.iter (fun _ (fn:Dw.Func.t) -> + let rec pscope (scope:Dw.Func.scope) = + pv (List.to_seq scope.vars); + List.iter pscope scope.scopes + in + pscope fn.func.scope + ) dwarf.funcs + let run_prog elfname name objdump_d branchtables = match Analyse.Utils.read_file_lines "src/analyse/html-preamble-insts.html" with @@ -56,20 +101,22 @@ let run_prog elfname name objdump_d branchtables = in *) State.Tree.iter (fun a st -> - match a with + let last_pc = st.last_pc in + (match a with | Block_lib.Start -> () | Block_lib.BranchAt pc -> - let last_pc = st.last_pc in if Elf.Address.(last_pc + 4 <> pc) then - Printf.printf "\nJUMP from %t:\n " Pp.(top Elf.Address.pp last_pc); + Printf.printf "\nJUMP from %t:\n\n" Pp.(top Elf.Address.pp last_pc); + printvars ~st ~dwarf pc; print_string (print_analyse_instruction pc); print_endline "BRANCH!"; | Block_lib.NormalAt pc -> - let last_pc = st.last_pc in if Elf.Address.(last_pc + 4 <> pc) then - Printf.printf "\nJUMP from %t:\n " Pp.(top Elf.Address.pp last_pc); + Printf.printf "\nJUMP from %t:\n\n" Pp.(top Elf.Address.pp last_pc); + printvars ~st ~dwarf pc; print_string (print_analyse_instruction pc); - | Block_lib.End _ -> ()) + | Block_lib.End _ -> ()); + ) tree; match Analyse.Utils.read_file_lines "src/analyse/html-postamble.html" with | Error _ -> () diff --git a/src/utils/sym.ml b/src/utils/sym.ml index 839f5c96..1510a8b3 100644 --- a/src/utils/sym.ml +++ b/src/utils/sym.ml @@ -2,7 +2,9 @@ type t = Z.t Dwarf.sym0 let pp x = x |> Dwarf.pphex_sym |> Pp.string -let to_int x = Z.to_int @@ Dwarf.sym_unwrap x "to_int" +let to_z x = Dwarf.sym_unwrap x "to_z" + +let to_int x = Z.to_int @@ to_z x let of_int x = Dwarf.Absolute (Z.of_int x) let of_int64 x = Dwarf.Absolute (Z.of_int64 x) From 7c5fd5f45174f97ebf2092bd3d25389216248828 Mon Sep 17 00:00:00 2001 From: Matej Urban Date: Sat, 1 Feb 2025 20:32:54 +0000 Subject: [PATCH 049/116] Fix ldst relocations --- src/elf/relocations.ml | 2 +- src/isla/cache.ml | 11 +++++------ src/isla/relocation.ml | 8 ++++---- 3 files changed, 10 insertions(+), 11 deletions(-) diff --git a/src/elf/relocations.ml b/src/elf/relocations.ml index 1a60a8bd..66cdc831 100644 --- a/src/elf/relocations.ml +++ b/src/elf/relocations.ml @@ -81,7 +81,7 @@ let pp_target = Pp.(function | AArch64 Abi_aarch64_symbolic_relocation.ADD -> !^"ADD" | AArch64 Abi_aarch64_symbolic_relocation.ADRP -> !^"ADRP" | AArch64 Abi_aarch64_symbolic_relocation.CALL -> !^"CALL" -| AArch64 Abi_aarch64_symbolic_relocation.LDST -> !^"LDST") +| AArch64 Abi_aarch64_symbolic_relocation.LDST b -> !^"LDST" ^^ int (1 lsl b)) let pp_rel rel = let hi, lo = rel.mask in diff --git a/src/isla/cache.ml b/src/isla/cache.ml index e5c95d72..17a71ce6 100644 --- a/src/isla/cache.ml +++ b/src/isla/cache.ml @@ -85,8 +85,8 @@ module Opcode (*: Cache.Key *) = struct | Some (Elf.Relocations.AArch64 Abi_aarch64_symbolic_relocation.Data320) -> 2 | Some (Elf.Relocations.AArch64 Abi_aarch64_symbolic_relocation.ADRP) -> 3 | Some (Elf.Relocations.AArch64 Abi_aarch64_symbolic_relocation.ADD) -> 4 - | Some (Elf.Relocations.AArch64 Abi_aarch64_symbolic_relocation.LDST) -> 5 - | Some (Elf.Relocations.AArch64 Abi_aarch64_symbolic_relocation.CALL) -> 6 + | Some (Elf.Relocations.AArch64 Abi_aarch64_symbolic_relocation.CALL) -> 5 + | Some (Elf.Relocations.AArch64 Abi_aarch64_symbolic_relocation.LDST b) -> 6 + b let reloc_of_id: int -> Relocation.t option = function | 0 -> None @@ -94,9 +94,8 @@ module Opcode (*: Cache.Key *) = struct | 2 -> Some (Elf.Relocations.AArch64 Abi_aarch64_symbolic_relocation.Data320) | 3 -> Some (Elf.Relocations.AArch64 Abi_aarch64_symbolic_relocation.ADRP) | 4 -> Some (Elf.Relocations.AArch64 Abi_aarch64_symbolic_relocation.ADD) - | 5 -> Some (Elf.Relocations.AArch64 Abi_aarch64_symbolic_relocation.LDST) - | 6 -> Some (Elf.Relocations.AArch64 Abi_aarch64_symbolic_relocation.CALL) - | _ -> Raise.fail "invalid reloc id" + | 5 -> Some (Elf.Relocations.AArch64 Abi_aarch64_symbolic_relocation.CALL) + | x -> Some (Elf.Relocations.AArch64 (Abi_aarch64_symbolic_relocation.LDST (x-6))) let equal a b = match (a, b) with @@ -105,7 +104,7 @@ module Opcode (*: Cache.Key *) = struct | _ -> false let small_enough bs rel_id = - BytesSeq.length bs < BytesSeq.int_bytes && rel_id < 8 + BytesSeq.length bs < (BytesSeq.int_bytes-1) && rel_id < (8*256) let hash = function | None -> 0 diff --git a/src/isla/relocation.ml b/src/isla/relocation.ml index 3e3004ad..d8e17a5a 100644 --- a/src/isla/relocation.ml +++ b/src/isla/relocation.ml @@ -25,9 +25,9 @@ let pp_opcode_with_segments (b, r) = BitVec.pp_smt (BitVec.extract 22 31 bits) ^^ !^" x0:12 " ^^ BitVec.pp_smt (BitVec.extract 0 9 bits) - | Abi_aarch64_symbolic_relocation.LDST -> (* TODO different width loads, alignment *) - BitVec.pp_smt (BitVec.extract 20 31 bits) - ^^ !^" x0:10 " ^^ + | Abi_aarch64_symbolic_relocation.LDST b -> (* TODO different width loads, alignment *) + BitVec.pp_smt (BitVec.extract (22-b) 31 bits) + ^^ !^" x0:" ^^ int (12-b) ^^ !^" " ^^ BitVec.pp_smt (BitVec.extract 0 9 bits) | Abi_aarch64_symbolic_relocation.CALL -> BitVec.pp_smt (BitVec.extract 26 31 bits) @@ -40,5 +40,5 @@ let segments_of_reloc: t -> segment list = function | Elf.Relocations.AArch64 Abi_aarch64_symbolic_relocation.Data320 -> fatal "invalid relocation for instructions (Data32)" | Elf.Relocations.AArch64 Abi_aarch64_symbolic_relocation.ADRP -> ["x0", (0, 1); "x1", (2, 20)] (* or absolute? ["x0", (12, 13); "x1", (14, 32)] *) | Elf.Relocations.AArch64 Abi_aarch64_symbolic_relocation.ADD -> ["x0", (0, 11)] -| Elf.Relocations.AArch64 Abi_aarch64_symbolic_relocation.LDST -> ["x0", (0, 9)] (* TODO depends on load size *) (* or absolute? ["x0", (2, 11)] *) +| Elf.Relocations.AArch64 Abi_aarch64_symbolic_relocation.LDST b -> ["x0", (0, 11-b)] (* TODO depends on load size *) (* or absolute? ["x0", (2, 11)] *) | Elf.Relocations.AArch64 Abi_aarch64_symbolic_relocation.CALL -> ["x0", (0, 25)] (* or absolute? ["x0", (2, 27)] *) \ No newline at end of file From 84798b3f458e6f8e72f045a7db84dce05e8584f6 Mon Sep 17 00:00:00 2001 From: Matej Urban Date: Wed, 5 Feb 2025 16:07:48 +0000 Subject: [PATCH 050/116] Fix rngmap croping --- src/utils/rngMap.ml | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/utils/rngMap.ml b/src/utils/rngMap.ml index 1e81ad21..42f872cb 100644 --- a/src/utils/rngMap.ml +++ b/src/utils/rngMap.ml @@ -303,14 +303,20 @@ module Make (Obj : LenObject) : S with type obj = Obj.t = struct let clear_crop t ~pos ~len ~crop = assert (len >= 0); (* Crop an possible object starting before the start but ending after the start. *) + let endp = pos + len in let t = match prev t (pos - 1) with | Some (addr, obj) when addr + Obj.len obj > pos -> + let objend = addr + Obj.len obj in + let t = if endp < objend then + IMap.add (pos + len) (crop ~pos:(endp - addr) ~len:(objend - endp) obj) t + else + t + in IMap.update addr (Option.map (crop ~pos:0 ~len:(pos - addr))) t | _ -> t in let seq = IMap.to_seq_from pos t in - let endp = pos + len in (* Remove all objects of the sequence from t until endp *) let rec remove_until t seq endp = match seq () with From 688d53968d96d30ea805fd7572ab40b8ca2b5eab Mon Sep 17 00:00:00 2001 From: Matej Urban Date: Wed, 5 Feb 2025 16:22:25 +0000 Subject: [PATCH 051/116] Fix symbolic bytes sub --- src/state/symbolicBytes.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/state/symbolicBytes.ml b/src/state/symbolicBytes.ml index e9bf6836..2693b0b3 100644 --- a/src/state/symbolicBytes.ml +++ b/src/state/symbolicBytes.ml @@ -148,7 +148,7 @@ module Make (Var : Exp.Var) : S with type var = Var.t = struct || *) let next = pos + off_len in - let* rest = sub_list ~pos:next ~len:(len - next) sb in + let* rest = sub_list ~pos:next ~len:(len - off_len) sb in let nexp = Typed.extract ~last:((8 * elen) - 1) ~first:(8 * off) e in Some (nexp :: rest) else From a9b1c224e6c1224be6bacc4e673d7677a1bbf805 Mon Sep 17 00:00:00 2001 From: Matej Urban Date: Wed, 5 Feb 2025 16:28:18 +0000 Subject: [PATCH 052/116] Eval debug variable expressions --- src/run/relProg.ml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/run/relProg.ml b/src/run/relProg.ml index 62c3f8da..e0923831 100644 --- a/src/run/relProg.ml +++ b/src/run/relProg.ml @@ -19,7 +19,11 @@ let pp_eval_loc sz st (loc: Dw.Loc.t) : PPrint.document = Some (State.read_noprov st ~addr ~size:(Ast.Size.of_bytes sz)) | Const x -> Some(x |> BitVec.of_z ~size:(8*sz) |> Exp.Typed.bits) | Dwarf _ops -> None in - Pp.optional State.Exp.pp value + Pp.optional (fun value -> + match Exp.ConcreteEval.eval_if_concrete value with + | Some(value) -> Exp.Value.pp value + | None -> State.Exp.pp value + ) value let printvars ~st ~(dwarf: Dw.t) pc = let st = State.copy_if_locked st in From 02cf6019379bb7ca1cf3394c875610488ce86ad5 Mon Sep 17 00:00:00 2001 From: Matej Urban Date: Wed, 5 Feb 2025 17:32:13 +0000 Subject: [PATCH 053/116] Nicer print (needs testing) --- src/run/relProg.ml | 44 ++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 40 insertions(+), 4 deletions(-) diff --git a/src/run/relProg.ml b/src/run/relProg.ml index e0923831..16a277b1 100644 --- a/src/run/relProg.ml +++ b/src/run/relProg.ml @@ -5,7 +5,42 @@ open Logs.Logger (struct let str = __MODULE__ end) -let pp_eval_loc sz st (loc: Dw.Loc.t) : PPrint.document = +let rec pp_array pp sz dims value = + match dims with + | [] -> pp value + | None::_ -> pp value + | Some d :: dims -> + let sz = sz / d in + Seq.iota d + |> Seq.map (fun x -> Exp.Typed.extract ~first:(8*x*sz) ~last:(8*(x+1)*sz-1) value) + |> List.of_seq + |> Pp.list (pp_array pp sz dims) + +let pp_typed ~(tenv: Ctype.env) ~(ctype: Ctype.t) ~pp (value: State.Exp.t) = + match ctype.unqualified with + | Machine _ -> pp value + | Cint _ -> pp value + | Cbool -> pp value + | Ptr _ -> pp value + | Struct { id; _ } -> + let s = IdMap.geti tenv.structs id in + Pp.( + Ctype.FieldMap.to_seq s.layout + |> Seq.map (fun (offset, (field:Ctype.field)) -> ( + opt string field.fname, + pp (Exp.Typed.extract ~first:(8*offset) ~last:(8*(offset + field.size)-1) value) + )) + |> List.of_seq + |> mapping s.name + ) + | Array { dims; _ } -> + let sz = Ctype.sizeof ctype in + pp_array pp sz dims value + | Enum _ -> pp value + | FuncPtr -> pp value + | Missing -> pp value + +let pp_eval_loc sz st ~(tenv: Ctype.env) ~(ctype: Ctype.t) (loc: Dw.Loc.t) : PPrint.document = let value = match loc with | Register reg -> Some (State.get_reg_exp st reg) | RegisterOffset (reg, off) -> @@ -19,11 +54,12 @@ let pp_eval_loc sz st (loc: Dw.Loc.t) : PPrint.document = Some (State.read_noprov st ~addr ~size:(Ast.Size.of_bytes sz)) | Const x -> Some(x |> BitVec.of_z ~size:(8*sz) |> Exp.Typed.bits) | Dwarf _ops -> None in - Pp.optional (fun value -> + let pp = fun value -> match Exp.ConcreteEval.eval_if_concrete value with | Some(value) -> Exp.Value.pp value | None -> State.Exp.pp value - ) value + in + Pp.optional (pp_typed ~tenv ~ctype ~pp) value let printvars ~st ~(dwarf: Dw.t) pc = let st = State.copy_if_locked st in @@ -41,7 +77,7 @@ let printvars ~st ~(dwarf: Dw.t) pc = None )) v.locs with | None -> () - | Some loc -> Printf.printf "%s = %t\n" v.name Pp.(top (pp_eval_loc sz st) loc); + | Some loc -> Printf.printf "%s = %t\n" v.name Pp.(top (pp_eval_loc sz st ~ctype:v.ctype ~tenv:dwarf.tenv) loc); ) vars in From 81d0deca0bf32fd0aff2d8ee3f6c839701aa637a Mon Sep 17 00:00:00 2001 From: Matej Urban Date: Sat, 15 Feb 2025 16:49:48 +0000 Subject: [PATCH 054/116] nicer print --- src/run/relProg.ml | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/src/run/relProg.ml b/src/run/relProg.ml index 16a277b1..6b140beb 100644 --- a/src/run/relProg.ml +++ b/src/run/relProg.ml @@ -40,18 +40,28 @@ let pp_typed ~(tenv: Ctype.env) ~(ctype: Ctype.t) ~pp (value: State.Exp.t) = | FuncPtr -> pp value | Missing -> pp value +let read_big st addr sz = + Seq.iota_step_up ~step:16 ~endi:sz + |> Seq.map (fun off -> + let addr = Exp.Typed.(addr + bits_int ~size:Arch.address_size off) in + let len = min 16 (sz - off) in + State.read_noprov st ~addr ~size:(Ast.Size.of_bytes len) + ) + |> List.of_seq + |> Exp.Typed.concat + let pp_eval_loc sz st ~(tenv: Ctype.env) ~(ctype: Ctype.t) (loc: Dw.Loc.t) : PPrint.document = let value = match loc with | Register reg -> Some (State.get_reg_exp st reg) | RegisterOffset (reg, off) -> let r = State.get_reg_exp st reg in - Some (State.read_noprov st ~addr:Exp.Typed.(r + bits_int ~size:Arch.address_size off) ~size:(Ast.Size.of_bytes sz)) + Some (read_big st Exp.Typed.(r + bits_int ~size:Arch.address_size off) sz) | StackFrame _off -> None | Global symoff -> let addr = Elf.SymTable.to_addr_offset symoff in let addr = State.Exp.of_address ~size:Arch.address_size addr in - Some (State.read_noprov st ~addr ~size:(Ast.Size.of_bytes sz)) + Some (read_big st addr sz) | Const x -> Some(x |> BitVec.of_z ~size:(8*sz) |> Exp.Typed.bits) | Dwarf _ops -> None in let pp = fun value -> @@ -62,6 +72,8 @@ let pp_eval_loc sz st ~(tenv: Ctype.env) ~(ctype: Ctype.t) (loc: Dw.Loc.t) : PPr Pp.optional (pp_typed ~tenv ~ctype ~pp) value let printvars ~st ~(dwarf: Dw.t) pc = + let out = ref "" in + let st = State.copy_if_locked st in let pv vars = Seq.iter (fun (v: Dw.Var.t) -> @@ -77,7 +89,7 @@ let printvars ~st ~(dwarf: Dw.t) pc = None )) v.locs with | None -> () - | Some loc -> Printf.printf "%s = %t\n" v.name Pp.(top (pp_eval_loc sz st ~ctype:v.ctype ~tenv:dwarf.tenv) loc); + | Some loc -> out := !out ^ Printf.sprintf "%s = %t\n" v.name Pp.(tos (pp_eval_loc sz st ~ctype:v.ctype ~tenv:dwarf.tenv) loc); ) vars in @@ -88,7 +100,8 @@ let printvars ~st ~(dwarf: Dw.t) pc = List.iter pscope scope.scopes in pscope fn.func.scope - ) dwarf.funcs + ) dwarf.funcs; + !out let run_prog elfname name objdump_d branchtables = @@ -147,13 +160,13 @@ let run_prog elfname name objdump_d branchtables = | Block_lib.BranchAt pc -> if Elf.Address.(last_pc + 4 <> pc) then Printf.printf "\nJUMP from %t:\n\n" Pp.(top Elf.Address.pp last_pc); - printvars ~st ~dwarf pc; + print_string @@ Analyse.Pp.css Analyse.Types.Html Render_vars @@ printvars ~st ~dwarf pc; print_string (print_analyse_instruction pc); print_endline "BRANCH!"; | Block_lib.NormalAt pc -> if Elf.Address.(last_pc + 4 <> pc) then Printf.printf "\nJUMP from %t:\n\n" Pp.(top Elf.Address.pp last_pc); - printvars ~st ~dwarf pc; + print_string @@ Analyse.Pp.css Analyse.Types.Html Render_vars @@ printvars ~st ~dwarf pc; print_string (print_analyse_instruction pc); | Block_lib.End _ -> ()); ) From 83f1d45c040870e7bc0f59a63dbe4b8fa6d291ef Mon Sep 17 00:00:00 2001 From: Matej Urban Date: Sat, 15 Feb 2025 23:45:16 +0000 Subject: [PATCH 055/116] Take state init function as arg in Run.Func --- src/run/func.ml | 10 +++++----- src/run/func.mli | 1 + src/state/base.ml | 11 +++++++++++ src/state/base.mli | 3 +++ 4 files changed, 20 insertions(+), 5 deletions(-) diff --git a/src/run/func.ml b/src/run/func.ml index 91167c8b..44ab9e46 100644 --- a/src/run/func.ml +++ b/src/run/func.ml @@ -52,7 +52,7 @@ open Logs.Logger (struct let str = __MODULE__ end) -let no_run_prep ~elf:elfname ~name ~entry = +let no_run_prep ~elf:elfname ~name ~entry ?(init = State.init_sections_symbolic) () = base "Running %s in %s" name elfname; let dwarf = Dw.of_file elfname in let elf = dwarf.elf in @@ -66,13 +66,13 @@ let no_run_prep ~elf:elfname ~name ~entry = let abi = Arch.get_abi api in Trace.Cache.start @@ Arch.get_isla_config (); base "Computing entry state"; - let start = Init.state () |> State.copy ~elf |> State.init_sections ~addr_size:Arch.address_size |> abi.init in + let start = Init.state () |> State.copy ~elf |> init |> abi.init in if entry then base "Entry state:\n%t" (Pp.topi State.pp start); (dwarf, elf, func, start) let get_state_tree ~elf:elfname ~name ?(dump = false) ?(entry = false) ?len ?(breakpoints = []) - ?loop ?tree_to_file () = - let (dwarf, elf, func, start) = no_run_prep ~elf:elfname ~name ~entry in + ?loop ?tree_to_file ?init () = + let (dwarf, elf, func, start) = no_run_prep ~elf:elfname ~name ~entry ?init () in match func.sym with | None -> fail "Function %s exists in DWARF data but does not have any code" name | Some sym -> @@ -104,7 +104,7 @@ let get_state_tree ~elf:elfname ~name ?(dump = false) ?(entry = false) ?len ?(br tree let cmd_func elfname name dump no_run entry len breakpoints loop tree_to_file = - if no_run then ignore @@ no_run_prep ~elf:elfname ~name ~entry + if no_run then ignore @@ no_run_prep ~elf:elfname ~name ~entry () else ignore @@ get_state_tree ~elf:elfname ~name ~dump ~entry ?len ~breakpoints ?loop ?tree_to_file () diff --git a/src/run/func.mli b/src/run/func.mli index 7f72d3f5..548f1f07 100644 --- a/src/run/func.mli +++ b/src/run/func.mli @@ -7,6 +7,7 @@ val get_state_tree : ?breakpoints:string list -> ?loop:int -> ?tree_to_file:string -> + ?init:(State.t -> State.t) -> unit -> Block_lib.label State.Tree.t diff --git a/src/state/base.ml b/src/state/base.ml index 96e35997..b214b4c3 100644 --- a/src/state/base.ml +++ b/src/state/base.ml @@ -546,6 +546,17 @@ let init_sections ~addr_size state = ) in state +let init_sections_symbolic state = + let state = copy_if_locked state in + let _ = Option.( + let+ elf = state.elf in + Elf.SymTable.iter elf.symbols @@ fun sym -> + if sym.typ = Elf.Symbol.OBJECT then + Hashtbl.replace state.mem.sections sym.addr.section Main + ) in + state + + let map_mut_exp (f : exp -> exp) s : unit = assert (not @@ is_locked s); Reg.Map.map_mut_current (Tval.map_exp f) s.regs; diff --git a/src/state/base.mli b/src/state/base.mli index 665b61a9..73f123db 100644 --- a/src/state/base.mli +++ b/src/state/base.mli @@ -404,6 +404,9 @@ val copy_if_locked : ?elf:Elf.File.t -> t -> t val init_sections : addr_size:int -> t -> t +(** Assigns all sections with global objects to Main fragment *) +val init_sections_symbolic : t -> t + (** {1 State convenience manipulation } *) From 7ad05907d0166c88f136b9700493e05e70378d0d Mon Sep 17 00:00:00 2001 From: Matej Urban Date: Sat, 15 Feb 2025 23:49:57 +0000 Subject: [PATCH 056/116] Make Sums.split sound We can guarantee that (a+b)[first:last] = a[first:last]+b[first:last] only when first=0 --- src/exp/sums.ml | 16 +--------------- 1 file changed, 1 insertion(+), 15 deletions(-) diff --git a/src/exp/sums.ml b/src/exp/sums.ml index 6d843f5d..287f4936 100644 --- a/src/exp/sums.ml +++ b/src/exp/sums.ml @@ -52,7 +52,7 @@ let rec split = let open Ast in function | Manyop (Bvmanyarith Bvadd, l, _) -> List.concat_map split l - | Unop (Extract (last, first), e, _) -> + | Unop (Extract (last, (0 as first)), e, _) -> let l = split e in List.map (Typed.extract ~first ~last) l | Unop (Bvneg, e, _) -> @@ -63,20 +63,6 @@ let rec split = let l' = split e' in let rl' = List.rev_map Typed.neg l' in List.rev_append rl' l - | Manyop (Concat, l, _) -> - let all_splits = List.map split l in - let defaults = List.map (fun e -> - let size = e |> Typed.get_type |> Typed.expect_bv in - Typed.bits_int ~size 0 - ) l in - let terms = List.transpose ~defaults all_splits in - List.map Typed.concat terms - | Unop (ZeroExtend m, e, _) -> - let l = split e in - List.map (Typed.unop (ZeroExtend m)) l - | Unop (SignExtend s, e, _) -> - let l = split e in - List.map (Typed.unop (SignExtend s)) l | e -> [e] let merge ~size l = if l = [] then Typed.zero ~size else Typed.sum l From 94304463890893d7011d84053e2af613a45f17e1 Mon Sep 17 00:00:00 2001 From: Matej Urban Date: Tue, 25 Feb 2025 10:27:00 +0000 Subject: [PATCH 057/116] Context simplifier for relocation TODO: move all simplification here instead of read_noprov --- src/relsim/base.ml | 115 +++++++++++++++++++++++++++++++++++++++++++ src/state/base.ml | 1 + src/trace/context.ml | 19 +++++-- src/trace/run.ml | 13 +++-- src/z3/z3.ml | 4 -- 5 files changed, 138 insertions(+), 14 deletions(-) create mode 100644 src/relsim/base.ml diff --git a/src/relsim/base.ml b/src/relsim/base.ml new file mode 100644 index 00000000..43df0a71 --- /dev/null +++ b/src/relsim/base.ml @@ -0,0 +1,115 @@ +(* open Logs.Logger (struct + let str = __MODULE__ +end) + +module Sums = Exp.Sums +module Typed = Exp.Typed + +module Var = struct + (** The type of variables *) + type t = Left of State.var | Right of State.var + + let equal a b = match (a,b) with + | Left a, Left b -> State.Var.equal a b + | Right a, Right b -> State.Var.equal a b + | _ -> false + + let pp = function + | Left v -> Pp.(!^"L:" ^^ State.Var.pp v) + | Right v -> Pp.(!^"R:" ^^ State.Var.pp v) + + (** Get the type of the variable *) + let ty = function Left v | Right v -> State.Var.ty v + + let hash = Hashtbl.hash + + let of_string = State.Var.of_string (*TODO*) +end + +module Exp = struct + include Exp.Make (Var) + + let left : State.Exp.t -> t = Ast.Manip.exp_map_var (fun v -> Var.Left v) + + let right : State.Exp.t -> t = Ast.Manip.exp_map_var (fun v -> Var.Right v) +end + +type sem_type = + | Value of int + | Ptr of sem_type + +type mem_rel = (State.Exp.t * State.Exp.t * sem_type) list + +let rec sem_type_of_ctype Ctype.{unqualified; _} = + match unqualified with + | Machine b -> Value b + | Cint { size; _ } -> Value size + | Cbool -> Value 1 + | Ptr { fragment=Ctype.DynArray t; _ } -> Ptr (sem_type_of_ctype t) + | _ -> Raise.todo() + +let mem_rel_of_dwarf (dw: (Dw.Var.t * Dw.Var.t) list) : mem_rel = + List.map (fun ((v1: Dw.Var.t), (v2: Dw.Var.t)) -> + let addr1 = match v1.locs with + | [_, Global a] -> + a |> Elf.SymTable.to_addr_offset + |> State.Exp.of_address ~size:Arch.address_size + (* |> Ast.Manip.exp_map_var (fun x -> Var.Left x) *) + | _ -> Raise.todo() + in + let addr2 = match v2.locs with + | [_, Global a] -> + a |> Elf.SymTable.to_addr_offset + |> State.Exp.of_address ~size:Arch.address_size + (* |> Ast.Manip.exp_map_var (fun x -> Var.Right x) *) + | _ -> Raise.todo() + in + let stp = sem_type_of_ctype v1.ctype in + (addr1, addr2, stp) + ) dw + +type rel = mem_rel * Exp.t list + +type event = State.Mem.Fragment.Event.t +type block = State.Mem.Fragment.Block.t + +let type_at (mem_rel:mem_rel) (block1:block) (block2:block) = + List.find_map (fun (e1,e2,t) -> + let (sym1, off1) = Sums.split_concrete e1 in + let (sym2, off2) = Sums.split_concrete e2 in + if (BitVec.to_int off1 == block1.offset && BitVec.to_int off2 == block2.offset + && Option.equal State.Exp.equal sym1 block1.base + && Option.equal State.Exp.equal sym2 block2.base) + then + Some t + else + None + ) mem_rel + +module Z3sim = Z3.Make (Var) + +let update_rel ((mem, asserts):rel) (e1: event) (e2: event) : rel = + (* TODO check sizes *) + match e1, e2 with + | (Read (block1, v1), Read (block2, v2)) -> ( + match type_at mem block1 block2 with + | Some (Value _) -> mem, Typed.(Exp.of_var (Left v1) = Exp.of_var (Right v2))::asserts + | Some (Ptr t) -> (State.Exp.of_var v1, State.Exp.of_var v2, t)::mem, asserts + | None -> mem, asserts + ) + | (Write (block1, e1), Write (block2, e2)) -> ( + (match type_at mem block1 block2 with + | Some (Value _) -> Z3 + | Some (Ptr t) -> Raise.todo() (* Check (e1, e2, t) in mem *) + | None -> Raise.fail "simrel failed"); + mem, asserts + ) + | _ -> Raise.fail "simrel failed" + + +let verify (st1:State.t) (st2:State.t) (dw:(Dw.Var.t * Dw.Var.t) list) = + let mem_rel = mem_rel_of_dwarf dw in + Raise.todo() + + + *) diff --git a/src/state/base.ml b/src/state/base.ml index b214b4c3..eebb0ace 100644 --- a/src/state/base.ml +++ b/src/state/base.ml @@ -645,6 +645,7 @@ let eval_address (s : t) (addr: Exp.t) : Elf.Address.t option = let read_noprov ?ctyp (s : t) ~(addr : Exp.t) ~(size : Mem.Size.t) : Exp.t = + debug "Addr: %t" Pp.(top Exp.pp addr); let elf_addr = eval_address s addr in debug "Address: %t" Pp.(top (optional Elf.Address.pp) elf_addr); match elf_addr with diff --git a/src/trace/context.ml b/src/trace/context.ml index 66a045ee..f233abe9 100644 --- a/src/trace/context.ml +++ b/src/trace/context.ml @@ -60,6 +60,7 @@ type t = { mem_reads : State.tval HashVector.t; (** Stores the result of memory reads *) state : State.t; segments : State.exp SMap.t; + asserts: State.exp list; dwarf : Dw.t option; (** Optionally DWARF information. If present, typing is enabled *) } @@ -68,19 +69,20 @@ let make_context ?dwarf ?relocation state = let reg_writes = Vec.empty () in let mem_reads = HashVector.empty () in - let segments = relocation + let segments, asserts = relocation |> Option.map (fun relocation -> let State.Relocation.{value;asserts;target} = State.Relocation.of_elf relocation in List.iter (State.push_relocation_assert state) asserts; - target + (target |> Isla.Relocation.segments_of_reloc |> SMap.of_list - |> SMap.map (fun (first, last) -> Exp.Typed.extract ~first ~last value) + |> SMap.map (fun (first, last) -> Exp.Typed.extract ~first ~last value), + asserts) ) - |> Option.value ~default:SMap.empty + |> Option.value ~default:(SMap.empty, []) in - { state; reg_writes; mem_reads; dwarf; segments } + { state; reg_writes; mem_reads; dwarf; segments; asserts } (** Expand a Trace variable to a State expression, using the context *) let expand_var ~(ctxt : t) (v : Base.Var.t) (a : Ast.no Ast.ty) : State.exp = @@ -93,3 +95,10 @@ let expand_var ~(ctxt : t) (v : Base.Var.t) (a : Ast.no Ast.ty) : State.exp = (** Tell if typing should enabled with this context *) let typing_enabled ~(ctxt : t) = ctxt.dwarf <> None + +module Z3St = State.Simplify.Z3St + +let simplify ~(ctxt : t) (exp : State.exp) : State.exp = + exp + |> Z3St.simplify_subterms_full ~hyps:ctxt.asserts + |> Z3St.simplify_full \ No newline at end of file diff --git a/src/trace/run.ml b/src/trace/run.ml index 45c9252d..b0250aae 100644 --- a/src/trace/run.ml +++ b/src/trace/run.ml @@ -64,12 +64,15 @@ type ctxt = Ctxt.t let expand ~(ctxt : ctxt) (exp : Base.exp) : State.exp = Ast.Manip.exp_var_subst (Ctxt.expand_var ~ctxt) exp +let expand_simplify ~(ctxt : ctxt) (exp : Base.exp) : State.exp = + exp |> expand ~ctxt |> Context.simplify ~ctxt + (** Expand a Trace expression to a typed State expression, using the context. If the context enables typing, the expression will actually be typed, otherwise the type will be [None] *) let expand_tval ~(ctxt : ctxt) (exp : Base.exp) : State.tval = - let sexp = expand ~ctxt exp in + let sexp = expand_simplify ~ctxt exp in if Ctxt.typing_enabled ~ctxt then let ctyp = Typer.expr ~ctxt exp in { ctyp; exp = sexp } @@ -84,7 +87,7 @@ let event_mut ~(ctxt : ctxt) (event : Base.event) = match event with | WriteReg { reg; value } -> Vec.add_one ctxt.reg_writes (reg, expand_tval ~ctxt value) | ReadMem { addr; value; size } -> - let naddr = expand ~ctxt addr in + let naddr = expand_simplify ~ctxt addr in debug "naddr: %t" (Pp.top State.Exp.pp naddr); let ptrtype = Typer.expr ~ctxt addr in debug "ptrtype: %t" Pp.(top (optional Ctype.pp) ptrtype); @@ -97,7 +100,7 @@ let event_mut ~(ctxt : ctxt) (event : Base.event) = in HashVector.set ctxt.mem_reads value tval | WriteMem { addr; value; size } -> ( - let naddr = expand ~ctxt addr in + let naddr = expand_simplify ~ctxt addr in debug "naddr: %t" (Pp.top State.Exp.pp naddr); let ptrtype = Typer.expr ~ctxt addr in debug "ptrtype: %t" Pp.(top (optional Ctype.pp) ptrtype); @@ -108,10 +111,10 @@ let event_mut ~(ctxt : ctxt) (event : Base.event) = let value = expand_tval ~ctxt value in Typer.write ~dwarf ctxt.state ?ptrtype ~addr:naddr ~size value | None -> - let value = expand ~ctxt value in + let value = expand_simplify ~ctxt value in State.write_noprov ctxt.state ~addr:naddr ~size value ) - | Assert exp -> State.push_assert ctxt.state (expand ~ctxt exp) + | Assert exp -> State.push_assert ctxt.state (expand_simplify ~ctxt exp) (** Run a trace on the provided state by mutation. Enable typing if [dwarf] is provided *) let trace_mut ?dwarf ?relocation (state : State.t) (events : Base.t) : unit = diff --git a/src/z3/z3.ml b/src/z3/z3.ml index 73733720..52c31a95 100644 --- a/src/z3/z3.ml +++ b/src/z3/z3.ml @@ -527,12 +527,8 @@ module Make (Var : Var) : S with type var = Var.t = struct let rec simplify_subterms serv (e : Exp.t) : Exp.t = e |> Ast.Manip.all_subterms |> List.find_opt (fun t -> - let et = Typed.get_type e in - let tt = Typed.get_type t in - Printf.printf "Types: %t, %t\n" Pp.(top Ast.pp_ty (Ast.Manip.ty_allow_mem et)) Pp.(top Ast.pp_ty (Ast.Manip.ty_allow_mem tt)); Typed.get_type e = Typed.get_type t && let result = check serv Typed.(e = t) in - Printf.printf "%t\n" Pp.(top (optional bool) result); result = Some true ) |> Option.map (simplify_subterms serv) From 27b4dafaa7fa8399491c039e362e9005d1dee5b5 Mon Sep 17 00:00:00 2001 From: Matej Urban Date: Tue, 18 Mar 2025 21:16:13 +0000 Subject: [PATCH 058/116] Fix caching --- src/isla/cache.ml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/isla/cache.ml b/src/isla/cache.ml index 17a71ce6..be6f64e8 100644 --- a/src/isla/cache.ml +++ b/src/isla/cache.ml @@ -115,7 +115,7 @@ module Opcode (*: Cache.Key *) = struct if small_enough bs rel_id then begin assert (not @@ IntBits.get i IntBits.back); let res = IntBits.blit l 0 i (IntBits.back - 3) 3 in - let res = IntBits.blit rel_id 0 res (IntBits.back - 6) 3 in + let res = IntBits.blit rel_id 0 res (IntBits.back - 14) 11 in res end else IntBits.set i IntBits.back @@ -133,8 +133,8 @@ module Opcode (*: Cache.Key *) = struct else if IntBits.get hash IntBits.back then Raise.todo() else - let data = IntBits.sub hash 0 (IntBits.back - 6) in - let reloc_id = IntBits.sub hash (IntBits.back - 6) 3 in + let data = IntBits.sub hash 0 (IntBits.back - 14) in + let reloc_id = IntBits.sub hash (IntBits.back - 14) 11 in let size = IntBits.sub hash (IntBits.back - 3) 3 in let b = Bytes.create size in Bits.unsafe_blit_of_int data 0 b 0 (size * 8); From 65eb0ae18e2e0c4b1d2131fa32137ed217213469 Mon Sep 17 00:00:00 2001 From: Matej Urban Date: Tue, 18 Mar 2025 21:47:59 +0000 Subject: [PATCH 059/116] Refactor: use get_state_tree in run_prog --- src/run/func.ml | 4 +-- src/run/func.mli | 1 + src/run/relProg.ml | 77 +++++++++++++++++----------------------------- 3 files changed, 32 insertions(+), 50 deletions(-) diff --git a/src/run/func.ml b/src/run/func.ml index 44ab9e46..2c5968c4 100644 --- a/src/run/func.ml +++ b/src/run/func.ml @@ -71,7 +71,7 @@ let no_run_prep ~elf:elfname ~name ~entry ?(init = State.init_sections_symbolic) (dwarf, elf, func, start) let get_state_tree ~elf:elfname ~name ?(dump = false) ?(entry = false) ?len ?(breakpoints = []) - ?loop ?tree_to_file ?init () = + ?loop ?tree_to_file ?init ?every_instruction () = let (dwarf, elf, func, start) = no_run_prep ~elf:elfname ~name ~entry ?init () in match func.sym with | None -> fail "Function %s exists in DWARF data but does not have any code" name @@ -96,7 +96,7 @@ let get_state_tree ~elf:elfname ~name ?(dump = false) ?(entry = false) ?len ?(br base "Instructions:\n%t\n" (Pp.topi Runner.pp_instr runner) end; base "Start running"; - let tree = Block_lib.run block start in + let tree = Block_lib.run block start ?every_instruction in tree_to_file |> Option.iter (fun x -> Files.write_string x @@ Pp.tos (State.Tree.pp_all Block_lib.pp_label) tree ()); diff --git a/src/run/func.mli b/src/run/func.mli index 548f1f07..eac27b23 100644 --- a/src/run/func.mli +++ b/src/run/func.mli @@ -8,6 +8,7 @@ val get_state_tree : ?loop:int -> ?tree_to_file:string -> ?init:(State.t -> State.t) -> + ?every_instruction:bool -> unit -> Block_lib.label State.Tree.t diff --git a/src/run/relProg.ml b/src/run/relProg.ml index 6b140beb..e375db07 100644 --- a/src/run/relProg.ml +++ b/src/run/relProg.ml @@ -112,18 +112,6 @@ let run_prog elfname name objdump_d branchtables = base "Running with rd %s in %s" name elfname; base "Loading %s" elfname; let dwarf = Dw.of_file elfname in - let elf = dwarf.elf in - let func = - Dw.get_func_opt ~name dwarf - |> Option.value_fun ~default:(fun () -> fail "Function %s wasn't found in %s" name elfname) - in - let api = Dw.Func.get_api func in - base "API %t" (Pp.top Arch.pp_api api); - base "Loading ABI"; - let abi = Arch.get_abi api in - Trace.Cache.start @@ Arch.get_isla_config (); - base "Computing entry state"; - let start = Init.state () |> State.copy ~elf |> State.init_sections ~addr_size:Arch.address_size |> abi.init in base "Loading %s for Analyse" elfname; let analyse_test = Analyse.Elf.parse_elf_file elfname in base "Analysing %s for Analyse" elfname; @@ -135,42 +123,35 @@ let run_prog elfname name objdump_d branchtables = Analyse.Pp.pp_instruction Analyse.Types.Html (*Ascii*) analyse_test analyse_analysis 0 index instr in - (* base "Entry state:\n%t" Pp.(topi State.pp start); *) - match func.sym with - | None -> fail "Function %s exists in DWARF data but do not have any code" name - | Some sym -> - let endpred = Block_lib.gen_endpred () in - let runner = Runner.of_dwarf dwarf in - let block = Block_lib.make ~runner ~start:sym.addr ~endpred in - base "Start running"; - let tree = Block_lib.run ~every_instruction:true block start in - base "Ended running, start pretty printing"; - (* This table will contain the state diff to print at each pc with a message *) - (* let instr_data : (Elf.Address.t, string * State.t * State.Reg.t list) Hashtbl.t = - Hashtbl.create 100 - in - let get_footprint pc = - Runner.get_normal_opt runner pc |> Option.fold ~none:[] ~some:Trace.Instr.footprint - in *) - State.Tree.iter - (fun a st -> - let last_pc = st.last_pc in - (match a with - | Block_lib.Start -> () - | Block_lib.BranchAt pc -> - if Elf.Address.(last_pc + 4 <> pc) then - Printf.printf "\nJUMP from %t:\n\n" Pp.(top Elf.Address.pp last_pc); - print_string @@ Analyse.Pp.css Analyse.Types.Html Render_vars @@ printvars ~st ~dwarf pc; - print_string (print_analyse_instruction pc); - print_endline "BRANCH!"; - | Block_lib.NormalAt pc -> - if Elf.Address.(last_pc + 4 <> pc) then - Printf.printf "\nJUMP from %t:\n\n" Pp.(top Elf.Address.pp last_pc); - print_string @@ Analyse.Pp.css Analyse.Types.Html Render_vars @@ printvars ~st ~dwarf pc; - print_string (print_analyse_instruction pc); - | Block_lib.End _ -> ()); - ) - tree; + base "Start running"; + let tree = Func.get_state_tree ~elf:elfname ~name ~init:(State.init_sections ~addr_size:Arch.address_size) ~every_instruction:true () in + base "Ended running, start pretty printing"; + (* This table will contain the state diff to print at each pc with a message *) + (* let instr_data : (Elf.Address.t, string * State.t * State.Reg.t list) Hashtbl.t = + Hashtbl.create 100 + in + let get_footprint pc = + Runner.get_normal_opt runner pc |> Option.fold ~none:[] ~some:Trace.Instr.footprint + in *) + State.Tree.iter + (fun a st -> + let last_pc = st.last_pc in + (match a with + | Block_lib.Start -> () + | Block_lib.BranchAt pc -> + if Elf.Address.(last_pc + 4 <> pc) then + Printf.printf "\nJUMP from %t:\n\n" Pp.(top Elf.Address.pp last_pc); + print_string @@ Analyse.Pp.css Analyse.Types.Html Render_vars @@ printvars ~st ~dwarf pc; + print_string (print_analyse_instruction pc); + print_endline "BRANCH!"; + | Block_lib.NormalAt pc -> + if Elf.Address.(last_pc + 4 <> pc) then + Printf.printf "\nJUMP from %t:\n\n" Pp.(top Elf.Address.pp last_pc); + print_string @@ Analyse.Pp.css Analyse.Types.Html Render_vars @@ printvars ~st ~dwarf pc; + print_string (print_analyse_instruction pc); + | Block_lib.End _ -> ()); + ) + tree; match Analyse.Utils.read_file_lines "src/analyse/html-postamble.html" with | Error _ -> () | Ok lines -> Array.iter (function s -> Printf.printf "%s\n" s) lines From f025a7c84ab25917c9056892e7b9141b4432d99b Mon Sep 17 00:00:00 2001 From: Matej Urban Date: Wed, 9 Apr 2025 09:55:00 +0100 Subject: [PATCH 060/116] Update for changes in linksem --- src/analyse/ControlFlow.ml | 5 +-- src/analyse/Elf.ml | 8 ++--- src/analyse/Symbols.ml | 4 +-- src/analyse/Utils.ml | 2 +- src/bin/copySources.ml | 4 +-- src/dw/addr.ml | 2 +- src/dw/var.ml | 2 +- src/elf/address.ml | 2 +- src/elf/linksemRelocatable.ml | 2 +- src/utils/sym.ml | 68 ++++++++++++++++------------------- 10 files changed, 46 insertions(+), 53 deletions(-) diff --git a/src/analyse/ControlFlow.ml b/src/analyse/ControlFlow.ml index 4c305098..bba61c63 100644 --- a/src/analyse/ControlFlow.ml +++ b/src/analyse/ControlFlow.ml @@ -139,7 +139,8 @@ let branch_table_target_addresses test filename_branch_table_option : (addr * ad (* chop into 4-byte words - as needed for branch offset tables, though not for all other things in .rodata *) - let rodata_words : (natural * natural) list = Dwarf.words_of_rel_byte_sequence rodata_addr (Dwarf.rbs_no_reloc bs) [] in (*HACK*) + let rodata_words : (natural * natural) list = + Dwarf.words_of_sym_byte_sequence rodata_addr (Dwarf_byte_sequence.sym_bs_construct bs (Pmap.empty Nat_big_num.compare)) [] in (*HACK*) let read_rodata_b addr = Dwarf.sym_natural_of_byte @@ -495,7 +496,7 @@ let parse_objdump_lines arch lines : objdump_instruction list = *) let with_symbolic_address (section: string) (addr, opcode_bytes, mnemonic, operands) : objdump_instruction = - (Dwarf.Offset (section, Nat_big_num.of_int64 addr), opcode_bytes, mnemonic, operands) + (Sym_ocaml.Num.Offset (section, Nat_big_num.of_int64 addr), opcode_bytes, mnemonic, operands) let rec parse_objdump_lines arch lines (next_index : int) (last_address : int64 option) (section: string option) : objdump_instruction list = diff --git a/src/analyse/Elf.ml b/src/analyse/Elf.ml index 5b3bb63c..574664ca 100644 --- a/src/analyse/Elf.ml +++ b/src/analyse/Elf.ml @@ -58,8 +58,8 @@ let pp_symbol_map (symbol_map : Elf_file.global_symbol_init_info) = String.concat "" (List.map (fun (name, (typ, _size, address, _mb, _binding)) -> - Printf.sprintf "**** name = %s address = %s typ = %d\n" name (pp_addr (Dwarf.Absolute address)) - (Sym.to_int (Dwarf.Absolute typ))) + Printf.sprintf "**** name = %s address = %s typ = %d\n" name (pp_addr (Sym_ocaml.Num.Absolute address)) + (Sym.to_int (Sym_ocaml.Num.Absolute typ))) symbol_map) (*****************************************************************************) @@ -153,8 +153,8 @@ let parse_elf_file (filename : string) : test = elf_file; arch; symbol_map (*@ (symbols_for_stacks !Globals.elf_threads)*); - e_entry = Dwarf.Absolute (entry); - e_machine = Dwarf.Absolute (machine); + e_entry = Sym_ocaml.Num.Absolute (entry); + e_machine = Sym_ocaml.Num.Absolute (machine); dwarf_static = ds; dwarf_semi_pp_frame_info; } diff --git a/src/analyse/Symbols.ml b/src/analyse/Symbols.ml index 360ffbf6..cc059ae4 100644 --- a/src/analyse/Symbols.ml +++ b/src/analyse/Symbols.ml @@ -28,7 +28,7 @@ let get_elf64_file_global_symbol_init (f: Elf_file.elf64_file) : global_symbol_i | None -> if machine = Elf_header.elf_ma_aarch64 then Error.bind - (Elf_symbolic.extract_elf64_relocations_for_section f Abi_aarch64_symbolic_relocation.abi_aarch64_relocation_to_abstract section) + (Elf_symbolic.extract_elf64_relocations_for_section f Abi_aarch64_symbolic_relocation.aarch64_relocation_interpreter section) @@ fun relocs -> Error.return (AArch64 relocs) else Error.fail @@ "machine not supported " ^ (Elf_header.string_of_elf_machine_architecture machine) @@ -43,7 +43,7 @@ let get_elf64_file_global_symbol_init (f: Elf_file.elf64_file) : global_symbol_i let bnd = Elf_symbol_table.extract_symbol_binding entry.elf64_st_info in Option.map ( fun section -> - let addr = Dwarf.Offset (section.elf64_section_name_as_string, addr_offset) in + let addr = Sym_ocaml.Num.Offset (section.elf64_section_name_as_string, addr_offset) in let data = if Byte_sequence.length0 section.elf64_section_body = Z.zero then Error.return (Byte_sequence.zeros size) else diff --git a/src/analyse/Utils.ml b/src/analyse/Utils.ml index bcd5a717..5d79072b 100644 --- a/src/analyse/Utils.ml +++ b/src/analyse/Utils.ml @@ -57,7 +57,7 @@ type addr = natural (* hackishly mask out bigint conversion failure *) let pp_addr (a : natural) = try - Dwarf.pp_sym Ml_bindings.hex_string_of_big_int_pad8 a + Sym_ocaml.Num.ppf Ml_bindings.hex_string_of_big_int_pad8 a with | Failure s -> let s' = "Failure: int64_of_big_int " ^ Sym.to_string a in (warn "pp_addr failure: %s" s); s' | e -> raise e diff --git a/src/bin/copySources.ml b/src/bin/copySources.ml index dc30fd01..ed6def6f 100644 --- a/src/bin/copySources.ml +++ b/src/bin/copySources.ml @@ -84,8 +84,8 @@ let process_file () : unit = else Some (Byte_sequence.string_of_byte_sequence - (rbs_unwrap (List.nth lnh.lnh_include_directories (dir - 1))))), - Byte_sequence.string_of_byte_sequence (rbs_unwrap lnfe.lnfe_path) )) + (Dwarf_byte_sequence.sym_bs_expect_const (List.nth lnh.lnh_include_directories (dir - 1))))), + Byte_sequence.string_of_byte_sequence (Dwarf_byte_sequence.sym_bs_expect_const lnfe.lnfe_path) )) lnh.lnh_file_entries in diff --git a/src/dw/addr.ml b/src/dw/addr.ml index 0604a6f7..201906bf 100644 --- a/src/dw/addr.ml +++ b/src/dw/addr.ml @@ -1,6 +1,6 @@ include Elf.Address let of_sym : Sym.t -> t = function -| Dwarf.Offset (section, offset) -> { section; offset = Z.to_int offset } +| Sym_ocaml.Num.Offset (section, offset) -> { section; offset = Z.to_int offset } | _ -> Raise.fail "expected section+offset" diff --git a/src/dw/var.ml b/src/dw/var.ml index e453d61b..b6010848 100644 --- a/src/dw/var.ml +++ b/src/dw/var.ml @@ -61,7 +61,7 @@ let rec loc_merge = function | [] -> [] let end_addr_of_sym = function -| Dwarf.Absolute z when Z.compare z (Z.of_int Int.max_int) > 0 -> None +| Sym_ocaml.Num.Absolute z when Z.compare z (Z.of_int Int.max_int) > 0 -> None | x -> Some (Addr.of_sym x) (** Create a DWARF variable from its linksem counterpart *) diff --git a/src/elf/address.ml b/src/elf/address.ml index f332f31a..33da9cd8 100644 --- a/src/elf/address.ml +++ b/src/elf/address.ml @@ -23,4 +23,4 @@ let (<=) = compare (<=) let (>=) = compare (>=) -let to_sym {section; offset} = Dwarf.Offset (section, Z.of_int offset) \ No newline at end of file +let to_sym {section; offset} = Sym_ocaml.Num.Offset (section, Z.of_int offset) \ No newline at end of file diff --git a/src/elf/linksemRelocatable.ml b/src/elf/linksemRelocatable.ml index d822c2fa..99998fb7 100644 --- a/src/elf/linksemRelocatable.ml +++ b/src/elf/linksemRelocatable.ml @@ -30,7 +30,7 @@ let get_elf64_file_global_symbol_init (f: Elf_file.elf64_file) : global_symbol_i | None -> if machine = Elf_header.elf_ma_aarch64 then Error.bind - (Elf_symbolic.extract_elf64_relocations_for_section f Abi_aarch64_symbolic_relocation.abi_aarch64_relocation_to_abstract section) + (Elf_symbolic.extract_elf64_relocations_for_section f Abi_aarch64_symbolic_relocation.aarch64_relocation_interpreter section) @@ fun relocs -> Error.return (AArch64 relocs) else Error.fail @@ "machine not supported " ^ (Elf_header.string_of_elf_machine_architecture machine) diff --git a/src/utils/sym.ml b/src/utils/sym.ml index 1510a8b3..42ef411d 100644 --- a/src/utils/sym.ml +++ b/src/utils/sym.ml @@ -1,15 +1,15 @@ -type t = Z.t Dwarf.sym0 +type t = Sym_ocaml.Num.t let pp x = x |> Dwarf.pphex_sym |> Pp.string -let to_z x = Dwarf.sym_unwrap x "to_z" +let to_z x = Sym_ocaml.Num.to_num x let to_int x = Z.to_int @@ to_z x -let of_int x = Dwarf.Absolute (Z.of_int x) -let of_int64 x = Dwarf.Absolute (Z.of_int64 x) +let of_int x = Sym_ocaml.Num.Absolute (Z.of_int x) +let of_int64 x = Sym_ocaml.Num.Absolute (Z.of_int64 x) -let equal = Dwarf.sym_comp Nat_big_num.equal +let equal = Sym_ocaml.Num.equal let max_addr = Z.(shift_left (of_int 1) 64 - (of_int 1)) @@ -17,40 +17,32 @@ let min_addr = Z.of_int 0 (* TODO very hacky *) let less x y = match (x, y) with -| (Dwarf.Absolute x, Dwarf.Offset (_, y)) when Nat_big_num.less x y -> true -| (Dwarf.Absolute x, Dwarf.Offset (_,_)) when Nat_big_num.greater_equal x max_addr -> false -| (Dwarf.Offset (_,_), Dwarf.Absolute y) when Nat_big_num.less max_addr y -> true -| (Dwarf.Offset (_, x), Dwarf.Absolute y) when Nat_big_num.greater_equal x y -> false -| _ -> Dwarf.sym_comp Nat_big_num.less x y -let less_equal = Dwarf.sym_comp Nat_big_num.less_equal -let greater = Dwarf.sym_comp Nat_big_num.greater -let greater_equal = Dwarf.sym_comp Nat_big_num.greater_equal -let compare = Dwarf.sym_comp Nat_big_num.compare - -let to_string = Dwarf.pp_sym Z.to_string - -let sub x y = match (x, y) with -| (Dwarf.Offset (s, a), Dwarf.Offset (t, b)) when s = t -> Dwarf.Absolute (Nat_big_num.sub a b) -| (Dwarf.Offset (s, a), Dwarf.Absolute b) -> Dwarf.Offset (s, Nat_big_num.sub a b) -| (Dwarf.Absolute a, Dwarf.Absolute b) -> Dwarf.Absolute (Nat_big_num.sub a b) -| _ -> Dwarf.Unknown - -let add x y = match (x, y) with -| (Dwarf.Offset (s, a), Dwarf.Absolute b) -> Dwarf.Offset (s, Nat_big_num.add a b) -| (Dwarf.Absolute (a), Dwarf.Offset (s,b)) -> Dwarf.Offset (s, Nat_big_num.add a b) -| (Dwarf.Absolute a, Dwarf.Absolute b) -> Dwarf.Absolute (Nat_big_num.add a b) -| _ -> Dwarf.Unknown - -let mul = Dwarf.sym_map2 Nat_big_num.mul - -let pow_int_positive x y = Dwarf.Absolute (Nat_big_num.pow_int_positive x y) - -let shift_left x s = Dwarf.sym_map (fun x -> Nat_big_num.shift_left x s) x -let shift_right x s = Dwarf.sym_map (fun x -> Nat_big_num.shift_right x s) x -let modulus = Dwarf.sym_map2 Nat_big_num.modulus +| (Sym_ocaml.Num.Absolute x, Sym_ocaml.Num.Offset (_, y)) when Nat_big_num.less x y -> true +| (Sym_ocaml.Num.Absolute x, Sym_ocaml.Num.Offset (_,_)) when Nat_big_num.greater_equal x max_addr -> false +| (Sym_ocaml.Num.Offset (_,_), Sym_ocaml.Num.Absolute y) when Nat_big_num.less max_addr y -> true +| (Sym_ocaml.Num.Offset (_, x), Sym_ocaml.Num.Absolute y) when Nat_big_num.greater_equal x y -> false +| _ -> Sym_ocaml.Num.comp Nat_big_num.less x y +let less_equal = Sym_ocaml.Num.less_equal +let greater = Sym_ocaml.Num.greater +let greater_equal = Sym_ocaml.Num.greater_equal +let compare = Sym_ocaml.Num.compare + +let to_string = Sym_ocaml.Num.ppf Z.to_string + +let sub = Sym_ocaml.Num.sub + +let add = Sym_ocaml.Num.add + +let mul = Sym_ocaml.Num.mul + +let pow_int_positive x y = Sym_ocaml.Num.Absolute (Nat_big_num.pow_int_positive x y) + +let shift_left x s = Sym_ocaml.Num.map (fun x -> Nat_big_num.shift_left x s) x +let shift_right x s = Sym_ocaml.Num.map (fun x -> Nat_big_num.shift_right x s) x +let modulus = Sym_ocaml.Num.map2 Nat_big_num.modulus let in_range first last x = match (first, last, x) with -| (Dwarf.Absolute f, Dwarf.Absolute l, Dwarf.Absolute x) -> Nat_big_num.less_equal f x && Nat_big_num.less_equal x l -| (Dwarf.Offset (s1, f), Dwarf.Offset (s2, l), Dwarf.Offset (s, x)) when s1 = s2 -> +| (Sym_ocaml.Num.Absolute f, Sym_ocaml.Num.Absolute l, Sym_ocaml.Num.Absolute x) -> Nat_big_num.less_equal f x && Nat_big_num.less_equal x l +| (Sym_ocaml.Num.Offset (s1, f), Sym_ocaml.Num.Offset (s2, l), Sym_ocaml.Num.Offset (s, x)) when s1 = s2 -> s1 = s && Nat_big_num.less_equal f x && Nat_big_num.less_equal x l (* TODO kinda hacky *) | _ -> Raise.fail "Can't determine if %t is in range [%t,%t]" (Pp.tos pp x) (Pp.tos pp first) (Pp.tos pp last) \ No newline at end of file From a212415f023939605ee7d93ee69a116e07e42d6d Mon Sep 17 00:00:00 2001 From: Matej Urban Date: Thu, 10 Apr 2025 12:26:15 +0100 Subject: [PATCH 061/116] Test script --- src/bin/readDwarf.ml | 1 + src/run/block_lib.ml | 13 ++++---- src/run/func.ml | 9 ++++-- src/run/relProg.ml | 4 ++- src/run/testRelProg.ml | 70 ++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 88 insertions(+), 9 deletions(-) create mode 100644 src/run/testRelProg.ml diff --git a/src/bin/readDwarf.ml b/src/bin/readDwarf.ml index 60a7749f..577cac14 100644 --- a/src/bin/readDwarf.ml +++ b/src/bin/readDwarf.ml @@ -73,6 +73,7 @@ let commands = Run.Block.command; Run.FuncRD.command; Run.RelProg.command; + Run.TestRelProg.command; CopySourcesCmd.command; Z3.Test.command; ] diff --git a/src/run/block_lib.ml b/src/run/block_lib.ml index 3082c0c1..3b62215f 100644 --- a/src/run/block_lib.ml +++ b/src/run/block_lib.ml @@ -93,24 +93,25 @@ let run ?(every_instruction = false) ?relevant (b : t) (start : State.t) : label assert (State.is_locked start); let rec run_from state = let pc_exp = State.get_reg_exp state pcreg in + State.Simplify.ctxfull state; if State.is_possible state then match b.endpred pc_exp with | Some endmsg -> info "Stopped at pc %t because %s" (Pp.top State.Exp.pp pc_exp) endmsg; - State.Simplify.ctxfull state; + (* State.Simplify.ctxfull state; *) State.lock state; State.Tree.{ state; data = End endmsg; rest = [] } | None -> ( - let prelock state = State.Simplify.ctxfull state in + (* let prelock state = State.Simplify.ctxfull state in *) if every_instruction then begin - prelock state; + (* prelock state; *) State.lock state end; let states = let pc = State.Exp.expect_sym_address pc_exp in if Option.fold ~none:true ~some:(Fun.flip Hashtbl.mem pc) relevant then ( info "Running pc %t" (Pp.top State.Exp.pp pc_exp); - Runner.run ~prelock b.runner state + Runner.run ~prelock:ignore b.runner state ) else ( info "Skipping pc %t" (Pp.top State.Exp.pp pc_exp); @@ -131,7 +132,7 @@ let run ?(every_instruction = false) ?relevant (b : t) (start : State.t) : label ) else begin info "Reached dead code at %t" (Pp.top State.Exp.pp pc_exp); - State.Simplify.ctxfull state; + (* State.Simplify.ctxfull state; *) State.lock state; State.Tree.{ state; data = End "Reached dead code"; rest = [] } end @@ -162,7 +163,7 @@ let gen_endpred ?min ?max ?loop ?(brks = []) () : State.exp -> string option = ( try Some (State.Exp.expect_sym_address pc_exp) with - _ -> debug "PC is sus"; None + _ -> None ) |> Option.map (fun pc -> debug "enpred: Evaluating PC %t" (Pp.top Elf.Address.pp pc); match (min, max, loop) with diff --git a/src/run/func.ml b/src/run/func.ml index 2c5968c4..e733d880 100644 --- a/src/run/func.ml +++ b/src/run/func.ml @@ -46,7 +46,7 @@ open Cmdliner open Config.CommonOpt -open Fun +(* open Fun *) open Logs.Logger (struct let str = __MODULE__ @@ -78,7 +78,12 @@ let get_state_tree ~elf:elfname ~name ?(dump = false) ?(entry = false) ?len ?(br | Some sym -> let brks = List.map - (Elf.SymTable.of_position_string elf.symbols %> Elf.SymTable.to_addr_offset) + (fun x -> + if String.starts_with ~prefix:"UND" x then (*HACK for undefined symbol*) + Elf.Address.{ section=x; offset=0 } + else + x |> Elf.SymTable.of_position_string elf.symbols |> Elf.SymTable.to_addr_offset + ) breakpoints in let (min, max) = diff --git a/src/run/relProg.ml b/src/run/relProg.ml index e375db07..7bf90660 100644 --- a/src/run/relProg.ml +++ b/src/run/relProg.ml @@ -124,7 +124,9 @@ let run_prog elfname name objdump_d branchtables = instr in base "Start running"; - let tree = Func.get_state_tree ~elf:elfname ~name ~init:(State.init_sections ~addr_size:Arch.address_size) ~every_instruction:true () in + let tree = Func.get_state_tree ~elf:elfname ~name ~init:(State.init_sections ~addr_size:Arch.address_size) ~every_instruction:true () + ~breakpoints:["UND.abort"; "UND.exit"] + in base "Ended running, start pretty printing"; (* This table will contain the state diff to print at each pc with a message *) (* let instr_data : (Elf.Address.t, string * State.t * State.Reg.t list) Hashtbl.t = diff --git a/src/run/testRelProg.ml b/src/run/testRelProg.ml new file mode 100644 index 00000000..1a429422 --- /dev/null +++ b/src/run/testRelProg.ml @@ -0,0 +1,70 @@ +open Cmdliner +open Config.CommonOpt + +open Logs.Logger (struct + let str = __MODULE__ +end) + +let test return_register exit_register name = + let tree = Func.get_state_tree ~elf:name ~name:"main" ~init:(State.init_sections ~addr_size:Arch.address_size) ~every_instruction:false () + ~breakpoints:["UND.abort"; "UND.exit"] + in + let pc = Arch.pc () in + let ret = State.Reg.of_string return_register in + let ext = State.Reg.of_string exit_register in + State.Tree.iter (fun l st -> + if State.is_possible st then + match l with + | Block_lib.End _ -> ( + let pc_exp = State.get_reg_exp st pc in + let ret_exp = match (try + Some (State.Exp.expect_sym_address pc_exp) + with + _ -> None + ) with + | Some pc_addr -> + if pc_addr = Elf.Address.{ section="UND.abort"; offset=0 } then + fail "abort called from %t" (Pp.top Elf.Address.pp st.last_pc) + else if pc_addr <> Elf.Address.{ section="UND.exit"; offset=0 } then + fail "finished at weird address %t" (Pp.top Elf.Address.pp pc_addr) + else + State.get_reg_exp st ext + | None -> + State.get_reg_exp st ret (* Symbolic pc = returned from main *) + in + let ret_val = ret_exp |> Exp.ConcreteEval.eval |> Exp.Value.expect_bv |> BitVec.to_int in + if ret_val <> 0 then + fail "non-zero return code %d" ret_val; + ) + | _ -> () + ) tree; + base "Success" + +let elf = + let doc = "ELF file from which to pull the code" in + Arg.(required & pos 0 (some non_dir_file) None & info [] ~docv:"ELF_FILE" ~doc) + +let return_register = + let doc = "The name of the register containing the return value of main function" in + Arg.(value & opt string "R0" & info ["r"] ~docv:"RETURN_REGISTER" ~doc) + +let exit_register = + let doc = "The name of the register containing the argument to exit function" in + Arg.(value & opt string "R0" & info ["e"] ~docv:"EXIT_REGISTER" ~doc) + + +let term = + Term.( + CmdlinerHelper.func_options comopts test + $ return_register $ exit_register $ elf) + +let info = + let doc = + "Test run relocatable file\ + + Test succeeds if all possble outcomes result in the program exiting with\ + with code 0" + in + Cmd.(info "test-rel-prog" ~doc ~exits) + +let command = (term, info) From a77a87b2ea128fe8698fbf90d3599a7112a1537b Mon Sep 17 00:00:00 2001 From: Matej Urban Date: Thu, 10 Apr 2025 12:36:59 +0100 Subject: [PATCH 062/116] Fix rodata --- src/elf/file.ml | 2 +- src/state/base.ml | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/elf/file.ml b/src/elf/file.ml index d628ed8f..1b56d28f 100644 --- a/src/elf/file.ml +++ b/src/elf/file.ml @@ -146,7 +146,7 @@ let of_file (filename : string) = Segment. { data; - addr = Sym.to_int addr; (* TODO *) + addr = 0; (* Meaningless for relocatable files *) size = BytesSeq.length data; read = true; write = false; diff --git a/src/state/base.ml b/src/state/base.ml index eebb0ace..2aa43a6c 100644 --- a/src/state/base.ml +++ b/src/state/base.ml @@ -589,8 +589,7 @@ let read_from_rodata (s : t) ~(addr : Exp.t) ~(size : Mem.Size.t) : Exp.t option | Some elf -> ( if not @@ ConcreteEval.is_concrete addr then None else - let int_addr = ConcreteEval.eval addr |> Value.expect_bv |> BitVec.to_int in - let sym_addr = Elf.Address.{ section = ".rodata"; offset = int_addr } in (* TODO this is wrong *) + let sym_addr = Exp.expect_sym_address addr in let size = size |> Ast.Size.to_bits in try let (sym, offset) = Elf.SymTable.of_addr_with_offset elf.symbols sym_addr in @@ -600,13 +599,14 @@ let read_from_rodata (s : t) ~(addr : Exp.t) ~(size : Mem.Size.t) : Exp.t option let bv = BytesSeq.getbvle ~size sym.data.data offset in (* TODO relocations *) Some (Typed.bits bv) with Not_found -> + let int_addr = sym_addr.offset in let rodata = elf.rodata in - if rodata.addr <= int_addr && int_addr + size < rodata.addr + rodata.size then + if sym_addr.section = ".rodata" && rodata.addr <= int_addr && int_addr + size < rodata.addr + rodata.size then let bv = BytesSeq.getbvle ~size rodata.data (int_addr - rodata.addr) in (* Assume little endian here *) Some (Typed.bits bv) else ( - warn "Failed to find symbol or rodata at 0x%x" int_addr; + warn "Failed to find symbol or rodata at %t" (Pp.top Elf.Address.pp sym_addr); None ) ) From c5e2b56433f14a21acf90e87dae0da9dce6deb50 Mon Sep 17 00:00:00 2001 From: Matej Urban Date: Thu, 10 Apr 2025 13:12:56 +0100 Subject: [PATCH 063/116] Fix loading objects --- src/state/base.ml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/state/base.ml b/src/state/base.ml index 2aa43a6c..458a3114 100644 --- a/src/state/base.ml +++ b/src/state/base.ml @@ -531,11 +531,11 @@ let init_sections ~addr_size state = let _ = Option.( let+ elf = state.elf in Elf.SymTable.iter elf.symbols @@ fun sym -> + let len = List.find (fun x -> sym.size mod x = 0) [16;8;4;2;1] in if sym.typ = Elf.Symbol.OBJECT then let provenance = Mem.create_section_frag ~addr_size state.mem sym.addr.section in - Seq.iota_step_up ~step:16 ~endi:sym.size + Seq.iota_step_up ~step:len ~endi:sym.size |> Seq.iter (fun off -> - let len = min 16 (sym.size - off) in let data = Elf.Symbol.sub sym off len in let addr = Exp.of_address ~size:addr_size Elf.Address.(sym.addr + off) in let size = Ast.Size.of_bytes len in From 2bc6c015396547af9d59bb48f052ac0e011ca40a Mon Sep 17 00:00:00 2001 From: Matej Urban Date: Thu, 10 Apr 2025 15:46:32 +0100 Subject: [PATCH 064/116] Fix reading rodata Also fixes a bug in read_from_rodata of comparing sizes in bits vs bytes --- src/run/testRelProg.ml | 1 + src/state/base.ml | 73 +++++++++++++++++++++--------------------- 2 files changed, 37 insertions(+), 37 deletions(-) diff --git a/src/run/testRelProg.ml b/src/run/testRelProg.ml index 1a429422..1f4b396d 100644 --- a/src/run/testRelProg.ml +++ b/src/run/testRelProg.ml @@ -9,6 +9,7 @@ let test return_register exit_register name = let tree = Func.get_state_tree ~elf:name ~name:"main" ~init:(State.init_sections ~addr_size:Arch.address_size) ~every_instruction:false () ~breakpoints:["UND.abort"; "UND.exit"] in + debug "%t" (Pp.top (State.Tree.pp_all Block_lib.pp_label) tree); let pc = Arch.pc () in let ret = State.Reg.of_string return_register in let ext = State.Reg.of_string exit_register in diff --git a/src/state/base.ml b/src/state/base.ml index 458a3114..2099b52b 100644 --- a/src/state/base.ml +++ b/src/state/base.ml @@ -583,43 +583,6 @@ let set_read (s : t) (read_num : int) (exp : Exp.t) = assert (Typed.get_type exp = Typed.get_type (Vec.get s.read_vars read_num |> Tval.exp)); Vec.update s.read_vars read_num @@ Tval.map_exp (Fun.const exp) -let read_from_rodata (s : t) ~(addr : Exp.t) ~(size : Mem.Size.t) : Exp.t option = - match s.elf with - | None -> None - | Some elf -> ( - if not @@ ConcreteEval.is_concrete addr then None - else - let sym_addr = Exp.expect_sym_address addr in - let size = size |> Ast.Size.to_bits in - try - let (sym, offset) = Elf.SymTable.of_addr_with_offset elf.symbols sym_addr in - if sym.writable then None - else - (* Assume little endian here *) - let bv = BytesSeq.getbvle ~size sym.data.data offset in (* TODO relocations *) - Some (Typed.bits bv) - with Not_found -> - let int_addr = sym_addr.offset in - let rodata = elf.rodata in - if sym_addr.section = ".rodata" && rodata.addr <= int_addr && int_addr + size < rodata.addr + rodata.size then - let bv = BytesSeq.getbvle ~size rodata.data (int_addr - rodata.addr) in - (* Assume little endian here *) - Some (Typed.bits bv) - else ( - warn "Failed to find symbol or rodata at %t" (Pp.top Elf.Address.pp sym_addr); - None - ) - ) - -let read ~provenance ?ctyp (s : t) ~(addr : Exp.t) ~(size : Mem.Size.t) : Exp.t = - assert (not @@ is_locked s); - let var = make_read ?ctyp s size in - let exp = Mem.read s.mem ~provenance ~var ~addr ~size in - let exp = if provenance = Main && exp = None then read_from_rodata ~addr ~size s else exp in - Option.iter (set_read s (Var.expect_readvar var)) exp; - Option.value exp ~default:(Exp.of_var var) - - let eval_address (s : t) (addr: Exp.t) : Elf.Address.t option = let ctxt0 = function Var.Section _ -> Value.bv @@ BitVec.of_int ~size:64 0 | _ -> raise ConcreteEval.Symbolic in let open Option in @@ -643,6 +606,42 @@ let eval_address (s : t) (addr: Exp.t) : Elf.Address.t option = None ) +let read_from_rodata (s : t) ~(addr : Exp.t) ~(size : Mem.Size.t) : Exp.t option = + debug "reading from rodata at address: %t" (Pp.top Exp.pp addr); + match s.elf with + | None -> None + | Some elf -> ( + Option.bind (eval_address s addr) @@ fun sym_addr -> + let size = size |> Ast.Size.to_bits in + try + let (sym, offset) = Elf.SymTable.of_addr_with_offset elf.symbols sym_addr in + if sym.writable then None + else ( + (* Assume little endian here *) + assert (Relocation.IMap.is_empty sym.data.relocations); + let bv = BytesSeq.getbvle ~size sym.data.data offset in (* TODO relocations *) + Some (Typed.bits bv) + ) + with Not_found -> + let int_addr = sym_addr.offset in + let rodata = elf.rodata in + if sym_addr.section = ".rodata" && rodata.addr <= int_addr && int_addr + size < rodata.addr + rodata.size * 8 then + let bv = BytesSeq.getbvle ~size rodata.data (int_addr - rodata.addr) in + (* Assume little endian here *) + Some (Typed.bits bv) + else ( + warn "Failed to find symbol or rodata at %t" (Pp.top Elf.Address.pp sym_addr); + None + ) + ) + +let read ~provenance ?ctyp (s : t) ~(addr : Exp.t) ~(size : Mem.Size.t) : Exp.t = + assert (not @@ is_locked s); + let var = make_read ?ctyp s size in + let exp = Mem.read s.mem ~provenance ~var ~addr ~size in + let exp = if provenance = Main && exp = None then read_from_rodata ~addr ~size s else exp in + Option.iter (set_read s (Var.expect_readvar var)) exp; + Option.value exp ~default:(Exp.of_var var) let read_noprov ?ctyp (s : t) ~(addr : Exp.t) ~(size : Mem.Size.t) : Exp.t = debug "Addr: %t" Pp.(top Exp.pp addr); From c1c5cc5bcd95adb4a6c8f3826bd1e8410c6a28e8 Mon Sep 17 00:00:00 2001 From: Matej Urban Date: Thu, 10 Apr 2025 16:34:17 +0100 Subject: [PATCH 065/116] Multiple rodata sections --- src/elf/file.ml | 44 ++++++++++++++++++++++++++++---------------- src/state/base.ml | 5 +++-- 2 files changed, 31 insertions(+), 18 deletions(-) diff --git a/src/elf/file.ml b/src/elf/file.ml index 1b56d28f..6acffd47 100644 --- a/src/elf/file.ml +++ b/src/elf/file.ml @@ -75,6 +75,8 @@ let machine_to_string = function (** Pretty prints a {!machine} *) let pp_machine mach = mach |> machine_to_string |> Pp.string +module SMap = Map.Make(String) + (** The type containing all the information about an ELF file *) type t = { filename : string; (** The name on the file system. Useful for error messages *) @@ -84,7 +86,7 @@ type t = { (** 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; (** The read-only data section *) + rodata : Segment.t SMap.t; (** The read-only data sections *) } (** Error on Elf parsing *) @@ -137,21 +139,31 @@ let of_file (filename : string) = within it, and so not suitable to be stored in the [RngMap] *) let elf_file = Elf_file.ELF_File_64 elf64_file in let rodata = - let (_, addr, data) = - Dwarf.extract_section_body_without_relocations elf_file ".rodata" false - (* `false' argument is for returning an empty byte-sequence if - section is not found, instead of throwing an exception *) - in - Printf.printf "%t" Pp.(top Sym.pp addr); - Segment. - { - data; - addr = 0; (* Meaningless for relocatable files *) - size = BytesSeq.length data; - read = true; - write = false; - execute = false; - } + SMap.of_list @@ List.filter_map Option.(fun (section:Elf_interpreted_section.elf64_interpreted_section) -> + let+ sname = if String.starts_with ~prefix:".rodata" section.elf64_section_name_as_string then + Some section.elf64_section_name_as_string + else + None + in + let (_, addr, data) = + Dwarf.extract_section_body_without_relocations elf_file sname false + (* `false' argument is for returning an empty byte-sequence if + section is not found, instead of throwing an exception *) + in + Printf.printf "%t" Pp.(top Sym.pp addr); + ( + sname, + Segment. + { + data; + addr = 0; (* Meaningless for relocatable files *) + size = BytesSeq.length data; + read = true; + write = false; + execute = false; + } + ) + ) elf64_file.elf64_file_interpreted_sections in info "ELF file %s has been loaded" filename; { filename; symbols; entry; machine; linksem = elf_file; rodata } diff --git a/src/state/base.ml b/src/state/base.ml index 2099b52b..28f7d087 100644 --- a/src/state/base.ml +++ b/src/state/base.ml @@ -624,8 +624,9 @@ let read_from_rodata (s : t) ~(addr : Exp.t) ~(size : Mem.Size.t) : Exp.t option ) with Not_found -> let int_addr = sym_addr.offset in - let rodata = elf.rodata in - if sym_addr.section = ".rodata" && rodata.addr <= int_addr && int_addr + size < rodata.addr + rodata.size * 8 then + let open Option in + let* rodata = Elf.File.SMap.find_opt sym_addr.section elf.rodata in + if rodata.addr <= int_addr && int_addr + size < rodata.addr + rodata.size * 8 then let bv = BytesSeq.getbvle ~size rodata.data (int_addr - rodata.addr) in (* Assume little endian here *) Some (Typed.bits bv) From 1ed0c5ccff3561e97a70d71a4ea49fe002b8faa3 Mon Sep 17 00:00:00 2001 From: Matej Urban Date: Thu, 10 Apr 2025 17:45:41 +0100 Subject: [PATCH 066/116] Generate section address constraints --- src/run/func.ml | 2 +- src/state/base.ml | 10 +++++++++- src/state/base.mli | 2 +- 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/src/run/func.ml b/src/run/func.ml index e733d880..4ea1d385 100644 --- a/src/run/func.ml +++ b/src/run/func.ml @@ -52,7 +52,7 @@ open Logs.Logger (struct let str = __MODULE__ end) -let no_run_prep ~elf:elfname ~name ~entry ?(init = State.init_sections_symbolic) () = +let no_run_prep ~elf:elfname ~name ~entry ?(init = State.init_sections_symbolic ~addr_size:Arch.address_size) () = base "Running %s in %s" name elfname; let dwarf = Dw.of_file elfname in let elf = dwarf.elf in diff --git a/src/state/base.ml b/src/state/base.ml index 28f7d087..077f2ac4 100644 --- a/src/state/base.ml +++ b/src/state/base.ml @@ -531,6 +531,10 @@ let init_sections ~addr_size state = let _ = Option.( let+ elf = state.elf in Elf.SymTable.iter elf.symbols @@ fun sym -> + let max_section_addr = Int.shift_left 1 addr_size - sym.size - sym.addr.offset in + push_assert state Typed.( + comp Ast.Bvule (Exp.of_var (Var.Section sym.addr.section)) (bits_int ~size:64 max_section_addr) + ); let len = List.find (fun x -> sym.size mod x = 0) [16;8;4;2;1] in if sym.typ = Elf.Symbol.OBJECT then let provenance = Mem.create_section_frag ~addr_size state.mem sym.addr.section in @@ -546,11 +550,15 @@ let init_sections ~addr_size state = ) in state -let init_sections_symbolic state = +let init_sections_symbolic ~addr_size state = let state = copy_if_locked state in let _ = Option.( let+ elf = state.elf in Elf.SymTable.iter elf.symbols @@ fun sym -> + let max_section_addr = Int.shift_left 1 addr_size - sym.size - sym.addr.offset in + push_assert state Typed.( + comp Ast.Bvule (Exp.of_var (Var.Section sym.addr.section)) (bits_int ~size:64 max_section_addr) + ); if sym.typ = Elf.Symbol.OBJECT then Hashtbl.replace state.mem.sections sym.addr.section Main ) in diff --git a/src/state/base.mli b/src/state/base.mli index 73f123db..1877b17d 100644 --- a/src/state/base.mli +++ b/src/state/base.mli @@ -405,7 +405,7 @@ val copy_if_locked : ?elf:Elf.File.t -> t -> t val init_sections : addr_size:int -> t -> t (** Assigns all sections with global objects to Main fragment *) -val init_sections_symbolic : t -> t +val init_sections_symbolic : addr_size:int -> t -> t (** {1 State convenience manipulation } *) From 65a7e044b98ab53d6c686c30820688ec6e8dfef1 Mon Sep 17 00:00:00 2001 From: Matej Urban Date: Fri, 11 Apr 2025 10:05:16 +0100 Subject: [PATCH 067/116] Fix nondet --- src/state/base.ml | 7 +++++++ src/state/base.mli | 3 +++ src/trace/context.ml | 11 +++++++++-- 3 files changed, 19 insertions(+), 2 deletions(-) diff --git a/src/state/base.ml b/src/state/base.ml index 077f2ac4..30dc867e 100644 --- a/src/state/base.ml +++ b/src/state/base.ml @@ -62,6 +62,8 @@ end type id = Id.t module Var = struct + let next_nondet = ref 0 + type t = | Register of Id.t * Reg.t (** The value of this register in this state *) | ReadVar of Id.t * int * Ast.Size.t @@ -159,6 +161,11 @@ module Var = struct | RetAddr -> Ast.Ty_BitVec 64 | NonDet (_, size) -> Ast.Ty_BitVec (Ast.Size.to_bits size) | Section _ -> Ast.Ty_BitVec 64 + + let new_nondet sz = + let v = NonDet (!next_nondet, sz) in + next_nondet := !next_nondet + 1; + v end type var = Var.t diff --git a/src/state/base.mli b/src/state/base.mli index 1877b17d..a7275a7e 100644 --- a/src/state/base.mli +++ b/src/state/base.mli @@ -145,6 +145,9 @@ module Var : sig (** Get the type of a variable *) val ty : t -> Reg.ty + + (** Get a fresh NonDet variable *) + val new_nondet : Ast.Size.t -> t end (** The type of variables *) diff --git a/src/trace/context.ml b/src/trace/context.ml index f233abe9..2fe350d4 100644 --- a/src/trace/context.ml +++ b/src/trace/context.ml @@ -58,6 +58,7 @@ module SMap = Map.Make (String) type t = { reg_writes : (State.Reg.t * State.tval) Vec.t; (** Stores the delayed register writes *) mem_reads : State.tval HashVector.t; (** Stores the result of memory reads *) + nondets : State.var HashVector.t; (** Stores the mapping of nondet variables *) state : State.t; segments : State.exp SMap.t; asserts: State.exp list; @@ -68,6 +69,7 @@ type t = { let make_context ?dwarf ?relocation state = let reg_writes = Vec.empty () in let mem_reads = HashVector.empty () in + let nondets = HashVector.empty () in let segments, asserts = relocation |> Option.map (fun relocation -> @@ -82,14 +84,19 @@ let make_context ?dwarf ?relocation state = ) |> Option.value ~default:(SMap.empty, []) in - { state; reg_writes; mem_reads; dwarf; segments; asserts } + { state; reg_writes; mem_reads; nondets; dwarf; segments; asserts } (** Expand a Trace variable to a State expression, using the context *) let expand_var ~(ctxt : t) (v : Base.Var.t) (a : Ast.no Ast.ty) : State.exp = assert (Base.Var.ty v = a); match v with | Register reg -> State.get_reg_exp ctxt.state reg - | NonDet (i, _) | Read (i, _) -> (HashVector.get ctxt.mem_reads i).exp (* TODO is the NonDet case correct *) + | NonDet (i, sz) -> HashVector.get_opt ctxt.nondets i + |> Option.value_fun ~default:(fun () -> + Fun.tee (HashVector.add ctxt.nondets i) (State.Var.new_nondet sz) + ) + |> State.Exp.of_var + | Read (i, _) -> (HashVector.get ctxt.mem_reads i).exp (* TODO is the NonDet case correct *) | Segment (name, _) -> SMap.find name ctxt.segments (*TODO put the actual value there*) (* | Segment (name, sz) -> Exp.Typed.extract ~first:0 ~last:(sz-1) (State.Exp.of_var (State.Var.Section name)) TODO put the actual value there *) From 9c760c08286ada49bafce5a3cd8c13dfd6726d34 Mon Sep 17 00:00:00 2001 From: Matej Urban Date: Fri, 11 Apr 2025 10:36:29 +0100 Subject: [PATCH 068/116] Fix symbolic bytes subranges Subranges were extracted as little and concatenated as big endian Now both are little endian --- src/state/symbolicBytes.ml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/state/symbolicBytes.ml b/src/state/symbolicBytes.ml index 2693b0b3..07a43622 100644 --- a/src/state/symbolicBytes.ml +++ b/src/state/symbolicBytes.ml @@ -126,6 +126,7 @@ module Make (Var : Exp.Var) : S with type var = Var.t = struct assert (pos + len <= elen); (Typed.extract ~last:((8 * (pos + len)) - 1) ~first:(8 * pos) e, len) + (* TODO should we care about endianness? *) (* Warning: This code is complicated because of all the indices. I tried to make diagrams to explain *) let sub ~pos ~len sb = @@ -166,7 +167,7 @@ module Make (Var : Exp.Var) : S with type var = Var.t = struct Some [Typed.extract ~last:((8 * taken_len) - 1) ~first:(8 * off) e] in let+ list = sub_list ~pos ~len sb in - Typed.concat list + Typed.concat @@ List.rev list let blit_exp exp ~pos ~len sb = assert (len > 0); From 63d3ebb1461f1b44ae43c4ea191520bb95d623b9 Mon Sep 17 00:00:00 2001 From: Matej Urban Date: Fri, 11 Apr 2025 13:23:58 +0100 Subject: [PATCH 069/116] Hack addres-size extract of constexpr --- src/trace/typer.ml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/trace/typer.ml b/src/trace/typer.ml index 238a17ef..25dc0a92 100644 --- a/src/trace/typer.ml +++ b/src/trace/typer.ml @@ -106,11 +106,13 @@ let unop (u : Ast.unop) tval : Ctype.t option = | Bvneg | Bvnot -> machine_of_size typ |> some | Extract (b, a) -> debug "Extracting from type %t" Pp.(top (opt Ctype.pp) tval.ctyp); - if (* HACK for adrp: a = 0 && b = Arch.address_size - 1 &&*) Ctype.is_ptr typ then tval.ctyp + if (* HACK for adrp: a = 0 && b = Arch.address_size - 1 &&*) Ctype.is_ptr typ then + tval.ctyp else let bitsize = b - a + 1 in let constexpr = typ.constexpr in - if bitsize mod 8 = 0 then Ctype.machine ~constexpr (bitsize / 8) |> some else None + if bitsize mod 8 = 0 || bitsize = Arch.address_size && constexpr then + Ctype.machine ~constexpr (bitsize / 8) |> some else None | ZeroExtend m | SignExtend m -> if m mod 8 = 0 then machine_of_size ~update:(m / 8) typ |> some else None From 34b6c323a705cce2ce1643724f0215202ac2f37a Mon Sep 17 00:00:00 2001 From: Matej Urban Date: Fri, 11 Apr 2025 13:33:33 +0100 Subject: [PATCH 070/116] Testing: Warn about read variables --- src/run/testRelProg.ml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/run/testRelProg.ml b/src/run/testRelProg.ml index 1f4b396d..067e3799 100644 --- a/src/run/testRelProg.ml +++ b/src/run/testRelProg.ml @@ -14,6 +14,14 @@ let test return_register exit_register name = let ret = State.Reg.of_string return_register in let ext = State.Reg.of_string exit_register in State.Tree.iter (fun l st -> + let found_symread = ref false in + st.read_vars |> Vec.iter Fun.(State.Tval.exp %> Ast.Manip.exp_iter_var (fun v -> + match v with + | State.Var.ReadVar _ -> found_symread := true; warn "State contains symbolic read variable:\n %t" (Pp.top State.Var.pp v) + | _ -> () + )); + if !found_symread then + warn "State:\n%t" (Pp.top State.pp st); if State.is_possible st then match l with | Block_lib.End _ -> ( From d65c184a7642a5edffdf73f9893c5555386bc2ea Mon Sep 17 00:00:00 2001 From: Matej Urban Date: Fri, 11 Apr 2025 14:33:31 +0100 Subject: [PATCH 071/116] More typer hacking New bits type for bit fragments that are not whole-byte size --- src/ctype/ctype.ml | 3 +++ src/run/relProg.ml | 1 + src/trace/typer.ml | 52 +++++++++++++++++++++++++++++++++++++++++----- 3 files changed, 51 insertions(+), 5 deletions(-) diff --git a/src/ctype/ctype.ml b/src/ctype/ctype.ml index 1c876f37..dbbe61f8 100644 --- a/src/ctype/ctype.ml +++ b/src/ctype/ctype.ml @@ -120,6 +120,7 @@ type unqualified = | Enum of { name : string; id : int } (** See {!env} for what the id refers to *) | FuncPtr (** Hack to accommodate PKVM *) | Missing (** Hack to accommodate PKVM *) + | Bits (** Hack to prevent losing type information when processing bitvectors with non-whole-byte sizes *) (** The internal representation of generalized C types *) and t = { @@ -337,6 +338,7 @@ let rec sizeof_unqualified = function | Array { elem; dims } -> let num = dims |> List.map (Option.value ~default:0) |> List.fold_left ( * ) 1 in num * sizeof elem + | Bits -> 0 (* Shouldn't use this value *) (** Give the size of an type. Need the environement. *) and sizeof t = sizeof_unqualified t.unqualified @@ -660,6 +662,7 @@ and pp_unqualified = function | Enum { name; _ } -> dprintf "Enum %s" name | FuncPtr -> dprintf "FuncPtr" | Missing -> dprintf "Missing" + | Bits -> dprintf "Bits" and pp_fragment frag = group diff --git a/src/run/relProg.ml b/src/run/relProg.ml index 7bf90660..7170b132 100644 --- a/src/run/relProg.ml +++ b/src/run/relProg.ml @@ -39,6 +39,7 @@ let pp_typed ~(tenv: Ctype.env) ~(ctype: Ctype.t) ~pp (value: State.Exp.t) = | Enum _ -> pp value | FuncPtr -> pp value | Missing -> pp value + | Bits -> pp value let read_big st addr sz = Seq.iota_step_up ~step:16 ~endi:sz diff --git a/src/trace/typer.ml b/src/trace/typer.ml index 25dc0a92..2dd8e1a2 100644 --- a/src/trace/typer.ml +++ b/src/trace/typer.ml @@ -98,6 +98,24 @@ let machine_of_size ?(update = 0) (typ : Ctype.t) : Ctype.t = let constexpr = typ.constexpr in Ctype.machine ~constexpr (size + update) +let is_const tval = + tval.ctyp + |> Option.map Ctype.is_constexpr + |> Option.value_fun ~default:(fun () -> Exp.ConcreteEval.is_concrete tval.exp) + +let constexpr_of_exp e = + let ty = Exp.Typed.get_type e in + if Exp.Typed.is_bv ty then + let bitsize = Exp.Typed.expect_bv ty in + Option.some @@ + if bitsize mod 8 = 0 || bitsize = Arch.address_size then + Ctype.machine ~constexpr:true (bitsize / 8) + else + Ctype.Bits |> Ctype.qual ~constexpr:true + else + None + + let unop (u : Ast.unop) tval : Ctype.t option = let open Option in let* typ = tval.ctyp in @@ -111,8 +129,7 @@ let unop (u : Ast.unop) tval : Ctype.t option = else let bitsize = b - a + 1 in let constexpr = typ.constexpr in - if bitsize mod 8 = 0 || bitsize = Arch.address_size && constexpr then - Ctype.machine ~constexpr (bitsize / 8) |> some else None + if bitsize mod 8 = 0 then Ctype.machine ~constexpr (bitsize / 8) |> some else None | ZeroExtend m | SignExtend m -> if m mod 8 = 0 then machine_of_size ~update:(m / 8) typ |> some else None @@ -214,12 +231,37 @@ let rec expr ~ctxt (exp : Base.exp) : Ctype.t option = | Bool _ -> None | Enum _ -> None | Vec _ -> None - | Unop (u, e, _) -> expr_tval ~ctxt e |> unop u + | Unop (u, e, _) -> + let tval = expr_tval ~ctxt e in + Option.( + unop u tval + ||| + if is_const tval then + constexpr_of_exp exp + else + None + ) | Binop (b, e, e', _) -> let te = expr_tval ~ctxt e in let te' = expr_tval ~ctxt e' in - binop ~ctxt b te te' - | Manyop (m, el, _) -> List.map (expr_tval ~ctxt) el |> manyop ~ctxt m + Option.( + binop ~ctxt b te te' + ||| + if is_const te && is_const te' then + constexpr_of_exp exp + else + None + ) + | Manyop (m, el, _) -> + let tvals = List.map (expr_tval ~ctxt) el in + Option.( + manyop ~ctxt m tvals + ||| + if List.for_all is_const tvals then + constexpr_of_exp exp + else + None + ) | Ite _ -> None | Bound _ -> . | Let _ -> . From af5e7fe5ab2b500d71fe742c961c6c73bba0e1ee Mon Sep 17 00:00:00 2001 From: Matej Urban Date: Mon, 14 Apr 2025 10:18:22 +0100 Subject: [PATCH 072/116] Better section asserts --- src/elf/file.ml | 23 ++++++++++++++++++----- src/state/base.ml | 32 ++++++++++++++++++++++++-------- 2 files changed, 42 insertions(+), 13 deletions(-) diff --git a/src/elf/file.ml b/src/elf/file.ml index 6acffd47..3dcc5291 100644 --- a/src/elf/file.ml +++ b/src/elf/file.ml @@ -77,6 +77,12 @@ let pp_machine mach = mach |> machine_to_string |> Pp.string module SMap = Map.Make(String) +type section = { + name : string; + size : int; + align : int; +} + (** The type containing all the information about an ELF file *) type t = { filename : string; (** The name on the file system. Useful for error messages *) @@ -87,6 +93,7 @@ type t = { 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; } (** Error on Elf parsing *) @@ -138,10 +145,16 @@ let of_file (filename : string) = - the range of the section is guaranteed to overlap with any symbols within it, and so not suitable to be stored in the [RngMap] *) let elf_file = Elf_file.ELF_File_64 elf64_file in + let sections = List.map (fun (s:Elf_interpreted_section.elf64_interpreted_section) -> { + name=s.elf64_section_name_as_string; + size=Z.to_int s.elf64_section_size; + align=Z.to_int s.elf64_section_align; + }) elf64_file.elf64_file_interpreted_sections + in let rodata = - SMap.of_list @@ List.filter_map Option.(fun (section:Elf_interpreted_section.elf64_interpreted_section) -> - let+ sname = if String.starts_with ~prefix:".rodata" section.elf64_section_name_as_string then - Some section.elf64_section_name_as_string + SMap.of_list @@ List.filter_map Option.(fun section -> + let+ sname = if String.starts_with ~prefix:".rodata" section.name then + Some section.name else None in @@ -163,7 +176,7 @@ let of_file (filename : string) = execute = false; } ) - ) elf64_file.elf64_file_interpreted_sections + ) sections in info "ELF file %s has been loaded" filename; - { filename; symbols; entry; machine; linksem = elf_file; rodata } + { filename; symbols; entry; machine; linksem = elf_file; rodata; sections } diff --git a/src/state/base.ml b/src/state/base.ml index 30dc867e..d6e93053 100644 --- a/src/state/base.ml +++ b/src/state/base.ml @@ -533,15 +533,34 @@ let set_impossible state = assert (not @@ is_locked state); state.asserts <- [Typed.false_] +let push_section_constraints ~addr_size state sections = + List.iter (fun (s:Elf.File.section) -> + let max_section_addr = Int.shift_left 1 addr_size - s.size in + let s_exp = (Exp.of_var (Var.Section s.name)) in + (* The whole section fits in memory *) + push_assert state Typed.(comp Ast.Bvule s_exp (bits_int ~size:64 max_section_addr)); + (* The load address cannot be 0 *) + push_assert state Typed.(not (s_exp = (bits_int ~size:64 0))); + if s.align > 1 then + let (align_pow, _) = Seq.ints 0 + |> Seq.drop_while (fun x -> Int.shift_left 1 x < s.align) + |> Seq.uncons + |> Option.get + in + if s.align = Int.shift_left 1 align_pow then + let last = align_pow - 1 in + (* Section address is aligned *) + push_assert state Typed.(extract ~first:0 ~last s_exp = zero ~size:align_pow) + else + warn "Section alignment is not a power of two: %d" s.align; + ) sections + let init_sections ~addr_size state = let state = copy_if_locked state in let _ = Option.( let+ elf = state.elf in + push_section_constraints ~addr_size state elf.sections; Elf.SymTable.iter elf.symbols @@ fun sym -> - let max_section_addr = Int.shift_left 1 addr_size - sym.size - sym.addr.offset in - push_assert state Typed.( - comp Ast.Bvule (Exp.of_var (Var.Section sym.addr.section)) (bits_int ~size:64 max_section_addr) - ); let len = List.find (fun x -> sym.size mod x = 0) [16;8;4;2;1] in if sym.typ = Elf.Symbol.OBJECT then let provenance = Mem.create_section_frag ~addr_size state.mem sym.addr.section in @@ -561,11 +580,8 @@ let init_sections_symbolic ~addr_size state = let state = copy_if_locked state in let _ = Option.( let+ elf = state.elf in + push_section_constraints ~addr_size state elf.sections; Elf.SymTable.iter elf.symbols @@ fun sym -> - let max_section_addr = Int.shift_left 1 addr_size - sym.size - sym.addr.offset in - push_assert state Typed.( - comp Ast.Bvule (Exp.of_var (Var.Section sym.addr.section)) (bits_int ~size:64 max_section_addr) - ); if sym.typ = Elf.Symbol.OBJECT then Hashtbl.replace state.mem.sections sym.addr.section Main ) in From 22996d3917b72d825e3ed5a70383493fa454cd6c Mon Sep 17 00:00:00 2001 From: Matej Urban Date: Mon, 14 Apr 2025 18:32:05 +0100 Subject: [PATCH 073/116] Better logs --- src/run/relProg.ml | 19 +++++-- src/run/testRelProg.ml | 111 ++++++++++++++++++++++++++++------------- src/state/simplify.ml | 11 +++- 3 files changed, 99 insertions(+), 42 deletions(-) diff --git a/src/run/relProg.ml b/src/run/relProg.ml index 7170b132..bb7b2edf 100644 --- a/src/run/relProg.ml +++ b/src/run/relProg.ml @@ -41,12 +41,15 @@ let pp_typed ~(tenv: Ctype.env) ~(ctype: Ctype.t) ~pp (value: State.Exp.t) = | Missing -> pp value | Bits -> pp value -let read_big st addr sz = +let read_big ~prov st addr sz = + let addr = Exp.Typed.extract ~last:(Arch.address_size-1) ~first:0 addr in Seq.iota_step_up ~step:16 ~endi:sz |> Seq.map (fun off -> let addr = Exp.Typed.(addr + bits_int ~size:Arch.address_size off) in let len = min 16 (sz - off) in - State.read_noprov st ~addr ~size:(Ast.Size.of_bytes len) + match prov with + | None -> State.read_noprov st ~addr ~size:(Ast.Size.of_bytes len) + | Some p -> State.read ~provenance:p st ~addr:addr ~size:(Ast.Size.of_bytes len) ) |> List.of_seq |> Exp.Typed.concat @@ -55,14 +58,20 @@ let pp_eval_loc sz st ~(tenv: Ctype.env) ~(ctype: Ctype.t) (loc: Dw.Loc.t) : PPr let value = match loc with | Register reg -> Some (State.get_reg_exp st reg) | RegisterOffset (reg, off) -> - let r = State.get_reg_exp st reg in - Some (read_big st Exp.Typed.(r + bits_int ~size:Arch.address_size off) sz) + let r = State.get_reg st reg in + let open Ctype in + let prov = Option.bind r.ctyp (fun ctype -> + match ctype.unqualified with + | Ptr { provenance; _ } -> Some provenance + | _ -> None + ) in + Some (read_big ~prov st Exp.Typed.(r.exp + bits_int ~size:64 off) sz) | StackFrame _off -> None | Global symoff -> let addr = Elf.SymTable.to_addr_offset symoff in let addr = State.Exp.of_address ~size:Arch.address_size addr in - Some (read_big st addr sz) + Some (read_big ~prov:None st addr sz) | Const x -> Some(x |> BitVec.of_z ~size:(8*sz) |> Exp.Typed.bits) | Dwarf _ops -> None in let pp = fun value -> diff --git a/src/run/testRelProg.ml b/src/run/testRelProg.ml index 067e3799..3ac67b19 100644 --- a/src/run/testRelProg.ml +++ b/src/run/testRelProg.ml @@ -5,6 +5,75 @@ open Logs.Logger (struct let str = __MODULE__ end) +type err = { msg: string; asserts: State.Exp.t list } +type node_result = { all_fail: bool; errors: err list } +let node_result_of_result = function +| Ok () -> { all_fail=false; errors=[] } +| Error e -> { all_fail=true; errors=[e] } + +let rec process_tree ~pc ~ret ~ext (node:Block_lib.label State.Tree.t) = + let l = node.data in + let st = node.state in + + let found_symread = ref false in + st.read_vars |> Vec.iter Fun.(State.Tval.exp %> Ast.Manip.exp_iter_var (fun v -> + match v with + | State.Var.ReadVar _ -> found_symread := true; warn "State contains symbolic read variable:\n %t" (Pp.top State.Var.pp v) + | _ -> () + )); + if !found_symread then + warn "State:\n%t" (Pp.top State.pp st); + + if not (State.is_possible st) then + { all_fail=true; errors=[] } + else match l with + | Block_lib.End _ -> let result = ( + let pc_exp = State.get_reg_exp st pc in + let pc_addr = try + Some (State.Exp.expect_sym_address pc_exp) + with + _ -> None + in + let ret_exp = match pc_addr with + | Some pc_addr -> + if pc_addr = Elf.Address.{ section="UND.abort"; offset=0 } then + Result.error { + msg=Printf.sprintf "abort called from %t" (Pp.tos Elf.Address.pp st.last_pc); + asserts=st.asserts; + } + else if pc_addr <> Elf.Address.{ section="UND.exit"; offset=0 } then + Result.error { + msg=Printf.sprintf "finished at weird address %t" (Pp.tos Elf.Address.pp pc_addr); + asserts=st.asserts; + } + else + Result.ok (State.get_reg_exp st ext) + | None -> + Result.ok (State.get_reg_exp st ret) (* Symbolic pc = returned from main *) + in + Result.bind ret_exp @@ fun ret_exp -> + let ret_val = ret_exp |> Exp.ConcreteEval.eval |> Exp.Value.expect_bv |> BitVec.to_int in + if ret_val <> 0 then + Result.error { + msg=Printf.sprintf "non-zero return code %d" ret_val; + asserts=st.asserts + } + else + Result.ok () + ) in + node_result_of_result result + | _ -> + let results = List.map (process_tree ~pc ~ret ~ext) node.rest in + let all_errors = List.bind results (fun x -> x.errors) in + if List.for_all (fun x -> x.all_fail) results then { + all_fail=true; + errors=List.map (fun x -> {x with asserts=st.asserts}) all_errors; + } + else { + all_fail=false; + errors=all_errors; + } + let test return_register exit_register name = let tree = Func.get_state_tree ~elf:name ~name:"main" ~init:(State.init_sections ~addr_size:Arch.address_size) ~every_instruction:false () ~breakpoints:["UND.abort"; "UND.exit"] @@ -13,41 +82,13 @@ let test return_register exit_register name = let pc = Arch.pc () in let ret = State.Reg.of_string return_register in let ext = State.Reg.of_string exit_register in - State.Tree.iter (fun l st -> - let found_symread = ref false in - st.read_vars |> Vec.iter Fun.(State.Tval.exp %> Ast.Manip.exp_iter_var (fun v -> - match v with - | State.Var.ReadVar _ -> found_symread := true; warn "State contains symbolic read variable:\n %t" (Pp.top State.Var.pp v) - | _ -> () - )); - if !found_symread then - warn "State:\n%t" (Pp.top State.pp st); - if State.is_possible st then - match l with - | Block_lib.End _ -> ( - let pc_exp = State.get_reg_exp st pc in - let ret_exp = match (try - Some (State.Exp.expect_sym_address pc_exp) - with - _ -> None - ) with - | Some pc_addr -> - if pc_addr = Elf.Address.{ section="UND.abort"; offset=0 } then - fail "abort called from %t" (Pp.top Elf.Address.pp st.last_pc) - else if pc_addr <> Elf.Address.{ section="UND.exit"; offset=0 } then - fail "finished at weird address %t" (Pp.top Elf.Address.pp pc_addr) - else - State.get_reg_exp st ext - | None -> - State.get_reg_exp st ret (* Symbolic pc = returned from main *) - in - let ret_val = ret_exp |> Exp.ConcreteEval.eval |> Exp.Value.expect_bv |> BitVec.to_int in - if ret_val <> 0 then - fail "non-zero return code %d" ret_val; - ) - | _ -> () - ) tree; - base "Success" + let results = process_tree ~pc ~ret ~ext tree in + if List.is_empty results.errors then + base "Success" + else + fail "Some paths fail: %t" Pp.( + top (list (fun (e:err) -> !^(e.msg) ^^ !^" when " ^^ list State.Exp.pp e.asserts)) results.errors + ) let elf = let doc = "ELF file from which to pull the code" in diff --git a/src/state/simplify.ml b/src/state/simplify.ml index 3fee6e92..14088110 100644 --- a/src/state/simplify.ml +++ b/src/state/simplify.ml @@ -90,11 +90,18 @@ let ctxfull state = (fun e -> declare e; match Z3St.check_both serv e with - | Some true -> None + | Some true -> + debug "%t is redundant" (Pp.top Exp.pp e); + None | Some false -> found_false := true; + debug "%t is impossible" (Pp.top Exp.pp e); None - | None -> Some e) + | None -> + debug "%t is possible" (Pp.top Exp.pp e); + Z3St.send_assert serv e; + Some e + ) state.asserts in (* If state is impossible then it has a single assertion: false *) From 0cf6b06ff2ab5f52af4bfcc2f5227805f7b26ec9 Mon Sep 17 00:00:00 2001 From: Matej Urban Date: Mon, 14 Apr 2025 20:41:48 +0100 Subject: [PATCH 074/116] Fix provenance with section fragments --- src/state/base.ml | 50 ++++++++++++++++++++++++++++++++--------------- 1 file changed, 34 insertions(+), 16 deletions(-) diff --git a/src/state/base.ml b/src/state/base.ml index d6e93053..18c52de8 100644 --- a/src/state/base.ml +++ b/src/state/base.ml @@ -320,24 +320,26 @@ module Mem = struct mutable main : Fragment.t; frags : (Exp.t * Fragment.t) Vec.t; sections : (string, provenance) Hashtbl.t; (* mapping sections to their fragments *) + mutable allow_main : bool; (* HACK to prvent incorrectly assuming Main provenance when using section fragments *) } (** Get the main fragment of memory *) - let get_main { main; frags = _; sections = _ } = main + let get_main { main; _ } = main (** Empty memory, every address is unbound *) - let empty () = { main = Fragment.empty; frags = Vec.empty (); sections = Hashtbl.create 10 } + let empty () = { main = Fragment.empty; frags = Vec.empty (); sections = Hashtbl.create 10; allow_main = true } (** Build a new memory from the old one by keeping the old one as a base *) let from mem = { main = Fragment.from mem.main; frags = Vec.map (Pair.map Fun.id Fragment.from) mem.frags; - sections = Hashtbl.copy mem.sections; + sections = Hashtbl.copy mem.sections; + allow_main = mem.allow_main; } (** Copy the memory so that it can be mutated separately *) - let copy mem = { main = mem.main; frags = Vec.copy mem.frags; sections = Hashtbl.copy mem.sections } + let copy mem = { main = mem.main; frags = Vec.copy mem.frags; sections = Hashtbl.copy mem.sections; allow_main = mem.allow_main } (** Add a new fragment with the specified base *) let new_frag mem base = @@ -559,11 +561,13 @@ let init_sections ~addr_size state = let state = copy_if_locked state in let _ = Option.( let+ elf = state.elf in + state.mem.allow_main <- false; push_section_constraints ~addr_size state elf.sections; + List.iter (fun (x:Elf.File.section) -> Mem.create_section_frag ~addr_size state.mem x.name |> ignore) elf.sections; Elf.SymTable.iter elf.symbols @@ fun sym -> let len = List.find (fun x -> sym.size mod x = 0) [16;8;4;2;1] in if sym.typ = Elf.Symbol.OBJECT then - let provenance = Mem.create_section_frag ~addr_size state.mem sym.addr.section in + let provenance = Mem.get_section_provenance state.mem sym.addr.section in Seq.iota_step_up ~step:len ~endi:sym.size |> Seq.iter (fun off -> let data = Elf.Symbol.sub sym off len in @@ -667,15 +671,18 @@ let read_from_rodata (s : t) ~(addr : Exp.t) ~(size : Mem.Size.t) : Exp.t option ) ) -let read ~provenance ?ctyp (s : t) ~(addr : Exp.t) ~(size : Mem.Size.t) : Exp.t = +let rec read ~provenance ?ctyp (s : t) ~(addr : Exp.t) ~(size : Mem.Size.t) : Exp.t = assert (not @@ is_locked s); - let var = make_read ?ctyp s size in - let exp = Mem.read s.mem ~provenance ~var ~addr ~size in - let exp = if provenance = Main && exp = None then read_from_rodata ~addr ~size s else exp in - Option.iter (set_read s (Var.expect_readvar var)) exp; - Option.value exp ~default:(Exp.of_var var) - -let read_noprov ?ctyp (s : t) ~(addr : Exp.t) ~(size : Mem.Size.t) : Exp.t = + if provenance = Ctype.Main && not s.mem.allow_main then + read_noprov ?ctyp s ~addr ~size + else + let var = make_read ?ctyp s size in + let exp = Mem.read s.mem ~provenance ~var ~addr ~size in + let exp = if exp = None then read_from_rodata ~addr ~size s else exp in + Option.iter (set_read s (Var.expect_readvar var)) exp; + Option.value exp ~default:(Exp.of_var var) + +and read_noprov ?ctyp (s : t) ~(addr : Exp.t) ~(size : Mem.Size.t) : Exp.t = debug "Addr: %t" Pp.(top Exp.pp addr); let elf_addr = eval_address s addr in debug "Address: %t" Pp.(top (optional Elf.Address.pp) elf_addr); @@ -684,16 +691,23 @@ let read_noprov ?ctyp (s : t) ~(addr : Exp.t) ~(size : Mem.Size.t) : Exp.t = let addr_size = addr |> Typed.get_type |> Typed.expect_bv in let addr = Exp.of_address ~size:addr_size elf_addr in let provenance = Mem.get_section_provenance s.mem elf_addr.section in + if provenance = Ctype.Main && not s.mem.allow_main then + Raise.fail "Main fragment should not be used here"; read ~provenance ?ctyp s ~addr ~size | None when Vec.length s.mem.frags = 0 -> + if not s.mem.allow_main then + Raise.fail "Main fragment should not be used here"; read ~provenance:Ctype.Main ?ctyp s ~addr ~size | None -> Raise.fail "Trying to access %t in state %d: No provenance info" Pp.(tos Exp.pp addr) s.id -let write ~provenance (s : t) ~(addr : Exp.t) ~(size : Mem.Size.t) (value : Exp.t) : unit = +let rec write ~provenance (s : t) ~(addr : Exp.t) ~(size : Mem.Size.t) (value : Exp.t) : unit = assert (not @@ is_locked s); - Mem.write ~provenance s.mem ~addr ~size ~exp:value + if provenance = Ctype.Main && not s.mem.allow_main then + write_noprov s ~addr ~size value + else + Mem.write ~provenance s.mem ~addr ~size ~exp:value -let write_noprov (s : t) ~(addr : Exp.t) ~(size : Mem.Size.t) (value : Exp.t) : unit = +and write_noprov (s : t) ~(addr : Exp.t) ~(size : Mem.Size.t) (value : Exp.t) : unit = let elf_addr = eval_address s addr in debug "Address: %t" Pp.(top (optional Elf.Address.pp) elf_addr); match elf_addr with @@ -701,8 +715,12 @@ let write_noprov (s : t) ~(addr : Exp.t) ~(size : Mem.Size.t) (value : Exp.t) : let addr_size = addr |> Typed.get_type |> Typed.expect_bv in let addr = Exp.of_address ~size:addr_size elf_addr in let provenance = Mem.get_section_provenance s.mem elf_addr.section in + if provenance = Ctype.Main && not s.mem.allow_main then + Raise.fail "Main fragment should not be used here"; write ~provenance s ~addr ~size value | None when Vec.length s.mem.frags = 0 -> + if not s.mem.allow_main then + Raise.fail "Main fragment should not be used here"; write ~provenance:Ctype.Main s ~addr ~size value | None -> Raise.fail "Trying to access %t in state %d: No provenance info" Pp.(tos Exp.pp addr) s.id From 1f25a8fcdda7a1b930193e72587e925a4c0508f3 Mon Sep 17 00:00:00 2001 From: Matej Urban Date: Thu, 24 Apr 2025 13:20:54 +0100 Subject: [PATCH 075/116] Make fbreg locations work --- src/dw/var.ml | 15 +++++++++++-- src/run/relProg.ml | 52 ++++++++++++++++++++++++++++++++-------------- 2 files changed, 49 insertions(+), 18 deletions(-) diff --git a/src/dw/var.ml b/src/dw/var.ml index b6010848..a6a28c6c 100644 --- a/src/dw/var.ml +++ b/src/dw/var.ml @@ -48,7 +48,13 @@ type range = Addr.t * Addr.t option (** Type of a DWARF variable *) -type t = { name : string; param : bool; ctype : Ctype.t; locs : (range * Loc.t) list } +type t = { + name : string; + param : bool; + ctype : Ctype.t; + locs : (range * Loc.t) list; + locs_frame_base : (range * Loc.t) list; +} (** Type of a DWARF variable in linksem *) type linksem_t = Dwarf.sdt_variable_or_formal_parameter @@ -78,7 +84,12 @@ let of_linksem (elf : Elf.File.t) (env : Ctype.env) (lvar : linksem_t) : t = |> List.map (fun (a, b, l) -> ((Addr.of_sym a, end_addr_of_sym b), Loc.of_linksem elf l)) |> loc_merge in - { name; param; ctype; locs } + let locs_frame_base = + lvar.svfp_locations_frame_base |> Option.value ~default:[] + |> List.map (fun (a, b, l) -> ((Addr.of_sym a, end_addr_of_sym b), Loc.of_linksem elf l)) + |> loc_merge + in + { name; param; ctype; locs; locs_frame_base } (** Pretty print a variable *) let pp_raw v = diff --git a/src/run/relProg.ml b/src/run/relProg.ml index bb7b2edf..aeca7ac6 100644 --- a/src/run/relProg.ml +++ b/src/run/relProg.ml @@ -54,8 +54,8 @@ let read_big ~prov st addr sz = |> List.of_seq |> Exp.Typed.concat -let pp_eval_loc sz st ~(tenv: Ctype.env) ~(ctype: Ctype.t) (loc: Dw.Loc.t) : PPrint.document = - let value = match loc with +let eval_loc ?frame_value sz st (loc: Dw.Loc.t) : State.Exp.t option = + match loc with | Register reg -> Some (State.get_reg_exp st reg) | RegisterOffset (reg, off) -> let r = State.get_reg st reg in @@ -66,14 +66,40 @@ let pp_eval_loc sz st ~(tenv: Ctype.env) ~(ctype: Ctype.t) (loc: Dw.Loc.t) : PPr | _ -> None ) in Some (read_big ~prov st Exp.Typed.(r.exp + bits_int ~size:64 off) sz) - | StackFrame _off -> - None + | StackFrame off -> + (* This is a bit hacky, should instead extract the provenance from frame_value *) + let stack_provenance = Option.bind (State.get_reg st (Arch.sp())).ctyp (fun ctype -> + match ctype.unqualified with + | Ptr { provenance; _ } -> Some provenance + | _ -> None + ) in + + let open Option in + let+ frame_value = frame_value in + debug "Reading from %t" Pp.(top State.Exp.pp Exp.Typed.(frame_value + bits_int ~size:64 off)); + read_big ~prov:stack_provenance st Exp.Typed.(frame_value + bits_int ~size:64 off) sz | Global symoff -> let addr = Elf.SymTable.to_addr_offset symoff in let addr = State.Exp.of_address ~size:Arch.address_size addr in Some (read_big ~prov:None st addr sz) | Const x -> Some(x |> BitVec.of_z ~size:(8*sz) |> Exp.Typed.bits) - | Dwarf _ops -> None in + | Dwarf _ops -> None + +let eval_loc_from_list ?frame_value sz st pc locs= + let open Option in + let+ loc = List.find_map (fun ((lo,hi), loc) -> ( + let open Elf.Address in + let* hi = hi in + let* over = lo <= pc in + let* under = pc < hi in + if over && under then + Some loc + else + None + )) locs in + eval_loc ?frame_value sz st loc + +let pp_variable_value ~(tenv: Ctype.env) ~(ctype: Ctype.t) value = let pp = fun value -> match Exp.ConcreteEval.eval_if_concrete value with | Some(value) -> Exp.Value.pp value @@ -88,18 +114,12 @@ let printvars ~st ~(dwarf: Dw.t) pc = let pv vars = Seq.iter (fun (v: Dw.Var.t) -> let sz = Ctype.sizeof v.ctype in - match List.find_map (fun ((lo,hi), loc) -> Option.( - let open Elf.Address in - let* hi = hi in - let* over = lo <= pc in - let* under = pc < hi in - if over && under then - Some loc - else - None - )) v.locs with + let frame_value = eval_loc_from_list sz st pc v.locs_frame_base |> Option.join in + debug "Frame value %t" Pp.(top (optional State.Exp.pp) frame_value); + let value = eval_loc_from_list ?frame_value sz st pc v.locs in + match value with | None -> () - | Some loc -> out := !out ^ Printf.sprintf "%s = %t\n" v.name Pp.(tos (pp_eval_loc sz st ~ctype:v.ctype ~tenv:dwarf.tenv) loc); + | Some var_val -> out := !out ^ Printf.sprintf "%s = %t\n" v.name Pp.(tos (pp_variable_value ~ctype:v.ctype ~tenv:dwarf.tenv) var_val); ) vars in From 0ff8cc9510fc77ef1b9ab65496d4b31b5955b045 Mon Sep 17 00:00:00 2001 From: Matej Urban Date: Sun, 27 Apr 2025 11:44:58 +0100 Subject: [PATCH 076/116] Fix read rodata --- src/state/base.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/state/base.ml b/src/state/base.ml index 18c52de8..ac324ec0 100644 --- a/src/state/base.ml +++ b/src/state/base.ml @@ -661,7 +661,7 @@ let read_from_rodata (s : t) ~(addr : Exp.t) ~(size : Mem.Size.t) : Exp.t option let int_addr = sym_addr.offset in let open Option in let* rodata = Elf.File.SMap.find_opt sym_addr.section elf.rodata in - if rodata.addr <= int_addr && int_addr + size < rodata.addr + rodata.size * 8 then + if rodata.addr <= int_addr && int_addr + size <= rodata.addr + rodata.size * 8 then let bv = BytesSeq.getbvle ~size rodata.data (int_addr - rodata.addr) in (* Assume little endian here *) Some (Typed.bits bv) From 38f9e1146b9a416a2a4735f95b0d989a0e64ed09 Mon Sep 17 00:00:00 2001 From: Matej Urban Date: Sun, 27 Apr 2025 11:45:45 +0100 Subject: [PATCH 077/116] Fix printing execution --- src/run/relProg.ml | 40 +++++++++++++++++++++------------------- 1 file changed, 21 insertions(+), 19 deletions(-) diff --git a/src/run/relProg.ml b/src/run/relProg.ml index aeca7ac6..5526eb21 100644 --- a/src/run/relProg.ml +++ b/src/run/relProg.ml @@ -165,25 +165,27 @@ let run_prog elfname name objdump_d branchtables = let get_footprint pc = Runner.get_normal_opt runner pc |> Option.fold ~none:[] ~some:Trace.Instr.footprint in *) - State.Tree.iter - (fun a st -> - let last_pc = st.last_pc in - (match a with - | Block_lib.Start -> () - | Block_lib.BranchAt pc -> - if Elf.Address.(last_pc + 4 <> pc) then - Printf.printf "\nJUMP from %t:\n\n" Pp.(top Elf.Address.pp last_pc); - print_string @@ Analyse.Pp.css Analyse.Types.Html Render_vars @@ printvars ~st ~dwarf pc; - print_string (print_analyse_instruction pc); - print_endline "BRANCH!"; - | Block_lib.NormalAt pc -> - if Elf.Address.(last_pc + 4 <> pc) then - Printf.printf "\nJUMP from %t:\n\n" Pp.(top Elf.Address.pp last_pc); - print_string @@ Analyse.Pp.css Analyse.Types.Html Render_vars @@ printvars ~st ~dwarf pc; - print_string (print_analyse_instruction pc); - | Block_lib.End _ -> ()); - ) - tree; + let rec iter (f:Block_lib.label State.Tree.t) = + let st = f.state in + let last_pc = st.last_pc in + (match f.data with + | Block_lib.Start -> () + | Block_lib.BranchAt pc | Block_lib.NormalAt pc -> + if Elf.Address.(last_pc + 4 <> pc) then + Printf.printf "\nJUMP from %t:\n\n" Pp.(top Elf.Address.pp last_pc); + print_string @@ Analyse.Pp.css Analyse.Types.Html Render_vars @@ printvars ~st ~dwarf pc; + print_string (print_analyse_instruction pc); + | Block_lib.End _ -> + print_string "END"; + ); + let succ = List.filter (fun (s:Block_lib.label State.Tree.t) -> + State.is_possible s.state + ) f.rest in + if List.length succ > 1 then + print_string "BRANCH!"; + List.iter iter succ + in + iter tree; match Analyse.Utils.read_file_lines "src/analyse/html-postamble.html" with | Error _ -> () | Ok lines -> Array.iter (function s -> Printf.printf "%s\n" s) lines From 19343af0ac199093efc34c49d4f4260d2a0ccf6a Mon Sep 17 00:00:00 2001 From: Matej Urban Date: Wed, 30 Apr 2025 21:16:03 +0100 Subject: [PATCH 078/116] Nicer visualization --- src/run/relProg.ml | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/run/relProg.ml b/src/run/relProg.ml index 5526eb21..a7f8d109 100644 --- a/src/run/relProg.ml +++ b/src/run/relProg.ml @@ -16,22 +16,22 @@ let rec pp_array pp sz dims value = |> List.of_seq |> Pp.list (pp_array pp sz dims) -let pp_typed ~(tenv: Ctype.env) ~(ctype: Ctype.t) ~pp (value: State.Exp.t) = +let pp_typed ~(tenv: Ctype.env) ~(ctype: Ctype.t) ~(pp : ?hex:bool -> _ -> _) (value: State.Exp.t) = match ctype.unqualified with | Machine _ -> pp value | Cint _ -> pp value | Cbool -> pp value - | Ptr _ -> pp value + | Ptr _ -> pp ~hex:true value | Struct { id; _ } -> let s = IdMap.geti tenv.structs id in Pp.( Ctype.FieldMap.to_seq s.layout |> Seq.map (fun (offset, (field:Ctype.field)) -> ( - opt string field.fname, + Option.value ~default:"?" field.fname, pp (Exp.Typed.extract ~first:(8*offset) ~last:(8*(offset + field.size)-1) value) )) |> List.of_seq - |> mapping s.name + |> record s.name ) | Array { dims; _ } -> let sz = Ctype.sizeof ctype in @@ -100,8 +100,9 @@ let eval_loc_from_list ?frame_value sz st pc locs= eval_loc ?frame_value sz st loc let pp_variable_value ~(tenv: Ctype.env) ~(ctype: Ctype.t) value = - let pp = fun value -> + let pp ?(hex=false) = fun value -> match Exp.ConcreteEval.eval_if_concrete value with + | Some(Exp.Value.Bv bv) when not hex -> Pp.int @@ BitVec.to_int bv | Some(value) -> Exp.Value.pp value | None -> State.Exp.pp value in From e66ab9ca456260dfb1b586b1c640661ef9b2ad2f Mon Sep 17 00:00:00 2001 From: Matej Urban Date: Thu, 1 May 2025 23:34:17 +0100 Subject: [PATCH 079/116] Allow relocations in rodata and fix endianness problems --- src/elf/file.ml | 23 +++++++++++++--------- src/elf/linksemRelocatable.ml | 18 +++++++++-------- src/elf/segment.ml | 4 ++-- src/state/base.ml | 37 ++++++++++++++++++++++++----------- src/trace/run.ml | 2 ++ 5 files changed, 54 insertions(+), 30 deletions(-) diff --git a/src/elf/file.ml b/src/elf/file.ml index 3dcc5291..ecac2613 100644 --- a/src/elf/file.ml +++ b/src/elf/file.ml @@ -152,23 +152,28 @@ let of_file (filename : string) = }) elf64_file.elf64_file_interpreted_sections in let rodata = - SMap.of_list @@ List.filter_map Option.(fun section -> - let+ sname = if String.starts_with ~prefix:".rodata" section.name then - Some section.name + SMap.of_list @@ List.filter_map Option.(fun (section:Elf_interpreted_section.elf64_interpreted_section) -> + let+ sname = if String.starts_with ~prefix:".rodata" section.elf64_section_name_as_string then + Some section.elf64_section_name_as_string else None in - let (_, addr, data) = - Dwarf.extract_section_body_without_relocations elf_file sname false + let data = section.elf64_section_body in + Printf.printf "%t" Pp.(top BytesSeq.pp data); + let relocations = match LinksemRelocatable.get_relocations_for_section elf64_file sname with + | Error.Fail s -> elferror "LinksemRelocatable: get_relocations_for_section: %s" s + | Error.Success x -> Relocations.of_linksem x + in + (* let (_, addr, data) = + Dwarf.extract_section_body elf_file Abi_aarch64_symbolic_relocation.aarch64_data_relocation_interpreter sname false (* `false' argument is for returning an empty byte-sequence if section is not found, instead of throwing an exception *) - in - Printf.printf "%t" Pp.(top Sym.pp addr); + in *) ( sname, Segment. { - data; + data = (data, relocations); addr = 0; (* Meaningless for relocatable files *) size = BytesSeq.length data; read = true; @@ -176,7 +181,7 @@ let of_file (filename : string) = execute = false; } ) - ) sections + ) elf64_file.elf64_file_interpreted_sections in info "ELF file %s has been loaded" filename; { filename; symbols; entry; machine; linksem = elf_file; rodata; sections } diff --git a/src/elf/linksemRelocatable.ml b/src/elf/linksemRelocatable.ml index 99998fb7..eb0900d8 100644 --- a/src/elf/linksemRelocatable.ml +++ b/src/elf/linksemRelocatable.ml @@ -19,21 +19,23 @@ type global_symbol_init_info = symbol list open Elf_symbol_table open Elf_interpreted_section +let get_relocations_for_section (f:Elf_file.elf64_file) section = + let machine = f.elf64_file_header.elf64_machine in + if machine = Elf_header.elf_ma_aarch64 then + Error.bind + (Elf_symbolic.extract_elf64_relocations_for_section f Abi_aarch64_symbolic_relocation.aarch64_relocation_interpreter section) + @@ fun relocs -> Error.return (AArch64 relocs) + else + Error.fail @@ "machine not supported " ^ (Elf_header.string_of_elf_machine_architecture machine) + let get_elf64_file_global_symbol_init (f: Elf_file.elf64_file) : global_symbol_init_info Error.error = let secs = f.elf64_file_interpreted_sections in - let machine = f.elf64_file_header.elf64_machine in Error.bind (Elf_file.get_elf64_file_symbol_table f) @@ fun (symtab, strtab) -> let rel_cache = ref SMap.empty in let get_relocs section = match SMap.find_opt section !rel_cache with | Some rels -> rels - | None -> - if machine = Elf_header.elf_ma_aarch64 then - Error.bind - (Elf_symbolic.extract_elf64_relocations_for_section f Abi_aarch64_symbolic_relocation.aarch64_relocation_interpreter section) - @@ fun relocs -> Error.return (AArch64 relocs) - else - Error.fail @@ "machine not supported " ^ (Elf_header.string_of_elf_machine_architecture machine) + | None -> get_relocations_for_section f section in List.filter_map ( fun entry -> diff --git a/src/elf/segment.ml b/src/elf/segment.ml index faacf802..51b82009 100644 --- a/src/elf/segment.ml +++ b/src/elf/segment.ml @@ -51,7 +51,7 @@ (** The type of a segment *) type t = { - data : BytesSeq.t; + data : BytesSeq.t * Relocations.t; addr : int; (** The actual start address of the BytesSeq *) size : int; (** redundant with {!Utils.BytesSeq.length} data *) read : bool; @@ -66,7 +66,7 @@ let of_linksem (lseg : Elf_interpreted_segment.elf64_interpreted_segment) : t = BytesSeq.blit lseg.elf64_segment_body 0 bytes 0 (Z.to_int lseg.elf64_segment_size); let (read, write, execute) = lseg.elf64_segment_flags in { - data = BytesSeq.of_bytes bytes; + data = BytesSeq.of_bytes bytes, Relocations.IMap.empty; addr = Z.to_int lseg.elf64_segment_base; size; read; diff --git a/src/state/base.ml b/src/state/base.ml index ac324ec0..d1272de3 100644 --- a/src/state/base.ml +++ b/src/state/base.ml @@ -294,10 +294,12 @@ module Relocation = struct else [] in + let v, a = ( - Typed.concat (before @ relocation.value :: after), + Typed.concat (after @ relocation.value :: before), relocation.asserts @ asserts - ) + ) in + v,a ) data.relocations (exp, []) end @@ -647,24 +649,37 @@ let read_from_rodata (s : t) ~(addr : Exp.t) ~(size : Mem.Size.t) : Exp.t option | None -> None | Some elf -> ( Option.bind (eval_address s addr) @@ fun sym_addr -> - let size = size |> Ast.Size.to_bits in + let size = size |> Ast.Size.to_bytes in try let (sym, offset) = Elf.SymTable.of_addr_with_offset elf.symbols sym_addr in if sym.writable then None else ( - (* Assume little endian here *) - assert (Relocation.IMap.is_empty sym.data.relocations); - let bv = BytesSeq.getbvle ~size sym.data.data offset in (* TODO relocations *) - Some (Typed.bits bv) + let data = Elf.Symbol.sub sym offset size in + let value, asserts = Relocation.exp_of_data data in + + if not @@ List.is_empty asserts then + warn "Relocaiton assserts in .rodata ignored: %t" Pp.(top (list Exp.pp) asserts); + + Some value ) with Not_found -> let int_addr = sym_addr.offset in let open Option in let* rodata = Elf.File.SMap.find_opt sym_addr.section elf.rodata in - if rodata.addr <= int_addr && int_addr + size <= rodata.addr + rodata.size * 8 then - let bv = BytesSeq.getbvle ~size rodata.data (int_addr - rodata.addr) in - (* Assume little endian here *) - Some (Typed.bits bv) + if rodata.addr <= int_addr && int_addr + size <= rodata.addr + rodata.size then + let data, relocations = rodata.data in + let data = BytesSeq.sub data (int_addr - rodata.addr) size in + base "Addr offset: %d, size: %d" int_addr size; + base "All relocs: %t" (Pp.top Elf.Relocations.pp relocations); + let relocations = Elf.Relocations.sub relocations (int_addr - rodata.addr) size in + base "Sub relocs: %t" (Pp.top Elf.Relocations.pp relocations); + let value, asserts = Relocation.exp_of_data {data; relocations} in + base "Value: %t" (Pp.top Exp.pp value); + + if not @@ List.is_empty asserts then + warn "Relocaiton assserts in .rodata ignored: %t" Pp.(top (list Exp.pp) asserts); + + Some value else ( warn "Failed to find symbol or rodata at %t" (Pp.top Elf.Address.pp sym_addr); None diff --git a/src/trace/run.ml b/src/trace/run.ml index b0250aae..5298484f 100644 --- a/src/trace/run.ml +++ b/src/trace/run.ml @@ -98,6 +98,7 @@ let event_mut ~(ctxt : ctxt) (event : Base.event) = | None -> State.read_noprov ctxt.state ~addr:naddr ~size |> State.Tval.of_exp in + debug "read value: %t" Pp.(top State.Tval.pp tval); HashVector.set ctxt.mem_reads value tval | WriteMem { addr; value; size } -> ( let naddr = expand_simplify ~ctxt addr in @@ -109,6 +110,7 @@ let event_mut ~(ctxt : ctxt) (event : Base.event) = let ptrtype = Typer.expr ~ctxt addr in debug "Typed write mem with ptr:%t" (Pp.top (Pp.opt Ctype.pp) ptrtype); let value = expand_tval ~ctxt value in + debug "written value: %t" Pp.(top State.Tval.pp value); Typer.write ~dwarf ctxt.state ?ptrtype ~addr:naddr ~size value | None -> let value = expand_simplify ~ctxt value in From 0f9393ab4a535e6128151d8cfb0083b8eac94978 Mon Sep 17 00:00:00 2001 From: Matej Urban Date: Thu, 1 May 2025 23:51:09 +0100 Subject: [PATCH 080/116] Remove print --- src/elf/file.ml | 1 - src/state/base.ml | 4 ---- 2 files changed, 5 deletions(-) diff --git a/src/elf/file.ml b/src/elf/file.ml index ecac2613..91bef6fc 100644 --- a/src/elf/file.ml +++ b/src/elf/file.ml @@ -159,7 +159,6 @@ let of_file (filename : string) = None in let data = section.elf64_section_body in - Printf.printf "%t" Pp.(top BytesSeq.pp data); let relocations = match LinksemRelocatable.get_relocations_for_section elf64_file sname with | Error.Fail s -> elferror "LinksemRelocatable: get_relocations_for_section: %s" s | Error.Success x -> Relocations.of_linksem x diff --git a/src/state/base.ml b/src/state/base.ml index d1272de3..bec126eb 100644 --- a/src/state/base.ml +++ b/src/state/base.ml @@ -669,12 +669,8 @@ let read_from_rodata (s : t) ~(addr : Exp.t) ~(size : Mem.Size.t) : Exp.t option if rodata.addr <= int_addr && int_addr + size <= rodata.addr + rodata.size then let data, relocations = rodata.data in let data = BytesSeq.sub data (int_addr - rodata.addr) size in - base "Addr offset: %d, size: %d" int_addr size; - base "All relocs: %t" (Pp.top Elf.Relocations.pp relocations); let relocations = Elf.Relocations.sub relocations (int_addr - rodata.addr) size in - base "Sub relocs: %t" (Pp.top Elf.Relocations.pp relocations); let value, asserts = Relocation.exp_of_data {data; relocations} in - base "Value: %t" (Pp.top Exp.pp value); if not @@ List.is_empty asserts then warn "Relocaiton assserts in .rodata ignored: %t" Pp.(top (list Exp.pp) asserts); From 6524a9dd64f1deb62b4aa166ae27ab40ce5c3ae2 Mon Sep 17 00:00:00 2001 From: Matej Urban Date: Fri, 2 May 2025 00:01:06 +0100 Subject: [PATCH 081/116] Fix debug loc parsing --- src/dw/loc.ml | 13 ++++++++----- src/run/relProg.ml | 6 +++++- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/src/dw/loc.ml b/src/dw/loc.ml index 3ead2a51..a63fcc53 100644 --- a/src/dw/loc.ml +++ b/src/dw/loc.ml @@ -72,7 +72,7 @@ type t = | RegisterOffset of State.Reg.t * int (** At register + offset address *) | StackFrame of int (** On the stackFrame with offset *) | Global of Elf.SymTable.sym_offset (** Global variable with an offset *) - | Const of Z.t + | Const of Sym.t | Dwarf of dwop list (** Uninterpreted dwarf location *) (** The type of a location in linksem format *) @@ -125,12 +125,12 @@ let of_linksem ?(amap = Arch.dwarf_reg_map ()) (elf : Elf.File.t) : linksem_t -> let addr = Addr.of_sym @@ sym_of_oav arg in try Global (Elf.SymTable.of_addr_with_offset elf.symbols @@ addr) with Not_found -> - warn "Symbol at 0x%x not found in Loc.of_linksem" (int_of_oav arg); + warn "Symbol at %t not found in Loc.of_linksem" (Pp.top Sym.pp (sym_of_oav arg)); Dwarf ops ) (* Other *) | [{ op_semantics = OpSem_lit; op_argument_values = [arg]; _ }; { op_semantics = OpSem_stack_value; _ }] -> - let value = Sym.to_z @@ sym_of_oav arg in + let value = sym_of_oav arg in Const value | ops -> Dwarf ops @@ -140,7 +140,7 @@ let to_string = function | RegisterOffset (reg, off) -> Printf.sprintf "[%s+%x]" (State.Reg.to_string reg) off | StackFrame off -> Printf.sprintf "[frame+%x]" off | Global symoff -> Elf.SymTable.string_of_sym_offset symoff - | Const x -> Z.to_string x + | Const x -> Sym.to_string x | Dwarf ops -> Dwarf.pp_operations ops (** Compare two location. Loc.t is not compatible with polymorphic compare *) @@ -159,7 +159,10 @@ let compare l1 l2 = Pair.compare ~fst:Elf.Symbol.compare (sym1, off1) (sym2, off2) | (Global (_, _), _) -> -1 | (_, Global (_, _)) -> 1 - | (Const x, Const y) -> Z.compare x y + | (Const (Absolute x), Const (Absolute y)) -> Z.compare x y + | (Const (Offset(s,x)), Const (Offset(t,y))) -> Pair.compare ~snd:Z.compare (s,x) (t,y) + | (Const (Absolute _), Const (Offset _)) -> -1 + | (Const (Offset _), Const (Absolute _)) -> 1 | (Const _, _) -> -1 | (_, Const _) -> 1 | (Dwarf ops1, Dwarf ops2) -> compare ops1 ops2 diff --git a/src/run/relProg.ml b/src/run/relProg.ml index a7f8d109..20719885 100644 --- a/src/run/relProg.ml +++ b/src/run/relProg.ml @@ -82,7 +82,11 @@ let eval_loc ?frame_value sz st (loc: Dw.Loc.t) : State.Exp.t option = let addr = Elf.SymTable.to_addr_offset symoff in let addr = State.Exp.of_address ~size:Arch.address_size addr in Some (read_big ~prov:None st addr sz) - | Const x -> Some(x |> BitVec.of_z ~size:(8*sz) |> Exp.Typed.bits) + | Const x -> + Some (match x with + | Absolute x -> x |> BitVec.of_z ~size:(8*sz) |> Exp.Typed.bits + | Offset (s, o) -> State.Exp.of_address ~size:(8*sz) Elf.Address.{section=s; offset=Z.to_int o} + ) | Dwarf _ops -> None let eval_loc_from_list ?frame_value sz st pc locs= From 2feb3a6c6ace8bb0d1cbf00b9ffda0e9ab83338e Mon Sep 17 00:00:00 2001 From: Matej Urban Date: Fri, 2 May 2025 12:52:28 +0100 Subject: [PATCH 082/116] Add sections non-overlap constraints --- src/elf/file.ml | 15 +++-- src/run/func.ml | 4 +- src/run/funcRD.ml | 2 +- src/run/relProg.ml | 2 +- src/run/testRelProg.ml | 2 +- src/state/base.ml | 139 ++++++++++++++++++++++++----------------- src/state/base.mli | 4 +- 7 files changed, 99 insertions(+), 69 deletions(-) diff --git a/src/elf/file.ml b/src/elf/file.ml index 91bef6fc..095ad835 100644 --- a/src/elf/file.ml +++ b/src/elf/file.ml @@ -145,11 +145,16 @@ let of_file (filename : string) = - the range of the section is guaranteed to overlap with any symbols within it, and so not suitable to be stored in the [RngMap] *) let elf_file = Elf_file.ELF_File_64 elf64_file in - let sections = List.map (fun (s:Elf_interpreted_section.elf64_interpreted_section) -> { - name=s.elf64_section_name_as_string; - size=Z.to_int s.elf64_section_size; - align=Z.to_int s.elf64_section_align; - }) elf64_file.elf64_file_interpreted_sections + let sections = List.filter_map (fun (s:Elf_interpreted_section.elf64_interpreted_section) -> + if Z.equal Z.zero (Z.logand s.elf64_section_flags Elf_section_header_table.shf_alloc) then + None + else + Some { + name=s.elf64_section_name_as_string; + size=Z.to_int s.elf64_section_size; + align=Z.to_int s.elf64_section_align; + } + ) elf64_file.elf64_file_interpreted_sections in let rodata = SMap.of_list @@ List.filter_map Option.(fun (section:Elf_interpreted_section.elf64_interpreted_section) -> diff --git a/src/run/func.ml b/src/run/func.ml index 4ea1d385..08ac6c2b 100644 --- a/src/run/func.ml +++ b/src/run/func.ml @@ -52,7 +52,7 @@ open Logs.Logger (struct let str = __MODULE__ end) -let no_run_prep ~elf:elfname ~name ~entry ?(init = State.init_sections_symbolic ~addr_size:Arch.address_size) () = +let no_run_prep ~elf:elfname ~name ~entry ?(init = State.init_sections_symbolic ~sp:Arch.sp ~addr_size:Arch.address_size) () = base "Running %s in %s" name elfname; let dwarf = Dw.of_file elfname in let elf = dwarf.elf in @@ -66,7 +66,7 @@ let no_run_prep ~elf:elfname ~name ~entry ?(init = State.init_sections_symbolic let abi = Arch.get_abi api in Trace.Cache.start @@ Arch.get_isla_config (); base "Computing entry state"; - let start = Init.state () |> State.copy ~elf |> init |> abi.init in + let start = Init.state () |> State.copy ~elf |> abi.init |> init in if entry then base "Entry state:\n%t" (Pp.topi State.pp start); (dwarf, elf, func, start) diff --git a/src/run/funcRD.ml b/src/run/funcRD.ml index f2d1fffb..052b50b9 100644 --- a/src/run/funcRD.ml +++ b/src/run/funcRD.ml @@ -74,7 +74,7 @@ let run_func_rd elfname name objdump_d branchtables breakpoints = let abi = Arch.get_abi api in Trace.Cache.start @@ Arch.get_isla_config (); base "Computing entry state"; - let start = Init.state () |> State.copy ~elf |> State.init_sections ~addr_size:Arch.address_size |> abi.init in + let start = Init.state () |> State.copy ~elf |> abi.init |> State.init_sections ~sp:Arch.sp ~addr_size:Arch.address_size in base "Loading %s for Analyse" elfname; let analyse_test = Analyse.Elf.parse_elf_file elfname in base "Analysing %s for Analyse" elfname; diff --git a/src/run/relProg.ml b/src/run/relProg.ml index 20719885..f4a0f354 100644 --- a/src/run/relProg.ml +++ b/src/run/relProg.ml @@ -159,7 +159,7 @@ let run_prog elfname name objdump_d branchtables = instr in base "Start running"; - let tree = Func.get_state_tree ~elf:elfname ~name ~init:(State.init_sections ~addr_size:Arch.address_size) ~every_instruction:true () + let tree = Func.get_state_tree ~elf:elfname ~name ~init:(State.init_sections ~sp:Arch.sp ~addr_size:Arch.address_size) ~every_instruction:true () ~breakpoints:["UND.abort"; "UND.exit"] in base "Ended running, start pretty printing"; diff --git a/src/run/testRelProg.ml b/src/run/testRelProg.ml index 3ac67b19..e9f15408 100644 --- a/src/run/testRelProg.ml +++ b/src/run/testRelProg.ml @@ -75,7 +75,7 @@ let rec process_tree ~pc ~ret ~ext (node:Block_lib.label State.Tree.t) = } let test return_register exit_register name = - let tree = Func.get_state_tree ~elf:name ~name:"main" ~init:(State.init_sections ~addr_size:Arch.address_size) ~every_instruction:false () + let tree = Func.get_state_tree ~elf:name ~name:"main" ~init:(State.init_sections ~sp:Arch.sp ~addr_size:Arch.address_size) ~every_instruction:false () ~breakpoints:["UND.abort"; "UND.exit"] in debug "%t" (Pp.top (State.Tree.pp_all Block_lib.pp_label) tree); diff --git a/src/state/base.ml b/src/state/base.ml index bec126eb..2f4c78ba 100644 --- a/src/state/base.ml +++ b/src/state/base.ml @@ -537,63 +537,6 @@ let set_impossible state = assert (not @@ is_locked state); state.asserts <- [Typed.false_] -let push_section_constraints ~addr_size state sections = - List.iter (fun (s:Elf.File.section) -> - let max_section_addr = Int.shift_left 1 addr_size - s.size in - let s_exp = (Exp.of_var (Var.Section s.name)) in - (* The whole section fits in memory *) - push_assert state Typed.(comp Ast.Bvule s_exp (bits_int ~size:64 max_section_addr)); - (* The load address cannot be 0 *) - push_assert state Typed.(not (s_exp = (bits_int ~size:64 0))); - if s.align > 1 then - let (align_pow, _) = Seq.ints 0 - |> Seq.drop_while (fun x -> Int.shift_left 1 x < s.align) - |> Seq.uncons - |> Option.get - in - if s.align = Int.shift_left 1 align_pow then - let last = align_pow - 1 in - (* Section address is aligned *) - push_assert state Typed.(extract ~first:0 ~last s_exp = zero ~size:align_pow) - else - warn "Section alignment is not a power of two: %d" s.align; - ) sections - -let init_sections ~addr_size state = - let state = copy_if_locked state in - let _ = Option.( - let+ elf = state.elf in - state.mem.allow_main <- false; - push_section_constraints ~addr_size state elf.sections; - List.iter (fun (x:Elf.File.section) -> Mem.create_section_frag ~addr_size state.mem x.name |> ignore) elf.sections; - Elf.SymTable.iter elf.symbols @@ fun sym -> - let len = List.find (fun x -> sym.size mod x = 0) [16;8;4;2;1] in - if sym.typ = Elf.Symbol.OBJECT then - let provenance = Mem.get_section_provenance state.mem sym.addr.section in - Seq.iota_step_up ~step:len ~endi:sym.size - |> Seq.iter (fun off -> - let data = Elf.Symbol.sub sym off len in - let addr = Exp.of_address ~size:addr_size Elf.Address.(sym.addr + off) in - let size = Ast.Size.of_bytes len in - let (exp, asserts) = Relocation.exp_of_data data in - Mem.write ~provenance state.mem ~addr ~size ~exp; - List.iter (push_relocation_assert state) asserts; - ) - ) in - state - -let init_sections_symbolic ~addr_size state = - let state = copy_if_locked state in - let _ = Option.( - let+ elf = state.elf in - push_section_constraints ~addr_size state elf.sections; - Elf.SymTable.iter elf.symbols @@ fun sym -> - if sym.typ = Elf.Symbol.OBJECT then - Hashtbl.replace state.mem.sections sym.addr.section Main - ) in - state - - let map_mut_exp (f : exp -> exp) s : unit = assert (not @@ is_locked s); Reg.Map.map_mut_current (Tval.map_exp f) s.regs; @@ -781,6 +724,88 @@ let set_last_pc state pc = assert (not @@ is_locked state); state.last_pc <- pc + +let push_section_constraints ~sp ~addr_size state sections = + let sp = sp () in + let rec f : Elf.File.section list -> unit = function + | [] -> () + | s::rest -> ( + let max_section_addr = Int.shift_left 1 addr_size - s.size in + let s_exp = (Exp.of_var (Var.Section s.name)) in + (* The whole section fits in memory *) + push_assert state Typed.(comp Ast.Bvule s_exp (bits_int ~size:64 max_section_addr)); + (* The load address cannot be 0 *) + push_assert state Typed.(not (s_exp = (bits_int ~size:64 0))); + if s.align > 1 then ( + let (align_pow, _) = Seq.ints 0 + |> Seq.drop_while (fun x -> Int.shift_left 1 x < s.align) + |> Seq.uncons + |> Option.get + in + if s.align = Int.shift_left 1 align_pow then + let last = align_pow - 1 in + (* Section address is aligned *) + push_assert state Typed.(extract ~first:0 ~last s_exp = zero ~size:align_pow) + else + warn "Section alignment is not a power of two: %d" s.align + ); + (* Sections don't overlap *) + let s_end = Typed.(s_exp + bits_int ~size:64 s.size) in (* we know this doesn't overflow thanks to the other constraints *) + List.iter (fun (s2:Elf.File.section) -> + let s2_exp = (Exp.of_var (Var.Section s2.name)) in + let s2_end = Typed.(s2_exp + bits_int ~size:64 s2.size) in + let order1 = Typed.(comp Ast.Bvule s_end s2_exp) in + let order2 = Typed.(comp Ast.Bvule s2_end s_exp) in + push_assert state Typed.(manyop Or [order1; order2]) + ) rest; + (* Doesn't overlap with stack *) + let stack_end = get_reg_exp state sp in + let stack_start = Typed.(stack_end - bits_int ~size:64 0x1000) in + let order1 = Typed.(comp Ast.Bvule s_end stack_start) in + let order2 = Typed.(comp Ast.Bvule stack_end s_exp) in + push_assert state Typed.(manyop Or [order1; order2]); + + f rest + ) + in + f sections + +let init_sections ~sp ~addr_size state = + let state = copy_if_locked state in + let _ = Option.( + let+ elf = state.elf in + state.mem.allow_main <- false; + push_section_constraints ~sp ~addr_size state elf.sections; + List.iter (fun (x:Elf.File.section) -> Mem.create_section_frag ~addr_size state.mem x.name |> ignore) elf.sections; + Elf.SymTable.iter elf.symbols @@ fun sym -> + let len = List.find (fun x -> sym.size mod x = 0) [16;8;4;2;1] in + if sym.typ = Elf.Symbol.OBJECT then + let provenance = Mem.get_section_provenance state.mem sym.addr.section in + Seq.iota_step_up ~step:len ~endi:sym.size + |> Seq.iter (fun off -> + let data = Elf.Symbol.sub sym off len in + let addr = Exp.of_address ~size:addr_size Elf.Address.(sym.addr + off) in + let size = Ast.Size.of_bytes len in + let (exp, asserts) = Relocation.exp_of_data data in + Mem.write ~provenance state.mem ~addr ~size ~exp; + List.iter (push_relocation_assert state) asserts; + ) + ) in + lock state; + state + +let init_sections_symbolic ~sp ~addr_size state = + let state = copy_if_locked state in + let _ = Option.( + let+ elf = state.elf in + push_section_constraints ~sp ~addr_size state elf.sections; + Elf.SymTable.iter elf.symbols @@ fun sym -> + if sym.typ = Elf.Symbol.OBJECT then + Hashtbl.replace state.mem.sections sym.addr.section Main + ) in + lock state; + state + let pp s = let open Pp in record "state" diff --git a/src/state/base.mli b/src/state/base.mli index a7275a7e..2d373fd4 100644 --- a/src/state/base.mli +++ b/src/state/base.mli @@ -405,10 +405,10 @@ val copy : ?elf:Elf.File.t -> t -> t The returned state is always unlocked *) val copy_if_locked : ?elf:Elf.File.t -> t -> t -val init_sections : addr_size:int -> t -> t +val init_sections : sp:(unit -> Reg.t) -> addr_size:int -> t -> t (** Assigns all sections with global objects to Main fragment *) -val init_sections_symbolic : addr_size:int -> t -> t +val init_sections_symbolic : sp:(unit -> Reg.t) -> addr_size:int -> t -> t (** {1 State convenience manipulation } *) From cfa543e7ffdd49dac31a95fb07a3474bcc2985fc Mon Sep 17 00:00:00 2001 From: Matej Urban Date: Sat, 3 May 2025 13:22:10 +0100 Subject: [PATCH 083/116] debug --- src/analyse/Symbols.ml | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/analyse/Symbols.ml b/src/analyse/Symbols.ml index cc059ae4..6e44279b 100644 --- a/src/analyse/Symbols.ml +++ b/src/analyse/Symbols.ml @@ -1,5 +1,9 @@ (* TODO header *) +open Logs.Logger (struct + let str = __MODULE__ +end) + module SMap = Map.Make (String) type rels = @@ -24,12 +28,14 @@ let get_elf64_file_global_symbol_init (f: Elf_file.elf64_file) : global_symbol_i let rel_cache = ref SMap.empty in let get_relocs section = match SMap.find_opt section !rel_cache with - | Some rels -> rels + | Some rels -> Error.return rels | None -> if machine = Elf_header.elf_ma_aarch64 then Error.bind (Elf_symbolic.extract_elf64_relocations_for_section f Abi_aarch64_symbolic_relocation.aarch64_relocation_interpreter section) - @@ fun relocs -> Error.return (AArch64 relocs) + @@ fun relocs -> + rel_cache := SMap.add section (AArch64 relocs) !rel_cache; + Error.return (AArch64 relocs) else Error.fail @@ "machine not supported " ^ (Elf_header.string_of_elf_machine_architecture machine) in @@ -65,6 +71,7 @@ let get_elf64_file_global_symbol_init (f: Elf_file.elf64_file) : global_symbol_i in Error.bind data @@ fun data -> Error.bind (String_table.get_string_at name strtab) @@ fun str -> + debug "Processed %s\n" str; Error.return (str, (typ, size, addr, (data, relocs), bnd)) ) (List.nth_opt secs shndx) ) symtab |> Error.mapM Fun.id \ No newline at end of file From e9ca1854d9b3f77220c068cd2e3ca2e54b94003d Mon Sep 17 00:00:00 2001 From: Matej Urban Date: Sat, 3 May 2025 15:28:42 +0100 Subject: [PATCH 084/116] More relocation types --- src/elf/relocations.ml | 4 +++- src/isla/cache.ml | 9 +++++++-- src/isla/relocation.ml | 17 +++++++++++++---- 3 files changed, 23 insertions(+), 7 deletions(-) diff --git a/src/elf/relocations.ml b/src/elf/relocations.ml index 66cdc831..9d2d2107 100644 --- a/src/elf/relocations.ml +++ b/src/elf/relocations.ml @@ -81,7 +81,9 @@ let pp_target = Pp.(function | AArch64 Abi_aarch64_symbolic_relocation.ADD -> !^"ADD" | AArch64 Abi_aarch64_symbolic_relocation.ADRP -> !^"ADRP" | AArch64 Abi_aarch64_symbolic_relocation.CALL -> !^"CALL" -| AArch64 Abi_aarch64_symbolic_relocation.LDST b -> !^"LDST" ^^ int (1 lsl b)) +| AArch64 Abi_aarch64_symbolic_relocation.LDST b -> !^"LDST" ^^ int (1 lsl b) +| AArch64 Abi_aarch64_symbolic_relocation.CONDBR -> !^"CONDBR" +| AArch64 Abi_aarch64_symbolic_relocation.B -> !^"B") let pp_rel rel = let hi, lo = rel.mask in diff --git a/src/isla/cache.ml b/src/isla/cache.ml index be6f64e8..1cf72492 100644 --- a/src/isla/cache.ml +++ b/src/isla/cache.ml @@ -86,7 +86,9 @@ module Opcode (*: Cache.Key *) = struct | Some (Elf.Relocations.AArch64 Abi_aarch64_symbolic_relocation.ADRP) -> 3 | Some (Elf.Relocations.AArch64 Abi_aarch64_symbolic_relocation.ADD) -> 4 | Some (Elf.Relocations.AArch64 Abi_aarch64_symbolic_relocation.CALL) -> 5 - | Some (Elf.Relocations.AArch64 Abi_aarch64_symbolic_relocation.LDST b) -> 6 + b + | Some (Elf.Relocations.AArch64 Abi_aarch64_symbolic_relocation.LDST b) -> assert (b < 5); 6 + b + | Some (Elf.Relocations.AArch64 Abi_aarch64_symbolic_relocation.CONDBR) -> 11 + | Some (Elf.Relocations.AArch64 Abi_aarch64_symbolic_relocation.B) -> 12 let reloc_of_id: int -> Relocation.t option = function | 0 -> None @@ -95,7 +97,10 @@ module Opcode (*: Cache.Key *) = struct | 3 -> Some (Elf.Relocations.AArch64 Abi_aarch64_symbolic_relocation.ADRP) | 4 -> Some (Elf.Relocations.AArch64 Abi_aarch64_symbolic_relocation.ADD) | 5 -> Some (Elf.Relocations.AArch64 Abi_aarch64_symbolic_relocation.CALL) - | x -> Some (Elf.Relocations.AArch64 (Abi_aarch64_symbolic_relocation.LDST (x-6))) + | x when x < 11 -> Some (Elf.Relocations.AArch64 (Abi_aarch64_symbolic_relocation.LDST (x-6))) + | 11 -> Some (Elf.Relocations.AArch64 Abi_aarch64_symbolic_relocation.CONDBR) + | 12 -> Some (Elf.Relocations.AArch64 Abi_aarch64_symbolic_relocation.B) + | x -> fail "Invalid relocation id %d" x let equal a b = match (a, b) with diff --git a/src/isla/relocation.ml b/src/isla/relocation.ml index d8e17a5a..c73e231b 100644 --- a/src/isla/relocation.ml +++ b/src/isla/relocation.ml @@ -25,20 +25,29 @@ let pp_opcode_with_segments (b, r) = BitVec.pp_smt (BitVec.extract 22 31 bits) ^^ !^" x0:12 " ^^ BitVec.pp_smt (BitVec.extract 0 9 bits) - | Abi_aarch64_symbolic_relocation.LDST b -> (* TODO different width loads, alignment *) + | Abi_aarch64_symbolic_relocation.LDST b -> BitVec.pp_smt (BitVec.extract (22-b) 31 bits) ^^ !^" x0:" ^^ int (12-b) ^^ !^" " ^^ BitVec.pp_smt (BitVec.extract 0 9 bits) | Abi_aarch64_symbolic_relocation.CALL -> BitVec.pp_smt (BitVec.extract 26 31 bits) ^^ !^" x0:26 " + | Abi_aarch64_symbolic_relocation.CONDBR -> + BitVec.pp_smt (BitVec.extract 24 31 bits) + ^^ !^" x0:19 " + ^^ BitVec.pp_smt (BitVec.extract 0 4 bits) + | Abi_aarch64_symbolic_relocation.B -> + BitVec.pp_smt (BitVec.extract 26 31 bits) + ^^ !^" x0:26 " ) (* for interpreting the segments *) let segments_of_reloc: t -> segment list = function | Elf.Relocations.AArch64 Abi_aarch64_symbolic_relocation.Data640 -> fatal "invalid relocation for instructions (Data64)" | Elf.Relocations.AArch64 Abi_aarch64_symbolic_relocation.Data320 -> fatal "invalid relocation for instructions (Data32)" -| Elf.Relocations.AArch64 Abi_aarch64_symbolic_relocation.ADRP -> ["x0", (0, 1); "x1", (2, 20)] (* or absolute? ["x0", (12, 13); "x1", (14, 32)] *) +| Elf.Relocations.AArch64 Abi_aarch64_symbolic_relocation.ADRP -> ["x0", (0, 1); "x1", (2, 20)] | Elf.Relocations.AArch64 Abi_aarch64_symbolic_relocation.ADD -> ["x0", (0, 11)] -| Elf.Relocations.AArch64 Abi_aarch64_symbolic_relocation.LDST b -> ["x0", (0, 11-b)] (* TODO depends on load size *) (* or absolute? ["x0", (2, 11)] *) -| Elf.Relocations.AArch64 Abi_aarch64_symbolic_relocation.CALL -> ["x0", (0, 25)] (* or absolute? ["x0", (2, 27)] *) \ No newline at end of file +| Elf.Relocations.AArch64 Abi_aarch64_symbolic_relocation.LDST b -> ["x0", (0, 11-b)] +| Elf.Relocations.AArch64 Abi_aarch64_symbolic_relocation.CALL -> ["x0", (0, 25)] +| Elf.Relocations.AArch64 Abi_aarch64_symbolic_relocation.CONDBR -> ["x0", (0, 18)] +| Elf.Relocations.AArch64 Abi_aarch64_symbolic_relocation.B -> ["x0", (0, 25)] From d72240d5048d9bebbdd969c52a7fb264b25eb129 Mon Sep 17 00:00:00 2001 From: Matej Urban Date: Sat, 3 May 2025 16:51:38 +0100 Subject: [PATCH 085/116] new relocation representation in linksem --- src/analyse/Symbols.ml | 2 +- src/elf/linksemRelocatable.ml | 2 +- src/elf/relocations.ml | 34 +++++++++++++++------------------- 3 files changed, 17 insertions(+), 21 deletions(-) diff --git a/src/analyse/Symbols.ml b/src/analyse/Symbols.ml index 6e44279b..d555ae9f 100644 --- a/src/analyse/Symbols.ml +++ b/src/analyse/Symbols.ml @@ -7,7 +7,7 @@ end) module SMap = Map.Make (String) type rels = - | AArch64 of (Z.t, Abi_aarch64_symbolic_relocation.aarch64_relocation_target Elf_symbolic.abstract_relocation) Pmap.map + | AArch64 of (Z.t, Abi_aarch64_symbolic_relocation.aarch64_relocation_target Elf_symbolic.universal_relocation) Pmap.map type sym_data = Byte_sequence_wrapper.byte_sequence * rels diff --git a/src/elf/linksemRelocatable.ml b/src/elf/linksemRelocatable.ml index eb0900d8..5f92e2e0 100644 --- a/src/elf/linksemRelocatable.ml +++ b/src/elf/linksemRelocatable.ml @@ -5,7 +5,7 @@ module SMap = Map.Make (String) type sym_addr = string * Z.t type rels = - | AArch64 of (Z.t, Abi_aarch64_symbolic_relocation.aarch64_relocation_target Elf_symbolic.abstract_relocation) Pmap.map + | AArch64 of (Z.t, Abi_aarch64_symbolic_relocation.aarch64_relocation_target Elf_symbolic.universal_relocation) Pmap.map type sym_data = Byte_sequence_wrapper.byte_sequence * rels diff --git a/src/elf/relocations.ml b/src/elf/relocations.ml index 9d2d2107..8021bde4 100644 --- a/src/elf/relocations.ml +++ b/src/elf/relocations.ml @@ -12,8 +12,6 @@ type exp = | Const of int | BinOp of (exp * binary_operation * exp) | UnOp of (unary_operation * exp) -(* | AssertRange of (exp * int * int) *) -(* | Mask of (exp * int * int) *) type assertion = | Range of int64 * int64 @@ -30,32 +28,30 @@ type t = rel IMap.t type linksem_t = LinksemRelocatable.rels -let exp_of_linksem = +let rel_of_aarch64_linksem Elf_symbolic.{rel_desc_value; rel_desc_checks; rel_desc_mask; rel_desc_target } = let rec value_of_linksem = function | Elf_symbolic.Section s -> Section s | Elf_symbolic.Const x -> Const (Z.to_int x) | Elf_symbolic.BinOp (x, op, y) -> BinOp (value_of_linksem x, op, value_of_linksem y) | Elf_symbolic.UnOp (op, x) -> UnOp (op, value_of_linksem x) - | Elf_symbolic.AssertRange (_, _, _) -> Raise.fail "AssertRange should not occur in value expression" - | Elf_symbolic.AssertAlignment (_, _) -> Raise.fail "AssertAlignment should not occur in value expression" - | Elf_symbolic.Mask (_, _, _) -> Raise.fail "AssertRange should not occur in value expression" - in function - | Elf_symbolic.Mask (e, hi, lo) -> - let rec extract_asserts e = - match e with - | Elf_symbolic.AssertRange (e, min, max) -> let (e, a) = extract_asserts e in e, Range (Z.to_int64 min, Z.to_int64 max) :: a - | Elf_symbolic.AssertAlignment (e, bits) -> let (e, a) = extract_asserts e in e, Alignment (Z.to_int bits) :: a - | e -> e, [] - in - let e, assertions = extract_asserts e in - fun target -> {target; assertions; mask = (Z.to_int hi, Z.to_int lo); value = value_of_linksem e} - | _ -> Raise.fail "Expression does not have Mask in top level" + in + let assertions = List.map (function + | Elf_symbolic.Overflow (min, max) -> Range (Z.to_int64 min, Z.to_int64 max) + | Elf_symbolic.Alignment (bits) -> Alignment (Z.to_int bits) + ) rel_desc_checks in + let hi, lo = rel_desc_mask in + { + target=AArch64 rel_desc_target; + assertions; + mask = (Z.to_int hi, Z.to_int lo); + value = value_of_linksem rel_desc_value + } let of_linksem: linksem_t -> t = function | LinksemRelocatable.AArch64 relocs -> - let add k Elf_symbolic.{ arel_value; arel_target } m = - IMap.add (Z.to_int k) (exp_of_linksem arel_value (AArch64 arel_target)) m + let add k rel m = + IMap.add (Z.to_int k) (rel_of_aarch64_linksem rel) m in Pmap.fold add relocs IMap.empty From f2f1d24bc3b1cce7923d13d44068f84c7482d76f Mon Sep 17 00:00:00 2001 From: Matej Urban Date: Sat, 3 May 2025 16:52:16 +0100 Subject: [PATCH 086/116] rename relocation assertions to checks --- src/elf/relocations.ml | 6 +++--- src/state/base.ml | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/elf/relocations.ml b/src/elf/relocations.ml index 8021bde4..04117864 100644 --- a/src/elf/relocations.ml +++ b/src/elf/relocations.ml @@ -20,7 +20,7 @@ type assertion = type rel = { target : target; value : exp; - assertions: assertion list; + checks: assertion list; mask : int * int; } @@ -35,14 +35,14 @@ let rel_of_aarch64_linksem Elf_symbolic.{rel_desc_value; rel_desc_checks; rel_de | Elf_symbolic.BinOp (x, op, y) -> BinOp (value_of_linksem x, op, value_of_linksem y) | Elf_symbolic.UnOp (op, x) -> UnOp (op, value_of_linksem x) in - let assertions = List.map (function + let checks = List.map (function | Elf_symbolic.Overflow (min, max) -> Range (Z.to_int64 min, Z.to_int64 max) | Elf_symbolic.Alignment (bits) -> Alignment (Z.to_int bits) ) rel_desc_checks in let hi, lo = rel_desc_mask in { target=AArch64 rel_desc_target; - assertions; + checks; mask = (Z.to_int hi, Z.to_int lo); value = value_of_linksem rel_desc_value } diff --git a/src/state/base.ml b/src/state/base.ml index 2f4c78ba..2657fa7d 100644 --- a/src/state/base.ml +++ b/src/state/base.ml @@ -264,7 +264,7 @@ module Relocation = struct | Alignment b -> let last = b-1 in Typed.(extract ~first:0 ~last value = bits_int ~size:b 0) - ) relocation.assertions in + ) relocation.checks in let (last, first) = relocation.mask in let value = Typed.extract ~first ~last value in { value; asserts; target = relocation.target } From cd6e5a8bd46f95111d14faf55cf9e5914e3dda78 Mon Sep 17 00:00:00 2001 From: Matej Urban Date: Sat, 3 May 2025 16:54:02 +0100 Subject: [PATCH 087/116] rm notes-TODO --- notes-TODO | 22 ---------------------- 1 file changed, 22 deletions(-) delete mode 100644 notes-TODO diff --git a/notes-TODO b/notes-TODO deleted file mode 100644 index 95c382c0..00000000 --- a/notes-TODO +++ /dev/null @@ -1,22 +0,0 @@ -Symbolic symbol table -- value of symbol?? (we don't have segments in relocatable files) -- can probably keep the same api, but addresses are symbolic - -Instruction fetch: is it sound? (rewriting .text) - -Z3 finding unique solution -- Get model -> assert not model -> check now it is unsat -- Need to extend the protocol probably - -SIMREL -- state = (pc,M) (and registers TODO) -- (pc,M1) ~ (pc.M2) iff there is MT, such that - MT(A1,A2,sz) = T => M1[A1:A1+sz] =T M2[A2:A2+sz] - and respects dwarf at pc - and maybe some consistency of MT?? e.g. overlaping ranges -- relation (=T) defined using MT - - (=base_type) is equality - - (=struct) fieldwise - - (=*T): - A1 =*T A2 <=> MT(A1,A2,sz(T)) = T -- Hoare logic (or similar) with MT as variable (only read/write commands) From 1fa15c3d83bba0e8b3b19a2c86e15a3cfd7ff485 Mon Sep 17 00:00:00 2001 From: maturvo <59334936+maturvo@users.noreply.github.com> Date: Tue, 13 May 2025 01:48:20 +0100 Subject: [PATCH 088/116] Simrel (#2) * wip * wip * Debug prints * wip * Refactor simrel * Global memory processing * fixes * better printing * cleanup --- src/bin/dune | 4 +- src/bin/readDwarf.ml | 1 + src/relsim/base.ml | 115 --------- src/relsim/dune | 6 + src/relsim/relsim.ml | 447 ++++++++++++++++++++++++++++++++++ src/state/base.ml | 4 + src/state/base.mli | 3 + src/state/reg.mli | 3 + src/state/symbolicFragment.ml | 2 + src/trace/context.ml | 5 +- src/utils/fullVec.ml | 5 + src/utils/fullVec.mli | 3 + 12 files changed, 480 insertions(+), 118 deletions(-) delete mode 100644 src/relsim/base.ml create mode 100644 src/relsim/dune create mode 100644 src/relsim/relsim.ml diff --git a/src/bin/dune b/src/bin/dune index fea23d27..02b51b85 100644 --- a/src/bin/dune +++ b/src/bin/dune @@ -4,7 +4,7 @@ (modules main) (flags (:standard -open Utils)) - (libraries config run utils sig_aarch64 other_cmds)) + (libraries config run utils sig_aarch64 other_cmds relsim)) (executable (name main_riscv64) @@ -20,4 +20,4 @@ (flags (:standard -open Utils)) (modules copySourcesCmd copySources dumpDwarf dumpSym readDwarf) - (libraries run utils config state trace)) + (libraries run utils config state trace relsim)) diff --git a/src/bin/readDwarf.ml b/src/bin/readDwarf.ml index 577cac14..9fe1ac33 100644 --- a/src/bin/readDwarf.ml +++ b/src/bin/readDwarf.ml @@ -76,6 +76,7 @@ let commands = Run.TestRelProg.command; CopySourcesCmd.command; Z3.Test.command; + Relsim.command; ] let _ = Printexc.record_backtrace Config.enable_backtrace diff --git a/src/relsim/base.ml b/src/relsim/base.ml deleted file mode 100644 index 43df0a71..00000000 --- a/src/relsim/base.ml +++ /dev/null @@ -1,115 +0,0 @@ -(* open Logs.Logger (struct - let str = __MODULE__ -end) - -module Sums = Exp.Sums -module Typed = Exp.Typed - -module Var = struct - (** The type of variables *) - type t = Left of State.var | Right of State.var - - let equal a b = match (a,b) with - | Left a, Left b -> State.Var.equal a b - | Right a, Right b -> State.Var.equal a b - | _ -> false - - let pp = function - | Left v -> Pp.(!^"L:" ^^ State.Var.pp v) - | Right v -> Pp.(!^"R:" ^^ State.Var.pp v) - - (** Get the type of the variable *) - let ty = function Left v | Right v -> State.Var.ty v - - let hash = Hashtbl.hash - - let of_string = State.Var.of_string (*TODO*) -end - -module Exp = struct - include Exp.Make (Var) - - let left : State.Exp.t -> t = Ast.Manip.exp_map_var (fun v -> Var.Left v) - - let right : State.Exp.t -> t = Ast.Manip.exp_map_var (fun v -> Var.Right v) -end - -type sem_type = - | Value of int - | Ptr of sem_type - -type mem_rel = (State.Exp.t * State.Exp.t * sem_type) list - -let rec sem_type_of_ctype Ctype.{unqualified; _} = - match unqualified with - | Machine b -> Value b - | Cint { size; _ } -> Value size - | Cbool -> Value 1 - | Ptr { fragment=Ctype.DynArray t; _ } -> Ptr (sem_type_of_ctype t) - | _ -> Raise.todo() - -let mem_rel_of_dwarf (dw: (Dw.Var.t * Dw.Var.t) list) : mem_rel = - List.map (fun ((v1: Dw.Var.t), (v2: Dw.Var.t)) -> - let addr1 = match v1.locs with - | [_, Global a] -> - a |> Elf.SymTable.to_addr_offset - |> State.Exp.of_address ~size:Arch.address_size - (* |> Ast.Manip.exp_map_var (fun x -> Var.Left x) *) - | _ -> Raise.todo() - in - let addr2 = match v2.locs with - | [_, Global a] -> - a |> Elf.SymTable.to_addr_offset - |> State.Exp.of_address ~size:Arch.address_size - (* |> Ast.Manip.exp_map_var (fun x -> Var.Right x) *) - | _ -> Raise.todo() - in - let stp = sem_type_of_ctype v1.ctype in - (addr1, addr2, stp) - ) dw - -type rel = mem_rel * Exp.t list - -type event = State.Mem.Fragment.Event.t -type block = State.Mem.Fragment.Block.t - -let type_at (mem_rel:mem_rel) (block1:block) (block2:block) = - List.find_map (fun (e1,e2,t) -> - let (sym1, off1) = Sums.split_concrete e1 in - let (sym2, off2) = Sums.split_concrete e2 in - if (BitVec.to_int off1 == block1.offset && BitVec.to_int off2 == block2.offset - && Option.equal State.Exp.equal sym1 block1.base - && Option.equal State.Exp.equal sym2 block2.base) - then - Some t - else - None - ) mem_rel - -module Z3sim = Z3.Make (Var) - -let update_rel ((mem, asserts):rel) (e1: event) (e2: event) : rel = - (* TODO check sizes *) - match e1, e2 with - | (Read (block1, v1), Read (block2, v2)) -> ( - match type_at mem block1 block2 with - | Some (Value _) -> mem, Typed.(Exp.of_var (Left v1) = Exp.of_var (Right v2))::asserts - | Some (Ptr t) -> (State.Exp.of_var v1, State.Exp.of_var v2, t)::mem, asserts - | None -> mem, asserts - ) - | (Write (block1, e1), Write (block2, e2)) -> ( - (match type_at mem block1 block2 with - | Some (Value _) -> Z3 - | Some (Ptr t) -> Raise.todo() (* Check (e1, e2, t) in mem *) - | None -> Raise.fail "simrel failed"); - mem, asserts - ) - | _ -> Raise.fail "simrel failed" - - -let verify (st1:State.t) (st2:State.t) (dw:(Dw.Var.t * Dw.Var.t) list) = - let mem_rel = mem_rel_of_dwarf dw in - Raise.todo() - - - *) diff --git a/src/relsim/dune b/src/relsim/dune new file mode 100644 index 00000000..2f6abe26 --- /dev/null +++ b/src/relsim/dune @@ -0,0 +1,6 @@ +(library + (name relsim) + (public_name read-dwarf.relsim) + (flags + (:standard -open Utils)) + (libraries utils ast state run z3)) \ No newline at end of file diff --git a/src/relsim/relsim.ml b/src/relsim/relsim.ml new file mode 100644 index 00000000..3146a1b4 --- /dev/null +++ b/src/relsim/relsim.ml @@ -0,0 +1,447 @@ +open Logs.Logger (struct + let str = __MODULE__ +end) + +open Cmdliner +open Config.CommonOpt + +module Sums = Exp.Sums +module Typed = Exp.Typed + +module Var = struct + type t = Left of State.var | Right of State.var + + let equal a b = match (a,b) with + | Left a, Left b -> State.Var.equal a b + | Right a, Right b -> State.Var.equal a b + | _ -> false + + let pp = function + | Left v -> Pp.(!^"L:" ^^ State.Var.pp v) + | Right v -> Pp.(!^"R:" ^^ State.Var.pp v) + + let ty = function Left v | Right v -> State.Var.ty v + + let hash = Hashtbl.hash + + let of_string s = + let v = State.Var.of_string @@ String.sub s 2 (String.length s - 2) in + match String.sub s 0 2 with + | "L:" -> Left v + | "R:" -> Right v + | _ -> Raise.inv_arg "Invalid variable: %s" s +end + +module Exp = struct + include Exp.Make (Var) + + let left : State.Exp.t -> t = Ast.Manip.exp_map_var (fun v -> Var.Left v) + + let right : State.Exp.t -> t = Ast.Manip.exp_map_var (fun v -> Var.Right v) +end + +module Z3sim = Z3.Make (Var) + +type sem_type = +| Value of int +| Ptr of sem_type + +type value_relation = +| Eq +| EqSection of string +| EqPage of string +| Indirect of sem_type + +let rec pp_sem_type = Pp.(function +| Value w -> !^"Val"^^(int w) +| Ptr typ -> (pp_sem_type typ)^^(!^"*") +) + +let pp_rel = Pp.(function +| Eq -> !^"Eq" +| EqSection s -> !^"EqSection " ^^ !^s +| EqPage s -> !^"EqPage " ^^ !^s +| Indirect typ -> !^"Indirect " ^^ (pp_sem_type typ) +) + +let rec sem_type_of_type (typ: Ctype.t) : sem_type = + match typ.unqualified with + | Ctype.Machine _ | Ctype.Cint _ | Ctype.Cbool | Ctype.Enum _ -> Value (Ctype.sizeof typ) + | Ptr { fragment=Ctype.DynArray typ'; _ } -> Ptr (sem_type_of_type typ') + | _ -> Raise.todo() + +let value_rel_for_type: Ctype.unqualified -> value_relation = function +| Ctype.Machine _ | Ctype.Cint _ | Ctype.Cbool | Ctype.Enum _ -> Eq +| Ptr { fragment=Ctype.Global s; _ } -> EqSection s +| Ptr { fragment=Ctype.DynFragment i; _ } -> EqSection ("Dyn_"^string_of_int i) +| Ptr { fragment=Ctype.DynArray typ'; _ } -> Indirect (sem_type_of_type typ') +| _ -> Raise.todo() + +exception SimulationFailure of string + +let fail_sim fmt = + let fail msg = raise(SimulationFailure msg) in + Printf.ksprintf fail fmt + +let pp_diff pre pp l r = + let open Pp in + surround 2 2 + pre + (!^"L: "^^pp l ^^ space ^^ !^"R: "^^pp r) + empty + +module ExpRel = struct + type t = State.exp * value_relation * State.exp + + let to_exp ((exp1, rel, exp2):t) = + let open Option in + let modify e = + match rel with + | Eq -> e |> some + | EqSection s -> Typed.(e - State.Exp.of_var (State.Var.Section s)) |> some + (* TODO this is probably wrong: *) + | EqPage s -> Typed.(e - concat [extract ~first:12 ~last:63 (State.Exp.of_var (State.Var.Section s)); bits_int ~size:12 0]) |> some + | Indirect _ -> None + in + let+ e1, e2 = lift_pair (modify exp1, modify exp2) in + Typed.((Exp.left e1) = (Exp.right e2)) + + let pp ((a, r, b):t) = + let open Pp in + pp_diff + (pp_rel r ^^ !^" between") + Exp.pp (Exp.left a) (Exp.right b) +end + +module RegRel = struct + type t = value_relation State.Reg.Map.t + + let special_regs = ["OSDLR_EL1"; "OSLSR_EL1"; "EDSCR"; "SCR_EL3"] + + let infer_from_types (s:State.t) = + State.Reg.Map.mapi (fun reg (r:State.Tval.t) -> + if List.exists ((=) (State.Reg.to_string reg)) special_regs then + Some Eq + else + Option.map (fun (r:Ctype.t) -> + value_rel_for_type r.unqualified + ) r.ctyp + ) s.regs + + let to_exp_rel (s1:State.t) (s2:State.t) reg_rel : ExpRel.t list = + let bindings = State.Reg.Map.bindings reg_rel in + List.filter_map (fun (reg, rel) -> + Option.map (fun rel -> + State.get_reg_exp s1 reg, rel, State.get_reg_exp s2 reg + ) rel + ) bindings +end + +module StackRel = struct + module RelMap = RngMap.Make (struct + type t = value_relation * int + let len (_, sz: t) = sz + end) + type t = RelMap.t + + type loc = { offset:int; size:int } + + module Event = State.Mem.Fragment.Event + + let loc_of_blocks (blk1:State.Mem.Fragment.Block.t) (blk2:State.Mem.Fragment.Block.t) = + if Option.is_some blk1.base || Option.is_some blk2.base then + Raise.todo(); + if blk1.offset != blk2.offset || blk1.size != blk2.size then + fail_sim "blocks don't match (%d, %t bytes) (%d, %t bytes)" + blk1.offset (Pp.tos Ast.Size.pp_bytes blk1.size) + blk2.offset (Pp.tos Ast.Size.pp_bytes blk2.size); + { offset=blk1.offset; size=Ast.Size.to_bytes blk1.size } + + let rel_at_loc stack loc = + let open Option in + let* ((rel, relsz), reloff) = RelMap.at_off_opt stack loc.offset in + if loc.size != relsz || reloff != 0 then + (warn "Size not matching"; None) + else + Some rel + + let clear_loc stack loc = + RelMap.clear stack ~pos:loc.offset ~len:loc.size + + let infer_from_types ~stack_frag (st1:State.t) = + let frag = Vec.get st1.fenv.frags stack_frag in + let stack = ref RelMap.empty in + State.Fragment.iteri (fun off ctype -> + stack := RelMap.add !stack off (value_rel_for_type ctype.unqualified, Ctype.len ctype) + ) frag; + !stack +end + +module GlobalRel = struct + type eq_pair = State.exp * State.exp * sem_type + + type t = eq_pair list + + let find ~hyps (rel:t) a1 a2 = + let check_one a1 a2 (a1', a2', typ) = + debug "%t %t %t %t" (Pp.top State.Exp.pp a1) (Pp.top State.Exp.pp a2) (Pp.top State.Exp.pp a1') (Pp.top State.Exp.pp a2'); + let equal = Z3sim.check_full ~hyps Typed.(manyop Ast.And [Exp.left a1= Exp.left a1'; Exp.right a2= Exp.right a2']) in + match equal with + | Some true -> Some typ + | _ -> None + in + List.find_map (check_one a1 a2) rel + + let add (rel:t) ((a1, a2, typ): eq_pair) = + (a1, a2, typ)::rel + + let check ~hyps (rel:t) ((a1, a2, typ): eq_pair) = + Option.map ((=) typ) (find ~hyps rel a1 a2) + + let rel_of_sem_type = function + | Ptr t -> Indirect t + | Value _ -> Eq +end + +let block_addr (blk : State.Mem.Fragment.Block.t) = + let ext = 64-Arch.address_size in + match blk.base with + | None -> Typed.bits_int ~size:64 blk.offset + | Some b -> Typed.(unop (Ast.ZeroExtend ext) b + bits_int ~size:64 blk.offset) + +let ptr_safety_asserts typ v = + let sz = match typ with + | Value x -> x + | Ptr _ -> 8 (*assume 64 bit pointers*) + in + let topbits = (64 - Arch.address_size) in + let small_enough = Typed.(extract ~first:Arch.address_size ~last:63 v = zero ~size:topbits) in + + let last = sz - 1 in + let aligned = Typed.(extract ~first:0 ~last v = zero ~size:sz) in + + [small_enough; aligned] + +module Context = struct + type t = { + asserts: Exp.t list; + stack: StackRel.t; + global: GlobalRel.t; + } + + module Event = State.Mem.Fragment.Event + + let add_expr_rel (ctxt:t) rel = + match rel with + | (v1, Indirect t, v2) -> + let safety1 = ptr_safety_asserts t v1 |> List.map Exp.left in + let safety2 = ptr_safety_asserts t v2 |> List.map Exp.right in + let nullptrs = Typed.((Exp.left v1 = zero ~size:64) = (Exp.right v2 = zero ~size:64)) in + { + asserts = safety1 @ safety2 @ nullptrs::ctxt.asserts; + global = GlobalRel.add ctxt.global (v1, v2, t); + stack = ctxt.stack; + } + | rel -> + let exp = Option.value_fail (ExpRel.to_exp rel) "Failed to convert relation to expression" in + { ctxt with asserts = exp::ctxt.asserts } + + let check_expr_rel (ctxt:t) rel = + match rel with + | (v1, Indirect t, v2) -> + GlobalRel.check ~hyps:ctxt.asserts ctxt.global (v1, v2, t) + | rel -> + let exp = Option.value_fail (ExpRel.to_exp rel) "Failed to convert relation to expression" in + Z3sim.check_full ~hyps:ctxt.asserts exp + + let process_stack_operation event1 event2 (ctxt: t) = + match event1, event2 with + | Event.Read (blk1, v1), Event.Read (blk2, v2) -> + let loc = StackRel.loc_of_blocks blk1 blk2 in + let rel = StackRel.rel_at_loc ctxt.stack loc in + ( match rel with + | Some rel -> add_expr_rel ctxt (State.Exp.of_var v1, rel, State.Exp.of_var v2) + | None -> (debug "No relation for stack read %t %t" (Pp.top Event.pp event1) (Pp.top Event.pp event2); ctxt) + ) + | Event.Write (blk1, _exp1), Event.Write (blk2, _exp2) -> + let loc = StackRel.loc_of_blocks blk1 blk2 in + { ctxt with stack = StackRel.clear_loc ctxt.stack loc} + | _ -> fail_sim "traces don't match %t %t" (Pp.tos Event.pp event1) (Pp.tos Event.pp event2) + + let process_global_operation event1 event2 (ctxt: t) = + match event1, event2 with + | Event.Read (blk1, v1), Event.Read (blk2, v2) -> + let addr1 = block_addr blk1 in + let addr2 = block_addr blk2 in + let typ = GlobalRel.find ~hyps:ctxt.asserts ctxt.global addr1 addr2 in + ( match typ with + | Some typ -> + let rel = GlobalRel.rel_of_sem_type typ in + add_expr_rel ctxt (State.Exp.of_var v1, rel, State.Exp.of_var v2) + | None -> (warn "No relation for global read %t %t" (Pp.top Event.pp event1) (Pp.top Event.pp event2); ctxt) + ) + | Event.Write (blk1, exp1), Event.Write (blk2, exp2) -> + let addr1 = block_addr blk1 in + let addr2 = block_addr blk2 in + let typ = GlobalRel.find ~hyps:ctxt.asserts ctxt.global addr1 addr2 in + ( match typ with + | Some typ -> + let rel = GlobalRel.rel_of_sem_type typ in + if check_expr_rel ctxt (exp1, rel, exp2) <> Some true then + fail_sim "Unable to verify %t" (Pp.tos ExpRel.pp (exp1, rel, exp2)) + | None -> fail_sim "Unable to determine target type for global write %t %t" (Pp.tos Event.pp event1) (Pp.tos Event.pp event2) + ); + ctxt + | _ -> fail_sim "traces don't match %t %t" (Pp.tos Event.pp event1) (Pp.tos Event.pp event2) + + let infer_from_types ~stack_frag ~(dwarf:Dw.t) (state: State.t) = + let stack = StackRel.infer_from_types state ~stack_frag in + + let regs = RegRel.infer_from_types state in + let register_rels = RegRel.to_exp_rel state state regs in + + let global_variable_rels = + Hashtbl.to_seq_values dwarf.vars + |> Seq.map (fun (v:Dw.Var.t) -> + let typ = sem_type_of_type v.ctype in + match v.locs with + | [_, Global addr] -> + let addr = Elf.SymTable.to_addr_offset addr in + let exp = State.Exp.of_address ~size:64 addr in + (exp, Indirect typ, exp) + | _ -> + Raise.fail "Weird location description for global variable: %t" (Pp.tos Dw.Var.pp_raw v); + ) + |> List.of_seq + in + + let ctxt = { stack; asserts=[]; global=[] } in + List.fold_left add_expr_rel ctxt (register_rels @ global_variable_rels) +end + +type simrel = (State.Id.t*State.Id.t, Context.t) Hashtbl.t + +let stack_prov = 0(* TODO determine stack_frag automatically *) +let stack_frag = 0(* TODO determine stack_frag automatically *) + +exception SimulationFailureWithContext of { + msg:string; + states: State.t * State.t; + ctxt: Context.t; +} + +let rec checksim ~(rel:simrel) (s1:State.t) (s2:State.t) = + Hashtbl.find_opt rel (s1.id, s2.id) + |> Option.value_fun ~default:(fun() -> + let bs1 = Option.value_fail s1.base_state "no base state" in + let bs2 = Option.value_fail s2.base_state "no base state" in + let prev_ctxt = checksim ~rel bs1 bs2 in + try + + (* Process stack trace *) + let ((_,mem1), (_,mem2)) = State.Mem.(get_frag s1.mem stack_prov, get_frag s2.mem stack_prov) in + let (trc1, trc2) = State.Mem.Fragment.(get_trace mem1, get_trace mem2) in + + let ctxt = List.fold_right2 Context.process_stack_operation trc1 trc2 prev_ctxt in + + (* Process global trace *) + let (mem1, mem2) = State.Mem.(get_main s1.mem, get_main s2.mem) in + let (trc1, trc2) = State.Mem.Fragment.(get_trace mem1, get_trace mem2) in + + let ctxt = List.fold_right2 Context.process_global_operation trc1 trc2 ctxt in + + + let asst1 = List.map Exp.left s1.asserts in + let asst2 = List.map Exp.right s2.asserts in + let impl1 = List.find_all Fun.(Z3sim.check_full ~hyps:(asst1@ctxt.asserts) %> flip Option.value_fail "TODO: Z3 failed" %> not) asst2 in + let impl2 = List.find_all Fun.(Z3sim.check_full ~hyps:(asst2@ctxt.asserts) %> flip Option.value_fail "TODO: Z3 failed" %> not) asst1 in + if not (List.is_empty impl1 && List.is_empty impl2) then + fail_sim "%t" Pp.(Fun.const @@ sprint @@ pp_diff + !^"States not equivalent on path conditions" + (list Exp.pp) + impl2 + impl1 + ); + + Hashtbl.add rel (s1.id, s2.id) ctxt; + ctxt + with + | SimulationFailure s -> raise @@ SimulationFailureWithContext { + msg=s; + states=(s1,s2); + ctxt=prev_ctxt; + } + ) + +let check_return_values ~(ret_reg) ~(ret_type:Ctype.t) ~(rel:simrel) (s1:State.t) (s2:State.t) = + let ctxt = Hashtbl.find rel (s1.id, s2.id) in + + let ret_val1 = State.get_reg_exp s1 ret_reg in + let ret_val2 = State.get_reg_exp s2 ret_reg in + let rel = value_rel_for_type ret_type.unqualified in + + if Context.check_expr_rel ctxt (ret_val1, rel, ret_val2) <> Some true then + raise @@ SimulationFailureWithContext { + msg=Printf.sprintf "Return values not equivalent +Condition: %t\n" (Pp.tos ExpRel.pp (ret_val1, rel, ret_val2)); + states=(s1,s2); + ctxt=ctxt; + } + + +let run elf name = + let dwarf = Dw.of_file elf in + let tree = Run.Func.get_state_tree ~elf ~name () in + let initial_state = tree.state in + + let initial_ctxt = Context.infer_from_types ~stack_frag ~dwarf initial_state in + + let simrel:simrel = Hashtbl.create 10 in + Hashtbl.add simrel (initial_state.id, initial_state.id) initial_ctxt; + + debug "%t" (Pp.top (State.Tree.pp_all Run.Block_lib.pp_label) tree); + + let ret = Option.( + let* func =Dw.get_func_opt ~name dwarf in + let+ typ = func.func.ret in + if Ctype.sizeof typ > 8 then + Raise.fail "unsupported return type %t" (Pp.tos Ctype.pp typ) + else + ((Arch.dwarf_reg_map()).(0), typ) + ) in + + try + State.Tree.prefix_iter (fun _ s -> + checksim ~rel:simrel s s |> ignore; + if State.get_reg_exp s (Arch.pc()) = State.Exp.of_var State.Var.RetAddr then + Option.iter (fun (ret_reg, ret_type) -> + check_return_values ~ret_reg ~ret_type ~rel:simrel s s; + ) ret + ) tree; + base "Simulation successful" + with + | SimulationFailureWithContext e -> + let st, _ = e.states in + debug "Failing state: %t" (Pp.top State.pp st); + base "Simulation failed:\n\n%s" e.msg + +let elf = + let doc = "ELF file from which to pull the code" in + Arg.(required & pos 0 (some non_dir_file) None & info [] ~docv:"ELF_FILE" ~doc) + +let func = + let doc = "Symbol name of the function to run" in + Arg.(value & pos 1 string "main" & info [] ~docv:"FUNCTION" ~doc) + +let term = + Term.( + CmdlinerHelper.func_options comopts run + $ elf $ func) + +let info = + let doc = + "Simulation relation on relocatable binary" + in + Cmd.(info "relsim" ~doc ~exits) + +let command = (term, info) diff --git a/src/state/base.ml b/src/state/base.ml index 2657fa7d..17659776 100644 --- a/src/state/base.ml +++ b/src/state/base.ml @@ -328,6 +328,10 @@ module Mem = struct (** Get the main fragment of memory *) let get_main { main; _ } = main + (** Get fragment *) + let get_frag mem i = + Vec.get mem.frags i + (** Empty memory, every address is unbound *) let empty () = { main = Fragment.empty; frags = Vec.empty (); sections = Hashtbl.create 10; allow_main = true } diff --git a/src/state/base.mli b/src/state/base.mli index 2d373fd4..73d6c370 100644 --- a/src/state/base.mli +++ b/src/state/base.mli @@ -259,6 +259,9 @@ module Mem : sig (** Get the main fragment of memory *) val get_main : t -> Fragment.t + + (** Get fragment *) + val get_frag : t -> int -> Exp.t * Fragment.t end (** {1 State type } *) diff --git a/src/state/reg.mli b/src/state/reg.mli index fd67c573..b9692051 100644 --- a/src/state/reg.mli +++ b/src/state/reg.mli @@ -216,6 +216,9 @@ module Map : sig (** Map the function all the registers (including future, not yet added ones) *) val map : ('a -> 'b) -> 'a t -> 'b t + (** Same as {!map} but with the index *) + val mapi : (reg -> 'a -> 'b) -> 'a t -> 'b t + (** Map the function on all the register by mutation (including future ones) *) val map_mut : ('a -> 'a) -> 'a t -> unit diff --git a/src/state/symbolicFragment.ml b/src/state/symbolicFragment.ml index dbc941fa..40ef78f2 100644 --- a/src/state/symbolicFragment.ml +++ b/src/state/symbolicFragment.ml @@ -98,6 +98,8 @@ module type S = sig type t = | Read of Block.t * var (** From [Block.t], read [var] *) | Write of Block.t * exp (** To [Block.t], write [exp] *) + + val pp : t -> Pp.document end (** The type of a memory fragment *) diff --git a/src/trace/context.ml b/src/trace/context.ml index 2fe350d4..59950dc2 100644 --- a/src/trace/context.ml +++ b/src/trace/context.ml @@ -106,6 +106,9 @@ let typing_enabled ~(ctxt : t) = ctxt.dwarf <> None module Z3St = State.Simplify.Z3St let simplify ~(ctxt : t) (exp : State.exp) : State.exp = + debug "Before simplification: %t" (Pp.top State.Exp.pp exp); + debug "Before simplification: %t" (Pp.top State.Exp.pp (Z3St.simplify_full exp)); exp |> Z3St.simplify_subterms_full ~hyps:ctxt.asserts - |> Z3St.simplify_full \ No newline at end of file + |> Z3St.simplify_full + |> Fun.tee (fun e -> debug "After simplification: %t" (Pp.top State.Exp.pp e)) \ No newline at end of file diff --git a/src/utils/fullVec.ml b/src/utils/fullVec.ml index f6bea2c1..998e2232 100644 --- a/src/utils/fullVec.ml +++ b/src/utils/fullVec.ml @@ -107,6 +107,11 @@ let map f fv = let gen = fv.gen %> f in { vec; gen } +let mapi f fv = + let vec = Vec.mapi f fv.vec in + let gen = (fun i -> fv.gen i |> f i) in + { vec; gen } + let map_mut f fv = Vec.map_mut f fv.vec; fv.gen <- fv.gen %> f diff --git a/src/utils/fullVec.mli b/src/utils/fullVec.mli index 51d9f079..9a67676f 100644 --- a/src/utils/fullVec.mli +++ b/src/utils/fullVec.mli @@ -89,6 +89,9 @@ val get_vec_until : 'a t -> int -> 'a Vec.t (** Map the function over the fullvec. Postcompose the map on the generator *) val map : ('a -> 'b) -> 'a t -> 'b t +(** Same as {!map} but with the index *) +val mapi : (int -> 'a -> 'b) -> 'a t -> 'b t + (** Map the function over the fullvector by mutation. Postcompose the map on the generator.contents Warning, a lot of {!map_mut} may make the generator big and slow. Maybe try to use {!set_after} to reset it when required.*) From 0d7f6b19051fc66d06a7cb8834e1e5598047f194 Mon Sep 17 00:00:00 2001 From: maturvo <59334936+maturvo@users.noreply.github.com> Date: Tue, 13 May 2025 01:53:03 +0100 Subject: [PATCH 089/116] Fix analyse (#3) --- src/analyse/CallGraph.ml | 6 +- src/analyse/ControlFlow.ml | 108 ++++++++++++++++++++------- src/analyse/ControlFlowTypes.ml | 1 + src/analyse/DwarfFrameInfo.ml | 7 +- src/analyse/DwarfInliningInfo.ml | 6 +- src/analyse/DwarfLineInfo.ml | 6 +- src/analyse/DwarfVarInfo.ml | 8 +- src/analyse/Pp.ml | 14 +++- src/analyse/html-preamble-insts.html | 3 + src/utils/sym.ml | 36 ++++++--- 10 files changed, 141 insertions(+), 54 deletions(-) diff --git a/src/analyse/CallGraph.ml b/src/analyse/CallGraph.ml index 026db808..1a070b26 100644 --- a/src/analyse/CallGraph.ml +++ b/src/analyse/CallGraph.ml @@ -110,7 +110,7 @@ let mk_call_graph test (an : CollectedType.analysis) = if not (List.exists - (function (a'', _) -> Sym.equal a' a'') + (function (a'', _) -> Sym.Ordered.equal a' a'') elf_symbols) then Some (a', ["FROM BL:" ^ s']) else None) @@ -122,7 +122,7 @@ let mk_call_graph test (an : CollectedType.analysis) = match axs with | [] -> acc | (a, x) :: axs' -> - if not (List.exists (function (a', _) -> Sym.equal a a') acc) then + if not (List.exists (function (a', _) -> Sym.Ordered.equal a a') acc) then dedup axs' ((a, x) :: acc) else dedup axs' acc in @@ -133,7 +133,7 @@ let mk_call_graph test (an : CollectedType.analysis) = List.sort (function | (a, _) -> ( - function (a', _) -> Sym.compare a a' + function (a', _) -> Sym.Ordered.compare a a' )) (elf_symbols @ extra_bl_targets) in diff --git a/src/analyse/ControlFlow.ml b/src/analyse/ControlFlow.ml index bba61c63..8eefb233 100644 --- a/src/analyse/ControlFlow.ml +++ b/src/analyse/ControlFlow.ml @@ -295,9 +295,9 @@ with (Scanf.Scan_failure _ | End_of_file) -> Scanf.sscanf s "%Lx" (fun i64 -> Sym.of_int64 i64) -let parse_target s = +let parse_target base s = match Scanf.sscanf s " %s %s" (fun s1 s2 -> (s1, s2)) with - | (s1, s2) -> Some (parse_addr s1, s2) + | (s1, s2) -> Some (Sym.add base (parse_addr s1), s2) | exception _ -> None let parse_drop_one s = @@ -309,7 +309,20 @@ let parse_drop_one s = | (_, s') -> Some s' | exception _ -> None -let parse_control_flow_instruction s mnemonic s' : control_flow_insn = +let parse_relocation_target symbol_map s = + let s, offset = match String.split_on_char '+' s with + | [s1; s2] -> + s1, Scanf.sscanf s2 "0x%x" Fun.id + | [s] -> s, 0 + | _ -> fatal "Unable to parse relocation target '%s'" s + in + let addr = List.find_map (fun (name, (_,_,addr,_,_)) -> if name = s then Some addr else None) symbol_map in + Option.map (Sym.add (Sym.of_int offset)) addr + +let parse_control_flow_instruction symbol_map base s mnemonic s' relocation : control_flow_insn = + let relocation_target = Option.bind relocation (fun (_typ, target) -> + Option.map (fun a -> (a, target)) (parse_relocation_target symbol_map target) + ) in (* Printf.printf "s=\"%s\" mnemonic=\"%s\" mnemonic chars=\"%s\" s'=\"%s\" "s mnemonic (String.concat "," (List.map (function c -> string_of_int (Char.code c)) (char_list_of_string mnemonic))) s';flush stdout;*) let c = if List.mem String.equal mnemonic [".word"] then C_no_instruction @@ -320,9 +333,9 @@ let parse_control_flow_instruction s mnemonic s' : control_flow_insn = (String.length mnemonic >= 2 && String.sub mnemonic 0 2 = "b.") || List.mem String.equal mnemonic ["b"; "bl"] then - match parse_target s' with - | None -> raise (Failure ("b./b/bl parse error for: \"" ^ s ^ "\"\n")) - | Some (a, s) -> + match parse_target base s', relocation_target with + | None, None -> raise (Failure ("b./b/bl parse error for: \"" ^ s ^ "\"\n")) + | _, Some(a, s) | Some (a, s), None -> if mnemonic = "b" then C_branch (a, s) else if mnemonic = "bl" then C_branch_and_link (a, s) else C_branch_cond (mnemonic, a, s) @@ -330,9 +343,9 @@ let parse_control_flow_instruction s mnemonic s' : control_flow_insn = match parse_drop_one s' with | None -> raise (Failure ("cbz/cbnz 1 parse error for: " ^ s ^ "\n")) | Some s' -> ( - match parse_target s' with - | None -> raise (Failure ("cbz/cbnz 2 parse error for: " ^ s ^ "\n")) - | Some (a, s) -> C_branch_cond (mnemonic, a, s) + match parse_target base s', relocation_target with + | None, None -> raise (Failure ("cbz/cbnz 2 parse error for: " ^ s ^ "\n")) + | _, Some(a, s) | Some (a, s), None -> C_branch_cond (mnemonic, a, s) ) else if List.mem String.equal mnemonic ["tbz"; "tbnz"] then match parse_drop_one s' with @@ -341,9 +354,9 @@ let parse_control_flow_instruction s mnemonic s' : control_flow_insn = match parse_drop_one s'' with | None -> raise (Failure ("tbz/tbnz 2 parse error for: " ^ s ^ "\n")) | Some s''' -> ( - match parse_target s''' with - | None -> raise (Failure ("tbz/tbnz 3 parse error for: " ^ s ^ "\n")) - | Some (a, s'''') -> + match parse_target base s''', relocation_target with + | None, None -> raise (Failure ("tbz/tbnz 3 parse error for: " ^ s ^ "\n")) + | _, Some(a, s'''') | Some (a, s''''), None -> (* Printf.printf "s=%s mnemonic=%s s'=%s s''=%s s'''=%s s''''=%s\n"s mnemonic s' s'' s''' s'''';*) C_branch_cond (mnemonic, a, s'''') ) @@ -438,14 +451,21 @@ AArch64: 10004: 52800129 mov w9, #0x9 // #9 *) +let relocation_regexp_string = "[ \t][0-9a-fA-F]+:[ \t]\\([0-9A-Z_]+\\)\t\\(.*\\)" + let objdump_line_regexp = - Str.regexp " *\\([0-9a-fA-F]+\\):[ \t]\\([0-9a-fA-F ]+\\)\t\\([^ \r\t\n]+\\) *\\(.*\\)$" + Str.regexp (" *\\([0-9a-fA-F]+\\):[ \t]\\([0-9a-fA-F ]+\\)\t\\([^ \r\t\n]+\\)[ \t]*\\([^:]*\\)\\(" ^ relocation_regexp_string ^ "\\)?$") let section_start_line_regexp = Str.regexp "Disassembly of section \\(.*\\):$" +type relocation = string * string + +type raw_objdump_instruction = + int64 (*address*) * int list (*opcode bytes*) * string (*mnemonic*) * string * relocation option + type objdump_instruction = - natural (*address*) * int list (*opcode bytes*) * string (*mnemonic*) * string + natural (*address*) * int list (*opcode bytes*) * string (*mnemonic*) * string * relocation option (*args etc*) @@ -455,7 +475,7 @@ let parse_section_start s = else None -let parse_objdump_line (s : string) : (int64 * int list * string * string) option = +let parse_objdump_line (s : string) : raw_objdump_instruction option = let parse_hex_int64 s' = try Scanf.sscanf s' "%Lx" (fun i64 -> i64) with _ -> fatal "cannot parse address in objdump line %s\n" s @@ -474,6 +494,7 @@ let parse_objdump_line (s : string) : (int64 * int list * string * string) optio in if Str.string_match objdump_line_regexp s 0 then begin + (* debug "matched line"; *) let addr_int64 = parse_hex_int64 (Str.matched_group 1 s) in let op = Str.matched_group 2 s in let op = strip_whitespace op in @@ -486,17 +507,43 @@ let parse_objdump_line (s : string) : (int64 * int list * string * string) optio let opcode_bytes = List.map parse_hex_int opcode_byte_strings in let mnemonic = Str.matched_group 3 s in let operands = Str.matched_group 4 s in - Some (addr_int64, opcode_bytes, mnemonic, operands) + let relocation = try + Some (Str.matched_group 6 s, Str.matched_group 7 s) + with + | Not_found -> None + in + Some (addr_int64, opcode_bytes, mnemonic, operands, relocation) end else None +(* let parse_objdump_relocation (s : string) : (string * string) option = + let parse_hex_int s' = + try Scanf.sscanf s' "%x" (fun i -> i) + with _ -> fatal "cannot parse relocation '%s' in objdump line %s\n" s' s + in + if Str.string_match objdump_line_regexp s 0 then + begin + let addr = Str.matched_group 1 s in + let op = Str.matched_group 2 s in + let op = strip_whitespace op in + let opcode_byte_strings = + [String.sub op 0 2; + String.sub op 2 2; + String.sub op 4 2; + String.sub op 6 2] + in + let opcode_bytes = List.map parse_hex_int opcode_byte_strings in + Some (addr, op) + end + else None *) + (* let parse_objdump_lines arch lines : objdump_instruction list = List.filter_map (parse_objdump_line arch) (Array.to_list lines) *) -let with_symbolic_address (section: string) (addr, opcode_bytes, mnemonic, operands) : objdump_instruction = - (Sym_ocaml.Num.Offset (section, Nat_big_num.of_int64 addr), opcode_bytes, mnemonic, operands) +let with_symbolic_address (section: string) (addr, opcode_bytes, mnemonic, operands, relocation) : objdump_instruction = + (Sym_ocaml.Num.Offset (section, Nat_big_num.of_int64 addr), opcode_bytes, mnemonic, operands, relocation) let rec parse_objdump_lines arch lines (next_index : int) (last_address : int64 option) (section: string option) : objdump_instruction list = @@ -506,7 +553,7 @@ let rec parse_objdump_lines arch lines (next_index : int) (last_address : int64 match parse_objdump_line lines.(next_index) with (* skip over unparseable lines *) | None -> parse_objdump_lines arch lines (next_index + 1) last_address section - | Some ((addr, _opcode_bytes, _mnemonic, _operands) as i) -> ( + | Some ((addr, _opcode_bytes, _mnemonic, _operands, _relocation) as i) -> ( let mki = with_symbolic_address (Option.get section) in match last_address with | None -> mki i :: parse_objdump_lines arch lines (next_index + 1) (Some addr) section @@ -515,7 +562,7 @@ let rec parse_objdump_lines arch lines (next_index : int) (last_address : int64 if addr > last_address'' then (* fake up "missing" instructions for any gaps in the address space*) (*warn "gap in objdump instruction address sequence at %s" (pp_addr last_address'');*) - mki (last_address'', [], "missing", "") + mki (last_address'', [], "missing", "", None) :: parse_objdump_lines arch lines next_index (Some last_address'') section else mki i :: parse_objdump_lines arch lines (next_index + 1) (Some addr) section ) @@ -546,7 +593,7 @@ let mk_instructions test filename_objdump_d filename_branch_table_option : Array.iteri (function | k -> ( - function (addr, _, _, _) -> Hashtbl.add tbl addr k + function (addr, _, _, _, _) -> Hashtbl.add tbl addr k )) objdump_instructions; ( (function @@ -563,9 +610,14 @@ let mk_instructions test filename_objdump_d filename_branch_table_option : let instructions = Array.map (function - | (addr, opcode_bytes, mnemonic, operands) -> + | (addr, opcode_bytes, mnemonic, operands, relocation) -> + (* a bit hacky *) + let base = match addr with + | Sym_ocaml.Num.Offset(s,_) -> Sym_ocaml.Num.section s + | Sym_ocaml.Num.Absolute(_) -> Sym.of_int 0 + in let c : control_flow_insn = - parse_control_flow_instruction ("objdump line " ^ pp_addr addr) mnemonic operands + parse_control_flow_instruction test.symbol_map base ("objdump line " ^ pp_addr addr) mnemonic operands relocation in let targets = @@ -579,12 +631,18 @@ let mk_instructions test filename_objdump_d filename_branch_table_option : i_operands = operands; i_control_flow = c; i_targets = targets; + i_relocation = relocation; }) objdump_instructions in let address_of_index k = instructions.(k).i_addr in + Array.sort + (fun i1 i2 -> + Sym.Ordered.compare (i1.i_addr) (i2.i_addr)) + instructions; + (instructions, index_of_address, index_option_of_address, address_of_index) (* pull out indirect branches *) @@ -616,11 +674,11 @@ let highlight c = (* highlight branch targets to earlier addresses*) let pp_target_addr_wrt (addr : natural) (c : control_flow_insn) (a : natural) = - (if highlight c && Sym.less a addr then "^" else "") ^ pp_addr a + (if highlight c && Sym.Ordered.less a addr then "^" else "") ^ pp_addr a (* highlight branch come-froms from later addresses*) let pp_come_from_addr_wrt (addr : natural) (c : control_flow_insn) (a : natural) = - (if highlight c && Sym.greater a addr then "v" else "") ^ pp_addr a + (if highlight c && Sym.Ordered.greater a addr then "v" else "") ^ pp_addr a (* let pp_branch_targets (xs : (addr * control_flow_insn * (target_kind * addr * int * string) list) list) diff --git a/src/analyse/ControlFlowTypes.ml b/src/analyse/ControlFlowTypes.ml index 97815632..4ecb695f 100644 --- a/src/analyse/ControlFlowTypes.ml +++ b/src/analyse/ControlFlowTypes.ml @@ -83,6 +83,7 @@ type instruction = { i_operands : string; i_control_flow : control_flow_insn; i_targets : target list; + i_relocation : (string * string) option; } type come_from = { diff --git a/src/analyse/DwarfFrameInfo.ml b/src/analyse/DwarfFrameInfo.ml index 991b295d..63da2fc1 100644 --- a/src/analyse/DwarfFrameInfo.ml +++ b/src/analyse/DwarfFrameInfo.ml @@ -53,14 +53,15 @@ open ControlFlowTypes let aof ((a : natural), (_cfa : string), (_regs : (string * string) list)) = a +(* TODO does Sym.Ordered work as we want? *) let rec f (aof : 'b -> natural) (a : natural) (last : 'b option) (bs : 'b list) : 'b option = match (last, bs) with | (None, []) -> None - | (Some b', []) -> if Sym.greater_equal a (aof b') then Some b' else None + | (Some b', []) -> if Sym.Ordered.greater_equal a (aof b') then Some b' else None | (None, b'' :: bs') -> f aof a (Some b'') bs' | (Some b', b'' :: bs') -> - if Sym.less a (aof b') then None - else if Sym.greater_equal a (aof b') && Sym.less a (aof b'') then Some b' + if Sym.Ordered.less a (aof b') then None + else if Sym.Ordered.greater_equal a (aof b') && Sym.Ordered.less a (aof b'') then Some b' else f aof a (Some b'') bs' let mk_frame_info test instructions : diff --git a/src/analyse/DwarfInliningInfo.ml b/src/analyse/DwarfInliningInfo.ml index 832b4cac..4180e17e 100644 --- a/src/analyse/DwarfInliningInfo.ml +++ b/src/analyse/DwarfInliningInfo.ml @@ -68,7 +68,7 @@ let mk_inlining test sdt instructions = let addr = i.i_addr in let issr_still_current = List.filter - (function (_label, ((_n1, n2), (_m, _n), _is)) -> Sym.less addr n2) + (function (_label, ((_n1, n2), (_m, _n), _is)) -> Sym.Ordered.less addr n2) issr_current in @@ -83,8 +83,8 @@ let mk_inlining test sdt instructions = let (issr_starting_here0, issr_rest') = find_first - (function ((_n1, n2), (_m, _n), _is) -> Sym.less_equal n2 addr) - (function ((n1, _n2), (_m, _n), _is) -> Sym.equal n1 addr) + (function ((_n1, n2), (_m, _n), _is) -> Sym.Ordered.less_equal n2 addr) + (function ((n1, _n2), (_m, _n), _is) -> Sym.Ordered.equal n1 addr) [] issr_rest in diff --git a/src/analyse/DwarfLineInfo.ml b/src/analyse/DwarfLineInfo.ml index 7cb6bf73..2cbf4fef 100644 --- a/src/analyse/DwarfLineInfo.ml +++ b/src/analyse/DwarfLineInfo.ml @@ -269,11 +269,11 @@ let mk_line_info (eli : Dwarf.evaluated_line_info) instructions : let elifis = Array.make size [] in let sequences = List.flatten (List.map split_into_sequences eli) in - let compare_sequence s1 s2 = Sym.compare s1.elis_first s2.elis_first in + let compare_sequence s1 s2 = Sym.Ordered.compare s1.elis_first s2.elis_first in let sequences_sorted = List.sort compare_sequence sequences in let entries = List.flatten (List.map split_into_entries sequences_sorted) in - let compare_entry e1 e2 = Sym.compare e1.elie_first e2.elie_first in + let compare_entry e1 e2 = Sym.Ordered.compare e1.elie_first e2.elie_first in let entries_sorted = List.sort compare_entry entries in (*List.iter (function elie -> Printf.printf "%s" (pp_elie_concise elie)) entries_sorted;*) @@ -286,7 +286,7 @@ let mk_line_info (eli : Dwarf.evaluated_line_info) instructions : match remaining with | [] -> (acc, remaining) | elie :: remaining' -> - if Sym.less_equal elie.elie_first addr then + if Sym.Ordered.less_equal elie.elie_first addr then mk_new_perhaps_relevant (elie :: acc) remaining' else (acc, remaining) in diff --git a/src/analyse/DwarfVarInfo.ml b/src/analyse/DwarfVarInfo.ml index 144e48b5..8e1ffbec 100644 --- a/src/analyse/DwarfVarInfo.ml +++ b/src/analyse/DwarfVarInfo.ml @@ -260,7 +260,7 @@ let pp_ranged_var (prefix : string) (var : ranged_var) : string = let pp_ranged_vars (prefix : string) (vars : ranged_var list) : string = String.concat "" (List.map (pp_ranged_var prefix) vars) -let compare_pc_ranges ((n1, _, _), _) ((n1', _, _), _) = compare n1 n1' +let compare_pc_ranges ((n1, _, _), _) ((n1', _, _), _) = Sym.Ordered.compare n1 n1' let local_by_pc_ranges (((svfp : Dwarf.sdt_variable_or_formal_parameter), _context) as var) : ranged_var list = @@ -299,14 +299,14 @@ let mk_ranged_vars_at_instructions (sdt_d : Dwarf.sdt_dwarf) instructions : if k >= size then () else let addr = instructions.(k).i_addr in - if not (Sym.less addr_prev addr) then + if not (Sym.Ordered.less addr_prev addr) then fatal "mk_ranged_vars_at_instructions found non-increasing address %s" (pp_addr addr); let (still_current, old) = - List.partition (function ((_, n2, _), _) -> Sym.less addr n2) prev + List.partition (function ((_, n2, _), _) -> Sym.Ordered.less addr n2) prev in let (new', remaining') = partition_first - (function ((n1, _n2, _ops), _var) as _rv -> Sym.greater_equal addr n1) + (function ((n1, _n2, _ops), _var) as _rv -> Sym.Ordered.greater_equal addr n1) remaining in (* TODO: do we need to drop any that have been totally skipped over? *) diff --git a/src/analyse/Pp.ml b/src/analyse/Pp.ml index 0bf41866..cc57b5f6 100644 --- a/src/analyse/Pp.ml +++ b/src/analyse/Pp.ml @@ -74,6 +74,7 @@ type render_kind = | Render_vars_old | Render_inlining | Render_ctrlflow + | Render_relocation let render_colour = function | Render_symbol_star -> "gold" @@ -86,6 +87,7 @@ let render_colour = function | Render_vars_old -> "grey" | Render_inlining -> "red" | Render_ctrlflow -> "white" + | Render_relocation -> "purple" let render_class_name = function | Render_symbol_star -> "symbol-star" @@ -98,6 +100,7 @@ let render_class_name = function | Render_vars_old -> "vars-old" | Render_inlining -> "inlining" | Render_ctrlflow -> "ctrlflow" + | Render_relocation -> "relocation" type html_idiom = HI_span | HI_pre | HI_classless_span | HI_font @@ -327,13 +330,20 @@ let pp_instruction m test an rendered_control_flow_common_prefix_end k i = (ControlFlowPpText.pp_glyphs rendered_control_flow_common_prefix_end an.rendered_control_flow.(k)) (* the address and (hex) instruction *) - ^ css m Render_instruction - (pp_addr addr ^ ": " + ^ css m Render_instruction ( + pp_addr addr ^ ": " ^ pp_opcode_bytes test.arch i.i_opcode (* the dissassembly from objdump *) ^ " " ^ i.i_mnemonic ^ "\t" ^ i.i_operands ) + ^ css m Render_relocation + (match i.i_relocation with + | None -> "" + | Some (typ, targ) -> + "\t" ^ typ ^ " " ^ targ + ) + (* the instruction's control flow *) (* any indirect-branch control flow from this instruction *) ^ css m Render_ctrlflow (begin diff --git a/src/analyse/html-preamble-insts.html b/src/analyse/html-preamble-insts.html index 36645dfd..3b8adb8b 100644 --- a/src/analyse/html-preamble-insts.html +++ b/src/analyse/html-preamble-insts.html @@ -81,6 +81,9 @@ .ctrlflow { color: white; } + .relocation { + color: mediumpurple; + } a:link { color: aqua; background-color: transparent; diff --git a/src/utils/sym.ml b/src/utils/sym.ml index 42ef411d..83421beb 100644 --- a/src/utils/sym.ml +++ b/src/utils/sym.ml @@ -16,16 +16,18 @@ let max_addr = Z.(shift_left (of_int 1) 64 - (of_int 1)) let min_addr = Z.of_int 0 (* TODO very hacky *) -let less x y = match (x, y) with -| (Sym_ocaml.Num.Absolute x, Sym_ocaml.Num.Offset (_, y)) when Nat_big_num.less x y -> true -| (Sym_ocaml.Num.Absolute x, Sym_ocaml.Num.Offset (_,_)) when Nat_big_num.greater_equal x max_addr -> false -| (Sym_ocaml.Num.Offset (_,_), Sym_ocaml.Num.Absolute y) when Nat_big_num.less max_addr y -> true -| (Sym_ocaml.Num.Offset (_, x), Sym_ocaml.Num.Absolute y) when Nat_big_num.greater_equal x y -> false -| _ -> Sym_ocaml.Num.comp Nat_big_num.less x y -let less_equal = Sym_ocaml.Num.less_equal -let greater = Sym_ocaml.Num.greater -let greater_equal = Sym_ocaml.Num.greater_equal -let compare = Sym_ocaml.Num.compare +let compare x y = match (x, y) with +| (Sym_ocaml.Num.Absolute x, Sym_ocaml.Num.Offset (_, y)) when Nat_big_num.less x y -> -1 +| (Sym_ocaml.Num.Absolute x, Sym_ocaml.Num.Offset (_, _)) when Nat_big_num.greater_equal x max_addr -> 1 +| (Sym_ocaml.Num.Offset (_, x), Sym_ocaml.Num.Absolute y) when Nat_big_num.less y x -> 1 +| (Sym_ocaml.Num.Offset (_, _), Sym_ocaml.Num.Absolute y) when Nat_big_num.greater_equal y max_addr -> -1 +| (x, y) -> Sym_ocaml.Num.compare x y + +let less x y = compare x y < 0 +let less_equal x y = compare x y <= 0 +(* let equal x y = compare x y = 0 *) +let greater x y = compare x y > 0 +let greater_equal x y = compare x y >= 0 let to_string = Sym_ocaml.Num.ppf Z.to_string @@ -45,4 +47,16 @@ let in_range first last x = match (first, last, x) with | (Sym_ocaml.Num.Absolute f, Sym_ocaml.Num.Absolute l, Sym_ocaml.Num.Absolute x) -> Nat_big_num.less_equal f x && Nat_big_num.less_equal x l | (Sym_ocaml.Num.Offset (s1, f), Sym_ocaml.Num.Offset (s2, l), Sym_ocaml.Num.Offset (s, x)) when s1 = s2 -> s1 = s && Nat_big_num.less_equal f x && Nat_big_num.less_equal x l (* TODO kinda hacky *) -| _ -> Raise.fail "Can't determine if %t is in range [%t,%t]" (Pp.tos pp x) (Pp.tos pp first) (Pp.tos pp last) \ No newline at end of file +| _ -> Raise.fail "Can't determine if %t is in range [%t,%t]" (Pp.tos pp x) (Pp.tos pp first) (Pp.tos pp last) + +module Ordered = struct + let compare x y = match (x, y) with + | (Sym_ocaml.Num.Offset (s1, _x), Sym_ocaml.Num.Offset (s2, _y)) when s1 <> s2 -> String.compare s1 s2 + | (x, y) -> compare x y + + let less_equal x y = compare x y <= 0 + let less x y = compare x y < 0 + let greater x y = compare x y > 0 + let greater_equal x y = compare x y >= 0 + let equal x y = compare x y = 0 +end \ No newline at end of file From 11557319a2044cf76b91a29303c3714cf2c69451 Mon Sep 17 00:00:00 2001 From: Peter Sewell Date: Sat, 14 Jun 2025 10:49:08 +0100 Subject: [PATCH 090/116] fix syntax for parsing objdump with %-separated relocations --- src/analyse/ControlFlow.ml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/analyse/ControlFlow.ml b/src/analyse/ControlFlow.ml index 8eefb233..5089c761 100644 --- a/src/analyse/ControlFlow.ml +++ b/src/analyse/ControlFlow.ml @@ -451,10 +451,19 @@ AArch64: 10004: 52800129 mov w9, #0x9 // #9 *) +(* matej version *) +(* let relocation_regexp_string = "[ \t][0-9a-fA-F]+:[ \t]\\([0-9A-Z_]+\\)\t\\(.*\\)" let objdump_line_regexp = Str.regexp (" *\\([0-9a-fA-F]+\\):[ \t]\\([0-9a-fA-F ]+\\)\t\\([^ \r\t\n]+\\)[ \t]*\\([^:]*\\)\\(" ^ relocation_regexp_string ^ "\\)?$") +*) +(* ps version *) + +let relocation_regexp_string = "%[ \t]+[0-9a-fA-F]+:[ \t]+\\([0-9A-Z_]+\\)[ \t]+\\(.*\\)" + +let objdump_line_regexp = + Str.regexp (" *\\([0-9a-fA-F]+\\):[ \t]\\([0-9a-fA-F ]+\\)\t\\([^ \r\t\n]+\\)[ \t]*\\([^%]*\\)\\(" ^ relocation_regexp_string ^ "\\)?$") let section_start_line_regexp = Str.regexp "Disassembly of section \\(.*\\):$" From 43d8f0a268f18b0ab712ff62462af6cc4a335a1b Mon Sep 17 00:00:00 2001 From: Matej Urban Date: Wed, 9 Jul 2025 17:19:47 +0100 Subject: [PATCH 091/116] Fix loading files --- .gitignore | 1 + src/analyse/Pp.ml | 10 ---------- src/analyse/Utils.ml | 10 ++++++++++ src/run/funcRD.ml | 13 ++++++------- src/run/relProg.ml | 13 ++++++------- 5 files changed, 23 insertions(+), 24 deletions(-) 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/src/analyse/Pp.ml b/src/analyse/Pp.ml index cc57b5f6..354fb260 100644 --- a/src/analyse/Pp.ml +++ b/src/analyse/Pp.ml @@ -676,16 +676,6 @@ let chunks_of_ranged_cu m test an filename_stem ((low, high), cu) = (title, instructions_chunk :: chunks0) let wrap_body m (chunk_name, chunk_title, chunk_body) = - let read_html name = - let rec inter_p = function - | [] -> Error "not found" - | dir::dirs -> - let filename = Filename.concat dir name in - if Sys.file_exists filename - then read_file_lines filename - else inter_p dirs - in inter_p (Htmlpaths.Sites.html) - in match m with | Ascii -> ( if chunk_name = "instructions" then diff --git a/src/analyse/Utils.ml b/src/analyse/Utils.ml index 5d79072b..b249f58d 100644 --- a/src/analyse/Utils.ml +++ b/src/analyse/Utils.ml @@ -119,3 +119,13 @@ let sys_command s = else let exit_code = Sys.command s in if exit_code <> 0 then fatal "sys_command %s failed with exit code %d" s exit_code else () + +let read_html name = + let rec inter_p = function + | [] -> Error "not found" + | dir::dirs -> + let filename = Filename.concat dir name in + if Sys.file_exists filename + then read_file_lines filename + else inter_p dirs + in inter_p (Htmlpaths.Sites.html) \ No newline at end of file diff --git a/src/run/funcRD.ml b/src/run/funcRD.ml index 052b50b9..b2696435 100644 --- a/src/run/funcRD.ml +++ b/src/run/funcRD.ml @@ -56,10 +56,9 @@ open Logs.Logger (struct end) let run_func_rd elfname name objdump_d branchtables breakpoints = - match Analyse.Utils.read_file_lines "src/analyse/html-preamble-insts.html" with - | Error _ -> () - | Ok lines -> Array.iter (function s -> Printf.printf "%s\n" s) lines - ; + (match Analyse.Utils.read_html "html-preamble-insts.html" with + | Error _ -> err "Could not read html-preamble-insts.html" + | Ok lines -> Array.iter (function s -> Printf.printf "%s\n" s) lines); base "Running with rd %s in %s" name elfname; base "Loading %s" elfname; let dwarf = Dw.of_file elfname in @@ -148,9 +147,9 @@ let run_func_rd elfname name objdump_d branchtables breakpoints = base "At %t, %s:\n%t" Pp.(top Elf.Address.pp pc) msg Pp.(topi (State.pp_partial ~regs) st)); print_string (print_analyse_instruction pc))) runner.funcs; - match Analyse.Utils.read_file_lines "src/analyse/html-postamble.html" with - | Error _ -> () - | Ok lines -> Array.iter (function s -> Printf.printf "%s\n" s) lines + (match Analyse.Utils.read_html "html-postamble.html" with + | Error _ -> err "Could not read html-postamble.html" + | Ok lines -> Array.iter (function s -> Printf.printf "%s\n" s) lines) let elf = let doc = "ELF file from which to pull the code" in diff --git a/src/run/relProg.ml b/src/run/relProg.ml index f4a0f354..1e83bd0d 100644 --- a/src/run/relProg.ml +++ b/src/run/relProg.ml @@ -140,10 +140,9 @@ let printvars ~st ~(dwarf: Dw.t) pc = let run_prog elfname name objdump_d branchtables = - match Analyse.Utils.read_file_lines "src/analyse/html-preamble-insts.html" with - | Error _ -> () - | Ok lines -> Array.iter (function s -> Printf.printf "%s\n" s) lines - ; + (match Analyse.Utils.read_html "html-preamble-insts.html" with + | Error _ -> err "Could not read html-preamble-insts.html" + | Ok lines -> Array.iter (function s -> Printf.printf "%s\n" s) lines); base "Running with rd %s in %s" name elfname; base "Loading %s" elfname; let dwarf = Dw.of_file elfname in @@ -191,9 +190,9 @@ let run_prog elfname name objdump_d branchtables = List.iter iter succ in iter tree; - match Analyse.Utils.read_file_lines "src/analyse/html-postamble.html" with - | Error _ -> () - | Ok lines -> Array.iter (function s -> Printf.printf "%s\n" s) lines + (match Analyse.Utils.read_html "html-postamble.html" with + | Error _ -> err "Could not read html-postamble.html" + | Ok lines -> Array.iter (function s -> Printf.printf "%s\n" s) lines) let elf = From b5b27006f0e995df2690991fc122ae76b95563e2 Mon Sep 17 00:00:00 2001 From: Matej Urban Date: Wed, 9 Jul 2025 18:34:39 +0100 Subject: [PATCH 092/116] wip --- src/elf/address.ml | 2 ++ src/elf/file.ml | 66 +++++++++++++++++++++++++++++++++++++++++++- src/elf/symTable.ml | 19 +++++++++---- src/elf/symTable.mli | 13 +++++---- src/elf/symbol.ml | 28 ++++++++++++++++--- src/elf/symbol.mli | 26 ++++++++++++----- 6 files changed, 131 insertions(+), 23 deletions(-) diff --git a/src/elf/address.ml b/src/elf/address.ml index 33da9cd8..d22d48bb 100644 --- a/src/elf/address.ml +++ b/src/elf/address.ml @@ -3,6 +3,8 @@ type t = { offset: int; } +let absolute x = { section = ""; offset = x } + let pp addr = Pp.(!^(addr.section) ^^ !^"+" ^^ ptr addr.offset) let of_linksem (section, offset) = { section; offset = Z.to_int offset } diff --git a/src/elf/file.ml b/src/elf/file.ml index 095ad835..4bc5244f 100644 --- a/src/elf/file.ml +++ b/src/elf/file.ml @@ -137,7 +137,7 @@ let of_file (filename : string) = let entry = Z.to_int elf64_file.elf64_file_header.elf64_entry in let machine = machine_of_linksem elf64_file.elf64_file_header.elf64_machine in debug "Loading ELF symbols of %s" filename; - let symbols = SymTbl.of_linksem symbol_map in + let symbols = SymTbl.of_linksem_relocatable symbol_map in debug "Adding .rodata section of %s" filename; (* We add the .rodata section seperately from the symbols because - it can contain non-symbol information such as string literals and @@ -189,3 +189,67 @@ let of_file (filename : string) = in info "ELF file %s has been loaded" filename; { filename; symbols; entry; machine; linksem = elf_file; rodata; sections } + +let of_executable_file (filename : string) = + info "Loading ELF file %s" filename; + (* parse the ELF file using linksem *) + let ( (elf_file : Elf_file.elf_file), + (elf_epi : Sail_interface.executable_process_image), + (symbol_map : Elf_file.global_symbol_init_info) ) = + match Sail_interface.populate_and_obtain_global_symbol_init_info filename with + | Error.Fail s -> elferror "Linksem: populate_and_obtain_global_symbol_init_info: %s" s + | Error.Success x -> x + in + (* Check this is a 64 bits ELF file *) + begin + match elf_file with + | Elf_file.ELF_File_32 _ -> elferror "32 bits elf files unsupported" + | _ -> () + end; + let (segments, entry, machine) = + match elf_epi with + | ELF_Class_32 _ -> elferror "32 bits elf file class unsupported" + | ELF_Class_64 (s, e, m) -> (s, e, m) + in + + (* Extract all the segments *) + let segments = + List.filter_map + (fun (seg, prov) -> if prov = Elf_file.FromELF then Some seg else None) + segments + in + let entry = Z.to_int entry in + let machine = machine_of_linksem machine in + debug "Loading ELF segments of %s" filename; + let segments = List.map Segment.of_linksem segments in + debug "Loaded ELF segments %t" + @@ Pp.top (Pp.list Pp.hex) + @@ List.map (fun x -> x.Segment.addr) segments; + debug "Loading ELF symbols of %s" filename; + let symbols = SymTbl.of_linksem_executable segments symbol_map in + debug "Adding .rodata section of %s" filename; + (* We add the .rodata section seperately from the symbols because + - it can contain non-symbol information such as string literals and + constants used in branch-register target calculations + - the range of the section is guaranteed to overlap with any symbols + within it, and so not suitable to be stored in the [RngMap] *) + (* TODO multiple rodata sections *) + let rodata = + let (_, addr, data) = + Dwarf.extract_section_body_without_relocations elf_file ".rodata" false + (* `false' argument is for returning an empty byte-sequence if + section is not found, instead of throwing an exception *) + in + Segment. + { + data=(data, Relocations.IMap.empty); + addr = Sym.to_int addr; + size = BytesSeq.length data; + read = true; + write = false; + execute = false; + } + in + info "ELF file %s has been loaded" filename; + (* TODO should we include the section info here as well? *) + { filename; symbols; entry; machine; linksem = elf_file; rodata=SMap.singleton ".rodata" rodata; sections = [] } \ No newline at end of file diff --git a/src/elf/symTable.ml b/src/elf/symTable.ml index f0ac45a1..667d1052 100644 --- a/src/elf/symTable.ml +++ b/src/elf/symTable.ml @@ -52,8 +52,6 @@ open Symbol type sym = Symbol.t -type linksem_sym = Symbol.linksem_t - type sym_offset = sym * int module RMap = RngMap.Make (Symbol) @@ -97,7 +95,10 @@ module AddrMap = struct end -type linksem_t = LinksemRelocatable.global_symbol_init_info +type linksem_relocatable_t = LinksemRelocatable.global_symbol_init_info + +type linksem_executable_t = Elf_file.global_symbol_init_info + type t = { by_name : sym SMap.t; by_addr : AddrMap.t } @@ -150,12 +151,18 @@ let of_position_string t s : sym_offset = if s.[0] = '0' then raise Not_found (* no absolute addresses *) else sym_offset_of_string t s -let of_linksem linksem_map = - let add_linksem_sym_to_map (map : t) (lsym : linksem_sym) = - if is_interesting_linksem lsym then add map (Symbol.of_linksem lsym) else map +let of_linksem_generic get_typ of_linksem linksem_map = + let add_linksem_sym_to_map (map : t) lsym = + if is_interesting_linksem get_typ lsym then add map (of_linksem lsym) else map in List.fold_left add_linksem_sym_to_map empty linksem_map +let of_linksem_relocatable = + of_linksem_generic linksem_relocatable_typ of_linksem_relocatable + +let of_linksem_executable segments = + of_linksem_generic linksem_executable_typ (of_linksem_executable segments) + let pp_raw st = AddrMap.bindings st.by_addr |> List.map (Pair.map Address.pp pp_raw) |> Pp.mapping "syms" let iter t f = SMap.iter (fun _ value -> f value) t.by_name diff --git a/src/elf/symTable.mli b/src/elf/symTable.mli index 18e7de50..ebf1256b 100644 --- a/src/elf/symTable.mli +++ b/src/elf/symTable.mli @@ -52,12 +52,12 @@ type sym = Symbol.t -type linksem_sym = Symbol.linksem_t - (** The type of a symbol with offset *) type sym_offset = sym * int -type linksem_t = LinksemRelocatable.global_symbol_init_info +type linksem_relocatable_t = LinksemRelocatable.global_symbol_init_info + +type linksem_executable_t = Elf_file.global_symbol_init_info (** The type of a symbol table. *) type t @@ -107,10 +107,13 @@ val sym_offset_of_string : t -> string -> sym_offset *) val of_position_string : t -> string -> sym_offset -(** Extract the symbol from the linksem symbol representation. +(** Extract the symbol from the linksem symbol representation (relocatable file). *) +val of_linksem_relocatable : linksem_relocatable_t -> t + +(** Extract the symbol from the linksem symbol representation (executable file). Need the segments for filling the missing symbol data *) -val of_linksem : linksem_t -> t +val of_linksem_executable : Segment.t list -> linksem_executable_t -> t (** Pretty print the table as a raw ocaml value *) val pp_raw : t -> Pp.document diff --git a/src/elf/symbol.ml b/src/elf/symbol.ml index 263c184b..8e4024d2 100644 --- a/src/elf/symbol.ml +++ b/src/elf/symbol.ml @@ -68,7 +68,9 @@ type t = { data : data; } -type linksem_t = LinksemRelocatable.symbol +type linksem_relocatable_t = LinksemRelocatable.symbol + +type linksem_executable_t = string * (Z.t * Z.t * Z.t * BytesSeq.t option * Z.t) let push_name s t = { t with other_names = s :: t.other_names } @@ -85,7 +87,9 @@ let typ_of_linksem ltyp = | 4 -> FILE | _ -> UNKNOWN -let linksem_typ (_name, (typ, _size, _addr, _data, _), _) = typ +let linksem_relocatable_typ (_name, (typ, _size, _addr, _data, _), _) = typ + +let linksem_executable_typ (_name, (typ, _size, _addr, _data, _)) = typ (** [LoadingError(name,addr)] means that symbol [name] at [addr] could not be loaded *) exception LoadingError of string * int @@ -100,7 +104,7 @@ let _ = (* module SMap = Map.Make (String) let locs = SMap.empty |> SMap.add ".text" 0 |> SMap.add ".data" 1000000 |> SMap.add ".eh_frame" 2000000 *) -let of_linksem (name, (typ, size, addr, (data, rels), _), writable) = +let of_linksem_relocatable (name, (typ, size, addr, (data, rels), _), writable) = let typ = typ_of_linksem typ in let size = Z.to_int size in let addr = Address.of_linksem addr in @@ -108,9 +112,25 @@ let of_linksem (name, (typ, size, addr, (data, rels), _), writable) = (* let addr = SMap.find section locs + Z.to_int offset in *) { name; other_names = []; typ; size; addr; data; writable } +let of_linksem_executable segs (name, (typ, size, addr, data, _)) = + let typ = typ_of_linksem typ in + let size = Z.to_int size in + let addr = Z.to_int addr in + let segment = + Option.value_fail (Segment.get_containing segs addr) "No segment contains symbol %s" name + in + let writable = segment.write in + let data = + data + |> Option.value_fun ~default:(fun () -> + (* TODO use some wrapper for byte sequences with relocations *) + Segment.get_addr (fun (bs, _) -> BytesSeq.getbs ~len:size bs) segment addr) + in + { name; other_names = []; typ; size; addr=Address.absolute addr; data={data; relocations=Relocations.IMap.empty}; writable } + let is_interesting = function OBJECT | FUNC -> true | _ -> false -let is_interesting_linksem lsym = lsym |> linksem_typ |> typ_of_linksem |> is_interesting +let is_interesting_linksem get_typ lsym = lsym |> get_typ |> typ_of_linksem |> is_interesting let sub sym off len = { data = BytesSeq.sub sym.data.data off len; diff --git a/src/elf/symbol.mli b/src/elf/symbol.mli index 3bc5803f..7ed2b952 100644 --- a/src/elf/symbol.mli +++ b/src/elf/symbol.mli @@ -73,8 +73,11 @@ type t = { data : data; } -(** The type of an ELF symbol in linksem. See {!of_linksem}*) -type linksem_t = LinksemRelocatable.symbol +(** The type of an ELF symbol in linksem (relocatable file). See {!of_linksem_relocatable}*) +type linksem_relocatable_t = LinksemRelocatable.symbol + +(** The type of an ELF symbol in linksem (executable file). See {!of_linksem_executable}*) +type linksem_executable_t = string * (Z.t * Z.t * Z.t * BytesSeq.t option * Z.t) (** Add a name to the other names list *) val push_name : string -> t -> t @@ -88,24 +91,33 @@ val len : t -> int (** Convert the integer type into typ *) val typ_of_linksem : linksem_typ -> typ -(** Get the type from the linksem symbol type *) -val linksem_typ : linksem_t -> linksem_typ +(** Get the type from the linksem symbol type (relocatable file) *) +val linksem_relocatable_typ : linksem_relocatable_t -> linksem_typ + +(** Get the type from the linksem symbol type (executable file) *) +val linksem_executable_typ : linksem_executable_t -> linksem_typ (** [LoadingError(name,addr)] means that symbol [name] at [addr] could not be loaded.*) exception LoadingError of string * int -(** Convert a symbol from linksem to read-dwarf representation using the segment data +(** Convert a symbol from linksem to read-dwarf representation (relocatable file). + + May raise {!LoadingError} when the symbol has no data +*) +val of_linksem_relocatable : linksem_relocatable_t -> t + +(** Convert a symbol from linksem to read-dwarf representation using the segment data (executable file). May raise {!LoadingError} when the symbol has no data and the data cannot be found in the segments *) -val of_linksem : linksem_t -> t +val of_linksem_executable : Segment.t list -> linksem_executable_t -> t (** Tell if a symbol type is interesting for readDwarf purposes *) val is_interesting : typ -> bool (** Tell if a linksem symbol is interesting for readDwarf purposes *) -val is_interesting_linksem : linksem_t -> bool +val is_interesting_linksem : ('a -> linksem_typ) -> 'a -> bool (** Take the BytesSeq.t corresponding to the offset and length *) val sub : t -> int -> int -> data From ab8fb0234b2f9ba3708d08da86762a91c93ab81d Mon Sep 17 00:00:00 2001 From: Matej Urban Date: Thu, 10 Jul 2025 18:59:09 +0100 Subject: [PATCH 093/116] Parse both executable and relocatable files --- src/dw/addr.ml | 2 +- src/elf/file.ml | 42 ++++++++++++++++++++++++++---------------- 2 files changed, 27 insertions(+), 17 deletions(-) diff --git a/src/dw/addr.ml b/src/dw/addr.ml index 201906bf..ded896e3 100644 --- a/src/dw/addr.ml +++ b/src/dw/addr.ml @@ -2,5 +2,5 @@ include Elf.Address let of_sym : Sym.t -> t = function | Sym_ocaml.Num.Offset (section, offset) -> { section; offset = Z.to_int offset } -| _ -> Raise.fail "expected section+offset" +| Sym_ocaml.Num.Absolute z -> absolute (Z.to_int z) diff --git a/src/elf/file.ml b/src/elf/file.ml index 4bc5244f..bafd91d0 100644 --- a/src/elf/file.ml +++ b/src/elf/file.ml @@ -107,21 +107,7 @@ let _ = (** Throw an {!ElfError} *) let elferror fmt = Printf.ksprintf (fun s -> raise (ElfError s)) fmt -(** Parse an ELF file to create an {!Elf.File.t} using Linksem. - - May raise an {!ElfError} -*) -let of_file (filename : string) = - info "Loading ELF file %s" filename; - (* parse the ELF file using linksem *) - let bs = match Byte_sequence.acquire filename with - | Error.Fail s -> elferror "Linksem: Byte_sequence.acquire: %s" s - | Error.Success x -> x - in - let elf64_file = match Elf_file.read_elf64_file bs with - | Error.Fail s -> elferror "Linksem: read_elf64_file: %s" s - | Error.Success x -> x - in +let of_relocatable_file (filename : string) elf64_file = let symbol_map = match LinksemRelocatable.get_elf64_file_global_symbol_init elf64_file with | Error.Fail s -> elferror "LinksemRelocatable: get_elf64_file_global_symbol_init: %s" s | Error.Success x -> x @@ -252,4 +238,28 @@ let of_executable_file (filename : string) = in info "ELF file %s has been loaded" filename; (* TODO should we include the section info here as well? *) - { filename; symbols; entry; machine; linksem = elf_file; rodata=SMap.singleton ".rodata" rodata; sections = [] } \ No newline at end of file + { filename; symbols; entry; machine; linksem = elf_file; rodata=SMap.singleton ".rodata" rodata; sections = [] } + + +(** Parse an ELF file to create an {!Elf.File.t} using Linksem. + + May raise an {!ElfError} +*) +let of_file (filename : string) = + info "Loading ELF file %s" filename; + (* parse the ELF file using linksem *) + let bs = match Byte_sequence.acquire filename with + | Error.Fail s -> elferror "Linksem: Byte_sequence.acquire: %s" s + | Error.Success x -> x + in + let elf64_file = match Elf_file.read_elf64_file bs with + | Error.Fail s -> elferror "Linksem: read_elf64_file: %s" s + | Error.Success x -> x + in + if Elf_header.is_elf64_relocatable_file elf64_file.elf64_file_header then + of_relocatable_file filename elf64_file + else if Elf_header.is_elf64_executable_file elf64_file.elf64_file_header then + (* TODO currently this loads the file twice *) + of_executable_file filename + else + elferror "Linksem: of_file: not an ELF64 relocatable or executable file: %s" filename \ No newline at end of file From a69e2d0a7c7a0588f510b09917346cb095dffbb2 Mon Sep 17 00:00:00 2001 From: Matej Urban Date: Thu, 10 Jul 2025 19:47:57 +0100 Subject: [PATCH 094/116] Allow both absolute and section-relative addresses (wip) the executor still expects pc values to be section-relative --- src/ctype/ctype.ml | 1 + src/dw/addr.ml | 2 +- src/elf/address.ml | 13 ++++++++----- src/elf/linksemRelocatable.ml | 2 +- src/elf/symTable.ml | 24 ++++++++++++++---------- src/elf/symbol.ml | 2 +- src/run/func.ml | 2 +- src/run/relProg.ml | 2 +- src/run/testRelProg.ml | 4 ++-- src/state/base.ml | 29 ++++++++++++++++++----------- src/trace/typer.ml | 2 +- 11 files changed, 49 insertions(+), 34 deletions(-) diff --git a/src/ctype/ctype.ml b/src/ctype/ctype.ml index dbbe61f8..cb97ab46 100644 --- a/src/ctype/ctype.ml +++ b/src/ctype/ctype.ml @@ -137,6 +137,7 @@ and fragment = | 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 *) + (* TODO broken - maybe shouldn't have the section string *) | Global of string (** The Global fragment that contains all the fixed ELF section .text, .data, .rodata, ... *) diff --git a/src/dw/addr.ml b/src/dw/addr.ml index ded896e3..8b3e0b55 100644 --- a/src/dw/addr.ml +++ b/src/dw/addr.ml @@ -1,6 +1,6 @@ include Elf.Address let of_sym : Sym.t -> t = function -| Sym_ocaml.Num.Offset (section, offset) -> { section; offset = Z.to_int offset } +| Sym_ocaml.Num.Offset (section, offset) -> { section = Some section; offset = Z.to_int offset } | Sym_ocaml.Num.Absolute z -> absolute (Z.to_int z) diff --git a/src/elf/address.ml b/src/elf/address.ml index d22d48bb..17e7ce1d 100644 --- a/src/elf/address.ml +++ b/src/elf/address.ml @@ -1,13 +1,13 @@ type t = { - section : string; + section : string option; offset: int; } -let absolute x = { section = ""; offset = x } +let absolute x = { section = None; offset = x } -let pp addr = Pp.(!^(addr.section) ^^ !^"+" ^^ ptr addr.offset) +let pp addr = Pp.(optional (fun s -> !^s ^^ !^"+") addr.section ^^ ptr addr.offset) -let of_linksem (section, offset) = { section; offset = Z.to_int offset } +let of_linksem_relocatable (section, offset) = { section = Some section; offset = Z.to_int offset } let (+) addr offset = { section = addr.section; offset = addr.offset + offset } @@ -25,4 +25,7 @@ let (<=) = compare (<=) let (>=) = compare (>=) -let to_sym {section; offset} = Sym_ocaml.Num.Offset (section, Z.of_int offset) \ No newline at end of file +let to_sym {section; offset} = + match section with + | Some s -> Sym_ocaml.Num.Offset (s, Z.of_int offset) + | None -> Sym_ocaml.Num.Absolute (Z.of_int offset) \ No newline at end of file diff --git a/src/elf/linksemRelocatable.ml b/src/elf/linksemRelocatable.ml index 5f92e2e0..9a3d2aab 100644 --- a/src/elf/linksemRelocatable.ml +++ b/src/elf/linksemRelocatable.ml @@ -1,4 +1,4 @@ -(* TODO header *) +(* TODO move to linksem? *) module SMap = Map.Make (String) diff --git a/src/elf/symTable.ml b/src/elf/symTable.ml index 667d1052..095cee81 100644 --- a/src/elf/symTable.ml +++ b/src/elf/symTable.ml @@ -56,12 +56,16 @@ type sym_offset = sym * int module RMap = RngMap.Make (Symbol) module SMap = Map.Make (String) +module OSMap = Map.Make (struct + type t = string option + let compare = Option.compare String.compare +end) module AddrMap = struct - type t = RMap.t SMap.t + type t = RMap.t OSMap.t let add t (addr: Address.t) sym = - SMap.update addr.section (fun old -> + OSMap.update addr.section (fun old -> let old = match old with | None -> RMap.empty | Some x -> x @@ -70,24 +74,24 @@ module AddrMap = struct ) t let update f t (addr: Address.t) = - SMap.update addr.section (Option.map (fun x -> RMap.update f x addr.offset)) t + OSMap.update addr.section (Option.map (fun x -> RMap.update f x addr.offset)) t - let empty = SMap.empty + let empty = OSMap.empty let at t (addr: Address.t) = - SMap.find addr.section t |> Fun.flip RMap.at addr.offset + OSMap.find addr.section t |> Fun.flip RMap.at addr.offset let at_opt t (addr: Address.t) = - Option.bind (SMap.find_opt addr.section t) @@ Fun.flip RMap.at_opt addr.offset + Option.bind (OSMap.find_opt addr.section t) @@ Fun.flip RMap.at_opt addr.offset let at_off t (addr: Address.t) = - SMap.find addr.section t |> Fun.flip RMap.at_off addr.offset + OSMap.find addr.section t |> Fun.flip RMap.at_off addr.offset let at_off_opt t (addr: Address.t) = - Option.bind (SMap.find_opt addr.section t) @@ Fun.flip RMap.at_off_opt addr.offset + Option.bind (OSMap.find_opt addr.section t) @@ Fun.flip RMap.at_off_opt addr.offset let bindings t = - let sections = SMap.bindings t in + let sections = OSMap.bindings t in List.bind sections @@ fun (section, rmap) -> let inner_bindings = RMap.bindings rmap in List.map (fun (offset, sym) -> (Address.{section; offset}, sym)) inner_bindings @@ -148,7 +152,7 @@ let sym_offset_of_string t s : sym_offset = let of_position_string t s : sym_offset = let s = String.trim s in if s = "" then raise Not_found; - if s.[0] = '0' then raise Not_found (* no absolute addresses *) + if s.[0] = '0' then raise Not_found (* no absolute addresses *) (* TODO handle absolute addresses *) else sym_offset_of_string t s let of_linksem_generic get_typ of_linksem linksem_map = diff --git a/src/elf/symbol.ml b/src/elf/symbol.ml index 8e4024d2..0e66a393 100644 --- a/src/elf/symbol.ml +++ b/src/elf/symbol.ml @@ -107,7 +107,7 @@ let locs = SMap.empty |> SMap.add ".text" 0 |> SMap.add ".data" 1000000 |> SMap. let of_linksem_relocatable (name, (typ, size, addr, (data, rels), _), writable) = let typ = typ_of_linksem typ in let size = Z.to_int size in - let addr = Address.of_linksem addr in + let addr = Address.of_linksem_relocatable addr in let data = { data; relocations = Relocations.of_linksem rels } in (* let addr = SMap.find section locs + Z.to_int offset in *) { name; other_names = []; typ; size; addr; data; writable } diff --git a/src/run/func.ml b/src/run/func.ml index 08ac6c2b..20fc555f 100644 --- a/src/run/func.ml +++ b/src/run/func.ml @@ -80,7 +80,7 @@ let get_state_tree ~elf:elfname ~name ?(dump = false) ?(entry = false) ?len ?(br List.map (fun x -> if String.starts_with ~prefix:"UND" x then (*HACK for undefined symbol*) - Elf.Address.{ section=x; offset=0 } + Elf.Address.{ section=Some x; offset=0 } else x |> Elf.SymTable.of_position_string elf.symbols |> Elf.SymTable.to_addr_offset ) diff --git a/src/run/relProg.ml b/src/run/relProg.ml index 1e83bd0d..cb92df2d 100644 --- a/src/run/relProg.ml +++ b/src/run/relProg.ml @@ -85,7 +85,7 @@ let eval_loc ?frame_value sz st (loc: Dw.Loc.t) : State.Exp.t option = | Const x -> Some (match x with | Absolute x -> x |> BitVec.of_z ~size:(8*sz) |> Exp.Typed.bits - | Offset (s, o) -> State.Exp.of_address ~size:(8*sz) Elf.Address.{section=s; offset=Z.to_int o} + | Offset (s, o) -> State.Exp.of_address ~size:(8*sz) Elf.Address.{section=Some s; offset=Z.to_int o} ) | Dwarf _ops -> None diff --git a/src/run/testRelProg.ml b/src/run/testRelProg.ml index e9f15408..412cde29 100644 --- a/src/run/testRelProg.ml +++ b/src/run/testRelProg.ml @@ -36,12 +36,12 @@ let rec process_tree ~pc ~ret ~ext (node:Block_lib.label State.Tree.t) = in let ret_exp = match pc_addr with | Some pc_addr -> - if pc_addr = Elf.Address.{ section="UND.abort"; offset=0 } then + if pc_addr = Elf.Address.{ section=Some "UND.abort"; offset=0 } then Result.error { msg=Printf.sprintf "abort called from %t" (Pp.tos Elf.Address.pp st.last_pc); asserts=st.asserts; } - else if pc_addr <> Elf.Address.{ section="UND.exit"; offset=0 } then + else if pc_addr <> Elf.Address.{ section=Some "UND.exit"; offset=0 } then Result.error { msg=Printf.sprintf "finished at weird address %t" (Pp.tos Elf.Address.pp pc_addr); asserts=st.asserts; diff --git a/src/state/base.ml b/src/state/base.ml index 17659776..2a1317d1 100644 --- a/src/state/base.ml +++ b/src/state/base.ml @@ -189,7 +189,7 @@ module Exp = struct | _ -> Raise.fail "Expected symbolic Section base" in let offset = BitVec.to_int conc in - Elf.Address.{ section; offset } + Elf.Address.{ section = Some section; offset } let of_section ~(size : int) (section : string) = Typed.extract ~last:(size-1) ~first:0 @@ -198,9 +198,10 @@ module Exp = struct let of_address ~(size : int) (addr : Elf.Address.t) = Typed.( - of_section ~size addr.section - + - bits_int ~size addr.offset + let offset = bits_int ~size addr.offset in + match addr.section with + | Some section -> of_section ~size section + offset + | None -> offset ) end @@ -433,8 +434,8 @@ module Mem = struct prov let get_section_provenance mem section = - Hashtbl.find_opt mem.sections section - |> Option.value ~default:Ctype.Main + let maybe_prov = Option.bind section (Hashtbl.find_opt mem.sections) in + Option.value maybe_prov ~default:Ctype.Main end type t = { @@ -491,7 +492,7 @@ let make ?elf () = mem = Mem.empty (); elf; fenv = Fragment.Env.make (); - last_pc = Elf.Address.{ section = ".text"; offset = 0 }; (* TODO is this right? *) + last_pc = Elf.Address.{ section = Some ".text"; offset = 0 }; (* TODO is this right? *) } in next_id := id + 1; @@ -576,13 +577,16 @@ let eval_address (s : t) (addr: Exp.t) : Elf.Address.t option = ConcreteEval.Symbolic -> None in let offset = offset_exp |> Value.expect_bv |> BitVec.to_int in + if ConcreteEval.is_concrete addr then + some @@ Elf.Address.absolute offset + else let sections = Hashtbl.create 10 in Ast.Manip.exp_iter_var (function Var.Section s -> Hashtbl.add sections s () | _ -> ()) addr; let hyps = load_relocation_asserts s in let size = addr |> Typed.get_type |> Typed.expect_bv in sections |> Hashtbl.to_seq_keys |> Seq.find_map (fun section -> - let address = Elf.Address.{ section; offset } in + let address = Elf.Address.{ section = Some section; offset } in let expression = Exp.of_address ~size address in if Z3St.check_full ~hyps Typed.(expression = addr) = Some true then Some address @@ -612,7 +616,9 @@ let read_from_rodata (s : t) ~(addr : Exp.t) ~(size : Mem.Size.t) : Exp.t option with Not_found -> let int_addr = sym_addr.offset in let open Option in - let* rodata = Elf.File.SMap.find_opt sym_addr.section elf.rodata in + (* TODO handle multiple rodata sections/segments in executable files *) + let rodata_section = Option.value sym_addr.section ~default:".rodata" in + let* rodata = Elf.File.SMap.find_opt rodata_section elf.rodata in if rodata.addr <= int_addr && int_addr + size <= rodata.addr + rodata.size then let data, relocations = rodata.data in let data = BytesSeq.sub data (int_addr - rodata.addr) size in @@ -709,8 +715,9 @@ let set_pc ~(pc : Reg.t) (s : t) (pcval : int) = let ctyp = Ctype.of_frag (Ctype.Global ".text") ~offset:pcval ~constexpr:true in set_reg s pc @@ Tval.make ~ctyp exp +(* TODO name is misleading *) let set_pc_sym ~(pc : Reg.t) (s : t) (pcval : Elf.Address.t) = - let exp = Typed.(var ~typ:(Ty_BitVec 64) (Var.Section pcval.section) + bits_int ~size:64 pcval.offset) in + let exp = Exp.of_address ~size:64 pcval in let ctyp = Ctype.of_frag (Ctype.Global ".text") ~offset:pcval.offset ~constexpr:true in set_reg s pc @@ Tval.make ~ctyp exp @@ -805,7 +812,7 @@ let init_sections_symbolic ~sp ~addr_size state = push_section_constraints ~sp ~addr_size state elf.sections; Elf.SymTable.iter elf.symbols @@ fun sym -> if sym.typ = Elf.Symbol.OBJECT then - Hashtbl.replace state.mem.sections sym.addr.section Main + Option.iter (fun s -> Hashtbl.replace state.mem.sections s Main) sym.addr.section ) in lock state; state diff --git a/src/trace/typer.ml b/src/trace/typer.ml index 2dd8e1a2..d8b8c664 100644 --- a/src/trace/typer.ml +++ b/src/trace/typer.ml @@ -300,7 +300,7 @@ let fragment_at ~(dwarf : Dw.t) ~fenv ~size (frag : Ctype.fragment) at : Ctype.t let* (typ, off) = Fragment.at_off_opt frag at in Ctype.type_at ~env ~size typ off | Global s -> ( - match Elf.SymTable.of_addr_with_offset_opt dwarf.elf.symbols Elf.Address.{ section = s; offset = at } with + match Elf.SymTable.of_addr_with_offset_opt dwarf.elf.symbols Elf.Address.{ section = Some s; offset = at } with | Some (sym, offset) -> ( match Hashtbl.find_opt dwarf.vars sym.name with | Some v -> Ctype.type_at ~env ~size v.ctype offset From c10e465e239dab76132e753eab4dba9361a96c2e Mon Sep 17 00:00:00 2001 From: Matej Urban Date: Thu, 10 Jul 2025 20:11:55 +0100 Subject: [PATCH 095/116] Executor: accept absolute pc address --- src/run/block_lib.ml | 10 +++++----- src/run/runner.ml | 4 ++-- src/run/testRelProg.ml | 2 +- src/state/base.ml | 24 +++++++++--------------- src/state/base.mli | 6 ++---- 5 files changed, 19 insertions(+), 27 deletions(-) diff --git a/src/run/block_lib.ml b/src/run/block_lib.ml index 3b62215f..3da4b3e7 100644 --- a/src/run/block_lib.ml +++ b/src/run/block_lib.ml @@ -108,7 +108,7 @@ let run ?(every_instruction = false) ?relevant (b : t) (start : State.t) : label State.lock state end; let states = - let pc = State.Exp.expect_sym_address pc_exp in + let pc = State.Exp.expect_address pc_exp in if Option.fold ~none:true ~some:(Fun.flip Hashtbl.mem pc) relevant then ( info "Running pc %t" (Pp.top State.Exp.pp pc_exp); Runner.run ~prelock:ignore b.runner state @@ -124,11 +124,11 @@ let run ?(every_instruction = false) ?relevant (b : t) (start : State.t) : label | [state] when not every_instruction -> run_from state | [nstate] when every_instruction -> let rest = [run_from nstate] in - { state; data = NormalAt (State.Exp.expect_sym_address pc_exp); rest } + { state; data = NormalAt (State.Exp.expect_address pc_exp); rest } | states -> let rest = List.map run_from states in State.Tree. - { state; data = BranchAt (State.Exp.expect_sym_address pc_exp); rest } + { state; data = BranchAt (State.Exp.expect_address pc_exp); rest } ) else begin info "Reached dead code at %t" (Pp.top State.Exp.pp pc_exp); @@ -138,7 +138,7 @@ let run ?(every_instruction = false) ?relevant (b : t) (start : State.t) : label end in let state = State.copy start in - State.set_pc_sym ~pc:pcreg state b.start; + State.set_pc ~pc:pcreg state b.start; let rest = [run_from state] in State.Tree.{ state = start; data = Start; rest } @@ -161,7 +161,7 @@ let gen_endpred ?min ?max ?loop ?(brks = []) () : State.exp -> string option = in fun pc_exp -> ( try - Some (State.Exp.expect_sym_address pc_exp) + Some (State.Exp.expect_address pc_exp) with _ -> None ) |> Option.map (fun pc -> diff --git a/src/run/runner.ml b/src/run/runner.ml index 7ae5ff59..8f311eec 100644 --- a/src/run/runner.ml +++ b/src/run/runner.ml @@ -191,7 +191,7 @@ let execute_normal ?(prelock = ignore) ~pc runner (instr : Trace.Instr.t) state let skip runner state : State.t list = let pc_exp = State.get_reg_exp state runner.pc in try - let pc = State.Exp.expect_sym_address pc_exp in + let pc = State.Exp.expect_address pc_exp in match fetch runner pc with | Normal { traces = _; read = _; written = _; length; opcode = _; relocation = _ } |Special length @@ -223,7 +223,7 @@ let skip runner state : State.t list = let run ?prelock runner state : State.t list = let pc_exp = State.get_reg_exp state runner.pc in try - let pc = State.Exp.expect_sym_address pc_exp in + let pc = State.Exp.expect_address pc_exp in match fetch runner pc with | Normal instr -> execute_normal ?prelock ~pc runner instr state | Special _ -> diff --git a/src/run/testRelProg.ml b/src/run/testRelProg.ml index 412cde29..d520e9e7 100644 --- a/src/run/testRelProg.ml +++ b/src/run/testRelProg.ml @@ -30,7 +30,7 @@ let rec process_tree ~pc ~ret ~ext (node:Block_lib.label State.Tree.t) = | Block_lib.End _ -> let result = ( let pc_exp = State.get_reg_exp st pc in let pc_addr = try - Some (State.Exp.expect_sym_address pc_exp) + Some (State.Exp.expect_address pc_exp) with _ -> None in diff --git a/src/state/base.ml b/src/state/base.ml index 2a1317d1..fb4381fa 100644 --- a/src/state/base.ml +++ b/src/state/base.ml @@ -182,14 +182,15 @@ module Exp = struct let of_reg id reg = Var.of_reg id reg |> of_var - let expect_sym_address exp = + let expect_address exp = let sym, conc = Exp.Sums.split_concrete exp in let section = match sym with - | Some(Ast.Var (Var.Section s, _)) -> s - | _ -> Raise.fail "Expected symbolic Section base" + | Some(Ast.Var (Var.Section s, _)) -> Some s + | None -> None + | Some e -> Raise.fail "Address %t contains symbolic subexpression: %t" (Pp.tos pp exp) (Pp.tos pp e) in let offset = BitVec.to_int conc in - Elf.Address.{ section = Some section; offset } + Elf.Address.{ section = section; offset } let of_section ~(size : int) (section : string) = Typed.extract ~last:(size-1) ~first:0 @@ -709,14 +710,7 @@ let get_reg_exp s reg = get_reg s reg |> Tval.exp let update_reg_exp (s : t) (reg : Reg.t) (f : exp -> exp) = Reg.Map.get s.regs reg |> Tval.map_exp f |> Reg.Map.set s.regs reg -(* TODO *) -let set_pc ~(pc : Reg.t) (s : t) (pcval : int) = - let exp = Typed.bits_int ~size:64 pcval in - let ctyp = Ctype.of_frag (Ctype.Global ".text") ~offset:pcval ~constexpr:true in - set_reg s pc @@ Tval.make ~ctyp exp - -(* TODO name is misleading *) -let set_pc_sym ~(pc : Reg.t) (s : t) (pcval : Elf.Address.t) = +let set_pc ~(pc : Reg.t) (s : t) (pcval : Elf.Address.t) = let exp = Exp.of_address ~size:64 pcval in let ctyp = Ctype.of_frag (Ctype.Global ".text") ~offset:pcval.offset ~constexpr:true in set_reg s pc @@ Tval.make ~ctyp exp @@ -724,12 +718,12 @@ let set_pc_sym ~(pc : Reg.t) (s : t) (pcval : Elf.Address.t) = let bump_pc ~(pc : Reg.t) (s : t) (bump : int) = let pc_exp = get_reg_exp s pc in - let old_pc = Exp.expect_sym_address pc_exp in + let old_pc = Exp.expect_address pc_exp in let new_pc = Elf.Address.(old_pc + bump) in - set_pc_sym ~pc s new_pc + set_pc ~pc s new_pc let concretize_pc ~(pc : Reg.t) (s : t) = - pc |> get_reg_exp s |> eval_address s |> Option.iter (set_pc_sym ~pc s) + pc |> get_reg_exp s |> eval_address s |> Option.iter (set_pc ~pc s) let set_last_pc state pc = assert (not @@ is_locked state); diff --git a/src/state/base.mli b/src/state/base.mli index 73d6c370..696fe923 100644 --- a/src/state/base.mli +++ b/src/state/base.mli @@ -162,7 +162,7 @@ module Exp : sig (** Create an expression from an register and a state id *) val of_reg : id -> Reg.t -> t - val expect_sym_address : t -> Elf.Address.t + val expect_address : t -> Elf.Address.t val of_section : size:int -> string -> t @@ -498,9 +498,7 @@ val update_reg_exp : t -> Reg.t -> (exp -> exp) -> unit (** {1 Pc manipulation } *) (** Set the PC to a concrete value and keep its type appropriate *) -val set_pc : pc:Reg.t -> t -> int -> unit - -val set_pc_sym : pc:Reg.t -> t -> Elf.Address.t -> unit +val set_pc : pc:Reg.t -> t -> Elf.Address.t -> unit (** Bump a concrete PC by a concrete bump (generally the size of a non-branching instruction *) val bump_pc : pc:Reg.t -> t -> int -> unit From 1cafb80388193ea9b075e09c8f2115d89f778424 Mon Sep 17 00:00:00 2001 From: Matej Urban Date: Sun, 20 Jul 2025 13:42:19 +0200 Subject: [PATCH 096/116] remove old todos --- src/trace/context.ml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/trace/context.ml b/src/trace/context.ml index 59950dc2..3b484435 100644 --- a/src/trace/context.ml +++ b/src/trace/context.ml @@ -96,9 +96,8 @@ let expand_var ~(ctxt : t) (v : Base.Var.t) (a : Ast.no Ast.ty) : State.exp = Fun.tee (HashVector.add ctxt.nondets i) (State.Var.new_nondet sz) ) |> State.Exp.of_var - | Read (i, _) -> (HashVector.get ctxt.mem_reads i).exp (* TODO is the NonDet case correct *) - | Segment (name, _) -> SMap.find name ctxt.segments (*TODO put the actual value there*) - (* | Segment (name, sz) -> Exp.Typed.extract ~first:0 ~last:(sz-1) (State.Exp.of_var (State.Var.Section name)) TODO put the actual value there *) + | Read (i, _) -> (HashVector.get ctxt.mem_reads i).exp + | Segment (name, _) -> SMap.find name ctxt.segments (** Tell if typing should enabled with this context *) let typing_enabled ~(ctxt : t) = ctxt.dwarf <> None From 6a6fee937be75ea5d3c36bcf83c0a91d1713beb5 Mon Sep 17 00:00:00 2001 From: Matej Urban Date: Sun, 20 Jul 2025 17:30:14 +0200 Subject: [PATCH 097/116] Enable run-instr --- src/run/instr.ml | 33 +++++++++++++++++++++------------ 1 file changed, 21 insertions(+), 12 deletions(-) diff --git a/src/run/instr.ml b/src/run/instr.ml index 0098d35b..07c15d78 100644 --- a/src/run/instr.ml +++ b/src/run/instr.ml @@ -74,7 +74,7 @@ open Logs.Logger (struct let str = __MODULE__ end) -type traces = IslaTraces of Isla.rtrc list | Traces of Trace.t list +type traces = IslaTraces of Isla.rtrc list | Traces of Elf.Relocations.rel option * Trace.t list let instr = let doc = "Instruction to run(either directly on in sym+offset way. See --elf" in @@ -123,7 +123,7 @@ let elf = in Arg.(value & opt (some non_dir_file) None & info ["e"; "elf"] ~doc) -let get_instr arch instr elfopt : BytesSeq.t = +let get_instr arch instr elfopt : Elf.Symbol.data = let (elfname, symname) = match elfopt with | None -> @@ -140,7 +140,7 @@ let get_instr arch instr elfopt : BytesSeq.t = in debug "Got symbol:\n%t\n" (Pp.topi Elf.Symbol.pp_raw sym); let len = 4 (* TODO proper Instruction length system *) in - BytesSeq.sub sym.data.data off len (*TODO relocations*) + Elf.Symbol.sub sym off len let instr_term = Term.(CmdlinerHelper.func_options comopts get_instr $ arch $ instr $ elf) @@ -148,15 +148,24 @@ let simp_trace_term = Term.(const ( || ) $ simp_trace $ simp) let simp_state_term = Term.(const ( || ) $ simp_state $ simp) -let get_traces _instr _isla_run _dump_types : traces = - Raise.todo() - (* Isla.Cache.start @@ Arch.get_isla_config (); +let get_traces (instr: Elf.Symbol.data) isla_run dump_types : traces = + Isla.Cache.start @@ Arch.get_isla_config (); (* I call Init.init manually to print the register types *) Init.init () |> ignore; - let rtraces = Isla.Cache.get_traces (instr, None) in (* TODO relocs *) + let reloc = Elf.Relocations.IMap.find_opt 0 instr.relocations in + let reloc_typ = Option.map (fun (x: Elf.Relocations.rel) -> x.target) reloc in + let segments, rtraces = match Isla.Cache.get_traces (instr.data, reloc_typ) with + | Isla.Traces tr -> [], tr + | Isla.TracesWithSegments (Segments s, tr) -> s, tr + in List.iter (fun t -> Isla.Type.type_trc t |> ignore) rtraces; if dump_types then base "Register types:\n%t\n" (Pp.topi State.Reg.pp_index ()); - if isla_run then IslaTraces rtraces else Traces (List.map Trace.of_isla rtraces) *) + if isla_run then ( + if not @@ List.is_empty segments then + Raise.fail "Isla run doesn't support symbolic opcodes"; + IslaTraces rtraces + ) else + Traces (reloc, List.map (Trace.of_isla segments) rtraces) let pre_traces_term = Term.(const get_traces $ instr_term $ isla_run $ reg_types) @@ -164,9 +173,9 @@ let simp_traces simp_traces traces = if simp_traces then ( match traces with | IslaTraces _ -> traces - | Traces trcs -> + | Traces (reloc, trcs) -> Z3.ensure_started (); - Traces (List.map Trace.simplify trcs) + Traces (reloc, List.map Trace.simplify trcs) ) else traces @@ -176,7 +185,7 @@ let dump_traces dump_traces traces = match traces with | IslaTraces trcs -> List.iteri (fun i trc -> base "Trace %d:\n%t\n" i (Pp.topi Isla.pp_trc trc)) trcs - | Traces trcs -> + | Traces (_reloc, trcs) -> List.iteri (fun i trc -> base "Trace %d:\n%t\n" i (Pp.topi Trace.pp trc)) trcs end; traces @@ -192,7 +201,7 @@ let run_instr dump_init norun simp_state traces = match traces with | IslaTraces trcs -> List.map ((Isla.Run.trc [@ocaml.warning "-3"] (* deprecated *)) init_state) trcs - | Traces trcs -> List.map (Trace.Run.trace init_state) trcs + | Traces (relocation, trcs) -> List.map (Trace.Run.trace ?relocation init_state) trcs in let states = if simp_state then begin From 320f7642625e198a1f66377fede350f646e03496 Mon Sep 17 00:00:00 2001 From: Matej Urban Date: Sun, 20 Jul 2025 17:46:18 +0200 Subject: [PATCH 098/116] Allow parsing absolute addresses --- src/elf/symTable.ml | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/elf/symTable.ml b/src/elf/symTable.ml index 095cee81..93207a20 100644 --- a/src/elf/symTable.ml +++ b/src/elf/symTable.ml @@ -152,8 +152,11 @@ let sym_offset_of_string t s : sym_offset = let of_position_string t s : sym_offset = let s = String.trim s in if s = "" then raise Not_found; - if s.[0] = '0' then raise Not_found (* no absolute addresses *) (* TODO handle absolute addresses *) - else sym_offset_of_string t s + + if s.[0] = '0' then + of_addr_with_offset t (Address.absolute (int_of_string s)) + else + sym_offset_of_string t s let of_linksem_generic get_typ of_linksem linksem_map = let add_linksem_sym_to_map (map : t) lsym = From 34cbb38578619faf7ef4055a07c999b4bcd9d0d9 Mon Sep 17 00:00:00 2001 From: Matej Urban Date: Sun, 20 Jul 2025 18:11:25 +0200 Subject: [PATCH 099/116] Comment on bit sizes used by relocation expressions --- src/state/base.ml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/state/base.ml b/src/state/base.ml index fb4381fa..9a1443d1 100644 --- a/src/state/base.ml +++ b/src/state/base.ml @@ -245,9 +245,11 @@ module Relocation = struct } let rec exp_of_relocation_exp: Elf.Relocations.exp -> exp = + (* Note: the expressions are on 64bit integers, which should be enough to avoid overflow + (address space is usally smaller than 64 bits). Could consider using 128bit just to be safe (TODO). *) let f = exp_of_relocation_exp in function - | Section s -> Exp.of_var (Var.Section s) (* TODO size? *) - | Const x -> Typed.bits (BitVec.of_int x ~size:64) (* TODO size? *) + | Section s -> Exp.of_var (Var.Section s) + | Const x -> Typed.bits (BitVec.of_int x ~size:64) | BinOp (a, Add, b) -> Typed.(f a + f b) | BinOp (a, Sub, b) -> Typed.(f a - f b) | BinOp (a, And, b) -> Typed.manyop (AstGen.Ott.Bvmanyarith AstGen.Ott.Bvand) [f a; f b] From 9a414ea538f11ade17fcc03cecfd645e4d56802c Mon Sep 17 00:00:00 2001 From: Matej Urban Date: Mon, 21 Jul 2025 12:33:45 +0200 Subject: [PATCH 100/116] Use 0 as initial last_pc value (less ambiguous) --- src/state/base.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/state/base.ml b/src/state/base.ml index 9a1443d1..1f92f410 100644 --- a/src/state/base.ml +++ b/src/state/base.ml @@ -495,7 +495,7 @@ let make ?elf () = mem = Mem.empty (); elf; fenv = Fragment.Env.make (); - last_pc = Elf.Address.{ section = Some ".text"; offset = 0 }; (* TODO is this right? *) + last_pc = Elf.Address.absolute 0 } in next_id := id + 1; From e8166a6e79c7321774ed57de8d1f69b8c6bb332c Mon Sep 17 00:00:00 2001 From: Matej Urban Date: Mon, 21 Jul 2025 12:43:07 +0200 Subject: [PATCH 101/116] Resolve TODO --- src/trace/typer.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/trace/typer.ml b/src/trace/typer.ml index d8b8c664..d596b670 100644 --- a/src/trace/typer.ml +++ b/src/trace/typer.ml @@ -222,7 +222,7 @@ let rec expr ~ctxt (exp : Base.exp) : Ctype.t option = | Var (Register reg, _) -> State.get_reg ctxt.state reg |> State.Tval.ctyp | Var (Read (r, _), _) -> HashVector.get ctxt.mem_reads r |> State.Tval.ctyp | Var (NonDet _, _) -> None - | Var (Segment _, _) -> None (* TODO? *) + | Var (Segment _, _) -> None (* Bitsize is usually not whole bytes, so cannot be typed properly in this system *) | Bits (bv, _) -> let size = BitVec.size bv in if size mod 8 = 0 || size = Arch.address_size then From 97e188a7b7a4cf5616045e7ed8ff1044ae396862 Mon Sep 17 00:00:00 2001 From: Matej Urban Date: Mon, 21 Jul 2025 13:04:49 +0200 Subject: [PATCH 102/116] Reloc byte seq --- src/arch/aarch64/sig.ml | 2 +- src/arch/riscv64/sig.ml | 2 +- src/elf/relocBytesSeq.ml | 4 ++++ src/elf/symbol.ml | 9 +++------ src/elf/symbol.mli | 5 +---- src/run/runner.ml | 2 +- 6 files changed, 11 insertions(+), 13 deletions(-) create mode 100644 src/elf/relocBytesSeq.ml diff --git a/src/arch/aarch64/sig.ml b/src/arch/aarch64/sig.ml index c46bb8b8..0dadfb20 100644 --- a/src/arch/aarch64/sig.ml +++ b/src/arch/aarch64/sig.ml @@ -368,7 +368,7 @@ let split_into_instrs (data: Elf.Symbol.data) = let (_, rel, rest) = IMap.split pos data.relocations in if Option.is_some @@ IMap.find_first_opt (fun i -> i < pos + 4) rest then Raise.fail "Misaligned relocation"; - Elf.Symbol.{ + Elf.RelocBytesSeq.{ data = bytes; relocations = rel |> Option.map (IMap.singleton 0) |> Option.value ~default:IMap.empty; } diff --git a/src/arch/riscv64/sig.ml b/src/arch/riscv64/sig.ml index 4f661598..7b28e6cd 100644 --- a/src/arch/riscv64/sig.ml +++ b/src/arch/riscv64/sig.ml @@ -324,7 +324,7 @@ let split_into_instrs (data: Elf.Symbol.data) = let (_, rel, rest) = IMap.split pos data.relocations in if Option.is_some @@ IMap.find_first_opt (fun i -> i < pos + 4) rest then Raise.fail "Misaligned relocation"; - Elf.Symbol.{ + Elf.RelocBytesSeq.{ data = bytes; relocations = rel |> Option.map (IMap.singleton 0) |> Option.value ~default:IMap.empty; } diff --git a/src/elf/relocBytesSeq.ml b/src/elf/relocBytesSeq.ml new file mode 100644 index 00000000..2b7497ae --- /dev/null +++ b/src/elf/relocBytesSeq.ml @@ -0,0 +1,4 @@ +type t = { + data: BytesSeq.t; + relocations: Relocations.t +} diff --git a/src/elf/symbol.ml b/src/elf/symbol.ml index 0e66a393..99c30c9d 100644 --- a/src/elf/symbol.ml +++ b/src/elf/symbol.ml @@ -52,10 +52,7 @@ type typ = NOTYPE | OBJECT | FUNC | SECTION | FILE | UNKNOWN type linksem_typ = Z.t -type data = { - data: BytesSeq.t; - relocations: Relocations.t -} +type data = RelocBytesSeq.t type t = { name : string; @@ -108,7 +105,7 @@ let of_linksem_relocatable (name, (typ, size, addr, (data, rels), _), writable) let typ = typ_of_linksem typ in let size = Z.to_int size in let addr = Address.of_linksem_relocatable addr in - let data = { data; relocations = Relocations.of_linksem rels } in + let data : data = { data; relocations = Relocations.of_linksem rels } in (* let addr = SMap.find section locs + Z.to_int offset in *) { name; other_names = []; typ; size; addr; data; writable } @@ -132,7 +129,7 @@ let is_interesting = function OBJECT | FUNC -> true | _ -> false let is_interesting_linksem get_typ lsym = lsym |> get_typ |> typ_of_linksem |> is_interesting -let sub sym off len = { +let sub sym off len : data = { data = BytesSeq.sub sym.data.data off len; relocations = Relocations.sub sym.data.relocations off len; } diff --git a/src/elf/symbol.mli b/src/elf/symbol.mli index 7ed2b952..5557a9eb 100644 --- a/src/elf/symbol.mli +++ b/src/elf/symbol.mli @@ -55,10 +55,7 @@ type typ = NOTYPE | OBJECT | FUNC | SECTION | FILE | UNKNOWN type linksem_typ = Z.t -type data = { - data: BytesSeq.t; - relocations: Relocations.t -} +type data = RelocBytesSeq.t (** The ELF symbol. This type guarantee the data exists contrary to linksem symbols (it may be all zeros though) *) diff --git a/src/run/runner.ml b/src/run/runner.ml index 8f311eec..e8edcb8e 100644 --- a/src/run/runner.ml +++ b/src/run/runner.ml @@ -98,7 +98,7 @@ let load_sym runner (sym : Elf.Symbol.t) = let opcode_list = Arch.split_into_instrs sym.data in let addr = ref sym.addr in List.iter - (fun Elf.Symbol.{ data = code; relocations } -> + (fun Elf.RelocBytesSeq.{ data = code; relocations } -> let (addr, instr_len) = let result = !addr and len = BytesSeq.length code in addr := Elf.Address.(!addr + len); From b0efce6a70af42f033c9d05b09b485d7b3607914 Mon Sep 17 00:00:00 2001 From: Matej Urban Date: Thu, 31 Jul 2025 14:02:12 +0200 Subject: [PATCH 103/116] Use RelocByteSeq --- src/elf/relocBytesSeq.ml | 22 ++++++++++++++++++++++ src/elf/relocations.ml | 4 +++- src/elf/symbol.ml | 13 +++++-------- src/run/instr.ml | 6 +++--- src/run/runner.ml | 10 +++++----- 5 files changed, 38 insertions(+), 17 deletions(-) diff --git a/src/elf/relocBytesSeq.ml b/src/elf/relocBytesSeq.ml index 2b7497ae..94993e40 100644 --- a/src/elf/relocBytesSeq.ml +++ b/src/elf/relocBytesSeq.ml @@ -2,3 +2,25 @@ type t = { data: BytesSeq.t; relocations: Relocations.t } + +type linksem_t = BytesSeq.t * LinksemRelocatable.sym_data + +let sub seq off len = { + data = BytesSeq.sub seq.data off len; + relocations = Relocations.sub seq.relocations off len; +} + +let of_linksem (data, rel) = { data; relocations=Relocations.of_linksem rel } + +let pp seq = Pp.(pair (BytesSeq.ppby ~by:4) Relocations.pp (seq.data, seq.relocations)) + +let of_bytes_seq data = { data; relocations=Relocations.empty } + +(* Special case where only a single relocation is allowed *) +(* used by the executor *) +type opcode = BytesSeq.t * Relocations.rel + +let as_opcode seq = + (seq.data, Relocations.IMap.find_opt 0 seq.relocations) + +let length seq = BytesSeq.length seq.data diff --git a/src/elf/relocations.ml b/src/elf/relocations.ml index 04117864..6e07fa6b 100644 --- a/src/elf/relocations.ml +++ b/src/elf/relocations.ml @@ -89,4 +89,6 @@ let pp rels = if IMap.is_empty rels then Pp.empty else - Pp.(mapping "relocations" @@ List.map (fun (i, r) -> (hex i, pp_rel r)) (IMap.to_list rels)) \ No newline at end of file + Pp.(mapping "relocations" @@ List.map (fun (i, r) -> (hex i, pp_rel r)) (IMap.to_list rels)) + +let empty : t = IMap.empty diff --git a/src/elf/symbol.ml b/src/elf/symbol.ml index 99c30c9d..27a8b370 100644 --- a/src/elf/symbol.ml +++ b/src/elf/symbol.ml @@ -101,11 +101,11 @@ let _ = (* module SMap = Map.Make (String) let locs = SMap.empty |> SMap.add ".text" 0 |> SMap.add ".data" 1000000 |> SMap.add ".eh_frame" 2000000 *) -let of_linksem_relocatable (name, (typ, size, addr, (data, rels), _), writable) = +let of_linksem_relocatable (name, (typ, size, addr, data, _), writable) = let typ = typ_of_linksem typ in let size = Z.to_int size in let addr = Address.of_linksem_relocatable addr in - let data : data = { data; relocations = Relocations.of_linksem rels } in + let data = RelocBytesSeq.of_linksem data in (* let addr = SMap.find section locs + Z.to_int offset in *) { name; other_names = []; typ; size; addr; data; writable } @@ -123,16 +123,13 @@ let of_linksem_executable segs (name, (typ, size, addr, data, _)) = (* TODO use some wrapper for byte sequences with relocations *) Segment.get_addr (fun (bs, _) -> BytesSeq.getbs ~len:size bs) segment addr) in - { name; other_names = []; typ; size; addr=Address.absolute addr; data={data; relocations=Relocations.IMap.empty}; writable } + { name; other_names = []; typ; size; addr=Address.absolute addr; data=RelocBytesSeq.of_bytes_seq data; writable } let is_interesting = function OBJECT | FUNC -> true | _ -> false let is_interesting_linksem get_typ lsym = lsym |> get_typ |> typ_of_linksem |> is_interesting -let sub sym off len : data = { - data = BytesSeq.sub sym.data.data off len; - relocations = Relocations.sub sym.data.relocations off len; -} +let sub sym off len = RelocBytesSeq.sub sym.data off len let compare s1 s2 = compare s1.addr s2.addr @@ -159,5 +156,5 @@ let pp_raw sym = (* ("addr", ptr sym.addr); *) ("size", ptr sym.size); ("writable", bool sym.writable); - ("data", pair (BytesSeq.ppby ~by:4) Relocations.pp (sym.data.data, sym.data.relocations)); + ("data", RelocBytesSeq.pp sym.data); ]) diff --git a/src/run/instr.ml b/src/run/instr.ml index 07c15d78..9b0f8623 100644 --- a/src/run/instr.ml +++ b/src/run/instr.ml @@ -148,13 +148,13 @@ let simp_trace_term = Term.(const ( || ) $ simp_trace $ simp) let simp_state_term = Term.(const ( || ) $ simp_state $ simp) -let get_traces (instr: Elf.Symbol.data) isla_run dump_types : traces = +let get_traces (instr: Elf.RelocBytesSeq.t) isla_run dump_types : traces = Isla.Cache.start @@ Arch.get_isla_config (); (* I call Init.init manually to print the register types *) Init.init () |> ignore; - let reloc = Elf.Relocations.IMap.find_opt 0 instr.relocations in + let (data, reloc) = Elf.RelocBytesSeq.as_opcode instr in let reloc_typ = Option.map (fun (x: Elf.Relocations.rel) -> x.target) reloc in - let segments, rtraces = match Isla.Cache.get_traces (instr.data, reloc_typ) with + let segments, rtraces = match Isla.Cache.get_traces (data, reloc_typ) with | Isla.Traces tr -> [], tr | Isla.TracesWithSegments (Segments s, tr) -> s, tr in diff --git a/src/run/runner.ml b/src/run/runner.ml index e8edcb8e..3c389d83 100644 --- a/src/run/runner.ml +++ b/src/run/runner.ml @@ -98,16 +98,16 @@ let load_sym runner (sym : Elf.Symbol.t) = let opcode_list = Arch.split_into_instrs sym.data in let addr = ref sym.addr in List.iter - (fun Elf.RelocBytesSeq.{ data = code; relocations } -> + (fun code -> let (addr, instr_len) = - let result = !addr and len = BytesSeq.length code in + let result = !addr and len = Elf.RelocBytesSeq.length code in addr := Elf.Address.(!addr + len); (result, len) in - debug "Relocation at address %t: %t" (Pp.top Elf.Address.pp addr) (Pp.top Elf.Relocations.pp relocations); + debug "Relocation at address %t: %t" (Pp.top Elf.Address.pp addr) (Pp.top Elf.Relocations.pp code.relocations); try - let reloc = Elf.Relocations.IMap.find_opt 0 relocations in - let instr = Trace.Cache.get_instr (code, reloc) in + let opc = Elf.RelocBytesSeq.as_opcode code in + let instr = Trace.Cache.get_instr opc in if instr.traces = [] then begin debug "Instruction at %t in %s is loaded as special" (Pp.top Elf.Address.pp addr) sym.name; Hashtbl.add runner.instrs addr (Special instr_len) From da3095e8019cf2e626b2b66d769873f90ef603a7 Mon Sep 17 00:00:00 2001 From: Matej Urban Date: Thu, 31 Jul 2025 14:21:31 +0200 Subject: [PATCH 104/116] Use RelocBytesSeq in Elf.Segment --- src/elf/file.ml | 4 ++-- src/elf/relocBytesSeq.ml | 6 ++++++ src/elf/segment.ml | 4 ++-- src/elf/symbol.ml | 2 +- src/state/base.ml | 6 ++---- 5 files changed, 13 insertions(+), 9 deletions(-) diff --git a/src/elf/file.ml b/src/elf/file.ml index bafd91d0..8e05a3ae 100644 --- a/src/elf/file.ml +++ b/src/elf/file.ml @@ -163,7 +163,7 @@ let of_relocatable_file (filename : string) elf64_file = sname, Segment. { - data = (data, relocations); + data = {data; relocations}; addr = 0; (* Meaningless for relocatable files *) size = BytesSeq.length data; read = true; @@ -228,7 +228,7 @@ let of_executable_file (filename : string) = in Segment. { - data=(data, Relocations.IMap.empty); + data=RelocBytesSeq.of_bytes_seq data; addr = Sym.to_int addr; size = BytesSeq.length data; read = true; diff --git a/src/elf/relocBytesSeq.ml b/src/elf/relocBytesSeq.ml index 94993e40..28e693eb 100644 --- a/src/elf/relocBytesSeq.ml +++ b/src/elf/relocBytesSeq.ml @@ -24,3 +24,9 @@ let as_opcode seq = (seq.data, Relocations.IMap.find_opt 0 seq.relocations) let length seq = BytesSeq.length seq.data + +let expect_bs_no_relocations {data; relocations} = + if Relocations.IMap.is_empty relocations then + data + else + failwith "Byte sequence has relocations" diff --git a/src/elf/segment.ml b/src/elf/segment.ml index 51b82009..fc138e99 100644 --- a/src/elf/segment.ml +++ b/src/elf/segment.ml @@ -51,7 +51,7 @@ (** The type of a segment *) type t = { - data : BytesSeq.t * Relocations.t; + data : RelocBytesSeq.t; addr : int; (** The actual start address of the BytesSeq *) size : int; (** redundant with {!Utils.BytesSeq.length} data *) read : bool; @@ -66,7 +66,7 @@ let of_linksem (lseg : Elf_interpreted_segment.elf64_interpreted_segment) : t = BytesSeq.blit lseg.elf64_segment_body 0 bytes 0 (Z.to_int lseg.elf64_segment_size); let (read, write, execute) = lseg.elf64_segment_flags in { - data = BytesSeq.of_bytes bytes, Relocations.IMap.empty; + data = RelocBytesSeq.of_bytes_seq (BytesSeq.of_bytes bytes); addr = Z.to_int lseg.elf64_segment_base; size; read; diff --git a/src/elf/symbol.ml b/src/elf/symbol.ml index 27a8b370..593a9744 100644 --- a/src/elf/symbol.ml +++ b/src/elf/symbol.ml @@ -121,7 +121,7 @@ let of_linksem_executable segs (name, (typ, size, addr, data, _)) = data |> Option.value_fun ~default:(fun () -> (* TODO use some wrapper for byte sequences with relocations *) - Segment.get_addr (fun (bs, _) -> BytesSeq.getbs ~len:size bs) segment addr) + Segment.get_addr Fun.(RelocBytesSeq.expect_bs_no_relocations %> BytesSeq.getbs ~len:size) segment addr) in { name; other_names = []; typ; size; addr=Address.absolute addr; data=RelocBytesSeq.of_bytes_seq data; writable } diff --git a/src/state/base.ml b/src/state/base.ml index 1f92f410..b3ed1eb5 100644 --- a/src/state/base.ml +++ b/src/state/base.ml @@ -623,10 +623,8 @@ let read_from_rodata (s : t) ~(addr : Exp.t) ~(size : Mem.Size.t) : Exp.t option let rodata_section = Option.value sym_addr.section ~default:".rodata" in let* rodata = Elf.File.SMap.find_opt rodata_section elf.rodata in if rodata.addr <= int_addr && int_addr + size <= rodata.addr + rodata.size then - let data, relocations = rodata.data in - let data = BytesSeq.sub data (int_addr - rodata.addr) size in - let relocations = Elf.Relocations.sub relocations (int_addr - rodata.addr) size in - let value, asserts = Relocation.exp_of_data {data; relocations} in + let data = Elf.RelocBytesSeq.sub rodata.data (int_addr - rodata.addr) size in + let value, asserts = Relocation.exp_of_data data in if not @@ List.is_empty asserts then warn "Relocaiton assserts in .rodata ignored: %t" Pp.(top (list Exp.pp) asserts); From 935547dba71a94ce898c6f6b7000d02427045c2f Mon Sep 17 00:00:00 2001 From: Matej Urban Date: Thu, 31 Jul 2025 17:31:11 +0200 Subject: [PATCH 105/116] Enable run-bb --- src/isla/base.ml | 5 +++++ src/run/BB.ml | 4 ++-- src/run/bb_lib.ml | 23 +++++++++++++---------- src/run/instr.ml | 5 +---- src/trace/cache.ml | 5 +---- 5 files changed, 22 insertions(+), 20 deletions(-) diff --git a/src/isla/base.ml b/src/isla/base.ml index 94f4788b..5c2f190a 100644 --- a/src/isla/base.ml +++ b/src/isla/base.ml @@ -211,6 +211,11 @@ let parse_segments_channel ?filename (c : in_channel) : instruction_segments = | exn -> assert_failure (Printf.sprintf "Thrown: %s" (Printexc.to_string exn)) *) + +let trcs_to_list = function +| Traces trcs -> [], trcs +| TracesWithSegments (Segments s, trcs) -> s, trcs + (*****************************************************************************) (*****************************************************************************) (*****************************************************************************) diff --git a/src/run/BB.ml b/src/run/BB.ml index cdddee56..223148e7 100644 --- a/src/run/BB.ml +++ b/src/run/BB.ml @@ -93,7 +93,7 @@ let len = in Arg.(value & opt (some int) None & info ["l"; "len"] ~doc) -let get_code elfname symname len : BytesSeq.t = +let get_code elfname symname len : Elf.RelocBytesSeq.t = let elf = Elf.File.of_file elfname in Arch.load_elf_arch elf; let (sym, off) = @@ -101,7 +101,7 @@ let get_code elfname symname len : BytesSeq.t = with Not_found -> fail "The symbol %s cannot found in %s" symname elfname in let len = match len with Some i -> i | None -> sym.size - off in - (Elf.Symbol.sub sym off len).data (*TODO relocations*) + Elf.Symbol.sub sym off len let code_term = Term.(CmdlinerHelper.func_options comopts get_code $ elf $ sym $ len) diff --git a/src/run/bb_lib.ml b/src/run/bb_lib.ml index 6790fe1a..58a711d0 100644 --- a/src/run/bb_lib.ml +++ b/src/run/bb_lib.ml @@ -64,27 +64,30 @@ type t = { main : trc array } Also does the typing of traces for register discovery. TODO Support variable length instructions *) -let from_binary (_code : BytesSeq.t) : t = - Raise.todo() - (* let num = BytesSeq.length code / 4 in +let from_binary (code : Elf.RelocBytesSeq.t) : t = (* TODO fix fixed size instructions *) - if BytesSeq.length code != num * 4 then + (* TODO maybe this should be checked in Arch.split_into_instrs *) + if Elf.RelocBytesSeq.length code mod 4 <> 0 then failwith "BB.from_binary: The specified range cuts an instruction"; - let process (code : BytesSeq.t) : trc = - let get_normal : Isla.rtrc list -> trc = function + let process (code : Elf.RelocBytesSeq.t) : trc = + let get_normal rtrcs = + let segs, rtrcs = Isla.trcs_to_list rtrcs in + match rtrcs with | [] -> failwith "BB.from_binary: no normal path" | [trc] -> Isla.Type.type_trc trc |> ignore; - Trace.of_isla trc + Trace.of_isla segs trc | _ -> failwith "BB.from_binary: Multiple path instruction.\n\ If this is not a branching instruction, try `run-block --linear'." in - (code, None) |> Isla.Cache.get_traces |> get_normal (*TODO relocs *) + let (raw, reloc) = Elf.RelocBytesSeq.as_opcode code in + let reloc_typ = Option.map (fun (r : Elf.Relocations.rel) -> r.target) reloc in + (raw, reloc_typ) |> Isla.Cache.get_traces |> get_normal in - let main = code |> BytesSeq.to_listbs ~len:4 |> List.map process |> Array.of_list in - { main } *) + let main = code |> Arch.split_into_instrs |> List.map process |> Array.of_list in + { main } (* Sequence of the second test: mpool.c:116.6 (mpool_fini) 40012240: 37000049 tbnz diff --git a/src/run/instr.ml b/src/run/instr.ml index 9b0f8623..16b1e745 100644 --- a/src/run/instr.ml +++ b/src/run/instr.ml @@ -154,10 +154,7 @@ let get_traces (instr: Elf.RelocBytesSeq.t) isla_run dump_types : traces = Init.init () |> ignore; let (data, reloc) = Elf.RelocBytesSeq.as_opcode instr in let reloc_typ = Option.map (fun (x: Elf.Relocations.rel) -> x.target) reloc in - let segments, rtraces = match Isla.Cache.get_traces (data, reloc_typ) with - | Isla.Traces tr -> [], tr - | Isla.TracesWithSegments (Segments s, tr) -> s, tr - in + let segments, rtraces = (data, reloc_typ) |> Isla.Cache.get_traces |> Isla.trcs_to_list in List.iter (fun t -> Isla.Type.type_trc t |> ignore) rtraces; if dump_types then base "Register types:\n%t\n" (Pp.topi State.Reg.pp_index ()); if isla_run then ( diff --git a/src/trace/cache.ml b/src/trace/cache.ml index 7cb5625d..53ad01ef 100644 --- a/src/trace/cache.ml +++ b/src/trace/cache.ml @@ -160,10 +160,7 @@ let get_traces (opcode : Isla.Server.opcode) : Base.t list = match TC.get_opt cache (Some opcode) with | Some trcs -> trcs | None -> - let segments, isla_traces = match Isla.Cache.get_traces opcode with - | Traces t -> [], t - | TracesWithSegments (Segments s, t) -> s, t - in + let segments, isla_traces = opcode |> Isla.Cache.get_traces |> Isla.trcs_to_list in let traces = List.map (tee (Isla.Type.type_trc %> ignore) %> Base.of_isla segments) isla_traces in let straces = List.map Base.simplify traces in TC.add cache (Some opcode) straces; From ed23d3d312de35f1ae8e5484e1102597ecc5c1fb Mon Sep 17 00:00:00 2001 From: Matej Urban Date: Thu, 31 Jul 2025 17:36:42 +0200 Subject: [PATCH 106/116] Remove leftover debugging stuff --- src/elf/symbol.ml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/elf/symbol.ml b/src/elf/symbol.ml index 593a9744..a65df633 100644 --- a/src/elf/symbol.ml +++ b/src/elf/symbol.ml @@ -97,10 +97,6 @@ let _ = Some (Printf.sprintf "Symbol %s at 0x%x could not be loaded" name addr) | _ -> None) -(* for debugging TODO remove *) -(* module SMap = Map.Make (String) -let locs = SMap.empty |> SMap.add ".text" 0 |> SMap.add ".data" 1000000 |> SMap.add ".eh_frame" 2000000 *) - let of_linksem_relocatable (name, (typ, size, addr, data, _), writable) = let typ = typ_of_linksem typ in let size = Z.to_int size in From 92ef1a611d06a7780d61476d10ae248c0b52ba02 Mon Sep 17 00:00:00 2001 From: Matej Urban Date: Thu, 31 Jul 2025 17:41:15 +0200 Subject: [PATCH 107/116] cleanup todo --- src/elf/symbol.ml | 1 - 1 file changed, 1 deletion(-) diff --git a/src/elf/symbol.ml b/src/elf/symbol.ml index a65df633..ad25dbc0 100644 --- a/src/elf/symbol.ml +++ b/src/elf/symbol.ml @@ -116,7 +116,6 @@ let of_linksem_executable segs (name, (typ, size, addr, data, _)) = let data = data |> Option.value_fun ~default:(fun () -> - (* TODO use some wrapper for byte sequences with relocations *) Segment.get_addr Fun.(RelocBytesSeq.expect_bs_no_relocations %> BytesSeq.getbs ~len:size) segment addr) in { name; other_names = []; typ; size; addr=Address.absolute addr; data=RelocBytesSeq.of_bytes_seq data; writable } From db289a0782c51589b34bba22cff2208c23864795 Mon Sep 17 00:00:00 2001 From: Matej Urban Date: Thu, 31 Jul 2025 17:59:52 +0200 Subject: [PATCH 108/116] Rename sections -> relocatable_sections and clarify its usage It contains a list of sections whose addresses need to be represented symbolically --- src/elf/file.ml | 9 +++++---- src/state/base.ml | 7 ++++--- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/src/elf/file.ml b/src/elf/file.ml index 8e05a3ae..346cba00 100644 --- a/src/elf/file.ml +++ b/src/elf/file.ml @@ -93,7 +93,9 @@ type t = { 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; + relocatable_sections : section list; + (** Info about sections' size, alignment, ... constraints. Only used for symbolic execution + of relocatable files. *) } (** Error on Elf parsing *) @@ -174,7 +176,7 @@ let of_relocatable_file (filename : string) elf64_file = ) elf64_file.elf64_file_interpreted_sections in info "ELF file %s has been loaded" filename; - { filename; symbols; entry; machine; linksem = elf_file; rodata; sections } + { filename; symbols; entry; machine; linksem = elf_file; rodata; relocatable_sections = sections } let of_executable_file (filename : string) = info "Loading ELF file %s" filename; @@ -237,8 +239,7 @@ let of_executable_file (filename : string) = } in info "ELF file %s has been loaded" filename; - (* TODO should we include the section info here as well? *) - { filename; symbols; entry; machine; linksem = elf_file; rodata=SMap.singleton ".rodata" rodata; sections = [] } + { filename; symbols; entry; machine; linksem = elf_file; rodata=SMap.singleton ".rodata" rodata; relocatable_sections = [] } (** Parse an ELF file to create an {!Elf.File.t} using Linksem. diff --git a/src/state/base.ml b/src/state/base.ml index b3ed1eb5..8b608249 100644 --- a/src/state/base.ml +++ b/src/state/base.ml @@ -780,8 +780,9 @@ let init_sections ~sp ~addr_size state = let _ = Option.( let+ elf = state.elf in state.mem.allow_main <- false; - push_section_constraints ~sp ~addr_size state elf.sections; - List.iter (fun (x:Elf.File.section) -> Mem.create_section_frag ~addr_size state.mem x.name |> ignore) elf.sections; + push_section_constraints ~sp ~addr_size state elf.relocatable_sections; + List.iter (fun (x:Elf.File.section) + -> Mem.create_section_frag ~addr_size state.mem x.name |> ignore) elf.relocatable_sections; Elf.SymTable.iter elf.symbols @@ fun sym -> let len = List.find (fun x -> sym.size mod x = 0) [16;8;4;2;1] in if sym.typ = Elf.Symbol.OBJECT then @@ -803,7 +804,7 @@ let init_sections_symbolic ~sp ~addr_size state = let state = copy_if_locked state in let _ = Option.( let+ elf = state.elf in - push_section_constraints ~sp ~addr_size state elf.sections; + push_section_constraints ~sp ~addr_size state elf.relocatable_sections; Elf.SymTable.iter elf.symbols @@ fun sym -> if sym.typ = Elf.Symbol.OBJECT then Option.iter (fun s -> Hashtbl.replace state.mem.sections s Main) sym.addr.section From a84ce4cfebe780fcb68be4e1cbb1a08acd5e8706 Mon Sep 17 00:00:00 2001 From: Matej Urban Date: Thu, 31 Jul 2025 18:23:07 +0200 Subject: [PATCH 109/116] Reintroduce global fragment for all ELF sections --- src/arch/aarch64/sig.ml | 2 +- src/arch/riscv64/sig.ml | 2 +- src/ctype/ctype.ml | 8 ++++---- src/relsim/relsim.ml | 5 ++++- src/state/base.ml | 2 +- src/trace/typer.ml | 2 +- 6 files changed, 12 insertions(+), 9 deletions(-) diff --git a/src/arch/aarch64/sig.ml b/src/arch/aarch64/sig.ml index 0dadfb20..89aaedb8 100644 --- a/src/arch/aarch64/sig.ml +++ b/src/arch/aarch64/sig.ml @@ -331,7 +331,7 @@ let get_abi api = State.set_reg_type state sp (Ctype.of_frag ~provenance:stack_provenance @@ DynFragment stack_frag_id); State.set_reg state r30 - (State.Tval.of_var ~ctyp:(Ctype.of_frag_somewhere (Ctype.Global ".text")) RetAddr); (* TODO doesn't have to be .text *) + (State.Tval.of_var ~ctyp:(Ctype.of_frag_somewhere (Ctype.Global None)) RetAddr); let sp_exp = State.Exp.of_reg state.id sp in (* Assert that Sp is 16 bytes aligned *) State.push_assert state Exp.Typed.(extract ~last:3 ~first:0 sp_exp = bits_int ~size:4 0); diff --git a/src/arch/riscv64/sig.ml b/src/arch/riscv64/sig.ml index 7b28e6cd..5dc8d854 100644 --- a/src/arch/riscv64/sig.ml +++ b/src/arch/riscv64/sig.ml @@ -289,7 +289,7 @@ let get_abi api = State.set_reg_type state sp (Ctype.of_frag ~provenance:stack_provenance @@ DynFragment stack_frag_id); State.set_reg state ra - (State.Tval.of_var ~ctyp:(Ctype.of_frag_somewhere (Ctype.Global ".text")) RetAddr); + (State.Tval.of_var ~ctyp:(Ctype.of_frag_somewhere (Ctype.Global None)) RetAddr); let sp_exp = State.Exp.of_reg state.id sp in (* Assert that Sp is 16 bytes aligned *) State.push_assert state Exp.Typed.(extract ~last:3 ~first:0 sp_exp = bits_int ~size:4 0); diff --git a/src/ctype/ctype.ml b/src/ctype/ctype.ml index cb97ab46..e79cde62 100644 --- a/src/ctype/ctype.ml +++ b/src/ctype/ctype.ml @@ -137,10 +137,10 @@ and fragment = | 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 *) - (* TODO broken - maybe shouldn't have the section string *) - | Global of string + | Global of string option (** The Global fragment that contains all the fixed ELF section - .text, .data, .rodata, ... *) + .text, .data, .rodata, ... + If a section is given, it is the fragment consisting only of that section *) (** The type of an offset in a fragment *) and offset = Const of int (** Constant offset *) | Somewhere @@ -673,7 +673,7 @@ and pp_fragment frag = | DynArray t -> pp t ^^ !^"[]" | Unknown -> !^"unknown" | DynFragment i -> dprintf "frag %d" i - | Global s -> !^"global " ^^ !^s + | Global s -> !^"global " ^^ optional string s and pp_offset = function | Const off when off = 0 -> empty diff --git a/src/relsim/relsim.ml b/src/relsim/relsim.ml index 3146a1b4..c6e0db0d 100644 --- a/src/relsim/relsim.ml +++ b/src/relsim/relsim.ml @@ -70,9 +70,12 @@ let rec sem_type_of_type (typ: Ctype.t) : sem_type = | Ptr { fragment=Ctype.DynArray typ'; _ } -> Ptr (sem_type_of_type typ') | _ -> Raise.todo() +let unknown_section_counter = Counter.make 0 + let value_rel_for_type: Ctype.unqualified -> value_relation = function | Ctype.Machine _ | Ctype.Cint _ | Ctype.Cbool | Ctype.Enum _ -> Eq -| Ptr { fragment=Ctype.Global s; _ } -> EqSection s +| Ptr { fragment=Ctype.Global Some s; _ } -> EqSection s +| Ptr { fragment=Ctype.Global None; _ } -> EqSection ("Unknown_"^string_of_int (Counter.get unknown_section_counter)) | Ptr { fragment=Ctype.DynFragment i; _ } -> EqSection ("Dyn_"^string_of_int i) | Ptr { fragment=Ctype.DynArray typ'; _ } -> Indirect (sem_type_of_type typ') | _ -> Raise.todo() diff --git a/src/state/base.ml b/src/state/base.ml index 8b608249..26e8d0f8 100644 --- a/src/state/base.ml +++ b/src/state/base.ml @@ -712,7 +712,7 @@ let update_reg_exp (s : t) (reg : Reg.t) (f : exp -> exp) = let set_pc ~(pc : Reg.t) (s : t) (pcval : Elf.Address.t) = let exp = Exp.of_address ~size:64 pcval in - let ctyp = Ctype.of_frag (Ctype.Global ".text") ~offset:pcval.offset ~constexpr:true in + let ctyp = Ctype.of_frag (Ctype.Global pcval.section) ~offset:pcval.offset ~constexpr:true in set_reg s pc @@ Tval.make ~ctyp exp diff --git a/src/trace/typer.ml b/src/trace/typer.ml index d596b670..ed6a98fc 100644 --- a/src/trace/typer.ml +++ b/src/trace/typer.ml @@ -300,7 +300,7 @@ let fragment_at ~(dwarf : Dw.t) ~fenv ~size (frag : Ctype.fragment) at : Ctype.t let* (typ, off) = Fragment.at_off_opt frag at in Ctype.type_at ~env ~size typ off | Global s -> ( - match Elf.SymTable.of_addr_with_offset_opt dwarf.elf.symbols Elf.Address.{ section = Some s; offset = at } with + match Elf.SymTable.of_addr_with_offset_opt dwarf.elf.symbols Elf.Address.{ section = s; offset = at } with | Some (sym, offset) -> ( match Hashtbl.find_opt dwarf.vars sym.name with | Some v -> Ctype.type_at ~env ~size v.ctype offset From 652931b940a1d040940ab3ac06f2792972d39339 Mon Sep 17 00:00:00 2001 From: Matej Urban Date: Thu, 31 Jul 2025 19:12:16 +0200 Subject: [PATCH 110/116] Fix form of PC value The executor expects exactly the form section+offset, but previously it was set to section[63:0]+offset --- src/state/base.ml | 19 ++++++++++--------- src/state/base.mli | 4 ++-- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/src/state/base.ml b/src/state/base.ml index 26e8d0f8..0ffaa050 100644 --- a/src/state/base.ml +++ b/src/state/base.ml @@ -192,16 +192,17 @@ module Exp = struct let offset = BitVec.to_int conc in Elf.Address.{ section = section; offset } - let of_section ~(size : int) (section : string) = - Typed.extract ~last:(size-1) ~first:0 - (of_var @@ Var.Section section) - - - let of_address ~(size : int) (addr : Elf.Address.t) = + let of_section ?(size : int option) (section : string) = + let s = of_var @@ Var.Section section in + match size with + | None -> s + | Some size -> Typed.extract ~last:(size-1) ~first:0 s + + let of_address ?(size : int option) (addr : Elf.Address.t) = Typed.( - let offset = bits_int ~size addr.offset in + let offset = bits_int ~size:(Option.value ~default:64 size) addr.offset in match addr.section with - | Some section -> of_section ~size section + offset + | Some section -> of_section ?size section + offset | None -> offset ) end @@ -711,7 +712,7 @@ let update_reg_exp (s : t) (reg : Reg.t) (f : exp -> exp) = Reg.Map.get s.regs reg |> Tval.map_exp f |> Reg.Map.set s.regs reg let set_pc ~(pc : Reg.t) (s : t) (pcval : Elf.Address.t) = - let exp = Exp.of_address ~size:64 pcval in + let exp = Exp.of_address pcval in let ctyp = Ctype.of_frag (Ctype.Global pcval.section) ~offset:pcval.offset ~constexpr:true in set_reg s pc @@ Tval.make ~ctyp exp diff --git a/src/state/base.mli b/src/state/base.mli index 696fe923..f0702dda 100644 --- a/src/state/base.mli +++ b/src/state/base.mli @@ -164,9 +164,9 @@ module Exp : sig val expect_address : t -> Elf.Address.t - val of_section : size:int -> string -> t + val of_section : ?size:int -> string -> t - val of_address : size:int -> Elf.Address.t -> t + val of_address : ?size:int -> Elf.Address.t -> t end type exp = Exp.t From 85b24f783bbc073994cfb81d9b4bf047e73684b6 Mon Sep 17 00:00:00 2001 From: Matej Urban Date: Thu, 31 Jul 2025 19:44:41 +0200 Subject: [PATCH 111/116] Fix: stop emiting error about values being typed constexpr --- src/state/base.ml | 3 ++- src/trace/typer.ml | 20 ++++++++++++-------- 2 files changed, 14 insertions(+), 9 deletions(-) diff --git a/src/state/base.ml b/src/state/base.ml index 0ffaa050..391a9639 100644 --- a/src/state/base.ml +++ b/src/state/base.ml @@ -713,7 +713,8 @@ let update_reg_exp (s : t) (reg : Reg.t) (f : exp -> exp) = let set_pc ~(pc : Reg.t) (s : t) (pcval : Elf.Address.t) = let exp = Exp.of_address pcval in - let ctyp = Ctype.of_frag (Ctype.Global pcval.section) ~offset:pcval.offset ~constexpr:true in + let constexpr = Option.is_none pcval.section in + let ctyp = Ctype.of_frag (Ctype.Global pcval.section) ~offset:pcval.offset ~constexpr in set_reg s pc @@ Tval.make ~ctyp exp diff --git a/src/trace/typer.ml b/src/trace/typer.ml index ed6a98fc..161cc76b 100644 --- a/src/trace/typer.ml +++ b/src/trace/typer.ml @@ -202,14 +202,18 @@ let manyop ~ctxt (m : Ast.manyop) (tvals : tval list) : Ctype.t option = match offset with | Somewhere -> Some ctyp | Const _ -> ( - try - let new_int = - constexpr_to_int ~ctxt (Typed.concat (List.map (fun t -> t.exp) tvals)) - in - debug "concat hack: %x = %t" new_int - (Pp.top Base.pp_exp (Typed.concat (List.map (fun t -> t.exp) tvals))); - Some (Ctype.ptr_set ctyp new_int) - with ConcreteEval.Symbolic -> Ctype.ptr_forget ctyp |> Option.some + if (List.for_all (fun t -> Option.exists (fun (typ:Ctype.t) -> typ.constexpr) t.ctyp) tvals) then + (* if all are constexpr *) + try + let new_int = + constexpr_to_int ~ctxt (Typed.concat (List.map (fun t -> t.exp) tvals)) + in + debug "concat hack: %x = %t" new_int + (Pp.top Base.pp_exp (Typed.concat (List.map (fun t -> t.exp) tvals))); + Some (Ctype.ptr_set ctyp new_int) + with ConcreteEval.Symbolic -> Ctype.ptr_forget ctyp |> Option.some + else + Ctype.ptr_forget ctyp |> Option.some ) ) | _ -> None From d9fb48ae980b2c062b99f6f2089eccd338066f2a Mon Sep 17 00:00:00 2001 From: Matej Urban Date: Sat, 2 Aug 2025 19:00:03 +0200 Subject: [PATCH 112/116] Allow running isla test with relocations --- src/isla/test.ml | 56 +++++++++++++++++++++++++++++++----------------- 1 file changed, 36 insertions(+), 20 deletions(-) diff --git a/src/isla/test.ml b/src/isla/test.ml index b7a06ad7..6d5839d3 100644 --- a/src/isla/test.ml +++ b/src/isla/test.ml @@ -153,11 +153,11 @@ let input_f2m file sym : imode Term.ret = let imode_term = Term.(ret (const input_f2m $ file $ sym)) (** Input takes the imode and the main argument and returns the filename and input string *) -let input imode (arg : string) : (string * string) Term.ret = +let input imode (arg : string) : (string * string * Relocation.t option) Term.ret = match imode with - | CMD -> `Ok ("CLI input", arg) + | CMD -> `Ok ("CLI input", arg, None) | FILE -> ( - try `Ok (arg, read_string arg) with e -> `Error (false, Printexc.to_string e) + try `Ok (arg, read_string arg, None) with e -> `Error (false, Printexc.to_string e) ) | ELF s -> let filename = s ^ " in " ^ arg in @@ -166,7 +166,12 @@ let input imode (arg : string) : (string * string) Term.ret = try Elf.SymTable.of_position_string elf.symbols s with Not_found -> fail "The position %s could not be found in %s" s arg in - `Ok (filename, BytesSeq.to_string (BytesSeq.sub sym.data.data off 4)) (* TODO relocations *) + let data, reloc = Elf.RelocBytesSeq.as_opcode @@ Elf.Symbol.sub sym off 4 in + let reloc_typ = Option.map (fun (r : Elf.Relocations.rel) -> + base "Instruction has a relocation, executing symbolically"; + r.target + ) reloc in + `Ok (filename, BytesSeq.to_string data, reloc_typ) let input_term = Term.(ret (const input $ imode_term $ arg)) @@ -183,28 +188,30 @@ let isla_f2m direct hex bin sym : isla_mode Term.ret = let isla_mode_term = Term.(ret (CmdlinerHelper.func_option Logs.term isla_f2m $ direct $ hex $ bin $ sym)) -let isla_mode_to_request imode input = +let isla_mode_to_request imode input reloc_typ = match imode with - | ASM -> Server.TEXT_ASM input - | HEX -> Server.ASM (BytesSeq.of_hex input, None) (* TODO? *) - | BIN -> Server.ASM (BytesSeq.of_string input, None) + | ASM -> + Option.iter (fun r -> warn "Relocation is ignored %t" (Pp.top Elf.Relocations.pp_target r)) reloc_typ; + Server.TEXT_ASM input + | HEX -> Server.ASM (BytesSeq.of_hex input, reloc_typ) + | BIN -> Server.ASM (BytesSeq.of_string input, reloc_typ) | _ -> assert false (** Run isla and return a text trace with a filename (if mode is RAW than just return the trace and filename without isla) If isla return multiple traces, just silently pick the first non-exceptional one *) -let isla_run isla_mode arch (filename, input) : string * string * Server.config = +let isla_run isla_mode arch (filename, input, reloc_typ) : string * (string option * string) * Server.config = match isla_mode with - | RAW -> (filename, input, Config.File.get_isla_config arch) + | RAW -> (filename, (None, input), Config.File.get_isla_config arch) | _ -> Server.( Random.self_init (); let config = ConfigFile.get_isla_config arch in start config; - let msg : string = - match request (isla_mode_to_request isla_mode input) with - | Traces (_, l) -> List.assoc true l (* TODO segments *) + let msg : string option * string = + match request (isla_mode_to_request isla_mode input reloc_typ) with + | Traces (segs, l) -> segs, List.assoc true l | _ -> failwith "isla did not send back traces" in stop (); @@ -224,27 +231,36 @@ let processing_f2m noparse typer run simp = let pmode_term = Term.(const processing_f2m $ noparse $ typer $ run $ simp) (** Does the actual processing of the trace *) -let processing preprocessing pmode (filename, input, (config : Server.config)) : unit = +let processing preprocessing pmode (filename, (segments, input), (config : Server.config)) : unit = let parse input = + let segments = + Option.map Fun.(Base.parse_segments_string ~filename %> function Segments s -> s) segments + |> Option.value ~default:[] + in + let num_segments = List.length segments in let t = Base.parse_trc_string ~filename input in let t = Manip.remove_ignored config.ignored_regs t in + if num_segments <> 0 then + base "Instrction segments:\n%t\n" (Pp.top Base.pp_instruction_segments (Segments segments)); if preprocessing then begin - let pre = Preprocess.simplify_trc t in + let pre = Preprocess.simplify_trc ~num_segments t in base "Preprocessed trace:\n%t\n" (Pp.topi Base.pp_trc pre); - pre + pre, segments end else begin base "Trace:\n%t\n" (Pp.topi Base.pp_trc t); - t + t, segments end in - let typer t = + let typer (t, s) = let c = Type.type_trc t in base "Isla vars typing context:\n%t\n" (Pp.topi Type.pp_tcontext c); base "Register types:\n%t\n" (Pp.topi State.Reg.pp_index ()); - t + (t, s) in - let run trace = + let run (trace, s) = + if not (List.is_empty s) then + fail "Cannot run a trace with instruction segments"; let init_state = State.make () in State.lock init_state; base "Initial state:\n%t\n" (Pp.topi State.pp init_state); From 16d01d162eef354ccf6af01bc7252397e7edec7c Mon Sep 17 00:00:00 2001 From: Matej Urban Date: Tue, 12 Aug 2025 16:56:43 +0200 Subject: [PATCH 113/116] Remove "horrible hack" --- src/analyse/ControlFlow.ml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/analyse/ControlFlow.ml b/src/analyse/ControlFlow.ml index 5089c761..7f63e745 100644 --- a/src/analyse/ControlFlow.ml +++ b/src/analyse/ControlFlow.ml @@ -452,19 +452,19 @@ AArch64: *) (* matej version *) -(* + let relocation_regexp_string = "[ \t][0-9a-fA-F]+:[ \t]\\([0-9A-Z_]+\\)\t\\(.*\\)" let objdump_line_regexp = Str.regexp (" *\\([0-9a-fA-F]+\\):[ \t]\\([0-9a-fA-F ]+\\)\t\\([^ \r\t\n]+\\)[ \t]*\\([^:]*\\)\\(" ^ relocation_regexp_string ^ "\\)?$") -*) -(* ps version *) +(* ps version *) +(* let relocation_regexp_string = "%[ \t]+[0-9a-fA-F]+:[ \t]+\\([0-9A-Z_]+\\)[ \t]+\\(.*\\)" let objdump_line_regexp = Str.regexp (" *\\([0-9a-fA-F]+\\):[ \t]\\([0-9a-fA-F ]+\\)\t\\([^ \r\t\n]+\\)[ \t]*\\([^%]*\\)\\(" ^ relocation_regexp_string ^ "\\)?$") - +*) let section_start_line_regexp = Str.regexp "Disassembly of section \\(.*\\):$" From a0aada10a44993e58ff888a2b5483bad2e35b509 Mon Sep 17 00:00:00 2001 From: Matej Urban Date: Tue, 12 Aug 2025 17:16:17 +0200 Subject: [PATCH 114/116] Warn about weird lines in objdump --- src/analyse/ControlFlow.ml | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/analyse/ControlFlow.ml b/src/analyse/ControlFlow.ml index 7f63e745..2130814c 100644 --- a/src/analyse/ControlFlow.ml +++ b/src/analyse/ControlFlow.ml @@ -458,6 +458,8 @@ let relocation_regexp_string = "[ \t][0-9a-fA-F]+:[ \t]\\([0-9A-Z_]+\\)\t\\(.*\\ let objdump_line_regexp = Str.regexp (" *\\([0-9a-fA-F]+\\):[ \t]\\([0-9a-fA-F ]+\\)\t\\([^ \r\t\n]+\\)[ \t]*\\([^:]*\\)\\(" ^ relocation_regexp_string ^ "\\)?$") +let objdump_command = "aarch64-linux-gnu-objdump -d --reloc -w" + (* ps version *) (* let relocation_regexp_string = "%[ \t]+[0-9a-fA-F]+:[ \t]+\\([0-9A-Z_]+\\)[ \t]+\\(.*\\)" @@ -525,6 +527,10 @@ let parse_objdump_line (s : string) : raw_objdump_instruction option = end else None +let looks_like_objdump_line (s : string) : bool = + let regex = Str.regexp "[ \t]*[0-9a-fA-F]+:.*$" in + Str.string_match regex s 0 + (* let parse_objdump_relocation (s : string) : (string * string) option = let parse_hex_int s' = try Scanf.sscanf s' "%x" (fun i -> i) @@ -561,7 +567,10 @@ let rec parse_objdump_lines arch lines (next_index : int) (last_address : int64 let section = Option.fold ~none:section ~some:Option.some @@ parse_section_start lines.(next_index) in match parse_objdump_line lines.(next_index) with (* skip over unparseable lines *) - | None -> parse_objdump_lines arch lines (next_index + 1) last_address section + | None -> + if looks_like_objdump_line lines.(next_index) then + warn "Skipping unparseable objdump line %d: %s\nIf parsing aarch64 with relocations, generate objdump using: %s" next_index lines.(next_index) objdump_command; + parse_objdump_lines arch lines (next_index + 1) last_address section | Some ((addr, _opcode_bytes, _mnemonic, _operands, _relocation) as i) -> ( let mki = with_symbolic_address (Option.get section) in match last_address with From f7a569178490ed2c06409391485eb60e07b7ae6c Mon Sep 17 00:00:00 2001 From: Peter Sewell Date: Fri, 15 Aug 2025 11:38:44 +0100 Subject: [PATCH 115/116] add nm to isla_aarch64.toml, following isla/configs/armv8p5.toml --- src/config/isla_aarch64.toml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/config/isla_aarch64.toml b/src/config/isla_aarch64.toml index f762a682..d8d7d61c 100644 --- a/src/config/isla_aarch64.toml +++ b/src/config/isla_aarch64.toml @@ -14,12 +14,14 @@ os = "macos" arch = "aarch64" assembler = "as --target=aarch64-unknown-linux-gnu" objdump = "/opt/homebrew/opt/llvm/bin/llvm-objdump" +nm = "/opt/homebrew/opt/llvm/bin/llvm-nm" linker = "/opt/homebrew/opt/llvm/bin/ld.lld" [[toolchain]] name = "default" assembler = "aarch64-linux-gnu-as -march=armv8.1-a" objdump = "aarch64-linux-gnu-objdump" +nm = "aarch64-linux-gnu-nm" linker = "aarch64-linux-gnu-ld" [mmu] From af9d06a016b6bba1c5411dfc615e55e9f19e8790 Mon Sep 17 00:00:00 2001 From: Peter Sewell Date: Fri, 15 Aug 2025 11:59:58 +0100 Subject: [PATCH 116/116] wib --- doc/html/index.html | 2 +- doc/html/read-dwarf/Analyse/Base/index.html | 2 +- .../read-dwarf/Analyse/Collected/index.html | 6 +- .../Analyse/DwarfLineInfo/index.html | 30 ++++++++- doc/html/read-dwarf/Analyse/Elf/index.html | 2 +- .../read-dwarf/Analyse/ElfTypes/index.html | 10 ++- .../read-dwarf/Analyse/Globals/index.html | 2 +- doc/html/read-dwarf/Analyse/Pp/index.html | 50 ++++++++++++++- doc/html/read-dwarf/Analyse/Types/index.html | 2 +- doc/html/read-dwarf/Analyse/Utils/index.html | 2 +- doc/html/read-dwarf/Analyse/index.html | 2 +- doc/html/read-dwarf/Arch/index.html | 2 +- doc/html/read-dwarf/Architecture.html | 2 +- doc/html/read-dwarf/Ast/Base/Size/index.html | 2 +- doc/html/read-dwarf/Ast/Base/index.html | 57 ++++++++++++++++- doc/html/read-dwarf/Ast/Manip/index.html | 35 +++++++++- doc/html/read-dwarf/Ast/index.html | 57 ++++++++++++++++- .../read-dwarf/AstGen/Def/Size/index.html | 2 +- doc/html/read-dwarf/AstGen/Def/index.html | 9 ++- doc/html/read-dwarf/AstGen/Lexer/index.html | 2 +- doc/html/read-dwarf/AstGen/Ott/index.html | 5 +- doc/html/read-dwarf/AstGen/Parser/index.html | 14 +++- .../read-dwarf/AstGen/Parser_pp/index.html | 32 +++++++++- doc/html/read-dwarf/AstGen/index.html | 2 +- doc/html/read-dwarf/BinaryAnalysis.html | 2 +- doc/html/read-dwarf/CLI.html | 2 +- doc/html/read-dwarf/Config/Arch/index.html | 2 +- .../read-dwarf/Config/CommonOpt/index.html | 7 +- .../Config/File/ArchConf/Isla/index.html | 2 +- .../Config/File/ArchConf/index.html | 2 +- doc/html/read-dwarf/Config/File/Z3/index.html | 2 +- doc/html/read-dwarf/Config/File/index.html | 2 +- doc/html/read-dwarf/Config/index.html | 2 +- .../Config/module-type-S/index.html | 2 +- doc/html/read-dwarf/Configuration.html | 2 +- doc/html/read-dwarf/Ctype/FieldMap/index.html | 15 +++-- doc/html/read-dwarf/Ctype/index.html | 64 ++++++++++++++++++- doc/html/read-dwarf/Dw/Func/index.html | 2 +- doc/html/read-dwarf/Dw/Loc/index.html | 2 +- doc/html/read-dwarf/Dw/Var/index.html | 4 +- doc/html/read-dwarf/Dw/index.html | 2 +- doc/html/read-dwarf/Elf/File/index.html | 2 +- doc/html/read-dwarf/Elf/Segment/index.html | 10 ++- doc/html/read-dwarf/Elf/SymTable/index.html | 2 +- doc/html/read-dwarf/Elf/Symbol/index.html | 2 +- doc/html/read-dwarf/Elf/index.html | 2 +- .../read-dwarf/Exp/ConcreteEval/index.html | 2 +- .../Exp/Make/argument-1-Var/index.html | 2 +- doc/html/read-dwarf/Exp/Make/index.html | 2 +- doc/html/read-dwarf/Exp/PpExp/index.html | 16 ++++- doc/html/read-dwarf/Exp/Sums/index.html | 12 +++- doc/html/read-dwarf/Exp/Typed/index.html | 7 +- doc/html/read-dwarf/Exp/Value/index.html | 2 +- doc/html/read-dwarf/Exp/index.html | 2 +- .../read-dwarf/Exp/module-type-S/index.html | 2 +- .../read-dwarf/Exp/module-type-Var/index.html | 2 +- doc/html/read-dwarf/InstructionPipeline.html | 2 +- doc/html/read-dwarf/Isla/Base/index.html | 25 +++++++- .../read-dwarf/Isla/Cache/Epoch/index.html | 2 +- doc/html/read-dwarf/Isla/Cache/IC/index.html | 2 +- .../read-dwarf/Isla/Cache/Opcode/index.html | 8 ++- .../Isla/Cache/TraceList/index.html | 2 +- doc/html/read-dwarf/Isla/Cache/index.html | 2 +- doc/html/read-dwarf/Isla/Conv/index.html | 11 +++- doc/html/read-dwarf/Isla/Manip/index.html | 22 ++++++- .../read-dwarf/Isla/Preprocess/index.html | 2 +- doc/html/read-dwarf/Isla/Run/index.html | 6 +- .../read-dwarf/Isla/Server/Cmd/index.html | 2 +- doc/html/read-dwarf/Isla/Server/index.html | 2 +- doc/html/read-dwarf/Isla/Test/index.html | 6 +- doc/html/read-dwarf/Isla/Type/index.html | 16 ++++- doc/html/read-dwarf/Isla/index.html | 25 +++++++- .../Other_cmds/CopySources/index.html | 2 +- .../Other_cmds/CopySourcesCmd/index.html | 2 +- .../Other_cmds/DumpDwarf/index.html | 2 +- .../read-dwarf/Other_cmds/DumpSym/index.html | 2 +- .../Other_cmds/ReadDwarf/Default/index.html | 2 +- .../Other_cmds/ReadDwarf/index.html | 2 +- doc/html/read-dwarf/Other_cmds/index.html | 2 +- doc/html/read-dwarf/Printing.html | 2 +- doc/html/read-dwarf/Qtest_isla/index.html | 2 +- doc/html/read-dwarf/Qtest_run/index.html | 2 +- .../read-dwarf/Qtest_sig_aarch64/index.html | 2 +- doc/html/read-dwarf/Qtest_utils/index.html | 2 +- doc/html/read-dwarf/Run/BB/index.html | 2 +- doc/html/read-dwarf/Run/Bb_lib/index.html | 2 +- doc/html/read-dwarf/Run/Block/index.html | 9 ++- doc/html/read-dwarf/Run/Block_lib/index.html | 18 +++++- doc/html/read-dwarf/Run/Func/index.html | 14 +++- doc/html/read-dwarf/Run/FuncRD/index.html | 8 ++- doc/html/read-dwarf/Run/Init/index.html | 2 +- doc/html/read-dwarf/Run/Instr/index.html | 2 +- doc/html/read-dwarf/Run/ReadDwarf/index.html | 2 +- doc/html/read-dwarf/Run/Runner/index.html | 8 ++- doc/html/read-dwarf/Run/index.html | 2 +- doc/html/read-dwarf/Sig/index.html | 2 +- .../read-dwarf/Simrel/Base/MemRel/index.html | 5 +- .../read-dwarf/Simrel/Base/RegRel/index.html | 6 +- .../read-dwarf/Simrel/Base/Test2/index.html | 2 +- .../read-dwarf/Simrel/Base/Test3/index.html | 2 +- doc/html/read-dwarf/Simrel/Base/index.html | 9 ++- doc/html/read-dwarf/Simrel/index.html | 2 +- doc/html/read-dwarf/State/Base/Exp/index.html | 2 +- doc/html/read-dwarf/State/Base/Id/index.html | 2 +- .../State/Base/Mem/Fragment/Block/index.html | 2 +- .../State/Base/Mem/Fragment/Event/index.html | 2 +- .../State/Base/Mem/Fragment/index.html | 2 +- doc/html/read-dwarf/State/Base/Mem/index.html | 2 +- .../read-dwarf/State/Base/Tval/index.html | 2 +- doc/html/read-dwarf/State/Base/Var/index.html | 2 +- doc/html/read-dwarf/State/Base/index.html | 14 +++- .../read-dwarf/State/Fragment/Env/index.html | 2 +- doc/html/read-dwarf/State/Fragment/index.html | 15 +++-- doc/html/read-dwarf/State/Reg/Map/index.html | 2 +- doc/html/read-dwarf/State/Reg/Path/index.html | 2 +- doc/html/read-dwarf/State/Reg/index.html | 2 +- .../State/Simplify/ContextFull/index.html | 2 +- .../State/Simplify/Z3St/Htbl/index.html | 2 +- .../read-dwarf/State/Simplify/Z3St/index.html | 2 +- doc/html/read-dwarf/State/Simplify/index.html | 2 +- .../Make/argument-1-Var/index.html | 2 +- .../State/SymbolicBytes/Make/index.html | 2 +- .../read-dwarf/State/SymbolicBytes/index.html | 2 +- .../SymbolicBytes/module-type-S/index.html | 2 +- doc/html/read-dwarf/State/Tree/index.html | 2 +- doc/html/read-dwarf/State/index.html | 14 +++- doc/html/read-dwarf/SymbolicExecution.html | 2 +- doc/html/read-dwarf/SymbolicExpressions.html | 2 +- .../read-dwarf/Tests/BytesSeqT/index.html | 2 +- .../read-dwarf/Tests/Common/Gen/index.html | 43 ++++++++++++- doc/html/read-dwarf/Tests/Common/index.html | 2 +- .../read-dwarf/Tests/ConcreteEvalT/index.html | 14 +++- .../read-dwarf/Tests/ExpGen/ExpT/index.html | 2 +- .../read-dwarf/Tests/ExpGen/Gen/index.html | 5 +- .../read-dwarf/Tests/ExpGen/Var/index.html | 2 +- .../Tests/ExpGen/Z3/Htbl/index.html | 2 +- .../read-dwarf/Tests/ExpGen/Z3/index.html | 2 +- doc/html/read-dwarf/Tests/ExpGen/index.html | 9 ++- .../read-dwarf/Tests/SimplifyCheck/index.html | 14 +++- doc/html/read-dwarf/Tests/index.html | 2 +- doc/html/read-dwarf/Trace/Base/Exp/index.html | 2 +- .../Trace/Base/SimpContext/index.html | 2 +- doc/html/read-dwarf/Trace/Base/Var/index.html | 2 +- .../read-dwarf/Trace/Base/VarTbl/index.html | 2 +- .../Trace/Base/Z3Tr/Htbl/index.html | 2 +- .../read-dwarf/Trace/Base/Z3Tr/index.html | 2 +- doc/html/read-dwarf/Trace/Base/index.html | 14 +++- doc/html/read-dwarf/Trace/Cache/TC/index.html | 2 +- .../read-dwarf/Trace/Cache/Traces/index.html | 2 +- doc/html/read-dwarf/Trace/Cache/index.html | 2 +- doc/html/read-dwarf/Trace/Context/index.html | 6 +- doc/html/read-dwarf/Trace/Instr/index.html | 5 +- doc/html/read-dwarf/Trace/Run/index.html | 18 +++++- doc/html/read-dwarf/Trace/index.html | 13 +++- doc/html/read-dwarf/TypeInference.html | 2 +- doc/html/read-dwarf/Utilities.html | 2 +- doc/html/read-dwarf/Utils/Array/index.html | 6 +- doc/html/read-dwarf/Utils/BitVec/index.html | 8 ++- doc/html/read-dwarf/Utils/Bits/index.html | 2 +- doc/html/read-dwarf/Utils/BytesSeq/index.html | 2 +- .../Utils/Cache/Cmd/Test/Cache/index.html | 2 +- .../Utils/Cache/Cmd/Test/Single/index.html | 2 +- .../Utils/Cache/Cmd/Test/Value/index.html | 2 +- .../Utils/Cache/Cmd/Test/index.html | 2 +- .../read-dwarf/Utils/Cache/Cmd/index.html | 2 +- .../Utils/Cache/IntEpoch/index.html | 2 +- .../Cache/Make/argument-1-Key/index.html | 2 +- .../Cache/Make/argument-2-Value/index.html | 2 +- .../Cache/Make/argument-3-Epoch/index.html | 2 +- .../read-dwarf/Utils/Cache/Make/index.html | 2 +- .../Cache/Single/argument-1-Value/index.html | 2 +- .../read-dwarf/Utils/Cache/Single/index.html | 2 +- .../Utils/Cache/UnitEpoch/index.html | 2 +- doc/html/read-dwarf/Utils/Cache/index.html | 6 +- .../Utils/Cache/module-type-Epoch/index.html | 2 +- .../Utils/Cache/module-type-Key/index.html | 2 +- .../Utils/Cache/module-type-S/index.html | 2 +- .../Cache/module-type-SingleS/index.html | 2 +- .../Utils/Cache/module-type-Value/index.html | 2 +- .../read-dwarf/Utils/Cmd/IOServer/index.html | 2 +- .../Utils/Cmd/SocketServer/index.html | 2 +- doc/html/read-dwarf/Utils/Cmd/index.html | 6 +- .../Utils/CmdlinerHelper/index.html | 8 ++- doc/html/read-dwarf/Utils/Counter/index.html | 4 +- doc/html/read-dwarf/Utils/Files/index.html | 2 +- doc/html/read-dwarf/Utils/FullVec/index.html | 2 +- doc/html/read-dwarf/Utils/Fun/index.html | 2 +- .../read-dwarf/Utils/HashVector/index.html | 2 +- doc/html/read-dwarf/Utils/IdMap/index.html | 7 +- doc/html/read-dwarf/Utils/IntBits/index.html | 2 +- doc/html/read-dwarf/Utils/List/index.html | 16 ++++- .../Utils/Logs/Logger/argument-1-S/index.html | 2 +- .../read-dwarf/Utils/Logs/Logger/index.html | 4 +- doc/html/read-dwarf/Utils/Logs/index.html | 12 +++- .../Utils/Logs/module-type-String/index.html | 2 +- doc/html/read-dwarf/Utils/Option/index.html | 2 +- doc/html/read-dwarf/Utils/Pair/index.html | 12 +++- .../Utils/Pp/class-type-custom/index.html | 2 +- .../Utils/Pp/class-type-output/index.html | 2 +- doc/html/read-dwarf/Utils/Pp/index.html | 50 +++++++++++++-- doc/html/read-dwarf/Utils/Protect/index.html | 2 +- doc/html/read-dwarf/Utils/Raise/index.html | 2 +- .../read-dwarf/Utils/RngMap/IMap/index.html | 6 +- .../RngMap/Make/argument-1-Obj/index.html | 2 +- .../read-dwarf/Utils/RngMap/Make/index.html | 15 +++-- .../PairLenObject/argument-1-Obj/index.html | 2 +- .../Utils/RngMap/PairLenObject/index.html | 2 +- doc/html/read-dwarf/Utils/RngMap/index.html | 2 +- .../RngMap/module-type-LenObject/index.html | 2 +- .../RngMap/module-type-Object/index.html | 2 +- .../Utils/RngMap/module-type-S/index.html | 15 +++-- doc/html/read-dwarf/Utils/Seq/index.html | 2 +- doc/html/read-dwarf/Utils/String/index.html | 2 +- doc/html/read-dwarf/Utils/Vec/index.html | 2 +- doc/html/read-dwarf/Utils/WeakMap/index.html | 2 +- doc/html/read-dwarf/Utils/WeakPtr/index.html | 2 +- doc/html/read-dwarf/Utils/index.html | 2 +- .../read-dwarf/Z3/CheckContext/index.html | 2 +- .../Z3/ContextCounter/argument-1-S/index.html | 2 +- .../read-dwarf/Z3/ContextCounter/index.html | 2 +- .../Z3/Make/argument-1-Var/index.html | 2 +- doc/html/read-dwarf/Z3/Make/index.html | 2 +- doc/html/read-dwarf/Z3/SimpContext/index.html | 2 +- doc/html/read-dwarf/Z3/index.html | 12 +++- .../read-dwarf/Z3/module-type-S/index.html | 2 +- .../read-dwarf/Z3/module-type-Var/index.html | 2 +- doc/html/read-dwarf/index.html | 2 +- 227 files changed, 1075 insertions(+), 258 deletions(-) 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 @@ index - + diff --git a/doc/html/read-dwarf/Analyse/Base/index.html b/doc/html/read-dwarf/Analyse/Base/index.html index 5e6ed6ad..ff475dd7 100644 --- a/doc/html/read-dwarf/Analyse/Base/index.html +++ b/doc/html/read-dwarf/Analyse/Base/index.html @@ -1,2 +1,2 @@ -Base (read-dwarf.Analyse.Base)

Module Analyse.Base

val process_file : unit -> unit
\ No newline at end of file +Base (read-dwarf.Analyse.Base)

Module Analyse.Base

val process_file : unit -> unit
diff --git a/doc/html/read-dwarf/Analyse/Collected/index.html b/doc/html/read-dwarf/Analyse/Collected/index.html index 969e6d4e..b0c2c3cf 100644 --- a/doc/html/read-dwarf/Analyse/Collected/index.html +++ b/doc/html/read-dwarf/Analyse/Collected/index.html @@ -1,2 +1,6 @@ -Collected (read-dwarf.Analyse.Collected)

Module Analyse.Collected

collect the various test analysis data

val mk_analysis : ElfTypes.test -> string -> string option -> Analyse__.CollectedType.analysis
\ No newline at end of file +Collected (read-dwarf.Analyse.Collected)

Module Analyse.Collected

collect the various test analysis data

val mk_analysis : + ElfTypes.test -> + string -> + string option -> + Analyse__.CollectedType.analysis
diff --git a/doc/html/read-dwarf/Analyse/DwarfLineInfo/index.html b/doc/html/read-dwarf/Analyse/DwarfLineInfo/index.html index c07c349e..9fc2be74 100644 --- a/doc/html/read-dwarf/Analyse/DwarfLineInfo/index.html +++ b/doc/html/read-dwarf/Analyse/DwarfLineInfo/index.html @@ -1,2 +1,30 @@ -DwarfLineInfo (read-dwarf.Analyse.DwarfLineInfo)

Module Analyse.DwarfLineInfo

post-process DWARF source line info

type evaluated_line_info_sequence = {
elis_first : Utils.addr;
elis_last : Utils.addr;
elis_lnh : Dwarf.line_number_header;
elis_lines : Dwarf.line_number_registers list;
}
type evaluated_line_info_entry = {
elie_first : Utils.addr;
elie_last : Utils.addr;
elie_lnh : Dwarf.line_number_header;
elie_lnr : Dwarf.line_number_registers;
}
type evaluated_line_info_for_instruction = {
elifi_start : bool;
elifi_entry : evaluated_line_info_entry;
}
val pp_line_number_header_concise : Dwarf.line_number_header -> string
val pp_sequence_concise : evaluated_line_info_sequence -> string
val pp_elie_concise : evaluated_line_info_entry -> string
val split_into_sequences : (Dwarf.line_number_header * Dwarf.line_number_registers list) -> evaluated_line_info_sequence list
val split_into_entries : evaluated_line_info_sequence -> evaluated_line_info_entry list
val mk_line_info : Dwarf.evaluated_line_info -> Analyse__.ControlFlowTypes.instruction array -> evaluated_line_info_for_instruction list array
val source_file_cache : ((string option * string option * string) * string array option) list Stdlib.ref
val actual_directories : string option -> (string option * string option * string) -> string * string
val source_line : (string option * string option * string) -> int -> string option
val pp_source_line : string option -> int -> string
val mk_subprogram_name : Dwarf.dwarf_static -> evaluated_line_info_for_instruction -> string
val pp_dwarf_source_file_lines' : Types.ppmode -> Dwarf.dwarf_static -> bool -> bool -> evaluated_line_info_for_instruction -> string
val dwarf_source_file_line_numbers_by_index : ElfTypes.test -> evaluated_line_info_for_instruction list array -> int -> (string * int) list
\ No newline at end of file +DwarfLineInfo (read-dwarf.Analyse.DwarfLineInfo)

Module Analyse.DwarfLineInfo

post-process DWARF source line info

post-processed DWARF source line info

type evaluated_line_info_sequence = {
  1. elis_first : Utils.addr;
  2. elis_last : Utils.addr;
  3. elis_lnh : Dwarf.line_number_header;
  4. elis_lines : Dwarf.line_number_registers list;
}
type evaluated_line_info_entry = {
  1. elie_first : Utils.addr;
  2. elie_last : Utils.addr;
  3. elie_lnh : Dwarf.line_number_header;
  4. elie_lnr : Dwarf.line_number_registers;
}
type evaluated_line_info_for_instruction = {
  1. elifi_start : bool;
  2. elifi_entry : evaluated_line_info_entry;
}
val pp_line_number_header_concise : Dwarf.line_number_header -> string
val pp_sequence_concise : evaluated_line_info_sequence -> string
val pp_elie_concise : evaluated_line_info_entry -> string
val split_into_sequences : + (Dwarf.line_number_header * Dwarf.line_number_registers list) -> + evaluated_line_info_sequence list
val split_into_entries : + evaluated_line_info_sequence -> + evaluated_line_info_entry list
val mk_line_info : + Dwarf.evaluated_line_info -> + Analyse__.ControlFlowTypes.instruction array -> + evaluated_line_info_for_instruction list array

find and pretty-print source lines for addresses

val source_file_cache : + ((string option * string option * string) * string array option) list + Stdlib.ref
val actual_directories : + string option -> + (string option * string option * string) -> + string * string
val source_line : + (string option * string option * string) -> + int -> + string option
val pp_source_line : string option -> int -> string
val mk_subprogram_name : + Dwarf.dwarf_static -> + evaluated_line_info_for_instruction -> + string
val pp_dwarf_source_file_lines' : + Types.ppmode -> + Dwarf.dwarf_static -> + bool -> + bool -> + evaluated_line_info_for_instruction -> + string
val dwarf_source_file_line_numbers_by_index : + ElfTypes.test -> + evaluated_line_info_for_instruction list array -> + int -> + (string * int) list
diff --git a/doc/html/read-dwarf/Analyse/Elf/index.html b/doc/html/read-dwarf/Analyse/Elf/index.html index 44f5e77f..6ce11d14 100644 --- a/doc/html/read-dwarf/Analyse/Elf/index.html +++ b/doc/html/read-dwarf/Analyse/Elf/index.html @@ -1,2 +1,2 @@ -Elf (read-dwarf.Analyse.Elf)

Module Analyse.Elf

val pp_symbol_map : Elf_file.global_symbol_init_info -> string
val parse_elf_file : string -> ElfTypes.test
val marshal_to_file : string -> 'a -> unit
val marshal_from_file : string -> ElfTypes.test option
\ No newline at end of file +Elf (read-dwarf.Analyse.Elf)

Module Analyse.Elf

pp symbol map

val pp_symbol_map : Elf_file.global_symbol_init_info -> string

use linksem to parse ELF file and extract DWARF info

val parse_elf_file : string -> ElfTypes.test

marshal and unmarshal test

val marshal_to_file : string -> 'a -> unit
val marshal_from_file : string -> ElfTypes.test option
diff --git a/doc/html/read-dwarf/Analyse/ElfTypes/index.html b/doc/html/read-dwarf/Analyse/ElfTypes/index.html index e6176a29..49ac05e6 100644 --- a/doc/html/read-dwarf/Analyse/ElfTypes/index.html +++ b/doc/html/read-dwarf/Analyse/ElfTypes/index.html @@ -1,2 +1,10 @@ -ElfTypes (read-dwarf.Analyse.ElfTypes)

Module Analyse.ElfTypes

type of collected ELF-file data from linksem

type architecture =
| AArch64
| X86

architectures from linksem elf_header.lem

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;
}
\ No newline at end of file +ElfTypes (read-dwarf.Analyse.ElfTypes)

Module Analyse.ElfTypes

type of collected ELF-file data from linksem

type architecture =
  1. | AArch64
  2. | X86

architectures from linksem elf_header.lem

AMD x86-64 architecture, elf_ma_x86_64 = 62

type test = {
  1. elf_file : Elf_file.elf_file;
  2. arch : architecture;
  3. symbol_map : (string + * (Z.t + * Z.t + * Utils.Sym.t + * (Byte_sequence_wrapper.byte_sequence + * Analyse__.Symbols.rels) + * Z.t)) + list;
  4. e_entry : Utils.natural;
  5. e_machine : Utils.natural;
  6. dwarf_static : Dwarf.dwarf_static;
  7. dwarf_semi_pp_frame_info : (Utils.natural * string * (string * string) list) + list;
}
diff --git a/doc/html/read-dwarf/Analyse/Globals/index.html b/doc/html/read-dwarf/Analyse/Globals/index.html index e934f8c1..402b9123 100644 --- a/doc/html/read-dwarf/Analyse/Globals/index.html +++ b/doc/html/read-dwarf/Analyse/Globals/index.html @@ -1,2 +1,2 @@ -Globals (read-dwarf.Analyse.Globals)

Module Analyse.Globals

val elf : string option Stdlib.ref
val branch_table_data_file : string option Stdlib.ref
val objdump_d : string option Stdlib.ref
val elf2 : string option Stdlib.ref
val branch_table_data_file2 : string option Stdlib.ref
val objdump_d2 : string option Stdlib.ref
val qemu_log : string option Stdlib.ref
val comp_dir : string option Stdlib.ref
val cfg_dot_file : string option Stdlib.ref
val cfg_source_nodes : string option Stdlib.ref
val cfg_source_nodes2 : string option Stdlib.ref
val out_file : string option Stdlib.ref
val out_dir : string option Stdlib.ref
val clip_binary : bool Stdlib.ref
val show_vars : bool Stdlib.ref
val show_cfa : bool Stdlib.ref
val show_source : bool Stdlib.ref
val ppmode : Types.ppmode Stdlib.ref
val src_target_dir : string option Stdlib.ref
val copy_sources_dry_run : bool Stdlib.ref
val skylight : bool Stdlib.ref
\ No newline at end of file +Globals (read-dwarf.Analyse.Globals)

Module Analyse.Globals

val elf : string option Stdlib.ref
val branch_table_data_file : string option Stdlib.ref
val objdump_d : string option Stdlib.ref
val elf2 : string option Stdlib.ref
val branch_table_data_file2 : string option Stdlib.ref
val objdump_d2 : string option Stdlib.ref
val qemu_log : string option Stdlib.ref
val comp_dir : string option Stdlib.ref
val cfg_dot_file : string option Stdlib.ref
val cfg_source_nodes : string option Stdlib.ref
val cfg_source_nodes2 : string option Stdlib.ref
val out_file : string option Stdlib.ref
val out_dir : string option Stdlib.ref
val clip_binary : bool Stdlib.ref
val show_vars : bool Stdlib.ref
val show_cfa : bool Stdlib.ref
val show_source : bool Stdlib.ref
val ppmode : Types.ppmode Stdlib.ref
val src_target_dir : string option Stdlib.ref
val copy_sources_dry_run : bool Stdlib.ref
val skylight : bool Stdlib.ref
val morello : bool Stdlib.ref
val suppress_stuff : bool Stdlib.ref
diff --git a/doc/html/read-dwarf/Analyse/Pp/index.html b/doc/html/read-dwarf/Analyse/Pp/index.html index 1b63518b..45efa3f1 100644 --- a/doc/html/read-dwarf/Analyse/Pp/index.html +++ b/doc/html/read-dwarf/Analyse/Pp/index.html @@ -1,2 +1,50 @@ -Pp (read-dwarf.Analyse.Pp)

Module Analyse.Pp

type render_kind =
| Render_symbol_star
| Render_symbol_nostar
| Render_source
| Render_frame
| Render_instruction
| Render_vars
| Render_vars_new
| Render_vars_old
| Render_inlining
| Render_ctrlflow
val render_colour : render_kind -> string
val render_class_name : render_kind -> string
type html_idiom =
| HI_span
| HI_pre
| HI_classless_span
| HI_font
val html_idiom : html_idiom
val css : Types.ppmode -> render_kind -> string -> string
val last_frame_info : string Stdlib.ref
val last_var_info : string list Stdlib.ref
val last_source_info : string Stdlib.ref
val pp_instruction_init : unit -> unit
val pp_instruction : Types.ppmode -> ElfTypes.test -> Analyse__.CollectedType.analysis -> int -> int -> Analyse__.ControlFlowTypes.instruction -> string
val skylight : unit -> string
val chunk_filename_whole : Types.ppmode -> 'a -> string -> string * string
val chunk_filename_per_cu : Types.ppmode -> 'a -> string -> Dwarf.sdt_compilation_unit -> string * string
val wrap_chunks : Types.ppmode -> ('a * 'b * string) list -> ('a * 'b * string) list
val whole_file_chunks : Types.ppmode -> ElfTypes.test -> Analyse__.CollectedType.analysis -> 'a -> ('b * string * string * 'c) list list -> (string * string * string) list
val pp_instructions_ranged : Types.ppmode -> ElfTypes.test -> Analyse__.CollectedType.analysis -> (Utils.addr * Utils.addr) -> string
val chunks_of_ranged_cu : Types.ppmode -> ElfTypes.test -> Analyse__.CollectedType.analysis -> 'a -> ((Utils.natural * Utils.natural) * Dwarf.sdt_compilation_unit) -> string * (string * string * string) list
val wrap_body : Types.ppmode -> (string * string * string) -> string
val output_file : (string * string * string) -> unit
val output_whole_file_files : Types.ppmode -> ElfTypes.test -> Analyse__.CollectedType.analysis -> 'a -> ('b * string * string * 'c) list list -> unit
val output_per_cu_files : Types.ppmode -> ElfTypes.test -> Analyse__.CollectedType.analysis -> 'a -> ((Utils.natural * Utils.natural) * Dwarf.sdt_compilation_unit) list -> (string * string * string * string) list list
val pp_test_analysis : Types.ppmode -> ElfTypes.test -> Analyse__.CollectedType.analysis -> string
\ No newline at end of file +Pp (read-dwarf.Analyse.Pp)

Module Analyse.Pp

render collected analysis data to text or css

type render_kind =
  1. | Render_symbol_star
  2. | Render_symbol_nostar
  3. | Render_source
  4. | Render_frame
  5. | Render_instruction
  6. | Render_vars
  7. | Render_vars_new
  8. | Render_vars_old
  9. | Render_inlining
  10. | Render_ctrlflow
  11. | Render_relocation
val render_colour : render_kind -> string
val render_class_name : render_kind -> string
type html_idiom =
  1. | HI_span
  2. | HI_pre
  3. | HI_classless_span
  4. | HI_font
val html_idiom : html_idiom
val css : Types.ppmode -> render_kind -> string -> string

pretty-print one instruction

val last_frame_info : string Stdlib.ref
val last_var_info : string list Stdlib.ref
val last_source_info : string Stdlib.ref
val pp_instruction_init : unit -> unit
val pp_instruction : + Types.ppmode -> + ElfTypes.test -> + Analyse__.CollectedType.analysis -> + int -> + int -> + Analyse__.ControlFlowTypes.instruction -> + string

pretty-print test analysis

val skylight : unit -> string
val chunk_filename_whole : Types.ppmode -> 'a -> string -> string * string
val chunk_filename_per_cu : + Types.ppmode -> + 'a -> + string -> + Dwarf.sdt_compilation_unit -> + string * string
val wrap_chunks : + Types.ppmode -> + ('a * 'b * string) list -> + ('a * 'b * string) list
val whole_file_chunks : + Types.ppmode -> + ElfTypes.test -> + Analyse__.CollectedType.analysis -> + 'a -> + ('b * string * string * 'c) list list -> + (string * string * string) list
val pp_instructions_ranged : + Types.ppmode -> + ElfTypes.test -> + Analyse__.CollectedType.analysis -> + (Utils.addr * Sym_ocaml.Num.t) -> + string
val chunks_of_ranged_cu : + Types.ppmode -> + ElfTypes.test -> + Analyse__.CollectedType.analysis -> + 'a -> + ((Utils.natural * Utils.natural) * Dwarf.sdt_compilation_unit) -> + string * (string * string * string) list
val wrap_body : Types.ppmode -> (string * string * string) -> string
val output_file : (string * string * string) -> unit
val output_whole_file_files : + Types.ppmode -> + ElfTypes.test -> + Analyse__.CollectedType.analysis -> + 'a -> + ('b * string * string * 'c) list list -> + unit
val output_per_cu_files : + Types.ppmode -> + ElfTypes.test -> + Analyse__.CollectedType.analysis -> + 'a -> + ((Utils.natural * Utils.natural) * Dwarf.sdt_compilation_unit) list -> + (string * string * string * string) list list
val pp_test_analysis : + Types.ppmode -> + ElfTypes.test -> + Analyse__.CollectedType.analysis -> + string
diff --git a/doc/html/read-dwarf/Analyse/Types/index.html b/doc/html/read-dwarf/Analyse/Types/index.html index 442fddb6..f2ec21c7 100644 --- a/doc/html/read-dwarf/Analyse/Types/index.html +++ b/doc/html/read-dwarf/Analyse/Types/index.html @@ -1,2 +1,2 @@ -Types (read-dwarf.Analyse.Types)

Module Analyse.Types

types shared between analyse* and the read-dwarf top-level

type ppmode =
| Ascii
| Html
\ No newline at end of file +Types (read-dwarf.Analyse.Types)

Module Analyse.Types

types shared between analyse* and the read-dwarf top-level

type ppmode =
  1. | Ascii
  2. | Html
diff --git a/doc/html/read-dwarf/Analyse/Utils/index.html b/doc/html/read-dwarf/Analyse/Utils/index.html index 580f1f56..2d1db3b3 100644 --- a/doc/html/read-dwarf/Analyse/Utils/index.html +++ b/doc/html/read-dwarf/Analyse/Utils/index.html @@ -1,2 +1,2 @@ -Utils (read-dwarf.Analyse.Utils)

Module Analyse.Utils

Miscellaneous types and utility functions used throughout the analyse code

type natural = Nat_big_num.num

TODO: Maybe just use Z.t everywhere (it's shorter)

type addr = natural

machine address

val pp_addr : natural -> string
type index = int

index into instruction-indexed arrays

val measure_time : bool
val time : string -> ('a -> 'b) -> 'a -> 'b

Print the time this function call took. The string is just for the printed message

val read_file_lines : string -> (string array, string) Stdlib.result

Read all lines of a file

val html_escape : string -> string

escape HTML

val esc : Types.ppmode -> string -> string
val sys_command : string -> unit
\ No newline at end of file +Utils (read-dwarf.Analyse.Utils)

Module Analyse.Utils

Miscellaneous types and utility functions used throughout the analyse code

type natural = Utils.Sym.t

TODO: Maybe just use Z.t everywhere (it's shorter)

type addr = natural

machine address

val pp_addr : natural -> string
type index = int

index into instruction-indexed arrays

val measure_time : bool
val time : string -> ('a -> 'b) -> 'a -> 'b

Print the time this function call took. The string is just for the printed message

val read_file_lines : string -> (string array, string) Stdlib.result

Read all lines of a file

val html_escape : string -> string

escape HTML

val esc : Types.ppmode -> string -> string
val sys_command : string -> unit
diff --git a/doc/html/read-dwarf/Analyse/index.html b/doc/html/read-dwarf/Analyse/index.html index 01080a89..adcfd724 100644 --- a/doc/html/read-dwarf/Analyse/index.html +++ b/doc/html/read-dwarf/Analyse/index.html @@ -1,2 +1,2 @@ -Analyse (read-dwarf.Analyse)

Module Analyse

include Base
val process_file : unit -> unit
module Base : sig ... end
module Collected : sig ... end

collect the various test analysis data

module DwarfLineInfo : sig ... end

post-process DWARF source line info

module Elf : sig ... end
module ElfTypes : sig ... end

type of collected ELF-file data from linksem

module Globals : sig ... end
module Pp : sig ... end
module Types : sig ... end

types shared between analyse* and the read-dwarf top-level

module Utils : sig ... end

Miscellaneous types and utility functions used throughout the analyse code

\ No newline at end of file +Analyse (read-dwarf.Analyse)

Module Analyse

include module type of struct include Base end
val process_file : unit -> unit
module Base : sig ... end
module CallGraph : sig ... end

compute call-graph

module Collected : sig ... end

collect the various test analysis data

module DwarfLineInfo : sig ... end

post-process DWARF source line info

module Elf : sig ... end

pp symbol map

module ElfTypes : sig ... end

type of collected ELF-file data from linksem

module Globals : sig ... end
module Pp : sig ... end

render collected analysis data to text or css

module Types : sig ... end

types shared between analyse* and the read-dwarf top-level

module Utils : sig ... end

Miscellaneous types and utility functions used throughout the analyse code

diff --git a/doc/html/read-dwarf/Arch/index.html b/doc/html/read-dwarf/Arch/index.html index 41a73a6b..5a3f86de 100644 --- a/doc/html/read-dwarf/Arch/index.html +++ b/doc/html/read-dwarf/Arch/index.html @@ -1,2 +1,2 @@ -Arch (read-dwarf.Arch)

Module Arch

This module adds some code that is related to the Architecture specific modules but is in itself architecture independent.

include Sig
type func_api = {
args : Ctype.t list;
ret : Ctype.t option;
}

Describe the C API of a function

type func_abi = {
init : State.t -> State.t;

Gives the initial state for verifying the function, from a given global register state. Only global registers are kept.

}

Describe the ABI of a function

This is a record because I expect to add many other fields later.

type dwarf_reg_map = State.Reg.t array

The map of dwarf register: Which register number map to which ISA register

val supports : Config.Arch.t -> bool

Tells if this Arch module supports this architecture

val init : Config.Arch.t -> unit

If this arch module supports the architecture, then initialize read-dwarf state using this architecture

val initialized : unit -> Config.Arch.t option

Return Some(arch) is the loaded arch is arch and None if nothing is loaded yet.

val module_name : string

The name of the arch module. Must be the name of the module i.e. Config.arch_module

val loaded_name : string

For dynamic arch module, the name of the dynamically loaded module. Otherwise module_name

val address_size : int

The true size of addresses for memory operation

val dwarf_reg_map : unit -> dwarf_reg_map

Get the register map of the architecture

val is_local : State.Reg.t -> bool

Tell if a register is local for the ABI

val nop : unit -> Utils.BytesSeq.t

Give the opcode of the nop instruction (For Sail/Isla initialisation

val get_abi : func_api -> func_abi

Give the ABI of a function from it's C API

val pc : unit -> State.Reg.t

Give the register index for the program counter

val sp : unit -> State.Reg.t

Give the register index for the stack pointer

val assemble_to_elf : string -> string

Take an instruction string and give the name of an temporary ELF file created that contains the instruction at symbol instr.

val split_into_instrs : Utils.BytesSeq.t -> Utils.BytesSeq.t list

Split a byte-sequence into a list of instructions.

val is_ret : Utils.BytesSeq.t -> bool

Tell if an instruction is a return instruction.

val is_cmp : Utils.BytesSeq.t -> (State.Reg.t * Utils.BitVec.t) option

Tell if an instruction is a compare instruction. Returns Some (reg,bv) where the contents of reg are compared against the value bv if it is and None if not.

val is_bl : Utils.BytesSeq.t -> Utils.BitVec.t option

Tell if an instruction is an (unconditional) branch on immediate with link instructions. Returns Some bv where bv is the offset (from the address of this instruction, in the range +/-128MB) that is branched to if it is and None if not.

val ensure_loaded : Config.Arch.t -> unit

Ensure that the right architecture type is loaded

val pp_api : func_api -> Utils.Pp.document
val get : unit -> Config.Arch.t

Get the initialized architecture type. Fails (Failure) if not architecture was loaded

val get_config : unit -> Config.File.ArchConf.t

Get the configuration for the initialized architecture

val get_isla_config : unit -> Config.File.ArchConf.Isla.t

Get the Isla configuration for the initialized architecture

val load_elf_arch : Elf.File.t -> unit

Load the architecture of this File

\ No newline at end of file +Arch (read-dwarf.Arch)

Module Arch

This module adds some code that is related to the Architecture specific modules but is in itself architecture independent.

include module type of struct include Sig end
type func_api = Sig.func_api = {
  1. args : Ctype.t list;
  2. ret : Ctype.t option;
}

Describe the C API of a function

type func_abi = Sig.func_abi = {
  1. init : State.t -> State.t;
    (*

    Gives the initial state for verifying the function, from a given global register state. Only global registers are kept.

    *)
}

Describe the ABI of a function

This is a record because I expect to add many other fields later.

type dwarf_reg_map = State.Reg.t array

The map of dwarf register: Which register number map to which ISA register

val supports : Config.Arch.t -> bool

Tells if this Arch module supports this architecture

val init : Config.Arch.t -> unit

If this arch module supports the architecture, then initialize read-dwarf state using this architecture

val initialized : unit -> Config.Arch.t option

Return Some(arch) is the loaded arch is arch and None if nothing is loaded yet.

val module_name : string

The name of the arch module. Must be the name of the module i.e. Config.arch_module

val loaded_name : string

For dynamic arch module, the name of the dynamically loaded module. Otherwise module_name

val address_size : int

The true size of addresses for memory operation

val dwarf_reg_map : unit -> dwarf_reg_map

Get the register map of the architecture

val is_local : State.Reg.t -> bool

Tell if a register is local for the ABI

val nop : unit -> Utils.BytesSeq.t

Give the opcode of the nop instruction (For Sail/Isla initialisation

val get_abi : func_api -> func_abi

Give the ABI of a function from it's C API

val pc : unit -> State.Reg.t

Give the register index for the program counter

val sp : unit -> State.Reg.t

Give the register index for the stack pointer

val assemble_to_elf : string -> string

Take an instruction string and give the name of an temporary ELF file created that contains the instruction at symbol instr.

val split_into_instrs : Elf.Symbol.data -> Elf.Symbol.data list

Split a byte-sequence into a list of instructions.

val is_ret : Utils.BytesSeq.t -> bool

Tell if an instruction is a return instruction.

val is_cmp : Utils.BytesSeq.t -> (State.Reg.t * Utils.BitVec.t) option

Tell if an instruction is a compare instruction. Returns Some (reg,bv) where the contents of reg are compared against the value bv if it is and None if not.

val is_bl : Utils.BytesSeq.t -> Utils.BitVec.t option

Tell if an instruction is an (unconditional) branch on immediate with link instructions. Returns Some bv where bv is the offset (from the address of this instruction, in the range +/-128MB) that is branched to if it is and None if not.

val ensure_loaded : Config.Arch.t -> unit

Ensure that the right architecture type is loaded

val pp_api : func_api -> Utils.Pp.document
val get : unit -> Config.Arch.t

Get the initialized architecture type. Fails (Failure) if not architecture was loaded

val get_config : unit -> Config.File.ArchConf.t

Get the configuration for the initialized architecture

val get_isla_config : unit -> Config.File.ArchConf.Isla.t

Get the Isla configuration for the initialized architecture

val load_elf_arch : Elf.File.t -> unit

Load the architecture of this Elf.File

diff --git a/doc/html/read-dwarf/Architecture.html b/doc/html/read-dwarf/Architecture.html index 21f73096..88dcf8f5 100644 --- a/doc/html/read-dwarf/Architecture.html +++ b/doc/html/read-dwarf/Architecture.html @@ -1,2 +1,2 @@ -Architecture (read-dwarf.Architecture)

Architecture

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.

Architecture as a Virtual Module

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).

Architectural features

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.

Representation of the architectural state

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

\ No newline at end of file +Architecture (read-dwarf.Architecture)

Architecture

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.

Architecture as a Virtual Module

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).

Architectural features

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.

Representation of the architectural state

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

diff --git a/doc/html/read-dwarf/Ast/Base/Size/index.html b/doc/html/read-dwarf/Ast/Base/Size/index.html index a230760a..6bfe302a 100644 --- a/doc/html/read-dwarf/Ast/Base/Size/index.html +++ b/doc/html/read-dwarf/Ast/Base/Size/index.html @@ -1,2 +1,2 @@ -Size (read-dwarf.Ast.Base.Size)

Module Base.Size

include AstGen.Def.Size
type t =
| B8
| B16
| B32
| B64

The possible sizes for memory accesses. It may be necessary to add B128 at some point

val of_bytes : int -> t

Create a size value from a valid size in byte

val of_bits : int -> t

Create a size value from a valid size in bits

val to_bytes : t -> int

Get the byte size corresponding to that value

val to_bits : t -> int

Get the bits size corresponding to that value

val equal : 'a -> 'a -> bool
val pp_bytes : t -> Utils.Pp.document

Pretty-print a size as just the byte number

val pp_bits : t -> PPrintEngine.document

Pretty print a size at "16bits" for example

val to_bv : t -> 'a ty
\ No newline at end of file +Size (read-dwarf.Ast.Base.Size)

Module Base.Size

include module type of struct include AstGen.Def.Size end
type t = AstGen.Def.Size.t =
  1. | B8
  2. | B16
  3. | B32
  4. | B64
  5. | B128

The possible sizes for memory accesses.

val of_bytes : int -> t

Create a size value from a valid size in byte

val of_bits : int -> t

Create a size value from a valid size in bits

val to_bytes : t -> int

Get the byte size corresponding to that value

val to_bits : t -> int

Get the bits size corresponding to that value

val equal : 'a -> 'a -> bool
val pp_bytes : t -> Utils.Pp.document

Pretty-print a size as just the byte number

val pp_bits : t -> Utils.Pp.document

Pretty print a size at "16bits" for example

val to_bv : t -> 'a ty
diff --git a/doc/html/read-dwarf/Ast/Base/index.html b/doc/html/read-dwarf/Ast/Base/index.html index 76ecf51c..0c9483d0 100644 --- a/doc/html/read-dwarf/Ast/Base/index.html +++ b/doc/html/read-dwarf/Ast/Base/index.html @@ -1,7 +1,58 @@ -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 AstGen.Def
include AstGen.Ott
type bvar = string

syntax

type flag = string
type enum = int * int
type 'm binmem =
| Select of 'm
| Store of 'm
type bvarith =
| Bvuremi
| Bvsremi
| Bvsmodi
| Bvnand
| Bvnor
| Bvxnor
| Bvsub
| Bvudiv
| Bvudivi
| Bvsdiv
| Bvsdivi
| Bvurem
| Bvsrem
| Bvsmod
| Bvshl
| Bvlshr
| Bvashr
type bvcomp =
| Bvult
| Bvslt
| Bvule
| Bvsle
| Bvuge
| Bvsge
| Bvugt
| Bvsgt
type bvmanyarith =
| Bvand
| Bvor
| Bvxor
| Bvadd
| Bvmul
type unop =
| Not
| Bvnot
| Bvredand
| Bvredor
| Bvneg
| Extract of int * int
| ZeroExtend of int
| SignExtend of int
type 'm binop =
| Binmem of 'm binmem
| Eq
| Bvarith of bvarith
| Bvcomp of bvcomp
type manyop =
| And
| Or
| Bvmanyarith of bvmanyarith
| Concat
type ('a, 'v, 'b, 'm) exp =
| Var of 'v * 'a
| Bound of 'b * 'a
| Bits of Utils.BitVec.t * 'a
| Bool of bool * 'a
| Enum of enum * 'a
| Unop of unop * ('a'v'b'm) exp * 'a
| Binop of 'm binop * ('a'v'b'm) exp * ('a'v'b'm) exp * 'a
| Manyop of manyop * ('a'v'b'm) exp list * 'a
| Vec of ('a'v'b'm) exp list * 'a
| Ite of ('a'v'b'm) exp * ('a'v'b'm) exp * ('a'v'b'm) exp * 'a
| Let of 'b * ('a'v'b'm) exp * ('b * ('a'v'b'm) exp) list * ('a'v'b'm) exp * 'a
type 'm ty =
| Ty_Mem of 'm
| Ty_Bool
| Ty_BitVec of int
| Ty_Enum of int
| Ty_Array of 'm ty * 'm ty
type ('a, 'v, 'b, 'm) smt =
| DeclareConst of 'v * 'm ty
| DefineConst of 'v * ('a'v'b'm) exp
| Assert of ('a'v'b'm) exp
| Simplify of ('a'v'b'm) exp * (string * bool) list
| Push
| Pop
| GetVersion
| CheckSat
| Exit
type ('a, 'v, 'b, 'm) smt_ans =
| Error of string
| Version of string
| Sat
| Unsat
| Unknown
| Unsupported
| Exp of ('a'v'b'm) exp
type no =

Generic empty type. This kind of type is explained here. In particular, when this type appear in a match case, the match case can contain a refutation pattern . to indicate that this impossible.

A refutation pattern also work with higher-level constructors, for example in this case:

type a = A of int | B of no * int
+Base (read-dwarf.Ast.Base)

Module Ast.Base

The main module to use the AST of expression and SMT operation for a more generic overview of the AST, see SymbolicExpressions.

include module type of struct include AstGen.Def end
include module type of struct include AstGen.Ott end
type bvar = string

syntax

type flag = string
type enum = int * int
type bvmanyarith = AstGen.Ott.bvmanyarith =
  1. | Bvand
  2. | Bvor
  3. | Bvxor
  4. | Bvadd
  5. | Bvmul
type 'm binmem = 'm AstGen.Ott.binmem =
  1. | Select of 'm
  2. | Store of 'm
type bvcomp = AstGen.Ott.bvcomp =
  1. | Bvult
  2. | Bvslt
  3. | Bvule
  4. | Bvsle
  5. | Bvuge
  6. | Bvsge
  7. | Bvugt
  8. | Bvsgt
type bvarith = AstGen.Ott.bvarith =
  1. | Bvuremi
  2. | Bvsremi
  3. | Bvsmodi
  4. | Bvnand
  5. | Bvnor
  6. | Bvxnor
  7. | Bvsub
  8. | Bvudiv
  9. | Bvudivi
  10. | Bvsdiv
  11. | Bvsdivi
  12. | Bvurem
  13. | Bvsrem
  14. | Bvsmod
  15. | Bvshl
  16. | Bvlshr
  17. | Bvashr
type manyop = AstGen.Ott.manyop =
  1. | And
  2. | Or
  3. | Bvmanyarith of bvmanyarith
  4. | Concat
type 'm binop = 'm AstGen.Ott.binop =
  1. | Binmem of 'm binmem
  2. | Eq
  3. | Bvarith of bvarith
  4. | Bvcomp of bvcomp
type unop = AstGen.Ott.unop =
  1. | Not
  2. | Bvnot
  3. | Bvredand
  4. | Bvredor
  5. | Bvneg
  6. | Extract of int * int
  7. | ZeroExtend of int
  8. | SignExtend of int
type ('a, 'v, 'b, 'm) exp = ('a, 'v, 'b, 'm) AstGen.Ott.exp =
  1. | Var of 'v * 'a
  2. | Bound of 'b * 'a
  3. | Bits of Utils.BitVec.t * 'a
  4. | Bool of bool * 'a
  5. | Enum of enum * 'a
  6. | Unop of unop * ('a, 'v, 'b, 'm) exp * 'a
  7. | Binop of 'm binop * ('a, 'v, 'b, 'm) exp * ('a, 'v, 'b, 'm) exp * 'a
  8. | Manyop of manyop * ('a, 'v, 'b, 'm) exp list * 'a
  9. | Vec of ('a, 'v, 'b, 'm) exp list * 'a
  10. | Ite of ('a, 'v, 'b, 'm) exp * ('a, 'v, 'b, 'm) exp * ('a, 'v, 'b, 'm) exp * 'a
  11. | 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 =
  1. | Ty_Mem of 'm
  2. | Ty_Bool
  3. | Ty_BitVec of int
  4. | Ty_Enum of int
  5. | Ty_Array of 'm ty * 'm ty
type ('a, 'v, 'b, 'm) smt = ('a, 'v, 'b, 'm) AstGen.Ott.smt =
  1. | DeclareConst of 'v * 'm ty
  2. | DefineConst of 'v * ('a, 'v, 'b, 'm) exp
  3. | Assert of ('a, 'v, 'b, 'm) exp
  4. | Simplify of ('a, 'v, 'b, 'm) exp * (string * bool) list
  5. | Push
  6. | Pop
  7. | GetVersion
  8. | CheckSat
  9. | Exit
type ('a, 'v, 'b, 'm) smt_ans = ('a, 'v, 'b, 'm) AstGen.Ott.smt_ans =
  1. | Error of string
  2. | Version of string
  3. | Sat
  4. | Unsat
  5. | Unknown
  6. | Unsupported
  7. | 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 loc = Stdlib.Lexing.position

Lexing.position

type lrng = Isla_lang.AST.lrng

The type of an expression range. This imported from Isla to avoid having two incompatible types.

This represent a range in a source file, so generally this is a pair of loc, but it may also be Unknown.

module Size = AstGen.Def.Size
type rexp = (lrng, string, string, Size.t) exp

Raw expression coming out of the parser

type rty = Size.t ty

Raw type coming out of the parser

type rsmt = (lrng, string, string, Size.t) smt

Raw SMT command coming out of the parser

type rsmt_ans = (lrng, string, string, Size.t) smt_ans

Raw SMT answer coming out of the parser

Size

module Size : sig ... end

Parsing

module Parser = AstGen.Parser
module Lexer = AstGen.Lexer
val unknown : Isla_lang.AST.lrng
val loc : Stdlib.Lexing.position -> PPrint.document
exception ParseError of loc * string

Exception that represent an Isla parsing error

exception LexError of loc * string

Exception that represent an Isla lexing error

type lexbuf = Stdlib.Lexing.lexbuf
type lexer = lexbuf -> Parser.token
type 'a parser = lexer -> lexbuf -> 'a
val parse : 'a parser -> ('b -> lexbuf) -> ?⁠filename:string -> 'b -> 'a

Parse a single Isla instruction output from a Lexing.lexbuf

val from_string : string -> Stdlib.Lexing.lexbuf
val from_channel : Stdlib.in_channel -> Stdlib.Lexing.lexbuf
val parse_exp_string : ?⁠filename:string -> string -> (AstGen.Def.lrng, string, string, AstGen.Def.Size.t) AstGen.Ott.exp

Parse a single Isla expression from a string

val parse_exp_channel : ?⁠filename:string -> Stdlib.in_channel -> (AstGen.Def.lrng, string, string, AstGen.Def.Size.t) AstGen.Ott.exp

Parse a single Isla expression from a channel

val parse_smt_ans_string : ?⁠filename:string -> string -> AstGen.Def.rsmt_ans

Parse a single Isla expression from a string

val parse_smt_ans_channel : ?⁠filename:string -> Stdlib.in_channel -> AstGen.Def.rsmt_ans

Parse a single Isla expression from a channel

include AstGen.Parser_pp
val pp_raw_bvar : string -> PPrintEngine.document
val pp_raw_bvf : Utils.BitVec.t -> PPrintEngine.document
val pp_raw_flag : string -> PPrintEngine.document
val pp_raw_vvar : int -> PPrintEngine.document
val pp_raw_name : string -> PPrintEngine.document
val pp_raw_enum_ty : int -> PPrintEngine.document
val pp_raw_enum : AstGen.Ott.enum -> PPrintEngine.document
val pp_raw_int : int -> PPrintEngine.document
val pp_raw_bvi : int -> PPrintEngine.document
val pp_raw_bv : string -> PPrintEngine.document
val pp_raw_str : string -> PPrintEngine.document
val pp_raw_mem_size : AstGen.Def.Size.t -> Utils.Pp.document
val pp_raw_binmem : AstGen.Def.Size.t AstGen.Ott.binmem -> PPrintEngine.document
val pp_raw_var : ('a -> PPrintEngine.document) -> 'a -> PPrintEngine.document
val pp_raw_bbvar : string -> PPrintEngine.document
val pp_raw_bind : ('a -> PPrintEngine.document) -> (string * ('b'a, string, AstGen.Def.Size.t) AstGen.Ott.exp) -> PPrintEngine.document
val pp_raw_exp : ('a -> PPrintEngine.document) -> ('b'a, string, AstGen.Def.Size.t) AstGen.Ott.exp -> PPrintEngine.document
val pp_raw_fbool : (string * bool) -> PPrintEngine.document
val pp_raw_smt : ('a -> PPrintEngine.document) -> ('b'a, string, AstGen.Def.Size.t) AstGen.Ott.smt -> PPrintEngine.document
val pp_raw_smts : ('a -> PPrintEngine.document) -> ('b'a, string, AstGen.Def.Size.t) AstGen.Ott.smt list -> PPrintEngine.document
val pp_raw_smt_ans : ('a -> PPrintEngine.document) -> ('b'a, string, AstGen.Def.Size.t) AstGen.Ott.smt_ans -> PPrintEngine.document
val pp_raw_ty : AstGen.Def.Size.t AstGen.Ott.ty -> PPrintEngine.document
val pp_raw_bool : bool -> PPrintEngine.document
val pp_raw_unop : AstGen.Ott.unop -> PPrintEngine.document
val pp_raw_bvarith : AstGen.Ott.bvarith -> PPrintEngine.document
val pp_raw_bvcomp : AstGen.Ott.bvcomp -> PPrintEngine.document
val pp_raw_binop : AstGen.Def.Size.t AstGen.Ott.binop -> PPrintEngine.document
val pp_raw_bvmanyarith : AstGen.Ott.bvmanyarith -> PPrintEngine.document
val pp_raw_manyop : AstGen.Ott.manyop -> PPrintEngine.document
val pp_bvar : string -> PPrintEngine.document
val pp_bvf : Utils.BitVec.t -> PPrintEngine.document
val pp_flag : string -> PPrintEngine.document
val pp_vvar : int -> PPrintEngine.document
val pp_name : string -> PPrintEngine.document
val pp_enum_ty : int -> PPrintEngine.document
val pp_enum : AstGen.Ott.enum -> PPrintEngine.document
val pp_int : int -> PPrintEngine.document
val pp_bvi : int -> PPrintEngine.document
val pp_bv : string -> PPrintEngine.document
val pp_str : string -> PPrintEngine.document
val pp_j : int -> string
val pp_mem_size : AstGen.Def.Size.t -> Utils.Pp.document
val pp_binmem : AstGen.Def.Size.t AstGen.Ott.binmem -> PPrintEngine.document
val pp_var : ('a -> PPrintEngine.document) -> 'a -> PPrintEngine.document
val pp_bbvar : string -> PPrintEngine.document
val pp_bind : ('a -> PPrintEngine.document) -> (string * ('b'a, string, AstGen.Def.Size.t) AstGen.Ott.exp) -> PPrintEngine.document
val pp_exp : ('a -> PPrintEngine.document) -> ('b'a, string, AstGen.Def.Size.t) AstGen.Ott.exp -> PPrintEngine.document
val pp_fbool : (string * bool) -> PPrintEngine.document
val pp_smt : ('a -> PPrintEngine.document) -> ('b'a, string, AstGen.Def.Size.t) AstGen.Ott.smt -> PPrintEngine.document
val pp_smts : ('a -> PPrintEngine.document) -> ('b'a, string, AstGen.Def.Size.t) AstGen.Ott.smt list -> PPrintEngine.document
val pp_smt_ans : ('a -> PPrintEngine.document) -> ('b'a, string, AstGen.Def.Size.t) AstGen.Ott.smt_ans -> PPrintEngine.document
val pp_ty : AstGen.Def.Size.t AstGen.Ott.ty -> PPrintEngine.document
val pp_bool : bool -> PPrintEngine.document
val pp_unop : AstGen.Ott.unop -> PPrintEngine.document
val pp_bvarith : AstGen.Ott.bvarith -> PPrintEngine.document
val pp_bvcomp : AstGen.Ott.bvcomp -> PPrintEngine.document
val pp_binop : AstGen.Def.Size.t AstGen.Ott.binop -> PPrintEngine.document
val pp_bvmanyarith : AstGen.Ott.bvmanyarith -> PPrintEngine.document
val pp_manyop : AstGen.Ott.manyop -> PPrintEngine.document
val pp_loc : Stdlib.Lexing.position -> PPrint.document

Prints a lexing location

val pp_lrng : Isla_lang.AST.lrng -> PPrint.document

Prints a lexing range

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.

val destr_binmem : no binmem -> 'a

Expectors

Functions to assert that a specific constructor is used and get the value

val expect_bits : ('a'b'c'd) exp -> Utils.BitVec.t
val ty_expect_bv : 'a ty -> int

Construtors

val assert_smt : ('a'b'c'd) exp -> ('a'b'c'd) smt
val simplify_smt : ?⁠flags:(string * bool) list -> ('a'b'c'd) exp -> ('a'b'c'd) smt

Comparisons

val equal_exp : ?⁠annot:('a -> 'a -> bool) -> var:('b -> 'b -> bool) -> ?⁠bnd:('c -> 'c -> bool) -> ('a'b'c'd) exp -> ('a'b'c'd) exp -> bool

Equality for expressions; default polymorphic equality will fail.

\ 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.

type loc = Stdlib.Lexing.position
type lrng = Isla_lang.AST.lrng

The type of an expression range. This imported from Isla to avoid having two incompatible types.

This represent a range in a source file, so generally this is a pair of loc, but it may also be Unknown.

type rexp = (lrng, string, string, AstGen.Def.Size.t) exp

Raw expression coming out of the parser

Raw type coming out of the parser

type rsmt = (lrng, string, string, AstGen.Def.Size.t) smt

Raw SMT command coming out of the parser

type rsmt_ans = (lrng, string, string, AstGen.Def.Size.t) smt_ans

Raw SMT answer coming out of the parser

Size

module Size : sig ... end

Parsing

module Parser = AstGen.Parser
module Lexer = AstGen.Lexer
val unknown : Isla_lang.AST.lrng
val loc : Stdlib.Lexing.position -> PPrint.document
exception ParseError of loc * string

Exception that represent an Isla parsing error

exception LexError of loc * string

Exception that represent an Isla lexing error

type lexbuf = Stdlib.Lexing.lexbuf
type lexer = lexbuf -> Parser.token
type 'a parser = lexer -> lexbuf -> 'a
val parse : 'a parser -> ('b -> lexbuf) -> ?filename:string -> 'b -> 'a

Parse a single Isla instruction output from a Lexing.lexbuf

val from_string : string -> Stdlib.Lexing.lexbuf
val from_channel : Stdlib.in_channel -> Stdlib.Lexing.lexbuf
val parse_exp_string : + ?filename:string -> + string -> + (AstGen.Def.lrng, string, string, AstGen.Def.Size.t) AstGen.Ott.exp

Parse a single Isla expression from a string

val parse_exp_channel : + ?filename:string -> + Stdlib.in_channel -> + (AstGen.Def.lrng, string, string, AstGen.Def.Size.t) AstGen.Ott.exp

Parse a single Isla expression from a channel

val parse_smt_ans_string : ?filename:string -> string -> AstGen.Def.rsmt_ans

Parse a single Isla expression from a string

val parse_smt_ans_channel : + ?filename:string -> + Stdlib.in_channel -> + AstGen.Def.rsmt_ans

Parse a single Isla expression from a channel

include module type of struct include AstGen.Parser_pp end
val pp_raw_bvar : string -> PPrint.document
val pp_raw_bvf : Utils.BitVec.t -> PPrint.document
val pp_raw_flag : string -> PPrint.document
val pp_raw_vvar : int -> PPrint.document
val pp_raw_name : string -> PPrint.document
val pp_raw_enum_ty : int -> PPrint.document
val pp_raw_enum : AstGen.Ott.enum -> PPrint.document
val pp_raw_nat : int -> PPrint.document
val pp_raw_bvi : int -> PPrint.document
val pp_raw_bv : string -> PPrint.document
val pp_raw_str : string -> PPrint.document
val pp_raw_mem_size : AstGen.Def.Size.t -> Utils.Pp.document
val pp_raw_binmem : AstGen.Def.Size.t AstGen.Ott.binmem -> PPrint.document
val pp_raw_var : ('a -> PPrint.document) -> 'a -> PPrint.document
val pp_raw_bbvar : string -> PPrint.document
val pp_raw_bind : + ('a -> PPrint.document) -> + (string * ('b, 'a, string, AstGen.Def.Size.t) AstGen.Ott.exp) -> + PPrint.document
val pp_raw_exp : + ('a -> PPrint.document) -> + ('b, 'a, string, AstGen.Def.Size.t) AstGen.Ott.exp -> + PPrint.document
val pp_raw_fbool : (string * bool) -> PPrint.document
val pp_raw_smt : + ('a -> PPrint.document) -> + ('b, 'a, string, AstGen.Def.Size.t) AstGen.Ott.smt -> + PPrint.document
val pp_raw_smts : + ('a -> PPrint.document) -> + ('b, 'a, string, AstGen.Def.Size.t) AstGen.Ott.smt list -> + PPrint.document
val pp_raw_smt_ans : + ('a -> PPrint.document) -> + ('b, 'a, string, AstGen.Def.Size.t) AstGen.Ott.smt_ans -> + PPrint.document
val pp_raw_int : int -> PPrint.document
val pp_raw_ty : AstGen.Def.Size.t AstGen.Ott.ty -> PPrint.document
val pp_raw_bool : bool -> PPrint.document
val pp_raw_unop : AstGen.Ott.unop -> PPrint.document
val pp_raw_bvarith : AstGen.Ott.bvarith -> PPrint.document
val pp_raw_bvcomp : AstGen.Ott.bvcomp -> PPrint.document
val pp_raw_binop : AstGen.Def.Size.t AstGen.Ott.binop -> PPrint.document
val pp_raw_bvmanyarith : AstGen.Ott.bvmanyarith -> PPrint.document
val pp_raw_manyop : AstGen.Ott.manyop -> PPrint.document
val pp_bvar : string -> PPrint.document
val pp_bvf : Utils.BitVec.t -> PPrint.document
val pp_flag : string -> PPrint.document
val pp_vvar : int -> PPrint.document
val pp_name : string -> PPrint.document
val pp_enum_ty : int -> PPrint.document
val pp_enum : AstGen.Ott.enum -> PPrint.document
val pp_nat : int -> PPrint.document
val pp_bvi : int -> PPrint.document
val pp_bv : string -> PPrint.document
val pp_str : string -> PPrint.document
val pp_j : int -> string
val pp_binmem : AstGen.Def.Size.t AstGen.Ott.binmem -> PPrint.document
val pp_var : ('a -> PPrint.document) -> 'a -> PPrint.document
val pp_bbvar : string -> PPrint.document
val pp_bind : + ('a -> PPrint.document) -> + (string * ('b, 'a, string, AstGen.Def.Size.t) AstGen.Ott.exp) -> + PPrint.document
val pp_exp : + ('a -> PPrint.document) -> + ('b, 'a, string, AstGen.Def.Size.t) AstGen.Ott.exp -> + PPrint.document
val pp_fbool : (string * bool) -> PPrint.document
val pp_smt : + ('a -> PPrint.document) -> + ('b, 'a, string, AstGen.Def.Size.t) AstGen.Ott.smt -> + PPrint.document
val pp_smts : + ('a -> PPrint.document) -> + ('b, 'a, string, AstGen.Def.Size.t) AstGen.Ott.smt list -> + PPrint.document
val pp_smt_ans : + ('a -> PPrint.document) -> + ('b, 'a, string, AstGen.Def.Size.t) AstGen.Ott.smt_ans -> + PPrint.document
val pp_int : int -> PPrint.document
val pp_ty : AstGen.Def.Size.t AstGen.Ott.ty -> PPrint.document
val pp_bool : bool -> PPrint.document
val pp_unop : AstGen.Ott.unop -> PPrint.document
val pp_bvarith : AstGen.Ott.bvarith -> PPrint.document
val pp_bvcomp : AstGen.Ott.bvcomp -> PPrint.document
val pp_binop : AstGen.Def.Size.t AstGen.Ott.binop -> PPrint.document
val pp_bvmanyarith : AstGen.Ott.bvmanyarith -> PPrint.document
val pp_manyop : AstGen.Ott.manyop -> PPrint.document
val pp_loc : Stdlib.Lexing.position -> PPrint.document

Prints a lexing location

val pp_lrng : Isla_lang.AST.lrng -> PPrint.document

Prints a lexing range

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.

val destr_binmem : no binmem -> 'a

Expectors

Functions to assert that a specific constructor is used and get the value

val expect_bits : ('a, 'b, 'c, 'd) exp -> Utils.BitVec.t
val ty_expect_bv : 'a ty -> int

Construtors

val assert_smt : ('a, 'b, 'c, 'd) exp -> ('a, 'b, 'c, 'd) smt
val simplify_smt : + ?flags:(string * bool) list -> + ('a, 'b, 'c, 'd) exp -> + ('a, 'b, 'c, 'd) smt

Comparisons

val equal_exp : + ?annot:('a -> 'a -> bool) -> + var:('b -> 'b -> bool) -> + ?bnd:('c -> 'c -> bool) -> + ('a, 'b, 'c, 'd) exp -> + ('a, 'b, 'c, 'd) exp -> + bool

Equality for expressions; default polymorphic equality will fail.

diff --git a/doc/html/read-dwarf/Ast/Manip/index.html b/doc/html/read-dwarf/Ast/Manip/index.html index c3d353bf..66f484a8 100644 --- a/doc/html/read-dwarf/Ast/Manip/index.html +++ b/doc/html/read-dwarf/Ast/Manip/index.html @@ -1,2 +1,35 @@ -Manip (read-dwarf.Ast.Manip)

Module Ast.Manip

This module provide generic facilities of expression and SMT statements provided by Ast. It is intended to only provide syntactic facilities over Ast types, in particular Ast.exp.

In particular it provides generic mapping and iteration function over expressions as well a function allowing to convert between the various Expression type parameter and options.

Warning: due to OCaml type system limitations, mainly issue #9456, this module is sometimes required to use Obj.magic in some specific cases. No other module should ever do that. If you need to use Obj.magic to bypass OCaml type system limitation about Ast type, add a function here instead.

Get annotations

val annot_exp : ('a'v'b'm) Base.exp -> 'a

Get the annotation of an expression

TODO: This would be much more efficient if the annotation was always the first member of the constructor and not the last (in that case the offset to fetch the annotation do not depend on the constructor index). This may require to modify ott.

Non-recursive maps and iters

This section is filled on demand.

direct_a_map_b take a function b -> b and applies it to all b in a, non-recursively. Then a new a with the same structure is formed.

direct_a_iter_b take a function b -> unit and applies it to all b in a, non-recursively.

val direct_exp_map_exp : (('a'v'b'm) Base.exp -> ('a'v'b'm) Base.exp) -> ('a'v'b'm) Base.exp -> ('a'v'b'm) Base.exp
val direct_exp_iter_exp : (('a'v'b'm) Base.exp -> unit) -> ('a'v'b'm) Base.exp -> unit
val direct_exp_fold_left_exp : ('a -> ('b'c'd'e) Base.exp -> 'a) -> 'a -> ('b'c'd'e) Base.exp -> 'a
val direct_exp_for_all_exp : (('a'b'c'd) Base.exp -> bool) -> ('a'b'c'd) Base.exp -> bool
val direct_exp_exists_exp : (('a'b'c'd) Base.exp -> bool) -> ('a'b'c'd) Base.exp -> bool

Recursive maps and iters

This section is filled on demand.

a_map_b take a function b -> b and applies it to all the b in a, and do that recursively on all b that appear directly or indirectly in a

a_iter_b take a function b -> unit and applies it to all the b in a, and do that recursively

Doing this when a = b is not well defined, and can be easily done using the direct version from previous section.

val exp_iter_var : ('v -> unit) -> ('a'v'b'm) Base.exp -> unit

Iterate a function on all the variable of an expression

val exp_map_var : va vb a b m. ('va -> 'vb) -> ('a'va'b'm) Base.exp -> ('a'vb'b'm) Base.exp
val exp_iter_annot : ('a -> unit) -> ('a'v'b'm) Base.exp -> unit

Iterate a function on all the annotations of an expression

Variable type conversion

All of those function convert the underlying variable type through the AST. They cannot be the usual map function because they change the type

val exp_conv_var : ('a -> 'b) -> ('c'a'd'e) Base.exp -> ('c'b'd'e) Base.exp

Old alias to make conversion explicit

val exp_var_subst : va a vb b m. ('va -> 'a -> ('a'vb'b'm) Base.exp) -> ('a'va'b'm) Base.exp -> ('a'vb'b'm) Base.exp

Substitute variable with expression according to substitution function

Bound variables and let-bindings

This section allow to go from expression without let-bindings to expression with them and vice-versa.

val allow_lets : ('a'vBase.no'm) Base.exp -> ('a'v'b'm) Base.exp

Allow bound variables and lets in an expression. This operation is a no-op and has no runtime cost, it's just a type change.

val smt_allow_lets : ('a'vBase.no'm) Base.smt -> ('a'v'b'm) Base.smt

Same as allow_lets but for the smt type

val unfold_lets : b1 a v b2 m. ?⁠context:('b1('a'v'b2'm) Base.exp) Stdlib.Hashtbl.t -> ('a'v'b1'm) Base.exp -> ('a'v'b2'm) Base.exp

Unfold all lets. There are no remaining lets in the output, Therefore the output type of let binding can be anything including Ast.no. In particular doing allow_lets after this function is useless

Memory operation

This section allow to go from expression without memory operations to expression with them and vice-versa.

val allow_mem : ('a'v'bBase.no) Base.exp -> ('a'v'b'm) Base.exp

Allow memory operations in an expression.

val smt_allow_mem : ('a'v'bBase.no) Base.smt -> ('a'v'b'm) Base.smt

Same as allow_mem but for the smt type

val ty_allow_mem : Base.no Base.ty -> 'm Base.ty

Same as allow_mem but for the ty type

val check_no_mem : ('a'v'b'm) Base.exp -> bool

Check that not memory operation take place in that expression. Return true if that's the case and false otherwise.

This is not resilient to change of type: If a new memory constructor is added, then this function will be wrong

val expect_no_mem : ?⁠handler:(unit -> ('a'v'b'm2) Base.exp) -> ('a'v'b'm1) Base.exp -> ('a'v'b'm2) Base.exp

Expect that an exp has no memory constructor, and then return it with memory removed from the type. Throws Failure if the value had memory constructors.

This is not resilient to change of type, If a new memory constructor is added, then this function will be unsound.

TODO: Find a way to make it resilient

\ No newline at end of file +Manip (read-dwarf.Ast.Manip)

Module Ast.Manip

This module provide generic facilities of expression and SMT statements provided by Ast. It is intended to only provide syntactic facilities over Ast types, in particular Ast.exp.

In particular it provides generic mapping and iteration function over expressions as well a function allowing to convert between the various Expression type parameter and options.

Warning: due to OCaml type system limitations, mainly issue #9456, this module is sometimes required to use Obj.magic in some specific cases. No other module should ever do that. If you need to use Obj.magic to bypass OCaml type system limitation about Ast type, add a function here instead.

Get annotations

val annot_exp : ('a, 'v, 'b, 'm) Base.exp -> 'a

Get the annotation of an expression

TODO: This would be much more efficient if the annotation was always the first member of the constructor and not the last (in that case the offset to fetch the annotation do not depend on the constructor index). This may require to modify ott.

Non-recursive maps and iters

This section is filled on demand.

direct_a_map_b take a function b -> b and applies it to all b in a, non-recursively. Then a new a with the same structure is formed.

direct_a_iter_b take a function b -> unit and applies it to all b in a, non-recursively.

val direct_exp_map_exp : + (('a, 'v, 'b, 'm) Base.exp -> ('a, 'v, 'b, 'm) Base.exp) -> + ('a, 'v, 'b, 'm) Base.exp -> + ('a, 'v, 'b, 'm) Base.exp
val direct_exp_iter_exp : + (('a, 'v, 'b, 'm) Base.exp -> unit) -> + ('a, 'v, 'b, 'm) Base.exp -> + unit
val direct_exp_fold_left_exp : + ('a -> ('b, 'c, 'd, 'e) Base.exp -> 'a) -> + 'a -> + ('b, 'c, 'd, 'e) Base.exp -> + 'a
val direct_exp_for_all_exp : + (('a, 'b, 'c, 'd) Base.exp -> bool) -> + ('a, 'b, 'c, 'd) Base.exp -> + bool
val direct_exp_exists_exp : + (('a, 'b, 'c, 'd) Base.exp -> bool) -> + ('a, 'b, 'c, 'd) Base.exp -> + bool

Recursive maps and iters

This section is filled on demand.

a_map_b take a function b -> b and applies it to all the b in a, and do that recursively on all b that appear directly or indirectly in a

a_iter_b take a function b -> unit and applies it to all the b in a, and do that recursively

Doing this when a = b is not well defined, and can be easily done using the direct version from previous section.

val exp_iter_var : ('v -> unit) -> ('a, 'v, 'b, 'm) Base.exp -> unit

Iterate a function on all the variable of an expression

val exp_map_var : + 'va 'vb 'a 'b 'm. ('va -> 'vb) -> + ('a, 'va, 'b, 'm) Base.exp -> + ('a, 'vb, 'b, 'm) Base.exp
val exp_iter_annot : ('a -> unit) -> ('a, 'v, 'b, 'm) Base.exp -> unit

Iterate a function on all the annotations of an expression

Variable type conversion

All of those function convert the underlying variable type through the AST. They cannot be the usual map function because they change the type

val exp_conv_var : + ('a -> 'b) -> + ('c, 'a, 'd, 'e) Base.exp -> + ('c, 'b, 'd, 'e) Base.exp

Old alias to make conversion explicit

val exp_var_subst : + 'va 'a 'vb 'b 'm. ('va -> 'a -> ('a, 'vb, 'b, 'm) Base.exp) -> + ('a, 'va, 'b, 'm) Base.exp -> + ('a, 'vb, 'b, 'm) Base.exp

Substitute variable with expression according to substitution function

Bound variables and let-bindings

This section allow to go from expression without let-bindings to expression with them and vice-versa.

val allow_lets : ('a, 'v, Base.no, 'm) Base.exp -> ('a, 'v, 'b, 'm) Base.exp

Allow bound variables and lets in an expression. This operation is a no-op and has no runtime cost, it's just a type change.

val smt_allow_lets : + ('a, 'v, Base.no, 'm) Base.smt -> + ('a, 'v, 'b, 'm) Base.smt

Same as allow_lets but for the smt type

val unfold_lets : + 'b1 'a 'v 'b2 'm. ?context:('b1, ('a, 'v, 'b2, 'm) Base.exp) Stdlib.Hashtbl.t -> + ('a, 'v, 'b1, 'm) Base.exp -> + ('a, 'v, 'b2, 'm) Base.exp

Unfold all lets. There are no remaining lets in the output, Therefore the output type of let binding can be anything including Ast.no. In particular doing allow_lets after this function is useless

Memory operation

This section allow to go from expression without memory operations to expression with them and vice-versa.

val allow_mem : ('a, 'v, 'b, Base.no) Base.exp -> ('a, 'v, 'b, 'm) Base.exp

Allow memory operations in an expression.

val smt_allow_mem : ('a, 'v, 'b, Base.no) Base.smt -> ('a, 'v, 'b, 'm) Base.smt

Same as allow_mem but for the smt type

val ty_allow_mem : Base.no Base.ty -> 'm Base.ty

Same as allow_mem but for the ty type

val check_no_mem : ('a, 'v, 'b, 'm) Base.exp -> bool

Check that not memory operation take place in that expression. Return true if that's the case and false otherwise.

This is not resilient to change of type: If a new memory constructor is added, then this function will be wrong

val expect_no_mem : + ?handler:(unit -> ('a, 'v, 'b, 'm2) Base.exp) -> + ('a, 'v, 'b, 'm1) Base.exp -> + ('a, 'v, 'b, 'm2) Base.exp

Expect that an exp has no memory constructor, and then return it with memory removed from the type. Throws Failure if the value had memory constructors.

This is not resilient to change of type, If a new memory constructor is added, then this function will be unsound.

TODO: Find a way to make it resilient

val all_subterms : ('a, 'b, 'c, 'd) Base.exp -> ('a, 'b, 'c, 'd) Base.exp list
diff --git a/doc/html/read-dwarf/Ast/index.html b/doc/html/read-dwarf/Ast/index.html index d404b5a8..515ae0e9 100644 --- a/doc/html/read-dwarf/Ast/index.html +++ b/doc/html/read-dwarf/Ast/index.html @@ -1,7 +1,58 @@ -Ast (read-dwarf.Ast)

Module Ast

include Base
include AstGen.Def
include AstGen.Ott
type bvar = string

syntax

type flag = string
type enum = int * int
type 'm binmem =
| Select of 'm
| Store of 'm
type bvarith =
| Bvuremi
| Bvsremi
| Bvsmodi
| Bvnand
| Bvnor
| Bvxnor
| Bvsub
| Bvudiv
| Bvudivi
| Bvsdiv
| Bvsdivi
| Bvurem
| Bvsrem
| Bvsmod
| Bvshl
| Bvlshr
| Bvashr
type bvcomp =
| Bvult
| Bvslt
| Bvule
| Bvsle
| Bvuge
| Bvsge
| Bvugt
| Bvsgt
type bvmanyarith =
| Bvand
| Bvor
| Bvxor
| Bvadd
| Bvmul
type unop =
| Not
| Bvnot
| Bvredand
| Bvredor
| Bvneg
| Extract of int * int
| ZeroExtend of int
| SignExtend of int
type 'm binop =
| Binmem of 'm binmem
| Eq
| Bvarith of bvarith
| Bvcomp of bvcomp
type manyop =
| And
| Or
| Bvmanyarith of bvmanyarith
| Concat
type ('a, 'v, 'b, 'm) exp =
| Var of 'v * 'a
| Bound of 'b * 'a
| Bits of Utils.BitVec.t * 'a
| Bool of bool * 'a
| Enum of enum * 'a
| Unop of unop * ('a'v'b'm) exp * 'a
| Binop of 'm binop * ('a'v'b'm) exp * ('a'v'b'm) exp * 'a
| Manyop of manyop * ('a'v'b'm) exp list * 'a
| Vec of ('a'v'b'm) exp list * 'a
| Ite of ('a'v'b'm) exp * ('a'v'b'm) exp * ('a'v'b'm) exp * 'a
| Let of 'b * ('a'v'b'm) exp * ('b * ('a'v'b'm) exp) list * ('a'v'b'm) exp * 'a
type 'm ty =
| Ty_Mem of 'm
| Ty_Bool
| Ty_BitVec of int
| Ty_Enum of int
| Ty_Array of 'm ty * 'm ty
type ('a, 'v, 'b, 'm) smt =
| DeclareConst of 'v * 'm ty
| DefineConst of 'v * ('a'v'b'm) exp
| Assert of ('a'v'b'm) exp
| Simplify of ('a'v'b'm) exp * (string * bool) list
| Push
| Pop
| GetVersion
| CheckSat
| Exit
type ('a, 'v, 'b, 'm) smt_ans =
| Error of string
| Version of string
| Sat
| Unsat
| Unknown
| Unsupported
| Exp of ('a'v'b'm) exp
type no =

Generic empty type. This kind of type is explained here. In particular, when this type appear in a match case, the match case can contain a refutation pattern . to indicate that this impossible.

A refutation pattern also work with higher-level constructors, for example in this case:

type a = A of int | B of no * int
+Ast (read-dwarf.Ast)

Module Ast

include module type of struct include Base end
include module type of struct include AstGen.Def end
include module type of struct include AstGen.Ott end
type bvar = string

syntax

type flag = string
type enum = int * int
type bvmanyarith = AstGen.Ott.bvmanyarith =
  1. | Bvand
  2. | Bvor
  3. | Bvxor
  4. | Bvadd
  5. | Bvmul
type 'm binmem = 'm AstGen.Ott.binmem =
  1. | Select of 'm
  2. | Store of 'm
type bvcomp = AstGen.Ott.bvcomp =
  1. | Bvult
  2. | Bvslt
  3. | Bvule
  4. | Bvsle
  5. | Bvuge
  6. | Bvsge
  7. | Bvugt
  8. | Bvsgt
type bvarith = AstGen.Ott.bvarith =
  1. | Bvuremi
  2. | Bvsremi
  3. | Bvsmodi
  4. | Bvnand
  5. | Bvnor
  6. | Bvxnor
  7. | Bvsub
  8. | Bvudiv
  9. | Bvudivi
  10. | Bvsdiv
  11. | Bvsdivi
  12. | Bvurem
  13. | Bvsrem
  14. | Bvsmod
  15. | Bvshl
  16. | Bvlshr
  17. | Bvashr
type manyop = AstGen.Ott.manyop =
  1. | And
  2. | Or
  3. | Bvmanyarith of bvmanyarith
  4. | Concat
type 'm binop = 'm AstGen.Ott.binop =
  1. | Binmem of 'm binmem
  2. | Eq
  3. | Bvarith of bvarith
  4. | Bvcomp of bvcomp
type unop = AstGen.Ott.unop =
  1. | Not
  2. | Bvnot
  3. | Bvredand
  4. | Bvredor
  5. | Bvneg
  6. | Extract of int * int
  7. | ZeroExtend of int
  8. | SignExtend of int
type ('a, 'v, 'b, 'm) exp = ('a, 'v, 'b, 'm) AstGen.Ott.exp =
  1. | Var of 'v * 'a
  2. | Bound of 'b * 'a
  3. | Bits of Utils.BitVec.t * 'a
  4. | Bool of bool * 'a
  5. | Enum of enum * 'a
  6. | Unop of unop * ('a, 'v, 'b, 'm) exp * 'a
  7. | Binop of 'm binop * ('a, 'v, 'b, 'm) exp * ('a, 'v, 'b, 'm) exp * 'a
  8. | Manyop of manyop * ('a, 'v, 'b, 'm) exp list * 'a
  9. | Vec of ('a, 'v, 'b, 'm) exp list * 'a
  10. | Ite of ('a, 'v, 'b, 'm) exp * ('a, 'v, 'b, 'm) exp * ('a, 'v, 'b, 'm) exp * 'a
  11. | 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 =
  1. | Ty_Mem of 'm
  2. | Ty_Bool
  3. | Ty_BitVec of int
  4. | Ty_Enum of int
  5. | Ty_Array of 'm ty * 'm ty
type ('a, 'v, 'b, 'm) smt = ('a, 'v, 'b, 'm) AstGen.Ott.smt =
  1. | DeclareConst of 'v * 'm ty
  2. | DefineConst of 'v * ('a, 'v, 'b, 'm) exp
  3. | Assert of ('a, 'v, 'b, 'm) exp
  4. | Simplify of ('a, 'v, 'b, 'm) exp * (string * bool) list
  5. | Push
  6. | Pop
  7. | GetVersion
  8. | CheckSat
  9. | Exit
type ('a, 'v, 'b, 'm) smt_ans = ('a, 'v, 'b, 'm) AstGen.Ott.smt_ans =
  1. | Error of string
  2. | Version of string
  3. | Sat
  4. | Unsat
  5. | Unknown
  6. | Unsupported
  7. | 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 loc = Stdlib.Lexing.position

Lexing.position

type lrng = Isla_lang.AST.lrng

The type of an expression range. This imported from Isla to avoid having two incompatible types.

This represent a range in a source file, so generally this is a pair of loc, but it may also be Unknown.

module Size = AstGen.Def.Size
type rexp = (lrng, string, string, Size.t) exp

Raw expression coming out of the parser

type rty = Size.t ty

Raw type coming out of the parser

type rsmt = (lrng, string, string, Size.t) smt

Raw SMT command coming out of the parser

type rsmt_ans = (lrng, string, string, Size.t) smt_ans

Raw SMT answer coming out of the parser

Size

module Size = Base.Size

Parsing

module Parser = AstGen.Parser
module Lexer = AstGen.Lexer
val unknown : Isla_lang.AST.lrng
val loc : Stdlib.Lexing.position -> PPrint.document
exception ParseError of loc * string

Exception that represent an Isla parsing error

exception LexError of loc * string

Exception that represent an Isla lexing error

type lexbuf = Stdlib.Lexing.lexbuf
type lexer = lexbuf -> Parser.token
type 'a parser = lexer -> lexbuf -> 'a
val parse : 'a parser -> ('b -> lexbuf) -> ?⁠filename:string -> 'b -> 'a

Parse a single Isla instruction output from a Lexing.lexbuf

val from_string : string -> Stdlib.Lexing.lexbuf
val from_channel : Stdlib.in_channel -> Stdlib.Lexing.lexbuf
val parse_exp_string : ?⁠filename:string -> string -> (AstGen.Def.lrng, string, string, AstGen.Def.Size.t) AstGen.Ott.exp

Parse a single Isla expression from a string

val parse_exp_channel : ?⁠filename:string -> Stdlib.in_channel -> (AstGen.Def.lrng, string, string, AstGen.Def.Size.t) AstGen.Ott.exp

Parse a single Isla expression from a channel

val parse_smt_ans_string : ?⁠filename:string -> string -> AstGen.Def.rsmt_ans

Parse a single Isla expression from a string

val parse_smt_ans_channel : ?⁠filename:string -> Stdlib.in_channel -> AstGen.Def.rsmt_ans

Parse a single Isla expression from a channel

include AstGen.Parser_pp
val pp_raw_bvar : string -> PPrintEngine.document
val pp_raw_bvf : Utils.BitVec.t -> PPrintEngine.document
val pp_raw_flag : string -> PPrintEngine.document
val pp_raw_vvar : int -> PPrintEngine.document
val pp_raw_name : string -> PPrintEngine.document
val pp_raw_enum_ty : int -> PPrintEngine.document
val pp_raw_enum : AstGen.Ott.enum -> PPrintEngine.document
val pp_raw_int : int -> PPrintEngine.document
val pp_raw_bvi : int -> PPrintEngine.document
val pp_raw_bv : string -> PPrintEngine.document
val pp_raw_str : string -> PPrintEngine.document
val pp_raw_mem_size : AstGen.Def.Size.t -> Utils.Pp.document
val pp_raw_binmem : AstGen.Def.Size.t AstGen.Ott.binmem -> PPrintEngine.document
val pp_raw_var : ('a -> PPrintEngine.document) -> 'a -> PPrintEngine.document
val pp_raw_bbvar : string -> PPrintEngine.document
val pp_raw_bind : ('a -> PPrintEngine.document) -> (string * ('b'a, string, AstGen.Def.Size.t) AstGen.Ott.exp) -> PPrintEngine.document
val pp_raw_exp : ('a -> PPrintEngine.document) -> ('b'a, string, AstGen.Def.Size.t) AstGen.Ott.exp -> PPrintEngine.document
val pp_raw_fbool : (string * bool) -> PPrintEngine.document
val pp_raw_smt : ('a -> PPrintEngine.document) -> ('b'a, string, AstGen.Def.Size.t) AstGen.Ott.smt -> PPrintEngine.document
val pp_raw_smts : ('a -> PPrintEngine.document) -> ('b'a, string, AstGen.Def.Size.t) AstGen.Ott.smt list -> PPrintEngine.document
val pp_raw_smt_ans : ('a -> PPrintEngine.document) -> ('b'a, string, AstGen.Def.Size.t) AstGen.Ott.smt_ans -> PPrintEngine.document
val pp_raw_ty : AstGen.Def.Size.t AstGen.Ott.ty -> PPrintEngine.document
val pp_raw_bool : bool -> PPrintEngine.document
val pp_raw_unop : AstGen.Ott.unop -> PPrintEngine.document
val pp_raw_bvarith : AstGen.Ott.bvarith -> PPrintEngine.document
val pp_raw_bvcomp : AstGen.Ott.bvcomp -> PPrintEngine.document
val pp_raw_binop : AstGen.Def.Size.t AstGen.Ott.binop -> PPrintEngine.document
val pp_raw_bvmanyarith : AstGen.Ott.bvmanyarith -> PPrintEngine.document
val pp_raw_manyop : AstGen.Ott.manyop -> PPrintEngine.document
val pp_bvar : string -> PPrintEngine.document
val pp_bvf : Utils.BitVec.t -> PPrintEngine.document
val pp_flag : string -> PPrintEngine.document
val pp_vvar : int -> PPrintEngine.document
val pp_name : string -> PPrintEngine.document
val pp_enum_ty : int -> PPrintEngine.document
val pp_enum : AstGen.Ott.enum -> PPrintEngine.document
val pp_int : int -> PPrintEngine.document
val pp_bvi : int -> PPrintEngine.document
val pp_bv : string -> PPrintEngine.document
val pp_str : string -> PPrintEngine.document
val pp_j : int -> string
val pp_mem_size : AstGen.Def.Size.t -> Utils.Pp.document
val pp_binmem : AstGen.Def.Size.t AstGen.Ott.binmem -> PPrintEngine.document
val pp_var : ('a -> PPrintEngine.document) -> 'a -> PPrintEngine.document
val pp_bbvar : string -> PPrintEngine.document
val pp_bind : ('a -> PPrintEngine.document) -> (string * ('b'a, string, AstGen.Def.Size.t) AstGen.Ott.exp) -> PPrintEngine.document
val pp_exp : ('a -> PPrintEngine.document) -> ('b'a, string, AstGen.Def.Size.t) AstGen.Ott.exp -> PPrintEngine.document
val pp_fbool : (string * bool) -> PPrintEngine.document
val pp_smt : ('a -> PPrintEngine.document) -> ('b'a, string, AstGen.Def.Size.t) AstGen.Ott.smt -> PPrintEngine.document
val pp_smts : ('a -> PPrintEngine.document) -> ('b'a, string, AstGen.Def.Size.t) AstGen.Ott.smt list -> PPrintEngine.document
val pp_smt_ans : ('a -> PPrintEngine.document) -> ('b'a, string, AstGen.Def.Size.t) AstGen.Ott.smt_ans -> PPrintEngine.document
val pp_ty : AstGen.Def.Size.t AstGen.Ott.ty -> PPrintEngine.document
val pp_bool : bool -> PPrintEngine.document
val pp_unop : AstGen.Ott.unop -> PPrintEngine.document
val pp_bvarith : AstGen.Ott.bvarith -> PPrintEngine.document
val pp_bvcomp : AstGen.Ott.bvcomp -> PPrintEngine.document
val pp_binop : AstGen.Def.Size.t AstGen.Ott.binop -> PPrintEngine.document
val pp_bvmanyarith : AstGen.Ott.bvmanyarith -> PPrintEngine.document
val pp_manyop : AstGen.Ott.manyop -> PPrintEngine.document
val pp_loc : Stdlib.Lexing.position -> PPrint.document

Prints a lexing location

val pp_lrng : Isla_lang.AST.lrng -> PPrint.document

Prints a lexing range

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.

val destr_binmem : no binmem -> 'a

Expectors

Functions to assert that a specific constructor is used and get the value

val expect_bits : ('a'b'c'd) exp -> Utils.BitVec.t
val ty_expect_bv : 'a ty -> int

Construtors

val assert_smt : ('a'b'c'd) exp -> ('a'b'c'd) smt
val simplify_smt : ?⁠flags:(string * bool) list -> ('a'b'c'd) exp -> ('a'b'c'd) smt

Comparisons

val equal_exp : ?⁠annot:('a -> 'a -> bool) -> var:('b -> 'b -> bool) -> ?⁠bnd:('c -> 'c -> bool) -> ('a'b'c'd) exp -> ('a'b'c'd) exp -> bool

Equality for expressions; default polymorphic equality will fail.

module Base : sig ... end

The main module to use the AST of expression and SMT operation for a more generic overview of the AST, see SymbolicExpressions.

module Manip : sig ... end

This module provide generic facilities of expression and SMT statements provided by Ast. It is intended to only provide syntactic facilities over Ast types, in particular Ast.exp.

\ 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.

type loc = Stdlib.Lexing.position
type lrng = Isla_lang.AST.lrng

The type of an expression range. This imported from Isla to avoid having two incompatible types.

This represent a range in a source file, so generally this is a pair of loc, but it may also be Unknown.

type rexp = (lrng, string, string, Base.Size.t) exp

Raw expression coming out of the parser

type rty = Base.Size.t ty

Raw type coming out of the parser

type rsmt = (lrng, string, string, Base.Size.t) smt

Raw SMT command coming out of the parser

type rsmt_ans = (lrng, string, string, Base.Size.t) smt_ans

Raw SMT answer coming out of the parser

Size

module Size = Base.Size

Parsing

module Parser = AstGen.Parser
module Lexer = AstGen.Lexer
val unknown : Isla_lang.AST.lrng
val loc : Stdlib.Lexing.position -> PPrint.document
exception ParseError of loc * string

Exception that represent an Isla parsing error

exception LexError of loc * string

Exception that represent an Isla lexing error

type lexbuf = Stdlib.Lexing.lexbuf
type lexer = lexbuf -> Parser.token
type 'a parser = lexer -> lexbuf -> 'a
val parse : 'a parser -> ('b -> lexbuf) -> ?filename:string -> 'b -> 'a

Parse a single Isla instruction output from a Lexing.lexbuf

val from_string : string -> Stdlib.Lexing.lexbuf
val from_channel : Stdlib.in_channel -> Stdlib.Lexing.lexbuf
val parse_exp_string : + ?filename:string -> + string -> + (AstGen.Def.lrng, string, string, AstGen.Def.Size.t) AstGen.Ott.exp

Parse a single Isla expression from a string

val parse_exp_channel : + ?filename:string -> + Stdlib.in_channel -> + (AstGen.Def.lrng, string, string, AstGen.Def.Size.t) AstGen.Ott.exp

Parse a single Isla expression from a channel

val parse_smt_ans_string : ?filename:string -> string -> AstGen.Def.rsmt_ans

Parse a single Isla expression from a string

val parse_smt_ans_channel : + ?filename:string -> + Stdlib.in_channel -> + AstGen.Def.rsmt_ans

Parse a single Isla expression from a channel

include module type of struct include AstGen.Parser_pp end
val pp_raw_bvar : string -> PPrint.document
val pp_raw_bvf : Utils.BitVec.t -> PPrint.document
val pp_raw_flag : string -> PPrint.document
val pp_raw_vvar : int -> PPrint.document
val pp_raw_name : string -> PPrint.document
val pp_raw_enum_ty : int -> PPrint.document
val pp_raw_enum : AstGen.Ott.enum -> PPrint.document
val pp_raw_nat : int -> PPrint.document
val pp_raw_bvi : int -> PPrint.document
val pp_raw_bv : string -> PPrint.document
val pp_raw_str : string -> PPrint.document
val pp_raw_mem_size : AstGen.Def.Size.t -> Utils.Pp.document
val pp_raw_binmem : AstGen.Def.Size.t AstGen.Ott.binmem -> PPrint.document
val pp_raw_var : ('a -> PPrint.document) -> 'a -> PPrint.document
val pp_raw_bbvar : string -> PPrint.document
val pp_raw_bind : + ('a -> PPrint.document) -> + (string * ('b, 'a, string, AstGen.Def.Size.t) AstGen.Ott.exp) -> + PPrint.document
val pp_raw_exp : + ('a -> PPrint.document) -> + ('b, 'a, string, AstGen.Def.Size.t) AstGen.Ott.exp -> + PPrint.document
val pp_raw_fbool : (string * bool) -> PPrint.document
val pp_raw_smt : + ('a -> PPrint.document) -> + ('b, 'a, string, AstGen.Def.Size.t) AstGen.Ott.smt -> + PPrint.document
val pp_raw_smts : + ('a -> PPrint.document) -> + ('b, 'a, string, AstGen.Def.Size.t) AstGen.Ott.smt list -> + PPrint.document
val pp_raw_smt_ans : + ('a -> PPrint.document) -> + ('b, 'a, string, AstGen.Def.Size.t) AstGen.Ott.smt_ans -> + PPrint.document
val pp_raw_int : int -> PPrint.document
val pp_raw_ty : AstGen.Def.Size.t AstGen.Ott.ty -> PPrint.document
val pp_raw_bool : bool -> PPrint.document
val pp_raw_unop : AstGen.Ott.unop -> PPrint.document
val pp_raw_bvarith : AstGen.Ott.bvarith -> PPrint.document
val pp_raw_bvcomp : AstGen.Ott.bvcomp -> PPrint.document
val pp_raw_binop : AstGen.Def.Size.t AstGen.Ott.binop -> PPrint.document
val pp_raw_bvmanyarith : AstGen.Ott.bvmanyarith -> PPrint.document
val pp_raw_manyop : AstGen.Ott.manyop -> PPrint.document
val pp_bvar : string -> PPrint.document
val pp_bvf : Utils.BitVec.t -> PPrint.document
val pp_flag : string -> PPrint.document
val pp_vvar : int -> PPrint.document
val pp_name : string -> PPrint.document
val pp_enum_ty : int -> PPrint.document
val pp_enum : AstGen.Ott.enum -> PPrint.document
val pp_nat : int -> PPrint.document
val pp_bvi : int -> PPrint.document
val pp_bv : string -> PPrint.document
val pp_str : string -> PPrint.document
val pp_j : int -> string
val pp_binmem : AstGen.Def.Size.t AstGen.Ott.binmem -> PPrint.document
val pp_var : ('a -> PPrint.document) -> 'a -> PPrint.document
val pp_bbvar : string -> PPrint.document
val pp_bind : + ('a -> PPrint.document) -> + (string * ('b, 'a, string, AstGen.Def.Size.t) AstGen.Ott.exp) -> + PPrint.document
val pp_exp : + ('a -> PPrint.document) -> + ('b, 'a, string, AstGen.Def.Size.t) AstGen.Ott.exp -> + PPrint.document
val pp_fbool : (string * bool) -> PPrint.document
val pp_smt : + ('a -> PPrint.document) -> + ('b, 'a, string, AstGen.Def.Size.t) AstGen.Ott.smt -> + PPrint.document
val pp_smts : + ('a -> PPrint.document) -> + ('b, 'a, string, AstGen.Def.Size.t) AstGen.Ott.smt list -> + PPrint.document
val pp_smt_ans : + ('a -> PPrint.document) -> + ('b, 'a, string, AstGen.Def.Size.t) AstGen.Ott.smt_ans -> + PPrint.document
val pp_int : int -> PPrint.document
val pp_ty : AstGen.Def.Size.t AstGen.Ott.ty -> PPrint.document
val pp_bool : bool -> PPrint.document
val pp_unop : AstGen.Ott.unop -> PPrint.document
val pp_bvarith : AstGen.Ott.bvarith -> PPrint.document
val pp_bvcomp : AstGen.Ott.bvcomp -> PPrint.document
val pp_binop : AstGen.Def.Size.t AstGen.Ott.binop -> PPrint.document
val pp_bvmanyarith : AstGen.Ott.bvmanyarith -> PPrint.document
val pp_manyop : AstGen.Ott.manyop -> PPrint.document
val pp_loc : Stdlib.Lexing.position -> PPrint.document

Prints a lexing location

val pp_lrng : Isla_lang.AST.lrng -> PPrint.document

Prints a lexing range

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.

val destr_binmem : no binmem -> 'a

Expectors

Functions to assert that a specific constructor is used and get the value

val expect_bits : ('a, 'b, 'c, 'd) exp -> Utils.BitVec.t
val ty_expect_bv : 'a ty -> int

Construtors

val assert_smt : ('a, 'b, 'c, 'd) exp -> ('a, 'b, 'c, 'd) smt
val simplify_smt : + ?flags:(string * bool) list -> + ('a, 'b, 'c, 'd) exp -> + ('a, 'b, 'c, 'd) smt

Comparisons

val equal_exp : + ?annot:('a -> 'a -> bool) -> + var:('b -> 'b -> bool) -> + ?bnd:('c -> 'c -> bool) -> + ('a, 'b, 'c, 'd) exp -> + ('a, 'b, 'c, 'd) exp -> + bool

Equality for expressions; default polymorphic equality will fail.

module Base : sig ... end

The main module to use the AST of expression and SMT operation for a more generic overview of the AST, see SymbolicExpressions.

module Manip : sig ... end

This module provide generic facilities of expression and SMT statements provided by Ast. It is intended to only provide syntactic facilities over Ast types, in particular Ast.exp.

diff --git a/doc/html/read-dwarf/AstGen/Def/Size/index.html b/doc/html/read-dwarf/AstGen/Def/Size/index.html index 0c37c020..bdc9c6a9 100644 --- a/doc/html/read-dwarf/AstGen/Def/Size/index.html +++ b/doc/html/read-dwarf/AstGen/Def/Size/index.html @@ -1,2 +1,2 @@ -Size (read-dwarf.AstGen.Def.Size)

Module Def.Size

type t =
| B8
| B16
| B32
| B64

The possible sizes for memory accesses. It may be necessary to add B128 at some point

val of_bytes : int -> t

Create a size value from a valid size in byte

val of_bits : int -> t

Create a size value from a valid size in bits

val to_bytes : t -> int

Get the byte size corresponding to that value

val to_bits : t -> int

Get the bits size corresponding to that value

val equal : 'a -> 'a -> bool
val pp_bytes : t -> Utils.Pp.document

Pretty-print a size as just the byte number

val pp_bits : t -> PPrintEngine.document

Pretty print a size at "16bits" for example

\ No newline at end of file +Size (read-dwarf.AstGen.Def.Size)

Module Def.Size

type t =
  1. | B8
  2. | B16
  3. | B32
  4. | B64
  5. | B128

The possible sizes for memory accesses.

val of_bytes : int -> t

Create a size value from a valid size in byte

val of_bits : int -> t

Create a size value from a valid size in bits

val to_bytes : t -> int

Get the byte size corresponding to that value

val to_bits : t -> int

Get the bits size corresponding to that value

val equal : 'a -> 'a -> bool
val pp_bytes : t -> Utils.Pp.document

Pretty-print a size as just the byte number

val pp_bits : t -> Utils.Pp.document

Pretty print a size at "16bits" for example

diff --git a/doc/html/read-dwarf/AstGen/Def/index.html b/doc/html/read-dwarf/AstGen/Def/index.html index 36f4a134..19c96b33 100644 --- a/doc/html/read-dwarf/AstGen/Def/index.html +++ b/doc/html/read-dwarf/AstGen/Def/index.html @@ -1,7 +1,10 @@ -Def (read-dwarf.AstGen.Def)

Module AstGen.Def

include Ott
type bvar = string

syntax

type flag = string
type enum = int * int
type 'm binmem =
| Select of 'm
| Store of 'm
type bvarith =
| Bvuremi
| Bvsremi
| Bvsmodi
| Bvnand
| Bvnor
| Bvxnor
| Bvsub
| Bvudiv
| Bvudivi
| Bvsdiv
| Bvsdivi
| Bvurem
| Bvsrem
| Bvsmod
| Bvshl
| Bvlshr
| Bvashr
type bvcomp =
| Bvult
| Bvslt
| Bvule
| Bvsle
| Bvuge
| Bvsge
| Bvugt
| Bvsgt
type bvmanyarith =
| Bvand
| Bvor
| Bvxor
| Bvadd
| Bvmul
type unop =
| Not
| Bvnot
| Bvredand
| Bvredor
| Bvneg
| Extract of int * int
| ZeroExtend of int
| SignExtend of int
type 'm binop =
| Binmem of 'm binmem
| Eq
| Bvarith of bvarith
| Bvcomp of bvcomp
type manyop =
| And
| Or
| Bvmanyarith of bvmanyarith
| Concat
type ('a, 'v, 'b, 'm) exp =
| Var of 'v * 'a
| Bound of 'b * 'a
| Bits of Utils.BitVec.t * 'a
| Bool of bool * 'a
| Enum of enum * 'a
| Unop of unop * ('a'v'b'm) exp * 'a
| Binop of 'm binop * ('a'v'b'm) exp * ('a'v'b'm) exp * 'a
| Manyop of manyop * ('a'v'b'm) exp list * 'a
| Vec of ('a'v'b'm) exp list * 'a
| Ite of ('a'v'b'm) exp * ('a'v'b'm) exp * ('a'v'b'm) exp * 'a
| Let of 'b * ('a'v'b'm) exp * ('b * ('a'v'b'm) exp) list * ('a'v'b'm) exp * 'a
type 'm ty =
| Ty_Mem of 'm
| Ty_Bool
| Ty_BitVec of int
| Ty_Enum of int
| Ty_Array of 'm ty * 'm ty
type ('a, 'v, 'b, 'm) smt =
| DeclareConst of 'v * 'm ty
| DefineConst of 'v * ('a'v'b'm) exp
| Assert of ('a'v'b'm) exp
| Simplify of ('a'v'b'm) exp * (string * bool) list
| Push
| Pop
| GetVersion
| CheckSat
| Exit
type ('a, 'v, 'b, 'm) smt_ans =
| Error of string
| Version of string
| Sat
| Unsat
| Unknown
| Unsupported
| Exp of ('a'v'b'm) exp
type no =

Generic empty type. This kind of type is explained here. In particular, when this type appear in a match case, the match case can contain a refutation pattern . to indicate that this impossible.

A refutation pattern also work with higher-level constructors, for example in this case:

type a = A of int | B of no * int
+Def (read-dwarf.AstGen.Def)

Module AstGen.Def

include module type of struct include Ott end
type bvar = string

syntax

type flag = string
type enum = int * int
type bvmanyarith = Ott.bvmanyarith =
  1. | Bvand
  2. | Bvor
  3. | Bvxor
  4. | Bvadd
  5. | Bvmul
type 'm binmem = 'm Ott.binmem =
  1. | Select of 'm
  2. | Store of 'm
type bvcomp = Ott.bvcomp =
  1. | Bvult
  2. | Bvslt
  3. | Bvule
  4. | Bvsle
  5. | Bvuge
  6. | Bvsge
  7. | Bvugt
  8. | Bvsgt
type bvarith = Ott.bvarith =
  1. | Bvuremi
  2. | Bvsremi
  3. | Bvsmodi
  4. | Bvnand
  5. | Bvnor
  6. | Bvxnor
  7. | Bvsub
  8. | Bvudiv
  9. | Bvudivi
  10. | Bvsdiv
  11. | Bvsdivi
  12. | Bvurem
  13. | Bvsrem
  14. | Bvsmod
  15. | Bvshl
  16. | Bvlshr
  17. | Bvashr
type manyop = Ott.manyop =
  1. | And
  2. | Or
  3. | Bvmanyarith of bvmanyarith
  4. | Concat
type 'm binop = 'm Ott.binop =
  1. | Binmem of 'm binmem
  2. | Eq
  3. | Bvarith of bvarith
  4. | Bvcomp of bvcomp
type unop = Ott.unop =
  1. | Not
  2. | Bvnot
  3. | Bvredand
  4. | Bvredor
  5. | Bvneg
  6. | Extract of int * int
  7. | ZeroExtend of int
  8. | SignExtend of int
type ('a, 'v, 'b, 'm) exp = ('a, 'v, 'b, 'm) Ott.exp =
  1. | Var of 'v * 'a
  2. | Bound of 'b * 'a
  3. | Bits of Utils.BitVec.t * 'a
  4. | Bool of bool * 'a
  5. | Enum of enum * 'a
  6. | Unop of unop * ('a, 'v, 'b, 'm) exp * 'a
  7. | Binop of 'm binop * ('a, 'v, 'b, 'm) exp * ('a, 'v, 'b, 'm) exp * 'a
  8. | Manyop of manyop * ('a, 'v, 'b, 'm) exp list * 'a
  9. | Vec of ('a, 'v, 'b, 'm) exp list * 'a
  10. | Ite of ('a, 'v, 'b, 'm) exp * ('a, 'v, 'b, 'm) exp * ('a, 'v, 'b, 'm) exp * 'a
  11. | 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 Ott.ty =
  1. | Ty_Mem of 'm
  2. | Ty_Bool
  3. | Ty_BitVec of int
  4. | Ty_Enum of int
  5. | Ty_Array of 'm ty * 'm ty
type ('a, 'v, 'b, 'm) smt = ('a, 'v, 'b, 'm) Ott.smt =
  1. | DeclareConst of 'v * 'm ty
  2. | DefineConst of 'v * ('a, 'v, 'b, 'm) exp
  3. | Assert of ('a, 'v, 'b, 'm) exp
  4. | Simplify of ('a, 'v, 'b, 'm) exp * (string * bool) list
  5. | Push
  6. | Pop
  7. | GetVersion
  8. | CheckSat
  9. | Exit
type ('a, 'v, 'b, 'm) smt_ans = ('a, 'v, 'b, 'm) Ott.smt_ans =
  1. | Error of string
  2. | Version of string
  3. | Sat
  4. | Unsat
  5. | Unknown
  6. | Unsupported
  7. | 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 = |

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 loc = Stdlib.Lexing.position

Lexing.position

type lrng = Isla_lang.AST.lrng

The type of an expression range. This imported from Isla to avoid having two incompatible types.

This represent a range in a source file, so generally this is a pair of loc, but it may also be Unknown.

module Size : sig ... end
type rexp = (lrng, string, string, Size.t) exp

Raw expression coming out of the parser

type rty = Size.t ty

Raw type coming out of the parser

type rsmt = (lrng, string, string, Size.t) smt

Raw SMT command coming out of the parser

type rsmt_ans = (lrng, string, string, Size.t) smt_ans

Raw SMT answer coming out of the parser

\ 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.

type loc = Stdlib.Lexing.position
type lrng = Isla_lang.AST.lrng

The type of an expression range. This imported from Isla to avoid having two incompatible types.

This represent a range in a source file, so generally this is a pair of loc, but it may also be Unknown.

module Size : sig ... end
type rexp = (lrng, string, string, Size.t) exp

Raw expression coming out of the parser

type rty = Size.t ty

Raw type coming out of the parser

type rsmt = (lrng, string, string, Size.t) smt

Raw SMT command coming out of the parser

type rsmt_ans = (lrng, string, string, Size.t) smt_ans

Raw SMT answer coming out of the parser

diff --git a/doc/html/read-dwarf/AstGen/Lexer/index.html b/doc/html/read-dwarf/AstGen/Lexer/index.html index 6f7fb60d..f03e70e6 100644 --- a/doc/html/read-dwarf/AstGen/Lexer/index.html +++ b/doc/html/read-dwarf/AstGen/Lexer/index.html @@ -1,2 +1,2 @@ -Lexer (read-dwarf.AstGen.Lexer)

Module AstGen.Lexer

exception Error of string
val __ocaml_lex_tables : Stdlib.Lexing.lex_tables
val token : Stdlib.Lexing.lexbuf -> Parser.token
val __ocaml_lex_token_rec : Stdlib.Lexing.lexbuf -> int -> Parser.token
\ No newline at end of file +Lexer (read-dwarf.AstGen.Lexer)

Module AstGen.Lexer

exception Error of string
val __ocaml_lex_tables : Stdlib.Lexing.lex_tables
val token : Stdlib.Lexing.lexbuf -> Parser.token
val __ocaml_lex_token_rec : Stdlib.Lexing.lexbuf -> int -> Parser.token
diff --git a/doc/html/read-dwarf/AstGen/Ott/index.html b/doc/html/read-dwarf/AstGen/Ott/index.html index f1d379eb..a157493d 100644 --- a/doc/html/read-dwarf/AstGen/Ott/index.html +++ b/doc/html/read-dwarf/AstGen/Ott/index.html @@ -1,2 +1,5 @@ -Ott (read-dwarf.AstGen.Ott)

Module AstGen.Ott

type bvar = string

syntax

type flag = string
type enum = int * int
type 'm binmem =
| Select of 'm
| Store of 'm
type bvarith =
| Bvuremi
| Bvsremi
| Bvsmodi
| Bvnand
| Bvnor
| Bvxnor
| Bvsub
| Bvudiv
| Bvudivi
| Bvsdiv
| Bvsdivi
| Bvurem
| Bvsrem
| Bvsmod
| Bvshl
| Bvlshr
| Bvashr
type bvcomp =
| Bvult
| Bvslt
| Bvule
| Bvsle
| Bvuge
| Bvsge
| Bvugt
| Bvsgt
type bvmanyarith =
| Bvand
| Bvor
| Bvxor
| Bvadd
| Bvmul
type unop =
| Not
| Bvnot
| Bvredand
| Bvredor
| Bvneg
| Extract of int * int
| ZeroExtend of int
| SignExtend of int
type 'm binop =
| Binmem of 'm binmem
| Eq
| Bvarith of bvarith
| Bvcomp of bvcomp
type manyop =
| And
| Or
| Bvmanyarith of bvmanyarith
| Concat
type ('a, 'v, 'b, 'm) exp =
| Var of 'v * 'a
| Bound of 'b * 'a
| Bits of Utils.BitVec.t * 'a
| Bool of bool * 'a
| Enum of enum * 'a
| Unop of unop * ('a'v'b'm) exp * 'a
| Binop of 'm binop * ('a'v'b'm) exp * ('a'v'b'm) exp * 'a
| Manyop of manyop * ('a'v'b'm) exp list * 'a
| Vec of ('a'v'b'm) exp list * 'a
| Ite of ('a'v'b'm) exp * ('a'v'b'm) exp * ('a'v'b'm) exp * 'a
| Let of 'b * ('a'v'b'm) exp * ('b * ('a'v'b'm) exp) list * ('a'v'b'm) exp * 'a
type 'm ty =
| Ty_Mem of 'm
| Ty_Bool
| Ty_BitVec of int
| Ty_Enum of int
| Ty_Array of 'm ty * 'm ty
type ('a, 'v, 'b, 'm) smt =
| DeclareConst of 'v * 'm ty
| DefineConst of 'v * ('a'v'b'm) exp
| Assert of ('a'v'b'm) exp
| Simplify of ('a'v'b'm) exp * (string * bool) list
| Push
| Pop
| GetVersion
| CheckSat
| Exit
type ('a, 'v, 'b, 'm) smt_ans =
| Error of string
| Version of string
| Sat
| Unsat
| Unknown
| Unsupported
| Exp of ('a'v'b'm) exp
\ No newline at end of file +Ott (read-dwarf.AstGen.Ott)

Module AstGen.Ott

warning: the backend selected ignores the file structure informations

type bvar = string

syntax

type flag = string
type enum = int * int
type bvmanyarith =
  1. | Bvand
  2. | Bvor
  3. | Bvxor
  4. | Bvadd
  5. | Bvmul
type 'm binmem =
  1. | Select of 'm
  2. | Store of 'm
type bvcomp =
  1. | Bvult
  2. | Bvslt
  3. | Bvule
  4. | Bvsle
  5. | Bvuge
  6. | Bvsge
  7. | Bvugt
  8. | Bvsgt
type bvarith =
  1. | Bvuremi
  2. | Bvsremi
  3. | Bvsmodi
  4. | Bvnand
  5. | Bvnor
  6. | Bvxnor
  7. | Bvsub
  8. | Bvudiv
  9. | Bvudivi
  10. | Bvsdiv
  11. | Bvsdivi
  12. | Bvurem
  13. | Bvsrem
  14. | Bvsmod
  15. | Bvshl
  16. | Bvlshr
  17. | Bvashr
type manyop =
  1. | And
  2. | Or
  3. | Bvmanyarith of bvmanyarith
  4. | Concat
type 'm binop =
  1. | Binmem of 'm binmem
  2. | Eq
  3. | Bvarith of bvarith
  4. | Bvcomp of bvcomp
type unop =
  1. | Not
  2. | Bvnot
  3. | Bvredand
  4. | Bvredor
  5. | Bvneg
  6. | Extract of int * int
  7. | ZeroExtend of int
  8. | SignExtend of int
type ('a, 'v, 'b, 'm) exp =
  1. | Var of 'v * 'a
  2. | Bound of 'b * 'a
  3. | Bits of Utils.BitVec.t * 'a
  4. | Bool of bool * 'a
  5. | Enum of enum * 'a
  6. | Unop of unop * ('a, 'v, 'b, 'm) exp * 'a
  7. | Binop of 'm binop * ('a, 'v, 'b, 'm) exp * ('a, 'v, 'b, 'm) exp * 'a
  8. | Manyop of manyop * ('a, 'v, 'b, 'm) exp list * 'a
  9. | Vec of ('a, 'v, 'b, 'm) exp list * 'a
  10. | Ite of ('a, 'v, 'b, 'm) exp * ('a, 'v, 'b, 'm) exp * ('a, 'v, 'b, 'm) exp * 'a
  11. | Let of 'b * ('a, 'v, 'b, 'm) exp + * ('b * ('a, 'v, 'b, 'm) exp) list + * ('a, 'v, 'b, 'm) exp + * 'a
type 'm ty =
  1. | Ty_Mem of 'm
  2. | Ty_Bool
  3. | Ty_BitVec of int
  4. | Ty_Enum of int
  5. | Ty_Array of 'm ty * 'm ty
type ('a, 'v, 'b, 'm) smt =
  1. | DeclareConst of 'v * 'm ty
  2. | DefineConst of 'v * ('a, 'v, 'b, 'm) exp
  3. | Assert of ('a, 'v, 'b, 'm) exp
  4. | Simplify of ('a, 'v, 'b, 'm) exp * (string * bool) list
  5. | Push
  6. | Pop
  7. | GetVersion
  8. | CheckSat
  9. | Exit
type ('a, 'v, 'b, 'm) smt_ans =
  1. | Error of string
  2. | Version of string
  3. | Sat
  4. | Unsat
  5. | Unknown
  6. | Unsupported
  7. | 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

diff --git a/doc/html/read-dwarf/AstGen/Parser/index.html b/doc/html/read-dwarf/AstGen/Parser/index.html index b69b033f..59c28654 100644 --- a/doc/html/read-dwarf/AstGen/Parser/index.html +++ b/doc/html/read-dwarf/AstGen/Parser/index.html @@ -1,2 +1,14 @@ -Parser (read-dwarf.AstGen.Parser)

Module AstGen.Parser

type token =
| ZERO_UNDERSCORE_EXTEND
| VVAR of int
| UNSUPPORTED
| UNSAT
| UNKNOWN
| TRUE
| STR of string
| STORE
| SIMPLIFY
| SIGN_UNDERSCORE_EXTEND
| SELECT
| SAT
| RPAREN
| PUSH
| POP
| OR
| NOT
| NAME of string
| MEM
| LPAREN_UNDERSCORE
| LPAREN
| LET
| ITE
| INT of int
| GET_MINUS_INFO
| FLAG of string
| FALSE
| EXTRACT
| EXIT
| ERROR
| EQ
| EOF
| ENUM_UNDERSCORE_TY of int
| ENUM of int * int
| DEFINE_MINUS_CONST
| DECLARE_MINUS_CONST
| CONCAT
| COLON_VERSION
| CHECK_MINUS_SAT
| BVXOR
| BVXNOR
| BVUREM_UNDERSCORE_I
| BVUREM
| BVULT
| BVULE
| BVUGT
| BVUGE
| BVUDIV_UNDERSCORE_I
| BVUDIV
| BVSUB
| BVSREM_UNDERSCORE_I
| BVSREM
| BVSMOD_UNDERSCORE_I
| BVSMOD
| BVSLT
| BVSLE
| BVSHL
| BVSGT
| BVSGE
| BVSDIV_UNDERSCORE_I
| BVSDIV
| BVREDOR
| BVREDAND
| BVOR
| BVNOT
| BVNOR
| BVNEG
| BVNAND
| BVMUL
| BVLSHR
| BVI of int
| BVF of Utils.BitVec.t
| BVASHR
| BVAR of string
| BVAND
| BVADD
| BV of string
| BOOL
| BITVEC
| ASSERT
| ARRAY
| AND
exception Error
val smts_start : (Stdlib.Lexing.lexbuf -> token) -> Stdlib.Lexing.lexbuf -> Def.rsmt list
val smt_start : (Stdlib.Lexing.lexbuf -> token) -> Stdlib.Lexing.lexbuf -> Def.rsmt
val smt_ans_start : (Stdlib.Lexing.lexbuf -> token) -> Stdlib.Lexing.lexbuf -> Def.rsmt_ans
val exp_start : (Stdlib.Lexing.lexbuf -> token) -> Stdlib.Lexing.lexbuf -> (Def.lrng, string, string, Def.Size.t) Ott.exp
\ No newline at end of file +Parser (read-dwarf.AstGen.Parser)

Module AstGen.Parser

type token =
  1. | ZERO_UNDERSCORE_EXTEND
  2. | VVAR of int
  3. | UNSUPPORTED
  4. | UNSAT
  5. | UNKNOWN
  6. | TRUE
  7. | STR of string
  8. | STORE
  9. | SIMPLIFY
  10. | SIGN_UNDERSCORE_EXTEND
  11. | SELECT
  12. | SAT
  13. | RPAREN
  14. | PUSH
  15. | POP
  16. | OR
  17. | NOT
  18. | NAT of int
  19. | NAME of string
  20. | MINUS
  21. | MEM
  22. | LPAREN_UNDERSCORE
  23. | LPAREN
  24. | LET
  25. | ITE
  26. | GET_MINUS_INFO
  27. | FLAG of string
  28. | FALSE
  29. | EXTRACT
  30. | EXIT
  31. | ERROR
  32. | EQ
  33. | EOF
  34. | ENUM_UNDERSCORE_TY of int
  35. | ENUM of int * int
  36. | DEFINE_MINUS_CONST
  37. | DECLARE_MINUS_CONST
  38. | CONCAT
  39. | COLON_VERSION
  40. | CHECK_MINUS_SAT
  41. | BVXOR
  42. | BVXNOR
  43. | BVUREM_UNDERSCORE_I
  44. | BVUREM
  45. | BVULT
  46. | BVULE
  47. | BVUGT
  48. | BVUGE
  49. | BVUDIV_UNDERSCORE_I
  50. | BVUDIV
  51. | BVSUB
  52. | BVSREM_UNDERSCORE_I
  53. | BVSREM
  54. | BVSMOD_UNDERSCORE_I
  55. | BVSMOD
  56. | BVSLT
  57. | BVSLE
  58. | BVSHL
  59. | BVSGT
  60. | BVSGE
  61. | BVSDIV_UNDERSCORE_I
  62. | BVSDIV
  63. | BVREDOR
  64. | BVREDAND
  65. | BVOR
  66. | BVNOT
  67. | BVNOR
  68. | BVNEG
  69. | BVNAND
  70. | BVMUL
  71. | BVLSHR
  72. | BVI of int
  73. | BVF of Utils.BitVec.t
  74. | BVASHR
  75. | BVAR of string
  76. | BVAND
  77. | BVADD
  78. | BV of string
  79. | BOOL
  80. | BITVEC
  81. | ASSERT
  82. | ARRAY
  83. | AND
exception Error
val smts_start : + (Stdlib.Lexing.lexbuf -> token) -> + Stdlib.Lexing.lexbuf -> + Def.rsmt list
val smt_start : + (Stdlib.Lexing.lexbuf -> token) -> + Stdlib.Lexing.lexbuf -> + Def.rsmt
val smt_ans_start : + (Stdlib.Lexing.lexbuf -> token) -> + Stdlib.Lexing.lexbuf -> + Def.rsmt_ans
val exp_start : + (Stdlib.Lexing.lexbuf -> token) -> + Stdlib.Lexing.lexbuf -> + (Def.lrng, string, string, Def.Size.t) Ott.exp
diff --git a/doc/html/read-dwarf/AstGen/Parser_pp/index.html b/doc/html/read-dwarf/AstGen/Parser_pp/index.html index c7fdf775..7c5e947e 100644 --- a/doc/html/read-dwarf/AstGen/Parser_pp/index.html +++ b/doc/html/read-dwarf/AstGen/Parser_pp/index.html @@ -1,2 +1,32 @@ -Parser_pp (read-dwarf.AstGen.Parser_pp)

Module AstGen.Parser_pp

val pp_raw_bvar : string -> PPrintEngine.document
val pp_raw_bvf : Utils.BitVec.t -> PPrintEngine.document
val pp_raw_flag : string -> PPrintEngine.document
val pp_raw_vvar : int -> PPrintEngine.document
val pp_raw_name : string -> PPrintEngine.document
val pp_raw_enum_ty : int -> PPrintEngine.document
val pp_raw_enum : Ott.enum -> PPrintEngine.document
val pp_raw_int : int -> PPrintEngine.document
val pp_raw_bvi : int -> PPrintEngine.document
val pp_raw_bv : string -> PPrintEngine.document
val pp_raw_str : string -> PPrintEngine.document
val pp_raw_mem_size : Def.Size.t -> Utils.Pp.document
val pp_raw_binmem : Def.Size.t Ott.binmem -> PPrintEngine.document
val pp_raw_var : ('a -> PPrintEngine.document) -> 'a -> PPrintEngine.document
val pp_raw_bbvar : string -> PPrintEngine.document
val pp_raw_bind : ('a -> PPrintEngine.document) -> (string * ('b'a, string, Def.Size.t) Ott.exp) -> PPrintEngine.document
val pp_raw_exp : ('a -> PPrintEngine.document) -> ('b'a, string, Def.Size.t) Ott.exp -> PPrintEngine.document
val pp_raw_fbool : (string * bool) -> PPrintEngine.document
val pp_raw_smt : ('a -> PPrintEngine.document) -> ('b'a, string, Def.Size.t) Ott.smt -> PPrintEngine.document
val pp_raw_smts : ('a -> PPrintEngine.document) -> ('b'a, string, Def.Size.t) Ott.smt list -> PPrintEngine.document
val pp_raw_smt_ans : ('a -> PPrintEngine.document) -> ('b'a, string, Def.Size.t) Ott.smt_ans -> PPrintEngine.document
val pp_raw_ty : Def.Size.t Ott.ty -> PPrintEngine.document
val pp_raw_bool : bool -> PPrintEngine.document
val pp_raw_unop : Ott.unop -> PPrintEngine.document
val pp_raw_bvarith : Ott.bvarith -> PPrintEngine.document
val pp_raw_bvcomp : Ott.bvcomp -> PPrintEngine.document
val pp_raw_binop : Def.Size.t Ott.binop -> PPrintEngine.document
val pp_raw_bvmanyarith : Ott.bvmanyarith -> PPrintEngine.document
val pp_raw_manyop : Ott.manyop -> PPrintEngine.document
val pp_bvar : string -> PPrintEngine.document
val pp_bvf : Utils.BitVec.t -> PPrintEngine.document
val pp_flag : string -> PPrintEngine.document
val pp_vvar : int -> PPrintEngine.document
val pp_name : string -> PPrintEngine.document
val pp_enum_ty : int -> PPrintEngine.document
val pp_enum : Ott.enum -> PPrintEngine.document
val pp_int : int -> PPrintEngine.document
val pp_bvi : int -> PPrintEngine.document
val pp_bv : string -> PPrintEngine.document
val pp_str : string -> PPrintEngine.document
val pp_j : int -> string
val pp_mem_size : Def.Size.t -> Utils.Pp.document
val pp_binmem : Def.Size.t Ott.binmem -> PPrintEngine.document
val pp_var : ('a -> PPrintEngine.document) -> 'a -> PPrintEngine.document
val pp_bbvar : string -> PPrintEngine.document
val pp_bind : ('a -> PPrintEngine.document) -> (string * ('b'a, string, Def.Size.t) Ott.exp) -> PPrintEngine.document
val pp_exp : ('a -> PPrintEngine.document) -> ('b'a, string, Def.Size.t) Ott.exp -> PPrintEngine.document
val pp_fbool : (string * bool) -> PPrintEngine.document
val pp_smt : ('a -> PPrintEngine.document) -> ('b'a, string, Def.Size.t) Ott.smt -> PPrintEngine.document
val pp_smts : ('a -> PPrintEngine.document) -> ('b'a, string, Def.Size.t) Ott.smt list -> PPrintEngine.document
val pp_smt_ans : ('a -> PPrintEngine.document) -> ('b'a, string, Def.Size.t) Ott.smt_ans -> PPrintEngine.document
val pp_ty : Def.Size.t Ott.ty -> PPrintEngine.document
val pp_bool : bool -> PPrintEngine.document
val pp_unop : Ott.unop -> PPrintEngine.document
val pp_bvarith : Ott.bvarith -> PPrintEngine.document
val pp_bvcomp : Ott.bvcomp -> PPrintEngine.document
val pp_binop : Def.Size.t Ott.binop -> PPrintEngine.document
val pp_bvmanyarith : Ott.bvmanyarith -> PPrintEngine.document
val pp_manyop : Ott.manyop -> PPrintEngine.document
\ No newline at end of file +Parser_pp (read-dwarf.AstGen.Parser_pp)

Module AstGen.Parser_pp

val pp_raw_bvar : string -> PPrint.document
val pp_raw_bvf : Utils.BitVec.t -> PPrint.document
val pp_raw_flag : string -> PPrint.document
val pp_raw_vvar : int -> PPrint.document
val pp_raw_name : string -> PPrint.document
val pp_raw_enum_ty : int -> PPrint.document
val pp_raw_enum : Ott.enum -> PPrint.document
val pp_raw_nat : int -> PPrint.document
val pp_raw_bvi : int -> PPrint.document
val pp_raw_bv : string -> PPrint.document
val pp_raw_str : string -> PPrint.document
val pp_raw_mem_size : Def.Size.t -> Utils.Pp.document
val pp_raw_binmem : Def.Size.t Ott.binmem -> PPrint.document
val pp_raw_var : ('a -> PPrint.document) -> 'a -> PPrint.document
val pp_raw_bbvar : string -> PPrint.document
val pp_raw_bind : + ('a -> PPrint.document) -> + (string * ('b, 'a, string, Def.Size.t) Ott.exp) -> + PPrint.document
val pp_raw_exp : + ('a -> PPrint.document) -> + ('b, 'a, string, Def.Size.t) Ott.exp -> + PPrint.document
val pp_raw_fbool : (string * bool) -> PPrint.document
val pp_raw_smt : + ('a -> PPrint.document) -> + ('b, 'a, string, Def.Size.t) Ott.smt -> + PPrint.document
val pp_raw_smts : + ('a -> PPrint.document) -> + ('b, 'a, string, Def.Size.t) Ott.smt list -> + PPrint.document
val pp_raw_smt_ans : + ('a -> PPrint.document) -> + ('b, 'a, string, Def.Size.t) Ott.smt_ans -> + PPrint.document
val pp_raw_int : int -> PPrint.document
val pp_raw_ty : Def.Size.t Ott.ty -> PPrint.document
val pp_raw_bool : bool -> PPrint.document
val pp_raw_unop : Ott.unop -> PPrint.document
val pp_raw_bvarith : Ott.bvarith -> PPrint.document
val pp_raw_bvcomp : Ott.bvcomp -> PPrint.document
val pp_raw_binop : Def.Size.t Ott.binop -> PPrint.document
val pp_raw_bvmanyarith : Ott.bvmanyarith -> PPrint.document
val pp_raw_manyop : Ott.manyop -> PPrint.document
val pp_bvar : string -> PPrint.document
val pp_bvf : Utils.BitVec.t -> PPrint.document
val pp_flag : string -> PPrint.document
val pp_vvar : int -> PPrint.document
val pp_name : string -> PPrint.document
val pp_enum_ty : int -> PPrint.document
val pp_enum : Ott.enum -> PPrint.document
val pp_nat : int -> PPrint.document
val pp_bvi : int -> PPrint.document
val pp_bv : string -> PPrint.document
val pp_str : string -> PPrint.document
val pp_j : int -> string
val pp_mem_size : Def.Size.t -> Utils.Pp.document
val pp_binmem : Def.Size.t Ott.binmem -> PPrint.document
val pp_var : ('a -> PPrint.document) -> 'a -> PPrint.document
val pp_bbvar : string -> PPrint.document
val pp_bind : + ('a -> PPrint.document) -> + (string * ('b, 'a, string, Def.Size.t) Ott.exp) -> + PPrint.document
val pp_exp : + ('a -> PPrint.document) -> + ('b, 'a, string, Def.Size.t) Ott.exp -> + PPrint.document
val pp_fbool : (string * bool) -> PPrint.document
val pp_smt : + ('a -> PPrint.document) -> + ('b, 'a, string, Def.Size.t) Ott.smt -> + PPrint.document
val pp_smts : + ('a -> PPrint.document) -> + ('b, 'a, string, Def.Size.t) Ott.smt list -> + PPrint.document
val pp_smt_ans : + ('a -> PPrint.document) -> + ('b, 'a, string, Def.Size.t) Ott.smt_ans -> + PPrint.document
val pp_int : int -> PPrint.document
val pp_ty : Def.Size.t Ott.ty -> PPrint.document
val pp_bool : bool -> PPrint.document
val pp_unop : Ott.unop -> PPrint.document
val pp_bvarith : Ott.bvarith -> PPrint.document
val pp_bvcomp : Ott.bvcomp -> PPrint.document
val pp_binop : Def.Size.t Ott.binop -> PPrint.document
val pp_bvmanyarith : Ott.bvmanyarith -> PPrint.document
val pp_manyop : Ott.manyop -> PPrint.document
diff --git a/doc/html/read-dwarf/AstGen/index.html b/doc/html/read-dwarf/AstGen/index.html index f331ac6a..13c57145 100644 --- a/doc/html/read-dwarf/AstGen/index.html +++ b/doc/html/read-dwarf/AstGen/index.html @@ -1,2 +1,2 @@ -AstGen (read-dwarf.AstGen)

Module AstGen

module Def : sig ... end
module Lexer : sig ... end
module Ott : sig ... end
module Parser : sig ... end
module Parser_pp : sig ... end
\ No newline at end of file +AstGen (read-dwarf.AstGen)

Module AstGen

module Def : sig ... end
module Lexer : sig ... end
module Ott : sig ... end

warning: the backend selected ignores the file structure informations

module Parser : sig ... end
module Parser_pp : sig ... end
diff --git a/doc/html/read-dwarf/BinaryAnalysis.html b/doc/html/read-dwarf/BinaryAnalysis.html index 6ebccd99..df558e32 100644 --- a/doc/html/read-dwarf/BinaryAnalysis.html +++ b/doc/html/read-dwarf/BinaryAnalysis.html @@ -1,2 +1,2 @@ -BinaryAnalysis (read-dwarf.BinaryAnalysis)

Binary Analysis

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.

Analyse

TODO: There is a lot a binary analysis code in Analyse* modules that should be documented.

ELF

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

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.

\ No newline at end of file +BinaryAnalysis (read-dwarf.BinaryAnalysis)

Binary Analysis

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.

Analyse

TODO: There is a lot a binary analysis code in Analyse* modules that should be documented.

ELF

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

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.

diff --git a/doc/html/read-dwarf/CLI.html b/doc/html/read-dwarf/CLI.html index 01570f7b..76c7fb20 100644 --- a/doc/html/read-dwarf/CLI.html +++ b/doc/html/read-dwarf/CLI.html @@ -1,2 +1,2 @@ -CLI (read-dwarf.CLI)

Command line interface

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.

Common options

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.

Low-level isla testing

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.

Information dumping

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.

Symbolic Execution

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.

Cache maintenance

The read-dwarf cache sub-command (in Utils.Cache.cmd) provides some cache maintenance operations.

\ No newline at end of file +CLI (read-dwarf.CLI)

Command line interface

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.

Common options

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.

Low-level isla testing

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.

Information dumping

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.

Symbolic Execution

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.

Cache maintenance

The read-dwarf cache sub-command (in Utils.Cache.cmd) provides some cache maintenance operations.

diff --git a/doc/html/read-dwarf/Config/Arch/index.html b/doc/html/read-dwarf/Config/Arch/index.html index 2e35b5e1..4c42bca2 100644 --- a/doc/html/read-dwarf/Config/Arch/index.html +++ b/doc/html/read-dwarf/Config/Arch/index.html @@ -1,2 +1,2 @@ -Arch (read-dwarf.Config.Arch)

Module Config.Arch

This module provides an enumeration of architecture for internal identification

type t =
| X86
| X86_64
| PpC
| PpC64
| ARM
| AARCH64

The supported architectures

val to_string : t -> string
val of_string : string -> t
val pp : t -> Utils.Pp.document
val size : t -> int
val fmt : Stdlib.Format.formatter -> t -> unit
val conv : t Cmdliner.Arg.conv
\ No newline at end of file +Arch (read-dwarf.Config.Arch)

Module Config.Arch

This module provides an enumeration of architecture for internal identification

type t =
  1. | X86
  2. | X86_64
  3. | PpC
  4. | PpC64
  5. | ARM
  6. | AARCH64
  7. | RISCV64

The supported architectures

val to_string : t -> string
val of_string : string -> t
val pp : t -> Utils.Pp.document
val size : t -> int
val fmt : Stdlib.Format.formatter -> t -> unit
val conv : t Cmdliner.Arg.conv
diff --git a/doc/html/read-dwarf/Config/CommonOpt/index.html b/doc/html/read-dwarf/Config/CommonOpt/index.html index 481df931..a17b71ad 100644 --- a/doc/html/read-dwarf/Config/CommonOpt/index.html +++ b/doc/html/read-dwarf/Config/CommonOpt/index.html @@ -1,2 +1,7 @@ -CommonOpt (read-dwarf.Config.CommonOpt)

Module Config.CommonOpt

This module provide support for common command line option to be used across multiple subcomands.

It also provide some utilities on the command line.

Exit codes

val exits : Cmdliner.Term.exit_info list

Description of read-dwarf exit codes. May need to be updated

Config options

This section is to manage the configuration file. See Config.File to see how the configuration file works

val config : unit Cmdliner.Term.t
val arch_val : 'a -> Arch.t option -> Arch.t
val arch_opt : Arch.t option Cmdliner.Term.t
val arch : Arch.t Cmdliner.Term.t

Isla options

val isla_client_ref : string Stdlib.ref

The isla_client path

val isla_client : unit Cmdliner.Term.t

Z3 options

val z3_ref : string Stdlib.ref

The z3 command

val z3 : unit Cmdliner.Term.t

The z3 option

Common option list

val comopts : unit Cmdliner.Term.t list

The list of common options. Almost all sub-commands should use this.

val quick_exe : name:string -> doc:string -> (unit -> 'a) -> unit
\ No newline at end of file +CommonOpt (read-dwarf.Config.CommonOpt)

Module Config.CommonOpt

This module provide support for common command line option to be used across multiple subcomands.

It also provide some utilities on the command line.

Exit codes

val exits : Cmdliner.Cmd.Exit.info list

Description of read-dwarf exit codes. May need to be updated

Config options

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 config : unit Cmdliner.Term.t
val arch_val : 'a -> Arch.t option -> Arch.t
val arch_opt : Arch.t option Cmdliner.Term.t
val arch : Arch.t Cmdliner.Term.t

Isla options

val isla_client_ref : string Stdlib.ref

The isla_client path

val isla_client : unit Cmdliner.Term.t

Z3 options

val z3_ref : string Stdlib.ref

The z3 command

val z3 : unit Cmdliner.Term.t

The z3 option

Common option list

val comopts : unit Cmdliner.Term.t list

The list of common options. Almost all sub-commands should use this.

val command_tuple_to_cmd_t : + ('a Cmdliner.Term.t * Cmdliner.Cmd.info) -> + 'a Cmdliner.Cmd.t
val grouped_exe : + (unit Cmdliner.Term.t * Cmdliner.Cmd.info) list -> + (unit Cmdliner.Term.t * Cmdliner.Cmd.info) -> + 'a
val quick_exe : name:string -> doc:string -> (unit -> unit) -> 'a
diff --git a/doc/html/read-dwarf/Config/File/ArchConf/Isla/index.html b/doc/html/read-dwarf/Config/File/ArchConf/Isla/index.html index bd3bbda9..20066ba3 100644 --- a/doc/html/read-dwarf/Config/File/ArchConf/Isla/index.html +++ b/doc/html/read-dwarf/Config/File/ArchConf/Isla/index.html @@ -1,2 +1,2 @@ -Isla (read-dwarf.Config.File.ArchConf.Isla)

Module ArchConf.Isla

This module provides the current isla configuration.

type t = {
ignored_regs : string list;

The list of register to be ignored

arch_file : string;

The name of the architecture.ir file to use. This is the file that was compiled with isla-sail from the sail source. Alternatively this file can be found in the isla-snapshots respository.

arch_toml : string;

The name of the architecture.toml file to use with Isla.

arch_file_digest : string;

The digest of the file in arch_file. This will be used instead of arch_file by digest.

linearize : string list;

List of sail function to be linearized. That means that if there is a control-flow branching in that function, instead of generating multiple traces, isla will generate a single trace with it-then-else expression

other_opts : string list;

List of other option to pass to isla. Ideally if an option is to be easily used, a new field of t should be created, but for quick and dirty testing, this field can be used.

}

Isla configuration option. Everything in here is salient for cache coherency which means that if any option if changed here, the whole Isla.Cache is invalidated. This is checked with digest.

val digest : t -> string

Produces a digest of the Isla configuration for invalidating the Isla.Cache when some configuration parameter changes

\ No newline at end of file +Isla (read-dwarf.Config.File.ArchConf.Isla)

Module ArchConf.Isla

This module provides the current isla configuration.

type t = {
  1. ignored_regs : string list;
    (*

    The list of register to be ignored

    *)
  2. 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.

    *)
  3. arch_toml : string;
    (*

    The name of the architecture.toml file to use with Isla.

    *)
  4. arch_file_digest : string;
    (*

    The digest of the file in arch_file. This will be used instead of arch_file by digest.

    *)
  5. 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

    *)
  6. other_opts : string list;
    (*

    List of other option to pass to isla. Ideally if an option is to be easily used, a new field of t should be created, but for quick and dirty testing, this field can be used.

    *)
}

Isla configuration option. Everything in here is salient for cache coherency which means that if any option if changed here, the whole Isla.Cache is invalidated. This is checked with digest.

val digest : t -> string

Produces a digest of the Isla configuration for invalidating the Isla.Cache when some configuration parameter changes

diff --git a/doc/html/read-dwarf/Config/File/ArchConf/index.html b/doc/html/read-dwarf/Config/File/ArchConf/index.html index df2522cf..e2430c93 100644 --- a/doc/html/read-dwarf/Config/File/ArchConf/index.html +++ b/doc/html/read-dwarf/Config/File/ArchConf/index.html @@ -1,2 +1,2 @@ -ArchConf (read-dwarf.Config.File.ArchConf)

Module File.ArchConf

module Isla : sig ... end

This module provides the current isla configuration.

type t = {
arch : Arch.t;

The architecture targeted by this record

toolchain : string;

The toolchain prefix for using this architecture with GNU binutils like objdump and as.

isla : Isla.t;

The isla configuration for this architecture

}

The list of all architecture specific configuration options

\ No newline at end of file +ArchConf (read-dwarf.Config.File.ArchConf)

Module File.ArchConf

module Isla : sig ... end

This module provides the current isla configuration.

type t = {
  1. arch : Arch.t;
    (*

    The architecture targeted by this record

    *)
  2. toolchain : string;
    (*

    The toolchain prefix for using this architecture with GNU binutils like objdump and as.

    *)
  3. isla : Isla.t;
    (*

    The isla configuration for this architecture

    *)
}

The list of all architecture specific configuration options

diff --git a/doc/html/read-dwarf/Config/File/Z3/index.html b/doc/html/read-dwarf/Config/File/Z3/index.html index 3c59d8b7..c00dada2 100644 --- a/doc/html/read-dwarf/Config/File/Z3/index.html +++ b/doc/html/read-dwarf/Config/File/Z3/index.html @@ -1,2 +1,2 @@ -Z3 (read-dwarf.Config.File.Z3)

Module File.Z3

type t = {
timeout : int option;

Timeout for individual requests in milliseconds

memory : int option;

Maximum memory, in MegaBytes

simplify_opts : (string * bool) list;

List of option for the simplify command

}
\ No newline at end of file +Z3 (read-dwarf.Config.File.Z3)

Module File.Z3

type t = {
  1. timeout : int option;
    (*

    Timeout for individual requests in milliseconds

    *)
  2. memory : int option;
    (*

    Maximum memory, in MegaBytes

    *)
  3. simplify_opts : (string * bool) list;
    (*

    List of option for the simplify command

    *)
}
diff --git a/doc/html/read-dwarf/Config/File/index.html b/doc/html/read-dwarf/Config/File/index.html index 87ced6a2..330a91ff 100644 --- a/doc/html/read-dwarf/Config/File/index.html +++ b/doc/html/read-dwarf/Config/File/index.html @@ -1,2 +1,2 @@ -File (read-dwarf.Config.File)

Module Config.File

This module is to handle the configuration file of the program.

For now I expect a TOML format and use the toml library from opam. The data structure themselves are format agnostic so if we change the TOML library of change of format, only this file must be modified. For example to JSON or YAML.

The top level usage is to first set the file reference which will be done by CommonOpt.config using ensure_loaded. Then the config can be accessed with the various Accessors

Configuration structure

All field of records in this section have a matching line in the checked-in config.toml. If something is optional here, and should be disabled in the default config.toml, put a commented line there, so that the correspondence can be immediately seen.

module ArchConf : sig ... end
module Z3 : sig ... end
type archs_type = (Arch.tArchConf.t) Stdlib.Hashtbl.t

Each architecture specific configuration is specified by a TOML section represented by a Arch.t record. They are stored in this type

type t = {
arch : Arch.t option;

The default architecture to be choosen when no ELF file is specified on the CLI. This is optional, but if supplied, it will override Config.arch

archs : archs_type;

All the architecture specific configurations

z3 : Z3.t;

The Z3 configuration

}

File loading

This section is about loading the configuration from a TOML file

val load : string -> unit

Load the configuration file from the specified file name

val ensure_loaded : string -> unit

If the configuration file is not already loaded, load it from the specified file.

Accessors

The section provide various accessor to access directly part of the configuration

The config must be loaded otherwise UnloadedConfig will be thrown

exception UnloadedConfig

This exception will be raised if any of the accessor is called while the config is not loaded.

val get_config : unit -> t

Get all the configuration information

val get_arch_name : unit -> Arch.t

Get the default architecture name to be used if no architecture is implicitly or explicitly specified on the CLI

val get_arch_config : Arch.t -> ArchConf.t

Get the architecture configuration for a specific architecture. To get the architecture configuration of the currently enabled architecture, Call Arch.get_config

val get_isla_config : Arch.t -> ArchConf.Isla.t

Get the isla configuration for a specific architecture. To get the isla configuration of the currently enabled architecture, Call Arch.get_isla_config

val get_z3_config : unit -> Z3.t

Get the Z3 configuration

\ No newline at end of file +File (read-dwarf.Config.File)

Module Config.File

This module is to handle the configuration file of the program.

For now I expect a TOML format and use the toml library from opam. The data structure themselves are format agnostic so if we change the TOML library of change of format, only this file must be modified. For example to JSON or YAML.

The top level usage is to first set the file reference which will be done by CommonOpt.config using ensure_loaded. Then the config can be accessed with the various Accessors

Configuration structure

All field of records in this section have a matching line in the checked-in config.toml. If something is optional here, and should be disabled in the default config.toml, put a commented line there, so that the correspondence can be immediately seen.

module ArchConf : sig ... end
module Z3 : sig ... end
type archs_type = (Arch.t, ArchConf.t) Stdlib.Hashtbl.t

Each architecture specific configuration is specified by a TOML section represented by a Arch.t record. They are stored in this type

type t = {
  1. 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

    *)
  2. archs : archs_type;
    (*

    All the architecture specific configurations

    *)
  3. z3 : Z3.t;
    (*

    The Z3 configuration

    *)
}

File loading

This section is about loading the configuration from a TOML file

val load : string -> unit

Load the configuration file from the specified file name

val ensure_loaded : string -> unit

If the configuration file is not already loaded, load it from the specified file.

Accessors

The section provide various accessor to access directly part of the configuration

The config must be loaded otherwise UnloadedConfig will be thrown

exception UnloadedConfig

This exception will be raised if any of the accessor is called while the config is not loaded.

val get_config : unit -> t

Get all the configuration information

val get_arch_name : unit -> Arch.t

Get the default architecture name to be used if no architecture is implicitly or explicitly specified on the CLI

val get_arch_config : Arch.t -> ArchConf.t

Get the architecture configuration for a specific architecture. To get the architecture configuration of the currently enabled architecture, Call Arch.get_config

val get_isla_config : Arch.t -> ArchConf.Isla.t

Get the isla configuration for a specific architecture. To get the isla configuration of the currently enabled architecture, Call Arch.get_isla_config

val get_z3_config : unit -> Z3.t

Get the Z3 configuration

diff --git a/doc/html/read-dwarf/Config/index.html b/doc/html/read-dwarf/Config/index.html index 9cd46265..c245c5d6 100644 --- a/doc/html/read-dwarf/Config/index.html +++ b/doc/html/read-dwarf/Config/index.html @@ -1,2 +1,2 @@ -Config (read-dwarf.Config)

Module Config

module type S = sig ... end
include S
val config_file : string

Default config file

val z3 : string

Default Z3 command. (Can be overidden with Z3_PATH and --z3)

val isla_client : string

Default Isla command. (Can be overidden with ISLA_CLIENT and --isla)

val arch : string

The default architecture to pick when none is specified

val enable_backtrace : bool

Whether to enable backtraces or not

val enable_tests : bool

Whether to enable internal unit test and the test test sub-command

module Arch : sig ... end

This module provides an enumeration of architecture for internal identification

module File : sig ... end

This module is to handle the configuration file of the program.

module CommonOpt : sig ... end

This module provide support for common command line option to be used across multiple subcomands.

\ No newline at end of file +Config (read-dwarf.Config)

Module Config

module type S = sig ... end

This is the compile-time configuration module. It is pulled from the root config.ml file or will fallback to the default_config.ml file

include S
val config_file : string

Default config file

val z3 : string

Default Z3 command. (Can be overidden with Z3_PATH and --z3)

val isla_client : string

Default Isla command. (Can be overidden with ISLA_CLIENT and --isla)

val arch : string

The default architecture to pick when none is specified

val enable_backtrace : bool

Whether to enable backtraces or not

val enable_tests : bool

Whether to enable internal unit test and the test test sub-command

module Arch : sig ... end

This module provides an enumeration of architecture for internal identification

module File : sig ... end

This module is to handle the configuration file of the program.

module CommonOpt : sig ... end

This module provide support for common command line option to be used across multiple subcomands.

diff --git a/doc/html/read-dwarf/Config/module-type-S/index.html b/doc/html/read-dwarf/Config/module-type-S/index.html index aff10564..7a6ba68b 100644 --- a/doc/html/read-dwarf/Config/module-type-S/index.html +++ b/doc/html/read-dwarf/Config/module-type-S/index.html @@ -1,2 +1,2 @@ -S (read-dwarf.Config.S)

Module type Config.S

val config_file : string

Default config file

val z3 : string

Default Z3 command. (Can be overidden with Z3_PATH and --z3)

val isla_client : string

Default Isla command. (Can be overidden with ISLA_CLIENT and --isla)

val arch : string

The default architecture to pick when none is specified

val enable_backtrace : bool

Whether to enable backtraces or not

val enable_tests : bool

Whether to enable internal unit test and the test test sub-command

\ No newline at end of file +S (read-dwarf.Config.S)

Module type Config.S

This is the compile-time configuration module. It is pulled from the root config.ml file or will fallback to the default_config.ml file

All compile-time configuration options are specified here. For runtime configuration, look at Config.File.

val config_file : string

Default config file

val z3 : string

Default Z3 command. (Can be overidden with Z3_PATH and --z3)

val isla_client : string

Default Isla command. (Can be overidden with ISLA_CLIENT and --isla)

val arch : string

The default architecture to pick when none is specified

val enable_backtrace : bool

Whether to enable backtraces or not

val enable_tests : bool

Whether to enable internal unit test and the test test sub-command

diff --git a/doc/html/read-dwarf/Configuration.html b/doc/html/read-dwarf/Configuration.html index 021f746d..44deee79 100644 --- a/doc/html/read-dwarf/Configuration.html +++ b/doc/html/read-dwarf/Configuration.html @@ -1,2 +1,2 @@ -Configuration (read-dwarf.Configuration)

Configuration

Compile time configuration

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

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.

\ No newline at end of file +Configuration (read-dwarf.Configuration)

Configuration

Compile time configuration

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

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.

diff --git a/doc/html/read-dwarf/Ctype/FieldMap/index.html b/doc/html/read-dwarf/Ctype/FieldMap/index.html index 292c64a9..316e9ba1 100644 --- a/doc/html/read-dwarf/Ctype/FieldMap/index.html +++ b/doc/html/read-dwarf/Ctype/FieldMap/index.html @@ -1,6 +1,11 @@ -FieldMap (read-dwarf.Ctype.FieldMap)

Module Ctype.FieldMap

A range map over field to represent a structure layout

type obj = field

The type of the contained object

type obj_off = obj * int

The type of an object with an offset

type t

The type of the map from address ranges to obj

val empty : t

An empty RngMap

val is_in : objaddr:int -> obj -> int -> bool

Test if an address is inside the object at address objaddr

val at : t -> int -> obj

Get the object containing the address. Throw Not_found if no object contains the address

val at_opt : t -> int -> obj option

Get the object containing the address. None if no object contains the address

val at_off : t -> int -> obj_off

Get the object containing the address and the offset of the address inside the object

at_off map addr = (obj, off) 

means:

         |                      |           |         |
-       map 0                 obj start    point    obj end
-         |<--------------addr-------------->|
-                                |<---off--->|
-                                |<------len obj------>|

In other words, at_off allow a change of coordinate from the map frame to the object frame.

Throw Not_found if no object contains the address

val at_off_opt : t -> int -> obj_off option

Get the object containing the address and the offset of the address inside the object. See at_off for more explanation.

None if no object contains the address

val update : (obj -> obj) -> t -> int -> t

Update the binding containing the provided address. If no binding contained the address, this is a no-op

val map : (obj -> obj) -> t -> t

Map a function over all the objects

val mapi : (int -> obj -> obj) -> t -> t

Map a function over all the objects with their address

val iter : (obj -> unit) -> t -> unit

Iter a function over all the objects

val iteri : (int -> obj -> unit) -> t -> unit

Iter a function over all the objects with their address

val clear_at : t -> int -> t

Clear the object containing the address if any

val clear : t -> pos:int -> len:int -> t

Clear an area of the RngMap.

If an object is partially in the specified block. It will be removed entirely.

See clear_crop for a different behavior. See clear_bounds to allow some bounds to be infinity.

val clear_crop : t -> pos:int -> len:int -> crop:(pos:int -> len:int -> obj -> obj) -> t

Clear an area of the RngMap.

If a block is partially in the specified block, It will be cropped by using the provided crop function.

crop ~pos ~len obj is supposed to crop the object obj and keep only the segment [pos:pos +len) of it (in the object coordinate frame).

val clear_bounds : ?⁠start:int -> ?⁠endp:int -> t -> t

Same as clear but if a bound is missing, then we erase until infinity in that direction. The target interval is [start:endp).

In particular clear_bounds map = empty.

val add : t -> int -> obj -> t

Add an object at a specific address. The whole range of addresses covered by the object must be free

val addp : t -> obj_off -> t

Add an object at a specific address. The whole range of addresses covered by the object must be free

val bindings : t -> (int * obj) list

Give the list of bindings

val to_seq : ?⁠start:int -> ?⁠endp:int -> t -> (int * obj) Utils.Seq.t

Return a sequence of all the object overlapping the range [start:endp). The first and last element may not be entierly contained in the ranged. If any bound is unspecified, it goes to infinity in that direction.

In particular to_seq map will iterate the entiere RngMap

\ No newline at end of file +FieldMap (read-dwarf.Ctype.FieldMap)

Module Ctype.FieldMap

A range map over field to represent a structure layout

type obj = field

The type of the contained object

type obj_off = obj * int

The type of an object with an offset

type t

The type of the map from address ranges to obj

val empty : t

An empty RngMap

val is_in : objaddr:int -> obj -> int -> bool

Test if an address is inside the object at address objaddr

val at : t -> int -> obj

Get the object containing the address. Throw Not_found if no object contains the address

val at_opt : t -> int -> obj option

Get the object containing the address. None if no object contains the address

val at_off : t -> int -> obj_off

Get the object containing the address and the offset of the address inside the object

at_off map addr = (obj, off) 

means:

   |                      |           |         |
+ map 0                 obj start    point    obj end
+   |<--------------addr-------------->|
+                          |<---off--->|
+                          |<------len obj------>|

In other words, at_off allow a change of coordinate from the map frame to the object frame.

Throw Not_found if no object contains the address

val at_off_opt : t -> int -> obj_off option

Get the object containing the address and the offset of the address inside the object. See at_off for more explanation.

None if no object contains the address

val update : (obj -> obj) -> t -> int -> t

Update the binding containing the provided address. If no binding contained the address, this is a no-op

val map : (obj -> obj) -> t -> t

Map a function over all the objects

val mapi : (int -> obj -> obj) -> t -> t

Map a function over all the objects with their address

val iter : (obj -> unit) -> t -> unit

Iter a function over all the objects

val iteri : (int -> obj -> unit) -> t -> unit

Iter a function over all the objects with their address

val clear_at : t -> int -> t

Clear the object containing the address if any

val clear : t -> pos:int -> len:int -> t

Clear an area of the RngMap.

If an object is partially in the specified block. It will be removed entirely.

See clear_crop for a different behavior. See clear_bounds to allow some bounds to be infinity.

val clear_crop : + t -> + pos:int -> + len:int -> + crop:(pos:int -> len:int -> obj -> obj) -> + t

Clear an area of the RngMap.

If a block is partially in the specified block, It will be cropped by using the provided crop function.

crop ~pos ~len obj is supposed to crop the object obj and keep only the segment [pos:pos +len) of it (in the object coordinate frame).

val clear_bounds : ?start:int -> ?endp:int -> t -> t

Same as clear but if a bound is missing, then we erase until infinity in that direction. The target interval is [start:endp).

In particular clear_bounds map = empty.

val add : t -> int -> obj -> t

Add an object at a specific address. The whole range of addresses covered by the object must be free

val addp : t -> obj_off -> t

Add an object at a specific address. The whole range of addresses covered by the object must be free

val bindings : t -> (int * obj) list

Give the list of bindings

val to_seq : ?start:int -> ?endp:int -> t -> (int * obj) Utils.Seq.t

Return a sequence of all the object overlapping the range [start:endp). The first and last element may not be entierly contained in the ranged. If any bound is unspecified, it goes to infinity in that direction.

In particular to_seq map will iterate the entiere RngMap

diff --git a/doc/html/read-dwarf/Ctype/index.html b/doc/html/read-dwarf/Ctype/index.html index 9bd6d26b..cb071f7c 100644 --- a/doc/html/read-dwarf/Ctype/index.html +++ b/doc/html/read-dwarf/Ctype/index.html @@ -1,2 +1,64 @@ -Ctype (read-dwarf.Ctype)

Module Ctype

This module provides the internal C-like type system. This type system is slightly different than the normal C type system. This module only provides the Ocaml datastructure to represent those types. The typing rules are implemented in Trace.Typer, where they are applied dire

These types follow the normal C type structure except for pointers that are more complex. To handle the fact that the C compiler knows perfectly the ABI and is "allowed" to used it, we have to make pointer types resist manual adjusting of pointers to point to the field of a struct. However the new pointer cannot just have type field_type* because one could want to get back a pointer to the whole structure by subtracting an offset from the field pointer. Thus a pointer must never forget information about its surroundings. Those surroundings are called a fragment and represent all the type information of the fragment of memory in which a pointer lies. The pointer is thus represented as a fragment of memory and an offset. Since the pointer type is more complex and packs more information, the surface syntax has changed. A pointer type is written between braces, so A* becomes {A}, but in more complex cases, all informations fit between the braces.

Furthermore, the fragment part of the pointer does not record any information about aliasing: two different type fragments are perfectly allowed to alias. To handle non-aliasing properties, like the stack not aliasing the heap or restrict pointers, pointers also have a provenance field. See State.Mem.

There is another problem: The C language does not define a C type system for the whole program, contrary to C++. It defines only a type system per compilation unit. This limitation is too annoying to work with so the module implements some kind of linking of types, similar to C++ rules. See C type linking: From Linksem.

DWARF constants

val vDW_ATE_address : int
val vDW_ATE_boolean : int
val vDW_ATE_signed : int
val vDW_ATE_signed_char : int
val vDW_ATE_unsigned : int
val vDW_ATE_unsigned_char : int

Types

type provenance =
| Restricted of int
| Main

This is the provenance of the pointer. This tells to which symbolic memory block a pointer points to. To get the full explanation, go to State.Mem

val pp_provenance : provenance -> Utils.Pp.document
type unqualified =
| Machine of int

Size in bytes for now

| Cint of {
name : string;
signed : bool;
size : int;
ischar : bool;
}
| 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 {
name : string;
size : int;
id : int;
}

See env for what the id refers to. The int is the size

| Array of {
elem : t;
dims : int option list;
}
| Enum of {
name : string;
id : int;
}

See env for what the id refers to

| 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

and offset =
| Const of int

Constant offset

| Somewhere

The type of an offset in a fragment

type linksem_t = Dwarf.c_type

The type of C types in Linksem

type field = {
fname : string option;
offset : int;
typ : t;
size : int;
}

A field in a structure

type linksem_field = linksem_t Dwarf.struct_union_member
module FieldMap : Utils.RngMap.S with type obj = field

A range map over field to represent a structure layout

type struc = {
layout : FieldMap.t;
name : string;
size : int;
complete : bool;
}

The type of a C structure.

A structure can be complete or incomplete but due to some internal hackery this is a need for a subtle difference with C: Even incomplete structure have a size, they just don't have any field.

A incomplete struct can and will often be complete later as the interpretation of DWARF information advances.

The name field is the linking name of the struct.

type enum = {
name : string;
labels : (int, string option) Stdlib.Hashtbl.t;
}

The representation type of a C enumeration

type cupdie_id = int * int

The identifier for a linksem_cupdie. See ids_of_cupdie

type linksem_env = linksem_t list

The type of environement linksem gives us.

Only structs, enums and unions can appear. A type can appear multiple times and also be forward-declared with missing data like size. env_of_linksem hopefully deals with all those problems

type linksem_indexed_env = (cupdie_idlinksem_t) Stdlib.Hashtbl.t

An version of the linksem environement indexed by cupdie_id

type env = {
structs : (string, struc) Utils.IdMap.t;
enums : (string, enum) Utils.IdMap.t;
lenv : linksem_indexed_env;
}

The type environment that contain mapping from linking name and a generated id

to the actual content of structs and enumerations.

Linking names can be:

  • A plain name for a struct/enum declared with a tag.
  • typedef.name for an unnamed struct declared in a typedef
  • outer.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 ptr_size : int

The size of pointer. This will be configurable later

val enum_size : int

The size of enums. This will be configurable later

Comparisons

val equal : t -> t -> bool

Convenience manipulation

val is_struct : t -> bool
val is_array : t -> bool
val is_ptr : t -> bool
val is_scalar : t -> bool
val is_composite : t -> bool
val is_constexpr : t -> bool
val ptr : t -> unqualified

Make a simple pointer from a type

val voidstar : unqualified

A void* pointer

val qual : ?⁠const:bool -> ?⁠volatile:bool -> ?⁠restrict:bool -> ?⁠constexpr:bool -> unqualified -> t

Create a qualified type from an unqualified type with the specified qualifiers

val add_qual : ?⁠const:bool -> ?⁠volatile:bool -> ?⁠restrict:bool -> ?⁠constexpr:bool -> t -> t

update specified qualifiers. Other qualifier are kept

val machine : ?⁠constexpr:bool -> int -> t

Create a machine type of that size without qualifiers

val of_frag : ?⁠provenance:provenance -> ?⁠offset:int -> ?⁠constexpr:bool -> ?⁠restrict:bool -> fragment -> t

Create a pointer to fragment with specified offset (0 by default)

val of_frag_somewhere : ?⁠provenance:provenance -> ?⁠constexpr:bool -> ?⁠restrict:bool -> fragment -> t

Create a pointer to fragment Somewhere

val incomplete_struct : string -> int -> struc

Build an incomplete struct with a linking name and a size

val make_env : linksem_indexed_env -> env

Makes an empty environement from and indexed linksem environement

val offset_update : offset -> int -> offset

Update an offset with an integer update

val ptr_update : t -> int -> t

Update an pointer with an integer update

val ptr_set : t -> int -> t

Set a arbitrary new offset in a pointer

val ptr_forget : t -> t

Make a pointer forget it's offset

Sizeof

This section give implementation of sizeof function.

Dynamic array have size 0 until we are able to deal with C99 last member dynamic arrays. This will mess up with type_at. TODO fix it.

val sizeof_unqualified : unqualified -> int

Give the size of an unqualified type. Need the environement.

val sizeof : t -> int

Give the size of an type. Need the environement.

val len : t -> int

For being used in RngMap.LenObject

This section contain the whole hierarchy of function used to convert type from DWARF representation to the internal type system.

During this conversion, C type linking happens. This means that structure with the same name from different compilation unit are identified as being the same. If they do not have the same layout, an error is raised. This would mean that either the C program does very weird thing with it's type that we don't want to verify or more likely that it is ill-formed. As we have to deal with anonymous struct, name are a bit more complex than plain struct tags. See env for a description.

The top-level interface for types is of_linksem and the top-level interface for environement is env_of_linksem. Those will be called by Dw at DWARF loading time to generate a coherent type system.

type conversion_context = {
env : env;
}

This type is a conversion context.

Its role is to contain all the things that all the functions in this section will need to convert types.

val ids_of_cupdie : Dwarf.cupdie -> cupdie_id

Get the id of a linksem cupdie

val pp_decl : Dwarf.decl -> PPrintEngine.document

Pretty print the dwarf decl type

TODO: Move that in the appropriate place

exception LinkError

This exception is raised when the type we are trying to reach must came from another translation unit or later in the current one.

The information is incomplete at this moment to create is.

Normally this exception should only happen during the initial env_of_linksem. If it happens elsewhere, either the code used an anonymous struct that do not have C++-like linkage or the compiler did not do its job.

val base_type_of_linksem : ?⁠size:Z.t -> encoding:Z.t -> string -> unqualified

Convert a base type with a name and encoding and maybe a size to its inner representation

Only integers, chars and bools supported. No floating points

val field_of_linksem : cc:conversion_context -> linksem_field -> FieldMap.obj
val field_map_of_linksem : string -> cc:conversion_context -> linksem_field list -> FieldMap.t
val struc_of_linksem : cc:conversion_context -> string -> int -> linksem_field list -> struc
val struct_type_of_linksem : ?⁠force_complete:bool -> cc:conversion_context -> cupdie:Dwarf.cupdie -> mname:string option -> decl:Dwarf.decl -> unqualified

Build a struct from it's cupdie and name. If force_complete is true and the struct is incomplete. It will try to complete is using cupdie and throw LinkError if it fails.

val enum_of_linksem : cc:conversion_context -> string -> Dwarf.enumeration_member list -> enum
val enum_type_of_linksem : cc:conversion_context -> cupdie:Dwarf.cupdie -> mname:string option -> decl:Dwarf.decl -> unqualified
val unqualified_of_linksem : ?⁠force_complete:bool -> cc:conversion_context -> linksem_t -> unqualified

Convert an unqualified type. Union are just Machine n where n is their size

val of_linksem_none : unit -> t

Placeholder for whatever the right thing to do is when linksem found no type - TODO: fix

val of_linksem_cc : ?⁠force_complete:bool -> cc:conversion_context -> ?⁠const:bool -> ?⁠volatile:bool -> ?⁠restrict:bool -> linksem_t -> t

The main of_linksem that take a full conversion_context and qualifiers.

The user friendly version is of_linksem

See struct_type_of_linksem for the explanation of force_complete.

All the qualifier passed as parameter are added to the resulting type.

val of_linksem : env:env -> linksem_t -> t

The user friendly interface that convert a type using the environment. This useful when there is no additional linking information to pass on

val env_of_linksem : linksem_env -> env

The main environment conversion function.

This the function that deal with all the type linking process, forward declaration an all that stuff.

First it create the indexed linksem environment (member lenv of env),

Then it registers all named structs as incomplete in the environment (to deal with self recursion, only named structs can self-recurse).

Finally it run of_linksem_cc on all the types with force_complete on. During this phase it ignore all LinkError that arise. It assumes that if some thing was incomplete at that point, it will become complete later.

Then it can return the freshly built environment.

If some structs are still incomplete, but are actually used, the LinkError will be raised at the time of use.

Pretty printing

val pp_signed : bool -> PPrintEngine.document
val pp : t -> Utils.Pp.document

Pretty print a type. If an environement is provided, structs and enums will be printed with a name, otherwise they will just have a number

val pp_unqualified : unqualified -> Utils.Pp.document
val pp_fragment : fragment -> Utils.Pp.document
val pp_offset : offset -> Utils.Pp.document
val pp_arr : t -> int option list -> Utils.Pp.document
val pp_arr_dim : int option -> Utils.Pp.document
val pp_field : field -> PPrintEngine.document
val pp_struct : struc -> Utils.Pp.document
val pp_enums : enum -> Utils.Pp.document
val pp_env : env -> Utils.Pp.document

Print the whole environement (not the linksem indexed environment)

Type at

This section is about getting the type at a specific offset in things.

val struct_at : env:env -> size:int -> struc -> int -> t Utils.Option.t

Same as type_at but for structs

val type_at : env:env -> size:int -> t -> int -> t Utils.Option.t

Get the type of size size at the provided offset in another type

\ No newline at end of file +Ctype (read-dwarf.Ctype)

Module Ctype

This module provides the internal C-like type system. This type system is slightly different than the normal C type system. This module only provides the Ocaml datastructure to represent those types. The typing rules are implemented in Trace.Typer, where they are applied dire

These types follow the normal C type structure except for pointers that are more complex. To handle the fact that the C compiler knows perfectly the ABI and is "allowed" to used it, we have to make pointer types resist manual adjusting of pointers to point to the field of a struct. However the new pointer cannot just have type field_type* because one could want to get back a pointer to the whole structure by subtracting an offset from the field pointer. Thus a pointer must never forget information about its surroundings. Those surroundings are called a fragment and represent all the type information of the fragment of memory in which a pointer lies. The pointer is thus represented as a fragment of memory and an offset. Since the pointer type is more complex and packs more information, the surface syntax has changed. A pointer type is written between braces, so A* becomes {A}, but in more complex cases, all informations fit between the braces.

Furthermore, the fragment part of the pointer does not record any information about aliasing: two different type fragments are perfectly allowed to alias. To handle non-aliasing properties, like the stack not aliasing the heap or restrict pointers, pointers also have a provenance field. See State.Mem.

There is another problem: The C language does not define a C type system for the whole program, contrary to C++. It defines only a type system per compilation unit. This limitation is too annoying to work with so the module implements some kind of linking of types, similar to C++ rules. See C type linking: From Linksem.

DWARF constants

val vDW_ATE_address : int
val vDW_ATE_boolean : int
val vDW_ATE_signed : int
val vDW_ATE_signed_char : int
val vDW_ATE_unsigned : int
val vDW_ATE_unsigned_char : int

Types

type provenance =
  1. | Restricted of int
  2. | Main

This is the provenance of the pointer. This tells to which symbolic memory block a pointer points to. To get the full explanation, go to State.Mem

val pp_provenance : provenance -> Utils.Pp.document
type unqualified =
  1. | Machine of int
    (*

    Size in bytes for now

    *)
  2. | Cint of {
    1. name : string;
    2. signed : bool;
    3. size : int;
    4. ischar : bool;
    }
  3. | Cbool
  4. | Ptr of {
    1. fragment : fragment;
    2. offset : offset;
    3. 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).

    *)
  5. | Struct of {
    1. name : string;
    2. size : int;
    3. id : int;
    }
    (*

    See env for what the id refers to. The int is the size

    *)
  6. | Array of {
    1. elem : t;
    2. dims : int option list;
    }
  7. | Enum of {
    1. name : string;
    2. id : int;
    }
    (*

    See env for what the id refers to

    *)
  8. | FuncPtr
    (*

    Hack to accommodate PKVM

    *)
  9. | Missing
    (*

    Hack to accommodate PKVM

    *)
  10. | Bits
    (*

    Hack to prevent losing type information when processing bitvectors with non-whole-byte sizes

    *)

The unqualified part of the C type without const volatile, ...

and t = {
  1. unqualified : unqualified;
  2. const : bool;
  3. volatile : bool;
  4. restrict : bool;
  5. constexpr : bool;
}

The internal representation of generalized C types

and fragment =
  1. | Unknown
    (*

    Unknown type, But without possibility of learning

    *)
  2. | Single of t
    (*

    Single object: Only when accessing of a global variable

    *)
  3. | DynArray of t
    (*

    Generic C pointer, may point to multiple element of that type

    *)
  4. | DynFragment of int
    (*

    Writable fragment for memory whose type is changing dynamically

    *)
  5. | Global of string
    (*

    The Global fragment that contains all the fixed ELF section .text, .data, .rodata, ...

    *)

The type of a memory fragment

and offset =
  1. | Const of int
    (*

    Constant offset

    *)
  2. | Somewhere

The type of an offset in a fragment

type linksem_t = Dwarf.c_type

The type of C types in Linksem

type field = {
  1. fname : string option;
  2. offset : int;
  3. typ : t;
  4. size : int;
}

A field in a structure

type linksem_field = linksem_t Dwarf.struct_union_member
module FieldMap : Utils.RngMap.S with type obj = field

A range map over field to represent a structure layout

type struc = {
  1. layout : FieldMap.t;
  2. name : string;
  3. size : int;
  4. 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 = {
  1. name : string;
  2. labels : (int, string option) Stdlib.Hashtbl.t;
}

The representation type of a C enumeration

type cupdie_id = int * int

The identifier for a linksem_cupdie. See ids_of_cupdie

type linksem_env = linksem_t list

The type of environement linksem gives us.

Only structs, enums and unions can appear. A type can appear multiple times and also be forward-declared with missing data like size. env_of_linksem hopefully deals with all those problems

type linksem_indexed_env = (cupdie_id, linksem_t) Stdlib.Hashtbl.t

An version of the linksem environement indexed by cupdie_id

type env = {
  1. structs : (string, struc) Utils.IdMap.t;
  2. enums : (string, enum) Utils.IdMap.t;
  3. 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:

  • A plain name for a struct/enum declared with a tag.
  • typedef.name for an unnamed struct declared in a typedef
  • outer.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 ptr_size : int

The size of pointer. This will be configurable later

val enum_size : int

The size of enums. This will be configurable later

Comparisons

val equal : t -> t -> bool

Convenience manipulation

val is_struct : t -> bool
val is_array : t -> bool
val is_ptr : t -> bool
val is_scalar : t -> bool
val is_composite : t -> bool
val is_constexpr : t -> bool
val ptr : t -> unqualified

Make a simple pointer from a type

val voidstar : unqualified

A void* pointer

val qual : + ?const:bool -> + ?volatile:bool -> + ?restrict:bool -> + ?constexpr:bool -> + unqualified -> + t

Create a qualified type from an unqualified type with the specified qualifiers

val add_qual : + ?const:bool -> + ?volatile:bool -> + ?restrict:bool -> + ?constexpr:bool -> + t -> + t

update specified qualifiers. Other qualifier are kept

val machine : ?constexpr:bool -> int -> t

Create a machine type of that size without qualifiers

val of_frag : + ?provenance:provenance -> + ?offset:int -> + ?constexpr:bool -> + ?restrict:bool -> + fragment -> + t

Create a pointer to fragment with specified offset (0 by default)

val of_frag_somewhere : + ?provenance:provenance -> + ?constexpr:bool -> + ?restrict:bool -> + fragment -> + t

Create a pointer to fragment Somewhere

val incomplete_struct : string -> int -> struc

Build an incomplete struct with a linking name and a size

val make_env : linksem_indexed_env -> env

Makes an empty environement from and indexed linksem environement

val offset_update : offset -> int -> offset

Update an offset with an integer update

val ptr_update : t -> int -> t

Update an pointer with an integer update

val ptr_set : t -> int -> t

Set a arbitrary new offset in a pointer

val ptr_forget : t -> t

Make a pointer forget it's offset

Sizeof

This section give implementation of sizeof function.

Dynamic array have size 0 until we are able to deal with C99 last member dynamic arrays. This will mess up with type_at. TODO fix it.

val sizeof_unqualified : unqualified -> int

Give the size of an unqualified type. Need the environement.

val sizeof : t -> int

Give the size of an type. Need the environement.

val len : t -> int

For being used in RngMap.LenObject

This section contain the whole hierarchy of function used to convert type from DWARF representation to the internal type system.

During this conversion, C type linking happens. This means that structure with the same name from different compilation unit are identified as being the same. If they do not have the same layout, an error is raised. This would mean that either the C program does very weird thing with it's type that we don't want to verify or more likely that it is ill-formed. As we have to deal with anonymous struct, name are a bit more complex than plain struct tags. See env for a description.

The top-level interface for types is of_linksem and the top-level interface for environement is env_of_linksem. Those will be called by Dw at DWARF loading time to generate a coherent type system.

type conversion_context = {
  1. env : env;
}

This type is a conversion context.

Its role is to contain all the things that all the functions in this section will need to convert types.

val ids_of_cupdie : Dwarf.cupdie -> cupdie_id

Get the id of a linksem cupdie

val pp_decl : Dwarf.decl -> Utils.Pp.document

Pretty print the dwarf decl type

TODO: Move that in the appropriate place

exception LinkError

This exception is raised when the type we are trying to reach must came from another translation unit or later in the current one.

The information is incomplete at this moment to create is.

Normally this exception should only happen during the initial env_of_linksem. If it happens elsewhere, either the code used an anonymous struct that do not have C++-like linkage or the compiler did not do its job.

val base_type_of_linksem : + ?size:Sym_ocaml.Num.t -> + encoding:Sym_ocaml.Num.t -> + string -> + unqualified

Convert a base type with a name and encoding and maybe a size to its inner representation

Only integers, chars and bools supported. No floating points

val field_of_linksem : cc:conversion_context -> linksem_field -> FieldMap.obj
val field_map_of_linksem : + string -> + cc:conversion_context -> + linksem_field list -> + FieldMap.t
val struc_of_linksem : + cc:conversion_context -> + string -> + int -> + linksem_field list -> + struc
val struct_type_of_linksem : + ?force_complete:bool -> + cc:conversion_context -> + cupdie:Dwarf.cupdie -> + mname:string option -> + decl:Dwarf.decl -> + unqualified

Build a struct from it's cupdie and name. If force_complete is true and the struct is incomplete. It will try to complete is using cupdie and throw LinkError if it fails.

val enum_of_linksem : + cc:conversion_context -> + string -> + Dwarf.enumeration_member list -> + enum
val enum_type_of_linksem : + cc:conversion_context -> + cupdie:Dwarf.cupdie -> + mname:string option -> + decl:Dwarf.decl -> + unqualified
val unqualified_of_linksem : + ?force_complete:bool -> + cc:conversion_context -> + linksem_t -> + unqualified

Convert an unqualified type. Union are just Machine n where n is their size

val of_linksem_none : unit -> t

Placeholder for whatever the right thing to do is when linksem found no type - TODO: fix

val of_linksem_cc : + ?force_complete:bool -> + cc:conversion_context -> + ?const:bool -> + ?volatile:bool -> + ?restrict:bool -> + linksem_t -> + t

The main of_linksem that take a full conversion_context and qualifiers.

The user friendly version is of_linksem

See struct_type_of_linksem for the explanation of force_complete.

All the qualifier passed as parameter are added to the resulting type.

val of_linksem : env:env -> linksem_t -> t

The user friendly interface that convert a type using the environment. This useful when there is no additional linking information to pass on

val env_of_linksem : linksem_env -> env

The main environment conversion function.

This the function that deal with all the type linking process, forward declaration an all that stuff.

First it create the indexed linksem environment (member lenv of env),

Then it registers all named structs as incomplete in the environment (to deal with self recursion, only named structs can self-recurse).

Finally it run of_linksem_cc on all the types with force_complete on. During this phase it ignore all LinkError that arise. It assumes that if some thing was incomplete at that point, it will become complete later.

Then it can return the freshly built environment.

If some structs are still incomplete, but are actually used, the LinkError will be raised at the time of use.

Pretty printing

val pp_signed : bool -> Utils.Pp.document
val pp : t -> Utils.Pp.document

Pretty print a type. If an environement is provided, structs and enums will be printed with a name, otherwise they will just have a number

val pp_unqualified : unqualified -> Utils.Pp.document
val pp_fragment : fragment -> Utils.Pp.document
val pp_offset : offset -> Utils.Pp.document
val pp_arr : t -> int option list -> Utils.Pp.document
val pp_arr_dim : int option -> Utils.Pp.document
val pp_field : field -> Utils.Pp.document
val pp_struct : struc -> Utils.Pp.document
val pp_enums : enum -> Utils.Pp.document
val pp_env : env -> Utils.Pp.document

Print the whole environement (not the linksem indexed environment)

Type at

This section is about getting the type at a specific offset in things.

val struct_at : env:env -> size:int -> struc -> int -> t Utils.Option.t

Same as type_at but for structs

val type_at : env:env -> size:int -> t -> int -> t Utils.Option.t

Get the type of size size at the provided offset in another type

diff --git a/doc/html/read-dwarf/Dw/Func/index.html b/doc/html/read-dwarf/Dw/Func/index.html index 3f539ef4..5c97503e 100644 --- a/doc/html/read-dwarf/Dw/Func/index.html +++ b/doc/html/read-dwarf/Dw/Func/index.html @@ -1,2 +1,2 @@ -Func (read-dwarf.Dw.Func)

Module Dw.Func

This module contain all the definition to handle functions as defined in the DWARF information of the target file

type func = {
name : string;
scope : scope;
ret : Ctype.t option;
}

Type of a dwarf function that may or may not be inlined

If this type stand on its own, then it is inlined. If it is inside a t then it's a top level function. There is no separate type for inline functions because they do not have any special data that a top level function may not have

and scope = {
vars : Var.t list;
funcs : func list;
scopes : scope list;
}

Type of a dwarf scope that may contain recursively other data. The lists here have the semantic meaning of sets: the order is irrelevant.

type linksem_func = Dwarf.sdt_subroutine

This is the type a dwarf function in linksem

type linksem_scope = Dwarf.sdt_lexical_block

This is the type a dwarf scope in linksem

val func_of_linksem : Elf.File.t -> Ctype.env -> linksem_func -> func

Create a Dwarf function from its linksem counterpart

val scope_of_linksem : Elf.File.t -> Ctype.env -> linksem_scope -> scope

Create and Dwarf scope from its linksem counterpart

val func_get_api : func -> Arch.func_api

Get the API of the function

val pp_raw_func : func -> Utils.Pp.OCaml.representation
val pp_raw_scope : scope -> Utils.Pp.OCaml.representation
type t = {
sym : Elf.Symbol.t option;
func : func;
}

This the type of a top-level function. It may have an associated elf symbol

This type will contain all necessary indexes for function-wide fast access to relevant dwarf information.

type linksem_t = Dwarf.sdt_subroutine

This is the type a dwarf top_level function in linksem

val of_linksem : Elf.File.t -> Ctype.env -> linksem_t -> t

Create a dwarf top level function from its Linksem counterpart. The ELF file is to get a potential matching symbol. For now the matching is made with name and then code address, but perhaps the code using this should be rephrased in terms of addresses instead of symbols, to be more resilient

val get_api : t -> Arch.func_api

Get the API of a top level function

val pp_raw : t -> Utils.Pp.document

Pretty print a raw top level function

\ No newline at end of file +Func (read-dwarf.Dw.Func)

Module Dw.Func

This module contain all the definition to handle functions as defined in the DWARF information of the target file

type func = {
  1. name : string;
  2. scope : scope;
  3. 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 = {
  1. vars : Var.t list;
  2. funcs : func list;
  3. 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.

type linksem_func = Dwarf.sdt_subroutine

This is the type a dwarf function in linksem

type linksem_scope = Dwarf.sdt_lexical_block

This is the type a dwarf scope in linksem

val func_of_linksem : Elf.File.t -> Ctype.env -> linksem_func -> func

Create a Dwarf function from its linksem counterpart

val scope_of_linksem : Elf.File.t -> Ctype.env -> linksem_scope -> scope

Create and Dwarf scope from its linksem counterpart

val func_get_api : func -> Arch.func_api

Get the API of the function

val pp_raw_func : func -> Utils.Pp.document
val pp_raw_scope : scope -> Utils.Pp.document
type t = {
  1. sym : Elf.Symbol.t option;
  2. 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.

type linksem_t = Dwarf.sdt_subroutine

This is the type a dwarf top_level function in linksem

val of_linksem : Elf.File.t -> Ctype.env -> linksem_t -> t

Create a dwarf top level function from its Linksem counterpart. The ELF file is to get a potential matching symbol. For now the matching is made with name and then code address, but perhaps the code using this should be rephrased in terms of addresses instead of symbols, to be more resilient

val get_api : t -> Arch.func_api

Get the API of a top level function

val pp_raw : t -> Utils.Pp.document

Pretty print a raw top level function

diff --git a/doc/html/read-dwarf/Dw/Loc/index.html b/doc/html/read-dwarf/Dw/Loc/index.html index dacbb921..b6774946 100644 --- a/doc/html/read-dwarf/Dw/Loc/index.html +++ b/doc/html/read-dwarf/Dw/Loc/index.html @@ -1,2 +1,2 @@ -Loc (read-dwarf.Dw.Loc)

Module Dw.Loc

This module represent architectural locations. Locations in DWARF information are represented as a little stack language (represented as dwop list) which describes how to compute the target value from the current concrete state. This computation can be arbitrarily complex, for example take half of the value from the high bits of a register and then the other half from memory at a specific address coming from another register.

Those expression are not directly usable to guide the type inference, as we would much more like simple direct information like "This value is in that register". To solve this, there is a custom type t that interpret simple static locations directly and leaves more complex location uninterpreted.

This interpretation is currently very basic. It remains to be seen if it actually need to be improved.

type dwop = Dwarf.operation

The type of a dwarf location stack operation

type t =
| Register of State.Reg.t

In the register

| RegisterOffset of State.Reg.t * int

At register + offset address

| StackFrame of int

On the stackFrame with offset

| Global of Elf.SymTable.sym_offset

Global variable with an offset

| Dwarf of dwop list

Uninterpreted dwarf location

The type of a location, as static as possible

type linksem_t = dwop list

The type of a location in linksem format

val vDW_OP_addr : int

The integer value of the DW_OP_addr constant in DWARF standard

TODO this should come from LinkSem's dwarf

val vDW_OP_reg0 : int

The integer value of the DW_OP_reg0 constant in DWARF standard

val vDW_OP_breg0 : int

The integer value of the DW_OP_breg0 constant in DWARF standard

val of_linksem : ?⁠amap:Arch.dwarf_reg_map -> Elf.File.t -> linksem_t -> t

Convert a linksem location description into a Loc.t

Very naive for now : If the list has a single element that we can translate directly, we do. Otherwise, we dump it into the t.Dwarf constructor

val to_string : t -> string

Convert the location to a string. This is not reversible

val compare : t -> t -> int

Compare two location. Loc.t is not compatible with polymorphic compare

val pp : t -> Utils.Pp.document

Pretty-print the location

\ No newline at end of file +Loc (read-dwarf.Dw.Loc)

Module Dw.Loc

This module represent architectural locations. Locations in DWARF information are represented as a little stack language (represented as dwop list) which describes how to compute the target value from the current concrete state. This computation can be arbitrarily complex, for example take half of the value from the high bits of a register and then the other half from memory at a specific address coming from another register.

Those expression are not directly usable to guide the type inference, as we would much more like simple direct information like "This value is in that register". To solve this, there is a custom type t that interpret simple static locations directly and leaves more complex location uninterpreted.

This interpretation is currently very basic. It remains to be seen if it actually need to be improved.

type dwop = Dwarf.operation

The type of a dwarf location stack operation

type t =
  1. | Register of State.Reg.t
    (*

    In the register

    *)
  2. | RegisterOffset of State.Reg.t * int
    (*

    At register + offset address

    *)
  3. | StackFrame of int
    (*

    On the stackFrame with offset

    *)
  4. | Global of Elf.SymTable.sym_offset
    (*

    Global variable with an offset

    *)
  5. | Const of Utils.Sym.t
  6. | Dwarf of dwop list
    (*

    Uninterpreted dwarf location

    *)

The type of a location, as static as possible

type linksem_t = dwop list

The type of a location in linksem format

val vDW_OP_addr : int

The integer value of the DW_OP_addr constant in DWARF standard

TODO this should come from LinkSem's dwarf

val vDW_OP_reg0 : int

The integer value of the DW_OP_reg0 constant in DWARF standard

val vDW_OP_breg0 : int

The integer value of the DW_OP_breg0 constant in DWARF standard

val of_linksem : ?amap:Arch.dwarf_reg_map -> Elf.File.t -> linksem_t -> t

Convert a linksem location description into a Loc.t

Very naive for now : If the list has a single element that we can translate directly, we do. Otherwise, we dump it into the t.Dwarf constructor

val to_string : t -> string

Convert the location to a string. This is not reversible

val compare : t -> t -> int

Compare two location. Loc.t is not compatible with polymorphic compare

val pp : t -> Utils.Pp.document

Pretty-print the location

diff --git a/doc/html/read-dwarf/Dw/Var/index.html b/doc/html/read-dwarf/Dw/Var/index.html index 994f650c..5587688d 100644 --- a/doc/html/read-dwarf/Dw/Var/index.html +++ b/doc/html/read-dwarf/Dw/Var/index.html @@ -1,2 +1,4 @@ -Var (read-dwarf.Dw.Var)

Module Dw.Var

This module contain all the definition to handle local and global variables as defined in the DWARF information of the target file

type t = {
name : string;
param : bool;
ctype : Ctype.t;
locs : ((int * int) * Loc.t) list;
}

Type of a DWARF variable

type linksem_t = Dwarf.sdt_variable_or_formal_parameter

Type of a DWARF variable in linksem

val loc_merge : (('a * 'a) * Loc.t) list -> (('a * 'a) * Loc.t) list

Merge contiguous location lists

val clamp_z : Z.t -> int

Convert from Z.t to int, if there is an overflow, returns Int.max_int instead of throwing

val of_linksem : Elf.File.t -> Ctype.env -> linksem_t -> t

Create a DWARF variable from its linksem counterpart

val pp_raw : t -> Utils.Pp.document

Pretty print a variable

\ No newline at end of file +Var (read-dwarf.Dw.Var)

Module Dw.Var

This module contain all the definition to handle local and global variables as defined in the DWARF information of the target file

type range = Elf.Address.t * Elf.Address.t option
type t = {
  1. name : string;
  2. param : bool;
  3. ctype : Ctype.t;
  4. locs : (range * Loc.t) list;
  5. locs_frame_base : (range * Loc.t) list;
}

Type of a DWARF variable

type linksem_t = Dwarf.sdt_variable_or_formal_parameter

Type of a DWARF variable in linksem

val loc_merge : + (('a * 'a option) * Loc.t) list -> + (('a * 'a option) * Loc.t) list

Merge contiguous location lists

val end_addr_of_sym : Utils.Sym.t -> Elf.Address.t option
val of_linksem : Elf.File.t -> Ctype.env -> linksem_t -> t

Create a DWARF variable from its linksem counterpart

val pp_raw : t -> Utils.Pp.document

Pretty print a variable

diff --git a/doc/html/read-dwarf/Dw/index.html b/doc/html/read-dwarf/Dw/index.html index ccfbe564..a32bda2f 100644 --- a/doc/html/read-dwarf/Dw/index.html +++ b/doc/html/read-dwarf/Dw/index.html @@ -1,2 +1,2 @@ -Dw (read-dwarf.Dw)

Module Dw

This module provides the specifically interpreted DWARF information needed for read-dwarf operations

I would have called this module dwarf but linksem decided that is was a good idea to dump all its modules in the global namespace.

module Var : sig ... end

This module contain all the definition to handle local and global variables as defined in the DWARF information of the target file

module Func : sig ... end

This module contain all the definition to handle functions as defined in the DWARF information of the target file

module Loc : sig ... end

This module represent architectural locations. Locations in DWARF information are represented as a little stack language (represented as dwop list) which describes how to compute the target value from the current concrete state. This computation can be arbitrarily complex, for example take half of the value from the high bits of a register and then the other half from memory at a specific address coming from another register.

type t = {
elf : Elf.File.t;
ldwarf : Dwarf.dwarf;
ldwarf_sdt : Dwarf.sdt_dwarf;
funcs : (string, Func.t) Stdlib.Hashtbl.t;
vars : (string, Var.t) Stdlib.Hashtbl.t;
tenv : Ctype.env;
}

The type that represent a elf-dwarf binary whose information has been fully interpreted

exception DwarfError of string

Error on Dwarf parsing

val dwarferror : ('a, unit, string, 'b) Stdlib.format4 -> 'a

Throw a DwarfError

val of_elf : Elf.File.t -> t

Get Dwarf information from an Elf file.

May raise a DwarfError if a problem occurs.

val of_file : string -> t

Get Dwarf information from an Elf file by name. Use File.of_file

val get_func_opt : name:string -> t -> Func.t option

Get a function by name

val pp_raw : t -> Utils.Pp.document

Pretty print dwarf data as an ocaml structure

\ No newline at end of file +Dw (read-dwarf.Dw)

Module Dw

This module provides the specifically interpreted DWARF information needed for read-dwarf operations

I would have called this module dwarf but linksem decided that is was a good idea to dump all its modules in the global namespace.

module Var : sig ... end

This module contain all the definition to handle local and global variables as defined in the DWARF information of the target file

module Func : sig ... end

This module contain all the definition to handle functions as defined in the DWARF information of the target file

module Loc : sig ... end

This module represent architectural locations. Locations in DWARF information are represented as a little stack language (represented as dwop list) which describes how to compute the target value from the current concrete state. This computation can be arbitrarily complex, for example take half of the value from the high bits of a register and then the other half from memory at a specific address coming from another register.

type t = {
  1. elf : Elf.File.t;
  2. ldwarf : Dwarf.dwarf;
  3. ldwarf_sdt : Dwarf.sdt_dwarf;
  4. funcs : (string, Func.t) Stdlib.Hashtbl.t;
  5. vars : (string, Var.t) Stdlib.Hashtbl.t;
  6. tenv : Ctype.env;
}

The type that represent a elf-dwarf binary whose information has been fully interpreted

exception DwarfError of string

Error on Dwarf parsing

val dwarferror : ('a, unit, string, 'b) Stdlib.format4 -> 'a

Throw a DwarfError

val of_elf : Elf.File.t -> t

Get Dwarf information from an Elf file.

May raise a DwarfError if a problem occurs.

val of_file : string -> t

Get Dwarf information from an Elf file by name. Use Elf.File.of_file

val get_func_opt : name:string -> t -> Func.t option

Get a function by name

val pp_raw : t -> Utils.Pp.document

Pretty print dwarf data as an ocaml structure

diff --git a/doc/html/read-dwarf/Elf/File/index.html b/doc/html/read-dwarf/Elf/File/index.html index 6f277bf4..0fcc7c19 100644 --- a/doc/html/read-dwarf/Elf/File/index.html +++ b/doc/html/read-dwarf/Elf/File/index.html @@ -1,2 +1,2 @@ -File (read-dwarf.Elf.File)

Module Elf.File

module SymTbl = SymTable
type machine =
| Supp of Config.Arch.t
| Other of int

The machine type of the ELF file. It can be a known architecture, or one that is not in Arch

val machine_of_linksem : Nat_big_num.num -> machine

Convert the linksem machine number to machine

val machine_to_string : machine -> string

Convert a machine to a human readable string

val pp_machine : machine -> Utils.Pp.document

Pretty prints a machine

type t = {
filename : string;

The name on the file system. Useful for error messages

symbols : SymTbl.t;

The symbol table

entry : int;

The address of the entry point; only used in dumpSym.ml

machine : machine;

The target architecture of the file; only used in arch.ml, dumpSym.ml, dw.ml

linksem : Elf_file.elf_file;

The original linksem structure for the file; only used in dw.ml

rodata : Segment.t;

The read-only data section

}

The type containing all the information about an ELF file

exception ElfError of string

Error on Elf parsing

val elferror : ('a, unit, string, 'b) Stdlib.format4 -> 'a

Throw an ElfError

val of_file : string -> t

Parse an ELF file to create an Elf.File.t using Linksem.

May raise an ElfError

\ No newline at end of file +File (read-dwarf.Elf.File)

Module Elf.File

This module represent an ELF 64 file. We do not deal with 32 bit ELF files for now The main interesting information is the symbol table.

module SymTbl = SymTable
type machine =
  1. | Supp of Config.Arch.t
  2. | Other of int

The machine type of the ELF file. It can be a known architecture, or one that is not in Arch

val machine_of_linksem : Nat_big_num.num -> machine

Convert the linksem machine number to machine

val machine_to_string : machine -> string

Convert a machine to a human readable string

val pp_machine : machine -> Utils.Pp.document

Pretty prints a machine

module SMap : sig ... end
type section = {
  1. name : string;
  2. size : int;
  3. align : int;
}
type t = {
  1. filename : string;
    (*

    The name on the file system. Useful for error messages

    *)
  2. symbols : SymTbl.t;
    (*

    The symbol table

    *)
  3. entry : int;
    (*

    The address of the entry point; only used in dumpSym.ml

    *)
  4. machine : machine;
    (*

    The target architecture of the file; only used in arch.ml, dumpSym.ml, dw.ml

    *)
  5. linksem : Elf_file.elf_file;
    (*

    The original linksem structure for the file; only used in dw.ml

    *)
  6. rodata : Segment.t SMap.t;
    (*

    The read-only data sections

    *)
  7. sections : section list;
}

The type containing all the information about an ELF file

exception ElfError of string

Error on Elf parsing

val elferror : ('a, unit, string, 'b) Stdlib.format4 -> 'a

Throw an ElfError

val of_file : string -> t

Parse an ELF file to create an Elf.File.t using Linksem.

May raise an ElfError

diff --git a/doc/html/read-dwarf/Elf/Segment/index.html b/doc/html/read-dwarf/Elf/Segment/index.html index 530ee09a..267dcfb7 100644 --- a/doc/html/read-dwarf/Elf/Segment/index.html +++ b/doc/html/read-dwarf/Elf/Segment/index.html @@ -1,2 +1,10 @@ -Segment (read-dwarf.Elf.Segment)

Module Elf.Segment

type t = {
data : Utils.BytesSeq.t;
addr : int;

The actual start address of the BytesSeq

size : int;

redundant with BytesSeq.length data

read : bool;
write : bool;
execute : bool;
}

The type of a segment

val of_linksem : Elf_interpreted_segment.elf64_interpreted_segment -> t

Loads a t using a linksem interpreted segment.

val is_in : t -> int -> bool

Check if an address is inside a segment

val get_addr : (Utils.BytesSeq.t -> int -> 'a) -> t -> int -> 'a

Get a value at an address which is in this segment using the getter provided

val get_addr_list_opt : (Utils.BytesSeq.t -> int -> 'a) -> t list -> int -> 'a option

Get a value at an address which is one of the segment of this list. It must be entirely in one of the segment

val get_containing : t list -> int -> t option

Get the segment containing an address, among a list of them or None

\ No newline at end of file +Segment (read-dwarf.Elf.Segment)

Module Elf.Segment

The goal of this module is to represent a segment as loaded in memory. In particular, all information about file layout is intentionally lost I use basic ints for speed. It it fails for some reason, I'll move to int64s.

This is basically a Utils.BytesSeq with metadata.

type t = {
  1. data : Utils.BytesSeq.t * Relocations.t;
  2. addr : int;
    (*

    The actual start address of the BytesSeq

    *)
  3. size : int;
    (*

    redundant with Utils.BytesSeq.length data

    *)
  4. read : bool;
  5. write : bool;
  6. execute : bool;
}

The type of a segment

val of_linksem : Elf_interpreted_segment.elf64_interpreted_segment -> t

Loads a t using a linksem interpreted segment.

val is_in : t -> int -> bool

Check if an address is inside a segment

val get_addr : + ((Utils.BytesSeq.t * Relocations.t) -> int -> 'a) -> + t -> + int -> + 'a

Get a value at an address which is in this segment using the getter provided

val get_addr_list_opt : + ((Utils.BytesSeq.t * Relocations.t) -> int -> 'a) -> + t list -> + int -> + 'a option

Get a value at an address which is one of the segment of this list. It must be entirely in one of the segment

val get_containing : t list -> int -> t option

Get the segment containing an address, among a list of them or None

diff --git a/doc/html/read-dwarf/Elf/SymTable/index.html b/doc/html/read-dwarf/Elf/SymTable/index.html index 93b539a8..24effb01 100644 --- a/doc/html/read-dwarf/Elf/SymTable/index.html +++ b/doc/html/read-dwarf/Elf/SymTable/index.html @@ -1,2 +1,2 @@ -SymTable (read-dwarf.Elf.SymTable)

Module Elf.SymTable

type sym = Symbol.t
type linksem_sym = Symbol.linksem_t
type sym_offset = sym * int

The type of a symbol with offset

type linksem_t = Elf_file.global_symbol_init_info
type t

The type of a symbol table.

val empty : t

The empty symbol table

val add : t -> sym -> t

Return a new table with the symbol added If there already exists a symbol covering the same area, merge them with Symbol.t.other_names

val of_name : t -> string -> sym

Get a symbol by name. Raise Not_found if no name matches

val of_name_opt : t -> string -> sym option

Get a symbol by name, None if no name matches

val of_addr : t -> int -> sym

Get the symbol owning that address. Not_found is raised if no symbol own that address.data See of_addr_opt

val of_addr_opt : t -> int -> sym option

Get the symbol owning that address. None if no symbol own that address. See of_addr

val of_addr_with_offset : t -> int -> sym_offset

Get a symbol with the offset that correspond to that address

val of_addr_with_offset_opt : t -> int -> sym_offset option

Get a symbol with the offset that correspond to that address

val to_addr_offset : sym_offset -> int

Get back the raw address from a symbol+offset value

val string_of_sym_offset : sym_offset -> string

Transform a symbol + offset into the corresponding string

val sym_offset_of_string : t -> string -> sym_offset

Transform a symbol + offset string into the actual symbol and the integer offset

val of_position_string : t -> string -> sym_offset

Convert a position string to a symbol + offset

A position string is a string describing a position in an ELF file. Two format are accepted for now:

  • A raw address of the form "0x40cafe"
  • A symbol name with optional offset like "sym" or "sym+4" or "sym+0x4"
val of_linksem : Segment.t list -> linksem_t -> t

Extract the symbol from the linksem symbol representation.

Need the segments for filling the missing symbol data

val pp_raw : t -> Utils.Pp.document

Pretty print the table as a raw ocaml value

val iter : t -> (sym -> unit) -> unit

Iterate through all the symbols in the table.

val fold : t -> 'a -> (sym -> 'a -> 'a) -> 'a

Fold over all the symbols in the table.

\ No newline at end of file +SymTable (read-dwarf.Elf.SymTable)

Module Elf.SymTable

The module provide a type to represent a symbol table.

The interesting operations provided are fetching symbol by name and knowing which symbol owns a specific address

of_position_string provides a convenient way of describing a position in the ELF file from a human text input like the CLI.

type sym = Symbol.t
type linksem_sym = Symbol.linksem_t
type sym_offset = sym * int

The type of a symbol with offset

type t

The type of a symbol table.

val empty : t

The empty symbol table

val add : t -> sym -> t

Return a new table with the symbol added If there already exists a symbol covering the same area, merge them with Symbol.t.other_names

val of_name : t -> string -> sym

Get a symbol by name. Raise Not_found if no name matches

val of_name_opt : t -> string -> sym option

Get a symbol by name, None if no name matches

val of_addr : t -> Address.t -> sym

Get the symbol owning that address. Not_found is raised if no symbol own that address.data See of_addr_opt

val of_addr_opt : t -> Address.t -> sym option

Get the symbol owning that address. None if no symbol own that address. See of_addr

val of_addr_with_offset : t -> Address.t -> sym_offset

Get a symbol with the offset that correspond to that address

val of_addr_with_offset_opt : t -> Address.t -> sym_offset option

Get a symbol with the offset that correspond to that address

val to_addr_offset : sym_offset -> Address.t

Get back the raw address from a symbol+offset value

val string_of_sym_offset : sym_offset -> string

Transform a symbol + offset into the corresponding string

val sym_offset_of_string : t -> string -> sym_offset

Transform a symbol + offset string into the actual symbol and the integer offset

val of_position_string : t -> string -> sym_offset

Convert a position string to a symbol + offset

A position string is a string describing a position in an ELF file. Two format are accepted for now:

  • A raw address of the form "0x40cafe"
  • A symbol name with optional offset like "sym" or "sym+4" or "sym+0x4"
val of_linksem : linksem_t -> t

Extract the symbol from the linksem symbol representation.

Need the segments for filling the missing symbol data

val pp_raw : t -> Utils.Pp.document

Pretty print the table as a raw ocaml value

val iter : t -> (sym -> unit) -> unit

Iterate through all the symbols in the table.

val fold : t -> 'a -> (sym -> 'a -> 'a) -> 'a

Fold over all the symbols in the table.

diff --git a/doc/html/read-dwarf/Elf/Symbol/index.html b/doc/html/read-dwarf/Elf/Symbol/index.html index 4af067dd..5dd618d6 100644 --- a/doc/html/read-dwarf/Elf/Symbol/index.html +++ b/doc/html/read-dwarf/Elf/Symbol/index.html @@ -1,2 +1,2 @@ -Symbol (read-dwarf.Elf.Symbol)

Module Elf.Symbol

type typ =
| NOTYPE
| OBJECT
| FUNC
| SECTION
| FILE
| UNKNOWN

The type of the ELF symbol

type linksem_typ = Z.t
type t = {
name : string;
other_names : string list;
typ : typ;
addr : int;
size : int;
writable : bool;
data : Utils.BytesSeq.t;
}

The ELF symbol. This type guarantee the data exists contrary to linksem symbols (it may be all zeros though)

type linksem_t = string * (Z.t * Z.t * Z.t * Utils.BytesSeq.t option * Z.t)

The type of an ELF symbol in linksem. See of_linksem

val push_name : string -> t -> t

Add a name to the other names list

val is_in : t -> int -> bool

Check if an address is in a symbol

val len : t -> int

For conformance with the Utils.RngMap.LenObject module type

val typ_of_linksem : linksem_typ -> typ

Convert the integer type into typ

val linksem_typ : linksem_t -> linksem_typ

Get the type from the linksem symbol type

exception LoadingError of string * int

LoadingError(name,addr) means that symbol name at addr could not be loaded.

val of_linksem : Segment.t list -> linksem_t -> t

Convert a symbol from linksem to read-dwarf representation using the segment data

May raise LoadingError when the symbol has no data and the data cannot be found in the segments

val is_interesting : typ -> bool

Tell if a symbol type is interesting for readDwarf purposes

val is_interesting_linksem : linksem_t -> bool

Tell if a linksem symbol is interesting for readDwarf purposes

val sub : t -> int -> int -> Utils.BytesSeq.t

Take the BytesSeq.t corresponding to the offset and length

val compare : t -> t -> int

Starting address comparison

val pp_typ : typ -> Utils.Pp.document

Pretty prints a symbol type

val pp_raw : t -> Utils.Pp.document

Raw pretty printing of a symbol

\ No newline at end of file +Symbol (read-dwarf.Elf.Symbol)

Module Elf.Symbol

This module represent an Elf symbol. One important difference with linksem symbols is that the symbols of this module always have the corresponding data (code or initial value). That's why function like of_linksem_with_data exist.

For now addresses are in ints and assume the top bit is sign extended. It may become Int64.t if required

type typ =
  1. | NOTYPE
  2. | OBJECT
  3. | FUNC
  4. | SECTION
  5. | FILE
  6. | UNKNOWN

The type of the ELF symbol

type linksem_typ = Z.t
type data = {
  1. data : Utils.BytesSeq.t;
  2. relocations : Relocations.t;
}
type t = {
  1. name : string;
  2. other_names : string list;
  3. typ : typ;
  4. addr : Address.t;
  5. size : int;
  6. writable : bool;
  7. data : data;
}

The ELF symbol. This type guarantee the data exists contrary to linksem symbols (it may be all zeros though)

type linksem_t = LinksemRelocatable.symbol

The type of an ELF symbol in linksem. See of_linksem

val push_name : string -> t -> t

Add a name to the other names list

Check if an address is in a symbol

val len : t -> int

For conformance with the Utils.RngMap.LenObject module type

val typ_of_linksem : linksem_typ -> typ

Convert the integer type into typ

val linksem_typ : linksem_t -> linksem_typ

Get the type from the linksem symbol type

exception LoadingError of string * int

LoadingError(name,addr) means that symbol name at addr could not be loaded.

val of_linksem : linksem_t -> t

Convert a symbol from linksem to read-dwarf representation using the segment data

May raise LoadingError when the symbol has no data and the data cannot be found in the segments

val is_interesting : typ -> bool

Tell if a symbol type is interesting for readDwarf purposes

val is_interesting_linksem : linksem_t -> bool

Tell if a linksem symbol is interesting for readDwarf purposes

val sub : t -> int -> int -> data

Take the BytesSeq.t corresponding to the offset and length

val compare : t -> t -> int

Starting address comparison

val pp_typ : typ -> Utils.Pp.document

Pretty prints a symbol type

val pp_raw : t -> Utils.Pp.document

Raw pretty printing of a symbol

diff --git a/doc/html/read-dwarf/Elf/index.html b/doc/html/read-dwarf/Elf/index.html index bb94bf45..656e75b8 100644 --- a/doc/html/read-dwarf/Elf/index.html +++ b/doc/html/read-dwarf/Elf/index.html @@ -1,2 +1,2 @@ -Elf (read-dwarf.Elf)

Module Elf

module File : sig ... end
module Segment : sig ... end
module SymTable : sig ... end
module Symbol : sig ... end
\ No newline at end of file +Elf (read-dwarf.Elf)

Module Elf

module Address : sig ... end
module File : sig ... end

This module represent an ELF 64 file. We do not deal with 32 bit ELF files for now The main interesting information is the symbol table.

module LinksemRelocatable : sig ... end
module Relocations : sig ... end
module Segment : sig ... end

The goal of this module is to represent a segment as loaded in memory. In particular, all information about file layout is intentionally lost I use basic ints for speed. It it fails for some reason, I'll move to int64s.

module SymTable : sig ... end

The module provide a type to represent a symbol table.

module Symbol : sig ... end

This module represent an Elf symbol. One important difference with linksem symbols is that the symbols of this module always have the corresponding data (code or initial value). That's why function like of_linksem_with_data exist.

diff --git a/doc/html/read-dwarf/Exp/ConcreteEval/index.html b/doc/html/read-dwarf/Exp/ConcreteEval/index.html index 83761a80..4f4b7d3a 100644 --- a/doc/html/read-dwarf/Exp/ConcreteEval/index.html +++ b/doc/html/read-dwarf/Exp/ConcreteEval/index.html @@ -1,2 +1,2 @@ -ConcreteEval (read-dwarf.Exp.ConcreteEval)

Module Exp.ConcreteEval

This module provides a way of making concrete evaluation of an expression. The only required thing is a context.

exception Symbolic

Thrown when trying to concretely evaluate a symbolic expression

type 'v context = 'v -> Value.t

A map from variables to concrete values. This map should throw Symbolic when the variable should be treated as symbolic.

val eval : ?⁠ctxt:'v context -> ('a'vAst.noAst.no) Ast.exp -> Value.t

Evaluate concretely an expression and return a Value.

If the expression is not concrete, it will throw Symbolic.

The default ctxt consider all variables to be symbolic.

eval may succeed even if is_concrete is false in presence of conditionals. Indeed only the taken branch of the conditional is evaluated, so the other may be symbolic. For example:

eval ExpTyped.(ite ~cond:(bool true) (bits_smt #x2a) (var ...)) = ExpTyped.(bits_smt #x2a)
val is_concrete : (____) Ast.exp -> bool

Tell if an expression is concrete, which means no variables of any kind

val eval_if_concrete : ('a'vAst.noAst.no) Ast.exp -> Value.t option

Evaluate an expression if it's concrete and returns None otherwise

\ No newline at end of file +ConcreteEval (read-dwarf.Exp.ConcreteEval)

Module Exp.ConcreteEval

This module provides a way of making concrete evaluation of an expression. The only required thing is a context.

exception Symbolic

Thrown when trying to concretely evaluate a symbolic expression

type 'v context = 'v -> Value.t

A map from variables to concrete values. This map should throw Symbolic when the variable should be treated as symbolic.

val eval : ?ctxt:'v context -> ('a, 'v, Ast.no, Ast.no) Ast.exp -> Value.t

Evaluate concretely an expression and return a Value.

If the expression is not concrete, it will throw Symbolic.

The default ctxt consider all variables to be symbolic.

eval may succeed even if is_concrete is false in presence of conditionals. Indeed only the taken branch of the conditional is evaluated, so the other may be symbolic. For example:

 eval ExpTyped.(ite ~cond:(bool true) (bits_smt #x2a) (var ...)) = ExpTyped.(bits_smt #x2a)
val is_concrete : (_, _, _, _) Ast.exp -> bool

Tell if an expression is concrete, which means no variables of any kind

val eval_if_concrete : ('a, 'v, Ast.no, Ast.no) Ast.exp -> Value.t option

Evaluate an expression if it's concrete and returns None otherwise

diff --git a/doc/html/read-dwarf/Exp/Make/argument-1-Var/index.html b/doc/html/read-dwarf/Exp/Make/argument-1-Var/index.html index ee51d3fc..ee4a4934 100644 --- a/doc/html/read-dwarf/Exp/Make/argument-1-Var/index.html +++ b/doc/html/read-dwarf/Exp/Make/argument-1-Var/index.html @@ -1,2 +1,2 @@ -1-Var (read-dwarf.Exp.Make.1-Var)

Parameter Make.1-Var

type t

The type of variables

val equal : t -> t -> bool

Equality predicate that will be passed to expressions

val pp : t -> Utils.Pp.document

Pretty printer to be used, both for memory pretty printing and for sending memory to Z3

val ty : t -> ty

Get the type of the variable

\ No newline at end of file +Var (read-dwarf.Exp.Make.Var)

Parameter Make.Var

type t

The type of variables

val equal : t -> t -> bool

Equality predicate that will be passed to expressions

val pp : t -> Utils.Pp.document

Pretty printer to be used, both for memory pretty printing and for sending memory to Z3

val ty : t -> ty

Get the type of the variable

diff --git a/doc/html/read-dwarf/Exp/Make/index.html b/doc/html/read-dwarf/Exp/Make/index.html index 51cb1fcf..61f17189 100644 --- a/doc/html/read-dwarf/Exp/Make/index.html +++ b/doc/html/read-dwarf/Exp/Make/index.html @@ -1,2 +1,2 @@ -Make (read-dwarf.Exp.Make)

Module Exp.Make

Parameters

Signature

type var = Var.t

The type of variable provided in the functor

type t = (varAst.no) Typed.t

The type of expression on which this module works

val equal : t -> t -> bool

Test syntactic equality. a + b and b + a would test different under this predicate

val pp : t -> Utils.Pp.document

Pretty print the expression using PpExp

val pp_smt : t -> Utils.Pp.document

Pretty print the expression in SMTLIB language

val of_var : var -> t

Create an expression from a variable

val add_type : ('avarAst.noAst.no) Ast.exp -> t

Convert a similar but untyped expression to an expression of type t

\ No newline at end of file +Make (read-dwarf.Exp.Make)

Module Exp.Make

Parameters

module Var : Var

Signature

type var = Var.t

The type of variable provided in the functor

type t = (var, Ast.no) Typed.t

The type of expression on which this module works

val equal : t -> t -> bool

Test syntactic equality. a + b and b + a would test different under this predicate

val pp : t -> Utils.Pp.document

Pretty print the expression using PpExp

val pp_smt : t -> Utils.Pp.document

Pretty print the expression in SMTLIB language

val of_var : var -> t

Create an expression from a variable

val add_type : ('a, var, Ast.no, Ast.no) Ast.exp -> t

Convert a similar but untyped expression to an expression of type t

diff --git a/doc/html/read-dwarf/Exp/PpExp/index.html b/doc/html/read-dwarf/Exp/PpExp/index.html index d9041a96..4fd1f4e2 100644 --- a/doc/html/read-dwarf/Exp/PpExp/index.html +++ b/doc/html/read-dwarf/Exp/PpExp/index.html @@ -1,2 +1,16 @@ -PpExp (read-dwarf.Exp.PpExp)

Module Exp.PpExp

This module provides a human readable pretty printing for Ast expressions

If you don't want to bother with details, use pp_exp and don't read the rest.

The precedence are the ones from C/C++ and Ocaml with some tweaks. In particular the precedence between bitwise operation and arithmetic operation are separated, so parenthesis will always be required between them.

The order is:

  • Extraction and extension
  • Concatenation
  • unary minus, unary bitwise negation, unary reduction
  • multiplications, divisions, separately shift
  • additions, substractions, separately bitwise operation
  • comparisons
  • equality
  • and
  • or
  • ifs

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)))

Precedence

type prec =
| IF
| OR
| AND
| EQ
| COMP
| ADD
| MUL
| BITS
| SHIFTS
| UNARY
| CONCAT
| EXTRACT
| PARENS

The operators possible precedence

val compat : outer:prec -> inner:prec -> bool

Figure out if an expression of precedence inner in an expression of precedence outer needs parentheses

val prec_unop : Ast.unop -> prec
val prec_bvarith : Ast.bvarith -> prec
val prec_binop : Ast.no Ast.binop -> prec
val prec_bvmanyarith : Ast.bvmanyarith -> prec
val prec_manyop : Ast.manyop -> prec

Actual Pretty printing

val pp_bits : Utils.BitVec.t -> Utils.Pp.document
val ppnot : Utils.Pp.document
val pp_unop : Ast.unop -> Utils.Pp.document -> Utils.Pp.document
val sym_bvarith : Ast.bvarith -> Utils.Pp.document
val sym_bvcomp : Ast.bvcomp -> Utils.Pp.document
val pp_binop : Ast.no Ast.binop -> Utils.Pp.document -> Utils.Pp.document -> Utils.Pp.document
val sym_bvmanyarith : Ast.bvmanyarith -> Utils.Pp.document
val pp_manyop : Ast.manyop -> PPrintEngine.document list -> Utils.Pp.document
val pp_if : PPrintEngine.document -> PPrintEngine.document -> PPrintEngine.document -> Utils.Pp.document
val pp_exp_prec : ('v -> Utils.Pp.document) -> ('a'vAst.noAst.no) Ast.exp -> Utils.Pp.document * prec

Pretty print an expression and return its precedence

val pp_exp : ('a -> Utils.Pp.document) -> ('b'aAst.noAst.no) Ast.exp -> Utils.Pp.document

The main function for pretty printing an expression

\ No newline at end of file +PpExp (read-dwarf.Exp.PpExp)

Module Exp.PpExp

This module provides a human readable pretty printing for Ast expressions

If you don't want to bother with details, use pp_exp and don't read the rest.

The precedence are the ones from C/C++ and Ocaml with some tweaks. In particular the precedence between bitwise operation and arithmetic operation are separated, so parenthesis will always be required between them.

The order is:

  • Extraction and extension
  • Concatenation
  • unary minus, unary bitwise negation, unary reduction
  • multiplications, divisions, separately shift
  • additions, substractions, separately bitwise operation
  • comparisons
  • equality
  • and
  • or
  • ifs

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)))

Precedence

type prec =
  1. | IF
  2. | OR
  3. | AND
  4. | EQ
  5. | COMP
  6. | ADD
  7. | MUL
  8. | BITS
  9. | SHIFTS
  10. | UNARY
  11. | CONCAT
  12. | EXTRACT
  13. | PARENS

The operators possible precedence

val compat : outer:prec -> inner:prec -> bool

Figure out if an expression of precedence inner in an expression of precedence outer needs parentheses

val prec_unop : Ast.unop -> prec
val prec_bvarith : Ast.bvarith -> prec
val prec_binop : Ast.no Ast.binop -> prec
val prec_bvmanyarith : Ast.bvmanyarith -> prec
val prec_manyop : Ast.manyop -> prec

Actual Pretty printing

val ppnot : Utils.Pp.document
val sym_bvarith : Ast.bvarith -> Utils.Pp.document
val sym_bvcomp : Ast.bvcomp -> Utils.Pp.document
val sym_bvmanyarith : Ast.bvmanyarith -> Utils.Pp.document
val pp_exp_prec : + ('v -> Utils.Pp.document) -> + ('a, 'v, Ast.no, Ast.no) Ast.exp -> + Utils.Pp.document * prec

Pretty print an expression and return its precedence

val pp_exp : + ('a -> Utils.Pp.document) -> + ('b, 'a, Ast.no, Ast.no) Ast.exp -> + Utils.Pp.document

The main function for pretty printing an expression

diff --git a/doc/html/read-dwarf/Exp/Sums/index.html b/doc/html/read-dwarf/Exp/Sums/index.html index 0e459d63..d78d7340 100644 --- a/doc/html/read-dwarf/Exp/Sums/index.html +++ b/doc/html/read-dwarf/Exp/Sums/index.html @@ -1,2 +1,12 @@ -Sums (read-dwarf.Exp.Sums)

Module Exp.Sums

This module provide sum manipulation functionality on top of typed expression Typed.t

This provide a semantic view of sums as list of terms and conversion

val split : ('v'm) Typed.t -> ('v'm) Typed.t list

Split an expression as a list of terms. This function sees through +,- and extracts.

Any expression e should have the same semantic meaning as Typed.sum (split e).

TODO I need to sort according to an arbitrary order to be able to compare reliably. This will probably be part of a more general simplifier work.

val merge : size:int -> ('v'm) Typed.t list -> ('v'm) Typed.t

Merge a list of terms into a sum expression. This is an upgrade of Typed.sum to allow empty lists. In the case of an empty list, a 0 of size size will be inserted instead.

val add_term : term:('v'm) Typed.t -> ('v'm) Typed.t -> ('v'm) Typed.t

Add a term to a sum, This is the same, as using split, then adding term to the list, then merging with Typed.sum

val remove_term : equal:(('v'm) Typed.t -> ('v'm) Typed.t -> bool) -> term:('v'm) Typed.t -> ('v'm) Typed.t -> ('v'm) Typed.t option

Remove a term from a sum. Return Some res if successful and None otherwise.

val smart_substract : equal:(('v'm) Typed.t -> ('v'm) Typed.t -> bool) -> term:('v'm) Typed.t -> ('v'm) Typed.t -> ('v'm) Typed.t

Same as remove_term but if the term is not found, add the opposite (Ast.unop.Bvneg) to the sum

val split_concrete : ('vAst.no) Typed.t -> ('vAst.no) Typed.t option * Utils.BitVec.t

Split away the concrete terms of the sum and the symbolic part. The symbolic part can be None if the expression was fully concrete. If the symbolic part is Some e, then not has_concrete_term e will hold.

val has_concrete_term : ('v'm) Typed.t -> bool

Tells if an expression has a concrete term

\ No newline at end of file +Sums (read-dwarf.Exp.Sums)

Module Exp.Sums

This module provide sum manipulation functionality on top of typed expression Typed.t

This provide a semantic view of sums as list of terms and conversion

val split : ('v, 'm) Typed.t -> ('v, 'm) Typed.t list

Split an expression as a list of terms. This function sees through +,- and extracts.

Any expression e should have the same semantic meaning as Typed.sum (split e).

TODO I need to sort according to an arbitrary order to be able to compare reliably. This will probably be part of a more general simplifier work.

val merge : size:int -> ('v, 'm) Typed.t list -> ('v, 'm) Typed.t

Merge a list of terms into a sum expression. This is an upgrade of Typed.sum to allow empty lists. In the case of an empty list, a 0 of size size will be inserted instead.

val add_term : term:('v, 'm) Typed.t -> ('v, 'm) Typed.t -> ('v, 'm) Typed.t

Add a term to a sum, This is the same, as using split, then adding term to the list, then merging with Typed.sum

val remove_term : + equal:(('v, 'm) Typed.t -> ('v, 'm) Typed.t -> bool) -> + term:('v, 'm) Typed.t -> + ('v, 'm) Typed.t -> + ('v, 'm) Typed.t option

Remove a term from a sum. Return Some res if successful and None otherwise.

val smart_substract : + equal:(('v, 'm) Typed.t -> ('v, 'm) Typed.t -> bool) -> + term:('v, 'm) Typed.t -> + ('v, 'm) Typed.t -> + ('v, 'm) Typed.t

Same as remove_term but if the term is not found, add the opposite (Ast.unop.Bvneg) to the sum

val split_concrete : + ('v, Ast.no) Typed.t -> + ('v, Ast.no) Typed.t option * Utils.BitVec.t

Split away the concrete terms of the sum and the symbolic part. The symbolic part can be None if the expression was fully concrete. If the symbolic part is Some e, then not has_concrete_term e will hold.

val has_concrete_term : ('v, 'm) Typed.t -> bool

Tells if an expression has a concrete term

diff --git a/doc/html/read-dwarf/Exp/Typed/index.html b/doc/html/read-dwarf/Exp/Typed/index.html index 5d99b36b..c2a91124 100644 --- a/doc/html/read-dwarf/Exp/Typed/index.html +++ b/doc/html/read-dwarf/Exp/Typed/index.html @@ -1,2 +1,7 @@ -Typed (read-dwarf.Exp.Typed)

Module Exp.Typed

This module provide operation on typed expressions i.e expressions whose annotations are their SMT type (Ast.ty).

type ('v, 'm) t = ('m Ast.ty'vAst.no'm) Ast.exp

The type for a typed expression.

  • The 'v type is the type of expression variable.
  • The '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.

Generic operation on types

val get_type : ('v'm) t -> 'm Ast.ty

Get the type of a typed expression. Specialized version of Ast.Manip.annot_exp

val is_bool : 'a Ast.ty -> bool
val is_bv : 'a Ast.ty -> bool
val is_enum : 'a Ast.ty -> bool
val expect_bool : 'a Ast.ty -> unit
val expect_bv : 'a Ast.ty -> int
val expect_enum : 'a Ast.ty -> int

Generic constructors

This constuctors build a new expression as required, and compute the new type. They assume the operation is well typed (They assert it).

val var : typ:'m Ast.ty -> 'v -> ('v'm) t
val bits : Utils.BitVec.t -> ('v'm) t
val bool : bool -> ('v'm) t
val enum : Ast.enum -> ('v'm) t
val vec_idx_type : 'a Ast.ty

This is completely arbitrary, because it is not currently used.

val vec : ('a Ast.ty'bAst.no'a) Ast.exp list -> ('a Ast.ty'bAst.no'a) Ast.exp
val unop : Ast.unop -> ('v'm) t -> ('v'm) t
val binop : 'm Ast.binop -> ('v'm) t -> ('v'm) t -> ('v'm) t
val manyop : AstGen.Ott.manyop -> ('v'm) t list -> ('v'm) t

In addition to well-typedness requirement, this function will throw Invalid_argument if the list is empty. If the list has a single element, it will just return that element instead of building the symbolic operation.

val ite : cond:('v'm) t -> ('v'm) t -> ('v'm) t -> ('v'm) t

Specific constructors

val bits_int : size:int -> int -> ('a'b) t
val bits_smt : string -> ('a'b) t
val zero : size:int -> ('a'b) t
val true_ : ('a'b) t
val false_ : ('a'b) t
val (+) : ('a'b) t -> ('a'b) t -> ('a'b) t
val sum : ('a'b) t list -> ('a'b) t
val sub : ('a'b) t -> ('a'b) t -> ('a'b) t
val (-) : ('a'b) t -> ('a'b) t -> ('a'b) t
val (*) : ('a'b) t -> ('a'b) t -> ('a'b) t
val prod : ('a'b) t list -> ('a'b) t
val sdiv : ('a'b) t -> ('a'b) t -> ('a'b) t
val not : ('a'b) t -> ('a'b) t
val neg : ('a'b) t -> ('a'b) t
val extract : first:int -> last:int -> ('a'b) t -> ('a'b) t
val eq : ('a'b) t -> ('a'b) t -> ('a'b) t
val (=) : ('a'b) t -> ('a'b) t -> ('a'b) t
val concat : ('a'b) t list -> ('a'b) t
val comp : Ast.bvcomp -> ('a'b) t -> ('a'b) t -> ('a'b) t

Add types

val add_type : a v m. ty_of_var:('a -> 'v -> 'm Ast.ty) -> ('a'vAst.no'm) Ast.exp -> ('v'm) t

Replace the annotation of expression by the SMT types. The expression must be already well typed (you can trust the SMT solver on this one)

It will still assert that an expression is well typed as a side effect.

val is_well_typed : ('v'm) t -> bool

Check if an expression is well typed

\ No newline at end of file +Typed (read-dwarf.Exp.Typed)

Module Exp.Typed

This module provide operation on typed expressions i.e expressions whose annotations are their SMT type (Ast.ty).

type ('v, 'm) t = ('m Ast.ty, 'v, Ast.no, 'm) Ast.exp

The type for a typed expression.

  • The 'v type is the type of expression variable.
  • The '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.

Generic operation on types

val get_type : ('v, 'm) t -> 'm Ast.ty

Get the type of a typed expression. Specialized version of Ast.Manip.annot_exp

val is_bool : 'a Ast.ty -> bool
val is_bv : 'a Ast.ty -> bool
val is_enum : 'a Ast.ty -> bool
val expect_bool : 'a Ast.ty -> unit
val expect_bv : 'a Ast.ty -> int
val expect_enum : 'a Ast.ty -> int

Generic constructors

This constuctors build a new expression as required, and compute the new type. They assume the operation is well typed (They assert it).

val var : typ:'m Ast.ty -> 'v -> ('v, 'm) t
val bits : Utils.BitVec.t -> ('v, 'm) t
val bool : bool -> ('v, 'm) t
val enum : Ast.enum -> ('v, 'm) t
val vec_idx_type : 'a Ast.ty

This is completely arbitrary, because it is not currently used.

val vec : + ('a Ast.ty, 'b, Ast.no, 'a) Ast.exp list -> + ('a Ast.ty, 'b, Ast.no, 'a) Ast.exp
val unop : Ast.unop -> ('v, 'm) t -> ('v, 'm) t
val binop : 'm Ast.binop -> ('v, 'm) t -> ('v, 'm) t -> ('v, 'm) t
val manyop : AstGen.Ott.manyop -> ('v, 'm) t list -> ('v, 'm) t

In addition to well-typedness requirement, this function will throw Invalid_argument if the list is empty. If the list has a single element, it will just return that element instead of building the symbolic operation.

val ite : cond:('v, 'm) t -> ('v, 'm) t -> ('v, 'm) t -> ('v, 'm) t

Specific constructors

val bits_int : size:int -> int -> ('a, 'b) t
val bits_smt : string -> ('a, 'b) t
val zero : size:int -> ('a, 'b) t
val true_ : ('a, 'b) t
val false_ : ('a, 'b) t
val (+) : ('a, 'b) t -> ('a, 'b) t -> ('a, 'b) t
val sum : ('a, 'b) t list -> ('a, 'b) t
val sub : ('a, 'b) t -> ('a, 'b) t -> ('a, 'b) t
val (-) : ('a, 'b) t -> ('a, 'b) t -> ('a, 'b) t
val (*) : ('a, 'b) t -> ('a, 'b) t -> ('a, 'b) t
val prod : ('a, 'b) t list -> ('a, 'b) t
val sdiv : ('a, 'b) t -> ('a, 'b) t -> ('a, 'b) t
val not : ('a, 'b) t -> ('a, 'b) t
val neg : ('a, 'b) t -> ('a, 'b) t
val extract : first:int -> last:int -> ('a, 'b) t -> ('a, 'b) t
val eq : ('a, 'b) t -> ('a, 'b) t -> ('a, 'b) t
val (=) : ('a, 'b) t -> ('a, 'b) t -> ('a, 'b) t
val concat : ('a, 'b) t list -> ('a, 'b) t
val comp : Ast.bvcomp -> ('a, 'b) t -> ('a, 'b) t -> ('a, 'b) t

Add types

val add_type : + 'a 'v 'm. ty_of_var:('a -> 'v -> 'm Ast.ty) -> + ('a, 'v, Ast.no, 'm) Ast.exp -> + ('v, 'm) t

Replace the annotation of expression by the SMT types. The expression must be already well typed (you can trust the SMT solver on this one)

It will still assert that an expression is well typed as a side effect.

val is_well_typed : ('v, 'm) t -> bool

Check if an expression is well typed

diff --git a/doc/html/read-dwarf/Exp/Value/index.html b/doc/html/read-dwarf/Exp/Value/index.html index 89969f26..67c5cacd 100644 --- a/doc/html/read-dwarf/Exp/Value/index.html +++ b/doc/html/read-dwarf/Exp/Value/index.html @@ -1,2 +1,2 @@ -Value (read-dwarf.Exp.Value)

Module Exp.Value

This module provide a type to represent concrete values.

There is not concrete value for memory yet, but maybe there should be at some point to be able to fully support concrete evaluation without external tool

type t =
| Bool of bool
| Enum of Ast.enum
| Bv of Utils.BitVec.t
| Vec of t list

The type of concrete values

val to_string : t -> string

String representation of concrete values

val pp : t -> Utils.Pp.document

Pretty printer for concrete values

val bool : bool -> t

Bool constructor

val enum : Ast.enum -> t

Enum constructor

val bv : Utils.BitVec.t -> t

Bv constructor

val vec : t list -> t

Vec constructor

val expect_bool : t -> bool

Extract a boolean or fail

val expect_enum : t -> Ast.enum

Extract an enumeration or fail

val expect_bv : ?⁠size:int -> t -> Utils.BitVec.t

Extract a bit vector or fail. If size is specified, then it fail if the size don't match

val to_exp : t -> ('v'm) Typed.t

Convert to a constant expression

\ No newline at end of file +Value (read-dwarf.Exp.Value)

Module Exp.Value

This module provide a type to represent concrete values.

There is not concrete value for memory yet, but maybe there should be at some point to be able to fully support concrete evaluation without external tool

type t =
  1. | Bool of bool
  2. | Enum of Ast.enum
  3. | Bv of Utils.BitVec.t
  4. | Vec of t list

The type of concrete values

val to_string : t -> string

String representation of concrete values

val pp : t -> Utils.Pp.document

Pretty printer for concrete values

val bool : bool -> t

Bool constructor

val enum : Ast.enum -> t

Enum constructor

val bv : Utils.BitVec.t -> t

Bv constructor

val vec : t list -> t

Vec constructor

val expect_bool : t -> bool

Extract a boolean or fail

val expect_enum : t -> Ast.enum

Extract an enumeration or fail

val expect_bv : ?size:int -> t -> Utils.BitVec.t

Extract a bit vector or fail. If size is specified, then it fail if the size don't match

val to_exp : t -> ('v, 'm) Typed.t

Convert to a constant expression

diff --git a/doc/html/read-dwarf/Exp/index.html b/doc/html/read-dwarf/Exp/index.html index 869fe7e1..f9cebcc0 100644 --- a/doc/html/read-dwarf/Exp/index.html +++ b/doc/html/read-dwarf/Exp/index.html @@ -1,2 +1,2 @@ -Exp (read-dwarf.Exp)

Module Exp

This module intends to provider a convenience expression module by lifting function like equality or pretty printing from variable to expressions.

It is restricted to typed expression as defined in Typed

For now it also do not support memory expression, but it will soon.

module ConcreteEval : sig ... end

This module provides a way of making concrete evaluation of an expression. The only required thing is a context.

module PpExp : sig ... end

This module provides a human readable pretty printing for Ast expressions

module Sums : sig ... end

This module provide sum manipulation functionality on top of typed expression Typed.t

module Typed : sig ... end

This module provide operation on typed expressions i.e expressions whose annotations are their SMT type (Ast.ty).

module Value : sig ... end

This module provide a type to represent concrete values.

type ty = Ast.no Ast.ty

The type of memory-less expressions

module type Var = sig ... end

This is the type signature for variable required by this module

module type S = sig ... end

The signature of the output module of Make

module Make : functor (Var : Var) -> S with type var = Var.t
module Pp = PpExp
\ No newline at end of file +Exp (read-dwarf.Exp)

Module Exp

This module intends to provider a convenience expression module by lifting function like equality or pretty printing from variable to expressions.

It is restricted to typed expression as defined in Typed

For now it also do not support memory expression, but it will soon.

module ConcreteEval : sig ... end

This module provides a way of making concrete evaluation of an expression. The only required thing is a context.

module PpExp : sig ... end

This module provides a human readable pretty printing for Ast expressions

module Sums : sig ... end

This module provide sum manipulation functionality on top of typed expression Typed.t

module Typed : sig ... end

This module provide operation on typed expressions i.e expressions whose annotations are their SMT type (Ast.ty).

module Value : sig ... end

This module provide a type to represent concrete values.

type ty = Ast.no Ast.ty

The type of memory-less expressions

module type Var = sig ... end

This is the type signature for variable required by this module

module type S = sig ... end

The signature of the output module of Make

module Make (Var : Var) : S with type var = Var.t
module Pp = PpExp
diff --git a/doc/html/read-dwarf/Exp/module-type-S/index.html b/doc/html/read-dwarf/Exp/module-type-S/index.html index 9cafd742..edb23b6e 100644 --- a/doc/html/read-dwarf/Exp/module-type-S/index.html +++ b/doc/html/read-dwarf/Exp/module-type-S/index.html @@ -1,2 +1,2 @@ -S (read-dwarf.Exp.S)

Module type Exp.S

The signature of the output module of Make

type var

The type of variable provided in the functor

type t = (varAst.no) Typed.t

The type of expression on which this module works

val equal : t -> t -> bool

Test syntactic equality. a + b and b + a would test different under this predicate

val pp : t -> Utils.Pp.document

Pretty print the expression using PpExp

val pp_smt : t -> Utils.Pp.document

Pretty print the expression in SMTLIB language

val of_var : var -> t

Create an expression from a variable

val add_type : ('avarAst.noAst.no) Ast.exp -> t

Convert a similar but untyped expression to an expression of type t

\ No newline at end of file +S (read-dwarf.Exp.S)

Module type Exp.S

The signature of the output module of Make

type var

The type of variable provided in the functor

type t = (var, Ast.no) Typed.t

The type of expression on which this module works

val equal : t -> t -> bool

Test syntactic equality. a + b and b + a would test different under this predicate

val pp : t -> Utils.Pp.document

Pretty print the expression using PpExp

val pp_smt : t -> Utils.Pp.document

Pretty print the expression in SMTLIB language

val of_var : var -> t

Create an expression from a variable

val add_type : ('a, var, Ast.no, Ast.no) Ast.exp -> t

Convert a similar but untyped expression to an expression of type t

diff --git a/doc/html/read-dwarf/Exp/module-type-Var/index.html b/doc/html/read-dwarf/Exp/module-type-Var/index.html index 6e220e36..86cac564 100644 --- a/doc/html/read-dwarf/Exp/module-type-Var/index.html +++ b/doc/html/read-dwarf/Exp/module-type-Var/index.html @@ -1,2 +1,2 @@ -Var (read-dwarf.Exp.Var)

Module type Exp.Var

This is the type signature for variable required by this module

type t

The type of variables

val equal : t -> t -> bool

Equality predicate that will be passed to expressions

val pp : t -> Utils.Pp.document

Pretty printer to be used, both for memory pretty printing and for sending memory to Z3

val ty : t -> ty

Get the type of the variable

\ No newline at end of file +Var (read-dwarf.Exp.Var)

Module type Exp.Var

This is the type signature for variable required by this module

type t

The type of variables

val equal : t -> t -> bool

Equality predicate that will be passed to expressions

val pp : t -> Utils.Pp.document

Pretty printer to be used, both for memory pretty printing and for sending memory to Z3

val ty : t -> ty

Get the type of the variable

diff --git a/doc/html/read-dwarf/InstructionPipeline.html b/doc/html/read-dwarf/InstructionPipeline.html index 03863c3c..28828e7a 100644 --- a/doc/html/read-dwarf/InstructionPipeline.html +++ b/doc/html/read-dwarf/InstructionPipeline.html @@ -1,2 +1,2 @@ -InstructionPipeline (read-dwarf.InstructionPipeline)

Instruction Pipeline

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

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.

Calling 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.

Preprocessing of isla traces

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 traces caching

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.

Typing and register learning

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.

Running Isla traces directly

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.

Trace and Instr

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.

\ No newline at end of file +InstructionPipeline (read-dwarf.InstructionPipeline)

Instruction Pipeline

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

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.

Calling 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.

Preprocessing of isla traces

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 traces caching

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.

Typing and register learning

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.

Running Isla traces directly

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.

Trace and Instr

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.

diff --git a/doc/html/read-dwarf/Isla/Base/index.html b/doc/html/read-dwarf/Isla/Base/index.html index 8799b01e..ee10fecd 100644 --- a/doc/html/read-dwarf/Isla/Base/index.html +++ b/doc/html/read-dwarf/Isla/Base/index.html @@ -1,2 +1,25 @@ -Base (read-dwarf.Isla.Base)

Module Isla.Base

This module wraps all isla-lang functionality. No other module should directly touch the Isla_lang module.

The isla trace syntax is compose of events that a regrouped into traces. There are a lot of possible events, and some of them are unsupported. in particular all the ones that deal with concurrency.

All the variable in isla traces are numbered. They are named v28 in the syntax and are represented as a plain int in Ocaml. The structure of the traces is a mix of SMT declaration about those variable and actual processor events. SMT declaration use SMT expressions of type exp which entirely distinct from Ast.exp (Use Isla.Conv to convert). On the contrary processor event do not contain direct SMT expressions but Sail values of type valu. A valu can be either a single symbolic variable, a concrete bitvector/boolean/enumeration value or a more complex sail structure with fields that contain other 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.

Aliases

include Isla_lang.AST
type enum = int * int
type lrng = Isla_lang__Isla_lang_ast.lrng =
| UnknownRng
| Generated of lrng
| Range of Stdlib.Lexing.position * Stdlib.Lexing.position
exception Parse_error_locn of lrng * string
val pp_lpos : Stdlib.Lexing.position -> PPrint.document
val pp_lrng : lrng -> PPrint.document
type bvmanyarith = Isla_lang__Isla_lang_ast.bvmanyarith =
| Bvand
| Bvor
| Bvxor
| Bvadd
| Bvmul
type bvcomp = Isla_lang__Isla_lang_ast.bvcomp =
| Bvult
| Bvslt
| Bvule
| Bvsle
| Bvuge
| Bvsge
| Bvugt
| Bvsgt
type bvarith = Isla_lang__Isla_lang_ast.bvarith =
| Bvnand
| Bvnor
| Bvxnor
| Bvsub
| Bvudiv
| Bvudivi
| Bvsdiv
| Bvsdivi
| Bvurem
| Bvsrem
| Bvsmod
| Bvshl
| Bvlshr
| Bvashr
type manyop = Isla_lang__Isla_lang_ast.manyop =
| And
| Or
| Bvmanyarith of bvmanyarith
| Concat
type unop = Isla_lang__Isla_lang_ast.unop =
| Not
| Bvnot
| Bvredand
| Bvredor
| Bvneg
| Extract of int * int
| ZeroExtend of int
| SignExtend of int
type binop = Isla_lang__Isla_lang_ast.binop =
| Eq
| Bvarith of bvarith
| Bvcomp of bvcomp
type accessor = Isla_lang__Isla_lang_ast.accessor =
| Field of string
type ty = Isla_lang__Isla_lang_ast.ty =
| Ty_Bool
| Ty_BitVec of int
| Ty_Enum of int
| Ty_Array of ty * ty
type 'a exp = 'a Isla_lang__Isla_lang_ast.exp =
| Var of int * 'a
| Bits of string * 'a
| Bool of bool * 'a
| Enum of enum * 'a
| Unop of unop * 'a exp * 'a
| Binop of binop * 'a exp * 'a exp * 'a
| Manyop of manyop * 'a exp list * 'a
| Ite of 'a exp * 'a exp * 'a exp * 'a
type valu = Isla_lang__Isla_lang_ast.valu =
| Val_Symbolic of int
| Val_Bool of bool
| Val_I of int * int
| Val_Bits of string
| Val_Enum of enum
| Val_String of string
| Val_Unit
| Val_NamedUnit of string
| Val_Vector of valu list
| Val_List of valu list
| Val_Struct of (string * valu) list
| Val_Poison
type accessor_list = Isla_lang__Isla_lang_ast.accessor_list =
| Nil
| Cons of accessor list
type 'a smt = 'a Isla_lang__Isla_lang_ast.smt =
| DeclareConst of int * ty
| DefineConst of int * 'a exp
| Assert of 'a exp
| DefineEnum of int
type valu_option = valu option
type valu_concrete = Isla_lang__Isla_lang_ast.valu_concrete =
| CVal_Bool of bool
| CVal_I of int * int
| CVal_Bits of string
| CVal_Enum of enum
| CVal_String of string
| CVal_Unit
| CVal_NamedUnit of string
| CVal_Vector of valu list
| CVal_List of valu list
| CVal_Struct of (string * valu) list
| CVal_Poison
type 'a event = 'a Isla_lang__Isla_lang_ast.event =
| Smt of 'a smt * 'a
| Branch of int * string * 'a
| ReadReg of string * accessor_list * valu * 'a
| WriteReg of string * accessor_list * valu * 'a
| ReadMem of valu * valu * valu * int * valu_option * 'a
| WriteMem of int * valu * valu * valu * int * valu_option * 'a
| BranchAddress of valu * 'a
| Barrier of valu * 'a
| CacheOp of valu * valu * 'a
| MarkReg of string * string * 'a
| Cycle of 'a
| Instr of valu * 'a
| Sleeping of int * 'a
| WakeRequest of 'a
| SleepRequest of 'a
type 'a trc = 'a Isla_lang__Isla_lang_ast.trc =
| Trace of 'a event list
type 'a exp_val = 'a Isla_lang__Isla_lang_ast.exp_val =
| EV_Bits of string * 'a
| EV_Bool of bool * 'a
| EV_Enum of enum * 'a
| EV_Unop of unop * 'a exp_val * 'a
| EV_Binop of binop * 'a exp_val * 'a exp_val * 'a
| EV_Manyop of manyop * 'a exp_val list * 'a
| EV_Ite of 'a exp_val * 'a exp_val * 'a exp_val * 'a
module Lexer = Isla_lang.Lexer
module Parser = Isla_lang.Parser
type loc = Stdlib.Lexing.position
type rtrc = lrng trc

The type of raw traces out of the parser

type revent = lrng event

The type of raw events out of the parser

type rsmt = lrng smt

The type of raw SMT declaration out of the parser

type rexp = lrng exp

The type of raw expressions out of the parser

IslaTrace parsing

exception ParseError of loc * string

Exception that represent an Isla parsing error

exception LexError of loc * string

Exception that represent an Isla lexing error

type lexer = Stdlib.Lexing.lexbuf -> Parser.token
type 'a parser = lexer -> Stdlib.Lexing.lexbuf -> 'a
val parse : 'a parser -> ?⁠filename:string -> Stdlib.Lexing.lexbuf -> 'a

Parse a single Isla instruction output from a Lexing.lexbuf

val parse_exp : ?⁠filename:string -> Stdlib.Lexing.lexbuf -> rexp

Parse a single Isla expression from a Lexing.lexbuf

val parse_exp_string : ?⁠filename:string -> string -> rexp

Parse a single Isla expression from a string

val parse_exp_channel : ?⁠filename:string -> Stdlib.in_channel -> rexp

Parse a single Isla expression from a channel

val parse_trc : ?⁠filename:string -> Stdlib.Lexing.lexbuf -> rtrc

Parse an Isla trace from a Lexing.lexbuf

val parse_trc_string : ?⁠filename:string -> string -> rtrc

Parse an Isla trace from a string

val parse_trc_channel : ?⁠filename:string -> Stdlib.in_channel -> rtrc

Parse an Isla trace from a channel

IslaTrace pretty printing

include Isla_lang.PP
val pp_raw_vvar : int -> PPrintEngine.document
val pp_raw_name : string -> PPrintEngine.document
val pp_raw_enum_ty : int -> PPrintEngine.document
val pp_raw_enum : Isla_lang__.Isla_lang_ast.enum -> PPrintEngine.document
val pp_raw_int : int -> PPrintEngine.document
val pp_raw_bvi : int -> PPrintEngine.document
val pp_raw_bv : string -> PPrintEngine.document
val pp_raw_str : string -> PPrintEngine.document
val pp_raw_ty : Isla_lang__.Isla_lang_ast.ty -> PPrintEngine.document
val pp_raw_bool : bool -> PPrintEngine.document
val pp_raw_unop : Isla_lang__.Isla_lang_ast.unop -> PPrintEngine.document
val pp_raw_bvarith : Isla_lang__.Isla_lang_ast.bvarith -> PPrintEngine.document
val pp_raw_bvcomp : Isla_lang__.Isla_lang_ast.bvcomp -> PPrintEngine.document
val pp_raw_binop : Isla_lang__.Isla_lang_ast.binop -> PPrintEngine.document
val pp_raw_bvmanyarith : Isla_lang__.Isla_lang_ast.bvmanyarith -> PPrintEngine.document
val pp_raw_manyop : Isla_lang__.Isla_lang_ast.manyop -> PPrintEngine.document
val pp_raw_exp : 'a Isla_lang__.Isla_lang_ast.exp -> PPrintEngine.document
val pp_raw_exp_val : 'a Isla_lang__.Isla_lang_ast.exp_val -> PPrintEngine.document
val pp_raw_smt : 'a Isla_lang__.Isla_lang_ast.smt -> PPrintEngine.document
val pp_raw_valu : Isla_lang__.Isla_lang_ast.valu -> PPrintEngine.document
val pp_raw_selem : (string * Isla_lang__.Isla_lang_ast.valu) -> PPrintEngine.document
val pp_raw_valu_concrete : Isla_lang__.Isla_lang_ast.valu_concrete -> PPrintEngine.document
val pp_raw_selem_concrete : (string * Isla_lang__.Isla_lang_ast.valu_concrete) -> PPrintEngine.document
val pp_raw_accessor : Isla_lang__.Isla_lang_ast.accessor -> PPrintEngine.document
val pp_raw_accessor_list : Isla_lang__.Isla_lang_ast.accessor_list -> PPrintEngine.document
val pp_raw_valu_option : Isla_lang__.Isla_lang_ast.valu_option -> PPrintEngine.document
val pp_raw_event : 'a Isla_lang__.Isla_lang_ast.event -> PPrintEngine.document
val pp_raw_trc : 'a Isla_lang__.Isla_lang_ast.trc -> PPrintEngine.document
val pp_vvar : int -> PPrintEngine.document
val pp_name : string -> PPrintEngine.document
val pp_enum_ty : int -> PPrintEngine.document
val pp_enum : Isla_lang__.Isla_lang_ast.enum -> PPrintEngine.document
val pp_int : int -> PPrintEngine.document
val pp_bvi : int -> PPrintEngine.document
val pp_bv : string -> PPrintEngine.document
val pp_str : string -> PPrintEngine.document
val pp_j : int -> string
val pp_ty : Isla_lang__.Isla_lang_ast.ty -> PPrintEngine.document
val pp_bool : bool -> PPrintEngine.document
val pp_unop : Isla_lang__.Isla_lang_ast.unop -> PPrintEngine.document
val pp_bvarith : Isla_lang__.Isla_lang_ast.bvarith -> PPrintEngine.document
val pp_bvcomp : Isla_lang__.Isla_lang_ast.bvcomp -> PPrintEngine.document
val pp_binop : Isla_lang__.Isla_lang_ast.binop -> PPrintEngine.document
val pp_bvmanyarith : Isla_lang__.Isla_lang_ast.bvmanyarith -> PPrintEngine.document
val pp_manyop : Isla_lang__.Isla_lang_ast.manyop -> PPrintEngine.document
val pp_exp : 'a Isla_lang__.Isla_lang_ast.exp -> PPrintEngine.document
val pp_exp_val : 'a Isla_lang__.Isla_lang_ast.exp_val -> PPrintEngine.document
val pp_smt : 'a Isla_lang__.Isla_lang_ast.smt -> PPrintEngine.document
val pp_valu : Isla_lang__.Isla_lang_ast.valu -> PPrintEngine.document
val pp_selem : (string * Isla_lang__.Isla_lang_ast.valu) -> PPrintEngine.document
val pp_valu_concrete : Isla_lang__.Isla_lang_ast.valu_concrete -> PPrintEngine.document
val pp_selem_concrete : (string * Isla_lang__.Isla_lang_ast.valu_concrete) -> PPrintEngine.document
val pp_accessor : Isla_lang__.Isla_lang_ast.accessor -> PPrintEngine.document
val pp_accessor_list : Isla_lang__.Isla_lang_ast.accessor_list -> PPrintEngine.document
val pp_valu_option : Isla_lang__.Isla_lang_ast.valu_option -> PPrintEngine.document
val pp_event : 'a Isla_lang__.Isla_lang_ast.event -> PPrintEngine.document
val pp_trc : 'a Isla_lang__.Isla_lang_ast.trc -> PPrintEngine.document
\ No newline at end of file +Base (read-dwarf.Isla.Base)

Module Isla.Base

This module wraps all isla-lang functionality. No other module should directly touch the Isla_lang module.

The isla trace syntax is compose of events that a regrouped into traces. There are a lot of possible events, and some of them are unsupported. in particular all the ones that deal with concurrency.

All the variable in isla traces are numbered. They are named v28 in the syntax and are represented as a plain int in Ocaml. The structure of the traces is a mix of SMT declaration about those variable and actual processor events. SMT declaration use SMT expressions of type exp which entirely distinct from Ast.exp (Use Isla.Conv to convert). On the contrary processor event do not contain direct SMT expressions but Sail values of type valu. A valu can be either a single symbolic variable, a concrete bitvector/boolean/enumeration value or a more complex sail structure with fields that contain other 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.

Aliases

include module type of struct include Isla_lang.AST end
type enum = int * int
type lrng = Isla_lang__Isla_lang_ast.lrng =
  1. | UnknownRng
  2. | Generated of lrng
  3. | Range of Stdlib.Lexing.position * Stdlib.Lexing.position
exception Parse_error_locn of lrng * string
val pp_lpos : Stdlib.Lexing.position -> PPrint.document
val pp_lrng : lrng -> PPrint.document
type accessor = Isla_lang__Isla_lang_ast.accessor =
  1. | Field of string
type bvarith = Isla_lang__Isla_lang_ast.bvarith =
  1. | Bvnand
  2. | Bvnor
  3. | Bvxnor
  4. | Bvsub
  5. | Bvudiv
  6. | Bvudivi
  7. | Bvsdiv
  8. | Bvsdivi
  9. | Bvurem
  10. | Bvsrem
  11. | Bvsmod
  12. | Bvshl
  13. | Bvlshr
  14. | Bvashr
type bvcomp = Isla_lang__Isla_lang_ast.bvcomp =
  1. | Bvult
  2. | Bvslt
  3. | Bvule
  4. | Bvsle
  5. | Bvuge
  6. | Bvsge
  7. | Bvugt
  8. | Bvsgt
type bvmanyarith = Isla_lang__Isla_lang_ast.bvmanyarith =
  1. | Bvand
  2. | Bvor
  3. | Bvxor
  4. | Bvadd
  5. | Bvmul
type accessor_list = Isla_lang__Isla_lang_ast.accessor_list =
  1. | Nil
  2. | Cons of accessor list
type unop = Isla_lang__Isla_lang_ast.unop =
  1. | Not
  2. | Bvnot
  3. | Bvredand
  4. | Bvredor
  5. | Bvneg
  6. | Extract of int * int
  7. | ZeroExtend of int
  8. | SignExtend of int
type binop = Isla_lang__Isla_lang_ast.binop =
  1. | Eq
  2. | Bvarith of bvarith
  3. | Bvcomp of bvcomp
type manyop = Isla_lang__Isla_lang_ast.manyop =
  1. | And
  2. | Or
  3. | Bvmanyarith of bvmanyarith
  4. | Concat
type base_val = Isla_lang__Isla_lang_ast.base_val =
  1. | Val_Symbolic of int
  2. | Val_Bool of bool
  3. | Val_Bits of string
  4. | Val_Enum of enum
type assume_val = Isla_lang__Isla_lang_ast.assume_val =
  1. | AVal_Var of string * accessor_list
  2. | AVal_Bool of bool
  3. | AVal_Bits of string
  4. | AVal_Enum of enum
type ty = Isla_lang__Isla_lang_ast.ty =
  1. | Ty_Bool
  2. | Ty_BitVec of int
  3. | Ty_Enum of int
  4. | Ty_Array of ty * ty
type !'a exp = 'a Isla_lang__Isla_lang_ast.exp =
  1. | Val of base_val * 'a
  2. | Unop of unop * 'a exp * 'a
  3. | Binop of binop * 'a exp * 'a exp * 'a
  4. | Manyop of manyop * 'a exp list * 'a
  5. | Ite of 'a exp * 'a exp * 'a exp * 'a
type valu = Isla_lang__Isla_lang_ast.valu =
  1. | RegVal_Base of base_val
  2. | RegVal_I of int * int
  3. | RegVal_String of string
  4. | RegVal_Unit
  5. | RegVal_Vector of valu list
  6. | RegVal_List of valu list
  7. | RegVal_Struct of (string * valu) list
  8. | RegVal_Constructor of string * valu
  9. | RegVal_Poison
type !'a a_exp = 'a Isla_lang__Isla_lang_ast.a_exp =
  1. | AExp_Val of assume_val * 'a
  2. | AExp_Unop of unop * 'a a_exp * 'a
  3. | AExp_Binop of binop * 'a a_exp * 'a a_exp * 'a
  4. | AExp_Manyop of manyop * 'a a_exp list * 'a
  5. | AExp_Ite of 'a a_exp * 'a a_exp * 'a a_exp * 'a
type !'a smt = 'a Isla_lang__Isla_lang_ast.smt =
  1. | DeclareConst of int * ty
  2. | DefineConst of int * 'a exp
  3. | Assert of 'a exp
  4. | DefineEnum of int
type arg_list = Isla_lang__Isla_lang_ast.arg_list =
  1. | NilArgs
  2. | ListArgs of valu list
type tag_value = valu option
type segment = Isla_lang__Isla_lang_ast.segment =
  1. | Segment of string * int * int
type !'a event = 'a Isla_lang__Isla_lang_ast.event =
  1. | Smt of 'a smt * 'a
  2. | Branch of int * string * 'a
  3. | ReadReg of string * accessor_list * valu * 'a
  4. | WriteReg of string * accessor_list * valu * 'a
  5. | ReadMem of valu * valu * valu * int * tag_value * 'a
  6. | WriteMem of valu * valu * valu * valu * int * tag_value * 'a
  7. | BranchAddress of valu * 'a
  8. | Barrier of valu * 'a
  9. | CacheOp of valu * valu * 'a
  10. | MarkReg of string * string * 'a
  11. | Cycle of 'a
  12. | Instr of valu * 'a
  13. | Sleeping of int * 'a
  14. | WakeRequest of 'a
  15. | SleepRequest of 'a
  16. | AssumeReg of string * accessor_list * valu * 'a
  17. | Assume of 'a a_exp * 'a
  18. | FunAssume of string * valu * arg_list * 'a
  19. | UseFunAssume of string * valu * arg_list * 'a
  20. | AbstractCall of string * valu * arg_list * 'a
  21. | AbstractPrimop of string * valu * arg_list * 'a
type instruction_segments = Isla_lang__Isla_lang_ast.instruction_segments =
  1. | Segments of segment list
type !'a trc = 'a Isla_lang__Isla_lang_ast.trc =
  1. | Trace of 'a event list
type !'a maybe_fork = 'a Isla_lang__Isla_lang_ast.maybe_fork =
  1. | Cases of string * 'a tree_trc list
  2. | End
and !'a tree_trc = 'a Isla_lang__Isla_lang_ast.tree_trc =
  1. | TreeTrace of 'a event list * 'a maybe_fork
type !'a trcs = 'a Isla_lang__Isla_lang_ast.trcs =
  1. | Traces of 'a trc list
  2. | TracesWithSegments of instruction_segments * 'a trc list
type !'a whole_tree = 'a Isla_lang__Isla_lang_ast.whole_tree =
  1. | BareTree of 'a tree_trc
  2. | TreeWithSegments of instruction_segments * 'a tree_trc
val subst_val_base_val : base_val -> int -> base_val -> base_val
val subst_val_exp : base_val -> int -> 'a exp -> 'a exp
val subst_val_valu : base_val -> int -> valu -> valu
val subst_val_smt : base_val -> int -> 'a smt -> 'a smt
val subst_val_arg_list : base_val -> int -> arg_list -> arg_list
val subst_val_event : base_val -> int -> 'a event -> 'a event
val subst_val_tree_trc : base_val -> int -> 'a tree_trc -> 'a tree_trc
val subst_val_maybe_fork : base_val -> int -> 'a maybe_fork -> 'a maybe_fork
val subst_val_trc : base_val -> int -> 'a trc -> 'a trc
val subst_val_whole_tree : base_val -> int -> 'a whole_tree -> 'a whole_tree
val subst_val_trcs : base_val -> int -> 'a trcs -> 'a trcs
module Lexer = Isla_lang.Lexer
module Parser = Isla_lang.Parser
type loc = Stdlib.Lexing.position
type rtrc = lrng trc

The type of raw traces out of the parser

type revent = lrng event

The type of raw events out of the parser

type rsmt = lrng smt

The type of raw SMT declaration out of the parser

type rexp = lrng exp

The type of raw expressions out of the parser

type rtrcs = lrng trcs

IslaTrace parsing

exception ParseError of loc * string

Exception that represent an Isla parsing error

exception LexError of loc * string

Exception that represent an Isla lexing error

type lexer = Stdlib.Lexing.lexbuf -> Parser.token
type 'a parser = lexer -> Stdlib.Lexing.lexbuf -> 'a
val parse : 'a parser -> ?filename:string -> Stdlib.Lexing.lexbuf -> 'a

Parse a single Isla instruction output from a Lexing.lexbuf

val parse_exp : ?filename:string -> Stdlib.Lexing.lexbuf -> rexp

Parse a single Isla expression from a Lexing.lexbuf

val parse_exp_string : ?filename:string -> string -> rexp

Parse a single Isla expression from a string

val parse_exp_channel : ?filename:string -> Stdlib.in_channel -> rexp

Parse a single Isla expression from a channel

val parse_trc : ?filename:string -> Stdlib.Lexing.lexbuf -> rtrc

Parse an Isla trace from a Lexing.lexbuf

val parse_trc_string : ?filename:string -> string -> rtrc

Parse an Isla trace from a string

val parse_trc_channel : ?filename:string -> Stdlib.in_channel -> rtrc

Parse an Isla trace from a channel

val parse_trcs : + ?filename:string -> + Stdlib.Lexing.lexbuf -> + Isla_lang__.Isla_lang_ast.lrng Isla_lang__.Isla_lang_ast.trcs
val parse_trcs_string : ?filename:string -> string -> rtrcs
val parse_trcs_channel : ?filename:string -> Stdlib.in_channel -> rtrcs
val parse_segments : + ?filename:string -> + Stdlib.Lexing.lexbuf -> + instruction_segments
val parse_segments_string : ?filename:string -> string -> instruction_segments
val parse_segments_channel : + ?filename:string -> + Stdlib.in_channel -> + instruction_segments

IslaTrace pretty printing

include module type of struct include Isla_lang.PP end
val pp_raw_vvar : int -> PPrint.document
val pp_raw_name : string -> PPrint.document
val pp_raw_enum_ty : int -> PPrint.document
val pp_raw_enum : Isla_lang__.Isla_lang_ast.enum -> PPrint.document
val pp_raw_nat : int -> PPrint.document
val pp_raw_bvi : int -> PPrint.document
val pp_raw_bv : string -> PPrint.document
val pp_raw_str : string -> PPrint.document
val pp_raw_int : int -> PPrint.document
val pp_raw_ty : Isla_lang__.Isla_lang_ast.ty -> PPrint.document
val pp_raw_bool : bool -> PPrint.document
val pp_raw_unop : Isla_lang__.Isla_lang_ast.unop -> PPrint.document
val pp_raw_bvarith : Isla_lang__.Isla_lang_ast.bvarith -> PPrint.document
val pp_raw_bvcomp : Isla_lang__.Isla_lang_ast.bvcomp -> PPrint.document
val pp_raw_binop : Isla_lang__.Isla_lang_ast.binop -> PPrint.document
val pp_raw_bvmanyarith : + Isla_lang__.Isla_lang_ast.bvmanyarith -> + PPrint.document
val pp_raw_manyop : Isla_lang__.Isla_lang_ast.manyop -> PPrint.document
val pp_raw_base_val : Isla_lang__.Isla_lang_ast.base_val -> PPrint.document
val pp_raw_assume_val : Isla_lang__.Isla_lang_ast.assume_val -> PPrint.document
val pp_raw_exp : 'a Isla_lang__.Isla_lang_ast.exp -> PPrint.document
val pp_raw_a_exp : 'a Isla_lang__.Isla_lang_ast.a_exp -> PPrint.document
val pp_raw_smt : 'a Isla_lang__.Isla_lang_ast.smt -> PPrint.document
val pp_raw_valu : Isla_lang__.Isla_lang_ast.valu -> PPrint.document
val pp_raw_selem : (string * Isla_lang__.Isla_lang_ast.valu) -> PPrint.document
val pp_raw_accessor : Isla_lang__.Isla_lang_ast.accessor -> PPrint.document
val pp_raw_accessor_list : + Isla_lang__.Isla_lang_ast.accessor_list -> + PPrint.document
val pp_raw_arg_list : Isla_lang__.Isla_lang_ast.arg_list -> PPrint.document
val pp_raw_tag_value : Isla_lang__.Isla_lang_ast.tag_value -> PPrint.document
val pp_raw_event : 'a Isla_lang__.Isla_lang_ast.event -> PPrint.document
val pp_raw_segment : Isla_lang__.Isla_lang_ast.segment -> PPrint.document
val pp_raw_instruction_segments : + Isla_lang__.Isla_lang_ast.instruction_segments -> + PPrint.document
val pp_raw_trc : 'a Isla_lang__.Isla_lang_ast.trc -> PPrint.document
val pp_raw_trcs : 'a Isla_lang__.Isla_lang_ast.trcs -> PPrint.document
val pp_raw_maybe_fork : + 'a Isla_lang__.Isla_lang_ast.maybe_fork -> + PPrint.document
val pp_raw_tree_trc : 'a Isla_lang__.Isla_lang_ast.tree_trc -> PPrint.document
val pp_raw_whole_tree : + 'a Isla_lang__.Isla_lang_ast.whole_tree -> + PPrint.document
val pp_vvar : int -> PPrint.document
val pp_name : string -> PPrint.document
val pp_enum_ty : int -> PPrint.document
val pp_enum : Isla_lang__.Isla_lang_ast.enum -> PPrint.document
val pp_nat : int -> PPrint.document
val pp_bvi : int -> PPrint.document
val pp_bv : string -> PPrint.document
val pp_str : string -> PPrint.document
val pp_j : int -> string
val pp_int : int -> PPrint.document
val pp_ty : Isla_lang__.Isla_lang_ast.ty -> PPrint.document
val pp_bool : bool -> PPrint.document
val pp_unop : Isla_lang__.Isla_lang_ast.unop -> PPrint.document
val pp_bvarith : Isla_lang__.Isla_lang_ast.bvarith -> PPrint.document
val pp_bvcomp : Isla_lang__.Isla_lang_ast.bvcomp -> PPrint.document
val pp_binop : Isla_lang__.Isla_lang_ast.binop -> PPrint.document
val pp_bvmanyarith : Isla_lang__.Isla_lang_ast.bvmanyarith -> PPrint.document
val pp_manyop : Isla_lang__.Isla_lang_ast.manyop -> PPrint.document
val pp_base_val : Isla_lang__.Isla_lang_ast.base_val -> PPrint.document
val pp_assume_val : Isla_lang__.Isla_lang_ast.assume_val -> PPrint.document
val pp_exp : 'a Isla_lang__.Isla_lang_ast.exp -> PPrint.document
val pp_a_exp : 'a Isla_lang__.Isla_lang_ast.a_exp -> PPrint.document
val pp_smt : 'a Isla_lang__.Isla_lang_ast.smt -> PPrint.document
val pp_valu : Isla_lang__.Isla_lang_ast.valu -> PPrint.document
val pp_selem : (string * Isla_lang__.Isla_lang_ast.valu) -> PPrint.document
val pp_accessor : Isla_lang__.Isla_lang_ast.accessor -> PPrint.document
val pp_accessor_list : + Isla_lang__.Isla_lang_ast.accessor_list -> + PPrint.document
val pp_arg_list : Isla_lang__.Isla_lang_ast.arg_list -> PPrint.document
val pp_tag_value : Isla_lang__.Isla_lang_ast.tag_value -> PPrint.document
val pp_event : 'a Isla_lang__.Isla_lang_ast.event -> PPrint.document
val pp_segment : Isla_lang__.Isla_lang_ast.segment -> PPrint.document
val pp_instruction_segments : + Isla_lang__.Isla_lang_ast.instruction_segments -> + PPrint.document
val pp_trc : 'a Isla_lang__.Isla_lang_ast.trc -> PPrint.document
val pp_trcs : 'a Isla_lang__.Isla_lang_ast.trcs -> PPrint.document
val pp_maybe_fork : 'a Isla_lang__.Isla_lang_ast.maybe_fork -> PPrint.document
val pp_tree_trc : 'a Isla_lang__.Isla_lang_ast.tree_trc -> PPrint.document
val pp_whole_tree : 'a Isla_lang__.Isla_lang_ast.whole_tree -> PPrint.document
diff --git a/doc/html/read-dwarf/Isla/Cache/Epoch/index.html b/doc/html/read-dwarf/Isla/Cache/Epoch/index.html index f04929de..a1a49fdd 100644 --- a/doc/html/read-dwarf/Isla/Cache/Epoch/index.html +++ b/doc/html/read-dwarf/Isla/Cache/Epoch/index.html @@ -1,2 +1,2 @@ -Epoch (read-dwarf.Isla.Cache.Epoch)

Module Cache.Epoch

type t = string * int * string
val to_file : string -> (string * int * string) -> unit
val of_file : string -> string * int * string
val of_config : Server.Config.t -> string * int * string

Build the epoch from the config. This function does the config Digest

val compat : 'a -> 'a -> bool
\ No newline at end of file +Epoch (read-dwarf.Isla.Cache.Epoch)

Module Cache.Epoch

type t = string * int * string
val to_file : string -> (string * int * string) -> unit
val of_file : string -> string * int * string
val of_config : Config.File.ArchConf.Isla.t -> string * int * string

Build the epoch from the config. This function does the config Digest

val compat : 'a -> 'a -> bool
diff --git a/doc/html/read-dwarf/Isla/Cache/IC/index.html b/doc/html/read-dwarf/Isla/Cache/IC/index.html index 382b1443..3fac4151 100644 --- a/doc/html/read-dwarf/Isla/Cache/IC/index.html +++ b/doc/html/read-dwarf/Isla/Cache/IC/index.html @@ -1,2 +1,2 @@ -IC (read-dwarf.Isla.Cache.IC)

Module Cache.IC

The isla cache module

type key = Opcode.t
type value = TraceList.t
type epoch = Epoch.t
type t = Utils__Cache.Make(Opcode)(TraceList)(Epoch).t
val make : ?⁠fake:bool -> string -> epoch -> t
val get_opt : t -> key -> value option
val get : t -> key -> value
val add : t -> key -> value -> unit
val remove : t -> key -> unit
\ No newline at end of file +IC (read-dwarf.Isla.Cache.IC)

Module Cache.IC

The isla cache module

type key = Opcode.t
type value = TraceList.t
type epoch = Epoch.t
type t
val make : ?fake:bool -> string -> epoch -> t
val get_opt : t -> key -> value option
val get : t -> key -> value
val add : t -> key -> value -> unit
val remove : t -> key -> unit
diff --git a/doc/html/read-dwarf/Isla/Cache/Opcode/index.html b/doc/html/read-dwarf/Isla/Cache/Opcode/index.html index 4c58297c..69c4ebd7 100644 --- a/doc/html/read-dwarf/Isla/Cache/Opcode/index.html +++ b/doc/html/read-dwarf/Isla/Cache/Opcode/index.html @@ -1,2 +1,8 @@ -Opcode (read-dwarf.Isla.Cache.Opcode)

Module Cache.Opcode

Implementation of Cache.Key for opcodes.

It is a special encoding of BytesSeq. If it is short enough to fit in the hash, then we do it. Otherwise we store in a file.

The exact encoding is here (back mean the last/top bit of the integer, i.e. IntBits.back):

Short encoding: bit 0 to back -3 : The data bit back -3 to back: The size of the data bit back : cleared

Long encoding: bit 0 to back -1 : The start of the data bit back -1: set

type t = Utils.BytesSeq.t option
val equal : Utils.BytesSeq.t option -> Utils.BytesSeq.t option -> bool
val hash : Utils.BytesSeq.t option -> Utils.IntBits.t
val to_file : string -> Utils.BytesSeq.t option -> unit
val of_file : Utils.IntBits.t -> string -> Utils.BytesSeq.t option
\ No newline at end of file +Opcode (read-dwarf.Isla.Cache.Opcode)

Module Cache.Opcode

Implementation of Cache.Key for opcodes.

It is a special encoding of BytesSeq. If it is short enough to fit in the hash, then we do it. Otherwise we store in a file.

The exact encoding is here (back mean the last/top bit of the integer, i.e. IntBits.back):

Short encoding: bit 0 to back -3 : The data bit back -3 to back: The size of the data bit back : cleared

Long encoding: bit 0 to back -1 : The start of the data bit back -1: set

type t = Server.opcode option
val reloc_id : Relocation.t option -> int
val reloc_of_id : int -> Relocation.t option
val equal : + (Utils.BytesSeq.t * 'a) option -> + (Utils.BytesSeq.t * 'a) option -> + bool
val small_enough : Utils.BytesSeq.t -> int -> bool
val hash : (Utils.BytesSeq.t * Relocation.t option) option -> Utils.IntBits.t
val to_file : 'a -> (Utils.BytesSeq.t * Relocation.t option) option -> unit
val of_file : + Utils.IntBits.t -> + 'a -> + (Utils.BytesSeq.t * Relocation.t option) option
diff --git a/doc/html/read-dwarf/Isla/Cache/TraceList/index.html b/doc/html/read-dwarf/Isla/Cache/TraceList/index.html index f1e7394b..3f2e335d 100644 --- a/doc/html/read-dwarf/Isla/Cache/TraceList/index.html +++ b/doc/html/read-dwarf/Isla/Cache/TraceList/index.html @@ -1,2 +1,2 @@ -TraceList (read-dwarf.Isla.Cache.TraceList)

Module Cache.TraceList

Representation of trace lists on disk.

It is just a list of traces separated by new lines

type t = Base.rtrc list
val to_file : string -> t -> unit
val of_file : string -> t
\ No newline at end of file +TraceList (read-dwarf.Isla.Cache.TraceList)

Module Cache.TraceList

Representation of trace lists on disk.

It is just a list of traces separated by new lines

type t = Base.rtrcs
val to_file : string -> t -> unit
val of_file : string -> t
diff --git a/doc/html/read-dwarf/Isla/Cache/index.html b/doc/html/read-dwarf/Isla/Cache/index.html index 2b8f0e57..532df455 100644 --- a/doc/html/read-dwarf/Isla/Cache/index.html +++ b/doc/html/read-dwarf/Isla/Cache/index.html @@ -1,2 +1,2 @@ -Cache (read-dwarf.Isla.Cache)

Module Isla.Cache

This module provide a caching system for isla trace on top of Server.

It uses the cache named "isla" of Cache

Call start to start and stop. Do not interact directly with the Server if you use the cache.

You can use ensure_started to force the Server to start but you probably shouldn't do that. By the default the Server is only started if the traces of an instruction are required and not in the cache.

type config = Server.config

The type of Isla configuration

module Opcode : sig ... end

Implementation of Cache.Key for opcodes.

module TraceList : sig ... end

Representation of trace lists on disk.

val epoch : int

An epoch independant of the isla version, bump if you change the representation of the traces on disk.

Reset (or not) when bumping isla version (Server.required_version)

The Epoch also include the digest of the Isla configuration. Any change of configuration will wipe out the cache.

module Epoch : sig ... end
module IC : sig ... end

The isla cache module

val cache : (IC.t * config) option Stdlib.ref

This varaible stores the cache RAM representation

val configr : config option Stdlib.ref

If this is set and cache is also set, then the server should be started with the architecture inside this variable when required

val start : config -> unit

Start the caching system. Do not yet start the server

val stop : unit -> unit

Stop the caching system, stop the server if it was started

val ensure_started : unit -> unit

Start the server if not already started

val get_cache : unit -> IC.t * config

Get the cache and fails if the cache wasn't started

val get_traces : Utils.BytesSeq.t -> Base.rtrc list

Get the traces of the opcode given. Use Server if the value is not in the cache

val get_nop : unit -> Base.rtrc

Get the traces of the nop opcode (The initialization code). Use Server if the value is not in the cache

\ No newline at end of file +Cache (read-dwarf.Isla.Cache)

Module Isla.Cache

This module provide a caching system for isla trace on top of Server.

It uses the cache named "isla" of Cache

Call start to start and stop. Do not interact directly with the Server if you use the cache.

You can use ensure_started to force the Server to start but you probably shouldn't do that. By the default the Server is only started if the traces of an instruction are required and not in the cache.

type config = Server.config

The type of Isla configuration

module Opcode : sig ... end

Implementation of Cache.Key for opcodes.

module TraceList : sig ... end

Representation of trace lists on disk.

val epoch : int

An epoch independant of the isla version, bump if you change the representation of the traces on disk.

Reset (or not) when bumping isla version (Server.required_version)

The Epoch also include the digest of the Isla configuration. Any change of configuration will wipe out the cache.

module Epoch : sig ... end
module IC : sig ... end

The isla cache module

val cache : (IC.t * config) option Stdlib.ref

This varaible stores the cache RAM representation

val configr : config option Stdlib.ref

If this is set and cache is also set, then the server should be started with the architecture inside this variable when required

val start : config -> unit

Start the caching system. Do not yet start the server

val stop : unit -> unit

Stop the caching system, stop the server if it was started

val ensure_started : unit -> unit

Start the server if not already started

val get_cache : unit -> IC.t * config

Get the cache and fails if the cache wasn't started

val get_traces : Server.opcode -> Base.rtrcs

Get the traces of the opcode given. Use Server if the value is not in the cache

val get_nop : unit -> Base.rtrc

Get the traces of the nop opcode (The initialization code). Use Server if the value is not in the cache

diff --git a/doc/html/read-dwarf/Isla/Conv/index.html b/doc/html/read-dwarf/Isla/Conv/index.html index fc8e32c7..50a552b6 100644 --- a/doc/html/read-dwarf/Isla/Conv/index.html +++ b/doc/html/read-dwarf/Isla/Conv/index.html @@ -1,2 +1,11 @@ -Conv (read-dwarf.Isla.Conv)

Module Isla.Conv

val ty : Isla__.Base.ty -> 'a Ast.ty
val unop : Isla__.Base.unop -> Ast.unop
val bvarith : Isla__.Base.bvarith -> Ast.bvarith
val bvcomp : Isla__.Base.bvcomp -> Ast.bvcomp
val bvmanyarith : Isla__.Base.bvmanyarith -> Ast.bvmanyarith
val binop : Isla__.Base.binop -> 'm Ast.binop
val manyop : Isla__.Base.manyop -> Ast.manyop
val direct_exp_no_var : ('a Isla__.Base.exp -> ('a'v'b'm) Ast.exp) -> 'a Isla__.Base.exp -> ('a'v'b'm) Ast.exp
val exp_var_conv : (int -> 'v) -> 'a Isla__.Base.exp -> ('a'v'b'm) Ast.exp
val exp : 'a Isla__.Base.exp -> ('a, int, 'b'c) Ast.exp
val exp_var_subst : (int -> 'a -> ('a'v'b'm) Ast.exp) -> 'a Isla__.Base.exp -> ('a'v'b'm) Ast.exp

Convert an expression from isla to Ast but using a var-to-exp conversion function

val exp_add_type_var_subst : (int -> 'a -> ('v'm) Exp.Typed.t) -> 'a Isla__.Base.exp -> ('v'm) Exp.Typed.t

Convert directly from an untyped isla expression to an Exp.Typed by substituing isla variables with already typed expressions

val smt_var_conv : (int -> 'v) -> 'a Isla__.Base.smt -> ('a'v'b'm) Ast.smt
val smt : 'a Isla__.Base.smt -> ('a, int, 'b'c) Ast.smt
\ No newline at end of file +Conv (read-dwarf.Isla.Conv)

Module Isla.Conv

val ty : Base.ty -> 'a Ast.ty
val unop : Base.unop -> Ast.unop
val bvarith : Base.bvarith -> Ast.bvarith
val bvcomp : Base.bvcomp -> Ast.bvcomp
val bvmanyarith : Base.bvmanyarith -> Ast.bvmanyarith
val binop : Base.binop -> 'm Ast.binop
val manyop : Base.manyop -> Ast.manyop
val direct_exp_no_var : + ('a Base.exp -> ('a, 'v, 'b, 'm) Ast.exp) -> + 'a Base.exp -> + ('a, 'v, 'b, 'm) Ast.exp
val exp_var_conv : (int -> 'v) -> 'a Base.exp -> ('a, 'v, 'b, 'm) Ast.exp
val exp : 'a Base.exp -> ('a, int, 'b, 'c) Ast.exp
val exp_var_subst : + (int -> 'a -> ('a, 'v, 'b, 'm) Ast.exp) -> + 'a Base.exp -> + ('a, 'v, 'b, 'm) Ast.exp

Convert an expression from isla to Ast but using a var-to-exp conversion function

val exp_add_type_var_subst : + (int -> 'a -> ('v, 'm) Exp.Typed.t) -> + 'a Base.exp -> + ('v, 'm) Exp.Typed.t

Convert directly from an untyped isla expression to an Exp.Typed by substituing isla variables with already typed expressions

val smt_var_conv : (int -> 'v) -> 'a Base.smt -> ('a, 'v, 'b, 'm) Ast.smt
val smt : 'a Base.smt -> ('a, int, 'b, 'c) Ast.smt
diff --git a/doc/html/read-dwarf/Isla/Manip/index.html b/doc/html/read-dwarf/Isla/Manip/index.html index ba186bcc..3c046128 100644 --- a/doc/html/read-dwarf/Isla/Manip/index.html +++ b/doc/html/read-dwarf/Isla/Manip/index.html @@ -1,2 +1,22 @@ -Manip (read-dwarf.Isla.Manip)

Module Isla.Manip

This module provide generic manipulation function of isla ast

Trace Properties

val is_linear : 'a Isla__.Base.trc -> bool

Check if a trace is linear (has no branches)

Get annotations

val annot_exp : 'a Isla__.Base.exp -> 'a

Get the annotation of an expression

val annot_event : 'a Isla__.Base.event -> 'a

Get the annotation of an event

Non-recursive maps and iters

This section is filled on demand.

direct_a_map_b take a function b -> b and applies it to all b in a, non-recursively. Then a new a with the same structure is formed.

direct_a_iter_b take a function b -> unit and applies it to all b in a, non-recursively.

val direct_exp_map_exp : ('a Isla__.Base.exp -> 'a Isla__.Base.exp) -> 'a Isla__.Base.exp -> 'a Isla__.Base.exp
val direct_exp_iter_exp : ('a Isla__.Base.exp -> unit) -> 'a Isla__.Base.exp -> unit
val direct_smt_map_exp : ('a Isla__.Base.exp -> 'a Isla__.Base.exp) -> 'a Isla__.Base.smt -> 'a Isla__.Base.smt
val direct_event_map_exp : ('a Isla__.Base.exp -> 'a Isla__.Base.exp) -> 'a Isla__.Base.event -> 'a Isla__.Base.event
val direct_event_iter_valu : (Isla__.Base.valu -> unit) -> 'a Isla__.Base.event -> unit
val direct_event_map_valu : (Isla__.Base.valu -> Isla__.Base.valu) -> 'a Isla__.Base.event -> 'a Isla__.Base.event
val direct_valu_iter_valu : (Isla__.Base.valu -> unit) -> Isla__.Base.valu -> unit
val direct_valu_map_valu : (Isla__.Base.valu -> Isla__.Base.valu) -> Isla__.Base.valu -> Isla__.Base.valu

Recursive maps and iters

This section is filled on demand.

a_map_b take a function b -> b and applies it to all the b in a, and do that recursively on all b that appear directly or indirectly in a

a_iter_b take a function b -> unit and applies it to all the b in a, and do that recursively

Doing this when a = b is not well defined, and can be easily done using the direct version from previous section.

In case where a type is not recusive like event, both direct and recursive versions are the same.

val exp_iter_var : (int -> unit) -> 'a Isla__.Base.exp -> unit

iterate a function on all the variable of an expression

val event_iter_valu : (Isla__.Base.valu -> unit) -> 'a Isla__.Base.event -> unit

Variable substitution

val var_subst : (int -> 'a -> 'a Isla__.Base.exp) -> 'a Isla__.Base.exp -> 'a Isla__.Base.exp

Substitute variable with expression according to subst function

Accessor list conversion

val accessor_of_string : string -> Isla__.Base.accessor
val string_of_accessor : Isla__.Base.accessor -> string
val accessor_of_string_list : string list -> Isla__.Base.accessor_list
val string_of_accessor_list : Isla__.Base.accessor_list -> string list

Valu string path access

val valu_get : Isla__.Base.valu -> string list -> Isla__.Base.valu

Follow the path in a value like A.B.C in (struct (|B| (struct (|C| ...))))

Trace Filtering

This is some basic trace filtering that remove unwanted item from the trace in various situations

val split_cycle : 'a Isla__.Base.trc -> 'a Isla__.Base.trc * 'a Isla__.Base.trc

Split the trace between before and after the "cycle" event

val remove_init : 'a Isla__.Base.trc -> 'a Isla__.Base.trc

Remove all events before the "cycle" event, keep the SMT statements

val remove_ignored : Utils.String.t Utils.List.t -> 'a Isla__.Base.trc -> 'a Isla__.Base.trc

Remove all the events related to ignored registers

\ No newline at end of file +Manip (read-dwarf.Isla.Manip)

Module Isla.Manip

This module provide generic manipulation function of isla ast

Trace Properties

val is_linear : 'a Base.trc -> bool

Check if a trace is linear (has no branches)

Get annotations

val annot_exp : 'a Base.exp -> 'a

Get the annotation of an expression

val annot_event : 'a Base.event -> 'a

Get the annotation of an event

Non-recursive maps and iters

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 Base.exp -> 'a Base.exp) -> + 'a Base.exp -> + 'a Base.exp
val direct_exp_iter_exp : ('a Base.exp -> unit) -> 'a Base.exp -> unit
val direct_smt_map_exp : + ('a Base.exp -> 'a Base.exp) -> + 'a Base.smt -> + 'a Base.smt
val direct_event_map_exp : + ('a Base.exp -> 'a Base.exp) -> + 'a Base.event -> + 'a Base.event
val direct_event_iter_valu : (Base.valu -> unit) -> 'a Base.event -> unit
val direct_event_map_valu : + (Base.valu -> Base.valu) -> + 'a Base.event -> + 'a Base.event
val direct_valu_iter_valu : (Base.valu -> unit) -> Base.valu -> unit
val direct_valu_map_valu : (Base.valu -> Base.valu) -> Base.valu -> Base.valu

Recursive maps and iters

This section is filled on demand.

a_map_b take a function b -> b and applies it to all the b in a, and do that recursively on all b that appear directly or indirectly in a

a_iter_b take a function b -> unit and applies it to all the b in a, and do that recursively

Doing this when a = b is not well defined, and can be easily done using the direct version from previous section.

In case where a type is not recusive like event, both direct and recursive versions are the same.

val exp_iter_var : (int -> unit) -> 'a Base.exp -> unit

iterate a function on all the variable of an expression

val event_iter_valu : (Base.valu -> unit) -> 'a Base.event -> unit
val iter_valu_path : + (string list -> Base.valu -> unit) -> + string list -> + Base.valu -> + unit

Iterate a function over a valu broken up into fields, extending the given path.

val map_valu_path : + (string list -> Base.valu -> 'a) -> + string list -> + Base.valu -> + 'a list

Map a function over a valu broken up into fields, extending the given path.

Variable substitution

val var_subst : (int -> 'a -> 'a Base.exp) -> 'a Base.exp -> 'a Base.exp

Substitute variable with expression according to subst function

Accessor list conversion

val accessor_of_string : string -> Base.accessor
val string_of_accessor : Base.accessor -> string
val accessor_of_string_list : string list -> Base.accessor_list
val string_of_accessor_list : Base.accessor_list -> string list

Valu string path access

val valu_get : Base.valu -> string list -> Base.valu

Follow the path in a value like A.B.C in (struct (|B| (struct (|C| ...))))

Trace Filtering

This is some basic trace filtering that remove unwanted item from the trace in various situations

val split_cycle : 'a Base.trc -> 'a Base.trc * 'a Base.trc

Split the trace between before and after the "cycle" event

val remove_init : 'a Base.trc -> 'a Base.trc

Remove all events before the "cycle" event, keep the SMT statements

val remove_ignored : Utils.String.t Utils.List.t -> 'a Base.trc -> 'a Base.trc

Remove all the events related to ignored registers

diff --git a/doc/html/read-dwarf/Isla/Preprocess/index.html b/doc/html/read-dwarf/Isla/Preprocess/index.html index 3b6c43a8..d3a41b76 100644 --- a/doc/html/read-dwarf/Isla/Preprocess/index.html +++ b/doc/html/read-dwarf/Isla/Preprocess/index.html @@ -1,2 +1,2 @@ -Preprocess (read-dwarf.Isla.Preprocess)

Module Isla.Preprocess

This module is about preprocessing isla traces. This includes:

  • Removing exceptional traces
  • Removing useless operation and simplifying
  • Removing all effect of initialization before cycle
  • Splitting branches that are non exceptional the right way

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.

  • Special instructions like smc have zero traces and must be provided with special semantics
  • Normal instructions have one traces (all exceptional cases are classified as UB)
  • Branching instruction have more than one 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 simplify_trc : Base.rtrc -> Base.rtrc

Simplify a simple trace by removing all useless variables

val preprocess : Server.config -> Server.trcs -> Base.rtrc list

Preprocess a group of traces, by removing useless registers (according to the config), removing initialisation code and simplifying with simplify_trc

\ No newline at end of file +Preprocess (read-dwarf.Isla.Preprocess)

Module Isla.Preprocess

This module is about preprocessing isla traces. This includes:

  • Removing exceptional traces
  • Removing useless operation and simplifying
  • Removing all effect of initialization before cycle
  • Splitting branches that are non exceptional the right way

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.

  • Special instructions like smc have zero traces and must be provided with special semantics
  • Normal instructions have one traces (all exceptional cases are classified as UB)
  • Branching instruction have more than one 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 simplify_trc : ?num_segments:int -> Base.rtrc -> Base.rtrc

Simplify a simple trace by removing all useless variables

val preprocess : Server.config -> Server.trcs -> Base.rtrcs

Preprocess a group of traces, by removing useless registers (according to the config), removing initialisation code and simplifying with simplify_trc

diff --git a/doc/html/read-dwarf/Isla/Run/index.html b/doc/html/read-dwarf/Isla/Run/index.html index b172cb58..2b3762cf 100644 --- a/doc/html/read-dwarf/Isla/Run/index.html +++ b/doc/html/read-dwarf/Isla/Run/index.html @@ -1,2 +1,6 @@ -Run (read-dwarf.Isla.Run)

Module Isla.Run

This module provide facility to run Isla trace over states

The main functions are trc for pure interface and trc_mut for imperative interface.

RunError will be thrown when something goes wrong.

It is for testing purpose only, otherwise use Trace.Run. Typing does not work, and some other expected behavior may not work either.

This module can be considered deprecated/legacy.

exception RunError of Isla__.Base.lrng * string

Exception that represent an Isla runtime error which should not happen

val run_error : Isla__.Base.lrng -> ('a, unit, string, 'b) Stdlib.format4 -> 'a

Throw a run error with the string part as formated by the format string

type value_context = State.exp Utils.HashVector.t

The contex of value that associate isla variable numbers to state expression

val get_var : Isla__.Base.lrng -> State.Exp.t Utils.HashVector.t -> int -> State.Exp.t

Get the expression associated to the free variable. Throw RunError, if the variable is no bound. The lrng is for error reporting.

val exp_conv_subst : value_context -> Base.rexp -> State.exp

Convert an Isla expression to an Ast by substituing all free variable with the bound expression in the value_context.

If a variable is not bound, throw RunError

val exp_of_valu : Isla__.Base.lrng -> State.Exp.t Utils.HashVector.t -> Isla__.Base.valu -> State.exp

Give the State.exp that represents the input valu.

A symbolic variable i is represented by the expression bound to it in the provided value_context.

Newly created expression are located with the provided lrng.

If the value is not convertible to a state expression, throw a RunError

val write_to_var : 'a -> 'b Utils.HashVector.t -> int -> 'b -> unit

This function write an expression to symbolic variable. The write is ignored if the variable was already set because isla guarantee that it would be the same value (Trusting Isla here)

val write_to_valu : 'a -> 'b Utils.HashVector.t -> Isla__.Base.valu -> 'b -> unit

This function write an expression to an valu.

If the valu is a variable, it is added to the context, otherwise nothing happens.

val event_mut : value_context -> State.t -> Base.revent -> unit

Run an event on State.t and a value_context by mutating both

val trc_mut : ?⁠vc:State.exp Utils.HashVector.t -> State.t -> Base.rtrc -> unit

This function run an isla trace on a state by mutation. If a vc is provided, then it is used and mutated according to the trace.

Any encountered branch are ignored and their assertion are added to the state

val trc : State.t -> Base.rtrc -> State.t

This function run an isla trace on a state and return the end state as a new state

It is just a wrapper of run_trc_mut that remove the imperative interface The new state is fresh and locked.

\ No newline at end of file +Run (read-dwarf.Isla.Run)

Module Isla.Run

This module provide facility to run Isla trace over states

The main functions are trc for pure interface and trc_mut for imperative interface.

RunError will be thrown when something goes wrong.

It is for testing purpose only, otherwise use Trace.Run. Typing does not work, and some other expected behavior may not work either.

This module can be considered deprecated/legacy.

  • deprecated Should not be used by new modules
exception RunError of Base.lrng * string

Exception that represent an Isla runtime error which should not happen

val run_error : Base.lrng -> ('a, unit, string, 'b) Stdlib.format4 -> 'a

Throw a run error with the string part as formated by the format string

type value_context = State.exp Utils.HashVector.t

The contex of value that associate isla variable numbers to state expression

Get the expression associated to the free variable. Throw RunError, if the variable is no bound. The lrng is for error reporting.

val exp_conv_subst : value_context -> Base.rexp -> State.exp

Convert an Isla expression to an Ast by substituing all free variable with the bound expression in the value_context.

If a variable is not bound, throw RunError

val exp_of_valu : + Base.lrng -> + State.Exp.t Utils.HashVector.t -> + Base.valu -> + State.exp

Give the State.exp that represents the input valu.

A symbolic variable i is represented by the expression bound to it in the provided value_context.

Newly created expression are located with the provided lrng.

If the value is not convertible to a state expression, throw a RunError

val write_to_var : 'a -> 'b Utils.HashVector.t -> int -> 'b -> unit

This function write an expression to symbolic variable. The write is ignored if the variable was already set because isla guarantee that it would be the same value (Trusting Isla here)

val write_to_valu : 'a -> 'b Utils.HashVector.t -> Base.valu -> 'b -> unit

This function write an expression to an valu.

If the valu is a variable, it is added to the context, otherwise nothing happens.

val event_mut : value_context -> State.t -> Base.revent -> unit

Run an event on State.t and a value_context by mutating both

val trc_mut : ?vc:State.exp Utils.HashVector.t -> State.t -> Base.rtrc -> unit

This function run an isla trace on a state by mutation. If a vc is provided, then it is used and mutated according to the trace.

Any encountered branch are ignored and their assertion are added to the state

val trc : State.t -> Base.rtrc -> State.t

This function run an isla trace on a state and return the end state as a new state

It is just a wrapper of run_trc_mut that remove the imperative interface The new state is fresh and locked.

diff --git a/doc/html/read-dwarf/Isla/Server/Cmd/index.html b/doc/html/read-dwarf/Isla/Server/Cmd/index.html index b7394571..fd41a30f 100644 --- a/doc/html/read-dwarf/Isla/Server/Cmd/index.html +++ b/doc/html/read-dwarf/Isla/Server/Cmd/index.html @@ -1,2 +1,2 @@ -Cmd (read-dwarf.Isla.Server.Cmd)

Module Server.Cmd

This module provide a CLI subcommand to test isla directly. All isla output is reported as raw text

val server_test : Config.Arch.t -> unit
val term : unit Cmdliner.Term.t
val info : Cmdliner.Term.info
val command : unit Cmdliner.Term.t * Cmdliner.Term.info
\ No newline at end of file +Cmd (read-dwarf.Isla.Server.Cmd)

Module Server.Cmd

This module provide a CLI subcommand to test isla directly. All isla output is reported as raw text

val server_test : Config.Arch.t -> unit
val term : unit Cmdliner.Term.t
val info : Cmdliner.Cmd.info
val command : unit Cmdliner.Term.t * Cmdliner.Cmd.info
diff --git a/doc/html/read-dwarf/Isla/Server/index.html b/doc/html/read-dwarf/Isla/Server/index.html index c3939ef7..a3ffb766 100644 --- a/doc/html/read-dwarf/Isla/Server/index.html +++ b/doc/html/read-dwarf/Isla/Server/index.html @@ -1,2 +1,2 @@ -Server (read-dwarf.Isla.Server)

Module Isla.Server

This module is about launching isla as a background server and using it

Important remark: even if I call isla a server because in a logical sense read-dwarf is making request to isla and isla is serving them, in Unix sense read-dwarf is the server because it listen to the socket and isla connect to it.

module ConfigFile = Config.File
module CommonOpt = Config.CommonOpt
module Server = Utils.Cmd.SocketServer
type config = Config.t

The configuration record type

type trcs = (bool * Base.rtrc) list

The raw output of the server for an instruction.

It is a list of traces, each with a flag telling if they are normal traces (no processor exception/fault) or not

val required_version : string

Bump when updating isla. TODO: move the version checking to allow a range of version. Also, right now the cache invalidation is based on this and not on the actual isla version, which may be dangerous.

val req_num : int Stdlib.ref
val server : Server.t option Stdlib.ref

This instance of socket server for isla

val get_server : unit -> Server.t

Assume the server is started and get it out of the reference

val cmd_of_config : config -> string -> string array

Compute the isla-client command line from the isla configuration

val raw_start : config -> unit

Start the server with the specified architecture, do not attempt any checks

val raw_stop : unit -> unit

Stop the server by cutting the connection.

type basic_answer =
| Error
| Version of string
| StartTraces
| Trace of bool * string
| EndTraces

This should match exactly with the Answer type in isla-client code

val read_basic_answer : unit -> basic_answer

Read an answer from isla-client. This must match exactly write_answer in client.rs in isla

type answer =
| Version of string
| Traces of (bool * string) list

The interpreted answer. If the protocol is followed, then one request lead to exactly one answer of that type

val expect_version : answer -> string

Expect a version answer and fails if it is not the case

val expect_traces : answer -> (bool * string) list

Expect isla traces and fails if it is not the case

val expect_parsed_traces : answer -> trcs

Expect isla traces and fails if it is not the case, additionally parse them

exception IslaError

When isla encounter a non fatal error with that specific request. This error is recoverable and the sever can accept other requests

val read_answer : unit -> answer

Read the answer from isla, block until full answer

val pp_answer : answer -> PPrintEngine.document

Answer pretty printer

type request =
| TEXT_ASM of string
| ASM of Utils.BytesSeq.t
| VERSION
| STOP

The type of a request to isla

val string_of_request : request -> string

Convert a request into the string message expected by isla-client This should match the protocol

val send_string_request : string -> unit

Send a string request to the server, and do not wait for any answer

val string_request : string -> answer

Same as request but takes the request directly as a string

val request : request -> answer

Send a request and wait for answer

val request_bin_parsed : Utils.BytesSeq.t -> trcs

Request the traces of a binary instruction and parse the result.

This is the main entry point of this module.

val send_request : request -> unit

Send a request without expecting any answer

val stop : unit -> unit

Stop isla client by sending a stop request

val start : config -> unit

Start isla and check version

module Cmd : sig ... end

This module provide a CLI subcommand to test isla directly. All isla output is reported as raw text

\ No newline at end of file +Server (read-dwarf.Isla.Server)

Module Isla.Server

This module is about launching isla as a background server and using it

Important remark: even if I call isla a server because in a logical sense read-dwarf is making request to isla and isla is serving them, in Unix sense read-dwarf is the server because it listen to the socket and isla connect to it.

module ConfigFile = Config.File
module CommonOpt = Config.CommonOpt
module Server = Utils.Cmd.SocketServer
type config = Config.t

The configuration record type

type trcs = Base.instruction_segments option * (bool * Base.rtrc) list

The raw output of the server for an instruction.

It is a list of traces, each with a flag telling if they are normal traces (no processor exception/fault) or not

type opcode = Utils.BytesSeq.t * Relocation.t option
val required_version : string

Bump when updating isla. TODO: move the version checking to allow a range of version. Also, right now the cache invalidation is based on this and not on the actual isla version, which may be dangerous.

val req_num : int Stdlib.ref
val server : Server.t option Stdlib.ref

This instance of socket server for isla

val get_server : unit -> Server.t

Assume the server is started and get it out of the reference

val cmd_of_config : config -> string -> string array

Compute the isla-client command line from the isla configuration

val raw_start : config -> unit

Start the server with the specified architecture, do not attempt any checks

val raw_stop : unit -> unit

Stop the server by cutting the connection.

type basic_answer =
  1. | Error
  2. | Version of string
  3. | StartTraces
  4. | Trace of bool * string
  5. | EndTraces
  6. | Segments of string

This should match exactly with the Answer type in isla-client code

val read_basic_answer : unit -> basic_answer

Read an answer from isla-client. This must match exactly write_answer in client.rs in isla

type answer =
  1. | Version of string
  2. | Traces of string option * (bool * string) list

The interpreted answer. If the protocol is followed, then one request lead to exactly one answer of that type

val expect_version : answer -> string

Expect a version answer and fails if it is not the case

val expect_traces : answer -> string option * (bool * string) list

Expect isla traces and fails if it is not the case

val expect_parsed_traces : answer -> trcs

Expect isla traces and fails if it is not the case, additionally parse them

exception IslaError

When isla encounter a non fatal error with that specific request. This error is recoverable and the sever can accept other requests

val read_answer : unit -> answer

Read the answer from isla, block until full answer

val pp_answer : answer -> Utils.Pp.document

Answer pretty printer

type request =
  1. | TEXT_ASM of string
  2. | ASM of opcode
  3. | VERSION
  4. | STOP

The type of a request to isla

val string_of_request : request -> string

Convert a request into the string message expected by isla-client This should match the protocol

val send_string_request : string -> unit

Send a string request to the server, and do not wait for any answer

val string_request : string -> answer

Same as request but takes the request directly as a string

val request : request -> answer

Send a request and wait for answer

val request_bin_parsed : opcode -> trcs

Request the traces of a binary instruction and parse the result.

This is the main entry point of this module.

val send_request : request -> unit

Send a request without expecting any answer

val stop : unit -> unit

Stop isla client by sending a stop request

val start : config -> unit

Start isla and check version

Test that isla can start and keep a valid version

module Cmd : sig ... end

This module provide a CLI subcommand to test isla directly. All isla output is reported as raw text

diff --git a/doc/html/read-dwarf/Isla/Test/index.html b/doc/html/read-dwarf/Isla/Test/index.html index 07fd6b68..4f23db37 100644 --- a/doc/html/read-dwarf/Isla/Test/index.html +++ b/doc/html/read-dwarf/Isla/Test/index.html @@ -1,2 +1,6 @@ -Test (read-dwarf.Isla.Test)

Module Isla.Test

This file about testing interaction with Isla for single instructions

module SMT = Z3
type pmode =
| DUMP

Dump isla output on standard output

| PARSE

parse isla output and prettyprint it

| TYPE

Type isla output and dump var types. Also dump the deduced register file

| RUN

Run the the isla output on a test state and print all branches and states

| SIMP

Run the the isla output on a test state and also print a simplified version

The type of processing requested

type isla_mode =
| RAW

Do not call isla and take the input test as if it was isla output

| ASM

Call isla with text assembly

| HEX

Call isla with text hexadecimal as binary encoding of instruction

| BIN

Call isla with actually binary (for now 4 bytes)

The input syntax

type imode =
| CMD

Read the input as the main command line argument

| FILE

Read the input in a file

| ELF of string

Read the input from an elf symbol + offset. implies BIN for isla_mode

The way input is taken

val arg : string Cmdliner.Term.t
val direct : bool Cmdliner.Term.t
val bin : bool Cmdliner.Term.t
val hex : bool Cmdliner.Term.t
val noparse : bool Cmdliner.Term.t
val preprocess : bool Cmdliner.Term.t
val typer : bool Cmdliner.Term.t
val run : bool Cmdliner.Term.t
val simp : bool Cmdliner.Term.t
val file : bool Cmdliner.Term.t
val sym : string option Cmdliner.Term.t
val input_f2m : bool -> string option -> imode Cmdliner.Term.ret

Input flag to mode conversion

val imode_term : imode Cmdliner.Term.t
val input : imode -> string -> (string * string) Cmdliner.Term.ret

Input takes the imode and the main argument and returns the filename and input string

val input_term : (string * string) Cmdliner.Term.t
val isla_f2m : bool -> bool -> bool -> 'a option -> isla_mode Cmdliner.Term.ret

Convert various flag describe the mode of operation into the mode of operation If sym is activated, then the default mode is BIN and not ASM

val isla_mode_term : isla_mode Cmdliner.Term.t
val isla_mode_to_request : isla_mode -> string -> Server.request
val isla_run : isla_mode -> Config.Arch.t -> (string * string) -> string * string * Server.config

Run isla and return a text trace with a filename (if mode is RAW than just return the trace and filename without isla)

If isla return multiple traces, just silently pick the first non-exceptional one

val isla_term : (string * string * Server.config) Cmdliner.Term.t
val processing_f2m : bool -> bool -> bool -> bool -> pmode

How far into the processing pipeline we go. We just pick the deepest option chosen

val pmode_term : pmode Cmdliner.Term.t
val processing : bool -> pmode -> (string * string * Server.config) -> unit

Does the actual processing of the trace

val term : unit Cmdliner.Term.t
val info : Cmdliner.Term.info
val command : unit Cmdliner.Term.t * Cmdliner.Term.info
\ No newline at end of file +Test (read-dwarf.Isla.Test)

Module Isla.Test

This file about testing interaction with Isla for single instructions

module SMT = Z3
type pmode =
  1. | DUMP
    (*

    Dump isla output on standard output

    *)
  2. | PARSE
    (*

    parse isla output and prettyprint it

    *)
  3. | TYPE
    (*

    Type isla output and dump var types. Also dump the deduced register file

    *)
  4. | RUN
    (*

    Run the the isla output on a test state and print all branches and states

    *)
  5. | SIMP
    (*

    Run the the isla output on a test state and also print a simplified version

    *)

The type of processing requested

type isla_mode =
  1. | RAW
    (*

    Do not call isla and take the input test as if it was isla output

    *)
  2. | ASM
    (*

    Call isla with text assembly

    *)
  3. | HEX
    (*

    Call isla with text hexadecimal as binary encoding of instruction

    *)
  4. | BIN
    (*

    Call isla with actually binary (for now 4 bytes)

    *)

The input syntax

type imode =
  1. | CMD
    (*

    Read the input as the main command line argument

    *)
  2. | FILE
    (*

    Read the input in a file

    *)
  3. | ELF of string
    (*

    Read the input from an elf symbol + offset. implies BIN for isla_mode

    *)

The way input is taken

val arg : string Cmdliner.Term.t
val direct : bool Cmdliner.Term.t
val bin : bool Cmdliner.Term.t
val hex : bool Cmdliner.Term.t
val noparse : bool Cmdliner.Term.t
val preprocess : bool Cmdliner.Term.t
val typer : bool Cmdliner.Term.t
val run : bool Cmdliner.Term.t
val simp : bool Cmdliner.Term.t
val file : bool Cmdliner.Term.t
val sym : string option Cmdliner.Term.t
val input_f2m : bool -> string option -> imode Cmdliner.Term.ret

Input flag to mode conversion

val imode_term : imode Cmdliner.Term.t
val input : imode -> string -> (string * string) Cmdliner.Term.ret

Input takes the imode and the main argument and returns the filename and input string

val input_term : (string * string) Cmdliner.Term.t
val isla_f2m : bool -> bool -> bool -> 'a option -> isla_mode Cmdliner.Term.ret

Convert various flag describe the mode of operation into the mode of operation If sym is activated, then the default mode is BIN and not ASM

val isla_mode_term : isla_mode Cmdliner.Term.t
val isla_mode_to_request : isla_mode -> string -> Server.request
val isla_run : + isla_mode -> + Config.Arch.t -> + (string * string) -> + string * string * Server.config

Run isla and return a text trace with a filename (if mode is RAW than just return the trace and filename without isla)

If isla return multiple traces, just silently pick the first non-exceptional one

val isla_term : (string * string * Server.config) Cmdliner.Term.t
val processing_f2m : bool -> bool -> bool -> bool -> pmode

How far into the processing pipeline we go. We just pick the deepest option chosen

val pmode_term : pmode Cmdliner.Term.t
val processing : bool -> pmode -> (string * string * Server.config) -> unit

Does the actual processing of the trace

val term : unit Cmdliner.Term.t
val info : Cmdliner.Cmd.info
val command : unit Cmdliner.Term.t * Cmdliner.Cmd.info
diff --git a/doc/html/read-dwarf/Isla/Type/index.html b/doc/html/read-dwarf/Isla/Type/index.html index cf92e84a..66a01a7d 100644 --- a/doc/html/read-dwarf/Isla/Type/index.html +++ b/doc/html/read-dwarf/Isla/Type/index.html @@ -1,2 +1,16 @@ -Type (read-dwarf.Isla.Type)

Module Isla.Type

This module is about type isla trace and register discovery.

The actual goal is dicovering the existence and type of registers from Isla traces, but this require type the trace with ty and thus full type checking of traces. We expect isla to be correct, so a type error would be very surprising.

type type_context = Isla__.Base.ty Utils.HashVector.t

A context that associate Isla types to Isla variables

type lty = Isla__.Base.lrng * Isla__.Base.ty

A group of the source range and type for an expression

exception TypeError of Isla__.Base.lrng * string

Exception that represent an Isla typing error

val tassert : Isla__.Base.lrng -> string -> bool -> unit

Assert some properties for type correctness. Requires a lrng and a string error message

val expect_bool : string -> (Isla__.Base.lrng * Isla__.Base.ty) -> unit
val expect_bv : string -> (Isla__.Base.lrng * Isla__.Base.ty) -> int
val expect_enum : string -> (Isla__.Base.lrng * Isla__.Base.ty) -> int
val type_unop : Isla__.Base.lrng -> Isla__.Base.unop -> lty -> Isla__.Base.ty
val type_binop : Isla__.Base.lrng -> Isla__.Base.binop -> (Isla__.Base.lrng * Isla__.Base.ty) -> (Isla__.Base.lrng * Isla__.Base.ty) -> Isla__.Base.ty
val type_manyop : Isla__.Base.lrng -> Isla__.Base.manyop -> lty list -> Isla__.Base.ty
val type_valu : Isla__.Base.lrng -> type_context -> Isla__.Base.valu -> (State.Reg.Path.t * State.Reg.ty) list

Take an Isla value and a context and give the list of field that correspond to that value. Those fields would need to be prefixed with the top register name before being added in State.Reg

val ltype_expr : type_context -> Isla__.Base.lrng Isla__.Base.exp -> lty
val type_expr : type_context -> Isla__.Base.lrng Isla__.Base.exp -> Isla__.Base.ty
val type_trc : ?⁠tc:Isla__.Base.ty Utils.HashVector.t -> Base.rtrc -> Isla__.Base.ty Utils.HashVector.t

Add the new register found in the trace and returns the type context for free variables

val pp_tcontext : Isla_lang__.Isla_lang_ast.ty Utils.HashVector.t -> Utils.Pp.document

Print a type context for debugging

\ No newline at end of file +Type (read-dwarf.Isla.Type)

Module Isla.Type

This module is about type isla trace and register discovery.

The actual goal is dicovering the existence and type of registers from Isla traces, but this require type the trace with ty and thus full type checking of traces. We expect isla to be correct, so a type error would be very surprising.

type type_context = Base.ty Utils.HashVector.t

A context that associate Isla types to Isla variables

type lty = Base.lrng * Base.ty

A group of the source range and type for an expression

exception TypeError of Base.lrng * string

Exception that represent an Isla typing error

val tassert : Base.lrng -> string -> bool -> unit

Assert some properties for type correctness. Requires a lrng and a string error message

val expect_bool : string -> (Base.lrng * Base.ty) -> unit
val expect_bv : string -> (Base.lrng * Base.ty) -> int
val expect_enum : string -> (Base.lrng * Base.ty) -> int
val type_unop : Base.lrng -> Base.unop -> lty -> Base.ty
val type_binop : + Base.lrng -> + Base.binop -> + (Base.lrng * Base.ty) -> + (Base.lrng * Base.ty) -> + Base.ty
val type_manyop : Base.lrng -> Base.manyop -> lty list -> Base.ty
val type_valu : + Base.lrng -> + type_context -> + Base.valu -> + (State.Reg.Path.t * State.Reg.ty) list

Take an Isla value and a context and give the list of field that correspond to that value. Those fields would need to be prefixed with the top register name before being added in State.Reg

val ltype_expr : type_context -> Base.lrng Base.exp -> lty
val type_expr : type_context -> Base.lrng Base.exp -> Base.ty

Add the new register found in the trace and returns the type context for free variables

val pp_tcontext : + Isla_lang__.Isla_lang_ast.ty Utils.HashVector.t -> + Utils.Pp.document

Print a type context for debugging

diff --git a/doc/html/read-dwarf/Isla/index.html b/doc/html/read-dwarf/Isla/index.html index 1ee0a924..26d90f7f 100644 --- a/doc/html/read-dwarf/Isla/index.html +++ b/doc/html/read-dwarf/Isla/index.html @@ -1,2 +1,25 @@ -Isla (read-dwarf.Isla)

Module Isla

include Base

Aliases

include Isla_lang.AST
type enum = int * int
type lrng = Isla_lang__Isla_lang_ast.lrng =
| UnknownRng
| Generated of lrng
| Range of Stdlib.Lexing.position * Stdlib.Lexing.position
exception Parse_error_locn of lrng * string
val pp_lpos : Stdlib.Lexing.position -> PPrint.document
val pp_lrng : lrng -> PPrint.document
type bvmanyarith = Isla_lang__Isla_lang_ast.bvmanyarith =
| Bvand
| Bvor
| Bvxor
| Bvadd
| Bvmul
type bvcomp = Isla_lang__Isla_lang_ast.bvcomp =
| Bvult
| Bvslt
| Bvule
| Bvsle
| Bvuge
| Bvsge
| Bvugt
| Bvsgt
type bvarith = Isla_lang__Isla_lang_ast.bvarith =
| Bvnand
| Bvnor
| Bvxnor
| Bvsub
| Bvudiv
| Bvudivi
| Bvsdiv
| Bvsdivi
| Bvurem
| Bvsrem
| Bvsmod
| Bvshl
| Bvlshr
| Bvashr
type manyop = Isla_lang__Isla_lang_ast.manyop =
| And
| Or
| Bvmanyarith of bvmanyarith
| Concat
type unop = Isla_lang__Isla_lang_ast.unop =
| Not
| Bvnot
| Bvredand
| Bvredor
| Bvneg
| Extract of int * int
| ZeroExtend of int
| SignExtend of int
type binop = Isla_lang__Isla_lang_ast.binop =
| Eq
| Bvarith of bvarith
| Bvcomp of bvcomp
type accessor = Isla_lang__Isla_lang_ast.accessor =
| Field of string
type ty = Isla_lang__Isla_lang_ast.ty =
| Ty_Bool
| Ty_BitVec of int
| Ty_Enum of int
| Ty_Array of ty * ty
type 'a exp = 'a Isla_lang__Isla_lang_ast.exp =
| Var of int * 'a
| Bits of string * 'a
| Bool of bool * 'a
| Enum of enum * 'a
| Unop of unop * 'a exp * 'a
| Binop of binop * 'a exp * 'a exp * 'a
| Manyop of manyop * 'a exp list * 'a
| Ite of 'a exp * 'a exp * 'a exp * 'a
type valu = Isla_lang__Isla_lang_ast.valu =
| Val_Symbolic of int
| Val_Bool of bool
| Val_I of int * int
| Val_Bits of string
| Val_Enum of enum
| Val_String of string
| Val_Unit
| Val_NamedUnit of string
| Val_Vector of valu list
| Val_List of valu list
| Val_Struct of (string * valu) list
| Val_Poison
type accessor_list = Isla_lang__Isla_lang_ast.accessor_list =
| Nil
| Cons of accessor list
type 'a smt = 'a Isla_lang__Isla_lang_ast.smt =
| DeclareConst of int * ty
| DefineConst of int * 'a exp
| Assert of 'a exp
| DefineEnum of int
type valu_option = valu option
type valu_concrete = Isla_lang__Isla_lang_ast.valu_concrete =
| CVal_Bool of bool
| CVal_I of int * int
| CVal_Bits of string
| CVal_Enum of enum
| CVal_String of string
| CVal_Unit
| CVal_NamedUnit of string
| CVal_Vector of valu list
| CVal_List of valu list
| CVal_Struct of (string * valu) list
| CVal_Poison
type 'a event = 'a Isla_lang__Isla_lang_ast.event =
| Smt of 'a smt * 'a
| Branch of int * string * 'a
| ReadReg of string * accessor_list * valu * 'a
| WriteReg of string * accessor_list * valu * 'a
| ReadMem of valu * valu * valu * int * valu_option * 'a
| WriteMem of int * valu * valu * valu * int * valu_option * 'a
| BranchAddress of valu * 'a
| Barrier of valu * 'a
| CacheOp of valu * valu * 'a
| MarkReg of string * string * 'a
| Cycle of 'a
| Instr of valu * 'a
| Sleeping of int * 'a
| WakeRequest of 'a
| SleepRequest of 'a
type 'a trc = 'a Isla_lang__Isla_lang_ast.trc =
| Trace of 'a event list
type 'a exp_val = 'a Isla_lang__Isla_lang_ast.exp_val =
| EV_Bits of string * 'a
| EV_Bool of bool * 'a
| EV_Enum of enum * 'a
| EV_Unop of unop * 'a exp_val * 'a
| EV_Binop of binop * 'a exp_val * 'a exp_val * 'a
| EV_Manyop of manyop * 'a exp_val list * 'a
| EV_Ite of 'a exp_val * 'a exp_val * 'a exp_val * 'a
module Lexer = Isla_lang.Lexer
module Parser = Isla_lang.Parser
type loc = Stdlib.Lexing.position
type rtrc = lrng trc

The type of raw traces out of the parser

type revent = lrng event

The type of raw events out of the parser

type rsmt = lrng smt

The type of raw SMT declaration out of the parser

type rexp = lrng exp

The type of raw expressions out of the parser

IslaTrace parsing

exception ParseError of loc * string

Exception that represent an Isla parsing error

exception LexError of loc * string

Exception that represent an Isla lexing error

type lexer = Stdlib.Lexing.lexbuf -> Parser.token
type 'a parser = lexer -> Stdlib.Lexing.lexbuf -> 'a
val parse : 'a parser -> ?⁠filename:string -> Stdlib.Lexing.lexbuf -> 'a

Parse a single Isla instruction output from a Lexing.lexbuf

val parse_exp : ?⁠filename:string -> Stdlib.Lexing.lexbuf -> rexp

Parse a single Isla expression from a Lexing.lexbuf

val parse_exp_string : ?⁠filename:string -> string -> rexp

Parse a single Isla expression from a string

val parse_exp_channel : ?⁠filename:string -> Stdlib.in_channel -> rexp

Parse a single Isla expression from a channel

val parse_trc : ?⁠filename:string -> Stdlib.Lexing.lexbuf -> rtrc

Parse an Isla trace from a Lexing.lexbuf

val parse_trc_string : ?⁠filename:string -> string -> rtrc

Parse an Isla trace from a string

val parse_trc_channel : ?⁠filename:string -> Stdlib.in_channel -> rtrc

Parse an Isla trace from a channel

IslaTrace pretty printing

include Isla_lang.PP
val pp_raw_vvar : int -> PPrintEngine.document
val pp_raw_name : string -> PPrintEngine.document
val pp_raw_enum_ty : int -> PPrintEngine.document
val pp_raw_enum : Isla_lang__.Isla_lang_ast.enum -> PPrintEngine.document
val pp_raw_int : int -> PPrintEngine.document
val pp_raw_bvi : int -> PPrintEngine.document
val pp_raw_bv : string -> PPrintEngine.document
val pp_raw_str : string -> PPrintEngine.document
val pp_raw_ty : Isla_lang__.Isla_lang_ast.ty -> PPrintEngine.document
val pp_raw_bool : bool -> PPrintEngine.document
val pp_raw_unop : Isla_lang__.Isla_lang_ast.unop -> PPrintEngine.document
val pp_raw_bvarith : Isla_lang__.Isla_lang_ast.bvarith -> PPrintEngine.document
val pp_raw_bvcomp : Isla_lang__.Isla_lang_ast.bvcomp -> PPrintEngine.document
val pp_raw_binop : Isla_lang__.Isla_lang_ast.binop -> PPrintEngine.document
val pp_raw_bvmanyarith : Isla_lang__.Isla_lang_ast.bvmanyarith -> PPrintEngine.document
val pp_raw_manyop : Isla_lang__.Isla_lang_ast.manyop -> PPrintEngine.document
val pp_raw_exp : 'a Isla_lang__.Isla_lang_ast.exp -> PPrintEngine.document
val pp_raw_exp_val : 'a Isla_lang__.Isla_lang_ast.exp_val -> PPrintEngine.document
val pp_raw_smt : 'a Isla_lang__.Isla_lang_ast.smt -> PPrintEngine.document
val pp_raw_valu : Isla_lang__.Isla_lang_ast.valu -> PPrintEngine.document
val pp_raw_selem : (string * Isla_lang__.Isla_lang_ast.valu) -> PPrintEngine.document
val pp_raw_valu_concrete : Isla_lang__.Isla_lang_ast.valu_concrete -> PPrintEngine.document
val pp_raw_selem_concrete : (string * Isla_lang__.Isla_lang_ast.valu_concrete) -> PPrintEngine.document
val pp_raw_accessor : Isla_lang__.Isla_lang_ast.accessor -> PPrintEngine.document
val pp_raw_accessor_list : Isla_lang__.Isla_lang_ast.accessor_list -> PPrintEngine.document
val pp_raw_valu_option : Isla_lang__.Isla_lang_ast.valu_option -> PPrintEngine.document
val pp_raw_event : 'a Isla_lang__.Isla_lang_ast.event -> PPrintEngine.document
val pp_raw_trc : 'a Isla_lang__.Isla_lang_ast.trc -> PPrintEngine.document
val pp_vvar : int -> PPrintEngine.document
val pp_name : string -> PPrintEngine.document
val pp_enum_ty : int -> PPrintEngine.document
val pp_enum : Isla_lang__.Isla_lang_ast.enum -> PPrintEngine.document
val pp_int : int -> PPrintEngine.document
val pp_bvi : int -> PPrintEngine.document
val pp_bv : string -> PPrintEngine.document
val pp_str : string -> PPrintEngine.document
val pp_j : int -> string
val pp_ty : Isla_lang__.Isla_lang_ast.ty -> PPrintEngine.document
val pp_bool : bool -> PPrintEngine.document
val pp_unop : Isla_lang__.Isla_lang_ast.unop -> PPrintEngine.document
val pp_bvarith : Isla_lang__.Isla_lang_ast.bvarith -> PPrintEngine.document
val pp_bvcomp : Isla_lang__.Isla_lang_ast.bvcomp -> PPrintEngine.document
val pp_binop : Isla_lang__.Isla_lang_ast.binop -> PPrintEngine.document
val pp_bvmanyarith : Isla_lang__.Isla_lang_ast.bvmanyarith -> PPrintEngine.document
val pp_manyop : Isla_lang__.Isla_lang_ast.manyop -> PPrintEngine.document
val pp_exp : 'a Isla_lang__.Isla_lang_ast.exp -> PPrintEngine.document
val pp_exp_val : 'a Isla_lang__.Isla_lang_ast.exp_val -> PPrintEngine.document
val pp_smt : 'a Isla_lang__.Isla_lang_ast.smt -> PPrintEngine.document
val pp_valu : Isla_lang__.Isla_lang_ast.valu -> PPrintEngine.document
val pp_selem : (string * Isla_lang__.Isla_lang_ast.valu) -> PPrintEngine.document
val pp_valu_concrete : Isla_lang__.Isla_lang_ast.valu_concrete -> PPrintEngine.document
val pp_selem_concrete : (string * Isla_lang__.Isla_lang_ast.valu_concrete) -> PPrintEngine.document
val pp_accessor : Isla_lang__.Isla_lang_ast.accessor -> PPrintEngine.document
val pp_accessor_list : Isla_lang__.Isla_lang_ast.accessor_list -> PPrintEngine.document
val pp_valu_option : Isla_lang__.Isla_lang_ast.valu_option -> PPrintEngine.document
val pp_event : 'a Isla_lang__.Isla_lang_ast.event -> PPrintEngine.document
val pp_trc : 'a Isla_lang__.Isla_lang_ast.trc -> PPrintEngine.document
module Base : sig ... end

This module wraps all isla-lang functionality. No other module should directly touch the Isla_lang module.

module Cache : sig ... end

This module provide a caching system for isla trace on top of Server.

module Conv : sig ... end
module Manip : sig ... end

This module provide generic manipulation function of isla ast

module Preprocess : sig ... end

This module is about preprocessing isla traces. This includes:

module Run : sig ... end

This module provide facility to run Isla trace over states

module Server : sig ... end

This module is about launching isla as a background server and using it

module Test : sig ... end

This file about testing interaction with Isla for single instructions

module Type : sig ... end

This module is about type isla trace and register discovery.

\ No newline at end of file +Isla (read-dwarf.Isla)

Module Isla

include module type of struct include Base end

Aliases

include module type of struct include Isla_lang.AST end
type enum = int * int
type lrng = Isla_lang__Isla_lang_ast.lrng =
  1. | UnknownRng
  2. | Generated of lrng
  3. | Range of Stdlib.Lexing.position * Stdlib.Lexing.position
exception Parse_error_locn of lrng * string
val pp_lpos : Stdlib.Lexing.position -> PPrint.document
val pp_lrng : lrng -> PPrint.document
type accessor = Isla_lang__Isla_lang_ast.accessor =
  1. | Field of string
type bvarith = Isla_lang__Isla_lang_ast.bvarith =
  1. | Bvnand
  2. | Bvnor
  3. | Bvxnor
  4. | Bvsub
  5. | Bvudiv
  6. | Bvudivi
  7. | Bvsdiv
  8. | Bvsdivi
  9. | Bvurem
  10. | Bvsrem
  11. | Bvsmod
  12. | Bvshl
  13. | Bvlshr
  14. | Bvashr
type bvcomp = Isla_lang__Isla_lang_ast.bvcomp =
  1. | Bvult
  2. | Bvslt
  3. | Bvule
  4. | Bvsle
  5. | Bvuge
  6. | Bvsge
  7. | Bvugt
  8. | Bvsgt
type bvmanyarith = Isla_lang__Isla_lang_ast.bvmanyarith =
  1. | Bvand
  2. | Bvor
  3. | Bvxor
  4. | Bvadd
  5. | Bvmul
type accessor_list = Isla_lang__Isla_lang_ast.accessor_list =
  1. | Nil
  2. | Cons of accessor list
type unop = Isla_lang__Isla_lang_ast.unop =
  1. | Not
  2. | Bvnot
  3. | Bvredand
  4. | Bvredor
  5. | Bvneg
  6. | Extract of int * int
  7. | ZeroExtend of int
  8. | SignExtend of int
type binop = Isla_lang__Isla_lang_ast.binop =
  1. | Eq
  2. | Bvarith of bvarith
  3. | Bvcomp of bvcomp
type manyop = Isla_lang__Isla_lang_ast.manyop =
  1. | And
  2. | Or
  3. | Bvmanyarith of bvmanyarith
  4. | Concat
type base_val = Isla_lang__Isla_lang_ast.base_val =
  1. | Val_Symbolic of int
  2. | Val_Bool of bool
  3. | Val_Bits of string
  4. | Val_Enum of enum
type assume_val = Isla_lang__Isla_lang_ast.assume_val =
  1. | AVal_Var of string * accessor_list
  2. | AVal_Bool of bool
  3. | AVal_Bits of string
  4. | AVal_Enum of enum
type ty = Isla_lang__Isla_lang_ast.ty =
  1. | Ty_Bool
  2. | Ty_BitVec of int
  3. | Ty_Enum of int
  4. | Ty_Array of ty * ty
type !'a exp = 'a Isla_lang__Isla_lang_ast.exp =
  1. | Val of base_val * 'a
  2. | Unop of unop * 'a exp * 'a
  3. | Binop of binop * 'a exp * 'a exp * 'a
  4. | Manyop of manyop * 'a exp list * 'a
  5. | Ite of 'a exp * 'a exp * 'a exp * 'a
type valu = Isla_lang__Isla_lang_ast.valu =
  1. | RegVal_Base of base_val
  2. | RegVal_I of int * int
  3. | RegVal_String of string
  4. | RegVal_Unit
  5. | RegVal_Vector of valu list
  6. | RegVal_List of valu list
  7. | RegVal_Struct of (string * valu) list
  8. | RegVal_Constructor of string * valu
  9. | RegVal_Poison
type !'a a_exp = 'a Isla_lang__Isla_lang_ast.a_exp =
  1. | AExp_Val of assume_val * 'a
  2. | AExp_Unop of unop * 'a a_exp * 'a
  3. | AExp_Binop of binop * 'a a_exp * 'a a_exp * 'a
  4. | AExp_Manyop of manyop * 'a a_exp list * 'a
  5. | AExp_Ite of 'a a_exp * 'a a_exp * 'a a_exp * 'a
type !'a smt = 'a Isla_lang__Isla_lang_ast.smt =
  1. | DeclareConst of int * ty
  2. | DefineConst of int * 'a exp
  3. | Assert of 'a exp
  4. | DefineEnum of int
type arg_list = Isla_lang__Isla_lang_ast.arg_list =
  1. | NilArgs
  2. | ListArgs of valu list
type tag_value = valu option
type segment = Isla_lang__Isla_lang_ast.segment =
  1. | Segment of string * int * int
type !'a event = 'a Isla_lang__Isla_lang_ast.event =
  1. | Smt of 'a smt * 'a
  2. | Branch of int * string * 'a
  3. | ReadReg of string * accessor_list * valu * 'a
  4. | WriteReg of string * accessor_list * valu * 'a
  5. | ReadMem of valu * valu * valu * int * tag_value * 'a
  6. | WriteMem of valu * valu * valu * valu * int * tag_value * 'a
  7. | BranchAddress of valu * 'a
  8. | Barrier of valu * 'a
  9. | CacheOp of valu * valu * 'a
  10. | MarkReg of string * string * 'a
  11. | Cycle of 'a
  12. | Instr of valu * 'a
  13. | Sleeping of int * 'a
  14. | WakeRequest of 'a
  15. | SleepRequest of 'a
  16. | AssumeReg of string * accessor_list * valu * 'a
  17. | Assume of 'a a_exp * 'a
  18. | FunAssume of string * valu * arg_list * 'a
  19. | UseFunAssume of string * valu * arg_list * 'a
  20. | AbstractCall of string * valu * arg_list * 'a
  21. | AbstractPrimop of string * valu * arg_list * 'a
type instruction_segments = Isla_lang__Isla_lang_ast.instruction_segments =
  1. | Segments of segment list
type !'a trc = 'a Isla_lang__Isla_lang_ast.trc =
  1. | Trace of 'a event list
type !'a maybe_fork = 'a Isla_lang__Isla_lang_ast.maybe_fork =
  1. | Cases of string * 'a tree_trc list
  2. | End
and !'a tree_trc = 'a Isla_lang__Isla_lang_ast.tree_trc =
  1. | TreeTrace of 'a event list * 'a maybe_fork
type !'a trcs = 'a Isla_lang__Isla_lang_ast.trcs =
  1. | Traces of 'a trc list
  2. | TracesWithSegments of instruction_segments * 'a trc list
type !'a whole_tree = 'a Isla_lang__Isla_lang_ast.whole_tree =
  1. | BareTree of 'a tree_trc
  2. | TreeWithSegments of instruction_segments * 'a tree_trc
val subst_val_base_val : base_val -> int -> base_val -> base_val
val subst_val_exp : base_val -> int -> 'a exp -> 'a exp
val subst_val_valu : base_val -> int -> valu -> valu
val subst_val_smt : base_val -> int -> 'a smt -> 'a smt
val subst_val_arg_list : base_val -> int -> arg_list -> arg_list
val subst_val_event : base_val -> int -> 'a event -> 'a event
val subst_val_tree_trc : base_val -> int -> 'a tree_trc -> 'a tree_trc
val subst_val_maybe_fork : base_val -> int -> 'a maybe_fork -> 'a maybe_fork
val subst_val_trc : base_val -> int -> 'a trc -> 'a trc
val subst_val_whole_tree : base_val -> int -> 'a whole_tree -> 'a whole_tree
val subst_val_trcs : base_val -> int -> 'a trcs -> 'a trcs
module Lexer = Base.Lexer
module Parser = Base.Parser
type loc = Stdlib.Lexing.position
type rtrc = lrng trc

The type of raw traces out of the parser

type revent = lrng event

The type of raw events out of the parser

type rsmt = lrng smt

The type of raw SMT declaration out of the parser

type rexp = lrng exp

The type of raw expressions out of the parser

type rtrcs = lrng trcs

IslaTrace parsing

exception ParseError of loc * string

Exception that represent an Isla parsing error

exception LexError of loc * string

Exception that represent an Isla lexing error

type lexer = Stdlib.Lexing.lexbuf -> Parser.token
type 'a parser = lexer -> Stdlib.Lexing.lexbuf -> 'a
val parse : 'a parser -> ?filename:string -> Stdlib.Lexing.lexbuf -> 'a

Parse a single Isla instruction output from a Lexing.lexbuf

val parse_exp : ?filename:string -> Stdlib.Lexing.lexbuf -> rexp

Parse a single Isla expression from a Lexing.lexbuf

val parse_exp_string : ?filename:string -> string -> rexp

Parse a single Isla expression from a string

val parse_exp_channel : ?filename:string -> Stdlib.in_channel -> rexp

Parse a single Isla expression from a channel

val parse_trc : ?filename:string -> Stdlib.Lexing.lexbuf -> rtrc

Parse an Isla trace from a Lexing.lexbuf

val parse_trc_string : ?filename:string -> string -> rtrc

Parse an Isla trace from a string

val parse_trc_channel : ?filename:string -> Stdlib.in_channel -> rtrc

Parse an Isla trace from a channel

val parse_trcs : + ?filename:string -> + Stdlib.Lexing.lexbuf -> + Isla_lang__.Isla_lang_ast.lrng Isla_lang__.Isla_lang_ast.trcs
val parse_trcs_string : ?filename:string -> string -> rtrcs
val parse_trcs_channel : ?filename:string -> Stdlib.in_channel -> rtrcs
val parse_segments : + ?filename:string -> + Stdlib.Lexing.lexbuf -> + instruction_segments
val parse_segments_string : ?filename:string -> string -> instruction_segments
val parse_segments_channel : + ?filename:string -> + Stdlib.in_channel -> + instruction_segments

IslaTrace pretty printing

include module type of struct include Isla_lang.PP end
val pp_raw_vvar : int -> PPrint.document
val pp_raw_name : string -> PPrint.document
val pp_raw_enum_ty : int -> PPrint.document
val pp_raw_enum : Isla_lang__.Isla_lang_ast.enum -> PPrint.document
val pp_raw_nat : int -> PPrint.document
val pp_raw_bvi : int -> PPrint.document
val pp_raw_bv : string -> PPrint.document
val pp_raw_str : string -> PPrint.document
val pp_raw_int : int -> PPrint.document
val pp_raw_ty : Isla_lang__.Isla_lang_ast.ty -> PPrint.document
val pp_raw_bool : bool -> PPrint.document
val pp_raw_unop : Isla_lang__.Isla_lang_ast.unop -> PPrint.document
val pp_raw_bvarith : Isla_lang__.Isla_lang_ast.bvarith -> PPrint.document
val pp_raw_bvcomp : Isla_lang__.Isla_lang_ast.bvcomp -> PPrint.document
val pp_raw_binop : Isla_lang__.Isla_lang_ast.binop -> PPrint.document
val pp_raw_bvmanyarith : + Isla_lang__.Isla_lang_ast.bvmanyarith -> + PPrint.document
val pp_raw_manyop : Isla_lang__.Isla_lang_ast.manyop -> PPrint.document
val pp_raw_base_val : Isla_lang__.Isla_lang_ast.base_val -> PPrint.document
val pp_raw_assume_val : Isla_lang__.Isla_lang_ast.assume_val -> PPrint.document
val pp_raw_exp : 'a Isla_lang__.Isla_lang_ast.exp -> PPrint.document
val pp_raw_a_exp : 'a Isla_lang__.Isla_lang_ast.a_exp -> PPrint.document
val pp_raw_smt : 'a Isla_lang__.Isla_lang_ast.smt -> PPrint.document
val pp_raw_valu : Isla_lang__.Isla_lang_ast.valu -> PPrint.document
val pp_raw_selem : (string * Isla_lang__.Isla_lang_ast.valu) -> PPrint.document
val pp_raw_accessor : Isla_lang__.Isla_lang_ast.accessor -> PPrint.document
val pp_raw_accessor_list : + Isla_lang__.Isla_lang_ast.accessor_list -> + PPrint.document
val pp_raw_arg_list : Isla_lang__.Isla_lang_ast.arg_list -> PPrint.document
val pp_raw_tag_value : Isla_lang__.Isla_lang_ast.tag_value -> PPrint.document
val pp_raw_event : 'a Isla_lang__.Isla_lang_ast.event -> PPrint.document
val pp_raw_segment : Isla_lang__.Isla_lang_ast.segment -> PPrint.document
val pp_raw_instruction_segments : + Isla_lang__.Isla_lang_ast.instruction_segments -> + PPrint.document
val pp_raw_trc : 'a Isla_lang__.Isla_lang_ast.trc -> PPrint.document
val pp_raw_trcs : 'a Isla_lang__.Isla_lang_ast.trcs -> PPrint.document
val pp_raw_maybe_fork : + 'a Isla_lang__.Isla_lang_ast.maybe_fork -> + PPrint.document
val pp_raw_tree_trc : 'a Isla_lang__.Isla_lang_ast.tree_trc -> PPrint.document
val pp_raw_whole_tree : + 'a Isla_lang__.Isla_lang_ast.whole_tree -> + PPrint.document
val pp_vvar : int -> PPrint.document
val pp_name : string -> PPrint.document
val pp_enum_ty : int -> PPrint.document
val pp_enum : Isla_lang__.Isla_lang_ast.enum -> PPrint.document
val pp_nat : int -> PPrint.document
val pp_bvi : int -> PPrint.document
val pp_bv : string -> PPrint.document
val pp_str : string -> PPrint.document
val pp_j : int -> string
val pp_int : int -> PPrint.document
val pp_ty : Isla_lang__.Isla_lang_ast.ty -> PPrint.document
val pp_bool : bool -> PPrint.document
val pp_unop : Isla_lang__.Isla_lang_ast.unop -> PPrint.document
val pp_bvarith : Isla_lang__.Isla_lang_ast.bvarith -> PPrint.document
val pp_bvcomp : Isla_lang__.Isla_lang_ast.bvcomp -> PPrint.document
val pp_binop : Isla_lang__.Isla_lang_ast.binop -> PPrint.document
val pp_bvmanyarith : Isla_lang__.Isla_lang_ast.bvmanyarith -> PPrint.document
val pp_manyop : Isla_lang__.Isla_lang_ast.manyop -> PPrint.document
val pp_base_val : Isla_lang__.Isla_lang_ast.base_val -> PPrint.document
val pp_assume_val : Isla_lang__.Isla_lang_ast.assume_val -> PPrint.document
val pp_exp : 'a Isla_lang__.Isla_lang_ast.exp -> PPrint.document
val pp_a_exp : 'a Isla_lang__.Isla_lang_ast.a_exp -> PPrint.document
val pp_smt : 'a Isla_lang__.Isla_lang_ast.smt -> PPrint.document
val pp_valu : Isla_lang__.Isla_lang_ast.valu -> PPrint.document
val pp_selem : (string * Isla_lang__.Isla_lang_ast.valu) -> PPrint.document
val pp_accessor : Isla_lang__.Isla_lang_ast.accessor -> PPrint.document
val pp_accessor_list : + Isla_lang__.Isla_lang_ast.accessor_list -> + PPrint.document
val pp_arg_list : Isla_lang__.Isla_lang_ast.arg_list -> PPrint.document
val pp_tag_value : Isla_lang__.Isla_lang_ast.tag_value -> PPrint.document
val pp_event : 'a Isla_lang__.Isla_lang_ast.event -> PPrint.document
val pp_segment : Isla_lang__.Isla_lang_ast.segment -> PPrint.document
val pp_instruction_segments : + Isla_lang__.Isla_lang_ast.instruction_segments -> + PPrint.document
val pp_trc : 'a Isla_lang__.Isla_lang_ast.trc -> PPrint.document
val pp_trcs : 'a Isla_lang__.Isla_lang_ast.trcs -> PPrint.document
val pp_maybe_fork : 'a Isla_lang__.Isla_lang_ast.maybe_fork -> PPrint.document
val pp_tree_trc : 'a Isla_lang__.Isla_lang_ast.tree_trc -> PPrint.document
val pp_whole_tree : 'a Isla_lang__.Isla_lang_ast.whole_tree -> PPrint.document
module Base : sig ... end

This module wraps all isla-lang functionality. No other module should directly touch the Isla_lang module.

module Cache : sig ... end

This module provide a caching system for isla trace on top of Server.

module Conv : sig ... end
module Manip : sig ... end

This module provide generic manipulation function of isla ast

module Preprocess : sig ... end

This module is about preprocessing isla traces. This includes:

module Relocation : sig ... end
module Run : sig ... end

This module provide facility to run Isla trace over states

module Server : sig ... end

This module is about launching isla as a background server and using it

module Test : sig ... end

This file about testing interaction with Isla for single instructions

module Type : sig ... end

This module is about type isla trace and register discovery.

diff --git a/doc/html/read-dwarf/Other_cmds/CopySources/index.html b/doc/html/read-dwarf/Other_cmds/CopySources/index.html index bd066247..5f816635 100644 --- a/doc/html/read-dwarf/Other_cmds/CopySources/index.html +++ b/doc/html/read-dwarf/Other_cmds/CopySources/index.html @@ -1,2 +1,2 @@ -CopySources (read-dwarf.Other_cmds.CopySources)

Module Other_cmds.CopySources

val process_file : unit -> unit
\ No newline at end of file +CopySources (read-dwarf.Other_cmds.CopySources)

Module Other_cmds.CopySources

This module is the body implementation for the copy-sources subcommand.

val process_file : unit -> unit
diff --git a/doc/html/read-dwarf/Other_cmds/CopySourcesCmd/index.html b/doc/html/read-dwarf/Other_cmds/CopySourcesCmd/index.html index 37fd541a..3bab6499 100644 --- a/doc/html/read-dwarf/Other_cmds/CopySourcesCmd/index.html +++ b/doc/html/read-dwarf/Other_cmds/CopySourcesCmd/index.html @@ -1,2 +1,2 @@ -CopySourcesCmd (read-dwarf.Other_cmds.CopySourcesCmd)

Module Other_cmds.CopySourcesCmd

val elf : unit Cmdliner.Term.t
val src_target_dir : unit Cmdliner.Term.t
val dry_run : unit Cmdliner.Term.t
val options : unit Cmdliner.Term.t list
val full_term : unit Cmdliner.Term.t
val info : Cmdliner.Term.info
val command : unit Cmdliner.Term.t * Cmdliner.Term.info
\ No newline at end of file +CopySourcesCmd (read-dwarf.Other_cmds.CopySourcesCmd)

Module Other_cmds.CopySourcesCmd

This module is the command-line processing for the copy-sources subcommand.

val elf : unit Cmdliner.Term.t
val src_target_dir : unit Cmdliner.Term.t
val dry_run : unit Cmdliner.Term.t
val options : unit Cmdliner.Term.t list
val full_term : unit Cmdliner.Term.t
val info : Cmdliner.Cmd.info
val command : unit Cmdliner.Term.t * Cmdliner.Cmd.info
diff --git a/doc/html/read-dwarf/Other_cmds/DumpDwarf/index.html b/doc/html/read-dwarf/Other_cmds/DumpDwarf/index.html index 2112e126..8add435c 100644 --- a/doc/html/read-dwarf/Other_cmds/DumpDwarf/index.html +++ b/doc/html/read-dwarf/Other_cmds/DumpDwarf/index.html @@ -1,2 +1,2 @@ -DumpDwarf (read-dwarf.Other_cmds.DumpDwarf)

Module Other_cmds.DumpDwarf

val dump_dwarf : string -> unit
val elf : string Cmdliner.Term.t
val info : Cmdliner.Term.info
val term : unit Cmdliner.Term.t
val command : unit Cmdliner.Term.t * Cmdliner.Term.info
\ No newline at end of file +DumpDwarf (read-dwarf.Other_cmds.DumpDwarf)

Module Other_cmds.DumpDwarf

This module adds a command to dump the interpreted DWARF information of an ELF file. The DWARF information is dumped as interpreted by read-dwarf,

See ReadDwarf for a different dump/interpretation.

val dump_dwarf : string -> unit
val elf : string Cmdliner.Term.t
val info : Cmdliner.Cmd.info
val term : unit Cmdliner.Term.t
val command : unit Cmdliner.Term.t * Cmdliner.Cmd.info
diff --git a/doc/html/read-dwarf/Other_cmds/DumpSym/index.html b/doc/html/read-dwarf/Other_cmds/DumpSym/index.html index 57b2a542..58ab6c17 100644 --- a/doc/html/read-dwarf/Other_cmds/DumpSym/index.html +++ b/doc/html/read-dwarf/Other_cmds/DumpSym/index.html @@ -1,2 +1,2 @@ -DumpSym (read-dwarf.Other_cmds.DumpSym)

Module Other_cmds.DumpSym

val dump_symbols : string -> string option -> unit
val elf : string Cmdliner.Term.t
val sym : string option Cmdliner.Term.t
val info : Cmdliner.Term.info
val term : unit Cmdliner.Term.t
val command : unit Cmdliner.Term.t * Cmdliner.Term.info
\ No newline at end of file +DumpSym (read-dwarf.Other_cmds.DumpSym)

Module Other_cmds.DumpSym

This module adds a command to dump the symbol of an ELF file with their content

val dump_symbols : string -> string option -> unit
val elf : string Cmdliner.Term.t
val sym : string option Cmdliner.Term.t
val info : Cmdliner.Cmd.info
val term : unit Cmdliner.Term.t
val command : unit Cmdliner.Term.t * Cmdliner.Cmd.info
diff --git a/doc/html/read-dwarf/Other_cmds/ReadDwarf/Default/index.html b/doc/html/read-dwarf/Other_cmds/ReadDwarf/Default/index.html index 5a07519e..be6c7545 100644 --- a/doc/html/read-dwarf/Other_cmds/ReadDwarf/Default/index.html +++ b/doc/html/read-dwarf/Other_cmds/ReadDwarf/Default/index.html @@ -1,2 +1,2 @@ -Default (read-dwarf.Other_cmds.ReadDwarf.Default)

Module ReadDwarf.Default

val action : 'a -> unit

Default action to run when no command is set

val info : Cmdliner.Term.info

Global documentation string and name

val command : unit Cmdliner.Term.t * Cmdliner.Term.info

Default command

\ No newline at end of file +Default (read-dwarf.Other_cmds.ReadDwarf.Default)

Module ReadDwarf.Default

val action : 'a -> unit

Default action to run when no command is set

val info : Cmdliner.Cmd.info

Global documentation string and name

val command : unit Cmdliner.Term.t * Cmdliner.Cmd.info

Default command

diff --git a/doc/html/read-dwarf/Other_cmds/ReadDwarf/index.html b/doc/html/read-dwarf/Other_cmds/ReadDwarf/index.html index ab7e35c7..5a9baad2 100644 --- a/doc/html/read-dwarf/Other_cmds/ReadDwarf/index.html +++ b/doc/html/read-dwarf/Other_cmds/ReadDwarf/index.html @@ -1,2 +1,2 @@ -ReadDwarf (read-dwarf.Other_cmds.ReadDwarf)

Module Other_cmds.ReadDwarf

module Default : sig ... end
val commands : (unit Cmdliner.Term.t * Cmdliner.Term.info) list

List of all non-default commands

\ No newline at end of file +ReadDwarf (read-dwarf.Other_cmds.ReadDwarf)

Module Other_cmds.ReadDwarf

module Default : sig ... end
val commands : (unit Cmdliner.Term.t * Cmdliner.Cmd.info) list

List of all non-default commands

diff --git a/doc/html/read-dwarf/Other_cmds/index.html b/doc/html/read-dwarf/Other_cmds/index.html index de32370d..4c465ba4 100644 --- a/doc/html/read-dwarf/Other_cmds/index.html +++ b/doc/html/read-dwarf/Other_cmds/index.html @@ -1,2 +1,2 @@ -Other_cmds (read-dwarf.Other_cmds)

Module Other_cmds

module CopySources : sig ... end
module CopySourcesCmd : sig ... end
module DumpDwarf : sig ... end
module DumpSym : sig ... end
module ReadDwarf : sig ... end
\ No newline at end of file +Other_cmds (read-dwarf.Other_cmds)

Module Other_cmds

module CopySources : sig ... end

This module is the body implementation for the copy-sources subcommand.

module CopySourcesCmd : sig ... end

This module is the command-line processing for the copy-sources subcommand.

module DumpDwarf : sig ... end

This module adds a command to dump the interpreted DWARF information of an ELF file. The DWARF information is dumped as interpreted by read-dwarf,

module DumpSym : sig ... end

This module adds a command to dump the symbol of an ELF file with their content

module ReadDwarf : sig ... end
diff --git a/doc/html/read-dwarf/Printing.html b/doc/html/read-dwarf/Printing.html index dbf0cc36..a3a59a63 100644 --- a/doc/html/read-dwarf/Printing.html +++ b/doc/html/read-dwarf/Printing.html @@ -1,2 +1,2 @@ -Printing (read-dwarf.Printing)

Printing

Pretty printing

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.

Logging system

The logging system is defined in the module Utils.Logs and is the main way of signaling information like warnings to the user.

\ No newline at end of file +Printing (read-dwarf.Printing)

Printing

Pretty printing

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.

Logging system

The logging system is defined in the module Utils.Logs and is the main way of signaling information like warnings to the user.

diff --git a/doc/html/read-dwarf/Qtest_isla/index.html b/doc/html/read-dwarf/Qtest_isla/index.html index 7e5e5d1a..eb008fa2 100644 --- a/doc/html/read-dwarf/Qtest_isla/index.html +++ b/doc/html/read-dwarf/Qtest_isla/index.html @@ -1,2 +1,2 @@ -Qtest_isla (read-dwarf.Qtest_isla)

Module Qtest_isla

\ No newline at end of file +Qtest_isla (read-dwarf.Qtest_isla)

Module Qtest_isla

diff --git a/doc/html/read-dwarf/Qtest_run/index.html b/doc/html/read-dwarf/Qtest_run/index.html index bd673ea0..d8938fe3 100644 --- a/doc/html/read-dwarf/Qtest_run/index.html +++ b/doc/html/read-dwarf/Qtest_run/index.html @@ -1,2 +1,2 @@ -Qtest_run (read-dwarf.Qtest_run)

Module Qtest_run

\ No newline at end of file +Qtest_run (read-dwarf.Qtest_run)

Module Qtest_run

diff --git a/doc/html/read-dwarf/Qtest_sig_aarch64/index.html b/doc/html/read-dwarf/Qtest_sig_aarch64/index.html index e5eac1a3..1991be5c 100644 --- a/doc/html/read-dwarf/Qtest_sig_aarch64/index.html +++ b/doc/html/read-dwarf/Qtest_sig_aarch64/index.html @@ -1,2 +1,2 @@ -Qtest_sig_aarch64 (read-dwarf.Qtest_sig_aarch64)

Module Qtest_sig_aarch64

\ No newline at end of file +Qtest_sig_aarch64 (read-dwarf.Qtest_sig_aarch64)

Module Qtest_sig_aarch64

diff --git a/doc/html/read-dwarf/Qtest_utils/index.html b/doc/html/read-dwarf/Qtest_utils/index.html index bd5ff1f7..e58e65a4 100644 --- a/doc/html/read-dwarf/Qtest_utils/index.html +++ b/doc/html/read-dwarf/Qtest_utils/index.html @@ -1,2 +1,2 @@ -Qtest_utils (read-dwarf.Qtest_utils)

Module Qtest_utils

\ No newline at end of file +Qtest_utils (read-dwarf.Qtest_utils)

Module Qtest_utils

diff --git a/doc/html/read-dwarf/Run/BB/index.html b/doc/html/read-dwarf/Run/BB/index.html index 7ccd0561..fe32fb92 100644 --- a/doc/html/read-dwarf/Run/BB/index.html +++ b/doc/html/read-dwarf/Run/BB/index.html @@ -1,2 +1,2 @@ -BB (read-dwarf.Run.BB)

Module Run.BB

module SMT = Z3
val dump : bool Cmdliner.Term.t
val reg_types : bool Cmdliner.Term.t
val no_run : bool Cmdliner.Term.t
val simp_trace : bool Cmdliner.Term.t
val simp_state : bool Cmdliner.Term.t
val simp : bool Cmdliner.Term.t
val elf : string Cmdliner.Term.t
val sym : string Cmdliner.Term.t
val len : int option Cmdliner.Term.t
val get_code : string -> string -> int option -> Utils.BytesSeq.t
val code_term : Utils.BytesSeq.t Cmdliner.Term.t
val simp_trace_term : bool Cmdliner.Term.t
val simp_state_term : bool Cmdliner.Term.t
val get_bb : bool -> bool -> bool -> Utils.BytesSeq.t -> Bb_lib.t
val bb_term : Bb_lib.t Cmdliner.Term.t
val run_bb : bool -> bool -> Bb_lib.t -> unit
val term : unit Cmdliner.Term.t
val info : Cmdliner.Term.info
val command : unit Cmdliner.Term.t * Cmdliner.Term.info
\ No newline at end of file +BB (read-dwarf.Run.BB)

Module Run.BB

This module allow to do a test run of all the machinery for a single basic block

module SMT = Z3
val dump : bool Cmdliner.Term.t
val reg_types : bool Cmdliner.Term.t
val no_run : bool Cmdliner.Term.t
val simp_trace : bool Cmdliner.Term.t
val simp_state : bool Cmdliner.Term.t
val simp : bool Cmdliner.Term.t
val elf : string Cmdliner.Term.t
val sym : string Cmdliner.Term.t
val len : int option Cmdliner.Term.t
val get_code : string -> string -> int option -> Utils.BytesSeq.t
val code_term : Utils.BytesSeq.t Cmdliner.Term.t
val simp_trace_term : bool Cmdliner.Term.t
val simp_state_term : bool Cmdliner.Term.t
val get_bb : bool -> bool -> bool -> Utils.BytesSeq.t -> Bb_lib.t
val bb_term : Bb_lib.t Cmdliner.Term.t
val run_bb : bool -> bool -> Bb_lib.t -> unit
val term : unit Cmdliner.Term.t
val info : Cmdliner.Cmd.info
val command : unit Cmdliner.Term.t * Cmdliner.Cmd.info
diff --git a/doc/html/read-dwarf/Run/Bb_lib/index.html b/doc/html/read-dwarf/Run/Bb_lib/index.html index bb988b63..1d7e088d 100644 --- a/doc/html/read-dwarf/Run/Bb_lib/index.html +++ b/doc/html/read-dwarf/Run/Bb_lib/index.html @@ -1,2 +1,2 @@ -Bb_lib (read-dwarf.Run.Bb_lib)

Module Run.Bb_lib

type trc = Trace.t
type state = State.t
type t = {
main : trc array;
}

Type of a basic block.

The main part is the traces of all the non-branching instruction

val from_binary : Utils.BytesSeq.t -> t

Take a binary block and call isla on all the instruction to get traces Also does the typing of traces for register discovery. TODO Support variable length instructions

val simplify_mut : t -> unit

Simplifies the traces in the basic block

val run_mut : ?⁠dwarf:Dw.t -> State.t -> t -> unit

Run a linear basic block on a state by mutation.

If dwarf is provided, the run is typed.

val run : ?⁠dwarf:Dw.t -> State.t -> t -> state

Run a linear basic block on a trace and return a new state

If dwarf is provided, the run is typed.

val pp : t -> PPrintEngine.document

Pretty print the basic block (The traces)

\ No newline at end of file +Bb_lib (read-dwarf.Run.Bb_lib)

Module Run.Bb_lib

This module provide code to manipulate basic block and run them.

This is only for use by BB and debugging. I don't think this should be used for anything else. Block should generally be used instead.

type trc = Trace.t
type state = State.t
type t = {
  1. main : trc array;
}

Type of a basic block.

The main part is the traces of all the non-branching instruction

val from_binary : Utils.BytesSeq.t -> t

Take a binary block and call isla on all the instruction to get traces Also does the typing of traces for register discovery. TODO Support variable length instructions

val simplify_mut : t -> unit

Simplifies the traces in the basic block

val run_mut : ?dwarf:Dw.t -> State.t -> t -> unit

Run a linear basic block on a state by mutation.

If dwarf is provided, the run is typed.

val run : ?dwarf:Dw.t -> State.t -> t -> state

Run a linear basic block on a trace and return a new state

If dwarf is provided, the run is typed.

val pp : t -> Utils.Pp.document

Pretty print the basic block (The traces)

diff --git a/doc/html/read-dwarf/Run/Block/index.html b/doc/html/read-dwarf/Run/Block/index.html index daf6931c..c7a96824 100644 --- a/doc/html/read-dwarf/Run/Block/index.html +++ b/doc/html/read-dwarf/Run/Block/index.html @@ -1,2 +1,9 @@ -Block (read-dwarf.Run.Block)

Module Run.Block

val no_run : bool Cmdliner.Term.t
val reg_types : bool Cmdliner.Term.t
val len : int option Cmdliner.Term.t
val breakpoints : string list Cmdliner.Term.t
val ensure_linear : bool Cmdliner.Term.t
val elf : string Cmdliner.Term.t
val start : string Cmdliner.Term.t
val get_elf_start : string -> string -> Elf.File.t * Elf.SymTable.sym_offset
val elf_term : (Elf.File.t * Elf.SymTable.sym_offset) Cmdliner.Term.t
val gen_block : (Elf.File.t * Elf.SymTable.sym_offset) -> int option -> string list -> Elf.File.t * Block_lib.t
val elfblock_term : (Elf.File.t * Block_lib.t) Cmdliner.Term.t
val prune_paths : (State.Base.t -> bool) -> 'a State.Tree.t -> 'a State.Tree.t option
val has_assert_false : State.t -> bool
val run_block : (Elf.File.t * Block_lib.t) -> bool -> bool -> bool -> unit
val term : unit Cmdliner.Term.t
val info : Cmdliner.Term.info
val command : unit Cmdliner.Term.t * Cmdliner.Term.info
\ No newline at end of file +Block (read-dwarf.Run.Block)

Module Run.Block

This module add the run-block sub command.

This subcommand is about running a complex block of execution It start at a specific offset in a block and can terminate on various condition. If not enough condition are met, it may crash by reaching an invalid instruction

val no_run : bool Cmdliner.Term.t
val reg_types : bool Cmdliner.Term.t
val len : int option Cmdliner.Term.t
val breakpoints : string list Cmdliner.Term.t
val ensure_linear : bool Cmdliner.Term.t
val elf : string Cmdliner.Term.t
val start : string Cmdliner.Term.t
val get_elf_start : string -> string -> Elf.File.t * Elf.SymTable.sym_offset
val elf_term : (Elf.File.t * Elf.SymTable.sym_offset) Cmdliner.Term.t
val gen_block : + (Elf.File.t * Elf.SymTable.sym_offset) -> + int option -> + string list -> + Elf.File.t * Block_lib.t
val elfblock_term : (Elf.File.t * Block_lib.t) Cmdliner.Term.t
val prune_paths : + (State.Base.t -> bool) -> + 'a State.Tree.t -> + 'a State.Tree.t option
val has_assert_false : State.t -> bool
val run_block : (Elf.File.t * Block_lib.t) -> bool -> bool -> bool -> unit
val term : unit Cmdliner.Term.t
val info : Cmdliner.Cmd.info
val command : unit Cmdliner.Term.t * Cmdliner.Cmd.info
diff --git a/doc/html/read-dwarf/Run/Block_lib/index.html b/doc/html/read-dwarf/Run/Block_lib/index.html index fbd8b7a9..1e07179b 100644 --- a/doc/html/read-dwarf/Run/Block_lib/index.html +++ b/doc/html/read-dwarf/Run/Block_lib/index.html @@ -1,2 +1,18 @@ -Block_lib (read-dwarf.Run.Block_lib)

Module Run.Block_lib

type t = {
runner : Runner.t;
start : int;
endpred : State.exp -> string option;
}

endpred pc_exp gives when to stop

val make : runner:Runner.t -> start:int -> endpred:(State.exp -> string option) -> t

Build a complex block starting from start in sym and ending when endpred says so. endpred is a predicate on the symbolic PC expression

type label =
| Start

Root node of the tree

| End of string

Lead node of the tree, the string describe which end condition has be triggered

| BranchAt of int

A Branching node at a given PC

| NormalAt of int

A normal instruction at PC. Exists only if every_instruction is true

The labels on tree node at the output of run

val label_to_string : label -> string
val pp_label : label -> Utils.Pp.document
val run : ?⁠every_instruction:bool -> ?⁠relevant:(int, 'a) Stdlib.Hashtbl.t -> t -> State.t -> label State.Tree.t

Run the block an return a state tree indexed by the addresses of the branches.

When every_instruction is true, It will make a snapshot of the state i.e a tree node at each instruction. By default it will only make a Tree node on branching points.

The output is a tree because state merging is not implemented so if we are going twice on the same PC, the whole thing will be run twice separately in two separate tree branches.

val gen_endpred : ?⁠min:int -> ?⁠max:int -> ?⁠loop:int -> ?⁠brks:int list -> unit -> State.exp -> string option

Generic end predicate. Will stop if:

  • pc below min
  • pc above max
  • pc is one of brks
  • pc has be seen more than loop
\ No newline at end of file +Block_lib (read-dwarf.Run.Block_lib)

Module Run.Block_lib

This module provides a representation of a complex block of code.

The end of the block is decided by endpred, an arbitrary predicate on the pc. In particular if the PC is symbolic the execution is stopped anyway. This means that we either reached the top level function return or an unresolved branch table.

To generate easily end predicates, there is gen_endpred.

type t = {
  1. runner : Runner.t;
  2. start : Elf.Address.t;
  3. endpred : State.exp -> string option;
}

endpred pc_exp gives when to stop

val make : + runner:Runner.t -> + start:Elf.Address.t -> + endpred:(State.exp -> string option) -> + t

Build a complex block starting from start in sym and ending when endpred says so. endpred is a predicate on the symbolic PC expression

type label =
  1. | Start
    (*

    Root node of the tree

    *)
  2. | End of string
    (*

    Lead node of the tree, the string describe which end condition has be triggered

    *)
  3. | BranchAt of Elf.Address.t
    (*

    A Branching node at a given PC

    *)
  4. | NormalAt of Elf.Address.t
    (*

    A normal instruction at PC. Exists only if every_instruction is true

    *)

The labels on tree node at the output of run

val label_to_string : label -> string
val pp_label : label -> Utils.Pp.document
val run : + ?every_instruction:bool -> + ?relevant:(Elf.Address.t, 'a) Stdlib.Hashtbl.t -> + t -> + State.t -> + label State.Tree.t

Run the block an return a state tree indexed by the addresses of the branches.

When every_instruction is true, It will make a snapshot of the state i.e a tree node at each instruction. By default it will only make a Tree node on branching points.

The output is a tree because state merging is not implemented so if we are going twice on the same PC, the whole thing will be run twice separately in two separate tree branches.

val gen_endpred : + ?min:Elf.Address.t -> + ?max:Elf.Address.t -> + ?loop:int -> + ?brks:Elf.Address.t list -> + unit -> + State.exp -> + string option

Generic end predicate. Will stop if:

  • pc below min
  • pc above max
  • pc is one of brks
  • pc has be seen more than loop
diff --git a/doc/html/read-dwarf/Run/Func/index.html b/doc/html/read-dwarf/Run/Func/index.html index 91dccb8b..79caa5b3 100644 --- a/doc/html/read-dwarf/Run/Func/index.html +++ b/doc/html/read-dwarf/Run/Func/index.html @@ -1,2 +1,14 @@ -Func (read-dwarf.Run.Func)

Module Run.Func

val get_state_tree : elf:string -> name:string -> ?⁠dump:bool -> ?⁠entry:bool -> ?⁠len:int -> ?⁠breakpoints:string list -> ?⁠loop:int -> ?⁠tree_to_file:string -> unit -> Block_lib.label State.Tree.t
val command : unit Cmdliner.Term.t * Cmdliner.Term.info
\ No newline at end of file +Func (read-dwarf.Run.Func)

Module Run.Func

val get_state_tree : + elf:string -> + name:string -> + ?dump:bool -> + ?entry:bool -> + ?len:int -> + ?breakpoints:string list -> + ?loop:int -> + ?tree_to_file:string -> + ?init:(State.t -> State.t) -> + ?every_instruction:bool -> + unit -> + Block_lib.label State.Tree.t
val command : unit Cmdliner.Term.t * Cmdliner.Cmd.info
diff --git a/doc/html/read-dwarf/Run/FuncRD/index.html b/doc/html/read-dwarf/Run/FuncRD/index.html index 30f7c999..c4132dc6 100644 --- a/doc/html/read-dwarf/Run/FuncRD/index.html +++ b/doc/html/read-dwarf/Run/FuncRD/index.html @@ -1,2 +1,8 @@ -FuncRD (read-dwarf.Run.FuncRD)

Module Run.FuncRD

val run_func_rd : string -> string -> string -> string option -> string list -> unit
val elf : string Cmdliner.Term.t
val func : string Cmdliner.Term.t
val objdump_d : string Cmdliner.Term.t
val branch_table : string option Cmdliner.Term.t
val breakpoints : string list Cmdliner.Term.t
val term : unit Cmdliner.Term.t
val info : Cmdliner.Term.info
val command : unit Cmdliner.Term.t * Cmdliner.Term.info
\ No newline at end of file +FuncRD (read-dwarf.Run.FuncRD)

Module Run.FuncRD

This module is a merge of rd sub command and run-func --loop=1. It will run the equivalent of run-func --loop=1, and print the instruction like rd but will print the state in a light form (State.pp_partial) between instructions.

val run_func_rd : + string -> + string -> + string -> + string option -> + string list -> + unit
val elf : string Cmdliner.Term.t
val func : string Cmdliner.Term.t
val objdump_d : string Cmdliner.Term.t
val branch_table : string option Cmdliner.Term.t
val breakpoints : string list Cmdliner.Term.t
val term : unit Cmdliner.Term.t
val info : Cmdliner.Cmd.info
val command : unit Cmdliner.Term.t * Cmdliner.Cmd.info
diff --git a/doc/html/read-dwarf/Run/Init/index.html b/doc/html/read-dwarf/Run/Init/index.html index 14d4ff3a..fb4c39fb 100644 --- a/doc/html/read-dwarf/Run/Init/index.html +++ b/doc/html/read-dwarf/Run/Init/index.html @@ -1,2 +1,2 @@ -Init (read-dwarf.Run.Init)

Module Run.Init

val init_state : State.t option Stdlib.ref

The initial state

val init : unit -> State.t

Intialize this module by calling Isla on Arch.nop to get initial machine state

val state : unit -> State.t

Return the initial state. Compute it if required.

\ No newline at end of file +Init (read-dwarf.Run.Init)

Module Run.Init

This module handle architecture initialization. For now it trust default isla initialization but this should change soon (TODO)

val init_state : State.t option Stdlib.ref

The initial state

val init : unit -> State.t

Intialize this module by calling Isla on Arch.nop to get initial machine state

val state : unit -> State.t

Return the initial state. Compute it if required.

diff --git a/doc/html/read-dwarf/Run/Instr/index.html b/doc/html/read-dwarf/Run/Instr/index.html index f41285f8..01eb8e60 100644 --- a/doc/html/read-dwarf/Run/Instr/index.html +++ b/doc/html/read-dwarf/Run/Instr/index.html @@ -1,2 +1,2 @@ -Instr (read-dwarf.Run.Instr)

Module Run.Instr

type traces =
| IslaTraces of Isla.rtrc list
| Traces of Trace.t list
val instr : string Cmdliner.Term.t
val dump_trace : bool Cmdliner.Term.t
val dump_isla : bool Cmdliner.Term.t
val no_run : bool Cmdliner.Term.t
val isla_run : bool Cmdliner.Term.t
val simp_trace : bool Cmdliner.Term.t
val simp_state : bool Cmdliner.Term.t
val simp : bool Cmdliner.Term.t
val reg_types : bool Cmdliner.Term.t
val init : bool Cmdliner.Term.t
val elf : string option Cmdliner.Term.t
val get_instr : Config.Arch.t -> string -> string option -> Utils.BytesSeq.t
val instr_term : Utils.BytesSeq.t Cmdliner.Term.t
val simp_trace_term : bool Cmdliner.Term.t
val simp_state_term : bool Cmdliner.Term.t
val get_traces : Utils.BytesSeq.t -> bool -> bool -> traces
val pre_traces_term : traces Cmdliner.Term.t
val simp_traces : bool -> traces -> traces
val dump_traces : bool -> traces -> traces
val traces_term : traces Cmdliner.Term.t
val run_instr : bool -> bool -> bool -> traces -> unit
val term : unit Cmdliner.Term.t
val info : Cmdliner.Term.info
val command : unit Cmdliner.Term.t * Cmdliner.Term.info
\ No newline at end of file +Instr (read-dwarf.Run.Instr)

Module Run.Instr

This module add the run-instr sub command.

This subcommand is about testing the various operations that can happen in an instruction processing. This is not isla-test, the instructions will always be fetched from the cache and use Init. If this fail at an earlier point, use isla-test to debug the Isla pipeline

Additionally run-instr supports branching instructions.

type traces =
  1. | IslaTraces of Isla.rtrc list
  2. | Traces of Trace.t list
val instr : string Cmdliner.Term.t
val dump_trace : bool Cmdliner.Term.t
val dump_isla : bool Cmdliner.Term.t
val no_run : bool Cmdliner.Term.t
val isla_run : bool Cmdliner.Term.t
val simp_trace : bool Cmdliner.Term.t
val simp_state : bool Cmdliner.Term.t
val simp : bool Cmdliner.Term.t
val reg_types : bool Cmdliner.Term.t
val init : bool Cmdliner.Term.t
val elf : string option Cmdliner.Term.t
val get_instr : Config.Arch.t -> string -> string option -> Utils.BytesSeq.t
val instr_term : Utils.BytesSeq.t Cmdliner.Term.t
val simp_trace_term : bool Cmdliner.Term.t
val simp_state_term : bool Cmdliner.Term.t
val get_traces : 'a -> 'b -> 'c -> traces
val pre_traces_term : traces Cmdliner.Term.t
val simp_traces : bool -> traces -> traces
val dump_traces : bool -> traces -> traces
val traces_term : traces Cmdliner.Term.t
val run_instr : bool -> bool -> bool -> traces -> unit
val term : unit Cmdliner.Term.t
val info : Cmdliner.Cmd.info
val command : unit Cmdliner.Term.t * Cmdliner.Cmd.info
diff --git a/doc/html/read-dwarf/Run/ReadDwarf/index.html b/doc/html/read-dwarf/Run/ReadDwarf/index.html index 6db2c59b..6c8de8d1 100644 --- a/doc/html/read-dwarf/Run/ReadDwarf/index.html +++ b/doc/html/read-dwarf/Run/ReadDwarf/index.html @@ -1,2 +1,2 @@ -ReadDwarf (read-dwarf.Run.ReadDwarf)

Module Run.ReadDwarf

val dry_run : unit Cmdliner.Term.t
val skylight : unit Cmdliner.Term.t
val comp_dir : unit Cmdliner.Term.t
val clip_binary : unit Cmdliner.Term.t
val no_vars : unit Cmdliner.Term.t
val no_cfa : unit Cmdliner.Term.t
val no_source : unit Cmdliner.Term.t
val objdump_d : unit Cmdliner.Term.t
val branch_tables : unit Cmdliner.Term.t
val elf : unit Cmdliner.Term.t
val objdump_d2 : unit Cmdliner.Term.t
val branch_tables2 : unit Cmdliner.Term.t
val elf2 : unit Cmdliner.Term.t
val qemu_log : unit Cmdliner.Term.t
val out_file : unit Cmdliner.Term.t
val out_dir : unit Cmdliner.Term.t
val cfg_dot_file : unit Cmdliner.Term.t
val cfg_source_nodes : unit Cmdliner.Term.t
val cfg_source_nodes2 : unit Cmdliner.Term.t
val html : unit Cmdliner.Term.t
val options : unit Cmdliner.Term.t list
val full_term : unit Cmdliner.Term.t
val info : Cmdliner.Term.info
val command : unit Cmdliner.Term.t * Cmdliner.Term.info
\ No newline at end of file +ReadDwarf (read-dwarf.Run.ReadDwarf)

Module Run.ReadDwarf

This module implements the rd subcommand. This was to original meaning of "read-dwarf", and the first command.

Together with run-func-rd, this is the main user of the Analyse code.

val dry_run : unit Cmdliner.Term.t
val skylight : unit Cmdliner.Term.t
val comp_dir : unit Cmdliner.Term.t
val clip_binary : unit Cmdliner.Term.t
val no_vars : unit Cmdliner.Term.t
val no_cfa : unit Cmdliner.Term.t
val no_source : unit Cmdliner.Term.t
val objdump_d : unit Cmdliner.Term.t
val branch_tables : unit Cmdliner.Term.t
val elf : unit Cmdliner.Term.t
val objdump_d2 : unit Cmdliner.Term.t
val branch_tables2 : unit Cmdliner.Term.t
val elf2 : unit Cmdliner.Term.t
val qemu_log : unit Cmdliner.Term.t
val out_file : unit Cmdliner.Term.t
val out_dir : unit Cmdliner.Term.t
val cfg_dot_file : unit Cmdliner.Term.t
val cfg_source_nodes : unit Cmdliner.Term.t
val cfg_source_nodes2 : unit Cmdliner.Term.t
val html : unit Cmdliner.Term.t
val morello : unit Cmdliner.Term.t
val suppress_stuff : unit Cmdliner.Term.t
val options : unit Cmdliner.Term.t list
val full_term : unit Cmdliner.Term.t
val info : Cmdliner.Cmd.info
val command : unit Cmdliner.Term.t * Cmdliner.Cmd.info
diff --git a/doc/html/read-dwarf/Run/Runner/index.html b/doc/html/read-dwarf/Run/Runner/index.html index b146d078..dc45282f 100644 --- a/doc/html/read-dwarf/Run/Runner/index.html +++ b/doc/html/read-dwarf/Run/Runner/index.html @@ -1,2 +1,8 @@ -Runner (read-dwarf.Run.Runner)

Module Run.Runner

module Reg = State.Reg
type slot =
| Normal of Trace.Instr.t

The traces and the size of the instruction

| Special of int

Special instructions. Will be used to represent external events

| Nocode

The is no code at this address. Running it is UB. Also used if an address is in between instructions

| IslaFail of int

This means Isla pipeline failed on that instruction

Give the instruction descriptor at a given address

type t = {
elf : Elf.File.t;
dwarf : Dw.t option;
instrs : (int, slot) Stdlib.Hashtbl.t;

Instruction cache

pc : Reg.t;
funcs : int Utils.Vec.t;

Loaded functions by loading order

}
val of_elf : ?⁠dwarf:Dw.t -> Elf.File.t -> t
val of_dwarf : Dw.t -> t
val load_sym : t -> Elf.Symbol.t -> unit

Load a symbol into the runner. All instruction traces are fetched and cached.

TODO support variable length instructions.

val fetch : t -> int -> slot

Fetch an instruction, and return corresponding slot.

val execute_normal : ?⁠prelock:(State.t -> unit) -> pc:int -> t -> Trace.Instr.t -> State.t -> State.t list

Run the traces on the state.

If the state is unlocked and the instruction is single trace, then the function mutates the state and returns the same state.

Otherwise the state is locked (if not) and new states are returned

In any case the returned states are unlocked.

val skip : t -> State.t -> State.t list
val run : ?⁠prelock:(State.t -> unit) -> t -> State.t -> State.t list

Do the whole fetch and execute cycle. Take the PC from the state, and fetch it's instruction and then run it. It return the list of possible behavior of that instruction. Normally the union of the set of concrete states represented by this list of symbolic state cover all the defined behaviors of the fetched instruction from the initial state.

If the state is unlocked and the instruction is single trace, then the function mutates the state and returns the same state.

Otherwise the state is locked (if not) and new unlocked states are returned.

In any case the returned states are unlocked.

val expect_normal : t -> int -> Trace.Instr.t

Return the Instr.t data of the instruction at address, and throw Not_found if the instruction was invalid

val get_normal_opt : t -> int -> Trace.Instr.t option

Return the Instr.t data of the instruction at address, and None if the instruction was invalid

val pp_slot : slot -> PPrintEngine.document

Pretty prints a instruction slot

val pp_instr : t -> Utils.Pp.document

Dump instruction table

\ No newline at end of file +Runner (read-dwarf.Run.Runner)

Module Run.Runner

This module provide the program runner that caches all the information required to make a state transition

Later this module will handle inlining in a transparent way, and may also compress basic block traces (We'll need to check this is okay with type inference)

For now this module load instructions on a per-symbol basis, and do not try to load instructions outside of function symbol. TODO: remove this restriction.

The final goal of this module is to encode all necessary information to perform a state transition. In particular, the run function should never require more argument: If extra information is required, it should be part of the runner if it is dependent on the whole program or in the state if it is specific to the state.

module Reg = State.Reg
type slot =
  1. | Normal of Trace.Instr.t
    (*

    The traces and the size of the instruction

    *)
  2. | Special of int
    (*

    Special instructions. Will be used to represent external events

    *)
  3. | Nocode
    (*

    The is no code at this address. Running it is UB. Also used if an address is in between instructions

    *)
  4. | IslaFail of int
    (*

    This means Isla pipeline failed on that instruction

    *)

Give the instruction descriptor at a given address

type t = {
  1. elf : Elf.File.t;
  2. dwarf : Dw.t option;
  3. instrs : (Elf.Address.t, slot) Stdlib.Hashtbl.t;
    (*

    Instruction cache

    *)
  4. pc : Reg.t;
  5. funcs : Elf.Address.t Utils.Vec.t;
    (*

    Loaded functions by loading order

    *)
}
val of_elf : ?dwarf:Dw.t -> Elf.File.t -> t
val of_dwarf : Dw.t -> t
val load_sym : t -> Elf.Symbol.t -> unit

Load a symbol into the runner. All instruction traces are fetched and cached.

TODO support variable length instructions.

val fetch : t -> Elf.Address.t -> slot

Fetch an instruction, and return corresponding slot.

val execute_normal : + ?prelock:(State.t -> unit) -> + pc:Elf.Address.t -> + t -> + Trace.Instr.t -> + State.t -> + State.t list

Run the traces on the state.

If the state is unlocked and the instruction is single trace, then the function mutates the state and returns the same state.

Otherwise the state is locked (if not) and new states are returned

In any case the returned states are unlocked.

val skip : t -> State.t -> State.t list
val run : ?prelock:(State.t -> unit) -> t -> State.t -> State.t list

Do the whole fetch and execute cycle. Take the PC from the state, and fetch it's instruction and then run it. It return the list of possible behavior of that instruction. Normally the union of the set of concrete states represented by this list of symbolic state cover all the defined behaviors of the fetched instruction from the initial state.

If the state is unlocked and the instruction is single trace, then the function mutates the state and returns the same state.

Otherwise the state is locked (if not) and new unlocked states are returned.

In any case the returned states are unlocked.

val expect_normal : t -> Elf.Address.t -> Trace.Instr.t

Return the Instr.t data of the instruction at address, and throw Not_found if the instruction was invalid

val get_normal_opt : t -> Elf.Address.t -> Trace.Instr.t option

Return the Instr.t data of the instruction at address, and None if the instruction was invalid

val pp_slot : slot -> Utils.Pp.document

Pretty prints a instruction slot

val pp_instr : t -> Utils.Pp.document

Dump instruction table

diff --git a/doc/html/read-dwarf/Run/index.html b/doc/html/read-dwarf/Run/index.html index b1f686ce..9cdeb254 100644 --- a/doc/html/read-dwarf/Run/index.html +++ b/doc/html/read-dwarf/Run/index.html @@ -1,2 +1,2 @@ -Run (read-dwarf.Run)

Module Run

module BB : sig ... end
module Bb_lib : sig ... end
module Block : sig ... end
module Block_lib : sig ... end
module Func : sig ... end
module FuncRD : sig ... end
module Init : sig ... end
module Instr : sig ... end
module ReadDwarf : sig ... end
module Runner : sig ... end
\ No newline at end of file +Run (read-dwarf.Run)

Module Run

module BB : sig ... end

This module allow to do a test run of all the machinery for a single basic block

module Bb_lib : sig ... end

This module provide code to manipulate basic block and run them.

module Block : sig ... end

This module add the run-block sub command.

module Block_lib : sig ... end

This module provides a representation of a complex block of code.

module Func : sig ... end
module FuncRD : sig ... end

This module is a merge of rd sub command and run-func --loop=1. It will run the equivalent of run-func --loop=1, and print the instruction like rd but will print the state in a light form (State.pp_partial) between instructions.

module Init : sig ... end

This module handle architecture initialization. For now it trust default isla initialization but this should change soon (TODO)

module Instr : sig ... end

This module add the run-instr sub command.

module ReadDwarf : sig ... end

This module implements the rd subcommand. This was to original meaning of "read-dwarf", and the first command.

module RelProg : sig ... end
module Runner : sig ... end

This module provide the program runner that caches all the information required to make a state transition

module TestRelProg : sig ... end
diff --git a/doc/html/read-dwarf/Sig/index.html b/doc/html/read-dwarf/Sig/index.html index af8ce19c..aa8f7eaf 100644 --- a/doc/html/read-dwarf/Sig/index.html +++ b/doc/html/read-dwarf/Sig/index.html @@ -1,2 +1,2 @@ -Sig (read-dwarf.Sig)

Module Sig

This module define all architecture-dependent configuration

It should be used instead of Arch inside the architecture dependent modules.

Everything inside this module is copied into Arch, so module that can depend on Arch may do so.

type func_api = {
args : Ctype.t list;
ret : Ctype.t option;
}

Describe the C API of a function

type func_abi = {
init : State.t -> State.t;

Gives the initial state for verifying the function, from a given global register state. Only global registers are kept.

}

Describe the ABI of a function

This is a record because I expect to add many other fields later.

type dwarf_reg_map = State.Reg.t array

The map of dwarf register: Which register number map to which ISA register

val supports : Config.Arch.t -> bool

Tells if this Arch module supports this architecture

val init : Config.Arch.t -> unit

If this arch module supports the architecture, then initialize read-dwarf state using this architecture

val initialized : unit -> Config.Arch.t option

Return Some(arch) is the loaded arch is arch and None if nothing is loaded yet.

val module_name : string

The name of the arch module. Must be the name of the module i.e. Config.arch_module

val loaded_name : string

For dynamic arch module, the name of the dynamically loaded module. Otherwise module_name

val address_size : int

The true size of addresses for memory operation

val dwarf_reg_map : unit -> dwarf_reg_map

Get the register map of the architecture

val is_local : State.Reg.t -> bool

Tell if a register is local for the ABI

val nop : unit -> Utils.BytesSeq.t

Give the opcode of the nop instruction (For Sail/Isla initialisation

val get_abi : func_api -> func_abi

Give the ABI of a function from it's C API

val pc : unit -> State.Reg.t

Give the register index for the program counter

val sp : unit -> State.Reg.t

Give the register index for the stack pointer

val assemble_to_elf : string -> string

Take an instruction string and give the name of an temporary ELF file created that contains the instruction at symbol instr.

val split_into_instrs : Utils.BytesSeq.t -> Utils.BytesSeq.t list

Split a byte-sequence into a list of instructions.

val is_ret : Utils.BytesSeq.t -> bool

Tell if an instruction is a return instruction.

val is_cmp : Utils.BytesSeq.t -> (State.Reg.t * Utils.BitVec.t) option

Tell if an instruction is a compare instruction. Returns Some (reg,bv) where the contents of reg are compared against the value bv if it is and None if not.

val is_bl : Utils.BytesSeq.t -> Utils.BitVec.t option

Tell if an instruction is an (unconditional) branch on immediate with link instructions. Returns Some bv where bv is the offset (from the address of this instruction, in the range +/-128MB) that is branched to if it is and None if not.

\ No newline at end of file +Sig (read-dwarf.Sig)

Module Sig

This module define all architecture-dependent configuration

It should be used instead of Arch inside the architecture dependent modules.

Everything inside this module is copied into Arch, so module that can depend on Arch may do so.

type func_api = {
  1. args : Ctype.t list;
  2. ret : Ctype.t option;
}

Describe the C API of a function

type func_abi = {
  1. init : State.t -> State.t;
    (*

    Gives the initial state for verifying the function, from a given global register state. Only global registers are kept.

    *)
}

Describe the ABI of a function

This is a record because I expect to add many other fields later.

type dwarf_reg_map = State.Reg.t array

The map of dwarf register: Which register number map to which ISA register

val supports : Config.Arch.t -> bool

Tells if this Arch module supports this architecture

val init : Config.Arch.t -> unit

If this arch module supports the architecture, then initialize read-dwarf state using this architecture

val initialized : unit -> Config.Arch.t option

Return Some(arch) is the loaded arch is arch and None if nothing is loaded yet.

val module_name : string

The name of the arch module. Must be the name of the module i.e. Config.arch_module

val loaded_name : string

For dynamic arch module, the name of the dynamically loaded module. Otherwise module_name

val address_size : int

The true size of addresses for memory operation

val dwarf_reg_map : unit -> dwarf_reg_map

Get the register map of the architecture

val is_local : State.Reg.t -> bool

Tell if a register is local for the ABI

val nop : unit -> Utils.BytesSeq.t

Give the opcode of the nop instruction (For Sail/Isla initialisation

val get_abi : func_api -> func_abi

Give the ABI of a function from it's C API

val pc : unit -> State.Reg.t

Give the register index for the program counter

val sp : unit -> State.Reg.t

Give the register index for the stack pointer

val assemble_to_elf : string -> string

Take an instruction string and give the name of an temporary ELF file created that contains the instruction at symbol instr.

val split_into_instrs : Elf.Symbol.data -> Elf.Symbol.data list

Split a byte-sequence into a list of instructions.

val is_ret : Utils.BytesSeq.t -> bool

Tell if an instruction is a return instruction.

val is_cmp : Utils.BytesSeq.t -> (State.Reg.t * Utils.BitVec.t) option

Tell if an instruction is a compare instruction. Returns Some (reg,bv) where the contents of reg are compared against the value bv if it is and None if not.

val is_bl : Utils.BytesSeq.t -> Utils.BitVec.t option

Tell if an instruction is an (unconditional) branch on immediate with link instructions. Returns Some bv where bv is the offset (from the address of this instruction, in the range +/-128MB) that is branched to if it is and None if not.

diff --git a/doc/html/read-dwarf/Simrel/Base/MemRel/index.html b/doc/html/read-dwarf/Simrel/Base/MemRel/index.html index 4b3263f7..5d5baa24 100644 --- a/doc/html/read-dwarf/Simrel/Base/MemRel/index.html +++ b/doc/html/read-dwarf/Simrel/Base/MemRel/index.html @@ -1,2 +1,5 @@ -MemRel (read-dwarf.Simrel.Base.MemRel)

Module Base.MemRel

type block = State.Mem.Fragment.Block.t
val of_int : int -> ('a'b) ExTy.t
val eq_block : block -> block -> ('a'b) ExTy.t * (State__Base.Mem.Fragment.varAst.no) ExTy.t
type event = State.Mem.Fragment.Event.t
type trace = event list
val eq : trace -> trace -> (State__Base.Mem.Fragment.varAst.no) ExTy.t list

For now, just assume an equality relation for memory

val eq : State.t -> State.t -> (State__Base.Mem.Fragment.varAst.no) ExTy.t list
\ No newline at end of file +MemRel (read-dwarf.Simrel.Base.MemRel)

Module Base.MemRel

val of_int : int -> ('a, 'b) ExTy.t
val eq_block : + block -> + block -> + ('a, 'b) ExTy.t * (State__Base.Var.t, Ast.no) ExTy.t
type trace = event list
val eq : State.t -> State.t -> (State__Base.Var.t, Ast.no) ExTy.t list
diff --git a/doc/html/read-dwarf/Simrel/Base/RegRel/index.html b/doc/html/read-dwarf/Simrel/Base/RegRel/index.html index 52e441db..6e29fbd6 100644 --- a/doc/html/read-dwarf/Simrel/Base/RegRel/index.html +++ b/doc/html/read-dwarf/Simrel/Base/RegRel/index.html @@ -1,2 +1,6 @@ -RegRel (read-dwarf.Simrel.Base.RegRel)

Module Base.RegRel

val reg_map_fold : 'a -> ('a -> Reg.Map.reg -> 'b -> 'a) -> 'b Reg.Map.t -> 'a
val present : Reg.t Utils.List.t option -> Reg.t -> bool
val eq : ?⁠except:Reg.t Utils.List.t -> State.t -> State.t -> (State__Base.Exp.varAst.no) ExTy.t

This is not symmetric (does it need to be?). NOTE: If there is a reg in st2 that is not in st1, it will not be checked. Example: test3, O2, state 1, __defaultRAM -> |reg:0:__defaultRAM| (not in O0)

\ No newline at end of file +RegRel (read-dwarf.Simrel.Base.RegRel)

Module Base.RegRel

val reg_map_fold : 'a -> ('a -> Reg.Map.reg -> 'b -> 'a) -> 'b Reg.Map.t -> 'a
val present : Reg.t Utils.List.t option -> Reg.t -> bool
val eq : + ?except:Reg.t Utils.List.t -> + State.t -> + State.t -> + (State__Base.Var.t, Ast.no) ExTy.t

This is not symmetric (does it need to be?). NOTE: If there is a reg in st2 that is not in st1, it will not be checked. Example: test3, O2, state 1, __defaultRAM -> |reg:0:__defaultRAM| (not in O0)

diff --git a/doc/html/read-dwarf/Simrel/Base/Test2/index.html b/doc/html/read-dwarf/Simrel/Base/Test2/index.html index d848a2c2..64e03f12 100644 --- a/doc/html/read-dwarf/Simrel/Base/Test2/index.html +++ b/doc/html/read-dwarf/Simrel/Base/Test2/index.html @@ -1,2 +1,2 @@ -Test2 (read-dwarf.Simrel.Base.Test2)

Module Base.Test2

val infer_rels : 'a State.Tree.t pair -> rels
\ No newline at end of file +Test2 (read-dwarf.Simrel.Base.Test2)

Module Base.Test2

val infer_rels : 'a State.Tree.t pair -> rels
diff --git a/doc/html/read-dwarf/Simrel/Base/Test3/index.html b/doc/html/read-dwarf/Simrel/Base/Test3/index.html index fa2ae331..ee8c84da 100644 --- a/doc/html/read-dwarf/Simrel/Base/Test3/index.html +++ b/doc/html/read-dwarf/Simrel/Base/Test3/index.html @@ -1,2 +1,2 @@ -Test3 (read-dwarf.Simrel.Base.Test3)

Module Base.Test3

val infer_rels : 'a State.Tree.t pair -> rels
\ No newline at end of file +Test3 (read-dwarf.Simrel.Base.Test3)

Module Base.Test3

val infer_rels : 'a State.Tree.t pair -> rels
diff --git a/doc/html/read-dwarf/Simrel/Base/index.html b/doc/html/read-dwarf/Simrel/Base/index.html index 5497e80e..d40910bb 100644 --- a/doc/html/read-dwarf/Simrel/Base/index.html +++ b/doc/html/read-dwarf/Simrel/Base/index.html @@ -1,2 +1,9 @@ -Base (read-dwarf.Simrel.Base)

Module Simrel.Base

module Tree = State.Tree
module ExTy = Exp.Typed
module Tval = State.Tval
module Reg = State.Reg
module Simplify = State.Simplify
module Z3St = State.Simplify.Z3St
module Id = State.Id
type tree = Run.Block_lib.label Tree.t
type 'a pair = {
o0 : 'a;
o2 : 'a;
}
val rel_of : (State__Base.Exp.varAst.no) ExTy.t pair list -> Tval.t -> Tval.t -> (State__Base.Exp.varAst.no) ExTy.t
type field =
| Reg of Reg.t
type sep =
| Eq of field pair list
type rel =
| Same
| Star of sep * rel

Same should always be at the end.

type matched =
| Matched of State.t pair * rel
| NoMatch of State.t
type rels = matched list
module Test2 : sig ... end
module Test3 : sig ... end
module MemRel : sig ... end
module RegRel : sig ... end
val sep_to_exp : State.t pair -> sep -> field list * (State__Base.Exp.varAst.no) ExTy.t list
val assn_to_exp : State.t -> (State.Exp.varAst.no) ExTy.t
val rel_to_exp : State.t pair -> rel -> (State__Base.Exp.varAst.no) ExTy.t list
val check_rel : matched list -> bool
\ No newline at end of file +Base (read-dwarf.Simrel.Base)

Module Simrel.Base

This module encodes the definition and checking of the simulation relation.

module Tree = State.Tree
module ExTy = Exp.Typed
module Tval = State.Tval
module Reg = State.Reg
module Simplify = State.Simplify
module Z3St = State.Simplify.Z3St
module Id = State.Id
type 'a pair = {
  1. o0 : 'a;
  2. o2 : 'a;
}
val rel_of : + (State__Base.Var.t, Ast.no) ExTy.t pair list -> + Tval.t -> + Tval.t -> + (State__Base.Var.t, Ast.no) ExTy.t
type field =
  1. | Reg of Reg.t
type sep =
  1. | Eq of field pair list
type rel =
  1. | Same
  2. | Star of sep * rel

Same should always be at the end.

type matched =
  1. | Matched of State.t pair * rel
  2. | NoMatch of State.t
type rels = matched list
module Test2 : sig ... end
module Test3 : sig ... end
module MemRel : sig ... end
module RegRel : sig ... end
val sep_to_exp : + State.t pair -> + sep -> + field list * (State__Base.Var.t, Ast.no) ExTy.t list
val assn_to_exp : State.t -> (State.Exp.var, Ast.no) ExTy.t
val rel_to_exp : State.t pair -> rel -> (State__Base.Var.t, Ast.no) ExTy.t list
val check_rel : matched list -> bool
diff --git a/doc/html/read-dwarf/Simrel/index.html b/doc/html/read-dwarf/Simrel/index.html index 7c456945..30f4450a 100644 --- a/doc/html/read-dwarf/Simrel/index.html +++ b/doc/html/read-dwarf/Simrel/index.html @@ -1,2 +1,2 @@ -Simrel (read-dwarf.Simrel)

Module Simrel

module Base : sig ... end
\ No newline at end of file +Simrel (read-dwarf.Simrel)

Module Simrel

module Base : sig ... end

This module encodes the definition and checking of the simulation relation.

diff --git a/doc/html/read-dwarf/State/Base/Exp/index.html b/doc/html/read-dwarf/State/Base/Exp/index.html index 44e77e77..a2508ba5 100644 --- a/doc/html/read-dwarf/State/Base/Exp/index.html +++ b/doc/html/read-dwarf/State/Base/Exp/index.html @@ -1,2 +1,2 @@ -Exp (read-dwarf.State.Base.Exp)

Module Base.Exp

Module for state expressions

include Exp.S with type var = var
type var = var

The type of variable provided in the functor

type t = (varAst.no) Exp.Typed.t

The type of expression on which this module works

val equal : t -> t -> bool

Test syntactic equality. a + b and b + a would test different under this predicate

val pp : t -> Utils.Pp.document

Pretty print the expression using Exp.PpExp

val pp_smt : t -> Utils.Pp.document

Pretty print the expression in SMTLIB language

val of_var : var -> t

Create an expression from a variable

val add_type : ('avarAst.noAst.no) Ast.exp -> t

Convert a similar but untyped expression to an expression of type t

val of_reg : id -> Reg.t -> t

Create an expression from an register and a state id

\ No newline at end of file +Exp (read-dwarf.State.Base.Exp)

Module Base.Exp

Module for state expressions

include Exp.S with type var = var
type var = var

The type of variable provided in the functor

type t = (var, Ast.no) Exp.Typed.t

The type of expression on which this module works

val equal : t -> t -> bool

Test syntactic equality. a + b and b + a would test different under this predicate

val pp : t -> Utils.Pp.document

Pretty print the expression using PpExp

val pp_smt : t -> Utils.Pp.document

Pretty print the expression in SMTLIB language

val of_var : var -> t

Create an expression from a variable

val add_type : ('a, var, Ast.no, Ast.no) Ast.exp -> t

Convert a similar but untyped expression to an expression of type t

val of_reg : id -> Reg.t -> t

Create an expression from an register and a state id

val expect_sym_address : t -> Elf.Address.t
val of_section : size:int -> string -> t
val of_address : size:int -> Elf.Address.t -> t
diff --git a/doc/html/read-dwarf/State/Base/Id/index.html b/doc/html/read-dwarf/State/Base/Id/index.html index a1d926b9..d5249712 100644 --- a/doc/html/read-dwarf/State/Base/Id/index.html +++ b/doc/html/read-dwarf/State/Base/Id/index.html @@ -1,2 +1,2 @@ -Id (read-dwarf.State.Base.Id)

Module Base.Id

The type of a state ID. for now it's an integer, but it may change later In particular whether a state belong to O0 or O2 may be part of the id at some point.

type t
val to_string : t -> string
val of_string : string -> t
val equal : t -> t -> bool
val pp : t -> PPrintEngine.document
\ No newline at end of file +Id (read-dwarf.State.Base.Id)

Module Base.Id

The type of a state ID. for now it's an integer, but it may change later In particular whether a state belong to O0 or O2 may be part of the id at some point.

type t
val to_string : t -> string
val of_string : string -> t
val equal : t -> t -> bool
val pp : t -> Utils.Pp.document
diff --git a/doc/html/read-dwarf/State/Base/Mem/Fragment/Block/index.html b/doc/html/read-dwarf/State/Base/Mem/Fragment/Block/index.html index ea0e8a65..0ac5f837 100644 --- a/doc/html/read-dwarf/State/Base/Mem/Fragment/Block/index.html +++ b/doc/html/read-dwarf/State/Base/Mem/Fragment/Block/index.html @@ -1,2 +1,2 @@ -Block (read-dwarf.State.Base.Mem.Fragment.Block)

Module Fragment.Block

This module provide the concept of memory block, as used by a fragment.

The block represent a memory area that contain a single memory expression.

It may represent a symbolic or concrete address. In the fist case, It may also be concretely bounded.

type t = private {
base : exp option;

The symbolic base. If None the block is concrete

offset : int;

The concrete part of the address

size : Size.t;
bounds : (int * int) option;

Optional bounds: (min, max) means [min:max)

}

The type for representing memory blocks

val make_split : ?⁠bounds:(int * int) -> exp -> Size.t -> t

Make the block from an address and a size. The address is automatically split between a symbolic and concrete part.

\ No newline at end of file +Block (read-dwarf.State.Base.Mem.Fragment.Block)

Module Fragment.Block

This module provide the concept of memory block, as used by a fragment.

The block represent a memory area that contain a single memory expression.

It may represent a symbolic or concrete address. In the fist case, It may also be concretely bounded.

type t = private {
  1. base : exp option;
    (*

    The symbolic base. If None the block is concrete

    *)
  2. offset : int;
    (*

    The concrete part of the address

    *)
  3. size : Size.t;
  4. bounds : (int * int) option;
    (*

    Optional bounds: (min, max) means [min:max)

    *)
}

The type for representing memory blocks

val make_split : ?bounds:(int * int) -> exp -> Size.t -> t

Make the block from an address and a size. The address is automatically split between a symbolic and concrete part.

diff --git a/doc/html/read-dwarf/State/Base/Mem/Fragment/Event/index.html b/doc/html/read-dwarf/State/Base/Mem/Fragment/Event/index.html index 614c05fb..7b1e6ae5 100644 --- a/doc/html/read-dwarf/State/Base/Mem/Fragment/Event/index.html +++ b/doc/html/read-dwarf/State/Base/Mem/Fragment/Event/index.html @@ -1,2 +1,2 @@ -Event (read-dwarf.State.Base.Mem.Fragment.Event)

Module Fragment.Event

This module provide the trace of reads and writes to a symbolic fragment.

type t =
| Read of Block.t * var

From Block.t, read var

| Write of Block.t * exp

To Block.t, write exp

Types of memory events

\ No newline at end of file +Event (read-dwarf.State.Base.Mem.Fragment.Event)

Module Fragment.Event

This module provide the trace of reads and writes to a symbolic fragment.

type t =
  1. | Read of Block.t * var
    (*

    From Block.t, read var

    *)
  2. | Write of Block.t * exp
    (*

    To Block.t, write exp

    *)

Types of memory events

val pp : t -> Utils.Pp.document
diff --git a/doc/html/read-dwarf/State/Base/Mem/Fragment/index.html b/doc/html/read-dwarf/State/Base/Mem/Fragment/index.html index eea1a725..65a377f5 100644 --- a/doc/html/read-dwarf/State/Base/Mem/Fragment/index.html +++ b/doc/html/read-dwarf/State/Base/Mem/Fragment/index.html @@ -1,2 +1,2 @@ -Fragment (read-dwarf.State.Base.Mem.Fragment)

Module Mem.Fragment

type var = Var.t

The type of variables used

module Size = Ast.Size
type exp = (varAst.no) Exp.Typed.t

The type of expressions stored in the fragment

module Block : sig ... end

This module provide the concept of memory block, as used by a fragment.

module Event : sig ... end

This module provide the trace of reads and writes to a symbolic fragment.

type t

The type of a memory fragment

val get_trace : t -> Event.t list

Get the trace of given fragment

val empty : t

The empty memory fragment. Any read will be symbolic

val from : t -> t

Start a new memory fragment based on the previous one

val try_read : t -> Block.t -> exp option

Try to read a expression in a block. If one can provide a symbolic expression representing the content of the block then Some is returned, otherwise None is returned.

val try_read_naive : t -> Block.t -> exp option

Same semantic as try_read but ignores the caches. Is supposed to be slower.

The required property is that if try_read return Some value then try_read_naive must return the same value. It's possible that try_read_naive give a result when try_read don't

val read_sym : t -> Block.t -> var -> t

Read a symbolic variable from a block. This bound this symbolic variable to the The content of the block in the current memory state.contents

val write : t -> Block.t -> exp -> t

Write a symbolic expression at a block

val map_exp : (exp -> exp) -> t -> t

Map a function over all contained expressions. This function must not change the semantic meaning of symbolic expressions in any way. It is intended to be used with simplifying functions and the like

val iter_exp : (exp -> unit) -> t -> unit

Iter a function over all contained expression. Expression may appear more or less than anticipated because of various caching.

val check_cache : t -> unit

Check cache integrity. Throw if something is wrong

val is_empty : t -> bool

Tells if the memory is empty since it's initial base. (No new memory operation were added

val pp_raw : t -> Utils.Pp.document

Pretty prints the raw internals fragment. TODO: A nice pretty printer

\ No newline at end of file +Fragment (read-dwarf.State.Base.Mem.Fragment)

Module Mem.Fragment

type var = Var.t

The type of variables used

module Size = Ast.Size
type exp = (var, Ast.no) Exp.Typed.t

The type of expressions stored in the fragment

module Block : sig ... end

This module provide the concept of memory block, as used by a fragment.

module Event : sig ... end

This module provide the trace of reads and writes to a symbolic fragment.

type t

The type of a memory fragment

val get_trace : t -> Event.t list

Get the trace of given fragment

val empty : t

The empty memory fragment. Any read will be symbolic

val from : t -> t

Start a new memory fragment based on the previous one

val try_read : t -> Block.t -> exp option

Try to read a expression in a block. If one can provide a symbolic expression representing the content of the block then Some is returned, otherwise None is returned.

val try_read_naive : t -> Block.t -> exp option

Same semantic as try_read but ignores the caches. Is supposed to be slower.

The required property is that if try_read return Some value then try_read_naive must return the same value. It's possible that try_read_naive give a result when try_read don't

val read_sym : t -> Block.t -> var -> t

Read a symbolic variable from a block. This bound this symbolic variable to the The content of the block in the current memory state.contents

val write : t -> Block.t -> exp -> t

Write a symbolic expression at a block

val map_exp : (exp -> exp) -> t -> t

Map a function over all contained expressions. This function must not change the semantic meaning of symbolic expressions in any way. It is intended to be used with simplifying functions and the like

val iter_exp : (exp -> unit) -> t -> unit

Iter a function over all contained expression. Expression may appear more or less than anticipated because of various caching.

val check_cache : t -> unit

Check cache integrity. Throw if something is wrong

val is_empty : t -> bool

Tells if the memory is empty since it's initial base. (No new memory operation were added

val pp_raw : t -> Utils.Pp.document

Pretty prints the raw internals fragment. TODO: A nice pretty printer

diff --git a/doc/html/read-dwarf/State/Base/Mem/index.html b/doc/html/read-dwarf/State/Base/Mem/index.html index 787a820c..24df5155 100644 --- a/doc/html/read-dwarf/State/Base/Mem/index.html +++ b/doc/html/read-dwarf/State/Base/Mem/index.html @@ -1,2 +1,2 @@ -Mem (read-dwarf.State.Base.Mem)

Module Base.Mem

This module manages the memory part of the state.

The symbolic memory bounds certain symbolic address to symbolic values, but most addresses are not bound. This basically correspond to a concrete instantiation of a SymbolicFragment.

However in most case, we have some information that some symbolic value do not alias other symbolic values.For example in no address involving the stack pointer may alias any address no involving the stack pointer except in specific case of escaping which are explicitly not supported (yet). It is intended to support escaping later. There can be other kind of non-aliasing information in case of explicit restrict annotation or when implicitly passing or returning value by pointer (which some ABI do when such value are too big).

In the absence of escaping, such problem can be solved by representing the memory with multiple SymbolicFragment. One for the main memory, and one for each "restricted block". The module encapsulate all those different fragment.

To manage such a system, we use the C type system to carry around provenance information in the type Ctype.provenance. When doing a read or a write, this provenance is used to route the read or write to the right symbolic block.

This means that the provenance-tracking part of the C type system must be part of the TCB of read-dwarf as the soundness of the symbolic memory model depend on it.

Finally, the current implementation is only suitable for sequential execution, a new theoretical model and implementation must be developed for concurrent shared memory.

Implementation detail: This type has an imperative interface even if the underlying SymbolicFragment has a pure interface.

type t
val new_frag : t -> exp -> Ctype.provenance

Add a new fragment with the specified base

val get_main : t -> Fragment.t

Get the main fragment of memory

\ No newline at end of file +Mem (read-dwarf.State.Base.Mem)

Module Base.Mem

This module manages the memory part of the state.

The symbolic memory bounds certain symbolic address to symbolic values, but most addresses are not bound. This basically correspond to a concrete instantiation of a SymbolicFragment.

However in most case, we have some information that some symbolic value do not alias other symbolic values.For example in no address involving the stack pointer may alias any address no involving the stack pointer except in specific case of escaping which are explicitly not supported (yet). It is intended to support escaping later. There can be other kind of non-aliasing information in case of explicit restrict annotation or when implicitly passing or returning value by pointer (which some ABI do when such value are too big).

In the absence of escaping, such problem can be solved by representing the memory with multiple SymbolicFragment. One for the main memory, and one for each "restricted block". The module encapsulate all those different fragment.

To manage such a system, we use the C type system to carry around provenance information in the type Ctype.provenance. When doing a read or a write, this provenance is used to route the read or write to the right symbolic block.

This means that the provenance-tracking part of the C type system must be part of the TCB of read-dwarf as the soundness of the symbolic memory model depend on it.

Finally, the current implementation is only suitable for sequential execution, a new theoretical model and implementation must be developed for concurrent shared memory.

Implementation detail: This type has an imperative interface even if the underlying SymbolicFragment has a pure interface.

type t
val new_frag : t -> exp -> Ctype.provenance

Add a new fragment with the specified base

module Fragment : sig ... end
val get_main : t -> Fragment.t

Get the main fragment of memory

val get_frag : t -> int -> Exp.t * Fragment.t

Get fragment

diff --git a/doc/html/read-dwarf/State/Base/Tval/index.html b/doc/html/read-dwarf/State/Base/Tval/index.html index 6839b24a..a6837807 100644 --- a/doc/html/read-dwarf/State/Base/Tval/index.html +++ b/doc/html/read-dwarf/State/Base/Tval/index.html @@ -1,2 +1,2 @@ -Tval (read-dwarf.State.Base.Tval)

Module Base.Tval

Module for optionally typed state expressions. Those are symbolic values which may or may not have a C type.

type t = {
ctyp : Ctype.t option;
exp : exp;
}
val make : ?⁠ctyp:Ctype.t -> exp -> t

Make a new typed value with optionally a Ctype

val of_exp : ?⁠ctyp:Ctype.t -> exp -> t
val of_var : ?⁠ctyp:Ctype.t -> var -> t
val of_reg : ?⁠ctyp:Ctype.t -> id -> Reg.t -> t
val map_exp : (exp -> exp) -> t -> t
val iter_exp : (exp -> 'a) -> t -> 'a
val exp : t -> exp
val ctyp : t -> Ctype.t option
val equal : t -> t -> bool
val pp : t -> PPrintEngine.document
\ No newline at end of file +Tval (read-dwarf.State.Base.Tval)

Module Base.Tval

Module for optionally typed state expressions. Those are symbolic values which may or may not have a C type.

type t = {
  1. ctyp : Ctype.t option;
  2. exp : exp;
}
val make : ?ctyp:Ctype.t -> exp -> t

Make a new typed value with optionally a Ctype

val of_exp : ?ctyp:Ctype.t -> exp -> t
val of_var : ?ctyp:Ctype.t -> var -> t
val of_reg : ?ctyp:Ctype.t -> id -> Reg.t -> t
val map_exp : (exp -> exp) -> t -> t
val iter_exp : (exp -> 'a) -> t -> 'a
val exp : t -> exp
val ctyp : t -> Ctype.t option
val equal : t -> t -> bool
val pp : t -> Utils.Pp.document
diff --git a/doc/html/read-dwarf/State/Base/Var/index.html b/doc/html/read-dwarf/State/Base/Var/index.html index 397ee98f..12183a09 100644 --- a/doc/html/read-dwarf/State/Base/Var/index.html +++ b/doc/html/read-dwarf/State/Base/Var/index.html @@ -1,2 +1,2 @@ -Var (read-dwarf.State.Base.Var)

Module Base.Var

This module provide state variables. Those are all symbolic variables that may appear in a state. If the same (in the Var.equal sense) variable appear in two state, that mean that when considered together, there is an implicit relation between them.

This means that the set of pair of concrete states represented by a pair of symbolic state could be a strict subset of the Cartesian product of the sets of concrete state represented by each symbolic state individually.

type t =
| Register of id * Reg.t

The value of this register in this state

| ReadVar of id * int * Ast.Size.t

The result of a certain read in a certain state. The size part is not semantically important: Two ReadVar with same id and same number may no have different sizes

| Arg of int

A function argument

| RetArg

The address to which the return value should be written. This is used only in certain calling conventions

| RetAddr

The return address: The address to which a "return" instruction would jump.

| NonDet of int * Ast.Size.t

Variable representing non-determinism in the spec. Can only be a bit-vector for now.

The type of a variable in the state

val to_string : t -> string

Convert the variable to the string encoding. For parsing infrastructure reason, the encoding must always contain at least one :.

val expect_register : t -> Reg.t

Expect a register variable and return the corresponding register. Throw otherwise.

val expect_readvar : t -> int

Expect a read variable and return the corresponding index. Throw otherwise.

val of_string : string -> t

The opposite of to_string. Will raise Invalid_argument when the string don't match

val of_reg : id -> Reg.t -> t

Create a register variable bound to the provided state id and register

val equal : t -> t -> bool
val hash : t -> int

Hashing function for variable. For now it's polymorphic but this may stop at any time

val pp : t -> PPrintEngine.document

Basically to_string in pp mode

val pp_bar : t -> PPrintEngine.document

Pretty prints but with bars around

val ty : t -> Reg.ty

Get the type of a variable

\ No newline at end of file +Var (read-dwarf.State.Base.Var)

Module Base.Var

This module provide state variables. Those are all symbolic variables that may appear in a state. If the same (in the Var.equal sense) variable appear in two state, that mean that when considered together, there is an implicit relation between them.

This means that the set of pair of concrete states represented by a pair of symbolic state could be a strict subset of the Cartesian product of the sets of concrete state represented by each symbolic state individually.

type t =
  1. | Register of id * Reg.t
    (*

    The value of this register in this state

    *)
  2. | ReadVar of id * int * Ast.Size.t
    (*

    The result of a certain read in a certain state. The size part is not semantically important: Two ReadVar with same id and same number may no have different sizes

    *)
  3. | Arg of int
    (*

    A function argument

    *)
  4. | RetArg
    (*

    The address to which the return value should be written. This is used only in certain calling conventions

    *)
  5. | RetAddr
    (*

    The return address: The address to which a "return" instruction would jump.

    *)
  6. | NonDet of int * Ast.Size.t
    (*

    Variable representing non-determinism in the spec. Can only be a bit-vector for now.

    *)
  7. | Section of string
    (*

    Symbolic base address of ELF section. Assume 64bit for now.

    *)

The type of a variable in the state

val to_string : t -> string

Convert the variable to the string encoding. For parsing infrastructure reason, the encoding must always contain at least one :.

val expect_register : t -> Reg.t

Expect a register variable and return the corresponding register. Throw otherwise.

val expect_readvar : t -> int

Expect a read variable and return the corresponding index. Throw otherwise.

val of_string : string -> t

The opposite of to_string. Will raise Invalid_argument when the string don't match

val of_reg : id -> Reg.t -> t

Create a register variable bound to the provided state id and register

val equal : t -> t -> bool
val hash : t -> int

Hashing function for variable. For now it's polymorphic but this may stop at any time

val pp : t -> Utils.Pp.document

Basically to_string in pp mode

val pp_bar : t -> Utils.Pp.document

Pretty prints but with bars around

val ty : t -> Reg.ty

Get the type of a variable

val new_nondet : Ast.Size.t -> t

Get a fresh NonDet variable

diff --git a/doc/html/read-dwarf/State/Base/index.html b/doc/html/read-dwarf/State/Base/index.html index c943f564..c0f4fc1a 100644 --- a/doc/html/read-dwarf/State/Base/index.html +++ b/doc/html/read-dwarf/State/Base/index.html @@ -1,2 +1,14 @@ -Base (read-dwarf.State.Base)

Module State.Base

This module introduce a type to represent the state of the machine.

The symbolic state in this module do not mathematically represent a single state but a set of concrete state with all the symbolic variable over the whole range of their types. State also contain assertions, and so only represent the subset of concrete states, that satisfy all assertions.

Currently the state type only represent the register (including system register) and sequential memory part of an actual machine state. Any other architectural state is not represented.

Additionally, state contain C type information for the Ctype inference system. Those fields are not semantically part of the state and do not influence in any way which concrete states are represented by the symbolic state. Expect for provenance information: Pointers can be tagged with provenance information which mean that they are part of a specific restricted block of memory or the main block. See Mem for more information. Thus all the implicit non-aliasing assertion implied by those provenance field are to be considered as part of the group of assertion restricting the set of concrete state represented by a symbolic state.

The presence of C types is optional, which means that this state type can be used in a completely untyped context.

Concrete detail about how symbolic state are represented in Ocaml is in the documentation of t.

State id

module Id : sig ... end

The type of a state ID. for now it's an integer, but it may change later In particular whether a state belong to O0 or O2 may be part of the id at some point.

type id = Id.t

State variable management

module Var : sig ... end

This module provide state variables. Those are all symbolic variables that may appear in a state. If the same (in the Var.equal sense) variable appear in two state, that mean that when considered together, there is an implicit relation between them.

type var = Var.t

The type of variables

State expression and typed-value management

module Exp : sig ... end

Module for state expressions

type exp = Exp.t
module Tval : sig ... end

Module for optionally typed state expressions. Those are symbolic values which may or may not have a C type.

type tval = Tval.t

State memory management

module Mem : sig ... end

This module manages the memory part of the state.

State type

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 .rodata). It will affect the execution behavior. However the symbolic execution should always be more concrete with it than without it

fenv : Fragment.env;

The memory type environment. See Fragment.env

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.

val equal : t -> t -> bool

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:

  • The id can be serialized and read to external program and files without losing it's meaning.
  • The id allow to break cyclical type dependency.

This is in particular useful for variable that contain and id instead of a pointer to the state:

  • The variable can be serialized in text manner to a SMT solver and keep their meaning.
  • The 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 : (idt) Utils.WeakMap.t

Global map of states to associate them with identifiers

val next_id : id Stdlib.ref

Next unused id

val of_id : id -> t

Get a state from its id

val to_id : t -> id

Get the id of a state

State management

val lock : t -> unit

Lock the state. Once a state is locked it should not be mutated anymore

val unsafe_unlock : t -> unit

Unlock the state. This is dangerous, do not use if you do not know how to use it, The only realistic use case, is for calling a simplifier and thus not changing the semantic meaning of the state in any way while mutating it.

This is deprecated and should disappear at some point.

val is_locked : t -> bool

Tell if the state is locked, in which case it shouldn't be mutated

val is_possible : t -> bool

Tell is state is possible.

A state is impossible if it has a single assert that is false. This means that this symbolic state represent the empty set of concrete states.

StateSimplify.ctxfull will call the SMT solver and set the assertions to that if required so you should call that function before is_possible

val make : ?⁠elf:Elf.File.t -> unit -> t

Makes a fresh state with all variable fresh and new. This fresh state is unlocked.

This should only be used by Init, all other state should be derived from Init.state.

val copy : ?⁠elf:Elf.File.t -> t -> t

Do a deep copy of all the mutable part of the state, so it can be mutated without retro-action.

If the source state is locked, then new state is based on it (in the sense of t.base_state), otherwise it is a literal copy of each field.

The returned state is always unlocked

val copy_if_locked : ?⁠elf:Elf.File.t -> t -> t

Copy the state with copy if and only if it is locked. The returned state is always unlocked

State convenience manipulation

val push_assert : t -> exp -> unit

Add an assertion to a state

val set_impossible : t -> unit

Set a state to be impossible (single false assert).

val set_asserts : t -> exp list -> unit

Set a state's asserts

val map_mut_exp : (exp -> exp) -> t -> unit

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 iter_exp : (exp -> unit) -> t -> unit

Iterates a function on all the expressions of a state

val iter_var : (var -> unit) -> t -> unit

Iterates a function on all the variables of a state

State memory accessors

val make_read : t -> ?⁠ctyp:Ctype.t -> Ast.Size.t -> var

Create a new Var.t.ReadVar by mutating the state

val set_read : t -> int -> exp -> unit

Set a Var.t.ReadVar to a specific value in t.read_vars

val read_from_rodata : t -> addr:exp -> size:Ast.Size.t -> exp option

Read memory from rodata

val read : provenance:Ctype.provenance -> ?⁠ctyp:Ctype.t -> t -> addr:exp -> size:Ast.Size.t -> exp

Read the block designated by addr and size from the state and return an expression read. This will mutate the state to bind the read result to the newly created read variable.

The ctyp parameter may give a type to the read variable. This type is fully trusted and not checked in any way.

The expression could be either:

  • An actual expression if the read could be resolved.
  • Just the symbolic read variable if the read couldn't be resolved

This function is for case with provenance information is known.

val read_noprov : ?⁠ctyp:Ctype.t -> t -> addr:exp -> size:Ast.Size.t -> exp

A wrapper around read for use when there is no provenance information. It may able to still perform the read under certain condition and otherwise will fail.

val write : provenance:Ctype.provenance -> t -> addr:exp -> size:Ast.Size.t -> exp -> unit

Write the provided value in the block. Mutate the state.

val write_noprov : t -> addr:exp -> size:Ast.Size.t -> exp -> unit

A wrapper around write for use when there is no provenance information. It may able to still perform the write under certain condition and otherwise will fail.

State register accessors

val reset_reg : t -> ?⁠ctyp:Ctype.t -> Reg.t -> unit

Reset the register to a symbolic value, and resets the type to the provided type (or no type if not provided)

val set_reg : t -> Reg.t -> tval -> unit

Sets the content of register

val set_reg_type : t -> Reg.t -> Ctype.t -> unit

Sets the type of the register, leaves the value unchanged

val get_reg : t -> Reg.t -> tval

Get the content of the register with it's type

val get_reg_exp : t -> Reg.t -> exp

Get the content of the register without it's type

val update_reg_exp : t -> Reg.t -> (exp -> exp) -> unit

Apply a function to a register. Leave the type intact

Pc manipulation

val set_pc : pc:Reg.t -> t -> int -> unit

Set the PC to a concrete value and keep its type appropriate

val bump_pc : pc:Reg.t -> t -> int -> unit

Bump a concrete PC by a concrete bump (generally the size of a non-branching instruction

val concretize_pc : pc:Reg.t -> t -> unit

Try to evaluate the PC if it is concrete

val set_last_pc : t -> int -> unit

Set the last_pc of the state

Pretty printing

val pp : t -> PPrintEngine.document
val pp_partial : regs:Reg.t list -> t -> PPrintEngine.document

Print only the mentioned regs and the memory and asserts since the base_state. Until a better solution is found, the fenv will be printed entirely all the time

\ No newline at end of file +Base (read-dwarf.State.Base)

Module State.Base

This module introduce a type to represent the state of the machine.

The symbolic state in this module do not mathematically represent a single state but a set of concrete state with all the symbolic variable over the whole range of their types. State also contain assertions, and so only represent the subset of concrete states, that satisfy all assertions.

Currently the state type only represent the register (including system register) and sequential memory part of an actual machine state. Any other architectural state is not represented.

Additionally, state contain C type information for the Ctype inference system. Those fields are not semantically part of the state and do not influence in any way which concrete states are represented by the symbolic state. Expect for provenance information: Pointers can be tagged with provenance information which mean that they are part of a specific restricted block of memory or the main block. See Mem for more information. Thus all the implicit non-aliasing assertion implied by those provenance field are to be considered as part of the group of assertion restricting the set of concrete state represented by a symbolic state.

The presence of C types is optional, which means that this state type can be used in a completely untyped context.

Concrete detail about how symbolic state are represented in Ocaml is in the documentation of t.

State id

module Id : sig ... end

The type of a state ID. for now it's an integer, but it may change later In particular whether a state belong to O0 or O2 may be part of the id at some point.

type id = Id.t

State variable management

module Var : sig ... end

This module provide state variables. Those are all symbolic variables that may appear in a state. If the same (in the Var.equal sense) variable appear in two state, that mean that when considered together, there is an implicit relation between them.

type var = Var.t

The type of variables

State expression and typed-value management

module Exp : sig ... end

Module for state expressions

type exp = Exp.t
module Tval : sig ... end

Module for optionally typed state expressions. Those are symbolic values which may or may not have a C type.

type tval = Tval.t
module Relocation : sig ... end

State memory management

module Mem : sig ... end

This module manages the memory part of the state.

State type

type t = private {
  1. id : id;
  2. base_state : t option;
    (*

    The immediate dominator state in the control flow graph

    *)
  3. mutable locked : bool;
    (*

    Tells if the state is locked

    *)
  4. mutable regs : Tval.t Reg.Map.t;
    (*

    The values and types of registers

    *)
  5. read_vars : Tval.t Utils.Vec.t;
    (*

    The results of reads made since base state

    *)
  6. mutable asserts : exp list;
    (*

    Only asserts since base_state

    *)
  7. mutable relocation_asserts : exp list;
    (*

    Only asserts since base_state

    *)
  8. mem : Mem.t;
  9. 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

    *)
  10. fenv : Fragment.env;
    (*

    The memory type environment. See Fragment.env

    *)
  11. 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.

val equal : t -> t -> bool

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:

  • The id can be serialized and read to external program and files without losing it's meaning.
  • The id allow to break cyclical type dependency.

This is in particular useful for variable that contain and id instead of a pointer to the state:

  • The variable can be serialized in text manner to a SMT solver and keep their meaning.
  • The Var.t type can be defined before the t type.

That means that in theory a state could be Garbage collected while a variable still point to it. However a variable is only allowed to exists in a state if the id it points to is among the ancestors via the base_state relationship of the containing state. Since base_state is an GC-owning pointer, this ensure that while the containing state is alive, the variable target state is also alive.

val id2state : (id, t) Utils.WeakMap.t

Global map of states to associate them with identifiers

val next_id : id Stdlib.ref

Next unused id

val of_id : id -> t

Get a state from its id

val to_id : t -> id

Get the id of a state

State management

val lock : t -> unit

Lock the state. Once a state is locked it should not be mutated anymore

val unsafe_unlock : t -> unit

Unlock the state. This is dangerous, do not use if you do not know how to use it, The only realistic use case, is for calling a simplifier and thus not changing the semantic meaning of the state in any way while mutating it.

This is deprecated and should disappear at some point.

  • deprecated Stop unlocking states
val is_locked : t -> bool

Tell if the state is locked, in which case it shouldn't be mutated

val is_possible : t -> bool

Tell is state is possible.

A state is impossible if it has a single assert that is false. This means that this symbolic state represent the empty set of concrete states.

StateSimplify.ctxfull will call the SMT solver and set the assertions to that if required so you should call that function before is_possible

val make : ?elf:Elf.File.t -> unit -> t

Makes a fresh state with all variable fresh and new. This fresh state is unlocked.

This should only be used by Init, all other state should be derived from Init.state.

val copy : ?elf:Elf.File.t -> t -> t

Do a deep copy of all the mutable part of the state, so it can be mutated without retro-action.

If the source state is locked, then new state is based on it (in the sense of t.base_state), otherwise it is a literal copy of each field.

The returned state is always unlocked

val copy_if_locked : ?elf:Elf.File.t -> t -> t

Copy the state with copy if and only if it is locked. The returned state is always unlocked

val init_sections : sp:(unit -> Reg.t) -> addr_size:int -> t -> t
val init_sections_symbolic : sp:(unit -> Reg.t) -> addr_size:int -> t -> t

Assigns all sections with global objects to Main fragment

State convenience manipulation

val push_assert : t -> exp -> unit

Add an assertion to a state

val push_relocation_assert : t -> exp -> unit

Add an assertion to a state

val set_impossible : t -> unit

Set a state to be impossible (single false assert).

val set_asserts : t -> exp list -> unit

Set a state's asserts

val map_mut_exp : (exp -> exp) -> t -> unit

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 iter_exp : (exp -> unit) -> t -> unit

Iterates a function on all the expressions of a state

val iter_var : (var -> unit) -> t -> unit

Iterates a function on all the variables of a state

State memory accessors

val make_read : t -> ?ctyp:Ctype.t -> Ast.Size.t -> var

Create a new Var.t.ReadVar by mutating the state

val set_read : t -> int -> exp -> unit

Set a Var.t.ReadVar to a specific value in t.read_vars

val read_from_rodata : t -> addr:exp -> size:Ast.Size.t -> exp option

Read memory from rodata

val read : + provenance:Ctype.provenance -> + ?ctyp:Ctype.t -> + t -> + addr:exp -> + size:Ast.Size.t -> + exp

Read the block designated by addr and size from the state and return an expression read. This will mutate the state to bind the read result to the newly created read variable.

The ctyp parameter may give a type to the read variable. This type is fully trusted and not checked in any way.

The expression could be either:

  • An actual expression if the read could be resolved.
  • Just the symbolic read variable if the read couldn't be resolved

This function is for case with provenance information is known.

val read_noprov : ?ctyp:Ctype.t -> t -> addr:exp -> size:Ast.Size.t -> exp

A wrapper around read for use when there is no provenance information. It may able to still perform the read under certain condition and otherwise will fail.

val write : + provenance:Ctype.provenance -> + t -> + addr:exp -> + size:Ast.Size.t -> + exp -> + unit

Write the provided value in the block. Mutate the state.

val write_noprov : t -> addr:exp -> size:Ast.Size.t -> exp -> unit

A wrapper around write for use when there is no provenance information. It may able to still perform the write under certain condition and otherwise will fail.

State register accessors

val reset_reg : t -> ?ctyp:Ctype.t -> Reg.t -> unit

Reset the register to a symbolic value, and resets the type to the provided type (or no type if not provided)

val set_reg : t -> Reg.t -> tval -> unit

Sets the content of register

val set_reg_type : t -> Reg.t -> Ctype.t -> unit

Sets the type of the register, leaves the value unchanged

val get_reg : t -> Reg.t -> tval

Get the content of the register with it's type

val get_reg_exp : t -> Reg.t -> exp

Get the content of the register without it's type

val update_reg_exp : t -> Reg.t -> (exp -> exp) -> unit

Apply a function to a register. Leave the type intact

Pc manipulation

val set_pc : pc:Reg.t -> t -> int -> unit

Set the PC to a concrete value and keep its type appropriate

val set_pc_sym : pc:Reg.t -> t -> Elf.Address.t -> unit
val bump_pc : pc:Reg.t -> t -> int -> unit

Bump a concrete PC by a concrete bump (generally the size of a non-branching instruction

val concretize_pc : pc:Reg.t -> t -> unit

Try to evaluate the PC if it is concrete

val set_last_pc : t -> Elf.Address.t -> unit

Set the last_pc of the state

Pretty printing

val pp : t -> Utils.Pp.document
val pp_partial : regs:Reg.t list -> t -> Utils.Pp.document

Print only the mentioned regs and the memory and asserts since the base_state. Until a better solution is found, the fenv will be printed entirely all the time

diff --git a/doc/html/read-dwarf/State/Fragment/Env/index.html b/doc/html/read-dwarf/State/Fragment/Env/index.html index 62f60353..d7bb0a67 100644 --- a/doc/html/read-dwarf/State/Fragment/Env/index.html +++ b/doc/html/read-dwarf/State/Fragment/Env/index.html @@ -1,2 +1,2 @@ -Env (read-dwarf.State.Fragment.Env)

Module Fragment.Env

type frag = t
type t = {
frags : frag Utils.Vec.t;
}
val make : unit -> t
val copy : t -> t
val add_typ : addr:int -> Ctype.t -> t -> id:int -> unit

Add the provided type at addr into a fragment indexed by id in the environment

val adds_frag : ?⁠frag:frag -> t -> unit

Add a new fragment to the environment. empty by default.

val add_frag : ?⁠frag:frag -> t -> int

Add a new fragment to the environment, returns the id of the new fragment. frag is empty by default.

val get : t -> int -> frag
val set : t -> int -> frag -> unit
val pp : t -> Utils.Pp.document
\ No newline at end of file +Env (read-dwarf.State.Fragment.Env)

Module Fragment.Env

type frag = t
type t = {
  1. frags : frag Utils.Vec.t;
}
val make : unit -> t
val copy : t -> t
val add_typ : addr:int -> Ctype.t -> t -> id:int -> unit

Add the provided type at addr into a fragment indexed by id in the environment

val adds_frag : ?frag:frag -> t -> unit

Add a new fragment to the environment. empty by default.

val add_frag : ?frag:frag -> t -> int

Add a new fragment to the environment, returns the id of the new fragment. frag is empty by default.

val get : t -> int -> frag
val set : t -> int -> frag -> unit
val pp : t -> Utils.Pp.document
diff --git a/doc/html/read-dwarf/State/Fragment/index.html b/doc/html/read-dwarf/State/Fragment/index.html index 441bfd7c..2943b6bd 100644 --- a/doc/html/read-dwarf/State/Fragment/index.html +++ b/doc/html/read-dwarf/State/Fragment/index.html @@ -1,6 +1,11 @@ -Fragment (read-dwarf.State.Fragment)

Module State.Fragment

This module define a memory fragment to be used by C types

Fragments

obj is just Ctype.t, but a bug in odoc hides that information. Fragment.obj actually do not exists.

The odoc PR is #349 on github, we just have to wait for it to be merged.

include Utils.RngMap.S with type obj := Ctype.t
type obj

The type of the contained object

type obj_off = obj * int

The type of an object with an offset

type t

The type of the map from address ranges to obj

val empty : t

An empty RngMap

val is_in : objaddr:int -> obj -> int -> bool

Test if an address is inside the object at address objaddr

val at : t -> int -> obj

Get the object containing the address. Throw Not_found if no object contains the address

val at_opt : t -> int -> obj option

Get the object containing the address. None if no object contains the address

val at_off : t -> int -> obj_off

Get the object containing the address and the offset of the address inside the object

at_off map addr = (obj, off) 

means:

         |                      |           |         |
-       map 0                 obj start    point    obj end
-         |<--------------addr-------------->|
-                                |<---off--->|
-                                |<------len obj------>|

In other words, at_off allow a change of coordinate from the map frame to the object frame.

Throw Not_found if no object contains the address

val at_off_opt : t -> int -> obj_off option

Get the object containing the address and the offset of the address inside the object. See at_off for more explanation.

None if no object contains the address

val update : (obj -> obj) -> t -> int -> t

Update the binding containing the provided address. If no binding contained the address, this is a no-op

val map : (obj -> obj) -> t -> t

Map a function over all the objects

val mapi : (int -> obj -> obj) -> t -> t

Map a function over all the objects with their address

val iter : (obj -> unit) -> t -> unit

Iter a function over all the objects

val iteri : (int -> obj -> unit) -> t -> unit

Iter a function over all the objects with their address

val clear_at : t -> int -> t

Clear the object containing the address if any

val clear : t -> pos:int -> len:int -> t

Clear an area of the RngMap.

If an object is partially in the specified block. It will be removed entirely.

See clear_crop for a different behavior. See clear_bounds to allow some bounds to be infinity.

val clear_crop : t -> pos:int -> len:int -> crop:(pos:int -> len:int -> obj -> obj) -> t

Clear an area of the RngMap.

If a block is partially in the specified block, It will be cropped by using the provided crop function.

crop ~pos ~len obj is supposed to crop the object obj and keep only the segment [pos:pos +len) of it (in the object coordinate frame).

val clear_bounds : ?⁠start:int -> ?⁠endp:int -> t -> t

Same as clear but if a bound is missing, then we erase until infinity in that direction. The target interval is [start:endp).

In particular clear_bounds map = empty.

val add : t -> int -> obj -> t

Add an object at a specific address. The whole range of addresses covered by the object must be free

val addp : t -> obj_off -> t

Add an object at a specific address. The whole range of addresses covered by the object must be free

val bindings : t -> (int * obj) list

Give the list of bindings

val to_seq : ?⁠start:int -> ?⁠endp:int -> t -> (int * obj) Utils.Seq.t

Return a sequence of all the object overlapping the range [start:endp). The first and last element may not be entierly contained in the ranged. If any bound is unspecified, it goes to infinity in that direction.

In particular to_seq map will iterate the entiere RngMap

val pp : t -> Utils.Pp.document

Environment

module Env : sig ... end
type env = Env.t
\ No newline at end of file +Fragment (read-dwarf.State.Fragment)

Module State.Fragment

This module define a memory fragment to be used by C types

Fragments

obj is just Ctype.t, but a bug in odoc hides that information. Fragment.obj actually do not exists.

The odoc PR is #349 on github, we just have to wait for it to be merged.

include Utils.RngMap.S with type obj := Ctype.t
type obj_off = Ctype.t * int

The type of an object with an offset

type t

The type of the map from address ranges to obj

val empty : t

An empty RngMap

val is_in : objaddr:int -> Ctype.t -> int -> bool

Test if an address is inside the object at address objaddr

val at : t -> int -> Ctype.t

Get the object containing the address. Throw Not_found if no object contains the address

val at_opt : t -> int -> Ctype.t option

Get the object containing the address. None if no object contains the address

val at_off : t -> int -> obj_off

Get the object containing the address and the offset of the address inside the object

at_off map addr = (obj, off) 

means:

   |                      |           |         |
+ map 0                 obj start    point    obj end
+   |<--------------addr-------------->|
+                          |<---off--->|
+                          |<------len obj------>|

In other words, at_off allow a change of coordinate from the map frame to the object frame.

Throw Not_found if no object contains the address

val at_off_opt : t -> int -> obj_off option

Get the object containing the address and the offset of the address inside the object. See at_off for more explanation.

None if no object contains the address

val update : (Ctype.t -> Ctype.t) -> t -> int -> t

Update the binding containing the provided address. If no binding contained the address, this is a no-op

val map : (Ctype.t -> Ctype.t) -> t -> t

Map a function over all the objects

val mapi : (int -> Ctype.t -> Ctype.t) -> t -> t

Map a function over all the objects with their address

val iter : (Ctype.t -> unit) -> t -> unit

Iter a function over all the objects

val iteri : (int -> Ctype.t -> unit) -> t -> unit

Iter a function over all the objects with their address

val clear_at : t -> int -> t

Clear the object containing the address if any

val clear : t -> pos:int -> len:int -> t

Clear an area of the RngMap.

If an object is partially in the specified block. It will be removed entirely.

See clear_crop for a different behavior. See clear_bounds to allow some bounds to be infinity.

val clear_crop : + t -> + pos:int -> + len:int -> + crop:(pos:int -> len:int -> Ctype.t -> Ctype.t) -> + t

Clear an area of the RngMap.

If a block is partially in the specified block, It will be cropped by using the provided crop function.

crop ~pos ~len obj is supposed to crop the object obj and keep only the segment [pos:pos +len) of it (in the object coordinate frame).

val clear_bounds : ?start:int -> ?endp:int -> t -> t

Same as clear but if a bound is missing, then we erase until infinity in that direction. The target interval is [start:endp).

In particular clear_bounds map = empty.

val add : t -> int -> Ctype.t -> t

Add an object at a specific address. The whole range of addresses covered by the object must be free

val addp : t -> obj_off -> t

Add an object at a specific address. The whole range of addresses covered by the object must be free

val bindings : t -> (int * Ctype.t) list

Give the list of bindings

val to_seq : ?start:int -> ?endp:int -> t -> (int * Ctype.t) Utils.Seq.t

Return a sequence of all the object overlapping the range [start:endp). The first and last element may not be entierly contained in the ranged. If any bound is unspecified, it goes to infinity in that direction.

In particular to_seq map will iterate the entiere RngMap

val pp : t -> Utils.Pp.document

Environment

module Env : sig ... end
type env = Env.t
diff --git a/doc/html/read-dwarf/State/Reg/Map/index.html b/doc/html/read-dwarf/State/Reg/Map/index.html index c060a6e6..ef8d36d9 100644 --- a/doc/html/read-dwarf/State/Reg/Map/index.html +++ b/doc/html/read-dwarf/State/Reg/Map/index.html @@ -1,2 +1,2 @@ -Map (read-dwarf.State.Reg.Map)

Module Reg.Map

This module provide a full map over register in the same way than FullVec provide a map of integers. It still need a generator to generate the value bound to not-yet-added registers.

Because the domain of registers is finite, some extra function are available like iter and iteri that are not possible in FullVec.

If a register is added with add, it is automatically and implicitly added to the Utils.Map and the generator must accept this new value. The generator will never be called on invalid register values (i.e. when the generator is called on a register, the former can get the latter's type and name with reg_type and to_string)

type reg = t
type 'a t

The type of the complete map

val init : (reg -> 'a) -> 'a t

Initialize the map with a generator

val reinit : 'a t -> (reg -> 'a) -> unit

Clear the map and restart with this generator

val copy : 'a t -> 'a t

Make a copy of the map

val set : 'a t -> reg -> 'a -> unit

Set the value of a register

val get : 'a t -> reg -> 'a

Get the value of a register

val map : ('a -> 'b) -> 'a t -> 'b t

Map the function all the registers (including future, not yet added ones)

val map_mut : ('a -> 'a) -> 'a t -> unit

Map the function on all the register by mutation (including future ones)

val map_mut_current : ('a -> 'a) -> 'a t -> unit

Map the function on all current register. Future registers are unchanged

val iter : ('a -> unit) -> 'a t -> unit

Iterate over all the value of all currently present registers

val iteri : (reg -> 'a -> unit) -> 'a t -> unit

Same as iter but also with the register index

val bindings : 'a t -> (reg * 'a) list

Give all the registers bindings

val pp : ('a -> Utils.Pp.document) -> 'a t -> Utils.Pp.document

Contrary to Utils.FullVector.pp, this one will print the binding of all registers, and may call the generator to do that

\ No newline at end of file +Map (read-dwarf.State.Reg.Map)

Module Reg.Map

This module provide a full map over register in the same way than Utils.FullVec provide a map of integers. It still need a generator to generate the value bound to not-yet-added registers.

Because the domain of registers is finite, some extra function are available like iter and iteri that are not possible in Utils.FullVec.

If a register is added with add, it is automatically and implicitly added to the Utils.Map and the generator must accept this new value. The generator will never be called on invalid register values (i.e. when the generator is called on a register, the former can get the latter's type and name with reg_type and to_string)

type reg = t
type 'a t

The type of the complete map

val init : (reg -> 'a) -> 'a t

Initialize the map with a generator

val reinit : 'a t -> (reg -> 'a) -> unit

Clear the map and restart with this generator

val copy : 'a t -> 'a t

Make a copy of the map

val set : 'a t -> reg -> 'a -> unit

Set the value of a register

val get : 'a t -> reg -> 'a

Get the value of a register

val map : ('a -> 'b) -> 'a t -> 'b t

Map the function all the registers (including future, not yet added ones)

val mapi : (reg -> 'a -> 'b) -> 'a t -> 'b t

Same as map but with the index

val map_mut : ('a -> 'a) -> 'a t -> unit

Map the function on all the register by mutation (including future ones)

val map_mut_current : ('a -> 'a) -> 'a t -> unit

Map the function on all current register. Future registers are unchanged

val iter : ('a -> unit) -> 'a t -> unit

Iterate over all the value of all currently present registers

val iteri : (reg -> 'a -> unit) -> 'a t -> unit

Same as iter but also with the register index

val bindings : 'a t -> (reg * 'a) list

Give all the registers bindings

val pp : ('a -> Utils.Pp.document) -> 'a t -> Utils.Pp.document

Contrary to Utils.FullVector.pp, this one will print the binding of all registers, and may call the generator to do that

diff --git a/doc/html/read-dwarf/State/Reg/Path/index.html b/doc/html/read-dwarf/State/Reg/Path/index.html index 16d86748..b622f091 100644 --- a/doc/html/read-dwarf/State/Reg/Path/index.html +++ b/doc/html/read-dwarf/State/Reg/Path/index.html @@ -1,2 +1,2 @@ -Path (read-dwarf.State.Reg.Path)

Module Reg.Path

type t = string list
val to_string : t -> string

Print the path as dotted list of identifier: ["A";"B";"C"] -> "A.B.C"

val of_string : string -> t

Parse the path as dotted list of identifier: "A.B.C" -> ["A";"B";"C"]

val pp : t -> Utils.Pp.document

Pretty print the path

\ No newline at end of file +Path (read-dwarf.State.Reg.Path)

Module Reg.Path

type t = string list
val to_string : t -> string

Print the path as dotted list of identifier: ["A";"B";"C"] -> "A.B.C"

val of_string : string -> t

Parse the path as dotted list of identifier: "A.B.C" -> ["A";"B";"C"]

val pp : t -> Utils.Pp.document

Pretty print the path

diff --git a/doc/html/read-dwarf/State/Reg/index.html b/doc/html/read-dwarf/State/Reg/index.html index b5afb465..b29db887 100644 --- a/doc/html/read-dwarf/State/Reg/index.html +++ b/doc/html/read-dwarf/State/Reg/index.html @@ -1,2 +1,2 @@ -Reg (read-dwarf.State.Reg)

Module State.Reg

This module handle the register abstraction.

A register is defined by a Path and a type ty. The path is a representation of dot separated list of identifiers.

Registers are not part of the Arch module because they are discovered dynamically. This module keeps a global index of all register of the current architecture (in a 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)

Paths

Representation of register path. The string reprensentation is with dots. A name may not contain dots, but this is not checked.

module Path : sig ... end

Registers

Global register properties and accessors

type t = private int

The type representing a register. The module invariant is that this type always contains a value bound in the global index and so this is always a valid register id.

type ty = Ast.no Ast.ty

The type of a register. This is isomorphic to Isla.ty. Use IslaConv.ty to convert

val of_int : int -> t option

Convert an integer into the corresponding register.

val mem_path : Path.t -> bool

Check if register is declared with that path

val mem_string : string -> bool

Check if a register is declared with that name. Same as Path.of_string |> mem_path

val of_path : Path.t -> t

Give the register corresponding to that path

val to_path : t -> Path.t

Give the path of a register

val of_string : string -> t

Give the register corresponding to a register name

val to_string : t -> string

Give the name of a register

val num : unit -> int

Give the current number of registers

val reg_type : t -> ty

Give the type of a register

val path_type : Path.t -> ty

Give the type of register path. Throw Not_found, it that path is not a declared register

val add : Path.t -> ty -> t

Add a new register to the global index. Return it's representation

val ensure_add : Path.t -> ty -> t

Ensure that a register with that path exists with that type, by adding it or checking it already exists with that type. Return the corresponding register

val adds : Path.t -> ty -> unit

Same as add but returns unit

val ensure_adds : Path.t -> ty -> unit

Ensure that a register with that path exists with that type, by adding it or checking it already exists with that type.

val iter : (Path.t -> t -> ty -> unit) -> unit

Run a function over all registers

val seq_all : unit -> t Utils.Seq.t

Returns a sequence of all registers

val (=) : t -> t -> bool

Equality predicate

val (<>) : t -> t -> bool

Inequality predicate

val compare : t -> t -> int

Compare according to an implementation-defined total order.

val pp : t -> Utils.Pp.document

Pretty prints a register (Just use to_string)

val pp_ty : ty -> Utils.Pp.document

Pretty prints a register type

val pp_index : unit -> Utils.Pp.document

Prints the register index (mainly for debugging I suppose)

Register map

To achieve a partial map on register, one may just used a plain Hashtbl. However as register is a finite type one may want to have a map where all the register are bound and thus access to a bound value cannot fail. This is complicated by the fact that new registers can be added after the creation of the map. To handle all those subtleties, there is the Utils.Map module.

module Map : sig ... end

This module provide a full map over register in the same way than FullVec provide a map of integers. It still need a generator to generate the value bound to not-yet-added registers.

\ No newline at end of file +Reg (read-dwarf.State.Reg)

Module State.Reg

This module handle the register abstraction.

A register is defined by a Path and a type ty. The path is a representation of dot separated list of identifiers.

Registers are not part of the Arch module because they are discovered dynamically. This module keeps a global index of all register of the current architecture (in a Utils.IdMap). This map also fix the types of registers.

This allow to represent registers as integer everywhere.

The module also provides Map and PMap a respectively full and partial maps over registers. They need special support (especially the full map) because new registers may be added at any time after the creation of the map.

TODO: Support sail vectors (A path will be of type (string + int) list)

Paths

Representation of register path. The string reprensentation is with dots. A name may not contain dots, but this is not checked.

module Path : sig ... end

Registers

Global register properties and accessors

type t = private int

The type representing a register. The module invariant is that this type always contains a value bound in the global index and so this is always a valid register id.

type ty = Ast.no Ast.ty

The type of a register. This is isomorphic to Isla.ty. Use IslaConv.ty to convert

val of_int : int -> t option

Convert an integer into the corresponding register.

val mem_path : Path.t -> bool

Check if register is declared with that path

val mem_string : string -> bool

Check if a register is declared with that name. Same as Path.of_string |> mem_path

val of_path : Path.t -> t

Give the register corresponding to that path

val to_path : t -> Path.t

Give the path of a register

val of_string : string -> t

Give the register corresponding to a register name

val to_string : t -> string

Give the name of a register

val num : unit -> int

Give the current number of registers

val reg_type : t -> ty

Give the type of a register

val path_type : Path.t -> ty

Give the type of register path. Throw Not_found, it that path is not a declared register

val add : Path.t -> ty -> t

Add a new register to the global index. Return it's representation

val ensure_add : Path.t -> ty -> t

Ensure that a register with that path exists with that type, by adding it or checking it already exists with that type. Return the corresponding register

val adds : Path.t -> ty -> unit

Same as add but returns unit

val ensure_adds : Path.t -> ty -> unit

Ensure that a register with that path exists with that type, by adding it or checking it already exists with that type.

val iter : (Path.t -> t -> ty -> unit) -> unit

Run a function over all registers

val seq_all : unit -> t Utils.Seq.t

Returns a sequence of all registers

val (=) : t -> t -> bool

Equality predicate

val (<>) : t -> t -> bool

Inequality predicate

val compare : t -> t -> int

Compare according to an implementation-defined total order.

val pp : t -> Utils.Pp.document

Pretty prints a register (Just use to_string)

val pp_ty : ty -> Utils.Pp.document

Pretty prints a register type

val pp_index : unit -> Utils.Pp.document

Prints the register index (mainly for debugging I suppose)

Register map

To achieve a partial map on register, one may just used a plain Hashtbl. However as register is a finite type one may want to have a map where all the register are bound and thus access to a bound value cannot fail. This is complicated by the fact that new registers can be added after the creation of the map. To handle all those subtleties, there is the Utils.Map module.

module Map : sig ... end

This module provide a full map over register in the same way than Utils.FullVec provide a map of integers. It still need a generator to generate the value bound to not-yet-added registers.

diff --git a/doc/html/read-dwarf/State/Simplify/ContextFull/index.html b/doc/html/read-dwarf/State/Simplify/ContextFull/index.html index 21566bbf..6a4b8d4c 100644 --- a/doc/html/read-dwarf/State/Simplify/ContextFull/index.html +++ b/doc/html/read-dwarf/State/Simplify/ContextFull/index.html @@ -1,2 +1,2 @@ -ContextFull (read-dwarf.State.Simplify.ContextFull)

Module Simplify.ContextFull

Do a context aware simplify, for now, it just does a context free simplify and then remove assertion that proven true or false by Z3

val counter : Utils.Counter.t
val openc : unit -> unit
val num : unit -> int
val closec : unit -> unit
\ No newline at end of file +ContextFull (read-dwarf.State.Simplify.ContextFull)

Module Simplify.ContextFull

Do a context aware simplify, for now, it just does a context free simplify and then remove assertion that proven true or false by Z3

val counter : Utils.Counter.t
val openc : unit -> unit
val num : unit -> int
val closec : unit -> unit
diff --git a/doc/html/read-dwarf/State/Simplify/Z3St/Htbl/index.html b/doc/html/read-dwarf/State/Simplify/Z3St/Htbl/index.html index f5f52eda..b7972655 100644 --- a/doc/html/read-dwarf/State/Simplify/Z3St/Htbl/index.html +++ b/doc/html/read-dwarf/State/Simplify/Z3St/Htbl/index.html @@ -1,2 +1,2 @@ -Htbl (read-dwarf.State.Simplify.Z3St.Htbl)

Module Z3St.Htbl

type key = var
type 'a t = 'a Z3.Make(State__.Base.Var).Htbl.t
val create : int -> 'a t
val clear : 'a t -> unit
val reset : 'a t -> unit
val copy : 'a t -> 'a t
val add : 'a t -> key -> 'a -> unit
val remove : 'a t -> key -> unit
val find : 'a t -> key -> 'a
val find_opt : 'a t -> key -> 'a option
val find_all : 'a t -> key -> 'a list
val replace : 'a t -> key -> 'a -> unit
val mem : 'a t -> key -> bool
val iter : (key -> 'a -> unit) -> 'a t -> unit
val filter_map_inplace : (key -> 'a -> 'a option) -> 'a t -> unit
val fold : (key -> 'a -> 'b -> 'b) -> 'a t -> 'b -> 'b
val length : 'a t -> int
val stats : 'a t -> Stdlib.Hashtbl.statistics
val to_seq : 'a t -> (key * 'a) Stdlib.Seq.t
val to_seq_keys : 'a t -> key Stdlib.Seq.t
val to_seq_values : 'a t -> 'a Stdlib.Seq.t
val add_seq : 'a t -> (key * 'a) Stdlib.Seq.t -> unit
val replace_seq : 'a t -> (key * 'a) Stdlib.Seq.t -> unit
val of_seq : (key * 'a) Stdlib.Seq.t -> 'a t
\ No newline at end of file +Htbl (read-dwarf.State.Simplify.Z3St.Htbl)

Module Z3St.Htbl

type key = var
type !'a t = 'a Z3.Make(State__.Base.Var).Htbl.t
val create : int -> 'a t
val clear : 'a t -> unit
val reset : 'a t -> unit
val copy : 'a t -> 'a t
val add : 'a t -> key -> 'a -> unit
val remove : 'a t -> key -> unit
val find : 'a t -> key -> 'a
val find_opt : 'a t -> key -> 'a option
val find_all : 'a t -> key -> 'a list
val replace : 'a t -> key -> 'a -> unit
val mem : 'a t -> key -> bool
val iter : (key -> 'a -> unit) -> 'a t -> unit
val filter_map_inplace : (key -> 'a -> 'a option) -> 'a t -> unit
val fold : (key -> 'a -> 'acc -> 'acc) -> 'a t -> 'acc -> 'acc
val length : 'a t -> int
val stats : 'a t -> Stdlib__Hashtbl.statistics
val to_seq : 'a t -> (key * 'a) Stdlib.Seq.t
val to_seq_keys : 'a t -> key Stdlib.Seq.t
val to_seq_values : 'a t -> 'a Stdlib.Seq.t
val add_seq : 'a t -> (key * 'a) Stdlib.Seq.t -> unit
val replace_seq : 'a t -> (key * 'a) Stdlib.Seq.t -> unit
val of_seq : (key * 'a) Stdlib.Seq.t -> 'a t
diff --git a/doc/html/read-dwarf/State/Simplify/Z3St/index.html b/doc/html/read-dwarf/State/Simplify/Z3St/index.html index 4ca8fdd1..71df6e7e 100644 --- a/doc/html/read-dwarf/State/Simplify/Z3St/index.html +++ b/doc/html/read-dwarf/State/Simplify/Z3St/index.html @@ -1,2 +1,2 @@ -Z3St (read-dwarf.State.Simplify.Z3St)

Module Simplify.Z3St

type var = Base.Var.t
type exp = (varAst.no) Exp.Typed.t
module Htbl : sig ... end
val declare_var_always : Z3.server -> var -> unit
val declare_var : Z3.server -> declared:unit Htbl.t -> var -> unit
val declare_vars : Z3.server -> declared:unit Htbl.t -> exp -> unit
val simplify : Z3.server -> exp -> exp
val simplify_decl : Z3.server -> declared:unit Htbl.t -> exp -> exp
val send_assert : Z3.server -> exp -> unit
val send_assert_decl : Z3.server -> declared:unit Htbl.t -> exp -> unit
val check : Z3.server -> exp -> bool option
val check_sat : Z3.server -> exp -> bool option
val check_both : Z3.server -> exp -> bool option
val simplify_full : exp -> exp
val check_full : ?⁠hyps:exp list -> exp -> bool option
val check_sat_full : exp list -> bool option
\ No newline at end of file +Z3St (read-dwarf.State.Simplify.Z3St)

Module Simplify.Z3St

type var = Base.Var.t
type exp = (var, Ast.no) Exp.Typed.t
module Htbl : sig ... end
val declare_var_always : Z3.server -> var -> unit
val declare_var : Z3.server -> declared:unit Htbl.t -> var -> unit
val declare_vars : Z3.server -> declared:unit Htbl.t -> exp -> unit
val simplify : Z3.server -> exp -> exp
val simplify_decl : Z3.server -> declared:unit Htbl.t -> exp -> exp
val send_assert : Z3.server -> exp -> unit
val send_assert_decl : Z3.server -> declared:unit Htbl.t -> exp -> unit
val check : Z3.server -> exp -> bool option
val check_sat : Z3.server -> exp -> bool option
val check_both : Z3.server -> exp -> bool option
val simplify_subterms : Z3.server -> exp -> exp
val simplify_subterms_decl : Z3.server -> declared:unit Htbl.t -> exp -> exp
val simplify_full : exp -> exp
val check_full : ?hyps:exp list -> exp -> bool option
val check_sat_full : exp list -> bool option
val simplify_subterms_full : ?hyps:exp list -> exp -> exp
diff --git a/doc/html/read-dwarf/State/Simplify/index.html b/doc/html/read-dwarf/State/Simplify/index.html index d1183674..6bc18d77 100644 --- a/doc/html/read-dwarf/State/Simplify/index.html +++ b/doc/html/read-dwarf/State/Simplify/index.html @@ -1,2 +1,2 @@ -Simplify (read-dwarf.State.Simplify)

Module State.Simplify

This module provide utility to simplify states

module Z3St : sig ... end
val ctxfree : Base.t -> unit

Context free simplify, just expression by expression. Do it mutably

module ContextFull : sig ... end

Do a context aware simplify, for now, it just does a context free simplify and then remove assertion that proven true or false by Z3

val ctxfull : Base.t -> unit

Do a context aware simplify, for now, it just does a context free simplify and then remove assertion that proven true or false by Z3.

If a state has a false assertion anywhere then all assertions will collapse in a single false.

\ No newline at end of file +Simplify (read-dwarf.State.Simplify)

Module State.Simplify

This module provide utility to simplify states

module Z3St : sig ... end
val ctxfree : Base.t -> unit

Context free simplify, just expression by expression. Do it mutably

module ContextFull : sig ... end

Do a context aware simplify, for now, it just does a context free simplify and then remove assertion that proven true or false by Z3

val ctxfull : Base.t -> unit

Do a context aware simplify, for now, it just does a context free simplify and then remove assertion that proven true or false by Z3.

If a state has a false assertion anywhere then all assertions will collapse in a single false.

diff --git a/doc/html/read-dwarf/State/SymbolicBytes/Make/argument-1-Var/index.html b/doc/html/read-dwarf/State/SymbolicBytes/Make/argument-1-Var/index.html index 94ab0a60..04363ebb 100644 --- a/doc/html/read-dwarf/State/SymbolicBytes/Make/argument-1-Var/index.html +++ b/doc/html/read-dwarf/State/SymbolicBytes/Make/argument-1-Var/index.html @@ -1,2 +1,2 @@ -1-Var (read-dwarf.State.SymbolicBytes.Make.1-Var)

Parameter Make.1-Var

type t

The type of variables

val equal : t -> t -> bool

Equality predicate that will be passed to expressions

val pp : t -> Utils.Pp.document

Pretty printer to be used, both for memory pretty printing and for sending memory to Z3

val ty : t -> Exp.ty

Get the type of the variable

\ No newline at end of file +Var (read-dwarf.State.SymbolicBytes.Make.Var)

Parameter Make.Var

type t

The type of variables

val equal : t -> t -> bool

Equality predicate that will be passed to expressions

val pp : t -> Utils.Pp.document

Pretty printer to be used, both for memory pretty printing and for sending memory to Z3

val ty : t -> Exp.ty

Get the type of the variable

diff --git a/doc/html/read-dwarf/State/SymbolicBytes/Make/index.html b/doc/html/read-dwarf/State/SymbolicBytes/Make/index.html index fa1465bd..851e664c 100644 --- a/doc/html/read-dwarf/State/SymbolicBytes/Make/index.html +++ b/doc/html/read-dwarf/State/SymbolicBytes/Make/index.html @@ -1,2 +1,2 @@ -Make (read-dwarf.State.SymbolicBytes.Make)

Module SymbolicBytes.Make

Parameters

Signature

type var = Var.t

The type of variables used

type exp = (varAst.no) Exp.Typed.t
type t

The type of symbolic bytes.

val empty : t

The empty symbolic byte sequence with no bytes defined

val sub : pos:int -> len:int -> t -> exp option

Extract an expression in [pos:pos+len). If any bytes in the range is undefined, Then None is returned, otherwise a expression is returned

val blit_exp : exp -> pos:int -> len:int -> t -> t

Write the expresion on the interval [pos:pos +len) of the bytes. The expression must be a bitvector of size exactly 8 * len.

TODO check it when values are type annotated

val clear_bounds : ?⁠start:int -> ?⁠endp:int -> t -> t

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 map_exp : (exp -> exp) -> t -> t

Map a function over all the contained expressions

val iter_exp : (exp -> unit) -> t -> unit

Iter a function over all the contained expressions

val pp : t -> Utils.Pp.document

Pretty prints the symbolic bytes

\ No newline at end of file +Make (read-dwarf.State.SymbolicBytes.Make)

Module SymbolicBytes.Make

Parameters

module Var : Exp.Var

Signature

type var = Var.t

The type of variables used

type exp = (var, Ast.no) Exp.Typed.t
type t

The type of symbolic bytes.

val empty : t

The empty symbolic byte sequence with no bytes defined

val sub : pos:int -> len:int -> t -> exp option

Extract an expression in [pos:pos+len). If any bytes in the range is undefined, Then None is returned, otherwise a expression is returned

val blit_exp : exp -> pos:int -> len:int -> t -> t

Write the expresion on the interval [pos:pos +len) of the bytes. The expression must be a bitvector of size exactly 8 * len.

TODO check it when values are type annotated

val clear_bounds : ?start:int -> ?endp:int -> t -> t

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 map_exp : (exp -> exp) -> t -> t

Map a function over all the contained expressions

val iter_exp : (exp -> unit) -> t -> unit

Iter a function over all the contained expressions

val pp : t -> Utils.Pp.document

Pretty prints the symbolic bytes

diff --git a/doc/html/read-dwarf/State/SymbolicBytes/index.html b/doc/html/read-dwarf/State/SymbolicBytes/index.html index 21e8f692..b3e2493a 100644 --- a/doc/html/read-dwarf/State/SymbolicBytes/index.html +++ b/doc/html/read-dwarf/State/SymbolicBytes/index.html @@ -1,2 +1,2 @@ -SymbolicBytes (read-dwarf.State.SymbolicBytes)

Module State.SymbolicBytes

This module provide high-level support for a symbolic array of bytes.

The main differences between this and a big concat expression is that:

  • It allows some bytes to be undefined
  • It allows fast (logarithmic or better) access to any byte or group of bytes.
  • It provides dedicated functions for efficiently extracting an expression from a range of bytes (reading) or blitting (writing) an expression to a range of bytes.
  • The indexing can start at any point (negative value can be mapped) without any costs

This data structure deliberately do not have any infrastructure to read or write bytes at symbolic positions. See SymbolicFragment for that.

This data structure do not have a concept of beginning or an end (But it has the concept of first defined byte and last defined byte). In particular addresses can be negative.

It is functorized of the type of variables (Var) to get variable equality and pretty-printing

Currently this has a pure interface.

module type S = sig ... end
module Make : functor (Var : Exp.Var) -> S with type var = Var.t
\ No newline at end of file +SymbolicBytes (read-dwarf.State.SymbolicBytes)

Module State.SymbolicBytes

This module provide high-level support for a symbolic array of bytes.

The main differences between this and a big concat expression is that:

  • It allows some bytes to be undefined
  • It allows fast (logarithmic or better) access to any byte or group of bytes.
  • It provides dedicated functions for efficiently extracting an expression from a range of bytes (reading) or blitting (writing) an expression to a range of bytes.
  • The indexing can start at any point (negative value can be mapped) without any costs

This data structure deliberately do not have any infrastructure to read or write bytes at symbolic positions. See SymbolicFragment for that.

This data structure do not have a concept of beginning or an end (But it has the concept of first defined byte and last defined byte). In particular addresses can be negative.

It is functorized of the type of variables (Var) to get variable equality and pretty-printing

Currently this has a pure interface.

module type S = sig ... end
module Make (Var : Exp.Var) : S with type var = Var.t
diff --git a/doc/html/read-dwarf/State/SymbolicBytes/module-type-S/index.html b/doc/html/read-dwarf/State/SymbolicBytes/module-type-S/index.html index 53b8ad9e..a49dbda4 100644 --- a/doc/html/read-dwarf/State/SymbolicBytes/module-type-S/index.html +++ b/doc/html/read-dwarf/State/SymbolicBytes/module-type-S/index.html @@ -1,2 +1,2 @@ -S (read-dwarf.State.SymbolicBytes.S)

Module type SymbolicBytes.S

type var

The type of variables used

type exp = (varAst.no) Exp.Typed.t
type t

The type of symbolic bytes.

val empty : t

The empty symbolic byte sequence with no bytes defined

val sub : pos:int -> len:int -> t -> exp option

Extract an expression in [pos:pos+len). If any bytes in the range is undefined, Then None is returned, otherwise a expression is returned

val blit_exp : exp -> pos:int -> len:int -> t -> t

Write the expresion on the interval [pos:pos +len) of the bytes. The expression must be a bitvector of size exactly 8 * len.

TODO check it when values are type annotated

val clear_bounds : ?⁠start:int -> ?⁠endp:int -> t -> t

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 map_exp : (exp -> exp) -> t -> t

Map a function over all the contained expressions

val iter_exp : (exp -> unit) -> t -> unit

Iter a function over all the contained expressions

val pp : t -> Utils.Pp.document

Pretty prints the symbolic bytes

\ No newline at end of file +S (read-dwarf.State.SymbolicBytes.S)

Module type SymbolicBytes.S

type var

The type of variables used

type exp = (var, Ast.no) Exp.Typed.t
type t

The type of symbolic bytes.

val empty : t

The empty symbolic byte sequence with no bytes defined

val sub : pos:int -> len:int -> t -> exp option

Extract an expression in [pos:pos+len). If any bytes in the range is undefined, Then None is returned, otherwise a expression is returned

val blit_exp : exp -> pos:int -> len:int -> t -> t

Write the expresion on the interval [pos:pos +len) of the bytes. The expression must be a bitvector of size exactly 8 * len.

TODO check it when values are type annotated

val clear_bounds : ?start:int -> ?endp:int -> t -> t

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 map_exp : (exp -> exp) -> t -> t

Map a function over all the contained expressions

val iter_exp : (exp -> unit) -> t -> unit

Iter a function over all the contained expressions

val pp : t -> Utils.Pp.document

Pretty prints the symbolic bytes

diff --git a/doc/html/read-dwarf/State/Tree/index.html b/doc/html/read-dwarf/State/Tree/index.html index a34d86a7..2f935422 100644 --- a/doc/html/read-dwarf/State/Tree/index.html +++ b/doc/html/read-dwarf/State/Tree/index.html @@ -1,2 +1,2 @@ -Tree (read-dwarf.State.Tree)

Module State.Tree

This module provides a tree of state to represent an unmerged execution

type 'a t = {
state : Base.t;
data : 'a;
rest : 'a t list;
}
val bars : string
val bars_length : int
val startbar : int -> Utils.Pp.document
val prefix_iter : ('a -> Base.t -> unit) -> 'a t -> unit
val postfix_iter : ('a -> Base.t -> unit) -> 'a t -> unit
val iter : ('a -> Base.t -> unit) -> 'a t -> unit

Default iter when you don't care about order

val map_to_list : ('a -> Base.t -> 'b) -> 'a t -> 'b list

This is prefix, do a List.rev to get a postfix version

val pp : ('a -> Utils.Pp.document) -> 'a t -> Utils.Pp.document
val pp_all : ('a -> Utils.Pp.document) -> 'a t -> Utils.Pp.document
\ No newline at end of file +Tree (read-dwarf.State.Tree)

Module State.Tree

This module provides a tree of state to represent an unmerged execution

type 'a t = {
  1. state : Base.t;
  2. data : 'a;
  3. rest : 'a t list;
}
val bars : string
val bars_length : int
val startbar : int -> Utils.Pp.document
val prefix_iter : ('a -> Base.t -> unit) -> 'a t -> unit
val postfix_iter : ('a -> Base.t -> unit) -> 'a t -> unit
val iter : ('a -> Base.t -> unit) -> 'a t -> unit

Default iter when you don't care about order

val map_to_list : ('a -> Base.t -> 'b) -> 'a t -> 'b list

This is prefix, do a List.rev to get a postfix version

val pp : ('a -> Utils.Pp.document) -> 'a t -> Utils.Pp.document
val pp_all : ('a -> Utils.Pp.document) -> 'a t -> Utils.Pp.document
diff --git a/doc/html/read-dwarf/State/index.html b/doc/html/read-dwarf/State/index.html index 1cbee381..e2a71c84 100644 --- a/doc/html/read-dwarf/State/index.html +++ b/doc/html/read-dwarf/State/index.html @@ -1,2 +1,14 @@ -State (read-dwarf.State)

Module State

include Base

State id

module Id = Base.Id

The type of a state ID. for now it's an integer, but it may change later In particular whether a state belong to O0 or O2 may be part of the id at some point.

type id = Id.t

State variable management

module Var = Base.Var

This module provide state variables. Those are all symbolic variables that may appear in a state. If the same (in the Var.equal sense) variable appear in two state, that mean that when considered together, there is an implicit relation between them.

type var = Var.t

The type of variables

State expression and typed-value management

module Exp = Base.Exp

Module for state expressions

type exp = Exp.t
module Tval = Base.Tval

Module for optionally typed state expressions. Those are symbolic values which may or may not have a C type.

type tval = Tval.t

State memory management

module Mem = Base.Mem

This module manages the memory part of the state.

State type

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 .rodata). It will affect the execution behavior. However the symbolic execution should always be more concrete with it than without it

fenv : Fragment.env;

The memory type environment. See Fragment.env

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.

val equal : t -> t -> bool

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:

  • The id can be serialized and read to external program and files without losing it's meaning.
  • The id allow to break cyclical type dependency.

This is in particular useful for variable that contain and id instead of a pointer to the state:

  • The variable can be serialized in text manner to a SMT solver and keep their meaning.
  • The 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 : (idt) Utils.WeakMap.t

Global map of states to associate them with identifiers

val next_id : id Stdlib.ref

Next unused id

val of_id : id -> t

Get a state from its id

val to_id : t -> id

Get the id of a state

State management

val lock : t -> unit

Lock the state. Once a state is locked it should not be mutated anymore

val unsafe_unlock : t -> unit

Unlock the state. This is dangerous, do not use if you do not know how to use it, The only realistic use case, is for calling a simplifier and thus not changing the semantic meaning of the state in any way while mutating it.

This is deprecated and should disappear at some point.

val is_locked : t -> bool

Tell if the state is locked, in which case it shouldn't be mutated

val is_possible : t -> bool

Tell is state is possible.

A state is impossible if it has a single assert that is false. This means that this symbolic state represent the empty set of concrete states.

StateSimplify.ctxfull will call the SMT solver and set the assertions to that if required so you should call that function before is_possible

val make : ?⁠elf:Elf.File.t -> unit -> t

Makes a fresh state with all variable fresh and new. This fresh state is unlocked.

This should only be used by Init, all other state should be derived from Init.state.

val copy : ?⁠elf:Elf.File.t -> t -> t

Do a deep copy of all the mutable part of the state, so it can be mutated without retro-action.

If the source state is locked, then new state is based on it (in the sense of t.base_state), otherwise it is a literal copy of each field.

The returned state is always unlocked

val copy_if_locked : ?⁠elf:Elf.File.t -> t -> t

Copy the state with copy if and only if it is locked. The returned state is always unlocked

State convenience manipulation

val push_assert : t -> exp -> unit

Add an assertion to a state

val set_impossible : t -> unit

Set a state to be impossible (single false assert).

val set_asserts : t -> exp list -> unit

Set a state's asserts

val map_mut_exp : (exp -> exp) -> t -> unit

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 iter_exp : (exp -> unit) -> t -> unit

Iterates a function on all the expressions of a state

val iter_var : (var -> unit) -> t -> unit

Iterates a function on all the variables of a state

State memory accessors

val make_read : t -> ?⁠ctyp:Ctype.t -> Ast.Size.t -> var

Create a new Var.t.ReadVar by mutating the state

val set_read : t -> int -> exp -> unit

Set a Var.t.ReadVar to a specific value in t.read_vars

val read_from_rodata : t -> addr:exp -> size:Ast.Size.t -> exp option

Read memory from rodata

val read : provenance:Ctype.provenance -> ?⁠ctyp:Ctype.t -> t -> addr:exp -> size:Ast.Size.t -> exp

Read the block designated by addr and size from the state and return an expression read. This will mutate the state to bind the read result to the newly created read variable.

The ctyp parameter may give a type to the read variable. This type is fully trusted and not checked in any way.

The expression could be either:

  • An actual expression if the read could be resolved.
  • Just the symbolic read variable if the read couldn't be resolved

This function is for case with provenance information is known.

val read_noprov : ?⁠ctyp:Ctype.t -> t -> addr:exp -> size:Ast.Size.t -> exp

A wrapper around read for use when there is no provenance information. It may able to still perform the read under certain condition and otherwise will fail.

val write : provenance:Ctype.provenance -> t -> addr:exp -> size:Ast.Size.t -> exp -> unit

Write the provided value in the block. Mutate the state.

val write_noprov : t -> addr:exp -> size:Ast.Size.t -> exp -> unit

A wrapper around write for use when there is no provenance information. It may able to still perform the write under certain condition and otherwise will fail.

State register accessors

val reset_reg : t -> ?⁠ctyp:Ctype.t -> Reg.t -> unit

Reset the register to a symbolic value, and resets the type to the provided type (or no type if not provided)

val set_reg : t -> Reg.t -> tval -> unit

Sets the content of register

val set_reg_type : t -> Reg.t -> Ctype.t -> unit

Sets the type of the register, leaves the value unchanged

val get_reg : t -> Reg.t -> tval

Get the content of the register with it's type

val get_reg_exp : t -> Reg.t -> exp

Get the content of the register without it's type

val update_reg_exp : t -> Reg.t -> (exp -> exp) -> unit

Apply a function to a register. Leave the type intact

Pc manipulation

val set_pc : pc:Reg.t -> t -> int -> unit

Set the PC to a concrete value and keep its type appropriate

val bump_pc : pc:Reg.t -> t -> int -> unit

Bump a concrete PC by a concrete bump (generally the size of a non-branching instruction

val concretize_pc : pc:Reg.t -> t -> unit

Try to evaluate the PC if it is concrete

val set_last_pc : t -> int -> unit

Set the last_pc of the state

Pretty printing

val pp : t -> PPrintEngine.document
val pp_partial : regs:Reg.t list -> t -> PPrintEngine.document

Print only the mentioned regs and the memory and asserts since the base_state. Until a better solution is found, the fenv will be printed entirely all the time

module Base : sig ... end

This module introduce a type to represent the state of the machine.

module Fragment : sig ... end

This module define a memory fragment to be used by C types

module Reg : sig ... end

This module handle the register abstraction.

module Simplify : sig ... end

This module provide utility to simplify states

module SymbolicBytes : sig ... end

This module provide high-level support for a symbolic array of bytes.

module SymbolicFragement = State__.SymbolicFragment
module Tree : sig ... end

This module provides a tree of state to represent an unmerged execution

\ No newline at end of file +State (read-dwarf.State)

Module State

include module type of struct include Base end

State id

module Id = Base.Id

The type of a state ID. for now it's an integer, but it may change later In particular whether a state belong to O0 or O2 may be part of the id at some point.

type id = Id.t

State variable management

module Var = Base.Var

This module provide state variables. Those are all symbolic variables that may appear in a state. If the same (in the Var.equal sense) variable appear in two state, that mean that when considered together, there is an implicit relation between them.

type var = Var.t

The type of variables

State expression and typed-value management

module Exp = Base.Exp

Module for state expressions

type exp = Exp.t
module Tval = Base.Tval

Module for optionally typed state expressions. Those are symbolic values which may or may not have a C type.

type tval = Tval.t
module Relocation = Base.Relocation

State memory management

module Mem = Base.Mem

This module manages the memory part of the state.

State type

type t = private Base.t = {
  1. id : id;
  2. base_state : t option;
    (*

    The immediate dominator state in the control flow graph

    *)
  3. mutable locked : bool;
    (*

    Tells if the state is locked

    *)
  4. mutable regs : Tval.t Reg.Map.t;
    (*

    The values and types of registers

    *)
  5. read_vars : Tval.t Utils.Vec.t;
    (*

    The results of reads made since base state

    *)
  6. mutable asserts : exp list;
    (*

    Only asserts since base_state

    *)
  7. mutable relocation_asserts : exp list;
    (*

    Only asserts since base_state

    *)
  8. mem : Mem.t;
  9. 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

    *)
  10. fenv : Fragment.env;
    (*

    The memory type environment. See Fragment.env

    *)
  11. 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.

val equal : t -> t -> bool

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:

  • The id can be serialized and read to external program and files without losing it's meaning.
  • The id allow to break cyclical type dependency.

This is in particular useful for variable that contain and id instead of a pointer to the state:

  • The variable can be serialized in text manner to a SMT solver and keep their meaning.
  • The Var.t type can be defined before the t type.

That means that in theory a state could be Garbage collected while a variable still point to it. However a variable is only allowed to exists in a state if the id it points to is among the ancestors via the base_state relationship of the containing state. Since base_state is an GC-owning pointer, this ensure that while the containing state is alive, the variable target state is also alive.

val id2state : (id, t) Utils.WeakMap.t

Global map of states to associate them with identifiers

val next_id : id Stdlib.ref

Next unused id

val of_id : id -> t

Get a state from its id

val to_id : t -> id

Get the id of a state

State management

val lock : t -> unit

Lock the state. Once a state is locked it should not be mutated anymore

val unsafe_unlock : t -> unit

Unlock the state. This is dangerous, do not use if you do not know how to use it, The only realistic use case, is for calling a simplifier and thus not changing the semantic meaning of the state in any way while mutating it.

This is deprecated and should disappear at some point.

  • deprecated Stop unlocking states
val is_locked : t -> bool

Tell if the state is locked, in which case it shouldn't be mutated

val is_possible : t -> bool

Tell is state is possible.

A state is impossible if it has a single assert that is false. This means that this symbolic state represent the empty set of concrete states.

StateSimplify.ctxfull will call the SMT solver and set the assertions to that if required so you should call that function before is_possible

val make : ?elf:Elf.File.t -> unit -> t

Makes a fresh state with all variable fresh and new. This fresh state is unlocked.

This should only be used by Init, all other state should be derived from Init.state.

val copy : ?elf:Elf.File.t -> t -> t

Do a deep copy of all the mutable part of the state, so it can be mutated without retro-action.

If the source state is locked, then new state is based on it (in the sense of t.base_state), otherwise it is a literal copy of each field.

The returned state is always unlocked

val copy_if_locked : ?elf:Elf.File.t -> t -> t

Copy the state with copy if and only if it is locked. The returned state is always unlocked

val init_sections : sp:(unit -> Reg.t) -> addr_size:int -> t -> t
val init_sections_symbolic : sp:(unit -> Reg.t) -> addr_size:int -> t -> t

Assigns all sections with global objects to Main fragment

State convenience manipulation

val push_assert : t -> exp -> unit

Add an assertion to a state

val push_relocation_assert : t -> exp -> unit

Add an assertion to a state

val set_impossible : t -> unit

Set a state to be impossible (single false assert).

val set_asserts : t -> exp list -> unit

Set a state's asserts

val map_mut_exp : (exp -> exp) -> t -> unit

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 iter_exp : (exp -> unit) -> t -> unit

Iterates a function on all the expressions of a state

val iter_var : (var -> unit) -> t -> unit

Iterates a function on all the variables of a state

State memory accessors

val make_read : t -> ?ctyp:Ctype.t -> Ast.Size.t -> var

Create a new Var.t.ReadVar by mutating the state

val set_read : t -> int -> exp -> unit

Set a Var.t.ReadVar to a specific value in t.read_vars

val read_from_rodata : t -> addr:exp -> size:Ast.Size.t -> exp option

Read memory from rodata

val read : + provenance:Ctype.provenance -> + ?ctyp:Ctype.t -> + t -> + addr:exp -> + size:Ast.Size.t -> + exp

Read the block designated by addr and size from the state and return an expression read. This will mutate the state to bind the read result to the newly created read variable.

The ctyp parameter may give a type to the read variable. This type is fully trusted and not checked in any way.

The expression could be either:

  • An actual expression if the read could be resolved.
  • Just the symbolic read variable if the read couldn't be resolved

This function is for case with provenance information is known.

val read_noprov : ?ctyp:Ctype.t -> t -> addr:exp -> size:Ast.Size.t -> exp

A wrapper around read for use when there is no provenance information. It may able to still perform the read under certain condition and otherwise will fail.

val write : + provenance:Ctype.provenance -> + t -> + addr:exp -> + size:Ast.Size.t -> + exp -> + unit

Write the provided value in the block. Mutate the state.

val write_noprov : t -> addr:exp -> size:Ast.Size.t -> exp -> unit

A wrapper around write for use when there is no provenance information. It may able to still perform the write under certain condition and otherwise will fail.

State register accessors

val reset_reg : t -> ?ctyp:Ctype.t -> Reg.t -> unit

Reset the register to a symbolic value, and resets the type to the provided type (or no type if not provided)

val set_reg : t -> Reg.t -> tval -> unit

Sets the content of register

val set_reg_type : t -> Reg.t -> Ctype.t -> unit

Sets the type of the register, leaves the value unchanged

val get_reg : t -> Reg.t -> tval

Get the content of the register with it's type

val get_reg_exp : t -> Reg.t -> exp

Get the content of the register without it's type

val update_reg_exp : t -> Reg.t -> (exp -> exp) -> unit

Apply a function to a register. Leave the type intact

Pc manipulation

val set_pc : pc:Reg.t -> t -> int -> unit

Set the PC to a concrete value and keep its type appropriate

val set_pc_sym : pc:Reg.t -> t -> Elf.Address.t -> unit
val bump_pc : pc:Reg.t -> t -> int -> unit

Bump a concrete PC by a concrete bump (generally the size of a non-branching instruction

val concretize_pc : pc:Reg.t -> t -> unit

Try to evaluate the PC if it is concrete

val set_last_pc : t -> Elf.Address.t -> unit

Set the last_pc of the state

Pretty printing

val pp : t -> Utils.Pp.document
val pp_partial : regs:Reg.t list -> t -> Utils.Pp.document

Print only the mentioned regs and the memory and asserts since the base_state. Until a better solution is found, the fenv will be printed entirely all the time

module Base : sig ... end

This module introduce a type to represent the state of the machine.

module Fragment : sig ... end

This module define a memory fragment to be used by C types

module Reg : sig ... end

This module handle the register abstraction.

module Simplify : sig ... end

This module provide utility to simplify states

module SymbolicBytes : sig ... end

This module provide high-level support for a symbolic array of bytes.

module SymbolicFragement : sig ... end

This module provides a representation of symbolic memory as both a trace and a caching mechanism That is able to fetch some actual read value when there is no risk of aliasing.

module Tree : sig ... end

This module provides a tree of state to represent an unmerged execution

diff --git a/doc/html/read-dwarf/SymbolicExecution.html b/doc/html/read-dwarf/SymbolicExecution.html index aee42b2e..43a2e807 100644 --- a/doc/html/read-dwarf/SymbolicExecution.html +++ b/doc/html/read-dwarf/SymbolicExecution.html @@ -1,2 +1,2 @@ -SymbolicExecution (read-dwarf.SymbolicExecution)

Symbolic 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.

\ No newline at end of file +SymbolicExecution (read-dwarf.SymbolicExecution)

Symbolic 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.

diff --git a/doc/html/read-dwarf/SymbolicExpressions.html b/doc/html/read-dwarf/SymbolicExpressions.html index 603979ff..67095228 100644 --- a/doc/html/read-dwarf/SymbolicExpressions.html +++ b/doc/html/read-dwarf/SymbolicExpressions.html @@ -1,2 +1,2 @@ -SymbolicExpressions (read-dwarf.SymbolicExpressions)

Symbolic Expressions

Expression representation and parametric types

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).

Build process and modules

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:

One 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.

Expression type parameter and options

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.

Operations on expressions

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.
  • And last but not least: 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.

Symbolic regions of memory

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.

\ No newline at end of file +SymbolicExpressions (read-dwarf.SymbolicExpressions)

Symbolic Expressions

Expression representation and parametric types

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).

Build process and modules

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:

One 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.

Expression type parameter and options

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.

Operations on expressions

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.
  • And last but not least: 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.

Symbolic regions of memory

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.

diff --git a/doc/html/read-dwarf/Tests/BytesSeqT/index.html b/doc/html/read-dwarf/Tests/BytesSeqT/index.html index ca788531..9eec7958 100644 --- a/doc/html/read-dwarf/Tests/BytesSeqT/index.html +++ b/doc/html/read-dwarf/Tests/BytesSeqT/index.html @@ -1,2 +1,2 @@ -BytesSeqT (read-dwarf.Tests.BytesSeqT)

Module Tests.BytesSeqT

val has_even_len : string -> bool
val hex_digit : char Tests.Common.Q.arbitrary
val hex_string : string Tests.Common.Q.arbitrary
val is_hex : char -> bool
val well_formed : Tests.Common.QCT.t
val odd_len : Tests.Common.QCT.t
val not_hex : Tests.Common.QCT.t
val tests : Tests.Common.QCT.t list
\ No newline at end of file +BytesSeqT (read-dwarf.Tests.BytesSeqT)

Module Tests.BytesSeqT

val has_even_len : string -> bool
val hex_digit : char Tests.Common.Q.arbitrary
val hex_string : string Tests.Common.Q.arbitrary
val is_hex : char -> bool
val well_formed : Tests.Common.QCT.t
val odd_len : Tests.Common.QCT.t
val not_hex : Tests.Common.QCT.t
val tests : Tests.Common.QCT.t list
diff --git a/doc/html/read-dwarf/Tests/Common/Gen/index.html b/doc/html/read-dwarf/Tests/Common/Gen/index.html index 991efcd9..e8948681 100644 --- a/doc/html/read-dwarf/Tests/Common/Gen/index.html +++ b/doc/html/read-dwarf/Tests/Common/Gen/index.html @@ -1,2 +1,43 @@ -Gen (read-dwarf.Tests.Common.Gen)

Module Common.Gen

include Q.Gen
type 'a t = Stdlib.Random.State.t -> 'a
type 'a sized = int -> Stdlib.Random.State.t -> 'a
val return : 'a -> 'a t
val pure : 'a -> 'a t
val (>>=) : 'a t -> ('a -> 'b t) -> 'b t
val (<*>) : ('a -> 'b) t -> 'a t -> 'b t
val map : ('a -> 'b) -> 'a t -> 'b t
val map2 : ('a -> 'b -> 'c) -> 'a t -> 'b t -> 'c t
val map3 : ('a -> 'b -> 'c -> 'd) -> 'a t -> 'b t -> 'c t -> 'd t
val map_keep_input : ('a -> 'b) -> 'a t -> ('a * 'b) t
val (>|=) : 'a t -> ('a -> 'b) -> 'b t
val (<$>) : ('a -> 'b) -> 'a t -> 'b t
val oneof : 'a t list -> 'a t
val oneofl : 'a list -> 'a t
val oneofa : 'a array -> 'a t
val frequency : (int * 'a t) list -> 'a t
val frequencyl : (int * 'a) list -> 'a t
val frequencya : (int * 'a) array -> 'a t
val shuffle_a : 'a array -> unit t
val shuffle_l : 'a list -> 'a list t
val shuffle_w_l : (int * 'a) list -> 'a list t
val unit : unit t
val bool : bool t
val float : float t
val pfloat : float t
val nfloat : float t
val float_bound_inclusive : float -> float t
val float_bound_exclusive : float -> float t
val float_range : float -> float -> float t
val (--.) : float -> float -> float t
val nat : int t
val big_nat : int t
val neg_int : int t
val pint : int t
val int : int t
val small_nat : int t
val small_int : int t
val small_signed_int : int t
val int_bound : int -> int t
val int_range : int -> int -> int t
val graft_corners : 'a t -> 'a list -> unit -> 'a t
val int_pos_corners : int list
val int_corners : int list
val (--) : int -> int -> int t
val ui32 : int32 t
val ui64 : int64 t
val list : 'a t -> 'a list t
val list_size : int t -> 'a t -> 'a list t
val list_repeat : int -> 'a t -> 'a list t
val array : 'a t -> 'a array t
val array_size : int t -> 'a t -> 'a array t
val array_repeat : int -> 'a t -> 'a array t
val opt : 'a t -> 'a option t
val pair : 'a t -> 'b t -> ('a * 'b) t
val triple : 'a t -> 'b t -> 'c t -> ('a * 'b * 'c) t
val quad : 'a t -> 'b t -> 'c t -> 'd t -> ('a * 'b * 'c * 'd) t
val char : char t
val printable : char t
val numeral : char t
val char_range : char -> char -> char t
val string_size : ?⁠gen:char t -> int t -> string t
val string : ?⁠gen:char t -> string t
val string_of : char t -> string t
val string_readable : string t
val small_string : ?⁠gen:char t -> string t
val small_list : 'a t -> 'a list t
val flatten_l : 'a t list -> 'a list t
val flatten_a : 'a t array -> 'a array t
val flatten_opt : 'a t option -> 'a option t
val flatten_res : ('a t'e) Stdlib.result -> ('a'e) Stdlib.result t
val small_array : 'a t -> 'a array t
val join : 'a t t -> 'a t
val sized : 'a sized -> 'a t
val sized_size : int t -> 'a sized -> 'a t
val fix : (('a -> 'b t) -> 'a -> 'b t) -> 'a -> 'b t
val generate : ?⁠rand:Stdlib.Random.State.t -> n:int -> 'a t -> 'a list
val generate1 : ?⁠rand:Stdlib.Random.State.t -> 'a t -> 'a
val let+ : 'a t -> ('a -> 'b) -> 'b t
val and+ : 'a t -> 'b t -> ('a * 'b) t
val let* : 'a t -> ('a -> 'b t) -> 'b t
val and* : 'a t -> 'b t -> ('a * 'b) t
\ No newline at end of file +Gen (read-dwarf.Tests.Common.Gen)

Module Common.Gen

include module type of struct include Q.Gen end
type !'a t = Stdlib.Random.State.t -> 'a
type !'a sized = int -> Stdlib.Random.State.t -> 'a
val return : 'a -> 'a t
val pure : 'a -> 'a t
val (>>=) : 'a t -> ('a -> 'b t) -> 'b t
val (<*>) : ('a -> 'b) t -> 'a t -> 'b t
val map : ('a -> 'b) -> 'a t -> 'b t
val map2 : ('a -> 'b -> 'c) -> 'a t -> 'b t -> 'c t
val map3 : ('a -> 'b -> 'c -> 'd) -> 'a t -> 'b t -> 'c t -> 'd t
val map4 : ('a -> 'b -> 'c -> 'd -> 'e) -> 'a t -> 'b t -> 'c t -> 'd t -> 'e t
val map5 : + ('a -> 'b -> 'c -> 'd -> 'e -> 'f) -> + 'a t -> + 'b t -> + 'c t -> + 'd t -> + 'e t -> + 'f t
val map_keep_input : ('a -> 'b) -> 'a t -> ('a * 'b) t
val (>|=) : 'a t -> ('a -> 'b) -> 'b t
val (<$>) : ('a -> 'b) -> 'a t -> 'b t
val oneof : 'a t list -> 'a t
val oneofl : 'a list -> 'a t
val oneofa : 'a array -> 'a t
val frequency : (int * 'a t) list -> 'a t
val frequencyl : (int * 'a) list -> 'a t
val frequencya : (int * 'a) array -> 'a t
val shuffle_a : 'a array -> unit t
val shuffle_l : 'a list -> 'a list t
val shuffle_w_l : (int * 'a) list -> 'a list t
val range_subset : size:int -> int -> int -> int array t
val array_subset : int -> 'a array -> 'a array t
val unit : unit t
val bool : bool t
val float : float t
val pfloat : float t
val nfloat : float t
val float_bound_inclusive : float -> float t
val float_bound_exclusive : float -> float t
val float_range : float -> float -> float t
val (--.) : float -> float -> float t
val exponential : float -> float t
val nat : int t
val big_nat : int t
val neg_int : int t
val pint : int t
val int : int t
val small_nat : int t
val small_int : int t
val small_signed_int : int t
val int_bound : int -> int t
val int_range : int -> int -> int t
val graft_corners : 'a t -> 'a list -> unit -> 'a t
val int_pos_corners : int list
val int_corners : int list
val (--) : int -> int -> int t
val int32 : int32 t
val int64 : int64 t
val ui32 : int32 t
val ui64 : int64 t
val list : 'a t -> 'a list t
val list_size : int t -> 'a t -> 'a list t
val list_repeat : int -> 'a t -> 'a list t
val array : 'a t -> 'a array t
val array_size : int t -> 'a t -> 'a array t
val array_repeat : int -> 'a t -> 'a array t
val option : ?ratio:float -> 'a t -> 'a option t
val opt : ?ratio:float -> 'a t -> 'a option t
val result : ?ratio:float -> 'a t -> 'e t -> ('a, 'e) Stdlib.result t
val char : char t
val printable : char t
val numeral : char t
val char_range : char -> char -> char t
val bytes_size : ?gen:char t -> int t -> bytes t
val bytes : ?gen:char t -> bytes t
val bytes_of : char t -> bytes t
val bytes_printable : bytes t
val bytes_small : bytes t
val bytes_small_of : char t -> bytes t
val string_size : ?gen:char t -> int t -> string t
val string : ?gen:char t -> string t
val string_of : char t -> string t
val string_readable : string t
  • deprecated see string_printable
val string_printable : string t
val small_string : ?gen:char t -> string t
val string_small : string t
val string_small_of : char t -> string t
val small_list : 'a t -> 'a list t
val flatten_l : 'a t list -> 'a list t
val flatten_a : 'a t array -> 'a array t
val flatten_opt : 'a t option -> 'a option t
val flatten_res : ('a t, 'e) Stdlib.result -> ('a, 'e) Stdlib.result t
val small_array : 'a t -> 'a array t
val pair : 'a t -> 'b t -> ('a * 'b) t
val triple : 'a t -> 'b t -> 'c t -> ('a * 'b * 'c) t
val quad : 'a t -> 'b t -> 'c t -> 'd t -> ('a * 'b * 'c * 'd) t
val tup2 : 'a t -> 'b t -> ('a * 'b) t
val tup3 : 'a t -> 'b t -> 'c t -> ('a * 'b * 'c) t
val tup4 : 'a t -> 'b t -> 'c t -> 'd t -> ('a * 'b * 'c * 'd) t
val tup5 : 'a t -> 'b t -> 'c t -> 'd t -> 'e t -> ('a * 'b * 'c * 'd * 'e) t
val tup6 : + 'a t -> + 'b t -> + 'c t -> + 'd t -> + 'e t -> + 'f t -> + ('a * 'b * 'c * 'd * 'e * 'f) t
val tup7 : + 'a t -> + 'b t -> + 'c t -> + 'd t -> + 'e t -> + 'f t -> + 'g t -> + ('a * 'b * 'c * 'd * 'e * 'f * 'g) t
val tup8 : + 'a t -> + 'b t -> + 'c t -> + 'd t -> + 'e t -> + 'f t -> + 'g t -> + 'h t -> + ('a * 'b * 'c * 'd * 'e * 'f * 'g * 'h) t
val tup9 : + 'a t -> + 'b t -> + 'c t -> + 'd t -> + 'e t -> + 'f t -> + 'g t -> + 'h t -> + 'i t -> + ('a * 'b * 'c * 'd * 'e * 'f * 'g * 'h * 'i) t
val join : 'a t t -> 'a t
val sized : 'a sized -> 'a t
val sized_size : int t -> 'a sized -> 'a t
val fix : (('a -> 'b t) -> 'a -> 'b t) -> 'a -> 'b t
val nat_split2 : int -> (int * int) t
val pos_split2 : int -> (int * int) t
val nat_split : size:int -> int -> int array t
val pos_split : size:int -> int -> int array t
val delay : (unit -> 'a t) -> 'a t
val generate : ?rand:Stdlib.Random.State.t -> n:int -> 'a t -> 'a list
val generate1 : ?rand:Stdlib.Random.State.t -> 'a t -> 'a
val (let+) : 'a t -> ('a -> 'b) -> 'b t
val (and+) : 'a t -> 'b t -> ('a * 'b) t
val (let*) : 'a t -> ('a -> 'b t) -> 'b t
val (and*) : 'a t -> 'b t -> ('a * 'b) t
diff --git a/doc/html/read-dwarf/Tests/Common/index.html b/doc/html/read-dwarf/Tests/Common/index.html index 5249c660..79333d8e 100644 --- a/doc/html/read-dwarf/Tests/Common/index.html +++ b/doc/html/read-dwarf/Tests/Common/index.html @@ -1,2 +1,2 @@ -Common (read-dwarf.Tests.Common)

Module Tests.Common

module Q = QCheck

This module provide all the common testing infrastructure. It is intended to be opened in all testing modules

module Gen : sig ... end
module QCT = Q.Test
module Print = Q.Print
val (==>) : bool -> bool -> bool
\ No newline at end of file +Common (read-dwarf.Tests.Common)

Module Tests.Common

module Q = QCheck

This module provide all the common testing infrastructure. It is intended to be opened in all testing modules

module Gen : sig ... end
module QCT = Q.Test
module Print = Q.Print
val (==>) : bool -> bool -> bool
diff --git a/doc/html/read-dwarf/Tests/ConcreteEvalT/index.html b/doc/html/read-dwarf/Tests/ConcreteEvalT/index.html index ab1818b2..9127e659 100644 --- a/doc/html/read-dwarf/Tests/ConcreteEvalT/index.html +++ b/doc/html/read-dwarf/Tests/ConcreteEvalT/index.html @@ -1,2 +1,14 @@ -ConcreteEvalT (read-dwarf.Tests.ConcreteEvalT)

Module Tests.ConcreteEvalT

module ConcreteEval = Exp.ConcreteEval
module Value = Exp.Value
module Typed = Exp.Typed
val const_exp_gen_ty : int -> Ast.no Ast.ty -> ('aAst.no) ExpGen.Gen.exp Tests.Common.Gen.t
val const_exp_gen : int -> ('aAst.no) ExpGen.Gen.exp Tests.Common.Gen.t
val const_exp_shrinker_top : ('a'b'c'd) Ast.Base.exp -> (('a'b'c'd) Ast.Base.exp -> unit) -> unit

Shrink by trying all sub expressions

val const_exp_shrinker_bot : ('a'bAst.noAst.no) Ast.exp -> (('c'd) Exp.Typed.t -> unit) -> unit

Shrink by replacing a non-atomic constant expression by it's constEval evaluation

val const_exp_shrinker : (Ast.no Ast.ty'aAst.noAst.no) Ast.Base.exp -> ((Ast.no Ast.ty'aAst.noAst.no) Ast.exp -> unit) -> unit

Shrink by using both const_exp_shrinker_top and const_exp_shrinker_bot

val const_exp : ExpGen.ExpT.t Tests.Common.Q.arbitrary
val concrete_eval : Tests.Common.QCT.t
val tests : Tests.Common.QCT.t list
\ No newline at end of file +ConcreteEvalT (read-dwarf.Tests.ConcreteEvalT)

Module Tests.ConcreteEvalT

This module is for testing ConcreteEval

module ConcreteEval = Exp.ConcreteEval
module Value = Exp.Value
module Typed = Exp.Typed
val const_exp_gen_ty : + int -> + Ast.no Ast.ty -> + ('a, Ast.no) ExpGen.Gen.exp Common.Gen.t
val const_exp_gen : int -> ('a, Ast.no) ExpGen.Gen.exp Common.Gen.t
val const_exp_shrinker_top : + ('a, 'b, 'c, 'd) Ast.Base.exp -> + (('a, 'b, 'c, 'd) Ast.Base.exp -> unit) -> + unit

Shrink by trying all sub expressions

val const_exp_shrinker_bot : + ('a, 'b, Ast.no, Ast.no) Ast.exp -> + (('c, 'd) Exp.Typed.t -> unit) -> + unit

Shrink by replacing a non-atomic constant expression by it's constEval evaluation

val const_exp_shrinker : + (Ast.no Ast.ty, 'a, Ast.no, Ast.no) Ast.Base.exp -> + (('a, Ast.no) Exp.Typed.t -> unit) -> + unit
val const_exp : ExpGen.ExpT.t Tests.Common.Q.arbitrary
val concrete_eval : Tests.Common.QCT.t
val tests : Tests.Common.QCT.t list
diff --git a/doc/html/read-dwarf/Tests/ExpGen/ExpT/index.html b/doc/html/read-dwarf/Tests/ExpGen/ExpT/index.html index 9581053d..138cb8f3 100644 --- a/doc/html/read-dwarf/Tests/ExpGen/ExpT/index.html +++ b/doc/html/read-dwarf/Tests/ExpGen/ExpT/index.html @@ -1,2 +1,2 @@ -ExpT (read-dwarf.Tests.ExpGen.ExpT)

Module ExpGen.ExpT

type var = Var.t
type t = (varAst.no) Exp.Typed.t
val equal : t -> t -> bool
val pp : t -> Utils.Pp.document
val pp_smt : t -> Utils.Pp.document
val of_var : var -> t
val add_type : ('avarAst.noAst.no) Ast.exp -> t
\ No newline at end of file +ExpT (read-dwarf.Tests.ExpGen.ExpT)

Module ExpGen.ExpT

type var = Var.t
type t = (var, Ast.no) Exp.Typed.t
val equal : t -> t -> bool
val pp : t -> Utils.Pp.document
val pp_smt : t -> Utils.Pp.document
val of_var : var -> t
val add_type : ('a, var, Ast.no, Ast.no) Ast.exp -> t
diff --git a/doc/html/read-dwarf/Tests/ExpGen/Gen/index.html b/doc/html/read-dwarf/Tests/ExpGen/Gen/index.html index 9b97bcb2..efe3a391 100644 --- a/doc/html/read-dwarf/Tests/ExpGen/Gen/index.html +++ b/doc/html/read-dwarf/Tests/ExpGen/Gen/index.html @@ -1,2 +1,5 @@ -Gen (read-dwarf.Tests.ExpGen.Gen)

Module ExpGen.Gen

type ('v, 'm) exp = ('v'm) Exp.Typed.t
val typ : 'a Ast.ty Tests.Common.Gen.t
val var_from_ty : 'a -> (int * 'a) Tests.Common.Gen.t
val bv_var : int -> (int * 'a Ast.ty) Tests.Common.Gen.t
val bool_var : (int * 'a Ast.ty) Tests.Common.Gen.t
val bitvec_size : Utils.BitVec.t Tests.Common.Gen.sized
val bitvec : Utils.BitVec.t Tests.Common.Gen.t
type ('v, 'm) gen_param = {
typ : Ast.no Ast.ty;

Cannot contain an enum

size : int;
bv_atom_gen : ('v'm) exp Tests.Common.Gen.sized;
bool_atom_gen : ('v'm) exp Tests.Common.Gen.t;
}
val atom : params:('a'b) gen_param -> ('a'b) exp Tests.Common.Gen.t

Generate an atom according to the params

val unop : 'a Ast.ty -> (Ast.unop * 'b Ast.ty) Tests.Common.Gen.t
val binop : 'a Ast.ty -> ('b Ast.binop * 'c Ast.ty * 'c Ast.ty) Tests.Common.Gen.t
val manyop : 'a Ast.ty -> (Ast.manyop * 'b Ast.ty Utils.List.t) Tests.Common.Gen.t
val exp_from_params : ('aAst.no) gen_param -> ('aAst.no) exp Tests.Common.Gen.t
val bv_consts : int -> ('a'b) Exp.Typed.t Tests.Common.Gen.t
val bv_atom_from_var : 'v Tests.Common.Gen.sized -> int -> ('v'a) Exp.Typed.t Tests.Common.Gen.t
val bv_atom_with_var : int -> (int * 'a Ast.ty'b) Exp.Typed.t Tests.Common.Gen.t
val bool_consts : ('a'b) Exp.Typed.t Tests.Common.Gen.t
val bool_atom_from_var : 'v Tests.Common.Gen.t -> ('v'a) Exp.Typed.t Tests.Common.Gen.t
val bool_atom_with_var : (int * 'a Ast.ty'b) Exp.Typed.t Tests.Common.Gen.t
\ No newline at end of file +Gen (read-dwarf.Tests.ExpGen.Gen)

Module ExpGen.Gen

type ('v, 'm) exp = ('v, 'm) Exp.Typed.t
val typ : 'a Ast.ty Common.Gen.t
val var_from_ty : 'a -> (int * 'a) Common.Gen.t
val bv_var : int -> (int * 'a Ast.ty) Common.Gen.t
val bool_var : (int * 'a Ast.ty) Common.Gen.t
type ('v, 'm) gen_param = {
  1. typ : Ast.no Ast.ty;
    (*

    Cannot contain an enum

    *)
  2. size : int;
  3. bv_atom_gen : ('v, 'm) exp Common.Gen.sized;
  4. bool_atom_gen : ('v, 'm) exp Common.Gen.t;
}
val atom : params:('a, 'b) gen_param -> ('a, 'b) exp Common.Gen.t

Generate an atom according to the params

val unop : 'a Ast.ty -> (Ast.unop * 'b Ast.ty) Common.Gen.t
val binop : 'a Ast.ty -> ('b Ast.binop * 'c Ast.ty * 'c Ast.ty) Common.Gen.t
val exp_from_params : ('a, Ast.no) gen_param -> ('a, Ast.no) exp Common.Gen.t
val bv_consts : int -> ('a, 'b) Exp.Typed.t Common.Gen.t
val bv_atom_from_var : + 'v Common.Gen.sized -> + int -> + ('v, 'a) Exp.Typed.t Common.Gen.t
val bv_atom_with_var : int -> (int * 'a Ast.ty, 'b) Exp.Typed.t Common.Gen.t
val bool_consts : ('a, 'b) Exp.Typed.t Common.Gen.t
val bool_atom_from_var : 'v Common.Gen.t -> ('v, 'a) Exp.Typed.t Common.Gen.t
val bool_atom_with_var : (int * 'a Ast.ty, 'b) Exp.Typed.t Common.Gen.t
diff --git a/doc/html/read-dwarf/Tests/ExpGen/Var/index.html b/doc/html/read-dwarf/Tests/ExpGen/Var/index.html index a690e2e0..466cba02 100644 --- a/doc/html/read-dwarf/Tests/ExpGen/Var/index.html +++ b/doc/html/read-dwarf/Tests/ExpGen/Var/index.html @@ -1,2 +1,2 @@ -Var (read-dwarf.Tests.ExpGen.Var)

Module ExpGen.Var

Test variables to instantiate variable dependent functors for testing.

type t = int * Ast.no Ast.ty
val ty : ('a * 'b) -> 'b
val pp : t -> PPrintEngine.document
val equal : 'a -> 'a -> bool
val hash : 'a -> int
val of_string : string -> t
\ No newline at end of file +Var (read-dwarf.Tests.ExpGen.Var)

Module ExpGen.Var

Test variables to instantiate variable dependent functors for testing.

type t = int * Ast.no Ast.ty
val ty : ('a * 'b) -> 'b
val pp : t -> Utils.Pp.document
val equal : 'a -> 'a -> bool
val hash : 'a -> int
val of_string : string -> t
diff --git a/doc/html/read-dwarf/Tests/ExpGen/Z3/Htbl/index.html b/doc/html/read-dwarf/Tests/ExpGen/Z3/Htbl/index.html index 1ec7ea60..5b06f439 100644 --- a/doc/html/read-dwarf/Tests/ExpGen/Z3/Htbl/index.html +++ b/doc/html/read-dwarf/Tests/ExpGen/Z3/Htbl/index.html @@ -1,2 +1,2 @@ -Htbl (read-dwarf.Tests.ExpGen.Z3.Htbl)

Module Z3.Htbl

type key = var
type 'a t = 'a Z3.Make(Var).Htbl.t
val create : int -> 'a t
val clear : 'a t -> unit
val reset : 'a t -> unit
val copy : 'a t -> 'a t
val add : 'a t -> key -> 'a -> unit
val remove : 'a t -> key -> unit
val find : 'a t -> key -> 'a
val find_opt : 'a t -> key -> 'a option
val find_all : 'a t -> key -> 'a list
val replace : 'a t -> key -> 'a -> unit
val mem : 'a t -> key -> bool
val iter : (key -> 'a -> unit) -> 'a t -> unit
val filter_map_inplace : (key -> 'a -> 'a option) -> 'a t -> unit
val fold : (key -> 'a -> 'b -> 'b) -> 'a t -> 'b -> 'b
val length : 'a t -> int
val stats : 'a t -> Stdlib.Hashtbl.statistics
val to_seq : 'a t -> (key * 'a) Stdlib.Seq.t
val to_seq_keys : 'a t -> key Stdlib.Seq.t
val to_seq_values : 'a t -> 'a Stdlib.Seq.t
val add_seq : 'a t -> (key * 'a) Stdlib.Seq.t -> unit
val replace_seq : 'a t -> (key * 'a) Stdlib.Seq.t -> unit
val of_seq : (key * 'a) Stdlib.Seq.t -> 'a t
\ No newline at end of file +Htbl (read-dwarf.Tests.ExpGen.Z3.Htbl)

Module Z3.Htbl

type key = var
type !'a t = 'a Z3.Make(Var).Htbl.t
val create : int -> 'a t
val clear : 'a t -> unit
val reset : 'a t -> unit
val copy : 'a t -> 'a t
val add : 'a t -> key -> 'a -> unit
val remove : 'a t -> key -> unit
val find : 'a t -> key -> 'a
val find_opt : 'a t -> key -> 'a option
val find_all : 'a t -> key -> 'a list
val replace : 'a t -> key -> 'a -> unit
val mem : 'a t -> key -> bool
val iter : (key -> 'a -> unit) -> 'a t -> unit
val filter_map_inplace : (key -> 'a -> 'a option) -> 'a t -> unit
val fold : (key -> 'a -> 'acc -> 'acc) -> 'a t -> 'acc -> 'acc
val length : 'a t -> int
val stats : 'a t -> Stdlib__Hashtbl.statistics
val to_seq : 'a t -> (key * 'a) Stdlib.Seq.t
val to_seq_keys : 'a t -> key Stdlib.Seq.t
val to_seq_values : 'a t -> 'a Stdlib.Seq.t
val add_seq : 'a t -> (key * 'a) Stdlib.Seq.t -> unit
val replace_seq : 'a t -> (key * 'a) Stdlib.Seq.t -> unit
val of_seq : (key * 'a) Stdlib.Seq.t -> 'a t
diff --git a/doc/html/read-dwarf/Tests/ExpGen/Z3/index.html b/doc/html/read-dwarf/Tests/ExpGen/Z3/index.html index eabe996a..a2e3e599 100644 --- a/doc/html/read-dwarf/Tests/ExpGen/Z3/index.html +++ b/doc/html/read-dwarf/Tests/ExpGen/Z3/index.html @@ -1,2 +1,2 @@ -Z3 (read-dwarf.Tests.ExpGen.Z3)

Module ExpGen.Z3

type var = Var.t
type exp = (varAst.no) Exp.Typed.t
module Htbl : sig ... end
val declare_var_always : Z3.server -> var -> unit
val declare_var : Z3.server -> declared:unit Htbl.t -> var -> unit
val declare_vars : Z3.server -> declared:unit Htbl.t -> exp -> unit
val simplify : Z3.server -> exp -> exp
val simplify_decl : Z3.server -> declared:unit Htbl.t -> exp -> exp
val send_assert : Z3.server -> exp -> unit
val send_assert_decl : Z3.server -> declared:unit Htbl.t -> exp -> unit
val check : Z3.server -> exp -> bool option
val check_sat : Z3.server -> exp -> bool option
val check_both : Z3.server -> exp -> bool option
val simplify_full : exp -> exp
val check_full : ?⁠hyps:exp list -> exp -> bool option
val check_sat_full : exp list -> bool option
\ No newline at end of file +Z3 (read-dwarf.Tests.ExpGen.Z3)

Module ExpGen.Z3

type var = Var.t
type exp = (var, Ast.no) Exp.Typed.t
module Htbl : sig ... end
val declare_var_always : Z3.server -> var -> unit
val declare_var : Z3.server -> declared:unit Htbl.t -> var -> unit
val declare_vars : Z3.server -> declared:unit Htbl.t -> exp -> unit
val simplify : Z3.server -> exp -> exp
val simplify_decl : Z3.server -> declared:unit Htbl.t -> exp -> exp
val send_assert : Z3.server -> exp -> unit
val send_assert_decl : Z3.server -> declared:unit Htbl.t -> exp -> unit
val check : Z3.server -> exp -> bool option
val check_sat : Z3.server -> exp -> bool option
val check_both : Z3.server -> exp -> bool option
val simplify_subterms : Z3.server -> exp -> exp
val simplify_subterms_decl : Z3.server -> declared:unit Htbl.t -> exp -> exp
val simplify_full : exp -> exp
val check_full : ?hyps:exp list -> exp -> bool option
val check_sat_full : exp list -> bool option
val simplify_subterms_full : ?hyps:exp list -> exp -> exp
diff --git a/doc/html/read-dwarf/Tests/ExpGen/index.html b/doc/html/read-dwarf/Tests/ExpGen/index.html index 4fac6aa8..fbaa5639 100644 --- a/doc/html/read-dwarf/Tests/ExpGen/index.html +++ b/doc/html/read-dwarf/Tests/ExpGen/index.html @@ -1,2 +1,9 @@ -ExpGen (read-dwarf.Tests.ExpGen)

Module Tests.ExpGen

type ('v, 'm) exp = ('v'm) Exp.Typed.t
module Var : sig ... end

Test variables to instantiate variable dependent functors for testing.

module ExpT : sig ... end
module Z3 : sig ... end
module Gen : sig ... end
val shrink_propagate : ('v'm) Exp.Typed.t Tests.Common.Q.Shrink.t -> ('v'm) Exp.Typed.t -> (('m Ast.ty'vAst.no'm) Ast.exp -> unit) -> unit

Propagate an existing shrinker, by try it on all descendants of this. The The provided shrinker must preserve the type.

val from_gen : ?⁠shrink:ExpT.t Tests.Common.Q.Shrink.t -> (ExpT.varAst.no) exp Tests.Common.Q.Gen.t -> ExpT.t Tests.Common.Q.arbitrary

Generate an expression arbitrary from a generator (and optionally a shrinker)

\ No newline at end of file +ExpGen (read-dwarf.Tests.ExpGen)

Module Tests.ExpGen

This module try to provide generic generators and arbitrary to work with expressions.

For now expression do not use enumeration since the Z3 back-end don't support enumeration yet.

type ('v, 'm) exp = ('v, 'm) Exp.Typed.t
module Var : sig ... end

Test variables to instantiate variable dependent functors for testing.

module ExpT : sig ... end
module Z3 : sig ... end
module Gen : sig ... end
val shrink_propagate : + ('v, 'm) Exp.Typed.t Tests.Common.Q.Shrink.t -> + ('v, 'm) Exp.Typed.t -> + (('v, 'm) Exp.Typed.t -> unit) -> + unit

Propagate an existing shrinker, by try it on all descendants of this. The The provided shrinker must preserve the type.

val from_gen : + ?shrink:ExpT.t Tests.Common.Q.Shrink.t -> + (ExpT.var, Ast.no) exp Tests.Common.Q.Gen.t -> + ExpT.t Tests.Common.Q.arbitrary

Generate an expression arbitrary from a generator (and optionally a shrinker)

diff --git a/doc/html/read-dwarf/Tests/SimplifyCheck/index.html b/doc/html/read-dwarf/Tests/SimplifyCheck/index.html index 3ed79d0f..d3f1188c 100644 --- a/doc/html/read-dwarf/Tests/SimplifyCheck/index.html +++ b/doc/html/read-dwarf/Tests/SimplifyCheck/index.html @@ -1,2 +1,14 @@ -SimplifyCheck (read-dwarf.Tests.SimplifyCheck)

Module Tests.SimplifyCheck

val var_exp_gen_ty : int -> Ast.no Ast.ty -> (int * 'a Ast.tyAst.no) ExpGen.Gen.exp Tests.Common.Gen.t
val var_exp_gen : int -> (int * 'a Ast.tyAst.no) ExpGen.Gen.exp Tests.Common.Gen.t
val const_exp_shrinker_top : ('a'b'c'd) Ast.Base.exp -> (('a'b'c'd) Ast.Base.exp -> unit) -> unit

Shrink by trying all sub expressions

val const_exp_shrinker_bot : (Ast.no Ast.tyExpGen.Z3.varAst.noAst.no) Ast.exp -> (ExpGen.Z3.exp -> unit) -> unit

Shrink by replacing a non-atomic expression by it's simplified version

val const_exp_shrinker : (Ast.no Ast.tyExpGen.Z3.varAst.noAst.no) Ast.Base.exp -> ((Ast.no Ast.tyExpGen.Z3.varAst.noAst.no) Ast.exp -> unit) -> unit

Shrink by using both const_exp_shrinker_top and const_exp_shrinker_bot

val var_exp : ExpGen.ExpT.t Tests.Common.Q.arbitrary
val simplify_check : Tests.Common.QCT.t
val tests : Tests.Common.QCT.t list
\ No newline at end of file +SimplifyCheck (read-dwarf.Tests.SimplifyCheck)

Module Tests.SimplifyCheck

This module is about testing Z3 simplify against Z3 check. It's not really about testing Z3 itself but about testing our parsing of Z3 expression output (in particular let bindings unfolding)

val var_exp_gen_ty : + int -> + Ast.no Ast.ty -> + (int * 'a Ast.ty, Ast.no) ExpGen.Gen.exp Common.Gen.t
val var_exp_gen : int -> (int * 'a Ast.ty, Ast.no) ExpGen.Gen.exp Common.Gen.t
val const_exp_shrinker_top : + ('a, 'b, 'c, 'd) Ast.Base.exp -> + (('a, 'b, 'c, 'd) Ast.Base.exp -> unit) -> + unit

Shrink by trying all sub expressions

val const_exp_shrinker_bot : + (Ast.no Ast.ty, ExpGen.Z3.var, Ast.no, Ast.no) Ast.exp -> + (ExpGen.Z3.exp -> unit) -> + unit

Shrink by replacing a non-atomic expression by it's simplified version

val const_exp_shrinker : + (Ast.no Ast.ty, ExpGen.Z3.var, Ast.no, Ast.no) Ast.Base.exp -> + ((ExpGen.Z3.var, Ast.no) Exp.Typed.t -> unit) -> + unit
val var_exp : ExpGen.ExpT.t Tests.Common.Q.arbitrary
val simplify_check : Tests.Common.QCT.t
val tests : Tests.Common.QCT.t list
diff --git a/doc/html/read-dwarf/Tests/index.html b/doc/html/read-dwarf/Tests/index.html index 1432d216..218eb149 100644 --- a/doc/html/read-dwarf/Tests/index.html +++ b/doc/html/read-dwarf/Tests/index.html @@ -1,2 +1,2 @@ -Tests (read-dwarf.Tests)

Module Tests

module BytesSeqT : sig ... end
module Common : sig ... end
module ConcreteEvalT : sig ... end
module ExpGen : sig ... end
module SimplifyCheck : sig ... end
\ No newline at end of file +Tests (read-dwarf.Tests)

Module Tests

module BytesSeqT : sig ... end
module Common : sig ... end
module ConcreteEvalT : sig ... end

This module is for testing ConcreteEval

module ExpGen : sig ... end

This module try to provide generic generators and arbitrary to work with expressions.

module SimplifyCheck : sig ... end

This module is about testing Z3 simplify against Z3 check. It's not really about testing Z3 itself but about testing our parsing of Z3 expression output (in particular let bindings unfolding)

diff --git a/doc/html/read-dwarf/Trace/Base/Exp/index.html b/doc/html/read-dwarf/Trace/Base/Exp/index.html index d8a88a78..e58de2b9 100644 --- a/doc/html/read-dwarf/Trace/Base/Exp/index.html +++ b/doc/html/read-dwarf/Trace/Base/Exp/index.html @@ -1,2 +1,2 @@ -Exp (read-dwarf.Trace.Base.Exp)

Module Base.Exp

include sig ... end
type var = Var.t
type t = (varAst.no) Exp.Typed.t
val equal : t -> t -> bool
val pp : t -> Utils.Pp.document
val pp_smt : t -> Utils.Pp.document
val of_var : var -> t
val add_type : ('avarAst.noAst.no) Ast.exp -> t
val of_reg : Var.Reg.t -> t
\ No newline at end of file +Exp (read-dwarf.Trace.Base.Exp)

Module Base.Exp

include sig ... end
type var = Var.t
type t = (var, Ast.no) Exp.Typed.t
val equal : t -> t -> bool
val pp : t -> Utils.Pp.document
val pp_smt : t -> Utils.Pp.document
val of_var : var -> t
val add_type : ('a, var, Ast.no, Ast.no) Ast.exp -> t
val of_reg : Var.Reg.t -> t
diff --git a/doc/html/read-dwarf/Trace/Base/SimpContext/index.html b/doc/html/read-dwarf/Trace/Base/SimpContext/index.html index e704dd0a..2993946e 100644 --- a/doc/html/read-dwarf/Trace/Base/SimpContext/index.html +++ b/doc/html/read-dwarf/Trace/Base/SimpContext/index.html @@ -1,2 +1,2 @@ -SimpContext (read-dwarf.Trace.Base.SimpContext)

Module Base.SimpContext

A instance of Z3.ContextCounter.

val counter : Utils.Counter.t
val openc : unit -> unit
val num : unit -> int
val closec : unit -> unit
\ No newline at end of file +SimpContext (read-dwarf.Trace.Base.SimpContext)

Module Base.SimpContext

A instance of Z3.ContextCounter.

val counter : Utils.Counter.t
val openc : unit -> unit
val num : unit -> int
val closec : unit -> unit
diff --git a/doc/html/read-dwarf/Trace/Base/Var/index.html b/doc/html/read-dwarf/Trace/Base/Var/index.html index e7f3f03e..5c0a9f6c 100644 --- a/doc/html/read-dwarf/Trace/Base/Var/index.html +++ b/doc/html/read-dwarf/Trace/Base/Var/index.html @@ -1,2 +1,2 @@ -Var (read-dwarf.Trace.Base.Var)

Module Base.Var

This module contains variable used in traces

module Reg = State.Reg
type t =
| Register of Reg.t

The value of the register at the beginning of the trace

| Read of int * Ast.Size.t

The result of that memory reading operation

| NonDet of int * Ast.Size.t

Variable representing non-determinism in the spec

A trace variable

val to_string : t -> string

Convert the variable to the string encoding. For parsing infractructure reason, the encoding must always contain at least one :.

val of_string : string -> t

Inverse of to_string

val pp : t -> Utils.Pp.document

Pretty prints the variable

val equal : 'a -> 'a -> bool
val hash : 'a -> int
val ty : t -> Reg.ty
val of_reg : Reg.t -> t
\ No newline at end of file +Var (read-dwarf.Trace.Base.Var)

Module Base.Var

This module contains variable used in traces

module Reg = State.Reg
type t =
  1. | Register of Reg.t
    (*

    The value of the register at the beginning of the trace

    *)
  2. | Read of int * Ast.Size.t
    (*

    The result of that memory reading operation

    *)
  3. | NonDet of int * Ast.Size.t
    (*

    Variable representing non-determinism in the spec

    *)
  4. | Segment of string * int
    (*

    Variable representing symbolic segment in the opcode

    *)

A trace variable

val to_string : t -> string

Convert the variable to the string encoding. For parsing infractructure reason, the encoding must always contain at least one :.

val of_string : string -> t

Inverse of to_string

val pp : t -> Utils.Pp.document

Pretty prints the variable

val equal : 'a -> 'a -> bool
val hash : 'a -> int
val ty : t -> Reg.ty
val of_reg : Reg.t -> t
diff --git a/doc/html/read-dwarf/Trace/Base/VarTbl/index.html b/doc/html/read-dwarf/Trace/Base/VarTbl/index.html index 62a8cb19..7df27d58 100644 --- a/doc/html/read-dwarf/Trace/Base/VarTbl/index.html +++ b/doc/html/read-dwarf/Trace/Base/VarTbl/index.html @@ -1,2 +1,2 @@ -VarTbl (read-dwarf.Trace.Base.VarTbl)

Module Base.VarTbl

type key = Var.t
type 'a t = 'a Stdlib__hashtbl.Make(Var).t
val create : int -> 'a t
val clear : 'a t -> unit
val reset : 'a t -> unit
val copy : 'a t -> 'a t
val add : 'a t -> key -> 'a -> unit
val remove : 'a t -> key -> unit
val find : 'a t -> key -> 'a
val find_opt : 'a t -> key -> 'a option
val find_all : 'a t -> key -> 'a list
val replace : 'a t -> key -> 'a -> unit
val mem : 'a t -> key -> bool
val iter : (key -> 'a -> unit) -> 'a t -> unit
val filter_map_inplace : (key -> 'a -> 'a option) -> 'a t -> unit
val fold : (key -> 'a -> 'b -> 'b) -> 'a t -> 'b -> 'b
val length : 'a t -> int
val stats : 'a t -> Stdlib__hashtbl.statistics
val to_seq : 'a t -> (key * 'a) Stdlib.Seq.t
val to_seq_keys : 'a t -> key Stdlib.Seq.t
val to_seq_values : 'a t -> 'a Stdlib.Seq.t
val add_seq : 'a t -> (key * 'a) Stdlib.Seq.t -> unit
val replace_seq : 'a t -> (key * 'a) Stdlib.Seq.t -> unit
val of_seq : (key * 'a) Stdlib.Seq.t -> 'a t
\ No newline at end of file +VarTbl (read-dwarf.Trace.Base.VarTbl)

Module Base.VarTbl

type key = Var.t
type !'a t = 'a Stdlib__Hashtbl.Make(Var).t
val create : int -> 'a t
val clear : 'a t -> unit
val reset : 'a t -> unit
val copy : 'a t -> 'a t
val add : 'a t -> key -> 'a -> unit
val remove : 'a t -> key -> unit
val find : 'a t -> key -> 'a
val find_opt : 'a t -> key -> 'a option
val find_all : 'a t -> key -> 'a list
val replace : 'a t -> key -> 'a -> unit
val mem : 'a t -> key -> bool
val iter : (key -> 'a -> unit) -> 'a t -> unit
val filter_map_inplace : (key -> 'a -> 'a option) -> 'a t -> unit
val fold : (key -> 'a -> 'acc -> 'acc) -> 'a t -> 'acc -> 'acc
val length : 'a t -> int
val stats : 'a t -> Stdlib__Hashtbl.statistics
val to_seq : 'a t -> (key * 'a) Stdlib.Seq.t
val to_seq_keys : 'a t -> key Stdlib.Seq.t
val to_seq_values : 'a t -> 'a Stdlib.Seq.t
val add_seq : 'a t -> (key * 'a) Stdlib.Seq.t -> unit
val replace_seq : 'a t -> (key * 'a) Stdlib.Seq.t -> unit
val of_seq : (key * 'a) Stdlib.Seq.t -> 'a t
diff --git a/doc/html/read-dwarf/Trace/Base/Z3Tr/Htbl/index.html b/doc/html/read-dwarf/Trace/Base/Z3Tr/Htbl/index.html index ce6c218b..22802d81 100644 --- a/doc/html/read-dwarf/Trace/Base/Z3Tr/Htbl/index.html +++ b/doc/html/read-dwarf/Trace/Base/Z3Tr/Htbl/index.html @@ -1,2 +1,2 @@ -Htbl (read-dwarf.Trace.Base.Z3Tr.Htbl)

Module Z3Tr.Htbl

type key = var
type 'a t = 'a Z3.Make(Var).Htbl.t
val create : int -> 'a t
val clear : 'a t -> unit
val reset : 'a t -> unit
val copy : 'a t -> 'a t
val add : 'a t -> key -> 'a -> unit
val remove : 'a t -> key -> unit
val find : 'a t -> key -> 'a
val find_opt : 'a t -> key -> 'a option
val find_all : 'a t -> key -> 'a list
val replace : 'a t -> key -> 'a -> unit
val mem : 'a t -> key -> bool
val iter : (key -> 'a -> unit) -> 'a t -> unit
val filter_map_inplace : (key -> 'a -> 'a option) -> 'a t -> unit
val fold : (key -> 'a -> 'b -> 'b) -> 'a t -> 'b -> 'b
val length : 'a t -> int
val stats : 'a t -> Stdlib.Hashtbl.statistics
val to_seq : 'a t -> (key * 'a) Stdlib.Seq.t
val to_seq_keys : 'a t -> key Stdlib.Seq.t
val to_seq_values : 'a t -> 'a Stdlib.Seq.t
val add_seq : 'a t -> (key * 'a) Stdlib.Seq.t -> unit
val replace_seq : 'a t -> (key * 'a) Stdlib.Seq.t -> unit
val of_seq : (key * 'a) Stdlib.Seq.t -> 'a t
\ No newline at end of file +Htbl (read-dwarf.Trace.Base.Z3Tr.Htbl)

Module Z3Tr.Htbl

type key = var
type !'a t = 'a Z3.Make(Var).Htbl.t
val create : int -> 'a t
val clear : 'a t -> unit
val reset : 'a t -> unit
val copy : 'a t -> 'a t
val add : 'a t -> key -> 'a -> unit
val remove : 'a t -> key -> unit
val find : 'a t -> key -> 'a
val find_opt : 'a t -> key -> 'a option
val find_all : 'a t -> key -> 'a list
val replace : 'a t -> key -> 'a -> unit
val mem : 'a t -> key -> bool
val iter : (key -> 'a -> unit) -> 'a t -> unit
val filter_map_inplace : (key -> 'a -> 'a option) -> 'a t -> unit
val fold : (key -> 'a -> 'acc -> 'acc) -> 'a t -> 'acc -> 'acc
val length : 'a t -> int
val stats : 'a t -> Stdlib__Hashtbl.statistics
val to_seq : 'a t -> (key * 'a) Stdlib.Seq.t
val to_seq_keys : 'a t -> key Stdlib.Seq.t
val to_seq_values : 'a t -> 'a Stdlib.Seq.t
val add_seq : 'a t -> (key * 'a) Stdlib.Seq.t -> unit
val replace_seq : 'a t -> (key * 'a) Stdlib.Seq.t -> unit
val of_seq : (key * 'a) Stdlib.Seq.t -> 'a t
diff --git a/doc/html/read-dwarf/Trace/Base/Z3Tr/index.html b/doc/html/read-dwarf/Trace/Base/Z3Tr/index.html index b361e7cf..fed8254f 100644 --- a/doc/html/read-dwarf/Trace/Base/Z3Tr/index.html +++ b/doc/html/read-dwarf/Trace/Base/Z3Tr/index.html @@ -1,2 +1,2 @@ -Z3Tr (read-dwarf.Trace.Base.Z3Tr)

Module Base.Z3Tr

type var = Var.t
type exp = (varAst.no) Exp.Typed.t
module Htbl : sig ... end
val declare_var_always : Z3.server -> var -> unit
val declare_var : Z3.server -> declared:unit Htbl.t -> var -> unit
val declare_vars : Z3.server -> declared:unit Htbl.t -> exp -> unit
val simplify : Z3.server -> exp -> exp
val simplify_decl : Z3.server -> declared:unit Htbl.t -> exp -> exp
val send_assert : Z3.server -> exp -> unit
val send_assert_decl : Z3.server -> declared:unit Htbl.t -> exp -> unit
val check : Z3.server -> exp -> bool option
val check_sat : Z3.server -> exp -> bool option
val check_both : Z3.server -> exp -> bool option
val simplify_full : exp -> exp
val check_full : ?⁠hyps:exp list -> exp -> bool option
val check_sat_full : exp list -> bool option
\ No newline at end of file +Z3Tr (read-dwarf.Trace.Base.Z3Tr)

Module Base.Z3Tr

type var = Var.t
type exp = (var, Ast.no) Exp.Typed.t
module Htbl : sig ... end
val declare_var_always : Z3.server -> var -> unit
val declare_var : Z3.server -> declared:unit Htbl.t -> var -> unit
val declare_vars : Z3.server -> declared:unit Htbl.t -> exp -> unit
val simplify : Z3.server -> exp -> exp
val simplify_decl : Z3.server -> declared:unit Htbl.t -> exp -> exp
val send_assert : Z3.server -> exp -> unit
val send_assert_decl : Z3.server -> declared:unit Htbl.t -> exp -> unit
val check : Z3.server -> exp -> bool option
val check_sat : Z3.server -> exp -> bool option
val check_both : Z3.server -> exp -> bool option
val simplify_subterms : Z3.server -> exp -> exp
val simplify_subterms_decl : Z3.server -> declared:unit Htbl.t -> exp -> exp
val simplify_full : exp -> exp
val check_full : ?hyps:exp list -> exp -> bool option
val check_sat_full : exp list -> bool option
val simplify_subterms_full : ?hyps:exp list -> exp -> exp
diff --git a/doc/html/read-dwarf/Trace/Base/index.html b/doc/html/read-dwarf/Trace/Base/index.html index 4cdf1c0d..d80a235a 100644 --- a/doc/html/read-dwarf/Trace/Base/index.html +++ b/doc/html/read-dwarf/Trace/Base/index.html @@ -1,3 +1,13 @@ -Base (read-dwarf.Trace.Base)

Module Trace.Base

This module defines a new simplified kind of trace to replace Isla traces in the later stages of the instruction semantics processing.

The traces are even simpler and more easily typable. The possible events are in the type event and traces (t) are just list of them.

Compared to Isla, the concept of reading a register do not exist anymore. Nor the concept of pure symbolic variable or Sail structured values. Instead expression can contain only registers and results of previous memory read as decribe in the type Var.t. All writing event directly write an entire expression. There are no intermediary variable definitions.

This raise a problem that if an isla trace reads a register after having written to it, then this is ambiguous to represent.

Thus a partially monadic representation has been chosen:

  • The effect of writes on registers are delayed to the end, which means that registers variable value in every expression is the value of that register at the beginning of the trace. In particular, if there are two write to the register, only the last one have any effect, the other can be deleted.
  • The memory operation behave in normal monadic way, in particular a memory read can read the value written by a previous write in the same instruction, even if this is very unlikely.

In the end, both assertion and register write to different registers can be reordered at will.

TODO: To make it more clean, getting register writes and assertions out of the trace would make sense like:

type mem_event = Read of ... | Write of ...
-type t = { asserts : exp list; reg_writes : (Reg.t * exp) list; mem: mem_event list }

For all those reason, concatenating two trace semantically is very different that concatenating list of event, and is not implemented yet.

The important functions are of_isla to convert and Isla traces and simplify for simplify traces.

module Var : sig ... end

This module contains variable used in traces

module ExpPp = Exp.Pp

A trace expression. No let bindings, no memory operations

module Typed = Exp.Typed
module Exp : sig ... end
type exp = Exp.t
type event =
| WriteReg of {
reg : State.Reg.t;
value : exp;
}
| ReadMem of {
addr : exp;
value : int;
size : Ast.Size.t;
}
| WriteMem of {
addr : exp;
value : exp;
size : Ast.Size.t;
}
| Assert of exp

The event type. See the module description for more details

type t = event list
val iter_var : (Exp.var -> unit) -> event list -> unit

Pretty printing

val pp_exp : ('aVar.tAst.noAst.no) Ast.exp -> Utils.Pp.document

Pretty print an expression

val pp_event : event -> Utils.Pp.document

Pretty print an event

val pp : event list -> PPrintEngine.document

Pretty print a trace

Isla to Trace conversion

This section perform the conversion from Isla trace to the traces of this module.

The conversion is generrally obvious, however there is subtlety: If the Isla trace reads a register after having written it, then the read produce the written expression instead of just the symbolic value of that register. That why there is a written_registers parameter to some function of this section.

exception OfIslaError

Throw an error in case of local conversion error. Normally a type-checked Isla trace should not fail in this section

type value_context = exp Utils.HashVector.t

The context mapping Isla variable numbers to trace expression

val get_var : 'a Utils.HashVector.t -> int -> 'a

Get the exression of the variable at the index. Throw OfIslaError if the variable is not bound

val exp_conv_subst : value_context -> Isla.rexp -> exp

Convert an Isla expression to a Trace expression by replacing all Isla variable by their value in the context. Throw OfIslaError if the substitution fails

val exp_of_valu : Isla_lang.AST.lrng -> exp Utils.HashVector.t -> Isla.valu -> exp

Convert an Isla.valu in a expression

val write_to_valu : 'a Utils.HashVector.t -> Isla.valu -> 'a -> unit

Write an expression to an Isla.valu

val event_of_isla : written_registers:(State.Reg.texp) Stdlib.Hashtbl.t -> read_counter:Utils.Counter.t -> vc:value_context -> Isla.revent -> event option

Convert an isla event to optionally a Trace event, most events are deleted

val of_isla : Isla.rtrc -> t

Top level function to convert an isla trace to one of this module

Trace simplification

module SimpContext : sig ... end

A instance of Z3.ContextCounter.

module Z3Tr : sig ... end
module VarTbl : sig ... end
val declare_non_det : Z3.server -> event list -> unit
val simplify : event list -> event list

Simplify a trace by using Z3. Perform both local expression simplification and global assertion removal (when an assertion is always true)

\ No newline at end of file +Base (read-dwarf.Trace.Base)

Module Trace.Base

This module defines a new simplified kind of trace to replace Isla traces in the later stages of the instruction semantics processing.

The traces are even simpler and more easily typable. The possible events are in the type event and traces (t) are just list of them.

Compared to Isla, the concept of reading a register do not exist anymore. Nor the concept of pure symbolic variable or Sail structured values. Instead expression can contain only registers and results of previous memory read as decribe in the type Var.t. All writing event directly write an entire expression. There are no intermediary variable definitions.

This raise a problem that if an isla trace reads a register after having written to it, then this is ambiguous to represent.

Thus a partially monadic representation has been chosen:

  • The effect of writes on registers are delayed to the end, which means that registers variable value in every expression is the value of that register at the beginning of the trace. In particular, if there are two write to the register, only the last one have any effect, the other can be deleted.
  • The memory operation behave in normal monadic way, in particular a memory read can read the value written by a previous write in the same instruction, even if this is very unlikely.

In the end, both assertion and register write to different registers can be reordered at will.

TODO: To make it more clean, getting register writes and assertions out of the trace would make sense like:

type mem_event = Read of ... | Write of ...
+type t = { asserts : exp list; reg_writes : (Reg.t * exp) list; mem: mem_event list }

For all those reason, concatenating two trace semantically is very different that concatenating list of event, and is not implemented yet.

The important functions are of_isla to convert and Isla traces and simplify for simplify traces.

module Var : sig ... end

This module contains variable used in traces

module ExpPp = Exp.Pp

A trace expression. No let bindings, no memory operations

module Typed = Exp.Typed
module Exp : sig ... end
type exp = Exp.t
type event =
  1. | WriteReg of {
    1. reg : State.Reg.t;
    2. value : exp;
    }
  2. | ReadMem of {
    1. addr : exp;
    2. value : int;
    3. size : Ast.Size.t;
    }
  3. | WriteMem of {
    1. addr : exp;
    2. value : exp;
    3. size : Ast.Size.t;
    }
  4. | Assert of exp

The event type. See the module description for more details

type t = event list
val iter_var : (Exp.var -> unit) -> event list -> unit

Pretty printing

val pp_exp : ('a, Var.t, Ast.no, Ast.no) Ast.exp -> Utils.Pp.document

Pretty print an expression

val pp_event : event -> Utils.Pp.document

Pretty print an event

val pp : event list -> Utils.Pp.document

Pretty print a trace

Isla to Trace conversion

This section perform the conversion from Isla trace to the traces of this module.

The conversion is generrally obvious, however there is subtlety: If the Isla trace reads a register after having written it, then the read produce the written expression instead of just the symbolic value of that register. That why there is a written_registers parameter to some function of this section.

exception OfIslaError

Throw an error in case of local conversion error. Normally a type-checked Isla trace should not fail in this section

type value_context = exp Utils.HashVector.t

The context mapping Isla variable numbers to trace expression

val get_var : 'a Utils.HashVector.t -> int -> 'a

Get the exression of the variable at the index. Throw OfIslaError if the variable is not bound

val exp_conv_subst : value_context -> Isla.rexp -> exp

Convert an Isla expression to a Trace expression by replacing all Isla variable by their value in the context. Throw OfIslaError if the substitution fails

val exp_of_valu : + Isla_lang.AST.lrng -> + exp Utils.HashVector.t -> + Isla.valu -> + exp

Convert an Isla.valu in a expression

val write_to_valu : 'a Utils.HashVector.t -> Isla.valu -> 'a -> unit

Write an expression to an Isla.valu

val events_of_isla : + segments_map:(string * int) Utils.HashVector.t -> + written_registers:(State.Reg.t, exp) Stdlib.Hashtbl.t -> + read_counter:Utils.Counter.t -> + vc:value_context -> + Isla.revent -> + event list

Convert an isla event to Trace events, most events are deleted

val of_isla : Isla.segment list -> Isla.rtrc -> t

Top level function to convert an isla trace to one of this module

Trace simplification

module SimpContext : sig ... end

A instance of Z3.ContextCounter.

module Z3Tr : sig ... end
module VarTbl : sig ... end
val declare_non_det : Z3.server -> event list -> unit
val simplify : event list -> event list

Simplify a trace by using Z3. Perform both local expression simplification and global assertion removal (when an assertion is always true)

diff --git a/doc/html/read-dwarf/Trace/Cache/TC/index.html b/doc/html/read-dwarf/Trace/Cache/TC/index.html index 8355a0cb..fcda26c6 100644 --- a/doc/html/read-dwarf/Trace/Cache/TC/index.html +++ b/doc/html/read-dwarf/Trace/Cache/TC/index.html @@ -1,2 +1,2 @@ -TC (read-dwarf.Trace.Cache.TC)

Module Cache.TC

type key = Opcode.t
type value = Traces.t
type epoch = Epoch.t
type t = Utils__Cache.Make(Opcode)(Traces)(Epoch).t
val make : ?⁠fake:bool -> string -> epoch -> t
val get_opt : t -> key -> value option
val get : t -> key -> value
val add : t -> key -> value -> unit
val remove : t -> key -> unit
\ No newline at end of file +TC (read-dwarf.Trace.Cache.TC)

Module Cache.TC

type key = Opcode.t
type value = Traces.t
type epoch = Epoch.t
type t
val make : ?fake:bool -> string -> epoch -> t
val get_opt : t -> key -> value option
val get : t -> key -> value
val add : t -> key -> value -> unit
val remove : t -> key -> unit
diff --git a/doc/html/read-dwarf/Trace/Cache/Traces/index.html b/doc/html/read-dwarf/Trace/Cache/Traces/index.html index a9011efd..c7183ba7 100644 --- a/doc/html/read-dwarf/Trace/Cache/Traces/index.html +++ b/doc/html/read-dwarf/Trace/Cache/Traces/index.html @@ -1,2 +1,2 @@ -Traces (read-dwarf.Trace.Cache.Traces)

Module Cache.Traces

Store 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 list
module Reg = State.Reg
type regs = (Reg.tReg.Path.t * Reg.ty) Stdlib.Hashtbl.t
val to_file : string -> t -> unit
val of_file : string -> t
\ No newline at end of file +Traces (read-dwarf.Trace.Cache.Traces)

Module Cache.Traces

Store 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 list
module Reg = State.Reg
type regs = (Reg.t, Reg.Path.t * Reg.ty) Stdlib.Hashtbl.t
val to_file : string -> t -> unit
val of_file : string -> t
diff --git a/doc/html/read-dwarf/Trace/Cache/index.html b/doc/html/read-dwarf/Trace/Cache/index.html index 06e7a6e4..fdec7ca1 100644 --- a/doc/html/read-dwarf/Trace/Cache/index.html +++ b/doc/html/read-dwarf/Trace/Cache/index.html @@ -1,2 +1,2 @@ -Cache (read-dwarf.Trace.Cache)

Module Trace.Cache

This module provides a caching system for fully processed traces

The top level function to get traces from an opcode is get_traces. This is the function called by the Run.Runner.

module Opcode = Isla.Cache.Opcode
module Epoch = Isla.Cache.Epoch
module Traces : sig ... end

Store element of type Base.tlist on disk.

module TC : sig ... end
val cache : TC.t option Stdlib.ref
type config = Isla.Cache.config
val start : Isla.Cache.config -> unit

Start the caching system. Start Isla.Cache too

val stop : unit -> unit

Stop the caching system

val get_cache : unit -> TC.t

Get the cache and fails if the cache wasn't started

val get_traces : Utils.BytesSeq.t -> Base.t list

Get the traces of the opcode given. Use Isla.Server if the value is not in the cache

val get_instr : Utils.BytesSeq.t -> Instr.t

Get a full blown Instr from the opcode, going through the whole Isla pipeline if necessary.

\ No newline at end of file +Cache (read-dwarf.Trace.Cache)

Module Trace.Cache

This module provides a caching system for fully processed traces

The top level function to get traces from an opcode is get_traces. This is the function called by the Run.Runner.

module Opcode = Isla.Cache.Opcode
module Epoch = Isla.Cache.Epoch
module Traces : sig ... end

Store element of type Base.tlist on disk.

module TC : sig ... end
val cache : TC.t option Stdlib.ref
type config = Isla.Cache.config
val start : Isla.Cache.config -> unit

Start the caching system. Start Isla.Cache too

val stop : unit -> unit

Stop the caching system

val get_cache : unit -> TC.t

Get the cache and fails if the cache wasn't started

val get_traces : Isla.Server.opcode -> Base.t list

Get the traces of the opcode given. Use Isla.Server if the value is not in the cache

val get_instr : (Utils.BytesSeq.t * Elf.Relocations.rel option) -> Instr.t

Get a full blown Instr from the opcode, going through the whole Isla pipeline if necessary.

diff --git a/doc/html/read-dwarf/Trace/Context/index.html b/doc/html/read-dwarf/Trace/Context/index.html index 856d32fa..2188bb5a 100644 --- a/doc/html/read-dwarf/Trace/Context/index.html +++ b/doc/html/read-dwarf/Trace/Context/index.html @@ -1,2 +1,6 @@ -Context (read-dwarf.Trace.Context)

Module Trace.Context

This module provide the type for a context to run a trace

Any information that should be required to run a trace but is not part of the state itself should be added here

type t = {
reg_writes : (State.Reg.t * State.tval) Utils.Vec.t;

Stores the delayed register writes

mem_reads : State.tval Utils.HashVector.t;

Stores the result of memory reads

state : State.t;
dwarf : Dw.t option;

Optionally DWARF information. If present, typing is enabled

}

The context to run a trace

val make_context : ?⁠dwarf:Dw.t -> State.t -> t

Build a context from a state

val expand_var : ctxt:t -> Base.Var.t -> Ast.no Ast.ty -> State.exp

Expand a Trace variable to a State expression, using the context

val map_var : ctxt:t -> Base.Var.t -> Ast.no Ast.ty -> State.var
val typing_enabled : ctxt:t -> bool

Tell if typing should enabled with this context

\ No newline at end of file +Context (read-dwarf.Trace.Context)

Module Trace.Context

This module provide the type for a context to run a trace

Any information that should be required to run a trace but is not part of the state itself should be added here

module SMap : sig ... end
type t = {
  1. reg_writes : (State.Reg.t * State.tval) Utils.Vec.t;
    (*

    Stores the delayed register writes

    *)
  2. mem_reads : State.tval Utils.HashVector.t;
    (*

    Stores the result of memory reads

    *)
  3. nondets : State.var Utils.HashVector.t;
    (*

    Stores the mapping of nondet variables

    *)
  4. state : State.t;
  5. segments : State.exp SMap.t;
  6. asserts : State.exp list;
  7. dwarf : Dw.t option;
    (*

    Optionally DWARF information. If present, typing is enabled

    *)
}

The context to run a trace

val make_context : + ?dwarf:Dw.t -> + ?relocation:Elf.Relocations.rel -> + State.t -> + t

Build a context from a state

val expand_var : ctxt:t -> Base.Var.t -> Ast.no Ast.ty -> State.exp

Expand a Trace variable to a State expression, using the context

val typing_enabled : ctxt:t -> bool

Tell if typing should enabled with this context

module Z3St = State.Simplify.Z3St
val simplify : ctxt:t -> State.exp -> State.exp
diff --git a/doc/html/read-dwarf/Trace/Instr/index.html b/doc/html/read-dwarf/Trace/Instr/index.html index 51bd4a64..52634f13 100644 --- a/doc/html/read-dwarf/Trace/Instr/index.html +++ b/doc/html/read-dwarf/Trace/Instr/index.html @@ -1,2 +1,5 @@ -Instr (read-dwarf.Trace.Instr)

Module Trace.Instr

This module provide the representation of an instruction. It only contains generic information about the opcode and not specific information about a place in the code.

module Reg = State.Reg
type trace_meta = {
trace : Base.t;
jump_target : Base.exp option;
read : Reg.t list;
written : Reg.t list;
}

A simple trace with its metadata

If there is a WriteReg {reg;value} event in trace where reg = Arch.pc (), jump = Some value, otherwise it is None. If there are multiple such events, it will store the value of the last one.

type t = {
traces : trace_meta list;
length : int;

Bytes length

read : Reg.t list;
written : Reg.t list;
opcode : Utils.BytesSeq.t;
}

A full instruction representation

val dedup_regs : State.Reg.t list -> State.Reg.t list
val footprint : t -> State.Reg.t list
val trace_meta_of_trace : Base.t -> trace_meta

Compute the metadata of trace

val of_traces : Utils.BytesSeq.t -> Base.t list -> t

Generate full instruction data from a list of traces

val pp : t -> Utils.Pp.document

Pretty print the representation of an instruction

\ No newline at end of file +Instr (read-dwarf.Trace.Instr)

Module Trace.Instr

This module provide the representation of an instruction. It only contains generic information about the opcode and not specific information about a place in the code.

module Reg = State.Reg
type trace_meta = {
  1. trace : Base.t;
  2. jump_target : Base.exp option;
  3. read : Reg.t list;
  4. 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.

module SMap : sig ... end
type t = {
  1. traces : trace_meta list;
  2. length : int;
    (*

    Bytes length

    *)
  3. read : Reg.t list;
  4. written : Reg.t list;
  5. opcode : Utils.BytesSeq.t;
  6. relocation : Elf.Relocations.rel option;
}

A full instruction representation

val dedup_regs : State.Reg.t list -> State.Reg.t list
val footprint : t -> State.Reg.t list
val trace_meta_of_trace : Base.t -> trace_meta

Compute the metadata of trace

val of_traces : + (Utils.BytesSeq.t * Elf.Relocations.rel option) -> + Base.t list -> + t

Generate full instruction data from a list of traces

val pp : t -> Utils.Pp.document

Pretty print the representation of an instruction

diff --git a/doc/html/read-dwarf/Trace/Run/index.html b/doc/html/read-dwarf/Trace/Run/index.html index 4da88007..2b54366d 100644 --- a/doc/html/read-dwarf/Trace/Run/index.html +++ b/doc/html/read-dwarf/Trace/Run/index.html @@ -1,2 +1,18 @@ -Run (read-dwarf.Trace.Run)

Module Trace.Run

This module is for running trace from Trace like Isla.Run runs Isla traces.

Due to the semantic of a register access being the register at the beginning of the trace, all register writes are not done immediately but delayed and stored in the context.

Typing is enabled if Context.typing_enabled returns true for functions that take a context. For other functions, typing is enabled if the dwarf optional argument is passed

module Ctxt = Context
type ctxt = Ctxt.t
val expand : ctxt:ctxt -> Base.exp -> State.exp

Expand a Trace expression to a State expression, using the context

val expand_tval : ctxt:ctxt -> Base.exp -> State.tval

Expand a Trace expression to a typed State expression, using the context.

If the context enables typing, the expression will actually be typed, otherwise the type will be None

val event_mut : ctxt:ctxt -> Base.event -> unit

Run the event. The modified state is the one inside ctxt.

val trace_mut : ?⁠dwarf:Dw.t -> State.t -> Base.t -> unit

Run a trace on the provided state by mutation. Enable typing if dwarf is provided

val trace : ?⁠dwarf:Dw.t -> State.t -> Base.t -> State.t

Run a trace on the provided state by returning an updated copy.

val trace_pc_mut : ?⁠dwarf:Dw.t -> next:int -> State.t -> Base.t -> unit

Run a trace by mutating the provided state including it's PC. If the trace modified the PC then nothing is done otherwise next is added to it.

Thus this function automatically handle moving the PC for fall-through instruction

\ No newline at end of file +Run (read-dwarf.Trace.Run)

Module Trace.Run

This module is for running trace from Trace like Isla.Run runs Isla traces.

Due to the semantic of a register access being the register at the beginning of the trace, all register writes are not done immediately but delayed and stored in the context.

Typing is enabled if Context.typing_enabled returns true for functions that take a context. For other functions, typing is enabled if the dwarf optional argument is passed

module Ctxt = Context
type ctxt = Ctxt.t
val expand : ctxt:ctxt -> Base.exp -> State.exp

Expand a Trace expression to a State expression, using the context

val expand_simplify : ctxt:ctxt -> Base.exp -> State.exp
val expand_tval : ctxt:ctxt -> Base.exp -> State.tval

Expand a Trace expression to a typed State expression, using the context.

If the context enables typing, the expression will actually be typed, otherwise the type will be None

val event_mut : ctxt:ctxt -> Base.event -> unit

Run the event. The modified state is the one inside ctxt.

val trace_mut : + ?dwarf:Dw.t -> + ?relocation:Elf.Relocations.rel -> + State.t -> + Base.t -> + unit

Run a trace on the provided state by mutation. Enable typing if dwarf is provided

val trace : + ?dwarf:Dw.t -> + ?relocation:Elf.Relocations.rel -> + State.t -> + Base.t -> + State.t

Run a trace on the provided state by returning an updated copy.

val trace_pc_mut : + ?dwarf:Dw.t -> + ?relocation:Elf.Relocations.rel -> + next:int -> + State.t -> + Base.t -> + unit

Run a trace by mutating the provided state including it's PC. If the trace modified the PC then nothing is done otherwise next is added to it.

Thus this function automatically handle moving the PC for fall-through instruction

diff --git a/doc/html/read-dwarf/Trace/index.html b/doc/html/read-dwarf/Trace/index.html index 2e849dff..a7567ffe 100644 --- a/doc/html/read-dwarf/Trace/index.html +++ b/doc/html/read-dwarf/Trace/index.html @@ -1,3 +1,12 @@ -Trace (read-dwarf.Trace)

Module Trace

include Base
module Var = Base.Var

This module contains variable used in traces

module ExpPp = Exp.Pp

A trace expression. No let bindings, no memory operations

module Typed = Exp.Typed
module Exp = Base.Exp
type exp = Exp.t
type event =
| WriteReg of {
reg : State.Reg.t;
value : exp;
}
| ReadMem of {
addr : exp;
value : int;
size : Ast.Size.t;
}
| WriteMem of {
addr : exp;
value : exp;
size : Ast.Size.t;
}
| Assert of exp

The event type. See the module description for more details

type t = event list
val iter_var : (Exp.var -> unit) -> event list -> unit

Pretty printing

val pp_exp : ('aVar.tAst.noAst.no) Ast.exp -> Utils.Pp.document

Pretty print an expression

val pp_event : event -> Utils.Pp.document

Pretty print an event

val pp : event list -> PPrintEngine.document

Pretty print a trace

Isla to Trace conversion

This section perform the conversion from Isla trace to the traces of this module.

The conversion is generrally obvious, however there is subtlety: If the Isla trace reads a register after having written it, then the read produce the written expression instead of just the symbolic value of that register. That why there is a written_registers parameter to some function of this section.

exception OfIslaError

Throw an error in case of local conversion error. Normally a type-checked Isla trace should not fail in this section

type value_context = exp Utils.HashVector.t

The context mapping Isla variable numbers to trace expression

val get_var : 'a Utils.HashVector.t -> int -> 'a

Get the exression of the variable at the index. Throw OfIslaError if the variable is not bound

val exp_conv_subst : value_context -> Isla.rexp -> exp

Convert an Isla expression to a Trace expression by replacing all Isla variable by their value in the context. Throw OfIslaError if the substitution fails

val exp_of_valu : Isla_lang.AST.lrng -> exp Utils.HashVector.t -> Isla.valu -> exp

Convert an Isla.valu in a expression

val write_to_valu : 'a Utils.HashVector.t -> Isla.valu -> 'a -> unit

Write an expression to an Isla.valu

val event_of_isla : written_registers:(State.Reg.texp) Stdlib.Hashtbl.t -> read_counter:Utils.Counter.t -> vc:value_context -> Isla.revent -> event option

Convert an isla event to optionally a Trace event, most events are deleted

val of_isla : Isla.rtrc -> t

Top level function to convert an isla trace to one of this module

Trace simplification

module SimpContext = Base.SimpContext

A instance of Z3.ContextCounter.

module Z3Tr = Base.Z3Tr
module VarTbl = Base.VarTbl
val declare_non_det : Z3.server -> event list -> unit
val simplify : event list -> event list

Simplify a trace by using Z3. Perform both local expression simplification and global assertion removal (when an assertion is always true)

module Base : sig ... end

This module defines a new simplified kind of trace to replace Isla traces in the later stages of the instruction semantics processing.

module Cache : sig ... end

This module provides a caching system for fully processed traces

module Context : sig ... end

This module provide the type for a context to run a trace

module Ctype = Ctype
module Instr : sig ... end

This module provide the representation of an instruction. It only contains generic information about the opcode and not specific information about a place in the code.

module Run : sig ... end

This module is for running trace from Trace like Isla.Run runs Isla traces.

\ No newline at end of file +Trace (read-dwarf.Trace)

Module Trace

include module type of struct include Base end
module Var = Base.Var

This module contains variable used in traces

module ExpPp = Exp.Pp

A trace expression. No let bindings, no memory operations

module Typed = Exp.Typed
module Exp = Base.Exp
type exp = Exp.t
type event = Base.event =
  1. | WriteReg of {
    1. reg : State.Reg.t;
    2. value : exp;
    }
  2. | ReadMem of {
    1. addr : exp;
    2. value : int;
    3. size : Ast.Size.t;
    }
  3. | WriteMem of {
    1. addr : exp;
    2. value : exp;
    3. size : Ast.Size.t;
    }
  4. | Assert of exp

The event type. See the module description for more details

type t = event list
val iter_var : (Exp.var -> unit) -> event list -> unit

Pretty printing

val pp_exp : ('a, Var.t, Ast.no, Ast.no) Ast.exp -> Utils.Pp.document

Pretty print an expression

val pp_event : event -> Utils.Pp.document

Pretty print an event

val pp : event list -> Utils.Pp.document

Pretty print a trace

Isla to Trace conversion

This section perform the conversion from Isla trace to the traces of this module.

The conversion is generrally obvious, however there is subtlety: If the Isla trace reads a register after having written it, then the read produce the written expression instead of just the symbolic value of that register. That why there is a written_registers parameter to some function of this section.

exception OfIslaError

Throw an error in case of local conversion error. Normally a type-checked Isla trace should not fail in this section

type value_context = exp Utils.HashVector.t

The context mapping Isla variable numbers to trace expression

val get_var : 'a Utils.HashVector.t -> int -> 'a

Get the exression of the variable at the index. Throw OfIslaError if the variable is not bound

val exp_conv_subst : value_context -> Isla.rexp -> exp

Convert an Isla expression to a Trace expression by replacing all Isla variable by their value in the context. Throw OfIslaError if the substitution fails

val exp_of_valu : + Isla_lang.AST.lrng -> + exp Utils.HashVector.t -> + Isla.valu -> + exp

Convert an Isla.valu in a expression

val write_to_valu : 'a Utils.HashVector.t -> Isla.valu -> 'a -> unit

Write an expression to an Isla.valu

val events_of_isla : + segments_map:(string * int) Utils.HashVector.t -> + written_registers:(State.Reg.t, exp) Stdlib.Hashtbl.t -> + read_counter:Utils.Counter.t -> + vc:value_context -> + Isla.revent -> + event list

Convert an isla event to Trace events, most events are deleted

val of_isla : Isla.segment list -> Isla.rtrc -> t

Top level function to convert an isla trace to one of this module

Trace simplification

module SimpContext = Base.SimpContext

A instance of Z3.ContextCounter.

module Z3Tr = Base.Z3Tr
module VarTbl = Base.VarTbl
val declare_non_det : Z3.server -> event list -> unit
val simplify : event list -> event list

Simplify a trace by using Z3. Perform both local expression simplification and global assertion removal (when an assertion is always true)

module Base : sig ... end

This module defines a new simplified kind of trace to replace Isla traces in the later stages of the instruction semantics processing.

module Cache : sig ... end

This module provides a caching system for fully processed traces

module Context : sig ... end

This module provide the type for a context to run a trace

module Ctype = Ctype
module Instr : sig ... end

This module provide the representation of an instruction. It only contains generic information about the opcode and not specific information about a place in the code.

module Run : sig ... end

This module is for running trace from Trace like Isla.Run runs Isla traces.

diff --git a/doc/html/read-dwarf/TypeInference.html b/doc/html/read-dwarf/TypeInference.html index da6562d8..9f797dc8 100644 --- a/doc/html/read-dwarf/TypeInference.html +++ b/doc/html/read-dwarf/TypeInference.html @@ -1,2 +1,2 @@ -TypeInference (read-dwarf.TypeInference)

Type Inference

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.

\ No newline at end of file +TypeInference (read-dwarf.TypeInference)

Type Inference

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.

diff --git a/doc/html/read-dwarf/Utilities.html b/doc/html/read-dwarf/Utilities.html index f5a90eb8..32f7a16c 100644 --- a/doc/html/read-dwarf/Utilities.html +++ b/doc/html/read-dwarf/Utilities.html @@ -1,2 +1,2 @@ -Utilities (read-dwarf.Utilities)

Utilities

Standard library extensions

Bits and Bytes manipulation

  • Bits: Intends to provide the same interface as Stdlib.Bytes but at the individual bit level. The external type is explicitely bytes.
  • IntBits: Same interface as Bits but on an integer. This provides understandable ways of moving set of bits around directly in integers without having to think about shifts.
  • BitVec: Concrete bitvector library based on zarith.
  • BytesSeq: Implement a view over a bytes. This view can be restricted in a pure interface without requiring to copy the bytes.
  • RngMap: Map structure that allow to index value by range of addresses. You can bind a whole interval of addresses to a value.

Resizable array and other integer-indexed data structures

  • Vec: Main resizable array module. Layer on top of the res library.
  • FullVec: Conceptually a array/vector that binds all the integers to values. It's implemented by a vector and a generator.
  • HashVector: A Data structure to use a vector as small int to something hashtable. Implemented as a 'a option Vec.t
  • IdMap: A hashmap that numbers the bindings such that each binding can be identified by either the key of the integer identifier. Useful for doing symbol numbering for example.
  • Counter: Just an integer counter to index something. It has a Counter.get function that give the next integer each time it's called.

Weak data structures

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.

Exception management

  • Protect: An improvement over protect.
  • Raise: Convenience function to raise and manage exception in an easier way.

File and socket management

  • Files: Various IO facilities around IO channel and file management
  • Cmd: 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.
\ No newline at end of file +Utilities (read-dwarf.Utilities)

Utilities

Standard library extensions

Bits and Bytes manipulation

  • Bits: Intends to provide the same interface as Stdlib.Bytes but at the individual bit level. The external type is explicitely bytes.
  • IntBits: Same interface as Bits but on an integer. This provides understandable ways of moving set of bits around directly in integers without having to think about shifts.
  • BitVec: Concrete bitvector library based on zarith.
  • BytesSeq: Implement a view over a bytes. This view can be restricted in a pure interface without requiring to copy the bytes.
  • RngMap: Map structure that allow to index value by range of addresses. You can bind a whole interval of addresses to a value.

Resizable array and other integer-indexed data structures

  • Vec: Main resizable array module. Layer on top of the res library.
  • FullVec: Conceptually a array/vector that binds all the integers to values. It's implemented by a vector and a generator.
  • HashVector: A Data structure to use a vector as small int to something hashtable. Implemented as a 'a option Vec.t
  • IdMap: A hashmap that numbers the bindings such that each binding can be identified by either the key of the integer identifier. Useful for doing symbol numbering for example.
  • Counter: Just an integer counter to index something. It has a Counter.get function that give the next integer each time it's called.

Weak data structures

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.

Exception management

  • Protect: An improvement over protect.
  • Raise: Convenience function to raise and manage exception in an easier way.

File and socket management

  • Files: Various IO facilities around IO channel and file management
  • Cmd: 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.
diff --git a/doc/html/read-dwarf/Utils/Array/index.html b/doc/html/read-dwarf/Utils/Array/index.html index 333b75db..7071f575 100644 --- a/doc/html/read-dwarf/Utils/Array/index.html +++ b/doc/html/read-dwarf/Utils/Array/index.html @@ -1,2 +1,6 @@ -Array (read-dwarf.Utils.Array)

Module Utils.Array

include Stdlib.Array
type 'a t = 'a array
val length : 'a array -> int
val get : 'a array -> int -> 'a
val set : 'a array -> int -> 'a -> unit
val make : int -> 'a -> 'a array
val create : int -> 'a -> 'a array
val create_float : int -> float array
val make_float : int -> float array
val init : int -> (int -> 'a) -> 'a array
val make_matrix : int -> int -> 'a -> 'a array array
val create_matrix : int -> int -> 'a -> 'a array array
val append : 'a array -> 'a array -> 'a array
val concat : 'a array list -> 'a array
val sub : 'a array -> int -> int -> 'a array
val copy : 'a array -> 'a array
val fill : 'a array -> int -> int -> 'a -> unit
val blit : 'a array -> int -> 'a array -> int -> int -> unit
val to_list : 'a array -> 'a list
val of_list : 'a list -> 'a array
val iter : ('a -> unit) -> 'a array -> unit
val iteri : (int -> 'a -> unit) -> 'a array -> unit
val map : ('a -> 'b) -> 'a array -> 'b array
val mapi : (int -> 'a -> 'b) -> 'a array -> 'b array
val fold_left : ('a -> 'b -> 'a) -> 'a -> 'b array -> 'a
val fold_right : ('b -> 'a -> 'a) -> 'b array -> 'a -> 'a
val iter2 : ('a -> 'b -> unit) -> 'a array -> 'b array -> unit
val map2 : ('a -> 'b -> 'c) -> 'a array -> 'b array -> 'c array
val for_all : ('a -> bool) -> 'a array -> bool
val exists : ('a -> bool) -> 'a array -> bool
val mem : 'a -> 'a array -> bool
val memq : 'a -> 'a array -> bool
val sort : ('a -> 'a -> int) -> 'a array -> unit
val stable_sort : ('a -> 'a -> int) -> 'a array -> unit
val fast_sort : ('a -> 'a -> int) -> 'a array -> unit
val to_seq : 'a array -> 'a Stdlib.Seq.t
val to_seqi : 'a array -> (int * 'a) Stdlib.Seq.t
val of_seq : 'a Stdlib.Seq.t -> 'a array
val unsafe_get : 'a array -> int -> 'a
val unsafe_set : 'a array -> int -> 'a -> unit
module Floatarray = Stdlib__array.Floatarray
val map_mut : ('a -> 'a) -> 'a array -> unit

Mutate the array by replacing each value x by f x

val of_list_mapi : (int -> 'a -> 'b) -> 'a list -> 'b array

of_list_mapi f l = of_list (List.mapi f i l) = mapi f (of_list l)

val of_list_map : ('a -> 'b) -> 'a list -> 'b array

of_list_map f l = of_list (List.map f l) = map f (of_list l)

Array scanning

val find_pair : ('a -> bool) -> 'a array -> int * 'a

Find the first value satisfying the predicate and return it with its index. Throw Not_found if no value satisfies the predicate

val find : ('a -> bool) -> 'a array -> 'a

Find the first value satisfying the predicate. Throw Not_found if no value satisfies the predicate

val find_index : ('a -> bool) -> 'a array -> int

Find the first index whose value satisfies the predicate. Throw Not_found if no value satisfies the predicate

val find_all_pairs : ('a -> bool) -> 'a t -> (int * 'a) list

Find all the values satisfying the predicate and return them with their index.

val find_all : ('a -> bool) -> 'a t -> 'a list

Find all the values satisfying the predicate

val find_all_indices : ('a -> bool) -> 'a t -> int list

Find all the indices whose value satisfies the predicate

\ No newline at end of file +Array (read-dwarf.Utils.Array)

Module Utils.Array

This module is for extending the Array module of the standard library

include module type of struct include Stdlib.Array end
type !'a t = 'a array
val length : 'a array -> int
val get : 'a array -> int -> 'a
val set : 'a array -> int -> 'a -> unit
val make : int -> 'a -> 'a array
val create_float : int -> float array
val init : int -> (int -> 'a) -> 'a array
val make_matrix : int -> int -> 'a -> 'a array array
val append : 'a array -> 'a array -> 'a array
val concat : 'a array list -> 'a array
val sub : 'a array -> int -> int -> 'a array
val copy : 'a array -> 'a array
val fill : 'a array -> int -> int -> 'a -> unit
val blit : 'a array -> int -> 'a array -> int -> int -> unit
val to_list : 'a array -> 'a list
val of_list : 'a list -> 'a array
val iter : ('a -> unit) -> 'a array -> unit
val iteri : (int -> 'a -> unit) -> 'a array -> unit
val map : ('a -> 'b) -> 'a array -> 'b array
val map_inplace : ('a -> 'a) -> 'a array -> unit
val mapi : (int -> 'a -> 'b) -> 'a array -> 'b array
val mapi_inplace : (int -> 'a -> 'a) -> 'a array -> unit
val fold_left : ('acc -> 'a -> 'acc) -> 'acc -> 'a array -> 'acc
val fold_left_map : + ('acc -> 'a -> 'acc * 'b) -> + 'acc -> + 'a array -> + 'acc * 'b array
val fold_right : ('a -> 'acc -> 'acc) -> 'a array -> 'acc -> 'acc
val iter2 : ('a -> 'b -> unit) -> 'a array -> 'b array -> unit
val map2 : ('a -> 'b -> 'c) -> 'a array -> 'b array -> 'c array
val for_all : ('a -> bool) -> 'a array -> bool
val exists : ('a -> bool) -> 'a array -> bool
val for_all2 : ('a -> 'b -> bool) -> 'a array -> 'b array -> bool
val exists2 : ('a -> 'b -> bool) -> 'a array -> 'b array -> bool
val mem : 'a -> 'a array -> bool
val memq : 'a -> 'a array -> bool
val find_opt : ('a -> bool) -> 'a array -> 'a option
val find_map : ('a -> 'b option) -> 'a array -> 'b option
val find_mapi : (int -> 'a -> 'b option) -> 'a array -> 'b option
val split : ('a * 'b) array -> 'a array * 'b array
val combine : 'a array -> 'b array -> ('a * 'b) array
val sort : ('a -> 'a -> int) -> 'a array -> unit
val stable_sort : ('a -> 'a -> int) -> 'a array -> unit
val fast_sort : ('a -> 'a -> int) -> 'a array -> unit
val to_seq : 'a array -> 'a Stdlib.Seq.t
val to_seqi : 'a array -> (int * 'a) Stdlib.Seq.t
val of_seq : 'a Stdlib.Seq.t -> 'a array
val unsafe_get : 'a array -> int -> 'a
val unsafe_set : 'a array -> int -> 'a -> unit
module Floatarray : sig ... end
val map_mut : ('a -> 'a) -> 'a array -> unit

Mutate the array by replacing each value x by f x

val of_list_mapi : (int -> 'a -> 'b) -> 'a list -> 'b array

of_list_mapi f l = of_list (List.mapi f i l) = mapi f (of_list l)

val of_list_map : ('a -> 'b) -> 'a list -> 'b array

of_list_map f l = of_list (List.map f l) = map f (of_list l)

Array scanning

val find_pair : ('a -> bool) -> 'a array -> int * 'a

Find the first value satisfying the predicate and return it with its index. Throw Not_found if no value satisfies the predicate

val find : ('a -> bool) -> 'a array -> 'a

Find the first value satisfying the predicate. Throw Not_found if no value satisfies the predicate

val find_index : ('a -> bool) -> 'a array -> int

Find the first index whose value satisfies the predicate. Throw Not_found if no value satisfies the predicate

val find_all_pairs : ('a -> bool) -> 'a t -> (int * 'a) list

Find all the values satisfying the predicate and return them with their index.

val find_all : ('a -> bool) -> 'a t -> 'a list

Find all the values satisfying the predicate

val find_all_indices : ('a -> bool) -> 'a t -> int list

Find all the indices whose value satisfies the predicate

diff --git a/doc/html/read-dwarf/Utils/BitVec/index.html b/doc/html/read-dwarf/Utils/BitVec/index.html index 8a7ca738..f788545d 100644 --- a/doc/html/read-dwarf/Utils/BitVec/index.html +++ b/doc/html/read-dwarf/Utils/BitVec/index.html @@ -1,2 +1,8 @@ -BitVec (read-dwarf.Utils.BitVec)

Module Utils.BitVec

type t

The type of a bitvector

exception SizeMismatch of int * int

Raise when the runtime size do not match on operation that require so (like add)

val size : t -> int

The size of the bitvector

val zero : size:int -> t

The bitvector representing 0 of specified size

val one : size:int -> t

The bitvector representing 1 of specified size

val minus_one : size:int -> t

The bitvector representing -1 of specified size

Integer conversions

val to_z : t -> Z.t

To a signed big integer

val to_uz : t -> Z.t

To an unsigned big integer

val of_z : size:int -> Z.t -> t

Of bit integer. Wrapped modulo 2^size.

val to_int : t -> int

To a signed integer. Fail if it doesn't fit

val to_uint : t -> int

To an unsigned integer. Fail if it doesn't fit without wrapping i.e the result is still positive

val of_int : size:int -> int -> t

Of integer. Wrapped modulo size.

val to_bool : t -> bool

Convert a one size bitvector to bool. Throw SizeMismatch if the bitvector is not one-sized

val of_bool : bool -> t

Create a one-sized bitvector representing the boolean

Bytes conversions

val to_bytes : t -> bytes

Return the shortest bytes that represent the bitvector in little-endian. There may be extra bits (if size is not a multiple of 8) which are zeros.

This bytes may be shorter that the bitvector size, for example the bitvector 1 of size 64bits, will still be returned by this function as a single byte 1. For another behavior, see to_bytes_exact.

val to_bytes_exact : t -> bytes

Return a bytes representation of mininal length to encompass the whole bitvector size. Extra bits (if size is not a multiple of 8) are zeros.

val bytes_store : bytes -> int -> t -> unit

Store the bitvector in the bytes at the specified offset in little endian. The bitvector size must be a multiple of 8 or Invalid_argument is thrown

val of_bytes : size:int -> bytes -> t

Read a bitvector from a bytes data (little endian)

val bytes_load : size:int -> bytes -> int -> t

Load a bitvector of size bits from the bytes at the specified offset (little endian). size must be a multiple of 8 or Invalid_argument is thrown

String conversions, printing

val of_string : ?⁠base:int -> size:int -> string -> t

Parse a string with specified base (10 if unspecified) and return a bitvector of size size. If the string is too big, the integer is still parsed and then wrapped modulo 2^size

val of_substring : ?⁠base:int -> size:int -> pos:int -> len:int -> string -> t

Same as of_string but on the substring starting at pos of length len.

val to_string : ?⁠base:int -> ?⁠unsigned:bool -> ?⁠force_width:bool -> ?⁠prefix:bool -> t -> string

Convert the value to a string representation in the specified base.

base can only be 2, 8, 10 or 16, otherwise the function fails.

Set unsigned to true to have unsigned values (signed by default).

Set prefix to true to have the 0x/0o/0b prefix (no prefix by default)

Set force_width to false to not have a digit length matching the bitvector length, otherwise leading zeros will be inserted to match the length.

val of_smt : string -> t

Convert a bitvector in the SMTLib format to a t

val to_smt : t -> string

Convert a bitvector to the SMTLib format

val pp_smt : t -> Utils.Pp.document

Print a bitvector with the SMTLib format

Arithmetic

val add : t -> t -> t

Add the values. Result is of the same size as the inputs.

Throw SizeMismatch if sizes differ

val sub : t -> t -> t

Subtract the values. Result is of the same size as the inputs.

Throw SizeMismatch if sizes differ

val neg : t -> t

Negate the value. Wrap if the value is the smaller integer (It will stay the smallest integer)

val mul : t -> t -> t

Multiply the values. Result is of the same size as the inputs.

Throw SizeMismatch if sizes differ

val sdiv : t -> t -> t

Divide the values as signed integers. Result is of the same size as the inputs.

It rounds the result toward zero.

Throw SizeMismatch if sizes differ.

Throw Division_by_zero if there is a division by zero.

val srem : t -> t -> t

Take the remainder of the signed division. Result is of the same size as the inputs.

a = sdiv a b * b + srem a b

Throw SizeMismatch if sizes differ.

Throw Division_by_zero if there is a division by zero.

val smod : t -> t -> t

Take the signed modulo. The result has the sign of the divisor. Result is of the same size as the inputs.

Throw SizeMismatch if sizes differ.

Throw Division_by_zero if there is a division by zero.

val udiv : t -> t -> t

Divide the values as unsigned integers. Result is of the same size as the inputs.

Throw SizeMismatch if sizes differ

Throw Division_by_zero if there is a division by zero.

val urem : t -> t -> t

Get the remainder of the unsigned division. Result if of the same size as the inputs.

a = udiv a b * b + urem a b

Throw SizeMismatch if sizes differ

Throw Division_by_zero if there is a division by zero.

Bit manipulation

val logand : t -> t -> t

Bitwise and of the values. Result is of the same size as the inputs.

Throw SizeMismatch if sizes differ

val logor : t -> t -> t

Bitwise or of the values. Result is of the same size as the inputs.

Throw SizeMismatch if sizes differ

val logxor : t -> t -> t

Bitwise xor of the values. Result is of the same size as the inputs.

Throw SizeMismatch if sizes differ

val lognot : t -> t

Bitwise not of the value. Result is of the same size as the input.

val redor : t -> bool

Do an or of all the bits in the bitvector

val redand : t -> bool

Do an and of all the bits in the bitvector

val shift_left : t -> int -> t

Do a left shift. The second argument must be non-negative

val shift_left_bv : t -> t -> t

Same as shift_left but the second argument is also a bitvector of any size interpreted as unsigned

val shift_right_arith : t -> int -> t

Do an arithmetic right shift (copy the sign bit). The second argument must be non-negative

val shift_right_arith_bv : t -> t -> t

Same as shift_right_arith but the second argument is also a bitvector of any size interpreted as unsigned

val shift_right_logic : t -> int -> t

Do an logical right shift (insert zeroes). The second argument must be non-negative

val shift_right_logic_bv : t -> t -> t

Same as shift_right_logic but the second argument is also a bitvector of any size interpreted as unsigned

val concat : t -> t -> t

Concatenates the bitvectors

val extract : int -> int -> t -> t

extract bv a b extract bits a to b included from bv. Indices start at 0

val zero_extend : int -> t -> t

Add the second argument of zeroes to the left

val sign_extend : int -> t -> t

Copy the bit sign as much as specified by the integer on the left

Infix operators

Divisions do not have any operators because signed and unsigned division have different semantics

val (+) : t -> t -> t

add

val (-) : t -> t -> t

sub

val (*) : t -> t -> t

mul

val (~-) : t -> t

neg

val (lsl) : t -> t -> t

shift_left_bv

val asl : t -> t -> t

shift_left_bv

val (lsr) : t -> t -> t

shift_right_logic_bv

val (asr) : t -> t -> t

shift_right_arith_bv

val (lnot) : t -> t

lognot

val (land) : t -> t -> t

logand

val (lor) : t -> t -> t

logor

val (lxor) : t -> t -> t

logxor

\ No newline at end of file +BitVec (read-dwarf.Utils.BitVec)

Module Utils.BitVec

This module provides an interface for a bit vector of dynamic size.

For now this is entirely based on zarith.

TODO: It could be nice to export this as a separate library on opam at some point

The value of type t is semantically pure and can be compare with polymorphic operators. It will compare the size first, then the value.

The size of bit vectors must always be strictly positive.

type t

The type of a bitvector

exception SizeMismatch of int * int

Raise when the runtime size do not match on operation that require so (like add)

val size : t -> int

The size of the bitvector

val zero : size:int -> t

The bitvector representing 0 of specified size

val one : size:int -> t

The bitvector representing 1 of specified size

val minus_one : size:int -> t

The bitvector representing -1 of specified size

Integer conversions

val to_z : t -> Z.t

To a signed big integer

val to_uz : t -> Z.t

To an unsigned big integer

val of_z : size:int -> Z.t -> t

Of bit integer. Wrapped modulo 2^size.

val to_int : t -> int

To a signed integer. Fail if it doesn't fit

val to_uint : t -> int

To an unsigned integer. Fail if it doesn't fit without wrapping i.e the result is still positive

val of_int : size:int -> int -> t

Of integer. Wrapped modulo size.

val to_bool : t -> bool

Convert a one size bitvector to bool. Throw SizeMismatch if the bitvector is not one-sized

val of_bool : bool -> t

Create a one-sized bitvector representing the boolean

Bytes conversions

val to_bytes : t -> bytes

Return the shortest bytes that represent the bitvector in little-endian. There may be extra bits (if size is not a multiple of 8) which are zeros.

This bytes may be shorter that the bitvector size, for example the bitvector 1 of size 64bits, will still be returned by this function as a single byte 1. For another behavior, see to_bytes_exact.

val to_bytes_exact : t -> bytes

Return a bytes representation of mininal length to encompass the whole bitvector size. Extra bits (if size is not a multiple of 8) are zeros.

val bytes_store : bytes -> int -> t -> unit

Store the bitvector in the bytes at the specified offset in little endian. The bitvector size must be a multiple of 8 or Invalid_argument is thrown

val of_bytes : size:int -> bytes -> t

Read a bitvector from a bytes data (little endian)

val bytes_load : size:int -> bytes -> int -> t

Load a bitvector of size bits from the bytes at the specified offset (little endian). size must be a multiple of 8 or Invalid_argument is thrown

String conversions, printing

val of_string : ?base:int -> size:int -> string -> t

Parse a string with specified base (10 if unspecified) and return a bitvector of size size. If the string is too big, the integer is still parsed and then wrapped modulo 2^size

val of_substring : ?base:int -> size:int -> pos:int -> len:int -> string -> t

Same as of_string but on the substring starting at pos of length len.

val to_string : + ?base:int -> + ?unsigned:bool -> + ?force_width:bool -> + ?prefix:bool -> + t -> + string

Convert the value to a string representation in the specified base.

base can only be 2, 8, 10 or 16, otherwise the function fails.

Set unsigned to true to have unsigned values (signed by default).

Set prefix to true to have the 0x/0o/0b prefix (no prefix by default)

Set force_width to false to not have a digit length matching the bitvector length, otherwise leading zeros will be inserted to match the length.

val of_smt : string -> t

Convert a bitvector in the SMTLib format to a t

val to_smt : t -> string

Convert a bitvector to the SMTLib format

val pp_smt : t -> Pp.document

Print a bitvector with the SMTLib format

Arithmetic

val add : t -> t -> t

Add the values. Result is of the same size as the inputs.

Throw SizeMismatch if sizes differ

val sub : t -> t -> t

Subtract the values. Result is of the same size as the inputs.

Throw SizeMismatch if sizes differ

val neg : t -> t

Negate the value. Wrap if the value is the smaller integer (It will stay the smallest integer)

val mul : t -> t -> t

Multiply the values. Result is of the same size as the inputs.

Throw SizeMismatch if sizes differ

val sdiv : t -> t -> t

Divide the values as signed integers. Result is of the same size as the inputs.

It rounds the result toward zero.

Throw SizeMismatch if sizes differ.

Throw Division_by_zero if there is a division by zero.

val srem : t -> t -> t

Take the remainder of the signed division. Result is of the same size as the inputs.

a = sdiv a b * b + srem a b

Throw SizeMismatch if sizes differ.

Throw Division_by_zero if there is a division by zero.

val smod : t -> t -> t

Take the signed modulo. The result has the sign of the divisor. Result is of the same size as the inputs.

Throw SizeMismatch if sizes differ.

Throw Division_by_zero if there is a division by zero.

val udiv : t -> t -> t

Divide the values as unsigned integers. Result is of the same size as the inputs.

Throw SizeMismatch if sizes differ

Throw Division_by_zero if there is a division by zero.

val urem : t -> t -> t

Get the remainder of the unsigned division. Result if of the same size as the inputs.

a = udiv a b * b + urem a b

Throw SizeMismatch if sizes differ

Throw Division_by_zero if there is a division by zero.

Bit manipulation

val logand : t -> t -> t

Bitwise and of the values. Result is of the same size as the inputs.

Throw SizeMismatch if sizes differ

val logor : t -> t -> t

Bitwise or of the values. Result is of the same size as the inputs.

Throw SizeMismatch if sizes differ

val logxor : t -> t -> t

Bitwise xor of the values. Result is of the same size as the inputs.

Throw SizeMismatch if sizes differ

val lognot : t -> t

Bitwise not of the value. Result is of the same size as the input.

val redor : t -> bool

Do an or of all the bits in the bitvector

val redand : t -> bool

Do an and of all the bits in the bitvector

val shift_left : t -> int -> t

Do a left shift. The second argument must be non-negative

val shift_left_bv : t -> t -> t

Same as shift_left but the second argument is also a bitvector of any size interpreted as unsigned

val shift_right_arith : t -> int -> t

Do an arithmetic right shift (copy the sign bit). The second argument must be non-negative

val shift_right_arith_bv : t -> t -> t

Same as shift_right_arith but the second argument is also a bitvector of any size interpreted as unsigned

val shift_right_logic : t -> int -> t

Do an logical right shift (insert zeroes). The second argument must be non-negative

val shift_right_logic_bv : t -> t -> t

Same as shift_right_logic but the second argument is also a bitvector of any size interpreted as unsigned

val concat : t -> t -> t

Concatenates the bitvectors

val extract : int -> int -> t -> t

extract bv a b extract bits a to b included from bv. Indices start at 0

val zero_extend : int -> t -> t

Add the second argument of zeroes to the left

val sign_extend : int -> t -> t

Copy the bit sign as much as specified by the integer on the left

Infix operators

Divisions do not have any operators because signed and unsigned division have different semantics

val (+) : t -> t -> t
val (-) : t -> t -> t
val (*) : t -> t -> t
val (~-) : t -> t
val (lsl) : t -> t -> t
val asl : t -> t -> t
val (lsr) : t -> t -> t
val (asr) : t -> t -> t
val lnot : t -> t
val (land) : t -> t -> t
val (lor) : t -> t -> t
val (lxor) : t -> t -> t
diff --git a/doc/html/read-dwarf/Utils/Bits/index.html b/doc/html/read-dwarf/Utils/Bits/index.html index df597c86..6737becc 100644 --- a/doc/html/read-dwarf/Utils/Bits/index.html +++ b/doc/html/read-dwarf/Utils/Bits/index.html @@ -1,2 +1,2 @@ -Bits (read-dwarf.Utils.Bits)

Module Utils.Bits

module Int = IntBits
val check_index : int -> int -> unit

check_index length i Check that the index i is valid to index an bytes of length length. Throw Invalid_argument if not

val check_range : int -> int -> int -> unit

check_range length i l Check that the range [i;i+l) is inside a bytes of length length. Throw Invalid_argument if not

val length : bytes -> int

Gives the length of a bytes in bits

val create : int -> bytes

Create a bytes large enough to store the specified amount of bits

val make : int -> bool -> bytes

Create a bytes large enough to store the specified amount of bits and initialize them as specified by the boolean

val unsafe_get : bytes -> int -> bool

Unsafe version of get

val get : bytes -> int -> bool

Get a bit at a specific index. See unsafe_get

val unsafe_set : bytes -> int -> unit

Unsafe version of set

val set : bytes -> int -> unit

Set a bit at a specific index. See unsafe_set

val unsafe_clear : bytes -> int -> unit

Unsafe version of clear

val clear : bytes -> int -> unit

Clear a bit at a specific index. See unsafe_clear

val unsafe_setb : bytes -> int -> bool -> unit

Unsafe version of setb

val setb : bytes -> int -> bool -> unit

Set a bit at a specific index according to a boolean. See unsafe_setb

val unsafe_blit_to_int : bytes -> int -> Int.t -> int -> int -> Int.t

Unsafe version of blit_to_int

val blit_to_int : bytes -> int -> Int.t -> int -> int -> Int.t

blit_to_int src isrc dest idest len blits the bits in range [isrc;isrc+len) of src to the range [idest;idest + len) of dest and returns the result. See unsafe_blit_to_int.

val unsafe_blit_of_int : Int.t -> int -> bytes -> int -> int -> unit

Unsafe version of blit_of_int

val blit_of_int : Int.t -> int -> bytes -> int -> int -> unit

blit_of_int src isrc dest idest len blits the bits in range [isrc;isrc+len) or src to the range [idest;idest + len) of dest by mutation. See unsafe_blit_of_int.

\ No newline at end of file +Bits (read-dwarf.Utils.Bits)

Module Utils.Bits

Like bytes, but for bit level manipulation. The underlying type is still bytes and thus the size has to be a multiple of 8.

The indexing is little endian: bit 9 is least significant bit of byte 1

module Int = IntBits
val check_index : int -> int -> unit

check_index length i Check that the index i is valid to index an bytes of length length. Throw Invalid_argument if not

val check_range : int -> int -> int -> unit

check_range length i l Check that the range [i;i+l) is inside a bytes of length length. Throw Invalid_argument if not

val length : bytes -> int

Gives the length of a bytes in bits

val create : int -> bytes

Create a bytes large enough to store the specified amount of bits

val make : int -> bool -> bytes

Create a bytes large enough to store the specified amount of bits and initialize them as specified by the boolean

val unsafe_get : bytes -> int -> bool

Unsafe version of get

val get : bytes -> int -> bool

Get a bit at a specific index. See unsafe_get

val unsafe_set : bytes -> int -> unit

Unsafe version of set

val set : bytes -> int -> unit

Set a bit at a specific index. See unsafe_set

val unsafe_clear : bytes -> int -> unit

Unsafe version of clear

val clear : bytes -> int -> unit

Clear a bit at a specific index. See unsafe_clear

val unsafe_setb : bytes -> int -> bool -> unit

Unsafe version of setb

val setb : bytes -> int -> bool -> unit

Set a bit at a specific index according to a boolean. See unsafe_setb

val unsafe_blit_to_int : bytes -> int -> Int.t -> int -> int -> Int.t

Unsafe version of blit_to_int

val blit_to_int : bytes -> int -> Int.t -> int -> int -> Int.t

blit_to_int src isrc dest idest len blits the bits in range [isrc;isrc+len) of src to the range [idest;idest + len) of dest and returns the result. See unsafe_blit_to_int.

val unsafe_blit_of_int : Int.t -> int -> bytes -> int -> int -> unit

Unsafe version of blit_of_int

val blit_of_int : Int.t -> int -> bytes -> int -> int -> unit

blit_of_int src isrc dest idest len blits the bits in range [isrc;isrc+len) or src to the range [idest;idest + len) of dest by mutation. See unsafe_blit_of_int.

diff --git a/doc/html/read-dwarf/Utils/BytesSeq/index.html b/doc/html/read-dwarf/Utils/BytesSeq/index.html index a08e6fe0..95a6910d 100644 --- a/doc/html/read-dwarf/Utils/BytesSeq/index.html +++ b/doc/html/read-dwarf/Utils/BytesSeq/index.html @@ -1,2 +1,2 @@ -BytesSeq (read-dwarf.Utils.BytesSeq)

Module Utils.BytesSeq

type t = Byte_sequence_wrapper.byte_sequence

Type inherited from linksem

val length : t -> int

Get the length of the byteseq in bytes

val equal : t -> t -> bool

Check if two byte sequence are equal byte for byte

val int_bytes : int

The size in bytes of an Ocaml int

Hexadecimal conversions

val to_hex : t -> string

Convert the byte sequence to an hexadecimal string

val to_hex_rev : t -> string

Convert the byte sequence to an reversed hexadecimal string. This will print it like a big-endian integer.

val of_hex : string -> t

Parse the string as hexadecimal like A4B767DF and create a bytes of this a binary data and then a bytesSeq view of it

Cutting the view

val sub : t -> int -> int -> t

sub bs start len Extract a sub range [start:start+len) of a byte sequence. This is O(1)

val front : int -> t -> t

front i bs Take the first i bytes of bs and discard the rest. Equivalent to sub bs 0 i

val back : int -> t -> t

back i bs Take the last i bytes of bs and discard the rest. Equivalent to sub bs i (length bs - i)

Interaction with bytes and raw string

val blit : t -> int -> bytes -> int -> int -> unit

blit src srcoff dst dstoff len copies len bytes from bytes sequence src, starting at index srcoff, to bytes dst, starting at index dstoff.

See Bytes.blit.

val of_bytes : bytes -> t

Create a view of the whole bytes

val of_string : string -> t

Create a view of the whole string as raw bytes

val to_string : t -> string

Create a copy of the view in a string

val bytes_sub : bytes -> int -> int -> t

Create a byte sequence view of a specified range of a bytes. See sub

Array conversions

val to_array : t -> char array

Convert to a char array

val of_array : char array -> t

Convert from a char array

Getters

val get : t -> int -> char

Get the bytes at the offset in the byte sequence

val unsafe_get : t -> int -> char

Unsafe version of get

val get16le : t -> int -> int

Get a 16 bit integer at the offset in the byte sequence as little endian

val get16be : t -> int -> int

Get a 16 bit integer at the offset in the byte sequence as big endian

val get32le : t -> int -> int32

Get a 32 bit integer at the offset in the byte sequence as little endian

val get32be : t -> int -> int32

Get a 32 bit integer at the offset in the byte sequence as big endian

val get64le : t -> int -> int64

Get a 64 bit integer at the offset in the byte sequence as little endian

val get64be : t -> int -> int64

Get a 64 bit integer at the offset in the byte sequence as big endian

val getintle : t -> int -> int

Get an Ocaml int at the offset in the byte sequence as little endian. The number of bytes read is 4 if Sys.int_size is 31 and 8 if Sys.int_size is 63

val getbs : len:int -> t -> int -> t

Get a byte sequence of length len at the offset in another byte sequence

val getbvle : size:int -> t -> int -> BitVec.t

Get a BitVec of size size at the offset in the byte sequence as little endian

val getintle_ze : t -> int -> int

Get an Ocaml int at the offset in the byte sequence as little endian. The number of bytes read is 4 if Sys.int_size is 31 and 8 if Sys.int_size is 63. If the read goes beyond the end of the sequence, instead of failing, zeros are read.

Iterators

Iterators over a byte sequence. If the length of the byte sequence is not a multiple of the step of the iteration then the trailing odd bytes are not iterated over.

val iter : (char -> unit) -> t -> unit
val iter16le : (int -> unit) -> t -> unit
val iter16be : (int -> unit) -> t -> unit
val iter32le : (int32 -> unit) -> t -> unit
val iter32be : (int32 -> unit) -> t -> unit
val iter64le : (int64 -> unit) -> t -> unit
val iter64be : (int64 -> unit) -> t -> unit
val iterbs : len:int -> (t -> unit) -> t -> unit

Iterate over the byte sequence by bytesequence of length len. If the total byte sequence is not of length a multiple of len then that iterated value will be shorter

val fold_left : ('a -> char -> 'a) -> 'a -> t -> 'a
val fold_left16le : ('a -> int -> 'a) -> 'a -> t -> 'a
val fold_left16be : ('a -> int -> 'a) -> 'a -> t -> 'a
val fold_left32le : ('a -> int32 -> 'a) -> 'a -> t -> 'a
val fold_left32be : ('a -> int32 -> 'a) -> 'a -> t -> 'a
val fold_left64le : ('a -> int64 -> 'a) -> 'a -> t -> 'a
val fold_left64be : ('a -> int64 -> 'a) -> 'a -> t -> 'a
val fold_leftbs : len:int -> ('a -> t -> 'a) -> 'a -> t -> 'a

List conversions

val to_list : t -> char list
val to_list16le : t -> int list
val to_list16be : t -> int list
val to_list32le : t -> int32 list
val to_list32be : t -> int32 list
val to_list64le : t -> int64 list
val to_list64be : t -> int64 list
val to_listbs : len:int -> t -> t list

Cut a byte sequence into a list of byte sequences of length len, (and a shorter last one if the total len is not a multiple of len)

Binary IO

val output : Stdlib.out_channel -> t -> unit

Output the raw data of the byte sequence on the output channel

val input : Stdlib.in_channel -> t

Output the raw date of the byte sequence of the input channel

Pretty Printing

val pp : t -> Utils.Pp.document

Pretty print a byte sequence as space separated bytes like ab cd ef.

Here "ab" is the byte number 0 and "ef" is the byte number 2.

val ppc : t -> Utils.Pp.document

Pretty print a byte sequence as an hexadecimal string like abcdef

Here "ab" is the byte number 0 and "ef" is the byte number 2.

This can also be seen as printing the bytesequence as a single integer encoded in big endian format.

val ppint : t -> Utils.Pp.document

Pretty print a byte sequence as an hexadecimal integer (in little endian). The byte order is reversed compared to ppc

For example the byte sequence ab cd ef will be printed as efcdab where "ab" is the byte number 0 and "ef" is the byte number 2.

val ppby : by:int -> t -> Utils.Pp.document

Pretty print a byte sequence by step of by bytes. Each block is pretty printed as an hex string like ppc and blocks are separated by spaces.

val ppbyint : by:int -> t -> Utils.Pp.document

Pretty print a byte sequence by step of by bytes. Each block is pretty printed as a reversed hex string i.e like an integer of length by. Thus each block will printed like with ppcint Blocks are separated by spaces.

For example to print a byte sequence as a space separated list of little-endian integers do:

ppbyint ~by:4 bs
\ No newline at end of file +BytesSeq (read-dwarf.Utils.BytesSeq)

Module Utils.BytesSeq

This module represent a byte sub view on a bytes object. Contrary to Bytes it is a non-owning immutable view. It do not prevent the original bytes from being modified, and the changes will be propagated in the view. It is additional sugar on top of Linksem's Byte_sequence_wrapper

About all the suffixed function:

  • All iteration function without suffix do the expected operation on char (as single bytes)
  • All iteration function with suffix nle do the expected operation on a sequence of integers of n bits as read in little endian.
  • All iteration function with suffix nbe do the expected operation on a sequence of integers of n bits as read in big endian.
  • All iteration function with suffix bs do the expected operation on a sequence of BytesSeq.t of specified length.
  • All iteration function with suffix bvle do the expected operation on a sequence of BitVec the specified size as read in little endian.
type t = Byte_sequence_wrapper.byte_sequence

Type inherited from linksem

val length : t -> int

Get the length of the byteseq in bytes

val equal : t -> t -> bool

Check if two byte sequence are equal byte for byte

val int_bytes : int

The size in bytes of an Ocaml int

Hexadecimal conversions

val to_hex : t -> string

Convert the byte sequence to an hexadecimal string

val to_hex_rev : t -> string

Convert the byte sequence to an reversed hexadecimal string. This will print it like a big-endian integer.

val of_hex : string -> t

Parse the string as hexadecimal like A4B767DF and create a bytes of this a binary data and then a bytesSeq view of it

Cutting the view

val sub : t -> int -> int -> t

sub bs start len Extract a sub range [start:start+len) of a byte sequence. This is O(1)

val front : int -> t -> t

front i bs Take the first i bytes of bs and discard the rest. Equivalent to sub bs 0 i

val back : int -> t -> t

back i bs Take the last i bytes of bs and discard the rest. Equivalent to sub bs i (length bs - i)

Interaction with bytes and raw string

val blit : t -> int -> bytes -> int -> int -> unit

blit src srcoff dst dstoff len copies len bytes from bytes sequence src, starting at index srcoff, to bytes dst, starting at index dstoff.

See Bytes.blit.

val of_bytes : bytes -> t

Create a view of the whole bytes

val of_string : string -> t

Create a view of the whole string as raw bytes

val to_string : t -> string

Create a copy of the view in a string

val bytes_sub : bytes -> int -> int -> t

Create a byte sequence view of a specified range of a bytes. See sub

Array conversions

val to_array : t -> char array

Convert to a char array

val of_array : char array -> t

Convert from a char array

Getters

val get : t -> int -> char

Get the bytes at the offset in the byte sequence

val unsafe_get : t -> int -> char

Unsafe version of get

val get16le : t -> int -> int

Get a 16 bit integer at the offset in the byte sequence as little endian

val get16be : t -> int -> int

Get a 16 bit integer at the offset in the byte sequence as big endian

val get32le : t -> int -> int32

Get a 32 bit integer at the offset in the byte sequence as little endian

val get32be : t -> int -> int32

Get a 32 bit integer at the offset in the byte sequence as big endian

val get64le : t -> int -> int64

Get a 64 bit integer at the offset in the byte sequence as little endian

val get64be : t -> int -> int64

Get a 64 bit integer at the offset in the byte sequence as big endian

val getintle : t -> int -> int

Get an Ocaml int at the offset in the byte sequence as little endian. The number of bytes read is 4 if Sys.int_size is 31 and 8 if Sys.int_size is 63

val getbs : len:int -> t -> int -> t

Get a byte sequence of length len at the offset in another byte sequence

val getbvle : size:int -> t -> int -> BitVec.t

Get a BitVec of size size at the offset in the byte sequence as little endian

val getintle_ze : t -> int -> int

Get an Ocaml int at the offset in the byte sequence as little endian. The number of bytes read is 4 if Sys.int_size is 31 and 8 if Sys.int_size is 63. If the read goes beyond the end of the sequence, instead of failing, zeros are read.

Iterators

Iterators over a byte sequence. If the length of the byte sequence is not a multiple of the step of the iteration then the trailing odd bytes are not iterated over.

val iter : (char -> unit) -> t -> unit
val iter16le : (int -> unit) -> t -> unit
val iter16be : (int -> unit) -> t -> unit
val iter32le : (int32 -> unit) -> t -> unit
val iter32be : (int32 -> unit) -> t -> unit
val iter64le : (int64 -> unit) -> t -> unit
val iter64be : (int64 -> unit) -> t -> unit
val iterbs : len:int -> (t -> unit) -> t -> unit

Iterate over the byte sequence by bytesequence of length len. If the total byte sequence is not of length a multiple of len then that iterated value will be shorter

val fold_left : ('a -> char -> 'a) -> 'a -> t -> 'a
val fold_left16le : ('a -> int -> 'a) -> 'a -> t -> 'a
val fold_left16be : ('a -> int -> 'a) -> 'a -> t -> 'a
val fold_left32le : ('a -> int32 -> 'a) -> 'a -> t -> 'a
val fold_left32be : ('a -> int32 -> 'a) -> 'a -> t -> 'a
val fold_left64le : ('a -> int64 -> 'a) -> 'a -> t -> 'a
val fold_left64be : ('a -> int64 -> 'a) -> 'a -> t -> 'a
val fold_leftbs : len:int -> ('a -> t -> 'a) -> 'a -> t -> 'a

List conversions

val to_list : t -> char list
val to_list16le : t -> int list
val to_list16be : t -> int list
val to_list32le : t -> int32 list
val to_list32be : t -> int32 list
val to_list64le : t -> int64 list
val to_list64be : t -> int64 list
val to_listbs : len:int -> t -> t list

Cut a byte sequence into a list of byte sequences of length len, (and a shorter last one if the total len is not a multiple of len)

Binary IO

val output : Stdlib.out_channel -> t -> unit

Output the raw data of the byte sequence on the output channel

val input : Stdlib.in_channel -> t

Output the raw date of the byte sequence of the input channel

Pretty Printing

val pp : t -> Pp.document

Pretty print a byte sequence as space separated bytes like ab cd ef.

Here "ab" is the byte number 0 and "ef" is the byte number 2.

val ppc : t -> Pp.document

Pretty print a byte sequence as an hexadecimal string like abcdef

Here "ab" is the byte number 0 and "ef" is the byte number 2.

This can also be seen as printing the bytesequence as a single integer encoded in big endian format.

val ppint : t -> Pp.document

Pretty print a byte sequence as an hexadecimal integer (in little endian). The byte order is reversed compared to ppc

For example the byte sequence ab cd ef will be printed as efcdab where "ab" is the byte number 0 and "ef" is the byte number 2.

val ppby : by:int -> t -> Pp.document

Pretty print a byte sequence by step of by bytes. Each block is pretty printed as an hex string like ppc and blocks are separated by spaces.

val ppbyint : by:int -> t -> Pp.document

Pretty print a byte sequence by step of by bytes. Each block is pretty printed as a reversed hex string i.e like an integer of length by. Thus each block will printed like with ppcint Blocks are separated by spaces.

For example to print a byte sequence as a space separated list of little-endian integers do:

ppbyint ~by:4 bs
diff --git a/doc/html/read-dwarf/Utils/Cache/Cmd/Test/Cache/index.html b/doc/html/read-dwarf/Utils/Cache/Cmd/Test/Cache/index.html index 671ef87e..49e5b7ae 100644 --- a/doc/html/read-dwarf/Utils/Cache/Cmd/Test/Cache/index.html +++ b/doc/html/read-dwarf/Utils/Cache/Cmd/Test/Cache/index.html @@ -1,2 +1,2 @@ -Cache (read-dwarf.Utils.Cache.Cmd.Test.Cache)

Module Test.Cache

type key = Key.t
type value = Value.t
type epoch = UnitEpoch.t
type t = Make(Key)(Value)(UnitEpoch).t

The type that represent the cache in RAM.

val make : ?⁠fake:bool -> string -> epoch -> t

Build a new cache management object with a name and an epoch If fake is set, the cache will not touch the disk and behave as a plain Hashtbl.

val get_opt : t -> key -> value option

Get a value from the cache or None if no value is bound to the key

val get : t -> key -> value

Get a value from the cache or throws Not_found if no value is bound

val add : t -> key -> value -> unit

Add a new binding. Fails if a binding already exists

val remove : t -> key -> unit

Remove a binding (Also delete the representation on disk

\ No newline at end of file +Cache (read-dwarf.Utils.Cache.Cmd.Test.Cache)

Module Test.Cache

type key = Key.t
type value = Value.t
type epoch = UnitEpoch.t

The type that represent the cache in RAM.

val make : ?fake:bool -> string -> epoch -> t

Build a new cache management object with a name and an epoch If fake is set, the cache will not touch the disk and behave as a plain Hashtbl.

val get_opt : t -> key -> value option

Get a value from the cache or None if no value is bound to the key

val get : t -> key -> value

Get a value from the cache or throws Not_found if no value is bound

val add : t -> key -> value -> unit

Add a new binding. Fails if a binding already exists

val remove : t -> key -> unit

Remove a binding (Also delete the representation on disk

diff --git a/doc/html/read-dwarf/Utils/Cache/Cmd/Test/Single/index.html b/doc/html/read-dwarf/Utils/Cache/Cmd/Test/Single/index.html index 41175f38..9a900792 100644 --- a/doc/html/read-dwarf/Utils/Cache/Cmd/Test/Single/index.html +++ b/doc/html/read-dwarf/Utils/Cache/Cmd/Test/Single/index.html @@ -1,2 +1,2 @@ -Single (read-dwarf.Utils.Cache.Cmd.Test.Single)

Module Test.Single

type value = Value.t
type t = Single(Value).t

The type that represent the cache in RAM.

val make : ?⁠fake:bool -> string -> t

Build a new cache management object with a name If fake is set, the cache will not touch the disk and behave as a plain ref.

val get_opt : t -> value option

Get a value from the cache or None if no value is stored

val get : t -> value

Get a value from the cache or throws Not_found if no value is bound

val set : t -> value -> unit

Set the value in the cache

val clear : t -> unit

Clear the value in the cache

\ No newline at end of file +Single (read-dwarf.Utils.Cache.Cmd.Test.Single)

Module Test.Single

type value = Value.t

The type that represent the cache in RAM.

val make : ?fake:bool -> string -> t

Build a new cache management object with a name If fake is set, the cache will not touch the disk and behave as a plain ref.

val get_opt : t -> value option

Get a value from the cache or None if no value is stored

val get : t -> value

Get a value from the cache or throws Not_found if no value is bound

val set : t -> value -> unit

Set the value in the cache

val clear : t -> unit

Clear the value in the cache

diff --git a/doc/html/read-dwarf/Utils/Cache/Cmd/Test/Value/index.html b/doc/html/read-dwarf/Utils/Cache/Cmd/Test/Value/index.html index a2d2255f..d0df6c3f 100644 --- a/doc/html/read-dwarf/Utils/Cache/Cmd/Test/Value/index.html +++ b/doc/html/read-dwarf/Utils/Cache/Cmd/Test/Value/index.html @@ -1,2 +1,2 @@ -Value (read-dwarf.Utils.Cache.Cmd.Test.Value)

Module Test.Value

type t = string
val to_file : string -> t -> unit

Serialize the value to a file

val of_file : string -> t

Get the value back from a file. Must match with to_file

\ No newline at end of file +Value (read-dwarf.Utils.Cache.Cmd.Test.Value)

Module Test.Value

type t = string
val to_file : string -> t -> unit

Serialize the value to a file

val of_file : string -> t

Get the value back from a file. Must match with to_file

diff --git a/doc/html/read-dwarf/Utils/Cache/Cmd/Test/index.html b/doc/html/read-dwarf/Utils/Cache/Cmd/Test/index.html index 8a36a13e..84476414 100644 --- a/doc/html/read-dwarf/Utils/Cache/Cmd/Test/index.html +++ b/doc/html/read-dwarf/Utils/Cache/Cmd/Test/index.html @@ -1,2 +1,2 @@ -Test (read-dwarf.Utils.Cache.Cmd.Test)

Module Cmd.Test

This module provide a int -> string cache for testing pruposes. It can be tested with read-dwarf cache --test

module Key : Key with type Key.t = int
module Value : Value with type t = string
module Cache : sig ... end
module Single : sig ... end
\ No newline at end of file +Test (read-dwarf.Utils.Cache.Cmd.Test)

Module Cmd.Test

This module provide a int -> string cache for testing pruposes. It can be tested with read-dwarf cache --test

module Key : Key with type t = int
module Value : Value with type t = string
module Cache : sig ... end
module Single : sig ... end
diff --git a/doc/html/read-dwarf/Utils/Cache/Cmd/index.html b/doc/html/read-dwarf/Utils/Cache/Cmd/index.html index a8714815..54d09dfa 100644 --- a/doc/html/read-dwarf/Utils/Cache/Cmd/index.html +++ b/doc/html/read-dwarf/Utils/Cache/Cmd/index.html @@ -1,2 +1,2 @@ -Cmd (read-dwarf.Utils.Cache.Cmd)

Module Cache.Cmd

The cache command line. TODO documentation of the inside

module Test : sig ... end

This module provide a int -> string cache for testing pruposes. It can be tested with read-dwarf cache --test

val clear : bool Cmdliner.Term.t
val all : bool Cmdliner.Term.t
val test : bool Cmdliner.Term.t
val list : bool Cmdliner.Term.t
val arg : string option Cmdliner.Term.t
val fake : bool Cmdliner.Term.t
type operation =
| CLEAR
| LIST
| TEST

The cache operataion to run

val op_f2m : bool -> bool -> bool -> operation Cmdliner.Term.ret

Input flags to mode conversion

val operation_term : operation Cmdliner.Term.t
val test : bool -> unit

The testing mini command line to test the Test cache

val dostuff : operation -> bool -> string option -> bool -> unit

Do the caching operation op

val term : unit Cmdliner.Term.t
val exits : Cmdliner.Term.exit_info list
val info : Cmdliner.Term.info
val command : unit Cmdliner.Term.t * Cmdliner.Term.info
\ No newline at end of file +Cmd (read-dwarf.Utils.Cache.Cmd)

Module Cache.Cmd

The cache command line. TODO documentation of the inside

module Test : sig ... end

This module provide a int -> string cache for testing pruposes. It can be tested with read-dwarf cache --test

val clear : bool Cmdliner.Term.t
val all : bool Cmdliner.Term.t
val list : bool Cmdliner.Term.t
val arg : string option Cmdliner.Term.t
val fake : bool Cmdliner.Term.t
type operation =
  1. | CLEAR
  2. | LIST
  3. | TEST

The cache operataion to run

val op_f2m : bool -> bool -> bool -> operation Cmdliner.Term.ret

Input flags to mode conversion

val operation_term : operation Cmdliner.Term.t
val test : bool -> unit

The testing mini command line to test the Test cache

val dostuff : operation -> bool -> string option -> bool -> unit

Do the caching operation op

val term : unit Cmdliner.Term.t
val exits : Cmdliner.Cmd.Exit.info list
val info : Cmdliner.Cmd.info
val command : unit Cmdliner.Term.t * Cmdliner.Cmd.info
diff --git a/doc/html/read-dwarf/Utils/Cache/IntEpoch/index.html b/doc/html/read-dwarf/Utils/Cache/IntEpoch/index.html index a6cdcd57..1059dea7 100644 --- a/doc/html/read-dwarf/Utils/Cache/IntEpoch/index.html +++ b/doc/html/read-dwarf/Utils/Cache/IntEpoch/index.html @@ -1,2 +1,2 @@ -IntEpoch (read-dwarf.Utils.Cache.IntEpoch)

Module Cache.IntEpoch

type t = int
val to_file : string -> int -> unit
val of_file : string -> int
val compat : 'a -> 'a -> bool
\ No newline at end of file +IntEpoch (read-dwarf.Utils.Cache.IntEpoch)

Module Cache.IntEpoch

type t = int
val to_file : string -> int -> unit
val of_file : string -> int
val compat : 'a -> 'a -> bool
diff --git a/doc/html/read-dwarf/Utils/Cache/Make/argument-1-Key/index.html b/doc/html/read-dwarf/Utils/Cache/Make/argument-1-Key/index.html index 6caebfa6..2a10c2cd 100644 --- a/doc/html/read-dwarf/Utils/Cache/Make/argument-1-Key/index.html +++ b/doc/html/read-dwarf/Utils/Cache/Make/argument-1-Key/index.html @@ -1,2 +1,2 @@ -1-Key (read-dwarf.Utils.Cache.Make.1-Key)

Parameter Make.1-Key

include Stdlib.Hashtbl.HashedType
type t
val equal : t -> t -> bool
val hash : t -> int
val to_file : string -> t -> unit

Write the necessary information to retrieve the key in a file. The filename supplied is without the key extension. The implementer must call to_keyfile on it

val of_file : int -> string -> t

Build back the key from the hash and a storage file. The filename supplied is without the key extension. The implementer must call to_keyfile on it.

If you start with a key and a file, then doing to_file file key and then of_file (hash key) file must return something equal to key.

\ No newline at end of file +Key (read-dwarf.Utils.Cache.Make.Key)

Parameter Make.Key

include Stdlib.Hashtbl.HashedType
type t
val equal : t -> t -> bool
val hash : t -> int
val to_file : string -> t -> unit

Write the necessary information to retrieve the key in a file. The filename supplied is without the key extension. The implementer must call to_keyfile on it

val of_file : int -> string -> t

Build back the key from the hash and a storage file. The filename supplied is without the key extension. The implementer must call to_keyfile on it.

If you start with a key and a file, then doing to_file file key and then of_file (hash key) file must return something equal to key.

diff --git a/doc/html/read-dwarf/Utils/Cache/Make/argument-2-Value/index.html b/doc/html/read-dwarf/Utils/Cache/Make/argument-2-Value/index.html index 871cfeec..46723309 100644 --- a/doc/html/read-dwarf/Utils/Cache/Make/argument-2-Value/index.html +++ b/doc/html/read-dwarf/Utils/Cache/Make/argument-2-Value/index.html @@ -1,2 +1,2 @@ -2-Value (read-dwarf.Utils.Cache.Make.2-Value)

Parameter Make.2-Value

type t
val to_file : string -> t -> unit

Serialize the value to a file

val of_file : string -> t

Get the value back from a file. Must match with to_file

\ No newline at end of file +Value (read-dwarf.Utils.Cache.Make.Value)

Parameter Make.Value

type t
val to_file : string -> t -> unit

Serialize the value to a file

val of_file : string -> t

Get the value back from a file. Must match with to_file

diff --git a/doc/html/read-dwarf/Utils/Cache/Make/argument-3-Epoch/index.html b/doc/html/read-dwarf/Utils/Cache/Make/argument-3-Epoch/index.html index fb9aa5d5..8f70d9ad 100644 --- a/doc/html/read-dwarf/Utils/Cache/Make/argument-3-Epoch/index.html +++ b/doc/html/read-dwarf/Utils/Cache/Make/argument-3-Epoch/index.html @@ -1,2 +1,2 @@ -3-Epoch (read-dwarf.Utils.Cache.Make.3-Epoch)

Parameter Make.3-Epoch

include Value
type t
val to_file : string -> t -> unit

Serialize the value to a file

val of_file : string -> t

Get the value back from a file. Must match with to_file

val compat : t -> t -> bool

Tests if epochs are compatible

\ No newline at end of file +Epoch (read-dwarf.Utils.Cache.Make.Epoch)

Parameter Make.Epoch

include Value
type t
val to_file : string -> t -> unit

Serialize the value to a file

val of_file : string -> t

Get the value back from a file. Must match with to_file

val compat : t -> t -> bool

Tests if epochs are compatible

diff --git a/doc/html/read-dwarf/Utils/Cache/Make/index.html b/doc/html/read-dwarf/Utils/Cache/Make/index.html index f911b0d3..704c629e 100644 --- a/doc/html/read-dwarf/Utils/Cache/Make/index.html +++ b/doc/html/read-dwarf/Utils/Cache/Make/index.html @@ -1,2 +1,2 @@ -Make (read-dwarf.Utils.Cache.Make)

Module Cache.Make

The Key must provide the Key interface, where the values must satisfy the Value interface.

The map is stored on the disk in a simple way. First the key is hashed and this hashed gives a filename with int_to_file. If there is no collision, then the value is stored in that file and the key is stored (optionally) in hash.key file (See Key for details).

If there is collision then the filename is a directory containing numbered files of all the key-value pairs. Again the value is in file named n when the key is (optionally) in file named n.key.

Parameters

Signature

type key = Key.t

The type of keys

type value = Value.t

The type of values

type epoch = Epoch.t

The type of the epoch

type t

The type that represent the cache in RAM.

val make : ?⁠fake:bool -> string -> epoch -> t

Build a new cache management object with a name and an epoch If fake is set, the cache will not touch the disk and behave as a plain Hashtbl.

val get_opt : t -> key -> value option

Get a value from the cache or None if no value is bound to the key

val get : t -> key -> value

Get a value from the cache or throws Not_found if no value is bound

val add : t -> key -> value -> unit

Add a new binding. Fails if a binding already exists

val remove : t -> key -> unit

Remove a binding (Also delete the representation on disk

\ No newline at end of file +Make (read-dwarf.Utils.Cache.Make)

Module Cache.Make

The Key must provide the Key interface, where the values must satisfy the Value interface.

The map is stored on the disk in a simple way. First the key is hashed and this hashed gives a filename with int_to_file. If there is no collision, then the value is stored in that file and the key is stored (optionally) in hash.key file (See Key for details).

If there is collision then the filename is a directory containing numbered files of all the key-value pairs. Again the value is in file named n when the key is (optionally) in file named n.key.

Parameters

module Key : Key
module Value : Value
module Epoch : Epoch

Signature

type key = Key.t

The type of keys

type value = Value.t

The type of values

type epoch = Epoch.t

The type of the epoch

type t

The type that represent the cache in RAM.

val make : ?fake:bool -> string -> epoch -> t

Build a new cache management object with a name and an epoch If fake is set, the cache will not touch the disk and behave as a plain Hashtbl.

val get_opt : t -> key -> value option

Get a value from the cache or None if no value is bound to the key

val get : t -> key -> value

Get a value from the cache or throws Not_found if no value is bound

val add : t -> key -> value -> unit

Add a new binding. Fails if a binding already exists

val remove : t -> key -> unit

Remove a binding (Also delete the representation on disk

diff --git a/doc/html/read-dwarf/Utils/Cache/Single/argument-1-Value/index.html b/doc/html/read-dwarf/Utils/Cache/Single/argument-1-Value/index.html index 438dbf34..37238ef2 100644 --- a/doc/html/read-dwarf/Utils/Cache/Single/argument-1-Value/index.html +++ b/doc/html/read-dwarf/Utils/Cache/Single/argument-1-Value/index.html @@ -1,2 +1,2 @@ -1-Value (read-dwarf.Utils.Cache.Single.1-Value)

Parameter Single.1-Value

type t
val to_file : string -> t -> unit

Serialize the value to a file

val of_file : string -> t

Get the value back from a file. Must match with to_file

\ No newline at end of file +Value (read-dwarf.Utils.Cache.Single.Value)

Parameter Single.Value

type t
val to_file : string -> t -> unit

Serialize the value to a file

val of_file : string -> t

Get the value back from a file. Must match with to_file

diff --git a/doc/html/read-dwarf/Utils/Cache/Single/index.html b/doc/html/read-dwarf/Utils/Cache/Single/index.html index 3ed876fd..5dfc8667 100644 --- a/doc/html/read-dwarf/Utils/Cache/Single/index.html +++ b/doc/html/read-dwarf/Utils/Cache/Single/index.html @@ -1,2 +1,2 @@ -Single (read-dwarf.Utils.Cache.Single)

Module Cache.Single

The functor is to make a single cached value. This do not support epochs (Yet)

TODO: Maybe the code would be simpler if this was a map from unit to the value.

Parameters

Signature

type value = Value.t

The type of the stored value

type t

The type that represent the cache in RAM.

val make : ?⁠fake:bool -> string -> t

Build a new cache management object with a name If fake is set, the cache will not touch the disk and behave as a plain ref.

val get_opt : t -> value option

Get a value from the cache or None if no value is stored

val get : t -> value

Get a value from the cache or throws Not_found if no value is bound

val set : t -> value -> unit

Set the value in the cache

val clear : t -> unit

Clear the value in the cache

\ No newline at end of file +Single (read-dwarf.Utils.Cache.Single)

Module Cache.Single

The functor is to make a single cached value. This do not support epochs (Yet)

TODO: Maybe the code would be simpler if this was a map from unit to the value.

Parameters

module Value : Value

Signature

type value = Value.t

The type of the stored value

type t

The type that represent the cache in RAM.

val make : ?fake:bool -> string -> t

Build a new cache management object with a name If fake is set, the cache will not touch the disk and behave as a plain ref.

val get_opt : t -> value option

Get a value from the cache or None if no value is stored

val get : t -> value

Get a value from the cache or throws Not_found if no value is bound

val set : t -> value -> unit

Set the value in the cache

val clear : t -> unit

Clear the value in the cache

diff --git a/doc/html/read-dwarf/Utils/Cache/UnitEpoch/index.html b/doc/html/read-dwarf/Utils/Cache/UnitEpoch/index.html index f66a8bd4..c2b2f68f 100644 --- a/doc/html/read-dwarf/Utils/Cache/UnitEpoch/index.html +++ b/doc/html/read-dwarf/Utils/Cache/UnitEpoch/index.html @@ -1,2 +1,2 @@ -UnitEpoch (read-dwarf.Utils.Cache.UnitEpoch)

Module Cache.UnitEpoch

A dummy epochs implementation, if not epoch is needed

type t = unit
val to_file : 'a -> unit -> unit
val of_file : 'a -> unit
val compat : unit -> unit -> bool
\ No newline at end of file +UnitEpoch (read-dwarf.Utils.Cache.UnitEpoch)

Module Cache.UnitEpoch

A dummy epochs implementation, if not epoch is needed

type t = unit
val to_file : 'a -> unit -> unit
val of_file : 'a -> unit
val compat : unit -> unit -> bool
diff --git a/doc/html/read-dwarf/Utils/Cache/index.html b/doc/html/read-dwarf/Utils/Cache/index.html index 83fbd0ed..65e2b6bb 100644 --- a/doc/html/read-dwarf/Utils/Cache/index.html +++ b/doc/html/read-dwarf/Utils/Cache/index.html @@ -1,2 +1,6 @@ -Cache (read-dwarf.Utils.Cache)

Module Utils.Cache

Utility

This section is not part of the external API, but creating an mli file here seemed needlessly annoying.

val base_dir : string

The name of the read-dwarf cache. .rdcache for now.

val find_dir : unit -> string

Find the current cache folder as described in the README.

When searching for a cache, this function will search if there already is a base_dir directory either in the current directory or one of its parent and use the closest one it find. If it finds none and need a cache, it will create a new directory named base_dir in the current directory. Therefore the returned directory always exists.

val removedir : string -> unit

Remove a directory and all it's content.

TODO: Make that Windows friendly

TODO move that in Files

val cleardir : string -> unit

Clear the content of directory

val int_to_file : int -> string

Convert an hash integer to a filename: 16 characters in hexadecimal

type file_type =
| NOPE
| FILE
| DIR of int

The type of an entry at a given hash

val file_type : string -> file_type

Give the file type of the given type. Do not count the number of entry in ther

val to_keyfile : string -> string

Transform a filename in the key filename

val movekey : string -> string -> unit

movekey old new moves a key correponding to the given filenames, if they exist

val removekey : string -> unit

Removes a key correponding to the given filename, if it exists

val clear_cache : string -> unit

Delete the supplied cache

val list_caches : unit -> string list

List all existing caches

val clear_all_caches : unit -> unit

Delete all the caches (and the base cache directory)

exception Exists

Thrown when adding a key that is already bound in cache

Input signatures

module type Key = sig ... end

The interface of keys.

module IntKey : Key with type Key.t = int

An implementation of Key on ints where hash is the identity and no file is written

module type Value = sig ... end

The interface of values: They just need to be able to be serialized to files. This is isn't plain Marshal because some values may want a human readable format.

module type Epoch = sig ... end

A cache can be indexed by an Epoch. When reading a cache with an incompatible epoch, then the cache is deleted on load. Use UnitEpoch to not have this functionality. Single caches currently do not support epochs.

module UnitEpoch : sig ... end

A dummy epochs implementation, if not epoch is needed

module IntEpoch : sig ... end

CacheMap

module type S = sig ... end

The output signature of Make

module Make : functor (Key : Key) -> functor (Value : Value) -> functor (Epoch : Epoch) -> S with type key = Key.t and type value = Value.t and type epoch = Epoch.t

The Key must provide the Key interface, where the values must satisfy the Value interface.

Single Cache

module type SingleS = sig ... end

The signature of the output of Single

module Single : functor (Value : Value) -> SingleS with type value = Value.t

The functor is to make a single cached value. This do not support epochs (Yet)

Command line operations

module Cmd : sig ... end

The cache command line. TODO documentation of the inside

\ No newline at end of file +Cache (read-dwarf.Utils.Cache)

Module Utils.Cache

This module implement a caching system i.e a persistant structure stored on the disk.

A cache can be either:

  • An associative map from keys to values, See Make
  • A single value, see Single

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

Utility

This section is not part of the external API, but creating an mli file here seemed needlessly annoying.

val base_dir : string

The name of the read-dwarf cache. .rdcache for now.

val find_dir : unit -> string

Find the current cache folder as described in the README.

When searching for a cache, this function will search if there already is a base_dir directory either in the current directory or one of its parent and use the closest one it find. If it finds none and need a cache, it will create a new directory named base_dir in the current directory. Therefore the returned directory always exists.

val removedir : string -> unit

Remove a directory and all it's content.

TODO: Make that Windows friendly

TODO move that in Files

val cleardir : string -> unit

Clear the content of directory

val int_to_file : int -> string

Convert an hash integer to a filename: 16 characters in hexadecimal

type file_type =
  1. | NOPE
  2. | FILE
  3. | DIR of int

The type of an entry at a given hash

val file_type : string -> file_type

Give the file type of the given type. Do not count the number of entry in ther

val to_keyfile : string -> string

Transform a filename in the key filename

val movekey : string -> string -> unit

movekey old new moves a key correponding to the given filenames, if they exist

val removekey : string -> unit

Removes a key correponding to the given filename, if it exists

val clear_cache : string -> unit

Delete the supplied cache

val list_caches : unit -> string list

List all existing caches

val clear_all_caches : unit -> unit

Delete all the caches (and the base cache directory)

exception Exists

Thrown when adding a key that is already bound in cache

Input signatures

module type Key = sig ... end

The interface of keys.

module IntKey : Key with type t = int

An implementation of Key on ints where hash is the identity and no file is written

module type Value = sig ... end

The interface of values: They just need to be able to be serialized to files. This is isn't plain Marshal because some values may want a human readable format.

module type Epoch = sig ... end

A cache can be indexed by an Epoch. When reading a cache with an incompatible epoch, then the cache is deleted on load. Use UnitEpoch to not have this functionality. Single caches currently do not support epochs.

module UnitEpoch : sig ... end

A dummy epochs implementation, if not epoch is needed

module IntEpoch : sig ... end

CacheMap

module type S = sig ... end

The output signature of Make

module Make + (Key : Key) + (Value : Value) + (Epoch : Epoch) : + S with type key = Key.t and type value = Value.t and type epoch = Epoch.t

The Key must provide the Key interface, where the values must satisfy the Value interface.

Single Cache

module type SingleS = sig ... end

The signature of the output of Single

module Single (Value : Value) : SingleS with type value = Value.t

The functor is to make a single cached value. This do not support epochs (Yet)

Command line operations

module Cmd : sig ... end

The cache command line. TODO documentation of the inside

diff --git a/doc/html/read-dwarf/Utils/Cache/module-type-Epoch/index.html b/doc/html/read-dwarf/Utils/Cache/module-type-Epoch/index.html index 4b92c8be..679f4e6a 100644 --- a/doc/html/read-dwarf/Utils/Cache/module-type-Epoch/index.html +++ b/doc/html/read-dwarf/Utils/Cache/module-type-Epoch/index.html @@ -1,2 +1,2 @@ -Epoch (read-dwarf.Utils.Cache.Epoch)

Module type Cache.Epoch

A cache can be indexed by an Epoch. When reading a cache with an incompatible epoch, then the cache is deleted on load. Use UnitEpoch to not have this functionality. Single caches currently do not support epochs.

include Value
type t
val to_file : string -> t -> unit

Serialize the value to a file

val of_file : string -> t

Get the value back from a file. Must match with to_file

val compat : t -> t -> bool

Tests if epochs are compatible

\ No newline at end of file +Epoch (read-dwarf.Utils.Cache.Epoch)

Module type Cache.Epoch

A cache can be indexed by an Epoch. When reading a cache with an incompatible epoch, then the cache is deleted on load. Use UnitEpoch to not have this functionality. Single caches currently do not support epochs.

include Value
type t
val to_file : string -> t -> unit

Serialize the value to a file

val of_file : string -> t

Get the value back from a file. Must match with to_file

val compat : t -> t -> bool

Tests if epochs are compatible

diff --git a/doc/html/read-dwarf/Utils/Cache/module-type-Key/index.html b/doc/html/read-dwarf/Utils/Cache/module-type-Key/index.html index 9f43ae85..dc702027 100644 --- a/doc/html/read-dwarf/Utils/Cache/module-type-Key/index.html +++ b/doc/html/read-dwarf/Utils/Cache/module-type-Key/index.html @@ -1,2 +1,2 @@ -Key (read-dwarf.Utils.Cache.Key)

Module type Cache.Key

The interface of keys.

Key are use to index values in the cache, however instead of using the key directly as a file name, the hexadecimal hash of the key is used trough int_to_file. See Make for more details.

There are two main way of managing keys:

  • The hash is injective, in which case there is no need for extra information.
  • The hash is not injective, in which case extra information required to disambiguate between different value with the same hash can be used. This information is stored in the file obtained with 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.

include Stdlib.Hashtbl.HashedType
type t
val equal : t -> t -> bool
val hash : t -> int
val to_file : string -> t -> unit

Write the necessary information to retrieve the key in a file. The filename supplied is without the key extension. The implementer must call to_keyfile on it

val of_file : int -> string -> t

Build back the key from the hash and a storage file. The filename supplied is without the key extension. The implementer must call to_keyfile on it.

If you start with a key and a file, then doing to_file file key and then of_file (hash key) file must return something equal to key.

\ No newline at end of file +Key (read-dwarf.Utils.Cache.Key)

Module type Cache.Key

The interface of keys.

Key are use to index values in the cache, however instead of using the key directly as a file name, the hexadecimal hash of the key is used trough int_to_file. See Make for more details.

There are two main way of managing keys:

  • The hash is injective, in which case there is no need for extra information.
  • The hash is not injective, in which case extra information required to disambiguate between different value with the same hash can be used. This information is stored in the file obtained with 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.

include Stdlib.Hashtbl.HashedType
type t
val equal : t -> t -> bool
val hash : t -> int
val to_file : string -> t -> unit

Write the necessary information to retrieve the key in a file. The filename supplied is without the key extension. The implementer must call to_keyfile on it

val of_file : int -> string -> t

Build back the key from the hash and a storage file. The filename supplied is without the key extension. The implementer must call to_keyfile on it.

If you start with a key and a file, then doing to_file file key and then of_file (hash key) file must return something equal to key.

diff --git a/doc/html/read-dwarf/Utils/Cache/module-type-S/index.html b/doc/html/read-dwarf/Utils/Cache/module-type-S/index.html index e63d17b1..f647b0fd 100644 --- a/doc/html/read-dwarf/Utils/Cache/module-type-S/index.html +++ b/doc/html/read-dwarf/Utils/Cache/module-type-S/index.html @@ -1,2 +1,2 @@ -S (read-dwarf.Utils.Cache.S)

Module type Cache.S

The output signature of Make

type key

The type of keys

type value

The type of values

type epoch

The type of the epoch

type t

The type that represent the cache in RAM.

val make : ?⁠fake:bool -> string -> epoch -> t

Build a new cache management object with a name and an epoch If fake is set, the cache will not touch the disk and behave as a plain Hashtbl.

val get_opt : t -> key -> value option

Get a value from the cache or None if no value is bound to the key

val get : t -> key -> value

Get a value from the cache or throws Not_found if no value is bound

val add : t -> key -> value -> unit

Add a new binding. Fails if a binding already exists

val remove : t -> key -> unit

Remove a binding (Also delete the representation on disk

\ No newline at end of file +S (read-dwarf.Utils.Cache.S)

Module type Cache.S

The output signature of Make

type key

The type of keys

type value

The type of values

type epoch

The type of the epoch

type t

The type that represent the cache in RAM.

val make : ?fake:bool -> string -> epoch -> t

Build a new cache management object with a name and an epoch If fake is set, the cache will not touch the disk and behave as a plain Hashtbl.

val get_opt : t -> key -> value option

Get a value from the cache or None if no value is bound to the key

val get : t -> key -> value

Get a value from the cache or throws Not_found if no value is bound

val add : t -> key -> value -> unit

Add a new binding. Fails if a binding already exists

val remove : t -> key -> unit

Remove a binding (Also delete the representation on disk

diff --git a/doc/html/read-dwarf/Utils/Cache/module-type-SingleS/index.html b/doc/html/read-dwarf/Utils/Cache/module-type-SingleS/index.html index 2512dab0..0904a4c7 100644 --- a/doc/html/read-dwarf/Utils/Cache/module-type-SingleS/index.html +++ b/doc/html/read-dwarf/Utils/Cache/module-type-SingleS/index.html @@ -1,2 +1,2 @@ -SingleS (read-dwarf.Utils.Cache.SingleS)

Module type Cache.SingleS

The signature of the output of Single

type value

The type of the stored value

type t

The type that represent the cache in RAM.

val make : ?⁠fake:bool -> string -> t

Build a new cache management object with a name If fake is set, the cache will not touch the disk and behave as a plain ref.

val get_opt : t -> value option

Get a value from the cache or None if no value is stored

val get : t -> value

Get a value from the cache or throws Not_found if no value is bound

val set : t -> value -> unit

Set the value in the cache

val clear : t -> unit

Clear the value in the cache

\ No newline at end of file +SingleS (read-dwarf.Utils.Cache.SingleS)

Module type Cache.SingleS

The signature of the output of Single

type value

The type of the stored value

type t

The type that represent the cache in RAM.

val make : ?fake:bool -> string -> t

Build a new cache management object with a name If fake is set, the cache will not touch the disk and behave as a plain ref.

val get_opt : t -> value option

Get a value from the cache or None if no value is stored

val get : t -> value

Get a value from the cache or throws Not_found if no value is bound

val set : t -> value -> unit

Set the value in the cache

val clear : t -> unit

Clear the value in the cache

diff --git a/doc/html/read-dwarf/Utils/Cache/module-type-Value/index.html b/doc/html/read-dwarf/Utils/Cache/module-type-Value/index.html index a94601e5..e711efcd 100644 --- a/doc/html/read-dwarf/Utils/Cache/module-type-Value/index.html +++ b/doc/html/read-dwarf/Utils/Cache/module-type-Value/index.html @@ -1,2 +1,2 @@ -Value (read-dwarf.Utils.Cache.Value)

Module type Cache.Value

The interface of values: They just need to be able to be serialized to files. This is isn't plain Marshal because some values may want a human readable format.

type t
val to_file : string -> t -> unit

Serialize the value to a file

val of_file : string -> t

Get the value back from a file. Must match with to_file

\ No newline at end of file +Value (read-dwarf.Utils.Cache.Value)

Module type Cache.Value

The interface of values: They just need to be able to be serialized to files. This is isn't plain Marshal because some values may want a human readable format.

type t
val to_file : string -> t -> unit

Serialize the value to a file

val of_file : string -> t

Get the value back from a file. Must match with to_file

diff --git a/doc/html/read-dwarf/Utils/Cmd/IOServer/index.html b/doc/html/read-dwarf/Utils/Cmd/IOServer/index.html index 5f0908e7..26f64385 100644 --- a/doc/html/read-dwarf/Utils/Cmd/IOServer/index.html +++ b/doc/html/read-dwarf/Utils/Cmd/IOServer/index.html @@ -1,2 +1,2 @@ -IOServer (read-dwarf.Utils.Cmd.IOServer)

Module Cmd.IOServer

This module provide functionality to run command in the background and communicate with it via redirection on it's standard input and output.

An example of use is in Z3

type t = {
cmd : cmd;
input : Stdlib.in_channel;

The output of the command from which answer can be read

output : Stdlib.out_channel;

The input of the server on which request can be sent

}

The type of pipe IO server

val start : cmd -> t

Start the server using the specified command

val stop : t -> unit

Stop the server.

May throw Crash on error.

\ No newline at end of file +IOServer (read-dwarf.Utils.Cmd.IOServer)

Module Cmd.IOServer

This module provide functionality to run command in the background and communicate with it via redirection on it's standard input and output.

An example of use is in Z3

type t = {
  1. cmd : cmd;
  2. input : Stdlib.in_channel;
    (*

    The output of the command from which answer can be read

    *)
  3. output : Stdlib.out_channel;
    (*

    The input of the server on which request can be sent

    *)
}

The type of pipe IO server

val start : cmd -> t

Start the server using the specified command

val stop : t -> unit

Stop the server.

May throw Crash on error.

diff --git a/doc/html/read-dwarf/Utils/Cmd/SocketServer/index.html b/doc/html/read-dwarf/Utils/Cmd/SocketServer/index.html index fd5a1e6a..4887250f 100644 --- a/doc/html/read-dwarf/Utils/Cmd/SocketServer/index.html +++ b/doc/html/read-dwarf/Utils/Cmd/SocketServer/index.html @@ -1,2 +1,2 @@ -SocketServer (read-dwarf.Utils.Cmd.SocketServer)

Module Cmd.SocketServer

This module provide functionality for a socket server with which one can communicate on a sockets.

An example of use is in Isla.Server

type t

The abstract type of a socket server

val start : name:string -> (string -> cmd) -> t

Start the server with provided name and wait for it to connect to the socket. Then build the Server.t object. The function argument must take a socket name and give a valid command line to call the server process and make it connect to the socket.

val stop : t -> unit

Stop the server and cut the connection, wait for the subprocess to die and then delete the socket

May throw Crash on error.

val read_byte : t -> int

Read a single byte from the server

val read_string : t -> string

Read a string with the following format:

| header : 4 bytes | data : header bytes |

In other words, read a 4 bytes number, then read that number of bytes into a string

val write_string : t -> string -> unit

Write a string in the same binary format as read_string

\ No newline at end of file +SocketServer (read-dwarf.Utils.Cmd.SocketServer)

Module Cmd.SocketServer

This module provide functionality for a socket server with which one can communicate on a sockets.

An example of use is in Isla.Server

type t

The abstract type of a socket server

val start : name:string -> (string -> cmd) -> t

Start the server with provided name and wait for it to connect to the socket. Then build the Server.t object. The function argument must take a socket name and give a valid command line to call the server process and make it connect to the socket.

val stop : t -> unit

Stop the server and cut the connection, wait for the subprocess to die and then delete the socket

May throw Crash on error.

val read_byte : t -> int

Read a single byte from the server

val read_string : t -> string

Read a string with the following format:

| header : 4 bytes | data : header bytes |

In other words, read a 4 bytes number, then read that number of bytes into a string

val write_string : t -> string -> unit

Write a string in the same binary format as read_string

diff --git a/doc/html/read-dwarf/Utils/Cmd/index.html b/doc/html/read-dwarf/Utils/Cmd/index.html index 54b25bbe..24a0b895 100644 --- a/doc/html/read-dwarf/Utils/Cmd/index.html +++ b/doc/html/read-dwarf/Utils/Cmd/index.html @@ -1,2 +1,6 @@ -Cmd (read-dwarf.Utils.Cmd)

Module Utils.Cmd

type cmd = string array

The type of a command to be sent. The program to call must be the item 0 of the array

exception Crash of cmd * Unix.process_status

If a program do not return with a 0 exit code, we throw that exception giving the command that failed and the invalid status it returned

Pipe calling

val call : cmd -> unit

Call the command without redirecting anything. Wait for completion before returning.

May throw Crash on error.

val call_send : cmd -> sender:(Stdlib.out_channel -> unit) -> unit

Call the command and then call sender to send it some data on it's stdin. Then wait for completion.

May throw Crash on error.

val call_send_string : cmd -> string -> unit

Call the command, send it the string on it's standard input and wait for completion.

May throw Crash on error.

val call_read : cmd -> reader:(Stdlib.in_channel -> 'a) -> 'a

Call the command and then call reader to parse what the command outputs on it's stdout. Then wait for completion and return the parsed value

May throw Crash on error.

val call_read_string : cmd -> string

Call the command, wait for completion, and return it's stdout in a string

val call_send_read : cmd -> sender:(Stdlib.out_channel -> unit) -> reader:(Stdlib.in_channel -> 'a) -> 'a

Call the command and then call sender to send the input data on it's stdin. Then call reader to parse an answer from stdout Then wait for completion and return the parsed value

May throw Crash on error.

Pipe server

module IOServer : sig ... end

This module provide functionality to run command in the background and communicate with it via redirection on it's standard input and output.

Socket server

module SocketServer : sig ... end

This module provide functionality for a socket server with which one can communicate on a sockets.

\ No newline at end of file +Cmd (read-dwarf.Utils.Cmd)

Module Utils.Cmd

This module provides high-level interaction with external processes.

This provide a functionality similar to Bos), but this lib is still unstable.

There are two main mode of communication provided:

  • Interaction with pipes: capture the stdin or stdout of the command and use them
  • Interaction with socket: Create a socket on which the child program can connect to.

Programs can be launched in two modes

  • Calling: They are called like a function and we wait for them to give a result before contnuing
  • Server: The are launch as a background process that stay there and can be called.

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.

type cmd = string array

The type of a command to be sent. The program to call must be the item 0 of the array

exception Crash of cmd * Unix.process_status

If a program do not return with a 0 exit code, we throw that exception giving the command that failed and the invalid status it returned

Pipe calling

val call : cmd -> unit

Call the command without redirecting anything. Wait for completion before returning.

May throw Crash on error.

val call_send : cmd -> sender:(Stdlib.out_channel -> unit) -> unit

Call the command and then call sender to send it some data on it's stdin. Then wait for completion.

May throw Crash on error.

val call_send_string : cmd -> string -> unit

Call the command, send it the string on it's standard input and wait for completion.

May throw Crash on error.

val call_read : cmd -> reader:(Stdlib.in_channel -> 'a) -> 'a

Call the command and then call reader to parse what the command outputs on it's stdout. Then wait for completion and return the parsed value

May throw Crash on error.

val call_read_string : cmd -> string

Call the command, wait for completion, and return it's stdout in a string

val call_send_read : + cmd -> + sender:(Stdlib.out_channel -> unit) -> + reader:(Stdlib.in_channel -> 'a) -> + 'a

Call the command and then call sender to send the input data on it's stdin. Then call reader to parse an answer from stdout Then wait for completion and return the parsed value

May throw Crash on error.

Pipe server

module IOServer : sig ... end

This module provide functionality to run command in the background and communicate with it via redirection on it's standard input and output.

Socket server

module SocketServer : sig ... end

This module provide functionality for a socket server with which one can communicate on a sockets.

diff --git a/doc/html/read-dwarf/Utils/CmdlinerHelper/index.html b/doc/html/read-dwarf/Utils/CmdlinerHelper/index.html index 4ddd7981..394c19b5 100644 --- a/doc/html/read-dwarf/Utils/CmdlinerHelper/index.html +++ b/doc/html/read-dwarf/Utils/CmdlinerHelper/index.html @@ -1,2 +1,8 @@ -CmdlinerHelper (read-dwarf.Utils.CmdlinerHelper)

Module Utils.CmdlinerHelper

val setter : 'a Stdlib.ref -> 'a Cmdliner.Term.t -> unit Cmdliner.Term.t

Return a unit Term that evaluates the input term and set the reference to the resulting value.

Due to the nature of Cmdliner, this may be evaluated multiple times, so setting the reference to anything else is dangerous.

val add_option : unit Cmdliner.Term.t -> 'a Cmdliner.Term.t -> 'a Cmdliner.Term.t

Add an unit term that need to be evaluated at the same time at the main term.

The order of evaluation is unspecified, but the option will be evaluated before the resulting term is returned.

val add_options : unit Cmdliner.Term.t list -> 'a Cmdliner.Term.t -> 'a Cmdliner.Term.t

Fold add_option on a list of option

val func_option : unit Cmdliner.Term.t -> 'a -> 'a Cmdliner.Term.t

Replaces Term.const but allow a unit terms (like the one generated by setter) to be evaluated before the function is called

val func_options : unit Cmdliner.Term.t list -> 'a -> 'a Cmdliner.Term.t

Same as func_option but with a list of unit terms

\ No newline at end of file +CmdlinerHelper (read-dwarf.Utils.CmdlinerHelper)

Module Utils.CmdlinerHelper

This module provide some Cmdliner helper functions.

val setter : 'a Stdlib.ref -> 'a Cmdliner.Term.t -> unit Cmdliner.Term.t

Return a unit Term that evaluates the input term and set the reference to the resulting value.

Due to the nature of Cmdliner, this may be evaluated multiple times, so setting the reference to anything else is dangerous.

val add_option : + unit Cmdliner.Term.t -> + 'a Cmdliner.Term.t -> + 'a Cmdliner.Term.t

Add an unit term that need to be evaluated at the same time at the main term.

The order of evaluation is unspecified, but the option will be evaluated before the resulting term is returned.

val add_options : + unit Cmdliner.Term.t list -> + 'a Cmdliner.Term.t -> + 'a Cmdliner.Term.t

Fold add_option on a list of option

val func_option : unit Cmdliner.Term.t -> 'a -> 'a Cmdliner.Term.t

Replaces Term.const but allow a unit terms (like the one generated by setter) to be evaluated before the function is called

val func_options : unit Cmdliner.Term.t list -> 'a -> 'a Cmdliner.Term.t

Same as func_option but with a list of unit terms

diff --git a/doc/html/read-dwarf/Utils/Counter/index.html b/doc/html/read-dwarf/Utils/Counter/index.html index 28b311e0..36f79d9e 100644 --- a/doc/html/read-dwarf/Utils/Counter/index.html +++ b/doc/html/read-dwarf/Utils/Counter/index.html @@ -1,3 +1,3 @@ -Counter (read-dwarf.Utils.Counter)

Module Utils.Counter

type t

The type of a counter.

val make : int -> t

Make a counter starting from the provided value. This means the first call to get on that counter will be the input value:

let c = make 42 in
-assert(get c = 42);
val get : t -> int

Get the next value of the counter and increment it

val read : t -> int

Get the current value of the counter

val skip : t -> unit

Skip a value of the counter equivalent to ingore (get ...)

\ No newline at end of file +Counter (read-dwarf.Utils.Counter)

Module Utils.Counter

This module provide a small counter object which is just a int reference on which get can be called to get an identifier and increment the reference

type t

The type of a counter.

val make : int -> t

Make a counter starting from the provided value. This means the first call to get on that counter will be the input value:

let c = make 42 in
+assert(get c = 42);
val get : t -> int

Get the next value of the counter and increment it

val read : t -> int

Get the current value of the counter

val skip : t -> unit

Skip a value of the counter equivalent to ingore (get ...)

diff --git a/doc/html/read-dwarf/Utils/Files/index.html b/doc/html/read-dwarf/Utils/Files/index.html index d8069710..001fb1bc 100644 --- a/doc/html/read-dwarf/Utils/Files/index.html +++ b/doc/html/read-dwarf/Utils/Files/index.html @@ -1,2 +1,2 @@ -Files (read-dwarf.Utils.Files)

Module Utils.Files

type 'a reader = Stdlib.in_channel -> 'a

The type of a reader that read an object from a channel. Function like input_* in Stdlib

type 'a writer = Stdlib.out_channel -> 'a -> unit

The type of a writer of 'a. Function named output_* in Stdlib

IO functions

val double_byte : bytes -> bytes

Double the size of a bytes object (technical function)

val input_bytes : Stdlib.in_channel -> bytes

Read the whole channel in a bytes

val input_string : Stdlib.in_channel -> string

Read the whole channel in a string

val input_sexp : Stdlib.in_channel -> string

Reads a S-expression from the input, line by line. When the sexp finishes, there should be nothing else on the line i.e. the last closing parenthesis should be followed by a new line.

val input_list : 'a reader -> Stdlib.in_channel -> 'a list

Try the reader until it fails with End_of_file and then build the list of all the successfully read objects in order.

val input_array : 'a reader -> Stdlib.in_channel -> 'a array

Try the reader until it fails with End_of_file and then build the array of all the successfully read objects in order.

val output_list : 'a writer -> Stdlib.out_channel -> 'a list -> unit

Output all the element of the list in order with the provided writer

Direct file IO

val read : 'a reader -> string -> 'a

Take a reader and a file and read an object from the file using the reader. Text mode

val read_bin : 'a reader -> string -> 'a

Take a reader and a file and read an object from the file using the reader. Binary mode

val write : 'a writer -> string -> 'a -> unit

Take a writer a file and object and write the object to the file using writer. Text mode

val write_bin : 'a writer -> string -> 'a -> unit

Take a writer fs a file and object and write the object to the file using writer. Binary mode

val read_string : string -> string

Return the content of specified file as a string

val write_string : string -> string -> unit

write_string file cont write cont in file which is overwritten if it exists

File management

val remove : string -> unit

Remove a file

val exists : string -> bool

Test if a files exists

val remove_at_exit : string -> unit

Remove a file at program exit

val add_to_relative : newp:string -> string -> string

Add newp in front of a relative path. If the path is not relative, then it is unchanged

\ No newline at end of file +Files (read-dwarf.Utils.Files)

Module Utils.Files

This module provides simplified file management and some channel interaction function.

The functions read and write are about dealing with a whole file at once without caring about opening or closing it.

type 'a reader = Stdlib.in_channel -> 'a

The type of a reader that read an object from a channel. Function like input_* in Stdlib

type 'a writer = Stdlib.out_channel -> 'a -> unit

The type of a writer of 'a. Function named output_* in Stdlib

IO functions

val double_byte : bytes -> bytes

Double the size of a bytes object (technical function)

val input_bytes : Stdlib.in_channel -> bytes

Read the whole channel in a bytes

val input_string : Stdlib.in_channel -> string

Read the whole channel in a string

val input_sexp : Stdlib.in_channel -> string

Reads a S-expression from the input, line by line. When the sexp finishes, there should be nothing else on the line i.e. the last closing parenthesis should be followed by a new line.

val input_list : 'a reader -> Stdlib.in_channel -> 'a list

Try the reader until it fails with End_of_file and then build the list of all the successfully read objects in order.

val input_array : 'a reader -> Stdlib.in_channel -> 'a array

Try the reader until it fails with End_of_file and then build the array of all the successfully read objects in order.

val output_list : 'a writer -> Stdlib.out_channel -> 'a list -> unit

Output all the element of the list in order with the provided writer

Direct file IO

val read : 'a reader -> string -> 'a

Take a reader and a file and read an object from the file using the reader. Text mode

val read_bin : 'a reader -> string -> 'a

Take a reader and a file and read an object from the file using the reader. Binary mode

val write : 'a writer -> string -> 'a -> unit

Take a writer a file and object and write the object to the file using writer. Text mode

val write_bin : 'a writer -> string -> 'a -> unit

Take a writer fs a file and object and write the object to the file using writer. Binary mode

val read_string : string -> string

Return the content of specified file as a string

val write_string : string -> string -> unit

write_string file cont write cont in file which is overwritten if it exists

File management

val remove : string -> unit

Remove a file

val exists : string -> bool

Test if a files exists

val remove_at_exit : string -> unit

Remove a file at program exit

val add_to_relative : newp:string -> string -> string

Add newp in front of a relative path. If the path is not relative, then it is unchanged

diff --git a/doc/html/read-dwarf/Utils/FullVec/index.html b/doc/html/read-dwarf/Utils/FullVec/index.html index ab6aeb9c..29fd72ad 100644 --- a/doc/html/read-dwarf/Utils/FullVec/index.html +++ b/doc/html/read-dwarf/Utils/FullVec/index.html @@ -1,2 +1,2 @@ -FullVec (read-dwarf.Utils.FullVec)

Module Utils.FullVec

type 'a t
val make : (int -> 'a) -> 'a t

Create a full vector from a generator

val copy : 'a t -> 'a t

Make a copy that can be mutated separately

val set : 'a t -> int -> 'a -> unit

Set the binding of that integer

val set_after : 'a t -> int -> (int -> 'a) -> unit

Set the binding of all integer after this value by supplying a new generator. The former generator is discarded. The bindings before the value keep their value (if there were not generated, they will be before discarding the old generator

val get : 'a t -> int -> 'a

Get the binding of that integer

val get_vec : 'a t -> 'a Vec.t

Get the underlying vector

val get_vec_until : 'a t -> int -> 'a Vec.t

Get a vector containing at least the elements until the specified value excluded

val map : ('a -> 'b) -> 'a t -> 'b t

Map the function over the fullvec. Postcompose the map on the generator

val map_mut : ('a -> 'a) -> 'a t -> unit

Map the function over the fullvector by mutation. Postcompose the map on the generator.contents Warning, a lot of map_mut may make the generator big and slow. Maybe try to use set_after to reset it when required.

val map_mut_until : limit:int -> ('a -> 'a) -> 'a t -> unit

Map the function over the fullvector until the limit. The rest is unchanged

val iter_until : limit:int -> ('a -> unit) -> 'a t -> unit

Iterate until the specified value (excluded). (The FullVec is infinite so you can't iter on all of it)

val iteri_until : limit:int -> (int -> 'a -> unit) -> 'a t -> unit

Same as iter_until but with the index

val pp : ('a -> Utils.Pp.document) -> 'a t -> Utils.Pp.document

Only prints the non-default values

\ No newline at end of file +FullVec (read-dwarf.Utils.FullVec)

Module Utils.FullVec

A full vector is a vector in which all non-negative integer are bound.

This consist in a normal vector and a function and all the bindings of value after the vector end are the result of the function call on that integer.

It is guaranted that the function will be called lazily on integer when required in stricly increasing order (and thus never twice on the same integer). However an integer may be skipped (When manually set). That means the function can have side effect if necessary. However if the structure is copied (map or copy), The generator may be called multiple time on some integer in the different copies.

Both set and get may generate calls to the generator if required.

Any attempt to use a negative integer will raise Invalid_argument.

A negative integer will never be passed to the generator.

type 'a t
val make : (int -> 'a) -> 'a t

Create a full vector from a generator

val copy : 'a t -> 'a t

Make a copy that can be mutated separately

val set : 'a t -> int -> 'a -> unit

Set the binding of that integer

val set_after : 'a t -> int -> (int -> 'a) -> unit

Set the binding of all integer after this value by supplying a new generator. The former generator is discarded. The bindings before the value keep their value (if there were not generated, they will be before discarding the old generator

val get : 'a t -> int -> 'a

Get the binding of that integer

val get_vec : 'a t -> 'a Vec.t

Get the underlying vector

val get_vec_until : 'a t -> int -> 'a Vec.t

Get a vector containing at least the elements until the specified value excluded

val map : ('a -> 'b) -> 'a t -> 'b t

Map the function over the fullvec. Postcompose the map on the generator

val mapi : (int -> 'a -> 'b) -> 'a t -> 'b t

Same as map but with the index

val map_mut : ('a -> 'a) -> 'a t -> unit

Map the function over the fullvector by mutation. Postcompose the map on the generator.contents Warning, a lot of map_mut may make the generator big and slow. Maybe try to use set_after to reset it when required.

val map_mut_until : limit:int -> ('a -> 'a) -> 'a t -> unit

Map the function over the fullvector until the limit. The rest is unchanged

val iter_until : limit:int -> ('a -> unit) -> 'a t -> unit

Iterate until the specified value (excluded). (The FullVec is infinite so you can't iter on all of it)

val iteri_until : limit:int -> (int -> 'a -> unit) -> 'a t -> unit

Same as iter_until but with the index

val pp : ('a -> Pp.document) -> 'a t -> Pp.document

Only prints the non-default values

diff --git a/doc/html/read-dwarf/Utils/Fun/index.html b/doc/html/read-dwarf/Utils/Fun/index.html index 85731d97..cc57d738 100644 --- a/doc/html/read-dwarf/Utils/Fun/index.html +++ b/doc/html/read-dwarf/Utils/Fun/index.html @@ -1,2 +1,2 @@ -Fun (read-dwarf.Utils.Fun)

Module Utils.Fun

include Stdlib.Fun
val id : 'a -> 'a
val const : 'a -> 'b -> 'a
val flip : ('a -> 'b -> 'c) -> 'b -> 'a -> 'c
val negate : ('a -> bool) -> 'a -> bool
val protect : finally:(unit -> unit) -> (unit -> 'a) -> 'a
exception Finally_raised of exn
val ($) : ('a -> 'b) -> 'a -> 'b

An other application operator. g @@ f @@ 4 replaces g (f 4) but h $ g 4 $ f 5 replaces h (g 4) (f 5)

@@ has higher precedence so h $ g @@ f 4 $ t @@ p 5 = h (g (f 4)) (t (p 5)) but in practice, I would advise not to mix them.

val (%>) : ('a -> 'b) -> ('b -> 'c) -> 'a -> 'c

Abstraction of piping above a parameter useful to refactor things like List.map f |> List.map g into List.map (f %> g).

The trivial definition is x |> (f %> g) = x |> f |> g

val tee : ('a -> unit) -> 'a -> 'a

When you want to run some imperative code on a value before continuing the pipeline

val curry : (('a * 'b) -> 'c) -> 'a -> 'b -> 'c

curry f a b = f (a, b)

val uncurry : ('a -> 'b -> 'c) -> ('a * 'b) -> 'c

uncurry f (a, b) = f a b

val ctrue : 'a -> bool

Shorthand for const true

val cfalse : 'a -> bool

Shorthand for const false

\ No newline at end of file +Fun (read-dwarf.Utils.Fun)

Module Utils.Fun

More functional combinator. This module extends the base OCaml API of Fun.

include module type of struct include Stdlib.Fun end
val id : 'a -> 'a
val const : 'a -> 'b -> 'a
val flip : ('a -> 'b -> 'c) -> 'b -> 'a -> 'c
val negate : ('a -> bool) -> 'a -> bool
val protect : finally:(unit -> unit) -> (unit -> 'a) -> 'a
exception Finally_raised of exn
val ($) : ('a -> 'b) -> 'a -> 'b

An other application operator. g @@ f @@ 4 replaces g (f 4) but h $ g 4 $ f 5 replaces h (g 4) (f 5)

@@ has higher precedence so h $ g @@ f 4 $ t @@ p 5 = h (g (f 4)) (t (p 5)) but in practice, I would advise not to mix them.

val (%>) : ('a -> 'b) -> ('b -> 'c) -> 'a -> 'c

Abstraction of piping above a parameter useful to refactor things like List.map f |> List.map g into List.map (f %> g).

The trivial definition is x |> (f %> g) = x |> f |> g

val tee : ('a -> unit) -> 'a -> 'a

When you want to run some imperative code on a value before continuing the pipeline

val curry : (('a * 'b) -> 'c) -> 'a -> 'b -> 'c

curry f a b = f (a, b)

val uncurry : ('a -> 'b -> 'c) -> ('a * 'b) -> 'c

uncurry f (a, b) = f a b

val ctrue : 'a -> bool

Shorthand for const true

val cfalse : 'a -> bool

Shorthand for const false

diff --git a/doc/html/read-dwarf/Utils/HashVector/index.html b/doc/html/read-dwarf/Utils/HashVector/index.html index 3f39059f..f91a7063 100644 --- a/doc/html/read-dwarf/Utils/HashVector/index.html +++ b/doc/html/read-dwarf/Utils/HashVector/index.html @@ -1,2 +1,2 @@ -HashVector (read-dwarf.Utils.HashVector)

Module Utils.HashVector

type 'a t

The type of the hash vector

val mem : 'a t -> int -> bool

Check if an element is set

val copy : 'a t -> 'a t

Copy the HashVector

exception Exists
val set : 'a t -> int -> 'a -> unit

Set a value. Create a new binding if necessary

val clear : 'a t -> int -> unit

Clear a binding, do nothing is the value is not bound.

val add : 'a t -> int -> 'a -> unit

Create a new binding. Throws Exists if a binding already exists

val get_opt : 'a t -> int -> 'a option

Return Some v if v is bound to the integer in the hash vector. Return None if nothing is bound

val get : 'a t -> int -> 'a

Return the value bound to the integer. Throw Not_found if the integer is not bound

val empty : unit -> 'a t

Create an empty hashVector.t

val bindings : 'a t -> (int * 'a) list

Returns a list of bindings in the hash vector

val of_seq : (int * 'a) Utils.Seq.t -> 'a t

Fill the hashVector from a sequence

val pp : ('a -> Utils.Pp.document) -> 'a t -> Utils.Pp.document

Pretty print a hashVector

\ No newline at end of file +HashVector (read-dwarf.Utils.HashVector)

Module Utils.HashVector

An hash vector allow a vector to behave as hash map indexed by small integers. It is hash map with the identity hash function.

If all goes well it has the same API as (int, 'a) Hashtbl.t for positive integers. Any attempt to use a negative integer will raise Invalid_argument.

type 'a t

The type of the hash vector

val mem : 'a t -> int -> bool

Check if an element is set

val copy : 'a t -> 'a t

Copy the HashVector

exception Exists
val set : 'a t -> int -> 'a -> unit

Set a value. Create a new binding if necessary

val clear : 'a t -> int -> unit

Clear a binding, do nothing is the value is not bound.

val add : 'a t -> int -> 'a -> unit

Create a new binding. Throws Exists if a binding already exists

val get_opt : 'a t -> int -> 'a option

Return Some v if v is bound to the integer in the hash vector. Return None if nothing is bound

val get : 'a t -> int -> 'a

Return the value bound to the integer. Throw Not_found if the integer is not bound

val empty : unit -> 'a t

Create an empty hashVector.t

val bindings : 'a t -> (int * 'a) list

Returns a list of bindings in the hash vector

val of_seq : (int * 'a) Seq.t -> 'a t

Fill the hashVector from a sequence

val pp : ('a -> Pp.document) -> 'a t -> Pp.document

Pretty print a hashVector

diff --git a/doc/html/read-dwarf/Utils/IdMap/index.html b/doc/html/read-dwarf/Utils/IdMap/index.html index 7346db5b..db581062 100644 --- a/doc/html/read-dwarf/Utils/IdMap/index.html +++ b/doc/html/read-dwarf/Utils/IdMap/index.html @@ -1,2 +1,7 @@ -IdMap (read-dwarf.Utils.IdMap)

Module Utils.IdMap

type ('a, 'b) t

The type of a idmap

'a is type of keys that index the structure

'b is the type of value that are indexed.

val length : ('a'b) t -> int

Gives the number of bindings in the idmap

exception Exists

Thrown when adding an existing value

Creation and adding

val make : unit -> ('a'b) t

Create a new idmap from scratch

val add : ('a'b) t -> 'a -> 'b -> int

Add a binding, and throw Exists if the binding already exists

val adds : ('a'b) t -> 'a -> 'b -> unit

Silent version of add that ignore the result

Keys

val to_ident : ('a'b) t -> 'a -> int

Convert a key in an identifier. Throws if the key is not bound

val to_ident_opt : ('a'b) t -> 'a -> int option

Convert a key in an identifier. None if the key is not bound

val of_ident : ('a'b) t -> int -> 'a

Convert an identifier to its corresponding key. Throws if the id is not bound

val mem : ('a'b) t -> 'a -> bool

Check if a key is bound

val mem_id : ('a'b) t -> int -> bool

Check if an id is bound

Accessors

val getk : ('a'b) t -> 'a -> 'b

Get a value by key. Raise Not_found if the key is not bound.

val getk_opt : ('a'b) t -> 'a -> 'b option

Get a value by key. None if the key is not bound.

val geti : ('a'b) t -> int -> 'b

Get a value by id. Raise Invalid_argument if the index is not bound.

val unsafe_geti : ('a'b) t -> int -> 'b

Get a value by id, unsafe.

val setk : ('a'b) t -> 'a -> 'b -> unit

Set a value by key. Raise Not_found if the key is not bound.

val seti : ('a'b) t -> int -> 'b -> unit

Set a value by id. Raise Invalid_argument if the index is not bound.

val unsafe_seti : ('a'b) t -> int -> 'b -> unit

Set a value by id, unsafe.

val fill_all : ('a'b) t -> 'b -> unit

Bind the value to all the keys with the specified value.

Functional accessors

val iter : ('a -> int -> 'b -> unit) -> ('a'b) t -> unit

Call the function on all the bindings of the idmap

val map_to_list : ('a -> int -> 'b -> 'c) -> ('a'b) t -> 'c list

Call the function on all the bindings of the idmap and return the list of results

Pretty print

val pp : ?⁠name:string -> keys:('a -> Utils.Pp.document) -> vals:('b -> Utils.Pp.document) -> ('a'b) t -> Utils.Pp.document

Pretty prints

\ No newline at end of file +IdMap (read-dwarf.Utils.IdMap)

Module Utils.IdMap

An IdMap is a map that associate an id to each key (and thus to each value).

The value can be indexed with the key or with the id.

The key can be retrieved from the id and vice versa.

Values can be retreived from both keys and value (

The id is an int

type ('a, 'b) t

The type of a idmap

'a is type of keys that index the structure

'b is the type of value that are indexed.

val length : ('a, 'b) t -> int

Gives the number of bindings in the idmap

exception Exists

Thrown when adding an existing value

Creation and adding

val make : unit -> ('a, 'b) t

Create a new idmap from scratch

val add : ('a, 'b) t -> 'a -> 'b -> int

Add a binding, and throw Exists if the binding already exists

val adds : ('a, 'b) t -> 'a -> 'b -> unit

Silent version of add that ignore the result

Keys

val to_ident : ('a, 'b) t -> 'a -> int

Convert a key in an identifier. Throws if the key is not bound

val to_ident_opt : ('a, 'b) t -> 'a -> int option

Convert a key in an identifier. None if the key is not bound

val of_ident : ('a, 'b) t -> int -> 'a

Convert an identifier to its corresponding key. Throws if the id is not bound

val mem : ('a, 'b) t -> 'a -> bool

Check if a key is bound

val mem_id : ('a, 'b) t -> int -> bool

Check if an id is bound

Accessors

val getk : ('a, 'b) t -> 'a -> 'b

Get a value by key. Raise Not_found if the key is not bound.

val getk_opt : ('a, 'b) t -> 'a -> 'b option

Get a value by key. None if the key is not bound.

val geti : ('a, 'b) t -> int -> 'b

Get a value by id. Raise Invalid_argument if the index is not bound.

val unsafe_geti : ('a, 'b) t -> int -> 'b

Get a value by id, unsafe.

val setk : ('a, 'b) t -> 'a -> 'b -> unit

Set a value by key. Raise Not_found if the key is not bound.

val seti : ('a, 'b) t -> int -> 'b -> unit

Set a value by id. Raise Invalid_argument if the index is not bound.

val unsafe_seti : ('a, 'b) t -> int -> 'b -> unit

Set a value by id, unsafe.

val fill_all : ('a, 'b) t -> 'b -> unit

Bind the value to all the keys with the specified value.

Functional accessors

val iter : ('a -> int -> 'b -> unit) -> ('a, 'b) t -> unit

Call the function on all the bindings of the idmap

val map_to_list : ('a -> int -> 'b -> 'c) -> ('a, 'b) t -> 'c list

Call the function on all the bindings of the idmap and return the list of results

Pretty print

val pp : + ?name:string -> + keys:('a -> Pp.document) -> + vals:('b -> Pp.document) -> + ('a, 'b) t -> + Pp.document

Pretty prints

diff --git a/doc/html/read-dwarf/Utils/IntBits/index.html b/doc/html/read-dwarf/Utils/IntBits/index.html index c2b95d27..e0de5068 100644 --- a/doc/html/read-dwarf/Utils/IntBits/index.html +++ b/doc/html/read-dwarf/Utils/IntBits/index.html @@ -1,2 +1,2 @@ -IntBits (read-dwarf.Utils.IntBits)

Module Utils.IntBits

type t = int

The type of a int as a bitfield. This is just to make signatures clearer

val length : int

The length of an integer in bits (Sys.int_size)

val back : int

The last index in an integer

val check_index : int -> unit

Check that the index is valid to index an integer.

Throw Invalid_argument if the index is not valid

val check_range : int -> int -> unit

Check that the range is valid to index an integer. See module documentation (IntBits) for the definition of a valid range.

Throw Invalid_argument if the range is not valid.

val init : bool -> t

Initialize an int with all zeros or all ones depending on the boolean

val get : t -> int -> bool

Get a bit at a specific index. See unsafe_get

val unsafe_get : t -> int -> bool

Unsafe version of get

val set : t -> int -> t

Set a bit at a specific index. See unsafe_set

val unsafe_set : t -> int -> t

Unsafe version of set

val clear : t -> int -> t

Clear a bit at a specific index. See unsafe_clear

val unsafe_clear : t -> int -> t

Unsafe version of clear

val setb : t -> int -> bool -> t

Set a bit at a specific index according to a boolean. See unsafe_setb

val unsafe_setb : t -> int -> bool -> t

Unsafe version of setb

val mask : int -> int -> t

mask i l creates a mask stating at i of length l. This means that the bits of the output in the range [i; i+l) are ones and the others are 0.

See unsafe_mask.

val unsafe_mask : int -> int -> t

Unsafe version of mask

val set_range : t -> int -> int -> t

set_range bf i l sets the range [i; i+l) to ones in bf. See unsafe_set_range

val unsafe_set_range : t -> int -> int -> t

Unsafe version of set_range

val clear_range : t -> int -> int -> t

clear_range bf i l sets the range [i; i+l) to zeroes in bf. See unsafe_set_range

val unsafe_clear_range : t -> int -> int -> t

Unsafe version of clear_range

val sub : t -> int -> int -> t

sub bf i l outputs the range [i; i+l) of bf. The bits above l of the result are zeroes.

See unsafe_sub

val unsafe_sub : t -> int -> int -> t

Unsafe version of sub

val set_sub : t -> int -> int -> t -> t

set_sub bf i l data sets the [i; i+l) range of bf to data. The bits above l of data are ignored.

See unsafe_set_sub

val unsafe_set_sub : t -> int -> int -> t -> t

Unsafe version of set_sub. However the bits above l of data must be zeroes

val blit : t -> int -> t -> int -> int -> t

unsafe_blit src isrc dest idest len copies [isrc; isrc+len) of src into [idest; idest +l) of dest.

See unsafe_blit

val unsafe_blit : t -> int -> t -> int -> int -> t

Unsafe version of blit

\ No newline at end of file +IntBits (read-dwarf.Utils.IntBits)

Module Utils.IntBits

Manipulate an int as bitfield of size 31 or 63.

I'm tired of having to think about bit shifts and bitwise operations when I do that stuff

Little endian indexing (0 is the least significant bit)

Ranges are specified with and index and a length. The index must be in [0,length) and the length must be in (0,length]. Those conditions will be named "valid range". Any range specified in that way can go after the end. When reading, it will behave as if it was zeroes, and on writes, all bits after the end are discarded.

All unsafe function implicitely assume that all indexes and ranges are valid. All safe functions throw if those conditions are not met.

type t = int

The type of a int as a bitfield. This is just to make signatures clearer

val length : int

The length of an integer in bits (Sys.int_size)

val back : int

The last index in an integer

val check_index : int -> unit

Check that the index is valid to index an integer.

Throw Invalid_argument if the index is not valid

val check_range : int -> int -> unit

Check that the range is valid to index an integer. See module documentation (IntBits) for the definition of a valid range.

Throw Invalid_argument if the range is not valid.

val init : bool -> t

Initialize an int with all zeros or all ones depending on the boolean

val get : t -> int -> bool

Get a bit at a specific index. See unsafe_get

val unsafe_get : t -> int -> bool

Unsafe version of get

val set : t -> int -> t

Set a bit at a specific index. See unsafe_set

val unsafe_set : t -> int -> t

Unsafe version of set

val clear : t -> int -> t

Clear a bit at a specific index. See unsafe_clear

val unsafe_clear : t -> int -> t

Unsafe version of clear

val setb : t -> int -> bool -> t

Set a bit at a specific index according to a boolean. See unsafe_setb

val unsafe_setb : t -> int -> bool -> t

Unsafe version of setb

val mask : int -> int -> t

mask i l creates a mask stating at i of length l. This means that the bits of the output in the range [i; i+l) are ones and the others are 0.

See unsafe_mask.

val unsafe_mask : int -> int -> t

Unsafe version of mask

val set_range : t -> int -> int -> t

set_range bf i l sets the range [i; i+l) to ones in bf. See unsafe_set_range

val unsafe_set_range : t -> int -> int -> t

Unsafe version of set_range

val clear_range : t -> int -> int -> t

clear_range bf i l sets the range [i; i+l) to zeroes in bf. See unsafe_set_range

val unsafe_clear_range : t -> int -> int -> t

Unsafe version of clear_range

val sub : t -> int -> int -> t

sub bf i l outputs the range [i; i+l) of bf. The bits above l of the result are zeroes.

See unsafe_sub

val unsafe_sub : t -> int -> int -> t

Unsafe version of sub

val set_sub : t -> int -> int -> t -> t

set_sub bf i l data sets the [i; i+l) range of bf to data. The bits above l of data are ignored.

See unsafe_set_sub

val unsafe_set_sub : t -> int -> int -> t -> t

Unsafe version of set_sub. However the bits above l of data must be zeroes

val blit : t -> int -> t -> int -> int -> t

unsafe_blit src isrc dest idest len copies [isrc; isrc+len) of src into [idest; idest +l) of dest.

See unsafe_blit

val unsafe_blit : t -> int -> t -> int -> int -> t

Unsafe version of blit

diff --git a/doc/html/read-dwarf/Utils/List/index.html b/doc/html/read-dwarf/Utils/List/index.html index c4b6b039..a8d5600d 100644 --- a/doc/html/read-dwarf/Utils/List/index.html +++ b/doc/html/read-dwarf/Utils/List/index.html @@ -1,2 +1,16 @@ -List (read-dwarf.Utils.List)

Module Utils.List

include Stdlib.List
type 'a t = 'a list =
| ([])
| (::) of 'a * 'a list
val length : 'a list -> int
val compare_lengths : 'a list -> 'b list -> int
val compare_length_with : 'a list -> int -> int
val cons : 'a -> 'a list -> 'a list
val hd : 'a list -> 'a
val tl : 'a list -> 'a list
val nth : 'a list -> int -> 'a
val nth_opt : 'a list -> int -> 'a option
val rev : 'a list -> 'a list
val init : int -> (int -> 'a) -> 'a list
val append : 'a list -> 'a list -> 'a list
val rev_append : 'a list -> 'a list -> 'a list
val concat : 'a list list -> 'a list
val flatten : 'a list list -> 'a list
val iter : ('a -> unit) -> 'a list -> unit
val iteri : (int -> 'a -> unit) -> 'a list -> unit
val map : ('a -> 'b) -> 'a list -> 'b list
val mapi : (int -> 'a -> 'b) -> 'a list -> 'b list
val rev_map : ('a -> 'b) -> 'a list -> 'b list
val filter_map : ('a -> 'b option) -> 'a list -> 'b list
val fold_left : ('a -> 'b -> 'a) -> 'a -> 'b list -> 'a
val fold_right : ('a -> 'b -> 'b) -> 'a list -> 'b -> 'b
val iter2 : ('a -> 'b -> unit) -> 'a list -> 'b list -> unit
val map2 : ('a -> 'b -> 'c) -> 'a list -> 'b list -> 'c list
val rev_map2 : ('a -> 'b -> 'c) -> 'a list -> 'b list -> 'c list
val fold_left2 : ('a -> 'b -> 'c -> 'a) -> 'a -> 'b list -> 'c list -> 'a
val fold_right2 : ('a -> 'b -> 'c -> 'c) -> 'a list -> 'b list -> 'c -> 'c
val for_all : ('a -> bool) -> 'a list -> bool
val exists : ('a -> bool) -> 'a list -> bool
val for_all2 : ('a -> 'b -> bool) -> 'a list -> 'b list -> bool
val exists2 : ('a -> 'b -> bool) -> 'a list -> 'b list -> bool
val mem : 'a -> 'a list -> bool
val memq : 'a -> 'a list -> bool
val find : ('a -> bool) -> 'a list -> 'a
val find_opt : ('a -> bool) -> 'a list -> 'a option
val filter : ('a -> bool) -> 'a list -> 'a list
val find_all : ('a -> bool) -> 'a list -> 'a list
val partition : ('a -> bool) -> 'a list -> 'a list * 'a list
val assoc : 'a -> ('a * 'b) list -> 'b
val assoc_opt : 'a -> ('a * 'b) list -> 'b option
val assq : 'a -> ('a * 'b) list -> 'b
val assq_opt : 'a -> ('a * 'b) list -> 'b option
val mem_assoc : 'a -> ('a * 'b) list -> bool
val mem_assq : 'a -> ('a * 'b) list -> bool
val remove_assoc : 'a -> ('a * 'b) list -> ('a * 'b) list
val remove_assq : 'a -> ('a * 'b) list -> ('a * 'b) list
val split : ('a * 'b) list -> 'a list * 'b list
val combine : 'a list -> 'b list -> ('a * 'b) list
val sort : ('a -> 'a -> int) -> 'a list -> 'a list
val stable_sort : ('a -> 'a -> int) -> 'a list -> 'a list
val fast_sort : ('a -> 'a -> int) -> 'a list -> 'a list
val sort_uniq : ('a -> 'a -> int) -> 'a list -> 'a list
val merge : ('a -> 'a -> int) -> 'a list -> 'a list -> 'a list
val to_seq : 'a list -> 'a Stdlib.Seq.t
val of_seq : 'a Stdlib.Seq.t -> 'a list
val repeat : int -> 'a -> 'a t

repeat n a return a list of n as.

val set_nth : 'a t -> int -> 'a -> 'a t

Set the nth value to the new value and return the modified list

val last : 'a t -> 'a

Give the last element of the list. Raise Invalid_argument if the list is empty.

val last_opt : 'a t -> 'a option

Give the last element of the list. Return None if the list is empty.

Iterators

val fold_left_same : ('a -> 'a -> 'a) -> 'a t -> 'a

Same as fold_left, but do not require a start element, instead the function start with the first element of the list: fold_left_same f [b1; ...; bn] = f (... (f (f b1 b2) ...) bn.

It will fail with Invalid_argument if the list is empty.

It can also be written as fold_left_same f l = fold_left f (hd l) (tl l)

val of_array_map : ('a -> 'b) -> 'a array -> 'b t

Map a function at the same time as we are creating a list from an array

Warning: The function is mapped from the right to the left (in case it has side-effects).

of_array_map f l = of_array (Array.map f l) = map f (of_array l)

val of_array_mapi : (int -> 'a -> 'b) -> 'a array -> 'c t

Same as of_array_map but with the index

val concat_map_rev : ('a -> 'b list) -> 'a t -> 'b t

Same as concat_map then rev

val concat_map : ('a -> 'b list) -> 'a t -> 'b t

Same as map then concat.

TODO: find a clean way of doing conditional compilation, otherwise this will shadow the official concat_map in 4.10

val find_map : ('a -> 'b option) -> 'a t -> 'b option

find_map f l applies f to the elements of l in order, and returns the first result of the form Some v, or None if f always return None

TODO: find a clean way of doing conditional compilation, otherwise this will shadow the official find_map in 4.10

val filter_opt : 'a option t -> 'a t

Takes a list of option and only keeps the Some. Equivalent to filter_map Fun.id

val partition_map : ('a -> 'b option) -> 'a list -> 'a t * 'b t

This function behaves as partition then a map. First we do a partition with the function assuming Some means true, then for all the extracted elements we map f on them and return the results.

Formally: partition_map f l = (filter (fun a -> f a = None) l, filter_map f l)

Removing elements

val remove : ('a -> bool) -> 'a t -> 'a list option

Remove the first element matching the predicate. Returns None if no element matches the predicate

val drop : int -> 'a t -> 'a t

Drop the specified number of item from the list. If n is greater than the size of the list, then return the empty list

val take_rev : int -> 'a t -> 'a t

Take the specified number of items from the list, but reverse If n is greater than the size of the list, then return the list reversed

Tail-recursive

val take : int -> 'a t -> 'a list

Take the specified number of items from the list. If n is greater than the size of the list, then return the list

l = take n l @ drop n l

val sub : pos:int -> len:int -> 'a t -> 'a list

sub l pos len return the sub-list of l starting at pos of length len

Sorted list manipulation

val merge_uniq : ('a -> 'a -> int) -> 'a t -> 'a t -> 'a t

Same as merge but duplicate elements are deleted. It is assumed that element are not duplicate in the argument (like if sorted with sort_uniq)

Sequences

val of_seq_rev : 'a Utils.Seq.t -> 'a t

Build a list from a sequence, but in reverse. Same as of_seq then rev

Equality and comparison

val equal : ('a -> 'b -> bool) -> 'a t -> 'b t -> bool
val mem : ('a -> 'b -> bool) -> 'a -> 'b t -> bool
val compare : ('a -> 'b -> int) -> 'a t -> 'b t -> int

If the list have the same length this a lexicographic compare. If one list is shorter, then the missing value a considered smaller than any actual values.

A mental model could to view list as infinite sequence of options that are None after the end of the list. Then it would be proper lexicographic ordering with Option.compare

List monad

val bind : 'a t -> ('a -> 'b list) -> 'b t

Monadic bind. It's just concat_map

val return : 'a -> 'a t

Monadic return

val short_combine : 'a t -> 'b t -> ('a * 'b) t

Same as combine but if a list is shorter then the elements of the longest list are discarded

val let+ : 'a list -> ('a -> 'b) -> 'b list

Applicative let binding. let+ x = xl in e = let* x = xl in return e

val and+ : 'a t -> 'b t -> ('a * 'b) t

Not strict applicative merge (short_combine). If both list have different length, the longer one is cropped

val let+! : 'a list -> ('a -> unit) -> unit

Iterative let binding (The expression in the in must be unit). This replace implicitly a unit member of the monad (that is assumed to be uninteresting) to a true unit. In other words, it's an iter: let+! x = l in e = List.iter (fun x -> e) l

val and+! : 'a list -> 'b list -> ('a * 'b) list

Strict applicative merge (combine). Will throw if lists have different length

val let* : 'a t -> ('a -> 'b list) -> 'b t

Monadic let binding

val prod : 'a t -> 'b list -> ('a * 'b) t

Do the Cartesian product of two lists

val and* : 'a t -> 'b list -> ('a * 'b) t

Monadic merge. let* x = xl and* y = yl in ... = let* x= xl in let* y = yl in ...

\ No newline at end of file +List (read-dwarf.Utils.List)

Module Utils.List

Extension of the List module of the standard library.

It port forward function of ocaml 4.10 among others.

include module type of struct include Stdlib.List end
type !'a t = 'a list =
  1. | []
  2. | :: of 'a * 'a list
val length : 'a list -> int
val compare_lengths : 'a list -> 'b list -> int
val compare_length_with : 'a list -> int -> int
val is_empty : 'a list -> bool
val cons : 'a -> 'a list -> 'a list
val hd : 'a list -> 'a
val tl : 'a list -> 'a list
val nth : 'a list -> int -> 'a
val nth_opt : 'a list -> int -> 'a option
val rev : 'a list -> 'a list
val init : int -> (int -> 'a) -> 'a list
val append : 'a list -> 'a list -> 'a list
val rev_append : 'a list -> 'a list -> 'a list
val concat : 'a list list -> 'a list
val flatten : 'a list list -> 'a list
val iter : ('a -> unit) -> 'a list -> unit
val iteri : (int -> 'a -> unit) -> 'a list -> unit
val map : ('a -> 'b) -> 'a list -> 'b list
val mapi : (int -> 'a -> 'b) -> 'a list -> 'b list
val rev_map : ('a -> 'b) -> 'a list -> 'b list
val filter_map : ('a -> 'b option) -> 'a list -> 'b list
val fold_left_map : + ('acc -> 'a -> 'acc * 'b) -> + 'acc -> + 'a list -> + 'acc * 'b list
val fold_left : ('acc -> 'a -> 'acc) -> 'acc -> 'a list -> 'acc
val fold_right : ('a -> 'acc -> 'acc) -> 'a list -> 'acc -> 'acc
val iter2 : ('a -> 'b -> unit) -> 'a list -> 'b list -> unit
val map2 : ('a -> 'b -> 'c) -> 'a list -> 'b list -> 'c list
val rev_map2 : ('a -> 'b -> 'c) -> 'a list -> 'b list -> 'c list
val fold_left2 : + ('acc -> 'a -> 'b -> 'acc) -> + 'acc -> + 'a list -> + 'b list -> + 'acc
val fold_right2 : + ('a -> 'b -> 'acc -> 'acc) -> + 'a list -> + 'b list -> + 'acc -> + 'acc
val for_all : ('a -> bool) -> 'a list -> bool
val exists : ('a -> bool) -> 'a list -> bool
val for_all2 : ('a -> 'b -> bool) -> 'a list -> 'b list -> bool
val exists2 : ('a -> 'b -> bool) -> 'a list -> 'b list -> bool
val memq : 'a -> 'a list -> bool
val find : ('a -> bool) -> 'a list -> 'a
val find_opt : ('a -> bool) -> 'a list -> 'a option
val find_index : ('a -> bool) -> 'a list -> int option
val find_mapi : (int -> 'a -> 'b option) -> 'a list -> 'b option
val filter : ('a -> bool) -> 'a list -> 'a list
val find_all : ('a -> bool) -> 'a list -> 'a list
val filteri : (int -> 'a -> bool) -> 'a list -> 'a list
val partition : ('a -> bool) -> 'a list -> 'a list * 'a list
val assoc : 'a -> ('a * 'b) list -> 'b
val assoc_opt : 'a -> ('a * 'b) list -> 'b option
val assq : 'a -> ('a * 'b) list -> 'b
val assq_opt : 'a -> ('a * 'b) list -> 'b option
val mem_assoc : 'a -> ('a * 'b) list -> bool
val mem_assq : 'a -> ('a * 'b) list -> bool
val remove_assoc : 'a -> ('a * 'b) list -> ('a * 'b) list
val remove_assq : 'a -> ('a * 'b) list -> ('a * 'b) list
val split : ('a * 'b) list -> 'a list * 'b list
val combine : 'a list -> 'b list -> ('a * 'b) list
val sort : ('a -> 'a -> int) -> 'a list -> 'a list
val stable_sort : ('a -> 'a -> int) -> 'a list -> 'a list
val fast_sort : ('a -> 'a -> int) -> 'a list -> 'a list
val sort_uniq : ('a -> 'a -> int) -> 'a list -> 'a list
val merge : ('a -> 'a -> int) -> 'a list -> 'a list -> 'a list
val to_seq : 'a list -> 'a Stdlib.Seq.t
val of_seq : 'a Stdlib.Seq.t -> 'a list
val repeat : int -> 'a -> 'a t

repeat n a return a list of n as.

val set_nth : 'a t -> int -> 'a -> 'a t

Set the nth value to the new value and return the modified list

val last : 'a t -> 'a

Give the last element of the list. Raise Invalid_argument if the list is empty.

val last_opt : 'a t -> 'a option

Give the last element of the list. Return None if the list is empty.

Iterators

val fold_left_same : ('a -> 'a -> 'a) -> 'a t -> 'a

Same as fold_left, but do not require a start element, instead the function start with the first element of the list: fold_left_same f [b1; ...; bn] = f (... (f (f b1 b2) ...) bn.

It will fail with Invalid_argument if the list is empty.

It can also be written as fold_left_same f l = fold_left f (hd l) (tl l)

val of_array_map : ('a -> 'b) -> 'a array -> 'b t

Map a function at the same time as we are creating a list from an array

Warning: The function is mapped from the right to the left (in case it has side-effects).

of_array_map f l = of_array (Array.map f l) = map f (of_array l)

val of_array_mapi : (int -> 'a -> 'b) -> 'a array -> 'c t

Same as of_array_map but with the index

val concat_map_rev : ('a -> 'b list) -> 'a t -> 'b t

Same as concat_map then rev

val concat_map : ('a -> 'b list) -> 'a t -> 'b t

Same as map then concat.

TODO: find a clean way of doing conditional compilation, otherwise this will shadow the official concat_map in 4.10

val find_map : ('a -> 'b option) -> 'a t -> 'b option

find_map f l applies f to the elements of l in order, and returns the first result of the form Some v, or None if f always return None

TODO: find a clean way of doing conditional compilation, otherwise this will shadow the official find_map in 4.10

val filter_opt : 'a option t -> 'a t

Takes a list of option and only keeps the Some. Equivalent to filter_map Fun.id

val partition_map : ('a -> 'b option) -> 'a list -> 'a t * 'b t

This function behaves as partition then a map. First we do a partition with the function assuming Some means true, then for all the extracted elements we map f on them and return the results.

Formally: partition_map f l = (filter (fun a -> f a = None) l, filter_map f l)

Removing elements

val remove : ('a -> bool) -> 'a t -> 'a list option

Remove the first element matching the predicate. Returns None if no element matches the predicate

val drop : int -> 'a t -> 'a t

Drop the specified number of item from the list. If n is greater than the size of the list, then return the empty list

val take_rev : int -> 'a t -> 'a t

Take the specified number of items from the list, but reverse If n is greater than the size of the list, then return the list reversed

Tail-recursive

val take : int -> 'a t -> 'a list

Take the specified number of items from the list. If n is greater than the size of the list, then return the list

l = take n l @ drop n l

val sub : pos:int -> len:int -> 'a t -> 'a list

sub l pos len return the sub-list of l starting at pos of length len

Sorted list manipulation

val merge_uniq : ('a -> 'a -> int) -> 'a t -> 'a t -> 'a t

Same as merge but duplicate elements are deleted. It is assumed that element are not duplicate in the argument (like if sorted with sort_uniq)

Sequences

val of_seq_rev : 'a Seq.t -> 'a t

Build a list from a sequence, but in reverse. Same as of_seq then rev

Equality and comparison

val equal : ('a -> 'b -> bool) -> 'a t -> 'b t -> bool
val mem : ('a -> 'b -> bool) -> 'a -> 'b t -> bool
val compare : ('a -> 'b -> int) -> 'a t -> 'b t -> int

If the list have the same length this a lexicographic compare. If one list is shorter, then the missing value a considered smaller than any actual values.

A mental model could to view list as infinite sequence of options that are None after the end of the list. Then it would be proper lexicographic ordering with Option.compare

List monad

val bind : 'a t -> ('a -> 'b list) -> 'b t

Monadic bind. It's just concat_map

val return : 'a -> 'a t

Monadic return

val short_combine : 'a t -> 'b t -> ('a * 'b) t

Same as combine but if a list is shorter then the elements of the longest list are discarded

val (let+) : 'a list -> ('a -> 'b) -> 'b list

Applicative let binding. let+ x = xl in e = let* x = xl in return e

val (and+) : 'a t -> 'b t -> ('a * 'b) t

Not strict applicative merge (short_combine). If both list have different length, the longer one is cropped

val (let+!) : 'a list -> ('a -> unit) -> unit

Iterative let binding (The expression in the in must be unit). This replace implicitly a unit member of the monad (that is assumed to be uninteresting) to a true unit. In other words, it's an iter: let+! x = l in e = List.iter (fun x -> e) l

val (and+!) : 'a list -> 'b list -> ('a * 'b) list

Strict applicative merge (combine). Will throw if lists have different length

val (let*) : 'a t -> ('a -> 'b list) -> 'b t

Monadic let binding

val prod : 'a t -> 'b list -> ('a * 'b) t

Do the Cartesian product of two lists

val (and*) : 'a t -> 'b list -> ('a * 'b) t

Monadic merge. let* x = xl and* y = yl in ... = let* x= xl in let* y = yl in ...

val hd_opt : 'a t -> 'a option
val transpose : defaults:'a list -> 'a t list -> 'a list t
diff --git a/doc/html/read-dwarf/Utils/Logs/Logger/argument-1-S/index.html b/doc/html/read-dwarf/Utils/Logs/Logger/argument-1-S/index.html index c942c117..2d30b30d 100644 --- a/doc/html/read-dwarf/Utils/Logs/Logger/argument-1-S/index.html +++ b/doc/html/read-dwarf/Utils/Logs/Logger/argument-1-S/index.html @@ -1,2 +1,2 @@ -1-S (read-dwarf.Utils.Logs.Logger.1-S)

Parameter Logger.1-S

val str : string
\ No newline at end of file +S (read-dwarf.Utils.Logs.Logger.S)

Parameter Logger.S

val str : string
diff --git a/doc/html/read-dwarf/Utils/Logs/Logger/index.html b/doc/html/read-dwarf/Utils/Logs/Logger/index.html index a6d9249d..f6d3ab9d 100644 --- a/doc/html/read-dwarf/Utils/Logs/Logger/index.html +++ b/doc/html/read-dwarf/Utils/Logs/Logger/index.html @@ -1,4 +1,4 @@ -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
+Logger (read-dwarf.Utils.Logs.Logger)

Module Logs.Logger

This declare a logger instance. The string parameter is the logger name. It should be the OCaml module name. The normal way of instantiating this module is:

open Logs.Logger (struct
   let str = __MODULE__
-end)

This module provide a lot of logging function, They only provide a format string interface but with Pp.top on may print efficiently but lazily arbitrary Pp.document to the logs.

Some examples:

warn "This weird thing happened, I choose that default behavior for case %s" case 
debug "Function ... received value %t" Pp.(top printer object) 

Parameters

Signature

val set_level : level -> unit

Override the level of this logger. It may still be overridden by the command line

val get_level : unit -> level

Get the current level

val log : level -> 'a printf

Log a specific level using a format string

val log_fatal : code:int -> level -> ('a'b) printf_fatal

Log a fatal problem with format string then shutdown with code

val base : 'a printf

Base command line output asked for by the CLI. Level is Base

val fail : ('a'b) printf_fatal

Failure due to external circumstances, generally wrong user input, then exit with code 1. Level is Base

val err : 'a printf

Declare a non-fatal internal error. Level is Err

val fatal : ('a'b) printf_fatal

Declare a fatal internal error then exit with code 2. Level is Err

val warn : 'a printf

Raise a warning. Level is Warn

val info : 'a printf

Print general information about what's happening. Level is Info

val debug : 'a printf

Print debugging information about what's happening. Level is Debug

val has_debug : unit -> bool

Tell if debug logging is enabled

\ No newline at end of file +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

module S : String

Signature

val set_level : level -> unit

Override the level of this logger. It may still be overridden by the command line

val get_level : unit -> level

Get the current level

val log : level -> 'a printf

Log a specific level using a format string

val log_fatal : code:int -> level -> ('a, 'b) printf_fatal

Log a fatal problem with format string then shutdown with code

val base : 'a printf

Base command line output asked for by the CLI. Level is Base

val fail : ('a, 'b) printf_fatal

Failure due to external circumstances, generally wrong user input, then exit with code 1. Level is Base

val err : 'a printf

Declare a non-fatal internal error. Level is Err

val fatal : ('a, 'b) printf_fatal

Declare a fatal internal error then exit with code 2. Level is Err

val warn : 'a printf

Raise a warning. Level is Warn

val info : 'a printf

Print general information about what's happening. Level is Info

val debug : 'a printf

Print debugging information about what's happening. Level is Debug

val has_debug : unit -> bool

Tell if debug logging is enabled

diff --git a/doc/html/read-dwarf/Utils/Logs/index.html b/doc/html/read-dwarf/Utils/Logs/index.html index 6d477894..874c8cb8 100644 --- a/doc/html/read-dwarf/Utils/Logs/index.html +++ b/doc/html/read-dwarf/Utils/Logs/index.html @@ -1,4 +1,12 @@ -Logs (read-dwarf.Utils.Logs)

Module Utils.Logs

type 'a printf = ('a, Stdlib.out_channel, unit) Stdlib.format -> 'a

The type of normal logging function

type ('a, 'b) printf_fatal = ('a, Stdlib.out_channel, unit, 'b) Stdlib.format4 -> 'a

The type of a failing logging function, that will exit after printing it's message

type level =
| Base

The actual output. The only thing printed in quiet mode. Should only appear in CLI modules

| 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 pp_level : level -> Utils.Pp.document

Pretty prints a level

val level_to_string : level -> string

Convert level to string

val level_to_header : level -> string

Convert level to string header like "[Error]". Base is the empty string

val set_default_level : level -> unit

Set a default level to all the modules. Erase local customized levels

val set_level : string -> level -> unit

Set level of a module by name

val level_conv : level Cmdliner.Arg.conv

Parser for log level on command line

val set_stdout_level : level -> unit

Set level below which the output goes to stdout

module type String = sig ... end
module Logger : functor (S : String) -> sig ... end

This declare a logger instance. The string parameter is the logger name. It should be the OCaml module name. The normal way of instantiating this module is:

val process_opts : bool -> bool list -> string list -> string list -> level -> unit
val term : unit Cmdliner.Term.t
\ No newline at end of file +end)

You can then use all the function in Logger to print logging messages in your module.

The rest of this module is about manipulation log level in the different modules. You can dynamically interact with the logs level from the command line by using CommonOpt.logs

type 'a printf = ('a, Stdlib.out_channel, unit) Stdlib.format -> 'a

The type of normal logging function

type ('a, 'b) printf_fatal = + ('a, Stdlib.out_channel, unit, 'b) Stdlib.format4 -> + 'a

The type of a failing logging function, that will exit after printing it's message

type level =
  1. | Base
    (*

    The actual output. The only thing printed in quiet mode. Should only appear in CLI modules

    *)
  2. | Err
    (*

    An error message

    *)
  3. | Warn
    (*

    An warning

    *)
  4. | Info
    (*

    Details on what happens that should be understandable if the person do not know the corresponding module

    *)
  5. | Debug
    (*

    Everything happening in detail

    *)

The type of log level

val pp_level : level -> Pp.document

Pretty prints a level

val level_to_string : level -> string

Convert level to string

val level_to_header : level -> string

Convert level to string header like "[Error]". Base is the empty string

val set_default_level : level -> unit

Set a default level to all the modules. Erase local customized levels

val set_level : string -> level -> unit

Set level of a module by name

val level_conv : level Cmdliner.Arg.conv

Parser for log level on command line

val set_stdout_level : level -> unit

Set level below which the output goes to stdout

module type String = sig ... end
module Logger (S : String) : sig ... end

This declare a logger instance. The string parameter is the logger name. It should be the OCaml module name. The normal way of instantiating this module is:

val process_opts : + bool -> + bool list -> + string list -> + string list -> + level -> + unit
val term : unit Cmdliner.Term.t
diff --git a/doc/html/read-dwarf/Utils/Logs/module-type-String/index.html b/doc/html/read-dwarf/Utils/Logs/module-type-String/index.html index db2bc1d0..3fe2f45c 100644 --- a/doc/html/read-dwarf/Utils/Logs/module-type-String/index.html +++ b/doc/html/read-dwarf/Utils/Logs/module-type-String/index.html @@ -1,2 +1,2 @@ -String (read-dwarf.Utils.Logs.String)

Module type Logs.String

val str : string
\ No newline at end of file +String (read-dwarf.Utils.Logs.String)

Module type Logs.String

val str : string
diff --git a/doc/html/read-dwarf/Utils/Option/index.html b/doc/html/read-dwarf/Utils/Option/index.html index 612e64b4..c443cd3f 100644 --- a/doc/html/read-dwarf/Utils/Option/index.html +++ b/doc/html/read-dwarf/Utils/Option/index.html @@ -1,2 +1,2 @@ -Option (read-dwarf.Utils.Option)

Module Utils.Option

include module type of Stdlib.Option
type 'a t = 'a option =
| None
| Some of 'a
val none : 'a option
val some : 'a -> 'a option
val value : 'a option -> default:'a -> 'a
val get : 'a option -> 'a
val bind : 'a option -> ('a -> 'b option) -> 'b option
val join : 'a option option -> 'a option
val map : ('a -> 'b) -> 'a option -> 'b option
val fold : none:'a -> some:('b -> 'a) -> 'b option -> 'a
val iter : ('a -> unit) -> 'a option -> unit
val is_none : 'a option -> bool
val is_some : 'a option -> bool
val equal : ('a -> 'a -> bool) -> 'a option -> 'a option -> bool
val compare : ('a -> 'a -> int) -> 'a option -> 'a option -> int
val to_result : none:'e -> 'a option -> ('a'e) Stdlib.result
val to_list : 'a option -> 'a list
val to_seq : 'a option -> 'a Stdlib.Seq.t

Utility

val take_first : 'a option -> 'a option -> 'a option

Take the value in the first argument if there is one, otherwise take the value in the second argument, otherwise None

val (|||) : 'a option -> 'a option -> 'a option

take_first as an operator:

Behave like boolean or but keep the value of the first option that gave true. This is associative but obviously not commutative.

val take_first_list : 'a option list -> 'a option

Take the value of the first Some in the list. returns None if all the option were None

val take_all : 'a option -> 'b option -> ('a * 'b) option

If both option have values, give Some of the pair, otherwise None

val (&&&) : 'a option -> 'b option -> ('a * 'b) option

Take_all as an operator:

Behave like boolean and but keep all the value of the options This is not associative because at type level (a * b) * c is not a * (b * c). Using monadic bindings is recommended for more that 2 operands.

val value_fail : 'a option -> ('b, unit, string, 'a) Stdlib.format4 -> 'b

Expect the option to contain a value and fails (Failure) otherwise. The format string specify the content of the failure

val value_fun : 'a option -> default:(unit -> 'a) -> 'a

Like Stdlib.Option.value but the default is a called function, that can throw instead of giving a value

val of_bool : some:'a -> bool -> 'a option

Create an option from a bool, with the some value

val of_bool_fun : some:(unit -> 'a) -> bool -> 'a option

Create an option from a bool, with the some value as computed by the some function

val for_all : ('a -> bool) -> 'a option -> bool

for_all p o = fold ~none:true ~some:p

val exists : ('a -> bool) -> 'a option -> bool

exists p o = fold ~none:false ~some:p

val guard : bool -> 'a -> 'a option

Return the second argument if the first is true, otherwise None

val guardn : bool -> 'a -> 'a option

Return the second argument if the first is false, otherwise None

Monadic bindings

val let+ : 'a option -> ('a -> 'b) -> 'b option

Applicative let.

let+ x = mx in e is Option.map (fun x -> e) mx

val and+ : 'a option -> 'b option -> ('a * 'b) option

Applicative and.

let+ x = mx and+ y = my in e give Some e if both mx and my were Somes.

val let+! : 'a option -> ('a -> unit) -> unit

Iter applicative let.

let+! x = mx in e runs e if mx contained a value i.e Option.iter (fun x -> e) mx

val let* : 'a option -> ('a -> 'b option) -> 'b option

Monadic let: let* x = mx in e is Option.bind mx (fun x -> e)

val and* : 'a option -> 'b option -> ('a * 'b) option

Monadic and: let* x = mx and* y = my in e is let* x = mx in let* y = my in e

Lists

val lift : 'a option list -> 'a list option

Commute the list and the option. If the list contains one None then the result is None. If you want to keep all the Some value, use List.filter_map.

This can be condidered as a list-wide take_all. A list-wide take_first would be List.find_map

val map_lift : ('a -> 'b option) -> 'a list -> 'b list option

The same as a List.map and then a lift

Pairs

val lift_pair : ('a option * 'b option) -> ('a * 'b) option

Lift a pair of options to an option of pair. It is the same as take_all.

val unlift_pair : ('a * 'b) option -> 'a option * 'b option

Unlift an option of pair to a pair of options

\ No newline at end of file +Option (read-dwarf.Utils.Option)

Module Utils.Option

This module extends the base OCaml API of Option.

In particular, it adds:

  • Monadic bindings
  • Option merging
  • Option lifting of list and pairs.
include module type of Stdlib.Option
type !'a t = 'a option =
  1. | None
  2. | Some of 'a
val none : 'a option
val some : 'a -> 'a option
val value : 'a option -> default:'a -> 'a
val get : 'a option -> 'a
val bind : 'a option -> ('a -> 'b option) -> 'b option
val join : 'a option option -> 'a option
val map : ('a -> 'b) -> 'a option -> 'b option
val fold : none:'a -> some:('b -> 'a) -> 'b option -> 'a
val iter : ('a -> unit) -> 'a option -> unit
val is_none : 'a option -> bool
val is_some : 'a option -> bool
val equal : ('a -> 'a -> bool) -> 'a option -> 'a option -> bool
val compare : ('a -> 'a -> int) -> 'a option -> 'a option -> int
val to_result : none:'e -> 'a option -> ('a, 'e) Stdlib.result
val to_list : 'a option -> 'a list
val to_seq : 'a option -> 'a Stdlib.Seq.t

Utility

val take_first : 'a option -> 'a option -> 'a option

Take the value in the first argument if there is one, otherwise take the value in the second argument, otherwise None

val (|||) : 'a option -> 'a option -> 'a option

take_first as an operator:

Behave like boolean or but keep the value of the first option that gave true. This is associative but obviously not commutative.

val take_first_list : 'a option list -> 'a option

Take the value of the first Some in the list. returns None if all the option were None

val take_all : 'a option -> 'b option -> ('a * 'b) option

If both option have values, give Some of the pair, otherwise None

val (&&&) : 'a option -> 'b option -> ('a * 'b) option

Take_all as an operator:

Behave like boolean and but keep all the value of the options This is not associative because at type level (a * b) * c is not a * (b * c). Using monadic bindings is recommended for more that 2 operands.

val value_fail : 'a option -> ('b, unit, string, 'a) Stdlib.format4 -> 'b

Expect the option to contain a value and fails (Failure) otherwise. The format string specify the content of the failure

val value_fun : 'a option -> default:(unit -> 'a) -> 'a

Like Stdlib.Option.value but the default is a called function, that can throw instead of giving a value

val of_bool : some:'a -> bool -> 'a option

Create an option from a bool, with the some value

val of_bool_fun : some:(unit -> 'a) -> bool -> 'a option

Create an option from a bool, with the some value as computed by the some function

val for_all : ('a -> bool) -> 'a option -> bool

for_all p o = fold ~none:true ~some:p

val exists : ('a -> bool) -> 'a option -> bool

exists p o = fold ~none:false ~some:p

val guard : bool -> 'a -> 'a option

Return the second argument if the first is true, otherwise None

val guardn : bool -> 'a -> 'a option

Return the second argument if the first is false, otherwise None

Monadic bindings

val (let+) : 'a option -> ('a -> 'b) -> 'b option

Applicative let.

let+ x = mx in e is Option.map (fun x -> e) mx

val (and+) : 'a option -> 'b option -> ('a * 'b) option

Applicative and.

let+ x = mx and+ y = my in e give Some e if both mx and my were Somes.

val (let+!) : 'a option -> ('a -> unit) -> unit

Iter applicative let.

let+! x = mx in e runs e if mx contained a value i.e Option.iter (fun x -> e) mx

val (let*) : 'a option -> ('a -> 'b option) -> 'b option

Monadic let: let* x = mx in e is Option.bind mx (fun x -> e)

val (and*) : 'a option -> 'b option -> ('a * 'b) option

Monadic and: let* x = mx and* y = my in e is let* x = mx in let* y = my in e

Lists

val lift : 'a option list -> 'a list option

Commute the list and the option. If the list contains one None then the result is None. If you want to keep all the Some value, use List.filter_map.

This can be condidered as a list-wide take_all. A list-wide take_first would be List.find_map

val map_lift : ('a -> 'b option) -> 'a list -> 'b list option

The same as a List.map and then a lift

Pairs

val lift_pair : ('a option * 'b option) -> ('a * 'b) option

Lift a pair of options to an option of pair. It is the same as take_all.

val unlift_pair : ('a * 'b) option -> 'a option * 'b option

Unlift an option of pair to a pair of options

diff --git a/doc/html/read-dwarf/Utils/Pair/index.html b/doc/html/read-dwarf/Utils/Pair/index.html index 01057086..164dc1ef 100644 --- a/doc/html/read-dwarf/Utils/Pair/index.html +++ b/doc/html/read-dwarf/Utils/Pair/index.html @@ -1,2 +1,12 @@ -Pair (read-dwarf.Utils.Pair)

Module Utils.Pair

val map : ('a -> 'c) -> ('b -> 'd) -> ('a * 'b) -> 'c * 'd

Map each function on one side of the pair

val iter : ('a -> unit) -> ('b -> unit) -> ('a * 'b) -> unit

Iter each function on one side of the pair

val swap : ('a * 'b) -> 'b * 'a

Swap the element of a pair

val compare : ?⁠fst:('a -> 'a -> int) -> ?⁠snd:('b -> 'b -> int) -> ('a * 'b) -> ('a * 'b) -> int

Compare a pair using provided comparison function. Both fst and snd default to the polymorphic compare

val equal : ?⁠fst:('a -> 'a -> bool) -> ?⁠snd:('b -> 'b -> bool) -> ('a * 'b) -> ('a * 'b) -> bool

Test pair equality using provided equality function. Both fst and snd default to the polymorphic equality

val make : 'a -> 'b -> 'a * 'b

Just build the pair, not useful on it's own but List.combine is just List.map2 Pair.make, and there are some other cases where it is handy for high-order programming

val split : 'a -> 'a * 'a

Just build a pair with twice the element. Useful in high order programming

val for_all : ('a -> bool) -> ('b -> bool) -> ('a * 'b) -> bool

Check that both individual predicates hold

val exists : ('a -> bool) -> ('b -> bool) -> ('a * 'b) -> bool

Check that at least one of the individual predicates holds

\ No newline at end of file +Pair (read-dwarf.Utils.Pair)

Module Utils.Pair

This module contain random utility functions dealing with pairs

val map : ('a -> 'c) -> ('b -> 'd) -> ('a * 'b) -> 'c * 'd

Map each function on one side of the pair

val iter : ('a -> unit) -> ('b -> unit) -> ('a * 'b) -> unit

Iter each function on one side of the pair

val swap : ('a * 'b) -> 'b * 'a

Swap the element of a pair

val compare : + ?fst:('a -> 'a -> int) -> + ?snd:('b -> 'b -> int) -> + ('a * 'b) -> + ('a * 'b) -> + int

Compare a pair using provided comparison function. Both fst and snd default to the polymorphic compare

val equal : + ?fst:('a -> 'a -> bool) -> + ?snd:('b -> 'b -> bool) -> + ('a * 'b) -> + ('a * 'b) -> + bool

Test pair equality using provided equality function. Both fst and snd default to the polymorphic equality

val make : 'a -> 'b -> 'a * 'b

Just build the pair, not useful on it's own but List.combine is just List.map2 Pair.make, and there are some other cases where it is handy for high-order programming

val split : 'a -> 'a * 'a

Just build a pair with twice the element. Useful in high order programming

val for_all : ('a -> bool) -> ('b -> bool) -> ('a * 'b) -> bool

Check that both individual predicates hold

val exists : ('a -> bool) -> ('b -> bool) -> ('a * 'b) -> bool

Check that at least one of the individual predicates holds

diff --git a/doc/html/read-dwarf/Utils/Pp/class-type-custom/index.html b/doc/html/read-dwarf/Utils/Pp/class-type-custom/index.html index 74f26f9a..60572228 100644 --- a/doc/html/read-dwarf/Utils/Pp/class-type-custom/index.html +++ b/doc/html/read-dwarf/Utils/Pp/class-type-custom/index.html @@ -1,2 +1,2 @@ -custom (read-dwarf.Utils.Pp.custom)

Class type Pp.custom

method compact : output -> unit
method pretty : output -> state -> int -> bool -> unit
method requirement : requirement
\ No newline at end of file +custom (read-dwarf.Utils.Pp.custom)

Class type Pp.custom

method compact : output -> unit
method pretty : output -> state -> int -> bool -> unit
method requirement : requirement
diff --git a/doc/html/read-dwarf/Utils/Pp/class-type-output/index.html b/doc/html/read-dwarf/Utils/Pp/class-type-output/index.html index 5b213ff7..265c379e 100644 --- a/doc/html/read-dwarf/Utils/Pp/class-type-output/index.html +++ b/doc/html/read-dwarf/Utils/Pp/class-type-output/index.html @@ -1,2 +1,2 @@ -output (read-dwarf.Utils.Pp.output)

Class type Pp.output

method char : char -> unit
method substring : string -> int -> int -> unit
\ No newline at end of file +output (read-dwarf.Utils.Pp.output)

Class type Pp.output

method char : char -> unit
method substring : string -> int -> int -> unit
diff --git a/doc/html/read-dwarf/Utils/Pp/index.html b/doc/html/read-dwarf/Utils/Pp/index.html index 00ed5bf3..50d84486 100644 --- a/doc/html/read-dwarf/Utils/Pp/index.html +++ b/doc/html/read-dwarf/Utils/Pp/index.html @@ -1,12 +1,52 @@ -Pp (read-dwarf.Utils.Pp)

Module Utils.Pp

This is a wrapper around the pprint library

include PPrint
type document = PPrintEngine.document
val empty : document
val char : char -> document
val string : string -> document
val substring : string -> int -> int -> document
val fancystring : string -> int -> document
val fancysubstring : string -> int -> int -> int -> document
val utf8string : string -> document
val utf8format : ('a, unit, string, document) Stdlib.format4 -> 'a
val hardline : document
val blank : int -> document
val break : int -> document
val (^^) : document -> document -> document
val nest : int -> document -> document
val group : document -> document
val ifflat : document -> document -> document
val align : document -> document
type point = int * int
type range = point * point
val range : (range -> unit) -> document -> document
module ToChannel = PPrintEngine.ToChannel
module ToBuffer = PPrintEngine.ToBuffer
module ToFormatter = PPrintEngine.ToFormatter
type requirement = int
val infinity : requirement
class type output = object ... end
type state = PPrintEngine.state = {
width : int;
ribbon : int;
mutable last_indent : int;
mutable line : int;
mutable column : int;
}
class type custom = object ... end
val custom : custom -> document
val requirement : document -> requirement
val pretty : output -> state -> int -> bool -> document -> unit
val compact : output -> document -> unit
val lparen : PPrintEngine.document
val rparen : PPrintEngine.document
val langle : PPrintEngine.document
val rangle : PPrintEngine.document
val lbrace : PPrintEngine.document
val rbrace : PPrintEngine.document
val lbracket : PPrintEngine.document
val rbracket : PPrintEngine.document
val squote : PPrintEngine.document
val dquote : PPrintEngine.document
val bquote : PPrintEngine.document
val semi : PPrintEngine.document
val colon : PPrintEngine.document
val comma : PPrintEngine.document
val space : PPrintEngine.document
val dot : PPrintEngine.document
val sharp : PPrintEngine.document
val slash : PPrintEngine.document
val backslash : PPrintEngine.document
val equals : PPrintEngine.document
val qmark : PPrintEngine.document
val tilde : PPrintEngine.document
val at : PPrintEngine.document
val percent : PPrintEngine.document
val dollar : PPrintEngine.document
val caret : PPrintEngine.document
val ampersand : PPrintEngine.document
val star : PPrintEngine.document
val plus : PPrintEngine.document
val minus : PPrintEngine.document
val underscore : PPrintEngine.document
val bang : PPrintEngine.document
val bar : PPrintEngine.document
val precede : PPrintEngine.document -> PPrintEngine.document -> PPrintEngine.document
val terminate : PPrintEngine.document -> PPrintEngine.document -> PPrintEngine.document
val enclose : PPrintEngine.document -> PPrintEngine.document -> PPrintEngine.document -> PPrintEngine.document
val squotes : PPrintEngine.document -> PPrintEngine.document
val dquotes : PPrintEngine.document -> PPrintEngine.document
val bquotes : PPrintEngine.document -> PPrintEngine.document
val braces : PPrintEngine.document -> PPrintEngine.document
val parens : PPrintEngine.document -> PPrintEngine.document
val angles : PPrintEngine.document -> PPrintEngine.document
val brackets : PPrintEngine.document -> PPrintEngine.document
val twice : PPrintEngine.document -> PPrintEngine.document
val repeat : int -> PPrintEngine.document -> PPrintEngine.document
val concat : PPrintEngine.document list -> PPrintEngine.document
val separate : PPrintEngine.document -> PPrintEngine.document list -> PPrintEngine.document
val concat_map : ('a -> PPrintEngine.document) -> 'a list -> PPrintEngine.document
val separate_map : PPrintEngine.document -> ('a -> PPrintEngine.document) -> 'a list -> PPrintEngine.document
val separate2 : PPrintEngine.document -> PPrintEngine.document -> PPrintEngine.document list -> PPrintEngine.document
val optional : ('a -> PPrintEngine.document) -> 'a option -> PPrintEngine.document
val lines : string -> PPrintEngine.document list
val arbitrary_string : string -> PPrintEngine.document
val words : string -> PPrintEngine.document list
val split : (char -> bool) -> string -> PPrintEngine.document list
val flow : PPrintEngine.document -> PPrintEngine.document list -> PPrintEngine.document
val flow_map : PPrintEngine.document -> ('a -> PPrintEngine.document) -> 'a list -> PPrintEngine.document
val url : string -> PPrintEngine.document
val hang : int -> PPrintEngine.document -> PPrintEngine.document
val prefix : int -> int -> PPrintEngine.document -> PPrintEngine.document -> PPrintEngine.document
val jump : int -> int -> PPrintEngine.document -> PPrintEngine.document
val infix : int -> int -> PPrintEngine.document -> PPrintEngine.document -> PPrintEngine.document -> PPrintEngine.document
val surround : int -> int -> PPrintEngine.document -> PPrintEngine.document -> PPrintEngine.document -> PPrintEngine.document
val soft_surround : int -> int -> PPrintEngine.document -> PPrintEngine.document -> PPrintEngine.document -> PPrintEngine.document
val surround_separate : int -> int -> PPrintEngine.document -> PPrintEngine.document -> PPrintEngine.document -> PPrintEngine.document -> PPrintEngine.document list -> PPrintEngine.document
val surround_separate_map : int -> int -> PPrintEngine.document -> PPrintEngine.document -> PPrintEngine.document -> PPrintEngine.document -> ('a -> PPrintEngine.document) -> 'a list -> PPrintEngine.document
val (!^) : string -> PPrintEngine.document
val (^/^) : PPrintEngine.document -> PPrintEngine.document -> PPrintEngine.document
val (^//^) : PPrintEngine.document -> PPrintEngine.document -> PPrintEngine.document
module OCaml = PPrintOCaml

Rendering documents

This section provide function to render documents in a text output.

However unless you know what you are doing, you may prefer using the Logs module with the function of "Render documents in format strings", instead of calling those function directly.

val fprint : Stdlib.out_channel -> document -> unit

Render the document in a pretty manner on the provided out_channel

val fprintln : Stdlib.out_channel -> document -> unit

Render the document in a compact manner on the provided out_channel

val print : document -> unit

Print the document on stdout. The general way of

val println : document -> unit

Print the document on stdout, then a endline, then flush

val eprintln : document -> unit

Print the document on stderr, then a endline, then flush

val sprintc : ToBuffer.document -> string

Print the document in a string, in a compact manner

val sprint : ToBuffer.document -> string

Print the document in a string, in a pretty manner

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 -> unit

Convert a document printer to a Printf format string via %t for function outputing on an out_channel

val topi : ('a -> document) -> 'a -> Stdlib.out_channel -> unit

Same as top but add 4 space of indentation everywhere on the object. The general use is:

Printf.printf "Object:\n%t\n" Pp.(topi printer object)

and it will print:

Object:
-    object line 1
-    object line 2
val tos : ('a -> ToBuffer.document) -> 'a -> unit -> string

Convert a document printer to a Printf format string via %t for function outputing in a string

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, PPrintEngine.document) Stdlib.format4 -> 'a

Printf like function that returns a document of the formatted string. In the end, it's just a string document, nothing more complex

val space : document

A breakable space

val nbspace : document

A non-breakable space (equivalent to char ' ')

val bool : bool -> PPrintEngine.document

Print a boolean as "true" or "false"

val int : int -> document

Print an int in decimal

val hex : int -> document

Print an int in hexadecimal as if it was unsigned

val shex : int -> document

Print an int in hexadecimal with a sign: -1 will be printed to -0x1

val ptr : int -> document

Print an int in hexadecimal with the 0x prefix

val byte : char -> document

Print a byte as 2 hexadecimal digit

val hex16 : int -> document

Print an unsigned 16 bit integer as 4 hexadecimal digit

val hex32 : int32 -> document

Print an unsigned 32 bit integer as 8 hexadecimal digit

val hex64 : int64 -> document

Print an unsigned 64 bit integer as 16 hexadecimal digit

val array : ('a -> OCaml.representation) -> 'a array -> OCaml.representation

Print an array when provided with an element printer

val list : ('a -> OCaml.representation) -> 'a list -> OCaml.representation

Print an list when provided with an element printer

val opt : ('a -> OCaml.representation) -> 'a option -> OCaml.representation

Print an option when provided with an element printer

val pair : ('a -> OCaml.representation) -> ('b -> OCaml.representation) -> ('a * 'b) -> OCaml.representation

Print an pair when provided with both element printers

val tup3 : ('a -> OCaml.representation) -> ('b -> OCaml.representation) -> ('c -> OCaml.representation) -> ('a * 'b * 'c) -> OCaml.representation

Print an 3-sized tuple when provided with all three element printers

val qstring : string -> PPrintEngine.document

Print a quoted string

val erase : 'a -> document

Ignore the input and print nothing

val mapping : string -> (document * document) list -> document

Prints a mapping with this style:

name{
+Pp (read-dwarf.Utils.Pp)

Module Utils.Pp

This module provide all pretty printing functionality. It's main goal is not directly handle the output but to handle how to layout complex data structure in a text format.

The main idea of this library is to separate the layout description phase from the part where you actually lay out the thing to pretty print. Thus the two stage of pretty printing is first to generate a document object describing the layout from the object to print and then render the document to the string using one of the printing

This is a wrapper around the pprint library

include module type of struct include PPrint end
type document = PPrint.document
val empty : document
val char : char -> document
val string : string -> document
val substring : string -> int -> int -> document
val fancystring : string -> int -> document
val fancysubstring : string -> int -> int -> int -> document
val utf8string : string -> document
val utf8format : ('a, unit, string, document) Stdlib.format4 -> 'a
val hardline : document
val blank : int -> document
val break : int -> document
val (^^) : document -> document -> document
val group : document -> document
val ifflat : document -> document -> document
val nest : int -> document -> document
val align : document -> document
type point = int * int
type range = point * point
val range : (range -> unit) -> document -> document
val is_empty : document -> bool
module type RENDERER = PPrint.RENDERER
module ToChannel = PPrint.ToChannel
module ToBuffer = PPrint.ToBuffer
module ToFormatter = PPrint.ToFormatter
type requirement = int
val infinity : requirement
class type output = object ... end
type state = PPrint.state = {
  1. width : int;
  2. ribbon : int;
  3. mutable last_indent : int;
  4. mutable line : int;
  5. mutable column : int;
}
class type custom = object ... end
val custom : custom -> document
val requirement : document -> requirement
val pretty : output -> state -> int -> bool -> document -> unit
val compact : output -> document -> unit
val lparen : document
val rparen : document
val langle : document
val rangle : document
val lbrace : document
val rbrace : document
val lbracket : document
val rbracket : document
val squote : document
val dquote : document
val bquote : document
val semi : document
val colon : document
val comma : document
val dot : document
val sharp : document
val slash : document
val backslash : document
val equals : document
val qmark : document
val tilde : document
val at : document
val percent : document
val dollar : document
val caret : document
val ampersand : document
val star : document
val plus : document
val minus : document
val underscore : document
val bang : document
val bar : document
val precede : document -> document -> document
val terminate : document -> document -> document
val enclose : document -> document -> document -> document
val squotes : document -> document
val dquotes : document -> document
val bquotes : document -> document
val braces : document -> document
val parens : document -> document
val angles : document -> document
val brackets : document -> document
val twice : document -> document
val repeat : int -> document -> document
val concat : document list -> document
val separate : document -> document list -> document
val concat_map : ('a -> document) -> 'a list -> document
val separate_map : document -> ('a -> document) -> 'a list -> document
val separate2 : document -> document -> document list -> document
val optional : ('a -> document) -> 'a option -> document
val lines : string -> document list
val arbitrary_string : string -> document
val words : string -> document list
val split : (char -> bool) -> string -> document list
val flow : document -> document list -> document
val flow_map : document -> ('a -> document) -> 'a list -> document
val url : string -> document
val hang : int -> document -> document
val prefix : int -> int -> document -> document -> document
val jump : int -> int -> document -> document
val infix : int -> int -> document -> document -> document -> document
val surround : int -> int -> document -> document -> document -> document
val soft_surround : int -> int -> document -> document -> document -> document
val surround_separate : + int -> + int -> + document -> + document -> + document -> + document -> + document list -> + document
val surround_separate_map : + int -> + int -> + document -> + document -> + document -> + document -> + ('a -> document) -> + 'a list -> + document
val (!^) : string -> document
val (^/^) : document -> document -> document
val (^//^) : document -> document -> document
module OCaml = PPrint.OCaml

Rendering documents

This section provide function to render documents in a text output.

However unless you know what you are doing, you may prefer using the Logs module with the function of "Render documents in format strings", instead of calling those function directly.

val fprint : Stdlib.out_channel -> document -> unit

Render the document in a pretty manner on the provided out_channel

val fprintln : Stdlib.out_channel -> document -> unit

Render the document in a compact manner on the provided out_channel

val print : document -> unit

Print the document on stdout. The general way of

val println : document -> unit

Print the document on stdout, then a endline, then flush

val eprintln : document -> unit

Print the document on stderr, then a endline, then flush

val sprintc : ToBuffer.document -> string

Print the document in a string, in a compact manner

val sprint : ToBuffer.document -> string

Print the document in a string, in a pretty manner

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 -> unit

Convert a document printer to a Printf format string via %t for function outputing on an out_channel

val topi : ('a -> document) -> 'a -> Stdlib.out_channel -> unit

Same as top but add 4 space of indentation everywhere on the object. The general use is:

Printf.printf "Object:\n%t\n" Pp.(topi printer object)

and it will print:

Object:
+object line 1
+object line 2
val tos : ('a -> ToBuffer.document) -> 'a -> unit -> string

Convert a document printer to a Printf format string via %t for function outputing in a string

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 -> 'a

Printf like function that returns a document of the formatted string. In the end, it's just a string document, nothing more complex

val space : document

A breakable space

val nbspace : document

A non-breakable space (equivalent to char ' ')

val bool : bool -> document

Print a boolean as "true" or "false"

val int : int -> document

Print an int in decimal

val hex : int -> document

Print an int in hexadecimal as if it was unsigned

val shex : int -> document

Print an int in hexadecimal with a sign: -1 will be printed to -0x1

val ptr : int -> document

Print an int in hexadecimal with the 0x prefix

val byte : char -> document

Print a byte as 2 hexadecimal digit

val hex16 : int -> document

Print an unsigned 16 bit integer as 4 hexadecimal digit

val hex32 : int32 -> document

Print an unsigned 32 bit integer as 8 hexadecimal digit

val hex64 : int64 -> document

Print an unsigned 64 bit integer as 16 hexadecimal digit

val array : ('a -> PPrint.document) -> 'a array -> PPrint.document

Print an array when provided with an element printer

val list : ('a -> PPrint.document) -> 'a list -> PPrint.document

Print an list when provided with an element printer

val opt : ('a -> PPrint.document) -> 'a option -> PPrint.document

Print an option when provided with an element printer

val pair : + ('a -> PPrint.document) -> + ('b -> PPrint.document) -> + ('a * 'b) -> + PPrint.document

Print an pair when provided with both element printers

val tup3 : + ('a -> PPrint.document) -> + ('b -> PPrint.document) -> + ('c -> PPrint.document) -> + ('a * 'b * 'c) -> + PPrint.document

Print an 3-sized tuple when provided with all three element printers

val qstring : string -> document

Print a quoted string

val erase : 'a -> document

Ignore the input and print nothing

val mapping : string -> (document * document) list -> document

Prints a mapping with this style:

name{
     key -> value;
     key -> value;
     key -> value;
-}
val hashtbl : ?⁠name:string -> ('a -> document) -> ('b -> document) -> ('a'b) Stdlib.Hashtbl.t -> document

Print a Hashtbl using mapping

val hashtbl_sorted : compare:('a -> 'a -> int) -> ?⁠name:string -> ('a -> document) -> ('b -> document) -> ('a'b) Stdlib.Hashtbl.t -> document

Print a sorted Hashtbl using mapping

val record : OCaml.type_name -> (OCaml.record_field * OCaml.representation) list -> document

Print a record in the format

name{
+}
val hashtbl : + ?name:string -> + ('a -> document) -> + ('b -> document) -> + ('a, 'b) Stdlib.Hashtbl.t -> + document

Print a Hashtbl using mapping

val hashtbl_sorted : + compare:('a -> 'a -> int) -> + ?name:string -> + ('a -> document) -> + ('b -> document) -> + ('a, 'b) Stdlib.Hashtbl.t -> + document

Print a sorted Hashtbl using mapping

val record : + OCaml.type_name -> + (OCaml.record_field * PPrint.document) list -> + document

Print a record in the format

name{
     field = value;
     field = value;
     field = value;
-}
val separate_mapi : document -> (int -> 'a -> document) -> 'a list -> document

Like separate_map but with the index

val concat_array_map : ('a -> document) -> 'a array -> document

Concatenate the documents produced by the function over the array

val concat_array_mapi : (int -> 'a -> document) -> 'a array -> document

Concatenate the document produced by the function on the array. The function also gets the index of the element

Special printer

val status : (int -> document) -> Unix.process_status -> document

Prints a Unix.process_status with an integer printer

val statusi : Unix.process_status -> document

Prints a Unix.process_status with decimal integers

val statush : Unix.process_status -> document

Prints a Unix.process_status with hexadecimal integers

\ No newline at end of file +}
val separate_mapi : document -> (int -> 'a -> document) -> 'a list -> document

Like separate_map but with the index

val concat_array_map : ('a -> document) -> 'a array -> document

Concatenate the documents produced by the function over the array

val concat_array_mapi : (int -> 'a -> document) -> 'a array -> document

Concatenate the document produced by the function on the array. The function also gets the index of the element

Special printer

val status : (int -> document) -> Unix.process_status -> document

Prints a Unix.process_status with an integer printer

val statusi : Unix.process_status -> document

Prints a Unix.process_status with decimal integers

val statush : Unix.process_status -> document

Prints a Unix.process_status with hexadecimal integers

diff --git a/doc/html/read-dwarf/Utils/Protect/index.html b/doc/html/read-dwarf/Utils/Protect/index.html index 476dab69..4509dd5a 100644 --- a/doc/html/read-dwarf/Utils/Protect/index.html +++ b/doc/html/read-dwarf/Utils/Protect/index.html @@ -1,2 +1,2 @@ -Protect (read-dwarf.Utils.Protect)

Module Utils.Protect

exception Protect_both of exn * exn

This exception it thrown when both function and protector have thrown

val protect : (unit -> 'a) -> (unit -> unit) -> 'a

protect f p runs f then p even if f throws.

If one of f or p throw, then that exception is transmitted as is If both throw, the pair of exceptions is encapsulated in Protect_both and thrown.

This behavior is slightly different from the standard protect.

\ No newline at end of file +Protect (read-dwarf.Utils.Protect)

Module Utils.Protect

This module provide try-with-finally kind of exception handling.

It provides a new kind of protect function.

TODO: Think if this behavior is really useful compared to the standard protect

exception Protect_both of exn * exn

This exception it thrown when both function and protector have thrown

val protect : (unit -> 'a) -> (unit -> unit) -> 'a

protect f p runs f then p even if f throws.

If one of f or p throw, then that exception is transmitted as is If both throw, the pair of exceptions is encapsulated in Protect_both and thrown.

This behavior is slightly different from the standard protect.

diff --git a/doc/html/read-dwarf/Utils/Raise/index.html b/doc/html/read-dwarf/Utils/Raise/index.html index b5b58737..852c024f 100644 --- a/doc/html/read-dwarf/Utils/Raise/index.html +++ b/doc/html/read-dwarf/Utils/Raise/index.html @@ -1,2 +1,2 @@ -Raise (read-dwarf.Utils.Raise)

Module Utils.Raise

val inv_arg : ('a, unit, string, 'b) Stdlib.format4 -> 'a

Printf like funtion that throws an Invalid_Argument with the formated string

val fail : ('a, unit, string, 'b) Stdlib.format4 -> 'a

Printf like funtion that throws a Failure with the formated string

val again : exn -> 'a

Raise again an exception without losing the backtrace.

exception Todo
val todo : unit -> 'a

Put that in unfinished places of the code that need to be completed

exception Unreachable
val unreachable : unit -> 'a

Put that in-place of dead code that is required by the typer

\ No newline at end of file +Raise (read-dwarf.Utils.Raise)

Module Utils.Raise

This module provide convenience facilities to raise exception or other exception management

val inv_arg : ('a, unit, string, 'b) Stdlib.format4 -> 'a

Printf like funtion that throws an Invalid_Argument with the formated string

val fail : ('a, unit, string, 'b) Stdlib.format4 -> 'a

Printf like funtion that throws a Failure with the formated string

val again : exn -> 'a

Raise again an exception without losing the backtrace.

exception Todo
val todo : unit -> 'a

Put that in unfinished places of the code that need to be completed

exception Unreachable
val unreachable : unit -> 'a

Put that in-place of dead code that is required by the typer

diff --git a/doc/html/read-dwarf/Utils/RngMap/IMap/index.html b/doc/html/read-dwarf/Utils/RngMap/IMap/index.html index cea35fce..176ccaa1 100644 --- a/doc/html/read-dwarf/Utils/RngMap/IMap/index.html +++ b/doc/html/read-dwarf/Utils/RngMap/IMap/index.html @@ -1,2 +1,6 @@ -IMap (read-dwarf.Utils.RngMap.IMap)

Module RngMap.IMap

An integer map: Map.Make(Int)

type key = Stdlib.Int.t
type 'a t = 'a Stdlib__map.Make(Stdlib.Int).t
val empty : 'a t
val is_empty : 'a t -> bool
val mem : key -> 'a t -> bool
val add : key -> 'a -> 'a t -> 'a t
val update : key -> ('a option -> 'a option) -> 'a t -> 'a t
val singleton : key -> 'a -> 'a t
val remove : key -> 'a t -> 'a t
val merge : (key -> 'a option -> 'b option -> 'c option) -> 'a t -> 'b t -> 'c t
val union : (key -> 'a -> 'a -> 'a option) -> 'a t -> 'a t -> 'a t
val compare : ('a -> 'a -> int) -> 'a t -> 'a t -> int
val equal : ('a -> 'a -> bool) -> 'a t -> 'a t -> bool
val iter : (key -> 'a -> unit) -> 'a t -> unit
val fold : (key -> 'a -> 'b -> 'b) -> 'a t -> 'b -> 'b
val for_all : (key -> 'a -> bool) -> 'a t -> bool
val exists : (key -> 'a -> bool) -> 'a t -> bool
val filter : (key -> 'a -> bool) -> 'a t -> 'a t
val partition : (key -> 'a -> bool) -> 'a t -> 'a t * 'a t
val cardinal : 'a t -> int
val bindings : 'a t -> (key * 'a) list
val min_binding : 'a t -> key * 'a
val min_binding_opt : 'a t -> (key * 'a) option
val max_binding : 'a t -> key * 'a
val max_binding_opt : 'a t -> (key * 'a) option
val choose : 'a t -> key * 'a
val choose_opt : 'a t -> (key * 'a) option
val split : key -> 'a t -> 'a t * 'a option * 'a t
val find : key -> 'a t -> 'a
val find_opt : key -> 'a t -> 'a option
val find_first : (key -> bool) -> 'a t -> key * 'a
val find_first_opt : (key -> bool) -> 'a t -> (key * 'a) option
val find_last : (key -> bool) -> 'a t -> key * 'a
val find_last_opt : (key -> bool) -> 'a t -> (key * 'a) option
val map : ('a -> 'b) -> 'a t -> 'b t
val mapi : (key -> 'a -> 'b) -> 'a t -> 'b t
val to_seq : 'a t -> (key * 'a) Stdlib.Seq.t
val to_seq_from : key -> 'a t -> (key * 'a) Stdlib.Seq.t
val add_seq : (key * 'a) Stdlib.Seq.t -> 'a t -> 'a t
val of_seq : (key * 'a) Stdlib.Seq.t -> 'a t
\ No newline at end of file +IMap (read-dwarf.Utils.RngMap.IMap)

Module RngMap.IMap

An integer map: Map.Make(Int)

type key = Stdlib.Int.t
type !'a t = 'a Stdlib__Map.Make(Stdlib.Int).t
val empty : 'a t
val add : key -> 'a -> 'a t -> 'a t
val add_to_list : key -> 'a -> 'a list t -> 'a list t
val update : key -> ('a option -> 'a option) -> 'a t -> 'a t
val singleton : key -> 'a -> 'a t
val remove : key -> 'a t -> 'a t
val merge : + (key -> 'a option -> 'b option -> 'c option) -> + 'a t -> + 'b t -> + 'c t
val union : (key -> 'a -> 'a -> 'a option) -> 'a t -> 'a t -> 'a t
val cardinal : 'a t -> int
val bindings : 'a t -> (key * 'a) list
val min_binding : 'a t -> key * 'a
val min_binding_opt : 'a t -> (key * 'a) option
val max_binding : 'a t -> key * 'a
val max_binding_opt : 'a t -> (key * 'a) option
val choose : 'a t -> key * 'a
val choose_opt : 'a t -> (key * 'a) option
val find : key -> 'a t -> 'a
val find_opt : key -> 'a t -> 'a option
val find_first : (key -> bool) -> 'a t -> key * 'a
val find_first_opt : (key -> bool) -> 'a t -> (key * 'a) option
val find_last : (key -> bool) -> 'a t -> key * 'a
val find_last_opt : (key -> bool) -> 'a t -> (key * 'a) option
val iter : (key -> 'a -> unit) -> 'a t -> unit
val fold : (key -> 'a -> 'acc -> 'acc) -> 'a t -> 'acc -> 'acc
val map : ('a -> 'b) -> 'a t -> 'b t
val mapi : (key -> 'a -> 'b) -> 'a t -> 'b t
val filter : (key -> 'a -> bool) -> 'a t -> 'a t
val filter_map : (key -> 'a -> 'b option) -> 'a t -> 'b t
val partition : (key -> 'a -> bool) -> 'a t -> 'a t * 'a t
val split : key -> 'a t -> 'a t * 'a option * 'a t
val is_empty : 'a t -> bool
val mem : key -> 'a t -> bool
val equal : ('a -> 'a -> bool) -> 'a t -> 'a t -> bool
val compare : ('a -> 'a -> int) -> 'a t -> 'a t -> int
val for_all : (key -> 'a -> bool) -> 'a t -> bool
val exists : (key -> 'a -> bool) -> 'a t -> bool
val to_list : 'a t -> (key * 'a) list
val of_list : (key * 'a) list -> 'a t
val to_seq : 'a t -> (key * 'a) Stdlib.Seq.t
val to_rev_seq : 'a t -> (key * 'a) Stdlib.Seq.t
val to_seq_from : key -> 'a t -> (key * 'a) Stdlib.Seq.t
val add_seq : (key * 'a) Stdlib.Seq.t -> 'a t -> 'a t
val of_seq : (key * 'a) Stdlib.Seq.t -> 'a t
diff --git a/doc/html/read-dwarf/Utils/RngMap/Make/argument-1-Obj/index.html b/doc/html/read-dwarf/Utils/RngMap/Make/argument-1-Obj/index.html index 62bdc02e..02072360 100644 --- a/doc/html/read-dwarf/Utils/RngMap/Make/argument-1-Obj/index.html +++ b/doc/html/read-dwarf/Utils/RngMap/Make/argument-1-Obj/index.html @@ -1,2 +1,2 @@ -1-Obj (read-dwarf.Utils.RngMap.Make.1-Obj)

Parameter Make.1-Obj

type t

The type to be indexed by starting addresses, must have a length

val len : t -> int

The type of range end

\ No newline at end of file +Obj (read-dwarf.Utils.RngMap.Make.Obj)

Parameter Make.Obj

type t

The type to be indexed by starting addresses, must have a length

val len : t -> int

The type of range end

diff --git a/doc/html/read-dwarf/Utils/RngMap/Make/index.html b/doc/html/read-dwarf/Utils/RngMap/Make/index.html index 18a830f2..b4fbf802 100644 --- a/doc/html/read-dwarf/Utils/RngMap/Make/index.html +++ b/doc/html/read-dwarf/Utils/RngMap/Make/index.html @@ -1,6 +1,11 @@ -Make (read-dwarf.Utils.RngMap.Make)

Module RngMap.Make

How to make a RngMap from a LenObject

Parameters

Signature

type obj = Obj.t

The type of the contained object

type obj_off = obj * int

The type of an object with an offset

type t

The type of the map from address ranges to obj

val empty : t

An empty RngMap

val is_in : objaddr:int -> obj -> int -> bool

Test if an address is inside the object at address objaddr

val at : t -> int -> obj

Get the object containing the address. Throw Not_found if no object contains the address

val at_opt : t -> int -> obj option

Get the object containing the address. None if no object contains the address

val at_off : t -> int -> obj_off

Get the object containing the address and the offset of the address inside the object

at_off map addr = (obj, off) 

means:

         |                      |           |         |
-       map 0                 obj start    point    obj end
-         |<--------------addr-------------->|
-                                |<---off--->|
-                                |<------len obj------>|

In other words, at_off allow a change of coordinate from the map frame to the object frame.

Throw Not_found if no object contains the address

val at_off_opt : t -> int -> obj_off option

Get the object containing the address and the offset of the address inside the object. See at_off for more explanation.

None if no object contains the address

val update : (obj -> obj) -> t -> int -> t

Update the binding containing the provided address. If no binding contained the address, this is a no-op

val map : (obj -> obj) -> t -> t

Map a function over all the objects

val mapi : (int -> obj -> obj) -> t -> t

Map a function over all the objects with their address

val iter : (obj -> unit) -> t -> unit

Iter a function over all the objects

val iteri : (int -> obj -> unit) -> t -> unit

Iter a function over all the objects with their address

val clear_at : t -> int -> t

Clear the object containing the address if any

val clear : t -> pos:int -> len:int -> t

Clear an area of the RngMap.

If an object is partially in the specified block. It will be removed entirely.

See clear_crop for a different behavior. See clear_bounds to allow some bounds to be infinity.

val clear_crop : t -> pos:int -> len:int -> crop:(pos:int -> len:int -> obj -> obj) -> t

Clear an area of the RngMap.

If a block is partially in the specified block, It will be cropped by using the provided crop function.

crop ~pos ~len obj is supposed to crop the object obj and keep only the segment [pos:pos +len) of it (in the object coordinate frame).

val clear_bounds : ?⁠start:int -> ?⁠endp:int -> t -> t

Same as clear but if a bound is missing, then we erase until infinity in that direction. The target interval is [start:endp).

In particular clear_bounds map = empty.

val add : t -> int -> obj -> t

Add an object at a specific address. The whole range of addresses covered by the object must be free

val addp : t -> obj_off -> t

Add an object at a specific address. The whole range of addresses covered by the object must be free

val bindings : t -> (int * obj) list

Give the list of bindings

val to_seq : ?⁠start:int -> ?⁠endp:int -> t -> (int * obj) Utils.Seq.t

Return a sequence of all the object overlapping the range [start:endp). The first and last element may not be entierly contained in the ranged. If any bound is unspecified, it goes to infinity in that direction.

In particular to_seq map will iterate the entiere RngMap

\ No newline at end of file +Make (read-dwarf.Utils.RngMap.Make)

Module RngMap.Make

How to make a RngMap from a LenObject

Parameters

module Obj : LenObject

Signature

type obj = Obj.t

The type of the contained object

type obj_off = obj * int

The type of an object with an offset

type t

The type of the map from address ranges to obj

val empty : t

An empty RngMap

val is_in : objaddr:int -> obj -> int -> bool

Test if an address is inside the object at address objaddr

val at : t -> int -> obj

Get the object containing the address. Throw Not_found if no object contains the address

val at_opt : t -> int -> obj option

Get the object containing the address. None if no object contains the address

val at_off : t -> int -> obj_off

Get the object containing the address and the offset of the address inside the object

at_off map addr = (obj, off) 

means:

   |                      |           |         |
+ map 0                 obj start    point    obj end
+   |<--------------addr-------------->|
+                          |<---off--->|
+                          |<------len obj------>|

In other words, at_off allow a change of coordinate from the map frame to the object frame.

Throw Not_found if no object contains the address

val at_off_opt : t -> int -> obj_off option

Get the object containing the address and the offset of the address inside the object. See at_off for more explanation.

None if no object contains the address

val update : (obj -> obj) -> t -> int -> t

Update the binding containing the provided address. If no binding contained the address, this is a no-op

val map : (obj -> obj) -> t -> t

Map a function over all the objects

val mapi : (int -> obj -> obj) -> t -> t

Map a function over all the objects with their address

val iter : (obj -> unit) -> t -> unit

Iter a function over all the objects

val iteri : (int -> obj -> unit) -> t -> unit

Iter a function over all the objects with their address

val clear_at : t -> int -> t

Clear the object containing the address if any

val clear : t -> pos:int -> len:int -> t

Clear an area of the RngMap.

If an object is partially in the specified block. It will be removed entirely.

See clear_crop for a different behavior. See clear_bounds to allow some bounds to be infinity.

val clear_crop : + t -> + pos:int -> + len:int -> + crop:(pos:int -> len:int -> obj -> obj) -> + t

Clear an area of the RngMap.

If a block is partially in the specified block, It will be cropped by using the provided crop function.

crop ~pos ~len obj is supposed to crop the object obj and keep only the segment [pos:pos +len) of it (in the object coordinate frame).

val clear_bounds : ?start:int -> ?endp:int -> t -> t

Same as clear but if a bound is missing, then we erase until infinity in that direction. The target interval is [start:endp).

In particular clear_bounds map = empty.

val add : t -> int -> obj -> t

Add an object at a specific address. The whole range of addresses covered by the object must be free

val addp : t -> obj_off -> t

Add an object at a specific address. The whole range of addresses covered by the object must be free

val bindings : t -> (int * obj) list

Give the list of bindings

val to_seq : ?start:int -> ?endp:int -> t -> (int * obj) Seq.t

Return a sequence of all the object overlapping the range [start:endp). The first and last element may not be entierly contained in the ranged. If any bound is unspecified, it goes to infinity in that direction.

In particular to_seq map will iterate the entiere RngMap

diff --git a/doc/html/read-dwarf/Utils/RngMap/PairLenObject/argument-1-Obj/index.html b/doc/html/read-dwarf/Utils/RngMap/PairLenObject/argument-1-Obj/index.html index 46964308..261aa2c9 100644 --- a/doc/html/read-dwarf/Utils/RngMap/PairLenObject/argument-1-Obj/index.html +++ b/doc/html/read-dwarf/Utils/RngMap/PairLenObject/argument-1-Obj/index.html @@ -1,2 +1,2 @@ -1-Obj (read-dwarf.Utils.RngMap.PairLenObject.1-Obj)

Parameter PairLenObject.1-Obj

type t

A type, for functors that accept generic types

\ No newline at end of file +Obj (read-dwarf.Utils.RngMap.PairLenObject.Obj)

Parameter PairLenObject.Obj

type t

A type, for functors that accept generic types

diff --git a/doc/html/read-dwarf/Utils/RngMap/PairLenObject/index.html b/doc/html/read-dwarf/Utils/RngMap/PairLenObject/index.html index 972eb08f..d5414133 100644 --- a/doc/html/read-dwarf/Utils/RngMap/PairLenObject/index.html +++ b/doc/html/read-dwarf/Utils/RngMap/PairLenObject/index.html @@ -1,2 +1,2 @@ -PairLenObject (read-dwarf.Utils.RngMap.PairLenObject)

Module RngMap.PairLenObject

For types that do not have an inner length representation, we add it with a pair

Parameters

Signature

type t = Obj.t * int

The type to be indexed by starting addresses, must have a length

val len : t -> int

The type of range end

\ No newline at end of file +PairLenObject (read-dwarf.Utils.RngMap.PairLenObject)

Module RngMap.PairLenObject

For types that do not have an inner length representation, we add it with a pair

Parameters

module Obj : Object

Signature

type t = Obj.t * int

The type to be indexed by starting addresses, must have a length

val len : t -> int

The type of range end

diff --git a/doc/html/read-dwarf/Utils/RngMap/index.html b/doc/html/read-dwarf/Utils/RngMap/index.html index 64b9d1f9..192e8d2b 100644 --- a/doc/html/read-dwarf/Utils/RngMap/index.html +++ b/doc/html/read-dwarf/Utils/RngMap/index.html @@ -1,2 +1,2 @@ -RngMap (read-dwarf.Utils.RngMap)

Module Utils.RngMap

module IMap : sig ... end

An integer map: Map.Make(Int)

module type Object = sig ... end

A module that represent a simple type with no operation

module type LenObject = sig ... end

A module for type that have a concept of length in the rngMap context

module PairLenObject : functor (Obj : Object) -> LenObject with type t = Obj.t * int

For types that do not have an inner length representation, we add it with a pair

module type S = sig ... end

The signature of the range map

module Make : functor (Obj : LenObject) -> S with type obj = Obj.t

How to make a RngMap from a LenObject

\ No newline at end of file +RngMap (read-dwarf.Utils.RngMap)

Module Utils.RngMap

A module giving a map indexing data with address ranges and providing access to quick access to data corresponding to any value in-between.

Each address is bound to at most one object so the ranges are not allowed to overlap.

In practice you dont specify the range. The size of the bound range is provided by the stored object itself that is assumed to have a length. The stored object must thus provide a len function as specfified by the LenObject signature. However a length can be added to any Object using PairLenObject

An important remark is that maps are perfectly allowed to have negative addresses, and will considered negative integer as negative addresses. An object of size 7 starting at -4 will end at 3

For now, this has a pure immutable interface.

module IMap : sig ... end

An integer map: Map.Make(Int)

module type Object = sig ... end

A module that represent a simple type with no operation

module type LenObject = sig ... end

A module for type that have a concept of length in the rngMap context

module PairLenObject (Obj : Object) : LenObject with type t = Obj.t * int

For types that do not have an inner length representation, we add it with a pair

module type S = sig ... end

The signature of the range map

module Make (Obj : LenObject) : S with type obj = Obj.t

How to make a RngMap from a LenObject

diff --git a/doc/html/read-dwarf/Utils/RngMap/module-type-LenObject/index.html b/doc/html/read-dwarf/Utils/RngMap/module-type-LenObject/index.html index 0bf6665c..ca11f439 100644 --- a/doc/html/read-dwarf/Utils/RngMap/module-type-LenObject/index.html +++ b/doc/html/read-dwarf/Utils/RngMap/module-type-LenObject/index.html @@ -1,2 +1,2 @@ -LenObject (read-dwarf.Utils.RngMap.LenObject)

Module type RngMap.LenObject

A module for type that have a concept of length in the rngMap context

type t

The type to be indexed by starting addresses, must have a length

val len : t -> int

The type of range end

\ No newline at end of file +LenObject (read-dwarf.Utils.RngMap.LenObject)

Module type RngMap.LenObject

A module for type that have a concept of length in the rngMap context

type t

The type to be indexed by starting addresses, must have a length

val len : t -> int

The type of range end

diff --git a/doc/html/read-dwarf/Utils/RngMap/module-type-Object/index.html b/doc/html/read-dwarf/Utils/RngMap/module-type-Object/index.html index f1ac2c22..e6f238f5 100644 --- a/doc/html/read-dwarf/Utils/RngMap/module-type-Object/index.html +++ b/doc/html/read-dwarf/Utils/RngMap/module-type-Object/index.html @@ -1,2 +1,2 @@ -Object (read-dwarf.Utils.RngMap.Object)

Module type RngMap.Object

A module that represent a simple type with no operation

type t

A type, for functors that accept generic types

\ No newline at end of file +Object (read-dwarf.Utils.RngMap.Object)

Module type RngMap.Object

A module that represent a simple type with no operation

type t

A type, for functors that accept generic types

diff --git a/doc/html/read-dwarf/Utils/RngMap/module-type-S/index.html b/doc/html/read-dwarf/Utils/RngMap/module-type-S/index.html index 770cae6d..b5aee7fe 100644 --- a/doc/html/read-dwarf/Utils/RngMap/module-type-S/index.html +++ b/doc/html/read-dwarf/Utils/RngMap/module-type-S/index.html @@ -1,6 +1,11 @@ -S (read-dwarf.Utils.RngMap.S)

Module type RngMap.S

The signature of the range map

type obj

The type of the contained object

type obj_off = obj * int

The type of an object with an offset

type t

The type of the map from address ranges to obj

val empty : t

An empty RngMap

val is_in : objaddr:int -> obj -> int -> bool

Test if an address is inside the object at address objaddr

val at : t -> int -> obj

Get the object containing the address. Throw Not_found if no object contains the address

val at_opt : t -> int -> obj option

Get the object containing the address. None if no object contains the address

val at_off : t -> int -> obj_off

Get the object containing the address and the offset of the address inside the object

at_off map addr = (obj, off) 

means:

         |                      |           |         |
-       map 0                 obj start    point    obj end
-         |<--------------addr-------------->|
-                                |<---off--->|
-                                |<------len obj------>|

In other words, at_off allow a change of coordinate from the map frame to the object frame.

Throw Not_found if no object contains the address

val at_off_opt : t -> int -> obj_off option

Get the object containing the address and the offset of the address inside the object. See at_off for more explanation.

None if no object contains the address

val update : (obj -> obj) -> t -> int -> t

Update the binding containing the provided address. If no binding contained the address, this is a no-op

val map : (obj -> obj) -> t -> t

Map a function over all the objects

val mapi : (int -> obj -> obj) -> t -> t

Map a function over all the objects with their address

val iter : (obj -> unit) -> t -> unit

Iter a function over all the objects

val iteri : (int -> obj -> unit) -> t -> unit

Iter a function over all the objects with their address

val clear_at : t -> int -> t

Clear the object containing the address if any

val clear : t -> pos:int -> len:int -> t

Clear an area of the RngMap.

If an object is partially in the specified block. It will be removed entirely.

See clear_crop for a different behavior. See clear_bounds to allow some bounds to be infinity.

val clear_crop : t -> pos:int -> len:int -> crop:(pos:int -> len:int -> obj -> obj) -> t

Clear an area of the RngMap.

If a block is partially in the specified block, It will be cropped by using the provided crop function.

crop ~pos ~len obj is supposed to crop the object obj and keep only the segment [pos:pos +len) of it (in the object coordinate frame).

val clear_bounds : ?⁠start:int -> ?⁠endp:int -> t -> t

Same as clear but if a bound is missing, then we erase until infinity in that direction. The target interval is [start:endp).

In particular clear_bounds map = empty.

val add : t -> int -> obj -> t

Add an object at a specific address. The whole range of addresses covered by the object must be free

val addp : t -> obj_off -> t

Add an object at a specific address. The whole range of addresses covered by the object must be free

val bindings : t -> (int * obj) list

Give the list of bindings

val to_seq : ?⁠start:int -> ?⁠endp:int -> t -> (int * obj) Utils.Seq.t

Return a sequence of all the object overlapping the range [start:endp). The first and last element may not be entierly contained in the ranged. If any bound is unspecified, it goes to infinity in that direction.

In particular to_seq map will iterate the entiere RngMap

\ No newline at end of file +S (read-dwarf.Utils.RngMap.S)

Module type RngMap.S

The signature of the range map

type obj

The type of the contained object

type obj_off = obj * int

The type of an object with an offset

type t

The type of the map from address ranges to obj

val empty : t

An empty RngMap

val is_in : objaddr:int -> obj -> int -> bool

Test if an address is inside the object at address objaddr

val at : t -> int -> obj

Get the object containing the address. Throw Not_found if no object contains the address

val at_opt : t -> int -> obj option

Get the object containing the address. None if no object contains the address

val at_off : t -> int -> obj_off

Get the object containing the address and the offset of the address inside the object

at_off map addr = (obj, off) 

means:

   |                      |           |         |
+ map 0                 obj start    point    obj end
+   |<--------------addr-------------->|
+                          |<---off--->|
+                          |<------len obj------>|

In other words, at_off allow a change of coordinate from the map frame to the object frame.

Throw Not_found if no object contains the address

val at_off_opt : t -> int -> obj_off option

Get the object containing the address and the offset of the address inside the object. See at_off for more explanation.

None if no object contains the address

val update : (obj -> obj) -> t -> int -> t

Update the binding containing the provided address. If no binding contained the address, this is a no-op

val map : (obj -> obj) -> t -> t

Map a function over all the objects

val mapi : (int -> obj -> obj) -> t -> t

Map a function over all the objects with their address

val iter : (obj -> unit) -> t -> unit

Iter a function over all the objects

val iteri : (int -> obj -> unit) -> t -> unit

Iter a function over all the objects with their address

val clear_at : t -> int -> t

Clear the object containing the address if any

val clear : t -> pos:int -> len:int -> t

Clear an area of the RngMap.

If an object is partially in the specified block. It will be removed entirely.

See clear_crop for a different behavior. See clear_bounds to allow some bounds to be infinity.

val clear_crop : + t -> + pos:int -> + len:int -> + crop:(pos:int -> len:int -> obj -> obj) -> + t

Clear an area of the RngMap.

If a block is partially in the specified block, It will be cropped by using the provided crop function.

crop ~pos ~len obj is supposed to crop the object obj and keep only the segment [pos:pos +len) of it (in the object coordinate frame).

val clear_bounds : ?start:int -> ?endp:int -> t -> t

Same as clear but if a bound is missing, then we erase until infinity in that direction. The target interval is [start:endp).

In particular clear_bounds map = empty.

val add : t -> int -> obj -> t

Add an object at a specific address. The whole range of addresses covered by the object must be free

val addp : t -> obj_off -> t

Add an object at a specific address. The whole range of addresses covered by the object must be free

val bindings : t -> (int * obj) list

Give the list of bindings

val to_seq : ?start:int -> ?endp:int -> t -> (int * obj) Seq.t

Return a sequence of all the object overlapping the range [start:endp). The first and last element may not be entierly contained in the ranged. If any bound is unspecified, it goes to infinity in that direction.

In particular to_seq map will iterate the entiere RngMap

diff --git a/doc/html/read-dwarf/Utils/Seq/index.html b/doc/html/read-dwarf/Utils/Seq/index.html index bf1a944c..93f08152 100644 --- a/doc/html/read-dwarf/Utils/Seq/index.html +++ b/doc/html/read-dwarf/Utils/Seq/index.html @@ -1,2 +1,2 @@ -Seq (read-dwarf.Utils.Seq)

Module Utils.Seq

include Stdlib.Seq
type 'a t = unit -> 'a node
and 'a node = 'a Stdlib__seq.node =
| Nil
| Cons of 'a * 'a t
val empty : 'a t
val return : 'a -> 'a t
val map : ('a -> 'b) -> 'a t -> 'b t
val filter : ('a -> bool) -> 'a t -> 'a t
val filter_map : ('a -> 'b option) -> 'a t -> 'b t
val flat_map : ('a -> 'b t) -> 'a t -> 'b t
val fold_left : ('a -> 'b -> 'a) -> 'a -> 'b t -> 'a
val iter : ('a -> unit) -> 'a t -> unit
val add_count : ?⁠start:int -> 'a t -> (int * 'a) t

Add a counter starting at start (default 0) in front of each element of the sequence

val iota : ?⁠start:int -> int -> int t

Generate an integer sequence up to len. Optionally may start at start instead of 0

val iota_step_up : ?⁠start:int -> step:int -> endi:int -> int t

Generate an integer sequence up to endi by stepping step. Optionally may start at start instead of 0

val stop_at : ('a -> bool) -> 'a t -> 'a t

Make the sequence stop when the condition is met

val cons : 'a -> 'a t -> unit -> 'a node

Add a new element in front of the sequence. That element will appear first before the rest of the sequence.

Added to Stdlib in Ocaml 4.11

val find_map : ('a -> 'b option) -> 'a t -> 'b option

Applies the specified function to the elements of the sequence in order, and returns the first result of the form Some v, or None if no such result was returned.

See List.find_map

\ No newline at end of file +Seq (read-dwarf.Utils.Seq)

Module Utils.Seq

This module is for extending the Seq module of the standard library

include module type of struct include Stdlib.Seq end
type !'a t = unit -> 'a node
and !'a node = 'a Stdlib__Seq.node =
  1. | Nil
  2. | Cons of 'a * 'a t
val is_empty : 'a t -> bool
val uncons : 'a t -> ('a * 'a t) option
val length : 'a t -> int
val iter : ('a -> unit) -> 'a t -> unit
val fold_left : ('acc -> 'a -> 'acc) -> 'acc -> 'a t -> 'acc
val iteri : (int -> 'a -> unit) -> 'a t -> unit
val fold_lefti : ('acc -> int -> 'a -> 'acc) -> 'acc -> 'a t -> 'acc
val for_all : ('a -> bool) -> 'a t -> bool
val exists : ('a -> bool) -> 'a t -> bool
val find : ('a -> bool) -> 'a t -> 'a option
val find_index : ('a -> bool) -> 'a t -> int option
val find_mapi : (int -> 'a -> 'b option) -> 'a t -> 'b option
val iter2 : ('a -> 'b -> unit) -> 'a t -> 'b t -> unit
val fold_left2 : ('acc -> 'a -> 'b -> 'acc) -> 'acc -> 'a t -> 'b t -> 'acc
val for_all2 : ('a -> 'b -> bool) -> 'a t -> 'b t -> bool
val exists2 : ('a -> 'b -> bool) -> 'a t -> 'b t -> bool
val equal : ('a -> 'b -> bool) -> 'a t -> 'b t -> bool
val compare : ('a -> 'b -> int) -> 'a t -> 'b t -> int
val empty : 'a t
val return : 'a -> 'a t
val init : int -> (int -> 'a) -> 'a t
val unfold : ('b -> ('a * 'b) option) -> 'b -> 'a t
val repeat : 'a -> 'a t
val forever : (unit -> 'a) -> 'a t
val cycle : 'a t -> 'a t
val iterate : ('a -> 'a) -> 'a -> 'a t
val map : ('a -> 'b) -> 'a t -> 'b t
val mapi : (int -> 'a -> 'b) -> 'a t -> 'b t
val filter : ('a -> bool) -> 'a t -> 'a t
val filter_map : ('a -> 'b option) -> 'a t -> 'b t
val scan : ('b -> 'a -> 'b) -> 'b -> 'a t -> 'b t
val take : int -> 'a t -> 'a t
val drop : int -> 'a t -> 'a t
val take_while : ('a -> bool) -> 'a t -> 'a t
val drop_while : ('a -> bool) -> 'a t -> 'a t
val group : ('a -> 'a -> bool) -> 'a t -> 'a t t
val memoize : 'a t -> 'a t
exception Forced_twice
val once : 'a t -> 'a t
val transpose : 'a t t -> 'a t t
val append : 'a t -> 'a t -> 'a t
val concat : 'a t t -> 'a t
val flat_map : ('a -> 'b t) -> 'a t -> 'b t
val concat_map : ('a -> 'b t) -> 'a t -> 'b t
val zip : 'a t -> 'b t -> ('a * 'b) t
val map2 : ('a -> 'b -> 'c) -> 'a t -> 'b t -> 'c t
val interleave : 'a t -> 'a t -> 'a t
val sorted_merge : ('a -> 'a -> int) -> 'a t -> 'a t -> 'a t
val product : 'a t -> 'b t -> ('a * 'b) t
val map_product : ('a -> 'b -> 'c) -> 'a t -> 'b t -> 'c t
val unzip : ('a * 'b) t -> 'a t * 'b t
val split : ('a * 'b) t -> 'a t * 'b t
val partition_map : ('a -> ('b, 'c) Stdlib.Either.t) -> 'a t -> 'b t * 'c t
val partition : ('a -> bool) -> 'a t -> 'a t * 'a t
val of_dispenser : (unit -> 'a option) -> 'a t
val to_dispenser : 'a t -> unit -> 'a option
val ints : int -> int t
val add_count : ?start:int -> 'a t -> (int * 'a) t

Add a counter starting at start (default 0) in front of each element of the sequence

val iota : ?start:int -> int -> int t

Generate an integer sequence up to len. Optionally may start at start instead of 0

val iota_step_up : ?start:int -> step:int -> endi:int -> int t

Generate an integer sequence up to endi by stepping step. Optionally may start at start instead of 0

val stop_at : ('a -> bool) -> 'a t -> 'a t

Make the sequence stop when the condition is met

val cons : 'a -> 'a t -> unit -> 'a node

Add a new element in front of the sequence. That element will appear first before the rest of the sequence.

Added to Stdlib in Ocaml 4.11

val find_map : ('a -> 'b option) -> 'a t -> 'b option

Applies the specified function to the elements of the sequence in order, and returns the first result of the form Some v, or None if no such result was returned.

See List.find_map

diff --git a/doc/html/read-dwarf/Utils/String/index.html b/doc/html/read-dwarf/Utils/String/index.html index 66e26772..dec0950d 100644 --- a/doc/html/read-dwarf/Utils/String/index.html +++ b/doc/html/read-dwarf/Utils/String/index.html @@ -1,2 +1,2 @@ -String (read-dwarf.Utils.String)

Module Utils.String

include Stdlib.String
val length : string -> int
val get : string -> int -> char
val set : bytes -> int -> char -> unit
val create : int -> bytes
val make : int -> char -> string
val init : int -> (int -> char) -> string
val copy : string -> string
val sub : string -> int -> int -> string
val fill : bytes -> int -> int -> char -> unit
val blit : string -> int -> bytes -> int -> int -> unit
val concat : string -> string list -> string
val iter : (char -> unit) -> string -> unit
val iteri : (int -> char -> unit) -> string -> unit
val map : (char -> char) -> string -> string
val mapi : (int -> char -> char) -> string -> string
val trim : string -> string
val escaped : string -> string
val index : string -> char -> int
val index_opt : string -> char -> int option
val rindex : string -> char -> int
val rindex_opt : string -> char -> int option
val index_from : string -> int -> char -> int
val index_from_opt : string -> int -> char -> int option
val rindex_from : string -> int -> char -> int
val rindex_from_opt : string -> int -> char -> int option
val contains : string -> char -> bool
val contains_from : string -> int -> char -> bool
val rcontains_from : string -> int -> char -> bool
val uppercase : string -> string
val lowercase : string -> string
val capitalize : string -> string
val uncapitalize : string -> string
val uppercase_ascii : string -> string
val lowercase_ascii : string -> string
val capitalize_ascii : string -> string
val uncapitalize_ascii : string -> string
type t = string
val compare : t -> t -> int
val equal : t -> t -> bool
val split_on_char : char -> string -> string list
val to_seq : t -> char Stdlib.Seq.t
val to_seqi : t -> (int * char) Stdlib.Seq.t
val of_seq : char Stdlib.Seq.t -> t
val unsafe_get : string -> int -> char
val unsafe_set : bytes -> int -> char -> unit
val unsafe_blit : string -> int -> bytes -> int -> int -> unit
val unsafe_fill : bytes -> int -> int -> char -> unit
val for_all : (char -> bool) -> string -> bool

Check if a predicate hold for all char in a string

val exists : (char -> bool) -> string -> bool

Check if a predicate hold for at least one char in a string

val to_list : string -> char list

Convert the list into a list of char. Probably a bad idea on large strings.

\ No newline at end of file +String (read-dwarf.Utils.String)

Module Utils.String

Extension of the String module of the standard library.

include module type of struct include Stdlib.String end
type t = string
val make : int -> char -> string
val init : int -> (int -> char) -> string
val empty : string
val length : string -> int
val get : string -> int -> char
val of_bytes : bytes -> string
val to_bytes : string -> bytes
val blit : string -> int -> bytes -> int -> int -> unit
val concat : string -> string list -> string
val cat : string -> string -> string
val equal : t -> t -> bool
val compare : t -> t -> int
val starts_with : prefix:string -> string -> bool
val ends_with : suffix:string -> string -> bool
val contains_from : string -> int -> char -> bool
val rcontains_from : string -> int -> char -> bool
val contains : string -> char -> bool
val sub : string -> int -> int -> string
val split_on_char : char -> string -> string list
val map : (char -> char) -> string -> string
val mapi : (int -> char -> char) -> string -> string
val fold_left : ('acc -> char -> 'acc) -> 'acc -> string -> 'acc
val fold_right : (char -> 'acc -> 'acc) -> string -> 'acc -> 'acc
val trim : string -> string
val escaped : string -> string
val uppercase_ascii : string -> string
val lowercase_ascii : string -> string
val capitalize_ascii : string -> string
val uncapitalize_ascii : string -> string
val iter : (char -> unit) -> string -> unit
val iteri : (int -> char -> unit) -> string -> unit
val index_from : string -> int -> char -> int
val index_from_opt : string -> int -> char -> int option
val rindex_from : string -> int -> char -> int
val rindex_from_opt : string -> int -> char -> int option
val index : string -> char -> int
val index_opt : string -> char -> int option
val rindex : string -> char -> int
val rindex_opt : string -> char -> int option
val to_seq : t -> char Stdlib.Seq.t
val to_seqi : t -> (int * char) Stdlib.Seq.t
val of_seq : char Stdlib.Seq.t -> t
val get_utf_8_uchar : t -> int -> Stdlib.Uchar.utf_decode
val is_valid_utf_8 : t -> bool
val get_utf_16be_uchar : t -> int -> Stdlib.Uchar.utf_decode
val is_valid_utf_16be : t -> bool
val get_utf_16le_uchar : t -> int -> Stdlib.Uchar.utf_decode
val is_valid_utf_16le : t -> bool
val get_uint8 : string -> int -> int
val get_int8 : string -> int -> int
val get_uint16_ne : string -> int -> int
val get_uint16_be : string -> int -> int
val get_uint16_le : string -> int -> int
val get_int16_ne : string -> int -> int
val get_int16_be : string -> int -> int
val get_int16_le : string -> int -> int
val get_int32_ne : string -> int -> int32
val hash : t -> int
val seeded_hash : int -> t -> int
val get_int32_be : string -> int -> int32
val get_int32_le : string -> int -> int32
val get_int64_ne : string -> int -> int64
val get_int64_be : string -> int -> int64
val get_int64_le : string -> int -> int64
val unsafe_get : string -> int -> char
val unsafe_blit : string -> int -> bytes -> int -> int -> unit
val for_all : (char -> bool) -> string -> bool

Check if a predicate hold for all char in a string

val exists : (char -> bool) -> string -> bool

Check if a predicate hold for at least one char in a string

val to_list : string -> char list

Convert the list into a list of char. Probably a bad idea on large strings.

diff --git a/doc/html/read-dwarf/Utils/Vec/index.html b/doc/html/read-dwarf/Utils/Vec/index.html index 285eb94f..ca9f9f32 100644 --- a/doc/html/read-dwarf/Utils/Vec/index.html +++ b/doc/html/read-dwarf/Utils/Vec/index.html @@ -1,2 +1,2 @@ -Vec (read-dwarf.Utils.Vec)

Module Utils.Vec

type 'a t

The vector type

val length : 'a t -> int
val empty : unit -> 'a t
val mem : 'a -> 'a t -> bool
val get : 'a t -> int -> 'a
val unsafe_get : 'a t -> int -> 'a
val set : 'a t -> int -> 'a -> unit
val unsafe_set : 'a t -> int -> 'a -> unit
val update : 'a t -> int -> ('a -> 'a) -> unit
val unsafe_update : 'a t -> int -> ('a -> 'a) -> unit
val copy : 'a t -> 'a t
val for_all : ('a -> bool) -> 'a t -> bool
val exists : ('a -> bool) -> 'a t -> bool
val map : ('a -> 'b) -> 'a t -> 'b t
val mapi : (int -> 'a -> 'b) -> 'a t -> 'b t
val iter : ('a -> unit) -> 'a t -> unit
val iteri : (int -> 'a -> unit) -> 'a t -> unit
val iter_until : limit:int -> ('a -> unit) -> 'a t -> unit

Iterates until the limit. If the vector is shorter, this is a plain iter

val iteri_until : limit:int -> (int -> 'a -> unit) -> 'a t -> unit

Same as iter_until but with the index

val iteri_rev : (int -> 'a -> unit) -> 'a t -> unit

Same as iteri but starting at the end

val to_list : 'a t -> 'a list
val to_listi : 'a t -> (int * 'a) list
val fold_right : ('a -> 'b -> 'b) -> 'a t -> 'b -> 'b
val foldi_right : (int -> 'a -> 'b -> 'b) -> 'a t -> 'b -> 'b
val fold_left : ('a -> 'b -> 'a) -> 'a -> 'b t -> 'a
val foldi_left : (int -> 'b -> 'a -> 'b) -> 'a t -> 'b -> 'b
val add_one : 'a t -> 'a -> unit

Add a new element at the end of the vector

val remove_one : 'a t -> unit

Remove the last element of the vector

val remove_n : 'a t -> int -> unit

Remove the last n elements of the vector

val to_array : 'a t -> 'a array
val of_array : 'a array -> 'a t
val ensure : 'a t -> int -> 'a -> unit

Ensure that the vector has size at least the int by resizing if needed The value provided will be use to set the newly created values.

val keep : 'a t -> int -> unit

Keep only the specified number of element. If the vector was smaller, do nothing.

val resize : 'a t -> int -> 'a -> unit

Resize the vector to the specified size. The value provided will be use to set the newly created values if relevant

val map2 : ('a -> 'b -> 'c) -> 'a t -> 'b t -> 'c t
val map_mut : ('a -> 'a) -> 'a t -> unit

Map the function on the vector mutably by replacing each cell by the result of the function

val map_mut_until : limit:int -> ('a -> 'a) -> 'a t -> unit

Same as map_mut but stop at limit. If the vector is shorter than limit this is a plain map_mut

val fill_all : 'a t -> 'a -> unit

Write the value in all the cells of the vector

val insert : 'a t -> int -> 'a -> unit

Insert an element at the specified position that may be one past the end

val to_seq_sub : 'a t -> pos:int -> len:int -> 'a Utils.Seq.t
val to_seq : 'a t -> 'a Utils.Seq.t
val to_seqi_sub : 'a t -> pos:int -> len:int -> (int * 'a) Utils.Seq.t
val to_seqi : 'a t -> (int * 'a) Utils.Seq.t
val pp : ('a -> Utils.Pp.document) -> 'a t -> Utils.Pp.document

Vector pretty printer

val ppi : ('a -> Utils.Pp.document) -> 'a t -> Utils.Pp.document

Vector pretty printer that also prints the index of each element using Pp.mapping

\ No newline at end of file +Vec (read-dwarf.Utils.Vec)

Module Utils.Vec

This module provide a resizable array. It is a rename of Res.array with added features

type 'a t

The vector type

val length : 'a t -> int
val empty : unit -> 'a t
val mem : 'a -> 'a t -> bool
val get : 'a t -> int -> 'a
val unsafe_get : 'a t -> int -> 'a
val set : 'a t -> int -> 'a -> unit
val unsafe_set : 'a t -> int -> 'a -> unit
val update : 'a t -> int -> ('a -> 'a) -> unit
val unsafe_update : 'a t -> int -> ('a -> 'a) -> unit
val copy : 'a t -> 'a t
val for_all : ('a -> bool) -> 'a t -> bool
val exists : ('a -> bool) -> 'a t -> bool
val map : ('a -> 'b) -> 'a t -> 'b t
val mapi : (int -> 'a -> 'b) -> 'a t -> 'b t
val iter : ('a -> unit) -> 'a t -> unit
val iteri : (int -> 'a -> unit) -> 'a t -> unit
val iter_until : limit:int -> ('a -> unit) -> 'a t -> unit

Iterates until the limit. If the vector is shorter, this is a plain iter

val iteri_until : limit:int -> (int -> 'a -> unit) -> 'a t -> unit

Same as iter_until but with the index

val iteri_rev : (int -> 'a -> unit) -> 'a t -> unit

Same as iteri but starting at the end

val to_list : 'a t -> 'a list
val to_listi : 'a t -> (int * 'a) list
val fold_right : ('a -> 'b -> 'b) -> 'a t -> 'b -> 'b
val foldi_right : (int -> 'a -> 'b -> 'b) -> 'a t -> 'b -> 'b
val fold_left : ('a -> 'b -> 'a) -> 'a -> 'b t -> 'a
val foldi_left : (int -> 'b -> 'a -> 'b) -> 'a t -> 'b -> 'b
val add_one : 'a t -> 'a -> unit

Add a new element at the end of the vector

val remove_one : 'a t -> unit

Remove the last element of the vector

val remove_n : 'a t -> int -> unit

Remove the last n elements of the vector

val to_array : 'a t -> 'a array
val of_array : 'a array -> 'a t
val ensure : 'a t -> int -> 'a -> unit

Ensure that the vector has size at least the int by resizing if needed The value provided will be use to set the newly created values.

val keep : 'a t -> int -> unit

Keep only the specified number of element. If the vector was smaller, do nothing.

val resize : 'a t -> int -> 'a -> unit

Resize the vector to the specified size. The value provided will be use to set the newly created values if relevant

val map2 : ('a -> 'b -> 'c) -> 'a t -> 'b t -> 'c t
val map_mut : ('a -> 'a) -> 'a t -> unit

Map the function on the vector mutably by replacing each cell by the result of the function

val map_mut_until : limit:int -> ('a -> 'a) -> 'a t -> unit

Same as map_mut but stop at limit. If the vector is shorter than limit this is a plain map_mut

val fill_all : 'a t -> 'a -> unit

Write the value in all the cells of the vector

val insert : 'a t -> int -> 'a -> unit

Insert an element at the specified position that may be one past the end

val to_seq_sub : 'a t -> pos:int -> len:int -> 'a Seq.t
val to_seq : 'a t -> 'a Seq.t
val to_seqi_sub : 'a t -> pos:int -> len:int -> (int * 'a) Seq.t
val to_seqi : 'a t -> (int * 'a) Seq.t
val pp : ('a -> Pp.document) -> 'a t -> Pp.document

Vector pretty printer

val ppi : ('a -> Pp.document) -> 'a t -> Pp.document

Vector pretty printer that also prints the index of each element using Pp.mapping

diff --git a/doc/html/read-dwarf/Utils/WeakMap/index.html b/doc/html/read-dwarf/Utils/WeakMap/index.html index 8316f6c6..8a7c70a5 100644 --- a/doc/html/read-dwarf/Utils/WeakMap/index.html +++ b/doc/html/read-dwarf/Utils/WeakMap/index.html @@ -1,2 +1,2 @@ -WeakMap (read-dwarf.Utils.WeakMap)

Module Utils.WeakMap

type ('a, 'b) t

The type of the weak map

val create : int -> ('a'b) t

The initial map

exception Exists
val add : ('a'b) t -> 'a -> 'b -> unit

Add a mapping to the map. raise Exists if a is already mapped

val get : ('a'b) t -> 'a -> 'b

Retrieves a value from the table. Throws Hashtbl.Not_found if the binding do not exists

\ No newline at end of file +WeakMap (read-dwarf.Utils.WeakMap)

Module Utils.WeakMap

This module provide a simple way of associated key to value without keeping them alive The key is owned by the map but the value is weakly pointed to. When the value is deleted by the GC, the binding disappears.

The value type must be heap allocated (basically not an integer or another weird type)

type ('a, 'b) t

The type of the weak map

val create : int -> ('a, 'b) t

The initial map

exception Exists
val add : ('a, 'b) t -> 'a -> 'b -> unit

Add a mapping to the map. raise Exists if a is already mapped

val get : ('a, 'b) t -> 'a -> 'b

Retrieves a value from the table. Throws Hashtbl.Not_found if the binding do not exists

diff --git a/doc/html/read-dwarf/Utils/WeakPtr/index.html b/doc/html/read-dwarf/Utils/WeakPtr/index.html index 5f75851f..68d89a48 100644 --- a/doc/html/read-dwarf/Utils/WeakPtr/index.html +++ b/doc/html/read-dwarf/Utils/WeakPtr/index.html @@ -1,2 +1,2 @@ -WeakPtr (read-dwarf.Utils.WeakPtr)

Module Utils.WeakPtr

type 'a t

The type of a weak pointer

val empty : unit -> 'a t

Make a new empty pointer

val make : 'a -> 'a t

Make a new pointer and set it with the given value

val seto : 'a t -> 'a option -> unit

Set or clear the value inside the pointer depending of the option

val set : 'a t -> 'a -> unit

Set the value inside the pointer

val reset : 'a t -> unit

Clear the value inside the pointer

val geto : 'a t -> 'a option

Retrieves the value and return None if the pointer is now empty

exception Deleted

Raised when accessing and empty ptr

val get : 'a t -> 'a

Retrieves the value. Will raise Deleted if the value was deleted by the GC

val check : 'a t -> bool

Check if the pointer is filled

\ No newline at end of file +WeakPtr (read-dwarf.Utils.WeakPtr)

Module Utils.WeakPtr

This module provide a simple weak pointer that can be rendered invalid by the GC

type 'a t

The type of a weak pointer

val empty : unit -> 'a t

Make a new empty pointer

val make : 'a -> 'a t

Make a new pointer and set it with the given value

val seto : 'a t -> 'a option -> unit

Set or clear the value inside the pointer depending of the option

val set : 'a t -> 'a -> unit

Set the value inside the pointer

val reset : 'a t -> unit

Clear the value inside the pointer

val geto : 'a t -> 'a option

Retrieves the value and return None if the pointer is now empty

exception Deleted

Raised when accessing and empty ptr

val get : 'a t -> 'a

Retrieves the value. Will raise Deleted if the value was deleted by the GC

val check : 'a t -> bool

Check if the pointer is filled

diff --git a/doc/html/read-dwarf/Utils/index.html b/doc/html/read-dwarf/Utils/index.html index 31829396..ad38aeff 100644 --- a/doc/html/read-dwarf/Utils/index.html +++ b/doc/html/read-dwarf/Utils/index.html @@ -1,2 +1,2 @@ -Utils (read-dwarf.Utils)

Module Utils

module Array : sig ... end
module BitVec : sig ... end
module Bits : sig ... end
module BytesSeq : sig ... end
module Cache : sig ... end
module Cmd : sig ... end
module CmdlinerHelper : sig ... end
module Counter : sig ... end
module Files : sig ... end
module FullVec : sig ... end
module Fun : sig ... end
module HashVector : sig ... end
module IdMap : sig ... end
module IntBits : sig ... end
module List : sig ... end
module Logs : sig ... end
module Option : sig ... end
module Pair : sig ... end
module Pp : sig ... end
module Protect : sig ... end
module Raise : sig ... end
module RngMap : sig ... end
module Seq : sig ... end
module String : sig ... end
module Vec : sig ... end
module WeakMap : sig ... end
module WeakPtr : sig ... end
\ No newline at end of file +Utils (read-dwarf.Utils)

Module Utils

module Array : sig ... end

This module is for extending the Array module of the standard library

module BitVec : sig ... end

This module provides an interface for a bit vector of dynamic size.

module Bits : sig ... end

Like bytes, but for bit level manipulation. The underlying type is still bytes and thus the size has to be a multiple of 8.

module BytesSeq : sig ... end

This module represent a byte sub view on a bytes object. Contrary to Bytes it is a non-owning immutable view. It do not prevent the original bytes from being modified, and the changes will be propagated in the view. It is additional sugar on top of Linksem's Byte_sequence_wrapper

module Cache : sig ... end

This module implement a caching system i.e a persistant structure stored on the disk.

module Cmd : sig ... end

This module provides high-level interaction with external processes.

module CmdlinerHelper : sig ... end

This module provide some Cmdliner helper functions.

module Counter : sig ... end

This module provide a small counter object which is just a int reference on which get can be called to get an identifier and increment the reference

module Files : sig ... end

This module provides simplified file management and some channel interaction function.

module FullVec : sig ... end

A full vector is a vector in which all non-negative integer are bound.

module Fun : sig ... end

More functional combinator. This module extends the base OCaml API of Fun.

module HashVector : sig ... end

An hash vector allow a vector to behave as hash map indexed by small integers. It is hash map with the identity hash function.

module IdMap : sig ... end

An IdMap is a map that associate an id to each key (and thus to each value).

module IntBits : sig ... end

Manipulate an int as bitfield of size 31 or 63.

module List : sig ... end

Extension of the List module of the standard library.

module Logs : sig ... end

This module provide a logging system for each module.

module Option : sig ... end

This module extends the base OCaml API of Option.

module Pair : sig ... end

This module contain random utility functions dealing with pairs

module Pp : sig ... end

This module provide all pretty printing functionality. It's main goal is not directly handle the output but to handle how to layout complex data structure in a text format.

module Protect : sig ... end

This module provide try-with-finally kind of exception handling.

module Raise : sig ... end

This module provide convenience facilities to raise exception or other exception management

module RngMap : sig ... end

A module giving a map indexing data with address ranges and providing access to quick access to data corresponding to any value in-between.

module Seq : sig ... end

This module is for extending the Seq module of the standard library

module String : sig ... end

Extension of the String module of the standard library.

module Sym : sig ... end
module Vec : sig ... end

This module provide a resizable array. It is a rename of Res.array with added features

module WeakMap : sig ... end

This module provide a simple way of associated key to value without keeping them alive The key is owned by the map but the value is weakly pointed to. When the value is deleted by the GC, the binding disappears.

module WeakPtr : sig ... end

This module provide a simple weak pointer that can be rendered invalid by the GC

diff --git a/doc/html/read-dwarf/Z3/CheckContext/index.html b/doc/html/read-dwarf/Z3/CheckContext/index.html index 83e0ead6..b990d59b 100644 --- a/doc/html/read-dwarf/Z3/CheckContext/index.html +++ b/doc/html/read-dwarf/Z3/CheckContext/index.html @@ -1,2 +1,2 @@ -CheckContext (read-dwarf.Z3.CheckContext)

Module Z3.CheckContext

val counter : Utils.Counter.t
val openc : unit -> unit
val num : unit -> int
val closec : unit -> unit
\ No newline at end of file +CheckContext (read-dwarf.Z3.CheckContext)

Module Z3.CheckContext

val counter : Utils.Counter.t
val openc : unit -> unit
val num : unit -> int
val closec : unit -> unit
diff --git a/doc/html/read-dwarf/Z3/ContextCounter/argument-1-S/index.html b/doc/html/read-dwarf/Z3/ContextCounter/argument-1-S/index.html index 293b4ab8..565b842c 100644 --- a/doc/html/read-dwarf/Z3/ContextCounter/argument-1-S/index.html +++ b/doc/html/read-dwarf/Z3/ContextCounter/argument-1-S/index.html @@ -1,2 +1,2 @@ -1-S (read-dwarf.Z3.ContextCounter.1-S)

Parameter ContextCounter.1-S

val str : string
\ No newline at end of file +S (read-dwarf.Z3.ContextCounter.S)

Parameter ContextCounter.S

val str : string
diff --git a/doc/html/read-dwarf/Z3/ContextCounter/index.html b/doc/html/read-dwarf/Z3/ContextCounter/index.html index 36935686..09086b01 100644 --- a/doc/html/read-dwarf/Z3/ContextCounter/index.html +++ b/doc/html/read-dwarf/Z3/ContextCounter/index.html @@ -1,2 +1,2 @@ -ContextCounter (read-dwarf.Z3.ContextCounter)

Module Z3.ContextCounter

Module for handling a context numbering scheme automatically.

To use it do: module Whatever = ContextCounter(struct let str= "name here" end) Then you can use it with Whatever.openc () and Whatever.closec (). You can get the current instance number with Whatever.num() at any time.

Parameters

Signature

val counter : Utils.Counter.t
val openc : unit -> unit

Open a new context of that ContextCounter. Ensure the server is started

val num : unit -> int

Get the current context number

val closec : unit -> unit

Close a context opened with openc. Assert that the current context was indeed opened by this module

\ No newline at end of file +ContextCounter (read-dwarf.Z3.ContextCounter)

Module Z3.ContextCounter

Module for handling a context numbering scheme automatically.

To use it do: module Whatever = ContextCounter(struct let str= "name here" end) Then you can use it with Whatever.openc () and Whatever.closec (). You can get the current instance number with Whatever.num() at any time.

Parameters

Signature

val counter : Utils.Counter.t
val openc : unit -> unit

Open a new context of that ContextCounter. Ensure the server is started

val num : unit -> int

Get the current context number

val closec : unit -> unit

Close a context opened with openc. Assert that the current context was indeed opened by this module

diff --git a/doc/html/read-dwarf/Z3/Make/argument-1-Var/index.html b/doc/html/read-dwarf/Z3/Make/argument-1-Var/index.html index 99665999..27eb425d 100644 --- a/doc/html/read-dwarf/Z3/Make/argument-1-Var/index.html +++ b/doc/html/read-dwarf/Z3/Make/argument-1-Var/index.html @@ -1,2 +1,2 @@ -1-Var (read-dwarf.Z3.Make.1-Var)

Parameter Make.1-Var

include Exp.Var
type t

The type of variables

val equal : t -> t -> bool

Equality predicate that will be passed to expressions

val pp : t -> Utils.Pp.document

Pretty printer to be used, both for memory pretty printing and for sending memory to Z3

val ty : t -> Exp.ty

Get the type of the variable

val hash : t -> int

Hashing function to store variable in Hashtbls. Must be compatible with equal

val of_string : string -> t

Parser from a string. Must be the inverse of pp

\ No newline at end of file +Var (read-dwarf.Z3.Make.Var)

Parameter Make.Var

include Exp.Var
type t

The type of variables

val equal : t -> t -> bool

Equality predicate that will be passed to expressions

val pp : t -> Utils.Pp.document

Pretty printer to be used, both for memory pretty printing and for sending memory to Z3

val ty : t -> Exp.ty

Get the type of the variable

val hash : t -> int

Hashing function to store variable in Hashtbls. Must be compatible with equal

val of_string : string -> t

Parser from a string. Must be the inverse of pp

diff --git a/doc/html/read-dwarf/Z3/Make/index.html b/doc/html/read-dwarf/Z3/Make/index.html index 88920fd3..c4df3e7f 100644 --- a/doc/html/read-dwarf/Z3/Make/index.html +++ b/doc/html/read-dwarf/Z3/Make/index.html @@ -1,2 +1,2 @@ -Make (read-dwarf.Z3.Make)

Module Z3.Make

Parameters

Signature

type var = Var.t

Variable declarations

The goal of those operation is to declare variables with their types to the SMT solver. The Htbl allow to declare every variable only once.

type exp = (varAst.no) Exp.Typed.t

The type of expression on which SMT operation are made

module Htbl : Stdlib.Hashtbl.S with type Htbl.key = var

Hash tables over variables

val declare_var_always : server -> var -> unit

Declare the variable regardless of whether it has already been declared

val declare_var : server -> declared:unit Htbl.t -> var -> unit

Declare the variable if it's not in declared. In that case, it also add it to declared

val declare_vars : server -> declared:unit Htbl.t -> exp -> unit

Declare all not yet declared the variable in the expression, then add them to declared

Simplification

val simplify : server -> exp -> exp

Simplify the expression in the current context. All the variable must already have been declared.

val simplify_decl : server -> declared:unit Htbl.t -> exp -> exp

Literally just declare_vars then simplify

SMT checking

val send_assert : server -> exp -> unit

Assert that expression in the current context

val send_assert_decl : server -> declared:unit Htbl.t -> exp -> unit

Literally just declare_vars then send_assert

val check : server -> exp -> bool option

Check if this expression is always true in the current context. None is returned when the SMT solver didn't know

val check_sat : server -> exp -> bool option

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

val check_both : server -> exp -> bool option

Check if this expression is always true of always false. None is returned if neither can be proven.

This results in two calls to the SMT solver. one with check and one with check_sat

Context less operation

Those operations do not require a context to operate. They require not setup and tear-down. They are standalone and fully automated.

On the other hand doing multiple one of those in sequence if much less efficient than using properly the functions of previous section.

val simplify_full : exp -> exp

Do a standalone simplification in it's own context. Do not need anything to be already declared or any context to be opened.

val check_full : ?⁠hyps:exp list -> exp -> bool option

Do a standalone check of whether the expression is implied by the list of hypothesis.

val check_sat_full : exp list -> bool option

Do a standalone check of whether the set of assertion is sat

\ No newline at end of file +Make (read-dwarf.Z3.Make)

Module Z3.Make

Parameters

module Var : Var

Signature

type var = Var.t

Variable declarations

The goal of those operation is to declare variables with their types to the SMT solver. The Htbl allow to declare every variable only once.

type exp = (var, Ast.no) Exp.Typed.t

The type of expression on which SMT operation are made

module Htbl : Stdlib.Hashtbl.S with type key = var

Hash tables over variables

val declare_var_always : server -> var -> unit

Declare the variable regardless of whether it has already been declared

val declare_var : server -> declared:unit Htbl.t -> var -> unit

Declare the variable if it's not in declared. In that case, it also add it to declared

val declare_vars : server -> declared:unit Htbl.t -> exp -> unit

Declare all not yet declared the variable in the expression, then add them to declared

Simplification

val simplify : server -> exp -> exp

Simplify the expression in the current context. All the variable must already have been declared.

val simplify_decl : server -> declared:unit Htbl.t -> exp -> exp

Literally just declare_vars then simplify

SMT checking

val send_assert : server -> exp -> unit

Assert that expression in the current context

val send_assert_decl : server -> declared:unit Htbl.t -> exp -> unit

Literally just declare_vars then send_assert

val check : server -> exp -> bool option

Check if this expression is always true in the current context. None is returned when the SMT solver didn't know

val check_sat : server -> exp -> bool option

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

val check_both : server -> exp -> bool option

Check if this expression is always true of always false. None is returned if neither can be proven.

This results in two calls to the SMT solver. one with check and one with check_sat

val simplify_subterms : server -> exp -> exp
val simplify_subterms_decl : server -> declared:unit Htbl.t -> exp -> exp

Context less operation

Those operations do not require a context to operate. They require not setup and tear-down. They are standalone and fully automated.

On the other hand doing multiple one of those in sequence if much less efficient than using properly the functions of previous section.

val simplify_full : exp -> exp

Do a standalone simplification in it's own context. Do not need anything to be already declared or any context to be opened.

val check_full : ?hyps:exp list -> exp -> bool option

Do a standalone check of whether the expression is implied by the list of hypothesis.

val check_sat_full : exp list -> bool option

Do a standalone check of whether the set of assertion is sat

val simplify_subterms_full : ?hyps:exp list -> exp -> exp
diff --git a/doc/html/read-dwarf/Z3/SimpContext/index.html b/doc/html/read-dwarf/Z3/SimpContext/index.html index 4a31f6b4..eb59a8e5 100644 --- a/doc/html/read-dwarf/Z3/SimpContext/index.html +++ b/doc/html/read-dwarf/Z3/SimpContext/index.html @@ -1,2 +1,2 @@ -SimpContext (read-dwarf.Z3.SimpContext)

Module Z3.SimpContext

val counter : Utils.Counter.t
val openc : unit -> unit
val num : unit -> int
val closec : unit -> unit
\ No newline at end of file +SimpContext (read-dwarf.Z3.SimpContext)

Module Z3.SimpContext

val counter : Utils.Counter.t
val openc : unit -> unit
val num : unit -> int
val closec : unit -> unit
diff --git a/doc/html/read-dwarf/Z3/index.html b/doc/html/read-dwarf/Z3/index.html index 79df9fe7..6cc568d3 100644 --- a/doc/html/read-dwarf/Z3/index.html +++ b/doc/html/read-dwarf/Z3/index.html @@ -1,2 +1,12 @@ -Z3 (read-dwarf.Z3)

Module Z3

This module handles a Z3 server

For high level usage, call start or ensure_started then instantiate the Make functor and use operation in the section about Context less operation:

For a more medium level usage, you may want to manage your context manually before making requests. The best way of doing that is by using a ContextCounter.

Once you are in the correct context, you can use your instantiated version of Make to do operations such as: S.declare_var or S.send_assert to build your context, and then S.simplify, S.check of S.check_sat.

For low-level details (All function in the first two section of this module should probably be reserved to those who understand the implementation)

The module keeps Z3 as a child process and communicates through pipes using Cmd.IOServer.

start sends the introduction in intro.smt2 to Z3 so that it is available in any context. SMT answer are parsed from the pipe with Files.input_sexp. If the wrong number of answer is expected, the system will just deadlock.

Raw server management

val z3_trace : bool

A boolean enabling context tracing (For better error messages)

type context_elem = {
name : string;
mutable num : int;
}

An element of the context stack. It has a name and a declaration number.

The declaration number is incremented at each declaration in the context.

type context = context_elem list

The type of a SMT context stack

val start_context : context

The starting context

val context_elem_to_string : context_elem -> string

Give a string representation of a context_elem

type server = {
ioserver : Utils.Cmd.IOServer.t;
config : Config.File.Z3.t;
mutable context : context;
}

The type of a Z3 server

val server : server option Stdlib.ref

The global Z3 server

val get_server : unit -> server

Assume the server is started and returns it.

val raw_start : unit -> unit

Start Z3 without any checks and without sending intro or pushing

val raw_stop : unit -> unit

Stop Z3 without asking politely

val get_context_string : server -> string

Give a string representation of the current context for error reporting

val incr_context : server -> unit

Increment the declaration number of the top context_elem.

Request and answer management

This section handle raw and less raw direct interaction with the Z3 server.

The send_* function are for sending information to the server with a server handle. This avoided checking if the server is open at each declaration.

The read_* functions are the same the other way around.

The request and command are high level versions

val send_string : server -> string -> unit

Send a string to the server and increment the declaration number in the context

val send_smt : server -> ?⁠ppv:('a -> Utils.Pp.document) -> ('b'a, string, AstGen.Def.Size.t) AstGen.Ott.smt -> unit

Send a smt statement to the server and increment the declaration number in the context

val read_string : server -> string

Read a string from the server (A Z3 answer is always a valid sexp)

val read_smt_ans : server -> Ast.rsmt_ans

Read a smt_ans from the server

val request : ?⁠ppv:('a -> Utils.Pp.document) -> ('b'a, string, AstGen.Def.Size.t) AstGen.Ott.smt -> Ast.rsmt_ans

Make a request to the server and expect an answer. Will hang if there is no answer

val command : ?⁠ppv:('a -> Utils.Pp.document) -> ('b'a, string, AstGen.Def.Size.t) AstGen.Ott.smt -> unit

Send a command or a declaration to the server

val expect_version : server -> Ast.rsmt_ans -> string

Expect a version answer and fails if it is not the case

val expect_exp : server -> Ast.rsmt_ans -> Ast.rexp

Expect an expression answer and fails if it is not the case

val get_version : unit -> string

Get the Z3 version string

val is_context_sat : server -> bool option

Check if the current context is sat

Full startup and shutdown

val stop : unit -> unit

Stop the Z3 server properly

val ensure_stopped : unit -> unit

Call stop if the server wasn't stopped

val start : unit -> unit

Start the Z3 Server and setup the intro

val ensure_started : unit -> unit

Call start if the server wasn't started

val ensure_started_get : unit -> server

Call start if the server wasn't started, then return the server

val reset : unit -> unit

Reset the Z3 server, forgetting everything. Useful for resetting in a test failure context, but probably shouldn't be used in normal operations

Context management

Z3 contexts are managed in a stack way.

There are implemented using (push) and (pop) on the Z3 side.

open_context can open a named context. However if it is expected to open a given context name multiple times, using ContextCounter is advised.

val open_context : server -> string -> unit

Open a new context with a name.

val close_context : server -> unit

Closes current context of the server

module ContextCounter : functor (S : Utils.Logs.String) -> sig ... end

Module for handling a context numbering scheme automatically.

High level interaction

This section provide functor that can be instantiated to get easy to use SMT functionality

module type Var = sig ... end

The functors in this section require a bit more support from variable than plain Exp.Var

module type S = sig ... end
module SimpContext : sig ... end
module CheckContext : sig ... end
module Make : functor (Var : Var) -> S with type var = Var.t
\ No newline at end of file +Z3 (read-dwarf.Z3)

Module Z3

This module handles a Z3 server

For high level usage, call start or ensure_started then instantiate the Make functor and use operation in the section about Context less operation:

For a more medium level usage, you may want to manage your context manually before making requests. The best way of doing that is by using a ContextCounter.

Once you are in the correct context, you can use your instantiated version of Make to do operations such as: S.declare_var or S.send_assert to build your context, and then S.simplify, S.check of S.check_sat.

For low-level details (All function in the first two section of this module should probably be reserved to those who understand the implementation)

The module keeps Z3 as a child process and communicates through pipes using Utils.Cmd.IOServer.

start sends the introduction in intro.smt2 to Z3 so that it is available in any context. SMT answer are parsed from the pipe with Utils.Files.input_sexp. If the wrong number of answer is expected, the system will just deadlock.

Raw server management

val z3_trace : bool

A boolean enabling context tracing (For better error messages)

type context_elem = {
  1. name : string;
  2. mutable num : int;
}

An element of the context stack. It has a name and a declaration number.

The declaration number is incremented at each declaration in the context.

type context = context_elem list

The type of a SMT context stack

val start_context : context

The starting context

val context_elem_to_string : context_elem -> string

Give a string representation of a context_elem

type server = {
  1. ioserver : Utils.Cmd.IOServer.t;
  2. config : Config.File.Z3.t;
  3. mutable context : context;
}

The type of a Z3 server

val server : server option Stdlib.ref

The global Z3 server

val get_server : unit -> server

Assume the server is started and returns it.

val raw_start : unit -> unit

Start Z3 without any checks and without sending intro or pushing

val raw_stop : unit -> unit

Stop Z3 without asking politely

val get_context_string : server -> string

Give a string representation of the current context for error reporting

val incr_context : server -> unit

Increment the declaration number of the top context_elem.

Request and answer management

This section handle raw and less raw direct interaction with the Z3 server.

The send_* function are for sending information to the server with a server handle. This avoided checking if the server is open at each declaration.

The read_* functions are the same the other way around.

The request and command are high level versions

val send_string : server -> string -> unit

Send a string to the server and increment the declaration number in the context

val send_smt : + server -> + ?ppv:('a -> Utils.Pp.document) -> + ('b, 'a, string, AstGen.Def.Size.t) AstGen.Ott.smt -> + unit

Send a smt statement to the server and increment the declaration number in the context

val read_string : server -> string

Read a string from the server (A Z3 answer is always a valid sexp)

val read_smt_ans : server -> Ast.rsmt_ans

Read a smt_ans from the server

val request : + ?ppv:('a -> Utils.Pp.document) -> + ('b, 'a, string, AstGen.Def.Size.t) AstGen.Ott.smt -> + Ast.rsmt_ans

Make a request to the server and expect an answer. Will hang if there is no answer

val command : + ?ppv:('a -> Utils.Pp.document) -> + ('b, 'a, string, AstGen.Def.Size.t) AstGen.Ott.smt -> + unit

Send a command or a declaration to the server

val expect_version : server -> Ast.rsmt_ans -> string

Expect a version answer and fails if it is not the case

val expect_exp : server -> Ast.rsmt_ans -> Ast.rexp

Expect an expression answer and fails if it is not the case

val get_version : unit -> string

Get the Z3 version string

val is_context_sat : server -> bool option

Check if the current context is sat

Full startup and shutdown

val stop : unit -> unit

Stop the Z3 server properly

val ensure_stopped : unit -> unit

Call stop if the server wasn't stopped

val start : unit -> unit

Start the Z3 Server and setup the intro

val ensure_started : unit -> unit

Call start if the server wasn't started

val ensure_started_get : unit -> server

Call start if the server wasn't started, then return the server

val reset : unit -> unit

Reset the Z3 server, forgetting everything. Useful for resetting in a test failure context, but probably shouldn't be used in normal operations

Context management

Z3 contexts are managed in a stack way.

There are implemented using (push) and (pop) on the Z3 side.

open_context can open a named context. However if it is expected to open a given context name multiple times, using ContextCounter is advised.

val open_context : server -> string -> unit

Open a new context with a name.

val close_context : server -> unit

Closes current context of the server

module ContextCounter (S : Utils.Logs.String) : sig ... end

Module for handling a context numbering scheme automatically.

High level interaction

This section provide functor that can be instantiated to get easy to use SMT functionality

module type Var = sig ... end

The functors in this section require a bit more support from variable than plain Exp.Var

module type S = sig ... end
module SimpContext : sig ... end
module CheckContext : sig ... end
module Make (Var : Var) : S with type var = Var.t
module Test : sig ... end
diff --git a/doc/html/read-dwarf/Z3/module-type-S/index.html b/doc/html/read-dwarf/Z3/module-type-S/index.html index fc0014a9..eeb82f69 100644 --- a/doc/html/read-dwarf/Z3/module-type-S/index.html +++ b/doc/html/read-dwarf/Z3/module-type-S/index.html @@ -1,2 +1,2 @@ -S (read-dwarf.Z3.S)

Module type Z3.S

type var

Variable declarations

The goal of those operation is to declare variables with their types to the SMT solver. The Htbl allow to declare every variable only once.

type exp = (varAst.no) Exp.Typed.t

The type of expression on which SMT operation are made

module Htbl : Stdlib.Hashtbl.S with type Htbl.key = var

Hash tables over variables

val declare_var_always : server -> var -> unit

Declare the variable regardless of whether it has already been declared

val declare_var : server -> declared:unit Htbl.t -> var -> unit

Declare the variable if it's not in declared. In that case, it also add it to declared

val declare_vars : server -> declared:unit Htbl.t -> exp -> unit

Declare all not yet declared the variable in the expression, then add them to declared

Simplification

val simplify : server -> exp -> exp

Simplify the expression in the current context. All the variable must already have been declared.

val simplify_decl : server -> declared:unit Htbl.t -> exp -> exp

Literally just declare_vars then simplify

SMT checking

val send_assert : server -> exp -> unit

Assert that expression in the current context

val send_assert_decl : server -> declared:unit Htbl.t -> exp -> unit

Literally just declare_vars then send_assert

val check : server -> exp -> bool option

Check if this expression is always true in the current context. None is returned when the SMT solver didn't know

val check_sat : server -> exp -> bool option

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

val check_both : server -> exp -> bool option

Check if this expression is always true of always false. None is returned if neither can be proven.

This results in two calls to the SMT solver. one with check and one with check_sat

Context less operation

Those operations do not require a context to operate. They require not setup and tear-down. They are standalone and fully automated.

On the other hand doing multiple one of those in sequence if much less efficient than using properly the functions of previous section.

val simplify_full : exp -> exp

Do a standalone simplification in it's own context. Do not need anything to be already declared or any context to be opened.

val check_full : ?⁠hyps:exp list -> exp -> bool option

Do a standalone check of whether the expression is implied by the list of hypothesis.

val check_sat_full : exp list -> bool option

Do a standalone check of whether the set of assertion is sat

\ No newline at end of file +S (read-dwarf.Z3.S)

Module type Z3.S

type var

Variable declarations

The goal of those operation is to declare variables with their types to the SMT solver. The Htbl allow to declare every variable only once.

type exp = (var, Ast.no) Exp.Typed.t

The type of expression on which SMT operation are made

module Htbl : Stdlib.Hashtbl.S with type key = var

Hash tables over variables

val declare_var_always : server -> var -> unit

Declare the variable regardless of whether it has already been declared

val declare_var : server -> declared:unit Htbl.t -> var -> unit

Declare the variable if it's not in declared. In that case, it also add it to declared

val declare_vars : server -> declared:unit Htbl.t -> exp -> unit

Declare all not yet declared the variable in the expression, then add them to declared

Simplification

val simplify : server -> exp -> exp

Simplify the expression in the current context. All the variable must already have been declared.

val simplify_decl : server -> declared:unit Htbl.t -> exp -> exp

Literally just declare_vars then simplify

SMT checking

val send_assert : server -> exp -> unit

Assert that expression in the current context

val send_assert_decl : server -> declared:unit Htbl.t -> exp -> unit

Literally just declare_vars then send_assert

val check : server -> exp -> bool option

Check if this expression is always true in the current context. None is returned when the SMT solver didn't know

val check_sat : server -> exp -> bool option

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

val check_both : server -> exp -> bool option

Check if this expression is always true of always false. None is returned if neither can be proven.

This results in two calls to the SMT solver. one with check and one with check_sat

val simplify_subterms : server -> exp -> exp
val simplify_subterms_decl : server -> declared:unit Htbl.t -> exp -> exp

Context less operation

Those operations do not require a context to operate. They require not setup and tear-down. They are standalone and fully automated.

On the other hand doing multiple one of those in sequence if much less efficient than using properly the functions of previous section.

val simplify_full : exp -> exp

Do a standalone simplification in it's own context. Do not need anything to be already declared or any context to be opened.

val check_full : ?hyps:exp list -> exp -> bool option

Do a standalone check of whether the expression is implied by the list of hypothesis.

val check_sat_full : exp list -> bool option

Do a standalone check of whether the set of assertion is sat

val simplify_subterms_full : ?hyps:exp list -> exp -> exp
diff --git a/doc/html/read-dwarf/Z3/module-type-Var/index.html b/doc/html/read-dwarf/Z3/module-type-Var/index.html index bf6d4ad2..0ccc033e 100644 --- a/doc/html/read-dwarf/Z3/module-type-Var/index.html +++ b/doc/html/read-dwarf/Z3/module-type-Var/index.html @@ -1,2 +1,2 @@ -Var (read-dwarf.Z3.Var)

Module type Z3.Var

The functors in this section require a bit more support from variable than plain Exp.Var

include Exp.Var
type t

The type of variables

val equal : t -> t -> bool

Equality predicate that will be passed to expressions

val pp : t -> Utils.Pp.document

Pretty printer to be used, both for memory pretty printing and for sending memory to Z3

val ty : t -> Exp.ty

Get the type of the variable

val hash : t -> int

Hashing function to store variable in Hashtbls. Must be compatible with equal

val of_string : string -> t

Parser from a string. Must be the inverse of pp

\ No newline at end of file +Var (read-dwarf.Z3.Var)

Module type Z3.Var

The functors in this section require a bit more support from variable than plain Exp.Var

include Exp.Var
type t

The type of variables

val equal : t -> t -> bool

Equality predicate that will be passed to expressions

val pp : t -> Utils.Pp.document

Pretty printer to be used, both for memory pretty printing and for sending memory to Z3

val ty : t -> Exp.ty

Get the type of the variable

val hash : t -> int

Hashing function to store variable in Hashtbls. Must be compatible with equal

val of_string : string -> t

Parser from a string. Must be the inverse of pp

diff --git a/doc/html/read-dwarf/index.html b/doc/html/read-dwarf/index.html index 80f9cd2a..c05c2753 100644 --- a/doc/html/read-dwarf/index.html +++ b/doc/html/read-dwarf/index.html @@ -1,2 +1,2 @@ -index (read-dwarf.index)

read-dwarf

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.

Overview

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.

  • The binary is parsed by the linksem library using the Elf and Dw modules. In this phase C-Type linking happens as described in C type linking: From Linksem
  • The function name is found and used in the Dw.Func format.
  • The API and ABI of the function are computed according to the current achitecture. The generic 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 :().
  • If we suppose the instruction caches are empty, the Isla.Server will be started and used by Run.Init to fetch the machine initial State. This will call isla to get the trace of the nop instruction. See InstructionPipeline
  • The function entry State can be computed from the machine initial state and the ABI.
  • We load the symbolic execution engine Run.Runner with the DWARF information and initialize it.
  • We create a 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.
  • The 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.
  • Then we pretty print the results using Utils.Pp and Utils.Logs. See Printing

Detailed pages

Here is a list of top-level pages that each explains a subgroup of functionality:

  • Architecture: All modules related to achitecture representation
  • BinaryAnalysis: All modules about reading ELF and DWARF information
  • CLI: All modules defining the command line interfaces.
  • Configuration: Configuration organisation
  • InstructionPipeline: All modules related to instruction semantics processing.
  • Printing: Generic information about printing and logging
  • SymbolicExecution: All modules related to top-level symbolic execution.
  • SymbolicExpressions: All modules related to symbolic expression manipulation
  • TypeInference: All modules about the C type system and type inference.
  • Utilities: List of modules that provide generic functionality

Dependencies

Here 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 analyzer
  • isla-lang: Isla traces parser
  • cmdliner: 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 module
  • ocamlgraph: 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.

Alphabetical index

Here is an alphabetical list of all modules, except src/arch/aarch64/sig.ml.

\ No newline at end of file +index (read-dwarf.index)

read-dwarf

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.

Overview

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.

  • The binary is parsed by the linksem library using the Elf and Dw modules. In this phase C-Type linking happens as described in C type linking: From Linksem
  • The function name is found and used in the Dw.Func format.
  • The API and ABI of the function are computed according to the current achitecture. The generic 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 :().
  • If we suppose the instruction caches are empty, the Isla.Server will be started and used by Run.Init to fetch the machine initial State. This will call isla to get the trace of the nop instruction. See InstructionPipeline
  • The function entry State can be computed from the machine initial state and the ABI.
  • We load the symbolic execution engine Run.Runner with the DWARF information and initialize it.
  • We create a 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.
  • The 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.
  • Then we pretty print the results using Utils.Pp and Utils.Logs. See Printing

Detailed pages

Here is a list of top-level pages that each explains a subgroup of functionality:

  • Architecture: All modules related to achitecture representation
  • BinaryAnalysis: All modules about reading ELF and DWARF information
  • CLI: All modules defining the command line interfaces.
  • Configuration: Configuration organisation
  • InstructionPipeline: All modules related to instruction semantics processing.
  • Printing: Generic information about printing and logging
  • SymbolicExecution: All modules related to top-level symbolic execution.
  • SymbolicExpressions: All modules related to symbolic expression manipulation
  • TypeInference: All modules about the C type system and type inference.
  • Utilities: List of modules that provide generic functionality

Dependencies

Here 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 analyzer
  • isla-lang: Isla traces parser
  • cmdliner: 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 module
  • ocamlgraph: 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.

Alphabetical index

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 dire
  • Dw This module provides the specifically interpreted DWARF information needed for read-dwarf operations
  • Elf
  • 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