breakpoints in baremetal code for arm stm32
Your understanding of software breakpoints is basically correct: the code is modified, i.e. a breakpoint instruction is inserted in place of the real instruction and the real instruction is saved somewhere else. The specific technique depends on the debugger, CPU and operating system.
Few debuggers are capable of applying software breakpoints in MCUs with the code residing in flash memory. But since flash memory can be changed (after all you upload your code to it), sophisticated debuggers can still use them.
One of them is SEGGER's J-Link (at least certain versions). See their Unlimited Flash Breakpoints feature for more information. They don't give away all the details of the implementation though.
So with the right debugger, you can still use software breakpoints in your bare metal project.