Как вы уже заметили, детали реализации конструкторов/функций инициализации сильно зависят от компилятора (версии). Хотя мне неизвестен инструмент для этого, то, что делают текущие версии 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
получает запись со всеми нулями, которая перезаписывается конечным адресом инициализатора при загрузке библиотеки. Таким образом, этот скрипт будет выводить адрес со всеми нулями.
person
Phillip
schedule
26.01.2015