Temporary Workaround
I worked around it with tiny printf:
Probably newlib printf is just taking too much memory. The PC changes to some strange things after this, which in theory should be the end of a array char[100]
.
cp = buf + BUF
Later it tries to execute
*--cp = something
and it crashes. Memory bug? There are many things I don't understand. For example, I am not sure whether the linker script is correct, or are the syscall functions. Before I can get further understanding, I have to stick with the tiny printf.
Original
I have an STM32F103RB board (Nucleo), and I just got USART1 working. Also, I tested Newlib functions, and puts()
works as expected. However, when I try to use printf()
for integers, like this:
printf("ADC: %d\r\n", test);
If test < 10
the program works, but if test >= 10
a hard fault is generated. After some GDB debugging I found that it generates from vfprintf:
#0 HardFault_Handler () at main.c:136
#1 <signal handler called>
#2 0x08005af4 in _vfprintf_r (data=<optimized out>, fp=0x20000384 <impure_data+852>, fmt0=fmt0@entry=0x20004f57 " \254\264", ap=...,
ap@entry=...) at ../../../../../../newlib/libc/stdio/vfprintf.c:1601
#3 0x08004fd0 in printf (fmt=0x800b4ac "ADC: %d\n\r") at ../../../../../../newlib/libc/stdio/printf.c:52
#4 0x080004e8 in main () at main.c:168
I cannot think of any solutions. This is my _sbrk()
:
caddr_t _sbrk(int nbytes) {
static caddr_t heap_ptr = NULL;
caddr_t base;
if (heap_ptr == NULL){
heap_ptr = (caddr_t) &_end;
}
if ((caddr_t) _stackend > heap_ptr + nbytes) {
base = heap_ptr;
heap_ptr += nbytes;
return (base);
}
else {
errno = ENOMEM;
return ((caddr_t) -1);
}
}
My minimum stack size is configured to be 1024 bytes. I tried to increase until 10k, but it still generates this hard fault. I have no idea how to debug this or find the problem. How can I fix it?
I noticed one thing. The failure comes at vfprintf.c:1601
, where I examined the pointer cp
in GDB:
(gdb) x 0x20004f57
0x20004f57: 0x00b4ac20
(gdb) x 0x00b4ac20
0xb4ac20: 0x20004f80
(gdb) x 0x20004f58
0x20004f58: 0x0800b4ac
(gdb)
I don't know why the address 0x20004f57
points to a non-existent address.
Also, p _stackend
gives
$6 = (caddr_t) 0xb33ea8a6 <error: Cannot access memory at address 0xb33ea8a6>
Which apparently does not exist
It seems that when _VFPRINTF_R()
is executed, while the goto number
is executed, the pointer cp
is corrupted:
3: cp = <optimized out>
2: fmt = 0x800b453 "\n\r"
(gdb) n
1057 base = DEC;
3: cp = <optimized out>
2: fmt = 0x800b453 "\n\r"
(gdb)
1400 number: if ((dprec = prec) >= 0)
3: cp = <optimized out>
2: fmt = 0x800b453 "\n\r"
(gdb)
1409 if (_uquad != 0 || prec != 0) {
3: cp = 0x20004b58 "\245ۊ\256\211W{\325\326\377Y\352\224\t x\207\220\230&-\031\032~\337\032\371\024\254\"(\214\354\363\b\241\365\022\035\037\252\026\243\206\235P\005OZn\245c\n\352\244E^ά\246\301Ӕ\271L\264"
2: fmt = 0x800b453 "\n\r"
(gdb)
The Linker Script:
/*
Linker script for STM32F10x_128K_20K
modified from
http://www.codesourcery.com/archives/arm-gnu/msg02972.html
http://communities.mentor.com/community/cs/archives/arm-gnu/msg02972.html
*/
/*
There will be a link error if there is not this amount of RAM free at the
end.
*/
/* _Minimum_Stack_Size = 256; */
_Minimum_Stack_Size = 1024;
ENTRY(Reset_Handler)
/* Memory Spaces Definitions */
MEMORY
{
RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 20K
FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 128K
}
__ram_start__ = ORIGIN(RAM);
__ram_size__ = LENGTH(RAM);
__ram_end__ = __ram_start__ + __ram_size__;
_estack = __ram_end__;
/* highest address of the user mode stack */
PROVIDE ( _Stack_Limit = _estack - _Minimum_Stack_Size );
/* Sections Definitions */
SECTIONS
{
.text :
{
KEEP(*(.isr_vector)) /* Startup code */
*(.text) /* code */
*(.text.*) /* remaining code */
*(.rodata) /* read-only data (constants) */
*(.rodata.*)
*(.glue_7)
*(.glue_7t)
*(.vfp11_veneer)
*(.v4_bx)
*(.ARM.extab* .gnu.linkonce.armextab.*)
} >FLASH
/* for exception handling/unwind - some Newlib functions (in
common with C++ and STDC++) use this. */
.ARM.extab :
{
*(.ARM.extab* .gnu.linkonce.armextab.*)
} > FLASH
__exidx_start = .;
.ARM.exidx :
{
*(.ARM.exidx* .gnu.linkonce.armexidx.*)
} > FLASH
__exidx_end = .;
. = ALIGN(4);
_etext = .;
/* This is used by the startup in order to initialize the .data secion
*/
_sidata = _etext;
/* This is the initialized data section
The program executes knowing that the data is in the RAM
but the loader puts the initial values in the FLASH (inidata).
It is one task of the startup to copy the initial values from FLASH to
RAM. */
.data : AT ( _sidata )
{
. = ALIGN(4);
/* This is used by the startup in order to initialize the .data
secion */
_sdata = . ;
*(.data)
*(.data.*)
. = ALIGN(4);
/* This is used by the startup in order to initialize the .data
secion */
_edata = . ;
} >RAM
/* This is the uninitialized data section */
.bss :
{
. = ALIGN(4);
/* This is used by the startup in order to initialize the .bss
secion */
_sbss = .;
__bss_start__ = _sbss;
*(.bss)
*(.bss.*)
*(COMMON)
. = ALIGN(4);
/* This is used by the startup in order to initialize the .bss
secion */
_ebss = . ;
__bss_end__ = _ebss;
} >RAM
PROVIDE ( end = _ebss );
PROVIDE ( _end = _ebss );
PROVIDE ( _exit = _ebss );
PROVIDE (_stackend = ORIGIN(RAM) + LENGTH(RAM) - _Minimum_Stack_Size);
/* This is the user stack section
This is just to check that there is enough RAM left for the User mode
stack
It should generate an error if it's full.
*/
._usrstack :
{
. = ALIGN(4);
_susrstack = . ;
. = . + _Minimum_Stack_Size ;
. = ALIGN(4);
_eusrstack = . ;
} >RAM
/* after that it's only debugging information. */
/* remove the debugging information from the standard libraries */
/*
DISCARD :
{
libc.a ( * )
libm.a ( * )
libgcc.a ( * )
}
*/
/* Stabs debugging sections. */
.stab 0 : { *(.stab) }
.stabstr 0 : { *(.stabstr) }
.stab.excl 0 : { *(.stab.excl) }
.stab.exclstr 0 : { *(.stab.exclstr) }
.stab.index 0 : { *(.stab.index) }
.stab.indexstr 0 : { *(.stab.indexstr) }
.comment 0 : { *(.comment) }
/* DWARF debug sections.
Symbols in the DWARF debugging sections are relative to the beginning
of the section so we begin them at 0. */
/* DWARF 1 */
.debug 0 : { *(.debug) }
.line 0 : { *(.line) }
/* GNU DWARF 1 extensions */
.debug_srcinfo 0 : { *(.debug_srcinfo) }
.debug_sfnames 0 : { *(.debug_sfnames) }
/* DWARF 1.1 and DWARF 2 */
.debug_aranges 0 : { *(.debug_aranges) }
.debug_pubnames 0 : { *(.debug_pubnames) }
/* DWARF 2 */
.debug_info 0 : { *(.debug_info .gnu.linkonce.wi.*) }
.debug_abbrev 0 : { *(.debug_abbrev) }
.debug_line 0 : { *(.debug_line) }
.debug_frame 0 : { *(.debug_frame) }
.debug_str 0 : { *(.debug_str) }
.debug_loc 0 : { *(.debug_loc) }
.debug_macinfo 0 : { *(.debug_macinfo) }
/* SGI/MIPS DWARF 2 extensions */
.debug_weaknames 0 : { *(.debug_weaknames) }
.debug_funcnames 0 : { *(.debug_funcnames) }
.debug_typenames 0 : { *(.debug_typenames) }
.debug_varnames 0 : { *(.debug_varnames) }
}
I didn't understand this linker script very well, and I modified the one that comes with "Discovering the STM32 Microcontroller".
OK, after all sorts of testing, I found that it was the toolchain I used. I built the toolchain myself using GCC 4.8, and according to http://forum.chibios.org/phpbb/viewtopic.php?f=16&t=2241 , GCC 4.8 has some problems. I switched back to CodeSourcery toolchain and it works again.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With