当前位置: 首页 > news >正文

cmake(动态库和静态库)

官方文档:https://cmake.org/cmake/help/latest/index.html

现代cmake 基于目标的库的发布和查找流程。

静态库

静态库是在编译期就把一组目标文件(.o / .obj),打包成一个归档文件,并在最终链接时,一次性复制到可执行文件或共享库中的代码与数据集合中。

静态库只是一个归档目标文件,没有经过链接阶段。在生成阶段不需要链接,在使用阶段才会发生链接。

一、文件形式

Linux / macOS/ BSD:libfoo.a(ar 归档格式)

windows:foo.lib (COFF归档格式,区别于import-lib)

现在制作一个加减法的静态库

下面是目录的结构,和每个CMakeLists.txt的内容

下面是构建目录

可以观察到,源代码目录和构建目录生成的二进制文件静态库位置是对应的。如果需要修改生成的位置。比如需要将二进制文件放进bin目录下,库文件放在lib目录下,可以使用RUNTIME_OUTPUT_DIRECTORY目标属性更改默认输出路径。

下面分别是修改二进制文件和库文件默认输出位置,在各自的CMakeLists.txt中进行修改。

CMAKE_BINARY_DIR的值,就是执行 cmake 命令时所在的那一层目录

下面是执行结果

可以观察到,二进制文件和库文件已经生成在了指定的目录下了

重点命令行解释

cmake是基于目标属性传递的现代化构建系统。

这里有目标属性属性API属性传递机制四个概念。其中目标、属性和属性API就是cmake的三大核心,这样的三大核心和属性的传递机制,共同构建了一个现代化的构建系统的核心。

目标-Target

目标有类型、属性和操作属性的API。

目标的种类如下

类型创建命令说明
EXECUTABLE
add_executable
main ,cur
STATIC
add_library(... STATIC)
libfoo.a, foo.lib
SHARED
add_library(... SHARED)
libfoo.so, foo.dll
MODULE
add_library(... MODULE)
插件:libplugin.so, 使⽤dlopen 运⾏时加载
OBJECT
add_library(... OBJECT)
仅 .o/.obj,存在于内存,不⽣成库⽂件
INTERFACE
add_library(... INTERFACE)
⽆库⽂件,携带使⽤要求
IMPORTED
add_library(... IMPORTED)
使⽤cmake 内存⽬标对象引⽤磁盘上的外
部构建产物
ALIAS
add_library(... ALIAS)
为同项⽬内的现有⽬标取别名

属性-Property

属性种类如下

类别作用域典型读/写命令常用属性示例
全局属性 (Global)
整个 CMake 运⾏⽣命周期
get/set_property(GLOBAL
PROPERTY …)
CMAKE_ROLE
⽬录属性
(Directory)
当前源码⽬录及其⼦ ⽬录
get/set_property(DIRECTORY
PROPERTY …)
INCLUDE_DIRECTORIES
⽬标属性 (Target)
单个构建⽬标(库、
可执⾏、接⼝库…)
get/set_property(TARGET
<tgt> PROPERTY …)
LINK_LIBRARIES
INCLUDE_DIRECTORIES
源⽂件属性
(Source File)
单个源码/资源⽂件
get/set_source_files_properti
es
COMPILE_FLAGS
测试属性 (Test)
由 add_test() 定义的
单个测试
get/set_tests_properties()
WORKING_DIRECTORY
安装⽂件属性
(Installed File)
install() ⽣成的安装
清单条⽬
set_property(INSTALL …
PROPERTY …)
RPATH

属性的作用域与传播范围(main ----> curl)

关键字对当前目标的构建影响是否传播对当前目标使用者的影响解释例子(面包和面粉的例子)
PRIVATE
生效不生效自己用制作面包的面粉品牌不公开
PUBLIC
生效生效自己-下游用
公开制作⾯包的⾯粉的品牌

INTERFAC
E
不生效生效自己不用-下游用
说明书,说明⽤什么⾯粉制
作,不卖东西

这里的关键字类似于c++中的限定关键字

操作目标和属性的API

类别典型命令(可选关键词)主要作用涉及的核心属性(部分示例)
1. 通⽤读 / 写接
set_target_properties()
get_target_property()
任意⽬标属性的设置、
追加、查询(最底层
API)
任何 prop_tgt
2. 编译阶段相关
target_compile_definitions
target_compile_options
target_precompile_headers
target_include_directories
target_sources
控制源⽂件编译:宏定
义、编译选项、语⾔特
性、预编译头、包含⽬
录、源⽂件列表等
COMPILE_DEFINITIONS、
COMPILE_OPTIONS、
COMPILE_FEATURES、
PRECOMPILE_HEADERS、
INCLUDE_DIRECTORIES、
SOURCES 等
3. 链接 & 输出
阶段相关
target_link_libraries
target_link_options
target_link_directories
配置⽬标被链接时的
库、选项及搜索路径
LINK_LIBRARIES
INTERFACE_LINK_LIBRARIES
LINK_OPTIONS
INTERFACE_LINK_OPTIONSLI
NK_DIRECTORIES
INTERFACE_LINK_DIRECTORIE
S
4. 安装 & 打包
阶段相关
install(TARGETS …)
install(EXPORT …)
⽣成安装规则与包,控
制⽬标在安装树中的布
局及其运⾏时⾏为
RUNTIME_OUTPUT_DIRECTO
RY、
LIBRARY_OUTPUT_DIRECTOR
Y、
ARCHIVE_OUTPUT_DIRECTOR
Y
EXPORT_NAME、
INSTALL_RPATH

add_library

添加一个静态库或者动态库目标,让cmake从指定的文件列表生成。

参数解释

参数含义
<name>
库的名称(不包含前缀和后缀,如 Foo 会⽣成 libFoo.a)。项⽬内部唯⼀
STATIC
创建静态库(默认值,若不指定类型)。
SHARED
创建动态库(共享库)。
【source】
构建库的源⽂件列表。

默认情况下,将在与调⽤命令的源树⽬录相对应的构建树⽬录中创建库⽂件。 可以使⽤ARCHIVE_OUTPUT_DIRECTORY LIBRARY_OUTPUT_DIRECTORY RUNTIME_OUTPUT_DIRECTORY ⽬标属性修改默认的输出路径。

target_include_directories

设置⽬标在开发发布阶段的头⽂件搜索⽬录,可以传递性传播给下游的使⽤⽅。

参数解释

参数含义
<target>
⽬标名称(由 add_executable 或 add_library 定义)
可以是普通库/可执⾏,也可以是 INTERFACE_LIBRARY 或导⼊⽬标
(IMPORTED)
<INTERFACE|PUBLIC|PRIVATE>
属性的作⽤域关键字
path
头⽂件搜索路径:
如果是相对路径则相对于当前的CMAKE_CURRENT_SOURCE_DIR

通过target_include_directories添加的 路径 最终是通过gcc 的 -I 参数传递给编译器的。

设置⼆进制⽬标的依赖库列表,相当于使⽤通⽤的set属性设置函数设置了LINK_LIBRARIES或者 INTERFACE_LINK_LIBRARIES这个属性,在cmake源代码⾥这2个属性是2个 vector<string> 成员。最终以 -l 的形式出现在gcc参数⾥

参数解释

参数含义
<target>
⽬标名称(由 add_executable 或 add_library 定义)
<INTERFACE|PUBLIC|PRIVATE>
属性的作⽤域关键字

PRIVATE 关键字:相当于使⽤set_target_properties 设置了LINK_LIBRARIES属性,设置的库列表 只 会写进⽬标的LINK_LIBRARIES列表⾥。

INTERFACE 关键字:相当于使⽤set_target_properties 设置了INTERFACE_LINK_LIBRARIES,只会 写进⽬标的INTERFACE_LINK_LIBARIERS列表⾥。

PUBLIC 关键字设置的列表会同时写进LINK_LIBRARIES和INTERFACE_LINK_LIBRARIES⾥。INTERFACE_LINK_LIBRARIES 列表出现的库会被传播给这个⽬标的使⽤⽅。

通过 target_link_libraries最终是通过gcc 的-l 选项传递给链接器的。

作用:

1、设置目标的依赖库列表,列出的依赖者会以 -l 的形式出现目标的gcc的参数中

2、建立依赖关系,被依赖者需要传播的属性可以沿着关系链传播给依赖者

解释:

在发布方发布库的时候,需要设置INTERFACE级别属性,告诉下游使用方,使用该库的时候需要链接什么资源以及配置文件。

而在下游的使用方,首先使用find_package查找到发布的库,创建可执行文件,并通过target_link_libraries读取上游发布方告知使用该库需要的组件的信息。

使用示例

接下来使用add_library、target_include_directories、target_link_libraries做个示例

首先下面是结构目录,和各级目录下的CMakeLists.txt内容

然后观察构建目录的生成流程,进入build目录,运行cmake --build . -v

在生成静态库的时候,正确执行了去对应位置下寻找private和public。也对应属性作用域的传播范围

在编译链接执行的之后,正确执行了去对应位置下寻找public和interface,包括需要额外链接的依赖库

API

操作通用读/写接口

set_target_properties和 get_target_properties

设置/查询 ⽬标(如可执⾏⽂件、库)的各种属性,控制编译、链接、安装等⾏为

参数解释

参数含义
<target1>
库的名称
<prop1> <value1>
属性名字和值

常见的属性名字和含义

属性名字含义gcc选项
INCLUDE_DIRECTORIES
构建规范-⾃⼰编译时包含的⽬录
-l
INTERFACE_INCLUDE_DIRECTORIES
使⽤要求-下游适⽤房需要包含的⽬
-l
LINK_LIBRARIES
构建规范-⾃⼰链接时需要链接的库
列表
-l
INTERFACE_LINK_LIBRARIES
使⽤要求-⾃⼰链接时需要链接的库
列表
-l
LIBRARY_OUTPUT_DIRECTORY
库的输出路径
LIBRARY_OUTPUT_NAME
库的⽂件的输出名字
BUILD_RPATH
构建⽬录中的运⾏时库⽂件搜索路
-Wl,-rpath
INSTALL_RPATH
安装⽬录中的运⾏时库⽂件搜索路
-Wl,-rpath

add_subdirectory

添加⼦⽬录到构建树,cmake会⾃动进⼊到源码树⼦⽬录,执⾏位于⼦⽬录⾥的CMakeLists.txt⽂件。(add_subdirectory()是CMake构建层次结构里的“递归入口”)

处理顺序:当处理到add_subdirectory的时候,CMake 会⽴即处理 source_dir 中的 CMakeLists.txt,当前⽂件的处理会暂停,直到⼦⽬录 处理完毕,在继续处理当前⽂件add_subdirectory之后的命令。

1)、目录变量作用域

子目录能读取父目录已经set的变量,也能覆盖再向下传递(继承性传递);父目录看不到子目录定义的普通变量(除非使用PARENT_SCOPE或CACHE)。

2)、目标范围

只要add_library()/ add_executable()定义了目标,他就被注册到了全局目标表(map<string, cmTarget>),在任何目录都能链接。

3)、include()/add_subdirectory()区别

include()只是立即执行另一个cmake脚本文件,不会开辟新的目录作用域,而add_subdirectory()会进入新的CMAKE_CURRENT_SOURCE_DIR / BINARY_DIR并递归生成。       

include(<file>) = 把这段脚本当做当前目录的一部分立即执行。add_subdirectory(<dir>) = 进入一个新目录作用域,递归执行其中的CMakeLists.txt,并为它指定独立的构建输出目录

参数解释

参数含义
source_dir
通常为当前⽂件夹下的⼦⽬录的名字。
[binary_dir]
cmake会在构建树⾥创建同名的⼦⽬录,⽤于保存⼦⽬录的的cmake⽂件⾥⽣成的⽬标和⼆进制

cmake内部静态库的生成与定位流程

第一步:目标生成

当你写下add_library(MyMath STATIC add.cpp sub.cpp)这⼀⾏命令时,cmake内部会在全局的 Targets 容器中注册⼀个名为 MyMath 的cmTarget⽬标。

第二步:目标信息存储

每个cmTargetInternals会存储⽬标的名称,类型,源⽂件列表,输出地址等属性。当你使⽤LIBRARY_OUTPUT_DIRECTORY等属性修改⽬标输出地址的时候,cmake也会更新最终的输出地址。

第三步:生成器阶段-定位静态库路径

CMake 配置阶段结束后,进⼊⽣成阶段(cmLocalGenerator、cmGlobalGenerator),⽣成器会遍历所有⽬标(cmTarget),根据⽬标的属性(如 ARCHIVE_OUTPUT_DIRECTORY)和平台规则, 推导出静态库的实际输出路径。⽐如:build/libmylib.a。

第四步:链接命令的⽣成

⽣成器会为每⼀个⽬标⽣成链接规则,其中包括⽬标⽂件的输出路径,最终会⽣成⼀下 makefile 指令

静态库(编译、链接、安装)

安装头文件、库文件和定义导入目标

常见内置变量

变量含义常见取值
CMAKE_SOURCE_DIR顶层源码目录(整个项目的根)
CMAKE_BINARY_DIR顶层构建目录(整个构建的根)
CMAKE_INSTALL_LIBDIR安装时库文件的标准相对目录(由 GNUInstallDirs 提供)lib 或 lib64
CMAKE_INSTALL_INCLUDEDIR安装时头文件的标准相对目录(由 GNUInstallDirs 提供)include
CMAKE_CURRENT_BINARY_DIR当前正在处理的 CMakeLists.txt 对应的构建目录
CMAKE_CURRENT_SOURCE_DIR当前正在处理的 CMakeLists.txt 所在的源码目录

下面是目录结构

下面是顶级CMakeLists.txt的内容

cmake_minimum_required(VERSION 3.27.4)project(InstallMyMathLANGUAGES CXX
)add_subdirectory(my_lib)

下面是子目录下的CMakeLists.txt内容(关键),

# 生成数学库和发布使用
# 1-4是数学库的生成  5-8是头文件的安装 9-10是安装配置文件的生成# 1、收集源代码
file(GLOB SRC_LISTS "src/*.cpp")
# 2、添加构建目标
add_library(MyMath STATIC ${SRC_LISTS})
# 3、设置库的使用要求,也就是下游消费者必须包含的头文件搜索路径
target_include_directories(MyMath INTERFACE # CMAKE_INSTALL_INCLUDEDIR的值就是include"$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>" # /usr/local/include 
)
# 4、设置库的默认输出路径
set_target_properties(MyMath PROPERTIESARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib
)# 凡是涉及到安装树的时候都是使用的相对路径,cmake会自动将相对路径拼接在prefix路径后面
# CMAKE_INSTALL_PREFIX的值就是/usr/local,这些内置变量都是在GNUInstallDirs模块中定义好的# 5、安装静态库文件
include(GNUInstallDirs)
install(TARGETS MyMath# 导出集合 在导出的时候,使用这个导出集合即可EXPORT MyMathTargets DESTINATION ${CMAKE_INSTALL_LIBDIR} # CMAKE_INSTALL_LIBDIR的值就是lib
)
# 6、安装静态库头文件
install(DIRECTORY include/DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/math # /usr/local/include/math/math.hFILES_MATCHING PATTERN "*.h" # 过滤
)
# 7、安装导出目标集合 到 构建目录(构建树)(本地)
export(EXPORT MyMathTargets# CMAKE_CURRENT_BINARY_DIR当前构建目录FILE ${CMAKE_CURRENT_BINARY_DIR}/MyMathTargets.cmake 
)
# 8、安装导出目标集合 到 安装目录(安装树)
install(EXPORT MyMathTargetsFILE MyMathTargets.cmakeNAMESPACE MyMath:: # 命名空间 MyMath::MyMath# CMAKE_INSTALL_LIBDIR标准安装路径DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/MyMath # /usr/local/lib/cmake/MyMath/MyMathTargets.cmake
)# 9、生成find_package需要的配置文件
include(CMakePackageConfigHelpers)
configure_package_config_file("${CMAKE_CURRENT_SOURCE_DIR}/Config.cmake.in" # 自己定义的模版"${CMAKE_CURRENT_BINARY_DIR}/MyMathConfig.cmake" #生成结果(构建目录)INSTALL_DESTINATION "lib/cmake/MyMath" #最终安装位置
)# 10、安装配置文件(第9步生成的)到cmake 规定的标准的安装路径
install(FILES"${CMAKE_CURRENT_BINARY_DIR}/MyMathConfig.cmake"DESTINATION "lib/cmake/MyMath"
)# 1-4 步:把代码变成静态库,并设定“对外接口”(头文件路径)。
# 5-6 步:把库文件和头文件“拷贝”到系统。
# 7-8 步:生成“导出脚本”,让 CMake 能在任何位置重新导入已安装的目标。
# 9-10 步:提供 find_package 的入口文件,完成“安装即发现”。

这是最终的执行结果

CMAKE_CURRENT_LIST_DIR表示当前正在处理的 .cmake 脚本或 CMakeLists.txt 文件所在的目录。

查找并使用目标

这是使用方的目录结构

如果在main.cpp中使用#include "math/math.h"会报错,就需要在c_cpp_properties.json中的includePath中增添 /usr/local/include 路径。

下面是CMakeLists.txt中的内容

cmake_minimum_required(VERSION 3.27.4)project(TestMyMath)add_executable(main main.cpp)# 查找MyMath数学库
find_package(MyMath CONFIG REQUIRED)
# 声明依赖
target_link_libraries(main PRIVATE MyMath::MyMath)

使用效果如下

export

项⽬中的⽬标(如可执⾏⽂件、库)及其相关属性导出到⼀个⽂件中,以便在其他项⽬中使⽤。

参数解释

参数含义
<export_name>
导出集的名称,⽤于在其他项⽬中引⽤
<file_name>
导出⽂件的名称,通常为 <export_name>Config.cmake
NAMESPACE <namespace>:
(可选)
为导出的⽬标添加命名空间,避免命名冲突
DESTINATION <destination>:
(可选)
指定导出⽂件的安装⽬录
CONFIGURATIONS <config1> :
(可选)
指定要导出的配置(如 Debug、Release)
INCLUDE_INSTALL_DIRS:(可选)
导出的配置⽂件中包含安装路径

export() 命令会⽣成⼀个 CMake ⽂件,其中包含已导出⽬标的信息(如位置、依赖项、编译选项)。 其他项⽬可以通过 find_package() 加载此配置⽂件,从⽽使⽤你项⽬中的⽬标。

通常和install 配合使⽤, install 定义需要导出的导出组,export 将导出组中的所有⽬标导出到配置⽂ 件。

与 install(EXPORT) 命令的区别

export(EXPORT):⽣成临时配置⽂件,仅⽤于从构建⽬录中导出⽬标。

install(EXPORT ...):⽣成正式配置⽂件,并安装到系统,⽤于从安装⽬录导出⽬标。

configure_package_config_file

根据模板⽂件⽣成⾃定义包配置⽂件,可以被其他项⽬使⽤来查找和配置你的包。

参数解释

参数含义
<input>
模板⽂件的路径,通常是⼀个 CMake 脚本⽂件,其中包含了⼀些变量和 逻辑,⽤于⽣成最终的配置⽂件
<output>
⽣成的配置⽂件的路径
COPYONLY:(可选)
如果指定,将直接复制模板⽂件⽽不进⾏任何配置
INSTALL_DESTINATION <dir>: (可选)
指定⽣成的配置⽂件的安装⽬录
CONFIGURATIONS <config1> : (可选)
指定要导出的配置(如 Debug、Release)
NO_SET_AND_CHECK_MACRO:
(可选)
如果指定,将不⽣成 set_and_check 宏,⽤于设置变量并验证其路径是
否存在
NO_CHECK_REQUIRED_COMPO
NENTS_MACRO:(可选)
如果指定,将不⽣成 check_required_components 宏,⽤于检查包的 所有必需组件是否都已找到并正确配置。

动态库(编译、链接、引用)

当前的目录结构如下

app下的CMakeLists.txt文件内容。这是作为动态库使用方的CMakeLists.txt文件

# 1、搜集文件列表
file(GLOB SRC_LISTS "*.cpp")
# 2、添加构建目标
add_executable(main ${SRC_LISTS})# 3、添加依赖库列表
# 这里是指定链接时的库,这样链接器才能找到库中定义,完成重定位
target_link_libraries(main PRIVATE MyMath)# 4、修改默认的输出路径
set_target_properties(main PROPERTIESRUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin
)# 5、打印库文件相对main的相对路径 自动定义命令
add_custom_command(TARGET mainPOST_BUILDCOMMAND ${CMAKE_COMMAND} -E echo "$<TARGET_FILE:MyMath>"COMMENT "输出动态库的全路径"
)

my_lib下的CMakeLists.txt文件内容。这是作为动态库的发布方的CMakeLists.txt文件

# 1、收集库的源代码
#将src目录下的所有cpp文件添加到SRC_LISTS变量中
file(GLOB SRC_LISTS "src/*.cpp")# 2、添加构建目标
#将SRC_LISTS变量中的文件构建动态库MyMath
add_library(MyMath SHARED ${SRC_LISTS})# 3、设置库的使用要求
# 实际上是告诉使用方头文件的位置
target_include_directories(MyMathPUBLIC ${CMAKE_CURRENT_LIST_DIR}/include
)# 4、修改默认的输出路径
set_target_properties(MyMath PROPERTIESARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/libLIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib
)# 5、添加 -fPIC 编译选项
target_compile_options(MyMath PRIVATE "-fPIC") #-fPIC全称为Position Independent Code,即位置无关代码,

顶级的CMakeLists.txt文件如下

# 1、设置最低版本号
cmake_minimum_required(VERSION 3.27.4)
# 2、设置项目名称
project(TestMyMath)
# 3、添加目录层级
add_subdirectory(my_lib)
add_subdirectory(app)

现在,下面是动态库的构建过程

这里可以看见动态库的生成过程中,使用了-fPIC选项,最终生成了以so结尾的动态库文件。

这里为什么生成动态库需要使用-fPIC选项呢?

-fPIC的全称为(Position Independent Code)位置无关代码。使用-fPIC选项的作用是让编译器生成“与位置无关”的机器码(Position Independent Code)。动态库必须被加载到任意进程地址空间,且多个进程要共享同一份物理代码段。如果代码不是位置无关的,就无法满足这两个需求。

另外,可以观察到。这里使用target_link_libraries命令和静态库的使用完全一致,并没有标注依赖的是静态库还是动态库。这是因为,在生成动、静态库的时候,有一个全局的cmTarget属性,其中包含着目标的属性,包括是static还是shared类型的。在使用target_link_libraries的时候,会自动根据这里的属性判断库的属性。

系统动态加载器-ld

elf文件介绍

elf文件是是一种用于可执行文件、目标文件、共享库的标准文件格式,它包含了程序运行所需的二进制代码、数据、符号表、重定位信息等,是操作系统加载和运行程序的基础。

elf作用:1、作为可执行文件的容器,存储编译后的机器指令和数据。2、通过共享库(.so 文件)实现代码复用,减少内存占用。3、统一的格式使得不同工具链(编译器、链接器、调试器)能够协同工作。4、包含调试符号(如 DWARF 格式),支持 GDB 等调试工具。

elf文件结构:

组成部分作用
ELF Header描述文件类型(可执行/共享库/目标文件)、目标架构(如 x86-64)、入口地址等。
Program Header Table描述如何加载文件到内存(段信息),仅存在于可执行文件和共享库中。
Section Header Table描述文件的节区(如 .text.data.bss),用于链接和调试。
节区(Sections)存储实际数据,如代码(.text)、全局变量(.data)、符号表(.symtab)等。

如何加载并运行elf文件?

内核加载过程(Linux为例):

当用户运行 ./program 时,操作系统通过以下步骤加载 ELF 文件:

1、读取 ELF Header

内核首先检查文件的前几个字节(7f 45 4c 46,即 ELF 的 ASCII 编码),确认是 ELF 格式。

2、解析 Program Header Table

内核根据段类型(如 PT_LOAD)将文件的代码段(.text)、数据段(.data)等加载到内存中,并设置权限(代码段为 r-x,数据段为 rw-)。

3、处理动态链接

如果 ELF 文件是动态链接的(如依赖 libc.so),内核会加载动态链接器(如 /lib64/ld-linux-x86-64.so.2),由它负责:

        加载所需的共享库列表并加载到内存。

        重定位符号(如 printf 的地址)。

        初始化全局变量(如 C++ 的全局构造函数)。

4、跳转到入口点

内核将 CPU 指令指针(RIP)设置为 ELF Header 中指定的入口地址(通常是 _start,而非 main),开始执行程序。

elf怎么知道程序依赖哪些动态库,并从哪里去加载需要的动态库的?

首先从elf文件中的DT_NEEDED字段了解到(需要的库)

另外动态链接器依次按照LD_LIBRARY_PATH环境变量列出的目录ELF文件的DT_RUNPATH记录系统缓存 /etc/ld.so.cache默认目录 /lib、/usr/lib/... 中去查找。如果在这四个路径下都没找到,就会报错。

从这里可以看出LD_LIBRARY_PATH的优先级高于elf文件中的DT_RUNPATH

cmake中的动态加载器

相较于上面Linux中使用动态加载器去寻找需要的库的逻辑,可以看见更多的依赖环境变量的设置,如果环境变量的设置出错,出错率就比较高。而cmake直接就将寻找动态库的逻辑改为了在elf文件的头中的DT_RUNPATH中去寻找。

动态库(编译-链接-安装)

下面是动态库的目录结构

顶级CMakeLists.txt中的内容

cmake_minimum_required(VERSION 3.27.4)project(InstallMyMathLANGUAGES CXX
)
add_subdirectory(my_lib)

子文件夹中的CMakeLists.txt中的内容

# 生成数学库和发布使用
# 1-4是数学库的生成  5-8是头文件的安装 9-10是安装配置文件的生成# 1、收集源代码
file(GLOB SRC_LISTS "src/*.cpp")
# 2、添加构建目标
add_library(MyMath SHARED ${SRC_LISTS})
# 3、设置库的使用要求,也就是下游消费者必须包含的头文件搜索路径
target_include_directories(MyMath INTERFACE # CMAKE_INSTALL_INCLUDEDIR的值就是include"$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>" # /usr/local/include 
)
# 4、设置库的默认输出路径
set_target_properties(MyMath PROPERTIESLIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/libOUTPUT_NAME MyMathVERSION 1.2.3SOVERSION 20COMPILE_OPTIONS "-fPIC"
)# 凡是涉及到安装树的时候都是使用的相对路径,cmake会自动将相对路径拼接在prefix路径后面
# CMAKE_INSTALL_PREFIX的值就是/usr/local,这些内置变量都是在GNUInstallDirs模块中定义好的# 5、安装静态库文件
include(GNUInstallDirs)
install(TARGETS MyMath# 导出集合 在导出的时候,使用这个导出集合即可EXPORT MyMathTargets DESTINATION ${CMAKE_INSTALL_LIBDIR} # CMAKE_INSTALL_LIBDIR的值就是lib
)
# 6、安装静态库头文件
install(DIRECTORY include/DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/math # /usr/local/include/math/math.hFILES_MATCHING PATTERN "*.h" # 过滤
)
# 7、安装导出目标集合 到 构建目录(构建树)(本地)
export(EXPORT MyMathTargets# CMAKE_CURRENT_BINARY_DIR当前构建目录FILE ${CMAKE_CURRENT_BINARY_DIR}/MyMathTargets.cmake 
)
# 8、安装导出目标集合 到 安装目录(安装树)
install(EXPORT MyMathTargetsFILE MyMathTargets.cmakeNAMESPACE MyMath:: # 命名空间 MyMath::MyMath# CMAKE_INSTALL_LIBDIR标准安装路径DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/MyMath # /usr/local/lib/cmake/MyMath/MyMathTargets.cmake
)# 9、生成find_package需要的配置文件
include(CMakePackageConfigHelpers)
configure_package_config_file("${CMAKE_CURRENT_SOURCE_DIR}/Config.cmake.in" # 自己定义的模版"${CMAKE_CURRENT_BINARY_DIR}/MyMathConfig.cmake" #生成结果(构建目录)INSTALL_DESTINATION "lib/cmake/MyMath" #最终安装位置
)# 10、安装配置文件(第9步生成的)到cmake 规定的标准的安装路径
install(FILES"${CMAKE_CURRENT_BINARY_DIR}/MyMathConfig.cmake"DESTINATION "lib/cmake/MyMath"
)# 1-4 步:把代码变成动态库,并设定“对外接口”(头文件路径)。
# 5-6 步:把库文件和头文件“拷贝”到系统。
# 7-8 步:生成“导出脚本”,让 CMake 能在任何位置重新导入已安装的目标。
# 9-10 步:提供 find_package 的入口文件,完成“安装即发现”。

动态库(查找-使用)

无论是动态库还是静态库的使用,大体都是一样的。只是在编译链接的时候,会有所不同。

这里查找库的路径是 /usr/local/lib下的动态库。

而在未安装之前,查找库的路径是构建树的目录。

rpath是有两个声明周期的,一个是构建阶段另一个是在安装阶段。而在安装之后,cmake会自动将rpath修改成为安装阶段的时候。

下面查看elf文件可以看出,rpath已经修改为了 /usr/local/lib 了

http://www.dtcms.com/a/447752.html

相关文章:

  • 装饰网站建设优惠套餐金华品牌网站建设
  • 使用Qt制作串口助手
  • 字符串最后一个单词的长度
  • 【第几小 / 分块】
  • 做网站 视频外链智慧团建注册志愿者入口
  • 书生浦语实战营第六期L1-G1000
  • 做网站得基础自适应 网站
  • 厦门网站建设外贸官方网站数据如何做脚注
  • 设计公司网站推广营销wordpress 阿里云虚拟主机
  • 做汽配找哪个网站好抖音分销系统开发
  • 发布网站后备案广西腾达建设集团有限公司网站
  • 上海市城市建设管理局网站网站降权恢复
  • 网站开发工程师 课程大纲服务号开发
  • 黑客怎么入侵网站ipad做网站服务器
  • 网站制作实验报告怎样做网络推广方案服务
  • 辛集建设局网站网站优化加盟
  • 建设网站需要哪些人海口模板建站定制
  • 创建网站的代码wordpress 心情评论插件
  • 网站敏感关键词.txt什么叫做优化
  • 手机投资网站网站备案审核要多久
  • 未经网安备案开设网站的最专业的外贸网站建设
  • 南昌专门做网站的公司网站建设征求意见表
  • 自贡建设能源开发有限公司网站河池网站优化
  • 周口网站建设公司免费的简历模板
  • 北京网站排名宝安网站推广平台
  • 简单网站制作软件wdcp 修改默认网站
  • 芜湖网站建设全包仅需800元青岛做公司网站的多吗
  • 中文wordpress网站模板wordpress the7 中文
  • 智慧旅游网站开发与设计专做淘宝的网站
  • 网站开发维护人员学校网站