2

I would like to create a build of my embedded C code which specifically checks that floating point operations aren't introduced into it by accident. I've tried adding +nofp to my [cortex-m3] processor architecture but GCC for ARM doesn't like that (probably because the cortex-m3 doesn't have a floating point unit). I've tried specifying -mfpu=none but that isn't a permitted option. I've tried leaving -lm off the linker command-line but the linker seems too clever to be fooled by that and is compiling code with double in it and resolving pow() anyway.

This post: https://gcc.gnu.org/legacy-ml/gcc-help/2011-07/msg00093.html from 2011 hints that GCC has no such option, since no-one is interested in it, which surprises me as it seems like a common thing to want, at least from an embedded standpoint, to avoid accidental C-library bloat.

Does anyone know of a way to do this with GCC/newlib without me having to go through and manually hack stuff out of the C library file it chooses?

Rob
  • 865
  • 1
  • 8
  • 21
  • C library bloat is not really the most significant issue with software floating point in targets with no FPU. Performance and non-deterministic timing are generally more significant. Generally however, you should just ask the question without presenting your justification. – Clifford Mar 02 '21 at 18:32
  • It is a bloat in my particular case: I'm not doing anything real time but in only 128 kbytes of code-space my core code is ~90 kbytes of which approximately half is the C library with the floating point versions, then adding my unit tests breaches the 128 kbyte boundary. I'd quite like them back :-). – Rob Mar 02 '21 at 18:38
  • Is it floating point math operations that is causing the bloat of the floating-point formatted I/O support? If you don't perform any fp math operations, no fp math library code should be linked. Newlib supports stdio-like functions without floating point such as `iprintf()` or you can rebuild the library with `#undef FLOATING_POINT` in every header that defines it. You cannot remove fp support from `scanf()` however. – Clifford Mar 02 '21 at 19:11
  • Yes, I think so: I'm using `iprintf()`, `sniprintf()`, `siscanf()`, `viprintf()`, have checked for `double` and `%f` (just in case) and occurrences of `math.h`, and have removed the few instances of `pow()`, yet I can't get the flash size to drop. If I could only slap a `nofp` on it somehow it would tell me where I'm going wrong and I could keep it that way. Would like to do this using the built versions of newlib that come with ARM GCC but maybe that's just not possible. – Rob Mar 02 '21 at 20:14
  • the technique suggested in my answer will take you less time than it took you to write your question. Moreover the map file will tell what is taking space, I case it if not floating point support as you suspect. You can also use it to check for printer etc. and is much simpler than searching the codebase. – Clifford Mar 02 '21 at 20:19
  • You'll have to roll out your own version of `pow` unfortunately. The C standard library is dysfunctional enough to not provide a fixed point version of it. Long as there's floating point present in the code, then typically sw floating point libs will get linked. – Lundin Mar 03 '21 at 09:40

1 Answers1

3

It is not just a library issue. Your target will use soft-fp, and the compiler will supply floating point code to implement arithmetic operators regardless of the library.

The solution I generally apply is to scan the map file for instances of the compiler supplied floating-point routines. If your code is "fp clean" there will be no such references. The math library and any other code that perform floating-point arithmetic operations will use these operator implementations, so you only need look for these operator calls and can ignore the Newlib math library functions.

The internal soft-fp routines are listed at https://gcc.gnu.org/onlinedocs/gccint/Soft-float-library-routines.html. It is probably feasible to manually check the mapfile for fp symbols but you might write yourself a script or tool to scan the map file for these names to check your. The cross-reference section of the map file will list all modules these symbols are used in so you can use that to identify where the floating point code is used.

The Newlib stdio functions support floating-point by default. If your formatted I/O is limited to printf() you can use iprintf() instead or you can rebuild Newlib with FLOATING_POINT undefined to remove floating point support from all but scanf() (no idea why). You can then use the map file technique again to find "banned" formatted I/O functions (although these are likely to also use the floating point operator functions in any case, so you will already have spotted them indirectly).

An alternative is to use an alternative stdio library to override the Newlib versions. There are any number of "tiny printf" implementations available you could use. If you link such a library as object code or list its library ahead of Newlib in the link command, it will override the Newlib versions.

Clifford
  • 88,407
  • 13
  • 85
  • 165
  • Thanks for that: I've just looked in the map file for all the `__` functions and I can see that it is deciding to include the C library version `/lib/thumb/v7-m/nofp\libg_nano.a` but from that, despite the fact that I'm not calling `vfprintf()`, it is still bringing in `vfprintf()` (and `printf()` for that matter). Now I'm using `-ffunction-sections -fdata-sections -fno-strict-aliasing` to allow the linker to drop unused functions but I have this feeling that it won't do that for functions that come in through libraries. Which leads me back to having to modify `newlib` manually. – Rob Mar 02 '21 at 20:48
  • @Rob printf is implemented using vfprintf. The cross reference section will tell you what is calling printf. In general the granularity if the library is at the object module level. So if you reference some public symbol defined in the same module as printf then printf will necessarily be linked. However for that reason I think the library is as granular as possible with one public function per module. – Clifford Mar 02 '21 at 20:54
  • You would get a better answer if you posted your map file perhaps. The map file also shows the size of objects. How much are you actually going to save by eliminating printf? Don't hack the library yet, because your analysis may be incorrect. – Clifford Mar 02 '21 at 20:59
  • A very quick search down the cross-reference table of the `.map` file for the functions listed in https://gcc.gnu.org/onlinedocs/gccint/Soft-float-library-routines.html shows none but it _has_ shown me that `mbedtls` (which I need certain crypto functions) from is referencing `printf()`, probably because I haven't configured it properly. Searching through the cross-reference is definitely the right way to find out what's what. Will attach `.map` file... – Rob Mar 02 '21 at 21:04
  • Map file can be obtained from my One Drive here: https://1drv.ms/u/s!AqUHa_qo4gdB-Eh8XGAJ_BgiQMHv?e=4IpoZT. – Rob Mar 02 '21 at 21:11
  • @Rob I think it becomes a different question then. A new question rather than posting the map file here might be more appropriate. – Clifford Mar 02 '21 at 21:11
  • Agreed, you have given me the cross-reference technique which is what I needed. Thanks! – Rob Mar 02 '21 at 21:14
  • In the answer, you may redefine the internal *softfp* routines as labels with no code (in 'C', assembler, or linker file). This will cause a linker error when the libgcc symbols are introduced. This will prevent code like `i = j * 1.;` from building. https://www.ijs.si/software/snprintf/ provides a harness to build your own `printf` variants. This will avoid pulling in floating point math from a C library. – artless noise Mar 04 '21 at 09:58