sgraenitz created this revision.
sgraenitz added reviewers: DavidSpickett, theraven, teemperor, bulbazord,
labath.
Herald added a project: All.
sgraenitz requested review of this revision.
Herald added a project: LLDB.
Herald added a subscriber: lldb-commits.
This is the next patch after D146058 <https://reviews.llvm.org/D146058>. We can
now parse expressions to print instance variables from ObjC classes. Until now
the expression parser would bail out with an error like this:
error: expression failed to parse:
error: Error [IRForTarget]: Couldn't find Objective-C indirect ivar symbol
OBJC_IVAR_$_TestObj._int
Repository:
rG LLVM Github Monorepo
https://reviews.llvm.org/D146154
Files:
lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionParser.cpp
lldb/source/Plugins/LanguageRuntime/ObjC/CMakeLists.txt
lldb/source/Plugins/LanguageRuntime/ObjC/GNUstepObjCRuntime/CMakeLists.txt
lldb/source/Plugins/LanguageRuntime/ObjC/GNUstepObjCRuntime/GNUstepObjCRuntime.cpp
lldb/source/Plugins/LanguageRuntime/ObjC/GNUstepObjCRuntime/GNUstepObjCRuntime.h
lldb/source/Plugins/LanguageRuntime/ObjC/ObjCLanguageRuntime.cpp
lldb/source/Plugins/LanguageRuntime/ObjC/ObjCLanguageRuntime.h
lldb/test/Shell/Expr/objc-gnustep-print.m
Index: lldb/test/Shell/Expr/objc-gnustep-print.m
===================================================================
--- lldb/test/Shell/Expr/objc-gnustep-print.m
+++ lldb/test/Shell/Expr/objc-gnustep-print.m
@@ -26,8 +26,14 @@
return class_createInstance(self, 0);
}
@end
-
-@interface TestObj : NSObject {}
+@interface TestObj : NSObject {
+ int _int;
+ float _float;
+ char _char;
+ void *_ptr_void;
+ NSObject *_ptr_nsobject;
+ id _id_objc;
+}
- (int)ok;
@end
@implementation TestObj
@@ -36,14 +42,14 @@
}
@end
-// RUN: %lldb -b -o "b objc-gnustep-print.m:35" -o "run" -o "p self" -o "p *self" -- %t | FileCheck %s --check-prefix=SELF
+// RUN: %lldb -b -o "b objc-gnustep-print.m:41" -o "run" -o "p self" -o "p *self" -- %t | FileCheck %s --check-prefix=SELF
//
-// SELF: (lldb) b objc-gnustep-print.m:35
+// SELF: (lldb) b objc-gnustep-print.m:41
// SELF: Breakpoint {{.*}} at objc-gnustep-print.m
//
// SELF: (lldb) run
// SELF: Process {{[0-9]+}} stopped
-// SELF: -[TestObj ok](self=[[SELF_PTR:0x[0-9]+]]{{.*}}) at objc-gnustep-print.m:35
+// SELF: -[TestObj ok](self=[[SELF_PTR:0x[0-9]+]]{{.*}}) at objc-gnustep-print.m:41
//
// SELF: (lldb) p self
// SELF: (TestObj *) $0 = [[SELF_PTR]]
@@ -56,6 +62,27 @@
// SELF: }
// SELF: }
+// RUN: %lldb -b -o "b objc-gnustep-print.m:88" -o "run" -o "p t->_int" -o "p t->_float" -o "p t->_char" \
+// RUN: -o "p t->_ptr_void" -o "p t->_ptr_nsobject" -o "p t->_id_objc" -- %t | FileCheck %s --check-prefix=MEMBERS_OUTSIDE
+//
+// MEMBERS_OUTSIDE: (lldb) p t->_int
+// MEMBERS_OUTSIDE: (int) $0 = 0
+//
+// MEMBERS_OUTSIDE: (lldb) p t->_float
+// MEMBERS_OUTSIDE: (float) $1 = 0
+//
+// MEMBERS_OUTSIDE: (lldb) p t->_char
+// MEMBERS_OUTSIDE: (char) $2 = '\0'
+//
+// MEMBERS_OUTSIDE: (lldb) p t->_ptr_void
+// MEMBERS_OUTSIDE: (void *) $3 = 0x0000000000000000
+//
+// MEMBERS_OUTSIDE: (lldb) p t->_ptr_nsobject
+// MEMBERS_OUTSIDE: (NSObject *) $4 = nil
+//
+// MEMBERS_OUTSIDE: (lldb) p t->_id_objc
+// MEMBERS_OUTSIDE: (id) $5 = nil
+
int main() {
TestObj *t = [TestObj new];
return [t ok];
Index: lldb/source/Plugins/LanguageRuntime/ObjC/ObjCLanguageRuntime.h
===================================================================
--- lldb/source/Plugins/LanguageRuntime/ObjC/ObjCLanguageRuntime.h
+++ lldb/source/Plugins/LanguageRuntime/ObjC/ObjCLanguageRuntime.h
@@ -38,7 +38,8 @@
enum class ObjCRuntimeVersions {
eObjC_VersionUnknown = 0,
eAppleObjC_V1 = 1,
- eAppleObjC_V2 = 2
+ eAppleObjC_V2 = 2,
+ eGNUstep_libobjc2 = 3,
};
typedef lldb::addr_t ObjCISA;
Index: lldb/source/Plugins/LanguageRuntime/ObjC/ObjCLanguageRuntime.cpp
===================================================================
--- lldb/source/Plugins/LanguageRuntime/ObjC/ObjCLanguageRuntime.cpp
+++ lldb/source/Plugins/LanguageRuntime/ObjC/ObjCLanguageRuntime.cpp
@@ -235,6 +235,22 @@
m_isa_to_descriptor.begin(), m_isa_to_descriptor.end());
}
+void ObjCLanguageRuntime::ReadObjCLibraryIfNeeded(
+ const ModuleList &module_list) {
+ if (!HasReadObjCLibrary()) {
+ std::lock_guard<std::recursive_mutex> guard(module_list.GetMutex());
+
+ size_t num_modules = module_list.GetSize();
+ for (size_t i = 0; i < num_modules; i++) {
+ auto mod = module_list.GetModuleAtIndex(i);
+ if (IsModuleObjCLibrary(mod)) {
+ ReadObjCLibrary(mod);
+ break;
+ }
+ }
+ }
+}
+
ObjCLanguageRuntime::ObjCISA
ObjCLanguageRuntime::GetParentClass(ObjCLanguageRuntime::ObjCISA isa) {
ClassDescriptorSP objc_class_sp(GetClassDescriptorFromISA(isa));
Index: lldb/source/Plugins/LanguageRuntime/ObjC/GNUstepObjCRuntime/GNUstepObjCRuntime.h
===================================================================
--- /dev/null
+++ lldb/source/Plugins/LanguageRuntime/ObjC/GNUstepObjCRuntime/GNUstepObjCRuntime.h
@@ -0,0 +1,107 @@
+//===-- GNUstepObjCRuntime.h ------------------------------------*- C++ -*-===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLDB_SOURCE_PLUGINS_LANGUAGERUNTIME_OBJC_GNUSTEPOBJCRUNTIME_GNUSTEPOBJCRUNTIME_H
+#define LLDB_SOURCE_PLUGINS_LANGUAGERUNTIME_OBJC_GNUSTEPOBJCRUNTIME_GNUSTEPOBJCRUNTIME_H
+
+#include "lldb/Target/LanguageRuntime.h"
+#include "lldb/lldb-private.h"
+
+#include "Plugins/LanguageRuntime/ObjC/ObjCLanguageRuntime.h"
+#include <optional>
+
+namespace lldb_private {
+
+class GNUstepObjCRuntime : public lldb_private::ObjCLanguageRuntime {
+public:
+ ~GNUstepObjCRuntime() override;
+
+ //
+ // PluginManager, PluginInterface and LLVM RTTI implementation
+ //
+
+ static char ID;
+
+ static void Initialize();
+
+ static void Terminate();
+
+ static lldb_private::LanguageRuntime *
+ CreateInstance(Process *process, lldb::LanguageType language);
+
+ static llvm::StringRef GetPluginNameStatic() {
+ return "gnustep-objc-libobjc2";
+ }
+
+ llvm::StringRef GetPluginName() override { return GetPluginNameStatic(); }
+
+ void ModulesDidLoad(const ModuleList &module_list) override;
+
+ bool isA(const void *ClassID) const override {
+ return ClassID == &ID || ObjCLanguageRuntime::isA(ClassID);
+ }
+
+ static bool classof(const LanguageRuntime *runtime) {
+ return runtime->isA(&ID);
+ }
+
+ //
+ // LanguageRuntime implementation
+ //
+ bool GetObjectDescription(Stream &str, Value &value,
+ ExecutionContextScope *exe_scope) override;
+
+ bool GetObjectDescription(Stream &str, ValueObject &object) override;
+
+ bool CouldHaveDynamicValue(ValueObject &in_value) override;
+
+ bool GetDynamicTypeAndAddress(ValueObject &in_value,
+ lldb::DynamicValueType use_dynamic,
+ TypeAndOrName &class_type_or_name,
+ Address &address,
+ Value::ValueType &value_type) override;
+
+ TypeAndOrName FixUpDynamicType(const TypeAndOrName &type_and_or_name,
+ ValueObject &static_value) override;
+
+ lldb::BreakpointResolverSP
+ CreateExceptionResolver(const lldb::BreakpointSP &bkpt, bool catch_bp,
+ bool throw_bp) override;
+
+ lldb::ThreadPlanSP GetStepThroughTrampolinePlan(Thread &thread,
+ bool stop_others) override;
+
+ //
+ // ObjCLanguageRuntime implementation
+ //
+
+ bool IsModuleObjCLibrary(const lldb::ModuleSP &module_sp) override;
+
+ bool ReadObjCLibrary(const lldb::ModuleSP &module_sp) override;
+
+ bool HasReadObjCLibrary() override { return m_objc_module_sp != nullptr; }
+
+ llvm::Expected<std::unique_ptr<UtilityFunction>>
+ CreateObjectChecker(std::string name, ExecutionContext &exe_ctx) override;
+
+ ObjCRuntimeVersions GetRuntimeVersion() const override {
+ return ObjCRuntimeVersions::eGNUstep_libobjc2;
+ }
+
+ void UpdateISAToDescriptorMapIfNeeded() override;
+
+protected:
+ // Call CreateInstance instead.
+ GNUstepObjCRuntime(Process *process);
+
+ lldb::ModuleSP m_objc_module_sp;
+};
+
+} // namespace lldb_private
+
+#endif // LLDB_SOURCE_PLUGINS_LANGUAGERUNTIME_OBJC_GNUSTEPOBJCRUNTIME_GNUSTEPOBJCRUNTIME_H
Index: lldb/source/Plugins/LanguageRuntime/ObjC/GNUstepObjCRuntime/GNUstepObjCRuntime.cpp
===================================================================
--- /dev/null
+++ lldb/source/Plugins/LanguageRuntime/ObjC/GNUstepObjCRuntime/GNUstepObjCRuntime.cpp
@@ -0,0 +1,182 @@
+//===-- 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);
+}
+
+LanguageRuntime *GNUstepObjCRuntime::CreateInstance(Process *process,
+ LanguageType language) {
+ if (language != eLanguageTypeObjC)
+ return nullptr;
+ if (!process)
+ return nullptr;
+ const llvm::Triple &TT = process->GetTarget().GetArchitecture().GetTriple();
+ if (TT.getVendor() == llvm::Triple::VendorType::Apple)
+ 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) {
+ 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();
+ return filename.starts_with("libobjc.so") || filename == "objc.dll";
+}
+
+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);
+}
Index: lldb/source/Plugins/LanguageRuntime/ObjC/GNUstepObjCRuntime/CMakeLists.txt
===================================================================
--- /dev/null
+++ lldb/source/Plugins/LanguageRuntime/ObjC/GNUstepObjCRuntime/CMakeLists.txt
@@ -0,0 +1,19 @@
+add_lldb_library(lldbPluginGNUstepObjCRuntime PLUGIN
+ GNUstepObjCRuntime.cpp
+
+ LINK_LIBS
+ lldbBreakpoint
+ lldbCore
+ lldbExpression
+ lldbHost
+ lldbInterpreter
+ lldbSymbol
+ lldbTarget
+ lldbUtility
+ lldbPluginExpressionParserClang
+ lldbPluginTypeSystemClang
+ CLANG_LIBS
+ clangAST
+ LINK_COMPONENTS
+ Support
+ )
Index: lldb/source/Plugins/LanguageRuntime/ObjC/CMakeLists.txt
===================================================================
--- lldb/source/Plugins/LanguageRuntime/ObjC/CMakeLists.txt
+++ lldb/source/Plugins/LanguageRuntime/ObjC/CMakeLists.txt
@@ -7,4 +7,6 @@
lldbTarget
lldbUtility
)
+
add_subdirectory(AppleObjCRuntime)
+add_subdirectory(GNUstepObjCRuntime)
Index: lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionParser.cpp
===================================================================
--- lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionParser.cpp
+++ lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionParser.cpp
@@ -577,12 +577,19 @@
if (process_sp && lang_opts.ObjC) {
if (auto *runtime = ObjCLanguageRuntime::Get(*process_sp)) {
- if (runtime->GetRuntimeVersion() ==
- ObjCLanguageRuntime::ObjCRuntimeVersions::eAppleObjC_V2)
+ switch (runtime->GetRuntimeVersion()) {
+ case ObjCLanguageRuntime::ObjCRuntimeVersions::eAppleObjC_V2:
lang_opts.ObjCRuntime.set(ObjCRuntime::MacOSX, VersionTuple(10, 7));
- else
+ break;
+ case ObjCLanguageRuntime::ObjCRuntimeVersions::eObjC_VersionUnknown:
+ case ObjCLanguageRuntime::ObjCRuntimeVersions::eAppleObjC_V1:
lang_opts.ObjCRuntime.set(ObjCRuntime::FragileMacOSX,
VersionTuple(10, 7));
+ break;
+ case ObjCLanguageRuntime::ObjCRuntimeVersions::eGNUstep_libobjc2:
+ lang_opts.ObjCRuntime.set(ObjCRuntime::GNUstep, VersionTuple(2, 0));
+ break;
+ }
if (runtime->HasNewLiteralsAndIndexing())
lang_opts.DebuggerObjCLiteral = true;
_______________________________________________
lldb-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits