Snprintf () prints trash floats using newlib nano

I am running an embedded system with a metal case with an ARM Cortex-M3 (STM32F205). When I try to use snprintf() with floating point numbers, for example:

 float f; f = 1.23; snprintf(s, 20, "%5.2f", f); 

I get garbage in s . The format seems to deserve attention, i.e. Trash is a well-formed string with numbers, a decimal point, and two trailing digits. However, if I repeat snprintf , the line may change between two calls.

Floating point math seems to work differently, and snprintf works with integers, like this:

 snprintf(s, 20, "%10d", 1234567); 

I am using the newlib-nano implementation with the -u _printf_float linker -u _printf_float . Compiler arm-none-eabi-gcc .

I have a strong suspicion of memory allocation problems, since integers are printed without any hiccups, but floating actions act as if they were damaged in the process. The printf family functions call malloc with floats, not integers.

The only piece of code that does not belong to newlib that I use in this context is my _sbrk() , which is required by malloc .

 caddr_t _sbrk(int incr) { extern char _Heap_Begin; // Defined by the linker. extern char _Heap_Limit; // Defined by the linker. static char* current_heap_end; char* current_block_address; // first allocation if (current_heap_end == 0) current_heap_end = &_Heap_Begin; current_block_address = current_heap_end; // increment and align to 4-octet border incr = (incr + 3) & (~3); current_heap_end += incr; // Overflow? if (current_heap_end > &_Heap_Limit) { errno = ENOMEM; current_heap_end = current_block_address; return (caddr_t) - 1; } return (caddr_t)current_block_address; } 

As far as I managed to track, this should work. Nobody seems to ever call it negative increments, but I guess this is due to the newlib malloc design. The only bit strange: the first call to _sbrk has a zero increment. (But it might just be malloc curiosity about the starting address of the heap.)

The stack should not collide with a heap, since for them about 60 kilobytes of RAM. The script compiler may be insane, but at least the heap and stack addresses seem correct.

+5
source share
2 answers

How can it happen that someone else is bitten by the same error, I am sending the answer to my question. However, it was a comment by @Notlikethat that suggested the correct answer.

This is a lesson. You will not steal. I borrowed the gcc linker script that came with the STMCubeMX code generator. Unfortunately, the script file and the startup file do not work.

Corresponding part of the source linker script:

 _estack = 0x2000ffff; 

and its copies in the start script:

 Reset_Handler: ldr sp, =_estack /* set stack pointer */ ... g_pfnVectors: .word _estack .word Reset_Handler ... 

The first position of the interrupt vector (at 0) should always point to the top vertex of the trigger. When the reset interrupt is reached, it also loads the stack pointer. (As far as I can tell, the latter is not needed, since HW reloads the SP from the 0th vector in any case before calling the reset handler.)

The Cortex-M stack pointer should always point to the last item on the stack. When starting up, there are no elements in the stack, and in this case, the pointer must point to the first address above the actual memory 0x020010000. With the original script builder, the stack pointer is set to 0x0200ffff, which actually leads to sp = 0x0200fffc (the hardware aligns according to the stack). After that, the stack shifts by 4.

I changed the linker script by removing the constant _estack definition and replacing it with _stacktop , as shown below. The definitions of memory were there before. I changed the name to see where the value is used.

 MEMORY { FLASH (rx) : ORIGIN = 0x8000000, LENGTH = 128K RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 64K } _stacktop = ORIGIN(RAM) + LENGTH(RAM); 

After that, the value of _stacktop is 0x20010000, and my numbers float perfectly ... The same problem can occur with any external (library) function using double-length parameters, since ARM Cortex ABI states that the stack should be aligned to 8 octets when calling external functions.

+9
source

snprintf takes size as the second argument. You can go through this example http://www.cplusplus.com/reference/cstdio/snprintf/

 /* snprintf example */ #include <stdio.h> int main () { char buffer [100]; int cx; cx = snprintf ( buffer, 100, "The half of %d is %d", 60, 60/2 ); snprintf ( buffer+cx, 100-cx, ", and the half of that is %d.", 60/2/2 ); puts (buffer); return 0; } 
+1
source

Source: https://habr.com/ru/post/1214251/


All Articles