+++ /dev/null
-The hantek-dso-extract.py tool extracts firmware from the driver that comes
-with the Hantek DSO-2xxx/52xx series USB oscilloscopes. Find the 32-bit
-driver installed on the windows system -- typically called DSOxxxx1.sys or
-DsoxxxxX861.sys, where xxxx is your device's model. Use it like this:
-
-$ ./hantek-dso-extract.py Dso2090X861.sys
-saved 4730 bytes to hantek-dso-2090.fw
-
-Copy the resulting file over to the location where sigrok installed its
-firmware files. By default this is /usr/local/share/sigrok-firmware
+++ /dev/null
-The parsepe.py tool can list all sections and symbols from a PE (portable
-executable) binary file, and extract the contents of a symbol.
-
-usage:
-parsepe.py -l <filename> list all sections and symbols in file
-parsepe.py -s <symbol> -x <filename> extract symbol from file
-
-TODO:
-- currently only handles COFF symbol tables
-- can only extract external (global) symbols
-
+++ /dev/null
-The saleae-logic16-extract.py tool extracts FPGA bitstreams from the
-vendor software for the Saleae Logic16 USB logic analyzer. Download
-the Linux version (either 32-bit or 64-bit will do), and unpack it
-to find the main binary called "Logic".
-
-Usage:
-
-$ ./saleae-logic16-extract.py Logic
-saved 149516 bytes to saleae-logic16-fpga-18.bitstream
-saved 149516 bytes to saleae-logic16-fpga-33.bitstream
-
-Copy the resulting files over to the location where sigrok installed its
-firmware files. By default this is /usr/local/share/sigrok-firmware.
+++ /dev/null
-#!/usr/bin/python3
-##
-## This file is part of the sigrok-util project.
-##
-## Copyright (C) 2012 Bert Vermeulen <bert@biot.com>
-##
-## 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 sys
-import os
-import re
-import struct
-from array import array
-
-import parsepe
-
-
-def find_model(filename):
- filename = os.path.split(filename)[-1]
- m = re.search('^dso([a-z0-9]+)1.sys$', filename, re.I)
- if m:
- model = m.group(1).upper()
- model = model.replace('X86', '').replace('AMD64', '').replace('IA64', '')
- if model == '520A':
- model = '5200A'
- else:
- model = 'unknown'
-
- return model
-
-
-def unsparse(data):
- p = 0
- maxaddr = 0
- blob = array('B', [0] * 0x4000)
- while p <= len(data) and data[p+4] == 0:
- num_bytes = struct.unpack("<H", data[p:p+2])[0]
- address = struct.unpack("<H", data[p+2:p+4])[0]
- chunk = array('B')
- chunk.frombytes(data[p+5:p+5+num_bytes])
- p += 22
-
- if address > 0x4000:
- # the FX2 only has 16K RAM. other writes are to registers
- # in the 0xe000 region, skip those
- continue
-
- blob[address:address+num_bytes] = chunk
-
- if address + num_bytes > maxaddr:
- maxaddr = address + num_bytes
-
- return blob[:maxaddr].tostring()
-
-
-def usage():
- print("hantek-dso-extract.py <driverfile>")
- sys.exit()
-
-
-#
-# main
-#
-
-if len(sys.argv) != 2:
- usage()
-
-try:
- filename = sys.argv[1]
- binihx = parsepe.extract_symbol(filename, '_firmware')
- if binihx is None:
- raise Exception("no firmware found")
- blob = unsparse(binihx)
- outfile = 'hantek-dso-' + find_model(filename) + '.fw'
- open(outfile, 'wb').write(blob)
- print("saved %d bytes to %s" % (len(blob), outfile))
-except Exception as e:
- print("Error: %s" % str(e))
-
-
-
--- /dev/null
+The parsepe.py tool can list all sections and symbols from a PE (portable
+executable) binary file, and extract the contents of a symbol.
+
+usage:
+parsepe.py -l <filename> list all sections and symbols in file
+parsepe.py -s <symbol> -x <filename> extract symbol from file
+
+TODO:
+- currently only handles COFF symbol tables
+- can only extract external (global) symbols
+
--- /dev/null
+This tool extracts firmware from the driver that comes
+with the Hantek DSO-2xxx/52xx series USB oscilloscopes. Find the 32-bit
+driver installed on the windows system -- typically called DSOxxxx1.sys or
+DsoxxxxX861.sys, where xxxx is your device's model. Use it like this:
+
+$ sigrok-fwextract-hantek-dso Dso2090X861.sys
+saved 4730 bytes to hantek-dso-2090.fw
+
+Copy the resulting file over to the location where sigrok installed its
+firmware files. By default this is /usr/local/share/sigrok-firmware.
--- /dev/null
+#!/usr/bin/python3
+##
+## This file is part of the sigrok-util project.
+##
+## Copyright (C) 2012 Bert Vermeulen <bert@biot.com>
+##
+## 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 sys
+import os
+from getopt import getopt
+import struct
+
+
+def parse(filename):
+ f = open(filename, 'rb')
+ if f.read(2) != b'MZ':
+ raise Exception("MZ signature not found.")
+
+ sections = []
+ # long e_lfanew
+ f.seek(0x3c)
+ pe_ptr = struct.unpack("<L", f.read(4))[0]
+ f.seek(pe_ptr)
+ if f.read(4) != b'\x50\x45\x00\x00':
+ raise Exception("PE signature not found.")
+ # skip Machine
+ f.seek(f.tell() + 2)
+ sections.append(['header', 324, 0])
+ num_sections = struct.unpack("<H", f.read(2))[0]
+ # skip TimeDateStamp
+ f.seek(f.tell() + 4)
+ symboltable_address = struct.unpack("<L", f.read(4))[0]
+ num_symbols = struct.unpack("<L", f.read(4))[0]
+ optheader_size = struct.unpack("<H", f.read(2))[0]
+ # skip past PE header and PE optional header
+ f.seek(f.tell() + 2 + optheader_size)
+
+ for i in range(num_sections):
+ name = f.read(8).decode('ascii', errors='ignore').strip('\x00')
+ # skip past Misc and VirtualAddress
+ f.seek(f.tell() + 8)
+ # SizeOfRawData
+ size = struct.unpack("<L", f.read(4))[0]
+ # PointerToRawData
+ ptr = struct.unpack("<L", f.read(4))[0]
+ # skip to next section header
+ f.seek(f.tell() + 16)
+ sections.append([name, size, ptr])
+
+ symbols = []
+ addr = symboltable_address
+ stringtable_address = symboltable_address + num_symbols * 18
+ for i in range(num_symbols):
+ f.seek(addr)
+ tmp = f.read(8)
+ symaddr = struct.unpack("<L", f.read(4))[0]
+ # skip SectionNumber and Type
+ symtype = struct.unpack("B", f.read(1))[0]
+ f.seek(f.tell() + 4)
+ if tmp[:4] == b'\x00\x00\x00\x00':
+ # symbol name is in the string table
+ straddr = stringtable_address + struct.unpack("<l", tmp[4:])[0]
+ f.seek(straddr)
+ tmpname = f.read(64)
+ name = tmpname[:tmpname.find(b'\x00')]
+ else:
+ name = tmp
+ name = name.decode('ascii', errors='ignore').strip('\x00')
+ # need IMAGE_SYM_CLASS_EXTERNAL
+ if symtype == 0x02:
+ size = 0
+ else:
+ size = None
+ if i != 0 and symbols[-1][2] is not None and symaddr > symbols[-1][1]:
+ symbols[-1][2] = symaddr - symbols[-1][1]
+ symbols.append([name, symaddr, size])
+ addr += 18
+
+ f.close()
+
+ return sections, symbols
+
+
+def list_all(filename):
+ sections, symbols = parse(filename)
+ if sections:
+ print("Sections:\n Name Size\t Position")
+ cnt = 0
+ for name, size, address in sections:
+ print("%-3d %-8s %5d\t 0x%.8x" % (cnt, name, size, address))
+ cnt += 1
+ if symbols:
+ print("\nSymbol table:\n Address Size Symbol")
+ for symbol, address, size in symbols:
+ if size is not None:
+ sizestr = "%5d" % size
+ else:
+ sizestr = " "
+ print("0x%.8x %s %-8s" % (address, sizestr, symbol))
+ print()
+
+
+def extract_symbol(filename, symbol):
+ sections, symbols = parse(filename)
+ if not symbols:
+ return None
+ data = None
+ for symbolname, address, size in symbols:
+ if symbolname == symbol:
+ if size is None:
+ raise Exception("symbol %s found, but has unknown size")
+ f = open(filename, 'rb')
+ f.seek(address)
+ data = f.read(size)
+ f.close()
+ if len(data) != size:
+ raise Exception("short file")
+ break
+
+ if data is None:
+ raise Exception("symbol %s not found" % symbol)
+
+ return data
+
+
+
+def usage():
+ print("usage: parsepe.py [-s <symbol>] <-l|-x> <filename>")
+ print(" -l list all sections and symbols in file")
+ print(" -x extract symbol from file (specify symbol name with -s)")
+ sys.exit()
+
+
+#
+# main
+#
+
+if __name__ == '__main__':
+ filename = symbol = mode = None
+ opts, args = getopt(sys.argv[1:], 's:lx')
+ for opt, arg in opts:
+ if opt == '-s':
+ symbol = arg
+ elif opt == '-l':
+ mode = 'list'
+ elif opt == '-x':
+ mode = 'extract'
+
+ if len(args) != 1:
+ usage()
+ if mode is None and symbol is None:
+ usage()
+
+ try:
+ filename = args[0]
+ if mode == 'list':
+ list_all(filename)
+ elif mode == 'extract':
+ if symbol is None:
+ raise Exception("specify a symbol to extract")
+ data = extract_symbol(filename, symbol)
+ outfile = os.path.splitext(filename)[0] + symbol
+ open(outfile, 'wb').write(data)
+ print("saved %d bytes to %s" % (len(data), outfile))
+ else:
+ raise Exception("specify -l or -x")
+ except Exception as e:
+ print("Error: %s" % str(e))
+
+
--- /dev/null
+#!/usr/bin/python3
+##
+## This file is part of the sigrok-util project.
+##
+## Copyright (C) 2012 Bert Vermeulen <bert@biot.com>
+##
+## 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 sys
+import os
+import re
+import struct
+from array import array
+
+import parsepe
+
+
+def find_model(filename):
+ filename = os.path.split(filename)[-1]
+ m = re.search('^dso([a-z0-9]+)1.sys$', filename, re.I)
+ if m:
+ model = m.group(1).upper()
+ model = model.replace('X86', '').replace('AMD64', '').replace('IA64', '')
+ if model == '520A':
+ model = '5200A'
+ else:
+ model = 'unknown'
+
+ return model
+
+
+def unsparse(data):
+ p = 0
+ maxaddr = 0
+ blob = array('B', [0] * 0x4000)
+ while p <= len(data) and data[p+4] == 0:
+ num_bytes = struct.unpack("<H", data[p:p+2])[0]
+ address = struct.unpack("<H", data[p+2:p+4])[0]
+ chunk = array('B')
+ chunk.frombytes(data[p+5:p+5+num_bytes])
+ p += 22
+
+ if address > 0x4000:
+ # the FX2 only has 16K RAM. other writes are to registers
+ # in the 0xe000 region, skip those
+ continue
+
+ blob[address:address+num_bytes] = chunk
+
+ if address + num_bytes > maxaddr:
+ maxaddr = address + num_bytes
+
+ return blob[:maxaddr].tostring()
+
+
+def usage():
+ print("sigrok-fwextract-hantek-dso <driverfile>")
+ sys.exit()
+
+
+#
+# main
+#
+
+if len(sys.argv) != 2:
+ usage()
+
+try:
+ filename = sys.argv[1]
+ binihx = parsepe.extract_symbol(filename, '_firmware')
+ if binihx is None:
+ raise Exception("no firmware found")
+ blob = unsparse(binihx)
+ outfile = 'hantek-dso-' + find_model(filename) + '.fw'
+ open(outfile, 'wb').write(blob)
+ print("saved %d bytes to %s" % (len(blob), outfile))
+except Exception as e:
+ print("Error: %s" % str(e))
+
+
+
+++ /dev/null
-#!/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
+++ /dev/null
-#!/usr/bin/python3
-##
-## This file is part of the sigrok-util project.
-##
-## Copyright (C) 2012 Bert Vermeulen <bert@biot.com>
-##
-## 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 sys
-import os
-from getopt import getopt
-import struct
-
-
-def parse(filename):
- f = open(filename, 'rb')
- if f.read(2) != b'MZ':
- raise Exception("MZ signature not found.")
-
- sections = []
- # long e_lfanew
- f.seek(0x3c)
- pe_ptr = struct.unpack("<L", f.read(4))[0]
- f.seek(pe_ptr)
- if f.read(4) != b'\x50\x45\x00\x00':
- raise Exception("PE signature not found.")
- # skip Machine
- f.seek(f.tell() + 2)
- sections.append(['header', 324, 0])
- num_sections = struct.unpack("<H", f.read(2))[0]
- # skip TimeDateStamp
- f.seek(f.tell() + 4)
- symboltable_address = struct.unpack("<L", f.read(4))[0]
- num_symbols = struct.unpack("<L", f.read(4))[0]
- optheader_size = struct.unpack("<H", f.read(2))[0]
- # skip past PE header and PE optional header
- f.seek(f.tell() + 2 + optheader_size)
-
- for i in range(num_sections):
- name = f.read(8).decode('ascii', errors='ignore').strip('\x00')
- # skip past Misc and VirtualAddress
- f.seek(f.tell() + 8)
- # SizeOfRawData
- size = struct.unpack("<L", f.read(4))[0]
- # PointerToRawData
- ptr = struct.unpack("<L", f.read(4))[0]
- # skip to next section header
- f.seek(f.tell() + 16)
- sections.append([name, size, ptr])
-
- symbols = []
- addr = symboltable_address
- stringtable_address = symboltable_address + num_symbols * 18
- for i in range(num_symbols):
- f.seek(addr)
- tmp = f.read(8)
- symaddr = struct.unpack("<L", f.read(4))[0]
- # skip SectionNumber and Type
- symtype = struct.unpack("B", f.read(1))[0]
- f.seek(f.tell() + 4)
- if tmp[:4] == b'\x00\x00\x00\x00':
- # symbol name is in the string table
- straddr = stringtable_address + struct.unpack("<l", tmp[4:])[0]
- f.seek(straddr)
- tmpname = f.read(64)
- name = tmpname[:tmpname.find(b'\x00')]
- else:
- name = tmp
- name = name.decode('ascii', errors='ignore').strip('\x00')
- # need IMAGE_SYM_CLASS_EXTERNAL
- if symtype == 0x02:
- size = 0
- else:
- size = None
- if i != 0 and symbols[-1][2] is not None and symaddr > symbols[-1][1]:
- symbols[-1][2] = symaddr - symbols[-1][1]
- symbols.append([name, symaddr, size])
- addr += 18
-
- f.close()
-
- return sections, symbols
-
-
-def list_all(filename):
- sections, symbols = parse(filename)
- if sections:
- print("Sections:\n Name Size\t Position")
- cnt = 0
- for name, size, address in sections:
- print("%-3d %-8s %5d\t 0x%.8x" % (cnt, name, size, address))
- cnt += 1
- if symbols:
- print("\nSymbol table:\n Address Size Symbol")
- for symbol, address, size in symbols:
- if size is not None:
- sizestr = "%5d" % size
- else:
- sizestr = " "
- print("0x%.8x %s %-8s" % (address, sizestr, symbol))
- print()
-
-
-def extract_symbol(filename, symbol):
- sections, symbols = parse(filename)
- if not symbols:
- return None
- data = None
- for symbolname, address, size in symbols:
- if symbolname == symbol:
- if size is None:
- raise Exception("symbol %s found, but has unknown size")
- f = open(filename, 'rb')
- f.seek(address)
- data = f.read(size)
- f.close()
- if len(data) != size:
- raise Exception("short file")
- break
-
- if data is None:
- raise Exception("symbol %s not found" % symbol)
-
- return data
-
-
-
-def usage():
- print("usage: parsepe.py [-s <symbol>] <-l|-x> <filename>")
- print(" -l list all sections and symbols in file")
- print(" -x extract symbol from file (specify symbol name with -s)")
- sys.exit()
-
-
-#
-# main
-#
-
-if __name__ == '__main__':
- filename = symbol = mode = None
- opts, args = getopt(sys.argv[1:], 's:lx')
- for opt, arg in opts:
- if opt == '-s':
- symbol = arg
- elif opt == '-l':
- mode = 'list'
- elif opt == '-x':
- mode = 'extract'
-
- if len(args) != 1:
- usage()
- if mode is None and symbol is None:
- usage()
-
- try:
- filename = args[0]
- if mode == 'list':
- list_all(filename)
- elif mode == 'extract':
- if symbol is None:
- raise Exception("specify a symbol to extract")
- data = extract_symbol(filename, symbol)
- outfile = os.path.splitext(filename)[0] + symbol
- open(outfile, 'wb').write(data)
- print("saved %d bytes to %s" % (len(data), outfile))
- else:
- raise Exception("specify -l or -x")
- except Exception as e:
- print("Error: %s" % str(e))
-
-
+++ /dev/null
-#!/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 sys
-import parseelf
-
-def extract_symbol(elf, symname, filename):
- blob = elf.load_symbol(elf.dynsym[symname])
- f = open(filename, 'wb')
- f.write(blob)
- f.close()
- print("saved %d bytes to %s" % (len(blob), filename))
-
-def extract_bitstream(elf, lv):
- extract_symbol(elf, 'gLogic16Lv'+lv+'CompressedBitstream',
- 'saleae-logic16-fpga-'+lv+'.bitstream')
-
-def usage():
- print("saleae-logic16-extract.py <programfile>")
- sys.exit()
-
-
-#
-# main
-#
-
-if len(sys.argv) != 2:
- usage()
-
-try:
- filename = sys.argv[1]
- elf = parseelf.elf(filename)
- extract_bitstream(elf, '18')
- extract_bitstream(elf, '33')
-except Exception as e:
- print("Error: %s" % str(e))
-
--- /dev/null
+This tool extracts FPGA bitstreams from the
+vendor software for the Saleae Logic16 USB logic analyzer. Download
+the Linux version (either 32-bit or 64-bit will do), and unpack it
+to find the main binary called "Logic".
+
+Usage:
+
+$ ./sigrok-fwextract-saleae-logic16 Logic
+saved 149516 bytes to saleae-logic16-fpga-18.bitstream
+saved 149516 bytes to saleae-logic16-fpga-33.bitstream
+
+Copy the resulting files over to the location where sigrok installed its
+firmware files. By default this is /usr/local/share/sigrok-firmware.
--- /dev/null
+#!/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
--- /dev/null
+#!/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 sys
+import parseelf
+
+def extract_symbol(elf, symname, filename):
+ blob = elf.load_symbol(elf.dynsym[symname])
+ f = open(filename, 'wb')
+ f.write(blob)
+ f.close()
+ print("saved %d bytes to %s" % (len(blob), filename))
+
+def extract_bitstream(elf, lv):
+ extract_symbol(elf, 'gLogic16Lv'+lv+'CompressedBitstream',
+ 'saleae-logic16-fpga-'+lv+'.bitstream')
+
+def usage():
+ print("sigrok-fwextract-saleae-logic16 <programfile>")
+ sys.exit()
+
+
+#
+# main
+#
+
+if len(sys.argv) != 2:
+ usage()
+
+try:
+ filename = sys.argv[1]
+ elf = parseelf.elf(filename)
+ extract_bitstream(elf, '18')
+ extract_bitstream(elf, '33')
+except Exception as e:
+ print("Error: %s" % str(e))
+