Ich muss einen Segmentierungsfehler bei Bibliotheksbereinigungsvorgängen von Drittanbietern abfangen. Dies geschieht manchmal kurz vor dem Beenden meines Programms, und ich kann den wahren Grund dafür nicht ermitteln. In der Windows-Programmierung könnte ich dies mit __try - __catch tun. Gibt es eine plattformübergreifende oder plattformspezifische Möglichkeit, dasselbe zu tun? Ich brauche das unter Linux, gcc.
c++
segmentation-fault
try-catch
Alex F.
quelle
quelle
Antworten:
Unter Linux können wir diese auch als Ausnahmen haben.
Wenn Ihr Programm einen Segmentierungsfehler ausführt, wird normalerweise ein
SIGSEGV
Signal gesendet . Sie können einen eigenen Handler für dieses Signal einrichten und die Folgen abmildern. Natürlich sollten Sie wirklich sicher sein , dass Sie kann aus der Situation zu erholen. In Ihrem Fall sollten Sie stattdessen Ihren Code debuggen.Zurück zum Thema. Ich bin kürzlich auf eine Bibliothek ( kurzes Handbuch ) gestoßen , die solche Signale in Ausnahmen umwandelt, sodass Sie Code wie folgt schreiben können:
try { *(int*) 0 = 0; } catch (std::exception& e) { std::cerr << "Exception caught : " << e.what() << std::endl; }
Ich habe es aber nicht überprüft.Funktioniert auf meiner x86-64 Gentoo Box. Es verfügt über ein plattformspezifisches Backend (aus der Java-Implementierung von gcc entlehnt), sodass es auf vielen Plattformen funktionieren kann. Es unterstützt nur x86 und x86-64, aber Sie können Backends von libjava erhalten, das sich in gcc-Quellen befindet.quelle
-fnon-call-exceptions
sicherstellen, dass es funktioniert - und dies verursacht Leistungskosten. Es besteht auch die Gefahr, dass Sie von einer Funktion ohne Ausnahmeunterstützung (wie einer C-Funktion) geworfen werden und später auslaufen / abstürzen../build_gcc_linux_release
gibt mehrere Fehler.Hier ist ein Beispiel, wie es in C gemacht wird.
#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; }
quelle
signal(7)
listet alle async-signal-sicheren Funktionen auf, die mit relativ wenig Sorgfalt verwendet werden können. Im obigen Beispiel ist es auch völlig sicher, da nichts anderes im Programm berührtstdout
als derprintf
Aufruf im Handler.Aus Gründen der Portabilität sollte man wahrscheinlich
std::signal
die Standard-C ++ - Bibliothek verwenden, aber es gibt viele Einschränkungen, was ein Signalhandler tun kann. Leider ist es nicht möglich, eine SIGSEGV aus einem C ++ - Programm heraus abzufangen , ohne ein undefiniertes Verhalten einzuführen, da in der Spezifikation Folgendes angegeben ist:abort
,exit
einige Atomfunktionen, Stromsignal - Handler installieren,memcpy
,memmove
, Typ Züge, `std :: bewegen, std::forward
, und einige mehr ).throw
Ausdruck verwendet.Dies beweist, dass es unmöglich ist, SIGSEGV innerhalb eines Programms mit streng standardmäßigem und portablem C ++ abzufangen. SIGSEGV ist nach wie vor durch das Betriebssystem gefangen und wird in der Regel an dem übergeordneten Prozess berichtet , wenn eine Warte Familie Funktion aufgerufen wird.
Bei der Verwendung des POSIX-Signals werden Sie wahrscheinlich auf die gleichen Probleme stoßen, da in 2.4.3 Signalaktionen eine Klausel steht :
Ein Wort zum
longjump
s. Angenommen, wir verwenden POSIX-Signale,longjump
hilft es nicht, das Abwickeln des Stapels zu simulieren:Dies bedeutet, dass die Fortsetzung, die durch den Aufruf von longjump aufgerufen wird, normalerweise nützliche Bibliotheksfunktionen wie oder oder von main nicht zuverlässig aufrufen kann
printf
, ohne ein undefiniertes Verhalten hervorzurufen . Als solches kann die Fortsetzung nur eingeschränkte Operationen ausführen und kann nur durch einen abnormalen Beendigungsmechanismus beendet werden.malloc
exit
Um es kurz zu machen, ein SIGSEGV abzufangen und die Ausführung des Programms in einem tragbaren Gerät fortzusetzen, ist wahrscheinlich nicht möglich, ohne UB einzuführen. Selbst wenn Sie auf einer Windows-Plattform arbeiten, für die Sie Zugriff auf die Behandlung strukturierter Ausnahmen haben, ist es erwähnenswert, dass MSDN empfiehlt, niemals zu versuchen, Hardware-Ausnahmen zu behandeln: Hardware-Ausnahmen
quelle
C ++ - Lösung finden Sie hier ( 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); } }
quelle
Manchmal möchten wir a abfangen
SIGSEGV
, um herauszufinden, ob ein Zeiger gültig ist, dh ob er auf eine gültige Speicheradresse verweist. (Oder überprüfen Sie sogar, ob ein beliebiger Wert ein Zeiger sein kann.)Eine Option ist, es mit zu überprüfen
isValidPtr()
(arbeitete auf 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); }
Eine andere Möglichkeit besteht darin, die Speicherschutzattribute zu lesen, was etwas schwieriger ist (funktioniert unter Android):
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()
istprintf()
zum Android-Protokoll.FIRST_UNUSED_BIT()
ist hier definiert .PPS Es ist möglicherweise keine gute Idee, alloca () in einer Schleife aufzurufen. Der Speicher wird möglicherweise erst freigegeben, wenn die Funktion zurückgegeben wird.
quelle