blob: 27e72114eefe071dbe4bb634431d5fdb525c6799 [file] [log] [blame]
Jim Ingham5a369122010-09-28 01:25:32 +00001//===-- AppleObjCTrampolineHandler.cpp ----------------------------*- C++ -*-===//
Chris Lattner30fdc8d2010-06-08 16:52:24 +00002//
3// The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9
Jim Ingham5a369122010-09-28 01:25:32 +000010#include "AppleObjCTrampolineHandler.h"
Chris Lattner30fdc8d2010-06-08 16:52:24 +000011
12// C Includes
13// C++ Includes
14// Other libraries and framework includes
15// Project includes
Jim Ingham5a369122010-09-28 01:25:32 +000016#include "AppleThreadPlanStepThroughObjCTrampoline.h"
17
Jim Ingham58221732010-11-05 00:18:21 +000018#include "lldb/Breakpoint/StoppointCallbackContext.h"
Chris Lattner30fdc8d2010-06-08 16:52:24 +000019#include "lldb/Core/ConstString.h"
Jim Ingham58221732010-11-05 00:18:21 +000020#include "lldb/Core/Debugger.h"
Greg Clayton53239f02011-02-08 05:05:52 +000021#include "lldb/Host/FileSpec.h"
Jim Ingham5a369122010-09-28 01:25:32 +000022#include "lldb/Core/Log.h"
23#include "lldb/Core/Module.h"
24#include "lldb/Core/Value.h"
Jim Ingham5a369122010-09-28 01:25:32 +000025#include "lldb/Symbol/ClangASTContext.h"
26#include "lldb/Target/ObjCLanguageRuntime.h"
27#include "lldb/Target/Process.h"
Chris Lattner30fdc8d2010-06-08 16:52:24 +000028#include "lldb/Target/RegisterContext.h"
29#include "lldb/Target/Target.h"
Jim Ingham5a369122010-09-28 01:25:32 +000030#include "lldb/Target/Thread.h"
Chris Lattner30fdc8d2010-06-08 16:52:24 +000031#include "lldb/Target/ExecutionContext.h"
Chris Lattner30fdc8d2010-06-08 16:52:24 +000032#include "lldb/Target/ThreadPlanRunToAddress.h"
33
34using namespace lldb;
35using namespace lldb_private;
36
Jim Ingham957373f2010-12-10 00:26:25 +000037const char *AppleObjCTrampolineHandler::g_lookup_implementation_function_name = "__lldb_objc_find_implementation_for_selector";
38const char *AppleObjCTrampolineHandler::g_lookup_implementation_function_code = " \n\
39extern \"C\" \n\
40{ \n\
41 extern void *class_getMethodImplementation(void *objc_class, void *sel); \n\
42 extern void *class_getMethodImplementation_stret(void *objc_class, void *sel); \n\
43 extern void * sel_getUid(char *name); \n\
44 extern int printf(const char *format, ...); \n\
45} \n\
46extern \"C\" void * __lldb_objc_find_implementation_for_selector (void *object, \n\
47 void *sel, \n\
48 int is_stret, \n\
49 int is_super, \n\
50 int is_super2, \n\
51 int is_fixup, \n\
52 int is_fixed, \n\
53 int debug) \n\
54{ \n\
55 struct __lldb_imp_return_struct \n\
56 { \n\
57 void *class_addr; \n\
58 void *sel_addr; \n\
59 void *impl_addr; \n\
60 }; \n\
61 \n\
62 struct __lldb_objc_class { \n\
63 void *isa; \n\
64 void *super_ptr; \n\
65 }; \n\
66 struct __lldb_objc_super { \n\
67 void *reciever; \n\
68 struct __lldb_objc_class *class_ptr; \n\
69 }; \n\
70 struct __lldb_msg_ref { \n\
71 void *dont_know; \n\
72 void *sel; \n\
73 }; \n\
74 \n\
75 struct __lldb_imp_return_struct return_struct; \n\
76 \n\
77 if (debug) \n\
78 printf (\"\\n*** Called with obj: 0x%p sel: 0x%p is_stret: %d is_super: %d, \" \n\
79 \"is_super2: %d, is_fixup: %d, is_fixed: %d\\n\", \n\
80 object, sel, is_stret, is_super, is_super2, is_fixup, is_fixed); \n\
81 if (is_super) \n\
82 { \n\
83 if (is_super2) \n\
84 { \n\
85 return_struct.class_addr = ((__lldb_objc_super *) object)->class_ptr->super_ptr; \n\
86 } \n\
87 else \n\
88 { \n\
89 return_struct.class_addr = ((__lldb_objc_super *) object)->class_ptr; \n\
90 } \n\
91 } \n\
92 else \n\
93 { \n\
94 void *class_ptr = (void *) [(id) object class]; \n\
95 if (class_ptr == object) \n\
96 { \n\
97 struct __lldb_objc_class *class_as_class_struct = (struct __lldb_objc_class *) class_ptr; \n\
98 if (debug) \n\
99 printf (\"Found a class object, need to return the meta class 0x%p -> 0x%p\\n\", \n\
100 class_ptr, class_as_class_struct->isa); \n\
101 return_struct.class_addr = class_as_class_struct->isa; \n\
102 } \n\
103 else \n\
104 { \n\
105 if (debug) \n\
106 printf (\"[object class] returned: 0x%p.\\n\", class_ptr); \n\
107 return_struct.class_addr = class_ptr; \n\
108 } \n\
109 } \n\
110 \n\
111 if (is_fixup) \n\
112 { \n\
113 if (is_fixed) \n\
114 { \n\
115 return_struct.sel_addr = ((__lldb_msg_ref *) sel)->sel; \n\
116 } \n\
117 else \n\
118 { \n\
119 char *sel_name = (char *) ((__lldb_msg_ref *) sel)->sel; \n\
120 return_struct.sel_addr = sel_getUid (sel_name); \n\
121 if (debug) \n\
122 printf (\"\\n*** Got fixed up selector: 0x%p for name %s.\\n\", \n\
123 return_struct.sel_addr, sel_name); \n\
124 } \n\
125 } \n\
126 else \n\
127 { \n\
128 return_struct.sel_addr = sel; \n\
129 } \n\
130 \n\
131 if (is_stret) \n\
132 { \n\
133 return_struct.impl_addr = class_getMethodImplementation_stret (return_struct.class_addr, \n\
134 return_struct.sel_addr); \n\
135 } \n\
136 else \n\
137 { \n\
138 return_struct.impl_addr = class_getMethodImplementation (return_struct.class_addr, \n\
139 return_struct.sel_addr); \n\
140 } \n\
141 if (debug) \n\
142 printf (\"\\n*** Returning implementation: 0x%p.\\n\", return_struct.impl_addr); \n\
143 \n\
144 return return_struct.impl_addr; \n\
145} \n\
146";
147
Jim Ingham58221732010-11-05 00:18:21 +0000148AppleObjCTrampolineHandler::AppleObjCVTables::VTableRegion::VTableRegion(AppleObjCVTables *owner, lldb::addr_t header_addr) :
149 m_valid (true),
150 m_owner(owner),
151 m_header_addr (header_addr),
152 m_code_start_addr(0),
153 m_code_end_addr (0),
154 m_next_region (0)
155{
156 SetUpRegion ();
157}
158
159void
160AppleObjCTrampolineHandler::AppleObjCVTables::VTableRegion::SetUpRegion()
161{
162 // The header looks like:
163 //
164 // uint16_t headerSize
165 // uint16_t descSize
166 // uint32_t descCount
167 // void * next
168 //
169 // First read in the header:
170
171 char memory_buffer[16];
172 Process *process = m_owner->GetProcess();
173 DataExtractor data(memory_buffer, sizeof(memory_buffer),
174 process->GetByteOrder(),
175 process->GetAddressByteSize());
176 size_t actual_size = 8 + process->GetAddressByteSize();
177 Error error;
178 size_t bytes_read = process->ReadMemory (m_header_addr, memory_buffer, actual_size, error);
179 if (bytes_read != actual_size)
180 {
181 m_valid = false;
182 return;
183 }
184
185 uint32_t offset_ptr = 0;
Greg Clayton526e5af2010-11-13 03:52:47 +0000186 const uint16_t header_size = data.GetU16(&offset_ptr);
187 const uint16_t descriptor_size = data.GetU16(&offset_ptr);
188 const size_t num_descriptors = data.GetU32(&offset_ptr);
Jim Ingham58221732010-11-05 00:18:21 +0000189
190 m_next_region = data.GetPointer(&offset_ptr);
191
192 // If the header size is 0, that means we've come in too early before this data is set up.
193 // Set ourselves as not valid, and continue.
Greg Clayton526e5af2010-11-13 03:52:47 +0000194 if (header_size == 0 || num_descriptors == 0)
Jim Ingham58221732010-11-05 00:18:21 +0000195 {
196 m_valid = false;
197 return;
198 }
199
200 // Now read in all the descriptors:
201 // The descriptor looks like:
202 //
203 // uint32_t offset
204 // uint32_t flags
205 //
206 // Where offset is either 0 - in which case it is unused, or
207 // it is the offset of the vtable code from the beginning of the descriptor record.
208 // Below, we'll convert that into an absolute code address, since I don't want to have
209 // to compute it over and over.
210
211 // Ingest the whole descriptor array:
Greg Clayton526e5af2010-11-13 03:52:47 +0000212 const lldb::addr_t desc_ptr = m_header_addr + header_size;
213 const size_t desc_array_size = num_descriptors * descriptor_size;
Jim Ingham58221732010-11-05 00:18:21 +0000214 DataBufferSP data_sp(new DataBufferHeap (desc_array_size, '\0'));
215 uint8_t* dst = (uint8_t*)data_sp->GetBytes();
216
217 DataExtractor desc_extractor (dst, desc_array_size,
218 process->GetByteOrder(),
219 process->GetAddressByteSize());
220 bytes_read = process->ReadMemory(desc_ptr, dst, desc_array_size, error);
221 if (bytes_read != desc_array_size)
222 {
223 m_valid = false;
224 return;
225 }
226
227 // The actual code for the vtables will be laid out consecutively, so I also
228 // compute the start and end of the whole code block.
229
230 offset_ptr = 0;
231 m_code_start_addr = 0;
232 m_code_end_addr = 0;
233
234 for (int i = 0; i < num_descriptors; i++)
235 {
236 lldb::addr_t start_offset = offset_ptr;
237 uint32_t offset = desc_extractor.GetU32 (&offset_ptr);
238 uint32_t flags = desc_extractor.GetU32 (&offset_ptr);
239 lldb:addr_t code_addr = desc_ptr + start_offset + offset;
240 m_descriptors.push_back (VTableDescriptor(flags, code_addr));
241
242 if (m_code_start_addr == 0 || code_addr < m_code_start_addr)
243 m_code_start_addr = code_addr;
244 if (code_addr > m_code_end_addr)
245 m_code_end_addr = code_addr;
246
247 offset_ptr = start_offset + descriptor_size;
248 }
249 // Finally, a little bird told me that all the vtable code blocks are the same size.
250 // Let's compute the blocks and if they are all the same add the size to the code end address:
251 lldb::addr_t code_size = 0;
252 bool all_the_same = true;
253 for (int i = 0; i < num_descriptors - 1; i++)
254 {
255 lldb::addr_t this_size = m_descriptors[i + 1].code_start - m_descriptors[i].code_start;
256 if (code_size == 0)
257 code_size = this_size;
258 else
259 {
260 if (this_size != code_size)
261 all_the_same = false;
262 if (this_size > code_size)
263 code_size = this_size;
264 }
265 }
266 if (all_the_same)
267 m_code_end_addr += code_size;
268}
269
270bool
271AppleObjCTrampolineHandler::AppleObjCVTables::VTableRegion::AddressInRegion (lldb::addr_t addr, uint32_t &flags)
272{
273 if (!IsValid())
274 return false;
275
276 if (addr < m_code_start_addr || addr > m_code_end_addr)
277 return false;
278
279 std::vector<VTableDescriptor>::iterator pos, end = m_descriptors.end();
280 for (pos = m_descriptors.begin(); pos != end; pos++)
281 {
282 if (addr <= (*pos).code_start)
283 {
284 flags = (*pos).flags;
285 return true;
286 }
287 }
288 return false;
289}
290
291void
292AppleObjCTrampolineHandler::AppleObjCVTables::VTableRegion::Dump (Stream &s)
293{
294 s.Printf ("Header addr: 0x%llx Code start: 0x%llx Code End: 0x%llx Next: 0x%llx\n",
295 m_header_addr, m_code_start_addr, m_code_end_addr, m_next_region);
296 size_t num_elements = m_descriptors.size();
297 for (size_t i = 0; i < num_elements; i++)
298 {
299 s.Indent();
300 s.Printf ("Code start: 0x%llx Flags: %d\n", m_descriptors[i].code_start, m_descriptors[i].flags);
301 }
302}
303
304AppleObjCTrampolineHandler::AppleObjCVTables::AppleObjCVTables (ProcessSP &process_sp, ModuleSP &objc_module_sp) :
305 m_process_sp(process_sp),
306 m_trampoline_header(LLDB_INVALID_ADDRESS),
307 m_trampolines_changed_bp_id(LLDB_INVALID_BREAK_ID),
308 m_objc_module_sp(objc_module_sp)
309{
310
311}
312
313AppleObjCTrampolineHandler::AppleObjCVTables::~AppleObjCVTables()
314{
315 if (m_trampolines_changed_bp_id != LLDB_INVALID_BREAK_ID)
316 m_process_sp->GetTarget().RemoveBreakpointByID (m_trampolines_changed_bp_id);
317}
318
319bool
320AppleObjCTrampolineHandler::AppleObjCVTables::InitializeVTableSymbols ()
321{
322 if (m_trampoline_header != LLDB_INVALID_ADDRESS)
323 return true;
324 Target &target = m_process_sp->GetTarget();
325
326 ModuleList &modules = target.GetImages();
327 size_t num_modules = modules.GetSize();
328 if (!m_objc_module_sp)
329 {
330 for (size_t i = 0; i < num_modules; i++)
331 {
332 if (m_process_sp->GetObjCLanguageRuntime()->IsModuleObjCLibrary (modules.GetModuleAtIndex(i)))
333 {
334 m_objc_module_sp = modules.GetModuleAtIndex(i);
335 break;
336 }
337 }
338 }
339
340 if (m_objc_module_sp)
341 {
342 ConstString trampoline_name ("gdb_objc_trampolines");
343 const Symbol *trampoline_symbol = m_objc_module_sp->FindFirstSymbolWithNameAndType(trampoline_name,
344 eSymbolTypeData);
345 if (trampoline_symbol != NULL)
346 {
Greg Clayton1a65ae12011-01-25 23:55:37 +0000347 if (!trampoline_symbol->GetValue().IsValid())
Jim Ingham58221732010-11-05 00:18:21 +0000348 return false;
349
Greg Clayton1a65ae12011-01-25 23:55:37 +0000350 m_trampoline_header = trampoline_symbol->GetValue().GetLoadAddress(&target);
Jim Ingham58221732010-11-05 00:18:21 +0000351 if (m_trampoline_header == LLDB_INVALID_ADDRESS)
352 return false;
353
354 // Next look up the "changed" symbol and set a breakpoint on that...
355 ConstString changed_name ("gdb_objc_trampolines_changed");
356 const Symbol *changed_symbol = m_objc_module_sp->FindFirstSymbolWithNameAndType(changed_name,
357 eSymbolTypeCode);
358 if (changed_symbol != NULL)
359 {
Greg Clayton1a65ae12011-01-25 23:55:37 +0000360 if (!changed_symbol->GetValue().IsValid())
Jim Ingham58221732010-11-05 00:18:21 +0000361 return false;
362
Greg Clayton1a65ae12011-01-25 23:55:37 +0000363 lldb::addr_t changed_addr = changed_symbol->GetValue().GetLoadAddress(&target);
Jim Ingham58221732010-11-05 00:18:21 +0000364 if (changed_addr != LLDB_INVALID_ADDRESS)
365 {
366 BreakpointSP trampolines_changed_bp_sp = target.CreateBreakpoint (changed_addr,
367 true);
368 if (trampolines_changed_bp_sp != NULL)
369 {
370 m_trampolines_changed_bp_id = trampolines_changed_bp_sp->GetID();
371 trampolines_changed_bp_sp->SetCallback (RefreshTrampolines, this, true);
372 return true;
373 }
374 }
375 }
376 }
377 }
378
379 return false;
380}
381
382bool
383AppleObjCTrampolineHandler::AppleObjCVTables::RefreshTrampolines (void *baton,
384 StoppointCallbackContext *context,
385 lldb::user_id_t break_id,
386 lldb::user_id_t break_loc_id)
387{
388 AppleObjCVTables *vtable_handler = (AppleObjCVTables *) baton;
389 if (vtable_handler->InitializeVTableSymbols())
390 {
391 // The Update function is called with the address of an added region. So we grab that address, and
392 // feed it into ReadRegions. Of course, our friend the ABI will get the values for us.
393 Process *process = context->exe_ctx.process;
394 const ABI *abi = process->GetABI();
395
396 ClangASTContext *clang_ast_context = process->GetTarget().GetScratchClangASTContext();
397 ValueList argument_values;
398 Value input_value;
399 void *clang_void_ptr_type = clang_ast_context->GetVoidPtrType(false);
400 input_value.SetValueType (Value::eValueTypeScalar);
Greg Clayton526e5af2010-11-13 03:52:47 +0000401 input_value.SetContext (Value::eContextTypeClangType, clang_void_ptr_type);
Jim Ingham58221732010-11-05 00:18:21 +0000402 argument_values.PushValue(input_value);
403
404 bool success = abi->GetArgumentValues (*(context->exe_ctx.thread), argument_values);
405 if (!success)
406 return false;
407
408 // Now get a pointer value from the zeroth argument.
409 Error error;
410 DataExtractor data;
411 error = argument_values.GetValueAtIndex(0)->GetValueAsData(&(context->exe_ctx), clang_ast_context->getASTContext(), data, 0);
412 uint32_t offset_ptr = 0;
413 lldb::addr_t region_addr = data.GetPointer(&offset_ptr);
414
415 if (region_addr != 0)
416 vtable_handler->ReadRegions(region_addr);
417 }
418 return false;
419}
420
421bool
422AppleObjCTrampolineHandler::AppleObjCVTables::ReadRegions ()
423{
424 // The no argument version reads the start region from the value of the gdb_regions_header, and
425 // gets started from there.
426
427 m_regions.clear();
428 if (!InitializeVTableSymbols())
429 return false;
430 char memory_buffer[8];
431 DataExtractor data(memory_buffer, sizeof(memory_buffer),
432 m_process_sp->GetByteOrder(),
433 m_process_sp->GetAddressByteSize());
434 Error error;
435 size_t bytes_read = m_process_sp->ReadMemory (m_trampoline_header, memory_buffer, m_process_sp->GetAddressByteSize(), error);
436 if (bytes_read != m_process_sp->GetAddressByteSize())
437 return false;
438
439 uint32_t offset_ptr = 0;
440 lldb::addr_t region_addr = data.GetPointer(&offset_ptr);
441 return ReadRegions (region_addr);
442}
443
444bool
445AppleObjCTrampolineHandler::AppleObjCVTables::ReadRegions (lldb::addr_t region_addr)
446{
447 if (!m_process_sp)
448 return false;
449
Greg Clayton2d4edfb2010-11-06 01:53:30 +0000450 LogSP log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_STEP));
Jim Ingham58221732010-11-05 00:18:21 +0000451
452 // We aren't starting at the trampoline symbol.
453 InitializeVTableSymbols ();
454 lldb::addr_t next_region = region_addr;
455
456 // Read in the sizes of the headers.
457 while (next_region != 0)
458 {
459 m_regions.push_back (VTableRegion(this, next_region));
460 if (!m_regions.back().IsValid())
461 {
462 m_regions.clear();
463 return false;
464 }
465 if (log)
466 {
467 StreamString s;
468 m_regions.back().Dump(s);
469 log->Printf("Read vtable region: \n%s", s.GetData());
470 }
471
472 next_region = m_regions.back().GetNextRegionAddr();
473 }
474
475 return true;
476}
477
478bool
479AppleObjCTrampolineHandler::AppleObjCVTables::IsAddressInVTables (lldb::addr_t addr, uint32_t &flags)
480{
481 region_collection::iterator pos, end = m_regions.end();
482 for (pos = m_regions.begin(); pos != end; pos++)
483 {
484 if ((*pos).AddressInRegion (addr, flags))
485 return true;
486 }
487 return false;
488}
489
Jim Ingham5a369122010-09-28 01:25:32 +0000490const AppleObjCTrampolineHandler::DispatchFunction
491AppleObjCTrampolineHandler::g_dispatch_functions[] =
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000492{
Jim Ingham957373f2010-12-10 00:26:25 +0000493 // NAME STRET SUPER SUPER2 FIXUP TYPE
494 {"objc_msgSend", false, false, false, DispatchFunction::eFixUpNone },
495 {"objc_msgSend_fixup", false, false, false, DispatchFunction::eFixUpToFix },
496 {"objc_msgSend_fixedup", false, false, false, DispatchFunction::eFixUpFixed },
497 {"objc_msgSend_stret", true, false, false, DispatchFunction::eFixUpNone },
498 {"objc_msgSend_stret_fixup", true, false, false, DispatchFunction::eFixUpToFix },
499 {"objc_msgSend_stret_fixedup", true, false, false, DispatchFunction::eFixUpFixed },
500 {"objc_msgSend_fpret", false, false, false, DispatchFunction::eFixUpNone },
501 {"objc_msgSend_fpret_fixup", false, false, false, DispatchFunction::eFixUpToFix },
502 {"objc_msgSend_fpret_fixedup", false, false, false, DispatchFunction::eFixUpFixed },
503 {"objc_msgSend_fp2ret", false, false, true, DispatchFunction::eFixUpNone },
504 {"objc_msgSend_fp2ret_fixup", false, false, true, DispatchFunction::eFixUpToFix },
505 {"objc_msgSend_fp2ret_fixedup", false, false, true, DispatchFunction::eFixUpFixed },
506 {"objc_msgSendSuper", false, true, false, DispatchFunction::eFixUpNone },
507 {"objc_msgSendSuper_stret", true, true, false, DispatchFunction::eFixUpNone },
508 {"objc_msgSendSuper2", false, true, true, DispatchFunction::eFixUpNone },
509 {"objc_msgSendSuper2_fixup", false, true, true, DispatchFunction::eFixUpToFix },
510 {"objc_msgSendSuper2_fixedup", false, true, true, DispatchFunction::eFixUpFixed },
511 {"objc_msgSendSuper2_stret", true, true, true, DispatchFunction::eFixUpNone },
512 {"objc_msgSendSuper2_stret_fixup", true, true, true, DispatchFunction::eFixUpToFix },
513 {"objc_msgSendSuper2_stret_fixedup", true, true, true, DispatchFunction::eFixUpFixed },
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000514 {NULL}
515};
516
Jim Ingham58221732010-11-05 00:18:21 +0000517AppleObjCTrampolineHandler::AppleObjCTrampolineHandler (ProcessSP process_sp, ModuleSP objc_module_sp) :
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000518 m_process_sp (process_sp),
Jim Ingham58221732010-11-05 00:18:21 +0000519 m_objc_module_sp (objc_module_sp),
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000520 m_impl_fn_addr (LLDB_INVALID_ADDRESS),
Jim Ingham957373f2010-12-10 00:26:25 +0000521 m_impl_stret_fn_addr (LLDB_INVALID_ADDRESS),
522 m_msg_forward_addr (LLDB_INVALID_ADDRESS)
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000523{
524 // Look up the known resolution functions:
525
526 ConstString get_impl_name("class_getMethodImplementation");
527 ConstString get_impl_stret_name("class_getMethodImplementation_stret");
Jim Ingham957373f2010-12-10 00:26:25 +0000528 ConstString msg_forward_name("_objc_msgForward");
529 ConstString msg_forward_stret_name("_objc_msgForward_stret");
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000530
Greg Claytonf5e56de2010-09-14 23:36:40 +0000531 Target *target = m_process_sp ? &m_process_sp->GetTarget() : NULL;
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000532 const Symbol *class_getMethodImplementation = m_objc_module_sp->FindFirstSymbolWithNameAndType (get_impl_name, eSymbolTypeCode);
533 const Symbol *class_getMethodImplementation_stret = m_objc_module_sp->FindFirstSymbolWithNameAndType (get_impl_stret_name, eSymbolTypeCode);
Jim Ingham957373f2010-12-10 00:26:25 +0000534 const Symbol *msg_forward = m_objc_module_sp->FindFirstSymbolWithNameAndType (msg_forward_name, eSymbolTypeCode);
535 const Symbol *msg_forward_stret = m_objc_module_sp->FindFirstSymbolWithNameAndType (msg_forward_stret_name, eSymbolTypeCode);
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000536
537 if (class_getMethodImplementation)
Greg Claytonf5e56de2010-09-14 23:36:40 +0000538 m_impl_fn_addr = class_getMethodImplementation->GetValue().GetLoadAddress(target);
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000539 if (class_getMethodImplementation_stret)
Greg Claytonf5e56de2010-09-14 23:36:40 +0000540 m_impl_stret_fn_addr = class_getMethodImplementation_stret->GetValue().GetLoadAddress(target);
Jim Ingham957373f2010-12-10 00:26:25 +0000541 if (msg_forward)
542 m_msg_forward_addr = msg_forward->GetValue().GetLoadAddress(target);
543 if (msg_forward_stret)
544 m_msg_forward_stret_addr = msg_forward_stret->GetValue().GetLoadAddress(target);
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000545
546 // FIXME: Do some kind of logging here.
547 if (m_impl_fn_addr == LLDB_INVALID_ADDRESS || m_impl_stret_fn_addr == LLDB_INVALID_ADDRESS)
548 return;
549
550 // Look up the addresses for the objc dispatch functions and cache them. For now I'm inspecting the symbol
551 // names dynamically to figure out how to dispatch to them. If it becomes more complicated than this we can
552 // turn the g_dispatch_functions char * array into a template table, and populate the DispatchFunction map
553 // from there.
554
555 for (int i = 0; g_dispatch_functions[i].name != NULL; i++)
556 {
557 ConstString name_const_str(g_dispatch_functions[i].name);
558 const Symbol *msgSend_symbol = m_objc_module_sp->FindFirstSymbolWithNameAndType (name_const_str, eSymbolTypeCode);
559 if (msgSend_symbol)
560 {
Greg Clayton710dd5a2011-01-08 20:28:42 +0000561 // FixMe: Make g_dispatch_functions static table of DispatchFunctions, and have the map be address->index.
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000562 // Problem is we also need to lookup the dispatch function. For now we could have a side table of stret & non-stret
563 // dispatch functions. If that's as complex as it gets, we're fine.
564
Greg Claytonf5e56de2010-09-14 23:36:40 +0000565 lldb::addr_t sym_addr = msgSend_symbol->GetValue().GetLoadAddress(target);
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000566
567 m_msgSend_map.insert(std::pair<lldb::addr_t, int>(sym_addr, i));
568 }
569 }
Jim Ingham58221732010-11-05 00:18:21 +0000570
571 // Build our vtable dispatch handler here:
572 m_vtables_ap.reset(new AppleObjCVTables(process_sp, m_objc_module_sp));
573 if (m_vtables_ap.get())
574 m_vtables_ap->ReadRegions();
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000575}
576
577ThreadPlanSP
Jim Ingham5a369122010-09-28 01:25:32 +0000578AppleObjCTrampolineHandler::GetStepThroughDispatchPlan (Thread &thread, bool stop_others)
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000579{
580 ThreadPlanSP ret_plan_sp;
581 lldb::addr_t curr_pc = thread.GetRegisterContext()->GetPC();
582
Jim Ingham58221732010-11-05 00:18:21 +0000583 DispatchFunction this_dispatch;
584 bool found_it = false;
585
Jim Ingham957373f2010-12-10 00:26:25 +0000586 // First step is to look and see if we are in one of the known ObjC dispatch functions. We've already compiled
587 // a table of same, so consult it.
588
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000589 MsgsendMap::iterator pos;
590 pos = m_msgSend_map.find (curr_pc);
591 if (pos != m_msgSend_map.end())
592 {
Jim Ingham58221732010-11-05 00:18:21 +0000593 this_dispatch = g_dispatch_functions[(*pos).second];
594 found_it = true;
595 }
596
Jim Ingham957373f2010-12-10 00:26:25 +0000597 // Next check to see if we are in a vtable region:
598
Jim Ingham58221732010-11-05 00:18:21 +0000599 if (!found_it)
600 {
601 uint32_t flags;
602 if (m_vtables_ap.get())
603 {
604 found_it = m_vtables_ap->IsAddressInVTables (curr_pc, flags);
605 if (found_it)
606 {
607 this_dispatch.name = "vtable";
608 this_dispatch.stret_return
609 = (flags & AppleObjCVTables::eOBJC_TRAMPOLINE_STRET) == AppleObjCVTables::eOBJC_TRAMPOLINE_STRET;
610 this_dispatch.is_super = false;
Jim Ingham9b23df52011-01-26 19:09:39 +0000611 this_dispatch.is_super2 = false;
Jim Ingham58221732010-11-05 00:18:21 +0000612 this_dispatch.fixedup = DispatchFunction::eFixUpFixed;
613 }
614 }
615 }
616
617 if (found_it)
618 {
Greg Clayton2d4edfb2010-11-06 01:53:30 +0000619 LogSP log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_STEP));
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000620
Jim Ingham957373f2010-12-10 00:26:25 +0000621 // We are decoding a method dispatch.
622 // First job is to pull the arguments out:
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000623
624 lldb::StackFrameSP thread_cur_frame = thread.GetStackFrameAtIndex(0);
625
626 Process *process = thread.CalculateProcess();
627 const ABI *abi = process->GetABI();
628 if (abi == NULL)
629 return ret_plan_sp;
630
631 Target *target = thread.CalculateTarget();
632
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000633 ClangASTContext *clang_ast_context = target->GetScratchClangASTContext();
634 ValueList argument_values;
Jim Ingham957373f2010-12-10 00:26:25 +0000635 Value void_ptr_value;
636 lldb::clang_type_t clang_void_ptr_type = clang_ast_context->GetVoidPtrType(false);
637 void_ptr_value.SetValueType (Value::eValueTypeScalar);
638 void_ptr_value.SetContext (Value::eContextTypeClangType, clang_void_ptr_type);
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000639
640 int obj_index;
641 int sel_index;
642
643 // If this is a struct return dispatch, then the first argument is the
644 // return struct pointer, and the object is the second, and the selector is the third.
645 // Otherwise the object is the first and the selector the second.
Jim Ingham58221732010-11-05 00:18:21 +0000646 if (this_dispatch.stret_return)
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000647 {
648 obj_index = 1;
649 sel_index = 2;
Jim Ingham957373f2010-12-10 00:26:25 +0000650 argument_values.PushValue(void_ptr_value);
651 argument_values.PushValue(void_ptr_value);
652 argument_values.PushValue(void_ptr_value);
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000653 }
654 else
655 {
656 obj_index = 0;
657 sel_index = 1;
Jim Ingham957373f2010-12-10 00:26:25 +0000658 argument_values.PushValue(void_ptr_value);
659 argument_values.PushValue(void_ptr_value);
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000660 }
661
662
663 bool success = abi->GetArgumentValues (thread, argument_values);
664 if (!success)
665 return ret_plan_sp;
Jim Ingham957373f2010-12-10 00:26:25 +0000666
667 // Pull the class out of the Object and see if we've already cached this method call,
668 // If so we can push a run-to-address plan directly. Otherwise we have to figure out where
669 // the implementation lives.
670
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000671 Value isa_value(*(argument_values.GetValueAtIndex(obj_index)));
Jim Ingham957373f2010-12-10 00:26:25 +0000672
673 // This is a little cheesy, but since object->isa is the first field,
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000674 // making the object value a load address value and resolving it will get
675 // the pointer sized data pointed to by that value...
Jim Ingham957373f2010-12-10 00:26:25 +0000676 ExecutionContext exe_ctx;
677 thread.CalculateExecutionContext (exe_ctx);
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000678
679 isa_value.SetValueType(Value::eValueTypeLoadAddress);
Jim Ingham957373f2010-12-10 00:26:25 +0000680 isa_value.ResolveValue(&exe_ctx, clang_ast_context->getASTContext());
681 lldb::addr_t isa_addr = isa_value.GetScalar().ULongLong();
682 lldb::addr_t sel_addr = argument_values.GetValueAtIndex(sel_index)->GetScalar().ULongLong();
683
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000684 if (log)
685 {
686 log->Printf("Resolving method call for class - 0x%llx and selector - 0x%llx",
Jim Ingham957373f2010-12-10 00:26:25 +0000687 isa_addr, sel_addr);
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000688 }
Jim Ingham5a369122010-09-28 01:25:32 +0000689 ObjCLanguageRuntime *objc_runtime = m_process_sp->GetObjCLanguageRuntime ();
690 assert(objc_runtime != NULL);
Jim Ingham957373f2010-12-10 00:26:25 +0000691
692 lldb::addr_t impl_addr = objc_runtime->LookupInMethodCache (isa_addr, sel_addr);
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000693
694 if (impl_addr == LLDB_INVALID_ADDRESS)
Jim Ingham957373f2010-12-10 00:26:25 +0000695 {
696 // We haven't seen this class/selector pair yet. Look it up.
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000697 StreamString errors;
Jim Ingham957373f2010-12-10 00:26:25 +0000698 Address impl_code_address;
699
700 ValueList dispatch_values;
701
702 // We've will inject a little function in the target that takes the object, selector and some flags,
703 // and figures out the implementation. Looks like:
704 // void *__lldb_objc_find_implementation_for_selector (void *object,
705 // void *sel,
706 // int is_stret,
707 // int is_super,
708 // int is_super2,
709 // int is_fixup,
710 // int is_fixed,
711 // int debug)
712 // So set up the arguments for that call.
713
714 dispatch_values.PushValue (*(argument_values.GetValueAtIndex(obj_index)));
715 dispatch_values.PushValue (*(argument_values.GetValueAtIndex(sel_index)));
716
717 Value flag_value;
718 lldb::clang_type_t clang_int_type = clang_ast_context->GetBuiltinTypeForEncodingAndBitSize(lldb::eEncodingSint, 32);
719 flag_value.SetValueType (Value::eValueTypeScalar);
720 flag_value.SetContext (Value::eContextTypeClangType, clang_int_type);
721
722 if (this_dispatch.stret_return)
723 flag_value.GetScalar() = 1;
724 else
725 flag_value.GetScalar() = 0;
726 dispatch_values.PushValue (flag_value);
727
728 if (this_dispatch.is_super)
729 flag_value.GetScalar() = 1;
730 else
731 flag_value.GetScalar() = 0;
732 dispatch_values.PushValue (flag_value);
733
734 if (this_dispatch.is_super2)
735 flag_value.GetScalar() = 1;
736 else
737 flag_value.GetScalar() = 0;
738 dispatch_values.PushValue (flag_value);
739
740 switch (this_dispatch.fixedup)
741 {
742 case DispatchFunction::eFixUpNone:
743 flag_value.GetScalar() = 0;
744 dispatch_values.PushValue (flag_value);
745 dispatch_values.PushValue (flag_value);
746 break;
747 case DispatchFunction::eFixUpFixed:
748 flag_value.GetScalar() = 1;
749 dispatch_values.PushValue (flag_value);
750 flag_value.GetScalar() = 1;
751 dispatch_values.PushValue (flag_value);
752 break;
753 case DispatchFunction::eFixUpToFix:
754 flag_value.GetScalar() = 1;
755 dispatch_values.PushValue (flag_value);
756 flag_value.GetScalar() = 0;
757 dispatch_values.PushValue (flag_value);
758 break;
759 }
760 if (log)
761 flag_value.GetScalar() = 1;
762 else
763 flag_value.GetScalar() = 0; // FIXME - Set to 0 when debugging is done.
764 dispatch_values.PushValue (flag_value);
765
766 // Now, if we haven't already, make and insert the function as a ClangUtilityFunction, and make and insert
767 // it's runner ClangFunction.
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000768 {
769 // Scope for mutex locker:
Greg Claytonb1320972010-07-14 00:18:15 +0000770 Mutex::Locker locker(m_impl_function_mutex);
Jim Ingham957373f2010-12-10 00:26:25 +0000771
772 // First stage is to make the ClangUtility to hold our injected function:
773
774#define USE_BUILTIN_FUNCTION 0 // Define this to 1 and we will use the get_implementation function found in the target.
775 // This is useful for debugging additions to the get_impl function 'cause you don't have
776 // to bother with string-ifying the code into g_lookup_implementation_function_code.
777
778 if (USE_BUILTIN_FUNCTION)
779 {
780 ConstString our_utility_function_name("__lldb_objc_find_implementation_for_selector");
781 SymbolContextList sc_list;
782 exe_ctx.target->GetImages().FindSymbolsWithNameAndType (our_utility_function_name, eSymbolTypeCode, sc_list);
783 if (sc_list.GetSize() == 1)
784 {
785 SymbolContext sc;
786 sc_list.GetContextAtIndex(0, sc);
787 if (sc.symbol != NULL)
788 impl_code_address = sc.symbol->GetValue();
789
Greg Clayton1629c432011-01-14 19:21:25 +0000790 //lldb::addr_t addr = impl_code_address.GetLoadAddress (exe_ctx.target);
791 //printf ("Getting address for our_utility_function: 0x%llx.\n", addr);
Jim Ingham957373f2010-12-10 00:26:25 +0000792 }
793 else
794 {
Greg Clayton1629c432011-01-14 19:21:25 +0000795 //printf ("Could not find implementation function address.\n");
Jim Ingham957373f2010-12-10 00:26:25 +0000796 return ret_plan_sp;
797 }
798 }
799 else if (!m_impl_code.get())
800 {
801 m_impl_code.reset (new ClangUtilityFunction (g_lookup_implementation_function_code,
802 g_lookup_implementation_function_name));
803 if (!m_impl_code->Install(errors, exe_ctx))
804 {
805 if (log)
806 log->Printf ("Failed to install implementation lookup: %s.", errors.GetData());
807 m_impl_code.reset();
808 return ret_plan_sp;
809 }
810 impl_code_address.Clear();
811 impl_code_address.SetOffset(m_impl_code->StartAddress());
812 }
813 else
814 {
815 impl_code_address.Clear();
816 impl_code_address.SetOffset(m_impl_code->StartAddress());
817 }
818
819 // Next make the runner function for our implementation utility function.
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000820 if (!m_impl_function.get())
821 {
822 m_impl_function.reset(new ClangFunction(process->GetTargetTriple().GetCString(),
823 clang_ast_context,
824 clang_void_ptr_type,
Jim Ingham957373f2010-12-10 00:26:25 +0000825 impl_code_address,
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000826 dispatch_values));
Jim Ingham957373f2010-12-10 00:26:25 +0000827
828 errors.Clear();
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000829 unsigned num_errors = m_impl_function->CompileFunction(errors);
830 if (num_errors)
831 {
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000832 if (log)
833 log->Printf ("Error compiling function: \"%s\".", errors.GetData());
834 return ret_plan_sp;
835 }
836
837 errors.Clear();
Jim Ingham957373f2010-12-10 00:26:25 +0000838 if (!m_impl_function->WriteFunctionWrapper(exe_ctx, errors))
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000839 {
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000840 if (log)
841 log->Printf ("Error Inserting function: \"%s\".", errors.GetData());
842 return ret_plan_sp;
843 }
844 }
845
Jim Ingham957373f2010-12-10 00:26:25 +0000846 }
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000847
848 errors.Clear();
849
Jim Ingham957373f2010-12-10 00:26:25 +0000850 // Now write down the argument values for this particular call. This looks like it might be a race condition
851 // if other threads were calling into here, but actually it isn't because we allocate a new args structure for
852 // this call by passing args_addr = LLDB_INVALID_ADDRESS...
853
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000854 lldb::addr_t args_addr = LLDB_INVALID_ADDRESS;
Jim Ingham957373f2010-12-10 00:26:25 +0000855 if (!m_impl_function->WriteFunctionArguments (exe_ctx, args_addr, impl_code_address, dispatch_values, errors))
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000856 return ret_plan_sp;
857
Jim Ingham957373f2010-12-10 00:26:25 +0000858 ret_plan_sp.reset (new AppleThreadPlanStepThroughObjCTrampoline (thread, this, args_addr,
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000859 dispatch_values.GetValueAtIndex(0)->GetScalar().ULongLong(),
Jim Ingham957373f2010-12-10 00:26:25 +0000860 isa_addr, sel_addr,
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000861 stop_others));
Jim Ingham58221732010-11-05 00:18:21 +0000862 if (log)
863 {
864 StreamString s;
865 ret_plan_sp->GetDescription(&s, eDescriptionLevelFull);
866 log->Printf("Using ObjC step plan: %s.\n", s.GetData());
867 }
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000868 }
869 else
870 {
871 if (log)
872 log->Printf ("Found implementation address in cache: 0x%llx", impl_addr);
873
874 ret_plan_sp.reset (new ThreadPlanRunToAddress (thread, impl_addr, stop_others));
875 }
876 }
877
878 return ret_plan_sp;
879}
880
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000881ClangFunction *
Jim Ingham5a369122010-09-28 01:25:32 +0000882AppleObjCTrampolineHandler::GetLookupImplementationWrapperFunction ()
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000883{
884 return m_impl_function.get();
885}