타사 라이브러리 정리 작업에서 세분화 오류를 잡아야합니다. 이것은 때때로 내 프로그램이 종료되기 직전에 발생하며 실제 이유를 수정할 수 없습니다. Windows 프로그래밍에서 __try-__catch로이 작업을 수행 할 수 있습니다. 동일한 작업을 수행하는 교차 플랫폼 또는 플랫폼 별 방법이 있습니까? Linux, gcc에서 필요합니다.
타사 라이브러리 정리 작업에서 세분화 오류를 잡아야합니다. 이것은 때때로 내 프로그램이 종료되기 직전에 발생하며 실제 이유를 수정할 수 없습니다. Windows 프로그래밍에서 __try-__catch로이 작업을 수행 할 수 있습니다. 동일한 작업을 수행하는 교차 플랫폼 또는 플랫폼 별 방법이 있습니까? Linux, gcc에서 필요합니다.
답변:
리눅스에서는 이것도 예외로 할 수 있습니다.
일반적으로 프로그램이 세그멘테이션 오류를 수행하면 SIGSEGV
신호 가 전송 됩니다. 이 신호에 대한 자체 핸들러를 설정하고 결과를 완화 할 수 있습니다. 물론 상황에서 회복 할 수 있다는 것을 정말로 확신해야합니다 . 귀하의 경우에는 대신 코드를 디버그해야한다고 생각합니다.
주제로 돌아갑니다. 최근에 그러한 신호를 예외로 변환 하는 라이브러리 ( 짧은 매뉴얼 )를 만났 으므로 다음과 같은 코드를 작성할 수 있습니다.
try
{
*(int*) 0 = 0;
}
catch (std::exception& e)
{
std::cerr << "Exception caught : " << e.what() << std::endl;
}
그래도 확인하지 않았습니다. x86-64 Gentoo 상자에서 작동합니다. 플랫폼 별 백엔드 (gcc의 Java 구현에서 차용)가 있으므로 많은 플랫폼에서 작동 할 수 있습니다. 기본적으로 x86 및 x86-64 만 지원하지만 gcc 소스에있는 libjava에서 백엔드를 가져올 수 있습니다.
-fnon-call-exceptions
작동하는지 확인해야하며 이에 대한 성능 비용이 있습니다. 또한 예외 지원 (예 : C 함수)없이 함수에서 던지고 나중에 누출 / 충돌 할 위험이 있습니다.
./build_gcc_linux_release
몇 가지 오류가 있습니다.
다음은 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;
}
signal(7)
상대적으로 거의주의를 기울이지 않고 사용할 수있는 모든 비동기 신호 안전 함수를 나열합니다. 위의 예에서는 프로그램의 다른 어떤 것도 처리기 stdout
의 printf
호출을 건드리지 않기 때문에 완전히 안전 합니다.
이식성 std::signal
을 위해 표준 C ++ 라이브러리에서 사용해야 하지만 신호 처리기가 수행 할 수있는 작업에는 많은 제한이 있습니다. 안타깝게도 사양에 다음과 같이 명시되어 있기 때문에 정의되지 않은 동작을 도입하지 않고 C ++ 프로그램 내에서 SIGSEGV를 잡을 수 없습니다 .
abort
, exit
몇몇 원자 기능, 전류 신호 처리기, 다시 memcpy
, memmove
입력 특성`표준 : 이동, std::forward
일부 이상, ).throw
표현식을 입니다.이것은 엄격한 표준 및 이식 가능한 C ++를 사용 하는 프로그램 내에서 SIGSEGV를 포착하는 것이 불가능하다는 것을 증명합니다 . SIGSEGV는 여전히 운영 체제에 의해 포착되며 일반적으로 대기 할 때 상위 프로세스에보고됩니다. family 함수가 호출 .
2.4.3 Signal Actions에 다음 과 같은 절이 있기 때문에 POSIX 신호를 사용하는 것과 동일한 종류의 문제가 발생할 수 있습니다 .
는에 의해 생성되지 않은 SIGBUS, SIGFPE, SIGILL 또는 SIGSEGV 신호에 대한 신호 끄는 기능에서 정상적으로 반환 후 공정의 동작이 정의되어
kill()
,sigqueue()
또는raise()
.
longjump
s 에 대한 한마디 . POSIX 신호를 사용한다고 가정하면 longjump
스택 해제를 시뮬레이션 하는 데 사용 하면 도움이되지 않습니다.
longjmp()
는 비동기 신호 안전 함수 이지만 비동기 신호 안전이 아닌 함수 또는 동등한 기능을 중단 한 신호 처리기에서 호출되는 경우 (예 :에exit()
대한 초기 호출에서 반환 된 후 수행되는 것과 동등한 처리main()
), 비동기 신호 안전이 아닌 함수 또는 이에 상응하는 후속 호출의 동작은 정의되지 않습니다.
이는 longjump 호출에 의해 호출 된 연속이 printf
, malloc
또는 같은 일반적으로 유용한 라이브러리 함수를 안정적으로 호출 할 수 없음을 의미합니다.exit
정의되지 않은 동작을 유도하지 않고 main에서 리턴 . 따라서 연속은 제한된 작업 만 수행 할 수 있으며 일부 비정상적인 종료 메커니즘을 통해서만 종료 될 수 있습니다.
SIGSEGV라는 잡기, 짧은 물건을 넣으려면 및 휴대용에서 프로그램의 실행을 재개하는 것은 UB를 도입하지 않고 아마 불가능하다. 구조적 예외 처리에 액세스 할 수있는 Windows 플랫폼에서 작업하는 경우에도 MSDN이 하드웨어 예외를 처리하지 않도록 권장한다는 점을 언급 할 가치가 있습니다. 하드웨어 예외
여기에 C ++ 솔루션이 있습니다 ( 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);
}
}
때때로 우리 SIGSEGV
는 포인터가 유효한지, 즉 유효한 메모리 주소를 참조하는지 알아보기 위해 a를 잡으려고합니다 . (또는 임의의 값이 포인터인지 확인하십시오.)
한 가지 옵션은 다음으로 확인하는 것입니다 isValidPtr()
(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);
}
또 다른 옵션은 메모리 보호 속성을 읽는 것입니다. 이는 좀 더 까다 롭습니다 (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()
는 printf()
Android 로그입니다. 여기FIRST_UNUSED_BIT()
에 정의되어 있습니다 .
PPS alloca () 를 호출하는 것은 좋은 생각이 아닐 수 있습니다. 루프에서 않을 수 있습니다. 함수가 반환 될 때까지 메모리가 해제되지 않을 수 있습니다.