Add new port: science/sigrok-firmware-utils

PR:		188808
Submitted by:	uffer

      ===============================================================

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.
This commit is contained in:
John Marino 2014-08-03 22:09:07 +00:00
parent f18ad55f8d
commit 7b05344a71
Notes: svn2git 2021-03-31 03:12:20 +00:00
svn path=/head/; revision=363948
12 changed files with 990 additions and 0 deletions

View file

@ -173,6 +173,7 @@
SUBDIR += rubygem-ai4r
SUBDIR += rubygem-netcdf
SUBDIR += sigrok-cli
SUBDIR += sigrok-firmware-utils
SUBDIR += silo
SUBDIR += simlib
SUBDIR += simsmith

View file

@ -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>

View file

@ -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

View file

@ -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

View file

@ -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))

View file

@ -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))

View file

@ -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.

View file

@ -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))

View file

@ -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.

View file

@ -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

View file

@ -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.

View file

@ -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