1/* 2 * Copyright (C) 2016 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17#include <assert.h> 18#include <fcntl.h> 19#include <gelf.h> 20#include <libelf.h> 21#include <sys/types.h> 22#include <stdbool.h> 23#include <unistd.h> 24#include <stdlib.h> 25#include <string.h> 26#include <stdint.h> 27#include <stdio.h> 28#include <stddef.h> 29#include <errno.h> 30 31#include <nanohub/nanohub.h> 32#include <nanohub/nanoapp.h> 33#include <nanohub/appRelocFormat.h> 34 35//This code assumes it is run on a LE CPU with unaligned access abilities. Sorry. 36 37#define FLASH_BASE 0x10000000 38#define RAM_BASE 0x80000000 39 40#define FLASH_SIZE 0x10000000 //256MB ought to be enough for everyone 41#define RAM_SIZE 0x10000000 //256MB ought to be enough for everyone 42 43//caution: double evaluation 44#define IS_IN_RANGE_E(_val, _rstart, _rend) (((_val) >= (_rstart)) && ((_val) < (_rend))) 45#define IS_IN_RANGE(_val, _rstart, _rsz) IS_IN_RANGE_E((_val), (_rstart), ((_rstart) + (_rsz))) 46#define IS_IN_RAM(_val) IS_IN_RANGE(_val, RAM_BASE, RAM_SIZE) 47#define IS_IN_FLASH(_val) IS_IN_RANGE(_val, FLASH_BASE, FLASH_SIZE) 48 49 50#define NANO_RELOC_TYPE_RAM 0 51#define NANO_RELOC_TYPE_FLASH 1 52#define NANO_RELOC_LAST 2 //must be <= (RELOC_TYPE_MASK >> RELOC_TYPE_SHIFT) 53 54struct RelocEntry { 55 uint32_t where; 56 uint32_t info; //bottom 8 bits is type, top 24 is sym idx 57}; 58 59#define RELOC_TYPE_ABS_S 2 60#define RELOC_TYPE_ABS_D 21 61#define RELOC_TYPE_SECT 23 62 63 64struct SymtabEntry { 65 uint32_t a; 66 uint32_t addr; 67 uint32_t b, c; 68}; 69 70struct NanoRelocEntry { 71 uint32_t ofstInRam; 72 uint8_t type; 73}; 74 75#ifndef ARRAY_SIZE 76#define ARRAY_SIZE(ary) (sizeof(ary) / sizeof((ary)[0])) 77#endif 78 79#define DBG(fmt, ...) printf(fmt "\n", ##__VA_ARGS__) 80#define ERR(fmt, ...) fprintf(stderr, fmt "\n", ##__VA_ARGS__) 81 82// Prints the given message followed by the most recent libelf error 83#define ELF_ERR(fmt, ...) ERR(fmt ": %s\n", ##__VA_ARGS__, elf_errmsg(-1)) 84 85struct ElfAppSection { 86 void *data; 87 size_t size; 88}; 89 90struct ElfNanoApp { 91 struct ElfAppSection flash; 92 struct ElfAppSection data; 93 struct ElfAppSection relocs; 94 struct ElfAppSection symtab; 95 96 // Not parsed from file, but constructed via genElfNanoRelocs 97 struct ElfAppSection packedNanoRelocs; 98}; 99 100static void fatalUsage(const char *name, const char *msg, const char *arg) 101{ 102 if (msg && arg) 103 fprintf(stderr, "Error: %s: %s\n\n", msg, arg); 104 else if (msg) 105 fprintf(stderr, "Error: %s\n\n", msg); 106 107 fprintf(stderr, "USAGE: %s [-v] [-k <key id>] [-a <app id>] [-r] [-n <layout name>] [-i <layout id>] <input file> [<output file>]\n" 108 " -v : be verbose\n" 109 " -n <layout name> : app, os, key\n" 110 " -i <layout id> : 1 (app), 2 (key), 3 (os)\n" 111 " -f <layout flags>: 16-bit hex value, stored as layout-specific flags\n" 112 " -c <chre api> : 16-bit hex value, stored as chre-major + chre-minor\n" 113 " -a <app ID> : 64-bit hex number != 0\n" 114 " -e <app version> : 32-bit hex number\n" 115 " -k <key ID> : 64-bit hex number != 0\n" 116 " -r : bare (no AOSP header); used only for inner OS image generation\n" 117 " -s : treat input as statically linked ELF (app layout only)\n" 118 " layout ID and layout name control the same parameter, so only one of them needs to be used\n" 119 , name); 120 exit(1); 121} 122 123static uint8_t *packNanoRelocs(struct NanoRelocEntry *nanoRelocs, uint32_t outNumRelocs, uint32_t *finalPackedNanoRelocSz, bool verbose) 124{ 125 uint32_t i, j, k; 126 uint8_t *packedNanoRelocs; 127 uint32_t packedNanoRelocSz; 128 uint32_t lastOutType = 0, origin = 0; 129 130 //sort by type and then offset 131 for (i = 0; i < outNumRelocs; i++) { 132 struct NanoRelocEntry t; 133 134 for (k = i, j = k + 1; j < outNumRelocs; j++) { 135 if (nanoRelocs[j].type > nanoRelocs[k].type) 136 continue; 137 if ((nanoRelocs[j].type < nanoRelocs[k].type) || (nanoRelocs[j].ofstInRam < nanoRelocs[k].ofstInRam)) 138 k = j; 139 } 140 memcpy(&t, nanoRelocs + i, sizeof(struct NanoRelocEntry)); 141 memcpy(nanoRelocs + i, nanoRelocs + k, sizeof(struct NanoRelocEntry)); 142 memcpy(nanoRelocs + k, &t, sizeof(struct NanoRelocEntry)); 143 144 if (verbose) 145 fprintf(stderr, "SortedReloc[%3" PRIu32 "] = {0x%08" PRIX32 ",0x%02" PRIX8 "}\n", i, nanoRelocs[i].ofstInRam, nanoRelocs[i].type); 146 } 147 148 //produce output nanorelocs in packed format 149 packedNanoRelocs = malloc(outNumRelocs * 6); //definitely big enough 150 packedNanoRelocSz = 0; 151 for (i = 0; i < outNumRelocs; i++) { 152 uint32_t displacement; 153 154 if (lastOutType != nanoRelocs[i].type) { //output type if ti changed 155 if (nanoRelocs[i].type - lastOutType == 1) { 156 packedNanoRelocs[packedNanoRelocSz++] = TOKEN_RELOC_TYPE_NEXT; 157 if (verbose) 158 fprintf(stderr, "Out: RelocTC (1) // to 0x%02" PRIX8 "\n", nanoRelocs[i].type); 159 } 160 else { 161 packedNanoRelocs[packedNanoRelocSz++] = TOKEN_RELOC_TYPE_CHG; 162 packedNanoRelocs[packedNanoRelocSz++] = nanoRelocs[i].type - lastOutType - 1; 163 if (verbose) 164 fprintf(stderr, "Out: RelocTC (0x%02" PRIX8 ") // to 0x%02" PRIX8 "\n", (uint8_t)(nanoRelocs[i].type - lastOutType - 1), nanoRelocs[i].type); 165 } 166 lastOutType = nanoRelocs[i].type; 167 origin = 0; 168 } 169 displacement = nanoRelocs[i].ofstInRam - origin; 170 origin = nanoRelocs[i].ofstInRam + 4; 171 if (displacement & 3) { 172 fprintf(stderr, "Unaligned relocs are not possible!\n"); 173 exit(-5); 174 } 175 displacement /= 4; 176 177 //might be start of a run. look into that 178 if (!displacement) { 179 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++); 180 if (j >= MIN_RUN_LEN) { 181 if (verbose) 182 fprintf(stderr, "Out: Reloc0 x%" PRIX32 "\n", j); 183 packedNanoRelocs[packedNanoRelocSz++] = TOKEN_CONSECUTIVE; 184 packedNanoRelocs[packedNanoRelocSz++] = j - MIN_RUN_LEN; 185 origin = nanoRelocs[j + i - 1].ofstInRam + 4; //reset origin to last one 186 i += j - 1; //loop will increment anyways, hence +1 187 continue; 188 } 189 } 190 191 //produce output 192 if (displacement <= MAX_8_BIT_NUM) { 193 if (verbose) 194 fprintf(stderr, "Out: Reloc8 0x%02" PRIX32 "\n", displacement); 195 packedNanoRelocs[packedNanoRelocSz++] = displacement; 196 } 197 else if (displacement <= MAX_16_BIT_NUM) { 198 if (verbose) 199 fprintf(stderr, "Out: Reloc16 0x%06" PRIX32 "\n", displacement); 200 displacement -= MAX_8_BIT_NUM; 201 packedNanoRelocs[packedNanoRelocSz++] = TOKEN_16BIT_OFST; 202 packedNanoRelocs[packedNanoRelocSz++] = displacement; 203 packedNanoRelocs[packedNanoRelocSz++] = displacement >> 8; 204 } 205 else if (displacement <= MAX_24_BIT_NUM) { 206 if (verbose) 207 fprintf(stderr, "Out: Reloc24 0x%08" PRIX32 "\n", displacement); 208 displacement -= MAX_16_BIT_NUM; 209 packedNanoRelocs[packedNanoRelocSz++] = TOKEN_24BIT_OFST; 210 packedNanoRelocs[packedNanoRelocSz++] = displacement; 211 packedNanoRelocs[packedNanoRelocSz++] = displacement >> 8; 212 packedNanoRelocs[packedNanoRelocSz++] = displacement >> 16; 213 } 214 else { 215 if (verbose) 216 fprintf(stderr, "Out: Reloc32 0x%08" PRIX32 "\n", displacement); 217 packedNanoRelocs[packedNanoRelocSz++] = TOKEN_32BIT_OFST; 218 packedNanoRelocs[packedNanoRelocSz++] = displacement; 219 packedNanoRelocs[packedNanoRelocSz++] = displacement >> 8; 220 packedNanoRelocs[packedNanoRelocSz++] = displacement >> 16; 221 packedNanoRelocs[packedNanoRelocSz++] = displacement >> 24; 222 } 223 } 224 225 *finalPackedNanoRelocSz = packedNanoRelocSz; 226 return packedNanoRelocs; 227} 228 229static int finalizeAndWrite(uint8_t *buf, uint32_t bufUsed, uint32_t bufSz, FILE *out, uint32_t layoutFlags, uint64_t appId, uint32_t chreApi) 230{ 231 int ret; 232 struct AppInfo app; 233 struct SectInfo *sect; 234 struct BinHdr *bin = (struct BinHdr *) buf; 235 struct ImageHeader outHeader = { 236 .aosp = (struct nano_app_binary_t) { 237 .header_version = 1, 238 .magic = NANOAPP_AOSP_MAGIC, 239 .app_id = appId, 240 .app_version = bin->hdr.appVer, 241 .flags = 0, // encrypted (1), signed (2) (will be set by other tools) 242 .chre_api_major = chreApi >> 8, 243 .chre_api_minor = chreApi & 0xFF, 244 }, 245 .layout = (struct ImageLayout) { 246 .magic = GOOGLE_LAYOUT_MAGIC, 247 .version = 1, 248 .payload = LAYOUT_APP, 249 .flags = layoutFlags | (chreApi ? 0x0010 : 0x0000), 250 }, 251 }; 252 uint32_t dataOffset = sizeof(outHeader) + sizeof(app); 253 uint32_t hdrDiff = dataOffset - sizeof(*bin); 254 app.sect = bin->sect; 255 app.vec = bin->vec; 256 257 assertMem(bufUsed + hdrDiff, bufSz); 258 259 memmove(buf + dataOffset, buf + sizeof(*bin), bufUsed - sizeof(*bin)); 260 bufUsed += hdrDiff; 261 memcpy(buf, &outHeader, sizeof(outHeader)); 262 memcpy(buf + sizeof(outHeader), &app, sizeof(app)); 263 sect = &app.sect; 264 265 //if we have any bytes to output, show stats 266 if (bufUsed) { 267 uint32_t codeAndRoDataSz = sect->data_data; 268 uint32_t relocsSz = sect->rel_end - sect->rel_start; 269 uint32_t gotSz = sect->got_end - sect->data_start; 270 uint32_t bssSz = sect->bss_end - sect->bss_start; 271 272 fprintf(stderr,"Final binary size %" PRIu32 " bytes\n", bufUsed); 273 fprintf(stderr, "\n"); 274 fprintf(stderr, " FW header size (flash): %6zu bytes\n", FLASH_RELOC_OFFSET); 275 fprintf(stderr, " Code + RO data (flash): %6" PRIu32 " bytes\n", codeAndRoDataSz); 276 fprintf(stderr, " Relocs (flash): %6" PRIu32 " bytes\n", relocsSz); 277 fprintf(stderr, " GOT + RW data (flash & RAM): %6" PRIu32 " bytes\n", gotSz); 278 fprintf(stderr, " BSS (RAM): %6" PRIu32 " bytes\n", bssSz); 279 fprintf(stderr, "\n"); 280 fprintf(stderr,"Runtime flash use: %" PRIu32 " bytes\n", (uint32_t)(codeAndRoDataSz + relocsSz + gotSz + FLASH_RELOC_OFFSET)); 281 fprintf(stderr,"Runtime RAM use: %" PRIu32 " bytes\n", gotSz + bssSz); 282 } 283 284 ret = fwrite(buf, bufUsed, 1, out) == 1 ? 0 : 2; 285 if (ret) 286 fprintf(stderr, "Failed to write output file: %s\n", strerror(errno)); 287 288 return ret; 289} 290 291static int handleApp(uint8_t **pbuf, uint32_t bufUsed, FILE *out, uint32_t layoutFlags, uint64_t appId, uint32_t appVer, uint32_t chreApi, bool verbose) 292{ 293 uint32_t i, numRelocs, numSyms, outNumRelocs = 0, packedNanoRelocSz; 294 struct NanoRelocEntry *nanoRelocs = NULL; 295 struct RelocEntry *relocs; 296 struct SymtabEntry *syms; 297 uint8_t *packedNanoRelocs; 298 uint32_t t; 299 struct BinHdr *bin; 300 int ret = -1; 301 struct SectInfo *sect; 302 uint8_t *buf = *pbuf; 303 uint32_t bufSz = bufUsed * 3 /2; 304 305 //make buffer 50% bigger than bufUsed in case relocs grow out of hand 306 buf = reallocOrDie(buf, bufSz); 307 *pbuf = buf; 308 309 //sanity checks 310 bin = (struct BinHdr*)buf; 311 if (bufUsed < sizeof(*bin)) { 312 fprintf(stderr, "File size too small\n"); 313 goto out; 314 } 315 316 if (bin->hdr.magic != NANOAPP_FW_MAGIC) { 317 fprintf(stderr, "Magic value is wrong: found %08" PRIX32 318 "; expected %08" PRIX32 "\n", 319 bin->hdr.magic, NANOAPP_FW_MAGIC); 320 goto out; 321 } 322 323 sect = &bin->sect; 324 bin->hdr.appVer = appVer; 325 326 //do some math 327 relocs = (struct RelocEntry*)(buf + sect->rel_start - FLASH_BASE); 328 syms = (struct SymtabEntry*)(buf + sect->rel_end - FLASH_BASE); 329 numRelocs = (sect->rel_end - sect->rel_start) / sizeof(struct RelocEntry); 330 numSyms = (bufUsed + FLASH_BASE - sect->rel_end) / sizeof(struct SymtabEntry); 331 332 //sanity 333 if (numRelocs * sizeof(struct RelocEntry) + sect->rel_start != sect->rel_end) { 334 fprintf(stderr, "Relocs of nonstandard size\n"); 335 goto out; 336 } 337 if (numSyms * sizeof(struct SymtabEntry) + sect->rel_end != bufUsed + FLASH_BASE) { 338 fprintf(stderr, "Syms of nonstandard size\n"); 339 goto out; 340 } 341 342 //show some info 343 fprintf(stderr, "\nRead %" PRIu32 " bytes of binary.\n", bufUsed); 344 345 if (verbose) 346 fprintf(stderr, "Found %" PRIu32 " relocs and a %" PRIu32 "-entry symbol table\n", numRelocs, numSyms); 347 348 //handle relocs 349 nanoRelocs = malloc(sizeof(struct NanoRelocEntry[numRelocs])); 350 if (!nanoRelocs) { 351 fprintf(stderr, "Failed to allocate a nano-reloc table\n"); 352 goto out; 353 } 354 355 for (i = 0; i < numRelocs; i++) { 356 uint32_t relocType = relocs[i].info & 0xff; 357 uint32_t whichSym = relocs[i].info >> 8; 358 uint32_t *valThereP; 359 360 if (whichSym >= numSyms) { 361 fprintf(stderr, "Reloc %" PRIu32 " references a nonexistent symbol!\n" 362 "INFO:\n" 363 " Where: 0x%08" PRIX32 "\n" 364 " type: %" PRIu32 "\n" 365 " sym: %" PRIu32 "\n", 366 i, relocs[i].where, relocs[i].info & 0xff, whichSym); 367 goto out; 368 } 369 370 if (verbose) { 371 const char *seg; 372 373 fprintf(stderr, "Reloc[%3" PRIu32 "]:\n {@0x%08" PRIX32 ", type %3" PRIu32 ", -> sym[%3" PRIu32 "]: {@0x%08" PRIX32 "}, ", 374 i, relocs[i].where, relocs[i].info & 0xff, whichSym, syms[whichSym].addr); 375 376 if (IS_IN_RANGE_E(relocs[i].where, sect->bss_start, sect->bss_end)) 377 seg = ".bss"; 378 else if (IS_IN_RANGE_E(relocs[i].where, sect->data_start, sect->data_end)) 379 seg = ".data"; 380 else if (IS_IN_RANGE_E(relocs[i].where, sect->got_start, sect->got_end)) 381 seg = ".got"; 382 else if (IS_IN_RANGE_E(relocs[i].where, FLASH_BASE, FLASH_BASE + sizeof(struct BinHdr))) 383 seg = "APPHDR"; 384 else 385 seg = "???"; 386 387 fprintf(stderr, "in %s}\n", seg); 388 } 389 /* handle relocs inside the header */ 390 if (IS_IN_FLASH(relocs[i].where) && relocs[i].where - FLASH_BASE < sizeof(struct BinHdr) && relocType == RELOC_TYPE_SECT) { 391 /* relocs in header are special - runtime corrects for them */ 392 if (syms[whichSym].addr) { 393 fprintf(stderr, "Weird in-header sect reloc %" PRIu32 " to symbol %" PRIu32 " with nonzero addr 0x%08" PRIX32 "\n", 394 i, whichSym, syms[whichSym].addr); 395 goto out; 396 } 397 398 valThereP = (uint32_t*)(buf + relocs[i].where - FLASH_BASE); 399 if (!IS_IN_FLASH(*valThereP)) { 400 fprintf(stderr, "In-header reloc %" PRIu32 " of location 0x%08" PRIX32 " is outside of FLASH!\n" 401 "INFO:\n" 402 " type: %" PRIu32 "\n" 403 " sym: %" PRIu32 "\n" 404 " Sym Addr: 0x%08" PRIX32 "\n", 405 i, relocs[i].where, relocType, whichSym, syms[whichSym].addr); 406 goto out; 407 } 408 409 // binary header generated by objcopy, .napp header and final FW header in flash are of different size. 410 // we subtract binary header offset here, so all the entry points are relative to beginning of "sect". 411 // FW will use § as a base to call these vectors; no more problems with different header sizes; 412 // Assumption: offsets between sect & vec, vec & code are the same in all images (or, in a simpler words, { sect, vec, code } 413 // must go together). this is enforced by linker script, and maintained by all tools and FW download code in the OS. 414 *valThereP -= FLASH_BASE + BINARY_RELOC_OFFSET; 415 416 if (verbose) 417 fprintf(stderr, " -> Nano reloc skipped for in-header reloc\n"); 418 419 continue; /* do not produce an output reloc */ 420 } 421 422 if (!IS_IN_RAM(relocs[i].where)) { 423 fprintf(stderr, "In-header reloc %" PRIu32 " of location 0x%08" PRIX32 " is outside of RAM!\n" 424 "INFO:\n" 425 " type: %" PRIu32 "\n" 426 " sym: %" PRIu32 "\n" 427 " Sym Addr: 0x%08" PRIX32 "\n", 428 i, relocs[i].where, relocType, whichSym, syms[whichSym].addr); 429 goto out; 430 } 431 432 valThereP = (uint32_t*)(buf + relocs[i].where + sect->data_data - RAM_BASE - FLASH_BASE); 433 434 nanoRelocs[outNumRelocs].ofstInRam = relocs[i].where - RAM_BASE; 435 436 switch (relocType) { 437 case RELOC_TYPE_ABS_S: 438 case RELOC_TYPE_ABS_D: 439 t = *valThereP; 440 441 (*valThereP) += syms[whichSym].addr; 442 443 if (IS_IN_FLASH(syms[whichSym].addr)) { 444 (*valThereP) -= FLASH_BASE + BINARY_RELOC_OFFSET; 445 nanoRelocs[outNumRelocs].type = NANO_RELOC_TYPE_FLASH; 446 } 447 else if (IS_IN_RAM(syms[whichSym].addr)) { 448 (*valThereP) -= RAM_BASE; 449 nanoRelocs[outNumRelocs].type = NANO_RELOC_TYPE_RAM; 450 } 451 else { 452 fprintf(stderr, "Weird reloc %" PRIu32 " to symbol %" PRIu32 " in unknown memory space (addr 0x%08" PRIX32 ")\n", 453 i, whichSym, syms[whichSym].addr); 454 goto out; 455 } 456 if (verbose) 457 fprintf(stderr, " -> Abs reference fixed up 0x%08" PRIX32 " -> 0x%08" PRIX32 "\n", t, *valThereP); 458 break; 459 460 case RELOC_TYPE_SECT: 461 if (syms[whichSym].addr) { 462 fprintf(stderr, "Weird sect reloc %" PRIu32 " to symbol %" PRIu32 " with nonzero addr 0x%08" PRIX32 "\n", 463 i, whichSym, syms[whichSym].addr); 464 goto out; 465 } 466 467 t = *valThereP; 468 469 if (IS_IN_FLASH(*valThereP)) { 470 nanoRelocs[outNumRelocs].type = NANO_RELOC_TYPE_FLASH; 471 *valThereP -= FLASH_BASE + BINARY_RELOC_OFFSET; 472 } 473 else if (IS_IN_RAM(*valThereP)) { 474 nanoRelocs[outNumRelocs].type = NANO_RELOC_TYPE_RAM; 475 *valThereP -= RAM_BASE; 476 } 477 else { 478 fprintf(stderr, "Weird sec reloc %" PRIu32 " to symbol %" PRIu32 479 " in unknown memory space (addr 0x%08" PRIX32 ")\n", 480 i, whichSym, *valThereP); 481 goto out; 482 } 483 if (verbose) 484 fprintf(stderr, " -> Sect reference fixed up 0x%08" PRIX32 " -> 0x%08" PRIX32 "\n", t, *valThereP); 485 break; 486 487 default: 488 fprintf(stderr, "Weird reloc %" PRIX32 " type %" PRIX32 " to symbol %" PRIX32 "\n", i, relocType, whichSym); 489 goto out; 490 } 491 492 if (verbose) 493 fprintf(stderr, " -> Nano reloc calculated as 0x%08" PRIX32 ",0x%02" PRIX8 "\n", nanoRelocs[i].ofstInRam, nanoRelocs[i].type); 494 outNumRelocs++; 495 } 496 497 packedNanoRelocs = packNanoRelocs(nanoRelocs, outNumRelocs, &packedNanoRelocSz, verbose); 498 499 //overwrite original relocs and symtab with nanorelocs and adjust sizes 500 memcpy(relocs, packedNanoRelocs, packedNanoRelocSz); 501 bufUsed -= sizeof(struct RelocEntry[numRelocs]); 502 bufUsed -= sizeof(struct SymtabEntry[numSyms]); 503 bufUsed += packedNanoRelocSz; 504 assertMem(bufUsed, bufSz); 505 sect->rel_end = sect->rel_start + packedNanoRelocSz; 506 507 //sanity 508 if (sect->rel_end - FLASH_BASE != bufUsed) { 509 fprintf(stderr, "Relocs end and file end not coincident\n"); 510 goto out; 511 } 512 513 //adjust headers for easy access (RAM) 514 if (!IS_IN_RAM(sect->data_start) || !IS_IN_RAM(sect->data_end) || !IS_IN_RAM(sect->bss_start) || 515 !IS_IN_RAM(sect->bss_end) || !IS_IN_RAM(sect->got_start) || !IS_IN_RAM(sect->got_end)) { 516 fprintf(stderr, "data, bss, or got not in ram\n"); 517 goto out; 518 } 519 sect->data_start -= RAM_BASE; 520 sect->data_end -= RAM_BASE; 521 sect->bss_start -= RAM_BASE; 522 sect->bss_end -= RAM_BASE; 523 sect->got_start -= RAM_BASE; 524 sect->got_end -= RAM_BASE; 525 526 //adjust headers for easy access (FLASH) 527 if (!IS_IN_FLASH(sect->data_data) || !IS_IN_FLASH(sect->rel_start) || !IS_IN_FLASH(sect->rel_end)) { 528 fprintf(stderr, "data.data, or rel not in flash\n"); 529 goto out; 530 } 531 sect->data_data -= FLASH_BASE + BINARY_RELOC_OFFSET; 532 sect->rel_start -= FLASH_BASE + BINARY_RELOC_OFFSET; 533 sect->rel_end -= FLASH_BASE + BINARY_RELOC_OFFSET; 534 535 ret = finalizeAndWrite(buf, bufUsed, bufSz, out, layoutFlags, appId, chreApi); 536out: 537 free(nanoRelocs); 538 return ret; 539} 540 541static void elfExtractSectionPointer(const Elf_Data *data, const char *name, struct ElfNanoApp *app) 542{ 543 // Maps section names to their byte offset in struct ElfNanoApp. Note that 544 // this assumes that the linker script puts text/code in the .flash section, 545 // RW data in .data, that relocs for .data are included in .rel.data, and 546 // the symbol table is emitted in .symtab 547 const struct SectionMap { 548 const char *name; 549 size_t offset; 550 } sectionMap[] = { 551 { 552 .name = ".flash", 553 .offset = offsetof(struct ElfNanoApp, flash), 554 }, 555 { 556 .name = ".data", 557 .offset = offsetof(struct ElfNanoApp, data), 558 }, 559 { 560 .name = ".rel.data", 561 .offset = offsetof(struct ElfNanoApp, relocs), 562 }, 563 { 564 .name = ".symtab", 565 .offset = offsetof(struct ElfNanoApp, symtab), 566 }, 567 }; 568 struct ElfAppSection *appSection; 569 uint8_t *appBytes = (uint8_t *) app; 570 571 for (size_t i = 0; i < ARRAY_SIZE(sectionMap); i++) { 572 if (strcmp(name, sectionMap[i].name) != 0) { 573 continue; 574 } 575 appSection = (struct ElfAppSection *) &appBytes[sectionMap[i].offset]; 576 577 appSection->data = data->d_buf; 578 appSection->size = data->d_size; 579 580 DBG("Found section %s with size %zu", name, appSection->size); 581 break; 582 } 583} 584 585// Populates a struct ElfNanoApp with data parsed from the ELF 586static bool elfParse(Elf *elf, struct ElfNanoApp *app) 587{ 588 size_t shdrstrndx; 589 Elf_Scn *scn = NULL; 590 GElf_Shdr shdr; 591 char *sectionName; 592 Elf_Data *elf_data; 593 594 memset(app, 0, sizeof(*app)); 595 if (elf_getshdrstrndx(elf, &shdrstrndx) != 0) { 596 ELF_ERR("Couldn't get section name string table index"); 597 return false; 598 } 599 600 while ((scn = elf_nextscn(elf, scn)) != NULL) { 601 if (gelf_getshdr(scn, &shdr) != &shdr) { 602 ELF_ERR("Error getting section header"); 603 return false; 604 } 605 sectionName = elf_strptr(elf, shdrstrndx, shdr.sh_name); 606 607 elf_data = elf_getdata(scn, NULL); 608 if (!elf_data) { 609 ELF_ERR("Error getting data for section %s", sectionName); 610 return false; 611 } 612 613 elfExtractSectionPointer(elf_data, sectionName, app); 614 } 615 616 return true; 617} 618 619static bool loadNanoappElfFile(const char *fileName, struct ElfNanoApp *app) 620{ 621 int fd; 622 Elf *elf; 623 624 if (elf_version(EV_CURRENT) == EV_NONE) { 625 ELF_ERR("Failed to initialize ELF library"); 626 return false; 627 } 628 629 fd = open(fileName, O_RDONLY, 0); 630 if (fd < 0) { 631 ERR("Failed to open file %s for reading: %s", fileName, strerror(errno)); 632 return false; 633 } 634 635 elf = elf_begin(fd, ELF_C_READ, NULL); 636 if (elf == NULL) { 637 ELF_ERR("Failed to open ELF"); 638 return false; 639 } 640 641 if (!elfParse(elf, app)) { 642 ERR("Failed to parse ELF file"); 643 return false; 644 } 645 646 return true; 647} 648 649// Subtracts the fixed memory region offset from an absolute address and returns 650// the associated NANO_RELOC_* value, or NANO_RELOC_LAST if the address is not 651// in the expected range. 652// Not strictly tied to ELF usage, but handled slightly differently. 653static uint8_t fixupAddrElf(uint32_t *addr) 654{ 655 uint8_t type; 656 657 // TODO: this assumes that the host running this tool has the same 658 // endianness as the image file/target processor 659 if (IS_IN_FLASH(*addr)) { 660 DBG("Fixup addr 0x%08" PRIX32 " (flash) --> 0x%08" PRIX32, *addr, 661 (uint32_t) (*addr - (FLASH_BASE + BINARY_RELOC_OFFSET))); 662 *addr -= FLASH_BASE + BINARY_RELOC_OFFSET; 663 type = NANO_RELOC_TYPE_FLASH; 664 } else if (IS_IN_RAM(*addr)) { 665 DBG("Fixup addr 0x%08" PRIX32 " (ram) --> 0x%08" PRIX32, *addr, 666 *addr - RAM_BASE); 667 *addr -= RAM_BASE; 668 type = NANO_RELOC_TYPE_RAM; 669 } else { 670 DBG("Error: invalid address 0x%08" PRIX32, *addr); 671 type = NANO_RELOC_LAST; 672 } 673 674 return type; 675} 676 677// Fixup addresses in the header to be relative. Not strictly tied to the ELF 678// format, but used only in that program flow in the current implementation. 679static bool fixupHeaderElf(const struct ElfNanoApp *app) 680{ 681 struct BinHdr *hdr = (struct BinHdr *) app->flash.data; 682 683 DBG("Appyling fixups to header"); 684 if (fixupAddrElf(&hdr->sect.data_start) != NANO_RELOC_TYPE_RAM || 685 fixupAddrElf(&hdr->sect.data_end) != NANO_RELOC_TYPE_RAM || 686 fixupAddrElf(&hdr->sect.bss_start) != NANO_RELOC_TYPE_RAM || 687 fixupAddrElf(&hdr->sect.bss_end) != NANO_RELOC_TYPE_RAM || 688 fixupAddrElf(&hdr->sect.got_start) != NANO_RELOC_TYPE_RAM || 689 fixupAddrElf(&hdr->sect.got_end) != NANO_RELOC_TYPE_RAM) { 690 ERR(".data, .bss, or .got not in RAM address space!"); 691 return false; 692 } 693 694 if (fixupAddrElf(&hdr->sect.rel_start) != NANO_RELOC_TYPE_FLASH || 695 fixupAddrElf(&hdr->sect.rel_end) != NANO_RELOC_TYPE_FLASH || 696 fixupAddrElf(&hdr->sect.data_data) != NANO_RELOC_TYPE_FLASH) { 697 ERR(".data loadaddr, or .relocs not in flash address space!"); 698 return false; 699 } 700 701 if (fixupAddrElf(&hdr->vec.init) != NANO_RELOC_TYPE_FLASH || 702 fixupAddrElf(&hdr->vec.end) != NANO_RELOC_TYPE_FLASH || 703 fixupAddrElf(&hdr->vec.handle) != NANO_RELOC_TYPE_FLASH) { 704 ERR("Entry point(s) not in flash address space!"); 705 return false; 706 } 707 708 return true; 709} 710 711// Fixup addresses in .data, .init_array/.fini_array, and .got, and generates 712// packed array of nano reloc entries. The app header must have already been 713// fixed up. 714static bool genElfNanoRelocs(struct ElfNanoApp *app, bool verbose) 715{ 716 const struct BinHdr *hdr = (const struct BinHdr *) app->flash.data; 717 const struct SectInfo *sect = &hdr->sect; 718 bool success = false; 719 720 size_t numDataRelocs = app->relocs.size / sizeof(Elf32_Rel); 721 size_t gotCount = (sect->got_end - sect->got_start) / sizeof(uint32_t); 722 size_t numInitFuncs = (sect->bss_start - sect->data_end) / sizeof(uint32_t); 723 724 size_t totalRelocCount = (numDataRelocs + numInitFuncs + gotCount); 725 struct NanoRelocEntry *nanoRelocs = malloc( 726 totalRelocCount * sizeof(struct NanoRelocEntry)); 727 if (!nanoRelocs) { 728 ERR("Couldn't allocate memory for nano relocs! Needed %zu bytes", 729 totalRelocCount * sizeof(struct NanoRelocEntry)); 730 return false; 731 } 732 733 uint8_t *data = app->data.data; 734 const Elf32_Rel *relocs = (const Elf32_Rel *) app->relocs.data; 735 const Elf32_Sym *syms = (const Elf32_Sym *) app->symtab.data; 736 size_t numRelocs = 0; 737 738 DBG("Parsing relocs for .data (%zu):", numDataRelocs); 739 for (size_t i = 0; i < numDataRelocs; i++) { 740 uint32_t type = ELF32_R_TYPE(relocs[i].r_info); 741 uint32_t sym = ELF32_R_SYM(relocs[i].r_info); 742 743 DBG(" [%3zu] 0x%08" PRIx32 " type %2" PRIu32 " symIdx %3" PRIu32 744 " --> 0x%08" PRIx32, i, relocs[i].r_offset, type, sym, 745 syms[sym].st_value); 746 // Note that R_ARM_TARGET1 is used for .init_array/.fini_array support, 747 // and can be interpreted either as ABS32 or REL32, depending on the 748 // runtime; we expect it to be ABS32. 749 if (type == R_ARM_ABS32 || type == R_ARM_TARGET1) { 750 if (!IS_IN_RAM(relocs[i].r_offset)) { 751 ERR("Reloc for .data not in RAM address range!"); 752 goto out; 753 } 754 uint32_t offset = relocs[i].r_offset - RAM_BASE; 755 uint32_t *addr = (uint32_t *) &data[offset]; 756 757 nanoRelocs[numRelocs].type = fixupAddrElf(addr); 758 nanoRelocs[numRelocs].ofstInRam = offset; 759 numRelocs++; 760 } else { 761 // TODO: Assuming that the ELF only contains absolute addresses in 762 // the .data section; may need to handle other relocation types in 763 // the future 764 ERR("Error: Unexpected reloc type %" PRIu32 " at index %zu", 765 type, i); 766 goto out; 767 } 768 } 769 770 DBG("Updating GOT entries (%zu):", gotCount); 771 for (uint32_t offset = sect->got_start; offset < sect->got_end; 772 offset += sizeof(uint32_t)) { 773 uint32_t *addr = (uint32_t *) &data[offset]; 774 // Skip values that are set to 0, these seem to be padding (?) 775 if (*addr) { 776 nanoRelocs[numRelocs].type = fixupAddrElf(addr); 777 nanoRelocs[numRelocs].ofstInRam = offset; 778 numRelocs++; 779 } 780 } 781 782 uint32_t packedNanoRelocSz = 0; 783 app->packedNanoRelocs.data = packNanoRelocs( 784 nanoRelocs, numRelocs, &packedNanoRelocSz, verbose); 785 app->packedNanoRelocs.size = packedNanoRelocSz; 786 success = true; 787out: 788 free(nanoRelocs); 789 return success; 790} 791 792static int handleAppStatic(const char *fileName, FILE *out, uint32_t layoutFlags, uint64_t appId, uint32_t appVer, uint32_t chreApi, bool verbose) 793{ 794 struct ElfNanoApp app; 795 796 if (!loadNanoappElfFile(fileName, &app) 797 || !fixupHeaderElf(&app) 798 || !genElfNanoRelocs(&app, verbose)) { 799 exit(2); 800 } 801 802 // Construct a single contiguous buffer, with extra room to fit the 803 // ImageHeader that will be prepended by finalizeAndWrite(). Note that this 804 // will allocate a bit more space than is needed, because some of the data 805 // from BinHdr will get discarded. 806 // TODO: this should be refactored to just write the binary components in 807 // order rather than allocating a big buffer, and moving data around 808 size_t bufSize = app.flash.size + app.data.size + app.packedNanoRelocs.size 809 + sizeof(struct ImageHeader); 810 uint8_t *buf = malloc(bufSize); 811 if (!buf) { 812 ERR("Failed to allocate %zu bytes for final app", bufSize); 813 exit(2); 814 } 815 816 size_t offset = 0; 817 memcpy(buf, app.flash.data, app.flash.size); 818 offset += app.flash.size; 819 memcpy(&buf[offset], app.data.data, app.data.size); 820 offset += app.data.size; 821 memcpy(&buf[offset], app.packedNanoRelocs.data, app.packedNanoRelocs.size); 822 offset += app.packedNanoRelocs.size; 823 824 // Update rel_end in the header to reflect the packed reloc size 825 struct BinHdr *hdr = (struct BinHdr *) buf; 826 hdr->sect.rel_end = hdr->sect.rel_start + app.packedNanoRelocs.size; 827 hdr->hdr.appVer = appVer; 828 829 return finalizeAndWrite(buf, offset, bufSize, out, layoutFlags, appId, chreApi); 830 // TODO: should free all memory we allocated... just letting the OS handle 831 // it for now 832} 833 834static int handleKey(uint8_t **pbuf, uint32_t bufUsed, FILE *out, uint32_t layoutFlags, uint64_t appId, uint64_t keyId) 835{ 836 uint8_t *buf = *pbuf; 837 struct KeyInfo ki = { .data = keyId }; 838 bool good = true; 839 840 struct ImageHeader outHeader = { 841 .aosp = (struct nano_app_binary_t) { 842 .header_version = 1, 843 .magic = NANOAPP_AOSP_MAGIC, 844 .app_id = appId, 845 }, 846 .layout = (struct ImageLayout) { 847 .magic = GOOGLE_LAYOUT_MAGIC, 848 .version = 1, 849 .payload = LAYOUT_KEY, 850 .flags = layoutFlags, 851 }, 852 }; 853 854 good = good && fwrite(&outHeader, sizeof(outHeader), 1, out) == 1; 855 good = good && fwrite(&ki, sizeof(ki), 1, out) == 1; 856 good = good && fwrite(buf, bufUsed, 1, out) == 1; 857 858 return good ? 0 : 2; 859} 860 861static int handleOs(uint8_t **pbuf, uint32_t bufUsed, FILE *out, uint32_t layoutFlags, bool bare) 862{ 863 uint8_t *buf = *pbuf; 864 bool good; 865 866 struct OsUpdateHdr os = { 867 .magic = OS_UPDT_MAGIC, 868 .marker = OS_UPDT_MARKER_INPROGRESS, 869 .size = bufUsed 870 }; 871 872 struct ImageHeader outHeader = { 873 .aosp = (struct nano_app_binary_t) { 874 .header_version = 1, 875 .magic = NANOAPP_AOSP_MAGIC, 876 }, 877 .layout = (struct ImageLayout) { 878 .magic = GOOGLE_LAYOUT_MAGIC, 879 .version = 1, 880 .payload = LAYOUT_OS, 881 .flags = layoutFlags, 882 }, 883 }; 884 885 if (!bare) 886 good = fwrite(&outHeader, sizeof(outHeader), 1, out) == 1; 887 else 888 good = fwrite(&os, sizeof(os), 1, out) == 1; 889 good = good && fwrite(buf, bufUsed, 1, out) == 1; 890 891 return good ? 0 : 2; 892} 893 894int main(int argc, char **argv) 895{ 896 uint32_t bufUsed = 0; 897 bool verbose = false; 898 uint8_t *buf = NULL; 899 uint64_t appId = 0; 900 uint64_t keyId = 0; 901 uint32_t appVer = 0; 902 uint32_t chreApi = 0; 903 uint32_t layoutId = 0; 904 uint32_t layoutFlags = 0; 905 int ret = -1; 906 uint32_t *u32Arg = NULL; 907 uint64_t *u64Arg = NULL; 908 const char **strArg = NULL; 909 const char *appName = argv[0]; 910 int posArgCnt = 0; 911 const char *posArg[2] = { NULL }; 912 FILE *out = NULL; 913 const char *layoutName = "app"; 914 const char *prev = NULL; 915 bool bareData = false; 916 bool staticElf = false; 917 918 for (int i = 1; i < argc; i++) { 919 char *end = NULL; 920 if (argv[i][0] == '-') { 921 prev = argv[i]; 922 if (!strcmp(argv[i], "-v")) 923 verbose = true; 924 else if (!strcmp(argv[i], "-r")) 925 bareData = true; 926 else if (!strcmp(argv[i], "-s")) 927 staticElf = true; 928 else if (!strcmp(argv[i], "-a")) 929 u64Arg = &appId; 930 else if (!strcmp(argv[i], "-c")) 931 u32Arg = &chreApi; 932 else if (!strcmp(argv[i], "-e")) 933 u32Arg = &appVer; 934 else if (!strcmp(argv[i], "-k")) 935 u64Arg = &keyId; 936 else if (!strcmp(argv[i], "-n")) 937 strArg = &layoutName; 938 else if (!strcmp(argv[i], "-i")) 939 u32Arg = &layoutId; 940 else if (!strcmp(argv[i], "-f")) 941 u32Arg = &layoutFlags; 942 else 943 fatalUsage(appName, "unknown argument", argv[i]); 944 } else { 945 if (u64Arg) { 946 uint64_t tmp = strtoull(argv[i], &end, 16); 947 if (*end == '\0') 948 *u64Arg = tmp; 949 u64Arg = NULL; 950 } else if (u32Arg) { 951 uint32_t tmp = strtoul(argv[i], &end, 16); 952 if (*end == '\0') 953 *u32Arg = tmp; 954 u32Arg = NULL; 955 } else if (strArg) { 956 *strArg = argv[i]; 957 strArg = NULL; 958 } else { 959 if (posArgCnt < 2) 960 posArg[posArgCnt++] = argv[i]; 961 else 962 fatalUsage(appName, "too many positional arguments", argv[i]); 963 } 964 prev = NULL; 965 } 966 } 967 if (prev) 968 fatalUsage(appName, "missing argument after", prev); 969 970 if (!posArgCnt) 971 fatalUsage(appName, "missing input file name", NULL); 972 973 if (!layoutId) { 974 if (strcmp(layoutName, "app") == 0) 975 layoutId = LAYOUT_APP; 976 else if (strcmp(layoutName, "os") == 0) 977 layoutId = LAYOUT_OS; 978 else if (strcmp(layoutName, "key") == 0) 979 layoutId = LAYOUT_KEY; 980 else 981 fatalUsage(appName, "Invalid layout name", layoutName); 982 } 983 984 if (staticElf && layoutId != LAYOUT_APP) 985 fatalUsage(appName, "Only app layout is supported for static option", NULL); 986 987 if (layoutId == LAYOUT_APP && !appId) 988 fatalUsage(appName, "App layout requires app ID", NULL); 989 if (layoutId == LAYOUT_KEY && !keyId) 990 fatalUsage(appName, "Key layout requires key ID", NULL); 991 if (layoutId == LAYOUT_OS && (keyId || appId)) 992 fatalUsage(appName, "OS layout does not need any ID", NULL); 993 994 if (!staticElf) { 995 buf = loadFile(posArg[0], &bufUsed); 996 fprintf(stderr, "Read %" PRIu32 " bytes\n", bufUsed); 997 } 998 999 if (!posArg[1]) 1000 out = stdout; 1001 else 1002 out = fopen(posArg[1], "w"); 1003 if (!out) 1004 fatalUsage(appName, "failed to create/open output file", posArg[1]); 1005 1006 switch(layoutId) { 1007 case LAYOUT_APP: 1008 if (staticElf) { 1009 ret = handleAppStatic(posArg[0], out, layoutFlags, appId, appVer, chreApi, verbose); 1010 } else { 1011 ret = handleApp(&buf, bufUsed, out, layoutFlags, appId, appVer, chreApi, verbose); 1012 } 1013 break; 1014 case LAYOUT_KEY: 1015 ret = handleKey(&buf, bufUsed, out, layoutFlags, appId, keyId); 1016 break; 1017 case LAYOUT_OS: 1018 ret = handleOs(&buf, bufUsed, out, layoutFlags, bareData); 1019 break; 1020 } 1021 1022 free(buf); 1023 fclose(out); 1024 return ret; 1025} 1026