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. Identity Server 4 从入门到落地(一)—— 从IdentityServer4.Admin开始

    最近项目中需要使用Identity Server 4,以前对这个技术只是有些了解,没有系统研究过,网上相关的资料不少,大多是从编写一个简单的认证服务开始,离能够落地使用有相当的距离,理论学习如何不结合 ...

  2. 洛谷 P5048 - [Ynoi2019 模拟赛] Yuno loves sqrt technology III(分块)

    题面传送门 qwq 感觉跟很多年前做过的一道题思路差不多罢,结果我竟然没想起那道题?!!所以说我 wtcl/wq 首先将 \(a_i\) 离散化. 如果允许离线那显然一遍莫队就能解决,复杂度 \(n\ ...

  3. WPS for Linux 字体配置(字体缺失解决办法)

    WPS for Linux 字体配置(字体缺失解决办法) 1. 背景 有些linux装完wps后提示"部分字体无法显示"或"some formula symbols mi ...

  4. 【基因组注释】ncRNA注释

    目录 1. ncRNA 2. 软件 tRNA注释 rRNA注释 其他ncRNA注释 3. 注释 tRNA rRNA snRNA.miRNA等 4. snRNA.miRNA等结果的统计 1. ncRNA ...

  5. 【宏基因组】MEGAN4,MEGAN5和MEGAN6的Linux安装和使用

    MEGAN(Metagenome Analyzer)是宏基因组学进行物种和功能研究的常用软件,实际上现在的Diamond+MEGAN6已经是一套比较完整的物种和功能注释流程了. 但是由于各种原因,我们 ...

  6. mysql 中@ 和 @@的区别

    @x 是 用户自定义的变量 (User variables are written as @var_name)@@x 是 global或session变量 (@@global @@session )@ ...

  7. 17.Power of Four-Leetcode

    #define IMIN numeric_limits<int>::min() #define IMAX numeric_limits<int>::max() class So ...

  8. 阿里云NAS性能测试

    测试方法:根据阿里云NAS官方文档进行测试 测试对象:性能型NAS,总容量1PB,已使用27.49GB(计算吞吐量时按30GB计算) 随机读IOPS测试 测试命令 fio -numjobs=1 -io ...

  9. 巩固javaweb第十四天

    巩固内容: 单行文本框: 单行文本框的基本语法格式如下: < input type="text"  name="输入信息的字"  value=" ...

  10. Go知识盲区--闭包

    1. 引言 关于闭包的说明,曾在很多篇幅中都有过一些说明,包括Go基础--函数2, go 函数进阶,异常与错误 都有所提到, 但是会发现,好像原理(理论)都懂,但是就是不知道如何使用,或者在看到一些源 ...