簡體   English   中英

Linux:同時使用 backtrace()、/proc/self/maps 和 addr2line 會導致無效結果

[英]Linux: using backtrace(), /proc/self/maps and addr2line together results in invalid result

我正在嘗試實現一種將程序的調用堆棧記錄到文件中然后稍后顯示的方法。 以下是步驟:

  • 將 /proc/self/maps 的內容寫入日志文件。
    • 在這個例子中,/proc/self/maps 的內容是:
    • 00400000-05cdc000 r-xp 00000000 00:51 12974779926 helloworld
    • 這意味着helloworld程序的基地址是0x400000。
  • 在程序中,每當一個有趣的代碼需要記錄其調用堆棧時,我都會使用函數backtrace()獲取調用堆棧的地址,然后寫入日志文件。 假設這個例子中的調用堆棧是:
    • 0x400001
    • 0x400003
  • 稍后,在單獨的日志查看器程序中,將打開並解析日志文件。 調用堆棧中的地址將被程序的基地址減去。 在這種情況下:
    • 0x400001 - 0x400000 = 1
  • 然后我使用這個扣除的偏移量來使用 addr2line 程序獲取行號:
    • addr2line -fCe hellowork 0x1
    • 然而這會產生??? 結果,即無效的偏移量。
  • 但是如果我不扣除調用堆棧的地址,而是將實際值傳遞給 add2line 命令:
    • addr2line -fCe hellowork 0x400001 ,然后返回正確的文件和行號。

問題是如果地址在共享對象中,那么絕對地址將不起作用,而扣除的偏移量將起作用。

為什么主可執行文件和共享對象的地址映射方式存在如此大的差異? 或者這可能是特定於backtrace實現的,以便它始終返回主可執行文件中函數的絕對地址?

為什么主可執行文件和共享對象的地址映射方式存在如此大的差異?

共享庫通常在地址 0 處鏈接並重新定位。 非位置可執行文件通常在x86_64 Linux 上的地址 0x400000 處鏈接,並且不得重定位(否則將不起作用)。

要找出給定的 ELF 二進制文件鏈接的位置,請查看第一個PT_LOAD段的p_vaddr地址( readelf -Wl foo會告訴你)。 此外,只有ET_DYN ELF 二進制文件可以重定位,而ET_EXEC二進制文件則不能。

請注意,存在與位置無關的可執行文件,對於它們,您需要進行減法運算。

請注意,共享庫通常在地址0(等作品減法)聯系在一起,但他們沒有 運行prelink上的共享庫將導致在非0地址鏈接的共享庫,然后減去你的使用將不能工作

實際上,您需要做的是從鏈接地址中減去運行時加載地址以獲得重定位(對於非 PIE 可執行文件為 0,對於共享庫為非 0),然后從程序中減去該重定位通過backtrace記錄的 counter 來獲取符號值。

最后,如果您使用dl_iterate_phdr迭代所有加載的 ELF 圖像,它提供的dlpi_addr正是您需要減去的重定位。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM