0

I was practicing a very simple stack using SASM and assembly language.

%include "io64.inc"
section .text
global CMAIN
CMAIN:
 mov rbp, rsp;
 push 378;
 pop rax;
 PRINT_DEC 1,ax
 xor rax, rax
 ret

When writing this code, if 378 was converted into bits, it would become 0001 0111 1010 and exceed 1 byte, so an error was expected, but 378 was printed normally. Why is this happening?

Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
  • 3
    _"it would become 0001 0111 1010 and exceed 1 byte, so an error was expected"_. Why? Where is this one-byte limit? And if there is such a limit, why are you expecting an error, rather than just, say, only the least significant byte of the value being printed? – Michael Jan 21 '22 at 08:52
  • PRINT_DEC 1,ax was thought to mean that ax would be printed in the size of 1 byte. But if 378 is in ax, it will be saved as 0000 0001 0111 1010. So, I thought it was impossible to print out the 2^9 part in 1 byte size. It seems to be my mistake not to expect only one byte portion to be printed. I should have considered not only errors but also 1 byte part printed. Since it is my first time using assembly language, I think I was very inexperienced. – 온새수옴 Jan 21 '22 at 11:46
  • Ok, well, from what I can tell it looks like the implementation of that `PRINT_DEC` macro will call `printf` with the format string `"%hhd"` in this particular case, and promote the second argument to 64-bit using `movsx rax,ax`. I'm not entirely sure what output that's supposed to generate (or if that's well-defined). Maybe someone who knows the C standard inside-out can answer. – Michael Jan 21 '22 at 16:26
  • @Michael: `printf("%hhd\n", 378)` works as if you passed `378 & 0xff` which is `122`. https://godbolt.org/z/Wzj5dd9fq. That's expected: in x86-64 System V, narrow args are allowed to have garbage in the high bytes. Of course, a variadic function has narrow args promoted to `int` in the caller. https://en.cppreference.com/w/c/io/fprintf is clear that `%c` converts the arg to `unsigned char` first. It's not explicit, but I think the description for `%hh` implies that printf will itself convert the arg to `signed char`, truncating it from `int`, before printing. – Peter Cordes Jan 21 '22 at 17:56
  • So `PRINT_DEC` must not be using `%hhd`, or else the C library on this platform works differently. (Windows x64 also allows high garbage in arg-passing registers, which callees must ignore. I'm guessing Windows from the CMAIN? But truncation to 8-bit or not has to happen within printf, because the actual C arg is an `int`, the minimum width for a variadic function). Anyway, one could single-step the asm and see what string the first arg points to. – Peter Cordes Jan 21 '22 at 17:59
  • (Oh, re-reading earlier comments, I guess it's the `1` arg to the macro that's supposed to make it 8-bit? I guess by choosing the format string? I guess the `movsx rax,ax` is from a generic macro that always sign-extends to a full register, and the call site here strangely chose 16-bit AX instead of some other width. Also strange that it's not into RDX as the 2nd arg for printf.) – Peter Cordes Jan 21 '22 at 18:02
  • @PeterCordes That C code in your godbolt snippet prints 378 if I build it with GCC 4.8.1 (x86_64-w64-mingw32). – Michael Jan 21 '22 at 18:07
  • @Michael: If `122` is required by ISO C, then this is probably another example of Microsoft's C library not being C99 compliant. Now that you mention it, I remember reading SO comments that MS's printf doesn't handle some format specifiers. – Peter Cordes Jan 21 '22 at 18:09
  • @Michael: Yup, [C program yielding different results on different machines despite using the same fixed length data types](https://stackoverflow.com/a/70594018) confirms that MSVCRT treats %hhd as %hd at least for scanf. Piece of garbage. – Peter Cordes Jan 21 '22 at 18:23
  • Now I think I understand why it is like that. Thank you so much, everyone. – 온새수옴 Jan 22 '22 at 11:45

0 Answers0