microzig icon indicating copy to clipboard operation
microzig copied to clipboard

printf functions don't format

Open Grazfather opened this issue 9 months ago • 1 comments

I am having a hard time tracking down exactly what the issue is, but it's easy to repro

#include <stdio.h>

char buf[0x10];

int main (void)
{
    // Prefill the buffer to show there IS a write
    buf[0] = 0x41;
    buf[1] = 0x41;
    buf[2] = 0x41;
    buf[3] = 0x41;
    buf[4] = 0x41;
    sprintf(buf, "aa%02X", 0x34);
}

avr-gcc -mmcu=atmega328 -Wall -Os -o blink3.elf blink.c

This should yield 61 61 33 34 00 in memory, but instead you get 00 41 41 41 41.

  0100:  00 00 00 00 00 00 00 00  00 41 41 41 41 00 00 00  .........AAAA...

(buf starts at 0x108 for me)

I think some check short circuits and the result length is determined to be 0, and it just writes a null byte there.

Grazfather avatar Apr 09 '25 17:04 Grazfather

Seems that the behaviour is different than I thought: It doesn't even write a null byte! Testing this more closely I noticed that #524 was throwing us off, since it was causing the sprintf call to use an empty string as the format string.

Now what I see suggests that sprintf is basically doing nothing:

  0100:  61 61 25 30 32 58 00 00  41 41 41 41 41 00 00 00  aa%02X..AAAAA...

Grazfather avatar May 04 '25 19:05 Grazfather

Better repro:

#include <stdarg.h>

char buf[16];

static inline void out_u8(unsigned char b) {
  asm volatile("out 1, %0" ::"r"(b));
}

int my_vsprintf(char *s, const char *format, va_list ap) {
  va_list aq;
  va_copy(aq, ap);
  int x = va_arg(aq, int);
  out_u8(x); // Should output 12
  x = va_arg(aq, int);
  out_u8(x); // Should output 34
  x = va_arg(aq, int);
  out_u8(x); // Should output 56
  return 0;
}

int my_sprintf(char *s, const char *format, ...) {
  va_list arg;
  int done;

  va_start(arg, format);
  done = my_vsprintf(s, format, arg);
  va_end(arg);

  return done;
}
int main(void) {
  // Prefill to show writes happen
  for (int i = 0; i < 5; i++) {
    buf[i] = 'A';
  }

  my_sprintf(buf, "aa%02X", 0x12, 0x34, 0x56, 0x78);

  asm volatile("out 0, %0" ::"r"((unsigned char)0));
  return 0;
}

This should output 12, 34, 56, but instead outputs 00, 00, 00

Grazfather avatar Sep 05 '25 12:09 Grazfather

Root caused: Nothing with our stack per-se, but rather the issue was that when using va_args, it causes the compiled to emit sty_ii instructions, which were not properly implemented.

Grazfather avatar Sep 08 '25 12:09 Grazfather