2014-09-16 13 views
8

Sto cercando di creare una libreria dinamica in Rust che esporti una struct come un simbolo che verrà caricato in un programma C tramite dlopen().Creazione di una struct C statica contenente stringhe

Tuttavia, sono in esecuzione in alcuni segfault durante l'accesso alla seconda stringa nella struttura, quindi ho creato un piccolo programma di test per provare a capire cosa sto facendo male.

Questo è il codice Rust (test.rs), compilato con "rustc --crate tipo test.rs dylib":

#[repr(C)] 
pub struct PluginDesc { 
    name: &'static str, 
    version: &'static str, 
    description: &'static str 
} 


#[no_mangle] 
pub static PLUGIN_DESC: PluginDesc = PluginDesc { 
    name: "Test Plugin\0", 
    version: "1.0\0", 
    description: "Test Rust Plugin\0" 
}; 

Ed ecco il programma C che tenta di caricare la libreria (test.c), compilato con "gcc test.c -ldl -o test":

#include <dlfcn.h> 
#include <stdio.h> 


typedef struct { 
    const char *name; 
    const char *version; 
    const char *description; 
} plugin_desc; 


int main(int argc, char **argv) { 
    void *handle; 
    plugin_desc *desc; 

    handle = dlopen("./libtest.so", RTLD_LOCAL | RTLD_LAZY); 
    if (!handle) { 
     printf("failed to dlopen: %s\n", dlerror()); 
     return 1; 
    } 

    desc = (plugin_desc *) dlsym(handle, "PLUGIN_DESC"); 
    if (!desc) { 
     printf("failed to dlsym: %s\n", dlerror()); 
     return 1; 
    } 

    printf("name: %p\n", desc->name); 
    printf("version: %p\n", desc->version); 
    printf("description: %p\n", desc->description); 

    return 0; 
} 

Questa è l'uscita:

name: 0x7fa59ef8d750 
version: 0xc 
description: 0x7fa59ef8d75c 

Come si può vedere, l'annuncio dress of desc-> version è in realtà 0xc (12), che è la lunghezza della prima stringa. Quindi sembra che la struttura che viene impacchettata nella libreria contenga anche la lunghezza della stringa dopo l'indirizzo di memoria.

Sto usando il tipo di stringa errato qui? Come puoi vedere, dovevo anche far terminare manualmente le stringhe NULL. Ho provato ad usare il wrapper CString ma in questo caso non sembra funzionare ("gli oggetti statici non possono avere distruttori").

sto correndo l'ultima nightly Ruggine su Linux:

$ rustc --version 
rustc 0.12.0-pre-nightly (f8426e2e2 2014-09-16 02:26:01 +0000) 
+0

Hai provato mettendo '* puntatori i8' nella struct invece? – nneonneo

+1

Sembra che le stringhe di ruggine non siano solo 'char *' s. Esiste un file .h che contiene una definizione di stringhe di ruggine per il collegamento con C? – EOF

risposta

2

Il layout di una fetta (&[T] o &str) è un puntatore seguita da una lunghezza, come documentato da the Slice struct of the std::raw module. Ecco perché la lettura del campo version dal codice C mostra la lunghezza del valore del campo name. (Si noti, tuttavia, che l'esatta disposizione della memoria delle sezioni non è considerata stabile, quindi potrebbe cambiare in una versione successiva. In ogni caso, si dovrebbe non passare tipi di dati specifici per Rust a C; include puntatori prime - e tipi annotati con #[repr(C)])

EDIT:. Purtroppo, sembra che ci sia alcun modo per fare questo a Rust, per ora. Ci sono funzioni per ottenere puntatori grezzi da sezioni, ma function calls are not allowed in static initializers. Come suggerito da sellibitze nei commenti, dovresti definire tale variabile in un file sorgente C.

+1

I letterali byte (di tipo '& [u8]') coercitano veramente a '* const u8'? – huon

+0

Purtroppo anche questo non funziona. Ho ricevuto l'errore "expected' * const i8', trovato '& 'static [u8]' (previsto i8, found vector) "ma mentre cambiava il tipo in" &' static [u8] "lo rendeva compilato, ha il stesso problema del codice originale. – chrippa

+0

@chrippa: Beh, c'è un metodo 'as_ptr' su slice. Ma non penso che tu possa chiamarlo come parte dell'inizializzazione di una statica. L'unica soluzione che vedo è scrivere un file C, compilarlo e collegarlo al resto del codice Rust. – sellibitze

3

La risposta breve è che non è possibile allocare staticamente una tale struttura. Future Rust guadagnerà probabilmente questa abilità.

Ciò che si può fare è allocare staticamente una struttura che contiene puntatori nulli e impostare quei puntatori nulli su qualcosa di utile quando si chiama la funzione. Ruggine ha static mut. Richiede codice non sicuro, è non codificabile affatto ed è (per quanto a mia conoscenza) considerato un odore codice.

Proprio qui considero una soluzione alternativa al fatto che non vi è alcun modo per trasformare un &[T] in un *const T in una statica.

static S: &'static [u8] = b"http://example.org/eg-amp_rust\n\0"; 
static mut desc: LV2Descriptor = LV2Descriptor { 
    amp_uri: 0 as *const libc::c_char, // ptr::null() isn't const fn (yet) 
}; 

#[no_mangle] 
pub extern fn lv2_descriptor(index: i32) -> *const LV2Descriptor { 
    let ptr = S.as_ptr() as *const libc::c_char; 
    unsafe { 
     desc.amp_uri = ptr; 
     &desc as *const LV2Descriptor 
    } 
} 

answer copied from duplicate question

Problemi correlati