web-dev-qa-db-fra.com

Liaison avec une ancienne version de libc pour fournir une plus grande couverture d'application

Les binaires Linux sont généralement liés dynamiquement à la bibliothèque système principale (libc). Cela permet de garder l'empreinte mémoire du binaire assez petite mais les binaires qui dépendent des dernières bibliothèques ne fonctionneront pas sur les systèmes plus anciens. Inversement, les binaires liés aux anciennes bibliothèques fonctionneront avec bonheur sur les derniers systèmes.

Par conséquent, afin de garantir que notre application a une bonne couverture pendant la distribution, nous devons déterminer la plus ancienne libc que nous pouvons prendre en charge et lier notre binaire à cela.

Comment déterminer la version la plus ancienne de libc à laquelle nous pouvons établir un lien?

64
Gearoid Murphy

Déterminez quels symboles de votre exécutable créent la dépendance vis-à-vis de la version indésirable de glibc.

$ objdump -p myprog
...
Version References:
  required from libc.so.6:
    0x09691972 0x00 05 GLIBC_2.3
    0x09691a75 0x00 03 GLIBC_2.2.5

$ objdump -T myprog | fgrep GLIBC_2.3
0000000000000000      DF *UND*  0000000000000000  GLIBC_2.3   realpath

Regardez dans la bibliothèque dépendante pour voir s'il y a des symboles dans les anciennes versions que vous pouvez lier:

$ objdump -T /lib/libc.so.6 | grep -w realpath
0000000000105d90 g    DF .text  0000000000000021 (GLIBC_2.2.5) realpath
000000000003e7b0 g    DF .text  00000000000004bf  GLIBC_2.3   realpath

Nous avons de la chance!

Demander la version à GLIBC_2.2.5 dans votre code:

#include <limits.h>
#include <stdlib.h>

__asm__(".symver realpath,realpath@GLIBC_2.2.5");

int main () {
    realpath ("foo", "bar");
}

Observez que GLIBC_2.3 n'est plus nécessaire:

$ objdump -p myprog
...
Version References:
  required from libc.so.6:
    0x09691a75 0x00 02 GLIBC_2.2.5

$ objdump -T myprog | grep realpath
0000000000000000      DF *UND*  0000000000000000  GLIBC_2.2.5 realpath

Pour plus d'informations, voir http://web.archive.org/web/20160107032111/http://www.trevorpounds.com/blog/?p=1 .

79
Sam Morris

Malheureusement, la solution de @ Sam ne fonctionne pas bien dans ma situation. Mais selon sa façon, j'ai trouvé ma propre façon de résoudre ce problème.

Voici ma situation:

J'écris un programme C++ en utilisant le framework Thrift (c'est un middleware RPC). Je préfère le lien statique au lien dynamique, donc mon programme est lié à libthrift.a statiquement au lieu de libthrift.so . Cependant, libthrift.a est lié dynamiquement à glibc, et puisque mon libthrift.a est construit sur mon système avec glibc 2.15, my libthrift.a utilise memcpy de la version 2.14 ( memcpy @ GLIBC_2.14 ) fourni par la glibc 2.15.

Mais le problème est que nos machines serveurs n'ont que la glibc version 2.5 qui n'a que memcpy@GLIBC_2.2.5 . Il est bien inférieur à memcpy@GLIBC_2.14 . Donc, bien sûr, mon programme serveur ne peut pas fonctionner sur ces machines.

Et j'ai trouvé cette solusion:

  1. Utilisation de .symver pour obtenir la référence à memcpy@GLIBC_2.2.5 .

  2. Écrire ma propre fonction __ wrap_memcpy qui appelle simplement memcpy@GLIBC_2.2.5 directement .

  3. Lors de la liaison de mon programme, ajoutez l'option - Wl, - wrap = memcpy à gcc/g ++.

Le code impliqué dans les étapes 1 et 2 est ici: https://Gist.github.com/nicky-zs/7541169

9
nicky_zs

Pour ce faire de manière plus automatisée, vous pouvez utiliser le script suivant pour créer une liste de tous les symboles plus récents dans votre GLIBC que dans une version donnée (définie à la ligne 2). Il crée un glibc.h fichier (nom de fichier défini par l'argument de script) qui contient tous les .symver déclarations. Vous pouvez ensuite ajouter -include glibc.h à votre CFLAGS pour vous assurer qu'il est récupéré partout dans votre compilation.

Cela suffit si vous n'utilisez aucune bibliothèque statique qui a été compilée sans l'inclusion ci-dessus. Si vous le faites et que vous ne souhaitez pas recompiler, vous pouvez utiliser objcopy pour créer une copie de la bibliothèque avec les symboles renommés dans les anciennes versions. La deuxième à la dernière ligne du script crée une version de votre système libstdc++.a qui sera lié aux anciens symboles de la glibc. Ajouter -L. (ou -Lpath/to/libstdc++.a/) fera que votre programme liera statiquement libstdc ++ sans lier un tas de nouveaux symboles. Si vous n'en avez pas besoin, supprimez les deux dernières lignes et le printf ... redeff ligne.

#!/bin/bash
maxver=2.9
headerf=${1:-glibc.h}
set -e
for lib in libc.so.6 libm.so.6 libpthread.so.0 libdl.so.2 libresolv.so.2 librt.so.1; do
objdump -T /usr/lib/$lib
done | awk -v maxver=${maxver} -vheaderf=${headerf} -vredeff=${headerf}.redef -f <(cat <<'EOF'
BEGIN {
split(maxver, ver, /\./)
limit_ver = ver[1] * 10000 + ver[2]*100 + ver[3]
}
/GLIBC_/ {
gsub(/\(|\)/, "",$(NF-1))
split($(NF-1), ver, /GLIBC_|\./)
vers = ver[2] * 10000 + ver[3]*100 + ver[4]
if (vers > 0) {
    if (symvertext[$(NF)] != $(NF-1))
        count[$(NF)]++
    if (vers <= limit_ver && vers > symvers[$(NF)]) {
        symvers[$(NF)] = vers
        symvertext[$(NF)] = $(NF-1)
    }
}
}
END {
for (s in symvers) {
    if (count[s] > 1) {
        printf("__asm__(\".symver %s,%s@%s\");\n", s, s, symvertext[s]) > headerf
        printf("%s %s@%s\n", s, s, symvertext[s]) > redeff
    }
}
}
EOF
)
sort ${headerf} -o ${headerf}
objcopy --redefine-syms=${headerf}.redef /usr/lib/libstdc++.a libstdc++.a
rm ${headerf}.redef
8
patstew

glibc 2.2 est une version minimale assez courante. Cependant, trouver une plate-forme de construction pour cette version peut être non trivial.

Une meilleure direction est probablement de penser au système d'exploitation le plus ancien que vous souhaitez prendre en charge et de vous en inspirer.

3
Douglas Leeder