Linux 환경에서 동적 라이브러리 컴파일

계속 만들고 성장을 가속화하십시오! "너겟 데일리 뉴플랜 · 6월 업데이트 챌린지" 참여 10일차 입니다 . 이벤트 상세보기 클릭

우리는 이전에 zeus(Zeus) 및 포세이돈(Poseidon)에서 사용하기 위해 libstar.a 정적 라이브러리를 컴파일했습니다. 정적 라이브러리는 콘텐츠를 프로그램에 복사하므로 디스크 저장 공간이 늘어납니다.

특정 라이브러리를 사용하는 소프트웨어가 100개이고 이 라이브러리가 100개 소프트웨어에 정적으로 링크되어 있으면 데이터 양이 매우 많아지기 때문에 운영 체제의 일부 기본 라이브러리는 다음 형식으로 상위 프로그램 호출에 제공됩니다. 동적 라이브러리의 .

그리고 동적 라이브러리는 동적 라이브러리를 독립적으로 업데이트하기 쉽고 동적 라이브러리의 메커니즘을 사용하여 플러그인 시스템을 만들 수 있다는 장점도 있습니다.

libstar.so 동적 라이브러리를 어떻게 컴파일합니까? 이것이 이 기사의 초점입니다.

관련 파일 sun.c , moon.c 및 earth.c는   추출 코드 mku9 이전 의 Universe 디렉토리에 여전히 있습니다. 다음 명령을 사용하여 libstar.so 동적 라이브러리를 컴파일합니다.

gcc -c -o sun.o sun.c
gcc -c -o moon.o moon.c
gcc -c -o earth.o earth.c 
gcc -fPIC -shared -o libstar.so sun.o moon.o earth.o
复制代码

-fPIC 는 컴파일 옵션이고 PIC는 위치 독립 코드의 약자로 위치 독립 코드를 생성한다는 의미입니다. 실행 결과는 다음과 같습니다.

linux-c-shared-1-1


이제 objdump를 사용하여 이 동적 라이브러리의 어셈블리 코드를 봅니다.

objdump -d libstar.so > star-so-dump.txt
复制代码

linux-c-shared-1-1-1

위 그림에서 알 수 있듯이 동적 라이브러리는 이전의 정적 라이브러리와 동일하지 않으며 두 호출의 주소가 수정되었습니다. 왜 이런거야? 동적 연결은 말할 것도 없고 런타임에만 연결됩니다.

이 시점에서 통화 주소가 수정 된 이유는 무엇입니까?

뒤에 있는  moon_rotate@plt plt의 전체 이름은 프로시저 연결 테이블이며 다음과 같이 다음 callq 590이 점프할 위치를 볼 수 있습니다.

linux-c-shared-1-1-1-1

위 그림의 590은 하드디스크 파일의 오프셋이며, 590바이트의 오프셋으로도 동일한 바이너리 내용을 볼 수 있다. 위 그림의 3가지 어셈블리 명령어는 실제로 앞에서 정의한 moon_rate 함수의 명령어가 아닙니다.

这个实际上是生成 动态库的时候,给call 00 00 00 的函数引用封装一层。通过 gdb 调试会会发现,程序是先 进入 moon_rate@plt ,然后再通过 jmpq 跳转到 真正的 moon_rate 函数。

这个过程可以理解为, 通过 Procedure Linkage Table 表,跳转到真正的函数。


我们再做一个实验,生成 libstar.so 动态库的时候,把 moon.o 删掉,不加入。看看 sun.o 里面对 moon_rate 的引用会不会被修正。

提醒:注意这个 libstar-err.so,这个漏了 moon.o 的动态库后面会用来显示一个错误的情况。

gcc -fPIC -shared -o libstar-err.so sun.o earth.o
复制代码

linux-c-shared-1-1-1-2

从上图可以看到,即使没有 moon.o 依然会被修正。但是 这个 libstar-err.so 是有问题的,可以使用 ldd 加上 -r 选项进行模拟重定位函数,会发现找不到 moon_rorate 函数的实现。

linux-c-shared-1-1-1-3


因为 sun.o 里面调了 printf 函数,所以用 ldd 查看,可以发现 libstar.so 跟 libc.so 已经建立了链接。

linux-c-shared-1-1-2


现在把 libstar.so 拷贝到 zeus 项目,执行以下命令编译。

gcc -c -o zeus.o zeus.c
gcc -o zeus zeus.o libstar.so
./zeus
复制代码

linux-c-shared-1-2

从上图可以看出,虽然可以顺利链接,生成 zeus 文件,但是运行的时候却报找不到 libstar.so 动态库,这是因为 Linux 环境默认不会从当前路径 加载动态库。而 Windows 环境会从当前路径 加载动态库。

那Linux 的加载器会从哪些目录搜索加载动态库呢?业界制定了一个 FHS (File Hierarchy Standard)标准,这个标准规定了一个系统中的系统文件应该如何存放,大部分Linux系统都遵循这个标准。

共享库的存放方式也在这个 FHS 标准里面,标准定义共享库可以放在 /lib , /usr/lib , /usr/local/lib 这 3个目录。所以运行加载动态库的时候,也会在这 3个目录搜索。

/etc/ld.so.conf.d/ 그러나 사용자 지정 구성 을 추가하는 일반적인 방법이  있습니다. ld.so.conf 다음과 같이 파일의 내용을 살펴보겠습니다  .

linux-c-shared-1-3

그가 다른 파일을 포함하고 있는 것이 발견되었으므로  /etc/ld.so.conf.d/star.conf 다음 내용으로 자체 구성 파일을 생성해야 합니다.

# star default configuration
/usr/local/star/lib
复制代码

그런 다음 디렉토리를 만들고  /usr/local/star/lib libstar.so를 복사합니다  /usr/local/star/lib .

이 시점에서 이전 star.conf 구성을 다시 로드하고 명령을 실행해야 합니다  sudo ldconfig. zeus를 다시 실행하는 데에는 문제가 없으며 현재 디렉터리에 있는 libstar.so를 삭제하더라도 실행  시 /usr/local/star/lib 디렉터리에서 동적 라이브러리를 검색 하기 때문에 실행할 수 있다.

linux-c-shared-1-4

위에서 링크를 위한 동적 라이브러리의 전체 이름을 직접 작성했는데, 실제로 동적 라이브러리를 링크할 때 일반적으로 사용되는 또 다른 구문은 다음과 같습니다. 다음과 같이:

gcc -o zeus zeus.o -L/usr/local/star/lib -lstar
复制代码

-L 지정된 라이브러리의 검색 경로입니다. 여기서 상기시키 듯이 링크와 로딩 실행은 서로 다른 두 가지 작업 입니다. 로드할 경로는 이전  /etc/ld.so.conf.d/star.conf 에 파일에 구성되었습니다. 링크할 때 여전히  -L 매개변수를 지정해야 합니다.


libstar-err.so 동적 라이브러리를 생성하기 전에는 이 동적 라이브러리에 moon.o가 없었습니다. zeus 프로젝트에서 이 동적 라이브러리를 사용하면 어떻게 될까요? libstar-err.so를 복사  /usr/local/star/lib하고 다음 명령을 실행하여 다시 컴파일하십시오.

mv libstar-err.so /usr/local/star/lib
gcc -o zeus-err zeus.o -L/usr/local/star/lib -lstar-err
复制代码

linux-c-shared-1-4-1

링커가 직접 오류를 보고하고 moon_rotate의 함수 구현을 찾을 수 없음을 알 수 있습니다.


관련 읽기:

1. "C++ 정적 라이브러리 및 동적 라이브러리" - Wu Qin

추천

출처juejin.im/post/7105383802075119647