void BinaryEdit::makeInitAndFiniIfNeeded() { using namespace Dyninst::SymtabAPI; Symtab* linkedFile = getAOut()->parse_img()->getObject(); // Disable this for .o's and static binaries if( linkedFile->isStaticBinary() || linkedFile->getObjectType() == obj_RelocatableFile ) { return; } bool foundInit = false; bool foundFini = false; vector <Function *> funcs; if (linkedFile->findFunctionsByName(funcs, "_init")) { foundInit = true; } if (linkedFile->findFunctionsByName(funcs, "_fini")) { foundFini = true; } if( !foundInit ) { Offset initOffset = linkedFile->getInitOffset(); Region *initsec = linkedFile->findEnclosingRegion(initOffset); if(!initOffset || !initsec) { unsigned char* emptyFunction = NULL; int emptyFuncSize = 0; #if defined(arch_x86) || defined(arch_x86_64) static unsigned char empty_32[] = { 0x55, 0x89, 0xe5, 0xc9, 0xc3 }; static unsigned char empty_64[] = { 0x55, 0x48, 0x89, 0xe5, 0xc9, 0xc3 }; if(linkedFile->getAddressWidth() == 8) { emptyFunction = empty_64; emptyFuncSize = 6; } else { emptyFunction = empty_32; emptyFuncSize = 5; } #elif defined (arch_power) static unsigned empty[] = {0x4e800020}; emptyFunction = (unsigned char*) empty; emptyFuncSize = 4; #endif //defined(arch_x86) || defined(arch_x86_64) linkedFile->addRegion(highWaterMark_, (void*)(emptyFunction), emptyFuncSize, ".init.dyninst", Dyninst::SymtabAPI::Region::RT_TEXT, true); highWaterMark_ += emptyFuncSize; lowWaterMark_ += emptyFuncSize; linkedFile->findRegion(initsec, ".init.dyninst"); assert(initsec); linkedFile->addSysVDynamic(DT_INIT, initsec->getMemOffset()); startup_printf("%s[%d]: creating .init.dyninst region, region addr 0x%lx\n", FILE__, __LINE__, initsec->getMemOffset()); } startup_printf("%s[%d]: ADDING _init at 0x%lx\n", FILE__, __LINE__, initsec->getMemOffset()); Symbol *initSym = new Symbol( "_init", Symbol::ST_FUNCTION, Symbol::SL_GLOBAL, Symbol::SV_DEFAULT, initsec->getMemOffset(), linkedFile->getDefaultModule(), initsec, UINT_MAX ); linkedFile->addSymbol(initSym); } if( !foundFini ) { Offset finiOffset = linkedFile->getFiniOffset(); Region *finisec = linkedFile->findEnclosingRegion(finiOffset); if(!finiOffset || !finisec) { unsigned char* emptyFunction = NULL; int emptyFuncSize = 0; #if defined(arch_x86) || defined(arch_x86_64) static unsigned char empty_32[] = { 0x55, 0x89, 0xe5, 0xc9, 0xc3 }; static unsigned char empty_64[] = { 0x55, 0x48, 0x89, 0xe5, 0xc9, 0xc3 }; if(linkedFile->getAddressWidth() == 8) { emptyFunction = empty_64; emptyFuncSize = 6; } else { emptyFunction = empty_32; emptyFuncSize = 5; } #elif defined (arch_power) static unsigned empty[] = {0x4e800020}; emptyFunction = (unsigned char*) empty; emptyFuncSize = 4; #elif defined (arch_aarch64) static unsigned char empty[] = { 0xfd, 0x7b, 0xbf, 0xa9, 0xfd, 0x03, 0x00, 0x91, 0xfd, 0x7b, 0xc1, 0xa8, 0xc0, 0x03, 0x5f, 0xd6}; emptyFunction = empty; emptyFuncSize = 16; #endif //defined(arch_x86) || defined(arch_x86_64) linkedFile->addRegion(highWaterMark_, (void*)(emptyFunction), emptyFuncSize, ".fini.dyninst", Dyninst::SymtabAPI::Region::RT_TEXT, true); highWaterMark_ += emptyFuncSize; lowWaterMark_ += emptyFuncSize; linkedFile->findRegion(finisec, ".fini.dyninst"); assert(finisec); linkedFile->addSysVDynamic(DT_FINI, finisec->getMemOffset()); startup_printf("%s[%d]: creating .fini.dyninst region, region addr 0x%lx\n", FILE__, __LINE__, finisec->getMemOffset()); } startup_printf("%s[%d]: ADDING _fini at 0x%lx\n", FILE__, __LINE__, finisec->getMemOffset()); Symbol *finiSym = new Symbol( "_fini", Symbol::ST_FUNCTION, Symbol::SL_GLOBAL, Symbol::SV_DEFAULT, finisec->getMemOffset(), linkedFile->getDefaultModule(), finisec, UINT_MAX ); linkedFile->addSymbol(finiSym); } }
func_instance *mapped_object::findGlobalDestructorFunc(const std::string &dtorHandler) { using namespace Dyninst::InstructionAPI; const pdvector<func_instance *> *funcs = findFuncVectorByMangled(dtorHandler); if( funcs != NULL ) { return funcs->at(0); } /* * If the symbol isn't found, try looking for it in a call in the * .fini section. It is the last call in .fini. * * The pattern is: * * _fini: * * ... some code ... * * call dtor_handler * * ... prologue ... */ Symtab *linkedFile = parse_img()->getObject(); Region *finiRegion = NULL; if( !linkedFile->findRegion(finiRegion, ".fini") ) { vector<Dyninst::SymtabAPI::Function *> symFuncs; if( linkedFile->findFunctionsByName(symFuncs, "_fini") ) { finiRegion = symFuncs[0]->getRegion(); }else{ logLine("failed to locate .fini Region or _fini function\n"); return NULL; } } if( finiRegion == NULL ) { logLine("failed to locate .fini Region or _fini function\n"); return NULL; } // Search for last call in the function Address dtorAddress = 0; unsigned bytesSeen = 0; const unsigned char *p = reinterpret_cast<const unsigned char *>(finiRegion->getPtrToRawData()); InstructionDecoder decoder(p, finiRegion->getDiskSize(), parse_img()->codeObject()->cs()->getArch()); Instruction::Ptr lastCall; Instruction::Ptr curInsn = decoder.decode(); while(curInsn && curInsn->isValid() && bytesSeen < finiRegion->getDiskSize()) { InsnCategory category = curInsn->getCategory(); if( category == c_CallInsn ) { lastCall = curInsn; break; } bytesSeen += curInsn->size(); curInsn = decoder.decode(); } if( !lastCall.get() || !lastCall->isValid() ) { logLine("heuristic for finding global destructor function failed\n"); return NULL; } Address callAddress = finiRegion->getMemOffset() + bytesSeen; RegisterAST thePC = RegisterAST( Dyninst::MachRegister::getPC(parse_img()->codeObject()->cs()->getArch())); Expression::Ptr callTarget = lastCall->getControlFlowTarget(); if( !callTarget.get() ) { logLine("failed to find global destructor function\n"); return NULL; } callTarget->bind(&thePC, Result(s64, callAddress)); Result actualTarget = callTarget->eval(); if( actualTarget.defined ) { dtorAddress = actualTarget.convert<Address>(); }else{ logLine("failed to find global destructor function\n"); return NULL; } if( !dtorAddress || !parse_img()->codeObject()->cs()->isValidAddress(dtorAddress) ) { logLine("invalid address for global destructor function\n"); return NULL; } // A targ stub should have been created at the address func_instance *ret = NULL; if( (ret = findFuncByEntry(dtorAddress)) == NULL ) { logLine("unable to find global destructor function\n"); return NULL; } inst_printf("%s[%d]: set global destructor address to 0x%lx\n", FILE__, __LINE__, dtorAddress); return ret; }
bool BinaryEdit::writeFile(const std::string &newFileName) { // Step 1: changes. inst_printf(" writing %s ... \n", newFileName.c_str()); Symtab *symObj = mobj->parse_img()->getObject(); // link to the runtime library if tramp guards are currently enabled if ( !symObj->isStaticBinary() && !BPatch::bpatch->isTrampRecursive() ) { assert(!runtime_lib.empty()); symObj->addLibraryPrereq((*runtime_lib.begin())->fileName()); } if( symObj->isStaticBinary() && isDirty() ) { if( !doStaticBinarySpecialCases() ) { cerr << "Failed to write file " << newFileName << ": static binary handler failed" << endl; return false; } } delayRelocation_ = false; relocate(); vector<Region*> oldSegs; symObj->getAllRegions(oldSegs); //Write any traps to the mutatee if (canUseTraps()) { trapMapping.shouldBlockFlushes(false); trapMapping.flush(); } // Now, we need to copy in the memory of the new segments for (unsigned i = 0; i < oldSegs.size(); i++) { codeRange *segRange = NULL; if (!memoryTracker_->find(oldSegs[i]->getMemOffset(), segRange)) { #if 0 // Looks like BSS if (newSegs[i].name == ".bss") #endif continue; //inst_printf (" segment name: %s\n", newSegs[i].name.c_str()); //assert(0); } //inst_printf(" ==> memtracker: Copying to 0x%lx from 0x%lx\n", //newSegs[i].loadaddr, segRange->get_local_ptr()); memoryTracker* mt = dynamic_cast<memoryTracker*>(segRange); assert(mt); if(mt->dirty) { oldSegs[i]->setPtrToRawData(segRange->get_local_ptr(), oldSegs[i]->getMemSize()); } //newSegs[i].data = segRange->get_local_ptr(); } // Okay, that does it for the old stuff. // Now we need to get the new stuff. That's all the allocated memory. First, big // buffer to hold it. void *newSectionPtr = malloc(highWaterMark_ - lowWaterMark_); pdvector<codeRange *> writes; memoryTracker_->elements(writes); for (unsigned i = 0; i < writes.size(); i++) { assert(newSectionPtr); memoryTracker *tracker = dynamic_cast<memoryTracker *>(writes[i]); assert(tracker); //inst_printf("memory tracker: 0x%lx load=0x%lx size=%d %s\n", //tracker->get_local_ptr(), tracker->get_address(), tracker->get_size(), //tracker->alloced ? "[A]" : ""); if (!tracker->alloced) continue; // Copy whatever is in there into the big buffer, at the appropriate address assert(tracker->get_address() >= lowWaterMark_); Address offset = tracker->get_address() - lowWaterMark_; assert((offset + tracker->get_size()) < highWaterMark_); void *ptr = (void *)(offset + (Address)newSectionPtr); memcpy(ptr, tracker->get_local_ptr(), tracker->get_size()); } // Righto. Now, that includes the old binary - by design - // so skip it and see what we're talking about size-wise. Which should // be less than the highWaterMark, so we can double-check. // Next, make a new section. We have the following parameters: // Offset vaddr: we get this from Symtab - "first free address with sufficient space" // std::string name: without reflection, ".dyninstInst" // unsigned long flags: these are a SymtabAPI abstraction. We're going with text|data because // we might have both. // bool loadable: heck yeah... Region *newSec = NULL; symObj->findRegion(newSec, ".dyninstInst"); if (newSec) { // We're re-instrumenting - will fail for now fprintf(stderr, "ERROR: unable to open/reinstrument previously instrumented binary %s!\n", newFileName.c_str()); return false; } symObj->addRegion(lowWaterMark_, newSectionPtr, highWaterMark_ - lowWaterMark_, ".dyninstInst", Region::RT_TEXTDATA, true); fprintf(stderr,"Write lowWater : %lx, highWater : %lx\n",lowWaterMark_,highWaterMark_); symObj->findRegion(newSec, ".dyninstInst"); assert(newSec); if (mobj == getAOut()) { // Add dynamic symbol relocations for (unsigned i=0; i < dependentRelocations.size(); i++) { Address to = dependentRelocations[i]->getAddress(); Symbol *referring = dependentRelocations[i]->getReferring(); /* if (!symObj->isStaticBinary() && !symObj->hasReldyn() && !symObj->hasReladyn()) { Address addr = referring->getOffset(); bool result = writeDataSpace((void *) to, getAddressWidth(), &addr); assert(result); continue; } */ // Create the relocationEntry relocationEntry localRel(to, referring->getMangledName(), referring, relocationEntry::getGlobalRelType(getAddressWidth(), referring)); /* if( mobj->isSharedLib() ) { localRel.setRelAddr(to - mobj->imageOffset()); } */ symObj->addExternalSymbolReference(referring, newSec, localRel); /* newSymbol = new Symbol(referring->getName(), Symbol::ST_FUNCTION, Symbol::SL_GLOBAL, Symbol::SV_DEFAULT, (Address)0, symObj->getDefaultModule(), NULL, 8, true, false); symObj->addSymbol(newSymbol, referring); if (!symObj->hasReldyn() && symObj->hasReladyn()) { newSec->addRelocationEntry(to, newSymbol, relocationEntry::dynrel, Region::RT_RELA); } else { newSec->addRelocationEntry(to, newSymbol, relocationEntry::dynrel); } */ } } pdvector<Symbol *> newSyms; buildDyninstSymbols(newSyms, newSec, symObj->getOrCreateModule("dyninstInst", lowWaterMark_)); for (unsigned i = 0; i < newSyms.size(); i++) { symObj->addSymbol(newSyms[i]); } // Okay, now... // Hand textSection and newSection to DynSymtab. // First, textSection. // From the SymtabAPI documentation: we have the following methods we want to use. // Symtab::addSection(Offset vaddr, void *data, unsigned int dataSize, std::string name, // unsigned long flags, bool loadable) // Symtab::updateCode(void *buffer, unsigned size) // Symtab::emit(std::string filename) // First, text assert(symObj); // And now we generate the new binary //if (!symObj->emit(newFileName.c_str())) { if (!symObj->emit(newFileName.c_str())) { SymtabError lastError = Symtab::getLastSymtabError(); showErrorCallback(109, Symtab::printError(lastError)); return false; } return true; }
func_instance *mapped_object::findGlobalConstructorFunc(const std::string &ctorHandler) { using namespace Dyninst::InstructionAPI; const pdvector<func_instance *> *funcs = findFuncVectorByMangled(ctorHandler); if( funcs != NULL ) { return funcs->at(0); } /* If the symbol isn't found, try looking for it in a call instruction in * the .init section * * On Linux, the instruction sequence is: * ... * some instructions * ... * call call_gmon_start * call frame_dummy * call ctor_handler * * On FreeBSD, the instruction sequence is: * ... * some instructions * ... * call frame_dummy * call ctor_handler */ Symtab *linkedFile = parse_img()->getObject(); Region *initRegion = NULL; if( !linkedFile->findRegion(initRegion, ".init") ) { vector<Dyninst::SymtabAPI::Function *> symFuncs; if( linkedFile->findFunctionsByName(symFuncs, "_init") ) { initRegion = symFuncs[0]->getRegion(); }else{ logLine("failed to locate .init Region or _init function\n"); return NULL; } } if( initRegion == NULL ) { logLine("failed to locate .init Region or _init function\n"); return NULL; } // Search for last of a fixed number of calls #if defined(os_freebsd) const unsigned CTOR_NUM_CALLS = 2; #else const unsigned CTOR_NUM_CALLS = 3; #endif Address ctorAddress = 0; unsigned bytesSeen = 0; unsigned numCalls = 0; const unsigned char *p = reinterpret_cast<const unsigned char *>(initRegion->getPtrToRawData()); InstructionDecoder decoder(p, initRegion->getDiskSize(), parse_img()->codeObject()->cs()->getArch()); Instruction::Ptr curInsn = decoder.decode(); while(numCalls < CTOR_NUM_CALLS && curInsn && curInsn->isValid() && bytesSeen < initRegion->getDiskSize()) { InsnCategory category = curInsn->getCategory(); if( category == c_CallInsn ) { numCalls++; } if( numCalls < CTOR_NUM_CALLS ) { bytesSeen += curInsn->size(); curInsn = decoder.decode(); } } if( numCalls != CTOR_NUM_CALLS ) { logLine("heuristic for finding global constructor function failed\n"); return NULL; } Address callAddress = initRegion->getMemOffset() + bytesSeen; RegisterAST thePC = RegisterAST( Dyninst::MachRegister::getPC(parse_img()->codeObject()->cs()->getArch())); Expression::Ptr callTarget = curInsn->getControlFlowTarget(); if( !callTarget.get() ) { logLine("failed to find global constructor function\n"); return NULL; } callTarget->bind(&thePC, Result(s64, callAddress)); Result actualTarget = callTarget->eval(); if( actualTarget.defined ) { ctorAddress = actualTarget.convert<Address>(); }else{ logLine("failed to find global constructor function\n"); return NULL; } if( !ctorAddress || !parse_img()->codeObject()->cs()->isValidAddress(ctorAddress) ) { logLine("invalid address for global constructor function\n"); return NULL; } func_instance *ret; if( (ret = findFuncByEntry(ctorAddress)) == NULL ) { logLine("unable to create representation for global constructor function\n"); return NULL; } inst_printf("%s[%d]: set global constructor address to 0x%lx\n", FILE__, __LINE__, ctorAddress); return ret; }
BinaryEdit *BinaryEdit::openFile(const std::string &file, PatchMgrPtr mgr, Dyninst::PatchAPI::Patcher *patch, const std::string &member) { if (!OS::executableExists(file)) { startup_printf("%s[%d]: failed to read file %s\n", FILE__, __LINE__, file.c_str()); std::string msg = std::string("Can't read executable file ") + file + (": ") + strerror(errno); showErrorCallback(68, msg.c_str()); return NULL; } fileDescriptor desc; if (!getStatFileDescriptor(file, desc)) { startup_printf("%s[%d]: failed to create file descriptor for %s!\n", FILE__, __LINE__, file.c_str()); return NULL; } // Open the mapped object as an archive member if( !member.empty() ) { desc.setMember(member); } BinaryEdit *newBinaryEdit = new BinaryEdit(); if (!newBinaryEdit) { startup_printf("%s[%d]: failed to create binary representation for %s!\n", FILE__, __LINE__, file.c_str()); } newBinaryEdit->mobj = mapped_object::createMappedObject(desc, newBinaryEdit); if (!newBinaryEdit->mobj) { startup_printf("%s[%d]: failed to create mapped object for %s\n", FILE__, __LINE__, file.c_str()); return NULL; } /* PatchAPI stuffs */ if (!mgr) { newBinaryEdit->initPatchAPI(); } else { newBinaryEdit->setMgr(mgr); assert(patch); newBinaryEdit->setPatcher(patch); } newBinaryEdit->addMappedObject(newBinaryEdit->mobj); /* End of PatchAPI stuffs */ // We now need to access the start of the new section we're creating. // I'm going through the mapped_object interface for now - // I assume we'll pass it to DynSymtab, then add our base // address to it at the mapped_ level. Symtab* linkedFile = newBinaryEdit->getAOut()->parse_img()->getObject(); Region *newSec = NULL; linkedFile->findRegion(newSec, ".dyninstInst"); if (newSec) { // We're re-instrumenting - will fail for now fprintf(stderr, "ERROR: unable to open/reinstrument previously instrumented binary %s!\n", file.c_str()); return NULL; } Address base = linkedFile->getFreeOffset(50*1024*1024); base += (1024*1024); base -= (base & (1024*1024-1)); newBinaryEdit->highWaterMark_ = base; newBinaryEdit->lowWaterMark_ = newBinaryEdit->highWaterMark_; //CHEN MODIFIED, this can reserve space for plt table, or even for inline code region //newBinaryEdit->highWaterMark_ += 0x100000; // Testing newBinaryEdit->makeInitAndFiniIfNeeded(); newBinaryEdit->createMemoryBackingStore(newBinaryEdit->getAOut()); newBinaryEdit->initialize(); //Don't count initialization in determining dirty newBinaryEdit->isDirty_ = false; //!(foundInit && foundFini); return newBinaryEdit; }