Таблица заголовков программы исполняемого или общего файла - это массив структур, описывающих сегменты и содержащих прочие сведения, необходимые системе для подготовки к выполнению программы. Сегмент объектного файла содержит один или несколько разделов, как описано в разделе Содержимое сегментов. Заголовки программы имеют смысл только для исполняемых и общих объектных файлов. Размер заголовка программы описан в заголовке ELF файла в элементах e_phentsize и e_phnum.
Более подробная информация приведена в разделе Заголовок
ELF в Главе 32.
Заголовок программы typedef struct { Elf32_Word p_type; Elf32_Off p_offset; Elf32_Addr p_vaddr; Elf32_Addr p_paddr; Elf32_Word p_filesz; Elf32_Word p_memsz; Elf32_Word p_flags; Elf32_Word p_align; } Elf32_Phdr; typedef struct { Elf64_Word p_type; Elf64_Word p_flags; Elf64_Off p_offset; Elf64_Addr p_vaddr; Elf64_Addr p_paddr; Elf64_Xword p_filesz; Elf64_Xword p_memsz; Elf64_Xword p_align; } Elf64_Phdr; |
Некоторые записи описывают сегмент процесса; другие содержат дополнительную информацию и не влияют на образ процесса. Записи сегмента могут быть указаны в любом порядке, за исключением специальных случаев, описанных ниже.
Ниже перечислены определенные значения типа; другие значение зарезервированы для использования в будущем.
Типы сегментов, таблица p_type
Имя | Значение |
---|---|
PT_NULL | 0 |
PT_LOAD | 1 |
PT_DYNAMIC | 2 |
PT_INTERP | 3 |
PT_NOTE | 4 |
PT_SHLIB | 5 |
PT_PHDR | 6 |
PT_LOOS | 0x60000000 |
PT_HIOS | 0x6fffffff |
PT_LOPROC | 0x70000000 |
PT_HIPROC | 0x7fffffff |
Более подробная информация приведена в разделе Раздел примечаний.
Более подробная информация приведена в разделе Интерпретатор программ.
Примечание: Все сегменты заголовков программы не обязательны, если специально не оговорено иное. Таблица заголовков конкретного файла может содержать только те элементы, которые относятся к данному файлу.
Как описано в разделе Загрузка программы, виртуальные адреса в заголовке программы могут не соответствовать виртуальным адресам в образе этой программы в памяти системы. В исполняемых файлах обычно указывается абсолютный код. Для правильного выполнения процессов сегменты должны находиться по виртуальным адресам, которые использовались при создании исполняемого файла. В то же время, сегменты общих объектов обычно содержат код, не зависящий от положения в памяти. Это позволяет изменять виртуальные адреса сегментов в различных процессах, не затрагивая поведение программы.
На некоторых платформах, если система выбирает виртуальные адреса для конкретных процессов, используются относительные смещения одного сегмента относительно другого в общем объекте. Независимый от положения в памяти код на таких платформах применяет относительную адресацию сегментов, поэтому взаимное расположение сегментов в памяти и в файле должно совпадать. Таким образом, разница между виртуальным адресом сегмента в памяти и соответствующим виртуальным адресом в файле постоянна для всех исполняемых или общих объектов одного процесса. Эта разница называется базовым адресом. Одним из применений базового адреса является перемещение образа в памяти при динамической компоновке.
Базовый адрес в исполняемом или общем объектном файле (на платформах, поддерживающих эту технологию) вычисляется во время выполнения на основе трех значений: адреса загрузки в виртуальную память, максимального размера страницы и минимального виртуального адреса загружаемого сегмента программы. Для вычисления базового адреса система определяет адрес, связанный с минимальным значением p_vaddr, для сегмента PT_LOAD. Этот адрес округляется снизу до ближайшего значения, кратного максимальному размеру страницы. Соответствующее значение p_vaddr также округляется снизу до ближайшего значения, кратного максимальному размеру страницы. Базовый адрес - это разница между округленным адресом в памяти и округленным значением p_vaddr.
Более подробная информация и примеры приведены в соответствующем разделе документации по процессору. Раздел Интерфейс операционной системы в Главе 32 документации по процессору содержит дополнительную информацию о виртуальном адресном пространстве и размере страниц.
В программе, загружаемой в память, должен быть хотя бы один загружаемый сегмент, хотя это и не следует явно из формата файла. При создании образа загружаемого сегмента в памяти система присваивает права доступа в соответствии с элементом p_flags.
Биты флага сегмента, таблица p_flags
Имя | Значение | Описание |
---|---|---|
PF_X | 0x1 | Выполнение |
PF_W | 0x2 | Запись |
PF_R | 0x4 | Чтение |
PF_MASKOS | 0x0ff00000 | Не задано |
PF_MASKPROC | 0xf0000000 | Не задано |
Все биты в маске PF_MASKOS зарезервированы для конкретной операционной системы.
Все биты в маске PF_MASKPROC зарезервированы для конкретного процессора. Их применение описано в документации по процессору.
Если некоторый бит равен 0, то соответствующий тип доступа запрещен. Действительные права доступа зависят также от блока управления памятью и могут различаться в разных системах. Хотя допустимы все комбинации флагов, система может предоставлять более широкие права доступа, чем запрошено. Однако права на запись предоставляются только в том случае, если они явно указаны. В следующей таблице показана точная интерпретация флагов в допустимая интерпретация. В системах, совместимых с ABI, может применяться любая схема.
Права доступа к сегменту
Флаги | Значение | Точная | Допустимая |
---|---|---|---|
none | 0 | Доступ запрещен | Доступ запрещен |
PF_X | 1 | Только выполнение | Чтение, выполнение |
PF_W | 2 | Только запись | Чтение, запись, выполнение |
PF_W+PF_X | 3 | Запись, выполнение | Чтение, запись, выполнение |
PF_R | 4 | Только чтение | Чтение, выполнение |
PF_R+PF_X | 5 | Чтение, выполнение | Чтение, выполнение |
PF_R+PF_W | 6 | Чтение, запись | Чтение, запись, выполнение |
PF_R+PF_W+PF_X | 7 | Чтение, запись, выполнение | Чтение, запись, выполнение |
Например, обычно для текстовых сегментов указываются права на чтение и выполнение. Сегменты данных обычно имеют права доступа на чтение, запись и выполнение.
Сегмент объектного файла может содержать несколько разделов, хотя в заголовке программы это и не отражено. Количество разделов также не имеет значения при загрузке программы. Однако некоторый набор данных должен обязательно присутствовать при выполнении, динамической компоновке и других операциях над программой. На следующих рисунках показано содержимое сегмента. Порядок и наличие разделов сегмента может различаться в разных операционных системах и на разных платформах. Более подробная информация приведена в документации по процессору.
Текстовые сегменты обычно содержат инструкции и данные, предназначенные только для чтения. Обычно они содержат разделы, описанные в Главе 32. В загружаемых сегментах могут присутствовать и другие разделы; приведенные ниже примеры не призваны дать полное и всеобъемлющее описание содержимого сегмента.
Текстовый сегмент
.text |
.rodata |
.hash |
.dynsym |
.dynstr |
.plt |
.rel.got |
Сегменты данных обычно содержат данные и инструкции, предназначенные для записи. Обычно они содержат следующие разделы.
Сегмент данных
.data |
.dynamic |
.got |
.bss |
Элемент заголовка PT_DYNAMIC указывает на раздел .dynamic, описанный в Динамический раздел. Разделы .got и .plt также содержат информацию о позиционно-независимом коде и динамической компоновке. Хотя раздел .plt показан в нашем примере в текстовом сегменте, в зависимости от процессора он может находиться как в текстовом сегменте, так и в сегменте данных. Более подробные сведения приведены в разделах Глобальная таблица смещения и Таблица компоновки процедур этой главы.
Как указано в разделе Разделы Главы 32, типом раздела .bss является SHT_NOBITS. Хотя он и не занимает места в файле, он влияет на образ сегмента в памяти. Обычно в конце сегмента находятся неинициализированные данные, поэтому значение p_memsz обычно превышает значение p_filesz в связанном элементе заголовка программы.
Иногда компоновщик должен снабдить некоторый объектный файл специальной информацией, применяемой другими программами для проверки соответствия, совместимости и т.п. Для этого можно применять разделы типа SHT_NOTE и элементы заголовка программы типа PT_NOTE. Раздел и элементы примечания могут содержать переменное число записей. В 64-разрядных объектах (файлах, где e_ident[EI_CLASS] равен ELFCLASS64) каждая запись - это массив 8-байтовых слов в формате применяемого процессора. В 32-разрядных объектах (файлах, где e_ident[EI_CLASS] равен ELFCLASS32) каждая запись - это массив 4-байтовых слов в формате применяемого процессора. Ниже приведены метки, помогающие понять структуру примечания, однако эти метки на являются частью спецификации.
Примечания
namesz |
descsz |
type |
name . . . |
desc . . . |
В следующем примере сегмент примечания содержит две записи.
Пример сегмента примечания
Примечание: Сегменты без имени (namesz=0) и с нулевой длиной имени (name[0]='\0') зарезервированы системой, но типы для них не определены. Все остальные имена должны содержать по крайней мере один символ.
Примечание: Информацию примечания указывать не обязательно. Наличие сегмента примечания не влияет на совместимость с ABI и не должно влиять на выполнение программы. В противном случае, программа считается несовместимой с ABI, и ее поведение не определено.