J'ai besoin d'intercepter une erreur de segmentation dans les opérations de nettoyage de bibliothèques tierces. Cela arrive parfois juste avant la fin de mon programme, et je ne peux pas corriger la vraie raison de cela. Dans la programmation Windows, je pourrais le faire avec __try - __catch. Existe-t-il une manière multi-plateforme ou spécifique à la plateforme de faire la même chose? J'ai besoin de ça sous Linux, gcc.Comment faire pour attraper la faute de segmentation sous Linux?
Répondre
Sur Linux, nous pouvons les avoir comme exceptions, aussi.
Normalement, lorsque votre programme exécute un défaut de segmentation, il reçoit un signal SIGSEGV
. Vous pouvez configurer votre propre gestionnaire pour ce signal et atténuer les conséquences. Bien sûr, vous devriez vraiment être sûr que vous pouvez récupérer de la situation. Dans votre cas, je pense, vous devriez déboguer votre code à la place.
Retour au sujet. J'ai récemment rencontré a library (short manual) qui transforme ces signaux à des exceptions, de sorte que vous pouvez écrire du code comme ceci:
try
{
*(int*) 0 = 0;
}
catch (std::exception& e)
{
std::cerr << "Exception catched : " << e.what() << std::endl;
}
n'a pas vérifié, cependant.
Fonctionne sur ma boîte Gentoo x86-64. Il a un backend spécifique à la plate-forme (emprunté à l'implémentation java de gcc), donc il peut fonctionner sur de nombreuses plateformes. Il prend en charge uniquement x86 et x86-64, mais vous pouvez obtenir des backend à partir de libjava, qui réside dans les sources gcc.
Voici un exemple de la façon de le faire en C.
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void segfault_sigaction(int signal, siginfo_t *si, void *arg)
{
printf("Caught segfault at address %p\n", si->si_addr);
exit(0);
}
int main(void)
{
int *foo = NULL;
struct sigaction sa;
memset(&sa, 0, sizeof(struct sigaction));
sigemptyset(&sa.sa_mask);
sa.sa_sigaction = segfault_sigaction;
sa.sa_flags = SA_SIGINFO;
sigaction(SIGSEGV, &sa, NULL);
/* Cause a seg fault */
*foo = 1;
return 0;
}
sizeof (sigaction) ==> sizeof (struct sigaction), sinon vous obtenez une erreur ISO C++ qui compile la chose. –
Puis-je obtenir une trace de pile quand elle est signalée? – daisy
Faire des E/S dans un gestionnaire de signal est une recette pour un désastre. –
C++ solution trouvée ici (http://www.cplusplus.com/forum/unices/16430/)
#include <signal.h>
#include <stdio.h>
#include <unistd.h>
void ouch(int sig)
{
printf("OUCH! - I got signal %d\n", sig);
}
int main()
{
struct sigaction act;
act.sa_handler = ouch;
sigemptyset(&act.sa_mask);
act.sa_flags = 0;
sigaction(SIGINT, &act, 0);
while(1) {
printf("Hello World!\n");
sleep(1);
}
}
Je sais que c'est juste un exemple que vous n'avez pas écrit, mais faire des E/S dans un gestionnaire de signal est une recette pour un désastre. –
@TimSeguine: répéter ce qui est au mieux très trompeur n'est pas une bonne idée (cf https://stackoverflow.com/questions/2350489/how-to-catch-segmentation-fault-in-linux#comment81651055_2436368) – stefanct
@ stefanct Les précautions nécessaires pour utiliser printf en toute sécurité dans un gestionnaire de signal ne sont pas triviales. Il n'y a rien de trompeur à ce sujet. Ceci est un exemple de jouet. Et même dans cet exemple de jouet, il est possible de faire une impasse si vous chronométrez correctement le SIGINT. Les deadlocks sont dangereux précisément parce qu'ils sont rares. Si vous pensez que ce conseil était trompeur, alors éloignez-vous de mon code, car je ne vous fais pas confiance à moins d'un kilomètre de celui-ci. –
Parfois, nous voulons attraper un SIGSEGV
pour savoir si un pointeur est valide, c'est-à-dire s'il fait référence à une adresse mémoire valide. (Ou même vérifier si une valeur arbitraire peut être un pointeur.)
Une option est de vérifier avec isValidPtr()
(a travaillé sur Android):
int isValidPtr(const void*p, int len) {
if (!p) {
return 0;
}
int ret = 1;
int nullfd = open("/dev/random", O_WRONLY);
if (write(nullfd, p, len) < 0) {
ret = 0;
/* Not OK */
}
close(nullfd);
return ret;
}
int isValidOrNullPtr(const void*p, int len) {
return !p||isValidPtr(p, len);
}
Une autre option est de lire les attributs de protection de la mémoire, qui est un peu plus compliqué (a travaillé sur les applications):
re_mprot.c:
#include <errno.h>
#include <malloc.h>
//#define PAGE_SIZE 4096
#include "dlog.h"
#include "stdlib.h"
#include "re_mprot.h"
struct buffer {
int pos;
int size;
char* mem;
};
char* _buf_reset(struct buffer*b) {
b->mem[b->pos] = 0;
b->pos = 0;
return b->mem;
}
struct buffer* _new_buffer(int length) {
struct buffer* res = malloc(sizeof(struct buffer)+length+4);
res->pos = 0;
res->size = length;
res->mem = (void*)(res+1);
return res;
}
int _buf_putchar(struct buffer*b, int c) {
b->mem[b->pos++] = c;
return b->pos >= b->size;
}
void show_mappings(void)
{
DLOG("-----------------------------------------------\n");
int a;
FILE *f = fopen("/proc/self/maps", "r");
struct buffer* b = _new_buffer(1024);
while ((a = fgetc(f)) >= 0) {
if (_buf_putchar(b,a) || a == '\n') {
DLOG("/proc/self/maps: %s",_buf_reset(b));
}
}
if (b->pos) {
DLOG("/proc/self/maps: %s",_buf_reset(b));
}
free(b);
fclose(f);
DLOG("-----------------------------------------------\n");
}
unsigned int read_mprotection(void* addr) {
int a;
unsigned int res = MPROT_0;
FILE *f = fopen("/proc/self/maps", "r");
struct buffer* b = _new_buffer(1024);
while ((a = fgetc(f)) >= 0) {
if (_buf_putchar(b,a) || a == '\n') {
char*end0 = (void*)0;
unsigned long addr0 = strtoul(b->mem, &end0, 0x10);
char*end1 = (void*)0;
unsigned long addr1 = strtoul(end0+1, &end1, 0x10);
if ((void*)addr0 < addr && addr < (void*)addr1) {
res |= (end1+1)[0] == 'r' ? MPROT_R : 0;
res |= (end1+1)[1] == 'w' ? MPROT_W : 0;
res |= (end1+1)[2] == 'x' ? MPROT_X : 0;
res |= (end1+1)[3] == 'p' ? MPROT_P
: (end1+1)[3] == 's' ? MPROT_S : 0;
break;
}
_buf_reset(b);
}
}
free(b);
fclose(f);
return res;
}
int has_mprotection(void* addr, unsigned int prot, unsigned int prot_mask) {
unsigned prot1 = read_mprotection(addr);
return (prot1 & prot_mask) == prot;
}
char* _mprot_tostring_(char*buf, unsigned int prot) {
buf[0] = prot & MPROT_R ? 'r' : '-';
buf[1] = prot & MPROT_W ? 'w' : '-';
buf[2] = prot & MPROT_X ? 'x' : '-';
buf[3] = prot & MPROT_S ? 's' : prot & MPROT_P ? 'p' : '-';
buf[4] = 0;
return buf;
}
re_mprot.h:
#include <alloca.h>
#include "re_bits.h"
#include <sys/mman.h>
void show_mappings(void);
enum {
MPROT_0 = 0, // not found at all
MPROT_R = PROT_READ, // readable
MPROT_W = PROT_WRITE, // writable
MPROT_X = PROT_EXEC, // executable
MPROT_S = FIRST_UNUSED_BIT(MPROT_R|MPROT_W|MPROT_X), // shared
MPROT_P = MPROT_S<<1, // private
};
// returns a non-zero value if the address is mapped (because either MPROT_P or MPROT_S will be set for valid addresses)
unsigned int read_mprotection(void* addr);
// check memory protection against the mask
// returns true if all bits corresponding to non-zero bits in the mask
// are the same in prot and read_mprotection(addr)
int has_mprotection(void* addr, unsigned int prot, unsigned int prot_mask);
// convert the protection mask into a string. Uses alloca(), no need to free() the memory!
#define mprot_tostring(x) (_mprot_tostring_((char*)alloca(8) , (x)))
char* _mprot_tostring_(char*buf, unsigned int prot);
PS DLOG()
est printf()
dans le journal Android. FIRST_UNUSED_BIT()
est défini here.
PPS Ce n'est peut-être pas une bonne idée d'appeler alloca() dans une boucle - la mémoire peut ne pas être libérée jusqu'à ce que la fonction retourne.
- 1. faute de segmentation graphviz
- 2. Faute de segmentation de liste liée C++
- 3. faute de segmentation flex/bison avant main()
- 4. Faute de segmentation - Arbre Huffman adaptatif
- 5. Segmentation sous Linux: la segmentation et la pagination sont-elles redondantes?
- 6. Comment faire pour attraper l'exception de CloseHandle()
- 7. C++ transformer avec une paire se Segmentation faute
- 8. Comment faire pour attraper BrokenRuleException dans PLINQO?
- 9. Comment faire pour détacher l'écran par programmation sous Linux?
- 10. Comment faire pour attraper l'erreur de fonction pg_connect()?
- 11. Processus Apache-httpd meurent avec la faute de segmentation sur le déploiement
- 12. Est-il possible en quelque sorte "sauver" la faute de segmentation sur Ruby
- 13. Auto-modification du code toujours des erreurs de segmentation sous Linux
- 14. Où la faute de segmentation est-elle générée dans ce code fileté?
- 15. Bonne façon de faire la programmation des threads sous Linux
- 16. Comment faire pour attraper ConfigurationErrorsException pour violer maxRequestLength?
- 17. Faire une interface réseau virtuelle sous Linux?
- 18. Comment faire pour attraper les événements ouverts de fichier
- 19. Bizarre erreurs de segmentation de la segmentation
- 20. Comment faire une authentification basique avec FireWatir sous Ubuntu Linux?
- 21. Capture des backtraces de défaillances de segmentation à partir de Ruby et Resque sous Linux
- 22. Comment faire une capture d'écran d'une application sous Linux?
- 23. Je ne suis pas sûr de ce qui cause ma faute de segmentation - C++
- 24. Comment faire pour convertir la fonction String en const wchar_t * sous Windows et Linux
- 25. Comment faire pour attraper python stdout dans le code C++
- 26. Comment faire pour attraper l'événement nommé dans Windows CE
- 27. Comment faire pour attraper l'erreur dans addToTag() [grails]
- 28. Comment faire pour emprisonner l'utilisateur linux
- 29. Comment faire attendre le programme C (sous Linux)?
- 30. Comment faire pour attraper DeadlineExceedException dans GAE/J?
+1 pour être sûr que vous pouvez récupérer avant d'attraper sig segfault__ –
Votre lien vers le manuel abrégé est cassé. – Ponkadoodle
Lancer à partir d'un gestionnaire de signal est une chose très dangereuse à faire. La plupart des compilateurs supposent que seuls les appels peuvent générer des exceptions et configurer les informations de déroulement en conséquence.Les langages qui transforment les exceptions matérielles en exceptions logicielles, comme Java et C#, sont conscients que tout peut être lancé; ce n'est pas le cas avec C++. Avec GCC, vous avez au moins besoin de '-fnon-call-exceptions' pour vous assurer que cela fonctionne - et cela entraîne des coûts de performance. Il y a aussi un risque que vous lanciez à partir d'une fonction sans exception support (comme une fonction C) et que vous fuyiez/plantiez plus tard. – zneak