diff --git a/lldb/include/lldb/Host/common/NativeProcessProtocol.h b/lldb/include/lldb/Host/common/NativeProcessProtocol.h --- a/lldb/include/lldb/Host/common/NativeProcessProtocol.h +++ b/lldb/include/lldb/Host/common/NativeProcessProtocol.h @@ -17,6 +17,7 @@ #include "lldb/Utility/ArchSpec.h" #include "lldb/Utility/Status.h" #include "lldb/Utility/TraceOptions.h" +#include "lldb/Utility/UnimplementedError.h" #include "lldb/lldb-private-forward.h" #include "lldb/lldb-types.h" #include "llvm/ADT/ArrayRef.h" @@ -112,10 +113,14 @@ virtual Status WriteMemory(lldb::addr_t addr, const void *buf, size_t size, size_t &bytes_written) = 0; - virtual Status AllocateMemory(size_t size, uint32_t permissions, - lldb::addr_t &addr) = 0; + virtual llvm::Expected AllocateMemory(size_t size, + uint32_t permissions) { + return llvm::make_error(); + } - virtual Status DeallocateMemory(lldb::addr_t addr) = 0; + virtual llvm::Error DeallocateMemory(lldb::addr_t addr) { + return llvm::make_error(); + } virtual lldb::addr_t GetSharedLibraryInfoAddress() = 0; diff --git a/lldb/source/Plugins/Process/FreeBSDRemote/NativeProcessFreeBSD.h b/lldb/source/Plugins/Process/FreeBSDRemote/NativeProcessFreeBSD.h --- a/lldb/source/Plugins/Process/FreeBSDRemote/NativeProcessFreeBSD.h +++ b/lldb/source/Plugins/Process/FreeBSDRemote/NativeProcessFreeBSD.h @@ -60,11 +60,6 @@ Status WriteMemory(lldb::addr_t addr, const void *buf, size_t size, size_t &bytes_written) override; - Status AllocateMemory(size_t size, uint32_t permissions, - lldb::addr_t &addr) override; - - Status DeallocateMemory(lldb::addr_t addr) override; - lldb::addr_t GetSharedLibraryInfoAddress() override; size_t UpdateThreads() override; diff --git a/lldb/source/Plugins/Process/FreeBSDRemote/NativeProcessFreeBSD.cpp b/lldb/source/Plugins/Process/FreeBSDRemote/NativeProcessFreeBSD.cpp --- a/lldb/source/Plugins/Process/FreeBSDRemote/NativeProcessFreeBSD.cpp +++ b/lldb/source/Plugins/Process/FreeBSDRemote/NativeProcessFreeBSD.cpp @@ -543,15 +543,6 @@ return Status(); } -Status NativeProcessFreeBSD::AllocateMemory(size_t size, uint32_t permissions, - lldb::addr_t &addr) { - return Status("Unimplemented"); -} - -Status NativeProcessFreeBSD::DeallocateMemory(lldb::addr_t addr) { - return Status("Unimplemented"); -} - lldb::addr_t NativeProcessFreeBSD::GetSharedLibraryInfoAddress() { // punt on this for now return LLDB_INVALID_ADDRESS; diff --git a/lldb/source/Plugins/Process/Linux/NativeProcessLinux.h b/lldb/source/Plugins/Process/Linux/NativeProcessLinux.h --- a/lldb/source/Plugins/Process/Linux/NativeProcessLinux.h +++ b/lldb/source/Plugins/Process/Linux/NativeProcessLinux.h @@ -71,10 +71,10 @@ Status WriteMemory(lldb::addr_t addr, const void *buf, size_t size, size_t &bytes_written) override; - Status AllocateMemory(size_t size, uint32_t permissions, - lldb::addr_t &addr) override; + llvm::Expected AllocateMemory(size_t size, + uint32_t permissions) override; - Status DeallocateMemory(lldb::addr_t addr) override; + llvm::Error DeallocateMemory(lldb::addr_t addr) override; size_t UpdateThreads() override; @@ -94,6 +94,7 @@ lldb::addr_t &load_addr) override; NativeThreadLinux *GetThreadByID(lldb::tid_t id); + NativeThreadLinux *GetCurrentThread(); llvm::ErrorOr> GetAuxvData() const override { @@ -127,6 +128,8 @@ llvm::Expected> GetSoftwareBreakpointTrapOpcode(size_t size_hint) override; + llvm::Expected Syscall(llvm::ArrayRef args); + private: MainLoop::SignalHandleUP m_sigchld_handle; ArchSpec m_arch; @@ -140,6 +143,9 @@ // the relevan breakpoint std::map m_threads_stepping_with_breakpoint; + /// Inferior memory (allocated by us) and its size. + llvm::DenseMap m_allocated_memory; + // Private Instance Methods NativeProcessLinux(::pid_t pid, int terminal_fd, NativeDelegate &delegate, const ArchSpec &arch, MainLoop &mainloop, diff --git a/lldb/source/Plugins/Process/Linux/NativeProcessLinux.cpp b/lldb/source/Plugins/Process/Linux/NativeProcessLinux.cpp --- a/lldb/source/Plugins/Process/Linux/NativeProcessLinux.cpp +++ b/lldb/source/Plugins/Process/Linux/NativeProcessLinux.cpp @@ -19,6 +19,10 @@ #include #include +#include "NativeThreadLinux.h" +#include "Plugins/Process/POSIX/ProcessPOSIXLog.h" +#include "Plugins/Process/Utility/LinuxProcMaps.h" +#include "Procfs.h" #include "lldb/Core/EmulateInstruction.h" #include "lldb/Core/ModuleSpec.h" #include "lldb/Host/Host.h" @@ -38,15 +42,11 @@ #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/FileSystem.h" #include "llvm/Support/Threading.h" -#include "NativeThreadLinux.h" -#include "Plugins/Process/POSIX/ProcessPOSIXLog.h" -#include "Plugins/Process/Utility/LinuxProcMaps.h" -#include "Procfs.h" - #include #include #include @@ -1347,43 +1347,134 @@ m_mem_region_cache.clear(); } -Status NativeProcessLinux::AllocateMemory(size_t size, uint32_t permissions, - lldb::addr_t &addr) { -// FIXME implementing this requires the equivalent of -// InferiorCallPOSIX::InferiorCallMmap, which depends on functional ThreadPlans -// working with Native*Protocol. -#if 1 - return Status("not implemented yet"); -#else - addr = LLDB_INVALID_ADDRESS; - - unsigned prot = 0; - if (permissions & lldb::ePermissionsReadable) - prot |= eMmapProtRead; - if (permissions & lldb::ePermissionsWritable) - prot |= eMmapProtWrite; - if (permissions & lldb::ePermissionsExecutable) - prot |= eMmapProtExec; - - // TODO implement this directly in NativeProcessLinux - // (and lift to NativeProcessPOSIX if/when that class is refactored out). - if (InferiorCallMmap(this, addr, 0, size, prot, - eMmapFlagsAnon | eMmapFlagsPrivate, -1, 0)) { - m_addr_to_mmap_size[addr] = size; - return Status(); - } else { - addr = LLDB_INVALID_ADDRESS; - return Status("unable to allocate %" PRIu64 - " bytes of memory with permissions %s", - size, GetPermissionsAsCString(permissions)); +llvm::Expected +NativeProcessLinux::Syscall(llvm::ArrayRef args) { + PopulateMemoryRegionCache(); + auto region_it = llvm::find_if(m_mem_region_cache, [](const auto &pair) { + return pair.first.GetExecutable() == 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(); + + NativeThreadLinux &thread = *GetThreadByID(GetID()); + assert(thread.GetState() == eStateStopped); + NativeRegisterContextLinux ®_ctx = thread.GetRegisterContext(); + + NativeRegisterContextLinux::SyscallData syscall_data = + *reg_ctx.GetSyscallData(); + + DataBufferSP 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); } -#endif + + 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); + + int status; + ::pid_t wait_pid = llvm::sys::RetryAfterSignal(-1, ::waitpid, thread.GetID(), + &status, __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; } -Status NativeProcessLinux::DeallocateMemory(lldb::addr_t addr) { - // FIXME see comments in AllocateMemory - required lower-level - // bits not in place yet (ThreadPlans) - return Status("not implemented"); +llvm::Expected +NativeProcessLinux::AllocateMemory(size_t size, uint32_t permissions) { + + llvm::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 NativeProcessLinux::DeallocateMemory(lldb::addr_t addr) { + llvm::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(); } size_t NativeProcessLinux::UpdateThreads() { @@ -1652,6 +1743,11 @@ NativeProcessProtocol::GetThreadByID(tid)); } +NativeThreadLinux *NativeProcessLinux::GetCurrentThread() { + return static_cast( + NativeProcessProtocol::GetCurrentThread()); +} + Status NativeProcessLinux::ResumeThread(NativeThreadLinux &thread, lldb::StateType state, int signo) { Log *const log = ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_THREAD); diff --git a/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux.h b/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux.h --- a/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux.h +++ b/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux.h @@ -31,6 +31,32 @@ // 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 llvm::Optional GetSyscallData() { return llvm::None; } + + 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 llvm::Optional GetMmapData() { return llvm::None; } + protected: lldb::ByteOrder GetByteOrder() const; diff --git a/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_x86_64.h b/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_x86_64.h --- a/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_x86_64.h +++ b/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_x86_64.h @@ -64,6 +64,10 @@ uint32_t NumSupportedHardwareWatchpoints() override; + llvm::Optional GetSyscallData() override; + + llvm::Optional GetMmapData() override; + protected: void *GetGPRBuffer() override { return &m_gpr_x86_64; } diff --git a/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_x86_64.cpp b/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_x86_64.cpp --- a/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_x86_64.cpp +++ b/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_x86_64.cpp @@ -1220,4 +1220,38 @@ (IsMPX(reg_index) ? 128 : 0); } +llvm::Optional +NativeRegisterContextLinux_x86_64::GetSyscallData() { + switch (GetRegisterInfoInterface().GetTargetArchitecture().GetMachine()) { + case llvm::Triple::x86: { + static const uint8_t Int80[] = {0xcd, 0x80}; + static const uint32_t Args[] = {lldb_eax_i386, lldb_ebx_i386, lldb_ecx_i386, + lldb_edx_i386, lldb_esi_i386, lldb_edi_i386, + lldb_ebp_i386}; + return SyscallData{Int80, Args, lldb_eax_i386}; + } + case llvm::Triple::x86_64: { + static const uint8_t Syscall[] = {0x0f, 0x05}; + static const uint32_t Args[] = { + lldb_rax_x86_64, lldb_rdi_x86_64, lldb_rsi_x86_64, lldb_rdx_x86_64, + lldb_r10_x86_64, lldb_r8_x86_64, lldb_r9_x86_64}; + return SyscallData{Syscall, Args, lldb_rax_x86_64}; + } + default: + llvm_unreachable("Unhandled architecture!"); + } +} + +llvm::Optional +NativeRegisterContextLinux_x86_64::GetMmapData() { + switch (GetRegisterInfoInterface().GetTargetArchitecture().GetMachine()) { + case llvm::Triple::x86: + return MmapData{192, 91}; + case llvm::Triple::x86_64: + return MmapData{9, 11}; + default: + llvm_unreachable("Unhandled architecture!"); + } +} + #endif // defined(__i386__) || defined(__x86_64__) diff --git a/lldb/source/Plugins/Process/NetBSD/NativeProcessNetBSD.h b/lldb/source/Plugins/Process/NetBSD/NativeProcessNetBSD.h --- a/lldb/source/Plugins/Process/NetBSD/NativeProcessNetBSD.h +++ b/lldb/source/Plugins/Process/NetBSD/NativeProcessNetBSD.h @@ -60,11 +60,6 @@ Status WriteMemory(lldb::addr_t addr, const void *buf, size_t size, size_t &bytes_written) override; - Status AllocateMemory(size_t size, uint32_t permissions, - lldb::addr_t &addr) override; - - Status DeallocateMemory(lldb::addr_t addr) override; - lldb::addr_t GetSharedLibraryInfoAddress() override; size_t UpdateThreads() override; diff --git a/lldb/source/Plugins/Process/NetBSD/NativeProcessNetBSD.cpp b/lldb/source/Plugins/Process/NetBSD/NativeProcessNetBSD.cpp --- a/lldb/source/Plugins/Process/NetBSD/NativeProcessNetBSD.cpp +++ b/lldb/source/Plugins/Process/NetBSD/NativeProcessNetBSD.cpp @@ -684,15 +684,6 @@ return Status(); } -Status NativeProcessNetBSD::AllocateMemory(size_t size, uint32_t permissions, - lldb::addr_t &addr) { - return Status("Unimplemented"); -} - -Status NativeProcessNetBSD::DeallocateMemory(lldb::addr_t addr) { - return Status("Unimplemented"); -} - lldb::addr_t NativeProcessNetBSD::GetSharedLibraryInfoAddress() { // punt on this for now return LLDB_INVALID_ADDRESS; diff --git a/lldb/source/Plugins/Process/Windows/Common/NativeProcessWindows.h b/lldb/source/Plugins/Process/Windows/Common/NativeProcessWindows.h --- a/lldb/source/Plugins/Process/Windows/Common/NativeProcessWindows.h +++ b/lldb/source/Plugins/Process/Windows/Common/NativeProcessWindows.h @@ -65,10 +65,10 @@ Status WriteMemory(lldb::addr_t addr, const void *buf, size_t size, size_t &bytes_written) override; - Status AllocateMemory(size_t size, uint32_t permissions, - lldb::addr_t &addr) override; + llvm::Expected AllocateMemory(size_t size, + uint32_t permissions) override; - Status DeallocateMemory(lldb::addr_t addr) override; + llvm::Error DeallocateMemory(lldb::addr_t addr) override; lldb::addr_t GetSharedLibraryInfoAddress() override; diff --git a/lldb/source/Plugins/Process/Windows/Common/NativeProcessWindows.cpp b/lldb/source/Plugins/Process/Windows/Common/NativeProcessWindows.cpp --- a/lldb/source/Plugins/Process/Windows/Common/NativeProcessWindows.cpp +++ b/lldb/source/Plugins/Process/Windows/Common/NativeProcessWindows.cpp @@ -217,13 +217,17 @@ return ProcessDebugger::WriteMemory(addr, buf, size, bytes_written); } -Status NativeProcessWindows::AllocateMemory(size_t size, uint32_t permissions, - lldb::addr_t &addr) { - return ProcessDebugger::AllocateMemory(size, permissions, addr); +llvm::Expected +NativeProcessWindows::AllocateMemory(size_t size, uint32_t permissions) { + lldb::addr_t addr; + Status ST = ProcessDebugger::AllocateMemory(size, permissions, addr); + if (ST.Success()) + return addr; + return ST.ToError(); } -Status NativeProcessWindows::DeallocateMemory(lldb::addr_t addr) { - return ProcessDebugger::DeallocateMemory(addr); +llvm::Error NativeProcessWindows::DeallocateMemory(lldb::addr_t addr) { + return ProcessDebugger::DeallocateMemory(addr).ToError(); } lldb::addr_t NativeProcessWindows::GetSharedLibraryInfoAddress() { return 0; } diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.h b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.h --- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.h +++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.h @@ -138,6 +138,8 @@ PacketResult Handle_memory_read(StringExtractorGDBRemote &packet); PacketResult Handle_M(StringExtractorGDBRemote &packet); + PacketResult Handle__M(StringExtractorGDBRemote &packet); + PacketResult Handle__m(StringExtractorGDBRemote &packet); PacketResult Handle_qMemoryRegionInfoSupported(StringExtractorGDBRemote &packet); diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp --- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp +++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp @@ -93,6 +93,10 @@ &GDBRemoteCommunicationServerLLGS::Handle_memory_read); RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_M, &GDBRemoteCommunicationServerLLGS::Handle_M); + RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType__M, + &GDBRemoteCommunicationServerLLGS::Handle__M); + RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType__m, + &GDBRemoteCommunicationServerLLGS::Handle__m); RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_p, &GDBRemoteCommunicationServerLLGS::Handle_p); RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_P, @@ -2321,6 +2325,84 @@ return SendPacketNoLock(response.GetString()); } +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerLLGS::Handle__M(StringExtractorGDBRemote &packet) { + Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS)); + + if (!m_debugged_process_up || + (m_debugged_process_up->GetID() == LLDB_INVALID_PROCESS_ID)) { + LLDB_LOGF( + log, + "GDBRemoteCommunicationServerLLGS::%s failed, no process available", + __FUNCTION__); + return SendErrorResponse(0x15); + } + + // Parse out the memory address. + packet.SetFilePos(strlen("_M")); + if (packet.GetBytesLeft() < 1) + return SendIllFormedResponse(packet, "Too short _M packet"); + + const lldb::addr_t size = packet.GetHexMaxU64(false, LLDB_INVALID_ADDRESS); + if (size == LLDB_INVALID_ADDRESS) + return SendIllFormedResponse(packet, "Address not valid"); + if (packet.GetChar() != ',') + return SendIllFormedResponse(packet, "Bad packet"); + Permissions perms = {}; + while (packet.GetBytesLeft() > 0) { + switch (packet.GetChar()) { + case 'r': + perms |= ePermissionsReadable; + break; + case 'w': + perms |= ePermissionsWritable; + break; + case 'x': + perms |= ePermissionsExecutable; + break; + default: + return SendIllFormedResponse(packet, "Bad permissions"); + } + } + + llvm::Expected addr = + m_debugged_process_up->AllocateMemory(size, perms); + if (!addr) + return SendErrorResponse(addr.takeError()); + + StreamGDBRemote response; + response.PutHex64(*addr); + return SendPacketNoLock(response.GetString()); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerLLGS::Handle__m(StringExtractorGDBRemote &packet) { + Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS)); + + if (!m_debugged_process_up || + (m_debugged_process_up->GetID() == LLDB_INVALID_PROCESS_ID)) { + LLDB_LOGF( + log, + "GDBRemoteCommunicationServerLLGS::%s failed, no process available", + __FUNCTION__); + return SendErrorResponse(0x15); + } + + // Parse out the memory address. + packet.SetFilePos(strlen("_m")); + if (packet.GetBytesLeft() < 1) + return SendIllFormedResponse(packet, "Too short m packet"); + + const lldb::addr_t addr = packet.GetHexMaxU64(false, LLDB_INVALID_ADDRESS); + if (addr == LLDB_INVALID_ADDRESS) + return SendIllFormedResponse(packet, "Address not valid"); + + if (llvm::Error Err = m_debugged_process_up->DeallocateMemory(addr)) + return SendErrorResponse(std::move(Err)); + + return SendOKResponse(); +} + GDBRemoteCommunication::PacketResult GDBRemoteCommunicationServerLLGS::Handle_M(StringExtractorGDBRemote &packet) { Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS)); diff --git a/lldb/test/API/lang/c/stepping/TestStepAndBreakpoints.py b/lldb/test/API/lang/c/stepping/TestStepAndBreakpoints.py --- a/lldb/test/API/lang/c/stepping/TestStepAndBreakpoints.py +++ b/lldb/test/API/lang/c/stepping/TestStepAndBreakpoints.py @@ -20,7 +20,7 @@ @add_test_categories(['pyapi', 'basic_process']) @expectedFailureAll(oslist=['freebsd'], bugnumber='llvm.org/pr17932') - @expectedFailureAll(oslist=["linux"], bugnumber="llvm.org/pr14437") + @expectedFailureAll(oslist=["linux"], archs=no_match(["i386", "x86_64"])) @expectedFailureAll(oslist=["windows"], bugnumber="llvm.org/pr24777") @expectedFailureNetBSD def test_and_python_api(self): diff --git a/lldb/test/API/tools/lldb-server/memory-allocation/Makefile b/lldb/test/API/tools/lldb-server/memory-allocation/Makefile new file mode 100644 --- /dev/null +++ b/lldb/test/API/tools/lldb-server/memory-allocation/Makefile @@ -0,0 +1,3 @@ +C_SOURCES := main.c + +include Makefile.rules diff --git a/lldb/test/API/tools/lldb-server/memory-allocation/TestGdbRemoteMemoryAllocation.py b/lldb/test/API/tools/lldb-server/memory-allocation/TestGdbRemoteMemoryAllocation.py new file mode 100644 --- /dev/null +++ b/lldb/test/API/tools/lldb-server/memory-allocation/TestGdbRemoteMemoryAllocation.py @@ -0,0 +1,101 @@ + +import gdbremote_testcase +from lldbsuite.test.decorators import * +from lldbsuite.test.lldbtest import * +from lldbsuite.test import lldbutil + + +supported_linux_archs = ["x86_64", "i386"] +supported_oses = ["linux"] + +class TestGdbRemoteMemoryAllocation(gdbremote_testcase.GdbRemoteTestCaseBase): + + mydir = TestBase.compute_mydir(__file__) + + def allocate(self, size, permissions): + self.test_sequence.add_log_lines(["read packet: $_M{:x},{}#00".format(size, permissions), + {"direction": "send", + "regex": + r"^\$([0-9a-f]+)#[0-9a-fA-F]{2}$", + "capture": { + 1: "addr"}}, + ], + True) + context = self.expect_gdbremote_sequence() + self.assertIsNotNone(context) + + addr = int(context.get("addr"), 16) + self.test_sequence.add_log_lines(["read packet: $qMemoryRegionInfo:{:x}#00".format(addr), + {"direction": "send", + "regex": + r"^\$start:([0-9a-fA-F]+);size:([0-9a-fA-F]+);permissions:([rwx]*);.*#[0-9a-fA-F]{2}$", + "capture": { + 1: "addr", + 2: "size", + 3: "permissions"}}, + "read packet: $_m{:x}#00".format(addr), + "send packet: $OK#00", + ], + True) + context = self.expect_gdbremote_sequence() + self.assertIsNotNone(context) + + self.assertEqual(addr, int(context.get("addr"), 16)) + self.assertLessEqual(size, int(context.get("size"), 16)) + self.assertEqual(permissions, context.get("permissions")) + + @skipIf(oslist=no_match(supported_oses)) + @skipIf(oslist=["linux"], archs=no_match(supported_linux_archs)) + @llgs_test + def test_supported(self): + """Make sure (de)allocation works on platforms where it's supposed to + work""" + self.init_llgs_test() + self.build() + self.set_inferior_startup_launch() + procs = self.prep_debug_monitor_and_inferior() + + self.allocate(0x1000, "r") + self.allocate(0x2000, "rw") + self.allocate(0x100, "rx") + self.allocate(0x1100, "rwx") + + @skipIf(oslist=["linux"], archs=supported_linux_archs) + @llgs_test + def test_unsupported(self): + """Make sure we get an "unsupported" error on platforms where the + feature is not implemented.""" + + self.init_llgs_test() + self.build() + self.set_inferior_startup_launch() + procs = self.prep_debug_monitor_and_inferior() + + self.test_sequence.add_log_lines(["read packet: $_M1000,rw#00", + "send packet: $#00", + ], + True) + self.expect_gdbremote_sequence() + + @llgs_test + def test_bad_packet(self): + """Make sure we get a proper error for malformed packets.""" + + self.init_llgs_test() + self.build() + self.set_inferior_startup_launch() + procs = self.prep_debug_monitor_and_inferior() + + def e(): + return {"direction": "send", + "regex": + r"^\$E([0-9a-fA-F]+){2}#[0-9a-fA-F]{2}$"} + + self.test_sequence.add_log_lines([ + "read packet: $_M#00", e(), + "read packet: $_M1x#00", e(), + "read packet: $_M1:#00", e(), + "read packet: $_M1,q#00", e(), + "read packet: $_m#00", e(), + ], True) + self.expect_gdbremote_sequence() diff --git a/lldb/test/API/tools/lldb-server/memory-allocation/main.c b/lldb/test/API/tools/lldb-server/memory-allocation/main.c new file mode 100644 --- /dev/null +++ b/lldb/test/API/tools/lldb-server/memory-allocation/main.c @@ -0,0 +1 @@ +int main() { return 0; } diff --git a/lldb/test/Shell/Expr/nodefaultlib.cpp b/lldb/test/Shell/Expr/nodefaultlib.cpp new file mode 100644 --- /dev/null +++ b/lldb/test/Shell/Expr/nodefaultlib.cpp @@ -0,0 +1,16 @@ +// Test that we're able to evaluate expressions in inferiors without the +// standard library (and mmap-like functions in particular). + +// REQUIRES: native +// XFAIL: system-linux && !(target-x86 || target-x86_64) +// XFAIL: system-netbsd || system-freebsd + +// RUN: %build %s --nodefaultlib -o %t +// RUN: %lldb %t -o "b main" -o run -o "p call_me(5, 6)" -o exit \ +// RUN: | FileCheck %s + +// CHECK: (int) $0 = 30 + +int call_me(int x, long y) { return x * y; } + +int main() { return call_me(4, 5); } diff --git a/lldb/unittests/TestingSupport/Host/NativeProcessTestUtils.h b/lldb/unittests/TestingSupport/Host/NativeProcessTestUtils.h --- a/lldb/unittests/TestingSupport/Host/NativeProcessTestUtils.h +++ b/lldb/unittests/TestingSupport/Host/NativeProcessTestUtils.h @@ -41,9 +41,6 @@ MOCK_METHOD0(Detach, Status()); MOCK_METHOD1(Signal, Status(int Signo)); MOCK_METHOD0(Kill, Status()); - MOCK_METHOD3(AllocateMemory, - Status(size_t Size, uint32_t Permissions, addr_t &Addr)); - MOCK_METHOD1(DeallocateMemory, Status(addr_t Addr)); MOCK_METHOD0(GetSharedLibraryInfoAddress, addr_t()); MOCK_METHOD0(UpdateThreads, size_t()); MOCK_CONST_METHOD0(GetAuxvData, @@ -147,4 +144,4 @@ }; } // namespace lldb_private -#endif \ No newline at end of file +#endif