簡體   English   中英

如何查找全局靜態初始化

[英]How to find global static initializations

我剛讀了這篇優秀的文章: http//neugierig.org/software/chromium/notes/2011/08/static-initializers.html然后我嘗試了: https//gcc.gnu.org/onlinedocs/gccint/Initialization html的

關於尋找初始化器的說法雖然對我不起作用。 .ctors部分不可用,但我可以找到.init_array (另請參見無法在二進制文件中找到.dtors和.ctors )。 但是我如何解釋輸出? 我的意思是,總結頁面的大小也可以通過size命令及其.bss列來處理 - 或者我錯過了什么?

此外, nm不報告任何*_GLOBAL__I_*符號,僅報告*_GLOBAL__N_*函數,並且 - 更有趣 - _GLOBAL__sub_I_somefile.cpp條目。 后者可能表示具有全局初始化的文件。 但是我可以以某種方式獲得正在運行的構造函數列表嗎? 理想情況下,工具會給我一個列表

Foo::Foo in file1.cpp:12
Bar::Bar in file2.cpp:45
...

(假設我有調試符號可用)。 有這樣的工具嗎? 如果沒有,怎么能寫出來呢? .init_array部分是否包含指向可通過某些DWARF魔法轉換為上述代碼的代碼的指針?

正如您已經觀察到的,構造函數/初始化函數的實現細節依賴於高度編譯器(版本)。 雖然我不知道這個工具,但目前GCC / clang版本的功能很簡單,只需讓一個小腳本完成工作: .init_array只是一個入口點列表。 objdump -s可用於加載列表,而nm用於查找符號名稱。 這是一個Python腳本,可以做到這一點。 它應該適用於由所述編譯器生成的任何二進制文件:

#!/usr/bin/env python
import os
import sys

# Load .init_array section
objdump_output = os.popen("objdump -s '%s' -j .init_array" % (sys.argv[1].replace("'", r"\'"),)).read()
is_64bit = "x86-64" in objdump_output
init_array = objdump_output[objdump_output.find("Contents of section .init_array:") + 33:]
initializers = []
for line in init_array.split("\n"):
    parts = line.split()
    if not parts:
        continue
    parts.pop(0)  # Remove offset
    parts.pop(-1) # Remove ascii representation

    if is_64bit:
        # 64bit pointers are 8 bytes long
        parts = [ "".join(parts[i:i+2]) for i in range(0, len(parts), 2) ]

    # Fix endianess
    parts = [ "".join(reversed([ x[i:i+2] for i in range(0, len(x), 2) ])) for x in parts ]

    initializers += parts

# Load disassembly for c++ constructors
dis_output = os.popen("objdump -d '%s' | c++filt" % (sys.argv[1].replace("'", r"\'"), )).read()
def find_associated_constructor(disassembly, symbol):
    # Find associated __static_initialization function
    loc = disassembly.find("<%s>" % symbol)
    if loc < 0:
        return False
    loc = disassembly.find(" <", loc)
    if loc < 0:
        return False
    symbol = disassembly[loc+2:disassembly.find("\n", loc)][:-1]
    if symbol[:23] != "__static_initialization":
        return False
    address = disassembly[disassembly.rfind(" ", 0, loc)+1:loc]
    loc = disassembly.find("%s <%s>" % (address, symbol))
    if loc < 0:
        return False
    # Find all callq's in that function
    end_of_function = disassembly.find("\n\n", loc)
    symbols = []
    while loc < end_of_function:
        loc = disassembly.find("callq", loc)
        if loc < 0 or loc > end_of_function:
            break
        loc = disassembly.find("<", loc)
        symbols.append(disassembly[loc+1:disassembly.find("\n", loc)][:-1])
    return symbols

# Load symbol names, if available
nm_output = os.popen("nm '%s'" % (sys.argv[1].replace("'", r"\'"), )).read()
nm_symbols = {}
for line in nm_output.split("\n"):
    parts = line.split()
    if not parts:
        continue
    nm_symbols[parts[0]] = parts[-1]

# Output a list of initializers
print("Initializers:")
for initializer in initializers:
    symbol = nm_symbols[initializer] if initializer in nm_symbols else "???"
    constructor = find_associated_constructor(dis_output, symbol)
    if constructor:
        for function in constructor:
            print("%s %s -> %s" % (initializer, symbol, function))
    else:
        print("%s %s" % (initializer, symbol))

C ++靜態初始化程序不是直接調用的,而是通過兩個生成的函數調用的, _GLOBAL__sub_I_..__static_initialization.. 該腳本使用這些函數的反匯編來獲取實際構造函數的名稱。 您需要使用c++filt工具來解除名稱,或者從腳本中刪除調用以查看原始符號名稱。

共享庫可以有自己的初始化列表,此腳本不會顯示這些列表。 這種情況稍微復雜一些:對於非靜態初始化程序, .init_array獲取一個全零條目,在加載庫時會被初始化程序的最終地址覆蓋。 所以這個腳本會輸出一個全零的地址。

加載ELF對象時會執行多項操作,而不僅僅是.init_array 為了概述,我建議查看libc加載器來源 ,尤其是_dl_init()call_init()

暫無
暫無

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

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