2929from typing import Dict , List , NamedTuple , NoReturn , Optional , Set , Type , \
3030 Union
3131
32+ try :
33+ from elftools .elf .elffile import ELFFile
34+ ELFTOOLS_MISSING = False
35+ except ImportError :
36+ ELFTOOLS_MISSING = True
37+
38+
3239# Turn on to enable just logging the commands that would be run (at
3340# info rather than debug level), without actually running them. This
3441# can break runners that are expecting output or if one command
3744
3845_logger = logging .getLogger ('runners' )
3946
47+ # FIXME: I assume this code belongs somewhere else, but i couldn't figure out
48+ # a good location for it, so i put it here for now
49+ # We could potentially search for RTT blocks in hex or bin files as well,
50+ # but since the magic string is "SEGGER RTT", i thought it might be better
51+ # to avoid, at the risk of false positives.
52+ def find_rtt_block (elf_file : str ) -> Optional [int ]:
53+ if ELFTOOLS_MISSING :
54+ raise RuntimeError ('the Python dependency elftools was missing; '
55+ 'see the getting started guide for details on '
56+ 'how to fix' )
57+
58+ with open (elf_file , 'rb' ) as f :
59+ elffile = ELFFile (f )
60+ for sect in elffile .iter_sections ('SHT_SYMTAB' ):
61+ symbols = sect .get_symbol_by_name ('_SEGGER_RTT' )
62+ if symbols is None :
63+ continue
64+ for s in symbols :
65+ return s .entry .get ('st_value' )
66+ return None
67+
4068
4169class _DebugDummyPopen :
4270
@@ -219,7 +247,7 @@ def __init__(self, program):
219247 super ().__init__ (errno .ENOENT , os .strerror (errno .ENOENT ), program )
220248
221249
222- _RUNNERCAPS_COMMANDS = {'flash' , 'debug' , 'debugserver' , 'attach' , 'simulate' , 'robot' }
250+ _RUNNERCAPS_COMMANDS = {'flash' , 'debug' , 'debugserver' , 'attach' , 'simulate' , 'robot' , 'rtt' }
223251
224252@dataclass
225253class RunnerCaps :
@@ -231,7 +259,7 @@ class RunnerCaps:
231259 Available capabilities:
232260
233261 - commands: set of supported commands; default is {'flash',
234- 'debug', 'debugserver', 'attach', 'simulate', 'robot'}.
262+ 'debug', 'debugserver', 'attach', 'simulate', 'robot', 'rtt' }.
235263
236264 - dev_id: whether the runner supports device identifiers, in the form of an
237265 -i, --dev-id option. This is useful when the user has multiple debuggers
@@ -268,6 +296,9 @@ class RunnerCaps:
268296 discovered in the build directory.
269297
270298 - hide_load_files: whether the elf/hex/bin file arguments should be hidden.
299+
300+ - rtt: whether the runner supports SEGGER RTT. This adds a --rtt-address
301+ option.
271302 '''
272303
273304 commands : Set [str ] = field (default_factory = lambda : set (_RUNNERCAPS_COMMANDS ))
@@ -279,6 +310,8 @@ class RunnerCaps:
279310 tool_opt : bool = False
280311 file : bool = False
281312 hide_load_files : bool = False
313+ rtt : bool = False # This capability exists separately from the rtt command
314+ # to allow other commands to use the rtt address
282315
283316 def __post_init__ (self ):
284317 if not self .commands .issubset (_RUNNERCAPS_COMMANDS ):
@@ -319,6 +352,7 @@ class RunnerConfig(NamedTuple):
319352 gdb : Optional [str ] = None # path to a usable gdb
320353 openocd : Optional [str ] = None # path to a usable openocd
321354 openocd_search : List [str ] = [] # add these paths to the openocd search path
355+ rtt_address : Optional [int ] = None # address of the rtt control block
322356
323357
324358_YN_CHOICES = ['Y' , 'y' , 'N' , 'n' , 'yes' , 'no' , 'YES' , 'NO' ]
@@ -572,6 +606,13 @@ def add_parser(cls, parser):
572606 help = (cls .tool_opt_help () if caps .tool_opt
573607 else argparse .SUPPRESS ))
574608
609+ if caps .rtt :
610+ parser .add_argument ('--rtt-address' , dest = 'rtt_address' ,
611+ type = lambda x : int (x , 0 ),
612+ help = "address of RTT control block. If not supplied, it will be autodetected if possible" )
613+ else :
614+ parser .add_argument ('--rtt-address' , help = argparse .SUPPRESS )
615+
575616 # Runner-specific options.
576617 cls .do_add_parser (parser )
577618
@@ -607,6 +648,8 @@ def create(cls, cfg: RunnerConfig,
607648 raise ValueError ("--file-type requires --file" )
608649 if args .file_type and not caps .file :
609650 _missing_cap (cls , '--file-type' )
651+ if args .rtt_address and not caps .rtt :
652+ _missing_cap (cls , '--rtt-address' )
610653
611654 ret = cls .do_create (cfg , args )
612655 if args .erase :
@@ -731,6 +774,19 @@ def require(program: str, path: Optional[str] = None) -> str:
731774 raise MissingProgram (program )
732775 return ret
733776
777+ def get_rtt_address (self ) -> int | None :
778+ '''Helper method for extracting a the RTT control block address.
779+
780+ If args.rtt_address was supplied, returns that.
781+
782+ Otherwise, attempt to locate an rtt block in the elf file.
783+ If this is not found, None is returned'''
784+ if self .cfg .rtt_address is not None :
785+ return self .cfg .rtt_address
786+ elif self .cfg .elf_file is not None :
787+ return find_rtt_block (self .cfg .elf_file )
788+ return None
789+
734790 def run_server_and_client (self , server , client , ** kwargs ):
735791 '''Run a server that ignores SIGINT, and a client that handles it.
736792
0 commit comments