OK. I can't remember the exact process I used. I posted about it e.g. here
https://www.eevblog.com/forum/programming/st-cube-gcc-how-to-override-a-function-not-defined-as-weak-(no-sources)/Searching that c:\st\ tree yields 66 items - see attached screenshot for a part. The one I picked is 1033kb.
Then, based on the project properties settings Cube selects one of these and copies the appropriate one to a more local location, which right now I can't find. But with a lot of work I eliminated most of the candidates; most of them won't even link, due to the .elf file not compatible with something (as usual, loads of google hits). In the end I had 2 options: v7 and v8. Actually you can see them in the screenshot below. So I went for the v8 file. Then I used a linker config to hard-select my copy of the file and to get the new printf to override the relevant part of it, as described in the above link.
Everything works and is rock solid, runnin 24/7 with a fully loaded RTOS. I also traced through the printf code to make sure it really is running what it is supposed to be.
AFAICT none of this is documented anywhere. As given this code is PD, why didn't ST provide the sources, and a .h file with a load of #defines in it? It would have been a lot easier than doing 66 compilations, each with different CPU etc options. The one Cube supports the whole range of ST CPUs, hence so many libc.a files.
Maybe a paid IDE (Keil?) is better supported, but ST Cube was chosen because it is free and that was a key criterion because in some cases the customer will be writing his own modules, so I can just point them to ST Cube and give them setup instructions for importing a sample project. Years ago I did another somewhat similar product which had a £400+ compiler kit but today the customer acceptance of that is zero.