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
54 struct 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
64 struct SymtabEntry {
65 uint32_t a;
66 uint32_t addr;
67 uint32_t b, c;
68 };
69
70 struct 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
85 struct ElfAppSection {
86 void *data;
87 size_t size;
88 };
89
90 struct 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
fatalUsage(const char * name,const char * msg,const char * arg)100 static 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
packNanoRelocs(struct NanoRelocEntry * nanoRelocs,uint32_t outNumRelocs,uint32_t * finalPackedNanoRelocSz,bool verbose)123 static 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
finalizeAndWrite(uint8_t * buf,uint32_t bufUsed,uint32_t bufSz,FILE * out,uint32_t layoutFlags,uint64_t appId,uint32_t chreApi)229 static 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
handleApp(uint8_t ** pbuf,uint32_t bufUsed,FILE * out,uint32_t layoutFlags,uint64_t appId,uint32_t appVer,uint32_t chreApi,bool verbose)291 static 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);
536 out:
537 free(nanoRelocs);
538 return ret;
539 }
540
elfExtractSectionPointer(const Elf_Data * data,const char * name,struct ElfNanoApp * app)541 static 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
elfParse(Elf * elf,struct ElfNanoApp * app)586 static 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
loadNanoappElfFile(const char * fileName,struct ElfNanoApp * app)619 static 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.
fixupAddrElf(uint32_t * addr)653 static 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.
fixupHeaderElf(const struct ElfNanoApp * app)679 static 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.
genElfNanoRelocs(struct ElfNanoApp * app,bool verbose)714 static 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;
787 out:
788 free(nanoRelocs);
789 return success;
790 }
791
handleAppStatic(const char * fileName,FILE * out,uint32_t layoutFlags,uint64_t appId,uint32_t appVer,uint32_t chreApi,bool verbose)792 static 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
handleKey(uint8_t ** pbuf,uint32_t bufUsed,FILE * out,uint32_t layoutFlags,uint64_t appId,uint64_t keyId)834 static 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
handleOs(uint8_t ** pbuf,uint32_t bufUsed,FILE * out,uint32_t layoutFlags,bool bare)861 static 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
main(int argc,char ** argv)894 int 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