74

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

したがって、配布中にアプリケーションが適切にカバーされるようにするには、サポートできる最も古いlibcを特定し、それに対してバイナリをリンクする必要があります。

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

4

4 に答える 4

97

実行可能ファイル内のどのシンボルが、望ましくないバージョンの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=103を参照してください。

于 2011-05-12T11:51:01.917 に答える
13

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

これは私の状況です:

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

しかし、問題は、サーバーマシンに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

于 2013-11-19T06:54:06.457 に答える
11

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

上記のインクルードなしでコンパイルされた静的ライブラリを使用しない場合は、これで十分です。再コンパイルしたくない場合は、を使用objcopyして、シンボルの名前を古いバージョンに変更したライブラリのコピーを作成できます。スクリプトの2番目から最後の行はlibstdc++.a、古いglibcシンボルに対してリンクするシステムのバージョンを作成します。-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
于 2016-09-16T18:07:39.690 に答える
4

glibc2.2はかなり一般的な最小バージョンです。ただし、そのバージョンのビルドプラットフォームを見つけることは簡単ではないかもしれません。

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

于 2010-10-27T11:31:47.040 に答える