Native code¶
App code can call the console's kernel and system library directly with DllImport; samples\NativeProbe does. Xbox modules export by ordinal only, so name the module and give the ordinal as #<decimal ordinal>, the way the platform's own assemblies import from agcore.dll and npctrl.dll:
using System.Runtime.InteropServices;
static class NativeMethods
{
// xam ordinal 0x282: DWORD XamGetSystemVersion()
[DllImport("xam.xex", EntryPoint = "#642")]
public static extern uint XamGetSystemVersion();
// xboxkrnl ordinal 0x84: VOID KeQuerySystemTime(PLARGE_INTEGER)
[DllImport("xboxkrnl.exe", EntryPoint = "#132")]
public static extern void KeQuerySystemTime(out long fileTime);
}
What the probe found under xenia-edge:
- No special permission needed: plain app code may call these, with no
[SecurityCritical]or other attribute. - Module names:
xam.xex(orxam) andxboxkrnl.exeboth resolve. - Data layout: the process is big-endian PowerPC, so integers,
out/refparameters and pointers are already in the layout the kernel expects. Pointers are 32-bit. - A wrong entry point doesn't throw. A name instead of an ordinal, or a wrong ordinal, becomes a call through a null pointer, and XamlHost quits. Take ordinals from xenia's
src/xenia/kernel/xam/xam_table.incandsrc/xenia/kernel/xboxkrnl/xboxkrnl_table.inc.
Structures and pointers¶
Many kernel and XAM calls take structures, or fill a buffer with structures that point into the same buffer (enumerators, profile settings, achievements). Marshal.AllocHGlobal isn't public on this runtime, but a pinned array is:
byte[] bytes = new byte[0x28];
GCHandle pin = GCHandle.Alloc(bytes, GCHandleType.Pinned);
try
{
uint address = (uint)pin.AddrOfPinnedObject().ToInt32();
// 0x227: DWORD XamUserGetSigninInfo(DWORD userIndex, DWORD flags, XUSER_SIGNIN_INFO*)
if (XamUserGetSigninInfo(0, 0, address) == 0)
{
ulong xuid = (ulong)BigEndian.U32(bytes, 0) << 32 | BigEndian.U32(bytes, 4);
}
}
finally
{
pin.Free();
}
with the pointer parameter declared as uint. What the probes found:
- Addresses are guest addresses.
AddrOfPinnedObjectgives the address the kernel sees, and a pointer the kernel writes into the buffer can be turned back into an offset by subtracting it. - Fields are big-endian, so read and write them byte by byte rather than with
BitConverter. unsafecode works too, with<AllowUnsafeBlocks>true</AllowUnsafeBlocks>:fixed,stackallocand pointer casts touintall run.- 64-bit arguments such as XUIDs pass as
ulong. - Keep arrays pinned only for the call. A pinned array stops the garbage collector from compacting around it.
- A wrong parameter list doesn't throw. A missing parameter shifts the rest, and the native side writes through whatever lands in a pointer's place: under xenia-edge that's a host crash.
XamUserGetXUID, for one, takes a type mask before the XUID pointer. Check signatures against xenia's*_entryfunctions.
samples\Common\NativeBuffer.cs wraps this pattern, with big-endian readers and writers and lookups for the strings and pointers in returned buffers.
How the platform reaches native code¶
The framework itself calls native code in these ways:
agcore.dllby ordinal:System.Windows'MS.Internal.XcpImports, for UI, media, DRM and downloads.npctrl.dllby ordinal:Microsoft.Xboxclasses (VuiManager,Kinect,Companion,MediaTransport,MediaCapabilities,DeviceDiscovery, ...) andSystem.Windows' output protection.- XamlHost extensions by name:
DllImport("Net"|"STORAGE"|"AUDIO"|"MEDIA"|..., EntryPoint = "Name"). XamlHost registers a table of named functions per extension at startup. Gamer services go through one of them,Net!GamerServices_DispatchCommand_Entrypoint, which takes a command number. xadvertising.xexby ordinal: the advertising SDK.- The CLR's internal calls into
xcoreclr.dll.
App code could call the XamlHost extensions the same way, but that hasn't been tried; their signatures have to be read from the decompiled Microsoft.Xbox.* assemblies.
A DLL of the app's own¶
An app can ship native code of its own in its package: a DLL in C, built with
xsdk and called with DllImport("<name>.xex", EntryPoint = "#<ordinal>")
like the system's modules. samples\NativeLibrary does; Samples says
how to build it and what such a DLL needs to load on a console as well as in xenia.