]> sigrok.org Git - sigrok-util.git/blame - firmware/saleae-logic16/parseelf.py
Move/rename firmware extractor tools.
[sigrok-util.git] / firmware / saleae-logic16 / parseelf.py
CommitLineData
a5419fb5
MC
1#!/usr/bin/python3
2##
3## This file is part of the sigrok-util project.
4##
5## Copyright (C) 2013 Marcus Comstedt <marcus@mc.pp.se>
6##
7## This program is free software; you can redistribute it and/or modify
8## it under the terms of the GNU General Public License as published by
9## the Free Software Foundation; either version 3 of the License, or
10## (at your option) any later version.
11##
12## This program is distributed in the hope that it will be useful,
13## but WITHOUT ANY WARRANTY; without even the implied warranty of
14## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15## GNU General Public License for more details.
16##
17## You should have received a copy of the GNU General Public License
18## along with this program; if not, see <http://www.gnu.org/licenses/>.
19##
20
21import struct
22
23class elf:
24
25 def read_struct(this, struct_fmt, struct_fields):
26 fmt = this.elf_endianprefix+str.translate(struct_fmt, this.elf_format);
27 fields = struct.unpack(fmt, this.file.read(struct.calcsize(fmt)))
28 return dict(zip(struct_fields, fields))
29
30 def read_ehdr(this):
31 return this.read_struct('16sHHWNNNWHHHHHH',
32 ('e_ident', 'e_type', 'e_machine', 'e_version',
33 'e_entry', 'e_phoff', 'e_shoff', 'e_flags',
34 'e_ehsize', 'e_phentsize', 'e_phnum',
35 'e_shentsize', 'e_shnum', 'e_shstrndx'))
36
37 def read_shdr(this):
38 return this.read_struct('WWNNNNWWNN',
39 ('sh_name', 'sh_type', 'sh_flags', 'sh_addr',
40 'sh_offset', 'sh_size', 'sh_link', 'sh_info',
41 'sh_addralign', 'sh_entsize'))
42
43 def read_section(this, shdr):
44 this.file.seek(shdr['sh_offset'])
45 return this.file.read(shdr['sh_size'])
46
47 def get_name(this, name, strtab=None):
48 strtab = strtab or this.strtab
49 nul = strtab.find(b'\0', name)
50 if nul < 0:
51 return bytes.decode(strtab[name:])
52 else:
53 return bytes.decode(strtab[name:nul])
54
55 def find_section(this, name):
56 for section in this.shdrs:
57 if this.get_name(section['sh_name']) == name:
58 return section
59 raise KeyError(name)
60
61 def parse_symbol(this):
62 if this.elf_wordsize == 64:
63 return this.read_struct('WBBHNX',
64 ('st_name', 'st_info', 'st_other',
65 'st_shndx', 'st_value', 'st_size'))
66 else:
67 return this.read_struct('WNWBBH',
68 ('st_name', 'st_value', 'st_size',
69 'st_info', 'st_other', 'st_shndx'))
70
71 def parse_symbols(this, symsecname, strsecname):
72 try:
73 symsechdr = this.find_section(symsecname)
74 strsechdr = this.find_section(strsecname)
75 except KeyError:
76 return {}
77 strsec = this.read_section(strsechdr)
78 this.file.seek(symsechdr['sh_offset'])
79 syms = [this.parse_symbol() for i in
80 range(0, symsechdr['sh_size'] // symsechdr['sh_entsize'])]
81 return {this.get_name(sym['st_name'], strsec): sym for sym in syms}
82
83 def address_to_offset(this, addr):
84 for section in this.shdrs:
85 if (section['sh_addr'] <= addr and
86 section['sh_addr']+section['sh_size'] > addr):
87 return section['sh_offset']+(addr-section['sh_addr'])
88 raise IndexError('address out of range')
89
90 def load_symbol(this, sym):
91 this.file.seek(this.address_to_offset(sym['st_value']))
92 return this.file.read(sym['st_size'])
93
94 def __init__(this, filename):
95 this.file = open(filename, 'rb')
96 magic = this.file.read(16)
97
98 if magic[:4] != b'\x7fELF':
99 raise Exception("ELF signature not found")
100
101 if magic[4] == 1:
102 this.elf_wordsize = 32
103 nativeint = 'Ii'
104 elif magic[4] == 2:
105 this.elf_wordsize = 64
106 nativeint = 'Qq'
107 else:
108 raise Exception("Invalid ELF file class")
109
110 if magic[5] == 1:
111 this.elf_endianprefix = '<'
112 elif magic[5] == 2:
113 this.elf_endianprefix = '>'
114 else:
115 raise Exception("Invalid ELF data encoding")
116
117 this.elf_format = str.maketrans('HWwXxNn', 'HIiQq'+nativeint)
118
119 this.file.seek(0)
120 this.ehdr = this.read_ehdr()
121 this.file.seek(this.ehdr['e_shoff'])
122 this.shdrs = [this.read_shdr() for i in range(this.ehdr['e_shnum'])]
123
124 this.strtab = this.read_section(this.shdrs[this.ehdr['e_shstrndx']])
125
126 this.symtab = this.parse_symbols('.symtab', '.strtab')
127 this.dynsym = this.parse_symbols('.dynsym', '.dynstr')
128
129 def __del__(this):
130 try:
131 this.file.close()
132 except AttributeError:
133 pass