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