// Build a list of symbols describing instrumentation and relocated functions. // To keep this list (somewhat) short, we're doing one symbol per extent of // instrumentation + relocation for a particular function. // New: do this for one mapped object. void BinaryEdit::buildDyninstSymbols(pdvector<Symbol *> &newSyms, Region *newSec, Module *newMod) { for (std::vector<SymtabAPI::Symbol *>::iterator iter = newDyninstSyms_.begin(); iter != newDyninstSyms_.end(); ++iter) { (*iter)->setModule(newMod); (*iter)->setRegion(newSec); newSyms.push_back(*iter); } for (CodeTrackers::iterator i = relocatedCode_.begin(); i != relocatedCode_.end(); ++i) { Relocation::CodeTracker *CT = *i; func_instance *currFunc = NULL; Address start = 0; unsigned size = 0; for (Relocation::CodeTracker::TrackerList::const_iterator iter = CT->trackers().begin(); iter != CT->trackers().end(); ++iter) { const Relocation::TrackerElement *tracker = *iter; func_instance *tfunc = tracker->func(); if (currFunc != tfunc) { // Starting a new function if (currFunc) { // Record the old one // currfunc set // start set size = tracker->reloc() - start; std::string name = currFunc->prettyName(); name.append("_dyninst"); Symbol *newSym = new Symbol(name.c_str(), Symbol::ST_FUNCTION, Symbol::SL_GLOBAL, Symbol::SV_DEFAULT, start, newMod, newSec, size); newSyms.push_back(newSym); } currFunc = tfunc; start = tracker->reloc(); size = 0; } else { // Accumulate size size = tracker->reloc() - start; } } } }
// Attempt to find the largest contiguous (in virtual address space) region. // This region must include ".data", and may include the other data like regions static inline bool find_data_region(pdvector<Address>& all_addr, pdvector<long>& all_size, pdvector<long>& all_disk, unsigned long& data_len, Address& data_off) { // Start at data and work back assert(all_addr[K_D_INDEX]); assert(all_size[K_D_INDEX]); assert(all_addr.size() == all_size.size()); Address current = all_addr[K_D_INDEX]; Address min_adr = current; Address max_adr = current + all_size[K_D_INDEX]; unsigned index, max=all_addr.size(); bool updated=true; while (updated) { updated = false; for (index=0; index<max; index++) { if (all_addr[index] && all_size[index] && all_disk[index] && ((all_addr[index] + all_size[index]) == current)) { current = all_addr[index]; updated = true; } } } min_adr = current; // Start at data and work forward current = max_adr; updated=true; while (updated) { updated = false; for (index=0; index<max; index++) { if (all_addr[index] && all_size[index] && all_disk[index] && (all_addr[index] == current)) { current = all_addr[index] + all_size[index]; updated = true; } } } max_adr = current; data_len = (max_adr - min_adr); data_off = min_adr; assert(min_adr <= all_addr[K_D_INDEX]); assert(max_adr >= all_addr[K_D_INDEX] + all_size[K_D_INDEX]); return true; }
// Return false if the PC is within the jump range of any of our // multiTramps bool instPoint::checkInst(pdvector<Address> &checkPCs) { for (unsigned sI = 0; sI < checkPCs.size(); sI++) { Address pc = checkPCs[sI]; for (unsigned iI = 0; iI < instances.size(); iI++) { multiTramp *mt = instances[iI]->multi(); // No multi -> not installed. if (!mt) continue; if ((pc > mt->instAddr()) && (pc < (mt->instAddr() + mt->instSize()))) { // We have a conflict. Now, we may still be able to make this // work; if we're not conflicting on the actual branch, we // may have trap-filled the remainder which allows us to // catch and transfer. if (pc < (mt->instAddr() + mt->branchSize())) { // We're in the jump area, conflict. fprintf(stderr, "MT conflict (MT from 0x%p to 0x%p, 0x%p to 0x%p dangerous), PC 0x%p\n", (void *)mt->instAddr(), (void *)(mt->instAddr() + mt->instSize()), (void *)mt->instAddr(), (void *)(mt->instAddr() + mt->branchSize()), (void *)pc); return false; } } } } #if defined(cap_relocation) // Yay check relocation if (!func()->relocationCheck(checkPCs)) return false; #endif return true; }
/* * BPatch_addressSpace::findModuleByAddr * * Returns the module that contains the specified address, or NULL if the * address is not within a module. Does NOT trigger parsing * * addr The address to use for the lookup. */ BPatch_module *BPatch_addressSpace::findModuleByAddr(Address addr) { std::vector<AddressSpace *> as; getAS(as); assert(as.size()); mapped_object *obj = as[0]->findObject(addr); if ( ! obj ) return NULL; const pdvector<mapped_module*> mods = obj->getModules(); if (mods.size()) { return getImage()->findOrCreateModule(mods[0]); } return NULL; }
bool dynamic_linking::getSharedObjects(pdvector<mapped_object *> &mapped_objects) { pdvector<fileDescriptor> descs; if (!processLinkMaps(descs)) return false; // Skip first entry: always the a.out for (unsigned i = 0; i < descs.size(); i++) { if (descs[i] != proc->getAOut()->getFileDesc()) { #if 0 fprintf(stderr, "DEBUG: match pattern %d, %d, %d, %d, %d\n", descs[i].file() == proc->getAOut()->getFileDesc().file(), descs[i].code() == proc->getAOut()->getFileDesc().code(), descs[i].data() == proc->getAOut()->getFileDesc().data(), descs[i].member() == proc->getAOut()->getFileDesc().member(), descs[i].pid() == proc->getAOut()->getFileDesc().pid()); #endif mapped_object *newobj = mapped_object::createMappedObject(descs[i], proc); if (newobj == NULL) continue; mapped_objects.push_back(newobj); #if defined(cap_save_the_world) setlowestSObaseaddr(descs[i].code()); #endif } } return true; } /* end getSharedObjects() */
bool mapped_module::findFuncVectorByPretty(const pdstring &funcname, pdvector<int_function *> &funcs) { // For efficiency sake, we grab the image vector and strip out the // functions we want. // We could also keep them all in modules and ditch the image-wide search; // the problem is that BPatch goes by module and internal goes by image. unsigned orig_size = funcs.size(); const pdvector<int_function *> *obj_funcs = obj()->findFuncVectorByPretty(funcname); if (!obj_funcs) return false; for (unsigned i = 0; i < obj_funcs->size(); i++) { if ((*obj_funcs)[i]->mod() == this) funcs.push_back((*obj_funcs)[i]); } return funcs.size() > orig_size; }
bool dynamic_linking::didLinkMapsChange(u_int &change_type, pdvector<fileDescriptor> &new_descs) { // get list of current shared objects const pdvector<mapped_object *> &curr_list = proc->mappedObjects(); if((change_type == SHAREDOBJECT_REMOVED) && (curr_list.size() == 0)) { return false; } // get the list from the process via /proc if (!processLinkMaps(new_descs)) { return false; } unsigned curr_size = curr_list.size(); unsigned descs_size = new_descs.size(); #if defined(os_linux) // The current mapped object list contains the a.out, the // result from processLinkMaps does not. Correct this // when accounting for size. for (unsigned i = 0; i < curr_list.size(); i++) { if (curr_list[i] == proc->getAOut()) { curr_size--; break; } } //Also make sure that we don't start accidently counting the a.out for (unsigned i = 0; i < new_descs.size(); i++) { if (!new_descs[i].isSharedObject()) { descs_size--; break; } } #endif // override change_type if we have definite evidence of a size chanage // in the link maps if (curr_size > descs_size) change_type = SHAREDOBJECT_REMOVED; else if (curr_size < descs_size) change_type = SHAREDOBJECT_ADDED; return true; }
// Read in from the contiguous data regions, put the data in 'buffer' static inline bool read_data_region(pdvector<Address>& all_addr, pdvector<long>& all_size, pdvector<long>& all_disk, unsigned long& data_len, Address& data_off, Word *buffer, LDFILE *ldptr) { unsigned index, max = all_disk.size(); Address max_adr = data_off + data_len; assert(all_size.size() == all_addr.size()); assert(all_disk.size() == all_addr.size()); for (index=0; index<max; index++) { if ((all_addr[index] >= data_off) && ((all_addr[index] + all_size[index]) <= max_adr)) { if (ldfseek(ldptr, all_disk[index], SEEK_SET) == -1) return false; Word *buf_temp = buffer + (all_addr[index] - data_off); if (ldfread((void*) buf_temp, 1, all_size[index], ldptr) != all_size[index]) return false; } } return true; }
static void emitNeededCallRestores(codeGen &gen, pdvector<Register> &saves) { for (unsigned i=0; i<saves.size(); i++) { switch (saves[i]) { case REGNUM_EAX: emitSimpleInsn(POP_EAX, gen); break; case REGNUM_EBX: emitSimpleInsn(POP_EBX, gen); break; case REGNUM_ECX: emitSimpleInsn(POP_ECX, gen); break; case REGNUM_EDX: emitSimpleInsn(POP_EDX, gen); break; case REGNUM_EDI: emitSimpleInsn(POP_EDI, gen); break; } } saves.clear(); }
pdvector< pdvector<resourceHandle> > callGraphDisplay::getSelections(bool &wholeProgram, pdvector<unsigned> &wholeProgramFocus) const { // returns a vector[num-hierarchies] of vector of selections. // The number of hierarchies is defined as the number of children of the // root node. If "Whole Program" was selection, it isn't returned with // the main result; it's returned by modifying the 2 params const unsigned numHierarchies = rootPtr->getNumChildren(); pdvector < pdvector<resourceHandle> > result(numHierarchies); bool wholeProgramImplicit = true; // so far... for (unsigned i=0; i < numHierarchies; i++) { where4tree<callGraphRootNode> *hierarchyRoot = rootPtr->getChildTree(i); pdvector <const callGraphRootNode *> thisHierarchySelections = hierarchyRoot->getSelections(); if (thisHierarchySelections.size()==0) // add hierarchy's root item thisHierarchySelections += &hierarchyRoot->getNodeData(); else // since the hierarchy selection was not empty, we do _not_ // want to implicitly select whole-program wholeProgramImplicit = false; result[i].resize(thisHierarchySelections.size()); for (unsigned j=0; j < thisHierarchySelections.size(); j++) result[i][j] = thisHierarchySelections[j]->getUniqueId(); } wholeProgram = wholeProgramImplicit || rootPtr->isHighlighted(); if (wholeProgram) { // write to wholeProgramFocus: wholeProgramFocus.resize(numHierarchies); for (unsigned i=0; i < numHierarchies; i++) { where4tree<callGraphRootNode> *hierarchyRoot = rootPtr->getChildTree(i); const callGraphRootNode &hierarchyRootData = hierarchyRoot->getNodeData(); unsigned hierarchyRootUniqueId = hierarchyRootData.getUniqueId(); wholeProgramFocus[i] = hierarchyRootUniqueId; } } return result; }
void instrCodeNode::prepareForSampling( const pdvector<threadMetFocusNode *> &thrNodes) { if(! instrLoaded()) return; for(unsigned i=0; i<thrNodes.size(); i++) { threadMetFocusNode *curThrNode = thrNodes[i]; V.sampledDataNode->prepareForSampling(curThrNode->getThreadIndex(), curThrNode->getValuePtr()); } #ifdef PAPI if (V.hwEvent != NULL) { V.hwEvent->enable(); } #endif }
void registerSpace::createRegSpaceInt(pdvector<registerSlot *> ®isters, registerSpace *rs) { for (unsigned i = 0; i < registers.size(); i++) { Register reg = registers[i]->number; rs->registers_[reg] = registers[i]; rs->registersByName[registers[i]->name] = registers[i]->number; switch (registers[i]->type) { case registerSlot::GPR: { bool physical = true; #if defined(arch_x86) || defined(arch_x86_64) if (rs->addr_width == 4) physical = false; #endif if (physical) rs->physicalRegisters_[reg] = registers[i]; rs->GPRs_.push_back(registers[i]); break; } case registerSlot::FPR: rs->FPRs_.push_back(registers[i]); break; case registerSlot::SPR: rs->SPRs_.push_back(registers[i]); break; case registerSlot::realReg: rs->physicalRegisters_[reg] = registers[i]; rs->realRegisters_.push_back(registers[i]); break; default: fprintf(stderr, "Error: no match for %d\n", registers[i]->type); assert(0); break; } } }
static void emitNeededCallSaves(codeGen &gen, Register regi, pdvector<Register> &extra_saves) { extra_saves.push_back(regi); switch (regi) { case REGNUM_EAX: emitSimpleInsn(PUSHEAX, gen); break; case REGNUM_EBX: emitSimpleInsn(PUSHEBX, gen); break; case REGNUM_ECX: emitSimpleInsn(PUSHECX, gen); break; case REGNUM_EDX: emitSimpleInsn(PUSHEDX, gen); break; case REGNUM_EDI: emitSimpleInsn(PUSHEDI, gen); break; } }
bool SignalHandler::waitForEvent(pdvector<EventRecord> &events_to_handle) { assert(waitLock); signal_printf("%s[%d]: waitForEvent, events_to_handle(%d), idle_flag %d\n", FILE__, __LINE__, events_to_handle.size(), idle()); while (idle()) { // Our eventlocks are paired mutexes and condition variables; this // is actually _not_ what we want because we want to be able to // wait on different things but have the same global mutex. So we fake it // by carefully unlocking and relocking things. // We now wait until _we_ are signalled by the generator; so we grab // our signal lock, give up the global mutex lock, and then wait; after // we're signalled we take the global mutex before giving up our own // waitLock. waitingForWakeup_ = true; signal_printf("%s[%d]: acquiring waitLock lock...\n", FILE__, __LINE__); waitLock->_Lock(FILE__, __LINE__); signal_printf("%s[%d]: releasing global mutex...\n", FILE__, __LINE__); assert(eventlock->depth() == 1); eventlock->_Unlock(FILE__, __LINE__); signal_printf("%s[%d]: sleeping for activation\n", FILE__, __LINE__); waitLock->_WaitForSignal(FILE__, __LINE__); signal_printf("%s[%d]: woken, reacquiring global lock...\n", FILE__, __LINE__); eventlock->_Lock(FILE__, __LINE__); signal_printf("%s[%d]: woken, releasing waitLock...\n", FILE__, __LINE__); waitLock->_Unlock(FILE__, __LINE__); waitingForWakeup_ = false; } return true; }
// findChangeToLinkMaps: This routine returns a vector of shared objects // that have been deleted or added to the link maps as indicated by // change_type. If an error occurs it sets error_occured to true. bool dynamic_linking::findChangeToLinkMaps(u_int &change_type, pdvector<mapped_object *> &changed_objects) { pdvector<fileDescriptor> new_descs; if (!didLinkMapsChange(change_type, new_descs)) { return false; } const pdvector<mapped_object *> &curr_list = proc->mappedObjects(); #if 0 fprintf(stderr, "CURR_LIST:\n"); for (unsigned foo = 0; foo < curr_list.size(); foo++) { fprintf(stderr, "%d: %s\0x%x\n", foo, curr_list[foo]->fileName().c_str(), curr_list[foo]->codeBase()); } #endif // if change_type is add then figure out what has been added if(change_type == SHAREDOBJECT_ADDED) { // Look for the one that doesn't match for (unsigned int i=0; i < new_descs.size(); i++) { bool found = false; for (unsigned int j = 0; j < curr_list.size(); j++) { #if 0 fprintf(stderr, "Comparing %s/0x%x/0x%x/%s/%d to %s/0x%x/0x%x/%s/%d\n", new_descs[i].file().c_str(), new_descs[i].code(), new_descs[i].data(), new_descs[i].member().c_str(), new_descs[i].pid(), curr_list[j]->getFileDesc().file().c_str(), curr_list[j]->getFileDesc().code(), curr_list[j]->getFileDesc().data(), curr_list[j]->getFileDesc().member().c_str(), curr_list[j]->getFileDesc().pid()); #endif if (new_descs[i] == curr_list[j]->getFileDesc()) { found = true; break; } } if (!found) { #if 0 fprintf(stderr, "Adding %s/%s\n", new_descs[i].file().c_str(), new_descs[i].member().c_str()); #endif mapped_object *newobj = mapped_object::createMappedObject(new_descs[i], proc); if (!newobj) continue; changed_objects.push_back(newobj); // SaveTheWorld bookkeeping #if defined(cap_save_the_world) char *tmpStr = new char[1+strlen(newobj->fileName().c_str())]; strcpy(tmpStr, newobj->fileName().c_str()); if( !strstr(tmpStr, "libdyninstAPI_RT.so") && !strstr(tmpStr, "libelf.so")){ //bperr(" dlopen: %s \n", tmpStr); newobj->openedWithdlopen(); } setlowestSObaseaddr(newobj->codeBase()); delete [] tmpStr; // SaveTheWorld bookkeeping #endif } } } // if change_type is remove then figure out what has been removed else if((change_type == SHAREDOBJECT_REMOVED) && (curr_list.size())) { // Look for the one that's not in descs bool stillThere[curr_list.size()]; for (unsigned k = 0; k < curr_list.size(); k++) stillThere[k] = false; #if defined(os_linux) || defined(os_solaris) // Linux never includes the a.out in its list of libraries. This makes a // certain amount of sense, but is still annoying. // Solaris throws it away; so hey. stillThere[0] = true; #endif for (unsigned int i=0; i < new_descs.size(); i++) { for (unsigned int j = 0; j < curr_list.size(); j++) { if (new_descs[i] == curr_list[j]->getFileDesc()) { stillThere[j] = true; break; } } } for (unsigned l = 0; l < curr_list.size(); l++) { if (!stillThere[l]) { changed_objects.push_back(curr_list[l]); } } } return true; }
/* * BPatch_image::createInstPointAtAddr * * Returns a pointer to a BPatch_point object representing an * instrumentation point at the given address. If the BPatch_function * argument is given it has to be the function that address belongs to or NULL. * The function is used to bypass the function that the address belongs to * The alternative argument is used to retrieve the point if the new point * intersects with another already existing one. * * Returns the pointer to the BPatch_point on success, or NULL upon * failure. * * address The address that the instrumenation point should refer to. */ BPatch_point *BPatch_image::createInstPointAtAddrWithAlt(void *address, BPatch_point** alternative, BPatch_function* bpf) { Address address_int = (Address) address; unsigned i; process *llproc = proc->llproc; int_function *func = NULL; if (bpf) { func = bpf->func; } else { func = llproc->findFuncByAddr(address_int); } if (func == NULL) return NULL; /* See if there is an instPoint at this address */ instPoint *p = NULL; if ((p = func->findInstPByAddr(address_int))) { return proc->findOrCreateBPPoint(NULL, p, BPatch_locInstruction); } /* Look in the regular instPoints of the enclosing function. */ /* This has an interesting side effect: "instrument the first instruction" may return with "entry instrumentation", which can have different semantics. */ // If it's in an uninstrumentable function, just return an error. if (!func->isInstrumentable()) { return NULL; } const pdvector<instPoint *> entries = func->funcEntries(); for (unsigned t = 0; t < entries.size(); t++) { assert(entries[t]); if (entries[t]->match(address_int)) { return proc->findOrCreateBPPoint(NULL, entries[t], BPatch_entry); } } const pdvector<instPoint*> &exits = func->funcExits(); for (i = 0; i < exits.size(); i++) { assert(exits[i]); if (exits[i]->match(address_int)) { return proc->findOrCreateBPPoint(NULL, exits[i], BPatch_exit); } } const pdvector<instPoint*> &calls = func->funcCalls(); for (i = 0; i < calls.size(); i++) { assert(calls[i]); if (calls[i]->match(address_int)) { return proc->findOrCreateBPPoint(NULL, calls[i], BPatch_subroutine); } } if(alternative) *alternative = NULL; /* We don't have an instPoint for this address, so make one. */ instPoint *newInstP = instPoint::createArbitraryInstPoint(address_int, proc->llproc); if (!newInstP) return NULL; return proc->findOrCreateBPPoint(NULL, newInstP, BPatch_locInstruction); }
// processLinkMaps: get a list of all loaded objects in fileDescriptor form. bool dynamic_linking::processLinkMaps(pdvector<fileDescriptor> &descs) { int proc_fd = proc->getRepresentativeLWP()->get_fd(); if(!proc_fd){ return false;} // step 2: get the runtime loader table from the process Address ldr_base_addr; ldr_context first; ldr_module module; assert(proc->readDataSpace((const void*)LDR_BASE_ADDR,sizeof(Address), &ldr_base_addr, true)); assert(proc->readDataSpace((const void*)ldr_base_addr,sizeof(ldr_context), &first, true)); assert(proc->readDataSpace((const void *) first.head,sizeof(ldr_module), &module, true)); while (module.next != first.head) { if (module.nregions == 0) { assert(proc->readDataSpace((const void *) module.next,sizeof(ldr_module), &module,true)); continue; } pdstring obj_name = pdstring(readDataString(proc, module.name)); ldr_region *regions; regions = (ldr_region *) malloc(module.nregions * sizeof(ldr_region)); assert(proc->readDataSpace((const void *) module.regions, sizeof(ldr_region)*module.nregions, regions, true)); long offset = regions[0].mapaddr - regions[0].vaddr; #ifdef notdef if (offset) { bperr("*** shared lib at non-default offset **: "); bperr(" %s\n", obj_name.c_str()); bperr(" offset = %ld\n", offset); } else { bperr("*** shared lib **: "); bperr(" %s\n", obj_name.c_str()); bperr(" at = %ld\n", regions[0].mapaddr); } #endif for (int i=0; i < module.nregions; i++) { long newoffset = regions[i].mapaddr - regions[i].vaddr; if (newoffset != offset) { bperr( "shared lib regions have different offsets\n"); } regions[i].name = (long unsigned) readDataString(proc, (void *) regions[i].name); // bperr(" region %d (%s) ", i, regions[i].name); // bperr("addr = %lx, ", regions[i].vaddr); // bperr("mapped at = %lx, ", regions[i].mapaddr); // bperr("size = %x\n", regions[i].size); } descs.push_back(fileDescriptor(obj_name, offset, offset, true)); free(regions); assert(proc->readDataSpace((const void *) module.next,sizeof(ldr_module), &module,true)); } return true; }
bool dynamic_linking::handleIfDueToSharedObjectMapping(EventRecord &ev, pdvector<mapped_object*> &changed_objects) { struct dyn_saved_regs regs; // multi-threaded: possible one of many threads hit the breakpoint pdvector<Frame> activeFrames; if (!proc->getAllActiveFrames(activeFrames)) { return false; } dyn_lwp *brk_lwp = NULL; sharedLibHook *hook = NULL; for (unsigned frame_iter = 0; frame_iter < activeFrames.size();frame_iter++) { hook = reachedLibHook(activeFrames[frame_iter].getPC()); if (hook) { brk_lwp = activeFrames[frame_iter].getLWP(); break; } } if (brk_lwp || force_library_load) { // find out what has changed in the link map // and process it r_debug debug_elm; if(!proc->readDataSpace((caddr_t)(r_debug_addr), sizeof(r_debug),(caddr_t)&(debug_elm),true)) { // bperr("read failed r_debug_addr = 0x%x\n",r_debug_addr); return false; } // if the state of the link maps is consistent then we can read // the link maps, otherwise just set the r_state value ev.what = r_state; // previous state of link maps r_state = debug_elm.r_state; // new state of link maps if( debug_elm.r_state == 0) { // figure out how link maps have changed, and then create // a list of either all the removed shared objects if this // was a dlclose or the added shared objects if this was a dlopen // kludge: the state of the first add can get screwed up // so if both change_type and r_state are 0 set change_type to 1 if(ev.what == 0) ev.what = SHAREDOBJECT_ADDED; findChangeToLinkMaps((u_int &)ev.what, changed_objects); } // Don't need to reset PC if (!force_library_load) { assert(brk_lwp); // Get the registers for this lwp brk_lwp->getRegisters(®s); #if defined(arch_sparc) // change the pc so that it will look like the retl instr // completed: set PC to o7 in current frame // we can do this because this retl doesn't correspond to // an instrumentation point, so we don't have to worry about // missing any instrumentation code by making it look like the // retl has already happend // first get the value of the stackpointer Address o7reg = regs.theIntRegs[R_O7]; o7reg += 2*instruction::size(); if(!(brk_lwp->changePC(o7reg, NULL))) { // bperr("error in changePC handleIfDueToSharedObjectMapping\n"); return false; } #else //x86 // set the pc to the "ret" instruction Address next_pc = regs[R_PC] + instruction::size(); if (!brk_lwp->changePC(next_pc)) return false; #endif } if (changed_objects.size() == 0) ev.what = 0; return true; } return false; }
Register registerSpace::getScratchRegister(codeGen &gen, pdvector<Register> &excluded, bool noCost, bool realReg) { static int num_allocs = 0; pdvector<registerSlot *> couldBeStolen; pdvector<registerSlot *> couldBeSpilled; debugPrint(); registerSlot *toUse = NULL; regalloc_printf("Allocating register: selection is %s\n", realReg ? (realRegisters_.empty() ? "GPRS" : "Real registers") : "GPRs"); pdvector<registerSlot *> ®s = (realReg ? (realRegisters_.empty() ? GPRs_ : realRegisters_ ) : GPRs_ ); regalloc_printf("%d options in registers\n", regs.size()); for (unsigned i = 0; i < regs.size(); i++) { registerSlot *reg = regs[i]; regalloc_printf("%s[%d]: getting scratch register, examining %d of %d: reg %d (%s), offLimits %d, refCount %d, liveState %s, keptValue %d\n", FILE__, __LINE__, i, regs.size(), reg->number, reg->name.c_str(), reg->offLimits, reg->refCount, (reg->liveState == registerSlot::live) ? "live" : ((reg->liveState == registerSlot::dead) ? "dead" : "spilled"), reg->keptValue); bool found = false; for (unsigned int i = 0; i < excluded.size(); ++i) { Register &ex_reg = excluded[i]; if (reg->number == ex_reg) { found = true; break; } } if (found) continue; if (reg->offLimits) continue; if (reg->refCount > 0) continue; if (reg->liveState == registerSlot::live) { // Don't do anything for now, but add to the "could be" list couldBeSpilled.push_back(reg); continue; } if (reg->keptValue) { // As above couldBeStolen.push_back(reg); continue; } // Hey, got one. toUse = reg; break; } if (toUse == NULL) { // Argh. Let's assume spilling is cheaper for (unsigned i = 0; i < couldBeSpilled.size(); i++) { if (spillRegister(couldBeSpilled[i]->number, gen, noCost)) { toUse = couldBeSpilled[i]; break; } } } // Still? if (toUse == NULL) { for (unsigned i = 0; i < couldBeStolen.size(); i++) { if (stealRegister(couldBeStolen[i]->number, gen, noCost)) { toUse = couldBeStolen[i]; break; } } } if (toUse == NULL) { // Crap. // debugPrint(); return REG_NULL; } toUse->alloc_num = num_allocs; num_allocs++; toUse->markUsed(false); gen.markRegDefined(toUse->number); return toUse->number; }
int insnCodeGen::createStackFrame(codeGen &gen, int numRegs, pdvector<Register>& freeReg, pdvector<Register>& excludeReg){ assert(0); //#warning "This function is not implemented yet!" return freeReg.size(); }
bool SignalHandler::handleForkExit(EventRecord &ev, bool &continueHint) { signal_printf("%s[%d]: Welcome to FORK EXIT for process %d\n", FILE__, __LINE__, ev.proc->getPid()); process *proc = ev.proc; // Fork handler time extern pdvector<process*> processVec; int childPid = INFO_TO_PID(ev.info); if (childPid == getpid()) { // this is a special case where the normal createProcess code // has created this process, but the attach routine runs soon // enough that the child (of the mutator) gets a fork exit // event. We don't care about this event, so we just continue // the process - jkh 1/31/00 signal_printf("%s[%d]: received FORK on self pid\n", FILE__, __LINE__); continueHint = true; return true; } else if (childPid > 0) { unsigned int i; for (i=0; i < processVec.size(); i++) { if (processVec[i] && (processVec[i]->getPid() == childPid)) break; } if (i== processVec.size()) { // this is a new child, register it with dyninst // Note: we need to wait for the child process to be created. sleep(1); // For now, we sleep (apparently), but the better solution is to // loop waiting for the child to be created and then attach to it. // We have seen the following order: // Parent exits fork // We get notification -- but no child yet. // Child is created // This seems to be OS dependent on who goes first - parent or child. // We leave the parent paused until the child is finished, // so that we can be sure to copy everything correctly. process *theChild = ev.proc->sh->newProcess(proc, (int) childPid, -1); if (!theChild) return false; proc->handleForkExit(theChild); // This may have been mucked with during the fork callback // If we're still paused, then hit run. It'd be nice if there was a // way to let the user say "stay paused!" -- bernat if (proc->sh->syncRunWhenFinished_ != runRequest) { signal_printf("%s[%d]: running parent post-FORK: overriding syncContinueState\n", FILE__, __LINE__); proc->sh->overrideSyncContinueState(runRequest); } continueHint = true; // Unlike normal, we want to start this guy up running (the user can pause if desired in // the callback) if (theChild->sh->syncRunWhenFinished_ != runRequest) { signal_printf("%s[%d]: running child post-FORK: overriding syncContinueState\n", FILE__, __LINE__); theChild->sh->overrideSyncContinueState(runRequest); } theChild->continueProc(); } } else { // Child signalGenerator may execute this guy ; leave it untouched. // If we've already received the stop (AKA childForkStopAlreadyReceived // is true), then we're getting double-signalled due to odd Linux behavior. // Continue the process. // If not, then set to true and leave paused. signal_printf("%s[%d]: child case in fork handling; stopAlreadyReceived = %d\n", FILE__, __LINE__, proc->sh->childForkStopAlreadyReceived_); // Might be a different signal generator from us... if (proc->sh->childForkStopAlreadyReceived_) { continueHint = true; } } return true; }
int EmitterIA32::emitCallParams(codeGen &gen, const pdvector<AstNodePtr> &operands, func_instance *target, pdvector<Register> &extra_saves, bool noCost) { callType call_conven = target->getCallingConvention(); int estimatedFrameSize = 0; pdvector <Register> srcs; Register ecx_target = REG_NULL, edx_target = REG_NULL; Address unused = ADDR_NULL; const int num_operands = operands.size(); switch (call_conven) { case unknown_call: case cdecl_call: case stdcall_call: //Push all registers onto stack for (unsigned u = 0; u < operands.size(); u++) { Register src = REG_NULL; Address unused = ADDR_NULL; if (!operands[u]->generateCode_phase2( gen, false, unused, src)) assert(0); assert(src != REG_NULL); srcs.push_back(src); } break; case thiscall_call: //Allocate the ecx register for the 'this' parameter if (num_operands) { //result = gen.rs()->allocateSpecificRegister(gen, REGNUM_ECX, false); //if (!result) { // emitNeededCallSaves(gen, REGNUM_ECX, extra_saves); //} if (!operands[0]->generateCode_phase2(gen, noCost, unused, ecx_target)) assert(0); } srcs.push_back(Null_Register); //Push other registers onto the stack for (unsigned u = 1; u < operands.size(); u++) { Register src = REG_NULL; Address unused = ADDR_NULL; if (!operands[u]->generateCode_phase2( gen, false, unused, src)) assert(0); assert(src != REG_NULL); srcs.push_back(src); } break; case fastcall_call: if (num_operands) { //Allocate the ecx register for the first parameter //ecx_target = gen.rs()->allocateSpecificRegister(gen, REGNUM_ECX, false); //if (!ecx_target) { // emitNeededCallSaves(gen, REGNUM_ECX, extra_saves); //} } if (num_operands > 1) { //Allocate the edx register for the second parameter //edx_target = gen.rs()->allocateSpecificRegister(gen, REGNUM_EDX, false); //if (!edx_target) { // emitNeededCallSaves(gen, REGNUM_EDX, extra_saves); //} } if (num_operands) { if (!operands[0]->generateCode_phase2(gen, noCost, unused, ecx_target)) assert(0); } if (num_operands > 1) { if (!operands[1]->generateCode_phase2(gen, noCost, unused, edx_target)) assert(0); } srcs.push_back(Null_Register); srcs.push_back(Null_Register); //Push other registers onto the stack for (unsigned u = 2; u < operands.size(); u++) { Register src = REG_NULL; Address unused = ADDR_NULL; if (!operands[u]->generateCode_phase2( gen, false, unused, src)) assert(0); assert(src != REG_NULL); srcs.push_back(src); } break; default: fprintf(stderr, "Internal error. Unknown calling convention\n"); assert(0); } // push arguments in reverse order, last argument first // must use int instead of unsigned to avoid nasty underflow problem: for (int i=srcs.size() - 1; i >= 0; i--) { if (srcs[i] == Null_Register) continue; RealRegister r = gen.rs()->loadVirtual(srcs[i], gen); ::emitPush(r, gen); estimatedFrameSize += 4; if (operands[i]->decRefCount()) gen.rs()->freeRegister(srcs[i]); } if (ecx_target != REG_NULL) { //Store the parameter in ecx gen.rs()->loadVirtualToSpecific(ecx_target, RealRegister(REGNUM_ECX), gen); } if (edx_target != REG_NULL) { gen.rs()->loadVirtualToSpecific(edx_target, RealRegister(REGNUM_EDX), gen); } return estimatedFrameSize; }
void HybridAnalysis::virtualFreeCB(BPatch_point *, void *t) { assert(virtualFreeAddr_ != 0); unsigned type = (unsigned) t; cerr << "virtualFree [" << hex << virtualFreeAddr_ << "," << virtualFreeAddr_ + (unsigned) virtualFreeSize_ << "], " << (unsigned) type << dec << endl; Address pageSize = proc()->lowlevel_process()->getMemoryPageSize(); // Windows page-aligns everything. unsigned addrShift = virtualFreeAddr_ % pageSize; unsigned sizeShift = pageSize - (virtualFreeSize_ % pageSize); virtualFreeAddr_ -= addrShift; if (type != MEM_RELEASE) { virtualFreeSize_ += addrShift + sizeShift; } // We need to: // 1) Remove any function with a block in the deleted range // 2) Remove memory translation for that range // 3) Skip trying to set permissions for any page in the range. // DEBUG! if (1 || type == MEM_RELEASE) { mapped_object *obj = proc()->lowlevel_process()->findObject(virtualFreeAddr_); if (!obj) return; virtualFreeAddr_ = obj->codeBase(); virtualFreeSize_ = obj->imageSize(); // DEBUG! cerr << "Removing VirtualAlloc'ed shared object " << obj->fileName() << endl; image *img = obj->parse_img(); proc()->lowlevel_process()->removeASharedObject(obj); virtualFreeAddr_ = 0; // Since removeASharedObject doesn't actually delete the object, // or its image (even if its refCount==0), make sure the image // goes away from global datastructure allImages for (unsigned int i=0; i < allImages.size(); i++) { if (img == allImages[i]) { allImages[i] = allImages.back(); allImages.pop_back(); } } return; } std::set<func_instance *> deletedFuncs; for (Address i = virtualFreeAddr_; i < (virtualFreeAddr_ + virtualFreeSize_); ++i) { proc()->lowlevel_process()->findFuncsByAddr(i, deletedFuncs); } for (std::set<func_instance *>::iterator iter = deletedFuncs.begin(); iter != deletedFuncs.end(); ++iter) { BPatch_function * bpfunc = proc()->findOrCreateBPFunc(*iter, NULL); if (!bpfunc) continue; PatchAPI::PatchModifier::remove(bpfunc->lowlevel_func()); } proc()->lowlevel_process()->getMemEm()->removeRegion(virtualFreeAddr_, virtualFreeSize_); // And nuke the RT cache proc()->lowlevel_process()->proc()->flushAddressCache_RT(virtualFreeAddr_, virtualFreeSize_); virtualFreeAddr_ = 0; return; }
// processLinkMaps: This routine is called by getSharedObjects to // process all shared objects that have been mapped into the process's // address space. This routine reads the link maps from the application // process to find the shared object file base mappings. It returns 0 on error. bool dynamic_linking::processLinkMaps(pdvector<fileDescriptor> &descs) { r_debug debug_elm; if(!proc->readDataSpace((caddr_t)(r_debug_addr), sizeof(r_debug),(caddr_t)&(debug_elm),true)) { // bperr("read d_ptr_addr failed r_debug_addr = 0x%lx\n",r_debug_addr); return 0; } // get each link_map object Link_map *next_link_map = debug_elm.r_map; Address next_addr = (Address)next_link_map; while(next_addr != 0){ Link_map link_elm; if(!proc->readDataSpace((caddr_t)(next_addr), sizeof(Link_map),(caddr_t)&(link_elm),true)) { logLine("read next_link_map failed\n"); return 0; } // get file name char f_name[256]; // assume no file names greater than 256 chars // check to see if reading 256 chars will go out of bounds // of data segment u_int f_amount = 256; bool done = false; for(u_int i=0; (i<256) && (!done); i++){ if(!proc->readDataSpace((caddr_t)((u_int)(link_elm.l_name)+i), sizeof(char),(caddr_t)(&(f_name[i])),true)){ } if(f_name[i] == '\0'){ done = true; f_amount = i+1; } } f_name[f_amount-1] = '\0'; pdstring obj_name = pdstring(f_name); parsing_cerr << "dynamicLinking::processLinkMaps(): file name of next shared obj=" << obj_name << endl; // create a mapped_object and add it to the list // kludge: ignore the entry if it has the same name as the // executable file...this seems to be the first link-map entry // VG(09/25/01): also ignore if address is 65536 or name is (unknown) if(obj_name != proc->getAOut()->fileName() && obj_name != proc->getAOut()->fullName() && link_elm.l_addr != 65536 && obj_name != "(unknown)" //strncmp(obj_name.c_str(), "(unknown)", 10) ) { fileDescriptor desc = fileDescriptor(obj_name, link_elm.l_addr, link_elm.l_addr, true); descs.push_back(desc); } next_addr = (Address)link_elm.l_next; } return true; }