Compare commits

...

53 Commits

Author SHA1 Message Date
Dave Murphy
8f5a425e6e
set debug flags properly 2024-10-23 16:39:22 +01:00
Dave Murphy
5ac0dfe82d
add force_debug_prod flag 2024-10-18 21:52:21 +01:00
borntohonk
cd6a723acb
Update NORETURN to NX_NORETURN for libnx 4.6.0+ (#38) 2024-01-30 09:57:59 +00:00
yellows8
b38421d04a
v2.4.4 2023-10-27 19:05:45 -04:00
fincs
f6c46269ef
Fix #37: regressions introduced by missed HBABI entry index updates 2022-10-16 00:06:07 +02:00
fincs
b1406d9eaa
Grant access to all SVCs (including the extended range) 2022-10-15 21:09:11 +02:00
fincs
7aefc7a312
v2.4.1 2021-04-11 12:42:08 +02:00
fincs
e7ff5cba3b
Detect Atmosphère, and pass it down through HBABI 2021-04-11 12:24:09 +02:00
fincs
56868b4325
v2.4.0 2020-12-19 17:41:07 +01:00
SciresM
723c09d7ab
hbl: grant read access to ktrace buffer (#36)
Also grant access to svcQueryIoMapping & hint it as available
2020-12-17 01:56:29 +01:00
fincs
e2b8f0c5b3
Minor cleanup/refactoring, memory usage optimization & UB removal 2020-12-08 14:40:21 +01:00
fincs
81553d91fd
Update npdm with name for new svc (0x37 svcGetResourceLimitPeakValue) 2020-12-01 13:53:11 +01:00
fincs
06b2d3bdfd
Use virtmemFindCodeMemory instead of virtmemFindAslr 2020-10-30 00:16:39 +01:00
fincs
e528d15d5a
Improve CodeMemory capability detection 2020-10-29 16:53:37 +01:00
fincs
18019db14a
Replace fatalThrow with diagAbortWithResult 2020-10-29 14:16:11 +01:00
fincs
c69c6a9e0d
Add debugger svcBreak pre/post NRO load/unload hooks 2020-10-29 14:16:11 +01:00
fincs
5ce1d33b7d
Use new virtmem API for NRO ASLR 2020-10-29 14:16:10 +01:00
fincs
a13d5e7af2
Remove workaround for very old homebrew (which is unusable anyway) 2020-10-29 14:16:10 +01:00
fincs
0ce48ebe22
Revoke access to CodeMemory syscalls if they are not usable 2020-10-29 14:16:10 +01:00
Michael Scire
7caa177353 support automatic recording games on 4.0.0-4.1.0 2020-09-22 23:49:57 +02:00
fincs
946ac4ede6
Bump version for release 2020-07-09 02:49:58 +02:00
Michael Scire
4d63750a44 hbl: update json for new format 2020-07-09 02:46:41 +02:00
fincs
bc48603694
Bump version for release 2020-06-15 23:55:19 +02:00
HookedBehemoth
faec3c5098
add permission to mount qlaunch romfs (#29) 2020-06-15 23:51:52 +02:00
fincs
2288c06401
Bump version for release 2020-05-12 00:03:21 +02:00
borntohonk
721fa9b525 Update nacp reference to reflect on libnx changes in libnx commit 20fd5bb9a21cd0c8bcea0cc7cc84640f8de714e6 2020-04-26 16:53:33 +02:00
fincs
19f1afbbf3
v2.3.1 2020-04-14 22:21:56 +02:00
masagrator
d9cb20c248
Add svcGetSystemInfo and update json to match libnx 3.1.0 svc.h (#26)
Also adds svcGetSystemInfo to SyscallAvailableHint
2020-04-12 01:49:30 +02:00
yellows8
24487fc496
Use InfoType_IsApplication to set g_isApplication when available. Updated for latest libnx. 2020-03-11 22:43:58 -04:00
fincs
5d276d1da7
v2.3.0 2019-12-11 00:59:08 +01:00
fincs
7b085d86f4
Update for latest libnx changes (again) 2019-12-11 00:58:46 +01:00
octopuserectus
9b2a16c731 Reflect latest libnx changes 2019-11-27 19:33:53 +01:00
fincs
0d526f9aaa
fatalSimple -> fatalThrow 2019-10-25 00:02:36 +02:00
yellows8
4cd81dcc31
Updated pmshell code for latest libnx. 2019-10-24 16:52:26 -04:00
fincs
2ddb129338
Code size improvements & memory usage shrunk down:
- fsdev current-working-directory support disabled (saves memory)
- Heap shrunk down from 128kb to 16kb
- SDMC mounted during NRO loading and unmounted afterwards
- POSIX file i/o functions used instead of stdio (one less layer of abstraction)
- Default NRO path is now a define for easy maintenance
2019-10-21 21:50:42 +02:00
fincs
3510f74c77
Update for latest libnx (including new-ipc) 2019-10-20 03:18:51 +02:00
yellows8
b33c5df54b
Updated readSetting to handle the out_size param from setsysGetSettingsItemValue with latest libnx. The buffer is now cleared on failure. 2019-10-04 18:34:51 -04:00
yellows8
92a14f706e
Updated nacp code for latest libnx. 2019-08-21 23:10:31 -04:00
fincs
ededd72fbf Use 2MB alignment instead of page alignment in heap size calculations 2019-07-29 21:35:06 +02:00
fincs
3364cc0e74 Use git describe in version string 2019-07-29 21:31:31 +02:00
fincs
2a0d4a7713 Add support for UserIdStorage & HosVersion homebrew ABI keys 2019-07-29 21:17:48 +02:00
fincs
7c1eb6235f Add version notice text 2019-07-29 18:12:18 +02:00
fincs
7f8fb00864 Add applet heap size configuration support, example usage is as follows:
[hbloader]
; Controls the size of the homebrew heap when running as applet.
; If set to zero, all available applet memory is used as heap.
; The default is zero.
applet_heap_size = u64!0x0
; Controls the amount of memory to reserve when running as applet
; for usage by other applets. This setting has no effect if
; applet_heap_size is non-zero. The default is zero.
applet_heap_reservation_size = u64!0x8000000
2019-07-29 18:12:00 +02:00
fincs
00942640a5 nroEntrypointTrampoline: Fix g_lastRet write size access bug 2019-07-29 17:11:38 +02:00
fincs
104e0fb0b3 Stub out unused exit logic, close SM handle once we don't need it anymore 2019-07-29 15:12:38 +02:00
fincs
549adde3ca Use svcGetInfo type enum and hosversionAtLeast 2019-07-29 15:09:33 +02:00
fincs
b45f3162a8 getOwnProcessHandle: use svcCreateSession directly instead of going through SM; replace MODULE_HBL with Module_HomebrewLoader 2019-07-29 14:42:37 +02:00
fincs
3af8c89024 Update for latest libnx 2019-03-26 14:03:41 +01:00
SciresM
8717f990ab Grant access to svcCallSecureMonitor 2019-01-24 17:46:29 +01:00
fincs
0556fbbd99 Only apply SM close bug workaround to very old apps (use absence of MOD0 as heuristic) 2018-12-06 02:18:50 +01:00
yellows8
96af83a70e Added support for exceptions. 2018-11-14 21:31:45 -05:00
SciresM
2d7e01f938 Fix crash when taking over some games on 5.0.0+ (#10)
* HBL: Fix crash when taking over some games on 5.0.0+ where the title supports automatic video-recording via the nacp.
2018-10-31 17:54:59 -04:00
roblabla
bde505a0bf Use new npdm-json format 2018-10-30 21:36:24 -04:00
4 changed files with 657 additions and 306 deletions

View File

@ -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)

177
hbl.json
View File

@ -12,21 +12,31 @@
"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" : { ],
"kernel_capabilities": [
{
"type": "kernel_flags",
"value": {
"highest_thread_priority": 59, "highest_thread_priority": 59,
"lowest_thread_priority": 28, "lowest_thread_priority": 28,
"highest_cpu_id": 2, "highest_cpu_id": 2,
"lowest_cpu_id": 0 "lowest_cpu_id": 0
}
}, },
"syscalls" : { {
"svcUnknown00" : "0x00", "type": "syscalls",
"value": {
"svcUnassigned00": "0x00",
"svcSetHeapSize": "0x01", "svcSetHeapSize": "0x01",
"svcSetMemoryPermission": "0x02", "svcSetMemoryPermission": "0x02",
"svcSetMemoryAttribute": "0x03", "svcSetMemoryAttribute": "0x03",
@ -72,7 +82,7 @@
"svcFlushDataCache": "0x2B", "svcFlushDataCache": "0x2B",
"svcMapPhysicalMemory": "0x2C", "svcMapPhysicalMemory": "0x2C",
"svcUnmapPhysicalMemory": "0x2D", "svcUnmapPhysicalMemory": "0x2D",
"svcGetFutureThreadInfo" : "0x2E", "svcGetDebugFutureThreadInfo": "0x2E",
"svcGetLastThreadInfo": "0x2F", "svcGetLastThreadInfo": "0x2F",
"svcGetResourceLimitLimitValue": "0x30", "svcGetResourceLimitLimitValue": "0x30",
"svcGetResourceLimitCurrentValue": "0x31", "svcGetResourceLimitCurrentValue": "0x31",
@ -80,24 +90,24 @@
"svcGetThreadContext3": "0x33", "svcGetThreadContext3": "0x33",
"svcWaitForAddress": "0x34", "svcWaitForAddress": "0x34",
"svcSignalToAddress": "0x35", "svcSignalToAddress": "0x35",
"svcUnknown36" : "0x36", "svcSynchronizePreemptionState": "0x36",
"svcUnknown37" : "0x37", "svcGetResourceLimitPeakValue": "0x37",
"svcUnknown38" : "0x38", "svcUnassigned38": "0x38",
"svcUnknown39" : "0x39", "svcCreateIoPool": "0x39",
"svcUnknown3a" : "0x3A", "svcCreateIoRegion": "0x3A",
"svcUnknown3b" : "0x3B", "svcUnassigned3B": "0x3B",
"svcDumpInfo" : "0x3C", "svcKernelDebug": "0x3C",
"svcDumpInfoNew" : "0x3D", "svcChangeKernelTraceState": "0x3D",
"svcUnknown3e" : "0x3E", "svcUnassigned3E": "0x3E",
"svcUnknown3f" : "0x3F", "svcUnassigned3F": "0x3F",
"svcCreateSession": "0x40", "svcCreateSession": "0x40",
"svcAcceptSession": "0x41", "svcAcceptSession": "0x41",
"svcReplyAndReceiveLight": "0x42", "svcReplyAndReceiveLight": "0x42",
"svcReplyAndReceive": "0x43", "svcReplyAndReceive": "0x43",
"svcReplyAndReceiveWithUserBuffer": "0x44", "svcReplyAndReceiveWithUserBuffer": "0x44",
"svcCreateEvent": "0x45", "svcCreateEvent": "0x45",
"svcUnknown46" : "0x46", "svcMapIoRegion": "0x46",
"svcUnknown47" : "0x47", "svcUnmapIoRegion": "0x47",
"svcMapPhysicalMemoryUnsafe": "0x48", "svcMapPhysicalMemoryUnsafe": "0x48",
"svcUnmapPhysicalMemoryUnsafe": "0x49", "svcUnmapPhysicalMemoryUnsafe": "0x49",
"svcSetUnsafeLimit": "0x4A", "svcSetUnsafeLimit": "0x4A",
@ -109,6 +119,19 @@
"svcCreateSharedMemory": "0x50", "svcCreateSharedMemory": "0x50",
"svcMapTransferMemory": "0x51", "svcMapTransferMemory": "0x51",
"svcUnmapTransferMemory": "0x52", "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", "svcDebugActiveProcess": "0x60",
"svcBreakDebugProcess": "0x61", "svcBreakDebugProcess": "0x61",
"svcTerminateDebugProcess": "0x62", "svcTerminateDebugProcess": "0x62",
@ -123,20 +146,118 @@
"svcWriteDebugProcessMemory": "0x6B", "svcWriteDebugProcessMemory": "0x6B",
"svcSetHardwareBreakPoint": "0x6C", "svcSetHardwareBreakPoint": "0x6C",
"svcGetDebugThreadParam": "0x6D", "svcGetDebugThreadParam": "0x6D",
"svcUnassigned6E": "0x6E",
"svcGetSystemInfo": "0x6F",
"svcCreatePort": "0x70",
"svcManageNamedPort": "0x71",
"svcConnectToPort": "0x72", "svcConnectToPort": "0x72",
"svcSetProcessMemoryPermission": "0x73", "svcSetProcessMemoryPermission": "0x73",
"svcMapProcessMemory": "0x74", "svcMapProcessMemory": "0x74",
"svcUnmapProcessMemory": "0x75", "svcUnmapProcessMemory": "0x75",
"svcQueryProcessMemory": "0x76", "svcQueryProcessMemory": "0x76",
"svcMapProcessCodeMemory": "0x77", "svcMapProcessCodeMemory": "0x77",
"svcUnmapProcessCodeMemory" : "0x78" "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"
}
}, },
"application_type" : 2, {
"min_kernel_version" : "0x30", "type": "application_type",
"handle_table_size" : 512, "value": 2
"debug_flags" : { },
"allow_debug" : true, {
"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 "force_debug": true
} }
},
{
"type": "map_region",
"value": [
{
"region_type": 1,
"is_ro": true
} }
]
}
]
} }

View File

@ -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;
} }

View File

@ -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