XFE Git
XFE Studio Git
Git 首页 全局搜索
XFE 主站 文档 NuGet

XFESystem

【Rust】XFE系统,基于Rust和汇编

公开
关注 0 Fork 0 Star 0
UTF-8
// XFESystem-owned implementations of the additional System.Native ABI needed by
// NativeApplication. Declarations derive from Microsoft's MIT-licensed .NET
// 10.0.12 sources; see SystemNativeContracts.h and ThirdParty/DotNetSystemNative.
// This is a RAM-only guest: managed MemoryWorkspace is not a POSIX filesystem.
#include "SystemNativeContracts.h"
#include "../NativePal.h"

extern "C" int* __errno_location();
extern "C" void* malloc(size_t);
extern "C" int snprintf(char*, size_t, const char*, ...);
extern "C" void xfe_runtime_write(const char*, uint64_t);
extern "C" uint64_t xfe_runtime_utc_filetime();
extern "C" [[noreturn]] void xfe_runtime_halt(const char*);

static_assert(sizeof(FileStatus) == 120 && offsetof(FileStatus, UserFlags) == 112, "FileStatus ABI");
static_assert(sizeof(IOVector) == 16 && sizeof(DirectoryEntry) == 16, "I/O vector ABI");
static_assert(sizeof(PollEvent) == 8 && sizeof(WinSize) == 8, "Console ABI");
static_assert(sizeof(ProcessCpuInformation) == 24 && sizeof(Passwd) == 48, "Process ABI");
static_assert(Error_ENOSYS == 0x10037 && Error_EBADF == 0x10008, "PAL errors are not errno values");

namespace {
// The runtime is compiled for the Linux x64 ABI. Raw errno values below are that
// ABI, not the host Windows CRT values. Every PAL value is the official enum.
struct ErrorMapping { int32_t native; Error pal; const char* message; };
constexpr ErrorMapping errors[] = {
    {0,Error_SUCCESS,"Success"}, {7,Error_E2BIG,"Argument list too long"},
    {13,Error_EACCES,"Permission denied"}, {98,Error_EADDRINUSE,"Address already in use"},
    {99,Error_EADDRNOTAVAIL,"Address not available"}, {97,Error_EAFNOSUPPORT,"Address family not supported"},
    {11,Error_EAGAIN,"Resource temporarily unavailable"}, {114,Error_EALREADY,"Operation already in progress"},
    {9,Error_EBADF,"Bad file descriptor"}, {74,Error_EBADMSG,"Bad message"},
    {16,Error_EBUSY,"Device or resource busy"}, {125,Error_ECANCELED,"Operation canceled"},
    {10,Error_ECHILD,"No child processes"}, {103,Error_ECONNABORTED,"Connection aborted"},
    {111,Error_ECONNREFUSED,"Connection refused"}, {104,Error_ECONNRESET,"Connection reset"},
    {35,Error_EDEADLK,"Resource deadlock avoided"}, {89,Error_EDESTADDRREQ,"Destination address required"},
    {33,Error_EDOM,"Numerical argument out of domain"}, {122,Error_EDQUOT,"Disk quota exceeded"},
    {17,Error_EEXIST,"File exists"}, {14,Error_EFAULT,"Bad address"},
    {27,Error_EFBIG,"File too large"}, {113,Error_EHOSTUNREACH,"No route to host"},
    {43,Error_EIDRM,"Identifier removed"}, {84,Error_EILSEQ,"Invalid byte sequence"},
    {115,Error_EINPROGRESS,"Operation in progress"}, {4,Error_EINTR,"Interrupted system call"},
    {22,Error_EINVAL,"Invalid argument"}, {5,Error_EIO,"Input/output error"},
    {106,Error_EISCONN,"Socket is connected"}, {21,Error_EISDIR,"Is a directory"},
    {40,Error_ELOOP,"Too many symbolic links"}, {24,Error_EMFILE,"Too many open files"},
    {31,Error_EMLINK,"Too many links"}, {90,Error_EMSGSIZE,"Message too long"},
    {72,Error_EMULTIHOP,"Multihop attempted"}, {36,Error_ENAMETOOLONG,"File name too long"},
    {100,Error_ENETDOWN,"Network is down"}, {102,Error_ENETRESET,"Network dropped connection"},
    {101,Error_ENETUNREACH,"Network is unreachable"}, {23,Error_ENFILE,"Too many open files in system"},
    {105,Error_ENOBUFS,"No buffer space available"}, {19,Error_ENODEV,"No such device"},
    {2,Error_ENOENT,"No such file or directory"}, {8,Error_ENOEXEC,"Exec format error"},
    {37,Error_ENOLCK,"No locks available"}, {67,Error_ENOLINK,"Link severed"},
    {12,Error_ENOMEM,"Cannot allocate memory"}, {42,Error_ENOMSG,"No message of desired type"},
    {92,Error_ENOPROTOOPT,"Protocol not available"}, {28,Error_ENOSPC,"No space left on device"},
    {38,Error_ENOSYS,"Function not implemented"}, {107,Error_ENOTCONN,"Socket is not connected"},
    {20,Error_ENOTDIR,"Not a directory"}, {39,Error_ENOTEMPTY,"Directory not empty"},
    {131,Error_ENOTRECOVERABLE,"State not recoverable"}, {88,Error_ENOTSOCK,"Not a socket"},
    {95,Error_ENOTSUP,"Operation not supported"}, {25,Error_ENOTTY,"Inappropriate ioctl for device"},
    {6,Error_ENXIO,"No such device or address"}, {75,Error_EOVERFLOW,"Value too large"},
    {130,Error_EOWNERDEAD,"Owner died"}, {1,Error_EPERM,"Operation not permitted"},
    {32,Error_EPIPE,"Broken pipe"}, {71,Error_EPROTO,"Protocol error"},
    {93,Error_EPROTONOSUPPORT,"Protocol not supported"}, {91,Error_EPROTOTYPE,"Protocol wrong type for socket"},
    {34,Error_ERANGE,"Numerical result out of range"}, {30,Error_EROFS,"Read-only file system"},
    {29,Error_ESPIPE,"Illegal seek"}, {3,Error_ESRCH,"No such process"},
    {116,Error_ESTALE,"Stale file handle"}, {110,Error_ETIMEDOUT,"Connection timed out"},
    {26,Error_ETXTBSY,"Text file busy"}, {18,Error_EXDEV,"Cross-device link"},
    {94,Error_ESOCKTNOSUPPORT,"Socket type not supported"}, {96,Error_EPFNOSUPPORT,"Protocol family not supported"},
    {108,Error_ESHUTDOWN,"Socket is shut down"}, {112,Error_EHOSTDOWN,"Host is down"},
    {61,Error_ENODATA,"No data available"},
    {-Error_EHOSTNOTFOUND,Error_EHOSTNOTFOUND,"Name lookup failed"},
    {-Error_ESOCKETERROR,Error_ESOCKETERROR,"Unidentified socket error"}
};
constexpr int32_t badfd = 9, fault = 14, invalid = 22, nosys = 38, missing = 2, notty = 25, seek = 29;
int32_t fail(int32_t code) { *__errno_location() = code; return -1; }
void* fail_pointer(int32_t code) { fail(code); return nullptr; }
template<class T> void clear(T* value) { if(value) *value = {}; }

// Descriptors represent actual serial output endpoints, never invented files.
// Dup allocates an independently closable alias; close cannot disable its source.
// No allocation or device call occurs inside this short scheduler-safe lock.
uint8_t descriptors[128] = {1,2,3}; // stdin, stdout, stderr; stdin has no byte backend.
uint32_t descriptor_lock;
void lock_descriptors() {
    while(__atomic_exchange_n(&descriptor_lock, 1, __ATOMIC_ACQUIRE)) __asm__ volatile("pause");
}
void unlock_descriptors() { __atomic_store_n(&descriptor_lock, 0, __ATOMIC_RELEASE); }
uint8_t endpoint(intptr_t fd) {
    if(fd < 0 || fd >= (intptr_t)sizeof(descriptors)) return 0;
    lock_descriptors(); uint8_t result = descriptors[fd]; unlock_descriptors(); return result;
}
int32_t unsupported_fd(intptr_t fd, int32_t code = nosys) { return fail(endpoint(fd) ? code : badfd); }
int32_t buffer_error(const void* buffer, int32_t count) {
    return count < 0 ? invalid : (!buffer && count ? fault : 0);
}
int32_t path_error(const char* path) {
    if(!path) return fault;
    // '/' is a logical managed working directory, with no native filesystem.
    return path[0] == '/' && path[1] == '\0' ? nosys : missing;
}
int32_t positional_error(intptr_t fd, const void* buffer, int32_t size, int64_t offset) {
    int32_t code = buffer_error(buffer, size);
    if(code) return code;
    if(offset < 0) return invalid;
    return endpoint(fd) ? seek : badfd;
}
int32_t vector_error(intptr_t fd, IOVector* vectors, int32_t count, int64_t offset) {
    if(count < 0 || count > 1024 || offset < 0) return invalid;
    if(count && !vectors) return fault;
    for(int32_t i = 0; i < count; i++) if(vectors[i].Count && !vectors[i].Base) return fault;
    return endpoint(fd) ? seek : badfd;
}
}

extern "C" int32_t SystemNative_ConvertErrorPlatformToPal(int32_t value) {
    for(const auto& item : errors) if(item.native == value) return item.pal;
    return Error_ENONSTANDARD;
}
extern "C" int32_t SystemNative_ConvertErrorPalToPlatform(int32_t value) {
    for(const auto& item : errors) if(item.pal == value) return item.native;
    return -1; // Matches the official release-mode fallback, never maps unknown to success.
}
extern "C" const char* SystemNative_StrErrorR(int32_t code, char* buffer, int32_t size) {
    const char* message = "Unknown error";
    for(const auto& item : errors) if(item.native == code) { message = item.message; break; }
    if(size < 0 || (!buffer && size)) { fail(invalid); return nullptr; }
    if(!buffer) return message; // GNU strerror_r permits returning its static message.
    int32_t i = 0;
    if(size) { while(message[i] && i < size - 1) { buffer[i] = message[i]; i++; } buffer[i] = '\0'; }
    if(message[i]) return nullptr;
    return buffer;
}

extern "C" intptr_t SystemNative_Dup(intptr_t fd) {
    if(fd < 0 || fd >= (intptr_t)sizeof(descriptors)) return fail(badfd);
    lock_descriptors();
    if(!descriptors[fd]) { unlock_descriptors(); return fail(badfd); }
    for(intptr_t next = 3; next < (intptr_t)sizeof(descriptors); next++) {
        if(!descriptors[next]) { descriptors[next] = descriptors[fd]; unlock_descriptors(); return next; }
    }
    unlock_descriptors(); return fail(24); // EMFILE
}
extern "C" int32_t SystemNative_Close(intptr_t fd) {
    if(fd < 0 || fd >= (intptr_t)sizeof(descriptors)) return fail(badfd);
    lock_descriptors(); bool valid = descriptors[fd] != 0; descriptors[fd] = 0; unlock_descriptors();
    return valid ? 0 : fail(badfd);
}
extern "C" int32_t SystemNative_Write(intptr_t fd, const void* buffer, int32_t size) {
    int32_t code = buffer_error(buffer, size); if(code) return fail(code);
    uint8_t kind = endpoint(fd); if(kind != 2 && kind != 3) return fail(badfd);
    const char* data = (const char*)buffer;
    // HAL deliberately bounds each UART request. Splitting preserves all bytes.
    for(int32_t offset = 0; offset < size;) {
        int32_t count = size - offset; if(count > 1048576) count = 1048576;
        xfe_runtime_write(data + offset, (uint64_t)count); offset += count;
    }
    return size;
}
extern "C" int32_t SystemNative_Read(intptr_t fd, void* buffer, int32_t size) {
    int32_t code = buffer_error(buffer, size); if(code) return fail(code);
    if(endpoint(fd) != 1) return fail(badfd);
    return size == 0 ? 0 : fail(nosys); // PS/2 desktop reports are not a POSIX stdin stream.
}
extern "C" int32_t SystemNative_ReadStdin(void* buffer, int32_t size) { return SystemNative_Read(0,buffer,size); }
extern "C" int32_t SystemNative_FStat(intptr_t fd, FileStatus* output) {
    if(!output) return fail(fault); clear(output);
    uint8_t kind = endpoint(fd); if(!kind) return fail(badfd);
    output->Mode = 0x2000; // PAL_S_IFCHR, actual serial character endpoint.
    output->Uid = output->Gid = UINT32_MAX; // This guest has no OS account database.
    output->RDev = kind; return 0;
}
extern "C" int32_t SystemNative_Stat(const char* path, FileStatus* output) {
    if(!output) return fail(fault); clear(output); return fail(path_error(path));
}
extern "C" int32_t SystemNative_LStat(const char* path, FileStatus* output) { return SystemNative_Stat(path,output); }
extern "C" intptr_t SystemNative_Open(const char* path, int32_t flags, int32_t) {
    if(!path) return fail(fault);
    if((flags & ~0x3ff) || (flags & 0xf) > 2) return fail(invalid);
    return fail((flags & (0x20 | 0x80)) || (flags & 0xf) ? nosys : path_error(path));
}
extern "C" int32_t SystemNative_Unlink(const char* path) { return fail(path_error(path)); }
extern "C" DIR* SystemNative_OpenDir(const char* path) { return (DIR*)fail_pointer(path_error(path)); }
extern "C" int32_t SystemNative_CloseDir(DIR*) { return fail(badfd); }
extern "C" int32_t SystemNative_ReadDir(DIR*, DirectoryEntry* entry) {
    if(!entry) return fault;
    clear(entry); return badfd; // Official contract: positive raw errno, not -1+errno.
}
extern "C" int32_t SystemNative_ReadLink(const char* path, char* buffer, int32_t size) {
    int32_t code = buffer_error(buffer,size); return fail(code ? code : path_error(path));
}
extern "C" int32_t SystemNative_FSync(intptr_t fd) { return unsupported_fd(fd); }
extern "C" int32_t SystemNative_FLock(intptr_t fd, int32_t operation) {
    int32_t action = operation & ~4; // PAL_LOCK_NB may accompany SH/EX/UN.
    if((operation & ~15) || (action != 1 && action != 2 && action != 8)) return fail(invalid);
    return unsupported_fd(fd);
}
extern "C" int32_t SystemNative_FTruncate(intptr_t fd, int64_t length) {
    if(length < 0) return fail(invalid); return unsupported_fd(fd);
}
extern "C" int32_t SystemNative_FAllocate(intptr_t fd, int64_t offset, int64_t length) {
    if(offset < 0 || length < 0) return fail(invalid); return unsupported_fd(fd);
}
extern "C" int64_t SystemNative_LSeek(intptr_t fd, int64_t, int32_t whence) {
    if(whence < 0 || whence > 2) return fail(invalid); return unsupported_fd(fd,seek);
}
extern "C" int32_t SystemNative_PRead(intptr_t fd, void* buffer, int32_t size, int64_t offset) {
    return fail(positional_error(fd,buffer,size,offset));
}
extern "C" int32_t SystemNative_PWrite(intptr_t fd, void* buffer, int32_t size, int64_t offset) {
    return fail(positional_error(fd,buffer,size,offset));
}
extern "C" int64_t SystemNative_PReadV(intptr_t fd, IOVector* vectors, int32_t count, int64_t offset) {
    return fail(vector_error(fd,vectors,count,offset));
}
extern "C" int64_t SystemNative_PWriteV(intptr_t fd, IOVector* vectors, int32_t count, int64_t offset) {
    return fail(vector_error(fd,vectors,count,offset));
}
extern "C" int32_t SystemNative_PosixFAdvise(intptr_t fd, int64_t offset, int64_t length, int32_t advice) {
    if(offset < 0 || length < 0 || advice < 0 || advice > 5) return invalid;
    return endpoint(fd) ? nosys : badfd; // This API returns raw errno without changing errno.
}
extern "C" uint32_t SystemNative_GetFileSystemType(intptr_t fd) { unsupported_fd(fd); return 0; }
extern "C" int32_t SystemNative_CanGetHiddenFlag() { return 0; }
extern "C" int32_t SystemNative_LChflagsCanSetHiddenFlag() { return 0; }

extern "C" int32_t SystemNative_Poll(PollEvent* events, uint32_t count, int32_t milliseconds, uint32_t* triggered) {
    // Unlike most APIs here, Poll returns a PAL Error value, not -1 or raw errno.
    if(!triggered || (count && !events)) return Error_EFAULT;
    *triggered = 0;
    if(milliseconds < -1 || count > 128) return Error_EINVAL;
    bool unsupported_read = false;
    for(uint32_t i = 0; i < count; i++) {
        events[i].TriggeredEvents = 0;
        if((uint16_t)events[i].Events & ~0x3fu) return Error_EINVAL;
        if(events[i].FileDescriptor < 0) continue; // poll ignores negative descriptors.
        uint8_t kind = endpoint(events[i].FileDescriptor);
        if(!kind) events[i].TriggeredEvents = 0x20; // PAL_POLLNVAL
        else if(kind == 1 && (events[i].Events & 3)) unsupported_read = true;
        else if(kind != 1 && (events[i].Events & 4)) events[i].TriggeredEvents = 4; // PAL_POLLOUT
        if(events[i].TriggeredEvents) (*triggered)++;
    }
    if(*triggered) return Error_SUCCESS;
    if(unsupported_read || milliseconds < 0) return Error_ENOSYS;
    if(milliseconds) {
        const auto* runtime = XfeRuntimeGetPal();
        if(!runtime || !runtime->sleep_milliseconds) return Error_ENOSYS;
        runtime->sleep_milliseconds((uint32_t)milliseconds);
    }
    return Error_SUCCESS;
}

extern "C" int32_t SystemNative_IsATty(intptr_t fd) { unsupported_fd(fd,notty); return 0; }
extern "C" int32_t SystemNative_GetWindowSize(intptr_t fd, WinSize* size) {
    if(!size) return fail(fault); clear(size); return unsupported_fd(fd,notty);
}
extern "C" int32_t SystemNative_InitializeTerminalAndSignalHandling() { fail(nosys); return 0; }
extern "C" void SystemNative_SetKeypadXmit(intptr_t fd, const char*) { unsupported_fd(fd,notty); }
extern "C" void SystemNative_GetControlCharacters(int32_t* names, uint8_t* values, int32_t count, uint8_t* disabled) {
    if(count < 0) { fail(invalid); return; }
    if(!disabled || (count && (!names || !values))) { fail(fault); return; }
    *disabled = 0; for(int32_t i = 0; i < count; i++) values[i] = 0; fail(notty);
}
extern "C" void SystemNative_InitializeConsoleBeforeRead(uint8_t, uint8_t) { fail(notty); }
extern "C" void SystemNative_UninitializeConsoleAfterRead() { fail(notty); }
extern "C" void SystemNative_UninitializeTerminal() { fail(nosys); }
extern "C" void SystemNative_SetTerminalInvalidationHandler(TerminalInvalidationCallback) { fail(nosys); }

extern "C" void* SystemNative_LoadLibrary(const char* name) { return fail_pointer(name ? nosys : fault); }
extern "C" void* SystemNative_GetProcAddress(void*, const char* name) { return fail_pointer(name ? nosys : fault); }
extern "C" void SystemNative_FreeLibrary(void*) { fail(nosys); }
extern "C" void* SystemNative_GetLoadLibraryError() {
    static const char message[] = "Dynamic loading is unavailable in the RAM-only native guest";
    return (void*)message; // dlerror contract: borrowed static UTF-8 string, not malloc memory.
}
extern "C" char* SystemNative_GetCwd(char* buffer, int32_t size) {
    if(size < 0) return (char*)fail_pointer(invalid);
    if(!buffer) {
        if(size && size < 2) return (char*)fail_pointer(34);
        buffer = (char*)malloc(size ? (size_t)size : 2);
        if(!buffer) return (char*)fail_pointer(12);
    } else if(size < 2) return (char*)fail_pointer(34);
    buffer[0] = '/'; buffer[1] = '\0'; return buffer;
}
extern "C" char* SystemNative_GetProcessPath() {
    static const char path[] = "/System/Kernel.elf";
    char* result = (char*)malloc(sizeof(path));
    if(!result) return (char*)fail_pointer(12);
    for(size_t i = 0; i < sizeof(path); i++) result[i] = path[i];
    return result; // Microsoft callers release this via SystemNative_Free.
}
extern "C" uint32_t SystemNative_GetEUid() { fail(nosys); return UINT32_MAX; }
extern "C" uint32_t SystemNative_GetEGid() { fail(nosys); return UINT32_MAX; }
extern "C" int32_t SystemNative_GetGroups(int32_t count, uint32_t* groups) {
    if(count < 0) return fail(invalid); if(count && !groups) return fail(fault); return fail(nosys);
}
extern "C" int32_t SystemNative_GetPwUidR(uint32_t, Passwd* output, char* buffer, int32_t size) {
    if(!output || (!buffer && size)) return fault;
    clear(output); return size < 0 ? invalid : nosys; // Positive raw errno, not success/no-entry.
}
extern "C" uint32_t SystemNative_InterfaceNameToIndex(char* name) { fail(name ? nosys : fault); return 0; }
extern "C" double SystemNative_GetCpuUtilization(ProcessCpuInformation* previous) {
    fail(previous ? nosys : fault); return 0; // Official failure fallback; this is not an idle measurement.
}
extern "C" int64_t SystemNative_GetSystemTimeAsTicks() {
    constexpr uint64_t unix_epoch_filetime = 116444736000000000ULL;
    uint64_t filetime = xfe_runtime_utc_filetime();
    // DateTime.UtcNow has no error channel here and never checks errno. Returning
    // zero on failure would manufacture 1970. Fail explicitly at this boundary.
    if(!filetime) xfe_runtime_halt("RTC_UNAVAILABLE");
    if(filetime < unix_epoch_filetime) return -(int64_t)(unix_epoch_filetime - filetime);
    uint64_t ticks = filetime - unix_epoch_filetime;
    if(ticks > INT64_MAX) xfe_runtime_halt("RTC_OUT_OF_RANGE");
    return (int64_t)ticks; // Official native ABI is 100ns since 1970, not .NET ticks since year 1.
}
extern "C" int32_t SystemNative_SNPrintF_1S(char* buffer, int32_t size, const char* format, const char* arg) {
    int32_t code = buffer_error(buffer,size); if(code) return fail(code);
    if(!format) return fail(fault); return snprintf(buffer,(size_t)size,format,arg);
}
extern "C" int32_t SystemNative_SNPrintF_1I(char* buffer, int32_t size, const char* format, int32_t arg) {
    int32_t code = buffer_error(buffer,size); if(code) return fail(code);
    if(!format) return fail(fault); return snprintf(buffer,(size_t)size,format,arg);
}