| 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 | |
| 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 | llvm::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 | |
| 113 | llvm::Error |
| 114 | GNUstepObjCRuntime::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 | |
| 120 | bool 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 | |
| 127 | bool 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 | |
| 134 | TypeAndOrName |
| 135 | GNUstepObjCRuntime::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 | |
| 164 | BreakpointResolverSP |
| 165 | GNUstepObjCRuntime::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 | |
| 177 | llvm::Expected<std::unique_ptr<UtilityFunction>> |
| 178 | GNUstepObjCRuntime::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 | |
| 199 | ThreadPlanSP |
| 200 | GNUstepObjCRuntime::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 | |
| 206 | void GNUstepObjCRuntime::UpdateISAToDescriptorMapIfNeeded() { |
| 207 | // TODO: Support lazily named and dynamically loaded Objective-C classes |
| 208 | } |
| 209 | |
| 210 | bool GNUstepObjCRuntime::IsModuleObjCLibrary(const ModuleSP &module_sp) { |
| 211 | const llvm::Triple &TT = GetTargetRef().GetArchitecture().GetTriple(); |
| 212 | return CanModuleBeGNUstepObjCLibrary(module_sp, TT); |
| 213 | } |
| 214 | |
| 215 | bool 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 | |
| 224 | void GNUstepObjCRuntime::ModulesDidLoad(const ModuleList &module_list) { |
| 225 | ReadObjCLibraryIfNeeded(module_list); |
| 226 | } |
| 227 | |