/** ** @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; }