1 //===-- GNUstepObjCRuntime.cpp --------------------------------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #include "GNUstepObjCRuntime.h"
10 
11 #include "Plugins/TypeSystem/Clang/TypeSystemClang.h"
12 
13 #include "lldb/Core/Module.h"
14 #include "lldb/Core/PluginManager.h"
15 #include "lldb/Core/ValueObject.h"
16 #include "lldb/Expression/UtilityFunction.h"
17 #include "lldb/Target/ExecutionContext.h"
18 #include "lldb/Target/Process.h"
19 #include "lldb/Target/Target.h"
20 #include "lldb/Utility/ArchSpec.h"
21 #include "lldb/Utility/ConstString.h"
22 
23 using namespace lldb;
24 using namespace lldb_private;
25 
26 LLDB_PLUGIN_DEFINE(GNUstepObjCRuntime)
27 
28 char GNUstepObjCRuntime::ID = 0;
29 
30 void GNUstepObjCRuntime::Initialize() {
31   PluginManager::RegisterPlugin(
32       GetPluginNameStatic(), "GNUstep Objective-C Language Runtime - libobjc2",
33       CreateInstance);
34 }
35 
36 void GNUstepObjCRuntime::Terminate() {
37   PluginManager::UnregisterPlugin(CreateInstance);
38 }
39 
40 LanguageRuntime *GNUstepObjCRuntime::CreateInstance(Process *process,
41                                                     LanguageType language) {
42   if (language != eLanguageTypeObjC)
43     return nullptr;
44   if (!process)
45     return nullptr;
46 
47   Target &target = process->GetTarget();
48   const llvm::Triple &TT = target.GetArchitecture().GetTriple();
49   if (TT.getVendor() == llvm::Triple::VendorType::Apple)
50     return nullptr;
51 
52   const ModuleList &images = target.GetImages();
53   if (TT.isOSBinFormatELF()) {
54     SymbolContextList eh_pers;
55     RegularExpression regex("__gnustep_objc[x]*_personality_v[0-9]+");
56     images.FindSymbolsMatchingRegExAndType(regex, eSymbolTypeCode, eh_pers);
57     if (eh_pers.GetSize() == 0)
58       return nullptr;
59   } else if (TT.isOSWindows()) {
60     SymbolContextList objc_mandatory;
61     images.FindSymbolsWithNameAndType(ConstString("__objc_load"),
62                                       eSymbolTypeCode, objc_mandatory);
63     if (objc_mandatory.GetSize() == 0)
64       return nullptr;
65   }
66 
67   return new GNUstepObjCRuntime(process);
68 }
69 
70 GNUstepObjCRuntime::~GNUstepObjCRuntime() = default;
71 
72 GNUstepObjCRuntime::GNUstepObjCRuntime(Process *process)
73     : ObjCLanguageRuntime(process), m_objc_module_sp(nullptr) {
74   ReadObjCLibraryIfNeeded(process->GetTarget().GetImages());
75 }
76 
77 bool GNUstepObjCRuntime::GetObjectDescription(Stream &str,
78                                               ValueObject &valobj) {
79   // TODO: ObjC has a generic way to do this
80   return false;
81 }
82 bool GNUstepObjCRuntime::GetObjectDescription(
83     Stream &strm, Value &value, ExecutionContextScope *exe_scope) {
84   // TODO: ObjC has a generic way to do this
85   return false;
86 }
87 
88 bool GNUstepObjCRuntime::CouldHaveDynamicValue(ValueObject &in_value) {
89   static constexpr bool check_cxx = false;
90   static constexpr bool check_objc = true;
91   return in_value.GetCompilerType().IsPossibleDynamicType(nullptr, check_cxx,
92                                                           check_objc);
93 }
94 
95 bool GNUstepObjCRuntime::GetDynamicTypeAndAddress(
96     ValueObject &in_value, DynamicValueType use_dynamic,
97     TypeAndOrName &class_type_or_name, Address &address,
98     Value::ValueType &value_type) {
99   return false;
100 }
101 
102 TypeAndOrName
103 GNUstepObjCRuntime::FixUpDynamicType(const TypeAndOrName &type_and_or_name,
104                                      ValueObject &static_value) {
105   CompilerType static_type(static_value.GetCompilerType());
106   Flags static_type_flags(static_type.GetTypeInfo());
107 
108   TypeAndOrName ret(type_and_or_name);
109   if (type_and_or_name.HasType()) {
110     // The type will always be the type of the dynamic object.  If our parent's
111     // type was a pointer, then our type should be a pointer to the type of the
112     // dynamic object.  If a reference, then the original type should be
113     // okay...
114     CompilerType orig_type = type_and_or_name.GetCompilerType();
115     CompilerType corrected_type = orig_type;
116     if (static_type_flags.AllSet(eTypeIsPointer))
117       corrected_type = orig_type.GetPointerType();
118     ret.SetCompilerType(corrected_type);
119   } else {
120     // If we are here we need to adjust our dynamic type name to include the
121     // correct & or * symbol
122     std::string corrected_name(type_and_or_name.GetName().GetCString());
123     if (static_type_flags.AllSet(eTypeIsPointer))
124       corrected_name.append(" *");
125     // the parent type should be a correctly pointer'ed or referenc'ed type
126     ret.SetCompilerType(static_type);
127     ret.SetName(corrected_name.c_str());
128   }
129   return ret;
130 }
131 
132 BreakpointResolverSP
133 GNUstepObjCRuntime::CreateExceptionResolver(const BreakpointSP &bkpt,
134                                             bool catch_bp, bool throw_bp) {
135   BreakpointResolverSP resolver_sp;
136 
137   if (throw_bp)
138     resolver_sp = std::make_shared<BreakpointResolverName>(
139         bkpt, "objc_exception_throw", eFunctionNameTypeBase,
140         eLanguageTypeUnknown, Breakpoint::Exact, 0, eLazyBoolNo);
141 
142   return resolver_sp;
143 }
144 
145 llvm::Expected<std::unique_ptr<UtilityFunction>>
146 GNUstepObjCRuntime::CreateObjectChecker(std::string name,
147                                         ExecutionContext &exe_ctx) {
148   // TODO: This function is supposed to check whether an ObjC selector is
149   // present for an object. Might be implemented similar as in the Apple V2
150   // runtime.
151   const char *function_template = R"(
152     extern "C" void
153     %s(void *$__lldb_arg_obj, void *$__lldb_arg_selector) {}
154   )";
155 
156   char empty_function_code[2048];
157   int len = ::snprintf(empty_function_code, sizeof(empty_function_code),
158                        function_template, name.c_str());
159 
160   assert(len < (int)sizeof(empty_function_code));
161   UNUSED_IF_ASSERT_DISABLED(len);
162 
163   return GetTargetRef().CreateUtilityFunction(empty_function_code, name,
164                                               eLanguageTypeC, exe_ctx);
165 }
166 
167 ThreadPlanSP
168 GNUstepObjCRuntime::GetStepThroughTrampolinePlan(Thread &thread,
169                                                  bool stop_others) {
170   // TODO: Implement this properly to avoid stepping into things like PLT stubs
171   return nullptr;
172 }
173 
174 void GNUstepObjCRuntime::UpdateISAToDescriptorMapIfNeeded() {
175   // TODO: Support lazily named and dynamically loaded Objective-C classes
176 }
177 
178 bool GNUstepObjCRuntime::IsModuleObjCLibrary(const ModuleSP &module_sp) {
179   if (!module_sp)
180     return false;
181   const FileSpec &module_file_spec = module_sp->GetFileSpec();
182   if (!module_file_spec)
183     return false;
184   llvm::StringRef filename = module_file_spec.GetFilename().GetStringRef();
185   const llvm::Triple &TT = GetTargetRef().GetArchitecture().GetTriple();
186   if (TT.isOSBinFormatELF())
187     return filename.starts_with("libobjc.so");
188   if (TT.isOSWindows())
189     return filename == "objc.dll";
190   return false;
191 }
192 
193 bool GNUstepObjCRuntime::ReadObjCLibrary(const ModuleSP &module_sp) {
194   assert(m_objc_module_sp == nullptr && "Check HasReadObjCLibrary() first");
195   m_objc_module_sp = module_sp;
196 
197   // Right now we don't use this, but we might want to check for debugger
198   // runtime support symbols like 'gdb_object_getClass' in the future.
199   return true;
200 }
201 
202 void GNUstepObjCRuntime::ModulesDidLoad(const ModuleList &module_list) {
203   ReadObjCLibraryIfNeeded(module_list);
204 }
205