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?
risposta
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.
-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. –
@R ..: Hm, pensavo che la variabile fosse usata in lettura/scrittura fino a quando ho letto la domanda in modo più approfondito. Aggiornato. –
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
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.
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
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
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. –
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
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
Si desidera il debug remoto fare una ricerca su Internet per ". linux debug remoto" un sacco di link – selbie
@selbie : Questo è il modo più difficile di fare le cose: –