diff options
Diffstat (limited to 'lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCTrampolineHandler.cpp')
-rw-r--r-- | lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCTrampolineHandler.cpp | 145 |
1 files changed, 104 insertions, 41 deletions
diff --git a/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCTrampolineHandler.cpp b/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCTrampolineHandler.cpp index 36f95c063b81..c96768c9f585 100644 --- a/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCTrampolineHandler.cpp +++ b/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCTrampolineHandler.cpp @@ -1,5 +1,4 @@ -//===-- AppleObjCTrampolineHandler.cpp ----------------------------*- C++ -//-*-===// +//===-- AppleObjCTrampolineHandler.cpp ------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -10,6 +9,7 @@ #include "AppleObjCTrampolineHandler.h" #include "AppleThreadPlanStepThroughObjCTrampoline.h" +#include "Plugins/TypeSystem/Clang/TypeSystemClang.h" #include "lldb/Breakpoint/StoppointCallbackContext.h" #include "lldb/Core/Debugger.h" #include "lldb/Core/Module.h" @@ -19,7 +19,6 @@ #include "lldb/Expression/FunctionCaller.h" #include "lldb/Expression/UserExpression.h" #include "lldb/Expression/UtilityFunction.h" -#include "lldb/Symbol/ClangASTContext.h" #include "lldb/Symbol/Symbol.h" #include "lldb/Target/ABI.h" #include "lldb/Target/ExecutionContext.h" @@ -319,7 +318,7 @@ void AppleObjCTrampolineHandler::AppleObjCVTables::VTableRegion::SetUpRegion() { const uint16_t descriptor_size = data.GetU16(&offset); const size_t num_descriptors = data.GetU32(&offset); - m_next_region = data.GetPointer(&offset); + m_next_region = data.GetAddress(&offset); // If the header size is 0, that means we've come in too early before this // data is set up. @@ -521,8 +520,8 @@ bool AppleObjCTrampolineHandler::AppleObjCVTables::RefreshTrampolines( Process *process = exe_ctx.GetProcessPtr(); const ABI *abi = process->GetABI().get(); - ClangASTContext *clang_ast_context = - ClangASTContext::GetScratch(process->GetTarget()); + TypeSystemClang *clang_ast_context = + TypeSystemClang::GetScratch(process->GetTarget()); if (!clang_ast_context) return false; @@ -548,7 +547,7 @@ bool AppleObjCTrampolineHandler::AppleObjCVTables::RefreshTrampolines( error = argument_values.GetValueAtIndex(0)->GetValueAsData(&exe_ctx, data, nullptr); lldb::offset_t offset = 0; - lldb::addr_t region_addr = data.GetPointer(&offset); + lldb::addr_t region_addr = data.GetAddress(&offset); if (region_addr != 0) vtable_handler->ReadRegions(region_addr); @@ -657,6 +656,27 @@ const AppleObjCTrampolineHandler::DispatchFunction DispatchFunction::eFixUpFixed}, }; +// This is the table of ObjC "accelerated dispatch" functions. They are a set +// of objc methods that are "seldom overridden" and so the compiler replaces the +// objc_msgSend with a call to one of the dispatch functions. That will check +// whether the method has been overridden, and directly call the Foundation +// implementation if not. +// This table is supposed to be complete. If ones get added in the future, we +// will have to add them to the table. +const char *AppleObjCTrampolineHandler::g_opt_dispatch_names[] = { + "objc_alloc", + "objc_autorelease", + "objc_release", + "objc_retain", + "objc_alloc_init", + "objc_allocWithZone", + "objc_opt_class", + "objc_opt_isKindOfClass", + "objc_opt_new", + "objc_opt_respondsToSelector", + "objc_opt_self", +}; + AppleObjCTrampolineHandler::AppleObjCTrampolineHandler( const ProcessSP &process_sp, const ModuleSP &objc_module_sp) : m_process_wp(), m_objc_module_sp(objc_module_sp), @@ -751,9 +771,24 @@ AppleObjCTrampolineHandler::AppleObjCTrampolineHandler( m_msgSend_map.insert(std::pair<lldb::addr_t, int>(sym_addr, i)); } } + + // Similarly, cache the addresses of the "optimized dispatch" function. + for (size_t i = 0; i != llvm::array_lengthof(g_opt_dispatch_names); i++) { + ConstString name_const_str(g_opt_dispatch_names[i]); + const Symbol *msgSend_symbol = + m_objc_module_sp->FindFirstSymbolWithNameAndType(name_const_str, + eSymbolTypeCode); + if (msgSend_symbol && msgSend_symbol->ValueIsAddress()) { + lldb::addr_t sym_addr = + msgSend_symbol->GetAddressRef().GetOpcodeLoadAddress(target); + + m_opt_dispatch_map.emplace(sym_addr, i); + } + } // Build our vtable dispatch handler here: - m_vtables_up.reset(new AppleObjCVTables(process_sp, m_objc_module_sp)); + m_vtables_up = + std::make_unique<AppleObjCVTables>(process_sp, m_objc_module_sp); if (m_vtables_up) m_vtables_up->ReadRegions(); } @@ -804,8 +839,8 @@ AppleObjCTrampolineHandler::SetupDispatchFunction(Thread &thread, } // Next make the runner function for our implementation utility function. - ClangASTContext *clang_ast_context = - ClangASTContext::GetScratch(thread.GetProcess()->GetTarget()); + TypeSystemClang *clang_ast_context = + TypeSystemClang::GetScratch(thread.GetProcess()->GetTarget()); if (!clang_ast_context) return LLDB_INVALID_ADDRESS; @@ -846,45 +881,53 @@ AppleObjCTrampolineHandler::SetupDispatchFunction(Thread &thread, return args_addr; } +const AppleObjCTrampolineHandler::DispatchFunction * +AppleObjCTrampolineHandler::FindDispatchFunction(lldb::addr_t addr) { + MsgsendMap::iterator pos; + pos = m_msgSend_map.find(addr); + if (pos != m_msgSend_map.end()) { + return &g_dispatch_functions[(*pos).second]; + } + return nullptr; +} + +void +AppleObjCTrampolineHandler::ForEachDispatchFunction( + std::function<void(lldb::addr_t, + const DispatchFunction &)> callback) { + for (auto elem : m_msgSend_map) { + callback(elem.first, g_dispatch_functions[elem.second]); + } +} + ThreadPlanSP AppleObjCTrampolineHandler::GetStepThroughDispatchPlan(Thread &thread, bool stop_others) { ThreadPlanSP ret_plan_sp; lldb::addr_t curr_pc = thread.GetRegisterContext()->GetPC(); - DispatchFunction this_dispatch; - bool found_it = false; + DispatchFunction vtable_dispatch + = {"vtable", 0, false, false, DispatchFunction::eFixUpFixed}; // First step is to look and see if we are in one of the known ObjC // dispatch functions. We've already compiled a table of same, so // consult it. - MsgsendMap::iterator pos; - pos = m_msgSend_map.find(curr_pc); - if (pos != m_msgSend_map.end()) { - this_dispatch = g_dispatch_functions[(*pos).second]; - found_it = true; - } - + const DispatchFunction *this_dispatch = FindDispatchFunction(curr_pc); + // Next check to see if we are in a vtable region: - if (!found_it) { + if (!this_dispatch && m_vtables_up) { uint32_t flags; - if (m_vtables_up) { - found_it = m_vtables_up->IsAddressInVTables(curr_pc, flags); - if (found_it) { - this_dispatch.name = "vtable"; - this_dispatch.stret_return = - (flags & AppleObjCVTables::eOBJC_TRAMPOLINE_STRET) == - AppleObjCVTables::eOBJC_TRAMPOLINE_STRET; - this_dispatch.is_super = false; - this_dispatch.is_super2 = false; - this_dispatch.fixedup = DispatchFunction::eFixUpFixed; - } + if (m_vtables_up->IsAddressInVTables(curr_pc, flags)) { + vtable_dispatch.stret_return = + (flags & AppleObjCVTables::eOBJC_TRAMPOLINE_STRET) == + AppleObjCVTables::eOBJC_TRAMPOLINE_STRET; + this_dispatch = &vtable_dispatch; } } - if (found_it) { + if (this_dispatch) { Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP)); // We are decoding a method dispatch. First job is to pull the @@ -901,7 +944,7 @@ AppleObjCTrampolineHandler::GetStepThroughDispatchPlan(Thread &thread, TargetSP target_sp(thread.CalculateTarget()); - ClangASTContext *clang_ast_context = ClangASTContext::GetScratch(*target_sp); + TypeSystemClang *clang_ast_context = TypeSystemClang::GetScratch(*target_sp); if (!clang_ast_context) return ret_plan_sp; @@ -921,7 +964,7 @@ AppleObjCTrampolineHandler::GetStepThroughDispatchPlan(Thread &thread, // the return struct pointer, and the object is the second, and // the selector is the third. Otherwise the object is the first // and the selector the second. - if (this_dispatch.stret_return) { + if (this_dispatch->stret_return) { obj_index = 1; sel_index = 2; argument_values.PushValue(void_ptr_value); @@ -963,8 +1006,8 @@ AppleObjCTrampolineHandler::GetStepThroughDispatchPlan(Thread &thread, // run-to-address plan directly. Otherwise we have to figure out // where the implementation lives. - if (this_dispatch.is_super) { - if (this_dispatch.is_super2) { + if (this_dispatch->is_super) { + if (this_dispatch->is_super2) { // In the objc_msgSendSuper2 case, we don't get the object // directly, we get a structure containing the object and the // class to which the super message is being sent. So we need @@ -1087,25 +1130,25 @@ AppleObjCTrampolineHandler::GetStepThroughDispatchPlan(Thread &thread, // flag_value.SetContext (Value::eContextTypeClangType, clang_int_type); flag_value.SetCompilerType(clang_int_type); - if (this_dispatch.stret_return) + if (this_dispatch->stret_return) flag_value.GetScalar() = 1; else flag_value.GetScalar() = 0; dispatch_values.PushValue(flag_value); - if (this_dispatch.is_super) + if (this_dispatch->is_super) flag_value.GetScalar() = 1; else flag_value.GetScalar() = 0; dispatch_values.PushValue(flag_value); - if (this_dispatch.is_super2) + if (this_dispatch->is_super2) flag_value.GetScalar() = 1; else flag_value.GetScalar() = 0; dispatch_values.PushValue(flag_value); - switch (this_dispatch.fixedup) { + switch (this_dispatch->fixedup) { case DispatchFunction::eFixUpNone: flag_value.GetScalar() = 0; dispatch_values.PushValue(flag_value); @@ -1135,7 +1178,7 @@ AppleObjCTrampolineHandler::GetStepThroughDispatchPlan(Thread &thread, // stop_others value passed in to us here: const bool trampoline_stop_others = false; ret_plan_sp = std::make_shared<AppleThreadPlanStepThroughObjCTrampoline>( - thread, this, dispatch_values, isa_addr, sel_addr, + thread, *this, dispatch_values, isa_addr, sel_addr, trampoline_stop_others); if (log) { StreamString s; @@ -1144,6 +1187,26 @@ AppleObjCTrampolineHandler::GetStepThroughDispatchPlan(Thread &thread, } } } + + // Finally, check if we have hit an "optimized dispatch" function. This will + // either directly call the base implementation or dispatch an objc_msgSend + // if the method has been overridden. So we just do a "step in/step out", + // setting a breakpoint on objc_msgSend, and if we hit the msgSend, we + // will automatically step in again. That's the job of the + // AppleThreadPlanStepThroughDirectDispatch. + if (!this_dispatch && !ret_plan_sp) { + MsgsendMap::iterator pos; + pos = m_opt_dispatch_map.find(curr_pc); + if (pos != m_opt_dispatch_map.end()) { + + const char *opt_name = g_opt_dispatch_names[(*pos).second]; + + bool trampoline_stop_others = false; + LazyBool step_in_should_stop = eLazyBoolCalculate; + ret_plan_sp = std::make_shared<AppleThreadPlanStepThroughDirectDispatch> ( + thread, *this, opt_name, trampoline_stop_others, step_in_should_stop); + } + } return ret_plan_sp; } |