From 3a8bf26b52c73d62c8fd5c5fa172c8ddc8820550 Mon Sep 17 00:00:00 2001 From: Nat! Date: Thu, 21 Mar 2019 12:04:23 +0100 Subject: [PATCH] mulle_lldb_70 squashed and four conflicts resolved --- .gitignore | 2 +- CODE_OWNERS.txt | 4 + INSTALL.txt | 17 + MULLE_LLDB_VERSION | 1 + README.md | 16 + bin/migrate-to-next-release | 182 +++++ include/lldb/Symbol/ClangASTContext.h | 3 + include/lldb/Target/LanguageRuntime.h | 4 + include/lldb/Target/ObjCLanguageRuntime.h | 4 + include/lldb/Target/Process.h | 6 + source/API/SystemInitializerFull.cpp | 7 + source/DataFormatters/ValueObjectPrinter.cpp | 41 +- source/Expression/ExpressionSourceCode.cpp | 16 + .../Clang/ClangExpressionParser.cpp | 16 +- .../ExpressionParser/Clang/IRForTarget.cpp | 61 +- source/Plugins/Language/ObjC/CMakeLists.txt | 3 + .../AppleObjCRuntime/AppleObjCRuntime.cpp | 1 + .../ObjC/AppleObjCRuntime/AppleObjCRuntime.h | 1 + .../LanguageRuntime/ObjC/CMakeLists.txt | 3 + .../ObjC/MulleObjCRuntime/CMakeLists.txt | 8 + .../ObjC/MulleObjCRuntime/Makefile | 11 + .../MulleObjCRuntime/MulleObjCRuntime.cpp | 527 +++++++++++++ .../ObjC/MulleObjCRuntime/MulleObjCRuntime.h | 129 ++++ .../MulleObjCRuntime/MulleObjCRuntimeV1.cpp | 534 +++++++++++++ .../MulleObjCRuntime/MulleObjCRuntimeV1.h | 170 +++++ .../MulleObjCTrampolineHandler.cpp | 711 ++++++++++++++++++ .../MulleObjCTrampolineHandler.h | 94 +++ ...lleThreadPlanStepThroughObjCTrampoline.cpp | 242 ++++++ ...MulleThreadPlanStepThroughObjCTrampoline.h | 82 ++ .../ObjC/MulleObjCRuntime/make-inc.sh | 73 ++ .../mulle-objc-dangerous-class-storage.inc | 15 + .../mulle-objc-dangerous-class-storage.src | 21 + .../mulle-objc-lookup-imp.inc | 30 + .../mulle-objc-lookup-imp.src | 40 + .../mulle-objc-object-checker.inc | 15 + .../mulle-objc-object-checker.src | 20 + .../SymbolFile/DWARF/DWARFASTParserClang.cpp | 139 +++- .../SymbolFile/DWARF/DWARFASTParserClang.h | 8 + source/Symbol/ClangASTContext.cpp | 29 +- source/Target/Process.cpp | 10 + source/Target/ThreadPlanShouldStopHere.cpp | 29 + source/lldb.cpp | 5 +- 42 files changed, 3284 insertions(+), 46 deletions(-) create mode 100644 MULLE_LLDB_VERSION create mode 100644 README.md create mode 100755 bin/migrate-to-next-release create mode 100644 source/Plugins/LanguageRuntime/ObjC/MulleObjCRuntime/CMakeLists.txt create mode 100644 source/Plugins/LanguageRuntime/ObjC/MulleObjCRuntime/Makefile create mode 100644 source/Plugins/LanguageRuntime/ObjC/MulleObjCRuntime/MulleObjCRuntime.cpp create mode 100644 source/Plugins/LanguageRuntime/ObjC/MulleObjCRuntime/MulleObjCRuntime.h create mode 100644 source/Plugins/LanguageRuntime/ObjC/MulleObjCRuntime/MulleObjCRuntimeV1.cpp create mode 100644 source/Plugins/LanguageRuntime/ObjC/MulleObjCRuntime/MulleObjCRuntimeV1.h create mode 100644 source/Plugins/LanguageRuntime/ObjC/MulleObjCRuntime/MulleObjCTrampolineHandler.cpp create mode 100644 source/Plugins/LanguageRuntime/ObjC/MulleObjCRuntime/MulleObjCTrampolineHandler.h create mode 100644 source/Plugins/LanguageRuntime/ObjC/MulleObjCRuntime/MulleThreadPlanStepThroughObjCTrampoline.cpp create mode 100644 source/Plugins/LanguageRuntime/ObjC/MulleObjCRuntime/MulleThreadPlanStepThroughObjCTrampoline.h create mode 100755 source/Plugins/LanguageRuntime/ObjC/MulleObjCRuntime/make-inc.sh create mode 100644 source/Plugins/LanguageRuntime/ObjC/MulleObjCRuntime/mulle-objc-dangerous-class-storage.inc create mode 100644 source/Plugins/LanguageRuntime/ObjC/MulleObjCRuntime/mulle-objc-dangerous-class-storage.src create mode 100644 source/Plugins/LanguageRuntime/ObjC/MulleObjCRuntime/mulle-objc-lookup-imp.inc create mode 100644 source/Plugins/LanguageRuntime/ObjC/MulleObjCRuntime/mulle-objc-lookup-imp.src create mode 100644 source/Plugins/LanguageRuntime/ObjC/MulleObjCRuntime/mulle-objc-object-checker.inc create mode 100644 source/Plugins/LanguageRuntime/ObjC/MulleObjCRuntime/mulle-objc-object-checker.src diff --git a/.gitignore b/.gitignore index 35da4f658f..78f57b3727 100644 --- a/.gitignore +++ b/.gitignore @@ -24,7 +24,7 @@ .*.swp .sw? # OS X specific files. -.DS_store +.DS_Store DerivedData/ # Remote build configuration files. diff --git a/CODE_OWNERS.txt b/CODE_OWNERS.txt index 8ee04557af..b5f1eeafce 100644 --- a/CODE_OWNERS.txt +++ b/CODE_OWNERS.txt @@ -50,3 +50,7 @@ D: Test suite N: Pavel Labath E: labath@google.com D: Linux, Android + +N: Nat! +E: nat@codeon.de +D: MulleObjC diff --git a/INSTALL.txt b/INSTALL.txt index ef7126994f..60b2e9d081 100644 --- a/INSTALL.txt +++ b/INSTALL.txt @@ -16,3 +16,20 @@ For instructions to build LLDB on Linux, or more details about supported compiler versions, other dependencies, and build flags, see: http://lldb.llvm.org/build.html + + +## Build fix for Xcode with mulle_lldb_40 + +The script will try to download and bail on `release_40` not being there + +Fix it like so: + +``` +cd llvm +git fetch origin refs/heads/release_40 +git checkout -b release_40 f3d3277bb713bb8aced9a7ac2e9b05c52d2844ee + +cd tools/clang +git fetch origin refs/heads/release_40 +git checkout -b release_40 21fe7e8f8ab44b67238af7bf9ba9d8afdf0c0e2c +``` diff --git a/MULLE_LLDB_VERSION b/MULLE_LLDB_VERSION new file mode 100644 index 0000000000..6ae5431db8 --- /dev/null +++ b/MULLE_LLDB_VERSION @@ -0,0 +1 @@ +6.0.0.3 diff --git a/README.md b/README.md new file mode 100644 index 0000000000..d6c22b180a --- /dev/null +++ b/README.md @@ -0,0 +1,16 @@ +[![Codeon Gmbh](CodeonLogo.png)](//www.codeon.de) + +# mulle-lldb + +This is a debugger for [mulle-objc](//www.mulle-kybernetik.com/weblog/2015/mulle_objc_a_new_objective_c_.html) +runtime. + +> See [README.txt](README.txt) for more information about lldb + +The debugger is built together with the [mulle-clang](https://github.com/Codeon-GmbH/mulle-clang) compiler. See the compiler for more details. + + +## Author + +[Nat!](//www.mulle-kybernetik.com/weblog) for +[Codeon GmbH](//www.codeon.de) diff --git a/bin/migrate-to-next-release b/bin/migrate-to-next-release new file mode 100755 index 0000000000..017a88f998 --- /dev/null +++ b/bin/migrate-to-next-release @@ -0,0 +1,182 @@ +#!/usr/bin/env bash + +[ "${TRACE}" = "YES" ] && set -x && : "$0" "$@" + +OLD_LLVM_BRANCH="release_70" +OLD_MULLE_DEV_BRANCH="mulle_lldb_70" +NEW_LLVM_BRANCH="release_80" +NEW_MULLE_DEV_BRANCH="mulle_lldb_80" + +LLVM_REMOTE="llvm" + +BEFOREFILE=.before-tags.txt +AFTERFILE=.after-tags.txt +SOURCEDIRS="include/ source/" + + +# +# this function should be identical in mulle-clang/mulle-lldb +# If you edit them edit the corresponding file too +# + +####### >-> DONT EDIT >-> + +migrate() +{ + [ ! -e "MULLE_LLDB_VERSION" ] && echo "cd to the root of mulle-lldb" >&2 && exit 1 + + if ! git rev-parse --verify "${OLD_MULLE_DEV_BRANCH}" > /dev/null 2>&1 + then + echo "Branch ${OLD_MULLE_DEV_BRANCH} must exist" >&2 && exit 1 + fi + + + if git rev-parse --verify "${NEW_MULLE_DEV_BRANCH}" > /dev/null 2>&1 + then + echo "Branch ${NEW_MULLE_DEV_BRANCH} must not exist yet" >&2 && exit 1 + fi + + # + # remove garbage tmp if present + # + if git rev-parse --verify "tmp_${NEW_MULLE_DEV_BRANCH}" > /dev/null 2>&1 + then + git branch -D "tmp_${NEW_MULLE_DEV_BRANCH}" || exit 1 + fi + + # + # remove garbage tag if present + # + if git rev-parse --verify "squashed_${OLD_MULLE_DEV_BRANCH}" > /dev/null 2>&1 + then + git tag -d "squashed_${OLD_MULLE_DEV_BRANCH}" || exit 1 + fi + + # + # get new version from LLVM (github) + # + if ! git ls-remote --exit-code llvm > /dev/null + then + git remote add "${LLVM_REMOTE}" https://github.com/llvm-mirror/lldb.git 2> /dev/null + fi + git fetch "${LLVM_REMOTE}" || exit 1 + + if ! git rev-parse --verify "${LLVM_REMOTE}/${OLD_LLVM_BRANCH}" > /dev/null 2>&1 + then + echo "Branch ${LLVM_REMOTE}/${OLD_LLVM_BRANCH} must exist" >&2 && exit 1 + fi + + + # find the place we forked from last time + ancestor="`git merge-base "${LLVM_REMOTE}/${OLD_LLVM_BRANCH}" "${OLD_MULLE_DEV_BRANCH}"`" + [ -z "${ancestor}" ] && echo "No common ancestor found" >&2 && exit 1 + + # create a new temporary branch to contain squashed patchset + echo "### 1: Checkout" >&2 && + + git checkout -b "tmp_${NEW_MULLE_DEV_BRANCH}" "${ancestor}" || exit 1 + + # + # squash everything into new branch + # this helps weed out re-edits and commits that weren't useful + # easing the conflict resolution + # + # ???? git merge --squash "tmp_${OLD_MULLE_DEV_BRANCH}" + echo "### 2: Squash Merge" >&2 + + git merge --squash "${OLD_MULLE_DEV_BRANCH}" || exit 1 + + + # commit stuff + echo "### 3: Commit" >&2 + + git commit -m "${OLD_MULLE_DEV_BRANCH} squashed" || exit 1 + + # remember until where did we squash the old branch (in case of + # future edits) + echo "### 4: Tag" >&2 + + git tag "squashed_${OLD_MULLE_DEV_BRANCH}" "${OLD_MULLE_DEV_BRANCH}" || exit 1 + + # count our change marker texts + grep -R '@mulle-' ${SOURCEDIRS} > "${BEFOREFILE}" || exit 1 + + # + # Now get the new stuff + # + echo "### 5: Checkout" >&2 + + git checkout -b "${NEW_MULLE_DEV_BRANCH}" "${LLVM_REMOTE}/${NEW_LLVM_BRANCH}" || exit 1 + + echo "### 6: Cherry pick" >&2 + + if ! git cherry-pick "tmp_${NEW_MULLE_DEV_BRANCH}" + then + git status -s + exit 1 + fi +} + + +cleanup() +{ + # count our change marker texts again + grep -R '@mulle-' ${SOURCEDIRS} > "${AFTERFILE}" || exit 1 + + local before + local after + + before="`cat "${BEFOREFILE}" `" + after="`cat "${AFTERFILE}" `" + + if [ "${before}" != "${after}" ] + then + echo "Some @mulle- tags got lost in the merge" >&2 + echo "before : ${BEFOREFILE}" >&2 + echo "after : ${AFTERFILE}" >&2 + diff "${BEFOREFILE}" "${AFTERFILE}" >&2 + exit 1 + fi + + # + # resolve conflicts manually. + # Check with grep '@mulle-objc' ... | wc -l, that all changes are present + # + echo "### 7: Tmp branch delete" >&2 + + git branch -D "tmp_${NEW_MULLE_DEV_BRANCH}" || exit 1 + rm "${AFTERFILE}" "${BEFOREFILE}" +} + + +####### <-< DONT EDIT <-< + + +# +# since an old version of this script gets checked out over the new one +# try to have bash parsed everything already +# +# +# since an old version of this script gets checked out over the new one +# try to have bash parsed everything already +# +[ ! -e "MULLE_LLDB_VERSION" ] && echo "cd to the root of mulle-lldb" >&2 && exit 1 + + +case "$1" in + 'continue'|'cleanup') + cleanup + ;; + + *) + if [ ! -f "${AFTERFILE}" ] + then + migrate + fi + cleanup + ;; +esac + + +migrate + diff --git a/include/lldb/Symbol/ClangASTContext.h b/include/lldb/Symbol/ClangASTContext.h index 4cff9b2565..ca04a81f63 100644 --- a/include/lldb/Symbol/ClangASTContext.h +++ b/include/lldb/Symbol/ClangASTContext.h @@ -883,6 +883,9 @@ class ClangASTContext : public TypeSystem { const char *name, // the full symbol name as seen in the symbol table // (lldb::opaque_compiler_type_t type, "-[NString // stringWithCString:]") +/// @mulle-objc@ add this for parameter names > + std::vector &function_param_decls, +/// @mulle-objc@ add this for parameter names < const CompilerType &method_compiler_type, lldb::AccessType access, bool is_artificial, bool is_variadic); diff --git a/include/lldb/Target/LanguageRuntime.h b/include/lldb/Target/LanguageRuntime.h index 2a2f47b853..08909396b9 100644 --- a/include/lldb/Target/LanguageRuntime.h +++ b/include/lldb/Target/LanguageRuntime.h @@ -132,6 +132,10 @@ class LanguageRuntime : public PluginInterface { Process *GetProcess() { return m_process; } + // @mulle-lldb@ code from swift-lldb > + virtual bool IsSymbolARuntimeThunk(const Symbol &symbol) { return false; } + // @mulle-lldb@ code from swift-lldb < + Target &GetTargetRef() { return m_process->GetTarget(); } virtual lldb::BreakpointResolverSP diff --git a/include/lldb/Target/ObjCLanguageRuntime.h b/include/lldb/Target/ObjCLanguageRuntime.h index 9eebf94630..8e8f279793 100644 --- a/include/lldb/Target/ObjCLanguageRuntime.h +++ b/include/lldb/Target/ObjCLanguageRuntime.h @@ -37,6 +37,9 @@ class ObjCLanguageRuntime : public LanguageRuntime { eObjC_VersionUnknown = 0, eAppleObjC_V1 = 1, eAppleObjC_V2 = 2 + // @mulle-lldb@ add runtime enum > + , eMulleObjC_V1 = 48 + // @mulle-lldb@ add runtime enum < }; typedef lldb::addr_t ObjCISA; @@ -218,6 +221,7 @@ class ObjCLanguageRuntime : public LanguageRuntime { virtual bool HasReadObjCLibrary() = 0; virtual lldb::ThreadPlanSP GetStepThroughTrampolinePlan(Thread &thread, + StackID &return_stack_id, bool stop_others) = 0; lldb::addr_t LookupInMethodCache(lldb::addr_t class_addr, lldb::addr_t sel); diff --git a/include/lldb/Target/Process.h b/include/lldb/Target/Process.h index be72b9a9c7..3b1af03f76 100644 --- a/include/lldb/Target/Process.h +++ b/include/lldb/Target/Process.h @@ -3132,6 +3132,12 @@ class Process : public std::enable_shared_from_this, std::unique_ptr m_dlopen_utility_func_up; std::once_flag m_dlopen_utility_func_flag_once; +// @mulle-lldb@ avoid failed threadplan calling formatters that execute a threadplan again > +public: + bool isThreadPlanLocked( void); +protected: +// @mulle-lldb@ avoid failed threadplan calling formatters that execute a threadplan again < + size_t RemoveBreakpointOpcodesFromBuffer(lldb::addr_t addr, size_t size, uint8_t *buf) const; diff --git a/source/API/SystemInitializerFull.cpp b/source/API/SystemInitializerFull.cpp index 42dea6a01a..55954da993 100644 --- a/source/API/SystemInitializerFull.cpp +++ b/source/API/SystemInitializerFull.cpp @@ -59,6 +59,9 @@ #include "Plugins/Language/ObjC/ObjCLanguage.h" #include "Plugins/Language/ObjCPlusPlus/ObjCPlusPlusLanguage.h" #include "Plugins/LanguageRuntime/CPlusPlus/ItaniumABI/ItaniumABILanguageRuntime.h" +// @mulle-lldb@ Initialize runtime > +#include "Plugins/LanguageRuntime/ObjC/MulleObjCRuntime/MulleObjCRuntimeV1.h" +// @mulle-lldb@ Initialize runtime < #include "Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV1.h" #include "Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.h" #include "Plugins/LanguageRuntime/RenderScript/RenderScriptRuntime/RenderScriptRuntime.h" @@ -354,6 +357,10 @@ SystemInitializerFull::Initialize(const InitializerOptions &options) { EmulateInstructionPPC64::Initialize(); SymbolFileDWARFDebugMap::Initialize(); ItaniumABILanguageRuntime::Initialize(); + // @mulle-lldb@ Initialize runtime > + // initialize ahead of AppleRuntimes, so we get searched first + MulleObjCRuntimeV1::Initialize(); + // @mulle-lldb@ Initialize runtime < AppleObjCRuntimeV2::Initialize(); AppleObjCRuntimeV1::Initialize(); SystemRuntimeMacOSX::Initialize(); diff --git a/source/DataFormatters/ValueObjectPrinter.cpp b/source/DataFormatters/ValueObjectPrinter.cpp index 082158822d..3b465e85ad 100644 --- a/source/DataFormatters/ValueObjectPrinter.cpp +++ b/source/DataFormatters/ValueObjectPrinter.cpp @@ -749,6 +749,8 @@ bool ValueObjectPrinter::PrintChildrenOneLiner(bool hide_names) { return true; } +// @mulle-lldb@ >> rewritten function to not hit DataVisualization::ShouldPrintAsOneLiner + void ValueObjectPrinter::PrintChildrenIfNeeded(bool value_printed, bool summary_printed) { // this flag controls whether we tried to display a description for this @@ -759,16 +761,35 @@ void ValueObjectPrinter::PrintChildrenIfNeeded(bool value_printed, auto curr_ptr_depth = m_ptr_depth; bool print_children = ShouldPrintChildren(is_failed_description, curr_ptr_depth); + + // DataVisualization::ShouldPrintAsOneLiner often called for + // print_oneline (see below) is very expensive, so use an + // early exit, if we are not printing children (also easier to read) + + if (!print_children) { + if (m_curr_depth >= m_options.m_max_depth && IsAggregate() && + ShouldPrintValueObject()) { + m_stream->PutCString("{...}\n"); + } else + m_stream->EOL(); + return; + } + + // + // TODO: maybe move the next line to #1#, but its unclear to me if + // DataVisualization::ShouldPrintAsOneLiner can modify *m_valobj + // bool print_oneline = (curr_ptr_depth.CanAllowExpansion() || m_options.m_show_types || !m_options.m_allow_oneliner_mode || m_options.m_flat_output || (m_options.m_pointer_as_array) || m_options.m_show_location) ? false : DataVisualization::ShouldPrintAsOneLiner(*m_valobj); + bool is_instance_ptr = IsInstancePointer(); uint64_t instance_ptr_value = LLDB_INVALID_ADDRESS; - if (print_children && is_instance_ptr) { + if (is_instance_ptr) { instance_ptr_value = m_valobj->GetValueAsUnsigned(0); if (m_printed_instance_pointers->count(instance_ptr_value)) { // we already printed this instance-is-pointer thing, so don't expand it @@ -781,19 +802,15 @@ void ValueObjectPrinter::PrintChildrenIfNeeded(bool value_printed, instance_ptr_value); // remember this guy for future reference } - if (print_children) { - if (print_oneline) { - m_stream->PutChar(' '); - PrintChildrenOneLiner(false); - m_stream->EOL(); - } else - PrintChildren(value_printed, summary_printed, curr_ptr_depth); - } else if (m_curr_depth >= m_options.m_max_depth && IsAggregate() && - ShouldPrintValueObject()) { - m_stream->PutCString("{...}\n"); - } else + // #1# + if (print_oneline) { + m_stream->PutChar(' '); + PrintChildrenOneLiner(false); m_stream->EOL(); + } else + PrintChildren(value_printed, summary_printed, curr_ptr_depth); } +// @mulle-lldb@ << rewritten function to not hit DataVisualization::ShouldPrintAsOneLiner bool ValueObjectPrinter::ShouldPrintValidation() { return m_options.m_run_validator; diff --git a/source/Expression/ExpressionSourceCode.cpp b/source/Expression/ExpressionSourceCode.cpp index 03b2d26a25..132f277e40 100644 --- a/source/Expression/ExpressionSourceCode.cpp +++ b/source/Expression/ExpressionSourceCode.cpp @@ -278,6 +278,22 @@ bool ExpressionSourceCode::GetText(std::string &text, debug_macros_stream.GetData(), g_expression_prefix, target_specific_defines, m_prefix.c_str()); + /// @mulle-objc@ hack some mulle-objc-runtime stuff into the expression > + /// @mulle-objc@ MUST CHANGE VALUES FOR EACH LLDB RELEASE!! + wrap_stream.Printf("\ +static const struct mulle_clang_objccompilerinfo\n\ +{\n\ + unsigned int load_version;\n\ + unsigned int runtime_version;\n\ +} __mulle_objc_objccompilerinfo =\n\ +{\n\ + 14, // load version must match \n\ + 0 // 0 to not emit __load_mulle_objc\n\ +};\n\ +"); + + /// @mulle-objc@ hack some mulle-objc-runtime stuff into the expression < + // First construct a tagged form of the user expression so we can find it // later: std::string tagged_body; diff --git a/source/Plugins/ExpressionParser/Clang/ClangExpressionParser.cpp b/source/Plugins/ExpressionParser/Clang/ClangExpressionParser.cpp index 6650c0db96..f434251986 100644 --- a/source/Plugins/ExpressionParser/Clang/ClangExpressionParser.cpp +++ b/source/Plugins/ExpressionParser/Clang/ClangExpressionParser.cpp @@ -436,8 +436,20 @@ ClangExpressionParser::ClangExpressionParser(ExecutionContextScope *exe_scope, m_compiler->getLangOpts().ObjCRuntime.set(ObjCRuntime::MacOSX, VersionTuple(10, 7)); else - m_compiler->getLangOpts().ObjCRuntime.set(ObjCRuntime::FragileMacOSX, - VersionTuple(10, 7)); +/// @mulle-lldb@ change to Mulle runtime > + { + m_compiler->getLangOpts().ObjCRuntime.set(ObjCRuntime::Mulle, + VersionTuple(0, 12)); + // tagged pointers are not good for JIT, but I don't know exactly why yet + // m_compiler->getLangOpts().ObjCDisableTaggedPointers = true; + + // turning off C++ is not good, because internally lldb assumes it + // compiles everything with C++ and then wants really C and puts "extern C" + // in front of it + // m_compiler->getLangOpts().CPlusPlus = false; + // m_compiler->getLangOpts().CPlusPlus11 = false; + } +/// @mulle-lldb@ change to Mulle runtime < if (process_sp->GetObjCLanguageRuntime()->HasNewLiteralsAndIndexing()) m_compiler->getLangOpts().DebuggerObjCLiteral = true; diff --git a/source/Plugins/ExpressionParser/Clang/IRForTarget.cpp b/source/Plugins/ExpressionParser/Clang/IRForTarget.cpp index 3a7cd58b70..02e668b4a5 100644 --- a/source/Plugins/ExpressionParser/Clang/IRForTarget.cpp +++ b/source/Plugins/ExpressionParser/Clang/IRForTarget.cpp @@ -430,8 +430,10 @@ bool IRForTarget::RewriteObjCConstString(llvm::GlobalVariable *ns_str, if (!m_CFStringCreateWithBytes) { lldb::addr_t CFStringCreateWithBytes_addr; +/// @mulle-lldb@ use a different named function > static lldb_private::ConstString g_CFStringCreateWithBytes_str( - "CFStringCreateWithBytes"); + "mulle_objc_lldb_create_staticstring"); +/// @mulle-lldb@ use a different named function < CFStringCreateWithBytes_addr = m_execution_unit.FindSymbol(g_CFStringCreateWithBytes_str); @@ -446,9 +448,11 @@ bool IRForTarget::RewriteObjCConstString(llvm::GlobalVariable *ns_str, return false; } +/// @mulle-lldb@ use a different named function > if (log) - log->Printf("Found CFStringCreateWithBytes at 0x%" PRIx64, + log->Printf("Found mulle_objc_lldb_create_staticstring at 0x%" PRIx64, CFStringCreateWithBytes_addr); +/// @mulle-lldb@ use a different named function < // Build the function type: // @@ -534,7 +538,9 @@ bool IRForTarget::RewriteObjCConstString(llvm::GlobalVariable *ns_str, [this, &CFSCWB_arguments](llvm::Function *function) -> llvm::Value * { return CallInst::Create( m_CFStringCreateWithBytes, CFSCWB_arguments, - "CFStringCreateWithBytes", +/// @mulle-lldb@ use a different named function > + "mulle_objc_lldb_create_staticstring", +/// @mulle-lldb@ use a different named function < llvm::cast( m_entry_instruction_finder.GetValue(function))); }); @@ -552,11 +558,14 @@ bool IRForTarget::RewriteObjCConstString(llvm::GlobalVariable *ns_str, return false; } - ns_str->eraseFromParent(); +/// @mulle-lldb@ just keep the original in there > +/// ns_str->eraseFromParent(); +/// @mulle-lldb@ just keep the original in there < return true; } + bool IRForTarget::RewriteObjCConstStrings() { lldb_private::Log *log( lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS)); @@ -569,23 +578,26 @@ bool IRForTarget::RewriteObjCConstStrings() { std::string value_name = vi->first().str(); const char *value_name_cstr = value_name.c_str(); - if (strstr(value_name_cstr, "_unnamed_cfstring_")) { - Value *nsstring_value = vi->second; - - GlobalVariable *nsstring_global = - dyn_cast(nsstring_value); +/// @mulle-lldb@ search for header named __unnamed_nsstring > + if (strstr(value_name_cstr, "__unnamed_nsstring")) { + Value *nsheader_value = vi->second; - if (!nsstring_global) { + GlobalVariable *nsheader_global = + dyn_cast(nsheader_value); + if (!nsheader_global) { if (log) - log->PutCString("NSString variable is not a GlobalVariable"); + log->PutCString("NSString header is not a GlobalVariable"); m_error_stream.Printf("Internal error [IRForTarget]: An Objective-C " - "constant string is not a global variable\n"); + "constant string header is not a global variable\n"); return false; - } + } +/// @mulle-lldb@ search for header named __unnamed_nsstring < - if (!nsstring_global->hasInitializer()) { +/// @mulle-lldb@ actually use header here > + if (!nsheader_global->hasInitializer()) { +/// @mulle-lldb@ actually use header here < if (log) log->PutCString("NSString variable does not have an initializer"); @@ -594,9 +606,11 @@ bool IRForTarget::RewriteObjCConstStrings() { return false; } - + +/// @mulle-lldb@ actually use header here > ConstantStruct *nsstring_struct = - dyn_cast(nsstring_global->getInitializer()); + dyn_cast(nsheader_global->getInitializer()); +/// @mulle-lldb@ actually use header here < if (!nsstring_struct) { if (log) @@ -736,6 +750,21 @@ bool IRForTarget::RewriteObjCConstStrings() { if (!cstr_array) cstr_global = NULL; +/// @mulle-lldb@ use actual string for substituion > + GlobalVariable *nsstring_global; + + nsstring_global = (GlobalVariable *) m_module->getNamedAlias( &value_name_cstr[ 1]); + if (!nsstring_global) { + if (log) + log->PutCString("NSString for header not found"); + + m_error_stream.Printf("Internal error [IRForTarget]: An Objective-C " + "constant string header is not a global variable\n"); + + return false; + } +/// @mulle-lldb@ use actual string for substituion < + if (!RewriteObjCConstString(nsstring_global, cstr_global)) { if (log) log->PutCString("Error rewriting the constant string"); diff --git a/source/Plugins/Language/ObjC/CMakeLists.txt b/source/Plugins/Language/ObjC/CMakeLists.txt index afb68d4de8..ad05cf478f 100644 --- a/source/Plugins/Language/ObjC/CMakeLists.txt +++ b/source/Plugins/Language/ObjC/CMakeLists.txt @@ -30,6 +30,9 @@ add_lldb_library(lldbPluginObjCLanguage PLUGIN lldbSymbol lldbTarget lldbUtility +# @mulle-objc@ > + lldbPluginMulleObjCRuntime +# @mulle-objc@ < lldbPluginAppleObjCRuntime lldbPluginClangCommon diff --git a/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntime.cpp b/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntime.cpp index ed47b481a8..a4172ae2eb 100644 --- a/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntime.cpp +++ b/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntime.cpp @@ -338,6 +338,7 @@ bool AppleObjCRuntime::ReadObjCLibrary(const ModuleSP &module_sp) { } ThreadPlanSP AppleObjCRuntime::GetStepThroughTrampolinePlan(Thread &thread, + StackID &return_stack_id, bool stop_others) { ThreadPlanSP thread_plan_sp; if (m_objc_trampoline_handler_ap.get()) diff --git a/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntime.h b/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntime.h index 8660646001..53ca37f171 100644 --- a/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntime.h +++ b/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntime.h @@ -67,6 +67,7 @@ class AppleObjCRuntime : public lldb_private::ObjCLanguageRuntime { bool HasReadObjCLibrary() override { return m_read_objc_library; } lldb::ThreadPlanSP GetStepThroughTrampolinePlan(Thread &thread, + StackID &return_stack_id, bool stop_others) override; // Get the "libobjc.A.dylib" module from the current target if we can find diff --git a/source/Plugins/LanguageRuntime/ObjC/CMakeLists.txt b/source/Plugins/LanguageRuntime/ObjC/CMakeLists.txt index af13dc6a14..3bc649265a 100644 --- a/source/Plugins/LanguageRuntime/ObjC/CMakeLists.txt +++ b/source/Plugins/LanguageRuntime/ObjC/CMakeLists.txt @@ -1 +1,4 @@ add_subdirectory(AppleObjCRuntime) +# @mulle-lldb@ > +add_subdirectory(MulleObjCRuntime) +# @mulle-lldb@ < diff --git a/source/Plugins/LanguageRuntime/ObjC/MulleObjCRuntime/CMakeLists.txt b/source/Plugins/LanguageRuntime/ObjC/MulleObjCRuntime/CMakeLists.txt new file mode 100644 index 0000000000..3f8b64c35c --- /dev/null +++ b/source/Plugins/LanguageRuntime/ObjC/MulleObjCRuntime/CMakeLists.txt @@ -0,0 +1,8 @@ +add_lldb_library(lldbPluginMulleObjCRuntime + MulleObjCRuntime.cpp + MulleObjCRuntimeV1.cpp + MulleObjCTrampolineHandler.cpp +# MulleObjCDeclVendor.cpp + MulleThreadPlanStepThroughObjCTrampoline.cpp +# MulleObjCTypeEncodingParser.cpp + ) diff --git a/source/Plugins/LanguageRuntime/ObjC/MulleObjCRuntime/Makefile b/source/Plugins/LanguageRuntime/ObjC/MulleObjCRuntime/Makefile new file mode 100644 index 0000000000..66ddfc4a29 --- /dev/null +++ b/source/Plugins/LanguageRuntime/ObjC/MulleObjCRuntime/Makefile @@ -0,0 +1,11 @@ +%.inc : %.src + ./make-inc.sh $< $@ + + +.PHONY: ALL + +ALL: mulle-objc-lookup-imp.inc \ + mulle-objc-object-checker.inc \ + mulle-objc-dangerous-class-storage.inc + + diff --git a/source/Plugins/LanguageRuntime/ObjC/MulleObjCRuntime/MulleObjCRuntime.cpp b/source/Plugins/LanguageRuntime/ObjC/MulleObjCRuntime/MulleObjCRuntime.cpp new file mode 100644 index 0000000000..e5ba49ed22 --- /dev/null +++ b/source/Plugins/LanguageRuntime/ObjC/MulleObjCRuntime/MulleObjCRuntime.cpp @@ -0,0 +1,527 @@ +//===-- MulleObjCRuntime.cpp -------------------------------------*- C++ +//-*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "MulleObjCRuntime.h" +#include "MulleObjCTrampolineHandler.h" + +#include "clang/AST/Type.h" + +#include "lldb/Breakpoint/BreakpointLocation.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/ModuleList.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Core/Scalar.h" +#include "lldb/Core/Section.h" +#include "lldb/Core/ValueObject.h" +#include "lldb/Expression/DiagnosticManager.h" +#include "lldb/Expression/FunctionCaller.h" +#include "lldb/Symbol/ClangASTContext.h" +#include "lldb/Symbol/ObjectFile.h" +#include "lldb/Target/ExecutionContext.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/RegisterContext.h" +#include "lldb/Target/StopInfo.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/Thread.h" +#include "lldb/Utility/ConstString.h" +#include "lldb/Utility/Log.h" +#include "lldb/Utility/Status.h" +#include "lldb/Utility/StreamString.h" + +#define MULLE_LOG LIBLLDB_LOG_LANGUAGE + +#include + +using namespace lldb; +using namespace lldb_private; + +static constexpr std::chrono::seconds g_po_function_timeout(15); + +MulleObjCRuntime::~MulleObjCRuntime() {} + +MulleObjCRuntime::MulleObjCRuntime(Process *process) + : ObjCLanguageRuntime(process), m_read_objc_library(false), + m_objc_trampoline_handler_ap(), m_Foundation_major() { + ReadObjCLibraryIfNeeded(process->GetTarget().GetImages()); +} + +bool MulleObjCRuntime::GetObjectDescription(Stream &str, ValueObject &valobj) { + CompilerType compiler_type(valobj.GetCompilerType()); + bool is_signed; + // ObjC objects can only be pointers (or numbers that actually represents + // pointers + // but haven't been typecast, because reasons..) + if (!compiler_type.IsIntegerType(is_signed) && !compiler_type.IsPointerType()) + return false; + + // Make the argument list: we pass one arg, the address of our pointer, to the + // print function. + Value val; + + if (!valobj.ResolveValue(val.GetScalar())) + return false; + + // Value Objects may not have a process in their ExecutionContextRef. But we + // need to have one + // in the ref we pass down to eventually call description. Get it from the + // target if it isn't + // present. + ExecutionContext exe_ctx; + if (valobj.GetProcessSP()) { + exe_ctx = ExecutionContext(valobj.GetExecutionContextRef()); + } else { + exe_ctx.SetContext(valobj.GetTargetSP(), true); + if (!exe_ctx.HasProcessScope()) + return false; + } + return GetObjectDescription(str, val, exe_ctx.GetBestExecutionContextScope()); +} +bool MulleObjCRuntime::GetObjectDescription(Stream &strm, Value &value, + ExecutionContextScope *exe_scope) { + if (!m_read_objc_library) + return false; + + ExecutionContext exe_ctx; + exe_scope->CalculateExecutionContext(exe_ctx); + Process *process = exe_ctx.GetProcessPtr(); + if (!process) + return false; + + // We need other parts of the exe_ctx, but the processes have to match. + assert(m_process == process); + + // Get the function address for the print function. + const Address *function_address = GetPrintForDebuggerAddr(); + if (!function_address) + return false; + + Target *target = exe_ctx.GetTargetPtr(); + CompilerType compiler_type = value.GetCompilerType(); + if (compiler_type) { + if (!ClangASTContext::IsObjCObjectPointerType(compiler_type)) { + strm.Printf("Value doesn't point to an ObjC object.\n"); + return false; + } + } else { + // If it is not a pointer, see if we can make it into a pointer. + ClangASTContext *ast_context = target->GetScratchClangASTContext(); + CompilerType opaque_type = ast_context->GetBasicType(eBasicTypeObjCID); + if (!opaque_type) + opaque_type = ast_context->GetBasicType(eBasicTypeVoid).GetPointerType(); + // value.SetContext(Value::eContextTypeClangType, opaque_type_ptr); + value.SetCompilerType(opaque_type); + } + + ValueList arg_value_list; + arg_value_list.PushValue(value); + + // This is the return value: + ClangASTContext *ast_context = target->GetScratchClangASTContext(); + + CompilerType return_compiler_type = ast_context->GetCStringType(true); + Value ret; + // ret.SetContext(Value::eContextTypeClangType, return_compiler_type); + ret.SetCompilerType(return_compiler_type); + + if (exe_ctx.GetFramePtr() == NULL) { + Thread *thread = exe_ctx.GetThreadPtr(); + if (thread == NULL) { + exe_ctx.SetThreadSP(process->GetThreadList().GetSelectedThread()); + thread = exe_ctx.GetThreadPtr(); + } + if (thread) { + exe_ctx.SetFrameSP(thread->GetSelectedFrame()); + } + } + + // Now we're ready to call the function: + + DiagnosticManager diagnostics; + lldb::addr_t wrapper_struct_addr = LLDB_INVALID_ADDRESS; + + if (!m_print_object_caller_up) { + Status error; + m_print_object_caller_up.reset( + exe_scope->CalculateTarget()->GetFunctionCallerForLanguage( + eLanguageTypeObjC, return_compiler_type, *function_address, + arg_value_list, "objc-object-description", error)); + if (error.Fail()) { + m_print_object_caller_up.reset(); + strm.Printf("Could not get function runner to call print for debugger " + "function: %s.", + error.AsCString()); + return false; + } + m_print_object_caller_up->InsertFunction(exe_ctx, wrapper_struct_addr, + diagnostics); + } else { + m_print_object_caller_up->WriteFunctionArguments( + exe_ctx, wrapper_struct_addr, arg_value_list, diagnostics); + } + + EvaluateExpressionOptions options; + options.SetUnwindOnError(true); + options.SetTryAllThreads(true); + options.SetStopOthers(true); + options.SetIgnoreBreakpoints(true); + options.SetTimeout(g_po_function_timeout); + + ExpressionResults results = m_print_object_caller_up->ExecuteFunction( + exe_ctx, &wrapper_struct_addr, options, diagnostics, ret); + if (results != eExpressionCompleted) { + strm.Printf("Error evaluating Print Object function: %d.\n", results); + return false; + } + + addr_t result_ptr = ret.GetScalar().ULongLong(LLDB_INVALID_ADDRESS); + + char buf[512]; + size_t cstr_len = 0; + size_t full_buffer_len = sizeof(buf) - 1; + size_t curr_len = full_buffer_len; + while (curr_len == full_buffer_len) { + Status error; + curr_len = process->ReadCStringFromMemory(result_ptr + cstr_len, buf, + sizeof(buf), error); + strm.Write(buf, curr_len); + cstr_len += curr_len; + } + return cstr_len > 0; +} + +// why this again ??? +lldb::ModuleSP MulleObjCRuntime::GetMulleObjCRuntimeModule() { + ModuleSP module_sp(m_objc_module_wp.lock()); + if (module_sp) + return module_sp; + + Process *process = GetProcess(); + if (process) { + const ModuleList &modules = process->GetTarget().GetImages(); + for (uint32_t idx = 0; idx < modules.GetSize(); idx++) { + module_sp = modules.GetModuleAtIndex(idx); + if (MulleObjCRuntime::IsMulleObjCRuntimeModule(module_sp)) { + m_objc_module_wp = module_sp; + return module_sp; + } + } + } + return ModuleSP(); +} + +Address *MulleObjCRuntime::GetPrintForDebuggerAddr() { + if (!m_PrintForDebugger_addr.get()) { + const ModuleList &modules = m_process->GetTarget().GetImages(); + + SymbolContextList contexts; + SymbolContext context; + + if ((!modules.FindSymbolsWithNameAndType(ConstString("_NSPrintForDebugger"), + eSymbolTypeCode, contexts)) && + (!modules.FindSymbolsWithNameAndType(ConstString("_CFPrintForDebugger"), + eSymbolTypeCode, contexts))) + return NULL; + + contexts.GetContextAtIndex(0, context); + + m_PrintForDebugger_addr.reset(new Address(context.symbol->GetAddress())); + } + + return m_PrintForDebugger_addr.get(); +} + +bool MulleObjCRuntime::CouldHaveDynamicValue(ValueObject &in_value) { + return in_value.GetCompilerType().IsPossibleDynamicType( + NULL, + false, // do not check C++ + true); // check ObjC +} + +bool MulleObjCRuntime::GetDynamicTypeAndAddress( + ValueObject &in_value, lldb::DynamicValueType use_dynamic, + TypeAndOrName &class_type_or_name, Address &address, + Value::ValueType &value_type) { + return false; +} + +TypeAndOrName +MulleObjCRuntime::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; +} + +bool MulleObjCRuntime::IsMulleObjCRuntimeModule(const ModuleSP &module_sp) { + if (! module_sp) + return false; + + SymbolContextList contexts; + + // + // if mulle_objc_lldb_lookup_implementation then this module + // contains the mulle_objc_runtime lldb code + // + if( module_sp->FindSymbolsWithNameAndType( + ConstString( "mulle_objc_lldb_lookup_implementation"), + eSymbolTypeCode, contexts)) + { + //fprintf( stderr, "MulleObjC runtime IN DA HOUSE at \"%s\"!!\n", + // module_sp->GetFileSpec().GetFilename().AsCString()); + return true; + } + return false; +} + + +static bool _IsSymbolARuntimeThunk(const Symbol &symbol) { + +// *** object calls *** + + static ConstString g_c_01 = ConstString( "mulle_objc_object_call"); + static ConstString g_c_02 = ConstString( "mulle_objc_object_inlinecall"); + static ConstString g_c_03 = ConstString( "mulle_objc_object_partialinlinecall"); + static ConstString g_c_04 = ConstString( "mulle_objc_object_inlinecall_variablemethodid"); + +// *** objects calls *** + + static ConstString g_c_05 = ConstString( "mulle_objc_objects_call"); + +// *** internal calls *** + + static ConstString g_c_06 = ConstString( "_mulle_objc_object_call2"); + static ConstString g_c_07 = ConstString( "_mulle_objc_object_call2_emptycache"); + static ConstString g_c_08 = ConstString( "_mulle_objc_object_call2_needcache"); + static ConstString g_c_09 = ConstString( "_mulle_objc_object_call_class"); + static ConstString g_c_10 = ConstString( "_mulle_objc_object_call_class_needcache"); + static ConstString g_c_11 = ConstString( "_mulle_objc_object_call_class_nofail"); + static ConstString g_c_12 = ConstString( "_mulle_objc_object_call_class_nocache"); + + +// *** super calls *** + static ConstString g_c_13 = ConstString( "_mulle_objc_object_supercall"); + static ConstString g_c_14 = ConstString( "_mulle_objc_object_inlinesupercall"); + static ConstString g_c_15 = ConstString( "_mulle_objc_object_partialinlinesupercall"); + + + + ConstString symbol_name = symbol.GetName(); + + if( ConstString::Equals( symbol_name, g_c_01, true)) + return( true); + if( ConstString::Equals( symbol_name, g_c_02, true)) + return( true); + if( ConstString::Equals( symbol_name, g_c_03, true)) + return( true); + if( ConstString::Equals( symbol_name, g_c_04, true)) + return( true); + if( ConstString::Equals( symbol_name, g_c_05, true)) + return( true); + if( ConstString::Equals( symbol_name, g_c_06, true)) + return( true); + if( ConstString::Equals( symbol_name, g_c_07, true)) + return( true); + if( ConstString::Equals( symbol_name, g_c_08, true)) + return( true); + if( ConstString::Equals( symbol_name, g_c_09, true)) + return( true); + if( ConstString::Equals( symbol_name, g_c_10, true)) + return( true); + if( ConstString::Equals( symbol_name, g_c_11, true)) + return( true); + if( ConstString::Equals( symbol_name, g_c_12, true)) + return( true); + if( ConstString::Equals( symbol_name, g_c_13, true)) + return( true); + if( ConstString::Equals( symbol_name, g_c_14, true)) + return( true); + if( ConstString::Equals( symbol_name, g_c_15, true)) + return( true); + + return( false); +} + + +bool MulleObjCRuntime::IsSymbolARuntimeThunk(const Symbol &symbol) { + + bool flag; + + Log *log(lldb_private::GetLogIfAllCategoriesSet(MULLE_LOG)); + + flag = _IsSymbolARuntimeThunk( symbol); + if( log) + log->Printf( "\"%s\" is %s thunk", symbol.GetName().GetCString(), + flag ? "a" : "not a"); + return( flag); +} + + +void MulleObjCRuntime::GetValuesForGlobalCFBooleans(lldb::addr_t &cf_true, + lldb::addr_t &cf_false) { + cf_true = cf_false = LLDB_INVALID_ADDRESS; +} + +bool MulleObjCRuntime::IsModuleObjCLibrary(const ModuleSP &module_sp) { + return IsMulleObjCRuntimeModule(module_sp); +} + +bool MulleObjCRuntime::ReadObjCLibrary(const ModuleSP &module_sp) { + // Maybe check here and if we have a handler already, and the UUID of this + // module is the same as the one in the + // current module, then we don't have to reread it? + m_objc_trampoline_handler_ap.reset( + new MulleObjCTrampolineHandler(m_process->shared_from_this(), module_sp)); + if (m_objc_trampoline_handler_ap.get() != NULL && m_objc_trampoline_handler_ap.get()->CanStepThrough()) { + m_read_objc_library = true; + // fprintf( stderr, "ReadObjCLibrary succeeds\n"); + return true; + } + + // fprintf( stderr, "ReadObjCLibrary fails\n"); + m_read_objc_library = false; // pedantically reset + return false; +} + +ThreadPlanSP MulleObjCRuntime::GetStepThroughTrampolinePlan(Thread &thread, + StackID &return_stack_id, + bool stop_others) { + ThreadPlanSP thread_plan_sp; + + if (m_objc_trampoline_handler_ap.get()) + thread_plan_sp = m_objc_trampoline_handler_ap->GetStepThroughDispatchPlan( + thread, return_stack_id, stop_others); + return thread_plan_sp; +} + +//------------------------------------------------------------------ +// Static Functions +//------------------------------------------------------------------ +ObjCLanguageRuntime::ObjCRuntimeVersions +MulleObjCRuntime::GetObjCVersion(Process *process, ModuleSP &objc_module_sp) { + if (!process) + return ObjCRuntimeVersions::eObjC_VersionUnknown; + + Target &target = process->GetTarget(); + + const ModuleList &target_modules = target.GetImages(); + std::lock_guard gaurd(target_modules.GetMutex()); + + size_t num_images = target_modules.GetSize(); + for (size_t i = 0; i < num_images; i++) { + ModuleSP module_sp = target_modules.GetModuleAtIndexUnlocked(i); + + // isLoadedInTarget doesn't work for me, since we get called + // _while_ loading apparently so main f.e. will be found as + // a symbol, but the module itself is not loaded yet + // (or so it seems, as we are statically linking ?) + + if ( // module_sp->IsLoadedInTarget(&target) && + IsMulleObjCRuntimeModule(module_sp) + ) { + return ObjCRuntimeVersions::eMulleObjC_V1; + } + } + + return ObjCRuntimeVersions::eObjC_VersionUnknown; +} + +void MulleObjCRuntime::SetExceptionBreakpoints() { + const bool catch_bp = false; + const bool throw_bp = true; + const bool is_internal = true; + + if (!m_objc_exception_bp_sp) { + m_objc_exception_bp_sp = LanguageRuntime::CreateExceptionBreakpoint( + m_process->GetTarget(), GetLanguageType(), catch_bp, throw_bp, + is_internal); + if (m_objc_exception_bp_sp) + m_objc_exception_bp_sp->SetBreakpointKind("ObjC exception"); + } else + m_objc_exception_bp_sp->SetEnabled(true); +} + +void MulleObjCRuntime::ClearExceptionBreakpoints() { + if (!m_process) + return; + + if (m_objc_exception_bp_sp.get()) { + m_objc_exception_bp_sp->SetEnabled(false); + } +} + +bool MulleObjCRuntime::ExceptionBreakpointsAreSet() { + return m_objc_exception_bp_sp && m_objc_exception_bp_sp->IsEnabled(); +} + +bool MulleObjCRuntime::ExceptionBreakpointsExplainStop( + lldb::StopInfoSP stop_reason) { + if (!m_process) + return false; + + if (!stop_reason || stop_reason->GetStopReason() != eStopReasonBreakpoint) + return false; + + uint64_t break_site_id = stop_reason->GetValue(); + return m_process->GetBreakpointSiteList().BreakpointSiteContainsBreakpoint( + break_site_id, m_objc_exception_bp_sp->GetID()); +} + +bool MulleObjCRuntime::CalculateHasNewLiteralsAndIndexing() { + return false; +} + +lldb::SearchFilterSP MulleObjCRuntime::CreateExceptionSearchFilter() { + return LanguageRuntime::CreateExceptionSearchFilter(); +} + +void MulleObjCRuntime::ReadObjCLibraryIfNeeded(const ModuleList &module_list) { + // it seems, that when you run again ModulesDidLoad gets called + // and this "caching" fcks things up + //if (!HasReadObjCLibrary()) + { + std::lock_guard 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; + } + } + } +} + +void MulleObjCRuntime::ModulesDidLoad(const ModuleList &module_list) { + ReadObjCLibraryIfNeeded(module_list); +} diff --git a/source/Plugins/LanguageRuntime/ObjC/MulleObjCRuntime/MulleObjCRuntime.h b/source/Plugins/LanguageRuntime/ObjC/MulleObjCRuntime/MulleObjCRuntime.h new file mode 100644 index 0000000000..669a6459a9 --- /dev/null +++ b/source/Plugins/LanguageRuntime/ObjC/MulleObjCRuntime/MulleObjCRuntime.h @@ -0,0 +1,129 @@ +//===-- MulleObjCRuntime.h --------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_MulleObjCRuntime_h_ +#define liblldb_MulleObjCRuntime_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +#include "llvm/ADT/Optional.h" + +// Project includes +#include "MulleObjCTrampolineHandler.h" +#include "MulleThreadPlanStepThroughObjCTrampoline.h" +#include "lldb/Target/LanguageRuntime.h" +#include "lldb/Target/ObjCLanguageRuntime.h" +#include "lldb/lldb-private.h" + +namespace lldb_private { + +class MulleObjCRuntime : public lldb_private::ObjCLanguageRuntime { +public: + ~MulleObjCRuntime() override; + + //------------------------------------------------------------------ + // Static Functions + //------------------------------------------------------------------ + // Note there is no CreateInstance, Initialize & Terminate functions here, + // because + // you can't make an instance of this generic runtime. + + static bool classof(const ObjCLanguageRuntime *runtime) { + switch (runtime->GetRuntimeVersion()) { + case ObjCRuntimeVersions::eMulleObjC_V1: + return true; + default: + return false; + } + } + + // These are generic runtime functions: + 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; + + // These are the ObjC specific functions. + + bool IsModuleObjCLibrary(const lldb::ModuleSP &module_sp) override; + + bool ReadObjCLibrary(const lldb::ModuleSP &module_sp) override; + + bool HasReadObjCLibrary() override { return m_read_objc_library; } + + lldb::ThreadPlanSP GetStepThroughTrampolinePlan(Thread &thread, + StackID &return_stack_id, + bool stop_others) override; + + // Get the "libobjc.A.dylib" module from the current target if we can find + // it, also cache it once it is found to ensure quick lookups. + lldb::ModuleSP GetMulleObjCRuntimeModule(); + + bool IsSymbolARuntimeThunk(const Symbol &symbol) override; + + // Sync up with the target + + void ModulesDidLoad(const ModuleList &module_list) override; + + void SetExceptionBreakpoints() override; + + void ClearExceptionBreakpoints() override; + + bool ExceptionBreakpointsAreSet() override; + + bool ExceptionBreakpointsExplainStop(lldb::StopInfoSP stop_reason) override; + + lldb::SearchFilterSP CreateExceptionSearchFilter() override; + + uint32_t GetFoundationVersion(); + + virtual void GetValuesForGlobalCFBooleans(lldb::addr_t &cf_true, + lldb::addr_t &cf_false); + +protected: + // Call CreateInstance instead. + MulleObjCRuntime(Process *process); + + bool CalculateHasNewLiteralsAndIndexing() override; + + static bool IsMulleObjCRuntimeModule(const lldb::ModuleSP &module_sp); + + static ObjCRuntimeVersions GetObjCVersion(Process *process, + lldb::ModuleSP &objc_module_sp); + + void ReadObjCLibraryIfNeeded(const ModuleList &module_list); + + Address *GetPrintForDebuggerAddr(); + + std::unique_ptr
m_PrintForDebugger_addr; + bool m_read_objc_library; + std::unique_ptr + m_objc_trampoline_handler_ap; + lldb::BreakpointSP m_objc_exception_bp_sp; + lldb::ModuleWP m_objc_module_wp; + std::unique_ptr m_print_object_caller_up; + + llvm::Optional m_Foundation_major; +}; + +} // namespace lldb_private + +#endif // liblldb_MulleObjCRuntime_h_ diff --git a/source/Plugins/LanguageRuntime/ObjC/MulleObjCRuntime/MulleObjCRuntimeV1.cpp b/source/Plugins/LanguageRuntime/ObjC/MulleObjCRuntime/MulleObjCRuntimeV1.cpp new file mode 100644 index 0000000000..0a30755a15 --- /dev/null +++ b/source/Plugins/LanguageRuntime/ObjC/MulleObjCRuntime/MulleObjCRuntimeV1.cpp @@ -0,0 +1,534 @@ +//===-- MulleObjCRuntimeV1.cpp --------------------------------------*- C++ +//-*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "MulleObjCRuntimeV1.h" +#include "MulleObjCTrampolineHandler.h" + +#include "clang/AST/Type.h" + +#include "lldb/Breakpoint/BreakpointLocation.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Core/Scalar.h" +#include "lldb/Expression/DiagnosticManager.h" +#include "lldb/Expression/FunctionCaller.h" +#include "lldb/Expression/UtilityFunction.h" +#include "lldb/Symbol/ClangASTContext.h" +#include "lldb/Symbol/Symbol.h" +#include "lldb/Target/ExecutionContext.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/RegisterContext.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/Thread.h" +#include "lldb/Utility/ConstString.h" +#include "lldb/Utility/Log.h" +#include "lldb/Utility/Status.h" +#include "lldb/Utility/StreamString.h" + +#include + +using namespace lldb; +using namespace lldb_private; + + +static const char *g_dangerous_function_name = +"__lldb_objc_get_dangerous_class_storage"; + +static const char *g_dangerous_function_code = + +#include "mulle-objc-dangerous-class-storage.inc" + +; + + +MulleObjCRuntimeV1::MulleObjCRuntimeV1(Process *process) + : MulleObjCRuntime(process), m_hash_signature(), + m_isa_hash_table_ptr(LLDB_INVALID_ADDRESS) {} + +// for V1 runtime we just try to return a class name as that is the minimum +// level of support +// required for the data formatters to work +bool MulleObjCRuntimeV1::GetDynamicTypeAndAddress( + ValueObject &in_value, lldb::DynamicValueType use_dynamic, + TypeAndOrName &class_type_or_name, Address &address, + Value::ValueType &value_type) { + class_type_or_name.Clear(); + value_type = Value::ValueType::eValueTypeScalar; + if (CouldHaveDynamicValue(in_value)) { + auto class_descriptor(GetClassDescriptor(in_value)); + if (class_descriptor && class_descriptor->IsValid() && + class_descriptor->GetClassName()) { + const addr_t object_ptr = in_value.GetPointerValue(); + address.SetRawAddress(object_ptr); + class_type_or_name.SetName(class_descriptor->GetClassName()); + } + } + return class_type_or_name.IsEmpty() == false; +} + +//------------------------------------------------------------------ +// Static Functions +//------------------------------------------------------------------ +lldb_private::LanguageRuntime * +MulleObjCRuntimeV1::CreateInstance(Process *process, + lldb::LanguageType language) { + if (language == eLanguageTypeObjC) { + ModuleSP objc_module_sp; + + if (MulleObjCRuntime::GetObjCVersion(process, objc_module_sp) == + ObjCRuntimeVersions::eMulleObjC_V1) + return new MulleObjCRuntimeV1(process); + else + return NULL; + } else + return NULL; +} + +void MulleObjCRuntimeV1::Initialize() { + PluginManager::RegisterPlugin( + GetPluginNameStatic(), "Mulle Objective C Language Runtime - Version 1", + CreateInstance); +} + +void MulleObjCRuntimeV1::Terminate() { + PluginManager::UnregisterPlugin(CreateInstance); +} + +lldb_private::ConstString MulleObjCRuntimeV1::GetPluginNameStatic() { + static ConstString g_name("mulle-objc-v1"); + return g_name; +} + +//------------------------------------------------------------------ +// PluginInterface protocol +//------------------------------------------------------------------ +ConstString MulleObjCRuntimeV1::GetPluginName() { + return GetPluginNameStatic(); +} + +uint32_t MulleObjCRuntimeV1::GetPluginVersion() { return 1; } + +BreakpointResolverSP +MulleObjCRuntimeV1::CreateExceptionResolver(Breakpoint *bkpt, bool catch_bp, + bool throw_bp) { + BreakpointResolverSP resolver_sp; + + if (throw_bp) + resolver_sp.reset(new BreakpointResolverName( + bkpt, "mulle_objc_exception_throw", eFunctionNameTypeBase, + eLanguageTypeUnknown, Breakpoint::Exact, 0, eLazyBoolNo)); + // FIXME: don't do catch yet. + return resolver_sp; +} + +struct BufStruct { + char contents[2048]; +}; + +#ifndef NDEBUG +static char object_checker_c[] = + +#include "mulle-objc-object-checker.inc" + +; +#endif + + +UtilityFunction *MulleObjCRuntimeV1::CreateObjectChecker(const char *name) { + std::unique_ptr buf(new BufStruct); + +#ifndef NDEBUG + int strformatsize = snprintf(&buf->contents[0], sizeof(buf->contents), + object_checker_c, + name); + assert(strformatsize < (int)sizeof(buf->contents)); +#endif + + Status error; + return GetTargetRef().GetUtilityFunctionForLanguage( + buf->contents, eLanguageTypeObjC, name, error); +} + +MulleObjCRuntimeV1::ClassDescriptorV1::ClassDescriptorV1( + ValueObject &isa_pointer) { + Initialize(isa_pointer.GetValueAsUnsigned(0), isa_pointer.GetProcessSP()); +} + +MulleObjCRuntimeV1::ClassDescriptorV1::ClassDescriptorV1( + ObjCISA isa, lldb::ProcessSP process_sp) { + Initialize(isa, process_sp); +} + +void MulleObjCRuntimeV1::ClassDescriptorV1::Initialize(ObjCISA isa, + lldb::ProcessSP process_sp) +{ + if( ! isa || ! process_sp) + { + // fprintf( stderr, "fail isa\n"); + m_valid = false; + return; + } + + // TPS no good for now + if( isa & 0x1) + { + m_valid = false; + return; + } + + m_valid = true; + + Status error; + + uint32_t ptr_size = process_sp->GetAddressByteSize(); + + // get isa of class + m_isa = process_sp->ReadPointerFromMemory( isa - ptr_size, error); + + if (error.Fail()) + { + // fprintf( stderr, "fail m_isa 0x%llx, isa 0x%llx\n", (long long) m_isa, (long long) isa); + m_valid = false; + return; + } + + + if (!IsPointerValid(m_isa,ptr_size)) + { + // fprintf( stderr, "fail isa\n"); + m_valid = false; + return; + } + + // get superclass or what ? + m_parent_isa = process_sp->ReadPointerFromMemory( isa + 3 * ptr_size, error); + + if (error.Fail()) + { + // fprintf( stderr, "fail superclass\n"); + m_valid = false; + return; + } + + if (!IsPointerValid(m_parent_isa,ptr_size,true)) + { + // fprintf( stderr, "fail superclass 2\n"); + m_valid = false; + return; + } + + // get name + lldb::addr_t name_ptr = process_sp->ReadPointerFromMemory( isa + 4 * ptr_size, error); + + if (error.Fail()) + { + // fprintf( stderr, "fail name offset\n"); + m_valid = false; + return; + } + + lldb::DataBufferSP buffer_sp(new DataBufferHeap(1024, 0)); + + size_t count = process_sp->ReadCStringFromMemory(name_ptr, (char*)buffer_sp->GetBytes(), 1024, error); + + if (error.Fail()) + { + // fprintf( stderr, "fail name read: 0x%llx\n", (unsigned long long) name_ptr); + m_valid = false; + return; + } + + if (count) + m_name = ConstString((char*)buffer_sp->GetBytes()); + else + m_name = ConstString(); + + // get instance_and_header_size + m_instance_size = (size_t) process_sp->ReadPointerFromMemory( isa + 8 * ptr_size, error); + if (error.Fail()) + { + // fprintf( stderr, "fail instance size\n"); + m_valid = false; + return; + } + + // subtract header size + m_instance_size -= ptr_size * 2; + + // fprintf( stderr, "Add class \"%s\" for isa=0x%llx with m_isa=0x%llx, m_parent_isa=0x%llx\n", m_name.AsCString(), (long long) isa, (long long) m_isa, (long long) m_parent_isa); + + m_process_wp = lldb::ProcessWP(process_sp); +} + + +MulleObjCRuntime::ClassDescriptorSP +MulleObjCRuntimeV1::ClassDescriptorV1::GetSuperclass() { + if (!m_valid) + return MulleObjCRuntime::ClassDescriptorSP(); + ProcessSP process_sp = m_process_wp.lock(); + if (!process_sp) + return MulleObjCRuntime::ClassDescriptorSP(); + return ObjCLanguageRuntime::ClassDescriptorSP( + new MulleObjCRuntimeV1::ClassDescriptorV1(m_parent_isa, process_sp)); +} + +MulleObjCRuntime::ClassDescriptorSP +MulleObjCRuntimeV1::ClassDescriptorV1::GetMetaclass() const { + return ClassDescriptorSP(); +} + +bool MulleObjCRuntimeV1::ClassDescriptorV1::Describe( + std::function const &superclass_func, + std::function const &instance_method_func, + std::function const &class_method_func, + std::function const &ivar_func) const { + return false; +} + + +// +// how often is basically the same code copied throught lldb ? +// This gets the class table of the current universe +// +lldb::addr_t MulleObjCRuntimeV1::CallDangerousGetClassTableFunction( Process *process) { + DiagnosticManager diagnostics; + ValueList emptyList; + lldb::addr_t args_addr = LLDB_INVALID_ADDRESS; + FunctionCaller *impl_function_caller = nullptr; + ExecutionContext exe_ctx; + EvaluateExpressionOptions options; + std::unique_ptr dangerous_function; + + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_LANGUAGE)); + + options.SetUnwindOnError(true); + options.SetIgnoreBreakpoints(true); + options.SetStopOthers(true); // placebo... + + ThreadSP thread_sp = m_process->GetThreadList().GetExpressionExecutionThread(); + if (!thread_sp) + { + if (log) + { + log->Printf( "Failed to acquire expression execution thread"); + diagnostics.Dump(log); + } + return( LLDB_INVALID_ADDRESS); + } + TargetSP target_sp( thread_sp->CalculateTarget()); + thread_sp->CalculateExecutionContext(exe_ctx); + + ClangASTContext *clang_ast_context = target_sp->GetScratchClangASTContext(); + + Value return_value; + CompilerType clang_void_ptr_type = clang_ast_context->GetBasicType(eBasicTypeVoid).GetPointerType(); + return_value.SetValueType(Value::eValueTypeScalar); + return_value.SetCompilerType( clang_void_ptr_type); + + Status error; + dangerous_function.reset(exe_ctx.GetTargetRef().GetUtilityFunctionForLanguage( + g_dangerous_function_code, eLanguageTypeObjC, + g_dangerous_function_name, error)); + if (error.Fail()) { + if (log) + { + log->Printf( + "Failed to get Utility Function for class walks: %s.", + error.AsCString()); + diagnostics.Dump(log); + } + dangerous_function.reset(); + return( LLDB_INVALID_ADDRESS); + } + + if (!dangerous_function->Install(diagnostics, exe_ctx)) { + if (log) { + log->Printf("Failed to install get_dangerous_class_storage function."); + diagnostics.Dump(log); + } + dangerous_function.reset(); + return( LLDB_INVALID_ADDRESS); + } + + // Next make the runner function for our implementation utility function. + impl_function_caller = dangerous_function->MakeFunctionCaller( + clang_void_ptr_type, emptyList, thread_sp, error); + if (error.Fail()) { + if (log) + { + log->Printf( + "Error getting function caller for dispatch lookup: \"%s\".", + error.AsCString()); + diagnostics.Dump(log); + } + return( LLDB_INVALID_ADDRESS); + } + + diagnostics.Clear(); + + // Now write down the argument values for this particular call. This looks + // like it might be a race condition + // if other threads were calling into here, but actually it isn't because we + // allocate a new args structure for + // this call by passing args_addr = LLDB_INVALID_ADDRESS... + + if (!impl_function_caller->WriteFunctionArguments( + exe_ctx, args_addr, emptyList, diagnostics)) { + if (log) { + log->Printf("Error writing function arguments."); + diagnostics.Dump(log); + } + return( LLDB_INVALID_ADDRESS); + } + + + impl_function_caller = dangerous_function->GetFunctionCaller(); + // Run the function + ExpressionResults results = + impl_function_caller->ExecuteFunction( exe_ctx, + &args_addr, + options, + diagnostics, + return_value); + + if (results != eExpressionCompleted) { + if (log) { + log->Printf("Error calling dangerous mulle functions."); + diagnostics.Dump(log); + } + return 0; + } + + return( return_value.GetScalar().ULongLong()); +} + + +lldb::addr_t MulleObjCRuntimeV1::GetISAHashTablePointer( Process *process) { + ModuleSP objc_module_sp(GetMulleObjCRuntimeModule()); + + if (!objc_module_sp) + return LLDB_INVALID_ADDRESS; + + if( process->isThreadPlanLocked()) + return LLDB_INVALID_ADDRESS; + // that pointer is fluctuating! + return CallDangerousGetClassTableFunction( process); +} + +// +// sadly need to read the mulle-concurrent-hashtable like this too +// OBVIOUSLY!! not optimal +// +void MulleObjCRuntimeV1::UpdateISAToDescriptorMapIfNeeded() { + // TODO: implement HashTableSignature... + Process *process = GetProcess(); + + if (process) { + // Update the process stop ID that indicates the last time we updated the + // map, whether it was successful or not. + m_isa_to_descriptor_stop_id = process->GetStopID(); + Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_LANGUAGE)); + + ProcessSP process_sp = process->shared_from_this(); + + // reread images for Objective-C library, I don't know why + ModuleSP objc_module_sp(GetMulleObjCRuntimeModule()); + + if (!objc_module_sp) + return; + + lldb::addr_t hash_table_ptr = GetISAHashTablePointer( process); + if (hash_table_ptr == LLDB_INVALID_ADDRESS) { + m_isa_to_descriptor_stop_id = UINT32_MAX; + return; // can't do anything + } + + // Read the mulle_concurrent_hashtable struct: + // __lldb_objc_get_dangerous_class_storage gives us + // + // struct _mulle_concurrent_hashmapstorage + // { + // mulle_atomic_pointer_t n_hashs; // with possibly empty values + // uintptr_t mask; // easier to read from debugger if void * size + // + // struct _mulle_concurrent_hashvaluepair entries[ 1]; + // }; + // struct _mulle_concurrent_hashvaluepair + // { + // intptr_t hash; + // mulle_atomic_pointer_t value; + // }; + + Status error; + DataBufferHeap buffer(1024, 0); + + // assume 64 bit max for now... + // why use m_process here, process up there ? and process_sp ??? + + const uint32_t addr_size = m_process->GetAddressByteSize(); + const ByteOrder byte_order = m_process->GetByteOrder(); + lldb::addr_t invalid; + + switch( addr_size) + { + case 4 : invalid = (lldb::addr_t) INT32_MIN; break; + case 8 : invalid = (lldb::addr_t) INT64_MIN; break; + default : return; + } + + if (process->ReadMemory( hash_table_ptr, buffer.GetBytes(), addr_size * 2, error) != + addr_size * 2) + return; + + DataExtractor data(buffer.GetBytes(), buffer.GetByteSize(), byte_order, + addr_size); + + // now read the storage structure first two fields + lldb::offset_t offset = addr_size; // Skip n_hashs field + uint64_t size = data.GetPointer(&offset) + 1; // get mask add 1 for size + uint64_t data_size = size * (2 * addr_size); // size + sizeof( entries) + buffer.SetByteSize( data_size); + + // read in entries en block, skip over n_hashs and mask + if( process->ReadMemory( hash_table_ptr + 2 * addr_size , buffer.GetBytes(), data_size, + error) != data_size) + return; + + data.SetData( buffer.GetBytes(), buffer.GetByteSize(), byte_order); + + offset = 0; + for (uint32_t bucket_idx = 0; bucket_idx < size; ++bucket_idx) + { + const lldb::addr_t classid = data.GetPointer(&offset); + const lldb::addr_t isa = data.GetPointer(&offset); + + if( classid == 0 || classid == invalid || isa == 0) + continue; + + if (!ISAIsCached(isa)) { + ClassDescriptorSP descriptor_sp( + new ClassDescriptorV1(isa, process_sp)); + + if (log && log->GetVerbose()) + log->Printf("MulleObjCRuntimeV1 added (ObjCISA)0x%" PRIx64 + " from mulle-objc-runtime to " + "isa->descriptor cache", + isa); + + AddClass(isa, descriptor_sp); + } + } + } +} + +DeclVendor *MulleObjCRuntimeV1::GetDeclVendor() { + return nullptr; +} diff --git a/source/Plugins/LanguageRuntime/ObjC/MulleObjCRuntime/MulleObjCRuntimeV1.h b/source/Plugins/LanguageRuntime/ObjC/MulleObjCRuntime/MulleObjCRuntimeV1.h new file mode 100644 index 0000000000..6406671c53 --- /dev/null +++ b/source/Plugins/LanguageRuntime/ObjC/MulleObjCRuntime/MulleObjCRuntimeV1.h @@ -0,0 +1,170 @@ +//===-- MulleObjCRuntimeV1.h ------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_MulleObjCRuntimeV1_h_ +#define liblldb_MulleObjCRuntimeV1_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "MulleObjCRuntime.h" +#include "lldb/Target/ObjCLanguageRuntime.h" +#include "lldb/lldb-private.h" + +namespace lldb_private { + +// +// The non-trampoline code uses LIBLLDB_LOG_LANGUAGE logging, which +// kinda fits by meaning and it is a relatively quiet channel. +// +// Enable verbose logging in lldb with `log enable -v lldb language` +// +class MulleObjCRuntimeV1 : public MulleObjCRuntime { +public: + ~MulleObjCRuntimeV1() override = default; + + //------------------------------------------------------------------ + // Static Functions + //------------------------------------------------------------------ + static void Initialize(); + + static void Terminate(); + + static lldb_private::LanguageRuntime * + CreateInstance(Process *process, lldb::LanguageType language); + + static lldb_private::ConstString GetPluginNameStatic(); + + static bool classof(const ObjCLanguageRuntime *runtime) { + switch (runtime->GetRuntimeVersion()) { + case ObjCRuntimeVersions::eMulleObjC_V1: + return true; + default: + return false; + } + } + + class ClassDescriptorV1 : public ObjCLanguageRuntime::ClassDescriptor { + public: + ClassDescriptorV1(ValueObject &isa_pointer); + ClassDescriptorV1(ObjCISA isa, lldb::ProcessSP process_sp); + + ~ClassDescriptorV1() override = default; + + ConstString GetClassName() override { return m_name; } + + ClassDescriptorSP GetSuperclass() override; + + ClassDescriptorSP GetMetaclass() const override; + + bool IsValid() override { return m_valid; } + + // + // TODO: should get on with this... + // + bool GetTaggedPointerInfo(uint64_t *info_bits = nullptr, + uint64_t *value_bits = nullptr, + uint64_t *payload = nullptr) override { + return false; + } + + uint64_t GetInstanceSize() override { return m_instance_size; } + + ObjCISA GetISA() override { return m_isa; } + + bool + Describe(std::function const + &superclass_func, + std::function const + &instance_method_func, + std::function const + &class_method_func, + std::function const &ivar_func) const override; + + protected: + void Initialize(ObjCISA isa, lldb::ProcessSP process_sp); + + private: + ConstString m_name; + ObjCISA m_isa; + ObjCISA m_parent_isa; + bool m_valid; + lldb::ProcessWP m_process_wp; + uint64_t m_instance_size; + }; + + // These are generic runtime functions: + bool GetDynamicTypeAndAddress(ValueObject &in_value, + lldb::DynamicValueType use_dynamic, + TypeAndOrName &class_type_or_name, + Address &address, + Value::ValueType &value_type) override; + + UtilityFunction *CreateObjectChecker(const char *) override; + + //------------------------------------------------------------------ + // PluginInterface protocol + //------------------------------------------------------------------ + ConstString GetPluginName() override; + + uint32_t GetPluginVersion() override; + + ObjCRuntimeVersions GetRuntimeVersion() const override { + return ObjCRuntimeVersions::eMulleObjC_V1; + } + + void UpdateISAToDescriptorMapIfNeeded() override; + + DeclVendor *GetDeclVendor() override; + +protected: + lldb::BreakpointResolverSP CreateExceptionResolver(Breakpoint *bkpt, + bool catch_bp, + bool throw_bp) override; + + class HashTableSignature { + public: + HashTableSignature() + : m_count(0), m_num_buckets(0), m_buckets_ptr(LLDB_INVALID_ADDRESS) {} + + bool NeedsUpdate(uint32_t count, uint32_t num_buckets, + lldb::addr_t buckets_ptr) { + return m_count != count || m_num_buckets != num_buckets || + m_buckets_ptr != buckets_ptr; + } + + void UpdateSignature(uint32_t count, uint32_t num_buckets, + lldb::addr_t buckets_ptr) { + m_count = count; + m_num_buckets = num_buckets; + m_buckets_ptr = buckets_ptr; + } + + protected: + uint32_t m_count; + uint32_t m_num_buckets; + lldb::addr_t m_buckets_ptr; + }; + + lldb::addr_t CallDangerousGetClassTableFunction( Process *process); + lldb::addr_t GetISAHashTablePointer( Process *process); + + HashTableSignature m_hash_signature; + lldb::addr_t m_isa_hash_table_ptr; + std::unique_ptr m_decl_vendor_ap; + +private: + MulleObjCRuntimeV1(Process *process); +}; + +} // namespace lldb_private + +#endif // liblldb_MulleObjCRuntimeV1_h_ diff --git a/source/Plugins/LanguageRuntime/ObjC/MulleObjCRuntime/MulleObjCTrampolineHandler.cpp b/source/Plugins/LanguageRuntime/ObjC/MulleObjCRuntime/MulleObjCTrampolineHandler.cpp new file mode 100644 index 0000000000..0fff5bf099 --- /dev/null +++ b/source/Plugins/LanguageRuntime/ObjC/MulleObjCRuntime/MulleObjCTrampolineHandler.cpp @@ -0,0 +1,711 @@ +//===-- MulleObjCTrampolineHandler.cpp ----------------------------*- C++ +//-*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "MulleObjCTrampolineHandler.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "MulleThreadPlanStepThroughObjCTrampoline.h" + +#include "lldb/Breakpoint/StoppointCallbackContext.h" +#include "lldb/Core/Debugger.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/StreamFile.h" +#include "lldb/Core/Value.h" +#include "lldb/Expression/DiagnosticManager.h" +#include "lldb/Expression/FunctionCaller.h" +#include "lldb/Expression/UserExpression.h" +#include "lldb/Expression/UtilityFunction.h" +#include "lldb/Symbol/ClangASTContext.h" +#include "lldb/Symbol/Symbol.h" +#include "lldb/Target/ABI.h" +#include "lldb/Target/ExecutionContext.h" +#include "lldb/Target/ObjCLanguageRuntime.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/RegisterContext.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/Thread.h" +#include "lldb/Target/ThreadPlanRunToAddress.h" +#include "lldb/Target/ThreadPlanStepOut.h" +#include "lldb/Utility/ConstString.h" +#include "lldb/Utility/FileSpec.h" +#include "lldb/Utility/Log.h" +#include "lldb/Utility/Status.h" + +#include "llvm/ADT/STLExtras.h" + +#define MULLE_LOG LIBLLDB_LOG_LANGUAGE +//#define MULLE_LOG LIBLLDB_LOG_STEP + + +using namespace lldb; +using namespace lldb_private; + +static const char *g_lookup_implementation_function_name = + "__lldb_objc_find_implementation_for_selector"; + +static const char g_lookup_implementation_function_code[] = + +#include "mulle-objc-lookup-imp.inc" +; + + +MulleObjCTrampolineHandler::~MulleObjCTrampolineHandler() {} + +// what should we step through anyway ? +// I would say "front" facing function calls emitted by -O0 +// which are: +// +// mulle_objc_object_call +// _mulle_objc_object_supercall +// +// We should also step through +// mulle_objc_fastlookup_infraclass_nofail +// but we can't for now. (Why not ?) +// +const MulleObjCTrampolineHandler::DispatchFunction + MulleObjCTrampolineHandler::g_dispatch_functions[] = { + // NAME HAS_CLASS_ARG HAS_SUPERID_ARG + { "mulle_objc_object_call", false, false }, + // super calls, where we have a classid as fourth parameter + { "_mulle_objc_object_supercall", false, true } + // optimized calls, where we have a class as fourth parameter + + // maybe these second stages too ? + // { "_mulle_objc_object_call2", false, false, false }, + // { "_mulle_objc_object_call_class", true, false, false } + }; + + +lldb::addr_t MulleObjCTrampolineHandler::LookupFunctionSymbol( const lldb::ProcessSP &process_sp, + const char *name) +{ + // Look up the addresses for the objc dispatch functions and cache them. For + // now I'm inspecting the symbol + // names dynamically to figure out how to dispatch to them. If it becomes + // more complicated than this we can + // turn the g_dispatch_functions char * array into a template table, and + // populate the DispatchFunction map + // from there. + + ConstString name_const_str( name); + const Symbol *msgSend_symbol = + m_objc_module_sp->FindFirstSymbolWithNameAndType(name_const_str, + eSymbolTypeCode); + if (msgSend_symbol && msgSend_symbol->ValueIsAddress()) { + // FixMe: Make g_dispatch_functions static table of DispatchFunctions, and + // have the map be address->index. + // Problem is we also need to lookup the dispatch function. For now we + // could have a side table of stret & non-stret + // dispatch functions. If that's as complex as it gets, we're fine. + Target *target = process_sp ? &process_sp->GetTarget() : NULL; + + lldb::addr_t sym_addr = + msgSend_symbol->GetAddressRef().GetOpcodeLoadAddress(target); + return( sym_addr); + } + else + { + // why not log ? + if (process_sp->CanJIT()) { + process_sp->GetTarget().GetDebugger().GetErrorFile()->Printf( + "Could not find implementation for function \"%s\"\n", + name_const_str.AsCString()); + } + } + return( LLDB_INVALID_ADDRESS); +} + + +MulleObjCTrampolineHandler::MulleObjCTrampolineHandler( + const ProcessSP &process_sp, const ModuleSP &objc_module_sp) + : m_process_wp(), m_objc_module_sp(objc_module_sp), + m_lookup_implementation_function_code(nullptr), + m_impl_fn_addr(LLDB_INVALID_ADDRESS), + m_msg_forward_addr(LLDB_INVALID_ADDRESS) { + if (process_sp) + m_process_wp = process_sp; + // Look up the known resolution functions: + + ConstString get_impl_name( "mulle_objc_lldb_lookup_implementation"); + ConstString msg_forward_name("__forward_mulle_objc_object_call"); + + Target *target = process_sp ? &process_sp->GetTarget() : NULL; + const Symbol *class_getMethodImplementation = + m_objc_module_sp->FindFirstSymbolWithNameAndType(get_impl_name, + eSymbolTypeCode); + const Symbol *msg_forward = m_objc_module_sp->FindFirstSymbolWithNameAndType( + msg_forward_name, eSymbolTypeCode); + + if (class_getMethodImplementation) + m_impl_fn_addr = + class_getMethodImplementation->GetAddress().GetOpcodeLoadAddress( + target); + if (msg_forward) + m_msg_forward_addr = msg_forward->GetAddress().GetOpcodeLoadAddress(target); + + // FIXME: Do some kind of logging here. + if (m_impl_fn_addr == LLDB_INVALID_ADDRESS) { + // If we can't even find the ordinary get method implementation function, + // then we aren't going to be able to + // step through any method dispatches. Warn to that effect and get out of + // here. + if (process_sp->CanJIT()) { + process_sp->GetTarget().GetDebugger().GetErrorFile()->Printf( + "Could not find implementation lookup function \"%s\"" + " in \"%s\" (%s)" + " stepping through ObjC method dispatch will not work.\n", + get_impl_name.AsCString(), + m_objc_module_sp->GetFileSpec().GetCString(), + class_getMethodImplementation ? "process failure" : "symbol not found"); + } + return; + } else { + m_lookup_implementation_function_code = + g_lookup_implementation_function_code; + } + + lldb::addr_t sym_addr; + + for (size_t i = 0; i != llvm::array_lengthof(g_dispatch_functions); i++) { + sym_addr = LookupFunctionSymbol( process_sp, + g_dispatch_functions[ i].name); + if( sym_addr != LLDB_INVALID_ADDRESS) + m_msgSend_map.insert(std::pair(sym_addr, i)); + } + + m_classlookup_addr[ 0] = LookupFunctionSymbol( process_sp, + "mulle_objc_global_lookup_infraclass_nofail"); + m_classlookup_addr[ 1] = LookupFunctionSymbol( process_sp, + "mulle_objc_global_lookup_infraclass_nofail_nofast"); + m_classlookup_addr[ 2] = LookupFunctionSymbol( process_sp, + "mulle_objc_object_lookup_infraclass_nofail"); + m_classlookup_addr[ 3] = LookupFunctionSymbol( process_sp, + "mulle_objc_object_lookup_infraclass_nofail_nofast"); +} + + +lldb::addr_t +MulleObjCTrampolineHandler::SetupDispatchFunction(Thread &thread, + ValueList &dispatch_values) { + ThreadSP thread_sp(thread.shared_from_this()); + ExecutionContext exe_ctx(thread_sp); + DiagnosticManager diagnostics; + Log *log(lldb_private::GetLogIfAllCategoriesSet(MULLE_LOG)); + + lldb::addr_t args_addr = LLDB_INVALID_ADDRESS; + FunctionCaller *impl_function_caller = nullptr; + + // Scope for mutex locker: + { + std::lock_guard guard(m_impl_function_mutex); + + // First stage is to make the ClangUtility to hold our injected function: + + if (!m_impl_code.get()) { + if (m_lookup_implementation_function_code != NULL) { + Status error; + m_impl_code.reset(exe_ctx.GetTargetRef().GetUtilityFunctionForLanguage( + m_lookup_implementation_function_code, eLanguageTypeObjC, + g_lookup_implementation_function_name, error)); + if (error.Fail()) { + if (log) + log->Printf( + "Failed to get Utility Function for implementation lookup: %s.", + error.AsCString()); + m_impl_code.reset(); + return args_addr; + } + + if (!m_impl_code->Install(diagnostics, exe_ctx)) { + if (log) { + log->Printf("Failed to install implementation lookup \"%s\".", g_lookup_implementation_function_name); + log->Printf( "Source code: ------\n%s\n------\n", m_lookup_implementation_function_code); + diagnostics.Dump(log); + } + m_impl_code.reset(); + return args_addr; + } + } else { + if (log) + log->Printf("No method lookup implementation code."); + return LLDB_INVALID_ADDRESS; + } + + // Next make the runner function for our implementation utility function. + ClangASTContext *clang_ast_context = + thread.GetProcess()->GetTarget().GetScratchClangASTContext(); + CompilerType clang_void_ptr_type = + clang_ast_context->GetBasicType(eBasicTypeVoid).GetPointerType(); + Status error; + + impl_function_caller = m_impl_code->MakeFunctionCaller( + clang_void_ptr_type, dispatch_values, thread_sp, error); + if (error.Fail()) { + if (log) + log->Printf( + "Error getting function caller for dispatch lookup: \"%s\".", + error.AsCString()); + return args_addr; + } + } else { + impl_function_caller = m_impl_code->GetFunctionCaller(); + } + } + + diagnostics.Clear(); + + // Now write down the argument values for this particular call. This looks + // like it might be a race condition + // if other threads were calling into here, but actually it isn't because we + // allocate a new args structure for + // this call by passing args_addr = LLDB_INVALID_ADDRESS... + + if (!impl_function_caller->WriteFunctionArguments( + exe_ctx, args_addr, dispatch_values, diagnostics)) { + if (log) { + log->Printf("Error writing function arguments."); + diagnostics.Dump(log); + } + return args_addr; + } + + return args_addr; +} + +bool +MulleObjCTrampolineHandler::GetDispatchFunctionForPCViaMap( lldb::addr_t curr_pc, DispatchFunction &this_dispatch) +{ + MsgsendMap::iterator pos; + + // fprintf( stderr, "%s PC: 0x%llx\n", __PRETTY_FUNCTION__, (unsigned long long) curr_pc); + + pos = m_msgSend_map.find( curr_pc); + if (pos != m_msgSend_map.end()) + { + this_dispatch = g_dispatch_functions[(*pos).second]; + // fprintf( stderr, "known dispatch pc\n"); + return( true); + } + return( false); +} + + +lldb::addr_t +MulleObjCTrampolineHandler::ReadIndirectJMPQ_X86_64( lldb::addr_t curr_pc) +{ +#pragma pack( push, 1) +#pragma pack( 2) + struct + { + uint16_t opcode; + int32_t offset; + } instruction; +#pragma pack( pop) + + Status error; + size_t bytes_read; + void *f; + lldb::addr_t table_adr; + + // fprintf( stderr, "%s PC: 0x%llx\n", __PRETTY_FUNCTION__, (unsigned long long) curr_pc); + + ProcessSP process_sp = m_process_wp.lock(); + if( ! process_sp) + { + // fprintf( stderr, "fail process\n"); + return( LLDB_INVALID_ADDRESS); + } + + bytes_read = process_sp->ReadMemory( curr_pc, &instruction, sizeof( instruction), error); + if( bytes_read != sizeof( instruction)) + { + // fprintf( stderr, "fail instruction read\n"); + return( LLDB_INVALID_ADDRESS); + } + if( instruction.opcode != 0x25FF) // jmpq rel a + { + // fprintf( stderr, "fail opcode: %x\n", instruction.opcode); + return( LLDB_INVALID_ADDRESS); + } + + // fprintf( stderr, "offset, 0x%x\n", instruction.offset); + table_adr = curr_pc + sizeof( instruction) + instruction.offset; + bytes_read = process_sp->ReadMemory( table_adr, &f, sizeof( f), error); + if( bytes_read != sizeof( f)) + { + // fprintf( stderr, "fail f read\n"); + return( LLDB_INVALID_ADDRESS); + } + return( (lldb::addr_t) f); +} + + +ThreadPlanSP +MulleObjCTrampolineHandler::GetStepOutDispatchPlan( Thread &thread, + bool stop_others) +{ + return( thread.QueueThreadPlanForStepOut( false, nullptr, false, stop_others, + eVoteYes, eVoteNoOpinion, + thread.GetSelectedFrameIndex())); +} + + +void MulleObjCTrampolineHandler::SetBreakpointForReturn( Thread &thread, const StackID &m_stack_id) +{ + break_id_t m_backstop_bkpt_id; + uint64_t m_start_address; + uint64_t m_backstop_addr; + + m_start_address = thread.GetRegisterContext()->GetPC(0); + + // We are going to return back to the concrete frame 1, we might pass by + // some inlined code that we're in + // the middle of by doing this, but it's easier than trying to figure out + // where the inlined code might return to. + + StackFrameSP return_frame_sp = thread.GetFrameWithStackID(m_stack_id); + + if (return_frame_sp) { + m_backstop_addr = return_frame_sp->GetFrameCodeAddress().GetLoadAddress( + thread.CalculateTarget().get()); + Breakpoint *return_bp = + thread.GetProcess() + ->GetTarget() + .CreateBreakpoint(m_backstop_addr, true, false) + .get(); + if (return_bp != nullptr) { + return_bp->SetThreadID(thread.GetID()); + m_backstop_bkpt_id = return_bp->GetID(); + return_bp->SetBreakpointKind("step-through-backstop"); + } + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP)); + if (log) { + log->Printf("Setting backstop breakpoint %d at address: 0x%" PRIx64, + m_backstop_bkpt_id, m_backstop_addr); + } + } +} + + +ThreadPlanSP +MulleObjCTrampolineHandler::GetStepThroughDispatchPlan( Thread &thread, + const StackID &stackid, + bool stop_others) +{ + ThreadPlanSP ret_plan_sp; + lldb::addr_t curr_pc = thread.GetRegisterContext()->GetPC(); + lldb::addr_t indirect_pc; + DispatchFunction this_dispatch; + bool found_it; + + Log *log(lldb_private::GetLogIfAllCategoriesSet( MULLE_LOG)); + + if( CanStepOver()) + { + int i; + + for( i = 0; i < 4; i++) + if( curr_pc == m_classlookup_addr[ i]) + { + if (log) + log->Printf( "Mulle: Return with \"step out of class-lookup\" plan."); + + ret_plan_sp.reset( new ThreadPlanStepOut( + thread, + nullptr, + false, stop_others, eVoteYes, + eVoteNoOpinion, thread.GetSelectedFrameIndex(), + eLazyBoolNo)); + return( ret_plan_sp); + } + } + + // First step is to look and see if we are in one of the known ObjC dispatch functions. We've already compiled + // a table of same, so consult it. + found_it = GetDispatchFunctionForPCViaMap( curr_pc, this_dispatch); + + // figure out the indirect address if PC is pointing to + // a jump vector + + if( ! found_it) + { + indirect_pc = ReadIndirectJMPQ_X86_64( curr_pc); + + // fprintf( stderr, "indirect PC: 0x%llx\n", (unsigned long long) indirect_pc); + + if( indirect_pc != LLDB_INVALID_ADDRESS) + { + // and consult again for indirect_pc + found_it = GetDispatchFunctionForPCViaMap( indirect_pc, this_dispatch); + } + } + + if( ! found_it) + { + if (log) + log->Printf( "Mulle: Unknown dispatch address 0x%llx. Returning empty plan.", (unsigned long long) curr_pc); + // fprintf( stderr, "*unknown dispatch*\n"); + return ret_plan_sp; + } + + // We are decoding a method dispatch. + // First job is to pull the arguments out: + + lldb::StackFrameSP thread_cur_frame = thread.GetStackFrameAtIndex(0); + + const ABI *abi = NULL; + ProcessSP process_sp (thread.CalculateProcess()); + if (process_sp) + abi = process_sp->GetABI().get(); + if (abi == NULL) + { + if (log) + log->Printf( "Mulle: Unknown ABI. Returning empty plan."); + return ret_plan_sp; + } + TargetSP target_sp (thread.CalculateTarget()); + + ClangASTContext *clang_ast_context = target_sp->GetScratchClangASTContext(); + ValueList argument_values; + + Value void_ptr_value; + CompilerType clang_void_ptr_type = clang_ast_context->GetBasicType(eBasicTypeVoid).GetPointerType(); + void_ptr_value.SetValueType (Value::eValueTypeScalar); + //void_ptr_value.SetContext (Value::eContextTypeClangType, clang_void_ptr_type); + void_ptr_value.SetCompilerType (clang_void_ptr_type); + + Value uint32_t_value; + CompilerType clang_uint32_type = clang_ast_context->GetBuiltinTypeForEncodingAndBitSize(lldb::eEncodingUint, 32); + uint32_t_value.SetValueType (Value::eValueTypeScalar); + //void_ptr_value.SetContext (Value::eContextTypeClangType, clang_void_ptr_type); + uint32_t_value.SetCompilerType(clang_uint32_type); + + Value int_value; + CompilerType clang_int_type = clang_ast_context->GetBuiltinTypeForEncodingAndBitSize(lldb::eEncodingSint, 32); + int_value.SetValueType (Value::eValueTypeScalar); + //flag_value.SetContext (Value::eContextTypeClangType, clang_int_type); + int_value.SetCompilerType (clang_int_type); + + argument_values.PushValue(void_ptr_value); // self + argument_values.PushValue(uint32_t_value); // _cmd + argument_values.PushValue(void_ptr_value); // _param + if( this_dispatch.has_superid_argument) + argument_values.PushValue(uint32_t_value); // superid + else + if( this_dispatch.has_superid_argument) + argument_values.PushValue(void_ptr_value); // class + + /* + * read from the registers and stack the actual values for + * the method call we are "intercepting" + */ + bool success = abi->GetArgumentValues (thread, argument_values); + if( ! success) + { + // fprintf( stderr, "fail getting argument values\n"); + if (log) + log->Printf( "Mulle: Problem getting argument values. Returning empty plan."); + return ret_plan_sp; + } + + lldb::addr_t obj_addr = argument_values.GetValueAtIndex(0)->GetScalar().ULongLong(); + lldb::addr_t sel_addr = argument_values.GetValueAtIndex(1)->GetScalar().ULongLong(); + // lldb::addr_t param_addr = argument_values.GetValueAtIndex(2)->GetScalar().ULongLong(); + + if( obj_addr == 0x0) + { + if (log) + log->Printf("Mulle: Asked to step to dispatch to nil object. Returning empty plan."); + return ret_plan_sp; + } + + ExecutionContext exe_ctx (thread.shared_from_this()); + Process *process = exe_ctx.GetProcessPtr(); + + // Figure out the class this is being dispatched to and see if we've already cached this method call, + // If so we can push a run-to-address plan directly. Otherwise we have to figure out where + // the implementation lives. + + // isa_addr will store the class pointer that the method is being dispatched to - so either the class + // directly or the super class if this is one of the objc_msgSendSuper flavors. That's mostly used to + // look up the class/selector pair in our cache. + + lldb::addr_t isa_addr = LLDB_INVALID_ADDRESS; + + // has cls + if( this_dispatch.has_class_argument) + { + Value cls_value( *(argument_values.GetValueAtIndex(3))); + + cls_value.SetCompilerType( clang_void_ptr_type); + cls_value.SetValueType( Value::eValueTypeLoadAddress); + cls_value.ResolveValue( &exe_ctx); + // fprintf( stderr, "super2\n"); + if ( ! cls_value.GetScalar().IsValid()) + { + if (log) + log->Printf("Mulle: Supplied class is invalid. Returning empty plan."); + return ret_plan_sp; + } + // fprintf( stderr, "get isa\n"); + isa_addr = cls_value.GetScalar().ULongLong(); + } + else + { + // In the direct dispatch case, the object->isa is the class pointer we want. + + // This is a little cheesy, but since object->isa is the first field, + // making the object value a load address value and resolving it will get + // the pointer sized data pointed to by that value... + + // Note, it isn't a fatal error not to be able to get the address from the object, since this might + // be a "tagged pointer" which isn't a real object, but rather some word length encoded dingus. + + // figure out isa from object argument. If its a tagged pointer, we can't use the isa lookup + // cache but run the lookup function every time + + if( (obj_addr & 0x7) == 0) // quick tagged pointer check + { + Value isa_value( obj_addr - (int) process->GetAddressByteSize()); + // fprintf( stderr, "super: 0x%llx\n", isa_value.GetScalar().ULongLong()); + isa_value.SetCompilerType(clang_void_ptr_type); + isa_value.SetValueType(Value::eValueTypeLoadAddress); + isa_value.ResolveValue(&exe_ctx); + + if (isa_value.GetScalar().IsValid()) + { + // fprintf( stderr, "get isa\n"); + isa_addr = isa_value.GetScalar().ULongLong(); + } + else + { + if (log) + log->Printf("Mulle: Supplied class is invalid. Returning empty plan."); + return ret_plan_sp; + } + } + } + // in the case of obj, _cmd, _param, clsid: isa_addr is still unknown + + // Okay, we've may have got the address of the class for which we're resolving this, let's see if it's in our cache: + lldb::addr_t impl_addr = LLDB_INVALID_ADDRESS; + + if( log) + { + log->Printf("Mulle: obj = 0x%llx\n", (unsigned long long) obj_addr); + log->Printf("Mulle: _cmd = 0x%llx\n", (unsigned long long) sel_addr); + // log->Printf("Mulle: _param : 0x%llx\n", (unsigned long long) param_addr); + log->Printf("Mulle: isa = 0x%llx\n", (unsigned long long) isa_addr); + } + + if (isa_addr != LLDB_INVALID_ADDRESS) + { + if (log) + { + log->Printf("Mulle: Resolving call for class - 0x%" PRIx64 " and selector - 0x%" PRIx64, + isa_addr, sel_addr); + } + ObjCLanguageRuntime *objc_runtime = thread.GetProcess()->GetObjCLanguageRuntime(); + assert(objc_runtime != NULL); + + impl_addr = objc_runtime->LookupInMethodCache (isa_addr, sel_addr); + // fprintf( stderr, "impl: 0x%llx\n", (unsigned long long)impl_addr); + } + + /* + * At this point we "know" that we are going to step through, either directly + * or via that trampoline thingy... so since out message sender is not a + * true trampoline but a c-function, we need to set a breakpoint + * (this is done better with the thunk code) + */ + // SetBreakpointForReturn( thread, stackid); + + if (impl_addr != LLDB_INVALID_ADDRESS) + { + // Yup, it was in the cache, so we can run to that address directly. + + if (log) + log->Printf ("Mulle: Found implementation address in cache: 0x%" PRIx64 ". Return run to address plan.", impl_addr); + + ret_plan_sp.reset (new ThreadPlanRunToAddress (thread, impl_addr, stop_others)); + // fprintf( stderr, "cached return\n"); + return ret_plan_sp; + } + + // We haven't seen this class/selector pair yet. Look it up. + if (log) + log->Printf( "Mulle: Lookup implementation for method"); + + ValueList dispatch_values; + + // We've will inject a little function in the target that takes the object, selector and some flags, + // and figures out the implementation. Looks like: + // void *__lldb_objc_find_implementation_for_selector (void *object, + // uint32_t sel, + // void *cls_or_superid, + // int is_superid, + // int debug, + // + // So set up the arguments for that call. + dispatch_values.PushValue (*(argument_values.GetValueAtIndex(0))); + dispatch_values.PushValue (*(argument_values.GetValueAtIndex(1))); + + if( this_dispatch.has_superid_argument || this_dispatch.has_class_argument) + { + Value value( *(argument_values.GetValueAtIndex( 3))); + + value.SetCompilerType( clang_void_ptr_type); + dispatch_values.PushValue( value); // just push it + } + + if( this_dispatch.has_superid_argument) + int_value.GetScalar() = 1; + else + if( this_dispatch.has_class_argument) + int_value.GetScalar() = 2; // FIXME - Set to 0 when debugging is done. + else + int_value.GetScalar() = 0; // FIXME - Set to 0 when debugging is done. + dispatch_values.PushValue(int_value); + + if (log && log->GetVerbose()) + int_value.GetScalar() = 1; + else + int_value.GetScalar() = 0; // FIXME - Set to 0 when debugging is done. + dispatch_values.PushValue( int_value); + + // The step through code might have to fill in the cache, so it is not safe to run only one thread. + // So we override the stop_others value passed in to us here: + + const bool trampoline_stop_others = false; + ret_plan_sp.reset (new MulleThreadPlanStepThroughObjCTrampoline (thread, + this, + dispatch_values, + isa_addr, + sel_addr, + trampoline_stop_others)); + if (log) + { + StreamString s; + ret_plan_sp->GetDescription(&s, eDescriptionLevelFull); + log->Printf( "Mulle: Using ObjC step through plan: %s.\n", s.GetData()); + } + // fprintf( stderr, "trampoline return\n"); + + return ret_plan_sp; +} + + +FunctionCaller * +MulleObjCTrampolineHandler::GetLookupImplementationFunctionCaller() { + return m_impl_code->GetFunctionCaller(); +} diff --git a/source/Plugins/LanguageRuntime/ObjC/MulleObjCRuntime/MulleObjCTrampolineHandler.h b/source/Plugins/LanguageRuntime/ObjC/MulleObjCRuntime/MulleObjCTrampolineHandler.h new file mode 100644 index 0000000000..50ce3091a2 --- /dev/null +++ b/source/Plugins/LanguageRuntime/ObjC/MulleObjCRuntime/MulleObjCTrampolineHandler.h @@ -0,0 +1,94 @@ +//===-- MulleObjCTrampolineHandler.h ----------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef lldb_MulleObjCTrampolineHandler_h_ +#define lldb_MulleObjCTrampolineHandler_h_ + +// C Includes +// C++ Includes +#include +#include +#include + +// Other libraries and framework includes +// Project includes +#include "lldb/Expression/UtilityFunction.h" +#include "lldb/lldb-public.h" + +namespace lldb_private { + +class MulleObjCTrampolineHandler { +public: + MulleObjCTrampolineHandler(const lldb::ProcessSP &process_sp, + const lldb::ModuleSP &objc_module_sp); + + ~MulleObjCTrampolineHandler(); + + lldb::ThreadPlanSP GetStepThroughDispatchPlan( Thread &thread, + const StackID &stackid, + bool stop_others); + lldb::ThreadPlanSP GetStepOutDispatchPlan(Thread &thread, + bool stop_others); + + + FunctionCaller *GetLookupImplementationFunctionCaller(); + + bool AddrIsMsgForward(lldb::addr_t addr) const { + return (addr == m_msg_forward_addr); + } + + struct DispatchFunction { + public: + const char *name; + bool has_class_argument; + bool has_superid_argument; + }; + + lldb::addr_t SetupDispatchFunction(Thread &thread, + ValueList &dispatch_values); + +protected: + lldb::addr_t ReadIndirectJMPQ_X86_64( lldb::addr_t curr_pc); + bool GetDispatchFunctionForPCViaMap( lldb::addr_t curr_pc, DispatchFunction &this_dispatch); + void SetBreakpointForReturn( Thread &thread, const StackID &m_stack_id); + +private: + lldb::addr_t LookupFunctionSymbol( const lldb::ProcessSP &process_sp, + const char *name); + + static const DispatchFunction g_dispatch_functions[]; + + typedef std::map MsgsendMap; // This table maps an dispatch + // fn address to the index in + // g_dispatch_functions + MsgsendMap m_msgSend_map; + + lldb::ProcessWP m_process_wp; + lldb::ModuleSP m_objc_module_sp; + const char *m_lookup_implementation_function_code; + std::unique_ptr m_impl_code; + std::mutex m_impl_function_mutex; + lldb::addr_t m_classlookup_addr[ 4]; + lldb::addr_t m_impl_fn_addr; + lldb::addr_t m_msg_forward_addr; // this is the function to "get" the forward method from the class + +public: + bool CanStepThrough() + { + return( m_impl_fn_addr != LLDB_INVALID_ADDRESS); + } + bool CanStepOver() + { + return( m_classlookup_addr[ 0] != LLDB_INVALID_ADDRESS); + } +}; + +} // namespace lldb_private + +#endif // lldb_MulleObjCTrampolineHandler_h_ diff --git a/source/Plugins/LanguageRuntime/ObjC/MulleObjCRuntime/MulleThreadPlanStepThroughObjCTrampoline.cpp b/source/Plugins/LanguageRuntime/ObjC/MulleObjCRuntime/MulleThreadPlanStepThroughObjCTrampoline.cpp new file mode 100644 index 0000000000..e79f80536e --- /dev/null +++ b/source/Plugins/LanguageRuntime/ObjC/MulleObjCRuntime/MulleThreadPlanStepThroughObjCTrampoline.cpp @@ -0,0 +1,242 @@ +//===-- MulleThreadPlanStepThroughObjCTrampoline.cpp +//--------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "MulleThreadPlanStepThroughObjCTrampoline.h" +#include "MulleObjCTrampolineHandler.h" +#include "lldb/Expression/DiagnosticManager.h" +#include "lldb/Expression/FunctionCaller.h" +#include "lldb/Expression/UtilityFunction.h" +#include "lldb/Target/ExecutionContext.h" +#include "lldb/Target/ObjCLanguageRuntime.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/Thread.h" +#include "lldb/Target/ThreadPlanRunToAddress.h" +#include "lldb/Target/ThreadPlanStepOut.h" +#include "lldb/Utility/Log.h" + +using namespace lldb; +using namespace lldb_private; + +#define MULLE_LOG LIBLLDB_LOG_LANGUAGE +//#define MULLE_LOG LIBLLDB_LOG_STEP +//---------------------------------------------------------------------- +// ThreadPlanStepThroughObjCTrampoline constructor +//---------------------------------------------------------------------- +MulleThreadPlanStepThroughObjCTrampoline:: + MulleThreadPlanStepThroughObjCTrampoline( + Thread &thread, MulleObjCTrampolineHandler *trampoline_handler, + ValueList &input_values, lldb::addr_t isa_addr, lldb::addr_t sel_addr, + bool stop_others) + : ThreadPlan(ThreadPlan::eKindGeneric, + "Step through MulleObjC trampoline", thread, eVoteNoOpinion, + eVoteNoOpinion), + m_trampoline_handler(trampoline_handler), + m_args_addr(LLDB_INVALID_ADDRESS), m_input_values(input_values), + m_isa_addr(isa_addr), m_sel_addr(sel_addr), m_impl_function(NULL), + m_stop_others(stop_others) {} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +MulleThreadPlanStepThroughObjCTrampoline:: + ~MulleThreadPlanStepThroughObjCTrampoline() {} + +void MulleThreadPlanStepThroughObjCTrampoline::DidPush() { + // Setting up the memory space for the called function text might require + // allocations, + // i.e. a nested function call. This needs to be done as a PreResumeAction. + m_thread.GetProcess()->AddPreResumeAction(PreResumeInitializeFunctionCaller, + (void *)this); +} + +bool MulleThreadPlanStepThroughObjCTrampoline::InitializeFunctionCaller() { + Log *log(lldb_private::GetLogIfAllCategoriesSet(MULLE_LOG)); + if (!m_func_sp) { + DiagnosticManager diagnostics; + + m_args_addr = + m_trampoline_handler->SetupDispatchFunction(m_thread, m_input_values); + + if (m_args_addr == LLDB_INVALID_ADDRESS) { + if (log) + log->Printf("Mulle: SetupDispatchFunction failed."); + return false; + } + m_impl_function = + m_trampoline_handler->GetLookupImplementationFunctionCaller(); + ExecutionContext exc_ctx; + EvaluateExpressionOptions options; + options.SetUnwindOnError(true); + options.SetIgnoreBreakpoints(true); + options.SetStopOthers(m_stop_others); + m_thread.CalculateExecutionContext(exc_ctx); + m_func_sp = m_impl_function->GetThreadPlanToCallFunction( + exc_ctx, m_args_addr, options, diagnostics); + m_func_sp->SetOkayToDiscard(true); + + if (log) + log->Printf("Mulle: thread plan queued"); + + m_thread.QueueThreadPlan(m_func_sp, false); + } + else + if (log) + log->Printf("Mulle: m_func_sp not set"); + return true; +} + +bool MulleThreadPlanStepThroughObjCTrampoline:: + PreResumeInitializeFunctionCaller(void *void_myself) { + MulleThreadPlanStepThroughObjCTrampoline *myself = + static_cast(void_myself); + return myself->InitializeFunctionCaller(); +} + +void MulleThreadPlanStepThroughObjCTrampoline::GetDescription( + Stream *s, lldb::DescriptionLevel level) { + if (level == lldb::eDescriptionLevelBrief) + s->Printf("Mulle: Step through ObjC trampoline"); + else { + s->Printf("Mulle: Stepping to implementation of ObjC method - obj: 0x%llx, isa: " + "0x%" PRIx64 ", sel: 0x%" PRIx64, + m_input_values.GetValueAtIndex(0)->GetScalar().ULongLong(), + m_isa_addr, m_sel_addr); + } +} + +bool MulleThreadPlanStepThroughObjCTrampoline::ValidatePlan(Stream *error) { + return true; +} + +bool MulleThreadPlanStepThroughObjCTrampoline::DoPlanExplainsStop( + Event *event_ptr) { + // If we get asked to explain the stop it will be because something went + // wrong (like the implementation for selector function crashed... We're + // going + // to figure out what to do about that, so we do explain the stop. + return true; +} + +lldb::StateType MulleThreadPlanStepThroughObjCTrampoline::GetPlanRunState() { + return eStateRunning; +} + +bool MulleThreadPlanStepThroughObjCTrampoline::ShouldStop(Event *event_ptr) { + // First stage: we are still handling the "call a function to get the target + // of the dispatch" + Log *log(lldb_private::GetLogIfAllCategoriesSet(MULLE_LOG)); + + if (log) + log->Printf("Mulle: should stop is called (asked)"); + + if (m_func_sp) { + if (!m_func_sp->IsPlanComplete()) { + if (log) + log->Printf("Mulle: plan is complete"); + return false; + } else { + if (!m_func_sp->PlanSucceeded()) { + SetPlanComplete(false); + if (log) + log->Printf("Mulle: plan failed (done)"); + return true; + } + m_func_sp.reset(); + } + } + + // Second stage, if all went well with the function calling, then fetch the + // target address, and + // queue up a "run to that address" plan. + if (!m_run_to_sp) { + Value target_addr_value; + ExecutionContext exc_ctx; + m_thread.CalculateExecutionContext(exc_ctx); + m_impl_function->FetchFunctionResults(exc_ctx, m_args_addr, + target_addr_value); + m_impl_function->DeallocateFunctionResults(exc_ctx, m_args_addr); + lldb::addr_t target_addr = target_addr_value.GetScalar().ULongLong(); + Address target_so_addr; + target_so_addr.SetOpcodeLoadAddress(target_addr, exc_ctx.GetTargetPtr()); + if (target_addr == 0) { + if (log) + log->Printf("Mulle: Got target implementation of 0x0, stopping."); + SetPlanComplete( false); + return true; + } + if (m_trampoline_handler->AddrIsMsgForward(target_addr)) { + if (log) + log->Printf( + "Mulle: Implementation lookup returned msgForward function: 0x%" PRIx64 + ", stopping.", + target_addr); + + SymbolContext sc = m_thread.GetStackFrameAtIndex(0)->GetSymbolContext( + eSymbolContextEverything); + const bool abort_other_plans = false; + const bool first_insn = true; + const uint32_t frame_idx = 0; + m_run_to_sp = m_thread.QueueThreadPlanForStepOutNoShouldStop( + abort_other_plans, &sc, first_insn, m_stop_others, eVoteNoOpinion, + eVoteNoOpinion, frame_idx); + m_run_to_sp->SetPrivate(true); + return false; + } + + if (log) + log->Printf("Mulle: Running to ObjC method implementation: 0x%" PRIx64, + target_addr); + + // can't cache if isa is unknown (TPS) + if( m_isa_addr != LLDB_INVALID_ADDRESS) + { + ObjCLanguageRuntime *objc_runtime = + GetThread().GetProcess()->GetObjCLanguageRuntime(); + assert(objc_runtime != NULL); + objc_runtime->AddToMethodCache(m_isa_addr, m_sel_addr, target_addr); + if (log) + log->Printf("Mulle: Added {isa-addr=0x%" PRIx64 ", sel-addr=0x%" PRIx64 + "} = addr=0x%" PRIx64 " to cache.", + m_isa_addr, m_sel_addr, target_addr); + } + // Extract the target address from the value: + + m_run_to_sp.reset( + new ThreadPlanRunToAddress(m_thread, target_so_addr, m_stop_others)); + m_thread.QueueThreadPlan(m_run_to_sp, false); + m_run_to_sp->SetPrivate(true); + return false; + } else if (m_thread.IsThreadPlanDone(m_run_to_sp.get())) { + // Third stage, work the run to target plan. + if (log) + log->Printf("Mulle: thread plan is done"); + SetPlanComplete( true); + return true; + } + + if (log) + log->Printf("Mulle: should stop says no"); + return false; +} + +// The base class MischiefManaged does some cleanup - so you have to call it +// in your MischiefManaged derived class. +bool MulleThreadPlanStepThroughObjCTrampoline::MischiefManaged() { + if (IsPlanComplete()) + return true; + else + return false; +} + +bool MulleThreadPlanStepThroughObjCTrampoline::WillStop() { return true; } diff --git a/source/Plugins/LanguageRuntime/ObjC/MulleObjCRuntime/MulleThreadPlanStepThroughObjCTrampoline.h b/source/Plugins/LanguageRuntime/ObjC/MulleObjCRuntime/MulleThreadPlanStepThroughObjCTrampoline.h new file mode 100644 index 0000000000..e7da0da0c3 --- /dev/null +++ b/source/Plugins/LanguageRuntime/ObjC/MulleObjCRuntime/MulleThreadPlanStepThroughObjCTrampoline.h @@ -0,0 +1,82 @@ +//===-- MulleThreadPlanStepThroughObjCTrampoline.h --------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef lldb_MulleThreadPlanStepThroughObjCTrampoline_h_ +#define lldb_MulleThreadPlanStepThroughObjCTrampoline_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "MulleObjCTrampolineHandler.h" +#include "lldb/Core/Value.h" +#include "lldb/Target/ThreadPlan.h" +#include "lldb/lldb-enumerations.h" +#include "lldb/lldb-types.h" + +// this is unchanged from AppleThreadPlanStepThroughObjcTrampoline except for the naming and some comments +namespace lldb_private { + +class MulleThreadPlanStepThroughObjCTrampoline : public ThreadPlan { +public: + MulleThreadPlanStepThroughObjCTrampoline( + Thread &thread, MulleObjCTrampolineHandler *trampoline_handler, + ValueList &values, lldb::addr_t isa_addr, lldb::addr_t sel_addr, + bool stop_others); + + ~MulleThreadPlanStepThroughObjCTrampoline() override; + + static bool PreResumeInitializeFunctionCaller(void *myself); + + void GetDescription(Stream *s, lldb::DescriptionLevel level) override; + + bool ValidatePlan(Stream *error) override; + + lldb::StateType GetPlanRunState() override; + + bool ShouldStop(Event *event_ptr) override; + + bool StopOthers() override { return m_stop_others; } + + // The base class MischiefManaged does some cleanup - so you have to call it + // in your MischiefManaged derived class. + bool MischiefManaged() override; + + void DidPush() override; + + bool WillStop() override; + +protected: + bool DoPlanExplainsStop(Event *event_ptr) override; + +private: + bool InitializeFunctionCaller(); + + MulleObjCTrampolineHandler *m_trampoline_handler; // FIXME - ensure this + // doesn't go away on us? + // SP maybe? + lldb::addr_t m_args_addr; // Stores the address for our step through function + // result structure. + // lldb::addr_t m_object_addr; // This is only for Description. + ValueList m_input_values; + lldb::addr_t m_isa_addr; // isa_addr and sel_addr are the keys we will use to + // cache the implementation. + lldb::addr_t m_sel_addr; + lldb::ThreadPlanSP m_func_sp; // This is the function call plan. We fill it + // at start, then set it + // to NULL when this plan is done. That way we know to go to: + lldb::ThreadPlanSP m_run_to_sp; // The plan that runs to the target. + FunctionCaller *m_impl_function; // This is a pointer to a impl function that + // is owned by the client that pushes this plan. + bool m_stop_others; +}; + +} // namespace lldb_private + +#endif // lldb_MulleThreadPlanStepThroughObjCTrampoline_h_ diff --git a/source/Plugins/LanguageRuntime/ObjC/MulleObjCRuntime/make-inc.sh b/source/Plugins/LanguageRuntime/ObjC/MulleObjCRuntime/make-inc.sh new file mode 100755 index 0000000000..8b9b65807c --- /dev/null +++ b/source/Plugins/LanguageRuntime/ObjC/MulleObjCRuntime/make-inc.sh @@ -0,0 +1,73 @@ +#! /bin/sh +# +# (c) 2017 code by Nat! +# + +usage() +{ + cat << EOF >&2 +Usage: make-inc.sh [input] [output] +EOF + exit 1 +} + + +escape_quotes() +{ + sed 's/\"/\\\"/g' +} + + +remove_cxx_comments() +{ + sed -e 's/\(.*\)\/\/.*/\1/' +} + + +remove_empty_lines() +{ + sed -e '/^[[:space:]]*$/d' +} + + +make_strings() +{ + # also kill leading and trailing whitespace + sed -e 's/[[:space:]]*\(.*\)[[:space:]]*/"\1\\n"/' +} + + +convert() +{ +# echo "extern \\\"C\\\"\\\n\"" +# echo "\"{\\\n\"" + + remove_cxx_comments | remove_empty_lines | escape_quotes | make_strings + +# echo "\"}\\\n\"" +} + + +# use c string concatenation +main() +{ + case $# in + 0) + convert + ;; + + 1) + cat "$1" | convert + ;; + + 2) + cat "$1" | convert > "$2" + ;; + + *) + usage + ;; + esac +} + +main "$@" diff --git a/source/Plugins/LanguageRuntime/ObjC/MulleObjCRuntime/mulle-objc-dangerous-class-storage.inc b/source/Plugins/LanguageRuntime/ObjC/MulleObjCRuntime/mulle-objc-dangerous-class-storage.inc new file mode 100644 index 0000000000..120a4a9501 --- /dev/null +++ b/source/Plugins/LanguageRuntime/ObjC/MulleObjCRuntime/mulle-objc-dangerous-class-storage.inc @@ -0,0 +1,15 @@ +"static const \n" +"struct mulle_clang_objccompilerinfo\n" +"{\n" +"unsigned int load_version;\n" +"unsigned int runtime_version;\n" +"} __mulle_objc_objccompilerinfo =\n" +"{\n" +"14, \n" +"0 \n" +"};\n" +"void *__lldb_objc_get_dangerous_class_storage( void)\n" +"{\n" +"extern void *mulle_objc_lldb_get_dangerous_classstorage_pointer( void);\n" +"return( mulle_objc_lldb_get_dangerous_classstorage_pointer());\n" +"}\n" diff --git a/source/Plugins/LanguageRuntime/ObjC/MulleObjCRuntime/mulle-objc-dangerous-class-storage.src b/source/Plugins/LanguageRuntime/ObjC/MulleObjCRuntime/mulle-objc-dangerous-class-storage.src new file mode 100644 index 0000000000..a993e80770 --- /dev/null +++ b/source/Plugins/LanguageRuntime/ObjC/MulleObjCRuntime/mulle-objc-dangerous-class-storage.src @@ -0,0 +1,21 @@ +extern "C" +{ + static const + struct mulle_clang_objccompilerinfo + { + unsigned int load_version; + unsigned int runtime_version; + } __mulle_objc_objccompilerinfo = + { + 14, // load version must match + 0 // 0 to not emit __load_mulle_objc + }; + + + void *__lldb_objc_get_dangerous_class_storage( void) + { + extern void *mulle_objc_lldb_get_dangerous_classstorage_pointer( void); + + return( mulle_objc_lldb_get_dangerous_classstorage_pointer()); + } +} diff --git a/source/Plugins/LanguageRuntime/ObjC/MulleObjCRuntime/mulle-objc-lookup-imp.inc b/source/Plugins/LanguageRuntime/ObjC/MulleObjCRuntime/mulle-objc-lookup-imp.inc new file mode 100644 index 0000000000..ff563290b2 --- /dev/null +++ b/source/Plugins/LanguageRuntime/ObjC/MulleObjCRuntime/mulle-objc-lookup-imp.inc @@ -0,0 +1,30 @@ +"extern \"C\"\n" +"{\n" +"static const\n" +"struct mulle_clang_objccompilerinfo\n" +"{\n" +"unsigned int load_version;\n" +"unsigned int runtime_version;\n" +"} __mulle_objc_objccompilerinfo =\n" +"{\n" +"14, \n" +"0 \n" +"};\n" +"void *__lldb_objc_find_implementation_for_selector( void *object,\n" +"uint32_t methodid,\n" +"void *cls_or_superid,\n" +"int is_superid,\n" +"int debug)\n" +"{\n" +"extern void *mulle_objc_lldb_lookup_implementation( void *obj,\n" +"uint32_t methodid,\n" +"void *cls_or_superid,\n" +"int is_superid,\n" +"int debug);\n" +"return( mulle_objc_lldb_lookup_implementation( object,\n" +"methodid,\n" +"cls_or_superid,\n" +"is_superid,\n" +"debug));\n" +"}\n" +"}\n" \ No newline at end of file diff --git a/source/Plugins/LanguageRuntime/ObjC/MulleObjCRuntime/mulle-objc-lookup-imp.src b/source/Plugins/LanguageRuntime/ObjC/MulleObjCRuntime/mulle-objc-lookup-imp.src new file mode 100644 index 0000000000..de05256a73 --- /dev/null +++ b/source/Plugins/LanguageRuntime/ObjC/MulleObjCRuntime/mulle-objc-lookup-imp.src @@ -0,0 +1,40 @@ +extern "C" +{ + static const + struct mulle_clang_objccompilerinfo + { + unsigned int load_version; + unsigned int runtime_version; + } __mulle_objc_objccompilerinfo = + { + 14, // load version must match + 0 // 0 to not emit __load_mulle_objc + }; + + // + // this file is transformed with make-inc.sh into mulle-objc-lookup.inc + // which is then included into MulleObjCTrampolineHandler.cpp + // + // use code in mulle_objc_runtime to do the actual call + // I still use this wrapping function in lldb, because I am too lazy + // to figure out how to do this correctly (and you are probabaly too :P) + // + void *__lldb_objc_find_implementation_for_selector( void *object, + uint32_t methodid, + void *cls_or_superid, + int is_superid, + int debug) + { + extern void *mulle_objc_lldb_lookup_implementation( void *obj, + uint32_t methodid, + void *cls_or_superid, + int is_superid, + int debug); + + return( mulle_objc_lldb_lookup_implementation( object, + methodid, + cls_or_superid, + is_superid, + debug)); + } +} \ No newline at end of file diff --git a/source/Plugins/LanguageRuntime/ObjC/MulleObjCRuntime/mulle-objc-object-checker.inc b/source/Plugins/LanguageRuntime/ObjC/MulleObjCRuntime/mulle-objc-object-checker.inc new file mode 100644 index 0000000000..85908dd931 --- /dev/null +++ b/source/Plugins/LanguageRuntime/ObjC/MulleObjCRuntime/mulle-objc-object-checker.inc @@ -0,0 +1,15 @@ +"static const \n" +"struct mulle_clang_objccompilerinfo\n" +"{\n" +"unsigned int load_version;\n" +"unsigned int runtime_version;\n" +"} __mulle_objc_objccompilerinfo =\n" +"{\n" +"14, \n" +"0 \n" +"};\n" +"void %s( void *$__lldb_obj, unsigned int $__lldb_sel)\n" +"{\n" +"extern void mulle_objc_lldb_check_object( void *$__lldb_obj, unsigned int $__lldb_sel);\n" +"mulle_objc_lldb_check_object( $__lldb_obj, $__lldb_sel);\n" +"}\n" diff --git a/source/Plugins/LanguageRuntime/ObjC/MulleObjCRuntime/mulle-objc-object-checker.src b/source/Plugins/LanguageRuntime/ObjC/MulleObjCRuntime/mulle-objc-object-checker.src new file mode 100644 index 0000000000..c15adc332d --- /dev/null +++ b/source/Plugins/LanguageRuntime/ObjC/MulleObjCRuntime/mulle-objc-object-checker.src @@ -0,0 +1,20 @@ +extern "C" +{ + static const + struct mulle_clang_objccompilerinfo + { + unsigned int load_version; + unsigned int runtime_version; + } __mulle_objc_objccompilerinfo = + { + 14, // load version must match + 0 // 0 to not emit __load_mulle_objc + }; + + void %s( void *$__lldb_obj, unsigned int $__lldb_sel) + { + extern void mulle_objc_lldb_check_object( void *$__lldb_obj, unsigned int $__lldb_sel); + + mulle_objc_lldb_check_object( $__lldb_obj, $__lldb_sel); + } +} \ No newline at end of file diff --git a/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp b/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp index 70d48e5f1d..29faffdae1 100644 --- a/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp +++ b/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp @@ -540,9 +540,9 @@ TypeSP DWARFASTParserClang::ParseTypeFromDWARF(const SymbolContext &sc, LanguageType class_language = eLanguageTypeUnknown; bool is_complete_objc_class = false; - size_t calling_convention + size_t calling_convention = llvm::dwarf::CallingConvention::DW_CC_normal; - + const size_t num_attributes = die.GetAttributes(attributes); if (num_attributes > 0) { uint32_t i; @@ -591,7 +591,7 @@ TypeSP DWARFASTParserClang::ParseTypeFromDWARF(const SymbolContext &sc, case DW_AT_calling_convention: calling_convention = form_value.Unsigned(); break; - + case DW_AT_allocated: case DW_AT_associated: case DW_AT_data_location: @@ -655,9 +655,11 @@ TypeSP DWARFASTParserClang::ParseTypeFromDWARF(const SymbolContext &sc, default_accessibility = eAccessPrivate; } +// @mulle-lldb@ DWARF also check for is_complete_objc_class to fix p *expr bug > if (byte_size_valid && byte_size == 0 && type_name_cstr && - !die.HasChildren() && + die.HasChildren() == false && ! is_complete_objc_class && // (mulle-lldb) is_complete_objc_class added sc.comp_unit->GetLanguage() == eLanguageTypeObjC) { +// @mulle-lldb@ DWARF also check for is_complete_objc_class to fix p *expr bug < // Work around an issue with clang at the moment where forward // declarations for objective C classes are emitted as: // DW_TAG_structure_type [2] @@ -818,7 +820,7 @@ TypeSP DWARFASTParserClang::ParseTypeFromDWARF(const SymbolContext &sc, } return TypeSP(); } - + clang::ClassTemplateSpecializationDecl *class_specialization_decl = m_ast.CreateClassTemplateSpecializationDecl( @@ -840,7 +842,7 @@ TypeSP DWARFASTParserClang::ParseTypeFromDWARF(const SymbolContext &sc, class_language, &metadata); } } - + // Store a forward declaration to this class type in case any // parameters in any class methods need it for the clang types for // function prototypes. @@ -949,7 +951,7 @@ TypeSP DWARFASTParserClang::ParseTypeFromDWARF(const SymbolContext &sc, m_ast.SetHasExternalStorage(clang_type.GetOpaqueQualType(), true); } } - + // If we made a clang type, set the trivial abi if applicable: We only // do this for pass by value - which implies the Trivial ABI. There // isn't a way to assert that something that would normally be pass by @@ -1350,7 +1352,11 @@ TypeSP DWARFASTParserClang::ParseTypeFromDWARF(const SymbolContext &sc, clang::ObjCMethodDecl *objc_method_decl = m_ast.AddMethodToObjCObjectType( - class_opaque_type, type_name_cstr, clang_type, + class_opaque_type, type_name_cstr, +/// @mulle-objc@ add this for parameter names > + function_param_decls, +/// @mulle-objc@ add this for parameter names < + clang_type, accessibility, is_artificial, is_variadic); type_handled = objc_method_decl != NULL; if (type_handled) { @@ -1627,7 +1633,7 @@ TypeSP DWARFASTParserClang::ParseTypeFromDWARF(const SymbolContext &sc, m_ast.CreateFunctionTemplateSpecializationInfo( function_decl, func_template_decl, template_param_infos); } - + lldbassert(function_decl); if (function_decl) { @@ -3312,6 +3318,72 @@ bool DWARFASTParserClang::ParseChildMembers( return true; } +/// @mulle-lldb@ make _param function arguments again > +/* + <2><64>: Abbrev Number: 4 (DW_TAG_formal_parameter) + <65> DW_AT_location : 2 byte block: 91 68 (DW_OP_fbreg: -24) + <68> DW_AT_name : (indirect string, offset: 0x9c): _param + <6c> DW_AT_type : <0x84> ***reference to DW_TAG_pointer_type*** + <70> DW_AT_artificial : 1 + <1><84>: Abbrev Number: 5 (DW_TAG_pointer_type) + <85> DW_AT_type : <0x89> + <1><89>: Abbrev Number: 8 (DW_TAG_structure_type) + <8a> DW_AT_name : (indirect string, offset: 0xb6): p.a:b: + <8e> DW_AT_byte_size : 16 + <8f> DW_AT_decl_file : 1 + <90> DW_AT_decl_line : 9 + <2><91>: Abbrev Number: 9 (DW_TAG_member) + <92> DW_AT_name : (indirect string, offset: 0xa3): a + <96> DW_AT_type : <0xaa> + <9a> DW_AT_decl_file : 1 + <9b> DW_AT_decl_line : 9 + <9c> DW_AT_data_member_location: 0 +*/ + +bool DWARFASTParserClang::ParseMulleABIParameters( const DWARFDIE &die, + const CompilerType &compiler_type, + std::vector &function_param_types, + std::vector &function_param_decls, + clang::StorageClass storage) +{ + if( ! compiler_type.IsPointerType()) + return( false); + + CompilerType paramType; + + paramType = compiler_type.GetPointeeType(); + if( ! paramType.IsAggregateType()) + return( false); + + uint32_t i, n; + CompilerType fieldType; + std::string name; + uint64_t bit_offset_ptr; + uint32_t bitfield_bit_size_ptr; + bool is_bitfield_ptr; + + n = paramType.GetNumFields(); + for( i = 0; i < n; i++) + { + fieldType = paramType.GetFieldAtIndex( i, + name, + &bit_offset_ptr, + &bitfield_bit_size_ptr, + &is_bitfield_ptr); + function_param_types.push_back( fieldType); + + clang::ParmVarDecl *param_var_decl = + m_ast.CreateParameterDeclaration( + name.c_str(), fieldType, storage); + assert(param_var_decl); + function_param_decls.push_back(param_var_decl); + + m_ast.SetMetadataAsUserID(param_var_decl, die.GetID()); + } + return( true); +} +/// @mulle-lldb@ make _param function arguments again < + size_t DWARFASTParserClang::ParseChildParameters( CompileUnit &comp_unit, clang::DeclContext *containing_decl_ctx, const DWARFDIE &parent_die, bool skip_artificial, bool &is_static, @@ -3322,6 +3394,8 @@ size_t DWARFASTParserClang::ParseChildParameters( if (!parent_die) return 0; + bool skipRemainingParameters = false; + size_t arg_idx = 0; for (DWARFDIE die = parent_die.GetFirstChild(); die.IsValid(); die = die.GetSibling()) { @@ -3379,7 +3453,8 @@ size_t DWARFASTParserClang::ParseChildParameters( } } - bool skip = false; + bool skip = skipRemainingParameters; + if (skip_artificial && is_artificial) { // In order to determine if a C++ member function is "const" we // have to look at the const-ness of "this"... @@ -3399,6 +3474,25 @@ size_t DWARFASTParserClang::ParseChildParameters( type_quals |= clang::Qualifiers::Const; if (encoding_mask & (1u << Type::eEncodingIsVolatileUID)) type_quals |= clang::Qualifiers::Volatile; + + } + } + skip = true; + } else { + + // HACK: Objective C formal parameters "self" and "_cmd" + // are not marked as artificial in the DWARF... + CompileUnit *comp_unit = die.GetLLDBCompileUnit(); + if (comp_unit) { + switch (comp_unit->GetLanguage()) { + case eLanguageTypeObjC: + case eLanguageTypeObjC_plus_plus: + if (name && name[0] && + (strcmp(name, "self") == 0 || strcmp(name, "_cmd") == 0)) + skip = true; + break; + default: + break; } } } @@ -3420,6 +3514,31 @@ size_t DWARFASTParserClang::ParseChildParameters( m_ast.SetMetadataAsUserID(param_var_decl, die.GetID()); } } + + /// @mulle-lldb@ make _param function arguments again > + if( skip && arg_idx == 2 && name && ! strcmp( name, "_param")) + { + CompileUnit *comp_unit = die.GetLLDBCompileUnit(); + if (comp_unit) { + switch (comp_unit->GetLanguage()) { + default: + break; + case eLanguageTypeObjC: + case eLanguageTypeObjC_plus_plus: + Type *type = die.ResolveTypeUID(DIERef(param_type_die_form)); + if (type) { + ParseMulleABIParameters( die, + type->GetForwardCompilerType(), + function_param_types, + function_param_decls, + storage); + skipRemainingParameters = true; + } + break; + } + } + } + /// @mulle-lldb@ make _param function arguments again < } arg_idx++; } break; diff --git a/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.h b/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.h index 63e058d7bf..a572a65b89 100644 --- a/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.h +++ b/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.h @@ -91,6 +91,14 @@ class DWARFASTParserClang : public DWARFASTParser { lldb::AccessType &default_accessibility, bool &is_a_class, lldb_private::ClangASTImporter::LayoutInfo &layout_info); +/// @mulle-lldb@ make _param function arguments again > + bool ParseMulleABIParameters( const DWARFDIE &die, + const lldb_private::CompilerType &compiler_type, + std::vector &function_param_types, + std::vector &function_param_decls, + clang::StorageClass storage); +/// @mulle-lldb@ make _param function arguments again < + size_t ParseChildParameters(lldb_private::CompileUnit &comp_unit, clang::DeclContext *containing_decl_ctx, diff --git a/source/Symbol/ClangASTContext.cpp b/source/Symbol/ClangASTContext.cpp index 85436b630b..9f8a425606 100644 --- a/source/Symbol/ClangASTContext.cpp +++ b/source/Symbol/ClangASTContext.cpp @@ -594,9 +594,14 @@ static void ParseLangArgs(LangOptions &Opts, InputKind IK, const char *triple) { break; case InputKind::Asm: case InputKind::C: - case InputKind::ObjC: LangStd = LangStandard::lang_gnu99; break; +// @mulle-objc@ > + case InputKind::ObjC: + case InputKind::ObjCAAM: + LangStd = LangStandard::lang_c11; + break; +// @mulle-objc@ < case InputKind::CXX: case InputKind::ObjCXX: LangStd = LangStandard::lang_gnucxx98; @@ -5173,6 +5178,9 @@ lldb::Encoding ClangASTContext::GetEncoding(lldb::opaque_compiler_type_t type, case clang::BuiltinType::ObjCClass: case clang::BuiltinType::ObjCId: case clang::BuiltinType::ObjCSel: +// @mulle-objc@ > + case clang::BuiltinType::ObjCProtocol: +// @mulle-objc@ < return lldb::eEncodingUint; case clang::BuiltinType::NullPtr: @@ -8600,7 +8608,11 @@ clang::ObjCMethodDecl *ClangASTContext::AddMethodToObjCObjectType( const char *name, // the full symbol name as seen in the symbol table // (lldb::opaque_compiler_type_t type, "-[NString // stringWithCString:]") - const CompilerType &method_clang_type, lldb::AccessType access, +/// @mulle-objc@ add this for parameter names > + std::vector &function_param_decls, +/// @mulle-objc@ add this for parameter names < + const CompilerType &method_clang_type, + lldb::AccessType access, bool is_artificial, bool is_variadic) { if (!type || !method_clang_type.IsValid()) return nullptr; @@ -8688,12 +8700,23 @@ clang::ObjCMethodDecl *ClangASTContext::AddMethodToObjCObjectType( if (num_args > 0) { llvm::SmallVector params; +/// @mulle-objc@ add this for parameter names > + IdentifierInfo *identifier; +/// @mulle-objc@ add this for parameter names < for (unsigned param_index = 0; param_index < num_args; ++param_index) { +/// @mulle-objc@ add this for parameter names > + identifier = nullptr; + if( param_index < function_param_decls.size()) + identifier = function_param_decls[ param_index]->getIdentifier(); +/// @mulle-objc@ add this for parameter names < + params.push_back(clang::ParmVarDecl::Create( *ast, objc_method_decl, clang::SourceLocation(), clang::SourceLocation(), - nullptr, // anonymous +/// @mulle-objc@ add this for parameter names > + identifier, +/// @mulle-objc@ add this for parameter names < method_function_prototype->getParamType(param_index), nullptr, clang::SC_Auto, nullptr)); } diff --git a/source/Target/Process.cpp b/source/Target/Process.cpp index fb3b758912..a35a3d4d28 100644 --- a/source/Target/Process.cpp +++ b/source/Target/Process.cpp @@ -4830,6 +4830,16 @@ HandleStoppedEvent(Thread &thread, const ThreadPlanSP &thread_plan_sp, return eExpressionInterrupted; } +// @mulle-lldb@ avoid failed threadplan calling formatters that execute a threadplan again > +bool Process::isThreadPlanLocked( void) +{ + if( ! m_run_thread_plan_lock.try_lock()) + return( true); + m_run_thread_plan_lock.unlock(); + return( false); +} +// @mulle-lldb@ avoid failed threadplan calling formatters that execute a threadplan again < + ExpressionResults Process::RunThreadPlan(ExecutionContext &exe_ctx, lldb::ThreadPlanSP &thread_plan_sp, diff --git a/source/Target/ThreadPlanShouldStopHere.cpp b/source/Target/ThreadPlanShouldStopHere.cpp index 8062d8059c..7c053683a6 100644 --- a/source/Target/ThreadPlanShouldStopHere.cpp +++ b/source/Target/ThreadPlanShouldStopHere.cpp @@ -9,6 +9,8 @@ #include "lldb/Target/ThreadPlanShouldStopHere.h" #include "lldb/Symbol/Symbol.h" +#include "lldb/Target/LanguageRuntime.h" +#include "lldb/Target/Thread.h" #include "lldb/Target/RegisterContext.h" #include "lldb/Target/Thread.h" #include "lldb/Utility/Log.h" @@ -79,6 +81,33 @@ bool ThreadPlanShouldStopHere::DefaultShouldStopHereCallback( } } + // @mulle-lldb@ code from swift-lldb > + // https://github.com/apple/swift-lldb/blob/stable/source/Target/ThreadPlanShouldStopHere.cpp + // (thx to jim ingham) + // Check whether the frame we are in is a language runtime thunk, only for + // step out (or same parent): + if (operation == eFrameCompareOlder || operation == eFrameCompareSameParent) { + Symbol *symbol = frame->GetSymbolContext(eSymbolContextSymbol).symbol; + if (symbol) { + LanguageRuntime *language_runtime; + bool is_thunk = false; + ProcessSP process_sp(current_plan->GetThread().GetProcess()); + enum LanguageType languages_to_try[] = { + eLanguageTypeObjC, eLanguageTypeSwift, eLanguageTypeC_plus_plus}; + + for (enum LanguageType language : languages_to_try) { + language_runtime = process_sp->GetLanguageRuntime(language); + if (language_runtime) + is_thunk = language_runtime->IsSymbolARuntimeThunk(*symbol); + if (is_thunk) { + should_stop_here = false; + break; + } + } + } + } + // @mulle-lldb@ code from swift-lldb (thx to jim ingham) < + // Always avoid code with line number 0. // FIXME: At present the ShouldStop and the StepFromHere calculate this // independently. If this ever diff --git a/source/lldb.cpp b/source/lldb.cpp index 1be0461977..5aed08d1e5 100644 --- a/source/lldb.cpp +++ b/source/lldb.cpp @@ -46,8 +46,9 @@ const char *lldb_private::GetVersion() { // as the clang tool. static std::string g_version_str; if (g_version_str.empty()) { - g_version_str += "lldb version "; - g_version_str += CLANG_VERSION_STRING; +/// @mulle-lldb@ mulle-lldb as version string > + g_version_str += "mulle-lldb version 6.0.0.3"; +/// @mulle-lldb@ mulle-lldb as version string < const char *lldb_repo = GetLLDBRepository(); const char *lldb_rev = GetLLDBRevision();