다음 스택 추적이 있습니다. 디버깅을 위해 이것에서 유용한 것을 알아낼 수 있습니까?
Program received signal SIGSEGV, Segmentation fault.
0x00000002 in ?? ()
(gdb) bt
#0 0x00000002 in ?? ()
#1 0x00000001 in ?? ()
#2 0xbffff284 in ?? ()
Backtrace stopped: previous frame inner to this frame (corrupt stack?)
(gdb)
를 얻을 때 코드를 살펴볼 곳은 어디 Segmentation fault
입니까? 스택 추적은 그다지 유용하지 않습니다.
참고 : 코드를 게시하면 SO 전문가가 답을 줄 것입니다. 나는 SO의 지침을 받아 직접 답을 찾고 싶으므로 여기에 코드를 게시하지 않습니다. 사과.
답변
이러한 가짜 주소 (0x00000002 등)는 실제로 SP 값이 아니라 PC 값입니다. 이제 가짜 (매우 작은) PC 주소를 가진 이런 종류의 SEGV를 얻을 때 99 %의 시간은 가짜 함수 포인터를 통해 호출하기 때문입니다. C ++의 가상 호출은 함수 포인터를 통해 구현되므로 가상 호출의 모든 문제는 동일한 방식으로 나타날 수 있습니다.
간접 호출 명령은 호출 후 PC를 스택으로 푸시 한 다음 PC를 대상 값 (이 경우 가짜)으로 설정하므로 이러한 상황 이 발생하면 수동으로 스택에서 PC를 꺼내어 쉽게 실행 취소 할 수 있습니다. . 32 비트 x86 코드에서는 다음을 수행합니다.
(gdb) set $pc = *(void **)$esp
(gdb) set $esp = $esp + 4
64 비트 x86 코드가 필요합니다.
(gdb) set $pc = *(void **)$rsp
(gdb) set $rsp = $rsp + 8
그런 다음 bt
코드가 실제로 어디에 있는지 파악할 수 있어야합니다 .
다른 1 %의 경우 오류는 일반적으로 스택에 저장된 어레이를 오버플로하여 스택을 덮어 쓰는 것으로 인해 발생합니다. 이 경우 valgrind 와 같은 도구를 사용하여 상황을 더 명확하게 파악할 수 있습니다.
답변
상황이 매우 간단하다면 Chris Dodd의 대답 이 가장 좋습니다. NULL 포인터를 통해 점프 한 것처럼 보입니다.
그러나 프로그램이 충돌하기 전에 발, 무릎, 목 및 눈에 총을 쏘았을 가능성이 있습니다. 스택을 덮어 쓰고 프레임 포인터를 엉망으로 만들고 기타 악영향을 미칠 수 있습니다. 그렇다면 해시를 풀면 감자와 고기가 표시되지 않을 것입니다.
보다 효율적인 솔루션은 디버거에서 프로그램을 실행하고 프로그램이 충돌 할 때까지 기능을 단계적으로 실행하는 것입니다. 충돌하는 함수가 식별되면 다시 시작하고 해당 함수로 들어가서 호출하는 함수가 충돌을 일으키는 지 확인합니다. 문제가되는 단일 코드 줄을 찾을 때까지 반복합니다. 75 %의 경우 수정이 명확 해집니다.
나머지 25 %의 상황에서 소위 불쾌감을주는 코드 라인은 붉은 청어입니다. 이전에 수천 줄이었던 이전에 많은 줄을 설정 한 (잘못된) 조건에 반응합니다. 이 경우 가장 좋은 과정은 다음과 같은 여러 요소에 따라 달라집니다. 대부분 코드에 대한 이해와 경험 :
- 디버거 감시 점을 설정하거나
printf
중요한 변수에 진단을 삽입 하면 필요한 A ha! - 다른 입력으로 테스트 조건을 변경하면 디버깅보다 더 많은 통찰력을 얻을 수 있습니다.
- 아마도 두 번째 눈은 당신의 가정을 확인하거나 간과 된 증거를 수집하도록 강요 할 것입니다.
- 때때로, 필요한 것은 저녁 식사에 가서 수집 된 증거에 대해 생각하는 것뿐입니다.
행운을 빕니다!
답변
스택 포인터가 유효하다고 가정합니다.
역 추적에서 SEGV가 어디에서 발생하는지 정확히 아는 것은 불가능할 수 있습니다. 처음 두 스택 프레임은 완전히 덮어 쓴 것 같습니다. 0xbffff284는 유효한 주소처럼 보이지만 다음 두 주소는 그렇지 않습니다. 스택을 자세히 살펴 보려면 다음을 시도해보십시오.
gdb $ x / 32ga $ rsp
또는 변형 (32를 다른 숫자로 대체). 그러면 주소 (a)로 형식이 지정된 거대한 (g) 크기의 스택 포인터에서 시작하여 몇 개의 단어 (32)가 출력됩니다. 형식에 대한 자세한 내용을 보려면 ‘help x’를 입력하십시오.
이 경우 센티넬 ‘printf’로 코드를 계측하는 것은 나쁜 생각이 아닐 수 있습니다.
답변
다른 레지스터 중 하나에 스택 포인터가 캐시되어 있는지 확인하십시오. 거기에서 스택을 검색 할 수 있습니다. 또한 이것이 포함 된 경우 스택은 매우 특정 주소에서 정의되는 경우가 많습니다. 이를 사용하면 때때로 적절한 스택을 얻을 수 있습니다. 이 모든 것은 초 공간으로 뛰어 들었을 때 프로그램이 그 과정에서 메모리 전체를 토하지 않았다고 가정합니다.
답변
스택 덮어 쓰기 인 경우 값은 프로그램에서 인식 할 수있는 것과 일치 할 수 있습니다.
예를 들어, 방금 스택을보고
(gdb) bt
#0 0x0000000000000000 in ?? ()
#1 0x000000000000342d in ?? ()
#2 0x0000000000000000 in ?? ()
그리고 0x342d
13357로 애플리케이션 로그를 작성했을 때 노드 ID로 판명되었습니다. 이는 즉시 스택 덮어 쓰기가 발생할 수있는 후보 사이트를 좁히는 데 도움이되었습니다.