224 lines
7.9 KiB
C++
224 lines
7.9 KiB
C++
//===-- 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);
|
|
}
|