kit

kit
git clone https://git.ryansepassi.com/git/kit.git
Log | Files | Refs | README

image.c (36500B)


      1 #include <kit/image.h>
      2 #include <kit/object.h>
      3 #include <stdarg.h>
      4 #include <stdint.h>
      5 #include <stdio.h>
      6 #include <string.h>
      7 
      8 #include "driver.h"
      9 
     10 #define IMAGE_TOOL "image"
     11 
     12 typedef struct ImageCliOpts {
     13   KitImageOptions image;
     14   /* Backing storage for the repeatable name lists referenced by `image`. Each
     15    * list owns its own array; the KitImageOptions pointers alias into them. */
     16   KitSlice* segments;
     17   uint32_t cap_segments;
     18   KitSlice* only_sections;
     19   uint32_t cap_only;
     20   KitSlice* remove_sections;
     21   uint32_t cap_remove;
     22   KitSlice* section_order;
     23   uint32_t cap_section_order;
     24   KitSlice* require_symbols;
     25   uint32_t cap_require_symbols;
     26   KitSlice* require_sections;
     27   uint32_t cap_require_sections;
     28   /* ELF/debug knobs (only meaningful for --format elf, which is deferred). */
     29   bool strip_debug;
     30   bool keep_symbols;
     31   const char* split_debug;
     32   const char* metadata;
     33   const char* input;
     34   const char* output;
     35 } ImageCliOpts;
     36 
     37 void driver_help_image(void) {
     38   driver_printf(
     39       "%.*s",
     40       KIT_SLICE_ARG(KIT_SLICE_LIT(
     41           "kit image — emit flat load images from objects and linked images\n"
     42           "\n"
     43           "USAGE\n"
     44           "  kit image [OPTIONS] INPUT [-o OUTPUT]\n"
     45           "\n"
     46           "FORMAT\n"
     47           "  --format bin             flat binary from loadable ranges "
     48           "(default)\n"
     49           "  --format rom             fixed-size flat binary (requires "
     50           "--pad-to)\n"
     51           "  --format sections        concatenate --section bytes in order\n"
     52           "  --format ihex            Intel HEX records from loadable ranges\n"
     53           "  --format srec            Motorola S-records from loadable ranges\n"
     54           "  --from segments          linked segments; relocatables use "
     55           "allocated sections (default)\n"
     56           "  --from sections          derive bytes from named sections\n"
     57           "\n"
     58           "SELECTION\n"
     59           "  --segment PT_LOAD        select a segment kind/name (may repeat)\n"
     60           "  --only-section NAME      restrict selection to NAME (may repeat)\n"
     61           "  --remove-section NAME    drop NAME from the selection (may "
     62           "repeat)\n"
     63           "  --section NAME           ordered section for --format sections "
     64           "(may repeat)\n"
     65           "\n"
     66           "ADDRESSING\n"
     67           "  --addr vaddr|paddr|lma   address field used for layout "
     68           "(default: vaddr)\n"
     69           "  --base ADDR              output offset 0 corresponds to ADDR\n"
     70           "  --bias N                 add signed bias to selected addresses\n"
     71           "\n"
     72           "LAYOUT\n"
     73           "  --fill BYTE              byte used for holes/padding (default: 0)\n"
     74           "  --fail-on-holes          reject gaps between selected ranges\n"
     75           "  --max-hole SIZE          reject any gap larger than SIZE\n"
     76           "  --align SIZE             pad final size up to SIZE\n"
     77           "  --pad-to SIZE            pad final size to SIZE\n"
     78           "  --max-size SIZE          reject output larger than SIZE\n"
     79           "\n"
     80           "KERNEL IMAGE HEADER (bin/rom; preserves the entry branch, fills the\n"
     81           "metadata tail — the first segment must reserve a 64-byte header)\n"
     82           "  --image-header[=arm64|riscv|auto]  synthesize a flat-kernel "
     83           "Image header\n"
     84           "  --image-text-offset N    header text_offset field (default: 0)\n"
     85           "  --image-endian little|big header endianness flag (default: "
     86           "little)\n"
     87           "  --image-page-size 4k|16k|64k  arm64 page-size flag (default: "
     88           "unspecified)\n"
     89           "\n"
     90           "VALIDATION\n"
     91           "  --require-entry          fail unless the image declares an "
     92           "entry\n"
     93           "  --require-symbol NAME    fail unless NAME is defined (may "
     94           "repeat)\n"
     95           "  --require-section NAME   fail unless NAME is present (may "
     96           "repeat)\n"
     97           "  --no-dynamic             reject dynamic-linking artifacts\n"
     98           "\n"
     99           "REPORTING\n"
    100           "  --metadata FILE          write a deterministic JSON sidecar\n"
    101           "\n"
    102           "OUTPUT\n"
    103           "  -o, --output FILE        write output to FILE (default: stdout)\n"
    104           "\n"
    105           "SIZE suffixes K, M, and G are accepted.\n"
    106           "\n"
    107           "TEXT-FORMAT ADDRESS LIMITS\n"
    108           "  IHEX/SREC records require adjusted addresses that fit their\n"
    109           "  32-bit range. Native linked images can start above that range.\n"
    110           "  --base changes flat-output placement and is rejected for IHEX/\n"
    111           "  SREC. Use an explicit signed --bias to adjust every record and\n"
    112           "  entry address; every adjusted address must fit in 32 bits.\n"
    113           "\n"
    114           "GETTING HELP\n"
    115           "  -h, --help              Show this help and exit\n"
    116           "\n"
    117           "EXAMPLES\n"
    118           "  # Build a minimal aarch64 freestanding linked image.\n"
    119           "  printf '.global _start\\n.text\\n_start:\\n  b _start\\n' > start.s\n"
    120           "  printf 'ENTRY(_start)\\nSECTIONS { . = 0x400000; .text : { "
    121           "*(.text*) } }\\n' > link.ld\n"
    122           "  kit as -target aarch64-none-elf -o start.o start.s\n"
    123           "  kit ld -T link.ld \\\n"
    124           "    -e _start -o kernel.elf start.o\n"
    125           "  kit image --format bin --require-entry \\\n"
    126           "    --require-symbol _start --require-section .text \\\n"
    127           "    --metadata kernel.json kernel.elf -o kernel.bin\n"
    128           "  kit size kernel.elf\n"
    129           "  # A high-address IHEX image needs an explicit representable bias.\n"
    130           "  printf 'ENTRY(_start)\\nSECTIONS { . = 0x100000000; .text : "
    131           "{ *(.text*) } }\\n' > high.ld\n"
    132           "  kit ld -T high.ld \\\n"
    133           "    -e _start -o high.elf start.o\n"
    134           "  kit image --format ihex --bias -4294967296 \\\n"
    135           "    high.elf -o high.hex\n"
    136           "\n"
    137           "EXIT CODES\n"
    138           "  0   success           1   image/I/O error           2   bad "
    139           "usage\n")));
    140 }
    141 
    142 static int take_value(int* i, int argc, char** argv, const char* flag,
    143                       const char** out) {
    144   const char* a = argv[*i];
    145   size_t flen = kit_slice_cstr(flag).len;
    146   if (driver_strneq(a, flag, flen) && a[flen] == '=') {
    147     *out = a + flen + 1;
    148     return 1;
    149   }
    150   if (driver_streq(a, flag)) {
    151     if (*i + 1 >= argc) return -1;
    152     *out = argv[++(*i)];
    153     return 1;
    154   }
    155   return 0;
    156 }
    157 
    158 static int digit_value(char c) {
    159   if (c >= '0' && c <= '9') return c - '0';
    160   if (c >= 'a' && c <= 'f') return 10 + c - 'a';
    161   if (c >= 'A' && c <= 'F') return 10 + c - 'A';
    162   return -1;
    163 }
    164 
    165 static int parse_size(const char* s, uint64_t* out) {
    166   size_t len;
    167   size_t end;
    168   size_t pos = 0;
    169   uint64_t val = 0;
    170   uint64_t mult = 1;
    171   int base = 10;
    172 
    173   if (!s || !s[0] || !out) return -1;
    174   len = driver_strlen(s);
    175   end = len;
    176   switch (s[end - 1]) {
    177     case 'k':
    178     case 'K':
    179       mult = 1024ull;
    180       --end;
    181       break;
    182     case 'm':
    183     case 'M':
    184       mult = 1024ull * 1024ull;
    185       --end;
    186       break;
    187     case 'g':
    188     case 'G':
    189       mult = 1024ull * 1024ull * 1024ull;
    190       --end;
    191       break;
    192     default:
    193       break;
    194   }
    195   if (end == 0) return -1;
    196   if (end >= 2 && s[0] == '0' && (s[1] == 'x' || s[1] == 'X')) {
    197     base = 16;
    198     pos = 2;
    199     if (pos >= end) return -1;
    200   }
    201   for (; pos < end; ++pos) {
    202     int d = digit_value(s[pos]);
    203     if (d < 0 || d >= base) return -1;
    204     if (val > (UINT64_MAX - (uint64_t)d) / (uint64_t)base) return -1;
    205     val = val * (uint64_t)base + (uint64_t)d;
    206   }
    207   if (val > UINT64_MAX / mult) return -1;
    208   *out = val * mult;
    209   return 0;
    210 }
    211 
    212 static int parse_i64_size(const char* s, int64_t* out) {
    213   int neg = 0;
    214   uint64_t mag;
    215   uint64_t min_mag = (uint64_t)INT64_MAX + 1u;
    216   if (!s || !s[0] || !out) return -1;
    217   if (s[0] == '-' || s[0] == '+') {
    218     neg = s[0] == '-';
    219     ++s;
    220   }
    221   if (!s[0] || parse_size(s, &mag) != 0) return -1;
    222   if (neg) {
    223     if (mag > min_mag) return -1;
    224     if (mag == min_mag) {
    225       *out = INT64_MIN;
    226     } else {
    227       *out = -(int64_t)mag;
    228     }
    229   } else {
    230     if (mag > (uint64_t)INT64_MAX) return -1;
    231     *out = (int64_t)mag;
    232   }
    233   return 0;
    234 }
    235 
    236 static int parse_format(const char* s, uint32_t* out) {
    237   if (driver_streq(s, "bin") || driver_streq(s, "binary")) {
    238     *out = KIT_IMAGE_FORMAT_BIN;
    239     return 0;
    240   }
    241   if (driver_streq(s, "rom")) {
    242     *out = KIT_IMAGE_FORMAT_ROM;
    243     return 0;
    244   }
    245   if (driver_streq(s, "sections")) {
    246     *out = KIT_IMAGE_FORMAT_SECTIONS;
    247     return 0;
    248   }
    249   if (driver_streq(s, "ihex") || driver_streq(s, "ihex8")) {
    250     *out = KIT_IMAGE_FORMAT_IHEX;
    251     return 0;
    252   }
    253   if (driver_streq(s, "srec") || driver_streq(s, "s19")) {
    254     *out = KIT_IMAGE_FORMAT_SREC;
    255     return 0;
    256   }
    257   if (driver_streq(s, "elf")) {
    258     *out = KIT_IMAGE_FORMAT_ELF;
    259     return 0;
    260   }
    261   return -1;
    262 }
    263 
    264 static int parse_from(const char* s, uint32_t* out) {
    265   if (driver_streq(s, "segments")) {
    266     *out = KIT_IMAGE_FROM_SEGMENTS;
    267     return 0;
    268   }
    269   if (driver_streq(s, "sections")) {
    270     *out = KIT_IMAGE_FROM_SECTIONS;
    271     return 0;
    272   }
    273   return -1;
    274 }
    275 
    276 static void image_unknown_value(const char* category, const char* value,
    277                                 const char* const* candidates,
    278                                 size_t candidate_count) {
    279   DriverSuggestion suggestions[3];
    280   size_t n = driver_suggest_values(value, candidates, candidate_count,
    281                                    suggestions, 3);
    282   if (n)
    283     driver_errf(IMAGE_TOOL, "unknown %s: %s; did you mean '%s'?", category,
    284                 value, suggestions[0].value);
    285   else
    286     driver_errf(IMAGE_TOOL, "unknown %s: %s", category, value);
    287 }
    288 
    289 static int parse_addr(const char* s, uint32_t* out) {
    290   if (driver_streq(s, "vaddr")) {
    291     *out = KIT_IMAGE_ADDR_VADDR;
    292     return 0;
    293   }
    294   if (driver_streq(s, "paddr")) {
    295     *out = KIT_IMAGE_ADDR_PADDR;
    296     return 0;
    297   }
    298   if (driver_streq(s, "lma")) {
    299     *out = KIT_IMAGE_ADDR_LMA;
    300     return 0;
    301   }
    302   return -1;
    303 }
    304 
    305 static int parse_image_header(const char* s, uint32_t* out) {
    306   if (driver_streq(s, "arm64") || driver_streq(s, "aarch64") ||
    307       driver_streq(s, "aa64")) {
    308     *out = KIT_IMAGE_HEADER_ARM64;
    309     return 0;
    310   }
    311   if (driver_streq(s, "riscv") || driver_streq(s, "riscv64") ||
    312       driver_streq(s, "rv64") || driver_streq(s, "riscv32") ||
    313       driver_streq(s, "rv32")) {
    314     *out = KIT_IMAGE_HEADER_RISCV;
    315     return 0;
    316   }
    317   if (driver_streq(s, "auto")) {
    318     *out = KIT_IMAGE_HEADER_AUTO;
    319     return 0;
    320   }
    321   return -1;
    322 }
    323 
    324 /* Append cstr `name` to a growable KitSlice list, re-aliasing the matching
    325  * KitImageOptions pointer at *aliased_ptr to the (possibly reallocated)
    326  * storage and bumping *count. */
    327 static int push_name(DriverEnv* env, KitSlice** arr, uint32_t* count,
    328                      uint32_t* cap, const KitSlice** aliased_ptr,
    329                      const char* name) {
    330   if (*count >= *cap) {
    331     uint32_t newcap = *cap ? *cap * 2u : 4u;
    332     KitSlice* next;
    333     if (newcap <= *cap) return -1;
    334     next = (KitSlice*)driver_alloc_zeroed(env, (size_t)newcap * sizeof(*next));
    335     if (!next) return -1;
    336     if (*arr) {
    337       memcpy(next, *arr, (size_t)(*count) * sizeof(*next));
    338       driver_free(env, *arr, (size_t)(*cap) * sizeof(**arr));
    339     }
    340     *arr = next;
    341     *cap = newcap;
    342     *aliased_ptr = next;
    343   }
    344   (*arr)[(*count)++] = kit_slice_cstr(name);
    345   return 0;
    346 }
    347 
    348 /* ---- metadata JSON sidecar ------------------------------------------------
    349  *
    350  * Deterministic: stable key order, no host paths, no timestamps. Values that
    351  * vary by input (target triple, entry, build id, ranges) are derived from the
    352  * opened object and the layout report.
    353  *
    354  * The driver has no public string-builder, so the document is assembled into a
    355  * small growable byte buffer with snprintf-backed appends, then written in one
    356  * shot. */
    357 
    358 typedef struct MetaBuf {
    359   DriverEnv* env;
    360   char* data;
    361   size_t len;
    362   size_t cap;
    363   int oom;
    364 } MetaBuf;
    365 
    366 static void meta_reserve(MetaBuf* mb, size_t extra) {
    367   size_t need = mb->len + extra + 1u; /* + NUL room for snprintf */
    368   if (mb->oom || need <= mb->cap) return;
    369   {
    370     size_t newcap = mb->cap ? mb->cap : 256u;
    371     char* next;
    372     while (newcap < need) {
    373       size_t doubled = newcap * 2u;
    374       if (doubled <= newcap) {
    375         mb->oom = 1;
    376         return;
    377       }
    378       newcap = doubled;
    379     }
    380     next = (char*)driver_alloc_zeroed(mb->env, newcap);
    381     if (!next) {
    382       mb->oom = 1;
    383       return;
    384     }
    385     if (mb->data) {
    386       memcpy(next, mb->data, mb->len);
    387       driver_free(mb->env, mb->data, mb->cap);
    388     }
    389     mb->data = next;
    390     mb->cap = newcap;
    391   }
    392 }
    393 
    394 static void meta_putc(MetaBuf* mb, char c) {
    395   meta_reserve(mb, 1);
    396   if (mb->oom) return;
    397   mb->data[mb->len++] = c;
    398 }
    399 
    400 static void meta_putf(MetaBuf* mb, const char* fmt, ...) {
    401   va_list ap, ap2;
    402   int n;
    403   va_start(ap, fmt);
    404   va_copy(ap2, ap);
    405   n = vsnprintf(NULL, 0, fmt, ap);
    406   va_end(ap);
    407   if (n < 0) {
    408     mb->oom = 1;
    409     va_end(ap2);
    410     return;
    411   }
    412   meta_reserve(mb, (size_t)n);
    413   if (mb->oom) {
    414     va_end(ap2);
    415     return;
    416   }
    417   vsnprintf(mb->data + mb->len, (size_t)n + 1u, fmt, ap2);
    418   va_end(ap2);
    419   mb->len += (size_t)n;
    420 }
    421 
    422 /* JSON-escape a section/segment name (quotes + minimal escaping). */
    423 static void meta_put_json_string(MetaBuf* mb, KitSlice s) {
    424   size_t i;
    425   meta_putc(mb, '"');
    426   for (i = 0; i < s.len; ++i) {
    427     char c = s.s[i];
    428     if (c == '"' || c == '\\') {
    429       meta_putc(mb, '\\');
    430       meta_putc(mb, c);
    431     } else if ((unsigned char)c < 0x20) {
    432       meta_putf(mb, "\\u%04x", (unsigned)(unsigned char)c);
    433     } else {
    434       meta_putc(mb, c);
    435     }
    436   }
    437   meta_putc(mb, '"');
    438 }
    439 
    440 /* Locate and parse a `.note.gnu.build-id` ELF note's descriptor bytes. Returns
    441  * the descriptor slice (borrowed from the object) or an empty slice. */
    442 static KitSlice read_build_id(KitObjFile* of) {
    443   KitObjSection sid = KIT_SECTION_NONE;
    444   const uint8_t* data = NULL;
    445   size_t len = 0;
    446   uint32_t namesz, descsz;
    447   size_t off;
    448   KitSlice empty = KIT_SLICE_NULL;
    449 
    450   if (kit_obj_section_by_name(of, kit_slice_cstr(".note.gnu.build-id"), &sid) !=
    451           KIT_OK ||
    452       sid == KIT_SECTION_NONE)
    453     return empty;
    454   if (kit_obj_section_data(of, sid, &data, &len) != KIT_OK || !data || len < 12)
    455     return empty;
    456   /* ELF note: u32 namesz, u32 descsz, u32 type, name[namesz], desc[descsz].
    457    * Little-endian (the only endianness kit links). */
    458   namesz = (uint32_t)data[0] | ((uint32_t)data[1] << 8) |
    459            ((uint32_t)data[2] << 16) | ((uint32_t)data[3] << 24);
    460   descsz = (uint32_t)data[4] | ((uint32_t)data[5] << 8) |
    461            ((uint32_t)data[6] << 16) | ((uint32_t)data[7] << 24);
    462   off = 12u + ((namesz + 3u) & ~3u);
    463   if (off > len || descsz > len - off) return empty;
    464   {
    465     KitSlice s;
    466     s.s = (const char*)data + off;
    467     s.len = descsz;
    468     return s;
    469   }
    470 }
    471 
    472 /* Emit the JSON list of selected segments/sections that contributed bytes.
    473  * For the section path this is the declared --section order; for the segment
    474  * path it is the loadable segments the layout selected — mirroring the
    475  * emitter's default-ELF-PT_LOAD / explicit-list policy plus only/remove
    476  * filters, so the sidecar reflects exactly what the image contains. */
    477 static int image_name_in_list(KitSlice name, const KitSlice* list, uint32_t n) {
    478   uint32_t i;
    479   for (i = 0; i < n; ++i) {
    480     if (name.len == list[i].len &&
    481         (name.len == 0 || memcmp(name.s, list[i].s, name.len) == 0))
    482       return 1;
    483   }
    484   return 0;
    485 }
    486 
    487 static int meta_section_selected(const KitObjSecInfo* sec,
    488                                  const KitImageOptions* opts) {
    489   if (!(sec->flags & KIT_SF_ALLOC) || sec->kind == KIT_SEC_BSS ||
    490       sec->size == 0)
    491     return 0;
    492   if (opts->nonly_sections &&
    493       !image_name_in_list(sec->name, opts->only_sections,
    494                           opts->nonly_sections))
    495     return 0;
    496   if (opts->nremove_sections &&
    497       image_name_in_list(sec->name, opts->remove_sections,
    498                          opts->nremove_sections))
    499     return 0;
    500   return 1;
    501 }
    502 
    503 static void meta_put_selection(MetaBuf* mb, KitObjFile* of,
    504                                const ImageCliOpts* o,
    505                                const KitImageReport* report) {
    506   uint32_t i;
    507   int first = 1;
    508   if (report->sections_concat) {
    509     for (i = 0; i < o->image.nsection_order; ++i) {
    510       if (!first) meta_putc(mb, ',');
    511       first = 0;
    512       meta_put_json_string(mb, o->image.section_order[i]);
    513     }
    514     return;
    515   }
    516   if (report->section_ranges) {
    517     uint32_t nsections = kit_obj_nsections(of);
    518     for (i = 0; i < nsections; ++i) {
    519       KitObjSecInfo sec;
    520       if (kit_obj_section(of, i, &sec) != KIT_OK ||
    521           !meta_section_selected(&sec, &o->image))
    522         continue;
    523       if (!first) meta_putc(mb, ',');
    524       first = 0;
    525       meta_put_json_string(mb, sec.name);
    526     }
    527     return;
    528   }
    529   {
    530     KitObjSegIter* it = NULL;
    531     KitObjSegInfo seg;
    532     KitObjFmt fmt = kit_obj_fmt(of);
    533     if (kit_obj_segiter_new(of, &it) != KIT_OK) return;
    534     while (kit_obj_segiter_next(it, &seg) == KIT_ITER_ITEM) {
    535       /* Defer to the emitter's own selection predicate so the sidecar lists
    536        * exactly the segments that contributed bytes — matching its PT_/case
    537        * folding and only/remove/default-PT_LOAD policy rather than a divergent
    538        * exact-byte reimplementation. */
    539       if (!kit_image_segment_selected(fmt, &seg, &o->image)) continue;
    540       if (!first) meta_putc(mb, ',');
    541       first = 0;
    542       if (seg.name.len)
    543         meta_put_json_string(mb, seg.name);
    544       else
    545         meta_put_json_string(mb, kit_slice_cstr("PT_LOAD"));
    546     }
    547     kit_obj_segiter_free(it);
    548   }
    549 }
    550 
    551 static const char* image_format_name(uint32_t format) {
    552   switch ((KitImageFormat)format) {
    553     case KIT_IMAGE_FORMAT_ROM:
    554       return "rom";
    555     case KIT_IMAGE_FORMAT_SECTIONS:
    556       return "sections";
    557     case KIT_IMAGE_FORMAT_ELF:
    558       return "elf";
    559     case KIT_IMAGE_FORMAT_IHEX:
    560       return "ihex";
    561     case KIT_IMAGE_FORMAT_SREC:
    562       return "srec";
    563     case KIT_IMAGE_FORMAT_BIN:
    564     default:
    565       return "bin";
    566   }
    567 }
    568 
    569 static int write_metadata(DriverEnv* env, const KitContext* ctx, KitObjFile* of,
    570                           const ImageCliOpts* o, const KitImageReport* report,
    571                           const char* path) {
    572   MetaBuf mb;
    573   KitTargetSpec spec;
    574   char triple[128];
    575   KitSlice build_id;
    576   int from_sections;
    577   KitWriter* w = NULL;
    578   int rc = 0;
    579 
    580   memset(&mb, 0, sizeof mb);
    581   mb.env = env;
    582 
    583   spec = kit_obj_target(of);
    584   if (driver_target_to_triple(spec, triple, sizeof triple) != 0)
    585     triple[0] = '\0';
    586   build_id = read_build_id(of);
    587   from_sections = report->sections_concat || report->section_ranges;
    588 
    589   /* Keys are emitted in a fixed order so the sidecar is byte-stable. */
    590   meta_putf(&mb, "{\n");
    591   meta_putf(&mb, "  \"format\": \"%s\",\n",
    592             image_format_name(o->image.format));
    593   meta_putf(&mb, "  \"source\": \"%s\",\n",
    594             from_sections ? "sections" : "segments");
    595   meta_putf(&mb, "  \"target\": \"%s\",\n", triple);
    596   meta_putf(&mb, "  \"object_format\": \"%s\",\n",
    597             kit_obj_fmt_name(kit_obj_fmt(of)));
    598   if (report->has_entry)
    599     meta_putf(&mb, "  \"entry\": \"0x%llx\",\n",
    600               (unsigned long long)report->emitted_entry);
    601   else
    602     meta_putf(&mb, "  \"entry\": null,\n");
    603   if (build_id.len) {
    604     size_t k;
    605     meta_putf(&mb, "  \"build_id\": \"");
    606     for (k = 0; k < build_id.len; ++k)
    607       meta_putf(&mb, "%02x", (unsigned)(unsigned char)build_id.s[k]);
    608     meta_putf(&mb, "\",\n");
    609   } else {
    610     meta_putf(&mb, "  \"build_id\": null,\n");
    611   }
    612 
    613   meta_putf(&mb, "  \"selection\": [");
    614   meta_put_selection(&mb, of, o, report);
    615   meta_putf(&mb, "],\n");
    616 
    617   meta_putf(&mb, "  \"addr_kind\": \"%s\",\n",
    618             o->image.addr == KIT_IMAGE_ADDR_PADDR ? "paddr"
    619             : o->image.addr == KIT_IMAGE_ADDR_LMA ? "lma"
    620                                                   : "vaddr");
    621   meta_putf(&mb, "  \"base\": \"0x%llx\",\n",
    622             (unsigned long long)report->base);
    623   meta_putf(&mb, "  \"original_base\": \"0x%llx\",\n",
    624             (unsigned long long)report->original_base);
    625   meta_putf(&mb, "  \"emitted_base\": \"0x%llx\",\n",
    626             (unsigned long long)report->emitted_base);
    627   if (report->has_entry) {
    628     meta_putf(&mb, "  \"original_entry\": \"0x%llx\",\n",
    629               (unsigned long long)report->original_entry);
    630     meta_putf(&mb, "  \"emitted_entry\": \"0x%llx\",\n",
    631               (unsigned long long)report->emitted_entry);
    632   } else {
    633     meta_putf(&mb, "  \"original_entry\": null,\n");
    634     meta_putf(&mb, "  \"emitted_entry\": null,\n");
    635   }
    636   meta_putf(&mb, "  \"base_is_load_address\": %s,\n",
    637             report->base_is_load_address ? "true" : "false");
    638   meta_putf(&mb, "  \"bias\": %lld,\n", (long long)o->image.bias);
    639   meta_putf(&mb, "  \"fill\": \"0x%02x\",\n", (unsigned)o->image.fill);
    640   meta_putf(&mb, "  \"payload_size\": %llu,\n",
    641             (unsigned long long)report->payload_size);
    642   meta_putf(&mb, "  \"output_size\": %llu,\n",
    643             (unsigned long long)report->size);
    644   if (report->image_header != KIT_IMAGE_HEADER_NONE) {
    645     meta_putf(&mb, "  \"image_header\": \"%s\",\n",
    646               report->image_header == KIT_IMAGE_HEADER_RISCV ? "riscv"
    647                                                              : "arm64");
    648     meta_putf(&mb, "  \"image_size\": %llu,\n",
    649               (unsigned long long)report->mem_size);
    650   }
    651   meta_putf(&mb, "  \"ranges\": %u,\n", (unsigned)report->nranges);
    652   meta_putf(&mb, "  \"max_hole\": %llu,\n",
    653             (unsigned long long)report->max_hole);
    654   meta_putf(&mb, "  \"warnings\": [");
    655   if (report->had_holes) meta_putf(&mb, "\"holes_present\"");
    656   meta_putf(&mb, "]\n");
    657   meta_putf(&mb, "}\n");
    658 
    659   if (mb.oom) {
    660     driver_errf(IMAGE_TOOL, "out of memory building metadata");
    661     rc = 1;
    662     goto done;
    663   }
    664   if (ctx->file_io->open_writer(ctx->file_io->user, path, &w) != KIT_OK) {
    665     driver_errf(IMAGE_TOOL, "failed to open metadata file: %s", path);
    666     rc = 1;
    667     goto done;
    668   }
    669   if (kit_writer_write(w, mb.data, mb.len) != KIT_OK ||
    670       kit_writer_status(w) != KIT_OK) {
    671     driver_writer_abort(w);
    672     driver_errf(IMAGE_TOOL, "failed to write metadata file: %s", path);
    673     rc = 1;
    674   }
    675 
    676 done:
    677   if (w) kit_writer_close(w);
    678   if (mb.data) driver_free(env, mb.data, mb.cap);
    679   return rc;
    680 }
    681 
    682 int driver_image(int argc, char** argv) {
    683   DriverEnv env;
    684   KitContext ctx;
    685   ImageCliOpts o;
    686   DriverLoad load = {0};
    687   KitSlice input = KIT_SLICE_NULL;
    688   KitObjFile* of = NULL;
    689   KitWriter* out = NULL;
    690   KitImageReport report;
    691   int own_writer = 0;
    692   int rc = 2;
    693   int i;
    694   int options = 1;
    695 
    696   if (argc < 2 || driver_argv_wants_help(argc, argv, 1)) {
    697     driver_help_image();
    698     return 0;
    699   }
    700 
    701   memset(&o, 0, sizeof o);
    702   memset(&report, 0, sizeof report);
    703   o.image.format = KIT_IMAGE_FORMAT_BIN;
    704   o.image.from = KIT_IMAGE_FROM_SEGMENTS;
    705   o.image.addr = KIT_IMAGE_ADDR_VADDR;
    706   o.image.fill = 0;
    707   driver_env_init(&env);
    708   ctx = driver_env_to_context(&env);
    709 
    710   for (i = 1; i < argc; ++i) {
    711     const char* a = argv[i];
    712     const char* val = NULL;
    713     int matched;
    714     if (options && driver_streq(a, "--")) {
    715       options = 0;
    716       continue;
    717     }
    718     if (!options) {
    719       if (o.input) {
    720         driver_errf(IMAGE_TOOL, "only one input may be given");
    721         goto done;
    722       }
    723       o.input = a;
    724       continue;
    725     }
    726     if (driver_streq(a, "-o")) {
    727       if (i + 1 >= argc) {
    728         driver_errf(IMAGE_TOOL, "%s requires a path", a);
    729         goto done;
    730       }
    731       o.output = argv[++i];
    732       continue;
    733     }
    734     matched = take_value(&i, argc, argv, "--output", &val);
    735     if (matched < 0) goto missing_value;
    736     if (matched) {
    737       o.output = val;
    738       continue;
    739     }
    740     matched = take_value(&i, argc, argv, "--format", &val);
    741     if (matched < 0) goto missing_value;
    742     if (matched) {
    743       if (parse_format(val, &o.image.format) != 0) {
    744         const char* const formats[] = {"bin",  "binary", "rom", "sections",
    745                                        "ihex", "ihex8",  "srec", "s19",
    746                                        "elf"};
    747         image_unknown_value("format", val, formats,
    748                             sizeof formats / sizeof formats[0]);
    749         goto done;
    750       }
    751       continue;
    752     }
    753     matched = take_value(&i, argc, argv, "--from", &val);
    754     if (matched < 0) goto missing_value;
    755     if (matched) {
    756       if (parse_from(val, &o.image.from) != 0) {
    757         const char* const sources[] = {"segments", "sections"};
    758         image_unknown_value("source", val, sources,
    759                             sizeof sources / sizeof sources[0]);
    760         goto done;
    761       }
    762       continue;
    763     }
    764     matched = take_value(&i, argc, argv, "--segment", &val);
    765     if (matched < 0) goto missing_value;
    766     if (matched) {
    767       if (push_name(&env, &o.segments, &o.image.nsegments, &o.cap_segments,
    768                     &o.image.segments, val) != 0)
    769         goto oom;
    770       continue;
    771     }
    772     matched = take_value(&i, argc, argv, "--only-section", &val);
    773     if (matched < 0) goto missing_value;
    774     if (matched) {
    775       if (push_name(&env, &o.only_sections, &o.image.nonly_sections,
    776                     &o.cap_only, &o.image.only_sections, val) != 0)
    777         goto oom;
    778       continue;
    779     }
    780     matched = take_value(&i, argc, argv, "--remove-section", &val);
    781     if (matched < 0) goto missing_value;
    782     if (matched) {
    783       if (push_name(&env, &o.remove_sections, &o.image.nremove_sections,
    784                     &o.cap_remove, &o.image.remove_sections, val) != 0)
    785         goto oom;
    786       continue;
    787     }
    788     matched = take_value(&i, argc, argv, "--section", &val);
    789     if (matched < 0) goto missing_value;
    790     if (matched) {
    791       if (push_name(&env, &o.section_order, &o.image.nsection_order,
    792                     &o.cap_section_order, &o.image.section_order, val) != 0)
    793         goto oom;
    794       continue;
    795     }
    796     matched = take_value(&i, argc, argv, "--require-symbol", &val);
    797     if (matched < 0) goto missing_value;
    798     if (matched) {
    799       if (push_name(&env, &o.require_symbols, &o.image.nrequire_symbols,
    800                     &o.cap_require_symbols, &o.image.require_symbols, val) != 0)
    801         goto oom;
    802       continue;
    803     }
    804     matched = take_value(&i, argc, argv, "--require-section", &val);
    805     if (matched < 0) goto missing_value;
    806     if (matched) {
    807       if (push_name(&env, &o.require_sections, &o.image.nrequire_sections,
    808                     &o.cap_require_sections, &o.image.require_sections, val) !=
    809           0)
    810         goto oom;
    811       continue;
    812     }
    813     if (driver_streq(a, "--require-entry")) {
    814       o.image.require_entry = true;
    815       continue;
    816     }
    817     if (driver_streq(a, "--no-dynamic")) {
    818       o.image.no_dynamic = true;
    819       continue;
    820     }
    821     if (driver_streq(a, "--strip-debug")) {
    822       o.strip_debug = true;
    823       continue;
    824     }
    825     if (driver_streq(a, "--keep-symbols")) {
    826       o.keep_symbols = true;
    827       continue;
    828     }
    829     matched = take_value(&i, argc, argv, "--split-debug", &val);
    830     if (matched < 0) goto missing_value;
    831     if (matched) {
    832       o.split_debug = val;
    833       continue;
    834     }
    835     matched = take_value(&i, argc, argv, "--metadata", &val);
    836     if (matched < 0) goto missing_value;
    837     if (matched) {
    838       o.metadata = val;
    839       continue;
    840     }
    841     matched = take_value(&i, argc, argv, "--addr", &val);
    842     if (matched < 0) goto missing_value;
    843     if (matched) {
    844       if (parse_addr(val, &o.image.addr) != 0) {
    845         driver_errf(IMAGE_TOOL, "unknown address kind: %s", val);
    846         goto done;
    847       }
    848       continue;
    849     }
    850     matched = take_value(&i, argc, argv, "--base", &val);
    851     if (matched < 0) goto missing_value;
    852     if (matched) {
    853       if (parse_size(val, &o.image.base) != 0) {
    854         driver_errf(IMAGE_TOOL, "invalid --base value: %s", val);
    855         goto done;
    856       }
    857       o.image.have_base = true;
    858       continue;
    859     }
    860     matched = take_value(&i, argc, argv, "--bias", &val);
    861     if (matched < 0) goto missing_value;
    862     if (matched) {
    863       if (parse_i64_size(val, &o.image.bias) != 0) {
    864         driver_errf(IMAGE_TOOL, "invalid --bias value: %s", val);
    865         goto done;
    866       }
    867       continue;
    868     }
    869     matched = take_value(&i, argc, argv, "--fill", &val);
    870     if (matched < 0) goto missing_value;
    871     if (matched) {
    872       uint64_t fill;
    873       if (parse_size(val, &fill) != 0 || fill > 255u) {
    874         driver_errf(IMAGE_TOOL, "invalid --fill byte: %s", val);
    875         goto done;
    876       }
    877       o.image.fill = (uint8_t)fill;
    878       continue;
    879     }
    880     if (driver_streq(a, "--fail-on-holes")) {
    881       o.image.fail_on_holes = true;
    882       continue;
    883     }
    884     matched = take_value(&i, argc, argv, "--max-hole", &val);
    885     if (matched < 0) goto missing_value;
    886     if (matched) {
    887       if (parse_size(val, &o.image.max_hole) != 0) {
    888         driver_errf(IMAGE_TOOL, "invalid --max-hole value: %s", val);
    889         goto done;
    890       }
    891       o.image.have_max_hole = true;
    892       continue;
    893     }
    894     matched = take_value(&i, argc, argv, "--align", &val);
    895     if (matched < 0) goto missing_value;
    896     if (matched) {
    897       if (parse_size(val, &o.image.align) != 0 || o.image.align == 0) {
    898         driver_errf(IMAGE_TOOL, "invalid --align value: %s", val);
    899         goto done;
    900       }
    901       o.image.have_align = true;
    902       continue;
    903     }
    904     matched = take_value(&i, argc, argv, "--pad-to", &val);
    905     if (matched < 0) goto missing_value;
    906     if (matched) {
    907       if (parse_size(val, &o.image.pad_to) != 0) {
    908         driver_errf(IMAGE_TOOL, "invalid --pad-to value: %s", val);
    909         goto done;
    910       }
    911       o.image.have_pad_to = true;
    912       continue;
    913     }
    914     matched = take_value(&i, argc, argv, "--max-size", &val);
    915     if (matched < 0) goto missing_value;
    916     if (matched) {
    917       if (parse_size(val, &o.image.max_size) != 0) {
    918         driver_errf(IMAGE_TOOL, "invalid --max-size value: %s", val);
    919         goto done;
    920       }
    921       o.image.have_max_size = true;
    922       continue;
    923     }
    924     /* Bare --image-header infers the arch from the object; --image-header=VALUE
    925      * names it. The space form is intentionally not accepted so a following
    926      * input path is never mistaken for the value. */
    927     if (driver_streq(a, "--image-header")) {
    928       o.image.image_header = KIT_IMAGE_HEADER_AUTO;
    929       continue;
    930     }
    931     if (driver_strneq(a, "--image-header=", 15)) {
    932       if (parse_image_header(a + 15, &o.image.image_header) != 0) {
    933         driver_errf(IMAGE_TOOL, "unknown image header: %s", a + 15);
    934         goto done;
    935       }
    936       continue;
    937     }
    938     matched = take_value(&i, argc, argv, "--image-text-offset", &val);
    939     if (matched < 0) goto missing_value;
    940     if (matched) {
    941       if (parse_size(val, &o.image.image_text_offset) != 0) {
    942         driver_errf(IMAGE_TOOL, "invalid --image-text-offset value: %s", val);
    943         goto done;
    944       }
    945       o.image.have_image_text_offset = true;
    946       continue;
    947     }
    948     matched = take_value(&i, argc, argv, "--image-endian", &val);
    949     if (matched < 0) goto missing_value;
    950     if (matched) {
    951       if (driver_streq(val, "little") || driver_streq(val, "le")) {
    952         o.image.image_big_endian = false;
    953       } else if (driver_streq(val, "big") || driver_streq(val, "be")) {
    954         o.image.image_big_endian = true;
    955       } else {
    956         driver_errf(IMAGE_TOOL, "invalid --image-endian value: %s", val);
    957         goto done;
    958       }
    959       continue;
    960     }
    961     matched = take_value(&i, argc, argv, "--image-page-size", &val);
    962     if (matched < 0) goto missing_value;
    963     if (matched) {
    964       uint64_t bytes;
    965       if (parse_size(val, &bytes) != 0) {
    966         driver_errf(IMAGE_TOOL, "invalid --image-page-size value: %s", val);
    967         goto done;
    968       }
    969       if (bytes == 4096u)
    970         o.image.image_page_size_kib = 4;
    971       else if (bytes == 16384u)
    972         o.image.image_page_size_kib = 16;
    973       else if (bytes == 65536u)
    974         o.image.image_page_size_kib = 64;
    975       else {
    976         driver_errf(IMAGE_TOOL, "--image-page-size must be 4k, 16k, or 64k");
    977         goto done;
    978       }
    979       continue;
    980     }
    981     if (a[0] == '-' && a[1] != '\0') {
    982       const char* const options[] = {
    983           "-o",                "--output",          "--format",
    984           "--from",            "--segment",         "--only-section",
    985           "--section",         "--remove-section",  "--addr",
    986           "--base",            "--bias",            "--fill",
    987           "--pad-to",          "--max-size",        "--metadata",
    988           "--require-entry",   "--require-symbol",  "--require-section",
    989           "--no-dynamic",      "--image-header",    "--image-load-offset",
    990           "--image-page-size", "--split-debug",     "--keep-symbols",
    991           "--strip-debug",     "-h",                "--help",
    992           "--version",
    993       };
    994       DriverSuggestion suggestions[3];
    995       size_t n = driver_suggest_values(a, options,
    996                                        sizeof options / sizeof options[0],
    997                                        suggestions, 3);
    998       if (n)
    999         driver_errf(IMAGE_TOOL, "unknown option: %s; did you mean '%s'?", a,
   1000                     suggestions[0].value);
   1001       else
   1002         driver_errf(IMAGE_TOOL, "unknown option: %s", a);
   1003       goto done;
   1004     }
   1005     if (o.input) {
   1006       driver_errf(IMAGE_TOOL, "only one input may be given");
   1007       goto done;
   1008     }
   1009     o.input = a;
   1010   }
   1011 
   1012   if (!o.input) {
   1013     driver_errf(IMAGE_TOOL, "missing input file");
   1014     goto done;
   1015   }
   1016   if (o.split_debug || o.keep_symbols || o.strip_debug) {
   1017     if (o.image.format != KIT_IMAGE_FORMAT_ELF) {
   1018       driver_errf(IMAGE_TOOL,
   1019                   "--strip-debug/--split-debug/--keep-symbols are only "
   1020                   "meaningful with --format elf");
   1021       goto done;
   1022     }
   1023   }
   1024   if (o.image.image_header == KIT_IMAGE_HEADER_NONE &&
   1025       (o.image.have_image_text_offset || o.image.image_big_endian ||
   1026        o.image.image_page_size_kib)) {
   1027     driver_errf(IMAGE_TOOL,
   1028                 "--image-text-offset/--image-endian/--image-page-size require "
   1029                 "--image-header");
   1030     goto done;
   1031   }
   1032   if (o.image.have_base &&
   1033       (o.image.format == KIT_IMAGE_FORMAT_IHEX ||
   1034        o.image.format == KIT_IMAGE_FORMAT_SREC)) {
   1035     driver_errf(IMAGE_TOOL,
   1036                 "--base is only valid for flat output; use --bias to rebase "
   1037                 "IHEX/SREC addresses");
   1038     goto done;
   1039   }
   1040 
   1041   if (driver_load_bytes(&env.file_io, IMAGE_TOOL, o.input, &load, &input) != 0) {
   1042     rc = 1;
   1043     goto done;
   1044   }
   1045 
   1046   if (kit_obj_open(&ctx, kit_slice_cstr(o.input), &input, &of) != KIT_OK) {
   1047     driver_errf(IMAGE_TOOL, "%s is not a recognized object or linked image",
   1048                 o.input);
   1049     rc = 1;
   1050     goto done;
   1051   }
   1052 
   1053   if (o.output) {
   1054     if (ctx.file_io->open_writer(ctx.file_io->user, o.output, &out) != KIT_OK) {
   1055       driver_errf(IMAGE_TOOL, "failed to open output: %s", o.output);
   1056       rc = 1;
   1057       goto done;
   1058     }
   1059   } else {
   1060     out = driver_stdout_writer(&env);
   1061     if (!out) goto oom;
   1062   }
   1063   own_writer = 1;
   1064 
   1065   if (kit_image_emit(&ctx, of, &input, &o.image, out, &report) != KIT_OK) {
   1066     driver_writer_abort(out);
   1067     rc = 1;
   1068     goto done;
   1069   }
   1070   /* Close the image writer before writing the sidecar so the image bytes are
   1071    * flushed and the metadata reflects a complete output. */
   1072   if (own_writer && out) {
   1073     kit_writer_close(out);
   1074     out = NULL;
   1075     own_writer = 0;
   1076   }
   1077   rc = 0;
   1078 
   1079   if (o.metadata) {
   1080     if (write_metadata(&env, &ctx, of, &o, &report, o.metadata) != 0) {
   1081       rc = 1;
   1082       goto done;
   1083     }
   1084   }
   1085 
   1086 done:
   1087   if (own_writer && out) kit_writer_close(out);
   1088   if (of) kit_obj_free(of);
   1089   driver_release_bytes(&env.file_io, &load);
   1090   if (o.segments)
   1091     driver_free(&env, o.segments, (size_t)o.cap_segments * sizeof(*o.segments));
   1092   if (o.only_sections)
   1093     driver_free(&env, o.only_sections,
   1094                 (size_t)o.cap_only * sizeof(*o.only_sections));
   1095   if (o.remove_sections)
   1096     driver_free(&env, o.remove_sections,
   1097                 (size_t)o.cap_remove * sizeof(*o.remove_sections));
   1098   if (o.section_order)
   1099     driver_free(&env, o.section_order,
   1100                 (size_t)o.cap_section_order * sizeof(*o.section_order));
   1101   if (o.require_symbols)
   1102     driver_free(&env, o.require_symbols,
   1103                 (size_t)o.cap_require_symbols * sizeof(*o.require_symbols));
   1104   if (o.require_sections)
   1105     driver_free(&env, o.require_sections,
   1106                 (size_t)o.cap_require_sections * sizeof(*o.require_sections));
   1107   driver_env_fini(&env);
   1108   return rc;
   1109 
   1110 missing_value:
   1111   driver_errf(IMAGE_TOOL, "%s requires a value", argv[i]);
   1112   goto done;
   1113 oom:
   1114   driver_errf(IMAGE_TOOL, "out of memory");
   1115   rc = 1;
   1116   goto done;
   1117 }