GNU/Linux >> Znalost Linux >  >> Linux

Proč a jak jsou některé sdílené knihovny spustitelné, jako by to byly spustitelné soubory?

Tato knihovna má main() funkce nebo ekvivalentní vstupní bod a byl zkompilován takovým způsobem, že je užitečný jako spustitelný soubor i jako sdílený objekt.

Zde je jeden návrh, jak to udělat, i když to pro mě nefunguje.

Zde je další z odpovědi na podobnou otázku na S.O, kterou bezostyšně plagiátorím, upravím a přidám trochu vysvětlení.

Nejprve zadejte zdroj pro naši ukázkovou knihovnu 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;                       
}                   

Zkompilujte to:

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

Zde kompilujeme sdílenou knihovnu (-fPIC ), ale sdělující linkeru, že jde o běžný spustitelný soubor (-pie ) a umožnit exportovat tabulku symbolů (-Wl,-E ), takže je lze užitečně propojit.

A ačkoli file řekne, že je to sdílený objekt, funguje jako spustitelný soubor:

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

Nyní musíme zjistit, zda to lze skutečně dynamicky propojit. Příklad programu program.c :

#include <stdio.h>

extern void sayHello (char*);

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

Pomocí extern ušetří nám nutnost vytvářet záhlaví. Nyní to zkompilujte:

gcc program.c -L. -ltest

Než jej budeme moci spustit, musíme přidat cestu libtest.so pro dynamický zavaděč:

export LD_LIBRARY_PATH=./

Nyní:

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

A ldd a.out zobrazí vazbu na libtest.so .

Všimněte si, že pochybuji, že takto je glibc skutečně kompilován, protože pravděpodobně není tak přenosný jako samotný glibc (viz man gcc s ohledem na -fPIC a -pie přepínače), ale demonstruje základní mechanismus. Pro skutečné podrobnosti byste se museli podívat na zdrojový makefile.


Pojďme se ponořit pro odpověď v náhodném úložišti glibc na GitHubu. Tato verze poskytuje „banner“ v souboru version.c .

Ve stejném souboru je několik zajímavých bodů:__libc_print_version funkce, která vytiskne text do stdout a __libc_main (void) symbol, který je dokumentován jako vstupní bod. Tento symbol se tedy nazývá při spuštění knihovny.

Jak tedy linker nebo kompilátor přesně ví, že se jedná o funkci vstupního bodu?

Pojďme se ponořit do makefile. V příznakech linkeru je jeden zajímavý:

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

Toto je tedy příznak linkeru pro nastavení vstupního bodu pro knihovnu. Při vytváření knihovny můžete zadat -e function_name příznak pro linker, který umožní spustitelné chování. Co to skutečně dělá? Podívejme se do manuálu (poněkud zastaralý, ale stále platný):

Jazyk příkazů linkeru obsahuje příkaz specificky pro definování první spustitelné instrukce ve výstupním souboru (jeho vstupní bod). Jeho argumentem je název symbolu:

ENTRY(symbol)

Stejně jako přiřazení symbolů může být příkaz ENTRY umístěn buď jako nezávislý příkaz v souboru příkazu, nebo mezi definice sekce v příkazu SECTIONS - podle toho, co dává vašemu rozvržení největší smysl.

VSTUP je pouze jedním z několika způsobů výběru vstupního bodu. Můžete jej označit kterýmkoli z následujících způsobů (zobrazeno v sestupném pořadí priority:metody výše v seznamu přepisují metody níže).

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. 

Tato pravidla můžete například použít ke generování vstupního bodu s příkazem přiřazení:pokud ve vašich vstupních souborech není definován žádný začátek symbolu, můžete jej jednoduše definovat a přiřadit mu vhodnou hodnotu ---

start =0x2020;

Příklad ukazuje absolutní adresu, ale můžete použít jakýkoli výraz. Pokud například vaše soubory vstupních objektů používají nějakou jinou konvenci názvu symbolu pro vstupní bod, můžete pro začátek přiřadit hodnotu jakéhokoli symbolu, který obsahuje počáteční adresu:

start =jiný_symbol;

(aktuální dokumentaci naleznete zde)

ld linker ve skutečnosti vytvoří spustitelný soubor s funkcí vstupního bodu, pokud zadáte volbu příkazového řádku -e (což je nejoblíbenější řešení), zadejte funkční symbol start , nebo specifikujte adresu symbolu pro assembler.

Upozorňujeme však, že není zaručeno, že bude fungovat s jinými linkery (nevím, zda llvm's lld má stejný příznak). Nevím, proč by to mělo být užitečné pro jiné účely, než je poskytování informací o souboru SO.


Linux
  1. Proč je scp tak pomalý a jak jej urychlit?

  2. Načítání sdílených knihoven a využití RAM?

  3. Jak se v prázdném prostředí nacházejí spustitelné soubory?

  1. Jak zacházet s dynamickými a statickými knihovnami v Linuxu

  2. Redis jako mezipaměť:Jak to funguje a proč ji používat

  3. Jak zjistit, kdo je přihlášen ve vašem systému a co dělá

  1. Proč jsou data důležitá a jak je chránit

  2. Proč jsou některé porty hlášeny Nmapem filtrovány a ostatní ne?

  3. Úvod do sdílených knihoven Linuxu (Jak vytvořit sdílené knihovny)