Compare commits

..
84 Commits
Author SHA1 Message Date
Ryan Houdek ae34b1e521 Docs: Update for release FEX-2209 2022-09-05 10:32:07 -07:00
Ryan Houdek c17da25617 Merge pull request #1973 from neobrain/refactor_single_thunkgen_output
Thunks: Consolidate all generated code to one file per library per platform
2022-09-05 10:20:54 -07:00
Tony Wasserka cc8ef16240 Thunks/gen: Consolidate all generated code to one file per library per platform 2022-09-05 15:03:49 +02:00
Tony Wasserka 30fac81c41 Thunks/gen: Remove unused symtable generation code 2022-09-05 15:03:49 +02:00
Ryan Houdek bbcca80b60 Merge pull request #1971 from Sonicadvance1/termux_namespace_collision
Syscalls: Use underscored shm syscall names
2022-09-05 02:55:29 -07:00
Ryan Houdek 56ae3716bb Syscalls: Use underscored shm syscall names
Removes us needing to carry a patch downstream in the termux package
repo.
2022-09-05 02:39:42 -07:00
Ryan Houdek 2fba3a4a9a LinuxAllocator: Uppercase function names
Not only does this match our internal naming convention. This avoids the
shm symbol namespace collision as well.
2022-09-05 02:39:42 -07:00
Ryan Houdek 8ba0312d40 Syscalls: Prefix _ to shm syscall names to avoid namespace conflict
Termux uses defines for these, so our token pasting fails, but we also
still want to use their define so we can fall down their emulation
library whenever possible.

Prefix an underscore to be able to use both our number definitions and
their defines in the same file.
2022-09-05 02:39:42 -07:00
Ryan Houdek 53623ffa72 Merge pull request #1941 from Sonicadvance1/vdso_implementation
Thunks: Adds VDSO thunk library
2022-09-04 23:02:49 -07:00
Mai d6199687f4 Merge pull request #1970 from Sonicadvance1/fix_fresh_runner
Github: Fix fresh runner rootfs checkout
2022-09-02 21:49:22 -04:00
Ryan Houdek 7cb413dde6 Github: Fix fresh runner rootfs checkout
build folder doesn't exist on a freshly started runner.
Executing the rootfs fetch script doesn't care what the working
directory is. Doesn't need to be in `build/` which doesn't exist on a
fresh runner and will fail chdir.
2022-09-02 15:15:21 -07:00
Ryan Houdek c5a7fc1e2a Resolve most VDSO comments 2022-09-02 15:13:18 -07:00
Ryan Houdek 0869b0aa29 FEXLinuxTests: Adds a VDSO test
Ensures VDSO is working as correctly as it can.
2022-09-02 13:31:36 -07:00
Ryan Houdek 4e81f847f2 ELFCodeLoader: Load VDSO thunk at application startup
This ensures it will always be available to the application as long as
the library is installed.
2022-09-02 13:31:36 -07:00
Ryan Houdek 8fa27d2d9f Thunks: Adds VDSO thunk library
VDSO is heavily abused by Proton games to the point it is showing up as
CPU time.
Implement a guest-facing only thunk library using the hardcoded VDSO
interface in Thunks.

If available this will always be loaded on application load and set the
auxv value to support it.

This requires a bit of special treatment as our first user of linker
scripts since the format of the ELF must be careful crafted to not break
applications trying to parse it.

This library exposes a handful of symbols:
- clock_gettime
- clock_getres
- gettimeofday
- time
- getcpu
- All previous with `__vdso_` prefix
- LINUX_2.6

All of these symbols get routed directly to the host architecture VDSO
interface if they exist.
AArch64 doesn't have getcpu or time VDSO.

In a microbench, VDSO improved bench times substantially
x86-64 host: 3.612s -> 1.369s - 2.63x speed
AArch64 host: 3.821s -> 2.284s - 1.67x speed
  - AArch64 isn't as improved due to missing VDSO symbols

This is also our first /always/ enabled thunk as long as the file exists
2022-09-02 13:31:36 -07:00
Ryan Houdek e5a8a29efe Thunks: Add support for lds linker script on Guest libraries
This is going to be necessary in the next commit
2022-09-02 13:31:36 -07:00
Ryan Houdek f967f53176 Thunks: Adds VDSO specific thunks
x86-64 has five symbols within VDSO that we need to emulate.

Pass this through either glibc or host vdso if the symbol exists.
AArch64 doesn't have the time or getcpu vdso interface, so fall down
glibc instead.
2022-09-02 13:31:36 -07:00
Ryan Houdek 31fefaae0d Merge pull request #1969 from Sonicadvance1/fexrootfsfetcher_fix_crash
FEXRootFSFetcher: Fix crash if curl fails to download rootfs definition file
2022-09-02 11:07:03 -07:00
Ryan Houdek 7f9edbf39e Merge pull request #1968 from Sonicadvance1/new_domain
New domain.
2022-09-02 10:57:35 -07:00
Ryan Houdek 760b9c8e7f FEXRootFSFetcher: Fix crash if curl fails to download rootfs definition file
This can happen when the link goes down or internet blip.
2022-09-02 10:53:51 -07:00
Ryan Houdek cc7fb008fc New domain.
Needed to fix FEXRootFSFetcher from #1967
2022-09-02 10:43:07 -07:00
Ryan Houdek 98dbfbe654 Merge pull request #1949 from lioncash/interp-op
InterpreterOps: Extend SSAData size to accomodate 256-bit operations
2022-09-02 09:54:58 -07:00
lioncash 6444726614 InterpreterOps: Use designated initializer for IR op data
Same behavior, but keeps everything all initialized at the point of
declaration, rather than after the fact.
2022-09-02 12:40:25 -04:00
lioncash 0a562fbb10 InterpreterOps: Extend SSAData to handle 256-bit vectors 2022-09-02 12:40:14 -04:00
Ryan Houdek 7d8950de40 Merge pull request #1966 from lioncash/bool
Arm64/JIT: Rename CanUseSVE to HostSupportsSVE
2022-09-01 13:13:00 -07:00
lioncash 3fdde0c90b Arm64/JIT: Rename CanUseSVE to HostSupportsSVE
This is a much more descriptive name.

Spawned off of discussion in #1944
2022-09-01 13:22:41 -04:00
Ryan Houdek e776f4cd4e Merge pull request #1948 from lioncash/svebit
VectorOps: Extend VAnd/VBic/VOr/VXor
2022-08-30 17:57:51 -07:00
Ryan Houdek e7d7dd13d7 Merge pull request #1945 from lioncash/vectormov
VectorOps: Extend VMov
2022-08-30 17:57:00 -07:00
Ryan Houdek d5c83a2e45 Merge pull request #1950 from lioncash/x86run
HostRunner: Handle upper YMM lanes in sigsegv handler
2022-08-30 17:55:38 -07:00
Ryan Houdek 37ccb13917 Merge pull request #1946 from lioncash/x86dep
x86_64/JIT: Resolve lingering fmt deprecation warning
2022-08-30 17:54:57 -07:00
Ryan Houdek 8439cf410f Merge pull request #1944 from lioncash/sveimm
VectorOps: Extend VectorImm
2022-08-30 17:54:32 -07:00
lioncash 614d9448f7 HostRunner: Handle upper YMM lanes in sigsegv handler
Now we properly copy out the upper lanes instead of ignoring them.
2022-08-25 14:13:19 -04:00
lioncash 70efdbba5d VectorOps: Handle 256-bit VectorImm
Extends VectorImm to be capable of using SVE to handle 256-bit length
vectors.
2022-08-24 13:28:48 -04:00
Stefanos Kornilios Mitsis Poiitidis 12fee91ebb Merge pull request #1947 from neobrain/fix_tmpnam_warning
unittests/ThunkLibs: Fix warning about "dangerous" use of tmpnam
2022-08-24 20:13:04 +03:00
lioncash bcb7e20619 VectorOps: Handle 256-bit VXor 2022-08-24 12:52:05 -04:00
lioncash afc5e8a140 VectorOps: Handle 256-bit VOr 2022-08-24 12:52:05 -04:00
lioncash 4be6626c89 VectorOps: Handle 256-bit VBic 2022-08-24 12:52:02 -04:00
lioncash 5f2b6d629b VectorOps: Handle 256-bit VAnd 2022-08-24 12:43:51 -04:00
Tony Wasserka 1b8f5f08f0 unittests/ThunkLibs: Fix warning about "dangerous" use of tmpnam
tmpnam is considered insecure since it's vulnerable to TOCTOU issues.
This is not an issue for these tests, but replacing tmpnam is not any
more complicated than silencing the warning.
2022-08-24 18:15:51 +02:00
lioncash 79674c697a x86_64/JIT: Resolve lingering fmt deprecation warning
Just a log that was missed during the previous fmt deprecation cleanup.
2022-08-24 11:49:02 -04:00
lioncash 416d8c1df6 VectorOps: Handle 256-bit VMov
Kind of sucky that SVE doesn't have a convenient way to manipulate
predicate registers with immediates or anything to make this nicer (that
I know of).

Having to use a temp to clear the upper part of the vector reliably is
bleh.
2022-08-24 11:35:34 -04:00
Ryan Houdek d03b6a9382 Merge pull request #1942 from lioncash/zero
VectorOps: Extend VectorZero
2022-08-23 20:00:39 -07:00
lioncash df22e0c796 x86_64/JITClass: Add ToYMM helper
Will be used in subsequent changes to handle 256-bit operations in the
x86-64 backend
2022-08-23 12:58:25 -04:00
lioncash 79a3bd75cc VectorOps: Handle 256-bit VectorZero 2022-08-23 12:57:59 -04:00
Stefanos Kornilios Mitsis Poiitidis e6acdcc583 Merge pull request #1940 from neobrain/refactor_1868_cleanups
Thunks: Minor cleanups for signature-based function pointer thunking
2022-08-23 01:08:51 +03:00
Tony Wasserka cd78984228 Thunks: Define _M_X86_64/_M_ARM_64 when invoking thunkgen
This avoids the need to provide a fallback definition for platform-specific
macros. The definitions are only added host-side, since only Host.h is
included in any interface files.
2022-08-22 18:11:28 +02:00
Tony Wasserka 2f007c5f0a Thunks: Remove unused parameters of exports initializer 2022-08-22 18:11:28 +02:00
Tony Wasserka ae64a1e30c Thunks: Replace compiler-specific attributes with FEX_DEFAULT_VISIBILITY 2022-08-22 18:11:28 +02:00
Stefanos Kornilios Mitsis Poiitidis 097184c3e0 Merge pull request #1926 from Sonicadvance1/no_irloader_on_no_tests
IRLoader/TestHarnessLoader: Don't build if not building tests
2022-08-21 09:50:44 +03:00
Stefanos Kornilios Mitsis Poiitidis 84a95adae1 Merge pull request #1931 from Sonicadvance1/support_thunksdb_in_config
Thunks: Support direct thunk config in configuration files
2022-08-21 09:50:08 +03:00
Stefanos Kornilios Mitsis Poiitidis 123b6728e4 Merge pull request #1932 from Sonicadvance1/fix_allocator_perf_hit
64BitAllocator: Fixes a significant state tracking perf problem
2022-08-21 09:47:54 +03:00
Ryan Houdek 69b4fc98ed 64BitAllocator: Fixes a significant state tracking perf problem
Due to how the 64-bit allocator previously worked, it was never subjected to memory
regions larger than 64GB to be tracked. With the change in
PR #1885, this has changed to have regions that will hit sizes larger
than 170TB on some platforms.

Better yet, even with smaller regions it still had a performance issue,
it just wasn't as visible.

First problem: We used MemSet instead of MemClear for the live page
clearing. This caused pages to be claimed as "always in use".

This would cause us to always scan the entire region on allocation, find
that it didn't work and allocate a fresh region on every slab
allocation.
jemalloc saving us here since it allocates slabs from the OS fairly
aggressively.

Second problem: We used MemSet (now changed to MemClear) to "clear" the
state tracking for pages.

This causes ~600MB of memory to be used purely for state tracking.
This was physically backed since we were writing to every bit of
tracking for handling 256TB of VA.
This had a fault dance with the kernel for every new page being hit
here.
Instead of clearing the the bits with a memset, clear it with madvise so
it doesn't consume physical pages at all.

This means we use significantly less physical memory for 32-bit
applications.

With this change, pressure-vessel startup time goes from 24 seconds down
to 17 seconds. 70% of the original startup time.
But really the main savings here comes from the memory reduction that
PR #1885 ballooned, but has been an unseen problem before.

Before that PR we were burning 2MB of physical memory per region for no
reason.
After that PR we were burning up to 600MB of physical memory per region
for no reason. Changing a bit depending on how large the region ended up
being.

This now ends up being 2 pages starting out and grows as more pages are
are used. A significant improvement.
2022-08-20 15:32:40 -07:00
Ryan Houdek 35cf7703b1 Thunks: Support direct thunk config in configuration files
Previously in order to enable thunks, we needed an independent
description file of which thunks to be enabled. This is nice for quickly
testing out new games by setting `FEX_THUNKCONFIG` environment variable.

For users that just want to enable thunks this is an unwieldy
indirection that doesn't make much sense at a glance.

Previously this meant you needed two files as an example:
```
  ryanh@ubuntu-linux-20-04-desktop:~/.fex-emu$ cat thunks.json
  {
    "ThunksDB": {
      "GL": 1,
      "Vulkan": 1
    }
  }
  ryanh@ubuntu-linux-20-04-desktop:~/.fex-emu$ cat AppConfig/EnderLiliesSteam-Linux-Shipping.json
  {
    "Config": {
      "ThunkConfig":"~\/.fex-emu\/thunks.json"
    }
  }
```

Instead of this unwieldy redirection just support `ThunksDB` json
directly in the AppConfig.

```
  ryanh@ubuntu-linux-20-04-desktop:~/.fex-emu$ cat AppConfig/EnderLiliesSteam-Linux-Shipping.json
  {
    "Config": {
      <...>
    },
    "ThunksDB": {
      "GL": 1,
      "Vulkan": 1
    }
  }
```

As can be seen this makes this significantly easier for new users
getting in to thunks. Depending on which path to enable thunks the user
is more comfortable with, they can still enable them using the
`ThunkConfig` option or embedding directly in the application
configuration.

Additionally this removes the older non-ThunksDB path to loading thunks.
All users of it have moved on to using ThunksDB.
2022-08-20 10:02:13 -07:00
Ryan Houdek 8c1137543b Config: Adds APP_CONFIG_NAME meta config option
We have separate configurations for the Application path versus the
application name we are using as a configuration choice.

Example 1: FEXBash "wine Crysis64.exe"

Previous APP_FILENAME will contain `/usr/bin/wine`, which is still used
elsewhere.

This new APP_CONFIG_NAME will contain `Crysis64.exe`

Example 2: FEXBash glxgears

Previous APP_FILENAME will contain `/usr/bin/glxgears`
APP_CONFIG_NAME will contain `glxgears`

We didn't have this exposed any other way before.
2022-08-20 09:58:23 -07:00
Ryan Houdek b8e66e56a0 Config: Remove log message about Config file existing without Config json object 2022-08-20 09:58:04 -07:00
Ryan Houdek fbb008e510 Merge pull request #1929 from Sonicadvance1/support_variadic_struct_packing
Thunks/X11: Support Variadic stack packing
2022-08-20 06:31:22 -07:00
Ryan Houdek 04678f8404 Merge pull request #1885 from FEX-Emu/skmp/simpler-memory-stealing
Allocator: Simplify StealMemory, make it less chatty with kernel space
2022-08-20 06:20:36 -07:00
Stefanos Kornilios Mitsis Poiitidis 1b5aaf1fb8 Allocator: Simplify StealMemory, make it less chatty with kernel space 2022-08-20 13:10:50 +03:00
Ryan Houdek d8e4873f43 Thunks/X11: Support Variadic stack packing
Found an issue with wine + DXVK + thunks where these were passing in
more than 7 arguments and crashing.

Create some assembly to support any size of variadic stack packing.
Only implemented for AArch64 for now.
2022-08-19 21:52:19 -07:00
Ryan Houdek 998a3d8353 Merge pull request #1928 from Sonicadvance1/more_x11_thunks
Thunks/X11: Adds missing XLibint functions
2022-08-19 17:08:40 -07:00
Ryan Houdek 336dedbed5 Merge pull request #1927 from Sonicadvance1/non_fatal_get_fdpath
FDUtils: Don't make unknown get_fdpath fatal
2022-08-19 17:08:32 -07:00
Ryan Houdek 955595be8a FDUtils: Don't make unknown get_fdpath fatal
Encountered this while running wine things.
Non-fatal so don't explode
2022-08-19 02:28:19 -07:00
Ryan Houdek 576bd4f69a Thunks/X11: Adds missing XLibint functions
Some of these were required to get thunking to work with Proton and
DXVK.
2022-08-19 00:03:54 -07:00
Ryan Houdek cd6915917b IRLoader/TestHarnessLoader: Don't build if not building tests
If we're not building tests then just don't build the IRLoader since it
won't get used.
2022-08-17 17:04:13 -07:00
Ryan Houdek 0adbe31112 Merge pull request #1919 from Sonicadvance1/termux_shm_library
Termux: Add android-shmem library
2022-08-15 11:14:27 -07:00
Ryan Houdek d5138f509c Merge pull request #1917 from Sonicadvance1/fix_compile_without_jemalloc
Thunks: Fix compile without jemalloc
2022-08-15 11:14:17 -07:00
Ryan Houdek 1fe6fc3feb Merge pull request #1920 from Sonicadvance1/tests_from_host_features
unittests: Support skipping unit tests based on host feature support
2022-08-15 11:07:02 -07:00
Ryan Houdek c03a7fd482 Merge pull request #1924 from 1ace/fix-git-abbrev
cmake: fix incorrect assumption about the value of git's core.abbrev
2022-08-15 10:44:59 -07:00
Eric Engestrom 31c47f05f4 cmake: fix incorrect assumption about the value of git's core.abbrev
While the default value of `git config core.abbrev` is `7` (used to
truncate the commit hash in several git commands) and most people don't
configure anything else, I happen to prefer when commits hashes stay
valid for a while, so I changed that value to `20`. Because of this,
FEX fails to build in External/FEXCore/Source/Interface/Core/CPUID.cpp:961

Let's be explicit in the git command about what minimum length of commit
hash we expect.
2022-08-15 16:25:27 +01:00
Ryan Houdek edad24479b unittests: Support skipping unit tests based on host feature support
For these unit tests we no longer need to put them in the disabled tests
file. Instead it will be skipped if the host doesn't support the feature
required.
2022-08-14 20:04:26 -07:00
Ryan Houdek bba58732e0 HostFeatures: Add new supported features flag 2022-08-14 19:56:14 -07:00
Ryan Houdek 8d1cc6cf51 Termux: Add android-shmem library
Otherwise we fail at linking.
2022-08-14 18:23:53 -07:00
Ryan Houdek f03d0be5ef Thunks: Fix compile without jemalloc
This only fixes the compile error. Thunks aren't expected to be used
without jemalloc enabled.
2022-08-13 11:29:11 -07:00
Ryan Houdek a2f4f494a9 Merge pull request #1916 from Sonicadvance1/server_socket_path_override
FEXServer: Support socket path override
2022-08-13 07:00:59 -07:00
Ryan Houdek 9de25c200c Merge pull request #1915 from Sonicadvance1/emulate_64bit_getdents
Linux: Emulate classic getdents syscall for x64 and x32
2022-08-13 07:00:52 -07:00
Ryan Houdek fe1f00aadb FEXServer: Support socket path override
This is necessary for the fexserver to function correctly when chrooting
in to our rootfs and doing things.

Requires independent rootfs script modifications which will come with
the next rootfs update.

Problem comes down to a chroot supporting multiple users, where our
typical use case is only one user. Bind the server file to a single
server for the entire chroot session regardless of users, solving this
problem inside the chroot.

Fixes apt-get inside of chroot, which runs as user _apt.
2022-08-12 22:52:30 -07:00
Ryan Houdek a847fac4ca Linux: Emulate classic getdents syscall for x64 and x32
Arm64 doesn't have the classic getdents syscall, only getdents64.
Old glibc versions (like 2.17) don't support getdents64

This fixes 64-bit ls with a centos 7 rootfs. Likely also fixes some other very
old applications.

Packing differences between 32-bit and 64-bit getdents means we need to
template this between the two types, otherwise it's quite similar.

No known applications rely on the 32-bit getdents but worked with test
applications.
2022-08-12 18:21:49 -07:00
Ryan Houdek 0496506fb7 Linux: Define linux_dirent types for x64
These don't match the compatibility defines and they aren't part of the
public interface.
2022-08-12 16:00:06 -07:00
Ryan Houdek e544591c9c Merge pull request #1901 from Sonicadvance1/build_thunks
CI: Build Thunks
2022-08-12 14:47:54 -07:00
Ryan Houdek e5237d1149 Github: Enable Thunkgen tests 2022-08-12 14:31:18 -07:00
Ryan Houdek b9c848c5e9 Thunks: Xext version check define function prototypes
Nothing major here
2022-08-12 14:31:18 -07:00
Ryan Houdek b64e61a793 Thunks: Check for X11 version
There is no define for this so we must generate our own.
Declare a type ourselves if the library is too old
2022-08-12 14:31:18 -07:00
Ryan Houdek 249e19bf2a Thunks: Asound fix building with alsa 1.2.2 2022-08-09 06:18:43 -07:00
Ryan Houdek baf52ed286 CI: Try building thunks 2022-08-09 05:30:53 -07:00
181 changed files with 2303 additions and 1382 deletions

No files matched your search

+12 -2
View File
@@ -40,7 +40,6 @@ jobs:
# Use a bash shell so we can use the same syntax for environment variable
# access regardless of the host operating system
shell: bash
working-directory: ${{runner.workspace}}/build
run: $GITHUB_WORKSPACE/Scripts/CI_FetchRootFS.py
- name : submodule checkout
@@ -65,7 +64,7 @@ jobs:
# Note the current convention is to use the -S and -B options here to specify source
# and build directories, but this is only available with CMake 3.13 and higher.
# The CMake binaries on the Github Actions machines are (as of this writing) 3.12
run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -G Ninja -DENABLE_LTO=False -DENABLE_ASSERTIONS=True -DENABLE_X86_HOST_DEBUG=True -DENABLE_INTERPRETER=True -DBUILD_FEX_LINUX_TESTS=True
run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -G Ninja -DENABLE_LTO=False -DENABLE_ASSERTIONS=True -DENABLE_X86_HOST_DEBUG=True -DENABLE_INTERPRETER=True -DBUILD_FEX_LINUX_TESTS=True -DBUILD_THUNKS=True
- name: Build
working-directory: ${{runner.workspace}}/build
@@ -178,6 +177,17 @@ jobs:
working-directory: ${{runner.workspace}}/build
run: mv ${{runner.workspace}}/build/Testing/Temporary/LastTest.log ${{runner.workspace}}/build/Testing/Temporary/LastTest_FEXLinuxTests.log || true
- name: Thunkgen tests
working-directory: ${{runner.workspace}}/build
shell: bash
run: cmake --build . --config $BUILD_TYPE --target thunkgen_tests
- name: Thunkgen Results move
if: ${{ always() }}
shell: bash
working-directory: ${{runner.workspace}}/build
run: mv ${{runner.workspace}}/build/Testing/Temporary/LastTest.log ${{runner.workspace}}/build/Testing/Temporary/LastTest_ThunkgenTests.log || true
- name: Truncate test results
if: ${{ always() }}
shell: bash
+1 -1
View File
@@ -476,7 +476,7 @@ endif()
set (CPACK_GENERATOR "DEB")
set (CPACK_PACKAGE_NAME fex-emu)
set (CPACK_PACKAGE_FILE_NAME "${CPACK_PACKAGE_NAME}-${GIT_DESCRIBE_STRING}_${CMAKE_SYSTEM_PROCESSOR}")
set (CPACK_PACKAGE_CONTACT "FEX-Emu Maintainers <team@fex-emu.org>")
set (CPACK_PACKAGE_CONTACT "FEX-Emu Maintainers <team@fex-emu.com>")
set (CPACK_PACKAGE_VERSION_MAJOR "${FEX_VERSION_MAJOR}")
set (CPACK_PACKAGE_VERSION_MINOR "${FEX_VERSION_MINOR}")
set (CPACK_PACKAGE_VERSION_PATCH "${FEX_VERSION_PATCH}")
+1 -1
View File
@@ -55,7 +55,7 @@ further defined and clarified by project maintainers.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported by contacting the project team at team@fex-emu.org. All
reported by contacting the project team at team@fex-emu.com. All
complaints will be reviewed and investigated and will result in a response that
is deemed necessary and appropriate to the circumstances. The project team is
obligated to maintain confidentiality with regard to the reporter of an incident.
+2 -2
View File
@@ -46,14 +46,14 @@ if (OVERRIDE_VERSION STREQUAL "detect")
if (GIT_FOUND)
execute_process(
COMMAND ${GIT_EXECUTABLE} rev-parse --short HEAD
COMMAND ${GIT_EXECUTABLE} rev-parse --short=7 HEAD
WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}"
OUTPUT_VARIABLE GIT_SHORT_HASH
ERROR_QUIET
OUTPUT_STRIP_TRAILING_WHITESPACE
)
execute_process(
COMMAND ${GIT_EXECUTABLE} describe
COMMAND ${GIT_EXECUTABLE} describe --abbrev=7
WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}"
OUTPUT_VARIABLE GIT_DESCRIBE_STRING
ERROR_QUIET
+1 -1
View File
@@ -82,7 +82,7 @@ namespace JSON {
json_t const* ConfigList = json_getProperty(json, "Config");
if (!ConfigList) {
LogMan::Msg::EFmt("Couldn't get config list");
// This is a non-error if the configuration file exists but no Config section
return;
}
+18
View File
@@ -351,6 +351,13 @@
"Desc": [
"Loads an AOT IR cache for the loaded executable."
]
},
"ServerSocketPath": {
"Type": "str",
"Default": "",
"Desc": [
"Override for a FEXServer socket path. Only useful for chroots."
]
}
}
},
@@ -368,6 +375,17 @@
"Type": "str",
"Default": ""
},
"APP_CONFIG_NAME": {
"Type": "str",
"Default": "",
"Desc": [
"This is the application config name that has been loaded.",
"This differs from APP_FILENAME in two ways",
"Where APP_FILENAME always points to the executable path that FEX-Emu is executing.",
"This matches what is used to load the AppLayer configuration name.",
"When running through a compatibility layer like wine, this will only be the exe name, instead of wine full path."
]
},
"IS64BIT_MODE": {
"Type": "bool",
"Default": "false"
+4
View File
@@ -202,6 +202,10 @@ namespace FEXCore::Context {
return CTX->AddCustomIREntrypoint(Entrypoint, Handler, Creator, Data);
}
void AppendThunkDefinitions(FEXCore::Context::Context *CTX, std::vector<FEXCore::IR::ThunkDefinition> const& Definitions) {
CTX->AppendThunkDefinitions(Definitions);
}
namespace Debug {
void CompileRIP(FEXCore::Context::Context *CTX, uint64_t RIP) {
CTX->CompileRIP(CTX->ParentThread, RIP);
+2
View File
@@ -322,6 +322,8 @@ namespace FEXCore::Context {
IRCaptureCache.SetAOTIRRenamer(CacheRenamer);
}
void AppendThunkDefinitions(std::vector<FEXCore::IR::ThunkDefinition> const& Definitions);
FEXCore::Utils::PooledAllocatorMMap OpDispatcherAllocator;
FEXCore::Utils::PooledAllocatorMMap FrontendAllocator;
+5
View File
@@ -1332,6 +1332,11 @@ namespace FEXCore::Context {
}
}
void Context::AppendThunkDefinitions(std::vector<FEXCore::IR::ThunkDefinition> const& Definitions) {
ThunkHandler->AppendThunkDefinitions(Definitions);
}
void ConfigureAOTGen(FEXCore::Core::InternalThreadState *Thread, std::set<uint64_t> *ExternalBranches, uint64_t SectionMaxAddress) {
Thread->FrontendDecoder->SetExternalBranches(ExternalBranches);
Thread->FrontendDecoder->SetSectionMaxAddress(SectionMaxAddress);
+10
View File
@@ -62,8 +62,13 @@ HostFeatures::HostFeatures() {
SupportsRCPC = Features.Has(vixl::CPUFeatures::Feature::kRCpc);
SupportsTSOImm9 = Features.Has(vixl::CPUFeatures::Feature::kRCpcImm);
Supports3DNow = true;
SupportsSSE4A = true;
SupportsAVX = Features.Has(vixl::CPUFeatures::Feature::kSVE2) &&
vixl::aarch64::CPU::ReadSVEVectorLengthInBits() >= 256;
SupportsSHA = true;
SupportsBMI1 = true;
SupportsBMI2 = true;
// We need to get the CPU's cache line size
// We expect sane targets that have correct cacheline sizes across clusters
@@ -85,7 +90,12 @@ HostFeatures::HostFeatures() {
SupportsRAND = Features.has(Xbyak::util::Cpu::tRDRAND) && Features.has(Xbyak::util::Cpu::tRDSEED);
SupportsRCPC = true;
SupportsTSOImm9 = true;
Supports3DNow = Features.has(Xbyak::util::Cpu::t3DN) && Features.has(Xbyak::util::Cpu::tE3DN);
SupportsSSE4A = Features.has(Xbyak::util::Cpu::tSSE4a);
SupportsAVX = true;
SupportsSHA = Features.has(Xbyak::util::Cpu::tSHA);
SupportsBMI1 = Features.has(Xbyak::util::Cpu::tBMI1);
SupportsBMI2 = Features.has(Xbyak::util::Cpu::tBMI2);
// xbyak doesn't know how to check for CLZero
uint32_t eax, ebx, ecx, edx;
@@ -159,21 +159,26 @@
break; \
}
struct InterpVector256 {
__uint128_t Lower;
__uint128_t Upper;
};
template<typename Res>
Res GetDest(void* SSAData, FEXCore::IR::OrderedNodeWrapper Op) {
auto DstPtr = &reinterpret_cast<__uint128_t*>(SSAData)[Op.ID().Value];
auto DstPtr = &reinterpret_cast<InterpVector256*>(SSAData)[Op.ID().Value];
return reinterpret_cast<Res>(DstPtr);
}
template<typename Res>
Res GetDest(void* SSAData, FEXCore::IR::NodeID Op) {
auto DstPtr = &reinterpret_cast<__uint128_t*>(SSAData)[Op.Value];
auto DstPtr = &reinterpret_cast<InterpVector256*>(SSAData)[Op.Value];
return reinterpret_cast<Res>(DstPtr);
}
template<typename Res>
Res GetSrc(void* SSAData, FEXCore::IR::OrderedNodeWrapper Src) {
auto DstPtr = &reinterpret_cast<__uint128_t*>(SSAData)[Src.ID().Value];
auto DstPtr = &reinterpret_cast<InterpVector256*>(SSAData)[Src.ID().Value];
return reinterpret_cast<Res>(DstPtr);
}
@@ -1,5 +1,6 @@
#include "Interface/Context/Context.h"
#include "Interface/Core/CPUID.h"
#include "InterpreterDefines.h"
#include "InterpreterOps.h"
#include "F80Ops.h"
@@ -336,23 +337,28 @@ void InterpreterOps::Op_NoOp(FEXCore::IR::IROp_Header *IROp, IROpData *Data, IR:
void InterpreterOps::InterpretIR(FEXCore::Core::CpuStateFrame *Frame, FEXCore::IR::IRListView const *CurrentIR) {
volatile void *StackEntry = alloca(0);
uintptr_t ListSize = CurrentIR->GetSSACount();
const uintptr_t ListSize = CurrentIR->GetSSACount();
static_assert(sizeof(FEXCore::IR::IROp_Header) == 4);
static_assert(sizeof(FEXCore::IR::OrderedNode) == 16);
auto BlockEnd = CurrentIR->GetBlocks().end();
InterpreterOps::IROpData OpData{};
OpData.State = Frame->Thread;
OpData.SSAData = alloca(ListSize * 16);
OpData.CurrentEntry = Frame->State.rip;
OpData.CurrentIR = CurrentIR;
OpData.StackEntry = StackEntry;
OpData.BlockIterator = CurrentIR->GetBlocks().begin();
constexpr size_t ListEntrySizeInBytes = sizeof(InterpVector256);
const size_t SSADataSize = ListSize * ListEntrySizeInBytes;
// Clear them all to zero. Required for Zero-extend semantics
memset(OpData.SSAData, 0, ListSize * 16);
InterpreterOps::IROpData OpData{
.State = Frame->Thread,
.CurrentEntry = Frame->State.rip,
.CurrentIR = CurrentIR,
.StackEntry = StackEntry,
.SSAData = alloca(SSADataSize),
.BlockResults = {},
.BlockIterator = CurrentIR->GetBlocks().begin(),
};
// Clear all SSAData entries to zero. Required for Zero-extend semantics
memset(OpData.SSAData, 0, SSADataSize);
while (1) {
using namespace FEXCore::IR;
@@ -23,7 +23,7 @@ DEF_OP(VectorImm) {
auto Op = IROp->C<IR::IROp_VectorImm>();
uint8_t OpSize = IROp->Size;
uint8_t Tmp[16];
uint8_t Tmp[32];
uint8_t Elements = OpSize / Op->Header.ElementSize;
uint8_t Imm = Op->Immediate;
@@ -426,6 +426,7 @@ void Arm64JITCore::Op_NoOp(IR::IROp_Header *IROp, IR::NodeID Node) {
Arm64JITCore::Arm64JITCore(FEXCore::Context::Context *ctx, FEXCore::Core::InternalThreadState *Thread)
: CPUBackend(Thread, INITIAL_CODE_SIZE, MAX_CODE_SIZE)
, Arm64Emitter(ctx, 0)
, HostSupportsSVE{ctx->HostFeatures.SupportsAVX}
, CTX {ctx} {
RAPass = Thread->PassManager->GetPass<IR::RegisterAllocationPass>("RA");
@@ -66,6 +66,7 @@ public:
private:
FEX_CONFIG_OPT(ParanoidTSO, PARANOIDTSO);
const bool HostSupportsSVE{};
Label *PendingTargetLabel;
FEXCore::Context::Context *CTX;
+176 -54
View File
@@ -12,17 +12,27 @@ using namespace vixl;
using namespace vixl::aarch64;
#define DEF_OP(x) void Arm64JITCore::Op_##x(IR::IROp_Header *IROp, IR::NodeID Node)
DEF_OP(VectorZero) {
uint8_t OpSize = IROp->Size;
switch (OpSize) {
case 8: {
eor(GetDst(Node).V8B(), GetDst(Node).V8B(), GetDst(Node).V8B());
break;
if (HostSupportsSVE) {
const auto Dst = GetDst(Node).Z().VnD();
eor(Dst, Dst, Dst);
} else {
const uint8_t OpSize = IROp->Size;
switch (OpSize) {
case 8: {
const auto Dst = GetDst(Node).V8B();
eor(Dst, Dst, Dst);
break;
}
case 16: {
const auto Dst = GetDst(Node).V16B();
eor(Dst, Dst, Dst);
break;
}
default:
LOGMAN_MSG_A_FMT("Unknown Op Size: {}", OpSize);
break;
}
case 16: {
eor(GetDst(Node).V16B(), GetDst(Node).V16B(), GetDst(Node).V16B());
break;
}
default: LOGMAN_MSG_A_FMT("Unknown Element Size: {}", OpSize); break;
}
}
@@ -30,15 +40,37 @@ DEF_OP(VectorImm) {
auto Op = IROp->C<IR::IROp_VectorImm>();
const uint8_t OpSize = IROp->Size;
const uint8_t Elements = OpSize / Op->Header.ElementSize;
const uint8_t ElementSize = Op->Header.ElementSize;
const uint8_t Elements = OpSize / ElementSize;
if (Op->Header.ElementSize == 8) {
// movi with 64bit element size doesn't do what we want here
LoadConstant(TMP1.X(), Op->Immediate);
dup(GetDst(Node).V2D(), TMP1.X());
}
else {
movi(GetDst(Node).VCast(OpSize * 8, Elements), Op->Immediate);
if (HostSupportsSVE) {
const auto Dst = [&] {
const auto Tmp = GetDst(Node).Z();
switch (ElementSize) {
case 1:
return Tmp.VnB();
case 2:
return Tmp.VnH();
case 4:
return Tmp.VnS();
case 8:
return Tmp.VnD();
default:
LOGMAN_MSG_A_FMT("Unhandled element size: {}", ElementSize);
return Tmp;
}
}();
dup(Dst, Op->Immediate);
} else {
if (ElementSize == 8) {
// movi with 64bit element size doesn't do what we want here
LoadConstant(TMP1.X(), Op->Immediate);
dup(GetDst(Node).V2D(), TMP1.X());
}
else {
movi(GetDst(Node).VCast(OpSize * 8, Elements), Op->Immediate);
}
}
}
@@ -79,59 +111,149 @@ DEF_OP(SplatVector4) {
}
DEF_OP(VMov) {
auto Op = IROp->C<IR::IROp_VMov>();
const uint8_t OpSize = IROp->Size;
auto Op = IROp->C<IR::IROp_VMov>();
const uint8_t OpSize = IROp->Size;
switch (OpSize) {
case 1: {
eor(VTMP1.V16B(), VTMP1.V16B(), VTMP1.V16B());
mov(VTMP1.V16B(), 0, GetSrc(Op->Source.ID()).V16B(), 0);
mov(GetDst(Node), VTMP1);
break;
}
case 2: {
eor(VTMP1.V16B(), VTMP1.V16B(), VTMP1.V16B());
mov(VTMP1.V8H(), 0, GetSrc(Op->Source.ID()).V8H(), 0);
mov(GetDst(Node), VTMP1);
break;
}
case 4: {
eor(VTMP1.V16B(), VTMP1.V16B(), VTMP1.V16B());
mov(VTMP1.V4S(), 0, GetSrc(Op->Source.ID()).V4S(), 0);
mov(GetDst(Node), VTMP1);
break;
}
case 8: {
mov(GetDst(Node).V8B(), GetSrc(Op->Source.ID()).V8B());
break;
}
case 16: {
if (GetDst(Node).GetCode() != GetSrc(Op->Source.ID()).GetCode())
mov(GetDst(Node).V16B(), GetSrc(Op->Source.ID()).V16B());
break;
}
default: LOGMAN_MSG_A_FMT("Unknown Element Size: {}", OpSize); break;
}
const auto Dst = GetDst(Node);
const auto Source = GetSrc(Op->Source.ID());
switch (OpSize) {
case 1: {
if (HostSupportsSVE) {
eor(VTMP1.Z().VnD(), VTMP1.Z().VnD(), VTMP1.Z().VnD());
} else {
eor(VTMP1.V16B(), VTMP1.V16B(), VTMP1.V16B());
}
mov(VTMP1.V16B(), 0, Source.V16B(), 0);
if (HostSupportsSVE) {
mov(Dst.Z().VnD(), VTMP1.Z().VnD());
} else {
mov(Dst, VTMP1);
}
break;
}
case 2: {
if (HostSupportsSVE) {
eor(VTMP1.Z().VnD(), VTMP1.Z().VnD(), VTMP1.Z().VnD());
} else {
eor(VTMP1.V16B(), VTMP1.V16B(), VTMP1.V16B());
}
mov(VTMP1.V8H(), 0, Source.V8H(), 0);
if (HostSupportsSVE) {
mov(Dst.Z().VnD(), VTMP1.Z().VnD());
} else {
mov(Dst, VTMP1);
}
break;
}
case 4: {
if (HostSupportsSVE) {
eor(VTMP1.Z().VnD(), VTMP1.Z().VnD(), VTMP1.Z().VnD());
} else {
eor(VTMP1.V16B(), VTMP1.V16B(), VTMP1.V16B());
}
mov(VTMP1.V4S(), 0, Source.V4S(), 0);
if (HostSupportsSVE) {
mov(Dst.Z().VnD(), VTMP1.Z().VnD());
} else {
mov(Dst, VTMP1);
}
break;
}
case 8: {
if (HostSupportsSVE) {
eor(VTMP1.Z().VnD(), VTMP1.Z().VnD(), VTMP1.Z().VnD());
mov(VTMP1.V8B(), Source.V8B());
mov(Dst.Z().VnB(), VTMP1.Z().VnB());
} else {
mov(Dst.V8B(), Source.V8B());
}
break;
}
case 16: {
if (HostSupportsSVE) {
eor(VTMP1.Z().VnD(), VTMP1.Z().VnD(), VTMP1.Z().VnD());
mov(VTMP1.V16B(), Source.V16B());
mov(Dst.Z().VnB(), VTMP1.Z().VnB());
} else {
if (Dst.GetCode() != Source.GetCode()) {
mov(Dst.V16B(), Source.V16B());
}
}
break;
}
case 32: {
if (Dst.GetCode() != Source.GetCode()) {
mov(Dst.Z().VnD(), Source.Z().VnD());
}
break;
}
default:
LOGMAN_MSG_A_FMT("Unknown Op Size: {}", OpSize);
break;
}
}
DEF_OP(VAnd) {
auto Op = IROp->C<IR::IROp_VAnd>();
and_(GetDst(Node).V16B(), GetSrc(Op->Vector1.ID()).V16B(), GetSrc(Op->Vector2.ID()).V16B());
const auto Dst = GetDst(Node);
const auto Vector1 = GetSrc(Op->Vector1.ID());
const auto Vector2 = GetSrc(Op->Vector2.ID());
if (HostSupportsSVE) {
and_(Dst.Z().VnD(), Vector1.Z().VnD(), Vector2.Z().VnD());
} else {
and_(Dst.V16B(), Vector1.V16B(), Vector2.V16B());
}
}
DEF_OP(VBic) {
auto Op = IROp->C<IR::IROp_VBic>();
bic(GetDst(Node).V16B(), GetSrc(Op->Vector1.ID()).V16B(), GetSrc(Op->Vector2.ID()).V16B());
const auto Dst = GetDst(Node);
const auto Vector1 = GetSrc(Op->Vector1.ID());
const auto Vector2 = GetSrc(Op->Vector2.ID());
if (HostSupportsSVE) {
bic(Dst.Z().VnD(), Vector1.Z().VnD(), Vector2.Z().VnD());
} else {
bic(Dst.V16B(), Vector1.V16B(), Vector2.V16B());
}
}
DEF_OP(VOr) {
auto Op = IROp->C<IR::IROp_VOr>();
orr(GetDst(Node).V16B(), GetSrc(Op->Vector1.ID()).V16B(), GetSrc(Op->Vector2.ID()).V16B());
const auto Dst = GetDst(Node);
const auto Vector1 = GetSrc(Op->Vector1.ID());
const auto Vector2 = GetSrc(Op->Vector2.ID());
if (HostSupportsSVE) {
orr(Dst.Z().VnD(), Vector1.Z().VnD(), Vector2.Z().VnD());
} else {
orr(Dst.V16B(), Vector1.V16B(), Vector2.V16B());
}
}
DEF_OP(VXor) {
auto Op = IROp->C<IR::IROp_VXor>();
eor(GetDst(Node).V16B(), GetSrc(Op->Vector1.ID()).V16B(), GetSrc(Op->Vector2.ID()).V16B());
const auto Dst = GetDst(Node);
const auto Vector1 = GetSrc(Op->Vector1.ID());
const auto Vector2 = GetSrc(Op->Vector2.ID());
if (HostSupportsSVE) {
eor(Dst.Z().VnD(), Vector1.Z().VnD(), Vector2.Z().VnD());
} else {
eor(Dst.V16B(), Vector1.V16B(), Vector2.V16B());
}
}
DEF_OP(VAdd) {
+3 -1
View File
@@ -25,6 +25,7 @@ $end_info$
#include <FEXCore/IR/IntrusiveIRList.h>
#include <FEXCore/IR/RegisterAllocationData.h>
#include <FEXCore/Utils/Allocator.h>
#include <FEXCore/Utils/EnumUtils.h>
#include <FEXCore/Utils/LogManager.h>
#include <algorithm>
@@ -293,7 +294,8 @@ void X86JITCore::Op_Unhandled(IR::IROp_Header *IROp, IR::NodeID Node) {
case FABI_UNKNOWN:
default:
#if defined(ASSERTIONS_ENABLED) && ASSERTIONS_ENABLED
LOGMAN_MSG_A_FMT("Unhandled IR Fallback ABI: {} {}", FEXCore::IR::GetName(IROp->Op), Info.ABI);
LOGMAN_MSG_A_FMT("Unhandled IR Fallback ABI: {} {}",
IR::GetName(IROp->Op), ToUnderlying(Info.ABI));
#endif
break;
}
@@ -181,6 +181,10 @@ private:
[[nodiscard]] Xbyak::Xmm GetSrc(IR::NodeID Node) const;
[[nodiscard]] Xbyak::Xmm GetDst(IR::NodeID Node) const;
[[nodiscard]] static Xbyak::Ymm ToYMM(const Xbyak::Xmm& xmm) {
return Xbyak::Ymm{xmm.getIdx()};
}
[[nodiscard]] Xbyak::RegExp GenerateModRM(Xbyak::Reg Base, IR::OrderedNodeWrapper Offset,
IR::MemOffsetType OffsetType, uint8_t OffsetScale) const;
@@ -24,10 +24,11 @@ DEF_OP(VectorZero) {
DEF_OP(VectorImm) {
auto Op = IROp->C<IR::IROp_VectorImm>();
uint8_t OpSize = IROp->Size;
auto Dst = GetDst(Node);
uint64_t Imm = Op->Immediate;
const uint8_t OpSize = IROp->Size;
const uint64_t Imm = Op->Immediate;
const auto Dst = GetDst(Node);
uint64_t Element{};
switch (Op->Header.ElementSize) {
@@ -61,9 +62,13 @@ DEF_OP(VectorImm) {
mov(TMP1, Element);
vmovq(Dst, TMP1);
if (OpSize == 16) {
// Duplicate to the upper 64bits if we are 128bits
movddup(Dst, Dst);
if (OpSize >= 16) {
LOGMAN_THROW_AA_FMT(OpSize == 16 || OpSize == 32,
"Can't handle a vector of size: {}", OpSize);
// Duplicate into upper elements
vbroadcastsd(ToYMM(Dst), Dst);
}
}
@@ -98,61 +103,90 @@ DEF_OP(VMov) {
auto Op = IROp->C<IR::IROp_VMov>();
const uint8_t OpSize = IROp->Size;
const auto Dst = GetDst(Node);
const auto Source = GetSrc(Op->Source.ID());
switch (OpSize) {
case 1: {
vpxor(xmm15, xmm15, xmm15);
pextrb(eax, GetSrc(Op->Source.ID()), 0);
pextrb(eax, Source, 0);
pinsrb(xmm15, eax, 0);
movapd(GetDst(Node), xmm15);
vmovapd(Dst, xmm15);
break;
}
case 2: {
vpxor(xmm15, xmm15, xmm15);
pextrw(eax, GetSrc(Op->Source.ID()), 0);
pextrw(eax, Source, 0);
pinsrw(xmm15, eax, 0);
movapd(GetDst(Node), xmm15);
vmovapd(Dst, xmm15);
break;
}
case 4: {
vpxor(xmm15, xmm15, xmm15);
pextrd(eax, GetSrc(Op->Source.ID()), 0);
pextrd(eax, Source, 0);
pinsrd(xmm15, eax, 0);
movapd(GetDst(Node), xmm15);
vmovapd(Dst, xmm15);
break;
}
case 8: {
movq(GetDst(Node), GetSrc(Op->Source.ID()));
vmovq(Dst, Source);
break;
}
case 16: {
movaps(GetDst(Node), GetSrc(Op->Source.ID()));
vmovaps(Dst, Source);
break;
}
default: LOGMAN_MSG_A_FMT("Unknown Element Size: {}", OpSize); break;
case 32: {
vmovaps(ToYMM(Dst), ToYMM(Source));
break;
}
default:
LOGMAN_MSG_A_FMT("Unknown Op Size: {}", OpSize);
break;
}
}
DEF_OP(VAnd) {
auto Op = IROp->C<IR::IROp_VAnd>();
vpand(GetDst(Node), GetSrc(Op->Vector1.ID()), GetSrc(Op->Vector2.ID()));
const auto Dst = ToYMM(GetDst(Node));
const auto Vector1 = ToYMM(GetSrc(Op->Vector1.ID()));
const auto Vector2 = ToYMM(GetSrc(Op->Vector2.ID()));
vpand(Dst, Vector1, Vector2);
}
DEF_OP(VBic) {
auto Op = IROp->C<IR::IROp_VBic>();
const auto Dst = ToYMM(GetDst(Node));
const auto Vector1 = ToYMM(GetSrc(Op->Vector1.ID()));
const auto Vector2 = ToYMM(GetSrc(Op->Vector2.ID()));
// This doesn't map directly to ARM
vpcmpeqd(xmm15, xmm15, xmm15);
vpxor(xmm15, GetSrc(Op->Vector2.ID()), xmm15);
vpand(GetDst(Node), GetSrc(Op->Vector1.ID()), xmm15);
vpcmpeqd(ymm15, ymm15, ymm15);
vpxor(ymm15, Vector2, ymm15);
vpand(Dst, Vector1, ymm15);
}
DEF_OP(VOr) {
auto Op = IROp->C<IR::IROp_VOr>();
vpor(GetDst(Node), GetSrc(Op->Vector1.ID()), GetSrc(Op->Vector2.ID()));
const auto Dst = ToYMM(GetDst(Node));
const auto Vector1 = ToYMM(GetSrc(Op->Vector1.ID()));
const auto Vector2 = ToYMM(GetSrc(Op->Vector2.ID()));
vpor(Dst, Vector1, Vector2);
}
DEF_OP(VXor) {
auto Op = IROp->C<IR::IROp_VXor>();
vpxor(GetDst(Node), GetSrc(Op->Vector1.ID()), GetSrc(Op->Vector2.ID()));
const auto Dst = ToYMM(GetDst(Node));
const auto Vector1 = ToYMM(GetSrc(Op->Vector1.ID()));
const auto Vector2 = ToYMM(GetSrc(Op->Vector2.ID()));
vpxor(Dst, Vector1, Vector2);
}
DEF_OP(VAdd) {
+19 -6
View File
@@ -28,7 +28,9 @@ $end_info$
#include <string>
#include <utility>
#ifdef ENABLE_JEMALLOC
#include "jemalloc/jemalloc.h"
#endif
struct LoadlibArgs {
const char *Name;
@@ -137,7 +139,7 @@ namespace FEXCore {
// sha256(fex:allocate_host_trampoline_for_guest_function)
{ 0x9b, 0xb2, 0xf4, 0xb4, 0x83, 0x7d, 0x28, 0x93, 0x40, 0xcb, 0xf4, 0x7a, 0x0b, 0x47, 0x85, 0x87, 0xf9, 0xbc, 0xb5, 0x27, 0xca, 0xa6, 0x93, 0xa5, 0xc0, 0x73, 0x27, 0x24, 0xae, 0xc8, 0xb8, 0x5a },
&AllocateHostTrampolineForGuestFunction
}
},
};
// Can't be a string_view. We need to keep a copy of the library name in-case string_view pointer goes away.
@@ -241,12 +243,17 @@ namespace FEXCore {
* and host heap pointers.
*/
static void IsHostHeapAllocation(void* ArgsRV) {
#ifdef ENABLE_JEMALLOC
struct ArgsRV_t {
void* ptr;
bool rv;
} *args = reinterpret_cast<ArgsRV_t*>(ArgsRV);
args->rv = je_is_known_allocation(args->ptr);
#else
// Thunks usage without jemalloc isn't supported
ERROR_AND_DIE_FMT("Unsupported: Thunks querying for host heap allocation information");
#endif
}
static void LoadLib(void *ArgsV) {
@@ -313,7 +320,7 @@ namespace FEXCore {
}
}
ThunkedFunction* LookupThunk(const IR::SHA256Sum &sha256) {
ThunkedFunction* LookupThunk(const IR::SHA256Sum &sha256) override {
std::shared_lock lk(ThunksMutex);
@@ -326,13 +333,19 @@ namespace FEXCore {
}
}
void RegisterTLSState(FEXCore::Core::InternalThreadState *Thread) {
void RegisterTLSState(FEXCore::Core::InternalThreadState *Thread) override {
::Thread = Thread;
}
void AppendThunkDefinitions(std::vector<FEXCore::IR::ThunkDefinition> const& Definitions) override {
for (auto & Definition : Definitions) {
Thunks.emplace(Definition.Sum, Definition.ThunkFunction);
}
}
};
ThunkHandler* ThunkHandler::Create() {
return new ThunkHandler_impl();
return new ThunkHandler_impl();
}
/**
@@ -358,7 +371,7 @@ namespace FEXCore {
* is to allocate the trampoline for a given GuestTarget/GuestUnpacker on the guest-side,
* and provide the HostPacker host-side.
*/
__attribute__((visibility("default")))
FEX_DEFAULT_VISIBILITY
HostToGuestTrampolinePtr* MakeHostTrampolineForGuestFunction(void* HostPacker, uintptr_t GuestTarget, uintptr_t GuestUnpacker) {
LOGMAN_THROW_AA_FMT(GuestTarget, "Tried to create host-trampoline to null pointer guest function");
@@ -416,7 +429,7 @@ namespace FEXCore {
return HostTrampoline;
}
__attribute__((visibility("default")))
FEX_DEFAULT_VISIBILITY
void FinalizeHostTrampolineForGuestFunction(HostToGuestTrampolinePtr* TrampolineAddress, void* HostPacker) {
auto& Trampoline = GetInstanceInfo(TrampolineAddress);
+6
View File
@@ -6,6 +6,10 @@ $end_info$
#pragma once
#include <FEXCore/IR/IR.h>
#include <vector>
namespace FEXCore::Context {
struct Context;
}
@@ -28,5 +32,7 @@ namespace FEXCore {
virtual ~ThunkHandler() { }
static ThunkHandler* Create();
virtual void AppendThunkDefinitions(std::vector<FEXCore::IR::ThunkDefinition> const& Definitions) = 0;
};
};
+102 -79
View File
@@ -6,6 +6,10 @@
#include <FEXHeaderUtils/TypeDefines.h>
#include <array>
#include <asm-generic/errno-base.h>
#include <cctype>
#include <cstdio>
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/user.h>
#ifdef ENABLE_JEMALLOC
@@ -131,91 +135,119 @@ namespace FEXCore::Allocator {
FEX_UNREACHABLE;
}
PtrCache* StealMemoryRegion(uintptr_t Begin, uintptr_t End) {
PtrCache *Cache{};
uint64_t CacheSize{};
uint64_t CurrentCacheOffset = 0;
constexpr std::array<size_t, 10> ReservedVMARegionSizes = {{
// Anything larger than 64GB fails out
64ULL * 1024 * 1024 * 1024, // 64GB
32ULL * 1024 * 1024 * 1024, // 32GB
16ULL * 1024 * 1024 * 1024, // 16GB
4ULL * 1024 * 1024 * 1024, // 4GB
1ULL * 1024 * 1024 * 1024, // 1GB
512ULL * 1024 * 1024, // 512MB
128ULL * 1024 * 1024, // 128MB
32ULL * 1024 * 1024, // 32MB
1ULL * 1024 * 1024, // 1MB
4096ULL // One page
}};
constexpr size_t AllocationSizeMaxIndex = ReservedVMARegionSizes.size() - 1;
uint64_t CurrentSizeIndex = 0;
#define STEAL_LOG(...) // fprintf(stderr, __VA_ARGS__)
int PROT_FLAGS = PROT_READ | PROT_WRITE;
for (size_t MemoryOffset = Begin; MemoryOffset < End;) {
size_t AllocationSize = ReservedVMARegionSizes[CurrentSizeIndex];
size_t MemoryOffsetUpper = MemoryOffset + AllocationSize;
std::vector<MemoryRegion> StealMemoryRegion(uintptr_t Begin, uintptr_t End) {
std::vector<MemoryRegion> Regions;
int MapsFD = open("/proc/self/maps", O_RDONLY);
LogMan::Throw::AFmt(MapsFD != -1, "Failed to open /proc/self/maps");
// If we would go above the upper bound on size then try the next size
if (MemoryOffsetUpper > End) {
++CurrentSizeIndex;
continue;
enum {ParseBegin, ParseEnd, ScanEnd} State = ParseBegin;
uintptr_t RegionBegin = 0;
uintptr_t RegionEnd = 0;
char Buffer[2048];
const char *Cursor;
ssize_t Remaining = 0;
for(;;) {
if (Remaining == 0) {
do {
Remaining = read(MapsFD, Buffer, sizeof(Buffer));
} while ( Remaining == -1 && errno == EAGAIN);
Cursor = Buffer;
}
void *Ptr = ::mmap(reinterpret_cast<void*>(MemoryOffset), AllocationSize, PROT_FLAGS, MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE | MAP_FIXED_NOREPLACE, -1, 0);
if (Remaining == 0 && State == ParseBegin) {
STEAL_LOG("[%d] EndOfFile; RegionBegin: %016lX RegionEnd: %016lX\n", __LINE__, RegionBegin, RegionEnd);
// If we managed to allocate and not get the address we want then unmap it
// This happens with kernels older than 4.17
if (reinterpret_cast<uintptr_t>(Ptr) + AllocationSize > End) {
::munmap(Ptr, AllocationSize);
Ptr = reinterpret_cast<void*>(~0ULL);
}
auto MapBegin = std::max(RegionEnd, Begin);
auto MapEnd = End;
// If we failed to allocate and we are on the smallest allocation size then just continue onward
// This page was unmappable
if (reinterpret_cast<uintptr_t>(Ptr) == ~0ULL && CurrentSizeIndex == AllocationSizeMaxIndex) {
CurrentSizeIndex = 0;
MemoryOffset += AllocationSize;
continue;
}
STEAL_LOG(" MapBegin: %016lX MapEnd: %016lX\n", MapBegin, MapEnd);
// Congratulations we were able to map this bit
// Reset and claim it was available
if (reinterpret_cast<uintptr_t>(Ptr) != ~0ULL) {
if (!Cache) {
Cache = reinterpret_cast<PtrCache *>(Ptr);
CacheSize = AllocationSize;
PROT_FLAGS = PROT_NONE;
}
else {
Cache[CurrentCacheOffset] = {
.Ptr = static_cast<uint64_t>(reinterpret_cast<uint64_t>(Ptr)),
.Size = static_cast<uint64_t>(AllocationSize)
};
++CurrentCacheOffset;
if (MapEnd > MapBegin) {
STEAL_LOG(" Reserving\n");
auto MapSize = MapEnd - MapBegin;
auto Alloc = mmap((void*)MapBegin, MapSize, PROT_NONE, MAP_ANONYMOUS | MAP_NORESERVE | MAP_PRIVATE | MAP_FIXED_NOREPLACE, -1, 0);
LogMan::Throw::AFmt(Alloc != MAP_FAILED, "mmap({:x},{:x}) failed", MapBegin, MapSize);
LogMan::Throw::AFmt(Alloc == (void*)MapBegin, "mmap({},{:x}) returned {} instead of {:x}", Alloc, MapBegin);
Regions.push_back({(void*)MapBegin, MapSize});
}
CurrentSizeIndex = 0;
MemoryOffset += AllocationSize;
close(MapsFD);
return Regions;
}
LogMan::Throw::AFmt(Remaining > 0, "Failed to parse /proc/self/maps");
auto c = *Cursor++;
Remaining--;
if (State == ScanEnd) {
if (c == '\n') {
State = ParseBegin;
}
continue;
}
// Couldn't allocate at this size
// Increase and continue
++CurrentSizeIndex;
if (State == ParseBegin) {
if (c == '-') {
STEAL_LOG("[%d] ParseBegin; RegionBegin: %016lX RegionEnd: %016lX\n", __LINE__, RegionBegin, RegionEnd);
auto MapBegin = std::max(RegionEnd, Begin);
auto MapEnd = std::min(RegionBegin, End);
STEAL_LOG(" MapBegin: %016lX MapEnd: %016lX\n", MapBegin, MapEnd);
if (MapEnd > MapBegin) {
STEAL_LOG(" Reserving\n");
auto MapSize = MapEnd - MapBegin;
auto Alloc = mmap((void*)MapBegin, MapSize, PROT_NONE, MAP_ANONYMOUS | MAP_NORESERVE | MAP_PRIVATE | MAP_FIXED_NOREPLACE, -1, 0);
LogMan::Throw::AFmt(Alloc != MAP_FAILED, "mmap({:x},{:x}) failed", MapBegin, MapSize);
LogMan::Throw::AFmt(Alloc == (void*)MapBegin, "mmap({},{:x}) returned {} instead of {:x}", Alloc, MapBegin);
Regions.push_back({(void*)MapBegin, MapSize});
}
RegionBegin = 0;
RegionEnd = 0;
State = ParseEnd;
continue;
} else {
LogMan::Throw::AFmt(std::isalpha(c) || std::isdigit(c), "Unexpected char '{}' in ParseBegin", c);
RegionBegin = (RegionBegin << 4) | (c <= '9' ? (c - '0') : (c - 'a' + 10));
}
}
if (State == ParseEnd) {
if (c == ' ') {
STEAL_LOG("[%d] ParseEnd; RegionBegin: %016lX RegionEnd: %016lX\n", __LINE__, RegionBegin, RegionEnd);
State = ScanEnd;
continue;
} else {
LogMan::Throw::AFmt(std::isalpha(c) || std::isdigit(c), "Unexpected char '{}' in ParseEnd", c);
RegionEnd = (RegionEnd << 4) | (c <= '9' ? (c - '0') : (c - 'a' + 10));
}
}
}
Cache[CurrentCacheOffset] = {
.Ptr = static_cast<uint64_t>(reinterpret_cast<uint64_t>(Cache)),
.Size = CacheSize,
};
return Cache;
ERROR_AND_DIE_FMT("unreachable");
}
PtrCache* Steal48BitVA() {
std::vector<MemoryRegion> Steal48BitVA() {
size_t Bits = FEXCore::Allocator::DetermineVASize();
if (Bits < 48) {
return nullptr;
return {};
}
uintptr_t Begin48BitVA = 0x0'8000'0000'0000ULL;
@@ -223,18 +255,9 @@ namespace FEXCore::Allocator {
return StealMemoryRegion(Begin48BitVA, End48BitVA);
}
void ReclaimMemoryRegion(PtrCache* Regions) {
if (Regions == nullptr) {
return;
}
for (size_t i = 0;; ++i) {
void *Ptr = reinterpret_cast<void*>(Regions[i].Ptr);
size_t Size = Regions[i].Size;
::munmap(Ptr, Size);
if (Ptr == Regions) {
break;
}
void ReclaimMemoryRegion(const std::vector<MemoryRegion> &Regions) {
for (const auto &Region: Regions) {
::munmap(Region.Ptr, Region.Size);
}
}
}
+51 -97
View File
@@ -70,7 +70,10 @@ namespace Alloc::OSAllocator {
ReservedVMARegion *SlabInfo;
uint64_t FreeSpace{};
uint32_t LastPageAllocation{};
FEXCore::FlexBitSet<uint64_t> UsedPages;
// Align UsedPages so it pads to the next page.
// Necessary to take advantage of madvise zero page pooling.
alignas(4096) FEXCore::FlexBitSet<uint64_t> UsedPages;
// This returns the size of the LiveVMARegion in addition to the flex set that tracks the used data
// The LiveVMARegion lives at the start of the VMA region which means on initialization we need to set that
@@ -93,16 +96,23 @@ namespace Alloc::OSAllocator {
Region->FreeSpace = Region->SlabInfo->RegionSize - SizePlusManagedData;
size_t NumPages = SizePlusManagedData >> FHU::FEX_PAGE_SHIFT;
// Memset the full tracking to zero to state nothing used
Region->UsedPages.MemSet(Region->SlabInfo->RegionSize >> FHU::FEX_PAGE_SHIFT);
// Use madvise to set the full tracking region to zero.
// This ensures unused pages are zero, while not having the backing pages consuming memory.
::madvise(Region->UsedPages.Memory + (NumPages * 4096), (Region->SlabInfo->RegionSize >> FHU::FEX_PAGE_SHIFT) - (NumPages * 4096), MADV_DONTNEED);
// Use madvise to claim WILLNEED on the beginning pages for initial state tracking.
// Improves performance of the following MemClear by not doing a page level fault dance for data necessary to track >170TB of used pages.
::madvise(Region->UsedPages.Memory, NumPages * 4096, MADV_WILLNEED);
// Set our reserved pages
for (size_t i = 0; i < NumPages; ++i) {
// Set our used pages
Region->UsedPages.Set(i);
}
Region->UsedPages.MemSet(NumPages);
Region->LastPageAllocation = NumPages;
}
};
static_assert(sizeof(LiveVMARegion) == 4096, "Needs to be the size of a page");
static_assert(std::is_trivially_copyable<LiveVMARegion>::value, "Needs to be trivially copyable");
static_assert(offsetof(LiveVMARegion, UsedPages) == sizeof(LiveVMARegion), "FlexBitSet needs to be at the end");
@@ -131,23 +141,29 @@ namespace Alloc::OSAllocator {
// Copy over the reserved data
LiveRange->SlabInfo = ReservedRegion;
// Initialize VMA
LiveVMARegion::InitializeVMARegionUsed(LiveRange, UsedSize);
// Add to our active tracked ranges
auto LiveIter = LiveRegions->emplace_back(LiveRange);
return LiveIter;
}
// 32-bit old kernel workarounds
FEXCore::Allocator::PtrCache *Steal32BitIfOldKernel();
std::vector<FEXCore::Allocator::MemoryRegion> Steal32BitIfOldKernel();
};
void OSAllocator_64Bit::DetermineVASize() {
size_t Bits = FEXCore::Allocator::DetermineVASize();
uintptr_t Size = 1ULL << Bits;
UPPER_BOUND = Size;
#if _M_X86_64 // Last page cannot be allocated on x86
UPPER_BOUND -= FHU::FEX_PAGE_SIZE;
#endif
UPPER_BOUND_PAGE = UPPER_BOUND / FHU::FEX_PAGE_SIZE;
}
@@ -490,11 +506,11 @@ int OSAllocator_64Bit::Munmap(void *addr, size_t length) {
return 0;
}
FEXCore::Allocator::PtrCache *OSAllocator_64Bit::Steal32BitIfOldKernel() {
std::vector<FEXCore::Allocator::MemoryRegion> OSAllocator_64Bit::Steal32BitIfOldKernel() {
// First calculate kernel version
struct utsname buf{};
if (uname(&buf) == -1) {
return nullptr;
return {};
}
int32_t Major{};
@@ -512,7 +528,7 @@ FEXCore::Allocator::PtrCache *OSAllocator_64Bit::Steal32BitIfOldKernel() {
if (Version >= ((4 << 24) | (17 << 16) | 0)) {
// If the kernel is >= 4.17 then it supports MAP_FIXED_NOREPLACE
return nullptr;
return {};
}
constexpr size_t LOWER_BOUND_32 = 0x1'0000;
@@ -523,101 +539,39 @@ FEXCore::Allocator::PtrCache *OSAllocator_64Bit::Steal32BitIfOldKernel() {
OSAllocator_64Bit::OSAllocator_64Bit() {
DetermineVASize();
auto ArrayPtr = Steal32BitIfOldKernel();
auto LowMem = Steal32BitIfOldKernel();
// On allocation try and steal the entire upper 64bits of address space for mapping
constexpr std::array<size_t, 8> ReservedVMARegionSizes = {{
// Anything larger than 64GB fails out
64ULL * 1024 * 1024 * 1024, // 64GB
32ULL * 1024 * 1024 * 1024, // 32GB
16ULL * 1024 * 1024 * 1024, // 16GB
4ULL * 1024 * 1024 * 1024, // 4GB
1ULL * 1024 * 1024 * 1024, // 1GB
512ULL * 1024 * 1024, // 512MB
128ULL * 1024 * 1024, // 128MB
4096ULL // One page
}};
auto Ranges = FEXCore::Allocator::StealMemoryRegion(LOWER_BOUND, UPPER_BOUND);
constexpr size_t AllocationSizeMaxIndex = ReservedVMARegionSizes.size() - 1;
for (auto [Ptr, AllocationSize]: Ranges) {
if (!ObjectAlloc) {
auto MaxSize = std::min(size_t(64) * 1024 * 1024, AllocationSize);
// Have the first region only be 4GB VMA
// Avoids conflicts with some tests
uint64_t CurrentSizeIndex = 3;
ReservedVMARegion *PrevReserved{};
for (size_t MemoryOffset = LOWER_BOUND; MemoryOffset < UPPER_BOUND;) {
size_t AllocationSize = ReservedVMARegionSizes[CurrentSizeIndex];
size_t MemoryOffsetUpper = MemoryOffset + AllocationSize;
// Allocate up to 64 MiB the first allocation for an intrusive allocator
mprotect(Ptr, MaxSize, PROT_READ | PROT_WRITE);
// If we would go above the upper bound on size then try the next size
if (MemoryOffsetUpper > UPPER_BOUND) {
++CurrentSizeIndex;
continue;
}
// This enables the kernel to use transparent large pages in the allocator which can reduce memory pressure
::madvise(Ptr, MaxSize, MADV_HUGEPAGE);
void *Ptr = ::mmap(reinterpret_cast<void*>(MemoryOffset), AllocationSize, PROT_NONE, MAP_FIXED_NOREPLACE | MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE, -1, 0);
ObjectAlloc = new (Ptr) Alloc::ForwardOnlyIntrusiveArenaAllocator(Ptr, MaxSize);
ReservedRegions = ObjectAlloc->new_construct(ReservedRegions, ObjectAlloc);
LiveRegions = ObjectAlloc->new_construct(LiveRegions, ObjectAlloc);
// If we managed to allocate and not get the address we want then unmap it
// This happens with kernels older than 4.17
if (reinterpret_cast<uintptr_t>(Ptr) != MemoryOffset &&
reinterpret_cast<uintptr_t>(Ptr) < LOWER_BOUND) {
::munmap(Ptr, AllocationSize);
Ptr = reinterpret_cast<void*>(~0ULL);
}
// If we failed to allocate and we are on the smallest allocation size then just continue onward
// This page was unmappable
if (reinterpret_cast<uintptr_t>(Ptr) == ~0ULL && CurrentSizeIndex == AllocationSizeMaxIndex) {
CurrentSizeIndex = 0;
MemoryOffset += AllocationSize;
continue;
}
// Congratulations we were able to map this bit
// Reset and claim it was available
if (reinterpret_cast<uintptr_t>(Ptr) != ~0ULL) {
if (!ObjectAlloc) {
// Steal the first allocation for an intrusive allocator
// Will be mprotected correctly already
mprotect(Ptr, AllocationSize, PROT_READ | PROT_WRITE);
ObjectAlloc = new (Ptr) Alloc::ForwardOnlyIntrusiveArenaAllocator(Ptr, AllocationSize);
ReservedRegions = ObjectAlloc->new_construct(ReservedRegions, ObjectAlloc);
LiveRegions = ObjectAlloc->new_construct(LiveRegions, ObjectAlloc);
if (AllocationSize > MaxSize) {
AllocationSize -= MaxSize;
(uint8_t*&)Ptr += MaxSize;
} else {
continue;
}
else {
// If the allocation size is large than a page, then try allowing it to be a huge page
// This enables the kernel to use transparent large pages in the allocator which can reduce memory pressure
// Considering we are allocating the entire VA space, this is a good thing
// If MADV_HUGEPAGE isn't support then this will fail harmlessly
if (AllocationSize > 4096) {
::madvise(Ptr, AllocationSize, MADV_HUGEPAGE);
}
bool Merged = false;
if (PrevReserved) {
Merged = MergeReservedRegionIfPossible(PrevReserved, reinterpret_cast<uint64_t>(Ptr), AllocationSize);
}
if (!Merged) {
ReservedVMARegion *Region = ObjectAlloc->new_construct<ReservedVMARegion>();
Region->Base = reinterpret_cast<uint64_t>(Ptr);
Region->RegionSize = AllocationSize;
ReservedRegions->emplace_back(Region);
PrevReserved = Region;
}
}
CurrentSizeIndex = 0;
MemoryOffset += AllocationSize;
continue;
}
// Couldn't allocate at this size
// Increase and continue
++CurrentSizeIndex;
ReservedVMARegion *Region = ObjectAlloc->new_construct<ReservedVMARegion>();
Region->Base = reinterpret_cast<uint64_t>(Ptr);
Region->RegionSize = AllocationSize;
ReservedRegions->emplace_back(Region);
}
FEXCore::Allocator::ReclaimMemoryRegion(ArrayPtr);
FEXCore::Allocator::ReclaimMemoryRegion(LowMem);
}
OSAllocator_64Bit::~OSAllocator_64Bit() {
+9
View File
@@ -5,6 +5,7 @@
#include <FEXCore/Core/SignalDelegator.h>
#include <FEXCore/Core/CPUID.h>
#include <FEXCore/IR/IR.h>
#include <FEXCore/Utils/CompilerDefs.h>
#include <istream>
@@ -280,4 +281,12 @@ namespace FEXCore::Context {
FEX_DEFAULT_VISIBILITY void ConfigureAOTGen(FEXCore::Core::InternalThreadState *Thread, std::set<uint64_t> *ExternalBranches, uint64_t SectionMaxAddress);
FEX_DEFAULT_VISIBILITY CustomIRResult AddCustomIREntrypoint(FEXCore::Context::Context *CTX, uintptr_t Entrypoint, std::function<void(uintptr_t Entrypoint, FEXCore::IR::IREmitter *)> Handler, void *Creator = nullptr, void *Data = nullptr);
/**
* @brief Allows the frontend to register its own thunk handlers independent of what is controlled in the backend.
*
* @param CTX A valid non-null context instance.
* @param Definitions A vector of thunk definitions that the frontend controls
*/
FEX_DEFAULT_VISIBILITY void AppendThunkDefinitions(FEXCore::Context::Context *CTX, std::vector<FEXCore::IR::ThunkDefinition> const& Definitions);
}
+5
View File
@@ -21,7 +21,12 @@ class HostFeatures final {
bool SupportsRCPC{};
bool SupportsTSOImm9{};
bool SupportsRAND{};
bool Supports3DNow{};
bool SupportsSSE4A{};
bool SupportsAVX{};
bool SupportsSHA{};
bool SupportsBMI1{};
bool SupportsBMI2{};
// Float exception behaviour
bool SupportsFlushInputsToZero{};
+8
View File
@@ -58,6 +58,14 @@ namespace FEXCore {
FEATURE_XTILE_DATA = 1U << 18,
};
bool HasExtendedContext() const {
return magic1 == FP_XSTATE_MAGIC;
}
bool HasYMMH() const {
return (xfeatures & FEATURE_YMM) != 0;
}
// If magic1 is set to FP_XSTATE_MAGIC, then the encompassing
// frame is an xstate frame. If 0, then it's a legacy frame.
uint32_t magic1;
+7
View File
@@ -413,6 +413,13 @@ struct SHA256Sum final {
}
};
typedef void ThunkedFunction(void* ArgsRv);
struct ThunkDefinition final {
SHA256Sum Sum;
ThunkedFunction *ThunkFunction;
};
class NodeIterator;
/* This iterator can be used to step though nodes.
+8 -7
View File
@@ -5,6 +5,7 @@
#include <cstdint>
#include <functional>
#include <sys/types.h>
#include <vector>
namespace FEXCore::Allocator {
using MMAP_Hook = void*(*)(void*, size_t, int, int, int, off_t);
@@ -24,19 +25,19 @@ namespace FEXCore::Allocator {
FEX_DEFAULT_VISIBILITY void ClearHooks();
FEX_DEFAULT_VISIBILITY size_t DetermineVASize();
// 48-bit VA handling
struct PtrCache {
uint64_t Ptr;
uint64_t Size;
struct MemoryRegion {
void *Ptr;
size_t Size;
};
FEX_DEFAULT_VISIBILITY PtrCache* StealMemoryRegion(uintptr_t Begin, uintptr_t End);
FEX_DEFAULT_VISIBILITY void ReclaimMemoryRegion(PtrCache* Regions);
FEX_DEFAULT_VISIBILITY std::vector<MemoryRegion> StealMemoryRegion(uintptr_t Begin, uintptr_t End);
FEX_DEFAULT_VISIBILITY void ReclaimMemoryRegion(const std::vector<MemoryRegion> & Regions);
// When running a 64-bit executable on ARM then userspace guest only gets 47 bits of VA
// This is a feature of x86-64 where the kernel gets a full 128TB of VA space
// x86-64 canonical addresses with bit 48 set will sign extend the address (Ignoring LA57)
// AArch64 canonical addresses are only up to bits 48/52 with the remainder being other things
// Use this to reserve the top 128TB of VA so the guest never see it
// Returns nullptr on host VA < 48bits
FEX_DEFAULT_VISIBILITY PtrCache* Steal48BitVA();
FEX_DEFAULT_VISIBILITY std::vector<MemoryRegion> Steal48BitVA();
}
+2 -2
View File
@@ -30,11 +30,11 @@ On AArch64 hosts the user **MUST** have an x86-64 RootFS [Creating a RootFS](#Ro
See the [Source Outline](docs/SourceOutline.md) for more information.
### Building FEX
Follow the guide on the official FEX-Emu Wiki [here](https://wiki.fex-emu.org/index.php/Development:Setting_up_FEX).
Follow the guide on the official FEX-Emu Wiki [here](https://wiki.fex-emu.com/index.php/Development:Setting_up_FEX).
### RootFS generation
AArch64 hosts require a rootfs for running applications.
Follow the guide on the wiki page for seeing how to set up the rootfs from scratch
https://wiki.fex-emu.org/index.php/Development:Setting_up_RootFS
https://wiki.fex-emu.com/index.php/Development:Setting_up_RootFS
![FEX diagram](docs/Diagram.svg)
+7 -1
View File
@@ -59,7 +59,13 @@ SyscallArm64File = "/include/uapi/asm-generic/unistd.h"
DefinitionRenameDict = {
"pread64": "pread_64",
"pwrite64": "pwrite_64",
"prlimit64": "prlimit_64"
"prlimit64": "prlimit_64",
# Shm symbols conflict with termux defines and FEX's syscall token pasting.
# Underscore at the start to avoid name collision
"shmget": "_shmget",
"shmctl": "_shmctl",
"shmat": "_shmat",
"shmdt": "_shmdt",
}
Definitions_x64 = []
+3 -1
View File
@@ -306,12 +306,14 @@ def GetRootFSPath():
return _RootFSPath
def CheckRootFSInstallStatus():
# Matches what is available on https://rootfs.fex-emu.org/file/fex-rootfs/RootFS_links_XXH3.txt
# Matches what is available on https://rootfs.fex-emu.com/file/fex-rootfs/RootFS_links.json
UbuntuVersionToRootFS = {
"20.04": "Ubuntu_21_04.sqsh",
"21.04": "Ubuntu_21_04.sqsh",
"21.10": "Ubuntu_21_10.sqsh",
"21.10": "Ubuntu_21_10.ero",
"22.04": "Ubuntu_22_04.sqsh",
"22.04": "Ubuntu_22_04.ero",
}
return os.path.exists(GetRootFSPath() + UbuntuVersionToRootFS[GetDistro()[1]])
+27 -9
View File
@@ -63,8 +63,15 @@ class Mode(Flag) :
MODE_64 = 1
class HostFeatures(Flag) :
ANY = 0
AVX_ONLY = 1
FEATURE_ANY = 0
FEATURE_3DNOW = (1 << 0)
FEATURE_SSE4A = (1 << 1)
FEATURE_AVX = (1 << 2)
FEATURE_RAND = (1 << 3)
FEATURE_SHA = (1 << 4)
FEATURE_CLZERO = (1 << 5)
FEATURE_BMI1 = (1 << 6)
FEATURE_BMI2 = (1 << 7)
RegStringLookup = {
"NONE": Regs.REG_NONE,
@@ -128,8 +135,14 @@ ModeStringLookup = {
}
HostFeaturesLookup = {
"ANY" : HostFeatures.ANY,
"AVX" : HostFeatures.AVX_ONLY,
"3DNOW" : HostFeatures.FEATURE_3DNOW,
"SSE4A" : HostFeatures.FEATURE_SSE4A,
"AVX" : HostFeatures.FEATURE_AVX,
"RAND" : HostFeatures.FEATURE_RAND,
"SHA" : HostFeatures.FEATURE_SHA,
"CLZERO" : HostFeatures.FEATURE_CLZERO,
"BMI1" : HostFeatures.FEATURE_BMI1,
"BMI2" : HostFeatures.FEATURE_BMI2,
}
def parse_hexstring(s):
@@ -152,7 +165,7 @@ def parse_json(json_text, output_file):
OptionIgnore = Regs.REG_NONE
OptionABI = ABI.ABI_SYSTEMV
OptionMode = Mode.MODE_64
OptionHostFeatures = HostFeatures.ANY
OptionHostFeatures = HostFeatures.FEATURE_ANY
OptionStackSize = 4096
OptionEntryPoint = 1
OptionRegData = {}
@@ -208,10 +221,15 @@ def parse_json(json_text, output_file):
if ("HOSTFEATURES" in json_object):
data = json_object["HOSTFEATURES"]
data = data.upper()
if not (data in HostFeaturesLookup):
sys.exit("Invalid host feature")
OptionHostFeatures = HostFeaturesLookup[data]
if not (type(data) is list):
sys.exit("HostFeatures value must be list of features")
for data_key in data:
data_key = data_key.upper()
if not (data_key in HostFeaturesLookup):
sys.exit("Invalid host feature")
OptionHostFeatures |= HostFeaturesLookup[data_key]
if ("STACKSIZE" in json_object):
data = json_object["STACKSIZE"]
+4 -3
View File
@@ -5,12 +5,13 @@
#include <fcntl.h>
#include <filesystem>
#include <linux/limits.h>
#include <optional>
#include <unistd.h>
namespace FEX {
[[maybe_unused]]
static
std::string get_fdpath(int fd) {
std::optional<std::string> get_fdpath(int fd) {
char SymlinkPath[PATH_MAX];
std::filesystem::path Path = std::filesystem::path("/proc/self/fd") / std::to_string(fd);
int Result = readlinkat(AT_FDCWD, Path.c_str(), SymlinkPath, sizeof(SymlinkPath));
@@ -18,8 +19,8 @@ std::string get_fdpath(int fd) {
return std::string(SymlinkPath, Result);
}
LOGMAN_MSG_A_FMT("Couldn't get symlink from /proc/self/fd/{}", fd);
return {};
// Not fatal if an FD doesn't point to a file
return std::nullopt;
}
}
+6 -1
View File
@@ -101,7 +101,12 @@ namespace FEXServerClient {
}
std::string GetServerSocketFile() {
return fmt::format("{}/{}.FEXServer.socket", std::filesystem::temp_directory_path().string(), ::geteuid());
FEX_CONFIG_OPT(ServerSocketPath, SERVERSOCKETPATH);
if (ServerSocketPath().empty()) {
return fmt::format("{}/{}.FEXServer.socket", std::filesystem::temp_directory_path().string(), ::geteuid());
}
return ServerSocketPath;
}
int GetServerFD() {
+36 -28
View File
@@ -2,8 +2,14 @@ add_subdirectory(LinuxSyscalls)
list(APPEND LIBS FEXCore Common)
if (TERMUX_BUILD)
# Termux needs android-shmem to get the shm emulation library.
list(APPEND LIBS android-shmem)
endif()
add_executable(FEXLoader
FEXLoader.cpp
VDSO_Emulation.cpp
AOT/AOTGenerator.cpp)
# Enable FEX APIs to be used by targets that use target_link_libraries on FEXLoader
@@ -159,18 +165,20 @@ install(TARGETS FEXBash
COMPONENT runtime
)
add_executable(TestHarnessRunner TestHarnessRunner/HostRunner.cpp TestHarnessRunner.cpp)
target_include_directories(TestHarnessRunner
PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}/Source/
${CMAKE_BINARY_DIR}/generated
)
target_link_libraries(TestHarnessRunner
PRIVATE
${LIBS}
LinuxEmulation
${PTHREAD_LIB}
)
if (BUILD_TESTS)
add_executable(TestHarnessRunner TestHarnessRunner/HostRunner.cpp TestHarnessRunner.cpp)
target_include_directories(TestHarnessRunner
PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}/Source/
${CMAKE_BINARY_DIR}/generated
)
target_link_libraries(TestHarnessRunner
PRIVATE
${LIBS}
LinuxEmulation
${PTHREAD_LIB}
)
# add_executable(UnitTestGenerator UnitTestGenerator.cpp)
# target_include_directories(UnitTestGenerator
@@ -184,19 +192,19 @@ target_link_libraries(TestHarnessRunner
# )
#
add_executable(IRLoader
IRLoader.cpp
)
target_include_directories(IRLoader
PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}/Source/
${CMAKE_BINARY_DIR}/generated
)
target_link_libraries(IRLoader
PRIVATE
${LIBS}
LinuxEmulation
${PTHREAD_LIB}
fmt::fmt
)
add_executable(IRLoader
IRLoader.cpp
)
target_include_directories(IRLoader
PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}/Source/
${CMAKE_BINARY_DIR}/generated
)
target_link_libraries(IRLoader
PRIVATE
${LIBS}
LinuxEmulation
${PTHREAD_LIB}
fmt::fmt
)
endif()
+14 -4
View File
@@ -79,7 +79,9 @@ class ELFCodeLoader2 final : public FEXCore::CodeLoader {
return false;
} else {
auto Filename = FEX::get_fdpath(file.fd);
Sections.push_back({Base, (uintptr_t)rv, size, (off_t)off, Filename, (prot & PROT_EXEC) != 0});
if (Filename.has_value()) {
Sections.push_back({Base, (uintptr_t)rv, size, (off_t)off, Filename.value(), (prot & PROT_EXEC) != 0});
}
return true;
}
@@ -207,6 +209,7 @@ class ELFCodeLoader2 final : public FEXCore::CodeLoader {
};
std::vector<LoadedSection> Sections;
ELFCodeLoader2(std::string const &Filename, std::string const &RootFS, [[maybe_unused]] std::vector<std::string> const &args, std::vector<std::string> const &ParsedArgs, char **const envp = nullptr, FEXCore::Config::Value<std::string> *AdditionalEnvp = nullptr) :
Args {args} {
@@ -424,9 +427,11 @@ class ELFCodeLoader2 final : public FEXCore::CodeLoader {
// On x86 only allows userspace to check for monitor and fs/gs base writing in CPL3
//AuxVariables.emplace_back(auxv_t{26, 0}); // AT_HWCAP2
// we don't support vsyscall or vDSO so we don't set those
// we don't support vsyscall so we don't set those
//AuxVariables.emplace_back(auxv_t{32, 0}); // AT_SYSINFO - Entry point to syscall
//AuxVariables.emplace_back(auxv_t{33, 0}); // AT_SYSINFO_EHDR - Address of the start of VDSO
if (VDSOBase) {
AuxVariables.emplace_back(auxv_t{33, reinterpret_cast<uint64_t>(VDSOBase)}); // AT_SYSINFO_EHDR - Address of the start of VDSO
}
}
else {
AuxVariables.emplace_back(auxv_t{4, 0x20}); // AT_PHENT
@@ -638,6 +643,10 @@ class ELFCodeLoader2 final : public FEXCore::CodeLoader {
return ElfValid;
}
void SetVDSOBase(void* Base) {
VDSOBase = Base;
}
constexpr static uint64_t BRK_SIZE = 8 * 1024 * 1024;
constexpr static uint64_t STACK_SIZE = 8 * 1024 * 1024;
@@ -650,6 +659,7 @@ class ELFCodeLoader2 final : public FEXCore::CodeLoader {
uint64_t ArgumentBackingSize{};
uint64_t EnvironmentBackingSize{};
uint64_t BaseOffset{};
FEX_CONFIG_OPT(AdditionalArguments, ADDITIONALARGUMENTS);
void* VDSOBase{};
FEX_CONFIG_OPT(AdditionalArguments, ADDITIONALARGUMENTS);
};
+12 -1
View File
@@ -9,6 +9,7 @@ $end_info$
#include "Common/ArgumentLoader.h"
#include "Common/FEXServerClient.h"
#include "ELFCodeLoader2.h"
#include "VDSO_Emulation.h"
#include "Tests/LinuxSyscalls/LinuxAllocator.h"
#include "Tests/LinuxSyscalls/Syscalls.h"
#include "Tests/LinuxSyscalls/x32/Syscalls.h"
@@ -328,10 +329,11 @@ int main(int argc, char **argv, char **const envp) {
}
FEXCore::Config::Set(FEXCore::Config::CONFIG_APP_FILENAME, std::filesystem::canonical(Program.first).string());
FEXCore::Config::Set(FEXCore::Config::CONFIG_APP_CONFIG_NAME, Program.second);
FEXCore::Config::Set(FEXCore::Config::CONFIG_IS64BIT_MODE, Loader.Is64BitMode() ? "1" : "0");
std::unique_ptr<FEX::HLE::MemAllocator> Allocator;
FEXCore::Allocator::PtrCache *Base48Bit{};
std::vector<FEXCore::Allocator::MemoryRegion> Base48Bit;
if (Loader.Is64BitMode()) {
// Destroy the 48th bit if it exists
@@ -388,6 +390,12 @@ int main(int argc, char **argv, char **const envp) {
auto Mapper = std::bind_front(&FEX::HLE::SyscallHandler::GuestMmap, SyscallHandler.get());
auto Unmapper = std::bind_front(&FEX::HLE::SyscallHandler::GuestMunmap, SyscallHandler.get());
if (Loader.Is64BitMode()) {
// Load VDSO in to memory prior to mapping our ELFs.
void* VDSOBase = FEX::VDSO::LoadVDSOThunks(Mapper);
Loader.SetVDSOBase(VDSOBase);
}
if (!Loader.MapMemory(Mapper, Unmapper)) {
// failed to map
LogMan::Msg::EFmt("Failed to map %d-bit elf file.", Loader.Is64BitMode() ? 64 : 32);
@@ -404,6 +412,9 @@ int main(int argc, char **argv, char **const envp) {
FEXCore::Context::SetSyscallHandler(CTX, SyscallHandler.get());
FEXCore::Context::InitCore(CTX, Loader.DefaultRIP(), Loader.GetStackPointer());
// Pass in our VDSO thunks
FEXCore::Context::AppendThunkDefinitions(CTX, FEX::VDSO::GetVDSOThunkDefinitions());
FEXCore::Context::ExitReason ShutdownReason = FEXCore::Context::ExitReason::EXIT_SHUTDOWN;
// There might already be an exit handler, leave it installed
+29 -2
View File
@@ -374,7 +374,27 @@ namespace FEX::HarnessHelper {
}
bool Is64BitMode() const { return BaseConfig.OptionMode == 1; }
bool RequiresAVX() const { return BaseConfig.OptionHostFeatures == 1; }
enum HostFeatures {
FEATURE_ANY = 0,
FEATURE_3DNOW = (1 << 0),
FEATURE_SSE4A = (1 << 1),
FEATURE_AVX = (1 << 2),
FEATURE_RAND = (1 << 3),
FEATURE_SHA = (1 << 4),
FEATURE_CLZERO = (1 << 5),
FEATURE_BMI1 = (1 << 6),
FEATURE_BMI2 = (1 << 7),
};
bool Requires3DNow() const { return BaseConfig.OptionHostFeatures & HostFeatures::FEATURE_3DNOW; }
bool RequiresSSE4A() const { return BaseConfig.OptionHostFeatures & HostFeatures::FEATURE_SSE4A; }
bool RequiresAVX() const { return BaseConfig.OptionHostFeatures & HostFeatures::FEATURE_AVX; }
bool RequiresRAND() const { return BaseConfig.OptionHostFeatures & HostFeatures::FEATURE_RAND; }
bool RequiresSHA() const { return BaseConfig.OptionHostFeatures & HostFeatures::FEATURE_SHA; }
bool RequiresCLZERO() const { return BaseConfig.OptionHostFeatures & HostFeatures::FEATURE_CLZERO; }
bool RequiresBMI1() const { return BaseConfig.OptionHostFeatures & HostFeatures::FEATURE_BMI1; }
bool RequiresBMI2() const { return BaseConfig.OptionHostFeatures & HostFeatures::FEATURE_BMI2; }
private:
FEX_CONFIG_OPT(ConfigDumpGPRs, DUMPGPRS);
@@ -506,7 +526,14 @@ namespace FEX::HarnessHelper {
}
bool Is64BitMode() const { return Config.Is64BitMode(); }
bool RequiresAVX() const { return Config.RequiresAVX(); }
bool Requires3DNow() const { return Config.Requires3DNow(); }
bool RequiresSSE4A() const { return Config.RequiresSSE4A(); }
bool RequiresAVX() const { return Config.RequiresAVX(); }
bool RequiresRAND() const { return Config.RequiresRAND(); }
bool RequiresSHA() const { return Config.RequiresSHA(); }
bool RequiresCLZERO() const { return Config.RequiresCLZERO(); }
bool RequiresBMI1() const { return Config.RequiresBMI1(); }
bool RequiresBMI2() const { return Config.RequiresBMI2(); }
private:
constexpr static uint64_t STACK_SIZE = FHU::FEX_PAGE_SIZE;
@@ -204,10 +204,10 @@ enum Syscalls_Arm64 {
SYSCALL_Arm64_semctl = 191,
SYSCALL_Arm64_semtimedop = 192,
SYSCALL_Arm64_semop = 193,
SYSCALL_Arm64_shmget = 194,
SYSCALL_Arm64_shmctl = 195,
SYSCALL_Arm64_shmat = 196,
SYSCALL_Arm64_shmdt = 197,
SYSCALL_Arm64__shmget = 194,
SYSCALL_Arm64__shmctl = 195,
SYSCALL_Arm64__shmat = 196,
SYSCALL_Arm64__shmdt = 197,
SYSCALL_Arm64_socket = 198,
SYSCALL_Arm64_socketpair = 199,
SYSCALL_Arm64_bind = 200,
@@ -714,7 +714,7 @@ namespace FEX::EmulatedFile {
dirfs != AT_FDCWD) {
// Passed in a dirfd that isn't magic FDCWD
// We need to get the path from the fd now
Path = FEX::get_fdpath(dirfs);
Path = FEX::get_fdpath(dirfs).value_or("");
if (pathname) {
if (!Path.empty()) {
+83 -96
View File
@@ -7,6 +7,7 @@ $end_info$
#include "Common/FDUtils.h"
#include "FEXCore/Config/Config.h"
#include "Tests/LinuxSyscalls/FileManagement.h"
#include "Tests/LinuxSyscalls/EmulatedFiles/EmulatedFiles.h"
#include "Tests/LinuxSyscalls/Syscalls.h"
@@ -222,16 +223,81 @@ void FileManager::LoadThunkDatabase(bool Global) {
FileManager::FileManager(FEXCore::Context::Context *ctx)
: EmuFD {ctx} {
bool LoadedThunkDatabase{};
auto ThunkConfigFile = ThunkConfig();
auto ThunkGuestPath = std::filesystem::path(ThunkGuestLibs());
if (ThunkConfigFile.size()) {
auto LoadThunksDB = [this, ThunkGuestPath](bool *LoadedThunkDatabase, json_t const* ThunksDB) {
// If a thunks DB property exists then we pull in data from the thunks database
// Load the initial thunks database
if (LoadedThunkDatabase) {
LoadThunkDatabase(true);
LoadThunkDatabase(false);
*LoadedThunkDatabase = true;
}
auto ThunkGuestPath = std::filesystem::path(ThunkGuestLibs());
// Now load this property
for (json_t const* Item = json_getChild(ThunksDB); Item != nullptr; Item = json_getSibling(Item)) {
const char *LibraryName = json_getName(Item);
int64_t LibraryEnabled = json_getInteger(Item);
if (LibraryEnabled != 0) {
// If the library is enabled then find it in the DB
// Enable the overlay and all the dependencies in one go
auto DBObject = ThunkDB.find(LibraryName);
if (DBObject != ThunkDB.end() &&
DBObject->second.Enabled == false) {
auto ThunkPath = ThunkGuestPath / DBObject->second.LibraryName;
if (std::filesystem::exists(ThunkPath)) {
for (auto Overlay : DBObject->second.Overlays) {
// Direct full path in guest RootFS to our overlay file
ThunkOverlays.emplace(Overlay, ThunkPath);
}
}
DBObject->second.Enabled = true;
// Now walk the dependencies and set them up as well
// Make sure to enable each one as we go to remove circular dependencies
std::function<void(std::unordered_set<std::string> &Depends)> InsertDependencies
= [this, &ThunkGuestPath, &InsertDependencies](std::unordered_set<std::string> &Depends) -> void {
for (auto &Depend : Depends) {
auto DBDepend = ThunkDB.find(Depend);
if (DBDepend != ThunkDB.end() &&
DBDepend->second.Enabled == false) {
auto ThunkPath = ThunkGuestPath / DBDepend->second.LibraryName;
if (std::filesystem::exists(ThunkPath)) {
for (auto Overlay : DBDepend->second.Overlays) {
// Direct full path in guest RootFS to our overlay file
ThunkOverlays.emplace(Overlay, ThunkPath);
}
}
// Enabled, now walk this dependencies
DBDepend->second.Enabled = true;
InsertDependencies(DBDepend->second.Depends);
}
}
};
InsertDependencies(DBObject->second.Depends);
}
}
}
};
// We try to load ThunksDB from {FEX global config, FEX user config, AppConfig Global, AppConfig Local, Defined ThunksConfig option}
// This doesn't support the classic thunks interface.
std::vector<std::string> ConfigPaths {
FEXCore::Config::GetConfigFileLocation(true),
FEXCore::Config::GetConfigFileLocation(false),
FEXCore::Config::GetApplicationConfig(AppConfigName(), true),
FEXCore::Config::GetApplicationConfig(AppConfigName(), false),
ThunkConfigFile,
};
for (const auto &Path : ConfigPaths) {
std::vector<char> FileData;
if (LoadFile(FileData, ThunkConfigFile)) {
FileData.push_back(0);
if (LoadFile(FileData, Path)) {
JSON::JsonAllocator Pool {
.PoolObject = {
.init = JSON::PoolInit,
@@ -240,101 +306,22 @@ FileManager::FileManager(FEXCore::Context::Context *ctx)
};
json_t const *json = json_createWithPool(&FileData.at(0), &Pool.PoolObject);
json_t const* thunks = json_getProperty( json, "thunks" );
if (thunks && json_getType(thunks) == JSON_OBJ) {
json_t const* thunk;
for( thunk = json_getChild( thunks ); thunk != 0; thunk = json_getSibling( thunk )) {
char const* GuestThunk = json_getName( thunk );
jsonType_t propertyType = json_getType( thunk );
if (propertyType == JSON_TEXT) {
char const* RootFSLib = json_getValue( thunk );
auto ThunkPath = ThunkGuestPath / GuestThunk;
if (std::filesystem::exists(ThunkPath)) {
ThunkOverlays.emplace(RootFSLib, ThunkPath);
}
} else if (propertyType == JSON_ARRAY) {
json_t const* child;
for( child = json_getChild( thunk ); child != 0; child = json_getSibling( child ) ) {
if (json_getType( child ) == JSON_TEXT) {
char const* RootFSLib = json_getValue( child );
auto ThunkPath = ThunkGuestPath / GuestThunk;
if (std::filesystem::exists(ThunkPath)) {
ThunkOverlays.emplace(RootFSLib, ThunkPath);
}
}
}
}
}
}
json_t const* ThunksDB = json_getProperty( json, "ThunksDB" );
if (ThunksDB) {
// If a thunks DB property exists then we pull in data from the thunks database
// Load the initial thunks database
LoadThunkDatabase(true);
LoadThunkDatabase(false);
// Now load this property
for (json_t const* Item = json_getChild(ThunksDB); Item != nullptr; Item = json_getSibling(Item)) {
const char *LibraryName = json_getName(Item);
int64_t LibraryEnabled = json_getInteger(Item);
if (LibraryEnabled != 0) {
// If the library is enabled then find it in the DB
// Enable the overlay and all the dependencies in one go
auto DBObject = ThunkDB.find(LibraryName);
if (DBObject != ThunkDB.end() &&
DBObject->second.Enabled == false) {
auto ThunkPath = ThunkGuestPath / DBObject->second.LibraryName;
if (std::filesystem::exists(ThunkPath)) {
for (auto Overlay : DBObject->second.Overlays) {
// Direct full path in guest RootFS to our overlay file
ThunkOverlays.emplace(Overlay, ThunkPath);
}
}
DBObject->second.Enabled = true;
// Now walk the dependencies and set them up as well
// Make sure to enable each one as we go to remove circular dependencies
std::function<void(std::unordered_set<std::string> &Depends)> InsertDependencies
= [this, &ThunkGuestPath, &InsertDependencies](std::unordered_set<std::string> &Depends) -> void {
for (auto &Depend : Depends) {
auto DBDepend = ThunkDB.find(Depend);
if (DBDepend != ThunkDB.end() &&
DBDepend->second.Enabled == false) {
auto ThunkPath = ThunkGuestPath / DBDepend->second.LibraryName;
if (std::filesystem::exists(ThunkPath)) {
for (auto Overlay : DBDepend->second.Overlays) {
// Direct full path in guest RootFS to our overlay file
ThunkOverlays.emplace(Overlay, ThunkPath);
}
}
// Enabled, now walk this dependencies
DBDepend->second.Enabled = true;
InsertDependencies(DBDepend->second.Depends);
}
}
};
InsertDependencies(DBObject->second.Depends);
}
}
}
// Now clear the thunk database since we're loaded
ThunkDB.clear();
LoadThunksDB(&LoadedThunkDatabase, ThunksDB);
}
}
}
if (false) {
// Useful for debugging
if (ThunkOverlays.size()) {
LogMan::Msg::IFmt("Thunk Overlays:");
for (const auto& [Overlay, ThunkPath] : ThunkOverlays) {
LogMan::Msg::IFmt("\t{} -> {}", Overlay, ThunkPath);
}
// Now clear the thunk database since we're loaded
ThunkDB.clear();
if (false) {
// Useful for debugging
if (ThunkOverlays.size()) {
LogMan::Msg::IFmt("Thunk Overlays:");
for (const auto& [Overlay, ThunkPath] : ThunkOverlays) {
LogMan::Msg::IFmt("\t{} -> {}", Overlay, ThunkPath);
}
}
}
@@ -575,7 +562,7 @@ uint64_t FileManager::Readlinkat(int dirfd, const char *pathname, char *buf, siz
dirfd != AT_FDCWD) {
// Passed in a dirfd that isn't magic FDCWD
// We need to get the path from the fd now
Path = FEX::get_fdpath(dirfd);
Path = FEX::get_fdpath(dirfd).value_or("");
if (pathname) {
if (!Path.empty()) {
@@ -80,6 +80,7 @@ private:
FEX_CONFIG_OPT(ThunkHostLibs, THUNKHOSTLIBS);
FEX_CONFIG_OPT(ThunkGuestLibs, THUNKGUESTLIBS);
FEX_CONFIG_OPT(ThunkConfig, THUNKCONFIG);
FEX_CONFIG_OPT(AppConfigName, APP_CONFIG_NAME);
uint32_t CurrentPID{};
void LoadThunkDatabase(bool Global);
+17 -17
View File
@@ -44,11 +44,11 @@ public:
FindPageRangePtr = &MemAllocator32Bit::FindPageRange;
}
}
void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset) override;
int munmap(void *addr, size_t length) override;
void *mremap(void *old_address, size_t old_size, size_t new_size, int flags, void *new_address) override;
uint64_t shmat(int shmid, const void* shmaddr, int shmflg, uint32_t *ResultAddress) override;
uint64_t shmdt(const void* shmaddr) override;
void *Mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset) override;
int Munmap(void *addr, size_t length) override;
void *Mremap(void *old_address, size_t old_size, size_t new_size, int flags, void *new_address) override;
uint64_t Shmat(int shmid, const void* shmaddr, int shmflg, uint32_t *ResultAddress) override;
uint64_t Shmdt(const void* shmaddr) override;
static constexpr bool SearchDown = true;
// PageAddr is a page already shifted to page index
@@ -131,7 +131,7 @@ uint64_t MemAllocator32Bit::FindPageRange_TopDown(uint64_t Start, size_t Pages)
return 0;
}
void *MemAllocator32Bit::mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset) {
void *MemAllocator32Bit::Mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset) {
std::scoped_lock<std::mutex> lk{AllocMutex};
size_t PagesLength = FEXCore::AlignUp(length, FHU::FEX_PAGE_SIZE) >> FHU::FEX_PAGE_SHIFT;
@@ -282,7 +282,7 @@ restart:
return 0;
}
int MemAllocator32Bit::munmap(void *addr, size_t length) {
int MemAllocator32Bit::Munmap(void *addr, size_t length) {
std::scoped_lock<std::mutex> lk{AllocMutex};
size_t PagesLength = FEXCore::AlignUp(length, FHU::FEX_PAGE_SIZE) >> FHU::FEX_PAGE_SHIFT;
@@ -327,7 +327,7 @@ int MemAllocator32Bit::munmap(void *addr, size_t length) {
return 0;
}
void *MemAllocator32Bit::mremap(void *old_address, size_t old_size, size_t new_size, int flags, void *new_address) {
void *MemAllocator32Bit::Mremap(void *old_address, size_t old_size, size_t new_size, int flags, void *new_address) {
size_t OldPagesLength = FEXCore::AlignUp(old_size, FHU::FEX_PAGE_SIZE) >> FHU::FEX_PAGE_SHIFT;
size_t NewPagesLength = FEXCore::AlignUp(new_size, FHU::FEX_PAGE_SIZE) >> FHU::FEX_PAGE_SHIFT;
@@ -404,7 +404,7 @@ void *MemAllocator32Bit::mremap(void *old_address, size_t old_size, size_t new_s
// New Size is >= old size
// First, try and allocate a region the size of the new size
void *MappedPtr = this->mmap(nullptr, new_size, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
void *MappedPtr = this->Mmap(nullptr, new_size, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
std::scoped_lock<std::mutex> lk{AllocMutex};
if (FEX::HLE::HasSyscallError(MappedPtr)) {
// Couldn't find a region that fit our space
@@ -435,7 +435,7 @@ void *MemAllocator32Bit::mremap(void *old_address, size_t old_size, size_t new_s
return reinterpret_cast<void*>(-errno);
}
uint64_t MemAllocator32Bit::shmat(int shmid, const void* shmaddr, int shmflg, uint32_t *ResultAddress) {
uint64_t MemAllocator32Bit::Shmat(int shmid, const void* shmaddr, int shmflg, uint32_t *ResultAddress) {
std::scoped_lock<std::mutex> lk{AllocMutex};
if (shmaddr != nullptr) {
@@ -548,9 +548,9 @@ restart:
}
}
}
uint64_t MemAllocator32Bit::shmdt(const void* shmaddr) {
uint64_t MemAllocator32Bit::Shmdt(const void* shmaddr) {
std::scoped_lock<std::mutex> lk{AllocMutex};
uint32_t AddrPage = reinterpret_cast<uint64_t>(shmaddr) >> FHU::FEX_PAGE_SHIFT;
auto it = PageToShm.find(AddrPage);
@@ -567,7 +567,7 @@ uint64_t MemAllocator32Bit::shmdt(const void* shmaddr) {
class MemAllocatorPassThrough final : public FEX::HLE::MemAllocator {
public:
void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset) override {
void *Mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset) override {
uint64_t Result = (uint64_t)::mmap(addr, length, prot, flags, fd, offset);
if (Result == ~0ULL) {
return reinterpret_cast<void*>(-errno);
@@ -575,12 +575,12 @@ public:
return reinterpret_cast<void*>(Result);
}
int munmap(void *addr, size_t length) override {
int Munmap(void *addr, size_t length) override {
uint64_t Result = (uint64_t)::munmap(addr, length);
SYSCALL_ERRNO();
}
void *mremap(void *old_address, size_t old_size, size_t new_size, int flags, void *new_address) override {
void *Mremap(void *old_address, size_t old_size, size_t new_size, int flags, void *new_address) override {
uint64_t Result = (uint64_t)::mremap(old_address, old_size, new_size, flags, new_address);
if (Result == ~0ULL) {
return reinterpret_cast<void*>(-errno);
@@ -588,7 +588,7 @@ public:
return reinterpret_cast<void*>(Result);
}
uint64_t shmat(int shmid, const void* shmaddr, int shmflg, uint32_t *ResultAddress) override {
uint64_t Shmat(int shmid, const void* shmaddr, int shmflg, uint32_t *ResultAddress) override {
uint64_t Result = (uint64_t)::shmat(shmid, reinterpret_cast<const void*>(shmaddr), shmflg);
if (Result != ~0ULL) {
*ResultAddress = Result;
@@ -597,7 +597,7 @@ public:
SYSCALL_ERRNO();
}
uint64_t shmdt(const void* shmaddr) override {
uint64_t Shmdt(const void* shmaddr) override {
uint64_t Result = ::shmdt(shmaddr);
SYSCALL_ERRNO();
}
+5 -5
View File
@@ -9,11 +9,11 @@ namespace FEX::HLE {
class MemAllocator {
public:
virtual ~MemAllocator() = default;
virtual void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset) = 0;
virtual int munmap(void *addr, size_t length) = 0;
virtual void *mremap(void *old_address, size_t old_size, size_t new_size, int flags, void *new_address) = 0;
virtual uint64_t shmat(int shmid, const void* shmaddr, int shmflg, uint32_t *ResultAddress) = 0;
virtual uint64_t shmdt(const void* shmaddr) = 0;
virtual void *Mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset) = 0;
virtual int Munmap(void *addr, size_t length) = 0;
virtual void *Mremap(void *old_address, size_t old_size, size_t new_size, int flags, void *new_address) = 0;
virtual uint64_t Shmat(int shmid, const void* shmaddr, int shmflg, uint32_t *ResultAddress) = 0;
virtual uint64_t Shmdt(const void* shmaddr) = 0;
};
std::unique_ptr<FEX::HLE::MemAllocator> Create32BitAllocator();
+108
View File
@@ -14,6 +14,8 @@ $end_info$
#include "Tests/LinuxSyscalls/Syscalls/Thread.h"
#include "Tests/LinuxSyscalls/x32/Syscalls.h"
#include "Tests/LinuxSyscalls/x64/Syscalls.h"
#include "Tests/LinuxSyscalls/x32/Types.h"
#include "Tests/LinuxSyscalls/x64/Types.h"
#include <FEXCore/Config/Config.h>
#include <FEXCore/Core/Context.h>
@@ -58,6 +60,112 @@ namespace FEX::HLE {
class SignalDelegator;
SyscallHandler *_SyscallHandler{};
template<bool IncrementOffset, typename T>
uint64_t GetDentsEmulation(int fd, T *dirp, uint32_t count) {
std::vector<uint8_t> TmpVector(count);
void *TmpPtr = reinterpret_cast<void*>(&TmpVector.at(0));
uint64_t Offset = 0;
uint64_t TmpOffset = 0;
// Copy the incoming structures to our temporary array
while (Offset < count) {
T *Incoming = (T*)(reinterpret_cast<uint64_t>(dirp) + Offset);
FEX::HLE::x64::linux_dirent_64 *Tmp = (FEX::HLE::x64::linux_dirent_64*)(reinterpret_cast<uint64_t>(TmpPtr) + TmpOffset);
if (!Incoming->d_reclen ||
(Offset + Incoming->d_reclen) > count) {
break;
}
size_t NewRecLen = FEXCore::AlignUp(Incoming->d_reclen + (sizeof(std::remove_reference<decltype(*Tmp)>::type) - sizeof(*Incoming)),
alignof(decltype(Tmp->d_ino)));
Tmp->d_ino = Incoming->d_ino;
Tmp->d_off = Incoming->d_off;
Tmp->d_reclen = NewRecLen;
// d_type is hidden at the very end of reclen
Tmp->d_type = Incoming->d_name[Incoming->d_reclen - offsetof(T, d_name) - 1];
// This actually copies one more byte than the string of d_name
// Copies a null byte for the string
size_t CopySize = std::clamp<uint32_t>(Incoming->d_reclen - offsetof(T, d_name) - 1, 0U, count - Offset);
memcpy(Tmp->d_name, Incoming->d_name, CopySize);
// We take up 8 more bytes of space
TmpOffset += NewRecLen;
Offset += Incoming->d_reclen;
}
uint64_t Result = syscall(SYSCALL_DEF(getdents64),
static_cast<uint64_t>(fd),
TmpPtr,
static_cast<uint64_t>(count));
// Now copy back in to the array we were given
if (Result != -1) {
// If the outgoing d_ino is smaller than the incoming d_ino from the kernel
// Then we need to check for overflow before writing any of the data back
if (sizeof(decltype(FEX::HLE::x64::linux_dirent_64::d_ino)) > sizeof(decltype(T::d_ino))) {
uint64_t TmpOffset = 0;
while (TmpOffset < Result) {
FEX::HLE::x64::linux_dirent_64 *Tmp = (FEX::HLE::x64::linux_dirent_64*)(reinterpret_cast<uint64_t>(TmpPtr) + TmpOffset);
decltype(T::d_ino) Result_d_ino = Tmp->d_ino;
if (Result_d_ino != Tmp->d_ino) {
// The resulting d_ino truncated, return error
return -EOVERFLOW;
}
TmpOffset += Tmp->d_reclen;
}
}
uint64_t Offset = 0;
uint64_t TmpOffset = 0;
size_t OffsetIndex = 1;
// With how the emulation occurs we will always return a smaller buffer than what was given to us
while (TmpOffset < Result) {
T *Outgoing = (T*)(reinterpret_cast<uint64_t>(dirp) + Offset);
FEX::HLE::x64::linux_dirent_64 *Tmp = (FEX::HLE::x64::linux_dirent_64*)(reinterpret_cast<uint64_t>(TmpPtr) + TmpOffset);
if (!Tmp->d_reclen) {
break;
}
size_t NewRecLen = FEXCore::AlignUp(Tmp->d_reclen - (sizeof(std::remove_reference<decltype(*Tmp)>::type) - sizeof(*Outgoing)),
alignof(decltype(Tmp->d_ino)));
Outgoing->d_ino = Tmp->d_ino;
// 32-bit getdents can't safely handle d_off
// A safe way of emulating this is to just use an incrementing offset from 1
Outgoing->d_off = IncrementOffset ? OffsetIndex : Tmp->d_off;
size_t OffsetOfName = offsetof(std::remove_reference<decltype(*Tmp)>::type, d_name);
Outgoing->d_reclen = NewRecLen;
// Copies null character as well
size_t NameLength = Tmp->d_reclen - OffsetOfName - 1;
memcpy(Outgoing->d_name, Tmp->d_name, NameLength);
// Copy the hidden d_type flag
Outgoing->d_name[Outgoing->d_reclen - offsetof(T, d_name) - 1] = Tmp->d_type;
TmpOffset += Tmp->d_reclen;
// Outgoing is 5 bytes smaller
Offset += NewRecLen;
++OffsetIndex;
}
Result = Offset;
}
SYSCALL_ERRNO();
}
template
uint64_t GetDentsEmulation<false>(int, FEX::HLE::x64::linux_dirent*, uint32_t);
template
uint64_t GetDentsEmulation<true>(int, FEX::HLE::x32::linux_dirent_32*, uint32_t);
static bool IsSupportedByInterpreter(std::string const &Filename) {
// If it is a supported ELF then we can
if (ELFLoader::ELFContainer::IsSupportedELF(Filename.c_str())) {
+2
View File
@@ -509,6 +509,8 @@ static bool HasSyscallError(const void* Result) {
return HasSyscallError(reinterpret_cast<uintptr_t>(Result));
}
template<bool IncrementOffset, typename T>
uint64_t GetDentsEmulation(int fd, T *dirp, uint32_t count);
}
// Registers syscall for both 32bit and 64bit
+2 -2
View File
@@ -18,14 +18,14 @@ namespace FEX::HLE {
void RegisterSHM(FEX::HLE::SyscallHandler *Handler) {
using namespace FEXCore::IR;
REGISTER_SYSCALL_IMPL_PASS_FLAGS(shmget, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
REGISTER_SYSCALL_IMPL_PASS_FLAGS(_shmget, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
[](FEXCore::Core::CpuStateFrame *Frame, key_t key, size_t size, int shmflg) -> uint64_t {
uint64_t Result = shmget(key, size, shmflg);
SYSCALL_ERRNO();
});
// XXX: shmid_ds is definitely not correct for 32-bit
REGISTER_SYSCALL_IMPL_PASS_FLAGS(shmctl, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
REGISTER_SYSCALL_IMPL_PASS_FLAGS(_shmctl, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
[](FEXCore::Core::CpuStateFrame *Frame, int shmid, int cmd, struct shmid_ds *buf) -> uint64_t {
uint64_t Result = ::shmctl(shmid, cmd, buf);
SYSCALL_ERRNO();
@@ -199,16 +199,17 @@ void SyscallHandler::TrackMmap(uintptr_t Base, uintptr_t Size, int Prot, int Fla
fstat64(fd, &buf);
MRID mrid {buf.st_dev, buf.st_ino};
auto [Iter, Inserted] = VMATracking.MappedResources.emplace(mrid, MappedResource {nullptr, nullptr, 0});
Resource = &Iter->second;
auto filename = FEX::get_fdpath(fd);
if (Inserted) {
auto filename = FEX::get_fdpath(fd);
if (filename.has_value()) {
auto [Iter, Inserted] = VMATracking.MappedResources.emplace(mrid, MappedResource {nullptr, nullptr, 0});
Resource = &Iter->second;
Resource->AOTIRCacheEntry = FEXCore::Context::LoadAOTIRCacheEntry(CTX, filename);
Resource->Iterator = Iter;
if (Inserted) {
Resource->AOTIRCacheEntry = FEXCore::Context::LoadAOTIRCacheEntry(CTX, filename.value());
Resource->Iterator = Iter;
}
}
} else if (Flags & MAP_SHARED) {
MRID mrid{SpecialDev::Anon, AnonSharedId++};
+1 -69
View File
@@ -583,75 +583,7 @@ namespace FEX::HLE::x32 {
REGISTER_SYSCALL_IMPL_X32(ioctl, ioctl32);
REGISTER_SYSCALL_IMPL_X32(getdents, [](FEXCore::Core::CpuStateFrame *Frame, int fd, void *dirp, uint32_t count) -> uint64_t {
#ifdef SYS_getdents
std::vector<uint8_t> TmpVector(count);
void *TmpPtr = reinterpret_cast<void*>(&TmpVector.at(0));
// Copy the incoming structures to our temporary array
for (uint64_t Offset = 0, TmpOffset = 0;
Offset < count;) {
linux_dirent_32 *Incoming = (linux_dirent_32*)(reinterpret_cast<uint64_t>(dirp) + Offset);
linux_dirent *Tmp = (linux_dirent*)(reinterpret_cast<uint64_t>(TmpPtr) + TmpOffset);
if (!Incoming->d_reclen ||
(Offset + Incoming->d_reclen) > count) {
break;
}
size_t NewRecLen = Incoming->d_reclen + (sizeof(linux_dirent) - sizeof(linux_dirent_32));
Tmp->d_ino = Incoming->d_ino;
Tmp->d_off = Incoming->d_off;
Tmp->d_reclen = NewRecLen;
// This actually copies two more bytes than the string of d_name
// Copies a null byte for the string
// Copies a d_type flag that lives after the name
size_t CopySize = std::clamp<uint32_t>(Incoming->d_reclen - offsetof(linux_dirent_32, d_name), 0U, count - Offset);
memcpy(Tmp->d_name, Incoming->d_name, CopySize);
// We take up 8 more bytes of space
TmpOffset += NewRecLen;
Offset += Incoming->d_reclen;
}
uint64_t Result = syscall(SYSCALL_DEF(getdents),
static_cast<uint64_t>(fd),
TmpPtr,
static_cast<uint64_t>(count));
// Now copy back in to the array we were given
if (Result != -1) {
uint64_t Offset = 0;
// With how the emulation occurs we will always return a smaller buffer than what was given to us
for (uint64_t TmpOffset = 0, num = 0; TmpOffset < Result; ++num) {
linux_dirent_32 *Outgoing = (linux_dirent_32*)(reinterpret_cast<uint64_t>(dirp) + Offset);
linux_dirent *Tmp = (linux_dirent*)(reinterpret_cast<uint64_t>(TmpPtr) + TmpOffset);
if (!Tmp->d_reclen) {
break;
}
size_t NewRecLen = Tmp->d_reclen - (sizeof(std::remove_reference<decltype(*Tmp)>::type) - sizeof(*Outgoing));
Outgoing->d_ino = Tmp->d_ino;
// If we pass d_off directly then we seem to encounter issues?
Outgoing->d_off = num; //Tmp->d_off;
size_t OffsetOfName = offsetof(std::remove_reference<decltype(*Tmp)>::type, d_name);
Outgoing->d_reclen = NewRecLen;
// Copies null character and d_type flag as well
memcpy(Outgoing->d_name, Tmp->d_name, Tmp->d_reclen - OffsetOfName);
TmpOffset += Tmp->d_reclen;
// Outgoing is 8 bytes smaller
Offset += NewRecLen;
}
Result = Offset;
}
SYSCALL_ERRNO();
#else
// XXX: Emulate
return -ENOSYS;
#endif
return GetDentsEmulation<true>(fd, reinterpret_cast<FEX::HLE::x32::linux_dirent_32*>(dirp), count);
});
REGISTER_SYSCALL_IMPL_X32(getdents64, [](FEXCore::Core::CpuStateFrame *Frame, int fd, void *dirp, uint32_t count) -> uint64_t {
+8 -8
View File
@@ -23,7 +23,7 @@ namespace FEX::HLE::x32 {
void *x32SyscallHandler::GuestMmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset) {
LOGMAN_THROW_AA_FMT((length >> 32) == 0, "values must fit to 32 bits");
auto Result = (uint64_t)GetAllocator()->mmap((void*)addr, length, prot, flags, fd, offset);
auto Result = (uint64_t)GetAllocator()->Mmap((void*)addr, length, prot, flags, fd, offset);
LOGMAN_THROW_AA_FMT((Result >> 32) == 0|| (Result >> 32) == 0xFFFFFFFF, "values must fit to 32 bits");
@@ -40,7 +40,7 @@ namespace FEX::HLE::x32 {
LOGMAN_THROW_AA_FMT((uintptr_t(addr) >> 32) == 0, "values must fit to 32 bits");
LOGMAN_THROW_AA_FMT((length >> 32) == 0, "values must fit to 32 bits");
auto Result = GetAllocator()->munmap(addr, length);
auto Result = GetAllocator()->Munmap(addr, length);
if (Result == 0) {
FEX::HLE::_SyscallHandler->TrackMunmap((uintptr_t)addr, length);
@@ -92,7 +92,7 @@ namespace FEX::HLE::x32 {
REGISTER_SYSCALL_IMPL_X32(mremap, [](FEXCore::Core::CpuStateFrame *Frame, void *old_address, size_t old_size, size_t new_size, int flags, void *new_address) -> uint64_t {
uint64_t Result = reinterpret_cast<uint64_t>(static_cast<FEX::HLE::x32::x32SyscallHandler*>(FEX::HLE::_SyscallHandler)->GetAllocator()->
mremap(old_address, old_size, new_size, flags, new_address));
Mremap(old_address, old_size, new_size, flags, new_address));
if (!FEX::HLE::HasSyscallError(Result)) {
FEX::HLE::_SyscallHandler->TrackMremap((uintptr_t)old_address, old_size, new_size, flags, Result);
@@ -111,11 +111,11 @@ namespace FEX::HLE::x32 {
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_X32(shmat, [](FEXCore::Core::CpuStateFrame *Frame, int shmid, const void *shmaddr, int shmflg) -> uint64_t {
REGISTER_SYSCALL_IMPL_X32(_shmat, [](FEXCore::Core::CpuStateFrame *Frame, int shmid, const void *shmaddr, int shmflg) -> uint64_t {
// also implemented in ipc:OP_SHMAT
uint32_t ResultAddr{};
uint64_t Result = static_cast<FEX::HLE::x32::x32SyscallHandler*>(FEX::HLE::_SyscallHandler)->GetAllocator()->
shmat(shmid, reinterpret_cast<const void*>(shmaddr), shmflg, &ResultAddr);
Shmat(shmid, reinterpret_cast<const void*>(shmaddr), shmflg, &ResultAddr);
if (!FEX::HLE::HasSyscallError(Result)) {
FEX::HLE::_SyscallHandler->TrackShmat(shmid, ResultAddr, shmflg);
@@ -126,11 +126,11 @@ namespace FEX::HLE::x32 {
}
});
REGISTER_SYSCALL_IMPL_X32(shmdt, [](FEXCore::Core::CpuStateFrame *Frame, const void *shmaddr) -> uint64_t {
REGISTER_SYSCALL_IMPL_X32(_shmdt, [](FEXCore::Core::CpuStateFrame *Frame, const void *shmaddr) -> uint64_t {
// also implemented in ipc:OP_SHMDT
uint64_t Result = static_cast<FEX::HLE::x32::x32SyscallHandler*>(FEX::HLE::_SyscallHandler)->GetAllocator()->
shmdt(shmaddr);
Shmdt(shmaddr);
if (!FEX::HLE::HasSyscallError(Result)) {
FEX::HLE::_SyscallHandler->TrackShmdt((uintptr_t)shmaddr);
}
+9 -9
View File
@@ -281,7 +281,7 @@ namespace FEX::HLE::x32 {
case OP_SHMAT: {
// also implemented in memory:shmat
Result = static_cast<FEX::HLE::x32::x32SyscallHandler*>(FEX::HLE::_SyscallHandler)->GetAllocator()->
shmat(first, reinterpret_cast<const void*>(ptr), second, reinterpret_cast<uint32_t*>(third));
Shmat(first, reinterpret_cast<const void*>(ptr), second, reinterpret_cast<uint32_t*>(third));
if (!FEX::HLE::HasSyscallError(Result)) {
FEX::HLE::_SyscallHandler->TrackShmat(first, *reinterpret_cast<uint32_t*>(third), second);
}
@@ -290,7 +290,7 @@ namespace FEX::HLE::x32 {
case OP_SHMDT: {
// also implemented in memory:shmdt
Result = static_cast<FEX::HLE::x32::x32SyscallHandler*>(FEX::HLE::_SyscallHandler)->GetAllocator()->
shmdt(reinterpret_cast<void*>(ptr));
Shmdt(reinterpret_cast<void*>(ptr));
if (!FEX::HLE::HasSyscallError(Result)) {
FEX::HLE::_SyscallHandler->TrackShmdt(ptr);
}
@@ -317,7 +317,7 @@ namespace FEX::HLE::x32 {
else {
buf = *shmun.buf32;
}
Result = ::syscall(SYSCALL_DEF(shmctl), shmid, cmd, &buf);
Result = ::syscall(SYSCALL_DEF(_shmctl), shmid, cmd, &buf);
// IPC_SET sets the internal data structure that the kernel uses
// No need to writeback
break;
@@ -326,7 +326,7 @@ namespace FEX::HLE::x32 {
case SHM_STAT_ANY:
case IPC_STAT: {
struct shmid64_ds buf{};
Result = ::syscall(SYSCALL_DEF(shmctl), shmid, cmd, &buf);
Result = ::syscall(SYSCALL_DEF(_shmctl), shmid, cmd, &buf);
if (Result != -1) {
if (IPC64) {
*shmun.buf64 = buf;
@@ -339,7 +339,7 @@ namespace FEX::HLE::x32 {
}
case IPC_INFO: {
struct shminfo si{};
Result = ::syscall(SYSCALL_DEF(shmctl), shmid, cmd, reinterpret_cast<struct shmid_ds*>(&si));
Result = ::syscall(SYSCALL_DEF(_shmctl), shmid, cmd, reinterpret_cast<struct shmid_ds*>(&si));
if (Result != -1) {
if (IPC64) {
*shmun.__buf64 = si;
@@ -352,7 +352,7 @@ namespace FEX::HLE::x32 {
}
case SHM_INFO: {
struct shm_info si{};
Result = ::syscall(SYSCALL_DEF(shmctl), shmid, cmd, reinterpret_cast<struct shmid_ds*>(&si));
Result = ::syscall(SYSCALL_DEF(_shmctl), shmid, cmd, reinterpret_cast<struct shmid_ds*>(&si));
if (Result != -1) {
// SHM_INFO doesn't follow IPC64 behaviour
*shmun.__buf_info_32 = si;
@@ -360,13 +360,13 @@ namespace FEX::HLE::x32 {
break;
}
case SHM_LOCK:
Result = ::syscall(SYSCALL_DEF(shmctl), shmid, cmd, nullptr);
Result = ::syscall(SYSCALL_DEF(_shmctl), shmid, cmd, nullptr);
break;
case SHM_UNLOCK:
Result = ::syscall(SYSCALL_DEF(shmctl), shmid, cmd, nullptr);
Result = ::syscall(SYSCALL_DEF(_shmctl), shmid, cmd, nullptr);
break;
case IPC_RMID:
Result = ::syscall(SYSCALL_DEF(shmctl), shmid, cmd, nullptr);
Result = ::syscall(SYSCALL_DEF(_shmctl), shmid, cmd, nullptr);
break;
default:
@@ -410,10 +410,10 @@ enum Syscalls_x86 {
SYSCALL_x86_rseq = 386,
SYSCALL_x86_semget = 393,
SYSCALL_x86_semctl = 394,
SYSCALL_x86_shmget = 395,
SYSCALL_x86_shmctl = 396,
SYSCALL_x86_shmat = 397,
SYSCALL_x86_shmdt = 398,
SYSCALL_x86__shmget = 395,
SYSCALL_x86__shmctl = 396,
SYSCALL_x86__shmat = 397,
SYSCALL_x86__shmdt = 398,
SYSCALL_x86_msgget = 399,
SYSCALL_x86_msgsnd = 400,
SYSCALL_x86_msgrcv = 401,
+2 -10
View File
@@ -10,6 +10,7 @@ $end_info$
#include "Tests/LinuxSyscalls/x64/Types.h"
#include <FEXCore/Utils/CompilerDefs.h>
#include <FEXCore/Utils/MathUtils.h>
#include <fcntl.h>
#include <poll.h>
@@ -196,16 +197,7 @@ namespace FEX::HLE::x64 {
});
REGISTER_SYSCALL_IMPL_X64(getdents, [](FEXCore::Core::CpuStateFrame *Frame, int fd, void *dirp, uint32_t count) -> uint64_t {
#ifdef SYS_getdents
uint64_t Result = syscall(SYSCALL_DEF(getdents),
static_cast<uint64_t>(fd),
reinterpret_cast<uint64_t>(dirp),
static_cast<uint64_t>(count));
SYSCALL_ERRNO();
#else
// XXX: Emulate
return -EFAULT;
#endif
return GetDentsEmulation<false>(fd, reinterpret_cast<FEX::HLE::x64::linux_dirent*>(dirp), count);
});
REGISTER_SYSCALL_IMPL_X64_PASS(getdents64, [](FEXCore::Core::CpuStateFrame *Frame, int fd, void *dirp, uint32_t count) -> uint64_t {
+4 -4
View File
@@ -30,7 +30,7 @@ namespace FEX::HLE::x64 {
bool Map32Bit = flags & FEX::HLE::X86_64_MAP_32BIT;
if (Map32Bit) {
Result = (uint64_t)Get32BitAllocator()->mmap(addr, length, prot,flags, fd, offset);
Result = (uint64_t)Get32BitAllocator()->Mmap(addr, length, prot,flags, fd, offset);
if (FEX::HLE::HasSyscallError(Result)) {
errno = -Result;
Result = -1;
@@ -49,7 +49,7 @@ namespace FEX::HLE::x64 {
int x64SyscallHandler::GuestMunmap(void *addr, uint64_t length) {
uint64_t Result{};
if (reinterpret_cast<uintptr_t>(addr) < 0x1'0000'0000ULL) {
Result = Get32BitAllocator()->munmap(addr, length);
Result = Get32BitAllocator()->Munmap(addr, length);
if (FEX::HLE::HasSyscallError(Result)) {
errno = -Result;
@@ -117,7 +117,7 @@ namespace FEX::HLE::x64 {
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_X64_FLAGS(shmat, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
REGISTER_SYSCALL_IMPL_X64_FLAGS(_shmat, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
[](FEXCore::Core::CpuStateFrame *Frame, int shmid, const void *shmaddr, int shmflg) -> uint64_t {
uint64_t Result = reinterpret_cast<uint64_t>(shmat(shmid, shmaddr, shmflg));
@@ -127,7 +127,7 @@ namespace FEX::HLE::x64 {
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_X64_FLAGS(shmdt, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
REGISTER_SYSCALL_IMPL_X64_FLAGS(_shmdt, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
[](FEXCore::Core::CpuStateFrame *Frame, const void *shmaddr) -> uint64_t {
uint64_t Result = ::shmdt(shmaddr);
@@ -36,9 +36,9 @@ enum Syscalls_x64 {
SYSCALL_x64_msync = 26,
SYSCALL_x64_mincore = 27,
SYSCALL_x64_madvise = 28,
SYSCALL_x64_shmget = 29,
SYSCALL_x64_shmat = 30,
SYSCALL_x64_shmctl = 31,
SYSCALL_x64__shmget = 29,
SYSCALL_x64__shmat = 30,
SYSCALL_x64__shmctl = 31,
SYSCALL_x64_dup = 32,
SYSCALL_x64_dup2 = 33,
SYSCALL_x64_pause = 34,
@@ -74,7 +74,7 @@ enum Syscalls_x64 {
SYSCALL_x64_semget = 64,
SYSCALL_x64_semop = 65,
SYSCALL_x64_semctl = 66,
SYSCALL_x64_shmdt = 67,
SYSCALL_x64__shmdt = 67,
SYSCALL_x64_msgget = 68,
SYSCALL_x64_msgsnd = 69,
SYSCALL_x64_msgrcv = 70,
+33
View File
@@ -194,4 +194,37 @@ using __time_t = time_t;
// Original definition in `arch/x86/include/uapi/asm/stat.h` for future excavation
static_assert(std::is_trivial<FEX::HLE::x64::guest_stat>::value, "Needs to be trivial");
static_assert(sizeof(FEX::HLE::x64::guest_stat) == 144, "Incorrect size");
// There is no public definition of this struct
// Matches the definition of `struct linux_dirent` in fs/readdir.c
struct
FEX_ANNOTATE("fex-match")
linux_dirent {
uint64_t d_ino;
uint64_t d_off;
uint16_t d_reclen;
char d_name[1];
/* Has hidden null character and d_type */
};
static_assert(std::is_trivial<linux_dirent>::value, "Needs to be trivial");
static_assert(offsetof(linux_dirent, d_ino) == 0, "Incorrect offset");
static_assert(offsetof(linux_dirent, d_off) == 8, "Incorrect offset");
static_assert(offsetof(linux_dirent, d_reclen) == 16, "Incorrect offset");
static_assert(offsetof(linux_dirent, d_name) == 18, "Incorrect offset");
static_assert(sizeof(linux_dirent) == 24, "Incorrect size");
// There is no public definition of this struct
// Matches the definition of `struct linux_dirent64` in include/linux/dirent.h
struct
FEX_ANNOTATE("fex-match")
FEX_PACKED
linux_dirent_64 {
uint64_t d_ino;
uint64_t d_off;
uint16_t d_reclen;
uint8_t d_type;
char d_name[];
};
static_assert(std::is_trivial<linux_dirent_64>::value, "Needs to be trivial");
static_assert(sizeof(linux_dirent_64) == 19, "Incorrect size");
}
+32 -12
View File
@@ -139,7 +139,7 @@ int main(int argc, char **argv, char **const envp) {
FEX_CONFIG_OPT(Core, CORE);
std::unique_ptr<FEX::HLE::MemAllocator> Allocator;
if (!Loader.Is64BitMode()) {
// Setup our userspace allocator
uint32_t KernelVersion = FEX::HLE::SyscallHandler::CalculateHostKernelVersion();
@@ -159,6 +159,32 @@ int main(int argc, char **argv, char **const envp) {
bool DidFault = false;
bool SupportsAVX = false;
FEXCore::Core::CPUState State;
FEXCore::Context::InitializeStaticTables(Loader.Is64BitMode() ? FEXCore::Context::MODE_64BIT : FEXCore::Context::MODE_32BIT);
auto CTX = FEXCore::Context::CreateNewContext();
FEXCore::Context::InitializeContext(CTX);
// Skip any tests that the host doesn't support features for
auto HostFeatures = FEXCore::Context::GetHostFeatures(CTX);
SupportsAVX = HostFeatures.SupportsAVX;
bool TestUnsupported =
(!HostFeatures.Supports3DNow && Loader.Requires3DNow()) ||
(!HostFeatures.SupportsSSE4A && Loader.RequiresSSE4A()) ||
(!SupportsAVX && Loader.RequiresAVX()) ||
(!HostFeatures.SupportsRAND && Loader.RequiresRAND()) ||
(!HostFeatures.SupportsSHA && Loader.RequiresSHA()) ||
(!HostFeatures.SupportsCLZERO && Loader.RequiresCLZERO()) ||
(!HostFeatures.SupportsBMI1 && Loader.RequiresBMI1()) ||
(!HostFeatures.SupportsBMI2 && Loader.RequiresBMI2());
if (TestUnsupported) {
FEXCore::Context::DestroyContext(CTX);
return 0;
}
if (Core != FEXCore::Config::CONFIG_CUSTOM) {
jmp_buf LongJump{};
int LongJumpVal{};
@@ -175,12 +201,6 @@ int main(int argc, char **argv, char **const envp) {
}, true);
// Run through FEX
FEXCore::Context::InitializeStaticTables(Loader.Is64BitMode() ? FEXCore::Context::MODE_64BIT : FEXCore::Context::MODE_32BIT);
auto CTX = FEXCore::Context::CreateNewContext();
FEXCore::Context::InitializeContext(CTX);
auto SyscallHandler = Loader.Is64BitMode() ? FEX::HLE::x64::CreateHandler(CTX, SignalDelegation.get())
: FEX::HLE::x32::CreateHandler(CTX, SignalDelegation.get(), std::move(Allocator));
@@ -195,14 +215,13 @@ int main(int argc, char **argv, char **const envp) {
FEXCore::Context::SetSignalDelegator(CTX, SignalDelegation.get());
FEXCore::Context::SetSyscallHandler(CTX, SyscallHandler.get());
bool Result1 = FEXCore::Context::InitCore(CTX, Loader.DefaultRIP(), Loader.GetStackPointer());
if (!Result1) {
return 1;
}
SupportsAVX = FEXCore::Context::GetHostFeatures(CTX).SupportsAVX;
LongJumpVal = setjmp(LongJump);
if (!LongJumpVal) {
FEXCore::Context::RunUntilExit(CTX);
@@ -212,8 +231,6 @@ int main(int argc, char **argv, char **const envp) {
FEXCore::Context::GetCPUState(CTX, &State);
SyscallHandler.reset();
FEXCore::Context::DestroyContext(CTX);
FEXCore::Context::ShutdownStaticTables();
} else {
// Run as host
SupportsAVX = true;
@@ -227,12 +244,15 @@ int main(int argc, char **argv, char **const envp) {
RunAsHost(SignalDelegation, Loader.DefaultRIP(), Loader.GetStackPointer(), &State);
}
FEXCore::Context::DestroyContext(CTX);
FEXCore::Context::ShutdownStaticTables();
bool Passed = !DidFault && Loader.CompareStates(&State, nullptr, SupportsAVX);
LogMan::Msg::IFmt("Faulted? {}", DidFault ? "Yes" : "No");
LogMan::Msg::IFmt("Passed? {}", Passed ? "Yes" : "No");
SignalDelegation.reset();
FEXCore::Config::Shutdown();
+10 -2
View File
@@ -3,6 +3,7 @@
#include <FEXCore/Core/CPUBackend.h>
#include <FEXCore/Core/Context.h>
#include <FEXCore/Core/CoreState.h>
#include <FEXCore/Core/UContext.h>
#include <FEXCore/Core/X86Enums.h>
#include <FEXCore/Debug/InternalThreadState.h>
#include <FEXCore/Utils/LogManager.h>
@@ -152,9 +153,16 @@ public:
for (size_t i = 0; i < FEXCore::Core::CPUState::NUM_XMMS; ++i) {
memcpy(&OutState->xmm.avx.data[i], &_mcontext->fpregs->_xmm[i], sizeof(_mcontext->fpregs->_xmm[0]));
}
const auto* xstate = reinterpret_cast<FEXCore::x86_64::xstate*>(_mcontext->fpregs);
const auto* reserved = &xstate->fpstate.sw_reserved;
if (reserved->HasExtendedContext() && reserved->HasYMMH()) {
for (size_t i = 0; i < FEXCore::Core::CPUState::NUM_XMMS; i++) {
memcpy(&OutState->xmm.avx.data[i][2], &xstate->ymmh.ymmh_space[i],
sizeof(xstate->ymmh.ymmh_space[0]));
}
}
uint16_t CurrentOffset = (_mcontext->fpregs->swd >> 11) & 7;
const uint16_t CurrentOffset = (_mcontext->fpregs->swd >> 11) & 7;
for (size_t i = 0; i < FEXCore::Core::CPUState::NUM_MMS; ++i) {
memcpy(&OutState->mm[(i + CurrentOffset) % 8], &_mcontext->fpregs->_st[i], sizeof(_mcontext->fpregs->_st[0]));
}
+177
View File
@@ -0,0 +1,177 @@
#include "VDSO_Emulation.h"
#include "FEXCore/IR/IR.h"
#include <FEXCore/Config/Config.h>
#include <FEXCore/Utils/MathUtils.h>
#include <FEXCore/Utils/LogManager.h>
#include <dlfcn.h>
#include <fcntl.h>
#include <filesystem>
#include <sys/mman.h>
#include <sys/time.h>
#include <unistd.h>
namespace FEX::VDSO {
using TimeType = decltype(::time)*;
using GetTimeOfDayType = decltype(::gettimeofday)*;
using ClockGetTimeType = decltype(::clock_gettime)*;
using ClockGetResType = decltype(::clock_getres)*;
using GetCPUType = decltype(::getcpu)*;
TimeType TimePtr = ::time;
GetTimeOfDayType GetTimeOfDayPtr = ::gettimeofday;
ClockGetTimeType ClockGetTimePtr = ::clock_gettime;
ClockGetResType ClockGetResPtr = ::clock_getres;
GetCPUType GetCPUPtr = ::getcpu;
static void time(void* ArgsRV) {
struct ArgsRV_t {
time_t *a_0;
uint64_t rv;
} *args = reinterpret_cast<ArgsRV_t*>(ArgsRV);
args->rv = TimePtr(args->a_0);
}
static void gettimeofday(void* ArgsRV) {
struct ArgsRV_t {
struct timeval *tv;
struct timezone *tz;
uint64_t rv;
} *args = reinterpret_cast<ArgsRV_t*>(ArgsRV);
args->rv = GetTimeOfDayPtr(args->tv, args->tz);
}
static void clock_gettime(void* ArgsRV) {
struct ArgsRV_t {
clockid_t clk_id;
struct timespec *tp;
uint64_t rv;
} *args = reinterpret_cast<ArgsRV_t*>(ArgsRV);
args->rv = ClockGetTimePtr(args->clk_id, args->tp);
}
static void clock_getres(void* ArgsRV) {
struct ArgsRV_t {
clockid_t clk_id;
struct timespec *tp;
uint64_t rv;
} *args = reinterpret_cast<ArgsRV_t*>(ArgsRV);
args->rv = ClockGetResPtr(args->clk_id, args->tp);
}
static void getcpu(void* ArgsRV) {
struct ArgsRV_t {
uint32_t *cpu;
uint32_t *node;
uint64_t rv;
} *args = reinterpret_cast<ArgsRV_t*>(ArgsRV);
args->rv = GetCPUPtr(args->cpu, args->node);
}
void LoadHostVDSO() {
void *vdso = dlopen("linux-vdso.so.1", RTLD_LAZY | RTLD_LOCAL | RTLD_NOLOAD);
if (!vdso) {
vdso = dlopen("linux-gate.so.1", RTLD_LAZY | RTLD_LOCAL | RTLD_NOLOAD);
}
if (!vdso) {
// We couldn't load VDSO, fallback to C implementations. Which will still be faster than emulated libc versions.
LogMan::Msg::IFmt("linux-vdso implementation falling back to libc. Consider enabling VDSO in your kernel.");
return;
}
auto SymbolPtr = dlsym(vdso, "__vdso_time");
if (SymbolPtr) {
TimePtr = reinterpret_cast<TimeType>(SymbolPtr);
}
SymbolPtr = dlsym(vdso, "__vdso_gettimeofday");
if (SymbolPtr) {
GetTimeOfDayPtr = reinterpret_cast<GetTimeOfDayType>(SymbolPtr);
}
SymbolPtr = dlsym(vdso, "__vdso_clock_gettime");
if (SymbolPtr) {
ClockGetTimePtr = reinterpret_cast<ClockGetTimeType>(SymbolPtr);
}
SymbolPtr = dlsym(vdso, "__vdso_clock_getres");
if (SymbolPtr) {
ClockGetResPtr = reinterpret_cast<ClockGetResType>(SymbolPtr);
}
SymbolPtr = dlsym(vdso, "__vdso_getcpu");
if (SymbolPtr) {
GetCPUPtr = reinterpret_cast<GetCPUType>(SymbolPtr);
}
dlclose(vdso);
}
static std::vector<FEXCore::IR::ThunkDefinition> VDSODefinitions = {
{
// sha256(libVDSO:time)
{ 0x37, 0x63, 0x46, 0xb0, 0x79, 0x06, 0x5f, 0x9d, 0x00, 0xb6, 0x8d, 0xfd, 0x9e, 0x4a, 0x62, 0xcd, 0x1e, 0x6c, 0xcc, 0x22, 0xcd, 0xb2, 0xc0, 0x17, 0x7d, 0x42, 0x6a, 0x40, 0xd1, 0xeb, 0xfa, 0xe0 },
&FEX::VDSO::time
},
{
// sha256(libVDSO:gettimeofday)
{ 0x77, 0x2a, 0xde, 0x1c, 0x13, 0x2d, 0xe9, 0x48, 0xaf, 0xe0, 0xba, 0xcc, 0x6a, 0x89, 0xff, 0xca, 0x4a, 0xdc, 0xd5, 0x63, 0x2c, 0xc5, 0x62, 0x8b, 0x5d, 0xde, 0x0b, 0x15, 0x35, 0xc6, 0xc7, 0x14 },
&FEX::VDSO::gettimeofday
},
{
// sha256(libVDSO:clock_gettime)
{ 0x3c, 0x96, 0x9b, 0x2d, 0xc3, 0xad, 0x2b, 0x3b, 0x9c, 0x4e, 0x4d, 0xca, 0x1c, 0xe8, 0x18, 0x4a, 0x12, 0x8a, 0xe4, 0xc1, 0x56, 0x92, 0x73, 0xce, 0x65, 0x85, 0x5f, 0x65, 0x7e, 0x94, 0x26, 0xbe },
&FEX::VDSO::clock_gettime
},
{
// sha256(libVDSO:clock_getres)
{ 0xe4, 0xa1, 0xf6, 0x23, 0x35, 0xae, 0xb7, 0xb6, 0xb0, 0x37, 0xc5, 0xc3, 0xa3, 0xfd, 0xbf, 0xa2, 0xa1, 0xc8, 0x95, 0x78, 0xe5, 0x76, 0x86, 0xdb, 0x3e, 0x6c, 0x54, 0xd5, 0x02, 0x60, 0xd8, 0x6d },
&FEX::VDSO::clock_getres
},
{
// sha256(libVDSO:getcpu)
{ 0x39, 0x83, 0x39, 0x36, 0x0f, 0x68, 0xd6, 0xfc, 0xc2, 0x3a, 0x97, 0x11, 0x85, 0x09, 0xc7, 0x25, 0xbb, 0x50, 0x49, 0x55, 0x6b, 0x0c, 0x9f, 0x50, 0x37, 0xf5, 0x9d, 0xb0, 0x38, 0x58, 0x57, 0x12 },
&FEX::VDSO::getcpu
},
};
void* LoadVDSOThunks(MapperFn Mapper) {
void* VDSOBase{};
FEX_CONFIG_OPT(ThunkGuestLibs, THUNKGUESTLIBS);
// Load VDSO if we can
auto ThunkGuestPath = std::filesystem::path(ThunkGuestLibs()) / "libVDSO-guest.so";
int VDSOFD = ::open(ThunkGuestPath.string().c_str(), O_RDONLY);
if (VDSOFD != -1) {
// Get file size
size_t VDSOSize = lseek(VDSOFD, 0, SEEK_END);
if (VDSOSize >= 4) {
// Reset to beginning
lseek(VDSOFD, 0, SEEK_SET);
VDSOSize = FEXCore::AlignUp(VDSOSize, 4096);
// Map the VDSO file to memory
VDSOBase = Mapper(nullptr, VDSOSize, PROT_READ, MAP_PRIVATE, VDSOFD, 0);
// Since we found our VDSO thunk library, find our host VDSO function implementations.
LoadHostVDSO();
}
close(VDSOFD);
}
return VDSOBase;
}
std::vector<FEXCore::IR::ThunkDefinition> const& GetVDSOThunkDefinitions() {
return VDSODefinitions;
}
}
+9
View File
@@ -0,0 +1,9 @@
#pragma once
#include <FEXCore/IR/IR.h>
namespace FEX::VDSO {
using MapperFn = std::function<void *(void *addr, size_t length, int prot, int flags, int fd, off_t offset)>;
void* LoadVDSOThunks(MapperFn Mapper);
std::vector<FEXCore::IR::ThunkDefinition> const& GetVDSOThunkDefinitions();
}
+15 -3
View File
@@ -8,6 +8,7 @@
#include <fstream>
#include <iostream>
#include <unistd.h>
#include <optional>
#include <sstream>
#include <sys/mman.h>
#include <sys/syscall.h>
@@ -441,7 +442,7 @@ namespace WebFileFetcher {
FileType Type;
};
const static std::string DownloadURL = "https://rootfs.fex-emu.org/file/fex-rootfs/RootFS_links.json";
const static std::string DownloadURL = "https://rootfs.fex-emu.com/file/fex-rootfs/RootFS_links.json";
std::string DownloadToString(const std::string &URL) {
std::string BigArgs =
@@ -514,10 +515,14 @@ namespace WebFileFetcher {
return &*alloc->json_objects->emplace(alloc->json_objects->end());
}
std::vector<FileTargets> GetRootFSLinks() {
std::optional<std::vector<FileTargets>> GetRootFSLinks() {
// Decode the filetargets
std::string Data = DownloadToString(DownloadURL);
if (Data.empty()) {
return std::nullopt;
}
JsonAllocator Pool {
.PoolObject = {
.init = PoolInit,
@@ -1121,7 +1126,14 @@ int main(int argc, char **argv, char **const envp) {
}
if (AskForConfirmation(Question)) {
auto Targets = WebFileFetcher::GetRootFSLinks();
auto TargetReturn = WebFileFetcher::GetRootFSLinks();
if (!TargetReturn.has_value()) {
ExecWithInfo("Couldn't download rootfs list from the server. Try again in a minute or report on the fex-emu issue tracker.");
return -1;
}
auto Targets = TargetReturn.value();
if (Targets.empty()) {
ExecWithInfo("Couldn't parse rootfs definition URL.");
return -1;
+92 -100
View File
@@ -471,9 +471,11 @@ void GenerateThunkLibsAction::EndSourceFileAction() {
return std::string { function_name } + "CBFN" + std::to_string(param_index);
};
if (!output_filenames.thunks.empty()) {
std::ofstream file(output_filenames.thunks);
// Files used guest-side
if (!output_filenames.guest.empty()) {
std::ofstream file(output_filenames.guest);
// Guest->Host transition points for API functions
file << "extern \"C\" {\n";
for (auto& thunk : thunks) {
const auto& function_name = thunk.function_name;
@@ -481,13 +483,14 @@ void GenerateThunkLibsAction::EndSourceFileAction() {
file << "MAKE_THUNK(" << libname << ", " << function_name << ", \"";
bool first = true;
for (auto c : sha256) {
file << (first ? "" : ", ") << "0x" << std::hex << std::setw(2) << std::setfill('0') << +c;
file << (first ? "" : ", ") << "0x" << std::hex << std::setw(2) << std::setfill('0') << +c << std::dec;
first = false;
}
file << "\")\n";
}
file << "}\n";
// Guest->Host transition points for invoking runtime host-function pointers based on their signature
for (auto type_it = funcptr_types.begin(); type_it != funcptr_types.end(); ++type_it) {
auto* type = *type_it;
std::string funcptr_signature = clang::QualType { type, 0 }.getAsString();
@@ -506,50 +509,8 @@ void GenerateThunkLibsAction::EndSourceFileAction() {
auto funcptr_idx = std::distance(funcptr_types.begin(), type_it);
file << " MAKE_CALLBACK_THUNK(callback_" << funcptr_idx << ", " << funcptr_signature << ", \"" << cb_sha256_str << "\");\n";
}
}
if (!output_filenames.function_packs_public.empty()) {
std::ofstream file(output_filenames.function_packs_public);
file << "extern \"C\" {\n";
for (auto& data : thunked_api) {
if (data.custom_guest_impl) {
continue;
}
const auto& function_name = data.function_name;
file << "__attribute__((alias(\"fexfn_pack_" << function_name << "\"))) auto " << function_name << "(";
for (std::size_t idx = 0; idx < data.param_types.size(); ++idx) {
auto& type = data.param_types[idx];
file << (idx == 0 ? "" : ", ") << format_decl(type, "a_" + std::to_string(idx));
}
file << ") -> " << data.return_type.getAsString() << ";\n";
}
for (std::size_t namespace_idx = 0; namespace_idx < namespaces.size(); ++namespace_idx) {
bool empty = true;
for (auto& symbol : thunked_api) {
if (symbol.symtable_namespace == namespace_idx) {
if (empty) {
file << "static struct { const char* name; void (*fn)(); } " << namespaces[namespace_idx].name << "_symtable[] = {\n";
empty = false;
}
file << " { \"" << symbol.function_name << "\", (void(*)())&" << symbol.function_name << " },\n";
}
}
if (!empty) {
file << " { nullptr, nullptr }\n";
file << "};\n";
}
}
file << "}\n";
}
if (!output_filenames.function_packs.empty()) {
std::ofstream file(output_filenames.function_packs);
// Thunks-internal packing functions
file << "extern \"C\" {\n";
for (auto& data : thunks) {
const auto& function_name = data.function_name;
@@ -592,15 +553,89 @@ void GenerateThunkLibsAction::EndSourceFileAction() {
file << "}\n";
}
file << "}\n";
// Publicly exports equivalent to symbols exported from the native guest library
file << "extern \"C\" {\n";
for (auto& data : thunked_api) {
if (data.custom_guest_impl) {
continue;
}
const auto& function_name = data.function_name;
file << "__attribute__((alias(\"fexfn_pack_" << function_name << "\"))) auto " << function_name << "(";
for (std::size_t idx = 0; idx < data.param_types.size(); ++idx) {
auto& type = data.param_types[idx];
file << (idx == 0 ? "" : ", ") << format_decl(type, "a_" + std::to_string(idx));
}
file << ") -> " << data.return_type.getAsString() << ";\n";
}
file << "}\n";
// Symbol enumerators
for (std::size_t namespace_idx = 0; namespace_idx < namespaces.size(); ++namespace_idx) {
const auto& ns = namespaces[namespace_idx];
file << "#define FOREACH_" << ns.name << (ns.name.empty() ? "" : "_") << "SYMBOL(EXPAND) \\\n";
for (auto& symbol : thunked_api) {
if (symbol.symtable_namespace.value_or(0) == namespace_idx) {
file << " EXPAND(" << symbol.function_name << ", \"TODO\") \\\n";
}
}
file << "\n";
}
}
if (!output_filenames.function_unpacks.empty()) {
std::ofstream file(output_filenames.function_unpacks);
// Files used host-side
if (!output_filenames.host.empty()) {
std::ofstream file(output_filenames.host);
// Forward declarations for symbols loaded from the native host library
for (auto& import : thunked_api) {
const auto& function_name = import.function_name;
const char* variadic_ellipsis = import.is_variadic ? ", ..." : "";
file << "using fexldr_type_" << libname << "_" << function_name << " = auto " << "(" << format_function_params(import) << variadic_ellipsis << ") -> " << import.return_type.getAsString() << ";\n";
file << "static fexldr_type_" << libname << "_" << function_name << " *fexldr_ptr_" << libname << "_" << function_name << ";\n";
}
file << "extern \"C\" {\n";
for (auto& thunk : thunks) {
const auto& function_name = thunk.function_name;
// Generate stub callbacks
for (auto& [cb_idx, cb] : thunk.callbacks) {
if (cb.is_stub) {
const char* variadic_ellipsis = cb.is_variadic ? ", ..." : "";
auto cb_function_name = "fexfn_unpack_" + get_callback_name(function_name, cb_idx) + "_stub";
file << "[[noreturn]] static " << cb.return_type.getAsString() << " "
<< cb_function_name << "("
<< format_function_params(cb) << variadic_ellipsis << ") {\n";
file << " fprintf(stderr, \"FATAL: Attempted to invoke callback stub for " << function_name << "\\n\");\n";
file << " std::abort();\n";
file << "}\n";
}
}
// Forward declarations for user-provided implementations
if (thunk.custom_host_impl) {
file << "static auto fexfn_impl_" << libname << "_" << function_name << "(";
for (std::size_t idx = 0; idx < thunk.param_types.size(); ++idx) {
// TODO: fex_guest_function_ptr for guest callbacks?
auto& type = thunk.param_types[idx];
file << (idx == 0 ? "" : ", ");
auto cb = thunk.callbacks.find(idx);
if (cb != thunk.callbacks.end() && cb->second.is_guest) {
file << "fex_guest_function_ptr a_" << idx;
} else {
file << format_decl(type, "a_" + std::to_string(idx));
}
}
// Using trailing return type as it makes handling function pointer returns much easier
file << ") -> " << thunk.return_type.getAsString() << ";\n";
}
// Packed argument structs used in fexfn_unpack_*
auto GeneratePackedArgs = [&](const auto &function_name, const auto &thunk) -> std::string {
std::string struct_name = "fexfn_packed_args_" + libname + "_" + function_name;
file << "struct " << struct_name << " {\n";
@@ -615,24 +650,9 @@ void GenerateThunkLibsAction::EndSourceFileAction() {
file << "};\n";
return struct_name;
};
/* Generate stub callbacks */
for (auto& [cb_idx, cb] : thunk.callbacks) {
if (cb.is_stub) {
const char* variadic_ellipsis = cb.is_variadic ? ", ..." : "";
auto cb_function_name = "fexfn_unpack_" + get_callback_name(function_name, cb_idx) + "_stub";
file << "[[noreturn]] static " << cb.return_type.getAsString() << " "
<< cb_function_name << "("
<< format_function_params(cb) << variadic_ellipsis << ") {\n";
file << " fprintf(stderr, \"FATAL: Attempted to invoke callback stub for " << function_name << "\\n\");\n";
file << " std::abort();\n";
file << "}\n";
}
}
auto struct_name = GeneratePackedArgs(function_name, thunk);
FunctionParams args = thunk;
// Unpacking functions
auto function_to_call = "fexldr_ptr_" + libname + "_" + function_name;
if (thunk.custom_host_impl) {
function_to_call = "fexfn_impl_" + libname + "_" + function_name;
@@ -657,29 +677,27 @@ void GenerateThunkLibsAction::EndSourceFileAction() {
}
};
file << format_function_args(args, format_param);
file << format_function_args(thunk, format_param);
}
file << ");\n";
file << "}\n";
}
file << "}\n";
}
if (!output_filenames.tab_function_unpacks.empty()) {
std::ofstream file(output_filenames.tab_function_unpacks);
// Endpoints for Guest->Host invocation of API functions
file << "static ExportEntry exports[] = {\n";
for (auto& thunk : thunks) {
const auto& function_name = thunk.function_name;
auto sha256 = get_sha256(function_name);
file << "{(uint8_t*)\"";
for (auto c : sha256) {
file << "\\x" << std::hex << std::setw(2) << std::setfill('0') << +c;
file << "\\x" << std::hex << std::setw(2) << std::setfill('0') << +c << std::dec;
}
file << "\", (void(*)(void *))&fexfn_unpack_" << libname << "_" << function_name << "}, // " << libname << ":" << function_name << "\n";
}
// Endpoints for Guest->Host invocation of runtime host-function pointers
for (auto& type : funcptr_types) {
std::string mangled_name = clang::QualType { type, 0 }.getAsString();
{
@@ -693,11 +711,10 @@ void GenerateThunkLibsAction::EndSourceFileAction() {
file << " {(uint8_t*)\"" << cb_sha256_str << "\", (void(*)(void *))&CallbackUnpack<" << mangled_name << ">::ForIndirectCall},\n";
}
}
}
if (!output_filenames.ldr.empty()) {
std::ofstream file(output_filenames.ldr);
file << " { nullptr, nullptr }\n";
file << "};\n";
// Symbol lookup from native host library
file << "static void* fexldr_ptr_" << libname << "_so;\n";
file << "extern \"C\" bool fexldr_init_" << libname << "() {\n";
@@ -714,32 +731,7 @@ void GenerateThunkLibsAction::EndSourceFileAction() {
}
file << " return true;\n";
file << "}\n";
}
if (!output_filenames.ldr_ptrs.empty()) {
std::ofstream file(output_filenames.ldr_ptrs);
for (auto& import : thunked_api) {
const auto& function_name = import.function_name;
const char* variadic_ellipsis = import.is_variadic ? ", ..." : "";
file << "using fexldr_type_" << libname << "_" << function_name << " = auto " << "(" << format_function_params(import) << variadic_ellipsis << ") -> " << import.return_type.getAsString() << ";\n";
file << "static fexldr_type_" << libname << "_" << function_name << " *fexldr_ptr_" << libname << "_" << function_name << ";\n";
}
}
if (!output_filenames.symbol_list.empty()) {
std::ofstream file(output_filenames.symbol_list);
for (std::size_t namespace_idx = 0; namespace_idx < namespaces.size(); ++namespace_idx) {
const auto& ns = namespaces[namespace_idx];
file << "#define FOREACH_" << ns.name << (ns.name.empty() ? "" : "_") << "SYMBOL(EXPAND) \\\n";
for (auto& symbol : thunked_api) {
if (symbol.symtable_namespace.value_or(0) == namespace_idx) {
file << " EXPAND(" << symbol.function_name << ", \"TODO\") \\\n";
}
}
file << "\n";
}
}
}
+2 -13
View File
@@ -5,19 +5,8 @@
#include <string>
struct OutputFilenames {
// Host
std::string function_unpacks;
std::string tab_function_unpacks;
std::string ldr;
std::string ldr_ptrs;
// Guest
std::string thunks;
std::string function_packs;
std::string function_packs_public;
// Guest + Host
std::string symbol_list;
std::string host;
std::string guest;
};
class GenerateThunkLibsAction : public clang::ASTFrontendAction {
+15 -25
View File
@@ -26,37 +26,27 @@ int main(int argc, char* argv[]) {
std::cerr << "\nError: " << error << "\n";
return EXIT_FAILURE;
}
char** const last_internal_arg = argv + argc;
// Process arguments before the "--" separator
if (argc != 5) {
print_usage(argv[0]);
return EXIT_FAILURE;
}
char** arg = argv + 1;
const auto filename = *arg++;
const std::string libname = *arg++;
const std::string target_abi = *arg++;
const std::string output_filename = *arg++;
// Iterate over generator targets (remaining arguments up to "--" separator)
OutputFilenames output_filenames;
while (arg < last_internal_arg) {
auto target = std::string { *arg++ };
auto out_filename = *arg++;
if (target == "-function_unpacks") {
output_filenames.function_unpacks = out_filename;
} else if (target == "-tab_function_unpacks") {
output_filenames.tab_function_unpacks = out_filename;
} else if (target == "-ldr") {
output_filenames.ldr = out_filename;
} else if (target == "-ldr_ptrs") {
output_filenames.ldr_ptrs = out_filename;
} else if (target == "-thunks") {
output_filenames.thunks = out_filename;
} else if (target == "-function_packs") {
output_filenames.function_packs = out_filename;
} else if (target == "-function_packs_public") {
output_filenames.function_packs_public = out_filename;
} else if (target == "-symbol_list") {
output_filenames.symbol_list = out_filename;
} else {
std::cerr << "Unrecognized generator target \"" << target << "\"\n";
return EXIT_FAILURE;
}
if (target_abi == "-host") {
output_filenames.host = output_filename;
} else if (target_abi == "-guest") {
output_filenames.guest = output_filename;
} else {
std::cerr << "Unrecognized generator target ABI \"" << target_abi << "\"\n";
return EXIT_FAILURE;
}
ClangTool Tool(*compile_db, { filename });
+111 -59
View File
@@ -17,7 +17,7 @@ else()
set(GENERATE_GUEST_INSTALL_TARGETS FALSE)
endif()
# Syntax: generate(libxyz libxyz-interface.cpp generator-targets...)
# Syntax: generate(libxyz libxyz-interface.cpp)
# This defines a target and a custom command:
# - custom command: Main build step that runs the thunk generator on the given interface definition
# - libxyz-guest-deps: Interface target to read include directories from which are passed to libclang when parsing the interface definition
@@ -25,36 +25,42 @@ function(generate NAME SOURCE_FILE)
# Interface target for the user to add include directories
add_library(${NAME}-guest-deps INTERFACE)
target_include_directories(${NAME}-guest-deps INTERFACE "${CMAKE_CURRENT_SOURCE_DIR}/../include")
target_compile_definitions(${NAME}-guest-deps INTERFACE GUEST_THUNK_LIBRARY)
# Shorthand for the include directories added after calling this function.
# This is not evaluated directly, hence directories added after return are still picked up
set(prop "$<TARGET_PROPERTY:${NAME}-guest-deps,INTERFACE_INCLUDE_DIRECTORIES>")
set(compile_prop "$<TARGET_PROPERTY:${NAME}-guest-deps,INTERFACE_COMPILE_DEFINITIONS>")
# Run thunk generator for each of the given output files
foreach(WHAT IN LISTS ARGN)
set(OUTFOLDER "${CMAKE_CURRENT_BINARY_DIR}/gen/${NAME}")
set(OUTFILE "${OUTFOLDER}/${WHAT}.inl")
set(OUTFOLDER "${CMAKE_CURRENT_BINARY_DIR}/gen")
set(OUTFILE "${OUTFOLDER}/thunkgen_guest_${NAME}.inl")
file(MAKE_DIRECTORY "${OUTFOLDER}")
file(MAKE_DIRECTORY "${OUTFOLDER}")
add_custom_command(
OUTPUT "${OUTFILE}"
DEPENDS "${GENERATOR_EXE}"
DEPENDS "${SOURCE_FILE}"
COMMAND "${GENERATOR_EXE}" "${SOURCE_FILE}" "${NAME}" "-${WHAT}" "${OUTFILE}" -- -std=c++17 -DGUEST_THUNK_LIBRARY
# Expand include directories to space-separated list of -isystem parameters
"$<$<BOOL:${prop}>:;-isystem$<JOIN:${prop},;-isystem>>"
VERBATIM
COMMAND_EXPAND_LISTS
)
add_custom_command(
OUTPUT "${OUTFILE}"
DEPENDS "${GENERATOR_EXE}"
DEPENDS "${SOURCE_FILE}"
COMMAND "${GENERATOR_EXE}" "${SOURCE_FILE}" "${NAME}" "-guest" "${OUTFILE}" -- -std=c++17
# Expand compile definitions to space-separated list of -D parameters
"$<$<BOOL:${compile_prop}>:;-D$<JOIN:${compile_prop},;-D>>"
# Expand include directories to space-separated list of -isystem parameters
"$<$<BOOL:${prop}>:;-isystem$<JOIN:${prop},;-isystem>>"
VERBATIM
COMMAND_EXPAND_LISTS
)
list(APPEND OUTPUTS "${OUTFILE}")
endforeach()
list(APPEND OUTPUTS "${OUTFILE}")
set(GEN_${NAME} ${OUTPUTS} PARENT_SCOPE)
endfunction()
function(add_guest_lib NAME)
function(add_guest_lib NAME SONAME)
set (SOURCE_FILE ../lib${NAME}/lib${NAME}_Guest.cpp)
get_filename_component(SOURCE_FILE_ABS "${SOURCE_FILE}" ABSOLUTE)
set (SOURCE_LDS_FILE ../lib${NAME}/lib${NAME}_Guest.lds)
get_filename_component(SOURCE_LDS_FILE_ABS "${SOURCE_LDS_FILE}" ABSOLUTE)
if (NOT EXISTS "${SOURCE_FILE_ABS}")
set (SOURCE_FILE ../lib${NAME}/Guest.cpp)
get_filename_component(SOURCE_FILE_ABS "${SOURCE_FILE}" ABSOLUTE)
@@ -64,13 +70,35 @@ function(add_guest_lib NAME)
endif()
add_library(${NAME}-guest ${TARGET_TYPE} ${SOURCE_FILE} ${GEN_lib${NAME}})
target_include_directories(${NAME}-guest PRIVATE "${CMAKE_CURRENT_BINARY_DIR}/gen/lib${NAME}")
target_include_directories(${NAME}-guest PRIVATE "${CMAKE_CURRENT_BINARY_DIR}/gen/")
target_compile_definitions(${NAME}-guest PRIVATE GUEST_THUNK_LIBRARY)
target_link_libraries(${NAME}-guest PRIVATE lib${NAME}-guest-deps)
## Make signed overflow well defined 2's complement overflow
target_compile_options(${NAME}-guest PRIVATE -fwrapv)
# Add linker script if set
if (EXISTS "${SOURCE_LDS_FILE_ABS}")
target_link_options(${NAME}-guest PRIVATE "-T" "${CMAKE_CURRENT_SOURCE_DIR}/../lib${NAME}/lib${NAME}_Guest.lds")
endif()
# We need to override the soname for the linker.
# Our guest thunk libraries are named `lib<Thunk>-guest`.
# Once we override the loaded name, the guest is free to dlopen again by SONAME rather than filepath.
# eg:
# dlopen("libGL.so.1", RTLD_GLOBAL | RTLD_NOW); -> We override this `libGL.so.1` to `libGL-guest.so`
# Later on in the program, it can do:
# dlopen("libGL.so.1", RTLD_GLOBAL | RTLD_NOLOAD);
# This second dlopen will only check to see if the previous load has made the library resident
# Searching for SONAME in the process.
#
# Additionally, VDSO can only be opened by SONAME.
# This means it will only ever open the handle with `dlopen("linux-vdso.so.1", RTLD_GLOBAL | RTLD_NOLOAD);
# Note that this doesn't have a lib prefix, and also since it doesn't exist on the filesystem, it can never
# Actually load from a path.
target_link_options(${NAME}-guest PRIVATE "LINKER:-soname,${SONAME}")
set_target_properties(${NAME}-guest PROPERTIES NO_SONAME ON)
if (GENERATE_GUEST_INSTALL_TARGETS)
install(TARGETS ${NAME}-guest DESTINATION ${DATA_DIRECTORY}/GuestThunks/)
endif()
@@ -79,17 +107,17 @@ endfunction()
#add_guest_lib(fex_malloc_loader)
#target_link_libraries(fex_malloc_loader-guest PRIVATE dl)
#generate(libfex_malloc thunks function_packs function_packs_public)
#generate(libfex_malloc)
#add_guest_lib(fex_malloc)
generate(libasound ${CMAKE_CURRENT_SOURCE_DIR}/../libasound/libasound_interface.cpp thunks function_packs function_packs_public)
add_guest_lib(asound)
generate(libasound ${CMAKE_CURRENT_SOURCE_DIR}/../libasound/libasound_interface.cpp)
add_guest_lib(asound "libasound.so.2")
generate(libEGL ${CMAKE_CURRENT_SOURCE_DIR}/../libEGL/libEGL_interface.cpp thunks function_packs function_packs_public)
add_guest_lib(EGL)
generate(libEGL ${CMAKE_CURRENT_SOURCE_DIR}/../libEGL/libEGL_interface.cpp)
add_guest_lib(EGL "libEGL.so.1")
generate(libGL ${CMAKE_CURRENT_SOURCE_DIR}/../libGL/libGL_interface.cpp thunks function_packs function_packs_public symbol_list)
add_guest_lib(GL)
generate(libGL ${CMAKE_CURRENT_SOURCE_DIR}/../libGL/libGL_interface.cpp)
add_guest_lib(GL "libGL.so.1")
# libGL must pull in libX11.so, so generate a placeholder libX11.so to link against
add_library(X11 SHARED ../libX11/libX11_NativeGuest.cpp)
@@ -97,59 +125,83 @@ target_link_libraries(GL-guest PRIVATE X11)
# disabled for now, headers are platform specific
# find_package(SDL2 REQUIRED)
# generate(libSDL2 thunks function_packs function_packs_public)
# generate(libSDL2)
# add_guest_lib(SDL2)
# target_include_directories(SDL2-guest PRIVATE ${SDL2_INCLUDE_DIRS})
# target_link_libraries(SDL2-guest PRIVATE GL)
# target_link_libraries(SDL2-guest PRIVATE dl)
generate(libX11 ${CMAKE_CURRENT_SOURCE_DIR}/../libX11/libX11_interface.cpp thunks function_packs function_packs_public)
add_guest_lib(X11)
find_package(PkgConfig)
pkg_search_module(X11 REQUIRED x11)
generate(libXext ${CMAKE_CURRENT_SOURCE_DIR}/../libXext/libXext_interface.cpp thunks function_packs function_packs_public)
add_guest_lib(Xext)
string(REGEX MATCH "([0-9]*)\.([0-9]*)\.([0-9]*)" _ "${X11_VERSION}")
set(X11_VERSION_MAJOR ${CMAKE_MATCH_1})
set(X11_VERSION_MINOR ${CMAKE_MATCH_2})
set(X11_VERSION_PATCH ${CMAKE_MATCH_3})
generate(libXrender ${CMAKE_CURRENT_SOURCE_DIR}/../libXrender/libXrender_interface.cpp thunks function_packs function_packs_public)
add_guest_lib(Xrender)
generate(libX11 ${CMAKE_CURRENT_SOURCE_DIR}/../libX11/libX11_interface.cpp)
add_guest_lib(X11 "libX11.so.6")
generate(libXfixes ${CMAKE_CURRENT_SOURCE_DIR}/../libXfixes/libXfixes_interface.cpp thunks function_packs function_packs_public)
add_guest_lib(Xfixes)
target_compile_definitions(libX11-guest-deps INTERFACE -DX11_VERSION_MAJOR=${X11_VERSION_MAJOR})
target_compile_definitions(libX11-guest-deps INTERFACE -DX11_VERSION_MINOR=${X11_VERSION_MINOR})
target_compile_definitions(libX11-guest-deps INTERFACE -DX11_VERSION_PATCH=${X11_VERSION_PATCH})
generate(libvulkan ${CMAKE_CURRENT_SOURCE_DIR}/../libvulkan/libvulkan_interface.cpp thunks function_packs function_packs_public symbol_list)
generate(libXext ${CMAKE_CURRENT_SOURCE_DIR}/../libXext/libXext_interface.cpp)
add_guest_lib(Xext "libXext.so.6")
target_compile_definitions(libXext-guest-deps INTERFACE -DX11_VERSION_MAJOR=${X11_VERSION_MAJOR})
target_compile_definitions(libXext-guest-deps INTERFACE -DX11_VERSION_MINOR=${X11_VERSION_MINOR})
target_compile_definitions(libXext-guest-deps INTERFACE -DX11_VERSION_PATCH=${X11_VERSION_PATCH})
generate(libXrender ${CMAKE_CURRENT_SOURCE_DIR}/../libXrender/libXrender_interface.cpp)
add_guest_lib(Xrender "libXrender.so.1")
generate(libXfixes ${CMAKE_CURRENT_SOURCE_DIR}/../libXfixes/libXfixes_interface.cpp)
add_guest_lib(Xfixes "libXfixes.so.3")
generate(libvulkan ${CMAKE_CURRENT_SOURCE_DIR}/../libvulkan/libvulkan_interface.cpp)
target_include_directories(libvulkan-guest-deps INTERFACE ${FEX_PROJECT_SOURCE_DIR}/External/Vulkan-Headers/include/)
add_guest_lib(vulkan)
add_guest_lib(vulkan "libvulkan.so.1")
generate(libxcb ${CMAKE_CURRENT_SOURCE_DIR}/../libxcb/libxcb_interface.cpp thunks function_packs function_packs_public)
add_guest_lib(xcb)
generate(libxcb ${CMAKE_CURRENT_SOURCE_DIR}/../libxcb/libxcb_interface.cpp)
add_guest_lib(xcb "libxcb.so.1")
generate(libxcb-dri2 ${CMAKE_CURRENT_SOURCE_DIR}/../libxcb-dri2/libxcb-dri2_interface.cpp thunks function_packs function_packs_public)
add_guest_lib(xcb-dri2)
generate(libxcb-dri2 ${CMAKE_CURRENT_SOURCE_DIR}/../libxcb-dri2/libxcb-dri2_interface.cpp)
add_guest_lib(xcb-dri2 "libxcb-dri2.so.0")
generate(libxcb-dri3 ${CMAKE_CURRENT_SOURCE_DIR}/../libxcb-dri3/libxcb-dri3_interface.cpp thunks function_packs function_packs_public)
add_guest_lib(xcb-dri3)
generate(libxcb-dri3 ${CMAKE_CURRENT_SOURCE_DIR}/../libxcb-dri3/libxcb-dri3_interface.cpp)
add_guest_lib(xcb-dri3 "libxcb-dri3.so.0")
generate(libxcb-xfixes ${CMAKE_CURRENT_SOURCE_DIR}/../libxcb-xfixes/libxcb-xfixes_interface.cpp thunks function_packs function_packs_public)
add_guest_lib(xcb-xfixes)
generate(libxcb-xfixes ${CMAKE_CURRENT_SOURCE_DIR}/../libxcb-xfixes/libxcb-xfixes_interface.cpp)
add_guest_lib(xcb-xfixes "libxcb-xfixes.so.0")
generate(libxcb-shm ${CMAKE_CURRENT_SOURCE_DIR}/../libxcb-shm/libxcb-shm_interface.cpp thunks function_packs function_packs_public)
add_guest_lib(xcb-shm)
generate(libxcb-shm ${CMAKE_CURRENT_SOURCE_DIR}/../libxcb-shm/libxcb-shm_interface.cpp)
add_guest_lib(xcb-shm "libxcb-shm.so.0")
generate(libxcb-sync ${CMAKE_CURRENT_SOURCE_DIR}/../libxcb-sync/libxcb-sync_interface.cpp thunks function_packs function_packs_public)
add_guest_lib(xcb-sync)
generate(libxcb-sync ${CMAKE_CURRENT_SOURCE_DIR}/../libxcb-sync/libxcb-sync_interface.cpp)
add_guest_lib(xcb-sync "libxcb-sync.so.1")
generate(libxcb-present ${CMAKE_CURRENT_SOURCE_DIR}/../libxcb-present/libxcb-present_interface.cpp thunks function_packs function_packs_public)
add_guest_lib(xcb-present)
generate(libxcb-present ${CMAKE_CURRENT_SOURCE_DIR}/../libxcb-present/libxcb-present_interface.cpp)
add_guest_lib(xcb-present "libxcb-present.so.0")
generate(libxcb-randr ${CMAKE_CURRENT_SOURCE_DIR}/../libxcb-randr/libxcb-randr_interface.cpp thunks function_packs function_packs_public)
add_guest_lib(xcb-randr)
generate(libxcb-randr ${CMAKE_CURRENT_SOURCE_DIR}/../libxcb-randr/libxcb-randr_interface.cpp)
add_guest_lib(xcb-randr "libxcb-randr.so.0")
generate(libxcb-glx ${CMAKE_CURRENT_SOURCE_DIR}/../libxcb-glx/libxcb-glx_interface.cpp thunks function_packs function_packs_public)
add_guest_lib(xcb-glx)
generate(libxcb-glx ${CMAKE_CURRENT_SOURCE_DIR}/../libxcb-glx/libxcb-glx_interface.cpp)
add_guest_lib(xcb-glx "libxcb-glx.so.0")
generate(libxshmfence ${CMAKE_CURRENT_SOURCE_DIR}/../libxshmfence/libxshmfence_interface.cpp thunks function_packs function_packs_public)
add_guest_lib(xshmfence)
generate(libxshmfence ${CMAKE_CURRENT_SOURCE_DIR}/../libxshmfence/libxshmfence_interface.cpp)
add_guest_lib(xshmfence "libxshmfence.so.1")
generate(libdrm ${CMAKE_CURRENT_SOURCE_DIR}/../libdrm/libdrm_interface.cpp thunks function_packs function_packs_public)
generate(libdrm ${CMAKE_CURRENT_SOURCE_DIR}/../libdrm/libdrm_interface.cpp)
target_include_directories(libdrm-guest-deps INTERFACE /usr/include/drm/)
target_include_directories(libdrm-guest-deps INTERFACE /usr/include/libdrm/)
add_guest_lib(drm)
add_guest_lib(drm "libdrm.so.2")
generate(libVDSO ${CMAKE_CURRENT_SOURCE_DIR}/../libVDSO/libVDSO_interface.cpp)
add_guest_lib(VDSO "linux-vdso.so.1")
# Can't use a stack protector because otherwise cross-compiling fails
# Not necessary anyway because it only trampolines
target_compile_options(VDSO-guest PRIVATE "-fno-stack-protector")
target_link_options(VDSO-guest PRIVATE "-T" "${CMAKE_CURRENT_SOURCE_DIR}/../libVDSO/libVDSO_Guest.lds" "-nostdlib"
"LINKER:--no-undefined" "LINKER:-z,max-page-size=4096" "LINKER:--hash-style=both")
+62 -40
View File
@@ -4,7 +4,7 @@ project(host-thunks)
set(CMAKE_CXX_STANDARD 17)
set (HOSTLIBS_DATA_DIRECTORY "${CMAKE_INSTALL_PREFIX}/lib/fex-emu" CACHE PATH "global data directory")
# Syntax: generate(libxyz libxyz-interface.cpp generator-targets...)
# Syntax: generate(libxyz libxyz-interface.cpp)
# This defines two targets and a custom command:
# - custom command: Main build step that runs the thunk generator on the given interface definition
# - libxyz-interface: Target for IDE integration (making sure libxyz-interface.cpp shows up as a source file in the project tree)
@@ -17,31 +17,37 @@ function(generate NAME SOURCE_FILE)
# Shorthand for the include directories added after calling this function.
# This is not evaluated directly, hence directories added after return are still picked up
set(prop "$<TARGET_PROPERTY:${NAME}-deps,INTERFACE_INCLUDE_DIRECTORIES>")
set(compile_prop "$<TARGET_PROPERTY:${NAME}-deps,INTERFACE_COMPILE_DEFINITIONS>")
if (CMAKE_SYSTEM_PROCESSOR MATCHES "x86_64")
list(APPEND compile_prop _M_X86_64=1)
elseif (CMAKE_SYSTEM_PROCESSOR MATCHES "aarch64")
list(APPEND compile_prop _M_ARM_64=1)
endif()
# Target for IDE integration
add_library(${NAME}-interface EXCLUDE_FROM_ALL ${SOURCE_FILE})
target_link_libraries(${NAME}-interface PRIVATE ${NAME}-deps)
# Run thunk generator for each of the given output files
foreach(WHAT IN LISTS ARGN)
set(OUTFOLDER "${CMAKE_CURRENT_BINARY_DIR}/gen/${NAME}")
set(OUTFILE "${OUTFOLDER}/${WHAT}.inl")
set(OUTFOLDER "${CMAKE_CURRENT_BINARY_DIR}/gen")
set(OUTFILE "${OUTFOLDER}/thunkgen_host_${NAME}.inl")
file(MAKE_DIRECTORY "${OUTFOLDER}")
add_custom_command(
OUTPUT "${OUTFILE}"
DEPENDS "${SOURCE_FILE}"
DEPENDS thunkgen
COMMAND thunkgen "${SOURCE_FILE}" "${NAME}" "-${WHAT}" "${OUTFILE}" -- -std=c++17
# Expand include directories to space-separated list of -isystem parameters
"$<$<BOOL:${prop}>:;-isystem$<JOIN:${prop},;-isystem>>"
VERBATIM
COMMAND_EXPAND_LISTS
)
file(MAKE_DIRECTORY "${OUTFOLDER}")
list(APPEND OUTPUTS "${OUTFILE}")
endforeach()
add_custom_command(
OUTPUT "${OUTFILE}"
DEPENDS "${SOURCE_FILE}"
DEPENDS thunkgen
COMMAND thunkgen "${SOURCE_FILE}" "${NAME}" "-host" "${OUTFILE}" -- -std=c++17
# Expand compile definitions to space-separated list of -D parameters
"$<$<BOOL:${compile_prop}>:;-D$<JOIN:${compile_prop},;-D>>"
# Expand include directories to space-separated list of -isystem parameters
"$<$<BOOL:${prop}>:;-isystem$<JOIN:${prop},;-isystem>>"
VERBATIM
COMMAND_EXPAND_LISTS
)
list(APPEND OUTPUTS "${OUTFILE}")
set(GEN_${NAME} ${OUTPUTS} PARENT_SCOPE)
endfunction()
@@ -57,7 +63,7 @@ function(add_host_lib NAME)
endif()
add_library(${NAME}-host SHARED ${SOURCE_FILE} ${GEN_lib${NAME}})
target_include_directories(${NAME}-host PRIVATE "${CMAKE_CURRENT_BINARY_DIR}/gen/lib${NAME}")
target_include_directories(${NAME}-host PRIVATE "${CMAKE_CURRENT_BINARY_DIR}/gen/")
target_link_libraries(${NAME}-host PRIVATE dl)
target_link_libraries(${NAME}-host PRIVATE lib${NAME}-deps)
## Make signed overflow well defined 2's complement overflow
@@ -71,16 +77,16 @@ endfunction()
#add_host_lib(fex_malloc_symbols)
#generate(libfex_malloc function_unpacks tab_function_unpacks ldr ldr_ptrs)
#generate(libfex_malloc)
#add_host_lib(fex_malloc)
generate(libasound ${CMAKE_CURRENT_SOURCE_DIR}/../libasound/libasound_interface.cpp function_unpacks tab_function_unpacks ldr ldr_ptrs)
generate(libasound ${CMAKE_CURRENT_SOURCE_DIR}/../libasound/libasound_interface.cpp)
add_host_lib(asound)
generate(libEGL ${CMAKE_CURRENT_SOURCE_DIR}/../libEGL/libEGL_interface.cpp function_unpacks tab_function_unpacks ldr ldr_ptrs)
generate(libEGL ${CMAKE_CURRENT_SOURCE_DIR}/../libEGL/libEGL_interface.cpp)
add_host_lib(EGL)
generate(libGL ${CMAKE_CURRENT_SOURCE_DIR}/../libGL/libGL_interface.cpp function_unpacks tab_function_unpacks ldr ldr_ptrs)
generate(libGL ${CMAKE_CURRENT_SOURCE_DIR}/../libGL/libGL_interface.cpp)
add_host_lib(GL)
find_package(OpenGL REQUIRED)
@@ -88,57 +94,73 @@ target_link_libraries(GL-host PRIVATE OpenGL::GL)
# disabled for now, headers are platform specific
# find_package(SDL2 REQUIRED)
# generate(libSDL2 function_unpacks tab_function_unpacks ldr ldr_ptrs)
# generate(libSDL2)
# add_host_lib(SDL2)
# target_include_directories(SDL2-host PRIVATE ${SDL2_INCLUDE_DIRS})
generate(libX11 ${CMAKE_CURRENT_SOURCE_DIR}/../libX11/libX11_interface.cpp function_unpacks tab_function_unpacks ldr ldr_ptrs)
find_package(PkgConfig)
pkg_search_module(X11 REQUIRED x11)
string(REGEX MATCH "([0-9]*)\.([0-9]*)\.([0-9]*)" _ "${X11_VERSION}")
set(X11_VERSION_MAJOR ${CMAKE_MATCH_1})
set(X11_VERSION_MINOR ${CMAKE_MATCH_2})
set(X11_VERSION_PATCH ${CMAKE_MATCH_3})
generate(libX11 ${CMAKE_CURRENT_SOURCE_DIR}/../libX11/libX11_interface.cpp)
add_host_lib(X11)
generate(libXext ${CMAKE_CURRENT_SOURCE_DIR}/../libXext/libXext_interface.cpp function_unpacks tab_function_unpacks ldr ldr_ptrs)
target_compile_definitions(libX11-deps INTERFACE -DX11_VERSION_MAJOR=${X11_VERSION_MAJOR})
target_compile_definitions(libX11-deps INTERFACE -DX11_VERSION_MINOR=${X11_VERSION_MINOR})
target_compile_definitions(libX11-deps INTERFACE -DX11_VERSION_PATCH=${X11_VERSION_PATCH})
generate(libXext ${CMAKE_CURRENT_SOURCE_DIR}/../libXext/libXext_interface.cpp)
add_host_lib(Xext)
generate(libXrender ${CMAKE_CURRENT_SOURCE_DIR}/../libXrender/libXrender_interface.cpp function_unpacks tab_function_unpacks ldr ldr_ptrs)
target_compile_definitions(libXext-deps INTERFACE -DX11_VERSION_MAJOR=${X11_VERSION_MAJOR})
target_compile_definitions(libXext-deps INTERFACE -DX11_VERSION_MINOR=${X11_VERSION_MINOR})
target_compile_definitions(libXext-deps INTERFACE -DX11_VERSION_PATCH=${X11_VERSION_PATCH})
generate(libXrender ${CMAKE_CURRENT_SOURCE_DIR}/../libXrender/libXrender_interface.cpp)
add_host_lib(Xrender)
generate(libXfixes ${CMAKE_CURRENT_SOURCE_DIR}/../libXfixes/libXfixes_interface.cpp function_unpacks tab_function_unpacks ldr ldr_ptrs)
generate(libXfixes ${CMAKE_CURRENT_SOURCE_DIR}/../libXfixes/libXfixes_interface.cpp)
add_host_lib(Xfixes)
generate(libvulkan ${CMAKE_CURRENT_SOURCE_DIR}/../libvulkan/libvulkan_interface.cpp function_unpacks tab_function_unpacks ldr ldr_ptrs symbol_list)
generate(libvulkan ${CMAKE_CURRENT_SOURCE_DIR}/../libvulkan/libvulkan_interface.cpp)
target_include_directories(libvulkan-deps INTERFACE ${FEX_PROJECT_SOURCE_DIR}/External/Vulkan-Headers/include/)
add_host_lib(vulkan)
generate(libxcb ${CMAKE_CURRENT_SOURCE_DIR}/../libxcb/libxcb_interface.cpp function_unpacks tab_function_unpacks ldr ldr_ptrs)
generate(libxcb ${CMAKE_CURRENT_SOURCE_DIR}/../libxcb/libxcb_interface.cpp)
add_host_lib(xcb)
generate(libxcb-dri2 ${CMAKE_CURRENT_SOURCE_DIR}/../libxcb-dri2/libxcb-dri2_interface.cpp function_unpacks tab_function_unpacks ldr ldr_ptrs)
generate(libxcb-dri2 ${CMAKE_CURRENT_SOURCE_DIR}/../libxcb-dri2/libxcb-dri2_interface.cpp)
add_host_lib(xcb-dri2)
generate(libxcb-dri3 ${CMAKE_CURRENT_SOURCE_DIR}/../libxcb-dri3/libxcb-dri3_interface.cpp function_unpacks tab_function_unpacks ldr ldr_ptrs)
generate(libxcb-dri3 ${CMAKE_CURRENT_SOURCE_DIR}/../libxcb-dri3/libxcb-dri3_interface.cpp)
add_host_lib(xcb-dri3)
generate(libxcb-xfixes ${CMAKE_CURRENT_SOURCE_DIR}/../libxcb-xfixes/libxcb-xfixes_interface.cpp function_unpacks tab_function_unpacks ldr ldr_ptrs)
generate(libxcb-xfixes ${CMAKE_CURRENT_SOURCE_DIR}/../libxcb-xfixes/libxcb-xfixes_interface.cpp)
add_host_lib(xcb-xfixes)
generate(libxcb-shm ${CMAKE_CURRENT_SOURCE_DIR}/../libxcb-shm/libxcb-shm_interface.cpp function_unpacks tab_function_unpacks ldr ldr_ptrs)
generate(libxcb-shm ${CMAKE_CURRENT_SOURCE_DIR}/../libxcb-shm/libxcb-shm_interface.cpp)
add_host_lib(xcb-shm)
generate(libxcb-sync ${CMAKE_CURRENT_SOURCE_DIR}/../libxcb-sync/libxcb-sync_interface.cpp function_unpacks tab_function_unpacks ldr ldr_ptrs)
generate(libxcb-sync ${CMAKE_CURRENT_SOURCE_DIR}/../libxcb-sync/libxcb-sync_interface.cpp)
add_host_lib(xcb-sync)
generate(libxcb-present ${CMAKE_CURRENT_SOURCE_DIR}/../libxcb-present/libxcb-present_interface.cpp function_unpacks tab_function_unpacks ldr ldr_ptrs)
generate(libxcb-present ${CMAKE_CURRENT_SOURCE_DIR}/../libxcb-present/libxcb-present_interface.cpp)
add_host_lib(xcb-present)
generate(libxcb-randr ${CMAKE_CURRENT_SOURCE_DIR}/../libxcb-randr/libxcb-randr_interface.cpp function_unpacks tab_function_unpacks ldr ldr_ptrs)
generate(libxcb-randr ${CMAKE_CURRENT_SOURCE_DIR}/../libxcb-randr/libxcb-randr_interface.cpp)
add_host_lib(xcb-randr)
generate(libxcb-glx ${CMAKE_CURRENT_SOURCE_DIR}/../libxcb-glx/libxcb-glx_interface.cpp function_unpacks tab_function_unpacks ldr ldr_ptrs)
generate(libxcb-glx ${CMAKE_CURRENT_SOURCE_DIR}/../libxcb-glx/libxcb-glx_interface.cpp)
add_host_lib(xcb-glx)
generate(libxshmfence ${CMAKE_CURRENT_SOURCE_DIR}/../libxshmfence/libxshmfence_interface.cpp function_unpacks tab_function_unpacks ldr ldr_ptrs)
generate(libxshmfence ${CMAKE_CURRENT_SOURCE_DIR}/../libxshmfence/libxshmfence_interface.cpp)
add_host_lib(xshmfence)
generate(libdrm ${CMAKE_CURRENT_SOURCE_DIR}/../libdrm/libdrm_interface.cpp function_unpacks tab_function_unpacks ldr ldr_ptrs)
generate(libdrm ${CMAKE_CURRENT_SOURCE_DIR}/../libdrm/libdrm_interface.cpp)
target_include_directories(libdrm-deps INTERFACE /usr/include/drm/)
target_include_directories(libdrm-deps INTERFACE /usr/include/libdrm/)
add_host_lib(drm)
+1 -4
View File
@@ -72,7 +72,7 @@ public:
#define EXPORTS(name) \
extern "C" { \
ExportEntry* fexthunks_exports_##name(uintptr_t allocate, uintptr_t finalize) { \
ExportEntry* fexthunks_exports_##name() { \
if (!fexldr_init_##name()) { \
return nullptr; \
} \
@@ -102,9 +102,6 @@ struct GuestcallInfo {
#elif defined(_M_ARM_64)
#define LOAD_INTERNAL_GUESTPTR_VIA_CUSTOM_ABI(target_variable) \
asm volatile("mov %0, x11" : "=r" (target_variable))
#else
#define LOAD_INTERNAL_GUESTPTR_VIA_CUSTOM_ABI(target_variable) \
abort()
#endif
template<typename>
+2 -4
View File
@@ -13,9 +13,7 @@ $end_info$
#include "common/Guest.h"
#include "thunks.inl"
#include "function_packs.inl"
#include "function_packs_public.inl"
#include "thunkgen_guest_libEGL.inl"
typedef void voidFunc();
@@ -27,4 +25,4 @@ extern "C" {
}
}
LOAD_LIB(libEGL)
LOAD_LIB(libEGL)
+2 -10
View File
@@ -12,14 +12,6 @@ $end_info$
#include "common/Host.h"
#include <dlfcn.h>
#include "ldr_ptrs.inl"
#include "function_unpacks.inl"
#include "thunkgen_host_libEGL.inl"
static ExportEntry exports[] = {
#include "tab_function_unpacks.inl"
{ nullptr, nullptr }
};
#include "ldr.inl"
EXPORTS(libEGL)
EXPORTS(libEGL)
+1 -4
View File
@@ -24,10 +24,7 @@ $end_info$
#include "common/Guest.h"
#include "thunks.inl"
#include "function_packs.inl"
#include "function_packs_public.inl"
#include "symbol_list.inl"
#include "thunkgen_guest_libGL.inl"
typedef void voidFunc();
+2 -10
View File
@@ -22,18 +22,10 @@ $end_info$
#include "common/Host.h"
#include "thunkgen_host_libGL.inl"
void* symbolFromGlXGetProcAddr(void*, const char* name) {
return (void*)glXGetProcAddress((const GLubyte*)name);
}
#include "ldr_ptrs.inl"
#include "function_unpacks.inl"
static ExportEntry exports[] = {
#include "tab_function_unpacks.inl"
{ nullptr, nullptr }
};
#include "ldr.inl"
EXPORTS(libGL)
+2 -4
View File
@@ -19,9 +19,7 @@ $end_info$
#include "common/Guest.h"
#include "thunks.inl"
#include "function_packs.inl"
#include "function_packs_public.inl"
#include "thunkgen_guest_libSDL2.inl"
LOAD_LIB(libSDL2)
@@ -70,4 +68,4 @@ extern "C" {
dlclose(lib);
}
}
}
}
+1 -10
View File
@@ -12,15 +12,6 @@ $end_info$
#include "common/Host.h"
#include <dlfcn.h>
#include "ldr_ptrs.inl"
#include "function_unpacks.inl"
static ExportEntry exports[] = {
#include "tab_function_unpacks.inl"
{ nullptr, nullptr }
};
#include "ldr.inl"
#include "thunkgen_host_libSDL2.inl"
EXPORTS(libSDL2)
+26
View File
@@ -0,0 +1,26 @@
/*
$info$
tags: thunklibs|VDSO
desc: Linux VDSO thunking
$end_info$
*/
#include <stdio.h>
#include <cstring>
#include <sched.h>
#include <sys/time.h>
#include <time.h>
#include "common/Guest.h"
#include "thunkgen_guest_libVDSO.inl"
extern "C" {
time_t __vdso_time(time_t *tloc) __attribute__((alias("fexfn_pack_time")));
int __vdso_gettimeofday(struct timeval *tv, struct timezone *tz) __attribute__((alias("fexfn_pack_gettimeofday")));
int __vdso_clock_gettime(clockid_t, struct timespec *) __attribute__((alias("fexfn_pack_clock_gettime")));
int __vdso_clock_getres(clockid_t, struct timespec *) __attribute__((alias("fexfn_pack_clock_getres")));
int __vdso_getcpu(uint32_t *, uint32_t *) __attribute__((alias("fexfn_pack_getcpu")));
}
+52
View File
@@ -0,0 +1,52 @@
SECTIONS {
. = SIZEOF_HEADERS;
.hash : { *(.hash) } :text
.gnu.hash : { *(.gnu.hash) }
.dynsym : { *(.dynsym) }
.dynstr : { *(.dynstr) }
.gnu.version : { *(.gnu.version) }
.gnu.version_d : { *(.gnu.version_d) }
.gnu.version_r : { *(.gnu.version_r) }
.dynamic : { *(.dynamic) } :text :dynamic
.rodata : {
*(.rodata*)
*(.data*)
*(.sdata*)
*(.got.plt) *(.got)
*(.gnu.linkonce.d.*)
*(.bss*)
*(.dynbss*)
*(.gnu.linkonce.b.*)
} :text
/DISCARD/ : {
*(.note)
*(.note.gnu.property)
*(.eh_frame_hdr)
*(.eh_frame)
*(.symtab)
}
}
PHDRS {
text PT_LOAD FLAGS(PF_R | PF_X) FILEHDR PHDRS;
dynamic PT_DYNAMIC FLAGS(PF_R);
note PT_NOTE FLAGS(PF_R);
}
VERSION {
LINUX_2.6 {
global:
__vdso_time;
time;
__vdso_gettimeofday;
gettimeofday;
__vdso_clock_gettime;
clock_gettime;
__vdso_clock_getres;
clock_getres;
__vdso_getcpu;
getcpu;
local: *;
};
}
+15
View File
@@ -0,0 +1,15 @@
#include <common/GeneratorInterface.h>
#include <sched.h>
#include <sys/time.h>
#include <time.h>
template<auto>
struct fex_gen_config {
};
template<> struct fex_gen_config<time> {};
template<> struct fex_gen_config<gettimeofday> {};
template<> struct fex_gen_config<clock_gettime> {};
template<> struct fex_gen_config<clock_getres> {};
template<> struct fex_gen_config<getcpu> {};
+4 -4
View File
@@ -26,10 +26,7 @@ $end_info$
#include "common/Guest.h"
#include <stdarg.h>
#include "thunks.inl"
#include "function_packs.inl"
#include "function_packs_public.inl"
#include "thunkgen_guest_libX11.inl"
// Custom implementations //
@@ -206,7 +203,10 @@ extern "C" {
MakeHostFunctionGuestCallable(ret->resource_alloc);
MakeHostFunctionGuestCallable(ret->idlist_alloc);
#if (X11_VERSION_MAJOR >= 1 && X11_VERSION_MINOR >= 7 && X11_VERSION_PATCH >= 0)
// Doesn't exist on older X11
MakeHostFunctionGuestCallable(ret->exit_handler);
#endif
return ret;
}
+278 -88
View File
@@ -24,117 +24,307 @@ $end_info$
#include <dlfcn.h>
#include <utility>
#include "ldr_ptrs.inl"
#include "thunkgen_host_libX11.inl"
#ifdef _M_ARM_64
// This Variadic asm only works for one signature
// ({uint32_t,uint64_t} a_0, size_t count, uint64_t *list)
//
// Variadic ABI for AArch64 (flat uint64_t):
// Arguments 0-7 is in registers
// 8+ stored on to stack
//
// The X11 functions we are calling need an additional nullptr passed in.
// nullptr will be at the end of the list of generated stack items when called through this.
// We will always generate a variadic frame of `count` objects + 1 for nullptr.
//
// Incoming:
// x0 = XIM
// x1 = count
// x2 = array of 64-bit values
// x3 = Function to call
//
// Outgoing:
// x0: Ptr
__attribute__((naked))
void *libX11_Variadic_u64(uint64_t a_0, size_t count, unsigned long *list, void *Func) {
asm volatile(R"(
# Move our function to x8, which will be unused
mov x8, x3
# Move our list to x9, which will be unused
mov x9, x2
# >6 means use stack callback
cmp x1, 6
b.gt .stack%=
# Setup a jump table
adr x10, .zero%=
adr x11, .jump_table%=
ldrb w11, [x11, x1]
add x10, x10, x11, lsl 2
br x10
.zero%=:
mov x1, 0
br x8
.one%=:
ldr x1, [x9, 0]
mov x2, #0
br x8
.two%=:
ldp x1, x2, [x9, 0]
mov x3, 0
br x8
.three%=:
ldp x1, x2, [x9, 0]
ldr x3, [x9, 16]
mov x4, 0
br x8
.four%=:
ldp x1, x2, [x9, 0]
ldp x3, x4, [x9, 16]
mov x5, 0
br x8
.five%=:
ldp x1, x2, [x9, 0]
ldp x3, x4, [x9, 16]
ldr x5, [x9, 32]
mov x6, 0
br x8
.six%=:
ldp x1, x2, [x9, 0]
ldp x3, x4, [x9, 16]
ldp x5, x6, [x9, 32]
mov x7, 0
br x8
.stack%=:
# Store LR and x28
stp x28, x30, [sp, -16]!
# x8 = <arg ptr>
# x0 = <arg im>
# x1 = <count>
# x9 = <list ptr>
# Stack objects
# Count >= 7
# Subtract 6 count objects
# Leaves us at least 1 (nullptr)
sub x1, x1, 6
# Round up to the nearest pair
and x10, x1, 1
add x10, x10, x1
# Multiply by eight to get the size of stack we need to create
lsl x10, x10, 3
# Allocate stack space
sub sp, sp, x10
# Store how much data we added to the stack in our callee saved register we stole
mov x28, x10
# Subtract one member due to nullptr ender
sub x10, x1, 1
# x11 - stack offset
mov x11, sp
# x12 - load offset
add x12, x9, (7 * 8)
cmp x10, 1
b.eq .single%=
b.lt .no_single%=
.load_pair%=:
ldp x1, x2, [x12], 16
stp x1, x2, [x11], 16
sub x10, x10, 8
cmp x10, 1
b.gt .load_pair%=
b.lt .no_single%=
# One variable at most
.single%=:
ldr x1, [x12]
stp x1, xzr, [x11]
b .top_reg_args%=
.no_single%=:
# Need to store nullptr
str xzr, [x11]
.top_reg_args%=:
ldp x1, x2, [x9, 0]
ldp x3, x4, [x9, 16]
ldp x5, x6, [x9, 32]
ldr x7, [x9, 48]
# Stack is setup going in to this
blr x8
# Move stack back
add sp, sp, x28
ldp x28, x30, [sp], 16
ret
.jump_table%=:
.byte (.zero%= - .zero%=) >> 2
.byte (.one%= - .zero%=) >> 2
.byte (.two%= - .zero%=) >> 2
.byte (.three%= - .zero%=) >> 2
.byte (.four%= - .zero%=) >> 2
.byte (.five%= - .zero%=) >> 2
.byte (.six%= - .zero%=) >> 2
)"
::: "memory"
);
}
#endif
_XIC *fexfn_impl_libX11_XCreateIC_internal(XIM a_0, size_t count, unsigned long *list) {
switch(count) {
case 0: return fexldr_ptr_libX11_XCreateIC(a_0, nullptr); break;
case 1: return fexldr_ptr_libX11_XCreateIC(a_0, list[0], nullptr); break;
case 2: return fexldr_ptr_libX11_XCreateIC(a_0, list[0], list[1], nullptr); break;
case 3: return fexldr_ptr_libX11_XCreateIC(a_0, list[0], list[1], list[2], nullptr); break;
case 4: return fexldr_ptr_libX11_XCreateIC(a_0, list[0], list[1], list[2], list[3], nullptr); break;
case 5: return fexldr_ptr_libX11_XCreateIC(a_0, list[0], list[1], list[2], list[3], list[4], nullptr); break;
case 6: return fexldr_ptr_libX11_XCreateIC(a_0, list[0], list[1], list[2], list[3], list[4], list[5], nullptr); break;
case 7: return fexldr_ptr_libX11_XCreateIC(a_0, list[0], list[1], list[2], list[3], list[4], list[5], list[6], nullptr); break;
default:
fprintf(stderr, "XCreateIC_internal FAILURE\n");
return nullptr;
}
switch(count) {
case 0: return fexldr_ptr_libX11_XCreateIC(a_0, nullptr); break;
case 1: return fexldr_ptr_libX11_XCreateIC(a_0, list[0], nullptr); break;
case 2: return fexldr_ptr_libX11_XCreateIC(a_0, list[0], list[1], nullptr); break;
case 3: return fexldr_ptr_libX11_XCreateIC(a_0, list[0], list[1], list[2], nullptr); break;
case 4: return fexldr_ptr_libX11_XCreateIC(a_0, list[0], list[1], list[2], list[3], nullptr); break;
case 5: return fexldr_ptr_libX11_XCreateIC(a_0, list[0], list[1], list[2], list[3], list[4], nullptr); break;
case 6: return fexldr_ptr_libX11_XCreateIC(a_0, list[0], list[1], list[2], list[3], list[4], list[5], nullptr); break;
case 7: return fexldr_ptr_libX11_XCreateIC(a_0, list[0], list[1], list[2], list[3], list[4], list[5], list[6], nullptr); break;
case 8: return fexldr_ptr_libX11_XCreateIC(a_0, list[0], list[1], list[2], list[3], list[4], list[5], list[6], list[7], nullptr); break;
default:
#ifdef _M_ARM_64
return reinterpret_cast<_XIC*>(libX11_Variadic_u64(reinterpret_cast<uint64_t>(a_0), count, list, reinterpret_cast<void*>(fexldr_ptr_libX11_XCreateIC)));
#else
fprintf(stderr, "XCreateIC_internal FAILURE\n");
return nullptr;
#endif
}
}
char* fexfn_impl_libX11_XGetICValues_internal(XIC a_0, size_t count, unsigned long *list) {
switch(count) {
case 0: return fexldr_ptr_libX11_XGetICValues(a_0, nullptr); break;
case 1: return fexldr_ptr_libX11_XGetICValues(a_0, list[0], nullptr); break;
case 2: return fexldr_ptr_libX11_XGetICValues(a_0, list[0], list[1], nullptr); break;
case 3: return fexldr_ptr_libX11_XGetICValues(a_0, list[0], list[1], list[2], nullptr); break;
case 4: return fexldr_ptr_libX11_XGetICValues(a_0, list[0], list[1], list[2], list[3], nullptr); break;
case 5: return fexldr_ptr_libX11_XGetICValues(a_0, list[0], list[1], list[2], list[3], list[4], nullptr); break;
case 6: return fexldr_ptr_libX11_XGetICValues(a_0, list[0], list[1], list[2], list[3], list[4], list[5], nullptr); break;
case 7: return fexldr_ptr_libX11_XGetICValues(a_0, list[0], list[1], list[2], list[3], list[4], list[5], list[6], nullptr); break;
default:
fprintf(stderr, "XGetICValues_internal FAILURE\n");
abort();
}
switch(count) {
case 0: return fexldr_ptr_libX11_XGetICValues(a_0, nullptr); break;
case 1: return fexldr_ptr_libX11_XGetICValues(a_0, list[0], nullptr); break;
case 2: return fexldr_ptr_libX11_XGetICValues(a_0, list[0], list[1], nullptr); break;
case 3: return fexldr_ptr_libX11_XGetICValues(a_0, list[0], list[1], list[2], nullptr); break;
case 4: return fexldr_ptr_libX11_XGetICValues(a_0, list[0], list[1], list[2], list[3], nullptr); break;
case 5: return fexldr_ptr_libX11_XGetICValues(a_0, list[0], list[1], list[2], list[3], list[4], nullptr); break;
case 6: return fexldr_ptr_libX11_XGetICValues(a_0, list[0], list[1], list[2], list[3], list[4], list[5], nullptr); break;
case 7: return fexldr_ptr_libX11_XGetICValues(a_0, list[0], list[1], list[2], list[3], list[4], list[5], list[6], nullptr); break;
case 8: return fexldr_ptr_libX11_XGetICValues(a_0, list[0], list[1], list[2], list[3], list[4], list[5], list[6], list[7], nullptr); break;
default:
#ifdef _M_ARM_64
return reinterpret_cast<char*>(libX11_Variadic_u64(reinterpret_cast<uint64_t>(a_0), count, list, reinterpret_cast<void*>(fexldr_ptr_libX11_XGetICValues)));
#else
fprintf(stderr, "XGetICValues_internal FAILURE\n");
abort();
#endif
}
}
char* fexfn_impl_libX11_XSetICValues_internal(XIC a_0, size_t count, unsigned long *list) {
switch(count) {
case 0: return fexldr_ptr_libX11_XSetICValues(a_0, nullptr); break;
case 1: return fexldr_ptr_libX11_XSetICValues(a_0, list[0], nullptr); break;
case 2: return fexldr_ptr_libX11_XSetICValues(a_0, list[0], list[1], nullptr); break;
case 3: return fexldr_ptr_libX11_XSetICValues(a_0, list[0], list[1], list[2], nullptr); break;
case 4: return fexldr_ptr_libX11_XSetICValues(a_0, list[0], list[1], list[2], list[3], nullptr); break;
case 5: return fexldr_ptr_libX11_XSetICValues(a_0, list[0], list[1], list[2], list[3], list[4], nullptr); break;
case 6: return fexldr_ptr_libX11_XSetICValues(a_0, list[0], list[1], list[2], list[3], list[4], list[5], nullptr); break;
case 7: return fexldr_ptr_libX11_XSetICValues(a_0, list[0], list[1], list[2], list[3], list[4], list[5], list[6], nullptr); break;
default:
fprintf(stderr, "XSetICValues_internal FAILURE\n");
abort();
}
switch(count) {
case 0: return fexldr_ptr_libX11_XSetICValues(a_0, nullptr); break;
case 1: return fexldr_ptr_libX11_XSetICValues(a_0, list[0], nullptr); break;
case 2: return fexldr_ptr_libX11_XSetICValues(a_0, list[0], list[1], nullptr); break;
case 3: return fexldr_ptr_libX11_XSetICValues(a_0, list[0], list[1], list[2], nullptr); break;
case 4: return fexldr_ptr_libX11_XSetICValues(a_0, list[0], list[1], list[2], list[3], nullptr); break;
case 5: return fexldr_ptr_libX11_XSetICValues(a_0, list[0], list[1], list[2], list[3], list[4], nullptr); break;
case 6: return fexldr_ptr_libX11_XSetICValues(a_0, list[0], list[1], list[2], list[3], list[4], list[5], nullptr); break;
case 7: return fexldr_ptr_libX11_XSetICValues(a_0, list[0], list[1], list[2], list[3], list[4], list[5], list[6], nullptr); break;
case 8: return fexldr_ptr_libX11_XSetICValues(a_0, list[0], list[1], list[2], list[3], list[4], list[5], list[6], list[7], nullptr); break;
default:
#ifdef _M_ARM_64
return reinterpret_cast<char*>(libX11_Variadic_u64(reinterpret_cast<uint64_t>(a_0), count, list, reinterpret_cast<void*>(fexldr_ptr_libX11_XSetICValues)));
#else
fprintf(stderr, "XSetICValues_internal FAILURE\n");
abort();
#endif
}
}
char* fexfn_impl_libX11_XGetIMValues_internal(XIM a_0, size_t count, void **list) {
switch(count) {
case 0: return fexldr_ptr_libX11_XGetIMValues(a_0, nullptr); break;
case 1: return fexldr_ptr_libX11_XGetIMValues(a_0, list[0], nullptr); break;
case 2: return fexldr_ptr_libX11_XGetIMValues(a_0, list[0], list[1], nullptr); break;
case 3: return fexldr_ptr_libX11_XGetIMValues(a_0, list[0], list[1], list[2], nullptr); break;
case 4: return fexldr_ptr_libX11_XGetIMValues(a_0, list[0], list[1], list[2], list[3], nullptr); break;
case 5: return fexldr_ptr_libX11_XGetIMValues(a_0, list[0], list[1], list[2], list[3], list[4], nullptr); break;
case 6: return fexldr_ptr_libX11_XGetIMValues(a_0, list[0], list[1], list[2], list[3], list[4], list[5], nullptr); break;
case 7: return fexldr_ptr_libX11_XGetIMValues(a_0, list[0], list[1], list[2], list[3], list[4], list[5], list[6], nullptr); break;
default:
fprintf(stderr, "XGetIMValues_internal FAILURE\n");
abort();
}
switch(count) {
case 0: return fexldr_ptr_libX11_XGetIMValues(a_0, nullptr); break;
case 1: return fexldr_ptr_libX11_XGetIMValues(a_0, list[0], nullptr); break;
case 2: return fexldr_ptr_libX11_XGetIMValues(a_0, list[0], list[1], nullptr); break;
case 3: return fexldr_ptr_libX11_XGetIMValues(a_0, list[0], list[1], list[2], nullptr); break;
case 4: return fexldr_ptr_libX11_XGetIMValues(a_0, list[0], list[1], list[2], list[3], nullptr); break;
case 5: return fexldr_ptr_libX11_XGetIMValues(a_0, list[0], list[1], list[2], list[3], list[4], nullptr); break;
case 6: return fexldr_ptr_libX11_XGetIMValues(a_0, list[0], list[1], list[2], list[3], list[4], list[5], nullptr); break;
case 7: return fexldr_ptr_libX11_XGetIMValues(a_0, list[0], list[1], list[2], list[3], list[4], list[5], list[6], nullptr); break;
case 8: return fexldr_ptr_libX11_XGetIMValues(a_0, list[0], list[1], list[2], list[3], list[4], list[5], list[6], list[7], nullptr); break;
default:
#ifdef _M_ARM_64
return reinterpret_cast<char*>(libX11_Variadic_u64(reinterpret_cast<uint64_t>(a_0), count, reinterpret_cast<unsigned long *>(list), reinterpret_cast<void*>(fexldr_ptr_libX11_XGetIMValues)));
#else
fprintf(stderr, "XGetIMValues_internal FAILURE\n");
abort();
#endif
}
}
char* fexfn_impl_libX11_XSetIMValues_internal(XIM a_0, size_t count, void **list) {
switch(count) {
case 0: return fexldr_ptr_libX11_XSetIMValues(a_0, nullptr); break;
case 1: return fexldr_ptr_libX11_XSetIMValues(a_0, list[0], nullptr); break;
case 2: return fexldr_ptr_libX11_XSetIMValues(a_0, list[0], list[1], nullptr); break;
case 3: return fexldr_ptr_libX11_XSetIMValues(a_0, list[0], list[1], list[2], nullptr); break;
case 4: return fexldr_ptr_libX11_XSetIMValues(a_0, list[0], list[1], list[2], list[3], nullptr); break;
case 5: return fexldr_ptr_libX11_XSetIMValues(a_0, list[0], list[1], list[2], list[3], list[4], nullptr); break;
case 6: return fexldr_ptr_libX11_XSetIMValues(a_0, list[0], list[1], list[2], list[3], list[4], list[5], nullptr); break;
case 7: return fexldr_ptr_libX11_XSetIMValues(a_0, list[0], list[1], list[2], list[3], list[4], list[5], list[6], nullptr); break;
default:
fprintf(stderr, "XSetIMValues_internal FAILURE\n");
abort();
}
switch(count) {
case 0: return fexldr_ptr_libX11_XSetIMValues(a_0, nullptr); break;
case 1: return fexldr_ptr_libX11_XSetIMValues(a_0, list[0], nullptr); break;
case 2: return fexldr_ptr_libX11_XSetIMValues(a_0, list[0], list[1], nullptr); break;
case 3: return fexldr_ptr_libX11_XSetIMValues(a_0, list[0], list[1], list[2], nullptr); break;
case 4: return fexldr_ptr_libX11_XSetIMValues(a_0, list[0], list[1], list[2], list[3], nullptr); break;
case 5: return fexldr_ptr_libX11_XSetIMValues(a_0, list[0], list[1], list[2], list[3], list[4], nullptr); break;
case 6: return fexldr_ptr_libX11_XSetIMValues(a_0, list[0], list[1], list[2], list[3], list[4], list[5], nullptr); break;
case 7: return fexldr_ptr_libX11_XSetIMValues(a_0, list[0], list[1], list[2], list[3], list[4], list[5], list[6], nullptr); break;
case 8: return fexldr_ptr_libX11_XSetIMValues(a_0, list[0], list[1], list[2], list[3], list[4], list[5], list[6], list[7], nullptr); break;
default:
#ifdef _M_ARM_64
return reinterpret_cast<char*>(libX11_Variadic_u64(reinterpret_cast<uint64_t>(a_0), count, reinterpret_cast<unsigned long *>(list), reinterpret_cast<void*>(fexldr_ptr_libX11_XSetIMValues)));
#else
fprintf(stderr, "XSetIMValues_internal FAILURE\n");
abort();
#endif
}
}
XVaNestedList fexfn_impl_libX11_XVaCreateNestedList_internal(int unused_arg, size_t count, void** list) {
switch(count) {
case 0: return fexldr_ptr_libX11_XVaCreateNestedList(unused_arg, nullptr); break;
case 1: return fexldr_ptr_libX11_XVaCreateNestedList(unused_arg, list[0], nullptr); break;
case 2: return fexldr_ptr_libX11_XVaCreateNestedList(unused_arg, list[0], list[1], nullptr); break;
case 3: return fexldr_ptr_libX11_XVaCreateNestedList(unused_arg, list[0], list[1], list[2], nullptr); break;
case 4: return fexldr_ptr_libX11_XVaCreateNestedList(unused_arg, list[0], list[1], list[2], list[3], nullptr); break;
case 5: return fexldr_ptr_libX11_XVaCreateNestedList(unused_arg, list[0], list[1], list[2], list[3], list[4], nullptr); break;
case 6: return fexldr_ptr_libX11_XVaCreateNestedList(unused_arg, list[0], list[1], list[2], list[3], list[4], list[5], nullptr); break;
case 7: return fexldr_ptr_libX11_XVaCreateNestedList(unused_arg, list[0], list[1], list[2], list[3], list[4], list[5], list[6], nullptr); break;
default:
fprintf(stderr, "XVaCreateNestedList_internal FAILURE\n");
abort();
}
switch(count) {
case 0: return fexldr_ptr_libX11_XVaCreateNestedList(unused_arg, nullptr); break;
case 1: return fexldr_ptr_libX11_XVaCreateNestedList(unused_arg, list[0], nullptr); break;
case 2: return fexldr_ptr_libX11_XVaCreateNestedList(unused_arg, list[0], list[1], nullptr); break;
case 3: return fexldr_ptr_libX11_XVaCreateNestedList(unused_arg, list[0], list[1], list[2], nullptr); break;
case 4: return fexldr_ptr_libX11_XVaCreateNestedList(unused_arg, list[0], list[1], list[2], list[3], nullptr); break;
case 5: return fexldr_ptr_libX11_XVaCreateNestedList(unused_arg, list[0], list[1], list[2], list[3], list[4], nullptr); break;
case 6: return fexldr_ptr_libX11_XVaCreateNestedList(unused_arg, list[0], list[1], list[2], list[3], list[4], list[5], nullptr); break;
case 7: return fexldr_ptr_libX11_XVaCreateNestedList(unused_arg, list[0], list[1], list[2], list[3], list[4], list[5], list[6], nullptr); break;
case 8: return fexldr_ptr_libX11_XVaCreateNestedList(unused_arg, list[0], list[1], list[2], list[3], list[4], list[5], list[6], list[7], nullptr); break;
default:
#ifdef _M_ARM_64
return reinterpret_cast<XVaNestedList>(libX11_Variadic_u64(unused_arg, count, reinterpret_cast<unsigned long *>(list), reinterpret_cast<void*>(fexldr_ptr_libX11_XVaCreateNestedList)));
#else
fprintf(stderr, "XVaCreateNestedList_internal FAILURE\n");
abort();
#endif
}
}
Status fexfn_impl_libX11_XInitThreadsInternal(uintptr_t, uintptr_t);
Status fexfn_impl_libX11__XReply(Display*, xReply*, int, Bool);
#include "function_unpacks.inl"
static ExportEntry exports[] = {
#include "tab_function_unpacks.inl"
{ nullptr, nullptr }
};
#include "ldr.inl"
static int (*ACTUAL_XInitDisplayLock_fn)(Display*) = nullptr;
static int (*INTERNAL_XInitDisplayLock_fn)(Display*) = nullptr;
+56
View File
@@ -472,6 +472,12 @@ template<> struct fex_gen_type<XID(Display*)> {}; // XDisplay::resource_alloc
template<> struct fex_gen_type<void(Display*/*, char*, int*/)> {}; // XDisplay::lock_fns->lock_display
template<> struct fex_gen_type<void(_XDisplay*, XID*, int)> {}; // XDisplay::idlist_alloc
#if !(X11_VERSION_MAJOR >= 1 && X11_VERSION_MINOR >= 7 && X11_VERSION_PATCH >= 0)
// Doesn't exist on older X11
typedef void (*XIOErrorExitHandler)(Display*, void*);
#endif
template<> struct fex_gen_type<std::remove_pointer_t<XIOErrorExitHandler>> {}; // XDisplay::exit_handler
template<> struct fex_gen_config<XOpenDisplay> : fexgen::custom_guest_entrypoint {};
@@ -630,3 +636,53 @@ template<> struct fex_gen_config<_XInitImageFuncPtrs> : fexgen::custom_guest_ent
template<> struct fex_gen_config<XVaCreateNestedList> {
using uniform_va_type = void*;
};
// Xlibint
template<> struct fex_gen_config<_XError> {};
template<> struct fex_gen_config<_XIOError> {};
template<> struct fex_gen_config<_XGetHostname> {};
template<> struct fex_gen_config<_XScreenOfWindow> {};
template<> struct fex_gen_config<_XAsyncErrorHandler> {};
template<> struct fex_gen_config<_XGetAsyncData> {};
template<> struct fex_gen_config<_XEventsQueued> {};
template<> struct fex_gen_config<_XEnq> {};
template<> struct fex_gen_config<_XDeq> {};
template<> struct fex_gen_config<_XUnknownWireEvent> {};
template<> struct fex_gen_config<_XUnknownWireEventCookie> {};
template<> struct fex_gen_config<_XUnknownCopyEventCookie> {};
template<> struct fex_gen_config<_XWireToEvent> {};
template<> struct fex_gen_config<_XDefaultWireError> {};
template<> struct fex_gen_config<_XPollfdCacheInit> {};
template<> struct fex_gen_config<_XPollfdCacheAdd> {};
template<> struct fex_gen_config<_XPollfdCacheDel> {};
template<> struct fex_gen_config<_XAllocID> {};
template<> struct fex_gen_config<_XAllocIDs> {};
template<> struct fex_gen_config<_XFreeExtData> {};
template<> struct fex_gen_config<XESetPrintErrorValues> : fexgen::returns_guest_pointer {};
template<> struct fex_gen_config<XESetWireToError> : fexgen::returns_guest_pointer {};
template<> struct fex_gen_config<XESetBeforeFlush> : fexgen::returns_guest_pointer {};
template<> struct fex_gen_config<_XRegisterInternalConnection> {};
template<> struct fex_gen_config<_XUnregisterInternalConnection> {};
template<> struct fex_gen_config<_XProcessInternalConnection> {};
template<> struct fex_gen_config<_XTextHeight> {};
template<> struct fex_gen_config<_XTextHeight16> {};
template<> struct fex_gen_config<_XEventToWire> {};
template<> struct fex_gen_config<_XF86LoadQueryLocaleFont> {};
template<> struct fex_gen_config<_XProcessWindowAttributes> {};
template<> struct fex_gen_config<_XDefaultError> {};
template<> struct fex_gen_config<_XDefaultIOError> {};
#if !(X11_VERSION_MAJOR >= 1 && X11_VERSION_MINOR >= 7 && X11_VERSION_PATCH >= 0)
// Doesn't exist on older X11
extern void _XDefaultIOErrorExit(Display *dpy, void *user_data);
#endif
template<> struct fex_gen_config<_XDefaultIOErrorExit> {};
template<> struct fex_gen_config<_XSetClipRectangles> {};
template<> struct fex_gen_config<_XGetWindowAttributes> {};
template<> struct fex_gen_config<_XPutBackEvent> {};
template<> struct fex_gen_config<_XFreeEventCookies> {};
template<> struct fex_gen_config<_XStoreEventCookie> {};
template<> struct fex_gen_config<_XFetchEventCookie> {};
template<> struct fex_gen_config<_XCopyEventCookie> {};
template<> struct fex_gen_config<xlocaledir> {};
+1 -4
View File
@@ -39,9 +39,6 @@ extern "C" {
#include "common/Guest.h"
#include "thunks.inl"
#include "function_packs.inl"
#include "function_packs_public.inl"
#include "thunkgen_guest_libXext.inl"
LOAD_LIB(libXext)
+1 -10
View File
@@ -41,15 +41,6 @@ extern "C" {
#include "common/Host.h"
#include <dlfcn.h>
#include "ldr_ptrs.inl"
#include "function_unpacks.inl"
static ExportEntry exports[] = {
#include "tab_function_unpacks.inl"
{ nullptr, nullptr }
};
#include "ldr.inl"
#include "thunkgen_host_libXext.inl"
EXPORTS(libXext)
+7
View File
@@ -172,7 +172,14 @@ template<> struct fex_gen_config<_XF86LoadQueryLocaleFont> {};
template<> struct fex_gen_config<_XProcessWindowAttributes> {};
template<> struct fex_gen_config<_XDefaultError> {};
template<> struct fex_gen_config<_XDefaultIOError> {};
#if !(X11_VERSION_MAJOR >= 1 && X11_VERSION_MINOR >= 7 && X11_VERSION_PATCH >= 0)
// Doesn't exist on older X11
extern void _XDefaultIOErrorExit(Display *dpy, void *user_data);
#endif
template<> struct fex_gen_config<_XDefaultIOErrorExit> {};
template<> struct fex_gen_config<_XSetClipRectangles> {};
template<> struct fex_gen_config<_XGetWindowAttributes> {};
template<> struct fex_gen_config<_XPutBackEvent> {};
+1 -3
View File
@@ -11,8 +11,6 @@ $end_info$
#include "common/Guest.h"
#include "thunks.inl"
#include "function_packs.inl"
#include "function_packs_public.inl"
#include "thunkgen_guest_libXfixes.inl"
LOAD_LIB(libXfixes)
+2 -10
View File
@@ -13,14 +13,6 @@ $end_info$
#include "common/Host.h"
#include <dlfcn.h>
#include "ldr_ptrs.inl"
#include "function_unpacks.inl"
static ExportEntry exports[] = {
#include "tab_function_unpacks.inl"
{ nullptr, nullptr }
};
#include "ldr.inl"
EXPORTS(libXfixes)
#include "thunkgen_host_libXfixes.inl"
EXPORTS(libXfixes)
+1 -3
View File
@@ -12,8 +12,6 @@ $end_info$
#include "common/Guest.h"
#include "thunks.inl"
#include "function_packs.inl"
#include "function_packs_public.inl"
#include "thunkgen_guest_libXrender.inl"
LOAD_LIB(libXrender)
+2 -11
View File
@@ -13,15 +13,6 @@ $end_info$
#include "common/Host.h"
#include <dlfcn.h>
#include "ldr_ptrs.inl"
#include "function_unpacks.inl"
static ExportEntry exports[] = {
#include "tab_function_unpacks.inl"
{ nullptr, nullptr }
};
#include "ldr.inl"
EXPORTS(libXrender)
#include "thunkgen_host_libXrender.inl"
EXPORTS(libXrender)
+1 -3
View File
@@ -16,8 +16,6 @@ extern "C" {
#include "common/Guest.h"
#include <stdarg.h>
#include "thunks.inl"
#include "function_packs.inl"
#include "function_packs_public.inl"
#include "thunkgen_guest_libasound.inl"
LOAD_LIB(libasound)
+2 -11
View File
@@ -11,15 +11,6 @@ $end_info$
#include "common/Host.h"
#include <dlfcn.h>
#include "ldr_ptrs.inl"
#include "function_unpacks.inl"
static ExportEntry exports[] = {
#include "tab_function_unpacks.inl"
{ nullptr, nullptr }
};
#include "ldr.inl"
EXPORTS(libasound)
#include "thunkgen_host_libasound.inl"
EXPORTS(libasound)
@@ -1,6 +1,7 @@
#include <common/GeneratorInterface.h>
#include <alsa/asoundlib.h>
#include <alsa/version.h>
template<auto>
struct fex_gen_config {
@@ -8,6 +9,10 @@ struct fex_gen_config {
};
template<> struct fex_gen_config<snd_asoundlib_version> {};
#if SND_LIB_VERSION < ((1 << 16) | (2 << 8) | (6))
// Exists on 1.2.6
int snd_dlpath(char *path, size_t path_len, const char *name);
#endif
template<> struct fex_gen_config<snd_dlpath> {};
template<> struct fex_gen_config<snd_dlopen> {};
template<> struct fex_gen_config<snd_dlsym> {};
+1 -3
View File
@@ -14,9 +14,7 @@ $end_info$
#include "common/Guest.h"
#include <stdarg.h>
#include "thunks.inl"
#include "function_packs.inl"
#include "function_packs_public.inl"
#include "thunkgen_guest_libdrm.inl"
extern "C" {
void FEX_malloc_free_on_host(void *Ptr) {
+1 -11
View File
@@ -12,7 +12,7 @@ $end_info$
#include <dlfcn.h>
#include <malloc.h>
#include "ldr_ptrs.inl"
#include "thunkgen_host_libdrm.inl"
static size_t fexfn_impl_libdrm_FEX_usable_size(void *a_0){
return malloc_usable_size(a_0);
@@ -22,14 +22,4 @@ static void fexfn_impl_libdrm_FEX_free_on_host(void *a_0){
free(a_0);
}
#include "function_unpacks.inl"
static ExportEntry exports[] = {
#include "tab_function_unpacks.inl"
{ nullptr, nullptr }
};
#include "ldr.inl"
EXPORTS(libdrm)
+1 -3
View File
@@ -14,9 +14,7 @@ $end_info$
#include "Types.h"
#include "thunks.inl"
#include "function_packs.inl"
#include "function_packs_public.inl"
#include "thunkgen_guest_libfex_malloc.inl"
#include <vector>
+1 -10
View File
@@ -15,9 +15,7 @@ $end_info$
#include "Types.h"
#include "ldr_ptrs.inl"
#include "function_unpacks.inl"
#include "thunkgen_host_libfex_malloc.inl"
void fexfn_impl_libfex_malloc_fex_get_allocation_ptrs(AllocationPtrs *Ptrs);
@@ -183,13 +181,6 @@ void *(*__memalign_hook)(size_t alignment, size_t size) =
fex_memalign;
}
static ExportEntry exports[] = {
#include "tab_function_unpacks.inl"
{ nullptr, nullptr }
};
#include "ldr.inl"
void fexfn_impl_libfex_malloc_fex_get_allocation_ptrs(AllocationPtrs *Ptrs) {
*Ptrs = AllocationPointers;
}
+1 -4
View File
@@ -18,10 +18,7 @@ $end_info$
#include <string_view>
#include <unordered_map>
#include "thunks.inl"
#include "function_packs.inl"
#include "function_packs_public.inl"
#include "symbol_list.inl"
#include "thunkgen_guest_libvulkan.inl"
extern "C" {
+1 -11
View File
@@ -18,7 +18,7 @@ $end_info$
#include <dlfcn.h>
#include "ldr_ptrs.inl"
#include "thunkgen_host_libvulkan.inl"
static bool SetupInstance{};
static std::mutex SetupMutex{};
@@ -135,14 +135,4 @@ static PFN_vkVoidFunction FEXFN_IMPL(vkGetInstanceProcAddr)(VkInstance a_0, cons
return ret;
}
#include "function_unpacks.inl"
static ExportEntry exports[] = {
#include "tab_function_unpacks.inl"
{ nullptr, nullptr }
};
#include "ldr.inl"
EXPORTS(libvulkan)
+1 -3
View File
@@ -15,9 +15,7 @@ $end_info$
#include "common/Guest.h"
#include <stdarg.h>
#include "thunks.inl"
#include "function_packs.inl"
#include "function_packs_public.inl"
#include "thunkgen_guest_libxcb-dri2.inl"
extern "C" {
+1 -10
View File
@@ -14,7 +14,7 @@ $end_info$
#include <dlfcn.h>
#include <malloc.h>
#include "ldr_ptrs.inl"
#include "thunkgen_host_libxcb-dri2.inl"
static void fexfn_impl_libxcb_dri2_FEX_xcb_dri2_init_extension(xcb_connection_t * a_0, xcb_extension_t * a_1);
@@ -26,15 +26,6 @@ static void fexfn_impl_libxcb_dri2_FEX_free_on_host(void *a_0){
free(a_0);
}
#include "function_unpacks.inl"
static ExportEntry exports[] = {
#include "tab_function_unpacks.inl"
{ nullptr, nullptr }
};
#include "ldr.inl"
static void fexfn_impl_libxcb_dri2_FEX_xcb_dri2_init_extension(xcb_connection_t * a_0, xcb_extension_t * a_1){
xcb_extension_t *ext{};
if (strcmp(a_1->name, "DRI2") == 0) {
+1 -3
View File
@@ -15,9 +15,7 @@ $end_info$
#include "common/Guest.h"
#include <stdarg.h>
#include "thunks.inl"
#include "function_packs.inl"
#include "function_packs_public.inl"
#include "thunkgen_guest_libxcb-dri3.inl"
extern "C" {
xcb_extension_t xcb_dri3_id = {
+1 -11
View File
@@ -14,7 +14,7 @@ $end_info$
#include <dlfcn.h>
#include <malloc.h>
#include "ldr_ptrs.inl"
#include "thunkgen_host_libxcb-dri3.inl"
static void fexfn_impl_libxcb_dri3_FEX_xcb_dri3_init_extension(xcb_connection_t * a_0, xcb_extension_t * a_1);
@@ -26,16 +26,6 @@ static void fexfn_impl_libxcb_dri3_FEX_free_on_host(void *a_0){
free(a_0);
}
#include "function_unpacks.inl"
static ExportEntry exports[] = {
#include "tab_function_unpacks.inl"
{ nullptr, nullptr }
};
#include "ldr.inl"
static void fexfn_impl_libxcb_dri3_FEX_xcb_dri3_init_extension(xcb_connection_t * a_0, xcb_extension_t * a_1){
xcb_extension_t *ext{};
if (strcmp(a_1->name, "DRI3") == 0) {
+1 -3
View File
@@ -15,9 +15,7 @@ $end_info$
#include "common/Guest.h"
#include <stdarg.h>
#include "thunks.inl"
#include "function_packs.inl"
#include "function_packs_public.inl"
#include "thunkgen_guest_libxcb-glx.inl"
extern "C" {
xcb_extension_t xcb_glx_id = {
Loaded 100 of 181 files, more files were not shown because too many files have changed in this diff. Show more