563 lines
		
	
	
		
			19 KiB
		
	
	
	
		
			Python
		
	
	
	
			
		
		
	
	
			563 lines
		
	
	
		
			19 KiB
		
	
	
	
		
			Python
		
	
	
	
# SPDX-License-Identifier: GPL-2.0+
 | 
						|
# Copyright (c) 2016 Google, Inc
 | 
						|
# Written by Simon Glass <sjg@chromium.org>
 | 
						|
#
 | 
						|
# Handle various things related to ELF images
 | 
						|
#
 | 
						|
 | 
						|
from collections import namedtuple, OrderedDict
 | 
						|
import io
 | 
						|
import os
 | 
						|
import re
 | 
						|
import shutil
 | 
						|
import struct
 | 
						|
import tempfile
 | 
						|
 | 
						|
from u_boot_pylib import command
 | 
						|
from u_boot_pylib import tools
 | 
						|
from u_boot_pylib import tout
 | 
						|
 | 
						|
ELF_TOOLS = True
 | 
						|
try:
 | 
						|
    from elftools.elf.elffile import ELFFile
 | 
						|
    from elftools.elf.elffile import ELFError
 | 
						|
    from elftools.elf.sections import SymbolTableSection
 | 
						|
except:  # pragma: no cover
 | 
						|
    ELF_TOOLS = False
 | 
						|
 | 
						|
# BSYM in little endian, keep in sync with include/binman_sym.h
 | 
						|
BINMAN_SYM_MAGIC_VALUE = 0x4d595342
 | 
						|
 | 
						|
# Information about an EFL symbol:
 | 
						|
# section (str): Name of the section containing this symbol
 | 
						|
# address (int): Address of the symbol (its value)
 | 
						|
# size (int): Size of the symbol in bytes
 | 
						|
# weak (bool): True if the symbol is weak
 | 
						|
# offset (int or None): Offset of the symbol's data in the ELF file, or None if
 | 
						|
#   not known
 | 
						|
Symbol = namedtuple('Symbol', ['section', 'address', 'size', 'weak', 'offset'])
 | 
						|
 | 
						|
# Information about an ELF file:
 | 
						|
#    data: Extracted program contents of ELF file (this would be loaded by an
 | 
						|
#           ELF loader when reading this file
 | 
						|
#    load: Load address of code
 | 
						|
#    entry: Entry address of code
 | 
						|
#    memsize: Number of bytes in memory occupied by loading this ELF file
 | 
						|
ElfInfo = namedtuple('ElfInfo', ['data', 'load', 'entry', 'memsize'])
 | 
						|
 | 
						|
 | 
						|
def GetSymbols(fname, patterns):
 | 
						|
    """Get the symbols from an ELF file
 | 
						|
 | 
						|
    Args:
 | 
						|
        fname: Filename of the ELF file to read
 | 
						|
        patterns: List of regex patterns to search for, each a string
 | 
						|
 | 
						|
    Returns:
 | 
						|
        None, if the file does not exist, or Dict:
 | 
						|
          key: Name of symbol
 | 
						|
          value: Hex value of symbol
 | 
						|
    """
 | 
						|
    stdout = tools.run('objdump', '-t', fname)
 | 
						|
    lines = stdout.splitlines()
 | 
						|
    if patterns:
 | 
						|
        re_syms = re.compile('|'.join(patterns))
 | 
						|
    else:
 | 
						|
        re_syms = None
 | 
						|
    syms = {}
 | 
						|
    syms_started = False
 | 
						|
    for line in lines:
 | 
						|
        if not line or not syms_started:
 | 
						|
            if 'SYMBOL TABLE' in line:
 | 
						|
                syms_started = True
 | 
						|
            line = None  # Otherwise code coverage complains about 'continue'
 | 
						|
            continue
 | 
						|
        if re_syms and not re_syms.search(line):
 | 
						|
            continue
 | 
						|
 | 
						|
        space_pos = line.find(' ')
 | 
						|
        value, rest = line[:space_pos], line[space_pos + 1:]
 | 
						|
        flags = rest[:7]
 | 
						|
        parts = rest[7:].split()
 | 
						|
        section, size =  parts[:2]
 | 
						|
        if len(parts) > 2:
 | 
						|
            name = parts[2] if parts[2] != '.hidden' else parts[3]
 | 
						|
            syms[name] = Symbol(section, int(value, 16), int(size, 16),
 | 
						|
                                flags[1] == 'w', None)
 | 
						|
 | 
						|
    # Sort dict by address
 | 
						|
    return OrderedDict(sorted(syms.items(), key=lambda x: x[1].address))
 | 
						|
 | 
						|
def _GetFileOffset(elf, addr):
 | 
						|
    """Get the file offset for an address
 | 
						|
 | 
						|
    Args:
 | 
						|
        elf (ELFFile): ELF file to check
 | 
						|
        addr (int): Address to search for
 | 
						|
 | 
						|
    Returns
 | 
						|
        int: Offset of that address in the ELF file, or None if not valid
 | 
						|
    """
 | 
						|
    for seg in elf.iter_segments():
 | 
						|
        seg_end = seg['p_vaddr'] + seg['p_filesz']
 | 
						|
        if seg.header['p_type'] == 'PT_LOAD':
 | 
						|
            if addr >= seg['p_vaddr'] and addr < seg_end:
 | 
						|
                return addr - seg['p_vaddr'] + seg['p_offset']
 | 
						|
 | 
						|
def GetFileOffset(fname, addr):
 | 
						|
    """Get the file offset for an address
 | 
						|
 | 
						|
    Args:
 | 
						|
        fname (str): Filename of ELF file to check
 | 
						|
        addr (int): Address to search for
 | 
						|
 | 
						|
    Returns
 | 
						|
        int: Offset of that address in the ELF file, or None if not valid
 | 
						|
    """
 | 
						|
    if not ELF_TOOLS:
 | 
						|
        raise ValueError("Python: No module named 'elftools'")
 | 
						|
    with open(fname, 'rb') as fd:
 | 
						|
        elf = ELFFile(fd)
 | 
						|
        return _GetFileOffset(elf, addr)
 | 
						|
 | 
						|
def GetSymbolFromAddress(fname, addr):
 | 
						|
    """Get the symbol at a particular address
 | 
						|
 | 
						|
    Args:
 | 
						|
        fname (str): Filename of ELF file to check
 | 
						|
        addr (int): Address to search for
 | 
						|
 | 
						|
    Returns:
 | 
						|
        str: Symbol name, or None if no symbol at that address
 | 
						|
    """
 | 
						|
    if not ELF_TOOLS:
 | 
						|
        raise ValueError("Python: No module named 'elftools'")
 | 
						|
    with open(fname, 'rb') as fd:
 | 
						|
        elf = ELFFile(fd)
 | 
						|
        syms = GetSymbols(fname, None)
 | 
						|
    for name, sym in syms.items():
 | 
						|
        if sym.address == addr:
 | 
						|
            return name
 | 
						|
 | 
						|
def GetSymbolFileOffset(fname, patterns):
 | 
						|
    """Get the symbols from an ELF file
 | 
						|
 | 
						|
    Args:
 | 
						|
        fname: Filename of the ELF file to read
 | 
						|
        patterns: List of regex patterns to search for, each a string
 | 
						|
 | 
						|
    Returns:
 | 
						|
        None, if the file does not exist, or Dict:
 | 
						|
          key: Name of symbol
 | 
						|
          value: Hex value of symbol
 | 
						|
    """
 | 
						|
    if not ELF_TOOLS:
 | 
						|
        raise ValueError("Python: No module named 'elftools'")
 | 
						|
 | 
						|
    syms = {}
 | 
						|
    with open(fname, 'rb') as fd:
 | 
						|
        elf = ELFFile(fd)
 | 
						|
 | 
						|
        re_syms = re.compile('|'.join(patterns))
 | 
						|
        for section in elf.iter_sections():
 | 
						|
            if isinstance(section, SymbolTableSection):
 | 
						|
                for symbol in section.iter_symbols():
 | 
						|
                    if not re_syms or re_syms.search(symbol.name):
 | 
						|
                        addr = symbol.entry['st_value']
 | 
						|
                        syms[symbol.name] = Symbol(
 | 
						|
                            section.name, addr, symbol.entry['st_size'],
 | 
						|
                            symbol.entry['st_info']['bind'] == 'STB_WEAK',
 | 
						|
                            _GetFileOffset(elf, addr))
 | 
						|
 | 
						|
    # Sort dict by address
 | 
						|
    return OrderedDict(sorted(syms.items(), key=lambda x: x[1].address))
 | 
						|
 | 
						|
def GetSymbolAddress(fname, sym_name):
 | 
						|
    """Get a value of a symbol from an ELF file
 | 
						|
 | 
						|
    Args:
 | 
						|
        fname: Filename of the ELF file to read
 | 
						|
        patterns: List of regex patterns to search for, each a string
 | 
						|
 | 
						|
    Returns:
 | 
						|
        Symbol value (as an integer) or None if not found
 | 
						|
    """
 | 
						|
    syms = GetSymbols(fname, [sym_name])
 | 
						|
    sym = syms.get(sym_name)
 | 
						|
    if not sym:
 | 
						|
        return None
 | 
						|
    return sym.address
 | 
						|
 | 
						|
def GetPackString(sym, msg):
 | 
						|
    """Get the struct.pack/unpack string to use with a given symbol
 | 
						|
 | 
						|
    Args:
 | 
						|
        sym (Symbol): Symbol to check. Only the size member is checked
 | 
						|
        @msg (str): String which indicates the entry being processed, used for
 | 
						|
            errors
 | 
						|
 | 
						|
    Returns:
 | 
						|
        str: struct string to use, .e.g. '<I'
 | 
						|
 | 
						|
    Raises:
 | 
						|
        ValueError: Symbol has an unexpected size
 | 
						|
    """
 | 
						|
    if sym.size == 4:
 | 
						|
        return '<I'
 | 
						|
    elif sym.size == 8:
 | 
						|
        return '<Q'
 | 
						|
    else:
 | 
						|
        raise ValueError('%s has size %d: only 4 and 8 are supported' %
 | 
						|
                         (msg, sym.size))
 | 
						|
 | 
						|
def GetSymbolOffset(elf_fname, sym_name, base_sym=None):
 | 
						|
    """Read the offset of a symbol compared to base symbol
 | 
						|
 | 
						|
    This is useful for obtaining the value of a single symbol relative to the
 | 
						|
    base of a binary blob.
 | 
						|
 | 
						|
    Args:
 | 
						|
        elf_fname: Filename of the ELF file to read
 | 
						|
        sym_name (str): Name of symbol to read
 | 
						|
        base_sym (str): Base symbol to sue to calculate the offset (or None to
 | 
						|
            use '__image_copy_start'
 | 
						|
 | 
						|
    Returns:
 | 
						|
        int: Offset of the symbol relative to the base symbol
 | 
						|
    """
 | 
						|
    if not base_sym:
 | 
						|
        base_sym = '__image_copy_start'
 | 
						|
    fname = tools.get_input_filename(elf_fname)
 | 
						|
    syms = GetSymbols(fname, [base_sym, sym_name])
 | 
						|
    base = syms[base_sym].address
 | 
						|
    val = syms[sym_name].address
 | 
						|
    return val - base
 | 
						|
 | 
						|
def LookupAndWriteSymbols(elf_fname, entry, section, is_elf=False,
 | 
						|
                          base_sym=None):
 | 
						|
    """Replace all symbols in an entry with their correct values
 | 
						|
 | 
						|
    The entry contents is updated so that values for referenced symbols will be
 | 
						|
    visible at run time. This is done by finding out the symbols offsets in the
 | 
						|
    entry (using the ELF file) and replacing them with values from binman's data
 | 
						|
    structures.
 | 
						|
 | 
						|
    Args:
 | 
						|
        elf_fname: Filename of ELF image containing the symbol information for
 | 
						|
            entry
 | 
						|
        entry: Entry to process
 | 
						|
        section: Section which can be used to lookup symbol values
 | 
						|
        base_sym: Base symbol marking the start of the image
 | 
						|
    """
 | 
						|
    if not base_sym:
 | 
						|
        base_sym = '__image_copy_start'
 | 
						|
    fname = tools.get_input_filename(elf_fname)
 | 
						|
    syms = GetSymbols(fname, ['image', 'binman'])
 | 
						|
    if is_elf:
 | 
						|
        if not ELF_TOOLS:
 | 
						|
            msg = ("Section '%s': entry '%s'" %
 | 
						|
                   (section.GetPath(), entry.GetPath()))
 | 
						|
            raise ValueError(f'{msg}: Cannot write symbols to an ELF file without Python elftools')
 | 
						|
        new_syms = {}
 | 
						|
        with open(fname, 'rb') as fd:
 | 
						|
            elf = ELFFile(fd)
 | 
						|
            for name, sym in syms.items():
 | 
						|
                offset = _GetFileOffset(elf, sym.address)
 | 
						|
                new_syms[name] = Symbol(sym.section, sym.address, sym.size,
 | 
						|
                                        sym.weak, offset)
 | 
						|
            syms = new_syms
 | 
						|
 | 
						|
    if not syms:
 | 
						|
        tout.debug('LookupAndWriteSymbols: no syms')
 | 
						|
        return
 | 
						|
    base = syms.get(base_sym)
 | 
						|
    if not base and not is_elf:
 | 
						|
        tout.debug('LookupAndWriteSymbols: no base')
 | 
						|
        return
 | 
						|
    base_addr = 0 if is_elf else base.address
 | 
						|
    for name, sym in syms.items():
 | 
						|
        if name.startswith('_binman'):
 | 
						|
            msg = ("Section '%s': Symbol '%s'\n   in entry '%s'" %
 | 
						|
                   (section.GetPath(), name, entry.GetPath()))
 | 
						|
            if is_elf:
 | 
						|
                # For ELF files, use the file offset
 | 
						|
                offset = sym.offset
 | 
						|
            else:
 | 
						|
                # For blobs use the offset of the symbol, calculated by
 | 
						|
                # subtracting the base address which by definition is at the
 | 
						|
                # start
 | 
						|
                offset = sym.address - base.address
 | 
						|
                if offset < 0 or offset + sym.size > entry.contents_size:
 | 
						|
                    raise ValueError('%s has offset %x (size %x) but the contents '
 | 
						|
                                     'size is %x' % (entry.GetPath(), offset,
 | 
						|
                                                     sym.size,
 | 
						|
                                                     entry.contents_size))
 | 
						|
            pack_string = GetPackString(sym, msg)
 | 
						|
            if name == '_binman_sym_magic':
 | 
						|
                value = BINMAN_SYM_MAGIC_VALUE
 | 
						|
            else:
 | 
						|
                # Look up the symbol in our entry tables.
 | 
						|
                value = section.GetImage().LookupImageSymbol(name, sym.weak,
 | 
						|
                                                             msg, base_addr)
 | 
						|
            if value is None:
 | 
						|
                value = -1
 | 
						|
                pack_string = pack_string.lower()
 | 
						|
            value_bytes = struct.pack(pack_string, value)
 | 
						|
            tout.debug('%s:\n   insert %s, offset %x, value %x, length %d' %
 | 
						|
                       (msg, name, offset, value, len(value_bytes)))
 | 
						|
            entry.data = (entry.data[:offset] + value_bytes +
 | 
						|
                        entry.data[offset + sym.size:])
 | 
						|
 | 
						|
def GetSymbolValue(sym, data, msg):
 | 
						|
    """Get the value of a symbol
 | 
						|
 | 
						|
    This can only be used on symbols with an integer value.
 | 
						|
 | 
						|
    Args:
 | 
						|
        sym (Symbol): Symbol to check
 | 
						|
        data (butes): Data for the ELF file - the symbol data appears at offset
 | 
						|
            sym.offset
 | 
						|
        @msg (str): String which indicates the entry being processed, used for
 | 
						|
            errors
 | 
						|
 | 
						|
    Returns:
 | 
						|
        int: Value of the symbol
 | 
						|
 | 
						|
    Raises:
 | 
						|
        ValueError: Symbol has an unexpected size
 | 
						|
    """
 | 
						|
    pack_string = GetPackString(sym, msg)
 | 
						|
    value = struct.unpack(pack_string, data[sym.offset:sym.offset + sym.size])
 | 
						|
    return value[0]
 | 
						|
 | 
						|
def MakeElf(elf_fname, text, data):
 | 
						|
    """Make an elf file with the given data in a single section
 | 
						|
 | 
						|
    The output file has a several section including '.text' and '.data',
 | 
						|
    containing the info provided in arguments.
 | 
						|
 | 
						|
    Args:
 | 
						|
        elf_fname: Output filename
 | 
						|
        text: Text (code) to put in the file's .text section
 | 
						|
        data: Data to put in the file's .data section
 | 
						|
    """
 | 
						|
    outdir = tempfile.mkdtemp(prefix='binman.elf.')
 | 
						|
    s_file = os.path.join(outdir, 'elf.S')
 | 
						|
 | 
						|
    # Spilt the text into two parts so that we can make the entry point two
 | 
						|
    # bytes after the start of the text section
 | 
						|
    text_bytes1 = ['\t.byte\t%#x' % byte for byte in text[:2]]
 | 
						|
    text_bytes2 = ['\t.byte\t%#x' % byte for byte in text[2:]]
 | 
						|
    data_bytes = ['\t.byte\t%#x' % byte for byte in data]
 | 
						|
    with open(s_file, 'w') as fd:
 | 
						|
        print('''/* Auto-generated C program to produce an ELF file for testing */
 | 
						|
 | 
						|
.section .text
 | 
						|
.code32
 | 
						|
.globl _start
 | 
						|
.type _start, @function
 | 
						|
%s
 | 
						|
_start:
 | 
						|
%s
 | 
						|
.ident "comment"
 | 
						|
 | 
						|
.comm fred,8,4
 | 
						|
 | 
						|
.section .empty
 | 
						|
.globl _empty
 | 
						|
_empty:
 | 
						|
.byte 1
 | 
						|
 | 
						|
.globl ernie
 | 
						|
.data
 | 
						|
.type ernie, @object
 | 
						|
.size ernie, 4
 | 
						|
ernie:
 | 
						|
%s
 | 
						|
''' % ('\n'.join(text_bytes1), '\n'.join(text_bytes2), '\n'.join(data_bytes)),
 | 
						|
        file=fd)
 | 
						|
    lds_file = os.path.join(outdir, 'elf.lds')
 | 
						|
 | 
						|
    # Use a linker script to set the alignment and text address.
 | 
						|
    with open(lds_file, 'w') as fd:
 | 
						|
        print('''/* Auto-generated linker script to produce an ELF file for testing */
 | 
						|
 | 
						|
PHDRS
 | 
						|
{
 | 
						|
    text PT_LOAD ;
 | 
						|
    data PT_LOAD ;
 | 
						|
    empty PT_LOAD FLAGS ( 6 ) ;
 | 
						|
    note PT_NOTE ;
 | 
						|
}
 | 
						|
 | 
						|
SECTIONS
 | 
						|
{
 | 
						|
    . = 0xfef20000;
 | 
						|
    ENTRY(_start)
 | 
						|
    .text . : SUBALIGN(0)
 | 
						|
    {
 | 
						|
        *(.text)
 | 
						|
    } :text
 | 
						|
    .data : {
 | 
						|
        *(.data)
 | 
						|
    } :data
 | 
						|
    _bss_start = .;
 | 
						|
    .empty : {
 | 
						|
        *(.empty)
 | 
						|
    } :empty
 | 
						|
    /DISCARD/ : {
 | 
						|
        *(.note.gnu.property)
 | 
						|
    }
 | 
						|
    .note : {
 | 
						|
        *(.comment)
 | 
						|
    } :note
 | 
						|
    .bss _bss_start  (OVERLAY) : {
 | 
						|
        *(.bss)
 | 
						|
    }
 | 
						|
}
 | 
						|
''', file=fd)
 | 
						|
    # -static: Avoid requiring any shared libraries
 | 
						|
    # -nostdlib: Don't link with C library
 | 
						|
    # -Wl,--build-id=none: Don't generate a build ID, so that we just get the
 | 
						|
    #   text section at the start
 | 
						|
    # -m32: Build for 32-bit x86
 | 
						|
    # -T...: Specifies the link script, which sets the start address
 | 
						|
    cc, args = tools.get_target_compile_tool('cc')
 | 
						|
    args += ['-static', '-nostdlib', '-Wl,--build-id=none', '-m32', '-T',
 | 
						|
            lds_file, '-o', elf_fname, s_file]
 | 
						|
    stdout = command.output(cc, *args)
 | 
						|
    shutil.rmtree(outdir)
 | 
						|
 | 
						|
def DecodeElf(data, location):
 | 
						|
    """Decode an ELF file and return information about it
 | 
						|
 | 
						|
    Args:
 | 
						|
        data: Data from ELF file
 | 
						|
        location: Start address of data to return
 | 
						|
 | 
						|
    Returns:
 | 
						|
        ElfInfo object containing information about the decoded ELF file
 | 
						|
    """
 | 
						|
    file_size = len(data)
 | 
						|
    with io.BytesIO(data) as fd:
 | 
						|
        elf = ELFFile(fd)
 | 
						|
        data_start = 0xffffffff;
 | 
						|
        data_end = 0;
 | 
						|
        mem_end = 0;
 | 
						|
        virt_to_phys = 0;
 | 
						|
 | 
						|
        for i in range(elf.num_segments()):
 | 
						|
            segment = elf.get_segment(i)
 | 
						|
            if segment['p_type'] != 'PT_LOAD' or not segment['p_memsz']:
 | 
						|
                skipped = 1  # To make code-coverage see this line
 | 
						|
                continue
 | 
						|
            start = segment['p_paddr']
 | 
						|
            mend = start + segment['p_memsz']
 | 
						|
            rend = start + segment['p_filesz']
 | 
						|
            data_start = min(data_start, start)
 | 
						|
            data_end = max(data_end, rend)
 | 
						|
            mem_end = max(mem_end, mend)
 | 
						|
            if not virt_to_phys:
 | 
						|
                virt_to_phys = segment['p_paddr'] - segment['p_vaddr']
 | 
						|
 | 
						|
        output = bytearray(data_end - data_start)
 | 
						|
        for i in range(elf.num_segments()):
 | 
						|
            segment = elf.get_segment(i)
 | 
						|
            if segment['p_type'] != 'PT_LOAD' or not segment['p_memsz']:
 | 
						|
                skipped = 1  # To make code-coverage see this line
 | 
						|
                continue
 | 
						|
            start = segment['p_paddr']
 | 
						|
            offset = 0
 | 
						|
            if start < location:
 | 
						|
                offset = location - start
 | 
						|
                start = location
 | 
						|
            # A legal ELF file can have a program header with non-zero length
 | 
						|
            # but zero-length file size and a non-zero offset which, added
 | 
						|
            # together, are greater than input->size (i.e. the total file size).
 | 
						|
            #  So we need to not even test in the case that p_filesz is zero.
 | 
						|
            # Note: All of this code is commented out since we don't have a test
 | 
						|
            # case for it.
 | 
						|
            size = segment['p_filesz']
 | 
						|
            #if not size:
 | 
						|
                #continue
 | 
						|
            #end = segment['p_offset'] + segment['p_filesz']
 | 
						|
            #if end > file_size:
 | 
						|
                #raise ValueError('Underflow copying out the segment. File has %#x bytes left, segment end is %#x\n',
 | 
						|
                                 #file_size, end)
 | 
						|
            output[start - data_start:start - data_start + size] = (
 | 
						|
                segment.data()[offset:])
 | 
						|
    return ElfInfo(output, data_start, elf.header['e_entry'] + virt_to_phys,
 | 
						|
                   mem_end - data_start)
 | 
						|
 | 
						|
def UpdateFile(infile, outfile, start_sym, end_sym, insert):
 | 
						|
    tout.notice("Creating file '%s' with data length %#x (%d) between symbols '%s' and '%s'" %
 | 
						|
                (outfile, len(insert), len(insert), start_sym, end_sym))
 | 
						|
    syms = GetSymbolFileOffset(infile, [start_sym, end_sym])
 | 
						|
    if len(syms) != 2:
 | 
						|
        raise ValueError("Expected two symbols '%s' and '%s': got %d: %s" %
 | 
						|
                         (start_sym, end_sym, len(syms),
 | 
						|
                          ','.join(syms.keys())))
 | 
						|
 | 
						|
    size = syms[end_sym].offset - syms[start_sym].offset
 | 
						|
    if len(insert) > size:
 | 
						|
        raise ValueError("Not enough space in '%s' for data length %#x (%d); size is %#x (%d)" %
 | 
						|
                         (infile, len(insert), len(insert), size, size))
 | 
						|
 | 
						|
    data = tools.read_file(infile)
 | 
						|
    newdata = data[:syms[start_sym].offset]
 | 
						|
    newdata += insert + tools.get_bytes(0, size - len(insert))
 | 
						|
    newdata += data[syms[end_sym].offset:]
 | 
						|
    tools.write_file(outfile, newdata)
 | 
						|
    tout.info('Written to offset %#x' % syms[start_sym].offset)
 | 
						|
 | 
						|
def read_loadable_segments(data):
 | 
						|
    """Read segments from an ELF file
 | 
						|
 | 
						|
    Args:
 | 
						|
        data (bytes): Contents of file
 | 
						|
 | 
						|
    Returns:
 | 
						|
        tuple:
 | 
						|
            list of segments, each:
 | 
						|
                int: Segment number (0 = first)
 | 
						|
                int: Start address of segment in memory
 | 
						|
                bytes: Contents of segment
 | 
						|
            int: entry address for image
 | 
						|
 | 
						|
    Raises:
 | 
						|
        ValueError: elftools is not available
 | 
						|
    """
 | 
						|
    if not ELF_TOOLS:
 | 
						|
        raise ValueError("Python: No module named 'elftools'")
 | 
						|
    with io.BytesIO(data) as inf:
 | 
						|
        try:
 | 
						|
            elf = ELFFile(inf)
 | 
						|
        except ELFError as err:
 | 
						|
            raise ValueError(err)
 | 
						|
        entry = elf.header['e_entry']
 | 
						|
        segments = []
 | 
						|
        for i in range(elf.num_segments()):
 | 
						|
            segment = elf.get_segment(i)
 | 
						|
            if segment['p_type'] != 'PT_LOAD' or not segment['p_memsz']:
 | 
						|
                skipped = 1  # To make code-coverage see this line
 | 
						|
                continue
 | 
						|
            start = segment['p_offset']
 | 
						|
            rend = start + segment['p_filesz']
 | 
						|
            segments.append((i, segment['p_paddr'], data[start:rend]))
 | 
						|
    return segments, entry
 | 
						|
 | 
						|
def is_valid(data):
 | 
						|
    """Check if some binary data is a valid ELF file
 | 
						|
 | 
						|
    Args:
 | 
						|
        data (bytes): Bytes to check
 | 
						|
 | 
						|
    Returns:
 | 
						|
        bool: True if a valid Elf file, False if not
 | 
						|
    """
 | 
						|
    try:
 | 
						|
        DecodeElf(data, 0)
 | 
						|
        return True
 | 
						|
    except ELFError:
 | 
						|
        return False
 |