Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 43 additions & 0 deletions .github/workflows/emscripten-libraries.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
name: Emscripten library portability

on:
pull_request:
paths:
- .github/workflows/emscripten-libraries.yml
- core/libs/**
push:
branches: [main]
paths:
- .github/workflows/emscripten-libraries.yml
- core/libs/**

permissions:
contents: read

jobs:
library-portability:
runs-on: ubuntu-latest
timeout-minutes: 20
steps:
- uses: actions/checkout@v7
with:
persist-credentials: false
- name: Compile native library sources and test guest long doubles
run: |
cmake -S core/libs/tests/emscripten -B build-native -DCMAKE_BUILD_TYPE=Release
cmake --build build-native --parallel 2
ctest --test-dir build-native --output-on-failure
- uses: actions/setup-node@v6
with:
node-version: '26'
- name: Compile memory64 libraries and test conversions and module errors
shell: bash
run: |
ANYPS5_TEST_NODE="$(command -v node)"
git clone --depth 1 https://github.com/emscripten-core/emsdk.git "$RUNNER_TEMP/emsdk"
"$RUNNER_TEMP/emsdk/emsdk" install 6.0.12
"$RUNNER_TEMP/emsdk/emsdk" activate 6.0.12
source "$RUNNER_TEMP/emsdk/emsdk_env.sh"
emcmake cmake -S core/libs/tests/emscripten -B build-portable -DCMAKE_BUILD_TYPE=Release -DANYPS5_NODE="$ANYPS5_TEST_NODE"
cmake --build build-portable --parallel 2
ctest --test-dir build-portable --output-on-failure
1 change: 1 addition & 0 deletions core/libs/prx/libSceNet/Export.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
#include <netinet/tcp.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <poll.h>
#include <unistd.h>
#endif
Expand Down
4 changes: 3 additions & 1 deletion core/libs/prx/libSceSysmodule/Export.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
#include <dlfcn.h>
#include <mach/mach.h>
#include <mach/mach_vm.h>
#else
#elif !defined(__EMSCRIPTEN__)
#include "prx/libc/include/specifics/linux/HostMapping.hpp"
#endif

Expand Down Expand Up @@ -149,6 +149,8 @@ bool fillModuleInfoForUnwind(std::uint64_t addr, ModuleInfoForUnwind* info) {
info->seg0_addr = region;
info->seg0_size = size;
return true;
#elif defined(__EMSCRIPTEN__)
return sceKernelGetModuleInfoForUnwind(addr, 0, info) == 0;
#else
const auto mapping = HostMapping::Find(addr, "sceSysmoduleGetModuleInfoForUnwind");
if (!mapping) {
Expand Down
8 changes: 7 additions & 1 deletion core/libs/prx/libSceVideoOut/include/VideoOutDriver.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,17 @@
#include <stdexcept>
#include "prx/libSceAgcDriver/Execution/include/VideoOutput.hpp"
#include "prx/libSceAgcDriver/Execution/include/DisplayBuffer.hpp"
#ifndef __EMSCRIPTEN__
#include "prx/libSceVideoOut/include/DisplayWindow.hpp"
#include "prx/libSceVideoOut/include/BufferReuseTracker.hpp"
#include "prx/libSceVideoOut/include/HostHdrProbe.hpp"
#endif
#include "prx/libSceVideoOut/include/BufferReuseTracker.hpp"
#include "prx/libc/include/Shutdown.hpp"
#include "prx/libkernel/Time/include/TimedWait.hpp"

#ifndef __EMSCRIPTEN__
#include "SDL.h"
#endif
#include "SceTypes.hpp"

static constexpr int VIDEO_OUT_ERROR_INVALID_VALUE = -2144796671;
Expand Down Expand Up @@ -255,8 +259,10 @@ class VideoOutDriver {
std::array<std::shared_ptr<AgcDriver::IVideoOutput>, VIDEO_OUT_NUM_MAX> outputs;
std::shared_ptr<FlipQueue> flipQueue = std::make_shared<FlipQueue>();

#ifndef __EMSCRIPTEN__
DisplayWindow window;
HostHdrProbe hdrProbe;
#endif
std::uint64_t hdrGeneration = 0;
std::atomic<int> displayRefreshRate{0};
std::atomic<std::uint32_t> displayResolution{VIDEO_OUT_RESOLUTION_1080P};
Expand Down
54 changes: 54 additions & 0 deletions core/libs/prx/libc/include/GuestLongDouble.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
#ifndef CORE_LIBS_PRX_LIBC_INCLUDE_GUESTLONGDOUBLE_HPP
#define CORE_LIBS_PRX_LIBC_INCLUDE_GUESTLONGDOUBLE_HPP

#include <cstdint>
#include <cstring>
#include <limits>

namespace LibcDetail {

inline constexpr std::size_t GuestLongDoubleBytes = 16;

static_assert(std::numeric_limits<long double>::digits == 64 || std::numeric_limits<long double>::digits == 113);

inline long double GuestLongDoubleLoad(const void* source) {
long double value;
static_assert(sizeof(value) == GuestLongDoubleBytes);
if constexpr (std::numeric_limits<long double>::digits == 64) {
std::memcpy(&value, source, sizeof(value));
} else {
std::uint64_t mantissa;
std::uint16_t signExponent;
std::memcpy(&mantissa, source, sizeof(mantissa));
std::memcpy(&signExponent, static_cast<const unsigned char*>(source) + 8, sizeof(signExponent));
const std::uint64_t fraction = mantissa & 0x7FFF'FFFF'FFFF'FFFFull;
const std::uint64_t low = fraction << 49;
const std::uint64_t high = (fraction >> 15) | (static_cast<std::uint64_t>(signExponent & 0x7FFF) << 48) | (static_cast<std::uint64_t>(signExponent >> 15) << 63);
std::memcpy(&value, &low, sizeof(low));
std::memcpy(reinterpret_cast<unsigned char*>(&value) + 8, &high, sizeof(high));
}
return value;
}

inline void GuestLongDoubleStore(void* target, long double value) {
if constexpr (std::numeric_limits<long double>::digits == 64) {
std::memcpy(target, &value, sizeof(value));
} else {
std::uint64_t low;
std::uint64_t high;
std::memcpy(&low, &value, sizeof(low));
std::memcpy(&high, reinterpret_cast<const unsigned char*>(&value) + 8, sizeof(high));
const auto exponent = static_cast<std::uint16_t>((high >> 48) & 0x7FFF);
const std::uint64_t fraction = ((high & 0xFFFF'FFFF'FFFFull) << 15) | (low >> 49);
const std::uint64_t mantissa = fraction | (exponent != 0 ? 0x8000'0000'0000'0000ull : 0);
const auto signExponent = static_cast<std::uint16_t>(exponent | ((high >> 63) << 15));
unsigned char bytes[GuestLongDoubleBytes] = {};
std::memcpy(bytes, &mantissa, sizeof(mantissa));
std::memcpy(bytes + 8, &signExponent, sizeof(signExponent));
std::memcpy(target, bytes, sizeof(bytes));
}
}

}

#endif
13 changes: 8 additions & 5 deletions core/libs/prx/libc/include/WindowsFormatting.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
#define CORE_LIBS_PRX_LIBC_INCLUDE_WINDOWSFORMATTING_HPP

#include "SceTypes.hpp"
#include "prx/libc/include/GuestLongDouble.hpp"
#include <algorithm>
#include <array>
#include <bit>
Expand Down Expand Up @@ -87,9 +88,13 @@ class FormatArguments {
args.overflow_arg_area = static_cast<char*>(args.overflow_arg_area) + 8;
}
}
T value;
std::memcpy(&value, address, sizeof(value));
return value;
if constexpr (std::is_same_v<T, long double>) {
return GuestLongDoubleLoad(address);
} else {
T value;
std::memcpy(&value, address, sizeof(value));
return value;
}
}

};
Expand Down Expand Up @@ -664,8 +669,6 @@ inline int FormatWindows(char* buffer, size_t size, const char* format, const vo
output.Value(spec + "ll" + conversion, value);
} else if (std::strchr("aAeEfFgG", conversion)) {
if (length == "L") {
static_assert(sizeof(long double) == 16);
static_assert(std::numeric_limits<long double>::digits == 64);
const long double value = args.Next<long double>();
if (std::isnan(value)) {
output.Value(PaddedStringSpec(spec), NanText(spec, std::signbit(value), conversion < 'a').c_str());
Expand Down
4 changes: 3 additions & 1 deletion core/libs/prx/libc/include/general/ExportMacros.hpp
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
#ifndef CORE_LIBS_PRX_LIBC_INCLUDE_GENERAL_EXPORTMACROS_HPP
#define CORE_LIBS_PRX_LIBC_INCLUDE_GENERAL_EXPORTMACROS_HPP

#if defined(__APPLE__)
#if defined(__EMSCRIPTEN__) && !defined(APS5_WASM_EXPORT_SCAN)
#define APS5_EXPORT(exportName, funcName)
#elif defined(__APPLE__)
#define APS5_EXPORT(exportName, funcName) \
__asm__(".globl \"_" exportName "_nid_no_patch_cut\"\n\t" \
".set \"_" exportName "_nid_no_patch_cut\", _" #funcName)
Expand Down
2 changes: 2 additions & 0 deletions core/libs/prx/libc/include/general/VabiMacros.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@

#ifdef _WIN32
#define APS5_VABI __attribute__((sysv_abi))
#elif defined(APS5_WASM_EXPORT_SCAN)
#define APS5_VABI APS5_VABI
#else
#define APS5_VABI
#endif
Expand Down
6 changes: 6 additions & 0 deletions core/libs/prx/libc/include/specifics/itanium/CxxAbi.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,12 @@

#include <cxxabi.h>

#ifdef __EMSCRIPTEN__
namespace __cxxabiv1 {
extern "C" int __cxa_thread_atexit(void (*)(void*), void*, void*) noexcept;
}
#endif

#ifdef _LIBCPPABI_VERSION
#include <typeinfo>

Expand Down
4 changes: 2 additions & 2 deletions core/libs/prx/libc/src/ApplicationProcessParameters.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
#define NOMINMAX
#endif
#include <windows.h>
#elif defined(__linux__) || defined(__APPLE__)
#elif defined(__linux__) || defined(__APPLE__) || defined(__EMSCRIPTEN__)
#include "prx/libc/include/specifics/linux/ElfTypes.hpp"
#include <dlfcn.h>
#endif
Expand Down Expand Up @@ -88,7 +88,7 @@ const void* ApplicationProcessParameters_nid_no_patch() {
}
if (!found) throw std::runtime_error("application heap: process metadata is missing; relink the executable");
return result;
#elif defined(__linux__) || defined(__APPLE__)
#elif defined(__linux__) || defined(__APPLE__) || defined(__EMSCRIPTEN__)
struct Search {
const void* parameters = nullptr;
bool invalid = false;
Expand Down
8 changes: 8 additions & 0 deletions core/libs/prx/libc/src/General.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -581,6 +581,9 @@ std::atomic<int> g_hostStandardError{-1};
}

extern "C" int KeepHostStandardError_nid_no_patch() {
#ifdef __EMSCRIPTEN__
return 2;
#else
const std::lock_guard lock(g_hostStandardErrorMutex);
if (const int kept = g_hostStandardError.load(); kept >= 0) return kept;
const int kept = ::fcntl(2, F_DUPFD_CLOEXEC, 3);
Expand All @@ -599,6 +602,7 @@ extern "C" int KeepHostStandardError_nid_no_patch() {
stderr = stream;
g_hostStandardError.store(kept);
return kept;
#endif
}

extern "C" int HostStandardError_nid_no_patch() {
Expand All @@ -611,6 +615,9 @@ std::atomic<int> g_hostStandardOutput{-1};
}

extern "C" int KeepHostStandardOutput_nid_no_patch() {
#ifdef __EMSCRIPTEN__
NotImplemented_nid_no_patch(__func__);
#else
const std::lock_guard lock(g_hostStandardOutputMutex);
if (const int kept = g_hostStandardOutput.load(); kept >= 0) return kept;
const int kept = ::fcntl(1, F_DUPFD_CLOEXEC, 3);
Expand All @@ -629,6 +636,7 @@ extern "C" int KeepHostStandardOutput_nid_no_patch() {
stdout = stream;
g_hostStandardOutput.store(kept);
return kept;
#endif
}

extern "C" int DuplicateHostDescriptor_nid_no_patch(int descriptor) {
Expand Down
4 changes: 2 additions & 2 deletions core/libs/prx/libc/src/GuestAllocations.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ void recordChange(void* mutation, const void* pointer, std::size_t bytes) {
if (mutation != nullptr && bytes != 0) static_cast<MutationState*>(mutation)->changed.emplace_back(reinterpret_cast<std::uintptr_t>(pointer), bytes);
}

#if !defined(_WIN32) && !defined(__APPLE__)
#if !defined(_WIN32) && !defined(__APPLE__) && !defined(__EMSCRIPTEN__)
std::vector<std::pair<std::uintptr_t, std::size_t>> fileBackedWritableImage() {
const auto image = std::filesystem::read_symlink("/proc/self/exe").string();
std::ifstream maps("/proc/self/maps");
Expand Down Expand Up @@ -631,7 +631,7 @@ struct RangePatch {
};

RangePatch replaceRange(const void* pointer, std::size_t bytes, bool remove, bool readable, bool writable, bool gpu) {
const auto address = reinterpret_cast<std::uintptr_t>(pointer);
const std::uint64_t address = reinterpret_cast<std::uintptr_t>(pointer);
require(bytes != 0 && bytes <= std::numeric_limits<std::uint64_t>::max() - address, "invalid guest protection or unmap range");
require(!writable || readable, "writable guest allocation must be readable");
const auto end = address + bytes;
Expand Down
Loading
Loading