753 lines
26 KiB
C
753 lines
26 KiB
C
/*
|
|
* Copyright (C) 2016 The Android Open Source Project
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
#include <assert.h>
|
|
#include <fcntl.h>
|
|
#include <sys/types.h>
|
|
#include <stdbool.h>
|
|
#include <unistd.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <stdint.h>
|
|
#include <stdio.h>
|
|
#include <stddef.h>
|
|
#include <errno.h>
|
|
|
|
#include <nanohub/nanohub.h>
|
|
#include <nanohub/nanoapp.h>
|
|
#include <nanohub/appRelocFormat.h>
|
|
|
|
//This code assumes it is run on a LE CPU with unaligned access abilities. Sorry.
|
|
|
|
#define FLASH_BASE 0x10000000u
|
|
#define RAM_BASE 0x80000000u
|
|
|
|
#define FLASH_SIZE 0x10000000u //256MB ought to be enough for everyone
|
|
#define RAM_SIZE 0x10000000u //256MB ought to be enough for everyone
|
|
|
|
//caution: double evaluation
|
|
#define IS_IN_RANGE_E(_val, _rstart, _rend) (((_val) >= (_rstart)) && ((_val) < (_rend)))
|
|
#define IS_IN_RANGE(_val, _rstart, _rsz) IS_IN_RANGE_E((_val), (_rstart), ((_rstart) + (_rsz)))
|
|
#define IS_IN_RAM(_val) IS_IN_RANGE(_val, RAM_BASE, RAM_SIZE)
|
|
#define IS_IN_FLASH(_val) IS_IN_RANGE(_val, FLASH_BASE, FLASH_SIZE)
|
|
|
|
|
|
#define NANO_RELOC_TYPE_RAM 0
|
|
#define NANO_RELOC_TYPE_FLASH 1
|
|
#define NANO_RELOC_LAST 2 //must be <= (RELOC_TYPE_MASK >> RELOC_TYPE_SHIFT)
|
|
|
|
struct RelocEntry {
|
|
uint32_t where;
|
|
uint32_t info; //bottom 8 bits is type, top 24 is sym idx
|
|
};
|
|
|
|
#define RELOC_TYPE_ABS_S 2
|
|
#define RELOC_TYPE_ABS_D 21
|
|
#define RELOC_TYPE_SECT 23
|
|
|
|
|
|
struct SymtabEntry {
|
|
uint32_t a;
|
|
uint32_t addr;
|
|
uint32_t b, c;
|
|
};
|
|
|
|
struct NanoRelocEntry {
|
|
uint32_t ofstInRam;
|
|
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
|
|
|
|
static FILE *stdlog = NULL;
|
|
|
|
#define DBG(fmt, ...) fprintf(stdlog, fmt "\n", ##__VA_ARGS__)
|
|
#define ERR(fmt, ...) fprintf(stderr, fmt "\n", ##__VA_ARGS__)
|
|
|
|
static void fatalUsage(const char *name, const char *msg, const char *arg)
|
|
{
|
|
if (msg && arg)
|
|
ERR("Error: %s: %s\n", msg, arg);
|
|
else if (msg)
|
|
ERR("Error: %s\n", msg);
|
|
|
|
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"
|
|
" -e <app ver> : 32-bit hex number\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);
|
|
}
|
|
|
|
bool packNanoRelocs(struct NanoAppInfo *app)
|
|
{
|
|
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 < app->nanoRelocSize; i++) {
|
|
struct NanoRelocEntry t;
|
|
|
|
for (k = i, j = k + 1; j < app->nanoRelocSize; j++) {
|
|
if (app->nanoReloc[j].type > app->nanoReloc[k].type)
|
|
continue;
|
|
if ((app->nanoReloc[j].type < app->nanoReloc[k].type) || (app->nanoReloc[j].ofstInRam < app->nanoReloc[k].ofstInRam))
|
|
k = j;
|
|
}
|
|
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 (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(app->nanoRelocSize * 6); //definitely big enough
|
|
packedNanoRelocSz = 0;
|
|
|
|
if (!packedNanoRelocs) {
|
|
ERR("Failed to allocate memory for packed relocs");
|
|
return false;
|
|
}
|
|
|
|
for (i = 0; i < app->nanoRelocSize; i++) {
|
|
uint32_t displacement;
|
|
|
|
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)
|
|
DBG("Out: RelocTC [size 1] // to 0x%02" PRIX8, app->nanoReloc[i].type);
|
|
} else {
|
|
packedNanoRelocs[packedNanoRelocSz++] = TOKEN_RELOC_TYPE_CHG;
|
|
packedNanoRelocs[packedNanoRelocSz++] = app->nanoReloc[i].type - lastOutType - 1;
|
|
if (verbose)
|
|
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 = app->nanoReloc[i].type;
|
|
origin = 0;
|
|
}
|
|
displacement = app->nanoReloc[i].ofstInRam - origin;
|
|
origin = app->nanoReloc[i].ofstInRam + 4;
|
|
if (displacement & 3) {
|
|
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) < 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)
|
|
DBG("Out: Reloc0 [size 2]; repeat=%zu", j);
|
|
packedNanoRelocs[packedNanoRelocSz++] = TOKEN_CONSECUTIVE;
|
|
packedNanoRelocs[packedNanoRelocSz++] = j - MIN_RUN_LEN;
|
|
origin = app->nanoReloc[j + i - 1].ofstInRam + 4; //reset origin to last one
|
|
i += j - 1; //loop will increment anyways, hence +1
|
|
continue;
|
|
}
|
|
}
|
|
|
|
//produce output
|
|
if (displacement <= MAX_8_BIT_NUM) {
|
|
if (verbose)
|
|
DBG("Out: Reloc8 [size 1] 0x%02" PRIX32, displacement);
|
|
packedNanoRelocs[packedNanoRelocSz++] = displacement;
|
|
} else if (displacement <= MAX_16_BIT_NUM) {
|
|
if (verbose)
|
|
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) {
|
|
if (verbose)
|
|
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 {
|
|
if (verbose)
|
|
DBG("Out: Reloc32 [size 5] 0x%08" PRIX32, displacement);
|
|
packedNanoRelocs[packedNanoRelocSz++] = TOKEN_32BIT_OFST;
|
|
packedNanoRelocs[packedNanoRelocSz++] = displacement;
|
|
packedNanoRelocs[packedNanoRelocSz++] = displacement >> 8;
|
|
packedNanoRelocs[packedNanoRelocSz++] = displacement >> 16;
|
|
packedNanoRelocs[packedNanoRelocSz++] = displacement >> 24;
|
|
}
|
|
}
|
|
|
|
app->packedNanoReloc = packedNanoRelocs;
|
|
app->packedNanoRelocSize = packedNanoRelocSz;
|
|
|
|
return true;
|
|
}
|
|
|
|
static int finalizeAndWrite(struct NanoAppInfo *inf, FILE *out, uint32_t layoutFlags, uint64_t appId)
|
|
{
|
|
bool good = true;
|
|
struct AppInfo app;
|
|
struct SectInfo *sect;
|
|
struct BinHdr *bin = inf->bin;
|
|
struct ImageHeader outHeader = {
|
|
.aosp = (struct nano_app_binary_t) {
|
|
.header_version = 1,
|
|
.magic = NANOAPP_AOSP_MAGIC,
|
|
.app_id = appId,
|
|
.app_version = bin->hdr.appVer,
|
|
.flags = 0, // encrypted (1), signed (2) (will be set by other tools)
|
|
},
|
|
.layout = (struct ImageLayout) {
|
|
.magic = GOOGLE_LAYOUT_MAGIC,
|
|
.version = 1,
|
|
.payload = LAYOUT_APP,
|
|
.flags = layoutFlags,
|
|
},
|
|
};
|
|
|
|
app.sect = bin->sect;
|
|
app.vec = bin->vec;
|
|
sect = &app.sect;
|
|
|
|
//if we have any bytes to output, show stats
|
|
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;
|
|
|
|
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);
|
|
}
|
|
}
|
|
|
|
return good ? 0 : 2;
|
|
}
|
|
|
|
// 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, uint32_t appVer, bool verbose)
|
|
{
|
|
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);
|
|
*pbuf = buf;
|
|
|
|
//sanity checks
|
|
bin = (struct BinHdr*)buf;
|
|
if (bufUsed < sizeof(*bin)) {
|
|
ERR("File size too small: %" PRIu32, bufUsed);
|
|
goto out;
|
|
}
|
|
|
|
if (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;
|
|
bin->hdr.appVer = appVer;
|
|
|
|
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 (!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
|
|
|
|
if (verbose)
|
|
DBG("Found %zu relocs and a %zu-entry symbol table", app.relocSize, app.symtabSize);
|
|
|
|
//handle relocs
|
|
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 < 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 >= app.symtabSize) {
|
|
relocDiag(&app, reloc, "references a nonexistent symbol");
|
|
goto out;
|
|
}
|
|
|
|
if (verbose) {
|
|
const char *seg;
|
|
|
|
if (IS_IN_RANGE_E(reloc->where, sect->bss_start, sect->bss_end))
|
|
seg = ".bss";
|
|
else if (IS_IN_RANGE_E(reloc->where, sect->data_start, sect->data_end))
|
|
seg = ".data";
|
|
else if (IS_IN_RANGE_E(reloc->where, sect->got_start, sect->got_end))
|
|
seg = ".got";
|
|
else if (IS_IN_RANGE_E(reloc->where, FLASH_BASE, FLASH_BASE + sizeof(struct BinHdr)))
|
|
seg = "APPHDR";
|
|
else
|
|
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(reloc->where) && reloc->where - FLASH_BASE < sizeof(struct BinHdr) && relocType == RELOC_TYPE_SECT) {
|
|
/* relocs in header are special - runtime corrects for them */
|
|
// 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 § 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.
|
|
|
|
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)
|
|
DBG(" -> Nano reloc skipped for in-header reloc");
|
|
|
|
continue; /* do not produce an output reloc */
|
|
}
|
|
|
|
// any other relocs may only happen in RAM
|
|
if (!IS_IN_RAM(reloc->where)) {
|
|
relocDiag(&app, reloc, "is not in RAM");
|
|
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 (!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)
|
|
sect->data_start -= RAM_BASE;
|
|
sect->data_end -= RAM_BASE;
|
|
sect->bss_start -= RAM_BASE;
|
|
sect->bss_end -= RAM_BASE;
|
|
sect->got_start -= RAM_BASE;
|
|
sect->got_end -= RAM_BASE;
|
|
|
|
//adjust headers for easy access (FLASH)
|
|
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(&app, out, layoutFlags, appId);
|
|
out:
|
|
free(app.nanoReloc);
|
|
free(app.packedNanoReloc);
|
|
return ret;
|
|
}
|
|
|
|
static int handleKey(uint8_t **pbuf, uint32_t bufUsed, FILE *out, uint32_t layoutFlags, uint64_t appId, uint64_t keyId)
|
|
{
|
|
uint8_t *buf = *pbuf;
|
|
struct KeyInfo ki = { .data = keyId };
|
|
bool good = true;
|
|
|
|
struct ImageHeader outHeader = {
|
|
.aosp = (struct nano_app_binary_t) {
|
|
.header_version = 1,
|
|
.magic = NANOAPP_AOSP_MAGIC,
|
|
.app_id = appId,
|
|
},
|
|
.layout = (struct ImageLayout) {
|
|
.magic = GOOGLE_LAYOUT_MAGIC,
|
|
.version = 1,
|
|
.payload = LAYOUT_KEY,
|
|
.flags = layoutFlags,
|
|
},
|
|
};
|
|
|
|
good = good && fwrite(&outHeader, sizeof(outHeader), 1, out) == 1;
|
|
good = good && fwrite(&ki, sizeof(ki), 1, out) == 1;
|
|
good = good && fwrite(buf, bufUsed, 1, out) == 1;
|
|
|
|
return good ? 0 : 2;
|
|
}
|
|
|
|
static int handleOs(uint8_t **pbuf, uint32_t bufUsed, FILE *out, uint32_t layoutFlags, bool bare)
|
|
{
|
|
uint8_t *buf = *pbuf;
|
|
bool good;
|
|
|
|
struct OsUpdateHdr os = {
|
|
.magic = OS_UPDT_MAGIC,
|
|
.marker = OS_UPDT_MARKER_INPROGRESS,
|
|
.size = bufUsed
|
|
};
|
|
|
|
struct ImageHeader outHeader = {
|
|
.aosp = (struct nano_app_binary_t) {
|
|
.header_version = 1,
|
|
.magic = NANOAPP_AOSP_MAGIC,
|
|
},
|
|
.layout = (struct ImageLayout) {
|
|
.magic = GOOGLE_LAYOUT_MAGIC,
|
|
.version = 1,
|
|
.payload = LAYOUT_OS,
|
|
.flags = layoutFlags,
|
|
},
|
|
};
|
|
|
|
if (!bare)
|
|
good = fwrite(&outHeader, sizeof(outHeader), 1, out) == 1;
|
|
else
|
|
good = fwrite(&os, sizeof(os), 1, out) == 1;
|
|
good = good && fwrite(buf, bufUsed, 1, out) == 1;
|
|
|
|
return good ? 0 : 2;
|
|
}
|
|
|
|
int main(int argc, char **argv)
|
|
{
|
|
uint32_t bufUsed = 0;
|
|
bool verbose = false;
|
|
uint8_t *buf = NULL;
|
|
uint64_t appId = 0;
|
|
uint64_t keyId = 0;
|
|
uint32_t appVer = 0;
|
|
uint32_t layoutId = 0;
|
|
uint32_t layoutFlags = 0;
|
|
int ret = -1;
|
|
uint32_t *u32Arg = NULL;
|
|
uint64_t *u64Arg = NULL;
|
|
const char **strArg = NULL;
|
|
const char *appName = argv[0];
|
|
int posArgCnt = 0;
|
|
const char *posArg[2] = { NULL };
|
|
FILE *out = NULL;
|
|
const char *layoutName = "app";
|
|
const char *prev = NULL;
|
|
bool bareData = false;
|
|
|
|
for (int i = 1; i < argc; i++) {
|
|
char *end = NULL;
|
|
if (argv[i][0] == '-') {
|
|
prev = argv[i];
|
|
if (!strcmp(argv[i], "-v"))
|
|
verbose = true;
|
|
else if (!strcmp(argv[i], "-r"))
|
|
bareData = true;
|
|
else if (!strcmp(argv[i], "-a"))
|
|
u64Arg = &appId;
|
|
else if (!strcmp(argv[i], "-e"))
|
|
u32Arg = &appVer;
|
|
else if (!strcmp(argv[i], "-k"))
|
|
u64Arg = &keyId;
|
|
else if (!strcmp(argv[i], "-n"))
|
|
strArg = &layoutName;
|
|
else if (!strcmp(argv[i], "-i"))
|
|
u32Arg = &layoutId;
|
|
else if (!strcmp(argv[i], "-f"))
|
|
u32Arg = &layoutFlags;
|
|
else
|
|
fatalUsage(appName, "unknown argument", argv[i]);
|
|
} else {
|
|
if (u64Arg) {
|
|
uint64_t tmp = strtoull(argv[i], &end, 16);
|
|
if (*end == '\0')
|
|
*u64Arg = tmp;
|
|
u64Arg = NULL;
|
|
} else if (u32Arg) {
|
|
uint32_t tmp = strtoul(argv[i], &end, 16);
|
|
if (*end == '\0')
|
|
*u32Arg = tmp;
|
|
u32Arg = NULL;
|
|
} else if (strArg) {
|
|
*strArg = argv[i];
|
|
strArg = NULL;
|
|
} else {
|
|
if (posArgCnt < 2)
|
|
posArg[posArgCnt++] = argv[i];
|
|
else
|
|
fatalUsage(appName, "too many positional arguments", argv[i]);
|
|
}
|
|
prev = NULL;
|
|
}
|
|
}
|
|
if (prev)
|
|
fatalUsage(appName, "missing argument after", prev);
|
|
|
|
if (!posArgCnt)
|
|
fatalUsage(appName, "missing input file name", NULL);
|
|
|
|
if (!layoutId) {
|
|
if (strcmp(layoutName, "app") == 0)
|
|
layoutId = LAYOUT_APP;
|
|
else if (strcmp(layoutName, "os") == 0)
|
|
layoutId = LAYOUT_OS;
|
|
else if (strcmp(layoutName, "key") == 0)
|
|
layoutId = LAYOUT_KEY;
|
|
else
|
|
fatalUsage(appName, "Invalid layout name", layoutName);
|
|
}
|
|
|
|
if (layoutId == LAYOUT_APP && !appId)
|
|
fatalUsage(appName, "App layout requires app ID", NULL);
|
|
if (layoutId == LAYOUT_KEY && !keyId)
|
|
fatalUsage(appName, "Key layout requires key ID", NULL);
|
|
if (layoutId == LAYOUT_OS && (keyId || appId))
|
|
fatalUsage(appName, "OS layout does not need any ID", NULL);
|
|
|
|
if (!posArg[1]) {
|
|
out = stdout;
|
|
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:
|
|
ret = handleApp(&buf, bufUsed, out, layoutFlags, appId, appVer, verbose);
|
|
break;
|
|
case LAYOUT_KEY:
|
|
ret = handleKey(&buf, bufUsed, out, layoutFlags, appId, keyId);
|
|
break;
|
|
case LAYOUT_OS:
|
|
ret = handleOs(&buf, bufUsed, out, layoutFlags, bareData);
|
|
break;
|
|
}
|
|
|
|
free(buf);
|
|
fclose(out);
|
|
return ret;
|
|
}
|