diff options
author | John Marino <marino@FreeBSD.org> | 2014-08-03 22:09:07 +0000 |
---|---|---|
committer | John Marino <marino@FreeBSD.org> | 2014-08-03 22:09:07 +0000 |
commit | 7b05344a71552fbc8b445d519493486610a4dc1a (patch) | |
tree | 989d2ad7d55bfdb2d748a5349d44d0b49ac777d3 /science/sigrok-firmware-utils | |
parent | f18ad55f8de6239f78357b14dd076d24397885da (diff) |
Notes
Diffstat (limited to 'science/sigrok-firmware-utils')
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 |