| //===-- AppleObjCRuntimeV2.cpp --------------------------------------*- C++ -*-===// |
| // |
| // The LLVM Compiler Infrastructure |
| // |
| // This file is distributed under the University of Illinois Open Source |
| // License. See LICENSE.TXT for details. |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "AppleObjCRuntimeV2.h" |
| #include "AppleObjCTrampolineHandler.h" |
| |
| #include "lldb/Core/ConstString.h" |
| #include "lldb/Core/Error.h" |
| #include "lldb/Core/Log.h" |
| #include "lldb/Core/Module.h" |
| #include "lldb/Core/PluginManager.h" |
| #include "lldb/Core/Scalar.h" |
| #include "lldb/Core/StreamString.h" |
| #include "lldb/Expression/ClangFunction.h" |
| #include "lldb/Symbol/ClangASTContext.h" |
| #include "lldb/Target/ExecutionContext.h" |
| #include "lldb/Target/Process.h" |
| #include "lldb/Target/RegisterContext.h" |
| #include "lldb/Target/Target.h" |
| #include "lldb/Target/Thread.h" |
| |
| #include <vector> |
| |
| using namespace lldb; |
| using namespace lldb_private; |
| |
| static const char *pluginName = "AppleObjCRuntimeV2"; |
| static const char *pluginDesc = "Apple Objective C Language Runtime - Version 2"; |
| static const char *pluginShort = "language.apple.objc.v2"; |
| |
| bool |
| AppleObjCRuntimeV2::GetObjectDescription (Stream &str, ValueObject &object, ExecutionContextScope *exe_scope) |
| { |
| ExecutionContext exe_ctx; |
| exe_scope->Calculate(exe_ctx); |
| |
| if (!exe_ctx.process) |
| return false; |
| |
| // We need other parts of the exe_ctx, but the processes have to match. |
| assert (m_process == exe_ctx.process); |
| |
| // ObjC objects can only be pointers: |
| if (!ClangASTContext::IsPointerType (object.GetOpaqueClangQualType())) |
| return NULL; |
| |
| // Get the function address for the print function. |
| const Address *function_address = GetPrintForDebuggerAddr(); |
| if (!function_address) |
| return false; |
| |
| // Make the argument list: we pass one arg, the address of our pointer, to the print function. |
| Scalar scalar; |
| |
| if (!ClangASTType::GetValueAsScalar (object.GetClangAST(), |
| object.GetOpaqueClangQualType(), |
| object.GetDataExtractor(), |
| 0, |
| object.GetByteSize(), |
| scalar)) |
| return NULL; |
| |
| Value val(scalar); |
| val.SetContext(Value::eContextTypeOpaqueClangQualType, |
| ClangASTContext::GetVoidPtrType(object.GetClangAST(), false)); |
| |
| ValueList arg_value_list; |
| arg_value_list.PushValue(val); |
| |
| // This is the return value: |
| const char *target_triple = exe_ctx.process->GetTargetTriple().GetCString(); |
| ClangASTContext *ast_context = exe_ctx.target->GetScratchClangASTContext(); |
| |
| void *return_qualtype = ast_context->GetCStringType(true); |
| Value ret; |
| ret.SetContext(Value::eContextTypeOpaqueClangQualType, return_qualtype); |
| |
| // Now we're ready to call the function: |
| ClangFunction func(target_triple, ast_context, return_qualtype, *function_address, arg_value_list); |
| StreamString error_stream; |
| |
| lldb::addr_t wrapper_struct_addr = LLDB_INVALID_ADDRESS; |
| func.InsertFunction(exe_ctx, wrapper_struct_addr, error_stream); |
| |
| ClangFunction::ExecutionResults results |
| = func.ExecuteFunction(exe_ctx, &wrapper_struct_addr, error_stream, true, 1000, true, ret); |
| if (results != ClangFunction::eExecutionCompleted) |
| { |
| str.Printf("Error evaluating Print Object function: %d.\n", results); |
| return false; |
| } |
| |
| addr_t result_ptr = ret.GetScalar().ULongLong(LLDB_INVALID_ADDRESS); |
| |
| // FIXME: poor man's strcpy - we should have a "read memory as string interface... |
| |
| Error error; |
| std::vector<char> desc; |
| while (1) |
| { |
| char byte = '\0'; |
| if (exe_ctx.process->ReadMemory(result_ptr + desc.size(), &byte, 1, error) != 1) |
| break; |
| |
| desc.push_back(byte); |
| |
| if (byte == '\0') |
| break; |
| } |
| |
| if (!desc.empty()) |
| { |
| str.PutCString(&desc.front()); |
| return true; |
| } |
| return false; |
| |
| } |
| |
| Address * |
| AppleObjCRuntimeV2::GetPrintForDebuggerAddr() |
| { |
| if (!m_PrintForDebugger_addr.get()) |
| { |
| ModuleList &modules = m_process->GetTarget().GetImages(); |
| |
| SymbolContextList contexts; |
| SymbolContext context; |
| |
| if((!modules.FindSymbolsWithNameAndType(ConstString ("_NSPrintForDebugger"), eSymbolTypeCode, contexts)) && |
| (!modules.FindSymbolsWithNameAndType(ConstString ("_CFPrintForDebugger"), eSymbolTypeCode, contexts))) |
| return NULL; |
| |
| contexts.GetContextAtIndex(0, context); |
| |
| m_PrintForDebugger_addr.reset(new Address(context.symbol->GetValue())); |
| } |
| |
| return m_PrintForDebugger_addr.get(); |
| } |
| |
| lldb::ValueObjectSP |
| AppleObjCRuntimeV2::GetDynamicValue (lldb::ValueObjectSP in_value, ExecutionContextScope *exe_scope) |
| { |
| lldb::ValueObjectSP ret_sp; |
| return ret_sp; |
| } |
| |
| bool |
| AppleObjCRuntimeV2::IsModuleObjCLibrary (const ModuleSP &module_sp) |
| { |
| const FileSpec &module_file_spec = module_sp->GetFileSpec(); |
| static ConstString ObjCName ("libobjc.A.dylib"); |
| |
| if (module_file_spec) |
| { |
| if (module_file_spec.GetFilename() == ObjCName) |
| return true; |
| } |
| |
| return false; |
| } |
| |
| bool |
| AppleObjCRuntimeV2::ReadObjCLibrary (const ModuleSP &module_sp) |
| { |
| // Maybe check here and if we have a handler already, and the UUID of this module is the same as the one in the |
| // current module, then we don't have to reread it? |
| m_objc_trampoline_handler_ap.reset(new AppleObjCTrampolineHandler (m_process->GetSP(), module_sp)); |
| if (m_objc_trampoline_handler_ap.get() != NULL) |
| { |
| m_read_objc_library = true; |
| return true; |
| } |
| else |
| return false; |
| } |
| |
| ThreadPlanSP |
| AppleObjCRuntimeV2::GetStepThroughTrampolinePlan (Thread &thread, bool stop_others) |
| { |
| ThreadPlanSP thread_plan_sp; |
| thread_plan_sp = m_objc_trampoline_handler_ap->GetStepThroughDispatchPlan (thread, stop_others); |
| return thread_plan_sp; |
| } |
| |
| //------------------------------------------------------------------ |
| // Static Functions |
| //------------------------------------------------------------------ |
| lldb_private::LanguageRuntime * |
| AppleObjCRuntimeV2::CreateInstance (Process *process, lldb::LanguageType language) |
| { |
| // FIXME: This should be a MacOS or iOS process, and we need to look for the OBJC section to make |
| // sure we aren't using the V1 runtime. |
| if (language == eLanguageTypeObjC) |
| return new AppleObjCRuntimeV2 (process); |
| else |
| return NULL; |
| } |
| |
| void |
| AppleObjCRuntimeV2::Initialize() |
| { |
| PluginManager::RegisterPlugin (pluginName, |
| pluginDesc, |
| CreateInstance); |
| } |
| |
| void |
| AppleObjCRuntimeV2::Terminate() |
| { |
| PluginManager::UnregisterPlugin (CreateInstance); |
| } |
| |
| //------------------------------------------------------------------ |
| // PluginInterface protocol |
| //------------------------------------------------------------------ |
| const char * |
| AppleObjCRuntimeV2::GetPluginName() |
| { |
| return pluginName; |
| } |
| |
| const char * |
| AppleObjCRuntimeV2::GetShortPluginName() |
| { |
| return pluginShort; |
| } |
| |
| uint32_t |
| AppleObjCRuntimeV2::GetPluginVersion() |
| { |
| return 1; |
| } |
| |
| void |
| AppleObjCRuntimeV2::GetPluginCommandHelp (const char *command, Stream *strm) |
| { |
| } |
| |
| Error |
| AppleObjCRuntimeV2::ExecutePluginCommand (Args &command, Stream *strm) |
| { |
| Error error; |
| error.SetErrorString("No plug-in command are currently supported."); |
| return error; |
| } |
| |
| Log * |
| AppleObjCRuntimeV2::EnablePluginLogging (Stream *strm, Args &command) |
| { |
| return NULL; |
| } |