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