如何在单个 (.a) 存档中仅获取所需的对象文件
How to take only required object files inside a single (.a) archive
只是一个简单的问题,但我在任何地方都找不到答案。将所有对象文件放入存档时,如何指示 clang++ 只获取所需的对象文件进行链接,以避免由于存档中不需要的符号而导致未定义的符号错误?
你将无法找到你想要的答案,因为你 想要让链接器做就是它默认做的事情。这是一个演示。 (它是用 C 语言而不是 C++ 只是为了避免混淆C++名称)。
三个源文件:
爱丽丝·
#include <stdio.h>
void alice(void)
{
puts("alice");
}
鲍勃·
#include <stdio.h>
void bob(void)
{
puts("bob");
}
玛丽·
#include <stdio.h>
void mary(void)
{
puts("mary");
}
编译它们并将目标文件放入存档中:
$ clang -Wall -c alice.c
$ clang -Wall -c bob.c
$ clang -Wall -c mary.c
$ ar rc libabm.a alice.o bob.o mary.o
以下是存档的成员列表:
$ ar -t libabm.a
alice.o
bob.o
mary.o
以下是这些成员的符号表:
$ nm libabm.a
alice.o:
0000000000000000 T alice
U puts
bob.o:
0000000000000000 T bob
U puts
mary.o:
0000000000000000 T mary
U puts
其中T
表示已定义的函数,U
未定义的函数。puts
是 在标准 C 库中定义,默认情况下将链接该库。
现在这里有一个在外部调用alice
的程序,因此依赖于alice.o
:
赛亚丽丝
extern void alice(void);
int main(void)
{
alice();
return 0;
}
这是另一个调用alice
并在外部bob
的程序,因此 依赖于alice.o
和bob.o
.
sayalice_n_bob.c
extern void alice(void);
extern void bob(void);
int main(void)
{
alice();
bob();
return 0;
}
同时编译这两个源:
$ clang -Wall -c sayalice.c
$ clang -Wall -c sayalice_n_bob.c
链接器选项-trace
指示链接器报告链接的对象文件和链接的 DSO。我们将使用 现在使用sayalice.o
和libabm.a
链接程序sayalice
:
$ clang -o sayalice sayalice.o -L. -labm -Wl,-trace
/usr/bin/ld: mode elf_x86_64
/usr/bin/../lib/gcc/x86_64-linux-gnu/7.2.0/../../../x86_64-linux-gnu/crt1.o
/usr/bin/../lib/gcc/x86_64-linux-gnu/7.2.0/../../../x86_64-linux-gnu/crti.o
/usr/bin/../lib/gcc/x86_64-linux-gnu/7.2.0/crtbegin.o
sayalice.o
(./libabm.a)alice.o
libgcc_s.so.1 (/usr/bin/../lib/gcc/x86_64-linux-gnu/7.2.0/libgcc_s.so.1)
/lib/x86_64-linux-gnu/libc.so.6
(/usr/lib/x86_64-linux-gnu/libc_nonshared.a)elf-init.oS
/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2
/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2
libgcc_s.so.1 (/usr/bin/../lib/gcc/x86_64-linux-gnu/7.2.0/libgcc_s.so.1)
/usr/bin/../lib/gcc/x86_64-linux-gnu/7.2.0/crtend.o
/usr/bin/../lib/gcc/x86_64-linux-gnu/7.2.0/../../../x86_64-linux-gnu/crtn.o
我们看到所有的样板 C 库和运行时都是链接的。和的我们创建的对象文件,仅链接了两个:
sayalice.o
(./libabm.a)alice.o
我们的计划不依赖libabm.a
的两个成员:
(./libabm.a)bob.o
(./libabm.a)mary.o
未链接。
运行程序:
$ ./sayalice
alice
上面写着"爱丽丝"。
然后为了进行比较,我们将链接程序sayalice_n_bob
,再次使用-trace
:
$ clang -o sayalice_n_bob sayalice_n_bob.o -L. -labm -Wl,-trace
/usr/bin/ld: mode elf_x86_64
/usr/bin/../lib/gcc/x86_64-linux-gnu/7.2.0/../../../x86_64-linux-gnu/crt1.o
/usr/bin/../lib/gcc/x86_64-linux-gnu/7.2.0/../../../x86_64-linux-gnu/crti.o
/usr/bin/../lib/gcc/x86_64-linux-gnu/7.2.0/crtbegin.o
sayalice_n_bob.o
(./libabm.a)alice.o
(./libabm.a)bob.o
libgcc_s.so.1 (/usr/bin/../lib/gcc/x86_64-linux-gnu/7.2.0/libgcc_s.so.1)
/lib/x86_64-linux-gnu/libc.so.6
(/usr/lib/x86_64-linux-gnu/libc_nonshared.a)elf-init.oS
/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2
/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2
libgcc_s.so.1 (/usr/bin/../lib/gcc/x86_64-linux-gnu/7.2.0/libgcc_s.so.1)
/usr/bin/../lib/gcc/x86_64-linux-gnu/7.2.0/crtend.o
/usr/bin/../lib/gcc/x86_64-linux-gnu/7.2.0/../../../x86_64-linux-gnu/crtn.o
这一次,我们链接了三个目标文件:
sayalice_n_bob.o
(./libabm.a)alice.o
(./libabm.a)bob.o
以及该程序不依赖libabm.a
的唯一成员:
(./libabm.a)mary.o
未链接。
该程序的运行方式如下:
$ ./sayalice_n_bob
alice
bob
以下是该程序的全局符号表:
$ nm -g sayalice_n_bob
0000000000400520 T alice
0000000000400540 T bob
0000000000601030 B __bss_start
0000000000601020 D __data_start
0000000000601020 W data_start
0000000000601028 D __dso_handle
0000000000601030 D _edata
0000000000601038 B _end
00000000004005d4 T _fini
w __gmon_start__
00000000004003d0 T _init
00000000004005e0 R _IO_stdin_used
00000000004005d0 T __libc_csu_fini
0000000000400560 T __libc_csu_init
U __libc_start_main@@GLIBC_2.2.5
00000000004004f0 T main
U puts@@GLIBC_2.2.5
0000000000400410 T _start
0000000000601030 D __TMC_END__
与alice
和bob
,但不是mary
.
如您所见,链接器的默认行为是您询问的行为 才能得到。阻止链接器仅提取以下存档成员 在链接中引用,而不是链接所有存档成员,您必须 明确告诉它这样做,将存档置于--whole-archive
范围内 链接命令行中的选项:
$ clang -o sayalice_n_bob sayalice_n_bob.o -L. -Wl,--whole-archive -labm -Wl,--no-whole-archive -Wl,-trace
/usr/bin/ld: mode elf_x86_64
/usr/bin/../lib/gcc/x86_64-linux-gnu/7.2.0/../../../x86_64-linux-gnu/crt1.o
/usr/bin/../lib/gcc/x86_64-linux-gnu/7.2.0/../../../x86_64-linux-gnu/crti.o
/usr/bin/../lib/gcc/x86_64-linux-gnu/7.2.0/crtbegin.o
sayalice_n_bob.o
(./libabm.a)alice.o
(./libabm.a)bob.o
(./libabm.a)mary.o
libgcc_s.so.1 (/usr/bin/../lib/gcc/x86_64-linux-gnu/7.2.0/libgcc_s.so.1)
/lib/x86_64-linux-gnu/libc.so.6
(/usr/lib/x86_64-linux-gnu/libc_nonshared.a)elf-init.oS
/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2
/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2
libgcc_s.so.1 (/usr/bin/../lib/gcc/x86_64-linux-gnu/7.2.0/libgcc_s.so.1)
/usr/bin/../lib/gcc/x86_64-linux-gnu/7.2.0/crtend.o
/usr/bin/../lib/gcc/x86_64-linux-gnu/7.2.0/../../../x86_64-linux-gnu/crtn.o
在那里,您会看到所有存档成员都已链接:
(./libabm.a)alice.o
(./libabm.a)bob.o
(./libabm.a)mary.o
程序现在定义了所有alice
、bob
和mary
:
$ nm -g sayalice_n_bob
0000000000400520 T alice
0000000000400540 T bob
0000000000601030 B __bss_start
0000000000601020 D __data_start
0000000000601020 W data_start
0000000000601028 D __dso_handle
0000000000601030 D _edata
0000000000601038 B _end
00000000004005f4 T _fini
w __gmon_start__
00000000004003d0 T _init
0000000000400600 R _IO_stdin_used
00000000004005f0 T __libc_csu_fini
0000000000400580 T __libc_csu_init
U __libc_start_main@@GLIBC_2.2.5
00000000004004f0 T main
0000000000400560 T mary
U puts@@GLIBC_2.2.5
0000000000400410 T _start
0000000000601030 D __TMC_END__
虽然它从不打电话给mary
.
退后一步
您问这个问题是因为您相信,如果您可以从存档链接 仅那些定义链接中已引用的符号的对象文件 那么链接就不会因程序对符号的未定义引用而失败 从不使用。但事实并非如此,这里证明事实并非如此。
另一个源文件:
爱丽丝2.c
#include <stdio.h>
extern void david(void);
void alice(void)
{
puts("alice");
}
void dave(void)
{
david();
}
编译为:
$ clang -Wall -c alice2.c
将alice.o
替换为libabm.a
中的alice2.o
:
$ ar d libabm.a alice.o
$ ar r libabm.a alice2.o
然后尝试像以前一样链接程序sayalice
:
$ clang -o sayalice sayalice.o -L. -labm -Wl,-trace
/usr/bin/ld: mode elf_x86_64
/usr/bin/../lib/gcc/x86_64-linux-gnu/7.2.0/../../../x86_64-linux-gnu/crt1.o
/usr/bin/../lib/gcc/x86_64-linux-gnu/7.2.0/../../../x86_64-linux-gnu/crti.o
/usr/bin/../lib/gcc/x86_64-linux-gnu/7.2.0/crtbegin.o
sayalice.o
(./libabm.a)alice2.o
libgcc_s.so.1 (/usr/bin/../lib/gcc/x86_64-linux-gnu/7.2.0/libgcc_s.so.1)
/lib/x86_64-linux-gnu/libc.so.6
(/usr/lib/x86_64-linux-gnu/libc_nonshared.a)elf-init.oS
/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2
/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2
libgcc_s.so.1 (/usr/bin/../lib/gcc/x86_64-linux-gnu/7.2.0/libgcc_s.so.1)
/usr/bin/../lib/gcc/x86_64-linux-gnu/7.2.0/crtend.o
/usr/bin/../lib/gcc/x86_64-linux-gnu/7.2.0/../../../x86_64-linux-gnu/crtn.o
./libabm.a(alice2.o): In function `dave':
alice2.c:(.text+0x25): undefined reference to `david'
/usr/bin/ld: link errors found, deleting executable `sayalice'
clang: error: linker command failed with exit code 1 (use -v to see invocation)
这一次,唯一被链接的存档成员是:
(./libabm.a)alice2.o
因为sayalice.o
中只调用alice
.然而,联动失败了 对函数david
的未定义引用,程序从不调用。david
只在函数dave
的定义中被调用,而dave
从不被调用。
尽管从未调用过dave
,但它的定义是链接的,因为它位于 对象文件,alice2.o
,链接以提供函数alice
的定义 - 这叫。随着dave
的定义在链接中,呼吁david
成为未解析的参照,默认情况下,链接必须找到 定义,或失败。所以它失败了。
然后你会看到,通过未定义的引用来链接的失败 程序从不使用的符号与链接器的事实一致不链接存档中未引用的对象文件。
如何在不使用的符号的未定义引用中幸存下来
如果您遇到这种联动故障,您可以通过指导 允许未定义引用的链接器。您可以简单地指示它忽略所有未定义的引用,例如:
$ clang -o sayalice sayalice.o -L. -labm -Wl,--unresolved-symbols=ignore-all
$ ./sayalice
alice
或者更谨慎地说,您可以指示它只是为了发出警告,而不是失败,对于 未定义的引用,例如:
$ clang -o sayalice sayalice.o -L. -labm -Wl,--warn-unresolved-symbols
./libabm.a(alice2.o): In function `dave':
alice2.c:(.text+0x25): warning: undefined reference to `david'
$ ./sayalice
alice
这样,您可以在诊断中检查唯一未定义的符号是 你期待的那些。
- 获取文件数据预处理器宏
- 如何在Windows内核中获取文件大小
- 从 C 中的变量获取文件的路径,C++
- 如何使用 C/C++ 和 system() 系统调用以外的其他方法在 Linux 中获取文件功能?
- 如何使用 jpeglib.h 获取文件是否为 JPEG 类型
- 谁能告诉我,程序中的错误是什么?该程序仅用于获取文件扩展名
- 我需要通过窗口句柄(HWND)获取文件,我该怎么办?
- 如何用C++部分地获取文件中的行
- 使用内置的Windows方法获取文件夹的内容
- 在 Apache Velocity 模板语言中获取文件目录
- 如何在C#或C++中获取文件或文件夹的有效权限?有什么 API 吗?
- 对字符求和并减去以获取文件
- 从 FutureAccessList 同步获取文件夹路径
- 通过HTTPS从POCO StreamCopier获取文件下载进度
- 如何使用C 17获取文件大小
- getline 无法获取文件的第一行
- 如何使用 Win API 获取文件夹中的文件列表(带过滤器)
- 无法从零MQ ZMQ_SERVER套接字中获取文件描述符
- HttpQueryInfo 获取文件大小
- 通过文件资源管理器获取文件夹目录