Всегда ли sh_addr равен sh_offset в формате файла ELF? - PullRequest
4 голосов
/ 22 июня 2010

недавно (да, уже не в школе) Я учил себя формату файла 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» всегда начинался с очень низкого виртуального адреса (мы говорим меньше 1000 часов). Поработав с GDB некоторое время, я заметил, что для каждого раздела sh_addr был равен sh_offset.

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

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

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

Ответы [ 2 ]

3 голосов
/ 22 июня 2010

Я попробовал это, и 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}
0 голосов
/ 23 июня 2010

Ладно, посмотрев на ответ Руди-Мура, я подумал, что еще раз проведу расследование с помощью gdb ...

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

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

...