-static
is not the default even with -nostdlib
when GCC is configured to make PIEs by default. Use gcc -m32 -static -nostdlib
to get the historical behaviour. (-static
implies -no-pie
). See What's the difference between "statically linked" and "not a dynamic executable" from Linux ldd? for more.
Also, you may need to disable alignment of other sections with gcc -Wl,--nmagic
or using a custom linker script, and maybe disable extra sections of metadata that GCC adds. Minimal executable size now 10x larger after linking than 2 years ago, for tiny programs?
You probably don't have a .eh_frame
section if you're not linking any compiler-generated (from C) .o
files. But if you were, you can disable that with gcc -fno-asynchronous-unwind-tables
. (See also How to remove "noise" from GCC/clang assembly output? for general tips aimed at looking at the compiler's asm text output, moreso than executable size.)
See also GCC + LD + NDISASM = huge amount of assembler instructions (ndisasm doesn't handle metadata at all, only flat binary, so it "disassembles" metadata. So the answer there includes info on how to avoid other sections.)
GCC -Wl,--build-id=none
will avoid including a .note.gnu.build-id
section in the executable.
$ nasm -felf32 foo.asm
$ gcc -m32 -static -nostdlib -Wl,--build-id=none -Wl,--nmagic foo.o
$ ll a.out
-rwxr-xr-x 1 peter peter 488 Dec 26 18:47 a.out
$ strip a.out
$ ll a.out
-rwxr-xr-x 1 peter peter 248 Dec 26 18:47 a.out
(Tested on x86-64 Arch GNU/Linux, NASM 2.15.05, gcc 10.2, ld
from GNU Binutils 2.35.1.)
You can check on the sections in your executable with readelf -a a.out
(or use a more specific option to only get part of readelf
's large output.) e.g. before stripping,
$ readelf -S unstripped_a.out
...
Section Headers:
[Nr] Name Type Addr Off Size ES Flg Lk Inf Al
[ 0] NULL 00000000 000000 000000 00 0 0 0
[ 1] .text PROGBITS 08048060 000060 00000c 00 AX 0 0 16
[ 2] .symtab SYMTAB 00000000 00006c 000070 10 3 3 4
[ 3] .strtab STRTAB 00000000 0000dc 000021 00 0 0 1
[ 4] .shstrtab STRTAB 00000000 0000fd 000021 00 0 0 1
And BTW, you definitely do not want to use nasm -felf64
on a file that uses BITS 32
, unless you're writing a kernel or something that switches from 64-bit long mode to 32-bit compat mode. Putting 32-bit machine code in a 64-bit object file is not helpful. Only ever use BITS
when you want raw binary mode to work (later in that tiny-ELF tutorial). When you're making a .o
to link, it only makes it possible to shoot yourself in the foot; don't do it. (Although it's not harmful if you do properly use nasm -felf32
that matches your BITS directive.)