|
19 | 19 | #include <string>
|
20 | 20 | #include <unordered_map>
|
21 | 21 |
|
| 22 | +#include "NativeThreadLinux.h" |
| 23 | +#include "Plugins/Process/POSIX/ProcessPOSIXLog.h" |
| 24 | +#include "Plugins/Process/Utility/LinuxProcMaps.h" |
| 25 | +#include "Procfs.h" |
22 | 26 | #include "lldb/Core/EmulateInstruction.h"
|
23 | 27 | #include "lldb/Core/ModuleSpec.h"
|
24 | 28 | #include "lldb/Host/Host.h"
|
|
38 | 42 | #include "lldb/Utility/State.h"
|
39 | 43 | #include "lldb/Utility/Status.h"
|
40 | 44 | #include "lldb/Utility/StringExtractor.h"
|
| 45 | +#include "llvm/ADT/ScopeExit.h" |
41 | 46 | #include "llvm/Support/Errno.h"
|
42 | 47 | #include "llvm/Support/FileSystem.h"
|
43 | 48 | #include "llvm/Support/Threading.h"
|
44 | 49 |
|
45 |
| -#include "NativeThreadLinux.h" |
46 |
| -#include "Plugins/Process/POSIX/ProcessPOSIXLog.h" |
47 |
| -#include "Plugins/Process/Utility/LinuxProcMaps.h" |
48 |
| -#include "Procfs.h" |
49 |
| - |
50 | 50 | #include <linux/unistd.h>
|
51 | 51 | #include <sys/socket.h>
|
52 | 52 | #include <sys/syscall.h>
|
@@ -1347,43 +1347,134 @@ void NativeProcessLinux::DoStopIDBumped(uint32_t newBumpId) {
|
1347 | 1347 | m_mem_region_cache.clear();
|
1348 | 1348 | }
|
1349 | 1349 |
|
1350 |
| -Status NativeProcessLinux::AllocateMemory(size_t size, uint32_t permissions, |
1351 |
| - lldb::addr_t &addr) { |
1352 |
| -// FIXME implementing this requires the equivalent of |
1353 |
| -// InferiorCallPOSIX::InferiorCallMmap, which depends on functional ThreadPlans |
1354 |
| -// working with Native*Protocol. |
1355 |
| -#if 1 |
1356 |
| - return Status("not implemented yet"); |
1357 |
| -#else |
1358 |
| - addr = LLDB_INVALID_ADDRESS; |
1359 |
| -
|
1360 |
| - unsigned prot = 0; |
1361 |
| - if (permissions & lldb::ePermissionsReadable) |
1362 |
| - prot |= eMmapProtRead; |
1363 |
| - if (permissions & lldb::ePermissionsWritable) |
1364 |
| - prot |= eMmapProtWrite; |
1365 |
| - if (permissions & lldb::ePermissionsExecutable) |
1366 |
| - prot |= eMmapProtExec; |
1367 |
| -
|
1368 |
| - // TODO implement this directly in NativeProcessLinux |
1369 |
| - // (and lift to NativeProcessPOSIX if/when that class is refactored out). |
1370 |
| - if (InferiorCallMmap(this, addr, 0, size, prot, |
1371 |
| - eMmapFlagsAnon | eMmapFlagsPrivate, -1, 0)) { |
1372 |
| - m_addr_to_mmap_size[addr] = size; |
1373 |
| - return Status(); |
1374 |
| - } else { |
1375 |
| - addr = LLDB_INVALID_ADDRESS; |
1376 |
| - return Status("unable to allocate %" PRIu64 |
1377 |
| - " bytes of memory with permissions %s", |
1378 |
| - size, GetPermissionsAsCString(permissions)); |
| 1350 | +llvm::Expected<uint64_t> |
| 1351 | +NativeProcessLinux::Syscall(llvm::ArrayRef<uint64_t> args) { |
| 1352 | + PopulateMemoryRegionCache(); |
| 1353 | + auto region_it = llvm::find_if(m_mem_region_cache, [](const auto &pair) { |
| 1354 | + return pair.first.GetExecutable() == MemoryRegionInfo::eYes; |
| 1355 | + }); |
| 1356 | + if (region_it == m_mem_region_cache.end()) |
| 1357 | + return llvm::createStringError(llvm::inconvertibleErrorCode(), |
| 1358 | + "No executable memory region found!"); |
| 1359 | + |
| 1360 | + addr_t exe_addr = region_it->first.GetRange().GetRangeBase(); |
| 1361 | + |
| 1362 | + NativeThreadLinux &thread = *GetThreadByID(GetID()); |
| 1363 | + assert(thread.GetState() == eStateStopped); |
| 1364 | + NativeRegisterContextLinux ®_ctx = thread.GetRegisterContext(); |
| 1365 | + |
| 1366 | + NativeRegisterContextLinux::SyscallData syscall_data = |
| 1367 | + *reg_ctx.GetSyscallData(); |
| 1368 | + |
| 1369 | + DataBufferSP registers_sp; |
| 1370 | + if (llvm::Error Err = reg_ctx.ReadAllRegisterValues(registers_sp).ToError()) |
| 1371 | + return std::move(Err); |
| 1372 | + auto restore_regs = llvm::make_scope_exit( |
| 1373 | + [&] { reg_ctx.WriteAllRegisterValues(registers_sp); }); |
| 1374 | + |
| 1375 | + llvm::SmallVector<uint8_t, 8> memory(syscall_data.Insn.size()); |
| 1376 | + size_t bytes_read; |
| 1377 | + if (llvm::Error Err = |
| 1378 | + ReadMemory(exe_addr, memory.data(), memory.size(), bytes_read) |
| 1379 | + .ToError()) { |
| 1380 | + return std::move(Err); |
1379 | 1381 | }
|
1380 |
| -#endif |
| 1382 | + |
| 1383 | + auto restore_mem = llvm::make_scope_exit( |
| 1384 | + [&] { WriteMemory(exe_addr, memory.data(), memory.size(), bytes_read); }); |
| 1385 | + |
| 1386 | + if (llvm::Error Err = reg_ctx.SetPC(exe_addr).ToError()) |
| 1387 | + return std::move(Err); |
| 1388 | + |
| 1389 | + for (const auto &zip : llvm::zip_first(args, syscall_data.Args)) { |
| 1390 | + if (llvm::Error Err = |
| 1391 | + reg_ctx |
| 1392 | + .WriteRegisterFromUnsigned(std::get<1>(zip), std::get<0>(zip)) |
| 1393 | + .ToError()) { |
| 1394 | + return std::move(Err); |
| 1395 | + } |
| 1396 | + } |
| 1397 | + if (llvm::Error Err = WriteMemory(exe_addr, syscall_data.Insn.data(), |
| 1398 | + syscall_data.Insn.size(), bytes_read) |
| 1399 | + .ToError()) |
| 1400 | + return std::move(Err); |
| 1401 | + |
| 1402 | + m_mem_region_cache.clear(); |
| 1403 | + |
| 1404 | + // With software single stepping the syscall insn buffer must also include a |
| 1405 | + // trap instruction to stop the process. |
| 1406 | + int req = SupportHardwareSingleStepping() ? PTRACE_SINGLESTEP : PTRACE_CONT; |
| 1407 | + if (llvm::Error Err = |
| 1408 | + PtraceWrapper(req, thread.GetID(), nullptr, nullptr).ToError()) |
| 1409 | + return std::move(Err); |
| 1410 | + |
| 1411 | + int status; |
| 1412 | + ::pid_t wait_pid = llvm::sys::RetryAfterSignal(-1, ::waitpid, thread.GetID(), |
| 1413 | + &status, __WALL); |
| 1414 | + if (wait_pid == -1) { |
| 1415 | + return llvm::errorCodeToError( |
| 1416 | + std::error_code(errno, std::generic_category())); |
| 1417 | + } |
| 1418 | + assert((unsigned)wait_pid == thread.GetID()); |
| 1419 | + |
| 1420 | + uint64_t result = reg_ctx.ReadRegisterAsUnsigned(syscall_data.Result, -ESRCH); |
| 1421 | + |
| 1422 | + // Values larger than this are actually negative errno numbers. |
| 1423 | + uint64_t errno_threshold = |
| 1424 | + (uint64_t(-1) >> (64 - 8 * m_arch.GetAddressByteSize())) - 0x1000; |
| 1425 | + if (result > errno_threshold) { |
| 1426 | + return llvm::errorCodeToError( |
| 1427 | + std::error_code(-result & 0xfff, std::generic_category())); |
| 1428 | + } |
| 1429 | + |
| 1430 | + return result; |
1381 | 1431 | }
|
1382 | 1432 |
|
1383 |
| -Status NativeProcessLinux::DeallocateMemory(lldb::addr_t addr) { |
1384 |
| - // FIXME see comments in AllocateMemory - required lower-level |
1385 |
| - // bits not in place yet (ThreadPlans) |
1386 |
| - return Status("not implemented"); |
| 1433 | +llvm::Expected<addr_t> |
| 1434 | +NativeProcessLinux::AllocateMemory(size_t size, uint32_t permissions) { |
| 1435 | + |
| 1436 | + llvm::Optional<NativeRegisterContextLinux::MmapData> mmap_data = |
| 1437 | + GetCurrentThread()->GetRegisterContext().GetMmapData(); |
| 1438 | + if (!mmap_data) |
| 1439 | + return llvm::make_error<UnimplementedError>(); |
| 1440 | + |
| 1441 | + unsigned prot = PROT_NONE; |
| 1442 | + assert((permissions & (ePermissionsReadable | ePermissionsWritable | |
| 1443 | + ePermissionsExecutable)) == permissions && |
| 1444 | + "Unknown permission!"); |
| 1445 | + if (permissions & ePermissionsReadable) |
| 1446 | + prot |= PROT_READ; |
| 1447 | + if (permissions & ePermissionsWritable) |
| 1448 | + prot |= PROT_WRITE; |
| 1449 | + if (permissions & ePermissionsExecutable) |
| 1450 | + prot |= PROT_EXEC; |
| 1451 | + |
| 1452 | + llvm::Expected<uint64_t> Result = |
| 1453 | + Syscall({mmap_data->SysMmap, 0, size, prot, MAP_ANONYMOUS | MAP_PRIVATE, |
| 1454 | + uint64_t(-1), 0}); |
| 1455 | + if (Result) |
| 1456 | + m_allocated_memory.try_emplace(*Result, size); |
| 1457 | + return Result; |
| 1458 | +} |
| 1459 | + |
| 1460 | +llvm::Error NativeProcessLinux::DeallocateMemory(lldb::addr_t addr) { |
| 1461 | + llvm::Optional<NativeRegisterContextLinux::MmapData> mmap_data = |
| 1462 | + GetCurrentThread()->GetRegisterContext().GetMmapData(); |
| 1463 | + if (!mmap_data) |
| 1464 | + return llvm::make_error<UnimplementedError>(); |
| 1465 | + |
| 1466 | + auto it = m_allocated_memory.find(addr); |
| 1467 | + if (it == m_allocated_memory.end()) |
| 1468 | + return llvm::createStringError(llvm::errc::invalid_argument, |
| 1469 | + "Memory not allocated by the debugger."); |
| 1470 | + |
| 1471 | + llvm::Expected<uint64_t> Result = |
| 1472 | + Syscall({mmap_data->SysMunmap, addr, it->second}); |
| 1473 | + if (!Result) |
| 1474 | + return Result.takeError(); |
| 1475 | + |
| 1476 | + m_allocated_memory.erase(it); |
| 1477 | + return llvm::Error::success(); |
1387 | 1478 | }
|
1388 | 1479 |
|
1389 | 1480 | size_t NativeProcessLinux::UpdateThreads() {
|
@@ -1652,6 +1743,11 @@ NativeThreadLinux *NativeProcessLinux::GetThreadByID(lldb::tid_t tid) {
|
1652 | 1743 | NativeProcessProtocol::GetThreadByID(tid));
|
1653 | 1744 | }
|
1654 | 1745 |
|
| 1746 | +NativeThreadLinux *NativeProcessLinux::GetCurrentThread() { |
| 1747 | + return static_cast<NativeThreadLinux *>( |
| 1748 | + NativeProcessProtocol::GetCurrentThread()); |
| 1749 | +} |
| 1750 | + |
1655 | 1751 | Status NativeProcessLinux::ResumeThread(NativeThreadLinux &thread,
|
1656 | 1752 | lldb::StateType state, int signo) {
|
1657 | 1753 | Log *const log = ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_THREAD);
|
|
0 commit comments