util: postprocess: remove support for elf, restructure relocation code

Change-Id: Ie6b4b52c3e26ca6abc2d5067b5f97290c50f7105
Signed-off-by: Alexey Polyudov <apolyudov@google.com>
(cherry picked from commit d489d84b219a2b635d30bd4314f6698fbffe98a8)
diff --git a/util/nanoapp_postprocess/Android.mk b/util/nanoapp_postprocess/Android.mk
index 89a62a3..b20deec 100644
--- a/util/nanoapp_postprocess/Android.mk
+++ b/util/nanoapp_postprocess/Android.mk
@@ -33,13 +33,6 @@
 
 LOCAL_MODULE := nanoapp_postprocess
 
-# libelf needed for ELF parsing support, libz required by libelf
-LOCAL_STATIC_LIBRARIES := libelf libz
-
-# Statically linking libc++ so this binary can be copied out of the tree and
-# still work (needed by dependencies)
-LOCAL_CXX_STL := libc++_static
-
 LOCAL_MODULE_TAGS := optional
 
 include $(BUILD_HOST_EXECUTABLE)
diff --git a/util/nanoapp_postprocess/Makefile b/util/nanoapp_postprocess/Makefile
index 9b05916..469e686 100644
--- a/util/nanoapp_postprocess/Makefile
+++ b/util/nanoapp_postprocess/Makefile
@@ -20,7 +20,7 @@
 CC_FLAGS = -Wall -Wextra -Werror -I../../lib/include --std=c99
 
 $(APP): $(SRC) Makefile
-	$(CC) $(CC_FLAGS) -o $(APP) -O2 $(SRC) -lelf
+	$(CC) $(CC_FLAGS) -o $(APP) -O2 $(SRC)
 
 clean:
 	rm -f $(APP)
\ No newline at end of file
diff --git a/util/nanoapp_postprocess/postprocess.c b/util/nanoapp_postprocess/postprocess.c
index 739454c..e498419 100644
--- a/util/nanoapp_postprocess/postprocess.c
+++ b/util/nanoapp_postprocess/postprocess.c
@@ -16,8 +16,6 @@
 
 #include <assert.h>
 #include <fcntl.h>
-#include <gelf.h>
-#include <libelf.h>
 #include <sys/types.h>
 #include <stdbool.h>
 #include <unistd.h>
@@ -72,115 +70,128 @@
     uint8_t type;
 };
 
+struct NanoAppInfo {
+    union {
+        struct BinHdr *bin;
+        uint8_t *data;
+    };
+    size_t dataSizeUsed;
+    size_t dataSizeAllocated;
+    size_t codeAndDataSize;   // not including symbols, relocs and BinHdr
+    size_t codeAndRoDataSize; // also not including GOT & RW data in flash
+    struct SymtabEntry *symtab;
+    size_t symtabSize; // number of symbols
+    struct RelocEntry *reloc;
+    size_t relocSize; // number of reloc entries
+    struct NanoRelocEntry *nanoReloc;
+    size_t nanoRelocSize; // number of nanoReloc entries <= relocSize
+    uint8_t *packedNanoReloc;
+    size_t packedNanoRelocSize;
+
+    bool debug;
+};
+
 #ifndef ARRAY_SIZE
 #define ARRAY_SIZE(ary) (sizeof(ary) / sizeof((ary)[0]))
 #endif
 
-#define DBG(fmt, ...) printf(fmt "\n", ##__VA_ARGS__)
+static FILE *stdlog = NULL;
+
+#define DBG(fmt, ...) fprintf(stdlog, fmt "\n", ##__VA_ARGS__)
 #define ERR(fmt, ...) fprintf(stderr, fmt "\n", ##__VA_ARGS__)
 
-// Prints the given message followed by the most recent libelf error
-#define ELF_ERR(fmt, ...) ERR(fmt ": %s\n", ##__VA_ARGS__, elf_errmsg(-1))
-
-struct ElfAppSection {
-    void  *data;
-    size_t size;
-};
-
-struct ElfNanoApp {
-    struct ElfAppSection flash;
-    struct ElfAppSection data;
-    struct ElfAppSection relocs;
-    struct ElfAppSection symtab;
-
-    // Not parsed from file, but constructed via genElfNanoRelocs
-    struct ElfAppSection packedNanoRelocs;
-};
-
 static void fatalUsage(const char *name, const char *msg, const char *arg)
 {
     if (msg && arg)
-        fprintf(stderr, "Error: %s: %s\n\n", msg, arg);
+        ERR("Error: %s: %s\n", msg, arg);
     else if (msg)
-        fprintf(stderr, "Error: %s\n\n", msg);
+        ERR("Error: %s\n", msg);
 
-    fprintf(stderr, "USAGE: %s [-v] [-k <key id>] [-a <app id>] [-r] [-n <layout name>] [-i <layout id>] <input file> [<output file>]\n"
-                    "       -v               : be verbose\n"
-                    "       -n <layout name> : app, os, key\n"
-                    "       -i <layout id>   : 1 (app), 2 (key), 3 (os)\n"
-                    "       -f <layout flags>: 16-bit hex value, stored as layout-specific flags\n"
-                    "       -a <app ID>      : 64-bit hex number != 0\n"
-                    "       -k <key ID>      : 64-bit hex number != 0\n"
-                    "       -r               : bare (no AOSP header); used only for inner OS image generation\n"
-                    "       -s               : treat input as statically linked ELF (app layout only)\n"
-                    "       layout ID and layout name control the same parameter, so only one of them needs to be used\n"
-                    , name);
+    ERR("USAGE: %s [-v] [-k <key id>] [-a <app id>] [-r] [-n <layout name>] [-i <layout id>] <input file> [<output file>]\n"
+        "       -v               : be verbose\n"
+        "       -n <layout name> : app, os, key\n"
+        "       -i <layout id>   : 1 (app), 2 (key), 3 (os)\n"
+        "       -f <layout flags>: 16-bit hex value, stored as layout-specific flags\n"
+        "       -a <app ID>      : 64-bit hex number != 0\n"
+        "       -k <key ID>      : 64-bit hex number != 0\n"
+        "       -r               : bare (no AOSP header); used only for inner OS image generation\n"
+        "       layout ID and layout name control the same parameter, so only one of them needs to be used\n"
+        , name);
     exit(1);
 }
 
-static uint8_t *packNanoRelocs(struct NanoRelocEntry *nanoRelocs, uint32_t outNumRelocs, uint32_t *finalPackedNanoRelocSz, bool verbose)
+bool packNanoRelocs(struct NanoAppInfo *app)
 {
-    uint32_t i, j, k;
+    size_t i, j, k;
     uint8_t *packedNanoRelocs;
     uint32_t packedNanoRelocSz;
     uint32_t lastOutType = 0, origin = 0;
+    bool verbose = app->debug;
 
     //sort by type and then offset
-    for (i = 0; i < outNumRelocs; i++) {
+    for (i = 0; i < app->nanoRelocSize; i++) {
         struct NanoRelocEntry t;
 
-        for (k = i, j = k + 1; j < outNumRelocs; j++) {
-            if (nanoRelocs[j].type > nanoRelocs[k].type)
+        for (k = i, j = k + 1; j < app->nanoRelocSize; j++) {
+            if (app->nanoReloc[j].type > app->nanoReloc[k].type)
                 continue;
-            if ((nanoRelocs[j].type < nanoRelocs[k].type) || (nanoRelocs[j].ofstInRam < nanoRelocs[k].ofstInRam))
+            if ((app->nanoReloc[j].type < app->nanoReloc[k].type) || (app->nanoReloc[j].ofstInRam < app->nanoReloc[k].ofstInRam))
                 k = j;
         }
-        memcpy(&t, nanoRelocs + i, sizeof(struct NanoRelocEntry));
-        memcpy(nanoRelocs + i, nanoRelocs + k, sizeof(struct NanoRelocEntry));
-        memcpy(nanoRelocs + k, &t, sizeof(struct NanoRelocEntry));
+        memcpy(&t, app->nanoReloc + i, sizeof(struct NanoRelocEntry));
+        memcpy(app->nanoReloc + i, app->nanoReloc + k, sizeof(struct NanoRelocEntry));
+        memcpy(app->nanoReloc + k, &t, sizeof(struct NanoRelocEntry));
 
-        if (verbose)
-            fprintf(stderr, "SortedReloc[%3" PRIu32 "] = {0x%08" PRIX32 ",0x%02" PRIX8 "}\n", i, nanoRelocs[i].ofstInRam, nanoRelocs[i].type);
+        if (app->debug)
+            DBG("SortedReloc[%3zu] = {0x%08" PRIX32 ",0x%02" PRIX8 "}", i, app->nanoReloc[i].ofstInRam, app->nanoReloc[i].type);
     }
 
     //produce output nanorelocs in packed format
-    packedNanoRelocs = malloc(outNumRelocs * 6); //definitely big enough
+    packedNanoRelocs = malloc(app->nanoRelocSize * 6); //definitely big enough
     packedNanoRelocSz = 0;
-    for (i = 0; i < outNumRelocs; i++) {
+
+    if (!packedNanoRelocs) {
+        ERR("Failed to allocate memory for packed relocs");
+        return false;
+    }
+
+    for (i = 0; i < app->nanoRelocSize; i++) {
         uint32_t displacement;
 
-        if (lastOutType != nanoRelocs[i].type) {  //output type if ti changed
-            if (nanoRelocs[i].type - lastOutType == 1) {
+        if (lastOutType != app->nanoReloc[i].type) {  //output type if ti changed
+            if (app->nanoReloc[i].type - lastOutType == 1) {
                 packedNanoRelocs[packedNanoRelocSz++] = TOKEN_RELOC_TYPE_NEXT;
                 if (verbose)
-                    fprintf(stderr, "Out: RelocTC (1) // to 0x%02" PRIX8 "\n", nanoRelocs[i].type);
-            }
-            else {
+                    DBG("Out: RelocTC [size 1] // to 0x%02" PRIX8, app->nanoReloc[i].type);
+            } else {
                 packedNanoRelocs[packedNanoRelocSz++] = TOKEN_RELOC_TYPE_CHG;
-                packedNanoRelocs[packedNanoRelocSz++] = nanoRelocs[i].type - lastOutType - 1;
+                packedNanoRelocs[packedNanoRelocSz++] = app->nanoReloc[i].type - lastOutType - 1;
                 if (verbose)
-                    fprintf(stderr, "Out: RelocTC (0x%02" PRIX8 ")  // to 0x%02" PRIX8 "\n", (uint8_t)(nanoRelocs[i].type - lastOutType - 1), nanoRelocs[i].type);
+                    DBG("Out: RelocTC [size 2] (0x%02" PRIX8 ")  // to 0x%02" PRIX8,
+                        (uint8_t)(app->nanoReloc[i].type - lastOutType - 1), app->nanoReloc[i].type);
             }
-            lastOutType = nanoRelocs[i].type;
+            lastOutType = app->nanoReloc[i].type;
             origin = 0;
         }
-        displacement = nanoRelocs[i].ofstInRam - origin;
-        origin = nanoRelocs[i].ofstInRam + 4;
+        displacement = app->nanoReloc[i].ofstInRam - origin;
+        origin = app->nanoReloc[i].ofstInRam + 4;
         if (displacement & 3) {
-            fprintf(stderr, "Unaligned relocs are not possible!\n");
-            exit(-5);
+            ERR("Unaligned relocs are not possible!");
+            return false;
         }
         displacement /= 4;
 
         //might be start of a run. look into that
         if (!displacement) {
-            for (j = 1; j + i < outNumRelocs && j < MAX_RUN_LEN && nanoRelocs[j + i].type == lastOutType && nanoRelocs[j + i].ofstInRam - nanoRelocs[j + i - 1].ofstInRam == 4; j++);
+            for (j = 1; (j + i) < app->nanoRelocSize && j < MAX_RUN_LEN &&
+                        app->nanoReloc[j + i].type == lastOutType &&
+                        (app->nanoReloc[j + i].ofstInRam - app->nanoReloc[j + i - 1].ofstInRam) == 4; j++);
             if (j >= MIN_RUN_LEN) {
                 if (verbose)
-                    fprintf(stderr, "Out: Reloc0  x%" PRIX32 "\n", j);
+                    DBG("Out: Reloc0 [size 2]; repeat=%zu", j);
                 packedNanoRelocs[packedNanoRelocSz++] = TOKEN_CONSECUTIVE;
                 packedNanoRelocs[packedNanoRelocSz++] = j - MIN_RUN_LEN;
-                origin = nanoRelocs[j + i - 1].ofstInRam + 4;  //reset origin to last one
+                origin = app->nanoReloc[j + i - 1].ofstInRam + 4;  //reset origin to last one
                 i += j - 1;  //loop will increment anyways, hence +1
                 continue;
             }
@@ -189,29 +200,26 @@
         //produce output
         if (displacement <= MAX_8_BIT_NUM) {
             if (verbose)
-                fprintf(stderr, "Out: Reloc8  0x%02" PRIX32 "\n", displacement);
+                DBG("Out: Reloc8 [size 1] 0x%02" PRIX32, displacement);
             packedNanoRelocs[packedNanoRelocSz++] = displacement;
-        }
-        else if (displacement <= MAX_16_BIT_NUM) {
+        } else if (displacement <= MAX_16_BIT_NUM) {
             if (verbose)
-                fprintf(stderr, "Out: Reloc16 0x%06" PRIX32 "\n", displacement);
+                DBG("Out: Reloc16 [size 3] 0x%06" PRIX32, displacement);
                         displacement -= MAX_8_BIT_NUM;
             packedNanoRelocs[packedNanoRelocSz++] = TOKEN_16BIT_OFST;
             packedNanoRelocs[packedNanoRelocSz++] = displacement;
             packedNanoRelocs[packedNanoRelocSz++] = displacement >> 8;
-        }
-        else if (displacement <= MAX_24_BIT_NUM) {
+        } else if (displacement <= MAX_24_BIT_NUM) {
             if (verbose)
-                fprintf(stderr, "Out: Reloc24 0x%08" PRIX32 "\n", displacement);
+                DBG("Out: Reloc24 [size 4] 0x%08" PRIX32, displacement);
                         displacement -= MAX_16_BIT_NUM;
             packedNanoRelocs[packedNanoRelocSz++] = TOKEN_24BIT_OFST;
             packedNanoRelocs[packedNanoRelocSz++] = displacement;
             packedNanoRelocs[packedNanoRelocSz++] = displacement >> 8;
             packedNanoRelocs[packedNanoRelocSz++] = displacement >> 16;
-        }
-        else  {
+        } else {
             if (verbose)
-                fprintf(stderr, "Out: Reloc32 0x%08" PRIX32 "\n", displacement);
+                DBG("Out: Reloc32 [size 5] 0x%08" PRIX32, displacement);
             packedNanoRelocs[packedNanoRelocSz++] = TOKEN_32BIT_OFST;
             packedNanoRelocs[packedNanoRelocSz++] = displacement;
             packedNanoRelocs[packedNanoRelocSz++] = displacement >> 8;
@@ -220,16 +228,18 @@
         }
     }
 
-    *finalPackedNanoRelocSz = packedNanoRelocSz;
-    return packedNanoRelocs;
+    app->packedNanoReloc = packedNanoRelocs;
+    app->packedNanoRelocSize = packedNanoRelocSz;
+
+    return true;
 }
 
-static int finalizeAndWrite(uint8_t *buf, uint32_t bufUsed, uint32_t bufSz, FILE *out, uint32_t layoutFlags, uint64_t appId)
+static int finalizeAndWrite(struct NanoAppInfo *inf, FILE *out, uint32_t layoutFlags, uint64_t appId)
 {
-    int ret;
+    bool good = true;
     struct AppInfo app;
     struct SectInfo *sect;
-    struct BinHdr *bin = (struct BinHdr *) buf;
+    struct BinHdr *bin = inf->bin;
     struct ImageHeader outHeader = {
         .aosp = (struct nano_app_binary_t) {
             .header_version = 1,
@@ -245,58 +255,159 @@
             .flags = layoutFlags,
         },
     };
-    uint32_t dataOffset = sizeof(outHeader) + sizeof(app);
-    uint32_t hdrDiff = dataOffset - sizeof(*bin);
+
     app.sect = bin->sect;
     app.vec  = bin->vec;
-
-    assertMem(bufUsed + hdrDiff, bufSz);
-
-    memmove(buf + dataOffset, buf + sizeof(*bin), bufUsed - sizeof(*bin));
-    bufUsed += hdrDiff;
-    memcpy(buf, &outHeader, sizeof(outHeader));
-    memcpy(buf + sizeof(outHeader), &app, sizeof(app));
     sect = &app.sect;
 
     //if we have any bytes to output, show stats
-    if (bufUsed) {
-        uint32_t codeAndRoDataSz = sect->data_data;
-        uint32_t relocsSz = sect->rel_end - sect->rel_start;
-        uint32_t gotSz = sect->got_end - sect->data_start;
-        uint32_t bssSz = sect->bss_end - sect->bss_start;
+    if (inf->codeAndRoDataSize) {
+        size_t binarySize = 0;
+        size_t gotSz = sect->got_end - sect->data_start;
+        size_t bssSz = sect->bss_end - sect->bss_start;
 
-        fprintf(stderr,"Final binary size %" PRIu32 " bytes\n", bufUsed);
-        fprintf(stderr, "\n");
-        fprintf(stderr, "       FW header size (flash):      %6zu bytes\n", FLASH_RELOC_OFFSET);
-        fprintf(stderr, "       Code + RO data (flash):      %6" PRIu32 " bytes\n", codeAndRoDataSz);
-        fprintf(stderr, "       Relocs (flash):              %6" PRIu32 " bytes\n", relocsSz);
-        fprintf(stderr, "       GOT + RW data (flash & RAM): %6" PRIu32 " bytes\n", gotSz);
-        fprintf(stderr, "       BSS (RAM):                   %6" PRIu32 " bytes\n", bssSz);
-        fprintf(stderr, "\n");
-        fprintf(stderr,"Runtime flash use: %" PRIu32 " bytes\n", (uint32_t)(codeAndRoDataSz + relocsSz + gotSz + FLASH_RELOC_OFFSET));
-        fprintf(stderr,"Runtime RAM use: %" PRIu32 " bytes\n", gotSz + bssSz);
+        good = fwrite(&outHeader, sizeof(outHeader), 1, out) == 1 && good;
+        binarySize += sizeof(outHeader);
+
+        good = fwrite(&app, sizeof(app), 1, out) == 1 && good;
+        binarySize += sizeof(app);
+
+        good = fwrite(&bin[1], inf->codeAndDataSize, 1, out) == 1 && good;
+        binarySize += inf->codeAndDataSize;
+
+        if (inf->packedNanoReloc && inf->packedNanoRelocSize) {
+            good = fwrite(inf->packedNanoReloc, inf->packedNanoRelocSize, 1, out) == 1 && good;
+            binarySize += inf->packedNanoRelocSize;
+        }
+
+        if (!good) {
+            ERR("Failed to write output file: %s\n", strerror(errno));
+        } else {
+            DBG("Final binary size %zu bytes", binarySize);
+            DBG("");
+            DBG("       FW header size (flash):      %6zu bytes", FLASH_RELOC_OFFSET);
+            DBG("       Code + RO data (flash):      %6zu bytes", inf->codeAndRoDataSize);
+            DBG("       Relocs (flash):              %6zu bytes", inf->packedNanoRelocSize);
+            DBG("       GOT + RW data (flash & RAM): %6zu bytes", gotSz);
+            DBG("       BSS (RAM):                   %6zu bytes", bssSz);
+            DBG("");
+            DBG("Runtime flash use: %zu bytes",
+                (size_t)(inf->codeAndRoDataSize + inf->packedNanoRelocSize + gotSz + FLASH_RELOC_OFFSET));
+            DBG("Runtime RAM use: %zu bytes", gotSz + bssSz);
+        }
     }
 
-    ret = fwrite(buf, bufUsed, 1, out) == 1 ? 0 : 2;
-    if (ret)
-        fprintf(stderr, "Failed to write output file: %s\n", strerror(errno));
+    return good ? 0 : 2;
+}
 
-    return ret;
+// Subtracts the fixed memory region offset from an absolute address and returns
+// the associated NANO_RELOC_* value, or NANO_RELOC_LAST if the address is not
+// in the expected range.
+static uint8_t fixupAddress(uint32_t *addr, struct SymtabEntry *sym, bool debug)
+{
+    uint8_t type;
+    uint32_t old = *addr;
+
+    (*addr) += sym->addr;
+    // TODO: this assumes that the host running this tool has the same
+    // endianness as the image file/target processor
+    if (IS_IN_RAM(*addr)) {
+        *addr -= RAM_BASE;
+        type = NANO_RELOC_TYPE_RAM;
+        if (debug)
+            DBG("Fixup addr 0x%08" PRIX32 " (RAM) --> 0x%08" PRIX32, old, *addr);
+    } else if (IS_IN_FLASH(*addr)) {
+        *addr -= FLASH_BASE + BINARY_RELOC_OFFSET;
+        type = NANO_RELOC_TYPE_FLASH;
+        if (debug)
+            DBG("Fixup addr 0x%08" PRIX32 " (FLASH) --> 0x%08" PRIX32, old, *addr);
+    } else {
+        ERR("Error: invalid address 0x%08" PRIX32, *addr);
+        type = NANO_RELOC_LAST;
+    }
+
+    return type;
+}
+
+static void relocDiag(const struct NanoAppInfo *app, const struct RelocEntry *reloc, const char *msg)
+{
+    size_t symIdx = reloc->info >> 8;
+    uint8_t symType = reloc->info;
+
+    ERR("Reloc %zu %s", reloc - app->reloc, msg);
+    ERR("INFO:");
+    ERR("        Where: 0x%08" PRIX32, reloc->where);
+    ERR("        type: %" PRIu8, symType);
+    ERR("        sym: %zu", symIdx);
+    if (symIdx < app->symtabSize) {
+        struct SymtabEntry *sym = &app->symtab[symIdx];
+        ERR("        addr: %" PRIu32, sym->addr);
+    } else {
+        ERR("        addr: <invalid>");
+    }
+}
+
+static uint8_t fixupReloc(struct NanoAppInfo *app, struct RelocEntry *reloc,
+                          struct SymtabEntry *sym, struct NanoRelocEntry *nanoReloc)
+{
+    uint8_t type;
+    uint32_t *addr;
+    uint32_t relocOffset = reloc->where;
+    uint32_t flashDataOffset = 0;
+
+    if (IS_IN_FLASH(relocOffset)) {
+        relocOffset -= FLASH_BASE;
+        flashDataOffset = 0;
+    } else if (IS_IN_RAM(reloc->where)) {
+        relocOffset = reloc->where - RAM_BASE;
+        flashDataOffset = app->bin->sect.data_data - FLASH_BASE;
+    } else {
+        relocDiag(app, reloc, "is neither in RAM nor in FLASH");
+        return NANO_RELOC_LAST;
+    }
+
+    addr = (uint32_t*)(app->data + flashDataOffset + relocOffset);
+
+    if (flashDataOffset + relocOffset >= app->dataSizeUsed - sizeof(*addr)) {
+        relocDiag(app, reloc, "points outside valid data area");
+        return NANO_RELOC_LAST;
+    }
+
+    switch (reloc->info & 0xFF) {
+    case RELOC_TYPE_ABS_S:
+    case RELOC_TYPE_ABS_D:
+        type = fixupAddress(addr, sym, app->debug);
+        break;
+
+    case RELOC_TYPE_SECT:
+        if (sym->addr) {
+            relocDiag(app, reloc, "has section relocation with non-zero symbol address");
+            return NANO_RELOC_LAST;
+        }
+        type = fixupAddress(addr, sym, app->debug);
+        break;
+    default:
+        relocDiag(app, reloc, "has unknown type");
+        type = NANO_RELOC_LAST;
+    }
+
+    if (nanoReloc && type != NANO_RELOC_LAST) {
+        nanoReloc->ofstInRam = relocOffset;
+        nanoReloc->type = type;
+    }
+
+    return type;
 }
 
 static int handleApp(uint8_t **pbuf, uint32_t bufUsed, FILE *out, uint32_t layoutFlags, uint64_t appId, bool verbose)
 {
-    uint32_t i, numRelocs, numSyms, outNumRelocs = 0, packedNanoRelocSz;
-    struct NanoRelocEntry *nanoRelocs = NULL;
-    struct RelocEntry *relocs;
-    struct SymtabEntry *syms;
-    uint8_t *packedNanoRelocs;
-    uint32_t t;
+    uint32_t i;
     struct BinHdr *bin;
     int ret = -1;
     struct SectInfo *sect;
     uint8_t *buf = *pbuf;
     uint32_t bufSz = bufUsed * 3 /2;
+    struct NanoAppInfo app;
 
     //make buffer 50% bigger than bufUsed in case relocs grow out of hand
     buf = reallocOrDie(buf, bufSz);
@@ -305,212 +416,139 @@
     //sanity checks
     bin = (struct BinHdr*)buf;
     if (bufUsed < sizeof(*bin)) {
-        fprintf(stderr, "File size too small\n");
+        ERR("File size too small: %" PRIu32, bufUsed);
         goto out;
     }
 
     if (bin->hdr.magic != NANOAPP_FW_MAGIC) {
-        fprintf(stderr, "Magic value is wrong: found %08" PRIX32
-                        "; expected %08" PRIX32 "\n",
-                        bin->hdr.magic, NANOAPP_FW_MAGIC);
+        ERR("Magic value is wrong: found %08" PRIX32"; expected %08" PRIX32, bin->hdr.magic, NANOAPP_FW_MAGIC);
         goto out;
     }
 
     sect = &bin->sect;
 
-    //do some math
-    relocs = (struct RelocEntry*)(buf + sect->rel_start - FLASH_BASE);
-    syms = (struct SymtabEntry*)(buf + sect->rel_end - FLASH_BASE);
-    numRelocs = (sect->rel_end - sect->rel_start) / sizeof(struct RelocEntry);
-    numSyms = (bufUsed + FLASH_BASE - sect->rel_end) / sizeof(struct SymtabEntry);
-
-    //sanity
-    if (numRelocs * sizeof(struct RelocEntry) + sect->rel_start != sect->rel_end) {
-        fprintf(stderr, "Relocs of nonstandard size\n");
+    if (!IS_IN_FLASH(sect->rel_start) || !IS_IN_FLASH(sect->rel_end) || !IS_IN_FLASH(sect->data_data)) {
+        ERR("relocation data or initialized data is not in FLASH");
         goto out;
     }
-    if (numSyms * sizeof(struct SymtabEntry) + sect->rel_end != bufUsed + FLASH_BASE) {
-        fprintf(stderr, "Syms of nonstandard size\n");
+    if (!IS_IN_RAM(sect->data_start) || !IS_IN_RAM(sect->data_end) || !IS_IN_RAM(sect->bss_start) ||
+        !IS_IN_RAM(sect->bss_end) || !IS_IN_RAM(sect->got_start) || !IS_IN_RAM(sect->got_end)) {
+        ERR("data, bss, or got not in ram\n");
+        goto out;
+    }
+
+    //do some math
+    app.reloc = (struct RelocEntry*)(buf + sect->rel_start - FLASH_BASE);
+    app.symtab = (struct SymtabEntry*)(buf + sect->rel_end - FLASH_BASE);
+    app.relocSize = (sect->rel_end - sect->rel_start) / sizeof(struct RelocEntry);
+    app.nanoRelocSize = 0;
+    app.symtabSize = (struct SymtabEntry*)(buf + bufUsed) - app.symtab;
+    app.data = buf;
+    app.dataSizeAllocated = bufSz;
+    app.dataSizeUsed = bufUsed;
+    app.codeAndRoDataSize = sect->data_data - FLASH_BASE - sizeof(*bin);
+    app.codeAndDataSize = sect->rel_start - FLASH_BASE - sizeof(*bin);
+    app.debug = verbose;
+    app.nanoReloc = NULL;
+    app.packedNanoReloc = NULL;
+
+    //sanity
+    if (app.relocSize * sizeof(struct RelocEntry) + sect->rel_start != sect->rel_end) {
+        ERR("Relocs of nonstandard size");
+        goto out;
+    }
+    if (app.symtabSize * sizeof(struct SymtabEntry) + sect->rel_end != bufUsed + FLASH_BASE) {
+        ERR("Syms of nonstandard size");
         goto out;
     }
 
     //show some info
-    fprintf(stderr, "\nRead %" PRIu32 " bytes of binary.\n", bufUsed);
 
     if (verbose)
-        fprintf(stderr, "Found %" PRIu32 " relocs and a %" PRIu32 "-entry symbol table\n", numRelocs, numSyms);
+        DBG("Found %zu relocs and a %zu-entry symbol table", app.relocSize, app.symtabSize);
 
     //handle relocs
-    nanoRelocs = malloc(sizeof(struct NanoRelocEntry[numRelocs]));
-    if (!nanoRelocs) {
-        fprintf(stderr, "Failed to allocate a nano-reloc table\n");
+    app.nanoReloc = malloc(sizeof(struct NanoRelocEntry[app.relocSize]));
+    if (!app.nanoReloc) {
+        ERR("Failed to allocate a nano-reloc table\n");
         goto out;
     }
 
-    for (i = 0; i < numRelocs; i++) {
-        uint32_t relocType = relocs[i].info & 0xff;
-        uint32_t whichSym = relocs[i].info >> 8;
-        uint32_t *valThereP;
+    for (i = 0; i < app.relocSize; i++) {
+        struct RelocEntry *reloc = &app.reloc[i];
+        struct NanoRelocEntry *nanoReloc = &app.nanoReloc[app.nanoRelocSize];
+        uint32_t relocType = reloc->info & 0xff;
+        uint32_t whichSym = reloc->info >> 8;
+        struct SymtabEntry *sym = &app.symtab[whichSym];
 
-        if (whichSym >= numSyms) {
-            fprintf(stderr, "Reloc %" PRIu32 " references a nonexistent symbol!\n"
-                            "INFO:\n"
-                            "        Where: 0x%08" PRIX32 "\n"
-                            "        type: %" PRIu32 "\n"
-                            "        sym: %" PRIu32 "\n",
-                i, relocs[i].where, relocs[i].info & 0xff, whichSym);
+        if (whichSym >= app.symtabSize) {
+            relocDiag(&app, reloc, "references a nonexistent symbol");
             goto out;
         }
 
         if (verbose) {
             const char *seg;
 
-            fprintf(stderr, "Reloc[%3" PRIu32 "]:\n {@0x%08" PRIX32 ", type %3" PRIu32 ", -> sym[%3" PRIu32 "]: {@0x%08" PRIX32 "}, ",
-                i, relocs[i].where, relocs[i].info & 0xff, whichSym, syms[whichSym].addr);
-
-            if (IS_IN_RANGE_E(relocs[i].where, sect->bss_start, sect->bss_end))
+            if (IS_IN_RANGE_E(reloc->where, sect->bss_start, sect->bss_end))
                 seg = ".bss";
-            else if (IS_IN_RANGE_E(relocs[i].where, sect->data_start, sect->data_end))
+            else if (IS_IN_RANGE_E(reloc->where, sect->data_start, sect->data_end))
                 seg = ".data";
-            else if (IS_IN_RANGE_E(relocs[i].where, sect->got_start, sect->got_end))
+            else if (IS_IN_RANGE_E(reloc->where, sect->got_start, sect->got_end))
                 seg = ".got";
-            else if (IS_IN_RANGE_E(relocs[i].where, FLASH_BASE, FLASH_BASE + sizeof(struct BinHdr)))
+            else if (IS_IN_RANGE_E(reloc->where, FLASH_BASE, FLASH_BASE + sizeof(struct BinHdr)))
                 seg = "APPHDR";
             else
                 seg = "???";
 
-            fprintf(stderr, "in   %s}\n", seg);
+            DBG("Reloc[%3" PRIu32 "]:\n {@0x%08" PRIX32 ", type %3" PRIu32 ", -> sym[%3" PRIu32 "]: {@0x%08" PRIX32 "}, in   %s}",
+                i, reloc->where, reloc->info & 0xff, whichSym, sym->addr, seg);
         }
         /* handle relocs inside the header */
-        if (IS_IN_FLASH(relocs[i].where) && relocs[i].where - FLASH_BASE < sizeof(struct BinHdr) && relocType == RELOC_TYPE_SECT) {
+        if (IS_IN_FLASH(reloc->where) && reloc->where - FLASH_BASE < sizeof(struct BinHdr) && relocType == RELOC_TYPE_SECT) {
             /* relocs in header are special - runtime corrects for them */
-            if (syms[whichSym].addr) {
-                fprintf(stderr, "Weird in-header sect reloc %" PRIu32 " to symbol %" PRIu32 " with nonzero addr 0x%08" PRIX32 "\n",
-                        i, whichSym, syms[whichSym].addr);
-                goto out;
-            }
-
-            valThereP = (uint32_t*)(buf + relocs[i].where - FLASH_BASE);
-            if (!IS_IN_FLASH(*valThereP)) {
-                fprintf(stderr, "In-header reloc %" PRIu32 " of location 0x%08" PRIX32 " is outside of FLASH!\n"
-                                "INFO:\n"
-                                "        type: %" PRIu32 "\n"
-                                "        sym: %" PRIu32 "\n"
-                                "        Sym Addr: 0x%08" PRIX32 "\n",
-                                i, relocs[i].where, relocType, whichSym, syms[whichSym].addr);
-                goto out;
-            }
-
-            // binary header generated by objcopy, .napp header and final FW header in flash are of different size.
+            // binary header generated by objcopy, .napp header and final FW header in flash are of different layout and size.
             // we subtract binary header offset here, so all the entry points are relative to beginning of "sect".
             // FW will use &sect as a base to call these vectors; no more problems with different header sizes;
             // Assumption: offsets between sect & vec, vec & code are the same in all images (or, in a simpler words, { sect, vec, code }
             // must go together). this is enforced by linker script, and maintained by all tools and FW download code in the OS.
-            *valThereP -= FLASH_BASE + BINARY_RELOC_OFFSET;
+
+            switch (fixupReloc(&app, reloc, sym, NULL)) {
+            case NANO_RELOC_TYPE_RAM:
+                relocDiag(&app, reloc, "is in APPHDR but relocated to RAM");
+                goto out;
+            case NANO_RELOC_TYPE_FLASH:
+                break;
+            default:
+                // other error happened; it is already reported
+                goto out;
+            }
 
             if (verbose)
-                fprintf(stderr, "  -> Nano reloc skipped for in-header reloc\n");
+                DBG("  -> Nano reloc skipped for in-header reloc");
 
             continue; /* do not produce an output reloc */
         }
 
-        if (!IS_IN_RAM(relocs[i].where)) {
-            fprintf(stderr, "In-header reloc %" PRIu32 " of location 0x%08" PRIX32 " is outside of RAM!\n"
-                            "INFO:\n"
-                            "        type: %" PRIu32 "\n"
-                            "        sym: %" PRIu32 "\n"
-                            "        Sym Addr: 0x%08" PRIX32 "\n",
-                            i, relocs[i].where, relocType, whichSym, syms[whichSym].addr);
+        // any other relocs may only happen in RAM
+        if (!IS_IN_RAM(reloc->where)) {
+            relocDiag(&app, reloc, "is not in RAM");
             goto out;
         }
 
-        valThereP = (uint32_t*)(buf + relocs[i].where + sect->data_data - RAM_BASE - FLASH_BASE);
-
-        nanoRelocs[outNumRelocs].ofstInRam = relocs[i].where - RAM_BASE;
-
-        switch (relocType) {
-            case RELOC_TYPE_ABS_S:
-            case RELOC_TYPE_ABS_D:
-                t = *valThereP;
-
-                (*valThereP) += syms[whichSym].addr;
-
-                if (IS_IN_FLASH(syms[whichSym].addr)) {
-                    (*valThereP) -= FLASH_BASE + BINARY_RELOC_OFFSET;
-                    nanoRelocs[outNumRelocs].type = NANO_RELOC_TYPE_FLASH;
-                }
-                else if (IS_IN_RAM(syms[whichSym].addr)) {
-                    (*valThereP) -= RAM_BASE;
-                    nanoRelocs[outNumRelocs].type = NANO_RELOC_TYPE_RAM;
-                }
-                else {
-                    fprintf(stderr, "Weird reloc %" PRIu32 " to symbol %" PRIu32 " in unknown memory space (addr 0x%08" PRIX32 ")\n",
-                            i, whichSym, syms[whichSym].addr);
-                    goto out;
-                }
-                if (verbose)
-                    fprintf(stderr, "  -> Abs reference fixed up 0x%08" PRIX32 " -> 0x%08" PRIX32 "\n", t, *valThereP);
-                break;
-
-            case RELOC_TYPE_SECT:
-                if (syms[whichSym].addr) {
-                    fprintf(stderr, "Weird sect reloc %" PRIu32 " to symbol %" PRIu32 " with nonzero addr 0x%08" PRIX32 "\n",
-                            i, whichSym, syms[whichSym].addr);
-                    goto out;
-                }
-
-                t = *valThereP;
-
-                if (IS_IN_FLASH(*valThereP)) {
-                    nanoRelocs[outNumRelocs].type = NANO_RELOC_TYPE_FLASH;
-                    *valThereP -= FLASH_BASE + BINARY_RELOC_OFFSET;
-                }
-                else if (IS_IN_RAM(*valThereP)) {
-                    nanoRelocs[outNumRelocs].type = NANO_RELOC_TYPE_RAM;
-                    *valThereP -= RAM_BASE;
-                }
-                else {
-                    fprintf(stderr, "Weird sec reloc %" PRIu32 " to symbol %" PRIu32
-                                    " in unknown memory space (addr 0x%08" PRIX32 ")\n",
-                                    i, whichSym, *valThereP);
-                    goto out;
-                }
-                if (verbose)
-                    fprintf(stderr, "  -> Sect reference fixed up 0x%08" PRIX32 " -> 0x%08" PRIX32 "\n", t, *valThereP);
-                break;
-
-            default:
-                fprintf(stderr, "Weird reloc %" PRIX32 " type %" PRIX32 " to symbol %" PRIX32 "\n", i, relocType, whichSym);
-                goto out;
+        if (fixupReloc(&app, reloc, sym, nanoReloc) != NANO_RELOC_LAST) {
+            app.nanoRelocSize++;
+            if (verbose)
+                DBG("  -> Nano reloc calculated as 0x%08" PRIX32 ",0x%02" PRIX8 "\n", nanoReloc->ofstInRam, nanoReloc->type);
         }
-
-        if (verbose)
-            fprintf(stderr, "  -> Nano reloc calculated as 0x%08" PRIX32 ",0x%02" PRIX8 "\n", nanoRelocs[i].ofstInRam, nanoRelocs[i].type);
-        outNumRelocs++;
     }
 
-    packedNanoRelocs = packNanoRelocs(nanoRelocs, outNumRelocs, &packedNanoRelocSz, verbose);
-
-    //overwrite original relocs and symtab with nanorelocs and adjust sizes
-    memcpy(relocs, packedNanoRelocs, packedNanoRelocSz);
-    bufUsed -= sizeof(struct RelocEntry[numRelocs]);
-    bufUsed -= sizeof(struct SymtabEntry[numSyms]);
-    bufUsed += packedNanoRelocSz;
-    assertMem(bufUsed, bufSz);
-    sect->rel_end = sect->rel_start + packedNanoRelocSz;
-
-    //sanity
-    if (sect->rel_end - FLASH_BASE != bufUsed) {
-        fprintf(stderr, "Relocs end and file end not coincident\n");
+    if (!packNanoRelocs(&app))
         goto out;
-    }
+
+    // we're going to write packed relocs; set correct size
+    sect->rel_end = sect->rel_start + app.packedNanoRelocSize;
 
     //adjust headers for easy access (RAM)
-    if (!IS_IN_RAM(sect->data_start) || !IS_IN_RAM(sect->data_end) || !IS_IN_RAM(sect->bss_start) ||
-        !IS_IN_RAM(sect->bss_end) || !IS_IN_RAM(sect->got_start) || !IS_IN_RAM(sect->got_end)) {
-        fprintf(stderr, "data, bss, or got not in ram\n");
-        goto out;
-    }
     sect->data_start -= RAM_BASE;
     sect->data_end -= RAM_BASE;
     sect->bss_start -= RAM_BASE;
@@ -519,312 +557,17 @@
     sect->got_end -= RAM_BASE;
 
     //adjust headers for easy access (FLASH)
-    if (!IS_IN_FLASH(sect->data_data) || !IS_IN_FLASH(sect->rel_start) || !IS_IN_FLASH(sect->rel_end)) {
-        fprintf(stderr, "data.data, or rel not in flash\n");
-        goto out;
-    }
     sect->data_data -= FLASH_BASE + BINARY_RELOC_OFFSET;
     sect->rel_start -= FLASH_BASE + BINARY_RELOC_OFFSET;
     sect->rel_end -= FLASH_BASE + BINARY_RELOC_OFFSET;
 
-    ret = finalizeAndWrite(buf, bufUsed, bufSz, out, layoutFlags, appId);
+    ret = finalizeAndWrite(&app, out, layoutFlags, appId);
 out:
-    free(nanoRelocs);
+    free(app.nanoReloc);
+    free(app.packedNanoReloc);
     return ret;
 }
 
-static void elfExtractSectionPointer(const Elf_Data *data, const char *name, struct ElfNanoApp *app)
-{
-    // Maps section names to their byte offset in struct ElfNanoApp. Note that
-    // this assumes that the linker script puts text/code in the .flash section,
-    // RW data in .data, that relocs for .data are included in .rel.data, and
-    // the symbol table is emitted in .symtab
-    const struct SectionMap {
-        const char *name;
-        size_t offset;
-    } sectionMap[] = {
-        {
-            .name = ".flash",
-            .offset = offsetof(struct ElfNanoApp, flash),
-        },
-        {
-            .name = ".data",
-            .offset = offsetof(struct ElfNanoApp, data),
-        },
-        {
-            .name = ".rel.data",
-            .offset = offsetof(struct ElfNanoApp, relocs),
-        },
-        {
-            .name = ".symtab",
-            .offset = offsetof(struct ElfNanoApp, symtab),
-        },
-    };
-    struct ElfAppSection *appSection;
-    uint8_t *appBytes = (uint8_t *) app;
-
-    for (size_t i = 0; i < ARRAY_SIZE(sectionMap); i++) {
-        if (strcmp(name, sectionMap[i].name) != 0) {
-            continue;
-        }
-        appSection = (struct ElfAppSection *) &appBytes[sectionMap[i].offset];
-
-        appSection->data = data->d_buf;
-        appSection->size = data->d_size;
-
-        DBG("Found section %s with size %zu", name, appSection->size);
-        break;
-    }
-}
-
-// Populates a struct ElfNanoApp with data parsed from the ELF
-static bool elfParse(Elf *elf, struct ElfNanoApp *app)
-{
-    size_t shdrstrndx;
-    Elf_Scn *scn = NULL;
-    GElf_Shdr shdr;
-    char *sectionName;
-    Elf_Data *elf_data;
-
-    memset(app, 0, sizeof(*app));
-    if (elf_getshdrstrndx(elf, &shdrstrndx) != 0) {
-        ELF_ERR("Couldn't get section name string table index");
-        return false;
-    }
-
-    while ((scn = elf_nextscn(elf, scn)) != NULL) {
-        if (gelf_getshdr(scn, &shdr) != &shdr) {
-            ELF_ERR("Error getting section header");
-            return false;
-        }
-        sectionName = elf_strptr(elf, shdrstrndx, shdr.sh_name);
-
-        elf_data = elf_getdata(scn, NULL);
-        if (!elf_data) {
-            ELF_ERR("Error getting data for section %s", sectionName);
-            return false;
-        }
-
-        elfExtractSectionPointer(elf_data, sectionName, app);
-    }
-
-    return true;
-}
-
-static bool loadNanoappElfFile(const char *fileName, struct ElfNanoApp *app)
-{
-    int fd;
-    Elf *elf;
-
-    if (elf_version(EV_CURRENT) == EV_NONE) {
-        ELF_ERR("Failed to initialize ELF library");
-        return false;
-    }
-
-    fd = open(fileName, O_RDONLY, 0);
-    if (fd < 0) {
-        ERR("Failed to open file %s for reading: %s", fileName, strerror(errno));
-        return false;
-    }
-
-    elf = elf_begin(fd, ELF_C_READ, NULL);
-    if (elf == NULL) {
-        ELF_ERR("Failed to open ELF");
-        return false;
-    }
-
-    if (!elfParse(elf, app)) {
-        ERR("Failed to parse ELF file");
-        return false;
-    }
-
-    return true;
-}
-
-// Subtracts the fixed memory region offset from an absolute address and returns
-// the associated NANO_RELOC_* value, or NANO_RELOC_LAST if the address is not
-// in the expected range.
-// Not strictly tied to ELF usage, but handled slightly differently.
-static uint8_t fixupAddrElf(uint32_t *addr)
-{
-    uint8_t type;
-
-    // TODO: this assumes that the host running this tool has the same
-    // endianness as the image file/target processor
-    if (IS_IN_FLASH(*addr)) {
-        DBG("Fixup addr 0x%08" PRIX32 " (flash) --> 0x%08" PRIX32, *addr,
-            (uint32_t) (*addr - (FLASH_BASE + BINARY_RELOC_OFFSET)));
-        *addr -= FLASH_BASE + BINARY_RELOC_OFFSET;
-        type = NANO_RELOC_TYPE_FLASH;
-    } else if (IS_IN_RAM(*addr)) {
-        DBG("Fixup addr 0x%08" PRIX32 " (ram)   --> 0x%08" PRIX32, *addr,
-            *addr - RAM_BASE);
-        *addr -= RAM_BASE;
-        type = NANO_RELOC_TYPE_RAM;
-    } else {
-        DBG("Error: invalid address 0x%08" PRIX32, *addr);
-        type = NANO_RELOC_LAST;
-    }
-
-    return type;
-}
-
-// Fixup addresses in the header to be relative. Not strictly tied to the ELF
-// format, but used only in that program flow in the current implementation.
-static bool fixupHeaderElf(const struct ElfNanoApp *app)
-{
-    struct BinHdr *hdr = (struct BinHdr *) app->flash.data;
-
-    DBG("Appyling fixups to header");
-    if (fixupAddrElf(&hdr->sect.data_start) != NANO_RELOC_TYPE_RAM ||
-        fixupAddrElf(&hdr->sect.data_end)   != NANO_RELOC_TYPE_RAM ||
-        fixupAddrElf(&hdr->sect.bss_start)  != NANO_RELOC_TYPE_RAM ||
-        fixupAddrElf(&hdr->sect.bss_end)    != NANO_RELOC_TYPE_RAM ||
-        fixupAddrElf(&hdr->sect.got_start)  != NANO_RELOC_TYPE_RAM ||
-        fixupAddrElf(&hdr->sect.got_end)    != NANO_RELOC_TYPE_RAM) {
-        ERR(".data, .bss, or .got not in RAM address space!");
-        return false;
-    }
-
-    if (fixupAddrElf(&hdr->sect.rel_start) != NANO_RELOC_TYPE_FLASH ||
-        fixupAddrElf(&hdr->sect.rel_end)   != NANO_RELOC_TYPE_FLASH ||
-        fixupAddrElf(&hdr->sect.data_data) != NANO_RELOC_TYPE_FLASH) {
-        ERR(".data loadaddr, or .relocs not in flash address space!");
-        return false;
-    }
-
-    if (fixupAddrElf(&hdr->vec.init)   != NANO_RELOC_TYPE_FLASH ||
-        fixupAddrElf(&hdr->vec.end)    != NANO_RELOC_TYPE_FLASH ||
-        fixupAddrElf(&hdr->vec.handle) != NANO_RELOC_TYPE_FLASH) {
-        ERR("Entry point(s) not in flash address space!");
-        return false;
-    }
-
-    return true;
-}
-
-// Fixup addresses in .data, .init_array/.fini_array, and .got, and generates
-// packed array of nano reloc entries. The app header must have already been
-// fixed up.
-static bool genElfNanoRelocs(struct ElfNanoApp *app, bool verbose)
-{
-    const struct BinHdr *hdr = (const struct BinHdr *) app->flash.data;
-    const struct SectInfo *sect = &hdr->sect;
-    bool success = false;
-
-    size_t numDataRelocs = app->relocs.size / sizeof(Elf32_Rel);
-    size_t gotCount = (sect->got_end - sect->got_start) / sizeof(uint32_t);
-    size_t numInitFuncs  = (sect->bss_start - sect->data_end) / sizeof(uint32_t);
-
-    size_t totalRelocCount = (numDataRelocs + numInitFuncs + gotCount);
-    struct NanoRelocEntry *nanoRelocs = malloc(
-        totalRelocCount * sizeof(struct NanoRelocEntry));
-    if (!nanoRelocs) {
-        ERR("Couldn't allocate memory for nano relocs! Needed %zu bytes",
-            totalRelocCount * sizeof(struct NanoRelocEntry));
-        return false;
-    }
-
-    uint8_t *data = app->data.data;
-    const Elf32_Rel *relocs = (const Elf32_Rel *) app->relocs.data;
-    const Elf32_Sym *syms   = (const Elf32_Sym *) app->symtab.data;
-    size_t numRelocs = 0;
-
-    DBG("Parsing relocs for .data (%zu):", numDataRelocs);
-    for (size_t i = 0; i < numDataRelocs; i++) {
-        uint32_t type = ELF32_R_TYPE(relocs[i].r_info);
-        uint32_t sym = ELF32_R_SYM(relocs[i].r_info);
-
-        DBG(" [%3zu] 0x%08" PRIx32 " type %2" PRIu32 " symIdx %3" PRIu32
-            " --> 0x%08" PRIx32, i, relocs[i].r_offset, type, sym,
-            syms[sym].st_value);
-        // Note that R_ARM_TARGET1 is used for .init_array/.fini_array support,
-        // and can be interpreted either as ABS32 or REL32, depending on the
-        // runtime; we expect it to be ABS32.
-        if (type == R_ARM_ABS32 || type == R_ARM_TARGET1) {
-            if (!IS_IN_RAM(relocs[i].r_offset)) {
-                ERR("Reloc for .data not in RAM address range!");
-                goto out;
-            }
-            uint32_t offset = relocs[i].r_offset - RAM_BASE;
-            uint32_t *addr = (uint32_t *) &data[offset];
-
-            nanoRelocs[numRelocs].type = fixupAddrElf(addr);
-            nanoRelocs[numRelocs].ofstInRam = offset;
-            numRelocs++;
-        } else {
-            // TODO: Assuming that the ELF only contains absolute addresses in
-            // the .data section; may need to handle other relocation types in
-            // the future
-            ERR("Error: Unexpected reloc type %" PRIu32 " at index %zu",
-                type, i);
-            goto out;
-        }
-    }
-
-    DBG("Updating GOT entries (%zu):", gotCount);
-    for (uint32_t offset = sect->got_start; offset < sect->got_end;
-            offset += sizeof(uint32_t)) {
-        uint32_t *addr = (uint32_t *) &data[offset];
-        // Skip values that are set to 0, these seem to be padding (?)
-        if (*addr) {
-            nanoRelocs[numRelocs].type = fixupAddrElf(addr);
-            nanoRelocs[numRelocs].ofstInRam = offset;
-            numRelocs++;
-        }
-    }
-
-    uint32_t packedNanoRelocSz = 0;
-    app->packedNanoRelocs.data = packNanoRelocs(
-        nanoRelocs, numRelocs, &packedNanoRelocSz, verbose);
-    app->packedNanoRelocs.size = packedNanoRelocSz;
-    success = true;
-out:
-    free(nanoRelocs);
-    return success;
-}
-
-static int handleAppStatic(const char *fileName, FILE *out, uint32_t layoutFlags, uint64_t appId, bool verbose)
-{
-    struct ElfNanoApp app;
-
-    if (!loadNanoappElfFile(fileName, &app)
-            || !fixupHeaderElf(&app)
-            || !genElfNanoRelocs(&app, verbose)) {
-        exit(2);
-    }
-
-    // Construct a single contiguous buffer, with extra room to fit the
-    // ImageHeader that will be prepended by finalizeAndWrite(). Note that this
-    // will allocate a bit more space than is needed, because some of the data
-    // from BinHdr will get discarded.
-    // TODO: this should be refactored to just write the binary components in
-    // order rather than allocating a big buffer, and moving data around
-    size_t bufSize = app.flash.size + app.data.size + app.packedNanoRelocs.size
-        + sizeof(struct ImageHeader);
-    uint8_t *buf = malloc(bufSize);
-    if (!buf) {
-        ERR("Failed to allocate %zu bytes for final app", bufSize);
-        exit(2);
-    }
-
-    size_t offset = 0;
-    memcpy(buf, app.flash.data, app.flash.size);
-    offset += app.flash.size;
-    memcpy(&buf[offset], app.data.data, app.data.size);
-    offset += app.data.size;
-    memcpy(&buf[offset], app.packedNanoRelocs.data, app.packedNanoRelocs.size);
-    offset += app.packedNanoRelocs.size;
-
-    // Update rel_end in the header to reflect the packed reloc size
-    struct BinHdr *hdr = (struct BinHdr *) buf;
-    hdr->sect.rel_end = hdr->sect.rel_start + app.packedNanoRelocs.size;
-
-    return finalizeAndWrite(buf, offset, bufSize, out, layoutFlags, appId);
-    // TODO: should free all memory we allocated... just letting the OS handle
-    // it for now
-}
-
 static int handleKey(uint8_t **pbuf, uint32_t bufUsed, FILE *out, uint32_t layoutFlags, uint64_t appId, uint64_t keyId)
 {
     uint8_t *buf = *pbuf;
@@ -905,7 +648,6 @@
     const char *layoutName = "app";
     const char *prev = NULL;
     bool bareData = false;
-    bool staticElf = false;
 
     for (int i = 1; i < argc; i++) {
         char *end = NULL;
@@ -915,8 +657,6 @@
                 verbose = true;
             else if (!strcmp(argv[i], "-r"))
                 bareData = true;
-            else if (!strcmp(argv[i], "-s"))
-                staticElf = true;
             else if (!strcmp(argv[i], "-a"))
                 u64Arg = &appId;
             else if (!strcmp(argv[i], "-k"))
@@ -969,9 +709,6 @@
             fatalUsage(appName, "Invalid layout name", layoutName);
     }
 
-    if (staticElf && layoutId != LAYOUT_APP)
-        fatalUsage(appName, "Only app layout is supported for static option", NULL);
-
     if (layoutId == LAYOUT_APP && !appId)
         fatalUsage(appName, "App layout requires app ID", NULL);
     if (layoutId == LAYOUT_KEY && !keyId)
@@ -979,25 +716,22 @@
     if (layoutId == LAYOUT_OS && (keyId || appId))
         fatalUsage(appName, "OS layout does not need any ID", NULL);
 
-    if (!staticElf) {
-        buf = loadFile(posArg[0], &bufUsed);
-        fprintf(stderr, "Read %" PRIu32 " bytes\n", bufUsed);
-    }
-
-    if (!posArg[1])
+    if (!posArg[1]) {
         out = stdout;
-    else
+        stdlog = stderr;
+    } else {
         out = fopen(posArg[1], "w");
+        stdlog = stdout;
+    }
     if (!out)
         fatalUsage(appName, "failed to create/open output file", posArg[1]);
 
+    buf = loadFile(posArg[0], &bufUsed);
+    DBG("Read %" PRIu32 " bytes from %s", bufUsed, posArg[0]);
+
     switch(layoutId) {
     case LAYOUT_APP:
-        if (staticElf) {
-            ret = handleAppStatic(posArg[0], out, layoutFlags, appId, verbose);
-        } else {
-            ret = handleApp(&buf, bufUsed, out, layoutFlags, appId, verbose);
-        }
+        ret = handleApp(&buf, bufUsed, out, layoutFlags, appId, verbose);
         break;
     case LAYOUT_KEY:
         ret = handleKey(&buf, bufUsed, out, layoutFlags, appId, keyId);