vm: fix mapping of file's last page

thanks @red_prig for investigation
This commit is contained in:
DH 2024-08-31 21:17:02 +03:00
parent ea2915467a
commit e6022c1c4c
6 changed files with 136 additions and 69 deletions

View File

@ -14,10 +14,6 @@ orbis::SysResult orbis::sys_sstk(Thread *, sint) {
orbis::SysResult orbis::sys_mmap(Thread *thread, caddr_t addr, size_t len,
sint prot, sint flags, sint fd, off_t pos) {
if (auto impl = thread->tproc->ops->mmap) {
// hack for audio control shared memory
if (len == 3880) {
return impl(thread, addr, 0x10000, prot, flags, fd, pos);
}
return impl(thread, addr, len, prot, flags, fd, pos);
}

View File

@ -8,6 +8,7 @@
#include "orbis/thread/Thread.hpp"
#include "orbis/uio.hpp"
#include "orbis/utils/Logs.hpp"
#include "rx/mem.hpp"
#include "vfs.hpp"
#include "vm.hpp"
#include <cerrno>
@ -15,6 +16,7 @@
#include <fcntl.h>
#include <filesystem>
#include <netinet/in.h>
#include <optional>
#include <span>
#include <string>
#include <sys/mman.h>
@ -25,7 +27,6 @@
#include <thread>
#include <unistd.h>
#include <vector>
#include <optional>
struct HostFile : orbis::File {
bool closeOnExit = true;
@ -339,20 +340,54 @@ static orbis::ErrorCode host_mmap(orbis::File *file, void **address,
return orbis::ErrorCode::ISDIR;
auto result =
rx::vm::map(*address, size, prot, flags, rx::vm::kMapInternalReserveOnly);
rx::vm::map(*address, size, prot, flags, rx::vm::kMapInternalReserveOnly,
hostFile->device.cast<IoDevice>().get(), offset);
if (result == (void *)-1) {
return orbis::ErrorCode::NOMEM;
}
result = ::mmap(result, size, prot & rx::vm::kMapProtCpuAll,
MAP_SHARED | MAP_FIXED, hostFile->hostFd, offset);
size = utils::alignUp(size, rx::vm::kPageSize);
result = ::mmap(
result, size, prot & rx::vm::kMapProtCpuAll,
((prot & rx::vm::kMapFlagPrivate) != 0 ? MAP_PRIVATE : MAP_SHARED) |
MAP_FIXED,
hostFile->hostFd, offset);
if (result == (void *)-1) {
auto result = convertErrno();
return result;
auto errc = convertErrno();
std::printf("Failed to map file at %p-%p\n", *address,
(char *)*address + size);
return errc;
}
std::printf("shm mapped at %p-%p\n", result, (char *)result + size);
std::printf("file mapped at %p-%p:%lx\n", result, (char *)result + size,
offset);
struct stat stat;
fstat(hostFile->hostFd, &stat);
if (stat.st_size < offset + size) {
std::size_t rest =
std::min(offset + size - stat.st_size, rx::vm::kPageSize);
if (rest > rx::mem::pageSize) {
auto fillSize =
utils::alignUp(rest, rx::mem::pageSize) - rx::mem::pageSize;
std::printf("adding dummy mapping %p-%p, file ends at %p\n",
(char *)result + size - fillSize, (char *)result + size,
(char *)result + (stat.st_size - offset));
auto ptr = ::mmap((char *)result + size - fillSize, fillSize,
prot & rx::vm::kMapProtCpuAll,
MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0);
if (ptr == (void *)-1) {
std::printf("failed to add dummy mapping %p-%p\n", result,
(char *)result + size);
}
}
}
*address = result;
return {};
@ -701,19 +736,21 @@ orbis::ErrorCode createSocket(orbis::Ref<orbis::File> *file,
return {};
}
static std::optional<std::string> findFileInDir(const std::filesystem::path &dir, const char *name) {
static std::optional<std::string>
findFileInDir(const std::filesystem::path &dir, const char *name) {
for (auto entry : std::filesystem::directory_iterator(dir)) {
auto entryName = entry.path().filename();
if (strcasecmp(entryName.c_str(), name) == 0) {
return entryName;
}
}
return{};
return {};
}
static std::optional<std::filesystem::path> toRealPath(const std::filesystem::path &inp) {
static std::optional<std::filesystem::path>
toRealPath(const std::filesystem::path &inp) {
if (inp.empty()) {
return{};
return {};
}
std::filesystem::path result;
@ -725,7 +762,7 @@ static std::optional<std::filesystem::path> toRealPath(const std::filesystem::pa
auto icaseElem = findFileInDir(result, elem.c_str());
if (!icaseElem) {
return{};
return {};
}
result /= *icaseElem;
@ -793,11 +830,13 @@ orbis::ErrorCode HostFsDevice::open(orbis::Ref<orbis::File> *file,
error = convertErrno();
if (auto icaseRealPath = toRealPath(realPath)) {
ORBIS_LOG_WARNING(__FUNCTION__, path, realPath.c_str(), icaseRealPath->c_str());
ORBIS_LOG_WARNING(__FUNCTION__, path, realPath.c_str(),
icaseRealPath->c_str());
hostFd = ::open(icaseRealPath->c_str(), realFlags, 0777);
if (hostFd < 0) {
ORBIS_LOG_ERROR("host_open failed", path, realPath.c_str(), icaseRealPath->c_str(), error);
ORBIS_LOG_ERROR("host_open failed", path, realPath.c_str(),
icaseRealPath->c_str(), error);
return convertErrno();
}
}
@ -899,7 +938,8 @@ orbis::ErrorCode HostFsDevice::rename(const char *from, const char *to,
return convertErrorCode(ec);
}
orbis::File *createHostFile(int hostFd, orbis::Ref<IoDevice> device, bool alignTruncate) {
orbis::File *createHostFile(int hostFd, orbis::Ref<IoDevice> device,
bool alignTruncate) {
auto newFile = orbis::knew<HostFile>();
newFile->hostFd = hostFd;
newFile->ops = &hostOps;

View File

@ -7,6 +7,7 @@
#include "orbis/thread/Thread.hpp"
#include "orbis/utils/Logs.hpp"
#include "orbis/utils/Rc.hpp"
#include "rx/mem.hpp"
#include <bit>
#include <cassert>
#include <cinttypes>
@ -20,32 +21,6 @@
#include <rx/MemoryTable.hpp>
namespace utils {
namespace {
void *map(void *address, std::size_t size, int prot, int flags, int fd = -1,
off_t offset = 0) {
return ::mmap(address, size, prot, flags, fd, offset);
}
void *reserve(std::size_t size) {
return map(nullptr, size, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS);
}
bool reserve(void *address, std::size_t size) {
return map(address, size, PROT_NONE,
MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED) != MAP_FAILED;
}
bool protect(void *address, std::size_t size, int prot) {
return ::mprotect(address, size, prot) == 0;
}
bool unmap(void *address, std::size_t size) {
return ::munmap(address, size) == 0;
}
} // namespace
} // namespace utils
static std::mutex g_mtx;
std::string rx::vm::mapFlagsToString(std::int32_t flags) {
@ -305,7 +280,8 @@ struct Block {
void setFlags(std::uint64_t firstPage, std::uint64_t pagesCount,
std::uint32_t flags, bool noOverwrite) {
modifyFlags(firstPage, pagesCount, flags, ~static_cast<std::uint32_t>(0), noOverwrite);
modifyFlags(firstPage, pagesCount, flags, ~static_cast<std::uint32_t>(0),
noOverwrite);
}
void addFlags(std::uint64_t firstPage, std::uint64_t pagesCount,
@ -681,7 +657,8 @@ static void reserve(std::uint64_t startAddress, std::uint64_t endAddress) {
auto pagesCount = (endAddress - startAddress + (rx::vm::kPageSize - 1)) >>
rx::vm::kPageShift;
gBlocks[blockIndex - kFirstBlock].setFlags(firstPage, pagesCount, kAllocated, false);
gBlocks[blockIndex - kFirstBlock].setFlags(firstPage, pagesCount, kAllocated,
false);
}
void rx::vm::fork(std::uint64_t pid) {
@ -711,18 +688,18 @@ void rx::vm::fork(std::uint64_t pid) {
}
if (prot & kMapProtCpuAll) {
auto mapping = utils::map(nullptr, kPageSize, PROT_WRITE, MAP_SHARED,
gMemoryShm, address - kMinAddress);
auto mapping = rx::mem::map(nullptr, kPageSize, PROT_WRITE, MAP_SHARED,
gMemoryShm, address - kMinAddress);
assert(mapping != MAP_FAILED);
utils::protect(reinterpret_cast<void *>(address), kPageSize, PROT_READ);
rx::mem::protect(reinterpret_cast<void *>(address), kPageSize, PROT_READ);
std::memcpy(mapping, reinterpret_cast<void *>(address), kPageSize);
utils::unmap(mapping, kPageSize);
utils::unmap(reinterpret_cast<void *>(address), kPageSize);
rx::mem::unmap(mapping, kPageSize);
rx::mem::unmap(reinterpret_cast<void *>(address), kPageSize);
mapping = utils::map(reinterpret_cast<void *>(address), kPageSize,
prot & kMapProtCpuAll, MAP_FIXED | MAP_SHARED,
gMemoryShm, address - kMinAddress);
mapping = rx::mem::map(reinterpret_cast<void *>(address), kPageSize,
prot & kMapProtCpuAll, MAP_FIXED | MAP_SHARED,
gMemoryShm, address - kMinAddress);
assert(mapping != MAP_FAILED);
}
@ -733,8 +710,8 @@ void rx::vm::fork(std::uint64_t pid) {
void rx::vm::reset() {
std::memset(gBlocks, 0, sizeof(gBlocks));
utils::unmap(reinterpret_cast<void *>(kMinAddress),
kMaxAddress - kMinAddress);
rx::mem::unmap(reinterpret_cast<void *>(kMinAddress),
kMaxAddress - kMinAddress);
if (::ftruncate64(gMemoryShm, 0) < 0) {
std::abort();
}
@ -743,8 +720,8 @@ void rx::vm::reset() {
}
reserve(0, kMinAddress);
utils::reserve(reinterpret_cast<void *>(kMinAddress),
kMaxAddress - kMinAddress);
rx::mem::reserve(reinterpret_cast<void *>(kMinAddress),
kMaxAddress - kMinAddress);
}
void rx::vm::initialize() {
@ -765,8 +742,8 @@ void rx::vm::initialize() {
reserve(0, kMinAddress); // unmapped area
utils::reserve(reinterpret_cast<void *>(kMinAddress),
kMaxAddress - kMinAddress);
rx::mem::reserve(reinterpret_cast<void *>(kMinAddress),
kMaxAddress - kMinAddress);
// orbis::bridge.setUpSharedMemory(kMinAddress, kMemorySize, "/orbis-memory");
}
@ -796,6 +773,7 @@ void *rx::vm::map(void *addr, std::uint64_t len, std::int32_t prot,
addr, len, mapProtToString(prot).c_str(),
mapFlagsToString(flags).c_str());
len = utils::alignUp(len, kPageSize);
auto pagesCount = (len + (kPageSize - 1)) >> kPageShift;
auto hitAddress = reinterpret_cast<std::uint64_t>(addr);
@ -820,7 +798,8 @@ void *rx::vm::map(void *addr, std::uint64_t len, std::int32_t prot,
flags &= ~kMapFlagsAlignMask;
bool noOverwrite = (flags & (kMapFlagNoOverwrite | kMapFlagFixed)) == (kMapFlagNoOverwrite | kMapFlagFixed);
bool noOverwrite = (flags & (kMapFlagNoOverwrite | kMapFlagFixed)) ==
(kMapFlagNoOverwrite | kMapFlagFixed);
if (hitAddress & (alignment - 1)) {
if (flags & kMapFlagStack) {
@ -929,8 +908,9 @@ void *rx::vm::map(void *addr, std::uint64_t len, std::int32_t prot,
}
block.setFlags((address & kBlockMask) >> kPageShift, pagesCount,
(prot & (kMapProtCpuAll | kMapProtGpuAll)) | kAllocated |
(isShared ? kShared : 0), false);
(prot & (kMapProtCpuAll | kMapProtGpuAll)) | kAllocated |
(isShared ? kShared : 0),
false);
/*
if (flags & kMapFlagStack) {
@ -962,9 +942,9 @@ void *rx::vm::map(void *addr, std::uint64_t len, std::int32_t prot,
return reinterpret_cast<void *>(address);
}
auto result =
utils::map(reinterpret_cast<void *>(address), len, prot & kMapProtCpuAll,
realFlags, gMemoryShm, address - kMinAddress);
auto result = rx::mem::map(reinterpret_cast<void *>(address), len,
prot & kMapProtCpuAll, realFlags, gMemoryShm,
address - kMinAddress);
if (result != MAP_FAILED && isAnon) {
bool needReprotect = (prot & PROT_WRITE) == 0;
@ -990,6 +970,7 @@ void *rx::vm::map(void *addr, std::uint64_t len, std::int32_t prot,
}
bool rx::vm::unmap(void *addr, std::uint64_t size) {
size = utils::alignUp(size, kPageSize);
auto pages = (size + (kPageSize - 1)) >> kPageShift;
auto address = reinterpret_cast<std::uint64_t>(addr);
@ -1021,13 +1002,14 @@ bool rx::vm::unmap(void *addr, std::uint64_t size) {
} else {
std::fprintf(stderr, "ignoring mapping %lx-%lx\n", address, address + size);
}
return utils::unmap(addr, size);
return rx::mem::unmap(addr, size);
}
bool rx::vm::protect(void *addr, std::uint64_t size, std::int32_t prot) {
std::printf("rx::vm::protect(addr = %p, len = %" PRIu64 ", prot = %s)\n",
addr, size, mapProtToString(prot).c_str());
size = utils::alignUp(size, kPageSize);
auto pages = (size + (kPageSize - 1)) >> kPageShift;
auto address = reinterpret_cast<std::uint64_t>(addr);
if (address < kMinAddress || address >= kMaxAddress || size > kMaxAddress ||

View File

@ -4,9 +4,18 @@ find_package(Git)
add_library(${PROJECT_NAME} OBJECT
src/mem.cpp
src/Version.cpp
)
target_include_directories(${PROJECT_NAME} PUBLIC include)
target_include_directories(${PROJECT_NAME}
PUBLIC
include
PRIVATE
include/${PROJECT_NAME}
)
execute_process(COMMAND date +%+4Y%m%d OUTPUT_VARIABLE RAW_VERSION)
string(STRIP "${RAW_VERSION}" RAW_VERSION)

13
rx/include/rx/mem.hpp Normal file
View File

@ -0,0 +1,13 @@
#pragma once
#include <cstddef>
namespace rx::mem {
extern const std::size_t pageSize;
void *map(void *address, std::size_t size, int prot, int flags, int fd = -1,
std::ptrdiff_t offset = 0);
void *reserve(std::size_t size);
bool reserve(void *address, std::size_t size);
bool protect(void *address, std::size_t size, int prot);
bool unmap(void *address, std::size_t size);
} // namespace rx::mem

27
rx/src/mem.cpp Normal file
View File

@ -0,0 +1,27 @@
#include "mem.hpp"
#include <sys/mman.h>
#include <unistd.h>
extern const std::size_t rx::mem::pageSize = sysconf(_SC_PAGE_SIZE);
void *rx::mem::map(void *address, std::size_t size, int prot, int flags,
int fd, std::ptrdiff_t offset) {
return ::mmap(address, size, prot, flags, fd, offset);
}
void *rx::mem::reserve(std::size_t size) {
return map(nullptr, size, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS);
}
bool rx::mem::reserve(void *address, std::size_t size) {
return map(address, size, PROT_NONE,
MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED) != MAP_FAILED;
}
bool rx::mem::protect(void *address, std::size_t size, int prot) {
return ::mprotect(address, size, prot) == 0;
}
bool rx::mem::unmap(void *address, std::size_t size) {
return ::munmap(address, size) == 0;
}