По сути, я делаю что-то похожее на https://wiki.osdev.org/ELF_Tutorial, где я загружаю данные в структуры и читаю различные секции по их смещениям. Хост имеет прямой порядок байтов, и я пытаюсь проанализировать файлы, которые были скомпилированы для цели с прямым порядком байтов. Я попытался сделать ту же последовательность кода с этими файлами с прямым порядком байтов, что и с файлами с прямым порядком байтов, но при попытке доступа к разделам в коде произошел сбой.
int fd = open(filename, O_RDONLY);
char *header_start = (char *)mmap(0, file_size, PROT_READ, MAP_PRIVATE, fd, 0);
Elf32_Ehdr* elf_ehdr = (Elf32_Ehdr *)header_start;
Elf32_Shdr* elf_shdrs = (Elf32_Shdr *)((int)header_start + elf_ehdr->e_shoff);
Elf32_Shdr* sh_strtab = &elf_shdrs[elf_ehdr->e_shstrndx];
// code segfaults here when trying to access sh_strtab->sh_offset for big endian
// files, but works just fine for little endian files
Почему код не выполняется для файлов с прямым порядком байтов?
В большом файле байтов elf_ehdr->e_shoff
будет большим порядковым числом, и порядок байтов должен быть соблюден.
Скажем, мы имеем дело с 32 битами и e_shoff
хорошее небольшое число, например 64. В старом порядке он будет записан в файле как 0x00000040. Но вы читаете этот файл на процессоре с прямым порядком байтов, поэтому 0x00000040 считывается из файла как двоичный двоичный объект и будет интерпретироваться процессором как 1073741824.
Elf32_Shdr* elf_shdrs = (Elf32_Shdr *)((int)header_start + elf_ehdr->e_shoff);
решает в
Elf32_Shdr* elf_shdrs = (Elf32_Shdr *)((int)header_start + 1073741824);
не
Elf32_Shdr* elf_shdrs = (Elf32_Shdr *)((int)header_start + 64);
и собирается пропустить цель с большим отрывом. Попытка получить доступ к членам в результате elf_shdrs
бродит в неопределенном поведении.
Быстрое исправление взлома
Elf32_Shdr* elf_shdrs = (Elf32_Shdr *)(header_start + ResolveEndian(elf_ehdr->e_shoff));
где ResolveEndian
представляет собой серию перегруженных функций, которые либо абсолютно ничего не делают, потому что порядковый номер файла совпадает с порядковым порядком в системе или меняет порядок байтов. Для многих примеров того, как это сделать, см. Как я могу преобразовать между значениями с прямым и младшим порядком байтов в C ++?
Более длинное исправление не будет использовать файлы, отображенные в памяти, а вместо этого десериализовать файл принимая во внимание различия в размерах переменных (и результирующие различия в смещениях) между 32- и 64-битными программами, а также порядок байтов. Это приведет к созданию более надежного и переносимого синтаксического анализатора, который всегда будет работать независимо от исходного ELF и реализации компилятора, используемой для построения синтаксического анализатора.
Других решений пока нет …