Search
lxdream.org :: lxdream/src/loader.c
lxdream 0.9.1
released Jun 29
Download Now
filename src/loader.c
changeset 1300:d18488c8668b
prev1298: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 }
.