Skip to content

Supporting Apple Silicon #90

@kevzhao2

Description

@kevzhao2

I spent some time debugging over the weekend and figured out the necessary changes for supporting Apple Silicon. I can try to restructure the code a bit and send in a pull request, but I just wanted to write down the things that I encountered.

W^X

Apple requires all pages to be W^X, and requires pages to be mapped in with the MAP_JIT flag.

  1. When applying detours, the relevant pages are already mapped in with the MAP_JIT flag. We just need to do the following:
    • Call pthread_jit_write_protect_np(false) (to enable write and disable execute)
    • Make the changes
    • Call pthread_jit_write_protect_np(true) (to disable write and enable execute)
  2. When creating the trampoline for the JIT hook, we need to mmap in memory with the MAP_JIT flag instead of calling the equivalent of malloc.

The first item has to be done entirely in native code because calling pthread_jit_write_protect_np(false) from managed code causes a bus error -- this is because pthread_jit_write_protect_np controls every page!

libclrjit.dylib

libclrjit.dylib doesn't seem to be one of the modules loaded into the process, which means that the JIT hooks don't work. This is, however, a pretty easy change to make.

Virtual Method Function Pointers

Hooking virtual methods seems to be a bit fraught (with arm64, at least?) because the function pointers that we get with DetourRuntimeNETPlatform.GetFunctionPointer don't seem to related to the entries found in the vtable, and I'm not exactly sure why this isn't the case for amd64...

My quick and dirty solution was to just read the vtable to get the function pointer:

var methodTable = method.DeclaringType.TypeHandle.Value;
var methodDesc = method.MethodHandle.Value;

var m_wSlotNumber = (ushort) Marshal.ReadInt16((IntPtr) (methodDesc.ToInt64() + 4));
var m_wFlags = (ushort) Marshal.ReadInt16((IntPtr) (methodDesc.ToInt64() + 6));
if ((m_wFlags & 0x8000) == 0) {
    m_wSlotNumber &= 0xff;
}

var chunkNumber = m_wSlotNumber / 8;
var chunkOffset = m_wSlotNumber % 8;

var vtablePtr = Marshal.ReadIntPtr((IntPtr) (methodTable.ToInt64() + 0x40 + 8 * chunkNumber));
var vtableSlotPtr = (IntPtr) (vtablePtr.ToInt64() + 8 * chunkOffset);

ptr = Marshal.ReadIntPtr(vtableSlotPtr);

Additionally, because the devirtualization code expects the entries to point to things that look like precode, I had to change the detour code a bit to match.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions