1//===-- AppleObjCTrampolineHandler.cpp ----------------------------*- C++ -*-===//
2//
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
10#include "lldb/lldb-python.h"
11
12#include "AppleObjCTrampolineHandler.h"
13
14// C Includes
15// C++ Includes
16// Other libraries and framework includes
17// Project includes
18#include "AppleThreadPlanStepThroughObjCTrampoline.h"
19
20#include "lldb/Breakpoint/StoppointCallbackContext.h"
21#include "lldb/Core/ConstString.h"
22#include "lldb/Core/Debugger.h"
23#include "lldb/Core/Log.h"
24#include "lldb/Core/Module.h"
25#include "lldb/Core/Value.h"
26#include "lldb/Expression/ClangExpression.h"
27#include "lldb/Expression/ClangFunction.h"
28#include "lldb/Expression/ClangUtilityFunction.h"
29#include "lldb/Host/FileSpec.h"
30#include "lldb/Symbol/ClangASTContext.h"
31#include "lldb/Symbol/Symbol.h"
32#include "lldb/Target/ObjCLanguageRuntime.h"
33#include "lldb/Target/Process.h"
34#include "lldb/Target/RegisterContext.h"
35#include "lldb/Target/Target.h"
36#include "lldb/Target/Thread.h"
37#include "lldb/Target/ExecutionContext.h"
38#include "lldb/Target/ThreadPlanRunToAddress.h"
39
40#include "llvm/ADT/STLExtras.h"
41
42using namespace lldb;
43using namespace lldb_private;
44
45const char *AppleObjCTrampolineHandler::g_lookup_implementation_function_name = "__lldb_objc_find_implementation_for_selector";
46const char *AppleObjCTrampolineHandler::g_lookup_implementation_function_code = NULL;
47const char *AppleObjCTrampolineHandler::g_lookup_implementation_with_stret_function_code = "                               \n\
48extern \"C\"                                                                                                    \n\
49{                                                                                                               \n\
50    extern void *class_getMethodImplementation(void *objc_class, void *sel);                                    \n\
51    extern void *class_getMethodImplementation_stret(void *objc_class, void *sel);                              \n\
52    extern void * sel_getUid(char *name);                                                                       \n\
53    extern int printf(const char *format, ...);                                                                 \n\
54}                                                                                                               \n\
55extern \"C\" void * __lldb_objc_find_implementation_for_selector (void *object,                                 \n\
56                                                    void *sel,                                                  \n\
57                                                    int is_stret,                                               \n\
58                                                    int is_super,                                               \n\
59                                                    int is_super2,                                              \n\
60                                                    int is_fixup,                                               \n\
61                                                    int is_fixed,                                               \n\
62                                                    int debug)                                                  \n\
63{                                                                                                               \n\
64    struct __lldb_imp_return_struct                                                                             \n\
65    {                                                                                                           \n\
66        void *class_addr;                                                                                       \n\
67        void *sel_addr;                                                                                         \n\
68        void *impl_addr;                                                                                        \n\
69    };                                                                                                          \n\
70                                                                                                                \n\
71    struct __lldb_objc_class {                                                                                  \n\
72        void *isa;                                                                                              \n\
73        void *super_ptr;                                                                                        \n\
74    };                                                                                                          \n\
75    struct __lldb_objc_super {                                                                                  \n\
76        void *reciever;                                                                                         \n\
77        struct __lldb_objc_class *class_ptr;                                                                    \n\
78    };                                                                                                          \n\
79    struct __lldb_msg_ref {                                                                                     \n\
80        void *dont_know;                                                                                        \n\
81        void *sel;                                                                                              \n\
82    };                                                                                                          \n\
83                                                                                                                \n\
84    struct __lldb_imp_return_struct return_struct;                                                              \n\
85                                                                                                                \n\
86    if (debug)                                                                                                  \n\
87        printf (\"\\n*** Called with obj: 0x%p sel: 0x%p is_stret: %d is_super: %d, \"                          \n\
88                \"is_super2: %d, is_fixup: %d, is_fixed: %d\\n\",                                               \n\
89                 object, sel, is_stret, is_super, is_super2, is_fixup, is_fixed);                               \n\
90    if (is_super)                                                                                               \n\
91    {                                                                                                           \n\
92        if (is_super2)                                                                                          \n\
93        {                                                                                                       \n\
94            return_struct.class_addr = ((__lldb_objc_super *) object)->class_ptr->super_ptr;                    \n\
95        }                                                                                                       \n\
96        else                                                                                                    \n\
97        {                                                                                                       \n\
98            return_struct.class_addr = ((__lldb_objc_super *) object)->class_ptr;                               \n\
99        }                                                                                                       \n\
100    }                                                                                                           \n\
101    else                                                                                                        \n\
102    {                                                                                                           \n\
103        void *class_ptr = (void *) [(id) object class];                                                         \n\
104        if (class_ptr == object)                                                                                \n\
105        {                                                                                                       \n\
106            struct __lldb_objc_class *class_as_class_struct = (struct __lldb_objc_class *) class_ptr;           \n\
107            if (debug)                                                                                          \n\
108                printf (\"Found a class object, need to return the meta class 0x%p -> 0x%p\\n\",                \n\
109                        class_ptr, class_as_class_struct->isa);                                                 \n\
110            return_struct.class_addr = class_as_class_struct->isa;                                              \n\
111        }                                                                                                       \n\
112        else                                                                                                    \n\
113        {                                                                                                       \n\
114            if (debug)                                                                                          \n\
115                printf (\"[object class] returned: 0x%p.\\n\", class_ptr);                                      \n\
116            return_struct.class_addr = class_ptr;                                                               \n\
117        }                                                                                                       \n\
118    }                                                                                                           \n\
119                                                                                                                \n\
120    if (is_fixup)                                                                                               \n\
121    {                                                                                                           \n\
122        if (is_fixed)                                                                                           \n\
123        {                                                                                                       \n\
124            return_struct.sel_addr = ((__lldb_msg_ref *) sel)->sel;                                             \n\
125        }                                                                                                       \n\
126        else                                                                                                    \n\
127        {                                                                                                       \n\
128            char *sel_name = (char *) ((__lldb_msg_ref *) sel)->sel;                                            \n\
129            return_struct.sel_addr = sel_getUid (sel_name);                                                     \n\
130            if (debug)                                                                                          \n\
131                printf (\"\\n*** Got fixed up selector: 0x%p for name %s.\\n\",                                 \n\
132                        return_struct.sel_addr, sel_name);                                                      \n\
133        }                                                                                                       \n\
134    }                                                                                                           \n\
135    else                                                                                                        \n\
136    {                                                                                                           \n\
137        return_struct.sel_addr = sel;                                                                           \n\
138    }                                                                                                           \n\
139                                                                                                                \n\
140    if (is_stret)                                                                                               \n\
141    {                                                                                                           \n\
142        return_struct.impl_addr = class_getMethodImplementation_stret (return_struct.class_addr,                \n\
143                                                                       return_struct.sel_addr);                 \n\
144    }                                                                                                           \n\
145    else                                                                                                        \n\
146    {                                                                                                           \n\
147        return_struct.impl_addr = class_getMethodImplementation (return_struct.class_addr,                      \n\
148                                                                       return_struct.sel_addr);                 \n\
149    }                                                                                                           \n\
150    if (debug)                                                                                                  \n\
151        printf (\"\\n*** Returning implementation: 0x%p.\\n\", return_struct.impl_addr);                        \n\
152                                                                                                                \n\
153    return return_struct.impl_addr;                                                                             \n\
154}                                                                                                               \n\
155";
156const char *AppleObjCTrampolineHandler::g_lookup_implementation_no_stret_function_code = "                      \n\
157extern \"C\"                                                                                                    \n\
158{                                                                                                               \n\
159    extern void *class_getMethodImplementation(void *objc_class, void *sel);                                    \n\
160    extern void * sel_getUid(char *name);                                                                       \n\
161    extern int printf(const char *format, ...);                                                                 \n\
162}                                                                                                               \n\
163extern \"C\" void * __lldb_objc_find_implementation_for_selector (void *object,                                 \n\
164                                                    void *sel,                                                  \n\
165                                                    int is_stret,                                               \n\
166                                                    int is_super,                                               \n\
167                                                    int is_super2,                                              \n\
168                                                    int is_fixup,                                               \n\
169                                                    int is_fixed,                                               \n\
170                                                    int debug)                                                  \n\
171{                                                                                                               \n\
172    struct __lldb_imp_return_struct                                                                             \n\
173    {                                                                                                           \n\
174        void *class_addr;                                                                                       \n\
175        void *sel_addr;                                                                                         \n\
176        void *impl_addr;                                                                                        \n\
177    };                                                                                                          \n\
178                                                                                                                \n\
179    struct __lldb_objc_class {                                                                                  \n\
180        void *isa;                                                                                              \n\
181        void *super_ptr;                                                                                        \n\
182    };                                                                                                          \n\
183    struct __lldb_objc_super {                                                                                  \n\
184        void *reciever;                                                                                         \n\
185        struct __lldb_objc_class *class_ptr;                                                                    \n\
186    };                                                                                                          \n\
187    struct __lldb_msg_ref {                                                                                     \n\
188        void *dont_know;                                                                                        \n\
189        void *sel;                                                                                              \n\
190    };                                                                                                          \n\
191                                                                                                                \n\
192    struct __lldb_imp_return_struct return_struct;                                                              \n\
193                                                                                                                \n\
194    if (debug)                                                                                                  \n\
195        printf (\"\\n*** Called with obj: 0x%p sel: 0x%p is_stret: %d is_super: %d, \"                          \n\
196                \"is_super2: %d, is_fixup: %d, is_fixed: %d\\n\",                                               \n\
197                 object, sel, is_stret, is_super, is_super2, is_fixup, is_fixed);                               \n\
198    if (is_super)                                                                                               \n\
199    {                                                                                                           \n\
200        if (is_super2)                                                                                          \n\
201        {                                                                                                       \n\
202            return_struct.class_addr = ((__lldb_objc_super *) object)->class_ptr->super_ptr;                    \n\
203        }                                                                                                       \n\
204        else                                                                                                    \n\
205        {                                                                                                       \n\
206            return_struct.class_addr = ((__lldb_objc_super *) object)->class_ptr;                               \n\
207        }                                                                                                       \n\
208    }                                                                                                           \n\
209    else                                                                                                        \n\
210    {                                                                                                           \n\
211        void *class_ptr = (void *) [(id) object class];                                                         \n\
212        if (class_ptr == object)                                                                                \n\
213        {                                                                                                       \n\
214            struct __lldb_objc_class *class_as_class_struct = (struct __lldb_objc_class *) class_ptr;           \n\
215            if (debug)                                                                                          \n\
216                printf (\"Found a class object, need to return the meta class 0x%p -> 0x%p\\n\",                \n\
217                        class_ptr, class_as_class_struct->isa);                                                 \n\
218            return_struct.class_addr = class_as_class_struct->isa;                                              \n\
219        }                                                                                                       \n\
220        else                                                                                                    \n\
221        {                                                                                                       \n\
222            if (debug)                                                                                          \n\
223                printf (\"[object class] returned: 0x%p.\\n\", class_ptr);                                      \n\
224            return_struct.class_addr = class_ptr;                                                               \n\
225        }                                                                                                       \n\
226    }                                                                                                           \n\
227                                                                                                                \n\
228    if (is_fixup)                                                                                               \n\
229    {                                                                                                           \n\
230        if (is_fixed)                                                                                           \n\
231        {                                                                                                       \n\
232            return_struct.sel_addr = ((__lldb_msg_ref *) sel)->sel;                                             \n\
233        }                                                                                                       \n\
234        else                                                                                                    \n\
235        {                                                                                                       \n\
236            char *sel_name = (char *) ((__lldb_msg_ref *) sel)->sel;                                            \n\
237            return_struct.sel_addr = sel_getUid (sel_name);                                                     \n\
238            if (debug)                                                                                          \n\
239                printf (\"\\n*** Got fixed up selector: 0x%p for name %s.\\n\",                                 \n\
240                        return_struct.sel_addr, sel_name);                                                      \n\
241        }                                                                                                       \n\
242    }                                                                                                           \n\
243    else                                                                                                        \n\
244    {                                                                                                           \n\
245        return_struct.sel_addr = sel;                                                                           \n\
246    }                                                                                                           \n\
247                                                                                                                \n\
248    return_struct.impl_addr = class_getMethodImplementation (return_struct.class_addr,                          \n\
249                                                             return_struct.sel_addr);                           \n\
250    if (debug)                                                                                                  \n\
251        printf (\"\\n*** Returning implementation: 0x%p.\\n\", return_struct.impl_addr);                        \n\
252                                                                                                                \n\
253    return return_struct.impl_addr;                                                                             \n\
254}                                                                                                               \n\
255";
256
257AppleObjCTrampolineHandler::AppleObjCVTables::VTableRegion::VTableRegion(AppleObjCVTables *owner, lldb::addr_t header_addr) :
258    m_valid (true),
259    m_owner(owner),
260    m_header_addr (header_addr),
261    m_code_start_addr(0),
262    m_code_end_addr (0),
263    m_next_region (0)
264{
265    SetUpRegion ();
266}
267
268AppleObjCTrampolineHandler::~AppleObjCTrampolineHandler()
269{
270}
271
272void
273AppleObjCTrampolineHandler::AppleObjCVTables::VTableRegion::SetUpRegion()
274{
275    // The header looks like:
276    //
277    //   uint16_t headerSize
278    //   uint16_t descSize
279    //   uint32_t descCount
280    //   void * next
281    //
282    // First read in the header:
283
284    char memory_buffer[16];
285    Process *process = m_owner->GetProcess();
286    DataExtractor data(memory_buffer, sizeof(memory_buffer),
287                       process->GetByteOrder(),
288                       process->GetAddressByteSize());
289    size_t actual_size = 8 + process->GetAddressByteSize();
290    Error error;
291    size_t bytes_read = process->ReadMemory (m_header_addr, memory_buffer, actual_size, error);
292    if (bytes_read != actual_size)
293    {
294        m_valid = false;
295        return;
296    }
297
298    lldb::offset_t offset = 0;
299    const uint16_t header_size = data.GetU16(&offset);
300    const uint16_t descriptor_size = data.GetU16(&offset);
301    const size_t num_descriptors = data.GetU32(&offset);
302
303    m_next_region = data.GetPointer(&offset);
304
305    // If the header size is 0, that means we've come in too early before this data is set up.
306    // Set ourselves as not valid, and continue.
307    if (header_size == 0 || num_descriptors == 0)
308    {
309        m_valid = false;
310        return;
311    }
312
313    // Now read in all the descriptors:
314    // The descriptor looks like:
315    //
316    // uint32_t offset
317    // uint32_t flags
318    //
319    // Where offset is either 0 - in which case it is unused, or
320    // it is the offset of the vtable code from the beginning of the descriptor record.
321    // Below, we'll convert that into an absolute code address, since I don't want to have
322    // to compute it over and over.
323
324    // Ingest the whole descriptor array:
325    const lldb::addr_t desc_ptr = m_header_addr + header_size;
326    const size_t desc_array_size = num_descriptors * descriptor_size;
327    DataBufferSP data_sp(new DataBufferHeap (desc_array_size, '\0'));
328    uint8_t* dst = (uint8_t*)data_sp->GetBytes();
329
330    DataExtractor desc_extractor (dst, desc_array_size,
331                                  process->GetByteOrder(),
332                                  process->GetAddressByteSize());
333    bytes_read = process->ReadMemory(desc_ptr, dst, desc_array_size, error);
334    if (bytes_read != desc_array_size)
335    {
336        m_valid = false;
337        return;
338    }
339
340    // The actual code for the vtables will be laid out consecutively, so I also
341    // compute the start and end of the whole code block.
342
343    offset = 0;
344    m_code_start_addr = 0;
345    m_code_end_addr = 0;
346
347    for (size_t i = 0; i < num_descriptors; i++)
348    {
349        lldb::addr_t start_offset = offset;
350        uint32_t voffset = desc_extractor.GetU32 (&offset);
351        uint32_t flags  = desc_extractor.GetU32 (&offset);
352        lldb::addr_t code_addr = desc_ptr + start_offset + voffset;
353        m_descriptors.push_back (VTableDescriptor(flags, code_addr));
354
355        if (m_code_start_addr == 0 || code_addr < m_code_start_addr)
356            m_code_start_addr = code_addr;
357        if (code_addr > m_code_end_addr)
358            m_code_end_addr = code_addr;
359
360        offset = start_offset + descriptor_size;
361    }
362    // Finally, a little bird told me that all the vtable code blocks are the same size.
363    // Let's compute the blocks and if they are all the same add the size to the code end address:
364    lldb::addr_t code_size = 0;
365    bool all_the_same = true;
366    for (size_t i = 0; i < num_descriptors - 1; i++)
367    {
368        lldb::addr_t this_size = m_descriptors[i + 1].code_start - m_descriptors[i].code_start;
369        if (code_size == 0)
370            code_size = this_size;
371        else
372        {
373            if (this_size != code_size)
374                all_the_same = false;
375            if (this_size > code_size)
376                code_size = this_size;
377        }
378    }
379    if (all_the_same)
380        m_code_end_addr += code_size;
381}
382
383bool
384AppleObjCTrampolineHandler::AppleObjCVTables::VTableRegion::AddressInRegion (lldb::addr_t addr, uint32_t &flags)
385{
386    if (!IsValid())
387        return false;
388
389    if (addr < m_code_start_addr || addr > m_code_end_addr)
390        return false;
391
392    std::vector<VTableDescriptor>::iterator pos, end = m_descriptors.end();
393    for (pos = m_descriptors.begin(); pos != end; pos++)
394    {
395        if (addr <= (*pos).code_start)
396        {
397            flags = (*pos).flags;
398            return true;
399        }
400    }
401    return false;
402}
403
404void
405AppleObjCTrampolineHandler::AppleObjCVTables::VTableRegion::Dump (Stream &s)
406{
407    s.Printf ("Header addr: 0x%" PRIx64 " Code start: 0x%" PRIx64 " Code End: 0x%" PRIx64 " Next: 0x%" PRIx64 "\n",
408              m_header_addr, m_code_start_addr, m_code_end_addr, m_next_region);
409    size_t num_elements = m_descriptors.size();
410    for (size_t i = 0; i < num_elements; i++)
411    {
412        s.Indent();
413        s.Printf ("Code start: 0x%" PRIx64 " Flags: %d\n", m_descriptors[i].code_start, m_descriptors[i].flags);
414    }
415}
416
417AppleObjCTrampolineHandler::AppleObjCVTables::AppleObjCVTables (const ProcessSP &process_sp,
418                                                                const ModuleSP &objc_module_sp) :
419    m_process_sp (process_sp),
420    m_trampoline_header (LLDB_INVALID_ADDRESS),
421    m_trampolines_changed_bp_id (LLDB_INVALID_BREAK_ID),
422    m_objc_module_sp (objc_module_sp)
423{
424
425}
426
427AppleObjCTrampolineHandler::AppleObjCVTables::~AppleObjCVTables()
428{
429    if (m_trampolines_changed_bp_id != LLDB_INVALID_BREAK_ID)
430        m_process_sp->GetTarget().RemoveBreakpointByID (m_trampolines_changed_bp_id);
431}
432
433bool
434AppleObjCTrampolineHandler::AppleObjCVTables::InitializeVTableSymbols ()
435{
436    if (m_trampoline_header != LLDB_INVALID_ADDRESS)
437        return true;
438    Target &target = m_process_sp->GetTarget();
439
440    const ModuleList &target_modules = target.GetImages();
441    Mutex::Locker modules_locker(target_modules.GetMutex());
442    size_t num_modules = target_modules.GetSize();
443    if (!m_objc_module_sp)
444    {
445        for (size_t i = 0; i < num_modules; i++)
446        {
447            if (m_process_sp->GetObjCLanguageRuntime()->IsModuleObjCLibrary (target_modules.GetModuleAtIndexUnlocked(i)))
448            {
449                m_objc_module_sp = target_modules.GetModuleAtIndexUnlocked(i);
450                break;
451            }
452        }
453    }
454
455    if (m_objc_module_sp)
456    {
457        ConstString trampoline_name ("gdb_objc_trampolines");
458        const Symbol *trampoline_symbol = m_objc_module_sp->FindFirstSymbolWithNameAndType (trampoline_name,
459                                                                                            eSymbolTypeData);
460        if (trampoline_symbol != NULL)
461        {
462            if (!trampoline_symbol->GetAddress().IsValid())
463                return false;
464
465            m_trampoline_header = trampoline_symbol->GetAddress().GetLoadAddress(&target);
466            if (m_trampoline_header == LLDB_INVALID_ADDRESS)
467                return false;
468
469            // Next look up the "changed" symbol and set a breakpoint on that...
470            ConstString changed_name ("gdb_objc_trampolines_changed");
471            const Symbol *changed_symbol = m_objc_module_sp->FindFirstSymbolWithNameAndType (changed_name,
472                                                                                             eSymbolTypeCode);
473            if (changed_symbol != NULL)
474            {
475                if (!changed_symbol->GetAddress().IsValid())
476                    return false;
477
478                lldb::addr_t changed_addr = changed_symbol->GetAddress().GetOpcodeLoadAddress (&target);
479                if (changed_addr != LLDB_INVALID_ADDRESS)
480                {
481                    BreakpointSP trampolines_changed_bp_sp = target.CreateBreakpoint (changed_addr, true);
482                    if (trampolines_changed_bp_sp)
483                    {
484                        m_trampolines_changed_bp_id = trampolines_changed_bp_sp->GetID();
485                        trampolines_changed_bp_sp->SetCallback (RefreshTrampolines, this, true);
486                        trampolines_changed_bp_sp->SetBreakpointKind ("objc-trampolines-changed");
487                        return true;
488                    }
489                }
490            }
491        }
492    }
493
494    return false;
495}
496
497bool
498AppleObjCTrampolineHandler::AppleObjCVTables::RefreshTrampolines (void *baton,
499                                                                  StoppointCallbackContext *context,
500                                                                  lldb::user_id_t break_id,
501                                                                  lldb::user_id_t break_loc_id)
502{
503    AppleObjCVTables *vtable_handler = (AppleObjCVTables *) baton;
504    if (vtable_handler->InitializeVTableSymbols())
505    {
506        // The Update function is called with the address of an added region.  So we grab that address, and
507        // feed it into ReadRegions.  Of course, our friend the ABI will get the values for us.
508        ExecutionContext exe_ctx (context->exe_ctx_ref);
509        Process *process = exe_ctx.GetProcessPtr();
510        const ABI *abi = process->GetABI().get();
511
512        ClangASTContext *clang_ast_context = process->GetTarget().GetScratchClangASTContext();
513        ValueList argument_values;
514        Value input_value;
515        ClangASTType clang_void_ptr_type = clang_ast_context->GetBasicType(eBasicTypeVoid).GetPointerType();
516
517        input_value.SetValueType (Value::eValueTypeScalar);
518        //input_value.SetContext (Value::eContextTypeClangType, clang_void_ptr_type);
519        input_value.SetClangType (clang_void_ptr_type);
520        argument_values.PushValue(input_value);
521
522        bool success = abi->GetArgumentValues (exe_ctx.GetThreadRef(), argument_values);
523        if (!success)
524            return false;
525
526        // Now get a pointer value from the zeroth argument.
527        Error error;
528        DataExtractor data;
529        error = argument_values.GetValueAtIndex(0)->GetValueAsData (&exe_ctx,
530                                                                    data,
531                                                                    0,
532                                                                    NULL);
533        lldb::offset_t offset = 0;
534        lldb::addr_t region_addr = data.GetPointer(&offset);
535
536        if (region_addr != 0)
537            vtable_handler->ReadRegions(region_addr);
538    }
539    return false;
540}
541
542bool
543AppleObjCTrampolineHandler::AppleObjCVTables::ReadRegions ()
544{
545    // The no argument version reads the start region from the value of the gdb_regions_header, and
546    // gets started from there.
547
548    m_regions.clear();
549    if (!InitializeVTableSymbols())
550        return false;
551    Error error;
552    lldb::addr_t region_addr = m_process_sp->ReadPointerFromMemory (m_trampoline_header, error);
553    if (error.Success())
554        return ReadRegions (region_addr);
555    return false;
556}
557
558bool
559AppleObjCTrampolineHandler::AppleObjCVTables::ReadRegions (lldb::addr_t region_addr)
560{
561    if (!m_process_sp)
562        return false;
563
564    Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_STEP));
565
566    // We aren't starting at the trampoline symbol.
567    InitializeVTableSymbols ();
568    lldb::addr_t next_region = region_addr;
569
570    // Read in the sizes of the headers.
571    while (next_region != 0)
572    {
573        m_regions.push_back (VTableRegion(this, next_region));
574        if (!m_regions.back().IsValid())
575        {
576            m_regions.clear();
577            return false;
578        }
579        if (log)
580        {
581            StreamString s;
582            m_regions.back().Dump(s);
583            log->Printf("Read vtable region: \n%s", s.GetData());
584        }
585
586        next_region = m_regions.back().GetNextRegionAddr();
587    }
588
589    return true;
590}
591
592bool
593AppleObjCTrampolineHandler::AppleObjCVTables::IsAddressInVTables (lldb::addr_t addr, uint32_t &flags)
594{
595    region_collection::iterator pos, end = m_regions.end();
596    for (pos = m_regions.begin(); pos != end; pos++)
597    {
598        if ((*pos).AddressInRegion (addr, flags))
599            return true;
600    }
601    return false;
602}
603
604const AppleObjCTrampolineHandler::DispatchFunction
605AppleObjCTrampolineHandler::g_dispatch_functions[] =
606{
607    // NAME                              STRET  SUPER  SUPER2  FIXUP TYPE
608    {"objc_msgSend",                     false, false, false, DispatchFunction::eFixUpNone    },
609    {"objc_msgSend_fixup",               false, false, false, DispatchFunction::eFixUpToFix   },
610    {"objc_msgSend_fixedup",             false, false, false, DispatchFunction::eFixUpFixed   },
611    {"objc_msgSend_stret",               true,  false, false, DispatchFunction::eFixUpNone    },
612    {"objc_msgSend_stret_fixup",         true,  false, false, DispatchFunction::eFixUpToFix   },
613    {"objc_msgSend_stret_fixedup",       true,  false, false, DispatchFunction::eFixUpFixed   },
614    {"objc_msgSend_fpret",               false, false, false, DispatchFunction::eFixUpNone    },
615    {"objc_msgSend_fpret_fixup",         false, false, false, DispatchFunction::eFixUpToFix   },
616    {"objc_msgSend_fpret_fixedup",       false, false, false, DispatchFunction::eFixUpFixed   },
617    {"objc_msgSend_fp2ret",              false, false,  true, DispatchFunction::eFixUpNone    },
618    {"objc_msgSend_fp2ret_fixup",        false, false,  true, DispatchFunction::eFixUpToFix   },
619    {"objc_msgSend_fp2ret_fixedup",      false, false,  true, DispatchFunction::eFixUpFixed   },
620    {"objc_msgSendSuper",                false, true,  false, DispatchFunction::eFixUpNone    },
621    {"objc_msgSendSuper_stret",          true,  true,  false, DispatchFunction::eFixUpNone    },
622    {"objc_msgSendSuper2",               false, true,   true, DispatchFunction::eFixUpNone    },
623    {"objc_msgSendSuper2_fixup",         false, true,   true, DispatchFunction::eFixUpToFix   },
624    {"objc_msgSendSuper2_fixedup",       false, true,   true, DispatchFunction::eFixUpFixed   },
625    {"objc_msgSendSuper2_stret",         true,  true,   true, DispatchFunction::eFixUpNone    },
626    {"objc_msgSendSuper2_stret_fixup",   true,  true,   true, DispatchFunction::eFixUpToFix   },
627    {"objc_msgSendSuper2_stret_fixedup", true,  true,   true, DispatchFunction::eFixUpFixed   },
628};
629
630AppleObjCTrampolineHandler::AppleObjCTrampolineHandler (const ProcessSP &process_sp,
631                                                        const ModuleSP &objc_module_sp) :
632    m_process_sp (process_sp),
633    m_objc_module_sp (objc_module_sp),
634    m_impl_fn_addr (LLDB_INVALID_ADDRESS),
635    m_impl_stret_fn_addr (LLDB_INVALID_ADDRESS),
636    m_msg_forward_addr (LLDB_INVALID_ADDRESS)
637{
638    // Look up the known resolution functions:
639
640    ConstString get_impl_name("class_getMethodImplementation");
641    ConstString get_impl_stret_name("class_getMethodImplementation_stret");
642    ConstString msg_forward_name("_objc_msgForward");
643    ConstString msg_forward_stret_name("_objc_msgForward_stret");
644
645    Target *target = m_process_sp ? &m_process_sp->GetTarget() : NULL;
646    const Symbol *class_getMethodImplementation = m_objc_module_sp->FindFirstSymbolWithNameAndType (get_impl_name, eSymbolTypeCode);
647    const Symbol *class_getMethodImplementation_stret = m_objc_module_sp->FindFirstSymbolWithNameAndType (get_impl_stret_name, eSymbolTypeCode);
648    const Symbol *msg_forward = m_objc_module_sp->FindFirstSymbolWithNameAndType (msg_forward_name, eSymbolTypeCode);
649    const Symbol *msg_forward_stret = m_objc_module_sp->FindFirstSymbolWithNameAndType (msg_forward_stret_name, eSymbolTypeCode);
650
651    if (class_getMethodImplementation)
652        m_impl_fn_addr = class_getMethodImplementation->GetAddress().GetOpcodeLoadAddress (target);
653    if  (class_getMethodImplementation_stret)
654        m_impl_stret_fn_addr = class_getMethodImplementation_stret->GetAddress().GetOpcodeLoadAddress (target);
655    if (msg_forward)
656        m_msg_forward_addr = msg_forward->GetAddress().GetOpcodeLoadAddress(target);
657    if  (msg_forward_stret)
658        m_msg_forward_stret_addr = msg_forward_stret->GetAddress().GetOpcodeLoadAddress(target);
659
660    // FIXME: Do some kind of logging here.
661    if (m_impl_fn_addr == LLDB_INVALID_ADDRESS)
662    {
663        // If we can't even find the ordinary get method implementation function, then we aren't going to be able to
664        // step through any method dispatches.  Warn to that effect and get out of here.
665        if (process_sp->CanJIT())
666        {
667            process_sp->GetTarget().GetDebugger().GetErrorStream().Printf("Could not find implementation lookup function \"%s\""
668                                                                          " step in through ObjC method dispatch will not work.\n",
669                                                                          get_impl_name.AsCString());
670        }
671        return;
672    }
673    else if (m_impl_stret_fn_addr == LLDB_INVALID_ADDRESS)
674    {
675        // It there is no stret return lookup function, assume that it is the same as the straight lookup:
676        m_impl_stret_fn_addr = m_impl_fn_addr;
677        // Also we will use the version of the lookup code that doesn't rely on the stret version of the function.
678        g_lookup_implementation_function_code = g_lookup_implementation_no_stret_function_code;
679    }
680    else
681    {
682        g_lookup_implementation_function_code = g_lookup_implementation_with_stret_function_code;
683    }
684
685    // Look up the addresses for the objc dispatch functions and cache them.  For now I'm inspecting the symbol
686    // names dynamically to figure out how to dispatch to them.  If it becomes more complicated than this we can
687    // turn the g_dispatch_functions char * array into a template table, and populate the DispatchFunction map
688    // from there.
689
690    for (size_t i = 0; i != llvm::array_lengthof(g_dispatch_functions); i++)
691    {
692        ConstString name_const_str(g_dispatch_functions[i].name);
693        const Symbol *msgSend_symbol = m_objc_module_sp->FindFirstSymbolWithNameAndType (name_const_str, eSymbolTypeCode);
694        if (msgSend_symbol)
695        {
696            // FixMe: Make g_dispatch_functions static table of DispatchFunctions, and have the map be address->index.
697            // Problem is we also need to lookup the dispatch function.  For now we could have a side table of stret & non-stret
698            // dispatch functions.  If that's as complex as it gets, we're fine.
699
700            lldb::addr_t sym_addr = msgSend_symbol->GetAddress().GetOpcodeLoadAddress(target);
701
702            m_msgSend_map.insert(std::pair<lldb::addr_t, int>(sym_addr, i));
703        }
704    }
705
706    // Build our vtable dispatch handler here:
707    m_vtables_ap.reset(new AppleObjCVTables(process_sp, m_objc_module_sp));
708    if (m_vtables_ap.get())
709        m_vtables_ap->ReadRegions();
710}
711
712lldb::addr_t
713AppleObjCTrampolineHandler::SetupDispatchFunction (Thread &thread, ValueList &dispatch_values)
714{
715    ExecutionContext exe_ctx (thread.shared_from_this());
716    Address impl_code_address;
717    StreamString errors;
718    Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_STEP));
719    lldb::addr_t args_addr = LLDB_INVALID_ADDRESS;
720
721    // Scope for mutex locker:
722    {
723        Mutex::Locker locker(m_impl_function_mutex);
724
725        // First stage is to make the ClangUtility to hold our injected function:
726
727    #define USE_BUILTIN_FUNCTION 0  // Define this to 1 and we will use the get_implementation function found in the target.
728                        // This is useful for debugging additions to the get_impl function 'cause you don't have
729                        // to bother with string-ifying the code into g_lookup_implementation_function_code.
730
731        if (USE_BUILTIN_FUNCTION)
732        {
733            ConstString our_utility_function_name("__lldb_objc_find_implementation_for_selector");
734            SymbolContextList sc_list;
735
736            exe_ctx.GetTargetRef().GetImages().FindSymbolsWithNameAndType (our_utility_function_name, eSymbolTypeCode, sc_list);
737            if (sc_list.GetSize() == 1)
738            {
739                SymbolContext sc;
740                sc_list.GetContextAtIndex(0, sc);
741                if (sc.symbol != NULL)
742                    impl_code_address = sc.symbol->GetAddress();
743
744                //lldb::addr_t addr = impl_code_address.GetOpcodeLoadAddress (exe_ctx.GetTargetPtr());
745                //printf ("Getting address for our_utility_function: 0x%" PRIx64 ".\n", addr);
746            }
747            else
748            {
749                //printf ("Could not find implementation function address.\n");
750                return args_addr;
751            }
752        }
753        else if (!m_impl_code.get())
754        {
755            if (g_lookup_implementation_function_code != NULL)
756            {
757                m_impl_code.reset (new ClangUtilityFunction (g_lookup_implementation_function_code,
758                                                             g_lookup_implementation_function_name));
759                if (!m_impl_code->Install(errors, exe_ctx))
760                {
761                    if (log)
762                        log->Printf ("Failed to install implementation lookup: %s.", errors.GetData());
763                    m_impl_code.reset();
764                    return args_addr;
765                }
766            }
767            else
768            {
769                if (log)
770                    log->Printf("No method lookup implementation code.");
771                errors.Printf ("No method lookup implementation code found.");
772                return LLDB_INVALID_ADDRESS;
773            }
774
775            impl_code_address.Clear();
776            impl_code_address.SetOffset(m_impl_code->StartAddress());
777        }
778        else
779        {
780            impl_code_address.Clear();
781            impl_code_address.SetOffset(m_impl_code->StartAddress());
782        }
783
784        // Next make the runner function for our implementation utility function.
785        if (!m_impl_function.get())
786        {
787            ClangASTContext *clang_ast_context = thread.GetProcess()->GetTarget().GetScratchClangASTContext();
788            ClangASTType clang_void_ptr_type = clang_ast_context->GetBasicType(eBasicTypeVoid).GetPointerType();
789            m_impl_function.reset(new ClangFunction (thread,
790                                                     clang_void_ptr_type,
791                                                     impl_code_address,
792                                                     dispatch_values));
793
794            errors.Clear();
795            unsigned num_errors = m_impl_function->CompileFunction(errors);
796            if (num_errors)
797            {
798                if (log)
799                    log->Printf ("Error compiling function: \"%s\".", errors.GetData());
800                return args_addr;
801            }
802
803            errors.Clear();
804            if (!m_impl_function->WriteFunctionWrapper(exe_ctx, errors))
805            {
806                if (log)
807                    log->Printf ("Error Inserting function: \"%s\".", errors.GetData());
808                return args_addr;
809            }
810        }
811    }
812
813    errors.Clear();
814
815    // Now write down the argument values for this particular call.  This looks like it might be a race condition
816    // if other threads were calling into here, but actually it isn't because we allocate a new args structure for
817    // this call by passing args_addr = LLDB_INVALID_ADDRESS...
818
819    if (!m_impl_function->WriteFunctionArguments (exe_ctx, args_addr, impl_code_address, dispatch_values, errors))
820    {
821        if (log)
822            log->Printf ("Error writing function arguments: \"%s\".", errors.GetData());
823        return args_addr;
824    }
825
826    return args_addr;
827}
828
829ThreadPlanSP
830AppleObjCTrampolineHandler::GetStepThroughDispatchPlan (Thread &thread, bool stop_others)
831{
832    ThreadPlanSP ret_plan_sp;
833    lldb::addr_t curr_pc = thread.GetRegisterContext()->GetPC();
834
835    DispatchFunction this_dispatch;
836    bool found_it = false;
837
838    // First step is to look and see if we are in one of the known ObjC dispatch functions.  We've already compiled
839    // a table of same, so consult it.
840
841    MsgsendMap::iterator pos;
842    pos = m_msgSend_map.find (curr_pc);
843    if (pos != m_msgSend_map.end())
844    {
845        this_dispatch = g_dispatch_functions[(*pos).second];
846        found_it = true;
847    }
848
849    // Next check to see if we are in a vtable region:
850
851    if (!found_it)
852    {
853        uint32_t flags;
854        if (m_vtables_ap.get())
855        {
856            found_it = m_vtables_ap->IsAddressInVTables (curr_pc, flags);
857            if (found_it)
858            {
859                this_dispatch.name = "vtable";
860                this_dispatch.stret_return
861                        = (flags & AppleObjCVTables::eOBJC_TRAMPOLINE_STRET) == AppleObjCVTables::eOBJC_TRAMPOLINE_STRET;
862                this_dispatch.is_super = false;
863                this_dispatch.is_super2 = false;
864                this_dispatch.fixedup = DispatchFunction::eFixUpFixed;
865            }
866        }
867    }
868
869    if (found_it)
870    {
871        Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_STEP));
872
873        // We are decoding a method dispatch.
874        // First job is to pull the arguments out:
875
876        lldb::StackFrameSP thread_cur_frame = thread.GetStackFrameAtIndex(0);
877
878        const ABI *abi = NULL;
879        ProcessSP process_sp (thread.CalculateProcess());
880        if (process_sp)
881            abi = process_sp->GetABI().get();
882        if (abi == NULL)
883            return ret_plan_sp;
884
885        TargetSP target_sp (thread.CalculateTarget());
886
887        ClangASTContext *clang_ast_context = target_sp->GetScratchClangASTContext();
888        ValueList argument_values;
889        Value void_ptr_value;
890        ClangASTType clang_void_ptr_type = clang_ast_context->GetBasicType(eBasicTypeVoid).GetPointerType();
891        void_ptr_value.SetValueType (Value::eValueTypeScalar);
892        //void_ptr_value.SetContext (Value::eContextTypeClangType, clang_void_ptr_type);
893        void_ptr_value.SetClangType (clang_void_ptr_type);
894
895        int obj_index;
896        int sel_index;
897
898        // If this is a struct return dispatch, then the first argument is the
899        // return struct pointer, and the object is the second, and the selector is the third.
900        // Otherwise the object is the first and the selector the second.
901        if (this_dispatch.stret_return)
902        {
903            obj_index = 1;
904            sel_index = 2;
905            argument_values.PushValue(void_ptr_value);
906            argument_values.PushValue(void_ptr_value);
907            argument_values.PushValue(void_ptr_value);
908        }
909        else
910        {
911            obj_index = 0;
912            sel_index = 1;
913            argument_values.PushValue(void_ptr_value);
914            argument_values.PushValue(void_ptr_value);
915        }
916
917
918        bool success = abi->GetArgumentValues (thread, argument_values);
919        if (!success)
920            return ret_plan_sp;
921
922        lldb::addr_t obj_addr = argument_values.GetValueAtIndex(obj_index)->GetScalar().ULongLong();
923        if (obj_addr == 0x0)
924        {
925            if (log)
926                log->Printf("Asked to step to dispatch to nil object, returning empty plan.");
927            return ret_plan_sp;
928        }
929
930        ExecutionContext exe_ctx (thread.shared_from_this());
931        Process *process = exe_ctx.GetProcessPtr();
932        // isa_addr will store the class pointer that the method is being dispatched to - so either the class
933        // directly or the super class if this is one of the objc_msgSendSuper flavors.  That's mostly used to
934        // look up the class/selector pair in our cache.
935
936        lldb::addr_t isa_addr = LLDB_INVALID_ADDRESS;
937        lldb::addr_t sel_addr = argument_values.GetValueAtIndex(sel_index)->GetScalar().ULongLong();
938
939        // Figure out the class this is being dispatched to and see if we've already cached this method call,
940        // If so we can push a run-to-address plan directly.  Otherwise we have to figure out where
941        // the implementation lives.
942
943        if (this_dispatch.is_super)
944        {
945            if (this_dispatch.is_super2)
946            {
947               // In the objc_msgSendSuper2 case, we don't get the object directly, we get a structure containing
948               // the object and the class to which the super message is being sent.  So we need to dig the super
949               // out of the class and use that.
950
951                Value super_value(*(argument_values.GetValueAtIndex(obj_index)));
952                super_value.GetScalar() += process->GetAddressByteSize();
953                super_value.ResolveValue (&exe_ctx);
954
955                if (super_value.GetScalar().IsValid())
956                {
957
958                    // isa_value now holds the class pointer.  The second word of the class pointer is the super-class pointer:
959                    super_value.GetScalar() += process->GetAddressByteSize();
960                    super_value.ResolveValue (&exe_ctx);
961                    if (super_value.GetScalar().IsValid())
962                        isa_addr = super_value.GetScalar().ULongLong();
963                    else
964                    {
965                       if (log)
966                        log->Printf("Failed to extract the super class value from the class in objc_super.");
967                    }
968                }
969                else
970                {
971                   if (log)
972                    log->Printf("Failed to extract the class value from objc_super.");
973                }
974            }
975            else
976            {
977               // In the objc_msgSendSuper case, we don't get the object directly, we get a two element structure containing
978               // the object and the super class to which the super message is being sent.  So the class we want is
979               // the second element of this structure.
980
981                Value super_value(*(argument_values.GetValueAtIndex(obj_index)));
982                super_value.GetScalar() += process->GetAddressByteSize();
983                super_value.ResolveValue (&exe_ctx);
984
985                if (super_value.GetScalar().IsValid())
986                {
987                    isa_addr = super_value.GetScalar().ULongLong();
988                }
989                else
990                {
991                   if (log)
992                    log->Printf("Failed to extract the class value from objc_super.");
993                }
994            }
995        }
996        else
997        {
998            // In the direct dispatch case, the object->isa is the class pointer we want.
999
1000            // This is a little cheesy, but since object->isa is the first field,
1001            // making the object value a load address value and resolving it will get
1002            // the pointer sized data pointed to by that value...
1003
1004            // Note, it isn't a fatal error not to be able to get the address from the object, since this might
1005            // be a "tagged pointer" which isn't a real object, but rather some word length encoded dingus.
1006
1007            Value isa_value(*(argument_values.GetValueAtIndex(obj_index)));
1008
1009            isa_value.SetValueType(Value::eValueTypeLoadAddress);
1010            isa_value.ResolveValue(&exe_ctx);
1011            if (isa_value.GetScalar().IsValid())
1012            {
1013                isa_addr = isa_value.GetScalar().ULongLong();
1014            }
1015            else
1016            {
1017               if (log)
1018                log->Printf("Failed to extract the isa value from object.");
1019            }
1020
1021        }
1022
1023        // Okay, we've got the address of the class for which we're resolving this, let's see if it's in our cache:
1024        lldb::addr_t impl_addr = LLDB_INVALID_ADDRESS;
1025
1026        if (isa_addr != LLDB_INVALID_ADDRESS)
1027        {
1028            if (log)
1029            {
1030                log->Printf("Resolving call for class - 0x%" PRIx64 " and selector - 0x%" PRIx64,
1031                            isa_addr, sel_addr);
1032            }
1033            ObjCLanguageRuntime *objc_runtime = m_process_sp->GetObjCLanguageRuntime ();
1034            assert(objc_runtime != NULL);
1035
1036            impl_addr = objc_runtime->LookupInMethodCache (isa_addr, sel_addr);
1037        }
1038
1039        if (impl_addr != LLDB_INVALID_ADDRESS)
1040        {
1041            // Yup, it was in the cache, so we can run to that address directly.
1042
1043            if (log)
1044                log->Printf ("Found implementation address in cache: 0x%" PRIx64, impl_addr);
1045
1046            ret_plan_sp.reset (new ThreadPlanRunToAddress (thread, impl_addr, stop_others));
1047        }
1048        else
1049        {
1050            // We haven't seen this class/selector pair yet.  Look it up.
1051            StreamString errors;
1052            Address impl_code_address;
1053
1054            ValueList dispatch_values;
1055
1056            // We've will inject a little function in the target that takes the object, selector and some flags,
1057            // and figures out the implementation.  Looks like:
1058            //      void *__lldb_objc_find_implementation_for_selector (void *object,
1059            //                                                          void *sel,
1060            //                                                          int is_stret,
1061            //                                                          int is_super,
1062            //                                                          int is_super2,
1063            //                                                          int is_fixup,
1064            //                                                          int is_fixed,
1065            //                                                          int debug)
1066            // So set up the arguments for that call.
1067
1068            dispatch_values.PushValue (*(argument_values.GetValueAtIndex(obj_index)));
1069            dispatch_values.PushValue (*(argument_values.GetValueAtIndex(sel_index)));
1070
1071            Value flag_value;
1072            ClangASTType clang_int_type = clang_ast_context->GetBuiltinTypeForEncodingAndBitSize(lldb::eEncodingSint, 32);
1073            flag_value.SetValueType (Value::eValueTypeScalar);
1074            //flag_value.SetContext (Value::eContextTypeClangType, clang_int_type);
1075            flag_value.SetClangType (clang_int_type);
1076
1077            if (this_dispatch.stret_return)
1078                flag_value.GetScalar() = 1;
1079            else
1080                flag_value.GetScalar() = 0;
1081            dispatch_values.PushValue (flag_value);
1082
1083            if (this_dispatch.is_super)
1084                flag_value.GetScalar() = 1;
1085            else
1086                flag_value.GetScalar() = 0;
1087            dispatch_values.PushValue (flag_value);
1088
1089            if (this_dispatch.is_super2)
1090                flag_value.GetScalar() = 1;
1091            else
1092                flag_value.GetScalar() = 0;
1093            dispatch_values.PushValue (flag_value);
1094
1095            switch (this_dispatch.fixedup)
1096            {
1097              case DispatchFunction::eFixUpNone:
1098                 flag_value.GetScalar() = 0;
1099                 dispatch_values.PushValue (flag_value);
1100                 dispatch_values.PushValue (flag_value);
1101                 break;
1102              case DispatchFunction::eFixUpFixed:
1103                 flag_value.GetScalar() = 1;
1104                 dispatch_values.PushValue (flag_value);
1105                 flag_value.GetScalar() = 1;
1106                 dispatch_values.PushValue (flag_value);
1107                 break;
1108              case DispatchFunction::eFixUpToFix:
1109                 flag_value.GetScalar() = 1;
1110                 dispatch_values.PushValue (flag_value);
1111                 flag_value.GetScalar() = 0;
1112                 dispatch_values.PushValue (flag_value);
1113                 break;
1114            }
1115            if (log && log->GetVerbose())
1116                flag_value.GetScalar() = 1;
1117            else
1118                flag_value.GetScalar() = 0;  // FIXME - Set to 0 when debugging is done.
1119            dispatch_values.PushValue (flag_value);
1120
1121
1122            // The step through code might have to fill in the cache, so it is not safe to run only one thread.
1123            // So we override the stop_others value passed in to us here:
1124            const bool trampoline_stop_others = false;
1125            ret_plan_sp.reset (new AppleThreadPlanStepThroughObjCTrampoline (thread,
1126                                                                             this,
1127                                                                             dispatch_values,
1128                                                                             isa_addr,
1129                                                                             sel_addr,
1130                                                                             trampoline_stop_others));
1131            if (log)
1132            {
1133                StreamString s;
1134                ret_plan_sp->GetDescription(&s, eDescriptionLevelFull);
1135                log->Printf("Using ObjC step plan: %s.\n", s.GetData());
1136            }
1137        }
1138    }
1139
1140    return ret_plan_sp;
1141}
1142
1143ClangFunction *
1144AppleObjCTrampolineHandler::GetLookupImplementationWrapperFunction ()
1145{
1146    return m_impl_function.get();
1147}
1148