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 }