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를 직접 호출하지 않는다는 것을 보면 혼란스러울 수 있습니다.