See http://www.sco.com/developers/devspecs/abi386-4.pdf page 54 for diagram. This tells us 1 implementation of the stack layout of a SYSV compliant system (At the moment the only one I am aware of). However the spec. mentions this to only be a potiential implementation that is complient. To quote,
Argument strings, environment strings, and the auxiliary information appear in no specific order within the information block; the system makes no guarantees about their arrangement
As well as,
When a process receives control, its stack holds the arguments and environment from exec(BA_OS). (* shows diagram *)
Which on linux corresponds to execve
(for proof just run strace ./<program> <args>
)
I have not delved into the full details on how execve
is implemented however it appears to pass on control to
the program with a stack organized in the same way found in the diagram.
The document also details the CDECL calling convention that C programs follow and this gives another reason why a given SYSV system would hold a stack sim. to that given in the diagram.
See https://refspecs.linuxfoundation.org/elf/elf.pdf page 19 (section 1-5) for entry on e_entry
.
which is the entry corresponding to the linker symbol _start
(the name “_start” idk is standardized but is what the gcc linker uses/requires, and what you will find in crt1.o
).
To quote from the elf spec this symbol does the following.
… gives the virtual address to which the system first transfers control, thus starting the process.
Thus between these information references have learned a likely layout of stack upon program startup for SYSV systems and where to find the startup address for given ELF program (albeit this is information assumes the C program was compiled using the Absolute object model (see pg.60 of SYSV spec.).
Here is a small example program that does not rely on glibc
/*
gcc -m32 -march=i386 -fno-pie -no-pie -fno-pic -fno-plt \
-static -mpreferred-stack-boundary=2 -mstackrealign -fno-stack-protector \
-nostdlib -nostartfiles -masm=intel -fno-ident \
-fno-omit-frame-pointer -fno-exceptions \
-fno-asynchronous-unwind-tables -fno-unwind-tables -mno-red-zone \
-Wa,--noexecstack -z noexecstack \
example.c \
-Wl,--gc-sections,--build-id=none,-Ttext=0x12345678 -Os -ffunction-sections \
-o example
*/
asm (".intel_syntax noprefix");
// NOTE!
// SYSV i386 ABI diagram shows an initial stack layout of,
// argc (top of stack)
// *argv
// but 'main' needs
// argc (top of stack)
// argv
asm ( /* Assembly function body */
".globl _start" "\n\t"
"_start:" "\n\t"
"lea eax, [esp+4]" "\n\t" // setup for argv
"mov ebx, [esp]" "\n\t"
"push eax" "\n\t" // <- argv
"push ebx" "\n\t" // <- argc (now atop of stack)
"call main" "\n\t"
"ret" "\n\t"
);
int main(int argc, char **argv) {
asm volatile("mov eax, 0x1 \n\t" // Exit program without segfault.
"mov ebx, %[result] \n\t" // Programs return value (just the value of argc).
"int 0x80" :: [result] "g" (argc) : "eax");
asm(".att_syntax noprefix");
}