1180 lines · cpp
1//===-- AppleObjCTrampolineHandler.cpp ------------------------------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8 9#include "AppleObjCTrampolineHandler.h"10#include "AppleThreadPlanStepThroughObjCTrampoline.h"11 12#include "Plugins/TypeSystem/Clang/TypeSystemClang.h"13#include "lldb/Breakpoint/StoppointCallbackContext.h"14#include "lldb/Core/Debugger.h"15#include "lldb/Core/Module.h"16#include "lldb/Core/Value.h"17#include "lldb/Expression/DiagnosticManager.h"18#include "lldb/Expression/FunctionCaller.h"19#include "lldb/Expression/UserExpression.h"20#include "lldb/Expression/UtilityFunction.h"21#include "lldb/Symbol/Symbol.h"22#include "lldb/Target/ABI.h"23#include "lldb/Target/ExecutionContext.h"24#include "lldb/Target/Process.h"25#include "lldb/Target/RegisterContext.h"26#include "lldb/Target/Target.h"27#include "lldb/Target/Thread.h"28#include "lldb/Target/ThreadPlanRunToAddress.h"29#include "lldb/Utility/ConstString.h"30#include "lldb/Utility/FileSpec.h"31#include "lldb/Utility/LLDBLog.h"32#include "lldb/Utility/Log.h"33 34#include "llvm/ADT/STLExtras.h"35#include "llvm/ADT/ScopeExit.h"36 37#include "Plugins/LanguageRuntime/ObjC/ObjCLanguageRuntime.h"38 39#include <memory>40 41using namespace lldb;42using namespace lldb_private;43 44const char *AppleObjCTrampolineHandler::g_lookup_implementation_function_name =45 "__lldb_objc_find_implementation_for_selector";46const char *AppleObjCTrampolineHandler::47 g_lookup_implementation_with_stret_function_code =48 R"(49 if (is_stret) {50 return_struct.impl_addr =51 class_getMethodImplementation_stret (return_struct.class_addr,52 return_struct.sel_addr);53 } else {54 return_struct.impl_addr =55 class_getMethodImplementation (return_struct.class_addr,56 return_struct.sel_addr);57 }58 if (debug)59 printf ("\n*** Returning implementation: %p.\n",60 return_struct.impl_addr);61 62 return return_struct.impl_addr;63}64)";65const char *66 AppleObjCTrampolineHandler::g_lookup_implementation_no_stret_function_code =67 R"(68 return_struct.impl_addr =69 class_getMethodImplementation (return_struct.class_addr,70 return_struct.sel_addr);71 if (debug)72 printf ("\n*** getMethodImpletation for addr: 0x%p sel: 0x%p result: 0x%p.\n",73 return_struct.class_addr, return_struct.sel_addr, return_struct.impl_addr);74 75 return return_struct.impl_addr;76}77)";78 79const char80 *AppleObjCTrampolineHandler::g_lookup_implementation_function_common_code =81 R"(82extern "C"83{84 extern void *class_getMethodImplementation(void *objc_class, void *sel);85 extern void *class_getMethodImplementation_stret(void *objc_class, void *sel);86 extern void * object_getClass (id object);87 extern void * sel_getUid(char *name);88 extern int printf(const char *format, ...);89}90extern "C" void * 91__lldb_objc_find_implementation_for_selector (void *object,92 void *sel,93 int is_str_ptr,94 int is_stret,95 int is_super,96 int is_super2,97 int is_fixup,98 int is_fixed,99 int debug)100{101 struct __lldb_imp_return_struct {102 void *class_addr;103 void *sel_addr;104 void *impl_addr;105 };106 107 struct __lldb_objc_class {108 void *isa;109 void *super_ptr;110 };111 struct __lldb_objc_super {112 void *receiver;113 struct __lldb_objc_class *class_ptr;114 };115 struct __lldb_msg_ref {116 void *dont_know;117 void *sel;118 };119 120 struct __lldb_imp_return_struct return_struct;121 122 if (debug)123 printf ("\n*** Called with obj: %p sel: %p is_str_ptr: %d "124 "is_stret: %d is_super: %d, "125 "is_super2: %d, is_fixup: %d, is_fixed: %d\n",126 object, sel, is_str_ptr, is_stret,127 is_super, is_super2, is_fixup, is_fixed);128 129 if (is_str_ptr) {130 if (debug)131 printf("*** Turning string: '%s'", sel);132 sel = sel_getUid((char *)sel);133 if (debug)134 printf("*** into sel to %p", sel);135 }136 if (is_super) {137 if (is_super2) {138 return_struct.class_addr 139 = ((__lldb_objc_super *) object)->class_ptr->super_ptr;140 } else {141 return_struct.class_addr = ((__lldb_objc_super *) object)->class_ptr;142 }143 if (debug)144 printf("*** Super, class addr: %p\n", return_struct.class_addr);145 } else {146 // This code seems a little funny, but has its reasons...147 // The call to [object class] is here because if this is a class, and has 148 // not been called into yet, we need to do something to force the class to 149 // initialize itself.150 // Then the call to object_getClass will actually return the correct class, 151 // either the class if object is a class instance, or the meta-class if it 152 // is a class pointer.153 void *class_ptr = (void *) [(id) object class];154 return_struct.class_addr = (id) object_getClass((id) object);155 if (debug) {156 if (class_ptr == object) {157 printf ("Found a class object, need to return the meta class %p -> %p\n",158 class_ptr, return_struct.class_addr);159 } else {160 printf ("[object class] returned: %p object_getClass: %p.\n",161 class_ptr, return_struct.class_addr);162 }163 }164 }165 166 if (is_fixup) {167 if (is_fixed) {168 return_struct.sel_addr = ((__lldb_msg_ref *) sel)->sel;169 } else {170 char *sel_name = (char *) ((__lldb_msg_ref *) sel)->sel;171 return_struct.sel_addr = sel_getUid (sel_name);172 if (debug)173 printf ("\n*** Got fixed up selector: %p for name %s.\n",174 return_struct.sel_addr, sel_name);175 }176 } else {177 return_struct.sel_addr = sel;178 }179)";180 181AppleObjCTrampolineHandler::AppleObjCVTables::VTableRegion::VTableRegion(182 AppleObjCVTables *owner, lldb::addr_t header_addr)183 : m_valid(true), m_owner(owner), m_header_addr(header_addr) {184 SetUpRegion();185}186 187AppleObjCTrampolineHandler::~AppleObjCTrampolineHandler() = default;188 189void AppleObjCTrampolineHandler::AppleObjCVTables::VTableRegion::SetUpRegion() {190 // The header looks like:191 //192 // uint16_t headerSize193 // uint16_t descSize194 // uint32_t descCount195 // void * next196 //197 // First read in the header:198 199 char memory_buffer[16];200 ProcessSP process_sp = m_owner->GetProcessSP();201 if (!process_sp)202 return;203 DataExtractor data(memory_buffer, sizeof(memory_buffer),204 process_sp->GetByteOrder(),205 process_sp->GetAddressByteSize());206 size_t actual_size = 8 + process_sp->GetAddressByteSize();207 Status error;208 size_t bytes_read =209 process_sp->ReadMemory(m_header_addr, memory_buffer, actual_size, error);210 if (bytes_read != actual_size) {211 m_valid = false;212 return;213 }214 215 lldb::offset_t offset = 0;216 const uint16_t header_size = data.GetU16(&offset);217 const uint16_t descriptor_size = data.GetU16(&offset);218 const size_t num_descriptors = data.GetU32(&offset);219 220 m_next_region = data.GetAddress(&offset);221 222 // If the header size is 0, that means we've come in too early before this223 // data is set up.224 // Set ourselves as not valid, and continue.225 if (header_size == 0 || num_descriptors == 0) {226 m_valid = false;227 return;228 }229 230 // Now read in all the descriptors:231 // The descriptor looks like:232 //233 // uint32_t offset234 // uint32_t flags235 //236 // Where offset is either 0 - in which case it is unused, or it is237 // the offset of the vtable code from the beginning of the238 // descriptor record. Below, we'll convert that into an absolute239 // code address, since I don't want to have to compute it over and240 // over.241 242 // Ingest the whole descriptor array:243 const lldb::addr_t desc_ptr = m_header_addr + header_size;244 const size_t desc_array_size = num_descriptors * descriptor_size;245 WritableDataBufferSP data_sp(new DataBufferHeap(desc_array_size, '\0'));246 uint8_t *dst = (uint8_t *)data_sp->GetBytes();247 248 DataExtractor desc_extractor(dst, desc_array_size, process_sp->GetByteOrder(),249 process_sp->GetAddressByteSize());250 bytes_read = process_sp->ReadMemory(desc_ptr, dst, desc_array_size, error);251 if (bytes_read != desc_array_size) {252 m_valid = false;253 return;254 }255 256 // The actual code for the vtables will be laid out consecutively, so I also257 // compute the start and end of the whole code block.258 259 offset = 0;260 m_code_start_addr = 0;261 m_code_end_addr = 0;262 263 for (size_t i = 0; i < num_descriptors; i++) {264 lldb::addr_t start_offset = offset;265 uint32_t voffset = desc_extractor.GetU32(&offset);266 uint32_t flags = desc_extractor.GetU32(&offset);267 lldb::addr_t code_addr = desc_ptr + start_offset + voffset;268 m_descriptors.push_back(VTableDescriptor(flags, code_addr));269 270 if (m_code_start_addr == 0 || code_addr < m_code_start_addr)271 m_code_start_addr = code_addr;272 if (code_addr > m_code_end_addr)273 m_code_end_addr = code_addr;274 275 offset = start_offset + descriptor_size;276 }277 // Finally, a little bird told me that all the vtable code blocks278 // are the same size. Let's compute the blocks and if they are all279 // the same add the size to the code end address:280 lldb::addr_t code_size = 0;281 bool all_the_same = true;282 for (size_t i = 0; i < num_descriptors - 1; i++) {283 lldb::addr_t this_size =284 m_descriptors[i + 1].code_start - m_descriptors[i].code_start;285 if (code_size == 0)286 code_size = this_size;287 else {288 if (this_size != code_size)289 all_the_same = false;290 if (this_size > code_size)291 code_size = this_size;292 }293 }294 if (all_the_same)295 m_code_end_addr += code_size;296}297 298bool AppleObjCTrampolineHandler::AppleObjCVTables::VTableRegion::299 AddressInRegion(lldb::addr_t addr, uint32_t &flags) {300 if (!IsValid())301 return false;302 303 if (addr < m_code_start_addr || addr > m_code_end_addr)304 return false;305 306 std::vector<VTableDescriptor>::iterator pos, end = m_descriptors.end();307 for (pos = m_descriptors.begin(); pos != end; pos++) {308 if (addr <= (*pos).code_start) {309 flags = (*pos).flags;310 return true;311 }312 }313 return false;314}315 316void AppleObjCTrampolineHandler::AppleObjCVTables::VTableRegion::Dump(317 Stream &s) {318 s.Printf("Header addr: 0x%" PRIx64 " Code start: 0x%" PRIx64319 " Code End: 0x%" PRIx64 " Next: 0x%" PRIx64 "\n",320 m_header_addr, m_code_start_addr, m_code_end_addr, m_next_region);321 size_t num_elements = m_descriptors.size();322 for (size_t i = 0; i < num_elements; i++) {323 s.Indent();324 s.Printf("Code start: 0x%" PRIx64 " Flags: %d\n",325 m_descriptors[i].code_start, m_descriptors[i].flags);326 }327}328 329AppleObjCTrampolineHandler::AppleObjCVTables::AppleObjCVTables(330 const ProcessSP &process_sp, const ModuleSP &objc_module_sp)331 : m_process_wp(), m_trampoline_header(LLDB_INVALID_ADDRESS),332 m_trampolines_changed_bp_id(LLDB_INVALID_BREAK_ID),333 m_objc_module_sp(objc_module_sp) {334 if (process_sp)335 m_process_wp = process_sp;336}337 338AppleObjCTrampolineHandler::AppleObjCVTables::~AppleObjCVTables() {339 ProcessSP process_sp = GetProcessSP();340 if (process_sp) {341 if (m_trampolines_changed_bp_id != LLDB_INVALID_BREAK_ID)342 process_sp->GetTarget().RemoveBreakpointByID(m_trampolines_changed_bp_id);343 }344}345 346bool AppleObjCTrampolineHandler::AppleObjCVTables::InitializeVTableSymbols() {347 if (m_trampoline_header != LLDB_INVALID_ADDRESS)348 return true;349 350 ProcessSP process_sp = GetProcessSP();351 if (process_sp) {352 Target &target = process_sp->GetTarget();353 354 if (!m_objc_module_sp) {355 for (ModuleSP module_sp : target.GetImages().Modules()) {356 if (ObjCLanguageRuntime::Get(*process_sp)357 ->IsModuleObjCLibrary(module_sp)) {358 m_objc_module_sp = module_sp;359 break;360 }361 }362 }363 364 if (m_objc_module_sp) {365 ConstString trampoline_name("gdb_objc_trampolines");366 const Symbol *trampoline_symbol =367 m_objc_module_sp->FindFirstSymbolWithNameAndType(trampoline_name,368 eSymbolTypeData);369 if (trampoline_symbol != nullptr) {370 m_trampoline_header = trampoline_symbol->GetLoadAddress(&target);371 if (m_trampoline_header == LLDB_INVALID_ADDRESS)372 return false;373 374 // Next look up the "changed" symbol and set a breakpoint on that...375 ConstString changed_name("gdb_objc_trampolines_changed");376 const Symbol *changed_symbol =377 m_objc_module_sp->FindFirstSymbolWithNameAndType(changed_name,378 eSymbolTypeCode);379 if (changed_symbol != nullptr) {380 const Address changed_symbol_addr = changed_symbol->GetAddress();381 if (!changed_symbol_addr.IsValid())382 return false;383 384 lldb::addr_t changed_addr =385 changed_symbol_addr.GetOpcodeLoadAddress(&target);386 if (changed_addr != LLDB_INVALID_ADDRESS) {387 BreakpointSP trampolines_changed_bp_sp =388 target.CreateBreakpoint(changed_addr, true, false);389 if (trampolines_changed_bp_sp) {390 m_trampolines_changed_bp_id = trampolines_changed_bp_sp->GetID();391 trampolines_changed_bp_sp->SetCallback(RefreshTrampolines, this,392 true);393 trampolines_changed_bp_sp->SetBreakpointKind(394 "objc-trampolines-changed");395 return true;396 }397 }398 }399 }400 }401 }402 return false;403}404 405bool AppleObjCTrampolineHandler::AppleObjCVTables::RefreshTrampolines(406 void *baton, StoppointCallbackContext *context, lldb::user_id_t break_id,407 lldb::user_id_t break_loc_id) {408 AppleObjCVTables *vtable_handler = (AppleObjCVTables *)baton;409 if (vtable_handler->InitializeVTableSymbols()) {410 // The Update function is called with the address of an added region. So we411 // grab that address, and412 // feed it into ReadRegions. Of course, our friend the ABI will get the413 // values for us.414 ExecutionContext exe_ctx(context->exe_ctx_ref);415 Process *process = exe_ctx.GetProcessPtr();416 const ABI *abi = process->GetABI().get();417 418 TypeSystemClangSP scratch_ts_sp =419 ScratchTypeSystemClang::GetForTarget(process->GetTarget());420 if (!scratch_ts_sp)421 return false;422 423 ValueList argument_values;424 Value input_value;425 CompilerType clang_void_ptr_type =426 scratch_ts_sp->GetBasicType(eBasicTypeVoid).GetPointerType();427 428 input_value.SetValueType(Value::ValueType::Scalar);429 // input_value.SetContext (Value::eContextTypeClangType,430 // clang_void_ptr_type);431 input_value.SetCompilerType(clang_void_ptr_type);432 argument_values.PushValue(input_value);433 434 bool success =435 abi->GetArgumentValues(exe_ctx.GetThreadRef(), argument_values);436 if (!success)437 return false;438 439 // Now get a pointer value from the zeroth argument.440 Status error;441 DataExtractor data;442 error = argument_values.GetValueAtIndex(0)->GetValueAsData(&exe_ctx, data,443 nullptr);444 lldb::offset_t offset = 0;445 lldb::addr_t region_addr = data.GetAddress(&offset);446 447 if (region_addr != 0)448 vtable_handler->ReadRegions(region_addr);449 }450 return false;451}452 453bool AppleObjCTrampolineHandler::AppleObjCVTables::ReadRegions() {454 // The no argument version reads the start region from the value of455 // the gdb_regions_header, and gets started from there.456 457 m_regions.clear();458 if (!InitializeVTableSymbols())459 return false;460 Status error;461 ProcessSP process_sp = GetProcessSP();462 if (process_sp) {463 lldb::addr_t region_addr =464 process_sp->ReadPointerFromMemory(m_trampoline_header, error);465 if (error.Success())466 return ReadRegions(region_addr);467 }468 return false;469}470 471bool AppleObjCTrampolineHandler::AppleObjCVTables::ReadRegions(472 lldb::addr_t region_addr) {473 ProcessSP process_sp = GetProcessSP();474 if (!process_sp)475 return false;476 477 Log *log = GetLog(LLDBLog::Step);478 479 // We aren't starting at the trampoline symbol.480 InitializeVTableSymbols();481 lldb::addr_t next_region = region_addr;482 483 // Read in the sizes of the headers.484 while (next_region != 0) {485 m_regions.push_back(VTableRegion(this, next_region));486 if (!m_regions.back().IsValid()) {487 m_regions.clear();488 return false;489 }490 if (log) {491 StreamString s;492 m_regions.back().Dump(s);493 LLDB_LOGF(log, "Read vtable region: \n%s", s.GetData());494 }495 496 next_region = m_regions.back().GetNextRegionAddr();497 }498 499 return true;500}501 502bool AppleObjCTrampolineHandler::AppleObjCVTables::IsAddressInVTables(503 lldb::addr_t addr, uint32_t &flags) {504 region_collection::iterator pos, end = m_regions.end();505 for (pos = m_regions.begin(); pos != end; pos++) {506 if ((*pos).AddressInRegion(addr, flags))507 return true;508 }509 return false;510}511 512const AppleObjCTrampolineHandler::DispatchFunction513 AppleObjCTrampolineHandler::g_dispatch_functions[] = {514 // NAME STRET SUPER SUPER2 FIXUP TYPE515 {"objc_msgSend", false, false, false, DispatchFunction::eFixUpNone},516 {"objc_msgSend_fixup", false, false, false,517 DispatchFunction::eFixUpToFix},518 {"objc_msgSend_fixedup", false, false, false,519 DispatchFunction::eFixUpFixed},520 {"objc_msgSend_stret", true, false, false,521 DispatchFunction::eFixUpNone},522 {"objc_msgSend_stret_fixup", true, false, false,523 DispatchFunction::eFixUpToFix},524 {"objc_msgSend_stret_fixedup", true, false, false,525 DispatchFunction::eFixUpFixed},526 {"objc_msgSend_fpret", false, false, false,527 DispatchFunction::eFixUpNone},528 {"objc_msgSend_fpret_fixup", false, false, false,529 DispatchFunction::eFixUpToFix},530 {"objc_msgSend_fpret_fixedup", false, false, false,531 DispatchFunction::eFixUpFixed},532 {"objc_msgSend_fp2ret", false, false, true,533 DispatchFunction::eFixUpNone},534 {"objc_msgSend_fp2ret_fixup", false, false, true,535 DispatchFunction::eFixUpToFix},536 {"objc_msgSend_fp2ret_fixedup", false, false, true,537 DispatchFunction::eFixUpFixed},538 {"objc_msgSendSuper", false, true, false, DispatchFunction::eFixUpNone},539 {"objc_msgSendSuper_stret", true, true, false,540 DispatchFunction::eFixUpNone},541 {"objc_msgSendSuper2", false, true, true, DispatchFunction::eFixUpNone},542 {"objc_msgSendSuper2_fixup", false, true, true,543 DispatchFunction::eFixUpToFix},544 {"objc_msgSendSuper2_fixedup", false, true, true,545 DispatchFunction::eFixUpFixed},546 {"objc_msgSendSuper2_stret", true, true, true,547 DispatchFunction::eFixUpNone},548 {"objc_msgSendSuper2_stret_fixup", true, true, true,549 DispatchFunction::eFixUpToFix},550 {"objc_msgSendSuper2_stret_fixedup", true, true, true,551 DispatchFunction::eFixUpFixed},552};553 554// This is the table of ObjC "accelerated dispatch" functions. They are a set555// of objc methods that are "seldom overridden" and so the compiler replaces the556// objc_msgSend with a call to one of the dispatch functions. That will check557// whether the method has been overridden, and directly call the Foundation 558// implementation if not. 559// This table is supposed to be complete. If ones get added in the future, we560// will have to add them to the table.561const char *AppleObjCTrampolineHandler::g_opt_dispatch_names[] = {562 "objc_alloc",563 "objc_autorelease",564 "objc_release",565 "objc_retain",566 "objc_alloc_init",567 "objc_allocWithZone",568 "objc_opt_class",569 "objc_opt_isKindOfClass",570 "objc_opt_new",571 "objc_opt_respondsToSelector",572 "objc_opt_self",573};574 575AppleObjCTrampolineHandler::AppleObjCTrampolineHandler(576 const ProcessSP &process_sp, const ModuleSP &objc_module_sp)577 : m_process_wp(), m_objc_module_sp(objc_module_sp),578 m_impl_fn_addr(LLDB_INVALID_ADDRESS),579 m_impl_stret_fn_addr(LLDB_INVALID_ADDRESS),580 m_msg_forward_addr(LLDB_INVALID_ADDRESS),581 m_msg_forward_stret_addr(LLDB_INVALID_ADDRESS) {582 if (process_sp)583 m_process_wp = process_sp;584 // Look up the known resolution functions:585 586 ConstString get_impl_name("class_getMethodImplementation");587 ConstString get_impl_stret_name("class_getMethodImplementation_stret");588 ConstString msg_forward_name("_objc_msgForward");589 ConstString msg_forward_stret_name("_objc_msgForward_stret");590 591 Target *target = process_sp ? &process_sp->GetTarget() : nullptr;592 const Symbol *class_getMethodImplementation =593 m_objc_module_sp->FindFirstSymbolWithNameAndType(get_impl_name,594 eSymbolTypeCode);595 const Symbol *class_getMethodImplementation_stret =596 m_objc_module_sp->FindFirstSymbolWithNameAndType(get_impl_stret_name,597 eSymbolTypeCode);598 const Symbol *msg_forward = m_objc_module_sp->FindFirstSymbolWithNameAndType(599 msg_forward_name, eSymbolTypeCode);600 const Symbol *msg_forward_stret =601 m_objc_module_sp->FindFirstSymbolWithNameAndType(msg_forward_stret_name,602 eSymbolTypeCode);603 604 if (class_getMethodImplementation)605 m_impl_fn_addr =606 class_getMethodImplementation->GetAddress().GetOpcodeLoadAddress(607 target);608 if (class_getMethodImplementation_stret)609 m_impl_stret_fn_addr =610 class_getMethodImplementation_stret->GetAddress().GetOpcodeLoadAddress(611 target);612 if (msg_forward)613 m_msg_forward_addr = msg_forward->GetAddress().GetOpcodeLoadAddress(target);614 if (msg_forward_stret)615 m_msg_forward_stret_addr =616 msg_forward_stret->GetAddress().GetOpcodeLoadAddress(target);617 618 // FIXME: Do some kind of logging here.619 if (m_impl_fn_addr == LLDB_INVALID_ADDRESS) {620 // If we can't even find the ordinary get method implementation function,621 // then we aren't going to be able to622 // step through any method dispatches. Warn to that effect and get out of623 // here.624 if (process_sp->CanJIT()) {625 process_sp->GetTarget().GetDebugger().GetAsyncErrorStream()->Printf(626 "Could not find implementation lookup function \"%s\""627 " step in through ObjC method dispatch will not work.\n",628 get_impl_name.AsCString());629 }630 return;631 }632 633 // We will either set the implementation to the _stret or non_stret version,634 // so either way it's safe to start filling the m_lookup_..._code here.635 m_lookup_implementation_function_code.assign(636 g_lookup_implementation_function_common_code);637 638 if (m_impl_stret_fn_addr == LLDB_INVALID_ADDRESS) {639 // It there is no stret return lookup function, assume that it is the same640 // as the straight lookup:641 m_impl_stret_fn_addr = m_impl_fn_addr;642 // Also we will use the version of the lookup code that doesn't rely on the643 // stret version of the function.644 m_lookup_implementation_function_code.append(645 g_lookup_implementation_no_stret_function_code);646 } else {647 m_lookup_implementation_function_code.append(648 g_lookup_implementation_with_stret_function_code);649 }650 651 // Look up the addresses for the objc dispatch functions and cache652 // them. For now I'm inspecting the symbol names dynamically to653 // figure out how to dispatch to them. If it becomes more654 // complicated than this we can turn the g_dispatch_functions char *655 // array into a template table, and populate the DispatchFunction656 // map from there.657 658 for (size_t i = 0; i != std::size(g_dispatch_functions); i++) {659 ConstString name_const_str(g_dispatch_functions[i].name);660 const Symbol *msgSend_symbol =661 m_objc_module_sp->FindFirstSymbolWithNameAndType(name_const_str,662 eSymbolTypeCode);663 if (msgSend_symbol && msgSend_symbol->ValueIsAddress()) {664 // FIXME: Make g_dispatch_functions static table of665 // DispatchFunctions, and have the map be address->index.666 // Problem is we also need to lookup the dispatch function. For667 // now we could have a side table of stret & non-stret dispatch668 // functions. If that's as complex as it gets, we're fine.669 670 lldb::addr_t sym_addr =671 msgSend_symbol->GetAddressRef().GetOpcodeLoadAddress(target);672 673 m_msgSend_map.insert(std::pair<lldb::addr_t, int>(sym_addr, i));674 }675 }676 677 // Similarly, cache the addresses of the "optimized dispatch" function.678 for (size_t i = 0; i != std::size(g_opt_dispatch_names); i++) {679 ConstString name_const_str(g_opt_dispatch_names[i]);680 const Symbol *msgSend_symbol =681 m_objc_module_sp->FindFirstSymbolWithNameAndType(name_const_str,682 eSymbolTypeCode);683 if (msgSend_symbol && msgSend_symbol->ValueIsAddress()) {684 lldb::addr_t sym_addr =685 msgSend_symbol->GetAddressRef().GetOpcodeLoadAddress(target);686 687 m_opt_dispatch_map.emplace(sym_addr, i);688 }689 }690 691 // Build our vtable dispatch handler here:692 m_vtables_up =693 std::make_unique<AppleObjCVTables>(process_sp, m_objc_module_sp);694 if (m_vtables_up)695 m_vtables_up->ReadRegions();696}697 698lldb::addr_t699AppleObjCTrampolineHandler::SetupDispatchFunction(Thread &thread,700 ValueList &dispatch_values) {701 ThreadSP thread_sp(thread.shared_from_this());702 ExecutionContext exe_ctx(thread_sp);703 Log *log = GetLog(LLDBLog::Step);704 705 lldb::addr_t args_addr = LLDB_INVALID_ADDRESS;706 FunctionCaller *impl_function_caller = nullptr;707 708 // Scope for mutex locker:709 {710 std::lock_guard<std::mutex> guard(m_impl_function_mutex);711 712 // First stage is to make the ClangUtility to hold our injected function:713 714 if (!m_impl_code) {715 if (!m_lookup_implementation_function_code.empty()) {716 auto utility_fn_or_error = exe_ctx.GetTargetRef().CreateUtilityFunction(717 m_lookup_implementation_function_code,718 g_lookup_implementation_function_name, eLanguageTypeC, exe_ctx);719 if (!utility_fn_or_error) {720 LLDB_LOG_ERROR(721 log, utility_fn_or_error.takeError(),722 "Failed to get Utility Function for implementation lookup: {0}.");723 return args_addr;724 }725 m_impl_code = std::move(*utility_fn_or_error);726 } else {727 LLDB_LOGF(log, "No method lookup implementation code.");728 return LLDB_INVALID_ADDRESS;729 }730 731 // Next make the runner function for our implementation utility function.732 TypeSystemClangSP scratch_ts_sp = ScratchTypeSystemClang::GetForTarget(733 thread.GetProcess()->GetTarget());734 if (!scratch_ts_sp)735 return LLDB_INVALID_ADDRESS;736 737 CompilerType clang_void_ptr_type =738 scratch_ts_sp->GetBasicType(eBasicTypeVoid).GetPointerType();739 Status error;740 741 impl_function_caller = m_impl_code->MakeFunctionCaller(742 clang_void_ptr_type, dispatch_values, thread_sp, error);743 if (error.Fail()) {744 LLDB_LOGF(log,745 "Error getting function caller for dispatch lookup: \"%s\".",746 error.AsCString());747 return args_addr;748 }749 } else {750 impl_function_caller = m_impl_code->GetFunctionCaller();751 }752 }753 754 // Now write down the argument values for this particular call.755 // This looks like it might be a race condition if other threads756 // were calling into here, but actually it isn't because we allocate757 // a new args structure for this call by passing args_addr =758 // LLDB_INVALID_ADDRESS...759 760 DiagnosticManager diagnostics;761 if (!impl_function_caller->WriteFunctionArguments(762 exe_ctx, args_addr, dispatch_values, diagnostics)) {763 if (log) {764 LLDB_LOGF(log, "Error writing function arguments.");765 diagnostics.Dump(log);766 }767 return args_addr;768 }769 770 return args_addr;771}772 773const AppleObjCTrampolineHandler::DispatchFunction *774AppleObjCTrampolineHandler::FindDispatchFunction(lldb::addr_t addr) {775 MsgsendMap::iterator pos;776 pos = m_msgSend_map.find(addr);777 if (pos != m_msgSend_map.end()) {778 return &g_dispatch_functions[(*pos).second];779 }780 return nullptr;781}782 783void AppleObjCTrampolineHandler::ForEachDispatchFunction(784 std::function<void(lldb::addr_t, const DispatchFunction &)> callback) {785 for (auto elem : m_msgSend_map) {786 callback(elem.first, g_dispatch_functions[elem.second]);787 }788}789 790ThreadPlanSP791AppleObjCTrampolineHandler::GetStepThroughDispatchPlan(Thread &thread,792 bool stop_others) {793 ThreadPlanSP ret_plan_sp;794 lldb::addr_t curr_pc = thread.GetRegisterContext()->GetPC();795 796 DispatchFunction vtable_dispatch = {"vtable", false, false, false,797 DispatchFunction::eFixUpFixed};798 // The selector specific stubs are a wrapper for objc_msgSend. They don't get799 // passed a SEL, but instead the selector string is encoded in the stub800 // name, in the form:801 // objc_msgSend$SelectorName802 // and the stub figures out the uniqued selector. If we find ourselves in803 // one of these stubs, we strip off the selector string and pass that to the804 // implementation finder function, which looks up the SEL (you have to do this805 // in process) and passes that to the runtime lookup function.806 DispatchFunction sel_stub_dispatch = {"sel-specific-stub", false, false,807 false, DispatchFunction::eFixUpNone};808 809 // First step is to see if we're in a selector-specific dispatch stub.810 // Those are of the form _objc_msgSend$<SELECTOR>, so see if the current811 // function has that name:812 Address func_addr;813 Target &target = thread.GetProcess()->GetTarget();814 llvm::StringRef sym_name;815 const DispatchFunction *this_dispatch = nullptr;816 817 if (target.ResolveLoadAddress(curr_pc, func_addr)) {818 Symbol *curr_sym = func_addr.CalculateSymbolContextSymbol();819 if (curr_sym)820 sym_name = curr_sym->GetName().GetStringRef();821 822 if (!sym_name.empty() && !sym_name.consume_front("objc_msgSend$"))823 sym_name = {};824 else825 this_dispatch = &sel_stub_dispatch;826 }827 bool in_selector_stub = !sym_name.empty();828 // Second step is to look and see if we are in one of the known ObjC829 // dispatch functions. We've already compiled a table of same, so830 // consult it.831 832 if (!in_selector_stub)833 this_dispatch = FindDispatchFunction(curr_pc);834 835 // Next check to see if we are in a vtable region:836 837 if (!this_dispatch && m_vtables_up) {838 uint32_t flags;839 if (m_vtables_up->IsAddressInVTables(curr_pc, flags)) {840 vtable_dispatch.stret_return =841 (flags & AppleObjCVTables::eOBJC_TRAMPOLINE_STRET) ==842 AppleObjCVTables::eOBJC_TRAMPOLINE_STRET;843 this_dispatch = &vtable_dispatch;844 }845 }846 847 // Since we set this_dispatch in both the vtable & sel specific stub cases848 // this if will be used for all three of those cases.849 if (this_dispatch) {850 Log *log = GetLog(LLDBLog::Step);851 852 // We are decoding a method dispatch. First job is to pull the853 // arguments out. If we are in a regular stub, we get self & selector,854 // but if we are in a selector-specific stub, we'll have to get that from855 // the string sym_name.856 857 lldb::StackFrameSP thread_cur_frame = thread.GetStackFrameAtIndex(0);858 859 const ABI *abi = nullptr;860 ProcessSP process_sp(thread.CalculateProcess());861 if (process_sp)862 abi = process_sp->GetABI().get();863 if (abi == nullptr)864 return ret_plan_sp;865 866 TargetSP target_sp(thread.CalculateTarget());867 868 TypeSystemClangSP scratch_ts_sp =869 ScratchTypeSystemClang::GetForTarget(*target_sp);870 if (!scratch_ts_sp)871 return ret_plan_sp;872 873 ValueList argument_values;874 Value void_ptr_value;875 CompilerType clang_void_ptr_type =876 scratch_ts_sp->GetBasicType(eBasicTypeVoid).GetPointerType();877 void_ptr_value.SetValueType(Value::ValueType::Scalar);878 // void_ptr_value.SetContext (Value::eContextTypeClangType,879 // clang_void_ptr_type);880 void_ptr_value.SetCompilerType(clang_void_ptr_type);881 882 int obj_index;883 int sel_index;884 885 // If this is a selector-specific stub then just push one value, 'cause886 // we only get the object.887 // If this is a struct return dispatch, then the first argument is888 // the return struct pointer, and the object is the second, and889 // the selector is the third.890 // Otherwise the object is the first and the selector the second.891 if (in_selector_stub) {892 obj_index = 0;893 sel_index = 1;894 argument_values.PushValue(void_ptr_value);895 } else if (this_dispatch->stret_return) {896 obj_index = 1;897 sel_index = 2;898 argument_values.PushValue(void_ptr_value);899 argument_values.PushValue(void_ptr_value);900 argument_values.PushValue(void_ptr_value);901 } else {902 obj_index = 0;903 sel_index = 1;904 argument_values.PushValue(void_ptr_value);905 argument_values.PushValue(void_ptr_value);906 }907 908 bool success = abi->GetArgumentValues(thread, argument_values);909 if (!success)910 return ret_plan_sp;911 912 lldb::addr_t obj_addr =913 argument_values.GetValueAtIndex(obj_index)->GetScalar().ULongLong();914 if (obj_addr == 0x0) {915 LLDB_LOGF(916 log,917 "Asked to step to dispatch to nil object, returning empty plan.");918 return ret_plan_sp;919 }920 921 ExecutionContext exe_ctx(thread.shared_from_this());922 // isa_addr will store the class pointer that the method is being923 // dispatched to - so either the class directly or the super class924 // if this is one of the objc_msgSendSuper flavors. That's mostly925 // used to look up the class/selector pair in our cache.926 927 lldb::addr_t isa_addr = LLDB_INVALID_ADDRESS;928 lldb::addr_t sel_addr = LLDB_INVALID_ADDRESS;929 // If we are not in a selector stub, get the sel address from the arguments.930 if (!in_selector_stub)931 sel_addr =932 argument_values.GetValueAtIndex(sel_index)->GetScalar().ULongLong();933 934 // Figure out the class this is being dispatched to and see if935 // we've already cached this method call, If so we can push a936 // run-to-address plan directly. Otherwise we have to figure out937 // where the implementation lives.938 939 if (this_dispatch->is_super) {940 if (this_dispatch->is_super2) {941 // In the objc_msgSendSuper2 case, we don't get the object942 // directly, we get a structure containing the object and the943 // class to which the super message is being sent. So we need944 // to dig the super out of the class and use that.945 946 Value super_value(*(argument_values.GetValueAtIndex(obj_index)));947 super_value.GetScalar() += process_sp->GetAddressByteSize();948 super_value.ResolveValue(&exe_ctx);949 950 if (super_value.GetScalar().IsValid()) {951 952 // isa_value now holds the class pointer. The second word of the953 // class pointer is the super-class pointer:954 super_value.GetScalar() += process_sp->GetAddressByteSize();955 super_value.ResolveValue(&exe_ctx);956 if (super_value.GetScalar().IsValid())957 isa_addr = super_value.GetScalar().ULongLong();958 else {959 LLDB_LOGF(log, "Failed to extract the super class value from the "960 "class in objc_super.");961 }962 } else {963 LLDB_LOGF(log, "Failed to extract the class value from objc_super.");964 }965 } else {966 // In the objc_msgSendSuper case, we don't get the object967 // directly, we get a two element structure containing the968 // object and the super class to which the super message is969 // being sent. So the class we want is the second element of970 // this structure.971 972 Value super_value(*(argument_values.GetValueAtIndex(obj_index)));973 super_value.GetScalar() += process_sp->GetAddressByteSize();974 super_value.ResolveValue(&exe_ctx);975 976 if (super_value.GetScalar().IsValid()) {977 isa_addr = super_value.GetScalar().ULongLong();978 } else {979 LLDB_LOGF(log, "Failed to extract the class value from objc_super.");980 }981 }982 } else {983 // In the direct dispatch case, the object->isa is the class pointer we984 // want.985 986 // This is a little cheesy, but since object->isa is the first field,987 // making the object value a load address value and resolving it will get988 // the pointer sized data pointed to by that value...989 990 // Note, it isn't a fatal error not to be able to get the991 // address from the object, since this might be a "tagged992 // pointer" which isn't a real object, but rather some word993 // length encoded dingus.994 995 Value isa_value(*(argument_values.GetValueAtIndex(obj_index)));996 997 isa_value.SetValueType(Value::ValueType::LoadAddress);998 isa_value.ResolveValue(&exe_ctx);999 if (isa_value.GetScalar().IsValid()) {1000 isa_addr = isa_value.GetScalar().ULongLong();1001 } else {1002 LLDB_LOGF(log, "Failed to extract the isa value from object.");1003 }1004 }1005 1006 // Okay, we've got the address of the class for which we're resolving this,1007 // let's see if it's in our cache:1008 lldb::addr_t impl_addr = LLDB_INVALID_ADDRESS;1009 // If this is a regular dispatch, look up the sel in our addr to sel cache:1010 if (isa_addr != LLDB_INVALID_ADDRESS) {1011 ObjCLanguageRuntime *objc_runtime =1012 ObjCLanguageRuntime::Get(*thread.GetProcess());1013 assert(objc_runtime != nullptr);1014 if (!in_selector_stub) {1015 LLDB_LOG(log, "Resolving call for class - {0} and selector - {1}",1016 isa_addr, sel_addr);1017 impl_addr = objc_runtime->LookupInMethodCache(isa_addr, sel_addr);1018 } else {1019 LLDB_LOG(log, "Resolving call for class - {0} and selector - {1}",1020 isa_addr, sym_name);1021 impl_addr = objc_runtime->LookupInMethodCache(isa_addr, sym_name);1022 }1023 }1024 // If it is a selector-specific stub dispatch, look in the string cache:1025 1026 if (impl_addr != LLDB_INVALID_ADDRESS) {1027 // Yup, it was in the cache, so we can run to that address directly.1028 1029 LLDB_LOGF(log, "Found implementation address in cache: 0x%" PRIx64,1030 impl_addr);1031 1032 ret_plan_sp = std::make_shared<ThreadPlanRunToAddress>(thread, impl_addr,1033 stop_others);1034 } else {1035 // We haven't seen this class/selector pair yet. Look it up.1036 StreamString errors;1037 Address impl_code_address;1038 1039 ValueList dispatch_values;1040 1041 // We've will inject a little function in the target that takes the1042 // object, selector/selector string and some flags,1043 // and figures out the implementation. Looks like:1044 // void *__lldb_objc_find_implementation_for_selector (void *object,1045 // void *sel,1046 // int1047 // is_str_ptr,1048 // int is_stret,1049 // int is_super,1050 // int is_super2,1051 // int is_fixup,1052 // int is_fixed,1053 // int debug)1054 // If we don't have an actual SEL, but rather a string version of the1055 // selector WE injected, set is_str_ptr to true, and sel to the address1056 // of the string.1057 // So set up the arguments for that call.1058 1059 dispatch_values.PushValue(*(argument_values.GetValueAtIndex(obj_index)));1060 lldb::addr_t sel_str_addr = LLDB_INVALID_ADDRESS;1061 if (!in_selector_stub) {1062 // If we don't have a selector string, push the selector from arguments.1063 dispatch_values.PushValue(1064 *(argument_values.GetValueAtIndex(sel_index)));1065 } else {1066 // Otherwise, inject the string into the target, and push that value for1067 // the sel argument.1068 Status error;1069 sel_str_addr = process_sp->AllocateMemory(1070 sym_name.size() + 1, ePermissionsReadable | ePermissionsWritable,1071 error);1072 if (sel_str_addr == LLDB_INVALID_ADDRESS || error.Fail()) {1073 LLDB_LOG(log,1074 "Could not allocate memory for selector string {0}: {1}",1075 sym_name, error);1076 return ret_plan_sp;1077 }1078 process_sp->WriteMemory(sel_str_addr, sym_name.str().c_str(),1079 sym_name.size() + 1, error);1080 if (error.Fail()) {1081 LLDB_LOG(log, "Could not write string to address {0}", sel_str_addr);1082 return ret_plan_sp;1083 }1084 Value sel_ptr_value(void_ptr_value);1085 sel_ptr_value.GetScalar() = sel_str_addr;1086 dispatch_values.PushValue(sel_ptr_value);1087 }1088 1089 Value flag_value;1090 CompilerType clang_int_type =1091 scratch_ts_sp->GetBuiltinTypeForEncodingAndBitSize(1092 lldb::eEncodingSint, 32);1093 flag_value.SetValueType(Value::ValueType::Scalar);1094 // flag_value.SetContext (Value::eContextTypeClangType, clang_int_type);1095 flag_value.SetCompilerType(clang_int_type);1096 1097 if (in_selector_stub)1098 flag_value.GetScalar() = 1;1099 else1100 flag_value.GetScalar() = 0;1101 dispatch_values.PushValue(flag_value);1102 1103 if (this_dispatch->stret_return)1104 flag_value.GetScalar() = 1;1105 else1106 flag_value.GetScalar() = 0;1107 dispatch_values.PushValue(flag_value);1108 1109 if (this_dispatch->is_super)1110 flag_value.GetScalar() = 1;1111 else1112 flag_value.GetScalar() = 0;1113 dispatch_values.PushValue(flag_value);1114 1115 if (this_dispatch->is_super2)1116 flag_value.GetScalar() = 1;1117 else1118 flag_value.GetScalar() = 0;1119 dispatch_values.PushValue(flag_value);1120 1121 switch (this_dispatch->fixedup) {1122 case DispatchFunction::eFixUpNone:1123 flag_value.GetScalar() = 0;1124 dispatch_values.PushValue(flag_value);1125 dispatch_values.PushValue(flag_value);1126 break;1127 case DispatchFunction::eFixUpFixed:1128 flag_value.GetScalar() = 1;1129 dispatch_values.PushValue(flag_value);1130 flag_value.GetScalar() = 1;1131 dispatch_values.PushValue(flag_value);1132 break;1133 case DispatchFunction::eFixUpToFix:1134 flag_value.GetScalar() = 1;1135 dispatch_values.PushValue(flag_value);1136 flag_value.GetScalar() = 0;1137 dispatch_values.PushValue(flag_value);1138 break;1139 }1140 if (log && log->GetVerbose())1141 flag_value.GetScalar() = 1;1142 else1143 flag_value.GetScalar() = 0; // FIXME - Set to 0 when debugging is done.1144 dispatch_values.PushValue(flag_value);1145 1146 ret_plan_sp = std::make_shared<AppleThreadPlanStepThroughObjCTrampoline>(1147 thread, *this, dispatch_values, isa_addr, sel_addr, sel_str_addr,1148 sym_name);1149 if (log) {1150 StreamString s;1151 ret_plan_sp->GetDescription(&s, eDescriptionLevelFull);1152 LLDB_LOGF(log, "Using ObjC step plan: %s.\n", s.GetData());1153 }1154 }1155 }1156 1157 // Finally, check if we have hit an "optimized dispatch" function. This will1158 // either directly call the base implementation or dispatch an objc_msgSend1159 // if the method has been overridden. So we just do a "step in/step out",1160 // setting a breakpoint on objc_msgSend, and if we hit the msgSend, we 1161 // will automatically step in again. That's the job of the 1162 // AppleThreadPlanStepThroughDirectDispatch.1163 if (!this_dispatch && !ret_plan_sp) {1164 MsgsendMap::iterator pos;1165 pos = m_opt_dispatch_map.find(curr_pc);1166 if (pos != m_opt_dispatch_map.end()) {1167 const char *opt_name = g_opt_dispatch_names[(*pos).second];1168 ret_plan_sp = std::make_shared<AppleThreadPlanStepThroughDirectDispatch>(1169 thread, *this, opt_name);1170 }1171 }1172 1173 return ret_plan_sp;1174}1175 1176FunctionCaller *1177AppleObjCTrampolineHandler::GetLookupImplementationFunctionCaller() {1178 return m_impl_code->GetFunctionCaller();1179}1180