print · rss · source

Compiler MySQL en statique

Cette section explique comment compiler MySQL de façon à obtenir un exécutable entièrement statique. L'intérèt principal de cette manipulation est qu'elle permet de créer un package qui fonctionne sur des architectures Linux hétérogènes.

Introduction : pourquoi compiler en statique ?

Pour :

  • un binaire parfois plus performant
  • en principe utilisable sur toutes les plateformes GNU/Linux indépendament de la libc

Contre :

  • un binaire parfois moins performant
  • un binaire très gros (éventuellement un facteur x10)
  • une mise à jour plus complexe des trous de sécurité qui nécessite de tout recompiler
  • le binaire ne fonctionnera pas forcement sur tous les kernels
  • un binaire compilé dynamiquement avec la glibc2 est censé pouvoir fonctionner avec toutes ses versions ultérieures
  • un binaire compilé dynamiquement bénéficie indirectement des améliorations apportées aux bibliothèques dont il dépend

Les difficultés liées à la glibc2
Un exécutable compilé avec l'option -static et la glibc2 n'est pas réellement statique. L'appel de certaines fonction, par exemples du type getpw* ou gethostby*, provoque le chargement de bibliothèques (libnss_files.so, libnss_dns.so et libresolv.so). On peut expérimenter ça en tentant de compiler statiquement le programme ci-dessous :

#include <pwd.h>
#include <sys/types.h>

int main(int ac, char *av[])
{
        getpwnam("root");
        return 0;
}
$ gcc -static foo.c
$ strace -eopen ./a.out

La raison est liée au service NSS permettant l'authentification et la résolution de noms sous Unix et dont les méthodes peuvent changer selon le contenu du fichier /etc/nsswitch.conf.

Une alternative
MySQL met à disposition des binaires génériques compilés statiquement. En principe, ils fonctionnent sur n'importe quel système GNU/Linux et profitent d'optimisations assez poussées lors de leur compilation.

Compiler en statique
Pour obtenir un vrai binaire statique, il faut une glibc2 compilée avec l'option --enable-static-nss. La démarche est complètement expliquée ci-dessous.

Attention ! En compilant la glibc2 en statique, on renonce à certaines fonctionnalités des services NSS. Notamment, dans le fichier /etc/nsswitch.conf, le mot clé compat doit être remplacé.

Compiler les binutils

Cette étape n'est pas forcement indispensable si le système sur lequel on effectue ses compilation est suffisament récent. Elle peut en revanche s'avèrer nécessaire si la compilation de la glibc échoue en raison d'un linker obsolete :

$ tar xfz binutils-2.18
$ cd binutils-2.18
$ export CC=gcc-3.4
$ ./configure --prefix=/home/binutils-2.18
$ make
$ make check
$ make install

Notes :

  • on peut utiliser make -j avec la valeur appropriée pour parallèliser la compilation
  • le make check peut echouer sans que cela soit forcement très grave car certains tests marchent ou échouent selon la version de gcc utilisée. Dans tous les cas, les logs sont dans ld/ld.log

Compiler la libc

Choix de la version
Le noyau Linux 2.6 permet plusieurs implémentations des threads. Les NPTL, qui viennent en remplacement des LinuxThreads, sont actuellement préconisés.
Les noyaux Linux 2.4 n'implémentent que les LinuxThreads. Notre glibc2 devra donc implémenter ceux-ci pour rendre nos binaires compatibles sur les différentes versions de noyaux Linux.
A partir de la version 2.4, la glibc2 n'implémente plus que les NPTL et ne supporte plus les LinuxThreads. Nous sommes donc obligé de choisir la glibc-2.3.
La commande suivante fournit plus de détails sur ce sujet : man pthreads

Compilation

$ tar xfz glibc-2.3.6.tar.gz
$ cd glibc-2.3.6
$ tar xfz ../glibc-linuxthreads-2.3.6.tar.gz

Il faut appliquer le patch suivant :

--- glibc-2.3.5/Makeconfig.old  Wed Jun 15 08:13:12 2005
+++ glibc-2.3.5/Makeconfig      Wed Jun 15 08:13:14 2005
@@ -487,7 +487,7 @@

 ifeq (yes,$(build-static))
-link-libc-static = $(common-objpfx)libc.a $(static-gnulib) $(common-objpfx)libc.a
+link-libc-static = $(common-objpfx)libc.a $(static-gnulib) $(otherlibs) $(common-objpfx)libc.a
 else
 ifeq (yes,$(build-shared))
--- glibc-2.3.5/elf/Makefile.old        Wed Jun 15 07:46:49 2005
+++ glibc-2.3.5/elf/Makefile    Wed Jun 15 08:14:00 2005
@@ -114,6 +114,13 @@
 install-bin-script = ldd
 endif

+ifeq (yes,$(build-static-nss))
+nssobjdir := $(patsubst ../$(subdir),.,$(common-objpfx)nss)
+resolvobjdir := $(patsubst ../$(subdir),.,$(common-objpfx)resolv)
+otherlibs += $(nssobjdir)/libnss_files.a $(resolvobjdir)/libnss_dns.a \
+            $(resolvobjdir)/libresolv.a
+endif

 others         = sprof sln

Ensuite :

$ cd ..
$ mkdir glibc-build
$ cd glibc-build
$ ../glibc-2.3.6/configure --prefix=/home/glibc-2.3.6 \
--enable-static-nss --enable-add-ons=linuxthreads \
--enable-kernel=2.4.18 --with-binutils=/home/binutils-2.18/bin 
$ make
$ make check
$ make install

Notes :

  • on peut utiliser make -j avec la valeur appropriée pour parallèliser la compilation
  • sur le système utilisé, la compilation a échoué avec gcc-3.4 alors qu'elle a réussi avec gcc-3.3

Tester la nouvelle bibliothèque
On peut tester en tentant de recompiler statiquement le programme suivant :

#include <pwd.h>
#include <stdio.h>
#include <sys/types.h>

int main(int ac, char *av[])
{
        struct passwd *st;
        st = getpwnam(av[1]);

        if (st)
                printf("%s: %d\n", av[1], (int)st->pw_uid);
        else
                printf("%s not known user\n", av[1]);

        return 0;
}
$ gcc -static foo.c -L/home/glibc-2.3.6/lib -lc -lnss_files -lnss_dns -lresolv
$ strace -eopen ./a.out $USER

On peut également tester la nouvelle bibliothèque en faisant une compilation dynamique :

$ gcc foo.c -L/home/glibc-2.3.6/lib -lnss_files -lnss_dns -lresolv \
-Wl,-dynamic-linker,/home/glibc-2.3.6/lib/ld-linux.so.2,-rpath,/home/glibc-2.3.6/lib
$ strace -eopen ./a.out $USER

Compiler MySQL 3.23

$ tar xfz mysql-3.23.58.tar.gz
$ cd mysql-3.23.58
$ ./configure --prefix=/home/mysql-3.23.58-static \
--enable-assembler --with-unix-socket-path=/tmp/mysql.sock \
--with-mysqld-ldflags=-all-static --with-client-ldflags=-all-static \
--with-other-libc=/home/glibc-2.3.6
$ make
$ make test
$ make install
$ basedir=/home/mysql-3.23.58-static
$ ./scripts/mysql_install_db --datadir=$basedir/var --basedir=$basedir --verbose
$ cd /home/mysql-3.23.58-static
$ chown -R mysql.mysql var/
$ chgrp -R mysql .
$ ./bin/safe_mysqld --basedir=$basedir --datadir=$basedir/var --skip-grant-tables &

Tester le nouveau binaire

$ /home/super-smack -D test.smack 10 100

Compiler MySQL 5.0

La compilation et l'installation sont similaires à celles de la version 3.23. L'appel au configure se fait de la façon suivante :

./configure --prefix=/home/mysql-5.0.45-static \
--enable-assembler --with-unix-socket-path=/tmp/mysql.sock \
--with-mysqld-ldflags=-all-static --with-client-ldflags=-all-static \
--with-other-libc=/home/glibc-2.3.6 \
--enable-local-infile --enable-static

Un bug au niveau du configure impose d'éditer le fichier ./dbug/Makefile :

$ vi +209 ./dbug/Makefile
#LDFLAGS =  -static -L/home/glibc-2.3.6/li
LDFLAGS =  -all-static -L/home/glibc-2.3.6/lib
print · rss · source
Page last modified on December 13, 2008, at 11:16 AM