mirror of
https://github.com/FEX-Emu/FEX.git
synced 2026-10-10 12:00:18 +02:00
When a process is in the process of forking and getting ready for execve, it is common practice to do a `close_range` or close loop to close all file descriptors before the execve. This is a security/sanitization feature to ensure that FDs aren't leaked to the child process. While it is more reliable to have these FDs opened with O_CLOEXEC, people get it wrong all the time so this feature has been put in place. Both python and glibc wrappers for launching applications do this. The problem with this for FEX is that we were using a FILE handle for emitting JIT symbols to the perf file. When the underlying FD is ripped out from under the FILE handle, it throws an assert that we can't recover from. Switching to a raw FD and checking to ensure the FD is still open on writes means that we can safely stop JIT symbol logging when a process is closing FDs under us. Fixes a crash in Steam early startup where a python script is run for checking if packages are installed.
87 lines
2.6 KiB
C++
87 lines
2.6 KiB
C++
#include "Common/JitSymbols.h"
|
|
|
|
#include <fcntl.h>
|
|
#include <string>
|
|
#include <unistd.h>
|
|
|
|
#include <fmt/format.h>
|
|
|
|
namespace FEXCore {
|
|
JITSymbols::JITSymbols() {
|
|
}
|
|
|
|
JITSymbols::~JITSymbols() {
|
|
if (fd != -1) {
|
|
close(fd);
|
|
}
|
|
}
|
|
|
|
void JITSymbols::InitFile() {
|
|
// We can't use FILE here since we must be robust against forking processes closing our FD from under us.
|
|
const auto PerfMap = fmt::format("/tmp/perf-{}.map", getpid());
|
|
|
|
fd = open(PerfMap.c_str(), O_CREAT | O_EXCL | O_WRONLY | O_DIRECT, 0644);
|
|
}
|
|
|
|
void JITSymbols::Register(const void *HostAddr, uint64_t GuestAddr, uint32_t CodeSize) {
|
|
if (fd == -1) return;
|
|
|
|
// Linux perf format is very straightforward
|
|
// `<HostPtr> <Size> <Name>\n`
|
|
const auto Buffer = fmt::format("{} {:x} JIT_0x{:x}_{}\n", HostAddr, CodeSize, GuestAddr, HostAddr);
|
|
auto Result = write(fd, Buffer.c_str(), Buffer.size());
|
|
if (Result == -1 && errno == EBADF) {
|
|
fd = -1;
|
|
}
|
|
}
|
|
|
|
void JITSymbols::Register(const void *HostAddr, uint32_t CodeSize, std::string_view Name) {
|
|
if (fd == -1) return;
|
|
|
|
// Linux perf format is very straightforward
|
|
// `<HostPtr> <Size> <Name>\n`
|
|
const auto Buffer = fmt::format("{} {:x} {}_{}\n", HostAddr, CodeSize, Name, HostAddr);
|
|
auto Result = write(fd, Buffer.c_str(), Buffer.size());
|
|
if (Result == -1 && errno == EBADF) {
|
|
fd = -1;
|
|
}
|
|
}
|
|
|
|
void JITSymbols::Register(const void *HostAddr, uint32_t CodeSize, std::string_view Name, uintptr_t Offset) {
|
|
if (fd == -1) return;
|
|
|
|
// Linux perf format is very straightforward
|
|
// `<HostPtr> <Size> <Name>\n`
|
|
const auto Buffer = fmt::format("{} {:x} {}+0x{:x} ({})\n", HostAddr, CodeSize, Name, Offset, HostAddr);
|
|
auto Result = write(fd, Buffer.c_str(), Buffer.size());
|
|
if (Result == -1 && errno == EBADF) {
|
|
fd = -1;
|
|
}
|
|
}
|
|
|
|
void JITSymbols::RegisterNamedRegion(const void *HostAddr, uint32_t CodeSize, std::string_view Name) {
|
|
if (fd == -1) return;
|
|
|
|
// Linux perf format is very straightforward
|
|
// `<HostPtr> <Size> <Name>\n`
|
|
const auto Buffer = fmt::format("{} {:x} {}\n", HostAddr, CodeSize, Name);
|
|
auto Result = write(fd, Buffer.c_str(), Buffer.size());
|
|
if (Result == -1 && errno == EBADF) {
|
|
fd = -1;
|
|
}
|
|
}
|
|
|
|
void JITSymbols::RegisterJITSpace(const void *HostAddr, uint32_t CodeSize) {
|
|
if (fd == -1) return;
|
|
|
|
// Linux perf format is very straightforward
|
|
// `<HostPtr> <Size> <Name>\n`
|
|
const auto Buffer = fmt::format("{} {:x} FEXJIT\n", HostAddr, CodeSize);
|
|
auto Result = write(fd, Buffer.c_str(), Buffer.size());
|
|
if (Result == -1 && errno == EBADF) {
|
|
fd = -1;
|
|
}
|
|
}
|
|
|
|
} // namespace FEXCore
|