sdk-core v2026.11.05
PS2 SDK - core tier (build-critical EE/IOP modules)
Loading...
Searching...
No Matches
elf.c
1/*
2# _____ ___ ____ ___ ____
3# ____| | ____| | | |____|
4# | ___| |____ ___| ____| | \ PS2DEV Open Source Project.
5#-----------------------------------------------------------------------
6# Copyright ps2dev - http://www.ps2dev.org
7# Licenced under Academic Free License version 2.0
8# Review ps2sdk README & LICENSE files for further details.
9*/
10
11#include "elf_loader_common.h"
12
13#include <kernel.h>
14#include <string.h>
15
16// Loader ELF variables
17extern u8 ldrsrc[];
18extern int size_ldrsrc;
19
20int elf_loader_is_elf_ehdr_valid(const elf_loader_elf32_ehdr_t *ehdr)
21{
22 int ret;
23
24 ret = 0;
25
26 if ( memcmp(
27 ehdr->e_ident,
28 "\x7F"
29 "ELF",
30 4) )
31 {
32 ret = -9;
33 }
34 else if ( ehdr->e_entry < 0x80000 || ehdr->e_entry > (32 * 1024 * 1024) )
35 {
36 ret = -10;
37 }
38 else if ( ehdr->e_ident[4] != 1 )
39 {
40 ret = -1;
41 }
42 else if ( ehdr->e_ident[5] != 1 )
43 {
44 ret = -2;
45 }
46 else if ( ehdr->e_type != 2 )
47 {
48 ret = -3;
49 }
50 else if ( ehdr->e_machine != 8 )
51 {
52 ret = -4;
53 }
54 else if ( ehdr->e_ehsize != sizeof(elf_loader_elf32_ehdr_t) )
55 {
56 ret = -5;
57 }
58 else if ( ehdr->e_phentsize != sizeof(elf_loader_elf32_phdr_t) )
59 {
60 ret = -6;
61 }
62 else if ( ehdr->e_phnum > ELF_LOADER_MAX_PROGRAM_HEADERS )
63 {
64 ret = -7;
65 }
66 else if ( ehdr->e_shnum && ehdr->e_shentsize != 40 )
67 {
68 ret = -8;
69 }
70 return ret;
71}
72
73int elf_loader_is_elf_valid_for_loading(const void *buf, size_t buf_size)
74{
75 int i;
76 int ret;
77 int ph_count;
78 int entrypoint_in_ph;
79 u32 highest_vaddr;
80 u32 highest_offset;
81 const elf_loader_elf32_ehdr_t *ehdr;
82 const elf_loader_elf32_phdr_t *phdr;
83
84 if ( buf_size < sizeof(elf_loader_elf32_ehdr_t) )
85 return -30;
86 ehdr = (const elf_loader_elf32_ehdr_t *)buf;
87 ret = elf_loader_is_elf_ehdr_valid(ehdr);
88 if ( ret )
89 return ret;
90 if ( buf_size < (ehdr->e_phoff + sizeof(elf_loader_elf32_ehdr_t)) )
91 return -31;
92 phdr = (const elf_loader_elf32_phdr_t *)(((const u8 *)buf) + ehdr->e_phoff);
93 ph_count = 0;
94 entrypoint_in_ph = 0;
95 highest_vaddr = 0x80000;
96 highest_offset = 0;
97 for ( i = 0; i < ehdr->e_phnum; i += 1 )
98 {
99 if ( phdr[i].p_type == 1 && phdr[i].p_filesz != 0 )
100 {
101 // Program headers need to be pre-sorted
102 if ( phdr[i].p_vaddr < highest_vaddr || phdr[i].p_offset < highest_offset )
103 {
104 return -20;
105 }
106 highest_vaddr = phdr[i].p_vaddr;
107 highest_offset = phdr[i].p_offset;
108 if ( ehdr->e_entry >= phdr[i].p_vaddr && (phdr[i].p_vaddr + phdr[i].p_filesz) > ehdr->e_entry )
109 {
110 entrypoint_in_ph = 1;
111 }
112 ph_count += 1;
113 }
114 }
115 if ( !ph_count || ph_count > ELF_LOADER_MAX_PROGRAM_HEADERS )
116 {
117 return -21;
118 }
119 if ( !entrypoint_in_ph )
120 {
121 return -22;
122 }
123 if ( buf_size < highest_offset )
124 return -32;
125 return 0;
126}
127
128int elf_loader_exec_elf_prepare_loadinfo(elf_loader_execinfo_t *execinfo, const void *buf, size_t buf_size)
129{
130 int ret;
131 const elf_loader_elf32_ehdr_t *ehdr;
132 const elf_loader_elf32_phdr_t *phdr;
133 int i;
134 u32 min_load_addr;
135 u32 max_load_addr;
136 u32 info_count;
137 u32 memsize;
138
139 ret = elf_loader_is_elf_valid_for_loading(buf, buf_size);
140 if ( ret )
141 return ret;
142 ehdr = (const elf_loader_elf32_ehdr_t *)buf;
143 phdr = (const elf_loader_elf32_phdr_t *)(((const u8 *)buf) + ehdr->e_phoff);
144 memsize = GetMemorySize();
145
146 min_load_addr = 0xFFFFFFFF;
147 max_load_addr = 0x00100000;
148
149 info_count = 0;
150 for ( i = 0; i < ehdr->e_phnum; i += 1 )
151 {
152 if ( phdr[i].p_type == 1 && phdr[i].p_filesz != 0 )
153 {
154 // Item: move memory down
155 execinfo->loaderinfo.items[info_count].dest_addr = (void *)(phdr[i].p_vaddr);
156 execinfo->loaderinfo.items[info_count].src_addr = (void *)(((u8 *)buf) + phdr[i].p_offset);
157 execinfo->loaderinfo.items[info_count].size = phdr[i].p_filesz;
158 {
159 u32 load_address;
160 u32 end_load_address;
161 load_address = phdr[i].p_vaddr;
162 end_load_address = load_address + phdr[i].p_filesz;
163 if ( load_address < min_load_addr )
164 {
165 min_load_addr = load_address;
166 }
167 if ( end_load_address > max_load_addr )
168 {
169 max_load_addr = end_load_address;
170 }
171 }
172 if (
173 (u8 *)(execinfo->loaderinfo.items[info_count].dest_addr)
174 >= ((u8 *)execinfo->loaderinfo.items[info_count].src_addr) )
175 break;
176 info_count += 1;
177 }
178 }
179 for ( i = ehdr->e_phnum - 1; i >= 0; i -= 1 )
180 {
181 if ( phdr[i].p_type == 1 && phdr[i].p_filesz != 0 )
182 {
183 // Item: move memory up
184 execinfo->loaderinfo.items[info_count].dest_addr = (void *)(phdr[i].p_vaddr);
185 execinfo->loaderinfo.items[info_count].src_addr = (void *)(((u8 *)buf) + phdr[i].p_offset);
186 execinfo->loaderinfo.items[info_count].size = phdr[i].p_filesz;
187 {
188 u32 load_address;
189 u32 end_load_address;
190 load_address = phdr[i].p_vaddr;
191 end_load_address = load_address + phdr[i].p_filesz;
192 if ( load_address < min_load_addr )
193 {
194 min_load_addr = load_address;
195 }
196 if ( end_load_address > max_load_addr )
197 {
198 max_load_addr = end_load_address;
199 }
200 }
201 if (
202 (u8 *)(execinfo->loaderinfo.items[info_count].dest_addr)
203 <= ((u8 *)execinfo->loaderinfo.items[info_count].src_addr) )
204 break;
205 info_count += 1;
206 }
207 }
208 if ( min_load_addr != 0xFFFFFFFF )
209 {
210 u32 wanted_size;
211 wanted_size = min_load_addr - 0x00100000;
212 if ( wanted_size != 0 )
213 {
214 // Item: clear memory
215 execinfo->loaderinfo.items[info_count].dest_addr = (void *)0x00100000;
216 execinfo->loaderinfo.items[info_count].src_addr = (void *)NULL;
217 execinfo->loaderinfo.items[info_count].size = wanted_size;
218 info_count += 1;
219 }
220 }
221 if ( max_load_addr != 0x00100000 )
222 {
223 u32 wanted_size;
224 wanted_size = memsize - (uiptr)max_load_addr;
225 // Item: clear memory
226 if ( wanted_size != 0 )
227 {
228 execinfo->loaderinfo.items[info_count].dest_addr = (void *)max_load_addr;
229 execinfo->loaderinfo.items[info_count].src_addr = (void *)NULL;
230 execinfo->loaderinfo.items[info_count].size = wanted_size;
231 info_count += 1;
232 }
233 }
234 // Item: execute entry point
235 execinfo->loaderinfo.items[info_count].dest_addr = (void *)(ehdr->e_entry);
236 execinfo->loaderinfo.items[info_count].src_addr = (void *)NULL; // GP returned is always NULL
237 execinfo->loaderinfo.items[info_count].size = 0;
238
239 return 0;
240}
241
242int elf_loader_exec_elf_prepare_arginfo(
243 elf_loader_execinfo_t *execinfo, const char *filename, const char *partition, int argc, char *argv[])
244{
245 char *ptr;
246 int len, i;
247
248 ptr = execinfo->arginfo.payload;
249 argc = (argc > 15) ? 15 : argc;
250 execinfo->arginfo.argc = argc + 1;
251 // Make pointer relative to payload, so that it can be relocated later
252 execinfo->arginfo.argv[0] = (char *)((uiptr)ptr - (uiptr)(execinfo->arginfo.payload));
253 if ( partition != NULL )
254 {
255 len = strlen(partition); // Not including NULL terminator
256 if ( ((ptr - execinfo->arginfo.payload) + len) > sizeof(execinfo->arginfo.payload) )
257 return -40;
258 memcpy(ptr, partition, len);
259 ptr += len;
260 }
261 len = strlen(filename) + 1;
262 if ( ((ptr - execinfo->arginfo.payload) + len) > sizeof(execinfo->arginfo.payload) )
263 return -40;
264 memcpy(ptr, filename, len);
265 ptr += len;
266 for ( i = 0; i < argc; i += 1 )
267 {
268 execinfo->arginfo.argv[i + 1] = (char *)((uiptr)ptr - (uiptr)(execinfo->arginfo.payload));
269 len = strlen(argv[i]) + 1;
270 if ( ((ptr - execinfo->arginfo.payload) + len) > sizeof(execinfo->arginfo.payload) )
271 return -40;
272 memcpy(ptr, argv[i], len);
273 ptr += len;
274 }
275 return 0;
276}
277
278// __attribute__((used)): see crt0.c's _main for why.
279static void elf_loader_ldr_entrypoint_stack() __attribute__((used));
280
281static char args_storage[1024];
282static elf_loader_loaderinfo_t sg_loaderinfo;
283
284// Modified crt0
285// bss zero is skipped, and argument handling removed
286static void elf_loader_ldr_entrypoint(void)
287{
288 // clang-format off
289 __asm__ __volatile__(
290 // SetupThread
291 // Arg0: gp value (NULL since not using small data)
292 "move $4, $0" "\n"
293 // Arg1: Stack address (at EE scratchpad)
294 "move $5, $0" "\n"
295 "lui $5, 0x7000" "\n"
296 // Arg2: Stack size (16 KB)
297 "move $6, $0" "\n"
298 "ori $6, $6, 0x4000" "\n"
299 // Arg3: args buffer (needs to not be NULL since kernel unconditionally writes argc to it)
300 "la $7, %0 \n"
301 // Arg4: root function
302 "la $8, ExitThread \n"
303 "move $gp, $4 \n"
304 "addiu $3, $0, 60 \n"
305 "syscall \n"
306 "move $sp, $2 \n"
307 // Jump to entrypoint that can use stack
308 "j elf_loader_ldr_entrypoint_stack \n"
309 : // No output registers
310 : "R"(args_storage));
311 // clang-format on
312}
313
314typedef void *(*ldr_getinternalinfo_callback)(void);
315#define LDR_ENTRYPOINT_ADDR 0x00084000
316
317static void elf_loader_ldr_entrypoint_stack()
318{
319 ldr_getinternalinfo_callback cb;
320
321 // Use VU1 data memory as heap
322 SetupHeap((void *)0x1100C000, 0x4000);
323
324 FlushCache(0);
325
326 memcpy((void *)LDR_ENTRYPOINT_ADDR, ldrsrc, size_ldrsrc);
327 // Use VU0 data memory as storage for loader information
328 memcpy((void *)0x11004000, &sg_loaderinfo, sizeof(elf_loader_loaderinfo_t));
329
330 FlushCache(0);
331 FlushCache(2);
332
333 cb = (void *)LDR_ENTRYPOINT_ADDR;
334 cb();
335 // Should be unreachable here
336 __builtin_trap();
337}
338
339int elf_loader_exec_elf(elf_loader_execinfo_t *execinfo)
340{
341 elf_loader_arginfo_t *low_arginfo;
342 int i;
343
344 if ( execinfo->arginfo.argc == 0 )
345 return -1;
346 memcpy(&sg_loaderinfo, &(execinfo->loaderinfo), sizeof(elf_loader_loaderinfo_t));
347 // Copy argument info to low EE memory (because EE wipes VU0, VU1, or SPR memory before reading it)
348 low_arginfo = (void *)0x00088000;
349 memcpy((void *)low_arginfo, &(execinfo->arginfo), sizeof(elf_loader_execinfo_t));
350 // Relocate the arguments to the new address
351 for ( i = 0; i < low_arginfo->argc; i += 1 )
352 {
353 low_arginfo->argv[i] = (char *)((uiptr)(low_arginfo->argv[i]) + (uiptr)(low_arginfo->payload));
354 }
355 ExecPS2(&elf_loader_ldr_entrypoint, NULL, 0, NULL);
356 return -1;
357}