├── fiddle_extra ├── __init__.py ├── frama_c │ ├── __init__.py │ ├── Makefile │ ├── bx-framac-patch │ ├── machdep_arm.ml │ ├── call.ml │ ├── call_analysis.ml │ └── dest_analysis.ml ├── scripts │ ├── __init__.py │ ├── emacs_modules │ │ └── substage.el │ └── sort_histogram.py ├── pymacs_request.py ├── fiddle_plugin.py └── find_write_loops.py ├── fiddle_gdb ├── __init__.py ├── watchpoints.py ├── enforce.py ├── hook_write.py └── calltrace.py ├── fiddle.cfg ├── fiddle ├── hw_info │ ├── bbxm │ │ ├── u_boot │ │ │ ├── spl │ │ │ │ ├── spl-events │ │ │ │ ├── substages.yml │ │ │ │ └── memory_map.yml │ │ │ └── main │ │ │ │ ├── main-events │ │ │ │ ├── substages.yml │ │ │ │ └── memory_map.yml │ │ ├── ocdinit │ │ ├── ocdinit2 │ │ ├── am37x_base_memory_map.csv │ │ └── hw_info.py │ └── _hw │ │ └── test │ │ └── _single │ │ ├── memory_map.yml │ │ └── substages.yml ├── __init__.py ├── reporter.py ├── run_cmds.py ├── memory_tree.py ├── unicorn_utils.py ├── r2_keeper.py ├── pytable_utils.py ├── qemusimpleparse.py ├── git_mgr.py ├── main.py ├── run_cmd.py ├── pymacs_request │ └── __init__.py ├── pure_utils.py ├── configs │ └── defaults.cfg ├── qemu_raw_trace.py ├── testsuite_utils.py ├── ia.py ├── external_source_manager.py ├── addr_space.py ├── process_args.py └── doit_manager.py ├── .gitmodules ├── .gitignore ├── LICENSE ├── setup.py └── README /fiddle_extra/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /fiddle_extra/frama_c/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /fiddle_extra/scripts/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /fiddle_gdb/__init__.py: -------------------------------------------------------------------------------- 1 | name = 'fiddle_gdb' 2 | -------------------------------------------------------------------------------- /fiddle.cfg: -------------------------------------------------------------------------------- 1 | /home/bx/fiddle/fiddle_examples/uboot_bbxm/fiddle.cfg -------------------------------------------------------------------------------- /fiddle/hw_info/bbxm/u_boot/spl/spl-events: -------------------------------------------------------------------------------- 1 | my_cpu_write 2 | 3 | -------------------------------------------------------------------------------- /fiddle/hw_info/bbxm/u_boot/main/main-events: -------------------------------------------------------------------------------- 1 | main_uboot_writes 2 | 3 | 4 | -------------------------------------------------------------------------------- /fiddle/__init__.py: -------------------------------------------------------------------------------- 1 | # Bootloader (and Beyond) Instrumentation Suite package 2 | name = 'fiddle' 3 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "fiddle_examples"] 2 | path = fiddle_examples 3 | url = https://github.com/bx/fiddle_examples 4 | -------------------------------------------------------------------------------- /fiddle/hw_info/_hw/test/_single/memory_map.yml: -------------------------------------------------------------------------------- 1 | regions: 2 | ALL: 3 | addresses: [0, 0xFFFFFFFFFFFFFFFF] 4 | type: "global" 5 | 6 | -------------------------------------------------------------------------------- /fiddle/hw_info/_hw/test/_single/substages.yml: -------------------------------------------------------------------------------- 1 | _start: 2 | substage_type: "bookkeeping" 3 | new_regions: ["ALL"] 4 | 5 | main: 6 | substage_type: "bookkeeping" 7 | 8 | -------------------------------------------------------------------------------- /fiddle/reporter.py: -------------------------------------------------------------------------------- 1 | from doit.reporter import ConsoleReporter 2 | import logging 3 | 4 | class FiddleReporter(ConsoleReporter): 5 | def write(self, text): 6 | if logging.getLogger().level <= logging.DEBUG: 7 | ConsoleReporter.write(self, text) 8 | -------------------------------------------------------------------------------- /fiddle/hw_info/bbxm/ocdinit: -------------------------------------------------------------------------------- 1 | init 2 | reset init 3 | arm core_state arm 4 | dap apsel 1 5 | reg r0 0x4020dff0 6 | mwb 0x4020DFF2 1 7 | mwb 0x4020DFF4 6 8 | dap apsel 0 9 | step 0x402007FC 10 | reg r0 0x4020dff0 11 | mwb 0x4020DFF2 1 12 | mwb 0x4020DFF4 6 13 | dap apsel 1 14 | -------------------------------------------------------------------------------- /fiddle/hw_info/bbxm/ocdinit2: -------------------------------------------------------------------------------- 1 | halt 2 | init 3 | reset init 4 | dap apsel 1 5 | bp 0x40200800 4 hw 6 | resume 7 | wait_halt 10000 8 | verify_jtag off 9 | verify_ircapture off 10 | poll off 11 | reg r0 0x4020dff0 12 | mwb 0x4020DFF2 1 13 | mwb 0x4020DFF4 6 14 | rbp 0x40200800 15 | dap apsel 0 16 | 17 | -------------------------------------------------------------------------------- /fiddle/run_cmds.py: -------------------------------------------------------------------------------- 1 | from enum import Enum 2 | 3 | cmds = Enum("CmdTyp", ["print_build_commands", "build_software", "create", "list_policies", "import_policy", 4 | "setup_trace", "run_new_trace", "do_trace", "list_test_runs", "print_trace_commands", 5 | "postprocess_trace", "hook", "list_instances"]) 6 | -------------------------------------------------------------------------------- /fiddle/memory_tree.py: -------------------------------------------------------------------------------- 1 | import intervaltree 2 | 3 | 4 | def int_repr(self): 5 | if self.end > 0xFFFFFFFF: 6 | ct = 16 7 | else: 8 | ct = 8 9 | fmt = "({0:%dX}, {1:%dX})" % (ct, ct) 10 | return fmt.format(self.begin, self.end) 11 | 12 | 13 | intervaltree.Interval.__str__ = int_repr 14 | intervaltree.Interval.__repr__ = int_repr 15 | 16 | # export version of intervaltree that prints all values as hex 17 | globals()['intervaltree'] = intervaltree 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | *~ 3 | \#* 4 | \** 5 | trace-* 6 | !trace-events 7 | *.h5 8 | *.cmi 9 | *.cmt 10 | *.cmo 11 | *.cmx 12 | *.cmxs 13 | *.cmti 14 | *.o 15 | *.out 16 | META.frama-c* 17 | *_DEP 18 | *.depend 19 | *.i 20 | *.frama-c 21 | *.session 22 | *.txt 23 | *.framac 24 | .python-version 25 | \.\#* 26 | *.frama 27 | *.framac 28 | .ropeproject 29 | .doit.db 30 | *.csv 31 | !am37x_base_memory_map.csv 32 | *.img 33 | *.pdf 34 | *.egg-info 35 | *.pdf 36 | /build 37 | /dist 38 | set.py 39 | -------------------------------------------------------------------------------- /fiddle_extra/frama_c/Makefile: -------------------------------------------------------------------------------- 1 | ARGS= -load-script machdep_arm.ml -machdep arm -load-script dest_analysis.ml -big-ints-hex 0 -absolute-valid-range 0x40000000-0xffffffff -val-initialization-padding-globals=no -const-writable -constfold -val -then -dst 2 | QUIET_ARGS=-kernel-verbose 0 -value-verbose 0 3 | UBOOT=~/u-boot-frama-c 4 | run: 5 | frama-c ${UBOOT}/mem-common.i ${ARGS} ${QUIET_ARGS} -main gpmc_init 6 | 7 | test: 8 | frama-c ${UBOOT}/boot-common-patched.i ${UBOOT}/beagle-patched.i ${UBOOT}/board-patched.i ${UBOOT}/omap_gpio-patched.i ${UBOOT}/boot.i ${UBOOT}/mem-common.i ${UBOOT}/spl-patched.i -main board_init_r ${ARGS} 9 | 10 | testgd: 11 | frama-c ${UBOOT}/boot-common-patched.i -main save_omap_boot_params -dst-more ${QUIET_ARGS} ${ARGS} 12 | 13 | 14 | testspl: 15 | frama-c ${UBOOT}/spl-patched.i -dst-more -main board_init_r ${ARGS} 16 | -------------------------------------------------------------------------------- /fiddle/unicorn_utils.py: -------------------------------------------------------------------------------- 1 | # from https://gist.github.com/moyix/669344f534c83e0704e0 2 | from unicorn.arm_const import * 3 | from unicorn.arm64_const import * 4 | from unicorn.x86_const import * 5 | 6 | 7 | class UnicornCPU(object): 8 | def __init__(self, arch): 9 | # Prevent infinite recursion 10 | super(UnicornCPU, self).__setattr__('arch', arch) 11 | 12 | def get_reg_name_val(self, name): 13 | name = name.upper() 14 | reg_name = 'UC_' + self.arch + '_REG_' + name 15 | try: 16 | reg_name_val = globals()[reg_name] 17 | except KeyError: 18 | raise AttributeError(reg_name) 19 | return reg_name_val 20 | 21 | c = UnicornCPU("ARM") 22 | 23 | def reg_val_of(cpu, name): 24 | c = UnicornCPU(cpu) 25 | return c.get_reg_name_val(name) 26 | 27 | def reg_val(name): 28 | global c 29 | return c.get_reg_name_val(name) 30 | -------------------------------------------------------------------------------- /fiddle/r2_keeper.py: -------------------------------------------------------------------------------- 1 | import r2pipe 2 | import json 3 | 4 | files = {} 5 | entry = {} 6 | bba = [] 7 | 8 | def gets(f, cmd): 9 | if f in files.keys(): 10 | handle = files[f] 11 | else: 12 | handle = r2pipe.open(f, ['-2']) 13 | files[f] = handle 14 | entry[f] = handle.cmd("s") 15 | handle.cmd('e anal.bb.maxsize=10000') 16 | handle.cmd('aac*') 17 | 18 | out = handle.cmd(cmd) 19 | return out 20 | 21 | 22 | def run_aab(f): 23 | if f in bba: 24 | return 25 | else: 26 | gets(f, "aab") # run basic block analysis 27 | bba.append(f) 28 | return 29 | 30 | def get(f, cmd): 31 | out = gets(f, cmd) 32 | try: 33 | return json.loads(out) 34 | except ValueError: 35 | return [] 36 | 37 | 38 | def entrypoint(f): 39 | return entry[f] 40 | 41 | 42 | def cd(f, dst): 43 | gets(f, "cd %s" % dst) 44 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Rebecca Shapiro 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /fiddle/hw_info/bbxm/am37x_base_memory_map.csv: -------------------------------------------------------------------------------- 1 | GPMC, 0x00000000, 0x3fffffff, rw, special 2 | On-chip Boot ROM, 0x40000000, 0x4001BFFF, r, ROM 3 | Reserved, 0x4001C000, 0x401FFFFF, ?, reserved 4 | SRAM internal, 0x40200000, 0x4020FFFF, rw, RAM 5 | Reserved, 0x40210000, 0x47FFFFFF, ?, reserved 6 | L4-Core, 0x48000000, 0x48FFFFFF, rw, registers 7 | L4-Per, 0x49000000, 0x490FFFFF, rw, registers 8 | Reserved, 0x49100000, 0x4FFFFFFF, ?, reserved 9 | SGX, 0x50000000, 0x5000FFFF, rw, registers 10 | Reserved, 0x50010000, 0x53FFFFFF, ?, reserved 11 | L4-Emu, 0x54000000, 0x547FFFFF, rw, registers 12 | Reserved, 0x54800000, 0x5BFFFFFF, ?, reserved 13 | IVA2.2 subsystem, 0x5C000000, 0x5EFFFFFF, rw, iva2.2 14 | Reserved, 0x5F000000, 0x67FFFFFF, ?, reserved 15 | L3 control registers, 0x68000000, 0x68FFFFFF, rw, registers 16 | Reserved, 0x69000000, 0x6BFFFFFF, ?, reserved 17 | SMS registers, 0x6C000000, 0x6CFFFFFF, rw, registers 18 | SDRC registers, 0x6D000000, 0x6DFFFFFF, rw, registers 19 | GPMC registers, 0x6E000000, 0x6EFFFFFF, rw, registers 20 | Reserved, 0x6F000000, 0x6FFFFFFF, ?, reserved 21 | SDRC/SMS virtual address space 0, 0x70000000, 0x7FFFFFFF, rw, virtual 22 | CS0 -- SDRAM, 0x80000000, 0x9FFFFFFF, rw, RAM 23 | CS1 -- SDRAM, 0xA0000000, 0xBFFFFFFF, rw, RAM 24 | Reserved, 0xC0000000, 0xDFFFFFFF, ?, reserved 25 | SDRC/SMS virtual address space 1, 0xE0000000, 0xFFFFFFFF, rw, virtual 26 | -------------------------------------------------------------------------------- /fiddle_extra/scripts/emacs_modules/substage.el: -------------------------------------------------------------------------------- 1 | ;; MIT License 2 | 3 | ;; Copyright (c) 2017 Rebecca ".bx" Shapiro 4 | 5 | ;; Permission is hereby granted, free of charge, to any person obtaining a copy 6 | ;; of this software and associated documentation files (the "Software"), to deal 7 | ;; in the Software without restriction, including without limitation the rights 8 | ;; to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | ;; copies of the Software, and to permit persons to whom the Software is 10 | ;; furnished to do so, subject to the following conditions: 11 | 12 | ;; The above copyright notice and this permission notice shall be included in all 13 | ;; copies or substantial portions of the Software. 14 | 15 | ;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | ;; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | ;; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | ;; AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | ;; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | ;; OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | ;; SOFTWARE. 22 | 23 | (defun mkpath (dir file) 24 | (concat dir "/" file)) 25 | 26 | (defun create-substage-calltraces (calltracefile substages outputdir) 27 | (if (not (file-directory-p outputdir)) 28 | (make-directory outputdir t)) 29 | (if (stringp substages) ; then open file to read out proposed substages 30 | (with-temp-buffer 31 | (insert-file-contents substages) 32 | (eval-buffer))) 33 | (save-excursion 34 | (find-file calltracefile) 35 | (goto-char (point-min)) 36 | (show-all) 37 | (message "substages %s" substages) 38 | (let ((min (pop substages))) 39 | (mapc (lambda (max) (ct-get-unique-functions-after-point outputdir (line-beginning-position min) (line-beginning-position (+ 1 max))) (message "%s-%s" min max) (setq min max)) substages)))) 40 | 41 | 42 | 43 | (provide 'instr-substage) 44 | 45 | -------------------------------------------------------------------------------- /fiddle/pytable_utils.py: -------------------------------------------------------------------------------- 1 | # MIT License 2 | 3 | # Copyright (c) 2017 Rebecca ".bx" Shapiro 4 | 5 | # Permission is hereby granted, free of charge, to any person obtaining a copy 6 | # of this software and associated documentation files (the "Software"), to deal 7 | # in the Software without restriction, including without limitation the rights 8 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | # copies of the Software, and to permit persons to whom the Software is 10 | # furnished to do so, subject to the following conditions: 11 | 12 | # The above copyright notice and this permission notice shall be included in all 13 | # copies or substantial portions of the Software. 14 | 15 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | # SOFTWARE. 22 | 23 | 24 | def get_rows(table, query): 25 | indices = table.get_where_list(query) # [r for r in rows] 26 | return [table[i] for i in indices] 27 | 28 | 29 | def query(table, query): 30 | return table.where(query) # [r for r in rows] 31 | 32 | 33 | def _print(line): 34 | print line 35 | 36 | 37 | def has_results(table, query): 38 | res = table.where(query) 39 | try: 40 | next(res) 41 | return True 42 | except StopIteration: 43 | return False 44 | 45 | 46 | def get_sorted(table, col, field=None): 47 | return table.read_sorted(col, field=field) 48 | 49 | 50 | def get_unique_result(table, query): 51 | res = get_rows(table, query) 52 | if len(res) > 1: 53 | raise Exception("more than 1 result matching query %s in table %s" % 54 | (query, str(table))) 55 | elif len(res) == 0: 56 | return None 57 | else: 58 | return res[0] 59 | -------------------------------------------------------------------------------- /fiddle/hw_info/bbxm/u_boot/main/substages.yml: -------------------------------------------------------------------------------- 1 | _start: 2 | substage_type : "bookkeeping" 3 | new_regions: ["ROM", "RAM", "SRAM0", "Registers", "missing_control_padconf0", "missing_control_padconf1", "missing_protection_mech_0"] 4 | lowlevel_init: 5 | substage_type: "bookkeeping" 6 | reclassified_regions: 7 | RAM.spl.main_stack: "readonly" 8 | RAM.rest: "stack" 9 | cpy_clk_code: 10 | substage_type: "loading" 11 | lowlevel_init_finish: 12 | substage_type: "bookkeeping" 13 | reclassified_regions: 14 | RAM.cpy_clk_code: "readonly" 15 | board_init_f_mem: 16 | substage_type: "patching" 17 | comment: "stack set by _main before board_init_f_mem" 18 | board_init_f_mem_finish: 19 | substage_type: "bookkeeping" 20 | reclassified_regions: 21 | RAM.spl.main_heap: "bookkeeping" 22 | RAM.spl.main_gd: "bookkeeping" 23 | RAM.spl.main_stack: "stack" 24 | board_init_f: 25 | substage_type: "bookkeeping" 26 | # dram_init? is already init'd and being used 27 | # reserve_global_data, zeros out gd_t 28 | # setup_reloc, copies gd to new_gd 29 | _main_finish: 30 | type: "bookkeeping" 31 | reclassified_regions: 32 | RAM.spl.main_stack: "readonly" 33 | RAM.rest: "readonly" 34 | SRAM1.stack: "stack" 35 | SRAM1.irq_stack: "stack" 36 | SRAM1.new_gd: "bookkeeping" 37 | SRAM1.gd: "bookkeeping" 38 | SRAM1.heap: "bookkeeping" 39 | new_regions: ["SRAM1.stack", "SRAM1.irq_stack", "SRAM1.new_gd", "SRAM1.gd", "SRAM1.heap", "SRAM1.uboot_image"] 40 | undefined_regions: ["SRAM0.rest"] 41 | relocate_code: 42 | type: "loading" 43 | here: 44 | type: "loading" 45 | comment: "relocate_vectors" 46 | reclassified_regions: 47 | SRAM1.uboot_image.text: "readonly" 48 | SRAM1.uboot_image.rodata: "readonly" 49 | SRAM1.uboot_image.rwdata: "bookkeeping" 50 | SRAM0.main_image.rodata: "readonly" 51 | SRAM0.main_image.rwdata: "readonly" 52 | SRAM0.main_image.rest: "readonly" 53 | SRAM0.reldyn: "readonly" 54 | SRAM0.mainbss: "readonly" 55 | clear_bss: 56 | type: "loading" 57 | board_init_r: 58 | type: "bookkeeping" 59 | comment: "file: common/board_r.c" 60 | new_regions: ["SRAM1.subsequent_stage_image"] 61 | reclassified_regions: 62 | SRAM1.uboot_image.mainbss: "bookkeeping" 63 | 64 | # initr_malloc -> SRAM1.heap (and zeros it out) 65 | # initr_env 66 | # jumptable_init 67 | # initr_enable_interrupts 68 | # autoboot_command 69 | -------------------------------------------------------------------------------- /fiddle_extra/frama_c/bx-framac-patch: -------------------------------------------------------------------------------- 1 | diff -r --recursive ./a/src/kernel_services/ast_queries/cil.ml ./b/src/kernel_services/ast_queries/cil.ml 2 | 317d316 3 | < (* Kernel.feedback ~level:2 "attrs: %s" (List.fold_left (fun s a -> s ^ "," ^ (attributeName a)) "" al); *) 4 | 4418c4417 5 | < | TComp (comp, _, a') -> f a'; 6 | --- 7 | > | TComp (comp, _, a') -> f a'; 8 | 9 | diff -r --recursive ./a/src/kernel_services/ast_queries/logic_typing.ml ./b/src/kernel_services/ast_queries/logic_typing.ml 10 | 4009,4011c4009,4010 11 | < "incompatible type of '%s' with volatile reads declaration" 12 | < fct; 13 | < 14 | --- 15 | > "incompatible type of '%s' with volatile writes declaration" 16 | > fct; 17 | 4034c4033 18 | < (* && Cil.typeHasAttributeDeep "volatile" ret *) 19 | --- 20 | > && Cil.typeHasAttributeDeep "volatile" ret 21 | 4037,4049c4036 22 | < checks_tsets_type fct ret_type (* tsets should have type: T *) 23 | < | Some [_,arg1,_] -> 24 | < Kernel.feedback ~level:2 "sdfsdf???? {%a}" Cil_datatype.Typ.pretty (typeOf_pointed arg1); 25 | < Kernel.feedback ~level:2 "sdfsdf???? {%a}" Cil_datatype.Typ.pretty ret_type; 26 | < Kernel.feedback ~level:2 "sdfsdf???? {%a}" Cil_datatype.Typ.pretty volatile_ret_type; 27 | < Kernel.feedback ~level:2 "had attr volatile deeb %b" (Cil.typeHasAttributeDeep "volatile" ret); 28 | < Kernel.feedback ~level:2 "had attr volatile %b" (Cil.typeHasAttribute "volatile" ret); 29 | < Kernel.feedback ~level:2 "sdfsdf???? eq ret type %b" (Cil_datatype.Typ.equal (typeOf_pointed arg1) ret_type); 30 | < Kernel.feedback ~level:2 "sdfsdf???? arg1 ptr %b" (isPointerType arg1); 31 | < Kernel.feedback ~level:2 "void %b" (isVoidType ret); 32 | < Kernel.feedback ~level:2 "varg %b" is_varg_arg; 33 | < Kernel.feedback ~level:2 "sdfsdf???? eq volatile ret type %b" (Cil_datatype.Typ.equal (typeOf_pointed arg1) volatile_ret_type); 34 | < error () 35 | --- 36 | > checks_tsets_type fct ret_type (* tsets should have type: T *) 37 | 4080c4067 38 | < (* && Cil.typeHasAttributeDeep "volatile" ret *) 39 | --- 40 | > && Cil.typeHasAttributeDeep "volatile" ret 41 | -------------------------------------------------------------------------------- /fiddle/hw_info/bbxm/hw_info.py: -------------------------------------------------------------------------------- 1 | def get_relocation(Main, stage, name, RelocDescriptor, utils): 2 | infos = [] 3 | elf = stage.elf 4 | cc = Main.cc 5 | r = RelocDescriptor("clk_code", None, None, None, None, 6 | None, stage, True, "go_to_speed") 7 | dstaddr = r.dstaddr 8 | srcdir = Main.get_runtime_config("temp_target_src_dir") 9 | cmd = "%sgdb --cd=%s " \ 10 | "-ex 'x/i 0x%x' --batch --nh --nx %s" % (cc, 11 | srcdir, 12 | dstaddr, elf) 13 | output = Main.shell.run_multiline_cmd(cmd) 14 | output = output[0].strip() 15 | output = output.split(';')[1].strip() 16 | dstaddr = long(output, 16) 17 | # now get value at this address 18 | cmd = "%sgdb --cd=%s " \ 19 | "-ex 'x/wx 0x%x' --batch --nh --nx %s" % (cc, srcdir, 20 | dstaddr, elf) 21 | output = Main.shell.run_multiline_cmd(cmd) 22 | output = output[0].strip() 23 | dstaddr = long(output.split(':')[1].strip(), 0) 24 | 25 | r.set_reloffset(dstaddr - r.cpystartaddr) 26 | if name == "clk_code": 27 | return r.get_row_information() 28 | 29 | if stage.stagename == 'main' and name == "reloc_code": 30 | cpystartaddr = utils.get_symbol_location("__image_copy_start", stage) 31 | cpyendaddr = utils.get_symbol_location("__image_copy_end", stage) 32 | r = RelocDescriptor("reloc_code", None, 33 | None, cpystartaddr, 34 | cpyendaddr, -1, stage, True) 35 | reloffset = 0x9ff00000 - 0x800a0000 # hand calculated 36 | r.set_reloffset(reloffset) 37 | mod = r.relmod 38 | infos.append(r.get_row_information()) 39 | 40 | # keep origional c_runtime_cpu_setup since it is run from orig location 41 | # after relocation. "unrelocate" it but keep orig 42 | name = "c_runtime_cpu_setup" 43 | (start, end) = utils.get_symbol_location_start_end(name, stage) 44 | hereaddr = utils.get_symbol_location("here", stage) 45 | startrel = (start + reloffset) % mod 46 | endrel = (end + reloffset) % mod 47 | r = RelocDescriptor("here", hereaddr, hereaddr, 48 | startrel, endrel, start, 49 | stage, False, name) 50 | r.set_reloffset(-1*reloffset) 51 | return r.get_row_information() 52 | -------------------------------------------------------------------------------- /fiddle_extra/frama_c/machdep_arm.ml: -------------------------------------------------------------------------------- 1 | (* MIT License *) 2 | 3 | (* Copyright (c) 2017 Rebecca ".bx" Shapiro *) 4 | 5 | (* Permission is hereby granted, free of charge, to any person obtaining a copy *) 6 | (* of this software and associated documentation files (the "Software"), to deal *) 7 | (* in the Software without restriction, including without limitation the rights *) 8 | (* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell *) 9 | (* copies of the Software, and to permit persons to whom the Software is *) 10 | (* furnished to do so, subject to the following conditions: *) 11 | 12 | (* The above copyright notice and this permission notice shall be included in all *) 13 | (* copies or substantial portions of the Software. *) 14 | 15 | (* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR *) 16 | (* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, *) 17 | (* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE *) 18 | (* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER *) 19 | (* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, *) 20 | (* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE *) 21 | (* SOFTWARE. *) 22 | 23 | (* Contents of e.g. file cil/src/machdep_x86_32.ml properly modified for your 24 | architecture. The MSVC configuration is never used by Frama-C, no need 25 | to edit it (but it must be included). *) 26 | 27 | open Cil_types 28 | 29 | let arm = { 30 | version = "arm machdep"; 31 | compiler = "gcc"; 32 | cpp_arch_flags = []; 33 | (* All types but char and long long are 16 bits *) 34 | sizeof_short = 2; 35 | sizeof_int = 4; 36 | sizeof_long = 4; 37 | sizeof_longlong = 8; 38 | sizeof_float = 4; 39 | sizeof_double = 8; 40 | sizeof_longdouble = 8; 41 | sizeof_ptr = 4; 42 | sizeof_void = 4; 43 | sizeof_fun = 4; 44 | wchar_t = "int"; 45 | alignof_str = 1; 46 | alignof_fun = 1; 47 | char_is_unsigned = false; 48 | underscore_name = false; 49 | const_string_literals = false; 50 | alignof_aligned = 8; 51 | has__builtin_va_list = true; 52 | __thread_is_keyword = true; 53 | alignof_short = 2; 54 | alignof_int = 4; 55 | alignof_long = 4; 56 | alignof_longlong = 8; 57 | alignof_float = 4; 58 | alignof_double = 8; 59 | alignof_longdouble = 8; 60 | alignof_ptr = 4; 61 | little_endian = true; 62 | size_t = "unsigned int"; 63 | ptrdiff_t = "int"; 64 | 65 | } 66 | 67 | let () = File.new_machdep "arm" arm 68 | 69 | -------------------------------------------------------------------------------- /fiddle/qemusimpleparse.py: -------------------------------------------------------------------------------- 1 | # MIT License 2 | 3 | # Copyright (c) 2017 Rebecca ".bx" Shapiro 4 | 5 | # Permission is hereby granted, free of charge, to any person obtaining a copy 6 | # of this software and associated documentation files (the "Software"), to deal 7 | # in the Software without restriction, including without limitation the rights 8 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | # copies of the Software, and to permit persons to whom the Software is 10 | # furnished to do so, subject to the following conditions: 11 | 12 | # The above copyright notice and this permission notice shall be included in all 13 | # copies or substantial portions of the Software. 14 | 15 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | # SOFTWARE. 22 | 23 | import re 24 | 25 | 26 | class QemuParsedObject(): 27 | def __init__(self, time, pid, size, dest, pc, lr, cpsr): 28 | # pid, size, dest, pc, lr, cpsr 29 | if type(time) == str: 30 | self.time = float(time) 31 | self.pid = int(pid) 32 | self.size = long(size, 16) 33 | self.pc = long(pc, 16) 34 | self.lr = long(lr, 16) 35 | self.dest = long(dest, 16) 36 | self.cpsr = long(cpsr, 16) 37 | else: 38 | self.time = time 39 | self.pid = pid 40 | self.size = size 41 | self.pc = pc 42 | self.lr = lr 43 | self.dest = dest 44 | self.cpsr = cpsr 45 | 46 | def __repr__(self): 47 | return "time=%.3f pid=%i size=%d dest=0x%x pc=0x%x lr=0x%x, cpsr=0x%x" \ 48 | % (self.time, self.pid, self.size, self.dest, self.pc, self.lr, self.cpsr) 49 | 50 | 51 | class QemuSimpleParse(): 52 | @staticmethod 53 | def toobject(line): 54 | rgx = re.compile(r'my_cpu_write ([0-9]+\.{0,1}[0-9]{0,3}) pid=([0-9]+) \ 55 | size=(0x[0-9a-fA-F]+) addr=(0x[0-9a-fA-F]{0,8}) pc=(0x[0-9a-fA-F]{0,8}) \ 56 | lr=(0x[0-9a-fA-F]{0,8}) cpsr=(0x[0-9a-fA-F]{0,8})') 57 | res = re.match(rgx, line) 58 | if res is None: 59 | print "this line doesn't parse properly: %s" % line 60 | res = res.groups() 61 | return QemuParsedObject(*res) 62 | 63 | @staticmethod 64 | def tostring(line): 65 | QemuSimpleParse.objtostring(QemuSimpleParse.toobject(line)) 66 | -------------------------------------------------------------------------------- /fiddle/git_mgr.py: -------------------------------------------------------------------------------- 1 | import pygit2 2 | 3 | # MIT License 4 | 5 | # Copyright (c) 2017 Rebecca ".bx" Shapiro 6 | 7 | # Permission is hereby granted, free of charge, to any person obtaining a copy 8 | # of this software and associated documentation files (the "Software"), to deal 9 | # in the Software without restriction, including without limitation the rights 10 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | # copies of the Software, and to permit persons to whom the Software is 12 | # furnished to do so, subject to the following conditions: 13 | 14 | # The above copyright notice and this permission notice shall be included in all 15 | # copies or substantial portions of the Software. 16 | 17 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | # SOFTWARE. 24 | 25 | 26 | class GitManager(): 27 | def __init__(self, root): 28 | self.root = root 29 | self.repo = pygit2.Repository(self.root) 30 | 31 | def get_info(self): 32 | return {'head': self.get_head(), 'commit': self.get_commit()} 33 | 34 | def commit_changes(self): 35 | self.repo.index.add_all() 36 | self.repo.index.write() 37 | tree = self.repo.index.write_tree() 38 | author = pygit2.Signature("bx", "bx@cs") 39 | old = self.repo.create_commit(self.get_head(), 40 | author, 41 | author, 42 | "auto commit for testing", 43 | tree, 44 | [self.repo.head.get_object().hex]) 45 | 46 | def has_nothing_to_commit(self): 47 | status = self.repo.status() 48 | okstatus = [pygit2.GIT_STATUS_IGNORED, pygit2.GIT_STATUS_CURRENT] 49 | for (path,flags) in status.iteritems(): 50 | if flags not in okstatus: 51 | print "need to commit %s, %s" %(path, flags) 52 | return False 53 | return True 54 | 55 | def get_section_info(self): 56 | info = { 57 | "git-root": self.root, 58 | "git-head": self.get_head(), 59 | "git-commit": self.get_commit() 60 | } 61 | return info 62 | 63 | def get_head(self): 64 | return self.repo.head.name 65 | 66 | def get_commit(self): 67 | return self.repo.head.target.hex 68 | -------------------------------------------------------------------------------- /fiddle/main.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python2 2 | # MIT License 3 | 4 | # Copyright (c) 2017 Rebecca ".bx" Shapiro 5 | 6 | # Permission is hereby granted, free of charge, to any person obtaining a copy 7 | # of this software and associated documentation files (the "Software"), to deal 8 | # in the Software without restriction, including without limitation the rights 9 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | # copies of the Software, and to permit persons to whom the Software is 11 | # furnished to do so, subject to the following conditions: 12 | 13 | # The above copyright notice and this permission notice shall be included in all 14 | # copies or substantial portions of the Software. 15 | 16 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | # SOFTWARE. 23 | 24 | from config import Main 25 | from doit.action import CmdAction 26 | import argparse 27 | import os 28 | import run_cmd 29 | import sys 30 | import process_args 31 | 32 | 33 | def go(): 34 | parser = process_args.FiddleArgParser("Fiddle test suite") 35 | args = parser.args 36 | shell = run_cmd.Cmd() 37 | task_mgr = parser.task_manager() 38 | 39 | if args.create: 40 | task_mgr.create_test_instance() 41 | elif args.import_policy: 42 | task_mgr.import_policy() 43 | elif args.run_new_trace: 44 | task_mgr.run_trace() 45 | elif args.postprocess_trace: 46 | task_mgr.postprocess_trace() 47 | # elif args.run_trace: 48 | # task_mgr.run_trace() 49 | 50 | if args.print_trace_commands: 51 | task_mgr.rt.do_print_cmds() 52 | #if args.buildcommands or 53 | if args.print_build_commands or args.build_software or args.print_build_commands: 54 | targets = args.build_software if args.build_software else args.print_build_commands 55 | ret = task_mgr.build(targets, True if args.build_software else False) 56 | if args.print_build_commands: 57 | for r in ret: 58 | for task in r.tasks: 59 | print "to %s %s:" % (task.name, task.basename) 60 | for action in task.list_tasks()['actions']: 61 | if isinstance(action, CmdAction): 62 | print "cd %s" % task.root_dir 63 | print action.expand_action() 64 | print "\n" 65 | 66 | 67 | if __name__ == '__main__': 68 | go() 69 | -------------------------------------------------------------------------------- /fiddle_gdb/watchpoints.py: -------------------------------------------------------------------------------- 1 | # MIT License 2 | 3 | # Copyright (c) 2017 Rebecca ".bx" Shapiro 4 | 5 | # Permission is hereby granted, free of charge, to any person obtaining a copy 6 | # of this software and associated documentation files (the "Software"), to deal 7 | # in the Software without restriction, including without limitation the rights 8 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | # copies of the Software, and to permit persons to whom the Software is 10 | # furnished to do so, subject to the following conditions: 11 | 12 | # The above copyright notice and this permission notice shall be included in all 13 | # copies or substantial portions of the Software. 14 | 15 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | # SOFTWARE. 22 | 23 | import gdb 24 | import time 25 | import os 26 | import sys 27 | import gdb_tools 28 | from gdb_tools import * 29 | import db_info 30 | 31 | now = True 32 | start = time.time() 33 | 34 | 35 | class Flush(): 36 | def __init__(self): 37 | global now 38 | if now: 39 | self.do() 40 | 41 | def do(self): 42 | gdb.flush() 43 | global start 44 | stop = time.time() 45 | gdb.write("watchpoint trace finished in %f minutes\n" % ((stop-start)/60), gdb.STDOUT) 46 | 47 | def __call__(self): 48 | global now 49 | if not now: 50 | self.do() 51 | 52 | 53 | class Watchpoints(gdb_tools.GDBPlugin): 54 | def __init__(self): 55 | bp_hooks = {'StageEndBreak': self.endstop_hook} 56 | parser_options = [ 57 | gdb_tools.GDBPluginParser("stage", ["stagename"]) 58 | ] 59 | disabled = ["LongwriteBreak", "WriteBreak"] 60 | gdb_tools.GDBPlugin.__init__(self, "watchpoints", bp_hooks, 61 | f_hook=self.f_hook, 62 | calculate_write_dst=False, 63 | exit_hook=self._exit_hook, 64 | disabled_breakpoints=disabled, 65 | parser_args=parser_options) 66 | 67 | def f_hook(self, args): 68 | pass 69 | 70 | def endstop_hook(self, bp, ret): 71 | return True 72 | 73 | def stage_finish(self, now=False): 74 | flush = Flush() 75 | if now: 76 | flush() 77 | else: 78 | gdb.post_event(flush) 79 | 80 | def _exit_hook(self, event): 81 | self.stage_finish(True) 82 | 83 | 84 | plugin_config = Watchpoints() 85 | -------------------------------------------------------------------------------- /fiddle/hw_info/bbxm/u_boot/spl/substages.yml: -------------------------------------------------------------------------------- 1 | _start: 2 | substage_type: "bookkeeping" 3 | new_regions: ["ROM", "RAM", "Registers", "missing_control_padconf0", "missing_control_padconf1", "missing_protection_mech_0"] 4 | 5 | lowlevel_init: 6 | substage_type: "bookkeeping" 7 | reclassified_regions: 8 | RAM.rest0: "stack" 9 | RAM.rest1: "stack" 10 | undefined_regions: ["RAM.rom_stack"] 11 | 12 | cpy_clk_code: 13 | substage_type: "loading" 14 | 15 | lowlevel_init_finish: 16 | substage_type: "bookkeeping" 17 | reclassified_regions: 18 | RAM.downloaded_image.public_stack.stack_rest0: "stack" 19 | RAM.downloaded_image.public_stack.stack_rest1: "stack" 20 | RAM.downloaded_image.public_stack.cpy_clk_code: "readonly" 21 | RAM.rest1: "readonly" 22 | 23 | 24 | _main: 25 | substage_type: "bookkeeping" 26 | undefined_regions: ["RAM.downloaded_image.scratch_space", "RAM.downloaded_image.public_stack", "RAM.rest1", "RAM.rest0", "RAM.downloaded_image.unused", "RAM.downloaded_image.public_stack.beginning", "RAM.downloaded_image", "RAM.downloaded_image.public_stack.stack_rest1", "RAM.downloaded_image.public_stack.stack_rest0", "RAM.downloaded_image.downloaded_image_text", "RAM.downloaded_image.downloaded_image_data", "RAM.downloaded_image.public_stack.cpy_clk_code"] 27 | reclassified_regions: 28 | RAM1.global_data: "stack" 29 | RAM1.later_stack: "readonly" 30 | RAM1.end: "readonly" 31 | new_regions: ["RAM1"] 32 | 33 | board_init_f_mem: 34 | substage_type: "patching" 35 | reclassified_regions: 36 | RAM1.global_data: "patching" 37 | RAM1.later_stack: "stack" 38 | SRAM.bss.sram_test: "readonly" 39 | new_regions: ["SRAM"] 40 | 41 | board_init_f_mem_finish: 42 | substage_type: "bookkeeping" 43 | reclassified_regions: 44 | RAM1.global_data: "global" 45 | SRAM.bss.sram_test: "patching" 46 | SRAM.image: "future" 47 | SRAM.image_header: "future" 48 | 49 | board_init_f: 50 | substage_type: "patching" 51 | allowed_symbols: ["revision"] 52 | 53 | _main_finish: 54 | substage_type: "bookkeeping" 55 | reclassified_regions: 56 | SRAM.bss: "patching" 57 | SRAM.nonbss: "readonly" 58 | undefined_regions: ["SRAM.bss.sram_test"] 59 | 60 | clear_bss: 61 | substage_type: "patching" 62 | 63 | board_init_r: 64 | substage_type: "loading" 65 | reclassified_regions: 66 | SRAM.bss: "bookkeeping" 67 | SRAM.heap: "global" 68 | allowed_symbols: ["mem_malloc_start", "mem_malloc_end", "mem_malloc_brk", "_u_boot_list_2_i2c_2_omap24_0", "spl_boot_list", "mmc_devices", "mem_malloc_brk", "current_mallinfo", "sbrk_base", "av_", "max_sbrked_mem", "max_total_mem", "cur_dev", "fat_registered", "spl_image", "cur_dev_num", "do_fat_read_at_block", "get_contents_vfatname_block", "cur_part_info"] 69 | 70 | spl_after_load_image: 71 | substage_type: "bookkeeping" 72 | reclassified_regions: 73 | SRAM.image: "readonly" 74 | SRAM.image_header: "readonly" 75 | 76 | jump_to_image_no_args: 77 | substage_type: "bookkeeping" 78 | -------------------------------------------------------------------------------- /fiddle_extra/scripts/sort_histogram.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python2 2 | 3 | import argparse 4 | import os 5 | import re 6 | self_path = __file__ 7 | 8 | if __name__ == "__main__": 9 | parser = argparse.ArgumentParser("histogram") 10 | parser.add_argument('-i', '--input', 11 | default=os.path.join(os.path.abspath(os.path.dirname(self_path)), 12 | "..", "..", "..", 13 | "bootloader_test_data3/bbxm.u-boot.bbxm" 14 | "_verified_defconfig.refsheadsorig.956bc" 15 | "003f0b4ad2b283ed319e686d9aa5861341b.dba2" 16 | "3fb68c1005c5b8bbc0be304d3b33/trace_data/" 17 | "00000001/consolidate_writes/spl-write_range_info.txt")) 18 | parser.add_argument('-n', '--head', 19 | action="store", default=0, type=int) 20 | args = parser.parse_args() 21 | print args.input 22 | entries = [] 23 | hex = "[a-fA-F0-9]" 24 | addrre = re.compile("pc=(%s*)/\[(%s*)\]" % (hex, hex)) 25 | destre = re.compile("\[(%s*)-(%s*)\]" % (hex, hex)) 26 | 27 | class entry(): 28 | def __init__(self, l): 29 | line = l.split() 30 | 31 | r = addrre.match(line[0]) 32 | self.pc = r.group(1) 33 | self.va = r.group(2) 34 | self.fn = line[1][1:-1] 35 | self.lr = line[2][3:] 36 | self.lr_funnc = line[3][1:-1] 37 | 38 | r = destre.match(line[4]) 39 | self.dest_lo = r.group(1) 40 | self.dest_hi = r.group(2) 41 | 42 | self.numbytes = long(line[5][1:-1]) 43 | self.repeat = long(line[6]) 44 | rest = (" ".join(line[8:])).split("--") 45 | self.asm = rest[1].strip() 46 | self.source = rest[2].strip() 47 | 48 | def __str__(self): 49 | if self.va == self.pc: 50 | addrs = "pc=0x%s" % self.pc 51 | else: 52 | addrs = "va:0x%s/pc:0x%s" % (self.va, self.pc) 53 | return "[repeat:%s] %s @ %s wrote %s bytes to 0x%s [%s]" % (self.repeat, 54 | self.fn, 55 | addrs, 56 | self.numbytes, 57 | self.dest_lo, 58 | self.asm) 59 | 60 | with open(args.input) as f: 61 | for l in f.readlines(): 62 | entries.append(entry(l)) 63 | 64 | sort = sorted(entries, cmp=lambda x, y: y.numbytes - x.numbytes) 65 | count = len(sort) if args.head < 1 else args.head 66 | for i in range(count): 67 | e = sort[i] 68 | print e 69 | -------------------------------------------------------------------------------- /fiddle/run_cmd.py: -------------------------------------------------------------------------------- 1 | # MIT License 2 | 3 | # Copyright (c) 2017 Rebecca ".bx" Shapiro 4 | 5 | # Permission is hereby granted, free of charge, to any person obtaining a copy 6 | # of this software and associated documentation files (the "Software"), to deal 7 | # in the Software without restriction, including without limitation the rights 8 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | # copies of the Software, and to permit persons to whom the Software is 10 | # furnished to do so, subject to the following conditions: 11 | 12 | # The above copyright notice and this permission notice shall be included in all 13 | # copies or substantial portions of the Software. 14 | 15 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | # SOFTWARE. 22 | 23 | import subprocess 24 | import os 25 | import shlex 26 | 27 | 28 | class Cmd(): 29 | def __init__(self): 30 | self.returncode = 0 31 | 32 | def _check_output(self, cmd, chdir=None, catcherror=False, teefile=None, verbose=False): 33 | cwd = os.getcwd() 34 | out = "" 35 | if chdir is not None: 36 | os.chdir(chdir) 37 | if verbose: 38 | print cmd 39 | if catcherror: 40 | try: 41 | if teefile: 42 | output = [] 43 | c = ["unbuffer"]+shlex.split(cmd) 44 | proc = subprocess.Popen(c, stdout=subprocess.PIPE) 45 | if teefile: 46 | teefile = open(teefile, "w") 47 | for i in iter(proc.stdout.readline, ''): 48 | output.append(i) 49 | if teefile: 50 | teefile.write(i) 51 | else: 52 | print i[0:-1] 53 | self.returncode = proc.returncode 54 | out = "".join(output) 55 | if teefile: 56 | teefile.close() 57 | else: 58 | out = subprocess.check_output(cmd, shell=True) 59 | except subprocess.CalledProcessError as e: 60 | self.returncode = e.returncode 61 | out = e.output 62 | else: 63 | self.returncode = 0 64 | out = subprocess.check_output(cmd, shell=True) 65 | if cwd is not None: 66 | os.chdir(cwd) 67 | if verbose: 68 | print out 69 | return out 70 | 71 | def run_cmd(self, cmd, pwd=None, catcherror=False): 72 | return self._check_output(cmd, pwd, catcherror).strip() 73 | 74 | def run_multiline_cmd(self, cmd, pwd=None, catcherror=True, teefile=None, verbose=False): 75 | return self._check_output(cmd, pwd, catcherror, teefile, verbose=verbose).split('\n') 76 | 77 | def get_last_cmd_return_value(self): 78 | return self.returncode 79 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | from setuptools import setup 4 | from setuptools.command.build_py import build_py 5 | 6 | from setuptools.command.install_lib import install_lib 7 | 8 | 9 | 10 | import pip 11 | from distutils.util import execute 12 | import os 13 | import sys 14 | 15 | 16 | 17 | # this too is a hack, but I am unable to get setuputils to instal a requirement from a subdirectory of the capstone git repository 18 | class PkgBuild(build_py): 19 | def run(self): 20 | if not (os.system('pip show capstone') == 0): 21 | os.system('pip install git+https://github.com/aquynh/capstone@next#subdirectory=bindings/python') 22 | build_py.run(self) 23 | 24 | 25 | # class PkgInstall(install_lib): 26 | # def run(self): 27 | # install_lib.run(self) 28 | # pth = os.path.dirname(os.path.realpath(__file__)) 29 | # img = os.path.join(pth, 'fiddle/hw_info/bbxm/beagleboard-xm-orig.img') 30 | 31 | # os.system("cp %s %s" % (img, os.path.join(self.install_base,"fiddle/hw_info/bbxm/"))) 32 | 33 | 34 | 35 | # I'm guessing the following block of commented-out code is not a kosher 36 | # way of installing capstone... 37 | # execute(os.system, ('git clone https://github.com/aquynh/capstone',)) 38 | # execute(os.system, ('cd capstone; git checkout -b next',)) 39 | # os.environ['PREFIX'] = sys.prefix 40 | # execute(os.system, ('cd capstone; ./make.sh',)) 41 | # execute(os.system, ('cd capstone; ./make.sh install',)) 42 | 43 | setup( 44 | name="fiddle", 45 | version='0.0.9', 46 | packages=['fiddle', 'fiddle_gdb', 'fiddle_extra'], 47 | license="MIT", 48 | long_description=open("README").read(), 49 | author="bx", 50 | author_email="bx@cs.dartmouth.edu", 51 | url="https://github.com/bx/bootloader_instrumentation_suite", 52 | zip_safe=False, 53 | entry_points={ 54 | 'console_scripts': [ 55 | 'fiddle = fiddle.main:go' 56 | ], 57 | }, 58 | install_requires=[ 59 | 'doit==0.29.0', 60 | 'PyYAML>=3.11', 61 | 'pathlib', 62 | 'pygit2', 63 | 'pdfminer', 64 | 'ipython', 65 | 'intervaltree', 66 | 'tables', 67 | 'unicorn', 68 | 'numpy', 69 | 'munch', 70 | 'sortedcontainers', 71 | 'ipython==5.7.0', 72 | 'functools32', 73 | 'toml.py', 74 | 'enum34', 75 | 'r2pipe', 76 | ], 77 | cmdclass={'build_py': PkgBuild,}, 78 | # 'install_lib': PkgInstall}, 79 | package_data={ 80 | 'fiddle_extras': [ 81 | "frama_c/Makefile", "frama_c/machdep_arm.ml", 82 | "frama_c/call.ml", "frama_c/dest_analysis.ml", 83 | "frama_c/call_analysis.ml"], 84 | 'fiddle': ['configs/defaults.cfg', 85 | 'hw_info/bbxm/ocdinit', "hw_info/bbxm/ocdinit2", 86 | "hw_info/_hw/test/_single", 'hw_info/bbxm/ocdinit', 87 | 'hw_info/bbxm.regs.csv', 'hw_info/bbxm/trace-events', 88 | 'hw_info/bbxm/hw_info.py', 89 | 'hw_info/bbxm/u_boot/main/main-events', 90 | 'hw_info/bbxm/u_boot/spl/spl-events', 91 | 'hw_info/bbxm/am37x_base_memory_map.csv', 92 | 'hw_info/bbxm/am37x_technical_reference.pdf', 93 | 'hw_info/bbxm/beagleboard-xm-orig.img'], 94 | }, 95 | 96 | ) 97 | -------------------------------------------------------------------------------- /fiddle_extra/frama_c/call.ml: -------------------------------------------------------------------------------- 1 | (* MIT License *) 2 | 3 | (* Copyright (c) 2017 Rebecca ".bx" Shapiro *) 4 | 5 | (* Permission is hereby granted, free of charge, to any person obtaining a copy *) 6 | (* of this software and associated documentation files (the "Software"), to deal *) 7 | (* in the Software without restriction, including without limitation the rights *) 8 | (* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell *) 9 | (* copies of the Software, and to permit persons to whom the Software is *) 10 | (* furnished to do so, subject to the following conditions: *) 11 | 12 | (* The above copyright notice and this permission notice shall be included in all *) 13 | (* copies or substantial portions of the Software. *) 14 | 15 | (* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR *) 16 | (* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, *) 17 | (* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE *) 18 | (* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER *) 19 | (* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, *) 20 | (* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE *) 21 | (* SOFTWARE. *) 22 | 23 | open Cil_types 24 | open Cil 25 | 26 | module SS = Set.Make(String) 27 | module Funcall_info = struct 28 | type funcallinfo = { 29 | lval:Cil_types.lval; 30 | exp:Cil_types.exp; 31 | lexloc:Cil_types.location; 32 | lvalloc:Locations.location; 33 | lvalfulladdr:Integer.t; 34 | instr:Cil_types.instr; 35 | min:Abstract_interp.Int.t option; 36 | max:Abstract_interp.Int.t option; 37 | } 38 | 39 | let is_instr s = 40 | match s with 41 | Instr _ -> true 42 | | _ -> false 43 | 44 | let lexloc_string info = 45 | let ({Lexing.pos_fname=f1; Lexing.pos_lnum=l1; _}, _) = info.lexloc in 46 | Printf.sprintf "%s:%d" f1 l1 47 | 48 | let get_lval info = 49 | info.lval 50 | 51 | let get_lvalloc info = 52 | info.lvalloc 53 | 54 | 55 | let instr_string info = 56 | let s = Printer.pp_instr Format.str_formatter info.instr in 57 | Format.flush_str_formatter s 58 | 59 | let eval_lval lval kinstr = 60 | !Db.Value.lval_to_loc ~with_alarms:CilE.warn_none_mode kinstr lval 61 | 62 | let has_fulladdr info = 63 | not (Integer.is_zero info.lvalfulladdr) 64 | 65 | let get_fulladdr info = 66 | info.lvalfulladdr 67 | 68 | let lval_string info = 69 | let s = Printer.pp_lval Format.str_formatter info.lval in 70 | Format.flush_str_formatter s 71 | 72 | let form_callstack_string cs = 73 | List.fold_right (fun c s -> (match c with (f, _) -> 74 | s ^ "->" ^ (Ast_info.Function.get_name f.fundec))) cs "" 75 | 76 | let build_callinfo s kinstr = 77 | if (Db.Value.is_computed()) && (Db.Value.is_reachable_stmt s) then 78 | (match Db.Value.get_stmt_state_callstack ~after:true s with 79 | None -> SS.empty 80 | | Some(state) -> Value_types.Callstack.Hashtbl.fold 81 | (fun cs state r -> 82 | (if Cvalue.Model.is_reachable state then 83 | SS.add (form_callstack_string cs) r 84 | else 85 | SS.empty)) 86 | state SS.empty) 87 | else 88 | SS.empty 89 | end 90 | -------------------------------------------------------------------------------- /fiddle/hw_info/bbxm/u_boot/main/memory_map.yml: -------------------------------------------------------------------------------- 1 | regions: 2 | ROM: 3 | type: "readonly" 4 | addresses: [0x40000000, 0x4001C000] 5 | RAM: 6 | addresses: [0x40200000, 0x40210000] 7 | type: "bookkeeping" 8 | include_children: True 9 | subregions: 10 | top: 11 | addresses: [0x40200000, 0x4020f840] 12 | cpy_clk_code: 13 | type: "future" 14 | addresses: [0x4020f840, "0x4020f840 + lowlevel_init - cpy_clk_code"] 15 | comment: "double-check end address, i thought it copies from go_to_speed" 16 | spl: 17 | type: "readonly" 18 | addresses: ["0x4020f840 + lowlevel_init - cpy_clk_code", "CONFIG_SYS_INIT_SP_ADDR - 0x40"] 19 | subregions: 20 | main_heap: 21 | type: "readonly" 22 | addresses: ["CONFIG_SYS_INIT_SP_ADDR - 0x40 - GD_SIZE - CONFIG_SYS_MALLOC_F_LEN", "CONFIG_SYS_INIT_SP_ADDR - 0x40 - GD_SIZE - 0x2"] 23 | #gd->malloc_base = 0x4020fa10 24 | main_gd: 25 | type: "patching" 26 | addresses: ["CONFIG_SYS_INIT_SP_ADDR - 0x40 - GD_SIZE - 0x2", "CONFIG_SYS_INIT_SP_ADDR - 0x40 - 0x2"] 27 | main_stack: 28 | type: "stack" 29 | addresses: "remainder" 30 | rest: 31 | addresses: "remainder" 32 | type: "stack" 33 | SRAM0: 34 | type: "bookkeeping" 35 | addresses: [0x80000000, 0xc0000000] 36 | include_children: True 37 | subregions: 38 | main_image: 39 | addresses: [0x80100000, ".bss_start.start"] 40 | subregions: 41 | text: 42 | addresses: [0x80100000, ".rodata.start"] 43 | rodata: 44 | addresses: [".rodata.start", ".data.start"] 45 | rwdata: 46 | addresses: [".data.start", "__rel_dyn_start"] 47 | rest: 48 | addresses: "remainder" 49 | reldyn: 50 | addresses: ["__rel_dyn_start", "__rel_dyn_end"] 51 | mainbss: 52 | addresses: ["__rel_dyn_end", "__bss_end"] 53 | rest: 54 | type: "readonly" 55 | addresses: "remainder" 56 | SRAM1: 57 | type: "readonly" 58 | include_children: True 59 | subregions: 60 | subsequent_stage_image: 61 | type: "future" 62 | addresses: [0x80000000,0x9d000000] 63 | comment: "an estimate for now" 64 | stack: 65 | addresses: [0x9d000000, 0x9ef3feb0] 66 | irq_stack: 67 | addresses: [0x9ef3fec0, 0x9ef3fed0] 68 | new_gd: 69 | addresses: [0x9ef3fed0, 0x9ef3ffa8] 70 | gd: 71 | addresses: [0x9ef3ffa8, 0x9ef40000] 72 | heap: 73 | addresses: [0x9ef40000, 0x9ff60000] 74 | uboot_image: 75 | addresses: [0x9ff60000, 0x9fff0000] 76 | type: "future" 77 | include_children: True 78 | subregions: 79 | text: 80 | addresses: "SRAM0.main_image.text.1_relocated" 81 | rodata: 82 | addresses: "SRAM0.main_image.rodata.1_relocated" 83 | rwdata: 84 | addresses: "SRAM0.main_image.rwdata.1_relocated" 85 | reldyn: 86 | addresses: "SRAM0.reldyn.1_relocated" 87 | mainbss: 88 | addresses: "SRAM0.mainbss.1_relocated" 89 | tlb: 90 | addresses: [0x9fff0000, 0xa0000000] 91 | 92 | Registers: 93 | csv: "regs.csv" 94 | type: "global" 95 | include_children: True 96 | missing_control_padconf0: 97 | type: "global" 98 | addresses: [0x48002150, 0x48002154] 99 | comment: "address written to by boootloader (set_muxconf_regs) must not listed in am37x hardware reference" 100 | missing_control_padconf1: 101 | type: "global" 102 | addresses: [0x48002154, 0x48002158] 103 | comment: "address written to by boootloader (set_muxconf_regs) must not listed in am37x hardware reference" 104 | missing_protection_mech_0: 105 | type: "global" 106 | addresses: [0x68010060, 0x68010068] 107 | comment: "address written to by boootloader (secure_unlock_mem) must not listed in am37x hardware reference" 108 | values: 109 | SCRATCH_SPACE_ADDR: 0x4020E000 110 | OMAP5_SCRATCH_SPACE_END: 0x4020E028 111 | ROM_STACK_START: 0x40205cb0 112 | CONFIG_SYS_INIT_SP_ADDR: 0x4020ff20 113 | GD_SIZE: 0xd8 114 | CONFIG_SYS_MALLOC_F_LEN: 0x400 115 | 116 | stagename: "main" 117 | -------------------------------------------------------------------------------- /fiddle_extra/pymacs_request.py: -------------------------------------------------------------------------------- 1 | # MIT License 2 | 3 | # Copyright (c) 2017 Rebecca ".bx" Shapiro 4 | 5 | # Permission is hereby granted, free of charge, to any person obtaining a copy 6 | # of this software and associated documentation files (the "Software"), to deal 7 | # in the Software without restriction, including without limitation the rights 8 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | # copies of the Software, and to permit persons to whom the Software is 10 | # furnished to do so, subject to the following conditions: 11 | 12 | # The above copyright notice and this permission notice shall be included in all 13 | # copies or substantial portions of the Software. 14 | 15 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | # SOFTWARE. 22 | 23 | from __future__ import unicode_literals 24 | import os, atexit, time, signal, subprocess 25 | 26 | class Emacs(): 27 | # Requests towards Emacs are written to file "_request", while 28 | # replies from Emacs are read from file "_reply". We call Emacs 29 | # attention by erasing "_reply", and Emacs calls our attention by 30 | # erasing "_request". These rules guarantee that no file is ever 31 | # read by one side before it has been fully written by the other. 32 | # Busy waiting, with built-in delays, is used on both sides. 33 | 34 | popen = None 35 | 36 | def __init__(self): 37 | self.comm_dir = os.getcwd() 38 | self.reply_file = os.path.join(self.comm_dir, "_reply") 39 | self.req_file = os.path.join(self.comm_dir, "_request") 40 | self.cleanup() 41 | atexit.register(self.cleanup) 42 | emacs = '/usr/bin/emacsclient' 43 | if not os.path.exists(emacs): 44 | self.command = None 45 | else: 46 | self.command = (emacs, "-s", "default") 47 | self.command = self.command + ('--eval', '(pymacs-run-one-request "%s")' % self.comm_dir) 48 | 49 | def cleanup(self): 50 | if self.popen is not None: 51 | self.popen.poll() 52 | if self.popen.returncode is None: 53 | os.kill(self.popen.pid, signal.SIGINT) 54 | os.waitpid(self.popen.pid, 0) 55 | self.popen = None 56 | if os.path.exists(self.req_file): 57 | os.remove(self.req_file) 58 | if os.path.exists(self.reply_file): 59 | os.remove(self.reply_file) 60 | 61 | def receive(self): 62 | while os.path.exists(self.req_file): 63 | self.popen.poll() 64 | assert (self.popen.returncode is None) or (self.popen.returncode == 0), self.popen.returncode 65 | time.sleep(0.005) 66 | 67 | self.popen.poll() 68 | assert (self.popen.returncode is None) or (self.popen.returncode == 0), self.popen.returncode 69 | handle = open(self.reply_file) 70 | buffer = handle.read() 71 | handle.close() 72 | return unicode(buffer) 73 | 74 | def send(self, text): 75 | handle = open(self.req_file, 'w') 76 | handle.write(text.encode('ascii', 'ignore')) 77 | handle.close() 78 | if os.path.exists(self.reply_file): 79 | os.remove(self.reply_file) 80 | if self.popen is None and self.command: 81 | self.popen = subprocess.Popen(self.command, stdout=open("/dev/null", "w")) 82 | self.popen.poll() 83 | assert self.popen.returncode is None, self.popen.returncode 84 | 85 | 86 | def ask_emacs(text, printer="prin1", execbuffer=""): 87 | Emacs.services = Emacs() 88 | if printer is not None: 89 | text = '(%s %s)' % (printer, text) 90 | if Emacs.command: 91 | Emacs.services.send(text) 92 | repl = Emacs.services.receive() 93 | Emacs.services.cleanup() 94 | return repl 95 | else: 96 | return None 97 | 98 | def ask_emacs_in_buffer(cmd, bufname): 99 | return ask_emacs('(with-current-buffer (get-buffer-create %s) %s)' 100 | % (bufname, cmd)) 101 | -------------------------------------------------------------------------------- /fiddle/pymacs_request/__init__.py: -------------------------------------------------------------------------------- 1 | # MIT License 2 | 3 | # Copyright (c) 2017 Rebecca ".bx" Shapiro 4 | 5 | # Permission is hereby granted, free of charge, to any person obtaining a copy 6 | # of this software and associated documentation files (the "Software"), to deal 7 | # in the Software without restriction, including without limitation the rights 8 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | # copies of the Software, and to permit persons to whom the Software is 10 | # furnished to do so, subject to the following conditions: 11 | 12 | # The above copyright notice and this permission notice shall be included in all 13 | # copies or substantial portions of the Software. 14 | 15 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | # SOFTWARE. 22 | 23 | from __future__ import unicode_literals 24 | import os, atexit, time, signal, subprocess 25 | 26 | name = "emacs_python_pipe" 27 | 28 | class Emacs(): 29 | # Requests towards Emacs are written to file "_request", while 30 | # replies from Emacs are read from file "_reply". We call Emacs 31 | # attention by erasing "_reply", and Emacs calls our attention by 32 | # erasing "_request". These rules guarantee that no file is ever 33 | # read by one side before it has been fully written by the other. 34 | # Busy waiting, with built-in delays, is used on both sides. 35 | 36 | popen = None 37 | 38 | def __init__(self): 39 | self.comm_dir = os.getcwd() 40 | self.reply_file = os.path.join(self.comm_dir, "_reply") 41 | self.req_file = os.path.join(self.comm_dir, "_request") 42 | self.cleanup() 43 | atexit.register(self.cleanup) 44 | emacs = '/usr/bin/emacsclient' 45 | self.command = (emacs, "-s", "default") 46 | self.command = self.command + ('--eval', '(pymacs-run-one-request "%s")' % self.comm_dir) 47 | 48 | def cleanup(self): 49 | if self.popen is not None: 50 | self.popen.poll() 51 | if self.popen.returncode is None: 52 | os.kill(self.popen.pid, signal.SIGINT) 53 | os.waitpid(self.popen.pid, 0) 54 | self.popen = None 55 | if os.path.exists(self.req_file): 56 | os.remove(self.req_file) 57 | if os.path.exists(self.reply_file): 58 | os.remove(self.reply_file) 59 | 60 | def receive(self): 61 | while os.path.exists(self.req_file): 62 | self.popen.poll() 63 | assert (self.popen.returncode is None) or (self.popen.returncode == 0), self.popen.returncode 64 | time.sleep(0.005) 65 | 66 | self.popen.poll() 67 | assert (self.popen.returncode is None) or (self.popen.returncode == 0), self.popen.returncode 68 | handle = open(self.reply_file) 69 | buffer = handle.read() 70 | handle.close() 71 | return unicode(buffer) 72 | 73 | def send(self, text): 74 | handle = open(self.req_file, 'w') 75 | handle.write(text.encode('ascii', 'ignore')) 76 | handle.close() 77 | if os.path.exists(self.reply_file): 78 | os.remove(self.reply_file) 79 | if self.popen is None: 80 | self.popen = subprocess.Popen(self.command, stdout=open("/dev/null", "w")) 81 | self.popen.poll() 82 | assert self.popen.returncode is None, self.popen.returncode 83 | 84 | 85 | def ask_emacs(text, printer="prin1", execbuffer=""): 86 | Emacs.services = Emacs() 87 | if printer is not None: 88 | text = '(%s %s)' % (printer, text) 89 | Emacs.services.send(text) 90 | repl = Emacs.services.receive() 91 | Emacs.services.cleanup() 92 | return repl 93 | 94 | 95 | def get_emacs_var(name): 96 | res = ask_emacs(name)[1: -1] # strip off quotation marks 97 | if res == "nil": 98 | return None 99 | else: 100 | return res 101 | 102 | 103 | def ask_emacs_in_buffer(cmd, bufname): 104 | return ask_emacs('(with-current-buffer (get-buffer-create %s) %s)' 105 | % (bufname, cmd)) 106 | -------------------------------------------------------------------------------- /fiddle/pure_utils.py: -------------------------------------------------------------------------------- 1 | # MIT License 2 | 3 | # Copyright (c) 2017 Rebecca ".bx" Shapiro 4 | 5 | # Permission is hereby granted, free of charge, to any person obtaining a copy 6 | # of this software and associated documentation files (the "Software"), to deal 7 | # in the Software without restriction, including without limitation the rights 8 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | # copies of the Software, and to permit persons to whom the Software is 10 | # furnished to do so, subject to the following conditions: 11 | 12 | # The above copyright notice and this permission notice shall be included in all 13 | # copies or substantial portions of the Software. 14 | 15 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | # SOFTWARE. 22 | 23 | import hashlib 24 | import run_cmd 25 | import re 26 | import r2_keeper as r2 27 | shell = run_cmd.Cmd() 28 | 29 | 30 | def file_md5(filename): 31 | m = hashlib.md5() 32 | with open(filename, "rb") as f: 33 | for block in iter(lambda: f.read(65536), b""): 34 | m.update(block) 35 | return m.hexdigest() 36 | 37 | 38 | def get_entrypoint(elf): 39 | try: 40 | return r2.entrypoint(elf) 41 | except KeyError: 42 | r2.gets(elf, "s") 43 | return r2.entrypoint(elf) 44 | 45 | 46 | def get_c_function_names(elf, cc="/usr/bin/"): 47 | cmd = '%sreadelf -W -s %s | grep FUNC 2>/dev/null' % (cc, elf) 48 | output = shell.run_multiline_cmd(cmd) 49 | 50 | results = [] 51 | for l in output: 52 | cols = l.split() 53 | if len(cols) > 7: 54 | addr = cols[1] 55 | name = cols[7] 56 | results.append((name, long(addr, 16))) 57 | 58 | return results 59 | 60 | 61 | def get_image_size(image): 62 | cmd = "/usr/bin/wc -c %s" % (image) 63 | output = shell.run_cmd(cmd) 64 | output = output.split() 65 | if len(output) == 2: 66 | return long(output[0], 0) 67 | else: 68 | return -1 69 | 70 | 71 | def get_min_max_pcs(elf): 72 | headers = get_section_headers(elf) 73 | lo = float('inf') 74 | hi = 0 75 | for h in headers: 76 | if (h['size'] > 0) and (h['flags'][-1] == 'x'): # if memory mapped 77 | hstart = h['address'] 78 | hstop = hstart+h['size'] 79 | if hstart < lo: 80 | lo = hstart 81 | if hstop > hi: 82 | hi = hstop 83 | return (lo, hi) 84 | 85 | def get_section_headers(elf): 86 | ds = r2.get(elf, "iSj") 87 | index = 0 88 | headers = [] 89 | if len(ds) > 0: 90 | # flags key differs between r2 versions 91 | if "flags" in ds[0]: 92 | flags = "flags" 93 | else: 94 | flags = "perm" 95 | for d in ds: 96 | h = { 97 | "number": index, 98 | "name": d["name"], 99 | "address": d["vaddr"], 100 | "offset": d["paddr"], 101 | "size": d['vsize'], 102 | "filesize": d['size'], 103 | "flags": d[flags] 104 | } 105 | headers.append(h) 106 | index += 1 107 | return headers 108 | 109 | 110 | def get_section_location(elf, name): 111 | start = 0 112 | end = 0 113 | 114 | headers = get_section_headers(elf) 115 | for h in headers: 116 | if h['name'] == name: 117 | start = h['address'] 118 | end = start+h['size'] 119 | return (start, end) 120 | return (-1, -1) 121 | 122 | 123 | def get_symbol_location(elf, name, debug=False): 124 | s = r2.get(elf, "isj") 125 | name = u"%s" % name # convert to unicode 126 | for i in s: 127 | if i["name"] == name: 128 | if debug: 129 | print i 130 | return i["vaddr"] 131 | return -1 132 | 133 | 134 | def addr2functionname(addr, elf, debug=False): 135 | old = r2.gets(elf, "s") 136 | r2.get(elf, "s 0x%x" % addr) 137 | s = r2.get(elf, "afi") 138 | r2.get(elf, "s %s" % old) 139 | def getname(i): 140 | name = i["name"] 141 | if i.name.startswith("sym."): 142 | name = name[4:] 143 | #print "addr2fn %x " % (addr) 144 | for i in s: 145 | if len(i) > 1: 146 | print s 147 | print "%x addr func" % addr 148 | raise Exception 149 | name = getname(i) 150 | 151 | return name 152 | return "" 153 | 154 | 155 | def addr2line(addr, elf, debug=False, fn=None): 156 | if fn is None: 157 | fn = addr2functionname(addr, elf, debug) 158 | addr = get_symbol_location(elf, fn, debug) 159 | old = r2.gets(elf, "s") 160 | r2.get(elf, "s 0x%x" % addr) 161 | s = r2.gets(elf, "CL") 162 | r2.get(elf, "s %s" % old) 163 | res = s.split() 164 | d = r2.gets(elf, "pwd") 165 | if debug and res: 166 | print "addr2line %s%s:%s" % (d, res[1][2:], res[3]) 167 | if res: 168 | return "%s%s:%s" % (d, res[1][2:], res[3]) 169 | else: 170 | return "" 171 | -------------------------------------------------------------------------------- /fiddle/hw_info/bbxm/u_boot/spl/memory_map.yml: -------------------------------------------------------------------------------- 1 | regions: 2 | ROM: 3 | type: "readonly" 4 | addresses: [0x40000000, 0x4001C000] 5 | RAM: 6 | addresses: [0x40200000, 0x40210000] 7 | include_children: True 8 | type: "readonly" 9 | subregions: 10 | rom_stack: 11 | type: "stack" 12 | addresses: ["ROM_STACK_START", "ROM_STACK_END"] 13 | downloaded_image: 14 | addresses: ["ROM_STACK_END", 0x4020FFB0] 15 | subregions: 16 | downloaded_image_text: 17 | type: "readonly" 18 | addresses: ["RAM.downloaded_image.start", ".text.end"] 19 | downloaded_image_data: 20 | type: "bookkeeping" 21 | addresses: [".text.end", "__image_copy_end"] 22 | unused: 23 | type: "readonly" 24 | addresses: ["__image_copy_end", "SCRATCH_SPACE_ADDR"] 25 | scratch_space: 26 | type: "bookkeeping" 27 | addresses: ["SCRATCH_SPACE_ADDR", "OMAP5_SCRATCH_SPACE_END"] 28 | remainder: 29 | type: "readonly" 30 | addresses: ["OMAP5_SCRATCH_SPACE_END", 0x4020F000] 31 | public_stack: 32 | type: "readonly" 33 | include_children: True 34 | addresses: [0x4020F000, 0x4020FFB0] 35 | subregions: 36 | beginning: 37 | type: "readonly" 38 | addresses: [0x4020F000, 0x4020f840] 39 | cpy_clk_code: 40 | type: "future" 41 | addresses: [0x4020f840, "0x4020f840 + lowlevel_init - cpy_clk_code"] 42 | stack_rest0: 43 | addresses: ["RAM.downloaded_image.public_stack.cpy_clk_code.end", 0x4020ff20] 44 | stack_rest1: 45 | addresses: [ 0x4020ff20, "RAM.downloaded_image.end"] 46 | rest0: 47 | addresses: ["RAM.downloaded_image.end", 0x4020fffc] 48 | type: "readonly" 49 | rest1: 50 | addresses: [0x4020fffc, RAM.end] 51 | type: "readonly" 52 | 53 | RAM1: 54 | include_children: True 55 | type: "readonly" 56 | addresses: ["RAM.start", "RAM.end"] 57 | subregions: 58 | begin: 59 | type: "readonly" 60 | addresses: ["RAM.start", ".text.end"] 61 | downloaded_image_data: 62 | type: "bookkeeping" 63 | addresses: [".text.end", "__image_copy_end"] 64 | later_stack: 65 | type: "readonly" 66 | addresses: ["__image_copy_end", "0x4020f840 + lowlevel_init - cpy_clk_code"] 67 | global_data: 68 | type: "stack" 69 | addresses: ["0x4020f840 + lowlevel_init - cpy_clk_code", 0x4020ff20] 70 | end: 71 | type: "readonly" 72 | addresses: [ 0x4020ff20, "RAM.end"] 73 | 74 | 75 | SRAM: 76 | type: "readonly" 77 | addresses: [0x80000000, 0xc0000000] 78 | include_children: True 79 | subregions: 80 | bss: 81 | addresses: [0x80000000, 0x80030144] 82 | subregions: 83 | sram_test: 84 | type: "patching" 85 | addresses: [[0x80000000, 0x80000008], [0x80000400, 0x80000404]] 86 | bss_rest: 87 | addresses: "remainder" 88 | after_bss: 89 | addresses: [0x80030144, "CONFIG_SYS_TEXT_BASE - sizeof_struct_image_header"] 90 | type: "readonly" 91 | image_header: 92 | type: "future" 93 | addresses: ["CONFIG_SYS_TEXT_BASE - sizeof_struct_image_header", "CONFIG_SYS_TEXT_BASE"] 94 | image: 95 | type: "future" 96 | addresses: ["CONFIG_SYS_TEXT_BASE", "CONFIG_SYS_TEXT_BASE + main.image_size"] 97 | post_image: 98 | addresses: ["CONFIG_SYS_TEXT_BASE + main.image_size", "CONFIG_SYS_SPL_MALLOC_START"] 99 | heap: 100 | type: "readonly" 101 | addresses: ["CONFIG_SYS_SPL_MALLOC_START", "CONFIG_SYS_SPL_MALLOC_START + CONFIG_SYS_SPL_MALLOC_SIZE"] 102 | nonbss: 103 | type: "readonly" 104 | addresses: ["CONFIG_SYS_SPL_MALLOC_START + CONFIG_SYS_SPL_MALLOC_SIZE", 0xc0000000] 105 | include_children: True 106 | subregions: 107 | sram_test: 108 | type: "patching" 109 | addresses: [[0xA0000400, 0xA0000404], [0xA0000000, 0xA0000008]] 110 | rest: 111 | addresses: "remainder" 112 | 113 | Registers: 114 | csv: "regs.csv" 115 | type: "global" 116 | include_children: True 117 | missing_control_padconf0: 118 | type: "global" 119 | addresses: [0x48002150, 0x48002154] 120 | comment: "address written to by boootloader (met_muxconf_regs) must not listed in am37x hardware reference" 121 | missing_control_padconf1: 122 | type: "global" 123 | addresses: [0x48002154, 0x48002158] 124 | comment: "address written to by boootloader (met_muxconf_regs) must not listed in am37x hardware reference" 125 | missing_protection_mech_0: 126 | type: "global" 127 | addresses: [0x68010060, 0x68010068] 128 | comment: "address written to by boootloader (secure_unlock_mem) must not listed in am37x hardware reference" 129 | 130 | values: 131 | SRAM_STACK: 0x4020fffc 132 | SCRATCH_SPACE_ADDR: 0x4020E000 133 | OMAP5_SCRATCH_SPACE_END: 0x4020E030 134 | ROM_STACK_START: 0x40200000 135 | ROM_STACK_END: 0x40200800 136 | CONFIG_SYS_SPL_MALLOC_START: 0x80208000 137 | CONFIG_SYS_SPL_MALLOC_SIZE: 0x100000 138 | CONFIG_SYS_TEXT_BASE: 0x80100000 139 | sizeof_struct_image_header: 64 140 | 141 | stagename: "spl" 142 | -------------------------------------------------------------------------------- /fiddle_extra/frama_c/call_analysis.ml: -------------------------------------------------------------------------------- 1 | (* MIT License *) 2 | 3 | (* Copyright (c) 2017 Rebecca ".bx" Shapiro *) 4 | 5 | (* Permission is hereby granted, free of charge, to any person obtaining a copy *) 6 | (* of this software and associated documentation files (the "Software"), to deal *) 7 | (* in the Software without restriction, including without limitation the rights *) 8 | (* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell *) 9 | (* copies of the Software, and to permit persons to whom the Software is *) 10 | (* furnished to do so, subject to the following conditions: *) 11 | 12 | (* The above copyright notice and this permission notice shall be included in all *) 13 | (* copies or substantial portions of the Software. *) 14 | 15 | (* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR *) 16 | (* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, *) 17 | (* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE *) 18 | (* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER *) 19 | (* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, *) 20 | (* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE *) 21 | (* SOFTWARE. *) 22 | 23 | open Cil_types 24 | open Cil 25 | open Printf 26 | open Cil_types 27 | open Cil 28 | 29 | module SS = Set.Make(String) 30 | module Funcall_info = struct 31 | type funcallinfo = { 32 | lval:Cil_types.lval; 33 | exp:Cil_types.exp; 34 | lexloc:Cil_types.location; 35 | lvalloc:Locations.location; 36 | lvalfulladdr:Integer.t; 37 | instr:Cil_types.instr; 38 | min:Abstract_interp.Int.t option; 39 | max:Abstract_interp.Int.t option; 40 | } 41 | 42 | let is_instr s = 43 | match s with 44 | Instr _ -> true 45 | | _ -> false 46 | 47 | let lexloc_string info = 48 | let ({Lexing.pos_fname=f1; Lexing.pos_lnum=l1; _}, _) = info.lexloc in 49 | Printf.sprintf "%s:%d" f1 l1 50 | 51 | let get_lval info = 52 | info.lval 53 | 54 | let get_lvalloc info = 55 | info.lvalloc 56 | 57 | 58 | let instr_string info = 59 | let s = Printer.pp_instr Format.str_formatter info.instr in 60 | Format.flush_str_formatter s 61 | 62 | let eval_lval lval kinstr = 63 | !Db.Value.lval_to_loc ~with_alarms:CilE.warn_none_mode kinstr lval 64 | 65 | let lval_string info = 66 | let s = Printer.pp_lval Format.str_formatter info.lval in 67 | Format.flush_str_formatter s 68 | 69 | let form_callstack_string cs = 70 | List.fold_right (fun c s -> (match c with (f, _) -> 71 | s ^ "->" ^ (Ast_info.Function.get_name f.fundec))) cs "" 72 | 73 | let build_callinfo s kinstr = 74 | if (Db.Value.is_computed()) && (Db.Value.is_reachable_stmt s) then 75 | (match Db.Value.get_stmt_state_callstack ~after:true s with 76 | None -> SS.empty 77 | | Some(state) -> Value_types.Callstack.Hashtbl.fold 78 | (fun cs state r -> 79 | (if Cvalue.Model.is_reachable state then 80 | SS.add (form_callstack_string cs) r 81 | else 82 | SS.empty)) 83 | state SS.empty) 84 | else 85 | SS.empty 86 | end 87 | 88 | 89 | let help_msg = "static function call stack analyzer" 90 | 91 | module Self = Plugin.Register 92 | (struct 93 | let name = "static function call stack analyzer" 94 | let shortname = "call" 95 | let help = help_msg 96 | end) 97 | 98 | module Enabled = Self.False 99 | (struct 100 | let option_name = "-call" 101 | let help = "when on (off by default), " ^ help_msg 102 | end) 103 | 104 | module Output_file = Self.String 105 | (struct 106 | let option_name = "-call-output" 107 | let default = "-" 108 | let arg_name = "output_file" 109 | let help = 110 | "file where the message is output (default: console)" 111 | end) 112 | 113 | let print_msg = 114 | object (self : 'self) 115 | method print msg stream = 116 | (match stream with 117 | Some(s) -> Printf.fprintf s "%s\n" msg; Self.result "%s" msg 118 | | None -> Self.result "%s" msg) 119 | 120 | method close stream = 121 | (match stream with 122 | Some(s) -> close_out s 123 | | None -> ()) 124 | 125 | end 126 | 127 | class call_visitor kf = 128 | object (self) 129 | inherit Visitor.frama_c_inplace 130 | 131 | val mutable callstacks = SS.empty 132 | method! vstmt stmt = callstacks <- SS.union (Funcall_info.build_callinfo stmt self#current_kinstr) callstacks; 133 | Cil.DoChildren; 134 | method result = callstacks 135 | end 136 | 137 | let run_call () = 138 | if Enabled.get() then 139 | let stream = 140 | if (Output_file.is_default()) then 141 | None 142 | else 143 | Some (open_out (Output_file.get())) 144 | in 145 | Globals.Functions.iter (fun name -> 146 | (try let d = (Kernel_function.get_definition name) in 147 | (let calls = new call_visitor name in 148 | ignore (Visitor.visitFramacFunction (calls :> Visitor.frama_c_visitor) d); 149 | let res = calls#result in SS.iter (fun s -> print_msg#print (String.sub s 2 ((String.length s) - 2 )) stream) res) 150 | with _ -> ())); 151 | print_msg#close stream 152 | 153 | 154 | 155 | let () = Db.Main.extend run_call 156 | -------------------------------------------------------------------------------- /fiddle/configs/defaults.cfg: -------------------------------------------------------------------------------- 1 | [Main] 2 | name = "default" 3 | root = '~/test_utils/{Main.name}' 4 | cc = '/usr/bin/' 5 | hardwareclass = '_hw' 6 | hw_info_path = "{Main.test_suite_dir}/fiddle/hw_info" 7 | test_data_path = "{Main.root}/results" 8 | 9 | [HardwareClass._hw] 10 | hw_info_path = "{Main.hw_info_path}/{HardwareClass._hw.name}" 11 | hosts = ['_plain'] 12 | addr_range = [0x00000000, 0xffffffffffffffff] 13 | 14 | [Target] 15 | software = "_test" 16 | default_stages = ['_single'] 17 | 18 | [HostConfig] 19 | [HostConfig._plain] 20 | tracing_methods = ['breakpoint', 'calltrace', 'enforce'] 21 | 22 | [Software] 23 | [Software._test] 24 | root = "{Main.root}/{Software._test.name}" 25 | build = true 26 | clean = "make clean" 27 | build_prepare = "./configure" 28 | build_cmd = "make" 29 | compiler = "/usr/bin/gcc" 30 | [Software._test.Files.target] 31 | relative_path = "test" 32 | type = "image" 33 | 34 | [Software.hook_write] 35 | build = false 36 | root = '{Main.test_suite_dir}/fiddle_gdb' 37 | binary = '{Software.hook_write.root}/hook_write.py' 38 | 39 | 40 | [Software.gdb] 41 | build = false 42 | root = '~/software/gcc-linaro-5.3-2016.02-x86_64_arm-eabi/bin' 43 | binary = '{Software.gdb.root}/arm-eabi-gdb' 44 | 45 | [Software.calltrace] 46 | build = false 47 | root = '{Main.test_suite_dir}' 48 | binary = '{Software.calltrace.root}/fiddle_gdb/calltrace.py' 49 | 50 | [Software.unicorn] 51 | build = false 52 | root = '{Main.test_suite_dir}' 53 | binary = '{Software.calltrace.root}/fiddle_gdb/unicorn_trace.py' 54 | 55 | [Software.enforce] 56 | build = false 57 | root = '{Main.test_suite_dir}' 58 | binary = '{Software.enforce.root}/fiddle_gdb/enforce.py' 59 | 60 | [Software.gdb_tools] 61 | build = false 62 | root = '{Main.test_suite_dir}' 63 | binary = '{Software.gdb_tools.root}/fiddle_gdb/gdb_tools.py' 64 | 65 | [Software.gdb_tools.GDBConfig] 66 | commands = ["python execfile(\"{Software.gdb_tools.binary}\")", 67 | "gdb_tools dir {runtime.python_path}", 68 | "gdb_tools update_path", 69 | ] 70 | 71 | 72 | [TargetStage] 73 | [TargetStage._single] 74 | elf = '{Software._test.Files.target.relative_path}' 75 | minpc = -1 76 | maxpc = -1 77 | exitpc = -1 78 | entrypoint = -1 79 | image_size = -1 80 | 81 | 82 | [TraceMethod] 83 | run = "I_CONF={config} {Software.gdb.binary} -ex 'set environment I_CONF={config}' {TraceMethod.gdb_commands} -ex 'gdb_tools go -p' -ex 'c' -ex 'monitor quit' -ex 'monitor exit' -ex 'q' && true" 84 | 85 | [TraceMethod.breakpoint] 86 | software = ["gdb_tools", "qemu", "hook_write"] 87 | 88 | 89 | [TraceMethod.breakpoint.GDBConfig] 90 | commands = ["gdb_tools plugin {Software.hook_write.binary}", 91 | "gdb_tools stages {runtime.enabled_stagenames}", 92 | "gdb_tools enable_policy {runtime.stage}", 93 | "hookwrite test_instance {runtime.instance_id}", 94 | "hookwrite test_trace {runtime.trace.id}", 95 | "hookwrite kill",] 96 | 97 | [TraceMethod.breakpoint.Files.db] 98 | type = "target" 99 | relative_path = "trace.h5" 100 | global_name = "runtime.trace.db.{runtime.stage}" 101 | 102 | [TraceMethod.calltrace] 103 | software = ["gdb", "gdb_tools", "calltrace"] 104 | 105 | [TraceMethod.calltrace.Files.org] 106 | type = "target" 107 | relative_path = "calltrace.org" 108 | 109 | #[TraceMethod.calltrace.Files.dep] 110 | #type = "file_dep" 111 | #subtype = "stage_dependent" 112 | #path = "{static_analysis.db}" 113 | 114 | [TraceMethod.calltrace.GDBConfig] 115 | commands = ["gdb_tools plugin {Software.calltrace.binary}", 116 | "calltrace test_instance {runtime.instance_id}", 117 | "calltrace test_trace {runtime.trace.id}", 118 | 'calltrace no_recursion {runtime.trace.host_configs.calltrace_no_recursion}', 119 | 'calltrace blacklist {runtime.trace.host_configs.calltrace_blacklist_{runtime.stage}}', 120 | 'calltrace stage_log {runtime.stage} {TraceMethod.calltrace.Files.org.{runtime.stage}}', 121 | 'calltrace sourceinfo', 122 | 'calltrace kill' 123 | ] 124 | 125 | [TraceMethod.enforce] 126 | software = ["gdb_tools", "enforce"] 127 | 128 | [TraceMethod.unicorn] 129 | software = ["gdb", "gdb_tools", "unicorn"] 130 | 131 | [TraceMethod.unicorn.Files.db] 132 | type = "target" 133 | relative_path = "trace.h5" 134 | global_name = "runtime.trace.db.{runtime.stage}" 135 | 136 | [TraceMethod.unicorn.GDBConfig] 137 | commands = ["gdb_tools plugin {Software.unicorn.binary}", 138 | "gdb_tools enable_policy {runtime.enabled_stagenames}", 139 | "unicorn test_instance {runtime.instance_id}", 140 | "unicorn test_trace {runtime.trace.id}", 141 | 'unicorn no_run', 142 | 'unicorn kill' 143 | ] 144 | 145 | 146 | 147 | [PostProcess.consolidate_writes] 148 | function = "_histogram" 149 | supported_traces = ["breakpoint", "framac"] 150 | 151 | [PostProcess.browse_db] 152 | function = "_browse_db" 153 | supported_traces = ["breakpoint", "framac", "watchpoint", "unicorn"] 154 | 155 | 156 | [PostProcess.policy_check] 157 | function = "_policy_check" 158 | supported_traces = ["breakpoint", "framac"] 159 | 160 | [PostProcess.consolidate_writes.Files.el_file] 161 | relative_path = "substages.el" 162 | type = "target" 163 | 164 | [PostProcess.consolidate_writes.Files.range_txt] 165 | relative_path = "write_range_info.txt" 166 | type = "target" 167 | 168 | 169 | 170 | [PostProcess.consolidate_writes.Files.range_csv] 171 | relative_path = "write_range_info.csv" 172 | type = "target" 173 | 174 | 175 | [PostProcess.consolidate_writes.Files.fn_lists] 176 | relative_path = "fn_lists" 177 | type = "target" 178 | 179 | [PostProcess.process_watchpoints] 180 | function = "_watchpoints" 181 | [PostProcess.process_watchpoints.Files.db] 182 | type = "output" 183 | path = "{runtime.trace.db.{runtime.stage}}" 184 | 185 | 186 | [PostProcess.noop] 187 | 188 | -------------------------------------------------------------------------------- /fiddle_gdb/enforce.py: -------------------------------------------------------------------------------- 1 | # MIT License 2 | 3 | # Copyright (c) 2017 Rebecca ".bx" Shapiro 4 | 5 | # Permission is hereby granted, free of charge, to any person obtaining a copy 6 | # of this software and associated documentation files (the "Software"), to deal 7 | # in the Software without restriction, including without limitation the rights 8 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | # copies of the Software, and to permit persons to whom the Software is 10 | # furnished to do so, subject to the following conditions: 11 | 12 | # The above copyright notice and this permission notice shall be included in all 13 | # copies or substantial portions of the Software. 14 | 15 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | # SOFTWARE. 22 | 23 | import gdb 24 | import sys 25 | import os 26 | import signal 27 | from fiddle.memory_tree import intervaltree 28 | import gdb_tools 29 | import substage 30 | import db_info 31 | 32 | 33 | # def int_repr(self): 34 | # return "({0:08X}, {1:08X})".format(self.begin, self.end) 35 | 36 | 37 | # intervaltree.Interval.__str__ = int_repr 38 | # intervaltree.Interval.__repr__ = int_repr 39 | do_halt = False 40 | allowed_writes = {} 41 | now = True 42 | check_inline = now 43 | 44 | 45 | class CloseSubstageDB(): 46 | def __init__(self, db): 47 | self.db 48 | global now 49 | if now: 50 | self.do() 51 | 52 | def do(self): 53 | self.db.close_dbs() 54 | 55 | def __call__(self): 56 | global now 57 | if not now: 58 | self.do() 59 | 60 | 61 | class CheckWrite(): 62 | def __init__(self, stage, name, substagenum, pc, 63 | relocated, startdest, enddest): 64 | self.name = name 65 | self.stage = stage 66 | self.pc = pc 67 | self.num = substagenum 68 | self.start = startdest 69 | self.end = enddest 70 | self.relocated = relocated 71 | global do_halt 72 | global check_inline 73 | if check_inline or do_halt: 74 | self.do() 75 | 76 | def do(self): 77 | global allowed_writes 78 | a = allowed_writes[self.stage.stagename][self.num] 79 | if not len(a.search(self.start, self.end)) == 1: 80 | gdb.write("#CAUGHT INVALID WRITE pc %x (%x-%x) substage %s (%s)\n" % (self.pc, 81 | self.start, 82 | self.end, 83 | self.name, 84 | self.num), 85 | gdb.STDOUT) 86 | global do_halt 87 | if do_halt: 88 | pid = gdb.selected_inferior().pid 89 | os.kill(pid, signal.SIGINT) 90 | 91 | def __call__(self): 92 | global check_inline 93 | global do_halt 94 | if not (check_inline or do_halt): 95 | self.do() 96 | 97 | 98 | class Enforce(gdb_tools.GDBPlugin): 99 | 100 | def __init__(self): 101 | bp_hooks = {'WriteBreak': self.write_stophook, 102 | 'LongWriteBreak': self.longwrite_stophook, 103 | 'SubstageEntryBreak': self.substage_stophook} 104 | parser_options = [ 105 | gdb_tools.GDBPluginParser("do_halt"), 106 | gdb_tools.GDBPluginParser("check_inline")] 107 | 108 | gdb_tools.GDBPlugin.__init__(self, "enforce", 109 | f_hook=self.finalize_hook, 110 | bp_hooks=bp_hooks, 111 | calculate_write_dst=True, 112 | parser_args=parser_options) 113 | 114 | def check_inline(self, args): 115 | global check_inline 116 | check_inline = True 117 | 118 | def do_halt(self, args): 119 | global do_halt 120 | do_halt = True 121 | 122 | def finalize_hook(self, args): 123 | substages = False 124 | for s in self.controller._stages.itervalues(): 125 | if s.substages: 126 | substages = True 127 | break 128 | if not substages: 129 | self.controller.gdb_print('No substages to set, do not know what to enforce', 130 | self.name) 131 | return 132 | global allowed_writes 133 | for s in [_s for _s in self.controller._stages.itervalues() 134 | if _s.stage in self.controller.stage_order]: 135 | name = s.stage.stagename 136 | # policy = self.controller.policy_file 137 | ss = self.controller._stages[name].substages_entrypoints 138 | i = db_info.get(s.stage) 139 | allowed_writes[name] = {} 140 | 141 | for n in range(0, len(ss)): 142 | allowed_writes[name][n] = i.allowed_substage_writes(n) 143 | 144 | def write_stophook(self, bp, ret): 145 | return self.longwrite_stophook(bp, ret) 146 | 147 | def substage_stophook(self, bp, ret): 148 | bp.msg("started substage %s (%s)\n" % (bp.substagenum, 149 | bp.fnname)) 150 | if not bp.controller.current_substage >= (bp.substagenum - 1): 151 | bp.msg("Uh oh! Entrypoint for substage number " 152 | "%s (%s) triggered at wrong time\n" % (bp.substagenum, 153 | bp.fnname)) 154 | return True 155 | return False 156 | 157 | def longwrite_stophook(self, bp, ret): 158 | substagenum = bp.controller.current_substage 159 | gdb.post_event(CheckWrite(bp.stage, 160 | bp.controller.current_substage_name, 161 | substagenum, 162 | bp.writeinfo['pc'], 163 | bp.relocated, 164 | bp.writeinfo['start'], 165 | bp.writeinfo['end'])) 166 | return ret 167 | 168 | 169 | plugin_config = Enforce() 170 | -------------------------------------------------------------------------------- /fiddle/qemu_raw_trace.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # Pretty-printer for simple trace backend binary trace files 4 | # 5 | # Copyright IBM, Corp. 2010 6 | # Modified by bx (bx@cs.dartmouth.edu) 2017 7 | # This work is licensed under the terms of the GNU GPL, version 2. t 8 | 9 | import struct 10 | import inspect 11 | import sys 12 | import os 13 | from config import Main 14 | qemu = Main.object_config_lookup("Software", "qemu") 15 | sys.path.append(os.path.join(qemu.root, "scripts")) 16 | try: 17 | from tracetool import _read_events 18 | from tracetool import Event 19 | from tracetool.backend.simple import is_string 20 | except ImportError as e: 21 | sys.stderr.write("QEMU's tracetool is required to analyze QEMU watchpoint events but was not found in your python path.\n") 22 | sys.stderr.write("Please download tracetool.py from %s and copy it to one of the following directories in your path:" % "https://raw.githubusercontent.com/qemu/qemu/86b5aacfb972ffe0fa5fac6028e9f0bc61050dda/scripts/tracetool.py\n") 23 | sys.stderr.write("%s\n" % sys.path) 24 | raise e 25 | import db_info 26 | 27 | header_event_id = 0xffffffffffffffff 28 | header_magic = 0xf2b177cb0aa429b4 29 | dropped_event_id = 0xfffffffffffffffe 30 | 31 | log_header_fmt = '=QQQ' 32 | rec_header_fmt = '=QQII' 33 | 34 | 35 | def read_header(fobj, hfmt): 36 | '''Read a trace record header''' 37 | hlen = struct.calcsize(hfmt) 38 | hdr = fobj.read(hlen) 39 | if len(hdr) != hlen: 40 | return None 41 | return struct.unpack(hfmt, hdr) 42 | 43 | 44 | def get_record(edict, rechdr, fobj): 45 | """Deserialize a trace record from a file 46 | into a tuple (event_num, timestamp, pid, arg1, ..., arg6).""" 47 | if rechdr is None: 48 | return None 49 | rec = (rechdr[0], rechdr[1], rechdr[3]) 50 | if rechdr[0] != dropped_event_id: 51 | event_id = rechdr[0] 52 | event = edict[event_id] 53 | for type, name in event.args: 54 | if is_string(type): 55 | l = fobj.read(4) 56 | (len,) = struct.unpack('=L', l) 57 | s = fobj.read(len) 58 | rec = rec + (s,) 59 | else: 60 | (value,) = struct.unpack('=Q', fobj.read(8)) 61 | rec = rec + (value,) 62 | else: 63 | (value,) = struct.unpack('=Q', fobj.read(8)) 64 | rec = rec + (value,) 65 | return rec 66 | 67 | 68 | def read_record(edict, fobj): 69 | """Deserialize a trace record from a file into a tuple 70 | (event_num, timestamp, pid, arg1, ..., arg6).""" 71 | rechdr = read_header(fobj, rec_header_fmt) 72 | return get_record(edict, rechdr, fobj) # return tuple of record elements 73 | 74 | 75 | def read_trace_header(fobj): 76 | """Read and verify trace file header""" 77 | header = read_header(fobj, log_header_fmt) 78 | if header is None or \ 79 | header[0] != header_event_id or \ 80 | header[1] != header_magic: 81 | raise ValueError('Not a valid trace file!') 82 | 83 | log_version = header[2] 84 | if log_version not in [0, 2, 3]: 85 | raise ValueError('Unknown version of tracelog format!') 86 | if log_version != 3: 87 | raise ValueError('Log format %d not supported with this QEMU release!' 88 | % log_version) 89 | 90 | 91 | def read_trace_records(edict, fobj): 92 | """Deserialize trace records from a file, yielding record tuples 93 | (event_num, timestamp, pid, arg1, ..., arg6).""" 94 | while True: 95 | rec = read_record(edict, fobj) 96 | if rec is None: 97 | break 98 | 99 | yield rec 100 | 101 | 102 | class Analyzer(object): 103 | """A trace file analyzer which processes trace records. 104 | 105 | An analyzer can be passed to run() or process(). The begin() method is 106 | invoked, then each trace record is processed, and finally the end() method 107 | is invoked. 108 | 109 | If a method matching a trace event name exists, it is invoked to process 110 | that trace record. Otherwise the catchall() method is invoked.""" 111 | 112 | def begin(self): 113 | """Called at the start of the trace.""" 114 | print() 115 | 116 | def catchall(self, event, rec, db): 117 | """Called if no specific method for processing a trace event has been found.""" 118 | pass 119 | 120 | def end(self): 121 | """Called at the end of the trace.""" 122 | pass 123 | 124 | 125 | class Formatter(Analyzer): 126 | def __init__(self): 127 | self.last_timestamp = None 128 | 129 | def catchall(self, event, rec, stage): 130 | timestamp = rec[1] 131 | if self.last_timestamp is None: 132 | self.last_timestamp = timestamp 133 | self.last_timestamp = timestamp 134 | 135 | i = 3 136 | pid = -1 137 | size = -1 138 | addr = -1 139 | pc = -1 140 | lr = -1 141 | cpsr = -1 142 | for t, n in event.args: 143 | if n == 'pid': 144 | pid = rec[i] 145 | elif n == 'size': 146 | size = rec[i] 147 | elif n == 'addr': 148 | addr = rec[i] 149 | elif n == 'pc': 150 | pc = rec[i] 151 | elif n == 'lr': 152 | lr = rec[i] 153 | elif n == 'cpsr': 154 | cpsr = rec[i] 155 | i = i+1 156 | db_info.get(stage).add_trace_write_entry(timestamp, pid, size, addr, pc, lr, cpsr) 157 | 158 | 159 | def process(events, log, analyzer, read_header, stage): 160 | 161 | """Invoke an analyzer on each event in a log.""" 162 | if isinstance(events, str): 163 | events = _read_events(open(events, 'r')) 164 | if isinstance(log, str): 165 | log = open(log, 'rb') 166 | 167 | if read_header: 168 | read_trace_header(log) 169 | 170 | dropped_event = Event.build("Dropped_Event(uint64_t num_events_dropped)") 171 | edict = {dropped_event_id: dropped_event} 172 | 173 | for num, event in enumerate(events): 174 | edict[num] = event 175 | 176 | def build_fn(analyzer, event): 177 | if isinstance(event, str): 178 | return analyzer.catchall 179 | 180 | fn = getattr(analyzer, event.name, None) 181 | if fn is None: 182 | return analyzer.catchall 183 | 184 | event_argcount = len(event.args) 185 | fn_argcount = len(inspect.getargspec(fn)[0]) - 1 186 | if fn_argcount == event_argcount + 1: 187 | # Include timestamp as first argument 188 | return lambda _, rec: fn(*((rec[1:2],) + rec[3:3 + event_argcount])) 189 | elif fn_argcount == event_argcount + 2: 190 | # Include timestamp and pid 191 | return lambda _, rec: fn(*rec[1:3 + event_argcount]) 192 | else: 193 | # Just arguments, no timestamp or pid 194 | return lambda _, rec: fn(*rec[3:3 + event_argcount]) 195 | 196 | analyzer.begin() 197 | fn_cache = {} 198 | db_info.create(stage, "tracedb") 199 | for rec in read_trace_records(edict, log): 200 | event_num = rec[0] 201 | event = edict[event_num] 202 | if event_num not in fn_cache: 203 | fn_cache[event_num] = build_fn(analyzer, event) 204 | fn_cache[event_num](event, rec, stage) 205 | db_info.get(stage).flush_tracedb() 206 | 207 | 208 | def process_and_import(events, rawtrace, stage): 209 | read_header = True 210 | events = _read_events(open(events, 'r')) 211 | process(events, rawtrace, Formatter(), read_header, stage) 212 | -------------------------------------------------------------------------------- /fiddle_gdb/hook_write.py: -------------------------------------------------------------------------------- 1 | # MIT License 2 | 3 | # Copyright (c) 2017 Rebecca ".bx" Shapiro 4 | 5 | # Permission is hereby granted, free of charge, to any person obtaining a copy 6 | # of this software and associated documentation files (the "Software"), to deal 7 | # in the Software without restriction, including without limitation the rights 8 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | # copies of the Software, and to permit persons to whom the Software is 10 | # furnished to do so, subject to the following conditions: 11 | 12 | # The above copyright notice and this permission notice shall be included in all 13 | # copies or substantial portions of the Software. 14 | 15 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | # SOFTWARE. 22 | 23 | import gdb 24 | import time 25 | import os 26 | import sys 27 | import gdb_tools 28 | from gdb_tools import * 29 | import db_info 30 | 31 | now = False 32 | db_written = False 33 | start = time.time() 34 | 35 | class WriteLog(): 36 | def __init__(self, msg): 37 | self.message = msg 38 | global now 39 | if now: 40 | self.do() 41 | 42 | def do(self): 43 | gdb.write(self.message, gdb.STDLOG) 44 | 45 | def __call__(self): 46 | global now 47 | if not now: 48 | self.do() 49 | 50 | 51 | class FlushDatabase(): 52 | def __init__(self, stage, for_now=False): 53 | self.stage = stage 54 | self.called = False 55 | global now 56 | if now or for_now: 57 | self.do() 58 | 59 | def do(self): 60 | self.called = True 61 | global db_written 62 | if db_written: 63 | return 64 | global start 65 | gdb.flush() 66 | db_info.get(self.stage).flush_tracedb() 67 | stop = time.time() 68 | gdb.write(".. finished in %f minutes\n" % ((stop-start)/60), gdb.STDOUT) 69 | db_written = True 70 | 71 | def __call__(self): 72 | if not self.called: 73 | self.do() 74 | 75 | 76 | class WriteDatabase(): 77 | def __init__(self, time, pid, size, dest, pc, lr, cpsr, 78 | origpc, stage, substage, substage_name, 79 | call_index=0, doit=False): 80 | self.time = time 81 | self.pid = pid 82 | self.size = size 83 | self.dest = dest 84 | self.pc = pc 85 | self.lr = lr 86 | self.cpsr = cpsr 87 | self.origpc = origpc 88 | self.num = substage 89 | self.substage_name = substage_name 90 | self.stage = stage 91 | self.cc = call_index 92 | global now 93 | if now or doit: 94 | self.do() 95 | 96 | def do(self): 97 | db_info.get(self.stage).add_trace_write_entry(self.time, self.pid, 98 | self.size, self.dest, 99 | self.pc, self.lr, 100 | self.cpsr, 101 | self.cc, 102 | self.num) 103 | if self.size < 0: 104 | end = self.dest 105 | start = self.dest + self.size 106 | else: 107 | start = self.dest 108 | end = self.dest + self.size 109 | db_info.get(self.stage).update_trace_writes('', self.pc, start, end, 110 | self.stage, 111 | self.origpc, self.num) 112 | 113 | def __call__(self): 114 | global now 115 | if not now: 116 | self.do() 117 | 118 | 119 | class HookWrite(gdb_tools.GDBPlugin): 120 | def __init__(self): 121 | bp_hooks = {'WriteBreak': self.write_stophook, 122 | 'LongwriteBreak': self.longwrite_stophook, 123 | 'StageEndBreak': self.endstop_hook} 124 | parser_options = [ 125 | gdb_tools.GDBPluginParser("flushall"), 126 | gdb_tools.GDBPluginParser("stage", ["stagename"]) 127 | ] 128 | gdb_tools.GDBPlugin.__init__(self, "hookwrite", bp_hooks, 129 | f_hook=self.f_hook, 130 | calculate_write_dst=True, 131 | exit_hook=self._exit_hook, 132 | parser_args=parser_options) 133 | 134 | def f_hook(self, args): 135 | for s in self.controller.stage_order: 136 | db_info.create(s, "tracedb") 137 | 138 | def write_stophook(self, bp, ret): 139 | self.process_write(bp.writeinfo, 140 | bp.relocated, 141 | bp.stage, 142 | bp.controller.current_substage, 143 | bp.controller.current_substage_name) 144 | return False 145 | 146 | def longwrite_stophook(self, bp, ret): 147 | # plugin = bp.controller.get_plugin(self.name) 148 | # calculate first and last write addresses 149 | start = bp.writeinfo['start'] 150 | end = bp.writeinfo['end'] 151 | writepc = bp.writeinfo['pc'] 152 | controller = bp.controller 153 | num = controller.current_substage 154 | name = controller.current_substage_name 155 | lr = bp.controller.get_reg_value('lr') 156 | cpsr = bp.controller.get_reg_value('cpsr') 157 | pid = 2 158 | # gdb.post_event(WriteLog("\n<%x longwrite...\n" % writepc)) 159 | if bp.relocated > 0: 160 | pid = 3 161 | for i in range(start, end, bp.writesize): 162 | waddr = i 163 | if bp.inplace: 164 | waddr = start 165 | 166 | self.dowriteinfo(waddr, bp.writesize, writepc, 167 | lr, cpsr, pid, writepc - bp.relocated, 168 | bp.stage, num, name) 169 | # gdb.post_event(WriteLog(">\n")) 170 | return False 171 | 172 | def stage_finish(self, now=False): 173 | fd = FlushDatabase(self.controller.current_stage) 174 | gdb.flush() 175 | if now: 176 | fd() 177 | else: 178 | gdb.post_event(fd) 179 | 180 | def _exit_hook(self, event): 181 | self.stage_finish(True) 182 | 183 | def endstop_hook(self, bp, ret): 184 | self.stage_finish() 185 | return True 186 | 187 | def flushall(self, args): 188 | gdb.post_event(FlushDatabase(self.controller.current_stage, now=True)) 189 | 190 | def process_write(self, writeinfo, relocated, stage, substage, name): 191 | pc = writeinfo['pc'] 192 | cpsr = writeinfo["cpsr"] 193 | lr = self.controller.get_reg_value('lr') 194 | size = writeinfo['end'] - writeinfo['start'] 195 | dst = writeinfo['start'] 196 | pid = 0 197 | if relocated > 0: 198 | pid = 1 199 | self.dowriteinfo(dst, size, pc, lr, cpsr, pid, pc - relocated, 200 | stage, substage, name) 201 | 202 | def dowriteinfo(self, writedst, size, pc, lr, cpsr, pid, origpc, 203 | stage, substage, name): 204 | if hasattr(self.controller, "call_count"): 205 | ccount = self.controller.call_count 206 | else: 207 | ccount = 0 208 | 209 | gdb.post_event(WriteDatabase(time.time(), 210 | pid, size, writedst, pc, lr, 211 | cpsr, origpc, stage, substage, 212 | name, ccount)) 213 | 214 | 215 | plugin_config = HookWrite() 216 | -------------------------------------------------------------------------------- /fiddle_extra/fiddle_plugin.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python2 2 | # MIT License 3 | 4 | # Copyright (c) 2017 Rebecca ".bx" Shapiro 5 | 6 | # Permission is hereby granted, free of charge, to any person obtaining a copy 7 | # of this software and associated documentation files (the "Software"), to deal 8 | # in the Software without restriction, including without limitation the rights 9 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | # copies of the Software, and to permit persons to whom the Software is 11 | # furnished to do so, subject to the following conditions: 12 | 13 | # The above copyright notice and this permission notice shall be included in all 14 | # copies or substantial portions of the Software. 15 | 16 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | # SOFTWARE. 23 | from fiddle import process_args 24 | from enum import Enum 25 | from fiddle.config import ConfigFile 26 | from fiddle.config import Main 27 | from fiddle import config 28 | import os 29 | 30 | 31 | _plugin_type = Enum("PluginType", 32 | ["PostProcess"]) 33 | 34 | 35 | class PluginResultFile(): 36 | file_type = Enum("PluginFile", 37 | ["target", "target_unique", "log", 38 | "config", "file_dep"]) 39 | 40 | def __init__(self, name, 41 | kind, plugin, 42 | default_rel_path=None, 43 | from_arg=False, 44 | shortened=None, 45 | parser_kws=None, 46 | stage_dependent=False): 47 | if default_rel_path is None: 48 | default_rel_path = "%s-file" % name 49 | self.default_rel_path = default_rel_path 50 | self.plugin = plugin 51 | self.name = name 52 | self.type_enum = kind 53 | self.type = kind.name 54 | global _plugin_type 55 | if self.plugin.type_enum is _plugin_type.PostProcess and \ 56 | self.type_enum in [self.file_type.target, 57 | self.file_type.target_unique]: 58 | self.stage_dependent = True 59 | else: 60 | self.stage_dependent = stage_dependent 61 | self.c_file = None 62 | self.registered = False 63 | self.shortened = shortened 64 | self.from_arg = from_arg 65 | self.arg_name = None 66 | self.parser_kws = parser_kws 67 | self._setup_parser_info() 68 | 69 | def full_path(self, stage=None): 70 | fs = getattr(getattr(Main.raw, self.plugin.type), self.plugin.name).Files 71 | raw = getattr(fs, self.name) 72 | if raw.stage_dependent: 73 | return getattr(raw, stage.stagename) 74 | else: 75 | return raw.path 76 | 77 | def relative_path(self, args): 78 | if self.from_arg: 79 | if self.from_arg is True: 80 | n = self.name 81 | else: 82 | n = self.from_arg 83 | return getattr(args, n) 84 | else: 85 | return self.default_rel_path 86 | 87 | def _setup_parser_info(self): 88 | if self.from_arg: 89 | if self.from_arg is not True: 90 | self.arg_name = "--" + self.from_arg 91 | else: 92 | self.arg_name = "--" + self.name 93 | 94 | kws = { 95 | "action": "store", 96 | "default": self.default_rel_path 97 | } 98 | if self.parser_kws is not None: 99 | self.parser_kws.update(kws) 100 | else: 101 | self.parser_kws = kws 102 | 103 | def to_obj_kws(self, args): 104 | kws = {} 105 | for i in ["type", "name", "stage_dependent"]: 106 | kws[i] = getattr(self, i) 107 | kws["relative_path"] = self.relative_path(args) 108 | return {self.name: kws} 109 | 110 | def register(self): 111 | if self.registered: 112 | return 113 | global _plugin_type 114 | if self.plugin.type_enum == _plugin_type.PostProcess: 115 | self.registered = True 116 | Main._set_generic_config() 117 | 118 | def parser_info(self): 119 | if not self.from_arg: 120 | raise Exception("Plugin %s file %s info is not obtained from an argument" % 121 | (self.plugin.name, self.name)) 122 | names = [] if not self.shortened else [self.shortened] 123 | names.append(self.arg_name) 124 | return (names, self.parser_kws) 125 | 126 | 127 | class FiddlePlugin(): 128 | plugin_type = _plugin_type 129 | 130 | def __init__(self, name, 131 | arg_parsers=[], 132 | kind=_plugin_type.PostProcess, 133 | files=[], 134 | software=[], 135 | supported_traces=Main.object_config_lookup("TraceMethod")): 136 | self.name = name 137 | if kind == PluginResultFile.file_type.target_unique: 138 | self.unique = True 139 | kind = PluginResultFile.file_type.target 140 | self.type_enum = kind 141 | self.type = kind.name 142 | self.software = software 143 | self.supported_traces = supported_traces 144 | self.args = None 145 | self.files = {f.name: f for f in files} 146 | self.unique = False 147 | self.config_obj = None 148 | self.parser = None 149 | self.task_mgr = None 150 | self.arg_parsers = arg_parsers 151 | 152 | def setup(self): 153 | file_args = map(lambda x: x.parser_info(), 154 | filter(lambda x: x.from_arg, 155 | self.files.itervalues())) 156 | self.arg_parsers.extend(file_args) 157 | 158 | self.parser = process_args.FiddleArgParser("F%splugin" % 159 | (self.name + " "), 160 | self, 161 | self.arg_parsers) 162 | self.args = self.parser.args 163 | self.task_mgr = self.parser.task_manager() 164 | if self.type_enum is self.plugin_type.PostProcess: 165 | self.task_mgr.postprocess_trace() 166 | else: 167 | raise Exception("unsupported plugin type %s" % self.type) 168 | 169 | def add_files(self, files): 170 | self.files.update({f.name: f for f in files}) 171 | 172 | def get_file_path(self, filename, stage=None): 173 | return self.files[filename].full_path(stage) 174 | 175 | def get_setup_tasks(self, mgr): 176 | tasks = [] 177 | d = Main.raw.runtime.trace.data_dir 178 | dstdir = os.path.join(d, "plugin_data", self.name) 179 | tasks.append(mgr._mkdir(dstdir)) 180 | tasks.extend(mgr.import_files(self.config_obj, 181 | getattr(Main.raw.PostProcess, 182 | self.name), 183 | dstdir, 184 | output_files=True)) 185 | return tasks 186 | 187 | def setup_config_obj(self): 188 | kws = {"name": self.name, "Files": {}} 189 | for f in self.files.itervalues(): 190 | kws["Files"].update(f.to_obj_kws(self.args)) 191 | if self.type_enum == self.plugin_type.PostProcess: 192 | self.config_obj = config.configtypes["PostProcess"](kws, 193 | self.name) 194 | for i in ["software", "supported_traces"]: 195 | setattr(self.config_obj, i, getattr(self, i)) 196 | if self.config_obj: 197 | self.config_obj.setup() 198 | 199 | def __call__(self, **args): 200 | self.run(**args) 201 | 202 | def run(self, **args): 203 | pass 204 | -------------------------------------------------------------------------------- /fiddle/testsuite_utils.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python2 2 | # MIT License 3 | 4 | # Copyright (c) 2017 Rebecca ".bx" Shapiro 5 | 6 | # Permission is hereby granted, free of charge, to any person obtaining a copy 7 | # of this software and associated documentation files (the "Software"), to deal 8 | # in the Software without restriction, including without limitation the rights 9 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | # copies of the Software, and to permit persons to whom the Software is 11 | # furnished to do so, subject to the following conditions: 12 | 13 | # The above copyright notice and this permission notice shall be included in all 14 | # copies or substantial portions of the Software. 15 | 16 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | # SOFTWARE. 23 | 24 | from config import Main 25 | import re 26 | import os 27 | import pure_utils 28 | import db_info 29 | import r2_keeper as r2 30 | import json 31 | 32 | 33 | def addr2functionname(addr, stage, debug=False): 34 | elf = stage.elf 35 | old = r2.gets(elf, "s") 36 | r2.get(elf, "s 0x%x" % addr) 37 | s = r2.get(elf, "afi") 38 | r2.get(elf, "s %s" % old) 39 | def getname(i): 40 | name = i["name"] 41 | if i.name.startswith("sym."): 42 | name = name[4:] 43 | #print "addr2fn %x " % (addr) 44 | for i in s: 45 | if len(i) > 1: 46 | print s 47 | print "%x addr func" % addr 48 | raise Exception 49 | name = getname(i) 50 | 51 | return name 52 | return "" 53 | 54 | 55 | def get_symbol_location(name, stage, debug=False): 56 | elf = stage.elf 57 | return pure_utils.get_symbol_location(elf, name, debug) 58 | 59 | 60 | def addr2line(addr, stage, debug=False): 61 | elf = stage.elf 62 | cc = Main.cc 63 | fn = db_info.get(stage).addr2functionname(addr) 64 | return pure_utils.addr2line(addr, elf, debug, fn) 65 | # 66 | # addr = get_symbol_location(fn, stage) 67 | # elf = stage.elf 68 | # old = r2.gets(elf, "s") 69 | # r2.get(elf, "s 0x%x" % addr) 70 | # s = r2.gets(elf, "CL") 71 | # r2.get(elf, "s %s" % old) 72 | # res = s.split() 73 | # d = r2.gets(elf, "pwd") 74 | # if debug and res: 75 | # print "addr2line %s%s:%s" % (d, res[1][2:], res[3]) 76 | # if res: 77 | # return "%s%s:%s" % (d, res[1][2:], res[3]) 78 | # else: 79 | # return "" 80 | 81 | def line2addrs(line, stage): 82 | srcdir = Main.raw.runtime.temp_target_src_dir 83 | cmd = "%sgdb -ex 'dir %s' -ex 'file %s' -ex 'info line %s' --batch --nh --nx %s 2>/dev/null" % (Main.cc, 84 | srcdir, 85 | stage.elf, 86 | line, stage.elf) 87 | output = Main.shell.run_multiline_cmd(cmd) 88 | startat = output[0] 89 | assembly = False 90 | isataddr = None 91 | restart = None 92 | reend = None 93 | if ("but contains no code." in startat) and (".S\" is at address" in startat): 94 | if (".S\" is at address" in startat): # is assembly 95 | assembly = True 96 | isataddr = re.compile("is at address (0x[0-9a-fA-F]{0,8})") 97 | 98 | else: 99 | restart = re.compile("starts at address (0x[0-9a-fA-F]{0,8})") 100 | reend = re.compile("and ends at (0x[0-9a-fA-F]{0,8})") 101 | 102 | if isataddr: 103 | if assembly: 104 | startaddr = long((isataddr.search(startat)).group(1), 0) 105 | return (startaddr, startaddr+4) 106 | else: 107 | return (-1, -1) 108 | else: 109 | startaddr = long((restart.search(startat)).group(1), 0) 110 | endaddr = long((reend.search(startat)).group(1), 0) 111 | return (startaddr, endaddr) 112 | 113 | 114 | def line2src(line): 115 | try: 116 | [path, lineno] = line.split(':') 117 | except ValueError: 118 | return "" 119 | cmd = "sed -n '%s,%sp' %s 2>/dev/null" % (lineno, lineno, path) 120 | try: 121 | output = Main.shell.run_cmd(cmd) 122 | return output 123 | except: 124 | return '' 125 | 126 | 127 | def addr2disasmobjdump(addr, sz, stage, thumb=True, debug=False): 128 | old = r2.gets(stage.elf, "s") 129 | r2.gets(stage.elf, "s 0x%x" % addr) 130 | if thumb: # force r2 to use the correct instruction size. sigh. 131 | r2.gets(stage.elf, "ahb 16") 132 | r2.gets(stage.elf, "e asm.bits=16") 133 | else: 134 | r2.gets(stage.elf, "ahb 32") 135 | r2.gets(stage.elf, "e asm.bits=32") 136 | r2.get(stage.elf, "pd 1") 137 | i = r2.get(stage.elf, "pdj 1")[0] 138 | 139 | if "disasm" in i or u"invalid" == i["type"] or u"invalid" == i["disasm"]: 140 | r2.get(stage.elf, "s %s" % old) 141 | return (None, None, None) 142 | fn = db_info.get(stage).addr2functionname(addr) 143 | r2.get(stage.elf, "s %s" % old) 144 | return (i['bytes'], i['disasm'], fn) 145 | 146 | 147 | def get_c_function_names(stage): 148 | return pure_utils.get_c_function_names(stage.elf, Main.cc) 149 | 150 | 151 | def get_section_headers(stage): 152 | elf = stage.elf 153 | return pure_utils.get_section_headers(elf) 154 | 155 | 156 | def get_section_location(name, stage): 157 | elf = stage.elf 158 | return pure_utils.get_section_location(elf,name) 159 | 160 | 161 | def get_symbol_location_start_end(name, stage, debug=False): 162 | elf = stage.elf 163 | s = r2.get(elf, "isj") 164 | for i in s: 165 | if i["name"] == name: 166 | addr = i["vaddr"] 167 | if debug: 168 | print i 169 | return (addr, addr + i["size"]) 170 | return (-1, -1) 171 | 172 | 173 | def get_line_addr(line, start, stage, debug=False, srcdir=None): 174 | cc = Main.cc 175 | elf = stage.elf 176 | if srcdir: 177 | srcdir = "--cd=%s " % (srcdir) 178 | cmd = "%sgdb %s -ex 'info line %s' --batch --nh --nx %s 2>/dev/null" % (cc, 179 | srcdir, 180 | line, elf) 181 | if debug: 182 | print cmd 183 | output = Main.shell.run_multiline_cmd(cmd) 184 | if debug: 185 | print output 186 | output = output[0] 187 | 188 | assembly = False 189 | if ("but contains no code." in output) and (".S\" is at address" in output): 190 | if (".S\" is at address" in output): # is assembly 191 | assembly = True 192 | readdr = re.compile("is at address (0x[0-9a-fA-F]{0,8})") 193 | elif start: 194 | readdr = re.compile("starts at address (0x[0-9a-fA-F]{0,8})") 195 | else: 196 | readdr = re.compile("and ends at (0x[0-9a-fA-F]{0,8})") 197 | if not readdr: 198 | return -1 199 | addrg = readdr.search(output) 200 | if not addrg: 201 | return -1 202 | res = long(addrg.group(1), 0) 203 | if assembly and (not start): 204 | res += 1 # give something larger for end endress for non-includive range 205 | return res 206 | 207 | 208 | def symbol_relocation_file(name, offset, stage, path=None, debug=False): 209 | if path is None: 210 | path = tempfile.NamedTemporaryFile("rw").name 211 | elf = stage.elf 212 | srcdir = Main.raw.runtime.temp_target_src_dir 213 | cc = Main.cc 214 | cmd = "%sobjcopy --extract-symbol -w -N \!%s "\ 215 | "--change-addresses=0x%x %s %s 2>/dev/null" % (cc, 216 | name, 217 | offset, 218 | elf, 219 | path) 220 | if debug: 221 | print cmd 222 | output = Main.shell.run_cmd(cmd) 223 | if debug: 224 | print output 225 | return path 226 | 227 | 228 | def addr_lo(addr): 229 | return addr & 0xFFFFFFFF 230 | 231 | 232 | def addr_hi(addr): 233 | return (addr & 0xFFFFFFFF00000000) >> 32 234 | -------------------------------------------------------------------------------- /fiddle/ia.py: -------------------------------------------------------------------------------- 1 | from capstone import * 2 | from capstone.arm import * 3 | import re 4 | 5 | # analyze instruction to figure out what 6 | # registers we need to calculate write destination 7 | # and also calculate destination for us given reg values 8 | class InstructionAnalyzer(): 9 | WORD_SIZE = 4 10 | writemnere = re.compile("(push)|(stm)|(str)|(stl)|(stc)") 11 | 12 | def __init__(self): 13 | self.thumb = Cs(CS_ARCH_ARM, CS_MODE_THUMB) 14 | self.thumb.detail = True 15 | # self.thumb.skipdata = True 16 | self.arm = Cs(CS_ARCH_ARM, CS_MODE_ARM) 17 | self.arm.detail = True 18 | # self.arm.skipdata = True 19 | self.cache = {} 20 | 21 | def disasm(self, value, thumb, pc, cache=False): 22 | offset = pc 23 | ins = None 24 | if cache and pc in self.cache.iterkeys(): 25 | return self.cache[pc] 26 | if thumb: 27 | ins = self.thumb.disasm(value, offset, count=1) 28 | else: 29 | ins = self.arm.disasm(value, offset, count=1) 30 | try: 31 | i = next(ins) 32 | except StopIteration as i: 33 | print "-%x %s '%s'-" % (pc, thumb, value.encode('hex')) 34 | raise i 35 | 36 | if cache: 37 | self.cache[pc] = i 38 | return i 39 | 40 | def is_instr_memstore(self, ins): 41 | return self.is_mne_memstore(ins.mnemonic) 42 | 43 | def get_flag_value(self, flag, cpsr): 44 | flags = {'n': (1 << 31), 'z': (1 << 30), 'c': (1 << 29), 'v': (1 << 28), 'q': (1 << 27)} 45 | return True if ((cpsr & flags[flag]) == flags[flag]) else False 46 | 47 | # inclase there is a conditional store check 48 | def store_will_happen(self, ins, regs): 49 | if self.has_condition_suffix(ins): 50 | cc = ins.cc 51 | cpsr = regs[-1] 52 | if cc == 0: 53 | # eq 54 | return self.get_flag_value('z', cpsr) is True 55 | elif cc == 1: 56 | # not equal, ne 57 | return self.get_flag_value('z', cpsr) is False 58 | elif cc == 2: 59 | # carry set, hs 60 | return self.get_flag_value('c', cpsr) is True 61 | elif cc == 3: 62 | # lo, cary clear 63 | return self.get_flag_value('c', cpsr) is False 64 | elif cc == 4: 65 | # mi, 66 | return self.get_flag_value('n', cpsr) is True 67 | elif cc == 5: 68 | # pl 69 | return self.get_flag_value('n', cpsr) is False 70 | elif cc == 6: 71 | # vs 72 | return self.get_flag_value('v', cpsr) is True 73 | elif cc == 7: 74 | return self.get_flag_value('v', cpsr) is False 75 | # vc 76 | elif cc == 8: 77 | # hi 78 | return (self.get_flag_value('c', cpsr) is True) \ 79 | and (self.get_flag_value('z', cpsr) is False) 80 | elif cc == 9: 81 | # ls 82 | return (self.get_flag_value('c', cpsr) is False) \ 83 | or (self.get_flag_value('z', cpsr) is True) 84 | elif cc == 10: 85 | # ge 86 | return (self.get_flag_value('n', cpsr) == self.get_flag_value('v', cpsr)) 87 | elif cc == 11: 88 | # lt 89 | return not (self.get_flag_value('n', cpsr) == self.get_flag_value('v', cpsr)) 90 | elif cc == 12: 91 | # gt 92 | return (self.get_flag_value('z', cpsr) is False) \ 93 | and (self.get_flag_value('n', cpsr) == self.get_flag_value('v', cpsr)) 94 | elif cc == 13: 95 | # le 96 | return (self.get_flag_value('z', cpsr) is True) \ 97 | or (not (self.get_flag_value('n', cpsr) == self.get_flag_value('v', cpsr))) 98 | else: 99 | return True 100 | else: 101 | return True 102 | 103 | def is_thumb(self, cpsr): 104 | CPSR_THUMB = 0x20 105 | return True if (cpsr & CPSR_THUMB) == CPSR_THUMB else False # if cpsr bit 5 is set 106 | 107 | def calculate_store_offset(self, ins, regs): 108 | if self.has_condition_suffix(ins): 109 | # strip off cpsr 110 | regs = regs[:-1] 111 | operands = ins.operands 112 | lshift = 0 113 | disp = 0 114 | for i in operands: 115 | if i.type == ARM_OP_MEM: 116 | lshift = i.mem.lshift 117 | disp = i.mem.disp 118 | if lshift > 0: 119 | regs[1] = (regs[1] << lshift) % (0xFFFFFFFF) 120 | 121 | return (sum(regs) + disp) % (0xFFFFFFFF) 122 | 123 | @classmethod 124 | def has_condition_suffix(cls, ins): 125 | # strip off '.w' designator if it is there, it just means 126 | # its a 32-bit Thumb instruction 127 | return ins.cc not in [ARM_CC_AL, ARM_CC_INVALID] 128 | 129 | @classmethod 130 | def calculate_store_size(cls, ins): 131 | mne = ins.mnemonic.encode("ascii") 132 | if mne.startswith('push') or mne.startswith('stl'): 133 | (read, write) = ins.regs_access() 134 | # cannot push sp value, so if it is in this list don't 135 | # include it in the count 136 | if 12 in read: 137 | read.remove(12) 138 | return -1*len(read)*InstructionAnalyzer.WORD_SIZE 139 | elif mne.startswith('stm'): # stm gets converted to push, but just in case 140 | (read, write) = ins.regs_access() 141 | return (len(read) - 1)*InstructionAnalyzer.WORD_SIZE 142 | elif mne.startswith('str'): 143 | # strip off '.w' designator if it is there, it just means 144 | # its a 32-bit Thumb instruction 145 | if mne[-1] == 'w': 146 | mne = mne[:-1] 147 | if mne[-1] == '.': 148 | mne = mne[:-1] 149 | 150 | # strip of any condition suffixes 151 | if cls.has_condition_suffix(ins): 152 | # remove last 2 chars 153 | mne = mne[:-2] 154 | 155 | # remove a final t if it is there 156 | if mne[-1] == 't': 157 | mne = mne[:-1] 158 | # now check to see how many bytes the instruction operates on 159 | if mne == 'str': 160 | return InstructionAnalyzer.WORD_SIZE 161 | elif mne[-1] == 'b': 162 | return 1 163 | elif mne[-1] == 'h': 164 | return InstructionAnalyzer.WORD_SIZE/2 165 | elif mne[-1] == 'd': 166 | return InstructionAnalyzer.WORD_SIZE*2 167 | else: # strex 168 | return InstructionAnalyzer.WORD_SIZE 169 | elif mne.startswith('stc'): 170 | if 'L' in mne: 171 | return InstructionAnalyzer.WORD_SIZE*8 172 | else: 173 | return InstructionAnalyzer.WORD_SIZE 174 | else: 175 | print "Do not know how to handle instruction mnemonic %s at %x (%x)" \ 176 | % (ins.mnemonic, ins.address, 0) 177 | return -1 178 | @classmethod 179 | def needed_regs(cls, ins): 180 | regs = [] 181 | if ins.id == 0: 182 | print "NO DATA!" 183 | return [] 184 | if ins.mnemonic.startswith("push"): 185 | regs = ['sp'] 186 | elif ins.mnemonic.startswith("stl") or ins.mnemonic.startswith("stm"): 187 | # stl is actually treated by capstone as push instruction 188 | # but we will check just in case 189 | # similar to push, first operand only 190 | regs = [ins.reg_name(ins.operands[0].reg).encode('ascii')] 191 | elif ins.mnemonic.startswith("str") or ins.mnemonic.startswith("stc"): 192 | readops = [] 193 | for i in ins.operands: 194 | if i.type == ARM_OP_MEM: 195 | if len(readops) > 0: 196 | print "multiple mem operators? We can't handle this!" 197 | return [] 198 | if i.mem.base != 0: 199 | readops.append(ins.reg_name(i.mem.base).encode('ascii')) 200 | if i.mem.index != 0: 201 | readops.append(ins.reg_name(i.mem.index).encode('ascii')) 202 | regs = readops 203 | if cls.has_condition_suffix(ins): 204 | regs.append('cpsr') 205 | return regs 206 | 207 | @classmethod 208 | def _is_mne_memstore(cls, mne): 209 | return InstructionAnalyzer.writemnere.match(mne) is not None 210 | 211 | def is_mne_memstore(self, mne): 212 | return self._is_mne_memstore(mne) 213 | -------------------------------------------------------------------------------- /fiddle/external_source_manager.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python 2 | # MIT License 3 | 4 | # Copyright (c) 2017 Rebecca ".bx" Shapiro 5 | 6 | # Permission is hereby granted, free of charge, to any person obtaining a copy 7 | # of this software and associated documentation files (the "Software"), to deal 8 | # in the Software without restriction, including without limitation the rights 9 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | # copies of the Software, and to permit persons to whom the Software is 11 | # furnished to do so, subject to the following conditions: 12 | 13 | # The above copyright notice and this permission notice shall be included in all 14 | # copies or substantial portions of the Software. 15 | 16 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | # SOFTWARE. 23 | 24 | from config import Main 25 | from doit.tools import run_once 26 | import sys 27 | import time 28 | import os 29 | import pathlib 30 | from doit.action import CmdAction 31 | from doit.tools import LongRunning 32 | import git_mgr 33 | 34 | 35 | class task_ran(object): 36 | def __init__(self, n): 37 | self.n = n 38 | 39 | def __call__(self, task, values): 40 | if self.n in values: 41 | ctime = self.n 42 | return (time.time() - ctime) > 0 43 | else: 44 | return False 45 | 46 | 47 | class CodeTask(object): 48 | def task_name(self, basename=None, name=None): 49 | if basename is None: 50 | basename = self.basename 51 | if name is None: 52 | name = self.name 53 | return "%s:%s" % (basename, name) 54 | 55 | @classmethod 56 | def get_task_name(cls, basename, name): 57 | return cls.task_name(cls(None, None, False), basename, name) 58 | 59 | def __init__(self, name, cfg, init=True): 60 | if init: 61 | for a in ['uptodate', 'targets', 'actions']: 62 | if not hasattr(self, a): 63 | setattr(self, a, []) 64 | self.basename = cfg.name 65 | self.name = name 66 | self.file_dep = [] 67 | self.task_dep = [] 68 | self.other = [] 69 | self.build_cfg = cfg 70 | self.root_dir = self.gf('root') 71 | if os.path.isdir(os.path.join(self.root_dir, ".git")): 72 | self.git = git_mgr.GitManager(self.root_dir) 73 | else: 74 | self.git = None 75 | 76 | def gf(self, field): 77 | return getattr(self.build_cfg, field) 78 | 79 | def path(self, rel): 80 | p = pathlib.Path(self.root_dir) / rel 81 | return p 82 | 83 | def save_timestamp(self): 84 | return {'%stime' % self.name: time.time()} 85 | 86 | def list_tasks(self): 87 | l = { 88 | 'name': self.name, 89 | 'basename': self.basename, 90 | #'verbosity': 2, 91 | #'verbosity': None, 92 | 'uptodate': self.uptodate, 93 | 'targets': self.targets, 94 | 'actions': self.actions, 95 | } 96 | return l 97 | 98 | def format_command(self, cmd): 99 | config_type = self.build_cfg.config_type if hasattr(self.build_cfg, "config_type") else "" 100 | config_name = self.build_cfg.config_name if hasattr(self.build_cfg, "config_name") else "" 101 | if config_type and config_name: 102 | cfg = Main.object_config_lookup(config_type, config_name) 103 | keywords = Main.__dict__ 104 | keywords.update(cfg.__dict__) 105 | # filter out keys that begin with _Main_ which are a result of use of the @property decorator 106 | keywords = {k.replace("_Main__", ""): v for (k, v) in keywords.iteritems()} 107 | cmd = cmd.format(**keywords) 108 | return cmd 109 | 110 | 111 | class CodeTaskClean(CodeTask): 112 | #defaults = {'clean': 'make clean'} 113 | 114 | def __init__(self, cfg): 115 | super(CodeTaskClean, self).__init__('do_clean', cfg) 116 | #(self.save_timestamp,), 117 | self.actions = [ 118 | CmdAction(self.format_command(self.gf("clean")), 119 | cwd=self.root_dir)] 120 | self.uptodate = [True] 121 | 122 | 123 | class CodeTaskConfig(CodeTask): 124 | #defaults = {'build_prepare': './configure'} 125 | 126 | def __init__(self, cfg): 127 | super(CodeTaskConfig, self).__init__('config', cfg) 128 | #(self.save_timestamp,), 129 | self.actions = [ 130 | CmdAction(self.format_command(self.gf('build_prepare')), 131 | cwd=self.root_dir)] 132 | #self.uptodate = [not task_ran("clean")] 133 | 134 | 135 | class CodeTaskBuild(CodeTask): 136 | #defaults = {'build_cmd': 'make'} 137 | #defaults.update(CodeTaskClean.defaults) 138 | #defaults.update(CodeTaskConfig.defaults) 139 | def __init__(self, cfg, printonly=False): 140 | super(CodeTaskBuild, self).__init__('build', cfg) 141 | if not printonly: 142 | c = "" 143 | for i in ['clean', 'build_prepare', 'build_cmd']: 144 | l = self.gf(i) 145 | if l: 146 | c += "%s; " % l 147 | else: 148 | c = self.gf("build_cmd") 149 | #self.uptodate = [task_ran('config')] 150 | self.targets = self.all_targets() 151 | self.actions = [CmdAction(self.format_command(c), 152 | cwd=self.root_dir)] 153 | 154 | def all_targets(self): 155 | files = self.gf("_files") 156 | paths = [] 157 | for (k, v) in files.iteritems(): 158 | if v.type == "target": 159 | path = v.relative_path 160 | root = getattr(v, "root_path", v.software.root) 161 | paths.append(os.path.join(root, path)) 162 | 163 | return paths 164 | 165 | 166 | 167 | class CodeTaskList(): 168 | def __init__(self, cfg, always_uptodate=False, printonly=False): 169 | self.build_cfg = cfg 170 | self.basename = cfg.name 171 | self.always_uptodate = always_uptodate 172 | self.root_dir = self.build_cfg.root 173 | if os.path.isdir(os.path.join(self.root_dir, ".git")): 174 | self.git = git_mgr.GitManager(self.root_dir) 175 | else: 176 | self.git = None 177 | self.build = CodeTaskBuild(cfg, printonly=printonly) 178 | if printonly: 179 | self.clean = CodeTaskClean(cfg) 180 | self.config = CodeTaskConfig(cfg) 181 | self.tasks = [self.clean, self.config, self.build] 182 | else: 183 | self.config = CodeTaskBuild(cfg, printonly=printonly) 184 | self.tasks = [self.build] 185 | 186 | if always_uptodate: 187 | for t in self.tasks: 188 | t.uptodate = [True] 189 | 190 | def has_nothing_to_commit(self): 191 | return self.git.has_nothing_to_commit() if self.git else True 192 | 193 | def commit_changes(self): 194 | if self.git: 195 | self.git.commit_changes() 196 | 197 | def get_gitinfo(self): 198 | if self.git: 199 | head = self.git.get_head() 200 | head = head.translate(None, "/") 201 | sha = self.git.get_commit() 202 | gitinfo = "%s.%s" % (head, sha) 203 | return (gitinfo, sha) 204 | else: 205 | return (None, None) #, None) 206 | 207 | def list_tasks(self): 208 | yield { 209 | 'basename': self.basename, 210 | 'name': None, 211 | } 212 | for task in self.tasks: 213 | yield task.list_tasks() 214 | 215 | 216 | class SourceLoader(): 217 | def __init__(self, build, print_only=False): 218 | self.init_only = True if not build else False 219 | self.builds = build 220 | ss = Main.config_class_lookup("Software") 221 | target_software = Main.target_software 222 | 223 | self.code_tasks = [CodeTaskList(s, s.name not in self.builds, print_only) 224 | for s in ss 225 | if (s.name in self.builds)] 226 | 227 | ## always need the target 228 | if target_software.name not in self.builds: 229 | self.code_tasks.append(CodeTaskList(Main.target_software, False)) 230 | #if self.init_only: 231 | # for c in self.code_tasks: 232 | # for t in c.tasks: 233 | # t.uptodate = [True] 234 | # else: 235 | # for c in self.code_tasks: 236 | # if c.basename in self.builds: 237 | # for t in c.tasks: 238 | # t.uptodate = [False] 239 | 240 | def list_tasks(self): 241 | #l = [] 242 | for c in self.code_tasks: 243 | # only if in bulid list 244 | if c.basename in self.builds: 245 | name = "task_%s" % c.basename 246 | tl = c.list_tasks 247 | yield (name, tl) 248 | #l.append((name, tl)) 249 | #return l 250 | -------------------------------------------------------------------------------- /fiddle/addr_space.py: -------------------------------------------------------------------------------- 1 | # MIT License 2 | 3 | # Copyright (c) 2017 Rebecca ".bx" Shapiro 4 | 5 | # Permission is hereby granted, free of charge, to any person obtaining a copy 6 | # of this software and associated documentation files (the "Software"), to deal 7 | # in the Software without restriction, including without limitation the rights 8 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | # copies of the Software, and to permit persons to whom the Software is 10 | # furnished to do so, subject to the following conditions: 11 | 12 | # The above copyright notice and this permission notice shall be included in all 13 | # copies or substantial portions of the Software. 14 | 15 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | # SOFTWARE. 22 | 23 | import config 24 | import pytable_utils 25 | import intervaltree 26 | import tables 27 | import os 28 | from config import Main 29 | import csv 30 | from fiddle_extra import parse_am37x_register_tables 31 | import testsuite_utils as utils 32 | register_map = {} 33 | 34 | 35 | mmap_perms = tables.Enum(['rw', 'r', '?', 'rwx', 'x', 'w']) 36 | 37 | 38 | mmap_type = tables.Enum(['special', 'reserved', 'rom', 'ram', 'registers', 39 | 'virtual', 'other', 'iva2.2']) 40 | 41 | 42 | var_type = tables.Enum(['staticvar', 'register', 'othervar', 43 | 'heapvar', 'stackvar', 'text']) 44 | vlist = ['rw', 'r', 'w', 'none', '?'] 45 | var_perms = tables.Enum(vlist) 46 | 47 | perms = tables.Enum(vlist + ["rwx", "x", "rx"]) 48 | 49 | 50 | class MemMapEntry(tables.IsDescription): 51 | name = tables.StringCol(512) 52 | startaddr = tables.UInt64Col() 53 | startaddrlo = tables.UInt32Col() 54 | startaddrhi = tables.UInt32Col() 55 | endaddr = tables.UInt64Col() 56 | endaddrlo = tables.UInt32Col() 57 | endaddrhi = tables.UInt32Col() 58 | perms = tables.EnumCol(perms, '?', base='uint8') 59 | kind = tables.EnumCol(mmap_type, 'other', base='uint8') 60 | 61 | # new = tables.BoolCol() 62 | # in_process = tables.BoolCol() 63 | 64 | 65 | class VarEntry(tables.IsDescription): 66 | name = tables.StringCol(512) 67 | startaddr = tables.UInt64Col() 68 | startaddrlo = tables.UInt32Col() 69 | startaddrhi = tables.UInt32Col() 70 | endaddr = tables.UInt64Col() 71 | endaddrlo = tables.UInt32Col() 72 | endaddrhi = tables.UInt32Col() 73 | substage = tables.Int16Col() 74 | kind = tables.EnumCol(var_type, 'othervar', base='uint8') 75 | perms = tables.EnumCol(var_perms, 'rw', base='uint8') 76 | rawkind = tables.StringCol(128) 77 | 78 | 79 | class RegEntry(tables.IsDescription): 80 | name = tables.StringCol(512) 81 | address = tables.UInt64Col() 82 | addresslo = tables.UInt32Col() 83 | addresshi = tables.UInt32Col() 84 | width = tables.UInt8Col() 85 | reset = tables.StringCol(16) 86 | typ = tables.StringCol(16) 87 | offset = tables.UInt64Col() 88 | offsetlo = tables.UInt32Col() 89 | offsethi = tables.UInt32Col() 90 | table = tables.StringCol(256) 91 | 92 | 93 | class AddrSpaceInfo(): 94 | def __init__(self): 95 | self.grpname = 'memory' 96 | self.csvs = [] 97 | self.reg_csvs = [] 98 | for (f, i) in Main.get_hardwareclass_config()._files.iteritems(): 99 | if i.type == "mmap": 100 | if getattr(i, "subtype", "") == "registers": 101 | self.reg_csvs.append(Main.populate_from_config(i.path)) 102 | else: 103 | self.csvs.append(Main.populate_from_config(i.path)) 104 | 105 | self.mem_tablename = "memmap" 106 | self.reg_tablename = "regs" 107 | self.h5group = None 108 | self.h5file = None 109 | self.memmap_table = None 110 | self.reg_table = None 111 | 112 | 113 | def open_dbs(self, loc, create=False): 114 | if create: 115 | self._create_tables(loc) 116 | else: 117 | self._open_tables(loc) 118 | 119 | def _create_tables(self, dbloc): 120 | dname = os.path.dirname(dbloc) 121 | self.h5file = tables.open_file(dbloc, mode="w", 122 | title="addr space info") 123 | self.h5group = self.h5file.create_group("/", self.grpname, "") 124 | self.memmap_table = self.h5file.create_table(self.h5group, self.mem_tablename, 125 | MemMapEntry, "") 126 | self.reg_table = self.h5file.create_table(self.h5group, self.reg_tablename, 127 | RegEntry, "") 128 | self._create_memmap_table() 129 | self._create_reg_table() 130 | 131 | 132 | def create_substage_memmap_table(self): 133 | for c in self.csvs: 134 | with open(c) as csvfile: 135 | fields = ['name', 'startaddr', 'endaddr', 'perms', 'kind'] 136 | reader = csv.DictReader(csvfile, fields) 137 | r = self.memmap_table.row 138 | for entry in reader: 139 | for f in fields: 140 | if "addr" in f: 141 | entry[f] = long(entry[f], 0) 142 | entry[f+"lo"] = utils.addr_lo(long(entry[f])) 143 | entry[f+"hi"] = utils.addr_hi(long(entry[f])) 144 | else: 145 | entry[f] = entry[f].strip().lower() 146 | if f == 'perms': 147 | entry[f] = getattr(mmap_perms, entry[f]) 148 | elif f == 'kind': 149 | entry[f] = getattr(mmap_type, entry[f]) 150 | r[f] = entry[f] 151 | #r['substage'] = substage 152 | r.append() 153 | self.memmap_table.cols.startaddrlo.create_index(kind='full') 154 | self.memmap_table.cols.startaddrhi.create_index(kind='full') 155 | self.memmap_table.cols.endaddrlo.create_index(kind='full') 156 | self.memmap_table.cols.endaddrhi.create_index(kind='full') 157 | self.memmap_table.flush() 158 | 159 | def _create_memmap_table(self): 160 | self.create_substage_memmap_table() 161 | 162 | def print_memmap_table(self): 163 | for r in self.memmap_table.iterrows(): 164 | perms = mmap_perms(r['perms']) 165 | kind = mmap_type(r['kind']) 166 | print "SECT: %s (0x%x -- 0x%x) (%s, %s)" % (r['name'], 167 | r['startaddr'], 168 | r['endaddr'], 169 | perms, kind) 170 | 171 | def print_reg_table(self): 172 | for r in self.reg_table.iterrows(): 173 | print "REG: %s (0x%x, %d bytes [offset %s]) (%s, %s) table %s" % (r['name'], 174 | r['address'], 175 | r['width'], 176 | r['offset'], 177 | r['typ'], 178 | r['reset'], 179 | r['table']) 180 | 181 | def _create_reg_table(self): 182 | for c in self.reg_csvs: 183 | (f, reader) = parse_am37x_register_tables.parsecsv(c) 184 | row = self.reg_table.row 185 | for r in reader: 186 | row['address'] = long(r['address'].strip(), 16) if r['address'] else 0 187 | row['addresslo'] = utils.addr_lo(long(row['address'])) 188 | row['addresshi'] = utils.addr_hi(long(row['address'])) 189 | row["offset"] = long(r["offset"].strip(), 16) if r["offset"] else 0 190 | row['offsetlo'] = utils.addr_lo(long(row['offset'])) 191 | row['offsethi'] = utils.addr_hi(long(row['offset'])) 192 | row["table"] = r["table"] if r["table"] else "" 193 | row["typ"] = r["typ"] if r["typ"] else "" 194 | row["width"] = long(r["width"]) if r["width"] else 0 195 | row["reset"] = r["reset"] if r["reset"] else "" 196 | row["name"] = r["name"] if r["name"] else "" 197 | if row['address'] == 0: 198 | print "addr not found in %s" % r 199 | 200 | row.append() 201 | f.close() 202 | self.reg_table.cols.addresslo.create_index(kind='full') 203 | self.reg_table.cols.addresshi.create_index(kind='full') 204 | self.reg_table.flush() 205 | 206 | def _open_tables(self, loc): 207 | self.h5file = tables.open_file(loc, mode="r") 208 | self.h5group = self.h5file.get_node("/%s" % self.grpname) 209 | self.memmap_table = getattr(self.h5group, self.mem_tablename) 210 | self.reg_table = getattr(self.h5group, self.reg_tablename) 211 | 212 | def close_dbs(self, flush_only=False): 213 | if self.h5file: 214 | self.h5file.flush() 215 | if not flush_only: 216 | self.h5file.close() 217 | self.h5file = None 218 | -------------------------------------------------------------------------------- /fiddle_extra/frama_c/dest_analysis.ml: -------------------------------------------------------------------------------- 1 | (* MIT License *) 2 | 3 | (* Copyright (c) 2017 Rebecca ".bx" Shapiro *) 4 | 5 | (* Permission is hereby granted, free of charge, to any person obtaining a copy *) 6 | (* of this software and associated documentation files (the "Software"), to deal *) 7 | (* in the Software without restriction, including without limitation the rights *) 8 | (* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell *) 9 | (* copies of the Software, and to permit persons to whom the Software is *) 10 | (* furnished to do so, subject to the following conditions: *) 11 | 12 | (* The above copyright notice and this permission notice shall be included in all *) 13 | (* copies or substantial portions of the Software. *) 14 | 15 | (* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR *) 16 | (* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, *) 17 | (* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE *) 18 | (* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER *) 19 | (* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, *) 20 | (* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE *) 21 | (* SOFTWARE. *) 22 | 23 | open Cil_types 24 | open Cil 25 | open Cil_types 26 | open Cil 27 | 28 | module SS = Set.Make(String) 29 | module Funcall_info = struct 30 | type funcallinfo = { 31 | lval:Cil_types.lval; 32 | exp:Cil_types.exp; 33 | lexloc:Cil_types.location; 34 | lvalloc:Locations.location; 35 | lvalfulladdr:Integer.t; 36 | instr:Cil_types.instr; 37 | min:Abstract_interp.Int.t option; 38 | max:Abstract_interp.Int.t option; 39 | } 40 | 41 | let form_callstack_string cs = 42 | List.fold_right (fun c s -> (match c with (f, _) -> 43 | s ^ "->" ^ (Ast_info.Function.get_name f.fundec))) cs "" 44 | let build_callinfo s kinstr = 45 | if (Db.Value.is_computed()) && (Db.Value.is_reachable_stmt s) then 46 | (match Db.Value.get_stmt_state_callstack ~after:true s with 47 | None -> SS.empty 48 | | Some(state) -> Value_types.Callstack.Hashtbl.fold 49 | (fun cs state r -> 50 | (if Cvalue.Model.is_reachable state then 51 | SS.add (form_callstack_string cs) r 52 | else 53 | SS.empty)) 54 | state SS.empty) 55 | else 56 | SS.empty 57 | end 58 | 59 | let help_msg = "Resolves as many memory write destinations as possible" 60 | 61 | module Self = Plugin.Register 62 | (struct 63 | let name = "write destination resolver" 64 | let shortname = "dst" 65 | let help = help_msg 66 | end) 67 | 68 | module Enabled = Self.False 69 | (struct 70 | let option_name = "-dst" 71 | let help = "when on (off by default), " ^ help_msg 72 | end) 73 | module More_enabled = Self.False 74 | (struct 75 | let option_name = "-dst-more" 76 | let help = "print more dst info, " ^ help_msg 77 | end) 78 | 79 | module Output_file = Self.String 80 | (struct 81 | let option_name = "-dst-output" 82 | let default = "-" 83 | let arg_name = "output_file" 84 | let help = 85 | "file where the message is output (default: console)" 86 | end) 87 | 88 | module Location_helper= struct 89 | let loc_to_loc_and_size loc = 90 | (Locations.loc_to_loc_without_size loc, Locations.loc_size loc) 91 | 92 | let loc_bytes_to_addr_int l = 93 | try 94 | match l with 95 | (Locations.Location_Bytes.Map(m), _) -> ( 96 | match Locations.Location_Bytes.M.find Base.null m with 97 | Ival.Set([|i|]) -> i 98 | (*| Ival.Set(s) -> ?? what to do when there is more than 1 solution *) 99 | | _ -> Integer.zero (* zero or more than one results *) 100 | ) 101 | | _ -> Integer.zero (* no location map *) 102 | with Not_found -> Integer.zero 103 | 104 | let get_min_max l = 105 | try 106 | (match l with 107 | (llv, lsz) -> 108 | (match ((Cvalue.V.project_ival llv), (Int_Base.project lsz)) with 109 | (v, sz) -> 110 | (match Ival.min_and_max v with 111 | (Some(min), Some(max)) -> (Some(min), Some(Integer.add max (Integer.native_div sz (Integer.of_int 8)))) 112 | | _ -> (None, None)))) 113 | with Cvalue.V.Not_based_on_null -> (None, None) 114 | end 115 | 116 | module Instr_info = struct 117 | type instrinfo = { 118 | lval:Cil_types.lval; 119 | exp:Cil_types.exp; 120 | lexloc:Cil_types.location; 121 | lvalloc:Locations.location; 122 | lvalfulladdr:Integer.t; 123 | instr:Cil_types.instr; 124 | min:Abstract_interp.Int.t option; 125 | max:Abstract_interp.Int.t option; 126 | callinfo:SS.t; 127 | } 128 | 129 | let lexloc_string info = 130 | let ({Lexing.pos_fname=f1; Lexing.pos_lnum=l1; _}, _) = info.lexloc in 131 | Printf.sprintf "%s:%d" f1 l1 132 | 133 | let get_lvalloc info = 134 | info.lvalloc 135 | 136 | let instr_string info = 137 | let s = Printer.pp_instr Format.str_formatter info.instr in 138 | Format.flush_str_formatter s 139 | 140 | let eval_lval lval kinstr = 141 | !Db.Value.lval_to_loc ~with_alarms:CilE.warn_none_mode kinstr lval 142 | 143 | let callstack_str info = 144 | if SS.is_empty info.callinfo then 145 | "" 146 | else 147 | SS.choose info.callinfo 148 | 149 | let lval_string info = 150 | let s = Printer.pp_lval Format.str_formatter info.lval in 151 | Format.flush_str_formatter s 152 | 153 | let build_instrinfo st kinstr = 154 | if Db.Value.is_computed() then 155 | match st.skind with 156 | Instr (Set(lv, e, location) as s) -> (let lvl = eval_lval lv kinstr in 157 | (let (min, max) = Location_helper.get_min_max (Location_helper.loc_to_loc_and_size lvl) in 158 | (let ii = { 159 | lval = lv; 160 | exp = e; 161 | lexloc = location; 162 | lvalloc = lvl; 163 | lvalfulladdr = Location_helper.loc_bytes_to_addr_int (Location_helper.loc_to_loc_and_size lvl); 164 | instr = s; 165 | min = min; 166 | max = max; 167 | callinfo = Funcall_info.build_callinfo st kinstr; 168 | } 169 | in Some(ii) 170 | ) 171 | ) 172 | ) 173 | | _ -> None 174 | else 175 | None 176 | end 177 | 178 | let print_msg = 179 | object (self : 'self) 180 | val mutable tofile = if Output_file.is_default() then false else true 181 | val mutable file_chan = if Output_file.is_default() then stdout else open_out (Output_file.get()) 182 | 183 | method ival_string ival = 184 | (let s = Abstract_interp.Int.pretty Format.str_formatter ival in 185 | Format.flush_str_formatter s; 186 | ) 187 | 188 | method print msg = 189 | if tofile then 190 | Printf.fprintf file_chan "%s\n" msg 191 | else 192 | Self.result "%s" msg 193 | method print_range info = 194 | (let {Instr_info.min=min; Instr_info.max=max; _} = info in 195 | match (min, max) with 196 | Some(min'), Some(max') -> 197 | self#print (Printf.sprintf "[%s, %s] %s in %s .. %s\n" (self#ival_string min') (self#ival_string max') (Instr_info.lval_string info) (Instr_info.lexloc_string info) (Instr_info.callstack_str info)); 198 | | _ -> (); 199 | ) 200 | method print_more info enabled = 201 | if enabled then 202 | (let s = Locations.pretty Format.str_formatter (Instr_info.get_lvalloc info) in 203 | self#print (Printf.sprintf "%s = %s (%s) .. %s \n" (Instr_info.instr_string info) (Format.flush_str_formatter s) (Instr_info.lexloc_string info) (Instr_info.callstack_str info)); 204 | ); 205 | () 206 | method close = 207 | if tofile then 208 | close_out file_chan 209 | end 210 | 211 | class print_dsts print_obj more = object (self: 'self) 212 | 213 | inherit Visitor.frama_c_inplace 214 | 215 | method! vstmt_aux s = 216 | (match Instr_info.build_instrinfo s self#current_kinstr with 217 | Some(info) -> (print_obj#print_range info; 218 | print_obj#print_more info more) 219 | | _ -> () 220 | ); 221 | Cil.DoChildren 222 | end 223 | 224 | let run () = 225 | if Enabled.get() then 226 | Visitor.visitFramacFileSameGlobals ( new print_dsts print_msg (More_enabled.get())) (Ast.get ()); 227 | print_msg#close 228 | 229 | let () = Db.Main.extend run 230 | 231 | -------------------------------------------------------------------------------- /fiddle/process_args.py: -------------------------------------------------------------------------------- 1 | import argparse 2 | import os 3 | from config import Main 4 | import instrumentation_results_manager as manager 5 | import doit_manager 6 | import run_cmds 7 | 8 | 9 | class FiddleArgParser(argparse.ArgumentParser): 10 | def __init__(self, name="", plugin=None, 11 | arg_parsers=[]): 12 | self.name = name 13 | parser = argparse.ArgumentParser(self.name) 14 | 15 | class SubstageFileAction(argparse.Action): 16 | def __init__(self, option_strings, dest, **kwargs): 17 | self.stages = Main.stages 18 | self.stagenames = [s.stagename for s in self.stages] 19 | if len(self.stages) == 1: 20 | self.nargs = 2 21 | else: 22 | self.nargs = 3 23 | self.sdefaults = {} 24 | kwargs['default'] = self.sdefaults 25 | super(SubstageFileAction, self).__init__(option_strings, 26 | dest, **kwargs) 27 | 28 | def __call__(self, parser, namespace, values, option_string=None): 29 | stagename = values[0] 30 | f = os.path.abspath(values[1]) 31 | d = os.path.abspath(values[2]) 32 | if stagename not in self.stagenames: 33 | raise argparse.ArgumentError(self, 34 | "%s not a valid stage, must " 35 | "be one of %s" % 36 | (stagename, 37 | str(self.stagenames))) 38 | if not os.path.exists(f): 39 | raise argparse.ArgumentError("Substage definition file I " 40 | "am trying to import from " 41 | "'%s' not found" % f) 42 | if not os.path.exists(d): 43 | raise argparse.ArgumentError("Region definition file I " 44 | "am trying to import from " 45 | "'%s' not found" % d) 46 | 47 | getattr(namespace, self.dest)[stagename] = (f, d) 48 | 49 | class SubstageNameAction(argparse.Action): 50 | def __init__(self, option_strings, dest, **kwargs): 51 | stages = Main.stages 52 | self.stagenames = [s.stagename for s in stages] 53 | self.nargs = 2 54 | defaults = {} 55 | kwargs['default'] = defaults 56 | super(SubstageNameAction, self).__init__(option_strings, 57 | dest, **kwargs) 58 | 59 | def __call__(self, parser, namespace, values, option_string=None): 60 | stagename = values[0] 61 | policy_name = values[1] 62 | if stagename not in self.stagenames: 63 | raise argparse.ArgumentError(self, 64 | "%s not a valid stage, must be one of %s" % 65 | (stagename, 66 | str(self.stagenames))) 67 | getattr(namespace, self.dest)[stagename] = policy_name 68 | if plugin is not None: 69 | cmds = None 70 | else: 71 | cmds = parser.add_mutually_exclusive_group() 72 | cmds.add_argument('-c', '--create', 73 | help='Create new result directory for ' 74 | 'current instance of ' 75 | 'software specified in configuration', 76 | action='store_true', default=False) 77 | 78 | cmds.add_argument('-I', '--import_policy', 79 | help="Import policy (substage and region definitions) into instance. ' \ 80 | 'Requires stage name, path to file containing proposed substages, " 81 | "and path to file containing region info", 82 | nargs="*", default={}, 83 | action=SubstageFileAction) 84 | cmds.add_argument('-L', '--list_policies', default=False, 85 | action="store_true", 86 | help="Lists instance's imported policies",) 87 | 88 | cmds.add_argument('-B', '--build_software', 89 | help="Name of software to clean, update git tree, " 90 | "and build (openocd, u-boot, qemu))", 91 | action="append", default=[]) 92 | cmds.add_argument('-b', '--print_build_commands', 93 | help="Print build commands for software", 94 | action="append") 95 | cmds.add_argument('-S', '--setup_trace', action="store_true", 96 | default=False) 97 | cmds.add_argument('-R', '--run_new_trace', action="store_true", 98 | default=False) 99 | cmds.add_argument('-D', '--do_trace', action="store_true", 100 | help="Performs trace if not already performed", 101 | default=False) 102 | cmds.add_argument('--list_instances', action="store_true", 103 | default=False) 104 | cmds.add_argument('-l', '--list_test_runs', action="store_true", 105 | default=False) 106 | cmds.add_argument('-p', '--postprocess_trace', default=[], 107 | action="append", 108 | choices=manager.PostTraceLoader.supported_types, 109 | help="Run trace postprocessing command") 110 | cmds.add_argument('-P', '--print_trace_commands', 111 | action='store_true', 112 | help='Prints commands used to produce trace') 113 | 114 | parser.add_argument('-i', '--select_instance', 115 | help='Name test instance use, " \ 116 | "by default we use newest', 117 | action='store', default=None) 118 | 119 | c = Main.get_hardwareclass_config() 120 | if not isinstance(c.default_host, str): 121 | raise Exception("check your configuration, default host should be a string") 122 | host = Main.object_config_lookup("HostConfig", c.default_host) 123 | parser.add_argument('-t', '--select_trace', action="store", 124 | help="Select existing instance's trace by name") 125 | parser.add_argument("-T", "--trace_methods", action="append", 126 | choices=[m.name for m in 127 | Main.object_config_lookup("TraceMethod")]) 128 | parser.add_argument('-s', '--stages', action='append', default=[]) 129 | parser.add_argument('-n', '--select_policy', 130 | action=SubstageNameAction, nargs=2) 131 | parser.add_argument('-H', '--host', action="store", default=host.name, 132 | choices=[m.name for m in 133 | Main.object_config_lookup("HostConfig")]) 134 | parser.add_argument('-v', '--verbose', action="store_true") 135 | parser.add_argument('-k', '--keep_temp_files', action="store_true") 136 | parser.add_argument('-q', '--quick', 137 | help='Try to skip some steps to be faster', 138 | action='store_true', default=False) 139 | for (n, ks) in arg_parsers: 140 | parser.add_argument(*n, **ks) 141 | args = parser.parse_args() 142 | args.hook = "" 143 | other = None 144 | cmd = None 145 | for c in list(run_cmds.cmds): 146 | n = c._name_ 147 | if (plugin is None) and getattr(args, n, None): 148 | cmd = c 149 | break 150 | else: 151 | setattr(args, n, None) 152 | if plugin is not None: 153 | cmd = run_cmds.cmds.hook 154 | if "all" in args.stages: 155 | args.stages = Main.stages 156 | elif not args.stages: 157 | args.stages = [s.stagename for s in Main.default_stages] 158 | if args.print_build_commands or args.build_software: 159 | print args.print_build_commands 160 | if args.print_build_commands is None: 161 | args.print_build_commands = [] 162 | if args.build_software is None: 163 | args.build_software = [] 164 | other = args.print_build_commands + args.build_software 165 | if args.import_policy: 166 | policy = args.import_policy 167 | else: 168 | policy = args.import_policy 169 | if not args.trace_methods: 170 | args.trace_methods = host.default_tracing 171 | 172 | self.parser = parser 173 | self.args = args 174 | self.other = other 175 | self.cmd = cmd 176 | self.policy = policy 177 | self._tm = None 178 | self.plugin = plugin 179 | 180 | def task_manager(self): 181 | if self._tm is None: 182 | self._tm = doit_manager.TaskManager(self.cmd, 183 | self.args.select_instance, 184 | self.args.select_trace, 185 | self.args.host, 186 | self.args.trace_methods, 187 | self.args.stages, 188 | self.policy, 189 | self.args.postprocess_trace, 190 | not self.args.keep_temp_files, 191 | self.args.quick, 192 | self.other, 193 | self.args.verbose, 194 | self.plugin) 195 | return self._tm 196 | -------------------------------------------------------------------------------- /fiddle_extra/find_write_loops.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import fiddle.staticanalysis 3 | import fiddle.r2_keeper as r2 4 | 5 | import sys 6 | from fiddle.staticanalysis import LongWriteInfo 7 | 8 | class LongWriteInfoOld(): 9 | 10 | #@classmethod 11 | #def from_start_end(cls, elf, start, end, thumb): 12 | # # get basic block 13 | # return cls(elf, branch_ins["offset"], thumb) 14 | 15 | def __init__(self, elf, start, end, thumb): 16 | self.valid = True 17 | self.start_ins = None 18 | self.start_ins_addr = None 19 | self.write_ins = None 20 | self.write_ins_addr = None 21 | self.finish_ins = None 22 | self.finish_ins_addr = None 23 | self.start = start 24 | self.end = end 25 | self.elf = elf 26 | self.thumb = thumb 27 | self.valid = False 28 | self.branch_ins_addr = None 29 | self.branch_ins = None 30 | 31 | # grab basic blocks surrounding this region 32 | r2.gets(elf, "s 0x%x" % self.start) 33 | if self.thumb: # force r2 to use the correct instruction size. sigh. 34 | r2.gets(elf, "ahb 16") 35 | r2.gets(elf, "e asm.bits=16") 36 | else: 37 | r2.gets(elf, "ahb 32") 38 | r2.gets(elf, "e asm.bits=32") 39 | self.bbs = r2.get(elf,"pdbj") 40 | next = self.bbs[-1]["offset"] + self.bbs[-1]["size"] 41 | while next < end: 42 | r2.get(elf, "s 0x%x" % next) 43 | self.bbs.extend(r2.get(elf, "pdbj")) 44 | next = self.bbs[-1]["offset"] + self.bbs[-1]["size"] 45 | # grab one more basic block 46 | r2.get(elf, "s 0x%x" % next) 47 | #print r2.gets(elf, "pdb") 48 | self.bbs.extend(r2.get(elf, "pdbj")) 49 | 50 | # lookup write instruction 51 | nwrites = 0 52 | for i in self.bbs: 53 | mne = i["opcode"].split()[0] 54 | 55 | if fiddle.staticanalysis.InstructionAnalyzer._is_mne_memstore(mne): 56 | nwrites += 1 57 | if (self.start <= i["offset"]) and (i["offset"] < self.end): 58 | if self.write_ins is not None: 59 | # if there are two write instruction in basic block, don't know what to do 60 | self.valid = False 61 | break 62 | else: 63 | self.write_ins = i 64 | self.valid = True 65 | self.write_ins_addr = self.write_ins["offset"] 66 | if nwrites > 1: 67 | print "Warning: %s write ins in these blocks" % nwrites 68 | if not self.valid: 69 | return 70 | 71 | # look for branch after write to find loop 72 | branch = None 73 | unconditional = False 74 | for b in self.bbs: 75 | if b["offset"] < start: 76 | # if "xrefs" in b.keys() and not self.branch_ins_addr: 77 | # self.branch_ins_addr = b["xrefs"][0]["addr"] 78 | continue 79 | if b["type"] == u"cjmp" or b["type"] == u"jmp": 80 | if b["type"] == "jmp": 81 | dst = b["jump"] 82 | r2.gets(elf, "s 0x%x" % dst) 83 | branch = r2.get(elf, "pdj 1")[0] 84 | self.finish_ins_addr = branch["offset"] + branch["size"] 85 | else: 86 | branch = b 87 | jump = branch["jump"] 88 | if jump not in [ii["offset"] for ii in self.bbs]: 89 | self.finish_ins_addr = jump 90 | #if not self.branch_ins_addr: 91 | # self.start_ins_addr = self.finish_ins_addr 92 | else: 93 | self.finish_ins_addr = branch["offset"] + branch["size"] 94 | #if not self.branch_ins_addr: 95 | # self.write_ins_addr 96 | 97 | 98 | r2.gets(elf, "s 0x%x" % self.finish_ins_addr) 99 | self.finish_ins = r2.get(elf, "pdj 1")[0] 100 | #r2.gets(elf, "s 0x%x" % self.branch_ins_addr) 101 | #self.branch_ins = r2.get(elf, "pdj 1")[0] 102 | #if not self.start_ins_addr: 103 | # self.start_ins_addr = self.branch_ins["jump"] 104 | self.start_ins_addr = self.write_ins_addr 105 | self.start_ins = self.write_ins 106 | #r2.get(elf, "s 0x%x" % self.start_ins_addr) 107 | #self.start_ins = r2.get(elf, "pdj 1")[0] 108 | 109 | #r2.gets(elf, "s 0x%x" % addr) 110 | #self.bbs = r2.get(elf,"pdbj") 111 | 112 | 113 | 114 | # if self.branch_ins["jump"] < self.branch_ins["offset"]: 115 | # # if this branches backwards, then the copying is finished once the next instruction is executed 116 | # if self.branch_ins["type"] == "cjmp": # if conditional jump 117 | # self.valid = True 118 | # self.finish_ins_addr = self.branch_ins["offset"] + self.branch_ins["size"] 119 | # else: 120 | # self.finish_ins_addr = None 121 | 122 | # else: 123 | # # otherwise destination of branch indicates test to determine if it is finished with loop 124 | # if self.branch_ins["type"] == "jmp": # if not conditional jump 125 | # dst = self.branch_ins["jump"] 126 | # r2.gets(elf, "s 0x%x" % dst) 127 | # bb = r2.get(elf, "pdbj") 128 | # r2.gets(elf, "pdb") 129 | # for i in bb: 130 | # if i["type"] == "cjmp": 131 | # jump = i["jump"] 132 | # offset = i["offset"] 133 | # self.branch_ins = i 134 | # self.valid = True 135 | # if jump not in [ii["offset"] for ii in self.bbs]: 136 | # # if does not jump back into loop 137 | # self.finish_ins_addr = jump 138 | # else: 139 | # # where it goes if it fails is the next instruction 140 | # self.finish_ins_addr = offset + i["size"] 141 | # break 142 | # elif i["type"] == "jmp": 143 | # break 144 | 145 | 146 | 147 | def __repr__(self): 148 | if not self.valid: 149 | return "" % self.start_ins_addr 150 | else: 151 | return "" % (self.start_ins_addr, self.write_ins_addr, self.finish_ins_addr) 152 | 153 | # def __repr__(self): 154 | # return str(self) 155 | 156 | def longwrite_info(elf, addr, thumb): 157 | l = LongWriteInfo(elf, start, end, thumb) 158 | return l 159 | 160 | def analyze(elf): 161 | r2.get(elf, "aas") 162 | tsts = [(0x40208b6e, True), 163 | (0x402025c0, False), # bss 164 | (0x40208b24, True), # memset 165 | # (0x40208b30, True), # memset, just 8 bits at most 166 | (0x40208b50, True), # memcpy 167 | (0x40206de0, True)] # mmc_read_data 168 | 169 | #for i in tsts: 170 | # l = longwrite_info(elf, i[0], i[1]) 171 | # print l 172 | 173 | tsts = [(0x40208b1e, 0x40208b20, True), # memset 174 | (0x40208b50, 0x40208b54, True), # memcpy 175 | (0x40206dd6, 0x40206dde, True), # mmc_read_data 176 | (0x402025c4, 0x402025c8, False) # clearbss 177 | ] # mmc_read_data 178 | for i in tsts: 179 | l = LongWriteInfo(elf, *i) 180 | print l 181 | 182 | 183 | 184 | def run(): 185 | if len(sys.argv) < 2: 186 | exit(0) 187 | elf = sys.argv[1] 188 | analyze(elf) 189 | 190 | if __name__ == "__main__": 191 | run() 192 | 193 | 194 | # / (fcn) sym._main_finish 56 195 | # | sym._main_finish (); 196 | # | ; CALL XREF from 0x40201436 (sym.board_init_f) 197 | # | 0x402025a4 610400fa blx sym.spl_relocate_stack_gd 198 | # | 0x402025a8 000050e3 cmp r0, 0 199 | # | 0x402025ac 00d0a011 movne sp, r0 200 | # ;-- $d: 201 | # | ,=< 0x402025b0 ffffffea b sym.clear_bss 202 | # | ;-- clear_bss: 203 | # | | ; CALL XREF from 0x402008ac (loc._d_21) 204 | # | `-> 0x402025b4 24009fe5 ldr r0, [0x402025e0] ; [0x402025e0:4]=0x80000000 obj.__bss_start 205 | # | 0x402025b8 24109fe5 ldr r1, [0x402025e4] ; [0x402025e4:4]=0x80030144 obj.__bss_end 206 | # | 0x402025bc 0020a0e3 mov r2, 0 207 | # ;-- clbss_l: 208 | # | .-> 0x402025c0 010050e1 cmp r0, r1 209 | # | : 0x402025c4 00208035 strlo r2, [r0] 210 | # | : 0x402025c8 04008032 addlo r0, r0, 4 211 | # | `=< 0x402025cc fbffff3a blo loc.clbss_l 212 | # | 0x402025d0 0900a0e1 mov r0, sb 213 | # | 0x402025d4 2c1099e5 ldr r1, [sb, 0x2c] 214 | # \ 0x402025d8 08f09fe5 ldr pc, [0x402025e8] ; [0x402025e8:4]=0x40203685 215 | 216 | 217 | # / (fcn) sym.memcpy 60 218 | # | sym.memcpy (); 219 | # | ; XREFS: CALL 0x40204ad2 CALL 0x40204b04 CALL 0x402061b4 220 | # | ; XREFS: CALL 0x40207a02 CALL 0x40207a7c CALL 0x40207bc2 221 | # | ; XREFS: CALL 0x40207bf8 CALL 0x40207c2a CALL 0x40207dc4 222 | # | ; XREFS: CALL 0x40208110 CALL 0x402081b4 CALL 0x402081d2 223 | # | ; XREFS: CALL 0x4020827e CALL 0x402084ea CALL 0x40208bee 224 | # | ; XREFS: CALL 0x40208bf8 225 | # | 0x40208b3c 8842 cmp r0, r1 226 | # | ,=< 0x40208b3e 1ad0 beq 0x40208b76 227 | # | | 0x40208b40 10b4 push {r4} 228 | # | | 0x40208b42 40ea0103 orr.w r3, r0, r1 229 | # | | 0x40208b46 13f0030f tst.w r3, 3 ; 3 230 | # | ,==< 0x40208b4a 07d0 beq 0x40208b5c 231 | # | || 0x40208b4c 0346 mov r3, r0 232 | # | ,===< 0x40208b4e 0ee0 b 0x40208b6e 233 | # | .----> 0x40208b50 0c68 ldr r4, [r1] 234 | # | :||| 0x40208b52 1c60 str r4, [r3] 235 | # | :||| 0x40208b54 043a subs r2, 4 236 | # | :||| 0x40208b56 0431 adds r1, 4 237 | # | :||| 0x40208b58 0433 adds r3, 4 238 | # | ,=====< 0x40208b5a 00e0 b 0x40208b5e 239 | # | |:|`--> 0x40208b5c 0346 mov r3, r0 240 | # | |:| | ; CODE XREF from 0x40208b5a (sym.memcpy) 241 | # | `-----> 0x40208b5e 032a cmp r2, 3 ; 3 242 | # | `====< 0x40208b60 f6d8 bhi 0x40208b50 243 | # | |,==< 0x40208b62 04e0 b 0x40208b6e 244 | # | .----> 0x40208b64 0a78 ldrb r2, [r1] 245 | # | :||| 0x40208b66 1a70 strb r2, [r3] 246 | # | :||| 0x40208b68 2246 mov r2, r4 247 | # | :||| 0x40208b6a 0131 adds r1, 1 248 | # | :||| 0x40208b6c 0133 adds r3, 1 249 | # | :||| ; CODE XREF from 0x40208b4e (sym.memcpy) 250 | # | :||| ; CODE XREF from 0x40208b62 (sym.memcpy) 251 | # | :``--> 0x40208b6e 541e subs r4, r2, 1 252 | # | : | 0x40208b70 002a cmp r2, 0 253 | # | `====< 0x40208b72 f7d1 bne 0x40208b64 254 | # | | 0x40208b74 10bc pop {r4} 255 | # \ `-> 0x40208b76 7047 bx lr 256 | # [0x40208b3c]> 257 | -------------------------------------------------------------------------------- /fiddle/doit_manager.py: -------------------------------------------------------------------------------- 1 | # MIT License 2 | 3 | # Copyright (c) 2017 Rebecca ".bx" Shapiro 4 | 5 | # Permission is hereby granted, free of charge, to any person obtaining a copy 6 | # of this software and associated documentation files (the "Software"), to deal 7 | # in the Software without restriction, including without limitation the rights 8 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | # copies of the Software, and to permit persons to whom the Software is 10 | # furnished to do so, subject to the following conditions: 11 | 12 | # The above copyright notice and this permission notice shall be included in all 13 | # copies or substantial portions of the Software. 14 | 15 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | # SOFTWARE. 22 | 23 | import external_source_manager 24 | import instrumentation_results_manager as manager 25 | from doit.cmd_base import ModuleTaskLoader, TaskLoader 26 | from doit.doit_cmd import DoitMain 27 | from doit.cmd_base import Command 28 | from doit.action import CmdAction 29 | from config import Main 30 | import pure_utils 31 | import sys 32 | import os 33 | import glob 34 | import difflib 35 | import yaml 36 | from datetime import datetime 37 | from enum import Enum 38 | from run_cmds import cmds 39 | import reporter 40 | import logging 41 | 42 | 43 | 44 | class ManagerException(Exception): 45 | pass 46 | 47 | 48 | class TaskManager(): 49 | loaders = [] 50 | tasks = {} 51 | def __init__(self, command, instance, trace, host=None, 52 | trace_list=[], stages=[], 53 | policies={}, post_trace_processes=[], 54 | rm_dir=True, quick=False, args=None, 55 | verbose=False, plugin=None): 56 | self.verbose = verbose 57 | self.DOIT_CONFIG = {'reporter': reporter.FiddleReporter} 58 | Main.verbose = self.verbose 59 | # shd = logging.StreamHandler() 60 | # shd.setLevel(logging.DEBUG) 61 | # shi = logging.StreamHandler() 62 | # shi.setLevel(logging.INFO) 63 | # formatter = logging.Formatter('%(message)s') 64 | # shd.setFormatter(formatter) 65 | # shi.setFormatter(formatter) 66 | # import sys 67 | # shd.stream = sys.stderr 68 | # shi.stream = sys.stdout 69 | # logging.getLogger().addHandler(shd) 70 | # logging.getLogger().addHandler(shi) 71 | 72 | if Main.verbose: 73 | logging.basicConfig(level=logging.DEBUG) 74 | v = 2 75 | else: 76 | logging.basicConfig(level=logging.INFO) 77 | v = 0 78 | Main.verbosity = v 79 | # self.DOIT_CONFIG['verbosity'] = v 80 | 81 | if command == cmds.list_instances: 82 | logging.info("Test instances") 83 | for i in self._get_all_ids(): 84 | path = os.path.join(Main.test_data_path, i) 85 | time = os.stat(i).st_ctime 86 | timestr = datetime.fromtimestamp(time).strftime('%Y-%m-%d %H:%M:%S') 87 | logging.info("%s\t%s" % (timestr, i)) 88 | return 89 | 90 | stages = [Main.stage_from_name(s) for s in stages] 91 | printonly = True if command in [cmds.print_build_commands, cmds.build_software] else False 92 | build = args if printonly else [] 93 | if command == cmds.create: 94 | build = [Main.target_software.name] 95 | 96 | self.src_manager = external_source_manager.SourceLoader(build, printonly) 97 | self.loaders.append(self.src_manager) 98 | if command in [cmds.print_build_commands, cmds.build_software]: 99 | return 100 | 101 | self.target_task = [s for s in self.src_manager.code_tasks 102 | if s.build_cfg.name == Main.target_software.name][0] 103 | 104 | if command == cmds.create: 105 | if not self.target_task.has_nothing_to_commit(): 106 | self.target_task.commit_changes() 107 | (instance_id, gitinfo) = self._calculate_current_id() 108 | else: 109 | self.target_task.build.uptodate = [True] 110 | gitinfo = None 111 | create_trace = False 112 | if instance is None: 113 | instance_id = self._get_newest_id() 114 | else: 115 | ids = self._get_all_ids() 116 | if instance not in ids: 117 | instance = difflib.get_close_matches(instance, 118 | ids, 1, 0)[0] 119 | instance_id = os.path.basename(instance) 120 | else: 121 | instance_id = instance 122 | if command == cmds.list_test_runs: 123 | logging.info("-- Test traces for instance '%s' --" % instance_id) 124 | for (time, name, ran) in manager.TraceTaskPrepLoader._existing_trace_ids(instance_id, True): 125 | logging.info("%s\t%s (data collected: %s)" % (time, name, ran)) 126 | return 127 | if command in [cmds.run_new_trace, cmds.setup_trace]: 128 | # create a new trace 129 | create_trace = True 130 | trace = [] 131 | elif command in [cmds.list_policies, cmds.import_policy, 132 | cmds.do_trace, cmds.print_trace_commands, 133 | cmds.postprocess_trace, cmds.hook]: 134 | create_trace = False 135 | # lookup existing trace 136 | trace_id = manager.TraceTaskPrepLoader.get_trace_name(instance_id, 137 | trace, 138 | create=create_trace) 139 | run_trace = False 140 | if command in [cmds.run_new_trace, cmds.do_trace]: 141 | run_trace = True 142 | if not policies: 143 | policies = manager.PolicyTaskLoader.default_policies(instance_id, stages) 144 | self.ti = manager.InstrumentationTaskLoader(self.target_task, 145 | instance_id, 146 | self, 147 | command == cmds.create, 148 | gitinfo, 149 | rm_dir) 150 | 151 | if command == cmds.create: 152 | self.pt = manager.PolicyTaskLoader(False, policies) 153 | self.loaders.append(manager.task_manager(verbose)) 154 | return 155 | 156 | if command in [cmds.list_policies, cmds.import_policy]: 157 | self.pt = manager.PolicyTaskLoader(command == cmds.import_policy, 158 | policies) 159 | if cmds.import_policy: 160 | self.loaders.append(manager.task_manager(verbose)) 161 | return 162 | logging.info("-- Avilable policies for instance '%s' --" % instance_id) 163 | for s in Main.stages: 164 | logging.info("Stage %s:" % s.stagename) 165 | rt = self.pt._policy_root(s) 166 | for f in glob.glob("%s/*" % rt): 167 | if os.path.isdir(f): 168 | t = os.stat(f).st_ctime 169 | timestr = datetime.fromtimestamp(t).strftime('%Y-%m-%d %H:%M:%S') 170 | logging.info("%s\t%s" % (timestr, os.path.basename(f))) 171 | return 172 | 173 | self.tp = manager.TraceTaskPrepLoader(trace_id, 174 | create_trace, 175 | run_trace, 176 | command == cmds.print_trace_commands, 177 | stages, 178 | trace_list, 179 | host, 180 | False) 181 | self.pt = manager.PolicyTaskLoader(command == cmds.import_policy, 182 | policies) 183 | self.rt = manager.TraceTaskLoader(create_trace, 184 | run_trace, 185 | command == cmds.print_trace_commands, 186 | quick) 187 | if command in [cmds.postprocess_trace, cmds.hook]: 188 | self.ppt = manager.PostTraceLoader(post_trace_processes, 189 | True, 190 | command is cmds.hook, 191 | plugin) 192 | 193 | else: 194 | self.ppt = None 195 | 196 | self.loaders.append(manager.task_manager(instance_id)) 197 | 198 | def _get_all_ids(self): 199 | root = Main.test_data_path 200 | return glob.glob(root + "/*") 201 | 202 | def _get_newest_id(self): 203 | choices = self._get_all_ids() 204 | newest = None 205 | newest_time = 0 206 | for i in choices: 207 | if not os.path.isdir(i): 208 | continue 209 | itime = os.stat(i).st_ctime 210 | if itime > newest_time: 211 | newest = i 212 | newest_time = itime 213 | n = os.path.basename(newest) 214 | return n 215 | 216 | def _calculate_current_id(self): 217 | (gitid, sha) = self.target_task.get_gitinfo() 218 | ccpath = self.target_task.build_cfg.compiler 219 | #ccpath = "%s%s" % (cc, cc_name) 220 | if hasattr(Main.target, "makecfg"): 221 | defconfig = Main.target.makecfg 222 | else: 223 | defconfig = "" 224 | hwclass = Main.get_hardwareclass_config().name 225 | targetsoftware = self.target_task.build_cfg.name 226 | ccinfo = pure_utils.file_md5(ccpath) 227 | gitinfo = {'local': self.target_task.build_cfg.root, 228 | 'sha1': sha} 229 | return ("%s.%s.%s.%s.%s" % (hwclass, targetsoftware, defconfig, gitid, ccinfo), 230 | gitinfo) 231 | 232 | def build(self, targets, do_build=True): 233 | if do_build: 234 | makes = [external_source_manager.CodeTask.get_task_name(b, "build") for b in targets] 235 | self.run(makes) 236 | return [] 237 | else: 238 | rets = [] 239 | for t in self.src_manager.code_tasks: 240 | if t.basename in targets: 241 | rets.append(t) 242 | return rets 243 | 244 | 245 | def run(self, cmds): 246 | tasks = {} 247 | for v in self.loaders: 248 | for name, l in v.list_tasks(): 249 | f = l 250 | tasks[name] = f 251 | 252 | tasks['DOIT_CONFIG'] = self.DOIT_CONFIG 253 | ml = ModuleTaskLoader(tasks) 254 | main = DoitMain(ml) 255 | main.config['default_tasks'] = cmds 256 | return main.run([]) 257 | 258 | def create_test_instance(self): 259 | nm = self.ti.get_build_name() 260 | logging.debug("about to run %s" % nm) 261 | ret = self.run([nm]) 262 | return ret 263 | 264 | def import_policy(self): 265 | #tp = self.tp.get_build_name() 266 | nm = self.pt.get_build_name() 267 | logging.debug("about to run %s" % nm) 268 | ret = self.run([nm]) 269 | return ret 270 | 271 | def run_trace(self): 272 | ti = self.ti.get_build_name() 273 | tp = self.tp.get_build_name() 274 | nm = self.rt.get_build_name() 275 | ip = self.pt.get_build_name() 276 | logging.debug("about to run %s" % nm) 277 | ret = self.run([ti, tp, ip, nm]) 278 | return ret 279 | 280 | def postprocess_trace(self): 281 | nm = self.ppt.get_build_name() 282 | logging.debug("about to run %s" % nm) 283 | ret = self.run([nm]) 284 | return ret 285 | -------------------------------------------------------------------------------- /fiddle_gdb/calltrace.py: -------------------------------------------------------------------------------- 1 | # MIT License 2 | 3 | # Copyright (c) 2017 Rebecca ".bx" Shapiro 4 | 5 | # Permission is hereby granted, free of charge, to any person obtaining a copy 6 | # of this software and associated documentation files (the "Software"), to deal 7 | # in the Software without restriction, including without limitation the rights 8 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | # copies of the Software, and to permit persons to whom the Software is 10 | # furnished to do so, subject to the following conditions: 11 | 12 | # The above copyright notice and this permission notice shall be included in all 13 | # copies or substantial portions of the Software. 14 | 15 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | # SOFTWARE. 22 | 23 | import gdb 24 | import os 25 | import re 26 | import sys 27 | # sys.path.append(os.path.join(sys.prefix, "lib/python2.7/site-packages")) 28 | import pure_utils 29 | import gdb_tools 30 | 31 | open_log = None 32 | now = False 33 | 34 | 35 | class CloseLog(): 36 | def __init__(self): 37 | global now 38 | if now: 39 | self.do() 40 | pass 41 | 42 | def do(self): 43 | global open_log 44 | if open_log is not None: 45 | open_log.close() 46 | open_log = None 47 | 48 | def __call__(self): 49 | global now 50 | if not now: 51 | self.do() 52 | 53 | 54 | class WriteResults(): 55 | def __init__(self, depth, name, kind, pc, line, count, minimal=False): 56 | self.line = line 57 | self.depth = depth 58 | self.name = name 59 | self.kind = kind 60 | self.pc = pc 61 | self.call_count = count 62 | self.entry = True if kind == "entry" else False 63 | self.minimal = minimal 64 | global now 65 | if now: 66 | self.do() 67 | 68 | def do(self): 69 | global open_log 70 | if self.entry: 71 | c = " > " 72 | else: 73 | c = " < " 74 | outstr = ("*" * (self.depth + 1)) + c + self.name 75 | if (not self.entry) and (not self.minimal): 76 | outstr += "@0x%x" % self.pc 77 | if self.line: 78 | outstr += " [[%s]]" % self.line 79 | outstr += "(%d)\n" % self.call_count 80 | if open_log: 81 | open_log.write(outstr) 82 | else: 83 | gdb.write(outstr, gdb.STDOUT) 84 | 85 | def __call__(self): 86 | global now 87 | if not now: 88 | self.do() 89 | 90 | 91 | class CallExitBreak(gdb_tools.TargetFinishBreakpoint): 92 | plugin_name = "calltrace" 93 | 94 | def __init__(self, name, controller, stage, entry, depth): 95 | self.name = name 96 | self.entry = entry 97 | self.plugin = controller.get_plugin(self.plugin_name) 98 | self.controller = controller 99 | self.valid = True 100 | self.depth = depth 101 | try: 102 | gdb_tools.TargetFinishBreakpoint.__init__(self, controller, True, stage) 103 | except ValueError: 104 | self.valid = False 105 | 106 | def out_of_scope(self): 107 | if self.entry.no_rec: 108 | self.controller.disable_breakpoint(self.entry, disable=False) 109 | self.controller.gdb_print("exit breakpoint for %s out of scope\n" % self.name, 110 | self.plugin.name) 111 | #print "exit breakpoint for %s out of scope\n" % self.name 112 | self.controller.depth = self.depth - 1 113 | 114 | def _stop(self, bp, ret): 115 | c = self.plugin 116 | # don't print exit if it occurred out-of-order with repect 117 | # to other function exits 118 | #print "%s C.depth %s, %s" % (self.name, c.depth, self.depth) 119 | 120 | #if c.depth == self.depth: 121 | gdb.post_event(WriteResults(self.depth, 122 | self.name, "exit", c.pc(), 123 | "", self.controller.call_count, 124 | c._minimal)) 125 | c.depth = self.depth - 1 126 | self.controller.call_count += 1 127 | if self.entry.no_rec: 128 | self.controller.disable_breakpoint(self.entry, disable=False) 129 | return False 130 | 131 | 132 | class CallEntryBreak(gdb_tools.TargetBreak): 133 | plugin_name = "calltrace" 134 | 135 | @classmethod 136 | def settable(cls, name, c): 137 | try: 138 | i = gdb.execute("x/x %s" % name, to_string=True).split()[0] 139 | except gdb.error as e: 140 | #c.gdb_print("%s cannot set breakpoint for %s\n" % (e, 141 | # name), "calltrace") 142 | return False 143 | return True 144 | 145 | def __init__(self, name, controller, stage, no_rec): 146 | self.name = name 147 | self.no_rec = no_rec 148 | self.stage = stage 149 | self.depth = 0 150 | self.plugin = controller.subcommand_parsers[self.plugin_name].plugin 151 | try: 152 | i = gdb.execute("x/x %s" % self.name, to_string=True).split()[0] 153 | except gdb.error as e: 154 | controller.gdb_print("%s cannot set breakpoint for %s\n" % (e, 155 | self.name), 156 | self.plugin.name) 157 | return 158 | i = re.sub(':', '', i) 159 | self.fnloc = long(i, 0) 160 | spec = "*(0x%x)" % self.fnloc 161 | self.line = re.sub(":", 162 | "::", 163 | pure_utils.addr2line(self.fnloc, 164 | stage.elf)) if self.plugin._sourceinfo else "" 165 | gdb_tools.TargetBreak.__init__(self, spec, controller, True, stage) 166 | 167 | def _stop(self, bp, ret): 168 | c = self.plugin 169 | c.depth += 1 170 | self.depth = c.depth 171 | #print "> %s, %s" % (self.name, self.depth) 172 | e = CallExitBreak(self.name, self.controller, self.stage, self, self.depth) 173 | if not e.valid: 174 | self.controller.gdb_print("could not set exit breakpoint for %s\n" % self.name, 175 | self.plugin.name) 176 | 177 | gdb.post_event(WriteResults(self.depth, 178 | self.name, "entry", self.fnloc, 179 | self.line, 180 | self.controller.call_count, 181 | c._minimal)) 182 | self.controller.call_count += 1 183 | if self.no_rec and self.breakpoint: 184 | self.controller.disable_breakpoint(self, delete=False) 185 | return False 186 | 187 | 188 | class CallTrace(gdb_tools.GDBPlugin): 189 | def __init__(self): 190 | self.depth = -1 191 | self._minimal = True 192 | self._sourceinfo = False 193 | self.results_written = False 194 | self.blacklisted = {} 195 | self.no_rec_funs = [] 196 | self.stage_logs = {} 197 | bp_hooks = {'StageEndBreak': self.stop_end} 198 | 199 | parser_options = [ 200 | gdb_tools.GDBPluginParser("stage_log", 201 | [gdb_tools.GDBPluginParserArg("log_args", 202 | nargs="*", default=[])]), 203 | 204 | gdb_tools.GDBPluginParser("blacklist", 205 | [gdb_tools.GDBPluginParserArg("stage_args", 206 | nargs="*", default=[])]), 207 | gdb_tools.GDBPluginParser("no_recursion", 208 | [gdb_tools.GDBPluginParserArg("recfns", 209 | nargs="*", default=[])]), 210 | gdb_tools.GDBPluginParser("sourceinfo", 211 | [gdb_tools.GDBPluginParserArg("enabled", 212 | nargs="?", default=True)]), 213 | gdb_tools.GDBPluginParser("minimal", 214 | [gdb_tools.GDBPluginParserArg("disabled", nargs="?", 215 | default=False)]), 216 | 217 | ] 218 | gdb_tools.GDBPlugin.__init__(self, "calltrace", 219 | bp_hooks=bp_hooks, 220 | stage_hook=self.setup_breakpoints, 221 | exit_hook=self._gdb_exit, 222 | disabled_breakpoints=[ 223 | #"WriteBreak", 224 | #"LongwriteBreak", 225 | # "EndLongwriteBreak", 226 | # "SubstageEntryBreak"], 227 | ], 228 | parser_args=parser_options) 229 | 230 | def no_recursion(self, args): 231 | self.no_rec_funs.extend(args.recfns) 232 | 233 | def minimal(self, args): 234 | if args.disabled is False: 235 | self._minimal = False 236 | else: 237 | self._minimal = True 238 | 239 | def sourceinfo(self, args): 240 | if args.enabled is True: 241 | self._sourceinfo = True 242 | else: 243 | self._sourceinfo = False 244 | 245 | def stage_log(self, args): 246 | current = None 247 | nextstage = True 248 | for l in args.log_args: 249 | if nextstage: 250 | current = l 251 | nextstage = False 252 | elif l == "--": 253 | nextstage = True 254 | else: 255 | self.stage_logs[current] = l 256 | 257 | def blacklist(self, args): 258 | current = None 259 | nextstage = True 260 | for l in args.stage_args: 261 | if nextstage: 262 | current = l 263 | nextstage = False 264 | self.blacklisted[current] = [] 265 | elif l == "--": 266 | nextstage = True 267 | else: 268 | self.blacklisted[current].append(l) 269 | 270 | def pc(self): 271 | return self.controller.get_reg_value('pc') 272 | 273 | def setup_breakpoints(self, startbreak, stage): 274 | c = self.controller 275 | c.call_count = 1 276 | if not gdb.current_progspace().filename: 277 | gdb.execute("file %s" % stage.elf) 278 | sname = stage.stagename 279 | if (sname in self.stage_logs.iterkeys()) and self.stage_logs[sname]: 280 | global open_log 281 | open_log = open(self.stage_logs[sname], 'w') 282 | 283 | self.results_written = False 284 | functions = pure_utils.get_c_function_names(stage.elf) 285 | sname = stage.stagename 286 | hasblacklist = sname in self.blacklisted.iterkeys() 287 | for (name, addr) in functions: 288 | if (not hasblacklist) or (hasblacklist and 289 | (name not in self.blacklisted[stage.stagename])): 290 | norec = name in self.no_rec_funs 291 | if CallEntryBreak.settable(name, c): 292 | CallEntryBreak(name, c, stage, norec) 293 | 294 | def stop_end(self, bp, ret, c=None): 295 | if bp is None and c is None: 296 | c.gdb_print("quitting\n", 297 | self.name) 298 | if c is None: 299 | c = bp.controller 300 | 301 | if self.results_written: 302 | return ret 303 | global open_log 304 | if open_log: 305 | c.gdb_print("results written to %s\n" % open_log.name, self.name) 306 | self.results_written = True 307 | # use event to make sure log is closed after all write events 308 | gdb.post_event(CloseLog()) 309 | return ret 310 | 311 | def _gdb_exit(self, event): 312 | self.stop_end(None, True, self.controller) 313 | 314 | 315 | plugin_config = CallTrace() 316 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | OVERVIEW 2 | 3 | tldr; since most of this documentation is outdated 4 | To run an example on x86_64 linux after installing, arm and aarch64 assume you have arm-linux-gnueabihf-* installed in /usr, and qemu-arm/qemu-aarch64. uboot assumes you have unpacked gcc linaro to ~/software/gcc-linaro-5.2-2015.11-x86_64_arm-linux-gnueabihf/ and built the special qemu 5 | To use the uboot example you need to download the am37x technical reference from http://www.ti.com/lit/pdf/sprugn4 (TI's "AM/DM37x Multimedia Device Technical Reference Manual (Silicon Revision 1.x) (Rev. R)") 6 | and copy it to $(fiddle install path)/hw_info/bbxm/am37x_technical_reference.pdf 7 | you will also need to push a copy of the beagleboard base SD image from https://cs.dartmouth.edu/~bx/beagleboard-xm-orig.img 8 | to $(fiddle install path)/hw_info/bbxm/beagleboard-xm-orig.img 9 | 10 | To work with the uboot/BBxM example, you 11 | 12 | 1. Enter target's directory 13 | cd fiddle_examples/ 14 | 2. Tell fiddle to build example and construct static analysis databases (then go take a nap, it may take a while) 15 | I_CONF=test.cfg fiddle -c 16 | 3; (optional) tell fiddle to import a policy to run analysis on (below is an example for test_arm) 17 | I_CONF=test.cfg fiddle -I _single policies/example2/substages.yml policies/example2/regions.yml 18 | 4. tell fiddle to execute dynamic analysis (will also take some time) 19 | I_CONF=test.cfg fiddle -R 20 | 5. (only for arm targets and if policy imported) tell it to postprocess writes (calculate write blocks) 21 | I_CONF=test.cfg fiddle -p consolidate_writes 22 | 6. (only if #5 completed) tell it to check whether the last run had any policy violations 23 | I_CONF=test.cfg fiddle -p policy_check 24 | 25 | If you want to edit the policy, edit it and start from step 3. 26 | If you change the test case, restart process from step 2 27 | 28 | NB: This README is super out-of-date and will eventually be updated 29 | 30 | This test suite helps you keep track of different versions of 31 | u-boot/build tools, static analysis of that build's binaries, and 32 | runtime trace results of running that binary on a given hardware 33 | configuration. For each u-boot/build configuration it keeps a database 34 | of information it statically gathered for each boot stage, boot stage 35 | images/ELF files, a prepared SD card image, and test results of 36 | runtime trace analyses. If it detects changes in the u-boot source or 37 | build tools it will create a new set of test result directories with a 38 | new sdcard image and static analysis results. 39 | 40 | LARGE PICTURE/TOOLSUITE DESIGN 41 | 42 | A significant portion of this tool suite's codebase was written to 43 | support an manage muliple target (bootloader) versions/builds, 44 | emulator versions, hardware debugging tool builds/versions, and the 45 | dependencies between/among different dynamic analyses and analyses of 46 | data collected from an instrumented execution of the target. Hence 47 | this instrumentation suite has become the over-engineered monster with 48 | which you are now becoming acquainted. 49 | 50 | CONFIGURATION 51 | 52 | 53 | 54 | TEST RESULTS DIRECTORY STRUCTURE 55 | 56 | It stores the results in a directory tree in the following manner: 57 | 58 | / -- each subdirectory of the test data root contains results for a different u-boot/compiler configuration 59 | / -- contains the results of a single bootloader configuration 60 | //bootsuite.yaml -- contains a cached version of the configuration file (more on this file later) 61 | //u-boot--analysis.h5 -- static analyis information for a given stage stored in a pytables database 62 | //images -- contains copies of the boot stage ELF and raw images for this configuration as well as the sdcard image 63 | //trace-data -- contains reults from runtime analyses 64 | //trace-data/ -- a runtime trace analysis instance 65 | //trace-data//info.yamp -- information about this trace test instance 66 | //trace-data// -- results for a given hardware instance (ie. QemuBreakpointTrace, QemuWatchpointTrace) and any cached configuration files, log files, etc for this test session 67 | //trace-data///-traces.h5 -- a database of dynamically gathered store operations ("trace database") for the given stage 68 | //trace-data///-trace-histogram.txt -- A human-readable summary of trace information gathered during this run formatted as: 69 | 70 | pc=/[<"virtual address" of write in ELF >file, pre-relocation>] () lr= () [-] () -- -- 71 | 72 | 73 | As you may have noticed, all results are kept in 2 types of pytables 74 | databases (any human-readable results are extracted from these 75 | databses). To explore these databases, I would recommend opening each 76 | in a ipython shell and poking around. 77 | 78 | There is the static analysis database (defined in staticanalysis.py) which contains: 79 | a root group named "staticanalysis" 80 | within "/staticanalysis", 7 separate tables 81 | - "relocs" -- Manually generated relocation information (such as what ranges of addresses get relocated during stage and to where) 82 | - "writes" -- Lists every memory store instruction statically found in the binary's ".text" section. Includes information such as pc, if it's thumb, what registers we must read in order to calculate the destination of the write, how many bytes it writes 83 | - smcs -- Location of every "smc" instruction (needed for baremetal debugging so we don't try to step through ROM instructions) 84 | - srcs -- Information on a given instruction such as file/line number, disassmbly, address. 85 | - funcs -- Information on a given function (name, start and end addr) 86 | - longwrites -- Manually generated information on looped writes so that we can generate tracing results of this loop without breaking or stepping through every loop iteration 87 | - skips -- Manually generated information on instruction ranges the baremetal debugger should "skip" (not step through) 88 | 89 | We create a separate one of these databases per boot stage. 90 | 91 | The second type of database is a trace table -- generated from runtime tracing information. This table is defined in database.py. 92 | It has 1 group, which is its stage name. This one group contains 2 tables: 93 | - "writes": an entry for each store instruction that occurs at runtime. Includes information such as pc (both as seen in ELF and perhaps a different relocated address), destination of write, size of write, lr, cpsr 94 | - "writerange": a summary of the write tables that consolodates information on sequential writes (of which the writehistogram is generated from) 95 | 96 | 97 | [INSTALL] 98 | 99 | I recommend setting up a separate python environment for this 100 | toolsuite using a tool such as virtualenv/virtualenvwrapper. 101 | 102 | You can install the tools via: 103 | > setup.py install 104 | 105 | SOFTWARE REQUIREMENTS 106 | - git 107 | - capstone (next branch -- https://github.com/aquynh/capstone/tree/next, I tested this with this commit -- https://github.com/aquynh/capstone/tree/c5dce55db4907f8f3bd326fd96d0b6a613b18e66 ) 108 | - gdbm (GNU database library) 109 | - libgit2 110 | - gdb with python2 bindings (most newer versions of gdb support python, however some only have python3, not python2) 111 | - Python 2 (I recommend version 2.7.11) 112 | - The following python2 libraries: 113 | - doit (version 0.29.0) 114 | - PyYAML 115 | - pathlib 116 | - pygit2 117 | - ipython 118 | - intervaltree 119 | - tables 120 | - capstone 121 | - unicorn 122 | - numpy 123 | - sortedcontainers 124 | - pdfminer 125 | - munch 126 | - functools32 127 | - toml.py 128 | - (optional) ipython v5.7.0 (for embeeded console support) 129 | - (optional, only if working with qemu watchpoint) qemu's tracetool.py -- https://raw.githubusercontent.com/qemu/qemu/86b5aacfb972ffe0fa5fac6028e9f0bc61050dda/scripts/tracetool.py , which needs to be placed somewhere in your python path) 130 | 131 | 132 | 133 | (may not be complete, but will list new ones as they are found) 134 | 135 | This test suite is written for python2.7, so make sure that is the 136 | version of python you are using (it will not work with python 3) 137 | 138 | You will need sudo access on your machine if you are debugging a bootloader. 139 | 140 | 141 | [Test target configuration] 142 | 143 | Fiddle searches for and loads configuration file at ~/.fiddle.cfg or 144 | the value of the I_CONF environmental variable. Any missing 145 | configuration from this file will be extracted from configs/defaults.cfg. 146 | 147 | 148 | 149 | Main/root: the root directory of which most all other paths are 150 | calculate from (the one exception to this is openocd_cfg paths, 151 | Software binary paths, and Software root paths who have the 'path: 152 | full' option set). 153 | 154 | test_data_path: relative location from Main/root where test data 155 | should be stored 156 | 157 | test_suite_path: relative location from Main/root where the directory 158 | containing this readme is store (bootloader_test_suite) 159 | 160 | cc: absolute location of CROSS_COMPILER path 161 | 162 | HardwareClass/sdskeleton: path to a sdcard image for which the test 163 | suite can copy to install new bootstage images 164 | 165 | 166 | Other things you may want to set: 167 | 168 | Software configuration: 169 | For any given Software entry you may need to set the value of root. 170 | The value of root typically should be the relative path to that the 171 | root of that software's source code from Main/root. However if "path: 172 | cc" is set, then the path is relative from Main.cc. If "path: full" 173 | is set, then the path is relative to your system's root directory. 174 | 175 | Also you may end up needing to tweak the build settings for a given 176 | piece of software. Build configuration can often be tweaked by setting 177 | the software's build_prepare (if build is set to StandardBuilder). 178 | However some pieces of software need test suite configuration 179 | information to properly configure themselves. In these cases the 180 | build option is not set to StandardBuilder and there is a class of the 181 | build option's name in config.py that calculates the build information 182 | (example: the UBootBuilder class) 183 | 184 | Main/booloader: it's value corresponds to a 'name' of a Bootloader 185 | entry in the configuration. It allows you to select between different 186 | bootloader configurations. A bootloader configuration selects what 187 | configuration (defconfig) it should biuld against and what stages it 188 | supports. Each item a Bootloader entry lists under stages corresponds 189 | to a Bootstages class with that name. 190 | 191 | 192 | 193 | USAGE 194 | 195 | The test suite uses pydoit to manage data collection requirements. 196 | For example, the test suite needs to preprocess the bootloader binary 197 | before it is able to execute and instrument the bootloader. 198 | 199 | 200 | This creates the directories it needs for its test results directory, 201 | builds u-boot, creates a new sd image, and runs all static analysis. 202 | If a test directory with the same bootloader/build tools configuration 203 | exists it will first delete that directory. 204 | 205 | This testsuite will detect any changes in the u-boot source code or 206 | gcc (and in the future any gcc plugins) and create a new 207 | test_config_instance directory along with a new sdcard image and 208 | static analysis database when it is invoked. 209 | 210 | 211 | You must first invoke the test suite via: main.py -c 212 | - this will cause it to preprocess the bootloader using options specified in bootsuite.yaml 213 | 214 | Then if you execute main.py -r 215 | - It will execute, instrument, and collect data from the binary 216 | 217 | You can then postprocess collected data via 218 | - main.py -p consolidate_writes 219 | - main.py -p browse_db 220 | - main.py -p policy_check 221 | 222 | Try to not invoke multiple instances of main.py at once as it may mess 223 | up your test results. 224 | 225 | If any bulids fail you can either ask it to rebuild all software via: 226 | main.py -b 227 | 228 | Or you can ask it for the build commands it uses on a piece of software via 229 | main.py -B 230 | (sotware_name as it is written in the software's configuration in bootsuite.yaml) 231 | 232 | 233 | You can ask the test suite to print commands it uses to execute and instrument: 234 | main.py -p 235 | hardware (or emulator) and to run the tracing software. 236 | 237 | All tracing tools, with the exception of QEMU watchpoints, will 238 | directly import their tracing data into the trace database and create the 239 | human-readable writehistogram file. 240 | 241 | PYTHON MODULES TO INSTALL 242 | interval 243 | yamlconf 244 | pygit2 245 | pyinter 246 | ipython 247 | sortedcontainers 248 | numpy 249 | numexpr 250 | tables 251 | doit 252 | --------------------------------------------------------------------------------