Skip to content

Intel HEX and S-record images ​

REA inventories Intel HEX (I8HEX, I16HEX, I32HEX) and Motorola S-record (S19, S28, S37) programming files through inspect-artifact / inspect_artifact, and materializes their decoded bytes through extract-artifact / extract_artifact. Decoding is static and needs no external tool.

sh
rea inspect-artifact ./firmware.hex --json
rea inspect-artifact ./firmware.s37 --detail summary --evidence_output evidence.json --json
rea extract-artifact ./firmware.hex --json

For MCP, open the file with open_binary, then call inspect_artifact. Summary delivery retains the complete Evidence; use parent_evidence_id with inspect_analysis_view to read selected regions and fields. extract_artifact returns a fresh output_root containing one raw file per region.

Recognition ​

A file is recognized only when its first line, starting at byte 0, is a fully valid record: correct start character, hex digits, byte count, checksum, known record type and fixed length. The file name does not matter. A byte-order mark, a leading blank line or an invalid first record leaves the file unrecognized, so it falls through to plain file classification. Recognition never searches for a record later in the file. The container formats are intel-hex and srec.

Regions and metadata ​

Each inventory entry is one address-contiguous region of decoded bytes, named 0x<start>-0x<end>.bin with an inclusive end address of eight lowercase hex digits, for example 0x08000000-0x0800001f.bin. Regions are listed in ascending address order. Out-of-order records are sorted into regions; gaps separate regions and are never filled.

Region format_metadata (hex-record-region) records start_address, end_address, length, record_count, and first_line/last_line: the 1-based line span of the contributing data records. compare_artifacts compares none of these fields: a region's name encodes its start and end addresses, its length follows from them, and record and line counts are layout.

Root format_metadata (hex-records) records:

FieldMeaning
encoding, variantintel-hex or srec; the variant derived from the record types seen, or mixed
line_count, record_count, data_record_countLines read, non-blank records, and data records
data_bytes, duplicate_bytes, region_countUnique decoded bytes, bytes rewritten with identical values, and regions
lowest_address, highest_addressAddress bounds of the decoded bytes, or null without data
terminationWhether Intel 01 or S-record S7/S8/S9 was present
start_address, start_address_recordThe Intel 05 EIP or the S7/S8/S9 address, with its record type
start_segment_cs, start_segment_ipThe Intel 03 CS:IP pair
header_hex, header_textS-record S0 data as hex, and as text only when every byte is printable ASCII
declared_data_record_countThe last S5/S6 count, validated against the data records before it

variant is null for an S-record file in which no data or termination record establishes an address width. Start addresses are declared facts, not proven entry points.

Facts, limitations and failures ​

SituationOutcome
Gaps between regionsSeparate regions; never zero-filled
Out-of-order recordsSorted into regions
An address written twice with the same valueAccepted and counted in duplicate_bytes
An address written twice with different valuesFails, naming the address and the line spans of both writes
Checksum mismatch, odd hex length, non-hex character, byte count disagreeing with the lineFails with the line, declared and computed values, and record type
Line longer than 521 (Intel) or 514 (S-record) characters, excluding the line endingFails before the line is decoded
Intel type 06 or higher, S4, wrong fixed length for Intel 01–05 or S5–S9Fails
Missing Intel 01 or S7–S9Regions are returned; coverage is unknown and the file may be truncated
A non-blank record after termination, or a second S0Fails; blank lines are allowed anywhere
S5/S6 count different from the data records before itFails
Termination type not matching the data address widthReported as a limitation; the declared fact is kept
Conflicting repeated 03/05 start recordsFails; identical repeats are allowed
An Intel data record whose 16-bit offset wraps past 0xFFFFFails: producers disagree on whether the address wraps or continues linearly
I16HEX data above 0xFFFFF, or data crossing the S1/S2/S3 address widthFails

Lowercase hex, LF and CRLF line endings, and trailing spaces or tabs are accepted. A CR not followed by LF is an invalid character. Failures apply to the whole file and return no partial regions.

Limits ​

The file must be at most 128 MiB. Every data byte needs two hex digits, so the decoder reserves at most half the file size for decoded bytes before reading, and stores only written addresses. The decoder retains at most 65,536 address-discontiguous record runs before merging them into regions. A run is a sequence of consecutive records whose addresses continue each other. Both are fixed resource bounds, not format limits, and fail as limits (file-size and capacity) with no runtime override. Configurable ceilings are tracked in #6176. The line-length limits are the one-byte record count: Intel HEX allows 255 data bytes, while S-record counts include address and checksum bytes.

Decoded regions are cached only for the reader's lifetime. Each open checks its admitted carrier identity again. Region streams copy at most 64 KiB per chunk, so consumers cannot mutate cached bytes and opening a large region does not copy it in full. Caller cancellation keeps its exact reason, and closing the reader waits for active region streams to close.

Analyzing a region ​

Inventory does not establish the CPU, byte order or memory map, or that a declared start address is executable. To disassemble a region, extract it and open the region file with open_binary using format raw-binary, an architecture you choose, and entry_point set to the region start address; raw-binary maps file offset 0 at entry_point. Each region is opened separately, so references between regions are not resolved. A caller-selected Ghidra language and base address for raw-binary imports is tracked in #4246; Thumb-only Cortex-M images may be refused or decoded in ARM state until #5744 is resolved.

Verification ​

Fixtures with computed checksums cover supported record types, checksums, termination, address gaps and overlaps, resource refusals, inventory, extraction and reader lifecycle. CLI and stdio MCP tests exercise the same artifact provider and advertised schemas, including retained Evidence views.

GNU objcopy 2.40 output was also checked on Linux: the system true ELF was converted to Intel HEX and S-record, with additional address-rebased I32HEX and S37 cases. Each yielded 13 regions. A 19,124,817-byte Intel HEX conversion of the system Python 3.11 ELF yielded 11 regions containing 6,799,261 decoded bytes. Public CLI inspection and extraction matched GNU BFD address ranges and independently exported section bytes. Adjacent GNU sections were joined without filling address gaps. These are real producer outputs from system applications, not vendor-firmware or target-execution verification; srec_cat and other producers remain unverified. No general speedup is claimed.

Released under the MIT License.