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 | name: GetPluginNameStatic(), description: "GNUstep Objective-C Language Runtime - libobjc2" , |
33 | create_callback: CreateInstance); |
34 | } |
35 | |
36 | void GNUstepObjCRuntime::Terminate() { |
37 | PluginManager::UnregisterPlugin(create_callback: 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(Prefix: "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(idx: i); |
61 | if (CanModuleBeGNUstepObjCLibrary(module_sp: 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(modules: 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, symbol_type: eSymbolTypeCode, sc_list&: eh_pers); |
87 | if (eh_pers.GetSize() == 0) |
88 | return nullptr; |
89 | } else if (TT.isOSWindows()) { |
90 | SymbolContextList objc_mandatory; |
91 | images.FindSymbolsWithNameAndType(name: ConstString("__objc_load" ), |
92 | symbol_type: eSymbolTypeCode, sc_list&: 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(module_list: 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(target_type: nullptr, check_cplusplus: 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(mask: 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(mask: eTypeIsPointer)) |
154 | corrected_name.append(s: " *" ); |
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 | args: bkpt, args: "objc_exception_throw" , args: eFunctionNameTypeBase, |
170 | args: eLanguageTypeUnknown, args: Breakpoint::Exact, args: 0, args: 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(s: empty_function_code, maxlen: sizeof(empty_function_code), |
188 | format: 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(expression: empty_function_code, name, |
194 | language: 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 | |