esp-idf/tools/unit-test-app/tools/CreateSectionTable.py

164 lines
5.9 KiB
Python

# This file is used to process section data generated by `objdump -s`
import re
class Section(object):
"""
One Section of section table. contains info about section name, address and raw data
"""
SECTION_START_PATTERN = re.compile(b'Contents of section (.+?):')
DATA_PATTERN = re.compile(b'([0-9a-f]{4,8})')
def __init__(self, name, start_address, data):
self.name = name
self.start_address = start_address
self.data = data
def __contains__(self, item):
""" check if the section name and address match this section """
if (item['section'] == self.name or item['section'] == 'any') \
and (self.start_address <= item['address'] < (self.start_address + len(self.data))):
return True
else:
return False
def __getitem__(self, item):
"""
process slice.
convert absolute address to relative address in current section and return slice result
"""
if isinstance(item, int):
return self.data[item - self.start_address]
elif isinstance(item, slice):
start = item.start if item.start is None else item.start - self.start_address
stop = item.stop if item.stop is None else item.stop - self.start_address
return self.data[start:stop]
return self.data[item]
def __str__(self):
return '%s [%08x - %08x]' % (self.name, self.start_address, self.start_address + len(self.data))
__repr__ = __str__
@classmethod
def parse_raw_data(cls, raw_data):
"""
process raw data generated by `objdump -s`, create section and return un-processed lines
:param raw_data: lines of raw data generated by `objdump -s`
:return: one section, un-processed lines
"""
name = ''
data = ''
start_address = 0
# first find start line
for i, line in enumerate(raw_data):
if b'Contents of section ' in line: # do strcmp first to speed up
match = cls.SECTION_START_PATTERN.search(line)
if match is not None:
name = match.group(1)
raw_data = raw_data[i + 1:]
break
else:
# do some error handling
raw_data = [b''] # add a dummy first data line
def process_data_line(line_to_process):
# first remove the ascii part
hex_part = line_to_process.split(b' ')[0]
# process rest part
data_list = cls.DATA_PATTERN.findall(hex_part)
try:
_address = int(data_list[0], base=16)
except IndexError:
_address = -1
def hex_to_str(hex_data):
if len(hex_data) % 2 == 1:
hex_data = b'0' + hex_data # append zero at the beginning
_length = len(hex_data)
return ''.join([chr(int(hex_data[_i:_i + 2], base=16))
for _i in range(0, _length, 2)])
return _address, ''.join([hex_to_str(x) for x in data_list[1:]])
# handle first line:
address, _data = process_data_line(raw_data[0])
if address != -1:
start_address = address
data += _data
raw_data = raw_data[1:]
for i, line in enumerate(raw_data):
address, _data = process_data_line(line)
if address == -1:
raw_data = raw_data[i:]
break
else:
data += _data
else:
# do error handling
raw_data = []
section = cls(name, start_address, data) if start_address != -1 else None
unprocessed_data = None if len(raw_data) == 0 else raw_data
return section, unprocessed_data
class SectionTable(object):
""" elf section table """
def __init__(self, file_name):
with open(file_name, 'rb') as f:
raw_data = f.readlines()
self.table = []
while raw_data:
section, raw_data = Section.parse_raw_data(raw_data)
self.table.append(section)
def get_unsigned_int(self, section, address, size=4, endian='LE'):
"""
get unsigned int from section table
:param section: section name; use "any" will only match with address
:param address: start address
:param size: size in bytes
:param endian: LE or BE
:return: int or None
"""
if address % 4 != 0 or size % 4 != 0:
print('warning: try to access without 4 bytes aligned')
key = {'address': address, 'section': section}
for section in self.table:
if key in section:
tmp = section[address:address + size]
value = 0
for i in range(size):
if endian == 'LE':
value += ord(tmp[i]) << (i * 8)
elif endian == 'BE':
value += ord(tmp[i]) << ((size - i - 1) * 8)
else:
print('only support LE or BE for parameter endian')
assert False
break
else:
value = None
return value
def get_string(self, section, address):
"""
get string ('\0' terminated) from section table
:param section: section name; use "any" will only match with address
:param address: start address
:return: string or None
"""
value = None
key = {'address': address, 'section': section}
for section in self.table:
if key in section:
value = section[address:]
for i, c in enumerate(value):
if c == '\0':
value = value[:i]
break
break
return value