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.

llvm-svn: 109938
This commit is contained in:
Sean Callanan 2010-07-31 01:32:05 +00:00
parent 0e11955c7a
commit 5300d37aa7
5 changed files with 303 additions and 68 deletions

View File

@ -29,33 +29,6 @@ namespace llvm {
namespace lldb_private {
//----------------------------------------------------------------------
// For cases in which there are multiple classes of types that are not
// interchangeable, to allow static type checking.
//----------------------------------------------------------------------
template <unsigned int C> class TaggedClangASTType : public ClangASTType
{
public:
TaggedClangASTType (void *type, clang::ASTContext *ast_context) :
ClangASTType(type, ast_context) { }
TaggedClangASTType (const TaggedClangASTType<C> &tw) :
ClangASTType(tw) { }
TaggedClangASTType () :
ClangASTType() { }
~TaggedClangASTType() { }
const TaggedClangASTType<C> &
operator= (const TaggedClangASTType<C> &tw)
{
ClangASTType::operator= (tw);
return *this;
}
};
class Error;
class Function;
class NameSearchContext;
@ -92,6 +65,9 @@ public:
llvm::Value**& value,
uint64_t &ptr);
bool GetFunctionAddress (const char *name,
uint64_t &ptr);
// Interface for DwarfExpression
Value *GetValueForIndex (uint32_t index);
@ -112,8 +88,8 @@ public:
void GetDecls (NameSearchContext &context,
const char *name);
private:
typedef TaggedClangASTType<0> TypeFromParser;
typedef TaggedClangASTType<1> TypeFromUser;
typedef TaggedASTType<0> TypeFromParser;
typedef TaggedASTType<1> TypeFromUser;
struct Tuple
{

View File

@ -15,7 +15,9 @@
namespace llvm {
class BasicBlock;
class CallInst;
class Constant;
class Function;
class Instruction;
class Module;
class TargetData;
class Value;
@ -37,22 +39,36 @@ public:
llvm::PassManagerType T = llvm::PMT_ModulePassManager);
llvm::PassManagerType getPotentialPassManagerType() const;
private:
// pass to rewrite Objective-C method calls to use the runtime function
// sel_registerName
bool RewriteObjCSelector(llvm::Instruction* selector_load,
llvm::Module &M);
bool rewriteObjCSelectors(llvm::Module &M,
llvm::BasicBlock &BB);
// pass to register referenced variables and redirect functions at their
// targets in the debugged process
bool MaybeHandleVariable(llvm::Module &M,
llvm::Value *V,
bool Store);
bool MaybeHandleCall(llvm::Module &M,
llvm::CallInst *C);
bool runOnBasicBlock(llvm::Module &M,
llvm::BasicBlock &BB);
bool resolveExternals(llvm::Module &M,
llvm::BasicBlock &BB);
// pass to find references to guard variables and excise them
bool removeGuards(llvm::Module &M,
llvm::BasicBlock &BB);
// pass to replace all identified variables with references to members of
// the argument struct
bool replaceVariables(llvm::Module &M,
llvm::Function *F);
bool replaceFunctions(llvm::Module &M,
llvm::Function *F);
llvm::Function &F);
lldb_private::ClangExpressionDeclMap *m_decl_map;
const llvm::TargetData *m_target_data;
llvm::Constant *m_sel_registerName;
};
#endif
#endif

View File

@ -204,6 +204,39 @@ ClangExpressionDeclMap::GetFunctionInfo (const clang::NamedDecl *decl,
return false;
}
bool
ClangExpressionDeclMap::GetFunctionAddress (const char *name,
uint64_t &ptr)
{
// Back out in all cases where we're not fully initialized
if (!m_exe_ctx || !m_exe_ctx->frame || !m_sym_ctx)
return false;
ConstString name_cs(name);
SymbolContextList sym_ctxs;
m_sym_ctx->FindFunctionsByName(name_cs, false, sym_ctxs);
if (!sym_ctxs.GetSize())
return false;
SymbolContext sym_ctx;
sym_ctxs.GetContextAtIndex(0, sym_ctx);
const Address *fun_address;
if (sym_ctx.function)
fun_address = &sym_ctx.function->GetAddressRange().GetBaseAddress();
else if (sym_ctx.symbol)
fun_address = &sym_ctx.symbol->GetAddressRangeRef().GetBaseAddress();
else
return false;
ptr = fun_address->GetLoadAddress(m_exe_ctx->process);
return true;
}
// Interface for DwarfExpression
lldb_private::Value
*ClangExpressionDeclMap::GetValueForIndex (uint32_t index)
@ -547,7 +580,7 @@ ClangExpressionDeclMap::FindVariableInScope(const SymbolContext &sym_ctx,
if (type->GetASTContext() == var->GetType()->GetClangAST())
{
if (!ClangASTContext::AreTypesSame(type->GetASTContext(), type, var->GetType()->GetOpaqueClangQualType()))
if (!ClangASTContext::AreTypesSame(type->GetASTContext(), type->GetOpaqueQualType(), var->GetType()->GetOpaqueClangQualType()))
continue;
}
else
@ -778,7 +811,7 @@ ClangExpressionDeclMap::AddOneVariable(NameSearchContext &context,
m_tuples.push_back(tuple);
if (log)
log->PutCString("Found variable");
log->Printf("Found variable %s, returned (NamedDecl)%p", context.Name.getAsString().c_str(), var_decl);
}
void
@ -851,5 +884,5 @@ ClangExpressionDeclMap::AddOneFunction(NameSearchContext &context,
m_tuples.push_back(tuple);
if (log)
log->PutCString("Found function");
log->Printf("Found function %s, returned (NamedDecl)%p", context.Name.getAsString().c_str(), fun_decl);
}

View File

@ -32,7 +32,8 @@ IRForTarget::IRForTarget(const void *pid,
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 @@ IRForTarget::~IRForTarget()
{
}
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 @@ IRForTarget::MaybeHandleVariable(Module &M,
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 @@ IRForTarget::MaybeHandleCall(Module &M,
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 @@ IRForTarget::MaybeHandleCall(Module &M,
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 @@ IRForTarget::runOnBasicBlock(Module &M, BasicBlock &BB)
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 @@ UnfoldConstant(Constant *C, Value *new_value, Instruction *first_entry_instructi
}
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 @@ IRForTarget::replaceVariables(Module &M, Function *F)
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 @@ IRForTarget::replaceVariables(Module &M, Function *F)
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 @@ IRForTarget::runOnModule(Module &M)
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)

View File

@ -181,6 +181,25 @@ ThreadPlanCallFunction::ShouldStop (Event *event_ptr)
{
if (PlanExplainsStop())
{
Log *log = lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_STEP);
if (log)
{
RegisterContext *reg_ctx = m_thread.GetRegisterContext();
log->PutCString("Function completed. Register state was:");
for (uint32_t register_index = 0, num_registers = reg_ctx->GetRegisterCount();
register_index < num_registers;
++register_index)
{
const char *register_name = reg_ctx->GetRegisterName(register_index);
uint64_t register_value = reg_ctx->ReadRegisterAsUnsigned(register_index, LLDB_INVALID_ADDRESS);
log->Printf(" %s = 0x%llx", register_name, register_value);
}
}
m_thread.RestoreSaveFrameZero(m_register_backup);
m_thread.ClearStackFrames();
SetPlanComplete();