sdk-core v2026.11.05
PS2 SDK - core tier (build-critical EE/IOP modules)
Loading...
Searching...
No Matches
crt0.c
1/*
2# _____ ___ ____ ___ ____
3# ____| | ____| | | |____|
4# | ___| |____ ___| ____| | \ PS2DEV Open Source Project.
5#-----------------------------------------------------------------------
6# Copyright (c) 2003 Marcus R. Brown <mrbrown@0xd6.org>
7# Licenced under Academic Free License version 2.0
8# Review ps2sdk README & LICENSE files for further details.
9*/
10
11#include <kernel.h>
12#include <stdio.h>
13#include <stdint.h>
14#include <stdnoreturn.h> // noreturn
15#include <string.h>
16#include <startup.h>
17
18extern char* _end;
19extern char* _heap_size;
20extern char* _fbss;
21extern char* _stack;
22extern char* _stack_size;
23
24__attribute__((weak)) void _ps2sdk_memory_init();
25__attribute__((weak)) void _init();
26__attribute__((weak)) void _fini();
27
28// Real, confirmed bug (2026-09-07): __attribute__((constructor)) never ran
29// anywhere in this SDK, on real hardware or any emulator - "if (_init)
30// _init();" below was always a no-op. _init/__libc_init_array are
31// normally provided by gcc's own crtbegin.o/crtend.o, which -nostartfiles
32// (required here - this file *is* the startup code) never links. This
33// compiler emits the legacy .ctors section (confirmed via objdump - not
34// .init_array), so crtbegin.o's own __do_global_ctors_aux is what would
35// have walked it; without that object, nothing does. __CTOR_LIST__/
36// __CTOR_END__ (provided by the linkfile, see its own comment) bound the
37// section directly, sidestepping the need for crtbegin.o's own -1
38// start-sentinel/0 end-terminator convention entirely. Real GCC .ctors
39// calling order is reverse-array (highest address/lowest priority
40// first), matching __do_global_ctors_aux's own real behavior.
41extern void (*__CTOR_LIST__[])(void);
42extern void (*__CTOR_END__[])(void);
43
44// clang emits .init_array instead of .ctors for global/static C++
45// constructors - see the linkfile's own comment on __init_array_start/
46// __init_array_end. Unlike .ctors (walked in reverse), .init_array runs
47// in forward array order per the Itanium C++ ABI.
48extern void (*__init_array_start[])(void);
49extern void (*__init_array_end[])(void);
50
51// Real, confirmed bug: any global/static C++ object with a non-trivial
52// destructor fails to link with "undefined reference to `__dso_handle`".
53// GCC emits a __cxa_atexit(dtor, obj, &__dso_handle) call at that
54// object's construction point; __dso_handle is normally defined by
55// gcc's own crtbegin.o, which -nostartfiles (required here - this file
56// *is* the startup code, see above) never links. A statically-linked
57// PS2 ELF is always exactly one "module" (no dlclose, no shared
58// objects), so the value only needs to be a stable, unique address -
59// same minimal fix used by other no-crtbegin bare-metal targets
60// (avr-libc and similar). weak so a real crtbegin.o's own definition
61// would still win if some future path ever links one instead.
62void *__dso_handle __attribute__((weak)) = &__dso_handle;
63
64static void _call_global_ctors(void)
65{
66 int count = __CTOR_END__ - __CTOR_LIST__;
67 for (int i = count - 1; i >= 0; i--)
68 __CTOR_LIST__[i]();
69
70 int init_array_count = __init_array_end - __init_array_start;
71 for (int i = 0; i < init_array_count; i++)
72 __init_array_start[i]();
73}
74
75void _libcglue_args_parse(int argc, char** argv);
76void _libcglue_init();
77void _libcglue_deinit();
78int main(int argc, char** argv);
79
80// __attribute__((used)): _main is only reachable via the raw "j _main"
81// below, not a real C call the compiler can see - without this it gets
82// optimized away as dead code before the asm ever runs. Real linker
83// relocation from that jump keeps it past --gc-sections regardless.
84static void _main() __attribute__((used));
85
86static struct sargs args;
87static struct sargs_start *args_start;
88
89/*
90 * First function to be called by the loader
91 * This function sets up the stack and heap.
92 * DO NOT USE THE STACK IN THIS FUNCTION!
93 */
94void __start(struct sargs_start *pargs)
95{
96 __asm__ __volatile__(
97 "# Clear bss area \n"
98 ".set noat \n"
99 "la $2, _fbss \n"
100 "la $3, _end \n"
101 "1: \n"
102 "sltu $1, $2, $3 \n"
103 "beq $1, $0, 2f \n"
104 "nop \n"
105 "sq $0, ($2) \n"
106 "addiu $2, $2, 16 \n"
107 "j 1b \n"
108 "nop \n"
109 "2: \n"
110 " \n"
111 "# Save first argument \n"
112 "sw %1, %0 \n"
113 " \n"
114 "# SetupThread \n"
115 "la $4, _gp \n"
116 "la $5, _stack \n"
117 "la $6, _stack_size \n"
118 "la $7, args \n"
119 "la $8, ExitThread \n"
120 "move $gp, $4 \n"
121 "addiu $3, $0, 60 \n"
122 "syscall \n"
123 "move $sp, $2 \n"
124 " \n"
125 "# Jump to _main \n"
126 "j _main \n"
127 ".set at \n"
128 : /* No outputs. */
129 : "m"(args_start), "r"(pargs)
130 : "1", "2", "3", "4", "5", "6", "7", "8");
131}
132
133/*
134 * Intermediate function between _start and main, stack can be used as normal.
135 */
136static void _main()
137{
138 int retval;
139 struct sargs *pa;
140
141 // initialize heap
142 SetupHeap(&_end, (int)&_heap_size);
143
144 // writeback data cache
145 FlushCache(0);
146
147 // Capability to override 32MiB on DESR/DVR models
148 // NOTE: this call can restart the application
149 if (_ps2sdk_memory_init)
150 _ps2sdk_memory_init();
151
152 // Initialize the kernel (Apply necessary patches).
153 _InitSys();
154
155 // Use arguments sent through start if sent (by ps2link for instance)
156 pa = &args;
157 if (args.argc == 0 && args_start != NULL && args_start->args.argc != 0)
158 pa = &args_start->args;
159
160 // call libcglue argument parsing
161 _libcglue_args_parse(pa->argc, pa->argv);
162
163 // initialize libcglue
164 _libcglue_init();
165
166 // Enable interruts
167 EI();
168
169 // call global constructors - see _call_global_ctors's own comment
170 // for why this replaces the old (always-dead) "if (_init) _init();"
171 _call_global_ctors();
172
173 // call main
174 retval = main(pa->argc, pa->argv);
175
176 // call global destructors (weak)
177 if (_fini)
178 _fini();
179
180 // uninitialize libcglue
181 _libcglue_deinit();
182
183 // Exit
184 Exit(retval);
185}
186
187noreturn void _exit(int status)
188{
189 // call global destructors (weak)
190 if (_fini)
191 _fini();
192
193 // uninitialize libcglue
194 _libcglue_deinit();
195
196 // Exit
197 Exit(status);
198}
u32 count
start sector of fragmented bd/file