diff --git a/.github/workflows/pr-code-format.yml b/.github/workflows/pr-code-format.yml index 05d69861e1841..5fcbee08789a9 100644 --- a/.github/workflows/pr-code-format.yml +++ b/.github/workflows/pr-code-format.yml @@ -7,6 +7,7 @@ on: pull_request: branches: - main + - gh-101657 - 'users/**' jobs: @@ -16,7 +17,7 @@ jobs: concurrency: group: ${{ github.workflow }}-${{ github.event.pull_request.number }} cancel-in-progress: true - if: github.repository == 'llvm/llvm-project' + if: github.repository == 'DhruvSrivastavaX/lldb-for-aix' steps: - name: Fetch LLVM sources uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 diff --git a/clang/lib/CodeGen/CGObjCMac.cpp b/clang/lib/CodeGen/CGObjCMac.cpp index a52c92cdbc83b..b611f4f194833 100644 --- a/clang/lib/CodeGen/CGObjCMac.cpp +++ b/clang/lib/CodeGen/CGObjCMac.cpp @@ -4948,10 +4948,14 @@ std::string CGObjCCommonMac::GetSectionName(StringRef Section, case llvm::Triple::COFF: assert(Section.starts_with("__") && "expected the name to begin with __"); return ("." + Section.substr(2) + "$B").str(); + case llvm::Triple::XCOFF: + // Hack to allow "p 10+1" on AIX for lldb + assert(Section.substr(0, 2) == "__" && + "expected the name to begin with __"); + return Section.substr(2).str(); case llvm::Triple::Wasm: case llvm::Triple::GOFF: case llvm::Triple::SPIRV: - case llvm::Triple::XCOFF: case llvm::Triple::DXContainer: llvm::report_fatal_error( "Objective-C support is unimplemented for object file format"); diff --git a/clang/test/SemaCXX/class-layout.cpp b/clang/test/SemaCXX/class-layout.cpp index 22fb34b8419c5..0931d905a9749 100644 --- a/clang/test/SemaCXX/class-layout.cpp +++ b/clang/test/SemaCXX/class-layout.cpp @@ -639,7 +639,7 @@ namespace PR37275 { #pragma pack(pop) } -#endif // !defined(__MVS__) && !defined(__AIX__) +#endif // !defined(__MVS__) && !defined(_AIX) namespace non_pod { struct t1 { diff --git a/lldb/include/lldb/Core/Module.h b/lldb/include/lldb/Core/Module.h index 8bb55c95773bc..ab6d14301e2f6 100644 --- a/lldb/include/lldb/Core/Module.h +++ b/lldb/include/lldb/Core/Module.h @@ -197,6 +197,9 @@ class Module : public std::enable_shared_from_this, bool SetLoadAddress(Target &target, lldb::addr_t value, bool value_is_offset, bool &changed); + bool SetLoadAddressByType(Target &target, lldb::addr_t value, + bool value_is_offset, bool &changed, int type_id); + /// \copydoc SymbolContextScope::CalculateSymbolContext(SymbolContext*) /// /// \see SymbolContextScope diff --git a/lldb/include/lldb/Core/ModuleSpec.h b/lldb/include/lldb/Core/ModuleSpec.h index 86be0383f8b47..be7c23ec62fb0 100644 --- a/lldb/include/lldb/Core/ModuleSpec.h +++ b/lldb/include/lldb/Core/ModuleSpec.h @@ -21,6 +21,7 @@ #include #include +#include namespace lldb_private { @@ -41,8 +42,26 @@ class ModuleSpec { } ModuleSpec(const FileSpec &file_spec, const ArchSpec &arch) - : m_file(file_spec), m_arch(arch), m_object_offset(0), - m_object_size(FileSystem::Instance().GetByteSize(file_spec)) {} + : m_arch(arch), m_object_offset(0) { + // parse object inside module format for example: /usr/ccs/lib/libc.a(shr_64.o) + llvm::SmallString<256> path_with_object; + file_spec.GetPath(path_with_object); + if (strstr(path_with_object.c_str(), "(") != nullptr) { + char *part; + char *str = (char *)path_with_object.c_str(); + part = strtok(str, "()"); + assert(part); + llvm::StringRef file_name(part); + part = strtok(nullptr, "()"); + assert(part); + m_object_name = ConstString(part); + m_file = FileSpec(file_name); + m_object_size = FileSystem::Instance().GetByteSize(m_file); + } else { + m_file = file_spec; + m_object_size = FileSystem::Instance().GetByteSize(file_spec); + } + } FileSpec *GetFileSpecPtr() { return (m_file ? &m_file : nullptr); } @@ -103,6 +122,8 @@ class ModuleSpec { ConstString &GetObjectName() { return m_object_name; } ConstString GetObjectName() const { return m_object_name; } + + void SetObjectName(ConstString objName) { m_object_name = objName; } uint64_t GetObjectOffset() const { return m_object_offset; } diff --git a/lldb/include/lldb/Host/HostInfoBase.h b/lldb/include/lldb/Host/HostInfoBase.h index b6a95fffb2db2..13dddb473aba8 100644 --- a/lldb/include/lldb/Host/HostInfoBase.h +++ b/lldb/include/lldb/Host/HostInfoBase.h @@ -151,6 +151,7 @@ class HostInfoBase { return {}; } + static bool ComputeSharedLibraryDirectory(FileSpec &file_spec); /// Returns the distribution id of the host /// /// This will be something like "ubuntu", "fedora", etc. on Linux. @@ -160,7 +161,6 @@ class HostInfoBase { static llvm::StringRef GetDistributionId() { return llvm::StringRef(); } protected: - static bool ComputeSharedLibraryDirectory(FileSpec &file_spec); static bool ComputeSupportExeDirectory(FileSpec &file_spec); static bool ComputeProcessTempFileDirectory(FileSpec &file_spec); static bool ComputeGlobalTempFileDirectory(FileSpec &file_spec); diff --git a/lldb/include/lldb/Host/XML.h b/lldb/include/lldb/Host/XML.h index da0f9cd7aa8c0..483589f1abc75 100644 --- a/lldb/include/lldb/Host/XML.h +++ b/lldb/include/lldb/Host/XML.h @@ -11,6 +11,11 @@ #include "lldb/Host/Config.h" +#if defined(_AIX) +//FIXME for AIX +#undef LLDB_ENABLE_LIBXML2 +#endif + #if LLDB_ENABLE_LIBXML2 #include #endif diff --git a/lldb/include/lldb/Host/aix/Host.h b/lldb/include/lldb/Host/aix/Host.h new file mode 100644 index 0000000000000..1e3487752995f --- /dev/null +++ b/lldb/include/lldb/Host/aix/Host.h @@ -0,0 +1,22 @@ +//===-- Host.h --------------------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_HOST_AIX_HOST_H +#define LLDB_HOST_AIX_HOST_H + +#include "lldb/lldb-types.h" +#include + +namespace lldb_private { + +// Get PID (i.e. the primary thread ID) corresponding to the specified TID. +std::optional getPIDForTID(lldb::pid_t tid); + +} // namespace lldb_private + +#endif // #ifndef LLDB_HOST_AIX_HOST_H diff --git a/lldb/include/lldb/Host/aix/Ptrace.h b/lldb/include/lldb/Host/aix/Ptrace.h new file mode 100644 index 0000000000000..88928f18102d7 --- /dev/null +++ b/lldb/include/lldb/Host/aix/Ptrace.h @@ -0,0 +1,62 @@ +//===-- Ptrace.h ------------------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// This file defines ptrace functions & structures + +#ifndef liblldb_Host_aix_Ptrace_h_ +#define liblldb_Host_aix_Ptrace_h_ + +#include + +#define DEBUG_PTRACE_MAXBYTES 20 + +// Support ptrace extensions even when compiled without required kernel support +#ifndef PTRACE_GETREGS +#define PTRACE_GETREGS (PT_COMMAND_MAX+1) +#endif +#ifndef PTRACE_SETREGS +#define PTRACE_SETREGS (PT_COMMAND_MAX+2) +#endif +#ifndef PTRACE_GETFPREGS +#define PTRACE_GETFPREGS (PT_COMMAND_MAX+3) +#endif +#ifndef PTRACE_SETFPREGS +#define PTRACE_SETFPREGS (PT_COMMAND_MAX+4) +#endif +#ifndef PTRACE_GETREGSET +#define PTRACE_GETREGSET 0x4204 +#endif +#ifndef PTRACE_SETREGSET +#define PTRACE_SETREGSET 0x4205 +#endif +#ifndef PTRACE_GET_THREAD_AREA +#define PTRACE_GET_THREAD_AREA (PT_COMMAND_MAX+5) +#endif +#ifndef PTRACE_ARCH_PRCTL +#define PTRACE_ARCH_PRCTL (PT_COMMAND_MAX+6) +#endif +#ifndef ARCH_GET_FS +#define ARCH_SET_GS 0x1001 +#define ARCH_SET_FS 0x1002 +#define ARCH_GET_FS 0x1003 +#define ARCH_GET_GS 0x1004 +#endif +#ifndef PTRACE_PEEKMTETAGS +#define PTRACE_PEEKMTETAGS (PT_COMMAND_MAX+7) +#endif +#ifndef PTRACE_POKEMTETAGS +#define PTRACE_POKEMTETAGS (PT_COMMAND_MAX+8) +#endif +#ifndef PTRACE_GETVRREGS +#define PTRACE_GETVRREGS (PT_COMMAND_MAX+9) +#endif +#ifndef PTRACE_GETVSRREGS +#define PTRACE_GETVSRREGS (PT_COMMAND_MAX+10) +#endif + +#endif // liblldb_Host_aix_Ptrace_h_ diff --git a/lldb/include/lldb/Host/common/GetOptInc.h b/lldb/include/lldb/Host/common/GetOptInc.h index c9c9e2496d5f9..652e6174ff8b6 100644 --- a/lldb/include/lldb/Host/common/GetOptInc.h +++ b/lldb/include/lldb/Host/common/GetOptInc.h @@ -35,7 +35,7 @@ struct option { int val; }; -int getopt(int argc, char *const argv[], const char *optstring); +int getopt(int argc, char *const argv[], const char *optstring) throw(); // from getopt.h extern char *optarg; diff --git a/lldb/include/lldb/Symbol/ObjectFile.h b/lldb/include/lldb/Symbol/ObjectFile.h index 1b9ae1fb31a69..fb4c4212c7d6f 100644 --- a/lldb/include/lldb/Symbol/ObjectFile.h +++ b/lldb/include/lldb/Symbol/ObjectFile.h @@ -407,6 +407,11 @@ class ObjectFile : public std::enable_shared_from_this, return false; } + virtual bool SetLoadAddressByType(Target &target, lldb::addr_t value, + bool value_is_offset, int type_id) { + return false; + } + /// Gets whether endian swapping should occur when extracting data from this /// object file. /// diff --git a/lldb/include/lldb/Target/ABI.h b/lldb/include/lldb/Target/ABI.h index 1a1f1724222e3..6b8eea31bbb7c 100644 --- a/lldb/include/lldb/Target/ABI.h +++ b/lldb/include/lldb/Target/ABI.h @@ -48,6 +48,12 @@ class ABI : public PluginInterface { lldb::addr_t returnAddress, llvm::ArrayRef args) const = 0; + virtual bool PrepareTrivialCall(lldb_private::Thread &thread, lldb::addr_t sp, + lldb::addr_t functionAddress, + lldb::addr_t tocAddress, + lldb::addr_t returnAddress, + llvm::ArrayRef args) const; + // Prepare trivial call used from ThreadPlanFunctionCallUsingABI // AD: // . Because i don't want to change other ABI's this is not declared pure diff --git a/lldb/include/lldb/Target/DynamicLoader.h b/lldb/include/lldb/Target/DynamicLoader.h index 75bb6cb6bb907..32d74eac28224 100644 --- a/lldb/include/lldb/Target/DynamicLoader.h +++ b/lldb/include/lldb/Target/DynamicLoader.h @@ -371,6 +371,12 @@ class DynamicLoader : public PluginInterface { lldb::addr_t base_addr, bool base_addr_is_offset); + virtual void UpdateLoadedSectionsByType(lldb::ModuleSP module, + lldb::addr_t link_map_addr, + lldb::addr_t base_addr, + bool base_addr_is_offset, + int type_id); + // Utility method so base classes can share implementation of // UpdateLoadedSections void UpdateLoadedSectionsCommon(lldb::ModuleSP module, lldb::addr_t base_addr, diff --git a/lldb/include/lldb/Target/Process.h b/lldb/include/lldb/Target/Process.h index a8892e9c43225..97e4ecd943e4b 100644 --- a/lldb/include/lldb/Target/Process.h +++ b/lldb/include/lldb/Target/Process.h @@ -65,6 +65,10 @@ #include "llvm/Support/Threading.h" #include "llvm/Support/VersionTuple.h" +#if defined(_AIX) +struct ld_xinfo; +#endif + namespace lldb_private { template struct Range; @@ -1952,6 +1956,10 @@ class Process : public std::enable_shared_from_this, Status GetMemoryRegionInfo(lldb::addr_t load_addr, MemoryRegionInfo &range_info); +#if defined(_AIX) + Status GetLDXINFO(struct ld_xinfo *info_ptr); +#endif + /// Obtain all the mapped memory regions within this process. /// /// \param[out] region_list @@ -2899,6 +2907,12 @@ void PruneThreadPlans(); "Process::DoGetMemoryRegionInfo() not supported"); } +#if defined(_AIX) + virtual Status DoGetLDXINFO(struct ld_xinfo *info_ptr) { + return Status("Process::DoGetLDXINFO() not supported"); + } +#endif + /// Provide an override value in the subclass for lldb's /// CPU-based logic for whether watchpoint exceptions are /// received before or after an instruction executes. diff --git a/lldb/include/lldb/Target/RegisterContextUnwind.h b/lldb/include/lldb/Target/RegisterContextUnwind.h index b10a364823b83..bb74de8d3d027 100644 --- a/lldb/include/lldb/Target/RegisterContextUnwind.h +++ b/lldb/include/lldb/Target/RegisterContextUnwind.h @@ -67,6 +67,10 @@ class RegisterContextUnwind : public lldb_private::RegisterContext { bool ReadPC(lldb::addr_t &start_pc); +#ifdef _AIX + bool ReadLR(lldb::addr_t &lr); +#endif + // Indicates whether this frame *behaves* like frame zero -- the currently // executing frame -- or not. This can be true in the middle of the stack // above asynchronous trap handlers (sigtramp) for instance. diff --git a/lldb/include/lldb/Target/Target.h b/lldb/include/lldb/Target/Target.h index 0d4e11b65339e..fbed369ce5796 100644 --- a/lldb/include/lldb/Target/Target.h +++ b/lldb/include/lldb/Target/Target.h @@ -538,6 +538,7 @@ class Target : public std::enable_shared_from_this, eBroadcastBitSymbolsChanged = (1 << 5), }; + // These two functions fill out the Broadcaster interface: static llvm::StringRef GetStaticBroadcasterClass(); @@ -1675,6 +1676,10 @@ class Target : public std::enable_shared_from_this, TargetStats &GetStatistics() { return m_stats; } +public: + SectionLoadList &GetSectionLoadListPublic() { + return GetSectionLoadList(); + } protected: /// Construct with optional file and arch. /// diff --git a/lldb/include/lldb/Target/ThreadPlanCallFunction.h b/lldb/include/lldb/Target/ThreadPlanCallFunction.h index cb6e7caebb4ad..7880db1592e04 100644 --- a/lldb/include/lldb/Target/ThreadPlanCallFunction.h +++ b/lldb/include/lldb/Target/ThreadPlanCallFunction.h @@ -27,6 +27,12 @@ class ThreadPlanCallFunction : public ThreadPlan { llvm::ArrayRef args, const EvaluateExpressionOptions &options); + ThreadPlanCallFunction(Thread &thread, const Address &function, + const Address &toc, + const CompilerType &return_type, + llvm::ArrayRef args, + const EvaluateExpressionOptions &options); + ThreadPlanCallFunction(Thread &thread, const Address &function, const EvaluateExpressionOptions &options); diff --git a/lldb/include/lldb/Utility/StringExtractorGDBRemote.h b/lldb/include/lldb/Utility/StringExtractorGDBRemote.h index dd468ef5bddef..9953bd6c24588 100644 --- a/lldb/include/lldb/Utility/StringExtractorGDBRemote.h +++ b/lldb/include/lldb/Utility/StringExtractorGDBRemote.h @@ -61,6 +61,7 @@ class StringExtractorGDBRemote : public StringExtractor { eServerPacketType_qQueryGDBServer, eServerPacketType_qKillSpawnedProcess, eServerPacketType_qLaunchSuccess, + eServerPacketType_qLDXINFO, eServerPacketType_qModuleInfo, eServerPacketType_qProcessInfoPID, eServerPacketType_qSpeedTest, diff --git a/lldb/source/API/CMakeLists.txt b/lldb/source/API/CMakeLists.txt index 4751ed319b259..1bd53355d9e4c 100644 --- a/lldb/source/API/CMakeLists.txt +++ b/lldb/source/API/CMakeLists.txt @@ -40,6 +40,115 @@ add_custom_target(lldb-sbapi-dwarf-enums DEPENDS ${sb_languages_file}) set_target_properties(lldb-sbapi-dwarf-enums PROPERTIES FOLDER "LLDB/Tablegenning") +if(CMAKE_SYSTEM_NAME MATCHES "AIX") +add_lldb_library(liblldb STATIC ${option_framework} + SBAddress.cpp + SBAddressRange.cpp + SBAddressRangeList.cpp + SBAttachInfo.cpp + SBBlock.cpp + SBBreakpoint.cpp + SBBreakpointLocation.cpp + SBBreakpointName.cpp + SBBreakpointOptionCommon.cpp + SBBroadcaster.cpp + SBCommandInterpreter.cpp + SBCommandInterpreterRunOptions.cpp + SBCommandReturnObject.cpp + SBCommunication.cpp + SBCompileUnit.cpp + SBSaveCoreOptions.cpp + SBData.cpp + SBDebugger.cpp + SBDeclaration.cpp + SBEnvironment.cpp + SBError.cpp + SBEvent.cpp + SBExecutionContext.cpp + SBExpressionOptions.cpp + SBFileSpec.cpp + SBFile.cpp + SBFileSpecList.cpp + SBFormat.cpp + SBFrame.cpp + SBFunction.cpp + SBHostOS.cpp + SBInstruction.cpp + SBInstructionList.cpp + SBLanguageRuntime.cpp + SBLaunchInfo.cpp + SBLineEntry.cpp + SBListener.cpp + SBMemoryRegionInfo.cpp + SBMemoryRegionInfoList.cpp + SBModule.cpp + SBModuleSpec.cpp + SBMutex.cpp + SBPlatform.cpp + SBProcess.cpp + SBProgress.cpp + SBProcessInfo.cpp + SBProcessInfoList.cpp + SBQueue.cpp + SBQueueItem.cpp + SBReproducer.cpp + SBScriptObject.cpp + SBSection.cpp + SBSourceManager.cpp + SBStatisticsOptions.cpp + SBStream.cpp + SBStringList.cpp + SBStructuredData.cpp + SBSymbol.cpp + SBSymbolContext.cpp + SBSymbolContextList.cpp + SBTarget.cpp + SBThread.cpp + SBThreadCollection.cpp + SBThreadPlan.cpp + SBTrace.cpp + SBTraceCursor.cpp + SBType.cpp + SBTypeCategory.cpp + SBTypeEnumMember.cpp + SBTypeFilter.cpp + SBTypeFormat.cpp + SBTypeNameSpecifier.cpp + SBTypeSummary.cpp + SBTypeSynthetic.cpp + SBValue.cpp + SBValueList.cpp + SBVariablesOptions.cpp + SBWatchpoint.cpp + SBWatchpointOptions.cpp + SBUnixSignals.cpp + SystemInitializerFull.cpp + ${lldb_python_wrapper} + ${lldb_lua_wrapper} + + DEPENDS + lldb-sbapi-dwarf-enums + + LINK_LIBS + lldbBreakpoint + lldbCore + lldbDataFormatters + lldbExpression + lldbHost + lldbInitialization + lldbInterpreter + lldbSymbol + lldbTarget + lldbUtility + lldbVersion + ${LLDB_ALL_PLUGINS} + LINK_COMPONENTS + Support + + ${option_install_prefix} +) + +else() add_lldb_library(liblldb SHARED ${option_framework} SBAddress.cpp SBAddressRange.cpp @@ -149,6 +258,7 @@ add_lldb_library(liblldb SHARED ${option_framework} ${option_install_prefix} ) +endif() # lib/pythonX.Y/dist-packages/lldb/_lldb.so is a symlink to lib/liblldb.so, # which depends on lib/libLLVM*.so (BUILD_SHARED_LIBS) or lib/libLLVM-10git.so diff --git a/lldb/source/Core/DynamicLoader.cpp b/lldb/source/Core/DynamicLoader.cpp index 4be9f3eb9abc5..d2ed92f8b0c97 100644 --- a/lldb/source/Core/DynamicLoader.cpp +++ b/lldb/source/Core/DynamicLoader.cpp @@ -117,6 +117,16 @@ void DynamicLoader::UpdateLoadedSections(ModuleSP module, addr_t link_map_addr, UpdateLoadedSectionsCommon(module, base_addr, base_addr_is_offset); } +void DynamicLoader::UpdateLoadedSectionsByType(lldb::ModuleSP module, + lldb::addr_t link_map_addr, + lldb::addr_t base_addr, + bool base_addr_is_offset, + int type_id) { + bool changed; + module->SetLoadAddressByType(m_process->GetTarget(), base_addr, base_addr_is_offset, + changed, type_id); +} + void DynamicLoader::UpdateLoadedSectionsCommon(ModuleSP module, addr_t base_addr, bool base_addr_is_offset) { diff --git a/lldb/source/Core/Mangled.cpp b/lldb/source/Core/Mangled.cpp index 0027bfaeda4f7..b77edebc059f1 100644 --- a/lldb/source/Core/Mangled.cpp +++ b/lldb/source/Core/Mangled.cpp @@ -182,6 +182,7 @@ GetItaniumDemangledStr(const char *M) { "Expected demangled_size to return length including trailing null"); } +#if !defined(_AIX) if (Log *log = GetLog(LLDBLog::Demangle)) { if (demangled_cstr) LLDB_LOGF(log, "demangled itanium: %s -> \"%s\"", M, demangled_cstr); @@ -193,6 +194,7 @@ GetItaniumDemangledStr(const char *M) { "demangled itanium: %s -> error: failed to retrieve name info", M); } +#endif return {demangled_cstr, std::move(info)}; } diff --git a/lldb/source/Core/Module.cpp b/lldb/source/Core/Module.cpp index 90997dada3666..ac86791200653 100644 --- a/lldb/source/Core/Module.cpp +++ b/lldb/source/Core/Module.cpp @@ -1497,6 +1497,18 @@ bool Module::SetLoadAddress(Target &target, lldb::addr_t value, return false; } +bool Module::SetLoadAddressByType(Target &target, lldb::addr_t value, + bool value_is_offset, bool &changed, int type_id) { + ObjectFile *object_file = GetObjectFile(); + if (object_file != nullptr) { + changed = object_file->SetLoadAddressByType(target, value, value_is_offset, type_id); + return true; + } else { + changed = false; + } + return false; +} + bool Module::MatchesModuleSpec(const ModuleSpec &module_ref) { const UUID &uuid = module_ref.GetUUID(); diff --git a/lldb/source/Core/ModuleList.cpp b/lldb/source/Core/ModuleList.cpp index d5ddf6e846112..bf9b081a47539 100644 --- a/lldb/source/Core/ModuleList.cpp +++ b/lldb/source/Core/ModuleList.cpp @@ -260,7 +260,17 @@ void ModuleList::ReplaceEquivalent( module_sp->GetArchitecture()); equivalent_module_spec.GetPlatformFileSpec() = module_sp->GetPlatformFileSpec(); - +#ifdef _AIX + // To remove the exact equivalent module, the object name must be + // specified. When the equivalent_module_spec object is created, its + // object name is initially set to NULL. This is because the module_sp's + // GetPath() returns a path in the format (/usr/ccs/libc.a), which does + // not include the object name. As a result, MatchesModuleSpec may return + // true even though the object name is NULL and doesn't match any loaded + // module. To fix this, set the object name of the equivalent_module_spec + // to be the same as the object name of the module_sp. */ + equivalent_module_spec.SetObjectName(module_sp->GetObjectName()); +#endif size_t idx = 0; while (idx < m_modules.size()) { ModuleSP test_module_sp(m_modules[idx]); diff --git a/lldb/source/Core/Section.cpp b/lldb/source/Core/Section.cpp index 27dcf987b0278..d86b76f310155 100644 --- a/lldb/source/Core/Section.cpp +++ b/lldb/source/Core/Section.cpp @@ -266,6 +266,10 @@ bool Section::ResolveContainedAddress(addr_t offset, Address &so_addr, bool Section::ContainsFileAddress(addr_t vm_addr) const { const addr_t file_addr = GetFileAddress(); +#ifdef _AIX + if (file_addr == 0) + return false; +#endif if (file_addr != LLDB_INVALID_ADDRESS && !IsThreadSpecific()) { if (file_addr <= vm_addr) { const addr_t offset = (vm_addr - file_addr) * m_target_byte_size; diff --git a/lldb/source/Host/CMakeLists.txt b/lldb/source/Host/CMakeLists.txt index b15d72e61b6e5..48e6c2bd8251f 100644 --- a/lldb/source/Host/CMakeLists.txt +++ b/lldb/source/Host/CMakeLists.txt @@ -144,7 +144,7 @@ else() aix/Host.cpp aix/HostInfoAIX.cpp aix/Support.cpp - ) + ) endif() endif() diff --git a/lldb/source/Host/common/GetOptInc.cpp b/lldb/source/Host/common/GetOptInc.cpp index c2044b6873221..e0ae2aa1774b3 100644 --- a/lldb/source/Host/common/GetOptInc.cpp +++ b/lldb/source/Host/common/GetOptInc.cpp @@ -409,7 +409,7 @@ static int getopt_internal(int nargc, char *const *nargv, const char *options, * [eventually this will replace the BSD getopt] */ #if defined(REPLACE_GETOPT) -int getopt(int nargc, char *const *nargv, const char *options) { +int getopt(int nargc, char *const *nargv, const char *options) throw() { /* * We don't pass FLAG_PERMUTE to getopt_internal() since diff --git a/lldb/source/Host/common/Host.cpp b/lldb/source/Host/common/Host.cpp index 5992b54318f73..7444163366fe5 100644 --- a/lldb/source/Host/common/Host.cpp +++ b/lldb/source/Host/common/Host.cpp @@ -1,4 +1,4 @@ -//===-- Host.cpp ----------------------------------------------------------===// + // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -20,6 +20,7 @@ #include #include #include + #include #endif @@ -343,8 +344,31 @@ bool Host::ResolveExecutableInBundle(FileSpec &file) { return false; } #ifndef _WIN32 +#if defined(_AIX) + +#include +extern char **p_xargv; + +#endif + FileSpec Host::GetModuleFileSpecForHostAddress(const void *host_addr) { FileSpec module_filespec; +#ifdef _AIX + // TODO: As the current AIX LLDB is static, we don't need dladdr which is + // only for shared library, Below is the hack to find the module name + // for static LLDB + // FIXME: If LLDB is later built as shared library, we have to find the way simillar to dladdr + // since AIX does not support the dladdr API. + if (host_addr == reinterpret_cast(HostInfoBase::ComputeSharedLibraryDirectory)) { + // FIXME: AIX dladdr return "lldb" for this case + if (p_xargv[0]) { + module_filespec.SetFile(p_xargv[0], FileSpec::Style::native); + FileSystem::Instance().Resolve(module_filespec); + return module_filespec; + } + } +#else +#if !defined(__ANDROID__) Dl_info info; if (::dladdr(host_addr, &info)) { if (info.dli_fname) { @@ -352,17 +376,13 @@ FileSpec Host::GetModuleFileSpecForHostAddress(const void *host_addr) { FileSystem::Instance().Resolve(module_filespec); } } +#endif +#endif return module_filespec; } #endif -#if !defined(__linux__) -bool Host::FindProcessThreads(const lldb::pid_t pid, TidMap &tids_to_attach) { - return false; -} -#endif - struct ShellInfo { ShellInfo() : process_reaped(false) {} diff --git a/lldb/source/Host/common/XML.cpp b/lldb/source/Host/common/XML.cpp index f480ef3166a44..fbc409105fe60 100644 --- a/lldb/source/Host/common/XML.cpp +++ b/lldb/source/Host/common/XML.cpp @@ -10,6 +10,9 @@ #include "lldb/Host/XML.h" #include "llvm/ADT/StringExtras.h" +#if defined(_AIX) +#undef LLDB_ENABLE_LIBXML2 +#endif using namespace lldb; using namespace lldb_private; diff --git a/lldb/source/Initialization/CMakeLists.txt b/lldb/source/Initialization/CMakeLists.txt index 26343f93ecfea..a6ad0ad625a26 100644 --- a/lldb/source/Initialization/CMakeLists.txt +++ b/lldb/source/Initialization/CMakeLists.txt @@ -1,4 +1,4 @@ -if ( CMAKE_SYSTEM_NAME MATCHES "Linux|Android|FreeBSD|NetBSD|OpenBSD" ) +if ( CMAKE_SYSTEM_NAME MATCHES "Linux|Android|FreeBSD|NetBSD|OpenBSD|AIX" ) list(APPEND EXTRA_PLUGINS lldbPluginProcessPOSIX) endif() diff --git a/lldb/source/Initialization/SystemInitializerCommon.cpp b/lldb/source/Initialization/SystemInitializerCommon.cpp index 1a172a95aa147..2e2d622d9981c 100644 --- a/lldb/source/Initialization/SystemInitializerCommon.cpp +++ b/lldb/source/Initialization/SystemInitializerCommon.cpp @@ -19,7 +19,7 @@ #include "lldb/Version/Version.h" #if defined(__linux__) || defined(__FreeBSD__) || defined(__NetBSD__) || \ - defined(__OpenBSD__) + defined(__OpenBSD__) || defined(_AIX) #include "Plugins/Process/POSIX/ProcessPOSIXLog.h" #endif @@ -79,7 +79,7 @@ llvm::Error SystemInitializerCommon::Initialize() { process_gdb_remote::ProcessGDBRemoteLog::Initialize(); #if defined(__linux__) || defined(__FreeBSD__) || defined(__NetBSD__) || \ - defined(__OpenBSD__) + defined(__OpenBSD__) || defined(_AIX) ProcessPOSIXLog::Initialize(); #endif #if defined(_WIN32) diff --git a/lldb/source/Plugins/ABI/PowerPC/ABISysV_ppc64.cpp b/lldb/source/Plugins/ABI/PowerPC/ABISysV_ppc64.cpp index f5327a1f403c0..b4805ff41071d 100644 --- a/lldb/source/Plugins/ABI/PowerPC/ABISysV_ppc64.cpp +++ b/lldb/source/Plugins/ABI/PowerPC/ABISysV_ppc64.cpp @@ -156,6 +156,9 @@ bool ABISysV_ppc64::PrepareTrivialCall(Thread &thread, addr_t sp, if (!reg_ctx->WriteRegisterFromUnsigned(r12_reg_info, func_addr)) return false; +#if defined(_AIX) + assert(0); +#else // Read TOC pointer value. reg_value = reg_ctx->ReadRegisterAsUnsigned(r2_reg_info, 0); @@ -171,6 +174,132 @@ bool ABISysV_ppc64::PrepareTrivialCall(Thread &thread, addr_t sp, (uint64_t)reg_value); if (!process_sp->WritePointerToMemory(sp + stack_offset, reg_value, error)) return false; +#endif + + // Read the current SP value. + reg_value = reg_ctx->ReadRegisterAsUnsigned(sp_reg_info, 0); + + // Save current SP onto the stack. + LLDB_LOGF(log, "Writing SP at SP(0x%" PRIx64 ")+0: 0x%" PRIx64, (uint64_t)sp, + (uint64_t)reg_value); + if (!process_sp->WritePointerToMemory(sp, reg_value, error)) + return false; + + // %r1 is set to the actual stack value. + LLDB_LOGF(log, "Writing SP: 0x%" PRIx64, (uint64_t)sp); + + if (!reg_ctx->WriteRegisterFromUnsigned(sp_reg_info, sp)) + return false; + + // %pc is set to the address of the called function. + + LLDB_LOGF(log, "Writing IP: 0x%" PRIx64, (uint64_t)func_addr); + + if (!reg_ctx->WriteRegisterFromUnsigned(pc_reg_info, func_addr)) + return false; + + return true; +} + +bool ABISysV_ppc64::PrepareTrivialCall(Thread &thread, addr_t sp, + addr_t func_addr, addr_t toc_addr, + addr_t return_addr, + llvm::ArrayRef args) const { + Log *log = GetLog(LLDBLog::Expressions); + + if (log) { + StreamString s; + s.Printf("ABISysV_ppc64::PrepareTrivialCall (tid = 0x%" PRIx64 + ", sp = 0x%" PRIx64 ", func_addr = 0x%" PRIx64 + ", return_addr = 0x%" PRIx64, + thread.GetID(), (uint64_t)sp, (uint64_t)func_addr, + (uint64_t)return_addr); + + for (size_t i = 0; i < args.size(); ++i) + s.Printf(", arg%" PRIu64 " = 0x%" PRIx64, static_cast(i + 1), + args[i]); + s.PutCString(")"); + log->PutString(s.GetString()); + } + + RegisterContext *reg_ctx = thread.GetRegisterContext().get(); + if (!reg_ctx) + return false; + + const RegisterInfo *reg_info = nullptr; + + if (args.size() > 8) // TODO handle more than 8 arguments + return false; + + for (size_t i = 0; i < args.size(); ++i) { + reg_info = reg_ctx->GetRegisterInfo(eRegisterKindGeneric, + LLDB_REGNUM_GENERIC_ARG1 + i); + LLDB_LOGF(log, "About to write arg%" PRIu64 " (0x%" PRIx64 ") into %s", + static_cast(i + 1), args[i], reg_info->name); + if (!reg_ctx->WriteRegisterFromUnsigned(reg_info, args[i])) + return false; + } + + // First, align the SP + + LLDB_LOGF(log, "16-byte aligning SP: 0x%" PRIx64 " to 0x%" PRIx64, + (uint64_t)sp, (uint64_t)(sp & ~0xfull)); + + sp &= ~(0xfull); // 16-byte alignment + + sp -= 544; // allocate frame to save TOC, RA and SP. + + Status error; + uint64_t reg_value; + const RegisterInfo *pc_reg_info = + reg_ctx->GetRegisterInfo(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC); + const RegisterInfo *sp_reg_info = + reg_ctx->GetRegisterInfo(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_SP); + ProcessSP process_sp(thread.GetProcess()); + const RegisterInfo *lr_reg_info = + reg_ctx->GetRegisterInfo(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_RA); + const RegisterInfo *r2_reg_info = reg_ctx->GetRegisterInfoAtIndex(2); + const RegisterInfo *r12_reg_info = reg_ctx->GetRegisterInfoAtIndex(12); + + // Save return address onto the stack. + LLDB_LOGF(log, + "Pushing the return address onto the stack: 0x%" PRIx64 + "(+16): 0x%" PRIx64, + (uint64_t)sp, (uint64_t)return_addr); + if (!process_sp->WritePointerToMemory(sp + 16, return_addr, error)) + return false; + + // Write the return address to link register. + LLDB_LOGF(log, "Writing LR: 0x%" PRIx64, (uint64_t)return_addr); + if (!reg_ctx->WriteRegisterFromUnsigned(lr_reg_info, return_addr)) + return false; + + // Write target address to %r12 register. + LLDB_LOGF(log, "Writing R12: 0x%" PRIx64, (uint64_t)func_addr); + if (!reg_ctx->WriteRegisterFromUnsigned(r12_reg_info, func_addr)) + return false; + +#if defined(_AIX) + LLDB_LOGF(log, "Writing R2: 0x%" PRIx64, (uint64_t)toc_addr); + if (!reg_ctx->WriteRegisterFromUnsigned(r2_reg_info, toc_addr)) + return false; +#else + // Read TOC pointer value. + reg_value = reg_ctx->ReadRegisterAsUnsigned(r2_reg_info, 0); + + // Write TOC pointer onto the stack. + uint64_t stack_offset; + if (GetByteOrder() == lldb::eByteOrderLittle) + stack_offset = 24; + else + stack_offset = 40; + + LLDB_LOGF(log, "Writing R2 (TOC) at SP(0x%" PRIx64 ")+%d: 0x%" PRIx64, + (uint64_t)(sp + stack_offset), (int)stack_offset, + (uint64_t)reg_value); + if (!process_sp->WritePointerToMemory(sp + stack_offset, reg_value, error)) + return false; +#endif // Read the current SP value. reg_value = reg_ctx->ReadRegisterAsUnsigned(sp_reg_info, 0); diff --git a/lldb/source/Plugins/ABI/PowerPC/ABISysV_ppc64.h b/lldb/source/Plugins/ABI/PowerPC/ABISysV_ppc64.h index c7885eb7bb100..1afdd6cb07481 100644 --- a/lldb/source/Plugins/ABI/PowerPC/ABISysV_ppc64.h +++ b/lldb/source/Plugins/ABI/PowerPC/ABISysV_ppc64.h @@ -24,6 +24,12 @@ class ABISysV_ppc64 : public lldb_private::RegInfoBasedABI { lldb::addr_t returnAddress, llvm::ArrayRef args) const override; + bool PrepareTrivialCall(lldb_private::Thread &thread, lldb::addr_t sp, + lldb::addr_t functionAddress, + lldb::addr_t tocAddress, + lldb::addr_t returnAddress, + llvm::ArrayRef args) const override; + bool GetArgumentValues(lldb_private::Thread &thread, lldb_private::ValueList &values) const override; diff --git a/lldb/source/Plugins/DynamicLoader/AIX-DYLD/CMakeLists.txt b/lldb/source/Plugins/DynamicLoader/AIX-DYLD/CMakeLists.txt new file mode 100644 index 0000000000000..02fe0d617955a --- /dev/null +++ b/lldb/source/Plugins/DynamicLoader/AIX-DYLD/CMakeLists.txt @@ -0,0 +1,11 @@ +add_definitions("-D_ALL_SOURCE") + +add_lldb_library(lldbPluginDynamicLoaderAIXDYLD PLUGIN + DynamicLoaderAIXDYLD.cpp + + LINK_LIBS + lldbCore + lldbTarget + LINK_COMPONENTS + Support + ) diff --git a/lldb/source/Plugins/DynamicLoader/AIX-DYLD/DynamicLoaderAIXDYLD.cpp b/lldb/source/Plugins/DynamicLoader/AIX-DYLD/DynamicLoaderAIXDYLD.cpp new file mode 100644 index 0000000000000..12f24c049f373 --- /dev/null +++ b/lldb/source/Plugins/DynamicLoader/AIX-DYLD/DynamicLoaderAIXDYLD.cpp @@ -0,0 +1,433 @@ +//===-- DynamicLoaderAIXDYLD.cpp --------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "DynamicLoaderAIXDYLD.h" + +#include "lldb/Core/Module.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Target/ExecutionContext.h" +#include "lldb/Target/Platform.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/RegisterContext.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/ThreadPlanStepInstruction.h" +#include "lldb/Utility/LLDBLog.h" +#include "lldb/Utility/Log.h" +#include "llvm/Support/FileSystem.h" +#if defined(_AIX) +#include +#include +#include +#include +#include +#endif + +/*#include "llvm/ADT/Triple.h" +*/ + +using namespace lldb; +using namespace lldb_private; + +LLDB_PLUGIN_DEFINE(DynamicLoaderAIXDYLD) + +DynamicLoaderAIXDYLD::DynamicLoaderAIXDYLD(Process *process) + : DynamicLoader(process) {} + +DynamicLoaderAIXDYLD::~DynamicLoaderAIXDYLD() = default; + +void DynamicLoaderAIXDYLD::Initialize() { + PluginManager::RegisterPlugin(GetPluginNameStatic(), + GetPluginDescriptionStatic(), CreateInstance); +} + +void DynamicLoaderAIXDYLD::Terminate() {} + +llvm::StringRef DynamicLoaderAIXDYLD::GetPluginDescriptionStatic() { + return "Dynamic loader plug-in that watches for shared library " + "loads/unloads in AIX processes."; +} + +DynamicLoader *DynamicLoaderAIXDYLD::CreateInstance(Process *process, + bool force) { + bool should_create = force; + if (!should_create) { + const llvm::Triple &triple_ref = + process->GetTarget().GetArchitecture().GetTriple(); + if (triple_ref.getOS() == llvm::Triple::AIX) + should_create = true; + } + + if (should_create) + return new DynamicLoaderAIXDYLD(process); + + return nullptr; +} + +void DynamicLoaderAIXDYLD::OnLoadModule(lldb::ModuleSP module_sp, + const ModuleSpec module_spec, + lldb::addr_t module_addr) { + + // Resolve the module unless we already have one. + if (!module_sp) { + Status error; + module_sp = m_process->GetTarget().GetOrCreateModule(module_spec, + true /* notify */, &error); + if (error.Fail()) + return; + } + + m_loaded_modules[module_sp] = module_addr; + UpdateLoadedSectionsCommon(module_sp, module_addr, false); + ModuleList module_list; + module_list.Append(module_sp); + m_process->GetTarget().ModulesDidLoad(module_list); +} + +void DynamicLoaderAIXDYLD::OnUnloadModule(lldb::addr_t module_addr) { + Address resolved_addr; + if (!m_process->GetTarget().ResolveLoadAddress(module_addr, resolved_addr)) + return; + + ModuleSP module_sp = resolved_addr.GetModule(); + if (module_sp) { + m_loaded_modules.erase(module_sp); + UnloadSectionsCommon(module_sp); + ModuleList module_list; + module_list.Append(module_sp); + m_process->GetTarget().ModulesDidUnload(module_list, false); + } +} + +lldb::addr_t DynamicLoaderAIXDYLD::GetLoadAddress(ModuleSP executable) { + // First, see if the load address is already cached. + auto it = m_loaded_modules.find(executable); + if (it != m_loaded_modules.end() && it->second != LLDB_INVALID_ADDRESS) + return it->second; + + lldb::addr_t load_addr = LLDB_INVALID_ADDRESS; + + // Second, try to get it through the process plugins. For a remote process, + // the remote platform will be responsible for providing it. + FileSpec file_spec(executable->GetPlatformFileSpec()); + bool is_loaded = false; + Status status = + m_process->GetFileLoadAddress(file_spec, is_loaded, load_addr); + // Servers other than lldb server could respond with a bogus address. + if (status.Success() && is_loaded && load_addr != LLDB_INVALID_ADDRESS) { + m_loaded_modules[executable] = load_addr; + return load_addr; + } + + //// Hack to try set breakpoint + //Breakpoint *dyld_break = m_process->GetTarget().CreateBreakpoint(0x100000638, true, false).get(); + //dyld_break->SetCallback(DynamicLoaderAIXDYLD::NotifyBreakpointHit, this, true); + //dyld_break->SetBreakpointKind("hack-debug"); + + return LLDB_INVALID_ADDRESS; +} + +bool DynamicLoaderAIXDYLD::NotifyBreakpointHit( + void *baton, StoppointCallbackContext *context, lldb::user_id_t break_id, + lldb::user_id_t break_loc_id) { +} + + +void DynamicLoaderAIXDYLD::ResolveExecutableModule( + lldb::ModuleSP &module_sp) { + Log *log = GetLog(LLDBLog::DynamicLoader); + + if (m_process == nullptr) + return; + + auto &target = m_process->GetTarget(); + const auto platform_sp = target.GetPlatform(); + + ProcessInstanceInfo process_info; + if (!m_process->GetProcessInfo(process_info)) { + LLDB_LOGF(log, + "DynamicLoaderPOSIXDYLD::%s - failed to get process info for " + "pid %" PRIu64, + __FUNCTION__, m_process->GetID()); + return; + } + + int32long64_t pid = m_process->GetID(); + char cwd[PATH_MAX], resolved_path[PATH_MAX]; + std::string executable_name; + bool path_resolved = false; + psinfo_t psinfo; + + std::string proc_file = "/proc/" + std::to_string(pid) + "/psinfo"; + std::string cwd_link = "/proc/" + std::to_string(pid) + "/cwd"; + std::ifstream file(proc_file, std::ios::binary); + if(!file.is_open()) + LLDB_LOGF(log, "Error: Unable to access process info "); + + file.read(reinterpret_cast(&psinfo), sizeof(psinfo_t)); + if(!file) + LLDB_LOGF(log, "Process info error: Failed to read "); + + std::string relative_path(psinfo.pr_fname); + LLDB_LOGF(log, "Relative path %s",relative_path.c_str()); + + if(readlink(cwd_link.c_str(), cwd, sizeof(cwd)) != -1){ + std::filesystem::path full_path = std::filesystem::path(cwd)/relative_path; + if(realpath(full_path.c_str(), resolved_path)) { + LLDB_LOGF(log, "Resolved Path using process info : %s", resolved_path); + path_resolved = true; + } + else + LLDB_LOGF(log, "Realpath error: Unable to resolve. "); + } + + executable_name = resolved_path; + if(path_resolved == false) { + std::string command_line(psinfo.pr_psargs); + LLDB_LOGF(log, "Command line: %s",command_line.c_str()); + if (!command_line.empty()) { + size_t space1 = command_line.find(' '); + executable_name = command_line.substr(0, space1); + LLDB_LOGF(log, "Resolved path using command line arg %s",executable_name.c_str()); + } + } + + LLDB_LOGF(log, "Executable Name %s",executable_name.c_str()); + process_info.SetExecutableFile(lldb_private::FileSpec(executable_name), + true); + + LLDB_LOGF( + log, "DynamicLoaderPOSIXDYLD::%s - got executable by pid %" PRIu64 ": %s", + __FUNCTION__, m_process->GetID(), + process_info.GetExecutableFile().GetPath().c_str()); + + ModuleSpec module_spec(process_info.GetExecutableFile(), + process_info.GetArchitecture()); + if (module_sp && module_sp->MatchesModuleSpec(module_spec)) + return; + + const auto executable_search_paths(Target::GetDefaultExecutableSearchPaths()); + auto error = platform_sp->ResolveExecutable( + module_spec, module_sp, + !executable_search_paths.IsEmpty() ? &executable_search_paths : nullptr); + if (error.Fail()) { + StreamString stream; + module_spec.Dump(stream); + + LLDB_LOGF(log, + "DynamicLoaderPOSIXDYLD::%s - failed to resolve executable " + "with module spec \"%s\": %s", + __FUNCTION__, stream.GetData(), error.AsCString()); + return; + } + + target.SetExecutableModule(module_sp, eLoadDependentsNo); +} + +bool DynamicLoaderAIXDYLD::IsCoreFile() const { + return !m_process->IsLiveDebugSession(); +} + +void DynamicLoaderAIXDYLD::FillCoreLoaderData(lldb_private::DataExtractor &data, + uint64_t loader_offset, uint64_t loader_size ) { + + Log *log = GetLog(LLDBLog::DynamicLoader); + LLDB_LOGF(log, "DynamicLoaderAIXDYLD::%s()", __FUNCTION__); + static char *buffer = (char *)malloc(loader_size); + if (buffer == NULL) { + LLDB_LOG(log, "Buffer allocation failed error: {0}", std::strerror(errno)); + return; + } + struct ld_info ldinfo[64] = {}; + struct ld_info *ptr; + int i = 0; + + ByteOrder byteorder = data.GetByteOrder(); + data.ExtractBytes(loader_offset, loader_size, eByteOrderBig, buffer); + ldinfo[0].ldinfo_next = 1; + + while (ldinfo[i++].ldinfo_next != 0) { + + ptr = (struct ld_info *)buffer; + ldinfo[i].ldinfo_next = ptr->ldinfo_next; + ldinfo[i].ldinfo_flags = ptr->ldinfo_flags; + ldinfo[i].ldinfo_core = ptr->ldinfo_core; + ldinfo[i].ldinfo_textorg = ptr->ldinfo_textorg; + ldinfo[i].ldinfo_textsize = ptr->ldinfo_textsize; + ldinfo[i].ldinfo_dataorg = ptr->ldinfo_dataorg; + ldinfo[i].ldinfo_datasize = ptr->ldinfo_datasize; + + char *filename = &ptr->ldinfo_filename[0]; + char *membername = filename + (strlen(filename) + 1); + strcpy(ldinfo[i].ldinfo_filename, filename); + + buffer += ptr->ldinfo_next; + struct ld_info *ptr2 = &(ldinfo[i]); + char *pathName = ptr2->ldinfo_filename; + char pathWithMember[PATH_MAX] = {0}; + if (strlen(membername) > 0) { + sprintf(pathWithMember, "%s(%s)", pathName, membername); + } else { + sprintf(pathWithMember, "%s", pathName); + } + + FileSpec file(pathWithMember); + ModuleSpec module_spec(file, m_process->GetTarget().GetArchitecture()); + LLDB_LOGF(log, "Module :%s", pathWithMember); + if (ModuleSP module_sp = m_process->GetTarget().GetOrCreateModule(module_spec, true /* notify */)) { + UpdateLoadedSectionsByType(module_sp, LLDB_INVALID_ADDRESS, (lldb::addr_t)ptr2->ldinfo_textorg, false, 1); + UpdateLoadedSectionsByType(module_sp, LLDB_INVALID_ADDRESS, (lldb::addr_t)ptr2->ldinfo_dataorg, false, 2); + // FIXME: .tdata, .bss + } + if (ptr2->ldinfo_next == 0) { + ptr2 = nullptr; + } + } +} + +void DynamicLoaderAIXDYLD::DidAttach() { + Log *log = GetLog(LLDBLog::DynamicLoader); + LLDB_LOGF(log, "DynamicLoaderAIXDYLD::%s()", __FUNCTION__); + + ModuleSP executable = GetTargetExecutable(); + ResolveExecutableModule(executable); + + if (!executable.get()) + return; + LLDB_LOGF(log, "DynamicLoaderAIXDYLD::%s()", __FUNCTION__); + + // Try to fetch the load address of the file from the process, since there + // could be randomization of the load address. + lldb::addr_t load_addr = GetLoadAddress(executable); + if (load_addr == LLDB_INVALID_ADDRESS) + return; + + // Request the process base address. + lldb::addr_t image_base = m_process->GetImageInfoAddress(); + if (image_base == load_addr) + return; + + // Rebase the process's modules if there is a mismatch. + UpdateLoadedSections(executable, LLDB_INVALID_ADDRESS, load_addr, false); + + ModuleList module_list; + module_list.Append(executable); + m_process->GetTarget().ModulesDidLoad(module_list); + auto error = m_process->LoadModules(); + LLDB_LOG_ERROR(log, std::move(error), "failed to load modules: {0}"); + +#if defined(_AIX) + // Get struct ld_xinfo (FIXME) + struct ld_xinfo ldinfo[64]; + Status status = m_process->GetLDXINFO(&(ldinfo[0])); + if (status.Fail()) { + Log *log = GetLog(LLDBLog::DynamicLoader); + LLDB_LOG(log, "LDXINFO failed: {0}", status); + return; + } + struct ld_xinfo *ptr = &(ldinfo[0]); + bool skip_current = true; + while (ptr != nullptr) { + char *pathName = (char *)ptr + ptr->ldinfo_filename; + char *memberName = pathName + (strlen(pathName) + 1); + if (!skip_current) { + // FIXME: buffer size + char pathWithMember[128] = {0}; + if (strlen(memberName) > 0) { + sprintf(pathWithMember, "%s(%s)", pathName, memberName); + } else { + sprintf(pathWithMember, "%s", pathName); + } + FileSpec file(pathWithMember); + ModuleSpec module_spec(file, m_process->GetTarget().GetArchitecture()); + if (ModuleSP module_sp = m_process->GetTarget().GetOrCreateModule(module_spec, true /* notify */)) { + UpdateLoadedSectionsByType(module_sp, LLDB_INVALID_ADDRESS, (lldb::addr_t)ptr->ldinfo_textorg, false, 1); + UpdateLoadedSectionsByType(module_sp, LLDB_INVALID_ADDRESS, (lldb::addr_t)ptr->ldinfo_dataorg, false, 2); + // FIXME: .tdata, .bss + } + } else { + skip_current = false; + } + if (ptr->ldinfo_next == 0) { + ptr = nullptr; + } else { + ptr = (struct ld_xinfo *)((char *)ptr + ptr->ldinfo_next); + } + } +#endif +} + +void DynamicLoaderAIXDYLD::DidLaunch() { + Log *log = GetLog(LLDBLog::DynamicLoader); + LLDB_LOGF(log, "DynamicLoaderAIXDYLD::%s()", __FUNCTION__); + + ModuleSP executable = GetTargetExecutable(); + if (!executable.get()) + return; + + lldb::addr_t load_addr = GetLoadAddress(executable); + if (load_addr != LLDB_INVALID_ADDRESS) { + // Update the loaded sections so that the breakpoints can be resolved. + UpdateLoadedSections(executable, LLDB_INVALID_ADDRESS, load_addr, false); + + ModuleList module_list; + module_list.Append(executable); + m_process->GetTarget().ModulesDidLoad(module_list); + auto error = m_process->LoadModules(); + LLDB_LOG_ERROR(log, std::move(error), "failed to load modules: {0}"); + } + +#if defined(_AIX) + // Get struct ld_xinfo (FIXME) + struct ld_xinfo ldinfo[64]; + Status status = m_process->GetLDXINFO(&(ldinfo[0])); + if (status.Fail()) { + Log *log = GetLog(LLDBLog::DynamicLoader); + LLDB_LOG(log, "LDXINFO failed: {0}", status); + return; + } + struct ld_xinfo *ptr = &(ldinfo[0]); + bool skip_current = true; + while (ptr != nullptr) { + char *pathName = (char *)ptr + ptr->ldinfo_filename; + char *memberName = pathName + (strlen(pathName) + 1); + if (!skip_current) { + // FIXME: buffer size + char pathWithMember[128] = {0}; + if (strlen(memberName) > 0) { + sprintf(pathWithMember, "%s(%s)", pathName, memberName); + } else { + sprintf(pathWithMember, "%s", pathName); + } + FileSpec file(pathWithMember); + ModuleSpec module_spec(file, m_process->GetTarget().GetArchitecture()); + if (ModuleSP module_sp = m_process->GetTarget().GetOrCreateModule(module_spec, true /* notify */)) { + UpdateLoadedSectionsByType(module_sp, LLDB_INVALID_ADDRESS, (lldb::addr_t)ptr->ldinfo_textorg, false, 1); + UpdateLoadedSectionsByType(module_sp, LLDB_INVALID_ADDRESS, (lldb::addr_t)ptr->ldinfo_dataorg, false, 2); + // FIXME: .tdata, .bss + } + } else { + skip_current = false; + } + if (ptr->ldinfo_next == 0) { + ptr = nullptr; + } else { + ptr = (struct ld_xinfo *)((char *)ptr + ptr->ldinfo_next); + } + } +#endif +} + +Status DynamicLoaderAIXDYLD::CanLoadImage() { + return Status(); } + +ThreadPlanSP +DynamicLoaderAIXDYLD::GetStepThroughTrampolinePlan(Thread &thread, + bool stop) { + //FIXME + return ThreadPlanSP(); +} diff --git a/lldb/source/Plugins/DynamicLoader/AIX-DYLD/DynamicLoaderAIXDYLD.h b/lldb/source/Plugins/DynamicLoader/AIX-DYLD/DynamicLoaderAIXDYLD.h new file mode 100644 index 0000000000000..097f8d048b77f --- /dev/null +++ b/lldb/source/Plugins/DynamicLoader/AIX-DYLD/DynamicLoaderAIXDYLD.h @@ -0,0 +1,64 @@ +//===-- DynamicLoaderAIXDYLD.h ------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SOURCE_PLUGINS_DYNAMICLOADER_AIX_DYLD_DYNAMICLOADERAIXDYLD_H +#define LLDB_SOURCE_PLUGINS_DYNAMICLOADER_AIX_DYLD_DYNAMICLOADERAIXDYLD_H + +#include "lldb/Target/DynamicLoader.h" +#include "lldb/lldb-forward.h" + +#include + +namespace lldb_private { + +class DynamicLoaderAIXDYLD : public DynamicLoader { +public: + DynamicLoaderAIXDYLD(Process *process); + + ~DynamicLoaderAIXDYLD() override; + + static void Initialize(); + static void Terminate(); + static llvm::StringRef GetPluginNameStatic() { return "aix-dyld"; } + static llvm::StringRef GetPluginDescriptionStatic(); + + static DynamicLoader *CreateInstance(Process *process, bool force); + + void OnLoadModule(lldb::ModuleSP module_sp, const ModuleSpec module_spec, + lldb::addr_t module_addr); + void OnUnloadModule(lldb::addr_t module_addr); + + void FillCoreLoaderData(lldb_private::DataExtractor &data, + uint64_t loader_offset, uint64_t loader_size); + + void DidAttach() override; + void DidLaunch() override; + Status CanLoadImage() override; + lldb::ThreadPlanSP GetStepThroughTrampolinePlan(Thread &thread, + bool stop) override; + + llvm::StringRef GetPluginName() override { return GetPluginNameStatic(); } + + static bool NotifyBreakpointHit(void *baton, StoppointCallbackContext *context, lldb::user_id_t break_id, lldb::user_id_t break_loc_id); + +protected: + lldb::addr_t GetLoadAddress(lldb::ModuleSP executable); + + /// Loads Module from inferior process. + void ResolveExecutableModule(lldb::ModuleSP &module_sp); + + /// Returns true if the process is for a core file. + bool IsCoreFile() const; + +private: + std::map m_loaded_modules; +}; + +} // namespace lldb_private + +#endif // LLDB_SOURCE_PLUGINS_DYNAMICLOADER_AIX_DYLD_DYNAMICLOADERWAIXDYLD_H diff --git a/lldb/source/Plugins/DynamicLoader/CMakeLists.txt b/lldb/source/Plugins/DynamicLoader/CMakeLists.txt index 01aba34b94169..77339d6c239f2 100644 --- a/lldb/source/Plugins/DynamicLoader/CMakeLists.txt +++ b/lldb/source/Plugins/DynamicLoader/CMakeLists.txt @@ -12,4 +12,5 @@ add_subdirectory(POSIX-DYLD) add_subdirectory(Static) add_subdirectory(Hexagon-DYLD) add_subdirectory(Windows-DYLD) +add_subdirectory(AIX-DYLD) add_subdirectory(wasm-DYLD) diff --git a/lldb/source/Plugins/Instruction/PPC64/EmulateInstructionPPC64.cpp b/lldb/source/Plugins/Instruction/PPC64/EmulateInstructionPPC64.cpp index 29516c57d2a20..5fc6521ef23e9 100644 --- a/lldb/source/Plugins/Instruction/PPC64/EmulateInstructionPPC64.cpp +++ b/lldb/source/Plugins/Instruction/PPC64/EmulateInstructionPPC64.cpp @@ -146,7 +146,25 @@ EmulateInstructionPPC64::GetOpcodeForInstruction(uint32_t opcode) { {0xfc000000, 0x38000000, &EmulateInstructionPPC64::EmulateADDI, "addi RT, RA, SI"}, {0xfc000003, 0xe8000000, &EmulateInstructionPPC64::EmulateLD, - "ld RT, DS(RA)"}}; + "ld RT, DS(RA)"}, +// {0xffff0003, 0x40820000, &EmulateInstructionPPC64::EmulateBNE, +// "bne TARGET"}, + {0xfc000002, 0x48000000, &EmulateInstructionPPC64::EmulateB, + "b TARGET"}, + {0xfc000003, 0x48000002, &EmulateInstructionPPC64::EmulateBA, + "ba TARGET"}, + {0xfc000003, 0x48000003, &EmulateInstructionPPC64::EmulateBLA, + "bla TARGET"}, + {0xfc000002, 0x40000000, &EmulateInstructionPPC64::EmulateBC, + "bc BO,BI,TARGET"}, + {0xfc000002, 0x40000002, &EmulateInstructionPPC64::EmulateBCA, + "bca BO,BI,TARGET"}, + {0xfc0007fe, 0x4c000020, &EmulateInstructionPPC64::EmulateBCLR, + "bclr BO,BI,BH"}, + {0xfc0007fe, 0x4c000420, &EmulateInstructionPPC64::EmulateBCCTR, + "bcctr BO,BI,BH"}, + {0xfc0007fe, 0x4c000460, &EmulateInstructionPPC64::EmulateBCTAR, + "bctar BO,BI,BH"}}; static const size_t k_num_ppc_opcodes = std::size(g_opcodes); for (size_t i = 0; i < k_num_ppc_opcodes; ++i) { @@ -169,12 +187,13 @@ bool EmulateInstructionPPC64::EvaluateInstruction(uint32_t evaluate_options) { bool success = false; - uint32_t orig_pc_value = 0; + uint64_t orig_pc_value = 0; if (auto_advance_pc) { orig_pc_value = ReadRegisterUnsigned(eRegisterKindLLDB, gpr_pc_ppc64le, 0, &success); if (!success) return false; + LLDB_LOG(GetLog(LLDBLog::Unwind), "orig_pc_value:{0}", orig_pc_value); } // Call the Emulate... function. @@ -183,11 +202,13 @@ bool EmulateInstructionPPC64::EvaluateInstruction(uint32_t evaluate_options) { return false; if (auto_advance_pc) { - uint32_t new_pc_value = + uint64_t new_pc_value = ReadRegisterUnsigned(eRegisterKindLLDB, gpr_pc_ppc64le, 0, &success); if (!success) return false; + LLDB_LOG(GetLog(LLDBLog::Unwind), "new_pc_value:{0}", new_pc_value); + if (new_pc_value == orig_pc_value) { EmulateInstruction::Context context; context.type = eContextAdvancePC; @@ -389,5 +410,174 @@ bool EmulateInstructionPPC64::EmulateADDI(uint32_t opcode) { return false; WriteRegisterUnsigned(ctx, eRegisterKindLLDB, gpr_r1_ppc64le, r1 + si_val); LLDB_LOG(log, "EmulateADDI: success!"); + + // FIX the next-pc + uint64_t pc_value = ReadRegisterUnsigned(eRegisterKindLLDB, gpr_pc_ppc64le, 0, &success); + uint64_t next_pc = pc_value + 4; + ctx.type = eContextAdjustPC; + WriteRegisterUnsigned(ctx, eRegisterKindLLDB, gpr_pc_ppc64le, next_pc); + + return true; +} + +bool EmulateInstructionPPC64::EmulateBC(uint32_t opcode) { + // FIXME:32bit M + uint32_t M = 0; + uint32_t target32 = Bits32(opcode, 15, 2) << 2; + uint64_t target = (uint64_t)target32 + ((target32 & 0x8000) ? 0xffffffffffff0000UL : 0); + uint32_t BO = Bits32(opcode, 25, 21); + bool success; + uint64_t ctr_value = ReadRegisterUnsigned(eRegisterKindLLDB, gpr_ctr_ppc64le, 0, &success); + if ((~BO) & (1U << 2)) + ctr_value = ctr_value - 1; + bool ctr_ok = (bool)(BO & (1U << 2)) | ((bool)(ctr_value != 0) ^ (bool)(BO & (1U << 1))); + uint64_t cr_value = ReadRegisterUnsigned(eRegisterKindLLDB, gpr_cr_ppc64le, 0, &success); + uint32_t BI = Bits32(opcode, 20, 16); + bool cond_ok = (bool)(BO & (1U << 4)) | (bool)(((cr_value >> (63 - (BI + 32))) & 1U) == ((BO >> 3) & 1U)); + + uint64_t pc_value = ReadRegisterUnsigned(eRegisterKindLLDB, gpr_pc_ppc64le, 0, &success); + uint64_t next_pc = pc_value + 4; + if (ctr_ok & cond_ok) + next_pc = pc_value + target; + + Context ctx; + ctx.type = eContextAdjustPC; + WriteRegisterUnsigned(ctx, eRegisterKindLLDB, gpr_pc_ppc64le, next_pc); + Log *log = GetLog(LLDBLog::Unwind); + LLDB_LOG(log, "EmulateBC: success!"); + return true; +} + +bool EmulateInstructionPPC64::EmulateBCA(uint32_t opcode) { + // FIXME:32bit M + uint32_t M = 0; + uint32_t target32 = Bits32(opcode, 15, 2) << 2; + uint64_t target = (uint64_t)target32 + ((target32 & 0x8000) ? 0xffffffffffff0000UL : 0); + uint32_t BO = Bits32(opcode, 25, 21); + bool success; + uint64_t ctr_value = ReadRegisterUnsigned(eRegisterKindLLDB, gpr_ctr_ppc64le, 0, &success); + if ((~BO) & (1U << 2)) + ctr_value = ctr_value - 1; + bool ctr_ok = (bool)(BO & (1U << 2)) | ((bool)(ctr_value != 0) ^ (bool)(BO & (1U << 1))); + uint64_t cr_value = ReadRegisterUnsigned(eRegisterKindLLDB, gpr_cr_ppc64le, 0, &success); + uint32_t BI = Bits32(opcode, 20, 16); + bool cond_ok = (bool)(BO & (1U << 4)) | (bool)(((cr_value >> (63 - (BI + 32))) & 1U) == ((BO >> 3) & 1U)); + + uint64_t pc_value = ReadRegisterUnsigned(eRegisterKindLLDB, gpr_pc_ppc64le, 0, &success); + uint64_t next_pc = pc_value + 4; + if (ctr_ok & cond_ok) + next_pc = target; + + Context ctx; + ctx.type = eContextAdjustPC; + WriteRegisterUnsigned(ctx, eRegisterKindLLDB, gpr_pc_ppc64le, next_pc); + Log *log = GetLog(LLDBLog::Unwind); + LLDB_LOG(log, "EmulateBCA: success!"); + return true; +} + +bool EmulateInstructionPPC64::EmulateBCLR(uint32_t opcode) { + // FIXME:32bit M + uint32_t M = 0; + uint32_t BO = Bits32(opcode, 25, 21); + bool success; + uint64_t ctr_value = ReadRegisterUnsigned(eRegisterKindLLDB, gpr_ctr_ppc64le, 0, &success); + if ((~BO) & (1U << 2)) + ctr_value = ctr_value - 1; + bool ctr_ok = (bool)(BO & (1U << 2)) | ((bool)(ctr_value != 0) ^ (bool)(BO & (1U << 1))); + uint64_t cr_value = ReadRegisterUnsigned(eRegisterKindLLDB, gpr_cr_ppc64le, 0, &success); + uint32_t BI = Bits32(opcode, 20, 16); + bool cond_ok = (bool)(BO & (1U << 4)) | (bool)(((cr_value >> (63 - (BI + 32))) & 1U) == ((BO >> 3) & 1U)); + + uint64_t pc_value = ReadRegisterUnsigned(eRegisterKindLLDB, gpr_pc_ppc64le, 0, &success); + uint64_t next_pc = pc_value + 4; + if (ctr_ok & cond_ok) { + next_pc = ReadRegisterUnsigned(eRegisterKindLLDB, gpr_lr_ppc64le, 0, &success); + next_pc &= ~((1UL << 2) - 1); + } + + Context ctx; + ctx.type = eContextAdjustPC; + WriteRegisterUnsigned(ctx, eRegisterKindLLDB, gpr_pc_ppc64le, next_pc); + Log *log = GetLog(LLDBLog::Unwind); + LLDB_LOG(log, "EmulateBCLR: success!"); + return true; +} + +bool EmulateInstructionPPC64::EmulateBCCTR(uint32_t opcode) { + // FIXME:32bit M + uint32_t M = 0; + uint32_t BO = Bits32(opcode, 25, 21); + bool success; + uint64_t cr_value = ReadRegisterUnsigned(eRegisterKindLLDB, gpr_cr_ppc64le, 0, &success); + uint32_t BI = Bits32(opcode, 20, 16); + bool cond_ok = (bool)(BO & (1U << 4)) | (bool)(((cr_value >> (63 - (BI + 32))) & 1U) == ((BO >> 3) & 1U)); + + Log *log = GetLog(LLDBLog::Unwind); + uint64_t pc_value = ReadRegisterUnsigned(eRegisterKindLLDB, gpr_pc_ppc64le, 0, &success); + uint64_t next_pc = pc_value + 4; + if (cond_ok) { + next_pc = ReadRegisterUnsigned(eRegisterKindLLDB, gpr_ctr_ppc64le, 0, &success); + next_pc &= ~((1UL << 2) - 1); + if (next_pc < 0x4000000) { + LLDB_LOGF(log, "EmulateBCCTR: next address %lx out of range, emulate by goto LR!"); + next_pc = ReadRegisterUnsigned(eRegisterKindLLDB, gpr_lr_ppc64le, 0, &success); + } + } + + Context ctx; + ctx.type = eContextAdjustPC; + WriteRegisterUnsigned(ctx, eRegisterKindLLDB, gpr_pc_ppc64le, next_pc); + LLDB_LOG(log, "EmulateBCCTR: success!"); + return true; +} + +bool EmulateInstructionPPC64::EmulateBCTAR(uint32_t opcode) { + // Not supported yet. + LLDB_LOG(GetLog(LLDBLog::Unwind), "EmulateBCTAR: not supported!"); + assert(0); + return false; +} + +bool EmulateInstructionPPC64::EmulateB(uint32_t opcode) { + uint32_t target32 = Bits32(opcode, 25, 2) << 2; + uint64_t target = (uint64_t)target32 + ((target32 & 0x2000000) ? 0xfffffffffc000000UL : 0); + + bool success; + uint64_t pc_value = ReadRegisterUnsigned(eRegisterKindLLDB, gpr_pc_ppc64le, 0, &success); + uint64_t next_pc = pc_value + target; + + Context ctx; + ctx.type = eContextAdjustPC; + WriteRegisterUnsigned(ctx, eRegisterKindLLDB, gpr_pc_ppc64le, next_pc); + Log *log = GetLog(LLDBLog::Unwind); + LLDB_LOG(log, "EmulateB: success!"); + return true; +} + +bool EmulateInstructionPPC64::EmulateBA(uint32_t opcode) { + Log *log = GetLog(LLDBLog::Unwind); + + bool success; + uint64_t next_pc = ReadRegisterUnsigned(eRegisterKindLLDB, gpr_lr_ppc64le, 0, &success); + + Context ctx; + ctx.type = eContextAdjustPC; + WriteRegisterUnsigned(ctx, eRegisterKindLLDB, gpr_pc_ppc64le, next_pc); + LLDB_LOG(log, "EmulateBA: emulate by branch to lr!"); + return true; +} + +bool EmulateInstructionPPC64::EmulateBLA(uint32_t opcode) { + Log *log = GetLog(LLDBLog::Unwind); + + bool success; + uint64_t pc_value = ReadRegisterUnsigned(eRegisterKindLLDB, gpr_pc_ppc64le, 0, &success); + uint64_t next_pc = pc_value + 4; + + Context ctx; + ctx.type = eContextAdjustPC; + WriteRegisterUnsigned(ctx, eRegisterKindLLDB, gpr_pc_ppc64le, next_pc); + LLDB_LOG(log, "EmulateBLA: emulate by branch to lr!"); return true; } diff --git a/lldb/source/Plugins/Instruction/PPC64/EmulateInstructionPPC64.h b/lldb/source/Plugins/Instruction/PPC64/EmulateInstructionPPC64.h index a9424f16b0ad0..d98b2880ca3b4 100644 --- a/lldb/source/Plugins/Instruction/PPC64/EmulateInstructionPPC64.h +++ b/lldb/source/Plugins/Instruction/PPC64/EmulateInstructionPPC64.h @@ -39,6 +39,12 @@ class EmulateInstructionPPC64 : public EmulateInstruction { return true; case eInstructionTypePCModifying: +#if defined(_AIX) + return true; +#else + return false; +#endif + case eInstructionTypeAll: return false; } @@ -84,6 +90,14 @@ class EmulateInstructionPPC64 : public EmulateInstruction { bool EmulateSTD(uint32_t opcode); bool EmulateOR(uint32_t opcode); bool EmulateADDI(uint32_t opcode); + bool EmulateB(uint32_t opcode); + bool EmulateBA(uint32_t opcode); + bool EmulateBLA(uint32_t opcode); + bool EmulateBC(uint32_t opcode); + bool EmulateBCA(uint32_t opcode); + bool EmulateBCLR(uint32_t opcode); + bool EmulateBCCTR(uint32_t opcode); + bool EmulateBCTAR(uint32_t opcode); }; } // namespace lldb_private diff --git a/lldb/source/Plugins/JITLoader/GDB/JITLoaderGDB.cpp b/lldb/source/Plugins/JITLoader/GDB/JITLoaderGDB.cpp index b6487d4e8ed4b..9dd19447f4a32 100644 --- a/lldb/source/Plugins/JITLoader/GDB/JITLoaderGDB.cpp +++ b/lldb/source/Plugins/JITLoader/GDB/JITLoaderGDB.cpp @@ -194,6 +194,10 @@ void JITLoaderGDB::SetJITBreakpoint(lldb_private::ModuleList &module_list) { if (jit_addr == LLDB_INVALID_ADDRESS) return; +#if defined(_AIX) + return; +#endif + m_jit_descriptor_addr = GetSymbolAddress( module_list, ConstString("__jit_debug_descriptor"), eSymbolTypeData); if (m_jit_descriptor_addr == LLDB_INVALID_ADDRESS) { diff --git a/lldb/source/Plugins/ObjectContainer/Big-Archive/CMakeLists.txt b/lldb/source/Plugins/ObjectContainer/Big-Archive/CMakeLists.txt new file mode 100644 index 0000000000000..612a36265b536 --- /dev/null +++ b/lldb/source/Plugins/ObjectContainer/Big-Archive/CMakeLists.txt @@ -0,0 +1,10 @@ +add_lldb_library(lldbPluginObjectContainerBigArchive PLUGIN + ObjectContainerBigArchive.cpp + + LINK_LIBS + lldbCore + lldbHost + lldbSymbol + LINK_COMPONENTS + Support + ) diff --git a/lldb/source/Plugins/ObjectContainer/Big-Archive/ObjectContainerBigArchive.cpp b/lldb/source/Plugins/ObjectContainer/Big-Archive/ObjectContainerBigArchive.cpp new file mode 100644 index 0000000000000..38756a0dd2969 --- /dev/null +++ b/lldb/source/Plugins/ObjectContainer/Big-Archive/ObjectContainerBigArchive.cpp @@ -0,0 +1,522 @@ +//===-- ObjectContainerBigArchive.cpp -------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "ObjectContainerBigArchive.h" + +#if defined(_WIN32) || defined(__ANDROID__) || defined(_AIX) +// Defines from ar, missing on Windows +#define ARMAG "!\n" +#define SARMAG 8 +#define ARFMAG "`\n" + +typedef struct ar_hdr { + char ar_name[16]; + char ar_date[12]; + char ar_uid[6], ar_gid[6]; + char ar_mode[8]; + char ar_size[10]; + char ar_fmag[2]; +} ar_hdr; +#else +#include +#endif + +#include "lldb/Core/Module.h" +#include "lldb/Core/ModuleSpec.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Host/FileSystem.h" +#include "lldb/Symbol/ObjectFile.h" +#include "lldb/Utility/ArchSpec.h" +#include "lldb/Utility/Stream.h" +#include "lldb/Utility/Timer.h" + +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Object/Archive.h" +#include "llvm/Support/Chrono.h" + +using namespace lldb; +using namespace lldb_private; + +LLDB_PLUGIN_DEFINE(ObjectContainerBigArchive) + +ObjectContainerBigArchive::Object::Object() : ar_name() {} + +void ObjectContainerBigArchive::Object::Clear() { + ar_name.Clear(); + modification_time = 0; + uid = 0; + gid = 0; + mode = 0; + size = 0; + file_offset = 0; + file_size = 0; +} + +lldb::offset_t +ObjectContainerBigArchive::Object::Extract(const DataExtractor &data, + lldb::offset_t offset) { + size_t ar_name_len = 0; + std::string str; + char *err; + + // File header + // + // The common format is as follows. + // + // Offset Length Name Format + // 0 16 File name ASCII right padded with spaces (no spaces + // allowed in file name) + // 16 12 File mod Decimal as cstring right padded with + // spaces + // 28 6 Owner ID Decimal as cstring right padded with + // spaces + // 34 6 Group ID Decimal as cstring right padded with + // spaces + // 40 8 File mode Octal as cstring right padded with + // spaces + // 48 10 File byte size Decimal as cstring right padded with + // spaces + // 58 2 File magic 0x60 0x0A + + // Make sure there is enough data for the file header and bail if not + if (!data.ValidOffsetForDataOfSize(offset, 60)) + return LLDB_INVALID_OFFSET; + + str.assign((const char *)data.GetData(&offset, 16), 16); + if (llvm::StringRef(str).starts_with("#1/")) { + // If the name is longer than 16 bytes, or contains an embedded space then + // it will use this format where the length of the name is here and the + // name characters are after this header. + ar_name_len = strtoul(str.c_str() + 3, &err, 10); + } else { + // Strip off any trailing spaces. + const size_t last_pos = str.find_last_not_of(' '); + if (last_pos != std::string::npos) { + if (last_pos + 1 < 16) + str.erase(last_pos + 1); + } + ar_name.SetCString(str.c_str()); + } + + str.assign((const char *)data.GetData(&offset, 12), 12); + modification_time = strtoul(str.c_str(), &err, 10); + + str.assign((const char *)data.GetData(&offset, 6), 6); + uid = strtoul(str.c_str(), &err, 10); + + str.assign((const char *)data.GetData(&offset, 6), 6); + gid = strtoul(str.c_str(), &err, 10); + + str.assign((const char *)data.GetData(&offset, 8), 8); + mode = strtoul(str.c_str(), &err, 8); + + str.assign((const char *)data.GetData(&offset, 10), 10); + size = strtoul(str.c_str(), &err, 10); + + str.assign((const char *)data.GetData(&offset, 2), 2); + if (str == ARFMAG) { + if (ar_name_len > 0) { + const void *ar_name_ptr = data.GetData(&offset, ar_name_len); + // Make sure there was enough data for the string value and bail if not + if (ar_name_ptr == nullptr) + return LLDB_INVALID_OFFSET; + str.assign((const char *)ar_name_ptr, ar_name_len); + ar_name.SetCString(str.c_str()); + } + file_offset = offset; + file_size = size - ar_name_len; + return offset; + } + return LLDB_INVALID_OFFSET; +} + +ObjectContainerBigArchive::Archive::Archive(const lldb_private::ArchSpec &arch, + const llvm::sys::TimePoint<> &time, + lldb::offset_t file_offset, + lldb_private::DataExtractor &data) + : m_arch(arch), m_modification_time(time), m_file_offset(file_offset), + m_objects(), m_data(data) {} + +ObjectContainerBigArchive::Archive::~Archive() = default; + +size_t ObjectContainerBigArchive::Archive::ParseObjects() { + DataExtractor &data = m_data; + std::string str; + lldb::offset_t offset = 0; + str.assign((const char *)data.GetData(&offset, (sizeof(llvm::object::BigArchiveMagic) - 1)), + (sizeof(llvm::object::BigArchiveMagic) - 1)); + if (str == llvm::object::BigArchiveMagic) { + llvm::Error err = llvm::Error::success(); + llvm::object::BigArchive bigAr(llvm::MemoryBufferRef(toStringRef(m_data.GetData()), llvm::StringRef("")), err); + if (err) + return 0; + + for (const llvm::object::Archive::Child &child : bigAr.children(err)) { + if (err) + continue; + if (!child.getParent()) + continue; + Object obj; + obj.Clear(); + // FIXME: check errors + llvm::Expected childNameOrErr = child.getName(); + if (!childNameOrErr) + continue; + obj.ar_name.SetCString(childNameOrErr->str().c_str()); + llvm::Expected> lastModifiedOrErr = child.getLastModified(); + if (!lastModifiedOrErr) + continue; + obj.modification_time = (uint32_t)llvm::sys::toTimeT(*(lastModifiedOrErr)); + llvm::Expected getUIDOrErr = child.getUID(); + if (!getUIDOrErr) + continue; + obj.uid = (uint16_t)*getUIDOrErr; + llvm::Expected getGIDOrErr = child.getGID(); + if (!getGIDOrErr) + continue; + obj.gid = (uint16_t)*getGIDOrErr; + llvm::Expected getAccessModeOrErr = child.getAccessMode(); + if (!getAccessModeOrErr) + continue; + obj.mode = (uint16_t)*getAccessModeOrErr; + llvm::Expected getRawSizeOrErr = child.getRawSize(); + if (!getRawSizeOrErr) + continue; + obj.size = (uint32_t)*getRawSizeOrErr; + + obj.file_offset = (lldb::offset_t)child.getDataOffset(); + + llvm::Expected getSizeOrErr = child.getSize(); + if (!getSizeOrErr) + continue; + obj.file_size = (lldb::offset_t)*getSizeOrErr; + + size_t obj_idx = m_objects.size(); + m_objects.push_back(obj); + // Insert all of the C strings out of order for now... + m_object_name_to_index_map.Append(obj.ar_name, obj_idx); + } + if (err) + return 0; + + // Now sort all of the object name pointers + m_object_name_to_index_map.Sort(); + } + return m_objects.size(); +} + +ObjectContainerBigArchive::Object * +ObjectContainerBigArchive::Archive::FindObject( + ConstString object_name, const llvm::sys::TimePoint<> &object_mod_time) { + const ObjectNameToIndexMap::Entry *match = + m_object_name_to_index_map.FindFirstValueForName(object_name); + if (!match) + return nullptr; + if (object_mod_time == llvm::sys::TimePoint<>()) + return &m_objects[match->value]; + + const uint64_t object_modification_date = llvm::sys::toTimeT(object_mod_time); + if (m_objects[match->value].modification_time == object_modification_date) + return &m_objects[match->value]; + + const ObjectNameToIndexMap::Entry *next_match = + m_object_name_to_index_map.FindNextValueForName(match); + while (next_match) { + if (m_objects[next_match->value].modification_time == + object_modification_date) + return &m_objects[next_match->value]; + next_match = m_object_name_to_index_map.FindNextValueForName(next_match); + } + + return nullptr; +} + +ObjectContainerBigArchive::Archive::shared_ptr +ObjectContainerBigArchive::Archive::FindCachedArchive( + const FileSpec &file, const ArchSpec &arch, + const llvm::sys::TimePoint<> &time, lldb::offset_t file_offset) { + std::lock_guard guard(Archive::GetArchiveCacheMutex()); + shared_ptr archive_sp; + Archive::Map &archive_map = Archive::GetArchiveCache(); + Archive::Map::iterator pos = archive_map.find(file); + // Don't cache a value for "archive_map.end()" below since we might delete an + // archive entry... + while (pos != archive_map.end() && pos->first == file) { + bool match = true; + if (arch.IsValid() && + !pos->second->GetArchitecture().IsCompatibleMatch(arch)) + match = false; + else if (file_offset != LLDB_INVALID_OFFSET && + pos->second->GetFileOffset() != file_offset) + match = false; + if (match) { + if (pos->second->GetModificationTime() == time) { + return pos->second; + } else { + // We have a file at the same path with the same architecture whose + // modification time doesn't match. It doesn't make sense for us to + // continue to use this Big archive since we cache only the object info + // which consists of file time info and also the file offset and file + // size of any contained objects. Since this information is now out of + // date, we won't get the correct information if we go and extract the + // file data, so we should remove the old and outdated entry. + archive_map.erase(pos); + pos = archive_map.find(file); + continue; // Continue to next iteration so we don't increment pos + // below... + } + } + ++pos; + } + return archive_sp; +} + +ObjectContainerBigArchive::Archive::shared_ptr +ObjectContainerBigArchive::Archive::ParseAndCacheArchiveForFile( + const FileSpec &file, const ArchSpec &arch, + const llvm::sys::TimePoint<> &time, lldb::offset_t file_offset, + DataExtractor &data) { + shared_ptr archive_sp(new Archive(arch, time, file_offset, data)); + if (archive_sp) { + const size_t num_objects = archive_sp->ParseObjects(); + if (num_objects > 0) { + std::lock_guard guard( + Archive::GetArchiveCacheMutex()); + Archive::GetArchiveCache().insert(std::make_pair(file, archive_sp)); + } else { + archive_sp.reset(); + } + } + return archive_sp; +} + +ObjectContainerBigArchive::Archive::Map & +ObjectContainerBigArchive::Archive::GetArchiveCache() { + static Archive::Map g_archive_map; + return g_archive_map; +} + +std::recursive_mutex & +ObjectContainerBigArchive::Archive::GetArchiveCacheMutex() { + static std::recursive_mutex g_archive_map_mutex; + return g_archive_map_mutex; +} + +void ObjectContainerBigArchive::Initialize() { + PluginManager::RegisterPlugin(GetPluginNameStatic(), + GetPluginDescriptionStatic(), CreateInstance, + GetModuleSpecifications); +} + +void ObjectContainerBigArchive::Terminate() { + PluginManager::UnregisterPlugin(CreateInstance); +} + +ObjectContainer *ObjectContainerBigArchive::CreateInstance( + const lldb::ModuleSP &module_sp, DataBufferSP &data_sp, + lldb::offset_t data_offset, const FileSpec *file, + lldb::offset_t file_offset, lldb::offset_t length) { + ConstString object_name(module_sp->GetObjectName()); + if (!object_name) + return nullptr; + + if (data_sp) { + // We have data, which means this is the first 512 bytes of the file Check + // to see if the magic bytes match and if they do, read the entire table of + // contents for the archive and cache it + DataExtractor data; + data.SetData(data_sp, data_offset, length); + if (file && data_sp && ObjectContainerBigArchive::MagicBytesMatch(data)) { + LLDB_SCOPED_TIMERF( + "ObjectContainerBigArchive::CreateInstance (module = %s, file = " + "%p, file_offset = 0x%8.8" PRIx64 ", file_size = 0x%8.8" PRIx64 ")", + module_sp->GetFileSpec().GetPath().c_str(), + static_cast(file), static_cast(file_offset), + static_cast(length)); + + // Map the entire .a file to be sure that we don't lose any data if the + // file gets updated by a new build while this .a file is being used for + // debugging + DataBufferSP archive_data_sp = + FileSystem::Instance().CreateDataBuffer(*file, length, file_offset); + if (!archive_data_sp) + return nullptr; + + lldb::offset_t archive_data_offset = 0; + + Archive::shared_ptr archive_sp(Archive::FindCachedArchive( + *file, module_sp->GetArchitecture(), module_sp->GetModificationTime(), + file_offset)); + std::unique_ptr container_up( + new ObjectContainerBigArchive(module_sp, archive_data_sp, + archive_data_offset, file, file_offset, + length)); + + if (container_up) { + if (archive_sp) { + // We already have this archive in our cache, use it + container_up->SetArchive(archive_sp); + return container_up.release(); + } else if (container_up->ParseHeader()) + return container_up.release(); + } + } + } else { + // No data, just check for a cached archive + Archive::shared_ptr archive_sp(Archive::FindCachedArchive( + *file, module_sp->GetArchitecture(), module_sp->GetModificationTime(), + file_offset)); + if (archive_sp) { + std::unique_ptr container_up( + new ObjectContainerBigArchive(module_sp, data_sp, data_offset, file, + file_offset, length)); + + if (container_up) { + // We already have this archive in our cache, use it + container_up->SetArchive(archive_sp); + return container_up.release(); + } + } + } + return nullptr; +} + +bool ObjectContainerBigArchive::MagicBytesMatch(const DataExtractor &data) { + uint32_t offset = 0; + const char *armag = (const char *)data.PeekData(offset, (sizeof(llvm::object::BigArchiveMagic) - 1)); + if (armag && ::strncmp(armag, llvm::object::BigArchiveMagic, (sizeof(llvm::object::BigArchiveMagic) - 1)) == 0) + return true; + return false; +} + +ObjectContainerBigArchive::ObjectContainerBigArchive( + const lldb::ModuleSP &module_sp, DataBufferSP &data_sp, + lldb::offset_t data_offset, const lldb_private::FileSpec *file, + lldb::offset_t file_offset, lldb::offset_t size) + : ObjectContainer(module_sp, file, file_offset, size, data_sp, data_offset), + m_archive_sp() {} +void ObjectContainerBigArchive::SetArchive(Archive::shared_ptr &archive_sp) { + m_archive_sp = archive_sp; +} + +ObjectContainerBigArchive::~ObjectContainerBigArchive() = default; + +bool ObjectContainerBigArchive::ParseHeader() { + if (m_archive_sp.get() == nullptr) { + if (m_data.GetByteSize() > 0) { + ModuleSP module_sp(GetModule()); + if (module_sp) { + m_archive_sp = Archive::ParseAndCacheArchiveForFile( + m_file, module_sp->GetArchitecture(), + module_sp->GetModificationTime(), m_offset, m_data); + } + // Clear the m_data that contains the entire archive data and let our + // m_archive_sp hold onto the data. + m_data.Clear(); + } + } + return m_archive_sp.get() != nullptr; +} + +void ObjectContainerBigArchive::Object::Dump(Stream *s) const { + printf("name = \"%s\"\n", ar_name.GetCString()); + printf("mtime = 0x%8.8" PRIx32 "\n", modification_time); + printf("size = 0x%8.8" PRIx32 " (%" PRIu32 ")\n", size, size); + printf("file_offset = 0x%16.16" PRIx64 " (%" PRIu64 ")\n", file_offset, + file_offset); + printf("file_size = 0x%16.16" PRIx64 " (%" PRIu64 ")\n\n", file_size, + file_size); +} + +ObjectFileSP ObjectContainerBigArchive::GetObjectFile(const FileSpec *file) { + ModuleSP module_sp(GetModule()); + if (module_sp) { + if (module_sp->GetObjectName() && m_archive_sp) { + Object *object = m_archive_sp->FindObject( + module_sp->GetObjectName(), module_sp->GetObjectModificationTime()); + if (object) { + lldb::offset_t data_offset = object->file_offset; + return ObjectFile::FindPlugin( + module_sp, file, m_offset + object->file_offset, object->file_size, + m_archive_sp->GetData().GetSharedDataBuffer(), data_offset); + } + } + } + return ObjectFileSP(); +} + +size_t ObjectContainerBigArchive::GetModuleSpecifications( + const lldb_private::FileSpec &file, lldb::DataBufferSP &data_sp, + lldb::offset_t data_offset, lldb::offset_t file_offset, + lldb::offset_t file_size, lldb_private::ModuleSpecList &specs) { + + // We have data, which means this is the first 512 bytes of the file Check to + // see if the magic bytes match and if they do, read the entire table of + // contents for the archive and cache it + DataExtractor data; + data.SetData(data_sp, data_offset, data_sp->GetByteSize()); + if (!file || !data_sp || !ObjectContainerBigArchive::MagicBytesMatch(data)) + return 0; + + const size_t initial_count = specs.GetSize(); + llvm::sys::TimePoint<> file_mod_time = FileSystem::Instance().GetModificationTime(file); + Archive::shared_ptr archive_sp( + Archive::FindCachedArchive(file, ArchSpec(), file_mod_time, file_offset)); + bool set_archive_arch = false; + if (!archive_sp) { + set_archive_arch = true; + data_sp = + FileSystem::Instance().CreateDataBuffer(file, file_size, file_offset); + if (data_sp) { + data.SetData(data_sp, 0, data_sp->GetByteSize()); + archive_sp = Archive::ParseAndCacheArchiveForFile( + file, ArchSpec(), file_mod_time, file_offset, data); + } + } + + if (archive_sp) { + const size_t num_objects = archive_sp->GetNumObjects(); + for (size_t idx = 0; idx < num_objects; ++idx) { + const Object *object = archive_sp->GetObjectAtIndex(idx); + if (object) { + const lldb::offset_t object_file_offset = + file_offset + object->file_offset; + if (object->file_offset < file_size && file_size > object_file_offset) { + if (ObjectFile::GetModuleSpecifications( + file, object_file_offset, file_size - object_file_offset, + specs)) { + ModuleSpec &spec = + specs.GetModuleSpecRefAtIndex(specs.GetSize() - 1); + llvm::sys::TimePoint<> object_mod_time( + std::chrono::seconds(object->modification_time)); + spec.GetObjectName() = object->ar_name; + spec.SetObjectOffset(object_file_offset); + spec.SetObjectSize(file_size - object_file_offset); + spec.GetObjectModificationTime() = object_mod_time; + } + } + } + } + } + const size_t end_count = specs.GetSize(); + size_t num_specs_added = end_count - initial_count; + if (set_archive_arch && num_specs_added > 0) { + // The archive was created but we didn't have an architecture so we need to + // set it + for (size_t i = initial_count; i < end_count; ++i) { + ModuleSpec module_spec; + if (specs.GetModuleSpecAtIndex(i, module_spec)) { + if (module_spec.GetArchitecture().IsValid()) { + archive_sp->SetArchitecture(module_spec.GetArchitecture()); + break; + } + } + } + } + return num_specs_added; +} diff --git a/lldb/source/Plugins/ObjectContainer/Big-Archive/ObjectContainerBigArchive.h b/lldb/source/Plugins/ObjectContainer/Big-Archive/ObjectContainerBigArchive.h new file mode 100644 index 0000000000000..ad9b814048a87 --- /dev/null +++ b/lldb/source/Plugins/ObjectContainer/Big-Archive/ObjectContainerBigArchive.h @@ -0,0 +1,177 @@ +//===-- ObjectContainerBigArchive.h -----------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SOURCE_PLUGINS_OBJECTCONTAINER_BIG_ARCHIVE_OBJECTCONTAINERBIGARCHIVE_H +#define LLDB_SOURCE_PLUGINS_OBJECTCONTAINER_BIG_ARCHIVE_OBJECTCONTAINERBIGARCHIVE_H + +#include "lldb/Core/UniqueCStringMap.h" +#include "lldb/Symbol/ObjectContainer.h" +#include "lldb/Utility/ArchSpec.h" +#include "lldb/Utility/ConstString.h" +#include "lldb/Utility/FileSpec.h" + +#include "llvm/Support/Chrono.h" + +#include +#include +#include + +class ObjectContainerBigArchive : public lldb_private::ObjectContainer { +public: + ObjectContainerBigArchive(const lldb::ModuleSP &module_sp, + lldb::DataBufferSP &data_sp, + lldb::offset_t data_offset, + const lldb_private::FileSpec *file, + lldb::offset_t offset, lldb::offset_t length); + + ~ObjectContainerBigArchive() override; + + // Static Functions + static void Initialize(); + + static void Terminate(); + + static llvm::StringRef GetPluginNameStatic() { return "big-archive"; } + + static llvm::StringRef GetPluginDescriptionStatic() { + return "Big Archive object container reader."; + } + + static lldb_private::ObjectContainer * + CreateInstance(const lldb::ModuleSP &module_sp, lldb::DataBufferSP &data_sp, + lldb::offset_t data_offset, const lldb_private::FileSpec *file, + lldb::offset_t offset, lldb::offset_t length); + + static size_t GetModuleSpecifications(const lldb_private::FileSpec &file, + lldb::DataBufferSP &data_sp, + lldb::offset_t data_offset, + lldb::offset_t file_offset, + lldb::offset_t length, + lldb_private::ModuleSpecList &specs); + + static bool MagicBytesMatch(const lldb_private::DataExtractor &data); + + // Member Functions + bool ParseHeader() override; + + size_t GetNumObjects() const override { + if (m_archive_sp) + return m_archive_sp->GetNumObjects(); + return 0; + } + + lldb::ObjectFileSP GetObjectFile(const lldb_private::FileSpec *file) override; + + // PluginInterface protocol + llvm::StringRef GetPluginName() override { return GetPluginNameStatic(); } + +protected: + struct Object { + Object(); + + void Clear(); + + lldb::offset_t Extract(const lldb_private::DataExtractor &data, + lldb::offset_t offset); + /// Object name in the archive. + lldb_private::ConstString ar_name; + + /// Object modification time in the archive. + uint32_t modification_time = 0; + + /// Object user id in the archive. + uint16_t uid = 0; + + /// Object group id in the archive. + uint16_t gid = 0; + + /// Object octal file permissions in the archive. + uint16_t mode = 0; + + /// Object size in bytes in the archive. + uint32_t size = 0; + + /// File offset in bytes from the beginning of the file of the object data. + lldb::offset_t file_offset = 0; + + /// Length of the object data. + lldb::offset_t file_size = 0; + + void Dump(lldb_private::Stream *s) const; + }; + + class Archive { + public: + typedef std::shared_ptr shared_ptr; + typedef std::multimap Map; + + Archive(const lldb_private::ArchSpec &arch, + const llvm::sys::TimePoint<> &mod_time, lldb::offset_t file_offset, + lldb_private::DataExtractor &data); + + ~Archive(); + + static Map &GetArchiveCache(); + + static std::recursive_mutex &GetArchiveCacheMutex(); + + static Archive::shared_ptr FindCachedArchive( + const lldb_private::FileSpec &file, const lldb_private::ArchSpec &arch, + const llvm::sys::TimePoint<> &mod_time, lldb::offset_t file_offset); + + static Archive::shared_ptr ParseAndCacheArchiveForFile( + const lldb_private::FileSpec &file, const lldb_private::ArchSpec &arch, + const llvm::sys::TimePoint<> &mod_time, lldb::offset_t file_offset, + lldb_private::DataExtractor &data); + + size_t GetNumObjects() const { return m_objects.size(); } + + const Object *GetObjectAtIndex(size_t idx) { + if (idx < m_objects.size()) + return &m_objects[idx]; + return nullptr; + } + + size_t ParseObjects(); + + Object *FindObject(lldb_private::ConstString object_name, + const llvm::sys::TimePoint<> &object_mod_time); + + lldb::offset_t GetFileOffset() const { return m_file_offset; } + + const llvm::sys::TimePoint<> &GetModificationTime() { + return m_modification_time; + } + + const lldb_private::ArchSpec &GetArchitecture() const { return m_arch; } + + void SetArchitecture(const lldb_private::ArchSpec &arch) { m_arch = arch; } + + bool HasNoExternalReferences() const; + + lldb_private::DataExtractor &GetData() { return m_data; } + + protected: + typedef lldb_private::UniqueCStringMap ObjectNameToIndexMap; + // Member Variables + lldb_private::ArchSpec m_arch; + llvm::sys::TimePoint<> m_modification_time; + lldb::offset_t m_file_offset; + std::vector m_objects; + ObjectNameToIndexMap m_object_name_to_index_map; + lldb_private::DataExtractor m_data; ///< The data for this object container + ///so we don't lose data if the .a files + ///gets modified + }; + + void SetArchive(Archive::shared_ptr &archive_sp); + + Archive::shared_ptr m_archive_sp; +}; + +#endif // LLDB_SOURCE_PLUGINS_OBJECTCONTAINER_BIG_ARCHIVE_OBJECTCONTAINERBIGARCHIVE_H diff --git a/lldb/source/Plugins/ObjectContainer/CMakeLists.txt b/lldb/source/Plugins/ObjectContainer/CMakeLists.txt index 4ae1bb138a9a4..fd99e10ad71e7 100644 --- a/lldb/source/Plugins/ObjectContainer/CMakeLists.txt +++ b/lldb/source/Plugins/ObjectContainer/CMakeLists.txt @@ -1,5 +1,6 @@ set_property(DIRECTORY PROPERTY LLDB_PLUGIN_KIND ObjectContainer) add_subdirectory(BSD-Archive) +add_subdirectory(Big-Archive) add_subdirectory(Universal-Mach-O) add_subdirectory(Mach-O-Fileset) diff --git a/lldb/source/Plugins/ObjectFile/AIXCore/CMakeLists.txt b/lldb/source/Plugins/ObjectFile/AIXCore/CMakeLists.txt new file mode 100644 index 0000000000000..5656b33a61726 --- /dev/null +++ b/lldb/source/Plugins/ObjectFile/AIXCore/CMakeLists.txt @@ -0,0 +1,13 @@ +add_lldb_library(lldbPluginObjectFileAIXCore PLUGIN + ObjectFileAIXCore.cpp + + LINK_LIBS + lldbCore + lldbHost + lldbSymbol + lldbTarget + lldbUtility + lldbPluginProcessUtility + LINK_COMPONENTS + Support + ) diff --git a/lldb/source/Plugins/ObjectFile/AIXCore/ObjectFileAIXCore.cpp b/lldb/source/Plugins/ObjectFile/AIXCore/ObjectFileAIXCore.cpp new file mode 100644 index 0000000000000..0ba1056866937 --- /dev/null +++ b/lldb/source/Plugins/ObjectFile/AIXCore/ObjectFileAIXCore.cpp @@ -0,0 +1,253 @@ +//===-- ObjectFileAIXCore.cpp -------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "ObjectFileAIXCore.h" + +#include +#include +#include +#include + +#include "lldb/Utility/FileSpecList.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/ModuleSpec.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Host/FileSystem.h" +#include "lldb/Symbol/SymbolContext.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/Target.h" +#include "lldb/Utility/ArchSpec.h" +#include "lldb/Utility/DataBufferHeap.h" +#include "lldb/Utility/LLDBLog.h" +#include "lldb/Utility/Log.h" +#include "lldb/Utility/Stream.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/BinaryFormat/XCOFF.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Object/XCOFFObjectFile.h" + +using namespace llvm; +using namespace lldb; +using namespace lldb_private; + +LLDB_PLUGIN_DEFINE(ObjectFileAIXCore) + +enum CoreVersion : uint64_t {AIXCORE32 = 0xFEEDDB1, AIXCORE64 = 0xFEEDDB2}; + +bool m_is_core = false; + +// Static methods. +void ObjectFileAIXCore::Initialize() { + PluginManager::RegisterPlugin(GetPluginNameStatic(), + GetPluginDescriptionStatic(), CreateInstance, + CreateMemoryInstance, GetModuleSpecifications); +} + +void ObjectFileAIXCore::Terminate() { + PluginManager::UnregisterPlugin(CreateInstance); +} + +ObjectFile *ObjectFileAIXCore::CreateInstance(const lldb::ModuleSP &module_sp, + DataBufferSP data_sp, + lldb::offset_t data_offset, + const lldb_private::FileSpec *file, + lldb::offset_t file_offset, + lldb::offset_t length) { + + if(m_is_core) + { + + bool mapped_writable = false; + if (!data_sp) { + data_sp = MapFileDataWritable(*file, length, file_offset); + if (!data_sp) + return nullptr; + data_offset = 0; + mapped_writable = true; + } + + assert(data_sp); + + // Update the data to contain the entire file if it doesn't already + if (data_sp->GetByteSize() < length) { + data_sp = MapFileDataWritable(*file, length, file_offset); + if (!data_sp) + return nullptr; + data_offset = 0; + mapped_writable = true; + } + + // If we didn't map the data as writable take ownership of the buffer. + if (!mapped_writable) { + data_sp = std::make_shared(data_sp->GetBytes(), + data_sp->GetByteSize()); + data_offset = 0; + } + + std::unique_ptr objfile_up(new ObjectFileAIXCore( + module_sp, data_sp, data_offset, file, file_offset, length)); + ArchSpec spec = objfile_up->GetArchitecture(); + objfile_up->SetModulesArchitecture(spec); + return objfile_up.release(); + + } +} + +ObjectFile *ObjectFileAIXCore::CreateMemoryInstance( + const lldb::ModuleSP &module_sp, WritableDataBufferSP data_sp, + const lldb::ProcessSP &process_sp, lldb::addr_t header_addr) { + return nullptr; +} + +size_t ObjectFileAIXCore::GetModuleSpecifications( + const lldb_private::FileSpec &file, lldb::DataBufferSP &data_sp, + lldb::offset_t data_offset, lldb::offset_t file_offset, + lldb::offset_t length, lldb_private::ModuleSpecList &specs) { + const size_t initial_count = specs.GetSize(); + + if (ObjectFileAIXCore::MagicBytesMatch(data_sp, 0, data_sp->GetByteSize())) { + // Need new ArchType??? + ArchSpec arch_spec = ArchSpec(eArchTypeXCOFF, XCOFF::TCPU_PPC64, LLDB_INVALID_CPUTYPE); + ModuleSpec spec(file, arch_spec); + spec.GetArchitecture().SetArchitecture(eArchTypeXCOFF, XCOFF::TCPU_PPC64, LLDB_INVALID_CPUTYPE, llvm::Triple::AIX); + specs.Append(spec); + } + return specs.GetSize() - initial_count; +} + +static bool AIXCoreHeaderCheckFromMagic(uint32_t magic) { + + Log *log = GetLog(LLDBLog::Modules); + bool ret = false; + switch (magic) { + case AIXCORE32: + LLDB_LOGF(log, "ObjectFileAIXCore: 32-bit not supported"); + break; + case AIXCORE64: + m_is_core = true; + ret = true; + break; + default: + break; + } + return ret; +} + +bool ObjectFileAIXCore::MagicBytesMatch(DataBufferSP &data_sp, + lldb::addr_t data_offset, + lldb::addr_t data_length) { + lldb_private::DataExtractor data; + data.SetData(data_sp, data_offset, data_length); + lldb::offset_t offset = 0; + offset += 4; // Skipping to the coredump version + uint32_t magic = data.GetU32(&offset); + return AIXCoreHeaderCheckFromMagic(magic); +} + +bool ObjectFileAIXCore::ParseHeader() { + + return false; +} + +ByteOrder ObjectFileAIXCore::GetByteOrder() const { + return eByteOrderBig; +} + +bool ObjectFileAIXCore::IsExecutable() const { + return false; +} + +uint32_t ObjectFileAIXCore::GetAddressByteSize() const { + return 8; +} + +AddressClass ObjectFileAIXCore::GetAddressClass(addr_t file_addr) { + return AddressClass::eUnknown; +} + +lldb::SymbolType ObjectFileAIXCore::MapSymbolType(llvm::object::SymbolRef::Type sym_type) { + if (sym_type == llvm::object::SymbolRef::ST_Function) + return lldb::eSymbolTypeCode; + else if (sym_type == llvm::object::SymbolRef::ST_Data) + return lldb::eSymbolTypeData; + return lldb::eSymbolTypeInvalid; +} + +void ObjectFileAIXCore::ParseSymtab(Symtab &lldb_symtab) { +} + +bool ObjectFileAIXCore::IsStripped() { + return false; +} + +void ObjectFileAIXCore::CreateSections(SectionList &unified_section_list) { +} + +void ObjectFileAIXCore::Dump(Stream *s) { +} + +ArchSpec ObjectFileAIXCore::GetArchitecture() { + ArchSpec arch_spec = ArchSpec(eArchTypeXCOFF, XCOFF::TCPU_PPC64, LLDB_INVALID_CPUTYPE); + return arch_spec; +} + +UUID ObjectFileAIXCore::GetUUID() { + return UUID(); +} + +uint32_t ObjectFileAIXCore::GetDependentModules(FileSpecList &files) { + + auto original_size = files.GetSize(); + return files.GetSize() - original_size; +} + +Address ObjectFileAIXCore::GetImageInfoAddress(Target *target) { + return Address(); +} + +lldb_private::Address ObjectFileAIXCore::GetBaseAddress() { + return lldb_private::Address(); +} +ObjectFile::Type ObjectFileAIXCore::CalculateType() { + return eTypeCoreFile; +} + +ObjectFile::Strata ObjectFileAIXCore::CalculateStrata() { + return eStrataUnknown; +} + +std::vector +ObjectFileAIXCore::GetLoadableData(Target &target) { + std::vector loadables; + return loadables; +} + +lldb::WritableDataBufferSP +ObjectFileAIXCore::MapFileDataWritable(const FileSpec &file, uint64_t Size, + uint64_t Offset) { + return FileSystem::Instance().CreateWritableDataBuffer(file.GetPath(), Size, + Offset); +} + +ObjectFileAIXCore::ObjectFileAIXCore(const lldb::ModuleSP &module_sp, + DataBufferSP data_sp, lldb::offset_t data_offset, + const FileSpec *file, lldb::offset_t file_offset, + lldb::offset_t length) + : ObjectFile(module_sp, file, file_offset, length, data_sp, data_offset) + { + if (file) + m_file = *file; +} + +ObjectFileAIXCore::ObjectFileAIXCore(const lldb::ModuleSP &module_sp, + DataBufferSP header_data_sp, + const lldb::ProcessSP &process_sp, + addr_t header_addr) + : ObjectFile(module_sp, process_sp, header_addr, header_data_sp) + { +} diff --git a/lldb/source/Plugins/ObjectFile/AIXCore/ObjectFileAIXCore.h b/lldb/source/Plugins/ObjectFile/AIXCore/ObjectFileAIXCore.h new file mode 100644 index 0000000000000..5dbd78d919bb6 --- /dev/null +++ b/lldb/source/Plugins/ObjectFile/AIXCore/ObjectFileAIXCore.h @@ -0,0 +1,121 @@ +//===-- ObjectFileAIXCore.h --------------------------------------- -*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SOURCE_PLUGINS_OBJECTFILE_AIXCORE_OBJECTFILEAIXCORE_H +#define LLDB_SOURCE_PLUGINS_OBJECTFILE_AIXCORE_OBJECTFILEAIXCORE_H + +#include + +#include + +#include "lldb/Symbol/ObjectFile.h" +#include "lldb/Utility/ArchSpec.h" +#include "lldb/Utility/FileSpec.h" +#include "lldb/Utility/UUID.h" +#include "lldb/lldb-private.h" +#include "llvm/Object/XCOFFObjectFile.h" + +/// \class ObjectFileAIXCore +/// Generic AIX CORE object file reader. +/// +/// This class provides a generic AIX Core (32/64 bit) reader plugin implementing +/// the ObjectFile protocol. +class ObjectFileAIXCore : public lldb_private::ObjectFile { +public: + // Static Functions + static void Initialize(); + + static void Terminate(); + + static llvm::StringRef GetPluginNameStatic() { return "aix-core-obj"; } + + static llvm::StringRef GetPluginDescriptionStatic() { + return "AIX core object file reader."; + } + + static lldb_private::ObjectFile * + CreateInstance(const lldb::ModuleSP &module_sp, lldb::DataBufferSP data_sp, + lldb::offset_t data_offset, const lldb_private::FileSpec *file, + lldb::offset_t file_offset, lldb::offset_t length); + + static lldb_private::ObjectFile *CreateMemoryInstance( + const lldb::ModuleSP &module_sp, lldb::WritableDataBufferSP data_sp, + const lldb::ProcessSP &process_sp, lldb::addr_t header_addr); + + static size_t GetModuleSpecifications(const lldb_private::FileSpec &file, + lldb::DataBufferSP &data_sp, + lldb::offset_t data_offset, + lldb::offset_t file_offset, + lldb::offset_t length, + lldb_private::ModuleSpecList &specs); + + static bool MagicBytesMatch(lldb::DataBufferSP &data_sp, lldb::addr_t offset, + lldb::addr_t length); + + static lldb::SymbolType MapSymbolType(llvm::object::SymbolRef::Type sym_type); + + // PluginInterface protocol + llvm::StringRef GetPluginName() override { return GetPluginNameStatic(); } + + // ObjectFile Protocol. + bool ParseHeader() override; + + lldb::ByteOrder GetByteOrder() const override; + + bool IsExecutable() const override; + + uint32_t GetAddressByteSize() const override; + + lldb_private::AddressClass GetAddressClass(lldb::addr_t file_addr) override; + + void ParseSymtab(lldb_private::Symtab &symtab) override; + + bool IsStripped() override; + + void CreateSections(lldb_private::SectionList &unified_section_list) override; + + void Dump(lldb_private::Stream *s) override; + + lldb_private::ArchSpec GetArchitecture() override; + + lldb_private::UUID GetUUID() override; + + uint32_t GetDependentModules(lldb_private::FileSpecList &files) override; + + lldb_private::Address + GetImageInfoAddress(lldb_private::Target *target) override; + lldb_private::Address GetBaseAddress() override; + + ObjectFile::Type CalculateType() override; + + ObjectFile::Strata CalculateStrata() override; + + ObjectFileAIXCore(const lldb::ModuleSP &module_sp, lldb::DataBufferSP data_sp, + lldb::offset_t data_offset, const lldb_private::FileSpec *file, + lldb::offset_t offset, lldb::offset_t length); + + ObjectFileAIXCore(const lldb::ModuleSP &module_sp, + lldb::DataBufferSP header_data_sp, + const lldb::ProcessSP &process_sp, lldb::addr_t header_addr); + +protected: + + static bool ParseAIXCoreHeader(lldb_private::DataExtractor &data, + lldb::offset_t *offset_ptr + ); + + std::vector + GetLoadableData(lldb_private::Target &target) override; + + static lldb::WritableDataBufferSP + MapFileDataWritable(const lldb_private::FileSpec &file, uint64_t Size, + uint64_t Offset); + +}; + +#endif // LLDB_SOURCE_PLUGINS_OBJECTFILE_AIXCORE_OBJECTFILEAIXCORE_H diff --git a/lldb/source/Plugins/ObjectFile/CMakeLists.txt b/lldb/source/Plugins/ObjectFile/CMakeLists.txt index 6004b1f414d43..df2a7174c58e0 100644 --- a/lldb/source/Plugins/ObjectFile/CMakeLists.txt +++ b/lldb/source/Plugins/ObjectFile/CMakeLists.txt @@ -11,3 +11,4 @@ add_subdirectory(PECOFF) add_subdirectory(XCOFF) add_subdirectory(Placeholder) add_subdirectory(wasm) +add_subdirectory(AIXCore) diff --git a/lldb/source/Plugins/ObjectFile/Minidump/ObjectFileMinidump.cpp b/lldb/source/Plugins/ObjectFile/Minidump/ObjectFileMinidump.cpp index be47991bb09fc..9aab76c6c48ba 100644 --- a/lldb/source/Plugins/ObjectFile/Minidump/ObjectFileMinidump.cpp +++ b/lldb/source/Plugins/ObjectFile/Minidump/ObjectFileMinidump.cpp @@ -51,7 +51,9 @@ size_t ObjectFileMinidump::GetModuleSpecifications( const lldb_private::FileSpec &file, lldb::DataBufferSP &data_sp, lldb::offset_t data_offset, lldb::offset_t file_offset, lldb::offset_t length, lldb_private::ModuleSpecList &specs) { +#if !defined(_AIX) specs.Clear(); +#endif return 0; } diff --git a/lldb/source/Plugins/ObjectFile/PDB/ObjectFilePDB.cpp b/lldb/source/Plugins/ObjectFile/PDB/ObjectFilePDB.cpp index f0832dbf07347..d76d6adb1be2c 100644 --- a/lldb/source/Plugins/ObjectFile/PDB/ObjectFilePDB.cpp +++ b/lldb/source/Plugins/ObjectFile/PDB/ObjectFilePDB.cpp @@ -116,18 +116,31 @@ size_t ObjectFilePDB::GetModuleSpecifications( ModuleSpec module_spec(file); llvm::BumpPtrAllocator allocator; std::unique_ptr pdb_file = loadPDBFile(file.GetPath(), allocator); - if (!pdb_file) + if (!pdb_file){ +#if !defined(_AIX) return initial_count; +#else + return specs.GetSize() - initial_count; +#endif + } auto info_stream = pdb_file->getPDBInfoStream(); if (!info_stream) { llvm::consumeError(info_stream.takeError()); +#if !defined(_AIX) return initial_count; +#else + return specs.GetSize() - initial_count; +#endif } auto dbi_stream = pdb_file->getPDBDbiStream(); if (!dbi_stream) { llvm::consumeError(dbi_stream.takeError()); +#if !defined(_AIX) return initial_count; +#else + return specs.GetSize() - initial_count; +#endif } lldb_private::UUID &uuid = module_spec.GetUUID(); diff --git a/lldb/source/Plugins/ObjectFile/PECOFF/ObjectFilePECOFF.cpp b/lldb/source/Plugins/ObjectFile/PECOFF/ObjectFilePECOFF.cpp index 4984445dcbab9..7e16ec05f25dd 100644 --- a/lldb/source/Plugins/ObjectFile/PECOFF/ObjectFilePECOFF.cpp +++ b/lldb/source/Plugins/ObjectFile/PECOFF/ObjectFilePECOFF.cpp @@ -253,8 +253,13 @@ size_t ObjectFilePECOFF::GetModuleSpecifications( lldb::offset_t data_offset, lldb::offset_t file_offset, lldb::offset_t length, lldb_private::ModuleSpecList &specs) { const size_t initial_count = specs.GetSize(); - if (!data_sp || !ObjectFilePECOFF::MagicBytesMatch(data_sp)) + if (!data_sp || !ObjectFilePECOFF::MagicBytesMatch(data_sp)){ +#if !defined(_AIX) return initial_count; +#else + return specs.GetSize() - initial_count; +#endif + } Log *log = GetLog(LLDBLog::Object); @@ -267,12 +272,21 @@ size_t ObjectFilePECOFF::GetModuleSpecifications( if (!binary) { LLDB_LOG_ERROR(log, binary.takeError(), "Failed to create binary for file ({1}): {0}", file); +#if !defined(_AIX) return initial_count; +#else + return specs.GetSize() - initial_count; +#endif } auto *COFFObj = llvm::dyn_cast(binary->get()); - if (!COFFObj) + if (!COFFObj){ +#if !defined(_AIX) return initial_count; +#else + return specs.GetSize() - initial_count; +#endif + } ModuleSpec module_spec(file); ArchSpec &spec = module_spec.GetArchitecture(); diff --git a/lldb/source/Plugins/ObjectFile/XCOFF/ObjectFileXCOFF.cpp b/lldb/source/Plugins/ObjectFile/XCOFF/ObjectFileXCOFF.cpp index d2c46edaf28cb..2602b338eb2c5 100644 --- a/lldb/source/Plugins/ObjectFile/XCOFF/ObjectFileXCOFF.cpp +++ b/lldb/source/Plugins/ObjectFile/XCOFF/ObjectFileXCOFF.cpp @@ -14,7 +14,10 @@ #include "lldb/Core/Progress.h" #include "lldb/Core/Section.h" #include "lldb/Host/FileSystem.h" +#include "lldb/Host/LZMA.h" +#include "lldb/Symbol/DWARFCallFrameInfo.h" #include "lldb/Symbol/SymbolContext.h" +#include "lldb/Target/SectionLoadList.h" #include "lldb/Target/Process.h" #include "lldb/Target/Target.h" #include "lldb/Utility/ArchSpec.h" @@ -25,9 +28,16 @@ #include "lldb/Utility/RangeMap.h" #include "lldb/Utility/Status.h" #include "lldb/Utility/Stream.h" +#include "lldb/Utility/Timer.h" +#include "llvm/ADT/IntervalMap.h" +#include "llvm/ADT/PointerUnion.h" #include "llvm/ADT/StringRef.h" #include "llvm/BinaryFormat/XCOFF.h" #include "llvm/Object/XCOFFObjectFile.h" +#include "llvm/Object/Decompressor.h" +#include "llvm/Support/CRC.h" +#include "llvm/Support/FormatVariadic.h" +#include "llvm/Support/MathExtras.h" #include "llvm/Support/MemoryBuffer.h" #include #include @@ -52,6 +62,8 @@ void ObjectFileXCOFF::Terminate() { PluginManager::UnregisterPlugin(CreateInstance); } +bool UGLY_FLAG_FOR_AIX __attribute__((weak)) = false; + ObjectFile *ObjectFileXCOFF::CreateInstance(const lldb::ModuleSP &module_sp, DataBufferSP data_sp, lldb::offset_t data_offset, @@ -85,6 +97,7 @@ ObjectFile *ObjectFileXCOFF::CreateInstance(const lldb::ModuleSP &module_sp, if (!objfile_up->ParseHeader()) return nullptr; + UGLY_FLAG_FOR_AIX = true; return objfile_up.release(); } @@ -174,6 +187,80 @@ bool ObjectFileXCOFF::ParseHeader() { return m_binary->fileHeader32()->Magic == XCOFF::XCOFF32; } +bool ObjectFileXCOFF::SetLoadAddress(Target &target, lldb::addr_t value, + bool value_is_offset) { + bool changed = false; + ModuleSP module_sp = GetModule(); + if (module_sp) { + size_t num_loaded_sections = 0; + SectionList *section_list = GetSectionList(); + + if (section_list) { + const size_t num_sections = section_list->GetSize(); + size_t sect_idx = 0; + + for (sect_idx = 0; sect_idx < num_sections; ++sect_idx) { + // Iterate through the object file sections to find all of the sections + // that have SHF_ALLOC in their flag bits. + SectionSP section_sp(section_list->GetSectionAtIndex(sect_idx)); + + if (section_sp && !section_sp->IsThreadSpecific()) { + addr_t load_addr = 0; + if (!value_is_offset) + load_addr = section_sp->GetFileAddress(); + else { + if (strcmp(section_sp->GetName().AsCString(), ".text") == 0) + load_addr = section_sp->GetFileOffset() + value; + else /* Other sections: data, bss, loader, dwline, dwinfo, dwabrev */ + load_addr = section_sp->GetFileAddress() + value; + } + if (target.GetSectionLoadListPublic().SetSectionLoadAddress( + section_sp, load_addr)) + ++num_loaded_sections; + } + } + changed = num_loaded_sections > 0; + } + } + return changed; +} + +bool ObjectFileXCOFF::SetLoadAddressByType(Target &target, lldb::addr_t value, + bool value_is_offset, int type_id) { + bool changed = false; + ModuleSP module_sp = GetModule(); + if (module_sp) { + size_t num_loaded_sections = 0; + SectionList *section_list = GetSectionList(); + if (section_list) { + const size_t num_sections = section_list->GetSize(); + size_t sect_idx = 0; + + for (sect_idx = 0; sect_idx < num_sections; ++sect_idx) { + // Iterate through the object file sections to find all of the sections + // that have SHF_ALLOC in their flag bits. + SectionSP section_sp(section_list->GetSectionAtIndex(sect_idx)); + if (type_id == 1 && section_sp && strcmp(section_sp->GetName().AsCString(), ".text") == 0) { + if (!section_sp->IsThreadSpecific()) { + if (target.GetSectionLoadListPublic().SetSectionLoadAddress( + section_sp, section_sp->GetFileOffset() + value)) + ++num_loaded_sections; + } + } else if (type_id == 2 && section_sp && strcmp(section_sp->GetName().AsCString(), ".data") == 0) { + if (!section_sp->IsThreadSpecific()) { + if (target.GetSectionLoadListPublic().SetSectionLoadAddress( + section_sp, section_sp->GetFileAddress() + value)) + ++num_loaded_sections; + } + } + } + changed = num_loaded_sections > 0; + } + } + return changed; +} + + ByteOrder ObjectFileXCOFF::GetByteOrder() const { return eByteOrderBig; } bool ObjectFileXCOFF::IsExecutable() const { return true; } @@ -370,7 +457,82 @@ ArchSpec ObjectFileXCOFF::GetArchitecture() { UUID ObjectFileXCOFF::GetUUID() { return UUID(); } -uint32_t ObjectFileXCOFF::GetDependentModules(FileSpecList &files) { return 0; } +std::optional ObjectFileXCOFF::GetDebugLink() { + return std::nullopt; +} + +uint32_t ObjectFileXCOFF::ParseDependentModules() { + ModuleSP module_sp(GetModule()); + if (!module_sp) + return 0; + + std::lock_guard guard(module_sp->GetMutex()); + if (m_deps_filespec) + return m_deps_filespec->GetSize(); + + // Cache coff binary if it is not done yet. + if (!CreateBinary()) + return 0; + + Log *log = GetLog(LLDBLog::Object); + LLDB_LOG(log, "this = {0}, module = {1} ({2}), file = {3}, binary = {4}", + this, GetModule().get(), GetModule()->GetSpecificationDescription(), + m_file.GetPath(), m_binary.get()); + + m_deps_filespec = FileSpecList(); + + auto ImportFilesOrError = m_binary->getImportFileTable(); + if (!ImportFilesOrError) { + consumeError(ImportFilesOrError.takeError()); + return 0; + } + return m_deps_filespec->GetSize(); +} + +uint32_t ObjectFileXCOFF::GetDependentModules(FileSpecList &files) { + auto num_modules = ParseDependentModules(); + auto original_size = files.GetSize(); + + for (unsigned i = 0; i < num_modules; ++i) + files.AppendIfUnique(m_deps_filespec->GetFileSpecAtIndex(i)); + + return files.GetSize() - original_size; +} + +Address ObjectFileXCOFF::GetImageInfoAddress(Target *target) { + return Address(); +} + +lldb_private::Address ObjectFileXCOFF::GetEntryPointAddress() { + if (m_entry_point_address.IsValid()) + return m_entry_point_address; + + if (!ParseHeader() || !IsExecutable()) + return m_entry_point_address; + + SectionList *section_list = GetSectionList(); + addr_t vm_addr = m_binary->is64Bit() ? m_binary->auxiliaryHeader64()->EntryPointAddr : + m_binary->auxiliaryHeader32()->EntryPointAddr; + SectionSP section_sp( + section_list->FindSectionContainingFileAddress(vm_addr)); + if (section_sp) { + lldb::offset_t offset_ptr = section_sp->GetFileOffset() + (vm_addr - section_sp->GetFileAddress()); + vm_addr = m_data.GetU64(&offset_ptr); + } + + if (!section_list) + m_entry_point_address.SetOffset(vm_addr); + else + m_entry_point_address.ResolveAddressUsingFileSections(vm_addr, + section_list); + + return m_entry_point_address; +} + +lldb_private::Address ObjectFileXCOFF::GetBaseAddress() { + // Get base address of the section + return Address(GetSectionList()->GetSectionAtIndex(0), 0); +} ObjectFile::Type ObjectFileXCOFF::CalculateType() { @@ -386,6 +548,21 @@ ObjectFile::Type ObjectFileXCOFF::CalculateType() { ObjectFile::Strata ObjectFileXCOFF::CalculateStrata() { return eStrataUnknown; } +llvm::StringRef +ObjectFileXCOFF::StripLinkerSymbolAnnotations(llvm::StringRef symbol_name) const { + return llvm::StringRef(); +} + +void ObjectFileXCOFF::RelocateSection(lldb_private::Section *section) +{ +} + +std::vector +ObjectFileXCOFF::GetLoadableData(Target &target) { + std::vector loadables; + return loadables; +} + lldb::WritableDataBufferSP ObjectFileXCOFF::MapFileDataWritable(const FileSpec &file, uint64_t Size, uint64_t Offset) { diff --git a/lldb/source/Plugins/ObjectFile/XCOFF/ObjectFileXCOFF.h b/lldb/source/Plugins/ObjectFile/XCOFF/ObjectFileXCOFF.h index 2cecd0315463a..ce4bcaa16df5a 100644 --- a/lldb/source/Plugins/ObjectFile/XCOFF/ObjectFileXCOFF.h +++ b/lldb/source/Plugins/ObjectFile/XCOFF/ObjectFileXCOFF.h @@ -62,6 +62,12 @@ class ObjectFileXCOFF : public lldb_private::ObjectFile { // ObjectFile Protocol. bool ParseHeader() override; + bool SetLoadAddress(lldb_private::Target &target, lldb::addr_t value, + bool value_is_offset) override; + + bool SetLoadAddressByType(lldb_private::Target &target, lldb::addr_t value, + bool value_is_offset, int type_id) override; + lldb::ByteOrder GetByteOrder() const override; bool IsExecutable() const override; @@ -82,11 +88,27 @@ class ObjectFileXCOFF : public lldb_private::ObjectFile { lldb_private::UUID GetUUID() override; + /// Return the contents of the .gnu_debuglink section, if the object file + /// contains it. + std::optional GetDebugLink(); + uint32_t GetDependentModules(lldb_private::FileSpecList &files) override; + lldb_private::Address + GetImageInfoAddress(lldb_private::Target *target) override; + + lldb_private::Address GetEntryPointAddress() override; + + lldb_private::Address GetBaseAddress() override; + ObjectFile::Type CalculateType() override; ObjectFile::Strata CalculateStrata() override; + + llvm::StringRef + StripLinkerSymbolAnnotations(llvm::StringRef symbol_name) const override; + + void RelocateSection(lldb_private::Section *section) override; ObjectFileXCOFF(const lldb::ModuleSP &module_sp, lldb::DataBufferSP data_sp, lldb::offset_t data_offset, @@ -101,6 +123,10 @@ class ObjectFileXCOFF : public lldb_private::ObjectFile { static lldb::WritableDataBufferSP MapFileDataWritable(const lldb_private::FileSpec &file, uint64_t Size, uint64_t Offset); + std::vector + GetLoadableData(lldb_private::Target &target) override; + uint32_t ParseDependentModules(); + private: bool CreateBinary(); @@ -118,6 +144,9 @@ class ObjectFileXCOFF : public lldb_private::ObjectFile { }; std::unique_ptr m_binary; + lldb_private::Address m_entry_point_address; + std::optional m_deps_filespec; + std::map> m_deps_base_members; }; #endif // LLDB_SOURCE_PLUGINS_OBJECTFILE_XCOFF_OBJECTFILE_H diff --git a/lldb/source/Plugins/Process/AIX/CMakeLists.txt b/lldb/source/Plugins/Process/AIX/CMakeLists.txt index 3a6d9ec118e60..52ace6ba7e652 100644 --- a/lldb/source/Plugins/Process/AIX/CMakeLists.txt +++ b/lldb/source/Plugins/Process/AIX/CMakeLists.txt @@ -1,5 +1,7 @@ add_lldb_library(lldbPluginProcessAIX NativeProcessAIX.cpp + NativeRegisterContextAIX.cpp + NativeRegisterContextAIX_ppc64.cpp NativeThreadAIX.cpp NativeRegisterContextAIX.cpp diff --git a/lldb/source/Plugins/Process/AIX/NativeProcessAIX.cpp b/lldb/source/Plugins/Process/AIX/NativeProcessAIX.cpp index cd5e3458e60e8..326495625e74d 100644 --- a/lldb/source/Plugins/Process/AIX/NativeProcessAIX.cpp +++ b/lldb/source/Plugins/Process/AIX/NativeProcessAIX.cpp @@ -1,4 +1,4 @@ -//===-- NativeProcessAIX.cpp ----------------------------------------------===// +//===-- NativeProcessAIX.cpp --------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -7,45 +7,263 @@ //===----------------------------------------------------------------------===// #include "NativeProcessAIX.h" + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include "NativeThreadAIX.h" #include "Plugins/Process/POSIX/ProcessPOSIXLog.h" +//#include "Plugins/Process/Utility/LinuxProcMaps.h" +//#include "Procfs.h" +#include "lldb/Core/ModuleSpec.h" #include "lldb/Host/Host.h" -#include "lldb/Host/HostInfo.h" #include "lldb/Host/HostProcess.h" #include "lldb/Host/ProcessLaunchInfo.h" +#include "lldb/Host/PseudoTerminal.h" +#include "lldb/Host/ThreadLauncher.h" +#include "lldb/Host/common/NativeRegisterContext.h" +#include "lldb/Host/aix/Ptrace.h" +//#include "lldb/Host/linux/Host.h" +//#include "lldb/Host/linux/Uio.h" #include "lldb/Host/posix/ProcessLauncherPosixFork.h" #include "lldb/Symbol/ObjectFile.h" -#include "lldb/Utility/Log.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/Target.h" +#include "lldb/Utility/LLDBAssert.h" +#include "lldb/Utility/LLDBLog.h" +#include "lldb/Utility/RegisterValue.h" #include "lldb/Utility/State.h" #include "lldb/Utility/Status.h" +#include "lldb/Utility/StringExtractor.h" +#include "llvm/ADT/ScopeExit.h" #include "llvm/Support/Errno.h" #include "llvm/Support/Error.h" -#include -#include -#include -#include -#include +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/Threading.h" + +#include #include -#include +#include +#include +//#include +#include +#include +#include +#include + +#ifdef __aarch64__ +#include +#include +#endif + +// Support hardware breakpoints in case it has not been defined +#ifndef TRAP_HWBKPT +#define TRAP_HWBKPT 4 +#endif + +#ifndef HWCAP2_MTE +#define HWCAP2_MTE (1 << 18) +#endif using namespace lldb; using namespace lldb_private; using namespace lldb_private::process_aix; using namespace llvm; +typedef std::function)> AIXMapCallback; +// Private bits we only need internally. + +static bool ProcessVmReadvSupported() { + static bool is_supported; + static llvm::once_flag flag; + + llvm::call_once(flag, [] { + Log *log = GetLog(POSIXLog::Process); + + uint32_t source = 0x47424742; + uint32_t dest = 0; + + struct iovec local, remote; + remote.iov_base = &source; + local.iov_base = &dest; + remote.iov_len = local.iov_len = sizeof source; + +#if 0 + // We shall try if cross-process-memory reads work by attempting to read a + // value from our own process. + ssize_t res = process_vm_readv(getpid(), &local, 1, &remote, 1, 0); + is_supported = (res == sizeof(source) && source == dest); + if (is_supported) + LLDB_LOG(log, + "Detected kernel support for process_vm_readv syscall. " + "Fast memory reads enabled."); + else + LLDB_LOG(log, + "syscall process_vm_readv failed (error: {0}). Fast memory " + "reads disabled.", + llvm::sys::StrError()); +#endif + }); + + return is_supported; +} + +static void MaybeLogLaunchInfo(const ProcessLaunchInfo &info) { + Log *log = GetLog(POSIXLog::Process); + if (!log) + return; + + if (const FileAction *action = info.GetFileActionForFD(STDIN_FILENO)) + LLDB_LOG(log, "setting STDIN to '{0}'", action->GetFileSpec()); + else + LLDB_LOG(log, "leaving STDIN as is"); + + if (const FileAction *action = info.GetFileActionForFD(STDOUT_FILENO)) + LLDB_LOG(log, "setting STDOUT to '{0}'", action->GetFileSpec()); + else + LLDB_LOG(log, "leaving STDOUT as is"); + + if (const FileAction *action = info.GetFileActionForFD(STDERR_FILENO)) + LLDB_LOG(log, "setting STDERR to '{0}'", action->GetFileSpec()); + else + LLDB_LOG(log, "leaving STDERR as is"); + + int i = 0; + for (const char **args = info.GetArguments().GetConstArgumentVector(); *args; + ++args, ++i) + LLDB_LOG(log, "arg {0}: '{1}'", i, *args); +} + +static void DisplayBytes(StreamString &s, void *bytes, uint32_t count) { + uint8_t *ptr = (uint8_t *)bytes; + const uint32_t loop_count = std::min(DEBUG_PTRACE_MAXBYTES, count); + for (uint32_t i = 0; i < loop_count; i++) { + s.Printf("[%x]", *ptr); + ptr++; + } +} + +static void PtraceDisplayBytes(int &req, void *data, size_t data_size) { + Log *log = GetLog(POSIXLog::Ptrace); + if (!log) + return; + StreamString buf; + + switch (req) { + case PTRACE_POKETEXT: { + DisplayBytes(buf, &data, 8); + LLDB_LOGV(log, "PTRACE_POKETEXT {0}", buf.GetData()); + break; + } + case PTRACE_POKEDATA: { + DisplayBytes(buf, &data, 8); + LLDB_LOGV(log, "PTRACE_POKEDATA {0}", buf.GetData()); + break; + } + case PTRACE_POKEUSER: { + DisplayBytes(buf, &data, 8); + LLDB_LOGV(log, "PTRACE_POKEUSER {0}", buf.GetData()); + break; + } + case PTRACE_SETREGS: { + DisplayBytes(buf, data, data_size); + LLDB_LOGV(log, "PTRACE_SETREGS {0}", buf.GetData()); + break; + } + case PTRACE_SETFPREGS: { + DisplayBytes(buf, data, data_size); + LLDB_LOGV(log, "PTRACE_SETFPREGS {0}", buf.GetData()); + break; + } +#if 0 + case PTRACE_SETSIGINFO: { + DisplayBytes(buf, data, sizeof(siginfo_t)); + LLDB_LOGV(log, "PTRACE_SETSIGINFO {0}", buf.GetData()); + break; + } +#endif + case PTRACE_SETREGSET: { + // Extract iov_base from data, which is a pointer to the struct iovec + DisplayBytes(buf, *(void **)data, data_size); + LLDB_LOGV(log, "PTRACE_SETREGSET {0}", buf.GetData()); + break; + } + default: {} + } +} + static constexpr unsigned k_ptrace_word_size = sizeof(void *); static_assert(sizeof(long) >= k_ptrace_word_size, "Size of long must be larger than ptrace word size"); // Simple helper function to ensure flags are enabled on the given file // descriptor. -static llvm::Error SetFDFlags(int fd, int flags) { +static Status EnsureFDFlags(int fd, int flags) { + Status error; + int status = fcntl(fd, F_GETFL); - if (status == -1) - return errorCodeToError(errnoAsErrorCode()); - if (fcntl(fd, F_SETFL, status | flags) == -1) - return errorCodeToError(errnoAsErrorCode()); - return Error::success(); + if (status == -1) { + error = Status::FromErrno(); + // error.SetErrorToErrno(); + return error; + } + + if (fcntl(fd, F_SETFL, status | flags) == -1) { + error = Status::FromErrno(); + // error.SetErrorToErrno(); + return error; + } + + return error; +} + +#if 0 +static llvm::Error AddPtraceScopeNote(llvm::Error original_error) { + Expected ptrace_scope = GetPtraceScope(); + if (auto E = ptrace_scope.takeError()) { + Log *log = GetLog(POSIXLog::Process); + LLDB_LOG(log, "error reading value of ptrace_scope: {0}", E); + + // The original error is probably more interesting than not being able to + // read or interpret ptrace_scope. + return original_error; + } + + // We only have suggestions to provide for 1-3. + switch (*ptrace_scope) { + case 1: + case 2: + return llvm::createStringError( + std::error_code(errno, std::generic_category()), + "The current value of ptrace_scope is %d, which can cause ptrace to " + "fail to attach to a running process. To fix this, run:\n" + "\tsudo sysctl -w kernel.yama.ptrace_scope=0\n" + "For more information, see: " + "https://www.kernel.org/doc/Documentation/security/Yama.txt.", + *ptrace_scope); + case 3: + return llvm::createStringError( + std::error_code(errno, std::generic_category()), + "The current value of ptrace_scope is 3, which will cause ptrace to " + "fail to attach to a running process. This value cannot be changed " + "without rebooting.\n" + "For more information, see: " + "https://www.kernel.org/doc/Documentation/security/Yama.txt."); + case 0: + default: + return original_error; + } } +#endif NativeProcessAIX::Manager::Manager(MainLoop &mainloop) : NativeProcessProtocol::Manager(mainloop) { @@ -59,9 +277,11 @@ NativeProcessAIX::Manager::Manager(MainLoop &mainloop) llvm::Expected> NativeProcessAIX::Manager::Launch(ProcessLaunchInfo &launch_info, - NativeDelegate &native_delegate) { + NativeDelegate &native_delegate) { Log *log = GetLog(POSIXLog::Process); + MaybeLogLaunchInfo(launch_info); + Status status; ::pid_t pid = ProcessLauncherPosixFork() .LaunchProcess(launch_info, status) @@ -85,43 +305,85 @@ NativeProcessAIX::Manager::Launch(ProcessLaunchInfo &launch_info, } LLDB_LOG(log, "inferior started, now in stopped state"); + ProcessInstanceInfo Info; + if (!Host::GetProcessInfo(pid, Info)) { + return llvm::make_error("Cannot get process architectrue", + llvm::inconvertibleErrorCode()); + } + /*llvm::Expected arch_or = + NativeRegisterContextAIX::DetermineArchitecture(pid); + if (!arch_or) + return arch_or.takeError();*/ + + // Set the architecture to the exe architecture. + LLDB_LOG(log, "pid = {0}, detected architecture {1}", pid, + Info.GetArchitecture().GetArchitectureName()); + return std::unique_ptr(new NativeProcessAIX( pid, launch_info.GetPTY().ReleasePrimaryFileDescriptor(), native_delegate, - HostInfo::GetArchitecture(HostInfo::eArchKind64), *this, {pid})); + Info.GetArchitecture(), *this, {pid})); } llvm::Expected> NativeProcessAIX::Manager::Attach( - lldb::pid_t pid, NativeProcessProtocol::NativeDelegate &native_delegate) { + lldb::pid_t pid, NativeProcessProtocol::NativeDelegate &native_delegate) { Log *log = GetLog(POSIXLog::Process); LLDB_LOG(log, "pid = {0:x}", pid); + ProcessInstanceInfo Info; + if (!Host::GetProcessInfo(pid, Info)) { + return llvm::make_error("Cannot get process architectrue", + llvm::inconvertibleErrorCode()); + } auto tids_or = NativeProcessAIX::Attach(pid); if (!tids_or) return tids_or.takeError(); +#if 0 + ArrayRef<::pid_t> tids = *tids_or; + llvm::Expected arch_or = + NativeRegisterContextAIX::DetermineArchitecture(tids[0]); + if (!arch_or) + return arch_or.takeError(); +#endif - return std::unique_ptr(new NativeProcessAIX( - pid, -1, native_delegate, - HostInfo::GetArchitecture(HostInfo::eArchKind64), *this, *tids_or)); + return std::unique_ptr( + new NativeProcessAIX(pid, -1, native_delegate, Info.GetArchitecture(), *this, *tids_or)); } lldb::addr_t NativeProcessAIX::GetSharedLibraryInfoAddress() { + // punt on this for now return LLDB_INVALID_ADDRESS; } +NativeProcessAIX::Extension +NativeProcessAIX::Manager::GetSupportedExtensions() const { + NativeProcessAIX::Extension supported = + Extension::multiprocess | Extension::fork | Extension::vfork | + Extension::pass_signals | Extension::auxv | Extension::libraries_svr4 | + Extension::siginfo_read; + +#ifdef __aarch64__ + // At this point we do not have a process so read auxv directly. + if ((getauxval(AT_HWCAP2) & HWCAP2_MTE)) + supported |= Extension::memory_tagging; +#endif + + return supported; +} + static std::optional> WaitPid() { Log *log = GetLog(POSIXLog::Process); int status; - ::pid_t wait_pid = - llvm::sys::RetryAfterSignal(-1, ::waitpid, -1, &status, WNOHANG); + ::pid_t wait_pid = llvm::sys::RetryAfterSignal( + -1, ::waitpid, -1, &status, /*__WALL | __WNOTHREAD |*/ WNOHANG); if (wait_pid == 0) return std::nullopt; if (wait_pid == -1) { Status error(errno, eErrorTypePOSIX); - LLDB_LOG(log, "waitpid(-1, &status, _) failed: {0}", error); + LLDB_LOG(log, "waitpid(-1, &status, _) failed: {1}", error); return std::nullopt; } @@ -133,26 +395,79 @@ static std::optional> WaitPid() { } void NativeProcessAIX::Manager::SigchldHandler() { + Log *log = GetLog(POSIXLog::Process); while (true) { auto wait_result = WaitPid(); if (!wait_result) return; + lldb::pid_t pid = wait_result->first; + WaitStatus status = wait_result->second; + + // Ask each process whether it wants to handle the event. Each event should + // be handled by exactly one process, but thread creation events require + // special handling. + // Thread creation consists of two events (one on the parent and one on the + // child thread) and they can arrive in any order nondeterministically. The + // parent event carries the information about the child thread, but not + // vice-versa. This means that if the child event arrives first, it may not + // be handled by any process (because it doesn't know the thread belongs to + // it). + bool handled = llvm::any_of(m_processes, [&](NativeProcessAIX *process) { + return process->TryHandleWaitStatus(pid, status); + }); + if (!handled) { + if (status.type == WaitStatus::Stop && status.status == SIGSTOP) { + // Store the thread creation event for later collection. + m_unowned_threads.insert(pid); + } else { + LLDB_LOG(log, "Ignoring waitpid event {0} for pid {1}", status, pid); + } + } } } -void NativeProcessAIX::Manager::CollectThread(::pid_t tid) {} +void NativeProcessAIX::Manager::CollectThread(::pid_t tid) { + Log *log = GetLog(POSIXLog::Process); + + if (m_unowned_threads.erase(tid)) + return; // We've encountered this thread already. + + // The TID is not tracked yet, let's wait for it to appear. + int status = -1; + LLDB_LOG(log, + "received clone event for tid {0}. tid not tracked yet, " + "waiting for it to appear...", + tid); + ::pid_t wait_pid = + llvm::sys::RetryAfterSignal(-1, ::waitpid, tid, &status, P_ALL/*__WALL*/); + + // It's theoretically possible to get other events if the entire process was + // SIGKILLed before we got a chance to check this. In that case, we'll just + // clean everything up when we get the process exit event. + + LLDB_LOG(log, + "waitpid({0}, &status, __WALL) => {1} (errno: {2}, status = {3})", + tid, wait_pid, errno, WaitStatus::Decode(status)); +} // Public Instance Methods NativeProcessAIX::NativeProcessAIX(::pid_t pid, int terminal_fd, - NativeDelegate &delegate, - const ArchSpec &arch, Manager &manager, - llvm::ArrayRef<::pid_t> tids) + NativeDelegate &delegate, + const ArchSpec &arch, Manager &manager, + llvm::ArrayRef<::pid_t> tids) : NativeProcessProtocol(pid, terminal_fd, delegate), m_manager(manager), m_arch(arch) { manager.AddProcess(*this); - if (m_terminal_fd != -1) - cantFail(SetFDFlags(m_terminal_fd, O_NONBLOCK)); + if (m_terminal_fd != -1) { + Status status = EnsureFDFlags(m_terminal_fd, O_NONBLOCK); + assert(status.Success()); + } + + for (const auto &tid : tids) { + NativeThreadAIX &thread = AddThread(tid, /*resume*/ false); + ThreadWasCreated(thread); + } // Let our process instance know the thread has stopped. SetCurrentThreadID(tids[0]); @@ -161,42 +476,922 @@ NativeProcessAIX::NativeProcessAIX(::pid_t pid, int terminal_fd, llvm::Expected> NativeProcessAIX::Attach(::pid_t pid) { Log *log = GetLog(POSIXLog::Process); + Status status; - if (llvm::Error err = PtraceWrapper(PT_ATTACH, pid).takeError()) - return err; + if ((status = PtraceWrapper(PT_ATTACH, pid)).Fail()) { + return status.ToError(); + } + + int wpid = + llvm::sys::RetryAfterSignal(-1, ::waitpid, pid, nullptr, WNOHANG); + if (wpid <= 0) { + return llvm::errorCodeToError( + std::error_code(errno, std::generic_category())); + } - int wpid = llvm::sys::RetryAfterSignal(-1, ::waitpid, pid, nullptr, WNOHANG); - if (wpid <= 0) - return llvm::errorCodeToError(errnoAsErrorCode()); LLDB_LOG(log, "adding pid = {0}", pid); - return std::vector<::pid_t>{pid}; + std::vector<::pid_t> tids; + tids.push_back(pid); + return std::move(tids); +} + +bool NativeProcessAIX::TryHandleWaitStatus(lldb::pid_t pid, + WaitStatus status) { + if (pid == GetID() && + (status.type == WaitStatus::Exit || status.type == WaitStatus::Signal)) { + // The process exited. We're done monitoring. Report to delegate. + SetExitStatus(status, true); + return true; + } + if (NativeThreadAIX *thread = GetThreadByID(pid)) { + MonitorCallback(*thread, status); + return true; + } + return false; +} + +// Handles all waitpid events from the inferior process. +void NativeProcessAIX::MonitorCallback(NativeThreadAIX &thread, + WaitStatus status) { + Log *log = GetLog(LLDBLog::Process); + + // Certain activities differ based on whether the pid is the tid of the main + // thread. + const bool is_main_thread = (thread.GetID() == GetID()); + + // Handle when the thread exits. + if (status.type == WaitStatus::Exit || status.type == WaitStatus::Signal) { + LLDB_LOG(log, + "got exit status({0}) , tid = {1} ({2} main thread), process " + "state = {3}", + status, thread.GetID(), is_main_thread ? "is" : "is not", + GetState()); + + // This is a thread that exited. Ensure we're not tracking it anymore. + StopTrackingThread(thread); + + assert(!is_main_thread && "Main thread exits handled elsewhere"); + return; + } + + int8_t signo = GetSignalInfo(status); + + // Get details on the signal raised. + if (signo) { + // We have retrieved the signal info. Dispatch appropriately. + if (signo == SIGTRAP) + MonitorSIGTRAP(status, thread); + else + MonitorSignal(status, thread); + } else { + assert(0); + } } -bool NativeProcessAIX::SupportHardwareSingleStepping() const { return false; } + +void NativeProcessAIX::MonitorSIGTRAP(const WaitStatus status, + NativeThreadAIX &thread) { + Log *log = GetLog(POSIXLog::Process); + const bool is_main_thread = (thread.GetID() == GetID()); + + NativeRegisterContextAIX ®_ctx = thread.GetRegisterContext(); + const RegisterInfo *pc_info = reg_ctx.GetRegisterInfoByName("pc", 0); + RegisterValue pc_value; + + switch (status.status) { + case SIGTRAP: + // Determine the source of SIGTRAP by checking current instruction: + // if that is trap instruction, then this is breakpoint, otherwise + // this is watchpoint. + reg_ctx.ReadRegister(pc_info, pc_value); + + MonitorBreakpoint(thread); + break; + default: + LLDB_LOG(log, "received unknown SIGTRAP stop event ({0}, pid {1} tid {2}", + status.status, GetID(), thread.GetID()); + MonitorSignal(status, thread); + break; + } +} + +void NativeProcessAIX::MonitorTrace(NativeThreadAIX &thread) { + Log *log = GetLog(POSIXLog::Process); + LLDB_LOG(log, "received trace event, pid = {0}", thread.GetID()); + + // This thread is currently stopped. + thread.SetStoppedByTrace(); + + StopRunningThreads(thread.GetID()); +} + +void NativeProcessAIX::MonitorBreakpoint(NativeThreadAIX &thread) { + Log *log = GetLog(LLDBLog::Process | LLDBLog::Breakpoints); + LLDB_LOG(log, "received breakpoint event, pid = {0}", thread.GetID()); + + // Mark the thread as stopped at breakpoint. + thread.SetStoppedByBreakpoint(); + FixupBreakpointPCAsNeeded(thread); + + if (m_threads_stepping_with_breakpoint.find(thread.GetID()) != + m_threads_stepping_with_breakpoint.end()) + thread.SetStoppedByTrace(); + + StopRunningThreads(thread.GetID()); +} + +void NativeProcessAIX::MonitorWatchpoint(NativeThreadAIX &thread, + uint32_t wp_index) { + Log *log = GetLog(LLDBLog::Process | LLDBLog::Watchpoints); + LLDB_LOG(log, "received watchpoint event, pid = {0}, wp_index = {1}", + thread.GetID(), wp_index); + + // Mark the thread as stopped at watchpoint. The address is at + // (lldb::addr_t)info->si_addr if we need it. + thread.SetStoppedByWatchpoint(wp_index); + + // We need to tell all other running threads before we notify the delegate + // about this stop. + StopRunningThreads(thread.GetID()); +} + +void NativeProcessAIX::MonitorSignal(const WaitStatus status, + NativeThreadAIX &thread) { + int8_t signo = GetSignalInfo(status); +#if 0 + const bool is_from_llgs = info.si_pid == getpid(); +#endif + + Log *log = GetLog(POSIXLog::Process); + + // POSIX says that process behaviour is undefined after it ignores a SIGFPE, + // SIGILL, SIGSEGV, or SIGBUS *unless* that signal was generated by a kill(2) + // or raise(3). Similarly for tgkill(2) on AIX. + // + // IOW, user generated signals never generate what we consider to be a + // "crash". + // + // Similarly, ACK signals generated by this monitor. + + // Handle the signal. + LLDB_LOG(log, + "received signal {0} ({1}) with code NA, (siginfo pid = {2}, " + "waitpid pid = {3})", + Host::GetSignalAsCString(signo), signo, thread.GetID(), GetID()); + +#if 0 + // Check for thread stop notification. + // FIXME + if (is_from_llgs /*&& (info.si_code == SI_TKILL)*/ && (signo == SIGSTOP)) { + // This is a tgkill()-based stop. + LLDB_LOG(log, "pid {0} tid {1}, thread stopped", GetID(), thread.GetID()); + + // Check that we're not already marked with a stop reason. Note this thread + // really shouldn't already be marked as stopped - if we were, that would + // imply that the kernel signaled us with the thread stopping which we + // handled and marked as stopped, and that, without an intervening resume, + // we received another stop. It is more likely that we are missing the + // marking of a run state somewhere if we find that the thread was marked + // as stopped. + const StateType thread_state = thread.GetState(); + if (!StateIsStoppedState(thread_state, false)) { + // An inferior thread has stopped because of a SIGSTOP we have sent it. + // Generally, these are not important stops and we don't want to report + // them as they are just used to stop other threads when one thread (the + // one with the *real* stop reason) hits a breakpoint (watchpoint, + // etc...). However, in the case of an asynchronous Interrupt(), this + // *is* the real stop reason, so we leave the signal intact if this is + // the thread that was chosen as the triggering thread. + if (m_pending_notification_tid != LLDB_INVALID_THREAD_ID) { + if (m_pending_notification_tid == thread.GetID()) + thread.SetStoppedBySignal(SIGSTOP, &info); + else + thread.SetStoppedWithNoReason(); + + SetCurrentThreadID(thread.GetID()); + SignalIfAllThreadsStopped(); + } else { + // We can end up here if stop was initiated by LLGS but by this time a + // thread stop has occurred - maybe initiated by another event. + Status error = ResumeThread(thread, thread.GetState(), 0); + if (error.Fail()) + LLDB_LOG(log, "failed to resume thread {0}: {1}", thread.GetID(), + error); + } + } else { + LLDB_LOG(log, + "pid {0} tid {1}, thread was already marked as a stopped " + "state (state={2}), leaving stop signal as is", + GetID(), thread.GetID(), thread_state); + SignalIfAllThreadsStopped(); + } + + // Done handling. + return; + } +#endif + + // Check if debugger should stop at this signal or just ignore it and resume + // the inferior. + if (m_signals_to_ignore.contains(signo) || signo == SIGCHLD) { + ResumeThread(thread, thread.GetState(), signo); + return; + } + + // This thread is stopped. + LLDB_LOG(log, "received signal {0}", Host::GetSignalAsCString(signo)); + thread.SetStoppedBySignal(signo); + + // Send a stop to the debugger after we get all other threads to stop. + StopRunningThreads(thread.GetID()); +} + +bool NativeProcessAIX::MonitorClone(NativeThreadAIX &parent, + lldb::pid_t child_pid, int event) { + Log *log = GetLog(POSIXLog::Process); + LLDB_LOG(log, "parent_tid={0}, child_pid={1}, event={2}", parent.GetID(), + child_pid, event); + + // WaitForCloneNotification(child_pid); + + switch (event) { +#if 0 + case PTRACE_EVENT_CLONE: { + // PTRACE_EVENT_CLONE can either mean a new thread or a new process. + // Try to grab the new process' PGID to figure out which one it is. + // If PGID is the same as the PID, then it's a new process. Otherwise, + // it's a thread. + auto tgid_ret = getPIDForTID(child_pid); + if (tgid_ret != child_pid) { + // A new thread should have PGID matching our process' PID. + assert(!tgid_ret || tgid_ret.getValue() == GetID()); + + NativeThreadAIX &child_thread = AddThread(child_pid, /*resume*/ true); + ThreadWasCreated(child_thread); + + // Resume the parent. + ResumeThread(parent, parent.GetState(), LLDB_INVALID_SIGNAL_NUMBER); + break; + } + } + LLVM_FALLTHROUGH; + case PTRACE_EVENT_FORK: + case PTRACE_EVENT_VFORK: { + bool is_vfork = event == PTRACE_EVENT_VFORK; + std::unique_ptr child_process{new NativeProcessAIX( + static_cast<::pid_t>(child_pid), m_terminal_fd, m_delegate, m_arch, + m_main_loop, {static_cast<::pid_t>(child_pid)})}; + if (!is_vfork) + child_process->m_software_breakpoints = m_software_breakpoints; + + Extension expected_ext = is_vfork ? Extension::vfork : Extension::fork; + if (bool(m_enabled_extensions & expected_ext)) { + m_delegate.NewSubprocess(this, std::move(child_process)); + // NB: non-vfork clone() is reported as fork + parent.SetStoppedByFork(is_vfork, child_pid); + StopRunningThreads(parent.GetID()); + } else { + child_process->Detach(); + ResumeThread(parent, parent.GetState(), LLDB_INVALID_SIGNAL_NUMBER); + } + break; + } +#endif + default: + llvm_unreachable("unknown clone_info.event"); + } + + return true; +} + +bool NativeProcessAIX::SupportHardwareSingleStepping() const { + return false; +} Status NativeProcessAIX::Resume(const ResumeActionList &resume_actions) { - return Status("unsupported"); + Log *log = GetLog(POSIXLog::Process); + LLDB_LOG(log, "pid {0}", GetID()); + + bool software_single_step = !SupportHardwareSingleStepping(); + + if (software_single_step) { + for (const auto &thread : m_threads) { + assert(thread && "thread list should not contain NULL threads"); + + const ResumeAction *const action = + resume_actions.GetActionForThread(thread->GetID(), true); + if (action == nullptr) + continue; + + if (action->state == eStateStepping) { + Status error = SetupSoftwareSingleStepping( + static_cast(*thread)); + if (error.Fail()) + return error; + } + } + } + + for (const auto &thread : m_threads) { + assert(thread && "thread list should not contain NULL threads"); + + const ResumeAction *const action = + resume_actions.GetActionForThread(thread->GetID(), true); + + if (action == nullptr) { + LLDB_LOG(log, "no action specified for pid {0} tid {1}", GetID(), + thread->GetID()); + continue; + } + + LLDB_LOG(log, "processing resume action state {0} for pid {1} tid {2}", + action->state, GetID(), thread->GetID()); + + switch (action->state) { + case eStateRunning: + case eStateStepping: { + // Run the thread, possibly feeding it the signal. + const int signo = action->signal; + Status error = ResumeThread(static_cast(*thread), + action->state, signo); + if (error.Fail()) + return Status::FromErrorStringWithFormat("NativeProcessAIX::%s: failed to resume thread " + "for pid %" PRIu64 ", tid %" PRIu64 ", error = %s", + __FUNCTION__, GetID(), thread->GetID(), + error.AsCString()); + + break; + } + + case eStateSuspended: + case eStateStopped: + break; + + default: + return Status::FromErrorStringWithFormat("NativeProcessAIX::%s (): unexpected state %s specified " + "for pid %" PRIu64 ", tid %" PRIu64, + __FUNCTION__, StateAsCString(action->state), GetID(), + thread->GetID()); + } + } + + return Status(); } -Status NativeProcessAIX::Halt() { return Status("unsupported"); } +Status NativeProcessAIX::Halt() { + Status error; -Status NativeProcessAIX::Detach() { return Status("unsupported"); } + if (kill(GetID(), SIGSTOP) != 0) + error = Status::FromErrno(); -Status NativeProcessAIX::Signal(int signo) { return Status("unsupported"); } + return error; +} -Status NativeProcessAIX::Interrupt() { return Status("unsupported"); } +Status NativeProcessAIX::Detach() { + Status error; -Status NativeProcessAIX::Kill() { return Status("unsupported"); } + // Tell ptrace to detach from the process. + if (GetID() == LLDB_INVALID_PROCESS_ID) + return error; -Status NativeProcessAIX::ReadMemory(lldb::addr_t addr, void *buf, size_t size, - size_t &bytes_read) { - return Status("unsupported"); + // Cancel out any SIGSTOPs we may have sent while stopping the process. + // Otherwise, the process may stop as soon as we detach from it. + kill(GetID(), SIGCONT); + + for (const auto &thread : m_threads) { + Status e = Detach(thread->GetID()); + if (e.Fail()) + error = + e.Clone(); // Save the error, but still attempt to detach from other threads. + } + + return error; } -Status NativeProcessAIX::WriteMemory(lldb::addr_t addr, const void *buf, - size_t size, size_t &bytes_written) { - return Status("unsupported"); +Status NativeProcessAIX::Signal(int signo) { + Status error; + + Log *log = GetLog(POSIXLog::Process); + LLDB_LOG(log, "sending signal {0} ({1}) to pid {1}", signo, + Host::GetSignalAsCString(signo), GetID()); + + if (kill(GetID(), signo)) + error = Status::FromErrno(); + + return error; +} + +Status NativeProcessAIX::Interrupt() { + // Pick a running thread (or if none, a not-dead stopped thread) as the + // chosen thread that will be the stop-reason thread. + Log *log = GetLog(POSIXLog::Process); + + NativeThreadProtocol *running_thread = nullptr; + NativeThreadProtocol *stopped_thread = nullptr; + + LLDB_LOG(log, "selecting running thread for interrupt target"); + for (const auto &thread : m_threads) { + // If we have a running or stepping thread, we'll call that the target of + // the interrupt. + const auto thread_state = thread->GetState(); + if (thread_state == eStateRunning || thread_state == eStateStepping) { + running_thread = thread.get(); + break; + } else if (!stopped_thread && StateIsStoppedState(thread_state, true)) { + // Remember the first non-dead stopped thread. We'll use that as a + // backup if there are no running threads. + stopped_thread = thread.get(); + } + } + + if (!running_thread && !stopped_thread) { + Status error("found no running/stepping or live stopped threads as target " + "for interrupt"); + LLDB_LOG(log, "skipping due to error: {0}", error); + + return error; + } + + NativeThreadProtocol *deferred_signal_thread = + running_thread ? running_thread : stopped_thread; + + LLDB_LOG(log, "pid {0} {1} tid {2} chosen for interrupt target", GetID(), + running_thread ? "running" : "stopped", + deferred_signal_thread->GetID()); + + StopRunningThreads(deferred_signal_thread->GetID()); + + return Status(); +} + +Status NativeProcessAIX::Kill() { + Log *log = GetLog(POSIXLog::Process); + LLDB_LOG(log, "pid {0}", GetID()); + + Status error; + + switch (m_state) { + case StateType::eStateInvalid: + case StateType::eStateExited: + case StateType::eStateCrashed: + case StateType::eStateDetached: + case StateType::eStateUnloaded: + // Nothing to do - the process is already dead. + LLDB_LOG(log, "ignored for PID {0} due to current state: {1}", GetID(), + m_state); + return error; + + case StateType::eStateConnected: + case StateType::eStateAttaching: + case StateType::eStateLaunching: + case StateType::eStateStopped: + case StateType::eStateRunning: + case StateType::eStateStepping: + case StateType::eStateSuspended: + // We can try to kill a process in these states. + break; + } + + if (kill(GetID(), SIGKILL) != 0) { + error = Status::FromErrno(); + return error; + } + + return error; +} + +Status NativeProcessAIX::GetMemoryRegionInfo(lldb::addr_t load_addr, + MemoryRegionInfo &range_info) { + // FIXME review that the final memory region returned extends to the end of + // the virtual address space, + // with no perms if it is not mapped. + + // Use an approach that reads memory regions from /proc/{pid}/maps. Assume + // proc maps entries are in ascending order. + // FIXME assert if we find differently. + + if (m_supports_mem_region == LazyBool::eLazyBoolNo) { + // We're done. + return Status("unsupported"); + } + + Status error = PopulateMemoryRegionCache(); + if (error.Fail()) { + return error; + } + + lldb::addr_t prev_base_address = 0; + + // FIXME start by finding the last region that is <= target address using + // binary search. Data is sorted. + // There can be a ton of regions on pthreads apps with lots of threads. + for (auto it = m_mem_region_cache.begin(); it != m_mem_region_cache.end(); + ++it) { + MemoryRegionInfo &proc_entry_info = it->first; + // If the target address comes before this entry, indicate distance to next + // region. + if (load_addr < proc_entry_info.GetRange().GetRangeBase()) { + range_info.GetRange().SetRangeBase(load_addr); + range_info.GetRange().SetByteSize( + proc_entry_info.GetRange().GetRangeBase() - load_addr); + range_info.SetReadable(MemoryRegionInfo::OptionalBool::eNo); + range_info.SetWritable(MemoryRegionInfo::OptionalBool::eNo); + range_info.SetExecutable(MemoryRegionInfo::OptionalBool::eNo); + range_info.SetMapped(MemoryRegionInfo::OptionalBool::eNo); + + return error; + } else if (proc_entry_info.GetRange().Contains(load_addr)) { + // The target address is within the memory region we're processing here. + range_info = proc_entry_info; + return error; + } + + // The target memory address comes somewhere after the region we just + // parsed. + } + + // If we made it here, we didn't find an entry that contained the given + // address. Return the load_addr as start and the amount of bytes betwwen + // load address and the end of the memory as size. + range_info.GetRange().SetRangeBase(load_addr); + range_info.GetRange().SetRangeEnd(LLDB_INVALID_ADDRESS); + range_info.SetReadable(MemoryRegionInfo::OptionalBool::eNo); + range_info.SetWritable(MemoryRegionInfo::OptionalBool::eNo); + range_info.SetExecutable(MemoryRegionInfo::OptionalBool::eNo); + range_info.SetMapped(MemoryRegionInfo::OptionalBool::eNo); + return error; +} + +// Parsing the AIX map file /proc/PID/map +// The map file contains an array of prmap structures +// which has all the information like size, startaddress, object name, permissions +bool ParseAIXMapRegions(const char *aix_map, AIXMapCallback const &callback) { + MemoryRegionInfo region; + struct prmap *prmapData = (struct prmap *)aix_map; + struct prmap *entry; + uint32_t perm_flag; + + for(entry = prmapData;!(entry->pr_size == 0 && entry->pr_vaddr == NULL); entry++) { + const char *o_name = aix_map + entry->pr_pathoff; + lldb::addr_t start_address = (lldb::addr_t )entry->pr_vaddr; + lldb::addr_t end_address = start_address + entry->pr_size; + region.GetRange().SetRangeBase(start_address); + region.GetRange().SetRangeEnd(end_address); + region.SetMapped(MemoryRegionInfo::OptionalBool::eYes); + perm_flag = entry->pr_mflags; + + if(perm_flag & MA_READ) + region.SetReadable(MemoryRegionInfo::OptionalBool::eYes); + else + region.SetReadable(MemoryRegionInfo::OptionalBool::eNo); + + if(perm_flag & MA_WRITE) + region.SetWritable(MemoryRegionInfo::OptionalBool::eYes); + else + region.SetWritable(MemoryRegionInfo::OptionalBool::eNo); + + if(perm_flag & MA_EXEC) + region.SetExecutable(MemoryRegionInfo::OptionalBool::eYes); + else + region.SetExecutable(MemoryRegionInfo::OptionalBool::eNo); + + if((perm_flag & MA_SLIBTEXT) || (perm_flag & MA_SLIBDATA)) + region.SetShared(MemoryRegionInfo::OptionalBool::eYes); + else if ((perm_flag & MA_PLIBTEXT) || (perm_flag & MA_PLIBDATA)) + region.SetShared(MemoryRegionInfo::OptionalBool::eNo); + else + region.SetShared(MemoryRegionInfo::OptionalBool::eDontKnow); + + if(o_name) + region.SetName(o_name); + + callback(region); + region.Clear(); + } + + return true; +} + + +Status NativeProcessAIX::PopulateMemoryRegionCache() { + Log *log = GetLog(POSIXLog::Process); + // If our cache is empty, pull the latest. There should always be at least + // one memory region if memory region handling is supported. + if (!m_mem_region_cache.empty()) { + LLDB_LOG(log, "reusing {0} cached memory region entries", + m_mem_region_cache.size()); + return Status(); + } + + Status Result; + + AIXMapCallback callback = [&](llvm::Expected Info) { + if (Info) { + FileSpec file_spec(Info->GetName().GetCString()); + FileSystem::Instance().Resolve(file_spec); + m_mem_region_cache.emplace_back(*Info, file_spec); + return true; + } + + Result = Status::FromError(Info.takeError()); + m_supports_mem_region = LazyBool::eLazyBoolNo; + LLDB_LOG(log, "failed to parse proc maps: {0}", Result); + return false; + }; + + auto BufferOrError = getProcFile(GetID(), "map"); + if (BufferOrError) { + std::unique_ptr MapBuffer = std::move(*BufferOrError); + ParseAIXMapRegions(MapBuffer->getBufferStart(), callback); + } + + if (Result.Fail()) + return Result; + + if (m_mem_region_cache.empty()) { + // No entries after attempting to read them. This shouldn't happen if + // /proc/{pid}/maps is supported. Assume we don't support map entries via + // procfs. + m_supports_mem_region = LazyBool::eLazyBoolNo; + LLDB_LOG(log, + "failed to find any procfs maps entries, assuming no support " + "for memory region metadata retrieval"); + return Status("not supported"); + } + + LLDB_LOG(log, "read {0} memory region entries from /proc/{1}/maps", + m_mem_region_cache.size(), GetID()); + + // We support memory retrieval, remember that. + m_supports_mem_region = LazyBool::eLazyBoolYes; + + return Status(); +} + +void NativeProcessAIX::DoStopIDBumped(uint32_t newBumpId) { + Log *log = GetLog(POSIXLog::Process); + LLDB_LOG(log, "newBumpId={0}", newBumpId); + LLDB_LOG(log, "clearing {0} entries from memory region cache", + m_mem_region_cache.size()); + m_mem_region_cache.clear(); +} + +llvm::Expected +NativeProcessAIX::Syscall(llvm::ArrayRef args) { + PopulateMemoryRegionCache(); + auto region_it = llvm::find_if(m_mem_region_cache, [](const auto &pair) { + return pair.first.GetExecutable() == MemoryRegionInfo::eYes && + pair.first.GetShared() != MemoryRegionInfo::eYes; + }); + if (region_it == m_mem_region_cache.end()) + return llvm::createStringError(llvm::inconvertibleErrorCode(), + "No executable memory region found!"); + + addr_t exe_addr = region_it->first.GetRange().GetRangeBase(); + + NativeThreadAIX &thread = *GetCurrentThread(); + assert(thread.GetState() == eStateStopped); + NativeRegisterContextAIX ®_ctx = thread.GetRegisterContext(); + + NativeRegisterContextAIX::SyscallData syscall_data = + *reg_ctx.GetSyscallData(); + + WritableDataBufferSP registers_sp; + if (llvm::Error Err = reg_ctx.ReadAllRegisterValues(registers_sp).ToError()) + return std::move(Err); + auto restore_regs = llvm::make_scope_exit( + [&] { reg_ctx.WriteAllRegisterValues(registers_sp); }); + + llvm::SmallVector memory(syscall_data.Insn.size()); + size_t bytes_read; + if (llvm::Error Err = + ReadMemory(exe_addr, memory.data(), memory.size(), bytes_read) + .ToError()) { + return std::move(Err); + } + + auto restore_mem = llvm::make_scope_exit( + [&] { WriteMemory(exe_addr, memory.data(), memory.size(), bytes_read); }); + + if (llvm::Error Err = reg_ctx.SetPC(exe_addr).ToError()) + return std::move(Err); + + for (const auto &zip : llvm::zip_first(args, syscall_data.Args)) { + if (llvm::Error Err = + reg_ctx + .WriteRegisterFromUnsigned(std::get<1>(zip), std::get<0>(zip)) + .ToError()) { + return std::move(Err); + } + } + if (llvm::Error Err = WriteMemory(exe_addr, syscall_data.Insn.data(), + syscall_data.Insn.size(), bytes_read) + .ToError()) + return std::move(Err); + + m_mem_region_cache.clear(); + + // With software single stepping the syscall insn buffer must also include a + // trap instruction to stop the process. + int req = SupportHardwareSingleStepping() ? PTRACE_SINGLESTEP : PTRACE_CONT; + if (llvm::Error Err = + PtraceWrapper(req, thread.GetID(), nullptr, nullptr).ToError()) + return std::move(Err); + + //FIXME + int status; + ::pid_t wait_pid = llvm::sys::RetryAfterSignal(-1, ::waitpid, thread.GetID(), + &status, P_ALL/*__WALL*/); + if (wait_pid == -1) { + return llvm::errorCodeToError( + std::error_code(errno, std::generic_category())); + } + assert((unsigned)wait_pid == thread.GetID()); + + uint64_t result = reg_ctx.ReadRegisterAsUnsigned(syscall_data.Result, -ESRCH); + + // Values larger than this are actually negative errno numbers. + uint64_t errno_threshold = + (uint64_t(-1) >> (64 - 8 * m_arch.GetAddressByteSize())) - 0x1000; + if (result > errno_threshold) { + return llvm::errorCodeToError( + std::error_code(-result & 0xfff, std::generic_category())); + } + + return result; +} + +llvm::Expected +NativeProcessAIX::AllocateMemory(size_t size, uint32_t permissions) { + + std::optional mmap_data = + GetCurrentThread()->GetRegisterContext().GetMmapData(); + if (!mmap_data) + return llvm::make_error(); + + unsigned prot = PROT_NONE; + assert((permissions & (ePermissionsReadable | ePermissionsWritable | + ePermissionsExecutable)) == permissions && + "Unknown permission!"); + if (permissions & ePermissionsReadable) + prot |= PROT_READ; + if (permissions & ePermissionsWritable) + prot |= PROT_WRITE; + if (permissions & ePermissionsExecutable) + prot |= PROT_EXEC; + + llvm::Expected Result = + Syscall({mmap_data->SysMmap, 0, size, prot, MAP_ANONYMOUS | MAP_PRIVATE, + uint64_t(-1), 0}); + if (Result) + m_allocated_memory.try_emplace(*Result, size); + return Result; +} + +llvm::Error NativeProcessAIX::DeallocateMemory(lldb::addr_t addr) { + std::optional mmap_data = + GetCurrentThread()->GetRegisterContext().GetMmapData(); + if (!mmap_data) + return llvm::make_error(); + + auto it = m_allocated_memory.find(addr); + if (it == m_allocated_memory.end()) + return llvm::createStringError(llvm::errc::invalid_argument, + "Memory not allocated by the debugger."); + + llvm::Expected Result = + Syscall({mmap_data->SysMunmap, addr, it->second}); + if (!Result) + return Result.takeError(); + + m_allocated_memory.erase(it); + return llvm::Error::success(); +} + +Status NativeProcessAIX::ReadMemoryTags(int32_t type, lldb::addr_t addr, + size_t len, + std::vector &tags) { + llvm::Expected details = + GetCurrentThread()->GetRegisterContext().GetMemoryTaggingDetails(type); + if (!details) + return Status::FromError(details.takeError()); + + // Ignore 0 length read + if (!len) + return Status(); + + // lldb will align the range it requests but it is not required to by + // the protocol so we'll do it again just in case. + // Remove tag bits too. Ptrace calls may work regardless but that + // is not a guarantee. + MemoryTagManager::TagRange range(details->manager->RemoveTagBits(addr), len); + range = details->manager->ExpandToGranule(range); + + // Allocate enough space for all tags to be read + size_t num_tags = range.GetByteSize() / details->manager->GetGranuleSize(); + tags.resize(num_tags * details->manager->GetTagSizeInBytes()); + + struct iovec tags_iovec; + uint8_t *dest = tags.data(); + lldb::addr_t read_addr = range.GetRangeBase(); + + // This call can return partial data so loop until we error or + // get all tags back. + while (num_tags) { + tags_iovec.iov_base = dest; + tags_iovec.iov_len = num_tags; + + Status error = NativeProcessAIX::PtraceWrapper( + details->ptrace_read_req, GetCurrentThreadID(), + reinterpret_cast(read_addr), static_cast(&tags_iovec), + 0, nullptr); + + if (error.Fail()) { + // Discard partial reads + tags.resize(0); + return error; + } + + size_t tags_read = tags_iovec.iov_len; + assert(tags_read && (tags_read <= num_tags)); + + dest += tags_read * details->manager->GetTagSizeInBytes(); + read_addr += details->manager->GetGranuleSize() * tags_read; + num_tags -= tags_read; + } + + return Status(); +} + +Status NativeProcessAIX::WriteMemoryTags(int32_t type, lldb::addr_t addr, + size_t len, + const std::vector &tags) { + llvm::Expected details = + GetCurrentThread()->GetRegisterContext().GetMemoryTaggingDetails(type); + if (!details) + return Status::FromError(details.takeError()); + + // Ignore 0 length write + if (!len) + return Status(); + + // lldb will align the range it requests but it is not required to by + // the protocol so we'll do it again just in case. + // Remove tag bits too. Ptrace calls may work regardless but that + // is not a guarantee. + MemoryTagManager::TagRange range(details->manager->RemoveTagBits(addr), len); + range = details->manager->ExpandToGranule(range); + + // Not checking number of tags here, we may repeat them below + llvm::Expected> unpacked_tags_or_err = + details->manager->UnpackTagsData(tags); + if (!unpacked_tags_or_err) + return Status::FromError(unpacked_tags_or_err.takeError()); + + llvm::Expected> repeated_tags_or_err = + details->manager->RepeatTagsForRange(*unpacked_tags_or_err, range); + if (!repeated_tags_or_err) + return Status::FromError(repeated_tags_or_err.takeError()); + + // Repack them for ptrace to use + llvm::Expected> final_tag_data = + details->manager->PackTags(*repeated_tags_or_err); + if (!final_tag_data) + return Status::FromError(final_tag_data.takeError()); + + struct iovec tags_vec; + uint8_t *src = final_tag_data->data(); + lldb::addr_t write_addr = range.GetRangeBase(); + // unpacked tags size because the number of bytes per tag might not be 1 + size_t num_tags = repeated_tags_or_err->size(); + + // This call can partially write tags, so we loop until we + // error or all tags have been written. + while (num_tags > 0) { + tags_vec.iov_base = src; + tags_vec.iov_len = num_tags; + + Status error = NativeProcessAIX::PtraceWrapper( + details->ptrace_write_req, GetCurrentThreadID(), + reinterpret_cast(write_addr), static_cast(&tags_vec), 0, + nullptr); + + if (error.Fail()) { + // Don't attempt to restore the original values in the case of a partial + // write + return error; + } + + size_t tags_written = tags_vec.iov_len; + assert(tags_written && (tags_written <= num_tags)); + + src += tags_written * details->manager->GetTagSizeInBytes(); + write_addr += details->manager->GetGranuleSize() * tags_written; + num_tags -= tags_written; + } + + return Status(); } size_t NativeProcessAIX::UpdateThreads() { @@ -206,49 +1401,701 @@ size_t NativeProcessAIX::UpdateThreads() { return m_threads.size(); } -Status NativeProcessAIX::GetLoadedModuleFileSpec(const char *module_path, - FileSpec &file_spec) { - return Status("unsupported"); -} - Status NativeProcessAIX::SetBreakpoint(lldb::addr_t addr, uint32_t size, - bool hardware) { + bool hardware) { if (hardware) return SetHardwareBreakpoint(addr, size); - return SetSoftwareBreakpoint(addr, size); + else + return SetSoftwareBreakpoint(addr, size); } Status NativeProcessAIX::RemoveBreakpoint(lldb::addr_t addr, bool hardware) { if (hardware) return RemoveHardwareBreakpoint(addr); - return NativeProcessProtocol::RemoveBreakpoint(addr); + else + return NativeProcessProtocol::RemoveBreakpoint(addr); } -llvm::Error NativeProcessAIX::Detach(lldb::tid_t tid) { - return PtraceWrapper(PT_DETACH, tid).takeError(); +llvm::Expected> +NativeProcessAIX::GetSoftwareBreakpointTrapOpcode(size_t size_hint) { + // The ARM reference recommends the use of 0xe7fddefe and 0xdefe but the + // linux kernel does otherwise. + static const uint8_t g_arm_opcode[] = {0xf0, 0x01, 0xf0, 0xe7}; + static const uint8_t g_thumb_opcode[] = {0x01, 0xde}; + + switch (GetArchitecture().GetMachine()) { + case llvm::Triple::arm: + switch (size_hint) { + case 2: + return llvm::ArrayRef(g_thumb_opcode); + case 4: + return llvm::ArrayRef(g_arm_opcode); + default: + return llvm::createStringError(llvm::inconvertibleErrorCode(), + "Unrecognised trap opcode size hint!"); + } + default: + return NativeProcessProtocol::GetSoftwareBreakpointTrapOpcode(size_hint); + } } -llvm::Expected NativeProcessAIX::PtraceWrapper(int req, lldb::pid_t pid, - void *addr, void *data, - size_t data_size) { - int ret; +Status NativeProcessAIX::ReadMemory(lldb::addr_t addr, void *buf, size_t size, + size_t &bytes_read) { + unsigned char *dst = static_cast(buf); + size_t remainder; + long data; - Log *log = GetLog(POSIXLog::Ptrace); - switch (req) { - case PT_ATTACH: - case PT_DETACH: - ret = ptrace64(req, pid, 0, 0, nullptr); - break; + Log *log = GetLog(POSIXLog::Memory); + LLDB_LOG(log, "addr = {0}, buf = {1}, size = {2}", addr, buf, size); + + for (bytes_read = 0; bytes_read < size; bytes_read += remainder) { + Status error = NativeProcessAIX::PtraceWrapper( + PT_READ_BLOCK, GetCurrentThreadID(), (void *)addr, nullptr, sizeof(data), &data); + if (error.Fail()) + return error; + + remainder = size - bytes_read; + remainder = remainder > k_ptrace_word_size ? k_ptrace_word_size : remainder; + + // Copy the data into our buffer + memcpy(dst, &data, remainder); + + LLDB_LOG(log, "[{0:x}]:{1:x}", addr, data); + addr += k_ptrace_word_size; + dst += k_ptrace_word_size; + } + return Status(); +} + +Status NativeProcessAIX::WriteMemory(lldb::addr_t addr, const void *buf, + size_t size, size_t &bytes_written) { + const unsigned char *src = static_cast(buf); + size_t remainder; + Status error; + + Log *log = GetLog(POSIXLog::Memory); + LLDB_LOG(log, "addr = {0}, buf = {1}, size = {2}", addr, buf, size); + + error = NativeProcessAIX::PtraceWrapper( + PT_WRITE_BLOCK, GetCurrentThreadID(), (void *)addr, nullptr, (int)size, (long *)buf); + if (error.Fail()) + return error; + + bytes_written = size; + return error; +} + +int8_t NativeProcessAIX::GetSignalInfo(WaitStatus wstatus) const { + return wstatus.status; +} + +Status NativeProcessAIX::GetEventMessage(lldb::tid_t tid, + unsigned long *message) { + //FIXME + return PtraceWrapper(PT_CLEAR/*PTRACE_GETEVENTMSG*/, tid, nullptr, message); +} + +Status NativeProcessAIX::Detach(lldb::tid_t tid) { + if (tid == LLDB_INVALID_THREAD_ID) + return Status(); + + return PtraceWrapper(PT_DETACH, tid); +} + +bool NativeProcessAIX::HasThreadNoLock(lldb::tid_t thread_id) { + for (const auto &thread : m_threads) { + assert(thread && "thread list should not contain NULL threads"); + if (thread->GetID() == thread_id) { + // We have this thread. + return true; + } + } + + // We don't have this thread. + return false; +} + +void NativeProcessAIX::StopTrackingThread(NativeThreadAIX &thread) { + Log *const log = GetLog(POSIXLog::Thread); + lldb::tid_t thread_id = thread.GetID(); + LLDB_LOG(log, "tid: {0}", thread_id); + + auto it = llvm::find_if(m_threads, [&](const auto &thread_up) { + return thread_up.get() == &thread; + }); + assert(it != m_threads.end()); + m_threads.erase(it); + + NotifyTracersOfThreadDestroyed(thread_id); + SignalIfAllThreadsStopped(); +} + +void NativeProcessAIX::NotifyTracersProcessDidStop() { +} + +void NativeProcessAIX::NotifyTracersProcessWillResume() { +} + +Status NativeProcessAIX::NotifyTracersOfNewThread(lldb::tid_t tid) { + Log *log = GetLog(POSIXLog::Thread); + Status error; + return error; +} + +Status NativeProcessAIX::NotifyTracersOfThreadDestroyed(lldb::tid_t tid) { + Log *log = GetLog(POSIXLog::Thread); + Status error; + return error; +} + +NativeThreadAIX &NativeProcessAIX::AddThread(lldb::tid_t thread_id, + bool resume) { + Log *log = GetLog(POSIXLog::Thread); + LLDB_LOG(log, "pid {0} adding thread with tid {1}", GetID(), thread_id); + + assert(!HasThreadNoLock(thread_id) && + "attempted to add a thread by id that already exists"); + + // If this is the first thread, save it as the current thread + if (m_threads.empty()) + SetCurrentThreadID(thread_id); + + m_threads.push_back(std::make_unique(*this, thread_id)); + NativeThreadAIX &thread = + static_cast(*m_threads.back()); + + Status tracing_error = NotifyTracersOfNewThread(thread.GetID()); + if (tracing_error.Fail()) { + thread.SetStoppedByProcessorTrace(tracing_error.AsCString()); + StopRunningThreads(thread.GetID()); + } else if (resume) + ResumeThread(thread, eStateRunning, LLDB_INVALID_SIGNAL_NUMBER); + else + thread.SetStoppedBySignal(SIGSTOP); + + return thread; +} + +Status NativeProcessAIX::GetLoadedModuleFileSpec(const char *module_path, + FileSpec &file_spec) { + Status error = PopulateMemoryRegionCache(); + if (error.Fail()) + return error; + + FileSpec module_file_spec(module_path); + FileSystem::Instance().Resolve(module_file_spec); + + file_spec.Clear(); + for (const auto &it : m_mem_region_cache) { + if (it.second.GetFilename() == module_file_spec.GetFilename()) { + file_spec = it.second; + return Status(); + } + } + return Status::FromErrorStringWithFormat("Module file (%s) not found in /proc/%" PRIu64 "/maps file!", + module_file_spec.GetFilename().AsCString(), GetID()); +} + +Status NativeProcessAIX::GetFileLoadAddress(const llvm::StringRef &file_name, + lldb::addr_t &load_addr) { + load_addr = LLDB_INVALID_ADDRESS; + + NativeThreadAIX &thread = *GetCurrentThread(); + NativeRegisterContextAIX ®_ctx = thread.GetRegisterContext(); + + // FIXME: buffer size + struct ld_xinfo info[64]; + if (ptrace64(PT_LDXINFO, reg_ctx.GetThread().GetID(), (long long)&(info[0]), sizeof(info), nullptr) == 0) { + load_addr = (unsigned long)info[0].ldinfo_textorg; + return Status(); + } + return Status("No load address found for specified file."); +} + +NativeThreadAIX *NativeProcessAIX::GetThreadByID(lldb::tid_t tid) { + return static_cast( + NativeProcessProtocol::GetThreadByID(tid)); +} + +NativeThreadAIX *NativeProcessAIX::GetCurrentThread() { + return static_cast( + NativeProcessProtocol::GetCurrentThread()); +} + +Status NativeProcessAIX::ResumeThread(NativeThreadAIX &thread, + lldb::StateType state, int signo) { + Log *const log = GetLog(POSIXLog::Thread); + LLDB_LOG(log, "tid: {0}", thread.GetID()); + + // Before we do the resume below, first check if we have a pending stop + // notification that is currently waiting for all threads to stop. This is + // potentially a buggy situation since we're ostensibly waiting for threads + // to stop before we send out the pending notification, and here we are + // resuming one before we send out the pending stop notification. + if (m_pending_notification_tid != LLDB_INVALID_THREAD_ID) { + LLDB_LOG(log, + "about to resume tid {0} per explicit request but we have a " + "pending stop notification (tid {1}) that is actively " + "waiting for this thread to stop. Valid sequence of events?", + thread.GetID(), m_pending_notification_tid); + } + + // Request a resume. We expect this to be synchronous and the system to + // reflect it is running after this completes. + switch (state) { + case eStateRunning: { + Status resume_result = thread.Resume(signo); + if (resume_result.Success()) + SetState(eStateRunning, true); + return resume_result; + } + case eStateStepping: { + Status step_result = thread.SingleStep(signo); + if (step_result.Success()) + SetState(eStateRunning, true); + return step_result; + } default: - llvm_unreachable("PT_ request not supported yet."); + LLDB_LOG(log, "Unhandled state {0}.", state); + llvm_unreachable("Unhandled state for resume"); + } +} + +//===----------------------------------------------------------------------===// + +void NativeProcessAIX::StopRunningThreads(const lldb::tid_t triggering_tid) { + Log *const log = GetLog(POSIXLog::Thread); + LLDB_LOG(log, "about to process event: (triggering_tid: {0})", + triggering_tid); + + m_pending_notification_tid = triggering_tid; + + // Request a stop for all the thread stops that need to be stopped and are + // not already known to be stopped. + for (const auto &thread : m_threads) { + if (StateIsRunningState(thread->GetState())) + static_cast(thread.get())->RequestStop(); + } + + SignalIfAllThreadsStopped(); + LLDB_LOG(log, "event processing done"); +} + +void NativeProcessAIX::SignalIfAllThreadsStopped() { + if (m_pending_notification_tid == LLDB_INVALID_THREAD_ID) + return; // No pending notification. Nothing to do. + + for (const auto &thread_sp : m_threads) { + if (StateIsRunningState(thread_sp->GetState())) + return; // Some threads are still running. Don't signal yet. + } + + // We have a pending notification and all threads have stopped. + Log *log = GetLog(LLDBLog::Process | LLDBLog::Breakpoints); + + // Clear any temporary breakpoints we used to implement software single + // stepping. + for (const auto &thread_info : m_threads_stepping_with_breakpoint) { + for (auto &&bp_addr : thread_info.second) { + Status error = RemoveBreakpoint(bp_addr); + if (error.Fail()) + LLDB_LOG(log, "pid = {0} remove stepping breakpoint: {1}", + thread_info.first, error); + } + } + m_threads_stepping_with_breakpoint.clear(); + + // Notify the delegate about the stop + SetCurrentThreadID(m_pending_notification_tid); + SetState(StateType::eStateStopped, true); + m_pending_notification_tid = LLDB_INVALID_THREAD_ID; +} + +void NativeProcessAIX::ThreadWasCreated(NativeThreadAIX &thread) { + Log *const log = GetLog(POSIXLog::Thread); + LLDB_LOG(log, "tid: {0}", thread.GetID()); + + if (m_pending_notification_tid != LLDB_INVALID_THREAD_ID && + StateIsRunningState(thread.GetState())) { + // We will need to wait for this new thread to stop as well before firing + // the notification. + thread.RequestStop(); + } +} + +#define DECLARE_REGISTER_INFOS_PPC64LE_STRUCT +#include "Plugins/Process/Utility/RegisterInfos_ppc64le.h" +#undef DECLARE_REGISTER_INFOS_PPC64LE_STRUCT + +static void GetRegister(lldb::pid_t pid, long long addr, void *buf) { + uint64_t val = 0; + ptrace64(PT_READ_GPR, pid, addr, 0, (int *)&val); + *(uint64_t *)buf = llvm::byteswap(val); +} + +static void SetRegister(lldb::pid_t pid, long long addr, void *buf) { + uint64_t val = llvm::byteswap(*(uint64_t *)buf); + ptrace64(PT_WRITE_GPR, pid, addr, 0, (int *)&val); +} + +static void GetFPRegister(lldb::pid_t pid, long long addr, void *buf) { + uint64_t val = 0; + ptrace64(PT_READ_FPR, pid, addr, 0, (int *)&val); + *(uint64_t *)buf = llvm::byteswap(val); +} + +static void GetVMRegister(lldb::tid_t tid, long long addr, void *buf) { + uint64_t val = 0; + ptrace64(PTT_READ_VEC, tid, addr, 0, (int *)&val); + //*(uint64_t *)buf = llvm::byteswap(val); +} + +static void GetVSRegister(lldb::tid_t tid, long long addr, void *buf) { + uint64_t val = 0; + ptrace64(PTT_READ_VSX, tid, addr, 0, (int *)&val); + //*(uint64_t *)buf = llvm::byteswap(val); +} + +// Wrapper for ptrace to catch errors and log calls. Note that ptrace sets +// errno on error because -1 can be a valid result (i.e. for PTRACE_PEEK*) +Status NativeProcessAIX::PtraceWrapper(int req, lldb::pid_t pid, void *addr, + void *data, size_t data_size, + long *result) { + Status error; + long int ret; + + Log *log = GetLog(POSIXLog::Ptrace); + + PtraceDisplayBytes(req, data, data_size); + + errno = 0; + + // for PTT_* + const char procdir[] = "/proc/"; + const char lwpdir[] = "/lwp/"; + std::string process_task_dir = procdir + std::to_string(pid) + lwpdir; + DIR *dirproc = opendir(process_task_dir.c_str()); + + lldb::tid_t tid = 0; + if (dirproc) { + struct dirent *direntry = nullptr; + while ((direntry = readdir(dirproc)) != nullptr) { + if (strcmp(direntry->d_name, ".") == 0 || strcmp(direntry->d_name, "..") == 0) { + continue; + } + tid = atoi(direntry->d_name); + break; + } + closedir(dirproc); + } + + if (req == PTRACE_GETREGS) { + GetRegister(pid, GPR0, &(((GPR *)data)->r0)); + GetRegister(pid, GPR1, &(((GPR *)data)->r1)); + GetRegister(pid, GPR2, &(((GPR *)data)->r2)); + GetRegister(pid, GPR3, &(((GPR *)data)->r3)); + GetRegister(pid, GPR4, &(((GPR *)data)->r4)); + GetRegister(pid, GPR5, &(((GPR *)data)->r5)); + GetRegister(pid, GPR6, &(((GPR *)data)->r6)); + GetRegister(pid, GPR7, &(((GPR *)data)->r7)); + GetRegister(pid, GPR8, &(((GPR *)data)->r8)); + GetRegister(pid, GPR9, &(((GPR *)data)->r9)); + GetRegister(pid, GPR10, &(((GPR *)data)->r10)); + GetRegister(pid, GPR11, &(((GPR *)data)->r11)); + GetRegister(pid, GPR12, &(((GPR *)data)->r12)); + GetRegister(pid, GPR13, &(((GPR *)data)->r13)); + GetRegister(pid, GPR14, &(((GPR *)data)->r14)); + GetRegister(pid, GPR15, &(((GPR *)data)->r15)); + GetRegister(pid, GPR16, &(((GPR *)data)->r16)); + GetRegister(pid, GPR17, &(((GPR *)data)->r17)); + GetRegister(pid, GPR18, &(((GPR *)data)->r18)); + GetRegister(pid, GPR19, &(((GPR *)data)->r19)); + GetRegister(pid, GPR20, &(((GPR *)data)->r20)); + GetRegister(pid, GPR21, &(((GPR *)data)->r21)); + GetRegister(pid, GPR22, &(((GPR *)data)->r22)); + GetRegister(pid, GPR23, &(((GPR *)data)->r23)); + GetRegister(pid, GPR24, &(((GPR *)data)->r24)); + GetRegister(pid, GPR25, &(((GPR *)data)->r25)); + GetRegister(pid, GPR26, &(((GPR *)data)->r26)); + GetRegister(pid, GPR27, &(((GPR *)data)->r27)); + GetRegister(pid, GPR28, &(((GPR *)data)->r28)); + GetRegister(pid, GPR29, &(((GPR *)data)->r29)); + GetRegister(pid, GPR30, &(((GPR *)data)->r30)); + GetRegister(pid, GPR31, &(((GPR *)data)->r31)); + GetRegister(pid, IAR, &(((GPR *)data)->pc)); + GetRegister(pid, MSR, &(((GPR *)data)->msr)); + //FIXME: origr3/softe/trap on AIX? + GetRegister(pid, CTR, &(((GPR *)data)->ctr)); + GetRegister(pid, LR, &(((GPR *)data)->lr)); + GetRegister(pid, XER, &(((GPR *)data)->xer)); + GetRegister(pid, CR, &(((GPR *)data)->cr)); + } else if (req == PTRACE_SETREGS) { + SetRegister(pid, GPR0, &(((GPR *)data)->r0)); + SetRegister(pid, GPR1, &(((GPR *)data)->r1)); + SetRegister(pid, GPR2, &(((GPR *)data)->r2)); + SetRegister(pid, GPR3, &(((GPR *)data)->r3)); + SetRegister(pid, GPR4, &(((GPR *)data)->r4)); + SetRegister(pid, GPR5, &(((GPR *)data)->r5)); + SetRegister(pid, GPR6, &(((GPR *)data)->r6)); + SetRegister(pid, GPR7, &(((GPR *)data)->r7)); + SetRegister(pid, GPR8, &(((GPR *)data)->r8)); + SetRegister(pid, GPR9, &(((GPR *)data)->r9)); + SetRegister(pid, GPR10, &(((GPR *)data)->r10)); + SetRegister(pid, GPR11, &(((GPR *)data)->r11)); + SetRegister(pid, GPR12, &(((GPR *)data)->r12)); + SetRegister(pid, GPR13, &(((GPR *)data)->r13)); + SetRegister(pid, GPR14, &(((GPR *)data)->r14)); + SetRegister(pid, GPR15, &(((GPR *)data)->r15)); + SetRegister(pid, GPR16, &(((GPR *)data)->r16)); + SetRegister(pid, GPR17, &(((GPR *)data)->r17)); + SetRegister(pid, GPR18, &(((GPR *)data)->r18)); + SetRegister(pid, GPR19, &(((GPR *)data)->r19)); + SetRegister(pid, GPR20, &(((GPR *)data)->r20)); + SetRegister(pid, GPR21, &(((GPR *)data)->r21)); + SetRegister(pid, GPR22, &(((GPR *)data)->r22)); + SetRegister(pid, GPR23, &(((GPR *)data)->r23)); + SetRegister(pid, GPR24, &(((GPR *)data)->r24)); + SetRegister(pid, GPR25, &(((GPR *)data)->r25)); + SetRegister(pid, GPR26, &(((GPR *)data)->r26)); + SetRegister(pid, GPR27, &(((GPR *)data)->r27)); + SetRegister(pid, GPR28, &(((GPR *)data)->r28)); + SetRegister(pid, GPR29, &(((GPR *)data)->r29)); + SetRegister(pid, GPR30, &(((GPR *)data)->r30)); + SetRegister(pid, GPR31, &(((GPR *)data)->r31)); + SetRegister(pid, IAR, &(((GPR *)data)->pc)); + SetRegister(pid, MSR, &(((GPR *)data)->msr)); + //FIXME: origr3/softe/trap on AIX? + SetRegister(pid, CTR, &(((GPR *)data)->ctr)); + SetRegister(pid, LR, &(((GPR *)data)->lr)); + SetRegister(pid, XER, &(((GPR *)data)->xer)); + SetRegister(pid, CR, &(((GPR *)data)->cr)); + } else if (req == PTRACE_GETFPREGS) { + GetFPRegister(pid, FPR0, &(((FPR *)data)->f0)); + GetFPRegister(pid, FPR1, &(((FPR *)data)->f1)); + GetFPRegister(pid, FPR2, &(((FPR *)data)->f2)); + GetFPRegister(pid, FPR3, &(((FPR *)data)->f3)); + GetFPRegister(pid, FPR4, &(((FPR *)data)->f4)); + GetFPRegister(pid, FPR5, &(((FPR *)data)->f5)); + GetFPRegister(pid, FPR6, &(((FPR *)data)->f6)); + GetFPRegister(pid, FPR7, &(((FPR *)data)->f7)); + GetFPRegister(pid, FPR8, &(((FPR *)data)->f8)); + GetFPRegister(pid, FPR9, &(((FPR *)data)->f9)); + GetFPRegister(pid, FPR10, &(((FPR *)data)->f10)); + GetFPRegister(pid, FPR11, &(((FPR *)data)->f11)); + GetFPRegister(pid, FPR12, &(((FPR *)data)->f12)); + GetFPRegister(pid, FPR13, &(((FPR *)data)->f13)); + GetFPRegister(pid, FPR14, &(((FPR *)data)->f14)); + GetFPRegister(pid, FPR15, &(((FPR *)data)->f15)); + GetFPRegister(pid, FPR16, &(((FPR *)data)->f16)); + GetFPRegister(pid, FPR17, &(((FPR *)data)->f17)); + GetFPRegister(pid, FPR18, &(((FPR *)data)->f18)); + GetFPRegister(pid, FPR19, &(((FPR *)data)->f19)); + GetFPRegister(pid, FPR20, &(((FPR *)data)->f20)); + GetFPRegister(pid, FPR21, &(((FPR *)data)->f21)); + GetFPRegister(pid, FPR22, &(((FPR *)data)->f22)); + GetFPRegister(pid, FPR23, &(((FPR *)data)->f23)); + GetFPRegister(pid, FPR24, &(((FPR *)data)->f24)); + GetFPRegister(pid, FPR25, &(((FPR *)data)->f25)); + GetFPRegister(pid, FPR26, &(((FPR *)data)->f26)); + GetFPRegister(pid, FPR27, &(((FPR *)data)->f27)); + GetFPRegister(pid, FPR28, &(((FPR *)data)->f28)); + GetFPRegister(pid, FPR29, &(((FPR *)data)->f29)); + GetFPRegister(pid, FPR30, &(((FPR *)data)->f30)); + GetFPRegister(pid, FPR31, &(((FPR *)data)->f31)); + GetFPRegister(pid, FPSCR, &(((FPR *)data)->fpscr)); + } else if (req == PTRACE_GETVRREGS && tid) { + GetVMRegister(tid, VR0, &(((VMX *)data)->vr0[0])); + GetVMRegister(tid, VR1, &(((VMX *)data)->vr1[0])); + GetVMRegister(tid, VR2, &(((VMX *)data)->vr2[0])); + GetVMRegister(tid, VR3, &(((VMX *)data)->vr3[0])); + GetVMRegister(tid, VR4, &(((VMX *)data)->vr4[0])); + GetVMRegister(tid, VR5, &(((VMX *)data)->vr5[0])); + GetVMRegister(tid, VR6, &(((VMX *)data)->vr6[0])); + GetVMRegister(tid, VR7, &(((VMX *)data)->vr7[0])); + GetVMRegister(tid, VR8, &(((VMX *)data)->vr8[0])); + GetVMRegister(tid, VR9, &(((VMX *)data)->vr9[0])); + GetVMRegister(tid, VR10, &(((VMX *)data)->vr10[0])); + GetVMRegister(tid, VR11, &(((VMX *)data)->vr11[0])); + GetVMRegister(tid, VR12, &(((VMX *)data)->vr12[0])); + GetVMRegister(tid, VR13, &(((VMX *)data)->vr13[0])); + GetVMRegister(tid, VR14, &(((VMX *)data)->vr14[0])); + GetVMRegister(tid, VR15, &(((VMX *)data)->vr15[0])); + GetVMRegister(tid, VR16, &(((VMX *)data)->vr16[0])); + GetVMRegister(tid, VR17, &(((VMX *)data)->vr17[0])); + GetVMRegister(tid, VR18, &(((VMX *)data)->vr18[0])); + GetVMRegister(tid, VR19, &(((VMX *)data)->vr19[0])); + GetVMRegister(tid, VR20, &(((VMX *)data)->vr20[0])); + GetVMRegister(tid, VR21, &(((VMX *)data)->vr21[0])); + GetVMRegister(tid, VR22, &(((VMX *)data)->vr22[0])); + GetVMRegister(tid, VR23, &(((VMX *)data)->vr23[0])); + GetVMRegister(tid, VR24, &(((VMX *)data)->vr24[0])); + GetVMRegister(tid, VR25, &(((VMX *)data)->vr25[0])); + GetVMRegister(tid, VR26, &(((VMX *)data)->vr26[0])); + GetVMRegister(tid, VR27, &(((VMX *)data)->vr27[0])); + GetVMRegister(tid, VR28, &(((VMX *)data)->vr28[0])); + GetVMRegister(tid, VR29, &(((VMX *)data)->vr29[0])); + GetVMRegister(tid, VR30, &(((VMX *)data)->vr30[0])); + GetVMRegister(tid, VR31, &(((VMX *)data)->vr31[0])); + GetVMRegister(tid, VSCR, &(((VMX *)data)->vscr[0])); + GetVMRegister(tid, VRSAVE, &(((VMX *)data)->vrsave)); + } else if (req == PTRACE_GETVSRREGS && tid) { + GetVSRegister(tid, VSR0, &(((VSX *)data)->vs0[0])); + GetVSRegister(tid, VSR1, &(((VSX *)data)->vs1[0])); + GetVSRegister(tid, VSR2, &(((VSX *)data)->vs2[0])); + GetVSRegister(tid, VSR3, &(((VSX *)data)->vs3[0])); + GetVSRegister(tid, VSR4, &(((VSX *)data)->vs4[0])); + GetVSRegister(tid, VSR5, &(((VSX *)data)->vs5[0])); + GetVSRegister(tid, VSR6, &(((VSX *)data)->vs6[0])); + GetVSRegister(tid, VSR7, &(((VSX *)data)->vs7[0])); + GetVSRegister(tid, VSR8, &(((VSX *)data)->vs8[0])); + GetVSRegister(tid, VSR9, &(((VSX *)data)->vs9[0])); + GetVSRegister(tid, VSR10, &(((VSX *)data)->vs10[0])); + GetVSRegister(tid, VSR11, &(((VSX *)data)->vs11[0])); + GetVSRegister(tid, VSR12, &(((VSX *)data)->vs12[0])); + GetVSRegister(tid, VSR13, &(((VSX *)data)->vs13[0])); + GetVSRegister(tid, VSR14, &(((VSX *)data)->vs14[0])); + GetVSRegister(tid, VSR15, &(((VSX *)data)->vs15[0])); + GetVSRegister(tid, VSR16, &(((VSX *)data)->vs16[0])); + GetVSRegister(tid, VSR17, &(((VSX *)data)->vs17[0])); + GetVSRegister(tid, VSR18, &(((VSX *)data)->vs18[0])); + GetVSRegister(tid, VSR19, &(((VSX *)data)->vs19[0])); + GetVSRegister(tid, VSR20, &(((VSX *)data)->vs20[0])); + GetVSRegister(tid, VSR21, &(((VSX *)data)->vs21[0])); + GetVSRegister(tid, VSR22, &(((VSX *)data)->vs22[0])); + GetVSRegister(tid, VSR23, &(((VSX *)data)->vs23[0])); + GetVSRegister(tid, VSR24, &(((VSX *)data)->vs24[0])); + GetVSRegister(tid, VSR25, &(((VSX *)data)->vs25[0])); + GetVSRegister(tid, VSR26, &(((VSX *)data)->vs26[0])); + GetVSRegister(tid, VSR27, &(((VSX *)data)->vs27[0])); + GetVSRegister(tid, VSR28, &(((VSX *)data)->vs28[0])); + GetVSRegister(tid, VSR29, &(((VSX *)data)->vs29[0])); + GetVSRegister(tid, VSR30, &(((VSX *)data)->vs30[0])); + GetVSRegister(tid, VSR31, &(((VSX *)data)->vs31[0])); + GetVSRegister(tid, VSR32, &(((VSX *)data)->vs32[0])); + GetVSRegister(tid, VSR33, &(((VSX *)data)->vs33[0])); + GetVSRegister(tid, VSR34, &(((VSX *)data)->vs34[0])); + GetVSRegister(tid, VSR35, &(((VSX *)data)->vs35[0])); + GetVSRegister(tid, VSR36, &(((VSX *)data)->vs36[0])); + GetVSRegister(tid, VSR37, &(((VSX *)data)->vs37[0])); + GetVSRegister(tid, VSR38, &(((VSX *)data)->vs38[0])); + GetVSRegister(tid, VSR39, &(((VSX *)data)->vs39[0])); + GetVSRegister(tid, VSR40, &(((VSX *)data)->vs40[0])); + GetVSRegister(tid, VSR41, &(((VSX *)data)->vs41[0])); + GetVSRegister(tid, VSR42, &(((VSX *)data)->vs42[0])); + GetVSRegister(tid, VSR43, &(((VSX *)data)->vs43[0])); + GetVSRegister(tid, VSR44, &(((VSX *)data)->vs44[0])); + GetVSRegister(tid, VSR45, &(((VSX *)data)->vs45[0])); + GetVSRegister(tid, VSR46, &(((VSX *)data)->vs46[0])); + GetVSRegister(tid, VSR47, &(((VSX *)data)->vs47[0])); + GetVSRegister(tid, VSR48, &(((VSX *)data)->vs48[0])); + GetVSRegister(tid, VSR49, &(((VSX *)data)->vs49[0])); + GetVSRegister(tid, VSR50, &(((VSX *)data)->vs50[0])); + GetVSRegister(tid, VSR51, &(((VSX *)data)->vs51[0])); + GetVSRegister(tid, VSR52, &(((VSX *)data)->vs52[0])); + GetVSRegister(tid, VSR53, &(((VSX *)data)->vs53[0])); + GetVSRegister(tid, VSR54, &(((VSX *)data)->vs54[0])); + GetVSRegister(tid, VSR55, &(((VSX *)data)->vs55[0])); + GetVSRegister(tid, VSR56, &(((VSX *)data)->vs56[0])); + GetVSRegister(tid, VSR57, &(((VSX *)data)->vs57[0])); + GetVSRegister(tid, VSR58, &(((VSX *)data)->vs58[0])); + GetVSRegister(tid, VSR59, &(((VSX *)data)->vs59[0])); + GetVSRegister(tid, VSR60, &(((VSX *)data)->vs60[0])); + GetVSRegister(tid, VSR61, &(((VSX *)data)->vs61[0])); + GetVSRegister(tid, VSR62, &(((VSX *)data)->vs62[0])); + GetVSRegister(tid, VSR63, &(((VSX *)data)->vs63[0])); + } else if (req < PT_COMMAND_MAX) { + if (req == PT_CONTINUE) { +#if 0 + // Use PTT_CONTINUE + const char procdir[] = "/proc/"; + const char lwpdir[] = "/lwp/"; + std::string process_task_dir = procdir + std::to_string(pid) + lwpdir; + DIR *dirproc = opendir(process_task_dir.c_str()); + + struct ptthreads64 pts; + int idx = 0; + lldb::tid_t tid = 0; + if (dirproc) { + struct dirent *direntry = nullptr; + while ((direntry = readdir(dirproc)) != nullptr) { + if (strcmp(direntry->d_name, ".") == 0 || strcmp(direntry->d_name, "..") == 0) { + continue; + } + tid = atoi(direntry->d_name); + pts.th[idx++] = tid; + } + closedir(dirproc); + } + pts.th[idx] = 0; + ret = ptrace64(PTT_CONTINUE, tid, (long long)1, (int)(size_t)data, (int *)&pts); +#else + int buf; + ptrace64(req, pid, 1, (int)(size_t)data, &buf); +#endif + } else if (req == PT_READ_BLOCK) { + ptrace64(req, pid, (long long)addr, (int)data_size, (int *)result); + } else if (req == PT_WRITE_BLOCK) { + ptrace64(req, pid, (long long)addr, (int)data_size, (int *)result); + } else if (req == PT_ATTACH) { + // Block SIGCHLD signal during attach to the process, + // to prevent interruptions. + // The ptrace operation may send SIGCHLD signals in certain cases + // during the attach, which can interfere. + static sigset_t signal_set; + sigemptyset (&signal_set); + sigaddset (&signal_set, SIGCHLD); + if(!pthread_sigmask( SIG_BLOCK, &signal_set, NULL)) + LLDB_LOG(log,"NativeProcessAIX::pthread_sigmask(SIG_BLOCK) Failed"); + + ptrace64(req, pid, 0, 0, nullptr); + + //Unblocking the SIGCHLD after attach work. + if(!pthread_sigmask( SIG_UNBLOCK, &signal_set, NULL )) + LLDB_LOG(log,"NativeProcessAIX::pthread_sigmask(SIG_UNBLOCK) Failed"); + } else if (req == PT_WATCH) { + ptrace64(req, pid, (long long)addr, (int)data_size, nullptr); + } else if (req == PT_DETACH) { + ptrace64(req, pid, 0, 0, nullptr); + } else { + assert(0 && "Not supported yet."); + } + } else { + assert(0 && "Not supported yet."); + } + + if (errno) { + error = Status::FromErrno(); + ret = -1; } LLDB_LOG(log, "ptrace({0}, {1}, {2}, {3}, {4})={5:x}", req, pid, addr, data, data_size, ret); - if (ret == -1) { - LLDB_LOG(log, "ptrace() failed"); - return llvm::errorCodeToError(errnoAsErrorCode()); - } - return ret; + PtraceDisplayBytes(req, data, data_size); + + if (error.Fail()) + LLDB_LOG(log, "ptrace() failed: {0}", error); + + return error; +} + +llvm::Expected NativeProcessAIX::TraceSupported() { + return NativeProcessProtocol::TraceSupported(); +} + +Error NativeProcessAIX::TraceStart(StringRef json_request, StringRef type) { + return NativeProcessProtocol::TraceStart(json_request, type); +} + +Error NativeProcessAIX::TraceStop(const TraceStopRequest &request) { + return NativeProcessProtocol::TraceStop(request); +} + +Expected NativeProcessAIX::TraceGetState(StringRef type) { + return NativeProcessProtocol::TraceGetState(type); +} + +Expected> NativeProcessAIX::TraceGetBinaryData( + const TraceGetBinaryDataRequest &request) { + return NativeProcessProtocol::TraceGetBinaryData(request); } diff --git a/lldb/source/Plugins/Process/AIX/NativeProcessAIX.h b/lldb/source/Plugins/Process/AIX/NativeProcessAIX.h index bc44f2b02af98..c90e21658f71e 100644 --- a/lldb/source/Plugins/Process/AIX/NativeProcessAIX.h +++ b/lldb/source/Plugins/Process/AIX/NativeProcessAIX.h @@ -6,22 +6,31 @@ // //===----------------------------------------------------------------------===// -#ifndef LLDB_SOURCE_PLUGINS_PROCESS_AIX_NATIVEPROCESSAIX_H -#define LLDB_SOURCE_PLUGINS_PROCESS_AIX_NATIVEPROCESSAIX_H +#ifndef liblldb_NativeProcessAIX_H_ +#define liblldb_NativeProcessAIX_H_ + +#include +#include -#include "Plugins/Process/Utility/NativeProcessSoftwareSingleStep.h" #include "lldb/Host/Debug.h" -#include "lldb/Host/common/NativeProcessProtocol.h" -#include "lldb/Host/posix/Support.h" +#include "lldb/Host/HostThread.h" #include "lldb/Target/MemoryRegionInfo.h" #include "lldb/Utility/ArchSpec.h" #include "lldb/Utility/FileSpec.h" #include "lldb/lldb-types.h" -#include "llvm/ADT/SmallPtrSet.h" -#include -#include +#include "llvm/ADT/SmallPtrSet.h" +#include "lldb/Host/linux/Support.h" +#include "lldb/Host/posix/Support.h" + +#include "NativeThreadAIX.h" +#include "lldb/Host/common/NativeProcessProtocol.h" +#include "Plugins/Process/Utility/NativeProcessSoftwareSingleStep.h" -namespace lldb_private::process_aix { +namespace lldb_private { +class Status; +class Scalar; + +namespace process_aix { /// \class NativeProcessAIX /// Manages communication with the inferior (debugee) process. /// @@ -29,20 +38,25 @@ namespace lldb_private::process_aix { /// for debugging. /// /// Changes in the inferior process state are broadcasted. -class NativeProcessAIX : public NativeProcessProtocol { +class NativeProcessAIX : public NativeProcessProtocol, + private NativeProcessSoftwareSingleStep { public: class Manager : public NativeProcessProtocol::Manager { public: - Manager(MainLoop &mainloop); + Manager(MainLoop &mainloop); llvm::Expected> Launch(ProcessLaunchInfo &launch_info, - NativeDelegate &native_delegate) override; + NativeDelegate &native_delegate) override; llvm::Expected> Attach(lldb::pid_t pid, NativeDelegate &native_delegate) override; - void AddProcess(NativeProcessAIX &process) { m_processes.insert(&process); } + Extension GetSupportedExtensions() const override; + + void AddProcess(NativeProcessAIX &process) { + m_processes.insert(&process); + } void RemoveProcess(NativeProcessAIX &process) { m_processes.erase(&process); @@ -56,7 +70,10 @@ class NativeProcessAIX : public NativeProcessProtocol { llvm::SmallPtrSet m_processes; - void SigchldHandler(); + // Threads (events) which haven't been claimed by any process. + llvm::DenseSet<::pid_t> m_unowned_threads; + + void SigchldHandler(); }; // NativeProcessProtocol Interface @@ -77,12 +94,26 @@ class NativeProcessAIX : public NativeProcessProtocol { lldb::addr_t GetSharedLibraryInfoAddress() override; + Status GetMemoryRegionInfo(lldb::addr_t load_addr, + MemoryRegionInfo &range_info) override; + Status ReadMemory(lldb::addr_t addr, void *buf, size_t size, size_t &bytes_read) override; Status WriteMemory(lldb::addr_t addr, const void *buf, size_t size, size_t &bytes_written) override; + llvm::Expected AllocateMemory(size_t size, + uint32_t permissions) override; + + llvm::Error DeallocateMemory(lldb::addr_t addr) override; + + Status ReadMemoryTags(int32_t type, lldb::addr_t addr, size_t len, + std::vector &tags) override; + + Status WriteMemoryTags(int32_t type, lldb::addr_t addr, size_t len, + const std::vector &tags) override; + size_t UpdateThreads() override; const ArchSpec &GetArchitecture() const override { return m_arch; } @@ -92,43 +123,162 @@ class NativeProcessAIX : public NativeProcessProtocol { Status RemoveBreakpoint(lldb::addr_t addr, bool hardware = false) override; + void DoStopIDBumped(uint32_t newBumpId) override; + Status GetLoadedModuleFileSpec(const char *module_path, FileSpec &file_spec) override; + Status GetFileLoadAddress(const llvm::StringRef &file_name, + lldb::addr_t &load_addr) override; + + NativeThreadAIX *GetThreadByID(lldb::tid_t id); + NativeThreadAIX *GetCurrentThread(); + llvm::ErrorOr> GetAuxvData() const override { - return getProcFile(GetID(), "auxv"); + // Not available on this target. + return llvm::errc::not_supported; } - Status GetFileLoadAddress(const llvm::StringRef &file_name, - lldb::addr_t &load_addr) override; + /// Tracing + /// These methods implement the jLLDBTrace packets + /// \{ + llvm::Error TraceStart(llvm::StringRef json_request, + llvm::StringRef type) override; + + llvm::Error TraceStop(const TraceStopRequest &request) override; + + llvm::Expected + TraceGetState(llvm::StringRef type) override; + + llvm::Expected> + TraceGetBinaryData(const TraceGetBinaryDataRequest &request) override; + + llvm::Expected TraceSupported() override; + /// } - static llvm::Expected PtraceWrapper(int req, lldb::pid_t pid, - void *addr = nullptr, - void *data = nullptr, - size_t data_size = 0); + // Interface used by NativeRegisterContext-derived classes. + static Status PtraceWrapper(int req, lldb::pid_t pid, void *addr = nullptr, + void *data = nullptr, size_t data_size = 0, + long *result = nullptr); bool SupportHardwareSingleStepping() const; + /// Writes a siginfo_t structure corresponding to the given thread ID to the + /// memory region pointed to by \p siginfo. + int8_t GetSignalInfo(WaitStatus wstatus) const; + +protected: + llvm::Expected> + GetSoftwareBreakpointTrapOpcode(size_t size_hint) override; + + llvm::Expected Syscall(llvm::ArrayRef args); + private: Manager &m_manager; + /*MainLoop::SignalHandleUP m_sigchld_handle;*/ ArchSpec m_arch; + /*MainLoop& m_main_loop;*/ + + LazyBool m_supports_mem_region = eLazyBoolCalculate; + std::vector> m_mem_region_cache; + + lldb::tid_t m_pending_notification_tid = LLDB_INVALID_THREAD_ID; + + /// Inferior memory (allocated by us) and its size. + llvm::DenseMap m_allocated_memory; // Private Instance Methods NativeProcessAIX(::pid_t pid, int terminal_fd, NativeDelegate &delegate, - const ArchSpec &arch, Manager &manager, - llvm::ArrayRef<::pid_t> tids); - - bool TryHandleWaitStatus(lldb::pid_t pid, WaitStatus status); + const ArchSpec &arch, Manager &manager, + llvm::ArrayRef<::pid_t> tids); // Returns a list of process threads that we have attached to. static llvm::Expected> Attach(::pid_t pid); - llvm::Error Detach(lldb::tid_t tid); + static Status SetDefaultPtraceOpts(const lldb::pid_t); + + bool TryHandleWaitStatus(lldb::pid_t pid, WaitStatus status); + + void MonitorCallback(NativeThreadAIX &thread, WaitStatus status); + + void MonitorSIGTRAP(const WaitStatus status, NativeThreadAIX &thread); + + void MonitorTrace(NativeThreadAIX &thread); + + void MonitorBreakpoint(NativeThreadAIX &thread); + + void MonitorWatchpoint(NativeThreadAIX &thread, uint32_t wp_index); + + void MonitorSignal(const WaitStatus status, NativeThreadAIX &thread); + + bool HasThreadNoLock(lldb::tid_t thread_id); + + void StopTrackingThread(NativeThreadAIX &thread); + + /// Create a new thread. + /// + /// If process tracing is enabled and the thread can't be traced, then the + /// thread is left stopped with a \a eStopReasonProcessorTrace status, and + /// then the process is stopped. + /// + /// \param[in] resume + /// If a tracing error didn't happen, then resume the thread after + /// creation if \b true, or leave it stopped with SIGSTOP if \b false. + NativeThreadAIX &AddThread(lldb::tid_t thread_id, bool resume); + + /// Start tracing a new thread if process tracing is enabled. + /// + /// Trace mechanisms should modify this method to provide automatic tracing + /// for new threads. + Status NotifyTracersOfNewThread(lldb::tid_t tid); + + /// Stop tracing threads upon a destroy event. + /// + /// Trace mechanisms should modify this method to provide automatic trace + /// stopping for threads being destroyed. + Status NotifyTracersOfThreadDestroyed(lldb::tid_t tid); + + void NotifyTracersProcessWillResume() override; + + void NotifyTracersProcessDidStop() override; + /// Writes the raw event message code (vis-a-vis PTRACE_GETEVENTMSG) + /// corresponding to the given thread ID to the memory pointed to by @p + /// message. + Status GetEventMessage(lldb::tid_t tid, unsigned long *message); + + void NotifyThreadDeath(lldb::tid_t tid); + + Status Detach(lldb::tid_t tid); + + // This method is requests a stop on all threads which are still running. It + // sets up a + // deferred delegate notification, which will fire once threads report as + // stopped. The + // triggerring_tid will be set as the current thread (main stop reason). + void StopRunningThreads(lldb::tid_t triggering_tid); + + // Notify the delegate if all threads have stopped. + void SignalIfAllThreadsStopped(); + + // Resume the given thread, optionally passing it the given signal. The type + // of resume + // operation (continue, single-step) depends on the state parameter. + Status ResumeThread(NativeThreadAIX &thread, lldb::StateType state, + int signo); + + void ThreadWasCreated(NativeThreadAIX &thread); void SigchldHandler(); + + Status PopulateMemoryRegionCache(); + + // Handle a clone()-like event. + bool MonitorClone(NativeThreadAIX &parent, lldb::pid_t child_pid, + int event); }; -} // namespace lldb_private::process_aix +} // namespace process_aix +} // namespace lldb_private -#endif // #ifndef LLDB_SOURCE_PLUGINS_PROCESS_AIX_NATIVEPROCESSAIX_H +#endif // #ifndef liblldb_NativeProcessAIX_H_ diff --git a/lldb/source/Plugins/Process/AIX/NativeRegisterContextAIX.cpp b/lldb/source/Plugins/Process/AIX/NativeRegisterContextAIX.cpp index e44cd7b5a30f5..071e55543cc3c 100644 --- a/lldb/source/Plugins/Process/AIX/NativeRegisterContextAIX.cpp +++ b/lldb/source/Plugins/Process/AIX/NativeRegisterContextAIX.cpp @@ -1,4 +1,4 @@ -//===---- NativeRegisterContextAIX.cpp ------------------------------------===// +//===-- NativeRegisterContextAIX.cpp ------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -7,48 +7,151 @@ //===----------------------------------------------------------------------===// #include "NativeRegisterContextAIX.h" + +#include "lldb/Host/common/NativeProcessProtocol.h" +#include "lldb/Host/common/NativeThreadProtocol.h" +#include "lldb/Utility/RegisterValue.h" + #include "Plugins/Process/AIX/NativeProcessAIX.h" +#include "Plugins/Process/POSIX/ProcessPOSIXLog.h" +#include "lldb/Host/aix/Ptrace.h" using namespace lldb_private; using namespace lldb_private::process_aix; lldb::ByteOrder NativeRegisterContextAIX::GetByteOrder() const { - return lldb::eByteOrderInvalid; + return m_thread.GetProcess().GetByteOrder(); } Status NativeRegisterContextAIX::ReadRegisterRaw(uint32_t reg_index, - RegisterValue ®_value) { - return Status("unimplemented"); + RegisterValue ®_value) { + const RegisterInfo *const reg_info = GetRegisterInfoAtIndex(reg_index); + if (!reg_info) + return Status::FromErrorStringWithFormat("register %" PRIu32 " not found", reg_index); + + return DoReadRegisterValue(GetPtraceOffset(reg_index), reg_info->name, + reg_info->byte_size, reg_value); } Status NativeRegisterContextAIX::WriteRegisterRaw(uint32_t reg_index, - const RegisterValue ®_value) { - return Status("unimplemented"); -} + const RegisterValue ®_value) { + uint32_t reg_to_write = reg_index; + RegisterValue value_to_write = reg_value; + + // Check if this is a subregister of a full register. + const RegisterInfo *reg_info = GetRegisterInfoAtIndex(reg_index); + if (reg_info->invalidate_regs && + (reg_info->invalidate_regs[0] != LLDB_INVALID_REGNUM)) { + Status error; + + RegisterValue full_value; + uint32_t full_reg = reg_info->invalidate_regs[0]; + const RegisterInfo *full_reg_info = GetRegisterInfoAtIndex(full_reg); -Status NativeRegisterContextAIX::ReadGPR() { return Status("unimplemented"); } + // Read the full register. + error = ReadRegister(full_reg_info, full_value); + if (error.Fail()) + return error; -Status NativeRegisterContextAIX::WriteGPR() { return Status("unimplemented"); } + lldb::ByteOrder byte_order = GetByteOrder(); + uint8_t dst[RegisterValue::kMaxRegisterByteSize]; -Status NativeRegisterContextAIX::ReadFPR() { return Status("unimplemented"); } + // Get the bytes for the full register. + const uint32_t dest_size = full_value.GetAsMemoryData( + *full_reg_info, dst, sizeof(dst), byte_order, error); + if (error.Success() && dest_size) { + uint8_t src[RegisterValue::kMaxRegisterByteSize]; -Status NativeRegisterContextAIX::WriteFPR() { return Status("unimplemented"); } + // Get the bytes for the source data. + const uint32_t src_size = reg_value.GetAsMemoryData( + *reg_info, src, sizeof(src), byte_order, error); + if (error.Success() && src_size && (src_size < dest_size)) { + // Copy the src bytes to the destination. + memcpy(dst + (reg_info->byte_offset & 0x1), src, src_size); + // Set this full register as the value to write. + value_to_write.SetBytes(dst, full_value.GetByteSize(), byte_order); + value_to_write.SetType(*full_reg_info); + reg_to_write = full_reg; + } + } + } -Status NativeRegisterContextAIX::ReadVMX() { return Status("unimplemented"); } + const RegisterInfo *const register_to_write_info_p = + GetRegisterInfoAtIndex(reg_to_write); + assert(register_to_write_info_p && + "register to write does not have valid RegisterInfo"); + if (!register_to_write_info_p) + return Status::FromErrorStringWithFormat("NativeRegisterContextAIX::%s failed to get RegisterInfo " + "for write register index %" PRIu32, + __FUNCTION__, reg_to_write); -Status NativeRegisterContextAIX::WriteVMX() { return Status("unimplemented"); } + return DoWriteRegisterValue(GetPtraceOffset(reg_index), reg_info->name, + reg_value); +} + +Status NativeRegisterContextAIX::ReadGPR() { + return NativeProcessAIX::PtraceWrapper( + PTRACE_GETREGS, m_thread.GetID(), nullptr, GetGPRBuffer(), GetGPRSize()); +} + +Status NativeRegisterContextAIX::WriteGPR() { + return NativeProcessAIX::PtraceWrapper( + PTRACE_SETREGS, m_thread.GetID(), nullptr, GetGPRBuffer(), GetGPRSize()); +} -Status NativeRegisterContextAIX::ReadVSX() { return Status("unimplemented"); } +Status NativeRegisterContextAIX::ReadFPR() { + return NativeProcessAIX::PtraceWrapper(PTRACE_GETFPREGS, m_thread.GetID(), + nullptr, GetFPRBuffer(), + GetFPRSize()); +} -Status NativeRegisterContextAIX::WriteVSX() { return Status("unimplemented"); } +Status NativeRegisterContextAIX::WriteFPR() { + return NativeProcessAIX::PtraceWrapper(PTRACE_SETFPREGS, m_thread.GetID(), + nullptr, GetFPRBuffer(), + GetFPRSize()); +} Status NativeRegisterContextAIX::ReadRegisterSet(void *buf, size_t buf_size, - unsigned int regset) { - return Status("unimplemented"); + unsigned int regset) { + return NativeProcessAIX::PtraceWrapper(PTRACE_GETREGSET, m_thread.GetID(), + static_cast(®set), buf, + buf_size); } Status NativeRegisterContextAIX::WriteRegisterSet(void *buf, size_t buf_size, - unsigned int regset) { - return Status("unimplemented"); + unsigned int regset) { + return NativeProcessAIX::PtraceWrapper(PTRACE_SETREGSET, m_thread.GetID(), + static_cast(®set), buf, + buf_size); +} + +Status NativeRegisterContextAIX::DoReadRegisterValue(uint32_t offset, + const char *reg_name, + uint32_t size, + RegisterValue &value) { + Log *log = GetLog(POSIXLog::Registers); + + long data; + Status error = NativeProcessAIX::PtraceWrapper( + PTRACE_PEEKUSER, m_thread.GetID(), reinterpret_cast(offset), + nullptr, 0, &data); + + if (error.Success()) + // First cast to an unsigned of the same size to avoid sign extension. + value.SetUInt(static_cast(data), size); + + LLDB_LOG(log, "{0}: {1:x}", reg_name, data); + return error; +} + +Status NativeRegisterContextAIX::DoWriteRegisterValue( + uint32_t offset, const char *reg_name, const RegisterValue &value) { + Log *log = GetLog(POSIXLog::Registers); + + void *buf = reinterpret_cast(value.GetAsUInt64()); + LLDB_LOG(log, "{0}: {1}", reg_name, buf); + + return NativeProcessAIX::PtraceWrapper( + PTRACE_POKEUSER, m_thread.GetID(), reinterpret_cast(offset), buf); } diff --git a/lldb/source/Plugins/Process/AIX/NativeRegisterContextAIX.h b/lldb/source/Plugins/Process/AIX/NativeRegisterContextAIX.h index e78483a7670f6..9c2a326856c0b 100644 --- a/lldb/source/Plugins/Process/AIX/NativeRegisterContextAIX.h +++ b/lldb/source/Plugins/Process/AIX/NativeRegisterContextAIX.h @@ -1,4 +1,4 @@ -//===---- NativeRegisterContextAIX.h ----------------------------*- C++ -*-===// +//===-- NativeRegisterContextAIX.h ----------------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -6,16 +6,79 @@ // //===----------------------------------------------------------------------===// -#ifndef LLDB_SOURCE_PLUGINS_PROCESS_AIX_NATIVEREGISTERCONTEXTAIX_H -#define LLDB_SOURCE_PLUGINS_PROCESS_AIX_NATIVEREGISTERCONTEXTAIX_H +#ifndef lldb_NativeRegisterContextAIX_h +#define lldb_NativeRegisterContextAIX_h #include "Plugins/Process/Utility/NativeRegisterContextRegisterInfo.h" +#include "lldb/Host/common/NativeThreadProtocol.h" +#include "lldb/Target/MemoryTagManager.h" +#include "llvm/Support/Error.h" -namespace lldb_private::process_aix { +namespace lldb_private { +namespace process_aix { + +class NativeThreadAIX; class NativeRegisterContextAIX : public virtual NativeRegisterContextRegisterInfo { +public: + // This function is implemented in the NativeRegisterContextAIX_* subclasses + // to create a new instance of the host specific NativeRegisterContextAIX. + // The implementations can't collide as only one NativeRegisterContextAIX_* + // variant should be compiled into the final executable. + static std::unique_ptr + CreateHostNativeRegisterContextAIX(const ArchSpec &target_arch, + NativeThreadAIX &native_thread); + + // Invalidates cached values in register context data structures + virtual void InvalidateAllRegisters(){} + + struct SyscallData { + /// The syscall instruction. If the architecture uses software + /// single-stepping, the instruction should also be followed by a trap to + /// ensure the process is stopped after the syscall. + llvm::ArrayRef Insn; + + /// Registers used for syscall arguments. The first register is used to + /// store the syscall number. + llvm::ArrayRef Args; + + uint32_t Result; ///< Register containing the syscall result. + }; + /// Return architecture-specific data needed to make inferior syscalls, if + /// they are supported. + virtual std::optional GetSyscallData() { return std::nullopt; } + + struct MmapData { + // Syscall numbers can be found (e.g.) in /usr/include/asm/unistd.h for the + // relevant architecture. + unsigned SysMmap; ///< mmap syscall number. + unsigned SysMunmap; ///< munmap syscall number + }; + /// Return the architecture-specific data needed to make mmap syscalls, if + /// they are supported. + virtual std::optional GetMmapData() { return std::nullopt; } + + struct MemoryTaggingDetails { + /// Object with tag handling utilities. If the function below returns + /// a valid structure, you can assume that this pointer is valid. + std::unique_ptr manager; + int ptrace_read_req; /// ptrace operation number for memory tag read + int ptrace_write_req; /// ptrace operation number for memory tag write + }; + /// Return architecture specific data needed to use memory tags, + /// if they are supported. + virtual llvm::Expected + GetMemoryTaggingDetails(int32_t type) { + return llvm::createStringError( + llvm::inconvertibleErrorCode(), + "Architecture does not support memory tagging"); + } + protected: + // NB: This constructor is here only because gcc<=6.5 requires a virtual base + // class initializer on abstract class (even though it is never used). It can + // be deleted once we move to gcc>=7.0. NativeRegisterContextAIX(NativeThreadProtocol &thread) : NativeRegisterContextRegisterInfo(thread, nullptr) {} @@ -40,23 +103,31 @@ class NativeRegisterContextAIX virtual Status WriteFPR(); - virtual Status ReadVMX(); - - virtual Status WriteVMX(); - - virtual Status ReadVSX(); - - virtual Status WriteVSX(); - virtual void *GetGPRBuffer() = 0; - virtual size_t GetGPRSize() = 0; + virtual size_t GetGPRSize() const { + return GetRegisterInfoInterface().GetGPRSize(); + } virtual void *GetFPRBuffer() = 0; virtual size_t GetFPRSize() = 0; + + virtual uint32_t GetPtraceOffset(uint32_t reg_index) { + return GetRegisterInfoAtIndex(reg_index)->byte_offset; + } + + // The Do*** functions are executed on the privileged thread and can perform + // ptrace + // operations directly. + virtual Status DoReadRegisterValue(uint32_t offset, const char *reg_name, + uint32_t size, RegisterValue &value); + + virtual Status DoWriteRegisterValue(uint32_t offset, const char *reg_name, + const RegisterValue &value); }; -} // namespace lldb_private::process_aix +} // namespace process_aix +} // namespace lldb_private -#endif // #ifndef LLDB_SOURCE_PLUGINS_PROCESS_AIX_NATIVEREGISTERCONTEXTAIX_H +#endif // #ifndef lldb_NativeRegisterContextAIX_h diff --git a/lldb/source/Plugins/Process/AIX/NativeRegisterContextAIX_ppc64.cpp b/lldb/source/Plugins/Process/AIX/NativeRegisterContextAIX_ppc64.cpp new file mode 100644 index 0000000000000..0132b52dec6f2 --- /dev/null +++ b/lldb/source/Plugins/Process/AIX/NativeRegisterContextAIX_ppc64.cpp @@ -0,0 +1,744 @@ +//===-- NativeRegisterContextAIX_ppc64.cpp ----------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// This implementation is related to the OpenPOWER ABI for Power Architecture +// 64-bit ELF V2 ABI + +#if defined(__powerpc64__) + +#include "NativeRegisterContextAIX_ppc64.h" + +#include "lldb/Host/common/NativeProcessProtocol.h" +#include "lldb/Utility/DataBufferHeap.h" +#include "lldb/Utility/Log.h" +#include "lldb/Utility/RegisterValue.h" +#include "lldb/Utility/Status.h" +#include "lldb/Host/aix/Ptrace.h" + +#include "Plugins/Process/AIX/NativeProcessAIX.h" +#include "Plugins/Process/Linux/Procfs.h" +#include "Plugins/Process/POSIX/ProcessPOSIXLog.h" +#include "Plugins/Process/Utility/RegisterInfoPOSIX_ppc64le.h" + +// System includes - They have to be included after framework includes because +// they define some macros which collide with variable names in other modules +#include +#include +#include +#include + +#define REG_CONTEXT_SIZE \ + (GetGPRSize() + GetFPRSize() + sizeof(m_vmx_ppc64le) + sizeof(m_vsx_ppc64le)) +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::process_aix; + +static const uint32_t g_gpr_regnums_ppc64le[] = { + gpr_r0_ppc64le, gpr_r1_ppc64le, gpr_r2_ppc64le, gpr_r3_ppc64le, + gpr_r4_ppc64le, gpr_r5_ppc64le, gpr_r6_ppc64le, gpr_r7_ppc64le, + gpr_r8_ppc64le, gpr_r9_ppc64le, gpr_r10_ppc64le, gpr_r11_ppc64le, + gpr_r12_ppc64le, gpr_r13_ppc64le, gpr_r14_ppc64le, gpr_r15_ppc64le, + gpr_r16_ppc64le, gpr_r17_ppc64le, gpr_r18_ppc64le, gpr_r19_ppc64le, + gpr_r20_ppc64le, gpr_r21_ppc64le, gpr_r22_ppc64le, gpr_r23_ppc64le, + gpr_r24_ppc64le, gpr_r25_ppc64le, gpr_r26_ppc64le, gpr_r27_ppc64le, + gpr_r28_ppc64le, gpr_r29_ppc64le, gpr_r30_ppc64le, gpr_r31_ppc64le, + gpr_pc_ppc64le, gpr_msr_ppc64le, gpr_origr3_ppc64le, gpr_ctr_ppc64le, + gpr_lr_ppc64le, gpr_xer_ppc64le, gpr_cr_ppc64le, gpr_softe_ppc64le, + gpr_trap_ppc64le, + LLDB_INVALID_REGNUM // register sets need to end with this flag +}; + +static const uint32_t g_fpr_regnums_ppc64le[] = { + fpr_f0_ppc64le, fpr_f1_ppc64le, fpr_f2_ppc64le, fpr_f3_ppc64le, + fpr_f4_ppc64le, fpr_f5_ppc64le, fpr_f6_ppc64le, fpr_f7_ppc64le, + fpr_f8_ppc64le, fpr_f9_ppc64le, fpr_f10_ppc64le, fpr_f11_ppc64le, + fpr_f12_ppc64le, fpr_f13_ppc64le, fpr_f14_ppc64le, fpr_f15_ppc64le, + fpr_f16_ppc64le, fpr_f17_ppc64le, fpr_f18_ppc64le, fpr_f19_ppc64le, + fpr_f20_ppc64le, fpr_f21_ppc64le, fpr_f22_ppc64le, fpr_f23_ppc64le, + fpr_f24_ppc64le, fpr_f25_ppc64le, fpr_f26_ppc64le, fpr_f27_ppc64le, + fpr_f28_ppc64le, fpr_f29_ppc64le, fpr_f30_ppc64le, fpr_f31_ppc64le, + fpr_fpscr_ppc64le, + LLDB_INVALID_REGNUM // register sets need to end with this flag +}; + +static const uint32_t g_vmx_regnums_ppc64le[] = { + vmx_vr0_ppc64le, vmx_vr1_ppc64le, vmx_vr2_ppc64le, vmx_vr3_ppc64le, + vmx_vr4_ppc64le, vmx_vr5_ppc64le, vmx_vr6_ppc64le, vmx_vr7_ppc64le, + vmx_vr8_ppc64le, vmx_vr9_ppc64le, vmx_vr10_ppc64le, vmx_vr11_ppc64le, + vmx_vr12_ppc64le, vmx_vr13_ppc64le, vmx_vr14_ppc64le, vmx_vr15_ppc64le, + vmx_vr16_ppc64le, vmx_vr17_ppc64le, vmx_vr18_ppc64le, vmx_vr19_ppc64le, + vmx_vr20_ppc64le, vmx_vr21_ppc64le, vmx_vr22_ppc64le, vmx_vr23_ppc64le, + vmx_vr24_ppc64le, vmx_vr25_ppc64le, vmx_vr26_ppc64le, vmx_vr27_ppc64le, + vmx_vr28_ppc64le, vmx_vr29_ppc64le, vmx_vr30_ppc64le, vmx_vr31_ppc64le, + vmx_vscr_ppc64le, vmx_vrsave_ppc64le, + LLDB_INVALID_REGNUM // register sets need to end with this flag +}; + +static const uint32_t g_vsx_regnums_ppc64le[] = { + vsx_vs0_ppc64le, vsx_vs1_ppc64le, vsx_vs2_ppc64le, vsx_vs3_ppc64le, + vsx_vs4_ppc64le, vsx_vs5_ppc64le, vsx_vs6_ppc64le, vsx_vs7_ppc64le, + vsx_vs8_ppc64le, vsx_vs9_ppc64le, vsx_vs10_ppc64le, vsx_vs11_ppc64le, + vsx_vs12_ppc64le, vsx_vs13_ppc64le, vsx_vs14_ppc64le, vsx_vs15_ppc64le, + vsx_vs16_ppc64le, vsx_vs17_ppc64le, vsx_vs18_ppc64le, vsx_vs19_ppc64le, + vsx_vs20_ppc64le, vsx_vs21_ppc64le, vsx_vs22_ppc64le, vsx_vs23_ppc64le, + vsx_vs24_ppc64le, vsx_vs25_ppc64le, vsx_vs26_ppc64le, vsx_vs27_ppc64le, + vsx_vs28_ppc64le, vsx_vs29_ppc64le, vsx_vs30_ppc64le, vsx_vs31_ppc64le, + vsx_vs32_ppc64le, vsx_vs33_ppc64le, vsx_vs34_ppc64le, vsx_vs35_ppc64le, + vsx_vs36_ppc64le, vsx_vs37_ppc64le, vsx_vs38_ppc64le, vsx_vs39_ppc64le, + vsx_vs40_ppc64le, vsx_vs41_ppc64le, vsx_vs42_ppc64le, vsx_vs43_ppc64le, + vsx_vs44_ppc64le, vsx_vs45_ppc64le, vsx_vs46_ppc64le, vsx_vs47_ppc64le, + vsx_vs48_ppc64le, vsx_vs49_ppc64le, vsx_vs50_ppc64le, vsx_vs51_ppc64le, + vsx_vs52_ppc64le, vsx_vs53_ppc64le, vsx_vs54_ppc64le, vsx_vs55_ppc64le, + vsx_vs56_ppc64le, vsx_vs57_ppc64le, vsx_vs58_ppc64le, vsx_vs59_ppc64le, + vsx_vs60_ppc64le, vsx_vs61_ppc64le, vsx_vs62_ppc64le, vsx_vs63_ppc64le, + LLDB_INVALID_REGNUM // register sets need to end with this flag +}; + +// Number of register sets provided by this context. +static constexpr int k_num_register_sets = 4; + +static const RegisterSet g_reg_sets_ppc64le[k_num_register_sets] = { + {"General Purpose Registers", "gpr", k_num_gpr_registers_ppc64le, + g_gpr_regnums_ppc64le}, + {"Floating Point Registers", "fpr", k_num_fpr_registers_ppc64le, + g_fpr_regnums_ppc64le}, + {"AltiVec/VMX Registers", "vmx", k_num_vmx_registers_ppc64le, + g_vmx_regnums_ppc64le}, + {"VSX Registers", "vsx", k_num_vsx_registers_ppc64le, + g_vsx_regnums_ppc64le}, +}; + +std::unique_ptr +NativeRegisterContextAIX::CreateHostNativeRegisterContextAIX( + const ArchSpec &target_arch, NativeThreadAIX &native_thread) { + switch (target_arch.GetMachine()) { + case llvm::Triple::ppc64: + return std::make_unique(target_arch, + native_thread); + default: + llvm_unreachable("have no register context for architecture"); + } +} + +NativeRegisterContextAIX_ppc64::NativeRegisterContextAIX_ppc64( + const ArchSpec &target_arch, NativeThreadProtocol &native_thread) + : NativeRegisterContextRegisterInfo( + native_thread, new RegisterInfoPOSIX_ppc64le(target_arch)), + NativeRegisterContextAIX(native_thread) { + if (target_arch.GetMachine() != llvm::Triple::ppc64) { + llvm_unreachable("Unhandled target architecture."); + } + + ::memset(&m_gpr_ppc64le, 0, sizeof(m_gpr_ppc64le)); + ::memset(&m_fpr_ppc64le, 0, sizeof(m_fpr_ppc64le)); + ::memset(&m_vmx_ppc64le, 0, sizeof(m_vmx_ppc64le)); + ::memset(&m_vsx_ppc64le, 0, sizeof(m_vsx_ppc64le)); + ::memset(&m_hwp_regs, 0, sizeof(m_hwp_regs)); +} + +uint32_t NativeRegisterContextAIX_ppc64::GetRegisterSetCount() const { + return k_num_register_sets; +} + +const RegisterSet * +NativeRegisterContextAIX_ppc64::GetRegisterSet(uint32_t set_index) const { + if (set_index < k_num_register_sets) + return &g_reg_sets_ppc64le[set_index]; + + return nullptr; +} + +uint32_t NativeRegisterContextAIX_ppc64::GetUserRegisterCount() const { + uint32_t count = 0; + for (uint32_t set_index = 0; set_index < k_num_register_sets; ++set_index) + count += g_reg_sets_ppc64le[set_index].num_registers; + return count; +} + +Status NativeRegisterContextAIX_ppc64::ReadRegister( + const RegisterInfo *reg_info, RegisterValue ®_value) { + Status error; + + if (!reg_info) { + error.FromErrorString("reg_info NULL"); + return error; + } + + const uint32_t reg = reg_info->kinds[lldb::eRegisterKindLLDB]; + + if (IsFPR(reg)) { + error = ReadFPR(); + if (error.Fail()) + return error; + + // Get pointer to m_fpr_ppc64le variable and set the data from it. + uint32_t fpr_offset = CalculateFprOffset(reg_info); + assert(fpr_offset < sizeof m_fpr_ppc64le); + uint8_t *src = (uint8_t *)&m_fpr_ppc64le + fpr_offset; + reg_value.SetFromMemoryData(*reg_info, src, reg_info->byte_size, + eByteOrderLittle, error); + } else if (IsVSX(reg)) { + uint32_t vsx_offset = CalculateVsxOffset(reg_info); + assert(vsx_offset < sizeof(m_vsx_ppc64le)); + + if (vsx_offset < sizeof(m_vsx_ppc64le) / 2) { + error = ReadVSX(); + if (error.Fail()) + return error; + + error = ReadFPR(); + if (error.Fail()) + return error; + + uint64_t value[2]; + uint8_t *dst, *src; + dst = (uint8_t *)&value; + src = (uint8_t *)&m_vsx_ppc64le + vsx_offset / 2; + ::memcpy(dst, src, 8); + dst += 8; + src = (uint8_t *)&m_fpr_ppc64le + vsx_offset / 2; + ::memcpy(dst, src, 8); + reg_value.SetFromMemoryData(*reg_info, &value, reg_info->byte_size, + eByteOrderLittle, error); + } else { + error = ReadVMX(); + if (error.Fail()) + return error; + + // Get pointer to m_vmx_ppc64le variable and set the data from it. + uint32_t vmx_offset = vsx_offset - sizeof(m_vsx_ppc64le) / 2; + uint8_t *src = (uint8_t *)&m_vmx_ppc64le + vmx_offset; + reg_value.SetFromMemoryData(*reg_info, src, reg_info->byte_size, + eByteOrderLittle, error); + } + } else if (IsVMX(reg)) { + error = ReadVMX(); + if (error.Fail()) + return error; + + // Get pointer to m_vmx_ppc64le variable and set the data from it. + uint32_t vmx_offset = CalculateVmxOffset(reg_info); + assert(vmx_offset < sizeof m_vmx_ppc64le); + uint8_t *src = (uint8_t *)&m_vmx_ppc64le + vmx_offset; + reg_value.SetFromMemoryData(*reg_info, src, reg_info->byte_size, + eByteOrderLittle, error); + } else if (IsGPR(reg)) { + error = ReadGPR(); + if (error.Fail()) + return error; + + uint8_t *src = (uint8_t *) &m_gpr_ppc64le + reg_info->byte_offset; + reg_value.SetFromMemoryData(*reg_info, src, reg_info->byte_size, + eByteOrderLittle, error); + } else { + return Status("failed - register wasn't recognized to be a GPR, FPR, VSX " + "or VMX, read strategy unknown"); + } + + return error; +} + +Status NativeRegisterContextAIX_ppc64::WriteRegister( + const RegisterInfo *reg_info, const RegisterValue ®_value) { + Status error; + if (!reg_info) + return Status("reg_info NULL"); + + const uint32_t reg_index = reg_info->kinds[lldb::eRegisterKindLLDB]; + if (reg_index == LLDB_INVALID_REGNUM) + return Status::FromErrorStringWithFormat("no lldb regnum for %s", reg_info && reg_info->name + ? reg_info->name + : ""); + + if (IsGPR(reg_index)) { + error = ReadGPR(); + if (error.Fail()) + return error; + + uint8_t *dst = (uint8_t *)&m_gpr_ppc64le + reg_info->byte_offset; + ::memcpy(dst, reg_value.GetBytes(), reg_value.GetByteSize()); + *(uint64_t *)dst = llvm::byteswap(*(uint64_t *)dst); + + error = WriteGPR(); + if (error.Fail()) + return error; + + return Status(); + } + + if (IsFPR(reg_index)) { + error = ReadFPR(); + if (error.Fail()) + return error; + + // Get pointer to m_fpr_ppc64le variable and set the data to it. + uint32_t fpr_offset = CalculateFprOffset(reg_info); + assert(fpr_offset < GetFPRSize()); + uint8_t *dst = (uint8_t *)&m_fpr_ppc64le + fpr_offset; + ::memcpy(dst, reg_value.GetBytes(), reg_value.GetByteSize()); + + error = WriteFPR(); + if (error.Fail()) + return error; + + return Status(); + } + + if (IsVMX(reg_index)) { + error = ReadVMX(); + if (error.Fail()) + return error; + + // Get pointer to m_vmx_ppc64le variable and set the data to it. + uint32_t vmx_offset = CalculateVmxOffset(reg_info); + assert(vmx_offset < sizeof(m_vmx_ppc64le)); + uint8_t *dst = (uint8_t *)&m_vmx_ppc64le + vmx_offset; + ::memcpy(dst, reg_value.GetBytes(), reg_value.GetByteSize()); + + error = WriteVMX(); + if (error.Fail()) + return error; + + return Status(); + } + + if (IsVSX(reg_index)) { + uint32_t vsx_offset = CalculateVsxOffset(reg_info); + assert(vsx_offset < sizeof(m_vsx_ppc64le)); + + if (vsx_offset < sizeof(m_vsx_ppc64le) / 2) { + error = ReadVSX(); + if (error.Fail()) + return error; + + error = ReadFPR(); + if (error.Fail()) + return error; + + uint64_t value[2]; + ::memcpy(value, reg_value.GetBytes(), 16); + uint8_t *dst, *src; + src = (uint8_t *)value; + dst = (uint8_t *)&m_vsx_ppc64le + vsx_offset / 2; + ::memcpy(dst, src, 8); + src += 8; + dst = (uint8_t *)&m_fpr_ppc64le + vsx_offset / 2; + ::memcpy(dst, src, 8); + + WriteVSX(); + WriteFPR(); + } else { + error = ReadVMX(); + if (error.Fail()) + return error; + + // Get pointer to m_vmx_ppc64le variable and set the data from it. + uint32_t vmx_offset = vsx_offset - sizeof(m_vsx_ppc64le) / 2; + uint8_t *dst = (uint8_t *)&m_vmx_ppc64le + vmx_offset; + ::memcpy(dst, reg_value.GetBytes(), reg_value.GetByteSize()); + WriteVMX(); + } + + return Status(); + } + + return Status("failed - register wasn't recognized to be a GPR, FPR, VSX " + "or VMX, write strategy unknown"); +} + +Status NativeRegisterContextAIX_ppc64::ReadAllRegisterValues( + lldb::WritableDataBufferSP &data_sp) { + Status error; + + data_sp.reset(new DataBufferHeap(REG_CONTEXT_SIZE, 0)); + error = ReadGPR(); + if (error.Fail()) + return error; + + error = ReadFPR(); + if (error.Fail()) + return error; + + error = ReadVMX(); + if (error.Fail()) + return error; + + error = ReadVSX(); + if (error.Fail()) + return error; + + uint8_t *dst = data_sp->GetBytes(); + ::memcpy(dst, &m_gpr_ppc64le, GetGPRSize()); + dst += GetGPRSize(); + ::memcpy(dst, &m_fpr_ppc64le, GetFPRSize()); + dst += GetFPRSize(); + ::memcpy(dst, &m_vmx_ppc64le, sizeof(m_vmx_ppc64le)); + dst += sizeof(m_vmx_ppc64le); + ::memcpy(dst, &m_vsx_ppc64le, sizeof(m_vsx_ppc64le)); + + return error; +} + +Status NativeRegisterContextAIX_ppc64::WriteAllRegisterValues( + const lldb::DataBufferSP &data_sp) { + Status error; + + if (!data_sp) { + error = Status::FromErrorStringWithFormat( + "NativeRegisterContextAIX_ppc64::%s invalid data_sp provided", + __FUNCTION__); + return error; + } + + if (data_sp->GetByteSize() != REG_CONTEXT_SIZE) { + error = Status::FromErrorStringWithFormat( + "NativeRegisterContextAIX_ppc64::%s data_sp contained mismatched " + "data size, expected %" PRIu64 ", actual %" PRIu64, + __FUNCTION__, REG_CONTEXT_SIZE, data_sp->GetByteSize()); + return error; + } + + const uint8_t *src = data_sp->GetBytes(); + if (src == nullptr) { + error = Status::FromErrorStringWithFormat("NativeRegisterContextAIX_ppc64::%s " + "DataBuffer::GetBytes() returned a null " + "pointer", + __FUNCTION__); + return error; + } + + ::memcpy(&m_gpr_ppc64le, src, GetGPRSize()); + error = WriteGPR(); + + if (error.Fail()) + return error; + + src += GetGPRSize(); + ::memcpy(&m_fpr_ppc64le, src, GetFPRSize()); + + error = WriteFPR(); + if (error.Fail()) + return error; + + src += GetFPRSize(); + ::memcpy(&m_vmx_ppc64le, src, sizeof(m_vmx_ppc64le)); + + error = WriteVMX(); + if (error.Fail()) + return error; + + src += sizeof(m_vmx_ppc64le); + ::memcpy(&m_vsx_ppc64le, src, sizeof(m_vsx_ppc64le)); + error = WriteVSX(); + + return error; +} + +bool NativeRegisterContextAIX_ppc64::IsGPR(unsigned reg) const { + return reg <= k_last_gpr_ppc64le; // GPR's come first. +} + +bool NativeRegisterContextAIX_ppc64::IsFPR(unsigned reg) const { + return (k_first_fpr_ppc64le <= reg && reg <= k_last_fpr_ppc64le); +} + +uint32_t NativeRegisterContextAIX_ppc64::CalculateFprOffset( + const RegisterInfo *reg_info) const { + return reg_info->byte_offset - + GetRegisterInfoAtIndex(k_first_fpr_ppc64le)->byte_offset; +} + +uint32_t NativeRegisterContextAIX_ppc64::CalculateVmxOffset( + const RegisterInfo *reg_info) const { + return reg_info->byte_offset - + GetRegisterInfoAtIndex(k_first_vmx_ppc64le)->byte_offset; +} + +uint32_t NativeRegisterContextAIX_ppc64::CalculateVsxOffset( + const RegisterInfo *reg_info) const { + return reg_info->byte_offset - + GetRegisterInfoAtIndex(k_first_vsx_ppc64le)->byte_offset; +} + +Status NativeRegisterContextAIX_ppc64::ReadVMX() { + return NativeProcessAIX::PtraceWrapper(PTRACE_GETVRREGS, m_thread.GetID(), + nullptr, &m_vmx_ppc64le, + sizeof(m_vmx_ppc64le)); +} + +Status NativeRegisterContextAIX_ppc64::WriteVMX() { + //FIXME + int regset = 0/*NT_PPC_VMX*/; + return NativeProcessAIX::PtraceWrapper(PT_CLEAR/*PTRACE_SETVRREGS*/, m_thread.GetID(), + ®set, &m_vmx_ppc64le, + sizeof(m_vmx_ppc64le)); +} + +Status NativeRegisterContextAIX_ppc64::ReadVSX() { + return NativeProcessAIX::PtraceWrapper(PTRACE_GETVSRREGS, m_thread.GetID(), + nullptr, &m_vsx_ppc64le, + sizeof(m_vsx_ppc64le)); +} + +Status NativeRegisterContextAIX_ppc64::WriteVSX() { + //FIXME + int regset = 0/*NT_PPC_VSX*/; + return NativeProcessAIX::PtraceWrapper(PT_CLEAR/*PTRACE_SETVSRREGS*/, m_thread.GetID(), + ®set, &m_vsx_ppc64le, + sizeof(m_vsx_ppc64le)); +} + +bool NativeRegisterContextAIX_ppc64::IsVMX(unsigned reg) { + return (reg >= k_first_vmx_ppc64le) && (reg <= k_last_vmx_ppc64le); +} + +bool NativeRegisterContextAIX_ppc64::IsVSX(unsigned reg) { + return (reg >= k_first_vsx_ppc64le) && (reg <= k_last_vsx_ppc64le); +} + +uint32_t NativeRegisterContextAIX_ppc64::NumSupportedHardwareWatchpoints() { + Log *log = GetLog(POSIXLog::Watchpoints); + + // Read hardware breakpoint and watchpoint information. + Status error = ReadHardwareDebugInfo(); + + if (error.Fail()) + return 0; + + LLDB_LOG(log, "{0}", m_max_hwp_supported); + return m_max_hwp_supported; +} + +uint32_t NativeRegisterContextAIX_ppc64::SetHardwareWatchpoint( + lldb::addr_t addr, size_t size, uint32_t watch_flags) { + Log *log = GetLog(POSIXLog::Watchpoints); + LLDB_LOG(log, "addr: {0:x}, size: {1:x} watch_flags: {2:x}", addr, size, + watch_flags); + + // Read hardware breakpoint and watchpoint information. + Status error = ReadHardwareDebugInfo(); + + if (error.Fail()) + return LLDB_INVALID_INDEX32; + + uint32_t control_value = 0, wp_index = 0; + lldb::addr_t real_addr = addr; + uint32_t rw_mode = 0; + + // Check if we are setting watchpoint other than read/write/access Update + // watchpoint flag to match ppc64le write-read bit configuration. + switch (watch_flags) { + case eWatchpointKindWrite: + //FIXME + //rw_mode = 0/*PPC_BREAKPOINT_TRIGGER_WRITE*/; + watch_flags = 2; + break; + // Watchpoint read not supported + case eWatchpointKindRead: + case (eWatchpointKindRead | eWatchpointKindWrite): + default: + return LLDB_INVALID_INDEX32; + } + + // Check if size has a valid hardware watchpoint length. + if (size != 8) + return LLDB_INVALID_INDEX32; + + // Check 8-byte alignment for hardware watchpoint target address. Below is a + // hack to recalculate address and size in order to make sure we can watch + // non 8-byte aligned addresses as well. + if (addr & 0x07) { + + addr_t begin = llvm::alignDown(addr, 8); + addr_t end = llvm::alignTo(addr + size, 8); + size = llvm::PowerOf2Ceil(end - begin); + + addr = addr & (~0x07); + } + + // Setup control value + control_value = watch_flags << 3; + control_value |= ((1 << size) - 1) << 5; + control_value |= (2 << 1) | 1; + + // Iterate over stored watchpoints and find a free wp_index + wp_index = LLDB_INVALID_INDEX32; + for (uint32_t i = 0; i < m_max_hwp_supported; i++) { + if ((m_hwp_regs[i].control & 1) == 0) { + wp_index = i; // Mark last free slot + } else if (m_hwp_regs[i].address == addr) { + return LLDB_INVALID_INDEX32; // We do not support duplicate watchpoints. + } + } + + if (wp_index == LLDB_INVALID_INDEX32) + return LLDB_INVALID_INDEX32; + + // Update watchpoint in local cache + m_hwp_regs[wp_index].real_addr = real_addr; + m_hwp_regs[wp_index].address = addr; + m_hwp_regs[wp_index].control = control_value; + //m_hwp_regs[wp_index].mode = rw_mode; + + // PTRACE call to set corresponding watchpoint register. + error = WriteHardwareDebugRegs(); + + if (error.Fail()) { + m_hwp_regs[wp_index].address = 0; + m_hwp_regs[wp_index].control &= llvm::maskTrailingZeros(1); + + return LLDB_INVALID_INDEX32; + } + + return wp_index; +} + +bool NativeRegisterContextAIX_ppc64::ClearHardwareWatchpoint( + uint32_t wp_index) { + Log *log = GetLog(POSIXLog::Watchpoints); + LLDB_LOG(log, "wp_index: {0}", wp_index); + + // Read hardware breakpoint and watchpoint information. + Status error = ReadHardwareDebugInfo(); + + if (error.Fail()) + return false; + + if (wp_index >= m_max_hwp_supported) + return false; + + // Create a backup we can revert to in case of failure. + lldb::addr_t tempAddr = m_hwp_regs[wp_index].address; + uint32_t tempControl = m_hwp_regs[wp_index].control; + long *tempSlot = reinterpret_cast(m_hwp_regs[wp_index].slot); + + // Update watchpoint in local cache + m_hwp_regs[wp_index].control &= llvm::maskTrailingZeros(1); + m_hwp_regs[wp_index].address = 0; + m_hwp_regs[wp_index].slot = 0; + m_hwp_regs[wp_index].mode = 0; + + // Ptrace call to update hardware debug registers + //FIXME + error = NativeProcessAIX::PtraceWrapper(PT_CLEAR/*PPC_PTRACE_DELHWDEBUG*/, + m_thread.GetID(), 0, tempSlot); + + if (error.Fail()) { + m_hwp_regs[wp_index].control = tempControl; + m_hwp_regs[wp_index].address = tempAddr; + m_hwp_regs[wp_index].slot = reinterpret_cast(tempSlot); + + return false; + } + + return true; +} + +uint32_t +NativeRegisterContextAIX_ppc64::GetWatchpointSize(uint32_t wp_index) { + Log *log = GetLog(POSIXLog::Watchpoints); + LLDB_LOG(log, "wp_index: {0}", wp_index); + + unsigned control = (m_hwp_regs[wp_index].control >> 5) & 0xff; + if (llvm::isPowerOf2_32(control + 1)) { + return llvm::popcount(control); + } + + return 0; +} + +bool NativeRegisterContextAIX_ppc64::WatchpointIsEnabled( + uint32_t wp_index) { + Log *log = GetLog(POSIXLog::Watchpoints); + LLDB_LOG(log, "wp_index: {0}", wp_index); + + return !!((m_hwp_regs[wp_index].control & 0x1) == 0x1); +} + +Status NativeRegisterContextAIX_ppc64::GetWatchpointHitIndex( + uint32_t &wp_index, lldb::addr_t trap_addr) { + Log *log = GetLog(POSIXLog::Watchpoints); + LLDB_LOG(log, "wp_index: {0}, trap_addr: {1:x}", wp_index, trap_addr); + + uint32_t watch_size; + lldb::addr_t watch_addr; + + for (wp_index = 0; wp_index < m_max_hwp_supported; ++wp_index) { + watch_size = GetWatchpointSize(wp_index); + watch_addr = m_hwp_regs[wp_index].address; + + if (WatchpointIsEnabled(wp_index) && trap_addr >= watch_addr && + trap_addr <= watch_addr + watch_size) { + m_hwp_regs[wp_index].hit_addr = trap_addr; + return Status(); + } + } + + wp_index = LLDB_INVALID_INDEX32; + return Status(); +} + +lldb::addr_t +NativeRegisterContextAIX_ppc64::GetWatchpointAddress(uint32_t wp_index) { + Log *log = GetLog(POSIXLog::Watchpoints); + LLDB_LOG(log, "wp_index: {0}", wp_index); + + if (wp_index >= m_max_hwp_supported) + return LLDB_INVALID_ADDRESS; + + if (WatchpointIsEnabled(wp_index)) + return m_hwp_regs[wp_index].real_addr; + else + return LLDB_INVALID_ADDRESS; +} + +lldb::addr_t +NativeRegisterContextAIX_ppc64::GetWatchpointHitAddress(uint32_t wp_index) { + Log *log = GetLog(POSIXLog::Watchpoints); + LLDB_LOG(log, "wp_index: {0}", wp_index); + + if (wp_index >= m_max_hwp_supported) + return LLDB_INVALID_ADDRESS; + + if (WatchpointIsEnabled(wp_index)) + return m_hwp_regs[wp_index].hit_addr; + + return LLDB_INVALID_ADDRESS; +} + +Status NativeRegisterContextAIX_ppc64::ReadHardwareDebugInfo() { + if (!m_refresh_hwdebug_info) { + return Status(); + } + + m_max_hwp_supported = 1; + m_max_hbp_supported = 0; + m_refresh_hwdebug_info = false; + + return Status(); +} + +Status NativeRegisterContextAIX_ppc64::WriteHardwareDebugRegs() { + Status error; + long ret; + + for (uint32_t i = 0; i < m_max_hwp_supported; i++) { + if ((m_hwp_regs[i].control & 1) == 0) + continue; + + error = NativeProcessAIX::PtraceWrapper(PT_WATCH, m_thread.GetID(), (void *)m_hwp_regs[i].address, nullptr, 8, &ret); + + if (error.Fail()) + return error; + + m_hwp_regs[i].slot = ret; + } + return error; +} + +#endif // defined(__powerpc64__) diff --git a/lldb/source/Plugins/Process/AIX/NativeRegisterContextAIX_ppc64.h b/lldb/source/Plugins/Process/AIX/NativeRegisterContextAIX_ppc64.h new file mode 100644 index 0000000000000..a29f786f2313a --- /dev/null +++ b/lldb/source/Plugins/Process/AIX/NativeRegisterContextAIX_ppc64.h @@ -0,0 +1,138 @@ +//===-- NativeRegisterContextAIX_ppc64.h --------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// This implementation is related to the OpenPOWER ABI for Power Architecture +// 64-bit ELF V2 ABI + +#if defined(__powerpc64__) + +#ifndef lldb_NativeRegisterContextAIX_ppc64_h +#define lldb_NativeRegisterContextAIX_ppc64_h + +#include "Plugins/Process/AIX/NativeRegisterContextAIX.h" +#include "Plugins/Process/Utility/lldb-ppc64le-register-enums.h" + +#define DECLARE_REGISTER_INFOS_PPC64LE_STRUCT +#include "Plugins/Process/Utility/RegisterInfos_ppc64le.h" +#undef DECLARE_REGISTER_INFOS_PPC64LE_STRUCT + +namespace lldb_private { +namespace process_aix { + +class NativeProcessAIX; + +class NativeRegisterContextAIX_ppc64 : public NativeRegisterContextAIX { +public: + NativeRegisterContextAIX_ppc64(const ArchSpec &target_arch, + NativeThreadProtocol &native_thread); + + uint32_t GetRegisterSetCount() const override; + + uint32_t GetUserRegisterCount() const override; + + const RegisterSet *GetRegisterSet(uint32_t set_index) const override; + + Status ReadRegister(const RegisterInfo *reg_info, + RegisterValue ®_value) override; + + Status WriteRegister(const RegisterInfo *reg_info, + const RegisterValue ®_value) override; + + Status ReadAllRegisterValues(lldb::WritableDataBufferSP &data_sp) override; + + Status WriteAllRegisterValues(const lldb::DataBufferSP &data_sp) override; + + // Hardware watchpoint management functions + + uint32_t NumSupportedHardwareWatchpoints() override; + + uint32_t SetHardwareWatchpoint(lldb::addr_t addr, size_t size, + uint32_t watch_flags) override; + + bool ClearHardwareWatchpoint(uint32_t hw_index) override; + + Status GetWatchpointHitIndex(uint32_t &wp_index, + lldb::addr_t trap_addr) override; + + lldb::addr_t GetWatchpointHitAddress(uint32_t wp_index) override; + + lldb::addr_t GetWatchpointAddress(uint32_t wp_index) override; + + uint32_t GetWatchpointSize(uint32_t wp_index); + + bool WatchpointIsEnabled(uint32_t wp_index); + +protected: + bool IsVMX(unsigned reg); + + bool IsVSX(unsigned reg); + + Status ReadVMX(); + + Status WriteVMX(); + + Status ReadVSX(); + + Status WriteVSX(); + + void *GetGPRBuffer() override { return &m_gpr_ppc64le; } + + void *GetFPRBuffer() override { return &m_fpr_ppc64le; } + + size_t GetFPRSize() override { return sizeof(m_fpr_ppc64le); } + +private: + GPR m_gpr_ppc64le; // 64-bit general purpose registers. + FPR m_fpr_ppc64le; // floating-point registers including extended register. + VMX m_vmx_ppc64le; // VMX registers. + VSX m_vsx_ppc64le; // Last lower bytes from first VSX registers. + + bool IsGPR(unsigned reg) const; + + bool IsFPR(unsigned reg) const; + + bool IsVMX(unsigned reg) const; + + bool IsVSX(unsigned reg) const; + + uint32_t CalculateFprOffset(const RegisterInfo *reg_info) const; + + uint32_t CalculateVmxOffset(const RegisterInfo *reg_info) const; + + uint32_t CalculateVsxOffset(const RegisterInfo *reg_info) const; + + Status ReadHardwareDebugInfo(); + + Status WriteHardwareDebugRegs(); + + // Debug register info for hardware watchpoints management. + struct DREG { + lldb::addr_t address; // Breakpoint/watchpoint address value. + lldb::addr_t hit_addr; // Address at which last watchpoint trigger + // exception occurred. + lldb::addr_t real_addr; // Address value that should cause target to stop. + uint32_t control; // Breakpoint/watchpoint control value. + uint32_t refcount; // Serves as enable/disable and reference counter. + long slot; // Saves the value returned from PTRACE_SETHWDEBUG. + int mode; // Defines if watchpoint is read/write/access. + }; + + std::array m_hwp_regs; + + // 16 is just a maximum value, query hardware for actual watchpoint count + uint32_t m_max_hwp_supported = 16; + uint32_t m_max_hbp_supported = 16; + bool m_refresh_hwdebug_info = true; +}; + +} // namespace process_aix +} // namespace lldb_private + +#endif // #ifndef lldb_NativeRegisterContextAIX_ppc64_h + +#endif // defined(__powerpc64__) diff --git a/lldb/source/Plugins/Process/AIX/NativeThreadAIX.cpp b/lldb/source/Plugins/Process/AIX/NativeThreadAIX.cpp index 3bb608168ce30..337caa7c750e8 100644 --- a/lldb/source/Plugins/Process/AIX/NativeThreadAIX.cpp +++ b/lldb/source/Plugins/Process/AIX/NativeThreadAIX.cpp @@ -7,42 +7,508 @@ //===----------------------------------------------------------------------===// #include "NativeThreadAIX.h" + +#include +#include + #include "NativeProcessAIX.h" +#include "NativeRegisterContextAIX.h" + +#include "lldb/Host/HostNativeThread.h" +#include "lldb/Utility/LLDBAssert.h" +#include "lldb/Utility/LLDBLog.h" +#include "lldb/Utility/Log.h" #include "lldb/Utility/State.h" +#include "lldb/lldb-enumerations.h" + +#include "llvm/ADT/SmallString.h" + +#include "Plugins/Process/POSIX/CrashReason.h" +#include +#include +#include +#include +#include + +#if 0 +#include +// Try to define a macro to encapsulate the tgkill syscall +#define tgkill(pid, tid, sig) \ + syscall(__NR_tgkill, static_cast<::pid_t>(pid), static_cast<::pid_t>(tid), \ + sig) +#endif using namespace lldb; using namespace lldb_private; using namespace lldb_private::process_aix; -NativeThreadAIX::NativeThreadAIX(NativeProcessAIX &process, lldb::tid_t tid) - : NativeThreadProtocol(process, tid), m_state(StateType::eStateInvalid) {} +namespace { +void LogThreadStopInfo(Log &log, const ThreadStopInfo &stop_info, + const char *const header) { + switch (stop_info.reason) { + case eStopReasonNone: + log.Printf("%s: %s no stop reason", __FUNCTION__, header); + return; + case eStopReasonTrace: + log.Printf("%s: %s trace, stopping signal 0x%" PRIx32, __FUNCTION__, header, + stop_info.signo); + return; + case eStopReasonBreakpoint: + log.Printf("%s: %s breakpoint, stopping signal 0x%" PRIx32, __FUNCTION__, + header, stop_info.signo); + return; + case eStopReasonWatchpoint: + log.Printf("%s: %s watchpoint, stopping signal 0x%" PRIx32, __FUNCTION__, + header, stop_info.signo); + return; + case eStopReasonSignal: + log.Printf("%s: %s signal 0x%02" PRIx32, __FUNCTION__, header, + stop_info.signo); + return; + case eStopReasonException: + log.Printf("%s: %s exception type 0x%02" PRIx64, __FUNCTION__, header, + stop_info.details.exception.type); + return; + case eStopReasonExec: + log.Printf("%s: %s exec, stopping signal 0x%" PRIx32, __FUNCTION__, header, + stop_info.signo); + return; + case eStopReasonPlanComplete: + log.Printf("%s: %s plan complete", __FUNCTION__, header); + return; + case eStopReasonThreadExiting: + log.Printf("%s: %s thread exiting", __FUNCTION__, header); + return; + case eStopReasonInstrumentation: + log.Printf("%s: %s instrumentation", __FUNCTION__, header); + return; + case eStopReasonProcessorTrace: + log.Printf("%s: %s processor trace", __FUNCTION__, header); + return; + default: + log.Printf("%s: %s invalid stop reason %" PRIu32, __FUNCTION__, header, + static_cast(stop_info.reason)); + } +} +} + +NativeThreadAIX::NativeThreadAIX(NativeProcessAIX &process, + lldb::tid_t tid) + : NativeThreadProtocol(process, tid), m_state(StateType::eStateInvalid), + m_stop_info(), + m_reg_context_up( + NativeRegisterContextAIX::CreateHostNativeRegisterContextAIX( + process.GetArchitecture(), *this)), + m_stop_description() {} -std::string NativeThreadAIX::GetName() { return ""; } +std::string NativeThreadAIX::GetName() { + NativeProcessAIX &process = GetProcess(); + auto BufferOrError = getProcFile(process.GetID(), "psinfo"); + if (!BufferOrError) + return ""; + auto &Buffer = *BufferOrError; + if (Buffer->getBufferSize() < sizeof(psinfo_t)) + return ""; + const psinfo_t *psinfo = + reinterpret_cast(Buffer->getBufferStart()); + return std::string(psinfo->pr_fname); +} lldb::StateType NativeThreadAIX::GetState() { return m_state; } bool NativeThreadAIX::GetStopReason(ThreadStopInfo &stop_info, - std::string &description) { - return false; + std::string &description) { + Log *log = GetLog(LLDBLog::Thread); + + description.clear(); + + switch (m_state) { + case eStateStopped: + case eStateCrashed: + case eStateExited: + case eStateSuspended: + case eStateUnloaded: + if (log) + LogThreadStopInfo(*log, m_stop_info, "m_stop_info in thread:"); + stop_info = m_stop_info; + description = m_stop_description; + if (log) + LogThreadStopInfo(*log, stop_info, "returned stop_info:"); + + return true; + + case eStateInvalid: + case eStateConnected: + case eStateAttaching: + case eStateLaunching: + case eStateRunning: + case eStateStepping: + case eStateDetached: + if (log) { + LLDB_LOGF(log, + "NativeThreadAIX::%s tid %" PRIu64 + " in state %s cannot answer stop reason", + __FUNCTION__, GetID(), StateAsCString(m_state)); + } + return false; + } + llvm_unreachable("unhandled StateType!"); } Status NativeThreadAIX::SetWatchpoint(lldb::addr_t addr, size_t size, - uint32_t watch_flags, bool hardware) { - return Status("Unable to Set hardware watchpoint."); + uint32_t watch_flags, bool hardware) { + if (!hardware) + return Status("not implemented"); + if (m_state == eStateLaunching) + return Status(); + Status error = RemoveWatchpoint(addr); + if (error.Fail()) + return error; + uint32_t wp_index = + m_reg_context_up->SetHardwareWatchpoint(addr, size, watch_flags); + if (wp_index == LLDB_INVALID_INDEX32) + return Status("Setting hardware watchpoint failed."); + m_watchpoint_index_map.insert({addr, wp_index}); + return Status(); } Status NativeThreadAIX::RemoveWatchpoint(lldb::addr_t addr) { + auto wp = m_watchpoint_index_map.find(addr); + if (wp == m_watchpoint_index_map.end()) + return Status(); + uint32_t wp_index = wp->second; + m_watchpoint_index_map.erase(wp); + if (m_reg_context_up->ClearHardwareWatchpoint(wp_index)) + return Status(); return Status("Clearing hardware watchpoint failed."); } -Status NativeThreadAIX::SetHardwareBreakpoint(lldb::addr_t addr, size_t size) { - return Status("Unable to set hardware breakpoint."); +Status NativeThreadAIX::SetHardwareBreakpoint(lldb::addr_t addr, + size_t size) { + if (m_state == eStateLaunching) + return Status(); + + Status error = RemoveHardwareBreakpoint(addr); + if (error.Fail()) + return error; + + uint32_t bp_index = m_reg_context_up->SetHardwareBreakpoint(addr, size); + + if (bp_index == LLDB_INVALID_INDEX32) + return Status("Setting hardware breakpoint failed."); + + m_hw_break_index_map.insert({addr, bp_index}); + return Status(); } Status NativeThreadAIX::RemoveHardwareBreakpoint(lldb::addr_t addr) { + auto bp = m_hw_break_index_map.find(addr); + if (bp == m_hw_break_index_map.end()) + return Status(); + + uint32_t bp_index = bp->second; + if (m_reg_context_up->ClearHardwareBreakpoint(bp_index)) { + m_hw_break_index_map.erase(bp); + return Status(); + } + return Status("Clearing hardware breakpoint failed."); } +Status NativeThreadAIX::Resume(uint32_t signo) { + const StateType new_state = StateType::eStateRunning; + MaybeLogStateChange(new_state); + m_state = new_state; + + m_stop_info.reason = StopReason::eStopReasonNone; + m_stop_description.clear(); + + // If watchpoints have been set, but none on this thread, then this is a new + // thread. So set all existing watchpoints. + if (m_watchpoint_index_map.empty()) { + NativeProcessAIX &process = GetProcess(); + + const auto &watchpoint_map = process.GetWatchpointMap(); + m_reg_context_up->ClearAllHardwareWatchpoints(); + for (const auto &pair : watchpoint_map) { + const auto &wp = pair.second; + SetWatchpoint(wp.m_addr, wp.m_size, wp.m_watch_flags, wp.m_hardware); + } + } + + // Set all active hardware breakpoint on all threads. + if (m_hw_break_index_map.empty()) { + NativeProcessAIX &process = GetProcess(); + + const auto &hw_breakpoint_map = process.GetHardwareBreakpointMap(); + m_reg_context_up->ClearAllHardwareBreakpoints(); + for (const auto &pair : hw_breakpoint_map) { + const auto &bp = pair.second; + SetHardwareBreakpoint(bp.m_addr, bp.m_size); + } + } + + intptr_t data = 0; + + if (signo != LLDB_INVALID_SIGNAL_NUMBER) + data = signo; + + return NativeProcessAIX::PtraceWrapper(PT_CONTINUE, GetID(), nullptr, + reinterpret_cast(data)); +} + +Status NativeThreadAIX::SingleStep(uint32_t signo) { + const StateType new_state = StateType::eStateStepping; + MaybeLogStateChange(new_state); + m_state = new_state; + m_stop_info.reason = StopReason::eStopReasonNone; + + intptr_t data = 0; + if (signo != LLDB_INVALID_SIGNAL_NUMBER) + data = signo; + + // If hardware single-stepping is not supported, we just do a continue. The + // breakpoint on the next instruction has been setup in + // NativeProcessAIX::Resume. + return NativeProcessAIX::PtraceWrapper( + GetProcess().SupportHardwareSingleStepping() ? PT_STEP : PT_CONTINUE, + m_tid, nullptr, reinterpret_cast(data)); +} + +void NativeThreadAIX::SetStoppedBySignal(uint32_t signo, + const siginfo_t *info) { + Log *log = GetLog(LLDBLog::Thread); + LLDB_LOGF(log, "NativeThreadAIX::%s called with signal 0x%02" PRIx32, + __FUNCTION__, signo); + + SetStopped(); + + m_stop_info.reason = StopReason::eStopReasonSignal; + m_stop_info.signo = signo; + + m_stop_description.clear(); + switch (signo) { + case SIGSEGV: + case SIGBUS: + case SIGFPE: + case SIGILL: + break; + } +} + +void NativeThreadAIX::AnnotateSyncTagCheckFault(const siginfo_t *info) { + int32_t allocation_tag_type = 0; + switch (GetProcess().GetArchitecture().GetMachine()) { + default: + return; + } + + auto details = + GetRegisterContext().GetMemoryTaggingDetails(allocation_tag_type); + if (!details) { + llvm::consumeError(details.takeError()); + return; + } + + // We assume that the stop description is currently: + // signal SIGSEGV: sync tag check fault (fault address: ) + // Remove the closing ) + m_stop_description.pop_back(); + + std::stringstream ss; + lldb::addr_t fault_addr = reinterpret_cast(info->si_addr); + std::unique_ptr manager(std::move(details->manager)); + + ss << " logical tag: 0x" << std::hex << manager->GetLogicalTag(fault_addr); + + std::vector allocation_tag_data; + // The fault address may not be granule aligned. ReadMemoryTags will granule + // align any range you give it, potentially making it larger. + // To prevent this set len to 1. This always results in a range that is at + // most 1 granule in size and includes fault_addr. + Status status = GetProcess().ReadMemoryTags(allocation_tag_type, fault_addr, + 1, allocation_tag_data); + + if (status.Success()) { + llvm::Expected> allocation_tag = + manager->UnpackTagsData(allocation_tag_data, 1); + if (allocation_tag) { + ss << " allocation tag: 0x" << std::hex << allocation_tag->front() << ")"; + } else { + llvm::consumeError(allocation_tag.takeError()); + ss << ")"; + } + } else + ss << ")"; + + m_stop_description += ss.str(); +} + +bool NativeThreadAIX::IsStopped(int *signo) { + if (!StateIsStoppedState(m_state, false)) + return false; + + // If we are stopped by a signal, return the signo. + if (signo && m_state == StateType::eStateStopped && + m_stop_info.reason == StopReason::eStopReasonSignal) { + *signo = m_stop_info.signo; + } + + // Regardless, we are stopped. + return true; +} + +void NativeThreadAIX::SetStopped() { + // On every stop, clear any cached register data structures + GetRegisterContext().InvalidateAllRegisters(); + + const StateType new_state = StateType::eStateStopped; + MaybeLogStateChange(new_state); + m_state = new_state; + m_stop_description.clear(); +} + +void NativeThreadAIX::SetStoppedByExec() { + Log *log = GetLog(LLDBLog::Thread); + LLDB_LOGF(log, "NativeThreadAIX::%s()", __FUNCTION__); + + SetStopped(); + + m_stop_info.reason = StopReason::eStopReasonExec; + m_stop_info.signo = SIGSTOP; +} + +void NativeThreadAIX::SetStoppedByBreakpoint() { + SetStopped(); + + m_stop_info.reason = StopReason::eStopReasonBreakpoint; + m_stop_info.signo = SIGTRAP; + m_stop_description.clear(); +} + +void NativeThreadAIX::SetStoppedByWatchpoint(uint32_t wp_index) { + SetStopped(); + + lldbassert(wp_index != LLDB_INVALID_INDEX32 && "wp_index cannot be invalid"); + + std::ostringstream ostr; + ostr << m_reg_context_up->GetWatchpointAddress(wp_index) << " "; + ostr << wp_index; + + /* + * MIPS: Last 3bits of the watchpoint address are masked by the kernel. For + * example: + * 'n' is at 0x120010d00 and 'm' is 0x120010d04. When a watchpoint is set at + * 'm', then + * watch exception is generated even when 'n' is read/written. To handle this + * case, + * find the base address of the load/store instruction and append it in the + * stop-info + * packet. + */ + ostr << " " << m_reg_context_up->GetWatchpointHitAddress(wp_index); + + m_stop_description = ostr.str(); + + m_stop_info.reason = StopReason::eStopReasonWatchpoint; + m_stop_info.signo = SIGTRAP; +} + +bool NativeThreadAIX::IsStoppedAtBreakpoint() { + return GetState() == StateType::eStateStopped && + m_stop_info.reason == StopReason::eStopReasonBreakpoint; +} + +bool NativeThreadAIX::IsStoppedAtWatchpoint() { + return GetState() == StateType::eStateStopped && + m_stop_info.reason == StopReason::eStopReasonWatchpoint; +} + +void NativeThreadAIX::SetStoppedByTrace() { + SetStopped(); + + m_stop_info.reason = StopReason::eStopReasonTrace; + m_stop_info.signo = SIGTRAP; +} + +void NativeThreadAIX::SetStoppedByFork(bool is_vfork, lldb::pid_t child_pid) { + SetStopped(); + + m_stop_info.reason = + is_vfork ? StopReason::eStopReasonVFork : StopReason::eStopReasonFork; + m_stop_info.details.fork.child_pid = child_pid; + m_stop_info.details.fork.child_tid = child_pid; +} + +void NativeThreadAIX::SetStoppedByVForkDone() { + SetStopped(); + + m_stop_info.reason = StopReason::eStopReasonVForkDone; +} + +void NativeThreadAIX::SetStoppedWithNoReason() { + SetStopped(); + + m_stop_info.reason = StopReason::eStopReasonNone; + m_stop_info.signo = 0; +} + +void NativeThreadAIX::SetStoppedByProcessorTrace( + llvm::StringRef description) { + SetStopped(); + + m_stop_info.reason = StopReason::eStopReasonProcessorTrace; + m_stop_info.signo = 0; + m_stop_description = description.str(); +} + +void NativeThreadAIX::SetExited() { + const StateType new_state = StateType::eStateExited; + MaybeLogStateChange(new_state); + m_state = new_state; + + m_stop_info.reason = StopReason::eStopReasonThreadExiting; +} + +Status NativeThreadAIX::RequestStop() { + Log *log = GetLog(LLDBLog::Thread); + + NativeProcessAIX &process = GetProcess(); + + lldb::pid_t pid = process.GetID(); + lldb::tid_t tid = GetID(); + + LLDB_LOGF(log, + "NativeThreadAIX::%s requesting thread stop(pid: %" PRIu64 + ", tid: %" PRIu64 ")", + __FUNCTION__, pid, tid); + + Status err; + errno = 0; + if (::kill(pid, SIGSTOP) != 0) { + err = Status::FromErrno(); + LLDB_LOGF(log, + "NativeThreadAIX::%s kill(%" PRIu64 ", SIGSTOP) failed: %s", + __FUNCTION__, pid, err.AsCString()); + } + return err; +} + +void NativeThreadAIX::MaybeLogStateChange(lldb::StateType new_state) { + Log *log = GetLog(LLDBLog::Thread); + // If we're not logging, we're done. + if (!log) + return; + + // If this is a state change to the same state, we're done. + lldb::StateType old_state = m_state; + if (new_state == old_state) + return; + + LLDB_LOG(log, "pid={0}, tid={1}: changing from state {2} to {3}", + m_process.GetID(), GetID(), old_state, new_state); +} + NativeProcessAIX &NativeThreadAIX::GetProcess() { return static_cast(m_process); } @@ -53,6 +519,13 @@ const NativeProcessAIX &NativeThreadAIX::GetProcess() const { llvm::Expected> NativeThreadAIX::GetSiginfo() const { - return llvm::createStringError(llvm::inconvertibleErrorCode(), - "Not implemented"); + auto siginfo_buf = + llvm::WritableMemoryBuffer::getNewUninitMemBuffer(sizeof(siginfo_t)); +#if 0 + Status error = + GetProcess().GetSignalInfo(GetID(), siginfo_buf->getBufferStart()); + if (!error.Success()) + return error.ToError(); +#endif + return std::move(siginfo_buf); } diff --git a/lldb/source/Plugins/Process/AIX/NativeThreadAIX.h b/lldb/source/Plugins/Process/AIX/NativeThreadAIX.h index e32d3db2c5fa2..706a7ce69da8e 100644 --- a/lldb/source/Plugins/Process/AIX/NativeThreadAIX.h +++ b/lldb/source/Plugins/Process/AIX/NativeThreadAIX.h @@ -6,12 +6,22 @@ // //===----------------------------------------------------------------------===// -#ifndef LLDB_SOURCE_PLUGINS_PROCESS_AIX_NATIVETHREADAIX_H_ -#define LLDB_SOURCE_PLUGINS_PROCESS_AIX_NATIVETHREADAIX_H_ +#ifndef liblldb_NativeThreadAIX_H_ +#define liblldb_NativeThreadAIX_H_ +#include "Plugins/Process/AIX/NativeRegisterContextAIX.h" #include "lldb/Host/common/NativeThreadProtocol.h" +#include "lldb/lldb-private-forward.h" -namespace lldb_private::process_aix { +#include "llvm/ADT/StringRef.h" + +#include +#include +#include +#include + +namespace lldb_private { +namespace process_aix { class NativeProcessAIX; @@ -29,6 +39,10 @@ class NativeThreadAIX : public NativeThreadProtocol { bool GetStopReason(ThreadStopInfo &stop_info, std::string &description) override; + NativeRegisterContextAIX &GetRegisterContext() override { + return *m_reg_context_up; + } + Status SetWatchpoint(lldb::addr_t addr, size_t size, uint32_t watch_flags, bool hardware) override; @@ -46,8 +60,67 @@ class NativeThreadAIX : public NativeThreadProtocol { GetSiginfo() const override; private: + // Interface for friend classes + + /// Resumes the thread. If \p signo is anything but + /// LLDB_INVALID_SIGNAL_NUMBER, deliver that signal to the thread. + Status Resume(uint32_t signo); + + /// Single steps the thread. If \p signo is anything but + /// LLDB_INVALID_SIGNAL_NUMBER, deliver that signal to the thread. + Status SingleStep(uint32_t signo); + + void SetStoppedBySignal(uint32_t signo, const siginfo_t *info = nullptr); + + /// Return true if the thread is stopped. + /// If stopped by a signal, indicate the signo in the signo argument. + /// Otherwise, return LLDB_INVALID_SIGNAL_NUMBER. + bool IsStopped(int *signo); + + void SetStoppedByExec(); + + void SetStoppedByBreakpoint(); + + void SetStoppedByWatchpoint(uint32_t wp_index); + + bool IsStoppedAtBreakpoint(); + + bool IsStoppedAtWatchpoint(); + + void SetStoppedByTrace(); + + void SetStoppedByFork(bool is_vfork, lldb::pid_t child_pid); + + void SetStoppedByVForkDone(); + + void SetStoppedWithNoReason(); + + void SetStoppedByProcessorTrace(llvm::StringRef description); + + void SetExited(); + + Status RequestStop(); + + // Private interface + void MaybeLogStateChange(lldb::StateType new_state); + + void SetStopped(); + + /// Extend m_stop_description with logical and allocation tag values. + /// If there is an error along the way just add the information we were able + /// to get. + void AnnotateSyncTagCheckFault(const siginfo_t *info); + + // Member Variables lldb::StateType m_state; + ThreadStopInfo m_stop_info; + std::unique_ptr m_reg_context_up; + std::string m_stop_description; + using WatchpointIndexMap = std::map; + WatchpointIndexMap m_watchpoint_index_map; + WatchpointIndexMap m_hw_break_index_map; }; -} // namespace lldb_private::process_aix +} // namespace process_aix +} // namespace lldb_private -#endif // #ifndef LLDB_SOURCE_PLUGINS_PROCESS_AIX_NATIVETHREADAIX_H_ +#endif // #ifndef liblldb_NativeThreadAIX_H_ diff --git a/lldb/source/Plugins/Process/CMakeLists.txt b/lldb/source/Plugins/Process/CMakeLists.txt index bd9b1b86dbf13..50fef3cb4617d 100644 --- a/lldb/source/Plugins/Process/CMakeLists.txt +++ b/lldb/source/Plugins/Process/CMakeLists.txt @@ -29,3 +29,4 @@ add_subdirectory(elf-core) add_subdirectory(mach-core) add_subdirectory(minidump) add_subdirectory(FreeBSDKernel) +add_subdirectory(aix-core) diff --git a/lldb/source/Plugins/Process/Utility/InferiorCallPOSIX.cpp b/lldb/source/Plugins/Process/Utility/InferiorCallPOSIX.cpp index 5226b9bbe7c78..8e74cce097894 100644 --- a/lldb/source/Plugins/Process/Utility/InferiorCallPOSIX.cpp +++ b/lldb/source/Plugins/Process/Utility/InferiorCallPOSIX.cpp @@ -46,8 +46,34 @@ bool lldb_private::InferiorCallMmap(Process *process, addr_t &allocated_addr, function_options.include_inlines = false; SymbolContextList sc_list; +#if !defined(_AIX) process->GetTarget().GetImages().FindFunctions( ConstString("mmap"), eFunctionNameTypeFull, function_options, sc_list); +#else + process->GetTarget().GetImages().FindFunctions( + ConstString("mmap64"), eFunctionNameTypeFull, function_options, sc_list); + SymbolContextList toc_list; + process->GetTarget().GetImages().FindSymbolsWithNameAndType( + ConstString("TOC"), lldb::eSymbolTypeAny, toc_list); + + AddressRange toc_range; + if (sc_list.GetSize() > 0) { + SymbolContext sc; + if (sc_list.GetContextAtIndex(0, sc)) { + for (int i = 0; i < toc_list.GetSize(); ++i) { + SymbolContext tocSC; + if (toc_list.GetContextAtIndex(i, tocSC)) { + if (tocSC.module_sp == sc.module_sp) { + if (tocSC.GetAddressRange(eSymbolContextSymbol, 0, false, + toc_range)) { + break; + } + } + } + } + } + } +#endif const uint32_t count = sc_list.GetSize(); if (count > 0) { SymbolContext sc; @@ -92,8 +118,15 @@ bool lldb_private::InferiorCallMmap(Process *process, addr_t &allocated_addr, MmapArgList args = process->GetTarget().GetPlatform()->GetMmapArgumentList( arch, addr, length, prot_arg, flags, fd, offset); +#if defined(_AIX) + lldb::ThreadPlanSP call_plan_sp( + new ThreadPlanCallFunction(*thread, mmap_addr, + toc_range.GetBaseAddress(), + void_ptr_type, args, options)); +#else lldb::ThreadPlanSP call_plan_sp(new ThreadPlanCallFunction( *thread, mmap_addr, void_ptr_type, args, options)); +#endif if (call_plan_sp) { DiagnosticManager diagnostics; diff --git a/lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_ppc64le.cpp b/lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_ppc64le.cpp index 159fd2856443c..d9b41d595147f 100644 --- a/lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_ppc64le.cpp +++ b/lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_ppc64le.cpp @@ -23,6 +23,8 @@ static const lldb_private::RegisterInfo * GetRegisterInfoPtr(const lldb_private::ArchSpec &target_arch) { switch (target_arch.GetMachine()) { + //HH + case llvm::Triple::ppc64: case llvm::Triple::ppc64le: return g_register_infos_ppc64le; default: @@ -34,6 +36,8 @@ GetRegisterInfoPtr(const lldb_private::ArchSpec &target_arch) { static uint32_t GetRegisterInfoCount(const lldb_private::ArchSpec &target_arch) { switch (target_arch.GetMachine()) { + //HitchHike + case llvm::Triple::ppc64: case llvm::Triple::ppc64le: return static_cast(sizeof(g_register_infos_ppc64le) / sizeof(g_register_infos_ppc64le[0])); diff --git a/lldb/source/Plugins/Process/aix-core/AIXCore.cpp b/lldb/source/Plugins/Process/aix-core/AIXCore.cpp new file mode 100644 index 0000000000000..bc496b5af273f --- /dev/null +++ b/lldb/source/Plugins/Process/aix-core/AIXCore.cpp @@ -0,0 +1,116 @@ +//===-- AIXCore.cpp ------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include + +#include "lldb/Core/Section.h" +#include "lldb/Utility/DataExtractor.h" +#include "lldb/Utility/Stream.h" +#include "lldb/Utility/DataBufferHeap.h" +#include "lldb/Utility/LLDBLog.h" +#include "lldb/Utility/Log.h" +#include "lldb/Utility/State.h" + +#include "AIXCore.h" + +using namespace AIXCORE; +using namespace lldb; +using namespace lldb_private; + +AIXCore64Header::AIXCore64Header() { memset(this, 0, sizeof(AIXCore64Header)); } + + +bool AIXCore64Header::ParseRegisterContext(lldb_private::DataExtractor &data, + lldb::offset_t *offset) { + // The data is arranged in this order in this coredump file + // so we have to fetch in this exact order. But need to change + // the context structure order according to Infos_ppc64 + for(int i = 0; i < 32; i++) + Fault.context.gpr[i] = data.GetU64(offset); + Fault.context.msr = data.GetU64(offset); + Fault.context.pc = data.GetU64(offset); + Fault.context.lr = data.GetU64(offset); + Fault.context.ctr = data.GetU64(offset); + Fault.context.cr = data.GetU32(offset); + Fault.context.xer = data.GetU32(offset); + Fault.context.fpscr = data.GetU32(offset); + Fault.context.fpscrx = data.GetU32(offset); + Fault.context.except[0] = data.GetU64(offset); + for(int i = 0; i < 32; i++) + Fault.context.fpr[i] = data.GetU64(offset); + Fault.context.fpeu = data.GetU8(offset); + Fault.context.fpinfo = data.GetU8(offset); + Fault.context.fpscr24_31 = data.GetU8(offset); + Fault.context.pad[0] = data.GetU8(offset); + Fault.context.excp_type = data.GetU32(offset); + + return true; +} +bool AIXCore64Header::ParseThreadContext(lldb_private::DataExtractor &data, + lldb::offset_t *offset) { + + lldb::offset_t offset_to_regctx = *offset; + offset_to_regctx += sizeof(thrdentry64); + Fault.thread.ti_tid = data.GetU64(offset); + Fault.thread.ti_pid = data.GetU32(offset); + int ret = ParseRegisterContext(data, &offset_to_regctx); + return true; +} + +bool AIXCore64Header::ParseUserData(lldb_private::DataExtractor &data, + lldb::offset_t *offset) { + User.process.pi_pid = data.GetU32(offset); + User.process.pi_ppid = data.GetU32(offset); + User.process.pi_sid = data.GetU32(offset); + User.process.pi_pgrp = data.GetU32(offset); + User.process.pi_uid = data.GetU32(offset); + User.process.pi_suid = data.GetU32(offset); + + *offset += 76; + + ByteOrder byteorder = data.GetByteOrder(); + size_t size = 33; + data.ExtractBytes(*offset, size, byteorder, User.process.pi_comm); + offset += size; + + return true; +} + +bool AIXCore64Header::ParseCoreHeader(lldb_private::DataExtractor &data, + lldb::offset_t *offset) { + + SignalNum = data.GetU8(offset); + Flag = data.GetU8(offset); + Entries = data.GetU16(offset); + Version = data.GetU32(offset); + FDInfo = data.GetU64(offset); + + LoaderOffset = data.GetU64(offset); + LoaderSize = data.GetU64(offset); + NumberOfThreads = data.GetU32(offset); + Reserved0 = data.GetU32(offset); + ThreadContextOffset = data.GetU64(offset); + NumSegRegion = data.GetU64(offset); + SegRegionOffset = data.GetU64(offset); + StackOffset = data.GetU64(offset); + StackBaseAddr = data.GetU64(offset); + StackSize = data.GetU64(offset); + DataRegionOffset = data.GetU64(offset); + DataBaseAddr = data.GetU64(offset); + DataSize = data.GetU64(offset); + + *offset += 104; + lldb::offset_t offset_to_user = (*offset + sizeof(ThreadContext64)); + int ret = 0; + ret = ParseThreadContext(data, offset); + ret = ParseUserData(data, &offset_to_user); + + return ret; + +} + diff --git a/lldb/source/Plugins/Process/aix-core/AIXCore.h b/lldb/source/Plugins/Process/aix-core/AIXCore.h new file mode 100644 index 0000000000000..3d78d5e92c7ab --- /dev/null +++ b/lldb/source/Plugins/Process/aix-core/AIXCore.h @@ -0,0 +1,125 @@ +//===-- AIXCore.h ----------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// Notes about AIX Process core dumps: +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SOURCE_PLUGINS_PROCESS_AIX_CORE_AIXCORE_H +#define LLDB_SOURCE_PLUGINS_PROCESS_AIX_CORE_AIXCORE_H + +#include "llvm/ADT/StringRef.h" +#include +#include +#include + +#include +#include + +namespace AIXCORE { + +struct RegContext { + // The data is arranged in order as filled by AIXCore.cpp in this coredump file + // so we have to fetch in that exact order, refer there. + // But need to change + // the context structure in order according to Infos_ppc64 + uint64_t gpr[32]; /* 64-bit gprs */ + unsigned long pc; /* msr */ + unsigned long msr; /* iar */ + unsigned long origr3; /* iar */ + unsigned long ctr; /* CTR */ + unsigned long lr; /* LR */ + unsigned long xer; /* XER */ + unsigned long cr; /* CR */ + unsigned long softe; /* CR */ + unsigned long trap; /* CR */ + unsigned int fpscr; /* floating pt status reg */ + unsigned int fpscrx; /* software ext to fpscr */ + unsigned long except[1]; /* exception address */ + double fpr[32]; /* floating pt regs */ + char fpeu; /* floating pt ever used */ + char fpinfo; /* floating pt info */ + char fpscr24_31; /* bits 24-31 of 64-bit FPSCR */ + char pad[1]; + int excp_type; /* exception type */ +}; + + struct ThreadContext64 { + struct thrdentry64 thread; + struct RegContext context; + }; + + struct UserData { + + struct procentry64 process; + unsigned long long reserved[16]; + }; + + struct AIXCore64Header { + + int8_t SignalNum; /* signal number (cause of error) */ + int8_t Flag; /* flag to describe core dump type */ + uint16_t Entries; /* number of core dump modules */ + uint32_t Version; /* core file format number */ + uint64_t FDInfo; /* offset to fd region in file */ + + uint64_t LoaderOffset; /* offset to loader region in file */ + uint64_t LoaderSize; /* size of loader region */ + + uint32_t NumberOfThreads ; /* number of elements in thread table */ + uint32_t Reserved0; /* Padding */ + uint64_t ThreadContextOffset; /* offset to thread context table */ + + uint64_t NumSegRegion; /* n of elements in segregion */ + uint64_t SegRegionOffset; /* offset to start of segregion table */ + + uint64_t StackOffset; /* offset of user stack in file */ + uint64_t StackBaseAddr; /* base address of user stack region */ + uint64_t StackSize; /* size of user stack region */ + + uint64_t DataRegionOffset; /* offset to user data region */ + uint64_t DataBaseAddr; /* base address of user data region */ + uint64_t DataSize; /* size of user data region */ + uint64_t SDataBase; /* base address of sdata region */ + uint64_t SDataSize; /* size of sdata region */ + + uint64_t NumVMRegions; /* number of anonymously mapped areas */ + uint64_t VMOffset; /* offset to start of vm_infox table */ + + int32_t ProcessorImplementation; /* processor implementation */ + uint32_t NumElementsCTX; /* n of elements in extended ctx table*/ + uint64_t CPRSOffset; /* Checkpoint/Restart offset */ + uint64_t ExtendedContextOffset; /* extended context offset */ + uint64_t OffsetUserKey; /* Offset to user-key exception data */ + uint64_t OffsetLoaderTLS; /* offset to the loader region in file + when a process uses TLS data */ + uint64_t TLSLoaderSize; /* size of the above loader region */ + uint64_t ExtendedProcEntry; /* Extended procentry64 information */ + uint64_t Reserved[2]; + + struct ThreadContext64 Fault; + + struct UserData User; + + AIXCore64Header(); + + bool ParseCoreHeader(lldb_private::DataExtractor &data, + lldb::offset_t *offset); + bool ParseThreadContext(lldb_private::DataExtractor &data, + lldb::offset_t *offset); + bool ParseUserData(lldb_private::DataExtractor &data, + lldb::offset_t *offset); + bool ParseRegisterContext(lldb_private::DataExtractor &data, + lldb::offset_t *offset); + bool ParseLoaderData(lldb_private::DataExtractor &data, + lldb::offset_t *offset); + + }; + + +} + +#endif // LLDB_SOURCE_PLUGINS_PROCESS_AIX_CORE_AIXCORE_H diff --git a/lldb/source/Plugins/Process/aix-core/CMakeLists.txt b/lldb/source/Plugins/Process/aix-core/CMakeLists.txt new file mode 100644 index 0000000000000..347717a362491 --- /dev/null +++ b/lldb/source/Plugins/Process/aix-core/CMakeLists.txt @@ -0,0 +1,16 @@ +add_definitions("-D_ALL_SOURCE") + +add_lldb_library(lldbPluginProcessAIXCore PLUGIN + ProcessAIXCore.cpp + AIXCore.cpp + ThreadAIXCore.cpp + RegisterContextCoreAIX_ppc64.cpp + + LINK_LIBS + lldbCore + lldbTarget + lldbPluginProcessUtility + LINK_COMPONENTS + BinaryFormat + Support + ) diff --git a/lldb/source/Plugins/Process/aix-core/ProcessAIXCore.cpp b/lldb/source/Plugins/Process/aix-core/ProcessAIXCore.cpp new file mode 100644 index 0000000000000..bb2db66e2980e --- /dev/null +++ b/lldb/source/Plugins/Process/aix-core/ProcessAIXCore.cpp @@ -0,0 +1,328 @@ +//===-- ProcessAIXCore.cpp ------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include + +#include +#include + +#include "lldb/Core/Module.h" +#include "lldb/Core/ModuleSpec.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Core/Section.h" +#include "lldb/Target/ABI.h" +#include "lldb/Target/DynamicLoader.h" +#include "lldb/Target/MemoryRegionInfo.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/UnixSignals.h" +#include "lldb/Utility/DataBufferHeap.h" +#include "lldb/Utility/LLDBLog.h" +#include "lldb/Utility/Log.h" +#include "lldb/Utility/State.h" + +#include "llvm/Support/Threading.h" +#include "Plugins/DynamicLoader/AIX-DYLD/DynamicLoaderAIXDYLD.h" + +#include "ProcessAIXCore.h" +#include "AIXCore.h" +#include "ThreadAIXCore.h" + +using namespace lldb_private; + +LLDB_PLUGIN_DEFINE(ProcessAIXCore) + +llvm::StringRef ProcessAIXCore::GetPluginDescriptionStatic() { + return "AIX core dump plug-in."; +} + +void ProcessAIXCore::Initialize() { + static llvm::once_flag g_once_flag; + + llvm::call_once(g_once_flag, []() { + PluginManager::RegisterPlugin(GetPluginNameStatic(), + GetPluginDescriptionStatic(), CreateInstance); + }); +} + +void ProcessAIXCore::Terminate() { + PluginManager::UnregisterPlugin(ProcessAIXCore::CreateInstance); +} + +lldb::ProcessSP ProcessAIXCore::CreateInstance(lldb::TargetSP target_sp, + lldb::ListenerSP listener_sp, + const FileSpec *crash_file, + bool can_connect) { + lldb::ProcessSP process_sp; + if (crash_file && !can_connect) { + const size_t header_size = sizeof(AIXCORE::AIXCore64Header); + + auto data_sp = FileSystem::Instance().CreateDataBuffer( + crash_file->GetPath(), header_size, 0); + + if (data_sp && data_sp->GetByteSize() == header_size) { + AIXCORE::AIXCore64Header aixcore_header; + DataExtractor data(data_sp, lldb::eByteOrderBig, 4); + lldb::offset_t data_offset = 0; + if(aixcore_header.ParseCoreHeader(data, &data_offset)) { + process_sp = std::make_shared(target_sp, listener_sp, + *crash_file); + } + } + + } + return process_sp; +} + +// ProcessAIXCore constructor +ProcessAIXCore::ProcessAIXCore(lldb::TargetSP target_sp, + lldb::ListenerSP listener_sp, + const FileSpec &core_file) + : PostMortemProcess(target_sp, listener_sp, core_file) {} + +// Destructor +ProcessAIXCore::~ProcessAIXCore() { + Clear(); + // We need to call finalize on the process before destroying ourselves to + // make sure all of the broadcaster cleanup goes as planned. If we destruct + // this class, then Process::~Process() might have problems trying to fully + // destroy the broadcaster. + Finalize(true /* destructing */); +} + +lldb::addr_t ProcessAIXCore::AddAddressRanges(AIXCORE::AIXCore64Header header) { + const lldb::addr_t addr = header.StackBaseAddr; + FileRange file_range(header.StackOffset, header.StackSize); + VMRangeToFileOffset::Entry range_entry(addr, header.StackSize, file_range); + + if (header.StackSize > 0) { + VMRangeToFileOffset::Entry *last_entry = m_core_aranges.Back(); + if (last_entry && + last_entry->GetRangeEnd() == range_entry.GetRangeBase() && + last_entry->data.GetRangeEnd() == range_entry.data.GetRangeBase() && + last_entry->GetByteSize() == last_entry->data.GetByteSize()) { + last_entry->SetRangeEnd(range_entry.GetRangeEnd()); + last_entry->data.SetRangeEnd(range_entry.data.GetRangeEnd()); + } else { + m_core_aranges.Append(range_entry); + } + } + + const uint32_t permissions = lldb::ePermissionsReadable | + lldb::ePermissionsWritable; + + m_core_range_infos.Append( + VMRangeToPermissions::Entry(addr, header.StackSize, permissions)); + + return addr; +} + +bool ProcessAIXCore::CanDebug(lldb::TargetSP target_sp, + bool plugin_specified_by_name) { + + if (!m_core_module_sp && FileSystem::Instance().Exists(m_core_file)) { + ModuleSpec core_module_spec(m_core_file, target_sp->GetArchitecture()); + Status error(ModuleList::GetSharedModule(core_module_spec, m_core_module_sp, + nullptr, nullptr, nullptr)); + if (m_core_module_sp) { + ObjectFile *core_objfile = m_core_module_sp->GetObjectFile(); + if (core_objfile && core_objfile->GetType() == ObjectFile::eTypeCoreFile){ + return true; + } + } + } + return false; + +} + +ArchSpec ProcessAIXCore::GetArchitecture() { + + ArchSpec arch = m_core_module_sp->GetObjectFile()->GetArchitecture(); + + ArchSpec target_arch = GetTarget().GetArchitecture(); + arch.MergeFrom(target_arch); + + return arch; +} + +lldb_private::DynamicLoader *ProcessAIXCore::GetDynamicLoader() { + if (m_dyld_up.get() == nullptr) { + m_dyld_up.reset(DynamicLoader::FindPlugin( + this, DynamicLoaderAIXDYLD::GetPluginNameStatic())); + } + return m_dyld_up.get(); +} + +void ProcessAIXCore::ParseAIXCoreFile() { + + Log *log = GetLog(LLDBLog::Process); + AIXSigInfo siginfo; + ThreadData thread_data; + + const lldb_private::UnixSignals &unix_signals = *GetUnixSignals(); + const ArchSpec &arch = GetArchitecture(); + + siginfo.Parse(m_aixcore_header, arch, unix_signals); + thread_data.siginfo = siginfo; + SetID(m_aixcore_header.User.process.pi_pid); + + thread_data.name.assign (m_aixcore_header.User.process.pi_comm, + strnlen (m_aixcore_header.User.process.pi_comm, + sizeof (m_aixcore_header.User.process.pi_comm))); + + lldb::DataBufferSP data_buffer_sp(new lldb_private::DataBufferHeap(sizeof(m_aixcore_header.Fault.context), 0)); + + memcpy(static_cast(const_cast(data_buffer_sp->GetBytes())), + &m_aixcore_header.Fault.context, sizeof(m_aixcore_header.Fault.context)); + + lldb_private::DataExtractor data(data_buffer_sp, lldb::eByteOrderBig, 8); + + thread_data.gpregset = DataExtractor(data, 0, sizeof(m_aixcore_header.Fault.context)); + m_thread_data.push_back(thread_data); + LLDB_LOGF(log, "ProcessAIXCore: Parsing Complete!"); + +} + +// Process Control +Status ProcessAIXCore::DoLoadCore() { + + Status error; + if (!m_core_module_sp) { + error = Status::FromErrorString("invalid core module"); + return error; + } + + FileSpec file = m_core_module_sp->GetObjectFile()->GetFileSpec(); + Log *log = GetLog(LLDBLog::Process); + + if (file) { + const size_t header_size = sizeof(AIXCORE::AIXCore64Header); + auto data_sp = FileSystem::Instance().CreateDataBuffer( + file.GetPath(), -1, 0); + if (data_sp && data_sp->GetByteSize() != 0) { + + DataExtractor data(data_sp, lldb::eByteOrderBig, 4); + lldb::offset_t data_offset = 0; + m_aixcore_header.ParseCoreHeader(data, &data_offset); + lldb::addr_t addr = AddAddressRanges(m_aixcore_header); + if (addr == LLDB_INVALID_ADDRESS) + LLDB_LOGF(log, "ProcessAIXCore: Invalid base address. Stack information will be limited"); + auto dyld = static_cast(GetDynamicLoader()); + dyld->FillCoreLoaderData(data, m_aixcore_header.LoaderOffset, + m_aixcore_header.LoaderSize); + + } else { + error = Status::FromErrorString("invalid data"); + return error; + } + } else { + error = Status::FromErrorString("invalid file"); + return error; + } + + m_thread_data_valid = true; + ParseAIXCoreFile(); + ArchSpec arch(m_core_module_sp->GetArchitecture()); + + ArchSpec target_arch = GetTarget().GetArchitecture(); + ArchSpec core_arch(m_core_module_sp->GetArchitecture()); + target_arch.MergeFrom(core_arch); + GetTarget().SetArchitecture(target_arch); + + lldb::ModuleSP exe_module_sp = GetTarget().GetExecutableModule(); + if (!exe_module_sp) { + ModuleSpec exe_module_spec; + exe_module_spec.GetArchitecture() = arch; + exe_module_spec.GetFileSpec().SetFile(m_aixcore_header.User.process.pi_comm, + FileSpec::Style::native); + exe_module_sp = + GetTarget().GetOrCreateModule(exe_module_spec, true /* notify */); + if (exe_module_sp) + GetTarget().SetExecutableModule(exe_module_sp, eLoadDependentsNo); + } + + return error; +} + +bool ProcessAIXCore::DoUpdateThreadList(ThreadList &old_thread_list, + ThreadList &new_thread_list) +{ + const ThreadData &td = m_thread_data[0]; + + lldb::ThreadSP thread_sp = + std::make_shared(*this, td); + new_thread_list.AddThread(thread_sp); + + return true; +} + +void ProcessAIXCore::RefreshStateAfterStop() {} + +// Process Memory +size_t ProcessAIXCore::ReadMemory(lldb::addr_t addr, void *buf, size_t size, + Status &error) { + if(addr == LLDB_INVALID_ADDRESS) + return 0; + + if (lldb::ABISP abi_sp = GetABI()) + addr = abi_sp->FixAnyAddress(addr); + + // Don't allow the caching that lldb_private::Process::ReadMemory does since + // in core files we have it all cached our our core file anyway. + return DoReadMemory(addr, buf, size, error); +} + +size_t ProcessAIXCore::DoReadMemory(lldb::addr_t addr, void *buf, size_t size, + Status &error) { + ObjectFile *core_objfile = m_core_module_sp->GetObjectFile(); + if (core_objfile == nullptr) + return 0; + // Get the address range + const VMRangeToFileOffset::Entry *address_range = + m_core_aranges.FindEntryThatContains(addr); + if (address_range == nullptr || address_range->GetRangeEnd() < addr) { + error = Status::FromErrorStringWithFormat( + "core file does not contain 0x%" PRIx64, addr); + return 0; + } + + // Convert the address into core file offset + const lldb::addr_t offset = addr - address_range->GetRangeBase(); + const lldb::addr_t file_start = address_range->data.GetRangeBase(); + const lldb::addr_t file_end = address_range->data.GetRangeEnd(); + size_t bytes_to_read = size; // Number of bytes to read from the core file + size_t bytes_copied = 0; // Number of bytes actually read from the core file + // Number of bytes available in the core file from the given address + lldb::addr_t bytes_left = 0; + + // Don't proceed if core file doesn't contain the actual data for this + // address range. + if (file_start == file_end) + return 0; + + // Figure out how many on-disk bytes remain in this segment starting at the + // given offset + if (file_end > file_start + offset) + bytes_left = file_end - (file_start + offset); + + if (bytes_to_read > bytes_left) + bytes_to_read = bytes_left; + + // If there is data available on the core file read it + if (bytes_to_read) + bytes_copied = + core_objfile->CopyData(offset + file_start, bytes_to_read, buf); + + return bytes_copied; +} + +Status ProcessAIXCore::DoGetMemoryRegionInfo(lldb::addr_t load_addr, + MemoryRegionInfo ®ion_info) { + return Status(); +} + +Status ProcessAIXCore::DoDestroy() { return Status(); } diff --git a/lldb/source/Plugins/Process/aix-core/ProcessAIXCore.h b/lldb/source/Plugins/Process/aix-core/ProcessAIXCore.h new file mode 100644 index 0000000000000..ffd9e401ee192 --- /dev/null +++ b/lldb/source/Plugins/Process/aix-core/ProcessAIXCore.h @@ -0,0 +1,111 @@ +//===-- ProcessAIXCore.h ----------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// Notes about AIX Process core dumps: +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SOURCE_PLUGINS_PROCESS_AIX_CORE_PROCESSAIXCORE_H +#define LLDB_SOURCE_PLUGINS_PROCESS_AIX_CORE_PROCESSAIXCORE_H + +#include +#include + +#include "lldb/Target/PostMortemProcess.h" +#include "lldb/Utility/Status.h" +#include "lldb/Target/Process.h" +#include "AIXCore.h" +#include "ThreadAIXCore.h" + +struct ThreadData; + +class ProcessAIXCore : public lldb_private::PostMortemProcess { +public: + // Constructors and Destructors + static lldb::ProcessSP + CreateInstance(lldb::TargetSP target_sp, lldb::ListenerSP listener_sp, + const lldb_private::FileSpec *crash_file_path, + bool can_connect); + + static void Initialize(); + + static void Terminate(); + + static llvm::StringRef GetPluginNameStatic() { return "aix-core"; } + + static llvm::StringRef GetPluginDescriptionStatic(); + + // Constructors and Destructors + ProcessAIXCore(lldb::TargetSP target_sp, lldb::ListenerSP listener_sp, + const lldb_private::FileSpec &core_file); + + ~ProcessAIXCore() override; + + // PluginInterface protocol + llvm::StringRef GetPluginName() override { return GetPluginNameStatic(); } + + // Process Control + lldb_private::Status DoDestroy() override; + + lldb_private::Status WillResume() override { + return lldb_private::Status::FromErrorStringWithFormatv( + "error: {0} does not support resuming processes", GetPluginName()); + } + + bool WarnBeforeDetach() const override { return false; } + + lldb_private::ArchSpec GetArchitecture(); + + bool CanDebug(lldb::TargetSP target_sp, + bool plugin_specified_by_name) override; + + // Creating a new process, or attaching to an existing one + lldb_private::Status DoLoadCore() override; + + bool DoUpdateThreadList(lldb_private::ThreadList &old_thread_list, + lldb_private::ThreadList &new_thread_list) override; + + lldb_private::Status + DoGetMemoryRegionInfo(lldb::addr_t load_addr, + lldb_private::MemoryRegionInfo ®ion_info) override; + + void RefreshStateAfterStop() override; + + lldb_private::DynamicLoader *GetDynamicLoader() override; + + // Process Memory + size_t ReadMemory(lldb::addr_t addr, void *buf, size_t size, + lldb_private::Status &error) override; + + size_t DoReadMemory(lldb::addr_t addr, void *buf, size_t size, + lldb_private::Status &error) override; + + void ParseAIXCoreFile(); + + lldb::addr_t AddAddressRanges(AIXCORE::AIXCore64Header header); + +private: + lldb::ModuleSP m_core_module_sp; + std::string m_dyld_plugin_name; + + typedef lldb_private::Range FileRange; + typedef lldb_private::RangeDataVector + VMRangeToFileOffset; + typedef lldb_private::RangeDataVector + VMRangeToPermissions; + + // Address ranges found in the core + VMRangeToFileOffset m_core_aranges; + VMRangeToPermissions m_core_range_infos; + + // True if m_thread_contexts contains valid entries + bool m_thread_data_valid = false; + AIXCORE::AIXCore64Header m_aixcore_header; + + std::vector m_thread_data; +}; + +#endif // LLDB_SOURCE_PLUGINS_PROCESS_AIX_CORE_PROCESSAIXCORE_H diff --git a/lldb/source/Plugins/Process/aix-core/RegisterContextCoreAIX_ppc64.cpp b/lldb/source/Plugins/Process/aix-core/RegisterContextCoreAIX_ppc64.cpp new file mode 100644 index 0000000000000..b243017bf9a2a --- /dev/null +++ b/lldb/source/Plugins/Process/aix-core/RegisterContextCoreAIX_ppc64.cpp @@ -0,0 +1,136 @@ +//===-- RegisterContextCoreAIX_ppc64.cpp ------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "RegisterContextCoreAIX_ppc64.h" + +#include "lldb/Target/Thread.h" +#include "lldb/Utility/DataBufferHeap.h" +#include "lldb/Utility/RegisterValue.h" + +#include "Plugins/Process/Utility/lldb-ppc64le-register-enums.h" +#include "Plugins/Process/elf-core/RegisterUtilities.h" + +#include + +using namespace lldb_private; + +RegisterContextCoreAIX_ppc64::RegisterContextCoreAIX_ppc64( + Thread &thread, RegisterInfoInterface *register_info, + const DataExtractor &gpregset) + : RegisterContextPOSIX_ppc64le(thread, 0, register_info) { + m_gpr_buffer = std::make_shared(gpregset.GetDataStart(), + gpregset.GetByteSize()); + m_gpr.SetData(m_gpr_buffer); + m_gpr.SetByteOrder(gpregset.GetByteOrder()); + + // This Code is for Registers like FPR, VSR, VMX and is disabled right now. + // It will be implemented as per need. + + /* ArchSpec arch = register_info->GetTargetArchitecture(); + DataExtractor fpregset;// = getRegset(notes, arch.GetTriple(), FPR_Desc); + m_fpr_buffer = std::make_shared(fpregset.GetDataStart(), + fpregset.GetByteSize()); + m_fpr.SetData(m_fpr_buffer); + m_fpr.SetByteOrder(fpregset.GetByteOrder()); + + DataExtractor vmxregset;// = getRegset(notes, arch.GetTriple(), PPC_VMX_Desc); + m_vmx_buffer = std::make_shared(vmxregset.GetDataStart(), + vmxregset.GetByteSize()); + m_vmx.SetData(m_vmx_buffer); + m_vmx.SetByteOrder(vmxregset.GetByteOrder()); + + DataExtractor vsxregset;// = getRegset(notes, arch.GetTriple(), PPC_VSX_Desc); + m_vsx_buffer = std::make_shared(vsxregset.GetDataStart(), + vsxregset.GetByteSize()); + m_vsx.SetData(m_vsx_buffer); + m_vsx.SetByteOrder(vsxregset.GetByteOrder());*/ +} + +size_t RegisterContextCoreAIX_ppc64::GetFPRSize() const { + return k_num_fpr_registers_ppc64le * sizeof(uint64_t); +} + +size_t RegisterContextCoreAIX_ppc64::GetVMXSize() const { + return (k_num_vmx_registers_ppc64le - 1) * sizeof(uint64_t) * 2 + + sizeof(uint32_t); +} + +size_t RegisterContextCoreAIX_ppc64::GetVSXSize() const { + return k_num_vsx_registers_ppc64le * sizeof(uint64_t) * 2; +} + +bool RegisterContextCoreAIX_ppc64::ReadRegister( + const RegisterInfo *reg_info, RegisterValue &value) { + lldb::offset_t offset = reg_info->byte_offset; + + if (IsFPR(reg_info->kinds[lldb::eRegisterKindLLDB])) { + uint64_t v; + offset -= GetGPRSize(); + offset = m_fpr.CopyData(offset, reg_info->byte_size, &v); + + if (offset == reg_info->byte_size) { + value.SetBytes(&v, reg_info->byte_size, m_fpr.GetByteOrder()); + return true; + } + } else if (IsVMX(reg_info->kinds[lldb::eRegisterKindLLDB])) { + uint32_t v[4]; + offset -= GetGPRSize() + GetFPRSize(); + offset = m_vmx.CopyData(offset, reg_info->byte_size, &v); + + if (offset == reg_info->byte_size) { + value.SetBytes(v, reg_info->byte_size, m_vmx.GetByteOrder()); + return true; + } + } else if (IsVSX(reg_info->kinds[lldb::eRegisterKindLLDB])) { + uint32_t v[4]; + lldb::offset_t tmp_offset; + offset -= GetGPRSize() + GetFPRSize() + GetVMXSize(); + + if (offset < GetVSXSize() / 2) { + tmp_offset = m_vsx.CopyData(offset / 2, reg_info->byte_size / 2, &v); + + if (tmp_offset != reg_info->byte_size / 2) { + return false; + } + + uint8_t *dst = (uint8_t *)&v + sizeof(uint64_t); + tmp_offset = m_fpr.CopyData(offset / 2, reg_info->byte_size / 2, dst); + + if (tmp_offset != reg_info->byte_size / 2) { + return false; + } + + value.SetBytes(&v, reg_info->byte_size, m_vsx.GetByteOrder()); + return true; + } else { + offset = + m_vmx.CopyData(offset - GetVSXSize() / 2, reg_info->byte_size, &v); + if (offset == reg_info->byte_size) { + value.SetBytes(v, reg_info->byte_size, m_vmx.GetByteOrder()); + return true; + } + } + } else { + uint64_t v = m_gpr.GetMaxU64(&offset, reg_info->byte_size); + + if (offset == reg_info->byte_offset + reg_info->byte_size) { + if (reg_info->byte_size < sizeof(v)) + value = (uint32_t)v; + else + value = v; + return true; + } + } + + return false; +} + +bool RegisterContextCoreAIX_ppc64::WriteRegister( + const RegisterInfo *reg_info, const RegisterValue &value) { + return false; +} diff --git a/lldb/source/Plugins/Process/aix-core/RegisterContextCoreAIX_ppc64.h b/lldb/source/Plugins/Process/aix-core/RegisterContextCoreAIX_ppc64.h new file mode 100644 index 0000000000000..8f1f71ce8d884 --- /dev/null +++ b/lldb/source/Plugins/Process/aix-core/RegisterContextCoreAIX_ppc64.h @@ -0,0 +1,46 @@ +//===-- RegisterContextCoreAIX_ppc64.h ----------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SOURCE_PLUGINS_PROCESS_AIX_CORE_REGISTERCONTEXTAIXCORE_PPC64_H +#define LLDB_SOURCE_PLUGINS_PROCESS_AIX_CORE_REGISTERCONTEXTAIXCORE_PPC64_H + +#include "Plugins/Process/Utility/RegisterContextPOSIX_ppc64le.h" +#include "lldb/Utility/DataExtractor.h" + +class RegisterContextCoreAIX_ppc64 : public RegisterContextPOSIX_ppc64le { +public: + RegisterContextCoreAIX_ppc64( + lldb_private::Thread &thread, + lldb_private::RegisterInfoInterface *register_info, + const lldb_private::DataExtractor &gpregset); + + bool ReadRegister(const lldb_private::RegisterInfo *reg_info, + lldb_private::RegisterValue &value) override; + + bool WriteRegister(const lldb_private::RegisterInfo *reg_info, + const lldb_private::RegisterValue &value) override; + +protected: + size_t GetFPRSize() const; + + size_t GetVMXSize() const; + + size_t GetVSXSize() const; + +private: + lldb::DataBufferSP m_gpr_buffer; + lldb::DataBufferSP m_fpr_buffer; + lldb::DataBufferSP m_vmx_buffer; + lldb::DataBufferSP m_vsx_buffer; + lldb_private::DataExtractor m_gpr; + lldb_private::DataExtractor m_fpr; + lldb_private::DataExtractor m_vmx; + lldb_private::DataExtractor m_vsx; +}; + +#endif // LLDB_SOURCE_PLUGINS_PROCESS_AIX_CORE_REGISTERCONTEXTAIXCORE_PPC64_H diff --git a/lldb/source/Plugins/Process/aix-core/ThreadAIXCore.cpp b/lldb/source/Plugins/Process/aix-core/ThreadAIXCore.cpp new file mode 100644 index 0000000000000..979e5199fe24d --- /dev/null +++ b/lldb/source/Plugins/Process/aix-core/ThreadAIXCore.cpp @@ -0,0 +1,127 @@ +//===-- ThreadAIXCore.cpp -------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "lldb/Target/RegisterContext.h" +#include "lldb/Target/StopInfo.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/UnixSignals.h" +#include "lldb/Target/Unwind.h" +#include "lldb/Utility/DataExtractor.h" +#include "lldb/Utility/LLDBLog.h" +#include "lldb/Utility/Log.h" +#include "lldb/Utility/ProcessInfo.h" + +#include "Plugins/Process/Utility/RegisterContextPOSIX_powerpc.h" +#include "Plugins/Process/Utility/RegisterContextPOSIX_ppc64le.h" +#include "Plugins/Process/Utility/RegisterInfoPOSIX_ppc64le.h" +#include "Plugins/Process/elf-core/RegisterContextPOSIXCore_powerpc.h" +#include "RegisterContextCoreAIX_ppc64.h" + +#include "ProcessAIXCore.h" +#include "AIXCore.h" +#include "ThreadAIXCore.h" + +#include +#include + +using namespace lldb; +using namespace lldb_private; + +// Construct a Thread object with given data +ThreadAIXCore::ThreadAIXCore(Process &process, const ThreadData &td) + : Thread(process, td.tid), m_thread_name(td.name), m_thread_reg_ctx_sp(), + m_gpregset_data(td.gpregset), + m_siginfo(std::move(td.siginfo)) {} + +ThreadAIXCore::~ThreadAIXCore() { DestroyThread(); } + +void ThreadAIXCore::RefreshStateAfterStop() { + GetRegisterContext()->InvalidateIfNeeded(false); +} + +RegisterContextSP ThreadAIXCore::GetRegisterContext() { + if (!m_reg_context_sp) { + m_reg_context_sp = CreateRegisterContextForFrame(nullptr); + } + return m_reg_context_sp; +} + +RegisterContextSP +ThreadAIXCore::CreateRegisterContextForFrame(StackFrame *frame) { + RegisterContextSP reg_ctx_sp; + uint32_t concrete_frame_idx = 0; + + if (frame) + concrete_frame_idx = frame->GetConcreteFrameIndex(); + + bool is_linux = false; + if (concrete_frame_idx == 0) { + if (m_thread_reg_ctx_sp) + return m_thread_reg_ctx_sp; + + ProcessAIXCore *process = static_cast(GetProcess().get()); + ArchSpec arch = process->GetArchitecture(); + RegisterInfoInterface *reg_interface = nullptr; + + switch (arch.GetMachine()) { + case llvm::Triple::ppc64: + reg_interface = new RegisterInfoPOSIX_ppc64le(arch); + m_thread_reg_ctx_sp = std::make_shared( + *this, reg_interface, m_gpregset_data); + break; + default: + break; + } + reg_ctx_sp = m_thread_reg_ctx_sp; + } else { + reg_ctx_sp = GetUnwinder().CreateRegisterContextForFrame(frame); + } + return reg_ctx_sp; +} + +bool ThreadAIXCore::CalculateStopInfo() { + ProcessSP process_sp(GetProcess()); + if (!process_sp) + return false; + + lldb::UnixSignalsSP unix_signals_sp(process_sp->GetUnixSignals()); + if (!unix_signals_sp) + return false; + + const char *sig_description; + std::string description = m_siginfo.GetDescription(*unix_signals_sp); + if (description.empty()) + sig_description = nullptr; + else + sig_description = description.c_str(); + + SetStopInfo(StopInfo::CreateStopReasonWithSignal( + *this, m_siginfo.si_signo, sig_description, m_siginfo.si_code)); + + SetStopInfo(m_stop_info_sp); + return true; +} + +void AIXSigInfo::Parse(const AIXCORE::AIXCore64Header data, const ArchSpec &arch, + const lldb_private::UnixSignals &unix_signals) { + si_signo = data.SignalNum; + sigfault.si_addr = data.Fault.context.pc; +} + +AIXSigInfo::AIXSigInfo() { memset(this, 0, sizeof(AIXSigInfo)); } + +size_t AIXSigInfo::GetSize(const lldb_private::ArchSpec &arch) { + return sizeof(AIXSigInfo); +} + +std::string AIXSigInfo::GetDescription( + const lldb_private::UnixSignals &unix_signals) const { + return unix_signals.GetSignalDescription(si_signo, 0, + sigfault.si_addr); + +} diff --git a/lldb/source/Plugins/Process/aix-core/ThreadAIXCore.h b/lldb/source/Plugins/Process/aix-core/ThreadAIXCore.h new file mode 100644 index 0000000000000..9ee157e9b2b9b --- /dev/null +++ b/lldb/source/Plugins/Process/aix-core/ThreadAIXCore.h @@ -0,0 +1,110 @@ +//===-- ThreadAIXCore.h -----------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SOURCE_PLUGINS_PROCESS_AIX_CORE_THREADAIXCORE_H +#define LLDB_SOURCE_PLUGINS_PROCESS_AIX_CORE_THREADAIXCORE_H + +#include "lldb/Target/Thread.h" +#include "lldb/Utility/DataExtractor.h" +#include "llvm/ADT/DenseMap.h" +#include +#include +#include "ProcessAIXCore.h" +#include "AIXCore.h" +#include "ThreadAIXCore.h" + +namespace lldb_private { +class ProcessInstanceInfo; +} + +struct AIXSigInfo { + + //COPY siginfo_t correctly for AIX version + int32_t si_signo; // Order matters for the first 3. + int32_t si_errno; + int32_t si_code; + struct alignas(8) { + lldb::addr_t si_addr; + int16_t si_addr_lsb; + union { + struct { + lldb::addr_t _lower; + lldb::addr_t _upper; + } _addr_bnd; + uint32_t _pkey; + } bounds; + } sigfault; + + enum SigInfoNoteType : uint8_t { eUnspecified, eNT_SIGINFO }; + SigInfoNoteType note_type; + + AIXSigInfo(); + + void Parse(const AIXCORE::AIXCore64Header data, + const lldb_private::ArchSpec &arch, + const lldb_private::UnixSignals &unix_signals); + + std::string + GetDescription(const lldb_private::UnixSignals &unix_signals) const; + + static size_t GetSize(const lldb_private::ArchSpec &arch); +}; + +struct ThreadData { + lldb_private::DataExtractor gpregset; + std::vector notes; + lldb::tid_t tid; + std::string name; + AIXSigInfo siginfo; + int prstatus_sig = 0; +}; + +class ThreadAIXCore : public lldb_private::Thread { +public: + ThreadAIXCore(lldb_private::Process &process, const ThreadData &td); + + ~ThreadAIXCore() override; + + void RefreshStateAfterStop() override; + + lldb::RegisterContextSP GetRegisterContext() override; + + lldb::RegisterContextSP + CreateRegisterContextForFrame(lldb_private::StackFrame *frame) override; + + static bool ThreadIDIsValid(lldb::tid_t thread) { return thread != 0; } + + const char *GetName() override { + if (m_thread_name.empty()) + return nullptr; + return m_thread_name.c_str(); + } + + void SetName(const char *name) override { + if (name && name[0]) + m_thread_name.assign(name); + else + m_thread_name.clear(); + } + + void CreateStopFromSigInfo(const AIXSigInfo &siginfo, + const lldb_private::UnixSignals &unix_signals); + +protected: + // Member variables. + std::string m_thread_name; + lldb::RegisterContextSP m_thread_reg_ctx_sp; + + lldb_private::DataExtractor m_gpregset_data; + std::vector m_notes; + AIXSigInfo m_siginfo; + + bool CalculateStopInfo() override; +}; + +#endif // LLDB_SOURCE_PLUGINS_PROCESS_AIX_CORE_THREADAIXCORE_H diff --git a/lldb/source/Plugins/Process/gdb-remote/CMakeLists.txt b/lldb/source/Plugins/Process/gdb-remote/CMakeLists.txt index 5c39384fa31b9..11ebdc149e6d2 100644 --- a/lldb/source/Plugins/Process/gdb-remote/CMakeLists.txt +++ b/lldb/source/Plugins/Process/gdb-remote/CMakeLists.txt @@ -6,6 +6,11 @@ lldb_tablegen(ProcessGDBRemotePropertiesEnum.inc -gen-lldb-property-enum-defs SOURCE ProcessGDBRemoteProperties.td TARGET LLDBPluginProcessGDBRemotePropertiesEnumGen) +if (UNIX AND ${CMAKE_SYSTEM_NAME} MATCHES "AIX") + remove_definitions("-D_XOPEN_SOURCE=700") + add_definitions("-D_ALL_SOURCE") +endif() + set(LLDB_PLUGINS lldbPluginProcessUtility ) diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp index 7d2bd452acca9..44ffcd0475480 100644 --- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp +++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp @@ -41,6 +41,10 @@ #include "llvm/Config/llvm-config.h" // for LLVM_ENABLE_ZLIB #include "llvm/Support/JSON.h" +#if defined(_AIX) +#include +#endif + #if HAVE_LIBCOMPRESSION #include #endif @@ -1730,6 +1734,32 @@ Status GDBRemoteCommunicationClient::GetMemoryRegionInfo( return error; } +#if defined(_AIX) +Status GDBRemoteCommunicationClient::GetLDXINFO(struct ld_xinfo *info_ptr) +{ + Status error; + + char packet[64]; + const int packet_len = ::snprintf(packet, sizeof(packet), "qLDXINFO"); + assert(packet_len < (int)sizeof(packet)); + UNUSED_IF_ASSERT_DISABLED(packet_len); + StringExtractorGDBRemote response; + if (SendPacketAndWaitForResponse(packet, response) == + PacketResult::Success && + response.GetResponseType() == StringExtractorGDBRemote::eResponse) { + llvm::MutableArrayRef infoData((uint8_t *)info_ptr, sizeof(struct ld_xinfo)*64); + size_t got_bytes = response.GetHexBytesAvail(infoData); + if (got_bytes != sizeof(struct ld_xinfo)*64) { + error.FromErrorString("qLDXINFO ret bad size"); + return error; + } + } else { + error.FromErrorString("qLDXINFO is not supported"); + } + return error; +} +#endif + Status GDBRemoteCommunicationClient::GetQXferMemoryMapRegionInfo( lldb::addr_t addr, MemoryRegionInfo ®ion) { Status error = LoadQXferMemoryMap(); diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h index a765e95bf9814..a405c734d2b3c 100644 --- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h +++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h @@ -32,6 +32,10 @@ #include "llvm/Support/VersionTuple.h" +#if defined(_AIX) +struct ld_xinfo; +#endif + namespace lldb_private { namespace process_gdb_remote { @@ -196,6 +200,9 @@ class GDBRemoteCommunicationClient : public GDBRemoteClientBase { Status GetMemoryRegionInfo(lldb::addr_t addr, MemoryRegionInfo &range_info); std::optional GetWatchpointSlotCount(); +#if defined(_AIX) + Status GetLDXINFO(struct ld_xinfo *info_ptr); +#endif std::optional GetWatchpointReportedAfter(); diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp index 89d2730cfccd0..00735791081c7 100644 --- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp +++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp @@ -48,6 +48,9 @@ #include "ProcessGDBRemote.h" #include "ProcessGDBRemoteLog.h" #include "lldb/Utility/StringExtractorGDBRemote.h" +#if defined(_AIX) +#include +#endif using namespace lldb; using namespace lldb_private; @@ -193,6 +196,8 @@ void GDBRemoteCommunicationServerLLGS::RegisterPacketHandlers() { &GDBRemoteCommunicationServerLLGS::Handle_Z); RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_z, &GDBRemoteCommunicationServerLLGS::Handle_z); + RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_qLDXINFO, + &GDBRemoteCommunicationServerLLGS::Handle_qLDXINFO); RegisterMemberFunctionHandler( StringExtractorGDBRemote::eServerPacketType_QPassSignals, &GDBRemoteCommunicationServerLLGS::Handle_QPassSignals); @@ -3005,6 +3010,29 @@ GDBRemoteCommunicationServerLLGS::Handle_z(StringExtractorGDBRemote &packet) { } } +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerLLGS::Handle_qLDXINFO(StringExtractorGDBRemote &packet) { + if (!m_current_process || + (m_current_process->GetID() == LLDB_INVALID_PROCESS_ID)) { + Log *log = GetLog(LLDBLog::Process); + LLDB_LOG(log, "qLDXINFO failed, no process available"); + return SendErrorResponse(0xff); + } + +#if defined(_AIX) + // FIXME: buffer size + struct ld_xinfo info[64]; + if (ptrace64(PT_LDXINFO, m_current_process->GetID(), (long long)&(info[0]), sizeof(info), nullptr) != 0) { + return SendErrorResponse(0xff); + } + StreamGDBRemote response; + response.PutBytesAsRawHex8(&(info[0]), sizeof(info)); + return SendPacketNoLock(response.GetString()); +#else + return SendErrorResponse(0xff); +#endif +} + GDBRemoteCommunication::PacketResult GDBRemoteCommunicationServerLLGS::Handle_s(StringExtractorGDBRemote &packet) { Log *log = GetLog(LLDBLog::Process | LLDBLog::Thread); diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.h b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.h index 646b6a102abf6..a464479e178de 100644 --- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.h +++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.h @@ -211,6 +211,8 @@ class GDBRemoteCommunicationServerLLGS PacketResult Handle_z(StringExtractorGDBRemote &packet); + PacketResult Handle_qLDXINFO(StringExtractorGDBRemote &packet); + PacketResult Handle_s(StringExtractorGDBRemote &packet); PacketResult Handle_qXfer(StringExtractorGDBRemote &packet); diff --git a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp index a2c34ddfc252e..bebc561f8cfd5 100644 --- a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp +++ b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp @@ -93,6 +93,10 @@ #include "llvm/Support/Threading.h" #include "llvm/Support/raw_ostream.h" +#if defined(_AIX) +#include +#endif + #if defined(__APPLE__) #define DEBUGSERVER_BASENAME "debugserver" #elif defined(_WIN32) @@ -3020,6 +3024,13 @@ Status ProcessGDBRemote::DoGetMemoryRegionInfo(addr_t load_addr, return error; } +#if defined(_AIX) +Status ProcessGDBRemote::DoGetLDXINFO(struct ld_xinfo *info_ptr) { + Status error(m_gdb_comm.GetLDXINFO(info_ptr)); + return error; +} +#endif + std::optional ProcessGDBRemote::GetWatchpointSlotCount() { return m_gdb_comm.GetWatchpointSlotCount(); } diff --git a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h index 7ae33837fd067..2a9d4e735e8ea 100644 --- a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h +++ b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h @@ -37,6 +37,10 @@ #include "GDBRemoteCommunicationClient.h" #include "GDBRemoteRegisterContext.h" +#if defined(_AIX) +struct ld_xinfo; +#endif + #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/StringMap.h" @@ -422,6 +426,10 @@ class ProcessGDBRemote : public Process, Status DoGetMemoryRegionInfo(lldb::addr_t load_addr, MemoryRegionInfo ®ion_info) override; +#if defined(_AIX) + Status DoGetLDXINFO(struct ld_xinfo *info_ptr) override; +#endif + private: // For ProcessGDBRemote only std::string m_partial_profile_data; diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/CMakeLists.txt b/lldb/source/Plugins/ScriptInterpreter/Python/CMakeLists.txt index 3d09cea464467..d1dc2a7bcfe8d 100644 --- a/lldb/source/Plugins/ScriptInterpreter/Python/CMakeLists.txt +++ b/lldb/source/Plugins/ScriptInterpreter/Python/CMakeLists.txt @@ -20,6 +20,11 @@ if (LLDB_ENABLE_LIBEDIT) endif() add_subdirectory(Interfaces) +if (UNIX AND ${CMAKE_SYSTEM_NAME} MATCHES "AIX") + remove_definitions("-D_XOPEN_SOURCE=700") + add_definitions("-D_ALL_SOURCE") +endif() + add_lldb_library(lldbPluginScriptInterpreterPython PLUGIN PythonDataObjects.cpp diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFFormValue.cpp b/lldb/source/Plugins/SymbolFile/DWARF/DWARFFormValue.cpp index 2e98e3c33acaf..e5ed4b6b2fd39 100644 --- a/lldb/source/Plugins/SymbolFile/DWARF/DWARFFormValue.cpp +++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFFormValue.cpp @@ -77,9 +77,8 @@ bool DWARFFormValue::ExtractValue(const DWARFDataExtractor &data, case DW_FORM_strp: case DW_FORM_line_strp: case DW_FORM_sec_offset: - assert(m_unit); - ref_addr_size = m_unit->GetFormParams().getDwarfOffsetByteSize(); - m_value.uval = data.GetMaxU64(offset_ptr, ref_addr_size); + //FIXME: For AIX + m_value.uval = data.GetMaxU64(offset_ptr, 4); break; case DW_FORM_addrx1: case DW_FORM_strx1: @@ -121,7 +120,11 @@ bool DWARFFormValue::ExtractValue(const DWARFDataExtractor &data, break; case DW_FORM_ref_addr: assert(m_unit); - ref_addr_size = m_unit->GetFormParams().getRefAddrByteSize(); + if (m_unit->GetVersion() <= 2) + ref_addr_size = m_unit->GetAddressByteSize(); + else + ref_addr_size = 4; + //FIXME: For AIX m_value.uval = data.GetMaxU64(offset_ptr, ref_addr_size); break; case DW_FORM_indirect: @@ -164,7 +167,7 @@ static FormSize g_form_sizes[] = { {1, 1}, // 0x0b DW_FORM_data1 {1, 1}, // 0x0c DW_FORM_flag {0, 0}, // 0x0d DW_FORM_sdata - {0, 0}, // 0x0e DW_FORM_strp (4 bytes for DWARF32, 8 bytes for DWARF64) + {1, 4}, // 0x0e DW_FORM_strp {0, 0}, // 0x0f DW_FORM_udata {0, 0}, // 0x10 DW_FORM_ref_addr (addr size for DWARF2 and earlier, 4 bytes // for DWARF32, 8 bytes for DWARF32 in DWARF 3 and later @@ -174,7 +177,7 @@ static FormSize g_form_sizes[] = { {1, 8}, // 0x14 DW_FORM_ref8 {0, 0}, // 0x15 DW_FORM_ref_udata {0, 0}, // 0x16 DW_FORM_indirect - {0, 0}, // 0x17 DW_FORM_sec_offset (4 bytes for DWARF32,8 bytes for DWARF64) + {1, 4}, // 0x17 DW_FORM_sec_offset {0, 0}, // 0x18 DW_FORM_exprloc {1, 0}, // 0x19 DW_FORM_flag_present {0, 0}, // 0x1a DW_FORM_strx (ULEB128) @@ -182,7 +185,7 @@ static FormSize g_form_sizes[] = { {1, 4}, // 0x1c DW_FORM_ref_sup4 {0, 0}, // 0x1d DW_FORM_strp_sup (4 bytes for DWARF32, 8 bytes for DWARF64) {1, 16}, // 0x1e DW_FORM_data16 - {0, 0}, // 0x1f DW_FORM_line_strp (4 bytes for DWARF32, 8 bytes for DWARF64) + {1, 4}, // 0x1f DW_FORM_line_strp {1, 8}, // 0x20 DW_FORM_ref_sig8 }; @@ -245,9 +248,13 @@ bool DWARFFormValue::SkipValue(dw_form_t form, return true; case DW_FORM_ref_addr: + ref_addr_size = 4; assert(unit); // Unit must be valid for DW_FORM_ref_addr objects or we will // get this wrong - ref_addr_size = unit->GetFormParams().getRefAddrByteSize(); + if (unit->GetVersion() <= 2) + ref_addr_size = unit->GetAddressByteSize(); + else + ref_addr_size = 4; *offset_ptr += ref_addr_size; return true; @@ -283,9 +290,7 @@ bool DWARFFormValue::SkipValue(dw_form_t form, case DW_FORM_sec_offset: case DW_FORM_strp: case DW_FORM_line_strp: - assert(unit); - ref_addr_size = unit->GetFormParams().getDwarfOffsetByteSize(); - *offset_ptr += ref_addr_size; + *offset_ptr += 4; return true; // 4 byte values @@ -502,6 +507,8 @@ dw_addr_t DWARFFormValue::Address() const { &offset, index_size); } +bool UGLY_FLAG_FOR_AIX __attribute__((weak)) = false; + std::pair DWARFFormValue::ReferencedUnitAndOffset() const { uint64_t value = m_value.uval; @@ -514,6 +521,8 @@ DWARFFormValue::ReferencedUnitAndOffset() const { assert(m_unit); // Unit must be valid for DW_FORM_ref forms that are compile // unit relative or we will get this wrong value += m_unit->GetOffset(); + if (UGLY_FLAG_FOR_AIX) + value -= 8; if (!m_unit->ContainsDIEOffset(value)) { m_unit->GetSymbolFileDWARF().GetObjectFile()->GetModule()->ReportError( "DW_FORM_ref* DIE reference {0:x16} is outside of its CU", value); diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFUnit.cpp b/lldb/source/Plugins/SymbolFile/DWARF/DWARFUnit.cpp index f216ab13e8936..0016e65e31a72 100644 --- a/lldb/source/Plugins/SymbolFile/DWARF/DWARFUnit.cpp +++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFUnit.cpp @@ -998,6 +998,12 @@ const DWARFDebugAranges &DWARFUnit::GetFunctionAranges() { return *m_func_aranges_up; } +/* AIX-NOTE - TODO: Removed conflicting code due to merge conflicts + * Refer Patches: 27,28,29,30,35 and 76 + * and modify the code accordingly. */ + +bool UGLY_FLAG_FOR_AIX __attribute__((weak)) = false; + llvm::Expected DWARFUnit::extract(SymbolFileDWARF &dwarf, user_id_t uid, const DWARFDataExtractor &debug_info, @@ -1073,7 +1079,24 @@ const lldb_private::DWARFDataExtractor &DWARFUnit::GetData() const { : m_dwarf.GetDWARFContext().getOrLoadDebugInfoData(); } -uint32_t DWARFUnit::GetHeaderByteSize() const { return m_header.getSize(); } +uint32_t DWARFUnit::GetHeaderByteSize() const { + switch (m_header.getUnitType()) { + case llvm::dwarf::DW_UT_compile: + if (UGLY_FLAG_FOR_AIX) + return 11 + 4/*GetDWARFSizeOfOffset*/; + else + return GetVersion() < 5 ? 11 : 12; + case llvm::dwarf::DW_UT_partial: + return GetVersion() < 5 ? 11 : 12; + case llvm::dwarf::DW_UT_skeleton: + case llvm::dwarf::DW_UT_split_compile: + return 20; + case llvm::dwarf::DW_UT_type: + case llvm::dwarf::DW_UT_split_type: + return GetVersion() < 5 ? 23 : 24; + } + llvm_unreachable("invalid UnitType."); +} std::optional DWARFUnit::GetStringOffsetSectionItem(uint32_t index) const { diff --git a/lldb/source/Target/ABI.cpp b/lldb/source/Target/ABI.cpp index b86fef6bf03e8..b7616ab436128 100644 --- a/lldb/source/Target/ABI.cpp +++ b/lldb/source/Target/ABI.cpp @@ -208,6 +208,15 @@ bool ABI::PrepareTrivialCall(Thread &thread, lldb::addr_t sp, llvm_unreachable("Should never get here!"); } +bool ABI::PrepareTrivialCall(Thread &thread, lldb::addr_t sp, + lldb::addr_t functionAddress, + lldb::addr_t tocAddress, + lldb::addr_t returnAddress, + llvm::ArrayRef args) const { + // dummy prepare trivial call + llvm_unreachable("Should never get here!"); +} + bool ABI::GetFallbackRegisterLocation( const RegisterInfo *reg_info, UnwindPlan::Row::AbstractRegisterLocation &unwind_regloc) { diff --git a/lldb/source/Target/CMakeLists.txt b/lldb/source/Target/CMakeLists.txt index b7788e80eecac..86cb9f4a23cb2 100644 --- a/lldb/source/Target/CMakeLists.txt +++ b/lldb/source/Target/CMakeLists.txt @@ -1,3 +1,8 @@ +if (UNIX AND ${CMAKE_SYSTEM_NAME} MATCHES "AIX") + remove_definitions("-D_XOPEN_SOURCE=700") + add_definitions("-D_ALL_SOURCE") +endif() + lldb_tablegen(TargetProperties.inc -gen-lldb-property-defs SOURCE TargetProperties.td TARGET LLDBTargetPropertiesGen) diff --git a/lldb/source/Target/Process.cpp b/lldb/source/Target/Process.cpp index bba1230c79920..53eb73d065063 100644 --- a/lldb/source/Target/Process.cpp +++ b/lldb/source/Target/Process.cpp @@ -76,6 +76,10 @@ #include "lldb/Utility/State.h" #include "lldb/Utility/Timer.h" +#if defined(_AIX) +#include +#endif + using namespace lldb; using namespace lldb_private; using namespace std::chrono; @@ -6193,6 +6197,12 @@ Status Process::GetMemoryRegionInfo(lldb::addr_t load_addr, return error; } +#if defined(_AIX) +Status Process::GetLDXINFO(struct ld_xinfo *info_ptr) { + return DoGetLDXINFO(info_ptr); +} +#endif + Status Process::GetMemoryRegions(lldb_private::MemoryRegionInfos ®ion_list) { Status error; diff --git a/lldb/source/Target/RegisterContextUnwind.cpp b/lldb/source/Target/RegisterContextUnwind.cpp index 880300d0637fb..85016ed417d0c 100644 --- a/lldb/source/Target/RegisterContextUnwind.cpp +++ b/lldb/source/Target/RegisterContextUnwind.cpp @@ -40,6 +40,9 @@ #include #include +#ifdef _AIX +#include "Plugins/Process/Utility/lldb-ppc64le-register-enums.h" +#endif using namespace lldb; using namespace lldb_private; @@ -1479,6 +1482,10 @@ RegisterContextUnwind::GetAbstractRegisterLocation(uint32_t lldb_regnum, // Answer the question: Where did THIS frame save the CALLER frame ("previous" // frame)'s register value? +#ifdef _AIX +extern bool UGLY_HACK_NULL_TOPFRAME; +#endif + enum UnwindLLDB::RegisterSearchResult RegisterContextUnwind::SavedLocationForRegister( uint32_t lldb_regnum, @@ -1528,6 +1535,11 @@ RegisterContextUnwind::SavedLocationForRegister( regloc.type = UnwindLLDB::ConcreteRegisterLocation::eRegisterInLiveRegisterContext; regloc.location.register_number = regnum.GetAsKind(eRegisterKindLLDB); +#ifdef _AIX + if (UGLY_HACK_NULL_TOPFRAME && regloc.location.register_number == 0x20) { + regloc.location.register_number = 0x24; + } +#endif m_registers[regnum.GetAsKind(eRegisterKindLLDB)] = regloc; UnwindLogMsg("supplying caller's register %s (%d) from the live " "RegisterContext at frame 0", @@ -2360,6 +2372,40 @@ bool RegisterContextUnwind::ReadPC(addr_t &pc) { } } +#ifdef _AIX +bool RegisterContextUnwind::ReadLR(addr_t &lr) { + if (!IsValid()) + return false; + + bool above_trap_handler = false; + if (GetNextFrame().get() && GetNextFrame()->IsValid() && + GetNextFrame()->IsTrapHandlerFrame()) + above_trap_handler = true; + + if (ReadGPRValue(eRegisterKindLLDB, gpr_lr_ppc64le, lr)) { + // A lr value of 0 or 1 is impossible in the middle of the stack -- it + // indicates the end of a stack walk. + // On the currently executing frame (or such a frame interrupted + // asynchronously by sigtramp et al) this may occur if code has jumped + // through a NULL pointer -- we want to be able to unwind past that frame + // to help find the bug. + + ProcessSP process_sp (m_thread.GetProcess()); + if (process_sp) + { + ABI *abi = process_sp->GetABI().get(); + if (abi) + lr = abi->FixCodeAddress(lr); + } + + return !(m_all_registers_available == false && + above_trap_handler == false && (lr == 0 || lr == 1)); + } else { + return false; + } +} +#endif + void RegisterContextUnwind::UnwindLogMsg(const char *fmt, ...) { Log *log = GetLog(LLDBLog::Unwind); if (!log) diff --git a/lldb/source/Target/ThreadPlanCallFunction.cpp b/lldb/source/Target/ThreadPlanCallFunction.cpp index 218111d4faf60..1151da982d828 100644 --- a/lldb/source/Target/ThreadPlanCallFunction.cpp +++ b/lldb/source/Target/ThreadPlanCallFunction.cpp @@ -127,6 +127,40 @@ ThreadPlanCallFunction::ThreadPlanCallFunction( m_valid = true; } +ThreadPlanCallFunction::ThreadPlanCallFunction( + Thread &thread, const Address &function, const Address &toc, + const CompilerType &return_type, + llvm::ArrayRef args, const EvaluateExpressionOptions &options) + : ThreadPlan(ThreadPlan::eKindCallFunction, "Call function plan", thread, + eVoteNoOpinion, eVoteNoOpinion), + m_valid(false), m_stop_other_threads(options.GetStopOthers()), + m_unwind_on_error(options.DoesUnwindOnError()), + m_ignore_breakpoints(options.DoesIgnoreBreakpoints()), + m_debug_execution(options.GetDebug()), + m_trap_exceptions(options.GetTrapExceptions()), m_function_addr(function), + m_function_sp(0), m_takedown_done(false), + m_should_clear_objc_exception_bp(false), + m_should_clear_cxx_exception_bp(false), + m_stop_address(LLDB_INVALID_ADDRESS), m_return_type(return_type) { + lldb::addr_t start_load_addr = LLDB_INVALID_ADDRESS; + lldb::addr_t function_load_addr = LLDB_INVALID_ADDRESS; + lldb::addr_t toc_addr = LLDB_INVALID_ADDRESS; + ABI *abi = nullptr; + + if (!ConstructorSetup(thread, abi, start_load_addr, function_load_addr)) + return; + + toc_addr = toc.GetLoadAddress(&GetTarget()); + + if (!abi->PrepareTrivialCall(thread, m_function_sp, function_load_addr, + toc_addr, start_load_addr, args)) + return; + + ReportRegisterState("Function call was set up. Register state was:"); + + m_valid = true; +} + ThreadPlanCallFunction::ThreadPlanCallFunction( Thread &thread, const Address &function, const EvaluateExpressionOptions &options) diff --git a/lldb/source/Target/UnwindLLDB.cpp b/lldb/source/Target/UnwindLLDB.cpp index 4d3f23948b487..764bea5bf86c6 100644 --- a/lldb/source/Target/UnwindLLDB.cpp +++ b/lldb/source/Target/UnwindLLDB.cpp @@ -68,6 +68,10 @@ uint32_t UnwindLLDB::DoGetFrameCount() { return m_frames.size(); } +#ifdef _AIX +bool UGLY_HACK_NULL_TOPFRAME = false; +#endif + bool UnwindLLDB::AddFirstFrame() { if (m_frames.size() > 0) return true; @@ -91,6 +95,17 @@ bool UnwindLLDB::AddFirstFrame() { if (!reg_ctx_sp->ReadPC(first_cursor_sp->start_pc)) goto unwind_done; +#ifdef _AIX + lldb::addr_t lr; + if (!reg_ctx_sp->ReadLR(lr)) + goto unwind_done; + + if (first_cursor_sp->start_pc == 0) { + first_cursor_sp->start_pc = lr; + UGLY_HACK_NULL_TOPFRAME = true; + } +#endif + // Everything checks out, so release the auto pointer value and let the // cursor own it in its shared pointer first_cursor_sp->reg_ctx_lldb_sp = reg_ctx_sp; diff --git a/lldb/source/Utility/StringExtractorGDBRemote.cpp b/lldb/source/Utility/StringExtractorGDBRemote.cpp index c5755b2733605..53456ba5af2a7 100644 --- a/lldb/source/Utility/StringExtractorGDBRemote.cpp +++ b/lldb/source/Utility/StringExtractorGDBRemote.cpp @@ -227,6 +227,8 @@ StringExtractorGDBRemote::GetServerPacketType() const { return eServerPacketType_qLaunchGDBServer; if (PACKET_MATCHES("qLaunchSuccess")) return eServerPacketType_qLaunchSuccess; + if (PACKET_MATCHES("qLDXINFO")) + return eServerPacketType_qLDXINFO; break; case 'M': diff --git a/lldb/test/CMakeLists.txt b/lldb/test/CMakeLists.txt index 6449ac5a9247f..80a64882f3521 100644 --- a/lldb/test/CMakeLists.txt +++ b/lldb/test/CMakeLists.txt @@ -155,7 +155,7 @@ if(TARGET clang) add_lldb_test_dependency(clang) # TestFullLtoStepping depends on LTO, and only runs when the compiler is clang. - add_lldb_test_dependency(LTO) + #add_lldb_test_dependency(LTO) if (TARGET libcxx OR ("libcxx" IN_LIST LLVM_ENABLE_RUNTIMES)) set(LLDB_HAS_LIBCXX ON) diff --git a/lldb/tools/driver/Driver.cpp b/lldb/tools/driver/Driver.cpp index 16cc736441b59..a3c365a3cc42b 100644 --- a/lldb/tools/driver/Driver.cpp +++ b/lldb/tools/driver/Driver.cpp @@ -648,8 +648,7 @@ void Driver::UpdateWindowSize() { } void sigint_handler(int signo) { -#ifdef _WIN32 - // Restore handler as it is not persistent on Windows. +#if defined(_WIN32) || defined(_AIX) // Restore handler as it is not persistent on Windows signal(SIGINT, sigint_handler); #endif @@ -713,8 +712,11 @@ static void printHelp(LLDBOptTable &table, llvm::StringRef tool_name) { int main(int argc, char const *argv[]) { // Editline uses for example iswprint which is dependent on LC_CTYPE. + // FIXME: this caused unexpected SIGTRAP on AIX +#ifndef _AIX std::setlocale(LC_ALL, ""); std::setlocale(LC_CTYPE, ""); +#endif // Setup LLVM signal handlers and make sure we call llvm_shutdown() on // destruction. diff --git a/lldb/tools/lldb-dap/CMakeLists.txt b/lldb/tools/lldb-dap/CMakeLists.txt index 8bbb402fdf782..257c7b1af5971 100644 --- a/lldb/tools/lldb-dap/CMakeLists.txt +++ b/lldb/tools/lldb-dap/CMakeLists.txt @@ -1,3 +1,12 @@ +if (HAVE_LIBPTHREAD) + list(APPEND extra_libs pthread) +endif () + +if (UNIX AND ${CMAKE_SYSTEM_NAME} MATCHES "AIX") + add_definitions("-D_AIX") + add_definitions("-D_ALL_SOURCE") +endif() + # We need to include the llvm components we depend on manually, as liblldb does # not re-export those. set(LLVM_LINK_COMPONENTS Support) diff --git a/lldb/tools/lldb-server/CMakeLists.txt b/lldb/tools/lldb-server/CMakeLists.txt index 1d8dc72a3f872..e1768a4bafc94 100644 --- a/lldb/tools/lldb-server/CMakeLists.txt +++ b/lldb/tools/lldb-server/CMakeLists.txt @@ -62,6 +62,7 @@ add_lldb_tool(lldb-server lldbPluginInstructionMIPS lldbPluginInstructionMIPS64 lldbPluginInstructionRISCV + lldbPluginInstructionPPC64 ${LLDB_SYSTEM_LIBS} ) diff --git a/lldb/tools/lldb-server/SystemInitializerLLGS.cpp b/lldb/tools/lldb-server/SystemInitializerLLGS.cpp index 5b280d6cf5280..52c2eae0c9033 100644 --- a/lldb/tools/lldb-server/SystemInitializerLLGS.cpp +++ b/lldb/tools/lldb-server/SystemInitializerLLGS.cpp @@ -49,6 +49,10 @@ using HostObjectFile = ObjectFileELF; #include "Plugins/Instruction/MIPS/EmulateInstructionMIPS.h" #endif +#if defined(_AIX) +#include "Plugins/Instruction/PPC64/EmulateInstructionPPC64.h" +#endif + #if defined(__riscv) #define LLDB_TARGET_RISCV #include "Plugins/Instruction/RISCV/EmulateInstructionRISCV.h" @@ -78,6 +82,10 @@ llvm::Error SystemInitializerLLGS::Initialize() { EmulateInstructionRISCV::Initialize(); #endif +#if defined(_AIX) + EmulateInstructionPPC64::Initialize(); +#endif + return llvm::Error::success(); } @@ -100,5 +108,9 @@ void SystemInitializerLLGS::Terminate() { EmulateInstructionRISCV::Terminate(); #endif +#if defined(_AIX) + EmulateInstructionPPC64::Terminate(); +#endif + SystemInitializerCommon::Terminate(); } diff --git a/lldb/tools/lldb-server/lldb-gdbserver.cpp b/lldb/tools/lldb-server/lldb-gdbserver.cpp index e1dc15d83069f..544f196f58a26 100644 --- a/lldb/tools/lldb-server/lldb-gdbserver.cpp +++ b/lldb/tools/lldb-server/lldb-gdbserver.cpp @@ -45,6 +45,8 @@ #include "Plugins/Process/NetBSD/NativeProcessNetBSD.h" #elif defined(_WIN32) #include "Plugins/Process/Windows/Common/NativeProcessWindows.h" +#elif defined(_AIX) +#include "Plugins/Process/AIX/NativeProcessAIX.h" #endif #ifndef LLGS_PROGRAM_NAME @@ -70,6 +72,8 @@ typedef process_freebsd::NativeProcessFreeBSD::Manager NativeProcessManager; typedef process_netbsd::NativeProcessNetBSD::Manager NativeProcessManager; #elif defined(_WIN32) typedef NativeProcessWindows::Manager NativeProcessManager; +#elif defined(_AIX) +typedef process_aix::NativeProcessAIX::Manager NativeProcessManager; #else // Dummy implementation to make sure the code compiles class NativeProcessManager : public NativeProcessProtocol::Manager { diff --git a/lldb/unittests/Host/MainLoopTest.cpp b/lldb/unittests/Host/MainLoopTest.cpp index 502028ae1a343..d26d303331d6a 100644 --- a/lldb/unittests/Host/MainLoopTest.cpp +++ b/lldb/unittests/Host/MainLoopTest.cpp @@ -335,7 +335,7 @@ TEST_F(MainLoopTest, TimedCallbackShortensSleep) { EXPECT_FALSE(long_callback_called); } -#ifdef LLVM_ON_UNIX +#if defined(LLVM_ON_UNIX) && !defined(_AIX) TEST_F(MainLoopTest, DetectsEOF) { PseudoTerminal term; diff --git a/lldb/unittests/Host/PipeTest.cpp b/lldb/unittests/Host/PipeTest.cpp index 4ee3b53cc552b..1577508341204 100644 --- a/lldb/unittests/Host/PipeTest.cpp +++ b/lldb/unittests/Host/PipeTest.cpp @@ -54,7 +54,8 @@ TEST_F(PipeTest, OpenAsReader) { } #endif -// Tests flaky on Windows +#if !defined(_AIX) +// This test is flaky on Windows on Arm. #ifndef _WIN32 TEST_F(PipeTest, WriteWithTimeout) { Pipe pipe; @@ -194,4 +195,5 @@ TEST_F(PipeTest, ReadWithTimeout) { llvm::HasValue(hello_world.size())); EXPECT_EQ(llvm::StringRef(buf, hello_world.size()), hello_world); } +#endif #endif /*ifndef _WIN32*/ diff --git a/lldb/unittests/Host/posix/TerminalTest.cpp b/lldb/unittests/Host/posix/TerminalTest.cpp index 5187a0c20a68b..64e6be64db80c 100644 --- a/lldb/unittests/Host/posix/TerminalTest.cpp +++ b/lldb/unittests/Host/posix/TerminalTest.cpp @@ -94,15 +94,19 @@ TEST_F(TerminalTest, SetRaw) { TEST_F(TerminalTest, SetBaudRate) { struct termios terminfo; +#if (defined(_AIX) && defined(B38400)) || !defined(_AIX) ASSERT_THAT_ERROR(m_term.SetBaudRate(38400), llvm::Succeeded()); ASSERT_EQ(tcgetattr(m_fd, &terminfo), 0); EXPECT_EQ(cfgetispeed(&terminfo), static_cast(B38400)); EXPECT_EQ(cfgetospeed(&terminfo), static_cast(B38400)); +#endif +#if (defined(_AIX) && defined(B115200)) || !defined(_AIX) ASSERT_THAT_ERROR(m_term.SetBaudRate(115200), llvm::Succeeded()); ASSERT_EQ(tcgetattr(m_fd, &terminfo), 0); EXPECT_EQ(cfgetispeed(&terminfo), static_cast(B115200)); EXPECT_EQ(cfgetospeed(&terminfo), static_cast(B115200)); +#endif // uncommon value #if defined(B153600) diff --git a/llvm/include/llvm/Object/XCOFFObjectFile.h b/llvm/include/llvm/Object/XCOFFObjectFile.h index da3538ecb3024..6a583d1641fe4 100644 --- a/llvm/include/llvm/Object/XCOFFObjectFile.h +++ b/llvm/include/llvm/Object/XCOFFObjectFile.h @@ -549,7 +549,6 @@ class LLVM_ABI XCOFFObjectFile : public ObjectFile { template const T *sectionHeaderTable() const; size_t getFileHeaderSize() const; - size_t getSectionHeaderSize() const; const XCOFFSectionHeader32 *toSection32(DataRefImpl Ref) const; const XCOFFSectionHeader64 *toSection64(DataRefImpl Ref) const; @@ -585,6 +584,9 @@ class LLVM_ABI XCOFFObjectFile : public ObjectFile { void checkSectionAddress(uintptr_t Addr, uintptr_t TableAddr) const; public: + size_t getSectionHeaderSize() const; + Expected getLoaderSectionAddress() const; + static constexpr uint64_t InvalidRelocOffset = std::numeric_limits::max(); diff --git a/llvm/lib/DebugInfo/DWARF/DWARFUnit.cpp b/llvm/lib/DebugInfo/DWARF/DWARFUnit.cpp index ef59c82fc6a01..f42e427d62e00 100644 --- a/llvm/lib/DebugInfo/DWARF/DWARFUnit.cpp +++ b/llvm/lib/DebugInfo/DWARF/DWARFUnit.cpp @@ -251,10 +251,16 @@ Expected DWARFUnit::getStringOffsetSectionItem(uint32_t Index) const { return DA.getRelocatedValue(ItemSize, &Offset); } +bool UGLY_FLAG_FOR_AIX __attribute__((weak)) = false; + Error DWARFUnitHeader::extract(DWARFContext &Context, const DWARFDataExtractor &debug_info, uint64_t *offset_ptr, DWARFSectionKind SectionKind) { + if (UGLY_FLAG_FOR_AIX) { + // FIXME: hack to get version + *offset_ptr += 8; + } Offset = *offset_ptr; Error Err = Error::success(); IndexEntry = nullptr; @@ -267,8 +273,13 @@ Error DWARFUnitHeader::extract(DWARFContext &Context, AbbrOffset = debug_info.getRelocatedValue( FormParams.getDwarfOffsetByteSize(), offset_ptr, nullptr, &Err); } else { - AbbrOffset = debug_info.getRelocatedValue( - FormParams.getDwarfOffsetByteSize(), offset_ptr, nullptr, &Err); + if (UGLY_FLAG_FOR_AIX) { + AbbrOffset = debug_info.getRelocatedValue( + 8, offset_ptr, nullptr, &Err); + } else { + AbbrOffset = debug_info.getRelocatedValue( + FormParams.getDwarfOffsetByteSize(), offset_ptr, nullptr, &Err); + } FormParams.AddrSize = debug_info.getU8(offset_ptr, &Err); // Fake a unit type based on the section type. This isn't perfect, // but distinguishing compile and type units is generally enough.