Skip to content

Instantly share code, notes, and snippets.

@jstaursky
Last active January 16, 2021 19:18
Show Gist options
  • Save jstaursky/e29160c3749cd6d388e81c3ba86866f4 to your computer and use it in GitHub Desktop.
Save jstaursky/e29160c3749cd6d388e81c3ba86866f4 to your computer and use it in GitHub Desktop.
IA32, 32-bit, x86

C lang program w/out glibc

SYSV ABI requirement

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.

ELF binary file format

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.).

glibc _start code replacement

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");
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment