kern/util/listblob.c

229 lines
5.4 KiB
C

/**
** @file listblob.c
**
** @author Warren R. Carithers
**
** Examine a binary blob of ELF files.
*/
#define _DEFAULT_SOURCE
#include <ctype.h>
#include <elf.h>
#include <fcntl.h>
#include <stdbool.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 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: 8 bytes
typedef struct header_s {
char magic[4];
uint32_t num;
} header_t;
// The program table entry is 32 bytes long. To accomplish this, the
// name field is 20 bytes long, which allows file names of 19 characters
// (followed by a trailing NUL byte).
//
// If that field is made longer, it should be incremented in multiples
// of four to avoid the insertion of padding bytes.
#define NAMELEN 20
// program descriptor: 32 bytes
typedef struct prog_s {
char name[NAMELEN]; // truncated name (19 chars plus NUL)
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;
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
bool defs = false; // print CPP #defines?
bool enums = false; // print C enums?
// header string for the userids.h file
const char header[] =
"/**\n"
"** @file userids.h\n"
"**\n"
"** @author Warren R. Carithers\n"
"**\n"
"** @brief IDs for user-level programs\n"
"**\n"
"** NOTE: this file is automatically generated when the user.img file\n"
"** is created. Do not edit this manually!\n"
"*/\n"
"\n"
"#ifndef USERIDS_H_\n"
"#define USERIDS_H_\n"
"\n"
"#ifndef ASM_SRC\n"
"/*\n"
"** These IDs are used to identify the various user programs.\n"
"** Each call to exec() will provide one of these as the first\n"
"** argument.\n"
"**\n"
"** This list should be updated if/when the collection of\n"
"** user processes changes.\n"
"*/\n"
"enum users_e {";
// trailer string for the userids.h file
const char trailer[] = "\n\t// sentinel\n\t, N_USERS\n"
"};\n"
"#endif /* !ASM_SRC */\n"
"\n"
"#endif";
/**
** Name: process
**
** Process a program list entry
**
** @param num Program list index
** @param prog Pointer to the program list entry
*/
void process(uint32_t num, prog_t *prog)
{
if (defs || enums) {
char *slash = strrchr(prog->name, '/');
if (slash == NULL) {
slash = prog->name;
} else {
++slash;
}
slash[0] = toupper(slash[0]);
if (defs) {
// just printing #define statements
printf("#define %-15s %2d\n", prog->name, num);
} else {
// printing a new userids.h file
if (num == 0) {
// first one, so print the file header
puts(header);
putchar('\t');
} else {
// second or later entry; limit to 8 per line
fputs(((num & 0x7) == 0) ? ",\n\t" : ", ", stdout);
}
printf("%s", prog->name);
}
} else {
// just printing information
printf("Entry %2d: ", num);
printf("%-s,", prog->name);
printf(" offset 0x%x, size 0x%x, flags %08x\n", prog->offset,
prog->size, prog->flags);
}
}
void usage(char *name)
{
fprintf(stderr, "usage: %s [-d | -e] blob_name\n", name);
}
int main(int argc, char *argv[])
{
if (argc < 2 || argc > 3) {
usage(argv[0]);
exit(1);
}
int nameix = 1;
// could use getopt() for this, but this is easy enough
if (argc == 3) {
if (strcmp(argv[1], "-d") == 0) {
defs = true;
} else if (strcmp(argv[1], "-e") == 0) {
enums = true;
} else {
usage(argv[0]);
exit(1);
}
nameix = 2;
}
char *name = argv[nameix];
int fd = open(name, O_RDONLY);
if (fd < 0) {
perror(name);
exit(1);
}
header_t hdr;
int n = read(fd, &hdr, sizeof(header_t));
if (n != sizeof(header_t)) {
fprintf(stderr, "%s: header read returned only %d bytes\n", name, n);
close(fd);
exit(1);
}
if (strcmp(hdr.magic, "BLB") != 0) {
fprintf(stderr, "%s: bad magic number\n", name);
close(fd);
exit(1);
}
if (hdr.num < 1) {
fprintf(stderr, "%s: no programs in blob?\n", name);
close(fd);
exit(1);
}
prog_t progs[hdr.num];
n = read(fd, progs, hdr.num * sizeof(prog_t));
if (n != (int)(hdr.num * sizeof(prog_t))) {
fprintf(stderr, "%s: prog table only %d bytes, expected %lu\n", name, n,
hdr.num * sizeof(prog_t));
close(fd);
exit(1);
}
for (uint32_t i = 0; i < hdr.num; ++i) {
process(i, &progs[i]);
}
if (enums) {
// print the file trailer
puts(trailer);
}
close(fd);
return 0;
}