Spaces:
Running
Running
File size: 54,232 Bytes
8bc8368 1ac0749 e9a3040 1ac0749 e9a3040 1ac0749 e9a3040 1ac0749 e9a3040 1ac0749 e9a3040 1ac0749 e9a3040 1ac0749 e9a3040 1ac0749 e9a3040 1ac0749 e9a3040 1ac0749 e9a3040 1ac0749 e9a3040 1ac0749 e9a3040 1ac0749 e9a3040 8bc8368 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 | <!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Intel HEX & S-record Image Audit — free firmware image check</title>
<meta name="description" content="Paste an Intel HEX or Motorola S-record image and get all 17 checks: every checksum recomputed, every byte-count field, overlaps, gaps, page alignment and the entry point. Free, in the browser, nothing truncated.">
<link rel="canonical" href="https://getreadystack.com/tools/hex-image-audit">
<style>
:root{--ink:#14181d;--mut:#5b6672;--line:#e3e7ec;--bg:#fbfcfd;--err:#b3261e;--warn:#8a6100;--ok:#1a6c3f;--acc:#12507e}
*{box-sizing:border-box}
body{margin:0;background:var(--bg);color:var(--ink);font:16px/1.55 -apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Helvetica,Arial,sans-serif}
.wrap{max-width:980px;margin:0 auto;padding:32px 20px 72px}
h1{font-size:27px;line-height:1.25;margin:0 0 6px;letter-spacing:-.01em}
.lede{color:var(--mut);margin:0 0 22px;max-width:70ch}
.panel{background:#fff;border:1px solid var(--line);border-radius:10px;padding:18px}
label{display:block;font-size:13px;font-weight:600;letter-spacing:.02em;text-transform:uppercase;color:var(--mut);margin:0 0 6px}
textarea{width:100%;height:230px;font:13px/1.45 ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;padding:12px;border:1px solid var(--line);border-radius:8px;background:#fff;color:var(--ink);resize:vertical}
.row{display:flex;gap:14px;align-items:flex-end;flex-wrap:wrap;margin-top:14px}
select{font:15px inherit;padding:9px 10px;border:1px solid var(--line);border-radius:8px;background:#fff;color:var(--ink)}
button{font:600 15px inherit;padding:11px 20px;border:0;border-radius:8px;background:var(--ink);color:#fff;cursor:pointer}
button.ghost{background:#fff;color:var(--acc);border:1px solid var(--line);font-weight:500}
#out{margin-top:26px}
.tally{font:600 17px inherit;margin:0 0 12px}
.tally .e{color:var(--err)} .tally .w{color:var(--warn)} .tally .g{color:var(--ok)}
table{border-collapse:collapse;width:100%;font-size:14px}
th{text-align:left;font-size:12px;text-transform:uppercase;letter-spacing:.04em;color:var(--mut);border-bottom:1px solid var(--line);padding:0 10px 7px}
td{border-bottom:1px solid var(--line);padding:9px 10px;vertical-align:top}
td.ln{font:13px ui-monospace,Menlo,Consolas,monospace;color:var(--mut);white-space:nowrap}
td.id{font:13px ui-monospace,Menlo,Consolas,monospace;white-space:nowrap}
.sev{display:inline-block;min-width:52px;font:600 11px inherit;letter-spacing:.06em;padding:2px 7px;border-radius:4px;text-align:center}
.sev.error{background:#fdecea;color:var(--err)} .sev.warn{background:#fdf3e0;color:var(--warn)}
.next{margin-top:26px;border:1px solid var(--line);border-left:3px solid var(--acc);border-radius:8px;background:#fff;padding:16px 18px}
.next p{margin:0 0 10px;color:var(--mut)} .next b{color:var(--ink)}
a.buy{display:inline-block;background:var(--acc);color:#fff;text-decoration:none;font-weight:600;padding:10px 18px;border-radius:8px}
.fine{font-size:13px;color:var(--mut);margin:10px 0 0}
footer{margin-top:34px;font-size:13px;color:var(--mut)}
footer a{color:var(--acc)}
</style>
<script data-grow-initializer="">!(function(){window.growMe||((window.growMe=function(e){window.growMe._.push(e);}),(window.growMe._=[]));var e=document.createElement("script");(e.type="text/javascript"),(e.src="https://faves.grow.me/main.js"),(e.defer=!0),e.setAttribute("data-grow-faves-site-id","U2l0ZTphZjljMzgyMC0yMmU5LTRhYjgtYTIxNS03OTRhMGY1N2ExYzM=");var t=document.getElementsByTagName("script")[0];t.parentNode.insertBefore(e,t);})();</script>
</head>
<body>
<div class="wrap">
<h1>Intel HEX & S-record Image Audit</h1>
<p class="lede">Paste a <code>.hex</code>, <code>.ihex</code>, <code>.s19</code>, <code>.s28</code>, <code>.s37</code>, <code>.srec</code> or <code>.mot</code> image. All <b>17 checks</b> run here in your browser — the same rules the VS Code extension runs, nothing truncated, nothing uploaded. Your image never leaves this page.</p>
<div class="panel">
<label for="img">Firmware image</label>
<textarea id="img" spellcheck="false" placeholder=":10000000... S1130000..."></textarea>
<div class="row">
<div>
<label for="page">Flash page size</label>
<select id="page">
<option value="256">256 bytes</option>
<option value="64">64 bytes</option>
<option value="128">128 bytes</option>
<option value="512">512 bytes</option>
<option value="1024">1024 bytes</option>
<option value="2048">2048 bytes</option>
</select>
</div>
<button id="run" type="button">Audit this image</button>
<button id="demo" type="button" class="ghost">Load a broken sample</button>
</div>
</div>
<div id="out"></div>
<div class="next">
<p><b>One image is free, forever, all 17 checks.</b> The licence covers the next job: sweep <b>every image in the build folder</b> in one pass and keep the dated Markdown audit report as a file you own — per-image release evidence for the technical documentation file. $29 once, one licence key per person or CI seat. A freelance firmware consultant averages $105 an hour.</p>
<a class="buy" href="https://getreadystack.com/api/buy/cl/polar_cl_AkmNU5yLwVJBdRMHSvuTCQI90j52v56xJsHZH4OTDjV">Get the full version — sweep the whole build folder, $29</a>
<p class="fine">Or install the free extension for VS Code and audit any open image at the line: <a href="https://marketplace.visualstudio.com/search?term=hex-image-audit">Intel HEX & S-record Image Audit</a>.</p>
</div>
<footer>17 checks · Intel HEX record types 00–05 · S-record types S0–S9 with 16-, 24- and 32-bit addresses. Under the EU Cyber Resilience Act, Article 13(12), technical documentation is kept at the disposal of market surveillance authorities for 10 years after the product is placed on the market or for the support period, whichever is longer; the reporting obligations have been in force since 11 September 2026.</footer>
</div>
<script>window.HEX_RULES = [
{"id": "record_start", "sev": "error", "title": "Line is not a record", "why": "Every Intel HEX line must begin with ':' and every Motorola line with 'S'. Anything else is a paste artefact, a diff marker or a stray editor line, and most programmers stop at it."},
{"id": "record_hex", "sev": "error", "title": "Non-hex or odd-length record", "why": "A record is whole bytes of uppercase-or-lowercase hex. An odd digit count or a stray character means a byte was dropped or split when the file was edited or generated."},
{"id": "record_length", "sev": "error", "title": "Byte-count field disagrees with the data", "why": "The LL field states how many data bytes follow. When a patch adds or removes bytes without fixing LL, loaders read past the end of the record or truncate it silently."},
{"id": "record_checksum", "sev": "error", "title": "Intel HEX checksum is wrong", "why": "The last byte is the two's complement of the sum of every byte before it. Hand-edited and machine-patched records almost always leave the old checksum behind; strict programmers abort, lax ones flash the record anyway."},
{"id": "record_type", "sev": "error", "title": "Unknown Intel HEX record type", "why": "Only types 00-05 are defined. An unknown type is a corrupted address field or a file from a different format, and behaviour is undefined across toolchains."},
{"id": "eof_record", "sev": "error", "title": "Missing or malformed end-of-file record", "why": "An Intel HEX image must end with :00000001FF. Without it a programmer cannot tell a complete image from a truncated download, and some simply hang waiting for more."},
{"id": "data_after_eof", "sev": "error", "title": "Records after the end-of-file record", "why": "Everything after the EOF record is ignored by conforming loaders. Appending a patch to the end of a .hex file is the classic way to ship a fix that never reaches the device."},
{"id": "address_overlap", "sev": "error", "title": "Two records write the same address", "why": "When two data records cover the same absolute byte, the flashed value depends on the load order of the tool. Merged images and appended patches are where this comes from."},
{"id": "extended_address", "sev": "error", "title": "Address wraps past 64 KiB with no extended-address record", "why": "Type 00 records carry only 16 address bits. Past 0xFFFF an image needs a type 04 or type 02 record; without one the data lands back at the bottom of memory."},
{"id": "srec_checksum", "sev": "error", "title": "S-record checksum is wrong", "why": "An S-record checksum is 0xFF minus the sum of the count, address and data bytes. A wrong one means the line was edited after generation."},
{"id": "srec_count", "sev": "error", "title": "S-record count field or terminator is wrong", "why": "The count byte covers address + data + checksum, and an S-record image must end with S7, S8 or S9. A wrong count shifts the checksum byte; a missing terminator leaves the entry point undefined."},
{"id": "srec_header", "sev": "warn", "title": "S-record image carries no identifiable S0 header", "why": "The S0 header record is where an S-record image states what it is: a module name (char[20]), a version and a revision inside its data field (Motorola S-record format, srec(5)). The spec makes S0 optional and plenty of toolchains emit it blank, which is harmless on the bench and expensive in the archive - an image kept as evidence cannot be tied back to the release it belongs to. Under the EU Cyber Resilience Act, Article 13(12), a manufacturer keeps the technical documentation at the disposal of market surveillance authorities for 10 years after the product is placed on the market or for the support period, whichever is longer, and the reporting obligations have been in force since 11 September 2026."},
{"id": "srec_tally", "sev": "warn", "title": "S5/S6 record tally does not match", "why": "S5 and S6 declare how many data records the file contains. A tally that no longer matches is proof the file was cut, merged or filtered after it was built."},
{"id": "address_gap", "sev": "warn", "title": "Unwritten gap inside the image", "why": "A gap means those cells keep whatever was in flash before. If the gap is not deliberate erased space it is a section the linker placed and the image dropped."},
{"id": "page_alignment", "sev": "warn", "title": "Block does not sit on a flash page boundary", "why": "Page-programmed flash writes whole pages. A block that starts or ends mid-page forces a read-modify-write, and on parts without it the rest of the page is erased."},
{"id": "start_address", "sev": "warn", "title": "Start address is outside the written image", "why": "A type 03/05 or S7/S8/S9 record names the entry point. If it points at an address the image never writes, the device jumps into unprogrammed flash."},
{"id": "case_consistency", "sev": "warn", "title": "Mixed-case hex digits", "why": "The format allows either case, but a file that switches case mid-way is a file that two different tools have written, which is worth knowing before you trust its checksums."}
];</script>
<script>
'use strict';
// Intel HEX / Motorola S-record image audit. Same file runs in Node (extension) and in the browser (free web tool).
var RULES = (typeof module !== 'undefined') ? require('./rules.json') : window.HEX_RULES;
var BY_ID = {};
for (var _i = 0; _i < RULES.length; _i++) { BY_ID[RULES[_i].id] = RULES[_i]; }
var SREC_ADDR_LEN = { '0': 2, '1': 2, '2': 3, '3': 4, '5': 2, '6': 3, '7': 4, '8': 3, '9': 2 };
function hh(n) { var s = n.toString(16).toUpperCase(); return '0x' + (s.length < 2 ? '0' + s : s); }
function ha(n) { var s = n.toString(16).toUpperCase(); while (s.length < 8) { s = '0' + s; } return '0x' + s; }
function check(text, opts) {
opts = opts || {};
var page = Number(opts.page) > 0 ? Number(opts.page) : 256;
var findings = [];
function add(id, line, msg) {
var r = BY_ID[id];
findings.push({ check: id, sev: r ? r.sev : 'error', msg: msg, line: line });
}
var lines = String(text == null ? '' : text).split(/\r?\n/);
var blocks = []; // {start, end, line}
var written = {}; // absolute address -> line, for overlap
var overlapBudget = 262144;
var overlapReported = {};
var ela = 0, esa = 0;
var eofLine = 0, seenIntel = false, seenSrec = false, srecTermLine = 0;
var dataRecords = 0, tally = null, tallyLine = 0;
var srecHeaderLine = 0, srecHeaderName = '';
var startAddr = null, startLine = 0;
var fileCase = '', caseFlagged = false;
var lastLine = 1;
function noteCase(body, ln) {
if (caseFlagged) { return; }
var hasU = /[A-F]/.test(body), hasL = /[a-f]/.test(body);
if (hasU && hasL) { caseFlagged = true; add('case_consistency', ln, 'this record mixes upper- and lower-case hex digits'); return; }
var c = hasU ? 'upper' : (hasL ? 'lower' : '');
if (!c) { return; }
if (!fileCase) { fileCase = c; return; }
if (fileCase !== c) {
caseFlagged = true;
add('case_consistency', ln, 'the image starts in ' + fileCase + '-case hex and switches to ' + c + '-case here, so two different tools have written this file');
}
}
function claim(start, len, ln) {
blocks.push({ start: start, end: start + len, line: ln });
if (len > overlapBudget) { overlapBudget = 0; return; }
for (var a = start; a < start + len; a++) {
if (overlapBudget <= 0) { return; }
overlapBudget--;
if (written[a] !== undefined) {
var key = written[a] + '>' + ln;
if (!overlapReported[key]) {
overlapReported[key] = 1;
add('address_overlap', ln, 'address ' + ha(a) + ' is already written by the record on line ' + written[a] + ', so the flashed byte depends on the load order of the tool');
}
} else {
written[a] = ln;
}
}
}
for (var i = 0; i < lines.length; i++) {
var ln = i + 1;
var s = lines[i].trim();
if (!s) { continue; }
lastLine = ln;
if (s.charAt(0) === ':') {
seenIntel = true;
var body = s.slice(1);
if (!/^[0-9A-Fa-f]+$/.test(body)) {
add('record_hex', ln, 'the record contains characters that are not hex digits');
continue;
}
if (body.length % 2 !== 0) {
add('record_hex', ln, 'the record holds ' + body.length + ' hex digits, which is not a whole number of bytes');
continue;
}
if (body.length < 10) {
add('record_hex', ln, 'the record is ' + body.length + ' hex digits long; the shortest legal record is 10 (count, address, type, checksum)');
continue;
}
noteCase(body, ln);
var b = [];
for (var j = 0; j < body.length; j += 2) { b.push(parseInt(body.substr(j, 2), 16)); }
var ll = b[0], addr = (b[1] << 8) | b[2], tt = b[3], dataLen = b.length - 5;
var sum = 0;
for (var k = 0; k < b.length - 1; k++) { sum += b[k]; }
var want = (0x100 - (sum & 0xFF)) & 0xFF, got = b[b.length - 1];
if (want !== got) {
add('record_checksum', ln, 'the record ends in ' + hh(got) + ' but the recomputed two\u2019s-complement checksum is ' + hh(want));
}
if (ll !== dataLen) {
add('record_length', ln, 'the byte-count field says ' + ll + ' but the record carries ' + dataLen + ' data byte' + (dataLen === 1 ? '' : 's'));
}
if (tt > 5) {
add('record_type', ln, 'record type ' + hh(tt) + ' is not one of the defined types 00-05');
continue;
}
if (eofLine) {
add('data_after_eof', ln, 'this record sits after the end-of-file record on line ' + eofLine + ', so conforming loaders never read it');
}
if (tt === 0) {
dataRecords++;
if (addr + dataLen > 0x10000) {
add('extended_address', ln, 'the record starts at ' + hh(b[1]) + hh(b[2]).slice(2) + ' and runs ' + dataLen + ' bytes past 0xFFFF; a type 04 record must raise the base address first');
}
claim(ela + esa + addr, dataLen, ln);
} else if (tt === 1) {
if (dataLen !== 0 || ll !== 0 || addr !== 0) {
add('eof_record', ln, 'the end-of-file record must be exactly :00000001FF, but this one declares address ' + hh(b[1]) + hh(b[2]).slice(2) + ' and ' + dataLen + ' data byte' + (dataLen === 1 ? '' : 's'));
}
if (!eofLine) { eofLine = ln; }
} else if (tt === 2 || tt === 4) {
if (dataLen !== 2) {
add('extended_address', ln, 'a type ' + hh(tt).slice(2) + ' extended-address record must carry exactly 2 data bytes, not ' + dataLen);
} else if (tt === 4) {
ela = ((b[4] << 8) | b[5]) * 65536; esa = 0;
} else {
esa = ((b[4] << 8) | b[5]) * 16; ela = 0;
}
} else if (tt === 3 || tt === 5) {
if (dataLen !== 4) {
add('start_address', ln, 'a type ' + hh(tt).slice(2) + ' start-address record must carry exactly 4 data bytes, not ' + dataLen);
} else {
startAddr = (tt === 5)
? (((b[4] << 24) >>> 0) + (b[5] << 16) + (b[6] << 8) + b[7])
: ((((b[4] << 8) | b[5]) * 16) + ((b[6] << 8) | b[7]));
startLine = ln;
}
}
continue;
}
if (/^[Ss][0-9]/.test(s)) {
seenSrec = true;
var t = s.charAt(1);
var sbody = s.slice(2);
if (!/^[0-9A-Fa-f]+$/.test(sbody) || sbody.length % 2 !== 0) {
add('record_hex', ln, 'the S-record payload is not a whole number of hex bytes');
continue;
}
noteCase(sbody, ln);
var sb = [];
for (var m = 0; m < sbody.length; m += 2) { sb.push(parseInt(sbody.substr(m, 2), 16)); }
if (sb.length < 3) { add('record_hex', ln, 'the S-record is too short to hold a count, an address and a checksum'); continue; }
var cnt = sb[0];
if (cnt !== sb.length - 1) {
add('srec_count', ln, 'the count byte says ' + cnt + ' but ' + (sb.length - 1) + ' byte' + (sb.length - 1 === 1 ? '' : 's') + ' follow it, which shifts the checksum byte');
}
var ssum = 0;
for (var n = 0; n < sb.length - 1; n++) { ssum += sb[n]; }
var swant = (0xFF - (ssum & 0xFF)) & 0xFF, sgot = sb[sb.length - 1];
if (swant !== sgot) {
add('srec_checksum', ln, 'the record ends in ' + hh(sgot) + ' but the recomputed S-record checksum is ' + hh(swant));
}
var alen = SREC_ADDR_LEN[t];
if (alen === undefined) { add('record_type', ln, 'S' + t + ' is not a defined S-record type'); continue; }
var sa = 0;
for (var p = 0; p < alen && 1 + p < sb.length; p++) { sa = (sa * 256) + sb[1 + p]; }
if (t === '1' || t === '2' || t === '3') {
dataRecords++;
var sdl = sb.length - 1 - alen - 1;
if (sdl > 0) { claim(sa, sdl, ln); }
} else if (t === '0') {
srecHeaderLine = ln;
var hd = sb.slice(1 + alen, sb.length - 1);
var htxt = '';
for (var h = 0; h < hd.length; h++) { htxt += (hd[h] >= 0x20 && hd[h] < 0x7F) ? String.fromCharCode(hd[h]) : ' '; }
srecHeaderName = htxt.replace(/\s+/g, ' ').trim();
} else if (t === '5' || t === '6') {
tally = sa; tallyLine = ln;
} else if (t === '7' || t === '8' || t === '9') {
srecTermLine = ln; startAddr = sa; startLine = ln;
}
continue;
}
add('record_start', ln, 'this line begins with \u201c' + s.slice(0, 12) + '\u201d, which is neither an Intel HEX record (\u201c:\u201d) nor an S-record (\u201cS\u201d)');
}
if (seenIntel && !eofLine) {
add('eof_record', lastLine, 'the image has no :00000001FF end-of-file record, so a programmer cannot tell it apart from a truncated download');
}
if (seenSrec && !srecTermLine) {
add('srec_count', lastLine, 'the S-record image has no S7, S8 or S9 termination record, so the entry point is undefined');
}
if (seenSrec && !srecHeaderLine) {
add('srec_header', lastLine, 'the image has no S0 header record, so the archived file states no module name, version or revision \u2014 nothing ties it back to the release it belongs to');
} else if (seenSrec && !srecHeaderName) {
add('srec_header', srecHeaderLine, 'the S0 header record is present but its data field carries no printable module name, so the archived image is still unidentifiable');
}
if (tally !== null && tally !== dataRecords) {
add('srec_tally', tallyLine, 'the S5/S6 record declares ' + tally + ' data record' + (tally === 1 ? '' : 's') + ' but the file contains ' + dataRecords);
}
var merged = [];
blocks.slice().sort(function (x, y) { return x.start - y.start; }).forEach(function (bl) {
var last = merged[merged.length - 1];
if (last && bl.start <= last.end) { last.end = Math.max(last.end, bl.end); } else { merged.push({ start: bl.start, end: bl.end, line: bl.line }); }
});
for (var g = 1; g < merged.length; g++) {
var gap = merged[g].start - merged[g - 1].end;
if (gap > 0) {
add('address_gap', merged[g].line, 'nothing is written between ' + ha(merged[g - 1].end) + ' and ' + ha(merged[g].start) + ' \u2014 a ' + gap + '-byte gap that keeps whatever was in flash before');
}
}
for (var q = 0; q < merged.length; q++) {
var bs = merged[q].start % page, be = merged[q].end % page;
if (bs !== 0 || be !== 0) {
add('page_alignment', merged[q].line, 'the block ' + ha(merged[q].start) + '-' + ha(merged[q].end - 1) + ' does not fill whole ' + page + '-byte pages (' + (bs !== 0 ? 'starts ' + bs + ' byte(s) into a page' : 'starts on a boundary') + ', ' + (be !== 0 ? 'ends ' + (page - be) + ' byte(s) short of one' : 'ends on a boundary') + ')');
}
}
if (startAddr !== null) {
var inside = false;
for (var r = 0; r < merged.length; r++) { if (startAddr >= merged[r].start && startAddr < merged[r].end) { inside = true; break; } }
if (!inside) {
add('start_address', startLine, 'the entry point ' + ha(startAddr) + ' is not inside any address range this image writes, so the device jumps into unprogrammed flash');
}
}
findings.sort(function (x, y) { return (x.line || 0) - (y.line || 0); });
return { findings: findings };
}
var API = { engine: { check: check }, RULES: RULES, RULE_COUNT: RULES.length };
if (typeof module !== 'undefined') { module.exports = API; }
if (typeof window !== 'undefined') { window.HEXENGINE = API; }
</script>
<script>
(function () {
'use strict';
var SAMPLE = ":100000000102030405060708090A0B0C0D0E0F1068\n:1000100002030405060708090A0B0C0D0E0F101148\n:10002000030405060708090A0B0C0D0E0F1011122A\n:100030000405060708090A0B0C0D0E0F10111208\n<<<<<<< HEAD\n:10001000090A0B0C0D0E0F101112131415161718D8\n:1003000005060708090A0B0C0D0E0F101112131425\n:10031000060708090A0B0C0D0E0F10111213141505\n:0400000300000800F1\n:00000001FF\n:100320000708090A0B0C0D0E0F10111213141516E5";
var img = document.getElementById('img'), page = document.getElementById('page'), out = document.getElementById('out');
function esc(s) { return String(s).replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>'); }
function run() {
var text = img.value;
if (!text.trim()) { out.innerHTML = '<p class="tally">Paste an image above, or load the broken sample.</p>'; return; }
var res = window.HEXENGINE.engine.check(text, { page: Number(page.value) });
var f = res.findings || [];
var errs = f.filter(function (x) { return x.sev === 'error'; }).length;
var warns = f.length - errs;
var lines = text.trim().split(/\r?\n/).length;
if (!f.length) {
out.innerHTML = '<p class="tally"><span class="g">Clean.</span> ' + lines + ' lines, all ' + window.HEXENGINE.RULE_COUNT + ' checks, nothing to report.</p>';
return;
}
var rows = f.map(function (x) {
return '<tr><td class="ln">L' + x.line + '</td><td><span class="sev ' + x.sev + '">' + x.sev + '</span></td>' +
'<td class="id">' + esc(x.check) + '</td><td>' + esc(x.msg) + '</td></tr>';
}).join('');
out.innerHTML = '<p class="tally">' + lines + ' lines · <span class="e">' + errs + ' error' + (errs === 1 ? '' : 's') +
'</span> · <span class="w">' + warns + ' warning' + (warns === 1 ? '' : 's') + '</span> · ' +
window.HEXENGINE.RULE_COUNT + ' checks run</p>' +
'<table><thead><tr><th>Line</th><th></th><th>Check</th><th>What the audit recomputed</th></tr></thead><tbody>' + rows + '</tbody></table>';
}
document.getElementById('run').addEventListener('click', run);
document.getElementById('demo').addEventListener('click', function () { img.value = SAMPLE; run(); });
page.addEventListener('change', function () { if (img.value.trim()) run(); });
})();
</script>
<section data-yj-install="free" style="margin:2rem auto 1rem;max-width:46rem;padding:1.25rem 1.25rem;border:1px solid #cfe3d6;border-radius:12px;background:#f6fbf8;font:16px/1.6 system-ui,-apple-system,Segoe UI,sans-serif"><h2 style="margin:0 0 .4rem;font-size:1.2rem">Install free</h2><p style="margin:0 0 .8rem;color:#41505f">Free for the file open in your editor - no key, no limit. The workspace sweep and the report ask for a key.</p><a href="https://marketplace.visualstudio.com/items?itemName=ReadyStack.hex-image-audit" rel="noopener" data-yj-install-door="mp" onclick="try{navigator.sendBeacon('/api/ev',new Blob([JSON.stringify({t:'install',slug:'hex-image-audit',src:'mp',path:location.pathname})],{type:'application/json'}))}catch(e){}" style="display:inline-block;margin:0 .5rem .5rem 0;padding:.6rem 1rem;border-radius:8px;background:#0f7b3f;color:#fff;text-decoration:none;font-weight:600">VS Code Marketplace</a><a href="https://open-vsx.org/extension/readystack/hex-image-audit" rel="noopener" data-yj-install-door="ovsx" onclick="try{navigator.sendBeacon('/api/ev',new Blob([JSON.stringify({t:'install',slug:'hex-image-audit',src:'ovsx',path:location.pathname})],{type:'application/json'}))}catch(e){}" style="display:inline-block;margin:0 .5rem .5rem 0;padding:.6rem 1rem;border-radius:8px;background:#0f7b3f;color:#fff;text-decoration:none;font-weight:600">Open VSX (Cursor, VSCodium)</a><a href="https://www.npmjs.com/package/@readystack/hex-image-audit" rel="noopener" data-yj-install-door="npm" onclick="try{navigator.sendBeacon('/api/ev',new Blob([JSON.stringify({t:'install',slug:'hex-image-audit',src:'npm',path:location.pathname})],{type:'application/json'}))}catch(e){}" style="display:inline-block;margin:0 .5rem .5rem 0;padding:.6rem 1rem;border-radius:8px;background:#0f7b3f;color:#fff;text-decoration:none;font-weight:600">npm</a><a href="https://www.npmjs.com/package/@readystack/hex-image-audit" rel="noopener" data-yj-install-door="mcp" onclick="try{navigator.sendBeacon('/api/ev',new Blob([JSON.stringify({t:'install',slug:'hex-image-audit',src:'mcp',path:location.pathname})],{type:'application/json'}))}catch(e){}" style="display:inline-block;margin:0 .5rem .5rem 0;padding:.6rem 1rem;border-radius:8px;background:#0f7b3f;color:#fff;text-decoration:none;font-weight:600">MCP server (npx)</a><a href="https://hub.docker.com/r/getreadystack/hex-image-audit" rel="noopener" data-yj-install-door="docker" onclick="try{navigator.sendBeacon('/api/ev',new Blob([JSON.stringify({t:'install',slug:'hex-image-audit',src:'docker',path:location.pathname})],{type:'application/json'}))}catch(e){}" style="display:inline-block;margin:0 .5rem .5rem 0;padding:.6rem 1rem;border-radius:8px;background:#0f7b3f;color:#fff;text-decoration:none;font-weight:600">Docker Hub</a><pre style="margin:.5rem 0 0;padding:.6rem .8rem;background:#f3f5f8;border-radius:8px;font-size:.9rem;overflow:auto">ext install ReadyStack.hex-image-audit
npx @readystack/hex-image-audit <file>
npx @readystack/hex-image-audit --mcp</pre><p style="margin:.6rem 0 0;font-size:.92rem;color:#41505f"><a href="https://getreadystack.com/api/buy/cl/polar_cl_l6iN1uWt0FwWu7tBsczD0jWpP2vxFM54Wdwqb3KPi1G" rel="nofollow" style="color:#0f7b3f;text-decoration:underline">Team? One key for every ReadyStack linter, 5 seats, $149 once</a></p></section>
<section data-yj-alert="rule" style="margin:1rem auto 1.5rem;max-width:46rem;padding:1.1rem 1.25rem;border:1px solid #d9dee6;border-radius:12px;background:#fbfcfd;font:15px/1.55 system-ui,-apple-system,Segoe UI,sans-serif"><b style="font-size:1.05rem">Get the full rule table as a PDF</b><div style="opacity:.78;margin:.35rem 0 .8rem">All 17 rules Intel HEX & S-record Image Audit checks, each with its source and date, on printable pages you can hand to your team or auditor. We email it now, and once more only if a rule changes. No newsletter.</div><div style="display:flex;gap:8px;flex-wrap:wrap"><input id="yjae" type="email" placeholder="you@example.com" style="flex:1;min-width:210px;padding:11px 13px;border:1px solid rgba(140,150,165,.34);border-radius:8px;background:#fff;color:inherit"><button id="yjab" style="padding:11px 18px;border-radius:8px;border:0;cursor:pointer;background:#0f7b3f;color:#fff;font:600 15px system-ui,sans-serif">Email me the PDF</button></div><div id="yjao" hidden style="margin-top:.6rem;color:#0f7b3f">Sent. It arrives within the hour - check spam too.</div><script>(function(){var b=document.getElementById("yjab"),e=document.getElementById("yjae");if(!b||!e)return;b.addEventListener("click",function(){var v=e.value.trim();if(!/.+@.+\..+/.test(v))return;try{localStorage.setItem("yj_em","1");}catch(_){}fetch(location.origin+"/api/subscribe",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({email:v,slug:"hex-image-audit",lang:"en",source:"rule_table"+((b.closest&&b.closest("section")&&b.closest("section").getAttribute("data-yj-moved"))?"_at_result":"")})}).then(function(r){var o=document.getElementById("yjao");o.hidden=false;if(!r.ok){o.textContent="Could not save - please try again.";}}).catch(function(){var o=document.getElementById("yjao");o.hidden=false;o.textContent="Could not save - please try again.";});});})();</script><script>(function(){var e=document.getElementById("yjae"),s=e&&e.closest?e.closest("section"):null,o=document.getElementById("out");if(!s||!o)return;var d=false;function mv(ev){if(d)return;if(ev&&ev.target&&s.contains(ev.target))return;d=true;var b=o;while(b.parentNode&&b.parentNode!==document.body&&b.parentNode.tagName!=="MAIN"&&b.parentNode.tagName!=="ARTICLE")b=b.parentNode;if(!b.parentNode||b===s||b.contains(s))return;try{b.parentNode.insertBefore(s,b.nextSibling);s.setAttribute("data-yj-moved","1");}catch(_){}}document.addEventListener("input",mv,true);document.addEventListener("change",mv,true);})();</script></section>
<section data-yj-cta="paid" style="margin:3rem auto;max-width:46rem;padding:1.5rem 1.25rem;border:1px solid #d8dee9;border-radius:12px;background:#fbfcfe;font:16px/1.6 system-ui,-apple-system,Segoe UI,sans-serif"><h2 style="margin:0 0 .5rem;font-size:1.25rem">Get the complete version <span class="yj-price">$29</span></h2><p style="margin:.25rem 0 1rem;color:#41505f">This page is the working piece. The full pack has everything below.</p><p style="margin:0 0 1rem;color:#41505f">Recomputes every checksum in a .hex or .srec image and maps every address it writes, before the image reaches the programmer.</p><p style="margin:0 0 1rem;color:#5b6b7c;font-size:.94rem">Freelance embedded software engineers average $104 an hour and firmware consultants $105 (contractrates.fyi, crowdsourced rates, June 2026). One hour of one of them re-reading one image costs more than this licence.</p><a href="https://getreadystack.com/api/buy/cl/polar_cl_AkmNU5yLwVJBdRMHSvuTCQI90j52v56xJsHZH4OTDjV" rel="nofollow" style="display:inline-block;padding:.7rem 1.3rem;border-radius:8px;background:#1b64f2;color:#fff;text-decoration:none;font-weight:600">Buy the full version — $29</a></section>
<!--yj-share-s101--><div style="margin:28px 0 8px;text-align:center"><button id="yjcp" type="button" style="font:inherit;padding:10px 18px;border:1px solid #8884;border-radius:10px;background:transparent;cursor:pointer">Copy link</button><button id="yjimg" type="button" style="font:inherit;padding:10px 18px;margin-left:8px;border:1px solid #8884;border-radius:10px;background:transparent;cursor:pointer">Save result as image</button></div><script>(function(){var b=document.getElementById("yjimg");if(!b)return;b.addEventListener("click",function(){var o=document.getElementById("out");var txt=(o&&(o.innerText||o.textContent)||"").trim();if(!txt){b.textContent="(no result yet)";setTimeout(function(){b.textContent="Save result as image";},1500);return;}var lines=txt.split(String.fromCharCode(10)).filter(function(x){return x.trim();}).slice(0,10);var W=1200,P=56,c=document.createElement("canvas");c.width=W;var lh=38,H=P*2+70+lines.length*lh+60;c.height=H;var x=c.getContext("2d");x.fillStyle="#1a1512";x.fillRect(0,0,W,H);x.fillStyle="#f3e9d2";x.font="600 34px system-ui,Segoe UI,Arial";var t=(document.title||"").split("|")[0].trim();x.fillText(t.slice(0,54),P,P+34);x.font="22px ui-monospace,Menlo,Consolas,monospace";x.fillStyle="#e8dcc4";for(var i=0;i<lines.length;i++){x.fillText(lines[i].slice(0,88),P,P+70+i*lh+22);}x.font="600 22px system-ui,Segoe UI,Arial";x.fillStyle="#c9a227";var d=location.hostname.replace("www.","");x.fillText(d,W-P-x.measureText(d).width,H-P+6);c.toBlob(function(bl){var u=URL.createObjectURL(bl),a=document.createElement("a");a.href=u;a.download=(t||"result").replace(/[^a-z0-9]+/gi,"-").toLowerCase()+"-"+d+".png";document.body.appendChild(a);a.click();setTimeout(function(){URL.revokeObjectURL(u);a.remove();},2000);},"image/png");});})();</script><script>(function(){var b=document.getElementById("yjcp");if(!b)return;var t=b.textContent;b.addEventListener("click",function(){var u=location.href.split("#")[0];function d(){b.textContent="Link copied";setTimeout(function(){b.textContent=t;},1800);}if(navigator.clipboard&&navigator.clipboard.writeText){navigator.clipboard.writeText(u).then(d,function(){prompt("",u);});}else{prompt("",u);}});})();</script>
<script data-yj="yj-view-beacon">(function(){try{var p=location.pathname;var k='yjv:'+p;if(sessionStorage.getItem(k))return;sessionStorage.setItem(k,'1');fetch('/api/ev',{method:'POST',keepalive:true,headers:{'Content-Type':'application/json'},body:JSON.stringify({path:p,lang:"en",ref:(document.referrer||''),src:(function(){try{return (new URLSearchParams(location.search).get('ref')||'').replace(/[^a-z_]/g,'').slice(0,12)}catch(e){return ''}})()})}).catch(function(){});}catch(e){}})();</script>
<script data-yj="yj-use-beacon">(function(){try{var o=document.getElementById('out');if(!o)return;var p=location.pathname,k='yju:'+p,sent=false;function fire(){if(sent||sessionStorage.getItem(k))return;if(!(o.textContent||'').replace(/\s+/g,'').length)return;sent=true;sessionStorage.setItem(k,'1');fetch('/api/ev',{method:'POST',keepalive:true,headers:{'Content-Type':'application/json'},body:JSON.stringify({path:p,lang:"en",t:'use',src:(function(){try{return (new URLSearchParams(location.search).get('ref')||'').replace(/[^a-z_]/g,'').slice(0,12)}catch(e){return ''}})()})}).catch(function(){});}var mo=new MutationObserver(function(){fire();if(sent)mo.disconnect();});mo.observe(o,{childList:true,characterData:true,subtree:true});}catch(e){}})();</script>
<div class="yj-home-find"><div class="yj-lgbar"><a href="/" class="yj-lg on">EN</a><a href="/de/" class="yj-lg">DE</a><a href="/ja/" class="yj-lg">JA</a><a href="/es/" class="yj-lg">ES</a><a href="/pt/" class="yj-lg">PT</a></div><section class="yj-find"><h2>Find a tool</h2><input id="yjq" type="search" autocomplete="off" placeholder="Type a product name, format or task"><div id="yjr"></div></section><style>.yj-lgbar{position:absolute;top:14px;right:18px;display:flex;gap:6px;z-index:60}.yj-lg{font:600 12px/1 system-ui,sans-serif;padding:6px 9px;border-radius:4px;text-decoration:none;color:#8b96a3;border:1px solid rgba(140,150,165,.28)}.yj-lg:hover{color:#e8eaed;border-color:rgba(160,175,195,.6)}.yj-lg.on{color:#0e1116;background:#cfd6de;border-color:#cfd6de}.yj-find{max-width:760px;margin:34px auto 8px;padding:0 20px}.yj-find h2{font:600 20px/1.3 system-ui,sans-serif;margin:0 0 10px}#yjq{width:100%;box-sizing:border-box;padding:13px 15px;font:400 15.5px system-ui,sans-serif;border:1px solid rgba(140,150,165,.34);border-radius:8px;background:rgba(255,255,255,.04);color:inherit}#yjq:focus{outline:none;border-color:#7FD1C4}#yjr{margin-top:10px;display:flex;flex-direction:column;gap:6px}#yjr a{display:flex;justify-content:space-between;gap:12px;padding:11px 14px;border:1px solid rgba(140,150,165,.22);border-radius:6px;text-decoration:none;font:400 14.5px/1.4 system-ui,sans-serif;color:inherit}#yjr a:hover{border-color:#7FD1C4}#yjr .pr{font:600 13px system-ui,sans-serif;color:#5fbf9a;white-space:nowrap}</style><script>(function(){var q=document.getElementById("yjq"),r=document.getElementById("yjr"),D=null;function load(){if(D)return Promise.resolve(D);return fetch("/search-index.json").then(function(x){return x.json();}).then(function(j){D=j;return j;}).catch(function(){D=[];return D;});}function esc(t){return String(t).replace(/[&<>"]/g,function(c){return {"&":"&","<":"<",">":">",'"':"""}[c];});}function run(){var v=q.value.trim().toLowerCase();if(v.length<2){r.innerHTML="";return;}load().then(function(j){var w=v.split(/\s+/),o=[];for(var i=0;i<j.length&&o.length<12;i++){var t=j[i].t.toLowerCase();var ok=true;for(var k=0;k<w.length;k++){if(t.indexOf(w[k])<0){ok=false;break;}}if(ok)o.push(j[i]);}r.innerHTML=o.length?o.map(function(x){return "<a href=\""+esc(x.u)+"\">"+"<span>"+esc(x.t)+"</span>"+(x.p?"<span class=\"pr\">$"+x.p+"</span>":"")+"</a>";}).join(""):"";});}q.addEventListener("input",run);q.addEventListener("focus",load);})();</script></div><section data-yj="yj-facts-v1" style="max-width:920px;margin:48px auto 0;padding:0 20px;font-family:system-ui,sans-serif"><style>.yj-article p{font:400 15.5px/1.7 system-ui,sans-serif;color:#26313b;margin:0 0 12px;max-width:72ch}.yj-article h2,.yj-article h3,.yj-article h4{font:600 17px/1.35 system-ui,sans-serif;color:#1b1f24;margin:22px 0 8px}.yj-article table{border-collapse:collapse;margin:10px 0 16px;font:14px/1.45 system-ui,sans-serif}.yj-article th,.yj-article td{border:1px solid #e2e6ea;padding:6px 10px;text-align:left}.yj-article th{background:#f4f6f8}.yj-article pre{background:#0f1720;color:#e6edf3;padding:12px 14px;border-radius:8px;overflow:auto;font:13px/1.5 ui-monospace,Menlo,monospace}.yj-article code{font:13px ui-monospace,Menlo,monospace;background:#f1f3f5;padding:1px 4px;border-radius:3px}.yj-article pre code{background:none;padding:0}.yj-article ul,.yj-article ol{font:15px/1.6 system-ui,sans-serif;color:#26313b}</style><p style="font:400 13px/1.5 system-ui,sans-serif;color:#6b7680;margin:0 0 14px"><time datetime="2026-09-21">Updated 2026-09-21</time> · ReadyStack</p><script type="application/ld+json">{"@context":"https://schema.org","@type":"WebPage","dateModified":"2026-09-21","author":{"@type":"Organization","name":"ReadyStack"}}</script><h2 style="font:600 21px/1.3 system-ui,sans-serif;margin:0 0 6px;color:#1b1f24">Worked example</h2><p style="font:400 14.5px/1.5 system-ui,sans-serif;margin:0 0 16px;color:#5b6570">Real numbers from this tool, line by line.</p><div class="yj-article"><p><img src="https://getreadystack.com/img/promo/sku58659_result_card.jpg" alt="Intel HEX & S-record Image Audit" loading="lazy" style="max-width:100%;border-radius:6px"></p>
<p>Eleven lines of a merged Intel HEX image came back with ten findings, six of them errors — and the build system that produced it had already called the image done. That is the whole problem an embedded firmware engineer has with .hex and .srec files: they are the one artefact in the build that nobody re-reads, because re-reading them by hand is arithmetic over thousands of records.</p>
<p>Here is what it found, verbatim. Line 3 ends in 0x2A; the recomputed two's-complement checksum is 0x28 — a constant was hand-patched and the checksum left behind. Line 4 declares a byte count of 16 and carries 15 data bytes. Line 5 is <code><<<<<<< HEAD</code>, a merge marker inside a firmware image. Line 6 writes address 0x00000010, which line 2 already wrote, so which byte reaches the flash depends on the load order of the tool. Line 9 names an entry point of 0x00000800 that no record ever writes, so the device jumps into unprogrammed flash. Line 11 is a data record sitting after <code>:00000001FF</code>, which no conforming loader will read — the classic way to ship a fix that never reaches the device.</p>
<p>None of that is exotic. Every one of those defects is what a merge, a last-minute patch, a script-generated image or a file mailed to a contract manufacturer leaves behind. The programmer at the flashing station is where you normally find out, and by then the boards are on the bench.</p>
<p>The extension runs 17 checks. It recomputes the two's-complement checksum of every Intel HEX record and the 0xFF-minus-sum checksum of every Motorola S-record from the record's own bytes; it compares every byte-count field with the data present; it validates record types, S-record count bytes and terminators; and it maps every absolute address the image writes, honouring type 02 and type 04 extended-address records, so it can report overlaps, unwritten gaps, data past 0xFFFF with no extended-address record in effect, blocks that straddle a flash page, and an entry point outside the image. Findings land in the Problems panel at their line.</p>
<p>The seventeenth check is new this month and it is not about the bench at all. An S-record image can carry an S0 header record naming the module (char[20]), its version and its revision. The format makes S0 optional and plenty of toolchains emit it blank — harmless while the image is on your desk, expensive once it is in an archive, because nothing ties the file back to the release it belongs to. Under the EU Cyber Resilience Act, Article 13(12), a manufacturer keeps the technical documentation at the disposal of market surveillance authorities for 10 years after the product is placed on the market or for the support period, whichever is longer, and the reporting obligations have been in force since 11 September 2026. An unidentifiable image is a record you cannot produce.</p>
<p>Auditing one image is free and complete: all 17 checks, no key, nothing truncated, no watermark and no counter, either in VS Code or in the browser, where the same rules run on your machine and the image is never uploaded. The licence buys a different job, not more checks: sweeping every matching image in the build folder in one pass and writing a dated Markdown audit report you keep as release evidence. The first sweep runs free for seven days, whole, so the report exists before the question of paying does.</p>
<p>Freelance embedded software engineers average $104 an hour and firmware consultants $105, on crowdsourced rates collected through June 2026. One hour of one of them re-reading one image costs more than the licence, which is $29 once, per person or CI seat, with a 7-day full refund.</p></div><h2 style="font:600 19px/1.3 system-ui,sans-serif;margin:30px 0 10px;color:#1b1f24">15 seconds — what it actually does</h2><video controls muted playsinline preload="none" poster="/img/promo/sku58659_result_card.jpg" style="width:100%;max-width:360px;border-radius:10px;display:block;margin:0 auto"><source src="/media/sku58659_short.mp4" type="video/mp4"></video><h2 style="font:600 21px/1.3 system-ui,sans-serif;margin:34px 0 6px;color:#1b1f24">Questions people ask</h2><details style="border-bottom:1px solid #e6e9ed;padding:10px 0"><summary style="cursor:pointer;font:600 15px/1.4 system-ui,sans-serif;color:#1b1f24">What does the Intel HEX & S-record Image Audit actually do?</summary><p style="margin:8px 0 2px;font:400 14.5px/1.6 system-ui,sans-serif;color:#33404b">It reads a .hex, .ihex, .s19, .s28, .s37, .srec or .mot file and runs 17 checks over it. It recomputes every Intel HEX and S-record checksum from the record's own bytes, compares every byte-count field with the data present, and maps every absolute address the image writes to find overlaps, gaps, page-straddling blocks and a stray entry point.</p></details><details style="border-bottom:1px solid #e6e9ed;padding:10px 0"><summary style="cursor:pointer;font:600 15px/1.4 system-ui,sans-serif;color:#1b1f24">Who is the Intel HEX & S-record Image Audit for?</summary><p style="margin:8px 0 2px;font:400 14.5px/1.6 system-ui,sans-serif;color:#33404b">Embedded firmware and release engineers who produce or receive .hex and S-record images: anyone who merges a bootloader with an application image, hand-patches a constant at the last minute, regenerates an image from a script, or mails one to a contract manufacturer. Also for whoever now archives each shipped image as a dated record for an EU market.</p></details><details style="border-bottom:1px solid #e6e9ed;padding:10px 0"><summary style="cursor:pointer;font:600 15px/1.4 system-ui,sans-serif;color:#1b1f24">Why is srec_cat or objcopy not enough?</summary><p style="margin:8px 0 2px;font:400 14.5px/1.6 system-ui,sans-serif;color:#33404b">They read these formats and will tell you a file is malformed, but they generally stop at the first record they cannot parse and hand you a parse error, not a list. They do not name every bad record, overlap, gap and page-straddling block at its line, and they leave you no dated report to archive.</p></details><details style="border-bottom:1px solid #e6e9ed;padding:10px 0"><summary style="cursor:pointer;font:600 15px/1.4 system-ui,sans-serif;color:#1b1f24">What is free and what needs a licence?</summary><p style="margin:8px 0 2px;font:400 14.5px/1.6 system-ui,sans-serif;color:#33404b">Auditing one image is free and complete: all 17 checks, no key, nothing truncated, no watermark, no trial counter, in the editor or in the browser. The licence covers a different job — sweeping every image in the build folder in one pass and writing a dated Markdown report you keep. The first sweep runs free for seven days.</p></details><details style="border-bottom:1px solid #e6e9ed;padding:10px 0"><summary style="cursor:pointer;font:600 15px/1.4 system-ui,sans-serif;color:#1b1f24">What would reviewing an image by hand cost instead?</summary><p style="margin:8px 0 2px;font:400 14.5px/1.6 system-ui,sans-serif;color:#33404b">Freelance embedded software engineers average $104 an hour and firmware consultants $105, on crowdsourced rates collected through June 2026. Reading one merged image record by record is an hour of that, and it is an hour nobody budgets, which is why the image ships unread. The licence is $29 once.</p></details><details style="border-bottom:1px solid #e6e9ed;padding:10px 0"><summary style="cursor:pointer;font:600 15px/1.4 system-ui,sans-serif;color:#1b1f24">Why not just ask ChatGPT or another AI chat?</summary><p style="margin:8px 0 2px;font:400 14.5px/1.6 system-ui,sans-serif;color:#33404b">A general AI chat answers from training data with a cutoff date, cannot read your repository and names no rule version. Intel HEX & S-record Image Audit checks the file you open against 17 rules from a rule set dated 2026-09-21, and points at the exact line. For a filing, an audit or a client you need that dated result on your own files.</p></details><script type="application/ld+json" data-yj="yj-faq-ld">{"@context": "https://schema.org", "@type": "FAQPage", "mainEntity": [{"@type": "Question", "name": "What does the Intel HEX & S-record Image Audit actually do?", "acceptedAnswer": {"@type": "Answer", "text": "It reads a .hex, .ihex, .s19, .s28, .s37, .srec or .mot file and runs 17 checks over it. It recomputes every Intel HEX and S-record checksum from the record's own bytes, compares every byte-count field with the data present, and maps every absolute address the image writes to find overlaps, gaps, page-straddling blocks and a stray entry point."}}, {"@type": "Question", "name": "Who is the Intel HEX & S-record Image Audit for?", "acceptedAnswer": {"@type": "Answer", "text": "Embedded firmware and release engineers who produce or receive .hex and S-record images: anyone who merges a bootloader with an application image, hand-patches a constant at the last minute, regenerates an image from a script, or mails one to a contract manufacturer. Also for whoever now archives each shipped image as a dated record for an EU market."}}, {"@type": "Question", "name": "Why is srec_cat or objcopy not enough?", "acceptedAnswer": {"@type": "Answer", "text": "They read these formats and will tell you a file is malformed, but they generally stop at the first record they cannot parse and hand you a parse error, not a list. They do not name every bad record, overlap, gap and page-straddling block at its line, and they leave you no dated report to archive."}}, {"@type": "Question", "name": "What is free and what needs a licence?", "acceptedAnswer": {"@type": "Answer", "text": "Auditing one image is free and complete: all 17 checks, no key, nothing truncated, no watermark, no trial counter, in the editor or in the browser. The licence covers a different job — sweeping every image in the build folder in one pass and writing a dated Markdown report you keep. The first sweep runs free for seven days."}}, {"@type": "Question", "name": "What would reviewing an image by hand cost instead?", "acceptedAnswer": {"@type": "Answer", "text": "Freelance embedded software engineers average $104 an hour and firmware consultants $105, on crowdsourced rates collected through June 2026. Reading one merged image record by record is an hour of that, and it is an hour nobody budgets, which is why the image ships unread. The licence is $29 once."}}, {"@type": "Question", "name": "Why not just ask ChatGPT or another AI chat?", "acceptedAnswer": {"@type": "Answer", "text": "A general AI chat answers from training data with a cutoff date, cannot read your repository and names no rule version. Intel HEX & S-record Image Audit checks the file you open against 17 rules from a rule set dated 2026-09-21, and points at the exact line. For a filing, an audit or a client you need that dated result on your own files."}}]}</script><div id="yj-ask" style="margin:36px 0 0;padding:18px 18px 16px;border:1px solid #e2e6ea;border-radius:10px;background:#fbfbfc"><h2 style="font:600 19px/1.3 system-ui,sans-serif;margin:0 0 4px;color:#1b1f24">Ask about this tool</h2><p style="font:400 13.5px/1.5 system-ui,sans-serif;margin:0 0 12px;color:#5b6570">One question, answered by the person who built it. Your email only if you want the answer sent.</p><form onsubmit="return yjAsk(event)" style="display:grid;gap:8px"><textarea name="question" required minlength="10" maxlength="2000" rows="3" placeholder="Your question" style="font:14px/1.5 system-ui,sans-serif;padding:10px;border:1px solid #cfd6dd;border-radius:6px"></textarea><div style="display:flex;gap:8px;flex-wrap:wrap"><input type="email" name="email" placeholder="Email (optional)" style="flex:1;min-width:200px;font:14px system-ui,sans-serif;padding:9px 10px;border:1px solid #cfd6dd;border-radius:6px"><button type="submit" style="font:600 14px system-ui,sans-serif;padding:9px 18px;border:0;border-radius:6px;background:#1b1f24;color:#fff;cursor:pointer">Send</button></div><p id="yj-ask-msg" style="font:13px system-ui,sans-serif;margin:2px 0 0;color:#0a6238;min-height:1em"></p></form><script>function yjAsk(e){e.preventDefault();var f=e.target,m=document.getElementById("yj-ask-msg");m.style.color="#0a6238";fetch(location.origin+"/api/ask",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({sku:58659,slug:"hex-image-audit",lang:"en",question:f.question.value,email:f.email.value,page:location.pathname})}).then(function(r){if(!r.ok)throw 0;m.textContent="Thanks \u2014 noted. Answers land on this page and, if you left an email, in your inbox.";f.question.value="";}).catch(function(){m.style.color="#a8551f";m.textContent="Could not send. Please try again in a minute.";});return false;}</script></div></section><section class="yj-related" style="max-width:920px;margin:56px auto 40px;padding:0 20px"><h2 style="font:600 21px/1.3 system-ui,sans-serif;margin:0 0 6px;color:#1b1f24">Same job, next tool</h2><p style="font:400 14.5px/1.5 system-ui,sans-serif;margin:0 0 20px;color:#5b6570">Free in the browser. The full version is a one-time licence.</p><div style="display:grid;grid-template-columns:repeat(auto-fill,minmax(250px,1fr));gap:14px"><a class="yj-rel-c" href="https://getreadystack.com/tools/firmware-release-gate"><span class="yj-rel-t">Firmware Release Gate for sdkconfig and prj.conf</span><span class="yj-rel-p">$29</span></a><a class="yj-rel-c" href="https://getreadystack.com/tools/base-image-eol-lint"><span class="yj-rel-t">Base Image EOL Lint - Dockerfile and CI end-of-life check</span><span class="yj-rel-p">$29</span></a><a class="yj-rel-c" href="https://getreadystack.com/tools/cbpr-address-lint"><span class="yj-rel-t">CBPR+ Structured Address Lint</span><span class="yj-rel-p">$29</span></a><a class="yj-rel-c" href="https://getreadystack.com/tools/consent-proof-record-lint"><span class="yj-rel-t">Consent Proof Record Lint (GDPR Art.7)</span><span class="yj-rel-p">$29</span></a><a class="yj-rel-c" href="https://getreadystack.com/tools/dora-ict-contract-clause-lint"><span class="yj-rel-t">DORA Art. 30 ICT Contract Clause Lint</span><span class="yj-rel-p">$29</span></a><a class="yj-rel-c" href="https://getreadystack.com/tools/play-release-blocker-lint"><span class="yj-rel-t">Google Play Release Blocker Lint - target API 36, Billing 8, 16 KB</span><span class="yj-rel-p">$29</span></a></div><style>.yj-rel-c{display:flex;flex-direction:column;gap:8px;padding:15px 16px;border:1px solid #e2e6ea;border-radius:6px;text-decoration:none;background:#fff}.yj-rel-c:hover{border-color:#b9c2cb;background:#fafbfc}.yj-rel-t{font:500 14.5px/1.42 system-ui,sans-serif;color:#1b1f24}.yj-rel-p{font:600 13px system-ui,sans-serif;color:#0a6238}</style></section></body>
</html>
|