0

The hello world program below (in pure standalone linux/x86 code) compiles fine but has an issue when the string "hello, world\n" is a local stack variable: In 64bit, there is no output, unless the local string declaration is preceded with the keyword static; but in 32bit that keyword is not needed! Why?

Edit:

This seems to be an inconsistency between 32bit and 64bit inline asm!

As pointed out in the other link, the problem was that 64bit system calls are different than 32bit, and has to be called very differently than done in the code below with int 0x80 (which only works for 32bit).

(Of course, there is no issue if the string is made a global/extern in data memory.)

Verified with gcc 5.4.0 and 4.8.4. See compilation command line in code comment.

/*
Standalone linux/x86 hello world program.  Compile in 32bit or 64bit with:

    gcc -m32|-m64 -Wall -nostdlib -e main prog.c

Or separate compile/assemble/link:

    32bit:
    gcc -m32 -Wall -nostdlib -S -o prog.s prog.c
    as -32 -o prog.o prog.s
    ld -melf_i386 -e main -nostdlib -o a.out prog.o

    64bit:
    gcc -m64 -Wall -nostdlib -S -o prog.s prog.c
    as -64 -o prog.o prog.s
    ld -melf_x86_64 -e main -nostdlib -o a.out prog.o
*/
void main(void) {
    unsigned long snum, fd, len, exitcode;

    /* In 64bit, the program gives no output unless the local declaration
       below has the keyword static, but in 32bit it works fine without it.
       Why??  (If the string is made global (data section) then it works
       in both 32bit and 64bit.)  Verified with gcc 5.4.0 and gcc 4.8.4.
    */
    char str[] = "hello, world\n";

    snum=4; fd=1; len=13;
    asm volatile (  /* Syscall write */
        "int $0x80\n\t" : : "a" (snum), "b" (fd), "c" (str), "d" (len));
    snum=1; exitcode=0;
    asm volatile (  /* Syscall exit*/
        "int $0x80\n\t" : : "a" (snum), "b" (exitcode));
}
GDGP
  • 31
  • 2
  • Read more about [crt0](https://en.wikipedia.org/wiki/Crt0) and study its implementation. It is more complex than what you believe. Also **edit your question** to improve it, and give a *lot* more motivation. Why do you use `-ffreestanding` for a program which is not freestanding, but is some Linux executable in user-land? Freestanding programs are "kernel like". Read also [ld-linux.so(8)](http://man7.org/linux/man-pages/man8/ld-linux.8.html), [elf(5)](http://man7.org/linux/man-pages/man5/elf.5.html) and [execve(2)](http://man7.org/linux/man-pages/man2/execve.2.html). Your program has no sense! – Basile Starynkevitch Dec 25 '17 at 08:08
  • @BasileStarynkevitch: I edited and dropped the `-ffreestanding` as you suggested. And I am already quite familiar with crt0, ELF, etc, thank you. Did you actually read my post? Since you seem to have missed the point entirely: This is really about **an inconsistency between 32bit and 64bit behavior.** If you really know why this is happening, may be you can point me to the specific reason. I looked at the disassembly (via `objdump -d`) and could not find it: The string gets loaded onto the stack in both cases, so I am probably missing something. Maybe some one can spot the difference ... – GDGP Dec 25 '17 at 08:57
  • 1
    My point is that using the dynamic linker on freestanding code is non-sense (and "undefined behavior"). The dynamic linker `ld-linux.so` is related to `crt0`. So if you compile freestanding code you should not link dynamically. The fact that it *apparently* works on 32 bits is just bad luck. And `crt0` is required by code compiled by `gcc` (unless in freestanding code, then dynamic linking has no sense) – Basile Starynkevitch Dec 25 '17 at 09:05
  • You should motivate your question. It would be very useful for us to understand what you are actually trying to do and why ! – Basile Starynkevitch Dec 25 '17 at 09:15
  • 2
    Yes, I agree that this is certainly a duplicate of the other link that I missed in my searches. Thanks for pointing that link out, which was much more helpful than general information about crt0, ELF, etc, which I am already familiar with past 32bit experience. I am very new to 64bit, and naively assumed that linux will be backwards compatible with system calls via int 0x80. @BasileStarynkevitch: I disagree though that 32bit was just bad luck; the linker was not the real problem, since the linker was used essentially to add ELF headers. The syscall was the real problem. – GDGP Dec 25 '17 at 09:30
  • No, you use GCC & `ld-linux.so` in a way it is not designed to work together. So if it works, it is bad luck. In fact, it is [undefined behavior](https://en.wikipedia.org/wiki/Undefined_behavior). Notably, `ld-linux` is using some environment variables (notably, but not only, `LD_LIBRARY_PATH`) which are set by `crt0` and which remain unset in your program. This is UB and you should not do that. – Basile Starynkevitch Dec 25 '17 at 09:50
  • More precisely `crt0` is transmitting environment variables to `ld-linux`, and you don't have any code related to that task – Basile Starynkevitch Dec 25 '17 at 09:57
  • If you look at the command line where I have compile/assembly/link pipeline separated (`gcc -S` is not calling the assembler or the linker), you will probably see what I mean. Things like `crt0`, `ld-linux.so` do not play any role here, and in fact I can run the final `ld` command with environment set to `LD_LIBRARY_PATH=""` There is nothing really here for the linker to link with, the linker is essentially only adding ELF/program headers. – GDGP Dec 25 '17 at 10:14
  • the `int 0x80` system calls backward compatibility is in many common 64b linux installations (NOT in windows subsystem linux), but you are producing elf64, so there's no reason for kernel to treat your binary in some compatibility mode, as the binary format is 64b only. The `int 0x80` may still work to some extent on common 64b linux, but YMMV, like you hit the problem with stack area yourself, and on pure 64b kernel it would fail completely, even with static/global variables. – Ped7g Dec 25 '17 at 11:21
  • @Ped7g: Thanks, your comment pretty much answers my question. As I found out, even in 64bit linux providing backwards compatibility for elf64 executables, the `int 0x80` backward compatibility does not work with the stack (since the stack pointer has significant bits set at bit positions greater than 32), but it often works with text/data memory locations (since under default conditions these memory locations are effectively 32bit). – GDGP Dec 26 '17 at 16:54

0 Answers0