linux应用编译链接运行时头文件以及库的查找

头文件以及库的查找

在linux环境中,要使C源码运行起来,要经历编译链接运行三个阶段。gcc通常负责编译,同时链接工具(如ld)通过其他flags来负责链接。

编译时头文件路径

C_INCLUDE_PATH、CPLUS_INCLUDE_PATH以及CPATH常被用于在全局性地添加预处理C/C++时的包含目录,其中C_INCLUDE_PATH仅对预处理C有效,CPLUS_INCLUDE_PATH仅对预处理C++有效,而CPATH对所有语言均有效。

export C_INCLUDE_PATH=/usr/local/libevent/include${C_INCLUDE_PATH:+:${C_INCLUDE_PATH}}
export CPLUS_INCLUDE_PATH=/usr/local/opencv3.2/include${CPLUS_INCLUDE_PATH:+:${CPLUS_INCLUDE_PATH}}
  • 查看gcc预处理C时的的搜索目录 echo | gcc -x c -E -Wp,-v - >/dev/null
  • 查看gcc预处理C++时的的搜索目录echo | gcc -x c++ -E -Wp,-v - >/dev/null
  • 查看clang预处理C++时的搜索目录echo | clang -x c++ -v -E -

运行时动态库的路径搜索顺序

  1. LD_PRELOAD环境变量,一般用于hack

  2. 编译目标代码时指定的动态库搜索路径(指的是用 -wl,rpath 或-R选项而不是-L),readelf -d命令可以查看编译的目标文件中rpath参数;

    gcc -Wl,-rpath,/home/arc/test,-rpath,/usr/local/lib test.c
    
  3. 环境变量LD_LIBRARY_PATH指定的动态库搜索路径;

    export LD_LIBRARY_PATH=/root/test/env/lib
    ./main
    

    或者

    LD_LIBRARY_PATH=.:$LD_LIBRARY_PATH ./main
    
  4. 配置文件 /etc/ld.so.conf 中指定的动态库搜索路径;

    将动态库路径追加到/etc/ld.so.conf文件或者/etc/ld.so.conf.d/目录下的文件后,记得一定要执行ldconfig 命令,该命令会将配置的所有路径下的库载入缓存文件/etc/ld.so.cache

    [root@callinglove ~]# cat /etc/ld.so.conf
    include ld.so.conf.d/*.conf
    /opt/opencv3.4.3/lib64
    [root@callinglove ~]# ldconfig 
    [root@callinglove ~]# ldconfig -p | grep opencv_core
    	libopencv_core.so.3.4 (libc6,x86-64) => /opt/opencv3.4.3/lib64/libopencv_core.so.3.4
    	libopencv_core.so (libc6,x86-64) => /opt/opencv3.4.3/lib64/libopencv_core.so
    
    [root@callinglove ~]# cat /etc/ld.so.conf.d/mysql-x86_64.conf 
    /usr/lib64/mysql
    [root@callinglove ~]# ls /usr/lib64/mysql/
    libmysqlclient_r.so.18  libmysqlclient_r.so.18.1.0  libmysqlclient.so.18  libmysqlclient.so.18.1.0  libmysqlclient.so.21  libmysqlclient.so.21.1.17  mecab  plugin
    [root@callinglove ~]# ldconfig -p | grep libmysqlclient
    	libmysqlclient.so.21 (libc6,x86-64) => /usr/lib64/mysql/libmysqlclient.so.21
    	libmysqlclient.so.18 (libc6,x86-64) => /usr/lib64/mysql/libmysqlclient.so.18
    

    mysql8将动态库的路径添加到/etc/ld.so.conf.d目录下的一个单独的conf文件中,也能完成配置,并且不会影响到其他的配置

  5. 默认的动态库搜索路径 /lib;

  6. 默认的动态库搜索路径 /usr/lib

编译时查找库的搜索路径

  1. 编译时使用 -L 指定库的路径;
    gcc main.c -o main -L./ -lcac
  2. 通过环境变量LIBRARY_PATH指定搜索路径
    LIBRARY_PATH=.:$LIBRARY_PATH gcc main.c -o main -lcac
  3. 系统标准路径/lib /usr/lib /usr/local/lib

比较

  1. 编译时查找的是静态库或动态库,而运行时查找的只是动态库;
  2. gcc参数 -L 指定编译时的链接路径,-Wl,-rpath 指定运行时链接路径;
  3. 编译时使用环境变量LIBRARY_PATH指定库的路径,运行时使用环境变量LD_LIBRARY_PATH/etc/ld.so.conf 指定库的路径;
  4. 编译时用的链接器是ld,而运行时用的链接器是 /lib/ld-linux.so.2;
  5. 编译时与运行时都会查找默认路径:/lib /usr/lib;
  6. 编译时还有一个默认路径:/usr/local/lib,而运行时不会默认找查该路径。

pkg-config

在大多数开源代码中autotool工具使用pkg-config命令来获取第三方库的相关编译与连接参数的

# pkg-config --help
Usage:
  pkg-config [OPTION?]

Help Options:
  -h, --help                              Show help options

Application Options:
  --version                               output version of pkg-config
  --modversion                            output version for package
  --atleast-pkgconfig-version=VERSION     require given version of pkg-config
  --libs                                  output all linker flags
  --static                                output linker flags for static linking
  --short-errors                          print short errors
  --libs-only-l                           output -l flags
  --libs-only-other                       output other libs (e.g. -pthread)
  --libs-only-L                           output -L flags
  --cflags                                output all pre-processor and compiler flags
  --cflags-only-I                         output -I flags
  --cflags-only-other                     output cflags not covered by the cflags-only-I option
  --variable=NAME                         get the value of variable named NAME
  --define-variable=NAME=VALUE            set variable NAME to VALUE
  --exists                                return 0 if the module(s) exist
  --print-variables                       output list of variables defined by the module
  --uninstalled                           return 0 if the uninstalled version of one or more module(s) or their dependencies will be used
  --atleast-version=VERSION               return 0 if the module is at least version VERSION
  --exact-version=VERSION                 return 0 if the module is at exactly version VERSION
  --max-version=VERSION                   return 0 if the module is at no newer than version VERSION
  --list-all                              list all known packages
  --debug                                 show verbose debug information
  --print-errors                          show verbose information about missing or conflicting packages,default if --cflags or --libs given on the command line
  --silence-errors                        be silent about errors (default unless --cflags or --libsgiven on the command line)
  --errors-to-stdout                      print errors from --print-errors to stdout not stderr
  --print-provides                        print which packages the package provides
  --print-requires                        print which packages the package requires
  --print-requires-private                print which packages the package requires for static linking

以ffmpeg中的libavcodec库为例做一个简单说明

# cat /usr/lib64/pkgconfig/libavcodec.pc
prefix=/usr
exec_prefix=${prefix}
libdir=/usr/lib64
includedir=/usr/include/ffmpeg

Name: libavcodec
Description: FFmpeg codec library
Version: 57.107.100
Requires: 
Requires.private: libswresample >= 2.9.100, libavutil >= 55.78.100
Conflicts:
Libs: -L${libdir}  -lavcodec 
Libs.private: -lXv -lX11 -lXext -lfontconfig -lfreetype -lSDL2 -lv4l2 -lpulse -lmodplug -ldrm -lbluray -lass -lgnutls -lgcrypt -ldl -lgpg-error -lSDL2 -lmfx -lstdc++ -ldl -lva-drm -lva-x11 -lva -lvdpau -lX11 -lva -lva-x11 -lX11 -lva -lva-drm -lva -lxcb -lxcb-shm -lxcb-xfixes -lxcb-shape -lcdio_paranoia -lcdio_cdda -lcdio -ljack -lasound -lSDL2 -lgcrypt -ldl -lgpg-error -lGL -lOpenCL -lopenal -lzvbi -lxvidcore -lx265 -lx264 -lvpx -lm -lvpx -lm -lvpx -lm -lvpx -lm -lvorbisenc -lvorbis -lm -logg -lvorbis -lm -logg -lvo-amrwbenc -lvidstab -lm -lgomp -lv4l2 -ltheoraenc -ltheoradec -logg -lspeex -lsoxr -lrsvg-2 -lm -lgio-2.0 -lgdk_pixbuf-2.0 -lcairo -lgobject-2.0 -lglib-2.0 -lpulse -lopus -lopenjp2 -lopencore-amrwb -lopencore-amrnb -lmp3lame -lmodplug -lmfx -lstdc++ -ldl -lva-drm -lva-x11 -lva -lgsm -lfribidi -lfreetype -lfontconfig -lfreetype -ldrm -lbluray -lass -lgnutls -lm -ldl -lbz2 -lz -pthread
Cflags: -I${includedir}

pkg-config的工作原理是根据指定库名称的pc文件获取相应编译参数

# pkg-config libavcodec --print-requires     // 依赖  {Requires}
# pkg-config libavcodec --print-provides     // 库提供者 {Name}:{Version}
libavcodec = 57.107.100
# pkg-config libavcodec --print-requires-private   // 库依赖的库 {Requires.private}
libswresample >= 2.9.100
libavutil >= 55.78.100
# pkg-config libavcodec --libs		// 连接该动态库时使用的连接选项  {Libs}
-lavcodec  
# pkg-config libavcodec --cflags    // 编译时使用的编译选项  {Cflags}
-I/usr/include/ffmpeg  
# pkg-config libavcodec --libs --static  // 使用静态库时使用的连接参数  {Libs} {Libs.private} {Requires.private}
-pthread -lavcodec -lXv -lXext -lvdpau -lX11 -lxcb -lxcb-shm -lxcb-xfixes -lxcb-shape -lcdio_paranoia -lcdio_cdda -lcdio -ljack -lasound -lSDL2 -lgcrypt -lgpg-error -lGL -lOpenCL -lopenal -lzvbi -lxvidcore -lx265 -lx264 -lvpx -lvorbisenc -lvorbis -lvo-amrwbenc -lvidstab -lgomp -lv4l2 -ltheoraenc -ltheoradec -logg -lspeex -lrsvg-2 -lgio-2.0 -lgdk_pixbuf-2.0 -lcairo -lgobject-2.0 -lglib-2.0 -lpulse -lopus -lopenjp2 -lopencore-amrwb -lopencore-amrnb -lmp3lame -lmodplug -lmfx -lstdc++ -lva-drm -lva-x11 -lva -lgsm -lfribidi -lfontconfig -lfreetype -lbluray -lass -lgnutls -ldl -lbz2 -lz -lswresample -lsoxr -lavutil -ldrm -lm  
# pkg-config libavcodec --cflags --static
-I/usr/include/ffmpeg

可以看出使用库的静态库使用,连接时需要连接该静态库以及静态库依赖的库(如果是静态库,需要安装依赖的开发包,yum安装的运行时库都是后缀有版本号的,连接时查找的动态库不带版本号)

连接顺序问题

https://gcc.gnu.org/onlinedocs/gcc-5.2.0/gcc/Link-Options.html#Link-Options

动态库使用实例

定义库的头文件

/*caculate.h*/

#ifndef CACULATE_HEAD_
#define CACULATE_HEAD_
//加法
int add(int a, int b);
//减法
int sub(int a, int b);
//除法
int div(int a, int b);
//乘法
int mul(int a, int b);

#endif

库中函数的实现

/*caculate.c文件*/
#include "caculate.h"

//求两个数的和
int add(int a, int b)
{
    return (a + b);
}
//减法
int sub(int a, int b)
{
    return (a - b);
}
//除法
int div(int a, int b)
{
    return (int)(a / b);
}
//乘法
int mul(int a, int b)
{
    return (a * b);
}

编译生产libcac.so文件如下: gcc -shared -fPIC caculate.c -o libcac.so

动态库的使用方法1

#include <stdio.h>
#include "caculate.h"

int main()
{
    int a = 20;
    int b = 10;
    printf("%d + %d = %d\n", a, b, add(a, b));
    printf("%d - %d = %d\n", a, b, sub(a, b));
    printf("%d / %d = %d\n", a, b, div(a, b));
    printf("%d * %d = %d\n", a, b, mul(a, b));
    return 0;
}

编译运行:

gcc main.c -o main -L ./ -lcac
LD_LIBRARY_PATH=.:$LD_LIBRARY_PATH ./main

动态库的使用方法2

#include <stdio.h>
#include <dlfcn.h>

#define DLL_FILE_NAME "libcac.so"

int main()
{
    void *handle;
    int (*func)(int, int);
    char *error;
    int a = 30;
    int b = 5;

    handle = dlopen(DLL_FILE_NAME, RTLD_NOW);
    if (handle == NULL)
    {
    fprintf(stderr, "Failed to open libaray %s error:%s\n", DLL_FILE_NAME, dlerror());
    return -1;
    }

    func = dlsym(handle, "add");
    printf("%d + %d = %d\n", a, b, func(a, b));

    func = dlsym(handle, "sub");
    printf("%d + %d = %d\n", a, b, func(a, b));

    func = dlsym(handle, "div");
    printf("%d + %d = %d\n", a, b, func(a, b));
    
    func = dlsym(handle, "mul");
    printf("%d + %d = %d\n", a, b, func(a, b));

    dlclose(handle);
    return 0;
}

编译运行

gcc call_main.c -o call_main -ldl -Wl,-rpath,./
./main

这里写图片描述

依赖静态库,编译生成动态库

opencv的动态库依赖一个三方静态库libippicv.a

$ PKG_CONFIG_PATH=/usr/local/opencv/lib/pkgconfig pkg-config --libs opencv
-L/usr/local/opencv3.1.0/lib -L/usr/local/opencv3.1.0/share/OpenCV/3rdparty/lib -lopencv_shape -lopencv_stitching -lopencv_objdetect -lopencv_superres -lopencv_videostab -lippicv -lopencv_calib3d -lopencv_features2d -lopencv_highgui -lopencv_videoio -lopencv_imgcodecs -lopencv_video -lopencv_photo -lopencv_ml -lopencv_imgproc -lopencv_flann -lopencv_core  
$ PKG_CONFIG_PATH=/usr/local/opencv/lib/pkgconfig pkg-config --cflags opencv
-I/usr/local/opencv3.1.0/include/opencv -I/usr/local/opencv3.1.0/include

动态库版本兼容问题

动态库与静态库同时存在

参考

  • 5
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

callinglove

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值