diff options
Diffstat (limited to 'linaro/arm-virt-bl/scripts/loadmap.c')
-rw-r--r-- | linaro/arm-virt-bl/scripts/loadmap.c | 861 |
1 files changed, 861 insertions, 0 deletions
diff --git a/linaro/arm-virt-bl/scripts/loadmap.c b/linaro/arm-virt-bl/scripts/loadmap.c new file mode 100644 index 0000000..6b73f99 --- /dev/null +++ b/linaro/arm-virt-bl/scripts/loadmap.c @@ -0,0 +1,861 @@ +#define _GNU_SOURCE + +#include <stdlib.h> +#include <stdio.h> +#include <errno.h> +#include <stdarg.h> +#include <string.h> +#include <elf.h> +#include <limits.h> +#include <endian.h> + +#include <unistd.h> +#include <fcntl.h> +#include <time.h> +#include <sys/stat.h> + +#define FRAG_INITIAL_SIZE 0x1000 + +#define TOOLS_GNU 'G' +#define TOOLS_RVCT 'R' +#define KIND_ASM 's' +#define KIND_LINKER_SCRIPT 'k' + +#define lengthof(a) (sizeof (a) / sizeof *(a)) + +struct personality_struct { + int tools; + int kind; + void (*init)(void); + void (*do_phdr)(Elf32_Ehdr *eh, Elf32_Phdr *phdrs, unsigned index); + void (*finish)(void); +}; + +static void init_gnu_asm(void); +static void init_rvct_asm(void); +static void init_gnu_lds(void); +static void init_rvct_scf(void); +static void do_phdr_gnu_asm(Elf32_Ehdr *eh, Elf32_Phdr *phdrs, unsigned index); +static void do_phdr_rvct_asm(Elf32_Ehdr *eh, Elf32_Phdr *phdrs, unsigned index); +static void do_phdr_gnu_lds(Elf32_Ehdr *eh, Elf32_Phdr *phdrs, unsigned index); +static void do_phdr_rvct_scf(Elf32_Ehdr *eh, Elf32_Phdr *phdrs, unsigned index); +static void finish_gnu_asm(void); +static void finish_rvct_asm(void); +static void finish_gnu_lds(void); +static void finish_rvct_scf(void); + +static const struct personality_struct personalities[] = { + { TOOLS_GNU, KIND_ASM, + init_gnu_asm, do_phdr_gnu_asm, finish_gnu_asm }, + { TOOLS_GNU, KIND_LINKER_SCRIPT, + init_gnu_lds, do_phdr_gnu_lds, finish_gnu_lds }, + { TOOLS_RVCT, KIND_ASM, + init_rvct_asm, do_phdr_rvct_asm, finish_rvct_asm }, + { TOOLS_RVCT, KIND_LINKER_SCRIPT, + init_rvct_scf, do_phdr_rvct_scf, finish_rvct_scf }, +}; + +static int opt_entry_point_set = 0; +static Elf32_Word opt_entry_point; +static int opt_load_address_set = 0; +static Elf32_Word opt_load_address; +static int opt_binary = 0; +static int opt_entry_thumb = 0; +static FILE *opt_input_file; +static char *opt_program_name; +static char bin_template[PATH_MAX]; /* section dump filename template */ +static char path_buf[PATH_MAX]; +static char opt_basename[PATH_MAX] = ""; +static int opt_toolchain = TOOLS_GNU; +static int opt_output_kind = KIND_ASM; +static struct personality_struct const *personality = NULL; + +struct frag +{ + char *buf; + size_t len; + size_t size; + size_t next_size; +}; + +static struct frag lfr; /* load region table or linker script built here */ +static struct frag zfr; /* zero-initialised region table built here */ +static struct frag binfr; /* binary data inclusions built here */ + +static int has_load = 0; +static int has_zi = 0; + +static void _die(unsigned lineno, char const *message) +{ + fprintf(stderr, "%s: Died at %s:%d: ", opt_program_name, __FILE__, lineno); + fputs(message, stderr); + exit(EXIT_FAILURE); +} + +#define die(message) _die(__LINE__, message) + +static void warn(char const *message) +{ + fprintf(stderr, "%s: Warning: %s", opt_program_name, message); +} + +/* + * Only use this function to grow a block. + * Spurious failures may occur if you try to shrink a block using this + * function. + */ +static void *xrealloc(void *block, size_t size) +{ + void *result; + + result = realloc(block, size); + if (!result) die("Out of memory\n"); + + return result; +} + +static void frag_init(struct frag *f) +{ + f->buf = NULL; + f->len = f->size = 0; + f->next_size = FRAG_INITIAL_SIZE; +} + +/* + * Grow the fragment by an arbitrary (hopefully sensible) amount. + * Use this if you know the frag needs to be bigger, but you don't + * know exactly how big you want it yet. + */ +static void frag_grow(struct frag *f) +{ + f->buf = xrealloc(f->buf, f->next_size); + f->size = f->next_size; + f->next_size *= 2; +} + +/* Use this to grow the fragment until it is at least size bytes in size */ +static void frag_ensure(struct frag *f, size_t size) +{ + while (f->size < size) + frag_grow(f); +} + +static void frag_shrink(struct frag *f, size_t size) +{ + void *result; + + if (f->size <= size) + return; + + result = realloc(f->buf, size); + if (result) + f->buf = result; + +} + +/* + * Ensure that size bytes can be appended at p. + * p should be NULL if the frag has no memory allocated to it yet, + * or if you want to append and the and of the last append made + * be frag_stpcpy or frag_pprintf. + * + * The pointer to append to is returned. It may be different + * from p, since realloc may move the frag's buffer. + */ +static char *frag_ensure_append(struct frag *f, char *p, size_t size) +{ + size_t offset; + + if (p) + offset = f->buf ? p - f->buf : 0; + else + offset = f->len; + + frag_ensure(f, offset + size); + + return f->buf + offset; +} + +/* + * Copy q to p in fragment f, growing f as needed. + * A pointer to the end of the resulting string is returned; + * This is most useful if p pointed to the end of the string in + * f to begin with: in that case, this function appends q to + * the string in f, returning the start pointer for subsequent + * appends. + */ +static char *frag_stpcpy(struct frag *f, char *p, char const *q) +{ + size_t len; + char *result; + + len = strlen(q); + + result = strcpy(frag_ensure_append(f, p, len + 1), q) + len; + f->len = result - f->buf; + + return result; +} + +/* + * Append a formatted string to the frag f, printf-style. + * The frag grows as needed. + * A pointer to the end of the resulting frag is returned + */ +static char *frag_pprintf(struct frag *f, char *p, char const *format, ...) +{ + va_list vl; + int n; + size_t offset; + + if (p) + offset = f->buf ? p - f->buf : 0; + else + offset = f->len; + + /* Ensure that at least the terminating NUL can be accomodated: */ + if (f->size < 1 + offset) + frag_grow(f); + + va_start(vl, format); + + while (1) { + n = vsnprintf(f->buf + offset, f->size - offset, format, vl); + if (n < 0) + return NULL; + + if ((unsigned)n < f->size - offset) + break; + + frag_grow(f); + } + + va_end(vl); + + f->len = offset + n; + + return f->buf + f->len; +} + +/* + * Read the whole file f into memory. + * Aborts on error; otherwise returns a non-NULL pointer to the file + * data. If result_size is non-NULL, the number of bytes of data + * read is stored to it. + */ +static void *fslurp(FILE *f, size_t *size) +{ + struct frag fr; + size_t total = 0; + + frag_init(&fr); + + do { + size_t nread; + + frag_grow(&fr); + + nread = fread(fr.buf + total, 1, fr.size - total, f); + total += nread; + } while (!(total < fr.size)); + + if (ferror(f)) + die("I/O error\n"); + + frag_shrink(&fr, total); + + if (size) + *size = total; + + return fr.buf; +} + +static int is_elf(Elf32_Ehdr *eh) +{ + if (memcmp(eh->e_ident, ELFMAG, SELFMAG)) + return 0; /* not an ELF file */ + + if (eh->e_ident[EI_CLASS] != ELFCLASS32) + die ("Only 32-bit ELF files are supported.\n"); + if (eh->e_ident[EI_DATA] != ELFDATA2LSB || + __BYTE_ORDER != __LITTLE_ENDIAN) + die ("Can't handle big-endian or foreign-endian ELF files.\n"); + + if (eh->e_type != ET_EXEC) + die ("Can't handle relocatable ELF images.\n"); + + if (!eh->e_phoff) + die ("ELF program header table missing.\n"); + + if (eh->e_phentsize != sizeof (Elf32_Phdr)) + die ("Unexpected ELF program header table entry size.\n"); + + /* ELF file looks OK */ + return 1; +} + +static char const *pf_to_section_basename(int phdr_flags) +{ + if (phdr_flags & PF_X) return ".text"; + if (phdr_flags & PF_W) return ".data"; + /* otherwise */ return ".rodata"; +} + +static void init_rvct_asm(void) +{ + frag_init(&lfr); + frag_stpcpy(&lfr, NULL, "\tAREA\t|.rodata.loadtab|, DATA, READONLY, ALIGN=2\n\n"); + + frag_init(&zfr); + frag_stpcpy(&zfr, NULL, "\tAREA\t|.rodata.zitab|, DATA, READONLY, ALIGN=2\n\n"); + + frag_init(&binfr); +} + +static void do_phdr_rvct_asm(Elf32_Ehdr *eh, Elf32_Phdr *phdrs, unsigned index) +{ + Elf32_Phdr *p = &phdrs[index]; + + if (p->p_type != PT_LOAD) + return; /* Ignore everything except loadable segments. */ + + if (p->p_filesz) { + int fd; + FILE *f = NULL; + + do { + snprintf(path_buf, sizeof path_buf, bin_template, rand()); + fd = open(path_buf, O_WRONLY | O_CREAT | O_EXCL, + S_IRWXU | S_IRWXG | S_IRWXO); + } while(fd == EEXIST); + + if (fd == -1 || !(f = fdopen(fd, "wb"))) + die("Could not create temporary file.\n"); + + fwrite ((char *)eh + p->p_offset, 1, p->p_filesz, f); + fclose(f); + + frag_pprintf(&binfr, NULL, + "\tAREA\t|%s.load.%s.%u|, %s, %s\n\n" + "__load_%s_%u_base\n" + "\tINCBIN\t%s\n" + "__load_%s_%u_size\tEQU\t. - __load_%s_%u_base\n\n", + pf_to_section_basename(p->p_flags), + opt_basename, index, + p->p_flags & PF_X ? "CODE" : "DATA", + p->p_flags & PF_W ? "READWRITE" : "READONLY", + opt_basename, index, + path_buf, + opt_basename, index, + opt_basename, index); + + frag_pprintf(&lfr, NULL, + "\tDCD\t__load_%s_%u_base\n" + "\tDCD\t0x%08x\n" + "\tDCD\t__load_%s_%u_size\n\n", + opt_basename, index, + p->p_vaddr, + opt_basename, index); + + has_load = 1; + } + + if (p->p_filesz < p->p_memsz) { + frag_pprintf(&binfr, NULL, + "\tAREA\t|.bss.load.%s.%u|, DATA, READWRITE, NOINIT\n\n" + "__bss_%s_%u_base\n" + "\tSPACE\t%u\n" + "__bss_%s_%u_size\tEQU\t. - __bss_%s_%u_base\n\n", + opt_basename, index, + opt_basename, index, + p->p_memsz - p->p_filesz, + opt_basename, index, + opt_basename, index); + + frag_pprintf(&zfr, NULL, + "\tDCD\t0x%08x\n" + "\tDCD\t0x%08x\n\n", + p->p_vaddr + p->p_filesz, p->p_memsz - p->p_filesz); + + has_zi = 1; + } +} + +static void finish_rvct_asm(void) +{ + fputs(binfr.buf, stdout); + if (has_load) + fputs(lfr.buf, stdout); + if (has_zi) + fputs(zfr.buf, stdout); + + printf( + "\tEXPORT\t__entry_%s\n" + "__entry_%s\tEQU\t0x%08x\n" + "\n", + + opt_basename, + opt_basename, opt_entry_point + ); + + puts("\tEND"); +} + +static void init_gnu_asm(void) +{ + frag_init(&lfr); + frag_stpcpy(&lfr, NULL, ".section .rodata.loadtab, \"a\"\n.align\n\n"); + + frag_init(&zfr); + frag_stpcpy(&zfr, NULL, ".section .rodata.zitab, \"a\"\n.align\n\n"); + + frag_init(&binfr); +} + +static void do_phdr_gnu_asm(Elf32_Ehdr *eh, Elf32_Phdr *phdrs, unsigned index) +{ + Elf32_Phdr *p = &phdrs[index]; + + if (p->p_type != PT_LOAD) + return; /* Ignore everything except loadable segments. */ + + if (p->p_filesz) { + int fd; + FILE *f = NULL; + + do { + snprintf(path_buf, sizeof path_buf, bin_template, rand()); + fd = open(path_buf, O_WRONLY | O_CREAT | O_EXCL, + S_IRWXU | S_IRWXG | S_IRWXO); + } while(fd == EEXIST); + + if (fd == -1 || !(f = fdopen(fd, "wb"))) + die("Could not create temporary file.\n"); + + fwrite ((char *)eh + p->p_offset, 1, p->p_filesz, f); + fclose(f); + + frag_pprintf(&binfr, NULL, + ".section %s.load.%s.%u, \"a%s%s\"\n\n" + "__load_%s_%u_base:\n" + "\t.incbin\t\"%s\"\n" + ".equ __load_%s_%u_size, . - __load_%s_%u_base\n\n", + pf_to_section_basename(p->p_flags), + opt_basename, index, + p->p_flags & PF_X ? "x" : "", + p->p_flags & PF_W ? "w" : "", + opt_basename, index, + path_buf, + opt_basename, index, + opt_basename, index); + + frag_pprintf(&lfr, NULL, + "\t.long\t__load_%s_%u_base\n" + "\t.long\t0x%08x\n" + "\t.long\t__load_%s_%u_size\n\n", + opt_basename, index, + p->p_vaddr, + opt_basename, index); + + has_load = 1; + } + + if (p->p_filesz < p->p_memsz) { + frag_pprintf(&binfr, NULL, + ".section .bss.load.%s.%u, \"aw\", %%nobits\n\n" + "__bss_%s_%u_base:\n" + "\t.space\t%u\n" + ".equ __bss_%s_%u_size, . - __bss_%s_%u_base\n\n", + opt_basename, index, + opt_basename, index, + p->p_memsz - p->p_filesz, + opt_basename, index, + opt_basename, index); + + frag_pprintf(&zfr, NULL, + "\t.long\t0x%08x\n" + "\t.long\t0x%08x\n\n", + p->p_vaddr + p->p_filesz, p->p_memsz - p->p_filesz); + + has_zi = 1; + } +} + +static void finish_gnu_asm(void) +{ + fputs(binfr.buf, stdout); + if (has_load) + fputs(lfr.buf, stdout); + if (has_zi) + fputs(zfr.buf, stdout); + + printf( + ".globl __entry_%s\n" + ".equ __entry_%s, 0x%08x\n", + + opt_basename, + opt_basename, opt_entry_point + ); +} + +static void init_rvct_scf(void) +{ + /* Only lfr used for linker scripts: */ + frag_init(&lfr); +} + +static void do_phdr_rvct_scf(Elf32_Ehdr *eh, Elf32_Phdr *phdrs, unsigned index) +{ + Elf32_Phdr *p = &phdrs[index]; + + if (p->p_type != PT_LOAD) + return; /* Ignore everything except loadable segments. */ + + if (p->p_filesz) + frag_pprintf(&lfr, NULL, + "%s.load.%s.%u 0x%08x 0x%08x\n" + "{\n" + "\t%s.load.%s.%u 0x%08x 0x%08x\n" + "\t{\n" + "\t\t*(%s.load.%s.%u)\n" + "\t}\n" + "}\n\n", + + pf_to_section_basename(p->p_flags), + opt_basename, index, + p->p_vaddr, p->p_filesz, + pf_to_section_basename(p->p_flags), + opt_basename, index, + p->p_vaddr, p->p_filesz, + pf_to_section_basename(p->p_flags), + opt_basename, index); + + if (p->p_filesz < p->p_memsz) + frag_pprintf(&lfr, NULL, + ".bss.load.%s.%u 0x%08x 0x%08x\n" + "{\n" + "\t.bss.load.%s.%u 0x%08x 0x%08x\n" + "\t{\n" + "\t\t*(.bss.load.%s.%u)\n" + "\t}\n" + "}\n\n", + + opt_basename, index, + p->p_vaddr + p->p_filesz, + p->p_memsz - p->p_filesz, + opt_basename, index, + p->p_vaddr + p->p_filesz, + p->p_memsz - p->p_filesz, + opt_basename, index); +} + +static void finish_rvct_scf(void) +{ + fputs(lfr.buf, stdout); +} + +static void init_gnu_lds(void) +{ + /* Only lfr used for linker scripts: */ + frag_init(&lfr); +} + +static void do_phdr_gnu_lds(Elf32_Ehdr *eh, Elf32_Phdr *phdrs, unsigned index) +{ + Elf32_Phdr *p = &phdrs[index]; + + if (p->p_type != PT_LOAD) + return; /* Ignore everything except loadable segments. */ + + if (p->p_filesz) + frag_pprintf(&lfr, NULL, + "\t%s.load.%s.%u 0x%08x : {\n" + "\t\t*(%s.load.%s.%u)\n" + "\t}\n\n", + pf_to_section_basename(p->p_flags), + opt_basename, index, + p->p_vaddr, + pf_to_section_basename(p->p_flags), + opt_basename, index); + + if (p->p_filesz < p->p_memsz) + frag_pprintf(&lfr, NULL, + "\t.bss.load.%s.%u 0x%08x : {\n" + "\t\t*(.bss.load.%s.%u)\n" + "\t}\n\n", + opt_basename, index, + p->p_vaddr + p->p_filesz, + opt_basename, index); +} + +static void finish_gnu_lds(void) +{ + fputs(lfr.buf, stdout); +} + +static void init(void) +{ + personality->init(); +} + +static void do_phdr(Elf32_Ehdr *eh, Elf32_Phdr *phdrs, unsigned index) +{ + personality->do_phdr(eh, phdrs, index); +} + +static void finish(void) +{ + personality->finish(); +} + +static void do_phdrs(Elf32_Ehdr *eh, Elf32_Phdr *phdrs, unsigned count) +{ + unsigned i; + + for (i = 0; i < count; i++) + do_phdr(eh, phdrs, i); +} + +static void do_elf(void *data) +{ + Elf32_Ehdr *eh = data; + + if (opt_load_address_set) + die("Cannot override ELF image load address.\n"); + + if (!opt_entry_point_set) + opt_entry_point = eh->e_entry; + + do_phdrs(eh, (Elf32_Phdr *)((char *)eh + eh->e_phoff), eh->e_phnum); +} + +/* + * For binary files, we create a fake program header describing the whole + * file, and then process it using do_phdrs. + */ +static void do_binary(void *data, size_t size) +{ + Elf32_Phdr phdr; + + phdr.p_type = PT_LOAD; + phdr.p_offset = 0; + if (!opt_load_address_set) + die("Explicit load address required for binary images.\n"); + phdr.p_vaddr = opt_load_address; + phdr.p_filesz = phdr.p_memsz = size; + phdr.p_flags = PF_R | PF_W | PF_X; + + do_phdrs(data, &phdr, 1); +} + +static void do_file(void *data, size_t size) +{ + if (!opt_binary && !is_elf(data)) { + opt_binary = 1; + warn("assuming binary file.\n"); + } + + if (opt_binary) + do_binary(data, size); + else + do_elf(data); +} + +static void die_usage(void) +{ + fprintf(stderr, + "Usage: %s [-GRskbt] [-l <load address>] [-e <entry point>] [-n <name>] ]<image>\n", + opt_program_name); + + fprintf(stderr, +"\nOutput selection:\n" +" -G: Generate output suitable for GNU binutils (default)\n" +" -R: Generate output suitable for ARM RVCT tools\n" +" -s: Generate assembler source to embed loadable image segments (default)\n" +" Data will be placed in sections .text.load.*, .rodata.load.* etc.\n" +" as appropriate for the section attributes.\n" +"\n" +" Tables for run-time loading is placed in the .rodata.loadtab and\n" +" .rodata.zitab sections. These can be discarded at link time if\n" +" you do not plan to do run-time loading. However, you will usually\n" +" want to retain and .rodata.zitab to zero-initialise .bss data if\n" +" the target ELF loader cannot be relied upon to do it, unless you are\n" +" sure the loaded code doesn't require uninitialised data to be zero-\n" +" initialised.\n" +"\n" +" -k: Generate linker script fragment describing image load map\n" +" Normally, you only want this if you want to place the loaded image\n" +" sections directly in a combined image. If you plan to use a run-\n" +" time loader instead, you should place the loaded sections in the link\n" +" map yourself using wildcards to match the generated section names\n" +" You can then use the tables emitted in .rodata.loadtab and\n" +" .rodata.zitab to perform run-time loading.\n" +"\n" +" For GNU ld [-G], a list of output section descriptions is generated,\n" +" suitable for pasting inside the SECTIONS { } command.\n" +"\n" +" For RVCT armlink [-R], a list of root region descriptions is generated,\n" +" suitable for pasting into a scatter file at the top level.\n" +"\n" +"\nImage options:\n" +" -t: Enter image in Thumb state (default is ARM; not meaningful for ELF)\n" +" -b: Treat <image> is a binary file instead of ELF (default is to guess)\n" +" -l <load address>: Load image to the <load address> (not meaningful for ELF)\n" +" -e <entry point>: Enter image at <entry point> (not meaningful for ELF;\n" +" default is the image start address for binary images)\n" +"\nOutput options:\n" +" -n <name>: base name for sections and labels describing the input (default is\n" +" the object name minus any path or extension\n" +"\n"); + + die("Invalid argument(s).\n"); +} + +static int is_asmchar(int c) +{ + return (c >= 'A' && c <= 'Z') || + (c >= 'a' && c <= 'z') || + (c >= '0' && c <= '9') || + c == '_' || + c == '.'; +} + +static void parse_args(int argc, char **argv) +{ + char *p; + char *q; + unsigned i; + + opt_program_name = argv[0]; + + while (1) switch (getopt(argc, argv, "e:l:n:GRkstb")) { + case 'e': + opt_entry_point_set = 1; + opt_entry_point = strtoul(optarg, NULL, 0); + break; + case 'l': + opt_load_address_set = 1; + opt_load_address = strtoul(optarg, NULL, 0); + break; + case 'n': + strncpy(opt_basename, optarg, sizeof opt_basename); + break; + case 'G': + opt_toolchain = TOOLS_GNU; + break; + case 'R': + opt_toolchain = TOOLS_RVCT; + break; + case 'k': + opt_output_kind = KIND_LINKER_SCRIPT; + break; + case 's': + opt_output_kind = KIND_ASM; + break; + case 't': + opt_entry_thumb = 1; + break; + case 'b': + opt_binary = 1; + break; + case '?': + die_usage(); + case -1: + /* + * There must be a single non-option argument, which must be + * the filename of the input image: + */ + if (optind + 1 != argc) + die_usage(); + + opt_input_file = fopen(argv[optind], "rb"); + if (!(opt_input_file)) die("Cannot open input file.\n"); + + /* If no base name was specified, generate a suitable name: */ + if (!*opt_basename) { + /* + * If the image name ends with a non-empty string + * following a path, keep only that trailing + * string: + */ + p = strrchr(argv[optind], '/'); + if (p) { + if (p[1]) + ++p; + else + p = NULL; + } + + strncpy(opt_basename, p ? p : argv[optind], + sizeof opt_basename); + + /* + * Remove special characters not allowed by the + * assembler / object format, and chop off any trailing + * extension, unless the resulting string would be + * empty: + */ + for (p = opt_basename; *p; p++) { + if (*p == '.' && p != opt_basename) { + *p = '\0'; + break; + } else if (!(is_asmchar(*p))) + *p = '_'; + } + } + + goto args_done; + } + +args_done: + if (opt_entry_thumb && opt_entry_point_set) opt_entry_point |= 1; + + /* + * Generate format template for temporary files into which to + * dump the loadable segment contents of the input image. + * The base pathname for these files is that of the input image, + */ + p = strrchr(argv[optind], '/'); + p = p ? p + 1 : argv[optind]; /* p = the name with the path removed */ + q = strrchr(p, '.'); + + if (q && q != p) *q = '\0'; + snprintf(bin_template, sizeof bin_template, "%s-%%d.bin", argv[optind]); + + for (i = 0; i < lengthof(personalities); i++) { + if (personalities[i].tools == opt_toolchain && + personalities[i].kind == opt_output_kind) { + personality = &personalities[i]; + return; + } + } + + die ("Unsupported configuration\n"); +} + +/* + * We consciously don't try to be robust against corrupt or invalid ELF + * files. If an out-of-range memory access happens, we'll segfault. + */ +int main(int argc, char **argv) +{ + size_t input_size; + char *data; + + parse_args(argc, argv); + + data = fslurp(opt_input_file, &input_size); + + srand(time(NULL) ^ getpid()); + init(); + + do_file(data, input_size); + /* data memory leaked, but we're about to quit, so it doesn't matter */ + + finish(); + + if (fclose(stdout)) + die("I/O error"); + + return EXIT_SUCCESS; +} |