blob: 9733a99bb5dcd8ab66d3b43691bc825f9de9b3d0 [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"
Chris Lattner30fdc8d2010-06-08 16:52:24 +000021#include "lldb/Core/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 {
347 const Address &temp_address = trampoline_symbol->GetValue();
348 if (!temp_address.IsValid())
349 return false;
350
351 m_trampoline_header = temp_address.GetLoadAddress(&target);
352 if (m_trampoline_header == LLDB_INVALID_ADDRESS)
353 return false;
354
355 // Next look up the "changed" symbol and set a breakpoint on that...
356 ConstString changed_name ("gdb_objc_trampolines_changed");
357 const Symbol *changed_symbol = m_objc_module_sp->FindFirstSymbolWithNameAndType(changed_name,
358 eSymbolTypeCode);
359 if (changed_symbol != NULL)
360 {
361 const Address &temp_address = changed_symbol->GetValue();
362 if (!temp_address.IsValid())
363 return false;
364
365 lldb::addr_t changed_addr = temp_address.GetLoadAddress(&target);
366 if (changed_addr != LLDB_INVALID_ADDRESS)
367 {
368 BreakpointSP trampolines_changed_bp_sp = target.CreateBreakpoint (changed_addr,
369 true);
370 if (trampolines_changed_bp_sp != NULL)
371 {
372 m_trampolines_changed_bp_id = trampolines_changed_bp_sp->GetID();
373 trampolines_changed_bp_sp->SetCallback (RefreshTrampolines, this, true);
374 return true;
375 }
376 }
377 }
378 }
379 }
380
381 return false;
382}
383
384bool
385AppleObjCTrampolineHandler::AppleObjCVTables::RefreshTrampolines (void *baton,
386 StoppointCallbackContext *context,
387 lldb::user_id_t break_id,
388 lldb::user_id_t break_loc_id)
389{
390 AppleObjCVTables *vtable_handler = (AppleObjCVTables *) baton;
391 if (vtable_handler->InitializeVTableSymbols())
392 {
393 // The Update function is called with the address of an added region. So we grab that address, and
394 // feed it into ReadRegions. Of course, our friend the ABI will get the values for us.
395 Process *process = context->exe_ctx.process;
396 const ABI *abi = process->GetABI();
397
398 ClangASTContext *clang_ast_context = process->GetTarget().GetScratchClangASTContext();
399 ValueList argument_values;
400 Value input_value;
401 void *clang_void_ptr_type = clang_ast_context->GetVoidPtrType(false);
402 input_value.SetValueType (Value::eValueTypeScalar);
Greg Clayton526e5af2010-11-13 03:52:47 +0000403 input_value.SetContext (Value::eContextTypeClangType, clang_void_ptr_type);
Jim Ingham58221732010-11-05 00:18:21 +0000404 argument_values.PushValue(input_value);
405
406 bool success = abi->GetArgumentValues (*(context->exe_ctx.thread), argument_values);
407 if (!success)
408 return false;
409
410 // Now get a pointer value from the zeroth argument.
411 Error error;
412 DataExtractor data;
413 error = argument_values.GetValueAtIndex(0)->GetValueAsData(&(context->exe_ctx), clang_ast_context->getASTContext(), data, 0);
414 uint32_t offset_ptr = 0;
415 lldb::addr_t region_addr = data.GetPointer(&offset_ptr);
416
417 if (region_addr != 0)
418 vtable_handler->ReadRegions(region_addr);
419 }
420 return false;
421}
422
423bool
424AppleObjCTrampolineHandler::AppleObjCVTables::ReadRegions ()
425{
426 // The no argument version reads the start region from the value of the gdb_regions_header, and
427 // gets started from there.
428
429 m_regions.clear();
430 if (!InitializeVTableSymbols())
431 return false;
432 char memory_buffer[8];
433 DataExtractor data(memory_buffer, sizeof(memory_buffer),
434 m_process_sp->GetByteOrder(),
435 m_process_sp->GetAddressByteSize());
436 Error error;
437 size_t bytes_read = m_process_sp->ReadMemory (m_trampoline_header, memory_buffer, m_process_sp->GetAddressByteSize(), error);
438 if (bytes_read != m_process_sp->GetAddressByteSize())
439 return false;
440
441 uint32_t offset_ptr = 0;
442 lldb::addr_t region_addr = data.GetPointer(&offset_ptr);
443 return ReadRegions (region_addr);
444}
445
446bool
447AppleObjCTrampolineHandler::AppleObjCVTables::ReadRegions (lldb::addr_t region_addr)
448{
449 if (!m_process_sp)
450 return false;
451
Greg Clayton2d4edfb2010-11-06 01:53:30 +0000452 LogSP log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_STEP));
Jim Ingham58221732010-11-05 00:18:21 +0000453
454 // We aren't starting at the trampoline symbol.
455 InitializeVTableSymbols ();
456 lldb::addr_t next_region = region_addr;
457
458 // Read in the sizes of the headers.
459 while (next_region != 0)
460 {
461 m_regions.push_back (VTableRegion(this, next_region));
462 if (!m_regions.back().IsValid())
463 {
464 m_regions.clear();
465 return false;
466 }
467 if (log)
468 {
469 StreamString s;
470 m_regions.back().Dump(s);
471 log->Printf("Read vtable region: \n%s", s.GetData());
472 }
473
474 next_region = m_regions.back().GetNextRegionAddr();
475 }
476
477 return true;
478}
479
480bool
481AppleObjCTrampolineHandler::AppleObjCVTables::IsAddressInVTables (lldb::addr_t addr, uint32_t &flags)
482{
483 region_collection::iterator pos, end = m_regions.end();
484 for (pos = m_regions.begin(); pos != end; pos++)
485 {
486 if ((*pos).AddressInRegion (addr, flags))
487 return true;
488 }
489 return false;
490}
491
Jim Ingham5a369122010-09-28 01:25:32 +0000492const AppleObjCTrampolineHandler::DispatchFunction
493AppleObjCTrampolineHandler::g_dispatch_functions[] =
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000494{
Jim Ingham957373f2010-12-10 00:26:25 +0000495 // NAME STRET SUPER SUPER2 FIXUP TYPE
496 {"objc_msgSend", false, false, false, DispatchFunction::eFixUpNone },
497 {"objc_msgSend_fixup", false, false, false, DispatchFunction::eFixUpToFix },
498 {"objc_msgSend_fixedup", false, false, false, DispatchFunction::eFixUpFixed },
499 {"objc_msgSend_stret", true, false, false, DispatchFunction::eFixUpNone },
500 {"objc_msgSend_stret_fixup", true, false, false, DispatchFunction::eFixUpToFix },
501 {"objc_msgSend_stret_fixedup", true, false, false, DispatchFunction::eFixUpFixed },
502 {"objc_msgSend_fpret", false, false, false, DispatchFunction::eFixUpNone },
503 {"objc_msgSend_fpret_fixup", false, false, false, DispatchFunction::eFixUpToFix },
504 {"objc_msgSend_fpret_fixedup", false, false, false, DispatchFunction::eFixUpFixed },
505 {"objc_msgSend_fp2ret", false, false, true, DispatchFunction::eFixUpNone },
506 {"objc_msgSend_fp2ret_fixup", false, false, true, DispatchFunction::eFixUpToFix },
507 {"objc_msgSend_fp2ret_fixedup", false, false, true, DispatchFunction::eFixUpFixed },
508 {"objc_msgSendSuper", false, true, false, DispatchFunction::eFixUpNone },
509 {"objc_msgSendSuper_stret", true, true, false, DispatchFunction::eFixUpNone },
510 {"objc_msgSendSuper2", false, true, true, DispatchFunction::eFixUpNone },
511 {"objc_msgSendSuper2_fixup", false, true, true, DispatchFunction::eFixUpToFix },
512 {"objc_msgSendSuper2_fixedup", false, true, true, DispatchFunction::eFixUpFixed },
513 {"objc_msgSendSuper2_stret", true, true, true, DispatchFunction::eFixUpNone },
514 {"objc_msgSendSuper2_stret_fixup", true, true, true, DispatchFunction::eFixUpToFix },
515 {"objc_msgSendSuper2_stret_fixedup", true, true, true, DispatchFunction::eFixUpFixed },
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000516 {NULL}
517};
518
Jim Ingham58221732010-11-05 00:18:21 +0000519AppleObjCTrampolineHandler::AppleObjCTrampolineHandler (ProcessSP process_sp, ModuleSP objc_module_sp) :
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000520 m_process_sp (process_sp),
Jim Ingham58221732010-11-05 00:18:21 +0000521 m_objc_module_sp (objc_module_sp),
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000522 m_impl_fn_addr (LLDB_INVALID_ADDRESS),
Jim Ingham957373f2010-12-10 00:26:25 +0000523 m_impl_stret_fn_addr (LLDB_INVALID_ADDRESS),
524 m_msg_forward_addr (LLDB_INVALID_ADDRESS)
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000525{
526 // Look up the known resolution functions:
527
528 ConstString get_impl_name("class_getMethodImplementation");
529 ConstString get_impl_stret_name("class_getMethodImplementation_stret");
Jim Ingham957373f2010-12-10 00:26:25 +0000530 ConstString msg_forward_name("_objc_msgForward");
531 ConstString msg_forward_stret_name("_objc_msgForward_stret");
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000532
Greg Claytonf5e56de2010-09-14 23:36:40 +0000533 Target *target = m_process_sp ? &m_process_sp->GetTarget() : NULL;
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000534 const Symbol *class_getMethodImplementation = m_objc_module_sp->FindFirstSymbolWithNameAndType (get_impl_name, eSymbolTypeCode);
535 const Symbol *class_getMethodImplementation_stret = m_objc_module_sp->FindFirstSymbolWithNameAndType (get_impl_stret_name, eSymbolTypeCode);
Jim Ingham957373f2010-12-10 00:26:25 +0000536 const Symbol *msg_forward = m_objc_module_sp->FindFirstSymbolWithNameAndType (msg_forward_name, eSymbolTypeCode);
537 const Symbol *msg_forward_stret = m_objc_module_sp->FindFirstSymbolWithNameAndType (msg_forward_stret_name, eSymbolTypeCode);
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000538
539 if (class_getMethodImplementation)
Greg Claytonf5e56de2010-09-14 23:36:40 +0000540 m_impl_fn_addr = class_getMethodImplementation->GetValue().GetLoadAddress(target);
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000541 if (class_getMethodImplementation_stret)
Greg Claytonf5e56de2010-09-14 23:36:40 +0000542 m_impl_stret_fn_addr = class_getMethodImplementation_stret->GetValue().GetLoadAddress(target);
Jim Ingham957373f2010-12-10 00:26:25 +0000543 if (msg_forward)
544 m_msg_forward_addr = msg_forward->GetValue().GetLoadAddress(target);
545 if (msg_forward_stret)
546 m_msg_forward_stret_addr = msg_forward_stret->GetValue().GetLoadAddress(target);
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000547
548 // FIXME: Do some kind of logging here.
549 if (m_impl_fn_addr == LLDB_INVALID_ADDRESS || m_impl_stret_fn_addr == LLDB_INVALID_ADDRESS)
550 return;
551
552 // Look up the addresses for the objc dispatch functions and cache them. For now I'm inspecting the symbol
553 // names dynamically to figure out how to dispatch to them. If it becomes more complicated than this we can
554 // turn the g_dispatch_functions char * array into a template table, and populate the DispatchFunction map
555 // from there.
556
557 for (int i = 0; g_dispatch_functions[i].name != NULL; i++)
558 {
559 ConstString name_const_str(g_dispatch_functions[i].name);
560 const Symbol *msgSend_symbol = m_objc_module_sp->FindFirstSymbolWithNameAndType (name_const_str, eSymbolTypeCode);
561 if (msgSend_symbol)
562 {
Greg Clayton710dd5a2011-01-08 20:28:42 +0000563 // FixMe: Make g_dispatch_functions static table of DispatchFunctions, and have the map be address->index.
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000564 // Problem is we also need to lookup the dispatch function. For now we could have a side table of stret & non-stret
565 // dispatch functions. If that's as complex as it gets, we're fine.
566
Greg Claytonf5e56de2010-09-14 23:36:40 +0000567 lldb::addr_t sym_addr = msgSend_symbol->GetValue().GetLoadAddress(target);
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000568
569 m_msgSend_map.insert(std::pair<lldb::addr_t, int>(sym_addr, i));
570 }
571 }
Jim Ingham58221732010-11-05 00:18:21 +0000572
573 // Build our vtable dispatch handler here:
574 m_vtables_ap.reset(new AppleObjCVTables(process_sp, m_objc_module_sp));
575 if (m_vtables_ap.get())
576 m_vtables_ap->ReadRegions();
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000577}
578
579ThreadPlanSP
Jim Ingham5a369122010-09-28 01:25:32 +0000580AppleObjCTrampolineHandler::GetStepThroughDispatchPlan (Thread &thread, bool stop_others)
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000581{
582 ThreadPlanSP ret_plan_sp;
583 lldb::addr_t curr_pc = thread.GetRegisterContext()->GetPC();
584
Jim Ingham58221732010-11-05 00:18:21 +0000585 DispatchFunction this_dispatch;
586 bool found_it = false;
587
Jim Ingham957373f2010-12-10 00:26:25 +0000588 // First step is to look and see if we are in one of the known ObjC dispatch functions. We've already compiled
589 // a table of same, so consult it.
590
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000591 MsgsendMap::iterator pos;
592 pos = m_msgSend_map.find (curr_pc);
593 if (pos != m_msgSend_map.end())
594 {
Jim Ingham58221732010-11-05 00:18:21 +0000595 this_dispatch = g_dispatch_functions[(*pos).second];
596 found_it = true;
597 }
598
Jim Ingham957373f2010-12-10 00:26:25 +0000599 // Next check to see if we are in a vtable region:
600
Jim Ingham58221732010-11-05 00:18:21 +0000601 if (!found_it)
602 {
603 uint32_t flags;
604 if (m_vtables_ap.get())
605 {
606 found_it = m_vtables_ap->IsAddressInVTables (curr_pc, flags);
607 if (found_it)
608 {
609 this_dispatch.name = "vtable";
610 this_dispatch.stret_return
611 = (flags & AppleObjCVTables::eOBJC_TRAMPOLINE_STRET) == AppleObjCVTables::eOBJC_TRAMPOLINE_STRET;
612 this_dispatch.is_super = false;
613 this_dispatch.fixedup = DispatchFunction::eFixUpFixed;
614 }
615 }
616 }
617
618 if (found_it)
619 {
Greg Clayton2d4edfb2010-11-06 01:53:30 +0000620 LogSP log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_STEP));
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000621
Jim Ingham957373f2010-12-10 00:26:25 +0000622 // We are decoding a method dispatch.
623 // First job is to pull the arguments out:
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000624
625 lldb::StackFrameSP thread_cur_frame = thread.GetStackFrameAtIndex(0);
626
627 Process *process = thread.CalculateProcess();
628 const ABI *abi = process->GetABI();
629 if (abi == NULL)
630 return ret_plan_sp;
631
632 Target *target = thread.CalculateTarget();
633
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000634 ClangASTContext *clang_ast_context = target->GetScratchClangASTContext();
635 ValueList argument_values;
Jim Ingham957373f2010-12-10 00:26:25 +0000636 Value void_ptr_value;
637 lldb::clang_type_t clang_void_ptr_type = clang_ast_context->GetVoidPtrType(false);
638 void_ptr_value.SetValueType (Value::eValueTypeScalar);
639 void_ptr_value.SetContext (Value::eContextTypeClangType, clang_void_ptr_type);
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000640
641 int obj_index;
642 int sel_index;
643
644 // If this is a struct return dispatch, then the first argument is the
645 // return struct pointer, and the object is the second, and the selector is the third.
646 // Otherwise the object is the first and the selector the second.
Jim Ingham58221732010-11-05 00:18:21 +0000647 if (this_dispatch.stret_return)
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000648 {
649 obj_index = 1;
650 sel_index = 2;
Jim Ingham957373f2010-12-10 00:26:25 +0000651 argument_values.PushValue(void_ptr_value);
652 argument_values.PushValue(void_ptr_value);
653 argument_values.PushValue(void_ptr_value);
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000654 }
655 else
656 {
657 obj_index = 0;
658 sel_index = 1;
Jim Ingham957373f2010-12-10 00:26:25 +0000659 argument_values.PushValue(void_ptr_value);
660 argument_values.PushValue(void_ptr_value);
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000661 }
662
663
664 bool success = abi->GetArgumentValues (thread, argument_values);
665 if (!success)
666 return ret_plan_sp;
Jim Ingham957373f2010-12-10 00:26:25 +0000667
668 // Pull the class out of the Object and see if we've already cached this method call,
669 // If so we can push a run-to-address plan directly. Otherwise we have to figure out where
670 // the implementation lives.
671
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000672 Value isa_value(*(argument_values.GetValueAtIndex(obj_index)));
Jim Ingham957373f2010-12-10 00:26:25 +0000673
674 // This is a little cheesy, but since object->isa is the first field,
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000675 // making the object value a load address value and resolving it will get
676 // the pointer sized data pointed to by that value...
Jim Ingham957373f2010-12-10 00:26:25 +0000677 ExecutionContext exe_ctx;
678 thread.CalculateExecutionContext (exe_ctx);
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000679
680 isa_value.SetValueType(Value::eValueTypeLoadAddress);
Jim Ingham957373f2010-12-10 00:26:25 +0000681 isa_value.ResolveValue(&exe_ctx, clang_ast_context->getASTContext());
682 lldb::addr_t isa_addr = isa_value.GetScalar().ULongLong();
683 lldb::addr_t sel_addr = argument_values.GetValueAtIndex(sel_index)->GetScalar().ULongLong();
684
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000685 if (log)
686 {
687 log->Printf("Resolving method call for class - 0x%llx and selector - 0x%llx",
Jim Ingham957373f2010-12-10 00:26:25 +0000688 isa_addr, sel_addr);
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000689 }
Jim Ingham5a369122010-09-28 01:25:32 +0000690 ObjCLanguageRuntime *objc_runtime = m_process_sp->GetObjCLanguageRuntime ();
691 assert(objc_runtime != NULL);
Jim Ingham957373f2010-12-10 00:26:25 +0000692
693 lldb::addr_t impl_addr = objc_runtime->LookupInMethodCache (isa_addr, sel_addr);
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000694
695 if (impl_addr == LLDB_INVALID_ADDRESS)
Jim Ingham957373f2010-12-10 00:26:25 +0000696 {
697 // We haven't seen this class/selector pair yet. Look it up.
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000698 StreamString errors;
Jim Ingham957373f2010-12-10 00:26:25 +0000699 Address impl_code_address;
700
701 ValueList dispatch_values;
702
703 // We've will inject a little function in the target that takes the object, selector and some flags,
704 // and figures out the implementation. Looks like:
705 // void *__lldb_objc_find_implementation_for_selector (void *object,
706 // void *sel,
707 // int is_stret,
708 // int is_super,
709 // int is_super2,
710 // int is_fixup,
711 // int is_fixed,
712 // int debug)
713 // So set up the arguments for that call.
714
715 dispatch_values.PushValue (*(argument_values.GetValueAtIndex(obj_index)));
716 dispatch_values.PushValue (*(argument_values.GetValueAtIndex(sel_index)));
717
718 Value flag_value;
719 lldb::clang_type_t clang_int_type = clang_ast_context->GetBuiltinTypeForEncodingAndBitSize(lldb::eEncodingSint, 32);
720 flag_value.SetValueType (Value::eValueTypeScalar);
721 flag_value.SetContext (Value::eContextTypeClangType, clang_int_type);
722
723 if (this_dispatch.stret_return)
724 flag_value.GetScalar() = 1;
725 else
726 flag_value.GetScalar() = 0;
727 dispatch_values.PushValue (flag_value);
728
729 if (this_dispatch.is_super)
730 flag_value.GetScalar() = 1;
731 else
732 flag_value.GetScalar() = 0;
733 dispatch_values.PushValue (flag_value);
734
735 if (this_dispatch.is_super2)
736 flag_value.GetScalar() = 1;
737 else
738 flag_value.GetScalar() = 0;
739 dispatch_values.PushValue (flag_value);
740
741 switch (this_dispatch.fixedup)
742 {
743 case DispatchFunction::eFixUpNone:
744 flag_value.GetScalar() = 0;
745 dispatch_values.PushValue (flag_value);
746 dispatch_values.PushValue (flag_value);
747 break;
748 case DispatchFunction::eFixUpFixed:
749 flag_value.GetScalar() = 1;
750 dispatch_values.PushValue (flag_value);
751 flag_value.GetScalar() = 1;
752 dispatch_values.PushValue (flag_value);
753 break;
754 case DispatchFunction::eFixUpToFix:
755 flag_value.GetScalar() = 1;
756 dispatch_values.PushValue (flag_value);
757 flag_value.GetScalar() = 0;
758 dispatch_values.PushValue (flag_value);
759 break;
760 }
761 if (log)
762 flag_value.GetScalar() = 1;
763 else
764 flag_value.GetScalar() = 0; // FIXME - Set to 0 when debugging is done.
765 dispatch_values.PushValue (flag_value);
766
767 // Now, if we haven't already, make and insert the function as a ClangUtilityFunction, and make and insert
768 // it's runner ClangFunction.
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000769 {
770 // Scope for mutex locker:
Greg Claytonb1320972010-07-14 00:18:15 +0000771 Mutex::Locker locker(m_impl_function_mutex);
Jim Ingham957373f2010-12-10 00:26:25 +0000772
773 // First stage is to make the ClangUtility to hold our injected function:
774
775#define USE_BUILTIN_FUNCTION 0 // Define this to 1 and we will use the get_implementation function found in the target.
776 // This is useful for debugging additions to the get_impl function 'cause you don't have
777 // to bother with string-ifying the code into g_lookup_implementation_function_code.
778
779 if (USE_BUILTIN_FUNCTION)
780 {
781 ConstString our_utility_function_name("__lldb_objc_find_implementation_for_selector");
782 SymbolContextList sc_list;
783 exe_ctx.target->GetImages().FindSymbolsWithNameAndType (our_utility_function_name, eSymbolTypeCode, sc_list);
784 if (sc_list.GetSize() == 1)
785 {
786 SymbolContext sc;
787 sc_list.GetContextAtIndex(0, sc);
788 if (sc.symbol != NULL)
789 impl_code_address = sc.symbol->GetValue();
790
Greg Clayton1629c432011-01-14 19:21:25 +0000791 //lldb::addr_t addr = impl_code_address.GetLoadAddress (exe_ctx.target);
792 //printf ("Getting address for our_utility_function: 0x%llx.\n", addr);
Jim Ingham957373f2010-12-10 00:26:25 +0000793 }
794 else
795 {
Greg Clayton1629c432011-01-14 19:21:25 +0000796 //printf ("Could not find implementation function address.\n");
Jim Ingham957373f2010-12-10 00:26:25 +0000797 return ret_plan_sp;
798 }
799 }
800 else if (!m_impl_code.get())
801 {
802 m_impl_code.reset (new ClangUtilityFunction (g_lookup_implementation_function_code,
803 g_lookup_implementation_function_name));
804 if (!m_impl_code->Install(errors, exe_ctx))
805 {
806 if (log)
807 log->Printf ("Failed to install implementation lookup: %s.", errors.GetData());
808 m_impl_code.reset();
809 return ret_plan_sp;
810 }
811 impl_code_address.Clear();
812 impl_code_address.SetOffset(m_impl_code->StartAddress());
813 }
814 else
815 {
816 impl_code_address.Clear();
817 impl_code_address.SetOffset(m_impl_code->StartAddress());
818 }
819
820 // Next make the runner function for our implementation utility function.
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000821 if (!m_impl_function.get())
822 {
823 m_impl_function.reset(new ClangFunction(process->GetTargetTriple().GetCString(),
824 clang_ast_context,
825 clang_void_ptr_type,
Jim Ingham957373f2010-12-10 00:26:25 +0000826 impl_code_address,
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000827 dispatch_values));
Jim Ingham957373f2010-12-10 00:26:25 +0000828
829 errors.Clear();
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000830 unsigned num_errors = m_impl_function->CompileFunction(errors);
831 if (num_errors)
832 {
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000833 if (log)
834 log->Printf ("Error compiling function: \"%s\".", errors.GetData());
835 return ret_plan_sp;
836 }
837
838 errors.Clear();
Jim Ingham957373f2010-12-10 00:26:25 +0000839 if (!m_impl_function->WriteFunctionWrapper(exe_ctx, errors))
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000840 {
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000841 if (log)
842 log->Printf ("Error Inserting function: \"%s\".", errors.GetData());
843 return ret_plan_sp;
844 }
845 }
846
Jim Ingham957373f2010-12-10 00:26:25 +0000847 }
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000848
849 errors.Clear();
850
Jim Ingham957373f2010-12-10 00:26:25 +0000851 // Now write down the argument values for this particular call. This looks like it might be a race condition
852 // if other threads were calling into here, but actually it isn't because we allocate a new args structure for
853 // this call by passing args_addr = LLDB_INVALID_ADDRESS...
854
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000855 lldb::addr_t args_addr = LLDB_INVALID_ADDRESS;
Jim Ingham957373f2010-12-10 00:26:25 +0000856 if (!m_impl_function->WriteFunctionArguments (exe_ctx, args_addr, impl_code_address, dispatch_values, errors))
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000857 return ret_plan_sp;
858
Jim Ingham957373f2010-12-10 00:26:25 +0000859 ret_plan_sp.reset (new AppleThreadPlanStepThroughObjCTrampoline (thread, this, args_addr,
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000860 dispatch_values.GetValueAtIndex(0)->GetScalar().ULongLong(),
Jim Ingham957373f2010-12-10 00:26:25 +0000861 isa_addr, sel_addr,
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000862 stop_others));
Jim Ingham58221732010-11-05 00:18:21 +0000863 if (log)
864 {
865 StreamString s;
866 ret_plan_sp->GetDescription(&s, eDescriptionLevelFull);
867 log->Printf("Using ObjC step plan: %s.\n", s.GetData());
868 }
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000869 }
870 else
871 {
872 if (log)
873 log->Printf ("Found implementation address in cache: 0x%llx", impl_addr);
874
875 ret_plan_sp.reset (new ThreadPlanRunToAddress (thread, impl_addr, stop_others));
876 }
877 }
878
879 return ret_plan_sp;
880}
881
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000882ClangFunction *
Jim Ingham5a369122010-09-28 01:25:32 +0000883AppleObjCTrampolineHandler::GetLookupImplementationWrapperFunction ()
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000884{
885 return m_impl_function.get();
886}