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

接上一篇

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

创建文件 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

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,504评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,434评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,089评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,378评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,472评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,506评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,519评论 3 413
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,292评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,738评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,022评论 2 329
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,194评论 1 342
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,873评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,536评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,162评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,413评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,075评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,080评论 2 352

推荐阅读更多精彩内容

  • 动态链接,在可执行文件装载时或运行时,由操作系统的装载程序加载库。大多数操作系统将解析外部引用(比如库)作为加载过...
    小5筒阅读 5,496评论 0 3
  • 一、温故而知新 1. 内存不够怎么办 内存简单分配策略的问题地址空间不隔离内存使用效率低程序运行的地址不确定 关于...
    SeanCST阅读 7,791评论 0 27
  • 背景 目前后台服务器都是由bin+so的方式构成,ServerFrame(bin)提供网络通信,内存管理,配置管理...
    laughxing阅读 2,596评论 1 4
  • 转自http://blog.csdn.net/navyhu/article/details/47023317理解链...
    扎Zn了老Fe阅读 1,438评论 0 0
  • 1. 介绍 使用GNU的工具我们如何在Linux下创建自己的程序函数库?一个“程序函数库”简单的说就是一个文件包含...
    逍遥_9353阅读 1,584评论 0 2