blob: 77a8318afbd9aeb85ac4b7ccf9805a5b225908b2 [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
Jim Ingham957373f2010-12-10 00:26:25 +0000667 ExecutionContext exe_ctx;
668 thread.CalculateExecutionContext (exe_ctx);
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000669
Jim Ingham8268cca2011-03-17 21:04:33 +0000670 // isa_addr will store the class pointer that the method is being dispatched to - so either the class
671 // directly or the super class if this is one of the objc_msgSendSuper flavors. That's mostly used to
672 // look up the class/selector pair in our cache.
Jim Ingham957373f2010-12-10 00:26:25 +0000673
Jim Ingham8268cca2011-03-17 21:04:33 +0000674 lldb::addr_t isa_addr = LLDB_INVALID_ADDRESS;
675 lldb::addr_t sel_addr = argument_values.GetValueAtIndex(sel_index)->GetScalar().ULongLong();
676
677 // Figure out the class this is being dispatched to and see if we've already cached this method call,
678 // If so we can push a run-to-address plan directly. Otherwise we have to figure out where
679 // the implementation lives.
680
681 if (this_dispatch.is_super)
682 {
683 if (this_dispatch.is_super2)
684 {
685 // In the objc_msgSendSuper2 case, we don't get the object directly, we get a structure containing
686 // the object and the class to which the super message is being sent. So we need to dig the super
687 // out of the class and use that.
688
689 Value super_value(*(argument_values.GetValueAtIndex(obj_index)));
690 super_value.GetScalar() += process->GetAddressByteSize();
691 super_value.ResolveValue (&exe_ctx, clang_ast_context->getASTContext());
692
693 if (super_value.GetScalar().IsValid())
694 {
695
696 // isa_value now holds the class pointer. The second word of the class pointer is the super-class pointer:
697 super_value.GetScalar() += process->GetAddressByteSize();
698 super_value.ResolveValue (&exe_ctx, clang_ast_context->getASTContext());
699 if (super_value.GetScalar().IsValid())
700 isa_addr = super_value.GetScalar().ULongLong();
701 else
702 {
703 if (log)
704 log->Printf("Failed to extract the super class value from the class in objc_super.");
705 }
706 }
707 else
708 {
709 if (log)
710 log->Printf("Failed to extract the class value from objc_super.");
711 }
712 }
713 else
714 {
715 // In the objc_msgSendSuper case, we don't get the object directly, we get a two element structure containing
716 // the object and the super class to which the super message is being sent. So the class we want is
717 // the second element of this structure.
718
719 Value super_value(*(argument_values.GetValueAtIndex(obj_index)));
720 super_value.GetScalar() += process->GetAddressByteSize();
721 super_value.ResolveValue (&exe_ctx, clang_ast_context->getASTContext());
722
723 if (super_value.GetScalar().IsValid())
724 {
725 isa_addr = super_value.GetScalar().ULongLong();
726 }
727 else
728 {
729 if (log)
730 log->Printf("Failed to extract the class value from objc_super.");
731 }
732 }
733 }
734 else
735 {
736 // In the direct dispatch case, the object->isa is the class pointer we want.
737
738 // This is a little cheesy, but since object->isa is the first field,
739 // making the object value a load address value and resolving it will get
740 // the pointer sized data pointed to by that value...
741
742 // Note, it isn't a fatal error not to be able to get the address from the object, since this might
743 // be a "tagged pointer" which isn't a real object, but rather some word length encoded dingus.
744
745 Value isa_value(*(argument_values.GetValueAtIndex(obj_index)));
746
747 isa_value.SetValueType(Value::eValueTypeLoadAddress);
748 isa_value.ResolveValue(&exe_ctx, clang_ast_context->getASTContext());
749 if (isa_value.GetScalar().IsValid())
750 {
751 isa_addr = isa_value.GetScalar().ULongLong();
752 }
753 else
754 {
755 if (log)
756 log->Printf("Failed to extract the isa value from object.");
757 }
758
759 }
760
761 // Okay, we've got the address of the class for which we're resolving this, let's see if it's in our cache:
762 lldb::addr_t impl_addr = LLDB_INVALID_ADDRESS;
763
764 if (isa_addr != LLDB_INVALID_ADDRESS)
765 {
766 if (log)
767 {
768 log->Printf("Resolving call for class - 0x%llx and selector - 0x%llx",
769 isa_addr, sel_addr);
770 }
771 ObjCLanguageRuntime *objc_runtime = m_process_sp->GetObjCLanguageRuntime ();
772 assert(objc_runtime != NULL);
773
774 impl_addr = objc_runtime->LookupInMethodCache (isa_addr, sel_addr);
775 }
776
777 if (impl_addr != LLDB_INVALID_ADDRESS)
778 {
779 // Yup, it was in the cache, so we can run to that address directly.
780
781 if (log)
782 log->Printf ("Found implementation address in cache: 0x%llx", impl_addr);
783
784 ret_plan_sp.reset (new ThreadPlanRunToAddress (thread, impl_addr, stop_others));
785 }
786 else
787 {
Jim Ingham957373f2010-12-10 00:26:25 +0000788 // We haven't seen this class/selector pair yet. Look it up.
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000789 StreamString errors;
Jim Ingham957373f2010-12-10 00:26:25 +0000790 Address impl_code_address;
791
792 ValueList dispatch_values;
793
794 // We've will inject a little function in the target that takes the object, selector and some flags,
795 // and figures out the implementation. Looks like:
796 // void *__lldb_objc_find_implementation_for_selector (void *object,
797 // void *sel,
798 // int is_stret,
799 // int is_super,
800 // int is_super2,
801 // int is_fixup,
802 // int is_fixed,
803 // int debug)
804 // So set up the arguments for that call.
805
806 dispatch_values.PushValue (*(argument_values.GetValueAtIndex(obj_index)));
807 dispatch_values.PushValue (*(argument_values.GetValueAtIndex(sel_index)));
808
809 Value flag_value;
810 lldb::clang_type_t clang_int_type = clang_ast_context->GetBuiltinTypeForEncodingAndBitSize(lldb::eEncodingSint, 32);
811 flag_value.SetValueType (Value::eValueTypeScalar);
812 flag_value.SetContext (Value::eContextTypeClangType, clang_int_type);
813
814 if (this_dispatch.stret_return)
815 flag_value.GetScalar() = 1;
816 else
817 flag_value.GetScalar() = 0;
818 dispatch_values.PushValue (flag_value);
819
820 if (this_dispatch.is_super)
821 flag_value.GetScalar() = 1;
822 else
823 flag_value.GetScalar() = 0;
824 dispatch_values.PushValue (flag_value);
825
826 if (this_dispatch.is_super2)
827 flag_value.GetScalar() = 1;
828 else
829 flag_value.GetScalar() = 0;
830 dispatch_values.PushValue (flag_value);
831
832 switch (this_dispatch.fixedup)
833 {
834 case DispatchFunction::eFixUpNone:
835 flag_value.GetScalar() = 0;
836 dispatch_values.PushValue (flag_value);
837 dispatch_values.PushValue (flag_value);
838 break;
839 case DispatchFunction::eFixUpFixed:
840 flag_value.GetScalar() = 1;
841 dispatch_values.PushValue (flag_value);
842 flag_value.GetScalar() = 1;
843 dispatch_values.PushValue (flag_value);
844 break;
845 case DispatchFunction::eFixUpToFix:
846 flag_value.GetScalar() = 1;
847 dispatch_values.PushValue (flag_value);
848 flag_value.GetScalar() = 0;
849 dispatch_values.PushValue (flag_value);
850 break;
851 }
852 if (log)
853 flag_value.GetScalar() = 1;
854 else
855 flag_value.GetScalar() = 0; // FIXME - Set to 0 when debugging is done.
856 dispatch_values.PushValue (flag_value);
857
858 // Now, if we haven't already, make and insert the function as a ClangUtilityFunction, and make and insert
859 // it's runner ClangFunction.
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000860 {
861 // Scope for mutex locker:
Greg Claytonb1320972010-07-14 00:18:15 +0000862 Mutex::Locker locker(m_impl_function_mutex);
Jim Ingham957373f2010-12-10 00:26:25 +0000863
864 // First stage is to make the ClangUtility to hold our injected function:
865
866#define USE_BUILTIN_FUNCTION 0 // Define this to 1 and we will use the get_implementation function found in the target.
867 // This is useful for debugging additions to the get_impl function 'cause you don't have
868 // to bother with string-ifying the code into g_lookup_implementation_function_code.
869
870 if (USE_BUILTIN_FUNCTION)
871 {
872 ConstString our_utility_function_name("__lldb_objc_find_implementation_for_selector");
873 SymbolContextList sc_list;
874 exe_ctx.target->GetImages().FindSymbolsWithNameAndType (our_utility_function_name, eSymbolTypeCode, sc_list);
875 if (sc_list.GetSize() == 1)
876 {
877 SymbolContext sc;
878 sc_list.GetContextAtIndex(0, sc);
879 if (sc.symbol != NULL)
880 impl_code_address = sc.symbol->GetValue();
881
Greg Clayton1629c432011-01-14 19:21:25 +0000882 //lldb::addr_t addr = impl_code_address.GetLoadAddress (exe_ctx.target);
883 //printf ("Getting address for our_utility_function: 0x%llx.\n", addr);
Jim Ingham957373f2010-12-10 00:26:25 +0000884 }
885 else
886 {
Greg Clayton1629c432011-01-14 19:21:25 +0000887 //printf ("Could not find implementation function address.\n");
Jim Ingham957373f2010-12-10 00:26:25 +0000888 return ret_plan_sp;
889 }
890 }
891 else if (!m_impl_code.get())
892 {
893 m_impl_code.reset (new ClangUtilityFunction (g_lookup_implementation_function_code,
894 g_lookup_implementation_function_name));
895 if (!m_impl_code->Install(errors, exe_ctx))
896 {
897 if (log)
898 log->Printf ("Failed to install implementation lookup: %s.", errors.GetData());
899 m_impl_code.reset();
900 return ret_plan_sp;
901 }
902 impl_code_address.Clear();
903 impl_code_address.SetOffset(m_impl_code->StartAddress());
904 }
905 else
906 {
907 impl_code_address.Clear();
908 impl_code_address.SetOffset(m_impl_code->StartAddress());
909 }
910
911 // Next make the runner function for our implementation utility function.
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000912 if (!m_impl_function.get())
913 {
Jim Ingham35944dd2011-03-17 20:02:56 +0000914 m_impl_function.reset(new ClangFunction (thread,
Greg Clayton514487e2011-02-15 21:59:32 +0000915 clang_ast_context,
916 clang_void_ptr_type,
917 impl_code_address,
918 dispatch_values));
Jim Ingham957373f2010-12-10 00:26:25 +0000919
920 errors.Clear();
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000921 unsigned num_errors = m_impl_function->CompileFunction(errors);
922 if (num_errors)
923 {
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000924 if (log)
925 log->Printf ("Error compiling function: \"%s\".", errors.GetData());
926 return ret_plan_sp;
927 }
928
929 errors.Clear();
Jim Ingham957373f2010-12-10 00:26:25 +0000930 if (!m_impl_function->WriteFunctionWrapper(exe_ctx, errors))
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000931 {
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000932 if (log)
933 log->Printf ("Error Inserting function: \"%s\".", errors.GetData());
934 return ret_plan_sp;
935 }
936 }
937
Jim Ingham957373f2010-12-10 00:26:25 +0000938 }
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000939
940 errors.Clear();
941
Jim Ingham957373f2010-12-10 00:26:25 +0000942 // Now write down the argument values for this particular call. This looks like it might be a race condition
943 // if other threads were calling into here, but actually it isn't because we allocate a new args structure for
944 // this call by passing args_addr = LLDB_INVALID_ADDRESS...
945
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000946 lldb::addr_t args_addr = LLDB_INVALID_ADDRESS;
Jim Ingham957373f2010-12-10 00:26:25 +0000947 if (!m_impl_function->WriteFunctionArguments (exe_ctx, args_addr, impl_code_address, dispatch_values, errors))
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000948 return ret_plan_sp;
949
Jim Ingham957373f2010-12-10 00:26:25 +0000950 ret_plan_sp.reset (new AppleThreadPlanStepThroughObjCTrampoline (thread, this, args_addr,
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000951 dispatch_values.GetValueAtIndex(0)->GetScalar().ULongLong(),
Jim Ingham957373f2010-12-10 00:26:25 +0000952 isa_addr, sel_addr,
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000953 stop_others));
Jim Ingham58221732010-11-05 00:18:21 +0000954 if (log)
955 {
956 StreamString s;
957 ret_plan_sp->GetDescription(&s, eDescriptionLevelFull);
958 log->Printf("Using ObjC step plan: %s.\n", s.GetData());
959 }
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000960 }
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000961 }
962
963 return ret_plan_sp;
964}
965
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000966ClangFunction *
Jim Ingham5a369122010-09-28 01:25:32 +0000967AppleObjCTrampolineHandler::GetLookupImplementationWrapperFunction ()
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000968{
969 return m_impl_function.get();
970}