Kri*_*isB 6 c unix linker shared-libraries static-libraries
摘要:
STATIC和SHARED lib之间的交叉使用函数导致STATIC lib的所有对象(甚至未使用!)都包含在最终的二进制文件中!
你不明白我的意思吗?:-P
坐下来阅读下面的完整故事!名字已经改变,以保护无辜.示例的目标是简单性和可重复性.
预告片:有一个SSCCE可用!(简短,自包含,正确(可编译),例如:http://www.sscce.org/ )
一开始,我有:
一个binary(main)调用fun1a()存储在STATIC lib(libsub.a)中的function (). main还有一个内部函数(mainsub()).
一个STATIC lib(libsub.a),它包含SEVERAL对象,每个对象都有其他几个使用的函数.
main在二进制文件中编译结果,该二进制文件只包含包含引用函数的对象(STATIC lib)的副本.在下面的示例中,main将只包含object的函数shared1.o(因为main是调用func1a())和NOT函数shared2.o(因为没有引用).
好 !
main.c libsub.a
+-------------+ +------------+
| main | | shared1.o |
| func1a() | <----> | func1a() |
| mainsub() | | func1b() |
+-------------+ | ---- |
| shared2.o |
| func2a() |
| func2b() |
+------------+
Run Code Online (Sandbox Code Playgroud)
作为改进,我想允许"外部"的人能够覆盖函数调用中main通过自己的代码,而无需重新编译我的二进制文件.
无论如何我没有提供源代码,也没有提供我的静态库.
为此,我打算提供一个"准备填充"功能骨架源.(那称为USER-EXIT?!)使用SHARED/DYNAMIC lib可以做到恕我直言.可以覆盖的函数是main(mainsub())或共享函数(func1a()...)的内部函数,并且将存储在共享库(.so)中以在链接期间添加/引用.
创建了新的源代码,前缀为"c",其中包含"标准"函数的"客户端"版本.使用(或不使用)覆盖功能的切换超出范围.只要采取如果UE是真的,那么就覆盖了.
cmain.c是一个新的来源Client_mainsub(),可以被称为"替代"mainsub()
cshared1.c是一个新的来源Client_func1a(),可以被称为"替代" func1a().事实上,所有功能都shared1.c可以替代cshared1.c
cshared2.c是一个新的来源Client_func2a(),可以被称为"替代"func2a()
概述变为:
main.c libsub.a clibsub.so
+-----------------------+ +------------------------+ +--------------------+
| main | | shared1.o | | cshared1.o |
| func1a() {} | | func1a() | | Client_func1a() |
| mainsub() | <-> | { if UE | <-> | {do ur stuff } |
| { if UE | | Client_func1a() | | |
| Client_mainsub() | | return } | | cshared2.o |
| return }| | func1b() | | Client_func2a() |
+-----------------------+ | ------- | >| {do ur stuff } |
^ | shared2.o | / +--------------------+
cmain.c v | func2a() | /
+--------------------+ | { if UE | /
| cmain | | Client_func2a() |<
| Client_mainsub() | | return } |
| {do ur stuff } | | func2b() |
+--------------------+ +------------------------+
Run Code Online (Sandbox Code Playgroud)
这里再次,因为main不调用func2a()nor func2b(),(STATIC)对象shared2.o不包含在二进制文件中,也不Client_func2a()存在对(SHARED)的引用.好 !
最后,简单地覆盖功能是不够的(或太多了!).我想外部的人能够调用我的函数(或没有)......但ALSO让他们做一些东西的权利BEFORE和/或右后我的功能.
因此,我们将粗略地使用伪代码而不是被func2a()愚蠢地替换Client_func2a()为:
shared2.c | cshared2.c
(assume UE=true)
|
func2a() { |Client_func2a() {
if UE {} |
Client_func2a() ==> do (or not) some stuf PRE call
|
| if (DOIT) { // activate or not standard call
| UE=false
| func2a() // do standard stuff
| UE=true
| } else
| { do ur bespoke stuff }
|
| do (or not) some stuf POST call
| }
<==
} else
{ do standard stuff }
}
Run Code Online (Sandbox Code Playgroud)
请记住,cshared2.c提供给其他人可以(或不)在提供的骨架上做自己的东西.
(注意:设置UE为false并返回true以Client_func2a()避免func2a()调用中的无限循环!;-))
现在来了我的问题.
在那种情况下,结果二进制现在包括shared2.o对象,尽管没有调用主要在任何函数shared2.c也没有cshared2.c!!!!!
搜索之后看起来是因为交叉调用/引用:
shared2.o contains func2a() that may call Client_func2a()
cshared2.o contains Client_func2a() that may call func2a()
Run Code Online (Sandbox Code Playgroud)
那么为什么main二进制包含shared2.o?
>dump -Tv main
main:
***Loader Section***
***Loader Symbol Table Information***
[Index] Value Scn IMEX Sclass Type IMPid Name
[0] 0x00000000 undef IMP RW EXTref libc.a(shr_64.o) errno
[1] 0x00000000 undef IMP DS EXTref libc.a(shr_64.o) __mod_init
[2] 0x00000000 undef IMP DS EXTref libc.a(shr_64.o) exit
[3] 0x00000000 undef IMP DS EXTref libc.a(shr_64.o) printf
[4] 0x00000000 undef IMP RW EXTref libc.a(shr_64.o) __n_pthreads
[5] 0x00000000 undef IMP RW EXTref libc.a(shr_64.o) __crt0v
[6] 0x00000000 undef IMP RW EXTref libc.a(shr_64.o) __malloc_user_defined_name
[7] 0x00000000 undef IMP DS EXTref libcmain.so Client_mainsub1
[8] 0x00000000 undef IMP DS EXTref libcshared.so Client_func1b
[9] 0x00000000 undef IMP DS EXTref libcshared.so Client_func1a
[10] 0x00000000 undef IMP DS EXTref libcshared.so Client_func2b <<< but why ??? ok bcoz func2b() is referenced ...
[11] 0x00000000 undef IMP DS EXTref libcshared.so Client_func2a <<< but why ??? ok bcoz func2a() is referenced ...
[12] 0x110000b50 .data ENTpt DS SECdef [noIMid] __start
[13] 0x110000b78 .data EXP DS SECdef [noIMid] func1a
[14] 0x110000b90 .data EXP DS SECdef [noIMid] func1b
[15] 0x110000ba8 .data EXP DS SECdef [noIMid] func2b <<< but why this ? Not a single call is made in main ???
[16] 0x110000bc0 .data EXP DS SECdef [noIMid] func2a <<< but why this ? Not a single call is made in main ???
Run Code Online (Sandbox Code Playgroud)
Note that simply putting in comment func2a() ( and func2b() ) solves the link issue (breaking the cross)... but it's not possible as I would like to keep a shared lib !?
The behavior is happening on AIX 7.1 with IBM XL C/C++ 12.1 , but it looks to be the same on Linux (Red Hat 5 + GCC 5.4 with some small changed in compilation param)
IBM XL C/C++ for AIX, V12.1 (5765-J02, 5725-C72)
Version: 12.01.0000.0000
Driver Version: 12.01(C/C++) Level: 120315
C Front End Version: 12.01(C/C++) Level: 120322
High-Level Optimizer Version: 12.01(C/C++) and 14.01(Fortran) Level: 120315
Low-Level Optimizer Version: 12.01(C/C++) and 14.01(Fortran) Level: 120321
Run Code Online (Sandbox Code Playgroud)
So I figure out this is surely a misunderstanding. Can anyone explain ?
As promised here are the SSCCE. You can replay my problem by recreating/downloading the following small files and run go.sh (see comment inside the script)
Edit1 : added code into the question, not on external site as suggested
main.c
#include <stdio.h>
#include "inc.h"
extern void func1a (), func1b ();
int UEXIT(char* file, char* func)
{
printf(" UEXIT file=<%s> func=<%s>\n",file,func);
return 1; /* always true for testing */
}
main (){
printf(">>> main\n");
func1a ();
mainsub ();
printf("<<< main\n");
}
mainsub () {
printf(">>> mainsub\n");
if(UEXIT("main","mainsub")) {
Client_mainsub1();
return;
}
printf("<<< mainsub\n");
}
Run Code Online (Sandbox Code Playgroud)
cmain.c
#include <stdio.h>
#include "inc.h"
void Client_mainsub1 () {
printf(">>>>>> Client_mainsub1\n");
printf("<<<<<< Client_mainsub1\n");
return;
}
Run Code Online (Sandbox Code Playgroud)
inc.h
extern int UEXIT(char * fileName, char * functionName);
Run Code Online (Sandbox Code Playgroud)
shared1.c
#include <stdio.h>
#include "inc.h"
void func1a (){
printf(">>>>> func1a\n");
if(UEXIT("main","func1a")) {
Client_func1a();
return;
}
printf("<<<<< func1a\n");
}
void func1b (){
printf(">>>>> func1b\n");
if(UEXIT("main","func1b")){
Client_func1b();
return;
}
printf("<<<<< func1b\n");
}
Run Code Online (Sandbox Code Playgroud)
shared2.c
#include <stdio.h>
#include "inc.h"
void func2a (){
printf(">>>>> func2a\n");
if(UEXIT("main","func2a")) {
Client_func2a();
return;
}
printf("<<<<< func2a\n");
}
void func2b (){
printf(">>>>> func2b\n");
if(UEXIT("main","func2b")){
Client_func2b();
return;
}
printf("<<<<< func2b\n");
}
Run Code Online (Sandbox Code Playgroud)
cshared1.c
#include <stdio.h>
#include "inc.h"
void Client_func1a () {
int standardFunctionCall = 0;
printf("\t>>>> Client_func1a\n");
if (standardFunctionCall) {
func1a();
}
printf("\t<<< Client_func1a\n");
return;
}
void Client_func1b () {
int standardFunctionCall = 0;
printf("\t>>>> Client_func1b\n");
if (standardFunctionCall) {
func1b();
}
printf("\t<<< Client_func1b\n");
return;
}
Run Code Online (Sandbox Code Playgroud)
cshared2.c
#include <stdio.h>
#include "inc.h"
void Client_func2a () {
int standardFunctionCall = 0;
printf("\t>>>> Client_func2a\n");
if (standardFunctionCall) {
func2a(); /* !!!!!! comment this to avoid crossed link with shared2.c !!!!! */
}
printf("\t<<< Client_func2a\n");
return;
}
void Client_func2b () {
int standardFunctionCall = 0;
printf("\t>>>> Client_func2b\n");
if (standardFunctionCall) {
func2b(); /* !!!!!! ALSO comment this to avoid crossed link with shared2.c !!!!! */
}
printf("\t<<< Client_func2b\n");
return;
}
Run Code Online (Sandbox Code Playgroud)
go.sh
#!/bin/bash
## usage :
## . ./go.sh
## so that the redefinition of LIBPATH is propagated to calling ENV ...
## otherwise : "Dependent module libcshared.so could not be loaded."
# default OBJECT_MODE to 64 bit (avoid explicitely setting -X64 options...)
export OBJECT_MODE=64
export LIBPATH=.:$LIBPATH
# Compile client functions for target binary
cc -q64 -c -o cmain.o cmain.c
# (1) Shared lib for internal function
cc -G -q64 -o libcmain.so cmain.o
# Compile common functions
cc -c shared2.c shared1.c
# Compile client common functions overwrite
cc -c cshared2.c cshared1.c
# (2) Built libsub.a for common functions (STATIC)
ar -rv libsub.a shared1.o shared2.o
# (3) Built libcshared.so for client common functions overwrite (SHARED)
cc -G -q64 -o libcshared.so cshared1.o cshared2.o
# Finally built binary using above (1) (2) (3)
# main only call func1a() , so should only include objects shared1
# But pragmatically shared2 is also included if cshared2 reference a possible call to func2() in shared2 !!!!????
# Check this with "nm main |grep shared2" or "nm main |grep func2" or "dump -Tv main |grep func2"
cc -q64 -o main main.c -bstatic libsub.a -bshared libcmain.so libcshared.so
# result is the same without specifying -bstatic or -bshared
#cc -q64 -o main2 main.c libsub.a libcmain.so libcshared.so
#If I split libcshared.so into libcshared1.so and libcshared2.so it is also the same :
#cc -G -q64 -o libcshared1.so cshared1.o
#cc -G -q64 -o libcshared2.so cshared2.o
#cc -q64 -o main4 main.c -bstatic libsub.a -bshared libcmain.so libcshared1.so libcshared2.so
#If I do not inlcude libcshared2.so, binary is of course well working, without reference to cshared2 nor shared2 .
# So why linker chooses to add STATIC shared2.o only if libcshared2.so is listed ?
# Is there a way to avoid this add of unused code ?
#cc -q64 -o main4 main.c -bstatic libsub.a -bshared libcmain.so libcshared1.so
Run Code Online (Sandbox Code Playgroud)
Edit2 : added RedHat version of go.sh script as requested
gored.sh
## usage :
## . ./gored.sh
## so that the redefinition of LD_LIBRARY_PATH is propagated to calling ENV ...
## otherwise : "Dependent module libcshared.so could not be loaded."
export LD_LIBRARY_PATH=.:$LD_LIBRARY_PATH
# Compile client functions for target binary
gcc -fPIC -c cmain.c
# (1) Shared lib for internal function
gcc -shared -o libcmain.so cmain.o
# Compile common functions
gcc -c shared2.c shared1.c
# Compile client common functions overwrite
gcc -fPIC -c cshared2.c cshared1.c
# (2) Built libsub.a for common functions (STATIC)
ar -rv libsub.a shared1.o shared2.o
# (3) Built libcshared.so for client common functions overwrite (SHARED)
gcc -shared -o libcshared.so cshared1.o cshared2.o
# Finally built binary using above (1) (2) (3)
# main only call func1a() , so should only include objects shared1
# But pragmatically shared2 is also included if cshared2 reference a possible call to func2() in shared2 !!!!????
# Check this with "nm main |grep shared2" or "nm main |grep func2" or "dump -Tv main |grep func2"
gcc -o main main.c libcmain.so libcshared.so libsub.a
#If I split libcshared.so into libcshared1.so and libcshared2.so it is also the same :
gcc -shared -o libcshared1.so cshared1.o
gcc -shared -o libcshared2.so cshared2.o
cc -o main2 main.c libcmain.so libcshared1.so libcshared2.so libsub.a
#If I do not inlcude libcshared2.so, binary is of course well working, without reference to cshared2 nor shared2 .
# So why linker chooses to add STATIC shared2.o only if libcshared2.so is listed ?
# Is there a way to avoid this add of unused code ?
cc -o main3 main.c libcmain.so libcshared1.so libsub.a
Run Code Online (Sandbox Code Playgroud)
Or here the full above files (without gored.sh) in a single .tar.bz2. (6KB).
Just copy/paste in a new file (ex poc.uue). Then type
uudecode poc.uue
Run Code Online (Sandbox Code Playgroud)
and you should get poc.tar.bz2
unzip, untar go into poc folder and run
. ./go.sh
Run Code Online (Sandbox Code Playgroud)
then
dump -Tv main
Run Code Online (Sandbox Code Playgroud)
or if under RedHat
nm main
Run Code Online (Sandbox Code Playgroud)
example of result after gored.sh :
poc>nm main |grep func2
* U Client_func2a
U Client_func2b
0000000000400924 T func2a
000000000040095d T func2b
poc>nm main2 |grep func2
U Client_func2a
U Client_func2b
0000000000400934 T func2a
000000000040096d T func2b
poc>nm main3 |grep func2
poc>
Run Code Online (Sandbox Code Playgroud)
Edit3: ASCII ART ! :-)
Here's the 'visual' final state with unused objects/references I think the linker is wrong to include. Or at least not smart enough to detect as unused.
Maybe that's normal or there's an option to avoid having unused static code in final binary. This doesn't look as a complex situation as the surounded tagged 'UNUSED !?' code is linked with nothing ? Isn't it ?
main.c libsub.a clibsub.so
+-----------------------+ +-------------------------+ +-----------------------------+
| main | | +---------------------+ | | +-------------------------+ |
| func1a(); <-------------\ | |shared1.o | | | | cshared1.o | |
| mainsub() | \------>func1a() { <-------------+ /-----> Client_func1a() { | |
| { if UE { | | | if UE { | | | / | | PRE-stuff | |
| Client_mainsub() | | | Client_func1a() <-----C---/ | | if (DOIT) { | |
| return ^ | | | return | | | | | UE=false | |
| } | | | | } else { | | +----------------> func1a() | |
| } | | | | do std stuff | | | | UE=true | |
+-------------|---------+ | | } | | | | } else { | |
| | | | | | | do bespoke stuff | |
| | | func1b() { | | | | } | |
| | | same as above | | | | POST-stuff | |
| | | } | | | | } | |
| | +---------------------+ | | | Client_func1b() {} | |
| | | | +-------------------------+ |
| ***|*******U*N*U*S*E*D**?!***|*****U*N*U*S*E*D**?!*******U*N*U*S*E*D**?!****
| * | +---------------------+ | | +-------------------------+ | *
| U | |shared2.o | | | | cshared2.o | | U
| * | | func2a() { <-------------+ /-----> Client_func2a() { | | *
| N | | if UE { | | | / | | PRE-stuff | | N
cmain.so | * | | Client_func2a())<-----C---/ | | if (DOIT) { | | *
+-------------|------+ U | | return | | | | | UE=false | | U
| cmain.o v | * | | } else { | | +----------------> func2a() | | *
| Client_mainsub() | S | | do std stuff | | | | UE=true | | S
| {do ur stuff } | * | | } | | | | } else { | | *
+--------------------+ E | | | | | | do bespoke stuff | | E
* | | func2b() { | | | | } | | *
D | | same as above | | | | POST-stuff | | D
* | | } | | | | Client_func2b() {} | | *
* | +---------------------+ | | +-------------------------+ | *
? +-------------------------+ +---------------------------+ | ?
! !
*********U*N*U*S*E*D**?!*************U*N*U*S*E*D**?!******U*N*U*S*E*D**?!***
Run Code Online (Sandbox Code Playgroud)
Any constructive answer to put me on the right way is welcome.
Thanks.
以下是令您困惑的链接器行为的简化说明:
主文件
extern void foo(void);
int main(void)
{
foo();
return 0;
}
Run Code Online (Sandbox Code Playgroud)
foo.c
#include <stdio.h>
void foo(void)
{
puts(__func__);
}
Run Code Online (Sandbox Code Playgroud)
酒吧
#include <stdio.h>
extern void do_bar(void);
void bar(void)
{
do_bar();
}
Run Code Online (Sandbox Code Playgroud)
do_bar.c
#include <stdio.h>
void do_bar(void)
{
puts(__func__);
}
Run Code Online (Sandbox Code Playgroud)
让我们将所有这些源文件编译为目标文件:
$ gcc -Wall -c main.c foo.c bar.c do_bar.c
Run Code Online (Sandbox Code Playgroud)
现在我们将尝试链接一个程序,如下所示:
$ gcc -o prog main.o foo.o bar.o
bar.o: In function `bar':
bar.c:(.text+0x5): undefined reference to `do_bar'
Run Code Online (Sandbox Code Playgroud)
未定义功能do_bar仅在定义引用bar,并bar在所有的程序不被引用。那么为什么联动失败呢?
很简单,这个链接失败了,因为我们告诉链接器链接 bar.o
到程序中;确实如此;并bar.o包含 的定义bar,其中引用了do_bar,而在链接中未定义。bar不被引用,但do_bar 就是-通过bar,这是在程序链接。
默认情况下,链接器要求在链接中定义程序链接中引用的任何符号。如果我们强迫它链接 的定义bar,那么它将需要 的定义do_bar,因为没有定义do_bar它实际上并没有得到的定义bar。如果链接 的定义bar,它不会质疑我们是否需要链接它,然后do_bar如果答案是否定的,则允许未定义的引用。
联动故障当然可以通过以下方式修复:
$ gcc -o prog main.o foo.o bar.o do_bar.o
$ ./prog
foo
Run Code Online (Sandbox Code Playgroud)
现在在这个插图中,bar.o程序中的链接只是毫无意义的。我们也可以通过不告诉链接器链接来成功链接bar.o。
gcc -o prog main.o foo.o
$ ./prog
foo
Run Code Online (Sandbox Code Playgroud)
bar.o和do_bar.o对于执行来说都是多余的main,但是程序只能与两者链接,或者两者都不链接
但是假设foo和bar被定义在同一个文件中?
它们可能在同一个目标文件中定义foobar.o:
ld -r -o foobar.o foo.o bar.o
Run Code Online (Sandbox Code Playgroud)
进而:
$ gcc -o prog main.o foobar.o
foobar.o: In function `bar':
(.text+0x18): undefined reference to `do_bar'
collect2: error: ld returned 1 exit status
Run Code Online (Sandbox Code Playgroud)
现在,链接器无法在不链接 的定义的foo情况下链接 的定义bar。所以再一次,我们必须链接 的定义do_bar:
$ gcc -o prog main.o foobar.o do_bar.o
$ ./prog
foo
Run Code Online (Sandbox Code Playgroud)
像这样链接,prog包含foo,bar和 的定义do_bar:
$ nm prog | grep -e foo -e bar
000000000000065d T bar
0000000000000669 T do_bar
000000000000064a T foo
Run Code Online (Sandbox Code Playgroud)
( T= 定义的函数符号)。
同样,foo并且bar可能在同一个共享库中定义:
$ gcc -Wall -fPIC -c foo.c bar.c
$ gcc -shared -o libfoobar.so foo.o bar.o
Run Code Online (Sandbox Code Playgroud)
然后这个链接:
$ gcc -o prog main.o -L. -lfoobar -Wl,-rpath=$(pwd)
./libfoobar.so: undefined reference to `do_bar'
collect2: error: ld returned 1 exit status
Run Code Online (Sandbox Code Playgroud)
和以前一样失败,并且可以以相同的方式修复:
$ gcc -o prog main.o do_bar.o -L. -lfoobar -Wl,-rpath=$(pwd)
$ ./prog
foo
Run Code Online (Sandbox Code Playgroud)
当我们链接共享库libfoobar.so而不是目标文件时foobar.o,我们prog有一个不同的符号表:
$ nm prog | grep -e foo -e bar
00000000000007aa T do_bar
U foo
Run Code Online (Sandbox Code Playgroud)
这一次,prog不包含foo或 的定义bar。它包含对 的未定义引用 ( U) foo,因为它调用foo,当然,现在在运行时, 中的定义将满足该引用libfoobar.so。甚至没有对 的未定义引用bar,也不应该有,因为程序从不调用bar.
但尽管如此,prog包含了定义的do_bar,这是目前从符号表中的所有功能未被引用。
这与您自己的 SSCCE 相呼应,但方式不那么复杂。在你的情况下:
目标文件libsub.a(shared2.o)链接到程序中以提供func2a和 的定义func2b。
必须找到并链接这些定义,因为它们分别在 和 的定义中被引用,而Client_func2a
和Client_func2b定义在 中libcshared.so。
libcshared.so必须链接以提供 的定义Client_func1a。
的定义Client_func1a必须找到和链接,因为它是从定义中引用func1a。
而func1a被称为main。
这就是为什么我们看到:
$ nm main | grep func2
U Client_func2a
U Client_func2b
00000000004009f7 T func2a
0000000000400a30 T func2b
Run Code Online (Sandbox Code Playgroud)
在程序的符号表中。
将定义链接到程序中用于它不调用的函数的情况并不少见。它通常以我们所见的方式发生:链接,递归地解析以 开头的符号引用main,发现它需要 的定义f,它只能通过链接一些目标文件来获得file.o,并且file.o
还链接了函数的定义g,从不调用。
什么是相当奇怪的是有一个程序像你落得main和喜欢我的最后一个版本prog,其中包含一个多余的功能(例如的定义do_bar链接到的决心引用来自其他多余的功能(例如)的定义bar),是不是程序中定义。即使有冗余的函数定义,通常我们也可以将它们链接回链接中的一个或多个目标文件,其中第一个冗余定义与一些必要的定义一起被拉入。
这种奇怪的情况是在以下情况下引起的:
gcc -o prog main.o do_bar.o -L. -lfoobar -Wl,-rpath=$(pwd)
Run Code Online (Sandbox Code Playgroud)
因为必须链接第一冗余函数定义(bar)由连接提供的共享库,libfoobar.so同时的定义do_bar
是由要求的bar是不在于共享库,或任何其他共享库,但在一个目标文件。
当程序与该共享库链接时,bar由 提供的定义libfoobar.so将保留在那里。它不会物理链接到程序中。这就是动态链接的本质。但是链接所需的任何目标文件——无论是独立的目标文件do_bar.o还是链接器从存档中提取的目标文件libsub.a(shared2.o)——只能物理链接到程序中。所以冗余do_bar出现在 的符号表中prog。但是bar解释了为什么do_bar存在
的冗余不存在。它在 的符号表中libfoobar.so。
当您发现程序中的死代码时,您可能希望链接器更智能。通常,它可以更智能,但需要付出一些额外的努力。您需要要求它进行垃圾收集部分,在此之前,您需要要求编译器通过在目标文件中生成数据部分和 功能部分来准备方式。请参阅如何使用 GCC 和 ld 删除未使用的 C/C++ 符号?,以及 答案
但是这种修剪死代码的方法在不寻常的情况下不起作用,即死代码在程序中被链接以满足链接 所需的共享库的冗余引用。链接器只能从它输出到程序中的部分中递归地收集未使用的部分,并且它只输出从目标文件输入的部分,而不是来自要动态链接的共享库的部分。
避免您main和我的死代码的正确方法prog是不要做那种特殊的链接,其中共享库将包含程序未调用但必须通过将死对象代码链接到您的程序来解决的未定义引用.
相反,当你构建一个共享库时,要么不要在其中留下任何未定义的引用,要么只留下由它自己的动态依赖项满足的未定义引用。
因此,构建我的正确方法libfoobar.so是:
$ gcc -shared -o libfoobar.so foo.o bar.o do_bar.o
Run Code Online (Sandbox Code Playgroud)
这为我提供了一个具有以下 API 的共享库:
void foo(void);
void bar(void);
Run Code Online (Sandbox Code Playgroud)
对于想要其中一个或两个的任何人,并且没有未定义的引用。然后我构建我的程序,它只是一个客户端foo:
$ gcc -o prog main.o -L. -lfoobar -Wl,-rpath=$(pwd)
$ ./prog
foo
Run Code Online (Sandbox Code Playgroud)
它不包含死代码:
$ nm prog | grep -e foo -e bar
U foo
Run Code Online (Sandbox Code Playgroud)
同样,如果您构建libshared.so没有未定义的引用,例如:
$ gcc -c -fPIC shared2.c shared1.c
$ ar -crs libsub.a shared1.o shared2.o
$ gcc -shared -o libcshared.so cshared1.o cshared2.o -L. -lsub
Run Code Online (Sandbox Code Playgroud)
然后链接您的程序:
$ gcc -o main main.c libcmain.so libcshared.so
Run Code Online (Sandbox Code Playgroud)
它也不会有死代码:
$ nm main | grep func
U func1a
Run Code Online (Sandbox Code Playgroud)
如果你不喜欢的事实,libsub.a(shared1.o)并libsub.a(shared2.o)
成为物理链接到libcshared.so该解决方案,然后采取
其他正统的方法来连接一个共享库:把所有的func*未定义功能libcshared.so:请libsub 还
一个共享库,然后是一个动态的依赖libcshared.so。
| 归档时间: |
|
| 查看次数: |
496 次 |
| 最近记录: |