如何找到全局静态初始化
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
(另请参阅 Can't find .dtors and .ctors in binary)。但是我该如何解释输出呢?我的意思是,总结页面大小也可以通过 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 魔法转换为上面的代码?
如您所见,contructors/initialization 函数的实现细节高度依赖于编译器(版本)。虽然我不知道用于此的工具,但当前 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
。要获得概述,我建议查看 sources of libc's loader,尤其是 _dl_init()
和 call_init()
。
我刚刚读了这篇优秀的文章:http://neugierig.org/software/chromium/notes/2011/08/static-initializers.html 然后我尝试了:https://gcc.gnu.org/onlinedocs/gccint/Initialization.html
虽然它所说的关于查找初始化程序的内容对我不起作用。 .ctors
部分不可用,但我可以找到 .init_array
(另请参阅 Can't find .dtors and .ctors in binary)。但是我该如何解释输出呢?我的意思是,总结页面大小也可以通过 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 魔法转换为上面的代码?
如您所见,contructors/initialization 函数的实现细节高度依赖于编译器(版本)。虽然我不知道用于此的工具,但当前 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
。要获得概述,我建议查看 sources of libc's loader,尤其是 _dl_init()
和 call_init()
。