2010-09-30 12 views
0

J'interpose la fonction memcpy() en C car l'application cible l'utilise pour concaténer des chaînes et je veux savoir quelles chaînes sont en cours de création. Le code est:Déterminer si un pointeur reçu est une chaîne, ushort ou un tableau

void * my_memcpy (void * destination, const void * source, size_t num) 
{ 
    void *ret = memcpy(destination, source, num); 
    // printf ("[MEMCPY] = %s \n", ret); 
    return ret; 
} 

La fonction est appelée, mais avec succès le premier paramètre peut être tout ce que je veux seulement tracer si le résultat est une chaîne ou un tableau. Je devrais demander si c'est un tableau ou une chaîne. Je sais que cela ne peut pas être simple: y a-t-il un moyen de savoir sur quoi pointe le RET? Je travaille sous MACOSX et en interposition avec DYLD.

Merci beaucoup.

+0

Avez-vous accès à un fichier de carte? Si oui, vous pouvez trouver où vos littéraux de chaîne sont placés et avoir des informations à ce sujet. Idem pour les tableaux globaux. –

+0

Vous ne devriez pas appeler 'printf' dans votre fonction, car' printf' est très compliqué et peut lui-même appeler 'memcpy'. Au lieu de cela, j'irais pour un simple 'write' ou' writev'. Ou vous pouvez ajouter un compteur d'activation thread-safe qui ne fait que la partie 'printf' si elle n'est pas déjà active. –

Répondre

2

Comme void* représente un bloc brut de mémoire, il n'y a aucun moyen de déterminer quelles données réelles se trouvent là. Toutefois, vous pouvez effectuer un vidage de la mémoire de type "chaîne" à chaque opération, mais donner à la sortie résultante une sorte de "limite de sortie supérieure".

Cela pourrait être mis en œuvre de la manière suivante:

const size_t kUpperLimit = 32; 

void output_memory_dump(void* memory) { 
    std::cout.write(reinterpret_cast<char*>(memory), kUpperLimit); 
} 

Pour les non-chaîne comme la sortie des données ne serait guère interprétable, mais sinon, vous obtiendrez ce que vous recherchez.

Vous pouvez essayer d'appliquer une approche fondée sur les conjectures comme itérer reinterpret_cast<void*>(memory) et faire is_alphanumeric && is_space contrôles à chaque symbole, mais cette approche ne semble pas très stable (qui sait ce qui pourrait se trouver en fait dans ce void* ...).

De toute façon, pour certaines situations qui pourraient être bien.

+0

Que faire si la région de mémoire est inférieure à 32 octets? Ou moins de 5? Comment savez-vous quelle limite supérieure utiliser? Eh bien, au moins vous pouvez utiliser 'num' pour cela, mais ce nombre peut être inférieur à la taille réelle de la zone de mémoire. –

+0

@Vlad Ok, nous aurons une sauvegarde de 5 octets réels et tout ce qui va après. Je suis presque sûr que cela ne peut pas conduire à des défauts d'accès en mode débogage et ainsi de suite. * Il est également évident, que vous ne pouvez pas avoir une approche "assez bonne" si vous avez seulement le pointeur "void *" et rien d'autre, mais pourquoi ne pas essayer ... * –

+0

@ HardCoder1986: Oh oui, il peut SEGFAULT votre programme facilement en raison de l'accès à la mémoire protégée. –

0

ret est égal au pointeur de destination. Mais il n'est pas possible de déterminer si c'est un tableau ou une chaîne, sauf si vous en savez plus sur le tableau ou la chaîne (par exemple, que la chaîne a une certaine longueur et est terminée par un caractère nul).

1

Vous pouvez d'abord appliquer des heuristiques à la mémoire copiée et, en fonction de cela, vous pouvez décider si vous souhaitez l'imprimer.

static int maybe_string(const void *data, size_t n) { 
    const unsigned char *p; 
    size_t i; 

    p = data; 
    for (i = 0; i < n; i++) { 
    int c = p[i]; 
    if (c == '\n' || c == '\r' || c == '\t') 
     continue; 
    if (1 <= c && c < 32) 
     return 0; /* unusual ASCII control character */ 
    if (c == '\0' && i > 5) 
     return 1; /* null-terminated and more than a few characters long */ 
    } 

    return 0; /* not null-terminated, so it isn't a string */ 
} 

Cette heuristique n'est pas parfaite. Par exemple, il échoue pour le motif suivant:

const char *str = "hello, world"; 
size_t len = strlen(str); 
char *buf = malloc(1024); 
memcpy(buf, str, len); 
buf[len] = '\0'; 

Si vous voulez attraper cela aussi, vous devrez changer la fonction ci-dessus.

+1

Considérant qu'il interpose la fonction standard 'memcpy', cela ralentira beaucoup le système. Sans oublier que la mémoire peut contenir n'importe quelle donnée binaire incluant tous les octets dans la gamme ASCII ce qui ne veut pas dire que c'est une chaîne. –

+0

Vous avez raison: lorsque * chaque * octet est dans la plage ASCII (et non pas \ 0), la mémoire ne contient pas de chaîne. C'est ce que le 'return 0' de la dernière ligne est pour. Mais sinon, l'affiche originale ressemblait plus à une simple curiosité et non à une utilisation en production, donc le ralentissement est probablement acceptable. Je ne pense pas que l'impression de quelques "chaînes" supplémentaires fait mal, je voulais juste m'assurer que le terminal ne soit pas dérouté par des caractères de contrôle accidentels. Par conséquent, vérifiez les caractères de contrôle ASCII inhabituels. –

0

Non, vous ne pouvez pas comprendre cela à partir d'un pointeur de type void. De plus, vous ne connaissez pas la taille de la source ou de la destination, donc l'approche heuristique ne fonctionnera pas. Cela ne fonctionnera pas pour d'autres raisons, par exemple, les données binaires stockées dans la zone mémoire pointée par void* peuvent vraiment avoir zéro octet à la fin, mais cela ne signifie pas que c'est une chaîne.