3 ## This file is part of the sigrok-util project.
5 ## Copyright (C) 2013 Marcus Comstedt <marcus@mc.pp.se>
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.
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.
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/>.
27 def reset(this, offs=0):
28 if offs < 0 or offs > this.length:
29 raise Exception('Reset past end of section')
30 this.address = this.baseaddr + offs
34 if this.offset + cnt > this.length:
35 raise Exception('Skip past end of section')
39 def peek(this, cnt, offs=0):
40 if this.offset + offs + cnt > this.length:
41 raise Exception('Peek past end of section')
42 return this.data[this.offset + offs : this.offset + offs + cnt]
44 def look_for(this, needle):
45 pos = this.data.find(needle, this.offset)
47 raise Exception('Needle not found in haystack')
48 this.skip(pos - this.offset)
50 def __init__(this, data, addr):
53 this.length = len(data)
56 def search_plt_32(plt, addr):
58 plt.look_for(struct.pack('<BBI', 0xff, 0x25, addr)) # jmp *addr32
61 def search_plt_64(plt, addr):
64 plt.look_for(b'\xff\x25') # jmpq *offs32(%rip)
65 offs = struct.unpack('<i', plt.peek(4, 2))[0]
66 if plt.address + offs + 6 == addr:
70 def find_hex_file_lines_constructor_32(text, hex_file_lines_got, got_plt):
72 text.look_for(b'\x8b\xbb') # mov offs32(%ebx),%edi
73 offs = struct.unpack('<i', text.peek(4, 2))[0]
74 if got_plt + offs == hex_file_lines_got:
79 def find_hex_file_lines_constructor_64(text, hex_file_lines_got):
81 text.look_for(b'\x48\x8b\x2d') # mov offs32(%rip),%rbp
82 offs = struct.unpack('<i', text.peek(4, 3))[0]
83 if text.address + offs + 7 == hex_file_lines_got:
88 def parse_hex_file_lines_constructor_32(text, basic_string_plt, got_plt, lines):
91 if text.peek(2) == b'\x8d\x45': # lea offs8(%ebp),%eax
93 elif text.peek(2) == b'\x8d\x85': # lea offs32(%ebp),%eax
95 if text.peek(1) == b'\xbe': # mov $imm32,%esi
97 elif text.peek(2) == b'\x31\xf6': # xor %esi,%esi
99 if text.peek(4) == b'\x89\x44\x24\x08': # mov %eax,0x8(%esp)
101 if text.peek(2) == b'\x8d\x83': # lea offs32(%ebx),%eax
102 straddr = struct.unpack('<i', text.peek(4, 2))[0]
106 raise Exception('Expected lea offs32(%ebx),%eax @ ' +
107 ('0x%x' % text.address))
108 if text.peek(4) == b'\x89\x44\x24\x04': # mov %eax,0x4(%esp)
110 if text.peek(3) == b'\x89\x3c\x24': # mov %edi,(%esp)
113 elif text.peek(2) == b'\x8d\x47': # lea offs8(%edi),%eax
114 offs = struct.unpack('<b', text.peek(1, 2))[0]
116 elif text.peek(2) == b'\x8d\x87': # lea offs32(%edi),%eax
117 offs = struct.unpack('<i', text.peek(4, 2))[0]
120 raise Exception('Expected lea offs(%edi),%eax @ ' +
121 ('0x%x' % text.address))
122 if offs < 0 or offs > (len(lines) << 2) or (offs & 3) != 0:
123 raise Exception('Invalid offset %d' % offs)
125 if lines[index] != 0:
126 raise Exception('Line %d filled multiple times' % index)
127 if text.peek(3) == b'\x89\x04\x24': # mov %eax,(%esp)
129 if text.peek(1) == b'\xe8': # call offs32
130 offs = struct.unpack('<i', text.peek(4, 1))[0]
132 if text.address + offs != basic_string_plt:
133 raise Exception('Expected call ZNSsC1EPKcRKSaIcE@plt @ ' +
134 ('0x%x' % text.address))
136 raise Exception('Expected call ZNSsC1EPKcRKSaIcE@plt @ ' +
137 ('0x%x' % text.address))
139 raise Exception('NULL pointer stored to index %d' % index)
140 lines[index] = straddr
143 def parse_hex_file_lines_constructor_64(text, basic_string_plt, lines):
146 if text.peek(1) == b'\xbb': # mov $imm32,%ebx
148 elif text.peek(2) == b'\x31\xdb': # xor %ebx,%ebx
150 if text.peek(4) == b'\x48\x8d\x54\x24': # lea offs8(%rsp),%rdx
152 elif text.peek(4) == b'\x48\x8d\x94\x24': # lea offs32(%rsp),%rdx
154 if text.peek(3) == b'\x48\x8d\x35': # lea offs32(%rip),%rsi
155 straddr = struct.unpack('<i', text.peek(4, 3))[0]
157 straddr += text.address
159 raise Exception('Expected lea offs(%rip),%rsi @ ' +
160 ('0x%x' % text.address))
161 if text.peek(3) == b'\x48\x89\xef': # mov %rbp,%rdi
164 elif text.peek(3) == b'\x48\x8d\x7d': # lea offs8(%rbp),%rdi
165 offs = struct.unpack('<b', text.peek(1, 3))[0]
167 elif text.peek(3) == b'\x48\x8d\xbd': # lea offs32(%rbp),%rdi
168 offs = struct.unpack('<i', text.peek(4, 3))[0]
171 raise Exception('Expected lea offs(%rbp),%rdi @ ' +
172 ('0x%x' % text.address))
173 if offs < 0 or offs > (len(lines) << 3) or (offs & 7) != 0:
174 raise Exception('Invalid offset %d' % offs)
176 if lines[index] != 0:
177 raise Exception('Line %d filled multiple times' % index)
178 if text.peek(1) == b'\xe8': # callq offs32
179 offs = struct.unpack('<i', text.peek(4, 1))[0]
181 if text.address + offs != basic_string_plt:
182 raise Exception('Expected callq ZNSsC1EPKcRKSaIcE@plt @ ' +
183 ('0x%x' % text.address))
185 raise Exception('Expected callq ZNSsC1EPKcRKSaIcE@plt @ ' +
186 ('0x%x' % text.address))
188 raise Exception('NULL pointer stored to index %d' % index)
189 lines[index] = straddr
192 def find_reloc(elf, symname):
193 for section, relocs in elf.relocs.items():
194 if 'symbols' in relocs and symname in relocs['symbols']:
195 symnum = relocs['symbols'][symname]['number']
196 for reloc in relocs['relocs']:
197 if reloc['r_sym'] == symnum:
199 raise Exception('Unable to find a relocation against ' + symname)
201 def ihex_to_binary(lines):
205 raise Exception('ihex line does not start with ":"')
206 line = bytes.fromhex(line[1:])
207 if (sum(bytearray(line)) & 0xff) != 0:
208 raise Exception('Invalid checksum in ihex')
209 (byte_count, address, rectype) = struct.unpack('>BHB', line[:4])
210 (data, checksum) = struct.unpack('>%dsB' % (byte_count), line[4:])
211 if rectype == 1 and byte_count == 0:
213 elif rectype != 0 or byte_count == 0:
214 raise Exception('Unexpected rectype %d with bytecount %d' %
215 (rectype, byte_count))
216 elif address in chunks:
217 raise Exception('Multiple ihex lines with address 0x%x' % address)
219 chunks[address] = data
221 for address in sorted(iter(chunks)):
222 if address < len(blob):
223 raise Exception('Overlapping ihex chunks')
224 elif address > len(blob):
225 blob += b'\x00' * (address - len(blob))
226 blob += chunks[address]
229 def extract_fx2_firmware(elf, symname, filename):
230 blob = elf.load_symbol(elf.dynsym[symname + 'Count'])
231 count = struct.unpack('<I', blob)[0]
232 got_plt = elf.find_section('.got.plt')['sh_addr']
233 hex_file_lines_got = find_reloc(elf, symname)['r_offset']
234 basic_string_got = find_reloc(elf, '_ZNSsC1EPKcRKSaIcE')['r_offset']
235 pltsec = elf.find_section('.plt')
236 plt = searcher(elf.read_section(pltsec), pltsec['sh_addr'])
238 if elf.elf_wordsize == 64:
239 basic_string_plt = search_plt_64(plt, basic_string_got)
241 basic_string_plt = search_plt_32(plt, basic_string_got)
243 raise Exception('Unable to find a PLT entry for _ZNSsC1EPKcRKSaIcE')
244 textsec = elf.find_section('.text')
245 text = searcher(elf.read_section(textsec), textsec['sh_addr'])
248 if elf.elf_wordsize == 64:
249 find_hex_file_lines_constructor_64(text, hex_file_lines_got)
251 find_hex_file_lines_constructor_32(text, hex_file_lines_got,
254 raise Exception('Unable to find constructor for ' + symname)
255 oldoffs = text.offset
258 if elf.elf_wordsize == 64:
259 parse_hex_file_lines_constructor_64(text, basic_string_plt, l)
261 parse_hex_file_lines_constructor_32(text, basic_string_plt,
266 rodatasec = elf.find_section('.rodata')
267 rodata = elf.read_section(rodatasec)
268 lo = rodatasec['sh_addr']
269 hi = lo + rodatasec['sh_size']
270 for i in range(count):
272 if addr < lo or addr >= hi:
273 raise Exception('Address 0x%x outside of .rodata section' % addr)
274 l[i] = elf.get_name(addr - lo, rodata)
275 blob = ihex_to_binary(l)
276 f = open(filename, 'wb')
279 print("saved %d bytes to %s" % (len(blob), filename))
281 def extract_symbol(elf, symname, filename):
282 blob = elf.load_symbol(elf.dynsym[symname])
283 f = open(filename, 'wb')
286 print("saved %d bytes to %s" % (len(blob), filename))
288 def extract_bitstream(elf, lv):
289 extract_symbol(elf, 'gLogic16Lv' + lv + 'CompressedBitstream',
290 'saleae-logic16-fpga-' + lv + '.bitstream')
293 print("sigrok-fwextract-saleae-logic16 <programfile>")
301 if len(sys.argv) != 2:
305 filename = sys.argv[1]
306 elf = parseelf.elf(filename)
307 if elf.ehdr['e_machine'] != 3 and elf.ehdr['e_machine'] != 62:
308 raise Exception('Unsupported e_machine')
309 extract_fx2_firmware(elf, 'gLogic16HexFileLines', 'saleae-logic16-fx2.fw')
310 extract_bitstream(elf, '18')
311 extract_bitstream(elf, '33')
312 except Exception as e:
313 print("Error: %s" % str(e))