8

私は現在、柔軟なC / C ++ビルドフレームワークに取り組んでおり、(願わくば)すぐにオープンソース化する予定です。(背景については、この質問を参照してください)。

以下のコマンドを使用して、ソース/ヘッダーファイルの#includeファイルの依存関係を生成しています。

gcc -M -MM -MF

上記と同様の方法でgcc/GNUユーティリティを使用して、実行可能ファイル(ユニットテスト+私の場合はターゲットプラットフォームのメイン実行可能ファイル)のリンカー(.oファイル)依存関係を巧みに推測する方法はありますか?現在、フレームワークは非常に多くの仮定を行っており、これらの依存関係を決定するのはかなり馬鹿げています。

nmコマンドを使用して、オブジェクトファイル内の未定義のシンボルのリストを作成できる1つのアプローチについて聞いたことがあります。たとえば、オブジェクトファイル(gcc -cを使用してコンパイルされた)でnmを実行すると、次のようになります-

nm -o module.o

module.o:         U _undefinedSymbol1
module.o:         U _undefinedSymbol2
module.o:0000386f T _definedSymbol

次に、これらの未定義のシンボルが定義されている他のオブジェクトファイルを探して、ファイルを正常にリンクするために必要なオブジェクトファイルの依存関係のリストを作成します。

これは、実行可能ファイルのリンカー依存関係を決定する際のベストプラクティスと見なされますか?これらの依存関係を推測する他の方法はありますか?ソリューションを提案するときは、すべてのオブジェクトファイルがすでに存在している(つまり、gcc -cを使用してコンパイルされている)と想定します。

4

3 に答える 3

8

異なる依存関係のセットを必要とする複数の実行可能ファイル(または単一の実行可能ファイル)がある場合、それを処理する通常の古典的な方法は、静的.aまたは共有.so(または同等)のライブラリを使用して、次のようなオブジェクトファイルを保持することです。複数のプログラムによって使用され、プログラムをそのライブラリにリンクするために使用されます。リンカは、静的アーカイブから正しいオブジェクトファイルを自動的に引き出します。共有ライブラリのプロセスは少し異なりますが、最終的な結果は同じです。実行可能ファイルには、実行時に使用できる正しいオブジェクトファイルがあります。

どのプログラムにも、そのプログラムに固有のファイルが少なくとも1つあります(通常、それはmain()プログラムを含むファイルです)。そのプログラムにはいくつかのファイルがあるかもしれません。これらのファイルはおそらくよく知られており、簡単に一覧表示できます。構成とコンパイルのオプションに応じて必要になる可能性のあるものは、おそらくプログラム間で共有され、ライブラリメカニズムを介して簡単に処理されます。

静的ライブラリと共有ライブラリのどちらを使用するかを決定する必要があります。共有ライブラリを適切に作成することは、静的ライブラリを作成することよりも困難です。一方、共有ライブラリを更新して、それを使用するすべてのプログラムにすぐに影響を与えることができますが、静的ライブラリは変更できますが、新しいライブラリに再リンクされたプログラムのみが変更の恩恵を受けます。

于 2012-09-23T07:30:27.000 に答える
5

nm次のPythonスクリプトを使用して、現在のディレクトリ内のすべてのオブジェクトファイルの出力を収集および処理できます。

#! /usr/bin/env python

import collections
import os
import re
import subprocess

addr_re = r"(?P<address>[0-9a-f]{1,16})?"
code_re = r"(?P<code>[a-z])"
symbol_re = r"(?P<symbol>[a-z0-9_.$]+)"
nm_line_re = re.compile(r"\s+".join([addr_re, code_re, symbol_re]) + "\s*$",
                        re.I)

requires = collections.defaultdict(set)
provides = collections.defaultdict(set)

def get_symbols(fname):
    lines = subprocess.check_output(["nm", "-g", fname])
    for l in lines.splitlines():
        m = nm_line_re.match(l)
        symbol = m.group('symbol')
        if m.group('code') == 'U':
            requires[fname].add(symbol)
        else:
            provides[symbol].add(fname)

for dirpath, dirnames, filenames in os.walk("."):
    for f in filenames:
        if f.endswith(".o"):
            get_symbols(f)

def pick(symbols):
    # If several files provide a symbol, choose the one with the shortest name.
    best = None
    for s in symbols:
        if best is None or len(s) < len(best):
            best = s
    if len(symbols) > 1:
        best = "*" + best
    return best

for fname, symbols in requires.items():
    dependencies = set(pick(provides[s]) for s in symbols if s in provides)
    print fname + ': ' + ' '.join(sorted(dependencies))

スクリプトは、現在のディレクトリとすべてのサブディレクトリでファイルを検索し、見つかった各ファイル.oを呼び出して、結果の出力を分析します。nmあるファイルで定義されておらず、別のファイルで定義されているシンボルは.o、2つのファイル間の依存関係として解釈されます。どこにも定義されていないシンボル(通常は外部ライブラリによって提供される)は無視されます。最後に、スクリプトはすべてのオブジェクトファイルの直接依存関係のリストを出力します。

シンボルが複数のオブジェクトファイルによって提供される場合、このスクリプトは、ファイル名が最も短いオブジェクトファイルへの依存関係を任意に想定します(選択したファイルに*出力のマークを付けます)。この動作は、関数を変更することで変更できますpick

このスクリプトはLinuxとMacOSで動作しますが、他のオペレーティングシステムは試していません。また、スクリプトのテストはわずかです。

于 2013-05-29T17:01:08.723 に答える
4

nmユーティリティは、libbfdを使用してオブジェクトファイル(および.aライブラリなどのアーカイブ)を読み取ります。私はあなたが本当にやりたいことは、あなたが知っているライブラリとこのプロジェクトの一部であるオブジェクトファイルで定義されたパブリックシンボルのデータベースを処理することだと思っています。その中の未定義のシンボルを見て、参照を解決するためにリンクする必要があるオブジェクト(プレーンまたはライブラリ)を判別できます。基本的に、リンカーと同じ仕事をしていますが、逆になっているので、どのシンボルを見つけることができますか。

GCCを使用している場合は、いつでも「binutils」のソースパッケージを調べて、nmのソースを見つけることができます。必要に応じて、ldのソースを見つけることもできます。確かに、内部でlibbfdを使用しているだけの場合は、nmを実行して出力を解析する必要はありません。自分で、libbfdを呼び出してください。

于 2012-09-25T22:43:57.493 に答える