aboutsummaryrefslogtreecommitdiff
path: root/libpsn00b/libc/start.c
diff options
context:
space:
mode:
authorJohn "Lameguy" Wilbert Villamor <lameguy64@gmail.com>2021-08-31 13:23:20 +0800
committerGitHub <noreply@github.com>2021-08-31 13:23:20 +0800
commitffa679d4d24b891cb59aba10946368f2ec00c391 (patch)
tree0cf6061915ebf48acdedf6d77b0c1b76eec5b8c3 /libpsn00b/libc/start.c
parent317dc2b91d3afcdbaddb035f38611d12af161970 (diff)
parentf2fc18f82dd7900465d6ab3ae2080726d5589d39 (diff)
downloadpsn00bsdk-ffa679d4d24b891cb59aba10946368f2ec00c391.tar.gz
Merge pull request #36 from spicyjpeg/dynlink
Dynamic linker, gp-relative addressing, ldscripts and more
Diffstat (limited to 'libpsn00b/libc/start.c')
-rw-r--r--libpsn00b/libc/start.c181
1 files changed, 93 insertions, 88 deletions
diff --git a/libpsn00b/libc/start.c b/libpsn00b/libc/start.c
index 354ebb9..c5872df 100644
--- a/libpsn00b/libc/start.c
+++ b/libpsn00b/libc/start.c
@@ -1,110 +1,115 @@
-#include <stdio.h>
+/*
+ * PSn00bSDK startup code
+ * (C) 2021 Lameguy64, spicyjpeg - MPL licensed
+ */
+
+#include <sys/types.h>
#include <string.h>
#include <malloc.h>
-#define load_gp() __asm__ volatile ( \
- "la $gp, _gp;" )
-
-extern int _end;
-extern int main(int argc, const char* argv[]);
+#define KERNEL_ARG_STRING ((const char *) 0x80000180)
+#define KERNEL_RETURN_VALUE ((volatile int *) 0x8000dffc)
-void _mem_init(void);
+/* Argument parsing */
-int __argc;
+int32_t __argc;
const char **__argv;
-static const char *_arg_ptrs_int[8];
-static char _arg_buff[132];
+#define ARGC_MAX 16
-static void _call_global_ctors(void)
-{
- extern void (*__CTOR_LIST__[])(void);
+static const char *_argv_buffer[ARGC_MAX];
+static char _arg_string_buffer[132];
- // Constructors are called in reverse order of the list
- int i;
- for (i = (int)__CTOR_LIST__[0]; i >= 1; i--) {
- // Each function handles one or more destructor (within
- // file scope)
- __CTOR_LIST__[i]();
+static void _parse_kernel_args() {
+ // Copy the argument string from kernel memory into a private buffer (which
+ // won't be cleared or deallocated) and trim it at the first newline.
+ memset(_arg_string_buffer, 0, 132);
+ strncpy(_arg_string_buffer, KERNEL_ARG_STRING, 128);
+
+ for (char *ptr = _arg_string_buffer; *ptr; ptr++) {
+ if ((*ptr == '\r') || (*ptr == '\n')) {
+ *ptr = 0;
+ break;
+ }
}
-}
-static void _call_global_dtors(void)
-{
- extern void (*__DTOR_LIST__[])(void);
+ __argv = _argv_buffer;
+ for (__argc = 0; __argc < ARGC_MAX; __argc++) {
+ const char *ptr;
+ if (!__argc)
+ ptr = strtok(_arg_string_buffer, " ");
+ else
+ ptr = strtok(0, " ");
- /* Destructors in forward order */
- int i;
- for (i = 0; i < (int)__DTOR_LIST__[0]; i++) {
- /* Each function handles one or more destructor (within
- * file scope) */
- __DTOR_LIST__[i + 1]();
+ _argv_buffer[__argc] = ptr;
+ if (!ptr)
+ break;
}
}
-void _parse_args( int argc, const char *args[] )
-{
- int i;
- char *c,*s;
-
- memset( _arg_buff, 0, 132 );
-
- if( !args )
- {
- // Use arguments from kernel if args is NULL
- strncpy( _arg_buff, (char*)0x180, 128 );
-
- // Clean-up args froom stray line-ends
- while( ( c = strrchr( _arg_buff, '\r' ) ) ||
- ( c = strrchr( _arg_buff, '\n' ) ) )
- *c = 0;
- }
- else
- {
- __argc = argc;
- __argv = args;
- return;
- }
-
- __argc = 0;
- for( i=0; i<8; i++ )
- _arg_ptrs_int[i] = 0;
-
- s = _arg_buff;
- while( c = strtok( s, " " ) )
- {
- _arg_ptrs_int[__argc] = c;
- __argc++;
- s = NULL;
- if( __argc >= 8 )
- break;
- }
-
- __argv = _arg_ptrs_int;
-
-} /* parse_args */
+/* Main */
+
+// How much space at the end of RAM to leave for the stack (instead of using it
+// as heap). By default 128 KB are reserved for the stack, but this constant
+// can be overridden in main.c (or anywhere else) simply by redeclaring it
+// without the weak attribute.
+const int32_t __attribute__((weak)) STACK_MAX_SIZE = 0x20000;
-void _start( int argc, const char *args[] )
-{
- // Load GP address
- load_gp();
+// These are defined by the linker script. Note that these are *NOT* pointers,
+// they are virtual symbols whose location matches their value. The simplest
+// way to turn them into pointers is to declare them as arrays, so here we go.
+extern uint8_t __text_start[];
+extern uint8_t __bss_start[];
+extern uint8_t _end[];
+//extern uint8_t _gp[];
+
+extern void (*__CTOR_LIST__[])(void);
+extern void (*__DTOR_LIST__[])(void);
+
+extern int32_t main(int32_t argc, const char* argv[]);
+
+// Even though _start() usually takes no arguments, this implementation allows
+// parent executables to pass args directly to child executables without having
+// to overwrite the arg strings in kernel RAM.
+void _start(int32_t override_argc, const char **override_argv) {
+ __asm__ volatile("la $gp, _gp;");
// Mem init assembly function (clears BSS and InitHeap to _end which is
// not possible to do purely in C because the linker complains about
// relocation truncated to fit: R_MIPS_GPREL16 against `_end'
// Workaround is to do it in assembly because la pseudo-op doesn't use
// stupid gp relative addressing
- _mem_init();
-
- // process command line arguments
- _parse_args( argc, args );
-
- _call_global_ctors();
-
- *((int*)0x8000DFFC) = main( __argc, __argv );
-
- _call_global_dtors();
-
- // Set return value to kernel return value area
-
-} /* _start */ \ No newline at end of file
+ //_mem_init();
+
+ // Clear BSS 4 bytes at a time. BSS is always aligned to 4 bytes by the
+ // linker script.
+ for (uint32_t *i = (uint32_t *) __bss_start; i < (uint32_t *) _end; i++)
+ *i = 0;
+
+ // Calculate how much RAM is available after the loaded executable and
+ // initialize heap accordingly.
+ void *exe_end = _end + 4;
+ unsigned int exe_size = (unsigned int) exe_end - (unsigned int) __text_start;
+ InitHeap(exe_end, 0x200000 - (exe_size + STACK_MAX_SIZE));
+
+ if (override_argv) {
+ __argc = override_argc;
+ __argv = override_argv;
+ } else {
+ _parse_kernel_args();
+ }
+
+ // Call the global constructors (if any) to initialize global objects
+ // before calling main(). Constructors are put by the linker script in a
+ // length-prefixed array in reverse order.
+ for (uint32_t i = (uint32_t) __CTOR_LIST__[0]; i >= 1; i--)
+ __CTOR_LIST__[i]();
+
+ // Store main()'s return value into the kernel return value area (for child
+ // executables).
+ *KERNEL_RETURN_VALUE = main(__argc, __argv);
+
+ // Call global destructors (in forward order).
+ for (uint32_t i = 0; i < (uint32_t) __DTOR_LIST__[0]; i++)
+ __DTOR_LIST__[i + 1]();
+}