diff options
Diffstat (limited to 'util/mkblob.c')
-rw-r--r-- | util/mkblob.c | 324 |
1 files changed, 324 insertions, 0 deletions
diff --git a/util/mkblob.c b/util/mkblob.c new file mode 100644 index 0000000..32c67dd --- /dev/null +++ b/util/mkblob.c @@ -0,0 +1,324 @@ +/** +** @file mkblob.c +** +** @author Warren R. Carithers +** +** Create a binary blob from a collection of ELF files. +*/ +#define _DEFAULT_SOURCE + +#include <stdio.h> +#include <stdint.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <elf.h> + +/* +** Blob file organization +** +** The file begins with a four-byte magic number and a four-byte integer +** indicating the number of ELF files contained in the blob. This is +** followed by an array of 32-byte file table entries, and then the contents +** of the ELF files in the order they appear in the program file table. +** +** Bytes Contents +** ----- ---------------------------- +** 0 - 3 File magic number ("BLB\0") +** 4 - 7 Number of ELF files in blob ("n") +** 8 - n*32+8 Program file table +** n*32+9 - ? ELF file contents +** +** Each program file table entry contains the following information: +** +** name File name (up to 19 characters long) +** offset Byte offset to the ELF header for this file +** size Size of this ELF file, in bytes +** flags Flags related to this file +*/ + +// blob header +typedef struct header_s { + char magic[4]; + uint32_t num; +} header_t; + +// length of the file name field +#define NAMELEN 20 + +// program descriptor +typedef struct prog_s { + char name[NAMELEN]; // truncated name (15 chars) + uint32_t offset; // offset from the beginning of the blob + uint32_t size; // size of this ELF module + uint32_t flags; // miscellaneous flags +} prog_t; + +// modules must be written as multiples of eight bytes +#define FL_ROUNDUP 0x00000001 + +// mask for mod 8 checking +#define FSIZE_MASK 0x00000007 + +// program list entry +typedef struct node_s { + prog_t *data; + char *fullname; + struct node_s *next; +} node_t; + +node_t *progs, *last_prog; // list pointers +uint32_t n_progs; // number of files being copied +uint32_t offset; // current file area offset + +/** +** Name: process +** +** Do the initial processing for an ELF file +** +** @param name The name of the file +*/ +void process( const char *name ) { + struct stat info; + + // check the name length + if( strlen(name) >= NAMELEN ) { + fprintf( stderr, "%s: name exceeds length limit (%d)\n", + name, NAMELEN-1 ); + return; + } + + // does it exist? + if( stat(name,&info) < 0 ) { + perror( name ); + return; + } + + // is it a regular file? + if( !S_ISREG(info.st_mode) ) { + fprintf( stderr, "%s: not a regular file\n", name ); + return; + } + + // open it and check the file header + int fd = open( name, O_RDONLY ); + if( fd < 0 ) { + perror( name ); + return; + } + + // read and check the ELF header + Elf32_Ehdr hdr; + int n = read( fd, &hdr, sizeof(Elf32_Ehdr) ); + close( fd ); + + if( n != sizeof(Elf32_Ehdr) ) { + fprintf( stderr, "%s: header read was short - only %d\n", name, n ); + return; + } + + if( hdr.e_ident[EI_MAG0] != ELFMAG0 || + hdr.e_ident[EI_MAG1] != ELFMAG1 || + hdr.e_ident[EI_MAG2] != ELFMAG2 || + hdr.e_ident[EI_MAG3] != ELFMAG3 ) { + fprintf( stderr, "%s: bad ELF magic number\n", name ); + return; + } + + // ok, it's a valid ELF file - create the prog list entry + prog_t *new = calloc( 1, sizeof(prog_t) ); + if( new == NULL ) { + fprintf( stderr, "%s: calloc prog returned NULL\n", name ); + return; + } + + node_t *node = calloc( 1, sizeof(node_t) ); + if( node == NULL ) { + free( new ); + fprintf( stderr, "%s: calloc node returned NULL\n", name ); + return; + } + + node->data = new; + node->fullname = strdup( name ); + + // copy in the name + + // only want the last component + const char *slash = strrchr( name, '/' ); + if( slash == NULL ) { + // only the file name + slash = name; + } else { + // skip the slash + ++slash; + } + + strncpy( new->name, slash, sizeof(new->name)-1 ); + new->offset = offset; + new->size = info.st_size; + + // bump our counters + ++n_progs; + offset += info.st_size; + + // make sure it's a multiple of eight bytes long + if( (info.st_size & FSIZE_MASK) != 0 ) { + // nope, so we must round it up when we write it out + new->flags |= FL_ROUNDUP; + // increases the offset to the next file + offset += 8 - (info.st_size & FSIZE_MASK); + } + + // add to the list + if( progs == NULL ) { + // first entry + progs = node; + } else { + // add to the end + if( last_prog == NULL ) { + fprintf( stderr, "%s: progs ! NULL, last_prog is NULL\n", name ); + free( new ); + free( node->fullname ); + free( node ); + return; + } + last_prog->next = node; + } + last_prog = node; +} + +/** +** Name: copy +** +** Copy the contents of a program list entry into the blob +** +** @param ofd The output FILE* to be written +** @param prog Pointer to the program list entry for the file +*/ +void copy( FILE *ofd, node_t *node ) { + + prog_t *prog = node->data; + + // open it so we can copy it + int fd = open( node->fullname, O_RDONLY ); + if( fd < 0 ) { + perror( node->fullname ); + return; + } + + uint8_t buf[512]; + + // copy it block-by-block + do { + int n = read( fd, buf, 512); + // no bytes --> we're done + if( n < 1 ) { + break; + } + // copy it, and verify the copy count + int k = fwrite( buf, 1, n, ofd ); + if( k != n ) { + fprintf( stderr, "%s: write of %d returned %d\n", + prog->name, n, k ); + } + } while( 1 ); + + printf( "%s: copied %d", prog->name, prog->size ); + + // do we need to round up? + if( (prog->flags & FL_ROUNDUP) != 0 ) { + + // we'll fill with NUL bytes + uint64_t filler = 0; + + // how many filler bytes do we need? + int nbytes = 8 - (prog->size & FSIZE_MASK); + + // do it, and check the transfer count to be sure + int n = fwrite( &filler, 1, nbytes, ofd ); + if( n != nbytes ) { + fprintf( stderr, "%s: fill write of %d returned %d\n", + prog->name, nbytes, n ); + } + + // report that we added some filler bytes + printf( "(+%d)", n ); + } + puts( " bytes" ); + + // all done! + close( fd ); +} + +int main( int argc, char *argv[] ) { + + // construct program list + for( int i = 1; i < argc; ++i ) { + process( argv[i] ); + } + + if( n_progs < 1 ) { + fputs( "Nothing to do... exiting.", stderr ); + exit( 0 ); + } + + // create the output file + FILE *ofd; + ofd = fopen( "user.img", "wb" ); + if( ofd == NULL ) { + perror( "user.img" ); + exit( 1 ); + } + + printf( "Processing %d ELF files\n", n_progs ); + + // we need to adjust the offset values so they are relative to the + // start of the blob, not relative to the start of the file area. + // do this by adding the sum of the file header and program entries + // to each offset field. + + uint32_t hlen = sizeof(header_t) + n_progs * sizeof(prog_t); + node_t *curr = progs; + while( curr != NULL ) { + curr->data->offset += hlen; + curr = curr->next; + } + + // write out the blob header + header_t hdr = { "BLB", n_progs }; + if( fwrite(&hdr,sizeof(header_t),1,ofd) != 1 ) { + perror( "blob header" ); + fclose( ofd ); + exit( 1 ); + } + + // next, the program entries + curr = progs; + while( curr != NULL ) { + if( fwrite(curr->data,sizeof(prog_t),1,ofd) != 1 ) { + perror( "blob prog entry write" ); + fclose( ofd ); + exit( 1 ); + } + curr = curr->next; + } + + // finally, copy the files + curr = progs; + while( curr != NULL ) { + prog_t *prog = curr->data; + copy( ofd, curr ); + node_t *tmp = curr; + curr = curr->next; + free( tmp->data ); + free( tmp->fullname ); + free( tmp ); + } + + fclose( ofd ); + + return 0; +} |