libnx/nx/source/runtime/init.c

153 lines
3.8 KiB
C

#include "types.h"
#include "runtime/env.h"
#include "services/sm.h"
#include "services/fatal.h"
#include "services/fs.h"
#include "services/hid.h"
#include "services/applet.h"
#include "runtime/devices/fs_dev.h"
void* __stack_top;
void NORETURN __nx_exit(Result rc, LoaderReturnFn retaddr);
void virtmemSetup(void);
void newlibSetup(void);
void argvSetup(void);
extern u32 __nx_applet_type;
// Must be a multiple of 0x200000.
__attribute__((weak)) size_t __nx_heap_size = 0;
/*
There are three ways of allocating heap:
- Normal syscall:
Allocates heap using |svcSetHeapSize|. The size is provided by a weak symbol
called |__nx_heap_size|. With the default 0 value, the size is automatically
determined with svcGetInfo. If running under a process where heap was already
allocated with svcSetHeapSize, __nx_heap_size should be set manually.
- Heap override:
Uses existing heap segment as provided by the homebrew loader environment
block. This happens automatically if such a setting is provided by the
homebrew environment.
- Custom override:
A program can override the weak symbol |__libnx_initheap| to setup a
different heap. In this case, the global variables |fake_heap_start|
and |fake_heap_end| needs to be set appropriately.
A custom override can be used to implement an "inner heap" located in the .bss
segment of a process, for example.
*/
void __attribute__((weak)) __libnx_initheap(void)
{
void* addr;
size_t size = 0;
size_t mem_available = 0, mem_used = 0;
if (envHasHeapOverride()) {
addr = envGetHeapOverrideAddr();
size = envGetHeapOverrideSize();
}
else {
if (__nx_heap_size==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;
}
else {
size = __nx_heap_size;
}
Result rc = svcSetHeapSize(&addr, size);
if (R_FAILED(rc))
fatalSimple(MAKERESULT(Module_Libnx, LibnxError_HeapAllocFailed));
}
// Newlib
extern char* fake_heap_start;
extern char* fake_heap_end;
fake_heap_start = (char*)addr;
fake_heap_end = (char*)addr + size;
}
void __attribute__((weak)) __appInit(void)
{
Result rc;
// Initialize default services.
rc = smInitialize();
if (R_FAILED(rc))
fatalSimple(MAKERESULT(Module_Libnx, LibnxError_InitFail_SM));
rc = appletInitialize();
if (R_FAILED(rc))
fatalSimple(MAKERESULT(Module_Libnx, LibnxError_InitFail_AM));
if (__nx_applet_type != AppletType_None) {
rc = hidInitialize();
if (R_FAILED(rc))
fatalSimple(MAKERESULT(Module_Libnx, LibnxError_InitFail_HID));
}
rc = fsInitialize();
if (R_FAILED(rc))
fatalSimple(MAKERESULT(Module_Libnx, LibnxError_InitFail_FS));
fsdevInit();
}
void __attribute__((weak)) __appExit(void)
{
// Cleanup default services.
fsdevExit();
fsExit();
hidExit();
appletExit();
smExit();
}
void __attribute__((weak)) __libnx_init(void* ctx, Handle main_thread, void* saved_lr)
{
// Called by crt0.
// Libnx initialization goes here.
envSetup(ctx, main_thread, saved_lr);
newlibSetup();
virtmemSetup();
__libnx_initheap();
// Build argc/argv if present
argvSetup();
// Initialize services.
__appInit();
// Call constructors.
void __libc_init_array(void);
__libc_init_array();
}
void __attribute__((weak)) NORETURN __libnx_exit(int rc)
{
// Call destructors.
void __libc_fini_array(void);
__libc_fini_array();
// Clean up services.
__appExit();
__nx_exit(0, envGetExitFuncPtr());
}