1//===-- AppleObjCRuntime.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 "AppleObjCRuntime.h"
11#include "AppleObjCTrampolineHandler.h"
12
13#include "llvm/Support/MachO.h"
14#include "clang/AST/Type.h"
15
16#include "lldb/Breakpoint/BreakpointLocation.h"
17#include "lldb/Core/ConstString.h"
18#include "lldb/Core/Error.h"
19#include "lldb/Core/Log.h"
20#include "lldb/Core/Module.h"
21#include "lldb/Core/ModuleList.h"
22#include "lldb/Core/PluginManager.h"
23#include "lldb/Core/Scalar.h"
24#include "lldb/Core/Section.h"
25#include "lldb/Core/StreamString.h"
26#include "lldb/Expression/ClangFunction.h"
27#include "lldb/Symbol/ClangASTContext.h"
28#include "lldb/Symbol/ObjectFile.h"
29#include "lldb/Target/ExecutionContext.h"
30#include "lldb/Target/Process.h"
31#include "lldb/Target/RegisterContext.h"
32#include "lldb/Target/StopInfo.h"
33#include "lldb/Target/Target.h"
34#include "lldb/Target/Thread.h"
35
36#include <vector>
37
38using namespace lldb;
39using namespace lldb_private;
40
41#define PO_FUNCTION_TIMEOUT_USEC 15*1000*1000
42
43bool
44AppleObjCRuntime::GetObjectDescription (Stream &str, ValueObject &valobj)
45{
46    bool is_signed;
47    // ObjC objects can only be pointers, but we extend this to integer types because an expression might just
48    // result in an address, and we should try that to see if the address is an ObjC object.
49
50    if (!(valobj.IsPointerType() || valobj.IsIntegerType(is_signed)))
51        return false;
52
53    // Make the argument list: we pass one arg, the address of our pointer, to the print function.
54    Value val;
55
56    if (!valobj.ResolveValue(val.GetScalar()))
57        return false;
58
59    ExecutionContext exe_ctx (valobj.GetExecutionContextRef());
60    return GetObjectDescription(str, val, exe_ctx.GetBestExecutionContextScope());
61
62}
63bool
64AppleObjCRuntime::GetObjectDescription (Stream &strm, Value &value, ExecutionContextScope *exe_scope)
65{
66    if (!m_read_objc_library)
67        return false;
68
69    ExecutionContext exe_ctx;
70    exe_scope->CalculateExecutionContext(exe_ctx);
71    Process *process = exe_ctx.GetProcessPtr();
72    if (!process)
73        return false;
74
75    // We need other parts of the exe_ctx, but the processes have to match.
76    assert (m_process == process);
77
78    // Get the function address for the print function.
79    const Address *function_address = GetPrintForDebuggerAddr();
80    if (!function_address)
81        return false;
82
83    Target *target = exe_ctx.GetTargetPtr();
84    ClangASTType clang_type = value.GetClangType();
85    if (clang_type)
86    {
87        if (!clang_type.IsObjCObjectPointerType())
88        {
89            strm.Printf ("Value doesn't point to an ObjC object.\n");
90            return false;
91        }
92    }
93    else
94    {
95        // If it is not a pointer, see if we can make it into a pointer.
96        ClangASTContext *ast_context = target->GetScratchClangASTContext();
97        ClangASTType opaque_type = ast_context->GetBasicType(eBasicTypeObjCID);
98        if (!opaque_type)
99            opaque_type = ast_context->GetBasicType(eBasicTypeVoid).GetPointerType();
100        //value.SetContext(Value::eContextTypeClangType, opaque_type_ptr);
101        value.SetClangType (opaque_type);
102    }
103
104    ValueList arg_value_list;
105    arg_value_list.PushValue(value);
106
107    // This is the return value:
108    ClangASTContext *ast_context = target->GetScratchClangASTContext();
109
110    ClangASTType return_clang_type = ast_context->GetCStringType(true);
111    Value ret;
112//    ret.SetContext(Value::eContextTypeClangType, return_clang_type);
113    ret.SetClangType (return_clang_type);
114
115    if (exe_ctx.GetFramePtr() == NULL)
116    {
117        Thread *thread = exe_ctx.GetThreadPtr();
118        if (thread == NULL)
119        {
120            exe_ctx.SetThreadSP(process->GetThreadList().GetSelectedThread());
121            thread = exe_ctx.GetThreadPtr();
122        }
123        if (thread)
124        {
125            exe_ctx.SetFrameSP(thread->GetSelectedFrame());
126        }
127    }
128
129    // Now we're ready to call the function:
130    ClangFunction func (*exe_ctx.GetBestExecutionContextScope(),
131                        return_clang_type,
132                        *function_address,
133                        arg_value_list);
134
135    StreamString error_stream;
136
137    lldb::addr_t wrapper_struct_addr = LLDB_INVALID_ADDRESS;
138    func.InsertFunction(exe_ctx, wrapper_struct_addr, error_stream);
139
140    const bool unwind_on_error = true;
141    const bool try_all_threads = true;
142    const bool stop_others = true;
143    const bool ignore_breakpoints = true;
144
145    ExecutionResults results = func.ExecuteFunction (exe_ctx,
146                                                     &wrapper_struct_addr,
147                                                     error_stream,
148                                                     stop_others,
149                                                     PO_FUNCTION_TIMEOUT_USEC /* 15 secs timeout */,
150                                                     try_all_threads,
151                                                     unwind_on_error,
152                                                     ignore_breakpoints,
153                                                     ret);
154    if (results != eExecutionCompleted)
155    {
156        strm.Printf("Error evaluating Print Object function: %d.\n", results);
157        return false;
158    }
159
160    addr_t result_ptr = ret.GetScalar().ULongLong(LLDB_INVALID_ADDRESS);
161
162    char buf[512];
163    size_t cstr_len = 0;
164    size_t full_buffer_len = sizeof (buf) - 1;
165    size_t curr_len = full_buffer_len;
166    while (curr_len == full_buffer_len)
167    {
168        Error error;
169        curr_len = process->ReadCStringFromMemory(result_ptr + cstr_len, buf, sizeof(buf), error);
170        strm.Write (buf, curr_len);
171        cstr_len += curr_len;
172    }
173    return cstr_len > 0;
174}
175
176lldb::ModuleSP
177AppleObjCRuntime::GetObjCModule ()
178{
179    ModuleSP module_sp (m_objc_module_wp.lock());
180    if (module_sp)
181        return module_sp;
182
183    Process *process = GetProcess();
184    if (process)
185    {
186        const ModuleList& modules = process->GetTarget().GetImages();
187        for (uint32_t idx = 0; idx < modules.GetSize(); idx++)
188        {
189            module_sp = modules.GetModuleAtIndex(idx);
190            if (AppleObjCRuntime::AppleIsModuleObjCLibrary(module_sp))
191            {
192                m_objc_module_wp = module_sp;
193                return module_sp;
194            }
195        }
196    }
197    return ModuleSP();
198}
199
200Address *
201AppleObjCRuntime::GetPrintForDebuggerAddr()
202{
203    if (!m_PrintForDebugger_addr.get())
204    {
205        const ModuleList &modules = m_process->GetTarget().GetImages();
206
207        SymbolContextList contexts;
208        SymbolContext context;
209
210        if ((!modules.FindSymbolsWithNameAndType(ConstString ("_NSPrintForDebugger"), eSymbolTypeCode, contexts)) &&
211           (!modules.FindSymbolsWithNameAndType(ConstString ("_CFPrintForDebugger"), eSymbolTypeCode, contexts)))
212            return NULL;
213
214        contexts.GetContextAtIndex(0, context);
215
216        m_PrintForDebugger_addr.reset(new Address(context.symbol->GetAddress()));
217    }
218
219    return m_PrintForDebugger_addr.get();
220}
221
222bool
223AppleObjCRuntime::CouldHaveDynamicValue (ValueObject &in_value)
224{
225    return in_value.GetClangType().IsPossibleDynamicType (NULL,
226                                                          false, // do not check C++
227                                                          true); // check ObjC
228}
229
230bool
231AppleObjCRuntime::GetDynamicTypeAndAddress (ValueObject &in_value,
232                                            lldb::DynamicValueType use_dynamic,
233                                            TypeAndOrName &class_type_or_name,
234                                            Address &address)
235{
236    return false;
237}
238
239bool
240AppleObjCRuntime::AppleIsModuleObjCLibrary (const ModuleSP &module_sp)
241{
242    if (module_sp)
243    {
244        const FileSpec &module_file_spec = module_sp->GetFileSpec();
245        static ConstString ObjCName ("libobjc.A.dylib");
246
247        if (module_file_spec)
248        {
249            if (module_file_spec.GetFilename() == ObjCName)
250                return true;
251        }
252    }
253    return false;
254}
255
256bool
257AppleObjCRuntime::IsModuleObjCLibrary (const ModuleSP &module_sp)
258{
259    return AppleIsModuleObjCLibrary(module_sp);
260}
261
262bool
263AppleObjCRuntime::ReadObjCLibrary (const ModuleSP &module_sp)
264{
265    // Maybe check here and if we have a handler already, and the UUID of this module is the same as the one in the
266    // current module, then we don't have to reread it?
267    m_objc_trampoline_handler_ap.reset(new AppleObjCTrampolineHandler (m_process->shared_from_this(), module_sp));
268    if (m_objc_trampoline_handler_ap.get() != NULL)
269    {
270        m_read_objc_library = true;
271        return true;
272    }
273    else
274        return false;
275}
276
277ThreadPlanSP
278AppleObjCRuntime::GetStepThroughTrampolinePlan (Thread &thread, bool stop_others)
279{
280    ThreadPlanSP thread_plan_sp;
281    if (m_objc_trampoline_handler_ap.get())
282        thread_plan_sp = m_objc_trampoline_handler_ap->GetStepThroughDispatchPlan (thread, stop_others);
283    return thread_plan_sp;
284}
285
286//------------------------------------------------------------------
287// Static Functions
288//------------------------------------------------------------------
289enum ObjCRuntimeVersions
290AppleObjCRuntime::GetObjCVersion (Process *process, ModuleSP &objc_module_sp)
291{
292    if (!process)
293        return eObjC_VersionUnknown;
294
295    Target &target = process->GetTarget();
296    const ModuleList &target_modules = target.GetImages();
297    Mutex::Locker modules_locker(target_modules.GetMutex());
298
299    size_t num_images = target_modules.GetSize();
300    for (size_t i = 0; i < num_images; i++)
301    {
302        ModuleSP module_sp = target_modules.GetModuleAtIndexUnlocked(i);
303        // One tricky bit here is that we might get called as part of the initial module loading, but
304        // before all the pre-run libraries get winnowed from the module list.  So there might actually
305        // be an old and incorrect ObjC library sitting around in the list, and we don't want to look at that.
306        // That's why we call IsLoadedInTarget.
307
308        if (AppleIsModuleObjCLibrary (module_sp) && module_sp->IsLoadedInTarget(&target))
309        {
310            objc_module_sp = module_sp;
311            ObjectFile *ofile = module_sp->GetObjectFile();
312            if (!ofile)
313                return eObjC_VersionUnknown;
314
315            SectionList *sections = module_sp->GetSectionList();
316            if (!sections)
317                return eObjC_VersionUnknown;
318            SectionSP v1_telltale_section_sp = sections->FindSectionByName(ConstString ("__OBJC"));
319            if (v1_telltale_section_sp)
320            {
321                return eAppleObjC_V1;
322            }
323            return eAppleObjC_V2;
324        }
325    }
326
327    return eObjC_VersionUnknown;
328}
329
330void
331AppleObjCRuntime::SetExceptionBreakpoints ()
332{
333    const bool catch_bp = false;
334    const bool throw_bp = true;
335    const bool is_internal = true;
336
337    if (!m_objc_exception_bp_sp)
338    {
339        m_objc_exception_bp_sp = LanguageRuntime::CreateExceptionBreakpoint (m_process->GetTarget(),
340                                                                            GetLanguageType(),
341                                                                            catch_bp,
342                                                                            throw_bp,
343                                                                            is_internal);
344        if (m_objc_exception_bp_sp)
345            m_objc_exception_bp_sp->SetBreakpointKind("ObjC exception");
346    }
347    else
348        m_objc_exception_bp_sp->SetEnabled(true);
349}
350
351
352void
353AppleObjCRuntime::ClearExceptionBreakpoints ()
354{
355    if (!m_process)
356        return;
357
358    if (m_objc_exception_bp_sp.get())
359    {
360        m_objc_exception_bp_sp->SetEnabled (false);
361    }
362}
363
364bool
365AppleObjCRuntime::ExceptionBreakpointsExplainStop (lldb::StopInfoSP stop_reason)
366{
367    if (!m_process)
368        return false;
369
370    if (!stop_reason ||
371        stop_reason->GetStopReason() != eStopReasonBreakpoint)
372        return false;
373
374    uint64_t break_site_id = stop_reason->GetValue();
375    return m_process->GetBreakpointSiteList().BreakpointSiteContainsBreakpoint (break_site_id,
376                                                                                m_objc_exception_bp_sp->GetID());
377}
378
379bool
380AppleObjCRuntime::CalculateHasNewLiteralsAndIndexing()
381{
382    if (!m_process)
383        return false;
384
385    Target &target(m_process->GetTarget());
386
387    static ConstString s_method_signature("-[NSDictionary objectForKeyedSubscript:]");
388    static ConstString s_arclite_method_signature("__arclite_objectForKeyedSubscript");
389
390    SymbolContextList sc_list;
391
392    if (target.GetImages().FindSymbolsWithNameAndType(s_method_signature, eSymbolTypeCode, sc_list) ||
393        target.GetImages().FindSymbolsWithNameAndType(s_arclite_method_signature, eSymbolTypeCode, sc_list))
394        return true;
395    else
396        return false;
397}
398
399lldb::SearchFilterSP
400AppleObjCRuntime::CreateExceptionSearchFilter ()
401{
402    Target &target = m_process->GetTarget();
403
404    if (target.GetArchitecture().GetTriple().getVendor() == llvm::Triple::Apple)
405    {
406        FileSpecList filter_modules;
407        filter_modules.Append(FileSpec("libobjc.A.dylib", false));
408        return target.GetSearchFilterForModuleList(&filter_modules);
409    }
410    else
411    {
412        return LanguageRuntime::CreateExceptionSearchFilter();
413    }
414}
415
416