def set_br_a(self, addr, cb, quiet=False, sym=None): """ Sets a breakpoint at address, and install callback cb for it. `addr` is a hexadecimal string as defined by RSP protocol. Also, because of this RSP implementation `addr` format should be the same as defined by `reg_fmt`. Tips: - Use `reg_fmt` attribute to get `addr` string from an integer. - Normally, an unparsed register value has the same format and can be used as is. """ if addr in self.br: print("warn: overwriting breakpoint at %s" % (sym or "0x" + addr)) br = self.br[addr] br.update(sym=sym, cb=cb) else: self.br[addr] = br = {'sym': sym, 'addr': addr, 'cb': cb} if self.z_breaks: tmp = self.fetch(b'Z0,%s,2' % addr) if tmp == b"": # Z/z packages are not supported, use code patching self.z_breaks = False br['old'] = unhex(self.fetch(b'm%s,2' % addr)) tmp = self.fetch(b'X%s,2:\xbe\xbe' % addr) else: br['old'] = unhex(self.fetch(b'm%s,2' % addr)) tmp = self.fetch(b'X%s,2:\xbe\xbe' % addr) if self.verbose and not quiet: print("set break: @%s (0x%s) %s" % (sym or "[unknown]", s(addr), s(tmp)))
def set_br_a(self, addr, cb, quiet=False, sym=None): """ Sets a breakpoint at address, and install callback cb for it. `addr` is a hexadecimal string as defined by RSP protocol. Also, because of this RSP implementation `addr` format should be the same as defined by `reg_fmt`. Tips: - Use `reg_fmt` attribute to get `addr` string from an integer. - Normally, an unparsed register value has the same format and can be used as is. """ if addr in self.br: print("warn: overwriting breakpoint at %s" % (sym or "0x" + addr)) br = self.br[addr] br.update(sym = sym, cb = cb) else: self.br[addr]= br = {'sym': sym, 'addr': addr, 'cb': cb} if self.z_breaks: tmp = self.fetch(b'Z0,%s,2' % addr) if tmp == b"": # Z/z packages are not supported, use code patching self.z_breaks = False br['old'] = unhex(self.fetch(b'm%s,2' % addr)) tmp = self.fetch(b'X%s,2:\xbe\xbe' % addr) else: br['old'] = unhex(self.fetch(b'm%s,2' % addr)) tmp = self.fetch(b'X%s,2:\xbe\xbe' % addr) if self.verbose and not quiet: print("set break: @%s (0x%s) %s" % (sym or "[unknown]", s(addr), s(tmp)))
def run(self, start=None, setpc=True): """ sets pc to start if given or to entry address from elf header, passes control to the device and handles breakpoints """ if setpc: if not start: entry_addr = self.elf.entry else: entry_addr = self.elf.symbols[start] if isinstance(self, CortexM3): entry_addr &= ~1 entry = self.reg_fmt % entry_addr if self.verbose: print("set new pc: @test (0x%s)" % s(entry)) self.set_reg(self.pc_reg, entry) if self.verbose: print('OK') if self.verbose: print("continuing") self.exit = False kind, sig, data = stop_reply(self.cont_all()) while kind in (b'T', b'S') and sig == 5: # Update current thread for a breakpoint handler. event = stop_event(data) self.thread = event[b"thread"] self.handle_br() if self.exit: return # Some threads can be created during the breakpoint handling. # `cont_all` resumes them.. kind, sig, data = stop_reply(self.cont_all()) if kind == b'W': # The process exited, getting values is impossible return if (kind, sig) != (b'T', 0x0b): print('strange signal %s' % sig) if hasattr(self, 'checkfault'): self.checkfault() else: src_line = self.get_src_line(int(self.regs[self.pc_reg], 16 - 1)) if src_line: print("0 %s:%s %s" % (src_line['file'], src_line['lineno'], src_line['line'])) else: print("%s %s" % (self.pc_reg, self.regs[self.pc_reg])) if isinstance(self, CortexM3): src_line = self.get_src_line(int(self.regs['lr'], 16) - 3) if src_line: print("1 %s:%s %s" % (src_line['file'], src_line['lineno'], src_line['line'])) else: print('lr %s' % s(self.regs['lr'])) self.dump_regs() self.read_ack(20) self.port.close(self) sys.exit(0)
def run(self, start=None, setpc=True): """ sets pc to start if given or to entry address from elf header, passes control to the device and handles breakpoints """ if setpc: if not start: entry_addr = self.elf.entry else: entry_addr = self.elf.symbols[start] if isinstance(self, CortexM3): entry_addr &= ~1 entry = self.reg_fmt % entry_addr if self.verbose: print("set new pc: @test (0x%s)" % s(entry)) self.set_reg(self.pc_reg, entry) if self.verbose: print('OK') if self.verbose: print("continuing") self.exit = False kind, sig, data = stop_reply(self.cont_all()) while kind in (b'T', b'S') and sig == 5: # Update current thread for a breakpoint handler. event = stop_event(data) self.thread = event[b"thread"] self.handle_br() if self.exit: return # Some threads can be created during the breakpoint handling. # `cont_all` resumes them.. kind, sig, data = stop_reply(self.cont_all()) if kind == b'W': # The process exited, getting values is impossible return if (kind, sig) != (b'T', 0x0b): print('strange signal %s' % sig) if hasattr(self, 'checkfault'): self.checkfault() else: src_line = self.get_src_line(int(self.regs[self.pc_reg],16 - 1)) if src_line: print("0 %s:%s %s" % (src_line['file'], src_line['lineno'], src_line['line'])) else: print("%s %s" % (self.pc_reg, self.regs[self.pc_reg])) if isinstance(self, CortexM3): src_line = self.get_src_line(int(self.regs['lr'],16) -3) if src_line: print("1 %s:%s %s" % (src_line['file'], src_line['lineno'], src_line['line'])) else: print('lr %s' % s(self.regs['lr'])) self.dump_regs() self.read_ack(20) self.port.close(self) sys.exit(0)
def __init__(self, port, elffile=None, verbose=False, noack=False, noshell=False): """ read the elf file if given by elffile, connects to the debugging device specified by port, and initializes itself. """ # Initially packet acknowledgment is enabled. # https://sourceware.org/gdb/onlinedocs/gdb/Packet-Acknowledgment.html self.ack = True self.registers = self.arch['regs'] self.reg_fmt = b"%%0%ux" % (self.arch['bitsize'] >> 2) self.__dict__['br'] = {} self.__dict__['verbose'] = verbose # open serial connection self.__dict__['port'] = Debugger(port) #serial.Serial(port, 115200, timeout=1) # parse elf for symbol table, entry point and work area self.__dict__['elf'] = ELF(elffile) if elffile else None if verbose and self.elf: print("work area: 0x%x" % self.elf.workarea) print("entry: 0x%x" % self.elf.entry) # check for signal from running target tmp = self.readpkt(timeout=1) if tmp and verbose: print('helo %s' % s(tmp)) #self.port.write(pack('qSupported:multiprocess+;qRelocInsn+')) self._get_feats() # By default, use Z/z packets to manipulate breakpoints self.z_breaks = True self._thread = None # select all threads initially self.thread = b"0" if noack and b"QStartNoAckMode+" in self.feats: self.fetchOK(b"QStartNoAckMode") self.ack = False self.read_ack = lambda *_, **__ : None # replace deprecated resumption commands with new vCont analogues # https://sourceware.org/gdb/onlinedocs/gdb/Packets.html#vCont-packet if b"vContSupported+" in self.feats: actions = self.fetch(b"vCont?").split(b';') # vCont[;action...] if b"c" in actions: self.cont = self.vContc self.cont_all = self.vContc_all if b"s" in actions: self.step = self.vConts if noshell and b"QStartupWithShell+" in self.feats: # extended mode is required self.fetchOK(b"!") self.fetchOK(b"QStartupWithShell:0") self.noshell = True else: self.noshell = False # attach self.connect()
def readpkt(self, timeout=0): """ blocks until it reads an RSP packet, and returns it's data""" c=b"" discards=[] if timeout>0: start = time.time() while(c!=b'$'): if c: discards.append(s(c)) c=self.port.read() if timeout>0 and start+timeout < time.time(): return if len(discards)>0 and self.verbose: print('discards %s' % discards) res=c while True: res += self.port.read() if res[-1:]==b'#': res += self.port.read() + self.port.read() if self.ack: try: res = unpack(res) except: self.port.write(b'-') res = b'' continue self.port.write(b'+') else: # Do not even check packages in NoAck mode. # If a user relies on the connection robustness then we # should provide as fast operation as we can. res = res[1:-3] #print("read %s" % res) return res
def to_file(self, dot_file_name): "Writes QOM tree to Graphviz file." graph = Digraph( name="QOM", graph_attr=dict(rankdir="LR"), node_attr=dict(shape="polygon", fontname="Momospace"), edge_attr=dict(style="filled"), ) for t in sort_topologically(v for v in self.tree.name2type.values() if v.parent is None): n = gv_node(t.name) label = t.name + "\\n0x%x" % t.impl.address if t.instance_casts: label += "\\n*" for cast in t.instance_casts: label += "\\n" + s(cast.name) graph.node(n, label=label) if t.parent: graph.edge(gv_node(t.parent), n) with open(dot_file_name, "w") as f: f.write(graph.source)
def dump(self, size, addr = None): """ dumps data from addr if given otherwise at beginning of .text segment aka self.elf.workarea""" if addr==None: addr=self.elf.workarea rd = b'' end = addr + size bsize = int(self.feats[b'PacketSize'], 16) // 2 while addr < end: bsize = bsize if addr + bsize < end else end - addr #print('m%x,%x' % (addr, bsize)) pkt = self.fetch(b'm%x,%x' % (addr, bsize)) if len(pkt) & 1 and pkt[:1] == b'E': # There is an assumption that stub only uses 'e' for data # hexadecimal representation and 'E' is only used for errors. # However, no confirmation has been found in the protocol # definition. But, according to the protocol error message # data length is always odd (i.e. Exx). raise RuntimeError("Reading %u bytes at 0x%x failed: %s " % ( bsize, addr, s(pkt) )) rd += unhex(rsp_decode(pkt)) addr += bsize #print("%s %s pkt %s" % (addr, bsize, pkt)) return rd
def readpkt(self, timeout=0): """ blocks until it reads an RSP packet, and returns it's data""" c = b"" discards = [] if timeout > 0: start = time.time() while (c != b'$'): if c: discards.append(s(c)) c = self.port.read() if timeout > 0 and start + timeout < time.time(): return if len(discards) > 0 and self.verbose: print('discards %s' % discards) res = c while True: res += self.port.read() if res[-1:] == b'#': res += self.port.read() + self.port.read() if self.ack: try: res = unpack(res) except: self.port.write(b'-') res = b'' continue self.port.write(b'+') else: # Do not even check packages in NoAck mode. # If a user relies on the connection robustness then we # should provide as fast operation as we can. res = res[1:-3] #print("read %s" % res) return res
def del_br(self, addr, quiet=False): """ deletes breakpoint at address addr """ #self.fetch('z0,%s,2' % addr) if 'old' in self.br[addr]: tmp = self.fetch(b'X%s,2:%s' % (addr, self.br[addr]['old'])) if self.verbose and not quiet: sym = self.br[addr]['sym'] or "[unknown]" print("clear breakpoint: @%s (0x%s) %s" % (sym, s(addr), s(tmp))) else: tmp = self.fetch(b'z0,%s,2' % addr) if tmp!= b'OK': print("failed to clear break: @%s (0x%s) %s" % ('FaultHandler', s(addr), s(tmp))) elif self.verbose and not quiet: print("clear break: @%s (0x%s) %s" % ('FaultHandler', s(addr), s(tmp))) del self.br[addr]
def lazy_dump_regs(self): """ refreshes and dumps registers via stdout """ self.refresh_regs() print('[r]' + ' '.join([ "%s:%s" % (r, s(self.regs.get(r))) for r in self.registers if self.regs.get(r) != self.prev_regs.get(r) ])) self.prev_regs = self.regs
def connect(self): """ attaches to blackmagic jtag debugger in swd mode """ # ignore redundant stuff tmp = self.readpkt(timeout=1) while (tmp): tmp = self.readpkt(timeout=1) # enable extended mode self.extended = self.fetch(b'!') == b"OK" # setup registers TODO # registers should be parsed from the output of, see target.xml #print(self.fetch('qXfer:features:read:target.xml:0,3fb')) #print(self.fetch('Xfer:features:read:target.xml:3cf,3fb')) #print(self.fetch('qXfer:memory-map:read::0,3fb')) #print(self.fetch('qXfer:memory-map:read::364,3fb')) self.port.setup(self) addr = struct.unpack(">I", self.getreg(4, 0x0000000c))[0] - 1 addr = self.reg_fmt % addr self.br[addr] = { 'sym': "FaultHandler", 'addr': addr, 'cb': self.checkfault } tmp = self.fetch(b'Z1,%s,2' % addr) if tmp == b'OK': if self.verbose: print("set break: @%s (0x%s) %s" % ('FaultHandler', s(addr), s(tmp))) return # vector_catch enable hard int bus stat chk nocp mm reset self.send( b'qRcmd,766563746f725f636174636820656e61626c65206861726420696e742062757320737461742063686b206e6f6370206d6d207265736574' ) pkt = self.readpkt() while pkt != b'OK': if pkt[:1] != b'O': raise ValueError('not O: %s' % s(pkt)) if self.verbose: print(unhex(pkt[1:-1])) pkt = self.readpkt()
def _get_feats(self): if self.ack: self.port.write(pack(b'+')) tmp = self.readpkt(timeout=1) if tmp and self.verbose: print('helo %s' % s(tmp)) self.send(b'qSupported:swbreak+;vContSupported+') feats = self.readpkt() if feats: self.feats = dict((ass.split(b'=') if b'=' in ass else (ass,None) for ass in feats.split(b';')))
def _get_feats(self): if self.ack: self.port.write(pack(b'+')) tmp = self.readpkt(timeout=1) if tmp and self.verbose: print('helo %s' % s(tmp)) self.send(b'qSupported:swbreak+;vContSupported+') feats = self.readpkt() if feats: self.feats = dict((ass.split(b'=') if b'=' in ass else (ass, None) for ass in feats.split(b';')))
def read_ack(self, retries=50): res = None while not res: res = self.port.read() discards = [] while res != b'+' and retries > 0: discards.append(s(res)) retries -= 1 res = self.port.read() if len(discards) > 0 and self.verbose: print('read_ack discards %s' % discards) if retries == 0: raise ValueError("retry fail")
def read_ack(self, retries=50): res = None while not res: res = self.port.read() discards = [] while res != b'+' and retries > 0: discards.append(s(res)) retries -= 1 res = self.port.read() if len(discards) > 0 and self.verbose: print('read_ack discards %s' % discards) if retries == 0: raise ValueError("retry fail")
def get_src_map(self, elffile): """ builds a dictionary of the DWARF information, used to populate self.src_map returns a dictionary with either the address as key, or filename:lineno the values are respectively {addr, file, lineno, line} and {addr, line} """ src_map = {} if not elffile.has_dwarf_info(): raise ValueError("No DWARF info found") _dwarfinfo = elffile.get_dwarf_info() for cu in _dwarfinfo.iter_CUs(): lineprogram = _dwarfinfo.line_program_for_CU(cu) cu_filename = lineprogram['file_entry'][0].name if len(lineprogram['include_directory']) > 0: dir_index = lineprogram['file_entry'][0].dir_index if dir_index > 0: dir = lineprogram['include_directory'][dir_index - 1] else: dir = '.' cu_filename = '%s/%s' % (dir, cu_filename) for entry in lineprogram.get_entries(): state = entry.state if state: fname = s(lineprogram['file_entry'][state.file - 1].name) line = self.fcache.get_src_lines(cu_filename, state.line) src_map["%08x" % state.address] = { 'file': fname, 'lineno': state.line, 'line': line } try: src_map["%s:%s" % (fname, state.line)].append({ 'addr': "%08x" % state.address, 'line': line }) except KeyError: src_map["%s:%s" % (fname, state.line)] = [{ 'addr': "%08x" % state.address, 'line': line }] return src_map
def connect(self): """ attaches to blackmagic jtag debugger in swd mode """ # ignore redundant stuff tmp = self.readpkt(timeout=1) while(tmp): tmp = self.readpkt(timeout=1) # enable extended mode self.extended = self.fetch(b'!') == b"OK" # setup registers TODO # registers should be parsed from the output of, see target.xml #print(self.fetch('qXfer:features:read:target.xml:0,3fb')) #print(self.fetch('Xfer:features:read:target.xml:3cf,3fb')) #print(self.fetch('qXfer:memory-map:read::0,3fb')) #print(self.fetch('qXfer:memory-map:read::364,3fb')) self.port.setup(self) addr=struct.unpack(">I", self.getreg(4, 0x0800000c))[0] - 1 addr = self.reg_fmt % addr self.br[addr]={'sym': "FaultHandler", 'addr': addr, 'cb': self.checkfault} tmp = self.fetch(b'Z1,%s,2' % addr) if tmp== b'OK': if self.verbose: print("set break: @%s (0x%s) %s" % ('FaultHandler', s(addr), s(tmp))) return # vector_catch enable hard int bus stat chk nocp mm reset self.send(b'qRcmd,766563746f725f636174636820656e61626c65206861726420696e742062757320737461742063686b206e6f6370206d6d207265736574') pkt=self.readpkt() while pkt!=b'OK': if pkt[:1]!=b'O': raise ValueError('not O: %s' % s(pkt)) if self.verbose: print(unhex(pkt[1:-1])) pkt=self.readpkt()
def handle_br(self): """ dumps register on breakpoint/signal, continues if unknown, otherwise it calls the appropriate callback. """ if self.verbose: print("") self.dump_regs() else: self.refresh_regs() if not self.regs[self.pc_reg] in self.br: print("unknown break point passed") self.dump_regs() return if self.verbose: br = self.br[self.regs[self.pc_reg]] print('breakpoint hit: %s' % (br['sym'] or "0x%s" % s(br['addr']))) self.br[self.regs[self.pc_reg]]['cb']()
def handle_br(self): """ dumps register on breakpoint/signal, continues if unknown, otherwise it calls the appropriate callback. """ if self.verbose: print("") self.dump_regs() else: self.refresh_regs() if not self.regs[self.pc_reg] in self.br: print("unknown break point passed") self.dump_regs() return if self.verbose: br = self.br[self.regs[self.pc_reg]] print('breakpoint hit: %s' % (br['sym'] or "0x%s" % s(br['addr']))) self.br[self.regs[self.pc_reg]]['cb']()
def get_src_map(self, elffile): """ builds a dictionary of the DWARF information, used to populate self.src_map returns a dictionary with either the address as key, or filename:lineno the values are respectively {addr, file, lineno, line} and {addr, line} """ src_map = {} if not elffile.has_dwarf_info(): raise ValueError("No DWARF info found") _dwarfinfo = elffile.get_dwarf_info() for cu in _dwarfinfo.iter_CUs(): lineprogram = _dwarfinfo.line_program_for_CU(cu) cu_filename = lineprogram['file_entry'][0].name if len(lineprogram['include_directory']) > 0: dir_index = lineprogram['file_entry'][0].dir_index if dir_index > 0: dir = lineprogram['include_directory'][dir_index - 1] else: dir = '.' cu_filename = '%s/%s' % (dir, cu_filename) for entry in lineprogram.get_entries(): state = entry.state if state: fname = s(lineprogram['file_entry'][state.file - 1].name) line = self.fcache.get_src_lines(cu_filename, state.line) src_map["%08x" % state.address] = {'file': fname, 'lineno': state.line, 'line': line} try: src_map["%s:%s" % (fname, state.line)].append({'addr': "%08x" % state.address, 'line': line}) except KeyError: src_map["%s:%s" % (fname, state.line)]= [{'addr': "%08x" % state.address, 'line': line}] return src_map
def dump(self, size, addr=None): """ dumps data from addr if given otherwise at beginning of .text segment aka self.elf.workarea""" if addr == None: addr = self.elf.workarea rd = b'' end = addr + size bsize = self.get_packet_size() // 2 while addr < end: bsize = bsize if addr + bsize < end else end - addr #print('m%x,%x' % (addr, bsize)) pkt = self.fetch(b'm%x,%x' % (addr, bsize)) if len(pkt) & 1 and pkt[:1] == b'E': # There is an assumption that stub only uses 'e' for data # hexadecimal representation and 'E' is only used for errors. # However, no confirmation has been found in the protocol # definition. But, according to the protocol error message # data length is always odd (i.e. Exx). raise RuntimeError("Reading %u bytes at 0x%x failed: %s " % (bsize, addr, s(pkt))) rd += unhex(rsp_decode(pkt)) addr += bsize #print("%s %s pkt %s" % (addr, bsize, pkt)) return rd
def del_br(self, addr, quiet=False): """ deletes breakpoint at address addr """ #self.fetch('z0,%s,2' % addr) if 'old' in self.br[addr]: tmp = self.fetch(b'X%s,2:%s' % (addr, self.br[addr]['old'])) if self.verbose and not quiet: sym = self.br[addr]['sym'] or "[unknown]" print("clear breakpoint: @%s (0x%s) %s" % (sym, s(addr), s(tmp))) else: tmp = self.fetch(b'z0,%s,2' % addr) if tmp != b'OK': print("failed to clear break: @%s (0x%s) %s" % ('FaultHandler', s(addr), s(tmp))) elif self.verbose and not quiet: print("clear break: @%s (0x%s) %s" % ('FaultHandler', s(addr), s(tmp))) del self.br[addr]
def dump_regs(self): """ refreshes and dumps registers via stdout """ self.refresh_regs() print(' '.join(["%s:%s" % (r, s(self.regs.get(r))) for r in self.registers]))
def dump_regs(self): """ refreshes and dumps registers via stdout """ self.refresh_regs() print(' '.join( ["%s:%s" % (r, s(self.regs.get(r))) for r in self.registers]))
def lazy_dump_regs(self): """ refreshes and dumps registers via stdout """ self.refresh_regs() print('[r]' + ' '.join(["%s:%s" % (r, s(self.regs.get(r))) for r in self.registers if self.regs.get(r)!=self.prev_regs.get(r)])) self.prev_regs=self.regs
def __init__(self, port, elffile=None, verbose=False, noack=False, noshell=False): """ read the elf file if given by elffile, connects to the debugging device specified by port, and initializes itself. """ # Initially packet acknowledgment is enabled. # https://sourceware.org/gdb/onlinedocs/gdb/Packet-Acknowledgment.html self.ack = True self.registers = self.arch['regs'] self.maxsize_g_packet = 0 self.reg_fmt = b"%%0%ux" % (self.arch['bitsize'] >> 2) self.__dict__['br'] = {} self.__dict__['verbose'] = verbose # open serial connection self.__dict__['port'] = Debugger( port) #serial.Serial(port, 115200, timeout=1) # parse elf for symbol table, entry point and work area self.__dict__['elf'] = ELF(elffile) if elffile else None if verbose and self.elf: print("work area: 0x%x" % self.elf.workarea) print("entry: 0x%x" % self.elf.entry) # check for signal from running target tmp = self.readpkt(timeout=1) if tmp and verbose: print('helo %s' % s(tmp)) #self.port.write(pack('qSupported:multiprocess+;qRelocInsn+')) self._get_feats() # By default, use Z/z packets to manipulate breakpoints self.z_breaks = True self._thread = None # select all threads initially self.thread = b"0" if noack and b"QStartNoAckMode+" in self.feats: self.fetchOK(b"QStartNoAckMode") self.ack = False self.read_ack = lambda *_, **__: None # replace deprecated resumption commands with new vCont analogues # https://sourceware.org/gdb/onlinedocs/gdb/Packets.html#vCont-packet if b"vContSupported+" in self.feats: actions = self.fetch(b"vCont?").split(b';') # vCont[;action...] if b"c" in actions: self.cont = self.vContc self.cont_all = self.vContc_all if b"s" in actions: self.step = self.vConts if noshell and b"QStartupWithShell+" in self.feats: # extended mode is required self.fetchOK(b"!") self.fetchOK(b"QStartupWithShell:0") self.noshell = True else: self.noshell = False # attach self.connect()