临时解决方法
我用小 printf 解决了这个问题:
可能 newlib printf 只是占用了太多内存。在这之后PC变成了一些奇怪的东西,理论上应该是数组的结尾char[100]
。
cp = buf + BUF
后来它尝试执行
*--cp = something
它崩溃了。内存错误?有很多事情我不明白。例如,我不确定链接描述文件是否正确,或者是否是系统调用函数。在进一步了解之前,我必须坚持使用微小的 printf。
原来的
我有一块 STM32F103RB 板(Nucleo),我刚刚让 USART1 工作。另外,我测试了 Newlib 函数,并按puts()
预期工作。但是,当我尝试使用printf()
整数时,如下所示:
printf("ADC: %d\r\n", test);
如果test < 10
程序有效,但如果test >= 10
生成硬故障。经过一些 GDB 调试后,我发现它是从 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
我想不出任何解决方案。这是我的_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);
}
}
我的最小堆栈大小配置为 1024 字节。我试图增加到 10k,但它仍然会产生这个硬故障。我不知道如何调试或找到问题。我该如何解决?
我注意到一件事。失败发生在vfprintf.c:1601
,我检查了cp
GDB 中的指针:
(gdb) x 0x20004f57
0x20004f57: 0x00b4ac20
(gdb) x 0x00b4ac20
0xb4ac20: 0x20004f80
(gdb) x 0x20004f58
0x20004f58: 0x0800b4ac
(gdb)
我不知道为什么地址0x20004f57
指向一个不存在的地址。
另外,p _stackend
给
$6 = (caddr_t) 0xb33ea8a6 <error: Cannot access memory at address 0xb33ea8a6>
显然不存在
似乎在_VFPRINTF_R()
执行时,在goto number
执行时,指针cp
已损坏:
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)
链接器脚本:
/*
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) }
}
对这个linker script不是很了解,修改了《Discovering the STM32 Microcontroller》附带的那个。