Catching Rogue Memory Accesses With ARM Watchpoint Comparators
Want to learn more about catching rogue memory accesses? Check out this tutorial to learn how with ARM watchpoint comparators and Eclipse.
Join the DZone community and get the full member experience.
Join For FreeIn my "Tutorial: Catching Rogue Memory Accesses with Eclipse and GDB Watchpoints," I used Eclipse/CDT and GDB watchpoints. I used a conditional watchpoint, but this comes with a performance hit. In this article, I will demonstrate how to use the ARM Cortex trace hardware to catch specific writes to a memory location without severe performance degradation. But for this, I need a little helper — the Deadbeef catcher!
Outline
Compared to the previous article, Tutorial: Catching Rogue Memory Accesses with Eclipse and GDB Watchpoints, I was going beyond the normal watchpoint usage. I'm going to use dedicated plugins and views in the free-of-charge Eclipse-based NXP MCUXpresso IDE 10.2.1. As a board, I'm using the NXP FRDM-K64F with an external NXP LPC-Link debug probe attached to it — see " Custom 3D Printed Enclosure for NXP LPC-Link2 Debug Probes."
Be aware that not every ARM Cortex-M device implements the capabilities used in this article. I'm using instruction trace with watchpoint triggers, which do need the ARM MTB (Micro Trace Buffer) to be implemented, which can be augmented with the ARM ETM (Embedded Trace Macrocell).
This uses a trace buffer in RAM, which can be configured as a ring buffer. On the plus side, I don't need any dedicated trace pins or expensive debug probes; I can use normal debug probes, which are able to read/write the memory on the device.
That's why I can use for example the NXP OpenSDA with Segger J-Link, P&E Multilink, or the NXP LPC-Link2:
The needed trace configuration and knowledge are in the debugger and IDE. That way, I have special views for configuration and trace viewing available in the NXP MCUXpresso IDE.
It would be possible to configure the trace and watchpoint hardware with GDB commands/scripting or directly in the application, but this would be beyond this article.
For further information, have a look at the provided IDE documentation:
Test Code
As in the previous article, I'm going to use the following function as a test case, emulating rogue access to memory.
static uint32_t testVar;
static void bar(void) {
for(counter=0; counter<5000000; counter++) {
testVar++;
if (counter==134566) { /* emulating something else corrupting my testVar */
PRINTF("catch me...");
testVar = 0xdeadbeef; /* outsch! */
PRINTF("if you can!");
}
__asm("nop"); /* just doing something */
}
}
What I want to catch with the debugger is a write to testVar
with a value of 0xdeadbeef.
Getting Started
Note that the views used below are the ones from the NXP MCUXpresso IDE 10.2.1 and are not available in other Eclipse distributions.
First, open the Instruction trace Config view:
You can open that view from the IDE Window > Show View menu:
As noted in the view, I need a working debug connection to the board, so make sure that you start a debug session, and have it halted/suspended. Then, the view should look like this:
Configuring Trace and Triggers
With the connection to the target, use the Refresh button:
This loads the current settings from the target:
Next, inside the 'Enable trace' tab, make sure that trace is enabled:
Keep that tab in the foreground/visible, as this defines the setting!
Next, we want to stall the ETM on the processor if the FIFO buffer is getting full. Below, I have configured it with 14 free bytes free, it shall stall the processor:
Next, I'm going to create the trigger event, if something writes to my variable in question. In my case, I see that my variable is located at 0x2000'001c:
I click on the 'Request' button to get a comparator. I configure it for 'Data Write' at the address 0x2000001c. The mask is optional — with this, I catch an address range.
This would trigger for any writes to that address (range). I'm interested in a piece of code that writes the 0xdeadbeef pattern. So, I'm going to add this as for the second comparator.
I use a second comparator with 'Data Value Write' and the data pattern as match value. I have it linked back to my address (data write) comparator ID 1 (for both links) to build a linked/combined comparator. Don't forget to specify the data size (Word or 32bits in my case):
Now, I have to specify what shall happen if the comparator triggers. Switch the "Trigger condition" to the tab with One Input. I'm using only one input to the condition, so I specify that the comparator ID 2, which is linked to ID 1. I have set up in one of the previous steps:
Keep that tab in the foreground/visible, as this defines the setting!
Next I’m going to configure how many words are written to the buffer after the trigger fires to get some context around the trigger event. I have configured it for 50 words below so I still have 462 words in the trace buffer to see where the code is coming from:
As the last set, I want the trigger to raise a debug request. This will stop the debugger, and I can inspect what happened:
Finally, use the green checkmark icon to store the settings on the target:
This configures the trace and trigger registers on the microcontroller. With the settings stored, the green button will turn grey:
Additionally, that toolbar has saved and restored buttons to save my precious configuration in a text/XML file (default is etmConfig.xml) on the disk.
If the view does not show the loaded values (I had this in 10.2.1) after loading from the XML file, close the Instruction Trace Config view and re-open it again. Change a value (e.g. disable trace) and store it on the target, then enable trace again and store it again on the target with the green button. You still have to enable the comparators. Make sure you cross-check the values in the settings.
In summary, below is my configuration with all the settings:
Catching It
Continue running the application with the debugger. If the trigger condition gets true, be aware that the debugger will halt the running application with a screeching stop:
As explained in " Tutorial: Catching Rogue Memory Accesses with Eclipse and GDB Watchpoints, " there will be some jitter and the target will stop a few instructions after the event, so you have to go back a few lines of code. In my application above, the target finally came to a stop while executing instructions inside _printf()
.
Excellent! We have found the place! But, it does not stop here as we are using trace, so we have recorded the instructions leading to this memory corruption. Let's have a look!
Instruction Trace
For this, open the Instruction Trace view (if not opened already):
Press the button in the toolbar of that view to get the data from the target (ETB content):
The toolbar has an icon, which I can use to jump to the trigger packet:
That way, the offending instruction is easily identifiable:
The view has two buttons that sync with the source and disassembly view. Because of this, I can go through the trace sequence and have the source for it (if available) synced:
Now, with knowing that location changing my memory location, all that I need to do now is to fix the bug behind it.
Watchpoints with gdb are good, but hardware watchpoint triggers are better. But, they are not easily accessible and might be complicated to set up. Using the MCUXpresso IDE helps because it provides a convenient access to the ARM MTB/ITM hardware. Due to this, I can configure a watchstop trigger and, as an extra benefit, I get instruction trace. I hope you find this tutorial useful!
Happy DeadBeef'ing!
Links
Published at DZone with permission of Erich Styger, DZone MVB. See the original article here.
Opinions expressed by DZone contributors are their own.
Comments