18

私たちのプロジェクト (C++、Linux、gcc、PowerPC) は、いくつかの共有ライブラリで構成されています。パッケージの新しいバージョンをリリースするときは、ソース コードが実際に影響を受けるライブラリのみを変更する必要があります。「変更」とは、絶対的なバイナリ IDを意味します (ファイルのチェックサムが比較されます。異なるチェックサム -> ポリシーに従って異なるバージョン)。(コードが変更されたか、ライブラリごとでなくても、プロジェクト全体が常に一度にビルドされることに注意してください)。

通常、これは、含まれているヘッダー ファイルの非公開部分を非表示にし、公開部分を変更しないことで実現できます。

ただし、ライブラリ libTableManager.so のクラス TableManager (TableManager.cpp ファイル内!) のデストラクタに単なるdelete追加が行われているにもかかわらず、ライブラリ libB.so (クラス TableManager を使用する) のバイナリ/チェックサムが追加されている場合がありました。が変更されました。

TableManager.h:

class TableManager 
{
public:
    TableManager();
    ~TableManager();
private:
    int* myPtr;
}

TableManager.cpp:

TableManager::~TableManager()
{
    doSomeCleanup();
    delete myPtr;     // this delete has been added
}

libB.so を で検査readelf --all libB.soし、.dynsym セクションを調べると、他のライブラリから動的に使用されるものも含め、すべての関数の長さが libB に格納されていることがわかりました。次のようになります (長さは 3 列目の 668 です)。

527: 00000000 668 FUNC GLOBAL DEFAULT UND _ZN12TableManagerD1Ev

だから私の質問は:

  1. 関数の長さが実際にクライアント ライブラリに格納されるのはなぜですか? 開始アドレスで十分ではないでしょうか?
  2. libB.so のコンパイル/リンク (一種の「ストリッピング」) の際に、これを何らかの形で抑制することはできますか? この依存度を減らしたいのですが...
4

2 に答える 2

11

ビンゴ。それは実際には、2008年に発見され修正されたbinutilsの一種の「バグ」です。サイズ情報は実際には役に立ちません

Simon Baldwinがbinutilsメーリングリストに書いたことは、問題を正確に説明しています(私が強調しています)。

現在、未定義のELFシンボルのサイズは、リンク時に、シンボルを提供するオブジェクトファイルまたはDSOからコピーされます。 このサイズは信頼できません。たとえば、一方が他方にリンクしている2つのDSOの場合です。下位レベルのDSOは、上位レベルのDSOを再構築するための厳しい要件なしに、シンボルサイズを変更するABI保存の変更を行うことができます。また、上位レベルのDSOが再構築された場合、ファイルのチェックサムを監視するツールは、上位レベルのDSOについて他に何も変更されていなくても、未定義のシンボルのサイズが変更されたために変更を登録します。これにより、チェックサムベースのシステムで不要で望ましくない再構築と変更のカスケードが発生する可能性があり ます。

古いシステム(binutils 2.16)に問題があります。デスクトップシステムのバージョン2.20と比較したところ、共有グローバルシンボルの長さは0でした。

157: 00000000     0 FUNC    GLOBAL DEFAULT  UND _ZN12TableManagerD1Ev
158: 00000000     0 FUNC    GLOBAL DEFAULT  UND _ZNSs6assignERKSs@GLIBCXX_3.4 (2)
159: 00000000     0 FUNC    GLOBAL DEFAULT  UND sleep@GLIBC_2.0 (6)
160: 00000000     0 FUNC    GLOBAL DEFAULT  UND _ZN4Gpio11setErrorLEDENS_

そこで、両方のbinutilsソースコードを比較しました。-voilàもう一度-Alanがメーリングリストで提案した修正があります。

ここに画像の説明を入力してください

古いプラットフォームを使用する必要があるため、パッチを適用してbinutilsを再コンパイルするだけかもしれません。お待ち頂きまして、ありがとうございます。

于 2012-11-30T14:27:47.370 に答える
8

ローダーのコードを確認する必要がありますが、この場合、その長さフィールドが何を達成しようとしているのかについて、かなり合理的な推測を行うことができると思います。

ローダーは、プロセスに配置されるすべての関数を取得し、それらをメモリ アドレスにマップする必要があります。したがって、最初の関数にアドレスを与えます。次に、2 番目の関数が最初の関数の終了後に続きますが、「最初の関数の終了」を知るには、最初の関数の長さを知る必要があります。

その長さを取得する方法は 2 つあります。ファイルにエンコードする方法 (ELF で見たように) か、関数を含むファイルを開いてから長さを取得する方法のいずれかです。そこの。

後者には (私には) 2 つの明らかな欠点があるように思われます。1 つ目は速度です。関数の長さを取得するためだけに余分なファイルをすべて開いたり、ヘッダーを解析したりするのは、現在のファイルから関数ごとに余分な 4 バイトを読み取るよりもほぼ確実に遅くなります。2 つ目は利便性です。ファイル内の関数を呼び出さない限り、そのファイルが存在する必要はまったくありません。ファイルから直接長さを読み取る場合 (たとえば、Windows が通常 DLL で行うように)、実際には使用されない場合でも、そのファイルがターゲット システムに存在する必要があります。

編集: 一部の人々は明らかに「達成することを意図している」という微妙な意味を (明らかにあまりにも) 見逃しているため、完全に明確にさせてください: このフィールドが実際に使用されていない (そして決して使用されていない) ことは合理的に確信しています。

ただし、この答えが間違っていると考える人は、プログラミングの 101 に戻って、インターフェースと実装の違いを学ぶ必要があります。

この場合、ファイル形式はインターフェイス (ローダーが使用できる一連の機能) を定義します。Linux の特定のケースでは、このフィールドは使用されていないようです。

ただし、フィールドがまだ存在しているという事実や、OPがフィールドが存在する理由について尋ねたという事実は変わりません。「使用されていません」と言うだけでは、それ自体は真実ですが、彼が尋ねた質問に答える/答えません.

于 2012-11-29T15:03:45.637 に答える