Rizin
unix-like reverse engineering framework and cli tools
io_ihex.c
Go to the documentation of this file.
1 // SPDX-FileCopyrightText: 2013-2017 pancake <pancake@nopcode.org>
2 // SPDX-FileCopyrightText: 2013-2017 fenugrec <fenugrec@users.sourceforge.net>
3 // SPDX-License-Identifier: LGPL-3.0-only
4 
5 /*
6 *** .hex format description : every line follows this pattern
7 :SSAAAARR<xx*SS>KK
8 SS: num of "xx" bytes
9 AAAA lower 16bits of address for resulting data (==0 for 01, 02, 04 and 05 records)
10 RR: rec type:
11  00 data
12  01 EOF (always SS=0, AAAA=0)
13  02 extended segment addr reg: (always SS=02, AAAA=0); data = 0x<xxyy> => bits 4..19 of following addresses
14  04 extended linear addr reg: (always SS=02, AAAA=0); data = 0x<xxyy> => bits 16..31 of following addresses
15  05 non-standard; could be "start linear address" AKA "entry point".
16 KK = 0 - (sum of all bytes)
17 
18 //sauce : http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.faqs/ka9903.html
19 
20 **** example records
21 :02000002fffffe #rec 02 : new seg = 0xffff, so next addresses will be (seg<<4)+AAAA
22 :02000000556643 #rec 00 : load 2 bytes [0x000f fff0]=0x55; [0x000f fff1]=0x66
23 :020000040800f2 #rec 04 : new linear addr = 0x0800, so next addresses will be (0x0800 <<16) + AAAA
24 :10000000480F0020F948000811480008134800086C #rec 00 : load 16 bytes @ 0x0800 0000
25 :04000005080049099D #rec 05 : entry point = 0x0800 4909 (ignored)
26 :00000001FF #rec 01 : EOF
27 
28 */
29 
30 #include "rz_io.h"
31 #include "rz_lib.h"
32 #include "rz_util.h"
33 #include <limits.h> //for INT_MAX
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <sys/types.h>
37 
38 // struct Rihex : holds sparse buffer + its own fd, for internal management
39 typedef struct {
40  int fd;
42 } Rihex;
43 
44 static bool ihex_write(RzIODesc *desc, Rihex *rih);
45 
46 static int __write(RzIO *io, RzIODesc *fd, const ut8 *buf, int count) {
47  if (!fd || !fd->data || (fd->perm & RZ_PERM_W) == 0 || count <= 0) {
48  return -1;
49  }
50  Rihex *rih = fd->data;
51  /* mem write */
52  if (rz_buf_write_at(rih->rbuf, io->off, buf, count) != count) {
53  eprintf("ihex:write(): sparse write failed\n");
54  return -1;
55  }
57  if (!ihex_write(fd, rih)) {
58  return -1;
59  }
60  return count;
61 }
62 
63 // fw04b : write 04 record (extended address); ret <0 if error
64 static int fw04b(FILE *fd, ut16 eaddr) {
65  ut8 cks = 0 - (6 + (eaddr >> 8) + (eaddr & 0xff));
66  return fprintf(fd, ":02000004%04X%02X\n", eaddr, cks);
67 }
68 
69 // write contiguous block of data to file; ret 0 if ok
70 // max 65535 bytes; assumes a 04 rec was written before
71 static int fwblock(FILE *fd, ut8 *b, ut32 start_addr, ut32 size) {
72  ut8 cks;
73  char linebuf[80];
74  ut16 last_addr;
75  int j;
76  ut32 i; // has to be bigger than size !
77 
78  if (size < 1 || size > 0x10000 || !fd || !b) {
79  return -1;
80  }
81 
82  for (i = 0; (i + 0x10) < size; i += 0x10) {
83  cks = 0x10;
84  cks += (i + start_addr) >> 8;
85  cks += (i + start_addr);
86  for (j = 0; j < 0x10; j++) {
87  cks += b[j];
88  }
89  cks = 0 - cks;
90  if (fprintf(fd, ":10%04x00%02x%02x%02x%02x%02x%02x%02x"
91  "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x\n",
92  (i + start_addr) & 0xffff, b[0], b[1], b[2], b[3], b[4], b[5], b[6],
93  b[7], b[8], b[9], b[10], b[11], b[12], b[13],
94  b[14], b[15], cks) < 0) {
95  return -1;
96  }
97  b += 0x10;
98  if (((i + start_addr) & 0xffff) < 0x10) {
99  // addr rollover: write ext address record
100  if (fw04b(fd, (i + start_addr) >> 16) < 0) {
101  return -1;
102  }
103  }
104  }
105  if (i == size) {
106  return 0;
107  }
108  // write crumbs
109  last_addr = i + start_addr;
110  cks = -last_addr;
111  cks -= last_addr >> 8;
112  for (j = 0; i < size; i++, j++) {
113  cks -= b[j];
114  sprintf(linebuf + (2 * j), "%02X", b[j]);
115  }
116  cks -= j;
117 
118  if (fprintf(fd, ":%02X%04X00%.*s%02X\n", j, last_addr, 2 * j, linebuf, cks) < 0) {
119  return -1;
120  }
121  return 0;
122 }
123 
124 static int __read(RzIO *io, RzIODesc *fd, ut8 *buf, int count) {
125  if (!fd || !fd->data || (count <= 0)) {
126  return -1;
127  }
128  Rihex *rih = fd->data;
129  memset(buf, io->Oxff, count);
130  int r = rz_buf_read_at(rih->rbuf, io->off, buf, count);
131  if (r >= 0) {
132  rz_buf_seek(rih->rbuf, r, RZ_BUF_CUR);
133  }
134  // sparse read return >= 0 but < count still means everything was read successfully,
135  // just maybe not entirely populated by chunks:
136  return r < 0 ? -1 : count;
137 }
138 
139 static int __close(RzIODesc *fd) {
140  if (!fd || !fd->data) {
141  return -1;
142  }
143  Rihex *rih = fd->data;
144  rz_buf_free(rih->rbuf);
145  free(rih);
146  fd->data = NULL;
147  return 0;
148 }
149 
150 static ut64 __lseek(struct rz_io_t *io, RzIODesc *fd, ut64 offset, int whence) {
151  Rihex *rih;
152  if (!fd || !fd->data) {
153  return -1;
154  }
155  rih = fd->data;
156  io->off = rz_buf_seek(rih->rbuf, offset, whence);
157  return io->off;
158 }
159 
160 static bool __plugin_open(RzIO *io, const char *pathname, bool many) {
161  return (!strncmp(pathname, "ihex://", 7));
162 }
163 
164 // ihex_parse : parse ihex file loaded at *str, fill sparse buffer "rbuf"
165 // supported rec types : 00, 01, 02, 04
166 // ret 0 if ok
167 static bool ihex_parse(RzBuffer *rbuf, char *str) {
168  ut8 *sec_tmp;
169  ut32 sec_start = 0; // addr for next section write
170  ut32 segreg = 0; // basis for addr fields
171  ut32 addr_tmp = 0; // addr for record
172  ut16 next_addr = 0; // for checking if records are sequential
173  char *eol;
174  ut8 cksum;
175  int extH, extL;
176  int bc = 0, type, byte, i, l;
177  // fugly macro to prevent an overflow of rz_buf_write_at() len
178 #define SEC_MAX (sec_size < INT_MAX) ? sec_size : INT_MAX
179  ut32 sec_size = 0;
180  const int sec_count = UT16_MAX;
181  sec_tmp = calloc(1, sec_count);
182  if (!sec_tmp) {
183  goto fail;
184  }
185  do {
186  l = sscanf(str, ":%02x%04x%02x", &bc, &addr_tmp, &type);
187  if (l != 3) {
188  eprintf("Invalid data in ihex file (%.*s)\n", 80, str);
189  goto fail;
190  }
191  bc &= 0xff;
192  addr_tmp &= 0xffff;
193  type &= 0xff;
194 
195  switch (type) {
196  case 0: // DATA
197  eol = strchr(str + 1, ':');
198  if (eol) {
199  *eol = 0;
200  }
201  cksum = bc;
202  cksum += addr_tmp >> 8;
203  cksum += addr_tmp;
204  cksum += type;
205 
206  if ((next_addr != addr_tmp) || ((sec_size + bc) > SEC_MAX)) {
207  // previous block is not contiguous, or
208  // section buffer is full => write a sparse chunk
209  if (sec_size && sec_size < UT16_MAX) {
210  if (rz_buf_write_at(rbuf, sec_start, sec_tmp, (int)sec_size) != sec_size) {
211  eprintf("sparse buffer problem, giving up\n");
212  goto fail;
213  }
214  }
215  // advance cursor, reset section
216  sec_start = segreg + addr_tmp;
217  next_addr = addr_tmp;
218  sec_size = 0;
219  }
220 
221  for (i = 0; i < bc; i++) {
222  if (sscanf(str + 9 + (i * 2), "%02x", &byte) != 1) {
223  eprintf("unparsable data !\n");
224  goto fail;
225  }
226  if (sec_size + i < sec_count) {
227  sec_tmp[sec_size + i] = (ut8)byte & 0xff;
228  }
229  cksum += byte;
230  }
231  sec_size += bc;
232  next_addr += bc;
233  if (eol) {
234  // checksum
235  if (sscanf(str + 9 + (i * 2), "%02x", &byte) != 1) {
236  eprintf("unparsable data !\n");
237  goto fail;
238  }
239  cksum += byte;
240  if (cksum != 0) {
241  ut8 fixedcksum = 0 - (cksum - byte);
242  eprintf("Checksum failed %02x (got %02x expected %02x)\n",
243  cksum, byte, fixedcksum);
244  goto fail;
245  }
246  *eol = ':';
247  }
248  str = eol;
249  break;
250  case 1: // EOF. we don't validate checksum here
251  if (sec_size) {
252  if (rz_buf_write_at(rbuf, sec_start, sec_tmp, sec_size) != sec_size) {
253  eprintf("sparse buffer problem, giving up. ssiz=%X, sstart=%X\n", sec_size, sec_start);
254  goto fail;
255  }
256  }
257  str = NULL;
258  break;
259  case 2: // extended segment record
260  case 4: // extended linear address rec
261  // both rec types are handled the same except :
262  // new address = seg_reg <<4 for type 02; new address = lin_addr <<16 for type 04.
263  // write current section
264  if (sec_size) {
265  if (rz_buf_write_at(rbuf, sec_start, sec_tmp, sec_size) != sec_size) {
266  eprintf("sparse buffer problem, giving up\n");
267  goto fail;
268  }
269  }
270  sec_size = 0;
271 
272  eol = strchr(str + 1, ':');
273  if (eol) {
274  *eol = 0;
275  }
276  cksum = bc;
277  cksum += addr_tmp >> 8;
278  cksum += addr_tmp;
279  cksum += type;
280  if ((bc != 2) || (addr_tmp != 0)) {
281  eprintf("invalid type 02/04 record!\n");
282  goto fail;
283  }
284  if ((sscanf(str + 9 + 0, "%02x", &extH) != 1) ||
285  (sscanf(str + 9 + 2, "%02x", &extL) != 1)) {
286  eprintf("unparsable data !\n");
287  goto fail;
288  }
289  extH &= 0xff;
290  extL &= 0xff;
291  cksum += extH + extL;
292 
293  segreg = extH << 8 | extL;
294 
295  // segment rec(02) gives bits 4..19; linear rec(04) is bits 16..31
296  segreg = segreg << ((type == 2) ? 4 : 16);
297  next_addr = 0;
298  sec_start = segreg;
299 
300  if (eol) {
301  // checksum
302  byte = 0; // break checksum if sscanf failed
303  if (sscanf(str + 9 + 4, "%02x", &byte) != 1) {
304  cksum = 1;
305  }
306  cksum += byte;
307  if (cksum != 0) {
308  ut8 fixedcksum = 0 - (cksum - byte);
309  eprintf("Checksum failed %02x (got %02x expected %02x)\n",
310  cksum, byte, fixedcksum);
311  goto fail;
312  }
313  *eol = ':';
314  }
315  str = eol;
316  break;
317  case 3: // undefined rec. Just skip.
318  case 5: // non-standard, sometimes "start linear address"
319  str = strchr(str + 1, ':');
320  break;
321  }
322  } while (str);
323  free(sec_tmp);
324  return true;
325 fail:
326  free(sec_tmp);
327  return false;
328 }
329 
330 static bool ihex_write(RzIODesc *desc, Rihex *rih) {
331  const char *pathname = desc->name + 7;
332  FILE *out = rz_sys_fopen(pathname, "w");
333  if (!out) {
334  eprintf("Cannot open '%s' for writing\n", pathname);
335  return false;
336  }
337  // disk write : process each sparse chunk
338  size_t chunks_count;
339  const RzBufferSparseChunk *chunks = rz_buf_sparse_get_chunks(rih->rbuf, &chunks_count);
340  ut64 addh_cur = 0;
341  for (size_t i = 0; i < chunks_count; i++) {
342  const RzBufferSparseChunk *rbs = &chunks[i];
343  ut64 from = rbs->from;
344  while (from >> 16 != rbs->to >> 16) {
345  // we cross a 64k boundary, so write in multiple steps
346  ut16 addl = from & 0xffff;
347  ut16 addh = from >> 16;
348  if (addh != addh_cur) {
349  addh_cur = addh;
350  // 04 record (ext address)
351  if (fw04b(out, addh) < 0) {
352  eprintf("ihex:write: file error\n");
353  fclose(out);
354  return false;
355  }
356  }
357  // 00 records (data)
358  ut32 tsiz = (ut32)0x10000 - (ut32)addl;
359  if (fwblock(out, rbs->data + (from - rbs->from), from, tsiz)) {
360  eprintf("ihex:fwblock error\n");
361  fclose(out);
362  return false;
363  }
364  from = ((from >> 16) + 1) << 16;
365  }
366  ut16 addh = from >> 16;
367  if (addh != addh_cur) {
368  addh_cur = addh;
369  // 04 record (ext address)
370  if (fw04b(out, addh) < 0) {
371  eprintf("ihex:write: file error\n");
372  fclose(out);
373  return false;
374  }
375  }
376  // 00 records (remaining data)
377  if (fwblock(out, rbs->data + (from - rbs->from), from, rbs->to - from + 1)) {
378  eprintf("ihex:fwblock error 2\n");
379  fclose(out);
380  return false;
381  }
382  }
383 
384  fprintf(out, ":00000001FF\n");
385  fclose(out);
386  return true;
387 }
388 
389 static RzIODesc *__open(RzIO *io, const char *pathname, int rw, int mode) {
390  Rihex *mal = NULL;
391  char *str = NULL;
392  if (__plugin_open(io, pathname, 0)) {
393  str = rz_file_slurp(pathname + 7, NULL);
394  if (!str) {
395  return NULL;
396  }
397  mal = RZ_NEW0(Rihex);
398  if (!mal) {
399  free(str);
400  return NULL;
401  }
402  mal->rbuf = rz_buf_new_sparse(io->Oxff);
403  if (!mal->rbuf) {
404  free(str);
405  free(mal);
406  return NULL;
407  }
408  if (!ihex_parse(mal->rbuf, str)) {
409  eprintf("ihex: failed to parse file\n");
410  free(str);
411  rz_buf_free(mal->rbuf);
412  free(mal);
413  return NULL;
414  }
415  free(str);
416  return rz_io_desc_new(io, &rz_io_plugin_ihex,
417  pathname, rw, mode, mal);
418  }
419  return NULL;
420 }
421 
422 static bool __resize(RzIO *io, RzIODesc *fd, ut64 size) {
423  if (!fd) {
424  return false;
425  }
426  Rihex *rih = fd->data;
427  if (!rih) {
428  return false;
429  }
430  if (!rz_buf_resize(rih->rbuf, size)) {
431  return false;
432  }
433  return ihex_write(fd, rih);
434 }
435 
437  .name = "ihex",
438  .desc = "Open intel HEX file",
439  .uris = "ihex://",
440  .license = "LGPL",
441  .open = __open,
442  .close = __close,
443  .read = __read,
444  .check = __plugin_open,
445  .lseek = __lseek,
446  .write = __write,
447  .resize = __resize
448 };
449 
450 #ifndef RZ_PLUGIN_INCORE
452  .type = RZ_LIB_TYPE_IO,
453  .data = &rz_io_plugin_ihex,
455 };
456 #endif
lzma_index ** i
Definition: index.h:629
const char * desc
Definition: bin_vsf.c:19
const lzma_allocator const uint8_t size_t uint8_t * out
Definition: block.h:528
#define RZ_API
#define NULL
Definition: cris-opc.c:27
#define r
Definition: crypto_rc6.c:12
static static sync static getppid static getegid const char static filename char static len const char char static bufsiz static mask static vfork const void static prot static getpgrp const char static swapflags static arg static fd static protocol static who struct sockaddr static addrlen static backlog struct timeval struct timezone static tz const struct iovec static count static mode const void const struct sockaddr static tolen const char static pathname void count
Definition: sflib.h:98
#define ut8
Definition: dcpu16.h:8
uint16_t ut16
uint32_t ut32
RZ_API void Ht_() free(HtName_(Ht) *ht)
Definition: ht_inc.c:130
static ut64 __lseek(struct rz_io_t *io, RzIODesc *fd, ut64 offset, int whence)
Definition: io_ihex.c:150
static int __read(RzIO *io, RzIODesc *fd, ut8 *buf, int count)
Definition: io_ihex.c:124
RzIOPlugin rz_io_plugin_ihex
Definition: io_ihex.c:436
static bool __plugin_open(RzIO *io, const char *pathname, bool many)
Definition: io_ihex.c:160
static RzIODesc * __open(RzIO *io, const char *pathname, int rw, int mode)
Definition: io_ihex.c:389
static int fw04b(FILE *fd, ut16 eaddr)
Definition: io_ihex.c:64
static bool __resize(RzIO *io, RzIODesc *fd, ut64 size)
Definition: io_ihex.c:422
RZ_API RzLibStruct rizin_plugin
Definition: io_ihex.c:451
static int fwblock(FILE *fd, ut8 *b, ut32 start_addr, ut32 size)
Definition: io_ihex.c:71
static int __write(RzIO *io, RzIODesc *fd, const ut8 *buf, int count)
Definition: io_ihex.c:46
static bool ihex_parse(RzBuffer *rbuf, char *str)
Definition: io_ihex.c:167
#define SEC_MAX
static bool ihex_write(RzIODesc *desc, Rihex *rih)
Definition: io_ihex.c:330
static int __close(RzIODesc *fd)
Definition: io_ihex.c:139
voidpf void uLong size
Definition: ioapi.h:138
voidpf uLong offset
Definition: ioapi.h:144
const char int mode
Definition: ioapi.h:137
voidpf void * buf
Definition: ioapi.h:138
sprintf
Definition: kernel.h:365
uint8_t ut8
Definition: lh5801.h:11
return memset(p, 0, total)
void * calloc(size_t number, size_t size)
Definition: malloc.c:102
static static fork const void static count static fd const char const char static newpath char char char static envp time_t static t const char static mode static whence const char static dir time_t static t unsigned static seconds const char struct utimbuf static buf static inc static sig const char pathname
Definition: sflib.h:66
int type
Definition: mipsasm.c:17
string FILE
Definition: benchmark.py:21
#define eprintf(x, y...)
Definition: rlcc.c:7
RZ_API bool rz_buf_resize(RZ_NONNULL RzBuffer *b, ut64 newsize)
Resize the buffer size.
Definition: buf.c:890
RZ_API st64 rz_buf_seek(RZ_NONNULL RzBuffer *b, st64 addr, int whence)
Modify the current cursor position in the buffer.
Definition: buf.c:1166
#define RZ_BUF_CUR
Definition: rz_buf.h:15
RZ_API st64 rz_buf_write_at(RZ_NONNULL RzBuffer *b, ut64 addr, RZ_NONNULL const ut8 *buf, ut64 len)
Write len bytes of the buffer at the specified address.
Definition: buf.c:1197
RZ_API st64 rz_buf_read_at(RZ_NONNULL RzBuffer *b, ut64 addr, RZ_NONNULL RZ_OUT ut8 *buf, ut64 len)
Read len bytes of the buffer at the specified address.
Definition: buf.c:1136
RZ_API const RzBufferSparseChunk * rz_buf_sparse_get_chunks(RzBuffer *b, RZ_NONNULL size_t *count)
Only for sparse RzBuffers, get all sparse data chunks currently populated.
Definition: buf_sparse.c:313
RZ_API void rz_buf_free(RzBuffer *b)
Free all internal data hold by the buffer and the buffer.
Definition: buf.c:1253
RZ_API RZ_OWN RzBuffer * rz_buf_new_sparse(ut8 Oxff)
Creates a sparse buffer.
Definition: buf.c:408
RZ_API RZ_OWN char * rz_file_slurp(const char *str, RZ_NULLABLE size_t *usz)
Definition: file.c:454
RZ_API RzIODesc * rz_io_desc_new(RzIO *io, RzIOPlugin *plugin, const char *uri, int flags, int mode, void *data)
Definition: io_desc.c:11
@ RZ_LIB_TYPE_IO
Definition: rz_lib.h:69
RZ_API FILE * rz_sys_fopen(const char *path, const char *mode)
Definition: sys.c:1815
#define RZ_NEW0(x)
Definition: rz_types.h:284
#define RZ_PERM_W
Definition: rz_types.h:94
#define UT16_MAX
#define RZ_VERSION
Definition: rz_version.h:8
static struct sockaddr static addrlen static backlog const void static flags void struct sockaddr from
Definition: sfsocketcall.h:123
#define b(i)
Definition: sha256.c:42
Definition: io_ihex.c:39
int fd
Definition: io_ihex.c:40
RzBuffer * rbuf
Definition: io_ihex.c:41
ut8 * data
size == to - from + 1
Definition: rz_buf.h:56
ut64 from
inclusive
Definition: rz_buf.h:54
ut64 to
inclusive, there can't be chunks with size == 0
Definition: rz_buf.h:55
const char * name
Definition: rz_io.h:115
const char * version
Definition: rz_io.h:117
Definition: rz_io.h:59
int Oxff
Definition: rz_io.h:65
ut64 off
Definition: rz_io.h:61
#define fail(test)
Definition: tests.h:29
static struct @626 mal
ut64(WINAPI *w32_GetEnabledXStateFeatures)()
static const z80_opcode fd[]
Definition: z80_tab.h:997