Introduction and overview
The Debugger module within RISC OS Pyromaniac uses the Capstone Engine to disassemble ARM code. The resulting output from the debugger is slightly different to that which with Classic Debugger module produced. The manner in which the disassembly is presented can be configured through the regular RISC OS Pyromaniac configuration settings, and system variables.
The RISC OS Pyromaniac version of the Debugger module also includes additional *Commands which were previously provided by extension commands.
System variables
The Debugger$Options variable is used to change the way in which the Debugger's *MemoryI and *DumpI commands functions. It is defined as a space separated list of options, where the leading character defines the option to set. At the current time, only a single option is defined.
Name | Meaning |
---|---|
C | Enables colouring of parts of the disassembly. |
SWI calls
R0 | = | instruction to disassemble |
R1 | = | address to assume the instruction came from |
R0 | preserved | |
R1 | = | address of buffer containing null-terminated text |
R2 | = | length of disassembled line |
R0 contains the instruction to disassemble, for the current system. R1 contains the
address from which to assume the instruction came, which is needed for
instructions such as B
, BL
,
LDR Rn, [PC...]
, and so on. On exit, R1 points to a
buffer which contains a zero terminated string. This string consists of the
instruction mnemonic, and any operands, in the format used by the *MemoryI instruction. The length in R2 excludes the
zero-byte. There may be comments following the operands, indicated by
a semicolon, for example ; Undefined behaviour
.
Earlier versions of the reference manuals defined this to be a 32bit value, assuming it to be for ARM. The SWI is now defined to disassemble an instruction in the current architecture of the system. This allows the SWI to be used on any system to decode instructions within the current system.
R0 | = | instruction to disassemble |
R1 | = | address to assume the instruction came from |
R0 | preserved | |
R1 | = | address of buffer containing null-terminated text |
R2 | = | length of disassembled line |
R0 contains the instruction to disassemble, for the current system. R1 contains the
address from which to assume the instruction came, which is needed for
instructions such as B
, BL
,
LDR Rn, [PC...]
, and so on. On exit, R1 points to a
buffer which contains a zero terminated string. This string consists of the
instruction mnemonic, and any operands, in the format used by the *MemoryI instruction. The length in R2 excludes the
zero-byte. There may be comments following the operands, indicated by
a semicolon, for example ; Undefined behaviour
.
R0 | = | BIC mask (flags to clear) |
R1 | = | EOR mask (flags to invert) |
R0 | = | old flags state before changes |
R1 | = | new flags state |
Debugger_Flags changes the default format that the disasssembly uses for instructions, operands and comments. The flags control individual features of the disassembly. Flags are architecture specific. The flags will be modified by applying the BIC mask to clear bits, and then the EOR mask to invert flags.
The flags may also be controlled from the command line with *DisassembleFlags, which uses the names of the flags to control their state.
For the ARM architecture, the flags may take the following values:
Bit(s) | Name | Meaning | |
---|---|---|---|
0 | FDwithR13 | Use FD with R13, eg. STMDB R13 becomes STMFD R13. | |
1 | APCS | Use APCS-R register set and recognise C function entry. | |
2 | LFMstack | Use stack notation with LFM and SFM where possible. | |
3 | LFS | Use LFS and SFS in preference to LFM & SFM. | |
4 | QuoteSWIs | Put quotes around SWI names. | |
5 | UseDCD | Use DCD instead of 'Undefined instruction', and BRK where DCD &x6000010 would be used. | |
6 | UseVDU | Use VDU x instead of SWI OS_WriteI+x. | |
7 | ANDEQasDCD | Use DCD instead of ANDEQ, MOV Rn,Rn (same register) etc. | |
8 | UseADRL | Use ADRL/ADRX instead of ADR then ADD/SUB on same reg. | |
9 | UseADRW | Use ADRW instead of ADD/SUB Rn,R12,#m and LDRW, STRW, LDRBW, STRBW instead of xxxx Rn,[R12,#m]. | |
10 | LongMul | Append L to UMUL, UMLA, SMUL, SMLA (thus using the 'official' forms). | |
11 | UseLDRL | Use LDRL instead of ADD/SUB Rn,Rm,#o + LDR Rn,[Rn,#p] and ADD/SUB Rm,Ra,#o + LDR Rn,[Ra,#p]! and STR instead of equivalent STRs. (The LDRWL form is enabled by this *and* UseADRW) | |
12 | UseNOP | Use NOP instead of MOV R0,R0. | |
13 | OldPSR | Use the old PSR suffixes _ctl, _flg, _all. | |
14 | Wide | Disassemble for wide display. | |
15 | HSLO | Use HS and LO instead of CS and CC. | |
16 | Shift | Use x<<y comments where possible for numbers >= 8192. This affects arithmetic and logic instructions. y is restricted to multiples of 4 if possible, unless x=1. | |
17 | Lower | Force all register names to lower case. | |
18 | ConstShift |
Display non-standard constant (x ROR y) as #x,y. This flag affects certain instructions in which the
constant is not stored in the standard way, possibly having unexpected effects if you try to reassemble
the code. | |
19 | ConstShiftAll | Display non-standard constant (x ROR y) as #x,y. | |
20-31 | - | Reserved for future expansion. |
Supports a limited set of the flags:
APCS, Lower, UseDCD, ANDEQasDCD, UseNOP, QuoteSWIs, UseVDU
R0 | = | instruction to disassemble |
R1 | = | address to assume the instruction came from |
R2 | = | disassembly flags to use |
R0 | preserved | |
R1 | = | address of buffer containing null-terminated text |
R2 | = | length of disassembled line |
Debugger_DisassemblePlus takes the same parameters as SWI Debugger_Disassemble, but overrides the default disassembly flags with explicit flags to use for this call. See SWI Debugger_Flags for details of the flags.
R0 | = | architecture identifier for disassembly, or -1 for current architecture
| ||||||||||||||
R1 | = | pointer to instruction bytes | ||||||||||||||
R2 | = | number of instruction bytes supplied | ||||||||||||||
R3 | = | address to assume the instruction came from | ||||||||||||||
R4 | = | pointer to output buffer, or 0 to use internal buffer | ||||||||||||||
R5 | = | size of output buffer, or 0 to read size required | ||||||||||||||
R6 | = | flags to apply to disassembly (architecture specific), or &FFFFFFFF for default flags |
R0 | = | architecture identifier used |
R1 | = | pointer to next instruction byte |
R2 | = | number of instruction bytes remaining in buffer, or a negative value to indicate the number of bytes that are required |
R3 | preserved | |
R4 | = | address of buffer containing null-terminated text, in either the supplied or internal buffer |
R5 | = | number of bytes remaining in buffer, or negative number of bytes needed if buffer too short |
R6 | = | length of disassembled text |
This SWI is used to disassemble instructions in an architecture agnostic manner. Whilst SWI Debugger_Disassemble can disassemble the current architecture, it does not provide the ability to decode in a thread safe manner, and does not support variable length instruction sequences. This SWI supports both these features, and allows greater control over the disassembly with flags.
The architecture identifier supplied is the same as that used by the module format, and OS_PlatformFeatures 64 interface.
To support variable length instructions, R1 contains the pointer to an instruction sequence to disassemble, and R2 contains the number of bytes which are available at the pointer. If insufficient bytes have been supplied, an error will be returned and R2 will be the negative number of bytes required.
If R5 is 0, or the value supplied is less than the size required for disassembly, an buffer overflow error (&1E4) will be returned with R5 set to the negative size required. If R4 is set to 0 an internal buffer will be used (R5 must be set to a non-0 value).
The disassembly format will use a semi-colon to delimit any additional commentary, as in standard ARM disassembly format.
*Commands
<architecture> | - | architecture to disassemble with:
| ||||||||
<addr_or_reg1> | - | hexadecimal address, module name, or register containing address for start of display | ||||||||
<addr_or_reg2> | - | hexadecimal offset, or register containing offset | ||||||||
<addr_or_reg3> | - | hexadecimal offset, or register containing offset |
*MemoryI disassembles memory into ARM instructions.
If only one address is given, 24 instructions are disassembled starting from addr_or_reg1. If two addresses are given, addr_or_reg2 specifies the end of the range to be disassembled (as an absolute address or, if '+' or '-' is present, as an offset from addr_or_reg1). If three addresses are given, addr_or_reg2 specifies an offset for the start from addr_or_reg1, and addr_or_reg3 specifies the end of the range to be disassembled (as an offset from the combined address given by addr_or_reg1 and addr_or_reg2).
If a module is supplied as the first address, the base address of the module will be used. These options are particularly useful for disassembling modules, which contain offsets, not addresses.
When ARM or Thumb are decoded, the allowed register names are r0 - r15, sp (equivalent to r13), lr (r14 without the psr bits) and pc (r15 without the psr bits). These are taken from the current ExceptionDumpArea. For non-ARM/Thumb architectures the register names are decoded using SWI OS_PlatformFeatures 64.
*modules
No. Position Workspace Name
...
22 0184D684 018016B4 Debugger
...
*memoryi 184D684 +24
0184D684 : .... : 00000000 : ANDEQ R0,R0,R0
0184D688 : \... : 0000005C : ANDEQ R0,R0,R12,ASR R0
0184D68C : (... : 00000128 : ANDEQ R0,R0,R8,LSR #2
0184D690 : .... : 00000104 : ANDEQ R0,R0,R4,LSL #2
0184D694 : (... : 00000028 : ANDEQ R0,R0,R8,LSR #32
0184D698 : >... : 0000003E : ANDEQ R0,R0,R14,LSR R0
0184D69C : h... : 00000168 : ANDEQ R0,R0,R8,ROR #2
0184D6A0 : ... : 00040380 : ANDEQ R0,R4,R0,LSL #7
0184D6A4 : u... : 000005FC : MULEQ R0,R12,R5
Offset of SWI handler is &5FC
Disassemble SWI handler
*memoryi 184D684 +5FC +20
0184DC80 : .B-e : E92D4200 : STMDB R13!,{R9,R14}
0184DC84 : .A|u : E49CC000 : LDR R12,[R12],#0
0184DC88 : ..;a : E33B0000 : TEQ R11,#0
0184DC8C : .... : 0A000005 : BEQ &0184DCA8
0184DC90 : ...a : E28F0004 : ADR R0,&0184DC9C
0184DC94 : _..e : EB00075F : BL &0184FA18
0184DC98 : . .e : E8BD8200 : LDMIA R13!,{R9,PC}
0184DC9C : .... : 0000010F : ANDEQ R0,R0,PC,LSL #2
<filename> | - | File to read and display contents of | ||||||||
<architecture> | - | architecture to disassemble with:
| ||||||||
<file_offset> | - | Offset to start displaying file from | ||||||||
<base_address> | - | Logical address to use as the start of the output |
*DumpI displays the contents of a file as a disassembly in a similar way to *Dump.
If the architecture is not supplied, the current architecture is used. If the file offset is not supplied, the entire file will be shown, from the beginning. If the base address is not supplied, the base address will be 0, unless the file is an Absolute or Debug image, in which case it will use &8000.
*ShowRegs displays the register contents for the saved state of the last exception. The information displayed is recovered from the program environment Exception Dump Area, the Exception Dump Region, and the Last Fault.
The information reported will be updated by the Kernel in the following cases:
- undefined instruction
- address exception
- data abort
- prefetch abort
- break point.
Additionally, applications may update the state with soft exception information.
The regions referenced by registers are reported if recognisable, together with any memory that is currently present (which may differ from the memory at the time of the exception). A disassembly of the memory leading up to the failure will be displayed using the Exception Dump Region information. This may help when the memory in question has been modified before returning to the command line.
Information about the last exception recorded by the Kernel is displayed, togther with information about the fault status registers and their addresses.
It also prints the address in memory where the registers are stored, so you can alter them (for example after a breakpoint) by using *MemoryA on these locations, before using *Continue.
*ShowRegs
Exception dump information for ARM (stored at &0700035c):
r0 = &070023ec, r1 = &00000000, r2 = &00008700, r3 = &00008201
r4 = &00107fd8, r5 = &0381f520, r6 = &03817cfc, r7 = &00000000
r8 = &00008700, r9 = &40000000, r10 = &0000002a, r11 = &03817cfe
r12 = &00230000, sp = &04107f8c, lr = &04107f8c, pc = &0700235c
CPSR= &80000013 : SVC-32 ARM fi ae qvczN
Locations:
lr: DA 'SVC Stack' (area offset &7f8c)
pc: DA 'Module allocations', module 'Aborter' (area offset &180)
Disassembly before pc (from time of exception):
&07002314 : &ea000012 : B &07002364
&07002318 : &ea000023 : B &070023ac
&0700231c : &e28f0000 : ADR r0, &07002324
&07002320 : &ea000005 : B &0700233c
&07002324 : &000001e6 : ANDEQ r0, r0, r6, ROR #3
&07002328 : &726f6241 : ADRVC r6, &f700232c
&0700232c : &20726574 : RSBSHS r6, r2, r4, ROR r5
&07002330 : &20495753 : SUBHS r5, r9, r3, ASR r7
&07002334 : &6e6b6e75 : MCRVS p14, #3, r6, c11, c5, #3
&07002338 : &006e776f : RSBEQ r7, lr, pc, ROR #14
&0700233c : &e1300000 : TEQ r0, r0
&07002340 : &e13f000f : TEQ pc, pc
&07002344 : &139ef201 : ORRSNE pc, lr, #&10000000 ; #268435456 = bit 28
&07002348 : &e328f201 : MSR apsr_nzcvq, #&10000000 ; #------ --- -- -- qVczn
&0700234c : &e1a0f00e : MOV pc, lr
&07002350 : &e3a0c823 : MOV r12, #&230000 ; #2293760 = 35<<16
&07002354 : &e1a0f00e : MOV pc, lr
&07002358 : &e3a0c823 : MOV r12, #&230000 ; #2293760 = 35<<16
&0700235c > &ee123456 > MRC p4, #0, r3, c2, c6, #2
&07002360 : &e1a0f00e : MOV pc, lr
&07002364 : &e1a0a00e : MOV r10, lr
&07002368 : &e3a0c823 : MOV r12, #&230000 ; #2293760 = 35<<16
Last abort:
Fault: &0700235c (DA 'Module allocations', module 'Aborter')
<flag_name> | - | Flag to control (see below) | ||||||||||
<state> | - | New state for the flag, which can take the following values:
|
*DisassembleFlags changes the current default flags that are used to change the disassembly of instructions. The flags are architecture specific.
See SWI Debugger_Flags for details of the flag names and their meaning.
*DisassembleFlags -APCS Y
<vector> | - | Which vector should be listed. May be supplied as a vector number or a vector name. |
The *Vector command will list the software vectors which have been claimed. A vector number or name may be supplied to list only that vector. When no parameter is supplied, all vectors on the system will be listed.
Vector 36 (SerialV):
INTERNAL : : riscos.swis.osserialop.vector_SerialV
The *Tickers command will list the current ticker claimants. Claimants registered with SWI OS_CallAfter and SWI OS_CallEvery will be listed, together with the internally registered ticker events.
Tickers:
&07001f14/&0700051c {DA 'Module area', module 'Freeway'} : 9 cs, every 10 cs
riscos.pymods.internet.RISCOSSockets.ticker(None) : 49 cs, every 50 cs
The *TickerStats command will show statistics about the Ticker system. If the statistics have been enabled, this will include call statistics over a period.
Ticker statistics:
Current tickers: 2
Dispatches:
second : current = 1 last = 2
15second : current = 3 last = -
minute : current = 29 last = -
Triggers:
second : current = 2 last = 2
15second : current = 4 last = -
minute : current = 14 last = -
Max queue:
second : current = 2 last = 2
15second : current = 2 last = -
minute : current = 2 last = -
Calls to CallEvery:
second : current = - last = -
15second : current = - last = -
minute : current = 2 last = -
Calls to CallAfter:
second : current = - last = -