aboutsummaryrefslogtreecommitdiff
path: root/science/sigrok-firmware-utils
diff options
context:
space:
mode:
authorJohn Marino <marino@FreeBSD.org>2014-08-03 22:09:07 +0000
committerJohn Marino <marino@FreeBSD.org>2014-08-03 22:09:07 +0000
commit7b05344a71552fbc8b445d519493486610a4dc1a (patch)
tree989d2ad7d55bfdb2d748a5349d44d0b49ac777d3 /science/sigrok-firmware-utils
parentf18ad55f8de6239f78357b14dd076d24397885da (diff)
Notes
Diffstat (limited to 'science/sigrok-firmware-utils')
-rw-r--r--science/sigrok-firmware-utils/Makefile43
-rw-r--r--science/sigrok-firmware-utils/files/README.parsepe11
-rw-r--r--science/sigrok-firmware-utils/files/parseelf.py172
-rw-r--r--science/sigrok-firmware-utils/files/parsepe.py183
-rw-r--r--science/sigrok-firmware-utils/files/sigrok-fwextract-hantek-dso90
-rw-r--r--science/sigrok-firmware-utils/files/sigrok-fwextract-hantek-dso.139
-rw-r--r--science/sigrok-firmware-utils/files/sigrok-fwextract-saleae-logic16329
-rw-r--r--science/sigrok-firmware-utils/files/sigrok-fwextract-saleae-logic16.140
-rw-r--r--science/sigrok-firmware-utils/files/sigrok-fwextract-sysclk-lwla41
-rw-r--r--science/sigrok-firmware-utils/files/sigrok-fwextract-sysclk-lwla.133
-rw-r--r--science/sigrok-firmware-utils/pkg-descr8
11 files changed, 989 insertions, 0 deletions
diff --git a/science/sigrok-firmware-utils/Makefile b/science/sigrok-firmware-utils/Makefile
new file mode 100644
index 000000000000..e25be0791548
--- /dev/null
+++ b/science/sigrok-firmware-utils/Makefile
@@ -0,0 +1,43 @@
+# Created by: Uffe Jakobsen <uffe@uffe.org>
+# $FreeBSD$
+
+PORTNAME= firmware
+PORTVERSION= 20140418
+CATEGORIES= science
+MASTER_SITES= #none
+PKGNAMEPREFIX= sigrok-
+PKGNAMESUFFIX= -utils
+DISTFILES= #none
+
+MAINTAINER= uffe@uffe.org
+COMMENT= Sigrok firmware extraction utils
+
+LICENSE= GPLv2
+
+USE_PYTHON= 3
+NO_BUILD= yes
+
+PORTDOCS= README.parsepe
+
+PY_FILES= parsepe.py parseelf.py
+
+EX_FILES= sigrok-fwextract-hantek-dso \
+ sigrok-fwextract-saleae-logic16 \
+ sigrok-fwextract-sysclk-lwla
+
+PLIST_FILES= ${PY_FILES:S,^,bin/,} \
+ ${EX_FILES:S,^,bin/,} \
+ ${EX_FILES:S,^,man/man1/,:S,$,.1.gz,}
+
+do-install:
+ @${MKDIR} ${STAGEDIR}${DOCSDIR}
+ ${INSTALL_MAN} ${FILESDIR}/README.parsepe ${STAGEDIR}${DOCSDIR}
+.for fil in ${PY_FILES}
+ ${INSTALL_SCRIPT} ${FILESDIR}/${fil} ${STAGEDIR}${PREFIX}/bin
+.endfor
+.for fil in ${EX_FILES}
+ ${INSTALL_SCRIPT} ${FILESDIR}/${fil} ${STAGEDIR}${PREFIX}/bin
+ ${INSTALL_MAN} ${FILESDIR}/${fil}.1 ${STAGEDIR}${MANDIRS}/man1
+.endfor
+
+.include <bsd.port.mk>
diff --git a/science/sigrok-firmware-utils/files/README.parsepe b/science/sigrok-firmware-utils/files/README.parsepe
new file mode 100644
index 000000000000..8a69b6f5545c
--- /dev/null
+++ b/science/sigrok-firmware-utils/files/README.parsepe
@@ -0,0 +1,11 @@
+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
+
diff --git a/science/sigrok-firmware-utils/files/parseelf.py b/science/sigrok-firmware-utils/files/parseelf.py
new file mode 100644
index 000000000000..4f267eaa0012
--- /dev/null
+++ b/science/sigrok-firmware-utils/files/parseelf.py
@@ -0,0 +1,172 @@
+#!/usr/bin/env 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_rela(this):
+ return this.read_struct('NNn', ('r_offset', 'r_info', 'r_addend'))
+
+ def parse_rel(this):
+ return this.read_struct('NN', ('r_offset', 'r_info'))
+
+ def fixup_reloc(this, reloc):
+ if not 'r_addend' in reloc:
+ reloc['r_addend'] = 0
+ if this.elf_wordsize == 64:
+ reloc['r_sym'] = reloc['r_info'] >> 32
+ reloc['r_type'] = reloc['r_info'] & 0xffffffff
+ else:
+ reloc['r_sym'] = reloc['r_info'] >> 8
+ reloc['r_type'] = reloc['r_info'] & 0xff
+ return reloc
+
+ 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 = [dict(this.parse_symbol(),number=i) 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 parse_relocs(this, section):
+ this.file.seek(section['sh_offset'])
+ if section['sh_type'] == 4:
+ relocs = [this.fixup_reloc(this.parse_rela()) for i in
+ range(0, section['sh_size'] // section['sh_entsize'])]
+ else:
+ relocs = [this.fixup_reloc(this.parse_rel()) for i in
+ range(0, section['sh_size'] // section['sh_entsize'])]
+ return relocs
+
+ 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')
+
+ this.relocs = {}
+ for section in this.shdrs:
+ if section['sh_type'] == 4 or section['sh_type'] == 9:
+ rels = {}
+ symsec = this.shdrs[section['sh_link']]
+ if this.get_name(symsec['sh_name']) == '.symtab':
+ rels['symbols'] = this.symtab
+ elif this.get_name(symsec['sh_name']) == '.dynsym':
+ rels['symbols'] = this.dynsym
+ rels['relocs'] = this.parse_relocs(section)
+ this.relocs[this.get_name(section['sh_name'])] = rels
+
+ def __del__(this):
+ try:
+ this.file.close()
+ except AttributeError:
+ pass
diff --git a/science/sigrok-firmware-utils/files/parsepe.py b/science/sigrok-firmware-utils/files/parsepe.py
new file mode 100644
index 000000000000..ac04fb2ee4b7
--- /dev/null
+++ b/science/sigrok-firmware-utils/files/parsepe.py
@@ -0,0 +1,183 @@
+#!/usr/bin/env 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))
+
+
diff --git a/science/sigrok-firmware-utils/files/sigrok-fwextract-hantek-dso b/science/sigrok-firmware-utils/files/sigrok-fwextract-hantek-dso
new file mode 100644
index 000000000000..c134578f10ff
--- /dev/null
+++ b/science/sigrok-firmware-utils/files/sigrok-fwextract-hantek-dso
@@ -0,0 +1,90 @@
+#!/usr/bin/env 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))
diff --git a/science/sigrok-firmware-utils/files/sigrok-fwextract-hantek-dso.1 b/science/sigrok-firmware-utils/files/sigrok-fwextract-hantek-dso.1
new file mode 100644
index 000000000000..3f6bd1942001
--- /dev/null
+++ b/science/sigrok-firmware-utils/files/sigrok-fwextract-hantek-dso.1
@@ -0,0 +1,39 @@
+.TH SIGROK\-FWEXTRACT\-HANTEK\-DSO 1 "Aug 08, 2013"
+.SH "NAME"
+sigrok\-fwextract\-hantek\-dso \- Extract Hantek DSO-2xxx/52xx firmware
+.SH "SYNOPSIS"
+.B sigrok\-fwextract\-hantek\-dso [FILE]
+.SH "DESCRIPTION"
+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
+.B DSOxxxx1.sys
+or
+.BR DsoxxxxX861.sys ,
+where xxxx is your device's model.
+.PP
+Use it like this:
+.PP
+.B " $ sigrok-fwextract-hantek-dso Dso2090X861.sys"
+.br
+.RB " saved 4730 bytes to hantek-dso-2090.fw"
+.PP
+Copy the resulting file over to the location where libsigrok expects
+to find its firmware files. By default this is
+.BR /usr/local/share/sigrok-firmware .
+.SH OPTIONS
+None.
+.SH "EXIT STATUS"
+Exits with 0 on success, 1 on most failures.
+.SH "SEE ALSO"
+\fBsigrok\-fwextract\-saleae\-logic16\fP(1)
+.SH "BUGS"
+Please report any bugs via Bugzilla
+.RB "(" http://sigrok.org/bugzilla ")"
+or on the sigrok\-devel mailing list
+.RB "(" sigrok\-devel@lists.souceforge.net ")."
+.SH "LICENSE"
+This program is covered by the GNU General Public License (GPL),
+version 3 or later.
+.SH "AUTHORS"
+Please see the individual source code files.
diff --git a/science/sigrok-firmware-utils/files/sigrok-fwextract-saleae-logic16 b/science/sigrok-firmware-utils/files/sigrok-fwextract-saleae-logic16
new file mode 100644
index 000000000000..10fec9e33154
--- /dev/null
+++ b/science/sigrok-firmware-utils/files/sigrok-fwextract-saleae-logic16
@@ -0,0 +1,329 @@
+#!/usr/bin/env 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 struct
+import parseelf
+
+class searcher:
+
+ def reset(this, offs=0):
+ if offs < 0 or offs > this.length:
+ raise Exception('Reset past end of section')
+ this.address = this.baseaddr + offs
+ this.offset = offs
+
+ def skip(this, cnt):
+ if this.offset + cnt > this.length:
+ raise Exception('Skip past end of section')
+ this.address += cnt
+ this.offset += cnt
+
+ def peek(this, cnt, offs=0):
+ if this.offset + offs + cnt > this.length:
+ raise Exception('Peek past end of section')
+ return this.data[this.offset + offs : this.offset + offs + cnt]
+
+ def look_for(this, needle):
+ pos = this.data.find(needle, this.offset)
+ if pos < 0:
+ raise Exception('Needle not found in haystack')
+ this.skip(pos - this.offset)
+
+ def look_for_either(this, needle1, needle2):
+ pos1 = this.data.find(needle1, this.offset)
+ pos2 = this.data.find(needle2, this.offset)
+ if pos1 < 0 and pos2 < 0:
+ raise Exception('Needle not found in haystack')
+ if pos1 < 0 or pos2 < pos1:
+ pos1 = pos2
+ this.skip(pos1 - this.offset)
+
+ def __init__(this, data, addr):
+ this.data = data
+ this.baseaddr = addr
+ this.length = len(data)
+ this.reset()
+
+def search_plt_32(plt, addr):
+ plt.reset()
+ plt.look_for(struct.pack('<BBI', 0xff, 0x25, addr)) # jmp *addr32
+ return plt.address
+
+def search_plt_64(plt, addr):
+ plt.reset()
+ while True:
+ plt.look_for(b'\xff\x25') # jmpq *offs32(%rip)
+ offs = struct.unpack('<i', plt.peek(4, 2))[0]
+ if plt.address + offs + 6 == addr:
+ return plt.address
+ plt.skip(2)
+
+def find_hex_file_lines_constructor_32(text, hex_file_lines_got, got_plt):
+ while True:
+ text.look_for_either(b'\x8b\xbb', b'\x8b\xb3') # mov offs32(%ebx),{%edi,%esi}
+ offs = struct.unpack('<i', text.peek(4, 2))[0]
+ if got_plt + offs == hex_file_lines_got:
+ text.skip(6)
+ return
+ text.skip(2)
+
+def find_hex_file_lines_constructor_64(text, hex_file_lines_got):
+ while True:
+ text.look_for(b'\x48\x8b\x2d') # mov offs32(%rip),%rbp
+ offs = struct.unpack('<i', text.peek(4, 3))[0]
+ if text.address + offs + 7 == hex_file_lines_got:
+ text.skip(7)
+ return
+ text.skip(3)
+
+def parse_hex_file_lines_constructor_32(text, basic_string_plt, got_plt, lines):
+ text.skip(-5)
+ esi = (text.peek(1) == b'\xb3')
+ text.skip(5)
+ cnt = len(lines)
+ while cnt > 0:
+ if text.peek(2) == b'\x8d\x45': # lea offs8(%ebp),%eax
+ text.skip(3)
+ elif text.peek(2) == b'\x8d\x85': # lea offs32(%ebp),%eax
+ text.skip(6)
+ if text.peek(1) == (b'\xbf' if esi else b'\xbe'): # mov $imm32,%esi
+ text.skip(5)
+ elif text.peek(2) == (b'\x31\xff' if esi else b'\x31\xf6'): # xor %esi,%esi
+ text.skip(2)
+ if text.peek(4) == b'\x89\x44\x24\x08': # mov %eax,0x8(%esp)
+ text.skip(4)
+ if text.peek(2) == b'\x8d\x83': # lea offs32(%ebx),%eax
+ straddr = struct.unpack('<i', text.peek(4, 2))[0]
+ text.skip(6)
+ straddr += got_plt
+ else:
+ raise Exception('Expected lea offs32(%ebx),%eax @ ' +
+ ('0x%x' % text.address))
+ if text.peek(4) == b'\x89\x44\x24\x04': # mov %eax,0x4(%esp)
+ text.skip(4)
+ if text.peek(3) == (b'\x89\x34\x24' if esi else b'\x89\x3c\x24'): # mov %edi,(%esp)
+ offs = 0
+ text.skip(3)
+ elif text.peek(2) == (b'\x8d\x46' if esi else b'\x8d\x47'): # lea offs8(%edi),%eax
+ offs = struct.unpack('<b', text.peek(1, 2))[0]
+ text.skip(3)
+ elif text.peek(2) == (b'\x8d\x86' if esi else b'\x8d\x87'): # lea offs32(%edi),%eax
+ offs = struct.unpack('<i', text.peek(4, 2))[0]
+ text.skip(6)
+ else:
+ raise Exception('Expected lea offs(%e'+('s' if esi else 'd')+'i),%eax @ ' +
+ ('0x%x' % text.address))
+ if offs < 0 or offs > (len(lines) << 2) or (offs & 3) != 0:
+ raise Exception('Invalid offset %d' % offs)
+ index = offs >> 2
+ if lines[index] != 0:
+ raise Exception('Line %d filled multiple times' % index)
+ if text.peek(3) == b'\x89\x04\x24': # mov %eax,(%esp)
+ text.skip(3)
+ if text.peek(1) == b'\xe8': # call offs32
+ offs = struct.unpack('<i', text.peek(4, 1))[0]
+ text.skip(5)
+ if text.address + offs != basic_string_plt:
+ raise Exception('Expected call ZNSsC1EPKcRKSaIcE@plt @ ' +
+ ('0x%x' % text.address))
+ else:
+ raise Exception('Expected call ZNSsC1EPKcRKSaIcE@plt @ ' +
+ ('0x%x' % text.address))
+ if straddr == 0:
+ raise Exception('NULL pointer stored to index %d' % index)
+ lines[index] = straddr
+ cnt -= 1
+
+def parse_hex_file_lines_constructor_64(text, basic_string_plt, lines):
+ cnt = len(lines)
+ while cnt > 0:
+ if text.peek(1) == b'\xbb': # mov $imm32,%ebx
+ text.skip(5)
+ elif text.peek(2) == b'\x31\xdb': # xor %ebx,%ebx
+ text.skip(2)
+ if text.peek(4) == b'\x48\x8d\x54\x24': # lea offs8(%rsp),%rdx
+ text.skip(5)
+ elif text.peek(4) == b'\x48\x8d\x94\x24': # lea offs32(%rsp),%rdx
+ text.skip(8)
+ if text.peek(3) == b'\x48\x8d\x35': # lea offs32(%rip),%rsi
+ straddr = struct.unpack('<i', text.peek(4, 3))[0]
+ text.skip(7)
+ straddr += text.address
+ else:
+ raise Exception('Expected lea offs(%rip),%rsi @ ' +
+ ('0x%x' % text.address))
+ if text.peek(3) == b'\x48\x89\xef': # mov %rbp,%rdi
+ offs = 0
+ text.skip(3)
+ elif text.peek(3) == b'\x48\x8d\x7d': # lea offs8(%rbp),%rdi
+ offs = struct.unpack('<b', text.peek(1, 3))[0]
+ text.skip(4)
+ elif text.peek(3) == b'\x48\x8d\xbd': # lea offs32(%rbp),%rdi
+ offs = struct.unpack('<i', text.peek(4, 3))[0]
+ text.skip(7)
+ else:
+ raise Exception('Expected lea offs(%rbp),%rdi @ ' +
+ ('0x%x' % text.address))
+ if text.peek(1) == b'\xbb': # mov $imm32,%ebx
+ text.skip(5)
+ elif text.peek(2) == b'\x31\xdb': # xor %ebx,%ebx
+ text.skip(2)
+ if offs < 0 or offs > (len(lines) << 3) or (offs & 7) != 0:
+ raise Exception('Invalid offset %d' % offs)
+ index = offs >> 3
+ if lines[index] != 0:
+ raise Exception('Line %d filled multiple times' % index)
+ if text.peek(1) == b'\xe8': # callq offs32
+ offs = struct.unpack('<i', text.peek(4, 1))[0]
+ text.skip(5)
+ if text.address + offs != basic_string_plt:
+ raise Exception('Expected callq ZNSsC1EPKcRKSaIcE@plt @ ' +
+ ('0x%x' % text.address))
+ else:
+ raise Exception('Expected callq ZNSsC1EPKcRKSaIcE@plt @ ' +
+ ('0x%x' % text.address))
+ if straddr == 0:
+ raise Exception('NULL pointer stored to index %d' % index)
+ lines[index] = straddr
+ cnt -= 1
+
+def find_reloc(elf, symname):
+ for section, relocs in elf.relocs.items():
+ if 'symbols' in relocs and symname in relocs['symbols']:
+ symnum = relocs['symbols'][symname]['number']
+ for reloc in relocs['relocs']:
+ if reloc['r_sym'] == symnum:
+ return reloc
+ raise Exception('Unable to find a relocation against ' + symname)
+
+def ihex_to_binary(lines):
+ chunks = {}
+ for line in lines:
+ if line[0] != ':':
+ raise Exception('ihex line does not start with ":"')
+ line = bytes.fromhex(line[1:])
+ if (sum(bytearray(line)) & 0xff) != 0:
+ raise Exception('Invalid checksum in ihex')
+ (byte_count, address, rectype) = struct.unpack('>BHB', line[:4])
+ (data, checksum) = struct.unpack('>%dsB' % (byte_count), line[4:])
+ if rectype == 1 and byte_count == 0:
+ pass
+ elif rectype != 0 or byte_count == 0:
+ raise Exception('Unexpected rectype %d with bytecount %d' %
+ (rectype, byte_count))
+ elif address in chunks:
+ raise Exception('Multiple ihex lines with address 0x%x' % address)
+ else:
+ chunks[address] = data
+ blob = b''
+ for address in sorted(iter(chunks)):
+ if address < len(blob):
+ raise Exception('Overlapping ihex chunks')
+ elif address > len(blob):
+ blob += b'\x00' * (address - len(blob))
+ blob += chunks[address]
+ return blob
+
+def extract_fx2_firmware(elf, symname, filename):
+ blob = elf.load_symbol(elf.dynsym[symname + 'Count'])
+ count = struct.unpack('<I', blob)[0]
+ got_plt = elf.find_section('.got.plt')['sh_addr']
+ hex_file_lines_got = find_reloc(elf, symname)['r_offset']
+ basic_string_got = find_reloc(elf, '_ZNSsC1EPKcRKSaIcE')['r_offset']
+ pltsec = elf.find_section('.plt')
+ plt = searcher(elf.read_section(pltsec), pltsec['sh_addr'])
+ try:
+ if elf.elf_wordsize == 64:
+ basic_string_plt = search_plt_64(plt, basic_string_got)
+ else:
+ basic_string_plt = search_plt_32(plt, basic_string_got)
+ except:
+ raise Exception('Unable to find a PLT entry for _ZNSsC1EPKcRKSaIcE')
+ textsec = elf.find_section('.text')
+ text = searcher(elf.read_section(textsec), textsec['sh_addr'])
+ while True:
+ try:
+ if elf.elf_wordsize == 64:
+ find_hex_file_lines_constructor_64(text, hex_file_lines_got)
+ else:
+ find_hex_file_lines_constructor_32(text, hex_file_lines_got,
+ got_plt)
+ except:
+ raise Exception('Unable to find constructor for ' + symname)
+ oldoffs = text.offset
+ l = [0]*count
+ try:
+ if elf.elf_wordsize == 64:
+ parse_hex_file_lines_constructor_64(text, basic_string_plt, l)
+ else:
+ parse_hex_file_lines_constructor_32(text, basic_string_plt,
+ got_plt, l)
+ break
+ except KeyError:
+ text.reset(oldoffs)
+ rodatasec = elf.find_section('.rodata')
+ rodata = elf.read_section(rodatasec)
+ lo = rodatasec['sh_addr']
+ hi = lo + rodatasec['sh_size']
+ for i in range(count):
+ addr = l[i]
+ if addr < lo or addr >= hi:
+ raise Exception('Address 0x%x outside of .rodata section' % addr)
+ l[i] = elf.get_name(addr - lo, rodata)
+ blob = ihex_to_binary(l)
+ f = open(filename, 'wb')
+ f.write(blob)
+ f.close()
+ print("saved %d bytes to %s" % (len(blob), filename))
+
+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)
+ if elf.ehdr['e_machine'] != 3 and elf.ehdr['e_machine'] != 62:
+ raise Exception('Unsupported e_machine')
+ extract_fx2_firmware(elf, 'gLogic16HexFileLines', 'saleae-logic16-fx2.fw')
+ extract_bitstream(elf, '18')
+ extract_bitstream(elf, '33')
+except Exception as e:
+ print("Error: %s" % str(e))
diff --git a/science/sigrok-firmware-utils/files/sigrok-fwextract-saleae-logic16.1 b/science/sigrok-firmware-utils/files/sigrok-fwextract-saleae-logic16.1
new file mode 100644
index 000000000000..3766602096ed
--- /dev/null
+++ b/science/sigrok-firmware-utils/files/sigrok-fwextract-saleae-logic16.1
@@ -0,0 +1,40 @@
+.TH SIGROK\-FWEXTRACT\-SALEAE\-LOGIC16 1 "Aug 08, 2013"
+.SH "NAME"
+sigrok\-fwextract\-saleae\-logic16 \- Extract Saleae Logic16 firmware
+.SH "SYNOPSIS"
+.B sigrok\-fwextract\-saleae\-logic16 [FILE]
+.SH "DESCRIPTION"
+This tool extracts FX2 firmware and 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".
+.PP
+In order to extract the firmware/bitstreams, run the following command:
+.PP
+.B " $ sigrok-fwextract-saleae-logic16 Logic"
+.br
+.RB " saved 5214 bytes to saleae-logic16-fx2.fw"
+.br
+.RB " saved 149516 bytes to saleae-logic16-fpga-18.bitstream"
+.br
+.RB " saved 149516 bytes to saleae-logic16-fpga-33.bitstream"
+.PP
+Copy the resulting files over to the location where libsigrok expects
+to find its firmware files. By default this is
+.BR /usr/local/share/sigrok-firmware .
+.SH OPTIONS
+None.
+.SH "EXIT STATUS"
+Exits with 0 on success, 1 on most failures.
+.SH "SEE ALSO"
+\fBsigrok\-fwextract\-hantek\-dso\fP(1)
+.SH "BUGS"
+Please report any bugs via Bugzilla
+.RB "(" http://sigrok.org/bugzilla ")"
+or on the sigrok\-devel mailing list
+.RB "(" sigrok\-devel@lists.souceforge.net ")."
+.SH "LICENSE"
+This program is covered by the GNU General Public License (GPL),
+version 3 or later.
+.SH "AUTHORS"
+Please see the individual source code files.
diff --git a/science/sigrok-firmware-utils/files/sigrok-fwextract-sysclk-lwla b/science/sigrok-firmware-utils/files/sigrok-fwextract-sysclk-lwla
new file mode 100644
index 000000000000..c9e261e25ba8
--- /dev/null
+++ b/science/sigrok-firmware-utils/files/sigrok-fwextract-sysclk-lwla
@@ -0,0 +1,41 @@
+#! /bin/sh -e
+##
+## This file is part of the sigrok-util project.
+##
+## Copyright (C) 2014 Daniel Elstner <daniel.kitta@gmail.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/>.
+##
+
+infile=$1
+if [ -z "$infile" ]; then
+ echo "Usage: $0 SETUP-EXE" >&2
+ exit 1
+fi
+
+# Verify the checksum to make sure this is the right binary file
+expectsum=f2a9333329200ad1d939d051257f914200cf0c765ff4962b2907dcf30716f455
+set '' $(sha256sum -b "$infile")
+
+if [ "$2" != "$expectsum" ]; then
+ echo "$0: checksum mismatch for '$infile'" >&2
+ echo "$0: make sure you picked the right file (lwla1034_EN_setup.exe on the CD-ROM)" >&2
+ exit 1
+fi
+
+# Extract the firmware binaries from the executable
+dd bs=1 skip=34110338 count=78398 if="$infile" of=sysclk-lwla1034-int.bitstream
+dd bs=1 skip=34266237 count=78247 if="$infile" of=sysclk-lwla1034-extpos.bitstream
+dd bs=1 skip=34344484 count=79145 if="$infile" of=sysclk-lwla1034-extneg.bitstream
+dd bs=1 skip=34578631 count=48525 if="$infile" of=sysclk-lwla1034-off.bitstream
diff --git a/science/sigrok-firmware-utils/files/sigrok-fwextract-sysclk-lwla.1 b/science/sigrok-firmware-utils/files/sigrok-fwextract-sysclk-lwla.1
new file mode 100644
index 000000000000..500f97f07e38
--- /dev/null
+++ b/science/sigrok-firmware-utils/files/sigrok-fwextract-sysclk-lwla.1
@@ -0,0 +1,33 @@
+.TH SIGROK\-FWEXTRACT\-SYSCLK\-LWLA 1 "Jan 04, 2014"
+.SH "NAME"
+sigrok\-fwextract\-sysclk\-lwla \- Extract SysClk LWLA* firmware
+.SH "SYNOPSIS"
+.B sigrok\-fwextract\-sysclk\-lwla SETUP-EXE
+.SH "DESCRIPTION"
+This tool extracts FPGA bitstreams from the vendor software for the SysClk
+LWLA1034 USB logic analyzer. Insert the CD-ROM that ships with the device,
+and locate the Windows installer executable "lwla1034_EN_setup.exe".
+.PP
+In order to extract the bitstreams, run the following command:
+.PP
+.B " $ sigrok-fwextract-sysclk-lwla lwla1034_EN_setup.exe"
+.PP
+Copy the resulting four bitstream files over to the location where libsigrok
+expects to find its firmware files. By default this is
+.BR /usr/local/share/sigrok-firmware .
+.SH OPTIONS
+None.
+.SH "EXIT STATUS"
+Exits with 0 on success, 1 on most failures.
+.SH "SEE ALSO"
+\fBsigrok\-fwextract\-hantek\-dso\fP(1)
+.SH "BUGS"
+Please report any bugs via Bugzilla
+.RB "(" http://sigrok.org/bugzilla ")"
+or on the sigrok\-devel mailing list
+.RB "(" sigrok\-devel@lists.souceforge.net ")."
+.SH "LICENSE"
+This program is covered by the GNU General Public License (GPL),
+version 3 or later.
+.SH "AUTHORS"
+Please see the individual source code files.
diff --git a/science/sigrok-firmware-utils/pkg-descr b/science/sigrok-firmware-utils/pkg-descr
new file mode 100644
index 000000000000..dcee66b9525d
--- /dev/null
+++ b/science/sigrok-firmware-utils/pkg-descr
@@ -0,0 +1,8 @@
+The sigrok project aims at creating a portable, cross-platform,
+Free/Libre/Open-Source signal analysis software suite that supports
+various device types, such as logic analyzers, MSOs, oscilloscopes,
+multimeters, LCR meters, sound level meters, thermometers, hygrometers,
+anemometers, light meters, DAQs, dataloggers, function generators,
+spectrum analyzers, power supplies, GPIB interfaces, and more.
+
+WWW: http://www.sigrok.org/wiki/Firmware