mirror of
https://git.FreeBSD.org/ports.git
synced 2024-11-21 00:25:50 +00:00
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:
parent
f18ad55f8d
commit
7b05344a71
Notes:
svn2git
2021-03-31 03:12:20 +00:00
svn path=/head/; revision=363948
@ -173,6 +173,7 @@
|
||||
SUBDIR += rubygem-ai4r
|
||||
SUBDIR += rubygem-netcdf
|
||||
SUBDIR += sigrok-cli
|
||||
SUBDIR += sigrok-firmware-utils
|
||||
SUBDIR += silo
|
||||
SUBDIR += simlib
|
||||
SUBDIR += simsmith
|
||||
|
43
science/sigrok-firmware-utils/Makefile
Normal file
43
science/sigrok-firmware-utils/Makefile
Normal 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>
|
11
science/sigrok-firmware-utils/files/README.parsepe
Normal file
11
science/sigrok-firmware-utils/files/README.parsepe
Normal 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
|
||||
|
172
science/sigrok-firmware-utils/files/parseelf.py
Normal file
172
science/sigrok-firmware-utils/files/parseelf.py
Normal 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
|
183
science/sigrok-firmware-utils/files/parsepe.py
Normal file
183
science/sigrok-firmware-utils/files/parsepe.py
Normal 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))
|
||||
|
||||
|
@ -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))
|
@ -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.
|
@ -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))
|
@ -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.
|
@ -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
|
@ -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.
|
8
science/sigrok-firmware-utils/pkg-descr
Normal file
8
science/sigrok-firmware-utils/pkg-descr
Normal 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
|
Loading…
Reference in New Issue
Block a user