Linux 动态库和静态库

Linux 动态库和静态库

七月 19, 2025 次阅读

静态库创建

我们先设计一个简单的头文件及实现方法:

// mymath.h
#ifndef _MY_MATH_H_
#define _MY_MATH_H_ 1

#include <iostream>

extern int my_errno;

int add(int x, int y);

int sub(int x, int y);

int mul(int x, int y);

int divi(int x, int y);

#endif

// mymath.cc
#include "mymath.h"

int my_errno = 0;

int add(int x, int y) { return x + y; }

int sub(int x, int y) { return x - y; }

int mul(int x, int y) { return x * y; }

int divi(int x, int y)
{
    if(y == 0){
        my_errno = 1;
        return -1;
    }
    return x / y;
}

通过 makefile 实现库的封装:

lib=libmymath.a

$(lib):mymath.o
    ar -rc $@ $^
mymath.o:mymath.cc
    g++ -c $^
.PHONY:clean
clean:
    rm -rf *.o *.a

output:
    mkdir -p ./lib/include
    mkdir -p ./lib/mymathlib
    cp *.h ./lib/include
    cp *.a ./lib/mymathlib
╭─ljx@VM-16-15-debian ~/linux_review/dy_st_lib  
╰─➤  make                                                                                                                                2 ↵
g++ -c mymath.cc
ar -rc libmymath.a mymath.o
╭─ljx@VM-16-15-debian ~/linux_review/dy_st_lib  
╰─➤  make output
mkdir -p ./lib/include
mkdir -p ./lib/mymathlib
cp *.h ./lib/include
cp *.a ./lib/mymathlib

这样一来,静态库就被存放到了 ./lib/mymathlib 下面,接下来我们写一段代码试一试能不能将该库使用起来:

#include <iostream>
#include "mymath.h"

int main()
{
    int n = divi(10, 0);
    std::cout << n << ", errno: " << my_errno << std::endl;
    return 0;
}
╭─ljx@VM-16-15-debian ~/linux_review/dy_st_lib
╰─➤  g++ -o main.o main.cc
/usr/bin/ld: /tmp/ccD6e3Zl.o: warning: relocation against `my_errno' in read-only section `.text'
/usr/bin/ld: /tmp/ccD6e3Zl.o: in function `main':
main.cc:(.text+0x13): undefined reference to `divi(int, int)'
/usr/bin/ld: main.cc:(.text+0x48): undefined reference to `my_errno'
/usr/bin/ld: warning: creating DT_TEXTREL in a PIE
collect2: error: ld returned 1 exit status

编译器无法找到我们定义的函数,这是因为我们并没有指定我们的文件所在路径,于是我们加上 -I选项 从而指定我们的头文件所在路径

╭─ljx@VM-16-15-debian ~/linux_review/dy_st_lib
╰─➤  g++ -o main.o main.cc -I ./lib/include/
/usr/bin/ld: /tmp/cc3M4mQd.o: warning: relocation against `my_errno' in read-only section `.text'
/usr/bin/ld: /tmp/cc3M4mQd.o: in function `main':
main.cc:(.text+0x13): undefined reference to `divi(int, int)'
/usr/bin/ld: main.cc:(.text+0x48): undefined reference to `my_errno'
/usr/bin/ld: warning: creating DT_TEXTREL in a PIE
collect2: error: ld returned 1 exit status

但是我们并没有告诉编译器我们的动态库在哪里,于是我们通过 -L 指定库路径,通过 -l 指定库名:

╭─ljx@VM-16-15-debian ~/linux_review/dy_st_lib
╰─➤  g++ -o main.o main.cc -I ./lib/include/ -L ./lib/mymathlib/ -lmymath
╭─ljx@VM-16-15-debian ~/linux_review/dy_st_lib
╰─➤  ./main.o
-1, errno: 1

可以看到,这么一来,静态库就被我们调用起来了,需要注意的是,库名指定是需要去掉前缀(lib)和后缀(.a)的,且建议将 -l 和库名写在一起

而若我们直接将对应的头文件和库文件链接到系统当中,就可以直接使用该头文件了,但需要注意的是,我们仍然需要指定库名,毕竟你这也不是原生库,编译器是不知道你这个库在哪里的

╭─ljx@VM-16-15-debian ~/linux_review/dy_st_lib
╰─➤  sudo ln -s /home/ljx/linux_review/dy_st_lib/lib/mymathlib/libmymath.a /lib64/libmymath.a
# 检查是否链接成功
╭─ljx@VM-16-15-debian ~/linux_review/dy_st_lib
╰─➤  ll /lib64/libmymath.a
lrwxrwxrwx 1 root root 58 Jul 19 17:45 /lib64/libmymath.a -> /home/ljx/linux_review/dy_st_lib/lib/mymathlib/libmymath.a
# 检查是否链接成功
╭─ljx@VM-16-15-debian ~/linux_review/dy_st_lib
╰─➤  sudo ln -s /home/ljx/linux_review/dy_st_lib/lib/include/mymath.h /usr/include/mymath.h
╭─ljx@VM-16-15-debian ~/linux_review/dy_st_lib
╰─➤  ll /usr/include/mymath.h
lrwxrwxrwx 1 root root 53 Jul 19 17:46 /usr/include/mymath.h -> /home/ljx/linux_review/dy_st_lib/lib/include/mymath.h

然后我们就可以直接编译该代码了:

╭─ljx@VM-16-15-debian ~/linux_review/dy_st_lib
╰─➤  g++ -o main.o main.cc -lmymath
╭─ljx@VM-16-15-debian ~/linux_review/dy_st_lib
╰─➤  ./main.o
-1, errno: 1

动态库创建

同样,先设计一个简单的头文件和实现方法

mylog 方法

// mylog.h
#ifndef _MYLOG_H_
#define _MYLOG_H_ 1

#include <iostream>
#include <chrono>
#include <ctime>

void mylog(const char *s);

#endif

//mylog.cc
#include "mylog.h"

void mylog(const char *s)
{
    // 获取当前时间点
    auto now = std::chrono::system_clock::now();
    // 转换为time_t类型
    std::time_t now_time = std::chrono::system_clock::to_time_t(now);
    // 转换为本地时间
    std::tm* local_time = std::localtime(&now_time);
    std::cout << std::asctime(local_time) << ": " << s << std::endl;
}

myprint 方法

// myprint.h
#ifndef _MYPRINT_H_
#define _MYPRINT_H_ 1

#include <iostream>

void print(const char *s);

#endif

// myprint.cc
#include "myprint.h"

void print(const char *s)
{
    std::cout << s << std::endl;
}

我们同样通过 makefile 实现对动态库的封装

static_lib=libmymath.a
dynamic_lib=libmymethod.so

.PHONY:all
all:$(static_lib) $(dynamic_lib)

$(static_lib):mymath.o
    ar -rc $@ $^
mymath.o:mymath.cc
    g++ -c $^

$(dynamic_lib):mylog.o myprint.o
    g++ -fPIC -shared -o $@ $^
mylog.o:mylog.cc
    g++ -fPIC -c $^
myprint.o:myprint.cc
    g++ -fPIC -c $^

.PHONY:clean
clean:
    rm -rf *.o *.a *.so mylib

output:
    mkdir -p ./mylib/include
    mkdir -p ./mylib/lib
    cp *.h ./mylib/include
    cp *.a ./mylib/lib
    cp *.so ./mylib/lib

需要注意的是,编译动态库是需要与位置无关码的

╭─ljx@VM-16-15-debian ~/linux_review/dy_st_lib  
╰─➤  make      
g++ -c mymath.cc
ar -rc libmymath.a mymath.o
g++ -fPIC -c mylog.cc
g++ -fPIC -c myprint.cc
g++ -fPIC -shared -o libmymethod.so mylog.o myprint.o
╭─ljx@VM-16-15-debian ~/linux_review/dy_st_lib  
╰─➤  make output
mkdir -p ./mylib/include
mkdir -p ./mylib/lib
cp *.h ./mylib/include
cp *.a ./mylib/lib
cp *.so ./mylib/lib

下面是测试代码:

#include <iostream>
#include "mymath.h"
#include "mylog.h"
#include "myprint.h"

int main()
{
    int n = divi(10, 0);
    std::cout << n << ", errno: " << my_errno << std::endl;
    mylog("hello, linux!");
    print("hello, dynamic library!");
    return 0;
}

我们尝试编译:

╭─ljx@VM-16-15-debian ~/linux_review/dy_st_lib  
╰─➤  g++ -o main.o main.cc -I ./mylib/lib/include/ -L ./mylib/lib/ -lmymath -lmymethod
╭─ljx@VM-16-15-debian ~/linux_review/dy_st_lib  
╰─➤  ./main.o
./main.o: error while loading shared libraries: libmymethod.so: cannot open shared object file: No such file or directory

我们会发现,程序加载是需要静态库的,而我们并没有指定静态库所在路径,以下是指定静态库路径的四种方法

1、将动态库拷贝到 /lib64 路径下

该方法和软链接相似,因此不做示例演示

2、将动态库软链接到 /lib64 路径下

╭─ljx@VM-16-15-debian ~/linux_review/dy_st_lib  
╰─➤  sudo ln -s /home/ljx/linux_review/dy_st_lib/mylib/lib/libmymethod.so /usr/lib/libmymethod.so      
╭─ljx@VM-16-15-debian ~/linux_review/dy_st_lib  
╰─➤  ll /usr/lib/libmymethod.so
lrwxrwxrwx 1 root root 57 Jul 19 22:56 /usr/lib/libmymethod.so -> /home/ljx/linux_review/dy_st_lib/mylib/lib/libmymethod.so
╭─ljx@VM-16-15-debian ~/linux_review/dy_st_lib  
╰─➤  ./main.o
-1, errno: 1
Sat Jul 19 22:56:40 2025
: hello, linux!
hello, dynamic library!

3、将环境变量导入 LD_LIBRARY_PATH

╭─ljx@VM-16-15-debian ~/linux_review/dy_st_lib  
╰─➤  export LD_LIBRARY_PATH=/home/ljx/linux_review/dy_st_lib/mylib/lib:$LD_LIBRARY_PATH
╭─ljx@VM-16-15-debian ~/linux_review/dy_st_lib  
╰─➤  ./main.o
-1, errno: 1
Sat Jul 19 22:58:28 2025
: hello, linux!
hello, dynamic library!

4、将路径写入 /etc/ld.so.conf.d/ 目录下的任意一个文件当中,然后 ldconfig