Чому і як деякі бібліотеки, що діляться спільно, виконуються, як ніби вони виконуються?


55

У 32-бітних системах Linux це викликає

$ /lib/libc.so.6

і на 64-бітних системах це

$ /lib/x86_64-linux-gnu/libc.so.6

в оболонці, забезпечує такий вихід:

GNU C Library stable release version 2.10.1, by Roland McGrath et al.
Copyright (C) 2009 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.
There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE.
Compiled by GNU CC version 4.4.0 20090506 (Red Hat 4.4.0-4).
Compiled on a Linux >>2.6.18-128.4.1.el5<< system on 2009-08-19.
Available extensions:
    The C stubs add-on version 2.1.2.
    crypt add-on version 2.1 by Michael Glad and others
    GNU Libidn by Simon Josefsson
    Native POSIX Threads Library by Ulrich Drepper et al
    BIND-8.2.3-T5B
    RT using linux kernel aio
For bug reporting instructions, please see:
<http://www.gnu.org/software/libc/bugs.html>.

Чому і як це відбувається, і як це можливо зробити в інших спільних бібліотеках?

Я подивився, /usr/libщоб знайти виконавчі файли, і знайшов /usr/lib/libvlc.so.5.5.0. Його виконання призвело до помилки сегментації . : - /


На додаток до всіх наступних відповідей, моє нагадування - якщо ви встановите біт x у спільній бібліотеці, можна було (можливо, все-таки є) завантажити його з виконуваного файлу навіть з чітким бітом r. Колись вважалося хорошою практикою безпеки - виганяти r біт зі світу на системні виконавчі файли та бібліотеки. Завдяки широкому розповсюдженню відкритого коду це стосується насправді більше до анонімного каталогу ftp / bin / ls. Мені залишення набору бітів x виглядає як візаж цієї старої практики.
Джошуа

Відповіді:


52

Ця бібліотека має main()функцію або еквівалентну точку входу, і вона була складена таким чином, що вона корисна і як виконуваний файл, і як спільний об'єкт.

Ось одна пропозиція про те, як це зробити, хоча це не працює для мене.

Ось ще одна відповідь на подібне запитання щодо ТА , яке я безсоромно плажуватиму, підробляю та додаю трохи пояснень.

По-перше, джерело для нашої прикладної бібліотеки test.c:

#include <stdio.h>                  

void sayHello (char *tag) {         
    printf("%s: Hello!\n", tag);    
}                                   

int main (int argc, char *argv[]) { 
    sayHello(argv[0]);              
    return 0;                       
}                   

Складіть, що:

gcc -fPIC -pie -o libtest.so test.c -Wl,-E

Тут ми складаємо спільну бібліотеку ( -fPIC), але повідомляємо лінкер, що це звичайний виконуваний файл ( -pie), і робимо його таблицю символів експортованою ( -Wl,-E), такою, з якою вона може бути корисно пов'язана.

І хоча fileскажемо, що це спільний об'єкт, він працює як виконуваний файл:

> ./libtest.so 
./libtest.so: Hello!

Тепер нам потрібно побачити, чи дійсно це може бути динамічно пов'язане. Приклад програми program.c:

#include <stdio.h>

extern void sayHello (char*);

int main (int argc, char *argv[]) {
    puts("Test program.");
    sayHello(argv[0]);
    return 0;
}

Використання externекономить нам необхідність створити заголовок. Тепер складіть це:

gcc program.c -L. -ltest

Перш ніж ми зможемо виконати його, нам потрібно додати шлях libtest.soдинамічного завантажувача:

export LD_LIBRARY_PATH=./

Зараз:

> ./a.out
Test program.
./a.out: Hello!

І ldd a.outпокаже зв'язок на libtest.so.

Зверніть увагу , що я сумніваюся , що це як Glibc насправді складена, так як це, ймовірно , не так , як портативна сам Glibc (див man gccвідношенні до -fPICі -pieперемикачів), але вона демонструє базовий механізм. Для отримання реальних деталей вам доведеться подивитися джерело makefile.


1
Чудова відповідь, дякую! :-) Я намагався використовувати nmв спільній бібліотеці, але це не була версія налагодження. Отож, чому libvlcй інші трапляються?
Ho1

1
Оскільки більшість спільних бібліотек не призначені для виконання, GNU libc- виняток.
goldilocks

Я знайшов двох інших: ldі libpthread.
Ho1

@ Ho1 ld.soособливий в інших відношеннях. В якійсь мірі це більше справжній виконуваний файл, ніж звичайний динамічно пов'язаний виконуваний файл.
Випадково832

1
Наведені вище варіанти хоч і створюють бібліотеку виконуваного спільного користування, але є неповною в тому сенсі, що вона помиляється на помилку, коли деякі виконувані файли намагаються зв’язатися з цим. Докладний зразок посилання доданий тут: unix.stackexchange.com/a/479334/152034
парасити

21

Давайте зануримось у відповідь випадковим glibc repo в github. Ця версія містить „банер” у файлі version.c.

У цьому ж файлі є декілька цікавих моментів: __libc_print_versionфункція, що забезпечує друк для того ж тексту та символу, __libc_main (void)який задокументований як точка входу. Тому цей символ називається під час роботи бібліотеки.

Отже, як знавець / компілятор знає, що це саме точка входу?

Давайте зануримося в makefile . У прапорах лінкера є цікавий прапор:

# Give libc.so an entry point and make it directly runnable itself.
LDFLAGS-c.so += -e __libc_main

Отже, це прапор лінкера для встановлення точки входу в бібліотеку. Під час створення бібліотеки ви можете передбачити -e function_nameстворення лінкером поведінки. Що це насправді робить? Давайте розглянемо посібник (дещо застарілий, але все-таки дійсний) :

Мова команд лінкера включає команду, спеціально для визначення першої виконуваної інструкції у вихідному файлі (його вхідній точці). Його аргументом є назва символу:

Вхід (символ)

Як і призначення символів, команда ENTRY може бути розміщена як незалежна команда у файлі команд, або серед визначень розділів у команді SECTIONS - все, що має найбільш сенс для вашого макета.

Вхід - це лише один із кількох способів вибору точки входу. Ви можете вказати його будь-яким із наведених нижче способів (показано у порядку зменшення пріоритету: методи, що знаходяться у списку, заміняють способи перегляду нижче).

the `-e' entry command-line option;
the ENTRY(symbol) command in a linker control script;
the value of the symbol start, if present;
the address of the first byte of the .text section, if present;
The address 0. 

Наприклад, ви можете використовувати ці правила для створення точки введення з оператором присвоєння: якщо у вхідних файлах не визначений запуск символу, ви можете просто визначити його, призначивши йому відповідне значення ---

старт = 0х2020;

Приклад показує абсолютну адресу, але ви можете використовувати будь-який вираз. Наприклад, якщо ваші вхідні об’єктні файли використовують якусь іншу умову-ім'я символу для точки входу, ви можете просто призначити значення того, що символ містить початкову адресу для початку:

start = other_symbol;

(поточну документацію можна знайти тут )

Дійсно ldLinker створює виконуваний файл з функцією точки входу, якщо ви надаєте йому параметр командного рядка -e(найпрактичніше рішення), надаєте символ функції startабо вводите адресу символу в асемблері.

Однак зауважте, що явно не гарантується, що працювати з іншими лінкерами (я не знаю, чи буде у lldm lld такий же прапор). Чому це повинно бути корисним для інших цілей, крім надання інформації про такий файл, я не знаю.


1
Якби це був python, він би забезпечував одиничні тести.
Ерік Аронестій
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.