Linux下.ko, .o, .so, .a, .la文件

本文详细介绍了Linux环境下动态链接库(.so)与静态库(.a)的区别与用途,包括如何创建、编译、链接以及调用动态库中的函数。此外,还阐述了如何正确地在makefile中编译和链接生成.so库文件,并提供了在其他程序中调用这些库文件函数的方法。最后,文章提供了环境变量和配置文件的调整技巧,以解决动态库无法链接的问题。


.o file - the unlinked output of the compiler - an object file

.a - out of using ar to "pack" several .o files into an object archive file. It is an efficient way of keeping track of and being able to access object modules during the link edit phase of compilation.

.so - shared object library - created by the linker from .o files. These shared libraries
are loaded into memory and accessed (all at one time) by lots of processes in the system. This saves memory.


Another way of reasoning about the differences is that .a files are linked in at compile time and .so files are linked in at run time. 

So when you compile a source file that calls functions in a .a file the code for those functions gets included, but when you call functions in a .so file they get included when you run the program.




Linux下文件的类型是不依赖于其后缀名的,但一般来讲:

.ko 是Linux 2.6内核使用的动态连接文件的后缀名,也就是模块文件,用来在Linux系统启动时加载内核模块

.o 是目标文件,相当于windows中的.obj文件

.so 为共享库,是shared object,用于动态连接的,和dll差不多

.a 为静态库,是好多个.o合在一起,用于静态连接

.la 为libtool自动生成的一些共享库,vi编辑查看,主要记录了一些配置信息。可以用如下命令查看file *.la来查看文件类型

创建.a库文件和.o库文件:

  $ gcc -c mylib.c

  $ ar -r mylib.a mylib.o

动态链接库*.so的编译与使用

1、动态库的编译

下面通过一个例子来介绍如何生成一个动态库。这里有一个头文件,三个.c文件:

so_test.h

test_a.c

test_b.c

test_c.c

我们将这几个文件编译成一个动态库:libtest.so。

 

代码

 

so_test.h:

#include <stdio.h>

#include <stdlib.h>

void test_a();

void test_b();

void test_c();

 

test_a.c:

#include \"so_test.h\"

void test_a()

{

    printf(\"this is in test_a...\\n\");

}

 

test_b.c:

#include \"so_test.h\"

void test_b()

{

    printf(\"this is in test_b...\\n\");

}

 

test_c.c:

#include \"so_test.h\"

void test_c()

{

    printf(\"this is in test_c...\\n\");

}

$ gcc test_a.c test_b.c test_c.c -fPIC -shared -o libtest.so

2、动态库的链接

在1、中,我们已经成功生成了一个自己的动态链接库libtest.so,下面我们通过一个程序来调用这

个库里的函数。程序的源文件为:test.c。

#include \"so_test.h\"

int main()

{

    test_a();

    test_b();

    test_c();

    return 0;

}

 

将test.c与动态库libtest.so链接生成执行文件test:

  $ gcc test.c -L. -ltest -o test

测试是否动态连接,如果列出libtest.so,那么应该是连接正常了

  $ ldd test

  这时应该会报找不到libtest.so,这里我们再执行一下:

  $ sudo cp libtest.so /usr/lib

  把这个库拷贝到系统默认的库路径即可,这样只是临时测试使用,更合理的方法看后面介绍

执行test,可以看到它是如何调用动态库中的函数的。

 

3、编译参数解析

最主要的是GCC命令行的一个选项:

-shared 该选项指定生成动态连接库(让连接器生成T类型的导出符号表,有时候也生成

    弱连接W类型的导出符号),不用该标志外部程序无法连接。相当于一个可执行文件

-fPIC:表示编译为位置独立的代码,不用此选项的话编译后的代码是位置相关的所以动

    态载入时是通过代码拷贝的方式来满足不同进程的需要,而不能达到真正代码段共享的目的。

-L.:表示要连接的库在当前目录中

-ltest:编译器查找动态连接库时有隐含的命名规则,即在给出的名字前面加上lib,后面加上.so来确定库的名称

    LD_LIBRARY_PATH:这个环境变量指示动态连接器可以装载动态库的路径。

当然如果有root权限的话,可以修改/etc/ld.so.conf文件,然后调用/sbin/ldconfig来

达到同样的目的,不过如果没有root权限,那么只能采用输出LD_LIBRARY_PATH的方法了。

4、注意

调用动态库的时候有几个问题会经常碰到,有时,明明已经将库的头文件所在目录 通过 “-I

”include进来了,库所在文件通过“-L”参数引导,并指定了“-l”的库名,但通过ldd命令察看时,

就是死活找不到你指定链接的so文件,这时你 要作的就是通过修改LD_LIBRARY_PATH或

者/etc/ld.so.conf文件来指定动态库的目录。通常这样做就可以解决库无法链接的问题 了。

makefile里面怎么正确的编译和连接生成.so库文件,然后又是在其他程序的makefile里面如何编译

和连接才能调用这个库文件的函数????

答:

你需要告诉动态链接器、加载器ld.so在哪里才能找到这个共享库,可以设置环境变量把库的

路径添加到库目录/lib和/usr/lib,LD_LIBRARY_PATH=$(pwd),这种方法采用命令行方法不太方便,一种替

代方法

^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^注释^^^^^^^^^^^^^^^^^^^^^^^^^^^^

LD_LIBRARY_PATH可以在/etc/profile还是~/.profile还是./bash_profile里设置,或者.bashrc里

改完后运行source /etc/profile或. /etc/profile

更好的办法是添入/etc/ld.so.conf, 然后执行/sbin/ldconfig

^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^注释^^^^^^^^^^^^^^^^^^^^^^^^^^^^

是把库路径添加到/etc/ld.so.conf,然后以root身份运行ldconfig

也可以在连接的时候指定文件路径和名称-I -L.

GCC=gcc

CFLAGS=-Wall -ggdb -fPIC

#CFLAGS=

all: libfunc test

libfunc:func.o func1.o

$(GCC) -shared -Wl,-soname,libfunc.so.1 -o libfunc.so.1.1 $<

ln -sf libfunc.so.1.1 libfunc.so.1

ln -sf libfunc.so.1 libfunc.so

***********************************************注释

************************************************

ln -s是用来创建软链接,也就相当于windows中的快捷方式,在当前目录中创建上一级目录中的文件

ttt的命名为ttt2软链接的命令是ln -s ../ttt ttt2,如果原文件也就是ttt文件删除的话,ttt2也变成了

空文件。

ln -d是用来创建硬链接,也就相当于windows中文件的副本,当原文件删除的时候,并不影响“副本

”的内容。

编译目标文件时使用gcc的-fPIC选项,产生与位置无关的代码并能被加载到任何地址:

gcc –fPIC –g –c liberr.c –o liberr.o

使用gcc的-shared和-soname选项;

使用gcc的-Wl选项把参数传递给连接器ld;

使用gcc的-l选项显示的连接C库,以保证可以得到所需的启动(startup)代码,从而避免程序在使

用不同的,可能不兼容版本的C库的系统上不能启动执行。

gcc –g –shared –Wl,-soname,liberr.so –o liberr.so.1.0.0 liberr.o –lc

建立相应的符号连接:

ln –s liberr.so.1.0.0 liberr.so.1;

ln –s liberr.so.1.0.0 liberr.so;

在MAKEFILE中:

$@

表示规则中的目标文件集。在模式规则中,如果有多个目标,那么,\"$@\"就是匹配于目标中模式

定义的集合。

$%

仅当目标是函数库文件中,表示规则中的目标成员名。例如,如果一个目标是\"foo.a(bar.o)\",

那么,\"$%\"就是\"bar.o\",\"$@\"就是\"foo.a\"。如果目标不是函数库文件(Unix下是[.a],Windows下是

[.lib]),那么,其值为空。

$<

依赖目标中的第一个目标名字。如果依赖目标是以模式(即\"%\")定义的,那么\"$<\"将是符合模

式的一系列的文件集。注意,其是一个一个取出来的。

$?

所有比目标新的依赖目标的集合。以空格分隔。

$^

所有的依赖目标的集合。以空格分隔。如果在依赖目标中有多个重复的,那个这个变量会去除重

复的依赖目标,只保留一份。

*********************************************注释

***********************************************************************

test: test.o libfunc

$(GCC) -o test test.o -L. -lfunc

%.o:%.c

$(GCC) -c $(CFLAGS) -o $@ $<

clean:

rm -fr *.o

rm -fr *.so*

rm -fr test

要生成.so文件,cc要带-shared 参数;要调用.so的文件,比如libfunc.so,可以在cc命令最后加上

-lfunc,还要视情况加上-L/usr/xxx 指出libfunc.so的路径;这样,在你要编译的源文件中就可以调用

libfunc.so这个库文件的函数.

前面的都说的差不多了,最后提醒一下最好提供一个接口头文件

动态加载,用dlopen,dlclose,dlsym


1.2.4. 安装交叉编译工具链¶ 发布包提供了工具链: glibc: gcc-11.1.0-20210608-sigmastar-glibc-x86_64_arm-linux-gnueabihf.tar.xz uclibc: arm-sigmastar-linux-uclibcgnueabihf-9.1.0.tar.xz 安装步骤如下: step1. 新建工具目录: sudo mkdir -p /tools/toolchain 或者 sudo mkdir -p /opt/sstar(本文以/tools/toolchain为例) step2. 解压安装: sudo tar -xvf ./gcc-11.1.0-20210608-sigmastar-glibc-x86_64_arm-linux-gnueabihf.tar.xz -C /tools/toolchain step3. 可直接在每次编译时export 或 直接添加到/etc/profile、~/.bashrc等启动脚本位置 例:export PATH=${PATH}:/tools/toolchain/gcc-11.1.0-20210608-sigmastar-glibc-x86_64_arm-linux-gnueabihf/bin 1.3. 其他配置¶ 1.3.1. 默认shell配置¶ 编译脚本默认使用的是bash,要求系统的默认shell为bash,可通过ls -la /bin/sh命令来确认。以最常用的Ubuntu为例,高版本的Ubuntu默认shell为dash,修改方式如下: # ls -la /bin/sh lrwxrwxrwx 1 root root 4 Jun 15 08:49 /bin/sh -> dash # sudo dpkg-reconfigure dash #在弹出的界面选择<NO> # ls -la /bin/sh lrwxrwxrwx 1 root root 4 Jun 15 08:49 /bin/sh -> bash 1.3.2. 设置默认python版本为python2.x¶ python2与python3的语义有差别,SDK编译脚本使用的是python2的语义,因此需要将系统默认python版本设置为python2.x,修改方式请参考网络上的相关文档,比如使用update-alternatives工具来配置。 注意:mksquashfs ubuntu自带有,若没有,再根据提示安装即可。 如果使用低版本的Ubuntu,如在Ubuntu 16.04上建议进行更新源的操作,具体操作请参考网络上的相关文档,若不更新可能会导致后续的编译错误。 1.3.3. 设置默认awk实现¶ 编译脚本默认使用awk/gawk,系统的awk版本需要为awk/gawk,可通过awk --help和awk --version命令来确认。以Ubuntu为例: # awk --help Usage: awk [POSIX or GNU style options] -f progfile [--] file ... Usage: awk [POSIX or GNU style options] [--] 'program' file ... POSIX options: GNU long options: (standard) -f progfile --file=progfile -F fs --field-separator=fs -v var=val --assign=var=val ... # awk --version GNU Awk 4.1.4, API: 1.1 (GNU MPFR 4.0.1, GNU MP 6.1.2) Copyright (C) 1989, 1991-2016 Free Software Foundation. ... 如果默认awk实现不是awk/gawk,安装gawk,然后修改awk实现为gawk: sudo apt-get install gawk sudo update-alternatives --config awk # 选择 gawk 作为默认的 awk 实现 3. 编译¶ 在SDK中编译有所区分,通过不同的配置文件来实现编译不同的uboot defconfig/kernel defconfig,以及打包成不同类型的文件系统格式。SDK中包含了最新的boot和kernel的sourceCode,SDK的编译过程主要包括: 编译uboot生成uboot image 编译kernel生成kernel image 依赖kernel build环境,将闭源MI模块如xxx.o和xxx_init.c编译链接成xxx.ko和xxx.so 制作分区,打包上述镜像及其余提前编译放置的SDK中的其他binary,生成不同类型的文件系统格式和estar烧录的脚本。 以本芯片支持的SPI NAND/NOR启动方式为例,举例SDK编译方法。 3.1. Toolchain配置¶ 3.1.1. glibc¶ export PATH=$PATH:/tools/toolchain/gcc-11.1.0-20210608-sigmastar-glibc-x86_64_arm-linux-gnueabihf/bin <若1.2.4中已添加至环境变量,则此步骤可省略> export CROSS_COMPILE=arm-linux-gnueabihf- export ARCH=arm 3.1.2. uclibc¶ export PATH=$PATH:/tools/toolchain/arm-sigmastar-linux-uclibcgnueabihf-9.1.0/bin <若1.2.4中已添加至环境变量,则此步骤可省略> export CROSS_COMPILE=arm-sigmastar-linux-uclibcgnueabihf- export ARCH=arm 3.2. 编译生成image¶ 解压SourceCode cd SourceCode tar zxvf xxx.tar.gz For BGA12 DDR4: 编译NOR: cd project make ipc_iford.nor.glibc-11.1.0-squashfs.ssc029a.512.bga12_ddr4_defconfig make clean;make image -j32 编译SPINAND: cd project make ipc_iford.spinand.glibc-11.1.0-squashfs.ssc029a.512.bga12_ddr4_defconfig make clean;make image -j32 For QFN128 DDR3: 编译NOR: cd project make ipc_iford.nor.glibc-11.1.0-squashfs.ssc029c.256.qfn128_ddr3_defconfig make clean;make image -j32 编译SPINAND: cd project make ipc_iford.spinand.glibc-11.1.0-squashfs.ssc029c.256.qfn128_ddr3_defconfig make clean;make image -j32 BGA11: NA(待补充) 从alkaid/project/image/output/images目录可获取烧录所需的所有bin档 注意: 切换不同的defconfig编译,需要先执行make clean,再执行make xxx_defconfig。 否则可能会执行报错 3.3. defconfig命名说明¶ 编译之前需要选择对应的defconfig,defconfig各字段说明如下: $(PRODUCT).$(FLASH).$(TOOLCHAIN)-$(TOOLCHAIN_VERSION)-$(FS_TYPE).$(BAROD).$(DDR_SIZE).$(PACKAGE)_[$(DDR_TYPE)]_[$(OPTION)]_defconfig 字段 字段说明 举例 PRODUCT 产品形态 如:ipc/usbcam/nvr/dispcam/ipc-rtos FLASH flash类型 如:nor/nand/emmc TOOLCHAIN 编译链 如:glibc/uclibc TOOLCHAIN_VERSION 编译链版本 如:11.1.0/10.2.1 FS_TYPE 文件系统类型 如:ramdisk/ext4fs/ramfs/squashfs BAROD 板子类型 如:ssc029a/ssm00a/ssm00c DDR_SIZE DDR大小 如:256/512x512(两颗512) PACKAGE 芯片封装类型 如:bga1/bga2/qfn128 DDR_TYPE DDR类型,此字段为可选字段 如:ddr4/ddr3/lpddr3/lpddr4/lpddr4x。如果此字段为空,DDR类型为ddr4 OPTION 标识其它一些功能,此字段为可选字段 如:hyp/optee/pm_rtos_dram/str 其中DDR_SIZE的含义,以512为例:DDR SIZE 512MB,对应.config文件中的LX_MEM的大小: 512MB=512*1024*1024Byte=0x20000000Byte 同样对应boot下bootargs参数中的LX_MEM=0x20000000 3.4. 编译闭源MI模块说明¶ Sigmastar SDK里kernel的source code是开源的,为了避免多媒体MI模块代码直接与kernel代码耦合,导致客户开关某些kernel config项需要重新编译MI ko/so,软件采用如下结构: 其中MI module driver以 xxx.o(如vif.o)形式提供给用户,位于sdk/linux/xxx/目录下; MI module init.c以xxx_init.c(如vif_init.c)形式提供给用户,位于sdk/linux/xxx/目录下; Cam wrapper driver位于kernel/drivers/sstar/cam_fs_wrapper/和cam_os_wrapper,最终编译进kenrel image; 当用户对kernel config有重大改动时,按照3.2章节编译project image: make xxx_defconfig 选择对应的project 编译config; make menuconfig,指定kernel编译时使用的客户自己的config; make clean;make image -j32 即可在project\release\chip\iford$(PRODUCT)\common$(TOOLCHAIN)$(TOOLCHAIN_VERSION)\release\modules\5.10\下获取 MI ko。 这是厂商demo编译的教程,详细为我介绍
最新发布
09-16
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值