filename | src/loader.c |
changeset | 1300:d18488c8668b |
prev | 1298:d0eb2307b847 |
author | nkeynes |
date | Wed May 27 08:46:29 2015 +1000 (8 years ago) |
permissions | -rw-r--r-- |
last change | Add support for extracting the ELF symbol table and printing symbol names alongside the SH4 disassembly |
view | annotate | diff | log | raw |
1 /**
2 * $Id$
3 *
4 * File loading routines, mostly for loading demos without going through the
5 * whole procedure of manually making a CD image for them.
6 *
7 * Copyright (c) 2005 Nathan Keynes.
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 */
20 #include <unistd.h>
21 #include <stdio.h>
22 #include <string.h>
23 #include <fcntl.h>
24 #include <sys/stat.h>
25 #include <errno.h>
26 #include <stdint.h>
27 #include <glib.h>
28 #include <elf.h>
29 #include "mem.h"
30 #include "bootstrap.h"
31 #include "dreamcast.h"
32 #include "config.h"
33 #include "loader.h"
34 #include "drivers/cdrom/cdrom.h"
35 #include "drivers/cdrom/isofs.h"
36 #include "gdrom/gdrom.h"
37 #include "sh4/sh4.h"
39 const char bootstrap_magic[32] = "SEGA SEGAKATANA SEGA ENTERPRISES";
40 const char iso_magic[6] = "\001CD001";
41 char *file_loader_extensions[][2] = {
42 { "sbi", "Self Boot Inducer" },
43 { "bin", "SH4 Bin file" },
44 { NULL, NULL } };
46 static cdrom_disc_t cdrom_wrap_elf( cdrom_disc_type_t type, const gchar *filename, int fd, ERROR *err );
47 static cdrom_disc_t cdrom_wrap_binary( cdrom_disc_type_t type, const gchar *filename, int fd, ERROR *err );
48 static gboolean file_load_binary( const gchar *filename, int fd, ERROR *err );
49 static gboolean file_load_elf( const gchar *filename, int fd, ERROR *err );
53 lxdream_file_type_t file_identify( const gchar *filename, int fd, ERROR *err )
54 {
55 char buf[32];
56 lxdream_file_type_t result = FILE_UNKNOWN;
57 gboolean mustClose = FALSE;
58 off_t posn;
60 if( fd == -1 ) {
61 fd = open( filename, O_RDONLY );
62 if( fd == -1 ) {
63 SET_ERROR( err, LX_ERR_FILE_NOOPEN, "Unable to open file '%s' (%s)" ,filename, strerror(errno) );
64 return FILE_ERROR;
65 }
66 mustClose = TRUE;
67 } else {
68 /* Save current file position */
69 posn = lseek(fd, 0, SEEK_CUR);
70 if( posn == -1 ) {
71 SET_ERROR( err, LX_ERR_FILE_IOERROR, "Unable to read from file '%s' (%s)", filename, strerror(errno) );
72 return FILE_ERROR;
73 }
74 }
76 int status = read(fd, buf, 32);
77 if( status == -1 ) {
78 SET_ERROR( err, LX_ERR_FILE_IOERROR, "Unable to read from file '%s' (%s)", filename, strerror(errno) );
79 result = FILE_ERROR;
80 } else if( status != 32 ) {
81 result = FILE_UNKNOWN;
82 } else if( buf[0] == 0x7F && buf[1] == 'E' &&
83 buf[2] == 'L' && buf[3] == 'F' ) {
84 result = FILE_ELF;
85 } else if( memcmp( buf, "PK\x03\x04", 4 ) == 0 ) {
86 result = FILE_ZIP;
87 } else if( memcmp( buf, DREAMCAST_SAVE_MAGIC, 16 ) == 0 ) {
88 result = FILE_SAVE_STATE;
89 } else if( lseek( fd, 32768, SEEK_SET ) == 32768 &&
90 read( fd, buf, 8 ) == 8 &&
91 memcmp( buf, iso_magic, 6) == 0 ) {
92 result = FILE_ISO;
93 } else {
94 /* Check the file extension - .bin = sh4 binary */
95 int len = strlen(filename);
96 struct stat st;
98 if( len > 4 && strcasecmp(filename + (len-4), ".bin") == 0 &&
99 fstat(fd, &st) != -1 && st.st_size <= BINARY_MAX_SIZE ) {
100 result = FILE_BINARY;
101 }
102 }
104 if( mustClose ) {
105 close(fd);
106 } else {
107 lseek( fd, posn, SEEK_SET );
108 }
109 return result;
110 }
113 gboolean file_load_exec( const gchar *filename, ERROR *err )
114 {
115 gboolean result = TRUE;
117 int fd = open( filename, O_RDONLY );
118 if( fd == -1 ) {
119 SET_ERROR( err, LX_ERR_FILE_NOOPEN, "Unable to open file '%s' (%s)" ,filename, strerror(errno) );
120 return FALSE;
121 }
123 lxdream_file_type_t type = file_identify(filename, fd, err);
124 switch( type ) {
125 case FILE_ERROR:
126 result = FALSE;
127 break;
128 case FILE_ELF:
129 result = file_load_elf( filename, fd, err );
130 break;
131 case FILE_BINARY:
132 result = file_load_binary( filename, fd, err );
133 break;
134 default:
135 SET_ERROR( err, LX_ERR_FILE_UNKNOWN, "File '%s' could not be recognized as an executable binary", filename );
136 result = FALSE;
137 break;
138 }
140 close(fd);
141 return result;
142 }
144 lxdream_file_type_t file_load_magic( const gchar *filename, gboolean wrap_exec, ERROR *err )
145 {
146 gboolean result;
147 /* Try disc types first */
148 cdrom_disc_t disc = cdrom_disc_open( filename, err );
149 if( disc != NULL ) {
150 gdrom_mount_disc(disc);
151 return FILE_DISC;
152 } else if( !IS_ERROR_CODE(err,LX_ERR_FILE_UNKNOWN) ) {
153 return FILE_ERROR;
154 }
156 int fd = open( filename, O_RDONLY );
157 if( fd == -1 ) {
158 SET_ERROR( err, LX_ERR_FILE_NOOPEN, "Unable to open file '%s' (%s)" ,filename, strerror(errno) );
159 return FILE_ERROR;
160 }
162 lxdream_file_type_t type = file_identify(filename, fd, err);
163 switch( type ) {
164 case FILE_ERROR:
165 result = FALSE;
166 break;
167 case FILE_ELF:
168 if( wrap_exec ) {
169 disc = cdrom_wrap_elf( CDROM_DISC_XA, filename, fd, err );
170 result = disc != NULL;
171 if( disc != NULL ) {
172 gdrom_mount_disc(disc);
173 }
174 } else {
175 result = file_load_elf( filename, fd, err );
176 }
177 break;
178 case FILE_BINARY:
179 if( wrap_exec ) {
180 disc = cdrom_wrap_binary( CDROM_DISC_XA, filename, fd, err );
181 result = disc != NULL;
182 if( disc != NULL ) {
183 gdrom_mount_disc(disc);
184 }
185 } else {
186 result = file_load_binary( filename, fd, err );
187 }
188 break;
189 case FILE_SAVE_STATE:
190 result = dreamcast_load_state( filename );
191 break;
192 case FILE_ZIP:
193 SET_ERROR( err, LX_ERR_FILE_UNSUP, "ZIP/SBI not currently supported" );
194 result = FALSE;
195 break;
196 case FILE_ISO:
197 SET_ERROR( err, LX_ERR_FILE_UNSUP, "ISO files are not currently supported" );
198 result = FALSE;
199 break;
200 default:
201 SET_ERROR( err, LX_ERR_FILE_UNKNOWN, "File '%s' could not be recognized", filename );
202 result = FALSE;
203 break;
204 }
205 close(fd);
206 if( result ) {
207 CLEAR_ERROR(err);
208 return type;
209 }
210 return FILE_ERROR;
211 }
213 void file_load_postload( const gchar *filename, int pc )
214 {
215 gchar *bootstrap_file = lxdream_get_global_config_path_value(CONFIG_BOOTSTRAP);
216 if( bootstrap_file != NULL && bootstrap_file[0] != '\0' ) {
217 /* Load in a bootstrap before the binary, to initialize everything
218 * correctly
219 */
220 if( mem_load_block( bootstrap_file, BOOTSTRAP_LOAD_ADDR, BOOTSTRAP_SIZE ) == 0 ) {
221 dreamcast_program_loaded( filename, BOOTSTRAP_ENTRY_ADDR );
222 g_free(bootstrap_file);
223 return;
224 }
225 }
226 dreamcast_program_loaded( filename, pc );
227 g_free(bootstrap_file);
228 }
230 static gboolean is_arch( Elf32_Ehdr *head, Elf32_Half mach ) {
231 return ( head->e_ident[EI_CLASS] == ELFCLASS32 &&
232 head->e_ident[EI_DATA] == ELFDATA2LSB &&
233 head->e_ident[EI_VERSION] == 1 &&
234 head->e_type == ET_EXEC &&
235 head->e_machine == mach &&
236 head->e_version == 1 );
237 }
239 #define is_sh4_elf(head) is_arch(head, EM_SH)
240 #define is_arm_elf(head) is_arch(head, EM_ARM)
242 static gboolean file_load_elf( const gchar *filename, int fd, ERROR *err )
243 {
244 Elf32_Ehdr head;
245 Elf32_Phdr phdr;
246 Elf32_Shdr shdr;
247 Elf32_Sym sym;
248 int i;
250 if( read( fd, &head, sizeof(head) ) != sizeof(head) )
251 return FALSE;
252 if( !is_sh4_elf(&head) ) {
253 SET_ERROR( err, LX_ERR_FILE_INVALID, "File is not an SH4 ELF executable file" );
254 return FALSE;
255 }
257 /* Program headers */
258 for( i=0; i<head.e_phnum; i++ ) {
259 lseek( fd, head.e_phoff + i*head.e_phentsize, SEEK_SET );
260 read( fd, &phdr, sizeof(phdr) );
261 if( phdr.p_type == PT_LOAD ) {
262 lseek( fd, phdr.p_offset, SEEK_SET );
263 sh4ptr_t target = mem_get_region( phdr.p_vaddr );
264 read( fd, target, phdr.p_filesz );
265 if( phdr.p_memsz > phdr.p_filesz ) {
266 memset( target + phdr.p_filesz, 0, phdr.p_memsz - phdr.p_filesz );
267 }
268 }
269 }
271 /* Find symbol table */
272 uint32_t symtabOffset = 0, symtabSize = 0, symtabEntSize = 0;
273 uint32_t strtabOffset = 0, strtabSize = 0;
274 for( int i = 0; i < head.e_shnum; i++ ) {
275 lseek( fd, head.e_shoff + i * head.e_shentsize, SEEK_SET );
276 read( fd, &shdr, sizeof( shdr ) );
277 if( shdr.sh_type == SHT_SYMTAB ) {
278 symtabOffset = shdr.sh_offset;
279 symtabSize = shdr.sh_size;
280 symtabEntSize = shdr.sh_entsize;
281 } else if( shdr.sh_type == SHT_STRTAB ) {
282 strtabOffset = shdr.sh_offset;
283 strtabSize = shdr.sh_size;
284 }
285 }
286 /* Extract symbols */
287 if( symtabOffset != 0 && strtabOffset != 0 ) {
288 unsigned numSymtabEntries = symtabSize / symtabEntSize;
289 char *data = g_malloc( numSymtabEntries * sizeof( struct sh4_symbol ) + strtabSize );
290 struct sh4_symbol *symtab = ( struct sh4_symbol * )data;
291 char *strings = data + ( numSymtabEntries * sizeof( struct sh4_symbol ) );
292 lseek( fd, strtabOffset, SEEK_SET );
293 read( fd, strings, strtabSize );
294 strings[strtabSize-1] = '\0'; /* Should already be 0, but just in case */
295 for( int i = 0; i < numSymtabEntries; i++ ) {
296 lseek( fd, symtabOffset + ( i * symtabEntSize ), SEEK_SET );
297 read( fd, &sym, sizeof( sym ) );
298 if( sym.st_name < strtabSize )
299 symtab[i].name = &strings[sym.st_name];
300 else
301 symtab[i].name = NULL;
302 symtab[i].address = sym.st_value;
303 symtab[i].size = sym.st_size;
304 }
305 sh4_set_symbol_table( symtab, numSymtabEntries, ( sh4_symtab_destroy_cb )free );
306 }
308 file_load_postload( filename, head.e_entry );
309 return TRUE;
310 }
312 static gboolean file_load_binary( const gchar *filename, int fd, ERROR *err )
313 {
314 struct stat st;
316 if( fstat( fd, &st ) == -1 ) {
317 SET_ERROR( err, LX_ERR_FILE_IOERROR, "Error reading binary file '%s' (%s)", filename, strerror(errno) );
318 return FALSE;
319 }
321 if( st.st_size > BINARY_MAX_SIZE ) {
322 SET_ERROR( err, LX_ERR_FILE_INVALID, "Binary file '%s' is too large to fit in memory", filename );
323 return FALSE;
324 }
326 sh4ptr_t target = mem_get_region( BINARY_LOAD_ADDR );
327 if( read( fd, target, st.st_size ) != st.st_size ) {
328 SET_ERROR( err, LX_ERR_FILE_IOERROR, "Error reading binary file '%s' (%s)", filename, strerror(errno) );
329 return FALSE;
330 }
332 file_load_postload( filename, BINARY_LOAD_ADDR );
333 return TRUE;
334 }
336 /**
337 * Create a new CDROM disc containing a single 1ST_READ.BIN.
338 * @param type The disc type - must be CDROM_DISC_GDROM or CDROM_DISC_XA
339 * @param bin The binary data (takes ownership)
340 * @param bin_size
341 */
342 cdrom_disc_t cdrom_disc_new_wrapped_binary( cdrom_disc_type_t type, const gchar *filename, unsigned char *bin, size_t bin_size,
343 ERROR *err )
344 {
345 IsoImage *iso = NULL;
346 unsigned char *data = bin;
347 cdrom_lba_t start_lba = 45000; /* GDROM_START */
348 char bootstrap[32768];
350 /* 1. Load in the bootstrap: Note failures here are considered configuration errors */
351 gchar *bootstrap_file = lxdream_get_global_config_path_value(CONFIG_BOOTSTRAP);
352 if( bootstrap_file == NULL || bootstrap_file[0] == '\0' ) {
353 g_free(data);
354 SET_ERROR( err, LX_ERR_CONFIG, "Unable to create CD image: bootstrap file is not configured" );
355 return NULL;
356 }
358 FILE *f = fopen( bootstrap_file, "ro" );
359 if( f == NULL ) {
360 g_free(data);
361 SET_ERROR( err, LX_ERR_CONFIG, "Unable to create CD image: bootstrap file '%s' could not be opened", bootstrap_file );
362 return FALSE;
363 }
364 size_t len = fread( bootstrap, 1, 32768, f );
365 fclose(f);
366 if( len != 32768 ) {
367 g_free(data);
368 SET_ERROR( err, LX_ERR_CONFIG, "Unable to create CD image: bootstrap file '%s' is invalid", bootstrap_file );
369 return FALSE;
370 }
372 /* 2. Scramble the binary if necessary (and set type settings) */
373 if( type != CDROM_DISC_GDROM ) {
374 /* scramble the binary if we're going the MIL-CD route */
375 unsigned char *scramblebin = g_malloc(bin_size);
376 bootprogram_scramble( scramblebin, bin, bin_size );
377 data = scramblebin;
378 start_lba = 0x2DB6; /* CDROM_START (does it matter?) */
379 g_free(bin);
380 }
382 /* 3. Frob the bootstrap data */
383 dc_bootstrap_head_t boot_header = (dc_bootstrap_head_t)bootstrap;
384 memcpy( boot_header->boot_file, "1ST_READ.BIN ", 16 );
385 char tmp[129];
386 int name_len = snprintf( tmp, 129, "lxdream wrapped image: %s", filename );
387 if( name_len < 128 )
388 memset( tmp+name_len, ' ', 128-name_len );
389 memcpy( boot_header->product_name, tmp, 128 );
390 // bootstrap_update_crc(bootstrap);
393 /* 4. Build the ISO image */
394 int status = iso_image_new("autocd", &iso);
395 if( status != 1 ) {
396 g_free(data);
397 SET_ERROR( err, LX_ERR_NOMEM, "Unable to create CD image: out of memory" );
398 return NULL;
399 }
401 IsoStream *stream;
402 if( iso_mem_stream_new(data, bin_size, &stream) != 1 ) {
403 g_free(data);
404 iso_image_unref(iso);
405 SET_ERROR( err, LX_ERR_NOMEM, "Unable to create CD image: out of memory" );
406 return NULL;
407 }
408 iso_tree_add_new_file(iso_image_get_root(iso), "1ST_READ.BIN", stream, NULL);
409 sector_source_t track = iso_sector_source_new( iso, SECTOR_MODE2_FORM1, start_lba,
410 bootstrap, err );
411 if( track == NULL ) {
412 iso_image_unref(iso);
413 return NULL;
414 }
416 cdrom_disc_t disc = cdrom_disc_new_from_track( type, track, start_lba, err );
417 iso_image_unref(iso);
418 if( disc != NULL ) {
419 disc->name = g_strdup(filename);
420 }
421 return disc;
422 }
424 static cdrom_disc_t cdrom_wrap_elf( cdrom_disc_type_t type, const gchar *filename, int fd, ERROR *err )
425 {
426 Elf32_Ehdr head;
427 int i;
429 /* Check the file header is actually an SH4 binary */
430 if( read( fd, &head, sizeof(head) ) != sizeof(head) )
431 return FALSE;
433 if( !is_sh4_elf(&head) ) {
434 SET_ERROR( err, LX_ERR_FILE_INVALID, "File is not an SH4 ELF executable file" );
435 return FALSE;
436 }
437 if( head.e_entry != BINARY_LOAD_ADDR ) {
438 SET_ERROR( err, LX_ERR_FILE_INVALID, "SH4 Binary has incorrect entry point (should be %08X but is %08X)", BINARY_LOAD_ADDR, head.e_entry );
439 return FALSE;
440 }
442 /* Load the program headers */
443 Elf32_Phdr phdr[head.e_phnum];
444 lseek( fd, head.e_phoff, SEEK_SET );
445 if( read( fd, phdr, sizeof(phdr) ) != sizeof(phdr) ) {
446 SET_ERROR( err, LX_ERR_FILE_INVALID, "File is not a valid executable file" );
447 return FALSE;
448 }
450 sh4addr_t start = (sh4addr_t)-1, end=0;
451 /* Scan program headers for memory range in use */
452 for( i=0; i<head.e_phnum; i++ ) {
453 if( phdr[i].p_type == PT_LOAD ) {
454 if( phdr[i].p_vaddr < start )
455 start = phdr[i].p_vaddr;
456 if( phdr[i].p_vaddr + phdr[i].p_memsz > end )
457 end = phdr[i].p_vaddr + phdr[i].p_memsz;
458 }
459 }
461 if( start != BINARY_LOAD_ADDR ) {
462 SET_ERROR( err, LX_ERR_FILE_INVALID, "SH4 Binary has incorrect load address (should be %08X but is %08X)", BINARY_LOAD_ADDR, start );
463 return FALSE;
464 }
465 if( end >= 0x8D000000 ) {
466 SET_ERROR( err, LX_ERR_FILE_INVALID, "SH4 binary is too large to fit in memory (end address is %08X)", end );
467 return FALSE;
468 }
470 /* Load the program into memory */
471 unsigned char *program = g_malloc0( end-start );
472 for( i=0; i<head.e_phnum; i++ ) {
473 if( phdr[i].p_type == PT_LOAD ) {
474 lseek( fd, phdr[i].p_offset, SEEK_SET );
475 uint32_t size = MIN( phdr[i].p_filesz, phdr[i].p_memsz);
476 int status = read( fd, program + phdr[i].p_vaddr - start, size );
477 if( status == -1 ) {
478 SET_ERROR( err, LX_ERR_FILE_IOERROR, "I/O error reading SH4 binary %s (%s)", filename, strerror(errno) );
479 } else if( status != size ) {
480 SET_ERROR( err, LX_ERR_FILE_IOERROR, "SH4 binary %s is corrupt", filename );
481 }
482 }
483 }
485 /* And finally pass it over to the disc wrapper */
486 return cdrom_disc_new_wrapped_binary(type, filename, program, end-start, err );
487 }
489 static cdrom_disc_t cdrom_wrap_binary( cdrom_disc_type_t type, const gchar *filename, int fd, ERROR *err )
490 {
491 struct stat st;
492 unsigned char *data;
493 size_t len;
495 if( fstat(fd, &st) == -1 ) {
496 SET_ERROR( err, LX_ERR_FILE_IOERROR, "Error reading binary file '%s' (%s)", filename, strerror(errno) );
497 return NULL;
498 }
500 data = g_malloc(st.st_size);
501 len = read( fd, data, st.st_size );
502 if( len != st.st_size ) {
503 SET_ERROR( err, LX_ERR_FILE_IOERROR, "Error reading binary file '%s' (%s)", filename, strerror(errno) );
504 free(data);
505 return NULL;
506 }
508 return cdrom_disc_new_wrapped_binary( type, filename, data, st.st_size, err );
509 }
511 cdrom_disc_t cdrom_wrap_magic( cdrom_disc_type_t type, const gchar *filename, ERROR *err )
512 {
513 cdrom_disc_t disc = NULL;
515 int fd = open( filename, O_RDONLY );
516 if( fd == -1 ) {
517 SET_ERROR( err, LX_ERR_FILE_NOOPEN, "Unable to open file '%s'", filename );
518 return NULL;
519 }
521 lxdream_file_type_t filetype = file_identify( filename, fd, err );
522 switch( filetype ) {
523 case FILE_ELF:
524 disc = cdrom_wrap_elf(type, filename, fd, err);
525 break;
526 case FILE_BINARY:
527 disc = cdrom_wrap_binary(type, filename, fd, err);
528 break;
529 default:
530 SET_ERROR( err, LX_ERR_FILE_UNKNOWN, "File '%s' cannot be wrapped (not a recognized binary)", filename );
531 break;
532 }
534 close(fd);
535 return disc;
537 }
.