2016-05-10 18 views
8

ho scritto un oggetto condiviso che modifica gli argomenti per FT_Load_Glyph e FT_Render_Glyph funzioni di FreeType, attualmente interponendo con LD_PRELOAD e dlsym.interposizione parte di un oggetto condiviso da soname

Questo funziona bene, ma io sono curioso di sapere se c'è un modo per rendere questi cambiamenti:

  • a tutti i programmi che utilizzano FreeType su un dato host (in esecuzione per esempio Debian);
  • senza sovrascrivere alcun programma che non sia effettivamente collegato a FreeType;
  • senza applicare semplicemente uno LD_PRELOAD a tutti i programmi sull'host;
  • senza richiedere alcuna manutenzione, a meno che il soname di FreeType non venga modificato; e
  • senza modificare alcun file di FreeType, né quelli di alcun programma sull'host.

Le uniche due “soluzioni” che sono stato in grado di elaborare sono brutti hack:

  • a LD_PRELOAD tutti i programmi, tutto il tempo, che sembra lento e fragile; oppure
  • da copiare ad es. libfreetype.so.6.12.3 a libxxxxtype.so.6.12.3; quindi
    • patch il soname in libxxxxtype.so.6.12.3 a libxxxxtype.so.6;
    • collega l'oggetto condiviso di interposizione allo libxxxxtype.so.6; e
    • installa l'oggetto condiviso come ad es. libfreetype.so.6.999.

avrei essenzialmente piace di patch trasparente una coppia di funzioni in un oggetto condiviso, pur lasciando le rimanenti funzioni attraverso, senza dover necessariamente accesso alla fonte dell'oggetto condiviso o programmi che lo utilizzano , ma se faccio un oggetto condiviso falso con il soname libfreetype.so.6, non riesco a vedere un modo pulito per collegarlo a (o dlopen) il vero libfreetype.so.6.

Questo è il mio primo vero esperimento con le librerie condivise, quindi per favore portatemi se questa domanda fa alcune ipotesi sbagliate, o semplicemente non ha senso.

+1

La soluzione basata sulla ridenominazione di 'libfreetype.so.x.y.z' sembra essere il modo giusto per farlo. Perché lo descrivi come brutto? – Leon

+0

Penso che le mie ragioni fossero (a) avrei dovuto mantenere una copia del vero 'libfreetype.so' che aveva un soname patchato, specialmente tenendolo aggiornato quando una nuova (o la stessa) versione di' libfreetype6' il pacchetto è installato, e (b) vorrei inquinare il "spazio dei nomi" globale con un 'libxxxxtype.so', che è almeno teoricamente fragile perché sarebbe impossibile trovare un nome che nessun autore di librerie avrebbe mai usato . (a) è mitigato dalla risposta di glorpen (in cambio di affidarsi a un percorso assoluto), e (b) può essere mitigato al punto in cui è solo teorico. –

risposta

3

Si può provare a utilizzare uprobes per rubare dinamicamente il controllo da alcune funzioni?

check http://www.brendangregg.com/blog/2015-06-28/linux-ftrace-uprobe.html

uprobes: tracciamento dinamico a livello utente, che è stata aggiunta a Linux 3.5 e migliorato in Linux 3.14. Ti consente di tracciare le funzioni a livello di utente; per esempio, il ritorno della funzione readline() da tutte le shell bash esecuzione, con la stringa restituita:

# ./uprobe 'r:bash:readline +0($retval):string' 
Tracing uprobe readline (r:readline /bin/bash:0x8db60 +0($retval):string). Ctrl-C to end. 
bash-11886 [003] d... 19601837.001935: readline: (0x41e876 <- 0x48db60) arg1="ls -l" 
bash-11886 [002] d... 19601851.008409: readline: (0x41e876 <- 0x48db60) arg1="echo "hello world"" 
bash-11886 [002] d... 19601854.099730: readline: (0x41e876 <- 0x48db60) arg1="df -h" 
bash-11886 [002] d... 19601858.805740: readline: (0x41e876 <- 0x48db60) arg1="cd .." 
bash-11886 [003] d... 19601898.378753: readline: (0x41e876 <- 0x48db60) arg1="foo bar" 
^C 
Ending tracing... 

E http://www.brendangregg.com/blog/2015-07-03/hacking-linux-usdt-ftrace.html

C'erano anche altre soluzioni di tracciamento funzioni user-space, come ftrace, systemtap, dtrace, lttng. Alcuni di essi richiedono la ricompilazione e la definizione di punti di tracciatura in modo statico nel programma; e gli uprobes sono "livello utente dinamico" ".

Alcuni collegamenti uprobes:

C'è handler di uprobes che ha pt_regs. Come detto nell'ultimo link: "Uprobes implementa quindi un meccanismo mediante il quale una funzione del kernel può essere invocata ogni volta che un processo esegue una specifica posizione dell'istruzione" e suggerisce che gli uprobe possono sostituire alcune soluzioni basate su ptrace/gdb; quindi c'è la possibilità di cambiare l'esecuzione di qualsiasi programma che colpisce l'uprobe attivo, cambiando il suo registro eip/rip (PC).

È possibile provare altri strumenti di strumentazione dinamici, come pin o dyninst; ma sono progettati per l'utilizzo per processo.

+0

sono molto interessanti - darò loro un colpo! Tutte e tre le risposte a questa domanda sono al punto, ma ti darò la taglia perché la tua riguarda una tecnica che non ha solo nulla a che fare con 'ld.so', ma è anche una che non ho mai sentito di prima. –

+0

Non ho idea esatta di come implementare così l'interposizione usando gli uprobes; ma Gregg è qui http://stackoverflow.com/users/2603561/brendan-gregg e potrebbe avere qualche idea. @Brendan Gregg, cosa ne pensi? – osgx

+0

Ho visto molte persone @tag altri utenti su Stack Overflow. Questo (o scrivendo un link al loro profilo) fa anche qualcosa in termini di notifiche? –

2

Un'altra soluzione sarebbe quella di rendere il sistema "overlay" per lib, con libfreetype personalizzato e quindi il proxy dei metodi non modificati alla lib reale.

Devi rendere compatibile la lib personalizzata con quella reale. È possibile farlo utilizzando dlopen con il percorso assoluto (ad esempio dlopen("/usr/lib64/libfreetype.so.6")), copiando le definizioni delle funzioni reali esportate e inoltrandole con dlsym. Pensa che per facilità di manutenzione è possibile sostituire i tipi di argomento proxy con semplice void*. Dovresti solo apportare modifiche quando cambiano le funzioni del freetype (numero di argomenti, nomi di funzioni).

Per creare lib "overlay", è possibile installare lib personalizzata in es. "/opt/myapp/lib64/libfreetype.so.6", quindi aggiungi questo percorso ai percorsi del tempo di esecuzione del linker dinamico. Potrebbe essere necessario creare collegamenti simbolici per altre versioni o compilare una nuova lib personalizzata se l'implementazione originale cambia. Qualunque cosa sia necessaria per oscurare la lib reale e mantenere altre app funzionanti :)

Google dice che per modificare i percorsi di caricamento del tempo di esecuzione su Debian è sufficiente modificare /etc/ld.so.conf. Aggiungi il percorso /opt/myapp/lib64 all'inizio, quindi verrà prima controllato. Ora qualsiasi app che cerca freetype dovrebbe caricare la tua lib, puoi controllarla con ldd <path to app>.

Posso pensare a un solo caso in cui questa soluzione non funzionerà: se l'app sta caricando libfreetype in bundle o caricandolo per percorso completo, non per nome.

+0

Abbastanza giusto! Pensavo di averlo già provato, e dlopen (3) non ha fatto nulla aprendo una libreria con lo stesso soname, ma non ho toccato questo progetto in più di sei settimane, quindi potrei inventarlo. Sarebbe bello se dlopen (3) fornisse un modo per aprire una libreria per soname "dal prossimo percorso nell'ordine di risoluzione all'ultimo", così che non devo usare un percorso assoluto, ma da quel punto Sarei raschiato in fondo alla canna per modi di criticare il tuo approccio. –

2

a LD_PRELOAD tutti i programmi, tutto il tempo, che sembra lento e fragile

Questa è una buona soluzione (per quello che vuoi). Non ne vedo uno migliore.

  • Non è fragile. Fornisce informazioni al linker di runtime in modo documentato. Non stai facendo niente, fingendo che qualcosa non sia quello che è. Stai solo modificando la gerarchia delle preferenze per la risoluzione del nome della funzione.

  • Non è lento. Il linker deve fare qualcosa prima o poi. È necessario verificare se è stato definito LD_PRELOAD, che in ogni caso è un'operazione dello spazio utente. Quindi seguirà quel percorso e caricherà la tua libreria prima di fare un sacco di altri lavori. Sarei stupito se il tempo fosse misurabile in circostanze normali.

Ci sono due dubbi che avrei, ma sono ortogonali alla tecnica. In realtà, il codice deve funzionare in tutti i casi, e bisogna scavare un po 'nel framework di creazione del processo per assicurarci che lo sia ovunque. Oltre a ciò, ld.so definisce le sue variabili d'ambiente proprio per l'uso previsto. Chi deve discutere?

+0

Hai ragione - Sono contento di aver coperto la mia parte posteriore con "sembra", perché non sono riuscito a valutare i tempi di esecuzione per avere un'idea concreta del tempo necessario per caricare una libreria. La mia unica altra avversione era il pensiero che avrei inquinato lo spazio dei nomi globale (come in "oh no, ora ogni programma ha un FT_Load_Glyph!"), Ma penso che sia importante solo se un programma è (a) basato sulla mancanza di esistenza di un FT_Load_Glyph per funzionare correttamente, o (b) basandosi sull'avere un FT_Load_Glyph che non ha nulla a che fare con FreeType - entrambi sembrano scenari molto elaborati. –

+0

Per quanto riguarda l'approccio, ho usato '/ etc/ld.so.preload' per evitare di cercare come configurare PAM (o systemd o ...) per definire globalmente una variabile d'ambiente, sebbene io sia carina certo che quelli non sono più "standard" (in senso POSIX) di '/ etc/ld.so.preload', che sembra essere una caratteristica specifica di glibc. Cosa intendi assicurando che il codice "[lavori] in tutti i casi"? Gli effetti –

Problemi correlati