• Makefile第十二课:Makefile动态库


    Makefile动态库

    前言

    学习杜老师推荐的Makefile教程视频,链接。记录下个人学习笔记,仅供自己参考。

    之前有转载过杜老师的从零Makefile落地算法大项目文章,感兴趣的可以看看。

    本课程主要讲解Makefile中动态库的编译和链接。

    1.Intro

    动态库的编译和链接过程跟静态库其实大体是一致的,只不过会有一些细微的参数上和命令上的差别。

    动态库是指程序在运行的时候去调用的一些库,它跟静态库的一个区别就是静态库会跟执行文件全部打包在一起然后编译出来,这样占用的内存空间较大;而动态库的代码和数据在编译时并没有被复制到可执行文件中,而是在运行时由操作系统动态加载到内存中。

    动态库的编译和链接步骤如下:

    编库

    编译 .c 文件 源文件 [.c/cpp] → Object 文件 [.o]

    g++ -c [.c/cpp][.c/cpp]... -o [.o][.o]... -I[.h/hpp] -g -fPIC
    
    • 1

    Object 文件 [.o] → 动态库文件 [lib 库名 .so]

    g++ -shared [.o][.o]... -o [lib库名.so] 
    
    • 1

    main 文件 [.c/cpp] → Object 文件 [.o]

    g++ -c [main.c/cpp] -o [.o] -I[.h/hpp] -g
    
    • 1

    链接

    链接 main 的 Object 文件与动态库文件 [lib 库名 .so]

    g++ [main.o] -o [可执行文件] -l[库名] -L[库路径] -Wl,-rpath=[库路径]
    
    • 1

    与静态库相比步骤基本相同,但是也有一些细微差别,下面我们来简单讨论下。

    差别1:为什么在编译动态库的过程中要加上 -fPIC 选项呢?-fPIC 选项的功能是什么呢?🤔

    -fPIC 是一个编译选项,它用于生成位置无关代码(Position Independent Code,PIC)。位置无关代码是一种编译方式,使得代码可以在内存中的任何位置加载和执行,而不受具体内存地址的限制。这对于动态库非常重要,因为动态库在不同的进程中可能被加载到不同的内存地址,而位置无关代码可以确保动态库在各种情况下都能正常工作。(from ChatGPT)

    下面是有关为什么编译动态库要使用 -fPIC 选项的解释:

    1. 共享性质:动态库是被多个程序共享的,因此必须能够在不同的内存地址中加载。如果不使用 -fPIC 选项生成位置无关代码,那么库中的代码可能会依赖于特定的内存地址,这会导致在不同的进程中无法正确加载和运行。

    2. 虚拟地址空间:在现代操作系统中,每个进程都有自己的虚拟地址空间,不同的进程可能将相同的库加载到不同的地址上。位置无关代码允许操作系统将库加载到适合的虚拟地址上,而不会发生冲突。

    3. 重定位:使用 -fPIC 选项生成的位置无关代码包含了相对地址或符号表,它们在运行时可以被动态链接器(如 ld.so)用来在加载库时执行必要的重定位操作。这些操作使得库中的代码能够正确地映射到当前进程的地址空间。

    综上所述,使用 -fPIC 编译选项生成位置无关代码是为了确保动态库能够在不同的进程中正确加载和执行,而不受具体内存地址的限制。这是动态库的关键特性,使其能够在共享库的情况下工作。

    差别2:为什么在链接动态库的过程中要加上 -Wl-rpath 选项呢?它们的功能是什么呢?🤔

    链接动态库时使用的 -Wl-rpath 选项是与编译器和链接器相关的选项,它们用于指定链接器的行为以及库的搜索路径,以确保在运行时能够正确找到和加载动态库。

    下面是关于这两个选项的解释:

    1. -Wl 选项:

    -Wl 选项允许你将后续的参数传递给链接器(ld)。通常,编译器(如 gcc)会在后台调用链接器来将编译的目标文件链接成可执行文件。通过 -Wl,你可以将链接器选项传递给链接器,以影响链接过程。

    例如,使用 -Wl,-rpath,/path/to/dynamic/library 可以将 -rpath 选项传递给链接器,告诉链接器在运行时搜索动态库时应该查找的路径。

    2. -rpath 选项:

    -rpath 选项用于指定运行时动态链接器(ld.so)在查找动态库时应该搜索的目录。它允许你在可执行文件中嵌入一个指定的动态库搜索路径,以确保在运行时正确找到和加载动态库。

    当你使用 -rpath 选项时,你可以将一个特定的路径添加到可执行文件中,以告诉操作系统在运行时首先搜索该路径以查找动态库,而不仅仅是依赖于默认的动态库搜索路径。这对于确保程序能够正确加载特定版本的动态库非常有用,而不受系统上其他版本的库的干扰。

    总之,-Wl 选项用于传递链接器选项,而 -rpath 选项用于指定动态库的搜索路径,以确保在运行时正确找到和加载动态库。这些选项对于确保动态库的可用性和版本一致性非常重要。

    2.编写cpp、hpp

    完整的目录结构如下所示:

    .
    ├── include
    │   ├── add.hpp
    │   └── minus.hpp
    ├── Makefile
    └── src
        ├── add.cpp
        ├── main.cpp
        └── minus.cpp
    
    2 directories, 6 files
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    add.hpp

    #ifndef ADD_HPP
    #define ADD_HPP
    
    int add(int a, int b);
    
    #endif // ADD_HPP
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    minus.hpp

    #ifndef MINUS_HPP
    #define MINUS_HPP
    
    int minus(int a, int b);
    
    #endif // MINUS_HPP
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    add.cpp

    #include "add.hpp"
    
    int add(int a, int b)
    {
        return a + b;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    minus.cpp

    #include "minus.hpp"
    
    int minus(int a, int b)
    {
        return a - b;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    main.cpp

    
    #include "add.hpp"
    #include "minus.hpp"
    #include 
    
    int main()
    {
        int a = 10; 
        int b = 5;
        
        int res1 = add(a, b);
        int res2 = minus(a, b);
        std::cout << res1 << std::endl;
    	std::cout << res2 << std::endl;
        
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    3.编译动态库

    Makefile 文件如下:

    cpp_srcs := $(shell find src -name "*.cpp")
    cpp_objs := $(patsubst src/%.cpp,objs/%.o,$(cpp_srcs))
    so_objs  := $(filter-out objs/main.o,$(cpp_objs))
    
    include_path := ./include
    
    I_options := $(include_path:%=-I%)
    
    compile_flags := -g -O3 -w -fPIC $(I_options)
    
    objs/%.o : src/%.cpp
    	@mkdir -p $(dir $@)
    	@g++ -c $^ -o $@ $(compile_flags)
    
    compile : $(cpp_objs)
    
    # ============== 编译动态库 ==============
    lib/libddd.so : $(so_objs)
    	@mkdir -p $(dir $@)
    	@g++ -shared $^ -o $@
    
    dynamic : lib/libddd.so
    
    debug :
    	@echo $(so_objs)
    
    .PHONY : debug compile dynamic
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    在终端执行 make dynamic 指令后,会在当前目录下生成 lib 文件夹,里面有生成的 libddd.so 动态库文件

    4.链接动态库

    Makefile 文件如下:

    cpp_srcs := $(shell find src -name "*.cpp")
    cpp_objs := $(patsubst src/%.cpp,objs/%.o,$(cpp_srcs))
    so_objs  := $(filter-out objs/main.o,$(cpp_objs))
    
    include_path := ./include
    library_path := ./lib
    
    linking_libs := ddd
    
    I_options := $(include_path:%=-I%)
    l_options := $(linking_libs:%=-l%)
    L_options := $(library_path:%=-L%)
    r_options := $(library_path:%=-Wl,-rpath=%)
    
    compile_flags := -g -O3 -w -fPIC $(I_options)
    linking_flags := $(l_options) $(L_options) $(r_options)
    
    objs/%.o : src/%.cpp
    	@mkdir -p $(dir $@)
    	@g++ -c $^ -o $@ $(compile_flags)
    
    compile : $(cpp_objs)
    
    # ============== 编译动态库 ==============
    lib/libddd.so : $(so_objs)
    	@mkdir -p $(dir $@)
    	@g++ -shared $^ -o $@
    
    dynamic : lib/libddd.so
    
    # ============== 链接动态库 ==============
    workspace/pro : objs/main.o compile dynamic
    	@mkdir -p $(dir $@) 
    	@g++ $< -o $@ $(linking_flags)
    
    run : workspace/pro
    	@./$<
    
    debug :
    	@echo $(so_objs)
    
    clean :
    	@rm -rf lib objs workspace/pro
    
    .PHONY : debug compile dynamic run
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45

    在终端执行 make run 指令运行效果如下:

    15
    5
    
    • 1
    • 2

    总结

    本次课程学习了 Makefile 中动态库的编译和链接过程。

  • 相关阅读:
    Windows网络管理及诊断命令整理
    数字孪生港口:提升港口运营和安全的未来之路
    MySQL高级学习笔记
    self-XSS漏洞SRC挖掘
    【由浅入深 - Java笔记】玩转List:List过滤和筛选
    嵌入式学习之Linux驱动(第九期_设备模型_教程更新了)_基于RK3568
    深入理解 python 虚拟机:花里胡哨的魔术方法
    系统设计类题目汇总四
    基于springboot的药店进销存管理系统
    vue中v-if和v-show的区别
  • 原文地址:https://blog.csdn.net/qq_40672115/article/details/133691214