[Lldb-commits] [lldb] 2c4226f - [lldb-server][linux] Add ability to allocate memory

Pavel Labath via lldb-commits lldb-commits at lists.llvm.org
Wed Oct 14 06:02:29 PDT 2020


Author: Pavel Labath
Date: 2020-10-14T15:02:09+02:00
New Revision: 2c4226f8ac2c925d7e1d59d1de1660cd1dd63f31

URL: https://github.com/llvm/llvm-project/commit/2c4226f8ac2c925d7e1d59d1de1660cd1dd63f31
DIFF: https://github.com/llvm/llvm-project/commit/2c4226f8ac2c925d7e1d59d1de1660cd1dd63f31.diff

LOG: [lldb-server][linux] Add ability to allocate memory

This patch adds support for the _M and _m gdb-remote packets, which
(de)allocate memory in the inferior. This works by "injecting" a
m(un)map syscall into the inferior. This consists of:
- finding an executable page of memory
- writing the syscall opcode to it
- setting up registers according to the os syscall convention
- single stepping over the syscall

The advantage of this approach over calling the mmap function is that
this works even in case the mmap function is buggy or unavailable. The
disadvantage is it is more platform-dependent, which is why this patch
only works on X86 (_32 and _64) right now. Adding support for other
linux architectures should be easy and consist of defining the
appropriate syscall constants. Adding support for other OSes depends on
the its ability to do a similar trick.

Differential Revision: https://reviews.llvm.org/D89124

Added: 
    lldb/test/API/tools/lldb-server/memory-allocation/Makefile
    lldb/test/API/tools/lldb-server/memory-allocation/TestGdbRemoteMemoryAllocation.py
    lldb/test/API/tools/lldb-server/memory-allocation/main.c
    lldb/test/Shell/Expr/nodefaultlib.cpp

Modified: 
    lldb/include/lldb/Host/common/NativeProcessProtocol.h
    lldb/source/Plugins/Process/FreeBSDRemote/NativeProcessFreeBSD.cpp
    lldb/source/Plugins/Process/FreeBSDRemote/NativeProcessFreeBSD.h
    lldb/source/Plugins/Process/Linux/NativeProcessLinux.cpp
    lldb/source/Plugins/Process/Linux/NativeProcessLinux.h
    lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux.h
    lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_x86_64.cpp
    lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_x86_64.h
    lldb/source/Plugins/Process/NetBSD/NativeProcessNetBSD.cpp
    lldb/source/Plugins/Process/NetBSD/NativeProcessNetBSD.h
    lldb/source/Plugins/Process/Windows/Common/NativeProcessWindows.cpp
    lldb/source/Plugins/Process/Windows/Common/NativeProcessWindows.h
    lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp
    lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.h
    lldb/test/API/lang/c/stepping/TestStepAndBreakpoints.py
    lldb/unittests/TestingSupport/Host/NativeProcessTestUtils.h

Removed: 
    


################################################################################
diff  --git a/lldb/include/lldb/Host/common/NativeProcessProtocol.h b/lldb/include/lldb/Host/common/NativeProcessProtocol.h
index 2faab6f587cd..8bdad5965a6c 100644
--- 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 @@ class NativeProcessProtocol {
   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<lldb::addr_t> AllocateMemory(size_t size,
+                                                      uint32_t permissions) {
+    return llvm::make_error<UnimplementedError>();
+  }
 
-  virtual Status DeallocateMemory(lldb::addr_t addr) = 0;
+  virtual llvm::Error DeallocateMemory(lldb::addr_t addr) {
+    return llvm::make_error<UnimplementedError>();
+  }
 
   virtual lldb::addr_t GetSharedLibraryInfoAddress() = 0;
 

diff  --git a/lldb/source/Plugins/Process/FreeBSDRemote/NativeProcessFreeBSD.cpp b/lldb/source/Plugins/Process/FreeBSDRemote/NativeProcessFreeBSD.cpp
index 4a50d8e4b960..e99d38f57eea 100644
--- a/lldb/source/Plugins/Process/FreeBSDRemote/NativeProcessFreeBSD.cpp
+++ b/lldb/source/Plugins/Process/FreeBSDRemote/NativeProcessFreeBSD.cpp
@@ -543,15 +543,6 @@ Status NativeProcessFreeBSD::PopulateMemoryRegionCache() {
   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/FreeBSDRemote/NativeProcessFreeBSD.h b/lldb/source/Plugins/Process/FreeBSDRemote/NativeProcessFreeBSD.h
index e1334c3bcf5d..8b4ae9889474 100644
--- a/lldb/source/Plugins/Process/FreeBSDRemote/NativeProcessFreeBSD.h
+++ b/lldb/source/Plugins/Process/FreeBSDRemote/NativeProcessFreeBSD.h
@@ -60,11 +60,6 @@ class NativeProcessFreeBSD : public NativeProcessELF {
   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/Linux/NativeProcessLinux.cpp b/lldb/source/Plugins/Process/Linux/NativeProcessLinux.cpp
index 79d803de158e..5d287370fb36 100644
--- a/lldb/source/Plugins/Process/Linux/NativeProcessLinux.cpp
+++ b/lldb/source/Plugins/Process/Linux/NativeProcessLinux.cpp
@@ -19,6 +19,10 @@
 #include <string>
 #include <unordered_map>
 
+#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 <linux/unistd.h>
 #include <sys/socket.h>
 #include <sys/syscall.h>
@@ -1347,43 +1347,134 @@ void NativeProcessLinux::DoStopIDBumped(uint32_t newBumpId) {
   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<uint64_t>
+NativeProcessLinux::Syscall(llvm::ArrayRef<uint64_t> 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 &reg_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<uint8_t, 8> 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<addr_t>
+NativeProcessLinux::AllocateMemory(size_t size, uint32_t permissions) {
+
+  llvm::Optional<NativeRegisterContextLinux::MmapData> mmap_data =
+      GetCurrentThread()->GetRegisterContext().GetMmapData();
+  if (!mmap_data)
+    return llvm::make_error<UnimplementedError>();
+
+  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<uint64_t> 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<NativeRegisterContextLinux::MmapData> mmap_data =
+      GetCurrentThread()->GetRegisterContext().GetMmapData();
+  if (!mmap_data)
+    return llvm::make_error<UnimplementedError>();
+
+  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<uint64_t> 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 @@ NativeThreadLinux *NativeProcessLinux::GetThreadByID(lldb::tid_t tid) {
       NativeProcessProtocol::GetThreadByID(tid));
 }
 
+NativeThreadLinux *NativeProcessLinux::GetCurrentThread() {
+  return static_cast<NativeThreadLinux *>(
+      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/NativeProcessLinux.h b/lldb/source/Plugins/Process/Linux/NativeProcessLinux.h
index 1366f0b1fe3c..ba953d32f277 100644
--- a/lldb/source/Plugins/Process/Linux/NativeProcessLinux.h
+++ b/lldb/source/Plugins/Process/Linux/NativeProcessLinux.h
@@ -71,10 +71,10 @@ class NativeProcessLinux : public NativeProcessELF {
   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<lldb::addr_t> 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 @@ class NativeProcessLinux : public NativeProcessELF {
                             lldb::addr_t &load_addr) override;
 
   NativeThreadLinux *GetThreadByID(lldb::tid_t id);
+  NativeThreadLinux *GetCurrentThread();
 
   llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>>
   GetAuxvData() const override {
@@ -127,6 +128,8 @@ class NativeProcessLinux : public NativeProcessELF {
   llvm::Expected<llvm::ArrayRef<uint8_t>>
   GetSoftwareBreakpointTrapOpcode(size_t size_hint) override;
 
+  llvm::Expected<uint64_t> Syscall(llvm::ArrayRef<uint64_t> args);
+
 private:
   MainLoop::SignalHandleUP m_sigchld_handle;
   ArchSpec m_arch;
@@ -140,6 +143,9 @@ class NativeProcessLinux : public NativeProcessELF {
   // the relevan breakpoint
   std::map<lldb::tid_t, lldb::addr_t> m_threads_stepping_with_breakpoint;
 
+  /// Inferior memory (allocated by us) and its size.
+  llvm::DenseMap<lldb::addr_t, lldb::addr_t> 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/NativeRegisterContextLinux.h b/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux.h
index da7a762419f8..7f321c0b5926 100644
--- a/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux.h
+++ b/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux.h
@@ -31,6 +31,32 @@ class NativeRegisterContextLinux : public NativeRegisterContextRegisterInfo {
   // 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<uint8_t> Insn;
+
+    /// Registers used for syscall arguments. The first register is used to
+    /// store the syscall number.
+    llvm::ArrayRef<uint32_t> 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<SyscallData> 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<MmapData> GetMmapData() { return llvm::None; }
+
 protected:
   lldb::ByteOrder GetByteOrder() const;
 

diff  --git a/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_x86_64.cpp b/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_x86_64.cpp
index f9a3c221227f..fad5f74426f5 100644
--- a/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_x86_64.cpp
+++ b/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_x86_64.cpp
@@ -1220,4 +1220,38 @@ NativeRegisterContextLinux_x86_64::GetPtraceOffset(uint32_t reg_index) {
          (IsMPX(reg_index) ? 128 : 0);
 }
 
+llvm::Optional<NativeRegisterContextLinux::SyscallData>
+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::MmapData>
+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/Linux/NativeRegisterContextLinux_x86_64.h b/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_x86_64.h
index 58b749025c56..5651602aae2e 100644
--- a/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_x86_64.h
+++ b/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_x86_64.h
@@ -64,6 +64,10 @@ class NativeRegisterContextLinux_x86_64 : public NativeRegisterContextLinux {
 
   uint32_t NumSupportedHardwareWatchpoints() override;
 
+  llvm::Optional<SyscallData> GetSyscallData() override;
+
+  llvm::Optional<MmapData> GetMmapData() override;
+
 protected:
   void *GetGPRBuffer() override { return &m_gpr_x86_64; }
 

diff  --git a/lldb/source/Plugins/Process/NetBSD/NativeProcessNetBSD.cpp b/lldb/source/Plugins/Process/NetBSD/NativeProcessNetBSD.cpp
index 5109022d80dd..be98a6386363 100644
--- a/lldb/source/Plugins/Process/NetBSD/NativeProcessNetBSD.cpp
+++ b/lldb/source/Plugins/Process/NetBSD/NativeProcessNetBSD.cpp
@@ -684,15 +684,6 @@ Status NativeProcessNetBSD::PopulateMemoryRegionCache() {
   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/NetBSD/NativeProcessNetBSD.h b/lldb/source/Plugins/Process/NetBSD/NativeProcessNetBSD.h
index 6a06773f40a8..cd98a05fc459 100644
--- a/lldb/source/Plugins/Process/NetBSD/NativeProcessNetBSD.h
+++ b/lldb/source/Plugins/Process/NetBSD/NativeProcessNetBSD.h
@@ -60,11 +60,6 @@ class NativeProcessNetBSD : public NativeProcessELF {
   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/Windows/Common/NativeProcessWindows.cpp b/lldb/source/Plugins/Process/Windows/Common/NativeProcessWindows.cpp
index beee3f586780..69a5cb20b90b 100644
--- a/lldb/source/Plugins/Process/Windows/Common/NativeProcessWindows.cpp
+++ b/lldb/source/Plugins/Process/Windows/Common/NativeProcessWindows.cpp
@@ -217,13 +217,17 @@ Status NativeProcessWindows::WriteMemory(lldb::addr_t addr, const void *buf,
   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<lldb::addr_t>
+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/Windows/Common/NativeProcessWindows.h b/lldb/source/Plugins/Process/Windows/Common/NativeProcessWindows.h
index 89888df46756..150f7083e869 100644
--- a/lldb/source/Plugins/Process/Windows/Common/NativeProcessWindows.h
+++ b/lldb/source/Plugins/Process/Windows/Common/NativeProcessWindows.h
@@ -65,10 +65,10 @@ class NativeProcessWindows : public NativeProcessProtocol,
   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<lldb::addr_t> 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/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp
index 6f4d18364b26..d19becca823f 100644
--- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp
+++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp
@@ -93,6 +93,10 @@ void GDBRemoteCommunicationServerLLGS::RegisterPacketHandlers() {
       &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 @@ GDBRemoteCommunicationServerLLGS::Handle_memory_read(
   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_t> 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/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.h b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.h
index 3ce285910c25..2a2f4adf4e0e 100644
--- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.h
+++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.h
@@ -138,6 +138,8 @@ class GDBRemoteCommunicationServerLLGS
   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/test/API/lang/c/stepping/TestStepAndBreakpoints.py b/lldb/test/API/lang/c/stepping/TestStepAndBreakpoints.py
index c20e443b6838..3fc077183421 100644
--- a/lldb/test/API/lang/c/stepping/TestStepAndBreakpoints.py
+++ b/lldb/test/API/lang/c/stepping/TestStepAndBreakpoints.py
@@ -20,7 +20,7 @@ def setUp(self):
 
     @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
index 000000000000..10495940055b
--- /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
index 000000000000..64e0782f4bf8
--- /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
index 000000000000..76e8197013aa
--- /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
index 000000000000..1bd1b6390989
--- /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
index dcd8b092b2e3..cdb16589dc27 100644
--- a/lldb/unittests/TestingSupport/Host/NativeProcessTestUtils.h
+++ b/lldb/unittests/TestingSupport/Host/NativeProcessTestUtils.h
@@ -41,9 +41,6 @@ template <typename T> class MockProcess : public T {
   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 @@ class FakeMemory {
 };
 } // namespace lldb_private
 
-#endif
\ No newline at end of file
+#endif


        


More information about the lldb-commits mailing list