Linux에서 라이브러리를 로드하기 위해 어떤 시스템 호출이 사용됩니까?

Linux에서 라이브러리를 로드하기 위해 어떤 시스템 호출이 사용됩니까?

strace출력 에서 실행 파일이 호출한 라이브러리의 경로는 에 있습니다 open(). 이는 동적으로 링크된 실행 파일에서 사용하는 시스템 호출입니까? 무엇에 대해 dlopen()? open()아마도 이것은 프로그램 실행에 중요한 역할을 하는 호출은 아닐 것 같습니다.

답변1

dlopen시스템 호출이 아니라 라이브러리 함수libdl 라이브러리. 에는 시스템 호출만 나타납니다 strace.

Linux 및 기타 여러 플랫폼(특히 실행 파일에 ELF 형식을 사용하는 플랫폼)에서는 dlopen대상 라이브러리를 사용 open()하고 이를 메모리에 매핑하여 이를 수행합니다 mmap(). mmap()여기에 실제로 중요한 부분이 있습니다. 라이브러리를 프로세스의 주소 공간에 병합하여 CPU가 해당 코드를 실행할 수 있도록 하는 것입니다. 하지만 이 작업을 open()수행하려면 먼저 파일을 구해야 합니다 mmap()!

답변2

dlopen은 공유 라이브러리라고 생각하는 것과 아무 관련이 없습니다. 공유 객체를 로드하는 방법에는 두 가지가 있습니다.

  1. 특정 공유 라이브러리의 함수를 사용하고 싶다고 컴파일 타임 링커(ld, 일반적으로 컴파일러를 통해 호출됨)에 알립니다. 이 접근 방식을 사용하면 컴파일 타임 링커를 실행할 때 라이브러리의 이름을 알아야 하지만 마치 프로그램에 정적으로 링크된 것처럼 라이브러리의 함수를 호출할 수 있습니다. 애플리케이션이 실행되면 main함수를 호출하기 전에 동적 런타임 링커(ld.so)가 호출되고 애플리케이션이 라이브러리의 함수를 찾을 수 있도록 애플리케이션의 프로세스 공간을 설정합니다. 여기에는 open()lubrary, mmap()ing 및 일부 조회 테이블 설정이 포함됩니다.
  2. 컴파일 타임 링커에게 를 연결하고 싶다고 알리면 libdl(첫 번째 방법을 사용하여) 그 링커에서 dlopen()dlsym()함수를 호출할 수 있습니다. dlopen을 사용하면 dlsym과 함께 사용하여 특정 함수에 대한 함수 포인터를 받을 수 있는 라이브러리에 대한 핸들을 얻을 수 있습니다. 이 접근 방식은 프로그래머에게 첫 번째 접근 방식보다 훨씬 더 복잡하며(링커가 자동으로 설정하도록 하는 대신 수동으로 설정해야 하기 때문에), 또한 더 취약합니다(컴파일된 결과를 얻지 못하기 때문에). - 첫 번째 방법에서 얻은 올바른 인수 유형을 사용하여 함수를 호출하고 있는지 확인할 시간입니다. 그러나 장점은 로드할 공유 객체(또는 전혀 로드할지 여부)를 런타임에 결정할 수 있다는 것입니다. 이것은 플러그인 유형 기능을 위한 인터페이스입니다. 마지막으로, dlopen 인터페이스는 그 메커니즘이 동적 링커(따라서 libltdl이러한 차이점을 추상화하려고 시도하는 libtool)의 정확한 구현에 의존하기 때문에 다른 방법보다 이식성이 떨어집니다 .

답변3

ltrace -S최소한의 예제를 분석하면 mmapglibc 2.23에서 사용된다는 것을 알 수 있습니다.

glibc 2.23, Ubuntu 16.04에서는 다음을 latrace -S사용하여 최소 프로그램에서 실행됩니다 dlopen.

ltrace -S ./dlopen.out

보여주다:

dlopen("libcirosantilli_ab.so", 1 <unfinished ...>
SYS_open("./x86_64/libcirosantilli_ab.so", 524288, 06267650550)      = -2
SYS_open("./libcirosantilli_ab.so", 524288, 06267650550)             = 3
SYS_read(3, "\177ELF\002\001\001", 832)                              = 832
SYS_brk(0)                                                           = 0x244c000
SYS_brk(0x246d000)                                                   = 0x246d000
SYS_fstat(3, 0x7fff42f9ce30)                                         = 0
SYS_getcwd("/home/ciro/bak/git/cpp-cheat"..., 128)                   = 54
SYS_mmap(0, 0x201028, 5, 2050)                                       = 0x7f1c323fe000
SYS_mprotect(0x7f1c323ff000, 2093056, 0)                             = 0
SYS_mmap(0x7f1c325fe000, 8192, 3, 2066)                              = 0x7f1c325fe000
SYS_close(3)                                                         = 0
SYS_mprotect(0x7f1c325fe000, 4096, 1)                                = 0

그래서 + 가 dlopen호출되는 것을 즉시 볼 수 있습니다 .openmmap

이 훌륭한 ltrace도구는 라이브러리 호출과 시스템 호출을 추적할 수 있으므로 이 시나리오에서 무슨 일이 일어나고 있는지 확인하는 데 적합합니다.

신중한 분석에 따르면 open파일 설명자 3(stdin, out 및 err 다음의 사용 가능한 설명자)를 반환하는 것으로 나타났습니다.

read그런 다음 해당 파일 설명자를 사용하지만TODO 에 대한 인수는 mmap4개로 제한되어 있으며 다섯 번째 인수이기 때문에 어떤 fd가 거기에서 사용되는지 알 수 없습니다.. strace예상대로 이것이 확인되었고 3우주의 질서가 회복되었습니다.

용감한 사람이라면 glibc 코드를 탐색할 수도 있지만 빠른 grep 후에도 찾을 수 없었고 mmap게으릅니다.

이것으로 테스트GitHub의 빌드 상용구를 사용한 최소 예.

답변4

strace시스템 호출(예: 커널에서 직접 구현된 함수)을 보고합니다. 동적 라이브러리는 커널 함수가 아닙니다. dlopen커널이 아닌 C 라이브러리의 일부입니다. 라이브러리 파일을 읽을 수 있도록 열기 위해 dlopen구현이 호출됩니다(시스템 호출).open

관련 정보