web-dev-qa-db-ja.com

古いバージョンのlibcにリンクして、アプリケーションのカバレッジを拡大します

Linuxバイナリは通常、コアシステムライブラリ(libc)に動的にリンクされます。これにより、バイナリのメモリフットプリントが非常に小さくなりますが、最新のライブラリに依存するバイナリは古いシステムでは実行されません。逆に、古いライブラリにリンクされたバイナリは、最新のシステムで問題なく実行されます。

したがって、配布中にアプリケーションのカバレッジが良好になるようにするには、サポートできる最も古いlibcを見つけて、それに対してバイナリをリンクする必要があります。

リンクできるlibcの最も古いバージョンをどのように判断する必要がありますか?

64
Gearoid Murphy

実行可能ファイル内のどのシンボルが望ましくないバージョンの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

依存ライブラリを調べて、リンクできる古いバージョンのシンボルがあるかどうかを確認します。

$ 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

私たちは幸運です!

コードのGLIBC_2.2.5からバージョンをリクエストします。

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

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

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

GLIBC_2.3が不要になったことを確認します。

$ 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

詳細については、 http://web.archive.org/web/20160107032111/http://www.trevorpounds.com/blog/?p=1 を参照してください。

79
Sam Morris

残念ながら、@ Samのソリューションは私の状況ではうまく機能しません。しかし、彼の方法によると、私はそれを解決するための独自の方法を見つけました。

これは私の状況です:

Thriftフレームワーク(RPCミドルウェア)を使用してC++プログラムを書いています。私は動的リンクよりも静的リンクを好むため、私のプログラムはlibthrift.soではなくlibthrift.aに静的にリンクされます。ただし、libthrift.aはglibcに動的にリンクされ、私のlibthrift.aはglibc 2.15を使用してシステム上に構築されるため、 mylibthrift.aは、バージョン2.14のmemcpyを使用します(memcpy @ GLIBC_2.14)glibc 2.15で提供。

しかし、問題は、サーバーマシンにmemcpy@GLIBC_2.2.5のみが含まれるglibcバージョン2.5のみがあることです。 memcpy@GLIBC_2.14よりもはるかに低いです。そのため、もちろん、私のサーバープログラムはそれらのマシンでは実行できません。

そして、私はこの孤独を見つけました:

  1. .symverを使用して、memcpy@GLIBC_2.2.5への参照を取得します。

  2. 直接memcpy@GLIBC_2.2.5を呼び出す関数__ wrap_memcpyを書く。

  3. プログラムをリンクするとき、-Wl、-wrap = memcpyオプションをgcc/g ++に追加します。

ステップ1と2に含まれるコードは次のとおりです。 https://Gist.github.com/nicky-zs/7541169

9
nicky_zs

より自動化された方法でこれを行うには、次のスクリプトを使用して、特定のバージョン(2行目で設定)よりもGLIBCで新しいすべてのシンボルのリストを作成できます。必要なすべてのglibc.h宣言を含む.symverファイル(スクリプト引数で設定されたファイル名)を作成します。その後、-include glibc.hをCFLAGSに追加して、コンパイル中のあらゆる場所でピックアップされるようにします。

上記のインクルードなしでコンパイルされた静的ライブラリを使用しない場合、これで十分です。再コンパイルしたくない場合は、objcopyを使用して、シンボルを古いバージョンに名前変更したライブラリのコピーを作成できます。スクリプトの最後から2行目は、古いglibcシンボルにリンクするシステムlibstdc++.aのバージョンを作成します。 -L.(または-Lpath/to/libstdc++.a/)を追加すると、プログラムは多数の新しいシンボルにリンクせずにlibstdc ++を静的にリンクします。これが必要ない場合は、最後の2行とprintf ... redeff行を削除してください。

#!/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は非常に一般的な最小バージョンです。ただし、そのバージョンのビルドプラットフォームを見つけるのは簡単ではありません。

おそらく、より良い方向は、あなたがサポートし、それに基づいて構築したい最も古いOSについて考えることです。

3
Douglas Leeder