Ram Debugging Issues

Hi, after digging thru different docs I finally manged to Debug in RAM.
My Setup is:
Black Magic Probe on STM32F4 Discovery Board, VSCode, Framework: opencm3, so your mileage may vary.

Starting with:

  1. copy the original linker file stm32f405x6.ld to the root of your project and name it


  1. Adapt the values:

rom (rx) : ORIGIN = 0x20000000, LENGTH = 112K
ram (rwx) : ORIGIN = 0x2001C000, LENGTH = 16K

  1. reference this file in your platformio.ini

board_build.ldscript = stm32f405x6_ram.ld

  1. Configure pio / bmp as:
board = disco_f407vg
platform = ststm32
framework = libopencm3
build_type = debug
upload_protocol = blackmagic
upload_port = /dev/ttyBmpGdb
debug_tool = blackmagic
debug_port = ${this.upload_port}
monitor_port =
build_flags =
board_build.ldscript = stm32f405x6_ram.ld
debug_init_break = tbreak reset_handler
debug_init_cmds =
  # set trace-commands on
  define pio_reset_halt_target
    set language auto
    ; automatically by load
#    set $pc=reset_handler
    set $r0=$r1=$r2=$r3=$r4=$r5=$r6=$r7=$r8=$r9=$r10=$r11=$r12=0
#  _stack does not exist, so "end" or:
    set $sp=vector_table.initial_sp_value
    set $msp=$sp
    ; VTOR in RAM, hardcoded
    set *0xE000ED08=0x20000000
  define pio_reset_run_target
  target extended-remote $DEBUG_PORT
  monitor swdp_scan
  attach 1
  set mem inaccessible-by-default off
  delete mem 0 # dont write flash
  1. build your project but don’t upload. Instead start debugging (Pio Build in Statusbar, than switch to Debug pane and press the green play button (PIO Debug)
    It should upload your ELF file to RAM and break in reset_handler. You can change this if you are quite sure that init stuff will work and change it to main or any location you like.

I tested with some interrupt / exception handlers which are working. I didn’t want to make HW changes (boot1 switch) nor software changes messing with reset_handler.c or startup.s (in different frameworks) so I put everything in the GDB init. Basically its just mapping your flash to RAM in Linker definition file, and change the provided debug init commands to:

  • forbid any kind of reset
  • re-implement missing init: SP / VTOR
  • optionally: delete flash mem area to no accidently write to it, init registers

Maybe not perfect but working. I will try to prove this on other boards/frameworks.

I continued testing, and found a strange problem which seems to happen only occasionally (!)
I tested with a simple systick handler, and a noticed a misalignment of one byte, from the entries in the vector table to the correctly aligned start adresses.

The consequences: all interrupts load wrong locations, even if this was tested succesfully.

gdb loads the elf

Start address 0x20000310, load size 1020
and pc is set to this value which is correct.

BUT in the vector table is it not:

$ p/x vector_table.reset
$3 = 0x20000311

the whole table is misaligned:

$ p vector_table
$1 = {initial_sp_value = 0x20020000
, reset = 0x20000311 <reset_handler>

systick = 0x20000239 <sys_tick_handler>

$ disass reset_handler
Dump of assembler code for function reset_handler:
=> 0x20000310 <+0>: push {r4, lr}

$ disass sys_tick_handler
Dump of assembler code for function sys_tick_handler:
0x20000238 <+0>: str.w r11, [sp, #-4]!

But in the vector table its +1 byte and 0x239 is misaligned.

$ p/x (0x20000000 + 415)
$7 = 0x20000239

This is also the wrong value in the firmware.bin file

I can debug my code, unless a delay function waits for the systick value to increase which never happens. also the interrupt is never called.

This is more related to framework / toolset:

The map file during the linking, states 8 bit aligned funtions for reset_handler and sys_tick_handler:

                0x0000000020000310       0x94 .pio/build/disco_f4/libFrameworkLibOpenCM3.a(vector.o)
                0x0000000020000310                reset_handler              
                0x0000000020000238       0x20 .pio/build/disco_f4/src/systick.o   

But the vector tablr is incorrect. its wrong in elf file as well as binary generated by objdump

$ arm-none-eabi-objdump -s .pio/build/disco_f4/firmware.elf
 Contents of section .text:
 20000000 00000220 11030020 0f030020 0d030020  ... ... ... ... 
 20000010 0d030020 0d030020 0d030020 00000000  ... ... ... ....
 20000020 00000000 00000000 00000000 0f030020  ............... 
 20000030 0f030020 00000000 0f030020 39020020  ... ....... 9..

It should be 0x10030020 for the 2nd and 0x38020020 for the 16th 32bit value.

Each of those entries is correct, because the address of a function which is supposed to be executed in Thumb mode vs ARM instruction mode has to have + 1 to the address. The address jumped to will have the last bit masked (set to 0).


Ok, I was searching at wrong directions. It seems that MSP was forgotten to be initialized, I added this in the first post. It seem to work now, tried with timer / uart ISR, even in single step mode.