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