bool SDFix::fixDestructors2() { bool replaced = false; for (GlobalVariable& gv : module->getGlobalList()) { if (! sd_isVtableName_ref(gv.getName())) continue; else if (! gv.hasInitializer()) continue; Constant* init = gv.getInitializer(); assert(init); ConstantArray* vtable = dyn_cast<ConstantArray>(init); assert(vtable); for(unsigned i=0; i<vtable->getNumOperands(); i++) { Constant* destructor = sd_getDestructorFunction(vtable->getOperand(i)); if (destructor == NULL) continue; // get the type from its name unsigned s = destructor->getName().size(); char type = destructor->getName()[s-3]; assert('0' <= type && type <= '2'); unsigned typeInt = type - '0'; DestructorInfo di(destructor, i); if(di.isDefined) continue; // this only handles the 1 -> 2 conversion assert(typeInt == 1); Function* f1 = di.getFunction(); assert(f1); std::string gv2Name = f1->getName(); unsigned l = gv2Name.length(); gv2Name = gv2Name.replace(l-3, 1, "2"); Function* f2 = module->getFunction(gv2Name); assert(f2 && ! f2->isDeclaration()); sd_print("Replacing %s with %s inside %s\n", f1->getName().data(), gv2Name.c_str(), gv.getName().data()); f1->replaceAllUsesWith(f2); replaced = true; } } return replaced; }
static Constant* sd_getDestructorFunction(Constant* vtblElement) { ConstantExpr* bcExpr = NULL; // if this a constant bitcast expression, this might be a vthunk if ((bcExpr = dyn_cast<ConstantExpr>(vtblElement)) && bcExpr->getOpcode() == BITCAST_OPCODE) { Constant* operand = bcExpr->getOperand(0); // this is a vthunk if (sd_isDestructorName(operand->getName())) { return operand; } } return NULL; }
static Constant* sd_isRTTI(Constant* vtblElement) { ConstantExpr* bcExpr = NULL; // if this a constant bitcast expression, this might be a vthunk if ((bcExpr = dyn_cast<ConstantExpr>(vtblElement)) && bcExpr->getOpcode() == Instruction::BitCast) { Constant* operand = bcExpr->getOperand(0); // this is a vthunk if (operand->getName().startswith("_ZTI")) { return operand; } } return NULL; }
bool SDFix::fixDestructors() { bool replaced = false; for (GlobalVariable& gv : module->getGlobalList()) { if (! sd_isVtableName_ref(gv.getName())) continue; else if (! gv.hasInitializer()) continue; Constant* init = gv.getInitializer(); assert(init); ConstantArray* vtable = dyn_cast<ConstantArray>(init); assert(vtable); // get an idea about the virtual function regions std::vector<vtbl_pair_t> vtblRegions = findSubVtables(vtable); // for each subvtable for(unsigned vtblInd = 0; vtblInd < vtblRegions.size(); vtblInd++) { // record the destructors used in the vtable std::vector<DestructorInfo> destructors(3); vtbl_pair_t p = vtblRegions[vtblInd]; for(unsigned i=p.first; i<p.second; i++) { Constant* destructor = sd_getDestructorFunction(vtable->getOperand(i)); if (destructor == NULL) continue; // get the type from its name unsigned s = destructor->getName().size(); char type = destructor->getName()[s-3]; assert('0' <= type && type <= '2'); unsigned typeInt = type - '0'; // store it temporarily destructors[typeInt] = DestructorInfo(destructor, i); } // deleting destructor should always be defined assert(! destructors[0].needsReplacement()); DestructorInfo* d1 = &destructors[1]; DestructorInfo* d2 = &destructors[2]; // only one of the rest could be undefined assert(! d1->needsReplacement() || ! d2->needsReplacement()); // if complete object destructor is missing... if (d1->needsReplacement()) { std::string gv2Name = d1->function->getName(); unsigned l = gv2Name.length(); gv2Name = gv2Name.replace(l-3, 1, "2"); Function* f1 = d1->getFunction(); assert(f1); Function* f2 = module->getFunction(gv2Name); assert(f2); sd_print("Replacing %s with %s inside %s\n", d1->function->getName().data(), gv2Name.c_str(), gv.getName().data()); f1->replaceAllUsesWith(f2); replaced = true; // if base object destructor is missing... } else if (d2->needsReplacement()) { std::string gv1Name = d2->function->getName(); unsigned l = gv1Name.length(); gv1Name = gv1Name.replace(l-3, 1, "1"); Function* f2 = d2->getFunction(); assert(f2); Function* f1 = module->getFunction(gv1Name); assert(f1); sd_print("Replacing %s with %s inside %s\n", d2->function->getName().data(), gv1Name.c_str(), gv.getName().data()); f2->replaceAllUsesWith(f1); replaced = true; } } } return replaced; }