PS2SDK
PS2 Homebrew Libraries
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
278static void elf_loader_ldr_entrypoint_stack();
279
280static char args_storage[1024];
281static elf_loader_loaderinfo_t sg_loaderinfo;
282
283// Modified crt0
284// bss zero is skipped, and argument handling removed
285static void elf_loader_ldr_entrypoint(void)
286{
287 // clang-format off
288 __asm__ __volatile__(
289 // SetupThread
290 // Arg0: gp value (NULL since not using small data)
291 "move $4, $0" "\n"
292 // Arg1: Stack address (at EE scratchpad)
293 "move $5, $0" "\n"
294 "lui $5, 0x7000" "\n"
295 // Arg2: Stack size (16 KB)
296 "move $6, $0" "\n"
297 "ori $6, $6, 0x4000" "\n"
298 // Arg3: args buffer (needs to not be NULL since kernel unconditionally writes argc to it)
299 "la $7, %0 \n"
300 // Arg4: root function
301 "la $8, ExitThread \n"
302 "move $gp, $4 \n"
303 "addiu $3, $0, 60 \n"
304 "syscall \n"
305 "move $sp, $2 \n"
306 // Jump to entrypoint that can use stack
307 "j %1 \n"
308 : // No output registers
309 : "R"(args_storage), "Csy"(elf_loader_ldr_entrypoint_stack));
310 // clang-format on
311}
312
313typedef void *(*ldr_getinternalinfo_callback)(void);
314#define LDR_ENTRYPOINT_ADDR 0x00084000
315
316static void elf_loader_ldr_entrypoint_stack()
317{
318 ldr_getinternalinfo_callback cb;
319
320 // Use VU1 data memory as heap
321 SetupHeap((void *)0x1100C000, 0x4000);
322
323 FlushCache(0);
324
325 memcpy((void *)LDR_ENTRYPOINT_ADDR, ldrsrc, size_ldrsrc);
326 // Use VU0 data memory as storage for loader information
327 memcpy((void *)0x11004000, &sg_loaderinfo, sizeof(elf_loader_loaderinfo_t));
328
329 FlushCache(0);
330 FlushCache(2);
331
332 cb = (void *)LDR_ENTRYPOINT_ADDR;
333 cb();
334 // Should be unreachable here
335 __builtin_trap();
336}
337
338int elf_loader_exec_elf(elf_loader_execinfo_t *execinfo)
339{
340 elf_loader_arginfo_t *low_arginfo;
341 int i;
342
343 if ( execinfo->arginfo.argc == 0 )
344 return -1;
345 memcpy(&sg_loaderinfo, &(execinfo->loaderinfo), sizeof(elf_loader_loaderinfo_t));
346 // Copy argument info to low EE memory (because EE wipes VU0, VU1, or SPR memory before reading it)
347 low_arginfo = (void *)0x00088000;
348 memcpy((void *)low_arginfo, &(execinfo->arginfo), sizeof(elf_loader_execinfo_t));
349 // Relocate the arguments to the new address
350 for ( i = 0; i < low_arginfo->argc; i += 1 )
351 {
352 low_arginfo->argv[i] = (char *)((uiptr)(low_arginfo->argv[i]) + (uiptr)(low_arginfo->payload));
353 }
354 ExecPS2(&elf_loader_ldr_entrypoint, NULL, 0, NULL);
355 return -1;
356}