繁体   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