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 GetPluginNameStatic(), "GNUstep Objective-C Language Runtime - libobjc2", 33 CreateInstance); 34 } 35 36 void GNUstepObjCRuntime::Terminate() { 37 PluginManager::UnregisterPlugin(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("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(i); 61 if (CanModuleBeGNUstepObjCLibrary(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(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 100 GNUstepObjCRuntime::~GNUstepObjCRuntime() = default; 101 102 GNUstepObjCRuntime::GNUstepObjCRuntime(Process *process) 103 : ObjCLanguageRuntime(process), m_objc_module_sp(nullptr) { 104 ReadObjCLibraryIfNeeded(process->GetTarget().GetImages()); 105 } 106 107 llvm::Error GNUstepObjCRuntime::GetObjectDescription(Stream &str, 108 ValueObject &valobj) { 109 return llvm::createStringError( 110 "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 "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(nullptr, 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) { 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(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(eTypeIsPointer)) 156 corrected_name.append(" *"); 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 bkpt, "objc_exception_throw", eFunctionNameTypeBase, 172 eLanguageTypeUnknown, Breakpoint::Exact, 0, 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(empty_function_code, sizeof(empty_function_code), 190 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(empty_function_code, name, 196 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