Debug

When an application is build, it often has to be debugged. This section describes the peculiarities of debugging.

Debugger

The debugger architecture is depicted below:

digraph x {
dbg_cli [label="Debug Command Line Interface"]
dbg_qt [label="Qt user interface"]
dbg_pt [label="Prompt_toolkit user interface"]
debugger [label="Debugger"]
dbg_cli -> debugger
dbg_qt -> debugger
dbg_pt -> debugger
debugger -> debug_interface
debug_interface [label="Debug Driver Interface"]
debug_interface -> gdb_client
debug_interface -> dummy_interface
gdb_client -> transport
transport -> socket
transport -> serial
}

The debugger class is the main piece of the debugger. This is created for a specific architecture and is given a driver to communicate with the target hardware.

class ppci.binutils.dbg.Debugger(arch, driver)

Main interface to the debugger. Give it a target architecture for which it must debug and driver plugin to connect to hardware.

clear_breakpoint(filename, row)

Remove a breakpoint

read_mem(address, size)

Read binary data from memory location

run()

Run the program

set_breakpoint(filename, row)

Set a breakpoint

step()

Single step the debugged program

stop()

Interrupt the currently running program

write_mem(address, data)

Write binary data to memory location

One of the classes that uses the debugger is the debug command line interface.

class ppci.binutils.dbg.cli.DebugCli(debugger, showsource=False)

Implement a console-based debugger interface.

do_clrbrk(arg)

Clear a breakpoint. Specify the location by “filename, row” for example: main.c, 5

do_disasm(_)

Print disassembly around current location

do_info(_)

Show some info about the debugger

do_nstep(count)

Single instruction step the debugger

do_p(arg)

Print a variable

do_print(arg)

Print a variable

do_q(_)

Quit the debugger

do_quit(_)

Quit the debugger

do_read(arg)

Read data from memory: read address,length

do_readregs(_)

Read registers

do_restart(_)

Restart the running program

do_run(_)

Continue the debugger

do_s(_)

Single step the debugger

do_setbrk(arg)

Set a breakpoint: setbrk filename, row

do_setreg(arg)

Set registervalue

do_sl(line)

step one line

do_step(_)

Single step the debugger

do_stepi(_)

Single instruction step the debugger

do_stepl(line)

step one line

do_stop(_)

Stop the running program

do_write(arg)

Write data to memory: write address,hexdata

do_writeregs(_)

Write registers

postcmd(stop, line)

Hook method executed just after a command dispatch is finished.

precmd(line)

Hook method executed just before the command line is interpreted, but after the input prompt is generated and issued.

To connect to your favorite hardware, subclass the DebugDriver class.

class ppci.binutils.dbg.DebugDriver

Degug driver interface.

Inherit this class to expose a target interface. This class implements primitives for a given hardware target.

get_registers(registers)

Get the values for a range of registers

The following class can be used to connect to a gdb server:

class ppci.binutils.dbg.gdb.client.GdbDebugDriver(arch, transport, pcresval=0, swbrkpt=False)

Implement debugging via the GDB remote interface.

GDB servers can communicate via the RSP protocol.

Helpfull resources:

http://www.embecosm.com/appnotes/ean4/
embecosm-howto-rsp-server-ean4-issue-2.html

Tried to make this class about the protocol itself, not about the sending and receiving of bytes. The protocol must be able to work using sockets and threads, serial port and threads and asyncio sockets.

clear_breakpoint(address: int)

Clear a breakpoint

connect()

Connect to the target

disconnect()

Disconnect the client

get_fp()

read the frame pointer

get_pc()

read the PC of the device

get_registers(registers)

Get the values for a range of registers

nstep(count)

Single step count times

read_mem(address: int, size: int)

Read memory from address

restart()

restart the device

run()

start the device

set_breakpoint(address: int)

Set a breakpoint

set_pc(value)

set the PC of the device

step()

Single step the device

write_mem(address: int, data)

Write memory

Debug info file formats

Debug information is of a complex nature. Various file formats exist to store this information. This section gives a short overview of the different formats.

pdb format

This is the microsoft debug format.

https://en.wikipedia.org/wiki/Program_database

Dwarf format

How a linked list is stored in dwarf format.

struct ll {
  int a;
  struct ll *next;
};
<1><57>: Abbrev Number: 3 (DW_TAG_base_type)
    <58>   DW_AT_byte_size   : 4
    <59>   DW_AT_encoding    : 5    (signed)
    <5a>   DW_AT_name        : int
 <1><5e>: Abbrev Number: 2 (DW_TAG_base_type)
    <5f>   DW_AT_byte_size   : 8
    <60>   DW_AT_encoding    : 5    (signed)
    <61>   DW_AT_name        : (indirect string, offset: 0x65): long int
 <1><65>: Abbrev Number: 2 (DW_TAG_base_type)
    <66>   DW_AT_byte_size   : 8
    <67>   DW_AT_encoding    : 7    (unsigned)
    <68>   DW_AT_name        : (indirect string, offset: 0xf6): sizetype
 <1><6c>: Abbrev Number: 2 (DW_TAG_base_type)
    <6d>   DW_AT_byte_size   : 1
    <6e>   DW_AT_encoding    : 6    (signed char)
    <6f>   DW_AT_name        : (indirect string, offset: 0x109): char
 <1><73>: Abbrev Number: 4 (DW_TAG_structure_type)
    <74>   DW_AT_name        : ll
    <77>   DW_AT_byte_size   : 16
    <78>   DW_AT_decl_file   : 1
    <79>   DW_AT_decl_line   : 4
    <7a>   DW_AT_sibling     : <0x95>
 <2><7e>: Abbrev Number: 5 (DW_TAG_member)
    <7f>   DW_AT_name        : a
    <81>   DW_AT_decl_file   : 1
    <82>   DW_AT_decl_line   : 5
    <83>   DW_AT_type        : <0x57>
    <87>   DW_AT_data_member_location: 0
 <2><88>: Abbrev Number: 6 (DW_TAG_member)
    <89>   DW_AT_name        : (indirect string, offset: 0xf1): next
    <8d>   DW_AT_decl_file   : 1
    <8e>   DW_AT_decl_line   : 6
    <8f>   DW_AT_type        : <0x95>
    <93>   DW_AT_data_member_location: 8
 <2><94>: Abbrev Number: 0
 <1><95>: Abbrev Number: 7 (DW_TAG_pointer_type)
    <96>   DW_AT_byte_size   : 8
    <97>   DW_AT_type        : <0x73>