2015-01-23 4 views
2

Я только что прочитал эту прекрасную статью: 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 в вышеуказанное?

ответ

2

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

+0

Спасибо! Но где преимущество этого в сравнении с «nm -a binaryOrSharedLibrary | grep GLOBAL__'? Это намного проще и генерирует для меня тот же результат. Символу не так сложно достать. Скорее, я хотел бы узнать файл/строку, чтобы найти то, что мне нужно изменить. – milianw

+0

Кроме того, в обоих случаях я получаю '_GLOBAL__sub_I_filename.cpp' вместо« правильных »символов для ctors, которые запускаются в этом файле. – milianw

+0

Символы могут, и часто в не-отладочных сборках, лишены. Это также удаляет символы «GLOBAL__». С другой стороны, обычно присутствует '.init_array'. Вторая причина, по которой я предпочитаю использовать '.init_array', состоит в том, что она более общая: функции инициализатора C (функции, имеющие' __attribute __ ((конструктор))) также используют этот механизм, но нет отдельного 'GLOBAL__' созданный для них символ. Что касается имени символа, то создается сгенерированная промежуточная функция. Найдите '__static_initialization_and_destruction_' в' objdump -d'. – Phillip

1

При загрузке объекта ELF выполняется несколько вещей, а не только .init_array. Чтобы получить обзор, я предлагаю посмотреть на sources of libc's loader, особенно _dl_init() и call_init().

Смежные вопросы