diff options
author | 2020-08-03 14:33:06 +0000 | |
---|---|---|
committer | 2020-08-03 14:33:06 +0000 | |
commit | 061da546b983eb767bad15e67af1174fb0bcf31c (patch) | |
tree | 83c78b820819d70aa40c36d90447978b300078c5 /gnu/llvm/lldb/source/Host/posix | |
parent | Import LLVM 10.0.0 release including clang, lld and lldb. (diff) | |
download | wireguard-openbsd-061da546b983eb767bad15e67af1174fb0bcf31c.tar.xz wireguard-openbsd-061da546b983eb767bad15e67af1174fb0bcf31c.zip |
Import LLVM 10.0.0 release including clang, lld and lldb.
ok hackroom
tested by plenty
Diffstat (limited to 'gnu/llvm/lldb/source/Host/posix')
-rw-r--r-- | gnu/llvm/lldb/source/Host/posix/ConnectionFileDescriptorPosix.cpp | 771 | ||||
-rw-r--r-- | gnu/llvm/lldb/source/Host/posix/DomainSocket.cpp | 155 | ||||
-rw-r--r-- | gnu/llvm/lldb/source/Host/posix/FileSystemPosix.cpp | 80 | ||||
-rw-r--r-- | gnu/llvm/lldb/source/Host/posix/HostInfoPosix.cpp | 149 | ||||
-rw-r--r-- | gnu/llvm/lldb/source/Host/posix/HostProcessPosix.cpp | 94 | ||||
-rw-r--r-- | gnu/llvm/lldb/source/Host/posix/HostThreadPosix.cpp | 61 | ||||
-rw-r--r-- | gnu/llvm/lldb/source/Host/posix/LockFilePosix.cpp | 62 | ||||
-rw-r--r-- | gnu/llvm/lldb/source/Host/posix/PipePosix.cpp | 318 | ||||
-rw-r--r-- | gnu/llvm/lldb/source/Host/posix/ProcessLauncherPosixFork.cpp | 218 |
9 files changed, 1908 insertions, 0 deletions
diff --git a/gnu/llvm/lldb/source/Host/posix/ConnectionFileDescriptorPosix.cpp b/gnu/llvm/lldb/source/Host/posix/ConnectionFileDescriptorPosix.cpp new file mode 100644 index 00000000000..2223e1625b1 --- /dev/null +++ b/gnu/llvm/lldb/source/Host/posix/ConnectionFileDescriptorPosix.cpp @@ -0,0 +1,771 @@ +//===-- ConnectionFileDescriptorPosix.cpp -----------------------*- 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 +// +//===----------------------------------------------------------------------===// + +#if defined(__APPLE__) +// Enable this special support for Apple builds where we can have unlimited +// select bounds. We tried switching to poll() and kqueue and we were panicing +// the kernel, so we have to stick with select for now. +#define _DARWIN_UNLIMITED_SELECT +#endif + +#include "lldb/Host/posix/ConnectionFileDescriptorPosix.h" +#include "lldb/Host/Config.h" +#include "lldb/Host/Socket.h" +#include "lldb/Host/SocketAddress.h" +#include "lldb/Utility/SelectHelper.h" +#include "lldb/Utility/Timeout.h" + +#include <errno.h> +#include <fcntl.h> +#include <stdlib.h> +#include <string.h> +#include <sys/types.h> + +#if LLDB_ENABLE_POSIX +#include <termios.h> +#include <unistd.h> +#endif + +#include <memory> +#include <sstream> + +#include "llvm/Support/Errno.h" +#include "llvm/Support/ErrorHandling.h" +#if defined(__APPLE__) +#include "llvm/ADT/SmallVector.h" +#endif +#include "lldb/Host/Host.h" +#include "lldb/Host/Socket.h" +#include "lldb/Host/common/TCPSocket.h" +#include "lldb/Utility/Log.h" +#include "lldb/Utility/StreamString.h" +#include "lldb/Utility/Timer.h" + +using namespace lldb; +using namespace lldb_private; + +const char *ConnectionFileDescriptor::LISTEN_SCHEME = "listen"; +const char *ConnectionFileDescriptor::ACCEPT_SCHEME = "accept"; +const char *ConnectionFileDescriptor::UNIX_ACCEPT_SCHEME = "unix-accept"; +const char *ConnectionFileDescriptor::CONNECT_SCHEME = "connect"; +const char *ConnectionFileDescriptor::TCP_CONNECT_SCHEME = "tcp-connect"; +const char *ConnectionFileDescriptor::UDP_SCHEME = "udp"; +const char *ConnectionFileDescriptor::UNIX_CONNECT_SCHEME = "unix-connect"; +const char *ConnectionFileDescriptor::UNIX_ABSTRACT_CONNECT_SCHEME = + "unix-abstract-connect"; +const char *ConnectionFileDescriptor::FD_SCHEME = "fd"; +const char *ConnectionFileDescriptor::FILE_SCHEME = "file"; + +namespace { + +llvm::Optional<llvm::StringRef> GetURLAddress(llvm::StringRef url, + llvm::StringRef scheme) { + if (!url.consume_front(scheme)) + return llvm::None; + if (!url.consume_front("://")) + return llvm::None; + return url; +} +} + +ConnectionFileDescriptor::ConnectionFileDescriptor(bool child_processes_inherit) + : Connection(), m_pipe(), m_mutex(), m_shutting_down(false), + m_waiting_for_accept(false), + m_child_processes_inherit(child_processes_inherit) { + Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_CONNECTION | + LIBLLDB_LOG_OBJECT)); + LLDB_LOGF(log, "%p ConnectionFileDescriptor::ConnectionFileDescriptor ()", + static_cast<void *>(this)); +} + +ConnectionFileDescriptor::ConnectionFileDescriptor(int fd, bool owns_fd) + : Connection(), m_pipe(), m_mutex(), m_shutting_down(false), + m_waiting_for_accept(false), m_child_processes_inherit(false) { + m_write_sp = std::make_shared<NativeFile>(fd, File::eOpenOptionWrite, owns_fd); + m_read_sp = std::make_shared<NativeFile>(fd, File::eOpenOptionRead, false); + + Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_CONNECTION | + LIBLLDB_LOG_OBJECT)); + LLDB_LOGF(log, + "%p ConnectionFileDescriptor::ConnectionFileDescriptor (fd = " + "%i, owns_fd = %i)", + static_cast<void *>(this), fd, owns_fd); + OpenCommandPipe(); +} + +ConnectionFileDescriptor::ConnectionFileDescriptor(Socket *socket) + : Connection(), m_pipe(), m_mutex(), m_shutting_down(false), + m_waiting_for_accept(false), m_child_processes_inherit(false) { + InitializeSocket(socket); +} + +ConnectionFileDescriptor::~ConnectionFileDescriptor() { + Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_CONNECTION | + LIBLLDB_LOG_OBJECT)); + LLDB_LOGF(log, "%p ConnectionFileDescriptor::~ConnectionFileDescriptor ()", + static_cast<void *>(this)); + Disconnect(nullptr); + CloseCommandPipe(); +} + +void ConnectionFileDescriptor::OpenCommandPipe() { + CloseCommandPipe(); + + Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_CONNECTION)); + // Make the command file descriptor here: + Status result = m_pipe.CreateNew(m_child_processes_inherit); + if (!result.Success()) { + LLDB_LOGF(log, + "%p ConnectionFileDescriptor::OpenCommandPipe () - could not " + "make pipe: %s", + static_cast<void *>(this), result.AsCString()); + } else { + LLDB_LOGF(log, + "%p ConnectionFileDescriptor::OpenCommandPipe() - success " + "readfd=%d writefd=%d", + static_cast<void *>(this), m_pipe.GetReadFileDescriptor(), + m_pipe.GetWriteFileDescriptor()); + } +} + +void ConnectionFileDescriptor::CloseCommandPipe() { + Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_CONNECTION)); + LLDB_LOGF(log, "%p ConnectionFileDescriptor::CloseCommandPipe()", + static_cast<void *>(this)); + + m_pipe.Close(); +} + +bool ConnectionFileDescriptor::IsConnected() const { + return (m_read_sp && m_read_sp->IsValid()) || + (m_write_sp && m_write_sp->IsValid()); +} + +ConnectionStatus ConnectionFileDescriptor::Connect(llvm::StringRef path, + Status *error_ptr) { + std::lock_guard<std::recursive_mutex> guard(m_mutex); + Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_CONNECTION)); + LLDB_LOGF(log, "%p ConnectionFileDescriptor::Connect (url = '%s')", + static_cast<void *>(this), path.str().c_str()); + + OpenCommandPipe(); + + if (!path.empty()) { + llvm::Optional<llvm::StringRef> addr; + if ((addr = GetURLAddress(path, LISTEN_SCHEME))) { + // listen://HOST:PORT + return SocketListenAndAccept(*addr, error_ptr); + } else if ((addr = GetURLAddress(path, ACCEPT_SCHEME))) { + // unix://SOCKNAME + return NamedSocketAccept(*addr, error_ptr); + } else if ((addr = GetURLAddress(path, UNIX_ACCEPT_SCHEME))) { + // unix://SOCKNAME + return NamedSocketAccept(*addr, error_ptr); + } else if ((addr = GetURLAddress(path, CONNECT_SCHEME))) { + return ConnectTCP(*addr, error_ptr); + } else if ((addr = GetURLAddress(path, TCP_CONNECT_SCHEME))) { + return ConnectTCP(*addr, error_ptr); + } else if ((addr = GetURLAddress(path, UDP_SCHEME))) { + return ConnectUDP(*addr, error_ptr); + } else if ((addr = GetURLAddress(path, UNIX_CONNECT_SCHEME))) { + // unix-connect://SOCKNAME + return NamedSocketConnect(*addr, error_ptr); + } else if ((addr = GetURLAddress(path, UNIX_ABSTRACT_CONNECT_SCHEME))) { + // unix-abstract-connect://SOCKNAME + return UnixAbstractSocketConnect(*addr, error_ptr); + } +#if LLDB_ENABLE_POSIX + else if ((addr = GetURLAddress(path, FD_SCHEME))) { + // Just passing a native file descriptor within this current process that + // is already opened (possibly from a service or other source). + int fd = -1; + + if (!addr->getAsInteger(0, fd)) { + // We have what looks to be a valid file descriptor, but we should make + // sure it is. We currently are doing this by trying to get the flags + // from the file descriptor and making sure it isn't a bad fd. + errno = 0; + int flags = ::fcntl(fd, F_GETFL, 0); + if (flags == -1 || errno == EBADF) { + if (error_ptr) + error_ptr->SetErrorStringWithFormat("stale file descriptor: %s", + path.str().c_str()); + m_read_sp.reset(); + m_write_sp.reset(); + return eConnectionStatusError; + } else { + // Don't take ownership of a file descriptor that gets passed to us + // since someone else opened the file descriptor and handed it to us. + // TODO: Since are using a URL to open connection we should + // eventually parse options using the web standard where we have + // "fd://123?opt1=value;opt2=value" and we can have an option be + // "owns=1" or "owns=0" or something like this to allow us to specify + // this. For now, we assume we must assume we don't own it. + + std::unique_ptr<TCPSocket> tcp_socket; + tcp_socket.reset(new TCPSocket(fd, false, false)); + // Try and get a socket option from this file descriptor to see if + // this is a socket and set m_is_socket accordingly. + int resuse; + bool is_socket = + !!tcp_socket->GetOption(SOL_SOCKET, SO_REUSEADDR, resuse); + if (is_socket) { + m_read_sp = std::move(tcp_socket); + m_write_sp = m_read_sp; + } else { + m_read_sp = + std::make_shared<NativeFile>(fd, File::eOpenOptionRead, false); + m_write_sp = + std::make_shared<NativeFile>(fd, File::eOpenOptionWrite, false); + } + m_uri = *addr; + return eConnectionStatusSuccess; + } + } + + if (error_ptr) + error_ptr->SetErrorStringWithFormat("invalid file descriptor: \"%s\"", + path.str().c_str()); + m_read_sp.reset(); + m_write_sp.reset(); + return eConnectionStatusError; + } else if ((addr = GetURLAddress(path, FILE_SCHEME))) { + std::string addr_str = addr->str(); + // file:///PATH + int fd = llvm::sys::RetryAfterSignal(-1, ::open, addr_str.c_str(), O_RDWR); + if (fd == -1) { + if (error_ptr) + error_ptr->SetErrorToErrno(); + return eConnectionStatusError; + } + + if (::isatty(fd)) { + // Set up serial terminal emulation + struct termios options; + ::tcgetattr(fd, &options); + + // Set port speed to maximum + ::cfsetospeed(&options, B115200); + ::cfsetispeed(&options, B115200); + + // Raw input, disable echo and signals + options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); + + // Make sure only one character is needed to return from a read + options.c_cc[VMIN] = 1; + options.c_cc[VTIME] = 0; + + llvm::sys::RetryAfterSignal(-1, ::tcsetattr, fd, TCSANOW, &options); + } + + int flags = ::fcntl(fd, F_GETFL, 0); + if (flags >= 0) { + if ((flags & O_NONBLOCK) == 0) { + flags |= O_NONBLOCK; + ::fcntl(fd, F_SETFL, flags); + } + } + m_read_sp = std::make_shared<NativeFile>(fd, File::eOpenOptionRead, true); + m_write_sp = std::make_shared<NativeFile>(fd, File::eOpenOptionWrite, false); + return eConnectionStatusSuccess; + } +#endif + if (error_ptr) + error_ptr->SetErrorStringWithFormat("unsupported connection URL: '%s'", + path.str().c_str()); + return eConnectionStatusError; + } + if (error_ptr) + error_ptr->SetErrorString("invalid connect arguments"); + return eConnectionStatusError; +} + +bool ConnectionFileDescriptor::InterruptRead() { + size_t bytes_written = 0; + Status result = m_pipe.Write("i", 1, bytes_written); + return result.Success(); +} + +ConnectionStatus ConnectionFileDescriptor::Disconnect(Status *error_ptr) { + Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_CONNECTION)); + LLDB_LOGF(log, "%p ConnectionFileDescriptor::Disconnect ()", + static_cast<void *>(this)); + + ConnectionStatus status = eConnectionStatusSuccess; + + if (!IsConnected()) { + LLDB_LOGF( + log, "%p ConnectionFileDescriptor::Disconnect(): Nothing to disconnect", + static_cast<void *>(this)); + return eConnectionStatusSuccess; + } + + if (m_read_sp && m_read_sp->IsValid() && + m_read_sp->GetFdType() == IOObject::eFDTypeSocket) + static_cast<Socket &>(*m_read_sp).PreDisconnect(); + + // Try to get the ConnectionFileDescriptor's mutex. If we fail, that is + // quite likely because somebody is doing a blocking read on our file + // descriptor. If that's the case, then send the "q" char to the command + // file channel so the read will wake up and the connection will then know to + // shut down. + std::unique_lock<std::recursive_mutex> locker(m_mutex, std::defer_lock); + if (!locker.try_lock()) { + if (m_pipe.CanWrite()) { + size_t bytes_written = 0; + Status result = m_pipe.Write("q", 1, bytes_written); + LLDB_LOGF(log, + "%p ConnectionFileDescriptor::Disconnect(): Couldn't get " + "the lock, sent 'q' to %d, error = '%s'.", + static_cast<void *>(this), m_pipe.GetWriteFileDescriptor(), + result.AsCString()); + } else if (log) { + LLDB_LOGF(log, + "%p ConnectionFileDescriptor::Disconnect(): Couldn't get the " + "lock, but no command pipe is available.", + static_cast<void *>(this)); + } + locker.lock(); + } + + // Prevents reads and writes during shutdown. + m_shutting_down = true; + + Status error = m_read_sp->Close(); + Status error2 = m_write_sp->Close(); + if (error.Fail() || error2.Fail()) + status = eConnectionStatusError; + if (error_ptr) + *error_ptr = error.Fail() ? error : error2; + + // Close any pipes we were using for async interrupts + m_pipe.Close(); + + m_uri.clear(); + m_shutting_down = false; + return status; +} + +size_t ConnectionFileDescriptor::Read(void *dst, size_t dst_len, + const Timeout<std::micro> &timeout, + ConnectionStatus &status, + Status *error_ptr) { + Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_CONNECTION)); + + std::unique_lock<std::recursive_mutex> locker(m_mutex, std::defer_lock); + if (!locker.try_lock()) { + LLDB_LOGF(log, + "%p ConnectionFileDescriptor::Read () failed to get the " + "connection lock.", + static_cast<void *>(this)); + if (error_ptr) + error_ptr->SetErrorString("failed to get the connection lock for read."); + + status = eConnectionStatusTimedOut; + return 0; + } + + if (m_shutting_down) { + if (error_ptr) + error_ptr->SetErrorString("shutting down"); + status = eConnectionStatusError; + return 0; + } + + status = BytesAvailable(timeout, error_ptr); + if (status != eConnectionStatusSuccess) + return 0; + + Status error; + size_t bytes_read = dst_len; + error = m_read_sp->Read(dst, bytes_read); + + if (log) { + LLDB_LOGF(log, + "%p ConnectionFileDescriptor::Read() fd = %" PRIu64 + ", dst = %p, dst_len = %" PRIu64 ") => %" PRIu64 ", error = %s", + static_cast<void *>(this), + static_cast<uint64_t>(m_read_sp->GetWaitableHandle()), + static_cast<void *>(dst), static_cast<uint64_t>(dst_len), + static_cast<uint64_t>(bytes_read), error.AsCString()); + } + + if (bytes_read == 0) { + error.Clear(); // End-of-file. Do not automatically close; pass along for + // the end-of-file handlers. + status = eConnectionStatusEndOfFile; + } + + if (error_ptr) + *error_ptr = error; + + if (error.Fail()) { + uint32_t error_value = error.GetError(); + switch (error_value) { + case EAGAIN: // The file was marked for non-blocking I/O, and no data were + // ready to be read. + if (m_read_sp->GetFdType() == IOObject::eFDTypeSocket) + status = eConnectionStatusTimedOut; + else + status = eConnectionStatusSuccess; + return 0; + + case EFAULT: // Buf points outside the allocated address space. + case EINTR: // A read from a slow device was interrupted before any data + // arrived by the delivery of a signal. + case EINVAL: // The pointer associated with fildes was negative. + case EIO: // An I/O error occurred while reading from the file system. + // The process group is orphaned. + // The file is a regular file, nbyte is greater than 0, the + // starting position is before the end-of-file, and the + // starting position is greater than or equal to the offset + // maximum established for the open file descriptor + // associated with fildes. + case EISDIR: // An attempt is made to read a directory. + case ENOBUFS: // An attempt to allocate a memory buffer fails. + case ENOMEM: // Insufficient memory is available. + status = eConnectionStatusError; + break; // Break to close.... + + case ENOENT: // no such file or directory + case EBADF: // fildes is not a valid file or socket descriptor open for + // reading. + case ENXIO: // An action is requested of a device that does not exist.. + // A requested action cannot be performed by the device. + case ECONNRESET: // The connection is closed by the peer during a read + // attempt on a socket. + case ENOTCONN: // A read is attempted on an unconnected socket. + status = eConnectionStatusLostConnection; + break; // Break to close.... + + case ETIMEDOUT: // A transmission timeout occurs during a read attempt on a + // socket. + status = eConnectionStatusTimedOut; + return 0; + + default: + LLDB_LOG(log, "this = {0}, unexpected error: {1}", this, + llvm::sys::StrError(error_value)); + status = eConnectionStatusError; + break; // Break to close.... + } + + return 0; + } + return bytes_read; +} + +size_t ConnectionFileDescriptor::Write(const void *src, size_t src_len, + ConnectionStatus &status, + Status *error_ptr) { + Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_CONNECTION)); + LLDB_LOGF(log, + "%p ConnectionFileDescriptor::Write (src = %p, src_len = %" PRIu64 + ")", + static_cast<void *>(this), static_cast<const void *>(src), + static_cast<uint64_t>(src_len)); + + if (!IsConnected()) { + if (error_ptr) + error_ptr->SetErrorString("not connected"); + status = eConnectionStatusNoConnection; + return 0; + } + + if (m_shutting_down) { + if (error_ptr) + error_ptr->SetErrorString("shutting down"); + status = eConnectionStatusError; + return 0; + } + + Status error; + + size_t bytes_sent = src_len; + error = m_write_sp->Write(src, bytes_sent); + + if (log) { + LLDB_LOGF(log, + "%p ConnectionFileDescriptor::Write(fd = %" PRIu64 + ", src = %p, src_len = %" PRIu64 ") => %" PRIu64 " (error = %s)", + static_cast<void *>(this), + static_cast<uint64_t>(m_write_sp->GetWaitableHandle()), + static_cast<const void *>(src), static_cast<uint64_t>(src_len), + static_cast<uint64_t>(bytes_sent), error.AsCString()); + } + + if (error_ptr) + *error_ptr = error; + + if (error.Fail()) { + switch (error.GetError()) { + case EAGAIN: + case EINTR: + status = eConnectionStatusSuccess; + return 0; + + case ECONNRESET: // The connection is closed by the peer during a read + // attempt on a socket. + case ENOTCONN: // A read is attempted on an unconnected socket. + status = eConnectionStatusLostConnection; + break; // Break to close.... + + default: + status = eConnectionStatusError; + break; // Break to close.... + } + + return 0; + } + + status = eConnectionStatusSuccess; + return bytes_sent; +} + +std::string ConnectionFileDescriptor::GetURI() { return m_uri; } + +// This ConnectionFileDescriptor::BytesAvailable() uses select() via +// SelectHelper +// +// PROS: +// - select is consistent across most unix platforms +// - The Apple specific version allows for unlimited fds in the fd_sets by +// setting the _DARWIN_UNLIMITED_SELECT define prior to including the +// required header files. +// CONS: +// - on non-Apple platforms, only supports file descriptors up to FD_SETSIZE. +// This implementation will assert if it runs into that hard limit to let +// users know that another ConnectionFileDescriptor::BytesAvailable() should +// be used or a new version of ConnectionFileDescriptor::BytesAvailable() +// should be written for the system that is running into the limitations. + +ConnectionStatus +ConnectionFileDescriptor::BytesAvailable(const Timeout<std::micro> &timeout, + Status *error_ptr) { + // Don't need to take the mutex here separately since we are only called from + // Read. If we ever get used more generally we will need to lock here as + // well. + + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_CONNECTION)); + LLDB_LOG(log, "this = {0}, timeout = {1}", this, timeout); + + // Make a copy of the file descriptors to make sure we don't have another + // thread change these values out from under us and cause problems in the + // loop below where like in FS_SET() + const IOObject::WaitableHandle handle = m_read_sp->GetWaitableHandle(); + const int pipe_fd = m_pipe.GetReadFileDescriptor(); + + if (handle != IOObject::kInvalidHandleValue) { + SelectHelper select_helper; + if (timeout) + select_helper.SetTimeout(*timeout); + + select_helper.FDSetRead(handle); +#if defined(_WIN32) + // select() won't accept pipes on Windows. The entire Windows codepath + // needs to be converted over to using WaitForMultipleObjects and event + // HANDLEs, but for now at least this will allow ::select() to not return + // an error. + const bool have_pipe_fd = false; +#else + const bool have_pipe_fd = pipe_fd >= 0; +#endif + if (have_pipe_fd) + select_helper.FDSetRead(pipe_fd); + + while (handle == m_read_sp->GetWaitableHandle()) { + + Status error = select_helper.Select(); + + if (error_ptr) + *error_ptr = error; + + if (error.Fail()) { + switch (error.GetError()) { + case EBADF: // One of the descriptor sets specified an invalid + // descriptor. + return eConnectionStatusLostConnection; + + case EINVAL: // The specified time limit is invalid. One of its + // components is negative or too large. + default: // Other unknown error + return eConnectionStatusError; + + case ETIMEDOUT: + return eConnectionStatusTimedOut; + + case EAGAIN: // The kernel was (perhaps temporarily) unable to + // allocate the requested number of file descriptors, or + // we have non-blocking IO + case EINTR: // A signal was delivered before the time limit + // expired and before any of the selected events occurred. + break; // Lets keep reading to until we timeout + } + } else { + if (select_helper.FDIsSetRead(handle)) + return eConnectionStatusSuccess; + + if (select_helper.FDIsSetRead(pipe_fd)) { + // There is an interrupt or exit command in the command pipe Read the + // data from that pipe: + char c; + + ssize_t bytes_read = llvm::sys::RetryAfterSignal(-1, ::read, pipe_fd, &c, 1); + assert(bytes_read == 1); + (void)bytes_read; + switch (c) { + case 'q': + LLDB_LOGF(log, + "%p ConnectionFileDescriptor::BytesAvailable() " + "got data: %c from the command channel.", + static_cast<void *>(this), c); + return eConnectionStatusEndOfFile; + case 'i': + // Interrupt the current read + return eConnectionStatusInterrupted; + } + } + } + } + } + + if (error_ptr) + error_ptr->SetErrorString("not connected"); + return eConnectionStatusLostConnection; +} + +ConnectionStatus +ConnectionFileDescriptor::NamedSocketAccept(llvm::StringRef socket_name, + Status *error_ptr) { + Socket *socket = nullptr; + Status error = + Socket::UnixDomainAccept(socket_name, m_child_processes_inherit, socket); + if (error_ptr) + *error_ptr = error; + m_write_sp.reset(socket); + m_read_sp = m_write_sp; + if (error.Fail()) { + return eConnectionStatusError; + } + m_uri.assign(socket_name); + return eConnectionStatusSuccess; +} + +ConnectionStatus +ConnectionFileDescriptor::NamedSocketConnect(llvm::StringRef socket_name, + Status *error_ptr) { + Socket *socket = nullptr; + Status error = + Socket::UnixDomainConnect(socket_name, m_child_processes_inherit, socket); + if (error_ptr) + *error_ptr = error; + m_write_sp.reset(socket); + m_read_sp = m_write_sp; + if (error.Fail()) { + return eConnectionStatusError; + } + m_uri.assign(socket_name); + return eConnectionStatusSuccess; +} + +lldb::ConnectionStatus +ConnectionFileDescriptor::UnixAbstractSocketConnect(llvm::StringRef socket_name, + Status *error_ptr) { + Socket *socket = nullptr; + Status error = Socket::UnixAbstractConnect(socket_name, + m_child_processes_inherit, socket); + if (error_ptr) + *error_ptr = error; + m_write_sp.reset(socket); + m_read_sp = m_write_sp; + if (error.Fail()) { + return eConnectionStatusError; + } + m_uri.assign(socket_name); + return eConnectionStatusSuccess; +} + +ConnectionStatus +ConnectionFileDescriptor::SocketListenAndAccept(llvm::StringRef s, + Status *error_ptr) { + m_port_predicate.SetValue(0, eBroadcastNever); + + Socket *socket = nullptr; + m_waiting_for_accept = true; + Status error = Socket::TcpListen(s, m_child_processes_inherit, socket, + &m_port_predicate); + if (error_ptr) + *error_ptr = error; + if (error.Fail()) + return eConnectionStatusError; + + std::unique_ptr<Socket> listening_socket_up; + + listening_socket_up.reset(socket); + socket = nullptr; + error = listening_socket_up->Accept(socket); + listening_socket_up.reset(); + if (error_ptr) + *error_ptr = error; + if (error.Fail()) + return eConnectionStatusError; + + InitializeSocket(socket); + return eConnectionStatusSuccess; +} + +ConnectionStatus ConnectionFileDescriptor::ConnectTCP(llvm::StringRef s, + Status *error_ptr) { + Socket *socket = nullptr; + Status error = Socket::TcpConnect(s, m_child_processes_inherit, socket); + if (error_ptr) + *error_ptr = error; + m_write_sp.reset(socket); + m_read_sp = m_write_sp; + if (error.Fail()) { + return eConnectionStatusError; + } + m_uri.assign(s); + return eConnectionStatusSuccess; +} + +ConnectionStatus ConnectionFileDescriptor::ConnectUDP(llvm::StringRef s, + Status *error_ptr) { + Socket *socket = nullptr; + Status error = Socket::UdpConnect(s, m_child_processes_inherit, socket); + if (error_ptr) + *error_ptr = error; + m_write_sp.reset(socket); + m_read_sp = m_write_sp; + if (error.Fail()) { + return eConnectionStatusError; + } + m_uri.assign(s); + return eConnectionStatusSuccess; +} + +uint16_t +ConnectionFileDescriptor::GetListeningPort(const Timeout<std::micro> &timeout) { + auto Result = m_port_predicate.WaitForValueNotEqualTo(0, timeout); + return Result ? *Result : 0; +} + +bool ConnectionFileDescriptor::GetChildProcessesInherit() const { + return m_child_processes_inherit; +} + +void ConnectionFileDescriptor::SetChildProcessesInherit( + bool child_processes_inherit) { + m_child_processes_inherit = child_processes_inherit; +} + +void ConnectionFileDescriptor::InitializeSocket(Socket *socket) { + m_write_sp.reset(socket); + m_read_sp = m_write_sp; + m_uri = socket->GetRemoteConnectionURI(); +} diff --git a/gnu/llvm/lldb/source/Host/posix/DomainSocket.cpp b/gnu/llvm/lldb/source/Host/posix/DomainSocket.cpp new file mode 100644 index 00000000000..7b7d8f085e5 --- /dev/null +++ b/gnu/llvm/lldb/source/Host/posix/DomainSocket.cpp @@ -0,0 +1,155 @@ +//===-- DomainSocket.cpp ----------------------------------------*- 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 +// +//===----------------------------------------------------------------------===// + +#include "lldb/Host/posix/DomainSocket.h" + +#include "llvm/Support/Errno.h" +#include "llvm/Support/FileSystem.h" + +#include <stddef.h> +#include <sys/socket.h> +#include <sys/un.h> + +using namespace lldb; +using namespace lldb_private; + +#ifdef __ANDROID__ +// Android does not have SUN_LEN +#ifndef SUN_LEN +#define SUN_LEN(ptr) \ + (offsetof(struct sockaddr_un, sun_path) + strlen((ptr)->sun_path)) +#endif +#endif // #ifdef __ANDROID__ + +namespace { + +const int kDomain = AF_UNIX; +const int kType = SOCK_STREAM; + +bool SetSockAddr(llvm::StringRef name, const size_t name_offset, + sockaddr_un *saddr_un, socklen_t &saddr_un_len) { + if (name.size() + name_offset > sizeof(saddr_un->sun_path)) + return false; + + memset(saddr_un, 0, sizeof(*saddr_un)); + saddr_un->sun_family = kDomain; + + memcpy(saddr_un->sun_path + name_offset, name.data(), name.size()); + + // For domain sockets we can use SUN_LEN in order to calculate size of + // sockaddr_un, but for abstract sockets we have to calculate size manually + // because of leading null symbol. + if (name_offset == 0) + saddr_un_len = SUN_LEN(saddr_un); + else + saddr_un_len = + offsetof(struct sockaddr_un, sun_path) + name_offset + name.size(); + +#if defined(__APPLE__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) + saddr_un->sun_len = saddr_un_len; +#endif + + return true; +} +} // namespace + +DomainSocket::DomainSocket(bool should_close, bool child_processes_inherit) + : Socket(ProtocolUnixDomain, should_close, child_processes_inherit) {} + +DomainSocket::DomainSocket(SocketProtocol protocol, + bool child_processes_inherit) + : Socket(protocol, true, child_processes_inherit) {} + +DomainSocket::DomainSocket(NativeSocket socket, + const DomainSocket &listen_socket) + : Socket(ProtocolUnixDomain, listen_socket.m_should_close_fd, + listen_socket.m_child_processes_inherit) { + m_socket = socket; +} + +Status DomainSocket::Connect(llvm::StringRef name) { + sockaddr_un saddr_un; + socklen_t saddr_un_len; + if (!SetSockAddr(name, GetNameOffset(), &saddr_un, saddr_un_len)) + return Status("Failed to set socket address"); + + Status error; + m_socket = CreateSocket(kDomain, kType, 0, m_child_processes_inherit, error); + if (error.Fail()) + return error; + if (llvm::sys::RetryAfterSignal(-1, ::connect, GetNativeSocket(), + (struct sockaddr *)&saddr_un, saddr_un_len) < 0) + SetLastError(error); + + return error; +} + +Status DomainSocket::Listen(llvm::StringRef name, int backlog) { + sockaddr_un saddr_un; + socklen_t saddr_un_len; + if (!SetSockAddr(name, GetNameOffset(), &saddr_un, saddr_un_len)) + return Status("Failed to set socket address"); + + DeleteSocketFile(name); + + Status error; + m_socket = CreateSocket(kDomain, kType, 0, m_child_processes_inherit, error); + if (error.Fail()) + return error; + if (::bind(GetNativeSocket(), (struct sockaddr *)&saddr_un, saddr_un_len) == + 0) + if (::listen(GetNativeSocket(), backlog) == 0) + return error; + + SetLastError(error); + return error; +} + +Status DomainSocket::Accept(Socket *&socket) { + Status error; + auto conn_fd = AcceptSocket(GetNativeSocket(), nullptr, nullptr, + m_child_processes_inherit, error); + if (error.Success()) + socket = new DomainSocket(conn_fd, *this); + + return error; +} + +size_t DomainSocket::GetNameOffset() const { return 0; } + +void DomainSocket::DeleteSocketFile(llvm::StringRef name) { + llvm::sys::fs::remove(name); +} + +std::string DomainSocket::GetSocketName() const { + if (m_socket != kInvalidSocketValue) { + struct sockaddr_un saddr_un; + saddr_un.sun_family = AF_UNIX; + socklen_t sock_addr_len = sizeof(struct sockaddr_un); + if (::getpeername(m_socket, (struct sockaddr *)&saddr_un, &sock_addr_len) == + 0) { + std::string name(saddr_un.sun_path + GetNameOffset(), + sock_addr_len - + offsetof(struct sockaddr_un, sun_path) - + GetNameOffset()); + if (name.back() == '\0') name.pop_back(); + return name; + } + } + return ""; +} + +std::string DomainSocket::GetRemoteConnectionURI() const { + if (m_socket != kInvalidSocketValue) { + return llvm::formatv("{0}://{1}", + GetNameOffset() == 0 ? "unix-connect" + : "unix-abstract-connect", + GetSocketName()); + } + return ""; +} diff --git a/gnu/llvm/lldb/source/Host/posix/FileSystemPosix.cpp b/gnu/llvm/lldb/source/Host/posix/FileSystemPosix.cpp new file mode 100644 index 00000000000..32fae68abb4 --- /dev/null +++ b/gnu/llvm/lldb/source/Host/posix/FileSystemPosix.cpp @@ -0,0 +1,80 @@ +//===-- FileSystem.cpp ------------------------------------------*- 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 +// +//===----------------------------------------------------------------------===// + +#include "lldb/Host/FileSystem.h" + +// C includes +#include <dirent.h> +#include <fcntl.h> +#include <sys/mount.h> +#include <sys/param.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <unistd.h> +#if defined(__NetBSD__) +#include <sys/statvfs.h> +#endif + +// lldb Includes +#include "lldb/Host/Host.h" +#include "lldb/Utility/Status.h" +#include "lldb/Utility/StreamString.h" + +#include "llvm/Support/Errno.h" +#include "llvm/Support/FileSystem.h" + +using namespace lldb; +using namespace lldb_private; + +const char *FileSystem::DEV_NULL = "/dev/null"; + +Status FileSystem::Symlink(const FileSpec &src, const FileSpec &dst) { + Status error; + if (::symlink(dst.GetCString(), src.GetCString()) == -1) + error.SetErrorToErrno(); + return error; +} + +Status FileSystem::Readlink(const FileSpec &src, FileSpec &dst) { + Status error; + char buf[PATH_MAX]; + ssize_t count = ::readlink(src.GetCString(), buf, sizeof(buf) - 1); + if (count < 0) + error.SetErrorToErrno(); + else { + buf[count] = '\0'; // Success + dst.SetFile(buf, FileSpec::Style::native); + } + return error; +} + +Status FileSystem::ResolveSymbolicLink(const FileSpec &src, FileSpec &dst) { + char resolved_path[PATH_MAX]; + if (!src.GetPath(resolved_path, sizeof(resolved_path))) { + return Status("Couldn't get the canonical path for %s", src.GetCString()); + } + + char real_path[PATH_MAX + 1]; + if (realpath(resolved_path, real_path) == nullptr) { + Status err; + err.SetErrorToErrno(); + return err; + } + + dst = FileSpec(real_path); + + return Status(); +} + +FILE *FileSystem::Fopen(const char *path, const char *mode) { + return llvm::sys::RetryAfterSignal(nullptr, ::fopen, path, mode); +} + +int FileSystem::Open(const char *path, int flags, int mode) { + return llvm::sys::RetryAfterSignal(-1, ::open, path, flags, mode); +} diff --git a/gnu/llvm/lldb/source/Host/posix/HostInfoPosix.cpp b/gnu/llvm/lldb/source/Host/posix/HostInfoPosix.cpp new file mode 100644 index 00000000000..63cc5dc65e0 --- /dev/null +++ b/gnu/llvm/lldb/source/Host/posix/HostInfoPosix.cpp @@ -0,0 +1,149 @@ +//===-- HostInfoPosix.cpp ---------------------------------------*- 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 +// +//===----------------------------------------------------------------------===// + +#include "lldb/Host/posix/HostInfoPosix.h" +#include "lldb/Utility/Log.h" +#include "lldb/Utility/UserIDResolver.h" + +#include "llvm/ADT/SmallString.h" +#include "llvm/ADT/Twine.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/raw_ostream.h" + +#include <grp.h> +#include <limits.h> +#include <mutex> +#include <pwd.h> +#include <stdlib.h> +#include <sys/types.h> +#include <unistd.h> + +using namespace lldb_private; + +size_t HostInfoPosix::GetPageSize() { return ::getpagesize(); } + +bool HostInfoPosix::GetHostname(std::string &s) { + char hostname[PATH_MAX]; + hostname[sizeof(hostname) - 1] = '\0'; + if (::gethostname(hostname, sizeof(hostname) - 1) == 0) { + s.assign(hostname); + return true; + } + return false; +} + +#ifdef __ANDROID__ +#include <android/api-level.h> +#endif +#if defined(__ANDROID_API__) && __ANDROID_API__ < 21 +#define USE_GETPWUID +#endif + +namespace { +class PosixUserIDResolver : public UserIDResolver { +protected: + llvm::Optional<std::string> DoGetUserName(id_t uid) override; + llvm::Optional<std::string> DoGetGroupName(id_t gid) override; +}; +} // namespace + +struct PasswdEntry { + std::string username; + std::string shell; +}; + +static llvm::Optional<PasswdEntry> GetPassword(id_t uid) { +#ifdef USE_GETPWUID + // getpwuid_r is missing from android-9 + // The caller should provide some thread safety by making sure no one calls + // this function concurrently, because using getpwuid is ultimately not + // thread-safe as we don't know who else might be calling it. + if (auto *user_info_ptr = ::getpwuid(uid)) + return PasswdEntry{user_info_ptr->pw_name, user_info_ptr->pw_shell}; +#else + struct passwd user_info; + struct passwd *user_info_ptr = &user_info; + char user_buffer[PATH_MAX]; + size_t user_buffer_size = sizeof(user_buffer); + if (::getpwuid_r(uid, &user_info, user_buffer, user_buffer_size, + &user_info_ptr) == 0 && + user_info_ptr) { + return PasswdEntry{user_info_ptr->pw_name, user_info_ptr->pw_shell}; + } +#endif + return llvm::None; +} + +llvm::Optional<std::string> PosixUserIDResolver::DoGetUserName(id_t uid) { + if (llvm::Optional<PasswdEntry> password = GetPassword(uid)) + return password->username; + return llvm::None; +} + +llvm::Optional<std::string> PosixUserIDResolver::DoGetGroupName(id_t gid) { +#ifndef __ANDROID__ + char group_buffer[PATH_MAX]; + size_t group_buffer_size = sizeof(group_buffer); + struct group group_info; + struct group *group_info_ptr = &group_info; + // Try the threadsafe version first + if (::getgrgid_r(gid, &group_info, group_buffer, group_buffer_size, + &group_info_ptr) == 0) { + if (group_info_ptr) + return std::string(group_info_ptr->gr_name); + } else { + // The threadsafe version isn't currently working for me on darwin, but the + // non-threadsafe version is, so I am calling it below. + group_info_ptr = ::getgrgid(gid); + if (group_info_ptr) + return std::string(group_info_ptr->gr_name); + } +#endif + return llvm::None; +} + +static llvm::ManagedStatic<PosixUserIDResolver> g_user_id_resolver; + +UserIDResolver &HostInfoPosix::GetUserIDResolver() { + return *g_user_id_resolver; +} + +uint32_t HostInfoPosix::GetUserID() { return getuid(); } + +uint32_t HostInfoPosix::GetGroupID() { return getgid(); } + +uint32_t HostInfoPosix::GetEffectiveUserID() { return geteuid(); } + +uint32_t HostInfoPosix::GetEffectiveGroupID() { return getegid(); } + +FileSpec HostInfoPosix::GetDefaultShell() { + if (const char *v = ::getenv("SHELL")) + return FileSpec(v); + if (llvm::Optional<PasswdEntry> password = GetPassword(::geteuid())) + return FileSpec(password->shell); + return FileSpec("/bin/sh"); +} + +bool HostInfoPosix::ComputeSupportExeDirectory(FileSpec &file_spec) { + return ComputePathRelativeToLibrary(file_spec, "/bin"); +} + +bool HostInfoPosix::ComputeHeaderDirectory(FileSpec &file_spec) { + FileSpec temp_file("/opt/local/include/lldb"); + file_spec.GetDirectory().SetCString(temp_file.GetPath().c_str()); + return true; +} + +bool HostInfoPosix::GetEnvironmentVar(const std::string &var_name, + std::string &var) { + if (const char *pvar = ::getenv(var_name.c_str())) { + var = std::string(pvar); + return true; + } + return false; +} diff --git a/gnu/llvm/lldb/source/Host/posix/HostProcessPosix.cpp b/gnu/llvm/lldb/source/Host/posix/HostProcessPosix.cpp new file mode 100644 index 00000000000..cc187d44246 --- /dev/null +++ b/gnu/llvm/lldb/source/Host/posix/HostProcessPosix.cpp @@ -0,0 +1,94 @@ +//===-- HostProcessPosix.cpp ------------------------------------*- 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 +// +//===----------------------------------------------------------------------===// + +#include "lldb/Host/Host.h" +#include "lldb/Host/FileSystem.h" +#include "lldb/Host/posix/HostProcessPosix.h" + +#include "llvm/ADT/STLExtras.h" + +#include <csignal> +#include <limits.h> +#include <unistd.h> + +using namespace lldb_private; + +namespace { +const int kInvalidPosixProcess = 0; +} + +HostProcessPosix::HostProcessPosix() + : HostNativeProcessBase(kInvalidPosixProcess) {} + +HostProcessPosix::HostProcessPosix(lldb::process_t process) + : HostNativeProcessBase(process) {} + +HostProcessPosix::~HostProcessPosix() {} + +Status HostProcessPosix::Signal(int signo) const { + if (m_process == kInvalidPosixProcess) { + Status error; + error.SetErrorString("HostProcessPosix refers to an invalid process"); + return error; + } + + return HostProcessPosix::Signal(m_process, signo); +} + +Status HostProcessPosix::Signal(lldb::process_t process, int signo) { + Status error; + + if (-1 == ::kill(process, signo)) + error.SetErrorToErrno(); + + return error; +} + +Status HostProcessPosix::Terminate() { return Signal(SIGKILL); } + +Status HostProcessPosix::GetMainModule(FileSpec &file_spec) const { + Status error; + + // Use special code here because proc/[pid]/exe is a symbolic link. + char link_path[PATH_MAX]; + if (snprintf(link_path, PATH_MAX, "/proc/%" PRIu64 "/exe", m_process) != 1) { + error.SetErrorString("Unable to build /proc/<pid>/exe string"); + return error; + } + + error = FileSystem::Instance().Readlink(FileSpec(link_path), file_spec); + if (!error.Success()) + return error; + + // If the binary has been deleted, the link name has " (deleted)" appended. + // Remove if there. + if (file_spec.GetFilename().GetStringRef().endswith(" (deleted)")) { + const char *filename = file_spec.GetFilename().GetCString(); + static const size_t deleted_len = strlen(" (deleted)"); + const size_t len = file_spec.GetFilename().GetLength(); + file_spec.GetFilename().SetCStringWithLength(filename, len - deleted_len); + } + return error; +} + +lldb::pid_t HostProcessPosix::GetProcessId() const { return m_process; } + +bool HostProcessPosix::IsRunning() const { + if (m_process == kInvalidPosixProcess) + return false; + + // Send this process the null signal. If it succeeds the process is running. + Status error = Signal(0); + return error.Success(); +} + +llvm::Expected<HostThread> HostProcessPosix::StartMonitoring( + const Host::MonitorChildProcessCallback &callback, bool monitor_signals) { + return Host::StartMonitoringChildProcess(callback, m_process, + monitor_signals); +} diff --git a/gnu/llvm/lldb/source/Host/posix/HostThreadPosix.cpp b/gnu/llvm/lldb/source/Host/posix/HostThreadPosix.cpp new file mode 100644 index 00000000000..d78bba517f6 --- /dev/null +++ b/gnu/llvm/lldb/source/Host/posix/HostThreadPosix.cpp @@ -0,0 +1,61 @@ +//===-- HostThreadPosix.cpp -------------------------------------*- 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 +// +//===----------------------------------------------------------------------===// + +#include "lldb/Host/posix/HostThreadPosix.h" +#include "lldb/Utility/Status.h" + +#include <errno.h> +#include <pthread.h> + +using namespace lldb; +using namespace lldb_private; + +HostThreadPosix::HostThreadPosix() {} + +HostThreadPosix::HostThreadPosix(lldb::thread_t thread) + : HostNativeThreadBase(thread) {} + +HostThreadPosix::~HostThreadPosix() {} + +Status HostThreadPosix::Join(lldb::thread_result_t *result) { + Status error; + if (IsJoinable()) { + int err = ::pthread_join(m_thread, result); + error.SetError(err, lldb::eErrorTypePOSIX); + } else { + if (result) + *result = nullptr; + error.SetError(EINVAL, eErrorTypePOSIX); + } + + Reset(); + return error; +} + +Status HostThreadPosix::Cancel() { + Status error; + if (IsJoinable()) { +#ifndef __FreeBSD__ + llvm_unreachable("someone is calling HostThread::Cancel()"); +#else + int err = ::pthread_cancel(m_thread); + error.SetError(err, eErrorTypePOSIX); +#endif + } + return error; +} + +Status HostThreadPosix::Detach() { + Status error; + if (IsJoinable()) { + int err = ::pthread_detach(m_thread); + error.SetError(err, eErrorTypePOSIX); + } + Reset(); + return error; +} diff --git a/gnu/llvm/lldb/source/Host/posix/LockFilePosix.cpp b/gnu/llvm/lldb/source/Host/posix/LockFilePosix.cpp new file mode 100644 index 00000000000..a6eae95c333 --- /dev/null +++ b/gnu/llvm/lldb/source/Host/posix/LockFilePosix.cpp @@ -0,0 +1,62 @@ +//===-- LockFilePosix.cpp ---------------------------------------*- 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 +// +//===----------------------------------------------------------------------===// + +#include "lldb/Host/posix/LockFilePosix.h" + +#include "llvm/Support/Errno.h" + +#include <fcntl.h> +#include <unistd.h> + +using namespace lldb; +using namespace lldb_private; + +namespace { + +Status fileLock(int fd, int cmd, int lock_type, const uint64_t start, + const uint64_t len) { + struct flock fl; + + fl.l_type = lock_type; + fl.l_whence = SEEK_SET; + fl.l_start = start; + fl.l_len = len; + fl.l_pid = ::getpid(); + + Status error; + if (llvm::sys::RetryAfterSignal(-1, ::fcntl, fd, cmd, &fl) == -1) + error.SetErrorToErrno(); + + return error; +} + +} // namespace + +LockFilePosix::LockFilePosix(int fd) : LockFileBase(fd) {} + +LockFilePosix::~LockFilePosix() { Unlock(); } + +Status LockFilePosix::DoWriteLock(const uint64_t start, const uint64_t len) { + return fileLock(m_fd, F_SETLKW, F_WRLCK, start, len); +} + +Status LockFilePosix::DoTryWriteLock(const uint64_t start, const uint64_t len) { + return fileLock(m_fd, F_SETLK, F_WRLCK, start, len); +} + +Status LockFilePosix::DoReadLock(const uint64_t start, const uint64_t len) { + return fileLock(m_fd, F_SETLKW, F_RDLCK, start, len); +} + +Status LockFilePosix::DoTryReadLock(const uint64_t start, const uint64_t len) { + return fileLock(m_fd, F_SETLK, F_RDLCK, start, len); +} + +Status LockFilePosix::DoUnlock() { + return fileLock(m_fd, F_SETLK, F_UNLCK, m_start, m_len); +} diff --git a/gnu/llvm/lldb/source/Host/posix/PipePosix.cpp b/gnu/llvm/lldb/source/Host/posix/PipePosix.cpp new file mode 100644 index 00000000000..3558ebcdc96 --- /dev/null +++ b/gnu/llvm/lldb/source/Host/posix/PipePosix.cpp @@ -0,0 +1,318 @@ +//===-- PipePosix.cpp -------------------------------------------*- 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 +// +//===----------------------------------------------------------------------===// + +#include "lldb/Host/posix/PipePosix.h" +#include "lldb/Host/HostInfo.h" +#include "lldb/Utility/SelectHelper.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/Support/Errno.h" +#include "llvm/Support/FileSystem.h" + +#if defined(__GNUC__) && (__GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 8)) +#ifndef _GLIBCXX_USE_NANOSLEEP +#define _GLIBCXX_USE_NANOSLEEP +#endif +#endif + +#include <functional> +#include <thread> + +#include <errno.h> +#include <fcntl.h> +#include <limits.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <unistd.h> + +using namespace lldb; +using namespace lldb_private; + +int PipePosix::kInvalidDescriptor = -1; + +enum PIPES { READ, WRITE }; // Constants 0 and 1 for READ and WRITE + +// pipe2 is supported by a limited set of platforms +// TODO: Add more platforms that support pipe2. +#if defined(__linux__) || (defined(__FreeBSD__) && __FreeBSD__ >= 10) || \ + defined(__NetBSD__) || defined(__OpenBSD__) +#define PIPE2_SUPPORTED 1 +#else +#define PIPE2_SUPPORTED 0 +#endif + +namespace { + +constexpr auto OPEN_WRITER_SLEEP_TIMEOUT_MSECS = 100; + +#if defined(FD_CLOEXEC) && !PIPE2_SUPPORTED +bool SetCloexecFlag(int fd) { + int flags = ::fcntl(fd, F_GETFD); + if (flags == -1) + return false; + return (::fcntl(fd, F_SETFD, flags | FD_CLOEXEC) == 0); +} +#endif + +std::chrono::time_point<std::chrono::steady_clock> Now() { + return std::chrono::steady_clock::now(); +} +} // namespace + +PipePosix::PipePosix() + : m_fds{PipePosix::kInvalidDescriptor, PipePosix::kInvalidDescriptor} {} + +PipePosix::PipePosix(lldb::pipe_t read, lldb::pipe_t write) + : m_fds{read, write} {} + +PipePosix::PipePosix(PipePosix &&pipe_posix) + : PipeBase{std::move(pipe_posix)}, + m_fds{pipe_posix.ReleaseReadFileDescriptor(), + pipe_posix.ReleaseWriteFileDescriptor()} {} + +PipePosix &PipePosix::operator=(PipePosix &&pipe_posix) { + PipeBase::operator=(std::move(pipe_posix)); + m_fds[READ] = pipe_posix.ReleaseReadFileDescriptor(); + m_fds[WRITE] = pipe_posix.ReleaseWriteFileDescriptor(); + return *this; +} + +PipePosix::~PipePosix() { Close(); } + +Status PipePosix::CreateNew(bool child_processes_inherit) { + if (CanRead() || CanWrite()) + return Status(EINVAL, eErrorTypePOSIX); + + Status error; +#if PIPE2_SUPPORTED + if (::pipe2(m_fds, (child_processes_inherit) ? 0 : O_CLOEXEC) == 0) + return error; +#else + if (::pipe(m_fds) == 0) { +#ifdef FD_CLOEXEC + if (!child_processes_inherit) { + if (!SetCloexecFlag(m_fds[0]) || !SetCloexecFlag(m_fds[1])) { + error.SetErrorToErrno(); + Close(); + return error; + } + } +#endif + return error; + } +#endif + + error.SetErrorToErrno(); + m_fds[READ] = PipePosix::kInvalidDescriptor; + m_fds[WRITE] = PipePosix::kInvalidDescriptor; + return error; +} + +Status PipePosix::CreateNew(llvm::StringRef name, bool child_process_inherit) { + if (CanRead() || CanWrite()) + return Status("Pipe is already opened"); + + Status error; + if (::mkfifo(name.data(), 0660) != 0) + error.SetErrorToErrno(); + + return error; +} + +Status PipePosix::CreateWithUniqueName(llvm::StringRef prefix, + bool child_process_inherit, + llvm::SmallVectorImpl<char> &name) { + llvm::SmallString<128> named_pipe_path; + llvm::SmallString<128> pipe_spec((prefix + ".%%%%%%").str()); + FileSpec tmpdir_file_spec = HostInfo::GetProcessTempDir(); + if (!tmpdir_file_spec) + tmpdir_file_spec.AppendPathComponent("/tmp"); + tmpdir_file_spec.AppendPathComponent(pipe_spec); + + // It's possible that another process creates the target path after we've + // verified it's available but before we create it, in which case we should + // try again. + Status error; + do { + llvm::sys::fs::createUniqueFile(tmpdir_file_spec.GetPath(), + named_pipe_path); + error = CreateNew(named_pipe_path, child_process_inherit); + } while (error.GetError() == EEXIST); + + if (error.Success()) + name = named_pipe_path; + return error; +} + +Status PipePosix::OpenAsReader(llvm::StringRef name, + bool child_process_inherit) { + if (CanRead() || CanWrite()) + return Status("Pipe is already opened"); + + int flags = O_RDONLY | O_NONBLOCK; + if (!child_process_inherit) + flags |= O_CLOEXEC; + + Status error; + int fd = llvm::sys::RetryAfterSignal(-1, ::open, name.data(), flags); + if (fd != -1) + m_fds[READ] = fd; + else + error.SetErrorToErrno(); + + return error; +} + +Status +PipePosix::OpenAsWriterWithTimeout(llvm::StringRef name, + bool child_process_inherit, + const std::chrono::microseconds &timeout) { + if (CanRead() || CanWrite()) + return Status("Pipe is already opened"); + + int flags = O_WRONLY | O_NONBLOCK; + if (!child_process_inherit) + flags |= O_CLOEXEC; + + using namespace std::chrono; + const auto finish_time = Now() + timeout; + + while (!CanWrite()) { + if (timeout != microseconds::zero()) { + const auto dur = duration_cast<microseconds>(finish_time - Now()).count(); + if (dur <= 0) + return Status("timeout exceeded - reader hasn't opened so far"); + } + + errno = 0; + int fd = ::open(name.data(), flags); + if (fd == -1) { + const auto errno_copy = errno; + // We may get ENXIO if a reader side of the pipe hasn't opened yet. + if (errno_copy != ENXIO && errno_copy != EINTR) + return Status(errno_copy, eErrorTypePOSIX); + + std::this_thread::sleep_for( + milliseconds(OPEN_WRITER_SLEEP_TIMEOUT_MSECS)); + } else { + m_fds[WRITE] = fd; + } + } + + return Status(); +} + +int PipePosix::GetReadFileDescriptor() const { return m_fds[READ]; } + +int PipePosix::GetWriteFileDescriptor() const { return m_fds[WRITE]; } + +int PipePosix::ReleaseReadFileDescriptor() { + const int fd = m_fds[READ]; + m_fds[READ] = PipePosix::kInvalidDescriptor; + return fd; +} + +int PipePosix::ReleaseWriteFileDescriptor() { + const int fd = m_fds[WRITE]; + m_fds[WRITE] = PipePosix::kInvalidDescriptor; + return fd; +} + +void PipePosix::Close() { + CloseReadFileDescriptor(); + CloseWriteFileDescriptor(); +} + +Status PipePosix::Delete(llvm::StringRef name) { + return llvm::sys::fs::remove(name); +} + +bool PipePosix::CanRead() const { + return m_fds[READ] != PipePosix::kInvalidDescriptor; +} + +bool PipePosix::CanWrite() const { + return m_fds[WRITE] != PipePosix::kInvalidDescriptor; +} + +void PipePosix::CloseReadFileDescriptor() { + if (CanRead()) { + close(m_fds[READ]); + m_fds[READ] = PipePosix::kInvalidDescriptor; + } +} + +void PipePosix::CloseWriteFileDescriptor() { + if (CanWrite()) { + close(m_fds[WRITE]); + m_fds[WRITE] = PipePosix::kInvalidDescriptor; + } +} + +Status PipePosix::ReadWithTimeout(void *buf, size_t size, + const std::chrono::microseconds &timeout, + size_t &bytes_read) { + bytes_read = 0; + if (!CanRead()) + return Status(EINVAL, eErrorTypePOSIX); + + const int fd = GetReadFileDescriptor(); + + SelectHelper select_helper; + select_helper.SetTimeout(timeout); + select_helper.FDSetRead(fd); + + Status error; + while (error.Success()) { + error = select_helper.Select(); + if (error.Success()) { + auto result = + ::read(fd, static_cast<char *>(buf) + bytes_read, size - bytes_read); + if (result != -1) { + bytes_read += result; + if (bytes_read == size || result == 0) + break; + } else if (errno == EINTR) { + continue; + } else { + error.SetErrorToErrno(); + break; + } + } + } + return error; +} + +Status PipePosix::Write(const void *buf, size_t size, size_t &bytes_written) { + bytes_written = 0; + if (!CanWrite()) + return Status(EINVAL, eErrorTypePOSIX); + + const int fd = GetWriteFileDescriptor(); + SelectHelper select_helper; + select_helper.SetTimeout(std::chrono::seconds(0)); + select_helper.FDSetWrite(fd); + + Status error; + while (error.Success()) { + error = select_helper.Select(); + if (error.Success()) { + auto result = ::write(fd, static_cast<const char *>(buf) + bytes_written, + size - bytes_written); + if (result != -1) { + bytes_written += result; + if (bytes_written == size) + break; + } else if (errno == EINTR) { + continue; + } else { + error.SetErrorToErrno(); + } + } + } + return error; +} diff --git a/gnu/llvm/lldb/source/Host/posix/ProcessLauncherPosixFork.cpp b/gnu/llvm/lldb/source/Host/posix/ProcessLauncherPosixFork.cpp new file mode 100644 index 00000000000..185c7f0fe24 --- /dev/null +++ b/gnu/llvm/lldb/source/Host/posix/ProcessLauncherPosixFork.cpp @@ -0,0 +1,218 @@ +//===-- ProcessLauncherPosixFork.cpp ----------------------------*- 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 +// +//===----------------------------------------------------------------------===// + +#include "lldb/Host/posix/ProcessLauncherPosixFork.h" +#include "lldb/Host/Host.h" +#include "lldb/Host/HostProcess.h" +#include "lldb/Host/Pipe.h" +#include "lldb/Host/ProcessLaunchInfo.h" +#include "lldb/Utility/FileSpec.h" +#include "lldb/Utility/Log.h" +#include "llvm/Support/Errno.h" + +#include <limits.h> +#include <sys/ptrace.h> +#include <sys/wait.h> +#include <unistd.h> + +#include <sstream> +#include <csignal> + +#ifdef __ANDROID__ +#include <android/api-level.h> +#define PT_TRACE_ME PTRACE_TRACEME +#endif + +#if defined(__ANDROID_API__) && __ANDROID_API__ < 15 +#include <linux/personality.h> +#elif defined(__linux__) +#include <sys/personality.h> +#endif + +using namespace lldb; +using namespace lldb_private; + +static void FixupEnvironment(Environment &env) { +#ifdef __ANDROID__ + // If there is no PATH variable specified inside the environment then set the + // path to /system/bin. It is required because the default path used by + // execve() is wrong on android. + env.try_emplace("PATH", "/system/bin"); +#endif +} + +static void LLVM_ATTRIBUTE_NORETURN ExitWithError(int error_fd, + const char *operation) { + int err = errno; + llvm::raw_fd_ostream os(error_fd, true); + os << operation << " failed: " << llvm::sys::StrError(err); + os.flush(); + _exit(1); +} + +static void DisableASLRIfRequested(int error_fd, const ProcessLaunchInfo &info) { +#if defined(__linux__) + if (info.GetFlags().Test(lldb::eLaunchFlagDisableASLR)) { + const unsigned long personality_get_current = 0xffffffff; + int value = personality(personality_get_current); + if (value == -1) + ExitWithError(error_fd, "personality get"); + + value = personality(ADDR_NO_RANDOMIZE | value); + if (value == -1) + ExitWithError(error_fd, "personality set"); + } +#endif +} + +static void DupDescriptor(int error_fd, const FileSpec &file_spec, int fd, + int flags) { + int target_fd = llvm::sys::RetryAfterSignal(-1, ::open, + file_spec.GetCString(), flags, 0666); + + if (target_fd == -1) + ExitWithError(error_fd, "DupDescriptor-open"); + + if (target_fd == fd) + return; + + if (::dup2(target_fd, fd) == -1) + ExitWithError(error_fd, "DupDescriptor-dup2"); + + ::close(target_fd); + return; +} + +static void LLVM_ATTRIBUTE_NORETURN ChildFunc(int error_fd, + const ProcessLaunchInfo &info) { + if (info.GetFlags().Test(eLaunchFlagLaunchInSeparateProcessGroup)) { + if (setpgid(0, 0) != 0) + ExitWithError(error_fd, "setpgid"); + } + + for (size_t i = 0; i < info.GetNumFileActions(); ++i) { + const FileAction &action = *info.GetFileActionAtIndex(i); + switch (action.GetAction()) { + case FileAction::eFileActionClose: + if (close(action.GetFD()) != 0) + ExitWithError(error_fd, "close"); + break; + case FileAction::eFileActionDuplicate: + if (dup2(action.GetFD(), action.GetActionArgument()) == -1) + ExitWithError(error_fd, "dup2"); + break; + case FileAction::eFileActionOpen: + DupDescriptor(error_fd, action.GetFileSpec(), action.GetFD(), + action.GetActionArgument()); + break; + case FileAction::eFileActionNone: + break; + } + } + + const char **argv = info.GetArguments().GetConstArgumentVector(); + + // Change working directory + if (info.GetWorkingDirectory() && + 0 != ::chdir(info.GetWorkingDirectory().GetCString())) + ExitWithError(error_fd, "chdir"); + + DisableASLRIfRequested(error_fd, info); + Environment env = info.GetEnvironment(); + FixupEnvironment(env); + Environment::Envp envp = env.getEnvp(); + + // Clear the signal mask to prevent the child from being affected by any + // masking done by the parent. + sigset_t set; + if (sigemptyset(&set) != 0 || + pthread_sigmask(SIG_SETMASK, &set, nullptr) != 0) + ExitWithError(error_fd, "pthread_sigmask"); + + if (info.GetFlags().Test(eLaunchFlagDebug)) { + // Do not inherit setgid powers. + if (setgid(getgid()) != 0) + ExitWithError(error_fd, "setgid"); + + // HACK: + // Close everything besides stdin, stdout, and stderr that has no file + // action to avoid leaking. Only do this when debugging, as elsewhere we + // actually rely on passing open descriptors to child processes. + for (int fd = 3; fd < sysconf(_SC_OPEN_MAX); ++fd) + if (!info.GetFileActionForFD(fd) && fd != error_fd) + close(fd); + + // Start tracing this child that is about to exec. + if (ptrace(PT_TRACE_ME, 0, nullptr, 0) == -1) + ExitWithError(error_fd, "ptrace"); + } + + // Execute. We should never return... + execve(argv[0], const_cast<char *const *>(argv), envp); + +#if defined(__linux__) + if (errno == ETXTBSY) { + // On android M and earlier we can get this error because the adb daemon + // can hold a write handle on the executable even after it has finished + // uploading it. This state lasts only a short time and happens only when + // there are many concurrent adb commands being issued, such as when + // running the test suite. (The file remains open when someone does an "adb + // shell" command in the fork() child before it has had a chance to exec.) + // Since this state should clear up quickly, wait a while and then give it + // one more go. + usleep(50000); + execve(argv[0], const_cast<char *const *>(argv), envp); + } +#endif + + // ...unless exec fails. In which case we definitely need to end the child + // here. + ExitWithError(error_fd, "execve"); +} + +HostProcess +ProcessLauncherPosixFork::LaunchProcess(const ProcessLaunchInfo &launch_info, + Status &error) { + char exe_path[PATH_MAX]; + launch_info.GetExecutableFile().GetPath(exe_path, sizeof(exe_path)); + + // A pipe used by the child process to report errors. + PipePosix pipe; + const bool child_processes_inherit = false; + error = pipe.CreateNew(child_processes_inherit); + if (error.Fail()) + return HostProcess(); + + ::pid_t pid = ::fork(); + if (pid == -1) { + // Fork failed + error.SetErrorStringWithFormatv("Fork failed with error message: {0}", + llvm::sys::StrError()); + return HostProcess(LLDB_INVALID_PROCESS_ID); + } + if (pid == 0) { + // child process + pipe.CloseReadFileDescriptor(); + ChildFunc(pipe.ReleaseWriteFileDescriptor(), launch_info); + } + + // parent process + + pipe.CloseWriteFileDescriptor(); + char buf[1000]; + int r = read(pipe.GetReadFileDescriptor(), buf, sizeof buf); + + if (r == 0) + return HostProcess(pid); // No error. We're done. + + error.SetErrorString(buf); + + llvm::sys::RetryAfterSignal(-1, waitpid, pid, nullptr, 0); + + return HostProcess(); +} |