Outre l'évidence (-Os -s
), l'alignement des fonctions sur la plus petite valeur possible qui ne plantera pas (je ne connais pas les exigences d'alignement ARM) peut extraire quelques octets par fonction.
-Os
devrait désactivez déjà les fonctions d'alignement, mais cela peut toujours être par défaut une valeur comme 4 ou 8. Si l'alignement par ex. à 1 est possible avec ARM, cela peut économiser quelques octets.
-ffast-math
(ou le moins abrasif -fno-math-errno
) ne définira pas errno et évitera certaines vérifications, ce qui réduit la taille du code. Si, comme la plupart des gens, vous ne lisez pas errno de toute façon, c'est une option.
Utiliser correctement __restrict
(ou restrict
) et const
supprime les charges redondantes, rendant le code à la fois plus rapide et plus petit (et plus correct). Marquer correctement les fonctions pures comme telles élimine les appels de fonction.
L'activation de LTO peut aider, et si cela n'est pas disponible, compiler tous les fichiers source dans un binaire en une seule fois (gcc foo.c bar.c baz.c -o program
au lieu de compiler foo.c
, bar.c
, et baz.c
aux fichiers objets d'abord, puis à la liaison) aura un effet similaire. Il rend tout visible à l'optimiseur en même temps, lui permettant éventuellement de mieux fonctionner.
-fdelete-null-pointer-checks
peut être une option (notez que ceci est normalement activé avec n'importe quel "O", mais pas sur les cibles embarquées).
Mettre des globals statiques (vous n'en avez, espérons-le, pas autant, mais quand même) dans une structure peut éliminer beaucoup de frais généraux en les initialisant. J'ai appris cela lors de l'écriture de mon premier chargeur OpenGL. Avoir tous les pointeurs de fonction dans une structure et initialiser la structure avec = {}
génère un appel à memset
, tandis que l'initialisation des pointeurs de la "manière normale" génère une centaine de kilo-octets de code juste pour mettre chacun à zéro individuellement.
Évitez les constructeurs non triviaux static local variables comme le diable (les types de POD ne posent aucun problème). Gcc initialisera les threads locaux statiques non triviaux à moins que vous ne compiliez avec -fno-threadsafe-statics
, qui contient beaucoup de liens de code supplémentaire (même si vous n'utilisez pas du tout de threads).
Utiliser quelque chose comme libowfat au lieu du crt normal peut grandement réduisez votre taille binaire.
Si vous voulez extraire jusqu'à la dernière goutte d'espace de vos fichiers binaires, vous devrez probablement apprendre l'assemblage. Pour une introduction très intéressante (et divertissante), consultez ce lien :
Un tutoriel Whirlwind sur la création d'exécutables ELF vraiment Teensy pour Linux
Vous pouvez également utiliser -nostartfiles
et/ou -nodefaultlibs
ou la combinaison des deux -nostdlib
. Si vous ne voulez pas de fichier de démarrage standard, vous devez alors écrire votre propre fonction _start. Voir aussi ce fil (archivé) sur oompf :
(citant 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
Résumé :l'extrait ci-dessus a généré un binaire de 294 octets , chaque octet 8 bits.
En supposant qu'un autre outil est également autorisé;-)
Considérez alors UPX :l'Ultimate Packer for Binaries qui utilise la décompression d'exécution.
Bon codage.