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 static bool CanModuleBeGNUstepObjCLibrary(const ModuleSP &module_sp,
41                                           const llvm::Triple &TT) {
42   if (!module_sp)
43     return false;
44   const FileSpec &module_file_spec = module_sp->GetFileSpec();
45   if (!module_file_spec)
46     return false;
47   llvm::StringRef filename = module_file_spec.GetFilename().GetStringRef();
48   if (TT.isOSBinFormatELF())
49     return filename.starts_with("libobjc.so");
50   if (TT.isOSWindows())
51     return filename == "objc.dll";
52   return false;
53 }
54 
55 static bool ScanForGNUstepObjCLibraryCandidate(const ModuleList &modules,
56                                                const llvm::Triple &TT) {
57   std::lock_guard<std::recursive_mutex> guard(modules.GetMutex());
58   size_t num_modules = modules.GetSize();
59   for (size_t i = 0; i < num_modules; i++) {
60     auto mod = modules.GetModuleAtIndex(i);
61     if (CanModuleBeGNUstepObjCLibrary(mod, TT))
62       return true;
63   }
64   return false;
65 }
66 
67 LanguageRuntime *GNUstepObjCRuntime::CreateInstance(Process *process,
68                                                     LanguageType language) {
69   if (language != eLanguageTypeObjC)
70     return nullptr;
71   if (!process)
72     return nullptr;
73 
74   Target &target = process->GetTarget();
75   const llvm::Triple &TT = target.GetArchitecture().GetTriple();
76   if (TT.getVendor() == llvm::Triple::VendorType::Apple)
77     return nullptr;
78 
79   const ModuleList &images = target.GetImages();
80   if (!ScanForGNUstepObjCLibraryCandidate(images, TT))
81     return nullptr;
82 
83   if (TT.isOSBinFormatELF()) {
84     SymbolContextList eh_pers;
85     RegularExpression regex("__gnustep_objc[x]*_personality_v[0-9]+");
86     images.FindSymbolsMatchingRegExAndType(regex, eSymbolTypeCode, eh_pers);
87     if (eh_pers.GetSize() == 0)
88       return nullptr;
89   } else if (TT.isOSWindows()) {
90     SymbolContextList objc_mandatory;
91     images.FindSymbolsWithNameAndType(ConstString("__objc_load"),
92                                       eSymbolTypeCode, objc_mandatory);
93     if (objc_mandatory.GetSize() == 0)
94       return nullptr;
95   }
96 
97   return new GNUstepObjCRuntime(process);
98 }
99 
100 GNUstepObjCRuntime::~GNUstepObjCRuntime() = default;
101 
102 GNUstepObjCRuntime::GNUstepObjCRuntime(Process *process)
103     : ObjCLanguageRuntime(process), m_objc_module_sp(nullptr) {
104   ReadObjCLibraryIfNeeded(process->GetTarget().GetImages());
105 }
106 
107 bool GNUstepObjCRuntime::GetObjectDescription(Stream &str,
108                                               ValueObject &valobj) {
109   // TODO: ObjC has a generic way to do this
110   return false;
111 }
112 bool GNUstepObjCRuntime::GetObjectDescription(
113     Stream &strm, Value &value, ExecutionContextScope *exe_scope) {
114   // TODO: ObjC has a generic way to do this
115   return false;
116 }
117 
118 bool GNUstepObjCRuntime::CouldHaveDynamicValue(ValueObject &in_value) {
119   static constexpr bool check_cxx = false;
120   static constexpr bool check_objc = true;
121   return in_value.GetCompilerType().IsPossibleDynamicType(nullptr, check_cxx,
122                                                           check_objc);
123 }
124 
125 bool GNUstepObjCRuntime::GetDynamicTypeAndAddress(
126     ValueObject &in_value, DynamicValueType use_dynamic,
127     TypeAndOrName &class_type_or_name, Address &address,
128     Value::ValueType &value_type) {
129   return false;
130 }
131 
132 TypeAndOrName
133 GNUstepObjCRuntime::FixUpDynamicType(const TypeAndOrName &type_and_or_name,
134                                      ValueObject &static_value) {
135   CompilerType static_type(static_value.GetCompilerType());
136   Flags static_type_flags(static_type.GetTypeInfo());
137 
138   TypeAndOrName ret(type_and_or_name);
139   if (type_and_or_name.HasType()) {
140     // The type will always be the type of the dynamic object.  If our parent's
141     // type was a pointer, then our type should be a pointer to the type of the
142     // dynamic object.  If a reference, then the original type should be
143     // okay...
144     CompilerType orig_type = type_and_or_name.GetCompilerType();
145     CompilerType corrected_type = orig_type;
146     if (static_type_flags.AllSet(eTypeIsPointer))
147       corrected_type = orig_type.GetPointerType();
148     ret.SetCompilerType(corrected_type);
149   } else {
150     // If we are here we need to adjust our dynamic type name to include the
151     // correct & or * symbol
152     std::string corrected_name(type_and_or_name.GetName().GetCString());
153     if (static_type_flags.AllSet(eTypeIsPointer))
154       corrected_name.append(" *");
155     // the parent type should be a correctly pointer'ed or referenc'ed type
156     ret.SetCompilerType(static_type);
157     ret.SetName(corrected_name.c_str());
158   }
159   return ret;
160 }
161 
162 BreakpointResolverSP
163 GNUstepObjCRuntime::CreateExceptionResolver(const BreakpointSP &bkpt,
164                                             bool catch_bp, bool throw_bp) {
165   BreakpointResolverSP resolver_sp;
166 
167   if (throw_bp)
168     resolver_sp = std::make_shared<BreakpointResolverName>(
169         bkpt, "objc_exception_throw", eFunctionNameTypeBase,
170         eLanguageTypeUnknown, Breakpoint::Exact, 0, eLazyBoolNo);
171 
172   return resolver_sp;
173 }
174 
175 llvm::Expected<std::unique_ptr<UtilityFunction>>
176 GNUstepObjCRuntime::CreateObjectChecker(std::string name,
177                                         ExecutionContext &exe_ctx) {
178   // TODO: This function is supposed to check whether an ObjC selector is
179   // present for an object. Might be implemented similar as in the Apple V2
180   // runtime.
181   const char *function_template = R"(
182     extern "C" void
183     %s(void *$__lldb_arg_obj, void *$__lldb_arg_selector) {}
184   )";
185 
186   char empty_function_code[2048];
187   int len = ::snprintf(empty_function_code, sizeof(empty_function_code),
188                        function_template, name.c_str());
189 
190   assert(len < (int)sizeof(empty_function_code));
191   UNUSED_IF_ASSERT_DISABLED(len);
192 
193   return GetTargetRef().CreateUtilityFunction(empty_function_code, name,
194                                               eLanguageTypeC, exe_ctx);
195 }
196 
197 ThreadPlanSP
198 GNUstepObjCRuntime::GetStepThroughTrampolinePlan(Thread &thread,
199                                                  bool stop_others) {
200   // TODO: Implement this properly to avoid stepping into things like PLT stubs
201   return nullptr;
202 }
203 
204 void GNUstepObjCRuntime::UpdateISAToDescriptorMapIfNeeded() {
205   // TODO: Support lazily named and dynamically loaded Objective-C classes
206 }
207 
208 bool GNUstepObjCRuntime::IsModuleObjCLibrary(const ModuleSP &module_sp) {
209   const llvm::Triple &TT = GetTargetRef().GetArchitecture().GetTriple();
210   return CanModuleBeGNUstepObjCLibrary(module_sp, TT);
211 }
212 
213 bool GNUstepObjCRuntime::ReadObjCLibrary(const ModuleSP &module_sp) {
214   assert(m_objc_module_sp == nullptr && "Check HasReadObjCLibrary() first");
215   m_objc_module_sp = module_sp;
216 
217   // Right now we don't use this, but we might want to check for debugger
218   // runtime support symbols like 'gdb_object_getClass' in the future.
219   return true;
220 }
221 
222 void GNUstepObjCRuntime::ModulesDidLoad(const ModuleList &module_list) {
223   ReadObjCLibraryIfNeeded(module_list);
224 }
225