自动为C / C ++可执行文件生成目标文件(链接器)依赖项

我目前正在开发一个灵活的C / C ++构建框架,我很快就会(希望)开源。 (有关背景,请参阅此问题)。

我使用以下命令为源/头文件生成#include文件依赖项。

gcc -M -MM -MF 

有没有一种巧妙地推断可执行文件的链接器(.o文件)依赖关系(unit testing+在我的情况下为目标平台的主要可执行文件)使用gcc / GNU实用程序以类似的方式? 目前,该框架做了很多假设,并且在确定这些依赖关系时非常愚蠢。

我听说过一种方法,其中nm命令可用于在目标文件中提供未定义符号的列表。 例如,在目标文件上运行nm(使用gcc -c编译)会出现类似这样的内容 –

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

然后,人们将查找其他目标文件,其中定义了这些未定义的符号以提供成功链接文件所需的目标文件依赖性列表。

这是确定可执行文件的链接器依赖性的最佳实践吗? 有没有其他方法可以推断出这些依赖关系? 在提出解决方案时,假设所有目标文件已经存在(即已经使用gcc -c编译)。

如果有多个可执行文件(甚至是单个可执行文件)需要不同的依赖项集,那么处理它的正常,经典方法是使用库 – 静态.a或共享.so (或等效项) – 来保存对象可以由多个程序使用的文件,以及将程序与该库链接的文件。 链接器会自动从静态存档中提取正确的目标文件。 共享库过程略有不同,但最终结果是相同的:可执行文件在运行时具有正确的目标文件。

对于任何程序,至少有一个程序唯一的文件(通常,这是包含main()程序的文件)。 该程序可能有一些文件。 这些文件可能是已知的并且可以轻松列出。 根据配置和编译选项可能需要的那些可能在程序之间共享,并且可以通过库机制轻松处理。

您必须决定是否要使用静态库或共享库。 创建共享库比创建静态库更难。 另一方面,您可以更新共享库并立即影响使用它的所有程序,而静态库可以更改,但只有使用新库重新链接的程序才能从更改中受益。

以下Python脚本可用于收集和处理当前目录中所有目标文件的nm输出:

 #! /usr/bin/env python import collections import os import re import subprocess addr_re = r"(?P
[0-9a-f]{1,16})?" code_re = r"(?P[az])" symbol_re = r"(?P[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文件中未定义并在另一个文件中定义的符号被解释为两个文件之间的依赖关系。 无处定义的符号(通常由外部库提供)将被忽略。 最后,该脚本打印所有目标文件的直接依赖项列表。

如果某个符号由多个目标文件提供,则此脚本会假定依赖于具有最短文件名的目标文件(并在输出中用*标记所选文件)。 可以通过修改函数pick来更改此行为。

该脚本适用于Linux和MacOS,我没有尝试过任何其他操作系统,脚本只是经过了轻微的测试。

nm实用程序使用libbfd读取目标文件(和档案,例如.a库)。 我在想你真正想要做的是处理你知道的库中定义的公共符号的数据库,以及在这个项目的一部分的目标文件中,以便在生成每个新的目标文件时可以查看其中的未定义符号,并确定需要链接以解析引用的哪个对象(纯文本或库中)。 基本上你是在做与链接器相同的工作,但是反过来,这样你就可以找到哪些符号。

如果您正在使用GCC,您可以随时查看“binutils”的源包以查找nm的源代码,如果需要,甚至可以查找ld。 你肯定不想运行nm并解析输出,只需在引擎盖下使用libbfd,只需自己调用libbfd即可。