0

I'm having problems with various print commands.
Everytime I'm trying to call printf() my system just hangs or sometimes resets.
I have working UART and I can print into my console with UART_PutChar() just fine.
fprintf works in simple cases of just printing plain string fprintf(stdout, "test\n");
however formatted strings will hang my system fprintf(stdout, "test %d\n", 1);
Hangs also occur when I'm trying to print data from .data section

char* dataString = "test\n\0";

int main(){
     fprintf(stdout, dataString);
}

printf will work when I print newlines printf("\n");
printf will only print newlines if I do something like

fputc('B', stdin); //notice stdin
printf("test\n"); //prints newline

In some cases when fprintf fails it will return EOF (cannot remember them now, will provide them tomorrow if it becomes relevant)
(f)printf seems to be calling putchar and I've retargeted it to UART.
Curiously I think it should be calling _write or _write_r routines provided by newlib-nano (and printf is not calling them).
As IDE i'm using EmBitz and toolchain provided by it (arm-none-eabi). CPU I'm using is at91sam7x128. I'm unable to debug my program with JTAG so i need to try only use UART for debugging.

char* dataSection = "data\n\0";
char* dataSingle = "A";
int bssSection = 0;

int main(){
    fprintf(stdout, "plain\n"); //works
    fprintf(stdout, dataSingle); //works
    fprintf(stdout, *(char*)(bssSection + 0x41)); //prints A, works
    printf("\n"); //works
    fprintf(stdout, "%d", 1); //hangs
    fprintf(stdout, dataSection); //hangs
    printf("plain printf\n"); //hangs
}

UPDATE starter script: SAM7.s

/*********************************************************************
*
*       Defines, used for the processor status register
*
**********************************************************************
*/
        ARM_MODE_USER  = 0x10        /* Normal User Mode                             */
        ARM_MODE_FIQ   = 0x11        /* FIQ Fast Interrupts Mode                     */
        ARM_MODE_IRQ   = 0x12        /* IRQ Standard Interrupts Mode                 */
        ARM_MODE_SVC   = 0x13        /* Supervisor Interrupts Mode                   */
        ARM_MODE_ABORT = 0x17        /* Abort Processing memory Faults Mode          */
        ARM_MODE_UNDEF = 0x1B        /* Undefined Instructions Mode                  */
        ARM_MODE_SYS   = 0x1F        /* System Running in Priviledged Operating Mode */
        ARM_MODE_MASK  = 0x1F

        I_BIT          = 0x80        /* Disables IRQ when I bit is set               */
        F_BIT          = 0x40        /* Disables FIQ when F bit is set               */  

/*********************************************************************
*
*       Vector table
*
**********************************************************************
*/
        .text
        .global  __vector
        .global  _exit

        .extern  Reset_Handler

        .arm
        .section .vectors, "ax"

__vector:
        ldr     pc,Reset_Addr   /* RESET                 vector */


Reset_Addr:     .word   Reset_Handler

__vector_end:

/*********************************************************************
*
*       Standard C (crt0) initialization function
*
**********************************************************************
*/
        .global OS_GetStackInfo
        .extern  __low_level_init
        .extern  main

crt0:
        /*
         * Call __low_level_init to initiliaze hardware
         * before calling c-standard startup
         */
        ldr     r0,=__low_level_init
        mov     lr, pc
        bx      r0
        /*
         * Relocate .data section
         * (Copy from ROM to RAM)
         */
        ldr   r1, =_etext
        ldr   r2, =_data
        ldr   r3, =_edata
LoopRel:
        cmp   r2, r3
        ldrlo r0, [r1], #4
        strlo r0, [r2], #4
        blo   LoopRel

        /*
         * Clear .bss section
         */
        ldr   r1, =__bss_start__
        ldr   r2, =__bss_end__
        ldr   r3, =0
bss_clear_loop:
        cmp   r1, r2
        strne r3, [r1], #+4
        bne   bss_clear_loop
        /*
         *  Prepare and call main()
         */
        mrs   r0, cpsr
        bic   r0, r0, #(I_BIT | F_BIT)     /* Enable FIQ and IRQ interrupt */
        msr   cpsr, r0
        mov   r0, #0                         /* No arguments are passed to main */
        mov   r1, #0
        ldr   r2, =main
        mov   lr, pc
        bx    r2
_exit:  b     _exit                          /* We should never come to here, just for sureness. */

/*********************************************************************
*
*       __low_level_init
*
**********************************************************************
*/
__low_level_init:
        bx lr
        .weak __low_level_init

/**********************************************************************
* Reset_Handler
*
* Execution starts here.
* After a reset, the mode is ARM, Supervisor, interrupts disabled.
*/
        .global  Reset_Handler
        .global  end
        .arm
        .section .text, "ax"

Reset_Handler:
        /*
         * Setup a stack for each mode
         */
        msr   CPSR_c, #ARM_MODE_UNDEF | I_BIT | F_BIT   /* Undefined Instruction Mode */
        ldr   sp, =__stack_und_end__

        msr   CPSR_c, #ARM_MODE_ABORT | I_BIT | F_BIT   /* Abort Mode */
        ldr   sp, =__stack_abt_end__

        msr   CPSR_c, #ARM_MODE_FIQ   | I_BIT | F_BIT   /* FIQ Mode */
        ldr   sp, =__stack_fiq_end__

        msr   CPSR_c, #ARM_MODE_IRQ   | I_BIT | F_BIT   /* IRQ Mode */
        ldr   sp, =__stack_irq_end__

        msr   CPSR_c, #ARM_MODE_SVC   | I_BIT | F_BIT   /* Supervisor Mode */
        ldr   sp, =__stack_svc_end__

        /*
         * Now enter crt0 function,
         * which does low-level and segment initialization.
         * and then calls main().
         */
        ldr   r0, =crt0
        mov   lr, pc
        bx    r0
end:    b     end
        .end

Linker script

ENTRY(__vector)

/*********************************************************************
*
*       Define stack sizes here
*/

FIQ_STACK_SIZE = 0x0;
IRQ_STACK_SIZE = 0x1000;
ABT_STACK_SIZE = 0x0;
UND_STACK_SIZE = 0x0;
SVC_STACK_SIZE = 0x1000;

MEMORY
{
  RAM   (wx)  : ORIGIN = 0x200000, LENGTH = 0x8000
  FLASH (rx)  : ORIGIN = 0x100000, LENGTH = 0x10000
}

SECTIONS
{



  .text :
  {
    *(.vectors);
    . = ALIGN(8);
    *(.init);
    . = ALIGN(8);
    *(.text);
    . = ALIGN(8);
    *(.rodata);
    . = ALIGN(8);
    *(.rodata*);
    . = ALIGN(8);
    *(.glue_7t);
    . = ALIGN(8);
    *(.glue_7);
    . = ALIGN(8);
    etext = .;
  } > FLASH


  . = ALIGN(8);
  _etext = . ;
  PROVIDE (etext = .);

  .data : AT (_etext)
  {
    PROVIDE (__data_start__ = .);
    _data = . ;
    *(.data)
    . = ALIGN(8);
    PROVIDE (__data_end__ = .);
  } > RAM

  . = ALIGN(8);
  _edata = . ;
  PROVIDE (edata = .);

  .bss :
  {
    PROVIDE (__bss_start__ = .);
    *(.bss)
    *(COMMON)
    . = ALIGN(8);
    PROVIDE (__bss_end__ = .);

    . = ALIGN(256);

    PROVIDE (__stack_start__ = .);

    PROVIDE (__stack_fiq_start__ = .);
    . += FIQ_STACK_SIZE;
    . = ALIGN(8);
    PROVIDE (__stack_fiq_end__ = .);

    PROVIDE (__stack_irq_start__ = .);
    . += IRQ_STACK_SIZE;
    . = ALIGN(8);
    PROVIDE (__stack_irq_end__ = .);

    PROVIDE (__stack_abt_start__ = .);
    . += ABT_STACK_SIZE;
    . = ALIGN(8);
    PROVIDE (__stack_abt_end__ = .);

    PROVIDE (__stack_und_start__ = .);
    . += UND_STACK_SIZE;
    . = ALIGN(8);
    PROVIDE (__stack_und_end__ = .);

    PROVIDE (__stack_svc_start__ = .);
    PROVIDE (__stack_svc_end__ = .);
    PROVIDE (__stack_end__ = .);
    PROVIDE (__heap_start__ = .);
    . += 0x1000;
    . = ALIGN(8);
    PROVIDE (__heap_end__ = .);
  } > RAM
}

UPDATE 2 My quick and dirty reimplementations of syscalls.

int _write(int fd, char* buf, int len){
    LED_On(1);
    while(*buf){
        UART_PutChar(*buf++);
    }
    return len;
}

void _ttywrch(int ch) {
    LED_On(1);
    UART_PutChar(ch);
}

signed int
putchar(signed int c)
{
   return fputc(c, stdout);
}

signed int
fputs(const char* pStr, FILE* pStream)
{
   signed int num = 0;

   while (*pStr != 0)
   {
      if (fputc(*pStr, pStream) == -1)
      {
         return -1;
      }

      num++;
      pStr++;
   }

   return num;
}

int
fputc(int c, FILE* pStream)
{
   if ((pStream == stdout) || (pStream == stderr))
   {
#ifdef UART_CONSOLE_CRLF
      if (c == '\n')
         UART_PutChar('\r');
#endif

      UART_PutChar(c);

      return c;
   }
   else
   {
      return EOF;
   }
}
TuKeZ
  • 11
  • 4
  • 2
    In my experience printf() can use a lot of stack space. Check for a stack overflow or just increase the stack size and see if things start working. – kkrambo Jun 01 '16 at 16:41
  • I have 4KB of stack and heap – TuKeZ Jun 01 '16 at 17:03
  • That is why many uCompilers omit the `printf` family unless specifically activated. 4KB is tiny. Can you use `itoa` etc and format the output yourself? – Weather Vane Jun 01 '16 at 18:38
  • 1
    Is that 4K total for heap and stack or a 4K stack and a 4K heap? `printf` does not use heap memory - what is the stack size? What C library are you using? I have seen `printf` implementation use that much stack on their own, then you need to consider the stack used by your own code as well. The simple test is to increase the stack size and see if the problem goes away - then consider whether you can actually afford that much stack and the overhead of `printf`. Consider other impelementations such as [Tiny Printf](http://www.sparetimelabs.com/tinyprintf/tinyprintf.php). – Clifford Jun 01 '16 at 18:43
  • @Clifford I'm only using prints for debugging so that won't be a problem. It's 4k each, i'm using newlib-nano for linking. Increasing stack size haven't solved my problem. – TuKeZ Jun 01 '16 at 18:48
  • 1
    I avoid printf like the plague in bare metal. have routines to print numbers in hex that take like 20 lines of C, can add a homebrew printf on top of that that is a couple hundred lines but doesnt require the massive backend that a generic C library printf requires. the biggest bloat should be from the first printf, the subsequent one not that much. maybe the division is getting you by printing in decimal? – old_timer Jun 01 '16 at 19:18
  • One would imagine that the newlib-nano code is correct, and the UART code you say is good, so that leaves the syscalls `_write()` stub. Is that correct? – Clifford Jun 01 '16 at 19:48
  • @dwelch I'm not using floats and I don't even need them, I'm fairly sure that they are configured in my newlib. – TuKeZ Jun 02 '16 at 06:14
  • @Clifford Yes, problem is that it is never called. – TuKeZ Jun 02 '16 at 06:15
  • because printf supports %f it has to link in a floating point library among other things. It is very bulky. Now if you compiled that out of the library somehow, well that is fine, it is still bulky. Did you try printf with hex instead of decimal to see if it is a gcclib thing vs a printf thing? – old_timer Jun 02 '16 at 12:13
  • the system backend for newlib is relatively small, I assume you have completely implemented something there? when using newlib just to get printf, I cut out all the meat and have each function return pass. For printf to work stdout or otherwise, there is a function that it calls to confirm it is a tty out or it does an open or something, then eventually it calls write. you could use your raw uart output function to print characters to see what is leading up to the printf (if you think it is a printf thing). – old_timer Jun 02 '16 at 12:18
  • If you are using newlib just to get at printf, consider the tiny printf or others like it that Clifford linked to. – old_timer Jun 02 '16 at 12:20
  • @dwelch Nope, cannot print hex either. All funcs I've reimplemented are `fputc` `fputs` `putchar` `_write` `_ttywrch`. tiny printf certainly solves my problem. As a side note, I did make some progress. It does seem to be newlib-nano problem as i cannot use my prints with it. However different version lets me (non nano version). – TuKeZ Jun 02 '16 at 12:45

2 Answers2

2

Your signed putchar() implementation is very broken. These syscalls are intended to be the lowest level I/O routines used by higher level libraries such as stdio. You cannot implement them using stdio! You have no doubt caused a recursive call that will not output anything and will also quickly overflow the stack - thereafter anything could happen, but nothing good.

int putchar( signed int c )
{
   UART_PutChar( c ) ;
}

Your _write() implementation is also unsafe and semantically incorrect. It assumes that buf is a nul terminated string, which need never be the case - you should use the len argument to output exactly what the caller requests:

int _write( int fd, char* buf, int len )
{
    fd = fd ;

    for( int i = 0; i < len; i++ )
    {
        UART_PutChar( buf[i] );
    }

    return i ;
}

The reason that _write() is never called is because you have circumvented it by reimplementing fputs() and fputc() - overriding the newlib implementations that depend only on the syscalls.

You have to realise that these routines are the foundations that the library relies on to work correctly - "quick & dirty" is not good enough; safe, simple and semantically precise is what you should be aiming for.

Clifford
  • 88,407
  • 13
  • 85
  • 165
  • True, i was ignorant to not to make `write` correct the first time. I didn't know that put char should be implemented like that. However after making the fixes you suggested and removinf `fputc` and `fputs` it's still now working. – TuKeZ Jun 03 '16 at 09:43
  • @TuKeZ : Thinking about it, you should remove `putchar()` too; only the syscalls stubs should be re-implemented - at least until you have it working. – Clifford Jun 03 '16 at 12:47
0

printf() on this type of embedded system typically requires a fair amount of support. In particular, there needs to be a _write() function implemented, but your successful output of simple strings suggests that exists and works.

Rather, your issue looks like it probably comes about in the situations where printf() needs to allocate memory. Through a long chain of dependencies, this can end up going through a simple malloc() and ultimately invoking your implementations of

void *_sbrk(int increment)

to increase the heap size. If you don't have an implementation of that, or if it fails or collides with other areas, it could readily explain why your failure occurs only on outputs that require some piecing together to generate.

Chris Stratton
  • 39,853
  • 6
  • 84
  • 117
  • I'll test this solution today, but _write() isn't ever called, only putchar is – TuKeZ Jun 02 '16 at 03:50
  • @TuKeZ : I would have been surprised if printf() in Newlib requres `malloc()`, but it may be the case. I would simplify the test and initially make sure that `malloc()` works on its own first. Without JTAG how do you know `_write()` is not called? – Clifford Jun 02 '16 at 11:53
  • @Clifford to be 100% I don't. I just put LED_On func there and UART call trying to print something if it ever was called. Not a led tuned on or UART put anything into console – TuKeZ Jun 02 '16 at 12:36
  • _write() might not be used if stream output is partially jumpered direct to hardware as in the edit, though that raises some additional concerns. – Chris Stratton Jun 02 '16 at 13:21