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;
+}
+