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/Expression/UtilityFunction.h"
16#include "lldb/Target/ExecutionContext.h"
17#include "lldb/Target/Process.h"
18#include "lldb/Target/Target.h"
19#include "lldb/Utility/ArchSpec.h"
20#include "lldb/Utility/ConstString.h"
21#include "lldb/ValueObject/ValueObject.h"
22
23using namespace lldb;
24using namespace lldb_private;
25
26LLDB_PLUGIN_DEFINE(GNUstepObjCRuntime)
27
28char GNUstepObjCRuntime::ID = 0;
29
30void GNUstepObjCRuntime::Initialize() {
31 PluginManager::RegisterPlugin(
32 name: GetPluginNameStatic(), description: "GNUstep Objective-C Language Runtime - libobjc2",
33 create_callback: CreateInstance);
34}
35
36void GNUstepObjCRuntime::Terminate() {
37 PluginManager::UnregisterPlugin(create_callback: CreateInstance);
38}
39
40static 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
55static 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
67LanguageRuntime *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
100GNUstepObjCRuntime::~GNUstepObjCRuntime() = default;
101
102GNUstepObjCRuntime::GNUstepObjCRuntime(Process *process)
103 : ObjCLanguageRuntime(process), m_objc_module_sp(nullptr) {
104 ReadObjCLibraryIfNeeded(module_list: process->GetTarget().GetImages());
105}
106
107llvm::Error GNUstepObjCRuntime::GetObjectDescription(Stream &str,
108 ValueObject &valobj) {
109 return llvm::createStringError(
110 Fmt: "LLDB's GNUStep runtime does not support object description");
111}
112
113llvm::Error
114GNUstepObjCRuntime::GetObjectDescription(Stream &strm, Value &value,
115 ExecutionContextScope *exe_scope) {
116 return llvm::createStringError(
117 Fmt: "LLDB's GNUStep runtime does not support object description");
118}
119
120bool GNUstepObjCRuntime::CouldHaveDynamicValue(ValueObject &in_value) {
121 static constexpr bool check_cxx = false;
122 static constexpr bool check_objc = true;
123 return in_value.GetCompilerType().IsPossibleDynamicType(target_type: nullptr, check_cplusplus: check_cxx,
124 check_objc);
125}
126
127bool GNUstepObjCRuntime::GetDynamicTypeAndAddress(
128 ValueObject &in_value, DynamicValueType use_dynamic,
129 TypeAndOrName &class_type_or_name, Address &address,
130 Value::ValueType &value_type, llvm::ArrayRef<uint8_t> &local_buffer) {
131 return false;
132}
133
134TypeAndOrName
135GNUstepObjCRuntime::FixUpDynamicType(const TypeAndOrName &type_and_or_name,
136 ValueObject &static_value) {
137 CompilerType static_type(static_value.GetCompilerType());
138 Flags static_type_flags(static_type.GetTypeInfo());
139
140 TypeAndOrName ret(type_and_or_name);
141 if (type_and_or_name.HasType()) {
142 // The type will always be the type of the dynamic object. If our parent's
143 // type was a pointer, then our type should be a pointer to the type of the
144 // dynamic object. If a reference, then the original type should be
145 // okay...
146 CompilerType orig_type = type_and_or_name.GetCompilerType();
147 CompilerType corrected_type = orig_type;
148 if (static_type_flags.AllSet(mask: eTypeIsPointer))
149 corrected_type = orig_type.GetPointerType();
150 ret.SetCompilerType(corrected_type);
151 } else {
152 // If we are here we need to adjust our dynamic type name to include the
153 // correct & or * symbol
154 std::string corrected_name(type_and_or_name.GetName().GetCString());
155 if (static_type_flags.AllSet(mask: eTypeIsPointer))
156 corrected_name.append(s: " *");
157 // the parent type should be a correctly pointer'ed or referenc'ed type
158 ret.SetCompilerType(static_type);
159 ret.SetName(corrected_name.c_str());
160 }
161 return ret;
162}
163
164BreakpointResolverSP
165GNUstepObjCRuntime::CreateExceptionResolver(const BreakpointSP &bkpt,
166 bool catch_bp, bool throw_bp) {
167 BreakpointResolverSP resolver_sp;
168
169 if (throw_bp)
170 resolver_sp = std::make_shared<BreakpointResolverName>(
171 args: bkpt, args: "objc_exception_throw", args: eFunctionNameTypeBase,
172 args: eLanguageTypeUnknown, args: Breakpoint::Exact, args: 0, args: eLazyBoolNo);
173
174 return resolver_sp;
175}
176
177llvm::Expected<std::unique_ptr<UtilityFunction>>
178GNUstepObjCRuntime::CreateObjectChecker(std::string name,
179 ExecutionContext &exe_ctx) {
180 // TODO: This function is supposed to check whether an ObjC selector is
181 // present for an object. Might be implemented similar as in the Apple V2
182 // runtime.
183 const char *function_template = R"(
184 extern "C" void
185 %s(void *$__lldb_arg_obj, void *$__lldb_arg_selector) {}
186 )";
187
188 char empty_function_code[2048];
189 int len = ::snprintf(s: empty_function_code, maxlen: sizeof(empty_function_code),
190 format: function_template, name.c_str());
191
192 assert(len < (int)sizeof(empty_function_code));
193 UNUSED_IF_ASSERT_DISABLED(len);
194
195 return GetTargetRef().CreateUtilityFunction(expression: empty_function_code, name,
196 language: eLanguageTypeC, exe_ctx);
197}
198
199ThreadPlanSP
200GNUstepObjCRuntime::GetStepThroughTrampolinePlan(Thread &thread,
201 bool stop_others) {
202 // TODO: Implement this properly to avoid stepping into things like PLT stubs
203 return nullptr;
204}
205
206void GNUstepObjCRuntime::UpdateISAToDescriptorMapIfNeeded() {
207 // TODO: Support lazily named and dynamically loaded Objective-C classes
208}
209
210bool GNUstepObjCRuntime::IsModuleObjCLibrary(const ModuleSP &module_sp) {
211 const llvm::Triple &TT = GetTargetRef().GetArchitecture().GetTriple();
212 return CanModuleBeGNUstepObjCLibrary(module_sp, TT);
213}
214
215bool GNUstepObjCRuntime::ReadObjCLibrary(const ModuleSP &module_sp) {
216 assert(m_objc_module_sp == nullptr && "Check HasReadObjCLibrary() first");
217 m_objc_module_sp = module_sp;
218
219 // Right now we don't use this, but we might want to check for debugger
220 // runtime support symbols like 'gdb_object_getClass' in the future.
221 return true;
222}
223
224void GNUstepObjCRuntime::ModulesDidLoad(const ModuleList &module_list) {
225 ReadObjCLibraryIfNeeded(module_list);
226}
227

source code of lldb/source/Plugins/LanguageRuntime/ObjC/GNUstepObjCRuntime/GNUstepObjCRuntime.cpp