美文网首页
工作中遇到的编译情况【下】

工作中遇到的编译情况【下】

作者: 晴空一垩 | 来源:发表于2019-08-04 17:27 被阅读0次

接上一篇

隐藏部分符号使之不可被外部引用

创建文件 c.c ,c.h

#include <stdio.h>

void testC();
#include "c.h"

void testInnerC(){
    printf("hello Inner C\n");
}

void testC(){
    printf("hello C\n");
}

执行编译

$ gcc -shared -fPIC -I. c.c -o libc.so

$ objdump -Tt libc.so |grep test
00000000000006b3 g     F .text  0000000000000013              testC
00000000000006a0 g     F .text  0000000000000013              testInnerC
00000000000006b3 g    DF .text  0000000000000013  Base        testC
00000000000006a0 g    DF .text  0000000000000013  Base        testInnerC

我们需要的是把testInnerC符号隐藏掉,需要怎么做呢?

如果可以的话,你可以想想。如果你已经知道了,那么就直接跳过吧。

废话不多说,直接给答案。

在c.c的文件中 ,增加关键字 __attribute__((visibility("default"))),就像这样

//c.c

__attribute__((visibility("default"))) void testC(){
    printf("hello C\n");
} 

然后编译参数也要变化

$ gcc -shared -fPIC -I. c.c -o libc.so -fvisibility=hidden

$ objdump -Tt libc.so |grep test
0000000000000670 l     F .text  0000000000000013              testInnerC
0000000000000683 g     F .text  0000000000000013              testC
0000000000000683 g    DF .text  0000000000000013  Base        testC

也许你会说:这不是还看得见符号吗?

上面的步骤,我们已经隐藏了定义,那我们再加去掉符号

$ strip libc.so
$ objdump -Tt libc.so |grep test
0000000000000683 g    DF .text  0000000000000013  Base        testC

可以看到符号和需要隐藏的定义都不见了,这个时候我们就可以给到别人使用了

我们可以连接直接我们连接过的库了。

我们再 main.c 里面增加 testC 的调用

$ gcc -c -I. a.c -o a.o
$ ar -cr liba.a a.o

$ gcc -shared -o libb.so -I. b.c

$ gcc -shared -o libc.so -I. c.c -fvisibility=hidden

$ gcc -I. -o main.exe main.c -L. -Wl,-Bstatic -la -Wl,-Bdynamic -lb -lc
/usr/bin/ld: /tmp/ccXEuLPw.o: undefined reference to symbol 'puts@@GLIBC_2.2.5'
//lib/x86_64-linux-gnu/libc.so.6: error adding symbols: DSO missing from command line
collect2: error: ld returned 1 exit status

这里却出现了异常经过分析是由于和系统的 libc.so 重名了,我们换个名字就可以 了

$ mv libc.so libmtc.so

$ gcc -I. -o main.exe main.c -L. -Wl,-Bstatic -la -Wl,-Bdynamic -lb -lmtc

$ ./main.exe 
hello main
hello A
hello B 
hello C

现在让我们调用 libmtc.so 里面的 testInnerC

需要修改下 c.h

#include <stdio.h>

void testC();

void testInnerC();

执行编译

$ gcc -I. -o main.exe main.c -L. -Wl,-Bstatic -la -Wl,-Bdynamic -lb -lmtc
/tmp/ccnG2ec3.o:在函数‘main’中:
main.c:(.text+0x3f):对‘testInnerC’未定义的引用
collect2: error: ld returned 1 exit status

在这里,你有发现为什么这里会有问题吗?

如果在这里让你想起了你之前项目遇到过的未定义的引用,那这就是我的荣幸。如果没有,那我们一起收获。

我们知道在 libmtc.so 里面是没有这个定义的,怎么知道,上面说到使用 objdump 来进行查看

$ objdump -Tt libmtc.so |grep test
0000000000000670 l     F .text  0000000000000013              testInnerC
0000000000000683 g     F .text  0000000000000013              testC
0000000000000683 g    DF .text  0000000000000013  Base        testC

如果你要调用则需要重新编译 libmtc.so 这个库,不加-fvisibility=hidden 即可通过编译

也许你会说,“这是因为你之前自己把它隐藏了的”,

那我们在写一个例子。

需求:如何在编译期就发现符号未定义

我们先来个正常的发现不了未定义的。

创建 d.c,d.h

#include <stdio.h>

void testD();

void testUD();
#include "d.h"

void testD(){
    printf("hello D\n");
}

在增加 e.c,e.h

#include "d.h"
#include "e.h"
void testE(){
    testD();
    printf("hello E\n");
}
#include <stdio.h>

void testE();

修改main.c

#include "a.h"
#include "b.h"
#include "c.h"
#include "e.h"

int main(int argc,char* argv[])
{
    printf("hello main\n");
    testA();
    testB();
    testC();
    //testInnerC();
    testE();
    return 0;
}

开始编译出 libd.solibe.so

$ gcc -shared -fPIC d.c -I. -o libd.so

$ gcc -shared -fPIC e.c -I. -o libe.so

$ gcc -I. -o main.exe main.c -L. -Wl,-Bstatic -la -Wl,-Bdynamic -lb -lmtc -ld -le

$ ./main.exe 
hello main
hello A
hello B 
hello C
hello D
hello E

到这里,都是正常的输出

现在让我们修改一下文件

#include "d.h"

void testD(){
    printf("hello D\n");
    testUD(); //发现这里做了修改,调用了一个没有定义的方法
}

开始编译,现打出静态库 libd.a 再将静态库达成动态库,再使用动态库libe.so 进行链接

$ gcc -c d.c -I. -o d.o

$ ar -cr libd.a d.o

$ gcc -shared -fPIC e.c -I. -L. -Wl,--whole-archive -Wl,-Bstatic -ld -Wl,-Bdynamic -Wl,-no-whole-archive -o libe.so

$ gcc -I. -o main.exe main.c -L. -Wl,-Bstatic -la -le -Wl,-Bdynamic -lb -lmtc  -le
./libe.so:对‘testUD’未定义的引用
collect2: error: ld returned 1 exit status

此时可以发现出现‘testUD’未定义的引用 的问题

也许你跟着上面的步骤,知道是我有个方法未定义,并且是在libd.a 里面就有问题了。

但是,如果只是给你一个库,不管静态库动态库,你只有再链接成可执行文件的时候才出现问题,那会导致你不知道从哪里下手。在大项目中更是如此,有可能会耗费你一天的精力,去发现哪里出现了问题。

那么怎么发现哪里有问题呢?需要在链接的时候增加一个参数 z,defs

$ gcc -shared -fPIC e.c -I. -L. -Wl,--whole-archive -Wl,-Bstatic -ld -Wl,-Bdynamic -Wl,-no-whole-archive -o libe.so -Wl,-z,defs
./libd.a(d.o):在函数‘testD’中:
d.c:(.text+0x16):对‘testUD’未定义的引用
collect2: error: ld returned 1 exit status

这样你就可以清晰的看出那个库里面的,哪个方法是有问题的。

这里建议所有的c/c++ 程序员都加上这个参数 -z,defs 在链接的时候,可以减少很多问题的!!!

需求:如何将一个静态库,打包成为一个动态库

还是使用之前的liba.a 现在我只有这个库,没有这个库的源码,现在想要吧liba.a 变成liba.so 怎么做呢?

我们知道链接的时候,优先链接动态库,如果动态库找不到就会去链接静态库

$ gcc -shared -L. -ld -o libtd.so

$ objdump -Tt libtd.so  |grep test
# 会发现没有输出

那么怎么做呢?

如果你想自己想一下的话,先不要看我的实现方案,或者你可以想想有没有其他的实现方案。

$ gcc -c -o d.o d.c
$ ar -cr libd.a d.o

$ gcc -shared -L. -Wl,--whole-archive -ld  -Wl,--no-whole-archive -o libtd.so
$ objdump -Tt libtd.so  |grep test 
#还是没有输出,

如果你有输出,那么恭喜你,你成功了,为什么我会失败?因为我的环境存在了同名的静态库和动态库

$ ls
a.c  a.o  b.h  c.h  d.h  e.c  liba.a   libd.a   libmtc.so  main.c
a.h  b.c  c.c  d.c  d.o  e.h  libb.so  libd.so  libtd.so

#我们需要强制指定使用静态库,或者删除动态库
$ gcc -shared -L. -Wl,--whole-archive -Wl,-Bstatic -ld -Wl,-Bdynamic  -Wl,--no-whole-archive -o libtd.so

$ nm -D libtd.so |grep test
0000000000000670 T testD

小结

几个重要的链接,编译参数
-Wl,-z,defs
-Wl,--whole-archive-Wl,--no-whole-archive
-Wl,-Bstatic-Wl,-Bdynamic

相关文章

  • 工作中遇到的编译情况【下】

    接上一篇 隐藏部分符号使之不可被外部引用 创建文件 c.c ,c.h 执行编译 我们需要的是把testInnerC...

  • 工作中遇到的编译情况【上】

    说在前面: 先从简单的问题开始 需求:和一个静态库一起打包成为可执行文件 应用场景:将所有的ffmpeg的库分别打...

  • 2018-10-17

    ReactNative 如何调试? 这里仅仅讨论在Mac 情况下,编译跑iOS的情况 在保证RN正常编译的情况下:...

  • configure: error: C compiler cc

    编译安装nginx时遇到C compiler cc is not found,一般情况下是因为没有安装gcc,但是...

  • C++调Python

    没有编译好的python36_d.lib库情况下,debug编译遇到如下问题: 解决办法:修改两个头文件1 注释掉...

  • Permission denied Command PhaseS

    xcode 编译遇到这样的情况 : Permission denied Command PhaseScriptE...

  • 工作中遇到的编译错误

    工作中项目工程编译时会遇到很多报错,解决办法基本是百度,有些问题之前遇到过了但时间长了容易忘记当时的解决办法...

  • uniapp H5跨域问题

    uniapp编译到app是不存在跨域问题的,但是编译到H5就会有跨域问题。记录一下工作中遇到跨域及解决跨域方法 方...

  • ASP.NET-调试相关-当前不会命中断点还未为文档加载任何符号

    我遇到的情况是项目编译设置问题,但是在网上也找到了一些其它情况的排查,在这里总结一下 当遇到VS“当前不会命中断点...

  • composer系列(一)composer

    composer的由来 玩过linux下的编译安装的朋友肯定遇到过这种情况,比如安装软件A,结果提示需要先安装软件...

网友评论

      本文标题:工作中遇到的编译情况【下】

      本文链接:https://www.haomeiwen.com/subject/jjpydctx.html