Browse Source

add file conversion

added a script to convert the elf file from gcc to TI Hex in order to
use it with remote flasher etc. requires python to run
fertito
Daniel Poelzleithner 12 years ago
parent
commit
417a74257d
  1. 0
      tools/__init__.py
  2. 320
      tools/elf.py
  3. 278
      tools/memory.py

0
tools/__init__.py

320
tools/elf.py

@ -0,0 +1,320 @@
#!/usr/bin/env python
# $Id: elf.py,v 1.1 2006/04/11 18:35:23 cliechti Exp $
import struct
# ELF object file reader
# (C) 2003 cliechti@gmx.net
# Python license
# size alignment
# Elf32_Addr 4 4 Unsigned program address
# Elf32_Half 2 2 Unsigned medium integer
# Elf32_Off 4 4 Unsigned file offset
# Elf32_Sword 4 4 Signed large integer
# Elf32_Word 4 4 Unsigned large integer
# unsignedchar 1 1 Unsigned small integer
#define EI_NIDENT 16
#~ typedef struct{
#~ unsigned char e_ident[EI_NIDENT];
#~ Elf32_Half e_type;
#~ Elf32_Half e_machine;
#~ Elf32_Word e_version;
#~ Elf32_Addr e_entry;
#~ Elf32_Off e_phoff;
#~ Elf32_Off e_shoff;
#~ Elf32_Word e_flags;
#~ Elf32_Half e_ehsize;
#~ Elf32_Half e_phentsize;
#~ Elf32_Half e_phnum;
#~ Elf32_Half e_shentsize;
#~ Elf32_Half e_shnum;
#~ Elf32_Half e_shstrndx;
#~ } Elf32_Ehdr;
#Section Header
#~ typedef struct {
#~ Elf32_Word sh_name;
#~ Elf32_Word sh_type;
#~ Elf32_Word sh_flags;
#~ Elf32_Addr sh_addr;
#~ Elf32_Off sh_offset;
#~ Elf32_Word sh_size;
#~ Elf32_Word sh_link;
#~ Elf32_Word sh_info;
#~ Elf32_Word sh_addralign;
#~ Elf32_Word sh_entsize;
#~ } Elf32_Shdr;
#~ typedef struct {
#~ Elf32_Word p_type;
#~ Elf32_Off p_offset;
#~ Elf32_Addr p_vaddr;
#~ Elf32_Addr p_paddr;
#~ Elf32_Word p_filesz;
#~ Elf32_Word p_memsz;
#~ Elf32_Word p_flags;
#~ Elf32_Word p_align;
#~ } Elf32_Phdr;
class ELFException(Exception): pass
class ELFSection:
"""read and store a section"""
Elf32_Shdr = "<IIIIIIIIII" #header format
#section types
SHT_NULL = 0
SHT_PROGBITS = 1
SHT_SYMTAB = 2
SHT_STRTAB = 3
SHT_RELA = 4
SHT_HASH = 5
SHT_DYNAMIC = 6
SHT_NOTE = 7
SHT_NOBITS = 8
SHT_REL = 9
SHT_SHLIB = 10
SHT_DYNSYM = 11
SHT_LOPROC = 0x70000000L
SHT_HIPROC = 0x7fffffffL
SHT_LOUSER = 0x80000000L
SHT_HIUSER = 0xffffffffL
#section attribute flags
SHF_WRITE = 0x1
SHF_ALLOC = 0x2
SHF_EXECINSTR = 0x4
SHF_MASKPROC = 0xf0000000L
def __init__(self):
"""creat a new empty section object"""
(self.sh_name, self.sh_type, self.sh_flags, self.sh_addr,
self.sh_offset, self.sh_size, self.sh_link, self.sh_info,
self.sh_addralign, self.sh_entsize) = [0]*10
self.name = None
self.data = None
self.lma = None
def fromString(self, s):
"""get section header from string"""
(self.sh_name, self.sh_type, self.sh_flags, self.sh_addr,
self.sh_offset, self.sh_size, self.sh_link, self.sh_info,
self.sh_addralign, self.sh_entsize) = struct.unpack(self.Elf32_Shdr, s)
def __str__(self):
"""pretty print for debug..."""
return "%s(%s, sh_type=%s, sh_flags=%s, "\
"sh_addr=0x%04x, sh_offset=0x%04x, sh_size=%s, sh_link=%s, "\
"sh_info=%s, sh_addralign=%s, sh_entsize=%s, lma=0x%04x)" % (
self.__class__.__name__,
self.name is not None and "%r" % self.name or "sh_name=%s" % self.sh_name,
self.sh_type, self.sh_flags, self.sh_addr,
self.sh_offset, self.sh_size, self.sh_link, self.sh_info,
self.sh_addralign, self.sh_entsize, self.lma)
class ELFProgramHeader:
"""Store and parse a program header"""
Elf32_Phdr = "<IIIIIIII" #header format
#segmet types
PT_NULL = 0
PT_LOAD = 1
PT_DYNAMIC = 2
PT_INTERP = 3
PT_NOTE = 4
PT_SHLIB = 5
PT_PHDR = 6
PT_LOPROC = 0x70000000L
PT_HIPROC = 0x7fffffffL
#segment flags
PF_R = 0x4 #segment is readable
PF_W = 0x2 #segment is writable
PF_X = 0x1 #segment is executable
def __init__(self):
"""create a new, empty segment/program header"""
(self.p_type, self.p_offset, self.p_vaddr, self.p_paddr,
self.p_filesz, self.p_memsz, self.p_flags, self.p_align) = [0]*8
self.data = None
def fromString(self, s):
"""parse header info from string"""
(self.p_type, self.p_offset, self.p_vaddr, self.p_paddr,
self.p_filesz, self.p_memsz, self.p_flags,
self.p_align) = struct.unpack(self.Elf32_Phdr, s)
def __str__(self):
"""pretty print for debug..."""
return "%s(p_type=%s, p_offset=0x%04x, p_vaddr=0x%04x, p_paddr=0x%04x, "\
"p_filesz=%s, p_memsz=%s, p_flags=%s, "\
"p_align=%s)" % (
self.__class__.__name__,
self.p_type, self.p_offset, self.p_vaddr, self.p_paddr,
self.p_filesz, self.p_memsz, self.p_flags,
self.p_align)
class ELFObject:
"""Object to read and handle an LEF object file"""
#header information
Elf32_Ehdr = "<16sHHIIIIIHHHHHH"
#offsets within e_ident
EI_MAG0 = 0 #File identification
EI_MAG1 = 1 #File identification
EI_MAG2 = 2 #File identification
EI_MAG3 = 3 #File identification
EI_CLASS = 4 #File class
EI_DATA = 5 #Data encoding
EI_VERSION = 6 #File version
EI_PAD = 7 #Start of padding bytes
EI_NIDENT = 16 #Size of e_ident[]
#elf file type flags
ET_NONE = 0 #No file type
ET_REL = 1 #Relocatable file
ET_EXEC = 2 #Executable file
ET_DYN = 3 #Shared object file
ET_CORE = 4 #Core file
ET_LOPROC = 0xff00 #Processor-specific
ET_HIPROC = 0xffff #Processor-specific
#ELF format
ELFCLASSNONE = 0 #Invalid class
ELFCLASS32 = 1 #32-bit objects
ELFCLASS64 = 2 #64-bit objects
#encoding
ELFDATANONE = 0 #Invalid data encoding
ELFDATA2LSB = 1 #See below
ELFDATA2MSB = 2 #See below
def __init__(self):
"""create a new elf object"""
(self.e_ident, self.e_type, self.e_machine, self.e_version,
self.e_entry, self.e_phoff, self.e_shoff,
self.e_flags, self.e_ehsize, self.e_phentsize, self.e_phnum,
self.e_shentsize, self.e_shnum, self.e_shstrndx) = [0]*14
def fromFile(self, fileobj):
"""read all relevant data from fileobj.
the file must be seekable"""
#get file header
(self.e_ident, self.e_type, self.e_machine, self.e_version,
self.e_entry, self.e_phoff, self.e_shoff,
self.e_flags, self.e_ehsize, self.e_phentsize, self.e_phnum,
self.e_shentsize, self.e_shnum, self.e_shstrndx) = struct.unpack(
self.Elf32_Ehdr, fileobj.read(struct.calcsize(self.Elf32_Ehdr)))
#verify if its a known format and realy an ELF file
if self.e_ident[0:4] != '\x7fELF' and\
self.e_ident[self.EI_CLASS] != self.ELFCLASS32 and\
self.e_ident[self.EI_DATA] != self.ELFDATA2LSB and\
self.e_ident[self.EI_VERSION] != 1:
raise ELFException("Not a valid ELF file")
#load programm headers
self.programmheaders = []
if self.e_phnum:
#load program headers
fileobj.seek(self.e_phoff)
for sectionnum in range(self.e_phnum):
shdr = (fileobj.read(self.e_phentsize) + '\0'* struct.calcsize(ELFProgramHeader.Elf32_Phdr))[0:struct.calcsize(ELFProgramHeader.Elf32_Phdr)]
psection = ELFProgramHeader()
psection.fromString(shdr)
if psection.p_offset: #skip if section has invalid offset in file
self.programmheaders.append(psection)
#~ #get the segment data from the file for each prg header
#~ for phdr in self.programmheaders:
#~ fileobj.seek(phdr.p_offset)
#~ phdr.data = fileobj.read(phdr.p_filesz)
#~ #pad if needed
#~ if phdr.p_filesz < phdr.p_memsz:
#~ phdr.data = phdr.data + '\0' * (phdr.p_memsz-phdr.p_filesz)
#load sections
self.sections = []
fileobj.seek(self.e_shoff)
for sectionnum in range(self.e_shnum):
shdr = (fileobj.read(self.e_shentsize) + '\0'* struct.calcsize(ELFSection.Elf32_Shdr))[0:struct.calcsize(ELFSection.Elf32_Shdr)]
elfsection = ELFSection()
elfsection.fromString(shdr)
self.sections.append(elfsection)
#load data for all sections
for section in self.sections:
fileobj.seek(section.sh_offset)
data = fileobj.read(section.sh_size)
section.data = data
if section.sh_type == ELFSection.SHT_STRTAB:
section.values = data.split('\0')
section.lma = self.getLMA(section)
#get section names
for section in self.sections:
start = self.sections[self.e_shstrndx].data[section.sh_name:]
section.name = start.split('\0')[0]
def getSection(self, name):
"""get section by name"""
for section in self.sections:
if section.name == '.text':
return section
def getProgrammableSections(self):
"""get all program headers that are marked as executable and
have suitable attributes to be code"""
res = []
for p in self.programmheaders:
#~ print p
#~ if section.sh_flags & self.SHF_ALLOC and section.name not in ('.data', '.data1', '.bss'):
#~ if p.p_type == ELFProgramHeader.PT_LOAD:# and p.p_paddr == p.p_vaddr and p.p_flags & ELFProgramHeader.PF_X:
if p.p_type == ELFProgramHeader.PT_LOAD:
res.append(p)
return res
def getLMA(self, section):
#magic load memory address calculation ;-)
for p in self.programmheaders:
if (p.p_paddr != 0 and \
p.p_type == ELFProgramHeader.PT_LOAD and \
p.p_vaddr != p.p_paddr and \
p.p_vaddr <= section.sh_addr and \
(p.p_vaddr + p.p_memsz >= section.sh_addr + section.sh_size) \
and (not (section.sh_flags & ELFSection.SHF_ALLOC and section.sh_type != ELFSection.SHT_NOBITS) \
or (p.p_offset <= section.sh_offset \
and (p.p_offset + p.p_filesz >= section.sh_offset + section.sh_size)))):
return section.sh_addr + p.p_paddr - p.p_vaddr
return section.sh_addr
def getSections(self):
"""get sections relevant for the application"""
res = []
for section in self.sections:
if section.sh_flags & ELFSection.SHF_ALLOC and section.sh_type != ELFSection.SHT_NOBITS:
res.append(section)
return res
def __str__(self):
"""pretty print for debug..."""
return "%s(self.e_type=%r, self.e_machine=%r, self.e_version=%r, sections=%r)" % (
self.__class__.__name__,
self.e_type, self.e_machine, self.e_version,
[section.name for section in self.sections])
if __name__ == '__main__':
print "This is only a module test!"
elf = ELFObject()
elf.fromFile(open("test.elf"))
if elf.e_type != ELFObject.ET_EXEC:
raise Exception("No executable")
print elf
#~ print repr(elf.getSection('.text').data)
#~ print [(s.name, hex(s.sh_addr)) for s in elf.getSections()]
print "-"*20
for p in elf.sections: print p
print "-"*20
for p in elf.getSections(): print p
print "-"*20
for p in elf.getProgrammableSections(): print p

278
tools/memory.py

@ -0,0 +1,278 @@
#!/usr/bin/env python
# $Id: memory.py,v 1.4.2.1 2009/05/19 09:07:21 rlim Exp $
import sys
import elf
DEBUG = 0
class FileFormatError(IOError):
"""file is not in the expected format"""
class Segment:
"""store a string with memory contents along with its startaddress"""
def __init__(self, startaddress = 0, data=None):
if data is None:
self.data = ''
else:
self.data = data
self.startaddress = startaddress
def __getitem__(self, index):
return self.data[index]
def __len__(self):
return len(self.data)
def __repr__(self):
return "Segment(startaddress=0x%04x, data=%r)" % (self.startaddress, self.data)
class Memory:
"""represent memory contents. with functions to load files"""
def __init__(self, filename=None):
self.segments = []
if filename:
self.filename = filename
self.loadFile(filename)
def append(self, seg):
self.segments.append(seg)
def __getitem__(self, index):
return self.segments[index]
def __len__(self):
return len(self.segments)
def __repr__(self):
return "Memory:\n%s" % ('\n'.join([repr(seg) for seg in self.segments]),)
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
def loadIHex(self, file):
"""load data from a (opened) file in Intel-HEX format"""
segmentdata = []
currentAddr = 0
startAddr = 0
extendAddr = 0
lines = file.readlines()
for l in lines:
if not l.strip(): continue #skip empty lines
if l[0] != ':': raise FileFormatError("line not valid intel hex data: '%s...'" % l[0:10])
l = l.strip() #fix CR-LF issues...
length = int(l[1:3],16)
address = int(l[3:7],16) + extendAddr
type = int(l[7:9],16)
check = int(l[-2:],16)
if type == 0x00:
if currentAddr != address:
if segmentdata:
self.segments.append( Segment(startAddr, ''.join(segmentdata)) )
startAddr = currentAddr = address
segmentdata = []
for i in range(length):
segmentdata.append( chr(int(l[9+2*i:11+2*i],16)) )
currentAddr = length + currentAddr
elif type == 0x02:
extendAddr = int(l[9:13],16) << 4
elif type in (0x01, 0x03, 0x04, 0x05):
pass
else:
sys.stderr.write("Ignored unknown field (type 0x%02x) in ihex file.\n" % type)
if segmentdata:
self.segments.append( Segment(startAddr, ''.join(segmentdata)) )
def loadTIText(self, file):
"""load data from a (opened) file in TI-Text format"""
startAddr = 0
segmentdata = []
#Convert data for MSP430, TXT-File is parsed line by line
for line in file: #Read one line
if not line: break #EOF
l = line.strip()
if l[0] == 'q': break
elif l[0] == '@': #if @ => new address => send frame and set new addr.
#create a new segment
if segmentdata:
self.segments.append( Segment(startAddr, ''.join(segmentdata)) )
startAddr = int(l[1:],16)
segmentdata = []
else:
for i in l.split():
try:
segmentdata.append(chr(int(i,16)))
except ValueError, e:
raise FileFormatError('File is no valid TI-Text (%s)' % e)
if segmentdata:
self.segments.append( Segment(startAddr, ''.join(segmentdata)) )
def loadELF(self, file):
"""load data from a (opened) file in ELF object format.
File must be seekable"""
obj = elf.ELFObject()
obj.fromFile(file)
if obj.e_type != elf.ELFObject.ET_EXEC:
raise Exception("No executable")
for section in obj.getSections():
if DEBUG:
sys.stderr.write("ELF section %s at 0x%04x %d bytes\n" % (section.name, section.lma, len(section.data)))
if len(section.data):
self.segments.append( Segment(section.lma, section.data) )
def loadFile(self, filename, fileobj=None):
"""fill memory with the contents of a file. file type is determined from extension"""
close = 0
if fileobj is None:
fileobj = open(filename, "rb")
close = 1
try:
#first check extension
try:
if filename[-4:].lower() == '.txt':
self.loadTIText(fileobj)
return
elif filename[-4:].lower() in ('.a43', '.hex'):
self.loadIHex(fileobj)
return
except FileFormatError:
pass #do contents based detection below
#then do a contents based detection
try:
self.loadELF(fileobj)
except elf.ELFException:
fileobj.seek(0)
try:
self.loadIHex(fileobj)
except FileFormatError:
fileobj.seek(0)
try:
self.loadTIText(fileobj)
except FileFormatError:
raise FileFormatError('file could not be loaded (not ELF, Intel-Hex, or TI-Text)')
finally:
if close:
fileobj.close()
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
def saveIHex(self, filelike):
"""write a string containing intel hex to given file object"""
noeof=0
for seg in self.segments:
address = seg.startaddress
data = seg.data
start = 0
while start<len(data):
end = start + 16
if end > len(data): end = len(data)
filelike.write(self._ihexline(address, data[start:end]))
start += 16
address += 16
filelike.write(self._ihexline(0, [], end=1)) #append no data but an end line
def _ihexline(self, address, buffer, end=0):
"""internal use: generate a line with intel hex encoded data"""
out = []
if end:
type = 1
else:
type = 0
out.append( ':%02X%04X%02X' % (len(buffer),address&0xffff,type) )
sum = len(buffer) + ((address>>8)&255) + (address&255) + (type&255)
for b in [ord(x) for x in buffer]:
out.append('%02X' % (b&255) )
sum += b&255
out.append('%02X\r\n' %( (-sum)&255))
return ''.join(out)
def saveTIText(self, filelike):
"""output TI-Text to given file object"""
for segment in self.segments:
filelike.write("@%04x\n" % segment.startaddress)
for i in range(0, len(segment.data), 16):
filelike.write("%s\n" % " ".join(["%02x" % ord(x) for x in segment.data[i:i+16]]))
filelike.write("q\n")
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
def getMemrange(self, fromadr, toadr):
"""get a range of bytes from the memory. unavailable values are filled with 0xff."""
res = ''
toadr = toadr + 1 #python indexes are excluding end, so include it
while fromadr < toadr:
for seg in self.segments:
segend = seg.startaddress + len(seg.data)
if seg.startaddress <= fromadr and fromadr < segend:
if toadr > segend: #not all data in segment
catchlength = segend - fromadr
else:
catchlength = toadr - fromadr
res = res + seg.data[fromadr-seg.startaddress : fromadr-seg.startaddress+catchlength]
fromadr = fromadr + catchlength #adjust start
if len(res) >= toadr-fromadr:
break #return res
else: #undefined memory is filled with 0xff
res = res + chr(255)
fromadr = fromadr + 1 #adjust start
return res
def getMem(self, address, size):
"""get a range of bytes from the memory. a ValueError is raised if
unavailable addresses are tried to read"""
data = []
for seg in self.segments:
#~ print "0x%04x " * 2 % (seg.startaddress, seg.startaddress + len(seg.data))
if seg.startaddress <= address and seg.startaddress + len(seg.data) >= address:
#segment contains data in the address range
offset = address - seg.startaddress
length = min(len(seg.data)-offset, size)
data.append(seg.data[offset:offset+length])
address += length
value = ''.join(data)
if len(value) != size:
raise ValueError("could not collect the requested data")
return value
def setMem(self, address, contents):
"""write a range of bytes to the memory. a segment covering the address
range to be written has to be existent. a ValueError is raised if not
all data could be written (attention: a part of the data may have been
written!)"""
#~ print "%04x: %r" % (address, contents)
for seg in self.segments:
#~ print "0x%04x " * 3 % (address, seg.startaddress, seg.startaddress + len(seg.data))
if seg.startaddress <= address and seg.startaddress + len(seg.data) >= address:
#segment contains data in the address range
offset = address - seg.startaddress
length = min(len(seg.data)-offset, len(contents))
seg.data = seg.data[:offset] + contents[:length] + seg.data[offset+length:]
contents = contents[length:] #cut away what is used
if not contents: return #stop if done
address += length
raise ValueError("could not write all data")
if __name__ == "__main__":
from optparse import OptionParser
parser = OptionParser()
parser.add_option("-i", "--input", dest="input",
help="read file")
parser.add_option("-o", "--output",
dest="output", help="output file")
parser.add_option("-f", "--format",
dest="format", help="output format [titxt,ihex]",
choices=["titxt", "ihex"],
default="titxt")
(options, args) = parser.parse_args()
if not options.output or not options.input:
parser.error("input and output are required")
mem = Memory()
mem.loadFile(options.input)
fp = open(options.output, "w")
if options.format == "titxt":
print "convert to TI Hex"
mem.saveTIText(fp)
elif options.format == "ihex":
print "convert to Intel Hex"
mem.saveIHex(fp)
fp.close()
Loading…
Cancel
Save