Files
James Gross a7b5f8deef Py3 cutover (try number: who knows?) (#328)
Massive cutover to python3 (baseline is 3.9.1, but is also tested/working on 3.7.x). This merge is a breaking change for any downstream consumers, as the python 2->3 strings/bytes change is central. Most APIs should still work roughly as intended, as I didn't majorly re-organize the codebase, so most APIs live where they lived before. But a changelog/migration guide will be included in the next PR for this.

This PR will constitute most if not all of the v1.0.0 release that is forthcoming soon.
2021-02-03 14:37:09 -05:00

153 lines
4.2 KiB
Python

'''
Parser objects for the Intel Hex file format.
'''
import binascii
import vstruct
from vstruct.primitives import *
IHEX_REC_DATA = 0
IHEX_REC_EOF = 1
IHEX_REC_EXSEG = 2 # Extended Segment Address Records
IHEX_REC_STARTSEG = 3 # The beginning code segment value
IHEX_REC_EXLINADDR = 4 # Extended Linear Address Records
IHEX_REC_STARTLINADDR = 5 # 32bit entry point
class IHexChunk(vstruct.VStruct):
def __init__(self):
vstruct.VStruct.__init__(self)
self.startcode = v_str(1)
self.bytecount = v_str(2)
self.address = v_str(4)
self.recordtype = v_str(2)
self.data = v_str(0)
self.csum = v_str(2)
def pcb_bytecount(self):
dsize = int(self.bytecount, 16)
self.vsGetField('data').vsSetLength( 2 * dsize )
def getAddress(self):
'''
Parse the address field and return the int type.
'''
return int( self.address, 16 )
def getData(self):
'''
Return the binary data payload for this chunk.
'''
return binascii.unhexlify(self.data)
class IHexFile(vstruct.VArray):
def vsParse(self, bytes, offset=0):
lines = bytes[offset:].splitlines()
for line in lines:
offset += 1 # there is an eaten newline for each
if not line:
continue
try:
c = IHexChunk()
c.vsParse( line )
offset += len( c )
self.vsAddElement( c )
except Exception as e:
logger.warning("Exception parsing: %r", e)
if int(c.recordtype, 16) == IHEX_REC_EOF:
break
return offset
def vsEmit(self, fast=False):
"""
Get back the byte sequence associated with this structure.
"""
if fast:
if self._vs_fastfields is None:
self._vsInitFastFields()
ffvals = [ ff.vsGetValue() for ff in self._vs_fastfields ]
return struct.pack(self._vs_fastfmt, *ffvals)
ret = b''
for fname, fobj in self.vsGetFields():
ret += fobj.vsEmit() + b'\r\n'
return ret.decode('utf-8')
def getEntryPoints(self):
'''
If a 32bit linear start address is defined for this file,
return it. Returns None if the 32bit entry point extension
is not present.
'''
evas = []
for fname, chunk in self:
ctype = int( chunk.recordtype, 16 )
if ctype == IHEX_REC_STARTLINADDR:
evas.append(int( chunk.data, 16 ))
elif ctype == IHEX_REC_STARTSEG:
startcs = int( chunk.data, 16 ) >> 16
startip = int( chunk.data, 16 ) & 0xffff
evas.append((startcs << 4) | startip)
return evas
def getMemoryMaps(self):
'''
Retrieve a set of memory maps defined by this hex file.
Memory maps are returned as a list of
( va, perms, fname, bytes ) tuples.
'''
import envi.memory as e_mem
# Get all the binary parts....
baseaddr = 0
memparts = []
for fname, chunk in self:
ctype = int( chunk.recordtype, 16 )
if ctype == IHEX_REC_DATA:
addr = chunk.getAddress() + baseaddr
memparts.append( (addr, chunk.getData()) )
continue
if ctype == IHEX_REC_EXSEG:
baseaddr = int( chunk.data, 16 ) << 4
continue
if ctype == IHEX_REC_EXLINADDR:
baseaddr = int( chunk.data, 16 ) << 16
continue
if ctype == IHEX_REC_STARTSEG:
continue
if ctype == IHEX_REC_EOF:
break
raise Exception('Unhandled IHEX chunk: %s' % chunk.recordtype)
memparts.sort()
maps = []
for addr, bytes in memparts:
# is this the next contiguous chunk?
if maps and addr == ( maps[-1][0] + len(maps[-1][3]) ):
maps[-1][3] += bytes
else:
maps.append( [ addr, e_mem.MM_RWX, '', bytes ] )
return maps