A parte l'ovvio (-Os -s
), l'allineamento delle funzioni al valore più piccolo possibile che non si bloccherà (non conosco i requisiti di allineamento ARM) potrebbe spremere alcuni byte per funzione.
-Os
dovrebbe disabilita già le funzioni di allineamento, ma questo potrebbe comunque avere un valore predefinito come 4 o 8. Se l'allineamento ad es. a 1 è possibile con ARM, che potrebbe risparmiare qualche byte.
-ffast-math
(o il meno abrasivo -fno-math-errno
) non imposterà errno ed eviterà alcuni controlli, il che riduce la dimensione del codice. Se, come la maggior parte delle persone, non leggi comunque errno, questa è un'opzione.
Usando correttamente __restrict
(o restrict
) e const
rimuove i carichi ridondanti, rendendo il codice più veloce e più piccolo (e più corretto). Contrassegnare correttamente le funzioni pure in quanto tali elimina le chiamate di funzione.
L'abilitazione di LTO può aiutare e, se non è disponibile, compilare tutti i file sorgente in un file binario in una volta sola (gcc foo.c bar.c baz.c -o program
invece di compilare foo.c
, bar.c
e baz.c
per obiettare prima i file e poi il collegamento) avrà un effetto simile. Rende tutto visibile all'ottimizzatore in una sola volta, possibilmente permettendogli di funzionare meglio.
-fdelete-null-pointer-checks
potrebbe essere un'opzione (nota che questo è normalmente abilitato con qualsiasi "O", ma non sui target incorporati).
Inserire variabili globali statiche (si spera di non averne così tante, ma comunque) in una struttura può eliminare molto sovraccarico inizializzandoli. L'ho imparato scrivendo il mio primo caricatore OpenGL. Avere tutti i puntatori di funzione in una struttura e inizializzare la struttura con = {}
genera una chiamata a memset
, mentre l'inizializzazione dei puntatori nel "modo normale" genera un centinaio di kilobyte di codice solo per azzerarli individualmente.
Evita il local statico del costruttore non banale variabili come il diavolo (i tipi POD non sono un problema). Gcc inizializzerà i locali statici del costruttore non banale thread-safe a meno che tu non compili con -fno-threadsafe-statics
, che si collega molto di codice extra (anche se non usi affatto i thread).
Usare qualcosa come libowfat invece del normale crt può molto riduci la tua dimensione binaria.
Se vuoi spremere fino all'ultima goccia di spazio dai tuoi binari, probabilmente dovrai imparare l'assembly. Per un'introduzione molto interessante (e divertente), guarda questo link:
Un tutorial vorticoso sulla creazione di eseguibili ELF davvero minuscoli per Linux
Puoi anche usare -nostartfiles
e/o -nodefaultlibs
o la combinazione di entrambi -nostdlib
. Nel caso in cui non desideri un file di avvio standard, devi scrivere la tua funzione _start allora. Vedi anche questo thread (archiviato) su oompf:
(citando Perrin)
# man syscalls
# cat phat.cc
extern "C" void _start() {
asm("int $0x80" :: "a"(1), "b"(42));
}
# g++ -fno-exceptions -Os -c phat.cc
# objdump -d phat.o
phat.o: file format elf64-x86-64
Disassembly of section .text:
0000000000000000 <_start>:
0: 53 push %rbx
1: b8 01 00 00 00 mov $0x1,%eax
6: bb 2a 00 00 00 mov $0x2a,%ebx
b: cd 80 int $0x80
d: 5b pop %rbx
e: c3 retq
# ld -nostdlib -nostartfiles phat.o -o phat
# sstrip phat
# ls -l phat
-rwxr-xr-x 1 tbp src 294 2007-04-11 22:47 phat
# ./phat; echo $?
42
Riepilogo:lo snippet precedente ha prodotto un file binario di 294 byte , ogni byte 8 bit.
Supponendo che sia consentito anche un altro strumento;-)
Quindi considera UPX:the Ultimate Packer for Binaries che utilizza la decompressione di runtime.
Buona programmazione.