Відповіді:
dlopen
це не системний виклик, це функція бібліотеки в бібліотеці libdl . У системі відображаються лише системні дзвінки strace
.
На Linux та на багатьох інших платформах (особливо на тих, які використовують формат ELF для виконуваних файлів), dlopen
реалізується шляхом відкриття цільової бібліотеки open()
та відображення її в пам'яті mmap()
. mmap()
насправді важлива частина тут, це те, що включає в себе бібліотеку в адресний простір процесу, щоб процесор міг виконувати свій код. Але ви маєте до open()
файлу, перш ніж зможете mmap()
!
ld-linux
ядро відображається у складі execve
системного виклику.
dlopen не має нічого спільного з спільними бібліотеками, як ви думаєте про них. Існує два способи завантаження спільного об’єкта:
main
функції та встановити процесний простір програми, щоб програма знайшла функції бібліотеки. Це включає open()
в себе лубрарій, а потім mmap()
його, після чого слід встановити кілька таблиць пошуку.libdl
, з якого ви потім (використовуючи перший метод) можете викликати dlopen()
іdlsym()
функції. За допомогою dlopen ви отримуєте ручку до бібліотеки, яку потім можете використовувати за допомогою dlsym для отримання покажчика функції на певну функцію. Цей метод набагато складніший для програміста, ніж перший метод (оскільки вам доведеться робити налаштування вручну, а не змусити лінкер зробити це автоматично для вас), а також він є більш крихким (оскільки ви не отримуєте компіляцію -час перевіряє, що ви викликаєте функції з правильними типами аргументів, як ви отримуєте в першому методі), але перевага полягає в тому, що ви можете вирішити, який спільний об’єкт завантажувати під час виконання (або навіть взагалі завантажувати його), роблячи цей інтерфейс призначений для функціональності типу плагінів. Нарешті, інтерфейс dlopen також менш портативний, ніж інший спосіб, оскільки його механіка залежить від точної реалізації динамічного лінкера (звідси libtool'slibltdl
, який намагається абстрагувати ці відмінності).Сьогодні більшість операційних систем використовують метод спільних бібліотек, запроваджений наприкінці 1987 року SunOS-4.0. Цей метод заснований на відображенні пам'яті через mmap ().
Зважаючи на той факт, що на початку 1990-х компанія Sun навіть пожертвувала старий код на основі a.out (Solaris в той час вже базувався на ELF) людям FreeBSD і що цей код згодом був переданий багатьом іншим системам (включаючи Linux) Ви можете зрозуміти, чому немає великої різниці між платформами.
ltrace -S
аналіз мінімального прикладу показує, що mmap
використовується в glibc 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
дзвінки open
+ mmap
.
Дивовижний ltrace
інструмент відстежує як виклики бібліотеки, так і системні виклики, і тому ідеально підходить для вивчення того, що відбувається в даному випадку.
Більш детальний аналіз показує, що open
повертає дескриптор файлу 3
(наступний вільний після stdin, out та err).
read
потім використовує цей дескриптор файлів, але TODO, чому mmap
аргументи обмежені чотирма, і ми не можемо побачити, який fd там використовувався, оскільки це 5-й аргумент . strace
підтверджує, як очікувалося, 3
це один, і порядок Всесвіту відновлюється.
Хоробрі душі також можуть вписатись у код glibc, але я не зміг знайти mmap
після швидкого грепу і я лінивий.
Випробуваний на цьому мінімальному прикладі з побудовою котлопластину на GitHub .
strace
звіти про системні виклики (тобто функції, реалізовані безпосередньо ядром). Динамічні бібліотеки не є функцією ядра; dlopen
є частиною бібліотеки С, а не ядром. Реалізація dlopen
виклику call open
(який є системним викликом) для відкриття бібліотечного файлу, щоб його можна було прочитати.
ltrace
.
ltrace -S
ідеально аналізує це, оскільки він також показує системні дзвінки