sigrok-fwextract-kingst-la2016: update documentation and add a generated README file
[sigrok-util.git] / firmware / saleae-logic16 / parseelf.py
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
21 import struct
22
23 class 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_rela(this):
72         return this.read_struct('NNn', ('r_offset', 'r_info', 'r_addend'))
73
74     def parse_rel(this):
75         return this.read_struct('NN', ('r_offset', 'r_info'))
76
77     def fixup_reloc(this, reloc):
78         if not 'r_addend' in reloc:
79             reloc['r_addend'] = 0
80         if this.elf_wordsize == 64:
81             reloc['r_sym'] = reloc['r_info'] >> 32
82             reloc['r_type'] = reloc['r_info'] & 0xffffffff
83         else:
84             reloc['r_sym'] = reloc['r_info'] >> 8
85             reloc['r_type'] = reloc['r_info'] & 0xff
86         return reloc
87
88     def parse_symbols(this, symsecname, strsecname):
89         try:
90             symsechdr = this.find_section(symsecname)
91             strsechdr = this.find_section(strsecname)
92         except KeyError:
93             return {}
94         strsec = this.read_section(strsechdr)
95         this.file.seek(symsechdr['sh_offset'])
96         syms = [dict(this.parse_symbol(),number=i) for i in
97                 range(0, symsechdr['sh_size'] // symsechdr['sh_entsize'])]
98         return {this.get_name(sym['st_name'], strsec): sym for sym in syms}
99
100     def parse_relocs(this, section):
101         this.file.seek(section['sh_offset'])
102         if section['sh_type'] == 4:
103             relocs = [this.fixup_reloc(this.parse_rela()) for i in
104                       range(0, section['sh_size'] // section['sh_entsize'])]
105         else:
106             relocs = [this.fixup_reloc(this.parse_rel()) for i in
107                       range(0, section['sh_size'] // section['sh_entsize'])]
108         return relocs
109
110     def address_to_offset(this, addr):
111         for section in this.shdrs:
112             if (section['sh_addr'] <= addr and
113                 section['sh_addr']+section['sh_size'] > addr):
114                 return section['sh_offset']+(addr-section['sh_addr'])
115         raise IndexError('address out of range')
116
117     def load_symbol(this, sym):
118         this.file.seek(this.address_to_offset(sym['st_value']))
119         return this.file.read(sym['st_size'])
120
121     def __init__(this, filename):
122         this.file = open(filename, 'rb')
123         magic = this.file.read(16)
124
125         if magic[:4] != b'\x7fELF':
126             raise Exception("ELF signature not found")
127
128         if magic[4] == 1:
129             this.elf_wordsize = 32
130             nativeint = 'Ii'
131         elif magic[4] == 2:
132             this.elf_wordsize = 64
133             nativeint = 'Qq'
134         else:
135             raise Exception("Invalid ELF file class")
136
137         if magic[5] == 1:
138             this.elf_endianprefix = '<'
139         elif magic[5] == 2:
140             this.elf_endianprefix = '>'
141         else:
142             raise Exception("Invalid ELF data encoding")
143
144         this.elf_format = str.maketrans('HWwXxNn', 'HIiQq'+nativeint)
145
146         this.file.seek(0)
147         this.ehdr = this.read_ehdr()
148         this.file.seek(this.ehdr['e_shoff'])
149         this.shdrs = [this.read_shdr() for i in range(this.ehdr['e_shnum'])]
150
151         this.strtab = this.read_section(this.shdrs[this.ehdr['e_shstrndx']])
152
153         this.symtab = this.parse_symbols('.symtab', '.strtab')
154         this.dynsym = this.parse_symbols('.dynsym', '.dynstr')
155
156         this.relocs = {}
157         for section in this.shdrs:
158             if section['sh_type'] == 4 or section['sh_type'] == 9:
159                 rels = {}
160                 symsec = this.shdrs[section['sh_link']]
161                 if this.get_name(symsec['sh_name']) == '.symtab':
162                     rels['symbols'] = this.symtab
163                 elif this.get_name(symsec['sh_name']) == '.dynsym':
164                     rels['symbols'] = this.dynsym
165                 rels['relocs'] = this.parse_relocs(section)
166                 this.relocs[this.get_name(section['sh_name'])] = rels
167
168     def __del__(this):
169         try:
170             this.file.close()
171         except AttributeError:
172             pass