kern/util/mkblob.c

315 lines
6.9 KiB
C

/**
** @file mkblob.c
**
** @author Warren R. Carithers
**
** Create a binary blob from a collection of ELF files.
*/
#define _DEFAULT_SOURCE
#include <elf.h>
#include <fcntl.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <unistd.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;
}