2012-06-29 7 views
9

Ho un programma linux C che gestisce la richiesta inviata a un socket TCP (associato a una particolare porta). Voglio essere in grado di interrogare lo stato interno del programma C tramite una richiesta a quella porta, ma non desidero codificare il codice su quali variabili globali possono essere interrogate. Quindi voglio che la query contenga il nome della stringa di un codice globale e il codice C per cercare quella stringa nella tabella dei simboli per trovare il suo indirizzo e quindi inviare il suo valore sul socket TCP. Ovviamente la tabella dei simboli non deve essere stata spogliata. Quindi il programma C può anche localizzare la propria tabella dei simboli, e c'è un'interfaccia di libreria per cercare i simboli dato il loro nome? Questo è un programma C eseguibile ELF creato con gcc.Un programma C in esecuzione può accedere alla propria tabella dei simboli?

+0

Si desidera il debug remoto fare una ricerca su Internet per ". linux debug remoto" un sacco di link – selbie

+0

@selbie : Questo è il modo più difficile di fare le cose: –

risposta

13

Questo è in realtà abbastanza semplice. Si utilizza dlopen/dlsym per accedere ai simboli. Affinché questo funzioni, i simboli devono essere presenti nella tabella dei simboli dinamici. Ci sono più tabelle di simboli!

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

__attribute__((visibility("default"))) 
const char A[] = "Value of A"; 

__attribute__((visibility("hidden"))) 
const char B[] = "Value of B"; 

const char C[] = "Value of C"; 

int main(int argc, char *argv[]) 
{ 
    void *hdl; 
    const char *ptr; 
    int i; 

    hdl = dlopen(NULL, 0); 
    for (i = 1; i < argc; ++i) { 
     ptr = dlsym(hdl, argv[i]); 
     printf("%s = %s\n", argv[i], ptr); 
    } 
    return 0; 
} 

Per aggiungere tutti i simboli della tabella dei simboli dinamica, utilizzare -Wl,--export-dynamic. Se si desidera rimuovere la maggior parte dei simboli dalla tabella dei simboli (consigliata), impostare -fvisibility=hidden e quindi aggiungere esplicitamente i simboli desiderati con __attribute__((visibility("default"))) o uno degli altri metodi.

 
~ $ gcc dlopentest.c -Wall -Wextra -ldl 
~ $ ./a.out A B C 
A = (null) 
B = (null) 
C = (null) 
~ $ gcc dlopentest.c -Wall -Wextra -ldl -Wl,--export-dynamic 
~ $ ./a.out A B C 
A = Value of A 
B = (null) 
C = Value of C 
~ $ gcc dlopentest.c -Wall -Wextra -ldl -Wl,--export-dynamic -fvisibility=hidden 
~ $ ./a.out A B C 
A = Value of A 
B = (null) 
C = (null) 

sicurezza

noti che c'è un sacco di spazio per cattivo comportamento.

 
$ ./a.out printf 
printf = ▯▯▯▯ (garbage) 

Se si desidera che questo sia sicuro, è necessario creare una whitelist di simboli consentiti.

+0

-stringa (poss ibly not terminated) i dati o le funzioni come stringa sono pericolosi. Altrimenti non vedo alcuna necessità di inserire i simboli nella whitelist. –

+0

@R ..: Hm, pensavo che la variabile fosse usata in lettura/scrittura fino a quando ho letto la domanda in modo più approfondito. Aggiornato. –

+0

Questo è a metà strada per quello che voglio ... il problema è che voglio essere in grado di rispondere in modo significativo indipendentemente dal tipo di simbolo. Cioè e se A fosse un "lungo" e B un "char" e C un "char *"? Ho bisogno di accedere al tipo di simbolo e al suo indirizzo. – JimKleck

1

Il termine per questo tipo di funzione è "reflection", e non è parte di C.

Se questo è per il debug, e si vuole essere in grado di ispezionare l'intero stato di un C programma in remoto, esaminare qualsiasi variabile, avviare e interrompere la sua esecuzione, e così via, si potrebbe prendere in considerazione GDBremote debugging:

GDB offre una modalità 'a distanza' spesso usato durante il debug dei sistemi embedded. L'operazione remota è quando GDB è in esecuzione su una macchina e il programma è eseguito su in un'altra. GDB può comunicare con lo 'stub' remoto che comprende il protocollo GDB via seriale o TCP/IP. È possibile creare un programma stub collegando i file stub appropriati forniti con il GDB , che implementano il lato di destinazione del protocollo di comunicazione . In alternativa, gdbserver può essere utilizzato per eseguire il debug in remoto del programma senza doverlo modificare in alcun modo.

+1

Questo è per la produzione, e voglio essere in grado di sfogliare qualsiasi simbolo senza dover mantenere una tabella di ricerca ... dopo tutto, quelle informazioni sono già nella tabella dei simboli – JimKleck

6

di file: reflect.c

#include <stdio.h> 
#include "reflect.h" 

struct sym_table_t gbl_sym_table[1] __attribute__((weak)) = {{NULL, NULL}}; 

void * reflect_query_symbol(const char *name) 
{ 
    struct sym_table_t *p = &gbl_sym_table[0]; 

    for(; p->name; p++) { 
     if(strcmp(p->name, name) == 0) { 
      return p->addr; 
     } 
    } 
    return NULL; 
} 

di file: reflect.h

#include <stdio.h> 

struct sym_table_t { 
    char *name; 
    void *addr; 
}; 

void * reflect_query_symbol(const char *name); 

di file: main.c

solo #include "riflettere.h" e chiamare reflect_query_symbol

esempio:

#include <stdio.h> 
#include "reflect.h" 

void foo(void) 
{ 
    printf("bar test\n"); 
} 

int uninited_data; 

int inited_data = 3; 

int main(int argc, char *argv[]) 
{ 
    int i; 
    void *addr; 

    for(i=1; i<argc; i++) { 
     addr = reflect_query_symbol(argv[i]); 
     if(addr) { 
      printf("%s lay at: %p\n", argv[i], addr); 
     } else { 
      printf("%s NOT found\n", argv[i], addr); 
     } 
    } 

    return 0; 
} 

di file:. Makefile

objs = main.o reflect.o 

main: $(objs) 
     gcc -o [email protected] $^ 
     nm [email protected] | awk 'BEGIN{ print "#include <stdio.h>"; print "#include \"reflect.h\""; print "struct sym_table_t gbl_sym_table[]={" } { if(NF==3){print "{\"" $$3 "\", (void*)0x" $$1 "},"}} END{print "{NULL,NULL} };"}' > .reflect.real.c 
     gcc -c .reflect.real.c -o .reflect.real.o 
     gcc -o [email protected] $^ .reflect.real.o 
     nm [email protected] | awk 'BEGIN{ print "#include <stdio.h>"; print "#include \"reflect.h\""; print "struct sym_table_t gbl_sym_table[]={" } { if(NF==3){print "{\"" $$3 "\", (void*)0x" $$1 "},"}} END{print "{NULL,NULL} };"}' > .reflect.real.c 
     gcc -c .reflect.real.c -o .reflect.real.o 
     gcc -o [email protected] $^ .reflect.real.o 
+0

Hai solo bisogno di scrivere due file come mostrare "reflect.c" e "reflect.h" e modificare il tuo "Makefile", otterrai una tabella con il nome del simbolo e l'indirizzo corrispondente del simbolo. –

+0

Voglio il tipo di simbolo, o almeno la dimensione, così come l'indirizzo. Sembra che il flag "-S" per nm lo farà. Non è necessario eseguire nm sul file eseguibile finale (poiché ho più .o's) per ottenere la dimensione corretta per gbl_sym_table, quindi eseguire nuovamente nm per riempirlo con gli indirizzi? Quindi finalmente ricostruire reflect.o e ricollegarsi per ottenere tutto nell'eseguibile? – JimKleck

+0

In realtà, perché preoccuparsi di nm e magia dei makefile? Il programma nm deve utilizzare alcune API per accedere alla tabella dei simboli, voglio che l'API utilizzi direttamente nel mio programma. – JimKleck

Problemi correlati