2016-04-01 5 views
6

Si consideri la seguente funzione:Come posso forzare gcc a chiamare una funzione direttamente nel codice PIC?

extern void test1(void); 
extern void test2(void) { 
    test1(); 
} 

Questo è il codice gcc genera senza -fpic su amd64 Linux:

test2: 
    jmp test1 

Quando compilo con -fpic, gcc chiama esplicitamente attraverso il PLT per abilitare simbolo interposizione:

test2: 
    jmp [email protected] 

Questo tuttavia non è strettamente necessario d per il codice indipendente dalla posizione e potrebbe essere lasciato fuori se non voglio supportare. Se necessario, il linker riscrive comunque il target di salto sul simbolo PLT.

Come posso, senza modificare il codice sorgente e senza rendere il codice compilato inadatto per una libreria condivisa, far sì che le chiamate di funzione vadano direttamente ai loro target invece di passare esplicitamente attraverso il PLT?

+1

Intendi per chiamate all'interno della tua libreria/eseguibile? Questo dovrebbe essere possibile in qualche modo, magari definendo un alias privato o qualcosa del genere. Ma per le chiamate alle funzioni nelle librerie alle quali si collega solo dinamicamente, non sono sicuro che il linker di runtime * possa * risolvere tali riferimenti. –

+0

@PeterCordes Senza '-fpic', il linker riscrive automaticamente i riferimenti ai simboli ai riferimenti PLT ove appropriato/richiesto. Questo è il comportamento che voglio, cioè il compilatore che genera chiamate normali a tutte le funzioni e il linker riscrive le chiamate che vanno a un altro oggetto condiviso. – fuz

+1

Does ['-fno-semantic-interposition'] (http://stackoverflow.com/questions/35745543/new-option-in-gcc-5-3-fno-semantic-interposition) fa quello che vuoi? Vedi anche http://stackoverflow.com/questions/34102989/shared-object-in-linux-without-symbol-interposition-fno-semantic-interposition. Questa domanda è una copia di entrambi? –

risposta

1

Se si dichiara test1() nascosta (__attribute__((__visibility__("hidden"))), il salto sarà diretta.

Ora test1() non può essere definita nella sua unità di traduzione fonte come nascosto, ma credo che nessun danno dovrebbe venire da quella discrepanza tranne il linguaggio C garantire che &test1 == &test1 possa essere danneggiato al momento dell'esecuzione se uno dei puntatori è stato ottenuto tramite un riferimento nascosto e uno tramite uno pubblico (il riferimento pubblico potrebbe essere stato interposto tramite il precaricamento o un DSO precedente a quello corrente nello scope di ricerca , mentre il riferimento nascosto (che si traduce in salti diretti) efficace impedisce qualsiasi tipo di interposizione)

Un modo più corretto per risolvere questo problema consiste nella definizione di due nomi per test1(): un nome pubblico e un nome privato/nascosto.

In gcc e clang, questo può essere fatto con qualche magia di alias, che può essere eseguita solo nell'unità di traduzione che definisce il simbolo.

macro possono rendere più bella:

#define PRIVATE __attribute__((__visibility__("hidden"))) 
#define PUBLIC __attribute__((__visibility__("default"))) 
#define PRIVATE_ALIAS(Alias,OfWhat) \ 
    extern __typeof(OfWhat) Alias __attribute((__alias__(#OfWhat), \ 
           __visibility__("hidden"))) 

#if HERE 
PUBLIC void test1(void) { } 
PRIVATE_ALIAS(test1__,test1); 
#else 
PUBLIC void test1(void); 
PRIVATE void test1__(void); 
#endif 

void call_test1(void) { test1(); } 
void call_test1__(void) { test1__(); } 

void call_ext0(void) { void ext0(void); ext0(); } 
void call_ext1(void) { PRIVATE void ext1(void); ext1(); } 

Le compila sopra (-O3, x86-64) in:

call_test1: 
     jmp  [email protected] 
call_test1__: 
     jmp  test1__ 
call_ext0: 
     jmp  [email protected] 
call_ext1: 
     jmp  ext1 

(Definizione qui = 1 in aggiunta inlines la chiamata test1 dal momento che è piccolo e locale e -O3 è acceso).

Esempio dal vivo al https://godbolt.org/g/eZvmp7.

-fno-semantic-interposition farà anche il lavoro, ma interromperà anche la garanzia del linguaggio C, ed è una sorta di grosso martello che non ha la granularità di aliasing.

+0

Penso che '-fno-semantic-interposition' è quello che volevo. – fuz

2

Se non è possibile modificare il codice sorgente, è possibile utilizzare un grande martello: -Bsymbolic bandiera linker:

Quando si crea una libreria condivisa, legare i riferimenti ai simboli globali per la definizione all'interno della libreria condivisa, se presente. Normalmente, è possibile per un programma collegato a una libreria condivisa per sovrascrivere la definizione all'interno della libreria condivisa. Questa opzione è significativa solo sulle piattaforme ELF che supportano le librerie condivise.

Ma attenzione che si interromperà se alcune parti della libreria si basano sull'interposizione di simboli. Consiglierei di andare con le funzioni di nascondimento che non devono essere esportate (impostando la visibilità nascosta su di esse) o chiamandole tramite alias nascosti (specificamente progettati per eseguire chiamate intra-libreria senza PLT in modo controllato).

Problemi correlati