//===-- 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 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()); } llvm::Error GNUstepObjCRuntime::GetObjectDescription(Stream &str, ValueObject &valobj) { return llvm::createStringError( "LLDB's GNUStep runtime does not support object description"); } llvm::Error GNUstepObjCRuntime::GetObjectDescription(Stream &strm, Value &value, ExecutionContextScope *exe_scope) { return llvm::createStringError( "LLDB's GNUStep runtime does not support object description"); } 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( bkpt, "objc_exception_throw", eFunctionNameTypeBase, eLanguageTypeUnknown, Breakpoint::Exact, 0, eLazyBoolNo); return resolver_sp; } llvm::Expected> 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); }