| //===-- GNUstepObjCRuntime.cpp --------------------------------------------===// |
| // |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| // See https://llvm.org/LICENSE.txt for license information. |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "GNUstepObjCRuntime.h" |
| |
| #include "Plugins/TypeSystem/Clang/TypeSystemClang.h" |
| |
| #include "lldb/Core/Module.h" |
| #include "lldb/Core/PluginManager.h" |
| #include "lldb/Core/ValueObject.h" |
| #include "lldb/Expression/UtilityFunction.h" |
| #include "lldb/Target/ExecutionContext.h" |
| #include "lldb/Target/Process.h" |
| #include "lldb/Target/Target.h" |
| #include "lldb/Utility/ArchSpec.h" |
| #include "lldb/Utility/ConstString.h" |
| |
| using namespace lldb; |
| using namespace lldb_private; |
| |
| LLDB_PLUGIN_DEFINE(GNUstepObjCRuntime) |
| |
| char GNUstepObjCRuntime::ID = 0; |
| |
| void GNUstepObjCRuntime::Initialize() { |
| PluginManager::RegisterPlugin( |
| GetPluginNameStatic(), "GNUstep Objective-C Language Runtime - libobjc2", |
| CreateInstance); |
| } |
| |
| void GNUstepObjCRuntime::Terminate() { |
| PluginManager::UnregisterPlugin(CreateInstance); |
| } |
| |
| static bool CanModuleBeGNUstepObjCLibrary(const ModuleSP &module_sp, |
| const llvm::Triple &TT) { |
| if (!module_sp) |
| return false; |
| const FileSpec &module_file_spec = module_sp->GetFileSpec(); |
| if (!module_file_spec) |
| return false; |
| llvm::StringRef filename = module_file_spec.GetFilename().GetStringRef(); |
| if (TT.isOSBinFormatELF()) |
| return filename.starts_with("libobjc.so"); |
| if (TT.isOSWindows()) |
| return filename == "objc.dll"; |
| return false; |
| } |
| |
| static bool ScanForGNUstepObjCLibraryCandidate(const ModuleList &modules, |
| const llvm::Triple &TT) { |
| std::lock_guard<std::recursive_mutex> guard(modules.GetMutex()); |
| size_t num_modules = modules.GetSize(); |
| for (size_t i = 0; i < num_modules; i++) { |
| auto mod = modules.GetModuleAtIndex(i); |
| if (CanModuleBeGNUstepObjCLibrary(mod, TT)) |
| return true; |
| } |
| return false; |
| } |
| |
| LanguageRuntime *GNUstepObjCRuntime::CreateInstance(Process *process, |
| LanguageType language) { |
| if (language != eLanguageTypeObjC) |
| return nullptr; |
| if (!process) |
| return nullptr; |
| |
| Target &target = process->GetTarget(); |
| const llvm::Triple &TT = target.GetArchitecture().GetTriple(); |
| if (TT.getVendor() == llvm::Triple::VendorType::Apple) |
| return nullptr; |
| |
| const ModuleList &images = target.GetImages(); |
| if (!ScanForGNUstepObjCLibraryCandidate(images, TT)) |
| return nullptr; |
| |
| if (TT.isOSBinFormatELF()) { |
| SymbolContextList eh_pers; |
| RegularExpression regex("__gnustep_objc[x]*_personality_v[0-9]+"); |
| images.FindSymbolsMatchingRegExAndType(regex, eSymbolTypeCode, eh_pers); |
| if (eh_pers.GetSize() == 0) |
| return nullptr; |
| } else if (TT.isOSWindows()) { |
| SymbolContextList objc_mandatory; |
| images.FindSymbolsWithNameAndType(ConstString("__objc_load"), |
| eSymbolTypeCode, objc_mandatory); |
| if (objc_mandatory.GetSize() == 0) |
| return nullptr; |
| } |
| |
| return new GNUstepObjCRuntime(process); |
| } |
| |
| GNUstepObjCRuntime::~GNUstepObjCRuntime() = default; |
| |
| GNUstepObjCRuntime::GNUstepObjCRuntime(Process *process) |
| : ObjCLanguageRuntime(process), m_objc_module_sp(nullptr) { |
| ReadObjCLibraryIfNeeded(process->GetTarget().GetImages()); |
| } |
| |
| bool GNUstepObjCRuntime::GetObjectDescription(Stream &str, |
| ValueObject &valobj) { |
| // TODO: ObjC has a generic way to do this |
| return false; |
| } |
| bool GNUstepObjCRuntime::GetObjectDescription( |
| Stream &strm, Value &value, ExecutionContextScope *exe_scope) { |
| // TODO: ObjC has a generic way to do this |
| return false; |
| } |
| |
| bool GNUstepObjCRuntime::CouldHaveDynamicValue(ValueObject &in_value) { |
| static constexpr bool check_cxx = false; |
| static constexpr bool check_objc = true; |
| return in_value.GetCompilerType().IsPossibleDynamicType(nullptr, check_cxx, |
| check_objc); |
| } |
| |
| bool GNUstepObjCRuntime::GetDynamicTypeAndAddress( |
| ValueObject &in_value, DynamicValueType use_dynamic, |
| TypeAndOrName &class_type_or_name, Address &address, |
| Value::ValueType &value_type) { |
| return false; |
| } |
| |
| TypeAndOrName |
| GNUstepObjCRuntime::FixUpDynamicType(const TypeAndOrName &type_and_or_name, |
| ValueObject &static_value) { |
| CompilerType static_type(static_value.GetCompilerType()); |
| Flags static_type_flags(static_type.GetTypeInfo()); |
| |
| TypeAndOrName ret(type_and_or_name); |
| if (type_and_or_name.HasType()) { |
| // The type will always be the type of the dynamic object. If our parent's |
| // type was a pointer, then our type should be a pointer to the type of the |
| // dynamic object. If a reference, then the original type should be |
| // okay... |
| CompilerType orig_type = type_and_or_name.GetCompilerType(); |
| CompilerType corrected_type = orig_type; |
| if (static_type_flags.AllSet(eTypeIsPointer)) |
| corrected_type = orig_type.GetPointerType(); |
| ret.SetCompilerType(corrected_type); |
| } else { |
| // If we are here we need to adjust our dynamic type name to include the |
| // correct & or * symbol |
| std::string corrected_name(type_and_or_name.GetName().GetCString()); |
| if (static_type_flags.AllSet(eTypeIsPointer)) |
| corrected_name.append(" *"); |
| // the parent type should be a correctly pointer'ed or referenc'ed type |
| ret.SetCompilerType(static_type); |
| ret.SetName(corrected_name.c_str()); |
| } |
| return ret; |
| } |
| |
| BreakpointResolverSP |
| GNUstepObjCRuntime::CreateExceptionResolver(const BreakpointSP &bkpt, |
| bool catch_bp, bool throw_bp) { |
| BreakpointResolverSP resolver_sp; |
| |
| if (throw_bp) |
| resolver_sp = std::make_shared<BreakpointResolverName>( |
| bkpt, "objc_exception_throw", eFunctionNameTypeBase, |
| eLanguageTypeUnknown, Breakpoint::Exact, 0, eLazyBoolNo); |
| |
| return resolver_sp; |
| } |
| |
| llvm::Expected<std::unique_ptr<UtilityFunction>> |
| GNUstepObjCRuntime::CreateObjectChecker(std::string name, |
| ExecutionContext &exe_ctx) { |
| // TODO: This function is supposed to check whether an ObjC selector is |
| // present for an object. Might be implemented similar as in the Apple V2 |
| // runtime. |
| const char *function_template = R"( |
| extern "C" void |
| %s(void *$__lldb_arg_obj, void *$__lldb_arg_selector) {} |
| )"; |
| |
| char empty_function_code[2048]; |
| int len = ::snprintf(empty_function_code, sizeof(empty_function_code), |
| function_template, name.c_str()); |
| |
| assert(len < (int)sizeof(empty_function_code)); |
| UNUSED_IF_ASSERT_DISABLED(len); |
| |
| return GetTargetRef().CreateUtilityFunction(empty_function_code, name, |
| eLanguageTypeC, exe_ctx); |
| } |
| |
| ThreadPlanSP |
| GNUstepObjCRuntime::GetStepThroughTrampolinePlan(Thread &thread, |
| bool stop_others) { |
| // TODO: Implement this properly to avoid stepping into things like PLT stubs |
| return nullptr; |
| } |
| |
| void GNUstepObjCRuntime::UpdateISAToDescriptorMapIfNeeded() { |
| // TODO: Support lazily named and dynamically loaded Objective-C classes |
| } |
| |
| bool GNUstepObjCRuntime::IsModuleObjCLibrary(const ModuleSP &module_sp) { |
| const llvm::Triple &TT = GetTargetRef().GetArchitecture().GetTriple(); |
| return CanModuleBeGNUstepObjCLibrary(module_sp, TT); |
| } |
| |
| bool GNUstepObjCRuntime::ReadObjCLibrary(const ModuleSP &module_sp) { |
| assert(m_objc_module_sp == nullptr && "Check HasReadObjCLibrary() first"); |
| m_objc_module_sp = module_sp; |
| |
| // Right now we don't use this, but we might want to check for debugger |
| // runtime support symbols like 'gdb_object_getClass' in the future. |
| return true; |
| } |
| |
| void GNUstepObjCRuntime::ModulesDidLoad(const ModuleList &module_list) { |
| ReadObjCLibraryIfNeeded(module_list); |
| } |