diff --git a/include/elfwrite.h b/include/elfwrite.h
index 2bebecbd29d02e105447147e5cc8bfb0bd9629bd..acc03e7a8520d5475dd7e4200c0fdd287b274f5e 100644
--- a/include/elfwrite.h
+++ b/include/elfwrite.h
@@ -43,14 +43,14 @@ class ElfWriter : public ExeWriter
 
 
 	public: 
-		ElfWriter(IRDB_SDK::FileIR_t* firp, bool write_sections, bool bss_opts) 
+		ElfWriter(EXEIO::exeio* p_exeiop, IRDB_SDK::FileIR_t* firp, bool write_sections, bool bss_opts) 
 			:
-				ExeWriter(firp,write_sections,bss_opts)
+				ExeWriter(p_exeiop, firp,write_sections,bss_opts)
 			{
 			}
 
 		virtual ~ElfWriter() {}
-		void Write(const EXEIO::exeio *exeiop, const std::string &out_file, const std::string &infile);
+		void Write(const std::string &out_file, const std::string &infile);
 
 
 	protected:
@@ -69,7 +69,7 @@ class ElfWriterImpl : public ElfWriter
 {
 	public:
 
-		ElfWriterImpl(IRDB_SDK::FileIR_t* firp, bool write_sections, bool bss_opts ) : ElfWriter(firp, write_sections, bss_opts) { } 
+		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); } 
diff --git a/include/exewrite.h b/include/exewrite.h
index d54f36f693893e9298aa2322cff95962845aca04..e2f5681bcde52085ae264bfb9ed1cf74e65de29e 100644
--- a/include/exewrite.h
+++ b/include/exewrite.h
@@ -52,24 +52,32 @@ class ExeWriter
 			}
 
 
-		unsigned int filesz; 
-		unsigned int memsz; 
-		unsigned int filepos;
-		unsigned int start_page;
-		unsigned int 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(IRDB_SDK::FileIR_t* firp, bool write_sections, bool bss_opts) : m_firp(firp), m_write_sections(write_sections), m_bss_opts(bss_opts) { }
-		virtual ~ExeWriter() {}
-		virtual void Write(const EXEIO::exeio *exeio, const std::string &out_file, const std::string &infile) = 0;
+		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;
@@ -88,12 +96,24 @@ class ExeWriter
 		template <class T> 
 		static inline T page_round_down(const T& x)
 		{
-			return x & (~(PAGE_SIZE-1));
+			return round_down_to(x,PAGE_SIZE);
 		}
 		template <class T> 
 		static inline T page_round_up(const T& x)
 		{
-			return  ( (((uintptr_t)(x)) + PAGE_SIZE-1)  & (~(PAGE_SIZE-1)) );
+			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();
diff --git a/include/pewrite.h b/include/pewrite.h
index bf98b2d1b1c7e2e8fbe2dd29fedcce7f7ad3ae4a..d2808007e0d86e48c9176898e1afee4bef8411f5 100644
--- a/include/pewrite.h
+++ b/include/pewrite.h
@@ -5,40 +5,192 @@
 #include <vector>
 #include <map>
 
-#ifndef PAGE_SIZE
-#define PAGE_SIZE 4096
-#endif
-
 
+template <int bitwidth>
 class PeWriter : ExeWriter
 {
-	public: 
-		PeWriter(IRDB_SDK::FileIR_t* firp, bool write_sections, bool bss_opts) 
-			: 
-				ExeWriter(firp,write_sections,bss_opts)
+
+	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_verson;
+				uint8_t minor_linker_verson;
+				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
+			{
+				uint64_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;
+				uint64_t sizeof_stack_reserve;
+				uint64_t sizeof_stack_commit;
+				uint64_t sizeof_heap_reserve;
+				uint64_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
 		{
-		}
-		virtual ~PeWriter() {}
-		void Write(const EXEIO::exeio *exeiop, const std::string &out_file, const std::string &infile);
+			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;
 
-	protected:
-};
+				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;
 
-template <int bitwidth>
-class PeWriterImpl : public PeWriter
-{
-	public:
-		PeWriterImpl(IRDB_SDK::FileIR_t* firp, bool write_sections, bool bss_opts) 
-			: PeWriter(firp,write_sections,bss_opts)
+
+				
+			}
+
+			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={};
+		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 PeWriter64 = PeWriterImpl<64>;
+
+using PeWriter64 = PeWriter<64>;
 
 
 
diff --git a/src/SConscript b/src/SConscript
index 515ca2d5125f9eecb6b9a93f0e309939f1fb2118..62933245754cc635f57b5735a8cf0942f91e02f1 100644
--- a/src/SConscript
+++ b/src/SConscript
@@ -43,6 +43,7 @@ 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/
@@ -52,9 +53,10 @@ libs='''
 	irdb-core 
 	irdb-cfg 
 	irdb-transform 
-	dl 
 	EXEIO
 	StructDiv
+	pebliss
+	dl 
 	'''
 
 libpath='''
diff --git a/src/elfwrite.cpp b/src/elfwrite.cpp
index af747e0e5f5dfc589dc151b4298e850d166e157f..183bd97790f4638d676fca45495305c687abd269 100644
--- a/src/elfwrite.cpp
+++ b/src/elfwrite.cpp
@@ -32,9 +32,7 @@ static inline uintptr_t page_round_up(uintptr_t x)
         return  ( (((uintptr_t)(x)) + PAGE_SIZE-1)  & (~(PAGE_SIZE-1)) );
 }
 
-
-
-void ElfWriter::Write(const EXEIO::exeio *exeiop, const string &out_file, const string &infile)
+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");
diff --git a/src/pewrite.cpp b/src/pewrite.cpp
index adaa0af51f469883f2df36fc2ed8d21479016a7d..1e1e301bb72fad9010a2fde18a54a3a62259bb36 100644
--- a/src/pewrite.cpp
+++ b/src/pewrite.cpp
@@ -13,6 +13,10 @@
 #include <string>     
 #include <fstream>
 #include <elf.h>
+#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
+#include <pe_bliss.h>
+#pragma GCC diagnostic pop
+
 
 
 using namespace IRDB_SDK;
@@ -20,70 +24,6 @@ using namespace std;
 using namespace zipr;
 using namespace EXEIO;
 
-
-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)) );
-}
-
-#if 0
-/*
-Note: all multi-byte values are stored LSB first. One block is 512 bytes, one paragraph is 16 bytes. See also the entry in Ralf Brown's Interrupt List
-
-Offset (hex)
-Meaning
-00-01	0x4d, 0x5a. This is the "magic number" of an EXE file. The first byte of the file is 0x4d and the second is 0x5a.
-02-03	The number of bytes in the last block of the program that are actually used. If this value is zero, that means the entire last block is used (i.e. the effective value is 512).
-04-05	Number of blocks in the file that are part of the EXE file. If [02-03] is non-zero, only that much of the last block is used.
-06-07	Number of relocation entries stored after the header. May be zero.
-08-09	Number of paragraphs in the header. The program's data begins just after the header, and this field can be used to calculate the appropriate file offset. The header includes the relocation entries. Note that some OSs and/or programs may fail if the header is not a multiple of 512 bytes.
-0A-0B	Number of paragraphs of additional memory that the program will need. This is the equivalent of the BSS size in a Unix program. The program can't be loaded if there isn't at least this much memory available to it.
-0C-0D	Maximum number of paragraphs of additional memory. Normally, the OS reserves all the remaining conventional memory for your program, but you can limit it with this field.
-0E-0F	Relative value of the stack segment. This value is added to the segment the program was loaded at, and the result is used to initialize the SS register.
-10-11	Initial value of the SP register.
-12-13	Word checksum. If set properly, the 16-bit sum of all words in the file should be zero. Usually, this isn't filled in.
-14-15	Initial value of the IP register.
-16-17	Initial value of the CS register, relative to the segment the program was loaded at.
-18-19	Offset of the first relocation item in the file.
-1A-1B	Overlay number. Normally zero, meaning that it's the main program.
-Here is a structure that can be used to represent the EXE header and relocation entries, assuming a 16-bit LSB machine:
-*/
-struct EXE {
-  unsigned short signature; /* == 0x5a4D */
-  unsigned short bytes_in_last_block;
-  unsigned short blocks_in_file;
-  unsigned short num_relocs;
-  unsigned short header_paragraphs;
-  unsigned short min_extra_paragraphs;
-  unsigned short max_extra_paragraphs;
-  unsigned short ss;
-  unsigned short sp;
-  unsigned short checksum;
-  unsigned short ip;
-  unsigned short cs;
-  unsigned short reloc_table_offset;
-  unsigned short overlay_number;
-};
-
-struct EXE_RELOC {
-  unsigned short offset;
-  unsigned short segment;
-};
-/*
-The offset of the beginning of the EXE data is computed like this:
-
-exe_data_start = exe.header_paragraphs * 16L;
-The offset of the byte just after the EXE data (in DJGPP, the size of the stub and the start of the COFF image) is computed like this:
-
-extra_data_start = exe.blocks_in_file * 512L;
-if (exe.bytes_in_last_block)
-  extra_data_start -= (512 - exe.bytes_in_last_block);
-  */
-#endif
 uint8_t dos_header[]= 
 	{
 	/* 00000000 */  0x4d, 0x5a, 0x90, 0x00, 0x03, 0x00, 0x00, 0x00,  0x04, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00,  // |MZ..............|
@@ -100,16 +40,223 @@ uint8_t dos_header[]=
  *      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)
  */
 
 
 
 
-void PeWriter::Write(const EXEIO::exeio *exeiop, const string &out_file, const string &infile)
+template<int width>
+void PeWriter<width>::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);
+	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>
+void PeWriter<width>::InitHeaders()
 {
-//	const auto time_since_epoch=time(nullptr);
 
-	assert(0); // to do 
+	// initialize the headers
+	coff_header_hdr = coff_header_t
+		({
+		 	0x00004550,    // "PE\0\0"
+			0x8664,        // 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,             // ?? have to figure size of optional headers out.
+			0x2f           // relocs stripped |  executable | line numbers stripped | large addresses OK
+	       } );
+
+
+	standard_coff_header_hdr = standard_coff_header_t
+		( {
+			0x20b,  // PE32+
+			2,      // version 2.25 linker (major part)
+			25,     // 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
+			0x1000, // ?? entry point
+			0x1000  // ?? have to figure out code base
+		} );
+
+
+	win_specific_fields_hdr = win_specific_fields_t
+		( {
+		m_firp->getArchitecture()->getFileBase(), 
+		           // image_base;
+		PAGE_SIZE, // section_alignment;
+		512,       // ?? file_alignment -- guessing this is a magic constant?
+		4,         // major_os_version -- constants, may very if we need to port to more win versions.  read from a.ncexe?
+		0,         // minor_os_version;
+		0,         // major_image_version;
+		0,         // minor_image_version;
+		5,         // major_subsystem_version;
+		2,         // minor_subsystem_version;
+		0,         // win32_version;
+		0,         // ?? sizeof_image in memory.  need to figure out.  we're creating constant headers, right?  maybe?
+		0x400,     // sizeof_headers;
+		0,         // checksum ?? need to fix later
+		3,         // subsystem ?? read from input file?
+		0x8000,    // dll_characteristics ?? read from input file?
+		0x200000,  // sizeof_stack_reserve -- maybe read from input file?
+		0x1000,    // sizeof_stack_commit -- maybe read from input file?
+		0x100000,  // sizeof_heap_reserve -- maybe read from input file?
+		0x1000,    // sizeof_heap_commit -- maybe read from input file?
+		0,         // loader_flags -- reserved, must be 0.
+		0x10       // number of rva_and_sizes -- always 16 from what I can tell?
+
+		} );
+
+	const auto pebliss=reinterpret_cast<pe_bliss::pe_base*>(m_exeiop->get_pebliss());
+	assert(pebliss);
+
+	//
+	// 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});
+	}
+
+
+	CreateSectionHeaders();
+}
+
+
+template<int width>
+void  PeWriter<width>::GenerateDataDirectory()
+{
+}
+
+template<int width>
+void  PeWriter<width>::CalculateHeaderSizes()
+{
+	// shorten name
+	auto &hdr_size=coff_header_hdr.sizeof_opt_header;
+
+	hdr_size  = 0;
+	hdr_size += sizeof(standard_coff_header_t);
+	hdr_size += sizeof(win_specific_fields_t);
+	hdr_size += sizeof(image_data_directory_t) * image_data_dir_hdrs.size();
+
+}
+
+template<int width>
+void  PeWriter<width>::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>
+void  PeWriter<width>::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);
+	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.
+
+	// update the header with the total file size.
+	win_specific_fields_hdr.sizeof_image=end_pos;
+
+
 
 }
 
+template class PeWriter<64>;
diff --git a/src/pinner_x86.cpp b/src/pinner_x86.cpp
index f88e9dd3fa49e1bdefd1e288ce7c0618d832ab9b..23ad72580ca4784e96655683a506b1066a9f04d6 100644
--- a/src/pinner_x86.cpp
+++ b/src/pinner_x86.cpp
@@ -192,7 +192,8 @@ bool ZiprPinnerX86_t::ShouldPinImmediately(Instruction_t *upinsn)
 		ft_ibta=upinsn->getFallthrough()->getIndirectBranchTargetAddress();
 
 	/* careful with 1 byte instructions that have a pinned fallthrough */ 
-	if(upinsn->getDataBits().length()==1)
+	const auto len=upinsn->getDataBits().length();
+	if(len==1)
 	{
 		if(upinsn->getFallthrough()==nullptr)
 			return true;
@@ -201,6 +202,28 @@ bool ZiprPinnerX86_t::ShouldPinImmediately(Instruction_t *upinsn)
 			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.
 	pin_at_next_byte = FindPinnedInsnAtAddr(upinsn_ibta->getVirtualOffset() + 1);
 	if ( pin_at_next_byte && 
@@ -221,7 +244,6 @@ bool ZiprPinnerX86_t::ShouldPinImmediately(Instruction_t *upinsn)
 	 */
 		pin_at_next_byte->getFallthrough() == upinsn->getFallthrough() ) 
 	/*
-	 *
 	 * x should become nop, put down immediately
 	 * x+1 should become the entire lock command.
 	 */
@@ -942,32 +964,48 @@ void ZiprPinnerX86_t::ReservePinnedInstructions()
 		const auto upinsn=up.getInstrution();
 		auto addr=upinsn->getIndirectBranchTargetAddress()->getVirtualOffset();
 
-		if(upinsn->getIndirectBranchTargetAddress()->getFileID() ==
-		   BaseObj_t::NOT_IN_DATABASE)
+		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 also attempt to pin any 1-byte insn that falls through to the next pinned address (nops are common).
+		 * 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)
-				printf("Final pinning %p-%p.  fid=%d\n", (void*)addr, (void*)(addr+upinsn->getDataBits().size()-1),
-				upinsn->getAddress()->getFileID());
+				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) {
+		if (m_verbose) 
+		{
 			printf("Working two byte pinning decision at %p for: ", (void*)addr);
 			printf("%s\n", upinsn->getComment().c_str());
 		}
@@ -1135,7 +1173,7 @@ void ZiprPinnerX86_t::ReservePinnedInstructions()
 				if (it==unresolved_pinned_addrs.end())
 					break;
 			}
-			// back up one, because the last  one still needs to be processed.
+			// back up one, because the last one still needs to be processed.
 			--it;
 
 			// resolve any new instructions added for the sled.
@@ -1143,8 +1181,6 @@ void ZiprPinnerX86_t::ReservePinnedInstructions()
 		}
 		else
 			assert(0); // impossible to reach, right?
-	
-
 	}
 }
 
diff --git a/src/zipr.cpp b/src/zipr.cpp
index 904fb6c67f9e01154b742c4229cc3a11fd8d0841..f4266f8a1eb09c4e4e226529e90cb4c38734acda 100644
--- a/src/zipr.cpp
+++ b/src/zipr.cpp
@@ -451,7 +451,7 @@ void ZiprImpl_t::FindFreeRanges(const std::string &name)
 	{ 
 		auto sec = exeiop->sections[i];
 		assert(sec);
-		ordered_sections.insert(std::pair<RangeAddress_t,int>(sec->get_address(), i));
+		ordered_sections.insert({sec->get_address(), i});
 	}
 
 	CreateExecutableScoops(ordered_sections);
@@ -502,18 +502,18 @@ void ZiprImpl_t::FindFreeRanges(const std::string &name)
 	);
 	for( auto it=sorted_scoop_set.begin(); it!=sorted_scoop_set.end(); ++it )
 	{
-		auto this_scoop=*it;
-		DataScoop_t* next_scoop=nullptr;
-		RangeAddress_t this_end = this_scoop->getEnd()->getVirtualOffset(),
-		               next_start = 0;
+		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 << "There's a scoop between " << std::hex
-			     << this_scoop->getStart()->getVirtualOffset()
-			     << " and " << std::hex << this_scoop->getEnd()->getVirtualOffset()
-			     << " with permissions " << std::hex << this_scoop->getRawPerms()
+			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;
 
 		/*
@@ -668,21 +668,11 @@ void ZiprImpl_t::FindFreeRanges(const std::string &name)
 	 * 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");
-
-	/*
-	DataScoop_t *textra_scoop = nullptr;
-	AddressID_t *textra_start_addr = new AddressID_t(),
-	            *textra_end_addr = new AddressID_t();
-		    */
-	// textra_start_addr->setVirtualOffset(textra_start);
-	// textra_end_addr->setVirtualOffset(textra_end);
-	// m_firp->getAddresses().insert(textra_start_addr);
-	// m_firp->getAddresses().insert(textra_end_addr);
-	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);
+	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"
@@ -703,8 +693,6 @@ void ZiprImpl_t::FindFreeRanges(const std::string &name)
 	 * 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.
-	textra_contents.resize(textra_end - textra_start + 1);
-	textra_scoop->setContents(textra_contents);
 	 */
 
 	m_zipr_scoops.insert(textra_scoop);
@@ -716,7 +704,9 @@ void ZiprImpl_t::FindFreeRanges(const std::string &name)
 
 	for(auto scoop : m_firp->getDataScoops())
 	{
-		if(scoop->isExecuteable()) continue;
+		// 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.
@@ -1667,19 +1657,18 @@ void ZiprImpl_t::PatchInstruction(RangeAddress_t from_addr, Instruction_t* to_in
 	// register that it's patch needs to be applied later. 
 
 	UnresolvedUnpinned_t uu(to_insn);
-	auto thepatch=Patch_t(from_addr,UncondJump_rel32);
-
+	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(pair<const  UnresolvedUnpinned_t,Patch_t>(uu,thepatch));
+		patch_list.insert({uu,thepatch});
 	}
 	else
 	{
-		RangeAddress_t to_addr=final_insn_locations[to_insn];
+		const auto to_addr=final_insn_locations[to_insn];
 		assert(to_addr!=0);
 		/*
 		 * TODO: This debugging output is not really exactly correct.
@@ -1911,33 +1900,14 @@ void ZiprImpl_t::OutputBinaryFile(const string &name)
 	const auto bit_width = m_firp->getArchitectureBitWidth();
 	const auto output_filename="c.out";
 
-	if(is_elf)
-	{
-		// create the output file in a totally different way using elfwriter. later we may 
-		// use this instead of the old way.
-
-		//auto elfiop=reinterpret_cast<ELFIO::elfio*>(exeiop->get_elfio());
-		auto ew=unique_ptr<ElfWriter>();
-		ew.reset(
-			bit_width == 64 ? (ElfWriter*)new ElfWriter64(m_firp, *m_add_sections, *m_bss_opts) :
-			bit_width == 32 ? (ElfWriter*)new ElfWriter32(m_firp, *m_add_sections, *m_bss_opts) :
-			throw invalid_argument("Unknown machine width")
-			);
-		ew->Write(exeiop,output_filename, "a.ncexe");
-		ew.reset(nullptr); // explicitly free ew as we're done with it
-	}
-	else if (is_pe)
-	{
-		assert(m_firp->getArchitectureBitWidth()==64);
-		auto pe_write=new PeWriter64(m_firp, *m_add_sections, *m_bss_opts);
-		pe_write->Write(exeiop,output_filename, "a.ncexe");
-	}
-	else
-	{
-		cout << "Cannot create output file of correct type " << endl;
-		assert(0); 
-		abort(); 
-	}
+	auto ew=unique_ptr<ExeWriter>(
+		is_pe  && bit_width == 64 ? (ExeWriter*)new PeWriter64(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
 	auto chmod_cmd=string("chmod +x ")+output_filename;
@@ -2009,11 +1979,11 @@ void ZiprImpl_t::dump_scoop_map()
 
 	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()
-		    <<hex<<left<<setw(10)<< scoop->getName()
+		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;
 	}
 }