--- /dev/null
+//===-- 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;
+
+ 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 (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) {
+ 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();
+ const llvm::Triple &TT = GetTargetRef().GetArchitecture().GetTriple();
+ if (TT.isOSBinFormatELF())
+ return filename.starts_with("libobjc.so");
+ if (TT.isOSWindows())
+ return filename == "objc.dll";
+ return false;
+}
+
+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);
+}
--- /dev/null
+//===-- 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 "llvm/ADT/StringRef.h"
+#include "llvm/Support/Error.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
}
@end
-@interface TestObj : NSObject {}
-- (int)ok;
+@interface TestObj : NSObject {
+ int _int;
+ float _float;
+ char _char;
+ void *_ptr_void;
+ NSObject *_ptr_nsobject;
+ id _id_objc;
+}
+- (void)check_ivars_zeroed;
+- (void)set_ivars;
@end
@implementation TestObj
-- (int)ok {
- return self ? 0 : 1;
+- (void)check_ivars_zeroed {
+ ;
+}
+- (void)set_ivars {
+ _int = 1;
+ _float = 2.0f;
+ _char = '\3';
+ _ptr_void = (void*)4;
+ _ptr_nsobject = (NSObject*)5;
+ _id_objc = (id)6;
}
@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:43" -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:43
// SELF: Breakpoint {{.*}} at objc-gnustep-print.m
//
// SELF: (lldb) run
// SELF: Process {{[0-9]+}} stopped
-// SELF: -[TestObj ok](self=[[SELF_PTR:0x[0-9a-f]+]]{{.*}}) at objc-gnustep-print.m:35
+// SELF: -[TestObj check_ivars_zeroed](self=[[SELF_PTR:0x[0-9a-f]+]]{{.*}}) at objc-gnustep-print.m
//
// SELF: (lldb) p self
// SELF: (TestObj *) [[SELF_PTR]]
// SELF: isa
// SELF: refcount
// SELF: }
+// SELF: _int = 0
+// SELF: _float = 0
+// SELF: _char = '\0'
+// SELF: _ptr_void = 0x{{0*}}
+// SELF: _ptr_nsobject = nil
+// SELF: _id_objc = nil
// SELF: }
+// RUN: %lldb -b -o "b objc-gnustep-print.m:106" -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=IVARS_SET
+//
+// IVARS_SET: (lldb) p t->_int
+// IVARS_SET: (int) 1
+//
+// IVARS_SET: (lldb) p t->_float
+// IVARS_SET: (float) 2
+//
+// IVARS_SET: (lldb) p t->_char
+// IVARS_SET: (char) '\x03'
+//
+// IVARS_SET: (lldb) p t->_ptr_void
+// IVARS_SET: (void *) 0x{{0*}}4
+//
+// IVARS_SET: (lldb) p t->_ptr_nsobject
+// IVARS_SET: (NSObject *) 0x{{0*}}5
+//
+// IVARS_SET: (lldb) p t->_id_objc
+// IVARS_SET: (id) 0x{{0*}}6
+
int main() {
TestObj *t = [TestObj new];
- return [t ok];
+ [t check_ivars_zeroed];
+ [t set_ivars];
+ return 0;
}