正如您已经观察到的那样,构造函数/初始化函数的实现细节高度依赖于编译器(版本)。虽然我不知道是否有工具可以做到这一点,但当前的GCC/clang版本所做的事情足够简单,可以让一个小脚本完成: .init_array
只是一个入口点列表。objdump -s
可用于加载列表,nm
可用于查找符号名称。以下是一个Python脚本,可以为任何由上述编译器生成的二进制文件工作:
import os
import sys
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)
parts.pop(-1)
if is_64bit:
parts = [ "".join(parts[i:i+2]) for i in range(0, len(parts), 2) ]
parts = [ "".join(reversed([ x[i:i+2] for i in range(0, len(x), 2) ])) for x in parts ]
initializers += parts
dis_output = os.popen("objdump -d '%s' | c++filt" % (sys.argv[1].replace("'", r"\'"), )).read()
def find_associated_constructor(disassembly, symbol):
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
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
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]
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
得到一个全零条目,在加载库时将其覆盖为初始化器的最终地址。因此,此脚本将输出一个带有所有零的地址。
nm -SlC --size-sort <ELF> | grep -F ' b '
非常有帮助。它可以找到许多全局的string
,map
和其他类似的东西。 - Trass3r