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
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      GetPluginNameStatic(), "GNUstep Objective-C Language Runtime - libobjc2",
33      CreateInstance);
34}
35
36void GNUstepObjCRuntime::Terminate() {
37  PluginManager::UnregisterPlugin(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("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(i);
61    if (CanModuleBeGNUstepObjCLibrary(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(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
100GNUstepObjCRuntime::~GNUstepObjCRuntime() = default;
101
102GNUstepObjCRuntime::GNUstepObjCRuntime(Process *process)
103    : ObjCLanguageRuntime(process), m_objc_module_sp(nullptr) {
104  ReadObjCLibraryIfNeeded(process->GetTarget().GetImages());
105}
106
107bool GNUstepObjCRuntime::GetObjectDescription(Stream &str,
108                                              ValueObject &valobj) {
109  // TODO: ObjC has a generic way to do this
110  return false;
111}
112bool 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
118bool 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
125bool 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
132TypeAndOrName
133GNUstepObjCRuntime::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
162BreakpointResolverSP
163GNUstepObjCRuntime::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
175llvm::Expected<std::unique_ptr<UtilityFunction>>
176GNUstepObjCRuntime::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
197ThreadPlanSP
198GNUstepObjCRuntime::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
204void GNUstepObjCRuntime::UpdateISAToDescriptorMapIfNeeded() {
205  // TODO: Support lazily named and dynamically loaded Objective-C classes
206}
207
208bool GNUstepObjCRuntime::IsModuleObjCLibrary(const ModuleSP &module_sp) {
209  const llvm::Triple &TT = GetTargetRef().GetArchitecture().GetTriple();
210  return CanModuleBeGNUstepObjCLibrary(module_sp, TT);
211}
212
213bool 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
222void GNUstepObjCRuntime::ModulesDidLoad(const ModuleList &module_list) {
223  ReadObjCLibraryIfNeeded(module_list);
224}
225