blob: a10ddcf92c4ee1152a0017495ee3a8e6657b0d4f [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"
25#include "lldb/Expression/ClangFunction.h"
26#include "lldb/Symbol/ClangASTContext.h"
27#include "lldb/Target/ObjCLanguageRuntime.h"
28#include "lldb/Target/Process.h"
Chris Lattner30fdc8d2010-06-08 16:52:24 +000029#include "lldb/Target/RegisterContext.h"
30#include "lldb/Target/Target.h"
Jim Ingham5a369122010-09-28 01:25:32 +000031#include "lldb/Target/Thread.h"
Chris Lattner30fdc8d2010-06-08 16:52:24 +000032#include "lldb/Target/ExecutionContext.h"
Chris Lattner30fdc8d2010-06-08 16:52:24 +000033#include "lldb/Target/ThreadPlanRunToAddress.h"
34
35using namespace lldb;
36using namespace lldb_private;
37
Jim Ingham58221732010-11-05 00:18:21 +000038
39AppleObjCTrampolineHandler::AppleObjCVTables::VTableRegion::VTableRegion(AppleObjCVTables *owner, lldb::addr_t header_addr) :
40 m_valid (true),
41 m_owner(owner),
42 m_header_addr (header_addr),
43 m_code_start_addr(0),
44 m_code_end_addr (0),
45 m_next_region (0)
46{
47 SetUpRegion ();
48}
49
50void
51AppleObjCTrampolineHandler::AppleObjCVTables::VTableRegion::SetUpRegion()
52{
53 // The header looks like:
54 //
55 // uint16_t headerSize
56 // uint16_t descSize
57 // uint32_t descCount
58 // void * next
59 //
60 // First read in the header:
61
62 char memory_buffer[16];
63 Process *process = m_owner->GetProcess();
64 DataExtractor data(memory_buffer, sizeof(memory_buffer),
65 process->GetByteOrder(),
66 process->GetAddressByteSize());
67 size_t actual_size = 8 + process->GetAddressByteSize();
68 Error error;
69 size_t bytes_read = process->ReadMemory (m_header_addr, memory_buffer, actual_size, error);
70 if (bytes_read != actual_size)
71 {
72 m_valid = false;
73 return;
74 }
75
76 uint32_t offset_ptr = 0;
77 uint16_t header_size = data.GetU16(&offset_ptr);
78 uint16_t descriptor_size = data.GetU16(&offset_ptr);
79 size_t num_descriptors = data.GetU32(&offset_ptr);
80
81 m_next_region = data.GetPointer(&offset_ptr);
82
83 // If the header size is 0, that means we've come in too early before this data is set up.
84 // Set ourselves as not valid, and continue.
85 if (header_size == 0)
86 {
87 m_valid = false;
88 return;
89 }
90
91 // Now read in all the descriptors:
92 // The descriptor looks like:
93 //
94 // uint32_t offset
95 // uint32_t flags
96 //
97 // Where offset is either 0 - in which case it is unused, or
98 // it is the offset of the vtable code from the beginning of the descriptor record.
99 // Below, we'll convert that into an absolute code address, since I don't want to have
100 // to compute it over and over.
101
102 // Ingest the whole descriptor array:
103 lldb::addr_t desc_ptr = m_header_addr + header_size;
104 size_t desc_array_size = num_descriptors * descriptor_size;
105 DataBufferSP data_sp(new DataBufferHeap (desc_array_size, '\0'));
106 uint8_t* dst = (uint8_t*)data_sp->GetBytes();
107
108 DataExtractor desc_extractor (dst, desc_array_size,
109 process->GetByteOrder(),
110 process->GetAddressByteSize());
111 bytes_read = process->ReadMemory(desc_ptr, dst, desc_array_size, error);
112 if (bytes_read != desc_array_size)
113 {
114 m_valid = false;
115 return;
116 }
117
118 // The actual code for the vtables will be laid out consecutively, so I also
119 // compute the start and end of the whole code block.
120
121 offset_ptr = 0;
122 m_code_start_addr = 0;
123 m_code_end_addr = 0;
124
125 for (int i = 0; i < num_descriptors; i++)
126 {
127 lldb::addr_t start_offset = offset_ptr;
128 uint32_t offset = desc_extractor.GetU32 (&offset_ptr);
129 uint32_t flags = desc_extractor.GetU32 (&offset_ptr);
130 lldb:addr_t code_addr = desc_ptr + start_offset + offset;
131 m_descriptors.push_back (VTableDescriptor(flags, code_addr));
132
133 if (m_code_start_addr == 0 || code_addr < m_code_start_addr)
134 m_code_start_addr = code_addr;
135 if (code_addr > m_code_end_addr)
136 m_code_end_addr = code_addr;
137
138 offset_ptr = start_offset + descriptor_size;
139 }
140 // Finally, a little bird told me that all the vtable code blocks are the same size.
141 // Let's compute the blocks and if they are all the same add the size to the code end address:
142 lldb::addr_t code_size = 0;
143 bool all_the_same = true;
144 for (int i = 0; i < num_descriptors - 1; i++)
145 {
146 lldb::addr_t this_size = m_descriptors[i + 1].code_start - m_descriptors[i].code_start;
147 if (code_size == 0)
148 code_size = this_size;
149 else
150 {
151 if (this_size != code_size)
152 all_the_same = false;
153 if (this_size > code_size)
154 code_size = this_size;
155 }
156 }
157 if (all_the_same)
158 m_code_end_addr += code_size;
159}
160
161bool
162AppleObjCTrampolineHandler::AppleObjCVTables::VTableRegion::AddressInRegion (lldb::addr_t addr, uint32_t &flags)
163{
164 if (!IsValid())
165 return false;
166
167 if (addr < m_code_start_addr || addr > m_code_end_addr)
168 return false;
169
170 std::vector<VTableDescriptor>::iterator pos, end = m_descriptors.end();
171 for (pos = m_descriptors.begin(); pos != end; pos++)
172 {
173 if (addr <= (*pos).code_start)
174 {
175 flags = (*pos).flags;
176 return true;
177 }
178 }
179 return false;
180}
181
182void
183AppleObjCTrampolineHandler::AppleObjCVTables::VTableRegion::Dump (Stream &s)
184{
185 s.Printf ("Header addr: 0x%llx Code start: 0x%llx Code End: 0x%llx Next: 0x%llx\n",
186 m_header_addr, m_code_start_addr, m_code_end_addr, m_next_region);
187 size_t num_elements = m_descriptors.size();
188 for (size_t i = 0; i < num_elements; i++)
189 {
190 s.Indent();
191 s.Printf ("Code start: 0x%llx Flags: %d\n", m_descriptors[i].code_start, m_descriptors[i].flags);
192 }
193}
194
195AppleObjCTrampolineHandler::AppleObjCVTables::AppleObjCVTables (ProcessSP &process_sp, ModuleSP &objc_module_sp) :
196 m_process_sp(process_sp),
197 m_trampoline_header(LLDB_INVALID_ADDRESS),
198 m_trampolines_changed_bp_id(LLDB_INVALID_BREAK_ID),
199 m_objc_module_sp(objc_module_sp)
200{
201
202}
203
204AppleObjCTrampolineHandler::AppleObjCVTables::~AppleObjCVTables()
205{
206 if (m_trampolines_changed_bp_id != LLDB_INVALID_BREAK_ID)
207 m_process_sp->GetTarget().RemoveBreakpointByID (m_trampolines_changed_bp_id);
208}
209
210bool
211AppleObjCTrampolineHandler::AppleObjCVTables::InitializeVTableSymbols ()
212{
213 if (m_trampoline_header != LLDB_INVALID_ADDRESS)
214 return true;
215 Target &target = m_process_sp->GetTarget();
216
217 ModuleList &modules = target.GetImages();
218 size_t num_modules = modules.GetSize();
219 if (!m_objc_module_sp)
220 {
221 for (size_t i = 0; i < num_modules; i++)
222 {
223 if (m_process_sp->GetObjCLanguageRuntime()->IsModuleObjCLibrary (modules.GetModuleAtIndex(i)))
224 {
225 m_objc_module_sp = modules.GetModuleAtIndex(i);
226 break;
227 }
228 }
229 }
230
231 if (m_objc_module_sp)
232 {
233 ConstString trampoline_name ("gdb_objc_trampolines");
234 const Symbol *trampoline_symbol = m_objc_module_sp->FindFirstSymbolWithNameAndType(trampoline_name,
235 eSymbolTypeData);
236 if (trampoline_symbol != NULL)
237 {
238 const Address &temp_address = trampoline_symbol->GetValue();
239 if (!temp_address.IsValid())
240 return false;
241
242 m_trampoline_header = temp_address.GetLoadAddress(&target);
243 if (m_trampoline_header == LLDB_INVALID_ADDRESS)
244 return false;
245
246 // Next look up the "changed" symbol and set a breakpoint on that...
247 ConstString changed_name ("gdb_objc_trampolines_changed");
248 const Symbol *changed_symbol = m_objc_module_sp->FindFirstSymbolWithNameAndType(changed_name,
249 eSymbolTypeCode);
250 if (changed_symbol != NULL)
251 {
252 const Address &temp_address = changed_symbol->GetValue();
253 if (!temp_address.IsValid())
254 return false;
255
256 lldb::addr_t changed_addr = temp_address.GetLoadAddress(&target);
257 if (changed_addr != LLDB_INVALID_ADDRESS)
258 {
259 BreakpointSP trampolines_changed_bp_sp = target.CreateBreakpoint (changed_addr,
260 true);
261 if (trampolines_changed_bp_sp != NULL)
262 {
263 m_trampolines_changed_bp_id = trampolines_changed_bp_sp->GetID();
264 trampolines_changed_bp_sp->SetCallback (RefreshTrampolines, this, true);
265 return true;
266 }
267 }
268 }
269 }
270 }
271
272 return false;
273}
274
275bool
276AppleObjCTrampolineHandler::AppleObjCVTables::RefreshTrampolines (void *baton,
277 StoppointCallbackContext *context,
278 lldb::user_id_t break_id,
279 lldb::user_id_t break_loc_id)
280{
281 AppleObjCVTables *vtable_handler = (AppleObjCVTables *) baton;
282 if (vtable_handler->InitializeVTableSymbols())
283 {
284 // The Update function is called with the address of an added region. So we grab that address, and
285 // feed it into ReadRegions. Of course, our friend the ABI will get the values for us.
286 Process *process = context->exe_ctx.process;
287 const ABI *abi = process->GetABI();
288
289 ClangASTContext *clang_ast_context = process->GetTarget().GetScratchClangASTContext();
290 ValueList argument_values;
291 Value input_value;
292 void *clang_void_ptr_type = clang_ast_context->GetVoidPtrType(false);
293 input_value.SetValueType (Value::eValueTypeScalar);
294 input_value.SetContext (Value::eContextTypeOpaqueClangQualType, clang_void_ptr_type);
295 argument_values.PushValue(input_value);
296
297 bool success = abi->GetArgumentValues (*(context->exe_ctx.thread), argument_values);
298 if (!success)
299 return false;
300
301 // Now get a pointer value from the zeroth argument.
302 Error error;
303 DataExtractor data;
304 error = argument_values.GetValueAtIndex(0)->GetValueAsData(&(context->exe_ctx), clang_ast_context->getASTContext(), data, 0);
305 uint32_t offset_ptr = 0;
306 lldb::addr_t region_addr = data.GetPointer(&offset_ptr);
307
308 if (region_addr != 0)
309 vtable_handler->ReadRegions(region_addr);
310 }
311 return false;
312}
313
314bool
315AppleObjCTrampolineHandler::AppleObjCVTables::ReadRegions ()
316{
317 // The no argument version reads the start region from the value of the gdb_regions_header, and
318 // gets started from there.
319
320 m_regions.clear();
321 if (!InitializeVTableSymbols())
322 return false;
323 char memory_buffer[8];
324 DataExtractor data(memory_buffer, sizeof(memory_buffer),
325 m_process_sp->GetByteOrder(),
326 m_process_sp->GetAddressByteSize());
327 Error error;
328 size_t bytes_read = m_process_sp->ReadMemory (m_trampoline_header, memory_buffer, m_process_sp->GetAddressByteSize(), error);
329 if (bytes_read != m_process_sp->GetAddressByteSize())
330 return false;
331
332 uint32_t offset_ptr = 0;
333 lldb::addr_t region_addr = data.GetPointer(&offset_ptr);
334 return ReadRegions (region_addr);
335}
336
337bool
338AppleObjCTrampolineHandler::AppleObjCVTables::ReadRegions (lldb::addr_t region_addr)
339{
340 if (!m_process_sp)
341 return false;
342
343 Log *log = lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_STEP);
344
345 // We aren't starting at the trampoline symbol.
346 InitializeVTableSymbols ();
347 lldb::addr_t next_region = region_addr;
348
349 // Read in the sizes of the headers.
350 while (next_region != 0)
351 {
352 m_regions.push_back (VTableRegion(this, next_region));
353 if (!m_regions.back().IsValid())
354 {
355 m_regions.clear();
356 return false;
357 }
358 if (log)
359 {
360 StreamString s;
361 m_regions.back().Dump(s);
362 log->Printf("Read vtable region: \n%s", s.GetData());
363 }
364
365 next_region = m_regions.back().GetNextRegionAddr();
366 }
367
368 return true;
369}
370
371bool
372AppleObjCTrampolineHandler::AppleObjCVTables::IsAddressInVTables (lldb::addr_t addr, uint32_t &flags)
373{
374 region_collection::iterator pos, end = m_regions.end();
375 for (pos = m_regions.begin(); pos != end; pos++)
376 {
377 if ((*pos).AddressInRegion (addr, flags))
378 return true;
379 }
380 return false;
381}
382
Jim Ingham5a369122010-09-28 01:25:32 +0000383const AppleObjCTrampolineHandler::DispatchFunction
384AppleObjCTrampolineHandler::g_dispatch_functions[] =
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000385{
386 // NAME STRET SUPER FIXUP TYPE
Jim Ingham5a369122010-09-28 01:25:32 +0000387 {"objc_msgSend", false, false, AppleObjCTrampolineHandler::DispatchFunction::eFixUpNone },
388 {"objc_msgSend_fixup", false, false, AppleObjCTrampolineHandler::DispatchFunction::eFixUpToFix },
389 {"objc_msgSend_fixedup", false, false, AppleObjCTrampolineHandler::DispatchFunction::eFixUpFixed },
390 {"objc_msgSend_stret", true, false, AppleObjCTrampolineHandler::DispatchFunction::eFixUpNone },
391 {"objc_msgSend_stret_fixup", true, false, AppleObjCTrampolineHandler::DispatchFunction::eFixUpToFix },
392 {"objc_msgSend_stret_fixedup", true, false, AppleObjCTrampolineHandler::DispatchFunction::eFixUpFixed },
393 {"objc_msgSend_fpret", false, false, AppleObjCTrampolineHandler::DispatchFunction::eFixUpNone },
394 {"objc_msgSend_fpret_fixup", false, false, AppleObjCTrampolineHandler::DispatchFunction::eFixUpToFix },
395 {"objc_msgSend_fpret_fixedup", false, false, AppleObjCTrampolineHandler::DispatchFunction::eFixUpFixed },
396 {"objc_msgSend_fp2ret", false, false, AppleObjCTrampolineHandler::DispatchFunction::eFixUpNone },
397 {"objc_msgSend_fp2ret_fixup", false, false, AppleObjCTrampolineHandler::DispatchFunction::eFixUpToFix },
398 {"objc_msgSend_fp2ret_fixedup", false, false, AppleObjCTrampolineHandler::DispatchFunction::eFixUpFixed },
399 {"objc_msgSendSuper", false, true, AppleObjCTrampolineHandler::DispatchFunction::eFixUpNone },
400 {"objc_msgSendSuper_stret", true, true, AppleObjCTrampolineHandler::DispatchFunction::eFixUpNone },
401 {"objc_msgSendSuper2", false, true, AppleObjCTrampolineHandler::DispatchFunction::eFixUpNone },
402 {"objc_msgSendSuper2_fixup", false, true, AppleObjCTrampolineHandler::DispatchFunction::eFixUpToFix },
403 {"objc_msgSendSuper2_fixedup", false, true, AppleObjCTrampolineHandler::DispatchFunction::eFixUpFixed },
404 {"objc_msgSendSuper2_stret", true, true, AppleObjCTrampolineHandler::DispatchFunction::eFixUpNone },
405 {"objc_msgSendSuper2_stret_fixup", true, true, AppleObjCTrampolineHandler::DispatchFunction::eFixUpToFix },
406 {"objc_msgSendSuper2_stret_fixedup", true, true, AppleObjCTrampolineHandler::DispatchFunction::eFixUpFixed },
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000407 {NULL}
408};
409
Jim Ingham58221732010-11-05 00:18:21 +0000410AppleObjCTrampolineHandler::AppleObjCTrampolineHandler (ProcessSP process_sp, ModuleSP objc_module_sp) :
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000411 m_process_sp (process_sp),
Jim Ingham58221732010-11-05 00:18:21 +0000412 m_objc_module_sp (objc_module_sp),
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000413 m_impl_fn_addr (LLDB_INVALID_ADDRESS),
414 m_impl_stret_fn_addr (LLDB_INVALID_ADDRESS)
415{
416 // Look up the known resolution functions:
417
418 ConstString get_impl_name("class_getMethodImplementation");
419 ConstString get_impl_stret_name("class_getMethodImplementation_stret");
420
Greg Claytonf5e56de2010-09-14 23:36:40 +0000421 Target *target = m_process_sp ? &m_process_sp->GetTarget() : NULL;
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000422 const Symbol *class_getMethodImplementation = m_objc_module_sp->FindFirstSymbolWithNameAndType (get_impl_name, eSymbolTypeCode);
423 const Symbol *class_getMethodImplementation_stret = m_objc_module_sp->FindFirstSymbolWithNameAndType (get_impl_stret_name, eSymbolTypeCode);
424
425 if (class_getMethodImplementation)
Greg Claytonf5e56de2010-09-14 23:36:40 +0000426 m_impl_fn_addr = class_getMethodImplementation->GetValue().GetLoadAddress(target);
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000427 if (class_getMethodImplementation_stret)
Greg Claytonf5e56de2010-09-14 23:36:40 +0000428 m_impl_stret_fn_addr = class_getMethodImplementation_stret->GetValue().GetLoadAddress(target);
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000429
430 // FIXME: Do some kind of logging here.
431 if (m_impl_fn_addr == LLDB_INVALID_ADDRESS || m_impl_stret_fn_addr == LLDB_INVALID_ADDRESS)
432 return;
433
434 // Look up the addresses for the objc dispatch functions and cache them. For now I'm inspecting the symbol
435 // names dynamically to figure out how to dispatch to them. If it becomes more complicated than this we can
436 // turn the g_dispatch_functions char * array into a template table, and populate the DispatchFunction map
437 // from there.
438
439 for (int i = 0; g_dispatch_functions[i].name != NULL; i++)
440 {
441 ConstString name_const_str(g_dispatch_functions[i].name);
442 const Symbol *msgSend_symbol = m_objc_module_sp->FindFirstSymbolWithNameAndType (name_const_str, eSymbolTypeCode);
443 if (msgSend_symbol)
444 {
445 // FixMe: Make g_dispatch_functions static table of DisptachFunctions, and have the map be address->index.
446 // Problem is we also need to lookup the dispatch function. For now we could have a side table of stret & non-stret
447 // dispatch functions. If that's as complex as it gets, we're fine.
448
Greg Claytonf5e56de2010-09-14 23:36:40 +0000449 lldb::addr_t sym_addr = msgSend_symbol->GetValue().GetLoadAddress(target);
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000450
451 m_msgSend_map.insert(std::pair<lldb::addr_t, int>(sym_addr, i));
452 }
453 }
Jim Ingham58221732010-11-05 00:18:21 +0000454
455 // Build our vtable dispatch handler here:
456 m_vtables_ap.reset(new AppleObjCVTables(process_sp, m_objc_module_sp));
457 if (m_vtables_ap.get())
458 m_vtables_ap->ReadRegions();
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000459}
460
461ThreadPlanSP
Jim Ingham5a369122010-09-28 01:25:32 +0000462AppleObjCTrampolineHandler::GetStepThroughDispatchPlan (Thread &thread, bool stop_others)
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000463{
464 ThreadPlanSP ret_plan_sp;
465 lldb::addr_t curr_pc = thread.GetRegisterContext()->GetPC();
466
Jim Ingham58221732010-11-05 00:18:21 +0000467 DispatchFunction this_dispatch;
468 bool found_it = false;
469
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000470 MsgsendMap::iterator pos;
471 pos = m_msgSend_map.find (curr_pc);
472 if (pos != m_msgSend_map.end())
473 {
Jim Ingham58221732010-11-05 00:18:21 +0000474 this_dispatch = g_dispatch_functions[(*pos).second];
475 found_it = true;
476 }
477
478 if (!found_it)
479 {
480 uint32_t flags;
481 if (m_vtables_ap.get())
482 {
483 found_it = m_vtables_ap->IsAddressInVTables (curr_pc, flags);
484 if (found_it)
485 {
486 this_dispatch.name = "vtable";
487 this_dispatch.stret_return
488 = (flags & AppleObjCVTables::eOBJC_TRAMPOLINE_STRET) == AppleObjCVTables::eOBJC_TRAMPOLINE_STRET;
489 this_dispatch.is_super = false;
490 this_dispatch.fixedup = DispatchFunction::eFixUpFixed;
491 }
492 }
493 }
494
495 if (found_it)
496 {
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000497 Log *log = lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_STEP);
498
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000499
500 lldb::StackFrameSP thread_cur_frame = thread.GetStackFrameAtIndex(0);
501
502 Process *process = thread.CalculateProcess();
503 const ABI *abi = process->GetABI();
504 if (abi == NULL)
505 return ret_plan_sp;
506
507 Target *target = thread.CalculateTarget();
508
509 // FIXME: Since neither the value nor the Clang QualType know their ASTContext,
510 // we have to make sure the type we put in our value list comes from the same ASTContext
511 // the ABI will use to get the argument values. THis is the bottom-most frame's module.
512
513 ClangASTContext *clang_ast_context = target->GetScratchClangASTContext();
514 ValueList argument_values;
515 Value input_value;
516 void *clang_void_ptr_type = clang_ast_context->GetVoidPtrType(false);
517 input_value.SetValueType (Value::eValueTypeScalar);
518 input_value.SetContext (Value::eContextTypeOpaqueClangQualType, clang_void_ptr_type);
519
520 int obj_index;
521 int sel_index;
522
523 // If this is a struct return dispatch, then the first argument is the
524 // return struct pointer, and the object is the second, and the selector is the third.
525 // Otherwise the object is the first and the selector the second.
Jim Ingham58221732010-11-05 00:18:21 +0000526 if (this_dispatch.stret_return)
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000527 {
528 obj_index = 1;
529 sel_index = 2;
530 argument_values.PushValue(input_value);
531 argument_values.PushValue(input_value);
532 argument_values.PushValue(input_value);
533 }
534 else
535 {
536 obj_index = 0;
537 sel_index = 1;
538 argument_values.PushValue(input_value);
539 argument_values.PushValue(input_value);
540 }
541
542
543 bool success = abi->GetArgumentValues (thread, argument_values);
544 if (!success)
545 return ret_plan_sp;
546
547 // Okay, the first value here is the object, we actually want the class of that object.
548 // For now we're just going with the ISA.
549 // FIXME: This should really be the return value of [object class] to properly handle KVO interposition.
550
551 Value isa_value(*(argument_values.GetValueAtIndex(obj_index)));
552
553 // This is a little cheesy, but since object->isa is the first field,
554 // making the object value a load address value and resolving it will get
555 // the pointer sized data pointed to by that value...
556 ExecutionContext exec_ctx;
Greg Clayton0603aa92010-10-04 01:05:56 +0000557 thread.CalculateExecutionContext (exec_ctx);
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000558
559 isa_value.SetValueType(Value::eValueTypeLoadAddress);
560 isa_value.ResolveValue(&exec_ctx, clang_ast_context->getASTContext());
561
Jim Ingham58221732010-11-05 00:18:21 +0000562 if (this_dispatch.fixedup == DispatchFunction::eFixUpFixed)
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000563 {
564 // For the FixedUp method the Selector is actually a pointer to a
565 // structure, the second field of which is the selector number.
566 Value *sel_value = argument_values.GetValueAtIndex(sel_index);
567 sel_value->GetScalar() += process->GetAddressByteSize();
568 sel_value->SetValueType(Value::eValueTypeLoadAddress);
569 sel_value->ResolveValue(&exec_ctx, clang_ast_context->getASTContext());
570 }
Jim Ingham58221732010-11-05 00:18:21 +0000571 else if (this_dispatch.fixedup == DispatchFunction::eFixUpToFix)
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000572 {
573 // FIXME: If the method dispatch is not "fixed up" then the selector is actually a
574 // pointer to the string name of the selector. We need to look that up...
575 // For now I'm going to punt on that and just return no plan.
576 if (log)
577 log->Printf ("Punting on stepping into un-fixed-up method dispatch.");
578 return ret_plan_sp;
579 }
580
581 // FIXME: If this is a dispatch to the super-class, we need to get the super-class from
582 // the class, and disaptch to that instead.
583 // But for now I just punt and return no plan.
Jim Ingham58221732010-11-05 00:18:21 +0000584 if (this_dispatch.is_super)
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000585 {
586 if (log)
587 log->Printf ("Punting on stepping into super method dispatch.");
588 return ret_plan_sp;
589 }
590
591 ValueList dispatch_values;
592 dispatch_values.PushValue (isa_value);
593 dispatch_values.PushValue(*(argument_values.GetValueAtIndex(sel_index)));
594
595 if (log)
596 {
597 log->Printf("Resolving method call for class - 0x%llx and selector - 0x%llx",
598 dispatch_values.GetValueAtIndex(0)->GetScalar().ULongLong(),
599 dispatch_values.GetValueAtIndex(1)->GetScalar().ULongLong());
600 }
Jim Ingham5a369122010-09-28 01:25:32 +0000601 ObjCLanguageRuntime *objc_runtime = m_process_sp->GetObjCLanguageRuntime ();
602 assert(objc_runtime != NULL);
603 lldb::addr_t impl_addr = objc_runtime->LookupInMethodCache (dispatch_values.GetValueAtIndex(0)->GetScalar().ULongLong(),
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000604 dispatch_values.GetValueAtIndex(1)->GetScalar().ULongLong());
605
606 if (impl_addr == LLDB_INVALID_ADDRESS)
607 {
608
Jim Ingham58221732010-11-05 00:18:21 +0000609 Address resolve_address(NULL, this_dispatch.stret_return ? m_impl_stret_fn_addr : m_impl_fn_addr);
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000610
611 StreamString errors;
612 {
613 // Scope for mutex locker:
Greg Claytonb1320972010-07-14 00:18:15 +0000614 Mutex::Locker locker(m_impl_function_mutex);
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000615 if (!m_impl_function.get())
616 {
617 m_impl_function.reset(new ClangFunction(process->GetTargetTriple().GetCString(),
618 clang_ast_context,
619 clang_void_ptr_type,
620 resolve_address,
621 dispatch_values));
622
623 unsigned num_errors = m_impl_function->CompileFunction(errors);
624 if (num_errors)
625 {
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000626 if (log)
627 log->Printf ("Error compiling function: \"%s\".", errors.GetData());
628 return ret_plan_sp;
629 }
630
631 errors.Clear();
632 if (!m_impl_function->WriteFunctionWrapper(exec_ctx, errors))
633 {
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000634 if (log)
635 log->Printf ("Error Inserting function: \"%s\".", errors.GetData());
636 return ret_plan_sp;
637 }
638 }
639
640 }
641
642 errors.Clear();
643
644 // Now write down the argument values for this call.
645 lldb::addr_t args_addr = LLDB_INVALID_ADDRESS;
646 if (!m_impl_function->WriteFunctionArguments (exec_ctx, args_addr, resolve_address, dispatch_values, errors))
647 return ret_plan_sp;
648
Jim Ingham5a369122010-09-28 01:25:32 +0000649 ret_plan_sp.reset (new AppleThreadPlanStepThroughObjCTrampoline (thread, this, args_addr,
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000650 argument_values.GetValueAtIndex(0)->GetScalar().ULongLong(),
651 dispatch_values.GetValueAtIndex(0)->GetScalar().ULongLong(),
652 dispatch_values.GetValueAtIndex(1)->GetScalar().ULongLong(),
653 stop_others));
Jim Ingham58221732010-11-05 00:18:21 +0000654 if (log)
655 {
656 StreamString s;
657 ret_plan_sp->GetDescription(&s, eDescriptionLevelFull);
658 log->Printf("Using ObjC step plan: %s.\n", s.GetData());
659 }
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000660 }
661 else
662 {
663 if (log)
664 log->Printf ("Found implementation address in cache: 0x%llx", impl_addr);
665
666 ret_plan_sp.reset (new ThreadPlanRunToAddress (thread, impl_addr, stop_others));
667 }
668 }
669
670 return ret_plan_sp;
671}
672
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000673ClangFunction *
Jim Ingham5a369122010-09-28 01:25:32 +0000674AppleObjCTrampolineHandler::GetLookupImplementationWrapperFunction ()
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000675{
676 return m_impl_function.get();
677}