diff --git a/.gitattributes b/.gitattributes index 9fd8d073f9761a47b68d2fb3a6de9dda562d409c..b35d339297f35a74e320439c744697b61cdb6393 100644 --- a/.gitattributes +++ b/.gitattributes @@ -4,10 +4,13 @@ include/range.h -text include/unresolved.h -text include/zipr.h -text include/zipr_all.h -text +include/zipr_optimizations.h -text include/zipr_options.h -text +include/zipr_stats.h -text src/Makefile -text src/main.cpp -text src/zipr.cpp -text +src/zipr_options.cpp -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 diff --git a/include/range.h b/include/range.h index 4bb6fb3ad2abd13965c6a72dfc739bd6ff5740e5..e0ec3b7ad5368b1dcbcd45a9a53cf8d63a84412f 100644 --- a/include/range.h +++ b/include/range.h @@ -38,10 +38,20 @@ class Range_t { public: Range_t(RangeAddress_t p_s, RangeAddress_t p_e) : m_start(p_s), m_end(p_e) { } + Range_t() : m_start(0), m_end(0) { } RangeAddress_t GetStart() { return m_start; } RangeAddress_t GetEnd() { return m_end; } + bool Is2ByteRange() + { + return (m_end - m_start) == 2; + }; + bool Is5ByteRange() + { + return (m_end - m_start) == 5; + }; + protected: RangeAddress_t m_start, m_end; diff --git a/include/unresolved.h b/include/unresolved.h index fa58014d350d1503a266c289c56eb226c84da912..49c5d620cf4508ca093cee7f015d38a8e66a09b3 100644 --- a/include/unresolved.h +++ b/include/unresolved.h @@ -71,10 +71,45 @@ inline bool operator< (const UnresolvedUnpinned_t& lhs, const UnresolvedUnpinned class UnresolvedPinned_t : public UnresolvedInfo_t { public: - UnresolvedPinned_t(libIRDB::Instruction_t* p_from) : from_instruction(p_from) {} + UnresolvedPinned_t(libIRDB::Instruction_t* p_from) : from_instruction(p_from), m_range(0,0), m_updated_address(0) {} + UnresolvedPinned_t(libIRDB::Instruction_t* p_from, Range_t range) : from_instruction(p_from), m_range(range) {} libIRDB::Instruction_t* GetInstruction() 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() + { + return m_updated_address != 0; + }; + void SetUpdatedAddress(RangeAddress_t address) + { + m_updated_address = address; + }; + RangeAddress_t GetUpdatedAddress() + { + return m_updated_address; + }; + private: libIRDB::Instruction_t* from_instruction; + Range_t m_range; + RangeAddress_t m_updated_address; friend bool operator< (const UnresolvedPinned_t& lhs, const UnresolvedPinned_t& rhs); diff --git a/include/zipr.h b/include/zipr.h index 8db9c3a73ccb15fe34d3a381cd126ce9bc8b0d27..fed065746962257bb3bf6b6e4696eb977fbe1811 100644 --- a/include/zipr.h +++ b/include/zipr.h @@ -31,12 +31,14 @@ #ifndef zipr_h #define zipr_h +class Options_t; +class Stats_t; class Zipr_t { public: - Zipr_t(libIRDB::FileIR_t* p_firp, const Options_t &p_opts) - : m_firp(p_firp), m_opts(p_opts) + Zipr_t(libIRDB::FileIR_t* p_firp, Options_t &p_opts) + : m_firp(p_firp), m_opts(p_opts) { total_dollops=0; total_dollop_space=0; @@ -55,13 +57,15 @@ class Zipr_t // data for the stuff we're rewriting. libIRDB::FileIR_t* m_firp; - const Options_t& m_opts; + Options_t& m_opts; + Stats_t *m_stats; // phases of rewriting. void FindFreeRanges(const std::string &name); void AddPinnedInstructions(); void ResolvePinnedInstructions(); void ReservePinnedInstructions(); + void PreReserve2ByteJumpTargets(); void ExpandPinnedInstructions(); void Fix2BytePinnedInstructions(); void OptimizePinnedInstructions(); @@ -71,6 +75,7 @@ class Zipr_t // range operatations void SplitFreeRange(RangeAddress_t addr); + void MergeFreeRange(RangeAddress_t addr); std::list<Range_t>::iterator FindFreeRange(RangeAddress_t addr); Range_t GetFreeRange(int size); @@ -104,6 +109,8 @@ class Zipr_t // 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); + libIRDB::Instruction_t *FindPinnedInsnAtAddr(RangeAddress_t addr); + bool ShouldPinImmediately(libIRDB::Instruction_t *upinsn); private: diff --git a/include/zipr_all.h b/include/zipr_all.h index 753d8ae8f79d608683d67f21c9f2912e2fafb181..3ec33419f3fadb33b158de441bc944cb076f701d 100644 --- a/include/zipr_all.h +++ b/include/zipr_all.h @@ -45,10 +45,12 @@ namespace zipr { -#include <zipr_options.h> #include <range.h> #include <unresolved.h> #include <zipr.h> +#include <zipr_optimizations.h> +#include <zipr_options.h> +#include <zipr_stats.h> }; diff --git a/include/zipr_optimizations.h b/include/zipr_optimizations.h new file mode 100644 index 0000000000000000000000000000000000000000..6d4601c3552edd1e154022451b0376fe33314004 --- /dev/null +++ b/include/zipr_optimizations.h @@ -0,0 +1,8 @@ +#ifndef zipr_optimizations_t +#define zipr_optimizations_t + +class Optimizations_t { + public: + enum OptimizationName_t { OptimizationPlopNotJump = 0 , NumberOfOptimizations}; +}; +#endif diff --git a/include/zipr_options.h b/include/zipr_options.h index 6cb2fe019fc2635e33ace6fce80cc49c1273a9eb..f84b6180a725b7ade29e1a657b40e10b0d4b0727 100644 --- a/include/zipr_options.h +++ b/include/zipr_options.h @@ -31,28 +31,36 @@ #ifndef zipr_options_h #define zipr_options_h +#include <zipr_all.h> #include <string> -// #include <libIRDB-core.hpp> +#include <unistd.h> +#include <libIRDB-core.hpp> class Options_t { public: - Options_t() : m_outname("b.out") { }; + Options_t() : m_outname("b.out") { } + + static Options_t* parse_args(int p_argc, char* p_argv[]); + static void print_usage(int p_argc, char *p_argv[]); - static Options_t* parse_args(int p_argc, char* p_argv[]) - { - Options_t *opt=new Options_t; - assert(opt); - opt->m_var_id=::atoi(p_argv[1]); - return opt; - }; - std::string GetOutputFileName(libIRDB::File_t* p_file) { return m_outname; } int GetVariantID() { return m_var_id; } + + void EnableOptimization(Optimizations_t::OptimizationName_t opt) + { + EnabledOptimizations[opt] = 1; + }; + + bool IsEnabledOptimization(Optimizations_t::OptimizationName_t opt) + { + return EnabledOptimizations[opt] == 1; + }; private: std::string m_outname; int m_var_id; + int EnabledOptimizations[Optimizations_t::NumberOfOptimizations]; }; #endif diff --git a/include/zipr_stats.h b/include/zipr_stats.h new file mode 100644 index 0000000000000000000000000000000000000000..938863de77042b26edea909c4c05e1efdf7e44b6 --- /dev/null +++ b/include/zipr_stats.h @@ -0,0 +1,18 @@ +#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; + } + }; + 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/src/Makefile b/src/Makefile index 484a9f909eb3b5ed42047055467c618ef80a2707..8d82759084433c605858397ad9f8fdcaef742005 100644 --- a/src/Makefile +++ b/src/Makefile @@ -3,7 +3,7 @@ INC=-I../include -I../third_party/ELFIO/elfio-2.2 -I$(SECURITY_TRANSFORMS_HOME)/ -SRCS=main.cpp zipr.cpp +SRCS=zipr.cpp zipr_options.cpp main.cpp OBJS=$(subst .cpp,.o, $(SRCS)) HDRS=../include/*.h EXE=zipr.exe diff --git a/src/main.cpp b/src/main.cpp index 54779a54c894717445ced7900adc38a63314bc6e..d114e813c7a7c2c00d3c2e8e57b705afc1008337 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -53,6 +53,12 @@ int main(int argc, char* argv[]) VariantID_t *pidp=NULL; FileIR_t * firp=NULL; + if ((!options->GetVariantID())) + { + Options_t::print_usage(argc, argv); + return 1; + } + try { /* setup the interface to the sql server */ diff --git a/src/zipr.cpp b/src/zipr.cpp index 8bee382358fcc3194f20a31cdf0bfe5271142340..363a29b89efd4067884b16017e7549061573aaee 100644 --- a/src/zipr.cpp +++ b/src/zipr.cpp @@ -54,6 +54,7 @@ using namespace ELFIO; void Zipr_t::CreateBinaryFile(const std::string &name) { + m_stats = new Stats_t(); // create ranges, including extra range that's def. big enough. FindFreeRanges(name); @@ -64,11 +65,30 @@ void Zipr_t::CreateBinaryFile(const std::string &name) // reserve space for pins ReservePinnedInstructions(); + PreReserve2ByteJumpTargets(); + // expand 2-byte pins into 4-byte pins ExpandPinnedInstructions(); - // Allocate space near 2-byte pins for a 5-byte pin - Fix2BytePinnedInstructions(); + 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(); @@ -96,16 +116,55 @@ void Zipr_t::FindFreeRanges(const std::string &name) RangeAddress_t last_end=0; RangeAddress_t max_addr=0; - // For all sections + std::map<RangeAddress_t, int> ordered_sections; + + /* + * Make an ordered list of the sections + * by their starting address. + */ Elf_Half n = elfiop->sections.size(); for ( Elf_Half i = 0; i < n; ++i ) { section* sec = elfiop->sections[i]; assert(sec); + ordered_sections.insert(std::pair<RangeAddress_t,int>(sec->get_address(), i)); + } + + std::map<RangeAddress_t, int>::iterator it = ordered_sections.begin(); + for (;it!=ordered_sections.end();) + { + section* sec = elfiop->sections[it->second]; + assert(sec); RangeAddress_t start=sec->get_address(); RangeAddress_t end=sec->get_size()+start-1; + printf("Section %s:\n", sec->get_name().c_str()); + + ++it; + if (false) + //if ((++it) != ordered_sections.end()) + { + /* + * TODO: This works. However, the updated + * section size is not properly handled + * in OutputBinaryFile. So, it is disabled + * until that is handled. + */ + section *next_section = elfiop->sections[it->second]; + + printf("Using %s as the next section (%p).\n", + next_section->get_name().c_str(), + (void*)next_section->get_address()); + printf("Modifying the section end. Was %p.", (void*)end); + + end = next_section->get_address() - 1; + sec->set_size(end - start); + + printf(". Is %p.\n", (void*)end); + + } + printf("max_addr is %p, end is %p\n", (void*)max_addr, (void*)end); if(start && end>max_addr) { @@ -185,6 +244,100 @@ list<Range_t>::iterator Zipr_t::FindFreeRange(RangeAddress_t addr) return free_ranges.end(); } +void Zipr_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); + bool merged = false; + list<Range_t>::iterator it=free_ranges.begin(); + for(;it!=free_ranges.end();++it) + { + Range_t r=*it; + if ((addr+1) == r.GetStart()) { + /* + * Make the beginning of this range + * one byte smaller! + */ + Range_t nnr(addr, r.GetEnd()); + printf("Expanded range:\n"); + printf("from: %p to %p\n", (void*)r.GetStart(), (void*)r.GetEnd()); + printf("to: %p to %p\n", (void*)nnr.GetStart(), (void*)nnr.GetEnd()); + free_ranges.insert(it, nnr); + free_ranges.erase(it); + nr = nnr; + merged = true; + break; + } else if ((addr-1) == r.GetEnd()) { + /* + * Make the end of this range one byte + * bigger + */ + Range_t nnr(r.GetStart(), addr); + printf("Expanded range:\n"); + printf("from: %p to %p\n", (void*)r.GetStart(), (void*)r.GetEnd()); + printf("to: %p to %p\n", (void*)nnr.GetStart(), (void*)nnr.GetEnd()); + free_ranges.insert(it, nnr); + free_ranges.erase(it); + nr = nnr; + merged = true; + break; + } + } + + if (!merged) + free_ranges.insert(it, nr); + + /* + * Correctness: + * Take a pass through and see if there are + * free ranges that can now be merged. This + * is important because it's possible that + * we added the byte to the end of a range + * where it could also have gone at the + * beginning of another. + */ + for(it=free_ranges.begin();it!=free_ranges.end();++it) + { + Range_t r = *it; + if (( + /* + * <--r--> + * <--nr--> + */ + (r.GetEnd() >= nr.GetStart() && + r.GetEnd() <= nr.GetEnd()) || + /* + * <--r--> + * <--nr--> + */ + (r.GetStart() <= nr.GetEnd() && + r.GetEnd() >= nr.GetEnd()) + ) && + (r.GetStart() != nr.GetStart() || + r.GetEnd() != nr.GetEnd())) + { + /* + * merge. + */ + Range_t merged_range(std::min(r.GetStart(), nr.GetStart()), std::max(r.GetEnd(), nr.GetEnd())); + printf("Merged two ranges:\n"); + printf("1: %p to %p\n", (void*)r.GetStart(), (void*)r.GetEnd()); + printf("2: %p to %p\n", (void*)nr.GetStart(), (void*)nr.GetEnd()); + free_ranges.insert(it, merged_range); + free_ranges.erase(it); + return; + } + } +} + void Zipr_t::SplitFreeRange(RangeAddress_t addr) { list<Range_t>::iterator it=FindFreeRange(addr); @@ -215,11 +368,36 @@ void Zipr_t::SplitFreeRange(RangeAddress_t addr) } } +Instruction_t *Zipr_t::FindPinnedInsnAtAddr(RangeAddress_t addr) +{ + for( + set<Instruction_t*>::const_iterator it=m_firp->GetInstructions().begin(); + it!=m_firp->GetInstructions().end(); + ++it + ) + { + RangeAddress_t ibta_addr; + Instruction_t* insn=*it; + assert(insn); -static bool should_pin_immediately(Instruction_t *upinsn) + if(!insn->GetIndirectBranchTargetAddress()) { + continue; + } + ibta_addr=(unsigned)insn-> + GetIndirectBranchTargetAddress()-> + GetVirtualOffset(); + + if (addr == ibta_addr) + return insn; + } + return NULL; +} + +bool Zipr_t::ShouldPinImmediately(Instruction_t *upinsn) { DISASM d; upinsn->Disassemble(d); + Instruction_t *pin_at_next_byte = NULL; if(d.Instruction.BranchType==RetType) return true; @@ -237,9 +415,124 @@ static bool should_pin_immediately(Instruction_t *upinsn) return true; } + /* + * lock cmpxchange op1 op2 [pinned at x] + * x x+1 x+2 x+3 + * + * pin at x and pin at x+1 + * + * x should become nop, put down immediately + * x+1 should become the entire lock command. + */ + if ((pin_at_next_byte = + FindPinnedInsnAtAddr(upinsn_ibta->GetVirtualOffset() + 1)) != NULL) + { + printf("Using pin_at_next_byte special case.\n"); + /* + * 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 Zipr_t::PreReserve2ByteJumpTargets() +{ + for(set<UnresolvedPinned_t>::const_iterator it=two_byte_pins.begin(); + it!=two_byte_pins.end(); + ) + { + UnresolvedPinned_t up=*it; + bool found_close_target = false; + Instruction_t* upinsn=up.GetInstruction(); + + RangeAddress_t addr; + + if (up.HasUpdatedAddress()) + { + addr = up.GetUpdatedAddress(); + } + else + { + addr=upinsn->GetIndirectBranchTargetAddress()->GetVirtualOffset(); + } + + /* + * 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) + { + printf("Looking for %d-byte jump targets to pre-reserve.\n", size); + for(int i=120;i>=-120;i--) + { + if(AreBytesFree(addr+i,size)) + { + 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), + (void*)upinsn->GetIndirectBranchTargetAddress()->GetVirtualOffset()); + + up.SetRange(Range_t(addr+i, addr+i+size)); + for (int j = up.GetRange().GetStart(); j<up.GetRange().GetEnd(); j++) + { + SplitFreeRange(j); + } + found_close_target = true; + break; + } + } + if (found_close_target) + break; + } + + if (!found_close_target) + { + printf("FATAL: No location for near jump reserved.\n"); + assert(false); + ++it; + } + else + { + UnresolvedPinned_t new_up = UnresolvedPinned_t(up.GetInstruction(), up.GetRange()); + if (up.HasUpdatedAddress()) + { + new_up.SetUpdatedAddress(up.GetUpdatedAddress()); + } + two_byte_pins.erase(it++); + two_byte_pins.insert(new_up); + + } + } +} + void Zipr_t::ReservePinnedInstructions() { set<UnresolvedPinned_t> reserved_pins; @@ -267,7 +560,7 @@ void Zipr_t::ReservePinnedInstructions() * 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). */ - if(should_pin_immediately(upinsn)) + if(ShouldPinImmediately(upinsn)) { printf("Final pinning %p-%p. fid=%d\n", (void*)addr, (void*)(addr+upinsn->GetDataBits().size()-1), upinsn->GetAddress()->GetFileID()); @@ -281,9 +574,13 @@ void Zipr_t::ReservePinnedInstructions() } char bytes[]={0xeb,0}; // jmp rel8 - printf("Two-byte Pinning %p-%p. fid=%d\n", (void*)addr, (void*)(addr+sizeof(bytes)-1), - upinsn->GetAddress()->GetFileID()); - + + printf("Two-byte Pinning %p-%p. fid=%d\n", + (void*)addr, + (void*)(addr+sizeof(bytes)-1), + upinsn->GetAddress()->GetFileID()); + printf("%s\n", upinsn->GetComment().c_str()); + two_byte_pins.insert(up); for(int i=0;i<sizeof(bytes);i++) { @@ -292,30 +589,37 @@ void Zipr_t::ReservePinnedInstructions() SplitFreeRange(addr+i); } } - } - void Zipr_t::ExpandPinnedInstructions() { /* now, all insns have 2-byte pins. See which ones we can make 5-byte pins */ - for( - set<UnresolvedPinned_t>::const_iterator it=two_byte_pins.begin(); - it!=two_byte_pins.end(); - ) + for( + set<UnresolvedPinned_t>::iterator it=two_byte_pins.begin(); + it!=two_byte_pins.end(); + ) { UnresolvedPinned_t up=*it; Instruction_t* upinsn=up.GetInstruction(); RangeAddress_t addr=upinsn->GetIndirectBranchTargetAddress()->GetVirtualOffset(); - char bytes[]={0xe9,0,0,0,0}; // jmp rel8 bool can_update=AreBytesFree(addr+2,sizeof(bytes)-2); if(can_update) { printf("Found %p can be updated to 5-byte jmp\n", (void*)addr); PlopJump(addr); + + /* + * Unreserve those bytes that we reserved before! + */ + for (int j = up.GetRange().GetStart(); j<up.GetRange().GetEnd(); j++) + { + MergeFreeRange(j); + } + up.SetRange(Range_t(0,0)); + five_byte_pins[up]=addr; two_byte_pins.erase(it++); total_5byte_pins++; @@ -323,7 +627,7 @@ void Zipr_t::ExpandPinnedInstructions() } else { - ++it; + ++it; printf("Found %p can NOT be updated to 5-byte jmp\n", (void*)addr); total_2byte_pins++; total_trampolines++; @@ -337,46 +641,92 @@ void Zipr_t::ExpandPinnedInstructions() void Zipr_t::Fix2BytePinnedInstructions() { - for( + for( set<UnresolvedPinned_t>::const_iterator it=two_byte_pins.begin(); - it!=two_byte_pins.end(); - ) + it!=two_byte_pins.end(); + ) { UnresolvedPinned_t up=*it; Instruction_t* upinsn=up.GetInstruction(); - RangeAddress_t addr=upinsn->GetIndirectBranchTargetAddress()->GetVirtualOffset(); + RangeAddress_t addr; + + if (up.HasUpdatedAddress()) + { + addr = up.GetUpdatedAddress(); + } + else + { + addr=upinsn->GetIndirectBranchTargetAddress()->GetVirtualOffset(); + } - // check for near branch instructions - for(int i=0;i<120;i++) // 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. + if (up.HasRange()) { - if(AreBytesFree(addr-i,5)) + /* + * Always clear out the previously reserved space. + */ + for (int j = up.GetRange().GetStart(); j<up.GetRange().GetEnd(); j++) { - printf("Found location for 2-byte->5-byte conversion (%p-%p)->(%p-%p)\n", - (void*)addr,(void*)(addr+1), (void*)(addr-i),(void*)(addr-i+4)); - - five_byte_pins[up]=addr-i; - PlopJump(addr-i); - PatchJump(addr, addr-i); - break; + MergeFreeRange(j); } - else if(AreBytesFree(addr+i,5)) - { - printf("Found location for 2-byte->5-byte conversion (%p-%p)->(%p-%p)\n", - (void*)addr,(void*)(addr+1), (void*)(addr+i),(void*)(addr+i+5)); - five_byte_pins[up]=addr+i; - PlopJump(addr+i); - PatchJump(addr, addr+i); - break; + if (up.GetRange().Is5ByteRange()) + { + 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()), + (void*)upinsn->GetIndirectBranchTargetAddress()->GetVirtualOffset()); + + five_byte_pins[up] = up.GetRange().GetStart(); + PlopJump(up.GetRange().GetStart()); + PatchJump(addr, up.GetRange().GetStart()); + + two_byte_pins.erase(it++); } - else + else if (up.HasRange() && up.GetRange().Is2ByteRange()) { -// printf("Not free at %p or %p\n", (void*)(addr-i),(void*)(addr+i)); + /* + * 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.GetInstruction()); + new_up.SetUpdatedAddress(up.GetRange().GetStart()); + + char bytes[]={0xeb,0}; // jmp rel8 + for(int i=0;i<sizeof(bytes);i++) + { + assert(byte_map.find(up.GetRange().GetStart()+i) == byte_map.end() ); + byte_map[up.GetRange().GetStart()+i]=bytes[i]; + SplitFreeRange(up.GetRange().GetStart()+i); + assert(!IsByteFree(up.GetRange().GetStart()+i)); + } + + printf("Patching 2 byte to 2 byte: %p to %p (orig: %p)\n", + (void*)addr, + (void*)up.GetRange().GetStart(), + (void*)upinsn->GetIndirectBranchTargetAddress()->GetVirtualOffset()); + + PatchJump(addr, up.GetRange().GetStart()); + two_byte_pins.erase(it++); + two_byte_pins.insert(new_up); } } - two_byte_pins.erase(it++); + else + { + printf("FATAL: Two byte pin without reserved range: %p\n", (void*)addr); + assert(false); + it++; + } } - } @@ -578,19 +928,41 @@ void Zipr_t::ProcessUnpinnedInstruction(const UnresolvedUnpinned_t &uu, const Pa DISASM d; cur_insn->Disassemble(d); int id=cur_insn->GetBaseID(); - printf("Emitting %d:%s at %p until ", id, d.CompleteInstr, (void*)cur_addr); - cur_addr=PlopInstruction(cur_insn,cur_addr); - printf("%p\n", (void*)cur_addr); - cur_insn=cur_insn->GetFallthrough(); - insn_count++; + RangeAddress_t to_addr; + /* + * Check to see if id is already plopped somewhere. + * If so, emit a jump to it and break. + * TODO: Test and enable. + */ + if (false) + //if ((to_addr=final_insn_locations[to_insn]) != 0) + { + printf("Fallthrough loop detected. " + "Emitting jump from %p to %p.\n", + (void*)cur_addr, + (void*)to_addr); + PlopJump(cur_addr); + PatchJump(cur_addr, to_addr); + cur_insn = NULL; + cur_addr+=5; + break; + } + else + { + printf("Emitting %d:%s at %p until ", id, d.CompleteInstr, (void*)cur_addr); + cur_addr=PlopInstruction(cur_insn,cur_addr); + printf("%p\n", (void*)cur_addr); + cur_insn=cur_insn->GetFallthrough(); + insn_count++; + } } if(cur_insn) { // Mark this insn as needing a patch since we couldn't completely empty // the 'fragment' we are translating into the elf section. - UnresolvedUnpinned_t uu(cur_insn); - Patch_t thepatch(cur_addr,UncondJump_rel32); - patch_list.insert(pair<UnresolvedUnpinned_t,Patch_t>(uu,thepatch)); + UnresolvedUnpinned_t uu(cur_insn); + Patch_t thepatch(cur_addr,UncondJump_rel32); + patch_list.insert(pair<UnresolvedUnpinned_t,Patch_t>(uu,thepatch)); PlopJump(cur_addr); truncated="truncated due to lack of space."; total_tramp_space+=5; @@ -946,8 +1318,6 @@ void Zipr_t::OutputBinaryFile(const string &name) { b=byte_map[i]; } - - if(i-start_of_new_space<200)// keep verbose output short enough. printf("Writing byte %#2x at %p, fileoffset=%lld\n", ((unsigned)b)&0xff, (void*)i, (long long)(i-start_of_new_space)); diff --git a/src/zipr_options.cpp b/src/zipr_options.cpp new file mode 100644 index 0000000000000000000000000000000000000000..4ca05858a701723f4669bb07d84516fc63be6c1f --- /dev/null +++ b/src/zipr_options.cpp @@ -0,0 +1,82 @@ +#include <zipr_all.h> +#include <unistd.h> +#include <stdlib.h> +#include <getopt.h> + +using namespace zipr; + +void Options_t::print_usage(int p_argc, char *p_argv[]) +{ + printf("%s [options]\n", p_argv[0]); + printf("\t-v variant-id\t--variant variant-id: " + "Variant ID. Mandatory. Default: none.\n"); + printf("\t-o file\t\t--output file: " + "Output file name. Optional. Default: b.out.\n"); + printf("\t-z optimization\t--optimize optimization: " + "Enable an optimization. Repeatable. Optional. \n"); +} + +Options_t* Options_t::parse_args(int p_argc, char* p_argv[]) +{ + extern char *optarg; + extern int optind, opterr, optopt; + int option = 0; + char options[] = "o:v:z:"; + struct option long_options[] = { + {"output", 1, 0, 'o'}, + {"variant", 1, 0, 'v'}, + {"optimize", 0, 0, 'z'}, + {0, 0, 0, 0}, + }; + + Options_t *opt=new Options_t; + assert(opt); + + while ((option = getopt_long( + p_argc, + p_argv, + options, + long_options, + NULL)) != -1) + { + switch (option) { + case 'z': + { + if (!strcmp("plopnotjump", ::optarg)) + { + opt->EnableOptimization( + Optimizations_t::OptimizationPlopNotJump); + } + else + { + printf("Warning: Unrecognized optimization: %s\n", ::optarg); + } + break; + } + case 'o': + { + opt->m_outname = std::string(::optarg); + break; + } + case 'v': + { + char *valid = NULL; + long int variant_id = ::strtol(::optarg, + &valid, + 10); + if (*valid == '\0') { + opt->m_var_id = variant_id; + break; + } + printf("Error: Invalid variant id (%s).\n", ::optarg); + break; + } + default: + { + printf("Warning: Unrecognized option!\n"); + break; + } + } + } + return opt; +}