def puts(self, addr, s): """Put string of bytes at given address. Will overwrite any previous entries. """ a = array('B', asbytes(s)) for i in range_g(len(a)): self._buf[addr + i] = a[i]
def puts(self, addr, s): """Put string of bytes at given address. Will overwrite any previous entries. """ a = array('B', asbytes(s)) for i in xrange(len(a)): self._buf[addr+i] = a[i]
def gets(self, addr, length): """Get string of bytes from given address. If any entries are blank from addr through addr+length, a NotEnoughDataError exception will be raised. Padding is not used.""" a = array('B', asbytes('\0' * length)) try: for i in range_g(length): a[i] = self._buf[addr + i] except KeyError: raise NotEnoughDataError(address=addr, length=length) return asstr(a.tostring())
def gets(self, addr, length): """Get string of bytes from given address. If any entries are blank from addr through addr+length, a NotEnoughDataError exception will be raised. Padding is not used.""" a = array('B', asbytes('\0'*length)) try: for i in xrange(length): a[i] = self._buf[addr+i] except KeyError: raise NotEnoughDataError(address=addr, length=length) return asstr(a.tostring())
def loadbin(self, fobj, offset=0): """Load bin file into internal buffer. Not needed if source set in constructor. This will overwrite addresses without warning if object was already initialized. @param fobj file name or file-like object @param offset starting address offset """ fread = getattr(fobj, "read", None) if fread is None: f = open(fobj, "rb") fread = f.read fclose = f.close else: fclose = None try: self.frombytes(array('B', asbytes(fread())), offset=offset) finally: if fclose: fclose()
def write_hex_file(self, f, write_start_addr=True): """Write data to file f in HEX format. @param f filename or file-like object for writing @param write_start_addr enable or disable writing start address record to file (enabled by default). If there is no start address in obj, nothing will be written regardless of this setting. """ fwrite = getattr(f, "write", None) if fwrite: fobj = f fclose = None else: fobj = open(f, 'w') fwrite = fobj.write fclose = fobj.close # Translation table for uppercasing hex ascii string. # timeit shows that using hexstr.translate(table) # is faster than hexstr.upper(): # 0.452ms vs. 0.652ms (translate vs. upper) if sys.version_info[0] >= 3: # Python 3 table = bytes(range_l(256)).upper() else: # Python 2 table = ''.join(chr(i).upper() for i in range_g(256)) # start address record if any if self.start_addr and write_start_addr: keys = dict_keys(self.start_addr) keys.sort() bin = array('B', asbytes('\0' * 9)) if keys == ['CS', 'IP']: # Start Segment Address Record bin[0] = 4 # reclen bin[1] = 0 # offset msb bin[2] = 0 # offset lsb bin[3] = 3 # rectyp cs = self.start_addr['CS'] bin[4] = (cs >> 8) & 0x0FF bin[5] = cs & 0x0FF ip = self.start_addr['IP'] bin[6] = (ip >> 8) & 0x0FF bin[7] = ip & 0x0FF bin[8] = (-sum(bin)) & 0x0FF # chksum fwrite(':' + asstr(hexlify(bin.tostring()).translate(table)) + '\n') elif keys == ['EIP']: # Start Linear Address Record bin[0] = 4 # reclen bin[1] = 0 # offset msb bin[2] = 0 # offset lsb bin[3] = 5 # rectyp eip = self.start_addr['EIP'] bin[4] = (eip >> 24) & 0x0FF bin[5] = (eip >> 16) & 0x0FF bin[6] = (eip >> 8) & 0x0FF bin[7] = eip & 0x0FF bin[8] = (-sum(bin)) & 0x0FF # chksum fwrite(':' + asstr(hexlify(bin.tostring()).translate(table)) + '\n') else: if fclose: fclose() raise InvalidStartAddressValueError(start_addr=self.start_addr) # data addresses = dict_keys(self._buf) addresses.sort() addr_len = len(addresses) if addr_len: minaddr = addresses[0] maxaddr = addresses[-1] if maxaddr > 65535: need_offset_record = True else: need_offset_record = False high_ofs = 0 cur_addr = minaddr cur_ix = 0 while cur_addr <= maxaddr: if need_offset_record: bin = array('B', asbytes('\0' * 7)) bin[0] = 2 # reclen bin[1] = 0 # offset msb bin[2] = 0 # offset lsb bin[3] = 4 # rectyp high_ofs = int(cur_addr >> 16) b = divmod(high_ofs, 256) bin[4] = b[0] # msb of high_ofs bin[5] = b[1] # lsb of high_ofs bin[6] = (-sum(bin)) & 0x0FF # chksum fwrite(':' + asstr(hexlify(bin.tostring()).translate(table)) + '\n') while True: # produce one record low_addr = cur_addr & 0x0FFFF # chain_len off by 1 chain_len = min(15, 65535 - low_addr, maxaddr - cur_addr) # search continuous chain stop_addr = cur_addr + chain_len if chain_len: ix = bisect_right( addresses, stop_addr, cur_ix, min(cur_ix + chain_len + 1, addr_len)) chain_len = ix - cur_ix # real chain_len # there could be small holes in the chain # but we will catch them by try-except later # so for big continuous files we will work # at maximum possible speed else: chain_len = 1 # real chain_len bin = array('B', asbytes('\0' * (5 + chain_len))) b = divmod(low_addr, 256) bin[1] = b[0] # msb of low_addr bin[2] = b[1] # lsb of low_addr bin[3] = 0 # rectype try: # if there is small holes we'll catch them for i in range_g(chain_len): bin[4 + i] = self._buf[cur_addr + i] except KeyError: # we catch a hole so we should shrink the chain chain_len = i bin = bin[:5 + i] bin[0] = chain_len bin[4 + chain_len] = (-sum(bin)) & 0x0FF # chksum fwrite(':' + asstr(hexlify(bin.tostring()).translate(table)) + '\n') # adjust cur_addr/cur_ix cur_ix += chain_len if cur_ix < addr_len: cur_addr = addresses[cur_ix] else: cur_addr = maxaddr + 1 break high_addr = int(cur_addr >> 16) if high_addr > high_ofs: break # end-of-file record fwrite(":00000001FF\n") if fclose: fclose()
def _tobinstr_really(self, start, end, pad, size): return asbytes( self._tobinarray_really(start, end, pad, size).tostring())
def _decode_record(self, s, line=0): '''Decode one record of HEX file. @param s line with HEX record. @param line line number (for error messages). @raise EndOfFile if EOF record encountered. ''' s = s.rstrip('\r\n') if not s: return # empty line if s[0] == ':': try: bin = array('B', unhexlify(asbytes(s[1:]))) except (TypeError, ValueError): # this might be raised by unhexlify when odd hexascii digits raise HexRecordError(line=line) length = len(bin) if length < 5: raise HexRecordError(line=line) else: raise HexRecordError(line=line) record_length = bin[0] if length != (5 + record_length): raise RecordLengthError(line=line) addr = bin[1] * 256 + bin[2] record_type = bin[3] if not (0 <= record_type <= 5): raise RecordTypeError(line=line) crc = sum(bin) crc &= 0x0FF if crc != 0: raise RecordChecksumError(line=line) if record_type == 0: # data record addr += self._offset for i in range_g(4, 4 + record_length): if not self._buf.get(addr, None) is None: raise AddressOverlapError(address=addr, line=line) self._buf[addr] = bin[i] addr += 1 # FIXME: addr should be wrapped # BUT after 02 record (at 64K boundary) # and after 04 record (at 4G boundary) elif record_type == 1: # end of file record if record_length != 0: raise EOFRecordError(line=line) raise _EndOfFile elif record_type == 2: # Extended 8086 Segment Record if record_length != 2 or addr != 0: raise ExtendedSegmentAddressRecordError(line=line) self._offset = (bin[4] * 256 + bin[5]) * 16 elif record_type == 4: # Extended Linear Address Record if record_length != 2 or addr != 0: raise ExtendedLinearAddressRecordError(line=line) self._offset = (bin[4] * 256 + bin[5]) * 65536 elif record_type == 3: # Start Segment Address Record if record_length != 4 or addr != 0: raise StartSegmentAddressRecordError(line=line) if self.start_addr: raise DuplicateStartAddressRecordError(line=line) self.start_addr = { 'CS': bin[4] * 256 + bin[5], 'IP': bin[6] * 256 + bin[7], } elif record_type == 5: # Start Linear Address Record if record_length != 4 or addr != 0: raise StartLinearAddressRecordError(line=line) if self.start_addr: raise DuplicateStartAddressRecordError(line=line) self.start_addr = { 'EIP': (bin[4] * 16777216 + bin[5] * 65536 + bin[6] * 256 + bin[7]), }
def _decode_record(self, s, line=0): '''Decode one record of HEX file. @param s line with HEX record. @param line line number (for error messages). @raise EndOfFile if EOF record encountered. ''' s = s.rstrip('\r\n') if not s: return # empty line if s[0] == ':': try: bin = array('B', unhexlify(asbytes(s[1:]))) except (TypeError, ValueError): # this might be raised by unhexlify when odd hexascii digits raise HexRecordError(line=line) length = len(bin) if length < 5: raise HexRecordError(line=line) else: raise HexRecordError(line=line) record_length = bin[0] if length != (5 + record_length): raise RecordLengthError(line=line) addr = bin[1]*256 + bin[2] record_type = bin[3] if not (0 <= record_type <= 5): raise RecordTypeError(line=line) crc = sum(bin) crc &= 0x0FF if crc != 0: raise RecordChecksumError(line=line) if record_type == 0: # data record addr += self._offset for i in xrange(4, 4+record_length): if not self._buf.get(addr, None) is None: raise AddressOverlapError(address=addr, line=line) self._buf[addr] = bin[i] addr += 1 # FIXME: addr should be wrapped # BUT after 02 record (at 64K boundary) # and after 04 record (at 4G boundary) elif record_type == 1: # end of file record if record_length != 0: raise EOFRecordError(line=line) raise _EndOfFile elif record_type == 2: # Extended 8086 Segment Record if record_length != 2 or addr != 0: raise ExtendedSegmentAddressRecordError(line=line) self._offset = (bin[4]*256 + bin[5]) * 16 elif record_type == 4: # Extended Linear Address Record if record_length != 2 or addr != 0: raise ExtendedLinearAddressRecordError(line=line) self._offset = (bin[4]*256 + bin[5]) * 65536 elif record_type == 3: # Start Segment Address Record if record_length != 4 or addr != 0: raise StartSegmentAddressRecordError(line=line) if self.start_addr: raise DuplicateStartAddressRecordError(line=line) self.start_addr = {'CS': bin[4]*256 + bin[5], 'IP': bin[6]*256 + bin[7], } elif record_type == 5: # Start Linear Address Record if record_length != 4 or addr != 0: raise StartLinearAddressRecordError(line=line) if self.start_addr: raise DuplicateStartAddressRecordError(line=line) self.start_addr = {'EIP': (bin[4]*16777216 + bin[5]*65536 + bin[6]*256 + bin[7]), }
def write_hex_file(self, f, write_start_addr=True): """Write data to file f in HEX format. @param f filename or file-like object for writing @param write_start_addr enable or disable writing start address record to file (enabled by default). If there is no start address in obj, nothing will be written regardless of this setting. """ fwrite = getattr(f, "write", None) if fwrite: fobj = f fclose = None else: fobj = open(f, 'w') fwrite = fobj.write fclose = fobj.close # Translation table for uppercasing hex ascii string. # timeit shows that using hexstr.translate(table) # is faster than hexstr.upper(): # 0.452ms vs. 0.652ms (translate vs. upper) if sys.version_info[0] >= 3: table = bytes(range(256)).upper() else: table = ''.join(chr(i).upper() for i in range(256)) # start address record if any if self.start_addr and write_start_addr: keys = self.start_addr.keys() keys.sort() bin = array('B', asbytes('\0'*9)) if keys == ['CS','IP']: # Start Segment Address Record bin[0] = 4 # reclen bin[1] = 0 # offset msb bin[2] = 0 # offset lsb bin[3] = 3 # rectyp cs = self.start_addr['CS'] bin[4] = (cs >> 8) & 0x0FF bin[5] = cs & 0x0FF ip = self.start_addr['IP'] bin[6] = (ip >> 8) & 0x0FF bin[7] = ip & 0x0FF bin[8] = (-sum(bin)) & 0x0FF # chksum fwrite(':' + asstr(hexlify(bin.tostring()).translate(table)) + '\n') elif keys == ['EIP']: # Start Linear Address Record bin[0] = 4 # reclen bin[1] = 0 # offset msb bin[2] = 0 # offset lsb bin[3] = 5 # rectyp eip = self.start_addr['EIP'] bin[4] = (eip >> 24) & 0x0FF bin[5] = (eip >> 16) & 0x0FF bin[6] = (eip >> 8) & 0x0FF bin[7] = eip & 0x0FF bin[8] = (-sum(bin)) & 0x0FF # chksum fwrite(':' + asstr(hexlify(bin.tostring()).translate(table)) + '\n') else: if fclose: fclose() raise InvalidStartAddressValueError(start_addr=self.start_addr) # data addresses = self._buf.keys() addresses.sort() addr_len = len(addresses) if addr_len: minaddr = addresses[0] maxaddr = addresses[-1] if maxaddr > 65535: need_offset_record = True else: need_offset_record = False high_ofs = 0 cur_addr = minaddr cur_ix = 0 while cur_addr <= maxaddr: if need_offset_record: bin = array('B', asbytes('\0'*7)) bin[0] = 2 # reclen bin[1] = 0 # offset msb bin[2] = 0 # offset lsb bin[3] = 4 # rectyp high_ofs = int(cur_addr>>16) b = divmod(high_ofs, 256) bin[4] = b[0] # msb of high_ofs bin[5] = b[1] # lsb of high_ofs bin[6] = (-sum(bin)) & 0x0FF # chksum fwrite(':' + asstr(hexlify(bin.tostring()).translate(table)) + '\n') while True: # produce one record low_addr = cur_addr & 0x0FFFF # chain_len off by 1 chain_len = min(15, 65535-low_addr, maxaddr-cur_addr) # search continuous chain stop_addr = cur_addr + chain_len if chain_len: ix = bisect_right(addresses, stop_addr, cur_ix, min(cur_ix+chain_len+1, addr_len)) chain_len = ix - cur_ix # real chain_len # there could be small holes in the chain # but we will catch them by try-except later # so for big continuous files we will work # at maximum possible speed else: chain_len = 1 # real chain_len bin = array('B', asbytes('\0'*(5+chain_len))) b = divmod(low_addr, 256) bin[1] = b[0] # msb of low_addr bin[2] = b[1] # lsb of low_addr bin[3] = 0 # rectype try: # if there is small holes we'll catch them for i in range(chain_len): bin[4+i] = self._buf[cur_addr+i] except KeyError: # we catch a hole so we should shrink the chain chain_len = i bin = bin[:5+i] bin[0] = chain_len bin[4+chain_len] = (-sum(bin)) & 0x0FF # chksum fwrite(':' + asstr(hexlify(bin.tostring()).translate(table)) + '\n') # adjust cur_addr/cur_ix cur_ix += chain_len if cur_ix < addr_len: cur_addr = addresses[cur_ix] else: cur_addr = maxaddr + 1 break high_addr = int(cur_addr>>16) if high_addr > high_ofs: break # end-of-file record fwrite(":00000001FF\n") if fclose: fclose()
def _tobinstr_really(self, start, end, pad, size): return asbytes(self._tobinarray_really(start, end, pad, size).tostring())