#!/usr/bin/env python3
#
# Copyright (C) 2018 ETH Zurich, University of Bologna and GreenWaves Technologies
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
#
# Authors: Germain Haugou, ETH (germain.haugou@iis.ee.ethz.ch)
#
from pathlib import Path
from typing import Union
from elftools.elf.elffile import ELFFile
import os
import os.path
import struct
import logging
[docs]class ElfParser(object):
"""
A helper class to parse ELF binaries and extract the relevant segments for preloading as well as the start address.
"""
def __init__(self, verbose=False):
self.binaries = []
self.mem = {}
self.verbose = verbose
self.areas = []
logging.info('Created stimuli generator')
[docs] def get_entry(self):
"""
Return the entry point of the ELF binary (the address where the core should start execution).
Returns:
int: The entry point address as an integer.
"""
with open(self.binaries[0], 'rb') as file:
elffile = ELFFile(file)
return elffile.header['e_entry']
[docs] def add_binary(self, binary: Union[str, os.PathLike]):
"""
Add an additional binary to the ElfParser instance.
All binaries added will be parsed together when `parse_binaries()` is called.
Args:
binary: A path to the binary to be added to the parser instance.
Returns:
"""
logging.info('Added binary: %s', binary)
self.binaries.append(binary)
[docs] def parse_binaries(self, word_width):
"""
Parses the added binaries and returns a dictionary with addr, data pairs.
The word_width determines how many bytes are agregated to a single entry in the returned dictionary.
E.g. calling 'stim_gen.parse_binaries(4)' will generates a dictionary were each key is a 4-byte aligned
address with corresponding 4 bytes of data. The byte ordering is little endian.
Args:
word_width (int): The width of one word in bytes
Returns:
Dict[int, int] A dictionary of address data pairs
"""
self.__parse_binaries(word_width)
return self.mem
def __add_mem_word(self, base, size, data, width):
aligned_base = base & ~(width - 1)
shift = base - aligned_base
iter_size = width - shift
if iter_size > size:
iter_size = size
value = self.mem.get(str(aligned_base))
if value is None:
value = 0
value &= ~(((1 << width) - 1) << (shift * 8))
value |= int.from_bytes(data[0:iter_size], byteorder='little') << (shift * 8)
self.mem[str(aligned_base)] = value
return iter_size
def __add_mem(self, base, size, data, width):
while size > 0:
iter_size = self.__add_mem_word(base, size, data, width)
size -= iter_size
base += iter_size
data = data[iter_size:]
def __gen_stim_slm(self, filename, width):
logging.info(' Generating to file: ' + filename)
try:
os.makedirs(os.path.dirname(filename))
except:
pass
with open(filename, 'w') as file:
for key in sorted(self.mem.keys()):
file.write('%X_%0*X\n' % (int(key), width * 2, self.mem.get(key)))
def __parse_binaries(self, width):
self.mem = {}
for binary in self.binaries:
with open(binary, 'rb') as file:
elffile = ELFFile(file)
for segment in elffile.iter_segments():
if segment['p_type'] == 'PT_LOAD':
data = segment.data()
addr = segment['p_paddr']
size = len(data)
load = True
if len(self.areas) != 0:
load = False
for area in self.areas:
if addr >= area[0] and addr + size <= area[1]:
load = True
break
if load:
logging.info(' Handling section (base: 0x%x, size: 0x%x)' % (addr, size))
self.__add_mem(addr, size, data, width)
if segment['p_filesz'] < segment['p_memsz']:
addr = segment['p_paddr'] + segment['p_filesz']
size = segment['p_memsz'] - segment['p_filesz']
logging.info(' Init section to 0 (base: 0x%x, size: 0x%x)' % (addr, size))
self.__add_mem(addr, size, [0] * size, width)
else:
logging.info(' Bypassing section (base: 0x%x, size: 0x%x)' % (addr, size))
[docs] def gen_stim_slm_64(self, stim_file):
self.__parse_binaries(8)
self.__gen_stim_slm(stim_file, 8)
[docs] def gen_stim_bin(self, stim_file):
self.__parse_binaries(1)
try:
os.makedirs(os.path.dirname(stim_file))
except:
pass
with open(stim_file, 'wb') as file:
prev_addr = None
for key in sorted(self.mem.keys()):
addr = int(key)
if prev_addr is not None:
while prev_addr != addr - 1:
file.write(struct.pack('B', 0))
prev_addr += 1
prev_addr = addr
file.write(struct.pack('B', int(self.mem.get(key))))