-#!/usr/bin/python3
-##
-## This file is part of the sigrok-util project.
-##
-## Copyright (C) 2013 Marcus Comstedt <marcus@mc.pp.se>
-##
-## This program is free software; you can redistribute it and/or modify
-## it under the terms of the GNU General Public License as published by
-## the Free Software Foundation; either version 3 of the License, or
-## (at your option) any later version.
-##
-## This program is distributed in the hope that it will be useful,
-## but WITHOUT ANY WARRANTY; without even the implied warranty of
-## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-## GNU General Public License for more details.
-##
-## You should have received a copy of the GNU General Public License
-## along with this program; if not, see <http://www.gnu.org/licenses/>.
-##
-
-import struct
-
-class elf:
-
- def read_struct(this, struct_fmt, struct_fields):
- fmt = this.elf_endianprefix+str.translate(struct_fmt, this.elf_format);
- fields = struct.unpack(fmt, this.file.read(struct.calcsize(fmt)))
- return dict(zip(struct_fields, fields))
-
- def read_ehdr(this):
- return this.read_struct('16sHHWNNNWHHHHHH',
- ('e_ident', 'e_type', 'e_machine', 'e_version',
- 'e_entry', 'e_phoff', 'e_shoff', 'e_flags',
- 'e_ehsize', 'e_phentsize', 'e_phnum',
- 'e_shentsize', 'e_shnum', 'e_shstrndx'))
-
- def read_shdr(this):
- return this.read_struct('WWNNNNWWNN',
- ('sh_name', 'sh_type', 'sh_flags', 'sh_addr',
- 'sh_offset', 'sh_size', 'sh_link', 'sh_info',
- 'sh_addralign', 'sh_entsize'))
-
- def read_section(this, shdr):
- this.file.seek(shdr['sh_offset'])
- return this.file.read(shdr['sh_size'])
-
- def get_name(this, name, strtab=None):
- strtab = strtab or this.strtab
- nul = strtab.find(b'\0', name)
- if nul < 0:
- return bytes.decode(strtab[name:])
- else:
- return bytes.decode(strtab[name:nul])
-
- def find_section(this, name):
- for section in this.shdrs:
- if this.get_name(section['sh_name']) == name:
- return section
- raise KeyError(name)
-
- def parse_symbol(this):
- if this.elf_wordsize == 64:
- return this.read_struct('WBBHNX',
- ('st_name', 'st_info', 'st_other',
- 'st_shndx', 'st_value', 'st_size'))
- else:
- return this.read_struct('WNWBBH',
- ('st_name', 'st_value', 'st_size',
- 'st_info', 'st_other', 'st_shndx'))
-
- def parse_symbols(this, symsecname, strsecname):
- try:
- symsechdr = this.find_section(symsecname)
- strsechdr = this.find_section(strsecname)
- except KeyError:
- return {}
- strsec = this.read_section(strsechdr)
- this.file.seek(symsechdr['sh_offset'])
- syms = [this.parse_symbol() for i in
- range(0, symsechdr['sh_size'] // symsechdr['sh_entsize'])]
- return {this.get_name(sym['st_name'], strsec): sym for sym in syms}
-
- def address_to_offset(this, addr):
- for section in this.shdrs:
- if (section['sh_addr'] <= addr and
- section['sh_addr']+section['sh_size'] > addr):
- return section['sh_offset']+(addr-section['sh_addr'])
- raise IndexError('address out of range')
-
- def load_symbol(this, sym):
- this.file.seek(this.address_to_offset(sym['st_value']))
- return this.file.read(sym['st_size'])
-
- def __init__(this, filename):
- this.file = open(filename, 'rb')
- magic = this.file.read(16)
-
- if magic[:4] != b'\x7fELF':
- raise Exception("ELF signature not found")
-
- if magic[4] == 1:
- this.elf_wordsize = 32
- nativeint = 'Ii'
- elif magic[4] == 2:
- this.elf_wordsize = 64
- nativeint = 'Qq'
- else:
- raise Exception("Invalid ELF file class")
-
- if magic[5] == 1:
- this.elf_endianprefix = '<'
- elif magic[5] == 2:
- this.elf_endianprefix = '>'
- else:
- raise Exception("Invalid ELF data encoding")
-
- this.elf_format = str.maketrans('HWwXxNn', 'HIiQq'+nativeint)
-
- this.file.seek(0)
- this.ehdr = this.read_ehdr()
- this.file.seek(this.ehdr['e_shoff'])
- this.shdrs = [this.read_shdr() for i in range(this.ehdr['e_shnum'])]
-
- this.strtab = this.read_section(this.shdrs[this.ehdr['e_shstrndx']])
-
- this.symtab = this.parse_symbols('.symtab', '.strtab')
- this.dynsym = this.parse_symbols('.dynsym', '.dynstr')
-
- def __del__(this):
- try:
- this.file.close()
- except AttributeError:
- pass