2012-08-27 10 views
6

Supponiamo di avere un file C senza dipendenza esterna e solo una sezione di dati const. Vorrei compilare questo file, e poi ottenere un blob binario che posso caricare in un altro programma, dove la funzione verrebbe utilizzata attraverso un puntatore a funzione.File di oggetto in codice binario

Facciamo un esempio, ecco un modulo binario fictionnal, f1.c

static const unsigned char mylut[256] = { 
    [0 ... 127] = 0, 
    [128 ... 255] = 1, 
}; 

void f1(unsigned char * src, unsigned char * dst, int len) 
{ 
    while(len) { 
     *dst++ = mylut[*src++]; 
     len--; 
    } 
} 

Vorrei compilarlo a f1.o, poi f1.bin, e usarlo come questo in prog .c

int somefunc() { 
    unsigned char * codedata; 
    f1_type_ptr f1_ptr; 
    /* open f1.bin, and read it into codedata */ 

    /* set function pointer to beginning of loaded data */ 
    f1_ptr =(f1_type_ptr)codedata; 

    /* call !*/ 
    f1_ptr(src, dst, len); 
} 

Suppongo che va da f1.c a f1.o comporta -fPIC per ottenere posizione di indipendenza. Quali sono i flag o lo script linker che posso usare per passare da f1.o a f1.bin?

Chiarimento:

so di collegamento dinamico. il collegamento dinamico non è possibile in questo caso. Il passo di collegamento deve essere puntatore funzione cast per i dati caricati, se possibile.

Si supponga di non disporre di supporto per il sistema operativo. Se potessi, vorrei ad esempio scrivere f1 in assembly con indirizzo relativo al PC.

+0

fai a sapere che è possibile utilizzare i file oggetto condivisi? Si compila il file .c in un file .so, quindi lo si carica 'dlopen()' nel proprio programma e si ottiene il puntatore alla funzione 'dlsym()' alla funzione. Quindi puoi chiamarlo. –

+0

Dimentichiamo libc e collegamento dinamico – shodanex

+0

Vuoi che il thingie 'f1.bin' sia caricato in modo dinamico (cioè in runtime)? Quindi devi costruire una libreria condivisa e usare ldopen() + ldsym() o altro caricatore di moduli (come gmodule). È probabile che provare a farlo in un altro modo sia difficile e rifiutato a causa di potenziali minacce alla sicurezza (esecuzione di segmenti di dati e così via). –

risposta

2

Si dovrebbe prendere in considerazione la costruzione di una libreria condivisa (dll per le finestre, o .so per Linux).

Costruire la lib in questo modo:

gcc -c -fPIC test.c 
gcc -shared test.o -o libtest.so 

Se si vuole caricare la libreria in modo dinamico dal codice, uno sguardo alla funzioni dlopen (3) e dlsym (3).

O se si desidera collegare la libreria al momento della compilazione, costruire il programma con

gcc -c main.c 
gcc main.o -o <binary name> -ltest 

EDIT:

Io non sono davvero sicuro di quello che dirò qui, ma questo potrebbe darti un indizio per progredire nella tua ricerca ...

Se non si desidera utilizzare dlopen e dlsym, si può provare a leggere la tabella dei simboli dal file .o al fine di trovare l'indirizzo della funzione, e poi, mmap il file oggetto in memoria con la lettura ed esecuzione dei diritti. Quindi dovresti essere in grado di eseguire il codice caricato all'indirizzo che hai trovato. Ma fai attenzione alle altre dipendenze che potresti incontrare in questo codice.

È possibile controllare la pagina man elf(5)

+0

Io non voglio usare il collegamento dinamico, ho modificato la domanda di conseguenza – shodanex

+0

** dlopen ** e ** dlsym ** non implicano il collegamento dinamico (poiché il tuo programma non sarà collegato alla libreria, il tuo binario ha vinto dipendono da esso e la libreria non sarà necessaria durante la compilazione). La funzione ** dlopen ** ti consente di caricare una libreria e ** dlsym ** restituirà l'indirizzo della funzione, in base al nome del simbolo che hai fornito. – phsym

+0

dlsym significa chiamare il sistema operativo fornito linker dinamico per analizzare il file di libreria, eseguire la mappatura, ecc .... – shodanex

6

Prima di tutto, come altro ha detto si dovrebbe considerare l'utilizzo di una DLL o SO.

Detto questo, se si vuole veramente farlo, è necessario sostituire lo script del linker. Qualcosa di simile a questo (non molto ben testato, ma penso che funziona):

ENTRY(_dummy_start) 
SECTIONS 
{ 
    _dummy_start = 0; 
    _GLOBAL_OFFSET_TABLE_ = 0; 
    .all : { 
     _all = .; 
     LONG(f1 - _all); 
     *(.text .text.* .data .data.* .rodata .rodata.*) 
    } 
} 

quindi compilare con:

$ gcc -c -fPIC test.c 

Collegamento con:

$ ld -T script.ld test.o -o test.elf 

ed estrarre il blob binario con :

$ objcopy -j .all -O binary test.elf test.bin 

Probabilmente som e spiegazione dello script è il benvenuto:

  • ENTRY(_dummy_start) Che solo evita l'avviso relativo al programma di non avere un punto di ingresso.
  • _dummy_start = 0; Definisce il simbolo utilizzato nella riga precedente. Il valore non è usato.
  • _GLOBAL_OFFSET_TABLE_ = 0; Ciò impedisce un altro errore del linker. Non penso che tu abbia davvero bisogno di questo simbolo, quindi può essere definito come 0.
  • .all Questo è il nome della sezione che raccoglierà tutti i byte del tuo blob. In questo esempio saranno tutte le sezioni .text, .data e .rodata insieme. Potresti averne bisogno di più se hai funzioni complicate, in questo caso objdump -x test.o è tuo amico.
  • LONG(f1 - _all) Non proprio necessario, ma si desidera conoscere l'offset della funzione nel blob, non è vero? Non si può presumere che sarà allo scostamento 0. Con questa linea i primi 4 byte nel blob saranno l'offset del simbolo f1 (la propria funzione). Modificare LONG con QUAD se si utilizzano puntatori a 64 bit.

UPDATE: E ora un test frettoloso e approssimativo (funziona!):

#include <stdio.h> 
#include <stdlib.h> 
#include <sys/mman.h> 

typedef void (*f1_t)(char *a, char *b, int len); 
f1_t f1; 

int main() 
{ 
    char *blob = (char*)valloc(4096); 
    FILE *f = fopen("test.bin", "rb"); 
    fread(blob, 1, 4096, f); 
    fclose(f); 

    unsigned offs = *(unsigned*)blob; 
    f1 = (f1_t)(blob + offs); 
    mprotect(blob, 4096, PROT_READ | PROT_WRITE | PROT_EXEC); 
    char txt[] = "¡hello world!"; 
    char txt2[sizeof(txt)] = ""; 
    f1(txt, txt2, sizeof(txt) - 1); 
    printf("%s\n%s\n", txt, txt2); 
    return 0; 

} 
+0

'_GLOBAL_OFFSET_TABLE_' è probabilmente necessario se riferimento ad altri simboli (come ad esempio una libreria standard) sono fatti. – AProgrammer

+0

@AProgrammer: Ma l'OP dice specificamente _no external dependency_, quindi probabilmente non è necessario.Se accede a qualsiasi libreria, allora dovrà collegare staticamente tutte le librerie nel blob o fare il collegamento dinamico stesso ... e questo sarebbe ... complicato. – rodrigo