aboutsummaryrefslogtreecommitdiff
path: root/linaro/arm-virt-bl/scripts/loadmap.c
diff options
context:
space:
mode:
Diffstat (limited to 'linaro/arm-virt-bl/scripts/loadmap.c')
-rw-r--r--linaro/arm-virt-bl/scripts/loadmap.c861
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;
+}