Skip site navigation (1)Skip section navigation (2)
Date:      Sat, 19 Apr 2014 22:09:01 GMT
From:      Uffe Jakobsen <uffe@uffe.org>
To:        freebsd-gnats-submit@FreeBSD.org
Subject:   ports/188808: New port: science/sigrok-firmware-utils
Message-ID:  <201404192209.s3JM917a095448@cgiserv.freebsd.org>
Resent-Message-ID: <201404192210.s3JMA0Fl099963@freefall.freebsd.org>

next in thread | raw e-mail | index | archive | help

>Number:         188808
>Category:       ports
>Synopsis:       New port: science/sigrok-firmware-utils
>Confidential:   no
>Severity:       non-critical
>Priority:       low
>Responsible:    freebsd-ports-bugs
>State:          open
>Quarter:        
>Keywords:       
>Date-Required:
>Class:          change-request
>Submitter-Id:   current-users
>Arrival-Date:   Sat Apr 19 22:10:00 UTC 2014
>Closed-Date:
>Last-Modified:
>Originator:     Uffe Jakobsen
>Release:        FreeBSD 10.0-RELEASE-p1
>Organization:
>Environment:
>Description:
New port: science/sigrok-firmware-utils

firmware extraction utils port/package for the existing sigrok ports

PS: these utils are not yet versioned by the sigrok project - and hence embedded into the files dir until a official release is made by the sigrok project.
 
>How-To-Repeat:

>Fix:


Patch attached with submission follows:

# This is a shell archive.  Save it in a file, remove anything before
# this line, and then unpack it by entering "sh file".  Note, it may
# create directories; files and directories will be owned by you and
# have default permissions.
#
# This archive contains:
#
#	sigrok-firmware-utils
#	sigrok-firmware-utils/pkg-descr
#	sigrok-firmware-utils/Makefile
#	sigrok-firmware-utils/files
#	sigrok-firmware-utils/files/README.parsepe
#	sigrok-firmware-utils/files/parseelf.py
#	sigrok-firmware-utils/files/parsepe.py
#	sigrok-firmware-utils/files/sigrok-fwextract-hantek-dso
#	sigrok-firmware-utils/files/sigrok-fwextract-hantek-dso.1
#	sigrok-firmware-utils/files/sigrok-fwextract-saleae-logic16
#	sigrok-firmware-utils/files/sigrok-fwextract-saleae-logic16.1
#	sigrok-firmware-utils/files/sigrok-fwextract-sysclk-lwla
#	sigrok-firmware-utils/files/sigrok-fwextract-sysclk-lwla.1
#
echo c - sigrok-firmware-utils
mkdir -p sigrok-firmware-utils > /dev/null 2>&1
echo x - sigrok-firmware-utils/pkg-descr
sed 's/^X//' >sigrok-firmware-utils/pkg-descr << '3b359701d5014b10b79994394229169b'
XThe sigrok project aims at creating a portable, cross-platform,
XFree/Libre/Open-Source signal analysis software suite that supports
Xvarious device types, such as logic analyzers, MSOs, oscilloscopes,
Xmultimeters, LCR meters, sound level meters, thermometers, 
Xhygrometers, anemometers, light meters, DAQs, dataloggers, 
Xfunction generators, spectrum analyzers, power supplies,
XGPIB interfaces, and more. 
X
XWWW: http://www.sigrok.org/wiki/Firmware
3b359701d5014b10b79994394229169b
echo x - sigrok-firmware-utils/Makefile
sed 's/^X//' >sigrok-firmware-utils/Makefile << 'e90f3a2def9b384cd15e058afd9abfa7'
X# Created by: Uffe Jakobsen <uffe@uffe.org>
X# $FreeBSD: $
X
XPORTNAME=	sigrok-firmware-utils
XPORTVERSION=	20140418
XPORTREVISION=	1
XCATEGORIES=	science
XMASTER_SITES=	#none
XDISTFILES=	#none
X
XMAINTAINER=	uffe@uffe.org
XCOMMENT=	Sigrok firmware extraction utils
X
XLICENSE=	GPLv2
X
X#RUN_DEPENDS=
X
XUSE_PYTHON=	3
XNO_BUILD=       yes
X
Xdo-install:
X	@${MKDIR} ${STAGEDIR}${DOCSDIR}
X	@${INSTALL_MAN} ${FILESDIR}/README.parsepe ${STAGEDIR}${DOCSDIR}
X.for fil in parsepe.py parseelf.py
X	@${INSTALL_SCRIPT} ${FILESDIR}/${fil} ${STAGEDIR}${PREFIX}/bin
X.endfor
X.for fil in sigrok-fwextract-hantek-dso sigrok-fwextract-saleae-logic16 sigrok-fwextract-sysclk-lwla
X	@${INSTALL_SCRIPT} ${FILESDIR}/${fil} ${STAGEDIR}${PREFIX}/bin
X	@${INSTALL_MAN} ${FILESDIR}/${fil}.1 ${STAGEDIR}${MANDIRS}/man1
X.endfor
X
X.include <bsd.port.mk>
e90f3a2def9b384cd15e058afd9abfa7
echo c - sigrok-firmware-utils/files
mkdir -p sigrok-firmware-utils/files > /dev/null 2>&1
echo x - sigrok-firmware-utils/files/README.parsepe
sed 's/^X//' >sigrok-firmware-utils/files/README.parsepe << '2b2202b606c8328182decf01fb94abe2'
XThe parsepe.py tool can list all sections and symbols from a PE (portable
Xexecutable) binary file, and extract the contents of a symbol.
X
Xusage:
Xparsepe.py -l <filename>                list all sections and symbols in file
Xparsepe.py -s <symbol> -x <filename>    extract symbol from file
X
XTODO:
X- currently only handles COFF symbol tables
X- can only extract external (global) symbols
X
2b2202b606c8328182decf01fb94abe2
echo x - sigrok-firmware-utils/files/parseelf.py
sed 's/^X//' >sigrok-firmware-utils/files/parseelf.py << '4d80edf57e0d349d85d48ea179dceac6'
X#!/usr/bin/env python3
X##
X## This file is part of the sigrok-util project.
X##
X## Copyright (C) 2013 Marcus Comstedt <marcus@mc.pp.se>
X##
X## This program is free software; you can redistribute it and/or modify
X## it under the terms of the GNU General Public License as published by
X## the Free Software Foundation; either version 3 of the License, or
X## (at your option) any later version.
X##
X## This program is distributed in the hope that it will be useful,
X## but WITHOUT ANY WARRANTY; without even the implied warranty of
X## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
X## GNU General Public License for more details.
X##
X## You should have received a copy of the GNU General Public License
X## along with this program; if not, see <http://www.gnu.org/licenses/>.
X##
X
Ximport struct
X
Xclass elf:
X
X    def read_struct(this, struct_fmt, struct_fields):
X        fmt = this.elf_endianprefix+str.translate(struct_fmt, this.elf_format);
X        fields = struct.unpack(fmt, this.file.read(struct.calcsize(fmt)))
X        return dict(zip(struct_fields, fields))
X
X    def read_ehdr(this):
X        return this.read_struct('16sHHWNNNWHHHHHH',
X                                ('e_ident', 'e_type', 'e_machine', 'e_version',
X                                 'e_entry', 'e_phoff', 'e_shoff', 'e_flags',
X                                 'e_ehsize', 'e_phentsize', 'e_phnum',
X                                 'e_shentsize', 'e_shnum', 'e_shstrndx'))
X
X    def read_shdr(this):
X        return this.read_struct('WWNNNNWWNN',
X                                ('sh_name', 'sh_type', 'sh_flags', 'sh_addr',
X                                 'sh_offset', 'sh_size', 'sh_link', 'sh_info',
X                                 'sh_addralign', 'sh_entsize'))
X
X    def read_section(this, shdr):
X        this.file.seek(shdr['sh_offset'])
X        return this.file.read(shdr['sh_size'])
X
X    def get_name(this, name, strtab=None):
X        strtab = strtab or this.strtab
X        nul = strtab.find(b'\0', name)
X        if nul < 0:
X            return bytes.decode(strtab[name:])
X        else:
X            return bytes.decode(strtab[name:nul])
X
X    def find_section(this, name):
X        for section in this.shdrs:
X            if this.get_name(section['sh_name']) == name:
X                return section
X        raise KeyError(name)
X
X    def parse_symbol(this):
X        if this.elf_wordsize == 64:
X            return this.read_struct('WBBHNX', 
X                                    ('st_name', 'st_info', 'st_other',
X                                     'st_shndx', 'st_value', 'st_size'))
X        else:
X            return this.read_struct('WNWBBH', 
X                                    ('st_name', 'st_value', 'st_size',
X                                     'st_info', 'st_other', 'st_shndx'))
X
X    def parse_rela(this):
X        return this.read_struct('NNn', ('r_offset', 'r_info', 'r_addend'))
X
X    def parse_rel(this):
X        return this.read_struct('NN', ('r_offset', 'r_info'))
X
X    def fixup_reloc(this, reloc):
X        if not 'r_addend' in reloc:
X            reloc['r_addend'] = 0
X        if this.elf_wordsize == 64:
X            reloc['r_sym'] = reloc['r_info'] >> 32
X            reloc['r_type'] = reloc['r_info'] & 0xffffffff
X        else:
X            reloc['r_sym'] = reloc['r_info'] >> 8
X            reloc['r_type'] = reloc['r_info'] & 0xff
X        return reloc
X
X    def parse_symbols(this, symsecname, strsecname):
X        try:
X            symsechdr = this.find_section(symsecname)
X            strsechdr = this.find_section(strsecname)
X        except KeyError:
X            return {}
X        strsec = this.read_section(strsechdr)
X        this.file.seek(symsechdr['sh_offset'])
X        syms = [dict(this.parse_symbol(),number=i) for i in
X                range(0, symsechdr['sh_size'] // symsechdr['sh_entsize'])]
X        return {this.get_name(sym['st_name'], strsec): sym for sym in syms}
X
X    def parse_relocs(this, section):
X        this.file.seek(section['sh_offset'])
X        if section['sh_type'] == 4:
X            relocs = [this.fixup_reloc(this.parse_rela()) for i in
X                      range(0, section['sh_size'] // section['sh_entsize'])]
X        else:
X            relocs = [this.fixup_reloc(this.parse_rel()) for i in
X                      range(0, section['sh_size'] // section['sh_entsize'])]
X        return relocs
X
X    def address_to_offset(this, addr):
X        for section in this.shdrs:
X            if (section['sh_addr'] <= addr and
X                section['sh_addr']+section['sh_size'] > addr):
X                return section['sh_offset']+(addr-section['sh_addr'])
X        raise IndexError('address out of range')
X
X    def load_symbol(this, sym):
X        this.file.seek(this.address_to_offset(sym['st_value']))
X        return this.file.read(sym['st_size'])
X
X    def __init__(this, filename):
X        this.file = open(filename, 'rb')
X        magic = this.file.read(16)
X
X        if magic[:4] != b'\x7fELF':
X            raise Exception("ELF signature not found")
X
X        if magic[4] == 1:
X            this.elf_wordsize = 32
X            nativeint = 'Ii'
X        elif magic[4] == 2:
X            this.elf_wordsize = 64
X            nativeint = 'Qq'
X        else:
X            raise Exception("Invalid ELF file class")
X
X        if magic[5] == 1:
X            this.elf_endianprefix = '<'
X        elif magic[5] == 2:
X            this.elf_endianprefix = '>'
X        else:
X            raise Exception("Invalid ELF data encoding")
X
X        this.elf_format = str.maketrans('HWwXxNn', 'HIiQq'+nativeint)
X
X        this.file.seek(0)
X        this.ehdr = this.read_ehdr()
X        this.file.seek(this.ehdr['e_shoff'])
X        this.shdrs = [this.read_shdr() for i in range(this.ehdr['e_shnum'])]
X
X        this.strtab = this.read_section(this.shdrs[this.ehdr['e_shstrndx']])
X
X        this.symtab = this.parse_symbols('.symtab', '.strtab')
X        this.dynsym = this.parse_symbols('.dynsym', '.dynstr')
X
X        this.relocs = {}
X        for section in this.shdrs:
X            if section['sh_type'] == 4 or section['sh_type'] == 9:
X                rels = {}
X                symsec = this.shdrs[section['sh_link']]
X                if this.get_name(symsec['sh_name']) == '.symtab':
X                    rels['symbols'] = this.symtab
X                elif this.get_name(symsec['sh_name']) == '.dynsym':
X                    rels['symbols'] = this.dynsym
X                rels['relocs'] = this.parse_relocs(section)
X                this.relocs[this.get_name(section['sh_name'])] = rels
X
X    def __del__(this):
X        try:
X            this.file.close()
X        except AttributeError:
X            pass
4d80edf57e0d349d85d48ea179dceac6
echo x - sigrok-firmware-utils/files/parsepe.py
sed 's/^X//' >sigrok-firmware-utils/files/parsepe.py << '3437c7374751c4486815a5c73fb7d553'
X#!/usr/bin/env python3
X##
X## This file is part of the sigrok-util project.
X##
X## Copyright (C) 2012 Bert Vermeulen <bert@biot.com>
X##
X## This program is free software; you can redistribute it and/or modify
X## it under the terms of the GNU General Public License as published by
X## the Free Software Foundation; either version 3 of the License, or
X## (at your option) any later version.
X##
X## This program is distributed in the hope that it will be useful,
X## but WITHOUT ANY WARRANTY; without even the implied warranty of
X## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
X## GNU General Public License for more details.
X##
X## You should have received a copy of the GNU General Public License
X## along with this program; if not, see <http://www.gnu.org/licenses/>.
X##
X
Ximport sys
Ximport os
Xfrom getopt import getopt
Ximport struct
X
X
Xdef parse(filename):
X	f = open(filename, 'rb')
X	if f.read(2) != b'MZ':
X		raise Exception("MZ signature not found.")
X
X	sections = []
X	# long e_lfanew
X	f.seek(0x3c)
X	pe_ptr = struct.unpack("<L", f.read(4))[0]
X	f.seek(pe_ptr)
X	if f.read(4) != b'\x50\x45\x00\x00':
X		raise Exception("PE signature not found.")
X	# skip Machine
X	f.seek(f.tell() + 2)
X	sections.append(['header', 324, 0])
X	num_sections = struct.unpack("<H", f.read(2))[0]
X	# skip TimeDateStamp
X	f.seek(f.tell() + 4)
X	symboltable_address = struct.unpack("<L", f.read(4))[0]
X	num_symbols = struct.unpack("<L", f.read(4))[0]
X	optheader_size = struct.unpack("<H", f.read(2))[0]
X	# skip past PE header and PE optional header
X	f.seek(f.tell() + 2 + optheader_size)
X
X	for i in range(num_sections):
X		name = f.read(8).decode('ascii', errors='ignore').strip('\x00')
X		# skip past Misc and VirtualAddress
X		f.seek(f.tell() + 8)
X		# SizeOfRawData
X		size = struct.unpack("<L", f.read(4))[0]
X		# PointerToRawData
X		ptr = struct.unpack("<L", f.read(4))[0]
X		# skip to next section header
X		f.seek(f.tell() + 16)
X		sections.append([name, size, ptr])
X
X	symbols = []
X	addr = symboltable_address
X	stringtable_address = symboltable_address + num_symbols * 18
X	for i in range(num_symbols):
X		f.seek(addr)
X		tmp = f.read(8)
X		symaddr = struct.unpack("<L", f.read(4))[0]
X		# skip SectionNumber and Type
X		symtype = struct.unpack("B", f.read(1))[0]
X		f.seek(f.tell() + 4)
X		if tmp[:4] == b'\x00\x00\x00\x00':
X			# symbol name is in the string table
X			straddr = stringtable_address + struct.unpack("<l", tmp[4:])[0]
X			f.seek(straddr)
X			tmpname = f.read(64)
X			name = tmpname[:tmpname.find(b'\x00')]
X		else:
X			name = tmp
X		name = name.decode('ascii', errors='ignore').strip('\x00')
X		# need IMAGE_SYM_CLASS_EXTERNAL
X		if symtype == 0x02:
X			size = 0
X		else:
X			size = None
X		if i != 0 and symbols[-1][2] is not None and symaddr > symbols[-1][1]:
X			symbols[-1][2] = symaddr - symbols[-1][1]
X		symbols.append([name, symaddr, size])
X		addr += 18
X
X	f.close()
X
X	return sections, symbols
X
X
Xdef list_all(filename):
X	sections, symbols = parse(filename)
X	if sections:
X		print("Sections:\n    Name         Size\t  Position")
X		cnt = 0
X		for name, size, address in sections:
X			print("%-3d %-8s    %5d\t  0x%.8x" % (cnt, name, size, address))
X			cnt += 1
X	if symbols:
X		print("\nSymbol table:\n   Address     Size    Symbol")
X		for symbol, address, size in symbols:
X			if size is not None:
X				sizestr = "%5d" % size
X			else:
X				sizestr = "     "
X			print("0x%.8x  %s    %-8s" % (address, sizestr, symbol))
X		print()
X
X
Xdef extract_symbol(filename, symbol):
X	sections, symbols = parse(filename)
X	if not symbols:
X		return None
X	data = None
X	for symbolname, address, size in symbols:
X		if symbolname == symbol:
X			if size is None:
X				raise Exception("symbol %s found, but has unknown size")
X			f = open(filename, 'rb')
X			f.seek(address)
X			data = f.read(size)
X			f.close()
X			if len(data) != size:
X				raise Exception("short file")
X			break
X
X	if data is None:
X		raise Exception("symbol %s not found" % symbol)
X
X	return data
X
X
X
Xdef usage():
X	print("usage: parsepe.py [-s <symbol>] <-l|-x> <filename>")
X	print("  -l   list all sections and symbols in file")
X	print("  -x   extract symbol from file (specify symbol name with -s)")
X	sys.exit()
X
X
X#
X# main
X#
X
Xif __name__ == '__main__':
X	filename = symbol = mode = None
X	opts, args = getopt(sys.argv[1:], 's:lx')
X	for opt, arg in opts:
X		if opt == '-s':
X			symbol = arg
X		elif opt == '-l':
X			mode = 'list'
X		elif opt == '-x':
X			mode = 'extract'
X
X	if len(args) != 1:
X		usage()
X	if mode is None and symbol is None:
X		usage()
X
X	try:
X		filename = args[0]
X		if mode == 'list':
X			list_all(filename)
X		elif mode == 'extract':
X			if symbol is None:
X				raise Exception("specify a symbol to extract")
X			data = extract_symbol(filename, symbol)
X			outfile = os.path.splitext(filename)[0] + symbol
X			open(outfile, 'wb').write(data)
X			print("saved %d bytes to %s" % (len(data), outfile))
X		else:
X			raise Exception("specify -l or -x")
X	except Exception as e:
X		print("Error: %s" % str(e))
X
X
3437c7374751c4486815a5c73fb7d553
echo x - sigrok-firmware-utils/files/sigrok-fwextract-hantek-dso
sed 's/^X//' >sigrok-firmware-utils/files/sigrok-fwextract-hantek-dso << '9e8cf85ef81a7022bc78a309937d30db'
X#!/usr/bin/env python3
X##
X## This file is part of the sigrok-util project.
X##
X## Copyright (C) 2012 Bert Vermeulen <bert@biot.com>
X##
X## This program is free software; you can redistribute it and/or modify
X## it under the terms of the GNU General Public License as published by
X## the Free Software Foundation; either version 3 of the License, or
X## (at your option) any later version.
X##
X## This program is distributed in the hope that it will be useful,
X## but WITHOUT ANY WARRANTY; without even the implied warranty of
X## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
X## GNU General Public License for more details.
X##
X## You should have received a copy of the GNU General Public License
X## along with this program; if not, see <http://www.gnu.org/licenses/>.
X##
X
Ximport sys
Ximport os
Ximport re
Ximport struct
Xfrom array import array
X
Ximport parsepe
X
X
Xdef find_model(filename):
X	filename = os.path.split(filename)[-1]
X	m = re.search('^dso([a-z0-9]+)1.sys$', filename, re.I)
X	if m:
X		model = m.group(1).upper()
X		model = model.replace('X86', '').replace('AMD64', '').replace('IA64', '')
X		if model == '520A':
X			model = '5200A'
X	else:
X		model = 'unknown'
X
X	return model
X
X
Xdef unsparse(data):
X	p = 0
X	maxaddr = 0
X	blob = array('B', [0] * 0x4000)
X	while p <= len(data) and data[p+4] == 0:
X		num_bytes = struct.unpack("<H", data[p:p+2])[0]
X		address = struct.unpack("<H", data[p+2:p+4])[0]
X		chunk = array('B')
X		chunk.frombytes(data[p+5:p+5+num_bytes])
X		p += 22
X
X		if address > 0x4000:
X			# the FX2 only has 16K RAM. other writes are to registers
X			# in the 0xe000 region, skip those
X			continue
X
X		blob[address:address+num_bytes] = chunk
X
X		if address + num_bytes > maxaddr:
X			maxaddr = address + num_bytes
X
X	return blob[:maxaddr].tostring()
X
X
Xdef usage():
X	print("sigrok-fwextract-hantek-dso <driverfile>")
X	sys.exit()
X
X
X#
X# main
X#
X
Xif len(sys.argv) != 2:
X	usage()
X
Xtry:
X	filename = sys.argv[1]
X	binihx = parsepe.extract_symbol(filename, '_firmware')
X	if binihx is None:
X		raise Exception("no firmware found")
X	blob = unsparse(binihx)
X	outfile = 'hantek-dso-' + find_model(filename) + '.fw'
X	open(outfile, 'wb').write(blob)
X	print("saved %d bytes to %s" % (len(blob), outfile))
Xexcept Exception as e:
X	print("Error: %s" % str(e))
9e8cf85ef81a7022bc78a309937d30db
echo x - sigrok-firmware-utils/files/sigrok-fwextract-hantek-dso.1
sed 's/^X//' >sigrok-firmware-utils/files/sigrok-fwextract-hantek-dso.1 << '15f3ef99cbaaea18b52bb09edd5060f6'
X.TH SIGROK\-FWEXTRACT\-HANTEK\-DSO 1 "Aug 08, 2013"
X.SH "NAME"
Xsigrok\-fwextract\-hantek\-dso \- Extract Hantek DSO-2xxx/52xx firmware
X.SH "SYNOPSIS"
X.B sigrok\-fwextract\-hantek\-dso [FILE]
X.SH "DESCRIPTION"
XThis tool extracts firmware from the driver that comes with the
XHantek DSO-2xxx/52xx series USB oscilloscopes. Find the 32-bit
Xdriver installed on the Windows system -- typically called
X.B DSOxxxx1.sys
Xor
X.BR DsoxxxxX861.sys ,
Xwhere xxxx is your device's model.
X.PP
XUse it like this:
X.PP
X.B "  $ sigrok-fwextract-hantek-dso Dso2090X861.sys"
X.br
X.RB "  saved 4730 bytes to hantek-dso-2090.fw"
X.PP
XCopy the resulting file over to the location where libsigrok expects
Xto find its firmware files. By default this is
X.BR /usr/local/share/sigrok-firmware .
X.SH OPTIONS
XNone.
X.SH "EXIT STATUS"
XExits with 0 on success, 1 on most failures.
X.SH "SEE ALSO"
X\fBsigrok\-fwextract\-saleae\-logic16\fP(1)
X.SH "BUGS"
XPlease report any bugs via Bugzilla
X.RB "(" http://sigrok.org/bugzilla ")"
Xor on the sigrok\-devel mailing list
X.RB "(" sigrok\-devel@lists.souceforge.net ")."
X.SH "LICENSE"
XThis program is covered by the GNU General Public License (GPL),
Xversion 3 or later.
X.SH "AUTHORS"
XPlease see the individual source code files.
15f3ef99cbaaea18b52bb09edd5060f6
echo x - sigrok-firmware-utils/files/sigrok-fwextract-saleae-logic16
sed 's/^X//' >sigrok-firmware-utils/files/sigrok-fwextract-saleae-logic16 << 'e5995e6f703124fdb2c681eb7495d542'
X#!/usr/bin/env python3
X##
X## This file is part of the sigrok-util project.
X##
X## Copyright (C) 2013 Marcus Comstedt <marcus@mc.pp.se>
X##
X## This program is free software; you can redistribute it and/or modify
X## it under the terms of the GNU General Public License as published by
X## the Free Software Foundation; either version 3 of the License, or
X## (at your option) any later version.
X##
X## This program is distributed in the hope that it will be useful,
X## but WITHOUT ANY WARRANTY; without even the implied warranty of
X## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
X## GNU General Public License for more details.
X##
X## You should have received a copy of the GNU General Public License
X## along with this program; if not, see <http://www.gnu.org/licenses/>.
X##
X
Ximport sys
Ximport struct
Ximport parseelf
X
Xclass searcher:
X
X    def reset(this, offs=0):
X        if offs < 0 or offs > this.length:
X            raise Exception('Reset past end of section')
X        this.address = this.baseaddr + offs
X        this.offset = offs
X
X    def skip(this, cnt):
X        if this.offset + cnt > this.length:
X            raise Exception('Skip past end of section')
X        this.address += cnt
X        this.offset += cnt
X
X    def peek(this, cnt, offs=0):
X        if this.offset + offs + cnt > this.length:
X            raise Exception('Peek past end of section')
X        return this.data[this.offset + offs : this.offset + offs + cnt]
X
X    def look_for(this, needle):
X        pos = this.data.find(needle, this.offset)
X        if pos < 0:
X            raise Exception('Needle not found in haystack')
X        this.skip(pos - this.offset)
X
X    def look_for_either(this, needle1, needle2):
X        pos1 = this.data.find(needle1, this.offset)
X        pos2 = this.data.find(needle2, this.offset)
X        if pos1 < 0 and pos2 < 0:
X            raise Exception('Needle not found in haystack')
X        if pos1 < 0 or pos2 < pos1:
X            pos1 = pos2
X        this.skip(pos1 - this.offset)
X
X    def __init__(this, data, addr):
X        this.data = data
X        this.baseaddr = addr
X        this.length = len(data)
X        this.reset()
X
Xdef search_plt_32(plt, addr):
X    plt.reset()
X    plt.look_for(struct.pack('<BBI', 0xff, 0x25, addr))	# jmp *addr32
X    return plt.address
X
Xdef search_plt_64(plt, addr):
X    plt.reset()
X    while True:
X        plt.look_for(b'\xff\x25')		# jmpq *offs32(%rip)
X        offs = struct.unpack('<i', plt.peek(4, 2))[0]
X        if plt.address + offs + 6 == addr:
X            return plt.address
X        plt.skip(2)
X
Xdef find_hex_file_lines_constructor_32(text, hex_file_lines_got, got_plt):
X    while True:
X        text.look_for_either(b'\x8b\xbb', b'\x8b\xb3')	# mov offs32(%ebx),{%edi,%esi}
X        offs = struct.unpack('<i', text.peek(4, 2))[0]
X        if got_plt + offs == hex_file_lines_got:
X            text.skip(6)
X            return
X        text.skip(2)
X
Xdef find_hex_file_lines_constructor_64(text, hex_file_lines_got):
X    while True:
X        text.look_for(b'\x48\x8b\x2d')		# mov offs32(%rip),%rbp
X        offs = struct.unpack('<i', text.peek(4, 3))[0]
X        if text.address + offs + 7 == hex_file_lines_got:
X            text.skip(7)
X            return
X        text.skip(3)
X
Xdef parse_hex_file_lines_constructor_32(text, basic_string_plt, got_plt, lines):
X    text.skip(-5)
X    esi = (text.peek(1) == b'\xb3')
X    text.skip(5)
X    cnt = len(lines)
X    while cnt > 0:
X        if text.peek(2) == b'\x8d\x45':		# lea offs8(%ebp),%eax
X            text.skip(3)
X        elif text.peek(2) == b'\x8d\x85':	# lea offs32(%ebp),%eax
X            text.skip(6)
X        if text.peek(1) == (b'\xbf' if esi else b'\xbe'):	# mov $imm32,%esi
X            text.skip(5)
X        elif text.peek(2) == (b'\x31\xff' if esi else b'\x31\xf6'):	# xor %esi,%esi
X            text.skip(2)
X        if text.peek(4) == b'\x89\x44\x24\x08':	# mov %eax,0x8(%esp)
X            text.skip(4)
X        if text.peek(2) == b'\x8d\x83':		# lea offs32(%ebx),%eax
X            straddr = struct.unpack('<i', text.peek(4, 2))[0]
X            text.skip(6)
X            straddr += got_plt
X        else:
X            raise Exception('Expected lea offs32(%ebx),%eax @ ' +
X                            ('0x%x' % text.address))
X        if text.peek(4) == b'\x89\x44\x24\x04':	# mov %eax,0x4(%esp)
X            text.skip(4)
X        if text.peek(3) == (b'\x89\x34\x24' if esi else b'\x89\x3c\x24'):	# mov %edi,(%esp)
X            offs = 0
X            text.skip(3)
X        elif text.peek(2) == (b'\x8d\x46' if esi else b'\x8d\x47'):	# lea offs8(%edi),%eax
X            offs = struct.unpack('<b', text.peek(1, 2))[0]
X            text.skip(3)
X        elif text.peek(2) == (b'\x8d\x86' if esi else b'\x8d\x87'):	# lea offs32(%edi),%eax
X            offs = struct.unpack('<i', text.peek(4, 2))[0]
X            text.skip(6)
X        else:
X            raise Exception('Expected lea offs(%e'+('s' if esi else 'd')+'i),%eax @ ' +
X                            ('0x%x' % text.address))
X        if offs < 0 or offs > (len(lines) << 2) or (offs & 3) != 0:
X            raise Exception('Invalid offset %d' % offs)
X        index = offs >> 2
X        if lines[index] != 0:
X            raise Exception('Line %d filled multiple times' % index)
X        if text.peek(3) == b'\x89\x04\x24':	# mov %eax,(%esp)
X            text.skip(3)
X        if text.peek(1) == b'\xe8':		# call offs32
X            offs = struct.unpack('<i', text.peek(4, 1))[0]
X            text.skip(5)
X            if text.address + offs != basic_string_plt:
X                raise Exception('Expected call ZNSsC1EPKcRKSaIcE@plt @ ' +
X                                ('0x%x' % text.address))
X        else:
X            raise Exception('Expected call ZNSsC1EPKcRKSaIcE@plt @ ' +
X                            ('0x%x' % text.address))
X        if straddr == 0:
X            raise Exception('NULL pointer stored to index %d' % index)
X        lines[index] = straddr
X        cnt -= 1
X
Xdef parse_hex_file_lines_constructor_64(text, basic_string_plt, lines):
X    cnt = len(lines)
X    while cnt > 0:
X        if text.peek(1) == b'\xbb':		# mov $imm32,%ebx
X            text.skip(5)
X        elif text.peek(2) == b'\x31\xdb':	# xor %ebx,%ebx
X            text.skip(2)
X        if text.peek(4) == b'\x48\x8d\x54\x24':	# lea offs8(%rsp),%rdx
X            text.skip(5)
X        elif text.peek(4) == b'\x48\x8d\x94\x24': # lea offs32(%rsp),%rdx
X            text.skip(8)
X        if text.peek(3) == b'\x48\x8d\x35':	# lea offs32(%rip),%rsi
X            straddr = struct.unpack('<i', text.peek(4, 3))[0]
X            text.skip(7)
X            straddr += text.address
X        else:
X            raise Exception('Expected lea offs(%rip),%rsi @ ' +
X                            ('0x%x' % text.address))
X        if text.peek(3) == b'\x48\x89\xef':	# mov %rbp,%rdi
X            offs = 0
X            text.skip(3)
X        elif text.peek(3) == b'\x48\x8d\x7d':	# lea offs8(%rbp),%rdi
X            offs = struct.unpack('<b', text.peek(1, 3))[0]
X            text.skip(4)
X        elif text.peek(3) == b'\x48\x8d\xbd':	# lea offs32(%rbp),%rdi
X            offs = struct.unpack('<i', text.peek(4, 3))[0]
X            text.skip(7)
X        else:
X            raise Exception('Expected lea offs(%rbp),%rdi @ ' +
X                            ('0x%x' % text.address))
X        if text.peek(1) == b'\xbb':		# mov $imm32,%ebx
X            text.skip(5)
X        elif text.peek(2) == b'\x31\xdb':	# xor %ebx,%ebx
X            text.skip(2)
X        if offs < 0 or offs > (len(lines) << 3) or (offs & 7) != 0:
X            raise Exception('Invalid offset %d' % offs)
X        index = offs >> 3
X        if lines[index] != 0:
X            raise Exception('Line %d filled multiple times' % index)
X        if text.peek(1) == b'\xe8':		# callq offs32
X            offs = struct.unpack('<i', text.peek(4, 1))[0]
X            text.skip(5)
X            if text.address + offs != basic_string_plt:
X                raise Exception('Expected callq ZNSsC1EPKcRKSaIcE@plt @ ' +
X                                ('0x%x' % text.address))
X        else:
X            raise Exception('Expected callq ZNSsC1EPKcRKSaIcE@plt @ ' +
X                            ('0x%x' % text.address))
X        if straddr == 0:
X            raise Exception('NULL pointer stored to index %d' % index)
X        lines[index] = straddr
X        cnt -= 1
X
Xdef find_reloc(elf, symname):
X    for section, relocs in elf.relocs.items():
X        if 'symbols' in relocs and symname in relocs['symbols']:
X            symnum = relocs['symbols'][symname]['number']
X            for reloc in relocs['relocs']:
X                if reloc['r_sym'] == symnum:
X                    return reloc
X    raise Exception('Unable to find a relocation against ' + symname)
X
Xdef ihex_to_binary(lines):
X    chunks = {}
X    for line in lines:
X        if line[0] != ':':
X            raise Exception('ihex line does not start with ":"')
X        line = bytes.fromhex(line[1:])
X        if (sum(bytearray(line)) & 0xff) != 0:
X            raise Exception('Invalid checksum in ihex')
X        (byte_count, address, rectype) = struct.unpack('>BHB', line[:4])
X        (data, checksum) = struct.unpack('>%dsB' % (byte_count), line[4:])
X        if rectype == 1 and byte_count == 0:
X            pass
X        elif rectype != 0 or byte_count == 0:
X            raise Exception('Unexpected rectype %d with bytecount %d' %
X                            (rectype, byte_count))
X        elif address in chunks:
X            raise Exception('Multiple ihex lines with address 0x%x' % address)
X        else:
X            chunks[address] = data
X    blob = b''
X    for address in sorted(iter(chunks)):
X        if address < len(blob):
X            raise Exception('Overlapping ihex chunks')
X        elif address > len(blob):
X            blob += b'\x00' * (address - len(blob))
X        blob += chunks[address]
X    return blob
X
Xdef extract_fx2_firmware(elf, symname, filename):
X    blob = elf.load_symbol(elf.dynsym[symname + 'Count'])
X    count = struct.unpack('<I', blob)[0]
X    got_plt = elf.find_section('.got.plt')['sh_addr']
X    hex_file_lines_got = find_reloc(elf, symname)['r_offset']
X    basic_string_got = find_reloc(elf, '_ZNSsC1EPKcRKSaIcE')['r_offset']
X    pltsec = elf.find_section('.plt')
X    plt = searcher(elf.read_section(pltsec), pltsec['sh_addr'])
X    try:
X        if elf.elf_wordsize == 64:
X            basic_string_plt = search_plt_64(plt, basic_string_got)
X        else:
X            basic_string_plt = search_plt_32(plt, basic_string_got)
X    except:
X        raise Exception('Unable to find a PLT entry for _ZNSsC1EPKcRKSaIcE')
X    textsec = elf.find_section('.text')
X    text = searcher(elf.read_section(textsec), textsec['sh_addr'])
X    while True:
X        try:
X            if elf.elf_wordsize == 64:
X                find_hex_file_lines_constructor_64(text, hex_file_lines_got)
X            else:
X                find_hex_file_lines_constructor_32(text, hex_file_lines_got,
X                                                   got_plt)
X        except:
X            raise Exception('Unable to find constructor for ' + symname)
X        oldoffs = text.offset
X        l = [0]*count
X        try:
X            if elf.elf_wordsize == 64:
X                parse_hex_file_lines_constructor_64(text, basic_string_plt, l)
X            else:
X                parse_hex_file_lines_constructor_32(text, basic_string_plt,
X                                                    got_plt, l)
X            break
X        except KeyError:
X            text.reset(oldoffs)
X    rodatasec = elf.find_section('.rodata')
X    rodata = elf.read_section(rodatasec)
X    lo = rodatasec['sh_addr']
X    hi = lo + rodatasec['sh_size']
X    for i in range(count):
X        addr = l[i]
X        if addr < lo or addr >= hi:
X            raise Exception('Address 0x%x outside of .rodata section' % addr)
X        l[i] = elf.get_name(addr - lo, rodata)
X    blob = ihex_to_binary(l)
X    f = open(filename, 'wb')
X    f.write(blob)
X    f.close()
X    print("saved %d bytes to %s" % (len(blob), filename))
X
Xdef extract_symbol(elf, symname, filename):
X    blob = elf.load_symbol(elf.dynsym[symname])
X    f = open(filename, 'wb')
X    f.write(blob)
X    f.close()
X    print("saved %d bytes to %s" % (len(blob), filename))
X
Xdef extract_bitstream(elf, lv):
X    extract_symbol(elf, 'gLogic16Lv' + lv + 'CompressedBitstream',
X                   'saleae-logic16-fpga-' + lv + '.bitstream')
X
Xdef usage():
X    print("sigrok-fwextract-saleae-logic16 <programfile>")
X    sys.exit()
X
X
X#
X# main
X#
X
Xif len(sys.argv) != 2:
X    usage()
X
Xtry:
X    filename = sys.argv[1]
X    elf = parseelf.elf(filename)
X    if elf.ehdr['e_machine'] != 3 and elf.ehdr['e_machine'] != 62:
X        raise Exception('Unsupported e_machine')
X    extract_fx2_firmware(elf, 'gLogic16HexFileLines', 'saleae-logic16-fx2.fw')
X    extract_bitstream(elf, '18')
X    extract_bitstream(elf, '33')
Xexcept Exception as e:
X    print("Error: %s" % str(e))
e5995e6f703124fdb2c681eb7495d542
echo x - sigrok-firmware-utils/files/sigrok-fwextract-saleae-logic16.1
sed 's/^X//' >sigrok-firmware-utils/files/sigrok-fwextract-saleae-logic16.1 << '5e8d93a1c27459f149b58d0f2797266d'
X.TH SIGROK\-FWEXTRACT\-SALEAE\-LOGIC16 1 "Aug 08, 2013"
X.SH "NAME"
Xsigrok\-fwextract\-saleae\-logic16 \- Extract Saleae Logic16 firmware
X.SH "SYNOPSIS"
X.B sigrok\-fwextract\-saleae\-logic16 [FILE]
X.SH "DESCRIPTION"
XThis tool extracts FX2 firmware and FPGA bitstreams from the vendor
Xsoftware for the Saleae Logic16 USB logic analyzer. Download the Linux
Xversion (either 32-bit or 64-bit will do), and unpack it to find the
Xmain binary called "Logic".
X.PP
XIn order to extract the firmware/bitstreams, run the following command:
X.PP
X.B "  $ sigrok-fwextract-saleae-logic16 Logic"
X.br
X.RB "  saved 5214 bytes to saleae-logic16-fx2.fw"
X.br
X.RB "  saved 149516 bytes to saleae-logic16-fpga-18.bitstream"
X.br
X.RB "  saved 149516 bytes to saleae-logic16-fpga-33.bitstream"
X.PP
XCopy the resulting files over to the location where libsigrok expects
Xto find its firmware files. By default this is
X.BR /usr/local/share/sigrok-firmware .
X.SH OPTIONS
XNone.
X.SH "EXIT STATUS"
XExits with 0 on success, 1 on most failures.
X.SH "SEE ALSO"
X\fBsigrok\-fwextract\-hantek\-dso\fP(1)
X.SH "BUGS"
XPlease report any bugs via Bugzilla
X.RB "(" http://sigrok.org/bugzilla ")"
Xor on the sigrok\-devel mailing list
X.RB "(" sigrok\-devel@lists.souceforge.net ")."
X.SH "LICENSE"
XThis program is covered by the GNU General Public License (GPL),
Xversion 3 or later.
X.SH "AUTHORS"
XPlease see the individual source code files.
5e8d93a1c27459f149b58d0f2797266d
echo x - sigrok-firmware-utils/files/sigrok-fwextract-sysclk-lwla
sed 's/^X//' >sigrok-firmware-utils/files/sigrok-fwextract-sysclk-lwla << '8d651df632ba51faf4f343abde174f15'
X#! /bin/sh -e
X##
X## This file is part of the sigrok-util project.
X##
X## Copyright (C) 2014 Daniel Elstner <daniel.kitta@gmail.com>
X##
X## This program is free software; you can redistribute it and/or modify
X## it under the terms of the GNU General Public License as published by
X## the Free Software Foundation; either version 3 of the License, or
X## (at your option) any later version.
X##
X## This program is distributed in the hope that it will be useful,
X## but WITHOUT ANY WARRANTY; without even the implied warranty of
X## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
X## GNU General Public License for more details.
X##
X## You should have received a copy of the GNU General Public License
X## along with this program; if not, see <http://www.gnu.org/licenses/>.
X##
X
Xinfile=$1
Xif [ -z "$infile" ]; then
X    echo "Usage: $0 SETUP-EXE" >&2
X    exit 1
Xfi
X
X# Verify the checksum to make sure this is the right binary file
Xexpectsum=f2a9333329200ad1d939d051257f914200cf0c765ff4962b2907dcf30716f455
Xset '' $(sha256sum -b "$infile")
X
Xif [ "$2" != "$expectsum" ]; then
X    echo "$0: checksum mismatch for '$infile'" >&2
X    echo "$0: make sure you picked the right file (lwla1034_EN_setup.exe on the CD-ROM)" >&2
X    exit 1
Xfi
X
X# Extract the firmware binaries from the executable
Xdd bs=1 skip=34110338 count=78398 if="$infile" of=sysclk-lwla1034-int.bitstream
Xdd bs=1 skip=34266237 count=78247 if="$infile" of=sysclk-lwla1034-extpos.bitstream
Xdd bs=1 skip=34344484 count=79145 if="$infile" of=sysclk-lwla1034-extneg.bitstream
Xdd bs=1 skip=34578631 count=48525 if="$infile" of=sysclk-lwla1034-off.bitstream
8d651df632ba51faf4f343abde174f15
echo x - sigrok-firmware-utils/files/sigrok-fwextract-sysclk-lwla.1
sed 's/^X//' >sigrok-firmware-utils/files/sigrok-fwextract-sysclk-lwla.1 << 'e68eba8e6fe272b583a51a3696d35072'
X.TH SIGROK\-FWEXTRACT\-SYSCLK\-LWLA 1 "Jan 04, 2014"
X.SH "NAME"
Xsigrok\-fwextract\-sysclk\-lwla \- Extract SysClk LWLA* firmware
X.SH "SYNOPSIS"
X.B sigrok\-fwextract\-sysclk\-lwla SETUP-EXE
X.SH "DESCRIPTION"
XThis tool extracts FPGA bitstreams from the vendor software for the SysClk
XLWLA1034 USB logic analyzer. Insert the CD-ROM that ships with the device,
Xand locate the Windows installer executable "lwla1034_EN_setup.exe".
X.PP
XIn order to extract the bitstreams, run the following command:
X.PP
X.B "  $ sigrok-fwextract-sysclk-lwla lwla1034_EN_setup.exe"
X.PP
XCopy the resulting four bitstream files over to the location where libsigrok
Xexpects to find its firmware files. By default this is
X.BR /usr/local/share/sigrok-firmware .
X.SH OPTIONS
XNone.
X.SH "EXIT STATUS"
XExits with 0 on success, 1 on most failures.
X.SH "SEE ALSO"
X\fBsigrok\-fwextract\-hantek\-dso\fP(1)
X.SH "BUGS"
XPlease report any bugs via Bugzilla
X.RB "(" http://sigrok.org/bugzilla ")"
Xor on the sigrok\-devel mailing list
X.RB "(" sigrok\-devel@lists.souceforge.net ")."
X.SH "LICENSE"
XThis program is covered by the GNU General Public License (GPL),
Xversion 3 or later.
X.SH "AUTHORS"
XPlease see the individual source code files.
e68eba8e6fe272b583a51a3696d35072
exit



>Release-Note:
>Audit-Trail:
>Unformatted:



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201404192209.s3JM917a095448>