mirror of
https://github.com/switchbrew/nx-hbloader.git
synced 2025-06-20 21:12:39 +02:00
Public release
This commit is contained in:
commit
b9aa673624
184
Makefile
Normal file
184
Makefile
Normal file
@ -0,0 +1,184 @@
|
||||
#---------------------------------------------------------------------------------
|
||||
.SUFFIXES:
|
||||
#---------------------------------------------------------------------------------
|
||||
|
||||
ifeq ($(strip $(DEVKITA64)),)
|
||||
$(error "Please set DEVKITA64 in your environment. export DEVKITA64=<path to>DEVKITA64")
|
||||
endif
|
||||
|
||||
TOPDIR ?= $(CURDIR)
|
||||
include $(DEVKITA64)/switch_rules
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# TARGET is the name of the output
|
||||
# BUILD is the directory where object files & intermediate files will be placed
|
||||
# SOURCES is a list of directories containing source code
|
||||
# DATA is a list of directories containing data 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".
|
||||
#
|
||||
# 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
|
||||
#---------------------------------------------------------------------------------
|
||||
TARGET := $(notdir $(CURDIR))
|
||||
BUILD := build
|
||||
SOURCES := source
|
||||
DATA := data
|
||||
INCLUDES := include
|
||||
EXEFS_SRC := exefs_src
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# options for code generation
|
||||
#---------------------------------------------------------------------------------
|
||||
ARCH := -march=armv8-a -mtp=soft -fPIE
|
||||
|
||||
CFLAGS := -g -Wall -O2 \
|
||||
-ffast-math \
|
||||
$(ARCH) $(DEFINES)
|
||||
|
||||
CFLAGS += $(INCLUDE) -DSWITCH
|
||||
|
||||
CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions -std=gnu++11
|
||||
|
||||
ASFLAGS := -g $(ARCH)
|
||||
LDFLAGS = -specs=switch.specs -g $(ARCH) -Wl,-Map,$(notdir $*.map)
|
||||
|
||||
LIBS := -lnx
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# list of directories containing libraries, this must be the top level containing
|
||||
# include and lib
|
||||
#---------------------------------------------------------------------------------
|
||||
LIBDIRS := $(PORTLIBS) $(LIBNX)
|
||||
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# no real need to edit anything past this point unless you need to add additional
|
||||
# rules for different file extensions
|
||||
#---------------------------------------------------------------------------------
|
||||
ifneq ($(BUILD),$(notdir $(CURDIR)))
|
||||
#---------------------------------------------------------------------------------
|
||||
|
||||
export OUTPUT := $(CURDIR)/$(TARGET)
|
||||
export TOPDIR := $(CURDIR)
|
||||
|
||||
export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \
|
||||
$(foreach dir,$(DATA),$(CURDIR)/$(dir))
|
||||
|
||||
export DEPSDIR := $(CURDIR)/$(BUILD)
|
||||
|
||||
CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c)))
|
||||
CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp)))
|
||||
SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s)))
|
||||
BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*)))
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# use CXX for linking C++ projects, CC for standard C
|
||||
#---------------------------------------------------------------------------------
|
||||
ifeq ($(strip $(CPPFILES)),)
|
||||
#---------------------------------------------------------------------------------
|
||||
export LD := $(CC)
|
||||
#---------------------------------------------------------------------------------
|
||||
else
|
||||
#---------------------------------------------------------------------------------
|
||||
export LD := $(CXX)
|
||||
#---------------------------------------------------------------------------------
|
||||
endif
|
||||
#---------------------------------------------------------------------------------
|
||||
|
||||
export OFILES := $(addsuffix .o,$(BINFILES)) \
|
||||
$(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o)
|
||||
|
||||
export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \
|
||||
$(foreach dir,$(LIBDIRS),-I$(dir)/include) \
|
||||
-I$(CURDIR)/$(BUILD)
|
||||
|
||||
export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib)
|
||||
|
||||
export BUILD_EXEFS_SRC := $(TOPDIR)/$(EXEFS_SRC)
|
||||
|
||||
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
|
||||
|
||||
.PHONY: $(BUILD) clean all
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
all: $(BUILD)
|
||||
|
||||
$(BUILD):
|
||||
@[ -d $@ ] || mkdir -p $@
|
||||
@$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
clean:
|
||||
@echo clean ...
|
||||
@rm -fr $(BUILD) $(TARGET).pfs0 $(TARGET).nso $(TARGET).nro $(TARGET).nacp $(TARGET).elf
|
||||
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
else
|
||||
.PHONY: all
|
||||
|
||||
DEPENDS := $(OFILES:.o=.d)
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# main targets
|
||||
#---------------------------------------------------------------------------------
|
||||
all : $(OUTPUT).pfs0 $(OUTPUT).nro
|
||||
|
||||
$(OUTPUT).pfs0 : $(OUTPUT).nso
|
||||
|
||||
$(OUTPUT).nso : $(OUTPUT).elf
|
||||
|
||||
ifeq ($(strip $(NO_NACP)),)
|
||||
$(OUTPUT).nro : $(OUTPUT).elf $(OUTPUT).nacp
|
||||
else
|
||||
$(OUTPUT).nro : $(OUTPUT).elf
|
||||
endif
|
||||
|
||||
$(OUTPUT).elf : $(OFILES)
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# you need a rule like this for each extension you use as binary data
|
||||
#---------------------------------------------------------------------------------
|
||||
%.bin.o : %.bin
|
||||
#---------------------------------------------------------------------------------
|
||||
@echo $(notdir $<)
|
||||
@$(bin2o)
|
||||
|
||||
-include $(DEPENDS)
|
||||
|
||||
#---------------------------------------------------------------------------------------
|
||||
endif
|
||||
#---------------------------------------------------------------------------------------
|
BIN
exefs_src/main.npdm
Normal file
BIN
exefs_src/main.npdm
Normal file
Binary file not shown.
345
source/main.c
Normal file
345
source/main.c
Normal file
@ -0,0 +1,345 @@
|
||||
#include <switch.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include "nro.h"
|
||||
|
||||
#define MODULE_HBL 347
|
||||
|
||||
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?";
|
||||
|
||||
static char g_argv[2048];
|
||||
static char g_nextArgv[2048];
|
||||
static char g_nextNroPath[512];
|
||||
static u64 g_nroAddr = 0;
|
||||
static u64 g_nroSize = 0;
|
||||
static NroHeader g_nroHeader;
|
||||
|
||||
static u8 g_savedTls[0x100];
|
||||
|
||||
// Used by trampoline.s
|
||||
Result g_lastRet = 0;
|
||||
|
||||
extern void* __stack_top;//Defined in libnx.
|
||||
#define STACK_SIZE 0x100000 //Change this if main-thread stack size ever changes.
|
||||
|
||||
void __libnx_initheap(void)
|
||||
{
|
||||
static char g_innerheap[0x20000];
|
||||
|
||||
extern char* fake_heap_start;
|
||||
extern char* fake_heap_end;
|
||||
|
||||
fake_heap_start = &g_innerheap[0];
|
||||
fake_heap_end = &g_innerheap[sizeof g_innerheap];
|
||||
}
|
||||
|
||||
void __appInit(void)
|
||||
{
|
||||
(void) g_easterEgg[0];
|
||||
|
||||
Result rc;
|
||||
|
||||
rc = smInitialize();
|
||||
if (R_FAILED(rc))
|
||||
fatalSimple(MAKERESULT(MODULE_HBL, 1));
|
||||
|
||||
rc = fsInitialize();
|
||||
if (R_FAILED(rc))
|
||||
fatalSimple(MAKERESULT(MODULE_HBL, 2));
|
||||
|
||||
fsdevInit();
|
||||
}
|
||||
|
||||
static void* g_heapAddr;
|
||||
static size_t g_heapSize;
|
||||
|
||||
void setupHbHeap(void)
|
||||
{
|
||||
u64 size = 0;
|
||||
void* addr = NULL;
|
||||
u64 mem_available = 0, mem_used = 0;
|
||||
Result rc=0;
|
||||
|
||||
svcGetInfo(&mem_available, 6, CUR_PROCESS_HANDLE, 0);
|
||||
svcGetInfo(&mem_used, 7, CUR_PROCESS_HANDLE, 0);
|
||||
if (mem_available > mem_used+0x200000)
|
||||
size = (mem_available - mem_used - 0x200000) & ~0x1FFFFF;
|
||||
if (size==0)
|
||||
size = 0x2000000*16;
|
||||
|
||||
rc = svcSetHeapSize(&addr, size);
|
||||
|
||||
if (R_FAILED(rc) || addr==NULL)
|
||||
fatalSimple(MAKERESULT(MODULE_HBL, 9));
|
||||
|
||||
g_heapAddr = addr;
|
||||
g_heapSize = size;
|
||||
}
|
||||
|
||||
static Handle g_port;
|
||||
static Handle g_procHandle;
|
||||
|
||||
void threadFunc(void* ctx)
|
||||
{
|
||||
Handle session;
|
||||
Result rc;
|
||||
|
||||
rc = svcWaitSynchronizationSingle(g_port, -1);
|
||||
if (R_FAILED(rc))
|
||||
fatalSimple(MAKERESULT(MODULE_HBL, 22));
|
||||
|
||||
rc = svcAcceptSession(&session, g_port);
|
||||
if (R_FAILED(rc))
|
||||
fatalSimple(MAKERESULT(MODULE_HBL, 14));
|
||||
|
||||
s32 idx = 0;
|
||||
rc = svcReplyAndReceive(&idx, &session, 1, 0, -1);
|
||||
if (R_FAILED(rc))
|
||||
fatalSimple(MAKERESULT(MODULE_HBL, 15));
|
||||
|
||||
IpcParsedCommand ipc;
|
||||
rc = ipcParse(&ipc);
|
||||
if (R_FAILED(rc))
|
||||
fatalSimple(MAKERESULT(MODULE_HBL, 16));
|
||||
|
||||
if (ipc.NumHandles != 1)
|
||||
fatalSimple(MAKERESULT(MODULE_HBL, 17));
|
||||
|
||||
g_procHandle = ipc.Handles[0];
|
||||
svcCloseHandle(session);
|
||||
}
|
||||
|
||||
void getOwnProcessHandle(void)
|
||||
{
|
||||
static Thread t;
|
||||
Result rc;
|
||||
|
||||
rc = threadCreate(&t, &threadFunc, NULL, 0x1000, 0x20, 0);
|
||||
if (R_FAILED(rc))
|
||||
fatalSimple(MAKERESULT(MODULE_HBL, 10));
|
||||
|
||||
rc = smUnregisterService("appletOE");
|
||||
if (R_FAILED(rc))
|
||||
fatalSimple(MAKERESULT(MODULE_HBL, 11));
|
||||
|
||||
rc = smRegisterService(&g_port, "appletOE", false, 1);
|
||||
if (R_FAILED(rc))
|
||||
fatalSimple(MAKERESULT(MODULE_HBL, 12));
|
||||
|
||||
rc = threadStart(&t);
|
||||
if (R_FAILED(rc))
|
||||
fatalSimple(MAKERESULT(MODULE_HBL, 13));
|
||||
|
||||
Service srv;
|
||||
rc = smGetService(&srv, "appletOE");
|
||||
if (R_FAILED(rc))
|
||||
fatalSimple(MAKERESULT(MODULE_HBL, 23));
|
||||
|
||||
IpcCommand ipc;
|
||||
ipcSendHandleCopy(&ipc, 0xffff8001);
|
||||
|
||||
struct {
|
||||
int x, y;
|
||||
}* raw;
|
||||
|
||||
raw = ipcPrepareHeader(&ipc, sizeof(*raw));
|
||||
raw->x = raw->y = 0;
|
||||
|
||||
rc = serviceIpcDispatch(&srv);
|
||||
|
||||
threadWaitForExit(&t);
|
||||
threadClose(&t);
|
||||
|
||||
serviceClose(&srv);
|
||||
svcCloseHandle(g_port);
|
||||
|
||||
smExit();
|
||||
}
|
||||
|
||||
void loadNro(void)
|
||||
{
|
||||
NroHeader* header = NULL;
|
||||
size_t rw_size=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);
|
||||
|
||||
if (g_nroSize > 0)
|
||||
{
|
||||
// Unmap previous NRO.
|
||||
header = &g_nroHeader;
|
||||
rw_size = header->Segments[2].Size + header->bssSize;
|
||||
rw_size = (rw_size+0xFFF) & ~0xFFF;
|
||||
|
||||
// .text
|
||||
rc = svcUnmapProcessCodeMemory(
|
||||
g_procHandle, g_nroAddr + header->Segments[0].FileOff, ((u64) g_heapAddr) + header->Segments[0].FileOff, header->Segments[0].Size);
|
||||
|
||||
if (R_FAILED(rc))
|
||||
fatalSimple(MAKERESULT(MODULE_HBL, 24));
|
||||
|
||||
// .rodata
|
||||
rc = svcUnmapProcessCodeMemory(
|
||||
g_procHandle, g_nroAddr + header->Segments[1].FileOff, ((u64) g_heapAddr) + header->Segments[1].FileOff, header->Segments[1].Size);
|
||||
|
||||
if (R_FAILED(rc))
|
||||
fatalSimple(MAKERESULT(MODULE_HBL, 25));
|
||||
|
||||
// .data + .bss
|
||||
rc = svcUnmapProcessCodeMemory(
|
||||
g_procHandle, g_nroAddr + header->Segments[2].FileOff, ((u64) g_heapAddr) + header->Segments[2].FileOff, rw_size);
|
||||
|
||||
if (R_FAILED(rc))
|
||||
fatalSimple(MAKERESULT(MODULE_HBL, 26));
|
||||
|
||||
g_nroAddr = g_nroSize = 0;
|
||||
}
|
||||
|
||||
if (strlen(g_nextNroPath) == 0)
|
||||
{
|
||||
strcpy(g_nextNroPath, "sdmc:/hbmenu.nro");
|
||||
strcpy(g_nextArgv, "sdmc:/hbmenu.nro");
|
||||
}
|
||||
|
||||
memcpy(g_argv, g_nextArgv, sizeof g_argv);
|
||||
|
||||
uint8_t *nrobuf = (uint8_t*) g_heapAddr;
|
||||
|
||||
NroStart* start = (NroStart*) (nrobuf + 0);
|
||||
header = (NroHeader*) (nrobuf + sizeof(NroStart));
|
||||
uint8_t* rest = (uint8_t*) (nrobuf + sizeof(NroStart) + sizeof(NroHeader));
|
||||
|
||||
FILE* f = fopen(g_nextNroPath, "rb");
|
||||
if (f == NULL)
|
||||
fatalSimple(MAKERESULT(MODULE_HBL, 3));
|
||||
|
||||
// Reset NRO path to load hbmenu by default next time.
|
||||
g_nextNroPath[0] = '\0';
|
||||
|
||||
if (fread(start, sizeof(*start), 1, f) != 1)
|
||||
fatalSimple(MAKERESULT(MODULE_HBL, 4));
|
||||
|
||||
if (fread(header, sizeof(*header), 1, f) != 1)
|
||||
fatalSimple(MAKERESULT(MODULE_HBL, 4));
|
||||
|
||||
if(header->Magic != NROHEADER_MAGICNUM)
|
||||
fatalSimple(MAKERESULT(MODULE_HBL, 5));
|
||||
|
||||
size_t rest_size = header->size - (sizeof(NroStart) + sizeof(NroHeader));
|
||||
if (fread(rest, rest_size, 1, f) != 1)
|
||||
fatalSimple(MAKERESULT(MODULE_HBL, 7));
|
||||
|
||||
fclose(f);
|
||||
|
||||
size_t total_size = header->size + header->bssSize;
|
||||
total_size = (total_size+0xFFF) & ~0xFFF;
|
||||
|
||||
rw_size = header->Segments[2].Size + header->bssSize;
|
||||
rw_size = (rw_size+0xFFF) & ~0xFFF;
|
||||
|
||||
int i;
|
||||
for (i=0; i<3; i++)
|
||||
{
|
||||
if (header->Segments[i].FileOff >= header->size || header->Segments[i].Size > header->size ||
|
||||
(header->Segments[i].FileOff + header->Segments[i].Size) > header->size)
|
||||
{
|
||||
fatalSimple(MAKERESULT(MODULE_HBL, 6));
|
||||
}
|
||||
}
|
||||
|
||||
// todo: Detect whether NRO fits into heap or not.
|
||||
|
||||
// Copy header to elsewhere because we're going to unmap it next.
|
||||
memcpy(&g_nroHeader, header, sizeof(g_nroHeader));
|
||||
header = &g_nroHeader;
|
||||
|
||||
u64 map_addr;
|
||||
|
||||
do {
|
||||
map_addr = randomGet64() & 0xFFFFFF000ull;
|
||||
rc = svcMapProcessCodeMemory(g_procHandle, map_addr, (u64)nrobuf, total_size);
|
||||
|
||||
} while (rc == 0xDC01 || rc == 0xD401);
|
||||
|
||||
if (R_FAILED(rc))
|
||||
fatalSimple(MAKERESULT(MODULE_HBL, 18));
|
||||
|
||||
// .text
|
||||
rc = svcSetProcessMemoryPermission(
|
||||
g_procHandle, map_addr + header->Segments[0].FileOff, header->Segments[0].Size, PERM_R | PERM_X);
|
||||
|
||||
if (R_FAILED(rc))
|
||||
fatalSimple(MAKERESULT(MODULE_HBL, 19));
|
||||
|
||||
// .rodata
|
||||
rc = svcSetProcessMemoryPermission(
|
||||
g_procHandle, map_addr + header->Segments[1].FileOff, header->Segments[1].Size, PERM_R);
|
||||
|
||||
if (R_FAILED(rc))
|
||||
fatalSimple(MAKERESULT(MODULE_HBL, 20));
|
||||
|
||||
// .data + .bss
|
||||
rc = svcSetProcessMemoryPermission(
|
||||
g_procHandle, map_addr + header->Segments[2].FileOff, rw_size, PERM_RW);
|
||||
|
||||
if (R_FAILED(rc))
|
||||
fatalSimple(MAKERESULT(MODULE_HBL, 21));
|
||||
|
||||
u64 nro_size = header->Segments[2].FileOff + rw_size;
|
||||
u64 nro_heap_start = ((u64) g_heapAddr) + nro_size;
|
||||
u64 nro_heap_size = g_heapSize + (u64) g_heapAddr - (u64) nro_heap_start;
|
||||
|
||||
#define M EntryFlag_IsMandatory
|
||||
|
||||
static ConfigEntry entries[] = {
|
||||
{ EntryType_MainThreadHandle, 0, {0, 0} },
|
||||
{ EntryType_ProcessHandle, 0, {0, 0} },
|
||||
{ EntryType_AppletType, 0, {AppletType_LibraryApplet, 0} },
|
||||
{ EntryType_OverrideHeap, M, {0, 0} },
|
||||
{ EntryType_Argv, 0, {0, 0} },
|
||||
{ EntryType_NextLoadPath, 0, {0, 0} },
|
||||
{ EntryType_LastLoadResult, 0, {0, 0} },
|
||||
{ EntryType_SyscallAvailableHint, 0, {0xffffffffffffffff, 0x1fc00000007ffff} },
|
||||
{ EntryType_EndOfList, 0, {0, 0} }
|
||||
};
|
||||
|
||||
// MainThreadHandle
|
||||
entries[0].Value[0] = envGetMainThreadHandle();
|
||||
// ProcessHandle
|
||||
entries[1].Value[0] = g_procHandle;
|
||||
// OverrideHeap
|
||||
entries[3].Value[0] = nro_heap_start;
|
||||
entries[3].Value[1] = nro_heap_size;
|
||||
// Argv
|
||||
entries[4].Value[1] = (u64) &g_argv[0];
|
||||
// NextLoadPath
|
||||
entries[5].Value[0] = (u64) &g_nextNroPath[0];
|
||||
entries[5].Value[1] = (u64) &g_nextArgv[0];
|
||||
// LastLoadResult
|
||||
entries[6].Value[0] = g_lastRet;
|
||||
|
||||
u64 entrypoint = map_addr;
|
||||
|
||||
g_nroAddr = map_addr;
|
||||
g_nroSize = nro_size;
|
||||
|
||||
memset(__stack_top - STACK_SIZE, 0, STACK_SIZE);
|
||||
|
||||
extern NORETURN void nroEntrypointTrampoline(u64 entries_ptr, u64 handle, u64 entrypoint);
|
||||
nroEntrypointTrampoline((u64) entries, -1, entrypoint);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
memcpy(g_savedTls, (u8*)armGetTls() + 0x100, 0x100);
|
||||
|
||||
setupHbHeap();
|
||||
getOwnProcessHandle();
|
||||
loadNro();
|
||||
|
||||
fatalSimple(MAKERESULT(MODULE_HBL, 8));
|
||||
return 0;
|
||||
}
|
42
source/nro.h
Normal file
42
source/nro.h
Normal file
@ -0,0 +1,42 @@
|
||||
#pragma once
|
||||
|
||||
#define NROHEADER_MAGICNUM 0x304f524e
|
||||
|
||||
#define ASSETHEADER_MAGICNUM 0x54455341
|
||||
#define ASSETHEADER_VERSION 0
|
||||
|
||||
typedef struct {
|
||||
u32 FileOff;
|
||||
u32 Size;
|
||||
} NsoSegment;
|
||||
|
||||
typedef struct {
|
||||
u32 unused;
|
||||
u32 modOffset;
|
||||
u8 Padding[8];
|
||||
} NroStart;
|
||||
|
||||
typedef struct {
|
||||
u32 Magic;
|
||||
u32 Unk1;
|
||||
u32 size;
|
||||
u32 Unk2;
|
||||
NsoSegment Segments[3];
|
||||
u32 bssSize;
|
||||
u32 Unk3;
|
||||
u8 BuildId[0x20];
|
||||
u8 Padding[0x20];
|
||||
} NroHeader;
|
||||
|
||||
typedef struct {
|
||||
u64 offset;
|
||||
u64 size;
|
||||
} AssetSection;
|
||||
|
||||
typedef struct {
|
||||
u32 magic;
|
||||
u32 version;
|
||||
AssetSection icon;
|
||||
AssetSection nacp;
|
||||
AssetSection romfs;
|
||||
} AssetHeader;
|
31
source/trampoline.s
Normal file
31
source/trampoline.s
Normal file
@ -0,0 +1,31 @@
|
||||
.section .text.nroEntrypointTrampoline, "ax", %progbits
|
||||
|
||||
.global nroEntrypointTrampoline
|
||||
.type nroEntrypointTrampoline, %function
|
||||
.align 2
|
||||
|
||||
.cfi_startproc
|
||||
|
||||
nroEntrypointTrampoline:
|
||||
|
||||
// Reset stack pointer.
|
||||
adrp x8, __stack_top //Defined in libnx.
|
||||
ldr x8, [x8, #:lo12:__stack_top]
|
||||
mov sp, x8
|
||||
|
||||
// Call NRO.
|
||||
blr x2
|
||||
|
||||
// Save retval
|
||||
adrp x1, g_lastRet
|
||||
add x1, x1, #:lo12:g_lastRet
|
||||
str x0, [x1]
|
||||
|
||||
// Reset stack pointer and load next NRO.
|
||||
adrp x8, __stack_top
|
||||
ldr x8, [x8, #:lo12:__stack_top]
|
||||
mov sp, x8
|
||||
|
||||
b loadNro
|
||||
|
||||
.cfi_endproc
|
Loading…
Reference in New Issue
Block a user