在不使用重新定义的符号时避免多重定义链接器错误
Avoid multiple definition linker error when not using the redefined symbols
我尝试构建一个链接到各种共享库和静态库的可执行文件。事实证明,两个静态库都定义了相同的符号,这导致了多重定义链接器错误。我的可执行文件不使用这个符号,所以这不是一个真正的问题。
我可以通过添加 --allow-multiple-definitions
标志来避免错误,但这似乎是一个核选项。如果我尝试使用多次定义的符号,我希望链接器发出警告。
有没有办法告诉链接器"complain for multiple definitions only if the symbol is used"?或者告诉它,"from lib ABC ignore symbol XYZ"。我正在 linux.
上使用 g++ 进行开发
您可能有问题的一种变体或不同的变体,
取决于您尚未考虑其相关性的事实。或者您可能两者兼而有之,因此我将介绍针对每个变体的解决方案。
您应该熟悉静态库的性质以及它们在链接中的使用方式,
as summarised here
Superflous Globals 符号变体
这里有几个源文件和一个 header 文件:
one.cpp
#include <onetwo.h>
int clash = 1;
int get_one()
{
return clash;
}
two.cpp
#include <onetwo.h>
int get_two()
{
return 2;
}
onetwo.h
#pragma once
extern int get_one();
extern int get_two();
这些已经内置到静态库中libonetwo.a
$ g++ -Wall -Wextra -pedantic -I. -c one.cpp two.cpp
$ ar rcs libonetwo.a one.o two.o
其预期 API 在 onetwo.h
中定义
同样,一些其他的源文件和一个header已经
内置于静态库 libfourfive.a
中,其预期 API
在 fourfive.h
中定义
four.cpp
#include <fourfive.h>
int clash = 4;
int get_four()
{
return clash;
}
five.cpp
#include <fourfive.h>
int get_five()
{
return 5;
}
fourfive.h
#pragma once
extern int get_four();
extern int get_five();
下面是依赖于这两个库的程序的源代码:
prog.cpp
#include <onetwo.h>
#include <fourfive.h>
int main()
{
return get_one() + get_four();
}
我们尝试这样构建:
$ g++ -Wall -Wextra -pedantic -I. -c prog.cpp
$ g++ -o prog prog.o -L. -lonetwo -lfourfive
/usr/bin/ld: ./libfourfive.a(four.o):(.data+0x0): multiple definition of `clash'; ./libonetwo.a(one.o):(.data+0x0): first defined here
collect2: error: ld returned 1 exit status
遇到符号 clash
的 name-collision,因为它在其中两个全局定义
object 个链接需要的文件,one.o
和 four.o
:
$ readelf -s libonetwo.a libfourfive.a | egrep '(File|Symbol|OBJECT|FUNC)'
File: libonetwo.a(one.o)
Symbol table '.symtab' contains 11 entries:
9: 0000000000000000 4 OBJECT GLOBAL DEFAULT 3 clash
10: 0000000000000000 16 FUNC GLOBAL DEFAULT 1 _Z7get_onev
File: libonetwo.a(two.o)
Symbol table '.symtab' contains 10 entries:
9: 0000000000000000 15 FUNC GLOBAL DEFAULT 1 _Z7get_twov
File: libfourfive.a(four.o)
Symbol table '.symtab' contains 11 entries:
9: 0000000000000000 4 OBJECT GLOBAL DEFAULT 3 clash
10: 0000000000000000 16 FUNC GLOBAL DEFAULT 1 _Z8get_fourv
File: libfourfive.a(five.o)
Symbol table '.symtab' contains 10 entries:
9: 0000000000000000 15 FUNC GLOBAL DEFAULT 1 _Z8get_fivev
我们自己的代码 prog.(cpp|o)
中没有引用问题符号 clash
。你想知道:
Is there a way to tell the linker "complain for multiple definitions only if the symbol is used"?
不,没有,但这无关紧要。 one.o
不会从 libonetwo.a
中提取
如果链接器不需要它来解析某些符号,则链接到程序中。它需要它
解决 get_one
。同样,它只链接了 four.o
,因为它需要解析 get_four
。
所以 clash
的冲突定义在链接中。尽管 prog.o
不使用 clash
,
它确实使用 get_one
,后者使用 clash
,而 打算 使用 one.o
中 clash
的定义。
同样 prog.o
使用 get_four
,它使用 clash
并打算使用 four.o
.
中的不同定义
即使 clash
没有被每个库和程序使用,它被定义的事实
在必须链接到程序中的多个 object 文件中意味着程序将包含
它的多个定义,只有 --allow-multiple-definitions
允许。
在这种情况下,您还会看到:
Or alternatively [is there a way to] tell it, "from lib ABC ignore symbol XYZ".
一般不会飞。如果我们可以告诉链接器忽略(比如说)clash
的定义
在 four.o
中并将符号到处解析为 one.o
中的定义(唯一的其他候选者),然后 get_four()
将 return 1 而不是我们程序中的 4。那
实际上是 --allow-multiple-definitions
的效果,因为它导致了第一个定义
在要使用的链接中。
通过检查 libonetwo.a
(或 libfourfive.a
)的源代码,我们可以相当自信地找到根
问题的原因。符号 clash
保留了外部链接,其中
它只需要内部链接,因为它没有在关联的
header 文件并且在库中没有被引用,除了
在定义它的文件中。有问题的源文件应该写成:
one_good.cpp
#include <onetwo.h>
namespace {
int clash = 1;
}
int get_one()
{
return clash;
}
four_good.cpp
#include <fourfive.h>
namespace {
int clash = 4;
}
int get_four()
{
return clash;
}
一切都会好的:
$ g++ -Wall -Wextra -pedantic -I. -c one_good.cpp four_good.cpp
$ readelf -s one_good.o four_good.o | egrep '(File|Symbol|OBJECT|FUNC)'
File: one_good.o
Symbol table '.symtab' contains 11 entries:
5: 0000000000000000 4 OBJECT LOCAL DEFAULT 3 _ZN12_GLOBAL__N_15clashE
10: 0000000000000000 16 FUNC GLOBAL DEFAULT 1 _Z7get_onev
File: four_good.o
Symbol table '.symtab' contains 11 entries:
5: 0000000000000000 4 OBJECT LOCAL DEFAULT 3 _ZN12_GLOBAL__N_15clashE
10: 0000000000000000 16 FUNC GLOBAL DEFAULT 1 _Z8get_fourv
$ g++ -o prog prog.o one_good.o four_good.o
$./prog; echo $?
5
由于re-writing这样的源代码不是一个选项,我们必须将object文件修改为
同样的效果。用于此的工具是 objcopy
.
$ objcopy --localize-symbol=clash libonetwo.a libonetwo_good.a
此命令与运行:
效果相同
$ objcopy --localize-symbol=clash orig.o fixed.o
在每个 object 文件上 libonetwo(orig.o)
输出一个固定的 object 文件 fixed.o
,
并将所有 fixed.o
文件归档到一个新的静态库 libonetwo_good.a
中。和
--localize-symbol=clash
对每个 object 文件的影响是更改链接
符号 clash
,如果已定义,则从外部 (GLOBAL
) 到内部 (LOCAL)
:
$ readelf -s libonetwo_good.a | egrep '(File|Symbol|OBJECT|FUNC)'
File: libonetwo_good.a(one.o)
Symbol table '.symtab' contains 11 entries:
9: 0000000000000000 4 OBJECT LOCAL DEFAULT 3 clash
10: 0000000000000000 16 FUNC GLOBAL DEFAULT 1 _Z7get_onev
File: libonetwo_good.a(two.o)
Symbol table '.symtab' contains 10 entries:
现在链接器无法在 libonetwo_good.a(one.o)
中看到 clash
的 LOCAL
定义。
这足以避免多重定义错误,但是由于 libfourfive.a
同样的缺陷,我们也会修复它:
$ objcopy --localize-symbol=clash libfourfive.a libfourfive_good.a
然后我们可以使用固定库重新链接 prog
成功。
$ g++ -o prog prog.o -L. -lonetwo_good -lfourfive_good
$ ./prog; echo $?
5
全局符号死锁变体
在这种情况下,libonetwo.a
的来源和 header 是:
one.cpp
#include <onetwo.h>
#include "priv_onetwo.h"
int inc_one()
{
return inc(clash);
}
two.cpp
#include <onetwo.h>
#include "priv_onetwo.h"
int inc_two()
{
return inc(clash + 1);
}
priv_onetwo.cpp
#include "priv_onetwo.h"
int clash = 1;
int inc(int i)
{
return i + 1;
}
priv_onetwo.h
#pragma once
extern int clash;
extern int inc(int);
onetwo.h
#pragma once
extern int inc_one();
extern int inc_two();
而 libfourfive.a
他们是:
four.cpp
#include <fourfive.h>
#include "priv_fourfive.h"
int dec_four()
{
return dec(clash);
}
five.cpp
#include <fourfive.h>
#include "priv_fourfive.h"
int dec_five()
{
return dec(clash + 1);
}
priv_fourfive.cpp
#include "priv_fourfive.h"
int clash = 4;
int dec(int i)
{
return i - 1;
}
priv_fourfive.h
#pragma once
extern int clash;
extern int dec(int);
fourfive.h
#pragma once
extern int dec_four();
extern int dec_five();
这些库中的每一个都在构建时定义了一些共同的内部结构
在源文件中 - (priv_onetwo.cpp
|priv_fourfive.cpp
)
- 这些内部构件是全局声明的通过私人 header 建立图书馆
- (priv_onetwo.h
|priv_fourfive.h
) - 不随库分发。
它们是未记录的符号,但仍然暴露给链接器。
现在每个库中有两个文件使未定义的 (UND
) 引用
到全局符号 clash
,它在另一个文件中定义:
$ readelf -s libonetwo.a libfourfive.a | egrep '(File|Symbol|OBJECT|FUNC|clash)'
File: libonetwo.a(one.o)
Symbol table '.symtab' contains 13 entries:
9: 0000000000000000 23 FUNC GLOBAL DEFAULT 1 _Z7inc_onev
10: 0000000000000000 0 NOTYPE GLOBAL DEFAULT UND clash
File: libonetwo.a(two.o)
Symbol table '.symtab' contains 13 entries:
9: 0000000000000000 26 FUNC GLOBAL DEFAULT 1 _Z7inc_twov
10: 0000000000000000 0 NOTYPE GLOBAL DEFAULT UND clash
File: libonetwo.a(priv_onetwo.o)
Symbol table '.symtab' contains 11 entries:
9: 0000000000000000 4 OBJECT GLOBAL DEFAULT 2 clash
10: 0000000000000000 19 FUNC GLOBAL DEFAULT 1 _Z3inci
File: libfourfive.a(four.o)
Symbol table '.symtab' contains 13 entries:
9: 0000000000000000 23 FUNC GLOBAL DEFAULT 1 _Z8dec_fourv
10: 0000000000000000 0 NOTYPE GLOBAL DEFAULT UND clash
File: libfourfive.a(five.o)
Symbol table '.symtab' contains 13 entries:
9: 0000000000000000 26 FUNC GLOBAL DEFAULT 1 _Z8dec_fivev
10: 0000000000000000 0 NOTYPE GLOBAL DEFAULT UND clash
File: libfourfive.a(priv_fourfive.o)
Symbol table '.symtab' contains 11 entries:
9: 0000000000000000 4 OBJECT GLOBAL DEFAULT 2 clash
10: 0000000000000000 19 FUNC GLOBAL DEFAULT 1 _Z3deci
我们这次的节目源是:
prog.cpp
#include <onetwo.h>
#include <fourfive.h>
int main()
{
return inc_one() + dec_four();
}
和:
$ g++ -Wall -Wextra -pedantic -I. -c prog.cpp
$ g++ -o prog prog.o -L. -lonetwo -lfourfive
/usr/bin/ld: ./libfourfive.a(priv_fourfive.o):(.data+0x0): multiple definition of `clash'; ./libonetwo.a(priv_onetwo.o):(.data+0x0): first defined here
collect2: error: ld returned 1 exit status
再次 clash
被多重定义。要解析 main
中的 inc_one
,
链接器需要 one.o
,这迫使它解析 inc
,这使得它需要
priv_onetwo.o
,其中包含 clash
的第一个定义。要解析 main
中的 dec_four
,
链接器需要 four.o
,这迫使它解析 dec
,这使得它需要
priv_fourfive.o
,其中包含 clash
的竞争定义。
在这种情况下,clash
具有外部链接并不是两个库中的编码错误。
它需要有外部链接。将 clash
的定义本地化为 objcopy
libonetwo.a(priv_onetwo.o)
或 libfourfive.a(priv_fourfive.o)
将不起作用。如果我们这样做
链接将成功但输出错误程序,因为链接器将解析
clash
到另一个 object 文件中幸存的 GLOBAL
定义:然后 dec_four()
将 return 0 而不是程序中的 3,dec_five()
将 return 1 而不是 4;否则 inc_one()
将 return 5 而 inc_two()
将 return 6。
如果我们本地化 两个 定义,那么 clash
的定义将不会在
prog
的链接以满足 one.o
或 four.o
中的引用,并且对于 clash
的未定义引用将失败
这次objcopy
又来救场了,只是有不同的选择1:
$ objcopy --redefine-sym clash=clash_onetwo libonetwo.a libonetwo_good.a
这条命令的作用是新建一个静态库libonetwo_good.a
,
包含新的 object 文件,它们与 libonetwo.a
中的文件成对地相同,
除了符号 clash
到处都被替换为 clash_onetwo
:
$ readelf -s libonetwo_good.a | egrep '(File|Symbol|clash)'
File: libonetwo_good.a(one.o)
Symbol table '.symtab' contains 13 entries:
10: 0000000000000000 0 NOTYPE GLOBAL DEFAULT UND clash_onetwo
File: libonetwo_good.a(two.o)
Symbol table '.symtab' contains 13 entries:
10: 0000000000000000 0 NOTYPE GLOBAL DEFAULT UND clash_onetwo
File: libonetwo_good.a(priv_onetwo.o)
Symbol table '.symtab' contains 11 entries:
9: 0000000000000000 4 OBJECT GLOBAL DEFAULT 2 clash_onetwo
我们将用libfourfive.a
做相应的事情:
$ objcopy --redefine-sym clash=clash_fourfive libfourfive.a libfourfive_good.a
现在我们可以再次出发了:
$ g++ -o prog prog.o -L. -lonetwo_good -lfourfive_good
$ ./prog; echo $?
5
在两个解决方案中,使用 The Superflous Globals Symbols Variant 的修复程序,如果
superflous globals 是你所拥有的,尽管 The Global Symbols Deadlock Variant 的修复
也会工作。篡改 object 文件是不可取的
在编译和链接之间;它只能是不可避免的或弊端较小的。但
如果你要篡改它们,本地化一个不应该是全局的全局符号
是一种比将符号名称更改为更透明的篡改
在源代码中没有来源。
[1] 不要忘记,如果您想将 objcopy
与任何选项参数一起使用
是 C++ object 文件中的符号,您必须使用 损坏的名称
C++ 标识符比映射到符号。在这个演示代码中,碰巧的是
C++ 标识符 clash
也是 clash
。但是,如果,例如完全合格的标识符有
是 onetwo::clash
,它的错位名称将是 _ZN6onetwo5clashE
,据报道
nm
或 readelf
。当然反过来,如果您希望使用 objcopy 来更改 _ZN6onetwo5clashE
一个 object 文件到一个将 demangle 为 onetwo::klash
的符号,然后该符号将
是 _ZN6onetwo5klashE
.
我尝试构建一个链接到各种共享库和静态库的可执行文件。事实证明,两个静态库都定义了相同的符号,这导致了多重定义链接器错误。我的可执行文件不使用这个符号,所以这不是一个真正的问题。
我可以通过添加 --allow-multiple-definitions
标志来避免错误,但这似乎是一个核选项。如果我尝试使用多次定义的符号,我希望链接器发出警告。
有没有办法告诉链接器"complain for multiple definitions only if the symbol is used"?或者告诉它,"from lib ABC ignore symbol XYZ"。我正在 linux.
上使用 g++ 进行开发您可能有问题的一种变体或不同的变体, 取决于您尚未考虑其相关性的事实。或者您可能两者兼而有之,因此我将介绍针对每个变体的解决方案。
您应该熟悉静态库的性质以及它们在链接中的使用方式, as summarised here
Superflous Globals 符号变体
这里有几个源文件和一个 header 文件:
one.cpp
#include <onetwo.h>
int clash = 1;
int get_one()
{
return clash;
}
two.cpp
#include <onetwo.h>
int get_two()
{
return 2;
}
onetwo.h
#pragma once
extern int get_one();
extern int get_two();
这些已经内置到静态库中libonetwo.a
$ g++ -Wall -Wextra -pedantic -I. -c one.cpp two.cpp
$ ar rcs libonetwo.a one.o two.o
其预期 API 在 onetwo.h
同样,一些其他的源文件和一个header已经
内置于静态库 libfourfive.a
中,其预期 API
在 fourfive.h
four.cpp
#include <fourfive.h>
int clash = 4;
int get_four()
{
return clash;
}
five.cpp
#include <fourfive.h>
int get_five()
{
return 5;
}
fourfive.h
#pragma once
extern int get_four();
extern int get_five();
下面是依赖于这两个库的程序的源代码:
prog.cpp
#include <onetwo.h>
#include <fourfive.h>
int main()
{
return get_one() + get_four();
}
我们尝试这样构建:
$ g++ -Wall -Wextra -pedantic -I. -c prog.cpp
$ g++ -o prog prog.o -L. -lonetwo -lfourfive
/usr/bin/ld: ./libfourfive.a(four.o):(.data+0x0): multiple definition of `clash'; ./libonetwo.a(one.o):(.data+0x0): first defined here
collect2: error: ld returned 1 exit status
遇到符号 clash
的 name-collision,因为它在其中两个全局定义
object 个链接需要的文件,one.o
和 four.o
:
$ readelf -s libonetwo.a libfourfive.a | egrep '(File|Symbol|OBJECT|FUNC)'
File: libonetwo.a(one.o)
Symbol table '.symtab' contains 11 entries:
9: 0000000000000000 4 OBJECT GLOBAL DEFAULT 3 clash
10: 0000000000000000 16 FUNC GLOBAL DEFAULT 1 _Z7get_onev
File: libonetwo.a(two.o)
Symbol table '.symtab' contains 10 entries:
9: 0000000000000000 15 FUNC GLOBAL DEFAULT 1 _Z7get_twov
File: libfourfive.a(four.o)
Symbol table '.symtab' contains 11 entries:
9: 0000000000000000 4 OBJECT GLOBAL DEFAULT 3 clash
10: 0000000000000000 16 FUNC GLOBAL DEFAULT 1 _Z8get_fourv
File: libfourfive.a(five.o)
Symbol table '.symtab' contains 10 entries:
9: 0000000000000000 15 FUNC GLOBAL DEFAULT 1 _Z8get_fivev
我们自己的代码 prog.(cpp|o)
中没有引用问题符号 clash
。你想知道:
Is there a way to tell the linker "complain for multiple definitions only if the symbol is used"?
不,没有,但这无关紧要。 one.o
不会从 libonetwo.a
中提取
如果链接器不需要它来解析某些符号,则链接到程序中。它需要它
解决 get_one
。同样,它只链接了 four.o
,因为它需要解析 get_four
。
所以 clash
的冲突定义在链接中。尽管 prog.o
不使用 clash
,
它确实使用 get_one
,后者使用 clash
,而 打算 使用 one.o
中 clash
的定义。
同样 prog.o
使用 get_four
,它使用 clash
并打算使用 four.o
.
即使 clash
没有被每个库和程序使用,它被定义的事实
在必须链接到程序中的多个 object 文件中意味着程序将包含
它的多个定义,只有 --allow-multiple-definitions
允许。
在这种情况下,您还会看到:
Or alternatively [is there a way to] tell it, "from lib ABC ignore symbol XYZ".
一般不会飞。如果我们可以告诉链接器忽略(比如说)clash
的定义
在 four.o
中并将符号到处解析为 one.o
中的定义(唯一的其他候选者),然后 get_four()
将 return 1 而不是我们程序中的 4。那
实际上是 --allow-multiple-definitions
的效果,因为它导致了第一个定义
在要使用的链接中。
通过检查 libonetwo.a
(或 libfourfive.a
)的源代码,我们可以相当自信地找到根
问题的原因。符号 clash
保留了外部链接,其中
它只需要内部链接,因为它没有在关联的
header 文件并且在库中没有被引用,除了
在定义它的文件中。有问题的源文件应该写成:
one_good.cpp
#include <onetwo.h>
namespace {
int clash = 1;
}
int get_one()
{
return clash;
}
four_good.cpp
#include <fourfive.h>
namespace {
int clash = 4;
}
int get_four()
{
return clash;
}
一切都会好的:
$ g++ -Wall -Wextra -pedantic -I. -c one_good.cpp four_good.cpp
$ readelf -s one_good.o four_good.o | egrep '(File|Symbol|OBJECT|FUNC)'
File: one_good.o
Symbol table '.symtab' contains 11 entries:
5: 0000000000000000 4 OBJECT LOCAL DEFAULT 3 _ZN12_GLOBAL__N_15clashE
10: 0000000000000000 16 FUNC GLOBAL DEFAULT 1 _Z7get_onev
File: four_good.o
Symbol table '.symtab' contains 11 entries:
5: 0000000000000000 4 OBJECT LOCAL DEFAULT 3 _ZN12_GLOBAL__N_15clashE
10: 0000000000000000 16 FUNC GLOBAL DEFAULT 1 _Z8get_fourv
$ g++ -o prog prog.o one_good.o four_good.o
$./prog; echo $?
5
由于re-writing这样的源代码不是一个选项,我们必须将object文件修改为
同样的效果。用于此的工具是 objcopy
.
$ objcopy --localize-symbol=clash libonetwo.a libonetwo_good.a
此命令与运行:
效果相同$ objcopy --localize-symbol=clash orig.o fixed.o
在每个 object 文件上 libonetwo(orig.o)
输出一个固定的 object 文件 fixed.o
,
并将所有 fixed.o
文件归档到一个新的静态库 libonetwo_good.a
中。和
--localize-symbol=clash
对每个 object 文件的影响是更改链接
符号 clash
,如果已定义,则从外部 (GLOBAL
) 到内部 (LOCAL)
:
$ readelf -s libonetwo_good.a | egrep '(File|Symbol|OBJECT|FUNC)'
File: libonetwo_good.a(one.o)
Symbol table '.symtab' contains 11 entries:
9: 0000000000000000 4 OBJECT LOCAL DEFAULT 3 clash
10: 0000000000000000 16 FUNC GLOBAL DEFAULT 1 _Z7get_onev
File: libonetwo_good.a(two.o)
Symbol table '.symtab' contains 10 entries:
现在链接器无法在 libonetwo_good.a(one.o)
中看到 clash
的 LOCAL
定义。
这足以避免多重定义错误,但是由于 libfourfive.a
同样的缺陷,我们也会修复它:
$ objcopy --localize-symbol=clash libfourfive.a libfourfive_good.a
然后我们可以使用固定库重新链接 prog
成功。
$ g++ -o prog prog.o -L. -lonetwo_good -lfourfive_good
$ ./prog; echo $?
5
全局符号死锁变体
在这种情况下,libonetwo.a
的来源和 header 是:
one.cpp
#include <onetwo.h>
#include "priv_onetwo.h"
int inc_one()
{
return inc(clash);
}
two.cpp
#include <onetwo.h>
#include "priv_onetwo.h"
int inc_two()
{
return inc(clash + 1);
}
priv_onetwo.cpp
#include "priv_onetwo.h"
int clash = 1;
int inc(int i)
{
return i + 1;
}
priv_onetwo.h
#pragma once
extern int clash;
extern int inc(int);
onetwo.h
#pragma once
extern int inc_one();
extern int inc_two();
而 libfourfive.a
他们是:
four.cpp
#include <fourfive.h>
#include "priv_fourfive.h"
int dec_four()
{
return dec(clash);
}
five.cpp
#include <fourfive.h>
#include "priv_fourfive.h"
int dec_five()
{
return dec(clash + 1);
}
priv_fourfive.cpp
#include "priv_fourfive.h"
int clash = 4;
int dec(int i)
{
return i - 1;
}
priv_fourfive.h
#pragma once
extern int clash;
extern int dec(int);
fourfive.h
#pragma once
extern int dec_four();
extern int dec_five();
这些库中的每一个都在构建时定义了一些共同的内部结构
在源文件中 - (priv_onetwo.cpp
|priv_fourfive.cpp
)
- 这些内部构件是全局声明的通过私人 header 建立图书馆
- (priv_onetwo.h
|priv_fourfive.h
) - 不随库分发。
它们是未记录的符号,但仍然暴露给链接器。
现在每个库中有两个文件使未定义的 (UND
) 引用
到全局符号 clash
,它在另一个文件中定义:
$ readelf -s libonetwo.a libfourfive.a | egrep '(File|Symbol|OBJECT|FUNC|clash)'
File: libonetwo.a(one.o)
Symbol table '.symtab' contains 13 entries:
9: 0000000000000000 23 FUNC GLOBAL DEFAULT 1 _Z7inc_onev
10: 0000000000000000 0 NOTYPE GLOBAL DEFAULT UND clash
File: libonetwo.a(two.o)
Symbol table '.symtab' contains 13 entries:
9: 0000000000000000 26 FUNC GLOBAL DEFAULT 1 _Z7inc_twov
10: 0000000000000000 0 NOTYPE GLOBAL DEFAULT UND clash
File: libonetwo.a(priv_onetwo.o)
Symbol table '.symtab' contains 11 entries:
9: 0000000000000000 4 OBJECT GLOBAL DEFAULT 2 clash
10: 0000000000000000 19 FUNC GLOBAL DEFAULT 1 _Z3inci
File: libfourfive.a(four.o)
Symbol table '.symtab' contains 13 entries:
9: 0000000000000000 23 FUNC GLOBAL DEFAULT 1 _Z8dec_fourv
10: 0000000000000000 0 NOTYPE GLOBAL DEFAULT UND clash
File: libfourfive.a(five.o)
Symbol table '.symtab' contains 13 entries:
9: 0000000000000000 26 FUNC GLOBAL DEFAULT 1 _Z8dec_fivev
10: 0000000000000000 0 NOTYPE GLOBAL DEFAULT UND clash
File: libfourfive.a(priv_fourfive.o)
Symbol table '.symtab' contains 11 entries:
9: 0000000000000000 4 OBJECT GLOBAL DEFAULT 2 clash
10: 0000000000000000 19 FUNC GLOBAL DEFAULT 1 _Z3deci
我们这次的节目源是:
prog.cpp
#include <onetwo.h>
#include <fourfive.h>
int main()
{
return inc_one() + dec_four();
}
和:
$ g++ -Wall -Wextra -pedantic -I. -c prog.cpp
$ g++ -o prog prog.o -L. -lonetwo -lfourfive
/usr/bin/ld: ./libfourfive.a(priv_fourfive.o):(.data+0x0): multiple definition of `clash'; ./libonetwo.a(priv_onetwo.o):(.data+0x0): first defined here
collect2: error: ld returned 1 exit status
再次 clash
被多重定义。要解析 main
中的 inc_one
,
链接器需要 one.o
,这迫使它解析 inc
,这使得它需要
priv_onetwo.o
,其中包含 clash
的第一个定义。要解析 main
中的 dec_four
,
链接器需要 four.o
,这迫使它解析 dec
,这使得它需要
priv_fourfive.o
,其中包含 clash
的竞争定义。
在这种情况下,clash
具有外部链接并不是两个库中的编码错误。
它需要有外部链接。将 clash
的定义本地化为 objcopy
libonetwo.a(priv_onetwo.o)
或 libfourfive.a(priv_fourfive.o)
将不起作用。如果我们这样做
链接将成功但输出错误程序,因为链接器将解析
clash
到另一个 object 文件中幸存的 GLOBAL
定义:然后 dec_four()
将 return 0 而不是程序中的 3,dec_five()
将 return 1 而不是 4;否则 inc_one()
将 return 5 而 inc_two()
将 return 6。
如果我们本地化 两个 定义,那么 clash
的定义将不会在
prog
的链接以满足 one.o
或 four.o
中的引用,并且对于 clash
这次objcopy
又来救场了,只是有不同的选择1:
$ objcopy --redefine-sym clash=clash_onetwo libonetwo.a libonetwo_good.a
这条命令的作用是新建一个静态库libonetwo_good.a
,
包含新的 object 文件,它们与 libonetwo.a
中的文件成对地相同,
除了符号 clash
到处都被替换为 clash_onetwo
:
$ readelf -s libonetwo_good.a | egrep '(File|Symbol|clash)'
File: libonetwo_good.a(one.o)
Symbol table '.symtab' contains 13 entries:
10: 0000000000000000 0 NOTYPE GLOBAL DEFAULT UND clash_onetwo
File: libonetwo_good.a(two.o)
Symbol table '.symtab' contains 13 entries:
10: 0000000000000000 0 NOTYPE GLOBAL DEFAULT UND clash_onetwo
File: libonetwo_good.a(priv_onetwo.o)
Symbol table '.symtab' contains 11 entries:
9: 0000000000000000 4 OBJECT GLOBAL DEFAULT 2 clash_onetwo
我们将用libfourfive.a
做相应的事情:
$ objcopy --redefine-sym clash=clash_fourfive libfourfive.a libfourfive_good.a
现在我们可以再次出发了:
$ g++ -o prog prog.o -L. -lonetwo_good -lfourfive_good
$ ./prog; echo $?
5
在两个解决方案中,使用 The Superflous Globals Symbols Variant 的修复程序,如果 superflous globals 是你所拥有的,尽管 The Global Symbols Deadlock Variant 的修复 也会工作。篡改 object 文件是不可取的 在编译和链接之间;它只能是不可避免的或弊端较小的。但 如果你要篡改它们,本地化一个不应该是全局的全局符号 是一种比将符号名称更改为更透明的篡改 在源代码中没有来源。
[1] 不要忘记,如果您想将 objcopy
与任何选项参数一起使用
是 C++ object 文件中的符号,您必须使用 损坏的名称
C++ 标识符比映射到符号。在这个演示代码中,碰巧的是
C++ 标识符 clash
也是 clash
。但是,如果,例如完全合格的标识符有
是 onetwo::clash
,它的错位名称将是 _ZN6onetwo5clashE
,据报道
nm
或 readelf
。当然反过来,如果您希望使用 objcopy 来更改 _ZN6onetwo5clashE
一个 object 文件到一个将 demangle 为 onetwo::klash
的符号,然后该符号将
是 _ZN6onetwo5klashE
.