From 1b0c6828974e6c18763600aeb09034d5686f21f1 Mon Sep 17 00:00:00 2001 From: Marcus Comstedt Date: Thu, 8 Aug 2013 00:33:27 +0200 Subject: [PATCH] sigrok-fwextract-saleae-logic16: Add extraction of FX2 firmware --- firmware/saleae-logic16/parseelf.py | 41 ++- .../sigrok-fwextract-saleae-logic16 | 260 ++++++++++++++++++ .../sigrok-fwextract-saleae-logic16.1 | 9 +- 3 files changed, 306 insertions(+), 4 deletions(-) diff --git a/firmware/saleae-logic16/parseelf.py b/firmware/saleae-logic16/parseelf.py index 1c6769e..3bbbf84 100644 --- a/firmware/saleae-logic16/parseelf.py +++ b/firmware/saleae-logic16/parseelf.py @@ -68,6 +68,23 @@ class elf: ('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) @@ -76,10 +93,20 @@ class elf: return {} strsec = this.read_section(strsechdr) this.file.seek(symsechdr['sh_offset']) - syms = [this.parse_symbol() for i in + 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 @@ -126,6 +153,18 @@ class elf: 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() diff --git a/firmware/saleae-logic16/sigrok-fwextract-saleae-logic16 b/firmware/saleae-logic16/sigrok-fwextract-saleae-logic16 index 09b8730..45f9827 100755 --- a/firmware/saleae-logic16/sigrok-fwextract-saleae-logic16 +++ b/firmware/saleae-logic16/sigrok-fwextract-saleae-logic16 @@ -19,8 +19,265 @@ ## 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 __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(' 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'\xbe': # mov $imm32,%esi + text.skip(5) + elif text.peek(2) == 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(' (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(' 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(' (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('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('= 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') @@ -47,6 +304,9 @@ if len(sys.argv) != 2: 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: diff --git a/firmware/saleae-logic16/sigrok-fwextract-saleae-logic16.1 b/firmware/saleae-logic16/sigrok-fwextract-saleae-logic16.1 index 54ea43a..3766602 100644 --- a/firmware/saleae-logic16/sigrok-fwextract-saleae-logic16.1 +++ b/firmware/saleae-logic16/sigrok-fwextract-saleae-logic16.1 @@ -4,14 +4,17 @@ sigrok\-fwextract\-saleae\-logic16 \- Extract Saleae Logic16 firmware .SH "SYNOPSIS" .B sigrok\-fwextract\-saleae\-logic16 [FILE] .SH "DESCRIPTION" -This tool extracts 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". +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" -- 2.30.2