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 bool GNUstepObjCRuntime::GetObjectDescription(Stream &str, 108 ValueObject &valobj) { 109 // TODO: ObjC has a generic way to do this 110 return false; 111 } 112 bool 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 118 bool 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 125 bool 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 132 TypeAndOrName 133 GNUstepObjCRuntime::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 162 BreakpointResolverSP 163 GNUstepObjCRuntime::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 175 llvm::Expected<std::unique_ptr<UtilityFunction>> 176 GNUstepObjCRuntime::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 197 ThreadPlanSP 198 GNUstepObjCRuntime::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 204 void GNUstepObjCRuntime::UpdateISAToDescriptorMapIfNeeded() { 205 // TODO: Support lazily named and dynamically loaded Objective-C classes 206 } 207 208 bool GNUstepObjCRuntime::IsModuleObjCLibrary(const ModuleSP &module_sp) { 209 const llvm::Triple &TT = GetTargetRef().GetArchitecture().GetTriple(); 210 return CanModuleBeGNUstepObjCLibrary(module_sp, TT); 211 } 212 213 bool 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 222 void GNUstepObjCRuntime::ModulesDidLoad(const ModuleList &module_list) { 223 ReadObjCLibraryIfNeeded(module_list); 224 } 225