Core-Dump
如何從 coredump 重建堆棧跟踪?
coredumpctl
創建 coredump 後,該實用程序會顯示程序的堆棧跟踪。例如,在 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 文件中的符號表有關)
正如您正確猜測的那樣,符號來自嵌入在 ELF 文件中的符號資訊。即使不存在完整的符號表,也需要一些符號資訊才能進行動態連結。
就實際的堆棧跟踪而言,當呼叫函式時,會保存 cpu 返回的位置。對於像 x86 這樣的 cpu,它被壓入堆棧。對於 RISC 機器,它通常被放入寄存器。如果函式想要呼叫任何其他函式(即它不是
leaf
函式)然後這個寄存器被壓入堆棧。堆棧跟踪程式碼在堆棧上找到這些地址,在其前面的符號中查找最近的地址並報告它。一些堆棧跟踪程式碼會同時列印符號名稱和距離,這可以讓您對它的準確性更有信心。例如,如果符號在返回地址之前只有 40 個字節,那麼與之前的 40,000 個字節相比,人們對它在該程式碼中的信心要大得多。在後一種情況下,人們可能會懷疑返回地址指向不同的函式,但該函式在符號表中沒有條目。很多事情都會使這不准確。如果編譯器將函式 a 內聯到函式 b 中,那麼您可能在函式 a 中,但堆棧跟踪會報告您在 b 中。
如果編譯器執行“尾呼叫優化”,其中函式 a 以類似的結尾
return b();
並且函式 c 呼叫函式 a,您可能希望跟踪顯示 c->a->b,但您只會看到 c->b。如果您查看 c 的原始碼並且您發現它從不直接呼叫 b,這可能會令人困惑。