繁体   English   中英

当SHARED lib引用它们时,为什么STATIC lib中的未使用对象包含在最终二进制文件中?

[英]Why unused objects in STATIC lib included in final binary when SHARED lib reference them?

摘要:

STATIC和SHARED lib之间的交叉使用功能导致STATIC lib的所有对象(甚至未使用!)都包含在最终的二进制文件中!

你不明白我的意思吗? :-P

坐下来阅读下面的完整故事! 名字已经改变,以保护无辜。 示例的目标是简单性和可重复性。

预告片:有一个SSCCE可用! (简短,自包含,正确(可编译),例如: http ://www.sscce.org/)

一开始,我有:

  • 二进制( main )调用存储在STATIC库( libsub.a )中的函数( fun1a() )。 main还有一个内部函数( mainsub() )。

  • 静态库( libsub.a由含有每几个对象与由其它源使用的几个函数)。

在二进制文件中编译main结果,该二进制文件包含包含引用函数的对象(STATIC lib)的副本。 在下面的示例中, main将只包含object shared1.o函数(因为main调用func1a() )而不是 shared2.o函数(因为没有引用)。

好 !

  main.c                 libsub.a    
+-------------+        +------------+
| main        |        | shared1.o  |
|  func1a()   | <----> |   func1a() |
|  mainsub()  |        |   func1b() |
+-------------+        |    ----    |
                       | shared2.o  |
                       |   func2a() |
                       |   func2b() |
                       +------------+

作为一项改进,我想让“外部”人员能够用他们自己的代码覆盖main调用的函数,而不必重新编译MY二进制文件。

无论如何我没有提供源代码,也没有提供我的静态库。

为此,我打算提供一个“准备填充”功能骨架源。 (那称为USER-EXIT?!)使用SHARED / DYNAMIC lib可以做到恕我直言。 可以覆盖的函数是main( mainsub() )的内部函数或共享函数( func1a() ...),并且将存储在共享库(.so)中以在链接期间添加/引用。

创建了新的源代码,前缀为“c”,其中包含“标准”函数的“客户端”版本。 使用(或不使用)覆盖功能的切换超出范围。 如果UE是真的那么就是这样,然后被覆盖。

cmain.c是一个包含Client_mainsub()的新源,可以被称为'替换'的mainsub()

cshared1.c是含有新的源Client_func1a()可以“在替换”的被称为func1a() 实际上, shared1.c所有函数shared1.c可以在cshared1.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()             |
   +--------------------+        +------------------------+

这里再次,作为main不调用func2a()func2b() ,(STATIC)对象shared2.o不包含在二进制文件中,也不存在对(SHARED) Client_func2a()引用。 好 !


最后,简单地覆盖功能是不够的(或太多了!)。 我想外部的人能够调用我的函数(或没有)...... 让他们做一些正确的东西, 之前和/或右我的功能

因此,不要将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 }
}

请记住, cshared2.c是提供给其他人,他们可以(或不)在提供的骨架上做自己的东西。

(注意:在Client_func2a() UE设置为false并返回true会避免func2a()调用中的无限循环!;-))

现在来了我的问题。

在这种情况下,结果二进制现在包括shared2.o尽管NO呼叫对象在主作出的任何功能shared2.c也不cshared2.c !!!!!

搜索之后看起来是因为交叉调用/引用:

shared2.o contains func2a() that may call Client_func2a()
cshared2.o contains Client_func2a() that may call func2a()

那么为什么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 ???

请注意,简单地放入注释func2a() (和func2b() )可以解决链接问题(打破交叉)......但是这不可能,因为我想保留共享库!

这种行为发生在使用IBM XL C / C ++ 12.1的AIX 7.1上,但在Linux上看起来是相同的(Red Hat 5 + GCC 5.4,编译参数中有一些小变化)

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

所以我发现这肯定是一种误解。 谁能解释一下?


正如这里所承诺的那样是SSCCE。 您可以通过重新创建/下载以下小文件来重播我的问题并运行go.sh(请参阅脚本内的注释)

Edit1 :在问题中添加了代码,而不是建议的外部网站

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");
}

cmain.c

#include <stdio.h>
#include "inc.h"

void Client_mainsub1 () {
    printf(">>>>>> Client_mainsub1\n");
    printf("<<<<<< Client_mainsub1\n");
return;
}

英寸

extern int UEXIT(char * fileName, char * functionName);

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");
}

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");
}

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;
}

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;
}

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

Edit2 :根据请求添加了RedHat版本的go.sh脚本

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

或者在这里完整的上述文件(没有gored.sh)在一个.tar.bz2中。 (6KB)。

https://pastebin.com/KsaqacAu

只需复制/粘贴一个新文件(ex poc.uue )。 然后输入

uudecode poc.uue

你应该得到poc.tar.bz2

解压缩,解压进入poc文件夹然后运行

. ./go.sh

然后

dump -Tv main 

或者如果在RedHat下

nm main

gored.sh之后的结果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>

Edit3:ASCII ART! :-)
这是使用未使用的对象/引用的“可视”最终状态我认为链接器包含错误。 或者至少不够智能,无法检测到未使用。 也许这是正常的,或者有一个选项可以避免在最终二进制文件中使用未使用的静态代码。 这看起来并不是一个复杂的情况,因为被标记为'未使用!' 代码没有任何关联? 不是吗?

     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**?!***     

任何建设性的答案让我走上正确的道路是受欢迎的。

谢谢。

以下是令您困惑的链接器行为的简化说明:

main.c中

extern void foo(void);

int main(void)
{
    foo();
    return 0;
}

foo.c的

#include <stdio.h>

void foo(void)
{
    puts(__func__);
}

bar.c

#include <stdio.h>

extern void do_bar(void);

void bar(void)
{
    do_bar();
}

do_bar.c

#include <stdio.h>

void do_bar(void)
{
    puts(__func__);
}

让我们将所有这些源文件编译为目标文件:

$ gcc -Wall -c main.c foo.c bar.c do_bar.c

现在我们将尝试链接程序,如下所示:

$ gcc -o prog main.o foo.o bar.o
bar.o: In function `bar':
bar.c:(.text+0x5): undefined reference to `do_bar'

未定义的函数do_bar仅在bar的定义中引用,并且bar根本不在程序中引用。 为什么然后联系失败?

很简单,这个链接失败了,因为我们告诉链接器将 bar.o 链接 到程序中 ; 它确实如此; bar.o包含bar的定义,它引用do_bar ,它没有在链接中定义。 bar未被引用,但是do_bar - by bar ,它在程序中链接。

默认情况下,链接器要求在链接中定义程序链接中引用的任何符号。 如果我们强迫它链接bar的定义,那么它将要求do_bar的定义,因为没有do_bar的定义它实际上没有 bar的定义。 如果它链接了bar的定义,它不会质疑我们是否需要链接它,如果答案是否,则允许对do_bar进行未定义的引用。

链接失败可以通过以下方式解决:

$ gcc -o prog main.o foo.o bar.o do_bar.o
$ ./prog
foo

现在在这个例子中,在程序中链接bar.o简直是无偿的。 我们也可以通过告诉链接器链接bar.o来成功链接。

gcc -o prog main.o foo.o
$ ./prog
foo

bar.odo_bar.o均是多余的,用于执行main ,但是程序只能用首尾相连,或既没有

但是假设foobar在同一个文件中定义了?

它们可能在同一个目标文件foobar.o

ld -r -o foobar.o foo.o bar.o

然后:

$ 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

现在,链接器无法链接foo的定义而不链接bar的定义。 所以我们必须再次链接do_bar的定义:

$ gcc -o prog main.o foobar.o do_bar.o
$ ./prog
foo

像这样链接, prog包含foobardo_bar定义:

$ nm prog | grep -e foo -e bar
000000000000065d T bar
0000000000000669 T do_bar
000000000000064a T foo

T =定义的函数符号)。

同样, foobar可能在同一个共享库中定义:

$ gcc -Wall -fPIC -c foo.c bar.c
$ gcc -shared -o libfoobar.so foo.o bar.o

然后这个联系:

$ gcc -o prog main.o -L. -lfoobar -Wl,-rpath=$(pwd)
./libfoobar.so: undefined reference to `do_bar'
collect2: error: ld returned 1 exit status

像以前一样失败,并且可以以相同的方式修复:

$ gcc -o prog main.o do_bar.o -L. -lfoobar -Wl,-rpath=$(pwd)
$ ./prog
foo

当我们链接共享库libfoobar.so而不是目标文件foobar.o ,我们的prog有一个不同的符号表:

$ nm prog | grep -e foo -e bar
00000000000007aa T do_bar
             U foo

这次, prog不包含foobar定义。 它包含一个未定义的引用( U )到foo ,因为它调用foo ,当然这个引用现在将在运行时由libfoobar.so的定义libfoobar.so 甚至没有未定义的bar参考,也不应该,因为程序从不调用bar

但是, prog仍然包含do_bar 的定义 ,现在它已从符号表中的所有函数中do_bar引用。

这与你自己的SSCCE相呼应,但却不那么复杂。 在你的情况下:

  • 目标文件libsub.a(shared2.o)链接到程序中,以提供func2afunc2b定义。

  • 必须找到并链接这些定义,因为它们分别在Client_func2aClient_func2b的定义中Client_func2b ,这些定义在libcshared.so中定义。

  • 必须链接libcshared.so以提供Client_func1a的定义。

  • 必须找到并链接Client_func1a的定义,因为它是从func1a的定义func1a

  • 并且func1amain调用。

这就是我们看到的原因:

$ nm main | grep func2
                 U Client_func2a
                 U Client_func2b
00000000004009f7 T func2a
0000000000400a30 T func2b

在您的程序的符号表中。

将定义链接到它不调用的函数的程序中并不罕见。 它通常以我们看到的方式发生:链接,递归解析以main开头的符号引用,发现它需要f的定义,它只能通过链接一些目标文件file.ofile.o它还链接了函数g的定义,它从未被调用。

什么相当奇怪的是喜欢你的节目结束了main和喜欢我的最后一个版本prog ,其中包含一个多余功能的定义(例如do_bar )链接到解决从另一个不必要函数的定义引用(如bar在程序中定义。 即使存在冗余的函数定义,通常我们也可以将它们链接回链接中的一个或多个目标文件,其中第一个冗余定义被引入以及一些必要的定义。

这种奇怪的原因是:

gcc -o prog main.o do_bar.o -L. -lfoobar -Wl,-rpath=$(pwd)

因为必须链接的第一个冗余函数定义( bar )是通过链接共享库 libfoobar.so ,而bar所要求的do_bar的定义不在该共享库或任何其他共享库中,而是在一个目标文件

当程序与该共享库链接时, libfoobar.so提供的bar的定义将保留在那里。 它不会与程序物理链接。 这就是动态联系的本质。 但是链接所需的任何目标文件 - 无论是像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

这给了我一个共享的库,它具有以下API:

void foo(void);
void bar(void);

对于谁想要其中一个或两个,没有未定义的引用。 然后我构建我的程序,这是foo的客户端:

$ gcc -o prog main.o -L. -lfoobar -Wl,-rpath=$(pwd)
$ ./prog
foo

并且它不包含死代码:

$ nm prog | grep -e foo -e bar
                 U foo

同样,如果您构建没有未定义引用的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

然后链接你的程序:

$ gcc -o main main.c libcmain.so  libcshared.so

它也没有死代码:

$ nm main | grep func
                 U func1a

如果您不喜欢libsub.a(shared1.o)libsub.a(shared2.o)通过此解决方案物理链接到libcshared.so这一事实,那么采用另一种正统方法来链接共享库:保留所有func* libcshared.so函数未定义:make libsub 也是一个共享库,它是libcshared.so的动态依赖libcshared.so

如果您只是想要摆脱未使用的函数,则可能不需要使用共享库。 对于GCC,试试这个 对于XL,更换-fdata-sections -ffunction-sections-qfuncsect 一个重要的相关主题是使用导出/导入列表和可见性选项。 它们控制链接到库中的额外符号是否导出到库外部。 有关更多信息,请参见此处

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM