mirror of
https://github.com/switchbrew/nx-hbloader.git
synced 2025-06-21 13:32:40 +02:00
Compare commits
29 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
8f5a425e6e | ||
|
5ac0dfe82d | ||
|
cd6a723acb | ||
|
b38421d04a | ||
|
f6c46269ef | ||
|
b1406d9eaa | ||
|
7aefc7a312 | ||
|
e7ff5cba3b | ||
|
56868b4325 | ||
|
723c09d7ab | ||
|
e2b8f0c5b3 | ||
|
81553d91fd | ||
|
06b2d3bdfd | ||
|
e528d15d5a | ||
|
18019db14a | ||
|
c69c6a9e0d | ||
|
5ce1d33b7d | ||
|
a13d5e7af2 | ||
|
0ce48ebe22 | ||
|
7caa177353 | ||
|
946ac4ede6 | ||
|
4d63750a44 | ||
|
bc48603694 | ||
|
faec3c5098 | ||
|
2288c06401 | ||
|
721fa9b525 | ||
|
19f1afbbf3 | ||
|
d9cb20c248 | ||
|
24487fc496 |
2
Makefile
2
Makefile
@ -43,7 +43,7 @@ SOURCES := source
|
||||
DATA := data
|
||||
INCLUDES := include
|
||||
#ROMFS := romfs
|
||||
APP_VERSION := 2.3.0
|
||||
APP_VERSION := 2.4.4
|
||||
|
||||
ifeq ($(RELEASE),)
|
||||
APP_VERSION := $(APP_VERSION)-$(shell git describe --dirty --always)
|
||||
|
134
hbl.json
134
hbl.json
@ -12,7 +12,10 @@
|
||||
"address_space_type": 1,
|
||||
"is_retail": true,
|
||||
"filesystem_access": {
|
||||
"permissions": "0xFFFFFFFFFFFFFFFF"
|
||||
"permissions": "0xFFFFFFFFFFFFFFFF",
|
||||
"content_owner_ids": [
|
||||
"0x0100000000001000"
|
||||
]
|
||||
},
|
||||
"service_host": [
|
||||
"*"
|
||||
@ -33,7 +36,7 @@
|
||||
{
|
||||
"type": "syscalls",
|
||||
"value": {
|
||||
"svcUnknown00": "0x00",
|
||||
"svcUnassigned00": "0x00",
|
||||
"svcSetHeapSize": "0x01",
|
||||
"svcSetMemoryPermission": "0x02",
|
||||
"svcSetMemoryAttribute": "0x03",
|
||||
@ -79,7 +82,7 @@
|
||||
"svcFlushDataCache": "0x2B",
|
||||
"svcMapPhysicalMemory": "0x2C",
|
||||
"svcUnmapPhysicalMemory": "0x2D",
|
||||
"svcGetFutureThreadInfo": "0x2E",
|
||||
"svcGetDebugFutureThreadInfo": "0x2E",
|
||||
"svcGetLastThreadInfo": "0x2F",
|
||||
"svcGetResourceLimitLimitValue": "0x30",
|
||||
"svcGetResourceLimitCurrentValue": "0x31",
|
||||
@ -87,24 +90,24 @@
|
||||
"svcGetThreadContext3": "0x33",
|
||||
"svcWaitForAddress": "0x34",
|
||||
"svcSignalToAddress": "0x35",
|
||||
"svcUnknown36": "0x36",
|
||||
"svcUnknown37": "0x37",
|
||||
"svcUnknown38": "0x38",
|
||||
"svcUnknown39": "0x39",
|
||||
"svcUnknown3a": "0x3A",
|
||||
"svcUnknown3b": "0x3B",
|
||||
"svcDumpInfo": "0x3C",
|
||||
"svcDumpInfoNew": "0x3D",
|
||||
"svcUnknown3e": "0x3E",
|
||||
"svcUnknown3f": "0x3F",
|
||||
"svcSynchronizePreemptionState": "0x36",
|
||||
"svcGetResourceLimitPeakValue": "0x37",
|
||||
"svcUnassigned38": "0x38",
|
||||
"svcCreateIoPool": "0x39",
|
||||
"svcCreateIoRegion": "0x3A",
|
||||
"svcUnassigned3B": "0x3B",
|
||||
"svcKernelDebug": "0x3C",
|
||||
"svcChangeKernelTraceState": "0x3D",
|
||||
"svcUnassigned3E": "0x3E",
|
||||
"svcUnassigned3F": "0x3F",
|
||||
"svcCreateSession": "0x40",
|
||||
"svcAcceptSession": "0x41",
|
||||
"svcReplyAndReceiveLight": "0x42",
|
||||
"svcReplyAndReceive": "0x43",
|
||||
"svcReplyAndReceiveWithUserBuffer": "0x44",
|
||||
"svcCreateEvent": "0x45",
|
||||
"svcUnknown46": "0x46",
|
||||
"svcUnknown47": "0x47",
|
||||
"svcMapIoRegion": "0x46",
|
||||
"svcUnmapIoRegion": "0x47",
|
||||
"svcMapPhysicalMemoryUnsafe": "0x48",
|
||||
"svcUnmapPhysicalMemoryUnsafe": "0x49",
|
||||
"svcSetUnsafeLimit": "0x4A",
|
||||
@ -116,6 +119,19 @@
|
||||
"svcCreateSharedMemory": "0x50",
|
||||
"svcMapTransferMemory": "0x51",
|
||||
"svcUnmapTransferMemory": "0x52",
|
||||
"svcCreateInterruptEvent": "0x53",
|
||||
"svcQueryPhysicalAddress": "0x54",
|
||||
"svcQueryIoMapping": "0x55",
|
||||
"svcCreateDeviceAddressSpace": "0x56",
|
||||
"svcAttachDeviceAddressSpace": "0x57",
|
||||
"svcDetachDeviceAddressSpace": "0x58",
|
||||
"svcMapDeviceAddressSpaceByForce": "0x59",
|
||||
"svcMapDeviceAddressSpaceAligned": "0x5A",
|
||||
"svcMapDeviceAddressSpace": "0x5B",
|
||||
"svcUnmapDeviceAddressSpace": "0x5C",
|
||||
"svcInvalidateProcessDataCache": "0x5D",
|
||||
"svcStoreProcessDataCache": "0x5E",
|
||||
"svcFlushProcessDataCache": "0x5F",
|
||||
"svcDebugActiveProcess": "0x60",
|
||||
"svcBreakDebugProcess": "0x61",
|
||||
"svcTerminateDebugProcess": "0x62",
|
||||
@ -130,6 +146,10 @@
|
||||
"svcWriteDebugProcessMemory": "0x6B",
|
||||
"svcSetHardwareBreakPoint": "0x6C",
|
||||
"svcGetDebugThreadParam": "0x6D",
|
||||
"svcUnassigned6E": "0x6E",
|
||||
"svcGetSystemInfo": "0x6F",
|
||||
"svcCreatePort": "0x70",
|
||||
"svcManageNamedPort": "0x71",
|
||||
"svcConnectToPort": "0x72",
|
||||
"svcSetProcessMemoryPermission": "0x73",
|
||||
"svcMapProcessMemory": "0x74",
|
||||
@ -137,7 +157,77 @@
|
||||
"svcQueryProcessMemory": "0x76",
|
||||
"svcMapProcessCodeMemory": "0x77",
|
||||
"svcUnmapProcessCodeMemory": "0x78",
|
||||
"svcCallSecureMonitor": "0x7F"
|
||||
"svcCreateProcess": "0x79",
|
||||
"svcStartProcess": "0x7A",
|
||||
"svcTerminateProcess": "0x7B",
|
||||
"svcGetProcessInfo": "0x7C",
|
||||
"svcCreateResourceLimit": "0x7D",
|
||||
"svcSetResourceLimitLimitValue": "0x7E",
|
||||
"svcCallSecureMonitor": "0x7F",
|
||||
"svcUnassigned80": "0x80",
|
||||
"svcUnassigned81": "0x81",
|
||||
"svcUnassigned82": "0x82",
|
||||
"svcUnassigned83": "0x83",
|
||||
"svcUnassigned84": "0x84",
|
||||
"svcUnassigned85": "0x85",
|
||||
"svcUnassigned86": "0x86",
|
||||
"svcUnassigned87": "0x87",
|
||||
"svcUnassigned88": "0x88",
|
||||
"svcUnassigned89": "0x89",
|
||||
"svcUnassigned8A": "0x8A",
|
||||
"svcUnassigned8B": "0x8B",
|
||||
"svcUnassigned8C": "0x8C",
|
||||
"svcUnassigned8D": "0x8D",
|
||||
"svcUnassigned8E": "0x8E",
|
||||
"svcUnassigned8F": "0x8F",
|
||||
"svcMapInsecureMemory": "0x90",
|
||||
"svcUnmapInsecureMemory": "0x91",
|
||||
"svcUnassigned92": "0x92",
|
||||
"svcUnassigned93": "0x93",
|
||||
"svcUnassigned94": "0x94",
|
||||
"svcUnassigned95": "0x95",
|
||||
"svcUnassigned96": "0x96",
|
||||
"svcUnassigned97": "0x97",
|
||||
"svcUnassigned98": "0x98",
|
||||
"svcUnassigned99": "0x99",
|
||||
"svcUnassigned9A": "0x9A",
|
||||
"svcUnassigned9B": "0x9B",
|
||||
"svcUnassigned9C": "0x9C",
|
||||
"svcUnassigned9D": "0x9D",
|
||||
"svcUnassigned9E": "0x9E",
|
||||
"svcUnassigned9F": "0x9F",
|
||||
"svcUnassignedA0": "0xA0",
|
||||
"svcUnassignedA1": "0xA1",
|
||||
"svcUnassignedA2": "0xA2",
|
||||
"svcUnassignedA3": "0xA3",
|
||||
"svcUnassignedA4": "0xA4",
|
||||
"svcUnassignedA5": "0xA5",
|
||||
"svcUnassignedA6": "0xA6",
|
||||
"svcUnassignedA7": "0xA7",
|
||||
"svcUnassignedA8": "0xA8",
|
||||
"svcUnassignedA9": "0xA9",
|
||||
"svcUnassignedAA": "0xAA",
|
||||
"svcUnassignedAB": "0xAB",
|
||||
"svcUnassignedAC": "0xAC",
|
||||
"svcUnassignedAD": "0xAD",
|
||||
"svcUnassignedAE": "0xAE",
|
||||
"svcUnassignedAF": "0xAF",
|
||||
"svcUnassignedB0": "0xB0",
|
||||
"svcUnassignedB1": "0xB1",
|
||||
"svcUnassignedB2": "0xB2",
|
||||
"svcUnassignedB3": "0xB3",
|
||||
"svcUnassignedB4": "0xB4",
|
||||
"svcUnassignedB5": "0xB5",
|
||||
"svcUnassignedB6": "0xB6",
|
||||
"svcUnassignedB7": "0xB7",
|
||||
"svcUnassignedB8": "0xB8",
|
||||
"svcUnassignedB9": "0xB9",
|
||||
"svcUnassignedBA": "0xBA",
|
||||
"svcUnassignedBB": "0xBB",
|
||||
"svcUnassignedBC": "0xBC",
|
||||
"svcUnassignedBD": "0xBD",
|
||||
"svcUnassignedBE": "0xBE",
|
||||
"svcUnassignedBF": "0xBF"
|
||||
}
|
||||
},
|
||||
{
|
||||
@ -155,9 +245,19 @@
|
||||
{
|
||||
"type": "debug_flags",
|
||||
"value": {
|
||||
"allow_debug": true,
|
||||
"allow_debug": false,
|
||||
"force_debug_prod": false,
|
||||
"force_debug": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "map_region",
|
||||
"value": [
|
||||
{
|
||||
"region_type": 1,
|
||||
"is_ro": true
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
277
source/main.c
277
source/main.c
@ -17,9 +17,17 @@ static u64 g_nroSize = 0;
|
||||
static NroHeader g_nroHeader;
|
||||
static bool g_isApplication = 0;
|
||||
|
||||
static NsApplicationControlData g_applicationControlData;
|
||||
static bool g_isAutomaticGameplayRecording = 0;
|
||||
static bool g_smCloseWorkaround = false;
|
||||
static enum {
|
||||
CodeMemoryUnavailable = 0,
|
||||
CodeMemoryForeignProcess = BIT(0),
|
||||
CodeMemorySameProcess = BIT(0) | BIT(1),
|
||||
} g_codeMemoryCapability = CodeMemoryUnavailable;
|
||||
|
||||
static Handle g_procHandle;
|
||||
|
||||
static void* g_heapAddr;
|
||||
static size_t g_heapSize;
|
||||
|
||||
static u64 g_appletHeapSize = 0;
|
||||
static u64 g_appletHeapReservationSize = 0;
|
||||
@ -36,8 +44,7 @@ bool __nx_fsdev_support_cwd = false;
|
||||
// Used by trampoline.s
|
||||
Result g_lastRet = 0;
|
||||
|
||||
extern void* __stack_top;//Defined in libnx.
|
||||
#define STACK_SIZE 0x100000 //Change this if main-thread stack size ever changes.
|
||||
void NX_NORETURN nroEntrypointTrampoline(const ConfigEntry* entries, u64 handle, u64 entrypoint);
|
||||
|
||||
void __libnx_initheap(void)
|
||||
{
|
||||
@ -70,16 +77,24 @@ void __appInit(void)
|
||||
{
|
||||
Result rc;
|
||||
|
||||
// Detect Atmosphère early on. This is required for hosversion logic.
|
||||
// In the future, this check will be replaced by detectMesosphere().
|
||||
Handle dummy;
|
||||
rc = svcConnectToNamedPort(&dummy, "ams");
|
||||
u32 ams_flag = (R_VALUE(rc) != KERNELRESULT(NotFound)) ? BIT(31) : 0;
|
||||
if (R_SUCCEEDED(rc))
|
||||
svcCloseHandle(dummy);
|
||||
|
||||
rc = smInitialize();
|
||||
if (R_FAILED(rc))
|
||||
fatalThrow(MAKERESULT(Module_HomebrewLoader, 1));
|
||||
diagAbortWithResult(MAKERESULT(Module_HomebrewLoader, 1));
|
||||
|
||||
rc = setsysInitialize();
|
||||
if (R_SUCCEEDED(rc)) {
|
||||
SetSysFirmwareVersion fw;
|
||||
rc = setsysGetFirmwareVersion(&fw);
|
||||
if (R_SUCCEEDED(rc))
|
||||
hosversionSet(MAKEHOSVERSION(fw.major, fw.minor, fw.micro));
|
||||
hosversionSet(ams_flag | MAKEHOSVERSION(fw.major, fw.minor, fw.micro));
|
||||
readSetting("applet_heap_size", &g_appletHeapSize, sizeof(g_appletHeapSize));
|
||||
readSetting("applet_heap_reservation_size", &g_appletHeapReservationSize, sizeof(g_appletHeapReservationSize));
|
||||
setsysExit();
|
||||
@ -87,18 +102,15 @@ void __appInit(void)
|
||||
|
||||
rc = fsInitialize();
|
||||
if (R_FAILED(rc))
|
||||
fatalThrow(MAKERESULT(Module_HomebrewLoader, 2));
|
||||
diagAbortWithResult(MAKERESULT(Module_HomebrewLoader, 2));
|
||||
}
|
||||
|
||||
void __wrap_exit(void)
|
||||
{
|
||||
// exit() effectively never gets called, so let's stub it out.
|
||||
fatalThrow(MAKERESULT(Module_HomebrewLoader, 39));
|
||||
diagAbortWithResult(MAKERESULT(Module_HomebrewLoader, 39));
|
||||
}
|
||||
|
||||
static void* g_heapAddr;
|
||||
static size_t g_heapSize;
|
||||
|
||||
static u64 calculateMaxHeapSize(void)
|
||||
{
|
||||
u64 size = 0;
|
||||
@ -138,14 +150,12 @@ static void setupHbHeap(void)
|
||||
Result rc = svcSetHeapSize(&addr, size);
|
||||
|
||||
if (R_FAILED(rc) || addr==NULL)
|
||||
fatalThrow(MAKERESULT(Module_HomebrewLoader, 9));
|
||||
diagAbortWithResult(MAKERESULT(Module_HomebrewLoader, 9));
|
||||
|
||||
g_heapAddr = addr;
|
||||
g_heapSize = size;
|
||||
}
|
||||
|
||||
static Handle g_procHandle;
|
||||
|
||||
static void procHandleReceiveThread(void* arg)
|
||||
{
|
||||
Handle session = (Handle)(uintptr_t)arg;
|
||||
@ -157,56 +167,71 @@ static void procHandleReceiveThread(void* arg)
|
||||
s32 idx = 0;
|
||||
rc = svcReplyAndReceive(&idx, &session, 1, INVALID_HANDLE, UINT64_MAX);
|
||||
if (R_FAILED(rc))
|
||||
fatalThrow(MAKERESULT(Module_HomebrewLoader, 15));
|
||||
diagAbortWithResult(MAKERESULT(Module_HomebrewLoader, 15));
|
||||
|
||||
HipcParsedRequest r = hipcParseRequest(base);
|
||||
if (r.meta.num_copy_handles != 1)
|
||||
fatalThrow(MAKERESULT(Module_HomebrewLoader, 17));
|
||||
diagAbortWithResult(MAKERESULT(Module_HomebrewLoader, 17));
|
||||
|
||||
g_procHandle = r.data.copy_handles[0];
|
||||
svcCloseHandle(session);
|
||||
}
|
||||
|
||||
//Gets the PID of the process with application_type==APPLICATION in the NPDM, then sets g_isApplication if it matches the current PID.
|
||||
static void getIsApplication(void) {
|
||||
Result rc=0;
|
||||
u64 cur_pid=0, app_pid=0;
|
||||
|
||||
g_isApplication = 0;
|
||||
|
||||
rc = svcGetProcessId(&cur_pid, CUR_PROCESS_HANDLE);
|
||||
if (R_FAILED(rc)) return;
|
||||
|
||||
rc = pmshellInitialize();
|
||||
// Sets g_isApplication if the hbloader process is running as an Application.
|
||||
static void getIsApplication(void)
|
||||
{
|
||||
Result rc;
|
||||
|
||||
// Try asking the kernel directly (only works on [9.0.0+] or mesosphère)
|
||||
u64 flag=0;
|
||||
rc = svcGetInfo(&flag, InfoType_IsApplication, CUR_PROCESS_HANDLE, 0);
|
||||
if (R_SUCCEEDED(rc)) {
|
||||
rc = pmshellGetApplicationProcessIdForShell(&app_pid);
|
||||
pmshellExit();
|
||||
g_isApplication = flag!=0;
|
||||
return;
|
||||
}
|
||||
|
||||
if (R_SUCCEEDED(rc) && cur_pid == app_pid) g_isApplication = 1;
|
||||
// Retrieve our process' PID
|
||||
u64 cur_pid=0;
|
||||
rc = svcGetProcessId(&cur_pid, CUR_PROCESS_HANDLE);
|
||||
if (R_FAILED(rc)) diagAbortWithResult(rc); // shouldn't happen
|
||||
|
||||
// Try reading the current application PID through pm:shell - if it matches ours then we are indeed an application
|
||||
rc = pmshellInitialize();
|
||||
if (R_SUCCEEDED(rc)) {
|
||||
u64 app_pid=0;
|
||||
rc = pmshellGetApplicationProcessIdForShell(&app_pid);
|
||||
pmshellExit();
|
||||
|
||||
if (cur_pid == app_pid)
|
||||
g_isApplication = 1;
|
||||
}
|
||||
}
|
||||
|
||||
//Gets the control.nacp for the current title id, and then sets g_isAutomaticGameplayRecording if less memory should be allocated.
|
||||
static void getIsAutomaticGameplayRecording(void) {
|
||||
if (hosversionAtLeast(5,0,0) && g_isApplication) {
|
||||
Result rc=0;
|
||||
u64 cur_tid=0;
|
||||
// Sets g_isAutomaticGameplayRecording if the current program has automatic gameplay recording enabled in its NACP.
|
||||
//Gets the control.nacp for the current program id, and then sets g_isAutomaticGameplayRecording if less memory should be allocated.
|
||||
static void getIsAutomaticGameplayRecording(void)
|
||||
{
|
||||
Result rc;
|
||||
|
||||
rc = svcGetInfo(&cur_tid, InfoType_ProgramId, CUR_PROCESS_HANDLE, 0);
|
||||
if (R_FAILED(rc)) return;
|
||||
// Do nothing if the HOS version predates [4.0.0], or we're not an application.
|
||||
if (hosversionBefore(4,0,0) || !g_isApplication)
|
||||
return;
|
||||
|
||||
g_isAutomaticGameplayRecording = 0;
|
||||
// Retrieve our process' Program ID
|
||||
u64 cur_progid=0;
|
||||
rc = svcGetInfo(&cur_progid, InfoType_ProgramId, CUR_PROCESS_HANDLE, 0);
|
||||
if (R_FAILED(rc)) diagAbortWithResult(rc); // shouldn't happen
|
||||
|
||||
rc = nsInitialize();
|
||||
// Try reading our NACP
|
||||
rc = nsInitialize();
|
||||
if (R_SUCCEEDED(rc)) {
|
||||
NsApplicationControlData data; // note: this is 144KB, which still fits comfortably within the 1MB of stack we have
|
||||
u64 size=0;
|
||||
rc = nsGetApplicationControlData(NsApplicationControlSource_Storage, cur_progid, &data, sizeof(data), &size);
|
||||
nsExit();
|
||||
|
||||
if (R_SUCCEEDED(rc)) {
|
||||
size_t dummy;
|
||||
rc = nsGetApplicationControlData(0x1, cur_tid, &g_applicationControlData, sizeof(g_applicationControlData), &dummy);
|
||||
nsExit();
|
||||
}
|
||||
|
||||
if (R_SUCCEEDED(rc) && g_applicationControlData.nacp.video_capture_mode == 2) g_isAutomaticGameplayRecording = 1;
|
||||
if (R_SUCCEEDED(rc) && data.nacp.video_capture == 2)
|
||||
g_isAutomaticGameplayRecording = 1;
|
||||
}
|
||||
}
|
||||
|
||||
@ -217,16 +242,16 @@ static void getOwnProcessHandle(void)
|
||||
Handle server_handle, client_handle;
|
||||
rc = svcCreateSession(&server_handle, &client_handle, 0, 0);
|
||||
if (R_FAILED(rc))
|
||||
fatalThrow(MAKERESULT(Module_HomebrewLoader, 12));
|
||||
diagAbortWithResult(MAKERESULT(Module_HomebrewLoader, 12));
|
||||
|
||||
Thread t;
|
||||
rc = threadCreate(&t, &procHandleReceiveThread, (void*)(uintptr_t)server_handle, NULL, 0x1000, 0x20, 0);
|
||||
if (R_FAILED(rc))
|
||||
fatalThrow(MAKERESULT(Module_HomebrewLoader, 10));
|
||||
diagAbortWithResult(MAKERESULT(Module_HomebrewLoader, 10));
|
||||
|
||||
rc = threadStart(&t);
|
||||
if (R_FAILED(rc))
|
||||
fatalThrow(MAKERESULT(Module_HomebrewLoader, 13));
|
||||
diagAbortWithResult(MAKERESULT(Module_HomebrewLoader, 13));
|
||||
|
||||
hipcMakeRequestInline(armGetTls(),
|
||||
.num_copy_handles = 1,
|
||||
@ -239,20 +264,56 @@ static void getOwnProcessHandle(void)
|
||||
threadClose(&t);
|
||||
}
|
||||
|
||||
static bool isKernel5xOrLater(void)
|
||||
{
|
||||
u64 dummy = 0;
|
||||
Result rc = svcGetInfo(&dummy, InfoType_UserExceptionContextAddress, INVALID_HANDLE, 0);
|
||||
return R_VALUE(rc) != KERNELRESULT(InvalidEnumValue);
|
||||
}
|
||||
|
||||
static bool isKernel4x(void)
|
||||
{
|
||||
u64 dummy = 0;
|
||||
Result rc = svcGetInfo(&dummy, InfoType_InitialProcessIdRange, INVALID_HANDLE, 0);
|
||||
return R_VALUE(rc) != KERNELRESULT(InvalidEnumValue);
|
||||
}
|
||||
|
||||
static void getCodeMemoryCapability(void)
|
||||
{
|
||||
if (detectMesosphere()) {
|
||||
// Mesosphère allows for same-process code memory usage.
|
||||
g_codeMemoryCapability = CodeMemorySameProcess;
|
||||
} else if (isKernel5xOrLater()) {
|
||||
// On [5.0.0+], the kernel does not allow the creator process of a CodeMemory object
|
||||
// to use svcControlCodeMemory on itself, thus returning InvalidMemoryState (0xD401).
|
||||
// However the kernel can be patched to support same-process usage of CodeMemory.
|
||||
// We can detect that by passing a bad operation and observe if we actually get InvalidEnumValue (0xF001).
|
||||
Handle code;
|
||||
Result rc = svcCreateCodeMemory(&code, g_heapAddr, 0x1000);
|
||||
if (R_SUCCEEDED(rc)) {
|
||||
rc = svcControlCodeMemory(code, (CodeMapOperation)-1, 0, 0x1000, 0);
|
||||
svcCloseHandle(code);
|
||||
|
||||
if (R_VALUE(rc) == KERNELRESULT(InvalidEnumValue))
|
||||
g_codeMemoryCapability = CodeMemorySameProcess;
|
||||
else
|
||||
g_codeMemoryCapability = CodeMemoryForeignProcess;
|
||||
}
|
||||
} else if (isKernel4x()) {
|
||||
// On [4.0.0-4.1.0] there is no such restriction on same-process CodeMemory usage.
|
||||
g_codeMemoryCapability = CodeMemorySameProcess;
|
||||
} else {
|
||||
// This kernel is too old to support CodeMemory syscalls.
|
||||
g_codeMemoryCapability = CodeMemoryUnavailable;
|
||||
}
|
||||
}
|
||||
|
||||
void loadNro(void)
|
||||
{
|
||||
NroHeader* header = NULL;
|
||||
size_t rw_size=0;
|
||||
Result rc=0;
|
||||
|
||||
if (g_smCloseWorkaround) {
|
||||
// For old applications, wait for SM to handle closing the SM session from this process.
|
||||
// If we don't do this, smInitialize will fail once eventually used later.
|
||||
// This is caused by a bug in old versions of libnx that was fixed in commit 68a77ac950.
|
||||
g_smCloseWorkaround = false;
|
||||
svcSleepThread(1000000000);
|
||||
}
|
||||
|
||||
memcpy((u8*)armGetTls() + 0x100, g_savedTls, 0x100);
|
||||
|
||||
if (g_nroSize > 0)
|
||||
@ -262,26 +323,30 @@ void loadNro(void)
|
||||
rw_size = header->segments[2].size + header->bss_size;
|
||||
rw_size = (rw_size+0xFFF) & ~0xFFF;
|
||||
|
||||
svcBreak(BreakReason_NotificationOnlyFlag | BreakReason_PreUnloadDll, g_nroAddr, g_nroSize);
|
||||
|
||||
// .text
|
||||
rc = svcUnmapProcessCodeMemory(
|
||||
g_procHandle, g_nroAddr + header->segments[0].file_off, ((u64) g_heapAddr) + header->segments[0].file_off, header->segments[0].size);
|
||||
|
||||
if (R_FAILED(rc))
|
||||
fatalThrow(MAKERESULT(Module_HomebrewLoader, 24));
|
||||
diagAbortWithResult(MAKERESULT(Module_HomebrewLoader, 24));
|
||||
|
||||
// .rodata
|
||||
rc = svcUnmapProcessCodeMemory(
|
||||
g_procHandle, g_nroAddr + header->segments[1].file_off, ((u64) g_heapAddr) + header->segments[1].file_off, header->segments[1].size);
|
||||
|
||||
if (R_FAILED(rc))
|
||||
fatalThrow(MAKERESULT(Module_HomebrewLoader, 25));
|
||||
diagAbortWithResult(MAKERESULT(Module_HomebrewLoader, 25));
|
||||
|
||||
// .data + .bss
|
||||
rc = svcUnmapProcessCodeMemory(
|
||||
g_procHandle, g_nroAddr + header->segments[2].file_off, ((u64) g_heapAddr) + header->segments[2].file_off, rw_size);
|
||||
|
||||
if (R_FAILED(rc))
|
||||
fatalThrow(MAKERESULT(Module_HomebrewLoader, 26));
|
||||
diagAbortWithResult(MAKERESULT(Module_HomebrewLoader, 26));
|
||||
|
||||
svcBreak(BreakReason_NotificationOnlyFlag | BreakReason_PostUnloadDll, g_nroAddr, g_nroSize);
|
||||
|
||||
g_nroAddr = g_nroSize = 0;
|
||||
}
|
||||
@ -294,6 +359,8 @@ void loadNro(void)
|
||||
|
||||
memcpy(g_argv, g_nextArgv, sizeof g_argv);
|
||||
|
||||
svcBreak(BreakReason_NotificationOnlyFlag | BreakReason_PreLoadDll, (uintptr_t)g_argv, sizeof(g_argv));
|
||||
|
||||
uint8_t *nrobuf = (uint8_t*) g_heapAddr;
|
||||
|
||||
NroStart* start = (NroStart*) (nrobuf + 0);
|
||||
@ -302,27 +369,27 @@ void loadNro(void)
|
||||
|
||||
rc = fsdevMountSdmc();
|
||||
if (R_FAILED(rc))
|
||||
fatalThrow(MAKERESULT(Module_HomebrewLoader, 404));
|
||||
diagAbortWithResult(MAKERESULT(Module_HomebrewLoader, 404));
|
||||
|
||||
int fd = open(g_nextNroPath, O_RDONLY);
|
||||
if (fd < 0)
|
||||
fatalThrow(MAKERESULT(Module_HomebrewLoader, 3));
|
||||
diagAbortWithResult(MAKERESULT(Module_HomebrewLoader, 3));
|
||||
|
||||
// Reset NRO path to load hbmenu by default next time.
|
||||
g_nextNroPath[0] = '\0';
|
||||
|
||||
if (read(fd, start, sizeof(*start)) != sizeof(*start))
|
||||
fatalThrow(MAKERESULT(Module_HomebrewLoader, 4));
|
||||
diagAbortWithResult(MAKERESULT(Module_HomebrewLoader, 4));
|
||||
|
||||
if (read(fd, header, sizeof(*header)) != sizeof(*header))
|
||||
fatalThrow(MAKERESULT(Module_HomebrewLoader, 4));
|
||||
diagAbortWithResult(MAKERESULT(Module_HomebrewLoader, 4));
|
||||
|
||||
if (header->magic != NROHEADER_MAGIC)
|
||||
fatalThrow(MAKERESULT(Module_HomebrewLoader, 5));
|
||||
diagAbortWithResult(MAKERESULT(Module_HomebrewLoader, 5));
|
||||
|
||||
size_t rest_size = header->size - (sizeof(NroStart) + sizeof(NroHeader));
|
||||
if (read(fd, rest, rest_size) != rest_size)
|
||||
fatalThrow(MAKERESULT(Module_HomebrewLoader, 7));
|
||||
diagAbortWithResult(MAKERESULT(Module_HomebrewLoader, 7));
|
||||
|
||||
close(fd);
|
||||
fsdevUnmountAll();
|
||||
@ -333,17 +400,13 @@ void loadNro(void)
|
||||
rw_size = header->segments[2].size + header->bss_size;
|
||||
rw_size = (rw_size+0xFFF) & ~0xFFF;
|
||||
|
||||
bool has_mod0 = false;
|
||||
if (start->mod_offset > 0 && start->mod_offset <= (total_size-0x24)) // Validate MOD0 offset
|
||||
has_mod0 = *(uint32_t*)(nrobuf + start->mod_offset) == 0x30444F4D; // Validate MOD0 header
|
||||
|
||||
int i;
|
||||
for (i=0; i<3; i++)
|
||||
{
|
||||
if (header->segments[i].file_off >= header->size || header->segments[i].size > header->size ||
|
||||
(header->segments[i].file_off + header->segments[i].size) > header->size)
|
||||
{
|
||||
fatalThrow(MAKERESULT(Module_HomebrewLoader, 6));
|
||||
diagAbortWithResult(MAKERESULT(Module_HomebrewLoader, 6));
|
||||
}
|
||||
}
|
||||
|
||||
@ -353,37 +416,35 @@ void loadNro(void)
|
||||
memcpy(&g_nroHeader, header, sizeof(g_nroHeader));
|
||||
header = &g_nroHeader;
|
||||
|
||||
u64 map_addr;
|
||||
|
||||
do {
|
||||
map_addr = randomGet64() & 0xFFFFFF000ull;
|
||||
rc = svcMapProcessCodeMemory(g_procHandle, map_addr, (u64)nrobuf, total_size);
|
||||
|
||||
} while (rc == 0xDC01 || rc == 0xD401);
|
||||
// Map code memory to a new randomized address
|
||||
virtmemLock();
|
||||
void* map_addr = virtmemFindCodeMemory(total_size, 0);
|
||||
rc = svcMapProcessCodeMemory(g_procHandle, (u64)map_addr, (u64)nrobuf, total_size);
|
||||
virtmemUnlock();
|
||||
|
||||
if (R_FAILED(rc))
|
||||
fatalThrow(MAKERESULT(Module_HomebrewLoader, 18));
|
||||
diagAbortWithResult(MAKERESULT(Module_HomebrewLoader, 18));
|
||||
|
||||
// .text
|
||||
rc = svcSetProcessMemoryPermission(
|
||||
g_procHandle, map_addr + header->segments[0].file_off, header->segments[0].size, Perm_R | Perm_X);
|
||||
g_procHandle, (u64)map_addr + header->segments[0].file_off, header->segments[0].size, Perm_R | Perm_X);
|
||||
|
||||
if (R_FAILED(rc))
|
||||
fatalThrow(MAKERESULT(Module_HomebrewLoader, 19));
|
||||
diagAbortWithResult(MAKERESULT(Module_HomebrewLoader, 19));
|
||||
|
||||
// .rodata
|
||||
rc = svcSetProcessMemoryPermission(
|
||||
g_procHandle, map_addr + header->segments[1].file_off, header->segments[1].size, Perm_R);
|
||||
g_procHandle, (u64)map_addr + header->segments[1].file_off, header->segments[1].size, Perm_R);
|
||||
|
||||
if (R_FAILED(rc))
|
||||
fatalThrow(MAKERESULT(Module_HomebrewLoader, 20));
|
||||
diagAbortWithResult(MAKERESULT(Module_HomebrewLoader, 20));
|
||||
|
||||
// .data + .bss
|
||||
rc = svcSetProcessMemoryPermission(
|
||||
g_procHandle, map_addr + header->segments[2].file_off, rw_size, Perm_Rw);
|
||||
g_procHandle, (u64)map_addr + header->segments[2].file_off, rw_size, Perm_Rw);
|
||||
|
||||
if (R_FAILED(rc))
|
||||
fatalThrow(MAKERESULT(Module_HomebrewLoader, 21));
|
||||
diagAbortWithResult(MAKERESULT(Module_HomebrewLoader, 21));
|
||||
|
||||
u64 nro_size = header->segments[2].file_off + rw_size;
|
||||
u64 nro_heap_start = ((u64) g_heapAddr) + nro_size;
|
||||
@ -399,7 +460,8 @@ void loadNro(void)
|
||||
{ EntryType_Argv, 0, {0, 0} },
|
||||
{ EntryType_NextLoadPath, 0, {0, 0} },
|
||||
{ EntryType_LastLoadResult, 0, {0, 0} },
|
||||
{ EntryType_SyscallAvailableHint, 0, {0xffffffffffffffff, 0x9fc1fff0007ffff} },
|
||||
{ EntryType_SyscallAvailableHint, 0, {UINT64_MAX, UINT64_MAX} },
|
||||
{ EntryType_SyscallAvailableHint2, 0, {UINT64_MAX, 0} },
|
||||
{ EntryType_RandomSeed, 0, {0, 0} },
|
||||
{ EntryType_UserIdStorage, 0, {(u64)(uintptr_t)&g_userIdStorage, 0} },
|
||||
{ EntryType_HosVersion, 0, {0, 0} },
|
||||
@ -407,12 +469,23 @@ void loadNro(void)
|
||||
};
|
||||
|
||||
ConfigEntry *entry_AppletType = &entries[2];
|
||||
ConfigEntry *entry_Syscalls = &entries[7];
|
||||
|
||||
if (g_isApplication) {
|
||||
entry_AppletType->Value[0] = AppletType_SystemApplication;
|
||||
entry_AppletType->Value[1] = EnvAppletFlags_ApplicationOverride;
|
||||
}
|
||||
|
||||
if (!(g_codeMemoryCapability & BIT(0))) {
|
||||
// Revoke access to svcCreateCodeMemory if it's not available.
|
||||
entry_Syscalls->Value[0x4B/64] &= ~(1UL << (0x4B%64));
|
||||
}
|
||||
|
||||
if (!(g_codeMemoryCapability & BIT(1))) {
|
||||
// Revoke access to svcControlCodeMemory if it's not available for same-process usage.
|
||||
entry_Syscalls->Value[0x4C/64] &= ~(1UL << (0x4C%64)); // svcControlCodeMemory
|
||||
}
|
||||
|
||||
// MainThreadHandle
|
||||
entries[0].Value[0] = envGetMainThreadHandle();
|
||||
// ProcessHandle
|
||||
@ -421,34 +494,25 @@ void loadNro(void)
|
||||
entries[3].Value[0] = nro_heap_start;
|
||||
entries[3].Value[1] = nro_heap_size;
|
||||
// Argv
|
||||
entries[4].Value[1] = (u64) &g_argv[0];
|
||||
entries[4].Value[1] = (u64)(uintptr_t)&g_argv[0];
|
||||
// NextLoadPath
|
||||
entries[5].Value[0] = (u64) &g_nextNroPath[0];
|
||||
entries[5].Value[1] = (u64) &g_nextArgv[0];
|
||||
entries[5].Value[0] = (u64)(uintptr_t)&g_nextNroPath[0];
|
||||
entries[5].Value[1] = (u64)(uintptr_t)&g_nextArgv[0];
|
||||
// LastLoadResult
|
||||
entries[6].Value[0] = g_lastRet;
|
||||
// RandomSeed
|
||||
entries[8].Value[0] = randomGet64();
|
||||
entries[8].Value[1] = randomGet64();
|
||||
entries[9].Value[0] = randomGet64();
|
||||
entries[9].Value[1] = randomGet64();
|
||||
// HosVersion
|
||||
entries[10].Value[0] = hosversionGet();
|
||||
entries[11].Value[0] = hosversionGet();
|
||||
entries[11].Value[1] = hosversionIsAtmosphere() ? 0x41544d4f53504852UL : 0; // 'ATMOSPHR'
|
||||
|
||||
u64 entrypoint = map_addr;
|
||||
|
||||
g_nroAddr = map_addr;
|
||||
g_nroAddr = (u64)map_addr;
|
||||
g_nroSize = nro_size;
|
||||
|
||||
memset(__stack_top - STACK_SIZE, 0, STACK_SIZE);
|
||||
svcBreak(BreakReason_NotificationOnlyFlag | BreakReason_PostLoadDll, g_nroAddr, g_nroSize);
|
||||
|
||||
if (!has_mod0) {
|
||||
// Apply sm-close workaround to NROs which do not contain a valid MOD0 header.
|
||||
// This heuristic is based on the fact that MOD0 support was added very shortly after
|
||||
// the fix for the sm-close bug (in fact, two commits later).
|
||||
g_smCloseWorkaround = true;
|
||||
}
|
||||
|
||||
extern NORETURN void nroEntrypointTrampoline(u64 entries_ptr, u64 handle, u64 entrypoint);
|
||||
nroEntrypointTrampoline((u64) entries, -1, entrypoint);
|
||||
nroEntrypointTrampoline(&entries[0], -1, g_nroAddr);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
@ -460,8 +524,9 @@ int main(int argc, char **argv)
|
||||
smExit(); // Close SM as we don't need it anymore.
|
||||
setupHbHeap();
|
||||
getOwnProcessHandle();
|
||||
getCodeMemoryCapability();
|
||||
loadNro();
|
||||
|
||||
fatalThrow(MAKERESULT(Module_HomebrewLoader, 8));
|
||||
diagAbortWithResult(MAKERESULT(Module_HomebrewLoader, 8));
|
||||
return 0;
|
||||
}
|
||||
|
@ -1,14 +1,8 @@
|
||||
.section .text.nroEntrypointTrampoline, "ax", %progbits
|
||||
|
||||
.align 2
|
||||
.global nroEntrypointTrampoline
|
||||
.type nroEntrypointTrampoline, %function
|
||||
.align 2
|
||||
|
||||
.global __libnx_exception_entry
|
||||
.type __libnx_exception_entry, %function
|
||||
|
||||
.cfi_startproc
|
||||
|
||||
nroEntrypointTrampoline:
|
||||
|
||||
// Reset stack pointer.
|
||||
@ -34,18 +28,20 @@ nroEntrypointTrampoline:
|
||||
|
||||
.section .text.__libnx_exception_entry, "ax", %progbits
|
||||
.align 2
|
||||
|
||||
.global __libnx_exception_entry
|
||||
.type __libnx_exception_entry, %function
|
||||
.cfi_startproc
|
||||
|
||||
__libnx_exception_entry:
|
||||
|
||||
// Divert execution to the NRO entrypoint (if a NRO is actually loaded).
|
||||
adrp x7, g_nroAddr
|
||||
ldr x7, [x7, #:lo12:g_nroAddr]
|
||||
cbz x7, __libnx_exception_entry_fail
|
||||
cbz x7, .Lfail
|
||||
br x7
|
||||
|
||||
__libnx_exception_entry_fail:
|
||||
mov w0, #0xf801
|
||||
bl svcReturnFromException
|
||||
b .
|
||||
.Lfail:
|
||||
// Otherwise, pass this unhandled exception right back to the kernel.
|
||||
mov w0, #0xf801 // KERNELRESULT(UnhandledUserInterrupt)
|
||||
svc 0x28 // svcReturnFromException
|
||||
|
||||
.cfi_endproc
|
||||
|
Loading…
Reference in New Issue
Block a user