kit

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

commit 522bdeee2d1c349d329a6d6ed5f5ad7a1f649b1f
parent f46d3c17738e810ae2964e5691842c90c19ce200
Author: Ryan Sepassi <rsepassi@gmail.com>
Date:   Thu, 16 Jul 2026 10:32:23 -0700

image: finish relocatable conversion and rebasing

Diffstat:
Mdoc/RELEASE_AUDIT_2026_6_0.md | 4++--
Mdriver/cmd/image.c | 108++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------------------
Mdriver/cmd/objcopy.c | 66++++++++++++++++++++++++++++++++++++++++++++++++++++++------------
Msrc/api/image.c | 2+-
Msrc/obj/image.c | 116+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------------
Mtest/tools/run.sh | 154+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--
6 files changed, 383 insertions(+), 67 deletions(-)

diff --git a/doc/RELEASE_AUDIT_2026_6_0.md b/doc/RELEASE_AUDIT_2026_6_0.md @@ -501,8 +501,8 @@ Implementation checkpoint (updated 2026-07-16): | KIT-P1-015 | **Complete** | 21 multi-source dependency assertions pass in the driver suite | | KIT-P2-003 | **Complete** | `test-driver-diagnostics`: 15 pass, 0 fail | | KIT-P1-007 | **Complete** | `test-driver-strip`: object API 26/0, relocatable/archive 5/0, linked ELF/Mach-O/PE matrix 91/0 | -| KIT-P1-017 | In progress | implementation under focused CLI audit | -| KIT-P2-002 | In progress | implementation under focused CLI audit | +| KIT-P1-017 | **Complete** | relocatable/linked ELF, Mach-O, COFF, and Wasm image cases included in `test-driver-tools`: 251 pass, 0 fail | +| KIT-P2-002 | **Complete** | 32-bit boundary/overflow, negative bias, entry records, metadata, and transactional cases included in `test-driver-tools`: 251 pass, 0 fail | | KIT-P3-003 | **Complete** | `test-link-macho-symbols` passes aarch64 and x86-64 Kit/platform oracles | | KIT-P1-016 | In progress | implementation under focused CLI audit | | KIT-P2-008 | In progress | implementation under focused CLI audit | diff --git a/driver/cmd/image.c b/driver/cmd/image.c @@ -38,23 +38,21 @@ void driver_help_image(void) { driver_printf( "%.*s", KIT_SLICE_ARG(KIT_SLICE_LIT( - "kit image — emit flat load images from linked objects\n" + "kit image — emit flat load images from objects and linked images\n" "\n" "USAGE\n" " kit image [OPTIONS] INPUT [-o OUTPUT]\n" "\n" "FORMAT\n" - " --format bin flat binary from loadable segments " + " --format bin flat binary from loadable ranges " "(default)\n" " --format rom fixed-size flat binary (requires " "--pad-to)\n" " --format sections concatenate --section bytes in order\n" - " --format ihex Intel HEX records from loadable " - "segments\n" - " --format srec Motorola S-records from loadable " - "segments\n" - " --from segments derive bytes from loadable segments " - "(default)\n" + " --format ihex Intel HEX records from loadable ranges\n" + " --format srec Motorola S-records from loadable ranges\n" + " --from segments linked segments; relocatables use " + "allocated sections (default)\n" " --from sections derive bytes from named sections\n" "\n" "SELECTION\n" @@ -109,10 +107,9 @@ void driver_help_image(void) { "TEXT-FORMAT ADDRESS LIMITS\n" " IHEX/SREC records require adjusted addresses that fit their\n" " 32-bit range. Native linked images can start above that range.\n" - " --base changes flat-output placement; it does not rebase IHEX or\n" - " SREC record addresses. Use an explicit signed --bias only when\n" - " every adjusted range is representable. A bias that makes IHEX\n" - " work may still be rejected for SREC in this release.\n" + " --base changes flat-output placement and is rejected for IHEX/\n" + " SREC. Use an explicit signed --bias to adjust every record and\n" + " entry address; every adjusted address must fit in 32 bits.\n" "\n" "GETTING HELP\n" " -h, --help Show this help and exit\n" @@ -478,11 +475,38 @@ static KitSlice read_build_id(KitObjFile* of) { * path it is the loadable segments the layout selected — mirroring the * emitter's default-ELF-PT_LOAD / explicit-list policy plus only/remove * filters, so the sidecar reflects exactly what the image contains. */ +static int image_name_in_list(KitSlice name, const KitSlice* list, uint32_t n) { + uint32_t i; + for (i = 0; i < n; ++i) { + if (name.len == list[i].len && + (name.len == 0 || memcmp(name.s, list[i].s, name.len) == 0)) + return 1; + } + return 0; +} + +static int meta_section_selected(const KitObjSecInfo* sec, + const KitImageOptions* opts) { + if (!(sec->flags & KIT_SF_ALLOC) || sec->kind == KIT_SEC_BSS || + sec->size == 0) + return 0; + if (opts->nonly_sections && + !image_name_in_list(sec->name, opts->only_sections, + opts->nonly_sections)) + return 0; + if (opts->nremove_sections && + image_name_in_list(sec->name, opts->remove_sections, + opts->nremove_sections)) + return 0; + return 1; +} + static void meta_put_selection(MetaBuf* mb, KitObjFile* of, - const ImageCliOpts* o, int from_sections) { + const ImageCliOpts* o, + const KitImageReport* report) { uint32_t i; int first = 1; - if (from_sections) { + if (report->sections_concat) { for (i = 0; i < o->image.nsection_order; ++i) { if (!first) meta_putc(mb, ','); first = 0; @@ -490,6 +514,19 @@ static void meta_put_selection(MetaBuf* mb, KitObjFile* of, } return; } + if (report->section_ranges) { + uint32_t nsections = kit_obj_nsections(of); + for (i = 0; i < nsections; ++i) { + KitObjSecInfo sec; + if (kit_obj_section(of, i, &sec) != KIT_OK || + !meta_section_selected(&sec, &o->image)) + continue; + if (!first) meta_putc(mb, ','); + first = 0; + meta_put_json_string(mb, sec.name); + } + return; + } { KitObjSegIter* it = NULL; KitObjSegInfo seg; @@ -534,12 +571,10 @@ static int write_metadata(DriverEnv* env, const KitContext* ctx, KitObjFile* of, const ImageCliOpts* o, const KitImageReport* report, const char* path) { MetaBuf mb; - KitObjImageInfo info; KitTargetSpec spec; char triple[128]; KitSlice build_id; int from_sections; - int have_info; KitWriter* w = NULL; int rc = 0; @@ -549,15 +584,8 @@ static int write_metadata(DriverEnv* env, const KitContext* ctx, KitObjFile* of, spec = kit_obj_target(of); if (driver_target_to_triple(spec, triple, sizeof triple) != 0) triple[0] = '\0'; - have_info = (kit_obj_image_info(of, &info) == KIT_OK); build_id = read_build_id(of); - /* Mirror obj_emit_image's source selection so the sidecar names the source - * that actually produced the bytes (rom draws from sections when --section - * is given). */ - from_sections = - (o->image.format == KIT_IMAGE_FORMAT_SECTIONS) || - (o->image.from == KIT_IMAGE_FROM_SECTIONS) || - (o->image.format == KIT_IMAGE_FORMAT_ROM && o->image.nsection_order > 0); + from_sections = report->sections_concat || report->section_ranges; /* Keys are emitted in a fixed order so the sidecar is byte-stable. */ meta_putf(&mb, "{\n"); @@ -568,9 +596,9 @@ static int write_metadata(DriverEnv* env, const KitContext* ctx, KitObjFile* of, meta_putf(&mb, " \"target\": \"%s\",\n", triple); meta_putf(&mb, " \"object_format\": \"%s\",\n", kit_obj_fmt_name(kit_obj_fmt(of))); - if (have_info && info.entry) + if (report->has_entry) meta_putf(&mb, " \"entry\": \"0x%llx\",\n", - (unsigned long long)info.entry); + (unsigned long long)report->emitted_entry); else meta_putf(&mb, " \"entry\": null,\n"); if (build_id.len) { @@ -584,7 +612,7 @@ static int write_metadata(DriverEnv* env, const KitContext* ctx, KitObjFile* of, } meta_putf(&mb, " \"selection\": ["); - meta_put_selection(&mb, of, o, from_sections); + meta_put_selection(&mb, of, o, report); meta_putf(&mb, "],\n"); meta_putf(&mb, " \"addr_kind\": \"%s\",\n", @@ -593,8 +621,21 @@ static int write_metadata(DriverEnv* env, const KitContext* ctx, KitObjFile* of, : "vaddr"); meta_putf(&mb, " \"base\": \"0x%llx\",\n", (unsigned long long)report->base); + meta_putf(&mb, " \"original_base\": \"0x%llx\",\n", + (unsigned long long)report->original_base); + meta_putf(&mb, " \"emitted_base\": \"0x%llx\",\n", + (unsigned long long)report->emitted_base); + if (report->has_entry) { + meta_putf(&mb, " \"original_entry\": \"0x%llx\",\n", + (unsigned long long)report->original_entry); + meta_putf(&mb, " \"emitted_entry\": \"0x%llx\",\n", + (unsigned long long)report->emitted_entry); + } else { + meta_putf(&mb, " \"original_entry\": null,\n"); + meta_putf(&mb, " \"emitted_entry\": null,\n"); + } meta_putf(&mb, " \"base_is_load_address\": %s,\n", - report->sections_concat ? "false" : "true"); + report->base_is_load_address ? "true" : "false"); meta_putf(&mb, " \"bias\": %lld,\n", (long long)o->image.bias); meta_putf(&mb, " \"fill\": \"0x%02x\",\n", (unsigned)o->image.fill); meta_putf(&mb, " \"payload_size\": %llu,\n", @@ -989,6 +1030,14 @@ int driver_image(int argc, char** argv) { "--image-header"); goto done; } + if (o.image.have_base && + (o.image.format == KIT_IMAGE_FORMAT_IHEX || + o.image.format == KIT_IMAGE_FORMAT_SREC)) { + driver_errf(IMAGE_TOOL, + "--base is only valid for flat output; use --bias to rebase " + "IHEX/SREC addresses"); + goto done; + } if (driver_load_bytes(&env.file_io, IMAGE_TOOL, o.input, &load, &input) != 0) { rc = 1; @@ -996,7 +1045,8 @@ int driver_image(int argc, char** argv) { } if (kit_obj_open(&ctx, kit_slice_cstr(o.input), &input, &of) != KIT_OK) { - driver_errf(IMAGE_TOOL, "%s is not a recognized linked object", o.input); + driver_errf(IMAGE_TOOL, "%s is not a recognized object or linked image", + o.input); rc = 1; goto done; } diff --git a/driver/cmd/objcopy.c b/driver/cmd/objcopy.c @@ -79,10 +79,10 @@ void driver_help_objcopy(void) { " names: elf*, mach-o / macho*, coff*, " "wasm*, binary, ihex, srec\n" "\n" - " The binary, ihex, and srec outputs currently require a linked\n" - " image; they reject relocatable .o inputs. For a flat image from a\n" - " linked executable, `kit image --format bin` provides explicit\n" - " segment/section selection and layout validation.\n" + " Binary, IHEX, and SREC use load segments from linked images and\n" + " allocated section contents from relocatable objects. Unapplied\n" + " relocations in selected sections are rejected. --only-section\n" + " and --remove-section select the ranges to emit.\n" "\n" "EXAMPLES\n" " kit objcopy --strip-debug input.o output.o\n" @@ -263,10 +263,10 @@ static int is_srec_fmt_name(const char* name) { return driver_streq(name, "srec") || driver_streq(name, "s19"); } -static int has_object_transforms(const CopyOpts* opts) { - return opts->op != COPY_OP_NONE || opts->nremove || opts->nonly || - opts->nrename_sec || opts->nadd || opts->nupdate || opts->nredef || - opts->nglob || opts->nloc || opts->nweak; +static int has_non_image_transforms(const CopyOpts* opts) { + return opts->op != COPY_OP_NONE || opts->nrename_sec || opts->nadd || + opts->nupdate || opts->nredef || opts->nglob || opts->nloc || + opts->nweak; } /* Lookup a symbol by name; KIT_OBJ_SYMBOL_NONE if not found. */ @@ -487,34 +487,76 @@ static int copy_one_object(DriverEnv* env, const KitContext* ctx, return rc; } +static KitSlice* copy_image_names(const KitContext* ctx, + const char* const* names, uint32_t n) { + KitSlice* slices; + uint32_t i; + size_t bytes; + if (n == 0) return NULL; + if ((size_t)n > SIZE_MAX / sizeof(*slices)) return NULL; + bytes = (size_t)n * sizeof(*slices); + slices = (KitSlice*)ctx->heap->alloc(ctx->heap, bytes, _Alignof(KitSlice)); + if (!slices) return NULL; + for (i = 0; i < n; ++i) slices[i] = kit_slice_cstr(names[i]); + return slices; +} + +static void free_image_names(const KitContext* ctx, KitSlice* names, + uint32_t n) { + if (names) + ctx->heap->free(ctx->heap, names, (size_t)n * sizeof(*names)); +} + static int copy_one_image(const KitContext* ctx, const CopyOpts* opts, const char* input_name, const KitSlice* input, const char* output_path) { KitImageOptions iopts; + KitSlice* only_sections = NULL; + KitSlice* remove_sections = NULL; KitWriter* w = NULL; KitStatus st; + int rc = 1; memset(&iopts, 0, sizeof iopts); iopts.format = opts->output_image_format; iopts.from = KIT_IMAGE_FROM_SEGMENTS; iopts.addr = KIT_IMAGE_ADDR_VADDR; iopts.fill = 0; + only_sections = copy_image_names(ctx, opts->only_sections, opts->nonly); + remove_sections = + copy_image_names(ctx, opts->remove_sections, opts->nremove); + if ((opts->nonly && !only_sections) || (opts->nremove && !remove_sections)) { + driver_errf(OBJCOPY_TOOL, "out of memory preparing section selection"); + goto done; + } + iopts.only_sections = only_sections; + iopts.nonly_sections = opts->nonly; + iopts.remove_sections = remove_sections; + iopts.nremove_sections = opts->nremove; if (ctx->file_io->open_writer(ctx->file_io->user, output_path, &w) != KIT_OK) { driver_errf(OBJCOPY_TOOL, "cannot open %.*s", KIT_SLICE_ARG(kit_slice_cstr(output_path))); - return 1; + goto done; } st = kit_image_emit_bytes(ctx, kit_slice_cstr(input_name), input, &iopts, w, NULL); if (st != KIT_OK || kit_writer_status(w) != KIT_OK) { driver_writer_abort(w); kit_writer_close(w); - return 1; + w = NULL; + goto done; } kit_writer_close(w); - return 0; + w = NULL; + rc = 0; + +done: + if (w) kit_writer_close(w); + free_image_names(ctx, only_sections, opts->nonly); + free_image_names(ctx, remove_sections, opts->nremove); + return rc; } int driver_objcopy(int argc, char** argv) { @@ -729,7 +771,7 @@ int driver_objcopy(int argc, char** argv) { } out_path = opts.output ? opts.output : opts.input; - if (opts.output_image && has_object_transforms(&opts)) { + if (opts.output_image && has_non_image_transforms(&opts)) { driver_errf(OBJCOPY_TOOL, "image output format cannot be combined with object transform " "options"); diff --git a/src/api/image.c b/src/api/image.c @@ -20,7 +20,7 @@ KitStatus kit_image_emit_bytes(const KitContext* ctx, KitSlice name, if (!ctx || !object_bytes || !out) return KIT_INVALID; st = kit_obj_open(ctx, name, object_bytes, &obj); if (st != KIT_OK) { - kit_ctx_diagf(ctx, "image: %.*s is not a recognized linked object", + kit_ctx_diagf(ctx, "image: %.*s is not a recognized object or linked image", KIT_SLICE_ARG(name)); return st; } diff --git a/src/obj/image.c b/src/obj/image.c @@ -477,7 +477,9 @@ static KitStatus collect_alloc_sections(const KitContext* ctx, size_t len = 0; uint64_t addr, end, mend; ImageRange r; - if (kit_obj_section(obj, i, &sec) != KIT_OK) { + st = kit_obj_section(obj, i, &sec); + if (st == KIT_NOT_FOUND) continue; + if (st != KIT_OK) { ranges_free(ctx, ranges, cap); return KIT_MALFORMED; } @@ -931,6 +933,32 @@ static KitStatus emit_ihex_eof_record(KitWriter* out) { return write_line(out, line, n); } +static KitStatus emit_ihex_start_record(KitWriter* out, uint32_t entry) { + uint8_t payload[4]; + uint32_t sum; + size_t i; + char line[32]; + size_t n = 0; + + payload[0] = (uint8_t)(entry >> 24); + payload[1] = (uint8_t)(entry >> 16); + payload[2] = (uint8_t)(entry >> 8); + payload[3] = (uint8_t)entry; + sum = 4u + 5u; + + line[n++] = ':'; + line_append_hex_u8(line, &n, 4u); + line_append_hex_u16(line, &n, 0u); + line_append_hex_u8(line, &n, 5u); + for (i = 0; i < sizeof payload; ++i) { + line_append_hex_u8(line, &n, payload[i]); + sum += payload[i]; + } + line_append_hex_u8(line, &n, (uint8_t)(~sum + 1u)); + line[n++] = '\n'; + return write_line(out, line, n); +} + static KitStatus emit_ihex_blob(const KitContext* ctx, KitWriter* out, uint64_t addr, const uint8_t* src, uint64_t len, uint8_t fill, int as_fill, @@ -999,6 +1027,10 @@ static KitStatus write_ihex_records(const KitContext* ctx, const ImageRange* ran &current_upper); if (st != KIT_OK) return st; } + if (report->has_entry) { + st = emit_ihex_start_record(out, (uint32_t)report->emitted_entry); + if (st != KIT_OK) return st; + } return emit_ihex_eof_record(out); } @@ -1108,7 +1140,7 @@ static uint32_t srec_start_addr_bytes(uint64_t addr) { static KitStatus write_srec_records(const KitContext* ctx, const ImageRange* ranges, uint32_t nranges, const KitImageOptions* opts, - const KitImageReport* report, uint64_t entry, + const KitImageReport* report, KitWriter* out) { const uint8_t header_name[] = "KIT"; uint64_t cur; @@ -1139,16 +1171,13 @@ static KitStatus write_srec_records(const KitContext* ctx, const ImageRange* ran st = emit_srec_blob(ctx, out, cur, NULL, pad, opts->fill, 1); if (st != KIT_OK) return st; } - if (entry > UINT32_MAX) { - kit_ctx_diagf(ctx, - "image: srec cannot emit above 32-bit addresses (got 0x%llx)", - (unsigned long long)entry); - return KIT_UNSUPPORTED; - } - if (emit_srec_data_record(out, srec_start_type(entry), - srec_start_addr_bytes(entry), entry, NULL, 0u) != - KIT_OK) { - return KIT_IO; + if (report->has_entry) { + uint64_t entry = report->emitted_entry; + if (emit_srec_data_record(out, srec_start_type(entry), + srec_start_addr_bytes(entry), entry, NULL, 0u) != + KIT_OK) { + return KIT_IO; + } } return KIT_OK; } @@ -1254,8 +1283,8 @@ KitStatus obj_emit_image(const KitContext* ctx, KitObjFile* obj, KitImageFormat format; KitStatus st; int from_sections; + int address_sections; uint64_t seg_mem_end = 0; - uint64_t srec_entry = 0; uint8_t image_hdr[64]; const uint8_t* image_hdr_p = NULL; @@ -1284,6 +1313,13 @@ KitStatus obj_emit_image(const KitContext* ctx, KitObjFile* obj, "/ `kit strip` to copy, normalize, or strip a linked ELF"); return KIT_UNSUPPORTED; } + if (opts->have_base && + (format == KIT_IMAGE_FORMAT_IHEX || format == KIT_IMAGE_FORMAT_SREC)) { + kit_ctx_diagf(ctx, + "image: --base is only valid for flat output; use --bias " + "to rebase ihex/srec addresses"); + return KIT_INVALID; + } if (opts->from != KIT_IMAGE_FROM_SEGMENTS && opts->from != KIT_IMAGE_FROM_SECTIONS) { kit_ctx_diagf(ctx, "image: invalid --from source"); @@ -1304,7 +1340,8 @@ KitStatus obj_emit_image(const KitContext* ctx, KitObjFile* obj, } /* Source selection. Five formats, two byte sources: - * bin/srec/ihex : load segments only. + * bin/srec/ihex : linked load segments, or allocated sections for a + * relocatable input / explicit section filters. * rom : load segments by default, or named sections when a * --section list (or --from sections) is supplied. * sections : named sections only (the format IS the concatenation). @@ -1354,16 +1391,51 @@ KitStatus obj_emit_image(const KitContext* ctx, KitObjFile* obj, st = validate_object(ctx, obj, opts); if (st != KIT_OK) return st; + address_sections = !from_sections && + (kit_obj_kind(obj) == KIT_OBJ_KIND_REL || + opts->nonly_sections || opts->nremove_sections); if (from_sections) { st = collect_sections(ctx, obj, opts, &ranges, &nranges, &cap, &seg_mem_end); + } else if (address_sections) { + st = collect_alloc_sections(ctx, obj, opts, &ranges, &nranges, &cap, + &seg_mem_end); } else { st = collect_ranges(ctx, obj, object_bytes, opts, &ranges, &nranges, &cap, &seg_mem_end); } + if (st == KIT_OK && !from_sections) { + ImageRange* normalized = NULL; + uint32_t nnormalized = 0, normalized_cap = 0; + st = normalize_overlaps(ctx, ranges, nranges, &normalized, &nnormalized, + &normalized_cap); + if (st == KIT_OK) { + ranges_free(ctx, ranges, cap); + ranges = normalized; + nranges = nnormalized; + cap = normalized_cap; + } + } if (st == KIT_OK) { + KitObjImageInfo info; memset(&report, 0, sizeof report); report.sections_concat = from_sections ? true : false; + report.section_ranges = address_sections ? true : false; + report.base_is_load_address = + (!from_sections && !address_sections) ? true : false; + if (!from_sections) report.original_base = ranges[0].original_addr; st = compute_layout(ctx, ranges, nranges, opts, seg_mem_end, &report); + if (st == KIT_OK) { + report.emitted_base = report.base; + if (kit_obj_kind(obj) != KIT_OBJ_KIND_REL && + kit_obj_image_info(obj, &info) == KIT_OK && info.entry != 0) { + report.has_entry = true; + report.original_entry = info.entry; + if (!u64_add_bias(info.entry, opts->bias, &report.emitted_entry)) { + kit_ctx_diagf(ctx, "image: entry address overflows after --bias"); + st = KIT_INVALID; + } + } + } } if (st == KIT_OK && opts->image_header != KIT_IMAGE_HEADER_NONE) { KitImageHeader kind; @@ -1378,20 +1450,24 @@ KitStatus obj_emit_image(const KitContext* ctx, KitObjFile* obj, if (st == KIT_OK) { if (format == KIT_IMAGE_FORMAT_IHEX) { st = check_32bit_text_image_range(ctx, &report, "ihex"); + if (st == KIT_OK && report.has_entry && + report.emitted_entry > UINT32_MAX) { + kit_ctx_diagf(ctx, + "image: ihex entry cannot emit above 32-bit addresses"); + st = KIT_UNSUPPORTED; + } if (st == KIT_OK) st = write_ihex_records(ctx, ranges, nranges, opts, &report, out); } else if (format == KIT_IMAGE_FORMAT_SREC) { - KitObjImageInfo info; - if (kit_obj_image_info(obj, &info) == KIT_OK) srec_entry = info.entry; st = check_32bit_text_image_range(ctx, &report, "srec"); - if (st == KIT_OK && srec_entry > UINT32_MAX) { + if (st == KIT_OK && report.has_entry && + report.emitted_entry > UINT32_MAX) { kit_ctx_diagf(ctx, - "image: srec cannot emit above 32-bit addresses"); + "image: srec entry cannot emit above 32-bit addresses"); st = KIT_UNSUPPORTED; } if (st == KIT_OK) - st = write_srec_records(ctx, ranges, nranges, opts, &report, srec_entry, - out); + st = write_srec_records(ctx, ranges, nranges, opts, &report, out); } else { st = write_layout(ctx, ranges, nranges, opts, &report, image_hdr_p, out); } diff --git a/test/tools/run.sh b/test/tools/run.sh @@ -494,6 +494,154 @@ run_fail image-ihex-over32 "$KIT" image --format ihex "$work/over32.elf" \ run_fail image-srec-over32 "$KIT" image --format srec "$work/over32.elf" \ -o "$work/over32.srec" +# A signed bias applies to records and the declared entry alike. Rebasing this +# image down by exactly 4 GiB makes both text formats representable; pin their +# entry records as well as the data-address path. +run_ok image-ihex-negative-bias "$KIT" image --format ihex \ + --bias -4294967296 --metadata "$work/biased.json" "$work/over32.elf" \ + -o "$work/biased.hex" +contains image-ihex-biased-ela "$work/biased.hex" ":020000040001F9" +contains image-ihex-biased-entry "$work/biased.hex" ":0400000500010000F6" +contains image-meta-original-base "$work/biased.json" \ + '"original_base": "0x100010000"' +contains image-meta-emitted-base "$work/biased.json" \ + '"emitted_base": "0x10000"' +contains image-meta-original-entry "$work/biased.json" \ + '"original_entry": "0x100010000"' +contains image-meta-emitted-entry "$work/biased.json" \ + '"emitted_entry": "0x10000"' +run_ok image-srec-negative-bias "$KIT" image --format srec \ + --bias -4294967296 "$work/over32.elf" -o "$work/biased.srec" +contains image-srec-biased-entry "$work/biased.srec" "S804010000FA" +expect_status 2 image-ihex-base-rejected "$KIT" image --format ihex \ + --base 0 "$work/highaddr.elf" -o "$work/base.hex" +contains image-ihex-base-hint "$work/image-ihex-base-rejected.err" \ + "rebase IHEX/SREC addresses" +run_fail image-negative-bias-underflow "$KIT" image --format ihex \ + --bias -9223372036854775808 "$work/kernel.elf" -o "$work/underflow.hex" +if [ ! -e "$work/underflow.hex" ]; then ok image-underflow-transaction; +else not_ok image-underflow-transaction "$work/underflow.hex"; fi + +# The top 32-bit byte is representable; a two-byte range at the same address +# crosses the boundary and must fail before an output transaction commits. +cat > "$work/boundary.s" <<'EOF' +.globl _start +.section .text +_start: + ret +EOF +cat > "$work/boundary.lds" <<'EOF' +ENTRY(_start) +SECTIONS { + . = 0xffffffff; + .text : ALIGN(1) { *(.text .text.*) } + /DISCARD/ : { *(.note.*) *(.comment) *(.eh_frame) } +} +EOF +run_ok image-text-boundary-build "$KIT" build-exe -target x86_64-none-elf \ + -nostdlib -static -no-pie -e _start -T "$work/boundary.lds" \ + "$work/boundary.s" -o "$work/boundary.elf" +run_ok image-ihex-boundary "$KIT" image --format ihex "$work/boundary.elf" \ + -o "$work/boundary.hex" +contains image-ihex-boundary-entry "$work/boundary.hex" \ + ":04000005FFFFFFFFFB" +run_ok image-srec-boundary "$KIT" image --format srec "$work/boundary.elf" \ + -o "$work/boundary.srec" +contains image-srec-boundary-entry "$work/boundary.srec" "S705FFFFFFFFFE" +cat > "$work/boundary-cross.s" <<'EOF' +.globl _start +.section .text +_start: + ret + nop +EOF +run_ok image-text-boundary-cross-build "$KIT" build-exe \ + -target x86_64-none-elf -nostdlib -static -no-pie -e _start \ + -T "$work/boundary.lds" "$work/boundary-cross.s" \ + -o "$work/boundary-cross.elf" +run_fail image-ihex-boundary-cross "$KIT" image --format ihex \ + "$work/boundary-cross.elf" -o "$work/boundary-cross.hex" +run_fail image-srec-boundary-cross "$KIT" image --format srec \ + "$work/boundary-cross.elf" -o "$work/boundary-cross.srec" +if [ ! -e "$work/boundary-cross.hex" ] && \ + [ ! -e "$work/boundary-cross.srec" ]; then + ok image-boundary-cross-transaction +else + not_ok image-boundary-cross-transaction +fi + +# Relocatable objects use allocated section contents. Sections retain their +# addresses, later selected overlaps win, filters apply before layout, and no +# synthetic entry record is invented. The same library path backs objcopy. +cat > "$work/raw-rel.s" <<'EOF' +.globl raw_entry +.section .text +raw_entry: + ret +.section .data + .byte 0x42 +EOF +run_ok image-rel-build "$KIT" as -target x86_64-none-elf \ + "$work/raw-rel.s" -o "$work/raw-rel.o" +run_ok objcopy-rel-binary "$KIT" objcopy -O binary "$work/raw-rel.o" \ + "$work/raw-rel.bin" +"$KIT" xxd -p "$work/raw-rel.bin" > "$work/raw-rel.hexbytes" +contains image-rel-overlap-later-wins "$work/raw-rel.hexbytes" "42" +run_ok objcopy-rel-only-text "$KIT" objcopy --only-section .text -O binary \ + "$work/raw-rel.o" "$work/raw-rel.text.bin" +"$KIT" xxd -p "$work/raw-rel.text.bin" > "$work/raw-rel.text.hexbytes" +contains image-rel-only-text-byte "$work/raw-rel.text.hexbytes" "c3" +run_ok objcopy-rel-remove-data "$KIT" objcopy --remove-section .data \ + -O binary "$work/raw-rel.o" "$work/raw-rel.nodata.bin" +same_file image-rel-remove-matches-only "$work/raw-rel.text.bin" \ + "$work/raw-rel.nodata.bin" +run_ok objcopy-rel-ihex "$KIT" objcopy -O ihex "$work/raw-rel.o" \ + "$work/raw-rel.ihex" +if ! grep -q '^:04000005' "$work/raw-rel.ihex"; then + ok image-rel-ihex-no-entry +else + not_ok image-rel-ihex-no-entry "$work/raw-rel.ihex" +fi +run_ok objcopy-rel-srec "$KIT" objcopy -O srec "$work/raw-rel.o" \ + "$work/raw-rel.srec" +if ! grep -q '^S[789]' "$work/raw-rel.srec"; then + ok image-rel-srec-no-entry +else + not_ok image-rel-srec-no-entry "$work/raw-rel.srec" +fi + +# Reject an unapplied relocation in a selected allocated section. +cat > "$work/raw-reloc.s" <<'EOF' +.globl raw_reloc +.section .text +raw_reloc: + call missing_target +EOF +run_ok image-reloc-build "$KIT" as -target x86_64-none-elf \ + "$work/raw-reloc.s" -o "$work/raw-reloc.o" +run_fail objcopy-reloc-rejected "$KIT" objcopy -O binary \ + "$work/raw-reloc.o" "$work/raw-reloc.bin" +if [ ! -e "$work/raw-reloc.bin" ]; then ok image-reloc-transaction; +else not_ok image-reloc-transaction "$work/raw-reloc.bin"; fi + +# Object-format conversions remain format-neutral at the image API boundary. +run_ok image-rel-macho-build "$KIT" objcopy -O mach-o "$work/raw-rel.o" \ + "$work/raw-rel.macho" +run_ok image-rel-macho "$KIT" objcopy --only-section __TEXT,__text -O binary \ + "$work/raw-rel.macho" "$work/raw-rel.macho.bin" +same_file image-rel-macho-bytes "$work/raw-rel.text.bin" \ + "$work/raw-rel.macho.bin" +run_ok image-rel-coff-build "$KIT" objcopy -O coff "$work/raw-rel.o" \ + "$work/raw-rel.coff" +run_ok image-rel-coff "$KIT" objcopy --only-section .text -O binary \ + "$work/raw-rel.coff" "$work/raw-rel.coff.bin" +same_file image-rel-coff-bytes "$work/raw-rel.text.bin" \ + "$work/raw-rel.coff.bin" +run_ok image-rel-wasm "$KIT" objcopy --only-section code -O binary \ + "$repo_root/test/objdump/wasm/cases/add.wasm" "$work/raw-rel.wasm.bin" +if [ -s "$work/raw-rel.wasm.bin" ]; then ok image-rel-wasm-nonempty; +else not_ok image-rel-wasm-nonempty "$work/raw-rel.wasm.bin"; fi + # ---- image: --format sections --------------------------------------------- # Concatenate named sections in DECLARED order. The fixture's .text is a lone # `ret` (0xc3) and .rodata is 0x11223344, so the concatenation and its reverse @@ -599,10 +747,10 @@ contains image-meta-seg-lowerload-selected "$work/meta.load.json" '"selection": run_ok image-meta-default-sel "$KIT" image --format bin \ --metadata "$work/meta.def.json" "$work/kernel.elf" -o "$work/sel.def.bin" contains image-meta-default-selected "$work/meta.def.json" '"selection": ["LOAD"' -# --remove-section drops it from BOTH the bytes and the reported selection; the -# removed name folds case the same way the emitter does. +# Explicit section filters switch to allocated-section ranges; an empty +# keep-set is a hard failure and cannot leave misleading metadata/output. run_fail image-meta-remove-all "$KIT" image --format bin \ - --remove-section pt_load --metadata "$work/meta.rm.json" "$work/kernel.elf" \ + --only-section .nope --metadata "$work/meta.rm.json" "$work/kernel.elf" \ -o "$work/sel.rm.bin" # ==== END kernel-FZ Bug 10 ===================================================