1dda28197Spatrick //===-- AppleObjCTrampolineHandler.cpp ------------------------------------===//
2061da546Spatrick //
3061da546Spatrick // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4061da546Spatrick // See https://llvm.org/LICENSE.txt for license information.
5061da546Spatrick // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6061da546Spatrick //
7061da546Spatrick //===----------------------------------------------------------------------===//
8061da546Spatrick
9061da546Spatrick #include "AppleObjCTrampolineHandler.h"
10061da546Spatrick #include "AppleThreadPlanStepThroughObjCTrampoline.h"
11061da546Spatrick
12dda28197Spatrick #include "Plugins/TypeSystem/Clang/TypeSystemClang.h"
13061da546Spatrick #include "lldb/Breakpoint/StoppointCallbackContext.h"
14061da546Spatrick #include "lldb/Core/Debugger.h"
15061da546Spatrick #include "lldb/Core/Module.h"
16061da546Spatrick #include "lldb/Core/StreamFile.h"
17061da546Spatrick #include "lldb/Core/Value.h"
18061da546Spatrick #include "lldb/Expression/DiagnosticManager.h"
19061da546Spatrick #include "lldb/Expression/FunctionCaller.h"
20061da546Spatrick #include "lldb/Expression/UserExpression.h"
21061da546Spatrick #include "lldb/Expression/UtilityFunction.h"
22061da546Spatrick #include "lldb/Symbol/Symbol.h"
23061da546Spatrick #include "lldb/Target/ABI.h"
24061da546Spatrick #include "lldb/Target/ExecutionContext.h"
25061da546Spatrick #include "lldb/Target/Process.h"
26061da546Spatrick #include "lldb/Target/RegisterContext.h"
27061da546Spatrick #include "lldb/Target/Target.h"
28061da546Spatrick #include "lldb/Target/Thread.h"
29061da546Spatrick #include "lldb/Target/ThreadPlanRunToAddress.h"
30061da546Spatrick #include "lldb/Utility/ConstString.h"
31061da546Spatrick #include "lldb/Utility/FileSpec.h"
32*f6aab3d8Srobert #include "lldb/Utility/LLDBLog.h"
33061da546Spatrick #include "lldb/Utility/Log.h"
34061da546Spatrick
35061da546Spatrick #include "llvm/ADT/STLExtras.h"
36*f6aab3d8Srobert #include "llvm/ADT/ScopeExit.h"
37061da546Spatrick
38061da546Spatrick #include "Plugins/LanguageRuntime/ObjC/ObjCLanguageRuntime.h"
39061da546Spatrick
40061da546Spatrick #include <memory>
41061da546Spatrick
42061da546Spatrick using namespace lldb;
43061da546Spatrick using namespace lldb_private;
44061da546Spatrick
45061da546Spatrick const char *AppleObjCTrampolineHandler::g_lookup_implementation_function_name =
46061da546Spatrick "__lldb_objc_find_implementation_for_selector";
47061da546Spatrick const char *AppleObjCTrampolineHandler::
48061da546Spatrick g_lookup_implementation_with_stret_function_code =
49*f6aab3d8Srobert R"(
50*f6aab3d8Srobert if (is_stret) {
51*f6aab3d8Srobert return_struct.impl_addr =
52*f6aab3d8Srobert class_getMethodImplementation_stret (return_struct.class_addr,
53*f6aab3d8Srobert return_struct.sel_addr);
54*f6aab3d8Srobert } else {
55*f6aab3d8Srobert return_struct.impl_addr =
56*f6aab3d8Srobert class_getMethodImplementation (return_struct.class_addr,
57*f6aab3d8Srobert return_struct.sel_addr);
58*f6aab3d8Srobert }
59*f6aab3d8Srobert if (debug)
60*f6aab3d8Srobert printf ("\n*** Returning implementation: %p.\n",
61*f6aab3d8Srobert return_struct.impl_addr);
62*f6aab3d8Srobert
63*f6aab3d8Srobert return return_struct.impl_addr;
64*f6aab3d8Srobert }
65*f6aab3d8Srobert )";
66061da546Spatrick const char *
67061da546Spatrick AppleObjCTrampolineHandler::g_lookup_implementation_no_stret_function_code =
68*f6aab3d8Srobert R"(
69*f6aab3d8Srobert return_struct.impl_addr =
70*f6aab3d8Srobert class_getMethodImplementation (return_struct.class_addr,
71*f6aab3d8Srobert return_struct.sel_addr);
72*f6aab3d8Srobert if (debug)
73*f6aab3d8Srobert printf ("\n*** getMethodImpletation for addr: 0x%p sel: 0x%p result: 0x%p.\n",
74*f6aab3d8Srobert return_struct.class_addr, return_struct.sel_addr, return_struct.impl_addr);
75*f6aab3d8Srobert
76*f6aab3d8Srobert return return_struct.impl_addr;
77*f6aab3d8Srobert }
78*f6aab3d8Srobert )";
79*f6aab3d8Srobert
80*f6aab3d8Srobert const char
81*f6aab3d8Srobert *AppleObjCTrampolineHandler::g_lookup_implementation_function_common_code =
82*f6aab3d8Srobert R"(
83*f6aab3d8Srobert extern "C"
84*f6aab3d8Srobert {
85*f6aab3d8Srobert extern void *class_getMethodImplementation(void *objc_class, void *sel);
86*f6aab3d8Srobert extern void *class_getMethodImplementation_stret(void *objc_class, void *sel);
87*f6aab3d8Srobert extern void * object_getClass (id object);
88*f6aab3d8Srobert extern void * sel_getUid(char *name);
89*f6aab3d8Srobert extern int printf(const char *format, ...);
90*f6aab3d8Srobert }
91*f6aab3d8Srobert extern "C" void *
92*f6aab3d8Srobert __lldb_objc_find_implementation_for_selector (void *object,
93*f6aab3d8Srobert void *sel,
94*f6aab3d8Srobert int is_str_ptr,
95*f6aab3d8Srobert int is_stret,
96*f6aab3d8Srobert int is_super,
97*f6aab3d8Srobert int is_super2,
98*f6aab3d8Srobert int is_fixup,
99*f6aab3d8Srobert int is_fixed,
100*f6aab3d8Srobert int debug)
101*f6aab3d8Srobert {
102*f6aab3d8Srobert struct __lldb_imp_return_struct {
103*f6aab3d8Srobert void *class_addr;
104*f6aab3d8Srobert void *sel_addr;
105*f6aab3d8Srobert void *impl_addr;
106*f6aab3d8Srobert };
107*f6aab3d8Srobert
108*f6aab3d8Srobert struct __lldb_objc_class {
109*f6aab3d8Srobert void *isa;
110*f6aab3d8Srobert void *super_ptr;
111*f6aab3d8Srobert };
112*f6aab3d8Srobert struct __lldb_objc_super {
113*f6aab3d8Srobert void *receiver;
114*f6aab3d8Srobert struct __lldb_objc_class *class_ptr;
115*f6aab3d8Srobert };
116*f6aab3d8Srobert struct __lldb_msg_ref {
117*f6aab3d8Srobert void *dont_know;
118*f6aab3d8Srobert void *sel;
119*f6aab3d8Srobert };
120*f6aab3d8Srobert
121*f6aab3d8Srobert struct __lldb_imp_return_struct return_struct;
122*f6aab3d8Srobert
123*f6aab3d8Srobert if (debug)
124*f6aab3d8Srobert printf ("\n*** Called with obj: %p sel: %p is_str_ptr: %d "
125*f6aab3d8Srobert "is_stret: %d is_super: %d, "
126*f6aab3d8Srobert "is_super2: %d, is_fixup: %d, is_fixed: %d\n",
127*f6aab3d8Srobert object, sel, is_str_ptr, is_stret,
128*f6aab3d8Srobert is_super, is_super2, is_fixup, is_fixed);
129*f6aab3d8Srobert
130*f6aab3d8Srobert if (is_str_ptr) {
131*f6aab3d8Srobert if (debug)
132*f6aab3d8Srobert printf("*** Turning string: '%s'", sel);
133*f6aab3d8Srobert sel = sel_getUid((char *)sel);
134*f6aab3d8Srobert if (debug)
135*f6aab3d8Srobert printf("*** into sel to %p", sel);
136*f6aab3d8Srobert }
137*f6aab3d8Srobert if (is_super) {
138*f6aab3d8Srobert if (is_super2) {
139*f6aab3d8Srobert return_struct.class_addr
140*f6aab3d8Srobert = ((__lldb_objc_super *) object)->class_ptr->super_ptr;
141*f6aab3d8Srobert } else {
142*f6aab3d8Srobert return_struct.class_addr = ((__lldb_objc_super *) object)->class_ptr;
143*f6aab3d8Srobert }
144*f6aab3d8Srobert if (debug)
145*f6aab3d8Srobert printf("*** Super, class addr: %p\n", return_struct.class_addr);
146*f6aab3d8Srobert } else {
147*f6aab3d8Srobert // This code seems a little funny, but has its reasons...
148*f6aab3d8Srobert // The call to [object class] is here because if this is a class, and has
149*f6aab3d8Srobert // not been called into yet, we need to do something to force the class to
150*f6aab3d8Srobert // initialize itself.
151*f6aab3d8Srobert // Then the call to object_getClass will actually return the correct class,
152*f6aab3d8Srobert // either the class if object is a class instance, or the meta-class if it
153*f6aab3d8Srobert // is a class pointer.
154*f6aab3d8Srobert void *class_ptr = (void *) [(id) object class];
155*f6aab3d8Srobert return_struct.class_addr = (id) object_getClass((id) object);
156*f6aab3d8Srobert if (debug) {
157*f6aab3d8Srobert if (class_ptr == object) {
158*f6aab3d8Srobert printf ("Found a class object, need to return the meta class %p -> %p\n",
159*f6aab3d8Srobert class_ptr, return_struct.class_addr);
160*f6aab3d8Srobert } else {
161*f6aab3d8Srobert printf ("[object class] returned: %p object_getClass: %p.\n",
162*f6aab3d8Srobert class_ptr, return_struct.class_addr);
163*f6aab3d8Srobert }
164*f6aab3d8Srobert }
165*f6aab3d8Srobert }
166*f6aab3d8Srobert
167*f6aab3d8Srobert if (is_fixup) {
168*f6aab3d8Srobert if (is_fixed) {
169*f6aab3d8Srobert return_struct.sel_addr = ((__lldb_msg_ref *) sel)->sel;
170*f6aab3d8Srobert } else {
171*f6aab3d8Srobert char *sel_name = (char *) ((__lldb_msg_ref *) sel)->sel;
172*f6aab3d8Srobert return_struct.sel_addr = sel_getUid (sel_name);
173*f6aab3d8Srobert if (debug)
174*f6aab3d8Srobert printf ("\n*** Got fixed up selector: %p for name %s.\n",
175*f6aab3d8Srobert return_struct.sel_addr, sel_name);
176*f6aab3d8Srobert }
177*f6aab3d8Srobert } else {
178*f6aab3d8Srobert return_struct.sel_addr = sel;
179*f6aab3d8Srobert }
180*f6aab3d8Srobert )";
181061da546Spatrick
VTableRegion(AppleObjCVTables * owner,lldb::addr_t header_addr)182061da546Spatrick AppleObjCTrampolineHandler::AppleObjCVTables::VTableRegion::VTableRegion(
183061da546Spatrick AppleObjCVTables *owner, lldb::addr_t header_addr)
184*f6aab3d8Srobert : m_valid(true), m_owner(owner), m_header_addr(header_addr) {
185061da546Spatrick SetUpRegion();
186061da546Spatrick }
187061da546Spatrick
188be691f3bSpatrick AppleObjCTrampolineHandler::~AppleObjCTrampolineHandler() = default;
189061da546Spatrick
SetUpRegion()190061da546Spatrick void AppleObjCTrampolineHandler::AppleObjCVTables::VTableRegion::SetUpRegion() {
191061da546Spatrick // The header looks like:
192061da546Spatrick //
193061da546Spatrick // uint16_t headerSize
194061da546Spatrick // uint16_t descSize
195061da546Spatrick // uint32_t descCount
196061da546Spatrick // void * next
197061da546Spatrick //
198061da546Spatrick // First read in the header:
199061da546Spatrick
200061da546Spatrick char memory_buffer[16];
201061da546Spatrick ProcessSP process_sp = m_owner->GetProcessSP();
202061da546Spatrick if (!process_sp)
203061da546Spatrick return;
204061da546Spatrick DataExtractor data(memory_buffer, sizeof(memory_buffer),
205061da546Spatrick process_sp->GetByteOrder(),
206061da546Spatrick process_sp->GetAddressByteSize());
207061da546Spatrick size_t actual_size = 8 + process_sp->GetAddressByteSize();
208061da546Spatrick Status error;
209061da546Spatrick size_t bytes_read =
210061da546Spatrick process_sp->ReadMemory(m_header_addr, memory_buffer, actual_size, error);
211061da546Spatrick if (bytes_read != actual_size) {
212061da546Spatrick m_valid = false;
213061da546Spatrick return;
214061da546Spatrick }
215061da546Spatrick
216061da546Spatrick lldb::offset_t offset = 0;
217061da546Spatrick const uint16_t header_size = data.GetU16(&offset);
218061da546Spatrick const uint16_t descriptor_size = data.GetU16(&offset);
219061da546Spatrick const size_t num_descriptors = data.GetU32(&offset);
220061da546Spatrick
221dda28197Spatrick m_next_region = data.GetAddress(&offset);
222061da546Spatrick
223061da546Spatrick // If the header size is 0, that means we've come in too early before this
224061da546Spatrick // data is set up.
225061da546Spatrick // Set ourselves as not valid, and continue.
226061da546Spatrick if (header_size == 0 || num_descriptors == 0) {
227061da546Spatrick m_valid = false;
228061da546Spatrick return;
229061da546Spatrick }
230061da546Spatrick
231061da546Spatrick // Now read in all the descriptors:
232061da546Spatrick // The descriptor looks like:
233061da546Spatrick //
234061da546Spatrick // uint32_t offset
235061da546Spatrick // uint32_t flags
236061da546Spatrick //
237061da546Spatrick // Where offset is either 0 - in which case it is unused, or it is
238061da546Spatrick // the offset of the vtable code from the beginning of the
239061da546Spatrick // descriptor record. Below, we'll convert that into an absolute
240061da546Spatrick // code address, since I don't want to have to compute it over and
241061da546Spatrick // over.
242061da546Spatrick
243061da546Spatrick // Ingest the whole descriptor array:
244061da546Spatrick const lldb::addr_t desc_ptr = m_header_addr + header_size;
245061da546Spatrick const size_t desc_array_size = num_descriptors * descriptor_size;
246*f6aab3d8Srobert WritableDataBufferSP data_sp(new DataBufferHeap(desc_array_size, '\0'));
247061da546Spatrick uint8_t *dst = (uint8_t *)data_sp->GetBytes();
248061da546Spatrick
249061da546Spatrick DataExtractor desc_extractor(dst, desc_array_size, process_sp->GetByteOrder(),
250061da546Spatrick process_sp->GetAddressByteSize());
251061da546Spatrick bytes_read = process_sp->ReadMemory(desc_ptr, dst, desc_array_size, error);
252061da546Spatrick if (bytes_read != desc_array_size) {
253061da546Spatrick m_valid = false;
254061da546Spatrick return;
255061da546Spatrick }
256061da546Spatrick
257061da546Spatrick // The actual code for the vtables will be laid out consecutively, so I also
258061da546Spatrick // compute the start and end of the whole code block.
259061da546Spatrick
260061da546Spatrick offset = 0;
261061da546Spatrick m_code_start_addr = 0;
262061da546Spatrick m_code_end_addr = 0;
263061da546Spatrick
264061da546Spatrick for (size_t i = 0; i < num_descriptors; i++) {
265061da546Spatrick lldb::addr_t start_offset = offset;
266061da546Spatrick uint32_t voffset = desc_extractor.GetU32(&offset);
267061da546Spatrick uint32_t flags = desc_extractor.GetU32(&offset);
268061da546Spatrick lldb::addr_t code_addr = desc_ptr + start_offset + voffset;
269061da546Spatrick m_descriptors.push_back(VTableDescriptor(flags, code_addr));
270061da546Spatrick
271061da546Spatrick if (m_code_start_addr == 0 || code_addr < m_code_start_addr)
272061da546Spatrick m_code_start_addr = code_addr;
273061da546Spatrick if (code_addr > m_code_end_addr)
274061da546Spatrick m_code_end_addr = code_addr;
275061da546Spatrick
276061da546Spatrick offset = start_offset + descriptor_size;
277061da546Spatrick }
278061da546Spatrick // Finally, a little bird told me that all the vtable code blocks
279061da546Spatrick // are the same size. Let's compute the blocks and if they are all
280061da546Spatrick // the same add the size to the code end address:
281061da546Spatrick lldb::addr_t code_size = 0;
282061da546Spatrick bool all_the_same = true;
283061da546Spatrick for (size_t i = 0; i < num_descriptors - 1; i++) {
284061da546Spatrick lldb::addr_t this_size =
285061da546Spatrick m_descriptors[i + 1].code_start - m_descriptors[i].code_start;
286061da546Spatrick if (code_size == 0)
287061da546Spatrick code_size = this_size;
288061da546Spatrick else {
289061da546Spatrick if (this_size != code_size)
290061da546Spatrick all_the_same = false;
291061da546Spatrick if (this_size > code_size)
292061da546Spatrick code_size = this_size;
293061da546Spatrick }
294061da546Spatrick }
295061da546Spatrick if (all_the_same)
296061da546Spatrick m_code_end_addr += code_size;
297061da546Spatrick }
298061da546Spatrick
299061da546Spatrick bool AppleObjCTrampolineHandler::AppleObjCVTables::VTableRegion::
AddressInRegion(lldb::addr_t addr,uint32_t & flags)300061da546Spatrick AddressInRegion(lldb::addr_t addr, uint32_t &flags) {
301061da546Spatrick if (!IsValid())
302061da546Spatrick return false;
303061da546Spatrick
304061da546Spatrick if (addr < m_code_start_addr || addr > m_code_end_addr)
305061da546Spatrick return false;
306061da546Spatrick
307061da546Spatrick std::vector<VTableDescriptor>::iterator pos, end = m_descriptors.end();
308061da546Spatrick for (pos = m_descriptors.begin(); pos != end; pos++) {
309061da546Spatrick if (addr <= (*pos).code_start) {
310061da546Spatrick flags = (*pos).flags;
311061da546Spatrick return true;
312061da546Spatrick }
313061da546Spatrick }
314061da546Spatrick return false;
315061da546Spatrick }
316061da546Spatrick
Dump(Stream & s)317061da546Spatrick void AppleObjCTrampolineHandler::AppleObjCVTables::VTableRegion::Dump(
318061da546Spatrick Stream &s) {
319061da546Spatrick s.Printf("Header addr: 0x%" PRIx64 " Code start: 0x%" PRIx64
320061da546Spatrick " Code End: 0x%" PRIx64 " Next: 0x%" PRIx64 "\n",
321061da546Spatrick m_header_addr, m_code_start_addr, m_code_end_addr, m_next_region);
322061da546Spatrick size_t num_elements = m_descriptors.size();
323061da546Spatrick for (size_t i = 0; i < num_elements; i++) {
324061da546Spatrick s.Indent();
325061da546Spatrick s.Printf("Code start: 0x%" PRIx64 " Flags: %d\n",
326061da546Spatrick m_descriptors[i].code_start, m_descriptors[i].flags);
327061da546Spatrick }
328061da546Spatrick }
329061da546Spatrick
AppleObjCVTables(const ProcessSP & process_sp,const ModuleSP & objc_module_sp)330061da546Spatrick AppleObjCTrampolineHandler::AppleObjCVTables::AppleObjCVTables(
331061da546Spatrick const ProcessSP &process_sp, const ModuleSP &objc_module_sp)
332061da546Spatrick : m_process_wp(), m_trampoline_header(LLDB_INVALID_ADDRESS),
333061da546Spatrick m_trampolines_changed_bp_id(LLDB_INVALID_BREAK_ID),
334061da546Spatrick m_objc_module_sp(objc_module_sp) {
335061da546Spatrick if (process_sp)
336061da546Spatrick m_process_wp = process_sp;
337061da546Spatrick }
338061da546Spatrick
~AppleObjCVTables()339061da546Spatrick AppleObjCTrampolineHandler::AppleObjCVTables::~AppleObjCVTables() {
340061da546Spatrick ProcessSP process_sp = GetProcessSP();
341061da546Spatrick if (process_sp) {
342061da546Spatrick if (m_trampolines_changed_bp_id != LLDB_INVALID_BREAK_ID)
343061da546Spatrick process_sp->GetTarget().RemoveBreakpointByID(m_trampolines_changed_bp_id);
344061da546Spatrick }
345061da546Spatrick }
346061da546Spatrick
InitializeVTableSymbols()347061da546Spatrick bool AppleObjCTrampolineHandler::AppleObjCVTables::InitializeVTableSymbols() {
348061da546Spatrick if (m_trampoline_header != LLDB_INVALID_ADDRESS)
349061da546Spatrick return true;
350061da546Spatrick
351061da546Spatrick ProcessSP process_sp = GetProcessSP();
352061da546Spatrick if (process_sp) {
353061da546Spatrick Target &target = process_sp->GetTarget();
354061da546Spatrick
355061da546Spatrick if (!m_objc_module_sp) {
356be691f3bSpatrick for (ModuleSP module_sp : target.GetImages().Modules()) {
357061da546Spatrick if (ObjCLanguageRuntime::Get(*process_sp)
358be691f3bSpatrick ->IsModuleObjCLibrary(module_sp)) {
359be691f3bSpatrick m_objc_module_sp = module_sp;
360061da546Spatrick break;
361061da546Spatrick }
362061da546Spatrick }
363061da546Spatrick }
364061da546Spatrick
365061da546Spatrick if (m_objc_module_sp) {
366061da546Spatrick ConstString trampoline_name("gdb_objc_trampolines");
367061da546Spatrick const Symbol *trampoline_symbol =
368061da546Spatrick m_objc_module_sp->FindFirstSymbolWithNameAndType(trampoline_name,
369061da546Spatrick eSymbolTypeData);
370061da546Spatrick if (trampoline_symbol != nullptr) {
371061da546Spatrick m_trampoline_header = trampoline_symbol->GetLoadAddress(&target);
372061da546Spatrick if (m_trampoline_header == LLDB_INVALID_ADDRESS)
373061da546Spatrick return false;
374061da546Spatrick
375061da546Spatrick // Next look up the "changed" symbol and set a breakpoint on that...
376061da546Spatrick ConstString changed_name("gdb_objc_trampolines_changed");
377061da546Spatrick const Symbol *changed_symbol =
378061da546Spatrick m_objc_module_sp->FindFirstSymbolWithNameAndType(changed_name,
379061da546Spatrick eSymbolTypeCode);
380061da546Spatrick if (changed_symbol != nullptr) {
381061da546Spatrick const Address changed_symbol_addr = changed_symbol->GetAddress();
382061da546Spatrick if (!changed_symbol_addr.IsValid())
383061da546Spatrick return false;
384061da546Spatrick
385061da546Spatrick lldb::addr_t changed_addr =
386061da546Spatrick changed_symbol_addr.GetOpcodeLoadAddress(&target);
387061da546Spatrick if (changed_addr != LLDB_INVALID_ADDRESS) {
388061da546Spatrick BreakpointSP trampolines_changed_bp_sp =
389061da546Spatrick target.CreateBreakpoint(changed_addr, true, false);
390061da546Spatrick if (trampolines_changed_bp_sp) {
391061da546Spatrick m_trampolines_changed_bp_id = trampolines_changed_bp_sp->GetID();
392061da546Spatrick trampolines_changed_bp_sp->SetCallback(RefreshTrampolines, this,
393061da546Spatrick true);
394061da546Spatrick trampolines_changed_bp_sp->SetBreakpointKind(
395061da546Spatrick "objc-trampolines-changed");
396061da546Spatrick return true;
397061da546Spatrick }
398061da546Spatrick }
399061da546Spatrick }
400061da546Spatrick }
401061da546Spatrick }
402061da546Spatrick }
403061da546Spatrick return false;
404061da546Spatrick }
405061da546Spatrick
RefreshTrampolines(void * baton,StoppointCallbackContext * context,lldb::user_id_t break_id,lldb::user_id_t break_loc_id)406061da546Spatrick bool AppleObjCTrampolineHandler::AppleObjCVTables::RefreshTrampolines(
407061da546Spatrick void *baton, StoppointCallbackContext *context, lldb::user_id_t break_id,
408061da546Spatrick lldb::user_id_t break_loc_id) {
409061da546Spatrick AppleObjCVTables *vtable_handler = (AppleObjCVTables *)baton;
410061da546Spatrick if (vtable_handler->InitializeVTableSymbols()) {
411061da546Spatrick // The Update function is called with the address of an added region. So we
412061da546Spatrick // grab that address, and
413061da546Spatrick // feed it into ReadRegions. Of course, our friend the ABI will get the
414061da546Spatrick // values for us.
415061da546Spatrick ExecutionContext exe_ctx(context->exe_ctx_ref);
416061da546Spatrick Process *process = exe_ctx.GetProcessPtr();
417061da546Spatrick const ABI *abi = process->GetABI().get();
418061da546Spatrick
419*f6aab3d8Srobert TypeSystemClangSP scratch_ts_sp =
420be691f3bSpatrick ScratchTypeSystemClang::GetForTarget(process->GetTarget());
421*f6aab3d8Srobert if (!scratch_ts_sp)
422061da546Spatrick return false;
423061da546Spatrick
424061da546Spatrick ValueList argument_values;
425061da546Spatrick Value input_value;
426061da546Spatrick CompilerType clang_void_ptr_type =
427*f6aab3d8Srobert scratch_ts_sp->GetBasicType(eBasicTypeVoid).GetPointerType();
428061da546Spatrick
429be691f3bSpatrick input_value.SetValueType(Value::ValueType::Scalar);
430061da546Spatrick // input_value.SetContext (Value::eContextTypeClangType,
431061da546Spatrick // clang_void_ptr_type);
432061da546Spatrick input_value.SetCompilerType(clang_void_ptr_type);
433061da546Spatrick argument_values.PushValue(input_value);
434061da546Spatrick
435061da546Spatrick bool success =
436061da546Spatrick abi->GetArgumentValues(exe_ctx.GetThreadRef(), argument_values);
437061da546Spatrick if (!success)
438061da546Spatrick return false;
439061da546Spatrick
440061da546Spatrick // Now get a pointer value from the zeroth argument.
441061da546Spatrick Status error;
442061da546Spatrick DataExtractor data;
443061da546Spatrick error = argument_values.GetValueAtIndex(0)->GetValueAsData(&exe_ctx, data,
444061da546Spatrick nullptr);
445061da546Spatrick lldb::offset_t offset = 0;
446dda28197Spatrick lldb::addr_t region_addr = data.GetAddress(&offset);
447061da546Spatrick
448061da546Spatrick if (region_addr != 0)
449061da546Spatrick vtable_handler->ReadRegions(region_addr);
450061da546Spatrick }
451061da546Spatrick return false;
452061da546Spatrick }
453061da546Spatrick
ReadRegions()454061da546Spatrick bool AppleObjCTrampolineHandler::AppleObjCVTables::ReadRegions() {
455061da546Spatrick // The no argument version reads the start region from the value of
456061da546Spatrick // the gdb_regions_header, and gets started from there.
457061da546Spatrick
458061da546Spatrick m_regions.clear();
459061da546Spatrick if (!InitializeVTableSymbols())
460061da546Spatrick return false;
461061da546Spatrick Status error;
462061da546Spatrick ProcessSP process_sp = GetProcessSP();
463061da546Spatrick if (process_sp) {
464061da546Spatrick lldb::addr_t region_addr =
465061da546Spatrick process_sp->ReadPointerFromMemory(m_trampoline_header, error);
466061da546Spatrick if (error.Success())
467061da546Spatrick return ReadRegions(region_addr);
468061da546Spatrick }
469061da546Spatrick return false;
470061da546Spatrick }
471061da546Spatrick
ReadRegions(lldb::addr_t region_addr)472061da546Spatrick bool AppleObjCTrampolineHandler::AppleObjCVTables::ReadRegions(
473061da546Spatrick lldb::addr_t region_addr) {
474061da546Spatrick ProcessSP process_sp = GetProcessSP();
475061da546Spatrick if (!process_sp)
476061da546Spatrick return false;
477061da546Spatrick
478*f6aab3d8Srobert Log *log = GetLog(LLDBLog::Step);
479061da546Spatrick
480061da546Spatrick // We aren't starting at the trampoline symbol.
481061da546Spatrick InitializeVTableSymbols();
482061da546Spatrick lldb::addr_t next_region = region_addr;
483061da546Spatrick
484061da546Spatrick // Read in the sizes of the headers.
485061da546Spatrick while (next_region != 0) {
486061da546Spatrick m_regions.push_back(VTableRegion(this, next_region));
487061da546Spatrick if (!m_regions.back().IsValid()) {
488061da546Spatrick m_regions.clear();
489061da546Spatrick return false;
490061da546Spatrick }
491061da546Spatrick if (log) {
492061da546Spatrick StreamString s;
493061da546Spatrick m_regions.back().Dump(s);
494061da546Spatrick LLDB_LOGF(log, "Read vtable region: \n%s", s.GetData());
495061da546Spatrick }
496061da546Spatrick
497061da546Spatrick next_region = m_regions.back().GetNextRegionAddr();
498061da546Spatrick }
499061da546Spatrick
500061da546Spatrick return true;
501061da546Spatrick }
502061da546Spatrick
IsAddressInVTables(lldb::addr_t addr,uint32_t & flags)503061da546Spatrick bool AppleObjCTrampolineHandler::AppleObjCVTables::IsAddressInVTables(
504061da546Spatrick lldb::addr_t addr, uint32_t &flags) {
505061da546Spatrick region_collection::iterator pos, end = m_regions.end();
506061da546Spatrick for (pos = m_regions.begin(); pos != end; pos++) {
507061da546Spatrick if ((*pos).AddressInRegion(addr, flags))
508061da546Spatrick return true;
509061da546Spatrick }
510061da546Spatrick return false;
511061da546Spatrick }
512061da546Spatrick
513061da546Spatrick const AppleObjCTrampolineHandler::DispatchFunction
514061da546Spatrick AppleObjCTrampolineHandler::g_dispatch_functions[] = {
515061da546Spatrick // NAME STRET SUPER SUPER2 FIXUP TYPE
516061da546Spatrick {"objc_msgSend", false, false, false, DispatchFunction::eFixUpNone},
517061da546Spatrick {"objc_msgSend_fixup", false, false, false,
518061da546Spatrick DispatchFunction::eFixUpToFix},
519061da546Spatrick {"objc_msgSend_fixedup", false, false, false,
520061da546Spatrick DispatchFunction::eFixUpFixed},
521061da546Spatrick {"objc_msgSend_stret", true, false, false,
522061da546Spatrick DispatchFunction::eFixUpNone},
523061da546Spatrick {"objc_msgSend_stret_fixup", true, false, false,
524061da546Spatrick DispatchFunction::eFixUpToFix},
525061da546Spatrick {"objc_msgSend_stret_fixedup", true, false, false,
526061da546Spatrick DispatchFunction::eFixUpFixed},
527061da546Spatrick {"objc_msgSend_fpret", false, false, false,
528061da546Spatrick DispatchFunction::eFixUpNone},
529061da546Spatrick {"objc_msgSend_fpret_fixup", false, false, false,
530061da546Spatrick DispatchFunction::eFixUpToFix},
531061da546Spatrick {"objc_msgSend_fpret_fixedup", false, false, false,
532061da546Spatrick DispatchFunction::eFixUpFixed},
533061da546Spatrick {"objc_msgSend_fp2ret", false, false, true,
534061da546Spatrick DispatchFunction::eFixUpNone},
535061da546Spatrick {"objc_msgSend_fp2ret_fixup", false, false, true,
536061da546Spatrick DispatchFunction::eFixUpToFix},
537061da546Spatrick {"objc_msgSend_fp2ret_fixedup", false, false, true,
538061da546Spatrick DispatchFunction::eFixUpFixed},
539061da546Spatrick {"objc_msgSendSuper", false, true, false, DispatchFunction::eFixUpNone},
540061da546Spatrick {"objc_msgSendSuper_stret", true, true, false,
541061da546Spatrick DispatchFunction::eFixUpNone},
542061da546Spatrick {"objc_msgSendSuper2", false, true, true, DispatchFunction::eFixUpNone},
543061da546Spatrick {"objc_msgSendSuper2_fixup", false, true, true,
544061da546Spatrick DispatchFunction::eFixUpToFix},
545061da546Spatrick {"objc_msgSendSuper2_fixedup", false, true, true,
546061da546Spatrick DispatchFunction::eFixUpFixed},
547061da546Spatrick {"objc_msgSendSuper2_stret", true, true, true,
548061da546Spatrick DispatchFunction::eFixUpNone},
549061da546Spatrick {"objc_msgSendSuper2_stret_fixup", true, true, true,
550061da546Spatrick DispatchFunction::eFixUpToFix},
551061da546Spatrick {"objc_msgSendSuper2_stret_fixedup", true, true, true,
552061da546Spatrick DispatchFunction::eFixUpFixed},
553061da546Spatrick };
554061da546Spatrick
555dda28197Spatrick // This is the table of ObjC "accelerated dispatch" functions. They are a set
556dda28197Spatrick // of objc methods that are "seldom overridden" and so the compiler replaces the
557dda28197Spatrick // objc_msgSend with a call to one of the dispatch functions. That will check
558dda28197Spatrick // whether the method has been overridden, and directly call the Foundation
559dda28197Spatrick // implementation if not.
560dda28197Spatrick // This table is supposed to be complete. If ones get added in the future, we
561dda28197Spatrick // will have to add them to the table.
562dda28197Spatrick const char *AppleObjCTrampolineHandler::g_opt_dispatch_names[] = {
563dda28197Spatrick "objc_alloc",
564dda28197Spatrick "objc_autorelease",
565dda28197Spatrick "objc_release",
566dda28197Spatrick "objc_retain",
567dda28197Spatrick "objc_alloc_init",
568dda28197Spatrick "objc_allocWithZone",
569dda28197Spatrick "objc_opt_class",
570dda28197Spatrick "objc_opt_isKindOfClass",
571dda28197Spatrick "objc_opt_new",
572dda28197Spatrick "objc_opt_respondsToSelector",
573dda28197Spatrick "objc_opt_self",
574dda28197Spatrick };
575dda28197Spatrick
AppleObjCTrampolineHandler(const ProcessSP & process_sp,const ModuleSP & objc_module_sp)576061da546Spatrick AppleObjCTrampolineHandler::AppleObjCTrampolineHandler(
577061da546Spatrick const ProcessSP &process_sp, const ModuleSP &objc_module_sp)
578061da546Spatrick : m_process_wp(), m_objc_module_sp(objc_module_sp),
579061da546Spatrick m_impl_fn_addr(LLDB_INVALID_ADDRESS),
580061da546Spatrick m_impl_stret_fn_addr(LLDB_INVALID_ADDRESS),
581*f6aab3d8Srobert m_msg_forward_addr(LLDB_INVALID_ADDRESS),
582*f6aab3d8Srobert m_msg_forward_stret_addr(LLDB_INVALID_ADDRESS) {
583061da546Spatrick if (process_sp)
584061da546Spatrick m_process_wp = process_sp;
585061da546Spatrick // Look up the known resolution functions:
586061da546Spatrick
587061da546Spatrick ConstString get_impl_name("class_getMethodImplementation");
588061da546Spatrick ConstString get_impl_stret_name("class_getMethodImplementation_stret");
589061da546Spatrick ConstString msg_forward_name("_objc_msgForward");
590061da546Spatrick ConstString msg_forward_stret_name("_objc_msgForward_stret");
591061da546Spatrick
592061da546Spatrick Target *target = process_sp ? &process_sp->GetTarget() : nullptr;
593061da546Spatrick const Symbol *class_getMethodImplementation =
594061da546Spatrick m_objc_module_sp->FindFirstSymbolWithNameAndType(get_impl_name,
595061da546Spatrick eSymbolTypeCode);
596061da546Spatrick const Symbol *class_getMethodImplementation_stret =
597061da546Spatrick m_objc_module_sp->FindFirstSymbolWithNameAndType(get_impl_stret_name,
598061da546Spatrick eSymbolTypeCode);
599061da546Spatrick const Symbol *msg_forward = m_objc_module_sp->FindFirstSymbolWithNameAndType(
600061da546Spatrick msg_forward_name, eSymbolTypeCode);
601061da546Spatrick const Symbol *msg_forward_stret =
602061da546Spatrick m_objc_module_sp->FindFirstSymbolWithNameAndType(msg_forward_stret_name,
603061da546Spatrick eSymbolTypeCode);
604061da546Spatrick
605061da546Spatrick if (class_getMethodImplementation)
606061da546Spatrick m_impl_fn_addr =
607061da546Spatrick class_getMethodImplementation->GetAddress().GetOpcodeLoadAddress(
608061da546Spatrick target);
609061da546Spatrick if (class_getMethodImplementation_stret)
610061da546Spatrick m_impl_stret_fn_addr =
611061da546Spatrick class_getMethodImplementation_stret->GetAddress().GetOpcodeLoadAddress(
612061da546Spatrick target);
613061da546Spatrick if (msg_forward)
614061da546Spatrick m_msg_forward_addr = msg_forward->GetAddress().GetOpcodeLoadAddress(target);
615061da546Spatrick if (msg_forward_stret)
616061da546Spatrick m_msg_forward_stret_addr =
617061da546Spatrick msg_forward_stret->GetAddress().GetOpcodeLoadAddress(target);
618061da546Spatrick
619061da546Spatrick // FIXME: Do some kind of logging here.
620061da546Spatrick if (m_impl_fn_addr == LLDB_INVALID_ADDRESS) {
621061da546Spatrick // If we can't even find the ordinary get method implementation function,
622061da546Spatrick // then we aren't going to be able to
623061da546Spatrick // step through any method dispatches. Warn to that effect and get out of
624061da546Spatrick // here.
625061da546Spatrick if (process_sp->CanJIT()) {
626061da546Spatrick process_sp->GetTarget().GetDebugger().GetErrorStream().Printf(
627061da546Spatrick "Could not find implementation lookup function \"%s\""
628061da546Spatrick " step in through ObjC method dispatch will not work.\n",
629061da546Spatrick get_impl_name.AsCString());
630061da546Spatrick }
631061da546Spatrick return;
632*f6aab3d8Srobert }
633*f6aab3d8Srobert
634*f6aab3d8Srobert // We will either set the implementation to the _stret or non_stret version,
635*f6aab3d8Srobert // so either way it's safe to start filling the m_lookup_..._code here.
636*f6aab3d8Srobert m_lookup_implementation_function_code.assign(
637*f6aab3d8Srobert g_lookup_implementation_function_common_code);
638*f6aab3d8Srobert
639*f6aab3d8Srobert if (m_impl_stret_fn_addr == LLDB_INVALID_ADDRESS) {
640061da546Spatrick // It there is no stret return lookup function, assume that it is the same
641061da546Spatrick // as the straight lookup:
642061da546Spatrick m_impl_stret_fn_addr = m_impl_fn_addr;
643061da546Spatrick // Also we will use the version of the lookup code that doesn't rely on the
644061da546Spatrick // stret version of the function.
645*f6aab3d8Srobert m_lookup_implementation_function_code.append(
646*f6aab3d8Srobert g_lookup_implementation_no_stret_function_code);
647061da546Spatrick } else {
648*f6aab3d8Srobert m_lookup_implementation_function_code.append(
649*f6aab3d8Srobert g_lookup_implementation_with_stret_function_code);
650061da546Spatrick }
651061da546Spatrick
652061da546Spatrick // Look up the addresses for the objc dispatch functions and cache
653061da546Spatrick // them. For now I'm inspecting the symbol names dynamically to
654061da546Spatrick // figure out how to dispatch to them. If it becomes more
655061da546Spatrick // complicated than this we can turn the g_dispatch_functions char *
656061da546Spatrick // array into a template table, and populate the DispatchFunction
657061da546Spatrick // map from there.
658061da546Spatrick
659*f6aab3d8Srobert for (size_t i = 0; i != std::size(g_dispatch_functions); i++) {
660061da546Spatrick ConstString name_const_str(g_dispatch_functions[i].name);
661061da546Spatrick const Symbol *msgSend_symbol =
662061da546Spatrick m_objc_module_sp->FindFirstSymbolWithNameAndType(name_const_str,
663061da546Spatrick eSymbolTypeCode);
664061da546Spatrick if (msgSend_symbol && msgSend_symbol->ValueIsAddress()) {
665061da546Spatrick // FIXME: Make g_dispatch_functions static table of
666061da546Spatrick // DispatchFunctions, and have the map be address->index.
667061da546Spatrick // Problem is we also need to lookup the dispatch function. For
668061da546Spatrick // now we could have a side table of stret & non-stret dispatch
669061da546Spatrick // functions. If that's as complex as it gets, we're fine.
670061da546Spatrick
671061da546Spatrick lldb::addr_t sym_addr =
672061da546Spatrick msgSend_symbol->GetAddressRef().GetOpcodeLoadAddress(target);
673061da546Spatrick
674061da546Spatrick m_msgSend_map.insert(std::pair<lldb::addr_t, int>(sym_addr, i));
675061da546Spatrick }
676061da546Spatrick }
677061da546Spatrick
678dda28197Spatrick // Similarly, cache the addresses of the "optimized dispatch" function.
679*f6aab3d8Srobert for (size_t i = 0; i != std::size(g_opt_dispatch_names); i++) {
680dda28197Spatrick ConstString name_const_str(g_opt_dispatch_names[i]);
681dda28197Spatrick const Symbol *msgSend_symbol =
682dda28197Spatrick m_objc_module_sp->FindFirstSymbolWithNameAndType(name_const_str,
683dda28197Spatrick eSymbolTypeCode);
684dda28197Spatrick if (msgSend_symbol && msgSend_symbol->ValueIsAddress()) {
685dda28197Spatrick lldb::addr_t sym_addr =
686dda28197Spatrick msgSend_symbol->GetAddressRef().GetOpcodeLoadAddress(target);
687dda28197Spatrick
688dda28197Spatrick m_opt_dispatch_map.emplace(sym_addr, i);
689dda28197Spatrick }
690dda28197Spatrick }
691dda28197Spatrick
692061da546Spatrick // Build our vtable dispatch handler here:
693dda28197Spatrick m_vtables_up =
694dda28197Spatrick std::make_unique<AppleObjCVTables>(process_sp, m_objc_module_sp);
695061da546Spatrick if (m_vtables_up)
696061da546Spatrick m_vtables_up->ReadRegions();
697061da546Spatrick }
698061da546Spatrick
699061da546Spatrick lldb::addr_t
SetupDispatchFunction(Thread & thread,ValueList & dispatch_values)700061da546Spatrick AppleObjCTrampolineHandler::SetupDispatchFunction(Thread &thread,
701061da546Spatrick ValueList &dispatch_values) {
702061da546Spatrick ThreadSP thread_sp(thread.shared_from_this());
703061da546Spatrick ExecutionContext exe_ctx(thread_sp);
704*f6aab3d8Srobert Log *log = GetLog(LLDBLog::Step);
705061da546Spatrick
706061da546Spatrick lldb::addr_t args_addr = LLDB_INVALID_ADDRESS;
707061da546Spatrick FunctionCaller *impl_function_caller = nullptr;
708061da546Spatrick
709061da546Spatrick // Scope for mutex locker:
710061da546Spatrick {
711061da546Spatrick std::lock_guard<std::mutex> guard(m_impl_function_mutex);
712061da546Spatrick
713061da546Spatrick // First stage is to make the ClangUtility to hold our injected function:
714061da546Spatrick
715061da546Spatrick if (!m_impl_code) {
716*f6aab3d8Srobert if (!m_lookup_implementation_function_code.empty()) {
717be691f3bSpatrick auto utility_fn_or_error = exe_ctx.GetTargetRef().CreateUtilityFunction(
718be691f3bSpatrick m_lookup_implementation_function_code,
719be691f3bSpatrick g_lookup_implementation_function_name, eLanguageTypeC, exe_ctx);
720be691f3bSpatrick if (!utility_fn_or_error) {
721be691f3bSpatrick LLDB_LOG_ERROR(
722be691f3bSpatrick log, utility_fn_or_error.takeError(),
723be691f3bSpatrick "Failed to get Utility Function for implementation lookup: {0}.");
724061da546Spatrick return args_addr;
725061da546Spatrick }
726be691f3bSpatrick m_impl_code = std::move(*utility_fn_or_error);
727061da546Spatrick } else {
728061da546Spatrick LLDB_LOGF(log, "No method lookup implementation code.");
729061da546Spatrick return LLDB_INVALID_ADDRESS;
730061da546Spatrick }
731061da546Spatrick
732061da546Spatrick // Next make the runner function for our implementation utility function.
733*f6aab3d8Srobert TypeSystemClangSP scratch_ts_sp = ScratchTypeSystemClang::GetForTarget(
734be691f3bSpatrick thread.GetProcess()->GetTarget());
735*f6aab3d8Srobert if (!scratch_ts_sp)
736061da546Spatrick return LLDB_INVALID_ADDRESS;
737061da546Spatrick
738061da546Spatrick CompilerType clang_void_ptr_type =
739*f6aab3d8Srobert scratch_ts_sp->GetBasicType(eBasicTypeVoid).GetPointerType();
740061da546Spatrick Status error;
741061da546Spatrick
742061da546Spatrick impl_function_caller = m_impl_code->MakeFunctionCaller(
743061da546Spatrick clang_void_ptr_type, dispatch_values, thread_sp, error);
744061da546Spatrick if (error.Fail()) {
745061da546Spatrick LLDB_LOGF(log,
746061da546Spatrick "Error getting function caller for dispatch lookup: \"%s\".",
747061da546Spatrick error.AsCString());
748061da546Spatrick return args_addr;
749061da546Spatrick }
750061da546Spatrick } else {
751061da546Spatrick impl_function_caller = m_impl_code->GetFunctionCaller();
752061da546Spatrick }
753061da546Spatrick }
754061da546Spatrick
755061da546Spatrick // Now write down the argument values for this particular call.
756061da546Spatrick // This looks like it might be a race condition if other threads
757061da546Spatrick // were calling into here, but actually it isn't because we allocate
758061da546Spatrick // a new args structure for this call by passing args_addr =
759061da546Spatrick // LLDB_INVALID_ADDRESS...
760061da546Spatrick
761be691f3bSpatrick DiagnosticManager diagnostics;
762061da546Spatrick if (!impl_function_caller->WriteFunctionArguments(
763061da546Spatrick exe_ctx, args_addr, dispatch_values, diagnostics)) {
764061da546Spatrick if (log) {
765061da546Spatrick LLDB_LOGF(log, "Error writing function arguments.");
766061da546Spatrick diagnostics.Dump(log);
767061da546Spatrick }
768061da546Spatrick return args_addr;
769061da546Spatrick }
770061da546Spatrick
771061da546Spatrick return args_addr;
772061da546Spatrick }
773061da546Spatrick
774dda28197Spatrick const AppleObjCTrampolineHandler::DispatchFunction *
FindDispatchFunction(lldb::addr_t addr)775dda28197Spatrick AppleObjCTrampolineHandler::FindDispatchFunction(lldb::addr_t addr) {
776dda28197Spatrick MsgsendMap::iterator pos;
777dda28197Spatrick pos = m_msgSend_map.find(addr);
778dda28197Spatrick if (pos != m_msgSend_map.end()) {
779dda28197Spatrick return &g_dispatch_functions[(*pos).second];
780dda28197Spatrick }
781dda28197Spatrick return nullptr;
782dda28197Spatrick }
783dda28197Spatrick
ForEachDispatchFunction(std::function<void (lldb::addr_t,const DispatchFunction &)> callback)784*f6aab3d8Srobert void AppleObjCTrampolineHandler::ForEachDispatchFunction(
785*f6aab3d8Srobert std::function<void(lldb::addr_t, const DispatchFunction &)> callback) {
786dda28197Spatrick for (auto elem : m_msgSend_map) {
787dda28197Spatrick callback(elem.first, g_dispatch_functions[elem.second]);
788dda28197Spatrick }
789dda28197Spatrick }
790dda28197Spatrick
791061da546Spatrick ThreadPlanSP
GetStepThroughDispatchPlan(Thread & thread,bool stop_others)792061da546Spatrick AppleObjCTrampolineHandler::GetStepThroughDispatchPlan(Thread &thread,
793061da546Spatrick bool stop_others) {
794061da546Spatrick ThreadPlanSP ret_plan_sp;
795061da546Spatrick lldb::addr_t curr_pc = thread.GetRegisterContext()->GetPC();
796061da546Spatrick
797*f6aab3d8Srobert DispatchFunction vtable_dispatch = {"vtable", false, false, false,
798*f6aab3d8Srobert DispatchFunction::eFixUpFixed};
799*f6aab3d8Srobert // The selector specific stubs are a wrapper for objc_msgSend. They don't get
800*f6aab3d8Srobert // passed a SEL, but instead the selector string is encoded in the stub
801*f6aab3d8Srobert // name, in the form:
802*f6aab3d8Srobert // objc_msgSend$SelectorName
803*f6aab3d8Srobert // and the stub figures out the uniqued selector. If we find ourselves in
804*f6aab3d8Srobert // one of these stubs, we strip off the selector string and pass that to the
805*f6aab3d8Srobert // implementation finder function, which looks up the SEL (you have to do this
806*f6aab3d8Srobert // in process) and passes that to the runtime lookup function.
807*f6aab3d8Srobert DispatchFunction sel_stub_dispatch = {"sel-specific-stub", false, false,
808*f6aab3d8Srobert false, DispatchFunction::eFixUpNone};
809061da546Spatrick
810*f6aab3d8Srobert // First step is to see if we're in a selector-specific dispatch stub.
811*f6aab3d8Srobert // Those are of the form _objc_msgSend$<SELECTOR>, so see if the current
812*f6aab3d8Srobert // function has that name:
813*f6aab3d8Srobert Address func_addr;
814*f6aab3d8Srobert Target &target = thread.GetProcess()->GetTarget();
815*f6aab3d8Srobert llvm::StringRef sym_name;
816*f6aab3d8Srobert const DispatchFunction *this_dispatch = nullptr;
817*f6aab3d8Srobert
818*f6aab3d8Srobert if (target.ResolveLoadAddress(curr_pc, func_addr)) {
819*f6aab3d8Srobert Symbol *curr_sym = func_addr.CalculateSymbolContextSymbol();
820*f6aab3d8Srobert if (curr_sym)
821*f6aab3d8Srobert sym_name = curr_sym->GetName().GetStringRef();
822*f6aab3d8Srobert
823*f6aab3d8Srobert if (!sym_name.empty() && !sym_name.consume_front("objc_msgSend$"))
824*f6aab3d8Srobert sym_name = {};
825*f6aab3d8Srobert else
826*f6aab3d8Srobert this_dispatch = &sel_stub_dispatch;
827*f6aab3d8Srobert }
828*f6aab3d8Srobert bool in_selector_stub = !sym_name.empty();
829*f6aab3d8Srobert // Second step is to look and see if we are in one of the known ObjC
830061da546Spatrick // dispatch functions. We've already compiled a table of same, so
831061da546Spatrick // consult it.
832061da546Spatrick
833*f6aab3d8Srobert if (!in_selector_stub)
834*f6aab3d8Srobert this_dispatch = FindDispatchFunction(curr_pc);
835061da546Spatrick
836061da546Spatrick // Next check to see if we are in a vtable region:
837061da546Spatrick
838dda28197Spatrick if (!this_dispatch && m_vtables_up) {
839061da546Spatrick uint32_t flags;
840dda28197Spatrick if (m_vtables_up->IsAddressInVTables(curr_pc, flags)) {
841dda28197Spatrick vtable_dispatch.stret_return =
842061da546Spatrick (flags & AppleObjCVTables::eOBJC_TRAMPOLINE_STRET) ==
843061da546Spatrick AppleObjCVTables::eOBJC_TRAMPOLINE_STRET;
844dda28197Spatrick this_dispatch = &vtable_dispatch;
845061da546Spatrick }
846061da546Spatrick }
847061da546Spatrick
848*f6aab3d8Srobert // Since we set this_dispatch in both the vtable & sel specific stub cases
849*f6aab3d8Srobert // this if will be used for all three of those cases.
850dda28197Spatrick if (this_dispatch) {
851*f6aab3d8Srobert Log *log = GetLog(LLDBLog::Step);
852061da546Spatrick
853061da546Spatrick // We are decoding a method dispatch. First job is to pull the
854*f6aab3d8Srobert // arguments out. If we are in a regular stub, we get self & selector,
855*f6aab3d8Srobert // but if we are in a selector-specific stub, we'll have to get that from
856*f6aab3d8Srobert // the string sym_name.
857061da546Spatrick
858061da546Spatrick lldb::StackFrameSP thread_cur_frame = thread.GetStackFrameAtIndex(0);
859061da546Spatrick
860061da546Spatrick const ABI *abi = nullptr;
861061da546Spatrick ProcessSP process_sp(thread.CalculateProcess());
862061da546Spatrick if (process_sp)
863061da546Spatrick abi = process_sp->GetABI().get();
864061da546Spatrick if (abi == nullptr)
865061da546Spatrick return ret_plan_sp;
866061da546Spatrick
867061da546Spatrick TargetSP target_sp(thread.CalculateTarget());
868061da546Spatrick
869*f6aab3d8Srobert TypeSystemClangSP scratch_ts_sp =
870be691f3bSpatrick ScratchTypeSystemClang::GetForTarget(*target_sp);
871*f6aab3d8Srobert if (!scratch_ts_sp)
872061da546Spatrick return ret_plan_sp;
873061da546Spatrick
874061da546Spatrick ValueList argument_values;
875061da546Spatrick Value void_ptr_value;
876061da546Spatrick CompilerType clang_void_ptr_type =
877*f6aab3d8Srobert scratch_ts_sp->GetBasicType(eBasicTypeVoid).GetPointerType();
878be691f3bSpatrick void_ptr_value.SetValueType(Value::ValueType::Scalar);
879061da546Spatrick // void_ptr_value.SetContext (Value::eContextTypeClangType,
880061da546Spatrick // clang_void_ptr_type);
881061da546Spatrick void_ptr_value.SetCompilerType(clang_void_ptr_type);
882061da546Spatrick
883061da546Spatrick int obj_index;
884061da546Spatrick int sel_index;
885061da546Spatrick
886*f6aab3d8Srobert // If this is a selector-specific stub then just push one value, 'cause
887*f6aab3d8Srobert // we only get the object.
888061da546Spatrick // If this is a struct return dispatch, then the first argument is
889061da546Spatrick // the return struct pointer, and the object is the second, and
890*f6aab3d8Srobert // the selector is the third.
891*f6aab3d8Srobert // Otherwise the object is the first and the selector the second.
892*f6aab3d8Srobert if (in_selector_stub) {
893*f6aab3d8Srobert obj_index = 0;
894*f6aab3d8Srobert sel_index = 1;
895*f6aab3d8Srobert argument_values.PushValue(void_ptr_value);
896*f6aab3d8Srobert } else if (this_dispatch->stret_return) {
897061da546Spatrick obj_index = 1;
898061da546Spatrick sel_index = 2;
899061da546Spatrick argument_values.PushValue(void_ptr_value);
900061da546Spatrick argument_values.PushValue(void_ptr_value);
901061da546Spatrick argument_values.PushValue(void_ptr_value);
902061da546Spatrick } else {
903061da546Spatrick obj_index = 0;
904061da546Spatrick sel_index = 1;
905061da546Spatrick argument_values.PushValue(void_ptr_value);
906061da546Spatrick argument_values.PushValue(void_ptr_value);
907061da546Spatrick }
908061da546Spatrick
909061da546Spatrick bool success = abi->GetArgumentValues(thread, argument_values);
910061da546Spatrick if (!success)
911061da546Spatrick return ret_plan_sp;
912061da546Spatrick
913061da546Spatrick lldb::addr_t obj_addr =
914061da546Spatrick argument_values.GetValueAtIndex(obj_index)->GetScalar().ULongLong();
915061da546Spatrick if (obj_addr == 0x0) {
916061da546Spatrick LLDB_LOGF(
917061da546Spatrick log,
918061da546Spatrick "Asked to step to dispatch to nil object, returning empty plan.");
919061da546Spatrick return ret_plan_sp;
920061da546Spatrick }
921061da546Spatrick
922061da546Spatrick ExecutionContext exe_ctx(thread.shared_from_this());
923061da546Spatrick // isa_addr will store the class pointer that the method is being
924061da546Spatrick // dispatched to - so either the class directly or the super class
925061da546Spatrick // if this is one of the objc_msgSendSuper flavors. That's mostly
926061da546Spatrick // used to look up the class/selector pair in our cache.
927061da546Spatrick
928061da546Spatrick lldb::addr_t isa_addr = LLDB_INVALID_ADDRESS;
929*f6aab3d8Srobert lldb::addr_t sel_addr = LLDB_INVALID_ADDRESS;
930*f6aab3d8Srobert // If we are not in a selector stub, get the sel address from the arguments.
931*f6aab3d8Srobert if (!in_selector_stub)
932*f6aab3d8Srobert sel_addr =
933061da546Spatrick argument_values.GetValueAtIndex(sel_index)->GetScalar().ULongLong();
934061da546Spatrick
935061da546Spatrick // Figure out the class this is being dispatched to and see if
936061da546Spatrick // we've already cached this method call, If so we can push a
937061da546Spatrick // run-to-address plan directly. Otherwise we have to figure out
938061da546Spatrick // where the implementation lives.
939061da546Spatrick
940dda28197Spatrick if (this_dispatch->is_super) {
941dda28197Spatrick if (this_dispatch->is_super2) {
942061da546Spatrick // In the objc_msgSendSuper2 case, we don't get the object
943061da546Spatrick // directly, we get a structure containing the object and the
944061da546Spatrick // class to which the super message is being sent. So we need
945061da546Spatrick // to dig the super out of the class and use that.
946061da546Spatrick
947061da546Spatrick Value super_value(*(argument_values.GetValueAtIndex(obj_index)));
948*f6aab3d8Srobert super_value.GetScalar() += process_sp->GetAddressByteSize();
949061da546Spatrick super_value.ResolveValue(&exe_ctx);
950061da546Spatrick
951061da546Spatrick if (super_value.GetScalar().IsValid()) {
952061da546Spatrick
953061da546Spatrick // isa_value now holds the class pointer. The second word of the
954061da546Spatrick // class pointer is the super-class pointer:
955*f6aab3d8Srobert super_value.GetScalar() += process_sp->GetAddressByteSize();
956061da546Spatrick super_value.ResolveValue(&exe_ctx);
957061da546Spatrick if (super_value.GetScalar().IsValid())
958061da546Spatrick isa_addr = super_value.GetScalar().ULongLong();
959061da546Spatrick else {
960061da546Spatrick LLDB_LOGF(log, "Failed to extract the super class value from the "
961061da546Spatrick "class in objc_super.");
962061da546Spatrick }
963061da546Spatrick } else {
964061da546Spatrick LLDB_LOGF(log, "Failed to extract the class value from objc_super.");
965061da546Spatrick }
966061da546Spatrick } else {
967061da546Spatrick // In the objc_msgSendSuper case, we don't get the object
968061da546Spatrick // directly, we get a two element structure containing the
969061da546Spatrick // object and the super class to which the super message is
970061da546Spatrick // being sent. So the class we want is the second element of
971061da546Spatrick // this structure.
972061da546Spatrick
973061da546Spatrick Value super_value(*(argument_values.GetValueAtIndex(obj_index)));
974*f6aab3d8Srobert super_value.GetScalar() += process_sp->GetAddressByteSize();
975061da546Spatrick super_value.ResolveValue(&exe_ctx);
976061da546Spatrick
977061da546Spatrick if (super_value.GetScalar().IsValid()) {
978061da546Spatrick isa_addr = super_value.GetScalar().ULongLong();
979061da546Spatrick } else {
980061da546Spatrick LLDB_LOGF(log, "Failed to extract the class value from objc_super.");
981061da546Spatrick }
982061da546Spatrick }
983061da546Spatrick } else {
984061da546Spatrick // In the direct dispatch case, the object->isa is the class pointer we
985061da546Spatrick // want.
986061da546Spatrick
987061da546Spatrick // This is a little cheesy, but since object->isa is the first field,
988061da546Spatrick // making the object value a load address value and resolving it will get
989061da546Spatrick // the pointer sized data pointed to by that value...
990061da546Spatrick
991061da546Spatrick // Note, it isn't a fatal error not to be able to get the
992061da546Spatrick // address from the object, since this might be a "tagged
993061da546Spatrick // pointer" which isn't a real object, but rather some word
994061da546Spatrick // length encoded dingus.
995061da546Spatrick
996061da546Spatrick Value isa_value(*(argument_values.GetValueAtIndex(obj_index)));
997061da546Spatrick
998be691f3bSpatrick isa_value.SetValueType(Value::ValueType::LoadAddress);
999061da546Spatrick isa_value.ResolveValue(&exe_ctx);
1000061da546Spatrick if (isa_value.GetScalar().IsValid()) {
1001061da546Spatrick isa_addr = isa_value.GetScalar().ULongLong();
1002061da546Spatrick } else {
1003061da546Spatrick LLDB_LOGF(log, "Failed to extract the isa value from object.");
1004061da546Spatrick }
1005061da546Spatrick }
1006061da546Spatrick
1007061da546Spatrick // Okay, we've got the address of the class for which we're resolving this,
1008061da546Spatrick // let's see if it's in our cache:
1009061da546Spatrick lldb::addr_t impl_addr = LLDB_INVALID_ADDRESS;
1010*f6aab3d8Srobert // If this is a regular dispatch, look up the sel in our addr to sel cache:
1011061da546Spatrick if (isa_addr != LLDB_INVALID_ADDRESS) {
1012061da546Spatrick ObjCLanguageRuntime *objc_runtime =
1013061da546Spatrick ObjCLanguageRuntime::Get(*thread.GetProcess());
1014061da546Spatrick assert(objc_runtime != nullptr);
1015*f6aab3d8Srobert if (!in_selector_stub) {
1016*f6aab3d8Srobert LLDB_LOG(log, "Resolving call for class - {0} and selector - {1}",
1017*f6aab3d8Srobert isa_addr, sel_addr);
1018061da546Spatrick impl_addr = objc_runtime->LookupInMethodCache(isa_addr, sel_addr);
1019*f6aab3d8Srobert } else {
1020*f6aab3d8Srobert LLDB_LOG(log, "Resolving call for class - {0} and selector - {1}",
1021*f6aab3d8Srobert isa_addr, sym_name);
1022*f6aab3d8Srobert impl_addr = objc_runtime->LookupInMethodCache(isa_addr, sym_name);
1023061da546Spatrick }
1024*f6aab3d8Srobert }
1025*f6aab3d8Srobert // If it is a selector-specific stub dispatch, look in the string cache:
1026061da546Spatrick
1027061da546Spatrick if (impl_addr != LLDB_INVALID_ADDRESS) {
1028061da546Spatrick // Yup, it was in the cache, so we can run to that address directly.
1029061da546Spatrick
1030061da546Spatrick LLDB_LOGF(log, "Found implementation address in cache: 0x%" PRIx64,
1031061da546Spatrick impl_addr);
1032061da546Spatrick
1033061da546Spatrick ret_plan_sp = std::make_shared<ThreadPlanRunToAddress>(thread, impl_addr,
1034061da546Spatrick stop_others);
1035061da546Spatrick } else {
1036061da546Spatrick // We haven't seen this class/selector pair yet. Look it up.
1037061da546Spatrick StreamString errors;
1038061da546Spatrick Address impl_code_address;
1039061da546Spatrick
1040061da546Spatrick ValueList dispatch_values;
1041061da546Spatrick
1042061da546Spatrick // We've will inject a little function in the target that takes the
1043*f6aab3d8Srobert // object, selector/selector string and some flags,
1044061da546Spatrick // and figures out the implementation. Looks like:
1045061da546Spatrick // void *__lldb_objc_find_implementation_for_selector (void *object,
1046061da546Spatrick // void *sel,
1047*f6aab3d8Srobert // int
1048*f6aab3d8Srobert // is_str_ptr,
1049061da546Spatrick // int is_stret,
1050061da546Spatrick // int is_super,
1051061da546Spatrick // int is_super2,
1052061da546Spatrick // int is_fixup,
1053061da546Spatrick // int is_fixed,
1054061da546Spatrick // int debug)
1055*f6aab3d8Srobert // If we don't have an actual SEL, but rather a string version of the
1056*f6aab3d8Srobert // selector WE injected, set is_str_ptr to true, and sel to the address
1057*f6aab3d8Srobert // of the string.
1058061da546Spatrick // So set up the arguments for that call.
1059061da546Spatrick
1060061da546Spatrick dispatch_values.PushValue(*(argument_values.GetValueAtIndex(obj_index)));
1061*f6aab3d8Srobert lldb::addr_t sel_str_addr = LLDB_INVALID_ADDRESS;
1062*f6aab3d8Srobert if (!in_selector_stub) {
1063*f6aab3d8Srobert // If we don't have a selector string, push the selector from arguments.
1064*f6aab3d8Srobert dispatch_values.PushValue(
1065*f6aab3d8Srobert *(argument_values.GetValueAtIndex(sel_index)));
1066*f6aab3d8Srobert } else {
1067*f6aab3d8Srobert // Otherwise, inject the string into the target, and push that value for
1068*f6aab3d8Srobert // the sel argument.
1069*f6aab3d8Srobert Status error;
1070*f6aab3d8Srobert sel_str_addr = process_sp->AllocateMemory(
1071*f6aab3d8Srobert sym_name.size() + 1, ePermissionsReadable | ePermissionsWritable,
1072*f6aab3d8Srobert error);
1073*f6aab3d8Srobert if (sel_str_addr == LLDB_INVALID_ADDRESS || error.Fail()) {
1074*f6aab3d8Srobert LLDB_LOG(log,
1075*f6aab3d8Srobert "Could not allocate memory for selector string {0}: {1}",
1076*f6aab3d8Srobert sym_name, error);
1077*f6aab3d8Srobert return ret_plan_sp;
1078*f6aab3d8Srobert }
1079*f6aab3d8Srobert process_sp->WriteMemory(sel_str_addr, sym_name.str().c_str(),
1080*f6aab3d8Srobert sym_name.size() + 1, error);
1081*f6aab3d8Srobert if (error.Fail()) {
1082*f6aab3d8Srobert LLDB_LOG(log, "Could not write string to address {0}", sel_str_addr);
1083*f6aab3d8Srobert return ret_plan_sp;
1084*f6aab3d8Srobert }
1085*f6aab3d8Srobert Value sel_ptr_value(void_ptr_value);
1086*f6aab3d8Srobert sel_ptr_value.GetScalar() = sel_str_addr;
1087*f6aab3d8Srobert dispatch_values.PushValue(sel_ptr_value);
1088*f6aab3d8Srobert }
1089061da546Spatrick
1090061da546Spatrick Value flag_value;
1091061da546Spatrick CompilerType clang_int_type =
1092*f6aab3d8Srobert scratch_ts_sp->GetBuiltinTypeForEncodingAndBitSize(
1093061da546Spatrick lldb::eEncodingSint, 32);
1094be691f3bSpatrick flag_value.SetValueType(Value::ValueType::Scalar);
1095061da546Spatrick // flag_value.SetContext (Value::eContextTypeClangType, clang_int_type);
1096061da546Spatrick flag_value.SetCompilerType(clang_int_type);
1097061da546Spatrick
1098*f6aab3d8Srobert if (in_selector_stub)
1099*f6aab3d8Srobert flag_value.GetScalar() = 1;
1100*f6aab3d8Srobert else
1101*f6aab3d8Srobert flag_value.GetScalar() = 0;
1102*f6aab3d8Srobert dispatch_values.PushValue(flag_value);
1103*f6aab3d8Srobert
1104dda28197Spatrick if (this_dispatch->stret_return)
1105061da546Spatrick flag_value.GetScalar() = 1;
1106061da546Spatrick else
1107061da546Spatrick flag_value.GetScalar() = 0;
1108061da546Spatrick dispatch_values.PushValue(flag_value);
1109061da546Spatrick
1110dda28197Spatrick if (this_dispatch->is_super)
1111061da546Spatrick flag_value.GetScalar() = 1;
1112061da546Spatrick else
1113061da546Spatrick flag_value.GetScalar() = 0;
1114061da546Spatrick dispatch_values.PushValue(flag_value);
1115061da546Spatrick
1116dda28197Spatrick if (this_dispatch->is_super2)
1117061da546Spatrick flag_value.GetScalar() = 1;
1118061da546Spatrick else
1119061da546Spatrick flag_value.GetScalar() = 0;
1120061da546Spatrick dispatch_values.PushValue(flag_value);
1121061da546Spatrick
1122dda28197Spatrick switch (this_dispatch->fixedup) {
1123061da546Spatrick case DispatchFunction::eFixUpNone:
1124061da546Spatrick flag_value.GetScalar() = 0;
1125061da546Spatrick dispatch_values.PushValue(flag_value);
1126061da546Spatrick dispatch_values.PushValue(flag_value);
1127061da546Spatrick break;
1128061da546Spatrick case DispatchFunction::eFixUpFixed:
1129061da546Spatrick flag_value.GetScalar() = 1;
1130061da546Spatrick dispatch_values.PushValue(flag_value);
1131061da546Spatrick flag_value.GetScalar() = 1;
1132061da546Spatrick dispatch_values.PushValue(flag_value);
1133061da546Spatrick break;
1134061da546Spatrick case DispatchFunction::eFixUpToFix:
1135061da546Spatrick flag_value.GetScalar() = 1;
1136061da546Spatrick dispatch_values.PushValue(flag_value);
1137061da546Spatrick flag_value.GetScalar() = 0;
1138061da546Spatrick dispatch_values.PushValue(flag_value);
1139061da546Spatrick break;
1140061da546Spatrick }
1141061da546Spatrick if (log && log->GetVerbose())
1142061da546Spatrick flag_value.GetScalar() = 1;
1143061da546Spatrick else
1144061da546Spatrick flag_value.GetScalar() = 0; // FIXME - Set to 0 when debugging is done.
1145061da546Spatrick dispatch_values.PushValue(flag_value);
1146061da546Spatrick
1147061da546Spatrick ret_plan_sp = std::make_shared<AppleThreadPlanStepThroughObjCTrampoline>(
1148*f6aab3d8Srobert thread, *this, dispatch_values, isa_addr, sel_addr, sel_str_addr,
1149*f6aab3d8Srobert sym_name);
1150061da546Spatrick if (log) {
1151061da546Spatrick StreamString s;
1152061da546Spatrick ret_plan_sp->GetDescription(&s, eDescriptionLevelFull);
1153061da546Spatrick LLDB_LOGF(log, "Using ObjC step plan: %s.\n", s.GetData());
1154061da546Spatrick }
1155061da546Spatrick }
1156061da546Spatrick }
1157061da546Spatrick
1158dda28197Spatrick // Finally, check if we have hit an "optimized dispatch" function. This will
1159dda28197Spatrick // either directly call the base implementation or dispatch an objc_msgSend
1160dda28197Spatrick // if the method has been overridden. So we just do a "step in/step out",
1161dda28197Spatrick // setting a breakpoint on objc_msgSend, and if we hit the msgSend, we
1162dda28197Spatrick // will automatically step in again. That's the job of the
1163dda28197Spatrick // AppleThreadPlanStepThroughDirectDispatch.
1164dda28197Spatrick if (!this_dispatch && !ret_plan_sp) {
1165dda28197Spatrick MsgsendMap::iterator pos;
1166dda28197Spatrick pos = m_opt_dispatch_map.find(curr_pc);
1167dda28197Spatrick if (pos != m_opt_dispatch_map.end()) {
1168dda28197Spatrick const char *opt_name = g_opt_dispatch_names[(*pos).second];
1169dda28197Spatrick ret_plan_sp = std::make_shared<AppleThreadPlanStepThroughDirectDispatch>(
1170be691f3bSpatrick thread, *this, opt_name);
1171dda28197Spatrick }
1172dda28197Spatrick }
1173dda28197Spatrick
1174061da546Spatrick return ret_plan_sp;
1175061da546Spatrick }
1176061da546Spatrick
1177061da546Spatrick FunctionCaller *
GetLookupImplementationFunctionCaller()1178061da546Spatrick AppleObjCTrampolineHandler::GetLookupImplementationFunctionCaller() {
1179061da546Spatrick return m_impl_code->GetFunctionCaller();
1180061da546Spatrick }
1181