$ touch a.c
$ gcc -c a.c
$ ld a.o
ld: warning: cannot find entry symbol _start; defaulting to 0000000000400078
gcc -nostdlib ./empty.c -o ./empty
Edit: This one actually runs correctly:
$ touch empty.c
$ gcc -static -nostartfiles ./empty.c -e_exit -o ./empty
$ ./empty && echo $?
$ rm -rf a
$ cp a.c a
$ chmod +x a
zsh: exec format error: ./a
The file is marked as executable, so the shell very reasonably tries to execute it by calling some well-chosen member of the exec() family (http://linux.die.net/man/3/exec).
The exec() function then needs to open and parse the file according to the formats it supports, which of course fails since the file is empty.
Do you simply mean that you expected the shell to validate this, and not try to execute empty files?
In Seventh Edition UNIX, /bin/true is an empty file; it is a shell script that succeeds at does nothing.
Some later commercial UNIXes are noted to have /bin/true contain nothing but comments containing a copyright notice for that nothing.
This was altered for 2008 to "A file that contains characters organized into zero or more lines."
The 2008 version is actually broken, since it contradicts itself -- a file cannot "contain characters" on zero lines.
I disagree. To me this doesn't mean that a file "contains at least one character", but that files are containers and their contained values are characters. Like most containers in computer science, the set of contained values can be empty, but it's still meaningful to say that it's a container that "contains characters".
I understand what happens here and why there is an error message in zsh, but I'm surprised by the fact that bash does not signal the error (exec returns -1, after all).
Bash includes logic to parse ELF, so I guess that after exec fails it tries to parse the file and has a special case for empty files.
$ ld a.o
ld: warning: -macosx_version_min not specified, assuming 10.7
Undefined symbols for architecture x86_64:
"start", referenced from:
implicit entry/start for main executable
ld: symbol(s) not found for inferred architecture x86_64
gcc -nostdlib ./empty.c -e0 -o ./empty
$ ld a.o -U _main -U start