mirror of
https://github.com/switchbrew/nx-hbloader.git
synced 2025-07-01 18:02:14 +02:00
Compare commits
53 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 | ||
|
5d276d1da7 | ||
|
7b085d86f4 | ||
|
9b2a16c731 | ||
|
0d526f9aaa | ||
|
4cd81dcc31 | ||
|
2ddb129338 | ||
|
3510f74c77 | ||
|
b33c5df54b | ||
|
92a14f706e | ||
|
ededd72fbf | ||
|
3364cc0e74 | ||
|
2a0d4a7713 | ||
|
7c1eb6235f | ||
|
7f8fb00864 | ||
|
00942640a5 | ||
|
104e0fb0b3 | ||
|
549adde3ca | ||
|
b45f3162a8 | ||
|
3af8c89024 | ||
|
8717f990ab | ||
|
0556fbbd99 | ||
|
96af83a70e | ||
|
2d7e01f938 | ||
|
bde505a0bf |
93
Makefile
93
Makefile
@ -15,14 +15,39 @@ include $(DEVKITPRO)/libnx/switch_rules
|
|||||||
# SOURCES is a list of directories containing source code
|
# SOURCES is a list of directories containing source code
|
||||||
# DATA is a list of directories containing data files
|
# DATA is a list of directories containing data files
|
||||||
# INCLUDES is a list of directories containing header files
|
# INCLUDES is a list of directories containing header files
|
||||||
# EXEFS_SRC is the optional input directory containing data copied into exefs, if anything this normally should only contain "main.npdm".
|
# ROMFS is the directory containing data to be added to RomFS, relative to the Makefile (Optional)
|
||||||
|
#
|
||||||
|
# NO_ICON: if set to anything, do not use icon.
|
||||||
|
# NO_NACP: if set to anything, no .nacp file is generated.
|
||||||
|
# APP_TITLE is the name of the app stored in the .nacp file (Optional)
|
||||||
|
# APP_AUTHOR is the author of the app stored in the .nacp file (Optional)
|
||||||
|
# APP_VERSION is the version of the app stored in the .nacp file (Optional)
|
||||||
|
# APP_TITLEID is the titleID of the app stored in the .nacp file (Optional)
|
||||||
|
# ICON is the filename of the icon (.jpg), relative to the project folder.
|
||||||
|
# If not set, it attempts to use one of the following (in this order):
|
||||||
|
# - <Project name>.jpg
|
||||||
|
# - icon.jpg
|
||||||
|
# - <libnx folder>/default_icon.jpg
|
||||||
|
#
|
||||||
|
# CONFIG_JSON is the filename of the NPDM config file (.json), relative to the project folder.
|
||||||
|
# If not set, it attempts to use one of the following (in this order):
|
||||||
|
# - <Project name>.json
|
||||||
|
# - config.json
|
||||||
|
# If a JSON file is provided or autodetected, an ExeFS PFS0 (.nsp) is built instead
|
||||||
|
# of a homebrew executable (.nro). This is intended to be used for sysmodules.
|
||||||
|
# NACP building is skipped as well.
|
||||||
#---------------------------------------------------------------------------------
|
#---------------------------------------------------------------------------------
|
||||||
TARGET := hbl
|
TARGET := hbl
|
||||||
BUILD := build
|
BUILD := build
|
||||||
SOURCES := source
|
SOURCES := source
|
||||||
DATA := data
|
DATA := data
|
||||||
INCLUDES := include
|
INCLUDES := include
|
||||||
EXEFS_SRC := exefs_src
|
#ROMFS := romfs
|
||||||
|
APP_VERSION := 2.4.4
|
||||||
|
|
||||||
|
ifeq ($(RELEASE),)
|
||||||
|
APP_VERSION := $(APP_VERSION)-$(shell git describe --dirty --always)
|
||||||
|
endif
|
||||||
|
|
||||||
#---------------------------------------------------------------------------------
|
#---------------------------------------------------------------------------------
|
||||||
# options for code generation
|
# options for code generation
|
||||||
@ -32,12 +57,12 @@ ARCH := -march=armv8-a -mtune=cortex-a57 -mtp=soft -fPIE
|
|||||||
CFLAGS := -g -Wall -O2 -ffunction-sections \
|
CFLAGS := -g -Wall -O2 -ffunction-sections \
|
||||||
$(ARCH) $(DEFINES)
|
$(ARCH) $(DEFINES)
|
||||||
|
|
||||||
CFLAGS += $(INCLUDE) -D__SWITCH__
|
CFLAGS += $(INCLUDE) -D__SWITCH__ -DVERSION=\"v$(APP_VERSION)\"
|
||||||
|
|
||||||
CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions -std=gnu++11
|
CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions
|
||||||
|
|
||||||
ASFLAGS := -g $(ARCH)
|
ASFLAGS := -g $(ARCH)
|
||||||
LDFLAGS = -specs=$(DEVKITPRO)/libnx/switch.specs -g $(ARCH) -Wl,-Map,$(notdir $*.map)
|
LDFLAGS = -specs=$(DEVKITPRO)/libnx/switch.specs -g $(ARCH) -Wl,-wrap,exit -Wl,-Map,$(notdir $*.map)
|
||||||
|
|
||||||
LIBS := -lnx
|
LIBS := -lnx
|
||||||
|
|
||||||
@ -93,8 +118,6 @@ export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \
|
|||||||
|
|
||||||
export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib)
|
export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib)
|
||||||
|
|
||||||
export BUILD_EXEFS_SRC := $(TOPDIR)/$(EXEFS_SRC)
|
|
||||||
|
|
||||||
ifeq ($(strip $(CONFIG_JSON)),)
|
ifeq ($(strip $(CONFIG_JSON)),)
|
||||||
jsons := $(wildcard *.json)
|
jsons := $(wildcard *.json)
|
||||||
ifneq (,$(findstring $(TARGET).json,$(jsons)))
|
ifneq (,$(findstring $(TARGET).json,$(jsons)))
|
||||||
@ -108,6 +131,35 @@ else
|
|||||||
export APP_JSON := $(TOPDIR)/$(CONFIG_JSON)
|
export APP_JSON := $(TOPDIR)/$(CONFIG_JSON)
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
ifeq ($(strip $(ICON)),)
|
||||||
|
icons := $(wildcard *.jpg)
|
||||||
|
ifneq (,$(findstring $(TARGET).jpg,$(icons)))
|
||||||
|
export APP_ICON := $(TOPDIR)/$(TARGET).jpg
|
||||||
|
else
|
||||||
|
ifneq (,$(findstring icon.jpg,$(icons)))
|
||||||
|
export APP_ICON := $(TOPDIR)/icon.jpg
|
||||||
|
endif
|
||||||
|
endif
|
||||||
|
else
|
||||||
|
export APP_ICON := $(TOPDIR)/$(ICON)
|
||||||
|
endif
|
||||||
|
|
||||||
|
ifeq ($(strip $(NO_ICON)),)
|
||||||
|
export NROFLAGS += --icon=$(APP_ICON)
|
||||||
|
endif
|
||||||
|
|
||||||
|
ifeq ($(strip $(NO_NACP)),)
|
||||||
|
export NROFLAGS += --nacp=$(CURDIR)/$(TARGET).nacp
|
||||||
|
endif
|
||||||
|
|
||||||
|
ifneq ($(APP_TITLEID),)
|
||||||
|
export NACPFLAGS += --titleid=$(APP_TITLEID)
|
||||||
|
endif
|
||||||
|
|
||||||
|
ifneq ($(ROMFS),)
|
||||||
|
export NROFLAGS += --romfsdir=$(CURDIR)/$(ROMFS)
|
||||||
|
endif
|
||||||
|
|
||||||
.PHONY: $(BUILD) clean all
|
.PHONY: $(BUILD) clean all
|
||||||
|
|
||||||
#---------------------------------------------------------------------------------
|
#---------------------------------------------------------------------------------
|
||||||
@ -120,7 +172,12 @@ $(BUILD):
|
|||||||
#---------------------------------------------------------------------------------
|
#---------------------------------------------------------------------------------
|
||||||
clean:
|
clean:
|
||||||
@echo clean ...
|
@echo clean ...
|
||||||
@rm -fr $(BUILD) $(TARGET).nsp $(TARGET).npdm $(TARGET).nso $(TARGET).elf
|
ifeq ($(strip $(APP_JSON)),)
|
||||||
|
@rm -fr $(BUILD) $(TARGET).nro $(TARGET).nacp $(TARGET).elf
|
||||||
|
else
|
||||||
|
@rm -fr $(BUILD) $(TARGET).nsp $(TARGET).nso $(TARGET).npdm $(TARGET).elf
|
||||||
|
endif
|
||||||
|
|
||||||
|
|
||||||
#---------------------------------------------------------------------------------
|
#---------------------------------------------------------------------------------
|
||||||
else
|
else
|
||||||
@ -131,16 +188,26 @@ DEPENDS := $(OFILES:.o=.d)
|
|||||||
#---------------------------------------------------------------------------------
|
#---------------------------------------------------------------------------------
|
||||||
# main targets
|
# main targets
|
||||||
#---------------------------------------------------------------------------------
|
#---------------------------------------------------------------------------------
|
||||||
all : $(OUTPUT).nsp
|
|
||||||
|
|
||||||
ifeq ($(strip $(APP_JSON)),)
|
ifeq ($(strip $(APP_JSON)),)
|
||||||
$(OUTPUT).nsp : $(OUTPUT).nso
|
|
||||||
|
all : $(OUTPUT).nro
|
||||||
|
|
||||||
|
ifeq ($(strip $(NO_NACP)),)
|
||||||
|
$(OUTPUT).nro : $(OUTPUT).elf $(OUTPUT).nacp
|
||||||
else
|
else
|
||||||
$(OUTPUT).nsp : $(OUTPUT).nso $(OUTPUT).npdm
|
$(OUTPUT).nro : $(OUTPUT).elf
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
else
|
||||||
|
|
||||||
|
all : $(OUTPUT).nsp
|
||||||
|
|
||||||
|
$(OUTPUT).nsp : $(OUTPUT).nso $(OUTPUT).npdm
|
||||||
|
|
||||||
$(OUTPUT).nso : $(OUTPUT).elf
|
$(OUTPUT).nso : $(OUTPUT).elf
|
||||||
|
|
||||||
|
endif
|
||||||
|
|
||||||
$(OUTPUT).elf : $(OFILES)
|
$(OUTPUT).elf : $(OFILES)
|
||||||
|
|
||||||
$(OFILES_SRC) : $(HFILES_BIN)
|
$(OFILES_SRC) : $(HFILES_BIN)
|
||||||
@ -148,7 +215,7 @@ $(OFILES_SRC) : $(HFILES_BIN)
|
|||||||
#---------------------------------------------------------------------------------
|
#---------------------------------------------------------------------------------
|
||||||
# you need a rule like this for each extension you use as binary data
|
# you need a rule like this for each extension you use as binary data
|
||||||
#---------------------------------------------------------------------------------
|
#---------------------------------------------------------------------------------
|
||||||
%.bin.o : %.bin
|
%.bin.o %_bin.h : %.bin
|
||||||
#---------------------------------------------------------------------------------
|
#---------------------------------------------------------------------------------
|
||||||
@echo $(notdir $<)
|
@echo $(notdir $<)
|
||||||
@$(bin2o)
|
@$(bin2o)
|
||||||
|
395
hbl.json
395
hbl.json
@ -1,142 +1,263 @@
|
|||||||
{
|
{
|
||||||
"name" : "hbloader",
|
"name": "hbloader",
|
||||||
"title_id" : "0x010000000000100D",
|
"title_id": "0x010000000000100D",
|
||||||
"title_id_range_min" : "0x010000000000100D",
|
"title_id_range_min": "0x010000000000100D",
|
||||||
"title_id_range_max" : "0x010000000000100D",
|
"title_id_range_max": "0x010000000000100D",
|
||||||
"main_thread_stack_size" : "0x100000",
|
"main_thread_stack_size": "0x100000",
|
||||||
"main_thread_priority" : 44,
|
"main_thread_priority": 44,
|
||||||
"default_cpu_id" : 0,
|
"default_cpu_id": 0,
|
||||||
"process_category" : 0,
|
"process_category": 0,
|
||||||
"pool_partition" : 0,
|
"pool_partition": 0,
|
||||||
"is_64_bit" : true,
|
"is_64_bit": true,
|
||||||
"address_space_type" : 1,
|
"address_space_type": 1,
|
||||||
"is_retail" : true,
|
"is_retail": true,
|
||||||
"filesystem_access" : {
|
"filesystem_access": {
|
||||||
"permissions" : "0xFFFFFFFFFFFFFFFF"
|
"permissions": "0xFFFFFFFFFFFFFFFF",
|
||||||
|
"content_owner_ids": [
|
||||||
|
"0x0100000000001000"
|
||||||
|
]
|
||||||
},
|
},
|
||||||
"service_access" : {
|
"service_host": [
|
||||||
"*" : false,
|
"*"
|
||||||
"*" : true
|
],
|
||||||
},
|
"service_access": [
|
||||||
"kernel_capabilities" : {
|
"*"
|
||||||
"kernel_flags" : {
|
],
|
||||||
"highest_thread_priority" : 59,
|
"kernel_capabilities": [
|
||||||
"lowest_thread_priority" : 28,
|
{
|
||||||
"highest_cpu_id" : 2,
|
"type": "kernel_flags",
|
||||||
"lowest_cpu_id" : 0
|
"value": {
|
||||||
},
|
"highest_thread_priority": 59,
|
||||||
"syscalls" : {
|
"lowest_thread_priority": 28,
|
||||||
"svcUnknown00" : "0x00",
|
"highest_cpu_id": 2,
|
||||||
"svcSetHeapSize" : "0x01",
|
"lowest_cpu_id": 0
|
||||||
"svcSetMemoryPermission" : "0x02",
|
|
||||||
"svcSetMemoryAttribute" : "0x03",
|
|
||||||
"svcMapMemory" : "0x04",
|
|
||||||
"svcUnmapMemory" : "0x05",
|
|
||||||
"svcQueryMemory" : "0x06",
|
|
||||||
"svcExitProcess" : "0x07",
|
|
||||||
"svcCreateThread" : "0x08",
|
|
||||||
"svcStartThread" : "0x09",
|
|
||||||
"svcExitThread" : "0x0A",
|
|
||||||
"svcSleepThread" : "0x0B",
|
|
||||||
"svcGetThreadPriority" : "0x0C",
|
|
||||||
"svcSetThreadPriority" : "0x0D",
|
|
||||||
"svcGetThreadCoreMask" : "0x0E",
|
|
||||||
"svcSetThreadCoreMask" : "0x0F",
|
|
||||||
"svcGetCurrentProcessorNumber" : "0x10",
|
|
||||||
"svcSignalEvent" : "0x11",
|
|
||||||
"svcClearEvent" : "0x12",
|
|
||||||
"svcMapSharedMemory" : "0x13",
|
|
||||||
"svcUnmapSharedMemory" : "0x14",
|
|
||||||
"svcCreateTransferMemory" : "0x15",
|
|
||||||
"svcCloseHandle" : "0x16",
|
|
||||||
"svcResetSignal" : "0x17",
|
|
||||||
"svcWaitSynchronization" : "0x18",
|
|
||||||
"svcCancelSynchronization" : "0x19",
|
|
||||||
"svcArbitrateLock" : "0x1A",
|
|
||||||
"svcArbitrateUnlock" : "0x1B",
|
|
||||||
"svcWaitProcessWideKeyAtomic" : "0x1C",
|
|
||||||
"svcSignalProcessWideKey" : "0x1D",
|
|
||||||
"svcGetSystemTick" : "0x1E",
|
|
||||||
"svcConnectToNamedPort" : "0x1F",
|
|
||||||
"svcSendSyncRequestLight" : "0x20",
|
|
||||||
"svcSendSyncRequest" : "0x21",
|
|
||||||
"svcSendSyncRequestWithUserBuffer" : "0x22",
|
|
||||||
"svcSendAsyncRequestWithUserBuffer" : "0x23",
|
|
||||||
"svcGetProcessId" : "0x24",
|
|
||||||
"svcGetThreadId" : "0x25",
|
|
||||||
"svcBreak" : "0x26",
|
|
||||||
"svcOutputDebugString" : "0x27",
|
|
||||||
"svcReturnFromException" : "0x28",
|
|
||||||
"svcGetInfo" : "0x29",
|
|
||||||
"svcFlushEntireDataCache" : "0x2A",
|
|
||||||
"svcFlushDataCache" : "0x2B",
|
|
||||||
"svcMapPhysicalMemory" : "0x2C",
|
|
||||||
"svcUnmapPhysicalMemory" : "0x2D",
|
|
||||||
"svcGetFutureThreadInfo" : "0x2E",
|
|
||||||
"svcGetLastThreadInfo" : "0x2F",
|
|
||||||
"svcGetResourceLimitLimitValue" : "0x30",
|
|
||||||
"svcGetResourceLimitCurrentValue" : "0x31",
|
|
||||||
"svcSetThreadActivity" : "0x32",
|
|
||||||
"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",
|
|
||||||
"svcCreateSession" : "0x40",
|
|
||||||
"svcAcceptSession" : "0x41",
|
|
||||||
"svcReplyAndReceiveLight" : "0x42",
|
|
||||||
"svcReplyAndReceive" : "0x43",
|
|
||||||
"svcReplyAndReceiveWithUserBuffer" : "0x44",
|
|
||||||
"svcCreateEvent" : "0x45",
|
|
||||||
"svcUnknown46" : "0x46",
|
|
||||||
"svcUnknown47" : "0x47",
|
|
||||||
"svcMapPhysicalMemoryUnsafe" : "0x48",
|
|
||||||
"svcUnmapPhysicalMemoryUnsafe" : "0x49",
|
|
||||||
"svcSetUnsafeLimit" : "0x4A",
|
|
||||||
"svcCreateCodeMemory" : "0x4B",
|
|
||||||
"svcControlCodeMemory" : "0x4C",
|
|
||||||
"svcSleepSystem" : "0x4D",
|
|
||||||
"svcReadWriteRegister" : "0x4E",
|
|
||||||
"svcSetProcessActivity" : "0x4F",
|
|
||||||
"svcCreateSharedMemory" : "0x50",
|
|
||||||
"svcMapTransferMemory" : "0x51",
|
|
||||||
"svcUnmapTransferMemory" : "0x52",
|
|
||||||
"svcDebugActiveProcess" : "0x60",
|
|
||||||
"svcBreakDebugProcess" : "0x61",
|
|
||||||
"svcTerminateDebugProcess" : "0x62",
|
|
||||||
"svcGetDebugEvent" : "0x63",
|
|
||||||
"svcContinueDebugEvent" : "0x64",
|
|
||||||
"svcGetProcessList" : "0x65",
|
|
||||||
"svcGetThreadList" : "0x66",
|
|
||||||
"svcGetDebugThreadContext" : "0x67",
|
|
||||||
"svcSetDebugThreadContext" : "0x68",
|
|
||||||
"svcQueryDebugProcessMemory" : "0x69",
|
|
||||||
"svcReadDebugProcessMemory" : "0x6A",
|
|
||||||
"svcWriteDebugProcessMemory" : "0x6B",
|
|
||||||
"svcSetHardwareBreakPoint" : "0x6C",
|
|
||||||
"svcGetDebugThreadParam" : "0x6D",
|
|
||||||
"svcConnectToPort" : "0x72",
|
|
||||||
"svcSetProcessMemoryPermission" : "0x73",
|
|
||||||
"svcMapProcessMemory" : "0x74",
|
|
||||||
"svcUnmapProcessMemory" : "0x75",
|
|
||||||
"svcQueryProcessMemory" : "0x76",
|
|
||||||
"svcMapProcessCodeMemory" : "0x77",
|
|
||||||
"svcUnmapProcessCodeMemory" : "0x78"
|
|
||||||
},
|
|
||||||
"application_type" : 2,
|
|
||||||
"min_kernel_version" : "0x30",
|
|
||||||
"handle_table_size" : 512,
|
|
||||||
"debug_flags" : {
|
|
||||||
"allow_debug" : true,
|
|
||||||
"force_debug" : true
|
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "syscalls",
|
||||||
|
"value": {
|
||||||
|
"svcUnassigned00": "0x00",
|
||||||
|
"svcSetHeapSize": "0x01",
|
||||||
|
"svcSetMemoryPermission": "0x02",
|
||||||
|
"svcSetMemoryAttribute": "0x03",
|
||||||
|
"svcMapMemory": "0x04",
|
||||||
|
"svcUnmapMemory": "0x05",
|
||||||
|
"svcQueryMemory": "0x06",
|
||||||
|
"svcExitProcess": "0x07",
|
||||||
|
"svcCreateThread": "0x08",
|
||||||
|
"svcStartThread": "0x09",
|
||||||
|
"svcExitThread": "0x0A",
|
||||||
|
"svcSleepThread": "0x0B",
|
||||||
|
"svcGetThreadPriority": "0x0C",
|
||||||
|
"svcSetThreadPriority": "0x0D",
|
||||||
|
"svcGetThreadCoreMask": "0x0E",
|
||||||
|
"svcSetThreadCoreMask": "0x0F",
|
||||||
|
"svcGetCurrentProcessorNumber": "0x10",
|
||||||
|
"svcSignalEvent": "0x11",
|
||||||
|
"svcClearEvent": "0x12",
|
||||||
|
"svcMapSharedMemory": "0x13",
|
||||||
|
"svcUnmapSharedMemory": "0x14",
|
||||||
|
"svcCreateTransferMemory": "0x15",
|
||||||
|
"svcCloseHandle": "0x16",
|
||||||
|
"svcResetSignal": "0x17",
|
||||||
|
"svcWaitSynchronization": "0x18",
|
||||||
|
"svcCancelSynchronization": "0x19",
|
||||||
|
"svcArbitrateLock": "0x1A",
|
||||||
|
"svcArbitrateUnlock": "0x1B",
|
||||||
|
"svcWaitProcessWideKeyAtomic": "0x1C",
|
||||||
|
"svcSignalProcessWideKey": "0x1D",
|
||||||
|
"svcGetSystemTick": "0x1E",
|
||||||
|
"svcConnectToNamedPort": "0x1F",
|
||||||
|
"svcSendSyncRequestLight": "0x20",
|
||||||
|
"svcSendSyncRequest": "0x21",
|
||||||
|
"svcSendSyncRequestWithUserBuffer": "0x22",
|
||||||
|
"svcSendAsyncRequestWithUserBuffer": "0x23",
|
||||||
|
"svcGetProcessId": "0x24",
|
||||||
|
"svcGetThreadId": "0x25",
|
||||||
|
"svcBreak": "0x26",
|
||||||
|
"svcOutputDebugString": "0x27",
|
||||||
|
"svcReturnFromException": "0x28",
|
||||||
|
"svcGetInfo": "0x29",
|
||||||
|
"svcFlushEntireDataCache": "0x2A",
|
||||||
|
"svcFlushDataCache": "0x2B",
|
||||||
|
"svcMapPhysicalMemory": "0x2C",
|
||||||
|
"svcUnmapPhysicalMemory": "0x2D",
|
||||||
|
"svcGetDebugFutureThreadInfo": "0x2E",
|
||||||
|
"svcGetLastThreadInfo": "0x2F",
|
||||||
|
"svcGetResourceLimitLimitValue": "0x30",
|
||||||
|
"svcGetResourceLimitCurrentValue": "0x31",
|
||||||
|
"svcSetThreadActivity": "0x32",
|
||||||
|
"svcGetThreadContext3": "0x33",
|
||||||
|
"svcWaitForAddress": "0x34",
|
||||||
|
"svcSignalToAddress": "0x35",
|
||||||
|
"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",
|
||||||
|
"svcMapIoRegion": "0x46",
|
||||||
|
"svcUnmapIoRegion": "0x47",
|
||||||
|
"svcMapPhysicalMemoryUnsafe": "0x48",
|
||||||
|
"svcUnmapPhysicalMemoryUnsafe": "0x49",
|
||||||
|
"svcSetUnsafeLimit": "0x4A",
|
||||||
|
"svcCreateCodeMemory": "0x4B",
|
||||||
|
"svcControlCodeMemory": "0x4C",
|
||||||
|
"svcSleepSystem": "0x4D",
|
||||||
|
"svcReadWriteRegister": "0x4E",
|
||||||
|
"svcSetProcessActivity": "0x4F",
|
||||||
|
"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",
|
||||||
|
"svcGetDebugEvent": "0x63",
|
||||||
|
"svcContinueDebugEvent": "0x64",
|
||||||
|
"svcGetProcessList": "0x65",
|
||||||
|
"svcGetThreadList": "0x66",
|
||||||
|
"svcGetDebugThreadContext": "0x67",
|
||||||
|
"svcSetDebugThreadContext": "0x68",
|
||||||
|
"svcQueryDebugProcessMemory": "0x69",
|
||||||
|
"svcReadDebugProcessMemory": "0x6A",
|
||||||
|
"svcWriteDebugProcessMemory": "0x6B",
|
||||||
|
"svcSetHardwareBreakPoint": "0x6C",
|
||||||
|
"svcGetDebugThreadParam": "0x6D",
|
||||||
|
"svcUnassigned6E": "0x6E",
|
||||||
|
"svcGetSystemInfo": "0x6F",
|
||||||
|
"svcCreatePort": "0x70",
|
||||||
|
"svcManageNamedPort": "0x71",
|
||||||
|
"svcConnectToPort": "0x72",
|
||||||
|
"svcSetProcessMemoryPermission": "0x73",
|
||||||
|
"svcMapProcessMemory": "0x74",
|
||||||
|
"svcUnmapProcessMemory": "0x75",
|
||||||
|
"svcQueryProcessMemory": "0x76",
|
||||||
|
"svcMapProcessCodeMemory": "0x77",
|
||||||
|
"svcUnmapProcessCodeMemory": "0x78",
|
||||||
|
"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"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "application_type",
|
||||||
|
"value": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "min_kernel_version",
|
||||||
|
"value": "0x30"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "handle_table_size",
|
||||||
|
"value": 512
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "debug_flags",
|
||||||
|
"value": {
|
||||||
|
"allow_debug": false,
|
||||||
|
"force_debug_prod": false,
|
||||||
|
"force_debug": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "map_region",
|
||||||
|
"value": [
|
||||||
|
{
|
||||||
|
"region_type": 1,
|
||||||
|
"is_ro": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
449
source/main.c
449
source/main.c
@ -1,30 +1,54 @@
|
|||||||
#include <switch.h>
|
#include <switch.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <stdio.h>
|
#include <fcntl.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
#define MODULE_HBL 347
|
#define DEFAULT_NRO "sdmc:/hbmenu.nro"
|
||||||
|
|
||||||
const char* g_easterEgg = "Do you mean to tell me that you're thinking seriously of building that way, when and if you are an architect?";
|
const char g_noticeText[] =
|
||||||
|
"nx-hbloader " VERSION "\0"
|
||||||
|
"Do you mean to tell me that you're thinking seriously of building that way, when and if you are an architect?";
|
||||||
|
|
||||||
static char g_argv[2048];
|
static char g_argv[2048];
|
||||||
static char g_nextArgv[2048];
|
static char g_nextArgv[2048];
|
||||||
static char g_nextNroPath[512];
|
static char g_nextNroPath[512];
|
||||||
static u64 g_nroAddr = 0;
|
u64 g_nroAddr = 0;
|
||||||
static u64 g_nroSize = 0;
|
static u64 g_nroSize = 0;
|
||||||
static NroHeader g_nroHeader;
|
static NroHeader g_nroHeader;
|
||||||
static bool g_isApplication = 0;
|
static bool g_isApplication = 0;
|
||||||
|
|
||||||
|
static bool g_isAutomaticGameplayRecording = 0;
|
||||||
|
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;
|
||||||
|
|
||||||
|
static u128 g_userIdStorage;
|
||||||
|
|
||||||
static u8 g_savedTls[0x100];
|
static u8 g_savedTls[0x100];
|
||||||
|
|
||||||
|
// Minimize fs resource usage
|
||||||
|
u32 __nx_fs_num_sessions = 1;
|
||||||
|
u32 __nx_fsdev_direntry_cache_size = 1;
|
||||||
|
bool __nx_fsdev_support_cwd = false;
|
||||||
|
|
||||||
// Used by trampoline.s
|
// Used by trampoline.s
|
||||||
Result g_lastRet = 0;
|
Result g_lastRet = 0;
|
||||||
|
|
||||||
extern void* __stack_top;//Defined in libnx.
|
void NX_NORETURN nroEntrypointTrampoline(const ConfigEntry* entries, u64 handle, u64 entrypoint);
|
||||||
#define STACK_SIZE 0x100000 //Change this if main-thread stack size ever changes.
|
|
||||||
|
|
||||||
void __libnx_initheap(void)
|
void __libnx_initheap(void)
|
||||||
{
|
{
|
||||||
static char g_innerheap[0x20000];
|
static char g_innerheap[0x4000];
|
||||||
|
|
||||||
extern char* fake_heap_start;
|
extern char* fake_heap_start;
|
||||||
extern char* fake_heap_end;
|
extern char* fake_heap_end;
|
||||||
@ -33,155 +57,255 @@ void __libnx_initheap(void)
|
|||||||
fake_heap_end = &g_innerheap[sizeof g_innerheap];
|
fake_heap_end = &g_innerheap[sizeof g_innerheap];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static Result readSetting(const char* key, void* buf, size_t size)
|
||||||
|
{
|
||||||
|
Result rc;
|
||||||
|
u64 actual_size;
|
||||||
|
const char* const section_name = "hbloader";
|
||||||
|
rc = setsysGetSettingsItemValueSize(section_name, key, &actual_size);
|
||||||
|
if (R_SUCCEEDED(rc) && actual_size != size)
|
||||||
|
rc = MAKERESULT(Module_Libnx, LibnxError_BadInput);
|
||||||
|
if (R_SUCCEEDED(rc))
|
||||||
|
rc = setsysGetSettingsItemValue(section_name, key, buf, size, &actual_size);
|
||||||
|
if (R_SUCCEEDED(rc) && actual_size != size)
|
||||||
|
rc = MAKERESULT(Module_Libnx, LibnxError_BadInput);
|
||||||
|
if (R_FAILED(rc)) memset(buf, 0, size);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
void __appInit(void)
|
void __appInit(void)
|
||||||
{
|
{
|
||||||
(void) g_easterEgg[0];
|
|
||||||
|
|
||||||
Result rc;
|
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();
|
rc = smInitialize();
|
||||||
if (R_FAILED(rc))
|
if (R_FAILED(rc))
|
||||||
fatalSimple(MAKERESULT(MODULE_HBL, 1));
|
diagAbortWithResult(MAKERESULT(Module_HomebrewLoader, 1));
|
||||||
|
|
||||||
|
rc = setsysInitialize();
|
||||||
|
if (R_SUCCEEDED(rc)) {
|
||||||
|
SetSysFirmwareVersion fw;
|
||||||
|
rc = setsysGetFirmwareVersion(&fw);
|
||||||
|
if (R_SUCCEEDED(rc))
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
rc = fsInitialize();
|
rc = fsInitialize();
|
||||||
if (R_FAILED(rc))
|
if (R_FAILED(rc))
|
||||||
fatalSimple(MAKERESULT(MODULE_HBL, 2));
|
diagAbortWithResult(MAKERESULT(Module_HomebrewLoader, 2));
|
||||||
|
|
||||||
fsdevMountSdmc();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void __appExit(void)
|
void __wrap_exit(void)
|
||||||
{
|
{
|
||||||
fsdevUnmountAll();
|
// exit() effectively never gets called, so let's stub it out.
|
||||||
fsExit();
|
diagAbortWithResult(MAKERESULT(Module_HomebrewLoader, 39));
|
||||||
smExit();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void* g_heapAddr;
|
static u64 calculateMaxHeapSize(void)
|
||||||
static size_t g_heapSize;
|
|
||||||
|
|
||||||
void setupHbHeap(void)
|
|
||||||
{
|
{
|
||||||
u64 size = 0;
|
u64 size = 0;
|
||||||
void* addr = NULL;
|
|
||||||
u64 mem_available = 0, mem_used = 0;
|
u64 mem_available = 0, mem_used = 0;
|
||||||
Result rc=0;
|
|
||||||
|
|
||||||
svcGetInfo(&mem_available, 6, CUR_PROCESS_HANDLE, 0);
|
svcGetInfo(&mem_available, InfoType_TotalMemorySize, CUR_PROCESS_HANDLE, 0);
|
||||||
svcGetInfo(&mem_used, 7, CUR_PROCESS_HANDLE, 0);
|
svcGetInfo(&mem_used, InfoType_UsedMemorySize, CUR_PROCESS_HANDLE, 0);
|
||||||
|
|
||||||
if (mem_available > mem_used+0x200000)
|
if (mem_available > mem_used+0x200000)
|
||||||
size = (mem_available - mem_used - 0x200000) & ~0x1FFFFF;
|
size = (mem_available - mem_used - 0x200000) & ~0x1FFFFF;
|
||||||
if (size==0)
|
if (size == 0)
|
||||||
size = 0x2000000*16;
|
size = 0x2000000*16;
|
||||||
|
if (size > 0x6000000 && g_isAutomaticGameplayRecording)
|
||||||
|
size -= 0x6000000;
|
||||||
|
|
||||||
rc = svcSetHeapSize(&addr, size);
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void setupHbHeap(void)
|
||||||
|
{
|
||||||
|
void* addr = NULL;
|
||||||
|
u64 size = calculateMaxHeapSize();
|
||||||
|
|
||||||
|
if (!g_isApplication) {
|
||||||
|
if (g_appletHeapSize) {
|
||||||
|
u64 requested_size = (g_appletHeapSize + 0x1FFFFF) &~ 0x1FFFFF;
|
||||||
|
if (requested_size < size)
|
||||||
|
size = requested_size;
|
||||||
|
}
|
||||||
|
else if (g_appletHeapReservationSize) {
|
||||||
|
u64 reserved_size = (g_appletHeapReservationSize + 0x1FFFFF) &~ 0x1FFFFF;
|
||||||
|
if (reserved_size < size)
|
||||||
|
size -= reserved_size;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Result rc = svcSetHeapSize(&addr, size);
|
||||||
|
|
||||||
if (R_FAILED(rc) || addr==NULL)
|
if (R_FAILED(rc) || addr==NULL)
|
||||||
fatalSimple(MAKERESULT(MODULE_HBL, 9));
|
diagAbortWithResult(MAKERESULT(Module_HomebrewLoader, 9));
|
||||||
|
|
||||||
g_heapAddr = addr;
|
g_heapAddr = addr;
|
||||||
g_heapSize = size;
|
g_heapSize = size;
|
||||||
}
|
}
|
||||||
|
|
||||||
static Handle g_port;
|
static void procHandleReceiveThread(void* arg)
|
||||||
static Handle g_procHandle;
|
|
||||||
|
|
||||||
void threadFunc(void* ctx)
|
|
||||||
{
|
{
|
||||||
Handle session;
|
Handle session = (Handle)(uintptr_t)arg;
|
||||||
Result rc;
|
Result rc;
|
||||||
|
|
||||||
rc = svcWaitSynchronizationSingle(g_port, -1);
|
void* base = armGetTls();
|
||||||
if (R_FAILED(rc))
|
hipcMakeRequestInline(base);
|
||||||
fatalSimple(MAKERESULT(MODULE_HBL, 22));
|
|
||||||
|
|
||||||
rc = svcAcceptSession(&session, g_port);
|
|
||||||
if (R_FAILED(rc))
|
|
||||||
fatalSimple(MAKERESULT(MODULE_HBL, 14));
|
|
||||||
|
|
||||||
s32 idx = 0;
|
s32 idx = 0;
|
||||||
rc = svcReplyAndReceive(&idx, &session, 1, 0, -1);
|
rc = svcReplyAndReceive(&idx, &session, 1, INVALID_HANDLE, UINT64_MAX);
|
||||||
if (R_FAILED(rc))
|
if (R_FAILED(rc))
|
||||||
fatalSimple(MAKERESULT(MODULE_HBL, 15));
|
diagAbortWithResult(MAKERESULT(Module_HomebrewLoader, 15));
|
||||||
|
|
||||||
IpcParsedCommand ipc;
|
HipcParsedRequest r = hipcParseRequest(base);
|
||||||
rc = ipcParse(&ipc);
|
if (r.meta.num_copy_handles != 1)
|
||||||
if (R_FAILED(rc))
|
diagAbortWithResult(MAKERESULT(Module_HomebrewLoader, 17));
|
||||||
fatalSimple(MAKERESULT(MODULE_HBL, 16));
|
|
||||||
|
|
||||||
if (ipc.NumHandles != 1)
|
g_procHandle = r.data.copy_handles[0];
|
||||||
fatalSimple(MAKERESULT(MODULE_HBL, 17));
|
|
||||||
|
|
||||||
g_procHandle = ipc.Handles[0];
|
|
||||||
svcCloseHandle(session);
|
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.
|
// Sets g_isApplication if the hbloader process is running as an Application.
|
||||||
void getIsApplication(void) {
|
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();
|
|
||||||
|
|
||||||
if (R_SUCCEEDED(rc)) {
|
|
||||||
rc = pmshellGetApplicationPid(&app_pid);
|
|
||||||
pmshellExit();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (R_SUCCEEDED(rc) && cur_pid == app_pid) g_isApplication = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
void getOwnProcessHandle(void)
|
|
||||||
{
|
{
|
||||||
static Thread t;
|
|
||||||
Result rc;
|
Result rc;
|
||||||
|
|
||||||
rc = threadCreate(&t, &threadFunc, NULL, 0x1000, 0x20, 0);
|
// Try asking the kernel directly (only works on [9.0.0+] or mesosphère)
|
||||||
if (R_FAILED(rc))
|
u64 flag=0;
|
||||||
fatalSimple(MAKERESULT(MODULE_HBL, 10));
|
rc = svcGetInfo(&flag, InfoType_IsApplication, CUR_PROCESS_HANDLE, 0);
|
||||||
|
if (R_SUCCEEDED(rc)) {
|
||||||
|
g_isApplication = flag!=0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
rc = smRegisterService(&g_port, "hb:ldr", false, 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
|
||||||
|
// Do nothing if the HOS version predates [4.0.0], or we're not an application.
|
||||||
|
if (hosversionBefore(4,0,0) || !g_isApplication)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// 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
|
||||||
|
|
||||||
|
// 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) && data.nacp.video_capture == 2)
|
||||||
|
g_isAutomaticGameplayRecording = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void getOwnProcessHandle(void)
|
||||||
|
{
|
||||||
|
Result rc;
|
||||||
|
|
||||||
|
Handle server_handle, client_handle;
|
||||||
|
rc = svcCreateSession(&server_handle, &client_handle, 0, 0);
|
||||||
if (R_FAILED(rc))
|
if (R_FAILED(rc))
|
||||||
fatalSimple(MAKERESULT(MODULE_HBL, 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))
|
||||||
|
diagAbortWithResult(MAKERESULT(Module_HomebrewLoader, 10));
|
||||||
|
|
||||||
rc = threadStart(&t);
|
rc = threadStart(&t);
|
||||||
if (R_FAILED(rc))
|
if (R_FAILED(rc))
|
||||||
fatalSimple(MAKERESULT(MODULE_HBL, 13));
|
diagAbortWithResult(MAKERESULT(Module_HomebrewLoader, 13));
|
||||||
|
|
||||||
Service srv;
|
hipcMakeRequestInline(armGetTls(),
|
||||||
rc = smGetService(&srv, "hb:ldr");
|
.num_copy_handles = 1,
|
||||||
if (R_FAILED(rc))
|
).copy_handles[0] = CUR_PROCESS_HANDLE;
|
||||||
fatalSimple(MAKERESULT(MODULE_HBL, 23));
|
|
||||||
|
|
||||||
IpcCommand ipc;
|
svcSendSyncRequest(client_handle);
|
||||||
ipcInitialize(&ipc);
|
svcCloseHandle(client_handle);
|
||||||
ipcSendHandleCopy(&ipc, 0xffff8001);
|
|
||||||
|
|
||||||
struct {
|
|
||||||
int x, y;
|
|
||||||
}* raw;
|
|
||||||
|
|
||||||
raw = ipcPrepareHeader(&ipc, sizeof(*raw));
|
|
||||||
raw->x = raw->y = 0;
|
|
||||||
|
|
||||||
rc = serviceIpcDispatch(&srv);
|
|
||||||
|
|
||||||
threadWaitForExit(&t);
|
threadWaitForExit(&t);
|
||||||
threadClose(&t);
|
threadClose(&t);
|
||||||
|
}
|
||||||
|
|
||||||
serviceClose(&srv);
|
static bool isKernel5xOrLater(void)
|
||||||
svcCloseHandle(g_port);
|
{
|
||||||
|
u64 dummy = 0;
|
||||||
|
Result rc = svcGetInfo(&dummy, InfoType_UserExceptionContextAddress, INVALID_HANDLE, 0);
|
||||||
|
return R_VALUE(rc) != KERNELRESULT(InvalidEnumValue);
|
||||||
|
}
|
||||||
|
|
||||||
rc = smUnregisterService("hb:ldr");
|
static bool isKernel4x(void)
|
||||||
if (R_FAILED(rc))
|
{
|
||||||
fatalSimple(MAKERESULT(MODULE_HBL, 11));
|
u64 dummy = 0;
|
||||||
|
Result rc = svcGetInfo(&dummy, InfoType_InitialProcessIdRange, INVALID_HANDLE, 0);
|
||||||
|
return R_VALUE(rc) != KERNELRESULT(InvalidEnumValue);
|
||||||
|
}
|
||||||
|
|
||||||
smExit();
|
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)
|
void loadNro(void)
|
||||||
@ -190,9 +314,6 @@ void loadNro(void)
|
|||||||
size_t rw_size=0;
|
size_t rw_size=0;
|
||||||
Result rc=0;
|
Result rc=0;
|
||||||
|
|
||||||
svcSleepThread(1000000000);//Wait for sm-sysmodule to handle closing the sm session from this process. Without this delay smInitialize will fail once eventually used later.
|
|
||||||
//TODO: Lower the above delay-value?
|
|
||||||
|
|
||||||
memcpy((u8*)armGetTls() + 0x100, g_savedTls, 0x100);
|
memcpy((u8*)armGetTls() + 0x100, g_savedTls, 0x100);
|
||||||
|
|
||||||
if (g_nroSize > 0)
|
if (g_nroSize > 0)
|
||||||
@ -202,65 +323,76 @@ void loadNro(void)
|
|||||||
rw_size = header->segments[2].size + header->bss_size;
|
rw_size = header->segments[2].size + header->bss_size;
|
||||||
rw_size = (rw_size+0xFFF) & ~0xFFF;
|
rw_size = (rw_size+0xFFF) & ~0xFFF;
|
||||||
|
|
||||||
|
svcBreak(BreakReason_NotificationOnlyFlag | BreakReason_PreUnloadDll, g_nroAddr, g_nroSize);
|
||||||
|
|
||||||
// .text
|
// .text
|
||||||
rc = svcUnmapProcessCodeMemory(
|
rc = svcUnmapProcessCodeMemory(
|
||||||
g_procHandle, g_nroAddr + header->segments[0].file_off, ((u64) g_heapAddr) + header->segments[0].file_off, header->segments[0].size);
|
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))
|
if (R_FAILED(rc))
|
||||||
fatalSimple(MAKERESULT(MODULE_HBL, 24));
|
diagAbortWithResult(MAKERESULT(Module_HomebrewLoader, 24));
|
||||||
|
|
||||||
// .rodata
|
// .rodata
|
||||||
rc = svcUnmapProcessCodeMemory(
|
rc = svcUnmapProcessCodeMemory(
|
||||||
g_procHandle, g_nroAddr + header->segments[1].file_off, ((u64) g_heapAddr) + header->segments[1].file_off, header->segments[1].size);
|
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))
|
if (R_FAILED(rc))
|
||||||
fatalSimple(MAKERESULT(MODULE_HBL, 25));
|
diagAbortWithResult(MAKERESULT(Module_HomebrewLoader, 25));
|
||||||
|
|
||||||
// .data + .bss
|
// .data + .bss
|
||||||
rc = svcUnmapProcessCodeMemory(
|
rc = svcUnmapProcessCodeMemory(
|
||||||
g_procHandle, g_nroAddr + header->segments[2].file_off, ((u64) g_heapAddr) + header->segments[2].file_off, rw_size);
|
g_procHandle, g_nroAddr + header->segments[2].file_off, ((u64) g_heapAddr) + header->segments[2].file_off, rw_size);
|
||||||
|
|
||||||
if (R_FAILED(rc))
|
if (R_FAILED(rc))
|
||||||
fatalSimple(MAKERESULT(MODULE_HBL, 26));
|
diagAbortWithResult(MAKERESULT(Module_HomebrewLoader, 26));
|
||||||
|
|
||||||
|
svcBreak(BreakReason_NotificationOnlyFlag | BreakReason_PostUnloadDll, g_nroAddr, g_nroSize);
|
||||||
|
|
||||||
g_nroAddr = g_nroSize = 0;
|
g_nroAddr = g_nroSize = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (strlen(g_nextNroPath) == 0)
|
if (g_nextNroPath[0] == '\0')
|
||||||
{
|
{
|
||||||
strcpy(g_nextNroPath, "sdmc:/hbmenu.nro");
|
memcpy(g_nextNroPath, DEFAULT_NRO, sizeof(DEFAULT_NRO));
|
||||||
strcpy(g_nextArgv, "sdmc:/hbmenu.nro");
|
memcpy(g_nextArgv, DEFAULT_NRO, sizeof(DEFAULT_NRO));
|
||||||
}
|
}
|
||||||
|
|
||||||
memcpy(g_argv, g_nextArgv, sizeof g_argv);
|
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;
|
uint8_t *nrobuf = (uint8_t*) g_heapAddr;
|
||||||
|
|
||||||
NroStart* start = (NroStart*) (nrobuf + 0);
|
NroStart* start = (NroStart*) (nrobuf + 0);
|
||||||
header = (NroHeader*) (nrobuf + sizeof(NroStart));
|
header = (NroHeader*) (nrobuf + sizeof(NroStart));
|
||||||
uint8_t* rest = (uint8_t*) (nrobuf + sizeof(NroStart) + sizeof(NroHeader));
|
uint8_t* rest = (uint8_t*) (nrobuf + sizeof(NroStart) + sizeof(NroHeader));
|
||||||
|
|
||||||
FILE* f = fopen(g_nextNroPath, "rb");
|
rc = fsdevMountSdmc();
|
||||||
if (f == NULL)
|
if (R_FAILED(rc))
|
||||||
fatalSimple(MAKERESULT(MODULE_HBL, 3));
|
diagAbortWithResult(MAKERESULT(Module_HomebrewLoader, 404));
|
||||||
|
|
||||||
|
int fd = open(g_nextNroPath, O_RDONLY);
|
||||||
|
if (fd < 0)
|
||||||
|
diagAbortWithResult(MAKERESULT(Module_HomebrewLoader, 3));
|
||||||
|
|
||||||
// Reset NRO path to load hbmenu by default next time.
|
// Reset NRO path to load hbmenu by default next time.
|
||||||
g_nextNroPath[0] = '\0';
|
g_nextNroPath[0] = '\0';
|
||||||
|
|
||||||
if (fread(start, sizeof(*start), 1, f) != 1)
|
if (read(fd, start, sizeof(*start)) != sizeof(*start))
|
||||||
fatalSimple(MAKERESULT(MODULE_HBL, 4));
|
diagAbortWithResult(MAKERESULT(Module_HomebrewLoader, 4));
|
||||||
|
|
||||||
if (fread(header, sizeof(*header), 1, f) != 1)
|
if (read(fd, header, sizeof(*header)) != sizeof(*header))
|
||||||
fatalSimple(MAKERESULT(MODULE_HBL, 4));
|
diagAbortWithResult(MAKERESULT(Module_HomebrewLoader, 4));
|
||||||
|
|
||||||
if(header->magic != NROHEADER_MAGIC)
|
if (header->magic != NROHEADER_MAGIC)
|
||||||
fatalSimple(MAKERESULT(MODULE_HBL, 5));
|
diagAbortWithResult(MAKERESULT(Module_HomebrewLoader, 5));
|
||||||
|
|
||||||
size_t rest_size = header->size - (sizeof(NroStart) + sizeof(NroHeader));
|
size_t rest_size = header->size - (sizeof(NroStart) + sizeof(NroHeader));
|
||||||
if (fread(rest, rest_size, 1, f) != 1)
|
if (read(fd, rest, rest_size) != rest_size)
|
||||||
fatalSimple(MAKERESULT(MODULE_HBL, 7));
|
diagAbortWithResult(MAKERESULT(Module_HomebrewLoader, 7));
|
||||||
|
|
||||||
fclose(f);
|
close(fd);
|
||||||
|
fsdevUnmountAll();
|
||||||
|
|
||||||
size_t total_size = header->size + header->bss_size;
|
size_t total_size = header->size + header->bss_size;
|
||||||
total_size = (total_size+0xFFF) & ~0xFFF;
|
total_size = (total_size+0xFFF) & ~0xFFF;
|
||||||
@ -274,7 +406,7 @@ void loadNro(void)
|
|||||||
if (header->segments[i].file_off >= header->size || header->segments[i].size > header->size ||
|
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)
|
(header->segments[i].file_off + header->segments[i].size) > header->size)
|
||||||
{
|
{
|
||||||
fatalSimple(MAKERESULT(MODULE_HBL, 6));
|
diagAbortWithResult(MAKERESULT(Module_HomebrewLoader, 6));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -284,37 +416,35 @@ void loadNro(void)
|
|||||||
memcpy(&g_nroHeader, header, sizeof(g_nroHeader));
|
memcpy(&g_nroHeader, header, sizeof(g_nroHeader));
|
||||||
header = &g_nroHeader;
|
header = &g_nroHeader;
|
||||||
|
|
||||||
u64 map_addr;
|
// Map code memory to a new randomized address
|
||||||
|
virtmemLock();
|
||||||
do {
|
void* map_addr = virtmemFindCodeMemory(total_size, 0);
|
||||||
map_addr = randomGet64() & 0xFFFFFF000ull;
|
rc = svcMapProcessCodeMemory(g_procHandle, (u64)map_addr, (u64)nrobuf, total_size);
|
||||||
rc = svcMapProcessCodeMemory(g_procHandle, map_addr, (u64)nrobuf, total_size);
|
virtmemUnlock();
|
||||||
|
|
||||||
} while (rc == 0xDC01 || rc == 0xD401);
|
|
||||||
|
|
||||||
if (R_FAILED(rc))
|
if (R_FAILED(rc))
|
||||||
fatalSimple(MAKERESULT(MODULE_HBL, 18));
|
diagAbortWithResult(MAKERESULT(Module_HomebrewLoader, 18));
|
||||||
|
|
||||||
// .text
|
// .text
|
||||||
rc = svcSetProcessMemoryPermission(
|
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))
|
if (R_FAILED(rc))
|
||||||
fatalSimple(MAKERESULT(MODULE_HBL, 19));
|
diagAbortWithResult(MAKERESULT(Module_HomebrewLoader, 19));
|
||||||
|
|
||||||
// .rodata
|
// .rodata
|
||||||
rc = svcSetProcessMemoryPermission(
|
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))
|
if (R_FAILED(rc))
|
||||||
fatalSimple(MAKERESULT(MODULE_HBL, 20));
|
diagAbortWithResult(MAKERESULT(Module_HomebrewLoader, 20));
|
||||||
|
|
||||||
// .data + .bss
|
// .data + .bss
|
||||||
rc = svcSetProcessMemoryPermission(
|
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))
|
if (R_FAILED(rc))
|
||||||
fatalSimple(MAKERESULT(MODULE_HBL, 21));
|
diagAbortWithResult(MAKERESULT(Module_HomebrewLoader, 21));
|
||||||
|
|
||||||
u64 nro_size = header->segments[2].file_off + rw_size;
|
u64 nro_size = header->segments[2].file_off + rw_size;
|
||||||
u64 nro_heap_start = ((u64) g_heapAddr) + nro_size;
|
u64 nro_heap_start = ((u64) g_heapAddr) + nro_size;
|
||||||
@ -330,18 +460,32 @@ void loadNro(void)
|
|||||||
{ EntryType_Argv, 0, {0, 0} },
|
{ EntryType_Argv, 0, {0, 0} },
|
||||||
{ EntryType_NextLoadPath, 0, {0, 0} },
|
{ EntryType_NextLoadPath, 0, {0, 0} },
|
||||||
{ EntryType_LastLoadResult, 0, {0, 0} },
|
{ EntryType_LastLoadResult, 0, {0, 0} },
|
||||||
{ EntryType_SyscallAvailableHint, 0, {0xffffffffffffffff, 0x1fc1fff0007ffff} },
|
{ EntryType_SyscallAvailableHint, 0, {UINT64_MAX, UINT64_MAX} },
|
||||||
|
{ EntryType_SyscallAvailableHint2, 0, {UINT64_MAX, 0} },
|
||||||
{ EntryType_RandomSeed, 0, {0, 0} },
|
{ EntryType_RandomSeed, 0, {0, 0} },
|
||||||
{ EntryType_EndOfList, 0, {0, 0} }
|
{ EntryType_UserIdStorage, 0, {(u64)(uintptr_t)&g_userIdStorage, 0} },
|
||||||
|
{ EntryType_HosVersion, 0, {0, 0} },
|
||||||
|
{ EntryType_EndOfList, 0, {(u64)(uintptr_t)g_noticeText, sizeof(g_noticeText)} }
|
||||||
};
|
};
|
||||||
|
|
||||||
ConfigEntry *entry_AppletType = &entries[2];
|
ConfigEntry *entry_AppletType = &entries[2];
|
||||||
|
ConfigEntry *entry_Syscalls = &entries[7];
|
||||||
|
|
||||||
if (g_isApplication) {
|
if (g_isApplication) {
|
||||||
entry_AppletType->Value[0] = AppletType_SystemApplication;
|
entry_AppletType->Value[0] = AppletType_SystemApplication;
|
||||||
entry_AppletType->Value[1] = EnvAppletFlags_ApplicationOverride;
|
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
|
// MainThreadHandle
|
||||||
entries[0].Value[0] = envGetMainThreadHandle();
|
entries[0].Value[0] = envGetMainThreadHandle();
|
||||||
// ProcessHandle
|
// ProcessHandle
|
||||||
@ -350,36 +494,39 @@ void loadNro(void)
|
|||||||
entries[3].Value[0] = nro_heap_start;
|
entries[3].Value[0] = nro_heap_start;
|
||||||
entries[3].Value[1] = nro_heap_size;
|
entries[3].Value[1] = nro_heap_size;
|
||||||
// Argv
|
// Argv
|
||||||
entries[4].Value[1] = (u64) &g_argv[0];
|
entries[4].Value[1] = (u64)(uintptr_t)&g_argv[0];
|
||||||
// NextLoadPath
|
// NextLoadPath
|
||||||
entries[5].Value[0] = (u64) &g_nextNroPath[0];
|
entries[5].Value[0] = (u64)(uintptr_t)&g_nextNroPath[0];
|
||||||
entries[5].Value[1] = (u64) &g_nextArgv[0];
|
entries[5].Value[1] = (u64)(uintptr_t)&g_nextArgv[0];
|
||||||
// LastLoadResult
|
// LastLoadResult
|
||||||
entries[6].Value[0] = g_lastRet;
|
entries[6].Value[0] = g_lastRet;
|
||||||
// RandomSeed
|
// RandomSeed
|
||||||
entries[8].Value[0] = randomGet64();
|
entries[9].Value[0] = randomGet64();
|
||||||
entries[8].Value[1] = randomGet64();
|
entries[9].Value[1] = randomGet64();
|
||||||
|
// HosVersion
|
||||||
|
entries[11].Value[0] = hosversionGet();
|
||||||
|
entries[11].Value[1] = hosversionIsAtmosphere() ? 0x41544d4f53504852UL : 0; // 'ATMOSPHR'
|
||||||
|
|
||||||
u64 entrypoint = map_addr;
|
g_nroAddr = (u64)map_addr;
|
||||||
|
|
||||||
g_nroAddr = map_addr;
|
|
||||||
g_nroSize = nro_size;
|
g_nroSize = nro_size;
|
||||||
|
|
||||||
memset(__stack_top - STACK_SIZE, 0, STACK_SIZE);
|
svcBreak(BreakReason_NotificationOnlyFlag | BreakReason_PostLoadDll, g_nroAddr, g_nroSize);
|
||||||
|
|
||||||
extern NORETURN void nroEntrypointTrampoline(u64 entries_ptr, u64 handle, u64 entrypoint);
|
nroEntrypointTrampoline(&entries[0], -1, g_nroAddr);
|
||||||
nroEntrypointTrampoline((u64) entries, -1, entrypoint);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc, char **argv)
|
int main(int argc, char **argv)
|
||||||
{
|
{
|
||||||
memcpy(g_savedTls, (u8*)armGetTls() + 0x100, 0x100);
|
memcpy(g_savedTls, (u8*)armGetTls() + 0x100, 0x100);
|
||||||
|
|
||||||
setupHbHeap();
|
|
||||||
getIsApplication();
|
getIsApplication();
|
||||||
|
getIsAutomaticGameplayRecording();
|
||||||
|
smExit(); // Close SM as we don't need it anymore.
|
||||||
|
setupHbHeap();
|
||||||
getOwnProcessHandle();
|
getOwnProcessHandle();
|
||||||
|
getCodeMemoryCapability();
|
||||||
loadNro();
|
loadNro();
|
||||||
|
|
||||||
fatalSimple(MAKERESULT(MODULE_HBL, 8));
|
diagAbortWithResult(MAKERESULT(Module_HomebrewLoader, 8));
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -1,11 +1,8 @@
|
|||||||
.section .text.nroEntrypointTrampoline, "ax", %progbits
|
.section .text.nroEntrypointTrampoline, "ax", %progbits
|
||||||
|
.align 2
|
||||||
.global nroEntrypointTrampoline
|
.global nroEntrypointTrampoline
|
||||||
.type nroEntrypointTrampoline, %function
|
.type nroEntrypointTrampoline, %function
|
||||||
.align 2
|
|
||||||
|
|
||||||
.cfi_startproc
|
.cfi_startproc
|
||||||
|
|
||||||
nroEntrypointTrampoline:
|
nroEntrypointTrampoline:
|
||||||
|
|
||||||
// Reset stack pointer.
|
// Reset stack pointer.
|
||||||
@ -18,8 +15,7 @@ nroEntrypointTrampoline:
|
|||||||
|
|
||||||
// Save retval
|
// Save retval
|
||||||
adrp x1, g_lastRet
|
adrp x1, g_lastRet
|
||||||
add x1, x1, #:lo12:g_lastRet
|
str w0, [x1, #:lo12:g_lastRet]
|
||||||
str x0, [x1]
|
|
||||||
|
|
||||||
// Reset stack pointer and load next NRO.
|
// Reset stack pointer and load next NRO.
|
||||||
adrp x8, __stack_top
|
adrp x8, __stack_top
|
||||||
@ -29,3 +25,23 @@ nroEntrypointTrampoline:
|
|||||||
b loadNro
|
b loadNro
|
||||||
|
|
||||||
.cfi_endproc
|
.cfi_endproc
|
||||||
|
|
||||||
|
.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, .Lfail
|
||||||
|
br x7
|
||||||
|
|
||||||
|
.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