Всегда ли sh_addr равен sh_offset в формате файла ELF?

недавно (ура, больше не школа) я учил себя формату файла ELF. Я в основном следил за документацией здесь: http://www.skyfree.org/linux/references/ELF_Format.pdf.

Все шло отлично, и я написал эту программу, чтобы получить информацию о разделах файла ELF:

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <elf.h>


void dumpShdrInfo(Elf32_Shdr elfShdr, const char *sectionName)
{
printf("Section '%s' starts at 0x%08X and ends at 0x%08X\n", 
    sectionName, elfShdr.sh_offset, elfShdr.sh_offset + elfShdr.sh_size);
}

int search(const char *name)
{
Elf32_Ehdr elfEhdr;
Elf32_Shdr *elfShdr;
FILE *targetFile;
char tempBuf[64];
int i, ret = -1;

targetFile = fopen(name, "r+b");

if(targetFile)
{
    /* read the ELF header */
    fread(&elfEhdr, sizeof(elfEhdr), 1, targetFile);


    /* Elf32_Ehdr.e_shnum specifies how many sections there are */
    elfShdr = calloc(elfEhdr.e_shnum, sizeof(*elfShdr));
    assert(elfShdr);

    /* set the file pointer to the section header offset and read it */
    fseek(targetFile, elfEhdr.e_shoff, SEEK_SET);
    fread(elfShdr, sizeof(*elfShdr), elfEhdr.e_shnum, targetFile);


    /* loop through every section */
    for(i = 0; (unsigned int)i < elfEhdr.e_shnum; i++)
    {


        /* if Elf32_Shdr.sh_addr isn't 0 the section will appear in memory*/
        if(elfShdr[i].sh_addr)
        {

            /* set the file pointer to the location of the section's name and then read the name */ 
            fseek(targetFile, elfShdr[elfEhdr.e_shstrndx].sh_offset + elfShdr[i].sh_name, SEEK_SET);
            fgets(tempBuf, sizeof(tempBuf), targetFile);

            #if defined(DEBUG)
            dumpShdrInfo(elfShdr[i], tempBuf);
            #endif
        }
    }

    fclose(targetFile);
    free(elfShdr);
}

return ret;
}

int main(int argc, char *argv[])
{
if(argc > 1)
{
    search(argv[1]);
}
return 0;
}

Запустив его несколько раз на паре файлов, я заметил кое-что странное. Раздел '.text' всегда начинался с очень низкого виртуального адреса (мы говорим меньше 1000h). Немного покопавшись в gdb, я заметил, что для каждого раздела sh_addr равен sh_offset.

Это то, что меня смущает - Elf32_Shdr.sh_addr задокументирован как «адрес, по которому должен находиться первый байт», а Elf32_Shdr.sh_offset задокументирован как «байтовое смещение от начала файла до первого байта». в функции». Если это оба случая, для меня действительно не имеет смысла, что они оба равны. Почему это?

Теперь я знаю, что есть разделы, которые содержат неинициализированные данные (думаю, .bss), поэтому было бы разумно, чтобы эти данные не отображались в файле, а появлялись в памяти процесса. Это будет означать, что для каждого раздела, следующего за вышеупомянутым, вычисление его виртуального адреса будет намного сложнее, чем простая переменная.

При этом есть ли способ определить виртуальный адрес раздела?


c elf
person masseyc    schedule 22.06.2010    source источник


Ответы (2)


Я попробовал это, и Elf32_Shdr.sh_addr не совпадает с Elf32_Shdr.sh_offset в моем примере. Он сдвинут на 0x08040000, что является виртуальным начальным адресом программы в памяти. Elf32_Shdr.sh_offset — это 0x00000570 для раздела «.text», а Elf32_Shdr.sh_addr — это 0x08048570 для того же раздела.

Как вы цитируете из документации, Elf32_Shdr.sh_offset - это «смещение в байтах от начала файла до первого байта в функции»:

$> hexdump -C -s 0x00000570 -n 64 elffile
00000570  31 ed 5e 89 e1 83 e4 f0  50 54 52 68 b0 88 04 08  |1.^.....PTRh....|
00000580  68 c0 88 04 08 51 56 68  66 88 04 08 e8 3b ff ff  |h....QVhf....;..|
00000590  ff f4 90 90 90 90 90 90  90 90 90 90 90 90 90 90  |................|
000005a0  55 89 e5 83 ec 08 80 3d  44 a0 04 08 00 74 0c eb  |U......=D....t..|

а Elf32_Shdr.sh_addr — это «адрес, по которому должен находиться первый байт». Это виртуальный адрес данных в памяти:

(gdb) print/x *(char[64] *) 0x08048570
$4 = {
0x31, 0xed, 0x5e, 0x89, 0xe1, 0x83, 0xe4, 0xf0, 0x50, 0x54, 0x52, 0x68, 0xb0, 0x88, 0x04, 0x08,
0x68, 0xc0, 0x88, 0x04, 0x08, 0x51, 0x56, 0x68, 0x66, 0x88, 0x04, 0x08, 0xe8, 0x3b, 0xff, 0xff,
0xff, 0xf4, 0x90 <repeats 14 times>,
0x55, 0x89, 0xe5, 0x83, 0xec, 0x08, 0x80, 0x3d, 0x44, 0xa0, 0x04, 0x08, 0x00, 0x74, 0x0c, 0xeb}
person rudi-moore    schedule 22.06.2010
comment
Вы используете код, который я вставил? Если нет, не могли бы вы указать, что я делаю неправильно? Результат, который я получаю, находится здесь pastebin.com/qTPG85sT. - person masseyc; 23.06.2010
comment
ошибаюсь, не обращайте на это внимания. stackoverflow.com/questions/3091363/ - person masseyc; 23.06.2010

Хорошо, посмотрев на ответ rudi-moore, я подумал, что еще раз исследую gdb...

Оказывается, в моем dumpShdrInfo я печатал sh_offset вместо sh_addr. У меня яркие воспоминания о написании этой функции и вводе "sh_addr", а также об отладке с помощью gdb и о том, что sh_offset равен sh_addr.

Тем не менее, я думаю, что я идиот, и мои воспоминания не стоят столько, потому что, как только я изменил его на sh_addr и перекомпилировал, он заработал. Вот что я получаю за программирование в 5 утра. :/

person masseyc    schedule 23.06.2010
comment
Да, этот 5AM-код, я его хорошо понимаю ;) Хорошо, что теперь он работает. - person rudi-moore; 23.06.2010
comment
Возможно, вы смотрели на разделяемую библиотеку, а не на основную программу. Совместно используемые библиотеки обычно имеют адрес загрузки 0, что означает, что они могут быть загружены по любому адресу, и, следовательно, смещения файлов и (относительные) виртуальные адреса обычно идентичны (для текстового сегмента; для данных они обычно отличаются на одну страницу). ). - person R.. GitHub STOP HELPING ICE; 02.07.2011