Added support for rewriting objc_msgSend so we can
call Objective-C methods from expressions. Also added
some more logging to the function-calling thread plan
so that we can see the registers when a function
finishes.
Also documented things maybe a bit better.
git-svn-id: https://llvm.org/svn/llvm-project/llvdb/trunk@109938 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/source/Expression/IRForTarget.cpp b/source/Expression/IRForTarget.cpp
index fb28a2e..995c42f 100644
--- a/source/Expression/IRForTarget.cpp
+++ b/source/Expression/IRForTarget.cpp
@@ -32,7 +32,8 @@
const TargetData *target_data) :
ModulePass(pid),
m_decl_map(decl_map),
- m_target_data(target_data)
+ m_target_data(target_data),
+ m_sel_registerName(NULL)
{
}
@@ -40,6 +41,165 @@
{
}
+static bool isObjCSelectorRef(Value *V)
+{
+ GlobalVariable *GV = dyn_cast<GlobalVariable>(V);
+
+ if (!GV || !GV->hasName() || !GV->getName().startswith("\01L_OBJC_SELECTOR_REFERENCES_"))
+ return false;
+
+ return true;
+}
+
+bool
+IRForTarget::RewriteObjCSelector(Instruction* selector_load,
+ Module &M)
+{
+ lldb_private::Log *log = lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS);
+
+ LoadInst *load = dyn_cast<LoadInst>(selector_load);
+
+ if (!load)
+ return false;
+
+ // Unpack the message name from the selector. In LLVM IR, an objc_msgSend gets represented as
+ //
+ // %tmp = load i8** @"\01L_OBJC_SELECTOR_REFERENCES_" ; <i8*>
+ // %call = call i8* (i8*, i8*, ...)* @objc_msgSend(i8* %obj, i8* %tmp, ...) ; <i8*>
+ //
+ // where %obj is the object pointer and %tmp is the selector.
+ //
+ // @"\01L_OBJC_SELECTOR_REFERENCES_" is a pointer to a character array called @"\01L_OBJC_METH_VAR_NAME_".
+ // @"\01L_OBJC_METH_VAR_NAME_" contains the string.
+
+ // Find the pointer's initializer (a ConstantExpr with opcode GetElementPtr) and get the string from its target
+
+ GlobalVariable *_objc_selector_references_ = dyn_cast<GlobalVariable>(load->getPointerOperand());
+
+ if (!_objc_selector_references_ || !_objc_selector_references_->hasInitializer())
+ return false;
+
+ Constant *osr_initializer = _objc_selector_references_->getInitializer();
+
+ ConstantExpr *osr_initializer_expr = dyn_cast<ConstantExpr>(osr_initializer);
+
+ if (!osr_initializer_expr || osr_initializer_expr->getOpcode() != Instruction::GetElementPtr)
+ return false;
+
+ Value *osr_initializer_base = osr_initializer_expr->getOperand(0);
+
+ if (!osr_initializer_base)
+ return false;
+
+ // Find the string's initializer (a ConstantArray) and get the string from it
+
+ GlobalVariable *_objc_meth_var_name_ = dyn_cast<GlobalVariable>(osr_initializer_base);
+
+ if (!_objc_meth_var_name_ || !_objc_meth_var_name_->hasInitializer())
+ return false;
+
+ Constant *omvn_initializer = _objc_meth_var_name_->getInitializer();
+
+ ConstantArray *omvn_initializer_array = dyn_cast<ConstantArray>(omvn_initializer);
+
+ if (!omvn_initializer_array->isString())
+ return false;
+
+ std::string omvn_initializer_string = omvn_initializer_array->getAsString();
+
+ if (log)
+ log->Printf("Found Objective-C selector reference %s", omvn_initializer_string.c_str());
+
+ // Construct a call to sel_registerName
+
+ if (!m_sel_registerName)
+ {
+ uint64_t srN_addr;
+
+ if (!m_decl_map->GetFunctionAddress("sel_registerName", srN_addr))
+ return false;
+
+ // Build the function type: struct objc_selector *sel_registerName(uint8_t*)
+
+ // The below code would be "more correct," but in actuality what's required is uint8_t*
+ //Type *sel_type = StructType::get(M.getContext());
+ //Type *sel_ptr_type = PointerType::getUnqual(sel_type);
+ const Type *sel_ptr_type = Type::getInt8PtrTy(M.getContext());
+
+ std::vector <const Type *> srN_arg_types;
+ srN_arg_types.push_back(Type::getInt8PtrTy(M.getContext()));
+ llvm::Type *srN_type = FunctionType::get(sel_ptr_type, srN_arg_types, false);
+
+ // Build the constant containing the pointer to the function
+ const IntegerType *intptr_ty = Type::getIntNTy(M.getContext(),
+ (M.getPointerSize() == Module::Pointer64) ? 64 : 32);
+ PointerType *srN_ptr_ty = PointerType::getUnqual(srN_type);
+ Constant *srN_addr_int = ConstantInt::get(intptr_ty, srN_addr, false);
+ m_sel_registerName = ConstantExpr::getIntToPtr(srN_addr_int, srN_ptr_ty);
+ }
+
+ SmallVector <Value*, 1> srN_arguments;
+
+ Constant *omvn_pointer = ConstantExpr::getBitCast(_objc_meth_var_name_, Type::getInt8PtrTy(M.getContext()));
+
+ srN_arguments.push_back(omvn_pointer);
+
+ CallInst *srN_call = CallInst::Create(m_sel_registerName,
+ srN_arguments.begin(),
+ srN_arguments.end(),
+ "srN",
+ selector_load);
+
+ // Replace the load with the call in all users
+
+ selector_load->replaceAllUsesWith(srN_call);
+
+ selector_load->eraseFromParent();
+
+ return true;
+}
+
+bool
+IRForTarget::rewriteObjCSelectors(Module &M,
+ BasicBlock &BB)
+{
+ lldb_private::Log *log = lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS);
+
+ BasicBlock::iterator ii;
+
+ typedef SmallVector <Instruction*, 2> InstrList;
+ typedef InstrList::iterator InstrIterator;
+
+ InstrList selector_loads;
+
+ for (ii = BB.begin();
+ ii != BB.end();
+ ++ii)
+ {
+ Instruction &inst = *ii;
+
+ if (LoadInst *load = dyn_cast<LoadInst>(&inst))
+ if (isObjCSelectorRef(load->getPointerOperand()))
+ selector_loads.push_back(&inst);
+ }
+
+ InstrIterator iter;
+
+ for (iter = selector_loads.begin();
+ iter != selector_loads.end();
+ ++iter)
+ {
+ if (!RewriteObjCSelector(*iter, M))
+ {
+ if(log)
+ log->PutCString("Couldn't rewrite a reference to an Objective-C selector");
+ return false;
+ }
+ }
+
+ return true;
+}
+
static clang::NamedDecl *
DeclForGlobalValue(Module &module,
GlobalValue *global_value)
@@ -85,10 +245,22 @@
Value *V,
bool Store)
{
+ lldb_private::Log *log = lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS);
+
if (GlobalVariable *global_variable = dyn_cast<GlobalVariable>(V))
- {
+ {
clang::NamedDecl *named_decl = DeclForGlobalValue(M, global_variable);
+ if (!named_decl)
+ {
+ if (isObjCSelectorRef(V))
+ return true;
+
+ if (log)
+ log->Printf("Found global variable %s without metadata", global_variable->getName().str().c_str());
+ return false;
+ }
+
std::string name = named_decl->getName().str();
void *qual_type = NULL;
@@ -134,28 +306,34 @@
return true;
clang::NamedDecl *fun_decl = DeclForGlobalValue(M, fun);
-
- if (!fun_decl)
- {
- if (log)
- log->Printf("Function %s wasn't in the metadata", fun->getName().str().c_str());
- return false;
- }
-
uint64_t fun_addr;
- Value **fun_value_ptr;
+ Value **fun_value_ptr = NULL;
- if (!m_decl_map->GetFunctionInfo(fun_decl, fun_value_ptr, fun_addr))
+ if (fun_decl)
{
- if (log)
- log->Printf("Function %s had no address", fun_decl->getNameAsCString());
- return false;
+ if (!m_decl_map->GetFunctionInfo(fun_decl, fun_value_ptr, fun_addr))
+ {
+ if (log)
+ log->Printf("Function %s had no address", fun_decl->getNameAsCString());
+ return false;
+ }
+ }
+ else
+ {
+ if (!m_decl_map->GetFunctionAddress(fun->getName().str().c_str(), fun_addr))
+ {
+ if (log)
+ log->Printf("Metadataless function %s had no address", fun->getName().str().c_str());
+ return false;
+ }
}
if (log)
- log->Printf("Found %s at %llx", fun_decl->getNameAsCString(), fun_addr);
+ log->Printf("Found %s at %llx", fun->getName().str().c_str(), fun_addr);
- if (!*fun_value_ptr)
+ Value *fun_addr_ptr;
+
+ if (!fun_value_ptr || !*fun_value_ptr)
{
std::vector<const Type*> params;
@@ -165,17 +343,22 @@
FunctionType *fun_ty = FunctionType::get(intptr_ty, params, true);
PointerType *fun_ptr_ty = PointerType::getUnqual(fun_ty);
Constant *fun_addr_int = ConstantInt::get(intptr_ty, fun_addr, false);
- Constant *fun_addr_ptr = ConstantExpr::getIntToPtr(fun_addr_int, fun_ptr_ty);
- *fun_value_ptr = fun_addr_ptr;
+ fun_addr_ptr = ConstantExpr::getIntToPtr(fun_addr_int, fun_ptr_ty);
+
+ if (fun_value_ptr)
+ *fun_value_ptr = fun_addr_ptr;
}
+
+ if (fun_value_ptr)
+ fun_addr_ptr = *fun_value_ptr;
- C->setCalledFunction(*fun_value_ptr);
+ C->setCalledFunction(fun_addr_ptr);
return true;
}
bool
-IRForTarget::runOnBasicBlock(Module &M, BasicBlock &BB)
+IRForTarget::resolveExternals(Module &M, BasicBlock &BB)
{
/////////////////////////////////////////////////////////////////////////
// Prepare the current basic block for execution in the remote process
@@ -192,7 +375,7 @@
if (LoadInst *load = dyn_cast<LoadInst>(&inst))
if (!MaybeHandleVariable(M, load->getPointerOperand(), false))
return false;
-
+
if (StoreInst *store = dyn_cast<StoreInst>(&inst))
if (!MaybeHandleVariable(M, store->getPointerOperand(), true))
return false;
@@ -409,7 +592,7 @@
}
bool
-IRForTarget::replaceVariables(Module &M, Function *F)
+IRForTarget::replaceVariables(Module &M, Function &F)
{
lldb_private::Log *log = lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS);
@@ -427,9 +610,9 @@
if (!m_decl_map->GetStructInfo (num_elements, size, alignment))
return false;
- Function::arg_iterator iter(F->getArgumentList().begin());
+ Function::arg_iterator iter(F.getArgumentList().begin());
- if (iter == F->getArgumentList().end())
+ if (iter == F.getArgumentList().end())
return false;
Argument *argument = iter;
@@ -440,7 +623,7 @@
if (log)
log->Printf("Arg: %s", PrintValue(argument).c_str());
- BasicBlock &entry_block(F->getEntryBlock());
+ BasicBlock &entry_block(F.getEntryBlock());
Instruction *first_entry_instruction(entry_block.getFirstNonPHIOrDbg());
if (!first_entry_instruction)
@@ -500,21 +683,29 @@
Function::iterator bbi;
+ //////////////////////////////////
+ // Run basic-block level passes
+ //
+
for (bbi = function->begin();
bbi != function->end();
++bbi)
{
- if (!runOnBasicBlock(M, *bbi))
+ if (!rewriteObjCSelectors(M, *bbi))
+ return false;
+
+ if (!resolveExternals(M, *bbi))
return false;
if (!removeGuards(M, *bbi))
return false;
}
- // TEMPORARY FOR DEBUGGING
- M.dump();
+ ///////////////////////////////
+ // Run function-level passes
+ //
- if (!replaceVariables(M, function))
+ if (!replaceVariables(M, *function))
return false;
if (log)