Linux下编译生成SO并进行调用执行

参考博客的博客:

C编译: 动态连接库 (.so文件) - Vamei - 博客园 (cnblogs.com)

C 多个动态库存在同名函数问题处理方法:-fvisibility=hidden_more_HH-CSDN博客_fvisibility

Linux编译动态链接库so避免运行时才发现函数未定义符号的错误undefined symbol的ld参数 (gmd20.github.io)

查看so库的方法__臣本布衣_新浪博客 (sina.com.cn)

Linux 动态库同名函数处理-iibull-ChinaUnix博客

1 代码结构

(1)include中是用于生成SO的头文件,当前只有一个test.h文件,内容如下:

void print_func(void);

(2)src中是用于生成SO的源文件,当前只有一个test.c文件,内容如下:

#include <stdio.h>

void print_func(void)
{
int i = 0; for (; i < 10; i++)
printf("i = %d\n", i);
return;
}

(3)Makefile文件是用于生成SO的,内容如下:

PROJECT=libprint_func.so

CC?=gcc

SOURCES=$(wildcard src/*.c)

OBJECTS=$(patsubst %.c,%.o,$(SOURCES))

.PHONY:clean

CFLAG = -Iinclude -fPIC -shared
LD_FLAG = -fPIC -s -Wl,-z,relro,-z,now,-z,noexecstack -fstack-protector-all $(PROJECT): $(OBJECTS)
mkdir -p lib
$(CC) -shared -o lib/$@ $(patsubst %.o,obj/%.o,$(notdir $(OBJECTS))) $(LD_FLAG)
@echo "finish $(PROJECT)" .c.o:
@mkdir -p obj
$(CC) -c $< $(CFLAG) -o obj/$(patsubst %.c,%.o,$(notdir $<)) clean:
-rm -rf obj lib
@echo "clean up"

生成的SO的名字为libprint_func.so

(4)main.c用于测试,函数内部会调用test.c中的函数,代码如下:

#include <stdio.h>
#include "test.h" int main(void)
{
print_func(); return 0;
}

2 编译SO

编译方式,直接在Makefile所在目录执行make即可。

$ ls
include/ main.c Makefile src/
$ make
cc -c src/test.c -Iinclude -fPIC -shared -o obj/test.o
mkdir -p lib
cc -shared -o lib/libprint_func.so obj/test.o -fPIC -s -Wl,-z,relro,-z,now,-z,noexecstack -fstack-protector-all
finish libprint_func.so

执行了make之后,会在当前目录生成两个文件夹lib和obj,lib目录存放SO文件,obj目录存在生成的obj文件。

$ ls lib/libprint_func.so
lib/libprint_func.so*
$ ls obj/test.o
obj/test.o

3 生成可执行文件

接着编译main.c,生成可执行文件进行测试。

编译方式:

gcc -o test main.c -lprint_func -Llib -Iinclude -Wall

-l:说明库文件的名字,使用-lprint_func (即libprint_func库文件)

-L:指定编译库文件所在位置

-I(大写的i):指定头文件所在文件

-Wall:打印所有警告

注意:编译时必须包括SO中函数的头文件(test.h),否则会提示隐形声明的警告。

例如:在main.c中注释掉#include "test.h"。

#include <stdio.h>
//#include "test.h" int main(void)
{
print_func(); return 0;
}

编译时会提示警告:

$ gcc -o test main.c -lprint_func -Llib -Iinclude -Wall
main.c: In function ‘main’:
main.c:6:5: warning: implicit declaration of function ‘print_func’ [-Wimplicit-function-declaration]
print_func();

4 执行可执行文件

直接执行可执行文件,会提示找不到libprint_func.so文件。

$ ./test
./test: error while loading shared libraries: libprint_func.so: cannot open shared object file: No such file or directory

可通过gcc -print-search-dirs命令来查看库的搜索路径:

$ gcc -print-search-dirs
install: /usr/lib/gcc/x86_64-redhat-linux/4.8.5/
programs: =/usr/libexec/gcc/x86_64-redhat-linux/4.8.5/:/usr/libexec/gcc/x86_64-redhat-linux/4.8.5/:/usr/libexec/gcc/x86_64-redhat-linux/:/usr/lib/gcc/x86_64-redhat-linux/4.8.5/:/usr/lib/gcc/x86_64-redhat-linux/:/usr/lib/gcc/x86_64-redhat-linux/4.8.5/../../../../x86_64-redhat-linux/bin/x86_64-redhat-linux/4.8.5/:/usr/lib/gcc/x86_64-redhat-linux/4.8.5/../../../../x86_64-redhat-linux/bin/
libraries: =/usr/lib/gcc/x86_64-redhat-linux/4.8.5/:/usr/lib/gcc/x86_64-redhat-linux/4.8.5/../../../../x86_64-redhat-linux/lib/x86_64-redhat-linux/4.8.5/:/usr/lib/gcc/x86_64-redhat-linux/4.8.5/../../../../x86_64-redhat-linux/lib/../lib64/:/usr/lib/gcc/x86_64-redhat-linux/4.8.5/../../../x86_64-redhat-linux/4.8.5/:/usr/lib/gcc/x86_64-redhat-linux/4.8.5/../../../../lib64/:/lib/x86_64-redhat-linux/4.8.5/:/lib/../lib64/:/usr/lib/x86_64-redhat-linux/4.8.5/:/usr/lib/../lib64/:/usr/lib/gcc/x86_64-redhat-linux/4.8.5/../../../../x86_64-redhat-linux/lib/:/usr/lib/gcc/x86_64-redhat-linux/4.8.5/../../../:/lib/:/usr/lib/

可以通过ldd命令查看可执行文件依赖的库:

$ ldd ./test
linux-vdso.so.1 => (0x00007ffd5b943000)
libprint_func.so => not found
libc.so.6 => /lib64/libc.so.6 (0x00007efec2afc000)
/lib64/ld-linux-x86-64.so.2 (0x00005593b3bba000)

可以看到找不到libprint_func.so库,有两种方法可以解决。

方法一:将libprint_func.so拷贝到系统的/lib/路径下,并执行ldconfig命令(此操作需要root权限才能搞定);

方法二:将libprint_func.so所在路径,增加到LD_LIBRARY_PATH环境变量中。

1)先使用方法二解决:

$ export LD_LIBRARY_PATH=./lib
$ ldd ./test
linux-vdso.so.1 => (0x00007ffeb81dd000)
libprint_func.so => ./lib/libprint_func.so (0x00007f35a3dfc000)
libc.so.6 => /lib64/libc.so.6 (0x00007f35a3a25000)
/lib64/ld-linux-x86-64.so.2 (0x00005625bf8b5000)
$ ./test
i = 0
i = 1
i = 2
i = 3
i = 4
i = 5
i = 6
i = 7
i = 8
i = 9

2)再使用方法一解决:

$ export LD_LIBRARY_PATH=./
$ ./test
./test: error while loading shared libraries: libprint_func.so: cannot open shared object file: No such file or directory
$ sudo cp lib/libprint_func.so /lib
$ sudo ldconfig
$ ./test
i = 0
i = 1
i = 2
i = 3
i = 4
i = 5
i = 6
i = 7
i = 8
i = 9

5 扩展

问题一:如果在不同的SO中存在相同的函数名,那么如何处理?

在之前test.c中的代码基础上,修改print_func函数内容:

#include <stdio.h>

void print_func(void)
{
int i = 0; for (i = 10; i < 20; i++)
printf("i = %d\n", i);
return;
}

然后重新生成一个SO文件,重命名为libprint_func00.so(更改Makefile)。

先删除/lib库下的libprint_func.so。

$ sudo rm /lib/libprint_func.so
$ sudo ldconfig

将libprint_func.so 和 libprint_func00.so 都放到当前目录的lib下:

$ ls ./lib/
libprint_func00.so* libprint_func.so*

然后使用方法二链接SO文件:

$ export LD_LIBRARY_PATH=./lib

编译:

$ gcc -o test main.c -lprint_func -lprint_func00  -Llib -Iinclude -Wall
$ ldd test
linux-vdso.so.1 => (0x00007ffd04d9b000)
libprint_func.so => ./lib/libprint_func.so (0x00007f729a480000)
libprint_func00.so => ./lib/libprint_func00.so (0x00007f729a27d000)
libc.so.6 => /lib64/libc.so.6 (0x00007f7299ea7000)
/lib64/ld-linux-x86-64.so.2 (0x000055f88ea81000)

执行:

$ ./test
i = 0
i = 1
i = 2
i = 3
i = 4
i = 5
i = 6
i = 7
i = 8
i = 9

可以看出,执行时调用的是libprint_func.so库

下面更改编译时SO文件的顺序(先-lprint_func00),然后执行:

$ gcc -o test main.c -lprint_func00 -lprint_func  -Llib -Iinclude -Wall
$ ./test
i = 10
i = 11
i = 12
i = 13
i = 14
i = 15
i = 16
i = 17
i = 18
i = 19

可以看出,执行时调用的是libprint_func00.so库

总结:SO中重名时,优先调用的是先链接的库。

对于不同库中函数重名的问题,可参考博客Linux 动态库同名函数处理-iibull-ChinaUnix博客,这里不进行说明。

6 总结

由于这是第一次尝试生成SO文件,部分地方可能存在问题,希望网友指出。

再次感谢参考的博客:

C编译: 动态连接库 (.so文件) - Vamei - 博客园 (cnblogs.com)

C 多个动态库存在同名函数问题处理方法:-fvisibility=hidden_more_HH-CSDN博客_fvisibility

Linux编译动态链接库so避免运行时才发现函数未定义符号的错误undefined symbol的ld参数 (gmd20.github.io)

查看so库的方法__臣本布衣_新浪博客 (sina.com.cn)

Linux 动态库同名函数处理-iibull-ChinaUnix博客

Linux下部分用于SO相关的命令:

打印SO中的符号信息:nm -D libxxx.so

$ nm -D ./lib/libprint_func.so
0000000000201000 B __bss_start
w __cxa_finalize
0000000000201000 B _edata
0000000000201008 B _end
0000000000000700 T _fini
w __gmon_start__
0000000000000580 T _init
w _ITM_deregisterTMCloneTable
w _ITM_registerTMCloneTable
w _Jv_RegisterClasses
U printf
00000000000006c5 T print_func

查看依赖关系:ldd libxxx.so

$ ldd ./lib/libprint_func.so
linux-vdso.so.1 => (0x00007fff56150000)
libc.so.6 => /lib64/libc.so.6 (0x00007fd3fd4e7000)
/lib64/ld-linux-x86-64.so.2 (0x00005598885c5000)

查看so库的属性:file libxxx.so

$ file ./lib/libprint_func.so
./lib/libprint_func.so: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, BuildID[sha1]=24735da88c6198d81974645c25367fae14a267a5, stripped

Linux下编译生成SO并进行调用执行的更多相关文章

  1. .netcore在linux下使用P/invoke方式调用linux动态库

    http://www.mamicode.com/info-detail-2358309.html   .netcore下已经实现了通过p/invoke方式调用linux的动态链接库(*.so)文件 1 ...

  2. Linux下安装配置Jmeter5.1,并执行jmx文件

    Windows下的jmeter是GUI模式,可查看操作,但是GUI对性能的干扰比较大,所有一般压测会在Linux上运行. 下面是Linux下安装配置Jmeter5.1,并执行jmx文件的步骤, 一.安 ...

  3. Linux下搭建实现HttpRunnerManager的异步执行、定时任务及任务监控

    前言 在之前搭建的HttpRunnerManager接口测试平台,我们还有一些功能没有实现,比如异步执行.定时任务.任务监控等,要完成异步执行,需要搭建 RabbitMQ 等环境,今天我们就来实现这些 ...

  4. 怎样在linux下编写C程序并编译执行

    一.Hello, world! 在linux下输入:(以hello.c为例)首先选中文件要保存的路径(如:cd work)vi hello.c(要编辑的文件名) 输入程序:# include<s ...

  5. linux下service+命令和直接去执行命令的区别,怎么自己建立一个service启动

    启动一些程序服务的时候,有时候直接去程序的bin目录下去执行命令,有时候利用service启动. 比如启动mysql服务时,大部分喜欢执行service mysqld start.当然也可以去mysq ...

  6. linux下怎样用c语言调用shell命令

    C程序调用shell脚本共同拥有三种法子 :system().popen().exec系列数call_exec1.c , system() 不用你自己去产生进程.它已经封装了,直接增加自己的命令 ex ...

  7. Linux下安卓ndk混合编译调用so方法——QuickStart学习

    转自:http://www.52pojie.cn/thread-313869-1-1.html #注意:.h 和.c中的错误eclipse不会检查,只会调用时在手机或虚拟机中死掉.因此需要仔细检查其中 ...

  8. linux下形如{command,parameter,parameter}执行命令 / bash花括号扩展

    背景 在复现vulhub上的漏洞ActiveMQ Deserialization Vulnerability (CVE-2015-5254)时,发现官方文档给出反弹shell的payload bash ...

  9. Linux下的C程序如何调用系统命令,并获取系统的输出信息到C程序中

    直接贴代码: #include <stdio.h> #include <string.h> #include <errno.h> int main(int argc ...

随机推荐

  1. [bzoj1005]明明的烦恼

    根据purfer序列的原理,每一个purfer序列都一一对应了一棵树,每一个点在purfer序列中出现的次数就是它的度数,那么直接用组合数去计算即可,注意要加高精度 1 #include<cst ...

  2. java及python调用RabbitMQ

    1,python调用MQ发送消息(生产者),话不多说,直接上干货 import pika 如下图 2.java调用MQ发送消息(生产者) 具体代码如下: python 的代码如下 connection ...

  3. char数据可以放入int[]中会自动转换

    int[] ary ={'b','c','a','d','e','f'};System.out.println(ary[0]);//98String str = new String(ary, 2, ...

  4. Java设计模式之(十三)——模板方法模式

    1.什么是模板模式? Define the skeleton of an algorithm in an operation, deferring some steps to subclasses. ...

  5. AtCoder Beginner Contest 204

    身败名裂了,\(AK\)场转掉分场. 都是水题不说了. 这篇文鸽了.

  6. 【2020五校联考NOIP #6】最佳观影

    题意: 给出一个 \(k \times k\) 的网格和 \(n\) 次操作.其中 \(k\) 为奇数. 每次操作给出一个数 \(m\).每次你要找出一个三元组 \((x,l,r)\) 使得: \(r ...

  7. Codeforces 1089I - Interval-Free Permutations(析合树计数)

    Codeforces 题面传送门 & 洛谷题面传送门 首先题目中涉及排列的 interval,因此可以想到析合树.由于本蒟蒻太菜了以至于没有听过这种神仙黑科技,因此简单介绍一下这种数据结构:我 ...

  8. 洛谷 P6667 - [清华集训2016] 如何优雅地求和(下降幂多项式,多项式)

    题面传送门 wjz:<如何优雅地 AK NOI> 我:如何优雅地爆零 首先,按照这题总结出来的一个小套路,看到多项式与组合数结合的题,可以考虑将普通多项式转为下降幂多项式,因为下降幂和组合 ...

  9. 作业帮上万个 CronJob 和在线业务混部,如何解决弱隔离问题并进一步提升资源利用率?

    作者 吕亚霖,作业帮基础架构 - 架构研发团队负责人.负责技术中台和基础架构工作.在作业帮期间主导了云原生架构演进.推动实施容器化改造.服务治理.GO 微服务框架.DevOps 的落地实践. 别路,作 ...

  10. Docker镜像相关操作

    批量导入镜像 ll *.tgz|awk '{print $NF}'|sed -r 's#(.*)#docker load -i \1#' |bash 批量打tag docker images | se ...