#include "mg.hpp" #include <assert.h> #include <stdexcept> #include <unistd.h> #include <memory> #include <inttypes.h> #include <algorithm> #include <elf.h> #include <cctype> #include <iomanip> #include <cstdlib> #include <random> #include <functional> using namespace std; using namespace IRDB_SDK; using namespace EXEIO; #define ALLOF(s) begin(s), end(s) // use this to determine whether a scoop has a given name. static struct ScoopFinder : binary_function<DataScoop_t*,string,bool> { // declare a simple scoop finder function that finds scoops by name bool operator()(const DataScoop_t* scoop, const string word) const { return (scoop->getName() == word); }; } finder; template<class S, class T> inline static bool contains(const S &container, const T& value) { return find(container.begin(), container.end(), value) != container.end(); } static bool arg_has_memory(const DecodedOperand_t &arg) { /* if it's relative memory, watch out! */ if(arg.isMemory()) return true; return false; } static bool arg_has_relative(const DecodedOperand_t &arg) { /* if it's relative memory, watch out! */ if(arg.isMemory() && arg.isPcrel()) return true; return false; } static DecodedOperandVector_t::iterator find_memory_operand(DecodedOperandVector_t &operands) { // const auto operands=disasm.getOperands(); auto the_arg=operands.end(); if(operands.size()>0 && arg_has_memory(*operands[0])) the_arg=next(operands.begin(),0); if(operands.size()>1 && arg_has_memory(*operands[1])) the_arg=next(operands.begin(),1); if(operands.size()>2 && arg_has_memory(*operands[2])) the_arg=next(operands.begin(),2); if(operands.size()>3 && arg_has_memory(*operands[3])) the_arg=next(operands.begin(),3); return the_arg; } template< typename T > static std::string to_hex_string( T i ) { std::stringstream stream; stream << "0x" << std::hex << i; return stream.str(); } template <class T_Sym, class T_Rela, class T_Rel, class T_Dyn, class T_Extractor> bool MoveGlobals_t<T_Sym,T_Rela,T_Rel,T_Dyn,T_Extractor>::is_elftable(DataScoop_t* ret) { return find(ALLOF(elftable_names), ret->getName()) != elftable_names.end() ; }; template <class T_Sym, class T_Rela, class T_Rel, class T_Dyn, class T_Extractor> bool MoveGlobals_t<T_Sym,T_Rela,T_Rel,T_Dyn,T_Extractor>::is_noptr_table(DataScoop_t* ret) { return find(ALLOF(elftable_nocodeptr_names), ret->getName()) != elftable_nocodeptr_names.end() ; }; template <class T_Sym, class T_Rela, class T_Rel, class T_Dyn, class T_Extractor> MoveGlobals_t<T_Sym,T_Rela,T_Rel,T_Dyn,T_Extractor>::MoveGlobals_t( VariantID_t *p_variantID, FileIR_t *p_variantIR, const string &p_dont_move, const string &p_move_only, const int p_max_mov, const bool p_random, const bool p_aggressive, const bool p_use_stars) : Transform_t(p_variantIR), exe_reader(NULL), tied_unpinned(0), tied_pinned(0), tied_nochange(0), ties_for_folded_constants(0), dont_move(p_dont_move), move_only(p_move_only), max_moveables(p_max_mov), random(p_random), aggressive(p_aggressive), m_use_stars(p_use_stars), m_verbose(getenv("MG_VERBOSE") != nullptr) { } template <class T_Sym, class T_Rela, class T_Rel, class T_Dyn, class T_Extractor> int MoveGlobals_t<T_Sym,T_Rela,T_Rel,T_Dyn,T_Extractor>::execute(pqxxDB_t &pqxx_interface) { // read the executeable file // load the executable. exe_reader = new EXEIO::exeio; assert(exe_reader); exe_reader->load((char*)"a.ncexe"); if(m_use_stars) { auto deep_analysis=DeepAnalysis_t::factory(getFileIR(), aeSTARS, {"SetDeepLoopAnalyses=true", "SetConstantPropagation=true"}); deep_global_static_ranges = deep_analysis -> getStaticGlobalRanges(); sentinels = deep_analysis -> getRangeSentinels(); cout<<dec; cout<<"#ATTRIBUTE "<<deep_global_static_ranges->size() <<" num_global_static_range_annotations" <<endl; cout<<"#ATTRIBUTE "<<sentinels->size() <<" num_sentinel_annotations" <<endl; } ParseSyms(exe_reader); SetupScoopMap(); FilterScoops(); TieScoops(); FindInstructionReferences(); // may record some scoops are tied together FindDataReferences(); FilterAndCoalesceTiedScoops(); UpdateScoopLocations(); PrintStats(); return 0; } // go through the .symtab and .dynsym bits of the table and make scoops for each symbol. template <class T_Sym, class T_Rela, class T_Rel, class T_Dyn, class T_Extractor> void MoveGlobals_t<T_Sym,T_Rela,T_Rel,T_Dyn,T_Extractor>::SetupScoopMap() { for(auto &s : getFileIR()->getDataScoops()) { if(s->getStart()->getVirtualOffset() == 0) continue; if(s->getName() == ".tdata") continue; if(s->getName() == ".tbss") continue; RangePair_t p(s->getStart()->getVirtualOffset(), s->getEnd()->getVirtualOffset()); scoop_map[p]=s; } } template <class T_Sym, class T_Rela, class T_Rel, class T_Dyn, class T_Extractor> DataScoop_t* MoveGlobals_t<T_Sym,T_Rela,T_Rel,T_Dyn,T_Extractor>::findScoopByAddress(const IRDB_SDK::VirtualOffset_t a) const { RangePair_t p(a,a); auto smit=scoop_map.find(p); if(smit==scoop_map.end()) return NULL; return smit->second; } template <class T_Sym, class T_Rela, class T_Rel, class T_Dyn, class T_Extractor> bool MoveGlobals_t<T_Sym, T_Rela, T_Rel, T_Dyn, T_Extractor>::AreScoopsAdjacent(const DataScoop_t *a, const DataScoop_t *b) const { bool adjacent = true; const IRDB_SDK::VirtualOffset_t aStart = a->getStart()->getVirtualOffset(); const IRDB_SDK::VirtualOffset_t aEnd = a->getEnd()->getVirtualOffset(); const IRDB_SDK::VirtualOffset_t bStart = b->getStart()->getVirtualOffset(); const IRDB_SDK::VirtualOffset_t bEnd = b->getEnd()->getVirtualOffset(); IRDB_SDK::VirtualOffset_t FirstEnd, SecondStart; if (aStart > bStart) { FirstEnd = bEnd; SecondStart = aStart; } else { FirstEnd = aEnd; SecondStart = bStart; } for (IRDB_SDK::VirtualOffset_t i = FirstEnd + 1; adjacent && (i < SecondStart); ++i) { DataScoop_t *c = findScoopByAddress(i); if (c) { adjacent = false; // found intervening scoop before SecondStart } } return adjacent; } // end of AreScoopsAdjacent() // go through the .symtab and .dynsym bits of the table and make scoops for each symbol. template <class T_Sym, class T_Rela, class T_Rel, class T_Dyn, class T_Extractor> void MoveGlobals_t<T_Sym,T_Rela,T_Rel,T_Dyn,T_Extractor>::ParseSyms(EXEIO::exeio * readerp) { auto max_id=getFileIR()->getMaxBaseID(); if(m_verbose) cout<<"Initial scoops:"<<endl; for(const auto &scoop : getFileIR()->getDataScoops()) { if(m_verbose) { cout<<"scoop: "<<scoop->getName()<<" ("<<hex<<scoop->getStart()->getVirtualOffset() <<"-"<<scoop->getEnd()->getVirtualOffset()<<")"<<endl; } const auto moveable_sections=set<string>({ ".interp", ".note.ABI-tag", ".note.gnu.build-id", ".gnu.hash", ".dynsym", ".dynstr", ".gnu.version", ".gnu.version_r", ".rel.dyn", ".rel.plt", ".rela.dyn", ".rela.plt", ".init_array", ".fini_array", ".jcr", ".dynamic", ".got", ".got.plt" }); // white list some scoops as moveable, despite the symbol table if(moveable_sections.find(scoop->getName())!=moveable_sections.end()) { cout<<"Register scoop "<<scoop->getName()<<" as movable"<<endl; moveable_scoops.insert(scoop); } } assert(readerp); auto elfiop=reinterpret_cast<ELFIO::elfio*>(readerp->get_elfio()); assert(elfiop); auto &reader=*elfiop; auto splits=0u; // for each section in the elf file. auto n = (Elf_Half) reader.sections.size(); for ( auto i = (Elf_Half ) 0; i < n; ++i ) { // For all sections auto sec = reader.sections[i]; const char* max_splits = m_verbose ? getenv("MG_MAX_SPLITS") : "0"; // if it's a symtab section if ( SHT_SYMTAB == sec->get_type() || SHT_DYNSYM == sec->get_type() ) { auto symbols = ELFIO::symbol_section_accessor ( reader, sec ); // for each symbol in the section auto sym_no = symbols.get_symbols_num(); for (auto i = (decltype(sym_no))0; i < sym_no; ++i ) { // check to see if we've been directed to not split everything up. if (max_splits && (splits >= strtoul(max_splits, NULL, 0))) break; auto name=std::string(); auto value=(Elf64_Addr)0; // note: elf64_addr OK for 32-bit machines still. auto size=(Elf_Xword)0; auto bind=(unsigned char)0; auto type=(unsigned char)0; auto section=(Elf_Half)0; auto other=(unsigned char)0; // elfio always takes a value of type Elf64-Addr regardless of mach type. symbols.get_symbol( i, name, value, size, bind, type, section, other ); // if it's a symbol that describes an object (as opposed to a binding, or a function or a ...) if(type==STT_OBJECT && (bind==STB_LOCAL || bind==STB_GLOBAL) && value!=0 && size!=0) { auto tosplit=getFileIR()->findScoop(value); // something went wrong if we can't find the scoop for this object. if(tosplit==NULL) continue; cout << "Section: "<<sec->get_name() << " name="<< name << " size=" <<hex<<size<< " addr="<<hex<<value<<" scoop: "<<tosplit->getName()<<endl; auto before=(DataScoop_t*)NULL, containing=(DataScoop_t*)NULL, after=(DataScoop_t*)NULL; if(m_verbose) { cout<<"\ttosplit: "<<hex<<tosplit->getStart()->getVirtualOffset()<<"-" <<tosplit->getEnd()->getVirtualOffset(); } if(value+size-1 > tosplit->getEnd()->getVirtualOffset()) { cout<<"Skipping symbol "<<name<<" due to an object that's already split?"<<endl; cout<<"Start (but not end) of "<<name<<" is in in object " << tosplit->getName()<<":("<<hex<<tosplit->getStart()->getVirtualOffset()<<"-" << tosplit->getEnd()->getVirtualOffset()<<")"<<endl;; continue; // try next symbol } if(moveable_scoops.find(tosplit)!=end(moveable_scoops)) { cout<<"Avoiding resplit of "<<name<<" due to an object that's already split?"<<endl; // don't re-split something that's arlready moveable. continue; } getFileIR()->splitScoop(tosplit, value, size, before,containing,after,&max_id); if(m_verbose) { if(before) { cout<<"\tBefore: "<<hex<<before->getStart()->getVirtualOffset() <<"-"<<before->getEnd()->getVirtualOffset(); } cout<<"\tContaining: "<<hex<<containing->getStart()->getVirtualOffset() <<"-"<<containing->getEnd()->getVirtualOffset(); if(after) { cout<<"\tAfter: "<<hex<<after->getStart()->getVirtualOffset() <<"-"<<after->getEnd()->getVirtualOffset(); } cout<<endl; } assert(containing); containing->setName(name); moveable_scoops.insert(containing); splits++; } } cout << std::endl; } } // guarantee unique scoop names auto scoop_names=set<string>(); for(auto & s : getFileIR()->getDataScoops()) { while(scoop_names.find(s->getName())!=scoop_names.end()) { cout<<"Rename scoop because of name conflict: "<<s->getName()<<" --> "; s->setName(s->getName()+"-renamed"+to_string(rand())); cout<<s->getName()<<endl; } scoop_names.insert(s->getName()); } cout<<"# ATTRIBUTE Non-Overlapping_Globals::data_scoop_splits_performed="<<dec<<splits<<endl; } template <class T_Sym, class T_Rela, class T_Rel, class T_Dyn, class T_Extractor> void MoveGlobals_t<T_Sym,T_Rela,T_Rel,T_Dyn,T_Extractor>::FilterScoops() { const auto mg_env = m_verbose; // filter using the move_only option DataScoopSet_t move_only_scoops; // for each word in move_only istringstream mo_ss(move_only); for_each(istream_iterator<string>(mo_ss), istream_iterator<string>(), [&](const string & word) { // find the scoop auto it=find_if(ALLOF(moveable_scoops), bind2nd(finder, word)); // if found, insert into the move_only set. if(it!=moveable_scoops.end()) { if(mg_env) cout<<"Keeping scoop (for mo_ss) "<< word << endl; move_only_scoops.insert(*it); } else { if(mg_env) cout<<"Skipping scoop (for mo_ss) "<< word << endl; } }); // update the moveable_scoops based on the move_only set. if(move_only != "" ) { moveable_scoops.clear(); moveable_scoops.insert(ALLOF(move_only_scoops)); if(mg_env) { cout<<"Moveable Scoops after move_only filter:"<<endl; for(auto &s : moveable_scoops) cout<<s->getName()<<endl; cout<<endl; } } // filter based on the dont_move option // for each word in dont_move istringstream dm_ss(dont_move); for_each(istream_iterator<string>(dm_ss), istream_iterator<string>(), [&](const string & word) { // find scoop by that name. auto it=find_if(ALLOF(moveable_scoops), bind2nd(finder,word)); if(it!=moveable_scoops.end()) { moveable_scoops.erase(*it); } }); if(dont_move!="") { if(m_verbose) { cout<<"Moveable Scoops after dont_move filter:"<<endl; for(auto &s : moveable_scoops) cout<<s->getName()<<endl; cout<<endl; } } if(max_moveables>0) { mt19937 generator(time(0)); uniform_real_distribution<double> distribution(0.0,1.0); while(moveable_scoops.size() > (unsigned)max_moveables) { if (random == true) { double rand_num = distribution(generator); int rand_idx = (int) (rand_num * moveable_scoops.size()); auto it = moveable_scoops.begin(); advance(it, rand_idx); moveable_scoops.erase(it); } else moveable_scoops.erase(prev(moveable_scoops.end())); } } } template <class T_Sym, class T_Rela, class T_Rel, class T_Dyn, class T_Extractor> void MoveGlobals_t<T_Sym,T_Rela,T_Rel,T_Dyn,T_Extractor>::TieScoops() { struct scoop_pairs_t { string first, second; }scoop_pairs[] = { { ".rel.dyn", ".rel.plt" }, // the dynamic linker goes through both sections together when LD_BIND_NOW is set. { ".rela.dyn", ".rela.plt" } // can't tie .got and .got.plt because of relro differences. // can make insanity happen. // { ".got", ".got.plt" } }; for_each(ALLOF(scoop_pairs), [this](const scoop_pairs_t pair) { auto it1=find_if(ALLOF(moveable_scoops), bind2nd(finder,pair.first)); auto it2=find_if(ALLOF(moveable_scoops), bind2nd(finder,pair.second)); // both exist, tie together. if(it1!=moveable_scoops.end() && it2!=moveable_scoops.end()) tied_scoops.insert(ScoopPair_t(*it1,*it2)); // first exists, rename for easier management later. else if(it1!=moveable_scoops.end() && it2==moveable_scoops.end()) (*it1)->setName(pair.first+" coalesced w/"+ pair.second); // second exists, rename for easier management later. else if(it1==moveable_scoops.end() && it2!=moveable_scoops.end()) (*it2)->setName(pair.first+" coalesced w/"+ pair.second); // or, none exists at all. }); } template <class T_Sym, class T_Rela, class T_Rel, class T_Dyn, class T_Extractor> void MoveGlobals_t<T_Sym,T_Rela,T_Rel,T_Dyn,T_Extractor>::HandleMemoryOperand(DecodedInstruction_t& disasm, const DecodedOperandVector_t::iterator the_arg, Instruction_t* insn, const DecodedOperandVector_t &the_arg_container) { // no mem arg. if(the_arg==the_arg_container.end()) { if(m_verbose) { cout << "Note: "<<hex<<" no memory op in:"; cout << insn->getBaseID()<<":"<<disasm.getDisassembly(); cout << endl; } return; } // shared objects don't need this, you have to use a pcrel addressing mode. if(!arg_has_relative(**the_arg) && exe_reader->isDLL()) { if(m_verbose) { cout << "Note: "<<hex<<" no dll-style address in:"; cout << insn->getBaseID()<<":"<<disasm.getDisassembly(); cout << endl; } return; } const auto small_memory_threshold= exe_reader->isDLL() ? 10 : 4096*10; auto to1 = (DataScoop_t*) NULL; // examine the memory operation to see if there's a pc-rel if ((*the_arg)->isMemory() && (*the_arg)->hasMemoryDisplacement() && (*the_arg)->getMemoryDisplacementEncodingSize() == 4 ) { auto rel_addr1 = (VirtualOffset_t)(*the_arg)->getMemoryDisplacement(); if (arg_has_relative(*(*the_arg))) rel_addr1 += insn->getDataBits().size(); to1 = DetectProperScoop(disasm, the_arg, insn, rel_addr1, false, the_arg_container); auto disp_offset = disasm.getMemoryDisplacementOffset(the_arg->get(),insn); auto disp_size = (*the_arg)->getMemoryDisplacementEncodingSize(); assert((0 < disp_offset) && (disp_offset <= (insn->getDataBits().size() - disp_size))); // skip if not found, executable, or not moveable. if (to1 && (to1->isExecuteable() || moveable_scoops.find(to1) == moveable_scoops.end())) { // do nothing, no log or action is necessary for pointers to code. if(m_verbose) { cout<<"Skipping (scoop exists, but exe scoop, or not moveable scoop) pcrel mem op in insn: " << hex << insn->getBaseID()<<":"<<disasm.getDisassembly()<<" to " << to1->getName()<<" (" <<hex<<to1->getStart()->getVirtualOffset()<<"-" <<hex<<to1->getEnd()->getVirtualOffset()<<")"<<endl; } } else if(to1) { // look for any pcrel relative relocs from fix_calls Relocation_t* pcrel_reloc=FindRelocationWithType(insn,"pcrel"); if(pcrel_reloc) { if(m_verbose) { cout<<"Setting pcrel mem op in insn: " << hex <<insn->getBaseID()<<":"<<disasm.getDisassembly()<<" to " << to1->getName()<<" (" <<hex<<to1->getStart()->getVirtualOffset()<<"-" <<hex<<to1->getEnd()->getVirtualOffset()<<")"<<endl; } pcrel_refs_to_scoops.insert({insn,to1}); } else { if(m_verbose) { cout<<"Absolute mem-op to scoop in insn: " << hex << insn->getBaseID()<<":"<<disasm.getDisassembly()<<" to " << to1->getName()<<" (" <<hex<<to1->getStart()->getVirtualOffset()<<"-" <<hex<<to1->getEnd()->getVirtualOffset()<<")"<<endl; } if(!is_noptr_table(to1)) absolute_refs_to_scoops.insert({insn,to1}); } } else if ( -small_memory_threshold < (int)rel_addr1 && (int)rel_addr1 < small_memory_threshold ) { if((0 != rel_addr1) && m_verbose) { cout << "Note: "<<hex<<rel_addr1<<" not declared address in (low addr thresh) :"; cout << insn->getBaseID()<<":"<<disasm.getDisassembly(); cout << endl; } } else { if ((0 != rel_addr1) && m_verbose) { cout << "Note: "<<hex<<rel_addr1<<" not declared address in (no scoop):"; cout << insn->getBaseID()<<":"<<disasm.getDisassembly(); cout << endl; } } } else { if(m_verbose) { cout << "Note: "<<hex<<" no address in:"; cout << insn->getBaseID()<<":"<<disasm.getDisassembly(); cout << endl; } } } template <class T_Sym, class T_Rela, class T_Rel, class T_Dyn, class T_Extractor> void MoveGlobals_t<T_Sym,T_Rela,T_Rel,T_Dyn,T_Extractor>::ApplyPcrelMemoryRelocation(Instruction_t* insn, DataScoop_t* to) { const auto disasmp=DecodedInstruction_t::factory(insn); const auto &disasm=*disasmp; auto operands=disasm.getOperands(); #if 1 // don't change instructions that reference re-pinned scoops. // This was necessary because we were not getting the zipr_unpin_plugin // to undo our changes to the instruction in the case of a re-pinned scoop. // That problem is fixed, but it is more efficient and safer to // avoid editing instructions that reference re-pinned scoops. if (moveable_scoops.find(to) == moveable_scoops.cend()) { if (m_verbose) { cout << "Avoiding editing of insn at " << hex << insn->getBaseID() << " after repinning scoop " << to->getName() << endl; } return; } #endif auto the_arg=find_memory_operand(operands); assert(the_arg!=operands.end()); unsigned int disp_offset=disasm.getMemoryDisplacementOffset(the_arg->get(),insn)/*the_arg->Memory.DisplacementAddr-disasm.EIP*/; unsigned int disp_size=(*the_arg)->getMemoryDisplacementEncodingSize() /*the_arg->Memory.DisplacementSize*/; Relocation_t* pcrel_reloc=FindRelocationWithType(insn,"pcrel"); pcrel_reloc->setWRT(to); // note about this case: the pcrel reloc already exists for the // case where an instruction is moving. // now the relocs WRT field indicates that the target might move too. // will have to edit push_relocs.zpi to handle this. assert(0<disp_offset && disp_offset<=(insn->getDataBits().size() - disp_size)); assert(disp_size==4); unsigned int new_disp=(*the_arg)->getMemoryDisplacement() /*the_arg->Memory.Displacement*/ - to->getStart()->getVirtualOffset(); insn->setDataBits(insn->getDataBits().replace(disp_offset, disp_size, (char*)&new_disp, disp_size)); } template <class T_Sym, class T_Rela, class T_Rel, class T_Dyn, class T_Extractor> void MoveGlobals_t<T_Sym,T_Rela,T_Rel,T_Dyn,T_Extractor>::ApplyAbsoluteMemoryRelocation(Instruction_t* insn, DataScoop_t* to) { const auto disasmp=DecodedInstruction_t::factory(insn); const auto &disasm=*disasmp; auto operands=disasm.getOperands(); #if 1 // don't change instructions that reference re-pinned scoops. // This was necessary because we were not getting the zipr_unpin_plugin // to undo our changes to the instruction in the case of a re-pinned scoop. // That problem is fixed, but it is more efficient and safer to // avoid editing instructions that reference re-pinned scoops. if (moveable_scoops.find(to) == moveable_scoops.cend()) { if (m_verbose) { cout << "Avoiding editing of insn at " << hex << insn->getBaseID() << " after repinning scoop " << to->getName() << endl; } return; } #endif auto the_arg = find_memory_operand(operands); unsigned int disp_offset=disasm.getMemoryDisplacementOffset(the_arg->get(),insn); unsigned int disp_size=(*the_arg)->getMemoryDisplacementEncodingSize(); assert(0<disp_offset && disp_offset<=insn->getDataBits().size() - disp_size); auto reloc=getFileIR()->addNewRelocation(insn,0, "absoluteptr_to_scoop",to); (void)reloc; // just giving to the ir assert(0<disp_offset && disp_offset<=(insn->getDataBits().size() - disp_size)); assert(disp_size==4); unsigned int new_disp=(*the_arg)->getMemoryDisplacement() /*the_arg->Memory.Displacement*/ - to->getStart()->getVirtualOffset(); insn->setDataBits(insn->getDataBits().replace(disp_offset, disp_size, (char*)&new_disp, disp_size)); } // See if STARS analyzed the instruction and determined which scoop it references. template <class T_Sym, class T_Rela, class T_Rel, class T_Dyn, class T_Extractor> DataScoop_t* MoveGlobals_t<T_Sym, T_Rela, T_Rel, T_Dyn, T_Extractor>::DetectAnnotationScoop(Instruction_t* insn) { if (!m_use_stars) return nullptr; const auto dgsr_it = deep_global_static_ranges->find(insn); const auto dgsr_found = dgsr_it != deep_global_static_ranges->end(); const auto sentinel_it = sentinels->find(insn); const auto is_sentinel = sentinel_it != sentinels->end(); auto ReferencedScoop = (DataScoop_t*)nullptr; if(dgsr_found && is_sentinel) { const auto StartAddr = dgsr_it->second; ReferencedScoop = findScoopByAddress(StartAddr); } return ReferencedScoop; } // end of DetectAnnotationScoop() template <class T_Sym, class T_Rela, class T_Rel, class T_Dyn, class T_Extractor> DataScoop_t* MoveGlobals_t<T_Sym,T_Rela,T_Rel,T_Dyn,T_Extractor>::DetectProperScoop(const DecodedInstruction_t& disasm, const DecodedOperandVector_t::iterator the_arg, Instruction_t* insn, VirtualOffset_t insn_addr, bool immed, const DecodedOperandVector_t &the_arg_container) { assert(insn); assert(immed || (the_arg != the_arg_container.end())); // immeds don't need an argument, but memory ops do. if (immed && (0 == insn_addr)) return NULL; // immed value of zero is not a scoop address const auto small_memory_threshold = exe_reader->isDLL() ? 10 : 4096 * 10; const auto ValidImmed = immed && (small_memory_threshold <= ((int)insn_addr)); auto ret = findScoopByAddress(insn_addr); // so far, we haven't run into any problems with not finding a scoop. we could later. if (!ret) { // check for things that _just_ run off the end of a scoop. for (auto i = 0; (i < 8) && (ret == NULL); i++) ret = findScoopByAddress(insn_addr - i); // check for things that just miss the beginning of a scoop for (auto i = 0; (i < 8) && (ret == NULL); i++) ret = findScoopByAddress(insn_addr + i); } // See if STARS analyzed the instruction and determined which scoop it references. const auto retSTARS = (immed && (!ValidImmed)) ? (DataScoop_t*)nullptr : DetectAnnotationScoop(insn); if (!ret) { if (nullptr != retSTARS) { cout << "Detected proper scoop using annotation, not using after DetectProperScoop failure for insn at " << hex << insn->getBaseID() << endl; } return ret; } /* check to see if it's directly pointing at an elftable that isn't allowed to have pointers */ if (is_noptr_table(ret)) { /* it's an elftable, so we don't need to look so hard because */ /* we probably aren't pointing to an elf table from an instruction */ /* find middle of table */ const auto mid_of_table = (ret->getStart()->getVirtualOffset() / 2) + (ret->getEnd()->getVirtualOffset() / 2); /* look forward if above middle, else look backwards */ const auto op = (insn_addr < mid_of_table) ? [](const VirtualOffset_t i, const VirtualOffset_t j) { return i - j; } : [](const VirtualOffset_t i, const VirtualOffset_t j) { return i + j; } ; /* start at begin/end of table depending on direction */ const auto addr = (insn_addr < mid_of_table) ? ret->getStart()->getVirtualOffset() : ret->getEnd()->getVirtualOffset() ; /* scan 128 bytes looking for a relevant scoop */ const auto thres = 128; for (auto i = 1; i < thres; i++) { /* check what's here */ auto candidate = findScoopByAddress(op(addr, i)); if (candidate != NULL) return candidate; } /* didn't find anything */ } /* if elftable */ /* Not an elf table use conservative and/or aggressive heuristics*/ ret = DetectProperScoop_ConsiderEndOfPrev(disasm, the_arg, insn, insn_addr, immed, ret, the_arg_container); if (!aggressive) ret = DetectProperScoop_ConsiderStartOfNext(disasm, the_arg, insn, insn_addr, immed, ret, the_arg_container); if (nullptr != retSTARS) { if (nullptr == ret) { // ret = retSTARS; // Dangerous to use; e.g. mov [rdi+0x200],rax will cause edit of 0x200 because RDI was resolved by STARS to a scoop address cout << "Detected proper scoop using annotation, not using after DetectProperScoop final failure for insn at " << hex << insn->getBaseID() << endl; } else if (retSTARS != ret) { // We have two different non-null choices. We will tie the two scoops // together if they are adjacent, and pin them both otherwise. if (AreScoopsAdjacent(ret, retSTARS)) // tie adjacent scoops { cout << "Tieing adjacent scoops due to STARS vs. DetectProperScoop conflict for insn at " << hex << insn->getBaseID() << endl; if (ret->getStart()->getVirtualOffset() < retSTARS->getStart()->getVirtualOffset()) tied_scoops.insert({ret, retSTARS}); else tied_scoops.insert({retSTARS, ret}); } else // not adjacent; must pin { cout << "Pinning non-adjacent scoops due to STARS vs. DetectProperScoop conflict for insn at " << hex << insn->getBaseID() << endl; if(!is_elftable(ret)) moveable_scoops.erase(ret); if(!is_elftable(retSTARS)) moveable_scoops.erase(retSTARS); } } } return ret; } // end of DetectProperScoop() template <class T_Sym, class T_Rela, class T_Rel, class T_Dyn, class T_Extractor> DataScoop_t* MoveGlobals_t<T_Sym,T_Rela,T_Rel,T_Dyn,T_Extractor>::DetectProperScoop_ConsiderStartOfNext( const DecodedInstruction_t& disasm, const DecodedOperandVector_t::iterator mem_arg, Instruction_t* insn, VirtualOffset_t insn_addr, bool immed, DataScoop_t* candidate_scoop, const DecodedOperandVector_t &mem_arg_container ) { assert(immed || mem_arg!=mem_arg_container.end()); // immeds don't need an argument, but memory ops do. const auto is_lea=disasm.getMnemonic() == string("lea"); const auto consider_multiple_sizes= is_lea || immed; auto strides= consider_multiple_sizes ? set<int>({1,2,4,8}) : set<int>({ (int)(*mem_arg)->getArgumentSizeInBytes() }); // get other strides from the containing function if(insn->getFunction()) { for(auto func_insn : insn->getFunction()->getInstructions()) { const auto dp=DecodedInstruction_t::factory(func_insn); const auto &d=*dp; auto potential_stride=0; if( d.getMnemonic()=="add" || d.getMnemonic()=="sub") { potential_stride=d.getImmediate(); } if(d.getMnemonic()=="lea") { potential_stride=d.getOperand(1)->getMemoryDisplacement(); } if(abs(potential_stride)<500 && potential_stride!=0) { strides.insert(potential_stride); strides.insert(-potential_stride); } }; } const auto stride_multipliers= set<int>({-1,1}); const auto contains_base_reg = mem_arg != mem_arg_container.end() && (*mem_arg)->hasBaseRegister(); const auto contains_index_reg = mem_arg != mem_arg_container.end() && (*mem_arg)->hasIndexRegister(); const auto contains_reg = contains_base_reg || contains_index_reg; const auto memory_access = mem_arg!=mem_arg_container.end() && !is_lea; const auto is_direct_memory_access = memory_access && !contains_reg; // check for a direct memory access if(is_direct_memory_access) { return candidate_scoop; } // calculate each offset=stride*multiplier pair auto candidate_offsets=set<int>(); for(auto stride : strides) { for(auto multiplier : stride_multipliers) { candidate_offsets.insert(stride*multiplier); }; }; // how to tie two scoops auto insert_scoop_pair=[&](DataScoop_t* a, DataScoop_t* b, int i, int offset) { const auto tied_scoop_pair = ScoopPair_t(a,b) ; assert(tied_scoop_pair.first->getEnd()->getVirtualOffset()+1 == tied_scoop_pair.second->getStart()->getVirtualOffset()); tied_scoops.insert(tied_scoop_pair); cout<<" Tieing scoops "<<tied_scoop_pair.first->getName()<<" and "<<tied_scoop_pair.second->getName()<<" for i="<<dec<<i<<" offset="<<offset<<endl; ties_for_folded_constants++; }; // how to decide if a scoop at offset i should be tied. // no scoop -> no tie // un-tie-able scoop -> no tie // else tie auto should_tie=[&](const int i, DataScoop_t* prev_scoop) -> DataScoop_t* { DataScoop_t *this_scoop=findScoopByAddress(insn_addr+i); // no scoop at this addr? if(this_scoop==nullptr) return nullptr; // un-tie-able scoop at this addr? if(is_noptr_table(this_scoop)) return nullptr; // if both scoops are already pinned, no reason to tie. const auto is_prev_moveable = moveable_scoops.find(prev_scoop)!=moveable_scoops.end(); const auto is_this_moveable = moveable_scoops.find(this_scoop)!=moveable_scoops.end(); if(!is_prev_moveable && !is_this_moveable) return nullptr; // else, tie return this_scoop; }; // check each offset for a scoop that needings tieing tot his one. for(auto offset : candidate_offsets) { assert(offset!=0); auto candidate_offset_scoop=findScoopByAddress(insn_addr+offset) ; // check to see if the offset is in a different scoop if(candidate_scoop != candidate_offset_scoop) { // yes, therefore we have to tie all scoops between the start and end together. // stop if there's an untieable scoop in the way. auto prev_scoop=candidate_scoop; if(offset < 0 ) { for(auto i=(int)-1;i>=offset; i--) { auto this_scoop=should_tie(i,prev_scoop); if(this_scoop) { if(this_scoop!=prev_scoop) insert_scoop_pair(this_scoop,prev_scoop, i, offset); prev_scoop=this_scoop; } else break; } } else { for(auto i=(int)1;i<=offset; i++) { auto this_scoop=should_tie(i,prev_scoop); if(this_scoop) { if(this_scoop!=prev_scoop) insert_scoop_pair(prev_scoop,this_scoop, i, offset); prev_scoop=this_scoop; } else break; } } } }; return candidate_scoop; } template <class T_Sym, class T_Rela, class T_Rel, class T_Dyn, class T_Extractor> DataScoop_t* MoveGlobals_t<T_Sym,T_Rela,T_Rel,T_Dyn,T_Extractor>::DetectProperScoop_ConsiderEndOfPrev( const DecodedInstruction_t& disasm, const DecodedOperandVector_t::iterator the_arg, Instruction_t* insn, VirtualOffset_t insn_addr, bool immed, DataScoop_t* ret, const DecodedOperandVector_t &the_arg_container ) { // possibility for future work: identify cases where // [addr+rbx*8] that came from something like =a[i-1]. And addr==a[-1]. // for now, memory operands that actually access memory, there's no additional analysis needed if(!immed && disasm.getMnemonic()!=string("lea")) // this should filter out cmp, move, test, add, with a memory operation return ret; // now we have an immediate or an lea (i.e., no cmp reg, [mem] operations) // that's pointing to a scoop. Let's check if it's a boundary between two scoops if(insn_addr!=ret->getStart()->getVirtualOffset()) // it's not, so just continue. return ret; // now look to see if there's a scoop regsitered that abuts this scoop; DataScoop_t *scoop_for_prev=findScoopByAddress(insn_addr-1); // if not found, we know we aren't in a boundary case. if(!scoop_for_prev) return ret; /* check to see if the immediate next instruction dereferences the destination of an lea. */ Instruction_t* next_insn=insn->getFallthrough(); if(next_insn == NULL) next_insn=insn->getTarget(); if(next_insn && disasm.getMnemonic() == string("lea")) { const auto lea_disasmp=DecodedInstruction_t::factory(insn); const auto &lea_disasm=*lea_disasmp;; string dstreg=lea_disasm.getOperand(0)->getString(); const auto next_disasmp=DecodedInstruction_t::factory(next_insn); const auto &next_disasm=*next_disasmp; auto memarg_container=next_disasm.getOperands(); const auto memarg=find_memory_operand(memarg_container); // if we found a memory operation that uses the register, with no indexing, then conclude that // we must access the variable after the address (not the variable before the address) // if(memarg && string(next_disasm.Instruction.Mnemonic)!="lea " && string(memarg->ArgMnemonic)==dstreg ) if(memarg!=memarg_container.end() && next_disasm.getMnemonic()!="lea" && (*memarg)->getString()==dstreg ) return ret; } // if we're in a function // check that function for other references to scoop_for_prev if(insn->getFunction()) { auto found_insn_it=find_if( ALLOF(insn->getFunction()->getInstructions()), [&](Instruction_t* func_insn) { // disassemble instruction const auto func_insn_disasmp=DecodedInstruction_t::factory(func_insn); const auto &func_insn_disasm=*func_insn_disasmp; auto func_insn_disasm_operands=func_insn_disasm.getOperands(); // enter instructions have 2 immediates, so we can't just "getImmediate()" if(func_insn_disasm.getMnemonic()=="enter") return false; // check the immediate // if(getFileIR()->findScoop(func_insn_disasm.Instruction.Immediat) == scoop_for_prev) if(scoop_for_prev->getStart()->getVirtualOffset() <= (VirtualOffset_t)func_insn_disasm.getImmediate() && (VirtualOffset_t)func_insn_disasm.getImmediate() <= scoop_for_prev->getEnd()->getVirtualOffset()) return true; // return from lamba that we found an insn. // don't bother with the memory check unless we're an LEA //if(func_insn_disasm.Instruction.Mnemonic!=string("lea ")) if(func_insn_disasm.getMnemonic()!=string("lea")) return false; // check the memory -- find the argument that's the mem ref; const auto the_arg=find_memory_operand(func_insn_disasm_operands); if(the_arg!=func_insn_disasm_operands.end()) { // see if the lea has a scoop reference. VirtualOffset_t addr=(*the_arg)->getMemoryDisplacement(); if(arg_has_relative(*(*the_arg))) addr+=insn->getDataBits().size(); if(findScoopByAddress(addr) == scoop_for_prev) return true; // return from lamba } // not found in this insn return false; // lambda return }); // no reference to prev_scoop found, just return; if(found_insn_it==insn->getFunction()->getInstructions().end()) { return ret; } } // if we make it this far, we note that a single function has sketchy (aka address-generating) references // to both scoop_for_prev and ret; // in this case, we need to make keep these two scoops together since we can't tell which way the sketchy ref's go. // for now, just record the sketchy refs. cout<<"Boundary note: instruction "<<insn->getBaseID()<<":"<<disasm.getDisassembly()<<" has immed/lea that points at boundary case."; if(insn->getFunction()) cout<<" In "<<insn->getFunction()->getName()<<"."; cout<<endl; cout<<"Keep together "<< scoop_for_prev->getName()<<" ("<<hex<< scoop_for_prev->getStart()->getVirtualOffset()<<"-"<<scoop_for_prev->getEnd()->getVirtualOffset()<<") and "<< ret->getName()<<" ("<<hex<< ret->getStart()->getVirtualOffset()<<"-"<<ret->getEnd()->getVirtualOffset()<<")"<<endl; tied_scoops.insert(ScoopPair_t(scoop_for_prev,ret)); return ret; } template <class T_Sym, class T_Rela, class T_Rel, class T_Dyn, class T_Extractor> void MoveGlobals_t<T_Sym,T_Rela,T_Rel,T_Dyn,T_Extractor>::ApplyImmediateRelocation(Instruction_t *insn, DataScoop_t* to) { const auto disasmp=DecodedInstruction_t::factory(insn); const auto &disasm=*disasmp; VirtualOffset_t rel_addr2=disasm.getImmediate(); // Instruction.Immediat; #if 1 // don't change instructions that reference re-pinned scoops. // This was necessary because we were not getting the zipr_unpin_plugin // to undo our changes to the instruction in the case of a re-pinned scoop. // That problem is fixed, but it is more efficient and safer to // avoid editing instructions that reference re-pinned scoops. if (moveable_scoops.find(to) == moveable_scoops.cend()) { if (m_verbose) { cout << "Avoiding editing of insn at " << hex << insn->getBaseID() << " after repinning scoop " << to->getName() << endl; } return; } #endif getFileIR()->addNewRelocation(insn,0, "immedptr_to_scoop", to); // fixme: insn bits changed here assert(strtoumax(disasm.getOperand(1)->getString().c_str(), NULL, 0) == rel_addr2); VirtualOffset_t new_addr = rel_addr2 - to->getStart()->getVirtualOffset(); assert(4 < insn->getDataBits().size()); insn->setDataBits(insn->getDataBits().replace(insn->getDataBits().size()-4, 4, (char*)&new_addr, 4)); cout<<"Non-Overlapping_Globals::ApplyImmediateReloc::Setting "<<hex<<insn->getBaseID()<<" to "<<insn->getDisassembly()<<endl; } template <class T_Sym, class T_Rela, class T_Rel, class T_Dyn, class T_Extractor> void MoveGlobals_t<T_Sym,T_Rela,T_Rel,T_Dyn,T_Extractor>::HandleImmediateOperand(const DecodedInstruction_t& disasm, const DecodedOperandVector_t::iterator the_arg, Instruction_t* insn) { // shared objects don't need this, you have to use a pcrel addressing mode. if(exe_reader->isDLL()) { return; } const int small_memory_threshold= exe_reader->isDLL() ? 10 : 4096*10; // enter instructions have 2 immediates, so we can't just "getImmediate()" if(disasm.getMnemonic()=="enter") return; VirtualOffset_t rel_addr2=disasm.getImmediate(); auto operands=disasm.getOperands(); DataScoop_t *to2=DetectProperScoop(disasm, operands.end(), insn, rel_addr2, true, operands); // skip if not found, executable, or not moveable. if( to2 && (to2->isExecuteable() || moveable_scoops.find(to2) == moveable_scoops.end())) { // do nothing, no log or action is necessary for (potential) pointers to code or // (potential) pointers to non-moveable data. } else if(to2) { // there's no need to find pointers in other types of instructions, // such as mul or vfmasubadd231 (yes, that's a real instruction on x86) // note: yes other instructions may have a memory operand with a pointer, but that's handled above. // this is for instruction's immediate fields, not their memory operand's displacement. // // compares, tests are often used because the compiler strength reduces. // moves are used to load addresses into a register. // adds are used to load addresses plus an offset into a register. // here's an example where sub is used with a pointer: // // DegenCount[strchr(Alphabet,iupac)-Alphabet] = ... // // 0x0000000000402a99 <+25>: call 0x401620 <strchr@plt> // 0x0000000000402a9e <+30>: mov rbp <- rax // 0x0000000000402aa1 <+33>: mov rdi <- rbx // 0x0000000000402aa4 <+36>: sub rbp <- 0x65b500 # note: constant is a poitner here! // 0x0000000000402aab <+43>: eax <- ... // 0x0000000000402ab0 <+48>: mov DWORD PTR [rbp*4+0x65b520] <- eax if(disasm.getMnemonic() == string("mov") || disasm.getMnemonic() == string("cmp") || disasm.getMnemonic() == string("test") || disasm.getMnemonic() == string("add") || disasm.getMnemonic() == string("sub") ) { if(m_verbose) { cout<<"Found non-mem ref in insn: "<<insn->getBaseID()<<":"<<disasm.getDisassembly()<<" to " << to2->getName() <<"(" <<hex<<to2->getStart()->getVirtualOffset()<<"-" <<hex<<to2->getEnd()->getVirtualOffset()<<")"<<endl; } if(!is_noptr_table(to2)) immed_refs_to_scoops.insert({insn,to2}); } } else { if ((int)rel_addr2 < -small_memory_threshold || (int) rel_addr2 > small_memory_threshold || m_verbose) { if ((0 != rel_addr2) && m_verbose) { cout << "Note: " << hex << rel_addr2 << " not declared address in:"; cout << insn->getBaseID() << ":" << disasm.getDisassembly(); cout << endl; } } } } // put in links between scoops and any references to them. template <class T_Sym, class T_Rela, class T_Rel, class T_Dyn, class T_Extractor> void MoveGlobals_t<T_Sym,T_Rela,T_Rel,T_Dyn,T_Extractor>::FindInstructionReferences() { for(auto insn : getFileIR()->getInstructions()) { auto disasmp=DecodedInstruction_t::factory(insn); auto &disasm=*disasmp; auto disasm_operands=disasm.getOperands(); // find memory arg. const auto the_arg=find_memory_operand(disasm_operands); if(m_verbose) cout<<"Considering "<<hex<<insn->getBaseID()<<":"<<disasm.getDisassembly()<<endl; HandleMemoryOperand(disasm,the_arg,insn, disasm_operands); HandleImmediateOperand(disasm,the_arg,insn); } } template <class T_Sym, class T_Rela, class T_Rel, class T_Dyn, class T_Extractor> void MoveGlobals_t<T_Sym,T_Rela,T_Rel,T_Dyn,T_Extractor>::ApplyDataRelocation(DataScoop_t *from, unsigned int offset, DataScoop_t* to) { assert(to && from); const char* data=from->getContents().c_str(); unsigned int byte_width=getFileIR()->getArchitectureBitWidth()/8; VirtualOffset_t val=(VirtualOffset_t)NULL; if(byte_width==4) val=*(int*)&data[offset]; else if(byte_width==8) val=*(long long*)&data[offset]; else assert(0); auto reloc=getFileIR()->addNewRelocation(from,offset, "dataptr_to_scoop", to); (void)reloc; // just giving to ir VirtualOffset_t newval=val-to->getStart()->getVirtualOffset(); // auto str=from->getContents(); // create new value for pointer. if(byte_width==4) { const auto intnewval=(unsigned int)newval; // 64->32 narrowing OK. from->replaceBytes(offset, string(reinterpret_cast<const char*>(&intnewval), byte_width)); } else if(byte_width==8) { from->replaceBytes(offset,string(reinterpret_cast<const char*>(&newval),byte_width)); } else assert(0); // from->setContents(str); } // // check if val is a pointer or part of a string that mimics a pointer // static inline bool is_part_of_string(VirtualOffset_t val, const DataScoop_t* from, const DataScoop_t* to, int offset) { assert(from && to); // locate strings that look like pointers but aren't. e.g.: "ion\0" and "ren\0". Note that both are null terminated. // this is a problem on 64-bit code because we screw up the string. // note: the most sigificant byte is 0, and the lower 3 signfiicant bytes are printable. // the least significant byte is special. In a valid pointer, it's almost always 00 or 01 for 64-bit code or shared libraries, // and 0x08 0x09 for 32-bit main executables. Very very rarely is it anything else. // however, for 0x01, 0x08, and 0x09 aren't printable, so we don't confuse these bytes in a string for an address and we don't need to detect this. if ( ((val >> 24) & 0xff) != 0 ) // check for non-0 return false; if ( !isprint(((val >> 16) & 0xff))) // and 3 printable characters. return false; if ( !isprint(((val >> 8) & 0xff))) return false; if ( !isprint(((val >> 0) & 0xff))) return false; // number of bytes that must precede the pointer and be string bytes to disambiguate a string's end from a pointer. const int string_preheader_size=4; // if we dont' have enough bytes of preheader, skip it. if( offset < string_preheader_size ) return false; // check each byte preceeding the candidate pointer to see if it's printable. for(auto i=0;i<string_preheader_size;i++) { if(i>offset) return false; unsigned char b=from->getContents()[offset-i]; if(!isprint(b)) return false; } #if 0 // we found enough string chars before the (candidate) pointer value, so we think that a string is here, not a pointer. if(m_verbose) { cout<<"Found string as non-ref "<<hex<<val<<" at "<<from->getName()<<"+"<<offset<<" (" <<hex<<from->getStart()->getVirtualOffset()<<"-" <<hex<<from->getEnd()->getVirtualOffset()<<") to " <<to->getName()<<" (" <<hex<<to->getStart()->getVirtualOffset()<<"-" <<hex<<to->getEnd()->getVirtualOffset()<<")"<<endl; } #endif return true; } // put in links between scoops and any references to them. template <class T_Sym, class T_Rela, class T_Rel, class T_Dyn, class T_Extractor> void MoveGlobals_t<T_Sym,T_Rela,T_Rel,T_Dyn,T_Extractor>::FindDataReferences() { unsigned int byte_width=getFileIR()->getArchitectureBitWidth()/8; typedef function<void (DataScoop_t*)> ScannerFunction_t; auto read_bytewidth=[&](const char* data, const int i) -> long long { auto val=(long long)0; if(byte_width==4) val=*(int*)&data[i]; else if(byte_width==8) val=*(long long*)&data[i]; else assert(0); return val; }; ScannerFunction_t got_scanner=[&](DataScoop_t* scoop) { // got scanner doesn't scan data section for shared objects since they can't have a constant address if(exe_reader->isDLL()) return; auto data=scoop->getContents().c_str(); auto len=scoop->getContents().size(); for ( auto i=0u; i+byte_width-1<len; i+=byte_width) { const auto val=read_bytewidth(data,i); auto to=findScoopByAddress(val); if(to) { if(m_verbose) { cout<<"Found ref "<<hex<<val<<" at "<<scoop->getName()<<"+"<<i<<" (" <<hex<<scoop->getStart()->getVirtualOffset()<<"-" <<hex<<scoop->getEnd()->getVirtualOffset()<<") to " <<to->getName()<<" (" <<hex<<to->getStart()->getVirtualOffset()<<"-" <<hex<<to->getEnd()->getVirtualOffset()<<")"<<endl; } data_refs_to_scoops.insert({scoop,i,to}); } } }; ScannerFunction_t default_scanner=[&](DataScoop_t* scoop) { // default scanner doesn't scan data section for shared objects since they can't have a constant address if(exe_reader->isDLL()) return; auto data=scoop->getContents().c_str(); auto len=scoop->getContents().size(); // try not to overrun the array for ( auto i=0u; i+byte_width-1<len; i+=byte_width) { auto val=read_bytewidth(data,i); auto to=findScoopByAddress(val); if(to) { auto aggressive_qualify_for_moving = [this](const DataScoop_t* from, DataScoop_t* &to, bool &move_ok, bool &disqualifies_to, const VirtualOffset_t addr, unsigned int offset_in_scoop ) -> void { move_ok=true; disqualifies_to=false; if( !to->isExecuteable() && moveable_scoops.find(to) != moveable_scoops.end() && !is_part_of_string(addr,from,to,offset_in_scoop) ) { return; } move_ok=false; }; auto qualify_for_moving = [this](const DataScoop_t* from, DataScoop_t* &to, bool &move_ok, bool &disqualifies_to, const VirtualOffset_t addr, unsigned int offset_in_scoop ) -> void { move_ok=true; disqualifies_to=false; // if points at executable scoop, we aren't doing that here! if(to->isExecuteable()) { move_ok=false; disqualifies_to=false; return ; } // if not moveable, we aren't doing that here. if ( moveable_scoops.find(to) == moveable_scoops.end()) { move_ok=false; disqualifies_to=false; return ; } /* the above worked ok-ish, but not great. trying this method to be more conservative */ { move_ok=false; disqualifies_to=true; return ; } /* // if this constant appears to be part of a string, skip it! if(is_part_of_string(addr,from,to,offset_in_scoop)) { move_ok=false; disqualifies_to=false; return ; } // very few variables start at an address that ends in 0x000 and often address-looking constants do // if we see such an address, pin-and-win. if ( (addr&0xfff) == 0x000 && addr==to->getStart()->getVirtualOffset()) { move_ok=false; disqualifies_to=true; return ; } // if we point at the start of a scoop, it's OK to move. if(addr==to->getStart()->getVirtualOffset()) { move_ok=true; disqualifies_to=false; return ; } // if it points near a scoop, but not directly at it, it's hard to tell if it's moveable or not if(abs((long)addr-(long)to->getStart()->getVirtualOffset()) < 16 ) { move_ok=false; disqualifies_to=true; return ; } // else, it's pointing in the middle of a scoop, so it's probably not a // pointer at all. { move_ok=false; disqualifies_to=false; return ; } */ }; auto move_ok=false; auto disqualifies_to=false; if(aggressive) aggressive_qualify_for_moving(scoop, to,move_ok,disqualifies_to,val, i); else qualify_for_moving(scoop, to,move_ok,disqualifies_to,val, i); if(move_ok) { if(m_verbose) { cout<<"Found ref "<<hex<<val<<" at "<<scoop->getName()<<"+"<<i<<" (" <<hex<<scoop->getStart()->getVirtualOffset()<<"-" <<hex<<scoop->getEnd()->getVirtualOffset()<<") to " <<to->getName()<<" (" <<hex<<to->getStart()->getVirtualOffset()<<"-" <<hex<<to->getEnd()->getVirtualOffset()<<")"<<endl; } // put those bytes back in the string. //ApplyDataRelocations(*sit,i,to); data_refs_to_scoops.insert({scoop,i,to}); } else { if(m_verbose) { cout<<"Found ref-looking-constant "<<hex<<val<<" at "<<scoop->getName()<<"+"<<i<<" (" <<hex<<scoop->getStart()->getVirtualOffset()<<"-" <<hex<<scoop->getEnd()->getVirtualOffset()<<") which would otherwise be to " <<to->getName()<<" (" <<hex<<to->getStart()->getVirtualOffset()<<"-" <<hex<<to->getEnd()->getVirtualOffset()<<")"<<endl; } } if(disqualifies_to) { if(!is_elftable(to)) { if(m_verbose) { cout<<"Ref-looking-constant "<<hex<<val<<" at "<<scoop->getName()<<"+"<<i<<" (" <<hex<<scoop->getStart()->getVirtualOffset()<<"-" <<hex<<scoop->getEnd()->getVirtualOffset()<<") is inconclusive. Repinning " <<to->getName()<<" (" <<hex<<to->getStart()->getVirtualOffset()<<"-" <<hex<<to->getEnd()->getVirtualOffset()<<")"<<endl; } moveable_scoops.erase(to); } else { if(m_verbose) { cout<<"Ref-looking-constant "<<hex<<val<<" at "<<scoop->getName()<<"+"<<i<<" (" <<hex<<scoop->getStart()->getVirtualOffset()<<"-" <<hex<<scoop->getEnd()->getVirtualOffset()<<") is inconclusive. Not repinning because is elftable " <<to->getName()<<" (" <<hex<<to->getStart()->getVirtualOffset()<<"-" <<hex<<to->getEnd()->getVirtualOffset()<<")"<<endl; } } } } else { if((0 != val) && m_verbose) { cout<<"Constant "<<hex<<val<<" at "<<scoop->getName()<<"+"<<i<<" (" <<hex<<scoop->getStart()->getVirtualOffset()<<"-" <<hex<<scoop->getEnd()->getVirtualOffset()<<") doesn't point at scoop."<<endl; } } } }; ScannerFunction_t dynsym_scanner=[&](DataScoop_t* scoop) { const char* data=scoop->getContents().c_str(); unsigned int len=scoop->getContents().size(); T_Sym* symptr=(T_Sym*)data; const char* end=data+len; while((const char*)symptr<end) { VirtualOffset_t val=symptr->st_value; DataScoop_t *to=findScoopByAddress(val); if(to) { unsigned int offset=(unsigned int)((VirtualOffset_t)symptr)-((VirtualOffset_t)data); offset+=((VirtualOffset_t)&symptr->st_value)-(VirtualOffset_t)symptr; if(m_verbose) { cout<<"Found dynsym:st_value ref "<<hex<<val<<" at "<<scoop->getName()<<"+"<<offset<<" (" <<hex<<scoop->getStart()->getVirtualOffset()<<"-" <<hex<<scoop->getEnd()->getVirtualOffset()<<") to " <<to->getName()<<" (" <<hex<<to->getStart()->getVirtualOffset()<<"-" <<hex<<to->getEnd()->getVirtualOffset()<<")"<<endl; } data_refs_to_scoops.insert({scoop,offset,to}); } symptr++; // next symbol } }; ScannerFunction_t rel_scanner=[&](DataScoop_t* scoop) { const char* data=scoop->getContents().c_str(); unsigned int len=scoop->getContents().size(); T_Rela * symptr=(T_Rela*)data; const char* end=data+len; while((const char*)symptr<end) { // handle offset field { VirtualOffset_t val=symptr->r_offset; DataScoop_t *to=findScoopByAddress(val); if(to) { unsigned int offset=(unsigned int)((VirtualOffset_t)symptr)-((VirtualOffset_t)data); offset+=((VirtualOffset_t)&symptr->r_offset)-(VirtualOffset_t)symptr; if(m_verbose) { cout<<"Found rela:r_offset ref "<<hex<<val<<" at "<<scoop->getName()<<"+"<<offset<<" (" <<hex<<scoop->getStart()->getVirtualOffset()<<"-" <<hex<<scoop->getEnd()->getVirtualOffset()<<") to " <<to->getName()<<" (" <<hex<<to->getStart()->getVirtualOffset()<<"-" <<hex<<to->getEnd()->getVirtualOffset()<<")"<<endl; } data_refs_to_scoops.insert({scoop,offset,to}); } } symptr++; // next symbol } }; ScannerFunction_t rela_scanner=[&](DataScoop_t* scoop) { const char* data=scoop->getContents().c_str(); unsigned int len=scoop->getContents().size(); T_Rela * symptr=(T_Rela*)data; const char* end=data+len; while((const char*)symptr<end) { // handle addend field { VirtualOffset_t val=symptr->r_addend; DataScoop_t *to=findScoopByAddress(val); if(to) { unsigned int offset=(unsigned int)((VirtualOffset_t)symptr)-((VirtualOffset_t)data); offset+=((VirtualOffset_t)&symptr->r_addend)-(VirtualOffset_t)symptr; if(m_verbose) { cout<<"Found rela:r_added ref "<<hex<<val<<" at "<<scoop->getName()<<"+"<<offset<<" (" <<hex<<scoop->getStart()->getVirtualOffset()<<"-" <<hex<<scoop->getEnd()->getVirtualOffset()<<") to " <<to->getName()<<" (" <<hex<<to->getStart()->getVirtualOffset()<<"-" <<hex<<to->getEnd()->getVirtualOffset()<<")"<<endl; } data_refs_to_scoops.insert({scoop,offset,to}); } } // handle offset field { VirtualOffset_t val=symptr->r_offset; DataScoop_t *to=findScoopByAddress(val); if(to) { unsigned int offset=(unsigned int)((VirtualOffset_t)symptr)-((VirtualOffset_t)data); offset+=((VirtualOffset_t)&symptr->r_offset)-(VirtualOffset_t)symptr; if(m_verbose) { cout<<"Found rela:r_offset ref "<<hex<<val<<" at "<<scoop->getName()<<"+"<<offset<<" (" <<hex<<scoop->getStart()->getVirtualOffset()<<"-" <<hex<<scoop->getEnd()->getVirtualOffset()<<") to " <<to->getName()<<" (" <<hex<<to->getStart()->getVirtualOffset()<<"-" <<hex<<to->getEnd()->getVirtualOffset()<<")"<<endl; } data_refs_to_scoops.insert({scoop,offset,to}); } } symptr++; // next symbol } }; ScannerFunction_t dynamic_scanner=[&](DataScoop_t* scoop) { const auto data=scoop->getContents().c_str(); const auto len=scoop->getContents().size(); auto symptr=(T_Dyn*)data; const char* end=data+len; while((const char*)symptr<end) { switch(symptr->d_tag) { case DT_INIT_ARRAY: case DT_FINI_ARRAY: case DT_GNU_HASH: case DT_STRTAB: case DT_SYMTAB: case DT_PLTGOT: case DT_JMPREL: case DT_RELA: case DT_VERNEED: case DT_VERSYM: { const auto val=symptr->d_un.d_val; auto *to=findScoopByAddress(val); if(to) { auto offset=(unsigned int) (((VirtualOffset_t)symptr)-((VirtualOffset_t)data)); offset+=((VirtualOffset_t)&symptr->d_un.d_val)-(VirtualOffset_t)symptr; if(m_verbose) { cout<<"Found .dynamic:d_val ref "<<hex<<val<<" at "<<scoop->getName()<<"+"<<offset<<" (" <<hex<<scoop->getStart()->getVirtualOffset()<<"-" <<hex<<scoop->getEnd()->getVirtualOffset()<<") to " <<to->getName()<<" (" <<hex<<to->getStart()->getVirtualOffset()<<"-" <<hex<<to->getEnd()->getVirtualOffset()<<")"<<endl; } data_refs_to_scoops.insert({scoop,offset,to}); } break; } default: // do nothing break; } symptr++; // next symbol } }; // special scanners for special sections const struct scoop_scanners_t { string name; ScannerFunction_t scanner_fn; } scoop_scanners[] = { { ".dynsym", dynsym_scanner }, { ".got", got_scanner }, { ".got.plt", got_scanner }, { ".rel.dyn", rel_scanner }, { ".rel.plt", rel_scanner }, { ".rel.dyn coalesced w/.rel.plt", rel_scanner }, { ".rela.dyn", rela_scanner }, { ".rela.plt", rela_scanner }, { ".rela.dyn coalesced w/.rela.plt", rela_scanner }, { ".dynamic", dynamic_scanner } }; // main algorithm: apply the right scanner for each scoop for_each(ALLOF(getFileIR()->getDataScoops()), [&](DataScoop_t* scoop) { auto scanner=find_if(ALLOF(scoop_scanners), [&](const scoop_scanners_t scanner) { return scanner.name==scoop->getName(); }); if(scanner!=end(scoop_scanners)) scanner->scanner_fn(scoop); else default_scanner(scoop); }); } template <class T_Sym, class T_Rela, class T_Rel, class T_Dyn, class T_Extractor> void MoveGlobals_t<T_Sym,T_Rela,T_Rel,T_Dyn,T_Extractor>::FilterAndCoalesceTiedScoops() { const auto is_in_dont_coalesce_scoops = [](const DataScoop_t* to_find) -> bool { const string dont_coalesce_scoops[] = { ".dynamic", ".jcr" }; const auto a_binder = bind1st(finder, to_find); const auto it=find_if(ALLOF(dont_coalesce_scoops), a_binder); return (it!=end(dont_coalesce_scoops)); }; // step 1: find everything that's tied to a pinned scoop and pin it. // repeat until no changes. bool changed=true; while(changed) { changed=false; for(auto it=tied_scoops.begin(); it!=tied_scoops.end(); /* nop */) { auto current=it++; const ScoopPair_t& p=*current; DataScoop_t* s1=p.first; DataScoop_t* s2=p.second; bool s1_moveable=contains(moveable_scoops, s1); bool s2_moveable=contains(moveable_scoops, s2); if(is_in_dont_coalesce_scoops(s1) || is_in_dont_coalesce_scoops(s2)) { cout<<"Skipping coalesce of "<<s1->getName()<<" and "<<s2->getName()<<endl; tied_scoops.erase(current); continue; } if(s1_moveable && s2_moveable) { // do nothing if they're both unpinned. tied_unpinned++; } else if(s1_moveable) { tied_pinned++; // s1 is pinned to an unmoveable, so it's unmoveable. cout<<"Re-pinning "<<s1->getName()<<endl; moveable_scoops.erase(s1); tied_scoops.erase(current); changed=true; } else if(s2_moveable) { cout<<"Re-pinning "<<s2->getName()<<endl; tied_pinned++; // s2 is pinned to an unmoveable. moveable_scoops.erase(s2); tied_scoops.erase(current); changed=true; } else { tied_nochange++; tied_scoops.erase(current); } } } // step 2, coalesce changed=true; while(changed) { changed=false; for(auto it=tied_scoops.begin(); it!=tied_scoops.end(); ) { auto current=it++; const ScoopPair_t& p=*current; DataScoop_t* s1=p.first; DataScoop_t* s2=p.second; if(is_in_dont_coalesce_scoops(s1) || is_in_dont_coalesce_scoops(s2)) { cout<<"Skipping coalesce of "<<s1->getName()<<" and "<<s2->getName()<<endl; continue; } bool s1_moveable=contains(moveable_scoops, s1); bool s2_moveable=contains(moveable_scoops, s2); // we previously removed anything that's pinned from moveable if(s1_moveable && s2_moveable) { // assert order is right assert(s1->getStart()->getVirtualOffset() < s2->getStart()->getVirtualOffset()); // check if these are adjacent. if(s1->getEnd()->getVirtualOffset()+1 < s2->getStart()->getVirtualOffset()) { // pad s1 to fill hole string new_contents=s1->getContents(); new_contents.resize(s2->getStart()->getVirtualOffset()-s1->getStart()->getVirtualOffset()); s1->getEnd()->setVirtualOffset(s2->getStart()->getVirtualOffset()-1); } else if(s1->getEnd()->getVirtualOffset()+1 == s2->getStart()->getVirtualOffset()) { // do nothing if they fit perfectly. } else assert(0); // overlapping scoops? cout<<"Coalescing 2-tied, but unpinned scoops "<<s1->getName()<<" and "<<s2->getName()<<"."<<endl; // update our inteneral data structures for how to apply relocs. auto insn_fixup_updater=[s1,s2](set<Insn_fixup_t> &the_set) { unsigned int size=the_set.size(); set<Insn_fixup_t> new_elements; auto it=the_set.begin(); while(it!=the_set.end()) { auto current = it++; auto replacer=*current; if(replacer.to == s2) { the_set.erase(current); replacer.to=s1; new_elements.insert(replacer); } } the_set.insert(new_elements.begin(), new_elements.end()); assert(size==the_set.size()); }; insn_fixup_updater(pcrel_refs_to_scoops); insn_fixup_updater(absolute_refs_to_scoops); insn_fixup_updater(immed_refs_to_scoops); auto scoop_fixup_updater=[s1,s2](set<Scoop_fixup_t> &the_set) { set<Scoop_fixup_t> new_elements; auto it=the_set.begin(); while(it!=the_set.end()) { auto current = it++; if(current->to == s2 || current->from==s2) { auto replacer=*current; if(replacer.to==s2) replacer.to=s1; if(replacer.from==s2) { replacer.from=s1; cout<<"Updating data_ref_to_scoops offset from "<<hex<<replacer.offset<<" to "<<replacer.offset+s1->getSize()<<endl; replacer.offset+=s1->getSize(); } the_set.erase(current); new_elements.insert(replacer); } } the_set.insert(new_elements.begin(), new_elements.end()); }; scoop_fixup_updater(data_refs_to_scoops); for(auto &r : getFileIR()->getRelocations()) { // s2 just came into existence, didn't it? // assert(r->getWRT()!=s2); // yes, but there may be relocs pointing at the s2 part of // a split object, and so the reloc might get updated to point to s2 instead. if( r->getWRT()==s2) { r->setWRT(s1); r->setAddend(r->getAddend()+s1->getSize()); } } /* don't remove scoop here, as it will delete s2. this bit is moved later. */ // s2's end addresss is about to go away, so // update s1's end VO instead of using s2 end addr. s1->getEnd()->setVirtualOffset(s2->getEnd()->getVirtualOffset()); moveable_scoops.erase(s2); // remove it from our analysis unsigned int old_s1_size=s1->getContents().size(); s1->setContents(s1->getContents()+s2->getContents()); s1->setName(s1->getName()+" coalesced w/"+ s2->getName()); if(!s2->isRelRo()) s1->clearRelRo(); s1->setRawPerms( s1->getRawPerms() | s2->getRawPerms()); // we just created s2 in this pass, right? // no, s2 could be one of the sections from the orig binary that we've been asked to move // and it might have relocs for unpinning //assert(s2->getRelocations().size()==0); // assert no relocs that're part of s2. // add s2's relocs to s1. for(auto reloc : s2->getRelocations()) { cout<<"Adjusting reloc "<< s2->getName()<<"+"<<reloc->getOffset()<<":"<<reloc->getType()<<" to "; reloc->setOffset(reloc->getOffset()+old_s1_size); auto s1_relocs=s1->getRelocations(); s1_relocs.insert(reloc); s1->setRelocations(s1_relocs); cout << s1->getName()<<"+"<<reloc->getOffset()<<":"<<reloc->getType()<<endl; } // tell s2 it has no relocs so when we remove it, they don't go away. s2->setRelocations({}); // we've processed this one. tied_scoops.erase(current); auto scoop_pair_first_finder= [s2](const ScoopPair_t& p2) { return (p2.first==s2); }; auto found=find_if(ALLOF(tied_scoops), scoop_pair_first_finder); if( found!=tied_scoops.end()) { ScoopPair_t p2=*found; p2.first=s1; tied_scoops.erase(found); tied_scoops.insert(p2); } assert(find_if(ALLOF(tied_scoops), scoop_pair_first_finder) ==tied_scoops.end()); // finally remove s2 from the IR. getFileIR()->removeScoop(s2); changed=true; break; } else assert(0); // why are there pinned scoops still? } } // ensure we handled eveything. assert(tied_scoops.size()==0); } template <class T_Sym, class T_Rela, class T_Rel, class T_Dyn, class T_Extractor> void MoveGlobals_t<T_Sym,T_Rela,T_Rel,T_Dyn,T_Extractor>::UpdateScoopLocations() { // apply all 3 types of relocs for instructions for_each(ALLOF(pcrel_refs_to_scoops), [this] (const Insn_fixup_t & it) { if (m_verbose) cout << "Applying pcrel w/wrt from " << it.from->getDisassembly() << " to " << it.to->getName() << " at " << hex << it.from->getBaseID() << endl; ApplyPcrelMemoryRelocation(it.from,it.to); }); for_each(ALLOF(absolute_refs_to_scoops), [this] (const Insn_fixup_t & it) { if (m_verbose) cout << "Applying absptr_to_scoop from " << it.from->getDisassembly() << " to " << it.to->getName() << " at " << hex << it.from->getBaseID() << endl; ApplyAbsoluteMemoryRelocation(it.from,it.to); }); for_each(ALLOF(immed_refs_to_scoops), [this] (const Insn_fixup_t & it) { if (m_verbose) cout << "Applying immedptr_to_scoop from " << it.from->getDisassembly() << " to " << it.to->getName() << " at " << hex << it.from->getBaseID() << endl; ApplyImmediateRelocation(it.from, it.to); }); for_each(ALLOF(data_refs_to_scoops), [this] (const Scoop_fixup_t & it) { if (m_verbose) cout << "Applying dataptr_to_scoop from " << it.from->getName() << " to " << it.to->getName() << " at " << hex << it.offset << endl; ApplyDataRelocation(it.from, it.offset, it.to); }); // unpin all the moveable scoops. for (auto sit : moveable_scoops) { VirtualOffset_t newend = sit->getEnd()->getVirtualOffset() - sit->getStart()->getVirtualOffset(); sit->getEnd()->setVirtualOffset(newend); sit->getStart()->setVirtualOffset(0); } } // would be nice to have a FindRelocation function that takes a parameterized type. template <class T_Sym, class T_Rela, class T_Rel, class T_Dyn, class T_Extractor> Relocation_t* MoveGlobals_t<T_Sym,T_Rela,T_Rel,T_Dyn,T_Extractor>::FindRelocationWithType(BaseObj_t* obj, std::string type) { for(auto reloc : obj->getRelocations()) { if (reloc->getType() == type) return reloc; } return NULL; } template <class T_Sym, class T_Rela, class T_Rel, class T_Dyn, class T_Extractor> void MoveGlobals_t<T_Sym,T_Rela,T_Rel,T_Dyn,T_Extractor>::PrintStats() { const auto resorted_moveable_scoops=DataScoopSet_t(ALLOF(moveable_scoops)); DataScoopSet_t unmoveable_scoops; unsigned long long moveable_scoop_bytes=0; unsigned long long unmoveable_scoop_bytes=0; unsigned long long total_scoop_bytes=0; set_difference( ALLOF(getFileIR()->getDataScoops()), ALLOF(resorted_moveable_scoops), inserter(unmoveable_scoops,unmoveable_scoops.end())); if(m_verbose) { cout<<"Moveable scoops: "<<endl; for_each(ALLOF(moveable_scoops), [](DataScoop_t* scoop) { cout<<"\t"<<scoop->getName()<<", contents: "<<endl; auto i=0u; const auto max_prints_env=getenv("MG_MAX_SCOOP_CONTENT_PRINT"); const auto max_prints=max_prints_env ? strtoul(max_prints_env,NULL,0) : 16ul; for(i=0;i+8<scoop->getSize() && i<max_prints;i+=8) cout<<"\t\tat:"<<hex<<i<<" value:0x"<<hex<<*(uint64_t*)&scoop->getContents().c_str()[i]<<" "<<endl; for(/* empty init */;i<scoop->getSize() && i<max_prints;i++) cout<<"\t\tat:"<<hex<<i<<" value:0x"<<hex<< + *(uint8_t*)&scoop->getContents().c_str()[i]<<" "<<endl; }); cout<<"Not moveable scoops: "<<endl; for_each(ALLOF(unmoveable_scoops), [](DataScoop_t* scoop) { cout<<"\t"<<scoop->getName()<<" at "<<hex<<scoop->getStart()->getVirtualOffset()<<endl; }); } // gather number of moveable bytes for_each(moveable_scoops.begin(), moveable_scoops.end(), [&moveable_scoop_bytes, &total_scoop_bytes](DataScoop_t* scoop) { moveable_scoop_bytes += scoop->getSize(); total_scoop_bytes+=scoop->getSize(); }); // gather number of unmoveable bytes for_each(unmoveable_scoops.begin(), unmoveable_scoops.end(), [&unmoveable_scoop_bytes,&total_scoop_bytes](DataScoop_t* scoop) { unmoveable_scoop_bytes +=scoop->getSize(); total_scoop_bytes+=scoop->getSize(); }); assert(getenv("SELF_VALIDATE")==nullptr || moveable_scoops.size() >= 5); assert(getenv("SELF_VALIDATE")==nullptr || (immed_refs_to_scoops.size() + pcrel_refs_to_scoops.size()+absolute_refs_to_scoops.size()) > 5); cout<<"# ATTRIBUTE ASSURANCE_Non-Overlapping_Globals::Total_data_items="<<dec<<unmoveable_scoops.size()+moveable_scoops.size()<<endl; cout<<"# ATTRIBUTE ASSURANCE_Non-Overlapping_Globals::Unmoveable_data_items="<<dec<<unmoveable_scoops.size()<<endl; cout<<"# ATTRIBUTE ASSURANCE_Non-Overlapping_Globals::Moveable_data_items="<<dec<<moveable_scoops.size()<<endl; cout<<"# ATTRIBUTE ASSURANCE_Non-Overlapping_Globals::Percent_data_items_moveable="<<std::fixed<<std::setprecision(1)<< ((float)moveable_scoops.size()/((float)(unmoveable_scoops.size()+moveable_scoops.size())))*100.00<<"%"<< endl; cout<<"# ATTRIBUTE ASSURANCE_Non-Overlapping_Globals::Unmoveable_data_items_in_bytes="<<dec<<unmoveable_scoop_bytes<<endl; cout<<"# ATTRIBUTE ASSURANCE_Non-Overlapping_Globals::Moveable_data_items_in_bytes="<<dec<<moveable_scoop_bytes<<endl; cout<<"# ATTRIBUTE ASSURANCE_Non-Overlapping_Globals::Total_data_items_in_bytes="<<dec<<total_scoop_bytes<<endl; cout << "# ATTRIBUTE ASSURANCE_Non-Overlapping_Globals::Percent_data_item_bytes_moved="<<std::fixed<<std::setprecision(1) << ((double)moveable_scoop_bytes/(double)total_scoop_bytes)*100.00 <<"%"<< endl; cout << "# ATTRIBUTE ASSURANCE_Non-Overlapping_Globals::Percent_data_item_bytes_not_moved=" << std::fixed <<std::setprecision(1)<< ((double)unmoveable_scoop_bytes/(double)total_scoop_bytes)*100.00 <<"%"<< endl; cout<<"# ATTRIBUTE Non-Overlapping_Globals::tied_scoops="<<dec<<tied_scoops.size()<<endl; cout<<"# ATTRIBUTE Non-Overlapping_Globals::pcrel_refs="<<dec<<pcrel_refs_to_scoops.size()<<endl; cout<<"# ATTRIBUTE Non-Overlapping_Globals::abs_refs="<<dec<<absolute_refs_to_scoops.size()<<endl; cout<<"# ATTRIBUTE Non-Overlapping_Globals::imm_refs="<<dec<<immed_refs_to_scoops.size()<<endl; cout<<"# ATTRIBUTE Non-Overlapping_Globals::data_refs="<<dec<<data_refs_to_scoops.size()<<endl; cout<<"# ATTRIBUTE Non-Overlapping_Globals::coalesced_scoops="<<dec<<tied_unpinned<<endl; cout<<"# ATTRIBUTE Non-Overlapping_Globals::repinned_scoops="<<dec<<tied_pinned<<endl; cout<<"# ATTRIBUTE Non-Overlapping_Globals::ties_for_folded_constants="<<dec<<ties_for_folded_constants<<endl; cout<<"# ATTRIBUTE Non-Overlapping_Globals::tied_scoop_pairs_that_were_already_pinned="<<dec<<tied_nochange<<endl; cout<<"#ATTRIBUTE mg::unmoveable_scoops="<<dec<<unmoveable_scoops.size()<<endl; cout<<"#ATTRIBUTE mg::moveable_scoops="<<dec<<moveable_scoops.size()<<endl; cout<<"#ATTRIBUTE mg::pcrel_refs="<<dec<<pcrel_refs_to_scoops.size()<<endl; cout<<"#ATTRIBUTE mg::abs_refs="<<dec<<absolute_refs_to_scoops.size()<<endl; cout<<"#ATTRIBUTE mg::imm_refs="<<dec<<immed_refs_to_scoops.size()<<endl; cout<<"#ATTRIBUTE mg::data_refs="<<dec<<data_refs_to_scoops.size()<<endl; cout<<"#ATTRIBUTE mg::coalesced_scoops="<<dec<<tied_unpinned<<endl; cout<<"#ATTRIBUTE mg::repinned_scoops="<<dec<<tied_pinned<<endl; cout<<"#ATTRIBUTE mg::ties_for_folded_constants="<<dec<<ties_for_folded_constants<<endl; cout<<"#ATTRIBUTE mg::tied_scoop_pairs_that_were_already_pinned="<<dec<<tied_nochange<<endl; } // explicit instatnation for elf32 and elf64 template class MoveGlobals_t<Elf32_Sym, Elf32_Rela, Elf32_Rel, Elf32_Dyn, Extractor32_t>; template class MoveGlobals_t<Elf64_Sym, Elf64_Rela, Elf64_Rel, Elf64_Dyn, Extractor64_t>;