First of all, thank you for taking the time to write this detailed reply. Second of all, it's working now!
Since you pointed at AN5185, I tried reading the device information table using the "Memory & File edition" tab, but I didn't see anything useful. According to AN5185, p. 32, Table 21, this is to be expected when using SWD: "So when accessing device via SWD, it is normal to not find device info table valid because it has not yet been written or CPU2 has not been enabled yet."
After that I decided to check the memory contents around 0x20030000 while single-stepping through the program, but nothing was changing there, everything remained at 0. This behaviour was certainly strange, so I compared this to a working program on a P-NUCLEO-WB55 and saw that various addresses were supposed to be written to theses locations.
This led me to compare the linker files of both the program on my custom board and the one running on the Nucleo and - lo and behold! - the shared RAM address in my linker file was incorrect.
Instead of
RAM_SHARED (xrw) : ORIGIN = 0x20030000, LENGTH = 10K
it was set to
RAM_SHARED (xrw) : ORIGIN = 0x20010000, LENGTH = 10K
This appears to be an error in the CubeMX project templates, because I never changed the file and the test project I just created has the same mistake.
I hope this thread is helpful to anyone having the same issue.