mirror of
https://github.com/FEX-Emu/FEX.git
synced 2026-10-11 07:00:21 +02:00
Base size is now only one page in size. We will then increment that BRK size by 8MB alignments. 256MB for 32bit applications was causing some applications on the edge to run out of virtual memory I was hitting some 32bit applications that were being fairly mean with BRK. They were allocating all of BRK space then running out of virtual memory space with its mmap handler fallback after freeing BRK space. This means we now munmap BRK pages on release for the guest, similar to behaviour that Linux does. Additionally I had an application that was getting very upset that BRK wasn't actually at the end of program space. So allocate at the end of program space like expected. brk test now passes from gvisor
185 lines
6.6 KiB
C++
185 lines
6.6 KiB
C++
#include <FEXCore/Utils/LogManager.h>
|
|
#include "Common/MathUtils.h"
|
|
|
|
#include "Tests/LinuxSyscalls/Syscalls.h"
|
|
#include "Tests/LinuxSyscalls/x64/Syscalls.h"
|
|
#include "Tests/LinuxSyscalls/x32/Syscalls.h"
|
|
|
|
#include <FEXCore/Core/X86Enums.h>
|
|
#include <fcntl.h>
|
|
#include <limits.h>
|
|
#include <linux/futex.h>
|
|
#include <numaif.h>
|
|
#include <poll.h>
|
|
#include <sys/mman.h>
|
|
#include <sys/time.h>
|
|
#include <sys/random.h>
|
|
#include <sys/sysinfo.h>
|
|
#include <sys/utsname.h>
|
|
#include <sys/shm.h>
|
|
#include <sys/syscall.h>
|
|
#include <unistd.h>
|
|
|
|
namespace FEX::HLE {
|
|
SyscallHandler *_SyscallHandler{};
|
|
|
|
uint64_t SyscallHandler::HandleBRK(FEXCore::Core::InternalThreadState *Thread, void *Addr) {
|
|
std::lock_guard<std::mutex> lk(MMapMutex);
|
|
|
|
uint64_t Result;
|
|
|
|
if (Addr == nullptr) { // Just wants to get the location of the program break atm
|
|
Result = DataSpace + DataSpaceSize;
|
|
}
|
|
else {
|
|
// Allocating out data space
|
|
uint64_t NewEnd = reinterpret_cast<uint64_t>(Addr);
|
|
if (NewEnd < DataSpace) {
|
|
// Not allowed to move brk end below original start
|
|
// Set the size to zero
|
|
DataSpaceSize = 0;
|
|
}
|
|
else {
|
|
uint64_t NewSize = NewEnd - DataSpace;
|
|
uint64_t NewSizeAligned = AlignUp(NewSize, 4096);
|
|
|
|
if (NewSizeAligned < DataSpaceMaxSize) {
|
|
// If we are shrinking the brk then munmap the ranges
|
|
// That way we gain the memory back and also give the application zero pages if it allocates again
|
|
// DataspaceMaxSize is always page aligned
|
|
|
|
uint64_t RemainingSize = DataSpaceMaxSize - NewSizeAligned;
|
|
// We have pages we can unmap
|
|
munmap(reinterpret_cast<void*>(DataSpace + NewSizeAligned), RemainingSize);
|
|
DataSpaceMaxSize = NewSizeAligned;
|
|
}
|
|
else if (NewSize > DataSpaceMaxSize) {
|
|
constexpr static uint64_t SizeAlignment = 8 * 1024 * 1024;
|
|
uint64_t AllocateNewSize = AlignUp(NewSize, SizeAlignment) - DataSpaceMaxSize;
|
|
if (!Is64BitMode() &&
|
|
(DataSpace + DataSpaceMaxSize + AllocateNewSize > 0x1'0000'0000ULL)) {
|
|
// If we are 32bit and we tried going about the 32bit limit then out of memory
|
|
return DataSpace + DataSpaceSize;
|
|
}
|
|
|
|
uint64_t NewBRK = (uint64_t)mmap((void*)(DataSpace + DataSpaceMaxSize), AllocateNewSize, PROT_READ | PROT_WRITE, MAP_FIXED_NOREPLACE | MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
|
|
|
|
if (NewBRK != (DataSpace + DataSpaceMaxSize)) {
|
|
// Couldn't allocate that the region we wanted
|
|
// Can happen if MAP_FIXED_NOREPLACE isn't understood by the kernel
|
|
munmap(reinterpret_cast<void*>(NewBRK), AllocateNewSize);
|
|
NewBRK = ~0ULL;
|
|
}
|
|
|
|
if (NewBRK == ~0ULL) {
|
|
// If we couldn't allocate a new region then out of memory
|
|
return DataSpace + DataSpaceSize;
|
|
}
|
|
else {
|
|
// Increase our BRK size
|
|
DataSpaceMaxSize += AllocateNewSize;
|
|
}
|
|
}
|
|
|
|
DataSpaceSize = NewSize;
|
|
}
|
|
Result = DataSpace + DataSpaceSize;
|
|
}
|
|
return Result;
|
|
}
|
|
|
|
void SyscallHandler::DefaultProgramBreak(uint64_t Base, uint64_t Size) {
|
|
DataSpace = Base;
|
|
DataSpaceMaxSize = Size;
|
|
DataSpaceStartingSize = Size;
|
|
}
|
|
|
|
SyscallHandler::SyscallHandler(FEXCore::Context::Context *ctx, FEX::HLE::SignalDelegator *_SignalDelegation)
|
|
: FM {ctx}
|
|
, SignalDelegation {_SignalDelegation} {
|
|
FEX::HLE::_SyscallHandler = this;
|
|
HostKernelVersion = CalculateHostKernelVersion();
|
|
}
|
|
|
|
SyscallHandler::~SyscallHandler() {
|
|
munmap(reinterpret_cast<void*>(DataSpace + DataSpaceStartingSize), DataSpaceMaxSize - DataSpaceStartingSize);
|
|
}
|
|
|
|
uint32_t SyscallHandler::CalculateHostKernelVersion() {
|
|
struct utsname buf{};
|
|
if (uname(&buf) == -1) {
|
|
return 0;
|
|
}
|
|
|
|
int32_t Major{};
|
|
int32_t Minor{};
|
|
int32_t Patch{};
|
|
char Tmp{};
|
|
std::istringstream ss{buf.release};
|
|
ss >> Major;
|
|
ss.read(&Tmp, 1);
|
|
ss >> Minor;
|
|
ss.read(&Tmp, 1);
|
|
ss >> Patch;
|
|
return (Major << 24) | (Minor << 16) | Patch;
|
|
}
|
|
|
|
uint64_t SyscallHandler::HandleSyscall(FEXCore::Core::InternalThreadState *Thread, FEXCore::HLE::SyscallArguments *Args) {
|
|
auto &Def = Definitions[Args->Argument[0]];
|
|
uint64_t Result{};
|
|
switch (Def.NumArgs) {
|
|
case 0: Result = std::invoke(Def.Ptr0, Thread); break;
|
|
case 1: Result = std::invoke(Def.Ptr1, Thread, Args->Argument[1]); break;
|
|
case 2: Result = std::invoke(Def.Ptr2, Thread, Args->Argument[1], Args->Argument[2]); break;
|
|
case 3: Result = std::invoke(Def.Ptr3, Thread, Args->Argument[1], Args->Argument[2], Args->Argument[3]); break;
|
|
case 4: Result = std::invoke(Def.Ptr4, Thread, Args->Argument[1], Args->Argument[2], Args->Argument[3], Args->Argument[4]); break;
|
|
case 5: Result = std::invoke(Def.Ptr5, Thread, Args->Argument[1], Args->Argument[2], Args->Argument[3], Args->Argument[4], Args->Argument[5]); break;
|
|
case 6: Result = std::invoke(Def.Ptr6, Thread, Args->Argument[1], Args->Argument[2], Args->Argument[3], Args->Argument[4], Args->Argument[5], Args->Argument[6]); break;
|
|
// for missing syscalls
|
|
case 255: return std::invoke(Def.Ptr1, Thread, Args->Argument[0]);
|
|
default:
|
|
LogMan::Msg::A("Unhandled syscall: %d", Args->Argument[0]);
|
|
return -1;
|
|
break;
|
|
}
|
|
#ifdef DEBUG_STRACE
|
|
Strace(Args, Result);
|
|
#endif
|
|
return Result;
|
|
}
|
|
|
|
#ifdef DEBUG_STRACE
|
|
void SyscallHandler::Strace(FEXCore::HLE::SyscallArguments *Args, uint64_t Ret) {
|
|
auto &Def = Definitions[Args->Argument[0]];
|
|
switch (Def.NumArgs) {
|
|
case 0: LogMan::Msg::D(Def.StraceFmt.c_str(), Ret); break;
|
|
case 1: LogMan::Msg::D(Def.StraceFmt.c_str(), Args->Argument[1], Ret); break;
|
|
case 2: LogMan::Msg::D(Def.StraceFmt.c_str(), Args->Argument[1], Args->Argument[2], Ret); break;
|
|
case 3: LogMan::Msg::D(Def.StraceFmt.c_str(), Args->Argument[1], Args->Argument[2], Args->Argument[3], Ret); break;
|
|
case 4: LogMan::Msg::D(Def.StraceFmt.c_str(), Args->Argument[1], Args->Argument[2], Args->Argument[3], Args->Argument[4], Ret); break;
|
|
case 5: LogMan::Msg::D(Def.StraceFmt.c_str(), Args->Argument[1], Args->Argument[2], Args->Argument[3], Args->Argument[4], Args->Argument[5], Ret); break;
|
|
case 6: LogMan::Msg::D(Def.StraceFmt.c_str(), Args->Argument[1], Args->Argument[2], Args->Argument[3], Args->Argument[4], Args->Argument[5], Args->Argument[6], Ret); break;
|
|
default: break;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
FEX::HLE::SyscallHandler *CreateHandler(FEXCore::Context::OperatingMode Mode,
|
|
FEXCore::Context::Context *ctx,
|
|
FEX::HLE::SignalDelegator *_SignalDelegation,
|
|
FEXCore::CodeLoader *Loader) {
|
|
|
|
FEX::HLE::SyscallHandler *Result{};
|
|
if (Mode == FEXCore::Context::MODE_64BIT) {
|
|
Result = FEX::HLE::x64::CreateHandler(ctx, _SignalDelegation);
|
|
}
|
|
else {
|
|
Result = FEX::HLE::x32::CreateHandler(ctx, _SignalDelegation);
|
|
}
|
|
|
|
Result->SetCodeLoader(Loader);
|
|
return Result;
|
|
}
|
|
|
|
}
|