Доброго времени суток участникам форума! Хочу разобраться в ELF-формате для того, чтобы написат дизассемблер. Разбираюсь на примере простой программы "Hello, world!", написанной на с++, скомпилированной gcc. Нашёл секции с кодом. Теперь возникает такая проблема: некоторые части секций именованы. Не могу понять, как найти информацию об этих именах (метках, насколько я понимаю). Ещё одна проблема в том, что встречаются пустые части в файле (как многоточие перед второй меткой -- см. ниже). Сложность в том, что пустые два байта, как в приведённом фрагменте, неправильно дизассемблируются. Решается эта сложность, как я представляю, нахождением информации о метках. Но как и где найти эту информацию? Вот кусок того, что выдаёт objdump: Disassembly of section .plt: 08048580 <__cxa_atexit@plt-0x10>: 8048580: ff 35 f8 9f 04 08 pushl 0x8049ff8 8048586: ff 25 fc 9f 04 08 jmp *0x8049ffc 804858c: 00 00 add %al,(%eax) ... 08048590 <__cxa_atexit@plt>: 8048590: ff 25 00 a0 04 08 jmp *0x804a000 8048596: 68 00 00 00 00 push $0x0 804859b: e9 e0 ff ff ff jmp 8048580 <_init+0x30>
Да, документацию нашёл без проблем. Но она на английском. Наверное, я что-то пропустил в ней, связанное с конкретно этой частью. Кто-нибудь может подсказать, как находить каждый раз длину участка файла, заполненного кодом, и его месторасположение?
Vasilii Нет, тут надо просто conrtol-flow отслеживать: видим команду безусловного перехода, значит следующие байты вовсе не обязательно будут командами. ELF не содержит информации о том, где заканчивается код, есть записи лишь о том, где он начинается -- это как раз выясняется метками. Кстати в *nix их вроде принято называть словом symbol, по-крайней мере мой опыт общения с libbfd, подсказывает что это так.
r90, спасибо! Я как раз думал, что информация об этом либо содержится в файле, либо выясняется переходами. Но, в таком случае, возникает резонный вопрос: откуда в таком случае тот же objdump знает имена меток? Здесь: 08048590 <__cxa_atexit@plt>: то, что в угловых скобках -- это имя метки. И такие имена по всему дизассемблированному файлу. В самом исполняемом файле эти имена присутствуют. Только непонятно, как их связать с адресами.
Имена находятся в таблице символов (.symtab). Находим секцию с типом SHT_SYMTAB, разбираем ее и получаем символы. Секция .symtab состоит из записей в формате: Код (Text): typedef struct { Elf64_Word st_name; /* Symbol name (string tbl index) */ unsigned char st_info; /* Symbol type and binding */ unsigned char st_other; /* Symbol visibility */ Elf64_Section st_shndx; /* Section index */ Elf64_Addr st_value; /* Symbol value */ Elf64_Xword st_size; /* Symbol size */ } Elf64_Sym; Количество записей в секции определяется с помощью размера секции и размера записи секции (sh_size / sh_entsize) из заголовка секции. Это для получения символов, которые определены в файле. Для получения символов, которые используются, но не определены надо разобрать SHT_REL и SHT_RELA: Код (Text): typedef struct { Elf64_Addr r_offset; /* Address */ Elf64_Xword r_info; /* Relocation type and symbol index */ } Elf64_Rel; typedef struct { Elf64_Addr r_offset; /* Address */ Elf64_Xword r_info; /* Relocation type and symbol index */ Elf64_Sxword r_addend; /* Addend */ } Elf64_Rela; Секции SHT_REL и SHT_RELA связаны с таблицей символов (SHT_SYMTAB) и описывают связь неопределенного в файле символа с адресами, где этот символ используется. Т.е. если мы вызываем ф-ию printf, по адресу 0х0: 0x0: 0xE8 0x0 0x0 0x0 0x0 call printf то релокация опишет, что байты по адресу 0x1 должны быть скорректированы, чтобы указывать на символ printf, когда он будет найден. Вкратце примерно так. P.S. Рекомендую все же заняться английским и заглянуть в файл /usr/include/elf.h