코어 덤프에서 스택 추적을 재구성하는 방법은 무엇입니까?

코어 덤프에서 스택 추적을 재구성하는 방법은 무엇입니까?

coredumpctl코어 덤프를 생성한 후 유틸리티는 프로그램의 스택 추적을 표시합니다.

예를 들어 Firefox 코어 덤프에서는 다음과 같습니다.

Stack trace of thread 14469:
#0  0x00007f0ac652d3bd pthread_cond_wait@@GLIBC_2.3.2 (libpthread.so.0)
#1  0x0000560f2ab95488 _ZN7mozilla6detail21ConditionVariableImpl4waitERNS0_9MutexImplE (firefox)
#2  0x0000560f2ab95646 _ZN7mozilla6detail21ConditionVariableImpl8wait_forERNS0_9MutexImplERKNS_16BaseTimeDurationINS_27TimeDurati>
#3  0x00007f0aba9799f9 n/a (libxul.so)
#4  0x00007f0aba96eb9a n/a (libxul.so)
#5  0x00007f0ac652708c start_thread (libpthread.so.0)
#6  0x00007f0ac5abce7f __clone (libc.so.6)

이것이 C 코드이므로 컴파일된다는 점을 고려하면 기호는 바이너리에 직접 포함되지 않습니다. 그렇다면 이것이 어떻게 가능할까요?
또한 readelf는 실제로 이 작업을 어떻게 수행합니까?

(제 생각에는 ELF 파일에 포함된 기호 테이블과 관련이 있는 것 같습니다.)

답변1

짐작하셨듯이 이러한 기호는 ELF 파일에 포함된 기호 정보에서 나옵니다. 완전한 기호 테이블이 존재하지 않더라도 동적 연결을 위해서는 일부 기호 정보가 필요합니다.

실제 스택 트레이스 측면에서는 함수 호출 시 CPU에서 반환한 위치가 저장된다. x86과 같은 CPU의 경우 스택에 푸시됩니다. RISC 머신의 경우 이는 일반적으로 레지스터에 저장됩니다. 함수가 다른 함수(즉, 함수가 아닌 경우)를 호출하려는 경우 leaf이 레지스터가 스택에 푸시됩니다. 스택 추적 코드는 스택에서 이러한 주소를 찾고, 그 앞의 기호에서 가장 가까운 주소를 찾아 이를 보고합니다. 일부 스택 추적 코드는 기호 이름과 해당 거리를 인쇄하므로 정확성에 대한 신뢰도를 높일 수 있습니다. 예를 들어, 기호가 반환 주소 앞에 40바이트만 있으면 그 기호가 40,000바이트 앞에 있는 것보다 코드에 있다는 확신이 더 큽니다. 후자의 경우, 반환 주소가 다른 함수를 가리키는 것으로 의심할 수 있지만 해당 함수에는 기호 테이블에 항목이 없습니다.

많은 요인으로 인해 이 결과가 부정확해질 수 있습니다. 컴파일러가 함수 a를 함수 b에 인라인하는 경우 사용자는 함수 a에 있을 수 있지만 스택 추적에서는 사용자가 b에 있다고 보고합니다.

컴파일러가 함수 a 가 함수 c 와 유사한 방식으로 끝나는 "꼬리 호출 최적화"를 수행하고 return b();함수 c 가 함수 a 를 호출하는 경우 추적에 c->a->b 가 표시될 것으로 예상할 수 있지만 c - 만 표시됩니다. >ㄴ. c의 소스 코드를 보고 b를 직접 호출하지 않는다는 것을 보면 혼란스러울 수 있습니다.

관련 정보