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 &sect 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