>  기사  >  시스템 튜토리얼  >  Linux에서 동적 링크와 정적 링크의 원래 의미는 무엇입니까?

Linux에서 동적 링크와 정적 링크의 원래 의미는 무엇입니까?

WBOY
WBOY앞으로
2024-02-05 17:45:021053검색

기존 규칙에 대해 먼저 몇 가지 질문을 해보겠습니다.

  • 왜 동적 연결인가?
  • 동적 연결을 수행하는 방법은 무엇입니까?
  • 주소 독립적 코드 기술이란 무엇입니까?
  • 지연 바인딩 기술이란 무엇입니까?
  • 프로그램이 실행되는 동안 명시적 링크를 수행하는 방법은 무엇입니까?

동적 연결이 필요한 이유는 무엇인가요?

동적 링크의 출현은 정적 링크의 몇 가지 단점을 해결하기 위한 것입니다.

  1. 메모리 및 디스크 공간 절약: 아래 그림과 같이
Linux 动态链接与静态链接原来是这么回事?

Program1과 Program2에는 각각 Program1.o와 Program2.o라는 두 개의 모듈이 포함되어 있으며 둘 다 Lib.o 모듈이 필요합니다. 정적 링크의 경우 두 대상 파일 모두 Lib.o 모듈을 사용하므로 링크에 의해 동시에 출력되는 Program1 및 Program2 실행 파일에 복사본이 동시에 실행될 때 Lib.o에는 두 개의 복사본이 있습니다. 디스크와 메모리에 복사할 때, 시스템에 Lib.o와 유사한 여러 프로그램이 공유 대상 파일이 있으면 많은 공간이 낭비됩니다.

  1. 정적 링크는 프로그램 업데이트 배포 및 릴리스에 매우 비우호적입니다

모듈이 20개 모듈에 의존하는 경우 20개 모듈 중 하나를 업데이트해야 할 때 모든 모듈을 찾아서 실행 가능한 프로그램으로 다시 컴파일해야 업데이트가 성공할 수 있습니다. 매우 큰 프로그램을 다시 구하려면 프로그램이 정적 링크를 사용하는 경우 네트워크를 통해 프로그램을 업데이트하는 것이 매우 불편할 수 있습니다. .

정적 연결의 단점을 해결하기 위해 동적 연결이 도입되었습니다. 동적 연결의 메모리 분포는 그림과 같습니다.

Linux 动态链接与静态链接原来是这么回事?

여러 프로그램이 동일한 공유 개체 파일에 의존합니다. 이 공유 개체 파일의 복사본은 디스크와 메모리에 하나만 있으며 복사본이 생성되지 않습니다. 간단히 말해서 구성하는 개체 파일을 연결하지 않습니다. 프로그램은 정적 링크와 마찬가지로 프로그램이 실행 중일 때만 링크가 수행되며 실행될 때까지 링크 프로세스가 연기됩니다. 동적 링크 방법을 사용하면 개발 프로세스 중에 각 모듈이 더욱 독립적이고 덜 결합되므로 다양한 개발자와 개발 조직이 독립적으로 개발하고 테스트하기가 더 쉬워집니다.

동적 연결을 수행하는 방법은 무엇입니까?

다음 코드를 보세요:

으아악

컴파일 및 실행 과정은 다음과 같습니다.

으아악

-fPIC, -shared를 통해 동적링크 라이브러리를 생성한 뒤, 실행 가능한 프로그램에 링크시켜 정상적으로 실행시킬 수 있습니다.

readelf 명령을 통해 동적 링크 라이브러리의 세그먼트 정보를 볼 수 있습니다.

으아악

동적 링크 모듈의 로딩 주소가 0부터 시작하는 것을 볼 수 있습니다. 0은 잘못된 주소이며 프로그램이 실행될 때 결정되며 컴파일 타임에는 불확실합니다.

프로그램 변경:

으아악

지도 정보를 읽으려면 실행하세요:

~/test$ ./test &
[1] 126
~/test$ func 1
cat /proc/126/maps
7ff2c59f0000-7ff2c5bd7000 r-xp 00000000 00:00 516391             /lib/x86_64-linux-gnu/libc-2.27.so
7ff2c5bd7000-7ff2c5be0000 ---p 001e7000 00:00 516391             /lib/x86_64-linux-gnu/libc-2.27.so
7ff2c5be0000-7ff2c5dd7000 ---p 000001f0 00:00 516391             /lib/x86_64-linux-gnu/libc-2.27.so
7ff2c5dd7000-7ff2c5ddb000 r--p 001e7000 00:00 516391             /lib/x86_64-linux-gnu/libc-2.27.so
7ff2c5ddb000-7ff2c5ddd000 rw-p 001eb000 00:00 516391             /lib/x86_64-linux-gnu/libc-2.27.so
7ff2c5ddd000-7ff2c5de1000 rw-p 00000000 00:00 0
7ff2c5df0000-7ff2c5df1000 r-xp 00000000 00:00 189022             /mnt/d/wzq/wzq/util/test/lib.so
7ff2c5df1000-7ff2c5df2000 ---p 00001000 00:00 189022             /mnt/d/wzq/wzq/util/test/lib.so
7ff2c5df2000-7ff2c5ff0000 ---p 00000002 00:00 189022             /mnt/d/wzq/wzq/util/test/lib.so
7ff2c5ff0000-7ff2c5ff1000 r--p 00000000 00:00 189022             /mnt/d/wzq/wzq/util/test/lib.so
7ff2c5ff1000-7ff2c5ff2000 rw-p 00001000 00:00 189022             /mnt/d/wzq/wzq/util/test/lib.so
7ff2c6000000-7ff2c6026000 r-xp 00000000 00:00 516353             /lib/x86_64-linux-gnu/ld-2.27.so
7ff2c6026000-7ff2c6027000 r-xp 00026000 00:00 516353             /lib/x86_64-linux-gnu/ld-2.27.so
7ff2c6227000-7ff2c6228000 r--p 00027000 00:00 516353             /lib/x86_64-linux-gnu/ld-2.27.so
7ff2c6228000-7ff2c6229000 rw-p 00028000 00:00 516353             /lib/x86_64-linux-gnu/ld-2.27.so
7ff2c6229000-7ff2c622a000 rw-p 00000000 00:00 0
7ff2c62e0000-7ff2c62e3000 rw-p 00000000 00:00 0
7ff2c62f0000-7ff2c62f2000 rw-p 00000000 00:00 0
7ff2c6400000-7ff2c6401000 r-xp 00000000 00:00 189023             /mnt/d/wzq/wzq/util/test/test
7ff2c6600000-7ff2c6601000 r--p 00000000 00:00 189023             /mnt/d/wzq/wzq/util/test/test
7ff2c6601000-7ff2c6602000 rw-p 00001000 00:00 189023             /mnt/d/wzq/wzq/util/test/test
7fffee96f000-7fffee990000 rw-p 00000000 00:00 0                 [heap]
7ffff6417000-7ffff6c17000 rw-p 00000000 00:00 0                 [stack]
7ffff729d000-7ffff729e000 r-xp 00000000 00:00 0                 [vdso]

可以看到,整个进程虚拟地址空间中,多出了几个文件的映射,lib.so和test一样,它们都是被操作系统用同样的方法映射到进程的虚拟地址空间,只是它们占据的虚拟地址和长度不同.

从maps里可以看见里面还有libc-2.27.so,这是C语言运行库,还有一个ld-2.27.so,这是Linux下的动态链接器,动态链接器和普通共享对象一样被映射到进程的地址空间,在系统开始运行test前,会先把控制权交给动态链接器,动态链接器完成所有的动态链接工作后会把控制权交给test,然后执行test程序。

当链接器将Program.o链接成可执行文件时,这时候链接器必须确定目标文件中所引用的func函数的性质,如果是一个定义于其它静态目标文件中的函数,那么链接器将会按照静态链接的规则,将Program.o的func函数地址进行重定位,如果func是一个定义在某个动态链接共享对象中的函数,那么链接器将会将这个符号的引用标记为一个动态链接的符号,不对它进行地址重定位,将这个过程留在装载时再进行。

动态链接的方式

动态链接有两种方式:装载时重定位和地址无关代码技术。

装载时重定位:

在链接时对所有绝对地址的引用不作重定位,而把这一步推迟到装载时完成,也叫基址重置,每个指令和数据相当于模块装载地址是固定的,系统会分配足够大的空间给装载模块,当装载地址确定后,那指令和数据地址自然也就确定了。

然而动态链接模块被装载映射到虚拟空间,指令被重定位后对于每个进程来讲是不同的,没有办法做到同一份指令被多个进程共享,所以指令对不同的进程来说有不同的副本,还是空间浪费,怎么解决这个问题?使用fPIC方法。

地址无关代码:

指令部分无法在多个进程之间共享,不能节省内存,所以引入了地址无关代码的技术。我们平时编程过程中可能都见过-fPIC的编译选项,这个就代表使用了地址无关代码技术来实现真正的动态链接。

基本思想就是使用GOT(全局偏移表),这是一个指向变量或函数地址的指针数组,当指令要访问变量或者调用函数时,会去GOT中找到相应的地址进行间接跳转访问,每个变量或函数都对应一个地址,链接器在装载模块的时候会查找每个变量和函数的地址,然后填充GOT中的各个项,确保每个指针指向的地址正确。GOT放在数据段,所以它可以在模块装载时被修改,并且每个进程都可以有独立的副本,相互不受影响。

tips

 

-fpic和-fPIC的区别:它们都是地址无关代码技术,-fpic产生的代码相对较小较快,但是在某些平台会有些限制,所以大多数情况下都是用-fPIC来产生地址无关代码。

 

-fPIC和-fPIE的区别:一个作用于共享对象,一个作用于可执行文件,一个以地址无关方式编译的可执行文件被称作地址无关可执行文件。

 

-fpie和-fPIE的区别:类似于-fpic和-fPIC的区别

延迟绑定技术

在程序刚启动时动态链接器会寻找并装载所需要的共享对象,然后进行符号地址寻址重定位等工作,这些工作会减慢程序的启动速度,如果解决?

使用PLT延迟绑定技术,这里会单独有一个叫.PLT的段,ELF将 GOT拆分成两个表.GOT和.GOT.PLT,其中.GOT用来保存全局变量的引用地址,.GOT.PLT用来保存外部函数的地址,每个外部函数在PLT中都有一个对应项,在初始化时不会绑定,而是在函数第一次被用到时才进行绑定,将函数真实地址与对应表项进行绑定,之后就可以进行间接跳转。

显式运行时链接

支持动态链接的系统往往都支持显式运行时链接,也叫运行时加载,让程序自己在运行时控制加载的模块,在需要时加载需要的模块,在不需要时将其卸载。这种运行时加载方式使得程序的模块组织变得很灵活,可以用来实现一些诸如插件、驱动等功能。

通过这四个API可以进行显式运行时链接:

dlopen():打开动态链接库
dlsym():查找符号
dlerror():错误处理
dlclose():关闭动态链接库

参考这段使用代码:

#include 
#include 

int main() {
   
   void *handle;
   void (*f)(int);
   char *error;

   handle = dlopen("./lib.so", RTLD_NOW);
   if (handle == NULL) {
       printf("handle null \n");
       return -1;
  }
   f = dlsym(handle, "func");
   do {
       if ((error = dlerror()) != NULL) {
           printf("error\n");
           break;
      }
       f(100);
  } while (0);
   dlclose(handle);

   return 0;
}

编译运行:

$ gcc -o test program.c -ldl
$ ./test
func 100

总结

为什么要进行动态链接?为了解决静态链接浪费空间和更新困难的缺点。

动态链接的方式?로드 시간 재배치 및 주소 독립적 코드 기술.

地址无关代码技术原理?GOT 구간을 통한 간접 점프.

延迟加载技术原理? 외부 기능 기호에 대해 PLT 세그먼트를 통해 지연 바인딩 및 간접 점프를 구현합니다.

如果进行显式运行时链接? 헤더 파일에 있는 4가지 함수를 통해 코드는 위와 같습니다.

위 내용은 Linux에서 동적 링크와 정적 링크의 원래 의미는 무엇입니까?의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
이 기사는 lxlinux.net에서 복제됩니다. 침해가 있는 경우 admin@php.cn으로 문의하시기 바랍니다. 삭제