sim-se: Implement statx system call for Linux x86-64 (#887)

Implement the statx Linux-specific system call for x86-64. statx is used
by LLVM's libc.

Change-Id: Ic000a36a5e5c1399996f520fa357b9354c73c864
This commit is contained in:
Nicholas Mosier
2024-04-01 08:23:39 -07:00
committed by GitHub
parent ec690de0da
commit 00d4b6825c
3 changed files with 152 additions and 1 deletions

View File

@@ -119,6 +119,49 @@ class X86Linux64 : public X86Linux, public OpenFlagTable<X86Linux64>
int64_t unused0[3];
};
struct tgt_statx
{
/* 0x00 */
uint32_t stx_mask;
uint32_t stx_blksize;
uint64_t stx_attributes;
/* 0x10 */
uint32_t stx_nlink;
uint32_t stx_uid;
uint32_t stx_gid;
uint16_t stx_mode;
uint16_t stx_spare0;
/* 0x20 */
uint64_t stx_ino;
uint64_t stx_size;
uint64_t stx_blocks;
uint64_t stx_attributes_mask;
/* 0x40 */
uint64_t stx_atimeX;
uint32_t stx_atime_nsec;
int32_t stx_atime_reserved;
uint64_t stx_btimeX;
uint32_t stx_btime_nsec;
int32_t stx_btime_reserved;
uint64_t stx_ctimeX;
uint32_t stx_ctime_nsec;
int32_t stx_ctime_reserved;
uint64_t stx_mtimeX;
uint32_t stx_mtime_nsec;
int32_t stx_mtime_reserved;
/* 0x80 */
uint32_t stx_rdev_major;
uint32_t stx_rdev_minor;
uint32_t stx_dev_major;
uint32_t stx_dev_minor;
/* 0x90 */
uint64_t stx_mnt_id;
uint64_t stx_spare2;
/* 0xa0 */
uint64_t stx_spare3[12];
/* 0x100 */
};
struct tgt_fsid
{
long val[2];

View File

@@ -375,7 +375,7 @@ SyscallDescTable<EmuLinux::SyscallABI64> EmuLinux::syscallDescs64 = {
{ 329, "pkey_mprotect" },
{ 330, "pkey_alloc" },
{ 331, "pkey_free" },
{ 332, "statx" },
{ 332, "statx", statxFunc<X86Linux64> },
{ 333, "io_pgetevents" },
{ 334, "rseq", ignoreFunc },
{ 424, "pidfd_send_signal" },

View File

@@ -57,6 +57,7 @@
/// application on the host machine.
#if defined(__linux__)
#include <linux/kdev_t.h>
#include <sched.h>
#include <sys/eventfd.h>
#include <sys/statfs.h>
@@ -677,6 +678,73 @@ copyOutStatfsBuf(TgtStatPtr tgt, HostStatPtr host)
#endif
}
template <typename OS, typename TgtStatPtr, typename HostStatPtr>
void
copyOutStatxBuf(TgtStatPtr tgt, HostStatPtr host, bool fakeTTY = false)
{
constexpr ByteOrder bo = OS::byteOrder;
if (fakeTTY) {
tgt->stx_dev_major = MAJOR(0xA);
tgt->stx_dev_minor = MINOR(0xA);
} else {
tgt->stx_dev_major = host->stx_dev_major;
tgt->stx_dev_minor = host->stx_dev_minor;
}
tgt->stx_dev_major = htog(tgt->stx_dev_major, bo);
tgt->stx_dev_minor = htog(tgt->stx_dev_minor, bo);
tgt->stx_ino = host->stx_ino;
tgt->stx_ino = htog(tgt->stx_ino, bo);
tgt->stx_mode = host->stx_mode;
if (fakeTTY) {
// Claim to be character device.
tgt->stx_mode &= ~S_IFMT;
tgt->stx_mode |= S_IFCHR;
}
tgt->stx_mode = htog(tgt->stx_mode, bo);
tgt->stx_nlink = host->stx_nlink;
tgt->stx_nlink = htog(tgt->stx_nlink, bo);
tgt->stx_uid = host->stx_uid;
tgt->stx_uid = htog(tgt->stx_uid, bo);
tgt->stx_gid = host->stx_gid;
tgt->stx_gid = htog(tgt->stx_gid, bo);
if (fakeTTY) {
tgt->stx_rdev_major = MAJOR(0x880d);
tgt->stx_rdev_minor = MINOR(0x880d);
} else {
tgt->stx_rdev_major = host->stx_rdev_major;
tgt->stx_rdev_minor = host->stx_rdev_minor;
}
tgt->stx_rdev_major = htog(tgt->stx_rdev_major, bo);
tgt->stx_rdev_minor = htog(tgt->stx_rdev_minor, bo);
tgt->stx_size = host->stx_size;
tgt->stx_size = htog(tgt->stx_size, bo);
#define statx_copy_time(out, in) \
out##X = in.tv_sec; \
out##X = htog(out##X, bo); \
out##_nsec = in.tv_sec; \
out##_nsec = htog(out##_nsec, bo);
statx_copy_time(tgt->stx_atime, host->stx_atime);
statx_copy_time(tgt->stx_btime, host->stx_btime);
statx_copy_time(tgt->stx_ctime, host->stx_ctime);
statx_copy_time(tgt->stx_mtime, host->stx_mtime);
#undef statx_copy_time
// Force the block size to be 8KB. This helps to ensure buffered io works
// consistently across different hosts.
tgt->stx_blksize = 0x2000;
tgt->stx_blksize = htog(tgt->stx_blksize, bo);
tgt->stx_blocks = host->stx_blocks;
tgt->stx_blocks = htog(tgt->stx_blocks, bo);
tgt->stx_mask = host->stx_mask;
tgt->stx_mask = htog(tgt->stx_mask, bo);
tgt->stx_attributes = host->stx_attributes;
tgt->stx_attributes = htog(tgt->stx_attributes, bo);
tgt->stx_attributes_mask = host->stx_attributes_mask;
tgt->stx_attributes_mask = htog(tgt->stx_attributes_mask, bo);
tgt->stx_mnt_id = host->stx_mnt_id;
tgt->stx_mnt_id = htog(tgt->stx_mnt_id, bo);
}
/// Target ioctl() handler. For the most part, programs call ioctl()
/// only to find out if their stdout is a tty, to determine whether to
/// do line or block buffering. We always claim that output fds are
@@ -1456,6 +1524,46 @@ stat64Func(SyscallDesc *desc, ThreadContext *tc,
return fstatat64Func<OS>(desc, tc, OS::TGT_AT_FDCWD, pathname, tgt_stat);
}
/// Target statx() handler.
template <class OS>
SyscallReturn
statxFunc(SyscallDesc *desc, ThreadContext *tc,
int dirfd, VPtr<> pathname, int flags,
unsigned int mask, VPtr<typename OS::tgt_statx> tgt_statx)
{
std::string path;
if (!SETranslatingPortProxy(tc).tryReadString(path, pathname))
return -EFAULT;
if (path.empty() && !(flags & OS::TGT_AT_EMPTY_PATH))
return -ENOENT;
flags = flags & ~OS::TGT_AT_EMPTY_PATH;
warn_if(flags != 0, "statx: Flag bits %#x not supported.", flags);
// Modifying path from the directory descriptor
if (auto res = atSyscallPath<OS>(tc, dirfd, path); !res.successful()) {
return res;
}
auto p = tc->getProcessPtr();
// Adjust path for cwd and redirection
path = p->checkPathRedirect(path);
struct statx host_buf;
std::memset(&host_buf, 0, sizeof host_buf);
int result = statx(AT_FDCWD, path.c_str(), flags, mask, &host_buf);
if (result < 0)
return -errno;
copyOutStatxBuf<OS>(tgt_statx, &host_buf);
return 0;
}
/// Target fstat64() handler.
template <class OS>
SyscallReturn