diff --git a/zipr/.gitattributes b/zipr/.gitattributes new file mode 100644 index 0000000000000000000000000000000000000000..58fb9afa9738bb3698e452b8a5e67dc4a103bf3e --- /dev/null +++ b/zipr/.gitattributes @@ -0,0 +1,107 @@ +* text=auto !eol +/LICENSE.txt -text +/Makefile.in -text +/README.sleds.txt -text +/SConscript -text +/SConstruct -text +/config.guess -text +/config.sub -text +/configure -text +/configure.in -text +include/ehwrite.h -text +include/elfwrite.h -text +include/plugin_man.h -text +include/sled.h -text +include/unresolved.h -text +include/zipr_all.h -text +include/zipr_dollop_man.h -text +include/zipr_impl.h -text +include/zipr_mem_space.h -text +include/zipr_optimizations.h -text +include/zipr_stats.h -text +include/zipr_utils.h -text +/install-sh -text +src/SConscript -text +src/SConstruct -text +src/dollop.cpp -text +src/ehwrite.cpp -text +src/elfwrite.cpp -text +src/main.cpp -text +src/memory_space.cpp -text +src/plugin_man.cpp -text +src/utils.cpp -text +src/zipr.cpp -text +src/zipr_dollop_man.cpp -text +src/zipr_options.cpp -text +src/zipr_stats.cpp -text +test/MemorySpace.cpp -text +test/SConscript -text +test/SConstruct -text +test/ZiprDollop.cpp -text +test/ZiprOptions.cpp -text +test/ZiprRange.cpp -text +test/dylib/Makefile -text +test/dylib/dylib.c -text +test/dylib/dylib.h -text +test/dylib/dylib_test.c -text +test/dylib/math_test.c -text +test/dylib/readline_test.c -text +third_party/ELFIO/elfio-2.2/AUTHORS -text +third_party/ELFIO/elfio-2.2/COPYING -text +third_party/ELFIO/elfio-2.2/ChangeLog -text +third_party/ELFIO/elfio-2.2/INSTALL -text +third_party/ELFIO/elfio-2.2/Makefile -text +third_party/ELFIO/elfio-2.2/Makefile.am -text +third_party/ELFIO/elfio-2.2/Makefile.in -text +third_party/ELFIO/elfio-2.2/NEWS -text +third_party/ELFIO/elfio-2.2/README -text +third_party/ELFIO/elfio-2.2/aclocal.m4 -text +third_party/ELFIO/elfio-2.2/config.log -text +third_party/ELFIO/elfio-2.2/config.status -text +third_party/ELFIO/elfio-2.2/configure -text +third_party/ELFIO/elfio-2.2/configure.ac -text +third_party/ELFIO/elfio-2.2/depcomp -text +third_party/ELFIO/elfio-2.2/doc/elfio.pdf -text svneol=unset#unset +third_party/ELFIO/elfio-2.2/elfio/elf_types.hpp -text +third_party/ELFIO/elfio-2.2/elfio/elfio.hpp -text +third_party/ELFIO/elfio-2.2/elfio/elfio_dump.hpp -text +third_party/ELFIO/elfio-2.2/elfio/elfio_dynamic.hpp -text +third_party/ELFIO/elfio-2.2/elfio/elfio_header.hpp -text +third_party/ELFIO/elfio-2.2/elfio/elfio_note.hpp -text +third_party/ELFIO/elfio-2.2/elfio/elfio_relocation.hpp -text +third_party/ELFIO/elfio-2.2/elfio/elfio_section.hpp -text +third_party/ELFIO/elfio-2.2/elfio/elfio_segment.hpp -text +third_party/ELFIO/elfio-2.2/elfio/elfio_strings.hpp -text +third_party/ELFIO/elfio-2.2/elfio/elfio_symbols.hpp -text +third_party/ELFIO/elfio-2.2/elfio/elfio_utils.hpp -text +third_party/ELFIO/elfio-2.2/examples/Makefile -text +third_party/ELFIO/elfio-2.2/examples/Makefile.am -text +third_party/ELFIO/elfio-2.2/examples/Makefile.in -text +third_party/ELFIO/elfio-2.2/examples/elfdump/.deps/elfdump.Po -text +third_party/ELFIO/elfio-2.2/examples/elfdump/ELFDump.vcxproj -text +third_party/ELFIO/elfio-2.2/examples/elfdump/Makefile -text +third_party/ELFIO/elfio-2.2/examples/elfdump/Makefile.am -text +third_party/ELFIO/elfio-2.2/examples/elfdump/Makefile.in -text +third_party/ELFIO/elfio-2.2/examples/elfdump/elfdump -text +third_party/ELFIO/elfio-2.2/examples/elfdump/elfdump.cpp -text +third_party/ELFIO/elfio-2.2/examples/tutorial/.deps/tutorial.Po -text +third_party/ELFIO/elfio-2.2/examples/tutorial/Makefile -text +third_party/ELFIO/elfio-2.2/examples/tutorial/Makefile.am -text +third_party/ELFIO/elfio-2.2/examples/tutorial/Makefile.in -text +third_party/ELFIO/elfio-2.2/examples/tutorial/tutorial -text +third_party/ELFIO/elfio-2.2/examples/tutorial/tutorial.cpp -text +third_party/ELFIO/elfio-2.2/examples/write_obj/.deps/write_obj.Po -text +third_party/ELFIO/elfio-2.2/examples/write_obj/Makefile -text +third_party/ELFIO/elfio-2.2/examples/write_obj/Makefile.am -text +third_party/ELFIO/elfio-2.2/examples/write_obj/Makefile.in -text +third_party/ELFIO/elfio-2.2/examples/write_obj/write_obj -text +third_party/ELFIO/elfio-2.2/examples/write_obj/write_obj.cpp -text +third_party/ELFIO/elfio-2.2/examples/writer/.deps/writer.Po -text +third_party/ELFIO/elfio-2.2/examples/writer/Makefile -text +third_party/ELFIO/elfio-2.2/examples/writer/Makefile.am -text +third_party/ELFIO/elfio-2.2/examples/writer/Makefile.in -text +third_party/ELFIO/elfio-2.2/examples/writer/writer -text +third_party/ELFIO/elfio-2.2/examples/writer/writer.cpp -text +third_party/ELFIO/elfio-2.2/install-sh -text +third_party/ELFIO/elfio-2.2/missing -text +third_party/elfio-2.2.tar.gz -text diff --git a/zipr/.gitignore b/zipr/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..cc1ac4b5bb96df40a42270f50ba4bc406097db63 --- /dev/null +++ b/zipr/.gitignore @@ -0,0 +1,9 @@ +build +.sconsign.dblite +*.o +*.swp +test/MemorySpace.exe +test/Options.exe +test/Range.exe +scons_build +*.exe diff --git a/zipr/.gitlab-ci.yml b/zipr/.gitlab-ci.yml new file mode 100644 index 0000000000000000000000000000000000000000..0bc945542c7e7835af507db7090fbc3925780bb8 --- /dev/null +++ b/zipr/.gitlab-ci.yml @@ -0,0 +1,178 @@ +before_script: + - "source ~gitlab-runner/cicd_support/cicd_support.shinc" + + +after_script: + - "echo Test Complete." + +stages: + - clean + - build + - test + + + +# +# Cleaning +# + +#template +.do-clean: &do-nightly-clean + stage: clean + script: + - ./cicd_tests/do-clean.sh + +# per os items +do-nightly-clean-ubuntu18: + <<: *do-nightly-clean + tags: + - ubuntu18 + variables: + OS: 'ubuntu18' + +do-nightly-clean-ubuntu16: + <<: *do-nightly-clean + tags: + - ubuntu16 + variables: + OS: 'ubuntu16' + +do-nightly-clean-centos76: + <<: *do-nightly-clean + tags: + - centos76 + variables: + OS: 'centos76' + + +# +# building +# + + +# template +.do-build: &do-build + stage: build + script: + - ./cicd_tests/do-build.sh + + +# per os items +do-build-ubuntu18: + <<: *do-build + tags: + - ubuntu18 + variables: + OS: 'ubuntu18' + + +do-build-ubuntu16: + <<: *do-build + tags: + - ubuntu16 + variables: + OS: 'ubuntu16' + +do-build-centos76: + <<: *do-build + tags: + - centos76 + variables: + OS: 'centos76' + + + +# +# $PSZ ls +# + +# template +.xform-ls: &xform-ls + stage: test + script: + - ./cicd_tests/xform-ls.sh + +#per OS +xform-ls-ubuntu18: + <<: *xform-ls + tags: + - ubuntu18 + variables: + OS: 'ubuntu18' + +xform-ls-ubuntu16: + <<: *xform-ls + tags: + - ubuntu16 + variables: + OS: 'ubuntu16' + +xform-ls-centos76: + <<: *xform-ls + tags: + - centos76 + variables: + OS: 'centos76' + +# +# $PSZ cat +# + +# template +.xform-cat: &xform-cat + stage: test + script: + - ./cicd_tests/xform-cat.sh + +xform-cat-ubuntu18: + <<: *xform-cat + tags: + - ubuntu18 + variables: + OS: 'ubuntu18' + +xform-cat-ubuntu16: + <<: *xform-cat + tags: + - ubuntu16 + variables: + OS: 'ubuntu16' + +xform-cat-centos76: + <<: *xform-cat + tags: + - centos76 + variables: + OS: 'centos76' + +# +# run zipr internal tests +# + +# template +.internal-tests: &internal-tests + stage: test + script: + - ./cicd_tests/internal-tests.sh + +#per OS +internal-tests-ubuntu18: + <<: *internal-tests + tags: + - ubuntu18 + variables: + OS: 'ubuntu18' + +internal-tests-ubuntu16: + <<: *internal-tests + tags: + - ubuntu16 + variables: + OS: 'ubuntu16' + +internal-tests-centos76: + <<: *internal-tests + tags: + - centos76 + variables: + OS: 'centos76' diff --git a/zipr/LICENSE b/zipr/LICENSE new file mode 100644 index 0000000000000000000000000000000000000000..7ab9c0ee27c2b4f09b23feafcf3ce802bb59665f --- /dev/null +++ b/zipr/LICENSE @@ -0,0 +1,29 @@ +BSD 3-Clause License + +Copyright (c) 2014-2021, Zephyr Software LLC +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/zipr/Makefile.in b/zipr/Makefile.in new file mode 100644 index 0000000000000000000000000000000000000000..dcaebfdc56e620c2a56f5b61c45e0e875f2702df --- /dev/null +++ b/zipr/Makefile.in @@ -0,0 +1,16 @@ + + + +bdir=@prefix@ + +all: + cd src;make + cd test;make + +install: all + cp src/zipr.exe $(bdir)/bin + if [ -f src/libzipr.a ]; then cp src/libzipr.a $(bdir)/lib; fi + +clean: + cd src;make clean + cd test;make clean diff --git a/zipr/README.sleds.txt b/zipr/README.sleds.txt new file mode 100644 index 0000000000000000000000000000000000000000..2bf809485e2788ef0b729cd60984a010d044864b --- /dev/null +++ b/zipr/README.sleds.txt @@ -0,0 +1,196 @@ +Using sleds for handling consecutive pins. + +Consider that there are consecutive pins at 00, 01, 02, 03, 04 and 05. We would lay down the following bytes at the addresses 00 through 09: +00: 68 +01: 68 +02: 68 +03: 68 +04: 68 +05: 68 +06: 90 +07: 90 +08: 90 +09: 90 + +If I jump to 00, the code looks like this: +push 68686868 +00: 68 +01: 68 +02: 68 +03: 68 +04: 68 +push 68909090 +05: 68 +06: 90 +07: 90 +08: 90 +09: 90 +and the stack looks like this: +rsp+0x8: 68686868 +rsp : 68909090 + +If I jump to 01, the code looks like this: +push 68686868 +01: 68 +02: 68 +03: 68 +04: 68 +05: 68 + +nop +06: 90 +nop +07: 90 +nop +08: 90 +nop +09: 90 +and the stack looks like this: +rsp : 68686868 + +If I jump to 02, the code looks like this: +push 68686890 +02: 68 +03: 68 +04: 68 +05: 68 +06: 90 + +nop +07: 90 +nop +08: 90 +nop +09: 90 +and the stack looks like this: +rsp : 68686890 + +If I jump to 03, the code looks like this: +push 68689090 +03: 68 +04: 68 +05: 68 +06: 90 +07: 90 + +nop +08: 90 +nop +09: 90 +and the stack looks like this: +rsp : 68689090 + +If I jump to 04, the code looks like this: +push 68909090 +04: 68 +05: 68 +06: 90 +07: 90 +08: 90 + +nop +09: 90 +and the stack looks like this: +rsp : 68909090 + +If I jump to 05, the code looks like this: +push +05: 68 +06: 90 +07: 90 +08: 90 +09: 90 + +and the stack looks like this: +rsp : 90909090 + +D: (00 target) +cmp rsp+0x8, 68686868 +jne X +cmp rsp+0x0, 68909090 +jne X +lea rsp, rsp+0x10 +jmp L00 (L00 is the code originally located at 00) + +X: (01 target) +cmp rsp+0x0, 68686868 (we know that rsp+0x8 != 68686868) +jne Y +lea rsp, rsp+0x8 +jmp L01 + +Y: (02 target) +cmp rsp+0x0, 68686890 (we know that rsp+0x8 != 68686868) +jne Z +lea rsp, rsp+0x8 +jmp L02 + +Z: (03 target) +cmp rsp+0x0, 68689090 (we know that rsp+0x8 != 68686868) +jne AA +lea rsp, rsp+0x8 +jmp L03 + +AA: (04 target) +cmp rsp+0x0, 68909090 (we know that rsp+0x8 != 68686868) +jne BB +lea rsp, rsp+0x8 +jmp L04 + +AA: (05 target) +cmp rsp+0x0, 90909090 (we know that rsp+0x8 != 68686868) +jne FAIL +lea rsp, rsp+0x8 +jmp L05 + +And here is an example of the code that is generated (it is +formatted for easier reading! + +# Sleds are needed for pins between 0x400110 and 0x400115 (inclusive) + 400110: 68 68 68 68 68 push 0x68686868 + 400115: 68 90 90 90 90 push 0xffffffff90909090 + 40011a: e9 00 00 00 00 jmp 0x40011f + +# Target 00 + 40011f: 48 81 7c 24 08 68 68 cmp QWORD PTR [rsp+0x8],0x68686868 + 400126: 68 68 + 400128: 0f 85 d2 0e 20 00 jne 0x601000 + 40012e: 48 81 3c 24 90 90 90 cmp QWORD PTR [rsp],0xffffffff90909090 + 400135: 90 + 400136: 0f 85 c4 0e 20 00 jne 0x601000 + 40013c: 48 8d 64 24 10 lea rsp,[rsp+0x10] + 400141: jmp <L00> + +# Target 01 + 601000: 48 81 3c 24 68 68 68 cmp QWORD PTR [rsp],0x68686868 + 601007: 68 + 601008: 0f 85 4f f1 df ff jne 0x40015d + 60100e: 48 8d 64 24 08 lea rsp,[rsp+0x8] + 601013: e9 2a f1 df ff jmp <L01> + +# Target 02 + 40015d: 48 81 3c 24 68 68 68 cmp QWORD PTR [rsp],0xffffffff90686868 + 400164: 90 + 400165: 0f 85 ad 0e 20 00 jne 0x601018 + 40016b: 48 8d 64 24 08 lea rsp,[rsp+0x8] + 400170: e9 ce ff ff ff jmp <L02> + +# Target 03 + 601018: 48 81 3c 24 68 68 90 cmp QWORD PTR [rsp],0xffffffff90906868 + 60101f: 90 + 601020: 0f 85 4f f1 df ff jne 0x400175 + 601026: 48 8d 64 24 08 lea rsp,[rsp+0x8] + 60102b: e9 14 f1 df ff jmp <L03> + +# Target 04 + 400175: 48 81 3c 24 68 90 90 cmp QWORD PTR [rsp],0xffffffff90909068 + 40017c: 90 + 40017d: 0f 85 0a 00 00 00 jne 0x40018d + 400183: 48 8d 64 24 08 lea rsp,[rsp+0x8] + 400188: e9 b8 ff ff ff jmp <L04> + +# Target 05 + 40018d: 48 81 3c 24 90 90 90 cmp QWORD PTR [rsp],0xffffffff90909090 + 400194: 90 + 400195: 0f 85 0a 00 00 00 jne <FAIL> + 40019b: 48 8d 64 24 08 lea rsp,[rsp+0x8] + 4001a0: e9 a1 ff ff ff jmp <L05> diff --git a/zipr/README.txt b/zipr/README.txt new file mode 100644 index 0000000000000000000000000000000000000000..5a29b14ef305203dc3a930037e9c71e1c71d1877 --- /dev/null +++ b/zipr/README.txt @@ -0,0 +1 @@ +This is Zipr! diff --git a/zipr/SConscript b/zipr/SConscript new file mode 100644 index 0000000000000000000000000000000000000000..99af86f95ea98217ac3f38cea0745d386651fb91 --- /dev/null +++ b/zipr/SConscript @@ -0,0 +1,22 @@ +import shutil +import os +import tarfile + +Import('env') + +# build security transforms +irdbenv=env.Clone(); + +zipr=SConscript("src/SConscript") + +pedi = Command( target = "./zipr-testoutput", + source = zipr, + action = "echo zipr; cd "+os.environ['PEASOUP_HOME']+"/zipr_install ; " +os.environ['PEDI_HOME']+"/pedi -m manifest.txt ; cd -" ) + +ret=[zipr] +if Dir('.').abspath == Dir('#.').abspath: + ret=pedi+zipr + + +Default(ret) +Return('ret') diff --git a/zipr/SConstruct b/zipr/SConstruct new file mode 100644 index 0000000000000000000000000000000000000000..cf51ffc05be8adc5ecc89bf788abd52bba5097d3 --- /dev/null +++ b/zipr/SConstruct @@ -0,0 +1,60 @@ +import os +import sys + +(sysname, nodename, release, version, machine)=os.uname() + + +env=Environment() + +# default build options +env.Replace(CFLAGS=" -fPIC ") +env.Replace(CXXFLAGS=" -std=c++11 -fPIC ") +env.Replace(LINKFLAGS=" -fPIC ") + +# parse arguments +env.Replace(SECURITY_TRANSFORMS_HOME=os.environ['SECURITY_TRANSFORMS_HOME']) +env.Replace(ZIPR_HOME=os.environ['ZIPR_HOME']) +env.Replace(ZIPR_INSTALL=os.environ['ZIPR_INSTALL']) +env.Replace(ZIPR_SDK=os.environ['ZIPR_SDK']) +env.Replace(profile=ARGUMENTS.get("profile",0)) +env.Replace(debug=ARGUMENTS.get("debug",0)) +env.Replace(do_cgc=ARGUMENTS.get("do_cgc",0)) +env.Replace(do_64bit_build=ARGUMENTS.get("do_64bit_build",0)) + +env.Append(LINKFLAGS=" -Wl,-unresolved-symbols=ignore-in-shared-libs ") + +if int(env['profile']) == 1: + print "Setting profile and debug mode" + env.Append(CFLAGS=" -pg") + env.Append(CXXFLAGS=" -pg") + env.Append(LINKFLAGS=" -pg") + env['debug'] = 1 +if int(env['debug']) == 1: + print "Setting debug mode" + env.Append(CFLAGS=" -g") + env.Append(CXXFLAGS=" -g") + env.Append(LINKFLAGS=" -g") +else: + print "Setting release mode" + env.Append(CFLAGS=" -O3") + env.Append(CXXFLAGS=" -O3") + env.Append(LINKFLAGS=" -O3") + +env['build_appfw']=0 +env['build_tools']=0 + +# add extra flag for solaris. +if sysname == "SunOS": + env.Append(LINKFLAGS=" -L/opt/csw/lib -DSOLARIS ") + env.Append(CFLAGS=" -I/opt/csw/include -DSOLARIS ") + env.Append(CXXFLAGS=" -I/opt/csw/include -DSOLARIS ") +else: + env.Append(CFLAGS=" -fPIE ") + env.Append(CXXFLAGS=" -fPIE ") + env.Append(LINKFLAGS=" -fPIE ") + + + +Export('env') +SConscript("SConscript") + diff --git a/zipr/cicd_tests/do-build.sh b/zipr/cicd_tests/do-build.sh new file mode 100755 index 0000000000000000000000000000000000000000..6fd94b26d74a89bb0829cd5a2f3007b89b8e00b6 --- /dev/null +++ b/zipr/cicd_tests/do-build.sh @@ -0,0 +1,34 @@ +#/bin/bash + +set -e +set -x + +main() +{ + + # gather info for debugging later, probably not necessary + pwd + hostname + whoami + env|grep "^CICD" + + local orig_dir=$(pwd) + + # puts peasoup_umbrella (and all submodules) in CICD_MODULE_WORK_DIR + cicd_setup_module_dependency opensrc/zipr.git zipr_umbrella + + + # puts the version of zipr to test in peasoup_umbrella/zipr. + cicd_put_module_in_tree zipr_umbrella/zipr + + # Build/run $PSZ, test result + cd $CICD_MODULE_WORK_DIR/zipr_umbrella + source set_env_vars + sudo ./get-peasoup-packages.sh all + ./build-all.sh + dropdb $PGDATABASE 2>/dev/null || true ; ./postgres_setup.sh + + cd $orig_dir +} + +main "$@" diff --git a/zipr/cicd_tests/do-clean.sh b/zipr/cicd_tests/do-clean.sh new file mode 100755 index 0000000000000000000000000000000000000000..f6b44214bd1f94648ce175dba52c2693eccc3777 --- /dev/null +++ b/zipr/cicd_tests/do-clean.sh @@ -0,0 +1,22 @@ +#/bin/bash + +set -e +set -x + +main() +{ + + # gather info for debugging later, probably not necessary + pwd + hostname + whoami + env|grep "^CICD" + + + if [[ $CICD_NIGHTLY == 1 ]] ; then + rm -rf $CICD_MODULE_WORK_DIR/zipr_umbrella + fi + +} + +main "$@" diff --git a/zipr/cicd_tests/internal-tests.sh b/zipr/cicd_tests/internal-tests.sh new file mode 100755 index 0000000000000000000000000000000000000000..0e74c4e74b8d9a531280e18adfeecb9caf1290cb --- /dev/null +++ b/zipr/cicd_tests/internal-tests.sh @@ -0,0 +1,14 @@ +#!/bin/bash + +set -e +set -x + +cd $CICD_MODULE_WORK_DIR/zipr_umbrella +source set_env_vars +# run zipr internal tests +cd $ZIPR_HOME/test; scons + +LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$PEASOUP_HOME/irdb-libs/lib +for i in *.exe; do ./$i; done + + diff --git a/zipr/cicd_tests/xform-cat.sh b/zipr/cicd_tests/xform-cat.sh new file mode 100755 index 0000000000000000000000000000000000000000..9e976892d8c1b5f910e8e2bec82af915e410114c --- /dev/null +++ b/zipr/cicd_tests/xform-cat.sh @@ -0,0 +1,10 @@ +cd $CICD_MODULE_WORK_DIR/zipr_umbrella + +set -e +set -x + +source set_env_vars +cd /tmp +rm -rf cat.rida ped_cat; $PSZ $(which cat) ./cat.rida -c rida=on -s meds_static=off --tempdir ped_cat || true +if [[ ! -x ./cat.rida ]]; then cat ped_ls/logs/*; fi +./cat.rida /dev/null diff --git a/zipr/cicd_tests/xform-ls.sh b/zipr/cicd_tests/xform-ls.sh new file mode 100755 index 0000000000000000000000000000000000000000..9b94ebf39ae5b4dd2ca0b93c5bb570d730770abf --- /dev/null +++ b/zipr/cicd_tests/xform-ls.sh @@ -0,0 +1,11 @@ +#!/bin/bash +set -e +set -x + +cd $CICD_MODULE_WORK_DIR/zipr_umbrella +source set_env_vars +cd /tmp +rm -rf ls.rida ped_ls; $PSZ /bin/ls ./ls.rida -c rida=on -s meds_static=off --tempdir ped_ls || true +if [[ ! -x ./ls.rida ]]; then cat ped_ls/logs/*; fi +rm -rf ped_ls +./ls.rida diff --git a/zipr/include/arch/arch_arm32.hpp b/zipr/include/arch/arch_arm32.hpp new file mode 100644 index 0000000000000000000000000000000000000000..3b6c3f45e52369d52b53ed6e969e2a88c7ca3101 --- /dev/null +++ b/zipr/include/arch/arch_arm32.hpp @@ -0,0 +1,38 @@ +#ifndef ARCHARM32_HPP +#define ARCHARM32_HPP + +class ZiprArchitectureHelperARM32_t : public ZiprArchitectureHelperBase_t +{ + public: + ZiprArchitectureHelperARM32_t(Zipr_SDK::Zipr_t* p_zipr_obj) : ZiprArchitectureHelperBase_t(p_zipr_obj) + { + } + + virtual IRDB_SDK::Instruction_t* createNewJumpInstruction(IRDB_SDK::FileIR_t *p_firp, IRDB_SDK::Instruction_t* p_existing) override + { + // An arm32 brand in binary is: cond 1 0 1 L signed_immed_24 + // + // where cond is the 4-bit condition field. for an uncond branch, we want cond=0b1110 + // and L is whether the link register is set. + // + // so, bytes = 0b11101010 0b00000000 0b00000000 0b00000000 + // + const auto bits =string("\x00\x00\x00\x0ea",4); + auto ret=IRDB_SDK::addNewDataBits(p_firp, p_existing, bits); + const auto d=IRDB_SDK::DecodedInstruction_t::factory(ret); + assert(d->valid()); + return ret; + } + + virtual IRDB_SDK::Instruction_t* createNewHaltInstruction(IRDB_SDK::FileIR_t *p_firp, IRDB_SDK::Instruction_t* p_existing) override + { + // for arm32, we are using a mov pc, #0 as a halt insn. + // 0xe3a0f000 + const auto bits =string("\x00\xf0\xa0\xe3",4); + auto ret=IRDB_SDK::addNewDataBits(p_firp, p_existing, bits); + const auto d=IRDB_SDK::DecodedInstruction_t::factory(ret); + assert(d->valid()); + return ret; + } +}; +#endif diff --git a/zipr/include/arch/arch_arm64.hpp b/zipr/include/arch/arch_arm64.hpp new file mode 100644 index 0000000000000000000000000000000000000000..ff20117de45415eed246a7bbeef4db0b39dd7120 --- /dev/null +++ b/zipr/include/arch/arch_arm64.hpp @@ -0,0 +1,33 @@ +#ifndef ARCHARM64_HPP +#define ARCHARM64_HPP + +class ZiprArchitectureHelperARM64_t : public ZiprArchitectureHelperBase_t +{ + public: + ZiprArchitectureHelperARM64_t(Zipr_SDK::Zipr_t* p_zipr_obj) : ZiprArchitectureHelperBase_t(p_zipr_obj) + { + } + virtual IRDB_SDK::Instruction_t* createNewJumpInstruction(IRDB_SDK::FileIR_t *p_firp, IRDB_SDK::Instruction_t* p_existing) override + { + // A Brandh unconditional, in binary is: 0001 0100 00000000 00000000 00000000 + // it includes a 26-bit immediate, which is +/- 128MB, which should be a good enough "jump anywhere" + // for now. + const auto bits =string("\x00\x00\x00\x014",4); + auto ret=IRDB_SDK::addNewDataBits(p_firp, p_existing, bits); + const auto d=IRDB_SDK::DecodedInstruction_t::factory(ret); + assert(d->valid()); + return ret; + } + virtual IRDB_SDK::Instruction_t* createNewHaltInstruction(IRDB_SDK::FileIR_t *p_firp, IRDB_SDK::Instruction_t* p_existing) override + { + // A halt unconditional, in binary is: + // 1101 0100 0100 0000 0000 0000 0000 0000 + // 0xd4400000 + const auto bits =string("\x00\x00\x40\xd4",4); + auto ret=IRDB_SDK::addNewDataBits(p_firp, p_existing, bits); + const auto d=IRDB_SDK::DecodedInstruction_t::factory(ret); + assert(d->valid()); + return ret; + } +}; +#endif diff --git a/zipr/include/arch/arch_base.hpp b/zipr/include/arch/arch_base.hpp new file mode 100644 index 0000000000000000000000000000000000000000..f84f5a7d87518db5afd401f1f32fd3498865b43a --- /dev/null +++ b/zipr/include/arch/arch_base.hpp @@ -0,0 +1,31 @@ +#ifndef ARCHBASE_HPP +#define ARCHBASE_HPP + + +class ZiprArchitectureHelperBase_t +{ + private: + const unique_ptr<ZiprPinnerBase_t > m_pinner ; + const unique_ptr<ZiprPatcherBase_t> m_patcher; + const unique_ptr<ZiprSizerBase_t > m_sizer ; + + ZiprArchitectureHelperBase_t()=delete; + + protected: + Zipr_t* m_zipr ; + ZiprArchitectureHelperBase_t(Zipr_SDK::Zipr_t* p_zipr_obj); + + public: + virtual IRDB_SDK::Instruction_t* createNewJumpInstruction(IRDB_SDK::FileIR_t *p_firp, IRDB_SDK::Instruction_t* p_existing)=0; + virtual IRDB_SDK::Instruction_t* createNewHaltInstruction(IRDB_SDK::FileIR_t *p_firp, IRDB_SDK::Instruction_t* p_existing)=0; + virtual RangeAddress_t splitDollop (IRDB_SDK::FileIR_t* firp, Zipr_SDK::Dollop_t* to_split, const RangeAddress_t p_cur_addr); + + static std::unique_ptr<ZiprArchitectureHelperBase_t> factory(Zipr_SDK::Zipr_t* zipr_obj); + + ZiprPinnerBase_t * getPinner () const { return m_pinner .get(); } + ZiprPatcherBase_t* getPatcher() const { return m_patcher.get(); } + ZiprSizerBase_t * getSizer () const { return m_sizer .get(); } + +}; + +#endif diff --git a/zipr/include/arch/arch_mips32.hpp b/zipr/include/arch/arch_mips32.hpp new file mode 100644 index 0000000000000000000000000000000000000000..201673aff2c59ef215f007987577587457b748d4 --- /dev/null +++ b/zipr/include/arch/arch_mips32.hpp @@ -0,0 +1,36 @@ +#ifndef ARCHMIPS32_HPP +#define ARCHMIPS32_HPP + +class ZiprArchitectureHelperMIPS32_t : public ZiprArchitectureHelperBase_t +{ + public: + ZiprArchitectureHelperMIPS32_t(Zipr_SDK::Zipr_t* p_zipr_obj) : ZiprArchitectureHelperBase_t(p_zipr_obj) + { + } + + virtual IRDB_SDK::Instruction_t* createNewJumpInstruction(IRDB_SDK::FileIR_t *p_firp, IRDB_SDK::Instruction_t* p_existing) override + { + // create a beq $0, $0, imm16 instruction + // to be PC-relative. + const auto bits =string("\x10\x00\x00\x000",4); + auto ret=IRDB_SDK::addNewDataBits(p_firp, p_existing, bits); + const auto d=IRDB_SDK::DecodedInstruction_t::factory(ret); + assert(d->valid()); + return ret; + } + + virtual IRDB_SDK::Instruction_t* createNewHaltInstruction(IRDB_SDK::FileIR_t *p_firp, IRDB_SDK::Instruction_t* p_existing) override + { + // 0b00000000 0b00000000 0b00000000 0b00110100 + // teq $0, $0, 0 --> trap always (as $0 is always 0) + const auto bits = string("\x00\x00\x00\x34",4); + auto ret = IRDB_SDK::addNewDataBits(p_firp, p_existing, bits); + const auto d = IRDB_SDK::DecodedInstruction_t::factory(ret); + assert(d->valid()); + return ret; + } + + virtual RangeAddress_t splitDollop(FileIR_t* p_firp, Zipr_SDK::Dollop_t* to_split, const RangeAddress_t p_cur_addr); + +}; +#endif diff --git a/zipr/include/arch/arch_x86.hpp b/zipr/include/arch/arch_x86.hpp new file mode 100644 index 0000000000000000000000000000000000000000..0e4216582dca4f8f2a0e1baba07bdf71f839ec6a --- /dev/null +++ b/zipr/include/arch/arch_x86.hpp @@ -0,0 +1,23 @@ +#ifndef ARCHX86_HPP +#define ARCHX86_HPP + + +class ZiprArchitectureHelperX86_t : public ZiprArchitectureHelperBase_t +{ + public: + ZiprArchitectureHelperX86_t(Zipr_SDK::Zipr_t* p_zipr_obj) : ZiprArchitectureHelperBase_t(p_zipr_obj) + { + } + + virtual IRDB_SDK::Instruction_t* createNewJumpInstruction(IRDB_SDK::FileIR_t *p_firp, IRDB_SDK::Instruction_t* p_existing) override + { + const auto bits=string("\xe9\x00\x00\x00\x00",5); /* jmp 0 */ + return IRDB_SDK::addNewDataBits(p_firp, p_existing, bits); + } + virtual IRDB_SDK::Instruction_t* createNewHaltInstruction(IRDB_SDK::FileIR_t *p_firp, IRDB_SDK::Instruction_t* p_existing) override + { + const auto bits=string("\xf4",1); /* hlt */ + return IRDB_SDK::addNewDataBits(p_firp, p_existing, bits); + } +}; +#endif diff --git a/zipr/include/ehwrite.h b/zipr/include/ehwrite.h new file mode 100644 index 0000000000000000000000000000000000000000..30a28ebcaed893ec977a175d34f46eebc1d00d47 --- /dev/null +++ b/zipr/include/ehwrite.h @@ -0,0 +1,290 @@ +#ifndef EhWriter_h +#define EhWriter_h + +#include <iostream> +#include <irdb-core> + +namespace EhWriter +{ + + using namespace std; + using namespace IRDB_SDK; + + class EhWriter_t + { + protected: + ZiprImpl_t& zipr_obj; + VirtualOffset_t eh_addr=0; + + EhWriter_t(ZiprImpl_t& p_zipr_obj) + : + zipr_obj(p_zipr_obj) + { + + } + + void SetEhAddr() + { + + auto page_round_up=[](const uintptr_t x) -> uintptr_t + { + auto page_size=(uintptr_t)PAGE_SIZE; + return ( (((uintptr_t)(x)) + page_size-1) & (~(page_size-1)) ); + }; + + // find maximum used scoop address. + const auto max_used_addr=max_element( + zipr_obj.getFileIR()->getDataScoops().begin(), + zipr_obj.getFileIR()->getDataScoops().end(), + [&](const DataScoop_t* a, const DataScoop_t* b) + { + assert(a && b && a->getEnd() && b->getEnd()) ; + return a->getEnd()->getVirtualOffset() < b->getEnd()->getVirtualOffset(); + } + ); + + // round it up and stringify it. + eh_addr=page_round_up((*max_used_addr)->getEnd()->getVirtualOffset()); + } + + + public: + virtual ~EhWriter_t() {} + virtual void GenerateNewEhInfo() =0; + + static unique_ptr<EhWriter_t> factory(ZiprImpl_t& p_zipr_obj); + }; + + class PE_unwind_info_t + { + private: + VirtualOffset_t start_rva = 0; + VirtualOffset_t end_rva = 0; + union + { + struct + { + uint8_t version:3; + uint8_t flags:5; + } parts; + uint8_t whole; + } ver_and_flags = {}; + uint8_t prolog_sz = 0; + union + { + struct + { + uint8_t frame_reg:4; + uint8_t frame_reg_offset:4; + } parts; + uint8_t whole; + } frame_reg_and_offset = {}; + vector<uint16_t> unwind_array; + + struct + { + uint32_t handler_rva; + vector<uint8_t> user_data; + } handle = {}; + + ZiprImpl_t& zipr_obj; + + public: + PE_unwind_info_t(Instruction_t* insns, ZiprImpl_t& zipr_obj); + + bool canExtend(Instruction_t* insn) const + { + const auto o = PE_unwind_info_t(insn, zipr_obj); + return tie( ver_and_flags.whole, prolog_sz, frame_reg_and_offset.whole, unwind_array, handle.handler_rva, handle.user_data) == + tie(o.ver_and_flags.whole, o.prolog_sz, o.frame_reg_and_offset.whole, o.unwind_array, o.handle.handler_rva, o.handle.user_data) ; + } + + void extend(Instruction_t* insn) + { + const auto &loc_map = *zipr_obj.getLocationMap(); + end_rva = loc_map.at(insn) - zipr_obj.getFileIR()->getArchitecture()->getFileBase() + insn->getDataBits().size(); + } + + void extend(const VirtualOffset_t to_addr) + { + end_rva = to_addr - zipr_obj.getFileIR()->getArchitecture()->getFileBase(); + } + + string get_raw_bytes() const + { + const auto unwind_sz = static_cast<uint8_t>(unwind_array.size()); + + auto ret = string(); + ret += string(reinterpret_cast<const char*>(&ver_and_flags.whole) , 1) ; + ret += string(reinterpret_cast<const char*>(&prolog_sz) , 1) ; + ret += string(reinterpret_cast<const char*>(&unwind_sz) , 1) ; + ret += string(reinterpret_cast<const char*>(&frame_reg_and_offset.whole), 1) ; + ret += string(reinterpret_cast<const char*>(unwind_array.data()) , 2*unwind_array.size()); + + // may need to pad the unwind array. + if( (unwind_sz & 0b1) == 0xb1 ) + ret += string(2, '\0'); + + + ret += string(reinterpret_cast<const char*>(&handle.handler_rva) , 4); + ret += string(reinterpret_cast<const char*>(handle.user_data.data()), handle.user_data.size()); + + return ret; + + } + + VirtualOffset_t getStartRVA() const { return start_rva; } + VirtualOffset_t getEndRVA() const { return end_rva; } + + + }; + + template <int ptrsize> + class PEEhWriter_t : public EhWriter_t + { + private: + using SehVector_t = vector<unique_ptr<PE_unwind_info_t> > ; + + SehVector_t BuildSehVector(); + void LayoutSehVector (const SehVector_t& seh_vector); + + public: + PEEhWriter_t(ZiprImpl_t& p_zipr_obj) + : EhWriter_t(p_zipr_obj) + + { + } + + virtual ~PEEhWriter_t() {} + + void GenerateNewEhInfo(); + + }; + + template <int ptrsize> + class ElfEhWriter_t : public EhWriter_t + { + + private: + + const string ehframe_s_filename="ehframe.s"; + const string ehframe_exe_filename="ehframe.exe"; + + class EhProgramListingManip_t : public EhProgramListing_t + { + public: + EhProgramListingManip_t(){} + EhProgramListingManip_t(const EhProgramListing_t &pgm) : EhProgramListing_t(pgm) { } + bool canExtend(const EhProgramListingManip_t &other); + void extend(const uint64_t inc_amt, const EhProgramListingManip_t &other); + bool isAdvanceDirective(const string &s) const; + string getPrintableString(const string &s) const; + private: + int getMergeIndex(const EhProgramListingManip_t &other); + + static const int DW_CFA_advance_loc1 = 0x02; + static const int DW_CFA_advance_loc2 = 0x03; + static const int DW_CFA_advance_loc4 = 0x04; + + }; + + class FDErepresentation_t; // forward decl + class CIErepresentation_t + { + public: + CIErepresentation_t(Instruction_t*, ElfEhWriter_t<ptrsize>* ehw); + bool canSupport(Instruction_t* insn) const; + Relocation_t* GetPersonalityReloc() const { return personality_reloc;} + + private: + // need nothing? + + EhProgramListingManip_t pgm; + uint64_t code_alignment_factor; + int64_t data_alignment_factor; + uint64_t return_reg; + Relocation_t* personality_reloc; + + mutable bool has_been_output; + + friend class FDErepresentation_t; + friend ElfEhWriter_t<ptrsize>; + }; + + static void print_pers(Instruction_t* insn, CIErepresentation_t *cie); + + + class FDErepresentation_t + { + private: + + class LSDArepresentation_t + { + public: + LSDArepresentation_t(Instruction_t* insn); + void extend(Instruction_t* insn); + bool canExtend(Instruction_t* insn) const; + bool exists() const { return callsite_table.size() > 0; } + + struct call_site_t + { + Instruction_t* cs_insn_start; + Instruction_t* cs_insn_end; + Instruction_t* landing_pad; + int action_table_index; + TTOrderVector_t actions; + }; + vector<call_site_t> callsite_table; + vector<TTOrderVector_t> action_table; + vector<Relocation_t*> type_table; + uint64_t tt_encoding; + + }; + + LSDArepresentation_t lsda; + CIErepresentation_t* cie; + VirtualOffset_t start_addr; + VirtualOffset_t end_addr; + VirtualOffset_t last_advance_addr; + // the pgm for the fde + EhProgramListingManip_t pgm; + + public: + FDErepresentation_t(Instruction_t* insn, ElfEhWriter_t<ptrsize>* ehw); + void extend(Instruction_t* insn, ElfEhWriter_t<ptrsize>* ehw); + bool canExtend(Instruction_t* insn, ElfEhWriter_t<ptrsize>* ehw); + bool hasLsda() const { return lsda.exists(); } + + friend ElfEhWriter_t<ptrsize>; + }; + + friend class FDErepresentation_t; + friend class CIErepresentation_t; + + vector<FDErepresentation_t*> all_fdes; + vector<CIErepresentation_t*> all_cies; + + VirtualOffset_t eh_frame_hdr_addr; // where the eh frame hdr will land. + string asm_comment; + + void BuildFDEs(); + void GenerateEhOutput(); + void CompileEhOutput(); + void ScoopifyEhOutput(); + + public: + ElfEhWriter_t(ZiprImpl_t& p_zipr_obj) + : EhWriter_t(p_zipr_obj) + + { + asm_comment = p_zipr_obj.getFileIR()->getArchitecture()->getMachineType()==admtAarch64 ? " // " : " # " ; + } + + virtual ~ElfEhWriter_t(); + + void GenerateNewEhInfo(); + + }; +} + +#endif diff --git a/zipr/include/elfwrite.h b/zipr/include/elfwrite.h new file mode 100644 index 0000000000000000000000000000000000000000..acc03e7a8520d5475dd7e4200c0fdd287b274f5e --- /dev/null +++ b/zipr/include/elfwrite.h @@ -0,0 +1,119 @@ + +#ifndef ElfWriter_h +#define ElfWriter_h + +#include <vector> +#include <map> + +#ifndef PAGE_SIZE +#define PAGE_SIZE 4096 +#endif + + +class ElfWriter : public ExeWriter +{ + protected: + + class StringTable_t + { + public: + + StringTable_t() { } ; + void AddString(const std::string &s) + { + if(locations.find(s)!=locations.end()) + return; + + locations[s]=table.size(); + table+=s; + table+='\0'; + } + void Write(FILE* fout) const + { + fwrite(table.c_str(), table.size(), 1, fout); + } + std::size_t size() const { return table.size(); } + std::size_t location(const std::string &s) const { return locations.at(s); } + + private: + + std::string table; + std::map<std::string,std::size_t> locations; + }; + + + public: + ElfWriter(EXEIO::exeio* p_exeiop, IRDB_SDK::FileIR_t* firp, bool write_sections, bool bss_opts) + : + ExeWriter(p_exeiop, firp,write_sections,bss_opts) + { + } + + virtual ~ElfWriter() {} + void Write(const std::string &out_file, const std::string &infile); + + + protected: + + virtual int getFileHeaderSize()=0; + virtual int getSegHeaderSize()=0; + virtual void LoadEhdr(FILE* fin)=0; + virtual void LoadPhdrs(FILE* fin)=0; + virtual void CreateNewPhdrs(const IRDB_SDK::VirtualOffset_t &min_addr, const IRDB_SDK::VirtualOffset_t &max_addr)=0; + virtual void WriteElf(FILE* fout)=0; + virtual void AddSections(FILE* fout)=0; +}; + +template <class T_Elf_Ehdr, class T_Elf_Phdr, class T_Elf_Addr, class T_Elf_Shdr, class T_Elf_Sym, class T_Elf_Rel, class T_Elf_Rela, class T_Elf_Dyn> +class ElfWriterImpl : public ElfWriter +{ + public: + + ElfWriterImpl(EXEIO::exeio* exeiop, IRDB_SDK::FileIR_t* firp, bool write_sections, bool bss_opts ) : ElfWriter(exeiop, firp, write_sections, bss_opts) { } + + protected: + int getFileHeaderSize() { return sizeof(T_Elf_Ehdr); } + int getSegHeaderSize() { return sizeof(T_Elf_Phdr); } + void LoadEhdr(FILE* fin); + void LoadPhdrs(FILE* fin); + void CreateNewPhdrs(const IRDB_SDK::VirtualOffset_t &min_addr, const IRDB_SDK::VirtualOffset_t &max_addr); + bool CreateNewPhdrs_PostAllocate(const IRDB_SDK::VirtualOffset_t &min_addr, const IRDB_SDK::VirtualOffset_t &max_addr); + bool CreateNewPhdrs_FirstPageAllocate(const IRDB_SDK::VirtualOffset_t &min_addr, const IRDB_SDK::VirtualOffset_t &max_addr) ; + bool readonly_space_at(const IRDB_SDK::VirtualOffset_t addr, const unsigned int size); + int locate_segment_index(const IRDB_SDK::VirtualOffset_t addr); + unsigned int count_filesz_to_seg(unsigned int seg); + bool CreateNewPhdrs_GapAllocate(const IRDB_SDK::VirtualOffset_t &min_addr, const IRDB_SDK::VirtualOffset_t &max_addr); + bool CreateNewPhdrs_PreAllocate(const IRDB_SDK::VirtualOffset_t &min_addr, const IRDB_SDK::VirtualOffset_t &max_addr); + bool CreateNewPhdrs_internal( + const IRDB_SDK::VirtualOffset_t &min_addr, + const IRDB_SDK::VirtualOffset_t &max_addr, + const int &first_seg_file_offset, + const bool &add_pt_load_for_phdr, + const size_t phdr_map_offset, + IRDB_SDK::VirtualOffset_t new_phdr_addr + ); + IRDB_SDK::DataScoop_t* find_scoop_by_name(const std::string& name, IRDB_SDK::FileIR_t* ); + void AddSections(FILE* fout); + void update_phdr_for_scoop_sections(IRDB_SDK::FileIR_t* ); + void trim_last_segment_filesz(IRDB_SDK::FileIR_t* firp); + + void WriteElf(FILE* fout); + private: + unsigned int DetermineMaxPhdrSize(); + + T_Elf_Ehdr ehdr; + T_Elf_Ehdr new_ehdr; + std::vector<T_Elf_Phdr> phdrs; + std::vector<T_Elf_Phdr> new_phdrs; +}; + +typedef class ElfWriterImpl<ELFIO::Elf64_Ehdr, ELFIO::Elf64_Phdr, ELFIO::Elf64_Addr, + ELFIO::Elf64_Shdr, ELFIO::Elf64_Sym, ELFIO::Elf64_Rel, ELFIO::Elf64_Rela, ELFIO::Elf64_Dyn> ElfWriter64; + +typedef class ElfWriterImpl<ELFIO::Elf32_Ehdr, ELFIO::Elf32_Phdr, ELFIO::Elf32_Addr, + ELFIO::Elf32_Shdr, ELFIO::Elf32_Sym, ELFIO::Elf32_Rel, ELFIO::Elf32_Rela, ELFIO::Elf32_Dyn> ElfWriter32; + + + +#endif + diff --git a/zipr/include/exewrite.h b/zipr/include/exewrite.h new file mode 100644 index 0000000000000000000000000000000000000000..e2f5681bcde52085ae264bfb9ed1cf74e65de29e --- /dev/null +++ b/zipr/include/exewrite.h @@ -0,0 +1,130 @@ + +#ifndef ExeWriter_h +#define ExeWriter_h + +#include <vector> +#include <map> + + +class ExeWriter +{ + protected: + + + class PageData_t + { + + public: + PageData_t() : m_perms(0), is_relro(false), data(PAGE_SIZE), inuse(PAGE_SIZE) { } + + void union_permissions(int p_perms) { m_perms|=p_perms; } + + bool is_zero_initialized() const + { + for(unsigned int i=0;i<data.size();i++) + { + if(data.at(i)!=0) + return false; + } + return true; + } + + int m_perms; + bool is_relro; + + std::vector<unsigned char> data; + std::vector<bool> inuse; + }; + class LoadSegment_t + { + public: + LoadSegment_t() :filesz(0), memsz(0), filepos(0), start_page(0), m_perms(0) { } + + LoadSegment_t( unsigned int p_filesz, unsigned int p_memsz, unsigned int p_filepos, unsigned int p_start_page, unsigned int p_m_perms) + : + filesz(p_filesz), + memsz(p_memsz), + filepos(p_filepos), + start_page(p_start_page), + m_perms(p_m_perms) + { + + } + + + uint64_t filesz; + uint64_t memsz; + uint64_t filepos; + uint64_t start_page; + uint64_t m_perms; + + }; + using LoadSegmentVector_t = std::vector<LoadSegment_t*>; + using PageMap_t = std::map<IRDB_SDK::VirtualOffset_t, PageData_t>; + + public: + ExeWriter(EXEIO::exeio* p_exeiop, IRDB_SDK::FileIR_t* firp, bool write_sections, bool bss_opts) + : + m_exeiop(p_exeiop), + m_firp(firp), + m_write_sections(write_sections), + m_bss_opts(bss_opts) + { + } + + virtual ~ExeWriter() {} + virtual void Write(const std::string &out_file, const std::string &infile) = 0; + + protected: + + EXEIO::exeio* m_exeiop; + IRDB_SDK::FileIR_t* m_firp; + bool m_write_sections; + bool m_bss_opts; + PageMap_t pagemap; + LoadSegmentVector_t segvec; + +#ifndef PAGE_SIZE + const int PAGE_SIZE=4096; +#endif + template <class T> + static T page_align(const T& in) + { + + return in&~(PAGE_SIZE-1); + } + template <class T> + static inline T page_round_down(const T& x) + { + return round_down_to(x,PAGE_SIZE); + } + template <class T> + static inline T page_round_up(const T& x) + { + return round_up_to(x,PAGE_SIZE); + } + template <class T> + static inline T round_down_to(const T& x, const uint64_t& to) + { + assert( (to & (to-1)) == 0 ); + return x & (~(to-1)); + } + template <class T> + static inline T round_up_to(const T& x, const uint64_t& to) + { + assert( (to & (to-1)) == 0 ); + return ( (((uintptr_t)(x)) + to-1) & (~(to-1)) ); + } + + IRDB_SDK::VirtualOffset_t DetectMinAddr(); + IRDB_SDK::VirtualOffset_t DetectMaxAddr(); + + void CreatePagemap(); + void CreateSegmap(); + void SortSegmap(); + +}; + + +#endif + diff --git a/zipr/include/patcher/patcher_arm32.hpp b/zipr/include/patcher/patcher_arm32.hpp new file mode 100644 index 0000000000000000000000000000000000000000..2e455c788bd97cc5b332817f1d9ab48ee562f724 --- /dev/null +++ b/zipr/include/patcher/patcher_arm32.hpp @@ -0,0 +1,57 @@ +/*************************************************************************** + * Copyright (c) 2014 Zephyr Software LLC. All rights reserved. + * + * This software is furnished under a license and/or other restrictive + * terms and may be used and copied only in accordance with such terms + * and the inclusion of the above copyright notice. This software or + * any other copies thereof may not be provided or otherwise made + * available to any other person without the express written consent + * of an authorized representative of Zephyr Software LCC. Title to, + * ownership of, and all rights in the software is retained by + * Zephyr Software LCC. + * + * Zephyr Software LLC. Proprietary Information + * + * Unless otherwise specified, the information contained in this + * directory, following this legend, and/or referenced herein is + * Zephyr Software LLC. (Zephyr) Proprietary Information. + * + * CONTACT + * + * For technical assistance, contact Zephyr Software LCC. at: + * + * + * Zephyr Software, LLC + * 2040 Tremont Rd + * Charlottesville, VA 22911 + * + * E-mail: jwd@zephyr-software.com + **************************************************************************/ + + + +#ifndef PATCHER_ARM32 +#define PATCHER_ARM32 + +class ZiprPatcherARM32_t : public ZiprPatcherBase_t +{ + // data + zipr::ZiprImpl_t* m_parent; + IRDB_SDK::FileIR_t* m_firp; + Zipr_SDK::MemorySpace_t &memory_space; + + std::map<RangeAddress_t, RangeAddress_t> redirect_map; + + + public: + + ZiprPatcherARM32_t(Zipr_SDK::Zipr_t* p_parent); + void ApplyNopToPatch(RangeAddress_t addr) override; + void ApplyPatch(RangeAddress_t from_addr, RangeAddress_t to_addr) override; + void PatchJump(RangeAddress_t at_addr, RangeAddress_t to_addr) override; + void PatchCall(RangeAddress_t at_addr, RangeAddress_t to_addr) override; + void CallToNop(RangeAddress_t at_addr) override; + + +}; +#endif diff --git a/zipr/include/patcher/patcher_arm64.hpp b/zipr/include/patcher/patcher_arm64.hpp new file mode 100644 index 0000000000000000000000000000000000000000..96f38f92d02acb921b8eabd9d4f2dbc3a611f3e5 --- /dev/null +++ b/zipr/include/patcher/patcher_arm64.hpp @@ -0,0 +1,57 @@ +/*************************************************************************** + * Copyright (c) 2014 Zephyr Software LLC. All rights reserved. + * + * This software is furnished under a license and/or other restrictive + * terms and may be used and copied only in accordance with such terms + * and the inclusion of the above copyright notice. This software or + * any other copies thereof may not be provided or otherwise made + * available to any other person without the express written consent + * of an authorized representative of Zephyr Software LCC. Title to, + * ownership of, and all rights in the software is retained by + * Zephyr Software LCC. + * + * Zephyr Software LLC. Proprietary Information + * + * Unless otherwise specified, the information contained in this + * directory, following this legend, and/or referenced herein is + * Zephyr Software LLC. (Zephyr) Proprietary Information. + * + * CONTACT + * + * For technical assistance, contact Zephyr Software LCC. at: + * + * + * Zephyr Software, LLC + * 2040 Tremont Rd + * Charlottesville, VA 22911 + * + * E-mail: jwd@zephyr-software.com + **************************************************************************/ + + + +#ifndef PATCHER_ARM64 +#define PATCHER_ARM64 + +class ZiprPatcherARM64_t : public ZiprPatcherBase_t +{ + // data + zipr::ZiprImpl_t* m_parent; + IRDB_SDK::FileIR_t* m_firp; + Zipr_SDK::MemorySpace_t &memory_space; + + std::map<RangeAddress_t, RangeAddress_t> redirect_map; + + + public: + + ZiprPatcherARM64_t(Zipr_SDK::Zipr_t* p_parent); + void ApplyNopToPatch(RangeAddress_t addr) override; + void ApplyPatch(RangeAddress_t from_addr, RangeAddress_t to_addr) override; + void PatchJump(RangeAddress_t at_addr, RangeAddress_t to_addr) override; + void PatchCall(RangeAddress_t at_addr, RangeAddress_t to_addr) override; + void CallToNop(RangeAddress_t at_addr) override; + + +}; +#endif diff --git a/zipr/include/patcher/patcher_base.hpp b/zipr/include/patcher/patcher_base.hpp new file mode 100644 index 0000000000000000000000000000000000000000..1609b3deae6a08d73682e32b16aa85de443e7b31 --- /dev/null +++ b/zipr/include/patcher/patcher_base.hpp @@ -0,0 +1,51 @@ +/*************************************************************************** + * Copyright (c) 2014 Zephyr Software LLC. All rights reserved. + * + * This software is furnished under a license and/or other restrictive + * terms and may be used and copied only in accordance with such terms + * and the inclusion of the above copyright notice. This software or + * any other copies thereof may not be provided or otherwise made + * available to any other person without the express written consent + * of an authorized representative of Zephyr Software LCC. Title to, + * ownership of, and all rights in the software is retained by + * Zephyr Software LCC. + * + * Zephyr Software LLC. Proprietary Information + * + * Unless otherwise specified, the information contained in this + * directory, following this legend, and/or referenced herein is + * Zephyr Software LLC. (Zephyr) Proprietary Information. + * + * CONTACT + * + * For technical assistance, contact Zephyr Software LCC. at: + * + * + * Zephyr Software, LLC + * 2040 Tremont Rd + * Charlottesville, VA 22911 + * + * E-mail: jwd@zephyr-software.com + **************************************************************************/ + + +#ifndef PATCHER_BASE +#define PATCHER_BASE + +class ZiprPatcherBase_t +{ + protected: + ZiprPatcherBase_t(){} + public: + virtual ~ZiprPatcherBase_t() { } + static unique_ptr<ZiprPatcherBase_t> factory(Zipr_SDK::Zipr_t* p_parent); + virtual void ApplyNopToPatch(RangeAddress_t addr)=0; + virtual void ApplyPatch(RangeAddress_t from_addr, RangeAddress_t to_addr)=0; + virtual void PatchJump(RangeAddress_t at_addr, RangeAddress_t to_addr)=0; + virtual void PatchCall(RangeAddress_t at_addr, RangeAddress_t to_addr)=0; + virtual void CallToNop(RangeAddress_t at_addr)=0; + + + +}; +#endif diff --git a/zipr/include/patcher/patcher_mips32.hpp b/zipr/include/patcher/patcher_mips32.hpp new file mode 100644 index 0000000000000000000000000000000000000000..b3e637411fd0c2336f73aac14be0f9fb2da49757 --- /dev/null +++ b/zipr/include/patcher/patcher_mips32.hpp @@ -0,0 +1,56 @@ +/*************************************************************************** + * Copyright (c) 2014 Zephyr Software LLC. All rights reserved. + * + * This software is furnished under a license and/or other restrictive + * terms and may be used and copied only in accordance with such terms + * and the inclusion of the above copyright notice. This software or + * any other copies thereof may not be provided or otherwise made + * available to any other person without the express written consent + * of an authorized representative of Zephyr Software LCC. Title to, + * ownership of, and all rights in the software is retained by + * Zephyr Software LCC. + * + * Zephyr Software LLC. Proprietary Information + * + * Unless otherwise specified, the information contained in this + * directory, following this legend, and/or referenced herein is + * Zephyr Software LLC. (Zephyr) Proprietary Information. + * + * CONTACT + * + * For technical assistance, contact Zephyr Software LCC. at: + * + * + * Zephyr Software, LLC + * 2040 Tremont Rd + * Charlottesville, VA 22911 + * + * E-mail: jwd@zephyr-software.com + **************************************************************************/ + + + +#ifndef PATCHER_MIPS32 +#define PATCHER_MIPS32 + +class ZiprPatcherMIPS32_t : public ZiprPatcherBase_t +{ + // data + zipr::ZiprImpl_t* m_parent; + IRDB_SDK::FileIR_t* m_firp; + Zipr_SDK::MemorySpace_t &memory_space; + + std::map<RangeAddress_t, RangeAddress_t> redirect_map; + + public: + + ZiprPatcherMIPS32_t(Zipr_SDK::Zipr_t* p_parent); + void ApplyNopToPatch(RangeAddress_t addr) override; + void ApplyPatch(RangeAddress_t from_addr, RangeAddress_t to_addr) override; + void PatchJump(RangeAddress_t at_addr, RangeAddress_t to_addr) override; + void PatchCall(RangeAddress_t at_addr, RangeAddress_t to_addr) override; + void CallToNop(RangeAddress_t at_addr) override; + + +}; +#endif diff --git a/zipr/include/patcher/patcher_x86.hpp b/zipr/include/patcher/patcher_x86.hpp new file mode 100644 index 0000000000000000000000000000000000000000..86014c95d8fc7e258a6f61fe1bbe68fafe6b6470 --- /dev/null +++ b/zipr/include/patcher/patcher_x86.hpp @@ -0,0 +1,56 @@ +/*************************************************************************** + * Copyright (c) 2014 Zephyr Software LLC. All rights reserved. + * + * This software is furnished under a license and/or other restrictive + * terms and may be used and copied only in accordance with such terms + * and the inclusion of the above copyright notice. This software or + * any other copies thereof may not be provided or otherwise made + * available to any other person without the express written consent + * of an authorized representative of Zephyr Software LCC. Title to, + * ownership of, and all rights in the software is retained by + * Zephyr Software LCC. + * + * Zephyr Software LLC. Proprietary Information + * + * Unless otherwise specified, the information contained in this + * directory, following this legend, and/or referenced herein is + * Zephyr Software LLC. (Zephyr) Proprietary Information. + * + * CONTACT + * + * For technical assistance, contact Zephyr Software LCC. at: + * + * + * Zephyr Software, LLC + * 2040 Tremont Rd + * Charlottesville, VA 22911 + * + * E-mail: jwd@zephyr-software.com + **************************************************************************/ + +#ifndef PATCHER_X86 +#define PATCHER_X86 + +class ZiprPatcherX86_t : public ZiprPatcherBase_t +{ + // data + zipr::ZiprImpl_t* m_parent; + IRDB_SDK::FileIR_t* m_firp; + Zipr_SDK::MemorySpace_t &memory_space; + + // methods + void RewritePCRelOffset(RangeAddress_t from_addr,RangeAddress_t to_addr, int insn_length, int offset_pos); + + public: + ZiprPatcherX86_t(Zipr_SDK::Zipr_t* p_parent) ; + void ApplyNopToPatch(RangeAddress_t addr) override; + void ApplyPatch(RangeAddress_t from_addr, RangeAddress_t to_addr) override; + void PatchJump(RangeAddress_t at_addr, RangeAddress_t to_addr) override; + void PatchCall(RangeAddress_t at_addr, RangeAddress_t to_addr) override; + void CallToNop(RangeAddress_t at_addr) override; + + + + +}; +#endif diff --git a/zipr/include/pewrite.h b/zipr/include/pewrite.h new file mode 100644 index 0000000000000000000000000000000000000000..d9304324826fefe65caba78e208283c364f386b1 --- /dev/null +++ b/zipr/include/pewrite.h @@ -0,0 +1,200 @@ + +#ifndef PeWriter_h +#define PeWriter_h + +#include <vector> +#include <map> + + +template <int bitwidth, class uintMa_t /* a uint<size>_t for the machine */ > +class PeWriter : ExeWriter +{ + + private: + const uint32_t file_alignment=PAGE_SIZE; + const uint8_t dos_header[0x80]= + { + /* 00000000 */ 0x4d, 0x5a, 0x90, 0x00, 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, // |MZ..............| + /* 00000010 */ 0xb8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // |........@.......| + /* 00000020 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // |................| + /* 00000030 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, // |................| + /* 00000040 */ 0x0e, 0x1f, 0xba, 0x0e, 0x00, 0xb4, 0x09, 0xcd, 0x21, 0xb8, 0x01, 0x4c, 0xcd, 0x21, 0x54, 0x68, // |........!..L.!Th| + /* 00000050 */ 0x69, 0x73, 0x20, 0x70, 0x72, 0x6f, 0x67, 0x72, 0x61, 0x6d, 0x20, 0x63, 0x61, 0x6e, 0x6e, 0x6f, // |is program canno| + /* 00000060 */ 0x74, 0x20, 0x62, 0x65, 0x20, 0x72, 0x75, 0x6e, 0x20, 0x69, 0x6e, 0x20, 0x44, 0x4f, 0x53, 0x20, // |t be run in DOS | + /* 00000070 */ 0x6d, 0x6f, 0x64, 0x65, 0x2e, 0x0d, 0x0d, 0x0a, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // |mode....$.......| + }; + + using coff_header_t = + struct coff_header + { + uint32_t magic; + uint16_t machine; + uint16_t number_of_sections; + uint32_t time_date_stamp; + uint32_t file_pointer_to_symboltable; + uint32_t number_of_symbols; + uint16_t sizeof_opt_header; + uint16_t characteristics; + }; + + using standard_coff_header_t = + struct standard_coff_header + { + uint16_t magic; + uint8_t major_linker_version; + uint8_t minor_linker_version; + uint32_t sizeof_code; + uint32_t sizeof_initd_data; + uint32_t sizeof_uninitd_data; + uint32_t entry_point; + uint32_t base_of_code; + }; + + using win_specific_fields_t = + struct win_specific_fields + { + uintMa_t image_base; + uint32_t section_alignment; + uint32_t file_alignment; + uint16_t major_os_version; + uint16_t minor_os_version; + uint16_t major_image_version; + uint16_t minor_image_version; + uint16_t major_subsystem_version; + uint16_t minor_subsystem_version; + uint32_t win32_version; + uint32_t sizeof_image; + uint32_t sizeof_headers; + uint32_t checksum; + uint16_t subsystem; + uint16_t dll_characteristics; + uintMa_t sizeof_stack_reserve; + uintMa_t sizeof_stack_commit; + uintMa_t sizeof_heap_reserve; + uintMa_t sizeof_heap_commit; + uint32_t loader_flags; + uint32_t num_rva_and_sizes; + }; + + using image_data_directory_t = + struct image_data_directory + { + uint32_t virtual_address; + uint32_t size; + }; + + // the characteristics bitmap for a section header + using section_characteristics_t = + enum section_characteristics + { + IMAGE_SCN_CNT_CODE = 0x00000020, // The section contains executable code. + IMAGE_SCN_CNT_INITIALIZED_DATA = 0x00000040, // The section contains initialized data. + IMAGE_SCN_CNT_UNINITIALIZED_DATA = 0x00000080, // The section contains uninitialized data. + IMAGE_SCN_GPREL = 0x00008000, // The section contains data referenced through the global pointer (GP). + IMAGE_SCN_LNK_NRELOC_OVFL = 0x01000000, // The section contains extended relocations. + IMAGE_SCN_MEM_DISCARDABLE = 0x02000000, // The section can be discarded as needed. + IMAGE_SCN_MEM_NOT_CACHED = 0x04000000, // The section cannot be cached. + IMAGE_SCN_MEM_NOT_PAGED = 0x08000000, // The section is not pageable. + IMAGE_SCN_MEM_SHARED = 0x10000000, // The section can be shared in memory. + IMAGE_SCN_MEM_EXECUTE = 0x20000000, // The section can be executed as code. + IMAGE_SCN_MEM_READ = 0x40000000, // The section can be read. + IMAGE_SCN_MEM_WRITE = 0x80000000 // The section can be written to. + }; + + // Each entry in the section table is a section header. In PE, a section is equiv. to an Elf Segment. + // Section headers are fixed size, following this format: + struct pe_section_header_t + { + pe_section_header_t(const LoadSegment_t* seg, FileIR_t* p_firp) + : + name{} + { + static int secno=0; + /* + * uint64_t filesz; + * uint64_t memsz; + * uint64_t filepos; + * uint64_t start_page; + * uint64_t m_perms; + */ + snprintf(name, sizeof(name), "sec%d", secno++); + virtual_size=seg->memsz; + virtual_addr=seg->start_page-p_firp->getArchitecture()->getFileBase(); + file_size=seg->memsz; + file_pointer_to_data=0; // fixed up later + file_pointer_to_relocs=0; + file_pointer_to_line_numbers=0; + num_relocs=0; + num_line_numbers=0; + characteristics=0; + + const auto is_exe = (seg->m_perms&0b1)==0b1; + const auto is_write = (seg->m_perms&0b10)==0b10; + const auto is_read = (seg->m_perms&0b100)==0b100; + + // set code bits or data bits + if(is_exe) + characteristics = IMAGE_SCN_CNT_CODE | IMAGE_SCN_MEM_EXECUTE; + else + characteristics = IMAGE_SCN_CNT_INITIALIZED_DATA; + + // set write bits + if(is_write) + characteristics |= IMAGE_SCN_MEM_WRITE; + + // set read bits + if(is_read) + characteristics |= IMAGE_SCN_MEM_READ; + + + + } + + char name[8]; // no null terminator, but null padded if <8 bytes + uint32_t virtual_size; // size of the section in memory + uint32_t virtual_addr; // virtual offset from image_base + uint32_t file_size; // initialized data size, rounded up to file_alignment. + uint32_t file_pointer_to_data; // must be multiple of file alignment from opt header. + uint32_t file_pointer_to_relocs; // must be 0 for images. + uint32_t file_pointer_to_line_numbers; // must be 0 for images. + uint16_t num_relocs; // must be 0 for images. + uint16_t num_line_numbers; // must be 0 for images. + uint32_t characteristics; + }; + + + coff_header_t coff_header_hdr={}; + standard_coff_header_t standard_coff_header_hdr={}; + uint32_t base_of_data=0; // an extra field for pe32 field that's not part of the pe32+ standard_coff_header_t + win_specific_fields_t win_specific_fields_hdr={}; + std::vector<image_data_directory_t> image_data_dir_hdrs={}; + std::vector<pe_section_header_t> section_headers={}; + std::FILE* fout=nullptr; + std::FILE* fin=nullptr; + + public: + PeWriter(EXEIO::exeio *exeiop, IRDB_SDK::FileIR_t* firp, bool write_sections, bool bss_opts) + : + ExeWriter(exeiop,firp,write_sections,bss_opts) + { + } + virtual ~PeWriter() {} + void Write(const std::string &out_file, const std::string &infile); + void InitHeaders(); + void GenerateDataDirectory(); + void CalculateHeaderSizes(); + void CreateSectionHeaders(); + void WriteFilePass1(); + + + protected: +}; + + +using PeWriter32 = PeWriter<32, uint32_t>; +using PeWriter64 = PeWriter<64, uint64_t>; + + + +#endif + diff --git a/zipr/include/pinner/pinner_arm32.hpp b/zipr/include/pinner/pinner_arm32.hpp new file mode 100644 index 0000000000000000000000000000000000000000..0a598d5c96c7d3d175fa18560718c54cbe9ac0e1 --- /dev/null +++ b/zipr/include/pinner/pinner_arm32.hpp @@ -0,0 +1,22 @@ +#ifndef ZIPR_PINNER_ARM32 +#define ZIPR_PINNER_ARM32 + +class ZiprPinnerARM32_t : public ZiprPinnerBase_t +{ + public: + ZiprPinnerARM32_t(Zipr_SDK::Zipr_t* p_parent); + virtual void doPinning() override; + + private: + zipr::ZiprImpl_t* m_parent; + IRDB_SDK::FileIR_t* m_firp; + Zipr_SDK::MemorySpace_t &memory_space; + Zipr_SDK::DollopManager_t &m_dollop_mgr; + Zipr_SDK::PlacementQueue_t &placement_queue; + + + +}; + + +#endif diff --git a/zipr/include/pinner/pinner_arm64.hpp b/zipr/include/pinner/pinner_arm64.hpp new file mode 100644 index 0000000000000000000000000000000000000000..afcd6a7ef21ca13ff1fbdd152c7b579bd6e3caec --- /dev/null +++ b/zipr/include/pinner/pinner_arm64.hpp @@ -0,0 +1,22 @@ +#ifndef ZIPR_PINNER_ARM64 +#define ZIPR_PINNER_ARM64 + +class ZiprPinnerARM64_t : public ZiprPinnerBase_t +{ + public: + ZiprPinnerARM64_t(Zipr_SDK::Zipr_t* p_parent); + virtual void doPinning() override; + + private: + zipr::ZiprImpl_t* m_parent; + IRDB_SDK::FileIR_t* m_firp; + Zipr_SDK::MemorySpace_t &memory_space; + Zipr_SDK::DollopManager_t &m_dollop_mgr; + Zipr_SDK::PlacementQueue_t &placement_queue; + + + +}; + + +#endif diff --git a/zipr/include/pinner/pinner_base.hpp b/zipr/include/pinner/pinner_base.hpp new file mode 100644 index 0000000000000000000000000000000000000000..81ca40b1b0ef5fb3646b9fdace5fc8dce6eb781e --- /dev/null +++ b/zipr/include/pinner/pinner_base.hpp @@ -0,0 +1,15 @@ +#ifndef ZIPR_PINNER +#define ZIPR_PINNER + +class ZiprPinnerBase_t +{ + protected: + ZiprPinnerBase_t() { } + public: + virtual void doPinning()=0; + static unique_ptr<ZiprPinnerBase_t> factory(Zipr_SDK::Zipr_t* parent); + virtual ~ZiprPinnerBase_t() { } +}; + + +#endif diff --git a/zipr/include/pinner/pinner_mips32.hpp b/zipr/include/pinner/pinner_mips32.hpp new file mode 100644 index 0000000000000000000000000000000000000000..8176807bc21ad2baa08f50fe58e16b1c37436c0f --- /dev/null +++ b/zipr/include/pinner/pinner_mips32.hpp @@ -0,0 +1,22 @@ +#ifndef ZIPR_PINNER_MIPS32 +#define ZIPR_PINNER_MIPS32 + +class ZiprPinnerMIPS32_t : public ZiprPinnerBase_t +{ + public: + ZiprPinnerMIPS32_t(Zipr_SDK::Zipr_t* p_parent); + virtual void doPinning() override; + + private: + zipr::ZiprImpl_t* m_parent; + IRDB_SDK::FileIR_t* m_firp; + Zipr_SDK::MemorySpace_t &memory_space; + Zipr_SDK::DollopManager_t &m_dollop_mgr; + Zipr_SDK::PlacementQueue_t &placement_queue; + + + +}; + + +#endif diff --git a/zipr/include/pinner/pinner_x86.hpp b/zipr/include/pinner/pinner_x86.hpp new file mode 100644 index 0000000000000000000000000000000000000000..c09966056ca7c62b3a8f906c125735520746bb82 --- /dev/null +++ b/zipr/include/pinner/pinner_x86.hpp @@ -0,0 +1,63 @@ +#ifndef ZIPR_PINNER_X86 +#define ZIPR_PINNER_X86 + +class ZiprPinnerX86_t : public ZiprPinnerBase_t +{ + public: + ZiprPinnerX86_t(Zipr_SDK::Zipr_t* p_parent); + virtual void doPinning() override; + + private: + + + // methods , remove from zipr_impl + void AddPinnedInstructions(); + void RecordPinnedInsnAddrs(); + bool ShouldPinImmediately(IRDB_SDK::Instruction_t *upinsn); + void PreReserve2ByteJumpTargets(); + void InsertJumpPoints68SledArea(Sled_t &sled); + IRDB_SDK::Instruction_t* Emit68Sled(RangeAddress_t addr, Sled_t sled, IRDB_SDK::Instruction_t* next_sled); + IRDB_SDK::Instruction_t* Emit68Sled(Sled_t sled); + void Update68Sled(Sled_t new_sled, Sled_t &existing_sled); + RangeAddress_t Do68Sled(RangeAddress_t addr); + void Clear68SledArea(Sled_t sled); + int Calc68SledSize(RangeAddress_t addr, size_t sled_overhead); + bool IsPinFreeZone(RangeAddress_t addr, int size); + void ReservePinnedInstructions(); + void ExpandPinnedInstructions(); + void Fix2BytePinnedInstructions(); + void OptimizePinnedInstructions(); + IRDB_SDK::Instruction_t* FindPinnedInsnAtAddr(RangeAddress_t addr); + + + // shortcut + IRDB_SDK::Instruction_t* FindPatchTargetAtAddr(RangeAddress_t addr) { return m_parent->FindPatchTargetAtAddr(addr); } + void PatchJump(RangeAddress_t at_addr, RangeAddress_t to_addr) { return m_parent->PatchJump(at_addr,to_addr); } + + + + + // data + zipr::ZiprImpl_t* m_parent; + Zipr_SDK::MemorySpace_t &memory_space; + Zipr_SDK::DollopManager_t &m_dollop_mgr; + IRDB_SDK::FileIR_t* m_firp; + Zipr_SDK::PlacementQueue_t &placement_queue; + bool m_verbose; + Stats_t* m_stats; + Zipr_SDK::InstructionLocationMap_t &final_insn_locations; + + + // notes: move these out of zipr_impl.h + std::set<UnresolvedPinned_t> two_byte_pins; + std::map<UnresolvedPinned_t,RangeAddress_t> five_byte_pins; + std::set<UnresolvedPinned_t,pin_sorter_t> unresolved_pinned_addrs; + std::map<RangeAddress_t,std::pair<IRDB_SDK::Instruction_t*, size_t> > m_InsnSizeAtAddrs; + std::map<RangeAddress_t, bool> m_AddrInSled; + std::set<Sled_t> m_sleds; + + +}; + + +#endif diff --git a/zipr/include/plugin_man.h b/zipr/include/plugin_man.h new file mode 100644 index 0000000000000000000000000000000000000000..d7ac3777e04025bacc0efb3e13c4277d5d1289b5 --- /dev/null +++ b/zipr/include/plugin_man.h @@ -0,0 +1,66 @@ +#ifndef plugin_man_h +#define plugin_man_h + +typedef Zipr_SDK::ZiprPluginInterface_t* DLFunctionHandle_t; +typedef std::vector<DLFunctionHandle_t> DLFunctionHandleSet_t; + +typedef Zipr_SDK::ZiprPluginInterface_t* (*GetPluginInterface_t)( + Zipr_SDK::Zipr_t* + ); + + +class ZiprPluginManager_t : public ZiprPluginInterface_t +{ + public: + ZiprPluginManager_t() : + m_verbose("verbose") + {} + + ZiprPluginManager_t + ( + Zipr_SDK::Zipr_t* zipr_obj, + zipr::ZiprOptions_t *p_opts + ) + : + m_verbose("verbose"), + m_opts(p_opts) + { + auto opts_global_ns = dynamic_cast<ZiprOptionsNamespace_t*>(m_opts->getNamespace("global")); + if (opts_global_ns) + opts_global_ns->addOption(&m_verbose); + open_plugins(zipr_obj, p_opts); + } + + virtual void PinningBegin(); + virtual void PinningEnd(); + + virtual void DollopBegin(); + virtual void DollopEnd(); + + virtual void CallbackLinkingBegin(); + virtual void CallbackLinkingEnd(); + + virtual RangeAddress_t PlaceScoopsBegin(const RangeAddress_t max_addr); + virtual RangeAddress_t PlaceScoopsEnd(const RangeAddress_t max_addr); + + virtual bool DoPluginsPlop(IRDB_SDK::Instruction_t*,std::list<DLFunctionHandle_t>&); + + virtual bool DoesPluginAddress(const Zipr_SDK::Dollop_t *, const RangeAddress_t &, Range_t &, bool &coalesce, bool &fallthrough_allowed, DLFunctionHandle_t &); + virtual bool DoesPluginRetargetCallback(const RangeAddress_t &, const DollopEntry_t *, RangeAddress_t &, DLFunctionHandle_t &) ; + virtual bool DoesPluginRetargetPin(const RangeAddress_t &, const Dollop_t *, RangeAddress_t &, DLFunctionHandle_t &) ; + + + private: + + void open_plugins + ( + Zipr_SDK::Zipr_t* zipr_obj, + Zipr_SDK::ZiprOptionsManager_t *p_opts + ); + ZiprBooleanOption_t m_verbose; + zipr::ZiprOptions_t *m_opts; + // storage for handles that've been dlopened() + DLFunctionHandleSet_t m_handleList; + +}; +#endif diff --git a/zipr/include/sizer/sizer_arm32.hpp b/zipr/include/sizer/sizer_arm32.hpp new file mode 100644 index 0000000000000000000000000000000000000000..82950bf2e6b5fd7abe6f1c5e850234b312edd1ec --- /dev/null +++ b/zipr/include/sizer/sizer_arm32.hpp @@ -0,0 +1,17 @@ +#ifndef SIZERARM32_HPP +#define SIZERARM32_HPP + +class ZiprSizerARM32_t : public ZiprSizerBase_t +{ + private: + RangeAddress_t TBZPlopDollopEntryWithTarget (Zipr_SDK::DollopEntry_t *entry, RangeAddress_t override_place, RangeAddress_t override_target) const; + RangeAddress_t DefaultPlopDollopEntryWithTarget(Zipr_SDK::DollopEntry_t *entry, RangeAddress_t override_place, RangeAddress_t override_target) const; + + public: + ZiprSizerARM32_t(Zipr_SDK::Zipr_t* p_zipr_obj); + size_t DetermineInsnSize(IRDB_SDK::Instruction_t*, bool account_for_jump = true) const override; + virtual RangeAddress_t PlopDollopEntryWithTarget(Zipr_SDK::DollopEntry_t *entry, RangeAddress_t override_place, RangeAddress_t override_target) const override; + + +}; +#endif diff --git a/zipr/include/sizer/sizer_arm64.hpp b/zipr/include/sizer/sizer_arm64.hpp new file mode 100644 index 0000000000000000000000000000000000000000..7977382769e0b30448f4e85f8bbd5f458b43e719 --- /dev/null +++ b/zipr/include/sizer/sizer_arm64.hpp @@ -0,0 +1,17 @@ +#ifndef SIZERARM64_HPP +#define SIZERARM64_HPP + +class ZiprSizerARM64_t : public ZiprSizerBase_t +{ + private: + RangeAddress_t TBZPlopDollopEntryWithTarget (Zipr_SDK::DollopEntry_t *entry, RangeAddress_t override_place, RangeAddress_t override_target) const; + RangeAddress_t DefaultPlopDollopEntryWithTarget(Zipr_SDK::DollopEntry_t *entry, RangeAddress_t override_place, RangeAddress_t override_target) const; + + public: + ZiprSizerARM64_t(Zipr_SDK::Zipr_t* p_zipr_obj); + size_t DetermineInsnSize(IRDB_SDK::Instruction_t*, bool account_for_jump = true) const override; + virtual RangeAddress_t PlopDollopEntryWithTarget(Zipr_SDK::DollopEntry_t *entry, RangeAddress_t override_place, RangeAddress_t override_target) const override; + + +}; +#endif diff --git a/zipr/include/sizer/sizer_base.hpp b/zipr/include/sizer/sizer_base.hpp new file mode 100644 index 0000000000000000000000000000000000000000..93069e25a762218a51276496fa3e24e7328e1d5d --- /dev/null +++ b/zipr/include/sizer/sizer_base.hpp @@ -0,0 +1,45 @@ +#ifndef SIZERBASE_HPP +#define SIZERBASE_HPP + +class ZiprImpl_t; + +class ZiprSizerBase_t +{ + protected: + ZiprSizerBase_t(Zipr_SDK::Zipr_t* p_zipr_obj, + const size_t p_CALLBACK_TRAMPOLINE_SIZE, + const size_t p_TRAMPOLINE_SIZE, + const size_t p_LONG_PIN_SIZE, + const size_t p_SHORT_PIN_SIZE, + const size_t p_ALIGNMENT + ) ; + ZiprMemorySpace_t &memory_space; + ZiprImpl_t &m_zipr_obj; + public: + virtual ~ZiprSizerBase_t() {} + // methods + /** Calculate entire size of a dollop and it's fallthroughs. + * + * Calculate the space needed to place dollop + * and all of it's fallthroughs. This function + * makes sure not to overcalculate the trampoline + * space to accomodate for the fallthroughs. + */ + virtual size_t DetermineDollopSizeInclFallthrough(Zipr_SDK::Dollop_t *dollop) const; + virtual size_t DetermineInsnSize(IRDB_SDK::Instruction_t*, bool account_for_jump = true) const =0; + virtual Range_t DoPlacement(size_t pminimum_valid_req_size) const; + virtual RangeAddress_t PlopDollopEntryWithTarget(Zipr_SDK::DollopEntry_t *entry, RangeAddress_t override_place, RangeAddress_t override_target) const =0; + + // maybe try to make these private/protected and provide getters eventually. + const size_t CALLBACK_TRAMPOLINE_SIZE; + const size_t TRAMPOLINE_SIZE; + const size_t LONG_PIN_SIZE; + const size_t SHORT_PIN_SIZE; + const size_t ALIGNMENT; + + // factory + static std::unique_ptr<ZiprSizerBase_t> factory(Zipr_SDK::Zipr_t *zipr_obj); + +}; + +#endif diff --git a/zipr/include/sizer/sizer_mips32.hpp b/zipr/include/sizer/sizer_mips32.hpp new file mode 100644 index 0000000000000000000000000000000000000000..6c22a626b110a4705546c42673ea2e9583e5bb77 --- /dev/null +++ b/zipr/include/sizer/sizer_mips32.hpp @@ -0,0 +1,15 @@ +#ifndef SIZERMIPS32_HPP +#define SIZERMIPS32_HPP + +class ZiprSizerMIPS32_t : public ZiprSizerBase_t +{ + private: + RangeAddress_t TBZPlopDollopEntryWithTarget (Zipr_SDK::DollopEntry_t *entry, RangeAddress_t override_place, RangeAddress_t override_target) const; + RangeAddress_t DefaultPlopDollopEntryWithTarget(Zipr_SDK::DollopEntry_t *entry, RangeAddress_t override_place, RangeAddress_t override_target) const; + + public: + ZiprSizerMIPS32_t(Zipr_SDK::Zipr_t* p_zipr_obj); + size_t DetermineInsnSize(IRDB_SDK::Instruction_t*, bool account_for_jump = true) const override; + virtual RangeAddress_t PlopDollopEntryWithTarget(Zipr_SDK::DollopEntry_t *entry, RangeAddress_t override_place, RangeAddress_t override_target) const override; +}; +#endif diff --git a/zipr/include/sizer/sizer_x86.hpp b/zipr/include/sizer/sizer_x86.hpp new file mode 100644 index 0000000000000000000000000000000000000000..69b51796d3dffeee9356263c702fe68953a8bb41 --- /dev/null +++ b/zipr/include/sizer/sizer_x86.hpp @@ -0,0 +1,14 @@ +#ifndef SIZERX86_HPP +#define SIZERX86_HPP + + +class ZiprSizerX86_t : public ZiprSizerBase_t +{ + public: + ZiprSizerX86_t(Zipr_SDK::Zipr_t* p_zipr_obj); + size_t DetermineInsnSize(IRDB_SDK::Instruction_t*, bool account_for_jump = true) const override; + virtual RangeAddress_t PlopDollopEntryWithTarget(Zipr_SDK::DollopEntry_t *entry, RangeAddress_t override_place, RangeAddress_t override_target) const override; + + +}; +#endif diff --git a/zipr/include/sled.h b/zipr/include/sled.h new file mode 100644 index 0000000000000000000000000000000000000000..98f53e34d10b3db44a2e3a65e8001e2a9796440b --- /dev/null +++ b/zipr/include/sled.h @@ -0,0 +1,169 @@ +/*************************************************************************** + * Copyright (c) 2014 Zephyr Software LLC. All rights reserved. + * + * This software is furnished under a license and/or other restrictive + * terms and may be used and copied only in accordance with such terms + * and the inclusion of the above copyright notice. This software or + * any other copies thereof may not be provided or otherwise made + * available to any other person without the express written consent + * of an authorized representative of Zephyr Software LCC. Title to, + * ownership of, and all rights in the software is retained by + * Zephyr Software LCC. + * + * Zephyr Software LLC. Proprietary Information + * + * Unless otherwise specified, the information contained in this + * directory, following this legend, and/or referenced herein is + * Zephyr Software LLC. (Zephyr) Proprietary Information. + * + * CONTACT + * + * For technical assistance, contact Zephyr Software LCC. at: + * + * + * Zephyr Software, LLC + * 2040 Tremont Rd + * Charlottesville, VA 22911 + * + * E-mail: jwd@zephyr-software.com + **************************************************************************/ + +#ifndef sled_h +#define sled_h + +class Sled_t +{ + public: + Sled_t(const Zipr_SDK::MemorySpace_t &memory_space, + Range_t range, + bool verbose=true) + : m_memory_space(memory_space), + m_range(range), + m_verbose(verbose) + { + } + + Zipr_SDK::Range_t SledRange(void) const + { + return m_range; + } + void SledRange(Zipr_SDK::Range_t range) + { + m_range = range; + } + + bool Overlaps(Sled_t candidate) + { + if (m_verbose) + std::cout << "Comparing " << *this << " with candidate " << candidate << std::endl; + /* + * candidate sled: candidate + * existing sled: this + * + * <----------------> + * candidate + * <---------------> + * existing + */ + return candidate.SledRange().getStart() <= m_range.getEnd() && + candidate.SledRange().getEnd() >= m_range.getStart(); + } + void MergeSledJumpPoints(Sled_t merging) + { + for (auto jmp_pnt : merging.m_jmp_pts) + { + if (m_verbose) + std::cout << "Merging jump point: " + << std::hex << jmp_pnt << std::endl; + + m_jmp_pts.insert(jmp_pnt); + } + } + + /* + * NB: We can only merge in sleds that are before + * this sled. + */ + void MergeSled(Sled_t merging) + { + /* + * merging sled: sled + * existing sled: this + * + * <---------<------>--------> + * merging + * <---------------> + * existing + */ + assert(merging.SledRange().getStart() <= m_range.getEnd() && + merging.SledRange().getEnd() >= m_range.getStart()); + + /* + * First, extend our range. + */ + m_range.setStart(std::min(merging.SledRange().getStart(), + m_range.getStart())); + + m_range.setEnd(std::max(merging.SledRange().getEnd(), + m_range.getEnd())); + + /* + * Second, merge in the jump points to this one. + */ + MergeSledJumpPoints(merging); + } + + IRDB_SDK::Instruction_t *Disambiguation(void) const + { + return m_disambiguation; + } + void Disambiguation(IRDB_SDK::Instruction_t *disambiguation) + { + m_disambiguation = disambiguation; + } + void AddJumpPoint(RangeAddress_t jmp_point) + { + m_jmp_pts.insert(jmp_point); + } + + std::set<RangeAddress_t>::iterator JumpPointsBegin() + { + return m_jmp_pts.begin(); + } + std::set<RangeAddress_t>::iterator JumpPointsEnd() + { + return m_jmp_pts.end(); + } + std::set<RangeAddress_t>::reverse_iterator JumpPointsReverseBegin() + { + return m_jmp_pts.rbegin(); + } + std::set<RangeAddress_t>::reverse_iterator JumpPointsReverseEnd() + { + return m_jmp_pts.rend(); + } + friend bool operator<(const Sled_t &lhs, const Sled_t &rhs); + friend std::ostream &operator<<(std::ostream &out, const Sled_t &sled); + + private: + const Zipr_SDK::MemorySpace_t &m_memory_space; + IRDB_SDK::Instruction_t *m_disambiguation; + std::set<RangeAddress_t> m_jmp_pts; + Range_t m_range; + bool m_verbose; +}; + +inline std::ostream &operator<<(std::ostream &out, const Sled_t &sled) +{ + return out << "Sled (" + << std::hex << sled.m_range.getStart() + << std::hex << " - " << sled.m_range.getEnd() + << ") with disambiguation " + << std::hex << sled.Disambiguation(); +} +inline bool operator<(const Sled_t &lhs, const Sled_t &rhs) +{ + return lhs.SledRange().getStart() < rhs.SledRange().getStart(); +} + +#endif //sled_h diff --git a/zipr/include/unresolved.h b/zipr/include/unresolved.h new file mode 100644 index 0000000000000000000000000000000000000000..0c764e5a396d228c14cb56ec3b22e4fb264c314c --- /dev/null +++ b/zipr/include/unresolved.h @@ -0,0 +1,156 @@ +/*************************************************************************** + * Copyright (c) 2014 Zephyr Software LLC. All rights reserved. + * + * This software is furnished under a license and/or other restrictive + * terms and may be used and copied only in accordance with such terms + * and the inclusion of the above copyright notice. This software or + * any other copies thereof may not be provided or otherwise made + * available to any other person without the express written consent + * of an authorized representative of Zephyr Software LCC. Title to, + * ownership of, and all rights in the software is retained by + * Zephyr Software LCC. + * + * Zephyr Software LLC. Proprietary Information + * + * Unless otherwise specified, the information contained in this + * directory, following this legend, and/or referenced herein is + * Zephyr Software LLC. (Zephyr) Proprietary Information. + * + * CONTACT + * + * For technical assistance, contact Zephyr Software LCC. at: + * + * + * Zephyr Software, LLC + * 2040 Tremont Rd + * Charlottesville, VA 22911 + * + * E-mail: jwd@zephyr-software.com + **************************************************************************/ + +#ifndef unresolved_h +#define unresolved_h + + + +enum UnresolvedType_t +{ + CondJump, + UncondJump_rel8, + UncondJump_rel32, + UncondJump_rel26, + Call, + Data32, + Data64 +}; + +class UnresolvedInfo_t +{ + public: + virtual IRDB_SDK::Instruction_t* getInstrution() const =0 ; + private: +}; + +// instructions that need a pin, but don't yet have one +class UnresolvedPinned_t : public UnresolvedInfo_t +{ + public: + UnresolvedPinned_t(IRDB_SDK::Instruction_t* p_from) : from_instruction(p_from), m_range(0,0), m_updated_address(0) { assert(p_from); } + UnresolvedPinned_t(IRDB_SDK::Instruction_t* p_from, Range_t range) : from_instruction(p_from), m_range(range), m_updated_address(0) { assert(p_from); } + IRDB_SDK::Instruction_t* getInstrution() const { return from_instruction; } + + /* + * Use the range to store the place where + * reserved space is held for this + * instruction. + */ + Range_t GetRange() const { return m_range; }; + void SetRange(Range_t range) { m_range = range; }; + bool HasRange() + { + return m_range.getStart() != 0 || m_range.getEnd() != 0; + }; + + /* + * Store an address with the UnresolvedPinned + * in case this instruction needs to float + * through the program based on a chain + * of two-byte calls. + */ + bool HasUpdatedAddress() const + { + return m_updated_address != 0; + }; + void SetUpdatedAddress(RangeAddress_t address) + { + m_updated_address = address; + }; + RangeAddress_t GetUpdatedAddress() const + { + return m_updated_address; + }; + + private: + IRDB_SDK::Instruction_t* from_instruction; + Range_t m_range; + RangeAddress_t m_updated_address; + + friend bool operator< (const UnresolvedPinned_t& lhs, const UnresolvedPinned_t& rhs); + +}; + +inline bool operator< (const UnresolvedPinned_t& lhs, const UnresolvedPinned_t& rhs) +{ + assert(lhs.from_instruction); + assert(rhs.from_instruction); + return lhs.from_instruction < rhs.from_instruction; +} + +// an ELF location that needs patching when an Unresolved instrcution +class Patch_t +{ + public: + Patch_t(RangeAddress_t p_from_addr, UnresolvedType_t p_t) : from_addr(p_from_addr), type(p_t) {} + + RangeAddress_t getAddress() const { return from_addr; } + UnresolvedType_t getType() { return type; } + void setType(UnresolvedType_t p_t) { type = p_t; } + size_t getSize() { + switch (type) { + case UncondJump_rel8: + return 2; + case UncondJump_rel32: + return 5; + default: + return 0; + } + } + + private: + RangeAddress_t from_addr; + UnresolvedType_t type; +}; + +// instructions that can float to any address, but don't yet have one +class UnresolvedUnpinned_t : public UnresolvedInfo_t +{ + public: + UnresolvedUnpinned_t(UnresolvedPinned_t up) : from_instruction(up.getInstrution()) {} + UnresolvedUnpinned_t(IRDB_SDK::Instruction_t* p_from) : from_instruction(p_from) + { assert(p_from); } + IRDB_SDK::Instruction_t* getInstrution() const { assert(from_instruction); return from_instruction; } + private: + IRDB_SDK::Instruction_t *from_instruction; + + friend bool operator< (const UnresolvedUnpinned_t& lhs, const UnresolvedUnpinned_t& rhs); +}; + +inline bool operator< (const UnresolvedUnpinned_t& lhs, const UnresolvedUnpinned_t& rhs) +{ + assert(lhs.from_instruction); + assert(rhs.from_instruction); + return lhs.from_instruction < rhs.from_instruction; +} + +typedef std::pair<UnresolvedUnpinned_t, Patch_t> UnresolvedUnpinnedPatch_t; +#endif diff --git a/zipr/include/zipr_all.h b/zipr/include/zipr_all.h new file mode 100644 index 0000000000000000000000000000000000000000..7fcf39e746c14301f51d28cb1ad2af463abac72b --- /dev/null +++ b/zipr/include/zipr_all.h @@ -0,0 +1,91 @@ +/*************************************************************************** + * Copyright (c) 2014 Zephyr Software LLC. All rights reserved. + * + * This software is furnished under a license and/or other restrictive + * terms and may be used and copied only in accordance with such terms + * and the inclusion of the above copyright notice. This software or + * any other copies thereof may not be provided or otherwise made + * available to any other person without the express written consent + * of an authorized representative of Zephyr Software LCC. Title to, + * ownership of, and all rights in the software is retained by + * Zephyr Software LCC. + * + * Zephyr Software LLC. Proprietary Information + * + * Unless otherwise specified, the information contained in this + * directory, following this legend, and/or referenced herein is + * Zephyr Software LLC. (Zephyr) Proprietary Information. + * + * CONTACT + * + * For technical assistance, contact Zephyr Software LCC. at: + * + * + * Zephyr Software, LLC + * 2040 Tremont Rd + * Charlottesville, VA 22911 + * + * E-mail: jwd@zephyr-software.com + **************************************************************************/ + +#ifndef zipr_all_h +#define zipr_all_h + +#include <stdlib.h> +#include <stdint.h> +#include <set> +#include <list> +#include <map> +#include <irdb-core> +#include <irdb-transform> +#include <algorithm> +#include <memory> + +#include "exeio.h" + +#pragma GCC diagnostic ignored "-Wsign-compare" +#include "elfio/elfio.hpp" +#include "elfio/elfio_dump.hpp" +#include "elf.h" +#pragma GCC diagnostic pop + +#include <zipr-sdk> +#include <zipr_options.h> + +namespace zipr +{ + +using namespace std; +using namespace Zipr_SDK; + +#define PAGE_SIZE 4096 + +#include <sled.h> +#include <unresolved.h> +#include <zipr_mem_space.h> +#include <plugin_man.h> +} + +// trying to move the namespace zipr into the header files +#include <zipr_dollop_man.h> + +namespace zipr +{ +#include <zipr_utils.h> +#include <pinner/pinner_base.hpp> +#include <patcher/patcher_base.hpp> +#include <sizer/sizer_base.hpp> +#include <arch/arch_base.hpp> +#include <zipr_impl.h> +#include <zipr_optimizations.h> +#include <zipr_stats.h> +#include <exewrite.h> +#include <elfwrite.h> +#include <pewrite.h> +#include <ehwrite.h> + +}; + +#include <zipr_dollop.h> + +#endif diff --git a/zipr/include/zipr_dollop.h b/zipr/include/zipr_dollop.h new file mode 100644 index 0000000000000000000000000000000000000000..dd2ca7c0373c5423f49360a45a46e7326f2e1969 --- /dev/null +++ b/zipr/include/zipr_dollop.h @@ -0,0 +1,125 @@ +#include <assert.h> +#include <list> + +namespace zipr +{ + using namespace std; + using namespace IRDB_SDK; + + class Dollop_t; + class DollopManager_t; + + class Placeable_t : virtual public Zipr_SDK::Placeable_t + { + public: + Placeable_t() : m_is_placed(false), m_placed_address(0) {} + void Place(RangeAddress_t place) { + assert(!m_is_placed); + m_is_placed = true; + m_placed_address = place; + }; + RangeAddress_t getPlace() const { return m_placed_address; } + bool isPlaced() const { return m_is_placed; } + protected: + bool m_is_placed; + RangeAddress_t m_placed_address; + + friend ostream &operator<<(ostream &, const Placeable_t &); + }; + + + class DollopPatch_t : /*virtual public Zipr_SDK::DollopPatch_t, */ public Placeable_t + { + public: + DollopPatch_t(Zipr_SDK::Dollop_t *target) : m_target(target) {}; + DollopPatch_t() : m_target(NULL) { }; + + Zipr_SDK::Dollop_t *getTarget() const { return m_target; } + void setTarget(Zipr_SDK::Dollop_t *target) { m_target = target; } + + friend ostream &operator<<(ostream &, const DollopPatch_t &); + private: + Zipr_SDK::Dollop_t *m_target; + }; + + class DollopEntry_t : virtual public Zipr_SDK::DollopEntry_t, public Placeable_t + { + public: + DollopEntry_t(Instruction_t *insn, Zipr_SDK::Dollop_t *member_of); + Instruction_t *getInstruction() const { return m_instruction; } + void setTargetDollop(Zipr_SDK::Dollop_t *target) { m_target_dollop = target; } + Zipr_SDK::Dollop_t *getTargetDollop() const { return m_target_dollop; } + Zipr_SDK::Dollop_t *getMemberOfDollop() const { return m_member_of_dollop; } + void MemberOfDollop(Zipr_SDK::Dollop_t *member_of) { m_member_of_dollop = member_of; } + + bool operator==(const DollopEntry_t &); + bool operator!=(const DollopEntry_t &); + private: + Instruction_t *m_instruction; + Zipr_SDK::Dollop_t *m_target_dollop, *m_member_of_dollop; + + friend ostream &operator<<(ostream &, const DollopEntry_t &); + }; + + class Dollop_t : virtual public Zipr_SDK::Dollop_t, public Placeable_t + { + public: + Dollop_t(Instruction_t *start, Zipr_SDK::DollopManager_t *); + Dollop_t() : + m_size(0), + m_fallthrough_dollop(NULL), + m_fallback_dollop(NULL), + m_fallthrough_patched(false), + m_coalesced(false), + m_was_truncated(false), + m_dollop_mgr(NULL) {} + + void setDollopManager(Zipr_SDK::DollopManager_t *mgr) { m_dollop_mgr = mgr; } + + size_t getSize() const { + return m_size; + } + void setSize(size_t size) { + m_size = size; + } + + size_t getDollopEntryCount() const { + return size(); + } + + Zipr_SDK::Dollop_t *split(Instruction_t *split_point); + void removeDollopEntries(Zipr_SDK::DollopEntryList_t::iterator, Zipr_SDK::DollopEntryList_t::iterator); + + void setFallbackDollop(Zipr_SDK::Dollop_t *fallback) { m_fallback_dollop = fallback; } + Zipr_SDK::Dollop_t *getFallbackDollop(void) const { return m_fallback_dollop; } + + void setFallthroughDollop(Zipr_SDK::Dollop_t *fallthrough) { m_fallthrough_dollop = fallthrough; } + Zipr_SDK::Dollop_t *getFallthroughDollop(void) const { return m_fallthrough_dollop; } + + bool isFallthroughPatched(void) const { return m_fallthrough_patched; } + void setFallthroughPatched(bool patched); + + Zipr_SDK::DollopEntry_t *getFallthroughDollopEntry(Zipr_SDK::DollopEntry_t *) const; + + bool wasTruncated(void) const { return m_was_truncated; } + void setTruncated(bool truncated) { m_was_truncated = truncated; } + + void setCoalesced(bool coalesced); + bool wasCoalesced(void) const { return m_coalesced; } + + void reCalculateSize(); + + static Zipr_SDK::Dollop_t* createNewDollop(IRDB_SDK::Instruction_t *start, Zipr_SDK::DollopManager_t *mgr); + + private: + size_t CalculateSize(); + size_t m_size; + Zipr_SDK::Dollop_t *m_fallthrough_dollop, *m_fallback_dollop; + bool m_fallthrough_patched; + bool m_coalesced; + bool m_was_truncated; + Zipr_SDK::DollopManager_t *m_dollop_mgr; + + friend ostream &operator<<(ostream &, const Dollop_t &); + }; +} diff --git a/zipr/include/zipr_dollop_man.h b/zipr/include/zipr_dollop_man.h new file mode 100644 index 0000000000000000000000000000000000000000..90026124995da3151fcf55122b024a1976e556dc --- /dev/null +++ b/zipr/include/zipr_dollop_man.h @@ -0,0 +1,158 @@ +/*************************************************************************** + * Copyright (c) 2014 Zephyr Software LLC. All rights reserved. + * + * This software is furnished under a license and/or other restrictive + * terms and may be used and copied only in accordance with such terms + * and the inclusion of the above copyright notice. This software or + * any other copies thereof may not be provided or otherwise made + * available to any other person without the express written consent + * of an authorized representative of Zephyr Software LCC. Title to, + * ownership of, and all rights in the software is retained by + * Zephyr Software LCC. + * + * Zephyr Software LLC. Proprietary Information + * + * Unless otherwise specified, the information contained in this + * directory, following this legend, and/or referenced herein is + * Zephyr Software LLC. (Zephyr) Proprietary Information. + * + * CONTACT + * + * For technical assistance, contact Zephyr Software LCC. at: + * + * + * Zephyr Software, LLC + * 2040 Tremont Rd + * Charlottesville, VA 22911 + * + * E-mail: jwd@zephyr-software.com + **************************************************************************/ + +#ifndef zipr_dollop_man_h +#define zipr_dollop_man_h + +#include <zipr-sdk> + +namespace zipr +{ + class DollopPatch_t; + + typedef std::set<Dollop_t*> DollopList_t; + typedef std::map<IRDB_SDK::Instruction_t*,Dollop_t*> InsnToDollopMap_t; + typedef std::list<DollopPatch_t*> DollopPatchList_t; + typedef std::map<Dollop_t*, DollopPatchList_t > DollopToDollopPatchListMap_t; + + class ZiprDollopManager_t : public DollopManager_t + { + public: + ZiprDollopManager_t() : m_refresh_stats(true), m_zipr(nullptr) {} + ZiprDollopManager_t(Zipr_SDK::Zipr_t *zipr) : m_refresh_stats(true), m_zipr(zipr) {} + + /* + * Adders. + */ + void addDollops(Dollop_t *dollop_head) { return AddDollops(dollop_head); } + void AddDollops(Dollop_t *dollop_head); + + + Zipr_SDK::Dollop_t *addNewDollops(IRDB_SDK::Instruction_t *start) { return AddNewDollops(start); } + Zipr_SDK::Dollop_t *AddNewDollops(IRDB_SDK::Instruction_t *start); + + /* + * Getters. + */ + Zipr_SDK::Dollop_t *getContainingDollop(IRDB_SDK::Instruction_t *insn); + + size_t getSize() { return Size(); } + size_t Size() { + return m_dollops.size(); + } + + /* + * Patch functions. + */ + void addDollopPatch(DollopPatch_t *new_patch) { return AddDollopPatch(new_patch); } + void AddDollopPatch(DollopPatch_t *new_patch) ; + + std::list<DollopPatch_t*> getPatchesToDollop(Dollop_t *target) { return PatchesToDollop(target); } + std::list<DollopPatch_t*> PatchesToDollop(Dollop_t *target) { + /* + * FIXME: This will throw an exception if target is + * not already in the list. [] fixes that but allocates. + * Decide what to do. + */ + return m_patches_to_dollops.at(target); + } + + /* + * Dollop target update functions. + */ + bool updateTargets(Dollop_t *t) { return UpdateTargets(t); } + bool UpdateTargets(Dollop_t *t); + + void updateAllTargets() { return UpdateAllTargets(); } + void UpdateAllTargets(); + + /* + * Iteration functions. + */ + DollopList_t::iterator begin() { return dollops_begin(); } + DollopList_t::iterator end() { return dollops_end(); } + + DollopList_t::iterator dollops_begin() { + return m_dollops.begin(); + } + DollopList_t::iterator dollops_end() { + return m_dollops.end(); + } + DollopList_t& getDollops() { return m_dollops; } + const DollopList_t& getDollops() const { return m_dollops; } + + /* + * Printing/output functions. + */ + void printDollopPatches(const std::ostream &o) { return PrintDollopPatches(o); } + void PrintDollopPatches(const std::ostream &o); + + friend std::ostream &operator<<(std::ostream &out, + const ZiprDollopManager_t &dollop_man); + + void printStats(std::ostream &out) { return PrintStats(out); } + void PrintStats(std::ostream &out); + + void printPlacementMap(const MemorySpace_t &memory_space, const std::string &map_filename) { return PrintPlacementMap(memory_space,map_filename); } + void PrintPlacementMap(const MemorySpace_t &memory_space, const std::string &map_filename); + + /* + * Helper functions. + */ + size_t determineDollopEntrySize(DollopEntry_t *entry) { return DetermineDollopEntrySize(entry); } + size_t DetermineDollopEntrySize(DollopEntry_t *entry); + + Zipr_SDK::Zipr_t* GetZipr() const { return m_zipr; } + private: + /* + * Helper functions. + */ + void addDollop(Dollop_t *dollop); + void CalculateStats(); + + /* + * Support variables. + */ + DollopList_t m_dollops; + InsnToDollopMap_t m_insn_to_dollop; + DollopPatchList_t m_patches; + DollopToDollopPatchListMap_t m_patches_to_dollops; + + /* + * Statistics. + */ + bool m_refresh_stats; + size_t m_total_dollop_space, m_total_dollop_entries; + unsigned int m_total_dollops, m_truncated_dollops; + + Zipr_SDK::Zipr_t* m_zipr; + }; +} +#endif diff --git a/zipr/include/zipr_dwarf2.hpp b/zipr/include/zipr_dwarf2.hpp new file mode 100644 index 0000000000000000000000000000000000000000..2e07329d706243f4705251f7611b104f8ca0a26e --- /dev/null +++ b/zipr/include/zipr_dwarf2.hpp @@ -0,0 +1,584 @@ +/* + Copyright 2017-2018 University of Virginia + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +#ifndef _DWARF2_H +#define _DWARF2_H 1 + +/* This file is derived from the DWARF specification (a public document) + Revision 2.0.0 (July 27, 1993) developed by the UNIX International + Programming Languages Special Interest Group (UI/PLSIG) and distributed + by UNIX International. Copies of this specification are available from + UNIX International, 20 Waterview Boulevard, Parsippany, NJ, 07054. */ + + +enum dwarf_tag + { + DW_TAG_padding = 0x00, + DW_TAG_array_type = 0x01, + DW_TAG_class_type = 0x02, + DW_TAG_entry_point = 0x03, + DW_TAG_enumeration_type = 0x04, + DW_TAG_formal_parameter = 0x05, + DW_TAG_imported_declaration = 0x08, + DW_TAG_label = 0x0a, + DW_TAG_lexical_block = 0x0b, + DW_TAG_member = 0x0d, + DW_TAG_pointer_type = 0x0f, + DW_TAG_reference_type = 0x10, + DW_TAG_compile_unit = 0x11, + DW_TAG_string_type = 0x12, + DW_TAG_structure_type = 0x13, + DW_TAG_subroutine_type = 0x15, + DW_TAG_typedef = 0x16, + DW_TAG_union_type = 0x17, + DW_TAG_unspecified_parameters = 0x18, + DW_TAG_variant = 0x19, + DW_TAG_common_block = 0x1a, + DW_TAG_common_inclusion = 0x1b, + DW_TAG_inheritance = 0x1c, + DW_TAG_inlined_subroutine = 0x1d, + DW_TAG_module = 0x1e, + DW_TAG_ptr_to_member_type = 0x1f, + DW_TAG_set_type = 0x20, + DW_TAG_subrange_type = 0x21, + DW_TAG_with_stmt = 0x22, + DW_TAG_access_declaration = 0x23, + DW_TAG_base_type = 0x24, + DW_TAG_catch_block = 0x25, + DW_TAG_const_type = 0x26, + DW_TAG_constant = 0x27, + DW_TAG_enumerator = 0x28, + DW_TAG_file_type = 0x29, + DW_TAG_friend = 0x2a, + DW_TAG_namelist = 0x2b, + DW_TAG_namelist_item = 0x2c, + DW_TAG_packed_type = 0x2d, + DW_TAG_subprogram = 0x2e, + DW_TAG_template_type_param = 0x2f, + DW_TAG_template_value_param = 0x30, + DW_TAG_thrown_type = 0x31, + DW_TAG_try_block = 0x32, + DW_TAG_variant_part = 0x33, + DW_TAG_variable = 0x34, + DW_TAG_volatile_type = 0x35, + /* SGI/MIPS Extensions */ + DW_TAG_MIPS_loop = 0x4081, + /* GNU extensions */ + DW_TAG_format_label = 0x4101, /* for FORTRAN 77 and Fortran 90 */ + DW_TAG_function_template = 0x4102, /* for C++ */ + DW_TAG_class_template = 0x4103, /* for C++ */ + DW_TAG_GNU_BINCL = 0x4104, + DW_TAG_GNU_EINCL = 0x4105 + }; + +#define DW_TAG_lo_user 0x4080 +#define DW_TAG_hi_user 0xffff + +/* flag that tells whether entry has a child or not */ +#define DW_children_no 0 +#define DW_children_yes 1 + +/* Form names and codes. */ +enum dwarf_form + { + DW_FORM_addr = 0x01, + DW_FORM_block2 = 0x03, + DW_FORM_block4 = 0x04, + DW_FORM_data2 = 0x05, + DW_FORM_data4 = 0x06, + DW_FORM_data8 = 0x07, + DW_FORM_string = 0x08, + DW_FORM_block = 0x09, + DW_FORM_block1 = 0x0a, + DW_FORM_data1 = 0x0b, + DW_FORM_flag = 0x0c, + DW_FORM_sdata = 0x0d, + DW_FORM_strp = 0x0e, + DW_FORM_udata = 0x0f, + DW_FORM_ref_addr = 0x10, + DW_FORM_ref1 = 0x11, + DW_FORM_ref2 = 0x12, + DW_FORM_ref4 = 0x13, + DW_FORM_ref8 = 0x14, + DW_FORM_ref_udata = 0x15, + DW_FORM_indirect = 0x16 + }; + +/* Attribute names and codes. */ + +enum dwarf_attribute + { + DW_AT_sibling = 0x01, + DW_AT_location = 0x02, + DW_AT_name = 0x03, + DW_AT_ordering = 0x09, + DW_AT_subscr_data = 0x0a, + DW_AT_byte_size = 0x0b, + DW_AT_bit_offset = 0x0c, + DW_AT_bit_size = 0x0d, + DW_AT_element_list = 0x0f, + DW_AT_stmt_list = 0x10, + DW_AT_low_pc = 0x11, + DW_AT_high_pc = 0x12, + DW_AT_language = 0x13, + DW_AT_member = 0x14, + DW_AT_discr = 0x15, + DW_AT_discr_value = 0x16, + DW_AT_visibility = 0x17, + DW_AT_import = 0x18, + DW_AT_string_length = 0x19, + DW_AT_common_reference = 0x1a, + DW_AT_comp_dir = 0x1b, + DW_AT_const_value = 0x1c, + DW_AT_containing_type = 0x1d, + DW_AT_default_value = 0x1e, + DW_AT_inline = 0x20, + DW_AT_is_optional = 0x21, + DW_AT_lower_bound = 0x22, + DW_AT_producer = 0x25, + DW_AT_prototyped = 0x27, + DW_AT_return_addr = 0x2a, + DW_AT_start_scope = 0x2c, + DW_AT_stride_size = 0x2e, + DW_AT_upper_bound = 0x2f, + DW_AT_abstract_origin = 0x31, + DW_AT_accessibility = 0x32, + DW_AT_address_class = 0x33, + DW_AT_artificial = 0x34, + DW_AT_base_types = 0x35, + DW_AT_calling_convention = 0x36, + DW_AT_count = 0x37, + DW_AT_data_member_location = 0x38, + DW_AT_decl_column = 0x39, + DW_AT_decl_file = 0x3a, + DW_AT_decl_line = 0x3b, + DW_AT_declaration = 0x3c, + DW_AT_discr_list = 0x3d, + DW_AT_encoding = 0x3e, + DW_AT_external = 0x3f, + DW_AT_frame_base = 0x40, + DW_AT_friend = 0x41, + DW_AT_identifier_case = 0x42, + DW_AT_macro_info = 0x43, + DW_AT_namelist_items = 0x44, + DW_AT_priority = 0x45, + DW_AT_segment = 0x46, + DW_AT_specification = 0x47, + DW_AT_static_link = 0x48, + DW_AT_type = 0x49, + DW_AT_use_location = 0x4a, + DW_AT_variable_parameter = 0x4b, + DW_AT_virtuality = 0x4c, + DW_AT_vtable_elem_location = 0x4d, + /* SGI/MIPS Extensions */ + DW_AT_MIPS_fde = 0x2001, + DW_AT_MIPS_loop_begin = 0x2002, + DW_AT_MIPS_tail_loop_begin = 0x2003, + DW_AT_MIPS_epilog_begin = 0x2004, + DW_AT_MIPS_loop_unroll_factor = 0x2005, + DW_AT_MIPS_software_pipeline_depth = 0x2006, + DW_AT_MIPS_linkage_name = 0x2007, + DW_AT_MIPS_stride = 0x2008, + DW_AT_MIPS_abstract_name = 0x2009, + DW_AT_MIPS_clone_origin = 0x200a, + DW_AT_MIPS_has_inlines = 0x200b, + /* GNU extensions. */ + DW_AT_sf_names = 0x2101, + DW_AT_src_info = 0x2102, + DW_AT_mac_info = 0x2103, + DW_AT_src_coords = 0x2104, + DW_AT_body_begin = 0x2105, + DW_AT_body_end = 0x2106 + }; + +#define DW_AT_lo_user 0x2000 /* implementation-defined range start */ +#define DW_AT_hi_user 0x3ff0 /* implementation-defined range end */ + +/* Location atom names and codes. */ + +enum dwarf_location_atom + { + DW_OP_addr = 0x03, + DW_OP_deref = 0x06, + DW_OP_const1u = 0x08, + DW_OP_const1s = 0x09, + DW_OP_const2u = 0x0a, + DW_OP_const2s = 0x0b, + DW_OP_const4u = 0x0c, + DW_OP_const4s = 0x0d, + DW_OP_const8u = 0x0e, + DW_OP_const8s = 0x0f, + DW_OP_constu = 0x10, + DW_OP_consts = 0x11, + DW_OP_dup = 0x12, + DW_OP_drop = 0x13, + DW_OP_over = 0x14, + DW_OP_pick = 0x15, + DW_OP_swap = 0x16, + DW_OP_rot = 0x17, + DW_OP_xderef = 0x18, + DW_OP_abs = 0x19, + DW_OP_and = 0x1a, + DW_OP_div = 0x1b, + DW_OP_minus = 0x1c, + DW_OP_mod = 0x1d, + DW_OP_mul = 0x1e, + DW_OP_neg = 0x1f, + DW_OP_not = 0x20, + DW_OP_or = 0x21, + DW_OP_plus = 0x22, + DW_OP_plus_uconst = 0x23, + DW_OP_shl = 0x24, + DW_OP_shr = 0x25, + DW_OP_shra = 0x26, + DW_OP_xor = 0x27, + DW_OP_bra = 0x28, + DW_OP_eq = 0x29, + DW_OP_ge = 0x2a, + DW_OP_gt = 0x2b, + DW_OP_le = 0x2c, + DW_OP_lt = 0x2d, + DW_OP_ne = 0x2e, + DW_OP_skip = 0x2f, + DW_OP_lit0 = 0x30, + DW_OP_lit1 = 0x31, + DW_OP_lit2 = 0x32, + DW_OP_lit3 = 0x33, + DW_OP_lit4 = 0x34, + DW_OP_lit5 = 0x35, + DW_OP_lit6 = 0x36, + DW_OP_lit7 = 0x37, + DW_OP_lit8 = 0x38, + DW_OP_lit9 = 0x39, + DW_OP_lit10 = 0x3a, + DW_OP_lit11 = 0x3b, + DW_OP_lit12 = 0x3c, + DW_OP_lit13 = 0x3d, + DW_OP_lit14 = 0x3e, + DW_OP_lit15 = 0x3f, + DW_OP_lit16 = 0x40, + DW_OP_lit17 = 0x41, + DW_OP_lit18 = 0x42, + DW_OP_lit19 = 0x43, + DW_OP_lit20 = 0x44, + DW_OP_lit21 = 0x45, + DW_OP_lit22 = 0x46, + DW_OP_lit23 = 0x47, + DW_OP_lit24 = 0x48, + DW_OP_lit25 = 0x49, + DW_OP_lit26 = 0x4a, + DW_OP_lit27 = 0x4b, + DW_OP_lit28 = 0x4c, + DW_OP_lit29 = 0x4d, + DW_OP_lit30 = 0x4e, + DW_OP_lit31 = 0x4f, + DW_OP_reg0 = 0x50, + DW_OP_reg1 = 0x51, + DW_OP_reg2 = 0x52, + DW_OP_reg3 = 0x53, + DW_OP_reg4 = 0x54, + DW_OP_reg5 = 0x55, + DW_OP_reg6 = 0x56, + DW_OP_reg7 = 0x57, + DW_OP_reg8 = 0x58, + DW_OP_reg9 = 0x59, + DW_OP_reg10 = 0x5a, + DW_OP_reg11 = 0x5b, + DW_OP_reg12 = 0x5c, + DW_OP_reg13 = 0x5d, + DW_OP_reg14 = 0x5e, + DW_OP_reg15 = 0x5f, + DW_OP_reg16 = 0x60, + DW_OP_reg17 = 0x61, + DW_OP_reg18 = 0x62, + DW_OP_reg19 = 0x63, + DW_OP_reg20 = 0x64, + DW_OP_reg21 = 0x65, + DW_OP_reg22 = 0x66, + DW_OP_reg23 = 0x67, + DW_OP_reg24 = 0x68, + DW_OP_reg25 = 0x69, + DW_OP_reg26 = 0x6a, + DW_OP_reg27 = 0x6b, + DW_OP_reg28 = 0x6c, + DW_OP_reg29 = 0x6d, + DW_OP_reg30 = 0x6e, + DW_OP_reg31 = 0x6f, + DW_OP_breg0 = 0x70, + DW_OP_breg1 = 0x71, + DW_OP_breg2 = 0x72, + DW_OP_breg3 = 0x73, + DW_OP_breg4 = 0x74, + DW_OP_breg5 = 0x75, + DW_OP_breg6 = 0x76, + DW_OP_breg7 = 0x77, + DW_OP_breg8 = 0x78, + DW_OP_breg9 = 0x79, + DW_OP_breg10 = 0x7a, + DW_OP_breg11 = 0x7b, + DW_OP_breg12 = 0x7c, + DW_OP_breg13 = 0x7d, + DW_OP_breg14 = 0x7e, + DW_OP_breg15 = 0x7f, + DW_OP_breg16 = 0x80, + DW_OP_breg17 = 0x81, + DW_OP_breg18 = 0x82, + DW_OP_breg19 = 0x83, + DW_OP_breg20 = 0x84, + DW_OP_breg21 = 0x85, + DW_OP_breg22 = 0x86, + DW_OP_breg23 = 0x87, + DW_OP_breg24 = 0x88, + DW_OP_breg25 = 0x89, + DW_OP_breg26 = 0x8a, + DW_OP_breg27 = 0x8b, + DW_OP_breg28 = 0x8c, + DW_OP_breg29 = 0x8d, + DW_OP_breg30 = 0x8e, + DW_OP_breg31 = 0x8f, + DW_OP_regx = 0x90, + DW_OP_fbreg = 0x91, + DW_OP_bregx = 0x92, + DW_OP_piece = 0x93, + DW_OP_deref_size = 0x94, + DW_OP_xderef_size = 0x95, + DW_OP_nop = 0x96 + }; + +#define DW_OP_lo_user 0x80 /* implementation-defined range start */ +#define DW_OP_hi_user 0xff /* implementation-defined range end */ + +/* Type encodings. */ + +enum dwarf_type + { + DW_ATE_void = 0x0, + DW_ATE_address = 0x1, + DW_ATE_boolean = 0x2, + DW_ATE_complex_float = 0x3, + DW_ATE_float = 0x4, + DW_ATE_signed = 0x5, + DW_ATE_signed_char = 0x6, + DW_ATE_unsigned = 0x7, + DW_ATE_unsigned_char = 0x8 + }; + +#define DW_ATE_lo_user 0x80 +#define DW_ATE_hi_user 0xff + +/* Array ordering names and codes. */ +enum dwarf_array_dim_ordering + { + DW_ORD_row_major = 0, + DW_ORD_col_major = 1 + }; + +/* access attribute */ +enum dwarf_access_attribute + { + DW_ACCESS_public = 1, + DW_ACCESS_protected = 2, + DW_ACCESS_private = 3 + }; + +/* visibility */ +enum dwarf_visibility_attribute + { + DW_VIS_local = 1, + DW_VIS_exported = 2, + DW_VIS_qualified = 3 + }; + +/* virtuality */ +enum dwarf_virtuality_attribute + { + DW_VIRTUALITY_none = 0, + DW_VIRTUALITY_virtual = 1, + DW_VIRTUALITY_pure_virtual = 2 + }; + +/* case sensitivity */ +enum dwarf_id_case + { + DW_ID_case_sensitive = 0, + DW_ID_up_case = 1, + DW_ID_down_case = 2, + DW_ID_case_insensitive = 3 + }; + +/* calling convention */ +enum dwarf_calling_convention + { + DW_CC_normal = 0x1, + DW_CC_program = 0x2, + DW_CC_nocall = 0x3 + }; + +#define DW_CC_lo_user 0x40 +#define DW_CC_hi_user 0xff + +/* inline attribute */ +enum dwarf_inline_attribute + { + DW_INL_not_inlined = 0, + DW_INL_inlined = 1, + DW_INL_declared_not_inlined = 2, + DW_INL_declared_inlined = 3 + }; + +/* discriminant lists */ +enum dwarf_discrim_list + { + DW_DSC_label = 0, + DW_DSC_range = 1 + }; + +/* line number opcodes */ +enum dwarf_line_number_ops + { + DW_LNS_extended_op = 0, + DW_LNS_copy = 1, + DW_LNS_advance_pc = 2, + DW_LNS_advance_line = 3, + DW_LNS_set_file = 4, + DW_LNS_set_column = 5, + DW_LNS_negate_stmt = 6, + DW_LNS_set_basic_block = 7, + DW_LNS_const_add_pc = 8, + DW_LNS_fixed_advance_pc = 9 + }; + +/* line number extended opcodes */ +enum dwarf_line_number_x_ops + { + DW_LNE_end_sequence = 1, + DW_LNE_set_address = 2, + DW_LNE_define_file = 3 + }; + +/* call frame information */ +enum dwarf_call_frame_info + { + DW_CFA_advance_loc = 0x40, + DW_CFA_offset = 0x80, + DW_CFA_restore = 0xc0, + DW_CFA_nop = 0x00, + DW_CFA_set_loc = 0x01, + DW_CFA_advance_loc1 = 0x02, + DW_CFA_advance_loc2 = 0x03, + DW_CFA_advance_loc4 = 0x04, + DW_CFA_offset_extended = 0x05, + DW_CFA_restore_extended = 0x06, + DW_CFA_undefined = 0x07, + DW_CFA_same_value = 0x08, + DW_CFA_register = 0x09, + DW_CFA_remember_state = 0x0a, + DW_CFA_restore_state = 0x0b, + DW_CFA_def_cfa = 0x0c, + DW_CFA_def_cfa_register = 0x0d, + DW_CFA_def_cfa_offset = 0x0e, + DW_CFA_def_cfa_expression = 0x0f, + DW_CFA_expression = 0x10, + /* Dwarf 2.1 */ + DW_CFA_offset_extended_sf = 0x11, + DW_CFA_def_cfa_sf = 0x12, + DW_CFA_def_cfa_offset_sf = 0x13, + /* added by jdh for newer version of dwarf? libc uses this in ubuntu 14.04 */ + DW_CFA_val_offset = 0x14, + DW_CFA_val_offset_sf = 0x15, + DW_CFA_val_expression = 0x16, + + + /* SGI/MIPS specific */ + DW_CFA_MIPS_advance_loc8 = 0x1d, + + /* GNU extensions */ + DW_CFA_GNU_window_save = 0x2d, + DW_CFA_GNU_args_size = 0x2e, + DW_CFA_GNU_negative_offset_extended = 0x2f + }; + +#define DW_CIE_ID 0xffffffff +#define DW_CIE_VERSION 1 + +#define DW_CFA_extended 0 +#define DW_CFA_low_user 0x1c +#define DW_CFA_high_user 0x3f + +#define DW_CHILDREN_no 0x00 +#define DW_CHILDREN_yes 0x01 + +#define DW_ADDR_none 0 + +/* Source language names and codes. */ + +enum dwarf_source_language + { + DW_LANG_C89 = 0x0001, + DW_LANG_C = 0x0002, + DW_LANG_Ada83 = 0x0003, + DW_LANG_C_plus_plus = 0x0004, + DW_LANG_Cobol74 = 0x0005, + DW_LANG_Cobol85 = 0x0006, + DW_LANG_Fortran77 = 0x0007, + DW_LANG_Fortran90 = 0x0008, + DW_LANG_Pascal83 = 0x0009, + DW_LANG_Modula2 = 0x000a, + DW_LANG_Java = 0x000b, + DW_LANG_Mips_Assembler = 0x8001 + }; + + +#define DW_LANG_lo_user 0x8000 /* implementation-defined range start */ +#define DW_LANG_hi_user 0xffff /* implementation-defined range start */ + +/* Names and codes for macro information. */ + +enum dwarf_macinfo_record_type + { + DW_MACINFO_define = 1, + DW_MACINFO_undef = 2, + DW_MACINFO_start_file = 3, + DW_MACINFO_end_file = 4, + DW_MACINFO_vendor_ext = 255 + }; + + +/* @@@ For use with GNU frame unwind information. */ + +#define DW_EH_PE_absptr 0x00 +#define DW_EH_PE_omit 0xff + +#define DW_EH_PE_uleb128 0x01 +#define DW_EH_PE_udata2 0x02 +#define DW_EH_PE_udata4 0x03 +#define DW_EH_PE_udata8 0x04 +#define DW_EH_PE_sleb128 0x09 +#define DW_EH_PE_sdata2 0x0A +#define DW_EH_PE_sdata4 0x0B +#define DW_EH_PE_sdata8 0x0C +#define DW_EH_PE_signed 0x08 + +#define DW_EH_PE_pcrel 0x10 +#define DW_EH_PE_textrel 0x20 +#define DW_EH_PE_datarel 0x30 +#define DW_EH_PE_funcrel 0x40 +#define DW_EH_PE_aligned 0x50 + +#define DW_EH_PE_indirect 0x80 + +#endif /* dwarf2.h */ diff --git a/zipr/include/zipr_impl.h b/zipr/include/zipr_impl.h new file mode 100644 index 0000000000000000000000000000000000000000..f8193cde3d17f91ed9ba9a2c91b70ecb576a9a3b --- /dev/null +++ b/zipr/include/zipr_impl.h @@ -0,0 +1,479 @@ +/*************************************************************************** + * Copyright (c) 2014 Zephyr Software LLC. All rights reserved. + * + * This software is furnished under a license and/or other restrictive + * terms and may be used and copied only in accordance with such terms + * and the inclusion of the above copyright notice. This software or + * any other copies thereof may not be provided or otherwise made + * available to any other person without the express written consent + * of an authorized representative of Zephyr Software LCC. Title to, + * ownership of, and all rights in the software is retained by + * Zephyr Software LCC. + * + * Zephyr Software LLC. Proprietary Information + * + * Unless otherwise specified, the information contained in this + * directory, following this legend, and/or referenced herein is + * Zephyr Software LLC. (Zephyr) Proprietary Information. + * + * CONTACT + * + * For technical assistance, contact Zephyr Software LCC. at: + * + * + * Zephyr Software, LLC + * 2040 Tremont Rd + * Charlottesville, VA 22911 + * + * E-mail: jwd@zephyr-software.com + **************************************************************************/ + +#ifndef zipr_impl_h +#define zipr_impl_h + +class Stats_t; + +class DataScoopByAddressComp_t +{ + public: + bool operator()(const IRDB_SDK::DataScoop_t *lhs, const IRDB_SDK::DataScoop_t *rhs) { + + if (!lhs || + !rhs || + !lhs->getStart() || + !rhs->getStart() + ) + throw std::logic_error("Cannot order scoops that have null elements."); + + return std::make_tuple(lhs->getStart()->getVirtualOffset(), lhs) < + std::make_tuple(rhs->getStart()->getVirtualOffset(), rhs); + } +}; +using DataScoopByAddressSet_t = std::set<IRDB_SDK::DataScoop_t*, DataScoopByAddressComp_t>; + + + + +class pin_sorter_t +{ + public: + bool operator() (const UnresolvedPinned_t& p1, const UnresolvedPinned_t& p2) + { + assert(p1.getInstrution()); + assert(p2.getInstrution()); + assert(p1.getInstrution()->getIndirectBranchTargetAddress() + && p1.getInstrution()->getIndirectBranchTargetAddress()->getVirtualOffset()!=0); + assert(p2.getInstrution()->getIndirectBranchTargetAddress() + && p2.getInstrution()->getIndirectBranchTargetAddress()->getVirtualOffset()!=0); + + return p1.getInstrution()->getIndirectBranchTargetAddress()->getVirtualOffset() < + p2.getInstrution()->getIndirectBranchTargetAddress()->getVirtualOffset() ; + } +}; + +class ZiprImpl_t : public Zipr_t +{ + + + public: + ZiprImpl_t(int argc, char **argv) : + m_stats(nullptr), + m_firp(nullptr), + m_variant_id(nullptr), + m_pqxx_interface(IRDB_SDK::pqxxDB_t::factory()), + lo(nullptr), + m_error(false), + m_dollop_mgr(this), + exeiop(new EXEIO::exeio), + start_of_new_space(0), + memory_space(), + m_zipr_options(argc-1, argv+1) + + { + Init(); + }; + ~ZiprImpl_t(); + + void CreateBinaryFile(); + bool Error() { + return m_error; + } + /* + * DetermineDollopEntrySize + * + * Determine the worst case dollop entry size. + * and account for the possibility that a plugin + * may be plopping this instruction and want + * to do some calculations. + */ + size_t determineDollopEntrySize(DollopEntry_t *entry, bool account_for_trampoline) { return DetermineDollopEntrySize(entry,account_for_trampoline); } + Zipr_SDK::RangeAddress_t plopDollopEntry( + DollopEntry_t *de, + RangeAddress_t override_place = 0, + RangeAddress_t override_target = 0) { return PlopDollopEntry(de,override_place,override_target); } + + Zipr_SDK::RangeAddress_t plopDollopEntryWithTarget( + DollopEntry_t *de, + RangeAddress_t override_place = 0, + RangeAddress_t override_target = 0) { return PlopDollopEntryWithTarget(de,override_place,override_target); } + + Zipr_SDK::RangeAddress_t plopDollopEntryWithCallback( + DollopEntry_t *de, + RangeAddress_t override_place = 0) { return PlopDollopEntryWithCallback(de,override_place); } + + + + + size_t DetermineDollopEntrySize(DollopEntry_t *entry, bool account_for_trampoline); + + Zipr_SDK::RangeAddress_t PlopDollopEntry( + DollopEntry_t *, + RangeAddress_t override_place = 0, + RangeAddress_t override_target = 0); + + Zipr_SDK::RangeAddress_t PlopDollopEntryWithTarget( + DollopEntry_t *, + RangeAddress_t override_place = 0, + RangeAddress_t override_target = 0); + + Zipr_SDK::RangeAddress_t PlopDollopEntryWithCallback( + DollopEntry_t *, + RangeAddress_t override_place = 0); + + // ZiprOptionsNamespace_t *registerOptions(ZiprOptionsNamespace_t *ns) { return RegisterOptions(ns); } + void registerOptions(); + + /* + * () + * + * Input: + * Output: + * Effects: + * + * + * + */ + void AskPluginsAboutPlopping(); + bool AskPluginsAboutPlopping(IRDB_SDK::Instruction_t *); + + void RecordNewPatch(const std::pair<RangeAddress_t, UnresolvedUnpinnedPatch_t>& p) + { + m_PatchAtAddrs.insert(p); + } + UnresolvedUnpinnedPatch_t FindPatch(const RangeAddress_t r) const + { + return m_PatchAtAddrs.at(r); + } + void RemovePatch(const RangeAddress_t r) + { + auto patch_it = m_PatchAtAddrs.find(r); + assert(patch_it != m_PatchAtAddrs.end()); + m_PatchAtAddrs.erase(patch_it); + } + IRDB_SDK::Instruction_t *FindPatchTargetAtAddr(RangeAddress_t addr); + void PatchJump(RangeAddress_t at_addr, RangeAddress_t to_addr); + void AddPatch(const UnresolvedUnpinned_t& uu, const Patch_t& thepatch) + { + patch_list.insert(pair<const UnresolvedUnpinned_t,Patch_t>(uu,thepatch)); + } + + + virtual Zipr_SDK::MemorySpace_t *getMemorySpace() { return &memory_space; } + virtual Zipr_SDK::MemorySpace_t *GetMemorySpace() { return &memory_space; } + virtual Zipr_SDK::DollopManager_t *getDollopManager() { return &m_dollop_mgr; } + virtual EXEIO::exeio *getEXEIO() { return exeiop; } + virtual IRDB_SDK::FileIR_t *getFileIR() { return m_firp; } + virtual Zipr_SDK::InstructionLocationMap_t *getLocationMap() { return &final_insn_locations; } + virtual Zipr_SDK::InstructionLocationMap_t *GetLocationMap() { return &final_insn_locations; } + virtual Zipr_SDK::PlacementQueue_t* getPlacementQueue() { return &placement_queue; } + virtual Zipr_SDK::ZiprOptionsManager_t* getOptionsManager() { return &m_zipr_options; } + virtual Zipr_SDK::PlacementQueue_t* GetPlacementQueue() { return &placement_queue; } + virtual Zipr_SDK::RangeAddress_t placeUnplacedScoops(Zipr_SDK::RangeAddress_t max) { return PlaceUnplacedScoops(max); } + virtual Zipr_SDK::RangeAddress_t PlaceUnplacedScoops(Zipr_SDK::RangeAddress_t max); + Stats_t* getStats() { return m_stats; } + ZiprSizerBase_t* getSizer() { return sizer; } + ZiprPatcherBase_t* GetPatcher() { return patcher; } + ZiprPinnerBase_t* GetPinner() { return pinner; } + + + private: + + void Init(); + + Zipr_SDK::RangeAddress_t _PlopDollopEntry(DollopEntry_t*, Zipr_SDK::RangeAddress_t override_address=0); + + /* + * FindFreeRanges() + * + * Input: Nothing + * Output: Nothing + * Uses: exeio + * Effects: memory_space + * + * Adds available memory ranges to memory space. + * It also adds the "large" section. + */ + void FindFreeRanges(const std::string &name); + + /* + * Input: A map of section addresses to integers in order of address + * Output: A new scoop with RX perms for each allocatable/executable series-of-segments. + * Uses: exeio + * Effects: IRDB_SDK IR. + * + * Creates a scoop for the executable instructions in the IR. + */ + void CreateExecutableScoops(const std::map<RangeAddress_t, int> &ordered_sections); + + + + /* + * Input: the IRDB_SDK IR, memory_space + * Output: a revised version of the IR taking memory into account + * Uses: IRDB_SDK IR + * Effects: IRDB_SDK IR. + * + * Creates a scoop for the executable instructions in the IR. + */ + void UpdateScoops(); + void CreateDollops(); + void PlaceDollops(); + void WriteDollops(); + void UpdatePins(); + + void RecalculateDollopSizes(); + + void ReplopDollopEntriesWithTargets(); + /* + * OptimizePinnedFallthrough() + * + * Input: None + * Output: None + * Uses: five_byte_pins + * Effects: patch_list, five_byte_pins + * + * Iterates through all five byte pin spaces + * and converts them to patches to their + * target instructions. Those patches are + * added to patch_list and removed from + * five_byte_pins. At the end of the function, + * five_byte_pins *should* be empty. + * + */ + void OptimizePinnedFallthroughs(); + /* + * PlopTheUnpinnedInstructions() + * + * Input: None + * Output: None + * Uses patch_list + * Effects: patch_list + * + * Iterates through all the patches and + * plops down the target instructions. + * Dynamically adds patches to patch_list + * as necessary (when it plops an instruction + * that jumps to another place that is not + * already plopped or that needs to be + * replopped). + * + */ + void PlopTheUnpinnedInstructions(); + + /* + * () + * + * Input: + * Output: + * Effects: + * + * + * + */ + void UpdateCallbacks(); + + /* + * () + * + * Input: + * Output: + * Effects: + * + * + * + */ + void PrintStats(); + /* + * RecordPinnedInsnAddrs() + * + * Input: None + * Output: None + * Effects: m_InsnAtAddrs + * + * Builds a map of pinned addresses -> insns + * used by FindPinnedInsnsAtAddr. + */ + void RecordPinnedInsnAddrs(); + + + void PerformPinning(); + + // zipr has some internal assumptions that multiple fallthroughs to the same instruction + // are problematic. this function adjusts the IR such that no multiple fallthroughs + // exist by adding direct jump instructions where necessary to eliminate multiple fallthroughs. + void FixMultipleFallthroughs(); + + // zipr assumes that jumps (jcc and jmp) are either 2 or 5 byte. + // sometimes they include a prefix to help the branch predictor + // remove unneeded prefixes to meet zipr assumptions. + void FixTwoByteWithPrefix(); + + // zipr also has internal assumptions about conditional branches having targets/fallthrougsh. + // patch any instructions with null fallthrougsh and targtes a halt instruction + void FixNoFallthroughs(); + + + + /* + * DetermineInsnSize + * + * Determine the worst case instruction size + * but do not account for the possibility that a plugin + * may be plopping this instruction and want + * to do some calculations. + */ + size_t DetermineInsnSize(IRDB_SDK::Instruction_t*, bool account_for_trampoline); + + // patching + void ApplyPatches(IRDB_SDK::Instruction_t* insn); + void PatchInstruction(RangeAddress_t addr, IRDB_SDK::Instruction_t* insn); + void RewritePCRelOffset(RangeAddress_t from_addr,RangeAddress_t to_addr, int insn_length, int offset_pos); + void applyPatch(RangeAddress_t from_addr, RangeAddress_t to_addr) { return ApplyPatch(from_addr,to_addr); } + void ApplyPatch(RangeAddress_t from_addr, RangeAddress_t to_addr); + void ApplyNopToPatch(RangeAddress_t addr); + void PatchCall(RangeAddress_t at_addr, RangeAddress_t to_addr); + void CallToNop(RangeAddress_t at_addr); + + // outputing new .exe + void FillSection(EXEIO::section* sec, FILE* fexe); + void OutputBinaryFile(const std::string &name); + IRDB_SDK::DataScoop_t* FindScoop(const RangeAddress_t &addr); + void WriteScoop(EXEIO::section* sec, std::FILE* fexe); + + + // helpers. + void ProcessUnpinnedInstruction(const UnresolvedUnpinned_t &uu, const Patch_t &p); + void InsertNewSegmentIntoExe(std::string old_file, std::string new_file, RangeAddress_t sec_start); + std::string AddCallbacksToNewSegment(const std::string& tmpname, RangeAddress_t end_of_new_space); + RangeAddress_t FindCallbackAddress(RangeAddress_t end_of_new_space,RangeAddress_t start_addr, const std::string &callback); + + // support + RangeAddress_t extend_section(EXEIO::section *sec,EXEIO::section *next_sec); + + void dump_scoop_map(); + void dump_instruction_map(); + virtual void RelayoutEhInfo(); + + + Stats_t *m_stats; + + // data for the stuff we're rewriting. + IRDB_SDK::FileIR_t* m_firp; + IRDB_SDK::VariantID_t* m_variant_id; + + std::unique_ptr<IRDB_SDK::FileIR_t> m_firp_p; + std::unique_ptr<IRDB_SDK::VariantID_t> m_variant_id_p; + std::unique_ptr<IRDB_SDK::pqxxDB_t> m_pqxx_interface; + pqxx::largeobject *lo; + + + bool m_error; + + + + // structures necessary for ZIPR algorithm. + std::multimap<UnresolvedUnpinned_t,Patch_t> patch_list; + + // a manager for all dollops + ZiprDollopManager_t m_dollop_mgr; + + // final mapping of instruction to address. + Zipr_SDK::InstructionLocationMap_t final_insn_locations; + std::map<RangeAddress_t,UnresolvedUnpinnedPatch_t> m_PatchAtAddrs; + + // unpatched callbacks + std::set<std::pair<Zipr_SDK::DollopEntry_t*,RangeAddress_t> > unpatched_callbacks; + + std::map<std::string,RangeAddress_t> callback_addrs; + + // way to read elf headers, etc. + EXEIO::exeio* exeiop; + + // records where we will insert extra bytes into the program. + RangeAddress_t start_of_new_space; + + ZiprMemorySpace_t memory_space; + Zipr_SDK::PlacementQueue_t placement_queue; + + RangeAddress_t bss_needed; + bool use_stratafier_mode; + + /* + * Scoops + */ + IRDB_SDK::DataScoopSet_t m_zipr_scoops; + + ZiprPluginManager_t plugman; + + std::map<IRDB_SDK::Instruction_t*, + std::unique_ptr<std::list<DLFunctionHandle_t>>> plopping_plugins; + + // Options Manager + ZiprOptions_t m_zipr_options; + + // Options + Zipr_SDK::ZiprStringOption_t *m_output_filename, + *m_callbacks, + *m_objcopy, + *m_dollop_map_filename; + Zipr_SDK::ZiprBooleanOption_t *m_replop, + *m_verbose, + *m_vverbose, + *m_apply_nop, + *m_add_sections, + *m_bss_opts; + Zipr_SDK::ZiprIntegerOption_t *m_variant, + *m_architecture, + *m_seed, + *m_paddable_minimum_distance; + + + + std::list<DollopEntry_t *> m_des_to_replop; + + + template <class T> static T page_align(const T& in) + { + return align_by(in,PAGE_SIZE); + } + template <class T> static T align_up_to(const T& in, const T &by) + { + return align_by(in+by-1,by); + } + template <class T> static T align_by(const T& in, const T &by) + { + // assert power of 2. + assert( (by & (by - 1)) == 0 ); + return in&~(by-1); + } + + // arch specific stuff + std::unique_ptr<ZiprArchitectureHelperBase_t> archhelper; + ZiprPinnerBase_t * pinner =nullptr; + ZiprPatcherBase_t* patcher=nullptr; + ZiprSizerBase_t * sizer =nullptr; + + +}; + +#endif diff --git a/zipr/include/zipr_mem_space.h b/zipr/include/zipr_mem_space.h new file mode 100644 index 0000000000000000000000000000000000000000..aa4bcd8b9d78e756d267c7b8d84436c77b659f9a --- /dev/null +++ b/zipr/include/zipr_mem_space.h @@ -0,0 +1,136 @@ +/*************************************************************************** + * Copyright (c) 2014 Zephyr Software LLC. All rights reserved. + * + * This software is furnished under a license and/or other restrictive + * terms and may be used and copied only in accordance with such terms + * and the inclusion of the above copyright notice. This software or + * any other copies thereof may not be provided or otherwise made + * available to any other person without the express written consent + * of an authorized representative of Zephyr Software LCC. Title to, + * ownership of, and all rights in the software is retained by + * Zephyr Software LCC. + * + * Zephyr Software LLC. Proprietary Information + * + * Unless otherwise specified, the information contained in this + * directory, following this legend, and/or referenced herein is + * Zephyr Software LLC. (Zephyr) Proprietary Information. + * + * CONTACT + * + * For technical assistance, contact Zephyr Software LCC. at: + * + * + * Zephyr Software, LLC + * 2040 Tremont Rd + * Charlottesville, VA 22911 + * + * E-mail: jwd@zephyr-software.com + **************************************************************************/ + +#ifndef memory_space_h +#define memory_space_h + +// a memory space _is_ a map of range addres to char, with additional functionality. +class ZiprMemorySpace_t : public MemorySpace_t +{ + public: + ZiprMemorySpace_t() : + free_ranges(), + original_free_ranges(), + max_plopped(0), + min_plopped(-1) + { + } + + void registerOptions(ZiprOptions_t* opt_man); + + // range operatations + void splitFreeRange(RangeAddress_t addr) { return SplitFreeRange(addr); } + void splitFreeRange(Range_t range) { return SplitFreeRange(range); } + void mergeFreeRange(RangeAddress_t addr) { return MergeFreeRange(addr); } + void mergeFreeRange(Range_t range) { return MergeFreeRange(range); } + RangeSet_t::iterator findFreeRange(RangeAddress_t addr) { return FindFreeRange(addr); } + void addFreeRange(Range_t newRange) { return AddFreeRange(newRange); } + void addFreeRange(Range_t newRange, bool original) { return AddFreeRange(newRange, original); } + void removeFreeRange(Range_t newRange) {return RemoveFreeRange(newRange); } + Range_t getLargeRange(void) { return GetLargeRange(); } + bool areBytesFree(RangeAddress_t addr, int num_bytes) { return AreBytesFree(addr,num_bytes); } + bool isByteFree(RangeAddress_t addr) { return IsByteFree(addr); } + bool isValidRange(RangeSet_t::iterator it) { return IsValidRange(it); } + void printMemorySpace(std::ostream &out) { return PrintMemorySpace(out); } + void plopBytes(RangeAddress_t addr, const char the_byte[], int num) { return PlopBytes(addr,the_byte,num); } + void plopByte(RangeAddress_t addr, char the_byte) { return PlopByte(addr,the_byte); } + void plopJump(RangeAddress_t addr) { return PlopJump(addr); } + RangeAddress_t getMinPlopped() const { return min_plopped; } + RangeAddress_t getMaxPlopped() const { return max_plopped; } + int getRangeCount() { return GetRangeCount(); } + + + + // range operations + void SplitFreeRange(RangeAddress_t addr); + void SplitFreeRange(Range_t split_from); + void MergeFreeRange(Range_t range); + void MergeFreeRange(RangeAddress_t addr); + RangeSet_t::iterator FindFreeRange(RangeAddress_t addr); + Range_t getFreeRange(int size); + Range_t getInfiniteFreeRange(); + std::list<Range_t> getFreeRanges(size_t size = 0); + std::pair<RangeSet_t::const_iterator,RangeSet_t::const_iterator> + getNearbyFreeRanges(const RangeAddress_t hint, size_t count = 0); + void AddFreeRange(Range_t newRange); + void AddFreeRange(Range_t newRange, bool original); + void RemoveFreeRange(Range_t newRange); + Range_t GetLargeRange(void); + + // queries about free areas. + bool AreBytesFree(RangeAddress_t addr, int num_bytes); + bool IsByteFree(RangeAddress_t addr); + bool IsValidRange(RangeSet_t::iterator it); + + int GetRangeCount(); + RangeSet_t getOriginalFreeRanges() const { return original_free_ranges; } + + void PrintMemorySpace(std::ostream &out); + + void PlopBytes(RangeAddress_t addr, const char the_byte[], int num) + { + for(int i=0;i<num;i++) + { + PlopByte(addr+i,the_byte[i]); + } + } + + void PlopByte(RangeAddress_t addr, char the_byte) + { + min_plopped=std::min(addr,min_plopped); + max_plopped=std::max(addr,max_plopped); + + if(this->find(addr) == this->end() && + IsValidRange(FindFreeRange(addr))) /* and, the range is free. */ + this->SplitFreeRange(addr); + (*this)[addr]=the_byte; + } + void PlopJump(RangeAddress_t addr) + { + char bytes[]={(char)0xe9,(char)0,(char)0,(char)0,(char)0}; // jmp rel8 + this->PlopBytes(addr,bytes,sizeof(bytes)); + } + RangeAddress_t GetMinPlopped() const { return min_plopped; } + RangeAddress_t GetMaxPlopped() const { return max_plopped; } + + + protected: + RangeSet_t free_ranges; // keep ordered + RangeSet_t original_free_ranges; // keep ordered + // std::set<Range_t, Range_tCompare> free_ranges; // keep ordered + + private: + RangeAddress_t max_plopped; + RangeAddress_t min_plopped; + Zipr_SDK::ZiprBooleanOption_t* m_verbose; + static bool SortRangeBySize(const Range_t &a, const Range_t &b); +}; + +#endif diff --git a/zipr/include/zipr_optimizations.h b/zipr/include/zipr_optimizations.h new file mode 100644 index 0000000000000000000000000000000000000000..da20c227b4404735bccced66c5b4fcf328092fee --- /dev/null +++ b/zipr/include/zipr_optimizations.h @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2014 - Zephyr Software LLC + * + * This file may be used and modified for non-commercial purposes as long as + * all copyright, permission, and nonwarranty notices are preserved. + * Redistribution is prohibited without prior written consent from Zephyr + * Software. + * + * Please contact the authors for restrictions applying to commercial use. + * + * THIS SOURCE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF + * MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * Author: Zephyr Software + * e-mail: jwd@zephyr-software.com + * URL : http://www.zephyr-software.com/ + * + */ + +#ifndef zipr_optimizations_t +#define zipr_optimizations_t + +class Optimizations_t { + public: + enum OptimizationName_t { OptimizationPlopNotJump = 0, OptimizationFallthroughPinned = 1, NumberOfOptimizations}; +}; +#endif diff --git a/zipr/include/zipr_options.h b/zipr/include/zipr_options.h new file mode 100644 index 0000000000000000000000000000000000000000..a05052229429f48ddf96d80e792711ea62fb9342 --- /dev/null +++ b/zipr/include/zipr_options.h @@ -0,0 +1,436 @@ + +#include <string> +#include <unistd.h> +#include <iostream> +#include <sstream> +#include <set> + +namespace zipr +{ + using namespace std; + + using ZiprOptionType_t = enum ZiprOptionType + { + zotZiprUntypedOptionType, + zotZiprIntegerOptionType, + zotZiprDoubleOptionType, + zotZiprBooleanOptionType, + zotZiprStringOptionType, + }; + + class ZiprOption_t : virtual public Zipr_SDK::ZiprOption_t + { + public: + ZiprOption_t(ZiprOptionType_t type, const string& key, const string& value) + : m_key(key), + m_untyped_value(value), + m_observee(nullptr), + m_takes_value(true), + m_needs_value(true), + m_required(false), + m_set(false), + m_option_type(type) {}; + virtual ~ZiprOption_t() { } + + virtual void setValue(const string &value) = 0; + virtual void setOption() = 0; + + string getStringValue() const + { + return m_untyped_value; + } + virtual string getValueString() const override + { + return m_untyped_value; + } + string getNamespace() const override + { + return m_namespace; + } + string getKey() const override + { + return m_key; + } + void setNeedsValue(bool needs_value) + { + m_needs_value = needs_value; + } + bool getNeedsValue() const + { + return m_needs_value; + } + void setTakesValue(bool takes_value) + { + m_takes_value = takes_value; + } + bool getTakesValue() const + { + return m_takes_value; + } + + bool isRequired() const + { + return m_required; + } + void setRequired(bool required=true) + { + m_required = required; + } + bool areRequirementMet() const + { + return (!m_required) || (m_set); + } + + virtual string getDescription() const override + { + return m_description; + } + + + void setDescription(string description) + { + m_description = description; + } + + void addObserver(ZiprOption_t *observer) + { + assert(observer->m_option_type == m_option_type); + //observer->setValue(m_untyped_value); + observer->m_observee = this; + m_observers.insert(observer); + } + protected: + string m_namespace, m_key, m_untyped_value, m_description; + set<ZiprOption_t*> m_observers; + ZiprOption_t *m_observee; + bool m_takes_value, m_needs_value; + bool m_required; + bool m_set; + void SetObserverValues(string value) + { + set<ZiprOption_t*>::const_iterator it = m_observers.begin(); + set<ZiprOption_t*>::const_iterator it_end = m_observers.end(); + for (; it!=it_end; it++) + (*it)->setValue(value); + } + ZiprOptionType_t m_option_type; + }; + + template <class T> + class ZiprTypedOption_t : virtual public Zipr_SDK::ZiprTypedOption_t<T>, public ZiprOption_t + { + public: + ZiprTypedOption_t(ZiprOptionType_t type, + const string& key, + const string& value) + : ZiprOption_t(type, key, value) {} + + void setValue(const string &value) + { + m_set = true; + m_untyped_value = value; + m_value = convertToTyped(value); + } + virtual void setOption() + { + } + + T getValue() const + { + if (m_observee!=nullptr) + + { + auto typed_observee = dynamic_cast<Zipr_SDK::ZiprTypedOption_t<T>*>(m_observee); + return typed_observee->getValue(); + } + return m_value; + } + operator T() const + { + return getValue(); + }; + virtual string getDescription() + { + return string(m_key + ": " + + ((!m_needs_value)?"[":"") + + "<" + getTypeName() + ">" + + ((!m_needs_value)?"]":"") + + ((m_description.length())?":\t":"") + m_description + ); + } + protected: + T m_value; + virtual T convertToTyped(const string& ) = 0; + virtual string convertToUntyped(const T&) = 0; + virtual string getTypeName() = 0; + }; + + template <class T> + class ZiprCompositeOption_t : public ZiprTypedOption_t<T>, public T + { + public: + ZiprCompositeOption_t(ZiprOptionType_t type, + string key, + string value) + : ZiprTypedOption_t<T>(type, key, value) {} + using ZiprTypedOption_t<T>::convertToTyped; + void setValue(const string &value) + { + ZiprTypedOption_t<T>::setValue(value); + T::operator=(convertToTyped(value)); + } + }; + + class ZiprStringOption_t : virtual public Zipr_SDK::ZiprStringOption_t, public ZiprCompositeOption_t<string> + { + public: + ZiprStringOption_t(string key, string value = "") + : ZiprCompositeOption_t(zotZiprStringOptionType, key, value) + { + ZiprCompositeOption_t::setValue(value); + } + void setValue(const string& value) + { + m_value = value; + m_untyped_value = convertToUntyped(value); + } + protected: + string convertToTyped(const string& value_to_convert) + { + return string(value_to_convert); + } + string convertToUntyped(const string& value_to_convert) + { + return string(value_to_convert); + } + string getTypeName() + { + return "string"; + } + }; + + class ZiprBooleanOption_t : virtual public Zipr_SDK::ZiprBooleanOption_t, public ZiprTypedOption_t<bool> + { + public: + ZiprBooleanOption_t(const string& key, const string& value = "") + : ZiprTypedOption_t(zotZiprBooleanOptionType, key, value) + { + m_untyped_value = value; + m_needs_value = false; + } + ZiprBooleanOption_t(const string& key, bool value) + : ZiprTypedOption_t(zotZiprBooleanOptionType, key, "") + { + m_value = value; + m_needs_value = false; + m_untyped_value = convertToUntyped(value); + } + void setOption() + { + m_untyped_value = "true"; + m_value = true; + m_set = true; + } + void setValue(const bool& value) + { + m_value = value; + m_untyped_value = convertToUntyped(value); + } + + private: + bool convertToTyped(const string& value_to_convert) + { + if (value_to_convert == "true") + return true; + else + return false; + } + string convertToUntyped(const bool& value_to_convert) + { + return (value_to_convert ? string("true") : string("false")); + } + string getTypeName() + { + return "bool"; + } + }; + + class ZiprIntegerOption_t : virtual public Zipr_SDK::ZiprIntegerOption_t, public ZiprTypedOption_t<size_t> + { + public: + ZiprIntegerOption_t(const string& key, const string& value = "") + : zipr::ZiprTypedOption_t<size_t>(zotZiprIntegerOptionType, key, value) + { + m_value = convertToTyped(value); + } + ZiprIntegerOption_t(const string& key, size_t value) + : ZiprTypedOption_t<size_t>(zotZiprIntegerOptionType, key, "") + { + m_value = value; + m_untyped_value = convertToUntyped(value); + } + void setValue(const size_t& value) + { + m_value = value; + m_untyped_value = convertToUntyped(value); + } + private: + size_t convertToTyped(const string& value_to_convert) + { + int converted = 0; + char *endptr = nullptr; + converted = strtol(value_to_convert.c_str(), + &endptr, 10); + if (*endptr != '\0') + { + m_set = false; + m_untyped_value = ""; + return 0; + } + return converted; + } + string convertToUntyped(const size_t& value_to_convert) + { + stringstream ss; + ss << value_to_convert; + return ss.str(); + } + string getTypeName() + { + return "integer"; + } + }; + + class ZiprDoubleOption_t : virtual public Zipr_SDK::ZiprDoubleOption_t, public ZiprTypedOption_t<double> + { + public: + ZiprDoubleOption_t(const string& key, const string& value = "") + : ZiprTypedOption_t<double>(zotZiprDoubleOptionType, key, value) + { + m_value = convertToTyped(value); + } + ZiprDoubleOption_t(const string& key, double value) + : ZiprTypedOption_t<double>(zotZiprDoubleOptionType, key, "") + { + m_value = value; + m_untyped_value = convertToUntyped(value); + } + void setValue(const double& value) + { + m_value = value; + m_untyped_value = convertToUntyped(value); + } + double getValue() const + { + if (m_observee!=nullptr) + + { + auto typed_observee = dynamic_cast<Zipr_SDK::ZiprTypedOption_t<double>*>(m_observee); + return typed_observee->getValue(); + } + return m_value; + } + operator double() const + { + return getValue(); + }; + private: + double convertToTyped(const string& value_to_convert) + { + double converted = 0.; + char *endptr = nullptr; + converted = strtof(value_to_convert.c_str(), + &endptr); + if (*endptr != '\0') + { + m_set = false; + m_untyped_value = ""; + return 0; + } + return converted; + } + string convertToUntyped(const double& value_to_convert) + { + stringstream ss; + ss << value_to_convert; + return ss.str(); + } + string getTypeName() { + return "double"; + } + }; + + class ZiprOptionsNamespace_t : public Zipr_SDK::ZiprOptionsNamespace_t, public set<ZiprOption_t*> + { + public: + ZiprOptionsNamespace_t(const string& ns) : m_namespace(ns) {} + virtual ~ZiprOptionsNamespace_t() + { + for(auto opt : all_options ) + delete opt; + } + + string getNamespace() + { + return m_namespace; + }; + void printNamespace(); + ZiprOption_t *optionByKey(const string& key); + void addOption(ZiprOption_t *option); + void printUsage(int tabs, ostream &out); + bool areRequirementsMet() const ; + void mergeNamespace(ZiprOptionsNamespace_t*); + + virtual string getNamespaceString() const override { return m_namespace; } + + + virtual Zipr_SDK::ZiprStringOption_t* getStringOption (const string& name, const string &description="", const string& default_value="") override + { return getOption<zipr::ZiprStringOption_t, string>(name,description,default_value); } + virtual Zipr_SDK::ZiprIntegerOption_t* getIntegerOption(const string& name, const string &description="", const size_t& default_value=0) override + { return getOption<zipr::ZiprIntegerOption_t, size_t>(name,description,default_value); } + virtual Zipr_SDK::ZiprBooleanOption_t* getBooleanOption(const string& name, const string &description="", const bool & default_value=false) override + { return getOption<zipr::ZiprBooleanOption_t, bool>(name,description,default_value); } + virtual Zipr_SDK::ZiprDoubleOption_t* getDoubleOption (const string& name, const string &description="", const double& default_value=0.0) override + { return getOption<zipr::ZiprDoubleOption_t, double>(name,description,default_value); } + + + private: + string m_namespace; + set<ZiprOption_t*> all_options; + + template<class S, class T> + S* getOption(const string& name, const string &description, const T& default_value) + { + auto new_opt=new S(name,default_value); + assert(new_opt); + new_opt->setDescription(description); + addOption(new_opt); + return new_opt; + } + + }; + + class ZiprOptions_t : public Zipr_SDK::ZiprOptionsManager_t + { + public: + ZiprOptions_t(int argc, char **argv); + ~ZiprOptions_t() + { + for(auto ns : m_namespaces) + delete ns; + } + + void addNamespace(ZiprOptionsNamespace_t *); + void printNamespaces(); + bool parse(ostream *error=&cout, ostream *warn=&cerr); + Zipr_SDK::ZiprOptionsNamespace_t *getNamespace(const string& name); + void printUsage(ostream &out); + bool areRequirementsMet() const; + + + private: + vector<string> m_arguments; + set<ZiprOptionsNamespace_t*> m_namespaces; + }; + +} diff --git a/zipr/include/zipr_stats.h b/zipr/include/zipr_stats.h new file mode 100644 index 0000000000000000000000000000000000000000..37a0383dbaedaf135624b25b8fb9606f5a9a3ab8 --- /dev/null +++ b/zipr/include/zipr_stats.h @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2014 - Zephyr Software LLC + * + * This file may be used and modified for non-commercial purposes as long as + * all copyright, permission, and nonwarranty notices are preserved. + * Redistribution is prohibited without prior written consent from Zephyr + * Software. + * + * Please contact the authors for restrictions applying to commercial use. + * + * THIS SOURCE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF + * MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * Author: Zephyr Software + * e-mail: jwd@zephyr-software.com + * URL : http://www.zephyr-software.com/ + * + */ + +#ifndef zipr_stats_t +#define zipr_stats_t + +class Stats_t +{ + public: + Stats_t() { + for (int i=0; i<Optimizations_t::NumberOfOptimizations; i++) + { + Hits[i] = Misses[i] = 0; + } + total_dollops = 0; + total_dollop_space = 0; + total_dollop_instructions = 0; + truncated_dollops = 0; + total_trampolines = 0; + total_2byte_pins = 0; + total_5byte_pins = 0; + total_tramp_space = 0; + total_other_space = 0; + total_free_ranges = 0; + total_did_coalesce = 0; + total_did_not_coalesce = 0; + truncated_dollops_during_coalesce = 0; + }; + + void PrintStats(std::ostream &out); + + /* + * General stats tracking. + */ + + int total_dollops; + int total_dollop_space; + int total_dollop_instructions; + int truncated_dollops; + int total_trampolines; + int total_2byte_pins; + int total_5byte_pins; + int total_tramp_space; + int total_other_space; + int total_free_ranges; + int total_did_coalesce; + int total_did_not_coalesce; + int truncated_dollops_during_coalesce; + + /* + * Optimization stats tracking. + */ + int Hits[Optimizations_t::NumberOfOptimizations]; + int Misses[Optimizations_t::NumberOfOptimizations]; + void Hit(Optimizations_t::OptimizationName_t opt) { Hits[opt]++; }; + void Missed(Optimizations_t::OptimizationName_t opt) { Misses[opt]++; }; +}; +#endif diff --git a/zipr/include/zipr_utils.h b/zipr/include/zipr_utils.h new file mode 100644 index 0000000000000000000000000000000000000000..ff32a4e932673ccacc8af14a4c49ca6300f5e33d --- /dev/null +++ b/zipr/include/zipr_utils.h @@ -0,0 +1,7 @@ + +#ifndef zipr_utils_h +#define zipr_utils_h + +extern void PrintStat(std::ostream &out, std::string description, double value); + +#endif diff --git a/zipr/src/SConscript b/zipr/src/SConscript new file mode 100644 index 0000000000000000000000000000000000000000..dc07c45e5f3f5fe56bef49976f3dd3fc6296fa6f --- /dev/null +++ b/zipr/src/SConscript @@ -0,0 +1,90 @@ +import os + +(sysname, nodename, release, version, machine)=os.uname() + + +Import('env') +myenv=env.Clone() +myenv.Replace(SECURITY_TRANSFORMS_HOME=os.environ['SECURITY_TRANSFORMS_HOME']) +myenv.Replace(ZIPR_HOME=os.environ['ZIPR_HOME']) +myenv.Replace(IRDB_SDK=os.environ['IRDB_SDK']) +myenv.Replace(ZIPR_SDK=os.environ['ZIPR_SDK']) + + +files= ''' + main.cpp + memory_space.cpp + plugin_man.cpp + zipr.cpp + zipr_options.cpp + zipr_stats.cpp + utils.cpp + dollop.cpp + zipr_dollop_man.cpp + elfwrite.cpp + pewrite.cpp + ehwrite.cpp + arch_base.cpp + arch_mips32.cpp + pinner_arm64.cpp + pinner_arm32.cpp + pinner_mips32.cpp + pinner_base.cpp + pinner_x86.cpp + patcher_arm64.cpp + patcher_arm32.cpp + patcher_mips32.cpp + patcher_base.cpp + patcher_x86.cpp + sizer_base.cpp + sizer_x86.cpp + sizer_arm64.cpp + sizer_arm32.cpp + sizer_mips32.cpp + range.cpp + ''' + +# ELFIO needs to be first so we get the zipr version instead of the sectrans version. the zipr version is modified to include get_offset. +cpppath=''' + . + $SECURITY_TRANSFORMS_HOME/third_party/elfio-code + $SECURITY_TRANSFORMS_HOME/libEXEIO/include + $SECURITY_TRANSFORMS_HOME/third_party/pebliss/pe_lib/ + $IRDB_SDK//include/ + $ZIPR_HOME/include/ + $ZIPR_SDK/include/ + ''' + +libs=''' + irdb-core + irdb-cfg + irdb-transform + EXEIO + StructDiv + pebliss + dl + ''' + +libpath=''' + $SECURITY_TRANSFORMS_HOME/lib + ''' + +if sysname=="SunOS": + myenv.Append(LINKFLAGS=" -xldscope=global ") # export all symbols +else: + myenv.Append(CCFLAGS=" -Wall -Werror -fmax-errors=2") + myenv.Append(LINKFLAGS=" -Wl,-E ") # export all symbols + + +myenv=myenv.Clone(CPPPATH=Split(cpppath), LIBS=Split(libs), LIBPATH=Split(libpath)) + +#print 'myenv=' +#print myenv.Dump() + +myenv.Append(CXXFLAGS=" -Wno-deprecated -fmax-errors=2") +ziprexe=myenv.Program("zipr.exe", Split(files)) + +install=myenv.Install("$PEASOUP_HOME/zipr_install/bin/", ziprexe) +Default(install) +ret=install+ziprexe +Return('ret') diff --git a/zipr/src/SConstruct b/zipr/src/SConstruct new file mode 100644 index 0000000000000000000000000000000000000000..c0dd68a00d406b0148a93709cf916ad6d05f282c --- /dev/null +++ b/zipr/src/SConstruct @@ -0,0 +1,6 @@ + + + +env=Environment() +Export('env') +SConscript("SConscript") diff --git a/zipr/src/arch_base.cpp b/zipr/src/arch_base.cpp new file mode 100644 index 0000000000000000000000000000000000000000..282624015263b2d2fe60cc99b154cdfd0b035559 --- /dev/null +++ b/zipr/src/arch_base.cpp @@ -0,0 +1,66 @@ + + +#include <zipr_all.h> + +namespace zipr +{ +#include <arch/arch_x86.hpp> +#include <arch/arch_arm64.hpp> +#include <arch/arch_arm32.hpp> +#include <arch/arch_mips32.hpp> +} +#include <memory> + +using namespace std; +using namespace IRDB_SDK; +using namespace zipr; + +ZiprArchitectureHelperBase_t::ZiprArchitectureHelperBase_t(Zipr_SDK::Zipr_t* p_zipr_obj) : + m_pinner (ZiprPinnerBase_t ::factory(p_zipr_obj)), + m_patcher(ZiprPatcherBase_t::factory(p_zipr_obj)), + m_sizer (ZiprSizerBase_t ::factory(p_zipr_obj)), + m_zipr (p_zipr_obj) +{ +} + + +unique_ptr<ZiprArchitectureHelperBase_t> ZiprArchitectureHelperBase_t::factory(Zipr_SDK::Zipr_t* p_zipr_obj) +{ + auto l_firp=p_zipr_obj->getFileIR(); + auto ret= l_firp->getArchitecture()->getMachineType() == admtX86_64 ? (ZiprArchitectureHelperBase_t*)new ZiprArchitectureHelperX86_t (p_zipr_obj) : + l_firp->getArchitecture()->getMachineType() == admtI386 ? (ZiprArchitectureHelperBase_t*)new ZiprArchitectureHelperX86_t (p_zipr_obj) : + l_firp->getArchitecture()->getMachineType() == admtAarch64 ? (ZiprArchitectureHelperBase_t*)new ZiprArchitectureHelperARM64_t(p_zipr_obj) : + l_firp->getArchitecture()->getMachineType() == admtArm32 ? (ZiprArchitectureHelperBase_t*)new ZiprArchitectureHelperARM32_t(p_zipr_obj) : + l_firp->getArchitecture()->getMachineType() == admtMips32 ? (ZiprArchitectureHelperBase_t*)new ZiprArchitectureHelperMIPS32_t(p_zipr_obj) : + throw domain_error("Cannot init architecture"); + + return unique_ptr<ZiprArchitectureHelperBase_t>(ret); +} + +/* + * Default way to split a dollop is to create a jump instruction, push it on the dollop, then ask plugins if they want anything to do with it + */ +RangeAddress_t ZiprArchitectureHelperBase_t::splitDollop(FileIR_t* p_firp, Zipr_SDK::Dollop_t* to_split, const RangeAddress_t p_cur_addr) +{ + auto zipr_impl = dynamic_cast<ZiprImpl_t*>(m_zipr); + auto cur_addr = p_cur_addr; + auto fallthrough = to_split->getFallthroughDollop(); + auto patch = createNewJumpInstruction(p_firp, nullptr); + auto patch_de = new DollopEntry_t(patch, to_split); + + patch_de->setTargetDollop(fallthrough); + patch_de->Place(cur_addr); + cur_addr += m_zipr->determineDollopEntrySize(patch_de, false); + + to_split->push_back(patch_de); + to_split->setFallthroughPatched(true); + + m_zipr->getPlacementQueue()->insert({fallthrough, cur_addr}); + /* + * Since we inserted a new instruction, we should + * check to see whether a plugin wants to plop it. + */ + zipr_impl->AskPluginsAboutPlopping(patch_de->getInstruction()); + + return cur_addr; +} diff --git a/zipr/src/arch_mips32.cpp b/zipr/src/arch_mips32.cpp new file mode 100644 index 0000000000000000000000000000000000000000..caa78ba5e2545021efeaee94ea131e17b5795218 --- /dev/null +++ b/zipr/src/arch_mips32.cpp @@ -0,0 +1,87 @@ +#include <zipr_all.h> + +namespace zipr +{ +#include <arch/arch_mips32.hpp> +} +#include <memory> + +using namespace std; +using namespace IRDB_SDK; +using namespace zipr; + +#define ALLOF(a) begin(a),end(a) + + + +/* + * Default way to split a dollop is to create a jump instruction, push it on the dollop, then ask plugins if they want anything to do with it + */ +RangeAddress_t ZiprArchitectureHelperMIPS32_t::splitDollop(FileIR_t* p_firp, Zipr_SDK::Dollop_t* to_split, const RangeAddress_t p_cur_addr) +{ + assert(!to_split->empty()); + const auto last_de = *to_split->rbegin(); // end of DollopEntryList + const auto last_insn = last_de->getInstruction(); + const auto last_insn_is_branch = DecodedInstruction_t::factory(last_insn)->isBranch(); + const auto nop_bits = string("\x00\x00\x00\x00",4); + auto zipr_impl = dynamic_cast<ZiprImpl_t*>(m_zipr); + auto cur_addr = p_cur_addr; + auto fallthrough = to_split->getFallthroughDollop(); + + const auto add_instruction = [&](Instruction_t* patch) -> DollopEntry_t* + { + /* need a dollop entry */ + auto patch_de = new DollopEntry_t(patch, to_split); + + /* place it */ + patch_de->Place(cur_addr); + + /* advance cur_addr */ + cur_addr += m_zipr->determineDollopEntrySize(patch_de, false); + + /* add it to the dollop and placement queue */ + to_split->push_back(patch_de); + m_zipr->getPlacementQueue()->insert({fallthrough, cur_addr}); + + /* + * Since we inserted a new instruction, we should + * check to see whether a plugin wants to plop it. + */ + zipr_impl->AskPluginsAboutPlopping(patch_de->getInstruction()); + + return patch_de; + }; + + + /* if the dollop ends in some kind of branch we need the delay slot instruction */ + if(last_insn_is_branch) + { + const auto is_delay_slot_reloc = [](const Relocation_t* reloc) -> bool { return reloc->getType() == "delay_slot1"; } ; + const auto &last_insn_relocs = last_insn->getRelocations(); + const auto delay_slot_insn_it = find_if(ALLOF(last_insn_relocs), is_delay_slot_reloc); + assert(delay_slot_insn_it != end(last_insn_relocs)); + const auto delay_slot_insn = dynamic_cast<Instruction_t*>((*delay_slot_insn_it)->getWRT()); + + auto new_delay_slot = delay_slot_insn == nullptr ? + IRDB_SDK::addNewDataBits(p_firp, nullptr, nop_bits) : + IRDB_SDK::addNewDataBits(p_firp, delay_slot_insn->getTarget(), delay_slot_insn->getDataBits()); + + /* add a copy of the delay slot instruction */ + add_instruction(new_delay_slot); + } + + /* add a jump instruction */ + const auto target_insn = (*fallthrough->begin())->getInstruction()->getFallthrough(); // jumps over delay slot + add_instruction(createNewJumpInstruction(p_firp, target_insn))->setTargetDollop(fallthrough); + + auto new_branch_delay_slot = IRDB_SDK::addNewDataBits(p_firp, nullptr, nop_bits); + + /* add a copy of the delay slot instruction */ + add_instruction(new_branch_delay_slot); + + /* finally, mark that we've patched this dollop to jump to the target */ + to_split->setFallthroughPatched(true); + + // renew the end of the newly placed instructions. + return cur_addr; +} diff --git a/zipr/src/cmdstr.hpp b/zipr/src/cmdstr.hpp new file mode 100644 index 0000000000000000000000000000000000000000..00e2b397f3db5f3aa53e234d25cde6cf36dd86c7 --- /dev/null +++ b/zipr/src/cmdstr.hpp @@ -0,0 +1,111 @@ +#ifndef cmdstr_hpp +#define cmdstr_hpp + +#include <spawn.h> +#include <poll.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <sys/wait.h> +#include <iostream> +#include <string> +#include <vector> +#include <array> +#include <functional> + +using namespace std; + +static inline pair<string,int> command_to_string( const string& command) +{ + auto ret=string(); + int exit_code=0; + posix_spawn_file_actions_t action; + + + const auto pipe_closer = function<void(int*)>([](int *p_pipefd) -> void + { + const auto pipe = *p_pipefd; + close(pipe); + delete p_pipefd; + }); + using PipeFD_t = unique_ptr<int,decltype(pipe_closer)>; + + const auto pipe_opener = [&]() -> vector<PipeFD_t> + { + auto pipe_fds = array<int,2>(); + if(pipe( pipe_fds.data())) + { + const auto err_str = string(strerror(errno)); + throw runtime_error("Cannot open pipe: " + err_str); + } + auto ret = vector<PipeFD_t>(); +// PipeFD_t p(new int(pipe_fds[0]), pipe_closer); +// ret.push_back(move(p)); + ret.push_back({new int(pipe_fds[0]), pipe_closer}); + ret.push_back({new int(pipe_fds[1]), pipe_closer}); + return ret; + }; + + auto cout_pipe_vec = pipe_opener(); + auto cerr_pipe_vec = pipe_opener(); + const auto cout_pipe = vector<int>{*(cout_pipe_vec[0]), *(cout_pipe_vec[1])}; + const auto cerr_pipe = vector<int>{*(cerr_pipe_vec[0]), *(cerr_pipe_vec[1])}; + + posix_spawn_file_actions_init(&action); + posix_spawn_file_actions_addclose(&action, cout_pipe[0]); + posix_spawn_file_actions_addclose(&action, cerr_pipe[0]); + posix_spawn_file_actions_adddup2(&action, cout_pipe[1], 1); + posix_spawn_file_actions_adddup2(&action, cerr_pipe[1], 2); + + posix_spawn_file_actions_addclose(&action, cout_pipe[1]); + posix_spawn_file_actions_addclose(&action, cerr_pipe[1]); + + vector<char> argsmem[] = {{'s', 'h', '\0'}, {'-', 'c', '\0'}}; // allows non-const access to literals + char *args[] = {&argsmem[0][0], &argsmem[1][0],const_cast<char*>(command.c_str()),nullptr}; + + pid_t pid; + if(posix_spawnp(&pid, args[0], &action, NULL, args, environ) != 0) + cout << "posix_spawnp failed with error: " << strerror(errno) << "\n"; + + cout_pipe_vec[1].reset(); + cerr_pipe_vec[1].reset(); // close child-side of pipes + + + // Read from pipes + string buffer(1024,' '); + vector<pollfd> plist = { {cout_pipe[0],POLLIN}, {cerr_pipe[0],POLLIN} }; + for ( int rval; (rval=poll(&plist[0],plist.size(),/*timeout*/-1))>0; ) + { + if ( plist[0].revents&POLLIN) + { + const auto bytes_read = read(cout_pipe[0], &buffer[0], buffer.length()); + ret += buffer.substr(0, static_cast<size_t>(bytes_read)); + } + else if ( plist[1].revents&POLLIN ) + { + const auto bytes_read = read(cerr_pipe[0], &buffer[0], buffer.length()); + ret += buffer.substr(0, static_cast<size_t>(bytes_read)); + } + else + break; // nothing left to read + } + + waitpid(pid,&exit_code,0); + cout << "exit code: " << exit_code << "\n"; + + posix_spawn_file_actions_destroy(&action); + return {ret,exit_code}; +} + +static inline int command_to_stream(const string& command, ostream& stream) +{ + cout << "Issuing command: " << command << endl; + const auto res = command_to_string(command); + + stream << res.first << endl; + return res.second; +} + + +#endif diff --git a/zipr/src/dollop.cpp b/zipr/src/dollop.cpp new file mode 100644 index 0000000000000000000000000000000000000000..4e6ac11d1985ee036f168d2668675dc7a9cef680 --- /dev/null +++ b/zipr/src/dollop.cpp @@ -0,0 +1,272 @@ +#include <zipr_all.h> +#include <iostream> + +#define ALLOF(a) std::begin((a)),std::end((a)) + +namespace Zipr_SDK +{ + bool DollopEntry_t::operator==(const DollopEntry_t &comp) + { + return + make_tuple( getInstruction(), getTargetDollop()) == + make_tuple(comp.getInstruction(), comp.getTargetDollop()) ; + } + + bool DollopEntry_t::operator!=(const Zipr_SDK::DollopEntry_t &comp) + { + return !operator==(comp); + } + + ostream &operator<<(ostream &out, const Zipr_SDK::Dollop_t &d) + { + + //for (it = d.begin(), it_end = d.end(); it != it_end; it++) + for (auto entry : d) + { + out << hex << *(entry) << endl; + } + auto fallback = d.getFallbackDollop(); + if ( fallback != nullptr) + out << "Fallback: " << hex << fallback << endl; + auto fallthrough = d.getFallthroughDollop(); + if ( fallthrough != nullptr) + out << "Fallthrough: " << hex << fallthrough << endl; + return out; + } +#if 0 + ostream &operator<<(ostream &out, const DollopPatch_t &p) { + out << hex << &p << ":" << hex << p.getTarget(); + return out; + } +#endif + + ostream &operator<<(ostream &out, const DollopEntry_t &p) { + out << "Instruction: " << hex << p.getInstruction() << endl; + out << "Target Dollop: " << hex << p.getTargetDollop() << endl; + return out; + } +} + +namespace zipr +{ + using namespace IRDB_SDK; + using namespace zipr; + using namespace std; + Dollop_t::Dollop_t(Instruction_t *start, Zipr_SDK::DollopManager_t *mgr) : + m_size(0), + m_fallthrough_dollop(nullptr), + m_fallback_dollop(nullptr), + m_fallthrough_patched(false), + m_coalesced(false), + m_was_truncated(false), + m_dollop_mgr(mgr) + { + Instruction_t *loop = nullptr; + + if (start == nullptr) + return; + + loop = start; + do { + push_back(new DollopEntry_t(loop, this)); + } while ((nullptr != (loop = loop->getFallthrough())) && + /* + * If this is a pinned instruction (or unpinned IBT), we want to stop! + */ + (nullptr == loop->getIndirectBranchTargetAddress()) + ); + + m_size = CalculateSize(); + } + + void Dollop_t::reCalculateSize() + { + m_size = CalculateSize(); + } + + size_t Dollop_t::CalculateSize() + { + // calculate the total, worst-case size of each dollop entry + auto total_dollop_entry_size = (size_t)0; + assert(m_dollop_mgr); + for (auto cur_de : *this ) + total_dollop_entry_size += m_dollop_mgr->determineDollopEntrySize(cur_de); + + // now determine if we need to add space for a trampoline. + + // no need for a trampoline if patched and/or coalesced. + if(m_fallthrough_patched || m_coalesced) + return total_dollop_entry_size; + + // no need for a trampoline if there is no fallthrough + const auto has_fallthrough = m_fallthrough_dollop || + (back() && back()->getInstruction() && back()->getInstruction()->getFallthrough()) + ; + if (!has_fallthrough) + return total_dollop_entry_size; + + // save space for a trampoline. + assert(m_dollop_mgr); + const auto l_dollop_mgr=dynamic_cast<ZiprDollopManager_t*>(m_dollop_mgr); + const auto l_zipr=l_dollop_mgr->GetZipr(); + const auto l_zipr_impl=dynamic_cast<ZiprImpl_t*>(l_zipr); + assert(l_zipr_impl); + return total_dollop_entry_size + l_zipr_impl->getSizer()->TRAMPOLINE_SIZE; + } + Zipr_SDK::DollopEntry_t *Dollop_t::getFallthroughDollopEntry(Zipr_SDK::DollopEntry_t *entry) const + { + const auto found_entry = find(ALLOF(*this), entry); + if (found_entry == end()) + return nullptr; + const auto next_entry=next(found_entry); + return next_entry == end() ? nullptr : *next_entry ; + } + + void Dollop_t::setCoalesced(bool coalesced) + { + m_coalesced = coalesced; + m_size = CalculateSize(); + } + + void Dollop_t::setFallthroughPatched(bool patched) + { + m_fallthrough_patched = patched; + m_size = CalculateSize(); + } + + Zipr_SDK::Dollop_t *Dollop_t::split(IRDB_SDK::Instruction_t *split_point) + { + /* + * 1. Find the matching dollop entry. + */ + DollopEntry_t query(split_point, nullptr); + Dollop_t *new_dollop = nullptr; + + auto de_split_point = find_if(begin(),end(), + [&query](const Zipr_SDK::DollopEntry_t *p) { +// cout << "Checking " +// << hex << query.Instruction() << " ?= " +// << hex << p->Instruction() << "." << endl; + return query.getInstruction() == p->getInstruction(); + }); + /* + * No matching split point. Just return nullptr. + */ + if (de_split_point == end()) + return nullptr; + + new_dollop = new Dollop_t(); + + new_dollop->setDollopManager(m_dollop_mgr); + + /* + * Set fallthrough and fallback dollop pointers. + * ----- ---- + * | | | | + * this - new - fallthrough + * | + * |----- + */ + if (m_fallthrough_dollop) + m_fallthrough_dollop->setFallbackDollop(new_dollop); + new_dollop->setFallbackDollop(this); + + new_dollop->setFallthroughDollop(m_fallthrough_dollop); + m_fallthrough_dollop = new_dollop; + + /* + * 2. Remove them from this one + */ + auto de_it = de_split_point; + while (de_it != end()) { + /* + * 3. Move them to a new one + */ + auto to_move = *de_it; + auto moved_it = de_it; + + de_it++; + + to_move->MemberOfDollop(new_dollop); + new_dollop->push_back(to_move); + erase(moved_it); + } + new_dollop->m_size = new_dollop->CalculateSize(); + m_size = CalculateSize(); + + /* + * 4. Return the new one + */ + return new_dollop; + } + + void Dollop_t::removeDollopEntries( + Zipr_SDK::DollopEntryList_t::iterator first_to_remove, + Zipr_SDK::DollopEntryList_t::iterator last_to_remove) + { + erase(first_to_remove, last_to_remove); + m_size = CalculateSize(); + } + + DollopEntry_t::DollopEntry_t(IRDB_SDK::Instruction_t *insn, Zipr_SDK::Dollop_t *member_of) + { + /* + * NB: This does not link if the insn has a target. + */ + m_instruction = insn; + m_target_dollop = nullptr; + m_member_of_dollop = member_of; + } + + bool DollopEntry_t::operator==(const DollopEntry_t &comp) { + cout << "operator==s being invoked " + << "(" << hex << m_instruction + << ", " << hex << comp.m_instruction + << ") " + << "(" << hex << m_target_dollop + << ", " << hex << comp.m_target_dollop + << ") " + << endl; + return comp.m_instruction == m_instruction && + comp.m_target_dollop == m_target_dollop; + } + + bool DollopEntry_t::operator!=(const DollopEntry_t &comp) { + return !operator==(comp); + } + + + + ostream &operator<<(ostream &out, const Dollop_t &d) + { + + //for (it = d.begin(), it_end = d.end(); it != it_end; it++) + for (auto entry : d) + { + out << hex << *(entry) << endl; + } + auto fallback = d.getFallbackDollop(); + if ( fallback != nullptr) + out << "Fallback: " << hex << fallback << endl; + auto fallthrough = d.getFallthroughDollop(); + if ( fallthrough != nullptr) + out << "Fallthrough: " << hex << fallthrough << endl; + return out; + } + + ostream &operator<<(ostream &out, const DollopPatch_t &p) { + out << hex << &p << ":" << hex << p.getTarget(); + return out; + } + + ostream &operator<<(ostream &out, const DollopEntry_t &p) { + out << "Instruction: " << hex << p.getInstruction() << endl; + out << "Target Dollop: " << hex << p.getTargetDollop() << endl; + return out; + } + + Zipr_SDK::Dollop_t* Dollop_t::createNewDollop(IRDB_SDK::Instruction_t *start, Zipr_SDK::DollopManager_t *mgr) + { + return new zipr::Dollop_t(start, mgr); + } +} diff --git a/zipr/src/ehwrite.cpp b/zipr/src/ehwrite.cpp new file mode 100644 index 0000000000000000000000000000000000000000..c754589b29e21b7593c517659753803300213f05 --- /dev/null +++ b/zipr/src/ehwrite.cpp @@ -0,0 +1,1586 @@ + +#include <zipr_all.h> +#include <irdb-core> +#include <iostream> +#include <stdlib.h> +#include <string.h> +#include <algorithm> +#include <map> +#include <assert.h> +#include <sys/mman.h> +#include <ctype.h> +#include <iostream> // cout +#include <string> // string, to_string +#include <fstream> +#include <elf.h> + +#include <zipr_dwarf2.hpp> + +#include "exeio.h" +#include "cmdstr.hpp" + +using namespace IRDB_SDK; +using namespace std; +using namespace zipr; +using namespace EXEIO; +using namespace EhWriter; + +#define ALLOF(a) begin(a),end(a) + +template < typename T > string to_hex_string( const T& n ) +{ + ostringstream stm ; + stm << hex << "0x" << n ; + return stm.str() ; +} + + + +PE_unwind_info_t::PE_unwind_info_t(Instruction_t* insn, ZiprImpl_t& p_zipr_obj) + : zipr_obj(p_zipr_obj) +{ + // extract bits from IR into this structure. + const auto ehpgm = insn->getEhProgram(); + const auto cie_pgm = ehpgm ->getCIEProgram(); + const auto fde_pgm = ehpgm ->getFDEProgram(); + const auto &ehcs = insn->getEhCallSite(); + const auto handler_insn = ehcs->getLandingPad(); + const auto &loc_map = *zipr_obj.getLocationMap(); + + // constants + ver_and_flags.parts.version = *reinterpret_cast<const uint8_t*>(cie_pgm[0].c_str()); + ver_and_flags.parts.flags = *reinterpret_cast<const uint8_t*>(cie_pgm[1].c_str()); + prolog_sz = 0; // don't do prologues for now. + frame_reg_and_offset.parts.frame_reg = *reinterpret_cast<const uint8_t*>(cie_pgm[2].c_str()); + frame_reg_and_offset.parts.frame_reg_offset = *reinterpret_cast<const uint8_t*>(cie_pgm[3].c_str()); + + // extract unwind array + for(const auto &s : fde_pgm) + unwind_array.push_back({*reinterpret_cast<const uint16_t*>(s.c_str())}); + + // extract the handler + if(handler_insn != nullptr) + handle.handler_rva = loc_map.at(handler_insn) - zipr_obj.getFileIR()->getArchitecture()->getFileBase(); + + start_rva = loc_map.at(insn) - zipr_obj.getFileIR()->getArchitecture()->getFileBase(); + end_rva = loc_map.at(insn) - zipr_obj.getFileIR()->getArchitecture()->getFileBase() + insn->getDataBits().size(); + + // extract user data + if(cie_pgm.size() > 4) + handle.user_data.insert(handle.user_data.end(), ALLOF(cie_pgm[4])); + +} + +template<int ptrsize> +typename PEEhWriter_t<ptrsize>::SehVector_t PEEhWriter_t<ptrsize>::BuildSehVector() +{ + // build a map of the instructions in program order + auto insn_in_order = map<VirtualOffset_t,Instruction_t*>(); + for(const auto& this_pair : *zipr_obj.GetLocationMap()) + insn_in_order[this_pair.second]=this_pair.first; + + // build the unwind directory and unwind info + auto insns_with_frame = 0u; + auto current_unwind_info = unique_ptr<PE_unwind_info_t>(); + auto all_unwind_infos = vector<unique_ptr<PE_unwind_info_t> >(); + + + // generate an unwind info struct for each chunk of code in the output program. + // record in all_unwind_infos + // for_each instruction in program order + for(const auto& this_pair : insn_in_order) + { + const auto &this_insn=this_pair.second; + const auto &this_addr=this_pair.first; + + // no eh pgm or call site? no worries, just ignore this insn + if(this_insn->getEhProgram()==nullptr && this_insn->getEhCallSite()==nullptr) + continue; + + insns_with_frame++; + + // if it has an unwinder and/or a call site, we will need an fde. + + // end this fde + if(current_unwind_info && !current_unwind_info->canExtend(this_insn)) + { + if(getenv("EH_VERBOSE")!=nullptr) + cout << "Ending unwind_info because insn " << hex << this_insn->getBaseID() << ":" << this_insn->getDisassembly() << " doesn't fit at " << this_addr << endl; + + current_unwind_info->extend(this_addr); // extend all the way up to this instruction, but then end this unwind info. + all_unwind_infos.push_back(move(current_unwind_info)); + } + + + // if we need to start a new fde, create one. + if(current_unwind_info == nullptr) + { + if(getenv("EH_VERBOSE")!=nullptr) + cout << "Creating new unwind_info for " << hex << this_insn->getBaseID() << ":" << this_insn->getDisassembly() << " at " << this_addr << endl; + current_unwind_info.reset(new PE_unwind_info_t(this_insn, zipr_obj)); + } + else + { + if(getenv("EH_VERBOSE")!=nullptr) + cout << "Extending unwind info for " << hex << this_insn->getBaseID() << ":" << this_insn->getDisassembly() << " at " << this_addr << endl; + + current_unwind_info->extend(this_insn); + } + } + const auto unwind_infos = all_unwind_infos.size(); + const auto avg_insn_per_unwind = insns_with_frame/(float)unwind_infos; + + cout << "# ATTRIBUTE ExceptionHandlerWrite::unwinds_calculated=" << dec << unwind_infos << endl; + cout << "# ATTRIBUTE ExceptionHandlerWrite::insns_with_eh_info=" << dec << insns_with_frame << endl; + cout << "# ATTRIBUTE ExceptionHandlerWrite::avg_insns_per_fde =" << dec << avg_insn_per_unwind << endl; + return (all_unwind_infos); +} + +template<int ptrsize> +void PEEhWriter_t<ptrsize>::LayoutSehVector(const PEEhWriter_t<ptrsize>::SehVector_t& seh_vector) +{ + const auto exc_dir_entry_sz = (sizeof(uint32_t)*3); + const auto exc_dir_sz = seh_vector.size() * exc_dir_entry_sz; + const auto unwind_info_addr = eh_addr + exc_dir_sz; + const auto firp = zipr_obj.getFileIR(); + const auto file_base = firp->getArchitecture()->getFileBase(); + + + + // assign addresses to the unwind infos. + // and record the bytes that go into the unwind area + auto cur_uia = unwind_info_addr; + auto ui_rvas = map<PE_unwind_info_t*, VirtualOffset_t>(); + auto all_ui_bytes = string(); + + for(auto & ui : seh_vector) + { + // record address + ui_rvas[ui.get()] = cur_uia - file_base; + + // get the raw bytes + auto raw_bytes = ui->get_raw_bytes(); + + // update the address of the next thing to be at the right place + cur_uia += raw_bytes.length(); + + // record the raw bytes for output in a moment. + all_ui_bytes += raw_bytes; + + } + + // now, record the exception directory entries + auto except_dir_str = string(); + for(auto &ui : seh_vector) + { + // create the entry + struct + { + uint32_t start_rva; + uint32_t end_rva; + uint32_t unwind_rva; + + } entry = + { + static_cast<uint32_t>(ui->getStartRVA()), + static_cast<uint32_t>(ui->getEndRVA()), + static_cast<uint32_t>(ui_rvas[ui.get()]) + }; + + // record the bytes as a string; + except_dir_str += string (reinterpret_cast<const char*>(&entry), sizeof(entry)); + } + + + // how to create a scoop + auto to_scoop=[&](const string& name, const string& contents, const VirtualOffset_t start_rva) -> DataScoop_t* + { + // create exception directory + const auto start_vo = start_rva; + const auto start_addr = firp->addNewAddress(BaseObj_t::NOT_IN_DATABASE, start_vo); + const auto end_vo = start_vo + contents.size(); + const auto end_addr = firp->addNewAddress(BaseObj_t::NOT_IN_DATABASE, end_vo); + return firp->addNewDataScoop(name, start_addr,end_addr,nullptr,4,false,contents); + }; + + // create a scoop in the IR for the new SEH data + to_scoop(".zipr_sehd" , except_dir_str, eh_addr); + to_scoop(".zipr_sehui", all_ui_bytes , eh_addr+except_dir_str.size()); + +} + +template<int ptrsize> +void PEEhWriter_t<ptrsize>::GenerateNewEhInfo() +{ + // find the location of the eh infos. + SetEhAddr(); + + // build the basic structure. + const auto seh_vec = BuildSehVector(); + LayoutSehVector(seh_vec); +} + +template<int ptrsize> +void ElfEhWriter_t<ptrsize>::GenerateNewEhInfo() +{ + + // find and record the max scoop addr, round it up + SetEhAddr(); + eh_frame_hdr_addr=eh_addr; + + BuildFDEs(); + GenerateEhOutput(); + CompileEhOutput(); + ScoopifyEhOutput(); + +} + +template <int ptrsize> +bool ElfEhWriter_t<ptrsize>::CIErepresentation_t::canSupport(Instruction_t* insn) const +{ + // if the insn is missing info, we can support it. + if(insn==nullptr) + return true; + if(insn->getEhProgram()==nullptr) + return true; + + // all info here. + + + // check that the program,CAF, DAF and RR match. if not, can't support + if(insn->getEhProgram()->getCIEProgram() != pgm || + insn->getEhProgram()->getCodeAlignmentFactor() != code_alignment_factor || + insn->getEhProgram()->getDataAlignmentFactor() != data_alignment_factor || + insn->getEhProgram()->getReturnRegNumber() != (int64_t)return_reg + ) + return false; + + auto personality_it=find_if( + insn->getEhProgram()->getRelocations().begin(), + insn->getEhProgram()->getRelocations().end(), + [](const Relocation_t* r) { return r->getType()=="personality"; }); + + // lastly, check for a compatible personality reloc. + // if incoming has no personality, we better have no personality to match. + if(personality_it==insn->getEhProgram()->getRelocations().end()) + { + return personality_reloc==nullptr; + } + + // incomming has personality, but do we? + if(personality_reloc==nullptr) return false; + + // compare personalities. + if(personality_reloc->getWRT() != (*personality_it)->getWRT()) return false; + if(personality_reloc->getAddend() != (*personality_it)->getAddend()) return false; + + return true; +} + +template <int ptrsize> +ElfEhWriter_t<ptrsize>::CIErepresentation_t::CIErepresentation_t(Instruction_t* insn, ElfEhWriter_t<ptrsize>* ehw) + : has_been_output(false) +{ + assert(insn && ehw && insn->getEhProgram()); + + pgm = insn->getEhProgram()->getCIEProgram(); + code_alignment_factor = insn->getEhProgram()->getCodeAlignmentFactor(); + data_alignment_factor = insn->getEhProgram()->getDataAlignmentFactor(); + return_reg = insn->getEhProgram()->getReturnRegNumber(); + + auto personality_it=find_if( + insn->getEhProgram()->getRelocations().begin(), + insn->getEhProgram()->getRelocations().end(), + [](const Relocation_t* r) { return r->getType()=="personality";}); + + personality_reloc = (personality_it==insn->getEhProgram()->getRelocations().end()) + ? (Relocation_t*)nullptr + : *personality_it; +} + + +template <int ptrsize> +void ElfEhWriter_t<ptrsize>::print_pers(Instruction_t* insn, ElfEhWriter_t<ptrsize>::CIErepresentation_t *cie) +{ + const auto pretty_print= [&](Relocation_t* pr) + { + if(pr==nullptr) + { + cout << "Found no personality reloc" << endl; + return; + } + const auto personality_scoop=dynamic_cast<DataScoop_t*>(pr->getWRT()); + const auto personality_insn=dynamic_cast<Instruction_t*>(pr->getWRT()); + + if(pr->getWRT()==nullptr) + cout << "\tFound null personality" << endl; + else if(personality_scoop) + cout << "\tFound personlity scoop " << personality_scoop->getName() << "+0x" << hex << pr->getAddend() << endl; + else if(personality_insn) + cout << "\tFound personlity instruction " << hex << personality_insn->getBaseID() << dec << ":" << hex << personality_insn->getDisassembly() << endl; + else + cout << "\tFound reloc: unexpected type? " << endl; + }; + + cout << " CIE-Personality addr= " << hex << cie->personality_reloc << dec << endl; + pretty_print(cie->GetPersonalityReloc()); + const auto personality_it=find_if( + insn->getEhProgram()->getRelocations().begin(), + insn->getEhProgram()->getRelocations().end(), + [](const Relocation_t* r) { return r->getType()=="personality"; }); + + const auto pr = (personality_it==insn->getEhProgram()->getRelocations().end()) + ? (Relocation_t*)nullptr + : *personality_it; + cout << " insn personality addr= " << hex << pr << dec << endl; + pretty_print(pr); + + +}; + + +template <int ptrsize> +ElfEhWriter_t<ptrsize>::FDErepresentation_t::FDErepresentation_t(Instruction_t* insn, ElfEhWriter_t<ptrsize>* ehw) + : + lsda(insn), + cie(nullptr) +{ + auto cie_it=find_if( ehw->all_cies.begin(), ehw->all_cies.end(), [&](const CIErepresentation_t* candidate) + { + return candidate->canSupport(insn); + }); + + if(cie_it==ehw->all_cies.end()) + { + cie=new CIErepresentation_t(insn,ehw); + ehw->all_cies.push_back(cie); + + if(getenv("EH_VERBOSE")!=nullptr) + cout << "Creating new CIE representation" << endl; + } + else + { + cie=*cie_it; + if(getenv("EH_VERBOSE")!=nullptr) + { + cout << "Re-using CIE representation" << endl; + print_pers(insn, cie); + } + } + + start_addr=ehw->zipr_obj.GetLocationMap()->at(insn); + last_advance_addr=start_addr; + end_addr=start_addr+insn->getDataBits().size(); + pgm=EhProgramListingManip_t(insn->getEhProgram()->getFDEProgram()); +} + + +template <int ptrsize> +int ElfEhWriter_t<ptrsize>::EhProgramListingManip_t::getMergeIndex(const EhProgramListingManip_t &other) +{ + auto other_index=(size_t)0; + for(const auto &this_ele : *this) + { + if(isAdvanceDirective(this_ele)) + continue; + + if(other_index >= other.size()) + return -1; + + if( this_ele!=other.at(other_index)) + return -1; + other_index++; + } + + return other_index; +} +template <int ptrsize> +bool ElfEhWriter_t<ptrsize>::EhProgramListingManip_t::canExtend(const EhProgramListingManip_t &other) +{ + return getMergeIndex(other) != -1; +} + +template <int ptrsize> +void ElfEhWriter_t<ptrsize>::EhProgramListingManip_t::extend(const uint64_t inc_amt, const EhProgramListingManip_t &other) +{ + if(size() > 0 && isAdvanceDirective(at(size()-1))) + { + auto &last_insn=at(size()-1); + auto old_inc_amt=(int)0; + auto old_size=(int)0; + switch(last_insn[0]) + { + case DW_CFA_advance_loc1: old_size=1; break; + case DW_CFA_advance_loc2: old_size=2; break; + case DW_CFA_advance_loc4: old_size=4; break; + } + for(auto i=0;i<old_size;i++) + ((char*)(&old_inc_amt))[i]=last_insn[i+1]; + + auto new_inc_amt=old_inc_amt+inc_amt; + auto opcode=(char)0; + auto new_size=(int)0; + if(new_inc_amt<=255) + { + new_size=1; + opcode=DW_CFA_advance_loc1; + } + else if(new_inc_amt<=65535) + { + new_size=2; + opcode=DW_CFA_advance_loc2; + } + else + { + new_size=4; + opcode=DW_CFA_advance_loc4; + } + last_insn.resize(1+new_size); + last_insn[0]=opcode; + for(auto i=0;i<new_size;i++) + last_insn[i+1]=((const char*)(&new_inc_amt))[i]; + } + else + { + auto new_size=(int)0; + auto opcode=(char)0; + if(inc_amt<=255) + { + new_size=1; + opcode=DW_CFA_advance_loc1; + } + else if(inc_amt<=65535) + { + new_size=2; + opcode=DW_CFA_advance_loc2; + } + else + { + new_size=4; + opcode=DW_CFA_advance_loc4; + } + // make an advance directive + string ehinsn; + ehinsn.resize(1+new_size); + ehinsn[0]=opcode; + for(auto i=0;i<new_size;i++) + ehinsn[i+1]=((const char*)(&inc_amt))[i]; + + // add it with this->push_back() + this->push_back(ehinsn); + } + + // add elements from other[merge_index]-other[other.size()] + auto merge_index=getMergeIndex(other); + assert(merge_index>=0); + for_each(other.begin()+merge_index,other.end(), [&](const string& s) + { + this->push_back(s); + }); + + +} + +template <int ptrsize> +bool ElfEhWriter_t<ptrsize>::EhProgramListingManip_t::isAdvanceDirective(const string &s) const +{ + // make sure uint8_t is an unsigned char. + static_assert(is_same<unsigned char, uint8_t>::value, "uint8_t is not unsigned char"); + + auto data=s.data(); + auto opcode=data[0]; + auto opcode_upper2=(uint8_t)(opcode >> 6); + auto opcode_lower6=(uint8_t)(opcode & (0x3f)); + + switch(opcode_upper2) + { + case 1: + { + return true; + } + case 0: + { + switch(opcode_lower6) + { + case DW_CFA_advance_loc1: + case DW_CFA_advance_loc2: + case DW_CFA_advance_loc4: + return true; + + } + } + } + return false; + +} + +// see https://en.wikipedia.org/wiki/LEB128 +template <int ptrsize> +static bool read_uleb128 + ( uint64_t &result, + uint32_t& position, + const uint8_t* const data, + const uint32_t max) +{ + result = 0; + auto shift = 0; + while( position < max ) + { + auto byte = data[position]; + position++; + result |= ( ( byte & 0x7f ) << shift); + if ( ( byte & 0x80) == 0) + break; + shift += 7; + } + return ( position > max ); + +} + +// see https://en.wikipedia.org/wiki/LEB128 +template <int ptrsize> +static bool read_sleb128 ( + int64_t &result, + uint32_t & position, + const uint8_t* const data, + const uint32_t max) +{ + result = 0; + auto shift = 0; + auto size = 64; // number of bits in signed integer; + auto byte=uint8_t(0); + do + { + byte = data [position]; + position++; + result |= ((byte & 0x7f) << shift); + shift += 7; + } while( (byte & 0x80) != 0); + + /* sign bit of byte is second high order bit (0x40) */ + if ((shift < size) && ( (byte & 0x40) !=0 /* sign bit of byte is set */)) + /* sign extend */ + result |= - (1 << shift); + return ( position > max ); + +} + + + + +template <int ptrsize> +static void print_uleb_operand( + stringstream &sout, + uint32_t pos, + const uint8_t* const data, + const uint32_t max) +{ + auto uleb=uint64_t(0xdeadbeef); + read_uleb128<ptrsize>(uleb, pos, data, max); + sout << " " << dec << uleb; +} + +template <int ptrsize> +static void print_sleb_operand( + stringstream &sout, + uint32_t pos, + const uint8_t* const data, + const uint32_t max) +{ + auto leb=int64_t(0xdeadbeef); + read_sleb128<ptrsize>(leb, pos, data, max); + sout << " " << dec << leb; +} + + + +template <int ptrsize> +string ElfEhWriter_t<ptrsize>::EhProgramListingManip_t::getPrintableString(const string &s) const +{ + + stringstream sout; + // make sure uint8_t is an unsigned char. + static_assert(is_same<unsigned char, uint8_t>::value, "uint8_t is not unsigned char"); + + auto data=s; + auto opcode=(uint8_t)data[0]; + auto opcode_upper2=(uint8_t)(opcode >> 6); + auto opcode_lower6=(uint8_t)(opcode & (0x3f)); + auto pos=uint32_t(1); + auto max=data.size(); + + switch(opcode_upper2) + { + case 1: + { + // case DW_CFA_advance_loc: + sout << " cfa_advance_loc " << dec << +opcode_lower6 << " * caf"; + break; + } + case 2: + { + uint64_t uleb=0; + sout << " cfa_offset "; + if(read_uleb128<ptrsize>(uleb, pos, (const uint8_t* const)data.data(), max)) + break; + // case DW_CFA_offset: + sout << dec << uleb; + break; + } + case 3: + { + // case DW_CFA_restore (register #): + sout << " cfa_restore"; + break; + } + case 0: + { + switch(opcode_lower6) + { + + case DW_CFA_nop: + sout << " nop" ; + break; + case DW_CFA_remember_state: + sout << " remember_state" ; + break; + case DW_CFA_restore_state: + sout << " restore_state" ; + break; + + // takes single uleb128 + case DW_CFA_undefined: + sout << " undefined" ; + print_uleb_operand<ptrsize>(sout,pos,(const uint8_t* const)data.data(),max); + break; + + case DW_CFA_same_value: + sout << " same_value "; + print_uleb_operand<ptrsize>(sout,pos,(const uint8_t* const)data.data(),max); + break; + case DW_CFA_restore_extended: + sout << " restore_extended "; + print_uleb_operand<ptrsize>(sout,pos,(const uint8_t* const)data.data(),max); + break; + case DW_CFA_def_cfa_register: + sout << " def_cfa_register "; + print_uleb_operand<ptrsize>(sout,pos,(const uint8_t* const)data.data(),max); + break; + case DW_CFA_GNU_args_size: + sout << " GNU_arg_size "; + print_uleb_operand<ptrsize>(sout,pos,(const uint8_t* const)data.data(),max); + break; + case DW_CFA_def_cfa_offset: + sout << " def_cfa_offset "; + print_uleb_operand<ptrsize>(sout,pos,(const uint8_t* const)data.data(),max); + break; + + case DW_CFA_set_loc: + { + auto arg=uintptr_t(0xDEADBEEF); + switch(ptrsize) + { + case 4: + arg=*(uint32_t*)&data.data()[pos]; break; + case 8: + arg=*(uint64_t*)&data.data()[pos]; break; + default: + assert(0); + } + sout << " set_loc " << hex << arg; + break; + } + case DW_CFA_advance_loc1: + { + auto loc=*(uint8_t*)(&data.data()[pos]); + sout << " advance_loc1 " << +loc << " * caf " ; + break; + } + + case DW_CFA_advance_loc2: + { + auto loc=*(uint16_t*)(&data.data()[pos]); + sout << " advance_loc2 " << +loc << " * caf " ; + break; + } + + case DW_CFA_advance_loc4: + { + auto loc=*(uint32_t*)(&data.data()[pos]); + sout << " advance_loc4 " << +loc << " * caf " ; + break; + } + case DW_CFA_offset_extended: + sout << " offset_extended "; + print_uleb_operand<ptrsize>(sout,pos,(const uint8_t* const)data.data(),max); + print_uleb_operand<ptrsize>(sout,pos,(const uint8_t* const)data.data(),max); + break; + case DW_CFA_register: + sout << " register "; + print_uleb_operand<ptrsize>(sout,pos,(const uint8_t* const)data.data(),max); + print_uleb_operand<ptrsize>(sout,pos,(const uint8_t* const)data.data(),max); + break; + case DW_CFA_def_cfa: + sout << " def_cfa "; + print_uleb_operand<ptrsize>(sout,pos,(const uint8_t* const)data.data(),max); + print_uleb_operand<ptrsize>(sout,pos,(const uint8_t* const)data.data(),max); + break; + case DW_CFA_def_cfa_sf: + sout << " def_cfa_sf "; + print_uleb_operand<ptrsize>(sout,pos,(const uint8_t* const)data.data(),max); + print_sleb_operand<ptrsize>(sout,pos,(const uint8_t* const)data.data(),max); + break; + + case DW_CFA_def_cfa_expression: + { + auto uleb=uint64_t(0); + sout << " def_cfa_expression "; + if(read_uleb128<ptrsize>(uleb, pos, (const uint8_t* const)data.data(), max)) + break; + sout << dec << uleb; + pos+=uleb; // doing this old school for now, as we aren't printing the expression. + sout << " (not printing expression)"; + break; + } + case DW_CFA_expression: + { + auto uleb1=uint64_t(0); + auto uleb2=uint64_t(0); + sout << " expression "; + if(read_uleb128<ptrsize>(uleb1, pos, (const uint8_t* const)data.data(), max)) + break; + if(read_uleb128<ptrsize>(uleb2, pos, (const uint8_t* const)data.data(), max)) + break; + sout << dec << uleb1 << " " << uleb2; + pos+=uleb2; + break; + } + case DW_CFA_val_expression: + { + auto uleb1=uint64_t(0); + auto uleb2=uint64_t(0); + sout << " val_expression "; + if(read_uleb128<ptrsize>(uleb1, pos, (const uint8_t* const)data.data(), max)) + break; + if(read_uleb128<ptrsize>(uleb2, pos, (const uint8_t* const)data.data(), max)) + break; + sout << dec << uleb1 << " " << uleb2; + pos+=uleb2; + break; + } + case DW_CFA_def_cfa_offset_sf: + { + auto leb=int64_t(0); + sout << " def_cfa_offset_sf "; + if(read_sleb128<ptrsize>(leb, pos, (const uint8_t* const)data.data(), max)) + break; + sout << dec << leb; + break; + } + case DW_CFA_offset_extended_sf: + { + auto uleb1=uint64_t(0); + auto sleb2=int64_t(0); + sout << " offset_extended_sf "; + if(read_uleb128<ptrsize>(uleb1, pos, (const uint8_t* const)data.data(), max)) + break; + if(read_sleb128<ptrsize>(sleb2, pos, (const uint8_t* const)data.data(), max)) + break; + sout << dec << uleb1 << " " << sleb2; + break; + } + + + /* SGI/MIPS specific */ + case DW_CFA_MIPS_advance_loc8: + + /* GNU extensions */ + case DW_CFA_GNU_window_save: + case DW_CFA_GNU_negative_offset_extended: + default: + sout << "Unhandled opcode cannot print. opcode=" << opcode; + } + break; + } + default: + assert(0); + } + + return sout.str(); +} + +template <int ptrsize> +ElfEhWriter_t<ptrsize>::FDErepresentation_t::LSDArepresentation_t::LSDArepresentation_t(Instruction_t* insn) + // if there are call sites, use the call site encoding. if not, set to omit for initializer. + // extend/canExtend should be able to extend an omit to a non-omit. + : tt_encoding( insn->getEhCallSite() ? insn->getEhCallSite()->getTTEncoding() : 0xff) +{ + + extend(insn); +} + + + +static const auto RelocsEqual=[](const Relocation_t* a, const Relocation_t* b) -> bool +{ + if(a==nullptr && b==nullptr) + return true; + if(a==nullptr || b==nullptr) + return false; + return + forward_as_tuple(a->getType(), a->getOffset(), a->getWRT(), a->getAddend()) == + forward_as_tuple(b->getType(), b->getOffset(), b->getWRT(), b->getAddend()); +}; + +template <int ptrsize> +bool ElfEhWriter_t<ptrsize>::FDErepresentation_t::LSDArepresentation_t::canExtend(Instruction_t* insn) const +{ + if(insn->getEhCallSite() == nullptr) + return true; + + const auto insn_tt_encoding = insn->getEhCallSite()->getTTEncoding(); + + // no type table + if(tt_encoding==0xff || insn_tt_encoding==0xff) // DW_EH_PE_omit (0xff) + { + // no encoding issues. + } + // check if tt encodings match. + else if(insn_tt_encoding!=tt_encoding) + return true; + + assert((tt_encoding&0xf)==0x3 || // encoding contains DW_EH_PE_udata4 + (tt_encoding)==0xff || // or is exactly DW_EH_PE_omit + ((tt_encoding&0xf)==0x0 && ptrsize==4) || // or is exactly DW_EH_PE_absptr on 32-bit + (tt_encoding&0xf)==0xb ); // or encoding contains DW_EH_PE_sdata4 + const auto tt_entry_size=4; + + const auto mismatch_tt_entry = find_if( + insn->getEhCallSite()->getRelocations().begin(), + insn->getEhCallSite()->getRelocations().end(), + [&](const Relocation_t* candidate_reloc) + { + const auto tt_index=candidate_reloc->getOffset()/tt_entry_size; + if(tt_index>=(int64_t)type_table.size()) + return false; + const auto &tt_entry=type_table.at(tt_index); + + if(tt_entry==nullptr) // entry is empty, so no conflict + return false; + return !RelocsEqual(candidate_reloc, tt_entry); + } + ); + + // return true if we found no mismatches + return (mismatch_tt_entry == insn->getEhCallSite()->getRelocations().end()); +} + +template <int ptrsize> +void ElfEhWriter_t<ptrsize>::FDErepresentation_t::LSDArepresentation_t::extend(Instruction_t* insn) +{ + // if there's no call site info, the LSDA doesn't need an extension. + if(insn->getEhCallSite() == nullptr) + return; + + const auto insn_tt_encoding = insn->getEhCallSite()->getTTEncoding(); + + // FIXME: optimization possibilty: see if the last call site in the table + // has the same set of catch-types + landing_pad and is "close enough" to this insn. + // if so, combine. + + cout << "Creating call sites in LSDA for " << hex << insn->getBaseID() << ":" << insn->getDisassembly() << endl; + + // just create a new entry in the CS table.. + auto cs=(call_site_t){0}; + + cs.cs_insn_start=insn; + cs.cs_insn_end=insn; + cs.landing_pad=insn->getEhCallSite()->getLandingPad(); + + if(tt_encoding == 0xff /* omit */) + { + /* existing is omit, use new encoding */ + tt_encoding=insn_tt_encoding; + } + else if(insn_tt_encoding == 0xff /* omit */) + { + /* new encoding is omit, use current tt encoding */ + } + else if(tt_encoding == insn_tt_encoding) + { + /* encodings match, -- do nothing */ + } + else + assert(0); // canExtend failure? + + + + + // go through the relocations on the eh call site and + // resolve each one by putting it into a set. + // the set will be (non-duplicating) inserted as a "entry" (really multiple entries) + // into the action table, + // Each reloc will also get a non-duplicating insert into the type table. + for(const auto &reloc : insn->getEhCallSite()->getRelocations()) + { + const auto wrt_scoop=dynamic_cast<DataScoop_t*>(reloc->getWRT()); + if(reloc->getWRT()==nullptr) + cout << "\tFound reloc: nullptr (catch all)" << endl; + else if(wrt_scoop) + cout << "\tFound reloc: scoop " << wrt_scoop->getName() << "+0x" << hex << reloc->getAddend() << endl; + else + cout << "\tFound reloc: unexpected type? " << endl; + + // for now, this is the only supported reloc type on a EhCallSite + assert(reloc->getType()=="type_table_entry"); + auto tt_it=find_if(type_table.begin(),type_table.end(), + [reloc](const Relocation_t* candidate) { return candidate!=nullptr && RelocsEqual(candidate,reloc); }); + if(tt_it==type_table.end()) + { + const auto tt_encoding = insn->getEhCallSite()->getTTEncoding(); + assert( + (tt_encoding&0xf)==0x3 || // encoding contains DW_EH_PE_udata4 + (tt_encoding&0xf)==0xb || // encoding contains DW_EH_PE_sdata4 + ((tt_encoding&0xf)==0x0 && ptrsize==4) // encoding contains DW_EH_PE_absptr && ptrsize==4 + ); + const auto tt_entry_size=4; + const auto tt_index= reloc->getOffset()/tt_entry_size; + if(tt_index>=(int64_t)type_table.size()) + type_table.resize(tt_index+1); + assert(type_table.at(tt_index)==nullptr || RelocsEqual(type_table.at(tt_index),reloc)); + type_table[tt_index]=reloc; + } + } + + cs.actions=insn->getEhCallSite()->getTTOrderVector(); + + auto at_it=find(action_table.begin(),action_table.end(), cs.actions); + if(at_it==action_table.end()) + { + // actions will be at the end of the action table. + cs.action_table_index=action_table.size(); + + // add to the action table + action_table.push_back(cs.actions); + } + else + { + // calc which entry in the action table for the call site. + cs.action_table_index=at_it-action_table.begin(); + } + + // the call site will index the action table at the end of the action table. + callsite_table.push_back(cs); +} + +template <int ptrsize> +bool ElfEhWriter_t<ptrsize>::FDErepresentation_t::canExtend(Instruction_t* insn, ElfEhWriter_t<ptrsize>* ehw) +{ + const auto insn_addr=ehw->zipr_obj.GetLocationMap()->at(insn); + const auto new_fde_thresh=100; + + if(insn_addr<start_addr) + return false; + + if(end_addr + new_fde_thresh < insn_addr) + return false; + + assert(cie); + if(!cie->canSupport(insn)) + return false; // can't change the CIE. + + return pgm.canExtend(insn->getEhProgram()->getFDEProgram()) && + lsda.canExtend(insn); + + +} + +template <int ptrsize> +void ElfEhWriter_t<ptrsize>::FDErepresentation_t::extend(Instruction_t* insn, ElfEhWriter_t<ptrsize>* ehw) +{ + const auto insn_addr=ehw->zipr_obj.GetLocationMap()->at(insn); + const auto new_end_addr=insn_addr+insn->getDataBits().size(); + const auto incr_amnt=insn_addr-last_advance_addr; + last_advance_addr=insn_addr; + + // add appropriate instructions to the pgm. + pgm.extend((incr_amnt)/cie->code_alignment_factor, insn->getEhProgram()->getFDEProgram()); + + lsda.extend(insn); + + // extend the end + end_addr=new_end_addr; + +} + +template<int ptrsize> +void ElfEhWriter_t<ptrsize>::BuildFDEs() +{ + // build a map of the instructions in program order + auto insn_in_order = map<VirtualOffset_t,Instruction_t*>(); + for(const auto& this_pair : *zipr_obj.GetLocationMap()) + insn_in_order[this_pair.second]=this_pair.first; + + // build the fdes (and cies/lsdas) for this insn, starting with a null fde in case none exist + auto current_fde=(FDErepresentation_t*)nullptr; + auto insns_with_frame=0; + + // for_each instruction in program order + for(const auto& this_pair : insn_in_order) + { + const auto &this_insn=this_pair.second; + const auto &this_addr=this_pair.first; + + // no eh pgm or call site? no worries, just ignore this insn + if(this_insn->getEhProgram()==nullptr && this_insn->getEhCallSite()==nullptr) + continue; + + insns_with_frame++; + + // if it has an unwinder and/or a call site, we will need an fde. + + // end this fde + if(current_fde && !current_fde->canExtend(this_insn, this)) + { + if(getenv("EH_VERBOSE")!=nullptr) + cout << "Ending FDE because insn " << hex << this_insn->getBaseID() << ":" << this_insn->getDisassembly() << " doesn't fit at " << this_addr << endl; + current_fde=nullptr; + } + + + // if we need to start a new fde, create one. + if(current_fde==nullptr) + { + if(getenv("EH_VERBOSE")!=nullptr) + cout << "Creating new FDE for " << hex << this_insn->getBaseID() << ":" << this_insn->getDisassembly() << " at " << this_addr << endl; + current_fde=new FDErepresentation_t(this_insn,this); + all_fdes.push_back(current_fde); + } + else + { + if(getenv("EH_VERBOSE")!=nullptr) + { + cout << "Extending new FDE for " << hex << this_insn->getBaseID() << ":" << this_insn->getDisassembly() << " at " << this_addr << endl; + print_pers(this_insn,current_fde->cie); + + } + current_fde->extend(this_insn,this); + } + } + + const auto avg_insn_per_fde = insns_with_frame/(float)all_fdes.size(); + + + assert(getenv("SELF_VALIDATE")==nullptr || all_fdes.size() > 10 ) ; + assert(getenv("SELF_VALIDATE")==nullptr || all_cies.size() > 0 ) ; + assert(getenv("SELF_VALIDATE")==nullptr || insns_with_frame > 10 ); + assert(getenv("SELF_VALIDATE")==nullptr || avg_insn_per_fde > 1 ) ; + + cout << "# ATTRIBUTE ExceptionHandlerWrite::fdes_calculated=" << dec << all_fdes.size() << endl; + cout << "# ATTRIBUTE ExceptionHandlerWrite::cies_calculated=" << dec << all_cies.size() << endl; + cout << "# ATTRIBUTE ExceptionHandlerWrite::insns_with_eh_info=" << dec << insns_with_frame << endl; + cout << "# ATTRIBUTE ExceptionHandlerWrite::avg_insns_per_fde=" << dec << avg_insn_per_fde << endl; +} + +template<int ptrsize> +void ElfEhWriter_t<ptrsize>::GenerateEhOutput() +{ + + auto output_program=[&](const EhProgramListingManip_t& p, ostream & out) + { + auto flags=out.flags();//save flags + for(auto i=0U; i < p.size() ; i ++ ) + { + const auto &s = p[i]; + if(i==p.size()-1 && p.isAdvanceDirective(s)) /* no need to output last insn if it's an advance */ + break; + out << " .byte "; + auto first=true; + for(const auto &c : s) + { + if(!first) + { + out << ", "; + } + else + first=false; + out << "0x" << hex << setfill('0') << setw(2) << ((int)c&0xff); + } + out << asm_comment << p.getPrintableString(s) << endl; + } + out.flags(flags); // restore flags + }; + const auto output_lsda=[&](const FDErepresentation_t* fde, ostream& out, const uint32_t lsda_num) -> void + { + const auto lsda=&fde->lsda; + out << "LSDA" << dec << lsda_num << ":" << endl; + if(!lsda->exists()) + { + return; + } + + // the encoding of the landing pad is done in two parts: + // landing_pad=landing_pad_base+landing_pad_offset. + // typically, the landing_pad_base is omitted and the fde->start_addr is used instead. + // However, sometimes the fde->start_addr is equal to the landing_pad, + // which would make the landing_pad_offset 0. + // but a landing_pad offset of 0 indicates no landing pad. + // To avoid this situation, we first detect it, + // then arbitrarily pick (and encode) a landing_pad_base that's + // not equal to any landing paad in the call site list. + const auto calc_landing_pad_base=[&]() -> uint64_t + { + // look for a landing pad that happens to match the fde->start_addr + const auto lp_base_conflict_it=find_if( + lsda->callsite_table.begin(), + lsda->callsite_table.end(), + [&](const typename FDErepresentation_t::LSDArepresentation_t::call_site_t& candidate) + { + if(candidate.landing_pad==nullptr) + return false; + const auto lp_addr=zipr_obj.GetLocationMap()->at(candidate.landing_pad); + return (lp_addr==fde->start_addr); + }); + + // if not found, there's no need to adjust the landing pad base address. + if(lp_base_conflict_it==lsda->callsite_table.end()) + return fde->start_addr; + + // we pick an arbitrary landing_pad base by taking the min of all + // landing pads (and the fde_start addr), and subtracting 1. + const auto min_cs_entry_it=min_element( + lsda->callsite_table.begin(), + lsda->callsite_table.end(), + [&](const typename FDErepresentation_t::LSDArepresentation_t::call_site_t& a , const typename FDErepresentation_t::LSDArepresentation_t::call_site_t &b) + { + const auto lp_a=a.landing_pad; + const auto lp_b=b.landing_pad; + if(lp_a && lp_b) + { + const auto lp_addr_a=zipr_obj.GetLocationMap()->at(lp_a); + const auto lp_addr_b=zipr_obj.GetLocationMap()->at(lp_b); + return lp_addr_a<lp_addr_b; + } + if(!lp_a && lp_b) + return false; + if(!lp_b && lp_a) + return true; + return false; + }); + const auto &min_cs_entry=*min_cs_entry_it; + const auto min_lp_addr=zipr_obj.GetLocationMap()->at(min_cs_entry.landing_pad); + const auto min_addr=min(min_lp_addr,fde->start_addr); + return min_addr-1; + + }; + + const auto landing_pad_base=calc_landing_pad_base(); + + // how to output actions + const auto output_action=[&](const IRDB_SDK::TTOrderVector_t &act, const uint32_t act_num) -> void + { + const auto &ttov=act; + const auto biggest_ttov_index=ttov.size()-1; + auto act_entry_num=biggest_ttov_index; + + for(int i=act_entry_num; i>=0; i--) + { + out << "LSDA" << dec << lsda_num << "_act" << act_num << "_start_entry" << act_entry_num << "" << ":" << endl; + out << " .sleb128 " << dec << ttov.at(act_entry_num) << endl; + if(act_entry_num==biggest_ttov_index) + out << " .sleb128 0 " << endl; + else + out << " .sleb128 LSDA" << lsda_num << "_act" << act_num << "_start_entry" << act_entry_num+1 << " - . " << endl; + act_entry_num--; + } + }; + + + const auto output_callsite=[&](const typename FDErepresentation_t::LSDArepresentation_t::call_site_t &cs, const uint32_t cs_num) -> void + { + const auto cs_start_addr=zipr_obj.GetLocationMap()->at(cs.cs_insn_start); + const auto cs_end_addr=zipr_obj.GetLocationMap()->at(cs.cs_insn_start)+cs.cs_insn_start->getDataBits().size(); + const auto cs_len=cs_end_addr-cs_start_addr; + out << "LSDA" << dec << lsda_num << "_cs_tab_entry" << cs_num << "_start:" << endl; + out << asm_comment << " 1) start of call site relative to FDE start addr (call site encoding)" << endl; + out << " .sleb128 0x" << hex << cs_start_addr << " - 0x" << hex << fde->start_addr << endl; + out << asm_comment << " 2) length of call site (call site encoding)" << endl; + out << " .sleb128 " << dec << cs_len << endl; + if(cs.landing_pad) + { + const auto lp_addr=zipr_obj.GetLocationMap()->at(cs.landing_pad); + out << asm_comment << " 3) the landing pad, or 0 if none exists. (call site encoding)" << endl; + out << " .sleb128 0x" << hex << lp_addr << " - 0x" << hex << landing_pad_base << endl; + } + else + { + out << asm_comment << " 3) the landing pad, or 0 if none exists. (call site encoding)" << endl; + out << " .sleb128 0" << endl; + } + if(cs.actions.size() > 0 ) + { + out << asm_comment << " 4) index into action table + 1 -- 0 indicates unwind only (call site encoding)" << endl; + out << " .sleb128 1 + LSDA" << dec << lsda_num << "_act" + << cs.action_table_index << "_start_entry0 - LSDA" << dec << lsda_num << "_action_tab_start" << endl; + } + else + { + out << asm_comment << " 4) index into action table + 1 -- 0 indicates unwind only (always uleb)" << endl; + out << " .uleb128 0 // no actions!" << endl; + } + out << "LSDA" << dec << lsda_num << "_cs_tab_entry" << cs_num << "_end:" << endl; + + }; + + const auto output_lsda_header=[&]() + { + if(landing_pad_base==fde->start_addr) + { + out << asm_comment << " 1) encoding of next field " << endl; + out << " .byte 0xff " << asm_comment << " DW_EH_PE_omit (0xff)" << endl; + out << "" << endl; + out << asm_comment << " 2) landing pad base, if omitted, use FDE start addr" << endl; + out << asm_comment << " .<fdebasetype> <fdebase> -- omitted. " << endl; + } + else + { + out << asm_comment << " 1) encoding of next field " << endl; + out << " .byte 0x1b " << asm_comment << " DW_EH_PE_pcrel (0x10) |sdata4 (0xb)" << endl; + out << "" << endl; + out << " " << asm_comment << " 2) landing pad base, if omitted, use FDE start addr" << endl; + out << " .int 0x" << hex << landing_pad_base << " + eh_frame_hdr_start - . - " << dec << eh_frame_hdr_addr << " " << asm_comment << " as pcrel|sdata4 . " << endl; + } + out << "" << endl; + out << asm_comment << " 3) encoding of type table entries" << endl; + out << " .byte 0x" << hex << lsda->tt_encoding << " " << asm_comment << " DW_EH_PE_udata4" << endl; + out << "" << endl; + out << asm_comment << " 4) type table pointer -- always a uleb128" << endl; + if(lsda->tt_encoding==0xff) /* omit */ + { + out << asm_comment << " .uleb128 LSDAptr omitted" << endl; + } + else + { + out << " .uleb128 LSDA" << dec << lsda_num << "_type_table_end - LSDA" << lsda_num << "_tt_ptr_end" << endl; + } + out << "LSDA" << dec << lsda_num << "_tt_ptr_end:" << endl; + out << "" << endl; + out << asm_comment << " 5) call site table encoding" << endl; + out << " .byte 0x9 " << asm_comment << " DW_EH_PE_sleb128 " << endl; + out << "" << endl; + out << asm_comment << " 6) the length of the call site table" << endl; + out << " .uleb128 LSDA" << dec << lsda_num << "_cs_tab_end-LSDA" << dec << lsda_num << "_cs_tab_start" << endl; + out << "LSDA" << dec << lsda_num << "_cs_tab_start:" << endl; + }; + + const auto output_call_site_table=[&]() + { + // output call site table + auto cs_num=0; + for(const auto & cs : lsda->callsite_table) + { + output_callsite(cs,cs_num++); + } + out << "LSDA" << dec << lsda_num << "_cs_tab_end:" << endl; + }; + const auto output_action_table=[&]() + { + // output action table + out << "LSDA" << dec << lsda_num << "_action_tab_start:" << endl; + auto act_num=0; + for(const auto & act : lsda->action_table) + { + output_action(act,act_num++); + } + out << "LSDA" << dec << lsda_num << "_action_tab_end:" << endl; + }; + + const auto output_type_table=[&]() + { + out << "LSDA" << dec << lsda_num << "_type_table_start:" << endl; + for_each( lsda->type_table.rbegin(), lsda->type_table.rend(), [&](const Relocation_t* reloc) + { + if(reloc==nullptr) + { + // indicates a catch all or empty type table entry + out << " .int 0x0 " << asm_comment << " not used!" << endl; + } + else if(reloc->getWRT()==nullptr) + { + // indicates a catch all or empty type table entry + out << " .int 0x0 " << asm_comment << " catch all " << endl; + } + else + { + // indicates a catch of a paritcular type + const auto scoop=dynamic_cast<DataScoop_t*>(reloc->getWRT()); + assert(scoop); + const auto final_addr=scoop->getStart()->getVirtualOffset() + reloc->getAddend(); + if(((lsda->tt_encoding)&0x10) == 0x10) // if encoding contains pcrel (0x10). + out << " .int 0x" << hex << final_addr << " + eh_frame_hdr_start - . - " << dec << eh_frame_hdr_addr << endl; + else + out << " .int 0x" << hex << final_addr << endl; + + } + }); + out << "LSDA" << dec << lsda_num << "_type_table_end:" << endl; + }; + + output_lsda_header(); + output_call_site_table(); + output_action_table(); + output_type_table(); + + }; + const auto output_cie=[&](const CIErepresentation_t* cie, ostream& out) -> void + { + assert(cie); + if(cie->has_been_output) + return; + + const auto cie_pos_it=find(all_cies.begin(), all_cies.end(), cie); + assert(cie_pos_it!=all_cies.end()); + + const auto personality_scoop=cie->personality_reloc ? dynamic_cast<DataScoop_t*> (cie->personality_reloc->getWRT()) : (DataScoop_t*)nullptr; + const auto personality_insn =cie->personality_reloc ? dynamic_cast<Instruction_t*>(cie->personality_reloc->getWRT()) : (Instruction_t*)nullptr; + const auto personality_addend=cie->personality_reloc ? cie->personality_reloc->getAddend() : 0; + + const auto cie_pos=cie_pos_it-all_cies.begin(); + + cie->has_been_output=true; + out << asm_comment << " cie " << dec << cie_pos << "" << endl; + out << "Lcie" << cie_pos << ":" << endl; + out << " .int Lcie" << cie_pos << "_end - Lcie" << cie_pos << " - 4 " << asm_comment << " length of this record. -4 because length doesn't include this field" << endl; + out << " .int 0 " << asm_comment << " cie (not fde)" << endl; + out << " .byte 3 " << asm_comment << " version" << endl; + out << " .asciz \"zPLR\" " << asm_comment << " aug string." << endl; + out << " .uleb128 " << dec << cie->code_alignment_factor << " " << asm_comment << " code alignment factor" << endl; + out << " .sleb128 " << dec << cie->data_alignment_factor << " " << asm_comment << " data alignment factor" << endl; + out << " .uleb128 " << dec << cie->return_reg << " " << asm_comment << " return address reg." << endl; + out << " " << asm_comment << " encode the Z (length)" << endl; + out << " .sleb128 Lcie" << cie_pos << "_aug_data_end-Lcie" << cie_pos << "_aug_data_start " << asm_comment << " Z -- handle length field" << endl; + out << "Lcie" << cie_pos << "_aug_data_start:" << endl; + out << "" << endl; + if(personality_scoop) + { + auto personality_value=personality_scoop->getStart()->getVirtualOffset()+personality_addend; + out << " " << asm_comment << "encode the P (personality encoding + personality routine)" << endl; + out << " .byte 0x80 | 0x10 | 0x0B " << asm_comment << " personality pointer encoding DH_EH_PE_indirect (0x80) | pcrel | sdata4" << endl; + out << " .int " << personality_value << " + eh_frame_hdr_start - . - " << dec << eh_frame_hdr_addr << " " << asm_comment << " actual personality routine, encoded as noted in prev line." << endl; + } + else if(personality_insn) + { + const auto personality_insn_addr=zipr_obj.GetLocationMap()->at(personality_insn); + const auto personality_value=personality_insn_addr+personality_addend; + out << " " << asm_comment << "encode the P (personality encoding + personality routine)" << endl; + out << " .byte 0x10 | 0x0B " << asm_comment << " personality pointer encoding pcrel | sdata4" << endl; + out << " .int " << personality_value << " + eh_frame_hdr_start - . - " << dec << eh_frame_hdr_addr << " " << asm_comment << " actual personality routine, encoded as noted in prev line." << endl; + } + else + { + assert(cie->personality_reloc==nullptr || cie->personality_reloc->getWRT()==nullptr); + out << " " << asm_comment << "encode the P (personality encoding + personality routine)" << endl; + out << " .byte 0x0B " << asm_comment << " personality pointer encoding sdata4" << endl; + out << " .int 0 " << asm_comment << " actual personality routine, encoded as noted in prev line." << endl; + } + out << "" << endl; + out << " " << asm_comment << " encode L (lsda encoding) " << endl; + out << " .byte 0x1b " << asm_comment << " LSDA encoding (pcrel|sdata4)" << endl; + out << "" << endl; + out << " " << asm_comment << " encode R (FDE encoding) " << endl; + out << " .byte 0x10 | 0x0B " << asm_comment << " FDE encoding (pcrel | sdata4)" << endl; + out << "Lcie" << cie_pos << "_aug_data_end:" << endl; + out << " " << asm_comment << " CIE program" << endl; + output_program(cie->pgm,out); + out << "" << endl; + out << " " << asm_comment << " pad with nops" << endl; + out << " .align 4, 0" << endl; + out << "Lcie" << cie_pos << "_end:" << endl; + + }; + auto output_fde=[&](const FDErepresentation_t* fde, ostream& out, const uint32_t fde_num) -> void + { + assert(fde && fde->cie); + output_cie(fde->cie,out); + + + auto cie_pos_it=find(all_cies.begin(), all_cies.end(), fde->cie); + assert(cie_pos_it!=all_cies.end()); + auto cie_pos=cie_pos_it-all_cies.begin(); + + out << "" << asm_comment << "fde " << dec << fde_num << "" << endl; + out << "Lfde" << fde_num << ":" << endl; + out << " .int Lfde" << fde_num << "_end - Lfde" << fde_num << " - 4 " << asm_comment << " length of this record. -4 because " + "length doesn't include this field." << endl; + out << " .int . - Lcie" << cie_pos << " " << asm_comment << " this is an FDE (not a " + "cie), and it's cie is CIE" << cie_pos << ". byte offset from start of field." << endl; + out << " .int 0x" << hex << fde->start_addr << dec << " + eh_frame_hdr_start - . - " << dec << eh_frame_hdr_addr << " " << asm_comment << " FDE start addr" << endl; + out << " .int " << dec << fde->end_addr-fde->start_addr << " " << asm_comment << " fde range length (i.e., can calc the " + "fde_end_addr from this -- note that pcrel is ignored here!)" << endl; + out << " " << asm_comment << "encode Z (length)" << endl; + out << " .uleb128 Lfde" << fde_num << "_aug_data_end-Lfde" << fde_num << "_aug_data_start" << endl; + out << "Lfde" << fde_num << "_aug_data_start:" << endl; + out << " " << asm_comment << "encode L (LSDA) " << endl; + if(fde->hasLsda()) + out << " .int LSDA" << fde_num << " - . " << asm_comment << " LSDA hard coded here (as pcrel+sdata4)" << endl; + else + out << " .int 0 + eh_frame_hdr_start - . - " << dec << eh_frame_hdr_addr << " " << asm_comment << " no LSDA, encoded with pcrel " << endl; + out << "Lfde" << fde_num << "_aug_data_end:" << endl; + out << "" << endl; + out << " " << asm_comment << " FDE" << fde_num << " program" << endl; + output_program(fde->pgm,out); + out << " .align 4, 0" << endl; + out << " Lfde" << fde_num << "_end:" << endl; + + }; + auto generate_eh_frame_hdr=[&](ostream& out) -> void + { + out << ".section eh_frame_hdr, \"a\", @progbits" << endl; + out << "eh_frame_hdr_start:" << endl; + out << " .byte 1 " << asm_comment << " version" << endl; + out << " .byte 0x10 | 0x0B " << asm_comment << " encoding for pointer to eh-frame -- DH_EH_PE_pcrel (0x10) | DH_EH_PE_sdata4 (0x0B)" << endl; + out << " .byte 0x03 " << asm_comment << " encoding for ; of entries in eh-frame-hdr -- BDH_EH_PE_udata4 (0x03)" << endl; + out << " .byte 0x30 | 0x0B " << asm_comment << " encoding for pointers (to fdes) held in the eh-frame-hdr header " + "-- DH_EH_PE_datarel (0x30) | DH_EH_PE_sdata4 (0x0b) " << endl; + + out << " .int Lfde_table - . " << asm_comment << " pointer to fde_table, encoded as an sdata4, pcrel" << endl; + out << " .int (eh_frame_table_end-eh_frame_table)/8 " << asm_comment << " number of FDEs in the header." << endl; + +// on some archs, this line causes an additional alignment, as the assembler feels free to align as much as you want. +// but the eh-parser is expecting the table _right here_, so we do the alignment manually by making sure the above part +// has a multiple-of-4 bytes. +// out << " .align 4" << endl; + out << "eh_frame_table:" << endl; + out << " " << asm_comment << " fde pointers" << endl; + + for(auto fde_num=0U; fde_num < all_fdes.size(); fde_num++) + { + const auto& fde=all_fdes[fde_num]; + out << " .int 0x" << hex << fde->start_addr<<" - " << dec << eh_frame_hdr_addr << endl; + out << " .int Lfde" << dec << fde_num << " - eh_frame_hdr_start" << endl; + } + + out << "eh_frame_table_end:" << endl; + + }; + auto generate_eh_frame=[&](ostream& out) -> void + { + out << ".section eh_frame, \"a\", @progbits" << endl; + out << "Lfde_table: " << asm_comment << " needed for xref to eh_frame_hdr" << endl; + + auto fde_num=0; + for(const auto& fde: all_fdes) + { + output_fde(fde,out, fde_num++); + } + + out << "\t.int 0 " << asm_comment << " null terminator " << endl; + }; + auto generate_gcc_except_table=[&](ostream& out) -> void + { + out << ".section gcc_except_table, \"a\", @progbits" << endl; + auto lsda_num=0; + for(const auto& fde: all_fdes) + { + output_lsda(fde,out,lsda_num++); + } + }; + + ofstream fout(ehframe_s_filename, ofstream::out); + if(!fout) + { + cerr << "Fatal: cannot open " << ehframe_s_filename << "." << endl; + exit(2); + } + + generate_eh_frame_hdr(fout); + generate_eh_frame(fout); + generate_gcc_except_table(fout); +} + +template<int ptrsize> +void ElfEhWriter_t<ptrsize>::CompileEhOutput() +{ + + const auto eh_frame_hdr_addr_str=to_hex_string(eh_frame_hdr_addr); + + // create and execute the command to build the ehframe. + auto cmd=(string)"$PEASOUP_HOME/tools/eh_frame_tools/eh_to_bin.sh "+ehframe_s_filename+" "+eh_frame_hdr_addr_str+" "+ehframe_exe_filename; + cout << "Running: " << cmd << endl; + auto res=command_to_stream(cmd,cout); // system(cmd.c_str()); + + // err check. + if( res==-1 || WEXITSTATUS(res)!=0 ) + { + perror("Cannot compile eh_frame."); + cerr << "Exit code=" << res << endl; + cerr << " for command=" << cmd << endl; + exit(2); + } +} + +template<int ptrsize> +void ElfEhWriter_t<ptrsize>::ScoopifyEhOutput() +{ + EXEIO::exeio ehframe_exe_rep; + ehframe_exe_rep.load(ehframe_exe_filename); + + auto to_scoop=[&](const string &secname)->void + { + const auto &sec=ehframe_exe_rep.sections[secname]; + + // if sec is missing, don't scoopify. + + if(sec==nullptr) return; + const auto data = string(sec->get_data(), sec->get_size()); + const auto start_vo = sec->get_address(); + const auto start_addr= zipr_obj.getFileIR()->addNewAddress(BaseObj_t::NOT_IN_DATABASE, start_vo); + const auto end_vo = sec->get_address()+sec->get_size()-1; + const auto end_addr = zipr_obj.getFileIR()->addNewAddress(BaseObj_t::NOT_IN_DATABASE, end_vo); + const auto new_scoop = zipr_obj.getFileIR()->addNewDataScoop(secname, start_addr,end_addr,nullptr,4,false,data); + (void)new_scoop; + + //zipr_obj.getFileIR()->getAddresses().insert(start_addr); + //zipr_obj.getFileIR()->getAddresses().insert(end_addr); + //zipr_obj.getFileIR()->getDataScoops().insert(new_scoop); + }; + + to_scoop(".eh_frame_hdr"); + to_scoop(".eh_frame"); + to_scoop(".gcc_except_table"); + + +} + +template<int ptrsize> +ElfEhWriter_t<ptrsize>::~ElfEhWriter_t() +{ + for(const auto &i : all_fdes) delete i; + for(const auto &i : all_cies) delete i; +} + + +unique_ptr<EhWriter_t> EhWriter_t::factory(ZiprImpl_t& zipr_obj) +{ + auto firp=zipr_obj.getFileIR(); + const auto is_pe = firp->getArchitecture()->getFileType() == adftPE; + const auto is_elf = firp->getArchitecture()->getFileType() == adftELFEXE || + firp->getArchitecture()->getFileType() == adftELFSO; + const auto is_64 = firp->getArchitectureBitWidth()==64; + const auto is_32 = firp->getArchitectureBitWidth()==32; + + auto eh = (is_elf && is_64) ? static_cast<EhWriter_t*>(new ElfEhWriter_t<8>(zipr_obj)) : + (is_elf && is_32) ? static_cast<EhWriter_t*>(new ElfEhWriter_t<4>(zipr_obj)) : + (is_pe && is_64) ? static_cast<EhWriter_t*>(new PEEhWriter_t <8>(zipr_obj)) : + throw invalid_argument("Cannot create eh factory for unknown file type/ptr size combo"); + + return unique_ptr<EhWriter_t>(eh); +} + + +/* make sure these get compiled */ +namespace zipr +{ + template class ElfEhWriter_t<8>; + template class ElfEhWriter_t<4>; +}; diff --git a/zipr/src/elfwrite.cpp b/zipr/src/elfwrite.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d5f6671aef02f49d2b5ecad60a368d799e78dd00 --- /dev/null +++ b/zipr/src/elfwrite.cpp @@ -0,0 +1,1642 @@ + +#include <zipr_all.h> +#include <irdb-core> +#include <iostream> +#include <stdlib.h> +#include <string.h> +#include <algorithm> +#include <map> +#include <assert.h> +#include <sys/mman.h> +#include <ctype.h> +#include <iostream> // cout +#include <string> // string, to_string +#include <fstream> +#include <elf.h> +#include <endian.h> + +#include "elfio/elfio.hpp" +#include "elfio/elfio_dump.hpp" + +using namespace IRDB_SDK; +using namespace std; +using namespace zipr; +using namespace ELFIO; + +template<class T_Elf_Ehdr, class T_Elf_Shdr> +static inline void host_to_shdr(const T_Elf_Ehdr& ehdr, T_Elf_Shdr& shdr) +{ +#if 0 + struct Elf64_Shdr + { + Elf64_Word sh_name + Elf64_Word sh_type + Elf64_Xword sh_flags + Elf64_Addr sh_addr + Elf64_Off sh_offset + Elf64_Xword sh_size + Elf64_Word sh_link + Elf64_Word sh_info + Elf64_Xword sh_addralign + Elf64_Xword sh_entsize + } + + steruct Elf32_Shdr + { + Elf32_Word sh_name + Elf32_Word sh_type + Elf32_Word sh_flags + Elf32_Addr sh_addr + Elf32_Off sh_offset + Elf32_Word sh_size + Elf32_Word sh_link + Elf32_Word sh_info + Elf32_Word sh_addralign + Elf32_Word sh_entsize + } +#endif + + + if(ehdr.e_ident[5]==1) // little endian elf file + { + shdr.sh_name = htole32(shdr.sh_name); + shdr.sh_type = htole32(shdr.sh_type); + shdr.sh_link = htole32(shdr.sh_link); + shdr.sh_info = htole32(shdr.sh_info); + if(sizeof(ehdr.e_entry)==4) // 32-bit header + { + shdr.sh_flags = htole32(shdr.sh_flags); + shdr.sh_addr = htole32(shdr.sh_addr); + shdr.sh_offset = htole32(shdr.sh_offset); + shdr.sh_size = htole32(shdr.sh_size); + shdr.sh_addralign = htole32(shdr.sh_addralign); + shdr.sh_entsize = htole32(shdr.sh_entsize); + } + else if(sizeof(ehdr.e_entry)==8) // 64-bit header + { + shdr.sh_flags = htole64(shdr.sh_flags); + shdr.sh_addr = htole64(shdr.sh_addr); + shdr.sh_offset = htole64(shdr.sh_offset); + shdr.sh_size = htole64(shdr.sh_size); + shdr.sh_addralign = htole64(shdr.sh_addralign); + shdr.sh_entsize = htole64(shdr.sh_entsize); + } + else + { + assert(0); + } + + } + else if(ehdr.e_ident[5]==2) // big endian elf file + { + shdr.sh_name = htobe32(shdr.sh_name); + shdr.sh_type = htobe32(shdr.sh_type); + shdr.sh_link = htobe32(shdr.sh_link); + shdr.sh_info = htobe32(shdr.sh_info); + if(sizeof(ehdr.e_entry)==4) // 32-bit header + { + shdr.sh_flags = htobe32(shdr.sh_flags); + shdr.sh_addr = htobe32(shdr.sh_addr); + shdr.sh_offset = htobe32(shdr.sh_offset); + shdr.sh_size = htobe32(shdr.sh_size); + shdr.sh_addralign = htobe32(shdr.sh_addralign); + shdr.sh_entsize = htobe32(shdr.sh_entsize); + } + else if(sizeof(ehdr.e_entry)==8) // 64-bit header + { + shdr.sh_flags = htobe64(shdr.sh_flags); + shdr.sh_addr = htobe64(shdr.sh_addr); + shdr.sh_offset = htobe64(shdr.sh_offset); + shdr.sh_size = htobe64(shdr.sh_size); + shdr.sh_addralign = htobe64(shdr.sh_addralign); + shdr.sh_entsize = htobe64(shdr.sh_entsize); + } + else + { + assert(0); + } + } + else + { + assert(0); + } + +} + +template<class T_Elf_Ehdr, class T_Elf_Shdr> +static inline void shdr_to_host(const T_Elf_Ehdr& ehdr, T_Elf_Shdr& shdr) +{ + +#if 0 + struct Elf64_Shdr + { + Elf64_Word sh_name + Elf64_Word sh_type + Elf64_Xword sh_flags + Elf64_Addr sh_addr + Elf64_Off sh_offset + Elf64_Xword sh_size + Elf64_Word sh_link + Elf64_Word sh_info + Elf64_Xword sh_addralign + Elf64_Xword sh_entsize + } + + steruct Elf32_Shdr + { + Elf32_Word sh_name + Elf32_Word sh_type + Elf32_Word sh_flags + Elf32_Addr sh_addr + Elf32_Off sh_offset + Elf32_Word sh_size + Elf32_Word sh_link + Elf32_Word sh_info + Elf32_Word sh_addralign + Elf32_Word sh_entsize + } +#endif + + + if(ehdr.e_ident[5]==1) // little endian elf file + { + shdr.sh_name = le32toh(shdr.sh_name); + shdr.sh_type = le32toh(shdr.sh_type); + shdr.sh_link = le32toh(shdr.sh_link); + shdr.sh_info = le32toh(shdr.sh_info); + if(sizeof(ehdr.e_entry)==4) // 32-bit header + { + shdr.sh_flags = le32toh(shdr.sh_flags); + shdr.sh_addr = le32toh(shdr.sh_addr); + shdr.sh_offset = le32toh(shdr.sh_offset); + shdr.sh_size = le32toh(shdr.sh_size); + shdr.sh_addralign = le32toh(shdr.sh_addralign); + shdr.sh_entsize = le32toh(shdr.sh_entsize); + } + else if(sizeof(ehdr.e_entry)==8) // 64-bit header + { + shdr.sh_flags = le64toh(shdr.sh_flags); + shdr.sh_addr = le64toh(shdr.sh_addr); + shdr.sh_offset = le64toh(shdr.sh_offset); + shdr.sh_size = le64toh(shdr.sh_size); + shdr.sh_addralign = le64toh(shdr.sh_addralign); + shdr.sh_entsize = le64toh(shdr.sh_entsize); + } + else + { + assert(0); + } + + } + else if(ehdr.e_ident[5]==2) // big endian elf file + { + shdr.sh_name = be32toh(shdr.sh_name); + shdr.sh_type = be32toh(shdr.sh_type); + shdr.sh_link = be32toh(shdr.sh_link); + shdr.sh_info = be32toh(shdr.sh_info); + if(sizeof(ehdr.e_entry)==4) // 32-bit header + { + shdr.sh_flags = be32toh(shdr.sh_flags); + shdr.sh_addr = be32toh(shdr.sh_addr); + shdr.sh_offset = be32toh(shdr.sh_offset); + shdr.sh_size = be32toh(shdr.sh_size); + shdr.sh_addralign = be32toh(shdr.sh_addralign); + shdr.sh_entsize = be32toh(shdr.sh_entsize); + } + else if(sizeof(ehdr.e_entry)==8) // 64-bit header + { + shdr.sh_flags = be64toh(shdr.sh_flags); + shdr.sh_addr = be64toh(shdr.sh_addr); + shdr.sh_offset = be64toh(shdr.sh_offset); + shdr.sh_size = be64toh(shdr.sh_size); + shdr.sh_addralign = be64toh(shdr.sh_addralign); + shdr.sh_entsize = be64toh(shdr.sh_entsize); + } + else + { + assert(0); + } + } + else + { + assert(0); + } + +} + +template<class T_Elf_Ehdr, class T_Elf_Phdr> +static inline void phdr_to_host(const T_Elf_Ehdr& ehdr, T_Elf_Phdr& phdr) +{ + +#if 0 + + 64-bit: + + struct { + Elf64_Word p_type; size=4 + Elf64_Word p_flags; size=4 + Elf64_Off p_offset; size=8 + Elf64_Addr p_vaddr; size=8 + Elf64_Addr p_paddr; size=8 + Elf64_Xword p_filesz; size=8 + Elf64_Xword p_memsz; size=8 + Elf64_Xword p_align; size=8 + } + + 32-bit: + + struct { + Elf32_Word p_type; 4 + Elf32_Off p_offset; 4 + Elf32_Addr p_vaddr; 4 + Elf32_Addr p_paddr; 4 + Elf32_Word p_filesz; 4 + Elf32_Word p_memsz; 4 + Elf32_Word p_flags; 4 + Elf32_Word p_align; 4 + } +#endif + + + if(ehdr.e_ident[5]==1) // little endian elf file + { + phdr.p_flags = le32toh(phdr.p_flags); + if(sizeof(ehdr.e_entry)==4) // 32-bit header + { + phdr.p_offset = le32toh(phdr.p_offset); + phdr.p_vaddr = le32toh(phdr.p_vaddr); + phdr.p_paddr = le32toh(phdr.p_paddr); + phdr.p_filesz = le32toh(phdr.p_filesz); + phdr.p_memsz = le32toh(phdr.p_memsz); + phdr.p_align = le32toh(phdr.p_align); + } + else if(sizeof(ehdr.e_entry)==8) // 64-bit header + { + phdr.p_offset = le64toh(phdr.p_offset); + phdr.p_vaddr = le64toh(phdr.p_vaddr); + phdr.p_paddr = le64toh(phdr.p_paddr); + phdr.p_filesz = le64toh(phdr.p_filesz); + phdr.p_memsz = le64toh(phdr.p_memsz); + phdr.p_align = le64toh(phdr.p_align); + } + else + { + assert(0); + } + + } + else if(ehdr.e_ident[5]==2) // big endian elf file + { + phdr.p_type = be32toh(phdr.p_type); + phdr.p_flags = be32toh(phdr.p_flags); + if(sizeof(ehdr.e_entry)==4) // 32-bit header + { + phdr.p_offset = be32toh(phdr.p_offset); + phdr.p_vaddr = be32toh(phdr.p_vaddr); + phdr.p_paddr = be32toh(phdr.p_paddr); + phdr.p_filesz = be32toh(phdr.p_filesz); + phdr.p_memsz = be32toh(phdr.p_memsz); + phdr.p_align = be32toh(phdr.p_align); + } + else if(sizeof(ehdr.e_entry)==8) // 64-bit header + { + phdr.p_offset = be64toh(phdr.p_offset); + phdr.p_vaddr = be64toh(phdr.p_vaddr); + phdr.p_paddr = be64toh(phdr.p_paddr); + phdr.p_filesz = be64toh(phdr.p_filesz); + phdr.p_memsz = be64toh(phdr.p_memsz); + phdr.p_align = be64toh(phdr.p_align); + } + else + { + assert(0); + } + + } + else + { + assert(0); + } + +} + +template<class T_Elf_Ehdr, class T_Elf_Phdr> +static inline void host_to_phdr(const T_Elf_Ehdr& ehdr, T_Elf_Phdr& phdr) +{ + +#if 0 + + 64-bit: + + struct { + Elf64_Word p_type; size=4 + Elf64_Word p_flags; size=4 + Elf64_Off p_offset; size=8 + Elf64_Addr p_vaddr; size=8 + Elf64_Addr p_paddr; size=8 + Elf64_Xword p_filesz; size=8 + Elf64_Xword p_memsz; size=8 + Elf64_Xword p_align; size=8 + } + + 32-bit: + + struct { + Elf32_Word p_type; 4 + Elf32_Off p_offset; 4 + Elf32_Addr p_vaddr; 4 + Elf32_Addr p_paddr; 4 + Elf32_Word p_filesz; 4 + Elf32_Word p_memsz; 4 + Elf32_Word p_flags; 4 + Elf32_Word p_align; 4 + } +#endif + + + if(ehdr.e_ident[5]==1) // little endian elf file + { + phdr.p_flags = htole32(phdr.p_flags); + if(sizeof(ehdr.e_entry)==4) // 32-bit header + { + phdr.p_offset = htole32(phdr.p_offset); + phdr.p_vaddr = htole32(phdr.p_vaddr); + phdr.p_paddr = htole32(phdr.p_paddr); + phdr.p_filesz = htole32(phdr.p_filesz); + phdr.p_memsz = htole32(phdr.p_memsz); + phdr.p_align = htole32(phdr.p_align); + } + else if(sizeof(ehdr.e_entry)==8) // 64-bit header + { + phdr.p_offset = htole64(phdr.p_offset); + phdr.p_vaddr = htole64(phdr.p_vaddr); + phdr.p_paddr = htole64(phdr.p_paddr); + phdr.p_filesz = htole64(phdr.p_filesz); + phdr.p_memsz = htole64(phdr.p_memsz); + phdr.p_align = htole64(phdr.p_align); + } + else + { + assert(0); + } + + } + else if(ehdr.e_ident[5]==2) // big endian elf file + { + phdr.p_type = htobe32(phdr.p_type); + phdr.p_flags = htobe32(phdr.p_flags); + if(sizeof(ehdr.e_entry)==4) // 32-bit header + { + phdr.p_offset = htobe32(phdr.p_offset); + phdr.p_vaddr = htobe32(phdr.p_vaddr); + phdr.p_paddr = htobe32(phdr.p_paddr); + phdr.p_filesz = htobe32(phdr.p_filesz); + phdr.p_memsz = htobe32(phdr.p_memsz); + phdr.p_align = htobe32(phdr.p_align); + } + else if(sizeof(ehdr.e_entry)==8) // 64-bit header + { + phdr.p_offset = htobe64(phdr.p_offset); + phdr.p_vaddr = htobe64(phdr.p_vaddr); + phdr.p_paddr = htobe64(phdr.p_paddr); + phdr.p_filesz = htobe64(phdr.p_filesz); + phdr.p_memsz = htobe64(phdr.p_memsz); + phdr.p_align = htobe64(phdr.p_align); + } + else + { + assert(0); + } + + } + else + { + assert(0); + } + +} + +template<class T_Elf_Ehdr> +static inline void ehdr_to_host(T_Elf_Ehdr& ehdr) +{ +#if 0 +struct Elf32_Ehdr { + unsigned char e_ident[16]; + Elf32_Half e_type; + Elf32_Half e_machine; + Elf32_Word e_version; + Elf32_Addr e_entry; + Elf32_Off e_phoff; + Elf32_Off e_shoff; + Elf32_Word e_flags; + Elf32_Half e_ehsize; + Elf32_Half e_phentsize; + Elf32_Half e_phnum; + Elf32_Half e_shentsize; + Elf32_Half e_shnum; + Elf32_Half e_shstrndx; +} +struct Elf64_Ehdr { + unsigned char e_ident[16]; + Elf64_Half e_type; + Elf64_Half e_machine; + Elf64_Word e_version; + Elf64_Addr e_entry; + Elf64_Off e_phoff; + Elf64_Off e_shoff; + Elf64_Word e_flags; + Elf64_Half e_ehsize; + Elf64_Half e_phentsize; + Elf64_Half e_phnum; + Elf64_Half e_shentsize; + Elf64_Half e_shnum; + Elf64_Half e_shstrndx; +} +#endif + + if(ehdr.e_ident[5]==1) // little endian elf file + { + ehdr.e_type = le16toh(ehdr.e_type); + ehdr.e_machine = le16toh(ehdr.e_machine); + ehdr.e_version = le32toh(ehdr.e_version); + ehdr.e_flags = le32toh(ehdr.e_flags); + ehdr.e_ehsize = le16toh(ehdr.e_ehsize); + ehdr.e_phentsize = le16toh(ehdr.e_phentsize); + ehdr.e_phnum = le16toh(ehdr.e_phnum); + ehdr.e_shentsize = le16toh(ehdr.e_shentsize); + ehdr.e_shnum = le16toh(ehdr.e_shnum); + ehdr.e_shstrndx = le16toh(ehdr.e_shstrndx); + + if(sizeof(ehdr.e_entry)==4) // 32-bit header + { + ehdr.e_entry = le32toh(ehdr.e_entry); + ehdr.e_phoff = le32toh(ehdr.e_phoff); + ehdr.e_shoff = le32toh(ehdr.e_shoff); + } + else if(sizeof(ehdr.e_entry)==8) // 64-bit header + { + ehdr.e_entry = le64toh(ehdr.e_entry); + ehdr.e_phoff = le64toh(ehdr.e_phoff); + ehdr.e_shoff = le64toh(ehdr.e_shoff); + } + else + { + assert(0); + } + + } + else if(ehdr.e_ident[5]==2) // big endian elf file + { + ehdr.e_type = be16toh(ehdr.e_type); + ehdr.e_machine = be16toh(ehdr.e_machine); + ehdr.e_version = be32toh(ehdr.e_version); + ehdr.e_flags = be32toh(ehdr.e_flags); + ehdr.e_ehsize = be32toh(ehdr.e_ehsize); + ehdr.e_phentsize = be16toh(ehdr.e_phentsize); + ehdr.e_phnum = be16toh(ehdr.e_phnum); + ehdr.e_shentsize = be16toh(ehdr.e_shentsize); + ehdr.e_shnum = be16toh(ehdr.e_shnum); + ehdr.e_shstrndx = be16toh(ehdr.e_shstrndx); + + if(sizeof(ehdr.e_entry)==4) //32-bit header + { + ehdr.e_entry = be32toh(ehdr.e_entry); + ehdr.e_phoff = be32toh(ehdr.e_phoff); + ehdr.e_shoff = be32toh(ehdr.e_shoff); + } + else if(sizeof(ehdr.e_entry)==8) // 64-bit header + { + ehdr.e_entry = be64toh(ehdr.e_entry); + ehdr.e_phoff = be64toh(ehdr.e_phoff); + ehdr.e_shoff = be64toh(ehdr.e_shoff); + } + else + { + assert(0); + } + + } + else + { + assert(0); + } +} + +template<class T_Elf_Ehdr> +static inline void host_to_ehdr(T_Elf_Ehdr& ehdr) +{ +#if 0 +struct Elf32_Ehdr { + unsigned char e_ident[16]; + Elf32_Half e_type; + Elf32_Half e_machine; + Elf32_Word e_version; + Elf32_Addr e_entry; + Elf32_Off e_phoff; + Elf32_Off e_shoff; + Elf32_Word e_flags; + Elf32_Half e_ehsize; + Elf32_Half e_phentsize; + Elf32_Half e_phnum; + Elf32_Half e_shentsize; + Elf32_Half e_shnum; + Elf32_Half e_shstrndx; +} +struct Elf64_Ehdr { + unsigned char e_ident[16]; + Elf64_Half e_type; + Elf64_Half e_machine; + Elf64_Word e_version; + Elf64_Addr e_entry; + Elf64_Off e_phoff; + Elf64_Off e_shoff; + Elf64_Word e_flags; + Elf64_Half e_ehsize; + Elf64_Half e_phentsize; + Elf64_Half e_phnum; + Elf64_Half e_shentsize; + Elf64_Half e_shnum; + Elf64_Half e_shstrndx; +} +#endif + if(ehdr.e_ident[5]==1) // little endian elf file + { + ehdr.e_type = htole16(ehdr.e_type); + ehdr.e_machine = htole16(ehdr.e_machine); + ehdr.e_version = htole32(ehdr.e_version); + ehdr.e_flags = htole32(ehdr.e_flags); + ehdr.e_ehsize = htole16(ehdr.e_ehsize); + ehdr.e_phentsize = htole16(ehdr.e_phentsize); + ehdr.e_phnum = htole16(ehdr.e_phnum); + ehdr.e_shentsize = htole16(ehdr.e_shentsize); + ehdr.e_shnum = htole16(ehdr.e_shnum); + ehdr.e_shstrndx = htole16(ehdr.e_shstrndx); + + if(sizeof(ehdr.e_entry)==4) // 32-bit header + { + ehdr.e_entry = htole32(ehdr.e_entry); + ehdr.e_phoff = htole32(ehdr.e_phoff); + ehdr.e_shoff = htole32(ehdr.e_shoff); + } + else if(sizeof(ehdr.e_entry)==8) // 64-bit header + { + ehdr.e_entry = htole64(ehdr.e_entry); + ehdr.e_phoff = htole64(ehdr.e_phoff); + ehdr.e_shoff = htole64(ehdr.e_shoff); + } + else + { + assert(0); + } + + } + else if(ehdr.e_ident[5]==2) // big endian elf file + { + ehdr.e_type = htobe16(ehdr.e_type); + ehdr.e_machine = htobe16(ehdr.e_machine); + ehdr.e_version = htobe32(ehdr.e_version); + ehdr.e_flags = htobe32(ehdr.e_flags); + ehdr.e_ehsize = htobe16(ehdr.e_ehsize); + ehdr.e_phentsize = htobe16(ehdr.e_phentsize); + ehdr.e_phnum = htobe16(ehdr.e_phnum); + ehdr.e_shentsize = htobe16(ehdr.e_shentsize); + ehdr.e_shnum = htobe16(ehdr.e_shnum); + ehdr.e_shstrndx = htobe16(ehdr.e_shstrndx); + + if(sizeof(ehdr.e_entry)==4) //32-bit header + { + ehdr.e_entry = htobe32(ehdr.e_entry); + ehdr.e_phoff = htobe32(ehdr.e_phoff); + ehdr.e_shoff = htobe32(ehdr.e_shoff); + } + else if(sizeof(ehdr.e_entry)==8) // 64-bit header + { + ehdr.e_entry = htobe64(ehdr.e_entry); + ehdr.e_phoff = htobe64(ehdr.e_phoff); + ehdr.e_shoff = htobe64(ehdr.e_shoff); + } + else + { + assert(0); + } + + } + else + { + assert(0); + } +} + +static inline uintptr_t page_round_down(uintptr_t x) +{ + return x & (~(PAGE_SIZE-1)); +} +static inline uintptr_t page_round_up(uintptr_t x) +{ + return ( (((uintptr_t)(x)) + PAGE_SIZE-1) & (~(PAGE_SIZE-1)) ); +} + +void ElfWriter::Write(const string &out_file, const string &infile) +{ + auto fin=fopen(infile.c_str(), "r"); + auto fout=fopen(out_file.c_str(), "w"); + assert(fin && fout); + + CreatePagemap(); + CreateSegmap(); + //SortSegmap(); + VirtualOffset_t min_addr=DetectMinAddr(); + VirtualOffset_t max_addr=DetectMaxAddr(); + + LoadEhdr(fin); + LoadPhdrs(fin); + CreateNewPhdrs(min_addr,max_addr); + + + WriteElf(fout); + + if( m_write_sections ) + AddSections(fout); + +} + +VirtualOffset_t ExeWriter::DetectMinAddr() +{ + auto firp=m_firp; + VirtualOffset_t min_addr=(*(firp->getDataScoops().begin()))->getStart()->getVirtualOffset(); + for(DataScoopSet_t::iterator it=firp->getDataScoops().begin(); it!=firp->getDataScoops().end(); ++it) + { + DataScoop_t* scoop=*it; + + if(scoop->getStart()->getVirtualOffset() < min_addr) + min_addr=scoop->getStart()->getVirtualOffset(); + + } + return min_addr; + +} + +VirtualOffset_t ExeWriter::DetectMaxAddr() +{ + auto firp=m_firp; + VirtualOffset_t max_addr=(*(firp->getDataScoops().begin()))->getEnd()->getVirtualOffset(); + for(DataScoopSet_t::iterator it=firp->getDataScoops().begin(); it!=firp->getDataScoops().end(); ++it) + { + DataScoop_t* scoop=*it; + + if(scoop->getEnd()->getVirtualOffset() > max_addr) + max_addr=scoop->getEnd()->getVirtualOffset(); + + } + return max_addr; + +} + +void ExeWriter::CreatePagemap() +{ + auto firp=m_firp; + +// for(DataScoopSet_t::iterator it=firp->getDataScoops().begin(); it!=firp->getDataScoops().end(); ++it) +// DataScoop_t* scoop=*it; +// + for(auto scoop : firp->getDataScoops()) + { + // tbss is an elf-byproduct that irdb doesn't entirely support. + // IRDB needs a better mechanism. + // To support this for now, we can just ignore it here. + if(scoop->getName()==".tbss") + continue; + + auto scoop_addr=scoop->getStart(); + auto start_addr=scoop_addr->getVirtualOffset(); + auto end_addr=scoop->getEnd()->getVirtualOffset(); + + // we'll deal with unpinned scoops later. + if(scoop_addr->getVirtualOffset()==0) + { + assert(0); // none for now? + continue; + } + + for(VirtualOffset_t i=page_align(start_addr); i<=end_addr; i+=PAGE_SIZE) + { + PageData_t &pagemap_i=pagemap[i]; + //cout<<"Writing scoop "<<scoop->getName()<<" to page: "<<hex<<i<<", perms="<<scoop->getRawPerms() + // << " start="<<hex<< scoop->getStart()->getVirtualOffset() + // << " end="<<hex<< scoop->getEnd()->getVirtualOffset() <<endl; + pagemap_i.union_permissions(scoop->getRawPerms()); + pagemap_i.is_relro |= scoop->isRelRo(); + for(int j=0;j<PAGE_SIZE;j++) + { + if(i+j < start_addr) + continue; + + if(i+j > end_addr) + continue; + + // get the data out of the scoop and put it into the page map. + VirtualOffset_t offset=i+j-start_addr; + if(offset<scoop->getContents().size()) + { + // cout<<"Updating page["<<hex<<i<<"+"<<j<<"("<<(i+j)<<")]="<<hex<<(int)scoop->getContents()[ offset ]<<endl; + pagemap_i.data[j]=scoop->getContents()[ offset ]; + pagemap_i.inuse[j]=true; + } + } + } + + } +} + +void ExeWriter::SortSegmap() +{ + // do one interation of a bubble sort to move the segement with the largest bss last. + for (unsigned int i=0; i<segvec.size()-1;i++) + { + int this_bss_size=segvec[i]->memsz-segvec[i]->filesz; + int next_bss_size=segvec[i+1]->memsz-segvec[i+1]->filesz; + + if(this_bss_size > next_bss_size) + { + swap(segvec[i],segvec[i+1]); + } + + } +} + +void ExeWriter::CreateSegmap() +{ + const auto should_bss_optimize= [&] (const PageData_t& perms) + { + return (perms.is_zero_initialized() && m_bss_opts); + }; + + + + // init some segment vars. + auto segstart=pagemap.begin()->first; + auto segperms=pagemap.begin()->second; + auto segend=segstart+PAGE_SIZE; + auto initend=segstart; + + const auto update_initend=[&](const PageData_t& perms) + { + if(should_bss_optimize(perms)) + initend=segstart; + else + initend=segend; + }; + + update_initend(segperms); + + auto it=pagemap.begin(); + ++it; // handled first one above. + + for( /* init'd above */; it!=pagemap.end(); ++it) + { + // grab page address and perms + const auto pagestart=it->first; + const auto &perms=it->second; + + + // if we switch perms, or skip a page + if( (perms.m_perms!=segperms.m_perms) || (segend!=pagestart)) + { + const auto seg=new LoadSegment_t(initend-segstart, segend-segstart, 0, segstart,segperms.m_perms); + segvec.push_back(seg); + + cout<<"Found segment "<<hex<<segstart<<"-"<<(segend-1)<<", perms="<<segperms.m_perms<<", memsz="<<seg->memsz<<", filesz="<<seg->filesz<<endl; + + segperms=perms; + segstart=pagestart; + segend=segstart+PAGE_SIZE; + + update_initend(perms); + + } + else + { + // else, same permission and next page, extend segment. + segend=pagestart+PAGE_SIZE; + if(! should_bss_optimize(perms) ) + initend=pagestart+PAGE_SIZE; + } + + } + + // make sure we print the last one + const auto seg=new LoadSegment_t(initend-segstart, segend-segstart, 0, segstart,segperms.m_perms); + segvec.push_back(seg); + + cout<<"Found segment "<<hex<<segstart<<"-"<<(segend-1)<<", perms="<<segperms.m_perms<<", memsz="<<seg->memsz<<", filesz="<<seg->filesz<<endl; + +} + + + + + + + +template <class T_Elf_Ehdr, class T_Elf_Phdr, class T_Elf_Addr, class T_Elf_Shdr, class T_Elf_Sym, class T_Elf_Rel, class T_Elf_Rela, class T_Elf_Dyn> +void ElfWriterImpl<T_Elf_Ehdr,T_Elf_Phdr,T_Elf_Addr, T_Elf_Shdr, T_Elf_Sym, T_Elf_Rel, T_Elf_Rela, T_Elf_Dyn>::LoadEhdr(FILE* fin) +{ + fseek(fin,0,SEEK_SET); + auto res=fread(&ehdr,sizeof(ehdr), 1, fin); + assert(res==1); + ehdr_to_host(ehdr); +}; + +template <class T_Elf_Ehdr, class T_Elf_Phdr, class T_Elf_Addr, class T_Elf_Shdr, class T_Elf_Sym, class T_Elf_Rel, class T_Elf_Rela, class T_Elf_Dyn> +void ElfWriterImpl<T_Elf_Ehdr,T_Elf_Phdr,T_Elf_Addr,T_Elf_Shdr, T_Elf_Sym, T_Elf_Rel, T_Elf_Rela, T_Elf_Dyn>::LoadPhdrs(FILE* fin) +{ + fseek(fin,ehdr.e_phoff,SEEK_SET); + phdrs.resize(ehdr.e_phnum); + for(unsigned int i=0;i<phdrs.size();i++) + { + auto res=fread(&phdrs[i], sizeof(phdrs[i]), 1, fin); + assert(res==1); + phdr_to_host(ehdr,phdrs[i]); + } +}; + +template <class T_Elf_Ehdr, class T_Elf_Phdr, class T_Elf_Addr, class T_Elf_Shdr, class T_Elf_Sym, class T_Elf_Rel, class T_Elf_Rela, class T_Elf_Dyn> +void ElfWriterImpl<T_Elf_Ehdr,T_Elf_Phdr,T_Elf_Addr,T_Elf_Shdr, T_Elf_Sym, T_Elf_Rel, T_Elf_Rela, T_Elf_Dyn>::CreateNewPhdrs( + const IRDB_SDK::VirtualOffset_t &min_addr, const IRDB_SDK::VirtualOffset_t &max_addr) +{ + + if(CreateNewPhdrs_GapAllocate(min_addr, max_addr)) + { + cout<<"ElfWriter Success with GapAllocate"<<endl; + return; + } + else if(CreateNewPhdrs_FirstPageAllocate(min_addr, max_addr)) + { + cout<<"ElfWriter Success with FirstPageAllocate"<<endl; + return; + } + else if(CreateNewPhdrs_PreAllocate(min_addr, max_addr)) + { + cout<<"ElfWriter Success with PreAllocate"<<endl; + return; + } + else if(CreateNewPhdrs_PostAllocate(min_addr, max_addr)) + { + cout<<"ElfWriter Success with PostAllocate"<<endl; + return; + } + else + { + cout<<"ElfWriter cannot find a place in the program for the PHDRS."<<endl; + assert(0); + } + +} + +template <class T_Elf_Ehdr, class T_Elf_Phdr, class T_Elf_Addr, class T_Elf_Shdr, class T_Elf_Sym, class T_Elf_Rel, class T_Elf_Rela, class T_Elf_Dyn> +bool ElfWriterImpl<T_Elf_Ehdr,T_Elf_Phdr,T_Elf_Addr,T_Elf_Shdr,T_Elf_Sym, T_Elf_Rel, T_Elf_Rela, T_Elf_Dyn>::CreateNewPhdrs_PostAllocate( + const IRDB_SDK::VirtualOffset_t &min_addr, const IRDB_SDK::VirtualOffset_t &max_addr) +{ + // post allocation not enabled, yet. + return false; +} + +template <class T_Elf_Ehdr, class T_Elf_Phdr, class T_Elf_Addr, class T_Elf_Shdr, class T_Elf_Sym, class T_Elf_Rel, class T_Elf_Rela, class T_Elf_Dyn> +bool ElfWriterImpl<T_Elf_Ehdr,T_Elf_Phdr,T_Elf_Addr,T_Elf_Shdr,T_Elf_Sym, T_Elf_Rel, T_Elf_Rela, T_Elf_Dyn>::CreateNewPhdrs_FirstPageAllocate( + const IRDB_SDK::VirtualOffset_t &min_addr, const IRDB_SDK::VirtualOffset_t &max_addr) +{ + // check to see if there's room on the first page for + unsigned int phdr_size=DetermineMaxPhdrSize(); + if(page_align(min_addr)+sizeof(T_Elf_Ehdr)+phdr_size > min_addr) + return false; + // this is an uncommon case -- we are typically adding + // segments and so the segment map won't fit on the first page. + // if this assertion hits, email jdhiser@gmail.com and attach your input pgm, + // then convert this to a return false to avoid assertion until he fixes it; + assert(0); +} + +template <class T_Elf_Ehdr, class T_Elf_Phdr, class T_Elf_Addr, class T_Elf_Shdr, class T_Elf_Sym, class T_Elf_Rel, class T_Elf_Rela, class T_Elf_Dyn> +bool ElfWriterImpl<T_Elf_Ehdr,T_Elf_Phdr,T_Elf_Addr, T_Elf_Shdr,T_Elf_Sym, T_Elf_Rel, T_Elf_Rela, T_Elf_Dyn>::readonly_space_at( + const IRDB_SDK::VirtualOffset_t addr, const unsigned int size) +{ + for(unsigned int i=0;i<size;i++) + { + IRDB_SDK::VirtualOffset_t page=page_align(addr+i); + IRDB_SDK::VirtualOffset_t page_offset=addr+i-page; + + // page not allocated yet, go ahead and call this byte free. + if(pagemap.find(page) == pagemap.end()) + continue; + + if(pagemap.at(page).inuse[page_offset]) + return false; + + // check that the page is not writable. 4=r, 2=w, 1=x. + if((pagemap.at(page).m_perms & 0x2) != 0) + return false; + } + return true; +} + +template <class T_Elf_Ehdr, class T_Elf_Phdr, class T_Elf_Addr, class T_Elf_Shdr, class T_Elf_Sym, class T_Elf_Rel, class T_Elf_Rela, class T_Elf_Dyn> +int ElfWriterImpl<T_Elf_Ehdr,T_Elf_Phdr,T_Elf_Addr,T_Elf_Shdr,T_Elf_Sym, T_Elf_Rel, T_Elf_Rela, T_Elf_Dyn>::locate_segment_index(const IRDB_SDK::VirtualOffset_t addr) +{ + // segment's are sorted by address. + for(unsigned int i=0;i<segvec.size();i++) + { + // if the filesz diverges from the memsz, then we have an issue. + // luckily, this only happens for bss, and there's almost always space + // after the text for the new phdr. + if(segvec[i]->filesz < segvec[i]->memsz) + return -1; + + // if the new_phdr_addr is in this segment. + if(segvec[i]->start_page <= addr && addr < segvec[i]->start_page + segvec[i]->filesz) + { + return i; + } + } + return -1; +} + +template <class T_Elf_Ehdr, class T_Elf_Phdr, class T_Elf_Addr, class T_Elf_Shdr, class T_Elf_Sym, class T_Elf_Rel, class T_Elf_Rela, class T_Elf_Dyn> +unsigned int ElfWriterImpl<T_Elf_Ehdr,T_Elf_Phdr,T_Elf_Addr,T_Elf_Shdr,T_Elf_Sym, T_Elf_Rel, T_Elf_Rela, T_Elf_Dyn>::count_filesz_to_seg(unsigned int seg) +{ + unsigned int filesz=0; + // segment's are sorted by address. + for(unsigned int i=0;i<seg;i++) + { + filesz+=segvec[i]->filesz; + } + return filesz; +} + +template <class T_Elf_Ehdr, class T_Elf_Phdr, class T_Elf_Addr, class T_Elf_Shdr, class T_Elf_Sym, class T_Elf_Rel, class T_Elf_Rela, class T_Elf_Dyn> +bool ElfWriterImpl<T_Elf_Ehdr,T_Elf_Phdr,T_Elf_Addr,T_Elf_Shdr,T_Elf_Sym, T_Elf_Rel, T_Elf_Rela, T_Elf_Dyn>::CreateNewPhdrs_GapAllocate( + const IRDB_SDK::VirtualOffset_t &min_addr, const IRDB_SDK::VirtualOffset_t &max_addr) +{ + /* for shared objects, we need the PHDR file offset to be equal to the + * memory offset because the kernel passes base_address+Ehdr::ph_off via + * the auxv array to ld.so. Where ld.so then uses that as an address. + */ + + // gap allocate assumes there's space on the first page for the EHdrs. If there's not, + // try pre-allocating. + if(page_align(min_addr)+sizeof(T_Elf_Ehdr) >= min_addr) + return false; + + // first, find the first free space that's big enough. + unsigned int phdr_size=DetermineMaxPhdrSize(); + IRDB_SDK::VirtualOffset_t new_phdr_addr=0; + for(unsigned int i=min_addr;i<max_addr; i++) + { + if(readonly_space_at(i,phdr_size)) + { + new_phdr_addr=i; + break; + } + + } + + // find segment + int new_phdr_segment_index=locate_segment_index(new_phdr_addr-1); + + // if there's no segment for the start, we'll have to allocate a page anyhow. just use the _Preallocate routine. + if(new_phdr_segment_index==-1) + return false; + + + // we've seen multi-page phdrs with NogOF (non-overlapping globals with overflow protection) + // and PreAllocate fails on PIE exe's and .so's, so let's try a bit harder to GapAllocate. + // a multiple-page phdr can't be gap allocated. + //if(phdr_size>=PAGE_SIZE) + // return false; + + // verify that the segment can be extended. + int pages_to_extend=0; // extend the segment by a page. + for(unsigned int i=0;i<phdr_size; i++) + { + IRDB_SDK::VirtualOffset_t this_addr=new_phdr_addr+i; + IRDB_SDK::VirtualOffset_t this_page=page_align(this_addr); + // find segment for phdr+i + int seg=locate_segment_index(this_addr); + + // if it's not allocated, we ran off the end of the segment. + // if we're also at the first byte of a page, extend the segment by a page. + if(seg==-1 && this_page==this_addr ) + pages_to_extend++; + + // this should be safe because the new page can't be in a segment already. + if(seg==-1) + continue; + + if(seg == new_phdr_segment_index) + continue; + // uh oh, we found that the phdr would cross into the next segment + // but, we know there's enough space in the next segment because + // 1) read_only_space_at returned true, so there's enough free space. + // 2) the free space cannot transcend the entire next segment, because that would mean the entire + // segment was empty (implying the segment is redundant). + // Thus, we can just stop looking here and extend the previous segment to abut the new segment. + // and the phdrs will span the two segments. + for(auto j=i+1;j<phdr_size; j++) + assert(seg==locate_segment_index(new_phdr_addr+j)); + break; + } + + // if we get here, we've found a spot for the PHDR. + + // mark the bytes of the pages as readable, and in-use. + for(unsigned int i=0;i<phdr_size; i++) + { + IRDB_SDK::VirtualOffset_t this_addr=new_phdr_addr+i; + IRDB_SDK::VirtualOffset_t this_page=page_align(this_addr); + IRDB_SDK::VirtualOffset_t this_offset=this_addr-this_page; + pagemap[this_page].inuse[this_offset]=true; + pagemap[this_page].union_permissions(0x4); // add read permission + } + segvec[new_phdr_segment_index]->filesz+=(PAGE_SIZE*pages_to_extend); + segvec[new_phdr_segment_index]->memsz+=(PAGE_SIZE*pages_to_extend); + + unsigned int fileoff=count_filesz_to_seg(new_phdr_segment_index); + fileoff+=(new_phdr_addr-segvec[new_phdr_segment_index]->start_page); + + return CreateNewPhdrs_internal(min_addr,max_addr,0x0,false,fileoff, new_phdr_addr); + +} + +template <class T_Elf_Ehdr, class T_Elf_Phdr, class T_Elf_Addr, class T_Elf_Shdr, class T_Elf_Sym, class T_Elf_Rel, class T_Elf_Rela, class T_Elf_Dyn> +bool ElfWriterImpl<T_Elf_Ehdr,T_Elf_Phdr,T_Elf_Addr,T_Elf_Shdr,T_Elf_Sym, T_Elf_Rel, T_Elf_Rela, T_Elf_Dyn>::CreateNewPhdrs_PreAllocate( + const IRDB_SDK::VirtualOffset_t &min_addr, const IRDB_SDK::VirtualOffset_t &max_addr) +{ + auto phdr_size=DetermineMaxPhdrSize(); + auto aligned_phdr_size=page_round_up(phdr_size); + auto total_header_size=phdr_size+sizeof(T_Elf_Ehdr); + //auto aligned_min_addr=page_align(min_addr); + + + /* check to see if it will fit in the address space above the first pinned address */ + if(total_header_size > min_addr) + return false; + + IRDB_SDK::VirtualOffset_t new_phdr_addr=(T_Elf_Addr)page_align(min_addr)-PAGE_SIZE+sizeof(T_Elf_Ehdr); + return CreateNewPhdrs_internal(min_addr,max_addr,aligned_phdr_size,true, sizeof(T_Elf_Ehdr), new_phdr_addr); +} + +template <class T_Elf_Ehdr, class T_Elf_Phdr, class T_Elf_Addr, class T_Elf_Shdr, class T_Elf_Sym, class T_Elf_Rel, class T_Elf_Rela, class T_Elf_Dyn> +DataScoop_t* ElfWriterImpl<T_Elf_Ehdr,T_Elf_Phdr,T_Elf_Addr,T_Elf_Shdr,T_Elf_Sym, T_Elf_Rel, T_Elf_Rela, T_Elf_Dyn>::find_scoop_by_name(const string& name, FileIR_t* firp) +{ + for(DataScoopSet_t::iterator it=firp->getDataScoops().begin(); it!=firp->getDataScoops().end(); ++it) + { + DataScoop_t* scoop=*it; + if(scoop->getName()==name) + return scoop; + } + + return nullptr; +} + +template <class T_Elf_Ehdr, class T_Elf_Phdr, class T_Elf_Addr, class T_Elf_Shdr, class T_Elf_Sym, class T_Elf_Rel, class T_Elf_Rela, class T_Elf_Dyn> +void ElfWriterImpl<T_Elf_Ehdr,T_Elf_Phdr,T_Elf_Addr,T_Elf_Shdr,T_Elf_Sym, T_Elf_Rel, T_Elf_Rela, T_Elf_Dyn>::update_phdr_for_scoop_sections(FileIR_t* firp) +{ + // look at each header. + for(unsigned i=0;i<new_phdrs.size(); i++) + { + + // this struct is a table/constant for mapping PT_names to section names. + struct pt_type_to_sec_name_t + { + unsigned int pt_type; + const char* sec_name; + } pt_type_to_sec_name[] = + { + {PT_INTERP, ".interp"}, + {PT_DYNAMIC, ".dynamic"}, + {PT_NOTE, ".note.ABI-tag"}, + {PT_GNU_EH_FRAME, ".eh_frame_hdr"} + }; + + // check if a type of header listed above. + for(unsigned k=0;k<(sizeof(pt_type_to_sec_name)/sizeof(pt_type_to_sec_name_t)); k++) + { + // check if a type of header listed above. + if(new_phdrs[i].p_type==pt_type_to_sec_name[k].pt_type) + { + // grab the name from the const table.. + // and find the scoop + DataScoop_t* scoop=find_scoop_by_name(pt_type_to_sec_name[k].sec_name, firp); + + if(scoop) + { + new_phdrs[i].p_vaddr=scoop->getStart()->getVirtualOffset(); + new_phdrs[i].p_paddr=scoop->getStart()->getVirtualOffset(); + new_phdrs[i].p_filesz= scoop->getEnd()->getVirtualOffset() - scoop->getStart()->getVirtualOffset() + 1; + new_phdrs[i].p_memsz = scoop->getEnd()->getVirtualOffset() - scoop->getStart()->getVirtualOffset() + 1; + + new_phdrs[i].p_offset=0; + for(unsigned j=0;j<new_phdrs.size(); j++) + { + if( new_phdrs[j].p_vaddr<= new_phdrs[i].p_vaddr && + new_phdrs[i].p_vaddr < new_phdrs[j].p_vaddr+new_phdrs[j].p_filesz) + { + new_phdrs[i].p_offset=new_phdrs[j].p_offset + new_phdrs[i].p_vaddr - new_phdrs[j].p_vaddr; + } + } + assert(new_phdrs[i].p_offset!=0); + } + } + } + } + return; +} +template <class T_Elf_Ehdr, class T_Elf_Phdr, class T_Elf_Addr, class T_Elf_Shdr, class T_Elf_Sym, class T_Elf_Rel, class T_Elf_Rela, class T_Elf_Dyn> +void ElfWriterImpl<T_Elf_Ehdr,T_Elf_Phdr,T_Elf_Addr,T_Elf_Shdr,T_Elf_Sym, T_Elf_Rel, T_Elf_Rela, T_Elf_Dyn>::trim_last_segment_filesz(FileIR_t* firp) +{ + // this seems OK and is necessary on centos... but on ubuntu, it's a no-go (segfault from loader). + // for now, disabling this optimization until we can figure out what's up. + +#if 0 + // skip trimming if we aren't doing bss optimization. + if(!m_bss_opts) return; + + + // find the last pt load segment header. + auto seg_to_trim=-1; + for(auto i=0u;i<new_phdrs.size(); i++) + { + if(new_phdrs[i].p_type==PT_LOAD) + { + seg_to_trim=i; + } + + } + assert(seg_to_trim!=-1); + + + + + const auto seg_start_addr=new_phdrs[seg_to_trim].p_vaddr; + const auto seg_filesz=new_phdrs[seg_to_trim].p_filesz; + const auto seg_end_addr=seg_start_addr+seg_filesz-1; + const auto seg_end_page_start=page_round_down(seg_end_addr); + const auto &page=pagemap[seg_end_page_start]; + + // don't write 0's at end of last page + auto k=PAGE_SIZE-1; + for (/* above */; k>=0; k--) + { + if(page.data[k]!=0) + break; + } + const auto last_nonzero_byte_in_seg = k; + (void)last_nonzero_byte_in_seg; + + const auto bytes_to_trim = 1 (PAGE_SIZE - 1) - last_nonzero_byte_in_seg; + + // lastly, update the filesz so we don't need the reste of those bytes. + // memsz is allowed to be bigger than filesz. + new_phdrs[seg_to_trim].p_filesz -= (bytes_to_trim); + + return; +#endif + +} + + + + +template <class T_Elf_Ehdr, class T_Elf_Phdr, class T_Elf_Addr, class T_Elf_Shdr, class T_Elf_Sym, class T_Elf_Rel, class T_Elf_Rela, class T_Elf_Dyn> +bool ElfWriterImpl<T_Elf_Ehdr,T_Elf_Phdr,T_Elf_Addr,T_Elf_Shdr,T_Elf_Sym, T_Elf_Rel, T_Elf_Rela, T_Elf_Dyn>::CreateNewPhdrs_internal( + const IRDB_SDK::VirtualOffset_t &min_addr, + const IRDB_SDK::VirtualOffset_t &max_addr, + const int &first_seg_file_offset, + const bool &add_pt_load_for_phdr, + const size_t phdr_map_offset, + IRDB_SDK::VirtualOffset_t new_phdr_addr + ) +{ + + + cout<<"Assigning phdr to address "<<hex<<new_phdr_addr<<endl; + cout<<"Assigning first seg to a file offset that's at least: "<<hex<<first_seg_file_offset<<endl; + + // create a load segments into the new header list. + // assume hdr are on first page. + unsigned int fileoff=first_seg_file_offset; + for(unsigned int i=0;i<segvec.size();i++) + { + T_Elf_Phdr thisphdr; + memset(&thisphdr,0,sizeof(thisphdr)); + thisphdr.p_type = PT_LOAD; + thisphdr.p_flags = (ELFIO::Elf_Word)segvec[i]->m_perms; + thisphdr.p_offset = fileoff; + cout<<"Assigning load["<<dec<<i<<"].ph_offset="<<hex<<fileoff<<endl; + thisphdr.p_vaddr = (T_Elf_Addr)segvec[i]->start_page; + thisphdr.p_paddr = (T_Elf_Addr)segvec[i]->start_page; + thisphdr.p_filesz = (ELFIO::Elf_Xword)segvec[i]->filesz; + thisphdr.p_memsz = (ELFIO::Elf_Xword)segvec[i]->memsz; + thisphdr.p_align= 0x1000; + + new_phdrs.push_back(thisphdr); + + // advance file pointer. + fileoff+=segvec[i]->filesz; + + } + + + // go through orig. phdrs any copy and that aren't of a type we are re-createing. + for(unsigned int i=0;i<phdrs.size();i++) + { + // skip any load headers, the irdb tells us what to load. + if(phdrs[i].p_type == PT_LOAD) + continue; + + // skip phdr header. + if(phdrs[i].p_type == PT_PHDR) + continue; + + // skip RELRO header, we're relocating stuff and wil have to create 1 or more. + if(phdrs[i].p_type == PT_GNU_RELRO) + continue; + + T_Elf_Phdr newphdr=phdrs[i]; + +// figure out how to make this an xform/step in $PS. +// instead of always doing it. +#if 0 + if(phdrs[i].p_type == PT_GNU_STACK) + newphdr.p_flags &= ~PF_X; // turn off executable stack. +#endif + + // find offset in loadable segment + // using segvec.size() instead of new_phdrs size to search for segments in the new list. + for(unsigned int j=0;j<segvec.size();j++) + { + if(new_phdrs[j].p_vaddr <= newphdr.p_vaddr && newphdr.p_vaddr <= new_phdrs[j].p_vaddr+new_phdrs[j].p_filesz) + { + newphdr.p_offset=new_phdrs[j].p_offset+(newphdr.p_vaddr - new_phdrs[j].p_vaddr); + break; + } + } + + // update offset + new_phdrs.push_back(newphdr); + } + + if(add_pt_load_for_phdr) + { + const T_Elf_Addr min_phdr_size=(new_phdrs.size()+1 ) * sizeof(T_Elf_Phdr); + const T_Elf_Addr phdr_size=page_round_up(min_phdr_size); + + // specify a load section for the new program's header and phdr + T_Elf_Phdr newheaderphdr; + memset(&newheaderphdr,0,sizeof(newheaderphdr)); + newheaderphdr.p_type = PT_LOAD; + newheaderphdr.p_flags =(ELFIO::Elf_Word)4; + newheaderphdr.p_offset =0; + newheaderphdr.p_vaddr =(T_Elf_Addr)page_align(new_phdr_addr); + newheaderphdr.p_paddr =(T_Elf_Addr)page_align(new_phdr_addr); + newheaderphdr.p_filesz =(ELFIO::Elf_Xword)phdr_size; + newheaderphdr.p_memsz =(ELFIO::Elf_Xword)phdr_size; + newheaderphdr.p_align =0x1000; + new_phdrs.insert(new_phdrs.begin(),newheaderphdr); + + auto size=newheaderphdr.p_vaddr+newheaderphdr.p_memsz; + auto start_addr=newheaderphdr.p_vaddr; + for(VirtualOffset_t i=page_align(newheaderphdr.p_vaddr); i<newheaderphdr.p_vaddr+newheaderphdr.p_memsz; i+=PAGE_SIZE) + { + cout<<"Updating pagemap for new phdr. To page: "<<hex<<i<<", perms="<<newheaderphdr.p_flags + << " start="<<hex<< newheaderphdr.p_vaddr + << " end="<<hex<< size << endl; + pagemap[i].union_permissions(newheaderphdr.p_vaddr+newheaderphdr.p_memsz); + pagemap[i].is_relro |= false; + for(int j=0;j<PAGE_SIZE;j++) + { + // get the data out of the scoop and put it into the page map. + if(start_addr <= i+j && i+j < start_addr + size) + { + pagemap[i].data[j]=0xf4; // don't update, phdrs are written separately. we just need space + // in the map. + pagemap[i].inuse[j]=true; + } + } + } + + } + + // create the new phdr phdr + cout<<"New phdrs at: "<<hex<<new_phdr_addr<<endl; + T_Elf_Phdr newphdrphdr; + memset(&newphdrphdr,0,sizeof(newphdrphdr)); + newphdrphdr.p_type = PT_PHDR; + newphdrphdr.p_flags =(ELFIO::Elf_Word)4; + newphdrphdr.p_offset =phdr_map_offset; + newphdrphdr.p_vaddr = new_phdr_addr; + newphdrphdr.p_paddr = new_phdr_addr; + newphdrphdr.p_filesz =(ELFIO::Elf_Xword)(new_phdrs.size()+1)*getSegHeaderSize(); + newphdrphdr.p_memsz =(ELFIO::Elf_Xword)(new_phdrs.size()+1)*getSegHeaderSize(); + newphdrphdr.p_align =0x1000; + new_phdrs.insert(new_phdrs.begin(),newphdrphdr); + + + vector<T_Elf_Phdr> relro_phdrs; + new_phdrs.insert(new_phdrs.end(), relro_phdrs.begin(), relro_phdrs.end()); + + // record the new ehdr. + new_ehdr=ehdr; + new_ehdr.e_phoff=phdr_map_offset; + new_ehdr.e_shoff=0; + new_ehdr.e_shnum=0; + new_ehdr.e_phnum=new_phdrs.size(); + new_ehdr.e_shstrndx=0; + + update_phdr_for_scoop_sections(m_firp); + trim_last_segment_filesz(m_firp); + + return true; +} + +template <class T_Elf_Ehdr, class T_Elf_Phdr, class T_Elf_Addr, class T_Elf_Shdr, class T_Elf_Sym, class T_Elf_Rel, class T_Elf_Rela, class T_Elf_Dyn> +void ElfWriterImpl<T_Elf_Ehdr,T_Elf_Phdr,T_Elf_Addr,T_Elf_Shdr,T_Elf_Sym, T_Elf_Rel, T_Elf_Rela, T_Elf_Dyn>::WriteElf(FILE* fout) +{ + assert(fout); + + // write the segments first, as they may overlap the ehdr and phdrs + // and we maintain those separately. + + // look at enach phdr entry, and write data as it says to. + unsigned int loadcnt=0; + for(unsigned int i=0;i<new_phdrs.size();i++) + { + if(new_phdrs[i].p_type==PT_LOAD) + { + loadcnt++; + + cout<<"phdr["<<dec<<loadcnt<<"] writing at:"<<hex<<new_phdrs[i].p_offset<<endl; + fseek(fout,new_phdrs[i].p_offset,SEEK_SET); + for(unsigned int j=0;j<new_phdrs[i].p_filesz;j+=PAGE_SIZE) + { + const PageData_t &page=pagemap[new_phdrs[i].p_vaddr+j]; + + // is it the last page? + if(j+PAGE_SIZE < new_phdrs[i].p_filesz || m_write_sections ) + { + fwrite(page.data.data(), PAGE_SIZE, 1, fout); + } + else + { +#if 0 +note: this does not work on centos as it can leave the segments +start address + filesize to be a number greater than the entire files size. +This causes the loader to complain. The "right" way to do this is to update the +filesz before we start writing out the elf. See "trim_last_seg_filesz" + // don't write 0's at end of last page + int k=0; + for (k=PAGE_SIZE-1;k>=0;k--) + { + if(page.data[k]!=0) + break; + } + cout<<"phdr["<<dec<<loadcnt<<"] optimizing last page write to size k="<<hex<<k<<endl; +#endif + const auto end_offset=new_phdrs[i].p_filesz; + const auto current_offset = j; + const auto bytes_to_write = end_offset - current_offset; + assert(bytes_to_write <= PAGE_SIZE); + fwrite(page.data.data(), bytes_to_write, 1, fout); + } + + } + } + } + + // write the header. + fseek(fout,0,SEEK_SET); + auto new_ehdr_endian_correct = new_ehdr; + host_to_ehdr(new_ehdr_endian_correct); + fwrite(&new_ehdr_endian_correct, sizeof(new_ehdr), 1, fout); + // write the phdrs, which may be part of a segment written above. + cout << "Writing segment headers at " << hex << new_ehdr.e_phoff << ", size=" << new_phdrs.size()*sizeof(new_phdrs[0]) << endl; + fseek(fout,new_ehdr.e_phoff,SEEK_SET); + + // doesn't account for endian issues +// fwrite(new_phdrs.data(), sizeof(new_phdrs[0]), new_phdrs.size(), fout); + for(auto phdr : new_phdrs) + { + host_to_phdr(ehdr,phdr); + fwrite(&phdr, sizeof(new_phdrs[0]), 1, fout); + } +} + +template <class T_Elf_Ehdr, class T_Elf_Phdr, class T_Elf_Addr, class T_Elf_Shdr, class T_Elf_Sym, class T_Elf_Rel, class T_Elf_Rela, class T_Elf_Dyn> +unsigned int ElfWriterImpl<T_Elf_Ehdr,T_Elf_Phdr,T_Elf_Addr,T_Elf_Shdr,T_Elf_Sym, T_Elf_Rel, T_Elf_Rela, T_Elf_Dyn>::DetermineMaxPhdrSize() +{ + unsigned int phdr_count=0; + /* count phdr's that aren't pt_load or pt_phdr */ + for(unsigned int i=0;i<phdrs.size();i++) + { + // skip any load sections, the irdb tells us what to load. + if(phdrs[i].p_type == PT_LOAD) + continue; + + // skip phdr section. + if(phdrs[i].p_type == PT_PHDR) + continue; + + phdr_count++; + } + + // add a phdr for each segment that needs a mapping + phdr_count+=segvec.size()*2; // each entry in the segvec may need a relro header. + + // add 2 more sections that 1) of type PT_PHDR, and 2) a possible PT_LOAD section to load the phdr. + // worst case is we allocate an entire new page for it. + phdr_count+=2; + + return phdr_count*sizeof(T_Elf_Phdr); +} + +template <class T_Elf_Ehdr, class T_Elf_Phdr, class T_Elf_Addr, class T_Elf_Shdr, class T_Elf_Sym, class T_Elf_Rel, class T_Elf_Rela, class T_Elf_Dyn> +void ElfWriterImpl<T_Elf_Ehdr,T_Elf_Phdr,T_Elf_Addr,T_Elf_Shdr,T_Elf_Sym, T_Elf_Rel, T_Elf_Rela, T_Elf_Dyn>::AddSections(FILE* fout) +{ + fseek(fout,0,SEEK_END); + long cur_file_pos=ftell(fout); + + + StringTable_t strtab; + map<DataScoop_t*,size_t> file_positions; + vector<T_Elf_Shdr> shdrs; + + // add each scoop name to the string table + // for_each(m_firp->getDataScoops().begin(), m_firp->getDataScoops().end(), [&](DataScoop_t* scoop) + for(auto scoop : m_firp->getDataScoops()) + { + strtab.AddString(scoop->getName()); + }; + + + string zipr_symtab=".scoop_symtab"; + strtab.AddString(zipr_symtab); + string null_symtab="nullptr"; + strtab.AddString(null_symtab); + + + // locate a file offset for each scoop by examining the output phdrs. + // for_each(m_firp->getDataScoops().begin(), m_firp->getDataScoops().end(), [&](DataScoop_t* scoop) + for(auto scoop : m_firp->getDataScoops()) + { + auto finder=find_if(new_phdrs.begin(), new_phdrs.end(), [scoop](const T_Elf_Phdr& phdr) + { + return (phdr.p_vaddr <= scoop->getStart()->getVirtualOffset() && + scoop->getStart()->getVirtualOffset() < phdr.p_vaddr+phdr.p_memsz); + }); + // assert we found it. + assert(finder!=new_phdrs.end()); + const T_Elf_Phdr& phdr=*finder; + + size_t filepos=phdr.p_offset + (scoop->getStart()->getVirtualOffset()-phdr.p_vaddr); + file_positions[scoop]=filepos; + }; + + T_Elf_Shdr null_shdr; + memset(&null_shdr,0,sizeof(null_shdr)); + null_shdr.sh_type=SHT_NULL; + null_shdr. sh_name =strtab.location(null_symtab); + + shdrs.push_back(null_shdr); + + struct section_type_map_t + { + string name; + unsigned int type; + unsigned int sh_ent_size; + string link; + } section_type_map[]={ + {".init_array", SHT_INIT_ARRAY, 0, "" }, + {".fini_array", SHT_FINI_ARRAY, 0, "" }, + {".dynamic", SHT_DYNAMIC, sizeof(T_Elf_Dyn), ".dynstr"}, + {".note.ABI-tag", SHT_NOTE, 0, ""}, + {".note.gnu.build-id", SHT_NOTE, 0, ""}, + {".gnu.hash", SHT_GNU_HASH, 0, ".dynsym"}, + {".dynsym", SHT_DYNSYM, sizeof(T_Elf_Sym), ".dynstr"}, + {".dynstr", SHT_STRTAB, 0, ""}, + {".shstrtab", SHT_STRTAB, 0, ""}, + {".symtab", SHT_SYMTAB, sizeof(T_Elf_Sym), ""}, + {".strtab", SHT_STRTAB, 0, ""}, + {".rel.dyn", SHT_REL, sizeof(T_Elf_Rel), ""}, + {".rela.dyn", SHT_RELA, sizeof(T_Elf_Rela), ".dynsym"}, + {".rel.plt", SHT_REL, sizeof(T_Elf_Rel), ".dynsym"}, + {".rela.plt", SHT_RELA, sizeof(T_Elf_Rela), ".dynsym"}, + {".gnu.version", SHT_GNU_versym, 2, ".dynsym"}, + {".gnu.version_r",SHT_GNU_verneed, 0, ".dynstr"}, + {".rela.dyn coalesced w/.rela.plt", SHT_RELA, sizeof(T_Elf_Rela), ".dynsym"} + }; + + + // for each scoop, pushback an shdr + // for_each(m_firp->getDataScoops().begin(), m_firp->getDataScoops().end(), [&](DataScoop_t* scoop) + for(auto scoop : m_firp->getDataScoops()) + { + + T_Elf_Shdr shdr; + shdr. sh_name =strtab.location(scoop->getName()); + + auto it=find_if(begin(section_type_map), end(section_type_map), [&scoop](const section_type_map_t &sm) + { + return scoop->getName()==sm.name; + }); + if(end(section_type_map) != it) + { + cout<<"Setting ent-size for "<<scoop->getName()<<" to "<<dec<<it->sh_ent_size<<endl; + shdr. sh_type = it->type; // sht_progbits, sht, sht_strtab, sht_symtab, ... + shdr. sh_entsize = it->sh_ent_size; + } + else + { + shdr. sh_type = SHT_PROGBITS; // sht_progbits, sht, sht_strtab, sht_symtab, ... + shdr. sh_entsize = 0; + } + shdr. sh_flags = SHF_ALLOC; // scoop->getRawPerms(); + if(scoop->isExecuteable()) + shdr. sh_flags |= SHF_EXECINSTR; + if(scoop->isWriteable()) + shdr. sh_flags |= SHF_WRITE; + shdr. sh_addr = scoop->getStart()->getVirtualOffset(); + shdr. sh_offset =file_positions[scoop]; + shdr. sh_size = scoop->getEnd()->getVirtualOffset() - scoop->getStart()->getVirtualOffset() + 1; + shdr. sh_link = SHN_UNDEF; + shdr. sh_info = 0 ; + shdr. sh_addralign= 0 ; // scoop->getAlign(); doesn't exist? + + shdrs.push_back(shdr); + }; + auto scoop_it=m_firp->getDataScoops().begin(); + for(unsigned int i=1; i<shdrs.size(); i++) // skip null shdr + { + T_Elf_Shdr & shdr = shdrs[i]; + auto map_it=find_if(begin(section_type_map), end(section_type_map), [&scoop_it](const section_type_map_t &sm) + { + return (*scoop_it)->getName()==sm.name; + }); + if(end(section_type_map) != map_it && map_it->link!="") + { + auto link_it=m_firp->getDataScoops().begin(); + for(unsigned int j=1; j<shdrs.size(); j++) // skip null shdr + { + if((*link_it)->getName() == map_it->link) + { + shdr.sh_link=j; + break; + } + link_it++; + } + } + scoop_it++; + } + + T_Elf_Shdr symtab_shdr; + symtab_shdr. sh_name =strtab.location(zipr_symtab); + symtab_shdr. sh_type = SHT_STRTAB; + symtab_shdr. sh_flags = 0; + symtab_shdr. sh_addr = 0; + symtab_shdr. sh_offset = cur_file_pos; + symtab_shdr. sh_size = strtab.size(); + symtab_shdr. sh_link = SHN_UNDEF; + symtab_shdr. sh_info = 0; + symtab_shdr. sh_addralign=0; + symtab_shdr. sh_entsize =0; + shdrs.push_back(symtab_shdr); + + cout<<"Writing strtab at filepos="<<hex<<cur_file_pos<<endl; + strtab.Write(fout); + + long shdr_file_pos=ftell(fout); + + cout<<"Writing section headers at filepos="<<hex<<shdr_file_pos<<endl; + // doesn't account for endianness +// fwrite(shdrs.data(), sizeof(T_Elf_Shdr), shdrs.size(), fout); + for(auto shdr : shdrs) + { + host_to_shdr(ehdr,shdr); + fwrite(&shdr, sizeof(T_Elf_Shdr), 1, fout); + + } + + new_ehdr.e_shentsize=sizeof(T_Elf_Shdr); + new_ehdr.e_shnum=shdrs.size(); + new_ehdr.e_shstrndx=shdrs.size()-1; // symtab was added last. + new_ehdr.e_shoff=shdr_file_pos; + + // rewrite the file header so that sections are listed. + fseek(fout,0,SEEK_SET); + auto endian_ehdr = new_ehdr; + host_to_ehdr(endian_ehdr); + fwrite(&endian_ehdr, sizeof(new_ehdr),1,fout); +} + +// explicit instantation of methods for 32- and 64-bit classes. +template class ElfWriterImpl<ELFIO::Elf64_Ehdr, ELFIO::Elf64_Phdr, ELFIO::Elf64_Addr, ELFIO::Elf64_Shdr, ELFIO::Elf64_Sym, ELFIO::Elf64_Rel, ELFIO::Elf64_Rela, ELFIO::Elf64_Dyn>; +template class ElfWriterImpl<ELFIO::Elf32_Ehdr, ELFIO::Elf32_Phdr, ELFIO::Elf32_Addr, ELFIO::Elf32_Shdr, ELFIO::Elf32_Sym, ELFIO::Elf32_Rel, ELFIO::Elf32_Rela, ELFIO::Elf32_Dyn>; + diff --git a/zipr/src/main.cpp b/zipr/src/main.cpp new file mode 100644 index 0000000000000000000000000000000000000000..7d5385f85392504df24eb909c378cc97bfd22026 --- /dev/null +++ b/zipr/src/main.cpp @@ -0,0 +1,53 @@ +/*************************************************************************** + * Copyright (c) 2014 Zephyr Software LLC. All rights reserved. + * + * This software is furnished under a license and/or other restrictive + * terms and may be used and copied only in accordance with such terms + * and the inclusion of the above copyright notice. This software or + * any other copies thereof may not be provided or otherwise made + * available to any other person without the express written consent + * of an authorized representative of Zephyr Software LLC. Title to, + * ownership of, and all rights in the software is retained by + * Zephyr Software LCC. + * + * Zephyr Software LLC. Proprietary Information + * + * Unless otherwise specified, the information contained in this + * directory, following this legend, and/or referenced herein is + * Zephyr Software LLC. (Zephyr) Proprietary Information. + * + * CONTACT + * + * For technical assistance, contact Zephyr Software LCC. at: + * + * + * Zephyr Software, LLC + * 2040 Tremont Rd + * Charlottesville, VA 22911 + * + * E-mail: jwd@zephyr-software.com + **************************************************************************/ + +#include <iostream> +#include <stdlib.h> +#include <string.h> +#include <map> +#include <assert.h> +#include <sys/mman.h> +#include <ctype.h> + + +#include <zipr_all.h> + + +using namespace zipr; +using namespace IRDB_SDK; +using namespace std; + +int main(int argc, char* argv[]) +{ + ZiprImpl_t zip(argc, argv); + + if (!zip.Error()) + zip.CreateBinaryFile(); +} diff --git a/zipr/src/memory_space.cpp b/zipr/src/memory_space.cpp new file mode 100644 index 0000000000000000000000000000000000000000..3c8f0a7229a476138b2c842c5a08841bad91bc94 --- /dev/null +++ b/zipr/src/memory_space.cpp @@ -0,0 +1,309 @@ +/* + * Copyright (c) 2014 - Zephyr Software LLC + * + * This file may be used and modified for non-commercial purposes as long as + * all copyright, permission, and nonwarranty notices are preserved. + * Redistribution is prohibited without prior written consent from Zephyr + * Software. + * + * Please contact the authors for restrictions applying to commercial use. + * + * THIS SOURCE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF + * MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * Author: Zephyr Software + * e-mail: jwd@zephyr-software.com + * URL : http://www.zephyr-software.com/ + * + */ + +#include <zipr_all.h> +#include <unistd.h> +#include <stdlib.h> +#include <getopt.h> +#include <algorithm> // std::random_shuffle +#include <vector> // std::vector +#include <ctime> // std::time +#include <cstdlib> // std::rand, std::srand + +using namespace zipr; +using namespace std; + +void ZiprMemorySpace_t::registerOptions(ZiprOptions_t* opt_man) +{ + auto global=opt_man->getNamespace("global"); + m_verbose=global->getBooleanOption("verbose"); + return; +} + +void ZiprMemorySpace_t::SplitFreeRange(Range_t split_from) +{ + RangeAddress_t counter, end; + for (counter = split_from.getStart(), end = split_from.getEnd(); + counter!=end; + counter++) + { + SplitFreeRange(counter); + } +} + +void ZiprMemorySpace_t::SplitFreeRange(RangeAddress_t addr) +{ + const auto it=FindFreeRange(addr); + assert(IsValidRange(it)); + + const auto r=*it; + if(r.getStart()==r.getEnd()) + { + assert(addr==r.getEnd()); + free_ranges.erase(it); + } + else if(addr==r.getStart()) + { + free_ranges.erase(it); + free_ranges.insert(Range_t(r.getStart()+1, r.getEnd())); + } + else if(addr==r.getEnd()) + { + free_ranges.erase(it); + free_ranges.insert(Range_t(r.getStart(), r.getEnd()-1)); + } + else // split range + { + free_ranges.erase(it); + free_ranges.insert(Range_t(r.getStart(), addr-1)); + free_ranges.insert(Range_t(addr+1, r.getEnd())); + } +} +void ZiprMemorySpace_t::MergeFreeRange(Range_t range) +{ + RangeAddress_t counter, end; + for (counter = range.getStart(), end = range.getEnd(); + counter!=end; + counter++) + { + MergeFreeRange(counter); + } +} +void ZiprMemorySpace_t::MergeFreeRange(RangeAddress_t addr) +{ + /* + * Make a new range of one byte. + * + * Then, look to see whether or not it + * can be merged with another range. + * + * If not, add it as a one byte range. + */ + + Range_t nr(addr, addr); + Range_t nrp1(addr+1, addr+1); + Range_t nrm1(addr-1, addr-1); + RangeSet_t::iterator itp1; + RangeSet_t::iterator itm1; + + itp1=free_ranges.find(nrp1); + itm1=free_ranges.find(nrm1); + Range_t r; + if(itp1!=free_ranges.end()) + { + r=*itp1; + assert((addr+1) == r.getStart()); + /* + * Make the beginning of this range + * one byte smaller! + */ + Range_t nnr(addr, r.getEnd()); + if (m_verbose) + { + printf("Expanded range: "); + printf("from: (%p - %p) ", (void*)r.getStart(), (void*)r.getEnd()); + printf("to: (%p - %p) \n", (void*)nnr.getStart(), (void*)nnr.getEnd()); + } + nr = nnr; + /* + * There is the possibility that we just expanded down and we + * now abut the end of a smaller free one. + */ + if(itm1!=free_ranges.end()) + { + Range_t r2=*itm1; + if (m_verbose) + { + printf("Expanded range: "); + printf("from: (%p - %p) ", (void*)nr.getStart(), (void*)nr.getEnd()); + printf("to: (%p - %p) \n", (void*)r2.getStart(), (void*)nnr.getEnd()); + } + nr.setStart(r2.getStart()); + free_ranges.erase(r2); + + } + /* + * Handling the analagous secondary merge case + * does not need to be handled here -- that + * would have been taken care of in the 1st + * conditional (just using opposite terms). + */ + free_ranges.erase(r); + } + else if(itm1!=free_ranges.end()) // not addr+1 is still busy, so we don't merge against it. + { + r=*itm1; + + assert((addr-1) == r.getEnd()) ; + /* + * Make the end of this range one byte + * bigger + */ + Range_t nnr(r.getStart(), addr); + if (m_verbose) + { + printf("Expanded range: "); + printf("from: (%p - %p) ", (void*)r.getStart(), (void*)r.getEnd()); + printf("to: (%p - %p) \n", (void*)nnr.getStart(), (void*)nnr.getEnd()); + } + nr = nnr; + free_ranges.erase(itm1); + } + else + { + // else both p1 and m1 are busy still, so we just insert nr + } + + // insert NR and be done. + free_ranges.insert(nr); + return; + +} + +void ZiprMemorySpace_t::PrintMemorySpace(std::ostream &out) +{ + for(auto r : free_ranges) + { + out <<"0x"<<std::hex<<r.getStart()<<" - 0x"<<std::hex<<r.getEnd()<<endl; + } +} + +RangeSet_t::iterator ZiprMemorySpace_t::FindFreeRange(RangeAddress_t addr) +{ + auto freer = free_ranges.find(Range_t(addr, addr)); + return freer; +} + +bool ZiprMemorySpace_t::IsValidRange(RangeSet_t::iterator it) +{ + return it!=free_ranges.end(); +} + +std::pair<RangeSet_t::const_iterator,RangeSet_t::const_iterator> + ZiprMemorySpace_t::getNearbyFreeRanges(const RangeAddress_t hint,size_t count) +{ + const auto search=Range_t(hint, hint+1); + const auto result = free_ranges.lower_bound(search); + /* + * TODO: Not quite sure what to make of this. + */ + return std::pair<RangeSet_t::const_iterator,RangeSet_t::const_iterator>( + result, free_ranges.end()); +} + +Range_t ZiprMemorySpace_t::GetLargeRange(void) +{ + for(auto r : free_ranges) + { + if(r.getEnd()==(RangeAddress_t)-1) + return r; + } + return Range_t(0,0); +} + +bool ZiprMemorySpace_t::SortRangeBySize(const Range_t &a, const Range_t &b) +{ + return (a.getEnd() - a.getStart()) <= (b.getEnd() - b.getStart()); +} + +std::list<Range_t> ZiprMemorySpace_t::getFreeRanges(size_t size) +{ + auto result=list<Range_t>(); + for(auto r : free_ranges) + { + if(r.getEnd() - r.getStart() >= (unsigned) size) + result.push_back(r); + } + result.sort(SortRangeBySize); + return result; +} + +Range_t ZiprMemorySpace_t::getInfiniteFreeRange() +{ + for(auto r : free_ranges) + { + if(r.getEnd()==(RangeAddress_t)-1) + return r; + } + assert(false); + return Range_t(0,0); +} + +Range_t ZiprMemorySpace_t::getFreeRange(int size) +{ + vector<Range_t> v; + Range_t big_range; + for(auto r : free_ranges) + { + if(r.getEnd()==(RangeAddress_t)-1) + big_range=r; + else if(r.getEnd() - r.getStart() >= (unsigned) size) + v.push_back(r); + + // that's enough randomization + if(v.size() > 100) + break; + } + if(v.size()==0) + return big_range; + + // choose random value to return. + int index=std::rand() % v.size(); + return v[index]; +} + +// queries about free areas. +bool ZiprMemorySpace_t::AreBytesFree(RangeAddress_t addr, int num_bytes) +{ + for(int i=0;i<num_bytes;i++) + if(!IsByteFree(addr+i)) + return false; + return true; +} + +bool ZiprMemorySpace_t::IsByteFree(RangeAddress_t addr) +{ + if (IsValidRange(FindFreeRange(addr))) + return true; + return false; +} + +void ZiprMemorySpace_t::AddFreeRange(Range_t newRange, bool original) +{ + if (original) + { + original_free_ranges.insert(Range_t(newRange.getStart(),newRange.getEnd())); + } + AddFreeRange(newRange); +} + +void ZiprMemorySpace_t::AddFreeRange(Range_t newRange) +{ + free_ranges.insert(Range_t(newRange.getStart(), newRange.getEnd())); +} +void ZiprMemorySpace_t::RemoveFreeRange(Range_t oldRange) +{ + free_ranges.erase(Range_t(oldRange.getStart(), oldRange.getEnd())); +} + +int ZiprMemorySpace_t::GetRangeCount() +{ + return free_ranges.size(); +} diff --git a/zipr/src/patcher_arm32.cpp b/zipr/src/patcher_arm32.cpp new file mode 100644 index 0000000000000000000000000000000000000000..bf27ee2f108664988cb65458411733eafdaf013d --- /dev/null +++ b/zipr/src/patcher_arm32.cpp @@ -0,0 +1,111 @@ +/*************************************************************************** + * Copyright (c) 2014 Zephyr Software LLC. All rights reserved. + * + * This software is furnished under a license and/or other restrictive + * terms and may be used and copied only in accordance with such terms + * and the inclusion of the above copyright notice. This software or + * any other copies thereof may not be provided or otherwise made + * available to any other person without the express written consent + * of an authorized representative of Zephyr Software LCC. Title to, + * ownership of, and all rights in the software is retained by + * Zephyr Software LCC. + * + * Zephyr Software LLC. Proprietary Information + * + * Unless otherwise specified, the information contained in this + * directory, following this legend, and/or referenced herein is + * Zephyr Software LLC. (Zephyr) Proprietary Information. + * + * CONTACT + * + * For technical assistance, contact Zephyr Software LCC. at: + * + * + * Zephyr Software, LLC + * 2040 Tremont Rd + * Charlottesville, VA 22911 + * + * E-mail: jwd@zephyr-software.com + **************************************************************************/ + +#include <zipr_all.h> +namespace zipr +{ +#include "patcher/patcher_arm32.hpp" +} +#include <irdb-core> +#include <iostream> +#include <stdlib.h> +#include <string.h> +#include <map> +#include <assert.h> +#include <sys/mman.h> +#include <ctype.h> +#include <iostream> // std::cout +#include <string> // std::string, std::to_string +#include <fstream> + +#define ALLOF(a) begin(a),end(a) + +using namespace IRDB_SDK; +using namespace std; +using namespace zipr; + +ZiprPatcherARM32_t::ZiprPatcherARM32_t(Zipr_SDK::Zipr_t* p_parent) : + m_parent(dynamic_cast<zipr::ZiprImpl_t*>(p_parent)), // upcast to ZiprImpl + m_firp(p_parent->getFileIR()), + memory_space(*p_parent->getMemorySpace()) + +{ +} + +void ZiprPatcherARM32_t::ApplyNopToPatch(RangeAddress_t addr) +{ assert(0); } + +void ZiprPatcherARM32_t::ApplyPatch(RangeAddress_t from_addr, RangeAddress_t to_addr) +{ + + const auto first_byte = (uint8_t)memory_space[from_addr+3]; + const auto new_offset = (int32_t)((to_addr) - (from_addr+8)) >> 2; + + // A Branch in binary is: op= cond4 1010 imm24=00 00000000 00000000 00000000 + // it includes a 24-bit immediate, which is +/- 32mb, which should be a good enough "jump anywhere" + // for now. + const auto mask3 = (0b111); + const auto is_branch = ((first_byte >> 1) & mask3) == (0b101); // any type of branch. + + if(is_branch) + { + // cout<<"Applying uncond branch patch from "<<hex<<from_addr<<" to "<<to_addr<<endl; + const auto non_imm_bits = 32U-24U; // 32 bits - imm24 + // assert there's no overflow. + assert((int64_t)(new_offset << non_imm_bits) == ((int64_t)new_offset) << non_imm_bits); + // or in opcode for first byte. set remaining bytes. + const auto mask24 = ((1<<24)-1); + const auto trimmed_offset = new_offset & mask24; + memory_space[from_addr+0] = (trimmed_offset>> 0)&0xff; + memory_space[from_addr+1] = (trimmed_offset>> 8)&0xff; + memory_space[from_addr+2] = (trimmed_offset>>16)&0xff; + // no need to write 4th byte. + } + else + assert(0); + +} + +void ZiprPatcherARM32_t::PatchJump(RangeAddress_t at_addr, RangeAddress_t to_addr) +{ + return this->ApplyPatch(at_addr,to_addr); +} +void ZiprPatcherARM32_t::PatchCall(RangeAddress_t at_addr, RangeAddress_t to_addr) +{ + assert(0); +} + + +void ZiprPatcherARM32_t::CallToNop(RangeAddress_t at_addr) +{ + assert(0); +} + + diff --git a/zipr/src/patcher_arm64.cpp b/zipr/src/patcher_arm64.cpp new file mode 100644 index 0000000000000000000000000000000000000000..7b07fe4e7c23c19bfdb246e17472da18d64c04e9 --- /dev/null +++ b/zipr/src/patcher_arm64.cpp @@ -0,0 +1,235 @@ +/*************************************************************************** + * Copyright (c) 2014 Zephyr Software LLC. All rights reserved. + * + * This software is furnished under a license and/or other restrictive + * terms and may be used and copied only in accordance with such terms + * and the inclusion of the above copyright notice. This software or + * any other copies thereof may not be provided or otherwise made + * available to any other person without the express written consent + * of an authorized representative of Zephyr Software LCC. Title to, + * ownership of, and all rights in the software is retained by + * Zephyr Software LCC. + * + * Zephyr Software LLC. Proprietary Information + * + * Unless otherwise specified, the information contained in this + * directory, following this legend, and/or referenced herein is + * Zephyr Software LLC. (Zephyr) Proprietary Information. + * + * CONTACT + * + * For technical assistance, contact Zephyr Software LCC. at: + * + * + * Zephyr Software, LLC + * 2040 Tremont Rd + * Charlottesville, VA 22911 + * + * E-mail: jwd@zephyr-software.com + **************************************************************************/ + +#include <zipr_all.h> +namespace zipr +{ +#include "patcher/patcher_arm64.hpp" +} +#include <irdb-core> +#include <iostream> +#include <stdlib.h> +#include <string.h> +#include <map> +#include <assert.h> +#include <sys/mman.h> +#include <ctype.h> +#include <iostream> // std::cout +#include <string> // std::string, std::to_string +#include <fstream> + +#define ALLOF(a) begin(a),end(a) + +using namespace IRDB_SDK; +using namespace std; +using namespace zipr; + +ZiprPatcherARM64_t::ZiprPatcherARM64_t(Zipr_SDK::Zipr_t* p_parent) : + m_parent(dynamic_cast<zipr::ZiprImpl_t*>(p_parent)), // upcast to ZiprImpl + m_firp(p_parent->getFileIR()), + memory_space(*p_parent->getMemorySpace()) + +{ +} + +void ZiprPatcherARM64_t::ApplyNopToPatch(RangeAddress_t addr) +{ assert(0); } + +void ZiprPatcherARM64_t::ApplyPatch(RangeAddress_t from_addr, RangeAddress_t to_addr) +{ + + const auto first_byte =(uint8_t)memory_space[from_addr+3]; + const auto second_byte=(uint8_t)memory_space[from_addr+2]; + const auto third_byte =(uint8_t)memory_space[from_addr+1]; + const auto fourth_byte=(uint8_t)memory_space[from_addr+0]; + const auto opcode = (first_byte >> 2) ; +// const auto insn_length=4U; + const auto new_offset=(int32_t)((to_addr)-from_addr) >> 2; + + const auto full_word= + (((uint32_t)first_byte ) << 24)| + (((uint32_t)second_byte) << 16)| + (((uint32_t)third_byte ) << 8)| + (((uint32_t)fourth_byte) << 0); + + // A Bracdh unconditional, in binary is: op=000101 imm26=00 00000000 00000000 00000000 + // it includes a 26-bit immediate, which is +/- 128MB, which should be a good enough "jump anywhere" + // for now. + // + const auto is_uncond_branch =(first_byte >>2) == (0x14 >> 2); // unconditional branch + const auto is_uncond_branch_and_link=(first_byte >>2) == (0x97 >> 2); // uncond branch and link + + // B.cond + // 01010100 imm19 0 cond + const auto is_branch_cond = first_byte== 0x54; // conditional branch + + // compare and branch + // sf 011 0101 imm19 Rt + // sf 011 0100 imm19 Rt + const auto is_compare_and_branch_nz = (first_byte & 0x7f) == 0x35; + const auto is_compare_and_branch_z = (first_byte & 0x7f) == 0x34; + const auto is_compare_and_branch = (is_compare_and_branch_nz || is_compare_and_branch_z); + + // b5 011 0111 b40 imm14 Rt -- tbnz + // b5 011 0110 b40 imm14 Rt -- tbz + const auto is_test_and_branch_nz = (first_byte & 0x7f) == 0x37; + const auto is_test_and_branch_z = (first_byte & 0x7f) == 0x36; + const auto is_test_and_branch = is_test_and_branch_nz || is_test_and_branch_z; + + if(is_uncond_branch || is_uncond_branch_and_link) + { + // cout<<"Applying uncond branch patch from "<<hex<<from_addr<<" to "<<to_addr<<endl; + const auto non_imm_bits=32U-26U; // 32 bits, imm26 + // assert there's no overflow. + assert((uint64_t)(new_offset << non_imm_bits) == ((uint64_t)new_offset) << non_imm_bits); + // or in opcode for first byte. set remaining bytes. + const auto mask26=((1<<26)-1); + const auto trimmed_offset=new_offset & mask26; + memory_space[from_addr+0]= (trimmed_offset>> 0)&0xff; + memory_space[from_addr+1]= (trimmed_offset>> 8)&0xff; + memory_space[from_addr+2]= (trimmed_offset>>16)&0xff; + memory_space[from_addr+3]=(opcode<<2) | ((trimmed_offset>>24)&0xff); + } + else if (is_branch_cond || is_compare_and_branch) + { + const auto non_mask_bits=32U-19; // 32 bits, imm19 + const auto mask19=(1<<19U)-1; + if((uint64_t)(new_offset << non_mask_bits) == ((uint64_t)new_offset) << non_mask_bits) + { + // the branch offset works here! + const auto full_word_clean=full_word & ~(mask19<<5); + const auto full_word_new_offset=full_word_clean | ((new_offset&mask19)<<5); + memory_space[from_addr+0]=(full_word_new_offset>> 0)&0xff; + memory_space[from_addr+1]=(full_word_new_offset>> 8)&0xff; + memory_space[from_addr+2]=(full_word_new_offset>>16)&0xff; + memory_space[from_addr+3]=(full_word_new_offset>>24)&0xff; + } + else + { + // branch offset didn't work. + // hopefully we can get there with a direct branch. + /* the plan when the branch offset doesn't fit: + * FA: b L0 + * FT: + * .. + * L0 b<cond> <args>, L2 # at tramp_start + * L1 b FT + * L2: b <target> + */ + const auto tramp_size=12; + + // check to see if we already had to trampoline from_addr. If so, + // patch the trampoline, not the actual redirect. + auto tramp_start=RangeAddress_t(0); + const auto redirect_it=redirect_map.find(from_addr); + if(redirect_it==redirect_map.end()) + { + // allocate new space in memory + const auto tramp_range=memory_space.getFreeRange(tramp_size); + tramp_start=tramp_range.getStart(); + // don't be too fancy, just reserve 12 bytes. + memory_space.splitFreeRange({tramp_start,tramp_start+tramp_size}); + // record that we had to trampoline this! + redirect_map[from_addr]=tramp_start; + } + else + { + // use previous tramp space. + tramp_start=redirect_it->second; + } + + const auto FA=from_addr; + const auto FT=from_addr+4; + const auto L0=tramp_start; + const auto L1=tramp_start+4; + const auto L2=tramp_start+8; + const auto branch_bytes=string("\x00\x00\x00\x014",4); + + // put the cond branch in the trampline, make it jump to L2 + memory_space[L0+0]=memory_space[FA+0]; + memory_space[L0+1]=memory_space[FA+1]; + memory_space[L0+2]=memory_space[FA+2]; + memory_space[L0+3]=memory_space[FA+3]; + ApplyPatch(L0,L2); + + // now make the original location jump to the trampoline + memory_space.plopBytes(FA, branch_bytes.c_str(), 4); + ApplyPatch(FA,L0);// make it jump to FT + + // now drop down a uncond jump for L1, and make it go to FT + // (i.e., jump around the jump to the target) + memory_space.plopBytes(L1, branch_bytes.c_str(), 4); + ApplyPatch(L1,FT);// make it jump to FT + + // lastly, put down the uncond jump at L2, and make it go to the target + memory_space.plopBytes(L2, branch_bytes.c_str(), 4); + ApplyPatch(L2,to_addr);// make it jump to +8 + + const auto disasm_str=DecodedInstruction_t::factory(from_addr, (const void*)&full_word, 4)->getDisassembly(); + + cout << "Had to trampline "<<disasm_str<< " at "<<hex<<from_addr + << " to " << L0 << " - " << L0+tramp_size<< " for target "<<to_addr<<endl; + + + } + } + else if (is_test_and_branch) + { + const auto non_mask_bits=32U-14; // 32 bits, imm14 + const auto mask14=(1<<14U)-1; + assert((uint64_t)(new_offset << non_mask_bits) == ((uint64_t)new_offset) << non_mask_bits); + const auto full_word_clean=full_word & ~(mask14<<5); + const auto full_word_new_offset=full_word_clean | ((new_offset&mask14)<<5); + memory_space[from_addr+0]=(full_word_new_offset>> 0)&0xff; + memory_space[from_addr+1]=(full_word_new_offset>> 8)&0xff; + memory_space[from_addr+2]=(full_word_new_offset>>16)&0xff; + memory_space[from_addr+3]=(full_word_new_offset>>24)&0xff; + } + else + assert(0); + +} + +void ZiprPatcherARM64_t::PatchJump(RangeAddress_t at_addr, RangeAddress_t to_addr) +{ + return this->ApplyPatch(at_addr,to_addr); +} +void ZiprPatcherARM64_t::PatchCall(RangeAddress_t at_addr, RangeAddress_t to_addr) +{ + assert(0); +} + + +void ZiprPatcherARM64_t::CallToNop(RangeAddress_t at_addr) +{ + assert(0); +} + + diff --git a/zipr/src/patcher_base.cpp b/zipr/src/patcher_base.cpp new file mode 100644 index 0000000000000000000000000000000000000000..8d71802cd51da1a3485cbd687d87435794ad51b9 --- /dev/null +++ b/zipr/src/patcher_base.cpp @@ -0,0 +1,71 @@ +/*************************************************************************** + * Copyright (c) 2014 Zephyr Software LLC. All rights reserved. + * + * This software is furnished under a license and/or other restrictive + * terms and may be used and copied only in accordance with such terms + * and the inclusion of the above copyright notice. This software or + * any other copies thereof may not be provided or otherwise made + * available to any other person without the express written consent + * of an authorized representative of Zephyr Software LCC. Title to, + * ownership of, and all rights in the software is retained by + * Zephyr Software LCC. + * + * Zephyr Software LLC. Proprietary Information + * + * Unless otherwise specified, the information contained in this + * directory, following this legend, and/or referenced herein is + * Zephyr Software LLC. (Zephyr) Proprietary Information. + * + * CONTACT + * + * For technical assistance, contact Zephyr Software LCC. at: + * + * + * Zephyr Software, LLC + * 2040 Tremont Rd + * Charlottesville, VA 22911 + * + * E-mail: jwd@zephyr-software.com + **************************************************************************/ + +#include <zipr_all.h> + +namespace zipr +{ +#include "patcher/patcher_mips32.hpp" +#include "patcher/patcher_arm32.hpp" +#include "patcher/patcher_arm64.hpp" +#include "patcher/patcher_x86.hpp" +} + +#include <irdb-core> +#include <iostream> +#include <stdlib.h> +#include <string.h> +#include <map> +#include <assert.h> +#include <sys/mman.h> +#include <ctype.h> +#include <iostream> // std::cout +#include <string> // std::string, std::to_string +#include <fstream> + +#define ALLOF(a) begin(a),end(a) + +using namespace IRDB_SDK; +using namespace std; +using namespace zipr; + +unique_ptr<ZiprPatcherBase_t> ZiprPatcherBase_t::factory(Zipr_SDK::Zipr_t* p_parent) +{ + auto l_firp=p_parent->getFileIR(); + auto ret= l_firp->getArchitecture()->getMachineType() == admtX86_64 ? (ZiprPatcherBase_t*)new ZiprPatcherX86_t (p_parent) : + l_firp->getArchitecture()->getMachineType() == admtI386 ? (ZiprPatcherBase_t*)new ZiprPatcherX86_t (p_parent) : + l_firp->getArchitecture()->getMachineType() == admtAarch64 ? (ZiprPatcherBase_t*)new ZiprPatcherARM64_t(p_parent) : + l_firp->getArchitecture()->getMachineType() == admtArm32 ? (ZiprPatcherBase_t*)new ZiprPatcherARM32_t(p_parent) : + l_firp->getArchitecture()->getMachineType() == admtMips32 ? (ZiprPatcherBase_t*)new ZiprPatcherMIPS32_t(p_parent) : + throw domain_error("Cannot init architecture"); + + return unique_ptr<ZiprPatcherBase_t>(ret); +} + diff --git a/zipr/src/patcher_mips32.cpp b/zipr/src/patcher_mips32.cpp new file mode 100644 index 0000000000000000000000000000000000000000..485c0d23ebec412533d81afd433244fe3e2bf7fe --- /dev/null +++ b/zipr/src/patcher_mips32.cpp @@ -0,0 +1,138 @@ +/*************************************************************************** + * Copyright (c) 2014 Zephyr Software LLC. All rights reserved. + * + * This software is furnished under a license and/or other restrictive + * terms and may be used and copied only in accordance with such terms + * and the inclusion of the above copyright notice. This software or + * any other copies thereof may not be provided or otherwise made + * available to any other person without the express written consent + * of an authorized representative of Zephyr Software LCC. Title to, + * ownership of, and all rights in the software is retained by + * Zephyr Software LCC. + * + * Zephyr Software LLC. Proprietary Information + * + * Unless otherwise specified, the information contained in this + * directory, following this legend, and/or referenced herein is + * Zephyr Software LLC. (Zephyr) Proprietary Information. + * + * CONTACT + * + * For technical assistance, contact Zephyr Software LCC. at: + * + * + * Zephyr Software, LLC + * 2040 Tremont Rd + * Charlottesville, VA 22911 + * + * E-mail: jwd@zephyr-software.com + **************************************************************************/ + +#include <zipr_all.h> +namespace zipr +{ +#include "patcher/patcher_mips32.hpp" +} +#include <irdb-core> +#include <iostream> +#include <stdlib.h> +#include <string.h> +#include <map> +#include <assert.h> +#include <sys/mman.h> +#include <ctype.h> +#include <iostream> // std::cout +#include <string> // std::string, std::to_string +#include <fstream> + +#define ALLOF(a) begin(a),end(a) + +using namespace IRDB_SDK; +using namespace std; +using namespace zipr; + +ZiprPatcherMIPS32_t::ZiprPatcherMIPS32_t(Zipr_SDK::Zipr_t* p_parent) : + m_parent(dynamic_cast<zipr::ZiprImpl_t*>(p_parent)), // upcast to ZiprImpl + m_firp(p_parent->getFileIR()), + memory_space(*p_parent->getMemorySpace()) + +{ +} + +void ZiprPatcherMIPS32_t::ApplyNopToPatch(RangeAddress_t addr) +{ assert(0); } + +void ZiprPatcherMIPS32_t::ApplyPatch(RangeAddress_t from_addr, RangeAddress_t to_addr) +{ + const auto mask6 = 0b111111; + const auto first_byte = (uint8_t)memory_space[from_addr+0]; + const auto second_byte = (uint8_t)memory_space[from_addr+1]; + const auto top6bits = (first_byte >> 2) & mask6; + const auto top16bits = (uint32_t(first_byte) << 8) | second_byte; + const auto top16bits_nocc = top16bits & ~(0b11100); + + + if( + top6bits == 0b000100 || // beq, + top6bits == 0b000001 || // bgez, bgezal, bltz, bltzal + top6bits == 0b000111 || // bgtz, + top6bits == 0b000110 || // blez, + top6bits == 0b000110 || // blez, + top6bits == 0b000101 || // bne + top16bits_nocc == 0b0100010100000000 || // bc1f + top16bits_nocc == 0b0100010100000001 // bc1t + ) + { + const auto new_offset = (int32_t)((to_addr) - (from_addr+4)) >> 2; + // Use a branch always. In mips, this will be a beq $0, $0, <label> as there is no branch always. + // format: 0001 00ss sstt iiii iiii iiii iiii iiii + // ssss=0b0000 + // tttt=0b0000 + // i...i = (from_addr-to_addr)>>2 + cout<<"Applying cond branch patch from "<<hex<<from_addr<<" to "<<to_addr<<endl; + const auto non_imm_bits = 16; + // assert there's no overflow. + assert((int64_t)(new_offset << non_imm_bits) == ((int64_t)new_offset) << non_imm_bits); + // or in opcode for first byte. set remaining bytes. + const auto mask16 = ((1<<16)-1); + const auto trimmed_offset = new_offset & mask16; + memory_space[from_addr+3] = (trimmed_offset>> 0)&0xff; + memory_space[from_addr+2] = (trimmed_offset>> 8)&0xff; + } + else if(top6bits == 0b00010) /* j and jal */ + { + const auto new_offset = (int32_t)(to_addr) >> 2; + cout<<"Applying uncond jump patch from "<<hex<<from_addr<<" to "<<to_addr<<endl; + const auto non_imm_bits = 32-26; + // assert there's no overflow. + assert((int64_t)(new_offset << non_imm_bits) == ((int64_t)new_offset) << non_imm_bits); + // or in opcode for first byte. set remaining bytes. + const auto mask26 = ((1<<26)-1); + const auto trimmed_offset = new_offset & mask26; + memory_space[from_addr+3] = (trimmed_offset>> 0)&0b11111111; /* low 8 bits */ + memory_space[from_addr+2] = (trimmed_offset>> 8)&0b11111111; /* 2nd 8 bits */ + memory_space[from_addr+1] = (trimmed_offset>> 16)&0b11111111; /* 3rd 8 bits */ + memory_space[from_addr+0] |= (trimmed_offset>> 24)&0b11; /* last 2 bits of 26 bit address. */ + + } + else + assert(0); + +} + +void ZiprPatcherMIPS32_t::PatchJump(RangeAddress_t at_addr, RangeAddress_t to_addr) +{ + return this->ApplyPatch(at_addr,to_addr); +} +void ZiprPatcherMIPS32_t::PatchCall(RangeAddress_t at_addr, RangeAddress_t to_addr) +{ + assert(0); +} + + +void ZiprPatcherMIPS32_t::CallToNop(RangeAddress_t at_addr) +{ + assert(0); +} + + diff --git a/zipr/src/patcher_x86.cpp b/zipr/src/patcher_x86.cpp new file mode 100644 index 0000000000000000000000000000000000000000..550b83f0b067b483acf3dde2c767259d0f7fd977 --- /dev/null +++ b/zipr/src/patcher_x86.cpp @@ -0,0 +1,220 @@ +/*************************************************************************** + * Copyright (c) 2014 Zephyr Software LLC. All rights reserved. + * + * This software is furnished under a license and/or other restrictive + * terms and may be used and copied only in accordance with such terms + * and the inclusion of the above copyright notice. This software or + * any other copies thereof may not be provided or otherwise made + * available to any other person without the express written consent + * of an authorized representative of Zephyr Software LCC. Title to, + * ownership of, and all rights in the software is retained by + * Zephyr Software LCC. + * + * Zephyr Software LLC. Proprietary Information + * + * Unless otherwise specified, the information contained in this + * directory, following this legend, and/or referenced herein is + * Zephyr Software LLC. (Zephyr) Proprietary Information. + * + * CONTACT + * + * For technical assistance, contact Zephyr Software LCC. at: + * + * + * Zephyr Software, LLC + * 2040 Tremont Rd + * Charlottesville, VA 22911 + * + * E-mail: jwd@zephyr-software.com + **************************************************************************/ + +#include <zipr_all.h> +namespace zipr +{ +#include "patcher/patcher_x86.hpp" +} +#include <irdb-core> +#include <iostream> +#include <stdlib.h> +#include <string.h> +#include <map> +#include <assert.h> +#include <sys/mman.h> +#include <ctype.h> +#include <iostream> // std::cout +#include <string> // std::string, std::to_string +#include <fstream> + +#define ALLOF(a) begin(a),end(a) + +using namespace IRDB_SDK; +using namespace std; +using namespace zipr; + +ZiprPatcherX86_t::ZiprPatcherX86_t(Zipr_SDK::Zipr_t* p_parent) : + m_parent(dynamic_cast<zipr::ZiprImpl_t*>(p_parent)), // upcast to ZiprImpl + m_firp(p_parent->getFileIR()), + memory_space(*p_parent->getMemorySpace()) +{ +} + +void ZiprPatcherX86_t::RewritePCRelOffset(RangeAddress_t from_addr,RangeAddress_t to_addr, int insn_length, int offset_pos) +{ + int new_offset=((unsigned int)to_addr)-((unsigned int)from_addr)-((unsigned int)insn_length); + + memory_space[from_addr+offset_pos+0]=(new_offset>>0)&0xff; + memory_space[from_addr+offset_pos+1]=(new_offset>>8)&0xff; + memory_space[from_addr+offset_pos+2]=(new_offset>>16)&0xff; + memory_space[from_addr+offset_pos+3]=(new_offset>>24)&0xff; +} + +void ZiprPatcherX86_t::ApplyNopToPatch(RangeAddress_t addr) +{ + /* + * TODO: Add assertion that this is really a patch. + */ + + /* + * 0F 1F 44 00 00H + */ + memory_space[addr] = (unsigned char)0x0F; + memory_space[addr+1] = (unsigned char)0x1F; + memory_space[addr+2] = (unsigned char)0x44; + memory_space[addr+3] = (unsigned char)0x00; + memory_space[addr+4] = (unsigned char)0x00; +} + +void ZiprPatcherX86_t::ApplyPatch(RangeAddress_t from_addr, RangeAddress_t to_addr) +{ + unsigned char insn_first_byte=memory_space[from_addr]; + unsigned char insn_second_byte=memory_space[from_addr+1]; + + switch(insn_first_byte) + { + case (unsigned char)0xF: // two byte escape + { + assert( insn_second_byte==(unsigned char)0x80 || // should be a JCC + insn_second_byte==(unsigned char)0x81 || + insn_second_byte==(unsigned char)0x82 || + insn_second_byte==(unsigned char)0x83 || + insn_second_byte==(unsigned char)0x84 || + insn_second_byte==(unsigned char)0x85 || + insn_second_byte==(unsigned char)0x86 || + insn_second_byte==(unsigned char)0x87 || + insn_second_byte==(unsigned char)0x88 || + insn_second_byte==(unsigned char)0x89 || + insn_second_byte==(unsigned char)0x8a || + insn_second_byte==(unsigned char)0x8b || + insn_second_byte==(unsigned char)0x8c || + insn_second_byte==(unsigned char)0x8d || + insn_second_byte==(unsigned char)0x8e || + insn_second_byte==(unsigned char)0x8f ); + + RewritePCRelOffset(from_addr,to_addr,6,2); + break; + } + + case (unsigned char)0xe8: // call + case (unsigned char)0xe9: // jmp + { + RewritePCRelOffset(from_addr,to_addr,5,1); + break; + } + + case (unsigned char)0xf0: // lock + case (unsigned char)0xf2: // rep/repe + case (unsigned char)0xf3: // repne + case (unsigned char)0x2e: // cs override + case (unsigned char)0x36: // ss override + case (unsigned char)0x3e: // ds override + case (unsigned char)0x26: // es override + case (unsigned char)0x64: // fs override + case (unsigned char)0x65: // gs override + case (unsigned char)0x66: // operand size override + case (unsigned char)0x67: // address size override + { + cout << "found patch for instruction with prefix. prefix is: "<<hex<<insn_first_byte<<". Recursing at "<<from_addr+1<<dec<<endl; + // recurse at addr+1 if we find a prefix byte has been plopped. + return this->ApplyPatch(from_addr+1, to_addr); + } + default: + { + if(m_firp->getArchitectureBitWidth()==64) /* 64-bit x86 machine assumed */ + { + /* check for REX prefix */ + if((unsigned char)0x40 <= insn_first_byte && insn_first_byte <= (unsigned char)0x4f) + { + cout << "found patch for instruction with prefix. prefix is: "<<hex<<insn_first_byte<<". Recursing at "<<from_addr+1<<dec<<endl; + // recurse at addr+1 if we find a prefix byte has been plopped. + return this->ApplyPatch(from_addr+1, to_addr); + } + } + std::cerr << "insn_first_byte: 0x" << hex << (int)insn_first_byte << dec << std::endl; + assert(0); + } + } +} + +void ZiprPatcherX86_t::PatchJump(RangeAddress_t at_addr, RangeAddress_t to_addr) +{ + uintptr_t off=to_addr-at_addr-2; + + assert(!memory_space.isByteFree(at_addr)); + + switch(memory_space[at_addr]) + { + case (char)0xe9: /* 5byte jump */ + { + RewritePCRelOffset(at_addr,to_addr,5,1); + break; + } + case (char)0xeb: /* 2byte jump */ + { + assert(off==(uintptr_t)(char)off); + + assert(!memory_space.isByteFree(at_addr+1)); + memory_space[at_addr+1]=(char)off; + break; + } + default: + { + assert(false); + } + } +} + + + +void ZiprPatcherX86_t::PatchCall(RangeAddress_t at_addr, RangeAddress_t to_addr) +{ + uintptr_t off=to_addr-at_addr-5; + + assert(!memory_space.isByteFree(at_addr)); + + switch(memory_space[at_addr]) + { + case (char)0xe8: /* 5byte call */ + { + assert(off==(uintptr_t)off); + assert(!memory_space.areBytesFree(at_addr+1,4)); + + memory_space[at_addr+1]=(char)(off>> 0)&0xff; + memory_space[at_addr+2]=(char)(off>> 8)&0xff; + memory_space[at_addr+3]=(char)(off>>16)&0xff; + memory_space[at_addr+4]=(char)(off>>24)&0xff; + break; + } + default: + assert(0); + + } +} + +void ZiprPatcherX86_t::CallToNop(RangeAddress_t at_addr) +{ + char bytes[]={(char)0x90,(char)0x90,(char)0x90,(char)0x90,(char)0x90}; // nop;nop;nop;nop;nop + memory_space.plopBytes(at_addr,bytes,sizeof(bytes)); +} + + + diff --git a/zipr/src/pewrite.cpp b/zipr/src/pewrite.cpp new file mode 100644 index 0000000000000000000000000000000000000000..4c72dc4a65be2f9f537085f1efd4adf3da4c4f8c --- /dev/null +++ b/zipr/src/pewrite.cpp @@ -0,0 +1,305 @@ + +#include <zipr_all.h> +#include <irdb-core> +#include <iostream> +#include <stdlib.h> +#include <string.h> +#include <algorithm> +#include <map> +#include <assert.h> +#include <sys/mman.h> +#include <ctype.h> +#include <iostream> +#include <string> +#include <fstream> +#include <elf.h> +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" +#include <pe_bliss.h> +#pragma GCC diagnostic pop + + +#define ALLOF(a) begin(a),end(a) + + +using namespace IRDB_SDK; +using namespace std; +using namespace zipr; +using namespace EXEIO; + +uint8_t dos_header[]= + { + /* 00000000 */ 0x4d, 0x5a, 0x90, 0x00, 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, // |MZ..............| + /* 00000010 */ 0xb8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // |........@.......| + /* 00000020 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // |................| + /* 00000030 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, // |................| + /* 00000040 */ 0x0e, 0x1f, 0xba, 0x0e, 0x00, 0xb4, 0x09, 0xcd, 0x21, 0xb8, 0x01, 0x4c, 0xcd, 0x21, 0x54, 0x68, // |........!..L.!Th| + /* 00000050 */ 0x69, 0x73, 0x20, 0x70, 0x72, 0x6f, 0x67, 0x72, 0x61, 0x6d, 0x20, 0x63, 0x61, 0x6e, 0x6e, 0x6f, // |is program canno| + /* 00000060 */ 0x74, 0x20, 0x62, 0x65, 0x20, 0x72, 0x75, 0x6e, 0x20, 0x69, 0x6e, 0x20, 0x44, 0x4f, 0x53, 0x20, // |t be run in DOS | + /* 00000070 */ 0x6d, 0x6f, 0x64, 0x65, 0x2e, 0x0d, 0x0d, 0x0a, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // |mode....$.......| + }; +/* + * notes: + * 0) A PE32+ file starts with a dos header (explained above). This is mostly ignored, except the word at byte 0x3c. + * 1) The 4-bytes at location 0x3c specify where the PE file header starts, this is the real meat of the heading. + * 2) the PE file header comes next. + * 3) The PE file header starts with the COFF header + * 3) The PE file header continues with the standard COFF header (which is diff than the COFF header) + */ + + + + +template<int width, class uintMa_t> +void PeWriter<width,uintMa_t>::Write(const string &out_file, const string &infile) +{ + // confirm our understanding of these fields + assert(sizeof(coff_header_t)==24); + assert(sizeof(standard_coff_header_t)==24); + // assert(sizeof(win_specific_fields_t)==88); not so true on pe32. + assert(sizeof(pe_section_header_t)==40); + + // open input/output files + fin=fopen(infile.c_str(), "r"); + fout=fopen(out_file.c_str(), "w"); + assert(fin && fout); + + // create the maps of the memory layout. + CreatePagemap(); + CreateSegmap(); + InitHeaders(); + GenerateDataDirectory(); + CalculateHeaderSizes(); + WriteFilePass1(); + WriteFilePass1(); + + /* close output files */ + fclose(fin); + fclose(fout); + + return; + +} + +template<int width, class uintMa_t> +void PeWriter<width,uintMa_t>::InitHeaders() +{ + const auto pebliss=reinterpret_cast<pe_bliss::pe_base*>(m_exeiop->get_pebliss()); + assert(pebliss); + + const auto orig_file_full_headers_str = pebliss -> get_full_headers_data(); + const auto orig_file_full_headers_cstr = orig_file_full_headers_str.c_str(); + const auto coff_header_offset = *reinterpret_cast<const uint32_t*>(orig_file_full_headers_cstr + 0x3c); // coff header at header ptr at 0x3c + const auto orig_file_standard_coff_header = (standard_coff_header_t*)(orig_file_full_headers_cstr+coff_header_offset+sizeof(coff_header_t)); + + // calculate the total size (last-first), rounded to page boundaries so that the OS can allocate that much virtual memory + // for this object. + const auto image_base = m_firp->getArchitecture()->getFileBase(); + const auto image_size = page_round_up(DetectMaxAddr()) - page_round_down(image_base); + + + const auto machine = (width == 64) ? 0x8664 : // x86 64 + (width == 32) ? 0x014c : // i386 + throw invalid_argument("Cannot map width to machine type"); + + // initialize the headers + coff_header_hdr = coff_header_t + ({ + 0x00004550, // "PE\0\0" + machine, // x86 64 + (uint16_t)segvec.size(), // size of the segment map + (uint32_t)time(nullptr), // time in seconds + 0, // ?? have to figure file pointer to symtable out. + 0, // no symbols + 0, // size of optional headers -- calc'd below. + pebliss->get_characteristics() // relocs stripped | executable | line numbers stripped | large addresses OK + } ); + + const auto magic_no = width==64 ? 0x20b : // PE32+ + width==32 ? 0x10b : // PE32 + throw invalid_argument("Width -> magic cannot be calculated"); + + standard_coff_header_hdr = standard_coff_header_t + ( { + magic_no, + orig_file_standard_coff_header->major_linker_version, // version 2.25 linker (major part) + orig_file_standard_coff_header->minor_linker_version, // version 2.25 linker (minor part) + 0x1234, // ?? have to figure out sizeof code + 0x5678, // ?? have to figure out initd data + 0x4321, // ?? have to figure out uninitd data + (uint32_t)(m_exeiop->get_entry() - image_base), // entry point + 0x1000 // ?? have to figure out code base + } ); + base_of_data=0x2000; + + + win_specific_fields_hdr = win_specific_fields_t + ( { + uintMa_t(image_base), // image_base; + PAGE_SIZE, // section_alignment; + 512, // file_alignment -- guessing this is a magic constant we can always use + pebliss->get_major_os_version(), // major_os_version -- constants, may very if we need to port to more win versions. read from a.ncexe? + pebliss->get_minor_os_version(), // minor_os_version; + 1, // major_image_version; + 0, // minor_image_version; + pebliss->get_major_subsystem_version(), // major_subsystem_version; + pebliss->get_minor_subsystem_version(), // minor_subsystem_version; + 0, // win32_version; + (uint32_t)image_size, // sizeof_image in memory (not including headers?) + 0x1000, // sizeof_headers (OK to over estimate?) + 0, // checksum ?? need to fix later + pebliss->get_subsystem(), // subsystem ?? read from input file? + pebliss->get_dll_characteristics(), // dll_characteristics + uintMa_t(pebliss->get_stack_size_reserve_64()), // sizeof_stack_reserve + uintMa_t(pebliss->get_stack_size_commit_64 ()), // sizeof_stack_commit + uintMa_t(pebliss->get_heap_size_reserve_64 ()), // sizeof_heap_reserve + uintMa_t(pebliss->get_heap_size_commit_64 ()), // sizeof_heap_commit + 0, // loader_flags -- reserved, must be 0. + 0x10 // number of rva_and_sizes -- always 16 from what I can tell? + } ); + + // + // Get data directories from pebliss + // + // magic number 16: the number of data directories in a PE32+ file for windows? not clear why this is the value or how to get it from pebliss. + for(auto id = 0u; id < 16; id++) + { + const auto rva = pebliss->get_directory_rva(id); + const auto rva_size = pebliss->get_directory_size(id); + cout<<"rva/size pair is "<<hex << rva << "/" << rva_size << endl; + image_data_dir_hdrs.push_back({rva, rva_size}); + } + + + auto exc_dir_it = find_if(ALLOF(m_firp->getDataScoops()), + [](const DataScoop_t* s) + { + return s->getName() == ".zipr_sehd"; + }); + if(exc_dir_it != end(m_firp->getDataScoops())) + { + const auto sehd_scoop=*exc_dir_it; + image_data_dir_hdrs[pe_bliss::pe_win::image_directory_entry_exception].virtual_address = sehd_scoop->getStart()->getVirtualOffset() - m_firp->getArchitecture()->getFileBase(); + image_data_dir_hdrs[pe_bliss::pe_win::image_directory_entry_exception].size = sehd_scoop->getSize(); + } + + CreateSectionHeaders(); +} + + +template<int width, class uintMa_t> +void PeWriter<width,uintMa_t>::GenerateDataDirectory() +{ +} + +template<int width, class uintMa_t> +void PeWriter<width,uintMa_t>::CalculateHeaderSizes() +{ + // shorten name + auto &hdr_size=coff_header_hdr.sizeof_opt_header; + + hdr_size = 0; + hdr_size += sizeof(standard_coff_header_t); + if(width==32) + hdr_size+=sizeof(uintMa_t); // add in BaseOfData field in sizing. + hdr_size += sizeof(win_specific_fields_t); + hdr_size += sizeof(image_data_directory_t) * image_data_dir_hdrs.size(); + + // for each section header (but the last), look to make sure the section fills + // the area until the start of the next section + for(auto i=0u; i<section_headers.size()-1; i++) + { + // get header entry I and i+1. + auto &shi = section_headers[i ]; + const auto &ship1 = section_headers[i+1]; + + // set the size to the only valid value. + shi.virtual_size = ship1.virtual_addr - shi.virtual_addr; + } + +} + +template<int width, class uintMa_t> +void PeWriter<width,uintMa_t>::CreateSectionHeaders() +{ + auto code_size=0u; + auto init_data_size=0u; + auto uninit_data_size=0u; + + auto sh=vector<pe_section_header_t>(); + for(auto seg : segvec) + { + section_headers.push_back({seg,m_firp}); + if((seg->m_perms & 0b1 ) == 0b1) + code_size+=seg->memsz; + else if((seg->m_perms & 0b100 ) == 0b100) + init_data_size+=seg->memsz; + } + + // update header with section size info. + standard_coff_header_hdr.sizeof_code = code_size; + standard_coff_header_hdr.sizeof_initd_data = init_data_size; + standard_coff_header_hdr.sizeof_uninitd_data = uninit_data_size; + +} + + +template<int width, class uintMa_t> +void PeWriter<width,uintMa_t>::WriteFilePass1() +{ + const auto res1=fseek(fout, 0,SEEK_SET); + assert(res1==0); + fwrite(&dos_header , sizeof(dos_header) , 1, fout); + fwrite(&coff_header_hdr , sizeof(coff_header_hdr) , 1, fout); + fwrite(&standard_coff_header_hdr, sizeof(standard_coff_header_hdr), 1, fout); + if(width==32) + fwrite(&base_of_data , sizeof(base_of_data), 1, fout); + fwrite(&win_specific_fields_hdr , sizeof(win_specific_fields_hdr) , 1, fout); + + fwrite(image_data_dir_hdrs.data(), sizeof(image_data_directory_t), image_data_dir_hdrs.size(), fout); + fwrite(section_headers.data() , sizeof(pe_section_header_t) , section_headers.size() , fout); + + auto file_base=m_firp->getArchitecture()->getFileBase(); + + for(auto& s : section_headers) + { + // get the current file position, and round it up to the nearest file-alignment position. + const auto pos=round_up_to(ftell(fout),file_alignment); + + // record it for pass2 + s.file_pointer_to_data=pos; + + // and jump there. + const auto res2=fseek(fout, pos,SEEK_SET); + assert(res2==0); + + // now write the info from the pages + const auto seg_end_addr = s.virtual_addr + s.file_size; + for(auto i = page_round_down(s.virtual_addr); i < seg_end_addr; i+=PAGE_SIZE) + { + const auto &page = pagemap[file_base+i]; + for(auto j=0u; j<PAGE_SIZE; j++) + { + // skip bytes that aren't in the section. + if(i+j < s.virtual_addr) continue; + if(i+j >= seg_end_addr ) continue; + + const auto byte=page.data.at(j); + fwrite(&byte, 1, 1, fout); + } + } + } + + // move it out to file_alignment bytes. + const auto end_pos=round_up_to(ftell(fout),file_alignment); + const auto res3=fseek(fout, end_pos-1,SEEK_SET); + assert(res3==0); + const auto zero=uint8_t(0); + fwrite(&zero,0,0,fout); // fill out the file with the last byte. + + +} + +template class PeWriter<64, uint64_t>; +template class PeWriter<32, uint32_t>; +// class PeWriter32; // instantiate templates. +// class PeWriter64; diff --git a/zipr/src/pinner_arm32.cpp b/zipr/src/pinner_arm32.cpp new file mode 100644 index 0000000000000000000000000000000000000000..ad21230c9e03c0105c04ea1c971cf45a16fd7530 --- /dev/null +++ b/zipr/src/pinner_arm32.cpp @@ -0,0 +1,63 @@ +#include <zipr_all.h> + +namespace zipr +{ +#include <pinner/pinner_arm32.hpp> +} +#include <memory> + +using namespace std; +using namespace IRDB_SDK; +using namespace zipr; + +ZiprPinnerARM32_t::ZiprPinnerARM32_t(Zipr_SDK::Zipr_t* p_parent) : + m_parent(dynamic_cast<zipr::ZiprImpl_t*>(p_parent)), // upcast to ZiprImpl + m_firp(p_parent->getFileIR()), + memory_space(*m_parent->getMemorySpace()), + m_dollop_mgr(*p_parent->getDollopManager()), + placement_queue(*p_parent->getPlacementQueue()) + +{ +} + +void ZiprPinnerARM32_t::doPinning() +{ + + + // deal with unpinned IBTs by putting them in the placement queue. + for(auto &insn : m_firp->getInstructions()) + { + if(insn->getIndirectBranchTargetAddress()==nullptr) + continue; + + if(insn->getIndirectBranchTargetAddress()->getVirtualOffset()==0) + { + // Unpinned IBT. Create dollop and add it to placement + // queue straight away--there are no pinning considerations. + auto newDoll=m_dollop_mgr.addNewDollops(insn); + placement_queue.insert({newDoll, 0}); + continue; + } + + auto ibta_addr=(RangeAddress_t)insn-> getIndirectBranchTargetAddress()-> getVirtualOffset(); + + // put unconditional branch with 24-bit offset in memory + // 1110 1010 0000 0000 0000 0000 0000 0000 + uint8_t bytes[]={'\x00','\x00','\x00',uint8_t('\xea')}; + for(auto i=0U;i<sizeof(bytes);i++) + { + const auto ibta_byte_addr = ibta_addr+i; + if(memory_space.find(ibta_byte_addr) != memory_space.end() ) + { + cout << "Byte is marked as both code and data at: " << hex << ibta_byte_addr << endl; + exit(10); + } + memory_space[ibta_byte_addr]=bytes[i]; + memory_space.splitFreeRange(ibta_byte_addr); + } + // insert a patch to patch the branch later. + const auto patch=Patch_t(ibta_addr, UnresolvedType_t::UncondJump_rel26); + const auto uu=UnresolvedUnpinned_t(insn); + m_parent->AddPatch(uu,patch); + } +} diff --git a/zipr/src/pinner_arm64.cpp b/zipr/src/pinner_arm64.cpp new file mode 100644 index 0000000000000000000000000000000000000000..3220d9b1738aa86b515994a96014a70042d34c78 --- /dev/null +++ b/zipr/src/pinner_arm64.cpp @@ -0,0 +1,59 @@ +#include <zipr_all.h> + +namespace zipr +{ +#include <pinner/pinner_arm64.hpp> +} +#include <memory> + +using namespace std; +using namespace IRDB_SDK; +using namespace zipr; + +ZiprPinnerARM64_t::ZiprPinnerARM64_t(Zipr_SDK::Zipr_t* p_parent) : + m_parent(dynamic_cast<zipr::ZiprImpl_t*>(p_parent)), // upcast to ZiprImpl + m_firp(p_parent->getFileIR()), + memory_space(*m_parent->getMemorySpace()), + m_dollop_mgr(*p_parent->getDollopManager()), + placement_queue(*p_parent->getPlacementQueue()) + +{ +} + +void ZiprPinnerARM64_t::doPinning() +{ + + + // deal with unpinned IBTs by putting them in the placement queue. + for(auto &insn : m_firp->getInstructions()) + { + if(insn->getIndirectBranchTargetAddress()==nullptr) + continue; + + if(insn->getIndirectBranchTargetAddress()->getVirtualOffset()==0) + { + // Unpinned IBT. Create dollop and add it to placement + // queue straight away--there are no pinning considerations. + auto newDoll=m_dollop_mgr.addNewDollops(insn); + placement_queue.insert({newDoll, 0}); + continue; + } + + auto ibta_addr=(RangeAddress_t)insn-> getIndirectBranchTargetAddress()-> getVirtualOffset(); + + // put unconditional branch with 26-bit offset in memory + // 0001 0100 0000 0000 0000 0000 0000 0000 + uint8_t bytes[]={'\x00','\x00','\x00','\x14'}; + for(auto i=0U;i<sizeof(bytes);i++) + { + assert(memory_space.find(ibta_addr+i) == memory_space.end() ); + memory_space[ibta_addr+i]=bytes[i]; + memory_space.splitFreeRange(ibta_addr+i); + } + // insert a patch to patch the branch later. + const auto patch=Patch_t(ibta_addr, UnresolvedType_t::UncondJump_rel26); + + const auto uu=UnresolvedUnpinned_t(insn); + m_parent->AddPatch(uu,patch); + } +} diff --git a/zipr/src/pinner_base.cpp b/zipr/src/pinner_base.cpp new file mode 100644 index 0000000000000000000000000000000000000000..7f79d9759a5e98b042136f793cd56f9b10d43b2b --- /dev/null +++ b/zipr/src/pinner_base.cpp @@ -0,0 +1,26 @@ +#include <zipr_all.h> + +namespace zipr +{ +#include <pinner/pinner_x86.hpp> +#include <pinner/pinner_arm64.hpp> +#include <pinner/pinner_arm32.hpp> +#include <pinner/pinner_mips32.hpp> +} +#include <memory> + +using namespace std; +using namespace IRDB_SDK; +using namespace zipr; + +unique_ptr<ZiprPinnerBase_t> ZiprPinnerBase_t::factory(Zipr_SDK::Zipr_t *p_parent) +{ + auto ret= p_parent->getFileIR()->getArchitecture()->getMachineType() == admtX86_64 ? (ZiprPinnerBase_t*)new ZiprPinnerX86_t (p_parent) : + p_parent->getFileIR()->getArchitecture()->getMachineType() == admtI386 ? (ZiprPinnerBase_t*)new ZiprPinnerX86_t (p_parent) : + p_parent->getFileIR()->getArchitecture()->getMachineType() == admtAarch64 ? (ZiprPinnerBase_t*)new ZiprPinnerARM64_t(p_parent) : + p_parent->getFileIR()->getArchitecture()->getMachineType() == admtArm32 ? (ZiprPinnerBase_t*)new ZiprPinnerARM32_t(p_parent) : + p_parent->getFileIR()->getArchitecture()->getMachineType() == admtMips32 ? (ZiprPinnerBase_t*)new ZiprPinnerMIPS32_t(p_parent) : + throw domain_error("Cannot init architecture"); + + return unique_ptr<ZiprPinnerBase_t>(ret); +} diff --git a/zipr/src/pinner_mips32.cpp b/zipr/src/pinner_mips32.cpp new file mode 100644 index 0000000000000000000000000000000000000000..2627aa1b157b381a973e345bc9e0412fc4810608 --- /dev/null +++ b/zipr/src/pinner_mips32.cpp @@ -0,0 +1,64 @@ +#include <zipr_all.h> + +namespace zipr +{ +#include <pinner/pinner_mips32.hpp> +} +#include <memory> + +using namespace std; +using namespace IRDB_SDK; +using namespace zipr; + +ZiprPinnerMIPS32_t::ZiprPinnerMIPS32_t(Zipr_SDK::Zipr_t* p_parent) : + m_parent(dynamic_cast<zipr::ZiprImpl_t*>(p_parent)), // upcast to ZiprImpl + m_firp(p_parent->getFileIR()), + memory_space(*m_parent->getMemorySpace()), + m_dollop_mgr(*p_parent->getDollopManager()), + placement_queue(*p_parent->getPlacementQueue()) + +{ +} + +void ZiprPinnerMIPS32_t::doPinning() +{ + + + // deal with unpinned IBTs by putting them in the placement queue. + for(auto &insn : m_firp->getInstructions()) + { + if(insn->getIndirectBranchTargetAddress()==nullptr) + continue; + + if(insn->getIndirectBranchTargetAddress()->getVirtualOffset()==0) + { + // Unpinned IBT. Create dollop and add it to placement + // queue straight away--there are no pinning considerations. + auto newDoll=m_dollop_mgr.addNewDollops(insn); + placement_queue.insert({newDoll, 0}); + continue; + } + + auto ibta_addr=(RangeAddress_t)insn-> getIndirectBranchTargetAddress()-> getVirtualOffset(); + + // put branch with 16-bit offset in memory + // i.e., beq $0, $0, imm16 + // including putting a nop in the delay slot. + uint8_t bytes[]={'\x10','\x00','\x00','\x00', '\x00', '\x00', '\x00', '\x00'}; + for(auto i=0U;i<sizeof(bytes);i++) + { + const auto ibta_byte_addr = ibta_addr+i; + if(memory_space.find(ibta_byte_addr) != memory_space.end() ) + { + cout << "Byte is marked as both code and data at: " << hex << ibta_byte_addr << endl; + exit(10); + } + memory_space[ibta_byte_addr]=bytes[i]; + memory_space.splitFreeRange(ibta_byte_addr); + } + // insert a patch to patch the branch later. + const auto patch=Patch_t(ibta_addr, UnresolvedType_t::UncondJump_rel26); + const auto uu=UnresolvedUnpinned_t(insn); + m_parent->AddPatch(uu,patch); + } +} diff --git a/zipr/src/pinner_x86.cpp b/zipr/src/pinner_x86.cpp new file mode 100644 index 0000000000000000000000000000000000000000..a331d371b51a19a94f7f1282800f46d0954eeba9 --- /dev/null +++ b/zipr/src/pinner_x86.cpp @@ -0,0 +1,1511 @@ +#include <zipr_all.h> +#include <iostream> +#include <stdlib.h> +#include <string.h> +#include <map> +#include <assert.h> +#include <sys/mman.h> +#include <ctype.h> +#include <iostream> // std::cout +#include <string> // std::string, std::to_string +#include <fstream> +#include <irdb-util> + +namespace zipr +{ +#include <pinner/pinner_x86.hpp> +} +#include <memory> + +using namespace std; +using namespace IRDB_SDK; +using namespace zipr; + +static int ceildiv(int a, int b) +{ + return (a+b-1)/b; +} + + +// +// Convert a 32-bit integer into a string that keystone will accept +// without indicating an overflow. +// return a string +inline string to_ks_string(uint32_t val) +{ + // Ks is very funky about what integers it accepts. + // 31-bit integers work in hex + if(val <= 0x7fffffff) + { + return "0x"+to_hex_string(val); + } + // and negative 31-bit integers work in hex. + else + { + return "-0x"+to_hex_string(-val); + } +} + + +#define ALLOF(a) begin(a),end(a) + +ZiprPinnerX86_t::ZiprPinnerX86_t(Zipr_SDK::Zipr_t* p_parent) : + m_parent(dynamic_cast<zipr::ZiprImpl_t*>(p_parent)), // upcast to ZiprImpl + memory_space(*p_parent->getMemorySpace()), + m_dollop_mgr(*p_parent->getDollopManager()), + m_firp(p_parent->getFileIR()), + placement_queue(*p_parent->getPlacementQueue()), + m_verbose(false), // fixme + m_stats(m_parent->getStats()), + final_insn_locations(*p_parent->getLocationMap()) +{ + +} + +void ZiprPinnerX86_t::doPinning() +{ + // Initial creation of the set of pinned instructions. + AddPinnedInstructions(); + + // Reserve space for pins. + ReservePinnedInstructions(); + + // Emit instruction immediately? + + //TODO: Reenable after option parsing is fixed. +#if 0 + if (m_opts.IsEnabledOptimization(Optimizations_t::OptimizationFallthroughPinned)) + { + OptimizePinnedFallthroughs(); + } +#endif + + PreReserve2ByteJumpTargets(); + + // expand 2-byte pins into 5-byte pins + ExpandPinnedInstructions(); + + while (!two_byte_pins.empty()) + { + /* + * Put down the five byte targets + * for two byte jumps, if any exist. + */ + printf("Going to Fix2BytePinnedInstructions.\n"); + Fix2BytePinnedInstructions(); + + /* + * If there are still two byte pins, + * try the dance again. + */ + if (!two_byte_pins.empty()) + { + printf("Going to Re PreReserve2ByteJumpTargets.\n"); + PreReserve2ByteJumpTargets(); + } + } + + // Convert all 5-byte pins into full fragments + OptimizePinnedInstructions(); +} + + +void ZiprPinnerX86_t::AddPinnedInstructions() +{ + // find the big chunk of free memory in case we need it for unassigned pins. + VirtualOffset_t next_pin_addr=memory_space.getInfiniteFreeRange().getStart(); + + + /* + * Start out by recording the pinned address into a map + * for use by other functions. + */ + RecordPinnedInsnAddrs(); + + for(auto insn : m_firp->getInstructions()) + { + assert(insn); + + if(insn->getIndirectBranchTargetAddress()==nullptr) + continue; + + if(insn->getIndirectBranchTargetAddress()->getVirtualOffset()==0) + { + // Unpinned IBT. Create dollop and add it to placement + // queue straight away--there are no pinning considerations. + auto newDoll=m_dollop_mgr.addNewDollops(insn); +#if 1 + (void)newDoll; +#else +// placement_queue.insert({newDoll, 0}); +#endif + continue; + } + + // deal with unassigned IBTAs. + if(insn->getIndirectBranchTargetAddress()->getVirtualOffset()==0) + { + insn->getIndirectBranchTargetAddress()->setVirtualOffset(next_pin_addr); + next_pin_addr+=5;// sizeof pin + } + + unresolved_pinned_addrs.insert({insn}); + } +} + +void ZiprPinnerX86_t::RecordPinnedInsnAddrs() +{ + for(auto insn : m_firp->getInstructions()) + { + assert(insn); + + if(!insn->getIndirectBranchTargetAddress() + || insn->getIndirectBranchTargetAddress()->getVirtualOffset()==0) + { + continue; + } + const auto ibta_addr=(RangeAddress_t)insn-> getIndirectBranchTargetAddress()-> getVirtualOffset(); + + /* + * Record the size of thing that we are pinning. + * We are going to use this information for doing + * sleds, if we have to. + * + * There are two different possibilities: + * + * 1. The instruction is turned into a jump. By default, + * we assume that turns into a two byte relative jump. + * This information will *not* be used in the case that + * this pin is overriden by a patch. In other words, when + * this pinned address is handled in ExpandPinnedInstructions() + * then it might be expanded into a five byter. However, + * when we deal this in the context of putting down a sled, + * we check for patches before falling back to this method. + * + * + * 2. The instruction cannot be turned into a jump -- it + * must be pinned immediately. In that case, we want to + * record the size of the instruction itself. + */ + if (ShouldPinImmediately(insn)) + m_InsnSizeAtAddrs[ibta_addr]={insn,insn->getDataBits().length()}; + else + m_InsnSizeAtAddrs[ibta_addr]={insn,2}; + } +} + + +bool ZiprPinnerX86_t::ShouldPinImmediately(Instruction_t *upinsn) +{ + const auto d=DecodedInstruction_t::factory (upinsn); + if(d->isReturn() ) + return true; + + const auto upinsn_ibta = upinsn->getIndirectBranchTargetAddress(); + const auto ft_ibta = upinsn->getFallthrough() != nullptr ? upinsn->getFallthrough()->getIndirectBranchTargetAddress() : (AddressID_t*)nullptr; + assert(upinsn_ibta!=nullptr && upinsn_ibta->getVirtualOffset()!=0); + + /* careful with 1 byte instructions that have a pinned fallthrough */ + const auto len=upinsn->getDataBits().length(); + if(len==1) + { + if(upinsn->getFallthrough()==nullptr) + return true; + if((ft_ibta && ft_ibta->getVirtualOffset()!=0) && (upinsn_ibta->getVirtualOffset()+1) == ft_ibta->getVirtualOffset()) + return true; + } + + + // look for a pinned ib + if(upinsn->getFallthrough()==nullptr && upinsn->getIBTargets()!=nullptr) + { + // check the bytes that follow to make sure we can fit it. + auto found=false; + for(auto i = 1u; i < len; i++) + { + + const auto ibt_at=FindPinnedInsnAtAddr(upinsn_ibta->getVirtualOffset() + i); + if(ibt_at) + found=true; + } + + // if the whole range is clear, we can pin the ib. + if(!found) + return true; + } + + + + + // find the insn pinned at the next byte. + auto pin_at_next_byte = FindPinnedInsnAtAddr(upinsn_ibta->getVirtualOffset() + 1); + if ( pin_at_next_byte && + + /* upinsn has lock prefix */ + upinsn->getDataBits()[0]==(char)(0xF0) && + /* + * upinsn: lock cmpxchange op1 op2 [pinned at x] + * x x+1 x+2 x+3 + * + * AND pin_at_next_byte (x+1) is: + */ + pin_at_next_byte->getDataBits() == upinsn->getDataBits().substr(1,upinsn->getDataBits().length()-1) && + /* + * cmpxchange op1 op2 [pinned at x+1] + * x+1 x+2 x+3 + * AND pin_at_next_byte->fallthrough() == upinsn->Fallthrough() + */ + pin_at_next_byte->getFallthrough() == upinsn->getFallthrough() + /* + * x should become nop, put down immediately + * x+1 should become the entire lock command. + */ + ) + { + if (m_verbose) + cout<<"Using pin_at_next_byte special case, addrs="<< + upinsn_ibta->getVirtualOffset()<<","<< + pin_at_next_byte->getAddress()->getVirtualOffset()<<endl; + /* + * Because upinsn is longer than + * 1 byte, we must be somehow + * pinned into ourselves. Fix! + */ + + /* + * Make pin_at_next_byte look like upinsn. + */ + pin_at_next_byte->setDataBits(upinsn->getDataBits()); + pin_at_next_byte->setComment(upinsn->getComment()); + pin_at_next_byte->setCallback(upinsn->getCallback()); + pin_at_next_byte->setFallthrough(upinsn->getFallthrough()); + pin_at_next_byte->setTarget(upinsn->getTarget()); + /* + * Convert upins to nop. + */ + string dataBits = upinsn->getDataBits(); + dataBits.resize(1); + dataBits[0] = 0x90; + upinsn->setDataBits(dataBits); + + return true; + } + return false; +} + +void ZiprPinnerX86_t::PreReserve2ByteJumpTargets() +{ + bool repeat = false; + + do + { + repeat = false; + for(auto it=two_byte_pins.begin(); it!=two_byte_pins.end(); /* empty */) + { + UnresolvedPinned_t up=*it; + bool found_close_target = false; + Instruction_t* upinsn=up.getInstrution(); + + RangeAddress_t addr; + + if (up.HasUpdatedAddress()) + { + addr = up.GetUpdatedAddress(); + } + else + { + addr=upinsn->getIndirectBranchTargetAddress()->getVirtualOffset(); + } + + if (m_AddrInSled[addr]) + { + /* + * There is no need to consider this pin at all! It was + * subsumed w/in a sled which has already handled it. + * Move along. + */ + if (m_verbose) + cout << "Two byte pin at 0x" + << std::hex << addr + << " is w/in a sled ... skipping and erasing." << endl; + two_byte_pins.erase(it++); + continue; + } + + /* + * Check for near branch instructions + * by starting far away! + * Note: two byte jump range is 127 bytes, + * but that's from the pc after it's been + * inc, etc. complicated goo. 120 is a + * safe estimate of range. + */ + for(int size=5;size>0;size-=3) + { + + //if (m_verbose) + // printf("Looking for %d-byte jump targets to pre-reserve.\n", size); + for(int i=120;i>=-120;i--) + { + if(memory_space.areBytesFree(addr+i,size)) + { + if (m_verbose) + printf("Found location for 2-byte->%d-byte conversion " + "(%p-%p)->(%p-%p) (orig: %p)\n", + size, + (void*)addr, + (void*)(addr+1), + (void*)(addr+i), + (void*)(addr+i+size), + (upinsn->getIndirectBranchTargetAddress() != nullptr) ? + (void*)(uintptr_t)upinsn->getIndirectBranchTargetAddress()->getVirtualOffset() : 0x0); + + up.SetRange(Range_t(addr+i, addr+i+size)); + for (auto j = up.GetRange().getStart(); j<up.GetRange().getEnd(); j++) + { + memory_space.splitFreeRange(j); + } + + /* + * We add chain entries early, as soon as the patch is + * prereserved. When we use it, we don't have to worry about it + * already being written as a patch. However, if we don't use it, + * we will need to remove it. See ExpandPinnedInstructions() for + * the place where we do the removal. + * + * addr: place of prereserved memory + * size: size of the amount of prereserved memory + */ + auto uu = UnresolvedUnpinned_t(up); + auto patch = Patch_t(up.GetRange().getStart(), UnresolvedType_t::UncondJump_rel32); + + if (size == 2) + patch.setType(UnresolvedType_t::UncondJump_rel8); + + auto uup = UnresolvedUnpinnedPatch_t(uu, patch); + + if (m_verbose) + cout << "Adding a chain entry at address " + << std::hex << up.GetRange().getStart() << endl; + m_parent->RecordNewPatch({up.GetRange().getStart(),uup}); + + found_close_target = true; + break; + } + } + if (found_close_target) + break; + } + + if (!found_close_target) + { + /* + * In the case that we did not find a nearby + * space for placing a 2/5 jmp, we are going + * to fall back to using a sled. + */ + + /* + * Algorithm: + * 1. Insert the sled at addr and record where it ends (end_addr). + * 2. For each pinned instruction, i, between addr and end_addr: + * a. If i exists in two_byte_pins indicating that it will + * be handled, we remove it. + * b. If i has prereserved space associated with it, we + * we clear that space. + */ + cout<<"Warning: No location for near jump reserved at 0x"<<hex<<addr<<"."<<endl; + + /* + * The first thing that we have to do is to tell + * ourselves that this is a chain entry. + */ + if (m_verbose) + cout << "Added emergency patch entry at " + << std::hex << addr << endl; + Patch_t emergency_patch(addr, UnresolvedType_t::UncondJump_rel8); + UnresolvedUnpinned_t uu(up); + UnresolvedUnpinnedPatch_t uup(uu, emergency_patch); + m_parent->RecordNewPatch(std::pair<RangeAddress_t, UnresolvedUnpinnedPatch_t>(addr,uup)); + + RangeAddress_t end_of_sled = Do68Sled(addr); + + m_firp->assembleRegistry(); + for (RangeAddress_t i = addr; i<end_of_sled; i++) + { + Instruction_t *found_pinned_insn = nullptr; + found_pinned_insn = FindPinnedInsnAtAddr(i); + if (found_pinned_insn) + { + /* + * TODO: Continue from here. Continue implementing the above + * algorithm. Don't forget to handle the case that Jason was + * explaining where a range of bytes in this sled may actually + * contain a two byte jmp that points to the ultimate + * five byte jump that ultimately terminates the chain. + */ + } + } + ++it; + } + else + { + UnresolvedPinned_t new_up = UnresolvedPinned_t(up.getInstrution(), up.GetRange()); + if (up.HasUpdatedAddress()) + { + new_up.SetUpdatedAddress(up.GetUpdatedAddress()); + } + two_byte_pins.erase(it++); + two_byte_pins.insert(new_up); + + } + } + } while (repeat); +} + +void ZiprPinnerX86_t::InsertJumpPoints68SledArea(Sled_t &sled) +{ + for (RangeAddress_t addr = sled.SledRange().getStart(); + addr < sled.SledRange().getEnd(); + addr++) + { + bool is_pin_point = false, is_patch_point = false; + /* + * There is the possibility that the sled is being put here + * because we have a pin that cannot be properly handled. + */ + is_pin_point = (nullptr != FindPinnedInsnAtAddr(addr)); + if (m_verbose && is_pin_point) + cout << "There is a pin at 0x" << std::hex << addr + << " inside a sled." << endl; + + /* + * There is the possibility that the sled is being put here + * because we have a chain entry that cannot properly be + * handled. + */ + is_patch_point = FindPatchTargetAtAddr(addr); + if (is_patch_point) + { + + cout << "There is a patch at 0x" + << std::hex << addr << " inside a sled." << endl; + } + + if (is_pin_point || is_patch_point) + { + if (m_verbose) + cout << "Adding Jump Point at 0x" << std::hex << addr << endl; + sled.AddJumpPoint(addr); + } + } +} + +Instruction_t* ZiprPinnerX86_t::Emit68Sled(RangeAddress_t addr, Sled_t sled, Instruction_t* next_sled) +{ + Instruction_t *sled_start_insn = nullptr; + auto sled_number = addr - sled.SledRange().getStart(); + size_t sled_size = sled.SledRange().getEnd() - sled.SledRange().getStart(); + + sled_start_insn = FindPinnedInsnAtAddr(addr); + if (!sled_start_insn) + sled_start_insn = FindPatchTargetAtAddr(addr); + assert(sled_start_insn != nullptr); + + if (m_verbose) + cout << "Begin emitting the 68 sled @ " << addr << "." << endl; + + const uint32_t push_lookup[]={0x68686868, + 0x90686868, + 0x90906868, + 0x90909068, + 0x90909090}; + const int number_of_pushed_values=ceildiv(sled_size-sled_number, 5); + vector<uint32_t> pushed_values(number_of_pushed_values, 0x68686868); + + // first pushed value is variable depending on the sled's index + pushed_values[0] = push_lookup[4-((sled_size-sled_number-1)%5)]; + + /* + * Emit something that looks like: + * if ( *(tos+0*stack_push_size)!=pushed_values[0] ) + * jmp next_sled; //missed + * if ( *(tos+1*stack_push_size)!=pushed_values[1] ) + * jmp next_sled; //missed + * ... + * if ( *(tos+number_of_pushed_values*stack_push_size-1)!=pushed_values[number_of_pushed_values-1] ) + * jmp next_sled; //missed + * lea rsp, [rsp+push_size] + * jmp dollop's translation // found + */ + + string stack_reg="rsp"; + string decoration="qword"; + if(m_firp->getArchitectureBitWidth()!=64) + { + decoration="dword"; + stack_reg="esp"; + } + const int stack_push_size=m_firp->getArchitectureBitWidth()/8; + + string lea_string=string("lea ")+stack_reg+", ["+stack_reg+"+" + to_string(stack_push_size*number_of_pushed_values)+"]"; + Instruction_t *lea=addNewAssembly(m_firp, nullptr, lea_string); + lea->setFallthrough(sled_start_insn); + + Instruction_t *old_cmp=lea; + + for(int i=0;i<number_of_pushed_values;i++) + { + string cmp_str="cmp "+decoration+" ["+stack_reg+"+ "+to_string(i*stack_push_size)+"], "+to_ks_string(pushed_values[i]); + Instruction_t* cmp=addNewAssembly(m_firp, nullptr, cmp_str); + Instruction_t *jne=addNewAssembly(m_firp, nullptr, "jne 0"); + cmp->setFallthrough(jne); + jne->setTarget(next_sled); + jne->setFallthrough(old_cmp); + + cout<<"Adding 68-sled bit: "+cmp_str+", jne 0 for sled at 0x"<<hex<<addr<<" entry="<<dec<<sled_number<<endl; + + old_cmp=cmp; + } + + /* + * now that all the cmp/jmp's are inserted, we are done with this sled. + */ + return old_cmp; +} + +Instruction_t* ZiprPinnerX86_t::Emit68Sled(Sled_t sled)// RangeAddress_t addr, int sled_size) +{ + + Instruction_t *top_of_sled=addNewAssembly(m_firp, nullptr, "hlt"); + + for (std::set<RangeAddress_t>::reverse_iterator + addr_iter=sled.JumpPointsReverseBegin(); + addr_iter != sled.JumpPointsReverseEnd(); + addr_iter++) + { + RangeAddress_t addr = *addr_iter; + if (m_verbose) + cout << "Specific Emit68Sled(" + << std::hex << addr << "," + << sled << "," + << std::hex << top_of_sled << ");" << endl; + + top_of_sled=Emit68Sled(addr, sled, top_of_sled); + } + return top_of_sled; +} + +/* + * Put the new sled into the existing sled. + * and do some other things. + * Note that the clearable sled is just the difference + * between the new and the old. We do not + * need to clear out any of the existing sled + * since it is just PUSHs at this point! + */ +void ZiprPinnerX86_t::Update68Sled(Sled_t new_sled, Sled_t &existing_sled) +{ + Range_t clearable_sled_range(new_sled.SledRange().getStart(), + existing_sled.SledRange().getStart()); + Sled_t clearable_sled(memory_space, clearable_sled_range); + + if (m_verbose) + cout << "Updating sled: " << existing_sled + << " with new sled: " << new_sled << endl; + + /* + * Put the jump points into the new sled area. + */ + InsertJumpPoints68SledArea(new_sled); + + cout << "Clearable sled: " << clearable_sled << endl; + clearable_sled.MergeSledJumpPoints(new_sled); + cout << "(Merged) Clearable sled: " << clearable_sled << endl; + /* + * Clear the chains in the new sled! + */ + Clear68SledArea(clearable_sled); + + /* + * Put in PUSHs in the new_sled. + */ + size_t i=0; + RangeAddress_t addr=new_sled.SledRange().getStart(); + for(;i<existing_sled.SledRange().getStart()-new_sled.SledRange().getStart(); + i++) + { + if (m_verbose) + cout << "Adding 68 at " + << std::hex << addr+i + << " for sled at 0x" + << std::hex << addr << endl; + + /* + * Do not assert that we are writing into a free space. + * We may be writing over a PUSH that was there before! + */ + assert(memory_space.isByteFree(addr+i) || memory_space[addr+i]==0x68); + memory_space[addr+i] = 0x68; + m_AddrInSled[addr+i] = true; + memory_space.splitFreeRange(addr+i); + } + + existing_sled.MergeSled(new_sled); + + assert(existing_sled.Disambiguation()); + + Instruction_t *sled_disambiguation = Emit68Sled(existing_sled); + + if (m_verbose) + cout << "Generated sled_disambiguation (in Update68Sled()): " << std::hex << sled_disambiguation << endl; + /* + * Update the disambiguation + * + * What we are doing is walking through the + * expanded or unexpanded pins and seeing + * if they match the end instruction that + * we are doing now. We updated the end + * instruction in the MergeSled(). Why is it + * that this might fail? + * + * TODO: This is really bad slow. + */ + + Instruction_t *disambiguations[2] = {existing_sled.Disambiguation(), + new_sled.Disambiguation()}; + bool disambiguation_updated = false; + for (int disambiguation_iter = 0; + disambiguation_iter<2; + disambiguation_iter++) + { + Instruction_t *disambiguation_to_update = + disambiguations[disambiguation_iter]; + /* + * The pin pointing to the disambiguation is only ever a 5 byte pin. + */ + for( + std::map<UnresolvedPinned_t,RangeAddress_t>::iterator it=five_byte_pins.begin(); + it!=five_byte_pins.end() /*&& !sled_disambiguation*/; + it++ + ) + { + RangeAddress_t addr=(*it).second; + UnresolvedPinned_t up=(*it).first; + + cout << std::hex << up.getInstrution() << " 5b vs " << disambiguation_to_update << endl; + if (up.getInstrution() == disambiguation_to_update) + { + five_byte_pins.erase(it); + UnresolvedPinned_t cup(sled_disambiguation); + cup.SetUpdatedAddress(up.GetUpdatedAddress()); + five_byte_pins[cup] = addr; + + disambiguation_updated = true; + break; + } + } + } + assert(disambiguation_updated); + + existing_sled.Disambiguation(sled_disambiguation); +} + +RangeAddress_t ZiprPinnerX86_t::Do68Sled(RangeAddress_t addr) +{ + char jmp_rel32_bytes[]={(char)0xe9,(char)0,(char)0,(char)0,(char)0}; + const size_t nop_overhead=4; // space for nops. + const size_t jmp_overhead=sizeof(jmp_rel32_bytes); // space for nops. + const size_t sled_overhead = nop_overhead + jmp_overhead; + const int sled_size=Calc68SledSize(addr, sled_overhead); + Sled_t sled(memory_space, Range_t(addr,addr+sled_size), m_verbose); + set<Sled_t>::iterator sled_it; + + if (m_verbose) + cout << "Adding 68-sled at 0x" << std::hex << addr + << " size="<< std::dec << sled_size << endl; + + for (sled_it = m_sleds.begin(); + sled_it != m_sleds.end(); + sled_it++) + { + Sled_t sled_i = *sled_it; + if (sled_i.Overlaps(sled)) + { + if (m_verbose) + cout << "Found overlapping sled: " << sled_i << " and " << sled << endl; + + m_sleds.erase(sled_it); + Update68Sled(sled, sled_i); + m_sleds.insert(sled_i); + /* + * Return the final address of the updated sled. + */ + return sled_i.SledRange().getEnd(); + } + } + + + InsertJumpPoints68SledArea(sled); + + /* + * It's possible that the sled that we are going to put here + * is actually going to overwrite some pins and chain entries. + * So, we have to make sure to unreserve that space. + */ + Clear68SledArea(sled); + + /* Now, let's (speculatively) clear out the overhead space. + */ + if (m_verbose) + cout << "Clearing overhead space at " << std::hex + << "(" << addr+sled_size << "." + << addr+sled_size+sled_overhead << ")." << endl; + for (size_t i=0;i<sled_overhead;i++) + if (!memory_space.isByteFree(addr+sled_size+i)) + memory_space.mergeFreeRange(addr+sled_size+i); + + /* + * Put down the sled. + */ + for(auto i=0;i<sled_size;i++) + { + if (m_verbose) + cout << "Adding 68 at " + << std::hex << addr+i + << " for sled at 0x" + << std::hex << addr << endl; + assert(memory_space.isByteFree(addr+i)); + memory_space[addr+i]=0x68; + m_AddrInSled[addr+i] = true; + memory_space.splitFreeRange(addr+i); + } + /* + * Put down the NOPs + */ + for(size_t i=0;i<nop_overhead;i++) + { + if (m_verbose) + cout << "Adding 90 at " + << std::hex << addr+sled_size+i + << " for sled at 0x" + << std::hex << addr << endl; + + assert(memory_space.isByteFree(addr+sled_size+i)); + memory_space[addr+sled_size+i] = 0x90; + m_AddrInSled[addr+sled_size+i] = true; + memory_space.splitFreeRange(addr+sled_size+i); + } + + /* + * That brings us to the part that actually, you know, does + * the jump to the proper target depending upon where we + * landed. + */ + Instruction_t* sled_disambiguation=Emit68Sled(sled); + + if (m_verbose) + cout << "Generated sled_disambiguation (in Do68Sled()): " << std::hex << sled_disambiguation << endl; + + if (m_verbose) + cout << "Pin for 68-sled at 0x" + << std::hex << addr <<" is " + << std::hex << (addr+sled_size+nop_overhead) << endl; + + /* + * Reserve the bytes for the jump at the end of the sled that + * will take us to the (above) disambiguator. + */ + for(size_t i=0;i<jmp_overhead;i++) + { + assert(memory_space.isByteFree(addr+sled_size+nop_overhead+i)); + memory_space[addr+sled_size+nop_overhead+i]=jmp_rel32_bytes[i]; + memory_space.splitFreeRange(addr+sled_size+nop_overhead+i); + //m_AddrInSled[addr+sled_size+nop_overhead+i] = true; + } + + /* + * We know that the jmp we just put down is a two byte jump. + * We want it to point to the sled_disambiguation so we + * put a two byte pin down. This will affect the work of the + * loop above where we are putting down two byte pins and + * attempting to expand them through chaining. + */ + UnresolvedPinned_t cup(sled_disambiguation); + cup.SetUpdatedAddress(addr+sled_size+nop_overhead); + five_byte_pins[cup] = addr+sled_size+nop_overhead; + if (m_verbose) + cout << "Put in a five byte jmp to the disambiguation " << std::hex << sled_disambiguation << " at " << std::hex << addr+sled_size+nop_overhead << endl; + + sled.Disambiguation(sled_disambiguation); + + if (m_verbose) + cout << "Inserting sled: " << sled << endl; + m_sleds.insert(sled); + + return addr+sled_size+nop_overhead+jmp_overhead; +} + + +void ZiprPinnerX86_t::Clear68SledArea(Sled_t sled) +{ + for (std::set<RangeAddress_t>::iterator addr_iter = sled.JumpPointsBegin(); + addr_iter != sled.JumpPointsEnd(); + addr_iter++) + { + RangeAddress_t addr = *addr_iter; + size_t clear_size = 0; + if (m_verbose) + cout << "Testing " << std::hex << addr << endl; + if (!(sled.SledRange().getStart() <= addr && addr <= sled.SledRange().getEnd())) + { + if (m_verbose) + cout << std::hex << addr << " outside sled range." << endl; + continue; + } + if (FindPatchTargetAtAddr(addr)) + { + UnresolvedUnpinnedPatch_t uup = m_parent->FindPatch(addr); + clear_size = uup.second.getSize(); + + if (m_verbose) + cout << "Need to clear a " << std::dec << clear_size + << " byte chain entry at " << std::hex << (addr) << endl; + } + else if (FindPinnedInsnAtAddr(addr)) + { + std::map<RangeAddress_t, std::pair<IRDB_SDK::Instruction_t*, size_t> > + ::iterator pinned_it = m_InsnSizeAtAddrs.find(addr); + + assert(pinned_it != m_InsnSizeAtAddrs.end()); + + + clear_size = pinned_it->second.second; + + if (m_verbose) + cout << "Need to clear a " << std::dec << clear_size + << " byte pin at " << std::hex << (addr) << endl; + } + else + assert(false); + + clear_size = std::min(sled.SledRange().getEnd(), addr+clear_size) - addr; + if (m_verbose) + cout << "Need to clear " << std::dec << clear_size << " bytes." << endl; + + if (clear_size>0) + { + /* + * We do want to free this space, but only if it + * is already in use. + */ + if (!memory_space.isByteFree(addr)) + memory_space.mergeFreeRange(Range_t(addr, addr+clear_size)); + assert(memory_space.isByteFree(addr)); + } + } +} + +int ZiprPinnerX86_t::Calc68SledSize(RangeAddress_t addr, size_t sled_overhead) +{ + int sled_size=0; + while(true) + { + auto i=(size_t)0; + for(i=0;i<sled_overhead;i++) + { + if (FindPinnedInsnAtAddr(addr+sled_size+i)) + { + if (m_verbose) + cout << "Sled free space broken up by pin at " + << std::hex << (addr+sled_size+i) << endl; + break; + } + else if (FindPatchTargetAtAddr(addr+sled_size+i)) + { + if (m_verbose) + cout << "Sled free space broken up by chain entry at " + << std::hex << (addr+sled_size+i) << endl; + break; + } + else + { + if (m_verbose) + cout << "Sled free space at " << std::hex << (addr+sled_size+i) << endl; + } + } + // if i==sled_overhead, that means that we found 6 bytes in a row free + // in the previous loop. Thus, we can end the 68 sled. + // if i<sled_overhead, we found a in-use byte, and the sled must continue. + if(i==sled_overhead) + { + assert(sled_size>2); + return sled_size; + } + + // try a sled that's 1 bigger. + sled_size+=(i+1); + } + + // cannot reach here? + assert(0); + +} + +bool ZiprPinnerX86_t::IsPinFreeZone(RangeAddress_t addr, int size) +{ + for(int i=0;i<size;i++) + if(FindPinnedInsnAtAddr(addr+i)!=nullptr) + return false; + return true; +} + + + +void ZiprPinnerX86_t::ReservePinnedInstructions() +{ + set<UnresolvedPinned_t> reserved_pins; + + + /* first, for each pinned instruction, try to + * put down a jump for the pinned instruction + */ + for( auto it=unresolved_pinned_addrs.begin(); it!=unresolved_pinned_addrs.end(); ++it) + { + char bytes[]={(char)0xeb,(char)0}; // jmp rel8 + UnresolvedPinned_t up=*it; + const auto upinsn=up.getInstrution(); + auto addr=upinsn->getIndirectBranchTargetAddress()->getVirtualOffset(); + + if(upinsn->getIndirectBranchTargetAddress()->getFileID() == BaseObj_t::NOT_IN_DATABASE) + continue; + + /* sometimes, we need can't just put down a 2-byte jump into the old slot + * we may need to do alter our technique if there are two consecutive pinned addresses (e.g. 800 and 801). + * That case is tricky, as we can't put even a 2-byte jump instruction down. + * so, we attempt to pin any 1-byte instructions with no fallthrough (returns are most common) immediately. + * We may also attempt to pin any 1-byte insn that falls through to the next pinned address (nops are common). + * We may also pin multi-byte instructions that don't fall through. + */ + if(ShouldPinImmediately(upinsn)) + { + if (m_verbose) + cout << "Final pinning " << hex << addr << "-" << (addr+upinsn->getDataBits().size()-1) << endl; + + for(auto i=0u;i<upinsn->getDataBits().size();i++) + { + memory_space[addr+i]=upinsn->getDataBits()[i]; + memory_space.splitFreeRange(addr+i); + m_stats->total_other_space++; + } + + // record the final location of this instruction. + final_insn_locations[upinsn] = addr; + + // create a dollop for this instruction and place it. + auto dollop=m_dollop_mgr.addNewDollops(upinsn); + assert(dollop); + dollop->Place(addr); + + auto place_addr=addr; + for(auto de : *dollop) + { + de->Place(place_addr); + place_addr+=sizeof(de->getInstruction()->getDataBits().size()); + } + + continue; + } + + if (m_verbose) + { + printf("Working two byte pinning decision at %p for: ", (void*)addr); + printf("%s\n", upinsn->getComment().c_str()); + } + + + // if the byte at x+1 is free, we can try a 2-byte jump (which may end up being converted to a 5-byte jump later). + if (FindPinnedInsnAtAddr(addr+1)==nullptr) + { + /* so common it's not worth printing + if (m_verbose) + { + printf("Can fit two-byte pin (%p-%p). fid=%d\n", + (void*)addr, + (void*)(addr+sizeof(bytes)-1), + upinsn->getAddress()->getFileID()); + } + */ + + /* + * Assert that the space is free. We already checked that it should be + * with the FindPinnedInsnAtAddr, but just to be safe. + */ + for(auto i=0u;i<sizeof(bytes);i++) + { + assert(memory_space.find(addr+i) == memory_space.end() ); + memory_space[addr+i]=bytes[i]; + memory_space.splitFreeRange(addr+i); + } + // insert the 2-byte pin to be patched later. + up.SetRange(Range_t(addr, addr+2)); + two_byte_pins.insert(up); + } + // this is the case where there are two+ pinned bytes in a row start. + // check and implement the 2-in-a-row test + // The way this work is to put down this instruction: + // 68 --opcode for push 4-byte immed (addr+0) + // ww (addr+1) + // xx (addr+2) + // yy (addr+3) + // zz (addr+4) + // jmp L1 (addr+5 to addr+6) + // ... + // L1: lea rsp, [rsp+8] + // jmp dollop(addr) + // where ww,xx are un-specified here (later, they will become a 2-byte jump for the pin at addr+1, which will + // be handled in other parts of the code.) However, at a minimum, the bytes for the jmp l1 need to be free + // and there is little flexibility on the 68 byte, which specifies that ww-zz are an operand to the push. + // Thus, the jump is at a fixed location. So, bytes addr+5 and addr+6 must be free. Also, for the sake of simplicity, + // we will check that xx, yy and zz are free so that later handling of addr+1 is uncomplicated. + // This technique is refered to as a "push disambiguator" or sometimes a "push sled" for short. + else if (IsPinFreeZone(addr+2,5)) + { + if (m_verbose) + printf("Cannot fit two byte pin; Using 2-in-a-row workaround.\n"); + /* + * The whole workaround pattern is: + * 0x68 0xXX 0xXX 0xXX 0xXX (push imm) + * lea rsp, rsp-8 + * 0xeb 0xXX (jmp) + * + * We put the lea into the irdb and then + * put down a pin with that as the target. + * We put the original instruction as + * the fallthrough for the lea. + */ + char push_bytes[]={(char)0x68,(char)0x00, /* We do not actually write */ + (char)0x00,(char)0x00, /* all these bytes but they */ + (char)0x00}; /* make counting easier (see*/ + /* below). */ + Instruction_t *lea_insn = nullptr; + + if(m_firp->getArchitectureBitWidth()==64) + lea_insn = addNewAssembly(m_firp, nullptr, "lea rsp, [rsp+8]"); + else + lea_insn = addNewAssembly(m_firp, nullptr, "lea esp, [esp+4]"); + + m_firp->assembleRegistry(); + lea_insn->setFallthrough(upinsn); + + /* + * Write the push opcode. + * Do NOT reserve any of the bytes in the imm value + * since those are going to contain the two byte pin + * to the adjacent pinned address. + */ + memory_space[addr] = push_bytes[0]; + memory_space.splitFreeRange(addr); + + addr += sizeof(push_bytes); + + // reserve the bytes for the jump at the end of the push. + for(auto i=0u;i<sizeof(bytes);i++) + { + assert(memory_space.find(addr+i) == memory_space.end() ); + memory_space[addr+i]=bytes[i]; + memory_space.splitFreeRange(addr+i); + } + + if (m_verbose) + printf("Advanced addr to %p\n", (void*)addr); + + /* + * Insert a new UnresolvePinned_t that tells future + * loops that we are going to use an updated address + * to place this instruction. + * + * This is a type of fiction because these won't really + * be pins in the strict sense. But, it's close enough. + */ + UnresolvedPinned_t cup(lea_insn); + cup.SetUpdatedAddress(addr); + two_byte_pins.insert(cup); + } + // If, those bytes aren't free, we will default to a "68 sled". + // the main concept for a 68 sled is that all bytes will be 68 until we get to an opening where we can "nop out" of + // the sled, re-sync the instruction stream, and inspect the stack to see what happened. Here is an example with 7 pins in a row. + // 0x8000: 68 + // 0x8001: 68 + // 0x8002: 68 + // 0x8003: 68 + // 0x8004: 68 + // 0x8005: 68 + // 0x8006: 68 + // 0x8007: 90 + // 0x8008: 90 + // 0x8009: 90 + // 0x800a: 90 + // <resync stream>: at this point regardless of where (between 0x8000-0x8006 the program transfered control, + // execution will resynchronize. For example, if we jump to 0x8000, the stream will be + // push 68686868 + // push 68909090 + // nop + // <resync> + // But if we jump to 0x8006, our stream will be: + // push 90909090 + // <resync> + // Note that the top of stack will contain 68909090,68686868 if we jumped to 0x8000, but 0x90909090 if we jumped to 0x8006 + // After we resync, we have to inspect the TOS elements to see which instruction we jumped to. + else if (FindPinnedInsnAtAddr(addr+1)) + { + const auto end_of_sled=Do68Sled(addr); + + // skip over some entries until we get passed the sled. + while (true) + { + // get this entry + const auto up=*it; + const auto upinsn=up.getInstrution(); + auto addr=upinsn->getIndirectBranchTargetAddress() ->getVirtualOffset(); + + // is the entry within the sled? + if(addr>=end_of_sled) + // nope, skip out of this while loop + break; + // inc the iterator so the for loop will continue at the right place. + ++it; + + /* + * It's possible that this pin is going to be the last + * one in the program. TODO: If this instruction abuts the + * end of the program's address space then there + * could be a problem. As of now, we assume that + * this is not the case. + */ + if (it==unresolved_pinned_addrs.end()) + break; + } + // back up one, because the last one still needs to be processed. + --it; + + // resolve any new instructions added for the sled. + m_firp->assembleRegistry(); + } + else + assert(0); // impossible to reach, right? + } +} + +void ZiprPinnerX86_t::ExpandPinnedInstructions() +{ + /* now, all insns have 2-byte pins. See which ones we can make 5-byte pins */ + + for( + set<UnresolvedPinned_t>::iterator it=two_byte_pins.begin(); + it!=two_byte_pins.end(); + ) + { + UnresolvedPinned_t up=*it; + Instruction_t* upinsn=up.getInstrution(); + RangeAddress_t addr=0; + + /* + * This is possible if we moved the address + * forward because we had consecutive pinned + * instructions and had to apply the workaround. + */ + if (up.HasUpdatedAddress()) + { + addr = up.GetUpdatedAddress(); + } + else + { + addr = upinsn->getIndirectBranchTargetAddress()->getVirtualOffset(); + } + + if (m_AddrInSled[addr]) + { + /* + * There is no need to consider this pin at all! It was + * subsumed w/in a sled which has already handled it. + * Move along. + */ + if (m_verbose) + cout << "Two byte pin at 0x" + << std::hex << addr + << " is w/in a sled ... skipping and erasing." << endl; + two_byte_pins.erase(it++); + continue; + } + + char bytes[]={(char)0xe9,(char)0,(char)0,(char)0,(char)0}; // jmp rel8 + bool can_update=memory_space.areBytesFree(addr+2,sizeof(bytes)-2); + if (m_verbose && can_update) + printf("Found %p can be updated to 5-byte jmp\n", (void*)addr); + + can_update &= !m_AddrInSled[up.GetRange().getStart()]; + if (m_verbose && can_update && m_AddrInSled[up.GetRange().getStart()]) + printf("%p was already fixed into a sled. Cannot update.\n", (void*)addr); + if(can_update) + { + memory_space.plopJump(addr); + + /* + * Unreserve those bytes that we reserved before! + */ + for (auto j = up.GetRange().getStart(); j<up.GetRange().getEnd(); j++) + { + if (!m_AddrInSled[j]) + memory_space.mergeFreeRange(j); + } + + /* + * We have a chain entry prereserved and we want to get + * rid of it now! + */ + if (m_verbose) + cout << "Erasing chain entry at 0x" + << std::hex << up.GetRange().getStart() << endl; + m_parent->RemovePatch(up.GetRange().getStart()); + + + up.SetRange(Range_t(0,0)); + five_byte_pins[up]=addr; + m_InsnSizeAtAddrs[addr] = std::pair<Instruction_t*, size_t>( + m_InsnSizeAtAddrs[addr].first, 5); + two_byte_pins.erase(it++); + m_stats->total_5byte_pins++; + m_stats->total_trampolines++; + } + else + { + ++it; + if (m_verbose) + printf("Found %p can NOT be updated to 5-byte jmp\n", (void*)addr); + m_stats->total_2byte_pins++; + m_stats->total_trampolines++; + m_stats->total_tramp_space+=2; + } + } + + printf("Totals: 2-byters=%d, 5-byters=%d\n", (int)two_byte_pins.size(), (int)five_byte_pins.size()); +} + + +void ZiprPinnerX86_t::Fix2BytePinnedInstructions() +{ + for( + set<UnresolvedPinned_t>::const_iterator it=two_byte_pins.begin(); + it!=two_byte_pins.end(); + ) + { + UnresolvedPinned_t up=*it; + Instruction_t* upinsn=up.getInstrution(); + RangeAddress_t addr; + + if (up.HasUpdatedAddress()) + { + addr = up.GetUpdatedAddress(); + } + else + { + addr=upinsn->getIndirectBranchTargetAddress()->getVirtualOffset(); + } + + /* + * This might have already been handled in a sled. + */ + if (m_AddrInSled[addr]) + { + if (m_verbose) + cout << "Skipping two byte pin at " + << std::hex << addr << " because it is in a sled." << endl; + /* + * If there are some reserved bytes for this then + * we want to unreserve it (but only if it is not + * in a sled itself since it would not be good to + * imply that space is now open). + */ + if (up.HasRange()) + { + for (auto j = up.GetRange().getStart(); j<up.GetRange().getEnd(); j++) + { + if (!m_AddrInSled[j]) + memory_space.mergeFreeRange(j); + } + } + two_byte_pins.erase(it++); + continue; + } + + if (up.HasRange()) + { + /* + * Always clear out the previously reserved space. + * Do this here because some/most of the algorithms + * that we use below assume that it is unreserved. + */ + for (auto j = up.GetRange().getStart(); j<up.GetRange().getEnd(); j++) + { + if (!m_AddrInSled[j]) + memory_space.mergeFreeRange(j); + } + + if (m_AddrInSled[up.GetRange().getStart()]) + { + if (m_verbose) + printf("Using previously reserved spot of 2-byte->x-byte conversion " + "(%p-%p)->(%p-%p) (orig: %p) because it was subsumed under sled\n", + (void*)addr, + (void*)(addr+1), + (void*)(up.GetRange().getStart()), + (void*)(up.GetRange().getEnd()), + (upinsn->getIndirectBranchTargetAddress() != nullptr) ? + (void*)(uintptr_t)upinsn->getIndirectBranchTargetAddress()->getVirtualOffset() : 0x0); + + /* + * We simply patch the jump to this target and do not add + * it to any list for further processing. We are done. + */ + PatchJump(addr, up.GetRange().getStart()); + two_byte_pins.erase(it++); + } + else if (up.GetRange().is5ByteRange()) + { + if (m_verbose) + printf("Using previously reserved spot of 2-byte->5-byte conversion " + "(%p-%p)->(%p-%p) (orig: %p)\n", + (void*)addr, + (void*)(addr+1), + (void*)(up.GetRange().getStart()), + (void*)(up.GetRange().getEnd()), + (upinsn->getIndirectBranchTargetAddress() != nullptr) ? + (void*)(uintptr_t)upinsn->getIndirectBranchTargetAddress()->getVirtualOffset() : 0x0); + + five_byte_pins[up] = up.GetRange().getStart(); + memory_space.plopJump(up.GetRange().getStart()); + PatchJump(addr, up.GetRange().getStart()); + + two_byte_pins.erase(it++); + } + else if (up.HasRange() && up.GetRange().is2ByteRange()) + { + /* + * Add jump to the reserved space. + * Make an updated up that has a new + * "addr" so that addr is handled + * correctly the next time through. + * + * Ie tell two_byte_pins list that + * the instruction is now at the jump + * target location. + */ + UnresolvedPinned_t new_up = + UnresolvedPinned_t(up.getInstrution()); + new_up.SetUpdatedAddress(up.GetRange().getStart()); + new_up.SetRange(up.GetRange()); + + char bytes[]={(char)0xeb,(char)0}; // jmp rel8 + for(unsigned int i=0;i<sizeof(bytes);i++) + { + assert(memory_space.find(up.GetRange().getStart()+i) == memory_space.end() ); + memory_space[up.GetRange().getStart()+i]=bytes[i]; + memory_space.splitFreeRange(up.GetRange().getStart()+i); + assert(!memory_space.isByteFree(up.GetRange().getStart()+i)); + } + + if (m_verbose) + printf("Patching 2 byte to 2 byte: %p to %p (orig: %p)\n", + (void*)addr, + (void*)up.GetRange().getStart(), + (void*)(uintptr_t)upinsn->getIndirectBranchTargetAddress()->getVirtualOffset()); + + PatchJump(addr, up.GetRange().getStart()); + + two_byte_pins.erase(it++); + two_byte_pins.insert(new_up); + } + } + else + { + printf("FATAL: Two byte pin without reserved range: %p\n", (void*)addr); + assert(false); + it++; + } + } +} + +void ZiprPinnerX86_t::OptimizePinnedInstructions() +{ + + // should only be 5-byte pins by now. + + assert(two_byte_pins.size()==0); + + + for( + std::map<UnresolvedPinned_t,RangeAddress_t>::iterator it=five_byte_pins.begin(); + it!=five_byte_pins.end(); + ) + { + RangeAddress_t addr=(*it).second; + UnresolvedPinned_t up=(*it).first; + + // ideally, we'll try to fill out the pinned 5-byte jump instructions with actual instructions + // from the program. That's an optimization. At the moment, let's just create a patch for each one. + + if (m_AddrInSled[addr]) + { + if (m_verbose) + cout << "Skipping five byte pin at " + << std::hex << addr << " because it is in a sled." << endl; + it++; + continue; + } + + UnresolvedUnpinned_t uu(up.getInstrution()); + Patch_t thepatch(addr,UncondJump_rel32); + m_parent->AddPatch(uu,thepatch); + memory_space.plopJump(addr); + + + bool can_optimize=false; // fixme + if(can_optimize) + { + //fixme + } + else + { + if (m_verbose) + { + //DISASM d; + //Disassemble(uu.getInstrution(),d); + auto d=DecodedInstruction_t::factory(uu.getInstrution()); + printf("Converting 5-byte pinned jump at %p-%p to patch to %d:%s\n", + (void*)addr,(void*)(addr+4), uu.getInstrution()->getBaseID(), d->getDisassembly().c_str()/*.CompleteInstr*/); + } + m_stats->total_tramp_space+=5; + } + + // remove and move to next pin + five_byte_pins.erase(it++); + } + +} + + + +Instruction_t *ZiprPinnerX86_t::FindPinnedInsnAtAddr(RangeAddress_t addr) +{ + std::map<RangeAddress_t,std::pair<IRDB_SDK::Instruction_t*, size_t> >::iterator it=m_InsnSizeAtAddrs.find(addr); + if(it!=m_InsnSizeAtAddrs.end()) + return it->second.first; + return nullptr; +} + + + + + + diff --git a/zipr/src/plugin_man.cpp b/zipr/src/plugin_man.cpp new file mode 100644 index 0000000000000000000000000000000000000000..e5345e6596107cb830ce8ada0d092ce89a906a74 --- /dev/null +++ b/zipr/src/plugin_man.cpp @@ -0,0 +1,307 @@ +/*************************************************************************** + * Copyright (c) 2014 Zephyr Software LLC. All rights reserved. + * + * This software is furnished under a license and/or other restrictive + * terms and may be used and copied only in accordance with such terms + * and the inclusion of the above copyright notice. This software or + * any other copies thereof may not be provided or otherwise made + * available to any other person without the express written consent + * of an authorized representative of Zephyr Software LCC. Title to, + * ownership of, and all rights in the software is retained by + * Zephyr Software LCC. + * + * Zephyr Software LLC. Proprietary Information + * + * Unless otherwise specified, the information contained in this + * directory, following this legend, and/or referenced herein is + * Zephyr Software LLC. (Zephyr) Proprietary Information. + * + * CONTACT + * + * For technical assistance, contact Zephyr Software LCC. at: + * + * + * Zephyr Software, LLC + * 2040 Tremont Rd + * Charlottesville, VA 22911 + * + * E-mail: jwd@zephyr-software.com + **************************************************************************/ + +#include <zipr_all.h> + +#include <dlfcn.h> +#include <sys/types.h> +#include <dirent.h> +#include <errno.h> +#include <vector> +#include <string> +#include <iostream> + + +using namespace std; +using namespace Zipr_SDK; +using namespace IRDB_SDK; +using namespace zipr; + + +#define dispatch_to(func) \ +for(DLFunctionHandleSet_t::iterator it=m_handleList.begin(); it!=m_handleList.end();++it) \ +{ \ + ZiprPluginInterface_t* zpi=(ZiprPluginInterface_t*)*it; \ + zpi->func(); \ +} + +#define dispatch_to_with_var(func, var) \ +for(DLFunctionHandleSet_t::iterator it=m_handleList.begin(); it!=m_handleList.end();++it) \ +{ \ + ZiprPluginInterface_t* zpi=(ZiprPluginInterface_t*)*it; \ + var=zpi->func(var); \ +} + +#define ALLOF(a) begin(a),end(a) + + + +/* + * Convert a bash PATH-like string into a vector of strings. + * by default, delimintor is : + * Returns the vector of strings + */ +const vector<string>& splitVar(const string& toSplit , const char delimiter = ':') +{ + static vector<string> result; + if( !result.empty() ) + return result; + if( toSplit.empty() ) + throw runtime_error( "toSplit should not be empty" ); + + auto previous = size_t(0); + auto index = toSplit.find( delimiter ); + while( index != string::npos ) + { + result.push_back( toSplit.substr(previous, index-previous)); + previous=index+1; + index = toSplit.find( delimiter, previous ); + } + result.push_back( toSplit.substr(previous) ); + + return result; +} + +/* + * sort_plugins_by_name() + * + * Use this function as the comparator for sorting + * the plugins by the name that they return in their + * toString() method. Sorting plugins by name is + * useful when trying to debug problems that depend + * on specific ordering. + */ +bool sort_plugins_by_name(DLFunctionHandle_t a, DLFunctionHandle_t b) +{ + return a->toString() < b->toString(); +} + +void ZiprPluginManager_t::PinningBegin() +{ + dispatch_to(doPinningBegin); +} + +void ZiprPluginManager_t::PinningEnd() +{ + dispatch_to(doPinningEnd); +} + +void ZiprPluginManager_t::DollopBegin() +{ + dispatch_to(doDollopBegin); +} + +void ZiprPluginManager_t::DollopEnd() +{ + dispatch_to(doDollopEnd); +} +void ZiprPluginManager_t::CallbackLinkingBegin() +{ + dispatch_to(doCallbackLinkingBegin); +} +void ZiprPluginManager_t::CallbackLinkingEnd() +{ + dispatch_to(doCallbackLinkingEnd); +} + +RangeAddress_t ZiprPluginManager_t::PlaceScoopsBegin(const RangeAddress_t max_addr) +{ + RangeAddress_t ret=max_addr; + dispatch_to_with_var(doPlaceScoopsBegin,ret); + return ret; +} + +RangeAddress_t ZiprPluginManager_t::PlaceScoopsEnd(const RangeAddress_t max_addr) +{ + RangeAddress_t ret=max_addr; + dispatch_to_with_var(doPlaceScoopsEnd,ret); + return ret; +} + + +bool ZiprPluginManager_t::DoesPluginAddress(const Zipr_SDK::Dollop_t *dollop, const RangeAddress_t &source, Range_t &place, bool &coalesce, bool &fallthrough_allowed, DLFunctionHandle_t &placer) +{ + DLFunctionHandleSet_t::iterator it=m_handleList.begin(); + for(m_handleList.begin();it!=m_handleList.end();++it) + { + ZiprPluginInterface_t* zpi=(ZiprPluginInterface_t*)*it; + if (Must == zpi->addressDollop(dollop, source, place, coalesce, fallthrough_allowed)) + { + placer = zpi; + return true; + } + } + return false; +} + +bool ZiprPluginManager_t::DoPluginsPlop(Instruction_t *insn, std::list<DLFunctionHandle_t> &callbacks) +{ + bool a_plugin_does_plop = false; + DLFunctionHandleSet_t::iterator it=m_handleList.begin(); + for(m_handleList.begin();it!=m_handleList.end();++it) + { + ZiprPluginInterface_t* zpi=(ZiprPluginInterface_t*)*it; + if (zpi->willPluginPlop(insn)) + { + callbacks.push_back(zpi); + a_plugin_does_plop = true; + } + } + return a_plugin_does_plop; +} + +bool ZiprPluginManager_t::DoesPluginRetargetCallback(const RangeAddress_t &callback_addr, const Zipr_SDK::DollopEntry_t *callback_entry, RangeAddress_t &target_address, DLFunctionHandle_t &patcher) +{ + DLFunctionHandleSet_t::iterator it=m_handleList.begin(); + for(m_handleList.begin();it!=m_handleList.end();++it) + { + ZiprPluginInterface_t* zpi=(ZiprPluginInterface_t*)*it; + if(Must==zpi->retargetCallback(callback_addr,callback_entry,target_address)) + { + patcher = zpi; + return true; + } + } + return false; +} + +bool ZiprPluginManager_t::DoesPluginRetargetPin(const RangeAddress_t &patch_addr, const Zipr_SDK::Dollop_t *target_dollop, RangeAddress_t &target_address, DLFunctionHandle_t &patcher) +{ + for(const auto& zpi : m_handleList) + { + if (Must == zpi->retargetPin(patch_addr, target_dollop, target_address)) + { + patcher = zpi; + return true; + } + } + return false; +} + +void ZiprPluginManager_t::open_plugins + ( + Zipr_SDK::Zipr_t* zipr_obj, + Zipr_SDK::ZiprOptionsManager_t *p_opts + ) +{ + const auto ziprPluginDirs=splitVar(getenv("ZIPR_PLUGIN_PATH")); + auto loadedBasenames = set<string>(); + for(const auto dir : ziprPluginDirs) + { + /* skip empty directories in list. */ + if(dir == "" ) + continue; + + const auto dp = opendir(dir.c_str()); + if(dp == nullptr) + { + cout << "Error(" << errno << ") opening plugins directory: " << dir << endl; + exit(1); + } + + auto dirp = (dirent*) nullptr; + while ((dirp = readdir(dp)) != nullptr) + { + const auto basename = string(dirp->d_name); + if(loadedBasenames.find(basename) != loadedBasenames.end()) + { + if (m_verbose) + cout<<"Already loaded "<<basename<<". Skipping..."<<endl; + continue; + + } + loadedBasenames.insert(basename); + + const auto name=dir+'/'+basename; + const auto zpi=string(".zpi"); + const auto extension=name.substr(name.size() - zpi.length()); + + // Automatically skip cwd and pwd entries. + if(basename == "." || basename == "..") + continue; + + if (extension!=zpi) + { + cout<<"File ("<<name<<") does not have proper extension, skipping."<<endl; + continue; // try next file + } + + // test if this library is already loaded, can happen when + // an entry in ZIPR_PLUGIN_PATH is duplicated. + const auto isLoaded = dlopen(name.c_str(), RTLD_LAZY|RTLD_GLOBAL|RTLD_NOLOAD) != nullptr; + if(isLoaded) + { + if (m_verbose) + cout<<"File ("<<name<<") already loaded."<<endl; + continue; + } + + if (m_verbose) + cout<<"Attempting load of file ("<<name<<")."<<endl; + + // actuall load + const auto handle=dlopen(name.c_str(), RTLD_LAZY|RTLD_GLOBAL); + if(!handle) + { + cerr<<"Failed to open file ("<<name<<"), error code: "<<dlerror()<<endl; + exit(1); + } + dlerror(); + + const auto sym=dlsym(handle,"GetPluginInterface"); + if(!sym) + { + cerr<<"Failed to find GetPluginInterface from file ("<<name<<"), error code: "<<dlerror()<<endl; + exit(1); + } + + const auto my_GetPluginInterface= (GetPluginInterface_t)sym; + const auto interface = (*my_GetPluginInterface)(zipr_obj); + + if(!interface) + { + cerr<<"Failed to get interface from file ("<<name<<")"<<endl; + exit(1); + } + + // constructors now register options + // auto global_ns = p_opts->getNamespace("global"); + // p_opts->addNamespace(interface->registerOptions(global_ns)); + + m_handleList.push_back(interface); + + } + closedir(dp); + + } + std::sort(ALLOF(m_handleList), sort_plugins_by_name); + + return; +} diff --git a/zipr/src/range.cpp b/zipr/src/range.cpp new file mode 100644 index 0000000000000000000000000000000000000000..89003c11f15f1c0db38da745bf46aa393eb0d02e --- /dev/null +++ b/zipr/src/range.cpp @@ -0,0 +1,59 @@ +#include <zipr-sdk> + +using namespace Zipr_SDK; + +Zipr_SDK::Range_t::Range_t(RangeAddress_t p_s, RangeAddress_t p_e) + : + m_start(p_s), + m_end(p_e) +{ +} + +Zipr_SDK::Range_t::Range_t() : m_start(0), m_end(0) +{ +} + +Zipr_SDK::Range_t::~Range_t() +{ +} + +RangeAddress_t Zipr_SDK::Range_t::getStart() const +{ + return m_start; +} + +RangeAddress_t Zipr_SDK::Range_t::getEnd() const +{ + return m_end; +} + +void Zipr_SDK::Range_t::setStart(RangeAddress_t s) +{ + m_start=s; +} + +void Zipr_SDK::Range_t::setEnd(RangeAddress_t e) +{ + m_end=e; +} + +bool Zipr_SDK::Range_t::is2ByteRange() const +{ + return (m_end - m_start) == 2; +}; + +bool Zipr_SDK::Range_t::is5ByteRange() const +{ + return (m_end - m_start) == 5; +}; + +bool Zipr_SDK::Range_t::isInfiniteRange() const +{ + return m_end==(RangeAddress_t)-1; +}; + +// Caution! Only works for NON-OVERLAPPING ranges. +bool Zipr_SDK::Range_tCompare::operator() (const Range_t first, const Range_t second) const +{ + return first.getEnd() < second.getStart(); +} diff --git a/zipr/src/sizer_arm32.cpp b/zipr/src/sizer_arm32.cpp new file mode 100644 index 0000000000000000000000000000000000000000..40f57f9300601209b367331f0592714edefa9634 --- /dev/null +++ b/zipr/src/sizer_arm32.cpp @@ -0,0 +1,40 @@ +#include <zipr_all.h> + +namespace zipr +{ +#include <sizer/sizer_arm32.hpp> +} + + +using namespace zipr ; +using namespace IRDB_SDK; + + +ZiprSizerARM32_t::ZiprSizerARM32_t(Zipr_SDK::Zipr_t* p_zipr_obj) : ZiprSizerBase_t(p_zipr_obj,4,4,4,4,4) +{ +} + +size_t ZiprSizerARM32_t::DetermineInsnSize(Instruction_t* insn, bool account_for_jump) const +{ + // need 4 bytes for insn, plus 4 bytes for a jump + const auto size = 4u; + const auto jmp_size = account_for_jump ? 4 : 0; + return size + jmp_size; +} + +RangeAddress_t ZiprSizerARM32_t::PlopDollopEntryWithTarget( + Zipr_SDK::DollopEntry_t *entry, + RangeAddress_t override_place, + RangeAddress_t override_target) const +{ + const auto addr = (override_place == 0) ? entry->getPlace() : override_place; + const auto target_addr = (override_target == 0) ? entry->getTargetDollop()->getPlace() : override_target; + const auto insn = entry->getInstruction(); + + // plop instruction an d make it target the right address. + memory_space.PlopBytes(addr, insn->getDataBits().c_str(), 4); + m_zipr_obj.GetPatcher()->ApplyPatch(addr, target_addr); + + return addr+4; +} + diff --git a/zipr/src/sizer_arm64.cpp b/zipr/src/sizer_arm64.cpp new file mode 100644 index 0000000000000000000000000000000000000000..e439e9cc9bf79584efb87a85c6b91f8bf9e71e9c --- /dev/null +++ b/zipr/src/sizer_arm64.cpp @@ -0,0 +1,92 @@ +#include <zipr_all.h> + +namespace zipr +{ +#include <sizer/sizer_arm64.hpp> +} + + +using namespace zipr ; +using namespace IRDB_SDK; + + +static bool is_tbz_type(Instruction_t* insn) +{ + const auto d=DecodedInstruction_t::factory(insn); + return d->getMnemonic()=="tbz" || d->getMnemonic()=="tbnz"; + +} + +ZiprSizerARM64_t::ZiprSizerARM64_t(Zipr_SDK::Zipr_t* p_zipr_obj) : ZiprSizerBase_t(p_zipr_obj,4,4,4,4,4) +{ +} + +size_t ZiprSizerARM64_t::DetermineInsnSize(Instruction_t* insn, bool account_for_jump) const +{ + // tbz plan needs 12 bytes. other plans need 4 + const auto size = is_tbz_type(insn) ? 12 : 4; + const auto jmp_size= account_for_jump ? 4 : 0; + return size + jmp_size; +} + +RangeAddress_t ZiprSizerARM64_t::PlopDollopEntryWithTarget( + Zipr_SDK::DollopEntry_t *entry, + RangeAddress_t override_place, + RangeAddress_t override_target) const +{ + assert(entry->getTargetDollop()); + if(is_tbz_type(entry->getInstruction())) + return TBZPlopDollopEntryWithTarget(entry,override_place,override_target); + return DefaultPlopDollopEntryWithTarget(entry,override_place,override_target); +} + +RangeAddress_t ZiprSizerARM64_t::TBZPlopDollopEntryWithTarget( + Zipr_SDK::DollopEntry_t *entry, + RangeAddress_t override_place, + RangeAddress_t override_target) const +{ +/* tbz plan: + * L0 tbz <args>, L2 + * L1 b L3 + * L2: b <target> + * L3: # fallthrough + */ + const auto addr = (override_place == 0) ? entry->getPlace() : override_place; + const auto target_addr = (override_target == 0) ? entry->getTargetDollop()->getPlace() : override_target; + const auto insn = entry->getInstruction(); + const auto branch_bytes=string("\x00\x00\x00\x014",4); + + // put the tbz first. + memory_space.PlopBytes(addr, insn->getDataBits().c_str(), 4); + m_zipr_obj.GetPatcher()->ApplyPatch(addr,addr+8);// make it jump to L2 + + // now drop down a uncond jump for L1, and make it go to L3 + // (i.e., jump around the jump to the target) + memory_space.PlopBytes(addr+4, branch_bytes.c_str(), 4); + m_zipr_obj.GetPatcher()->ApplyPatch(addr+4,addr+12);// make it jump to +8 + + // lastly, put down the uncond jump at L2, and make it go to the target + memory_space.PlopBytes(addr+8, branch_bytes.c_str(), 4); + m_zipr_obj.GetPatcher()->ApplyPatch(addr+8,target_addr);// make it jump to +8 + + return addr+12; /* address of fallthrough, L3 */ + +} + + +RangeAddress_t ZiprSizerARM64_t::DefaultPlopDollopEntryWithTarget( + Zipr_SDK::DollopEntry_t *entry, + RangeAddress_t override_place, + RangeAddress_t override_target) const +{ + const auto addr = (override_place == 0) ? entry->getPlace() : override_place; + const auto target_addr = (override_target == 0) ? entry->getTargetDollop()->getPlace() : override_target; + const auto insn = entry->getInstruction(); + + // plop instruction an d make it target the right address. + memory_space.PlopBytes(addr, insn->getDataBits().c_str(), 4); + m_zipr_obj.GetPatcher()->ApplyPatch(addr, target_addr); + + return addr+4; +} + diff --git a/zipr/src/sizer_base.cpp b/zipr/src/sizer_base.cpp new file mode 100644 index 0000000000000000000000000000000000000000..31b53db04369ed071bf0ca2ea1f2ebc97552ad8b --- /dev/null +++ b/zipr/src/sizer_base.cpp @@ -0,0 +1,95 @@ + + +#include <zipr_all.h> + +namespace zipr +{ +#include <sizer/sizer_x86.hpp> +#include <sizer/sizer_arm64.hpp> +#include <sizer/sizer_arm32.hpp> +#include <sizer/sizer_mips32.hpp> +} +#include <memory> + +using namespace std; +using namespace IRDB_SDK; +using namespace zipr; + + +unique_ptr<ZiprSizerBase_t> ZiprSizerBase_t::factory(Zipr_SDK::Zipr_t* p_zipr_obj) +{ + auto l_firp=p_zipr_obj->getFileIR(); + auto ret= l_firp->getArchitecture()->getMachineType() == admtX86_64 ? (ZiprSizerBase_t*)new ZiprSizerX86_t (p_zipr_obj) : + l_firp->getArchitecture()->getMachineType() == admtI386 ? (ZiprSizerBase_t*)new ZiprSizerX86_t (p_zipr_obj) : + l_firp->getArchitecture()->getMachineType() == admtAarch64 ? (ZiprSizerBase_t*)new ZiprSizerARM64_t(p_zipr_obj) : + l_firp->getArchitecture()->getMachineType() == admtArm32 ? (ZiprSizerBase_t*)new ZiprSizerARM32_t(p_zipr_obj) : + l_firp->getArchitecture()->getMachineType() == admtMips32 ? (ZiprSizerBase_t*)new ZiprSizerMIPS32_t(p_zipr_obj) : + throw domain_error("Cannot init architecture"); + + return unique_ptr<ZiprSizerBase_t>(ret); +} + +ZiprSizerBase_t::ZiprSizerBase_t(Zipr_SDK::Zipr_t* p_zipr_obj, + const size_t p_CALLBACK_TRAMPOLINE_SIZE, + const size_t p_TRAMPOLINE_SIZE, + const size_t p_LONG_PIN_SIZE, + const size_t p_SHORT_PIN_SIZE, + const size_t p_ALIGNMENT + ) : + memory_space(*dynamic_cast<zipr::ZiprMemorySpace_t*>(p_zipr_obj->getMemorySpace())), + m_zipr_obj(*dynamic_cast<zipr::ZiprImpl_t*>(p_zipr_obj)), + CALLBACK_TRAMPOLINE_SIZE(p_CALLBACK_TRAMPOLINE_SIZE), + TRAMPOLINE_SIZE (p_TRAMPOLINE_SIZE ), + LONG_PIN_SIZE (p_LONG_PIN_SIZE ), + SHORT_PIN_SIZE (p_SHORT_PIN_SIZE ), + ALIGNMENT (p_ALIGNMENT ) +{ +} + + +Range_t ZiprSizerBase_t::DoPlacement(const size_t size) const +{ + auto new_place=memory_space.getFreeRange(size+ALIGNMENT-1); + auto aligned_start=(new_place.getStart()+ALIGNMENT-1)&~(ALIGNMENT-1); + return { aligned_start, new_place.getEnd() }; +} + + +size_t ZiprSizerBase_t::DetermineDollopSizeInclFallthrough(Zipr_SDK::Dollop_t *dollop) const +{ + auto fallthroughs_wcds = 0; + auto fallthrough_it = dollop; + for ( /* empty */ ; fallthrough_it != nullptr; fallthrough_it = fallthrough_it->getFallthroughDollop()) + { + if (fallthrough_it->isPlaced()) + /* + * We are going to stop calculating when + * we see that we'll hit a dollop that + * is already placed. + */ + break; + + fallthroughs_wcds += fallthrough_it->getSize(); + /* + * For every dollop that we consolidate, + * we will lose TRAMPOLINE_SIZE bytes by + * not having to jump to the fallthrough. + * That space is included in getSize() + * result, so we subtract it here. + */ + if (fallthrough_it->getFallthroughDollop()) + fallthroughs_wcds -= TRAMPOLINE_SIZE; + } + /* + * If there is a fallthrough_it, that means + * that the fallthrough dollop would not be + * placed again. Instead, we would jump to + * it. So, we add back in a trampoline. + */ + if (fallthrough_it) + fallthroughs_wcds += TRAMPOLINE_SIZE; + + return fallthroughs_wcds; +} + + diff --git a/zipr/src/sizer_mips32.cpp b/zipr/src/sizer_mips32.cpp new file mode 100644 index 0000000000000000000000000000000000000000..e71c30dc5ff92764cf6d8392c8ac438b2018da72 --- /dev/null +++ b/zipr/src/sizer_mips32.cpp @@ -0,0 +1,46 @@ +#include <zipr_all.h> + +namespace zipr +{ +#include <sizer/sizer_mips32.hpp> +} + + +using namespace zipr ; +using namespace IRDB_SDK; + +#define ALLOF(a) begin(a),end(a) + +const auto is_delay_slot_reloc = [](const Relocation_t* reloc) -> bool { return reloc->getType() == "delay_slot1"; } ; + + +ZiprSizerMIPS32_t::ZiprSizerMIPS32_t(Zipr_SDK::Zipr_t* p_zipr_obj) : ZiprSizerBase_t(p_zipr_obj,8,8,8,8,4) +{ +} + +size_t ZiprSizerMIPS32_t::DetermineInsnSize(Instruction_t* insn, bool account_for_jump) const +{ + const auto is_branch = DecodedInstruction_t::factory(insn)->isBranch(); + // need 4 bytes for insn, plus 4 bytes for a jump + const auto size = 4u; + const auto delay_slot_size = is_branch ? 4u : 0u; + const auto jmp_size = account_for_jump ? 8u + delay_slot_size : 0u; + return size + jmp_size ; +} + +RangeAddress_t ZiprSizerMIPS32_t::PlopDollopEntryWithTarget( + Zipr_SDK::DollopEntry_t *entry, + RangeAddress_t override_place, + RangeAddress_t override_target) const +{ + const auto addr = (override_place == 0) ? entry->getPlace() : override_place; + const auto target_addr = (override_target == 0) ? entry->getTargetDollop()->getPlace() : override_target; + const auto insn = entry->getInstruction(); + + // plop instruction an d make it target the right address. + memory_space.PlopBytes(addr, insn->getDataBits().c_str(), 4); + m_zipr_obj.GetPatcher()->ApplyPatch(addr, target_addr); + + return addr+4; +} + diff --git a/zipr/src/sizer_x86.cpp b/zipr/src/sizer_x86.cpp new file mode 100644 index 0000000000000000000000000000000000000000..bdb0c7119a6ede2f45fa012ea7b8e64c007857b7 --- /dev/null +++ b/zipr/src/sizer_x86.cpp @@ -0,0 +1,206 @@ +#include <zipr_all.h> + +namespace zipr +{ +#include <sizer/sizer_x86.hpp> +} + +using namespace zipr ; +using namespace IRDB_SDK; + + +ZiprSizerX86_t::ZiprSizerX86_t(Zipr_SDK::Zipr_t* p_zipr_obj) : ZiprSizerBase_t(p_zipr_obj,10,5,2,5,1) +{ +} + +size_t ZiprSizerX86_t::DetermineInsnSize(Instruction_t* insn, bool account_for_jump) const +{ + + int required_size=0; + + switch(insn->getDataBits()[0]) + { + case (char)0x70: + case (char)0x71: + case (char)0x72: + case (char)0x73: + case (char)0x74: + case (char)0x75: + case (char)0x76: + case (char)0x77: + case (char)0x78: + case (char)0x79: + case (char)0x7a: + case (char)0x7b: + case (char)0x7c: + case (char)0x7d: + case (char)0x7e: + case (char)0x7f: + { + // two byte JCC -> 6byte JCC + required_size=6; + break; + } + + case (char)0xeb: + { + // two byte JMP -> 5byte JMP + required_size=5; + break; + } + + case (char)0xe0: + case (char)0xe1: + case (char)0xe2: + case (char)0xe3: + { + // loop, loopne, loopeq, jecxz + // convert to: + // <op> +5: + // jmp fallthrough + // +5: jmp target + // 2+5+5; + required_size=12; + break; + } + + + default: + { + required_size=insn->getDataBits().size(); + if (insn->getCallback()!="") required_size=CALLBACK_TRAMPOLINE_SIZE; + break; + } + } + + // add an extra 5 for a "trampoline" in case we have to end this fragment early + if (account_for_jump) + return required_size+TRAMPOLINE_SIZE; + else + return required_size; +} + +RangeAddress_t ZiprSizerX86_t::PlopDollopEntryWithTarget( + Zipr_SDK::DollopEntry_t *entry, + RangeAddress_t override_place, + RangeAddress_t override_target) const +{ + auto insn = entry->getInstruction(); + + assert(entry->getTargetDollop()); + + auto addr = entry->getPlace(); + auto target_addr = entry->getTargetDollop()->getPlace(); + auto ret = addr; + + if (override_place != 0) + addr = ret = override_place; + + if (override_target != 0) + target_addr = override_target; + + if(insn->getDataBits().length() >2) + { + memory_space.plopBytes(ret, + insn->getDataBits().c_str(), + insn->getDataBits().length() + ); + m_zipr_obj.GetPatcher()->ApplyPatch(ret, target_addr); + ret+=insn->getDataBits().length(); + return ret; + } + + // call, jmp, jcc of length 2. + char b=insn->getDataBits()[0]; + switch(b) + { + case (char)0x70: + case (char)0x71: + case (char)0x72: + case (char)0x73: + case (char)0x74: + case (char)0x75: + case (char)0x76: + case (char)0x77: + case (char)0x78: + case (char)0x79: + case (char)0x7a: + case (char)0x7b: + case (char)0x7c: + case (char)0x7d: + case (char)0x7e: + case (char)0x7f: + { + // two byte JCC + char bytes[]={(char)0x0f,(char)0xc0,(char)0x0,(char)0x0,(char)0x0,(char)0x0 }; // 0xc0 is a placeholder, overwritten next statement + bytes[1]=insn->getDataBits()[0]+0x10; // convert to jcc with 4-byte offset. + memory_space.plopBytes(ret,bytes, sizeof(bytes)); + m_zipr_obj.GetPatcher()->ApplyPatch(ret, target_addr); + ret+=sizeof(bytes); + return ret; + } + case (char)0xeb: + { + // two byte JMP + char bytes[]={(char)0xe9,(char)0x0,(char)0x0,(char)0x0,(char)0x0 }; + bytes[1]=insn->getDataBits()[0]+0x10; // convert to jcc with 4-byte offset. + memory_space.plopBytes(ret,bytes, sizeof(bytes)); + m_zipr_obj.GetPatcher()->ApplyPatch(ret, target_addr); + ret+=sizeof(bytes); + return ret; + } + case (char)0xe0: + case (char)0xe1: + case (char)0xe2: + case (char)0xe3: + { + // loop, loopne, loopeq, jecxz + // convert to: + // <op> +5: + // jmp fallthrough + // +5: jmp target + char bytes[]={0,0x5}; + auto fallthrough_de = entry->getMemberOfDollop()->getFallthroughDollopEntry(entry); + + /* + * This means that we have a fallthrough for this dollop entry + * that is actually the fallthrough of the dollop! Wowser. + * TODO: Before doing this, check to make sure that _entry_ + * is the last of the dollop. + */ + if (!fallthrough_de) + { + if(entry->getMemberOfDollop()->getFallthroughDollop()) + fallthrough_de = entry->getMemberOfDollop()->getFallthroughDollop()->front(); + else + // even a cond branch may have a null fallthrough, account for that here + // by plopping nothing + return ret; + } + + assert(fallthrough_de && fallthrough_de->isPlaced()); + + bytes[0]=insn->getDataBits()[0]; + memory_space.plopBytes(ret,bytes, sizeof(bytes)); + ret+=sizeof(bytes); + + memory_space.plopJump(ret); + m_zipr_obj.GetPatcher()->ApplyPatch(ret, fallthrough_de->getPlace()); + ret+=5; + + memory_space.plopJump(ret); + m_zipr_obj.GetPatcher()->ApplyPatch(ret, target_addr); + ret+=5; + + return ret; + + } + + default: + assert(0); + } +} + + + + diff --git a/zipr/src/utils.cpp b/zipr/src/utils.cpp new file mode 100644 index 0000000000000000000000000000000000000000..baa8a7ab5aa31f3431456f975897ea6af47c66d2 --- /dev/null +++ b/zipr/src/utils.cpp @@ -0,0 +1,9 @@ +#include <zipr_all.h> + +using namespace zipr; + +void zipr::PrintStat(std::ostream &out, std::string description, double value) +{ + out << description << ": " << std::dec << value << std::endl; +} + diff --git a/zipr/src/zipr.cpp b/zipr/src/zipr.cpp new file mode 100644 index 0000000000000000000000000000000000000000..943bad1b0ea1274873c2a37ef3d96d037ca3d7d9 --- /dev/null +++ b/zipr/src/zipr.cpp @@ -0,0 +1,2292 @@ +/*************************************************************************** + * Copyright (c) 2014 Zephyr Software LLC. All rights reserved. + * + * This software is furnished under a license and/or other restrictive + * terms and may be used and copied only in accordance with such terms + * and the inclusion of the above copyright notice. This software or + * any other copies thereof may not be provided or otherwise made + * available to any other person without the express written consent + * of an authorized representative of Zephyr Software LCC. Title to, + * ownership of, and all rights in the software is retained by + * Zephyr Software LCC. + * + * Zephyr Software LLC. Proprietary Information + * + * Unless otherwise specified, the information contained in this + * directory, following this legend, and/or referenced herein is + * Zephyr Software LLC. (Zephyr) Proprietary Information. + * + * CONTACT + * + * For technical assistance, contact Zephyr Software LCC. at: + * + * + * Zephyr Software, LLC + * 2040 Tremont Rd + * Charlottesville, VA 22911 + * + * E-mail: jwd@zephyr-software.com + **************************************************************************/ + +#include <zipr_all.h> +#include <irdb-core> +#include <iostream> +#include <stdlib.h> +#include <string.h> +#include <map> +#include <assert.h> +#include <sys/mman.h> +#include <ctype.h> +#include <iostream> // std::cout +#include <string> // std::string, std::to_string +#include <fstream> +#include "cmdstr.hpp" + +#define ALLOF(a) begin(a),end(a) + +using namespace IRDB_SDK; +using namespace std; +using namespace zipr; +using namespace EXEIO; +using namespace Zipr_SDK; + + +inline uintptr_t page_round_up(uintptr_t x) +{ + return ( (((uintptr_t)(x)) + PAGE_SIZE-1) & (~(PAGE_SIZE-1)) ); +} + +inline uintptr_t page_round_down(uintptr_t x) +{ + return ( (((uintptr_t)(x)) - (PAGE_SIZE-1)) & (~(PAGE_SIZE-1)) ); +} + + +template < typename T > std::string to_hex_string( const T& n ) +{ + std::ostringstream stm ; + stm << std::hex<< "0x"<< n ; + return stm.str() ; +} + +template < typename T > std::string to_string( const T& n ) +{ + std::ostringstream stm ; + stm << n ; + return stm.str() ; +} + +void ZiprImpl_t::Init() +{ + + // allocate stats + m_stats = new Stats_t(); + + bss_needed=0; + use_stratafier_mode=false; + ostream *error = &cout, *warn = nullptr; + + registerOptions(); + + /* + * Parse once to read the global and zipr options. + */ + m_zipr_options.parse(nullptr, nullptr); + if (m_variant->areRequirementMet()) + { + /* setup the interface to the sql server */ + BaseObj_t::setInterface(m_pqxx_interface.get()); + + m_variant_id_p=VariantID_t::factory(*m_variant); + m_variant_id=m_variant_id_p.get(); + assert(m_variant_id); + assert(m_variant_id->isRegistered()==true); + + if (*m_verbose) + cout<<"New Variant, after reading registration, is: "<<*m_variant_id << endl; + + auto this_file=m_variant_id->getMainFile(); + + // read the db + m_firp_p=FileIR_t::factory(m_variant_id, this_file); + m_firp=m_firp_p.get(); + assert(m_firp); + + } + plugman = ZiprPluginManager_t(this, &m_zipr_options); + + // need a file IR to create the arch-specific stuff. + // without it, we won't run anything anyhow + if(m_firp) + { + archhelper=ZiprArchitectureHelperBase_t::factory(this); + pinner =archhelper->getPinner (); + patcher=archhelper->getPatcher(); + sizer =archhelper->getSizer (); + } + + /* + * Parse again now that the plugins registered something. + */ + if (*m_verbose) + warn = &cout; + m_zipr_options.parse(error, warn); + + if (!m_zipr_options.areRequirementsMet()) { + m_zipr_options.printUsage(cout); + m_error = true; + return; + } +} + +ZiprImpl_t::~ZiprImpl_t() +{ + // delete m_firp; // taken care of by the unique_ptr the factory returns. + try + { + m_pqxx_interface->commit(); + } + catch (const DatabaseError_t& pnide) + { + cout<<"Unexpected database error: "<<pnide<<endl; + } +} + +void ZiprImpl_t::registerOptions() +{ + auto zipr_namespace = m_zipr_options.getNamespace("zipr"); + auto glbl_namespace = m_zipr_options.getNamespace("global"); + + + m_output_filename = zipr_namespace->getStringOption ("output", "Output file name.", "b.out"); + m_callbacks = zipr_namespace->getStringOption ("callbacks", "Set the path of the file which contains any required callbacks."); + m_objcopy = zipr_namespace->getStringOption ("objcopy", "Set the path of objcopy to use.", "/usr/bin/objcopy"); + m_dollop_map_filename = zipr_namespace->getStringOption ("dollop_map_filename", "Specify filename to save dollop map.", "dollop.map"); + m_replop = zipr_namespace->getBooleanOption("replop", "Replop all dollops.", false); + m_verbose = glbl_namespace->getBooleanOption("verbose", "Enable verbose output", false); + m_vverbose = glbl_namespace->getBooleanOption("very_verbose", "Be very verry verbose, I'm hunting wabbits.", false); + m_apply_nop = glbl_namespace->getBooleanOption("apply_nop", "This flag needs documentation.", false); + m_add_sections = glbl_namespace->getBooleanOption("add-sections", "Add sections to the output binary", true); + m_bss_opts = glbl_namespace->getBooleanOption("bss-opts", "Use BSS optimizationg when genreating output file", true); + m_variant = glbl_namespace->getIntegerOption("variant", "Which IRDB variant to Zipr."); + m_architecture = zipr_namespace->getIntegerOption("architecture", "Override default system architecture detection"); + m_seed = zipr_namespace->getIntegerOption("seed", "Specify a seed for randomization", getpid()); + m_paddable_minimum_distance = zipr_namespace->getIntegerOption("paddable_minimum_distance", "Specify the minimum size of a gap to be filled.", 5*1024); + + + m_variant->setRequired(true); + + memory_space.registerOptions(&m_zipr_options); +} + + +void ZiprImpl_t::CreateBinaryFile() +{ + exeiop->load("a.ncexe"); + + if (*m_architecture == 0) + { + if (*m_verbose) + cout << "Doing architecture autodetection." << endl; + m_architecture->setValue(IRDB_SDK::FileIR_t::getArchitectureBitWidth()); + if (*m_verbose) + cout << "Autodetected to " << (int)*m_architecture << endl; + } + + /* + * Take the seed and initialize the random number + * generator. + */ + std::srand((unsigned)*m_seed); + if (*m_verbose) + cout << "Seeded the random number generator with " << *m_seed << "." << endl; + + + FixTwoByteWithPrefix(); // have to do this before multi-fallthrough in case it creaates some. + FixNoFallthroughs(); // have to do this before multi-fallthrough in case it creaates some. + FixMultipleFallthroughs(); + + + // create ranges, including extra range that's def. big enough. + FindFreeRanges(*m_output_filename); + + plugman.PinningBegin(); + + // allocate and execute a pinning algorithm. + assert(pinner); + pinner->doPinning(); + + // tell plugins we are done pinning. + plugman.PinningEnd(); + + /* + * Let's go through all the instructions + * and determine if there are going to be + * plugins that want to plop an instruction! + */ + AskPluginsAboutPlopping(); + + CreateDollops(); + RecalculateDollopSizes(); + plugman.DollopBegin(); + PlaceDollops(); + plugman.DollopEnd(); + WriteDollops(); + ReplopDollopEntriesWithTargets(); + UpdatePins(); + + // tell plugins we are done plopping and about to link callbacks. + plugman.CallbackLinkingBegin(); + + // now that all instructions are put down, we can figure out where the callbacks for this file wil go. + // go ahead and update any callback sites with the new locations + UpdateCallbacks(); + + // ask plugman to inform the plugins we are done linking callbacks + plugman.CallbackLinkingEnd(); + + m_stats->total_free_ranges = memory_space.GetRangeCount(); + + // Update any scoops that were written by Zipr. + UpdateScoops(); + + // write binary file to disk + OutputBinaryFile(*m_output_filename); + + // print relevant information + PrintStats(); +} + +#if 0 +static bool in_same_segment(EXEIO::section* sec1, EXEIO::section* sec2, EXEIO::exeio* exeiop) +{ + auto n = exeiop->segments.size(); + for ( auto i = 0; i < n; ++i ) + { + uintptr_t segstart=exeiop->segments[i]->get_virtual_address(); + uintptr_t segsize=exeiop->segments[i]->get_file_size(); + + /* sec1 in segment i? */ + if(segstart <= sec1->get_address() && sec1->get_address() < (segstart+segsize)) + { + /* return true if sec2 also in segment */ + /* else return false */ + return (segstart <= sec2->get_address() && sec2->get_address() < (segstart+segsize)); + } + } + + return false; +} +#endif + + +// +// check if there's padding we can use between this section and the next section. +// +RangeAddress_t ZiprImpl_t::extend_section(EXEIO::section *sec, EXEIO::section *next_sec) +{ + assert(0); +#if 0 + RangeAddress_t start=sec->get_address(); + RangeAddress_t end=sec->get_size()+start; + if( (next_sec->get_flags() & SHF_ALLOC) != 0 && in_same_segment(sec,next_sec,elfiop)) + { + end=next_sec->get_address()-1; + cout<<"Extending range of " << sec->get_name() <<" to "<<std::hex<<end<<endl; + sec->set_size(next_sec->get_address() - sec->get_address() - 1); + } + return end; +#endif +} + +void ZiprImpl_t::CreateExecutableScoops(const std::map<RangeAddress_t, int> &ordered_sections) +{ + int count=0; + /* + * For each section, ... + */ + for(auto it = ordered_sections.begin(); it!=ordered_sections.end(); /* empty */ ) + { + auto sec = exeiop->sections[it->second]; + assert(sec); + + // skip non-exec and non-alloc sections. + // if( (sec->get_flags() & SHF_ALLOC) ==0 || (sec->get_flags() & SHF_EXECINSTR) ==0 ) + if(!sec->isLoadable() || !sec->isExecutable()) + { + ++it; + continue; + } + + // setup start of scoop. + auto text_start=m_firp->addNewAddress(m_firp->getFile()->getBaseID(), sec->get_address()); + + /* + * ... walk the subsequent sections and coalesce as many as possible + * into a single executable address range. + */ + while(1) + { + sec = exeiop->sections[it->second]; + + // skip non-alloc sections. + // if( (sec->get_flags() & SHF_ALLOC) ==0) + if(!sec->isLoadable()) + { + ++it; + continue; + } + // stop if not executable. + // if( (sec->get_flags() & SHF_EXECINSTR) ==0 ) + if(!sec->isExecutable()) + break; + + // try next + ++it; + if(it==ordered_sections.end()) + break; + } + + // setup end of scoop address + /* + auto text_end=new AddressID_t(); + // insert into IR + m_firp->getAddresses().insert(text_end); + */ + auto text_end=m_firp->addNewAddress(m_firp->getFile()->getBaseID(),0); + + // two cases for end-of-scoop + if (it==ordered_sections.end()) + // 1 ) this is the last section + text_end->setVirtualOffset(page_round_up(sec->get_address()+sec->get_size()-1)-1); + else + // 2 ) another section gets in the way. + text_end->setVirtualOffset(sec->get_address()-1); + + + + // setup a scoop for this section. + // zero init is OK, after zipring we'll update with the right bytes. + string text_contents; + string text_name=string(".zipr_text_")+to_string(count++); + if(count==1) + text_name=".text"; // use the name .text first. + + text_contents.resize(text_end->getVirtualOffset() - text_start->getVirtualOffset()+1); + // + // DataScoop_t* text_scoop=new DataScoop_t(m_firp->GetMaxBaseID()+1, text_name, text_start, text_end, nullptr, 5 /*R-X*/, false, text_contents); + // m_firp->getDataScoops().insert(text_scoop); + // + auto text_scoop=m_firp->addNewDataScoop(text_name, text_start, text_end, nullptr, 5 /*R-X*/, false, text_contents); + + cout<<"Adding scoop "<<text_scoop->getName()<<hex<<" at "<<hex<<text_start->getVirtualOffset()<<" - "<<text_end->getVirtualOffset()<<endl; + m_zipr_scoops.insert(text_scoop); + memory_space.AddFreeRange(Range_t(text_start->getVirtualOffset(),text_end->getVirtualOffset()), true); + } +} + + +RangeAddress_t ZiprImpl_t::PlaceUnplacedScoops(RangeAddress_t max_addr) +{ + max_addr=plugman.PlaceScoopsBegin(max_addr); + + auto scoops_by_perms= map<int,DataScoopSet_t>(); + for(auto scoop : m_firp->getDataScoops()) + { + // check if placed. + if(scoop->getStart()->getVirtualOffset()==0) + scoops_by_perms[scoop->isRelRo() << 16 | scoop->getRawPerms()].insert(scoop); + } + + // for(auto pit=scoops_by_perms.begin(); pit!=scoops_by_perms.end(); ++pit) + for(auto &p : scoops_by_perms ) + { + // start by rounding up to a page boundary so that page perms don't get unioned. + max_addr=page_round_up(max_addr); + for(auto &scoop : p.second) + { + max_addr=align_up_to(max_addr,(RangeAddress_t)16); // 16 byte align. + scoop->getStart()->setVirtualOffset(scoop->getStart()->getVirtualOffset()+max_addr); + scoop->getEnd()->setVirtualOffset(scoop->getEnd()->getVirtualOffset()+max_addr); + + // update so we actually place things at diff locations. + max_addr=scoop->getEnd()->getVirtualOffset()+1; + + cout<<"Placing scoop "<<scoop->getName()<<" at " + <<hex<<scoop->getStart()->getVirtualOffset()<<"-" + <<hex<<scoop->getEnd()->getVirtualOffset()<<endl; + } + } + + + // assert we unpinned everything + for(const auto s : m_firp->getDataScoops()) + assert(s->getStart()->getVirtualOffset()!=0); + + + max_addr=plugman.PlaceScoopsEnd(max_addr); + + return max_addr; +} + +void ZiprImpl_t::FindFreeRanges(const std::string &name) +{ + RangeAddress_t max_addr=0; + std::map<RangeAddress_t, int> ordered_sections; + DataScoopByAddressSet_t sorted_scoop_set; + + + /* + * This function should be the *only* place where + * scoops are added to m_zipr_scoops. This assert + * is here to maintain that variant. + */ + assert(m_zipr_scoops.empty()); + + /* + * Make an ordered list of the sections + * by their starting address. + */ + auto n = exeiop->sections.size(); + for ( auto i = 0; i < n; ++i ) + { + auto sec = exeiop->sections[i]; + assert(sec); + ordered_sections.insert({sec->get_address(), i}); + } + + CreateExecutableScoops(ordered_sections); + + // scan sections for a max-addr. + for (auto p : ordered_sections ) + { + section* sec = exeiop->sections[p.second]; + assert(sec); + + RangeAddress_t start=sec->get_address(); + RangeAddress_t end=sec->get_size()+start-1; + + if (*m_verbose) + printf("max_addr is %p, end is %p\n", (void*)max_addr, (void*)end); + + if(start && end>max_addr) + { + if (*m_verbose) + printf("new max_addr is %p\n", (void*)max_addr); + max_addr=end; + } + + // if( (sec->get_flags() & SHF_ALLOC) ==0 ) + if(!sec->isLoadable()) + continue; + } + + /* + * First things first: Let's put empty scoops + * in all the gaps. + */ + + if (*m_verbose) + cout << "Filling gaps that are larger than " << std::dec + << *m_paddable_minimum_distance << " bytes." << endl; + + /* + * Only put pinned data scoops into the list of + * scoops to consider for adding gap filling. + */ + copy_if(ALLOF(m_firp->getDataScoops()), + inserter(sorted_scoop_set, sorted_scoop_set.begin()), + [](DataScoop_t* ds) + { + return ds->getStart()->getVirtualOffset() != 0; + } + ); + for( auto it=sorted_scoop_set.begin(); it!=sorted_scoop_set.end(); ++it ) + { + const auto this_scoop = *it; + auto next_scoop = (DataScoop_t*)nullptr; + auto this_end = this_scoop->getEnd()->getVirtualOffset(); + auto next_start = RangeAddress_t(0); + + assert(this_scoop->getStart()->getVirtualOffset()!=0); + + if (*m_verbose) + cout << hex + << "There's a scoop between " << this_scoop->getStart()->getVirtualOffset() + << " and " << this_scoop->getEnd()->getVirtualOffset() + << " with permissions " << +this_scoop->getRawPerms() // print as int, not char + << endl; + + /* + * Never pad after the last scoop. + */ + if (std::next(it,1) != sorted_scoop_set.end()) + { + next_scoop = *std::next(it,1); + next_start = next_scoop->getStart()->getVirtualOffset(); + unsigned int new_padding_scoop_size = 0; + RangeAddress_t new_padding_scoop_start = this_end + 1; + RangeAddress_t new_padding_scoop_end = next_start - 1; + + if (this_end > next_start) { + /* + * It's possible that sections overlap + * one another. Computing the distance + * as an unsigned (as below) causes problems. + * So, we make a special check here. + */ + if (*m_verbose) + cout << "Not considering this section because it " + << "does not end before the next one starts." << endl; + continue; + } + + if (*m_verbose) + cout << "Considering a gap between: 0x" << std::hex + << new_padding_scoop_start << "-0x" + << std::hex << new_padding_scoop_end + << endl; + + /* + * If the adjacent scoop is writable, we + * do not want to put an executable scoop + * in the same page. + */ + if (this_scoop->isWriteable()) + { + new_padding_scoop_start = page_round_up(new_padding_scoop_start); + + if (*m_verbose) + cout << "Adjacent scoop is writable. Adjusting start up to 0x" + << std::hex << new_padding_scoop_start << "." << endl; + } + + /* + * If the next scoop is writable, we + * do not want to put an executable scoop + * in the same page. + */ + if (next_scoop->isWriteable()) + { + new_padding_scoop_end = page_round_down(new_padding_scoop_end); + + if (*m_verbose) + cout << "Next scoop is writable. Adjusting end down to 0x" + << std::hex << new_padding_scoop_end << "." << endl; + } + + /* + * After making the proper adjustments, we know + * the size of the gap. So, now we have to determine + * whether to pad or not: + * + * 1. Is the gap bigger than the user-defined gap criteria + * 2. One or both of the surrounding segments are not + * writable (a policy decision not to pad between + * writable segments. + */ + new_padding_scoop_size = new_padding_scoop_start - new_padding_scoop_end; + if ((new_padding_scoop_size>(unsigned int)*m_paddable_minimum_distance) && + (!this_scoop->isWriteable() || !next_scoop->isWriteable()) + ) + { + string new_padding_scoop_contents, new_padding_scoop_name; + int new_padding_scoop_perms = 0x5 /* r-x */; + + new_padding_scoop_name = "zipr_scoop_"+ + to_string(new_padding_scoop_start); + + /* + DataScoop_t *new_padding_scoop = nullptr; + AddressID_t *new_padding_scoop_start_addr = nullptr, + *new_padding_scoop_end_addr = nullptr; + new_padding_scoop_start_addr = new AddressID_t(); + new_padding_scoop_start_addr->setVirtualOffset(new_padding_scoop_start); + m_firp->getAddresses().insert(new_padding_scoop_start_addr); + */ + auto new_padding_scoop_start_addr=m_firp->addNewAddress(m_firp->getFile()->getBaseID(), new_padding_scoop_start); + /* + new_padding_scoop_end_addr = new AddressID_t(); + new_padding_scoop_end_addr->setVirtualOffset(new_padding_scoop_end); + m_firp->getAddresses().insert(new_padding_scoop_end_addr); + */ + auto new_padding_scoop_end_addr =m_firp->addNewAddress(m_firp->getFile()->getBaseID(), new_padding_scoop_end); + + cout << "Gap filling with a scoop between 0x" + << std::hex << new_padding_scoop_start << " and 0x" + << std::hex << new_padding_scoop_end + << endl; + + auto new_padding_scoop = m_firp->addNewDataScoop( + new_padding_scoop_name, + new_padding_scoop_start_addr, + new_padding_scoop_end_addr, + nullptr, + new_padding_scoop_perms, + false, + new_padding_scoop_contents); + new_padding_scoop_contents.resize(new_padding_scoop->getSize()); + new_padding_scoop->setContents(new_padding_scoop_contents); + + /* + * Insert this scoop into a list of scoops that Zipr added. + */ + m_zipr_scoops.insert(new_padding_scoop); + + /* + * Tell Zipr that it can put executable code in this section. + */ + memory_space.AddFreeRange(Range_t(new_padding_scoop_start, + new_padding_scoop_end), true); + } + } + } + + /* + * Scan the scoops that we added to see if we went beyond + * the previously highest known address. This should never + * happen because we never pad after the last scoop. + */ + auto max_addr_zipr_scoops_result = max_element(ALLOF(m_zipr_scoops), + [](DataScoop_t *a, DataScoop_t *b) + { + return a->getEnd()->getVirtualOffset() < + b->getEnd()->getVirtualOffset(); + } + ); + assert(max_addr>=(*max_addr_zipr_scoops_result)->getEnd()->getVirtualOffset()); + + max_addr=PlaceUnplacedScoops(max_addr); + + // now that we've looked at the sections, add a (mysterious) extra section in case we need to overflow + // the sections existing in the ELF. + RangeAddress_t new_free_page=page_round_up(max_addr); + + + /* + * TODO + * + * Make a scoop out of this. Insert it into m_zipr_scoops + * and m_firp->getDataScoops() + */ + auto textra_start = RangeAddress_t(new_free_page); + auto textra_end = (RangeAddress_t)-1; + auto textra_name = string("textra"); + auto textra_start_addr = m_firp->addNewAddress(m_firp->getFile()->getBaseID(), textra_start); + auto textra_end_addr = m_firp->addNewAddress(m_firp->getFile()->getBaseID(), textra_end); + + cout << "New free space: 0x" << std::hex << textra_start + << "-0x" + << std::hex << textra_end + << endl; + + auto textra_contents=string(); + auto textra_scoop = m_firp->addNewDataScoop( + textra_name, + textra_start_addr, + textra_end_addr, + nullptr, + /* r-x */5, + false, + textra_contents); + + /* + * Normally we would have to resize the underlying contents here. + * Unfortunately that's not a smart idea since it will be really big. + * Instead, we are going to do a batch resizing below. + */ + + m_zipr_scoops.insert(textra_scoop); + + memory_space.AddFreeRange(Range_t(new_free_page,(RangeAddress_t)-1), true); + if (*m_verbose) + printf("Adding (mysterious) free range 0x%p to EOF\n", (void*)new_free_page); + start_of_new_space=new_free_page; + + for(auto scoop : m_firp->getDataScoops()) + { + // skip the scoops we just added. + if(scoop->getBaseID()==BaseObj_t::NOT_IN_DATABASE) continue; + + // put scoops in memory to make sure they are busy, + // just in case they overlap with free ranges. + // this came up on Aarch64 because data is in the .text segment. + cout<<"Pre-allocating scoop "<<scoop->getName() << "=(" + << scoop->getStart()->getVirtualOffset() << "-" + << scoop->getEnd() ->getVirtualOffset() << ")"<<endl; + memory_space.PlopBytes(scoop->getStart()->getVirtualOffset(), + scoop->getContents().c_str(), + scoop->getContents().size() + ); + } +} + + +Instruction_t *ZiprImpl_t::FindPatchTargetAtAddr(RangeAddress_t addr) +{ + std::map<RangeAddress_t,UnresolvedUnpinnedPatch_t>::iterator it=m_PatchAtAddrs.find(addr); + if(it!=m_PatchAtAddrs.end()) + return it->second.first.getInstrution(); + return nullptr; +} + + +void ZiprImpl_t::WriteDollops() +{ + for (auto & dollop_to_write : m_dollop_mgr.getDollops() ) + { + assert(dollop_to_write != nullptr); + // skip unplaced dollops as they aren't necessary + if (!dollop_to_write->isPlaced()) + continue; + + // write each entry in the dollop + for (auto &entry_to_write : *dollop_to_write) + { + assert(entry_to_write != nullptr); + // plop it. + const auto de_end_loc = _PlopDollopEntry(entry_to_write); + + // sanity check that we didn't go passed the worst case size we calculate for this entry + const auto de_start_loc = entry_to_write->getPlace(); + const auto should_end_at = de_start_loc + DetermineDollopEntrySize(entry_to_write, false); + assert(de_end_loc == should_end_at); + /* + * Build up a list of those dollop entries that we have + * just written that have a target. See comment above + * ReplopDollopEntriesWithTargets() for the reason that + * we have to do this. + */ + const auto will_replop=entry_to_write->getTargetDollop()!=nullptr; + if (will_replop) + m_des_to_replop.push_back(entry_to_write); + } + } +} + +/* + * We have to potentially replop dollop entries with targets + * because: + * + * A plugin that writes dollop entries may put the instructions + * NOT in the first position. This is particularly common in CFI: + * + * 0x...01: f4 + * 0x...02: INSN + * + * However, the writer cannot know every place where that happens + * until after the entire WriteDollops() function has completed. + * So, we go back and do another pass here once we know all those + * actual instruction addresses (which are completely and fully + * assigned during the call to _PlopDollopEntry.). + */ +void ZiprImpl_t::ReplopDollopEntriesWithTargets() +{ + for (auto entry_to_write : m_des_to_replop) + { + Instruction_t *src_insn = nullptr; + RangeAddress_t src_insn_addr; + + src_insn = entry_to_write->getInstruction(); + + src_insn_addr = final_insn_locations[src_insn]; + _PlopDollopEntry(entry_to_write, src_insn_addr); + } +} + +void ZiprImpl_t::PlaceDollops() +{ + + auto count_pins=0u; + + /* + * Build up initial placement q with destinations of pins. + */ + for (auto p : patch_list) + { + const auto uu = p.first; + const auto patch = p.second; + auto target_insn = uu.getInstrution(); + auto target_dollop = m_dollop_mgr.getContainingDollop(target_insn); + assert(target_dollop); + + placement_queue.insert({target_dollop,patch.getAddress()}); + if (*m_verbose) + { + cout << "Original: " << hex << target_insn-> getAddress()-> getVirtualOffset() << " " + << "vs. Patch: " << patch.getAddress() << endl; + } + count_pins++; + } + + cout<<"# ATTRIBUTE Zipr::pins_detected="<<dec<<count_pins<<endl; + cout<<"# ATTRIBUTE Zipr::placement_queue_size="<<dec<<placement_queue.size()<<endl; + + assert(getenv("SELF_VALIDATE")==nullptr || count_pins > 3 ) ; + assert(getenv("SELF_VALIDATE")==nullptr || placement_queue.size() > 3 ) ; + + /* + * used to check if a reference dollop needs to be added to the placement queue + */ + const auto ensure_insn_is_placed=[&](Instruction_t* insn) + { + if(insn != nullptr) + { + auto containing=m_dollop_mgr.addNewDollops(insn); + assert(containing!=nullptr); + if(!containing->isPlaced()) + { + placement_queue.insert({containing, insn->getAddress()->getVirtualOffset()}); + } + } + }; + + // Make sure each instruction referenced in a relocation (regardless + // of if that relocation is on an instruction or a scoop) gets placed. + for(const auto &reloc : m_firp->getRelocations()) + ensure_insn_is_placed(dynamic_cast<Instruction_t*>(reloc->getWRT())); + + // Make sure each landing pad in a program gets placed. + for(const auto &cs : m_firp->getAllEhCallSites()) + ensure_insn_is_placed(cs->getLandingPad()); + + m_dollop_mgr.UpdateAllTargets(); + + while (!placement_queue.empty()) + { + auto placement=Range_t(); + auto placer = DLFunctionHandle_t(nullptr); + auto placed = false; + auto cur_addr = RangeAddress_t(0); + auto has_fallthrough = false; + auto fallthrough = (Zipr_SDK::Dollop_t*)(nullptr); + auto continue_placing = false; + auto initial_placement_abuts_pin = false; + auto initial_placement_abuts_fallthrough = false; + auto fits_entirely = false; + auto fallthrough_dollop_place = RangeAddress_t(0); + auto fallthrough_has_preplacement = false; + auto pq_entry = *(placement_queue.begin()); + placement_queue.erase(placement_queue.begin()); + + auto to_place = pq_entry.first; + auto from_address = pq_entry.second; + + if (*m_vverbose) + { + cout << "Placing dollop with original starting address: " << hex + << to_place->front()->getInstruction()->getAddress()->getVirtualOffset() << endl; + } + + if (to_place->isPlaced()) + continue; + + to_place->reCalculateSize(); + + auto minimum_valid_req_size = std::min( + DetermineDollopEntrySize(to_place->front(), true), + sizer->DetermineDollopSizeInclFallthrough(to_place)); + /* + * Ask the plugin manager if there are any plugins + * that want to tell us where to place this dollop. + */ + auto am_coalescing = false; + auto allowed_coalescing = true; + auto allowed_fallthrough = true; + if (plugman.DoesPluginAddress(to_place, from_address, placement, allowed_coalescing, allowed_fallthrough, placer)) + { + placed = true; + + if (*m_verbose) + cout << placer->toString() << " placed this dollop between " + << hex << placement.getStart() << " and " << placement.getEnd() + << endl; + + /* + * Check if the size that we got back is enough to hold + * at least a little bit of what we wanted. + * + * (1) We want to make sure that there is enough room for at least + * the first instruction of the dollop and space for a jump + * to the remainder of the dollop. + * + * (2) However, it's possible that the entirety of this dollop, plus + * any fallthroughs are going to fit. So, we need to check that + * possibility too. + * + * (3) Then there's the possibility that the dollop *has* a fallthrough + * but that the fallthrough is actually pinned and + * that pin is abutting the end of the dollop in which + * case we elide (I hate that term) the fallthrough jump. + * + * (4) Then there's the possibility that the dollop has a + * fallthrough but that the fallthrough is actually abutting + * the beginning of it's fallthrough dollop in which case we elide + * (still hate that term) the fallthrough jump. Very similar + * to case (3). + * + * TODO: Consider that allowed_coalescing may invalidate the + * possibility of the validity of the placement in (2). + */ + const auto has_fallthrough = to_place->getFallthroughDollop() != nullptr; + const auto ibta=has_fallthrough ? to_place->getFallthroughDollop()-> front()-> getInstruction()-> getIndirectBranchTargetAddress() : 0; + initial_placement_abuts_pin = has_fallthrough && + ibta && + ibta -> getVirtualOffset()!=0 && + ibta-> getVirtualOffset() == (placement.getStart() + to_place->getSize() - sizer->TRAMPOLINE_SIZE); + /* + * If this dollop has a fallthrough, find out where that + * fallthrough is (or is going to be) placed. That way + * we can determine if the current dollop is (or is going to be) + * adjacent to the place of the fallthrough. That means + * that we can keep from placing a jump to the dollo + * and instead just fallthrough. + */ + if (to_place->getFallthroughDollop() && allowed_fallthrough) + { + /* + * Find out where the fallthrough dollop is placed. + */ + if (to_place->getFallthroughDollop()->isPlaced()) + { + fallthrough_dollop_place = to_place->getFallthroughDollop()->getPlace(); + fallthrough_has_preplacement = true; + } + /* + * Find out where the fallthrough dollop is + * going to be placed. We only have to ask + * plugins about this since we know that zipr-proper + * does not preallocate placements like plugins + * are known to do. + */ + else + { + Range_t fallthrough_placement; + bool fallthrough_allowed_coalescing = false; + bool fallthrough_allowed_fallthrough = false; + DLFunctionHandle_t fallthrough_placer = nullptr; + /* + * Prospectively get the place for this dollop. That way + * we can determine whether or not we need to use a fallthrough! + */ + if (plugman.DoesPluginAddress(to_place->getFallthroughDollop(), + from_address, + fallthrough_placement, + fallthrough_allowed_coalescing, + fallthrough_allowed_fallthrough, + fallthrough_placer)) + { + fallthrough_dollop_place = fallthrough_placement.getStart(); + fallthrough_has_preplacement = true; + } + } + } + initial_placement_abuts_fallthrough = to_place->getFallthroughDollop() && + fallthrough_has_preplacement && + fallthrough_dollop_place == (placement.getStart() + to_place->getSize() - sizer->TRAMPOLINE_SIZE); + + + auto fits_entirely = (to_place->getSize() <= (placement.getEnd()-placement.getStart())); + + if (*m_verbose) + { + cout << "initial_placement_abuts_pin : " + <<initial_placement_abuts_pin << endl + << "initial_placement_abuts_fallthrough: " + << initial_placement_abuts_fallthrough << endl + << "fits_entirely : " + << fits_entirely << endl; + } + + if ( ((placement.getEnd()-placement.getStart()) < minimum_valid_req_size) && + !(initial_placement_abuts_pin || initial_placement_abuts_fallthrough || fits_entirely) + ) + { + if (*m_verbose) + cout << "Bad getNearbyFreeRange() result." << endl; + placed = false; + } + } + + if (!placed) + { + // cout << "Using default place locator." << endl; + /* + * TODO: Re-enable this ONCE we figure out why the dollop + * sizes are not being recalculated correctly. + */ + //placement = memory_space.getFreeRange(to_place->getSize()); + placement = sizer->DoPlacement(minimum_valid_req_size); + + /* + * Reset allowed_coalescing because DoesPluginAddress + * may have reset it and we may have rejected the results + * of that addressing. + */ + allowed_coalescing = true; + } + + cur_addr = placement.getStart(); + //cout << "Adjusting cur_addr to " << std::hex << cur_addr << " at A." << endl; + has_fallthrough = (to_place->getFallthroughDollop() != nullptr); + + if (*m_vverbose) + { + cout << "Dollop size=" << dec << to_place->getSize() << ". Placing in hole size=" + << (placement.getEnd() - placement.getStart()) << " hole at " << hex << cur_addr << endl; + cout << "Dollop " << ((has_fallthrough) ? "has " : "does not have ") + << "a fallthrough" << endl; + } + + const auto has_pinned_ibta= + to_place->front()->getInstruction()->getIndirectBranchTargetAddress() && + to_place->front()->getInstruction()->getIndirectBranchTargetAddress()->getVirtualOffset()!=0 ; + const auto pinned_ibta_addr = has_pinned_ibta ? + to_place-> front()->getInstruction()-> getIndirectBranchTargetAddress()-> getVirtualOffset() : + VirtualOffset_t(0); + if (has_pinned_ibta && cur_addr == pinned_ibta_addr) + { + unsigned int space_to_clear = sizer->SHORT_PIN_SIZE; + /* + * We have placed this dollop at the location where + * its first instruction was pinned in memory. + */ + if (*m_verbose) + cout << "Placed atop its own pin!" << endl; + + if (memory_space[cur_addr] == (char)0xe9) + space_to_clear = sizer->LONG_PIN_SIZE; + + for (unsigned int j = cur_addr; j<(cur_addr+space_to_clear); j++) + { + memory_space.mergeFreeRange(j); + } + + /* + * Remove the replaced pin from the patch list. + */ + UnresolvedUnpinned_t uu(to_place->front()->getInstruction()); + Patch_t emptypatch(cur_addr, UncondJump_rel32); + + auto found_patch = patch_list.find(uu); + assert(found_patch != patch_list.end()); + patch_list.erase(found_patch); + } + /* + * Handle the case where the placer put us atop the fallthrough + * link from it's FallbackDollop() + */ + else if ( // has dollop that falls through to us. + to_place->getFallbackDollop() && + // and it's already placed. + to_place->getFallbackDollop()->isPlaced() && + // and the place is adjacent to us + ( to_place->getFallbackDollop()->getPlace() + to_place->getFallbackDollop()->getSize() - sizer->TRAMPOLINE_SIZE) == placement.getStart() + ) + { + /* + * We have placed this dollop at the location where + * the fallthrough jump to this dollop was placed. + */ + if (*m_verbose) + cout << "Placed atop its own fallthrough!" << endl; + + /* + * Note: We do NOT have to clear any pre-reserved + * memory here now that we have pre-checks on + * whether the dollop is placed. Because of that + * precheck, this range will never be unnecessarily + * reserved for a jump. + */ + } + + assert(to_place->getSize() != 0); + + do + { + bool all_fallthroughs_fit = false; + size_t wcds = 0; + + if (am_coalescing) + { + /* + * Only reset this if we are on a + * second, third, fourth ... go-round. + */ + fits_entirely = false; + } + /* + * TODO: From here, we want to place the dollop + * that we just got a placement for, and subsequently + * place any dollops that are fallthroughs! + */ + + /* + * Assume that we will stop placing after this dollop. + */ + continue_placing = false; + + to_place->reCalculateSize(); + + /* + * Calculate before we place this dollop. + */ + wcds = sizer->DetermineDollopSizeInclFallthrough(to_place); + + to_place->Place(cur_addr); + + // cout << "to_place->getSize(): " << to_place->getSize() << endl; + + fits_entirely = (to_place->getSize() <= (placement.getEnd()-cur_addr)); + all_fallthroughs_fit = (wcds <= (placement.getEnd()-cur_addr)); + + auto dit = to_place->begin(); + auto dit_end = to_place->end(); + for ( /* empty */; dit != dit_end; dit++) + { + auto dollop_entry = *dit; + + /* + * There are several ways that a dollop could end: + * 1. There is no more fallthrough (handled above with + * the iterator through the dollop entries) + * 2. There is no more room in this range. + * a. Must account for a link between split dollops + * b. Must account for a possible fallthrough. + * So, we can put this dollop entry here if any of + * the following are true: + * 1. There is enough room for the instruction AND fallthrough. + * Call this the de_and_fallthrough_fit case. + * 2. There is enough room for the instruction AND it's the + * last instruction in the dollop AND there is no + * fallthrough. + * Call this the last_de_fits case. + * 3. This dollop has no fallthrough and fits entirely + * within the space allotted. + * Call this the fits_entirely case. + * 4. The dollop and all of its fallthroughs will fit + * "[A]ll of its fallthoughs will fit" encompasses + * the possibility that one of those is already + * placed -- we use the trampoline size at that point. + * See DetermineDollopSizeInclFallthrough(). + * Call this the all_fallthroughs_fit case. + * 5. NOT (All fallthroughs fit is the only way that we are + * allowed to proceed placing this dollop but we are not + * allowed to coalesce and we are out of space for the + * jump to the fallthrough.) + * Call this the !allowed_override case + * 6. There is enough room for this instruction AND it is + * the last entry of this dollop AND the dollop has a + * fallthrough AND that fallthrough is to a pin that + * immediately follows this instruction in memory. + * Call this initial_placement_abuts (calculated above). + */ + const auto de_and_fallthrough_fit = + // does this fit, i.e., end>current+rest_of_dollop + (placement.getEnd()>= (cur_addr+DetermineDollopEntrySize(dollop_entry, true))); + const auto is_last_insn = next(dit)==dit_end; /* last */ + const auto has_fallthrough_dollop = to_place->getFallthroughDollop()!=nullptr ; + const auto fits_with_fallthrough = placement.getEnd()>=(cur_addr+ DetermineDollopEntrySize(dollop_entry, has_fallthrough_dollop)); + const auto last_de_fits = is_last_insn && fits_with_fallthrough; + const auto could_fit_here = + de_and_fallthrough_fit || + fits_entirely || + last_de_fits || + initial_placement_abuts_pin || + initial_placement_abuts_fallthrough ; + const auto tramp_fits = + (placement.getEnd() - (cur_addr + DetermineDollopEntrySize( dollop_entry, false))) < sizer->TRAMPOLINE_SIZE; + const auto allowed_override = + allowed_coalescing || + could_fit_here || + !all_fallthroughs_fit || + !tramp_fits ; + const auto beneficial_to_override = + de_and_fallthrough_fit || + last_de_fits || + fits_entirely || + initial_placement_abuts_fallthrough || + initial_placement_abuts_pin || + all_fallthroughs_fit ; + + if (*m_vverbose) + { + struct custom_bool : numpunct<char> + { + protected: + string do_truename() const override { return "t" ; } + string do_falsename() const override { return "f" ; } + }; + static struct custom_bool *cb=new custom_bool; + + // set cout to print t/f + cout.imbue( { cout.getloc(), cb } ); + + + cout << "Placement stats: " + << de_and_fallthrough_fit << ", " + << last_de_fits << ", " + << fits_entirely << ", " + << all_fallthroughs_fit << ", " + << initial_placement_abuts_pin << ", " + << initial_placement_abuts_fallthrough << ", " + << initial_placement_abuts_pin << ", " + << allowed_override << noboolalpha << endl; + + } + + if ( beneficial_to_override && allowed_override ) + { + dollop_entry->Place(cur_addr); + const auto wcsz=DetermineDollopEntrySize(dollop_entry, false); + const auto next_cur_addr=cur_addr+wcsz; + if (*m_vverbose) + { + auto d=DecodedInstruction_t::factory(dollop_entry->getInstruction()); + cout << "Placing " << hex << dollop_entry->getInstruction()->getBaseID() + << ":" << d->getDisassembly() << " at " + << cur_addr << "-" << next_cur_addr << endl; + } + cur_addr=next_cur_addr; + if (dollop_entry->getTargetDollop()) + { + if (*m_vverbose) + cout << "Adding " << std::hex << dollop_entry->getTargetDollop() + << " to placement queue." << endl; + placement_queue.insert({dollop_entry->getTargetDollop(), cur_addr}); + } + } + else + { + /* + * We cannot fit all the instructions. Let's quit early. + */ + break; + } + } + + if (dit != dit_end) + { + /* + * Split the dollop where we stopped being able to place it. + * In this case, splitting the dollop will give it a fallthrough. + * That will be used below to put in the necessary patch. + * + * However ... (see below) + */ + auto split_dollop = to_place->split((*dit)->getInstruction()); + m_dollop_mgr.AddDollops(split_dollop); + + to_place->setTruncated(true); + if (am_coalescing) + m_stats->truncated_dollops_during_coalesce++; + + if (*m_vverbose) + cout << "Split a " + << ((am_coalescing) ? "coalesced " : " ") + << "dollop because it didn't fit. Fallthrough to " + << std::hex << split_dollop << "." << endl; + } + /* + * (from above) ... we do not want to "jump" to the + * fallthrough if we can simply place it following + * this one! + */ + + fallthrough = to_place->getFallthroughDollop(); + if ( fallthrough != nullptr && !to_place->wasCoalesced() ) + { + size_t fallthroughs_wcds, fallthrough_wcis, remaining_size; + + /* + * We do not care about the fallthrough dollop if its + * first instruction is pinned AND the last entry of this + * dollop abuts that pin. + */ + const auto has_ibta = fallthrough-> front()-> getInstruction()-> getIndirectBranchTargetAddress(); + const auto pinned_ibta_addr = has_ibta ? fallthrough-> front()-> getInstruction()-> getIndirectBranchTargetAddress()-> getVirtualOffset() : VirtualOffset_t(0); + const auto is_pinned_ibta_addr = has_ibta && pinned_ibta_addr!=0; + const auto is_pinned_here = (cur_addr == pinned_ibta_addr ) ; + if ( has_ibta && is_pinned_ibta_addr && is_pinned_here ) + { + if (*m_verbose) + cout << "Dollop had a fallthrough dollop and " + << "was placed abutting the fallthrough " + << "dollop's pinned first instruction. " + << endl; + /* + * Because the fallthrough dollop is pinned, we + * know that it is already in the placement q. That's + * the reason that we do not have to add it here. See + * below for a contrast. + */ + m_stats->total_did_not_coalesce++; + break; + } + + /* + * If the fallthrough is placed and it is immediately after + * this instruction, then we don't want to write anything else! + * + * TODO: This calculation is only valid if we are NOT coalescing. + * We need to change this condition or reset some of the variables + * so that we do not rely on !am_coalescing as a condition. + * Actually, we should make it work correctly -- ie, make sure that + * even if we do coaelesce something its fallthrough could + * be preplaced ... + */ + if (!am_coalescing && to_place->getFallthroughDollop() && fallthrough_has_preplacement && fallthrough_dollop_place == cur_addr) + { + if (*m_verbose) + cout << "Dollop had a fallthrough dollop and " + << "was placed abutting the fallthrough " + << "dollop's first instruction. " + << endl; + /* + * We are not coalescing, but we want to make sure that + * the fallthrough does get placed if zipr hasn't already + * done so. See above for a contrast. + */ + if (!to_place->getFallthroughDollop()->isPlaced()) + { + placement_queue.insert({to_place->getFallthroughDollop(), cur_addr}); + } + m_stats->total_did_not_coalesce++; + break; + } + + /* + * We could fit the entirety of the dollop (and + * fallthroughs) ... + */ + fallthroughs_wcds = sizer->DetermineDollopSizeInclFallthrough(fallthrough); + /* + * ... or maybe we just want to start the next dollop. + */ + fallthrough_wcis=DetermineDollopEntrySize(fallthrough-> front(), + true); + remaining_size = placement.getEnd() - cur_addr; + + /* + * We compare remaining_size to min(fallthroughs_wdcs, + * fallthrough_wcis) since the entirety of the dollop + * and its fallthroughs could (its unlikely) be + * smaller than the first instruction fallthrough + * in the fallthrough dollop and the trampoline size. + */ + if (*m_vverbose) + cout << "Determining whether to coalesce: " + << "Remaining: " << std::dec << remaining_size + << " vs Needed: " << std::dec + << std::min(fallthrough_wcis,fallthroughs_wcds) << endl; + + if (remaining_size < std::min(fallthrough_wcis,fallthroughs_wcds) || + fallthrough->isPlaced() || + !allowed_coalescing + ) + { + + if (*m_vverbose) + { + const auto end_of_cur_dollop_insn = (*to_place ->rbegin())->getInstruction(); + const auto start_of_ft_dollop_insn = (*fallthrough->begin())->getInstruction(); + cout << "Not coalescing " + << end_of_cur_dollop_insn->getDisassembly() << "@" << hex << end_of_cur_dollop_insn->getAddress()->getVirtualOffset() + << " and " + << start_of_ft_dollop_insn->getDisassembly() << "@" << hex << start_of_ft_dollop_insn->getAddress()->getVirtualOffset() + << string((fallthrough->isPlaced()) ? " because fallthrough is placed" : "") + << string((!allowed_coalescing) ? " because I am not allowed" : "") + << ". Add jmp to fallthrough dollop (" << std::hex << fallthrough << ")." << '\n'; + } + + cur_addr = archhelper->splitDollop(m_firp,to_place, cur_addr); +#if 0 + auto patch = archhelper->createNewJumpInstruction(m_firp, nullptr); + auto patch_de = new DollopEntry_t(patch, to_place); + + patch_de->setTargetDollop(fallthrough); + patch_de->Place(cur_addr); + cur_addr+=DetermineDollopEntrySize(patch_de, false); + //cout << "Adjusting cur_addr to " << std::hex << cur_addr << " at C." << endl; + + to_place->push_back(patch_de); + to_place->setFallthroughPatched(true); + + + placement_queue.insert({fallthrough, cur_addr}); + /* + * Since we inserted a new instruction, we should + * check to see whether a plugin wants to plop it. + */ + AskPluginsAboutPlopping(patch_de->getInstruction()); +#endif + + m_stats->total_did_not_coalesce++; + + /* + * Quit the do-while-true loop that is placing + * as many dollops in-a-row as possible. + */ + break; + } + else + { + if (*m_vverbose) + cout << "Coalescing fallthrough dollop." << endl; + to_place->setCoalesced(true); + /* + * Fallthrough is not placed and there is enough room to + * put (at least some of) it right below the previous one. + */ + to_place = fallthrough; + continue_placing = true; + m_stats->total_did_coalesce++; + am_coalescing = true; + } + } + } while (continue_placing); + /* + * This is the end of the do-while-true loop + * that will place as many fallthrough-linked + * dollops as possible. + */ + + /* + * Reserve the range that we just used. + */ + if (*m_vverbose) + cout << "Reserving " << std::hex << placement.getStart() + << ", " << std::hex << cur_addr << "." << endl; + memory_space.splitFreeRange(Range_t(placement.getStart(), cur_addr)); + } +} + +void ZiprImpl_t::RecalculateDollopSizes() +{ + for (auto &dollop : m_dollop_mgr.getDollops()) + dollop->reCalculateSize(); +} + +void ZiprImpl_t::CreateDollops() +{ + if (*m_verbose) + cout<< "Attempting to create " + << patch_list.size() + << " dollops for the pins." + << endl; + for (auto patch : patch_list ) + m_dollop_mgr.AddNewDollops(patch.first.getInstrution()); + + if (*m_verbose) + cout << "Done creating dollops for the pins! Updating all Targets" << endl; + + m_dollop_mgr.UpdateAllTargets(); + + if (*m_verbose) + cout << "Created " <<std::dec << m_dollop_mgr.Size() << " total dollops." << endl; +} + +void ZiprImpl_t::CallToNop(RangeAddress_t at_addr) +{ + assert(patcher); + patcher->CallToNop(at_addr); + return; +} + +void ZiprImpl_t::PatchCall(RangeAddress_t at_addr, RangeAddress_t to_addr) +{ + assert(patcher); + patcher->PatchCall(at_addr,to_addr); +} + +size_t ZiprImpl_t::DetermineDollopEntrySize(Zipr_SDK::DollopEntry_t *entry, bool account_for_fallthrough) +{ + std::map<Instruction_t*,unique_ptr<list<DLFunctionHandle_t>>>::const_iterator plop_it; + size_t opening_size = 0, closing_size = 0; + size_t wcis = DetermineInsnSize(entry->getInstruction(), account_for_fallthrough); + + plop_it = plopping_plugins.find(entry->getInstruction()); + if (plop_it != plopping_plugins.end()) + { + for (auto handle : *(plop_it->second)) + { + ZiprPluginInterface_t *zpi = dynamic_cast<ZiprPluginInterface_t*>(handle); + opening_size += zpi->getDollopEntryOpeningSize(entry); + closing_size += zpi->getDollopEntryClosingSize(entry); + } + } + + if (*m_verbose) + { + } + + return wcis+opening_size+closing_size; +} + +size_t ZiprImpl_t::DetermineInsnSize(Instruction_t* insn, bool account_for_fallthrough) +{ + std::map<Instruction_t*,unique_ptr<list<DLFunctionHandle_t>>>::const_iterator plop_it; + size_t worst_case_size = 0; + size_t default_worst_case_size = 0; + + default_worst_case_size = sizer->DetermineInsnSize(insn, account_for_fallthrough); + + plop_it = plopping_plugins.find(insn); + if (plop_it != plopping_plugins.end()) + { + for (auto handle : *(plop_it->second)) + { + ZiprPluginInterface_t *zpi = dynamic_cast<ZiprPluginInterface_t*>(handle); + worst_case_size =std::max(zpi->getInsnSize(insn, account_for_fallthrough), worst_case_size); + } + } + else + { + worst_case_size = default_worst_case_size; + } + + if (worst_case_size == 0) + { + if (*m_verbose) + cout << "Asked plugins about WCIS, but none responded." << endl; + worst_case_size = default_worst_case_size; + } + + if (*m_vverbose) + { + const auto inc_jmp=((account_for_fallthrough) ? " (including jump)" : ""); + cout << "Worst case size" << inc_jmp << ": " << worst_case_size << endl; + } + + return worst_case_size; +} + +bool ZiprImpl_t::AskPluginsAboutPlopping(Instruction_t *insn) +{ + /* + * Plopping plugins should hold a set. + */ + unique_ptr<list<DLFunctionHandle_t>> found_plopping_plugins = + unique_ptr<list<DLFunctionHandle_t>>(new std::list<DLFunctionHandle_t>()); + + if (plugman.DoPluginsPlop(insn, *found_plopping_plugins)) + { + if (*m_verbose) + for (auto pp : *found_plopping_plugins) + { + ZiprPluginInterface_t *zipr_plopping_plugin = + dynamic_cast<ZiprPluginInterface_t*>(pp); + cout << zipr_plopping_plugin->toString() + << " will plop "<<dec<<insn->getBaseID() << ":" + << insn->getDisassembly() << endl; + } + + plopping_plugins[insn] = std::move(found_plopping_plugins); + return true; + } + return false; +} + +void ZiprImpl_t::AskPluginsAboutPlopping() +{ + + for(auto &insn : m_firp->getInstructions()) + AskPluginsAboutPlopping(insn); +} + +void ZiprImpl_t::UpdatePins() +{ + while(!patch_list.empty()) + { + UnresolvedUnpinned_t uu=(*patch_list.begin()).first; + Patch_t p=(*patch_list.begin()).second; + Zipr_SDK::Dollop_t *target_dollop = nullptr; + Zipr_SDK::DollopEntry_t *target_dollop_entry = nullptr; + Instruction_t *target_dollop_entry_instruction = nullptr; + RangeAddress_t patch_addr, target_addr; + target_dollop = m_dollop_mgr.getContainingDollop(uu.getInstrution()); + assert(target_dollop != nullptr); + DLFunctionHandle_t patcher = nullptr; + + target_dollop_entry = target_dollop->front(); + assert(target_dollop_entry != nullptr); + + target_dollop_entry_instruction = target_dollop_entry->getInstruction(); + assert(target_dollop_entry_instruction != nullptr && + target_dollop_entry_instruction == uu.getInstrution()); + + + patch_addr = p.getAddress(); + target_addr = target_dollop_entry->getPlace(); + + if (final_insn_locations.end() != final_insn_locations.find(target_dollop_entry->getInstruction())) + target_addr = final_insn_locations[target_dollop_entry->getInstruction()]; + + if (plugman.DoesPluginRetargetPin(patch_addr, target_dollop, target_addr, patcher)) + { + if (*m_verbose) + { + cout << "Patching retargeted pin at " << hex<<patch_addr << " to " + << patcher->toString() << "-assigned address: " << target_addr << endl; + } + } + else + { + /* + * Even though DoesPluginRetargetPin() returned something other than + * Must, it could have still changed target_address. So, we have to + * reset it here, just in case. + */ + target_addr = target_dollop_entry->getPlace(); + + if (final_insn_locations.end() != final_insn_locations.find(target_dollop_entry->getInstruction())) + target_addr = final_insn_locations[target_dollop_entry->getInstruction()]; + + if (*m_verbose) + { + const auto d=DecodedInstruction_t::factory(target_dollop_entry_instruction); + cout << "Patching pin at " << hex << patch_addr << " to " + << target_addr << ": " << d->getDisassembly() << endl; + } + assert(target_dollop_entry_instruction != nullptr && + target_dollop_entry_instruction == uu.getInstrution()); + + } + + PatchJump(patch_addr, target_addr); + + patch_list.erase(patch_list.begin()); + } +} + +void ZiprImpl_t::PatchInstruction(RangeAddress_t from_addr, Instruction_t* to_insn) +{ + + // addr needs to go to insn, but insn has not yet been been pinned. + + + // patch the instruction at address addr to go to insn. if insn does not yet have a concrete address, + // register that it's patch needs to be applied later. + + UnresolvedUnpinned_t uu(to_insn); + const auto thepatch=Patch_t(from_addr,UncondJump_rel32); + const auto it=final_insn_locations.find(to_insn); + if(it==final_insn_locations.end()) + { + if (*m_verbose) + printf("Instruction cannot be patch yet, as target is unknown.\n"); + + patch_list.insert({uu,thepatch}); + } + else + { + const auto to_addr=final_insn_locations[to_insn]; + assert(to_addr!=0); + /* + * TODO: This debugging output is not really exactly correct. + */ + if (*m_verbose) + printf("Found a patch for %p -> %p\n", (void*)from_addr, (void*)to_addr); + // Apply Patch + ApplyPatch(from_addr, to_addr); + } +} + +RangeAddress_t ZiprImpl_t::_PlopDollopEntry(Zipr_SDK::DollopEntry_t *entry, RangeAddress_t override_address) +{ + const auto insn = entry->getInstruction(); + const auto insn_wcis = DetermineInsnSize(insn, false); + RangeAddress_t updated_addr = 0; + RangeAddress_t target_address = 0; + auto placed_insn = false; + const auto target_dollop=entry->getTargetDollop(); + if (target_dollop && target_dollop->front()) + { + const auto entry_target_head_insn=entry-> getTargetDollop()-> front()-> getInstruction(); + const auto target_address_iter = final_insn_locations.find(entry_target_head_insn); + if (target_address_iter != final_insn_locations.end()) + { + target_address = target_address_iter->second; + if (*m_verbose) + cout << "Found an updated target address location: " + << std::hex << target_address << endl; + } + } + + + auto placed_address = override_address == 0 ? entry->getPlace() : override_address; + const auto plop_it = plopping_plugins.find(insn); + if (plop_it != plopping_plugins.end()) + { + for (auto pp : *(plop_it->second)) + { + auto pp_placed_insn = false; + const auto handle = pp; + const auto zpi = dynamic_cast<ZiprPluginInterface_t*>(handle); + const auto plugin_ret=zpi->plopDollopEntry(entry, placed_address, target_address, insn_wcis, pp_placed_insn); + updated_addr = std::max(plugin_ret, updated_addr); + if (*m_verbose) + { + cout << zpi->toString() << " placed entry " + << std::hex << entry + << " at address: " << std::hex << placed_address + << " " << (pp_placed_insn ? "and placed" : "but did not place") + << " the instruction." + << endl; + } + + placed_insn |= pp_placed_insn; + } + } + + /* + * If no plugin actually placed the instruction, + * then we are going to do it ourselves. + */ + if (!placed_insn) + { + /* Some plugins, like scfi, may place the entry but leave it + up to zipr to place the instruction. This does assume that + zipr will place the instruction in a way that is compatible + with what the plugin is trying to do. + + TODO: Should we continue to allow this? + */ + const auto zipr_ret = PlopDollopEntry(entry, placed_address, target_address); + updated_addr = std::max(zipr_ret, updated_addr); + } + + // sanity check that we aren't moving an instruction that's already been placed. + const auto old_loc=final_insn_locations[insn]; + if(old_loc != 0 && old_loc != placed_address ) + { + static int count=0; + cout<<"Warning, Moving instruction "<<hex<<insn->getBaseID()<<":"<<insn->getComment() + <<" from "<<hex<<old_loc<<" to "<<placed_address<<endl; + cout<<"Happened for "<<dec<<count++<<" out of "<<m_firp->getInstructions().size()<<" instructions"<<endl; + } + + final_insn_locations[insn] = placed_address; + return updated_addr; +} + +RangeAddress_t ZiprImpl_t::PlopDollopEntry( + Zipr_SDK::DollopEntry_t *entry, + RangeAddress_t override_place, + RangeAddress_t override_target) +{ + Instruction_t *insn = entry->getInstruction(); + RangeAddress_t ret = entry->getPlace(), addr = entry->getPlace(); + + assert(insn); + + if (override_place != 0) + addr = ret = override_place; + + const auto d=DecodedInstruction_t::factory(insn); + + string raw_data = insn->getDataBits(); + string orig_data = insn->getDataBits(); + + + if(entry->getTargetDollop() && entry->getInstruction()->getCallback()=="") + { + RangeAddress_t target_address = 0; + auto target_insn = entry->getTargetDollop()->front()->getInstruction(); + + if (override_target == 0) + { + if (final_insn_locations.end() != final_insn_locations.find(target_insn)) + target_address = final_insn_locations[target_insn]; + } + else + { + if (*m_verbose) + cout << "Plopping with overriden target: Was: " + << hex << target_address << " Is: " << override_target << endl; + target_address = override_target; + } + + if (*m_verbose) + { + const auto print_target=((target_address != 0) ? target_address : entry->getTargetDollop()->getPlace()); + cout << "Plopping '"<<entry->getInstruction()->getDisassembly() <<"' at " << hex << addr + << " with target " << print_target << endl; + } + ret=PlopDollopEntryWithTarget(entry, addr, target_address); + } + else if(entry->getInstruction()->getCallback()!="") + { + if (*m_verbose) + cout << "Plopping at " << hex << addr << " with callback to " + << entry->getInstruction()->getCallback() << endl; + + ret=PlopDollopEntryWithCallback(entry, addr); + } + else + { + if (*m_verbose) + cout << "Plopping non-ctl "<<insn->getDisassembly()<<" at " << hex << addr << endl; + memory_space.PlopBytes(addr, insn->getDataBits().c_str(), insn->getDataBits().length()); + ret+=insn->getDataBits().length(); + } + + /* Reset the data bits for the instruction back to th + * need to re-plop this instruction later. we need t + * so we can replop appropriately. + */ + insn->setDataBits(orig_data); + return ret; +} + +RangeAddress_t ZiprImpl_t::PlopDollopEntryWithTarget( + Zipr_SDK::DollopEntry_t *entry, + RangeAddress_t override_place, + RangeAddress_t override_target) +{ + return sizer->PlopDollopEntryWithTarget(entry,override_place,override_target); +} + +RangeAddress_t ZiprImpl_t::PlopDollopEntryWithCallback( + Zipr_SDK::DollopEntry_t *entry, + RangeAddress_t override_place) +{ + auto at = entry->getPlace(); + auto originalAt = entry->getPlace(); + + if (override_place != 0) + at = originalAt = override_place; + + // emit call <callback> + { + char bytes[]={(char)0xe8,(char)0,(char)0,(char)0,(char)0}; // call rel32 + memory_space.PlopBytes(at, bytes, sizeof(bytes)); + unpatched_callbacks.insert({entry,at}); + at+=sizeof(bytes); + } + + // pop bogus ret addr + if(m_firp->getArchitectureBitWidth()==64) + { + char bytes[]={(char)0x48,(char)0x8d,(char)0x64,(char)0x24,(char)(m_firp->getArchitectureBitWidth()/0x08)}; // lea rsp, [rsp+8] + memory_space.PlopBytes(at, bytes, sizeof(bytes)); + at+=sizeof(bytes); + } + else if(m_firp->getArchitectureBitWidth()==32) + { + char bytes[]={(char)0x8d,(char)0x64,(char)0x24,(char)(m_firp->getArchitectureBitWidth()/0x08)}; // lea esp, [esp+4] + memory_space.PlopBytes(at, bytes, sizeof(bytes)); + at+=sizeof(bytes); + } + else + assert(0); + + assert(sizer->CALLBACK_TRAMPOLINE_SIZE<=(at-originalAt)); + return at; +} + + + + +DataScoop_t* ZiprImpl_t::FindScoop(const RangeAddress_t &addr) +{ + const auto find_it=find_if(ALLOF(m_firp->getDataScoops()), + [&](const DataScoop_t* scoop) + { + return scoop->getStart()->getVirtualOffset() <= addr && + addr < scoop->getEnd()->getVirtualOffset() ; + }); + return find_it==m_firp->getDataScoops().end() ? nullptr : *find_it; +} + + +void ZiprImpl_t::OutputBinaryFile(const string &name) +{ + // now that the textra scoop has been crated and setup, we have the info we need to + // re-generate the eh information. + RelayoutEhInfo(); + + const auto file_type = m_firp->getArchitecture()->getFileType(); + const auto is_elf = file_type == IRDB_SDK::adftELFEXE || file_type == IRDB_SDK::adftELFSO; + const auto is_pe = file_type == IRDB_SDK::adftPE; + const auto bit_width = m_firp->getArchitectureBitWidth(); + const auto output_filename="c.out"; + + auto ew=unique_ptr<ExeWriter>( + is_pe && bit_width == 64 ? (ExeWriter*)new PeWriter64(exeiop, m_firp, *m_add_sections, *m_bss_opts) : + is_pe && bit_width == 32 ? (ExeWriter*)new PeWriter32(exeiop, m_firp, *m_add_sections, *m_bss_opts) : + is_elf && bit_width == 64 ? (ExeWriter*)new ElfWriter64(exeiop, m_firp, *m_add_sections, *m_bss_opts) : + is_elf && bit_width == 32 ? (ExeWriter*)new ElfWriter32(exeiop, m_firp, *m_add_sections, *m_bss_opts) : + throw invalid_argument("Unknown file type/machine width combo") + ); + ew->Write(output_filename, "a.ncexe"); + ew.reset(nullptr); // explicitly free ew as we're done with it + + // change permissions on output file + const auto chmod_cmd=string("chmod +x ")+output_filename; + const auto res=command_to_stream(chmod_cmd,cout); + assert(res!=-1); + +} + + +void ZiprImpl_t::PrintStats() +{ + // do something like print stats as #ATTRIBUTES. + m_dollop_mgr.PrintStats(cout); + m_dollop_mgr.PrintPlacementMap(memory_space, *m_dollop_map_filename); + m_stats->PrintStats(cout); + + // and dump a map file of where we placed instructions. maybe guard with an option. + // default to dumping to zipr.map + dump_scoop_map(); + dump_instruction_map(); +} + +void ZiprImpl_t::UpdateCallbacks() +{ + // first byte of this range is the last used byte. + const auto range_it=memory_space.FindFreeRange((RangeAddress_t) -1); + assert(memory_space.IsValidRange(range_it)); + + + for(const auto &p : unpatched_callbacks) + { + auto entry=p.first; + Instruction_t *insn = entry->getInstruction(); + RangeAddress_t at=p.second; + RangeAddress_t to=0x0;//FindCallbackAddress(end_of_new_space,start_addr,insn->getCallback()); + DLFunctionHandle_t patcher = nullptr; + + if (plugman.DoesPluginRetargetCallback(at, entry, to, patcher)) + { + if (*m_verbose) + { + cout << "Patching retargeted callback at " << std::hex << at << " to " + << patcher->toString() << "-assigned address: " + << std::hex << to << endl; + } + } + + if(to) + { + cout<<"Patching callback "<< insn->getCallback()<<" at "<<std::hex<<at<<" to jump to "<<to<<endl; + PatchCall(at,to); + } + else + { + CallToNop(at); + } + } +} + +void ZiprImpl_t::dump_scoop_map() +{ + string filename="scoop.map"; // parameterize later. + std::ofstream ofs(filename.c_str(), ios_base::out); + ofs <<left<<setw(10)<<"ID" + <<left<<setw(10)<<"StartAddr" + <<left<<setw(10)<<"Size" + <<left<<setw(10)<<"Perms" + <<left<<setw(10)<<"Name"<<endl; + + for(const auto &scoop : m_firp->getDataScoops()) + { + ofs << hex << setw(10) << scoop->getBaseID() + << hex << left << setw(10) << scoop->getStart()->getVirtualOffset() + << hex << left << setw(10) << scoop->getSize() + << hex << left << setw(10) << +scoop->getRawPerms() // print as int, not char + << hex << left << setw(10) << scoop->getName() + << endl; + } +} +void ZiprImpl_t::dump_instruction_map() +{ + string filename="zipr.map"; // parameterize later. + std::ofstream ofs(filename.c_str(), ios_base::out); + + ofs <<left<<setw(10)<<"ID" + <<left<<setw(10)<<"OrigAddr" + <<left<<setw(10)<<"IBTA" + <<left<<setw(10)<<"NewAddr" + <<left<<setw(10)<<"FuncID" + <<left<<"Disassembly"<<endl; + + for(std::map<IRDB_SDK::Instruction_t*,RangeAddress_t>::iterator it=final_insn_locations.begin(); + it!=final_insn_locations.end(); ++it) + { + Instruction_t* insn=it->first; + AddressID_t* ibta=insn->getIndirectBranchTargetAddress(); + RangeAddress_t addr=it->second; + + ofs << hex << setw(10)<<insn->getBaseID() + <<hex<<left<<setw(10)<<insn->getAddress()->getVirtualOffset() + <<hex<<left<<setw(10)<< (ibta ? ibta->getVirtualOffset() : 0) + <<hex<<left<<setw(10)<<addr + <<hex<<left<<setw(10)<<( insn->getFunction() ? insn->getFunction()->getBaseID() : -1 ) + << left<<insn->getDisassembly()<<endl; + + + } +} + +void ZiprImpl_t::UpdateScoops() +{ + for( + DataScoopSet_t::iterator it=m_zipr_scoops.begin(); + it!=m_zipr_scoops.end(); + ) + { + DataScoop_t* scoop=*it; + VirtualOffset_t first_valid_address=0; + VirtualOffset_t last_valid_address=0; + + if(!scoop->isExecuteable()) + { + ++it; + continue; + } + + assert(m_zipr_scoops.find(scoop)!=m_zipr_scoops.end()); + + /* + * Yes, I know that this adds another iteration, but we need to know + * beforehand about shrinking. + */ + + if (scoop->getName() == "textra") + { + /* + * We have to do special handling for the textra scoop. + * If we do not, then the sheer scale of the default size + * of the textra scoop will cause Zipr to bomb during the + * next loop. + */ + auto frit=memory_space.FindFreeRange((RangeAddress_t) -1); + assert(memory_space.IsValidRange(frit)); + scoop->getEnd()->setVirtualOffset(frit->getStart()); + } + + for(auto i=scoop->getStart()->getVirtualOffset(); + i<= scoop->getEnd()->getVirtualOffset(); + i++ ) + { + if( ! memory_space.IsByteFree(i) ) + { + // record beginning if not already recorded. + if(first_valid_address==0) + first_valid_address=i; + + // record that this address was valid. + last_valid_address=i; + } + } + + if(last_valid_address==0 || first_valid_address==0) + { + if (*m_verbose) + cout << "Removing an empty scoop (" << scoop->getName() << ")." << endl; + /* + assert(first_valid_address==0); + assert(last_valid_address==0); + m_firp->getAddresses().erase(scoop->getStart()); + m_firp->getAddresses().erase(scoop->getEnd()); + + + m_firp->getDataScoops().erase(*it); + + // Delete addresses and then the scoop itself. + delete scoop->getStart(); + delete scoop->getEnd(); + delete scoop; + */ + it = m_zipr_scoops.erase(it); + m_firp->removeScoop(scoop); + scoop=nullptr; + } + else + { + if ((scoop->getStart()->getVirtualOffset() != first_valid_address || + scoop->getEnd()->getVirtualOffset() != last_valid_address) && + *m_verbose) + { + cout <<"Shrinking scoop "<<scoop->getName() + <<" to " + << std::hex << first_valid_address << "-" + << std::hex << last_valid_address << endl; + } + else if (*m_verbose) + { + cout<<"Leaving scoop "<<scoop->getName()<<" alone. "<<endl; + } + + cout << "Updating a scoop named " << scoop->getName() << endl; + assert(first_valid_address!=0); + assert(last_valid_address!=0); + scoop->getStart()->setVirtualOffset(first_valid_address); + scoop->getEnd()->setVirtualOffset(last_valid_address); + + /* + * Resize the contents. + */ + auto scoop_contents = scoop->getContents(); + scoop_contents.resize(scoop->getEnd()->getVirtualOffset() - + scoop->getStart()->getVirtualOffset() + 1); + assert(scoop->getSize() == scoop_contents.size()); + + /* + * And now update the contents. + */ + for(auto i=scoop->getStart()->getVirtualOffset(); + i<= scoop->getEnd()->getVirtualOffset(); + i++) + { + scoop_contents[i-scoop->getStart()->getVirtualOffset()]=memory_space[i]; + } + scoop->setContents(scoop_contents); + // m_firp->getDataScoops().insert(scoop); we added this earlier when we created the obj. + // jdh -- a bit worried that this'll break assumptions in other places + ++it; + } + } + return; +} + +void ZiprImpl_t::FixNoFallthroughs() +{ + auto hlt=archhelper->createNewHaltInstruction(m_firp, nullptr); + auto jmp=archhelper->createNewJumpInstruction(m_firp, nullptr); + + hlt->setFallthrough(jmp); + jmp->setTarget(hlt); + + for(const auto insn : m_firp->getInstructions()) + { + if(insn==hlt) continue; + if(insn==jmp) continue; + + if(insn->getFallthrough()==nullptr) + { + const auto d=DecodedInstruction_t::factory(insn); + if(d->isConditionalBranch()) + insn->setFallthrough(hlt); + } + if(insn->getTarget()==nullptr) + { + const auto d=DecodedInstruction_t::factory(insn); + if(d->isBranch() && !d->isReturn() && d->hasOperand(0) && d->getOperand(0)->isConstant()) + insn->setTarget(hlt); + } + + } + + +} + +void ZiprImpl_t::FixTwoByteWithPrefix() +{ + const auto is_x64 = m_firp->getArchitecture()->getMachineType() == admtX86_64; + const auto is_x32 = m_firp->getArchitecture()->getMachineType() == admtI386; + + if(!is_x64 && !is_x32) return; // only do this for x86 machines. + + for(const auto insn : m_firp->getInstructions()) + { + const auto d=DecodedInstruction_t::factory(insn); + if(!d->isBranch()) continue; // skip non-branches + if(d->isReturn()) continue; // skip returns + if(d->getOperands().size()!=1) continue; // skip branches that have no operands or more than one + if(!d->getOperand(0)->isConstant()) continue; // skip anything where the operand isn't a constant + + + while (true) + { + const auto b=static_cast<uint8_t>(insn->getDataBits()[0]); + // basic prefix check + const auto prefixes=set<uint8_t>({0x2e, 0x36, 0x3e, 0x26, 0x64, 0x65, 0x2e, 0x3e, 0xf0, 0xf2, 0xf3, 0x66, 0x67}); + if(prefixes.find(b)!=end(prefixes)) + { + // remove prefix + insn->setDataBits(insn->getDataBits().erase(0,1)); + } + // remove rex prefix when unnecessary + else if(m_firp->getArchitectureBitWidth()==64 && (b&0xf0)==0x40 /* has rex prefix */) + { + insn->setDataBits(insn->getDataBits().erase(0,1)); + } + else + break; + } + + } +} + + +void ZiprImpl_t::FixMultipleFallthroughs() +{ + auto count=0; + auto fallthrough_from=map<Instruction_t*, InstructionSet_t>(); + + for(auto & insn : m_firp->getInstructions()) + { + auto ft=insn->getFallthrough(); + if(ft) + fallthrough_from[ft].insert(insn); + }; + + for(auto &p : fallthrough_from) + { + auto ft=p.first; + if(p.second.size()>1) + { + // skip the first one, because something can fallthrough, just not everything. + for_each(next(p.second.begin()), p.second.end(), [&](Instruction_t* from) + { + + auto newjmp=archhelper->createNewJumpInstruction(m_firp,nullptr); + count++; + newjmp->setTarget(ft); + from->setFallthrough(newjmp); + + }); + }; + } + + // after we've inserted all the jumps, assemble them. + m_firp->assembleRegistry(); + + cout<<"# ATTRIBUTE Zipr::jumps_inserted_for_multiple_fallthroughs="<<dec<<count<<endl; +} + + +void ZiprImpl_t::RelayoutEhInfo() +{ + if(m_firp->getAllEhPrograms().size() == 0 && m_firp->getAllEhCallSites().size() ==0) + return; + + EhWriter::EhWriter_t::factory(*this) -> GenerateNewEhInfo(); +} + + +void ZiprImpl_t::ApplyNopToPatch(RangeAddress_t addr) +{ + if (!*m_apply_nop) + { + if (*m_verbose) + cout << "Skipping chance to apply nop to fallthrough patch." << endl; + return; + } + assert(patcher); + patcher->ApplyNopToPatch(addr); +} +void ZiprImpl_t::ApplyPatch(RangeAddress_t from_addr, RangeAddress_t to_addr) +{ + assert(patcher); + patcher->ApplyPatch(from_addr,to_addr); +} +void ZiprImpl_t::PatchJump(RangeAddress_t at_addr, RangeAddress_t to_addr) +{ + assert(patcher); + patcher->PatchJump(at_addr,to_addr); +} + diff --git a/zipr/src/zipr_dollop_man.cpp b/zipr/src/zipr_dollop_man.cpp new file mode 100644 index 0000000000000000000000000000000000000000..c7c374f9391860272aeba1b27d73eed76981afd0 --- /dev/null +++ b/zipr/src/zipr_dollop_man.cpp @@ -0,0 +1,440 @@ +#include <zipr_all.h> +#include <iostream> +#include <cstdlib> + +using namespace zipr; +using namespace std; +using namespace Zipr_SDK; +using namespace IRDB_SDK; + +#define ALLOF(a) std::begin(a),std::end(a) + +namespace zipr { + + Zipr_SDK::Dollop_t *ZiprDollopManager_t::AddNewDollops(Instruction_t *start) { + Zipr_SDK::Dollop_t *new_dollop = nullptr; + auto existing_dollop = getContainingDollop(start); + + /* + * This is the target dollop *only* + * if the target instruction is the first instruction. + */ + if (existing_dollop) + { + /* + * There is a target dollop. But, do we need to split it? + */ + if (existing_dollop->getDollopEntryCount() && + existing_dollop->front()->getInstruction() == start) { + /* + * Just return the existing dollop. + */ + return existing_dollop; + } + else { + /* + * Split at this dollop to make a new one! + */ + addDollops(new_dollop = existing_dollop->split(start)); + return new_dollop; + } + } + else + { + /* + * There is no target dollop. Let's create one! + */ + DollopEntryList_t::iterator it, it_end; + Zipr_SDK::Dollop_t *original_new_dollop = nullptr, *previous_dollop = nullptr; + Instruction_t *fallthrough = nullptr; + original_new_dollop = new_dollop = Dollop_t::createNewDollop(start,this); + + for (it = new_dollop->begin(), it_end = new_dollop->end(); + it != it_end; + it++) + { + auto containing_dollop = getContainingDollop((*it)->getInstruction()); + if (containing_dollop) + { + if (true) + cout << "Found an instruction in a new dollop that " + << "is already in a dollop: " << std::hex + << ((nullptr!=(*it)->getInstruction()->getAddress()) ? + (*it)->getInstruction()->getAddress()->getVirtualOffset():0x0) + << endl; + /* + * Reliably get a pointer to the containing dollop. + */ + auto fallthrough_dollop = addNewDollops((*it)->getInstruction()); + + /* + * Link this dollop to that one. Do this before + * removing the entries because RemoveDollopEntries() + * will recalculate the size and needs to know about + * the updated fallthrough dollop! + */ + new_dollop->setFallthroughDollop(fallthrough_dollop); + fallthrough_dollop->setFallbackDollop(new_dollop); + + /* + * Delete the overlapping instructions. + */ + new_dollop->removeDollopEntries(it, it_end); + + /* + * Put the new dollop in! + */ + addDollop(new_dollop); + + return new_dollop; + } + } + /* + * This is to handle the case where + * we stopped creating a dollop because + * the next instruction is pinned. We do + * not want to forget about the remaining + * entries here. So, we attempt to link + * to those, where possible. + */ + while ((fallthrough = new_dollop->back() ->getInstruction() ->getFallthrough()) != nullptr) + { + /* + * Look FIRST for a containing dollop. + * + * TODO: We *assert* that we do not have + * to check whether or not the fallthrough + * instruction is at the top of the stack. + * This is because we are only at this case + * when the dollop construction ended because + * the fallthrough is pinned. This implicitly + * means that it is the first instruction + * in the containing dollop. + */ + auto existing_dollop = getContainingDollop(fallthrough); + if (existing_dollop) + { + assert(existing_dollop->front()->getInstruction() == fallthrough); + new_dollop->setFallthroughDollop(existing_dollop); + existing_dollop->setFallbackDollop(new_dollop); + break; + } + /* + * Otherwise, create a new dollop from the fallthrough + * and link them together. + */ + previous_dollop = new_dollop; + + // cannot do this: + // new_dollop = Dollop_t::CreateNewDollop(fallthrough, this); + // because CreateNewDollop does not adaquately trim the dollop + // and it might result in an instruction being in two dollops + // Using AddNewDollops instead. + new_dollop = this->addNewDollops(fallthrough); + previous_dollop->setFallthroughDollop(new_dollop); + new_dollop->setFallbackDollop(previous_dollop); + } + addDollops(original_new_dollop); + return original_new_dollop; + } + } + + size_t ZiprDollopManager_t::DetermineDollopEntrySize(Zipr_SDK::DollopEntry_t *entry) + { + const auto l_zipr=dynamic_cast<ZiprImpl_t*>(m_zipr); + const auto sizer=l_zipr->getSizer(); + if (m_zipr != nullptr) + return m_zipr->determineDollopEntrySize(entry, false); + else + return sizer->DetermineInsnSize(entry->getInstruction(), false); + } + + void ZiprDollopManager_t::PrintDollopPatches(const ostream &out) { + // std::list<DollopPatch_t*>::const_iterator patch_it, patch_it_end; + + for (auto patch_it = m_patches.begin(), patch_it_end = m_patches.end(); + patch_it != patch_it_end; + patch_it++) { + cout << *(*patch_it) << endl; + } + } + + Zipr_SDK::Dollop_t *ZiprDollopManager_t::getContainingDollop(IRDB_SDK::Instruction_t *insn) + { + const auto it=m_insn_to_dollop.find(insn); + return it!=m_insn_to_dollop.end() ? it->second : nullptr; + + } + + void ZiprDollopManager_t::AddDollops(Zipr_SDK::Dollop_t *dollop_head) { + auto dollop = dollop_head; + while (dollop != nullptr) + { + addDollop(dollop); + dollop = dollop->getFallthroughDollop(); + } + m_refresh_stats = true; + } + + + + + /* TODO: Write a test case for the new conditional push_back. Make + * sure to test whether or not the instruction-to-dollop map + * is properly updated in all cases. + */ + void ZiprDollopManager_t::addDollop(Zipr_SDK::Dollop_t *dollop) { + /* + * We always want to update the isntruction-to-dollop map. + * However, we might not always want to push it on to the + * list of dollops -- it might already be there! + */ + /* + * Populate/update the instruction-to-dollop map. + */ + // std::list<DollopEntry_t*>::iterator it, it_end; + for (auto it = dollop->begin(), it_end = dollop->end(); + it != it_end; + it++) { + m_insn_to_dollop[(*it)->getInstruction()] = dollop; + } + /* + * Push the actual dollop onto the list of dollops + * if it's not already there. + */ + m_dollops.insert(dollop); + m_refresh_stats = true; + } + + bool ZiprDollopManager_t::UpdateTargets(Zipr_SDK::Dollop_t *dollop) { + auto changed = false; + const auto local_dollop=DollopEntryList_t(ALLOF(*dollop)); + for (auto &entry : local_dollop ) + { + auto insn=entry->getInstruction(); + if (insn->getTarget()) + { + auto new_target=addNewDollops(insn->getTarget()); + + /* + * In the case there is a change, we have to restart. + * The dollop that we are updating could itself have + * contained the target and the call would have + * split this dollop. That makes the iterator go + * haywire. + * + * But! We could avoid the break by using a copy of the set, which we do. + */ + if (new_target != entry->getTargetDollop()) { + entry->setTargetDollop(new_target); + changed = true; + } + } + } + + return changed; + } + + void ZiprDollopManager_t::UpdateAllTargets(void) { + // Used to make sure dollops are created for instructions referenced by + // relocations. + const auto handle_reloc=[this](const Relocation_t* reloc) + { + auto wrt_insn=dynamic_cast<Instruction_t*>(reloc->getWRT()); + if(wrt_insn) + { + // we don't bother marking a change because + // we only need to do this once for relocs + // and we are certain to get here once for every dollop + addNewDollops(wrt_insn); + cout<<"Adding new dollop for reloc of type="<<reloc->getType()<<endl; + + } + }; + + // Make sure dollops are created for instructions referenced by + // relocations. + for(auto &reloc : m_zipr->getFileIR()->getRelocations()) + handle_reloc(reloc); + + + auto changed = false; + auto changed_count=0; + auto update_count=0; + do { + changed = false; + const auto local_dollops=m_dollops; + for (auto entry : local_dollops) + { + changed |= UpdateTargets(entry); + update_count++; + if((update_count%1000000) == 0 ) + cout<<"number of dollops="<<dec<<m_dollops.size()<<". "<<dec<<update_count<<" iterations attempted."<<endl; + } + changed_count++; + } while (changed); + cout<<"All Targets updated. changed_count="<<dec<<changed_count<<". Update_count="<<update_count<<"."<<endl; + } + + std::ostream &operator<<(std::ostream &out, const ZiprDollopManager_t &dollop_man) { + DollopList_t::iterator it, it_end; + + for (it = dollop_man.m_dollops.begin(), it_end = dollop_man.m_dollops.end(); + it != it_end; + it++) { + auto entry = *it; + out << std::hex << entry << std::endl; + out << *entry << std::endl; + } + return out; + } + + void ZiprDollopManager_t::CalculateStats() + { + m_truncated_dollops = 0; + m_total_dollop_entries = 0; + m_total_dollops = Size(); + + for (auto dollop : m_dollops ) + { + m_total_dollop_entries += dollop->getDollopEntryCount(); + if (dollop->wasTruncated()) + m_truncated_dollops++; + } + m_refresh_stats = false; + } + + void ZiprDollopManager_t::PrintStats(std::ostream &out) + { + if (m_refresh_stats) + CalculateStats(); + + PrintStat(out, "Total dollops", m_total_dollops); + //PrintStat(out, "Total dollop size", total_dollop_space); + PrintStat(out, "Total dollop entries", m_total_dollop_entries); + PrintStat(out, "Truncated dollops", m_truncated_dollops); + PrintStat(out, "Avg dollop entries per dollop", + (double)m_total_dollop_entries/(double)m_total_dollops); + PrintStat(out, "Truncated dollop fraction", + (double)m_truncated_dollops/(double)m_total_dollops); + } + +#define LINE_LENGTH 32 +#define PRINT_LINE_HEADER(x) \ + map_output << endl << std::hex << (x) << ": "; + + void ZiprDollopManager_t::AddDollopPatch(DollopPatch_t *new_patch) + { + m_patches_to_dollops[new_patch->getTarget()].push_back(new_patch); + } + + void ZiprDollopManager_t::PrintPlacementMap( + const MemorySpace_t &_memory_space, + const std::string &map_filename) + { + const auto &memory_space = static_cast<const ZiprMemorySpace_t &>(_memory_space); + auto original_ranges = memory_space.getOriginalFreeRanges(); + + ofstream map_output(map_filename.c_str(), std::ofstream::out); + if (!map_output.is_open()) + return; + /* + * Loop through the original ranges. + */ + for (auto range_it=original_ranges.begin(), range_it_end=original_ranges.end(); + range_it != range_it_end; + range_it++) + { + /* + * Now loop through the dollops and + * record those contained in this range. + */ + auto current_range = *range_it; + map<RangeAddress_t, Zipr_SDK::Dollop_t*> dollops_in_range; + RangeAddress_t previous_dollop_end = 0; + Zipr_SDK::Dollop_t *dollop_to_print = nullptr; + + for (auto dollop_it = m_dollops.begin(), dollop_it_end = m_dollops.end(); + dollop_it != dollop_it_end; + dollop_it++) + { + auto dollop = (*dollop_it); + if (current_range.getStart() <= dollop->getPlace() && + current_range.getEnd() >= dollop->getPlace()) + dollops_in_range[dollop->getPlace()] = dollop; + } + + map_output << "==========" << endl; + map_output << "Range: 0x" << std::hex << current_range.getStart() + << " - 0x" << std::hex << current_range.getEnd() + << endl; + + previous_dollop_end = current_range.getStart(); + unsigned byte_print_counter = 0; + for (auto dollops_in_range_it = dollops_in_range.begin(), + dollops_in_range_end = dollops_in_range.end(); + dollops_in_range_it != dollops_in_range_end; + dollops_in_range_it++) + { + dollop_to_print = (*dollops_in_range_it).second; + if (previous_dollop_end < dollop_to_print->getPlace()) + { + for (unsigned i=0;i<(dollop_to_print->getPlace()-previous_dollop_end);i++) + { + if (!((byte_print_counter) % LINE_LENGTH)) + PRINT_LINE_HEADER((current_range.getStart()+byte_print_counter)) + map_output << "_"; + byte_print_counter++; + } +#if 0 + map_output << "0x" << std::hex << previous_dollop_end + << " - 0x" <<std::hex <<(dollop_to_print->getPlace()) + << ": (" << std::dec + << (dollop_to_print->getPlace() - previous_dollop_end) + << ") EMPTY" << endl; +#endif + } + for (unsigned i=0;i<(dollop_to_print->getSize());i++) + { + if (!((byte_print_counter) % LINE_LENGTH)) + PRINT_LINE_HEADER((current_range.getStart()+byte_print_counter)) + map_output << "X"; + byte_print_counter++; + } +#if 0 + map_output << "0x" << std::hex << dollop_to_print->getPlace() + << " - 0x" << std::hex + <<(dollop_to_print->getPlace()+dollop_to_print->getSize()) + << ": (" << std::dec << dollop_to_print->getSize() + << ") " + << endl; + +#endif + previous_dollop_end = dollop_to_print->getPlace() + + dollop_to_print->getSize(); + } + + if (dollop_to_print && current_range.getEnd() != (RangeAddress_t)-1 && + (previous_dollop_end < current_range.getEnd())) + { + for (unsigned i=0;i<(current_range.getEnd() - previous_dollop_end);i++) + { + if (!((byte_print_counter) % LINE_LENGTH)) + PRINT_LINE_HEADER((current_range.getStart()+byte_print_counter)) + map_output << "_"; + byte_print_counter++; + } +#if 0 + map_output << "0x" << std::hex << dollop_to_print->getPlace() + << " - 0x" << std::hex + <<(dollop_to_print->Place()+dollop_to_print->getSize()) + << ": (" << std::dec + << (current_range.getEnd() - previous_dollop_end) + << ") EMPTY" << endl; +#endif + } + map_output << endl; + } + map_output.close(); + } +} diff --git a/zipr/src/zipr_options.cpp b/zipr/src/zipr_options.cpp new file mode 100644 index 0000000000000000000000000000000000000000..c18800f79658a87f38517c207dbe53b8c42c9b2c --- /dev/null +++ b/zipr/src/zipr_options.cpp @@ -0,0 +1,275 @@ +/* + * Copyright (c) 2014 - Zephyr Software LLC + * + * This file may be used and modified for non-commercial purposes as long as + * all copyright, permission, and nonwarranty notices are preserved. + * Redistribution is prohibited without prior written consent from Zephyr + * Software. + * + * Please contact the authors for restrictions applying to commercial use. + * + * THIS SOURCE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF + * MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * Author: Zephyr Software + * e-mail: jwd@zephyr-software.com + * URL : http://www.zephyr-software.com/ + * + */ + +#include <zipr-sdk> +#include <zipr_options.h> +#include <unistd.h> +#include <iostream> +#include <cstddef> +#include <iomanip> + +#ifndef IMPLEMENTATION_DEBUG +#define IMPLEMENTATION_DEBUG 0 +#endif + +using namespace zipr; +using namespace std; + +void ZiprOptionsNamespace_t::printNamespace() { + ZiprOptionsNamespace_t::const_iterator it, it_end = end(); + for (it = begin(); it != it_end; it++) { + cout << (*it)->getKey() << ": " << (*it)->getStringValue() << endl; + } +} + +bool ZiprOptionsNamespace_t::areRequirementsMet() const { + ZiprOptionsNamespace_t::const_iterator it, it_end = end(); + for (it = begin(); it != it_end; it++) { + if (!(*it)->areRequirementMet()) + return false; + } + return true; +} + +void ZiprOptionsNamespace_t::addOption(ZiprOption_t *option) { + ZiprOption_t *existing_option = optionByKey(option->getKey()); + if (existing_option) { +#if IMPLEMENTATION_DEBUG + cout << "Found an existing option. Adding an observer." << endl; +#endif + existing_option->addObserver(option); + } + else { + insert(option); + } +} + +ZiprOption_t *ZiprOptionsNamespace_t::optionByKey(const string& key) { + ZiprOptionsNamespace_t::const_iterator it, it_end = end(); + for (it = begin(); it != it_end; it++) { + if ((*it)->getKey() == key) + return *it; + } + return nullptr; +} + +void ZiprOptionsNamespace_t::printUsage(int tabs, ostream &out) { + ZiprOptionsNamespace_t::const_iterator it, it_end = end(); + for (it = begin(); it != it_end; it++) { + string description = (*it)->getDescription(); + { int t = 0; for (; t<tabs; t++) cout << "\t"; } + out << std::setw(2); + if (!(*it)->isRequired()) + out << "["; + else + out << ""; + out << "--" + getNamespace() << ":" << description; + if (!(*it)->isRequired()) + out << " ]"; + out << endl; + } +} + +void ZiprOptions_t::printUsage(ostream &out) +{ + set<ZiprOptionsNamespace_t*>::const_iterator it, it_end = m_namespaces.end(); + for (it = m_namespaces.begin(); it != it_end; it++) + (*it)->printUsage(1, out); +} + +bool ZiprOptions_t::areRequirementsMet() const +{ + set<ZiprOptionsNamespace_t*>::const_iterator it, it_end = m_namespaces.end(); + for (it = m_namespaces.begin(); it != it_end; it++) + if (!(*it)->areRequirementsMet()) + return false; + return true; +} + +ZiprOptions_t::ZiprOptions_t(int argc, char **argv) +{ + int i = 0; + for (i = 0; i<argc; i++) { + m_arguments.push_back(string(argv[i])); + } +} + +bool ZiprOptions_t::parse(ostream *error, ostream *warn) +{ + bool success = true; + + auto it_end = m_arguments.end(); + for (auto it = m_arguments.begin(); it != it_end; it++) { + string ns, key, argument = *it; + string::size_type location = 0; + bool next_is_option_value = false; + + if (0 != (location = argument.find_first_of("--"))) { + if (warn) + *warn << "Warning: " << argument << " does not start with --" << endl; + continue; + } +#if IMPLEMENTATION_DEBUG + cout << "location: " << location << endl; +#endif + argument = argument.substr(location+2, string::npos); + if (string::npos == (location = argument.find_first_of(":"))) { + if (warn) + *warn << "Warning: " << argument << " going in global namespace."<<endl; + ns = "global"; + location = -1; + } else { + ns = argument.substr(0, location); + } +#if IMPLEMENTATION_DEBUG + cout << "argument: " << argument << endl; +#endif + key = argument.substr(location+1, string::npos); +#if IMPLEMENTATION_DEBUG + cout << "ns: " << ns << endl; + cout << "key: " << key << endl; +#endif + auto option_ns = dynamic_cast<zipr::ZiprOptionsNamespace_t*>(getNamespace(ns)); + if (!option_ns) + { + if (error) + *error << "Invalid namespace: " << ns << endl; + success = false; + continue; + } + auto option_option = option_ns->optionByKey(key); + if (!option_option) + { + if (error) + *error << "Error: namespace " + << ns + << " does not accept key " + << key << endl; + success = false; + continue; + } + /* + * By default, options need and take values. Some, though, + * take values but don't need them. Finally, some neither + * take nor need values. + */ + if (((it+1) != it_end) && (0 != (location = (*(it+1)).find_first_of("--")))) + { + next_is_option_value = true; + } + if (option_option->getNeedsValue()) + { + if ((it + 1) == it_end || !next_is_option_value) + { + if (error) + *error << ns << ":" << key << " is missing value." << endl; + success = false; + continue; + //return false; + } + option_option->setValue(*(++it)); + } + else if (option_option->getTakesValue()) + { + /* + * Check to see if the next argument starts with --. + * If it does, we consider it the next option + * and not the value to the previous option. + */ + if (next_is_option_value) + { + option_option->setValue(*(++it)); + } + else + { + option_option->setOption(); + } + } + else + { + option_option->setOption(); + } + } + return success; +} + +Zipr_SDK::ZiprOptionsNamespace_t *ZiprOptions_t::getNamespace(const string& ns) +{ + auto it_end = m_namespaces.end(); + for (auto it = m_namespaces.begin(); it != it_end; it++) + { + if ((*it)->getNamespace() == ns) + return *it; + } + + auto ret=new zipr::ZiprOptionsNamespace_t(ns); + m_namespaces.insert(ret); + return ret; +} + +void ZiprOptions_t::addNamespace(ZiprOptionsNamespace_t *ns) +{ + if (ns) + m_namespaces.insert(ns); +} + +void ZiprOptionsNamespace_t::mergeNamespace(ZiprOptionsNamespace_t *in) +{ + if (!in) return; + auto it_end = in->end(); + for (auto it = in->begin(); it != it_end; it++) + addOption(*it); +} + +void ZiprOptions_t::printNamespaces() +{ + auto it_end = m_namespaces.end(); + for (auto it = m_namespaces.begin(); it != it_end; it++) + { + cout << (*it)->getNamespace() << endl; + (*it)->printNamespace(); + } +} + + +#if 0 +Zipr_SDK::ZiprOptionsNamespace_t* ZiprOptions_t::getNamespace(const string& name) +{ +} + + +Zipr_SDK::ZiprStringOption_t* zipr::ZiprOptionsNamespace_t::getStringOption (const string& name, const string &description, const string& default_value) +{ +} + +Zipr_SDK::ZiprIntegerOption_t* zipr::ZiprOptionsNamespace_t::getIntegerOption(const string& name, const string &description, const size_t& default_value) +{ +} + +Zipr_SDK::ZiprBooleanOption_t* zipr::ZiprOptionsNamespace_t::getBooleanOption(const string& name, const string &description, const bool & default_value) +{ +} + +Zipr_SDK::ZiprDoubleOption_t* zipr::ZiprOptionsNamespace_t::getDoubleOption (const string& name, const string &description, const double& default_value) +{ + +} + +#endif diff --git a/zipr/src/zipr_stats.cpp b/zipr/src/zipr_stats.cpp new file mode 100644 index 0000000000000000000000000000000000000000..19e41a8da4d800cacc4ecf3f7489062b22b86d2e --- /dev/null +++ b/zipr/src/zipr_stats.cpp @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2014 - Zephyr Software LLC + * + * This file may be used and modified for non-commercial purposes as long as + * all copyright, permission, and nonwarranty notices are preserved. + * Redistribution is prohibited without prior written consent from Zephyr + * Software. + * + * Please contact the authors for restrictions applying to commercial use. + * + * THIS SOURCE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF + * MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * Author: Zephyr Software + * e-mail: jwd@zephyr-software.com + * URL : http://www.zephyr-software.com/ + * + */ + +#include <zipr_all.h> +#include <unistd.h> +#include <stdlib.h> +#include <getopt.h> +#include <iostream> + +using namespace zipr; + +void Stats_t::PrintStats(std::ostream &out) +{ + /* + * Optimizations + */ + //if(opts.IsEnabledOptimization(Optimizations_t::OptimizationFallthroughPinned)) + // TODO + if (false) + { + PrintStat(out, "Optimization: FallthroughPinned hit rate", + (double)Hits[Optimizations_t::OptimizationFallthroughPinned]/ + (Hits[Optimizations_t::OptimizationFallthroughPinned] + + Misses[Optimizations_t::OptimizationFallthroughPinned])); + } + + PrintStat(out, "Total trampolines", total_trampolines); + PrintStat(out, "Total 2-byte pin trampolines", total_2byte_pins); + PrintStat(out, "Total 5-byte pin trampolines", total_5byte_pins); + PrintStat(out, "Total trampoline space pins", total_tramp_space); + PrintStat(out, "Other space", total_other_space); + PrintStat(out, "Total free ranges", total_free_ranges); + PrintStat(out, " Coalesced", total_did_coalesce); + PrintStat(out, "Not Coalesced", total_did_not_coalesce); + PrintStat(out, "Truncated During Coalesced", truncated_dollops_during_coalesce); +} diff --git a/zipr/test/MemorySpace.cpp b/zipr/test/MemorySpace.cpp new file mode 100644 index 0000000000000000000000000000000000000000..e787b574cc25b17150febdcf68eba44b528a6715 --- /dev/null +++ b/zipr/test/MemorySpace.cpp @@ -0,0 +1,339 @@ +/* + * Copyright (c) 2014 - Zephyr Software LLC + * + * This file may be used and modified for non-commercial purposes as long as + * all copyright, permission, and nonwarranty notices are preserved. + * Redistribution is prohibited without prior written consent from Zephyr + * Software. + * + * Please contact the authors for restrictions applying to commercial use. + * + * THIS SOURCE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF + * MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * Author: Zephyr Software + * e-mail: jwd@zephyr-software.com + * URL : http://www.zephyr-software.com/ + * + */ + +#include <zipr_all.h> + +using namespace zipr; +using namespace std; + +#define INVOKE(a) \ +bool __ ## a ## _result = false; \ +__ ## a ## _result = a(); \ +printf(#a ":"); \ +if (__ ## a ## _result) \ +{ \ +printf(" pass\n"); \ +} \ +else \ +{ \ +printf(" fail\n"); \ +} + +bool TestSplitMemorySpace() +{ + ZiprMemorySpace_t m; + + m.AddFreeRange(Range_t(500, 600)); + + m.SplitFreeRange(550); + + assert(m.GetRangeCount() == 2); + + return true; +} + +bool TestBinarySearchMaxRange() +{ + std::set<Range_t>::iterator foundRange; + ZiprMemorySpace_t m; + m.AddFreeRange(Range_t(256, (RangeAddress_t)-1)); + + m.SplitFreeRange(336); + m.SplitFreeRange(345); + m.SplitFreeRange(355); + m.SplitFreeRange(365); + m.SplitFreeRange(375); + m.SplitFreeRange(385); + + + cout << "Looking for 0x" << std::hex << (RangeAddress_t)-1 << ":" << endl; + foundRange = m.FindFreeRange(RangeAddress_t((RangeAddress_t)-1)); + assert(m.IsValidRange(foundRange)); + cout << "Found: 0x" << std::hex << (*foundRange).getStart() << " - 0x" << (*foundRange).getEnd() << endl; + + return true; +} + + +bool TestBinarySearch() +{ + std::set<Range_t>::iterator foundRange; + ZiprMemorySpace_t m; + m.AddFreeRange(Range_t(256, 512)); + + m.SplitFreeRange(300); + m.SplitFreeRange(315); + m.SplitFreeRange(336); + m.SplitFreeRange(337); + m.SplitFreeRange(400); + + m.PrintMemorySpace(cout); + m.PrintMemorySpace(cout); + + cout << "Looking for 0x" << std::hex << 258 << ":" << endl; + foundRange = m.FindFreeRange(RangeAddress_t(258)); + assert(m.IsValidRange(foundRange)); + cout << "Found: 0x" << std::hex << (*foundRange).getStart() << " - 0x" << (*foundRange).getEnd() << endl; + + cout << "Looking for 0x" << std::hex << 335 << ":" << endl; + foundRange = m.FindFreeRange(RangeAddress_t(335)); + assert(m.IsValidRange(foundRange)); + cout << "Found: 0x" << std::hex << (*foundRange).getStart() << " - 0x" << (*foundRange).getEnd() << endl; + + cout << "Looking for 0x" << std::hex << 301 << ":" << endl; + foundRange = m.FindFreeRange(RangeAddress_t(301)); + assert(m.IsValidRange(foundRange)); + cout << "Found: 0x" << std::hex << (*foundRange).getStart() << " - 0x" << (*foundRange).getEnd() << endl; + + cout << "Looking for 0x" << std::hex << 316 << ":" << endl; + foundRange = m.FindFreeRange(RangeAddress_t(316)); + assert(m.IsValidRange(foundRange)); + cout << "Found: 0x" << std::hex << (*foundRange).getStart() << " - 0x" << (*foundRange).getEnd() << endl; + + cout << "Looking for 0x" << std::hex << 338 << ":" << endl; + foundRange = m.FindFreeRange(RangeAddress_t(338)); + assert(m.IsValidRange(foundRange)); + cout << "Found: 0x" << std::hex << (*foundRange).getStart() << " - 0x" << (*foundRange).getEnd() << endl; + + cout << "Looking for 0x" << std::hex << 401 << ":" << endl; + foundRange = m.FindFreeRange(RangeAddress_t(401)); + assert(m.IsValidRange(foundRange)); + cout << "Found: 0x" << std::hex << (*foundRange).getStart() << " - 0x" << (*foundRange).getEnd() << endl; + + cout << "Looking for 0x" << std::hex << 450 << ":" << endl; + foundRange = m.FindFreeRange(RangeAddress_t(450)); + assert(m.IsValidRange(foundRange)); + cout << "Found: 0x" << std::hex << (*foundRange).getStart() << " - 0x" << (*foundRange).getEnd() << endl; + + cout << "Looking for 0x" << std::hex << 512 << ":" << endl; + foundRange = m.FindFreeRange(RangeAddress_t(512)); + assert(m.IsValidRange(foundRange)); + cout << "Found: 0x" << std::hex << (*foundRange).getStart() << " - 0x" << (*foundRange).getEnd() << endl; + + cout << "Looking for 0x" << std::hex << 400 << ": (but won't find)" << endl; + foundRange = m.FindFreeRange(RangeAddress_t(400)); + assert(!m.IsValidRange(foundRange)); + + cout << "Looking for 0x" << std::hex << 300 << ": (but won't find)" << endl; + foundRange = m.FindFreeRange(RangeAddress_t(300)); + assert(!m.IsValidRange(foundRange)); + return true; +} +bool TestSort() +{ + ZiprMemorySpace_t m; + m.AddFreeRange(Range_t(256, 512)); + + m.SplitFreeRange(300); + m.SplitFreeRange(315); + m.SplitFreeRange(336); + m.SplitFreeRange(337); + m.SplitFreeRange(400); + + m.MergeFreeRange(300); + m.MergeFreeRange(337); + m.MergeFreeRange(315); + + m.PrintMemorySpace(cout); + m.PrintMemorySpace(cout); + + return true; +} + +bool TestInsertRemoveFreeRange() +{ + ZiprMemorySpace_t m; + m.AddFreeRange(Range_t(256, 512)); + m.AddFreeRange(Range_t(513, 1024)); + m.AddFreeRange(Range_t(1025, 4096)); + m.PrintMemorySpace(cout); + if (m.GetRangeCount() != 3) + return false; + m.RemoveFreeRange(Range_t(513, 1024)); + if (m.GetRangeCount() != 2) + return false; + m.PrintMemorySpace(cout); + m.RemoveFreeRange(Range_t(256, 512)); + m.RemoveFreeRange(Range_t(1025, 4096)); + m.PrintMemorySpace(cout); + return (m.GetRangeCount() == 0); +} + +bool TestMergeFreeRange() +{ + ZiprMemorySpace_t m; + m.AddFreeRange(Range_t(256, 512)); + + m.SplitFreeRange(300); + + m.SplitFreeRange(315); + + m.SplitFreeRange(336); + + m.SplitFreeRange(337); + + m.SplitFreeRange(400); + + cout << "Post Splits at:"; + cout << "0x" << std::hex << 300 << " "; + cout << "0x" << std::hex << 315 << " "; + cout << "0x" << std::hex << 336 << " "; + cout << "0x" << std::hex << 337 << " "; + cout << "0x" << std::hex << 400 << endl; + m.PrintMemorySpace(cout); + + m.MergeFreeRange(300); + + cout << "Post 0x" << std::hex << 300 << " Merge" << endl; + m.PrintMemorySpace(cout); + + m.MergeFreeRange(315); + + cout << "Post 0x" << std::hex << 315 << " Merge" << endl; + m.PrintMemorySpace(cout); + + m.MergeFreeRange(337); + + cout << "Post 0x" << std::hex << 337 << " Merge" << endl; + m.PrintMemorySpace(cout); + + assert(m.GetRangeCount() == 3); + assert(m.IsByteFree(300)); + assert(m.IsByteFree(315)); + assert(m.IsByteFree(317)); + assert(!m.IsByteFree(336)); + assert(!m.IsByteFree(400)); + return true; +} + +bool TestClearAllIteratively() +{ + Range_t removableRange; + ZiprMemorySpace_t m; + + m.AddFreeRange(Range_t(256, 512)); + m.AddFreeRange(Range_t(513, 1024)); + m.AddFreeRange(Range_t(1025, 4096)); + m.PrintMemorySpace(cout); + if (m.GetRangeCount() != 3) + return false; + while (m.GetRangeCount()) + { + removableRange = m.getFreeRange(0); + m.RemoveFreeRange(removableRange); + } + m.PrintMemorySpace(cout); + return m.GetRangeCount() == 0; +} + +bool TestEraseOneByter() +{ + Range_t removableRange; + ZiprMemorySpace_t m; + + m.AddFreeRange(Range_t(512, 512)); + m.AddFreeRange(Range_t(256, 300)); + m.RemoveFreeRange(Range_t(512, 512)); + + return m.GetRangeCount() == 1; +} + +bool TestClearSomeIteratively() +{ + Range_t removableRange; + ZiprMemorySpace_t m; + + m.AddFreeRange(Range_t(512, 512)); + m.AddFreeRange(Range_t(513, 1024)); + m.AddFreeRange(Range_t(1025, 4096)); + m.PrintMemorySpace(cout); + if (m.GetRangeCount() != 3) + return false; + /* + * NB: This is not pretty -- we are assuming that + * the "last" entry is the one that we want to + * keep. Test does good testing, but insert order + * should not be changed. + */ + while (m.GetRangeCount() != 1) + { + removableRange = m.getFreeRange(0); + if (removableRange.getEnd() == 4096) + continue; + m.RemoveFreeRange(removableRange); + } + m.PrintMemorySpace(cout); + return m.GetRangeCount() == 1; +} + +bool TestgetNearbyFreeRanges() +{ + ZiprMemorySpace_t m; + int result_count = 0; + pair<RangeSet_t::const_iterator, RangeSet_t::const_iterator> result; + RangeSet_t::const_iterator result_it, result_it_end; + + m.AddFreeRange(Range_t(512, 512)); + m.AddFreeRange(Range_t(513, 1024)); + m.AddFreeRange(Range_t(1025, 4096)); + + m.PrintMemorySpace(cout); + + result = m.getNearbyFreeRanges(RangeAddress_t(1000)); + for (result_it = result.first, result_it_end = result.second; + result_it != result_it_end; + result_it++, result_count++) {} + return result_count == 2; +} + +bool TestCopyConstructor() +{ + ZiprMemorySpace_t m, n; + + m.AddFreeRange(Range_t(512, 512)); + m.AddFreeRange(Range_t(513, 1024)); + m.AddFreeRange(Range_t(1025, 4096)); + + + n = m; + n.AddFreeRange(Range_t(8192, 16384)); + + cout << "m: " << endl; + m.PrintMemorySpace(cout); + cout << "n: " << endl; + n.PrintMemorySpace(cout); + + return true; +} + +int main(int argc, char *argv[]) +{ + INVOKE(TestSplitMemorySpace); + INVOKE(TestMergeFreeRange); + INVOKE(TestSort); + INVOKE(TestBinarySearch); + INVOKE(TestBinarySearchMaxRange); + INVOKE(TestInsertRemoveFreeRange); + INVOKE(TestClearAllIteratively); + INVOKE(TestClearSomeIteratively); + INVOKE(TestEraseOneByter); + INVOKE(TestgetNearbyFreeRanges); + INVOKE(TestCopyConstructor); +} diff --git a/zipr/test/SConscript b/zipr/test/SConscript new file mode 100644 index 0000000000000000000000000000000000000000..2eb7954f4934df51efea3f21ad34934d751f26f1 --- /dev/null +++ b/zipr/test/SConscript @@ -0,0 +1,76 @@ +import os + +Import('env') +myenv=env +myenv.Replace(SECURITY_TRANSFORMS_HOME=os.environ['SECURITY_TRANSFORMS_HOME']) +myenv.Replace(IRDB_SDK=os.environ['IRDB_SDK']) +myenv.Replace(ZIPR_HOME=os.environ['ZIPR_HOME']) +myenv.Replace(ZIPR_SDK=os.environ['ZIPR_SDK']) +myenv.Append(LINKFLAGS=" -Wl,-unresolved-symbols=ignore-in-shared-libs ") + + +MemorySpaceFiles= ''' + MemorySpace.cpp + ../src/memory_space.cpp + ../src/zipr_options.cpp + ../src/range.cpp + ''' + +RangeFiles= ''' + ZiprRange.cpp + ../src/memory_space.cpp + ../src/zipr_options.cpp + ../src/range.cpp + ''' + +OptionFiles= ''' + ZiprOptions.cpp + ../src/zipr_options.cpp + ../src/range.cpp + ''' + +DollopFiles= ''' + ZiprDollop.cpp + ../src/zipr_dollop_man.cpp + ../src/dollop.cpp + ../src/utils.cpp + ../src/range.cpp + ''' + +# ELFIO needs to be first so we get the zipr version instead of the sectrans version. the zipr version is modified to include get_offset. +cpppath=''' + . + $SECURITY_TRANSFORMS_HOME/third_party/elfio-code/ + $SECURITY_TRANSFORMS_HOME/libEXEIO/include + $IRDB_SDK/include/ + $ZIPR_HOME/include/ + $ZIPR_SDK/include/ + ''' + +libs=''' + irdb-core + irdb-cfg + irdb-transform + dl + EXEIO + ''' + +libpath=''' + $SECURITY_TRANSFORMS_HOME/lib + ''' + +myenv.Append(CCFLAGS=" -Wall ") +myenv.Append(CXXFLAGS=" -pg -std=c++0x -g -O0 ") +myenv.Append(LINKFLAGS=" -pg -Wl,-E ") # export all symbols + + +myenv=myenv.Clone(CPPPATH=Split(cpppath), LIBS=Split(""), LIBPATH=Split(libpath)) + +#print 'myenv=' +#print myenv.Dump() + +Range=myenv.Program("Range.exe", Split(RangeFiles)) +MemorySpace=myenv.Program("MemorySpace.exe", Split(MemorySpaceFiles)) +Dollop=myenv.Program("Dollop.exe", Split(DollopFiles)) +Default([MemorySpace, Range]) + diff --git a/zipr/test/SConstruct b/zipr/test/SConstruct new file mode 100644 index 0000000000000000000000000000000000000000..c0dd68a00d406b0148a93709cf916ad6d05f282c --- /dev/null +++ b/zipr/test/SConstruct @@ -0,0 +1,6 @@ + + + +env=Environment() +Export('env') +SConscript("SConscript") diff --git a/zipr/test/ZiprDollop.cpp b/zipr/test/ZiprDollop.cpp new file mode 100644 index 0000000000000000000000000000000000000000..85db52b4f12176e83fb417629df473c1ba3dc54a --- /dev/null +++ b/zipr/test/ZiprDollop.cpp @@ -0,0 +1,464 @@ +/* + * Copyright (c) 2014 - Zephyr Software LLC + * + * This file may be used and modified for non-commercial purposes as long as + * all copyright, permission, and nonwarranty notices are preserved. + * Redistribution is prohibited without prior written consent from Zephyr + * Software. + * + * Please contact the authors for restrictions applying to commercial use. + * + * THIS SOURCE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF + * MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * Author: Zephyr Software + * e-mail: jwd@zephyr-software.com + * URL : http://www.zephyr-software.com/ + * + */ + +#include <zipr_all.h> +#include <zipr_sdk.h> + +using namespace zipr; +using namespace std; +using namespace Zipr_SDK; + +#define INVOKE(a) \ +bool __ ## a ## _result = false; \ +printf("Invoking " #a ":\n"); \ +__ ## a ## _result = a(); \ +printf(#a ":"); \ +if (__ ## a ## _result) \ +{ \ +printf(" pass\n"); \ +} \ +else \ +{ \ +printf(" fail\n"); \ +} + +/* + * Test whether creating dollop from an instruction + * with no fallthrough properly excludes the remaining + * instructions. + */ +bool TestgetContainingDollopNoFallthrough() { + ZiprDollopManager_t dollop_man; + IRDB_SDK::Instruction_t *insn_a = new IRDB_SDK::Instruction_t(); + IRDB_SDK::Instruction_t *insn_b = new IRDB_SDK::Instruction_t(); + Dollop_t *dollop_a = nullptr; + + dollop_a = Dollop_t::CreateNewDollop(insn_a); + dollop_man.AddDollops(dollop_a); + + return dollop_man.getContainingDollop(insn_b) == nullptr && + dollop_man.getContainingDollop(insn_a) == dollop_a; +} + +/* + * Test whether creating a dollop from an instruction + * with a fallthrough properly contains the linked + * instructions. + */ +bool TestgetContainingDollopFallthrough(void) { + ZiprDollopManager_t dollop_man; + IRDB_SDK::Instruction_t *insn_a = new IRDB_SDK::Instruction_t(); + IRDB_SDK::Instruction_t *insn_b = new IRDB_SDK::Instruction_t(); + Dollop_t *dollop_a = nullptr; + + insn_a->setFallthrough(insn_b); + + dollop_a = Dollop_t::CreateNewDollop(insn_a); + dollop_man.AddDollops(dollop_a); + + return dollop_man.getContainingDollop(insn_b) == dollop_a && + dollop_man.getContainingDollop(insn_a) == dollop_a; +} + +/* + * Test whether getContainingDollop works + * properly when there is more than one + * dollop in the manager. + */ +bool TestgetContainingDollop(void) { + ZiprDollopManager_t dollop_man; + IRDB_SDK::Instruction_t *insn_a = new IRDB_SDK::Instruction_t(); + IRDB_SDK::Instruction_t *insn_b = new IRDB_SDK::Instruction_t(); + Dollop_t *dollop_a = Dollop_t::CreateNewDollop(insn_a); + Dollop_t *dollop_b = Dollop_t::CreateNewDollop(insn_b); + dollop_man.AddDollops(dollop_a); + dollop_man.AddDollops(dollop_b); + return dollop_man.getContainingDollop(insn_a) == dollop_a && + dollop_man.getContainingDollop(insn_b) == dollop_b; +} + +/* + * Sanity check whether adding a dollop to the + * dollop manager actually works. + */ +bool TestAddDollopEntry(void) { + ZiprDollopManager_t dollop_man; + IRDB_SDK::Instruction_t *insn = new IRDB_SDK::Instruction_t(); + dollop_man.AddDollops(Dollop_t::CreateNewDollop(insn)); + return 1 == dollop_man.Size(); +} + +bool TestDollopPatch(void) { + Dollop_t *a = nullptr; + DollopPatch_t patch; + + IRDB_SDK::Instruction_t *insn_a = new IRDB_SDK::Instruction_t(); + a = Dollop_t::CreateNewDollop(insn_a); + + patch.Target(a); + + return patch.Target() == a; +} + +bool TestDollopPatchDollopManager(void) { + ZiprDollopManager_t dollop_man; + DollopPatch_t patch_a, patch_b; + Dollop_t *dollop_a, *dollop_b; + + IRDB_SDK::Instruction_t *insn_a = new IRDB_SDK::Instruction_t(); + IRDB_SDK::Instruction_t *insn_b = new IRDB_SDK::Instruction_t(); + + dollop_a = Dollop_t::CreateNewDollop(insn_a); + dollop_b = Dollop_t::CreateNewDollop(insn_b); + + patch_a.Target(dollop_b); + patch_b.Target(dollop_a); + + dollop_man.AddDollopPatch(&patch_a); + dollop_man.AddDollopPatch(&patch_b); + + dollop_man.PrintDollopPatches(cout); + return true; +} + +/* + * Test whether adding a new dollop that starts + * with an instruction already in a dollop splits + * the existing dollop. + */ +bool TestAddNewDollopSplitsExistingDollop(void) { + bool success = true; + ZiprDollopManager_t dollop_man; + Dollop_t *a, *b; + + IRDB_SDK::Instruction_t *insn_a = new IRDB_SDK::Instruction_t(); + IRDB_SDK::Instruction_t *insn_b = new IRDB_SDK::Instruction_t(); + IRDB_SDK::Instruction_t *insn_c = new IRDB_SDK::Instruction_t(); + IRDB_SDK::Instruction_t *insn_d = new IRDB_SDK::Instruction_t(); + + /* + * a targets c + * c targets d (which will ultimately create a new dollop) + * a->b->c->d + * + * A: a, b, c, d + */ + + insn_a->setFallthrough(insn_b); + insn_b->setFallthrough(insn_c); + insn_c->setFallthrough(insn_d); + + a = Dollop_t::CreateNewDollop(insn_a); + dollop_man.AddDollops(a); + success = (a->getDollopEntryCount() == 4) && dollop_man.Size() == 1; + + + cout << "Before AddNewDollops()." << endl; + cout << dollop_man << endl; + + b = dollop_man.AddNewDollops(insn_c); + + cout << "After AddNewDollops()." << endl; + cout << dollop_man << endl; + return success && + a->getDollopEntryCount() == 2 && + b->getDollopEntryCount() == 2 && + dollop_man.Size() == 2 && + a->FallthroughDollop() == b && + b->FallbackDollop() == a; +} + +bool TestUpdateTargetsDollopManager(void) { + bool success = true; + ZiprDollopManager_t dollop_man; + Dollop_t *a, *c; + Dollop_t *original_insn_d_container = nullptr; + Dollop_t *updated_insn_d_container = nullptr; + + IRDB_SDK::Instruction_t *insn_a = new IRDB_SDK::Instruction_t(); + IRDB_SDK::Instruction_t *insn_b = new IRDB_SDK::Instruction_t(); + IRDB_SDK::Instruction_t *insn_c = new IRDB_SDK::Instruction_t(); + IRDB_SDK::Instruction_t *insn_d = new IRDB_SDK::Instruction_t(); + + /* + * a targets c + * c targets d (which will ultimately create a new dollop) + * a->b + * c->d + * + * A: a, b + * C: c, d + */ + + insn_a->setFallthrough(insn_b); + insn_c->setFallthrough(insn_d); + + insn_a->setTarget(insn_c); + insn_b->setTarget(insn_d); + + a = Dollop_t::CreateNewDollop(insn_a); + c = Dollop_t::CreateNewDollop(insn_c); + + cout << "&a: " << std::hex << a << endl; + cout << "&c: " << std::hex << c << endl; + + dollop_man.AddDollops(a); + dollop_man.AddDollops(c); + + original_insn_d_container = dollop_man.getContainingDollop(insn_d); + + success &= (original_insn_d_container == c); + + cout << "Before UpdateTargets([ac])" << endl; + + cout << dollop_man << endl; +/* + cout << "A: " << endl; + cout << (*a) << endl; + cout << "C: " << endl; + cout << (*c) << endl; +*/ + + dollop_man.UpdateTargets(a); + /* UpdateTargets(c) will notice that d is a target that is + * not at the head of a dollop. It will subsequently create + * a new dollop from that instruction by splitting + * the existing dollop. + */ + dollop_man.UpdateTargets(c); + + cout << "After UpdateTargets([ac])" << endl; + + cout << dollop_man << endl; +/* + cout << "A: " << endl; + cout << (*a) << endl; + cout << "C: " << endl; + cout << (*c) << endl; +*/ + updated_insn_d_container = dollop_man.getContainingDollop(insn_d); + return c->getDollopEntryCount() == 1 && + a->getDollopEntryCount() == 2 && + dollop_man.Size() == 3 && + updated_insn_d_container != original_insn_d_container && + success; +} + +bool TestDollopPatchMapDollopManager(void) { + bool success = true; + ZiprDollopManager_t dollop_man; + DollopPatch_t patch_a, patch_aa, patch_c; + Dollop_t *dollop_a, *dollop_c; + std::list<DollopPatch_t *>::const_iterator patches_it, patches_it_end; + + IRDB_SDK::Instruction_t *insn_a = new IRDB_SDK::Instruction_t(); + IRDB_SDK::Instruction_t *insn_c = new IRDB_SDK::Instruction_t(); + + dollop_a = Dollop_t::CreateNewDollop(insn_a); + dollop_c = Dollop_t::CreateNewDollop(insn_c); + + patch_a.Target(dollop_a); + patch_aa.Target(dollop_a); + patch_c.Target(dollop_c); + + dollop_man.AddDollops(dollop_a); + dollop_man.AddDollops(dollop_c); + + dollop_man.AddDollopPatch(&patch_a); + dollop_man.AddDollopPatch(&patch_aa); + dollop_man.AddDollopPatch(&patch_c); + + cout << "&dollop_a: " << std::hex << dollop_a << endl; + cout << "&dollop_c: " << std::hex << dollop_c << endl; + + std::list<DollopPatch_t *> patches = dollop_man.PatchesToDollop(dollop_a); + for (patches_it = patches.begin(), patches_it_end = patches.end(); + patches_it != patches.end(); + patches_it++) { + success &= ((*patches_it)->Target()) == dollop_a; + cout << *(*patches_it) << endl; + } + patches = dollop_man.PatchesToDollop(dollop_c); + for (patches_it = patches.begin(), patches_it_end = patches.end(); + patches_it != patches.end(); + patches_it++) { + success &= ((*patches_it)->Target()) == dollop_c; + cout << *(*patches_it) << endl; + } + return success; +} + +bool TestDollopFallthroughDollopEntry(void) { + Dollop_t *a; + DollopEntry_t *aa, *bb; + + a = new Dollop_t(); + + IRDB_SDK::Instruction_t *insn_a = new IRDB_SDK::Instruction_t(); + IRDB_SDK::Instruction_t *insn_b = new IRDB_SDK::Instruction_t(); + + aa = new DollopEntry_t(insn_a, a); + bb = new DollopEntry_t(insn_b, a); + + a->push_back(aa); + a->push_back(bb); + + return bb == a->FallthroughDollopEntry(aa) && + nullptr == a->FallthroughDollopEntry(bb) && + nullptr == a->FallthroughDollopEntry(nullptr); +} + +bool TestDollopSplit(void) { + Dollop_t *a, *b; + + IRDB_SDK::Instruction_t *insn_a = new IRDB_SDK::Instruction_t(); + IRDB_SDK::Instruction_t *insn_b = new IRDB_SDK::Instruction_t(); + IRDB_SDK::Instruction_t *insn_c = new IRDB_SDK::Instruction_t(); + IRDB_SDK::Instruction_t *insn_d = new IRDB_SDK::Instruction_t(); + IRDB_SDK::Instruction_t *insn_e = new IRDB_SDK::Instruction_t(); + + insn_a->setFallthrough(insn_b); + insn_b->setFallthrough(insn_c); + insn_c->setFallthrough(insn_d); + insn_d->setFallthrough(insn_e); + + a = Dollop_t::CreateNewDollop(insn_a); + + cout << "Dollop A: " << endl; + cout << *a << endl; + + b = a->Split(insn_b); + + cout << "Dollop A: " << endl; + cout << *a << endl; + cout << "Dollop B: " << endl; + cout << *b << endl; + + return a->getDollopEntryCount() == 1 && b->getDollopEntryCount() == 4 && + a->FallthroughDollop() == b && b->FallbackDollop() == a; +} + +bool TestDollopEntryEquals(void) { + DollopEntry_t *a, *b, *c, *d; + + IRDB_SDK::Instruction_t *insn_a = new IRDB_SDK::Instruction_t(); + IRDB_SDK::Instruction_t *insn_b = new IRDB_SDK::Instruction_t(); + + a = new DollopEntry_t(insn_a, nullptr); + b = new DollopEntry_t(insn_b, nullptr); + c = new DollopEntry_t(insn_a, nullptr); + d = new DollopEntry_t(insn_a, nullptr); + d->TargetDollop((Dollop_t*)0x5000); + + return *a != *b && + *b != *c && + *a == *a && + *b == *b && + *a == *c && + *a != *d; +} + +/* + * Test whether or not + * 1. dollops created from overlapping + * instructions are correctly split + * 2. whether or not the dollop manager realizes + * that dollops might be getting readded + * 3. whether or not dollop entries are reassigned + * to the proper containing dollops. + * + * Instruction layout: + * e\ + * b->c->d + * a/ + * + * Ultimate (correct) dollop layout: + * B_fallthrough: bf + * A: a, + * B: b, + * E: e, + * Anon: c->d + * A->Anon + * B->Anon + * C->Anon + * Anon->B_fallthrough + */ +bool TestCreateDollopsFromOverlappingInstructions(void) { + bool success = true; + ZiprDollopManager_t dollop_man; + Dollop_t *a, *b, *e, *b_fallthrough; + + IRDB_SDK::Instruction_t *insn_a = new IRDB_SDK::Instruction_t(); + IRDB_SDK::Instruction_t *insn_b = new IRDB_SDK::Instruction_t(); + IRDB_SDK::Instruction_t *insn_bf= new IRDB_SDK::Instruction_t(); + IRDB_SDK::Instruction_t *insn_c = new IRDB_SDK::Instruction_t(); + IRDB_SDK::Instruction_t *insn_d = new IRDB_SDK::Instruction_t(); + IRDB_SDK::Instruction_t *insn_e = new IRDB_SDK::Instruction_t(); + + insn_a->setFallthrough(insn_c); + insn_b->setFallthrough(insn_c); + insn_e->setFallthrough(insn_c); + insn_c->setFallthrough(insn_d); + + b_fallthrough = Dollop_t::CreateNewDollop(insn_bf); + b = Dollop_t::CreateNewDollop(insn_b); + b->FallthroughDollop(b_fallthrough); + dollop_man.AddDollops(b); + cout << "b (before transforming): " << endl << *b << endl; + success = (dollop_man.Size() == 2 && b->FallthroughDollop() == b_fallthrough); + + a = dollop_man.AddNewDollops(insn_a); + e = dollop_man.AddNewDollops(insn_e); + + cout << "a->FallthroughDollop(): " + << std::hex << a->FallthroughDollop() << endl; + cout << "b->FallthroughDollop(): " + << std::hex << a->FallthroughDollop() << endl; + cout << "e->FallthroughDollop(): " + << std::hex << e->FallthroughDollop() << endl; + cout << "Common tail: " << endl << *(b->FallthroughDollop()) << endl; + cout << "# of Dollops: " << dollop_man.Size() << endl; + + return success && + dollop_man.Size() == 5 && + dollop_man.getContainingDollop(insn_b) == b && + dollop_man.getContainingDollop(insn_a) == a && + a->FallthroughDollop() == b->FallthroughDollop() && + dollop_man.getContainingDollop(insn_c)->FallthroughDollop() == + b_fallthrough; +} + +int main(int argc, char *argv[]) +{ + INVOKE(TestAddDollopEntry); + INVOKE(TestDollopEntryEquals); + INVOKE(TestDollopSplit); + INVOKE(TestgetContainingDollop); + INVOKE(TestgetContainingDollopFallthrough); + INVOKE(TestgetContainingDollopNoFallthrough); + INVOKE(TestDollopPatch); + INVOKE(TestDollopPatchDollopManager); + INVOKE(TestDollopPatchMapDollopManager); + INVOKE(TestUpdateTargetsDollopManager); + INVOKE(TestAddNewDollopSplitsExistingDollop); + INVOKE(TestDollopFallthroughDollopEntry); + INVOKE(TestCreateDollopsFromOverlappingInstructions); + return 0; +} diff --git a/zipr/test/ZiprOptions.cpp b/zipr/test/ZiprOptions.cpp new file mode 100644 index 0000000000000000000000000000000000000000..bab8bc9930c1b40065c1622e20bac35ecc5fe6d8 --- /dev/null +++ b/zipr/test/ZiprOptions.cpp @@ -0,0 +1,140 @@ +/* + * Copyright (c) 2014 - Zephyr Software LLC + * + * This file may be used and modified for non-commercial purposes as long as + * all copyright, permission, and nonwarranty notices are preserved. + * Redistribution is prohibited without prior written consent from Zephyr + * Software. + * + * Please contact the authors for restrictions applying to commercial use. + * + * THIS SOURCE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF + * MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * Author: Zephyr Software + * e-mail: jwd@zephyr-software.com + * URL : http://www.zephyr-software.com/ + * + */ + +#include <zipr_all.h> +#include <zipr-sdk> + +using namespace zipr; +using namespace std; + +#define INVOKE(a) \ +bool __ ## a ## _result = false; \ +__ ## a ## _result = a(); \ +printf(#a ":"); \ +if (__ ## a ## _result) \ +{ \ +printf(" pass\n"); \ +} \ +else \ +{ \ +printf(" fail\n"); \ +} + +int main(int argc, char *argv[]) +{ + ZiprOptions_t options(argc-1, argv+1); + + ZiprOptionsNamespace_t global_ns("global"); + ZiprOptionsNamespace_t local_ns("local"); + + cout << "Constructing ZiprStringOption_t('a')." << endl; + ZiprStringOption_t global_a_option("a"); + cout << "Constructing ZiprStringOption_t('a')." << endl; + ZiprStringOption_t global_shadow_a_option("a"); + cout << "Constructing ZiprBooleanOption_t('b', true)." << endl; + ZiprBooleanOption_t local_b_option("b", true); + cout << "Constructing ZiprBooleanOption_t('b')." << endl; + ZiprBooleanOption_t local_shadow_b_option("b"); + cout << "Constructing ZiprBooleanOption_t('c', false)." << endl; + ZiprBooleanOption_t local_c_option("c", false); + cout << "Constructing ZiprStringOption_t('c', false)." << endl; + ZiprStringOption_t local_shadow_c_option("c"); + cout << "Constructing ZiprIntegerOption_t('d', '55')." << endl; + ZiprIntegerOption_t local_d_option("d", "55"); + cout << "Constructing ZiprIntegerOption_t('e')." << endl; + ZiprIntegerOption_t local_e_option("e"); + cout << "Constructing ZiprIntegerOption_t('e')." << endl; + ZiprIntegerOption_t local_shadow_e_option("e"); + cout << "Constructing ZiprDoubleOption_t('f')." << endl; + ZiprDoubleOption_t local_f_option("f"); + cout << "Constructing ZiprDoubleOption_t('g', 2.4)." << endl; + ZiprDoubleOption_t local_g_option("g", 2.4); + + local_b_option.setRequired(true); + local_b_option.setDescription("Set the B option."); + local_d_option.setRequired(true); + + global_ns.addOption(&global_a_option); + global_ns.addOption(&global_shadow_a_option); + local_ns.addOption(&local_b_option); + local_ns.addOption(&local_shadow_b_option); + local_ns.addOption(&local_c_option); + //local_ns.AddOption(&local_shadow_c_option); + local_ns.addOption(&local_d_option); + local_ns.addOption(&local_e_option); + local_ns.addOption(&local_shadow_e_option); + local_ns.addOption(&local_f_option); + local_ns.addOption(&local_g_option); + + options.addNamespace(&global_ns); + options.addNamespace(&local_ns); + + options.parse(&cout); + + if (!options.areRequirementsMet()) + { + cout << "Usage: " << endl; + options.printUsage(cout); + } + + options.printNamespaces(); + cout << "global_shadow_a_option: " << global_shadow_a_option.getValue() << endl; + cout << "local_shadow_b_option: " << local_shadow_b_option.getStringValue() << endl; + if (global_a_option == "avl") { + cout << "global_a_option is avl (" << global_a_option.substr(0,1) << ")." << endl; + } else { + cout << "global_a_option is NOT avl." << endl; + } + + if (local_b_option) { + cout << "local_b_option is true." << endl; + } else { + cout << "local_b_option is false." << endl; + } + if (local_shadow_b_option) { + cout << "local_shadow_b_option is true." << endl; + } else { + cout << "local_shadow_b_option is false." << endl; + } + if (!local_c_option) { + cout << "local_c_option is false." << endl; + } else { + cout << "local_c_option is true." << endl; + } + if (local_d_option == 55) { + cout << "local_d_option is " << local_d_option.getValue() << endl; + } + cout << "local_e_option: " << ((int)local_e_option) << endl; + if (local_shadow_e_option == local_e_option) { + cout << "local_shadow_e_option == local_e_option" << endl; + } + if (local_g_option == 2.5) { + cout << "local_g_option is 2.5!" << endl; + } + if (local_g_option != 2.5) { + cout << "local_g_option is NOT 2.5!" << endl; + } + if (local_g_option) { + cout << "local_g_option is!" << endl; + } + if (!local_g_option) { + cout << "local_g_option is NOT!" << endl; + } +} diff --git a/zipr/test/ZiprRange.cpp b/zipr/test/ZiprRange.cpp new file mode 100644 index 0000000000000000000000000000000000000000..bbef4a4a660cb9991a5117f0aa1dde541af6aecf --- /dev/null +++ b/zipr/test/ZiprRange.cpp @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2014 - Zephyr Software LLC + * + * This file may be used and modified for non-commercial purposes as long as + * all copyright, permission, and nonwarranty notices are preserved. + * Redistribution is prohibited without prior written consent from Zephyr + * Software. + * + * Please contact the authors for restrictions applying to commercial use. + * + * THIS SOURCE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF + * MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * Author: Zephyr Software + * e-mail: jwd@zephyr-software.com + * URL : http://www.zephyr-software.com/ + * + */ + +#include <zipr_all.h> + +using namespace zipr; +using namespace std; +using namespace Zipr_SDK; + +#define INVOKE(a) \ +bool __ ## a ## _result = false; \ +printf("Invoking " #a ":\n"); \ +__ ## a ## _result = a(); \ +printf(#a ":"); \ +if (__ ## a ## _result) \ +{ \ +printf(" pass\n"); \ +} \ +else \ +{ \ +printf(" fail\n"); \ +} + +class DollopMockup { + private: + int m_size; + public: + DollopMockup() : m_size(0) {}; + void Size(int size) { m_size = size; } + int Size() const { return m_size; } +}; + +bool TestRangeSpeed() { + ZiprMemorySpace_t m; + DollopMockup *d = new DollopMockup(); + + d->Size(50); + + for (int i = 0; + i<25; + i++) { + m.addFreeRange(Range_t(100*i, (100*(i+1))-1)); + } + + //m.PrintMemorySpace(cout); + const auto timeStart=clock(); + auto i=0; + for (i = 0; (clock()-timeStart)/CLOCKS_PER_SEC < 10; i++) + { + volatile RangeAddress_t found_start = 0; + auto placement = m.getFreeRange(d->Size()); + + found_start = placement.getStart(); + found_start++; + } + cout<<"In 10 seconds, executed "<<dec<<i<<" iterations of getFreeRange()"<<endl; + + return true; +} + +int main(int argc, char *argv[]) +{ + INVOKE(TestRangeSpeed); + return 0; +} diff --git a/zipr/test/dylib/Makefile b/zipr/test/dylib/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..885ffe7916c3821af2263400ace838b91edd36c4 --- /dev/null +++ b/zipr/test/dylib/Makefile @@ -0,0 +1,44 @@ +all: dylib dylib_test math_test readline_test + +dylib: dylib.c dylib.h Makefile + gcc -shared -fpic dylib.c -olibdylib.so + +dylib_test: dylib dylib_test.c Makefile + gcc -L. -o dylib_test dylib_test.c -ldylib +math_test: math_test.c Makefile + gcc -o math_test math_test.c -lm +readline_test: readline_test.c Makefile + gcc -o readline_test readline_test.c -lreadline + +libs/libreadline.so.6: + mkdir -p libs/ + $$PEASOUP_HOME/tools/ps_analyze.sh /lib/x86_64-linux-gnu/libreadline.so.6 libs/libreadline.so.6 --backend zipr +libs/libdylib.so: + mkdir -p libs/ + $$PEASOUP_HOME/tools/ps_analyze.sh libdylib.so libs/libdylib.so --backend zipr +libs/libm.so.6: + mkdir -p libs/ + $$PEASOUP_HOME/tools/ps_analyze.sh /lib/x86_64-linux-gnu/libm.so.6 libs/libm.so.6 --backend zipr + +test: dylib_test libs/libdylib.so math_test libs/libm.so.6 readline_test libs/libreadline.so.6 + LD_LIBRARY_PATH=libs/ ldd ./dylib_test + LD_LIBRARY_PATH=libs/ ./dylib_test > dylib_test.protected.out + ldd ./dylib_test + ./dylib_test > dylib_test.out + diff dylib_test.protected.out dylib_test.out + rm -rf dylib_test*.out + LD_LIBRARY_PATH=libs/ ldd ./math_test + LD_LIBRARY_PATH=libs/ ./math_test > math_test.protected.out + ldd ./math_test + ./math_test > math_test.out + diff math_test.protected.out math_test.out + rm -rf math_test*.out + LD_LIBRARY_PATH=libs/ ldd ./readline_test + echo "asdf" | LD_LIBRARY_PATH=libs/ ./readline_test > readline_test.protected.out + ldd ./readline_test + echo "asdf" | ./readline_test > readline_test.out + diff readline_test.protected.out readline_test.out + rm -rf readline_test*.out +clean: + rm -rf libs dylib dylib_test math_test libdylib.so readline_test + rm -rf peasoup_executable_directory* diff --git a/zipr/test/dylib/dylib.c b/zipr/test/dylib/dylib.c new file mode 100644 index 0000000000000000000000000000000000000000..56805b31f930fc8f861821ab1959b6193cebfe7d --- /dev/null +++ b/zipr/test/dylib/dylib.c @@ -0,0 +1,32 @@ +#include <stdio.h> +#include <stdarg.h> + +int b = 1; +int c = 2; + +int dynamic_function_a(void) { + printf("This is dynamic function a.\n"); + return 0; +} + +int dynamic_function_b(char *b, int n, ...) { + int i = 0; + va_list ap; + printf("This is dynamic function %s.\n", b); + va_start(ap, n); + for (i; i<n; i++) + { + char *t = nullptr; + t = va_arg(ap, char *); + printf("%d: %s\n", i+1, t); + } + va_end(ap); + return n-i; +} + +int dynamic_function_c(int a) { + int d = 0; + printf("This is dynamic function c.\n"); + d = a - b - c; + return d; +} diff --git a/zipr/test/dylib/dylib.h b/zipr/test/dylib/dylib.h new file mode 100644 index 0000000000000000000000000000000000000000..329d167d0bf597f4e394e2b4f9475b62d20c6056 --- /dev/null +++ b/zipr/test/dylib/dylib.h @@ -0,0 +1,6 @@ +#ifndef _DYLIB_H +#define _DYLIB_H +int dynamic_function_a(void); +int dynamic_function_b(char *b, int n, ...); +int dynamic_function_c(int a); +#endif // _DYLIB_H diff --git a/zipr/test/dylib/dylib_test.c b/zipr/test/dylib/dylib_test.c new file mode 100644 index 0000000000000000000000000000000000000000..761bdd0749828ecd12b563f8d47f6a1e05637117 --- /dev/null +++ b/zipr/test/dylib/dylib_test.c @@ -0,0 +1,10 @@ +#include <stdio.h> +#include "dylib.h" + +int main() { + int ret = 0; + ret += dynamic_function_a(); + ret += dynamic_function_b("b", 2, "a", "b"); + ret += dynamic_function_c(3); + return ret; +} diff --git a/zipr/test/dylib/math_test.c b/zipr/test/dylib/math_test.c new file mode 100644 index 0000000000000000000000000000000000000000..3fd86a0583cede1959bd0efa4e1d65f0d903a69d --- /dev/null +++ b/zipr/test/dylib/math_test.c @@ -0,0 +1,122 @@ +#include <stdio.h> +#define _GNU_SOURCE +#include <math.h> + +float f1 = 0.5f; +float f2 = 1.0f; +float f3 = 1.5f; + +#define FLOAT f1 +#define FLOAT2 f2 +#define FLOAT3 f3 + +int i1 = 1; +int pi1 = 1; +long int li1 = 1; + +#define INT i1 +#define PINT &pi1 +#define LINT li1 + +double d1 = 0.5; +double pd1 = 2.0f; +long double ld1 = 2.0f; + +#define DOUBLE d1 +#define PDOUBLE &pd1 +#define LDOUBLE ld1 + +char *a = "a"; +#define PCHAR a + +#define ARG1(N, ...) N +#define ARG2(_1, N, ...) N +#define ARG3(_1, _2, N, ...) N +#define ARG4(_1, _2, _3, N, ...) N +#define ARGC(...) ARGC_(__VA_ARGS__, 5, 4, 3, 2, 1) +#define ARGC_(_1, _2, _3, _4, _5, N, ...) N + +#define CONCAT(X, Y) _CONCAT(X,Y) +#define _CONCAT(X, Y) X ## Y +#define CALL(Y, ...) _CALL(Y, __VA_ARGS__) +#define _CALL(Y, ...) Y(__VA_ARGS__) +#define STR(X) XSTR(X) +#define XSTR(X) #X +#define TEST(...) \ + CALL(CONCAT(TEST, ARGC(__VA_ARGS__)), __VA_ARGS__) +#define TEST3(...) \ + STR(ARG1(__VA_ARGS__)), ARG1(__VA_ARGS__)(ARG2(__VA_ARGS__)) +#define TEST4(...) \ + STR(ARG1(__VA_ARGS__)), ARG1(__VA_ARGS__)(ARG2(__VA_ARGS__), ARG3(__VA_ARGS__)) +#define TEST5(...) \ + STR(ARG1(__VA_ARGS__)), ARG1(__VA_ARGS__)(ARG2(__VA_ARGS__), ARG3(__VA_ARGS__), ARG4(__VA_ARGS__)) + +int main() { + printf("%s: %f\n", TEST(acos,FLOAT,EXPECTED)); + printf("%s: %f\n", TEST(atan2,FLOAT,FLOAT2,EXPECTED)); + printf("%s: %f\n", TEST(asin,FLOAT,EXPECTED)); + printf("%s: %f\n", TEST(atan,FLOAT,EXPECTED)); + printf("%s: %f\n", TEST(atan2,FLOAT,FLOAT2,EXPECTED)); + printf("%s: %f\n", TEST(cos,FLOAT,EXPECTED)); + printf("%s: %f\n", TEST(sin,FLOAT,EXPECTED)); + printf("%s: %f\n", TEST(tan,FLOAT,EXPECTED)); + printf("%s: %f\n", TEST(cosh,FLOAT,EXPECTED)); + printf("%s: %f\n", TEST(sinh,FLOAT,EXPECTED)); + printf("%s: %f\n", TEST(tanh,FLOAT,EXPECTED)); + printf("%s: %f\n", TEST(acosh,FLOAT,EXPECTED)); + printf("%s: %f\n", TEST(asinh,FLOAT,EXPECTED)); + printf("%s: %f\n", TEST(atanh,FLOAT,EXPECTED)); + printf("%s: %f\n", TEST(exp,FLOAT,EXPECTED)); + printf("%s: %f\n", TEST(frexp,FLOAT,PINT,EXPECTED)); + printf("%s: %f\n", TEST(ldexp,FLOAT,INT,EXPECTED)); + printf("%s: %f\n", TEST(log,FLOAT,EXPECTED)); + printf("%s: %f\n", TEST(log10,FLOAT,EXPECTED)); + printf("%s: %f\n", TEST(modf,FLOAT,PDOUBLE,EXPECTED)); + //printf("%s: %f\n", TEST(exp10,DOUBLE,EXPECTED)); + //printf("%s: %f\n", TEST(pow10,DOUBLE,EXPECTED)); + printf("%s: %f\n", TEST(expm1,FLOAT,EXPECTED)); + printf("%s: %f\n", TEST(log1p,FLOAT,EXPECTED)); + printf("%s: %f\n", TEST(logb,FLOAT,EXPECTED)); + printf("%s: %f\n", TEST(exp2,FLOAT,EXPECTED)); + printf("%s: %f\n", TEST(log2,FLOAT,EXPECTED)); + printf("%s: %f\n", TEST(pow,FLOAT,FLOAT2,EXPECTED)); + printf("%s: %f\n", TEST(sqrt,FLOAT,EXPECTED)); + printf("%s: %f\n", TEST(hypot,FLOAT,FLOAT2,EXPECTED)); + printf("%s: %f\n", TEST(cbrt,FLOAT,EXPECTED)); + printf("%s: %f\n", TEST(ceil,FLOAT,EXPECTED)); + printf("%s: %f\n", TEST(fabs,FLOAT,EXPECTED)); + printf("%s: %f\n", TEST(floor,FLOAT,EXPECTED)); + printf("%s: %f\n", TEST(fmod,FLOAT,FLOAT2,EXPECTED)); + printf("%s: %f\n", TEST(drem,FLOAT,FLOAT2,EXPECTED)); + printf("%s: %f\n", TEST(significand,FLOAT,EXPECTED)); + printf("%s: %f\n", TEST(copysign,FLOAT,FLOAT2,EXPECTED)); + printf("%s: %f\n", TEST(nan,PCHAR,EXPECTED)); + printf("%s: %f\n", TEST(j0,FLOAT,EXPECTED)); + printf("%s: %f\n", TEST(j1,FLOAT,EXPECTED)); + printf("%s: %f\n", TEST(jn,INT,FLOAT,EXPECTED)); + printf("%s: %f\n", TEST(y0,FLOAT,EXPECTED)); + printf("%s: %f\n", TEST(y1,FLOAT,EXPECTED)); + printf("%s: %f\n", TEST(yn,INT,FLOAT,EXPECTED)); + printf("%s: %f\n", TEST(erf,FLOAT,EXPECTED)); + printf("%s: %f\n", TEST(erfc,FLOAT,EXPECTED)); + printf("%s: %f\n", TEST(lgamma,FLOAT,EXPECTED)); + printf("%s: %f\n", TEST(tgamma,FLOAT,EXPECTED)); + printf("%s: %f\n", TEST(gamma,FLOAT,EXPECTED)); + printf("%s: %f\n", TEST(lgamma_r,FLOAT,PINT,EXPECTED)); + printf("%s: %f\n", TEST(rint,FLOAT,EXPECTED)); + printf("%s: %f\n", TEST(nextafter,FLOAT,FLOAT2,EXPECTED)); + printf("%s: %f\n", TEST(nexttoward,FLOAT,LDOUBLE, EXPECTED)); + printf("%s: %f\n", TEST(remainder,FLOAT,FLOAT2,EXPECTED)); + printf("%s: %f\n", TEST(scalbn,FLOAT,INT,EXPECTED)); + printf("%s: %f\n", TEST(scalbln,FLOAT,LINT,EXPECTED)); + printf("%s: %f\n", TEST(nearbyint,FLOAT,EXPECTED)); + printf("%s: %f\n", TEST(round,FLOAT,EXPECTED)); + printf("%s: %f\n", TEST(trunc,FLOAT,EXPECTED)); + printf("%s: %f\n", TEST(remquo,FLOAT,FLOAT2,PINT,EXPECTED)); + printf("%s: %f\n", TEST(fdim,FLOAT,FLOAT2,EXPECTED)); + printf("%s: %f\n", TEST(fmax,FLOAT,FLOAT2,EXPECTED)); + printf("%s: %f\n", TEST(fmin,FLOAT,FLOAT2,EXPECTED)); + printf("%s: %f\n", TEST(fma,FLOAT,FLOAT2,FLOAT3,EXPECTED)); + printf("%s: %f\n", TEST(scalb,FLOAT,FLOAT2,EXPECTED)); + return 0; +} diff --git a/zipr/test/dylib/readline_test.c b/zipr/test/dylib/readline_test.c new file mode 100644 index 0000000000000000000000000000000000000000..c82e7f441e8f23f0ef83b50a9ecfa363d024dbc9 --- /dev/null +++ b/zipr/test/dylib/readline_test.c @@ -0,0 +1,11 @@ +#include <stdio.h> +#include <readline/readline.h> +#include <readline/history.h> + +int main() { + printf("Type your answer: "); + char *answer = readline(nullptr); + printf("%s is wrong.\n", answer); + return 0; +} +